Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.babelrc25
-rw-r--r--.babelrc.js38
-rw-r--r--.eslintrc.yml70
-rw-r--r--.flayignore34
-rw-r--r--.gitattributes1
-rw-r--r--.gitignore3
-rw-r--r--.gitlab-ci.yml412
-rw-r--r--.gitlab/CODEOWNERS.disabled20
-rw-r--r--.gitlab/issue_templates/Acceptance_Testing.md100
-rw-r--r--.gitlab/issue_templates/Documentation.md54
-rw-r--r--.gitlab/issue_templates/Security developer workflow.md12
-rw-r--r--.gitlab/issue_templates/Test plan.md96
-rw-r--r--.gitlab/merge_request_templates/Change documentation location.md32
-rw-r--r--.gitlab/merge_request_templates/Database changes.md28
-rw-r--r--.gitlab/merge_request_templates/Documentation.md36
-rw-r--r--.haml-lint.yml52
-rw-r--r--.nvmrc2
-rw-r--r--.prettierignore4
-rw-r--r--.rubocop.yml40
-rw-r--r--.rubocop_todo.yml94
-rw-r--r--.ruby-version2
-rw-r--r--CHANGELOG.md970
-rw-r--r--CONTRIBUTING.md156
-rw-r--r--Dangerfile5
-rw-r--r--Dockerfile.assets4
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile100
-rw-r--r--Gemfile.lock400
-rw-r--r--Gemfile.rails47
-rw-r--r--Gemfile.rails4.lock1159
-rw-r--r--Gemfile.rails57
-rw-r--r--Gemfile.rails5.lock1220
-rw-r--r--LICENSE18
-rw-r--r--PROCESS.md99
-rw-r--r--README.md6
-rw-r--r--VERSION2
-rw-r--r--app/assets/images/auth_buttons/auth0_64.pngbin0 -> 1815 bytes
-rw-r--r--app/assets/images/auth_buttons/azure_64.pngbin695 -> 199 bytes
-rw-r--r--app/assets/images/auth_buttons/bitbucket_64.pngbin2161 -> 1299 bytes
-rw-r--r--app/assets/images/auth_buttons/google_64.pngbin4366 -> 1625 bytes
-rw-r--r--app/assets/images/auth_buttons/jwt_64.pngbin0 -> 2457 bytes
-rw-r--r--app/assets/images/auth_buttons/shibboleth_64.pngbin0 -> 2993 bytes
-rw-r--r--app/assets/images/ci_favicons/canary/favicon_status_scheduled.icobin0 -> 5430 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_scheduled.pngbin0 -> 1072 bytes
-rw-r--r--app/assets/images/cluster_app_logos/elasticsearch.pngbin0 -> 796 bytes
-rw-r--r--app/assets/images/cluster_app_logos/gitlab.pngbin0 -> 1757 bytes
-rw-r--r--app/assets/images/cluster_app_logos/helm.pngbin0 -> 1438 bytes
-rw-r--r--app/assets/images/cluster_app_logos/jeager.pngbin0 -> 2619 bytes
-rw-r--r--app/assets/images/cluster_app_logos/jupyterhub.pngbin0 -> 895 bytes
-rw-r--r--app/assets/images/cluster_app_logos/knative.pngbin0 -> 11259 bytes
-rw-r--r--app/assets/images/cluster_app_logos/kubernetes.pngbin0 -> 1437 bytes
-rw-r--r--app/assets/images/cluster_app_logos/meltano.pngbin0 -> 580 bytes
-rw-r--r--app/assets/images/cluster_app_logos/prometheus.pngbin0 -> 923 bytes
-rw-r--r--app/assets/images/koding-logo.svg8
-rw-r--r--app/assets/javascripts/activities.js10
-rw-r--r--app/assets/javascripts/api.js54
-rw-r--r--app/assets/javascripts/awards_handler.js65
-rw-r--r--app/assets/javascripts/badges/components/badge.vue8
-rw-r--r--app/assets/javascripts/badges/components/badge_form.vue111
-rw-r--r--app/assets/javascripts/badges/components/badge_list.vue10
-rw-r--r--app/assets/javascripts/badges/components/badge_list_row.vue16
-rw-r--r--app/assets/javascripts/behaviors/copy_to_clipboard.js2
-rw-r--r--app/assets/javascripts/behaviors/details_behavior.js8
-rw-r--r--app/assets/javascripts/behaviors/index.js6
-rw-r--r--app/assets/javascripts/behaviors/markdown/copy_as_gfm.js109
-rw-r--r--app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js19
-rw-r--r--app/assets/javascripts/behaviors/markdown/highlight_current_user.js17
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_gfm.js4
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_math.js8
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_mermaid.js58
-rw-r--r--app/assets/javascripts/behaviors/preview_markdown.js230
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js40
-rw-r--r--app/assets/javascripts/behaviors/requires_input.js3
-rw-r--r--app/assets/javascripts/behaviors/secret_values.js8
-rw-r--r--app/assets/javascripts/behaviors/shortcuts.js35
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts.js126
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js26
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js (renamed from app/assets/javascripts/shortcuts_find_file.js)0
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js77
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js28
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js (renamed from app/assets/javascripts/shortcuts_network.js)0
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js14
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.js4
-rw-r--r--app/assets/javascripts/blob/3d_viewer/index.js62
-rw-r--r--app/assets/javascripts/blob/3d_viewer/mesh_object.js21
-rw-r--r--app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js2
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js28
-rw-r--r--app/assets/javascripts/blob/blob_line_permalink_updater.js18
-rw-r--r--app/assets/javascripts/blob/file_template_mediator.js42
-rw-r--r--app/assets/javascripts/blob/file_template_selector.js8
-rw-r--r--app/assets/javascripts/blob/notebook/index.js7
-rw-r--r--app/assets/javascripts/blob/sketch/index.js2
-rw-r--r--app/assets/javascripts/blob/stl_viewer.js4
-rw-r--r--app/assets/javascripts/blob/template_selector.js11
-rw-r--r--app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js4
-rw-r--r--app/assets/javascripts/blob/template_selectors/dockerfile_selector.js4
-rw-r--r--app/assets/javascripts/blob/template_selectors/gitignore_selector.js4
-rw-r--r--app/assets/javascripts/blob/template_selectors/license_selector.js6
-rw-r--r--app/assets/javascripts/blob/template_selectors/type_selector.js1
-rw-r--r--app/assets/javascripts/blob/viewer/index.js42
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js5
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js32
-rw-r--r--app/assets/javascripts/boards/components/board.js59
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.vue32
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue129
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js14
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue79
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue28
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js59
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue366
-rw-r--r--app/assets/javascripts/boards/components/issue_due_date.vue90
-rw-r--r--app/assets/javascripts/boards/components/issue_time_estimate.vue48
-rw-r--r--app/assets/javascripts/boards/components/modal/empty_state.vue4
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.vue19
-rw-r--r--app/assets/javascripts/boards/components/modal/header.vue80
-rw-r--r--app/assets/javascripts/boards/components/modal/index.vue245
-rw-r--r--app/assets/javascripts/boards/components/modal/list.vue204
-rw-r--r--app/assets/javascripts/boards/components/modal/lists_dropdown.vue17
-rw-r--r--app/assets/javascripts/boards/components/modal/tabs.vue30
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js63
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue40
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.vue123
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js12
-rw-r--r--app/assets/javascripts/boards/index.js77
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js33
-rw-r--r--app/assets/javascripts/boards/models/issue.js37
-rw-r--r--app/assets/javascripts/boards/models/list.js23
-rw-r--r--app/assets/javascripts/boards/services/board_service.js12
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js78
-rw-r--r--app/assets/javascripts/boards/stores/modal_store.js12
-rw-r--r--app/assets/javascripts/boards/utils/query_data.js21
-rw-r--r--app/assets/javascripts/breadcrumb.js15
-rw-r--r--app/assets/javascripts/build_artifacts.js18
-rw-r--r--app/assets/javascripts/build_variables.js10
-rw-r--r--app/assets/javascripts/ci_variable_list/ajax_variable_list.js58
-rw-r--r--app/assets/javascripts/ci_variable_list/ci_variable_list.js37
-rw-r--r--app/assets/javascripts/ci_variable_list/native_form_variable_list.js5
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js54
-rw-r--r--app/assets/javascripts/clusters/clusters_index.js24
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue390
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue508
-rw-r--r--app/assets/javascripts/clusters/components/gcp_signup_offer.js27
-rw-r--r--app/assets/javascripts/clusters/constants.js9
-rw-r--r--app/assets/javascripts/clusters/services/clusters_service.js1
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js21
-rw-r--r--app/assets/javascripts/comment_type_toggle.js56
-rw-r--r--app/assets/javascripts/commit/image_file.js322
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_bundle.js10
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue143
-rw-r--r--app/assets/javascripts/commit_merge_requests.js7
-rw-r--r--app/assets/javascripts/commits.js38
-rw-r--r--app/assets/javascripts/commons/bootstrap.js12
-rw-r--r--app/assets/javascripts/commons/gitlab_ui.js4
-rw-r--r--app/assets/javascripts/commons/index.js1
-rw-r--r--app/assets/javascripts/commons/polyfills.js4
-rw-r--r--app/assets/javascripts/commons/polyfills/element.js23
-rw-r--r--app/assets/javascripts/commons/polyfills/svg.js5
-rw-r--r--app/assets/javascripts/compare_autocomplete.js4
-rw-r--r--app/assets/javascripts/confirm_danger_modal.js24
-rw-r--r--app/assets/javascripts/contextual_sidebar.js7
-rw-r--r--app/assets/javascripts/create_item_dropdown.js13
-rw-r--r--app/assets/javascripts/create_label.js63
-rw-r--r--app/assets/javascripts/cycle_analytics/components/banner.vue43
-rw-r--r--app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue22
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_code_component.vue36
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_component.vue37
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue47
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_review_component.vue40
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue50
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_test_component.vue54
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time_component.vue24
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js24
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_service.js5
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_store.js36
-rw-r--r--app/assets/javascripts/deploy_keys/components/action_btn.vue6
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue14
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue10
-rw-r--r--app/assets/javascripts/deploy_keys/service/index.js9
-rw-r--r--app/assets/javascripts/diff.js14
-rw-r--r--app/assets/javascripts/diff_notes/components/comment_resolve_btn.js32
-rw-r--r--app/assets/javascripts/diff_notes/components/diff_note_avatars.js28
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js39
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_count.js12
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js22
-rw-r--r--app/assets/javascripts/diff_notes/mixins/discussion.js12
-rw-r--r--app/assets/javascripts/diff_notes/models/discussion.js18
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js9
-rw-r--r--app/assets/javascripts/diff_notes/stores/comments.js16
-rw-r--r--app/assets/javascripts/diffs/components/app.vue110
-rw-r--r--app/assets/javascripts/diffs/components/changed_files.vue171
-rw-r--r--app/assets/javascripts/diffs/components/changed_files_dropdown.vue126
-rw-r--r--app/assets/javascripts/diffs/components/commit_item.vue133
-rw-r--r--app/assets/javascripts/diffs/components/commit_widget.vue40
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions.vue128
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions_dropdown.vue36
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue86
-rw-r--r--app/assets/javascripts/diffs/components/diff_discussions.vue65
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue88
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue74
-rw-r--r--app/assets/javascripts/diffs/components/diff_gutter_avatars.vue12
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_gutter_content.vue47
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue52
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_cell.vue37
-rw-r--r--app/assets/javascripts/diffs/components/file_row_stats.vue30
-rw-r--r--app/assets/javascripts/diffs/components/image_diff_overlay.vue139
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_comment_row.vue20
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue21
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue31
-rw-r--r--app/assets/javascripts/diffs/components/no_changes.vue2
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue64
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue124
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue49
-rw-r--r--app/assets/javascripts/diffs/components/tree_list.vue175
-rw-r--r--app/assets/javascripts/diffs/constants.js7
-rw-r--r--app/assets/javascripts/diffs/index.js5
-rw-r--r--app/assets/javascripts/diffs/mixins/changed_files.js38
-rw-r--r--app/assets/javascripts/diffs/store/actions.js121
-rw-r--r--app/assets/javascripts/diffs/store/getters.js76
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js13
-rw-r--r--app/assets/javascripts/diffs/store/modules/index.js4
-rw-r--r--app/assets/javascripts/diffs/store/mutation_types.js10
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js183
-rw-r--r--app/assets/javascripts/diffs/store/utils.js222
-rw-r--r--app/assets/javascripts/dirty_submit/dirty_submit_collection.js13
-rw-r--r--app/assets/javascripts/dirty_submit/dirty_submit_factory.js9
-rw-r--r--app/assets/javascripts/dirty_submit/dirty_submit_form.js83
-rw-r--r--app/assets/javascripts/dismissable_callout.js27
-rw-r--r--app/assets/javascripts/dispatcher.js89
-rw-r--r--app/assets/javascripts/droplab/constants.js9
-rw-r--r--app/assets/javascripts/droplab/drop_down.js4
-rw-r--r--app/assets/javascripts/droplab/drop_lab.js5
-rw-r--r--app/assets/javascripts/droplab/keyboard.js53
-rw-r--r--app/assets/javascripts/droplab/plugins/ajax.js4
-rw-r--r--app/assets/javascripts/droplab/plugins/ajax_filter.js18
-rw-r--r--app/assets/javascripts/droplab/plugins/filter.js39
-rw-r--r--app/assets/javascripts/droplab/plugins/input_setter.js4
-rw-r--r--app/assets/javascripts/droplab/utils.js15
-rw-r--r--app/assets/javascripts/dropzone_input.js66
-rw-r--r--app/assets/javascripts/due_date_select.js10
-rw-r--r--app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js55
-rw-r--r--app/assets/javascripts/environments/components/container.vue72
-rw-r--r--app/assets/javascripts/environments/components/empty_state.vue61
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.vue54
-rw-r--r--app/assets/javascripts/environments/components/environment_external_url.vue9
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue100
-rw-r--r--app/assets/javascripts/environments/components/environment_monitoring.vue15
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.vue25
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.vue14
-rw-r--r--app/assets/javascripts/environments/components/environment_terminal_button.vue7
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue154
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue10
-rw-r--r--app/assets/javascripts/environments/components/stop_environment_modal.vue6
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js55
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue71
-rw-r--r--app/assets/javascripts/environments/index.js63
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js22
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js30
-rw-r--r--app/assets/javascripts/experimental_flags.js2
-rw-r--r--app/assets/javascripts/feature_highlight/feature_highlight.js18
-rw-r--r--app/assets/javascripts/feature_highlight/feature_highlight_helper.js18
-rw-r--r--app/assets/javascripts/files_comment_button.js14
-rw-r--r--app/assets/javascripts/filterable_list.js25
-rw-r--r--app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js24
-rw-r--r--app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue12
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_emoji.js26
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js25
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_non_user.js8
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js7
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js58
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js14
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js47
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js149
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_token_keys.js166
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_tokenizer.js63
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js156
-rw-r--r--app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js113
-rw-r--r--app/assets/javascripts/filtered_search/null_dropdown.js9
-rw-r--r--app/assets/javascripts/filtered_search/recent_searches_root.js11
-rw-r--r--app/assets/javascripts/filtered_search/stores/recent_searches_store.js13
-rw-r--r--app/assets/javascripts/flash.js45
-rw-r--r--app/assets/javascripts/fly_out_nav.js56
-rw-r--r--app/assets/javascripts/frequent_items/components/app.vue8
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue2
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue13
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js123
-rw-r--r--app/assets/javascripts/gl_dropdown.js2
-rw-r--r--app/assets/javascripts/gl_field_error.js3
-rw-r--r--app/assets/javascripts/gl_field_errors.js16
-rw-r--r--app/assets/javascripts/gl_form.js21
-rw-r--r--app/assets/javascripts/group.js12
-rw-r--r--app/assets/javascripts/group_avatar.js5
-rw-r--r--app/assets/javascripts/group_label_subscription.js10
-rw-r--r--app/assets/javascripts/groups/components/app.vue86
-rw-r--r--app/assets/javascripts/groups/components/group_folder.vue7
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue16
-rw-r--r--app/assets/javascripts/groups/components/groups.vue91
-rw-r--r--app/assets/javascripts/groups/components/item_actions.vue11
-rw-r--r--app/assets/javascripts/groups/components/item_stats.vue72
-rw-r--r--app/assets/javascripts/groups/components/item_stats_value.vue86
-rw-r--r--app/assets/javascripts/groups/constants.js24
-rw-r--r--app/assets/javascripts/groups/groups_filterable_list.js64
-rw-r--r--app/assets/javascripts/groups/index.js31
-rw-r--r--app/assets/javascripts/groups/new_group_child.js22
-rw-r--r--app/assets/javascripts/groups/store/groups_store.js23
-rw-r--r--app/assets/javascripts/groups/transfer_dropdown.js2
-rw-r--r--app/assets/javascripts/groups_select.js9
-rw-r--r--app/assets/javascripts/header.js55
-rw-r--r--app/assets/javascripts/helpers/avatar_helper.js4
-rw-r--r--app/assets/javascripts/ide/components/branches/search_list.vue10
-rw-r--r--app/assets/javascripts/ide/components/changed_file_icon.vue90
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue79
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/form.vue4
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list.vue77
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue14
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue25
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue58
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue11
-rw-r--r--app/assets/javascripts/ide/components/error_message.vue6
-rw-r--r--app/assets/javascripts/ide/components/file_finder/index.vue2
-rw-r--r--app/assets/javascripts/ide/components/file_finder/item.vue14
-rw-r--r--app/assets/javascripts/ide/components/file_row_extra.vue104
-rw-r--r--app/assets/javascripts/ide/components/file_templates/bar.vue80
-rw-r--r--app/assets/javascripts/ide/components/file_templates/dropdown.vue125
-rw-r--r--app/assets/javascripts/ide/components/ide.vue27
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue18
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue6
-rw-r--r--app/assets/javascripts/ide/components/ide_tree.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue20
-rw-r--r--app/assets/javascripts/ide/components/jobs/list.vue8
-rw-r--r--app/assets/javascripts/ide/components/jobs/stage.vue8
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/list.vue16
-rw-r--r--app/assets/javascripts/ide/components/nav_dropdown_button.vue4
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue49
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/upload.vue46
-rw-r--r--app/assets/javascripts/ide/components/panes/right.vue130
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue10
-rw-r--r--app/assets/javascripts/ide/components/preview/clientside.vue8
-rw-r--r--app/assets/javascripts/ide/components/preview/navigator.vue6
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue10
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue22
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue225
-rw-r--r--app/assets/javascripts/ide/components/repo_file_status_icon.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_loading_file.vue42
-rw-r--r--app/assets/javascripts/ide/components/repo_tab.vue2
-rw-r--r--app/assets/javascripts/ide/components/shared/tokened_input.vue4
-rw-r--r--app/assets/javascripts/ide/constants.js8
-rw-r--r--app/assets/javascripts/ide/index.js35
-rw-r--r--app/assets/javascripts/ide/lib/diff/diff.js14
-rw-r--r--app/assets/javascripts/ide/lib/diff/diff_worker.js2
-rw-r--r--app/assets/javascripts/ide/stores/actions.js26
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js38
-rw-r--r--app/assets/javascripts/ide/stores/actions/merge_request.js94
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js36
-rw-r--r--app/assets/javascripts/ide/stores/getters.js2
-rw-r--r--app/assets/javascripts/ide/stores/index.js4
-rw-r--r--app/assets/javascripts/ide/stores/modules/branches/mutations.js1
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js6
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/actions.js117
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/getters.js26
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/index.js12
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/mutation_types.js7
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/mutations.js21
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/state.js6
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js1
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/actions.js30
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/getters.js4
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/index.js12
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/mutation_types.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/mutations.js19
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/state.js5
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/actions.js4
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/mutations.js1
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js38
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js3
-rw-r--r--app/assets/javascripts/ide/stores/state.js1
-rw-r--r--app/assets/javascripts/image_diff/image_diff.js6
-rw-r--r--app/assets/javascripts/image_diff/init_discussion_tab.js3
-rw-r--r--app/assets/javascripts/image_diff/replaced_image_diff.js15
-rw-r--r--app/assets/javascripts/importer_status.js122
-rw-r--r--app/assets/javascripts/init_changes_dropdown.js2
-rw-r--r--app/assets/javascripts/init_legacy_filters.js14
-rw-r--r--app/assets/javascripts/init_notes.js8
-rw-r--r--app/assets/javascripts/integrations/integration_settings_form.js5
-rw-r--r--app/assets/javascripts/issuable/auto_width_dropdown_select.js5
-rw-r--r--app/assets/javascripts/issuable_bulk_update_actions.js20
-rw-r--r--app/assets/javascripts/issuable_bulk_update_sidebar.js6
-rw-r--r--app/assets/javascripts/issuable_form.js34
-rw-r--r--app/assets/javascripts/issuable_index.js9
-rw-r--r--app/assets/javascripts/issue.js75
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue517
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue181
-rw-r--r--app/assets/javascripts/issue_show/components/edit_actions.vue99
-rw-r--r--app/assets/javascripts/issue_show/components/edited.vue51
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue77
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description_template.vue76
-rw-r--r--app/assets/javascripts/issue_show/components/fields/title.vue20
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue139
-rw-r--r--app/assets/javascripts/issue_show/components/locked_warning.vue12
-rw-r--r--app/assets/javascripts/issue_show/components/title.vue5
-rw-r--r--app/assets/javascripts/issue_show/index.js7
-rw-r--r--app/assets/javascripts/issue_show/stores/index.js6
-rw-r--r--app/assets/javascripts/job.js213
-rw-r--r--app/assets/javascripts/jobs/components/artifacts_block.vue86
-rw-r--r--app/assets/javascripts/jobs/components/commit_block.vue58
-rw-r--r--app/assets/javascripts/jobs/components/empty_state.vue81
-rw-r--r--app/assets/javascripts/jobs/components/environments_block.vue140
-rw-r--r--app/assets/javascripts/jobs/components/erased_block.vue47
-rw-r--r--app/assets/javascripts/jobs/components/header.vue97
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue309
-rw-r--r--app/assets/javascripts/jobs/components/job_container_item.vue76
-rw-r--r--app/assets/javascripts/jobs/components/job_log.vue61
-rw-r--r--app/assets/javascripts/jobs/components/job_log_controllers.vue146
-rw-r--r--app/assets/javascripts/jobs/components/jobs_container.vue35
-rw-r--r--app/assets/javascripts/jobs/components/sidebar.vue291
-rw-r--r--app/assets/javascripts/jobs/components/sidebar_detail_row.vue59
-rw-r--r--app/assets/javascripts/jobs/components/sidebar_details_block.vue241
-rw-r--r--app/assets/javascripts/jobs/components/stages_dropdown.vue85
-rw-r--r--app/assets/javascripts/jobs/components/stuck_block.vue28
-rw-r--r--app/assets/javascripts/jobs/components/trigger_block.vue82
-rw-r--r--app/assets/javascripts/jobs/index.js25
-rw-r--r--app/assets/javascripts/jobs/job_details_bundle.js62
-rw-r--r--app/assets/javascripts/jobs/job_details_mediator.js67
-rw-r--r--app/assets/javascripts/jobs/mixins/delayed_job_mixin.js50
-rw-r--r--app/assets/javascripts/jobs/services/job_service.js11
-rw-r--r--app/assets/javascripts/jobs/store/actions.js237
-rw-r--r--app/assets/javascripts/jobs/store/getters.js53
-rw-r--r--app/assets/javascripts/jobs/store/index.js16
-rw-r--r--app/assets/javascripts/jobs/store/mutation_types.js34
-rw-r--r--app/assets/javascripts/jobs/store/mutations.js128
-rw-r--r--app/assets/javascripts/jobs/store/state.js33
-rw-r--r--app/assets/javascripts/jobs/stores/job_store.js11
-rw-r--r--app/assets/javascripts/jobs/svg/scroll_down.svg5
-rw-r--r--app/assets/javascripts/label_manager.js16
-rw-r--r--app/assets/javascripts/labels.js2
-rw-r--r--app/assets/javascripts/labels_select.js259
-rw-r--r--app/assets/javascripts/layout_nav.js60
-rw-r--r--app/assets/javascripts/lazy_loader.js107
-rw-r--r--app/assets/javascripts/lib/ace.js1
-rw-r--r--app/assets/javascripts/lib/utils/ace_utils.js6
-rw-r--r--app/assets/javascripts/lib/utils/ajax_cache.js7
-rw-r--r--app/assets/javascripts/lib/utils/axios_utils.js23
-rw-r--r--app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js10
-rw-r--r--app/assets/javascripts/lib/utils/cache.js2
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js110
-rw-r--r--app/assets/javascripts/lib/utils/datefix.js29
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js149
-rw-r--r--app/assets/javascripts/lib/utils/logoutput_behaviours.js17
-rw-r--r--app/assets/javascripts/lib/utils/navigation_utility.js15
-rw-r--r--app/assets/javascripts/lib/utils/notify.js6
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js2
-rw-r--r--app/assets/javascripts/lib/utils/pretty_time.js61
-rw-r--r--app/assets/javascripts/lib/utils/regexp.js3
-rw-r--r--app/assets/javascripts/lib/utils/scroll_utils.js24
-rw-r--r--app/assets/javascripts/lib/utils/simple_poll.js2
-rw-r--r--app/assets/javascripts/lib/utils/sticky.js24
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js108
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js24
-rw-r--r--app/assets/javascripts/lib/utils/tick_formats.js2
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js4
-rw-r--r--app/assets/javascripts/lib/utils/users_cache.js25
-rw-r--r--app/assets/javascripts/line_highlighter.js30
-rw-r--r--app/assets/javascripts/locale/ensure_single_line.js25
-rw-r--r--app/assets/javascripts/locale/index.js13
-rw-r--r--app/assets/javascripts/locale/sprintf.js2
-rw-r--r--app/assets/javascripts/main.js17
-rw-r--r--app/assets/javascripts/member_expiration_date.js10
-rw-r--r--app/assets/javascripts/members.js10
-rw-r--r--app/assets/javascripts/merge_conflicts/components/diff_file_editor.js22
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js2
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_store.js53
-rw-r--r--app/assets/javascripts/merge_request.js25
-rw-r--r--app/assets/javascripts/merge_request_tabs.js24
-rw-r--r--app/assets/javascripts/milestone.js34
-rw-r--r--app/assets/javascripts/milestone_select.js12
-rw-r--r--app/assets/javascripts/mini_pipeline_graph_dropdown.js17
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue49
-rw-r--r--app/assets/javascripts/monitoring/components/graph.vue79
-rw-r--r--app/assets/javascripts/monitoring/components/graph/axis.vue21
-rw-r--r--app/assets/javascripts/monitoring/components/graph/flag.vue7
-rw-r--r--app/assets/javascripts/monitoring/components/graph/legend.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/graph/track_info.vue1
-rw-r--r--app/assets/javascripts/monitoring/components/graph/track_line.vue1
-rw-r--r--app/assets/javascripts/monitoring/mixins/monitoring_mixins.js17
-rw-r--r--app/assets/javascripts/monitoring/services/monitoring_service.js49
-rw-r--r--app/assets/javascripts/monitoring/utils/measurements.js6
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js130
-rw-r--r--app/assets/javascripts/mr_notes/index.js2
-rw-r--r--app/assets/javascripts/mr_notes/stores/index.js17
-rw-r--r--app/assets/javascripts/namespace_select.js10
-rw-r--r--app/assets/javascripts/network/branch_graph.js164
-rw-r--r--app/assets/javascripts/network/raphael.js2
-rw-r--r--app/assets/javascripts/new_branch_form.js16
-rw-r--r--app/assets/javascripts/new_commit_form.js4
-rw-r--r--app/assets/javascripts/notebook/cells/code/index.vue72
-rw-r--r--app/assets/javascripts/notebook/cells/markdown.vue129
-rw-r--r--app/assets/javascripts/notebook/cells/output/html.vue44
-rw-r--r--app/assets/javascripts/notebook/cells/output/image.vue28
-rw-r--r--app/assets/javascripts/notebook/cells/output/index.vue128
-rw-r--r--app/assets/javascripts/notebook/cells/prompt.vue44
-rw-r--r--app/assets/javascripts/notebook/index.vue81
-rw-r--r--app/assets/javascripts/notes.js71
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue36
-rw-r--r--app/assets/javascripts/notes/components/diff_file_header.vue94
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue73
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue35
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter.vue96
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue119
-rw-r--r--app/assets/javascripts/notes/components/note_awards_list.vue82
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue14
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue29
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue55
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue221
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue41
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue38
-rw-r--r--app/assets/javascripts/notes/components/toggle_replies_widget.vue94
-rw-r--r--app/assets/javascripts/notes/constants.js2
-rw-r--r--app/assets/javascripts/notes/discussion_filters.js35
-rw-r--r--app/assets/javascripts/notes/index.js3
-rw-r--r--app/assets/javascripts/notes/services/notes_service.js5
-rw-r--r--app/assets/javascripts/notes/stores/actions.js137
-rw-r--r--app/assets/javascripts/notes/stores/collapse_utils.js7
-rw-r--r--app/assets/javascripts/notes/stores/getters.js23
-rw-r--r--app/assets/javascripts/notes/stores/index.js13
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js6
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js2
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js46
-rw-r--r--app/assets/javascripts/notes/stores/utils.js9
-rw-r--r--app/assets/javascripts/notifications_dropdown.js4
-rw-r--r--app/assets/javascripts/notifications_form.js10
-rw-r--r--app/assets/javascripts/pager.js44
-rw-r--r--app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js2
-rw-r--r--app/assets/javascripts/pages/admin/admin.js15
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/account_and_limits.js29
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/index.js8
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js62
-rw-r--r--app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js36
-rw-r--r--app/assets/javascripts/pages/admin/cohorts/index.js3
-rw-r--r--app/assets/javascripts/pages/admin/cohorts/usage_ping.js13
-rw-r--r--app/assets/javascripts/pages/admin/index.js6
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue65
-rw-r--r--app/assets/javascripts/pages/admin/projects/index.js3
-rw-r--r--app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue139
-rw-r--r--app/assets/javascripts/pages/admin/projects/index/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/runners/index.js10
-rw-r--r--app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue192
-rw-r--r--app/assets/javascripts/pages/admin/users/index.js6
-rw-r--r--app/assets/javascripts/pages/admin/users/new/index.js51
-rw-r--r--app/assets/javascripts/pages/constants.js1
-rw-r--r--app/assets/javascripts/pages/dashboard/groups/index/index.js4
-rw-r--r--app/assets/javascripts/pages/dashboard/issues/index.js10
-rw-r--r--app/assets/javascripts/pages/dashboard/merge_requests/index.js12
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js25
-rw-r--r--app/assets/javascripts/pages/groups/boards/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/clusters/destroy/index.js5
-rw-r--r--app/assets/javascripts/pages/groups/clusters/index/index.js5
-rw-r--r--app/assets/javascripts/pages/groups/clusters/show/index.js5
-rw-r--r--app/assets/javascripts/pages/groups/clusters/update/index.js5
-rw-r--r--app/assets/javascripts/pages/groups/edit/index.js14
-rw-r--r--app/assets/javascripts/pages/groups/index.js16
-rw-r--r--app/assets/javascripts/pages/groups/issues/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/merge_requests/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/milestones/show/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/show/group_tabs.js136
-rw-r--r--app/assets/javascripts/pages/groups/show/index.js14
-rw-r--r--app/assets/javascripts/pages/ide/index.js10
-rw-r--r--app/assets/javascripts/pages/instance_statistics/conversational_development_index/index.js (renamed from app/assets/javascripts/pages/admin/conversational_development_index/show/index.js)0
-rw-r--r--app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue169
-rw-r--r--app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue99
-rw-r--r--app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js16
-rw-r--r--app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js16
-rw-r--r--app/assets/javascripts/pages/profiles/index.js7
-rw-r--r--app/assets/javascripts/pages/profiles/show/index.js34
-rw-r--r--app/assets/javascripts/pages/profiles/two_factor_auths/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/activity/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/artifacts/browse/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/artifacts/file/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/boards/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/branches/new/index.js11
-rw-r--r--app/assets/javascripts/pages/projects/clusters/gcp/new/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/clusters/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/commits/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/find_file/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/graphs/charts/index.js22
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js93
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js223
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js66
-rw-r--r--app/assets/javascripts/pages/projects/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/init_blob.js7
-rw-r--r--app/assets/javascripts/pages/projects/init_form.js2
-rw-r--r--app/assets/javascripts/pages/projects/issues/form.js2
-rw-r--r--app/assets/javascripts/pages/projects/issues/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/issues/show.js7
-rw-r--r--app/assets/javascripts/pages/projects/jobs/index/index.js16
-rw-r--r--app/assets/javascripts/pages/projects/jobs/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue124
-rw-r--r--app/assets/javascripts/pages/projects/labels/index/index.js16
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/index/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js2
-rw-r--r--app/assets/javascripts/pages/projects/network/network.js12
-rw-r--r--app/assets/javascripts/pages/projects/network/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js22
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue104
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue46
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js3
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js4
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/charts/index.js59
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/index/index.js66
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/init_pipelines.js7
-rw-r--r--app/assets/javascripts/pages/projects/project.js69
-rw-r--r--app/assets/javascripts/pages/projects/settings/badges/index/index.js10
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue114
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue36
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue344
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/constants.js6
-rw-r--r--app/assets/javascripts/pages/projects/shared/project_avatar.js5
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/tree/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue70
-rw-r--r--app/assets/javascripts/pages/projects/wikis/index.js15
-rw-r--r--app/assets/javascripts/pages/search/show/search.js25
-rw-r--r--app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js2
-rw-r--r--app/assets/javascripts/pages/sessions/new/username_validator.js12
-rw-r--r--app/assets/javascripts/pages/snippets/form.js1
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js26
-rw-r--r--app/assets/javascripts/pages/users/index.js6
-rw-r--r--app/assets/javascripts/pages/users/user_overview_block.js52
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js103
-rw-r--r--app/assets/javascripts/pdf/index.vue103
-rw-r--r--app/assets/javascripts/pdf/page/index.vue103
-rw-r--r--app/assets/javascripts/performance_bar/components/performance_bar_app.vue19
-rw-r--r--app/assets/javascripts/performance_bar/components/simple_metric.vue17
-rw-r--r--app/assets/javascripts/performance_bar/index.js7
-rw-r--r--app/assets/javascripts/performance_bar/services/performance_bar_service.js28
-rw-r--r--app/assets/javascripts/performance_bar/stores/performance_bar_store.js4
-rw-r--r--app/assets/javascripts/pipelines/components/empty_state.vue12
-rw-r--r--app/assets/javascripts/pipelines/components/graph/action_component.vue24
-rw-r--r--app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue123
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue12
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_component.vue143
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue99
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_item.vue145
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue46
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/nav_controls.vue18
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_url.vue45
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue42
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue61
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_artifacts.vue26
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table.vue10
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue32
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue18
-rw-r--r--app/assets/javascripts/pipelines/components/time_ago.vue5
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines.js42
-rw-r--r--app/assets/javascripts/preview_markdown.js211
-rw-r--r--app/assets/javascripts/profile/account/components/delete_account_modal.vue126
-rw-r--r--app/assets/javascripts/profile/gl_crop.js49
-rw-r--r--app/assets/javascripts/profile/profile.js6
-rw-r--r--app/assets/javascripts/project_find_file.js97
-rw-r--r--app/assets/javascripts/project_import.js1
-rw-r--r--app/assets/javascripts/project_label_subscription.js43
-rw-r--r--app/assets/javascripts/project_select.js59
-rw-r--r--app/assets/javascripts/project_select_combo_button.js27
-rw-r--r--app/assets/javascripts/project_visibility.js5
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js4
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/project_import_gitlab_project.js16
-rw-r--r--app/assets/javascripts/projects/project_new.js59
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue8
-rw-r--r--app/assets/javascripts/prometheus_metrics/prometheus_metrics.js36
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_create.js8
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit.js59
-rw-r--r--app/assets/javascripts/protected_tags/protected_tag_create.js4
-rw-r--r--app/assets/javascripts/protected_tags/protected_tag_edit.js37
-rw-r--r--app/assets/javascripts/raven/raven_config.js2
-rw-r--r--app/assets/javascripts/read_more.js41
-rw-r--r--app/assets/javascripts/ref_select_dropdown.js5
-rw-r--r--app/assets/javascripts/registry/components/app.vue73
-rw-r--r--app/assets/javascripts/registry/components/collapsible_container.vue105
-rw-r--r--app/assets/javascripts/registry/components/table_registry.vue104
-rw-r--r--app/assets/javascripts/registry/index.js39
-rw-r--r--app/assets/javascripts/registry/stores/mutations.js1
-rw-r--r--app/assets/javascripts/reports/components/grouped_test_reports_app.vue133
-rw-r--r--app/assets/javascripts/reports/components/issue_status_icon.vue6
-rw-r--r--app/assets/javascripts/reports/components/issues_list.vue89
-rw-r--r--app/assets/javascripts/reports/components/modal.vue40
-rw-r--r--app/assets/javascripts/reports/components/report_issues.vue59
-rw-r--r--app/assets/javascripts/reports/components/report_item.vue53
-rw-r--r--app/assets/javascripts/reports/components/report_section.vue2
-rw-r--r--app/assets/javascripts/reports/components/summary_row.vue6
-rw-r--r--app/assets/javascripts/reports/components/test_issue_body.vue42
-rw-r--r--app/assets/javascripts/reports/store/actions.js8
-rw-r--r--app/assets/javascripts/reports/store/index.js13
-rw-r--r--app/assets/javascripts/reports/store/mutation_types.js1
-rw-r--r--app/assets/javascripts/reports/store/mutations.js4
-rw-r--r--app/assets/javascripts/reports/store/state.js1
-rw-r--r--app/assets/javascripts/right_sidebar.js61
-rw-r--r--app/assets/javascripts/search_autocomplete.js23
-rw-r--r--app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js21
-rw-r--r--app/assets/javascripts/set_status_modal/event_hub.js3
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue33
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue246
-rw-r--r--app/assets/javascripts/settings_panels.js16
-rw-r--r--app/assets/javascripts/shared/milestones/form.js1
-rw-r--r--app/assets/javascripts/shortcuts.js124
-rw-r--r--app/assets/javascripts/shortcuts_blob.js26
-rw-r--r--app/assets/javascripts/shortcuts_dashboard_navigation.js15
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js77
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js28
-rw-r--r--app/assets/javascripts/shortcuts_wiki.js14
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue3
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue3
-rw-r--r--app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue25
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue140
-rw-r--r--app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue34
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue124
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue192
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/help_state.vue14
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue14
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue42
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/todo.vue12
-rw-r--r--app/assets/javascripts/sidebar/lib/sidebar_move_issue.js20
-rw-r--r--app/assets/javascripts/sidebar/mount_milestone_sidebar.js21
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js39
-rw-r--r--app/assets/javascripts/sidebar/services/sidebar_service.js14
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js20
-rw-r--r--app/assets/javascripts/single_file_diff.js34
-rw-r--r--app/assets/javascripts/smart_interval.js20
-rw-r--r--app/assets/javascripts/star.js8
-rw-r--r--app/assets/javascripts/task_list.js9
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js20
-rw-r--r--app/assets/javascripts/terminal/terminal.js12
-rw-r--r--app/assets/javascripts/test_utils/simulate_drag.js56
-rw-r--r--app/assets/javascripts/test_utils/simulate_input.js2
-rw-r--r--app/assets/javascripts/toggle_buttons.js2
-rw-r--r--app/assets/javascripts/tree.js20
-rw-r--r--app/assets/javascripts/u2f/authenticate.js14
-rw-r--r--app/assets/javascripts/u2f/register.js13
-rw-r--r--app/assets/javascripts/u2f/util.js5
-rw-r--r--app/assets/javascripts/ui_development_kit.js18
-rw-r--r--app/assets/javascripts/usage_ping_consent.js31
-rw-r--r--app/assets/javascripts/users_select.js1101
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue149
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue32
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue56
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue52
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue109
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue36
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue38
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue50
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue56
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue22
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue56
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue32
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue28
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue142
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue220
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue26
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue68
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue148
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.js15
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue63
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue40
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue63
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js47
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/ee_switch_mr_widget_options.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/index.js8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue206
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/services/ee_switch_mr_widget_service.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_get_state_key.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_mr_widget_store.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_state_maps.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js5
-rw-r--r--app/assets/javascripts/vue_shared/components/bar_chart.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/changed_file_icon.vue110
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_badge_link.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/clipboard_button.vue18
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue9
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue75
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/deprecated_modal.vue150
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue15
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue63
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue37
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue82
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/file_icon.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js5
-rw-r--r--app/assets/javascripts/vue_shared/components/file_row.vue269
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue145
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_countdown.vue53
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/header_ci_component.vue76
-rw-r--r--app/assets/javascripts/vue_shared/components/icon.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/issue_warning.vue51
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_button.vue65
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_icon.vue45
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue235
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue121
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar.vue59
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue70
-rw-r--r--app/assets/javascripts/vue_shared/components/memory_graph.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue68
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_system_note.vue32
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue26
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/system_note.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination_links.vue38
-rw-r--r--app/assets/javascripts/vue_shared/components/panel_resizer.vue156
-rw-r--r--app/assets/javascripts/vue_shared/components/pikaday.vue106
-rw-r--r--app/assets/javascripts/vue_shared/components/project_avatar/image.vue123
-rw-r--r--app/assets/javascripts/vue_shared/components/recaptcha_modal.vue94
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue62
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue143
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue188
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue37
-rw-r--r--app/assets/javascripts/vue_shared/components/smart_virtual_list.vue42
-rw-r--r--app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/table_pagination.vue206
-rw-r--r--app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/toggle_button.vue100
-rw-r--r--app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue67
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue53
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue19
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_svg.vue2
-rw-r--r--app/assets/javascripts/vue_shared/directives/tooltip.js8
-rw-r--r--app/assets/javascripts/vue_shared/mixins/ci_pagination_api_mixin.js28
-rw-r--r--app/assets/javascripts/vue_shared/models/label.js2
-rw-r--r--app/assets/javascripts/vue_shared/translate.js9
-rw-r--r--app/assets/javascripts/vue_shared/vue_resource_interceptor.js4
-rw-r--r--app/assets/javascripts/zen_mode.js34
-rw-r--r--app/assets/stylesheets/application.scss2
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss24
-rw-r--r--app/assets/stylesheets/framework.scss3
-rw-r--r--app/assets/stylesheets/framework/avatar.scss15
-rw-r--r--app/assets/stylesheets/framework/awards.scss13
-rw-r--r--app/assets/stylesheets/framework/badges.scss2
-rw-r--r--app/assets/stylesheets/framework/blocks.scss11
-rw-r--r--app/assets/stylesheets/framework/buttons.scss74
-rw-r--r--app/assets/stylesheets/framework/calendar.scss12
-rw-r--r--app/assets/stylesheets/framework/callout.scss24
-rw-r--r--app/assets/stylesheets/framework/common.scss175
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss14
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss40
-rw-r--r--app/assets/stylesheets/framework/emojis.scss14
-rw-r--r--app/assets/stylesheets/framework/feature_highlight.scss4
-rw-r--r--app/assets/stylesheets/framework/files.scss24
-rw-r--r--app/assets/stylesheets/framework/filters.scss15
-rw-r--r--app/assets/stylesheets/framework/flash.scss4
-rw-r--r--app/assets/stylesheets/framework/forms.scss4
-rw-r--r--app/assets/stylesheets/framework/gfm.scss4
-rw-r--r--app/assets/stylesheets/framework/gitlab_theme.scss1
-rw-r--r--app/assets/stylesheets/framework/header.scss43
-rw-r--r--app/assets/stylesheets/framework/icons.scss3
-rw-r--r--app/assets/stylesheets/framework/images.scss4
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss6
-rw-r--r--app/assets/stylesheets/framework/jquery.scss15
-rw-r--r--app/assets/stylesheets/framework/layout.scss58
-rw-r--r--app/assets/stylesheets/framework/lists.scss19
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss5
-rw-r--r--app/assets/stylesheets/framework/mixins.scss172
-rw-r--r--app/assets/stylesheets/framework/mobile.scss13
-rw-r--r--app/assets/stylesheets/framework/modal.scss13
-rw-r--r--app/assets/stylesheets/framework/page_title.scss18
-rw-r--r--app/assets/stylesheets/framework/panels.scss5
-rw-r--r--app/assets/stylesheets/framework/read_more.scss13
-rw-r--r--app/assets/stylesheets/framework/responsive_tables.scss2
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss20
-rw-r--r--app/assets/stylesheets/framework/selects.scss12
-rw-r--r--app/assets/stylesheets/framework/snippets.scss2
-rw-r--r--app/assets/stylesheets/framework/tables.scss6
-rw-r--r--app/assets/stylesheets/framework/terms.scss15
-rw-r--r--app/assets/stylesheets/framework/timeline.scss6
-rw-r--r--app/assets/stylesheets/framework/toggle.scss8
-rw-r--r--app/assets/stylesheets/framework/typography.scss22
-rw-r--r--app/assets/stylesheets/framework/variables.scss246
-rw-r--r--app/assets/stylesheets/framework/variables_overrides.scss5
-rw-r--r--app/assets/stylesheets/framework/zen.scss4
-rw-r--r--app/assets/stylesheets/notify.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss277
-rw-r--r--app/assets/stylesheets/page_bundles/xterm.scss1455
-rw-r--r--app/assets/stylesheets/pages/admin.scss4
-rw-r--r--app/assets/stylesheets/pages/boards.scss214
-rw-r--r--app/assets/stylesheets/pages/branches.scss4
-rw-r--r--app/assets/stylesheets/pages/builds.scss123
-rw-r--r--app/assets/stylesheets/pages/clusters.scss92
-rw-r--r--app/assets/stylesheets/pages/commits.scss7
-rw-r--r--app/assets/stylesheets/pages/convdev_index.scss12
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss8
-rw-r--r--app/assets/stylesheets/pages/diff.scss178
-rw-r--r--app/assets/stylesheets/pages/editor.scss4
-rw-r--r--app/assets/stylesheets/pages/environments.scss18
-rw-r--r--app/assets/stylesheets/pages/events.scss82
-rw-r--r--app/assets/stylesheets/pages/graph.scss8
-rw-r--r--app/assets/stylesheets/pages/groups.scss66
-rw-r--r--app/assets/stylesheets/pages/help.scss4
-rw-r--r--app/assets/stylesheets/pages/issuable.scss53
-rw-r--r--app/assets/stylesheets/pages/issues.scss27
-rw-r--r--app/assets/stylesheets/pages/labels.scss10
-rw-r--r--app/assets/stylesheets/pages/login.scss31
-rw-r--r--app/assets/stylesheets/pages/members.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss86
-rw-r--r--app/assets/stylesheets/pages/milestone.scss2
-rw-r--r--app/assets/stylesheets/pages/note_form.scss38
-rw-r--r--app/assets/stylesheets/pages/notes.scss469
-rw-r--r--app/assets/stylesheets/pages/pages.scss7
-rw-r--r--app/assets/stylesheets/pages/pipeline_schedules.scss8
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss13
-rw-r--r--app/assets/stylesheets/pages/profile.scss32
-rw-r--r--app/assets/stylesheets/pages/projects.scss275
-rw-r--r--app/assets/stylesheets/pages/runners.scss10
-rw-r--r--app/assets/stylesheets/pages/search.scss12
-rw-r--r--app/assets/stylesheets/pages/settings.scss20
-rw-r--r--app/assets/stylesheets/pages/stat_graph.scss4
-rw-r--r--app/assets/stylesheets/pages/status.scss1
-rw-r--r--app/assets/stylesheets/pages/todos.scss10
-rw-r--r--app/assets/stylesheets/pages/tree.scss8
-rw-r--r--app/assets/stylesheets/pages/ui_dev_kit.scss2
-rw-r--r--app/assets/stylesheets/pages/xterm.scss1453
-rw-r--r--app/assets/stylesheets/performance_bar.scss13
-rw-r--r--app/controllers/abuse_reports_controller.rb4
-rw-r--r--app/controllers/admin/abuse_reports_controller.rb4
-rw-r--r--app/controllers/admin/appearances_controller.rb8
-rw-r--r--app/controllers/admin/application_controller.rb2
-rw-r--r--app/controllers/admin/application_settings_controller.rb64
-rw-r--r--app/controllers/admin/applications_controller.rb6
-rw-r--r--app/controllers/admin/background_jobs_controller.rb7
-rw-r--r--app/controllers/admin/broadcast_messages_controller.rb4
-rw-r--r--app/controllers/admin/dashboard_controller.rb8
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb2
-rw-r--r--app/controllers/admin/gitaly_servers_controller.rb2
-rw-r--r--app/controllers/admin/groups_controller.rb4
-rw-r--r--app/controllers/admin/health_check_controller.rb9
-rw-r--r--app/controllers/admin/hook_logs_controller.rb2
-rw-r--r--app/controllers/admin/hooks_controller.rb2
-rw-r--r--app/controllers/admin/identities_controller.rb4
-rw-r--r--app/controllers/admin/impersonation_tokens_controller.rb9
-rw-r--r--app/controllers/admin/impersonations_controller.rb2
-rw-r--r--app/controllers/admin/jobs_controller.rb4
-rw-r--r--app/controllers/admin/keys_controller.rb4
-rw-r--r--app/controllers/admin/labels_controller.rb2
-rw-r--r--app/controllers/admin/logs_controller.rb5
-rw-r--r--app/controllers/admin/projects_controller.rb6
-rw-r--r--app/controllers/admin/requests_profiles_controller.rb2
-rw-r--r--app/controllers/admin/runner_projects_controller.rb2
-rw-r--r--app/controllers/admin/runners_controller.rb13
-rw-r--r--app/controllers/admin/services_controller.rb4
-rw-r--r--app/controllers/admin/spam_logs_controller.rb4
-rw-r--r--app/controllers/admin/system_info_controller.rb2
-rw-r--r--app/controllers/admin/users_controller.rb4
-rw-r--r--app/controllers/application_controller.rb64
-rw-r--r--app/controllers/autocomplete_controller.rb74
-rw-r--r--app/controllers/boards/application_controller.rb2
-rw-r--r--app/controllers/boards/issues_controller.rb21
-rw-r--r--app/controllers/boards/lists_controller.rb2
-rw-r--r--app/controllers/chaos_controller.rb56
-rw-r--r--app/controllers/ci/lints_controller.rb2
-rw-r--r--app/controllers/clusters/applications_controller.rb28
-rw-r--r--app/controllers/clusters/base_controller.rb37
-rw-r--r--app/controllers/clusters/clusters_controller.rb218
-rw-r--r--app/controllers/concerns/accepts_pending_invitations.rb2
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb4
-rw-r--r--app/controllers/concerns/boards_responses.rb7
-rw-r--r--app/controllers/concerns/checks_collaboration.rb2
-rw-r--r--app/controllers/concerns/continue_params.rb2
-rw-r--r--app/controllers/concerns/controller_with_cross_project_access_check.rb2
-rw-r--r--app/controllers/concerns/creates_commit.rb6
-rw-r--r--app/controllers/concerns/cycle_analytics_params.rb2
-rw-r--r--app/controllers/concerns/diff_for_path.rb2
-rw-r--r--app/controllers/concerns/enforces_two_factor_authentication.rb4
-rw-r--r--app/controllers/concerns/group_tree.rb6
-rw-r--r--app/controllers/concerns/hooks_execution.rb2
-rw-r--r--app/controllers/concerns/internal_redirect.rb8
-rw-r--r--app/controllers/concerns/invalid_utf8_error_handler.rb27
-rw-r--r--app/controllers/concerns/issuable_actions.rb36
-rw-r--r--app/controllers/concerns/issuable_collections.rb62
-rw-r--r--app/controllers/concerns/issues_action.rb2
-rw-r--r--app/controllers/concerns/issues_calendar.rb4
-rw-r--r--app/controllers/concerns/labels_as_hash.rb28
-rw-r--r--app/controllers/concerns/lfs_request.rb2
-rw-r--r--app/controllers/concerns/members_presentation.rb7
-rw-r--r--app/controllers/concerns/membership_actions.rb4
-rw-r--r--app/controllers/concerns/merge_requests_action.rb4
-rw-r--r--app/controllers/concerns/milestone_actions.rb2
-rw-r--r--app/controllers/concerns/notes_actions.rb66
-rw-r--r--app/controllers/concerns/oauth_applications.rb2
-rw-r--r--app/controllers/concerns/params_backward_compatibility.rb2
-rw-r--r--app/controllers/concerns/preview_markdown.rb2
-rw-r--r--app/controllers/concerns/project_unauthorized.rb10
-rw-r--r--app/controllers/concerns/renders_blob.rb2
-rw-r--r--app/controllers/concerns/renders_commits.rb22
-rw-r--r--app/controllers/concerns/renders_member_access.rb4
-rw-r--r--app/controllers/concerns/renders_notes.rb6
-rw-r--r--app/controllers/concerns/repository_settings_redirect.rb2
-rw-r--r--app/controllers/concerns/requires_whitelisted_monitoring_client.rb2
-rw-r--r--app/controllers/concerns/routable_actions.rb18
-rw-r--r--app/controllers/concerns/send_file_upload.rb22
-rw-r--r--app/controllers/concerns/sends_blob.rb69
-rw-r--r--app/controllers/concerns/service_params.rb2
-rw-r--r--app/controllers/concerns/snippets_actions.rb2
-rw-r--r--app/controllers/concerns/spammable_actions.rb2
-rw-r--r--app/controllers/concerns/todos_actions.rb2
-rw-r--r--app/controllers/concerns/toggle_award_emoji.rb4
-rw-r--r--app/controllers/concerns/toggle_subscription_action.rb2
-rw-r--r--app/controllers/concerns/uploads_actions.rb6
-rw-r--r--app/controllers/concerns/with_performance_bar.rb8
-rw-r--r--app/controllers/concerns/workhorse_request.rb2
-rw-r--r--app/controllers/confirmations_controller.rb4
-rw-r--r--app/controllers/dashboard/application_controller.rb2
-rw-r--r--app/controllers/dashboard/groups_controller.rb2
-rw-r--r--app/controllers/dashboard/labels_controller.rb2
-rw-r--r--app/controllers/dashboard/milestones_controller.rb11
-rw-r--r--app/controllers/dashboard/projects_controller.rb6
-rw-r--r--app/controllers/dashboard/snippets_controller.rb2
-rw-r--r--app/controllers/dashboard/todos_controller.rb4
-rw-r--r--app/controllers/dashboard_controller.rb16
-rw-r--r--app/controllers/explore/application_controller.rb2
-rw-r--r--app/controllers/explore/groups_controller.rb2
-rw-r--r--app/controllers/explore/projects_controller.rb6
-rw-r--r--app/controllers/explore/snippets_controller.rb2
-rw-r--r--app/controllers/google_api/authorizations_controller.rb2
-rw-r--r--app/controllers/graphql_controller.rb2
-rw-r--r--app/controllers/groups/application_controller.rb2
-rw-r--r--app/controllers/groups/avatars_controller.rb2
-rw-r--r--app/controllers/groups/boards_controller.rb20
-rw-r--r--app/controllers/groups/children_controller.rb2
-rw-r--r--app/controllers/groups/clusters/applications_controller.rb18
-rw-r--r--app/controllers/groups/clusters_controller.rb25
-rw-r--r--app/controllers/groups/group_members_controller.rb2
-rw-r--r--app/controllers/groups/labels_controller.rb16
-rw-r--r--app/controllers/groups/milestones_controller.rb21
-rw-r--r--app/controllers/groups/runners_controller.rb2
-rw-r--r--app/controllers/groups/settings/badges_controller.rb13
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb13
-rw-r--r--app/controllers/groups/shared_projects_controller.rb2
-rw-r--r--app/controllers/groups/uploads_controller.rb2
-rw-r--r--app/controllers/groups/variables_controller.rb2
-rw-r--r--app/controllers/groups_controller.rb18
-rw-r--r--app/controllers/health_check_controller.rb2
-rw-r--r--app/controllers/health_controller.rb13
-rw-r--r--app/controllers/help_controller.rb2
-rw-r--r--app/controllers/ide_controller.rb4
-rw-r--r--app/controllers/import/base_controller.rb6
-rw-r--r--app/controllers/import/bitbucket_controller.rb4
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb26
-rw-r--r--app/controllers/import/fogbugz_controller.rb4
-rw-r--r--app/controllers/import/gitea_controller.rb4
-rw-r--r--app/controllers/import/github_controller.rb10
-rw-r--r--app/controllers/import/gitlab_controller.rb4
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb10
-rw-r--r--app/controllers/import/google_code_controller.rb4
-rw-r--r--app/controllers/import/manifest_controller.rb8
-rw-r--r--app/controllers/instance_statistics/cohorts_controller.rb6
-rw-r--r--app/controllers/instance_statistics/conversational_development_index_controller.rb2
-rw-r--r--app/controllers/invites_controller.rb8
-rw-r--r--app/controllers/jwt_controller.rb2
-rw-r--r--app/controllers/koding_controller.rb15
-rw-r--r--app/controllers/ldap/omniauth_callbacks_controller.rb2
-rw-r--r--app/controllers/metrics_controller.rb2
-rw-r--r--app/controllers/notification_settings_controller.rb10
-rw-r--r--app/controllers/oauth/applications_controller.rb4
-rw-r--r--app/controllers/oauth/authorizations_controller.rb4
-rw-r--r--app/controllers/oauth/authorized_applications_controller.rb2
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb9
-rw-r--r--app/controllers/passwords_controller.rb4
-rw-r--r--app/controllers/profiles/accounts_controller.rb4
-rw-r--r--app/controllers/profiles/active_sessions_controller.rb2
-rw-r--r--app/controllers/profiles/application_controller.rb2
-rw-r--r--app/controllers/profiles/avatars_controller.rb2
-rw-r--r--app/controllers/profiles/chat_names_controller.rb2
-rw-r--r--app/controllers/profiles/emails_controller.rb2
-rw-r--r--app/controllers/profiles/gpg_keys_controller.rb2
-rw-r--r--app/controllers/profiles/keys_controller.rb4
-rw-r--r--app/controllers/profiles/notifications_controller.rb4
-rw-r--r--app/controllers/profiles/passwords_controller.rb2
-rw-r--r--app/controllers/profiles/personal_access_tokens_controller.rb4
-rw-r--r--app/controllers/profiles/preferences_controller.rb2
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb4
-rw-r--r--app/controllers/profiles/u2f_registrations_controller.rb2
-rw-r--r--app/controllers/profiles_controller.rb6
-rw-r--r--app/controllers/projects/application_controller.rb8
-rw-r--r--app/controllers/projects/artifacts_controller.rb14
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb22
-rw-r--r--app/controllers/projects/avatars_controller.rb14
-rw-r--r--app/controllers/projects/badges_controller.rb2
-rw-r--r--app/controllers/projects/blame_controller.rb2
-rw-r--r--app/controllers/projects/blob_controller.rb30
-rw-r--r--app/controllers/projects/boards_controller.rb22
-rw-r--r--app/controllers/projects/branches_controller.rb4
-rw-r--r--app/controllers/projects/build_artifacts_controller.rb10
-rw-r--r--app/controllers/projects/builds_controller.rb2
-rw-r--r--app/controllers/projects/ci/lints_controller.rb2
-rw-r--r--app/controllers/projects/clusters/applications_controller.rb46
-rw-r--r--app/controllers/projects/clusters_controller.rb220
-rw-r--r--app/controllers/projects/commit_controller.rb11
-rw-r--r--app/controllers/projects/commits_controller.rb6
-rw-r--r--app/controllers/projects/compare_controller.rb23
-rw-r--r--app/controllers/projects/cycle_analytics/events_controller.rb2
-rw-r--r--app/controllers/projects/cycle_analytics_controller.rb2
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb11
-rw-r--r--app/controllers/projects/deploy_tokens_controller.rb2
-rw-r--r--app/controllers/projects/deployments_controller.rb6
-rw-r--r--app/controllers/projects/discussions_controller.rb4
-rw-r--r--app/controllers/projects/environments_controller.rb6
-rw-r--r--app/controllers/projects/find_file_controller.rb2
-rw-r--r--app/controllers/projects/forks_controller.rb6
-rw-r--r--app/controllers/projects/git_http_client_controller.rb2
-rw-r--r--app/controllers/projects/git_http_controller.rb7
-rw-r--r--app/controllers/projects/graphs_controller.rb2
-rw-r--r--app/controllers/projects/group_links_controller.rb2
-rw-r--r--app/controllers/projects/hook_logs_controller.rb2
-rw-r--r--app/controllers/projects/hooks_controller.rb3
-rw-r--r--app/controllers/projects/imports_controller.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb27
-rw-r--r--app/controllers/projects/jobs_controller.rb19
-rw-r--r--app/controllers/projects/labels_controller.rb19
-rw-r--r--app/controllers/projects/lfs_api_controller.rb4
-rw-r--r--app/controllers/projects/lfs_locks_api_controller.rb2
-rw-r--r--app/controllers/projects/lfs_storage_controller.rb6
-rw-r--r--app/controllers/projects/mattermosts_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests/application_controller.rb4
-rw-r--r--app/controllers/projects/merge_requests/conflicts_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests/creations_controller.rb10
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb28
-rw-r--r--app/controllers/projects/merge_requests_controller.rb78
-rw-r--r--app/controllers/projects/milestones_controller.rb11
-rw-r--r--app/controllers/projects/mirrors_controller.rb18
-rw-r--r--app/controllers/projects/network_controller.rb2
-rw-r--r--app/controllers/projects/notes_controller.rb4
-rw-r--r--app/controllers/projects/pages_controller.rb6
-rw-r--r--app/controllers/projects/pages_domains_controller.rb4
-rw-r--r--app/controllers/projects/pipeline_schedules_controller.rb4
-rw-r--r--app/controllers/projects/pipelines_controller.rb6
-rw-r--r--app/controllers/projects/pipelines_settings_controller.rb2
-rw-r--r--app/controllers/projects/project_members_controller.rb4
-rw-r--r--app/controllers/projects/prometheus/metrics_controller.rb2
-rw-r--r--app/controllers/projects/protected_branches_controller.rb2
-rw-r--r--app/controllers/projects/protected_refs_controller.rb2
-rw-r--r--app/controllers/projects/protected_tags_controller.rb2
-rw-r--r--app/controllers/projects/raw_controller.rb39
-rw-r--r--app/controllers/projects/refs_controller.rb67
-rw-r--r--app/controllers/projects/registry/application_controller.rb2
-rw-r--r--app/controllers/projects/registry/repositories_controller.rb18
-rw-r--r--app/controllers/projects/registry/tags_controller.rb2
-rw-r--r--app/controllers/projects/releases_controller.rb4
-rw-r--r--app/controllers/projects/repositories_controller.rb2
-rw-r--r--app/controllers/projects/runner_projects_controller.rb2
-rw-r--r--app/controllers/projects/runners_controller.rb2
-rw-r--r--app/controllers/projects/services_controller.rb2
-rw-r--r--app/controllers/projects/settings/badges_controller.rb13
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb13
-rw-r--r--app/controllers/projects/settings/integrations_controller.rb2
-rw-r--r--app/controllers/projects/settings/repository_controller.rb4
-rw-r--r--app/controllers/projects/snippets_controller.rb2
-rw-r--r--app/controllers/projects/tags_controller.rb11
-rw-r--r--app/controllers/projects/templates_controller.rb2
-rw-r--r--app/controllers/projects/todos_controller.rb2
-rw-r--r--app/controllers/projects/tree_controller.rb2
-rw-r--r--app/controllers/projects/triggers_controller.rb2
-rw-r--r--app/controllers/projects/uploads_controller.rb2
-rw-r--r--app/controllers/projects/variables_controller.rb2
-rw-r--r--app/controllers/projects/wikis_controller.rb25
-rw-r--r--app/controllers/projects_controller.rb30
-rw-r--r--app/controllers/registrations_controller.rb2
-rw-r--r--app/controllers/root_controller.rb6
-rw-r--r--app/controllers/search_controller.rb4
-rw-r--r--app/controllers/sent_notifications_controller.rb2
-rw-r--r--app/controllers/sessions_controller.rb4
-rw-r--r--app/controllers/sherlock/application_controller.rb2
-rw-r--r--app/controllers/sherlock/file_samples_controller.rb2
-rw-r--r--app/controllers/sherlock/queries_controller.rb2
-rw-r--r--app/controllers/sherlock/transactions_controller.rb2
-rw-r--r--app/controllers/snippets/notes_controller.rb4
-rw-r--r--app/controllers/snippets_controller.rb8
-rw-r--r--app/controllers/uploads_controller.rb2
-rw-r--r--app/controllers/user_callouts_controller.rb4
-rw-r--r--app/controllers/users/terms_controller.rb2
-rw-r--r--app/controllers/users_controller.rb17
-rw-r--r--app/finders/access_requests_finder.rb2
-rw-r--r--app/finders/admin/projects_finder.rb8
-rw-r--r--app/finders/admin/runners_finder.rb62
-rw-r--r--app/finders/applications_finder.rb22
-rw-r--r--app/finders/autocomplete/group_finder.rb37
-rw-r--r--app/finders/autocomplete/move_to_project_finder.rb35
-rw-r--r--app/finders/autocomplete/project_finder.rb35
-rw-r--r--app/finders/autocomplete/users_finder.rb87
-rw-r--r--app/finders/autocomplete_users_finder.rb68
-rw-r--r--app/finders/awarded_emoji_finder.rb21
-rw-r--r--app/finders/branches_finder.rb43
-rw-r--r--app/finders/clusters_finder.rb10
-rw-r--r--app/finders/concerns/created_at_filter.rb2
-rw-r--r--app/finders/concerns/custom_attributes_filter.rb4
-rw-r--r--app/finders/concerns/finder_methods.rb6
-rw-r--r--app/finders/concerns/finder_with_cross_project_access.rb2
-rw-r--r--app/finders/contributed_projects_finder.rb4
-rw-r--r--app/finders/environments_finder.rb4
-rw-r--r--app/finders/events_finder.rb17
-rw-r--r--app/finders/fork_projects_finder.rb4
-rw-r--r--app/finders/group_descendants_finder.rb24
-rw-r--r--app/finders/group_finder.rb4
-rw-r--r--app/finders/group_labels_finder.rb29
-rw-r--r--app/finders/group_members_finder.rb4
-rw-r--r--app/finders/group_projects_finder.rb4
-rw-r--r--app/finders/groups_finder.rb6
-rw-r--r--app/finders/issuable_finder.rb149
-rw-r--r--app/finders/issues_finder.rb24
-rw-r--r--app/finders/joined_groups_finder.rb21
-rw-r--r--app/finders/labels_finder.rb35
-rw-r--r--app/finders/license_template_finder.rb52
-rw-r--r--app/finders/members_finder.rb8
-rw-r--r--app/finders/merge_request_target_project_finder.rb4
-rw-r--r--app/finders/merge_requests_finder.rb27
-rw-r--r--app/finders/milestones_finder.rb6
-rw-r--r--app/finders/move_to_project_finder.rb21
-rw-r--r--app/finders/notes_finder.rb20
-rw-r--r--app/finders/pending_todos_finder.rb64
-rw-r--r--app/finders/personal_access_tokens_finder.rb6
-rw-r--r--app/finders/personal_projects_finder.rb4
-rw-r--r--app/finders/pipeline_schedules_finder.rb4
-rw-r--r--app/finders/pipelines_finder.rb20
-rw-r--r--app/finders/projects_finder.rb40
-rw-r--r--app/finders/runner_jobs_finder.rb4
-rw-r--r--app/finders/snippets_finder.rb203
-rw-r--r--app/finders/tags_finder.rb2
-rw-r--r--app/finders/template_finder.rb42
-rw-r--r--app/finders/todos_finder.rb82
-rw-r--r--app/finders/union_finder.rb10
-rw-r--r--app/finders/user_finder.rb58
-rw-r--r--app/finders/user_recent_events_finder.rb27
-rw-r--r--app/finders/users_finder.rb8
-rw-r--r--app/finders/users_with_pending_todos_finder.rb16
-rw-r--r--app/graphql/functions/base_function.rb2
-rw-r--r--app/graphql/functions/echo.rb2
-rw-r--r--app/graphql/gitlab_schema.rb2
-rw-r--r--app/graphql/mutations/concerns/mutations/resolves_project.rb2
-rw-r--r--app/graphql/mutations/merge_requests/base.rb2
-rw-r--r--app/graphql/resolvers/base_resolver.rb2
-rw-r--r--app/graphql/resolvers/concerns/resolves_pipelines.rb2
-rw-r--r--app/graphql/resolvers/full_path_resolver.rb2
-rw-r--r--app/graphql/resolvers/merge_request_pipelines_resolver.rb2
-rw-r--r--app/graphql/resolvers/merge_request_resolver.rb4
-rw-r--r--app/graphql/resolvers/project_pipelines_resolver.rb2
-rw-r--r--app/graphql/resolvers/project_resolver.rb2
-rw-r--r--app/graphql/types/base_enum.rb2
-rw-r--r--app/graphql/types/base_field.rb2
-rw-r--r--app/graphql/types/base_input_object.rb2
-rw-r--r--app/graphql/types/base_interface.rb2
-rw-r--r--app/graphql/types/base_object.rb2
-rw-r--r--app/graphql/types/base_scalar.rb2
-rw-r--r--app/graphql/types/base_union.rb2
-rw-r--r--app/graphql/types/ci/pipeline_status_enum.rb2
-rw-r--r--app/graphql/types/ci/pipeline_type.rb2
-rw-r--r--app/graphql/types/merge_request_type.rb2
-rw-r--r--app/graphql/types/permission_types/base_permission_type.rb2
-rw-r--r--app/graphql/types/permission_types/ci/pipeline.rb2
-rw-r--r--app/graphql/types/permission_types/merge_request.rb2
-rw-r--r--app/graphql/types/permission_types/project.rb4
-rw-r--r--app/graphql/types/project_type.rb2
-rw-r--r--app/graphql/types/query_type.rb2
-rw-r--r--app/graphql/types/time_type.rb2
-rw-r--r--app/helpers/accounts_helper.rb2
-rw-r--r--app/helpers/active_sessions_helper.rb2
-rw-r--r--app/helpers/appearances_helper.rb2
-rw-r--r--app/helpers/application_helper.rb44
-rw-r--r--app/helpers/application_settings_helper.rb66
-rw-r--r--app/helpers/auth_helper.rb21
-rw-r--r--app/helpers/auto_devops_helper.rb4
-rw-r--r--app/helpers/avatars_helper.rb31
-rw-r--r--app/helpers/award_emoji_helper.rb2
-rw-r--r--app/helpers/blame_helper.rb2
-rw-r--r--app/helpers/blob_helper.rb73
-rw-r--r--app/helpers/boards_helper.rb6
-rw-r--r--app/helpers/branches_helper.rb2
-rw-r--r--app/helpers/breadcrumbs_helper.rb2
-rw-r--r--app/helpers/broadcast_messages_helper.rb7
-rw-r--r--app/helpers/builds_helper.rb10
-rw-r--r--app/helpers/button_helper.rb19
-rw-r--r--app/helpers/calendar_helper.rb2
-rw-r--r--app/helpers/ci_status_helper.rb13
-rw-r--r--app/helpers/clusters_helper.rb7
-rw-r--r--app/helpers/commits_helper.rb13
-rw-r--r--app/helpers/compare_helper.rb2
-rw-r--r--app/helpers/components_helper.rb2
-rw-r--r--app/helpers/conversational_development_index_helper.rb2
-rw-r--r--app/helpers/cookies_helper.rb9
-rw-r--r--app/helpers/count_helper.rb16
-rw-r--r--app/helpers/dashboard_helper.rb6
-rw-r--r--app/helpers/defer_script_tag_helper.rb2
-rw-r--r--app/helpers/deploy_tokens_helper.rb2
-rw-r--r--app/helpers/diff_helper.rb13
-rw-r--r--app/helpers/dropdowns_helper.rb11
-rw-r--r--app/helpers/emails_helper.rb6
-rw-r--r--app/helpers/emoji_helper.rb2
-rw-r--r--app/helpers/environment_helper.rb4
-rw-r--r--app/helpers/environments_helper.rb2
-rw-r--r--app/helpers/events_helper.rb69
-rw-r--r--app/helpers/explore_helper.rb2
-rw-r--r--app/helpers/external_wiki_helper.rb2
-rw-r--r--app/helpers/favicon_helper.rb2
-rw-r--r--app/helpers/form_helper.rb2
-rw-r--r--app/helpers/git_helper.rb2
-rw-r--r--app/helpers/gitlab_routing_helper.rb2
-rw-r--r--app/helpers/graph_helper.rb6
-rw-r--r--app/helpers/groups_helper.rb38
-rw-r--r--app/helpers/hooks_helper.rb2
-rw-r--r--app/helpers/icons_helper.rb28
-rw-r--r--app/helpers/import_helper.rb10
-rw-r--r--app/helpers/instance_configuration_helper.rb2
-rw-r--r--app/helpers/issuables_helper.rb32
-rw-r--r--app/helpers/issues_helper.rb30
-rw-r--r--app/helpers/javascript_helper.rb2
-rw-r--r--app/helpers/kerberos_spnego_helper.rb2
-rw-r--r--app/helpers/labels_helper.rb22
-rw-r--r--app/helpers/lazy_image_tag_helper.rb8
-rw-r--r--app/helpers/markup_helper.rb41
-rw-r--r--app/helpers/mattermost_helper.rb2
-rw-r--r--app/helpers/members_helper.rb16
-rw-r--r--app/helpers/merge_requests_helper.rb16
-rw-r--r--app/helpers/milestones_helper.rb20
-rw-r--r--app/helpers/milestones_routing_helper.rb2
-rw-r--r--app/helpers/mirror_helper.rb2
-rw-r--r--app/helpers/namespaces_helper.rb8
-rw-r--r--app/helpers/nav_helper.rb18
-rw-r--r--app/helpers/notes_helper.rb8
-rw-r--r--app/helpers/notifications_helper.rb14
-rw-r--r--app/helpers/numbers_helper.rb4
-rw-r--r--app/helpers/page_layout_helper.rb16
-rw-r--r--app/helpers/pagination_helper.rb2
-rw-r--r--app/helpers/performance_bar_helper.rb2
-rw-r--r--app/helpers/pipeline_schedules_helper.rb2
-rw-r--r--app/helpers/preferences_helper.rb38
-rw-r--r--app/helpers/profiles_helper.rb16
-rw-r--r--app/helpers/projects_helper.rb77
-rw-r--r--app/helpers/repository_languages_helper.rb5
-rw-r--r--app/helpers/rss_helper.rb2
-rw-r--r--app/helpers/runners_helper.rb2
-rw-r--r--app/helpers/safe_params_helper.rb4
-rw-r--r--app/helpers/search_helper.rb21
-rw-r--r--app/helpers/selects_helper.rb28
-rw-r--r--app/helpers/sentry_helper.rb2
-rw-r--r--app/helpers/services_helper.rb4
-rw-r--r--app/helpers/sidekiq_helper.rb2
-rw-r--r--app/helpers/snippets_helper.rb2
-rw-r--r--app/helpers/sorting_helper.rb49
-rw-r--r--app/helpers/storage_health_helper.rb32
-rw-r--r--app/helpers/storage_helper.rb2
-rw-r--r--app/helpers/submodule_helper.rb39
-rw-r--r--app/helpers/system_note_helper.rb5
-rw-r--r--app/helpers/tab_helper.rb22
-rw-r--r--app/helpers/tags_helper.rb7
-rw-r--r--app/helpers/time_helper.rb14
-rw-r--r--app/helpers/todos_helper.rb11
-rw-r--r--app/helpers/tree_helper.rb30
-rw-r--r--app/helpers/triggers_helper.rb2
-rw-r--r--app/helpers/user_callouts_helper.rb2
-rw-r--r--app/helpers/users_helper.rb15
-rw-r--r--app/helpers/version_check_helper.rb23
-rw-r--r--app/helpers/visibility_level_helper.rb12
-rw-r--r--app/helpers/webpack_helper.rb2
-rw-r--r--app/helpers/wiki_helper.rb8
-rw-r--r--app/helpers/workhorse_helper.rb4
-rw-r--r--app/mailers/abuse_report_mailer.rb2
-rw-r--r--app/mailers/base_mailer.rb2
-rw-r--r--app/mailers/devise_mailer.rb9
-rw-r--r--app/mailers/email_rejection_mailer.rb2
-rw-r--r--app/mailers/emails/auto_devops.rb24
-rw-r--r--app/mailers/emails/issues.rb18
-rw-r--r--app/mailers/emails/members.rb2
-rw-r--r--app/mailers/emails/merge_requests.rb30
-rw-r--r--app/mailers/emails/notes.rb2
-rw-r--r--app/mailers/emails/pages_domains.rb2
-rw-r--r--app/mailers/emails/pipelines.rb8
-rw-r--r--app/mailers/emails/profile.rb6
-rw-r--r--app/mailers/emails/projects.rb2
-rw-r--r--app/mailers/notify.rb18
-rw-r--r--app/mailers/previews/devise_mailer_preview.rb2
-rw-r--r--app/mailers/previews/email_rejection_mailer_preview.rb2
-rw-r--r--app/mailers/previews/notify_preview.rb32
-rw-r--r--app/mailers/previews/repository_check_mailer_preview.rb2
-rw-r--r--app/mailers/repository_check_mailer.rb4
-rw-r--r--app/models/ability.rb2
-rw-r--r--app/models/application_record.rb5
-rw-r--r--app/models/application_setting.rb97
-rw-r--r--app/models/award_emoji.rb17
-rw-r--r--app/models/badge.rb2
-rw-r--r--app/models/blob.rb13
-rw-r--r--app/models/blob_viewer/gitlab_ci_yml.rb8
-rw-r--r--app/models/blob_viewer/package_json.rb3
-rw-r--r--app/models/board_group_recent_visit.rb25
-rw-r--r--app/models/board_project_recent_visit.rb25
-rw-r--r--app/models/ci/artifact_blob.rb2
-rw-r--r--app/models/ci/build.rb243
-rw-r--r--app/models/ci/job_artifact.rb82
-rw-r--r--app/models/ci/pipeline.rb103
-rw-r--r--app/models/ci/pipeline_enums.rb28
-rw-r--r--app/models/ci/pipeline_variable.rb4
-rw-r--r--app/models/ci/runner.rb62
-rw-r--r--app/models/ci/stage.rb5
-rw-r--r--app/models/ci/trigger_request.rb2
-rw-r--r--app/models/clusters/applications/helm.rb3
-rw-r--r--app/models/clusters/applications/ingress.rb1
-rw-r--r--app/models/clusters/applications/jupyter.rb8
-rw-r--r--app/models/clusters/applications/knative.rb55
-rw-r--r--app/models/clusters/applications/prometheus.rb3
-rw-r--r--app/models/clusters/applications/runner.rb3
-rw-r--r--app/models/clusters/cluster.rb69
-rw-r--r--app/models/clusters/concerns/application_core.rb2
-rw-r--r--app/models/clusters/concerns/application_status.rb35
-rw-r--r--app/models/clusters/group.rb10
-rw-r--r--app/models/clusters/kubernetes_namespace.rb83
-rw-r--r--app/models/clusters/platforms/kubernetes.rb101
-rw-r--r--app/models/clusters/project.rb3
-rw-r--r--app/models/commit.rb35
-rw-r--r--app/models/commit_status.rb32
-rw-r--r--app/models/commit_status_enums.rb20
-rw-r--r--app/models/compare.rb11
-rw-r--r--app/models/concerns/atomic_internal_id.rb2
-rw-r--r--app/models/concerns/avatarable.rb4
-rw-r--r--app/models/concerns/awardable.rb38
-rw-r--r--app/models/concerns/blob_language_from_git_attributes.rb13
-rw-r--r--app/models/concerns/blob_like.rb2
-rw-r--r--app/models/concerns/bulk_member_access_load.rb6
-rw-r--r--app/models/concerns/cacheable_attributes.rb10
-rw-r--r--app/models/concerns/case_sensitivity.rb47
-rw-r--r--app/models/concerns/deployable.rb30
-rw-r--r--app/models/concerns/deployment_platform.rb1
-rw-r--r--app/models/concerns/diff_positionable_note.rb81
-rw-r--r--app/models/concerns/each_batch.rb19
-rw-r--r--app/models/concerns/fast_destroy_all.rb4
-rw-r--r--app/models/concerns/from_union.rb51
-rw-r--r--app/models/concerns/has_status.rb19
-rw-r--r--app/models/concerns/ignorable_column.rb2
-rw-r--r--app/models/concerns/issuable.rb14
-rw-r--r--app/models/concerns/loaded_in_group_list.rb2
-rw-r--r--app/models/concerns/manual_inverse_association.rb2
-rw-r--r--app/models/concerns/mentionable.rb19
-rw-r--r--app/models/concerns/mentionable/reference_regexes.rb32
-rw-r--r--app/models/concerns/noteable.rb21
-rw-r--r--app/models/concerns/optionally_search.rb19
-rw-r--r--app/models/concerns/participable.rb2
-rw-r--r--app/models/concerns/project_services_loggable.rb28
-rw-r--r--app/models/concerns/protected_branch_access.rb11
-rw-r--r--app/models/concerns/protected_ref.rb10
-rw-r--r--app/models/concerns/protected_ref_access.rb18
-rw-r--r--app/models/concerns/protected_tag_access.rb3
-rw-r--r--app/models/concerns/redactable.rb33
-rw-r--r--app/models/concerns/referable.rb2
-rw-r--r--app/models/concerns/relative_positioning.rb79
-rw-r--r--app/models/concerns/resolvable_note.rb2
-rw-r--r--app/models/concerns/select_for_project_authorization.rb2
-rw-r--r--app/models/concerns/sha_attribute.rb2
-rw-r--r--app/models/concerns/sortable.rb2
-rw-r--r--app/models/concerns/spammable.rb2
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb12
-rw-r--r--app/models/concerns/strip_attribute.rb2
-rw-r--r--app/models/concerns/subscribable.rb8
-rw-r--r--app/models/concerns/token_authenticatable.rb60
-rw-r--r--app/models/concerns/token_authenticatable_strategies/base.rb73
-rw-r--r--app/models/concerns/token_authenticatable_strategies/digest.rb50
-rw-r--r--app/models/concerns/token_authenticatable_strategies/insecure.rb23
-rw-r--r--app/models/concerns/triggerable_hooks.rb7
-rw-r--r--app/models/concerns/with_uploads.rb2
-rw-r--r--app/models/container_repository.rb4
-rw-r--r--app/models/dashboard_group_milestone.rb6
-rw-r--r--app/models/deploy_key.rb2
-rw-r--r--app/models/deploy_token.rb5
-rw-r--r--app/models/deployment.rb89
-rw-r--r--app/models/diff_note.rb135
-rw-r--r--app/models/discussion_note.rb6
-rw-r--r--app/models/environment.rb11
-rw-r--r--app/models/environment_status.rb89
-rw-r--r--app/models/epic.rb4
-rw-r--r--app/models/event.rb21
-rw-r--r--app/models/forked_project_link.rb6
-rw-r--r--app/models/global_milestone.rb46
-rw-r--r--app/models/group.rb43
-rw-r--r--app/models/hooks/active_hook_filter.rb16
-rw-r--r--app/models/hooks/service_hook.rb6
-rw-r--r--app/models/hooks/web_hook.rb15
-rw-r--r--app/models/identity.rb8
-rw-r--r--app/models/identity/uniqueness_scopes.rb11
-rw-r--r--app/models/instance_configuration.rb4
-rw-r--r--app/models/internal_id.rb6
-rw-r--r--app/models/issue.rb58
-rw-r--r--app/models/key.rb12
-rw-r--r--app/models/label.rb16
-rw-r--r--app/models/label_link.rb2
-rw-r--r--app/models/label_note.rb97
-rw-r--r--app/models/legacy_diff_note.rb6
-rw-r--r--app/models/lfs_object.rb6
-rw-r--r--app/models/license_template.rb53
-rw-r--r--app/models/list.rb1
-rw-r--r--app/models/member.rb36
-rw-r--r--app/models/members/project_member.rb2
-rw-r--r--app/models/members_preloader.rb16
-rw-r--r--app/models/merge_request.rb58
-rw-r--r--app/models/merge_request_diff.rb19
-rw-r--r--app/models/milestone.rb23
-rw-r--r--app/models/namespace.rb31
-rw-r--r--app/models/network/commit.rb2
-rw-r--r--app/models/network/graph.rb4
-rw-r--r--app/models/note.rb57
-rw-r--r--app/models/notification_setting.rb9
-rw-r--r--app/models/pages_domain.rb2
-rw-r--r--app/models/personal_access_token.rb16
-rw-r--r--app/models/pool_repository.rb22
-rw-r--r--app/models/postgresql/replication_slot.rb11
-rw-r--r--app/models/project.rb322
-rw-r--r--app/models/project_authorization.rb8
-rw-r--r--app/models/project_auto_devops.rb22
-rw-r--r--app/models/project_feature.rb51
-rw-r--r--app/models/project_import_state.rb15
-rw-r--r--app/models/project_services/asana_service.rb4
-rw-r--r--app/models/project_services/bamboo_service.rb29
-rw-r--r--app/models/project_services/chat_message/merge_message.rb9
-rw-r--r--app/models/project_services/chat_message/push_message.rb47
-rw-r--r--app/models/project_services/discord_service.rb57
-rw-r--r--app/models/project_services/drone_ci_service.rb16
-rw-r--r--app/models/project_services/flowdock_service.rb48
-rw-r--r--app/models/project_services/gemnasium_service.rb62
-rw-r--r--app/models/project_services/hipchat_service.rb2
-rw-r--r--app/models/project_services/irker_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb8
-rw-r--r--app/models/project_services/jira_service.rb18
-rw-r--r--app/models/project_services/kubernetes_service.rb37
-rw-r--r--app/models/project_services/microsoft_teams_service.rb2
-rw-r--r--app/models/project_services/mock_ci_service.rb14
-rw-r--r--app/models/project_services/prometheus_service.rb9
-rw-r--r--app/models/project_services/slash_commands_service.rb4
-rw-r--r--app/models/project_services/teamcity_service.rb2
-rw-r--r--app/models/project_wiki.rb9
-rw-r--r--app/models/prometheus_metric.rb89
-rw-r--r--app/models/protected_ref_matcher.rb56
-rw-r--r--app/models/protected_tag.rb2
-rw-r--r--app/models/ref_matcher.rb46
-rw-r--r--app/models/remote_mirror.rb9
-rw-r--r--app/models/repository.rb127
-rw-r--r--app/models/resource_label_event.rb101
-rw-r--r--app/models/service.rb2
-rw-r--r--app/models/shard.rb25
-rw-r--r--app/models/site_statistic.rb4
-rw-r--r--app/models/snippet.rb81
-rw-r--r--app/models/ssh_host_key.rb130
-rw-r--r--app/models/system_note_metadata.rb7
-rw-r--r--app/models/todo.rb44
-rw-r--r--app/models/tree.rb51
-rw-r--r--app/models/upload.rb18
-rw-r--r--app/models/user.rb291
-rw-r--r--app/models/user_callout.rb7
-rw-r--r--app/models/user_callout_enums.rb16
-rw-r--r--app/models/user_preference.rb53
-rw-r--r--app/models/wiki_page.rb23
-rw-r--r--app/policies/application_setting/term_policy.rb2
-rw-r--r--app/policies/ci/build_policy.rb7
-rw-r--r--app/policies/ci/pipeline_policy.rb4
-rw-r--r--app/policies/ci/runner_policy.rb2
-rw-r--r--app/policies/clusters/cluster_policy.rb6
-rw-r--r--app/policies/deploy_key_policy.rb2
-rw-r--r--app/policies/deployment_policy.rb9
-rw-r--r--app/policies/group_policy.rb8
-rw-r--r--app/policies/issuable_policy.rb1
-rw-r--r--app/policies/issue_policy.rb4
-rw-r--r--app/policies/project_policy.rb20
-rw-r--r--app/presenters/blob_presenter.rb16
-rw-r--r--app/presenters/ci/build_presenter.rb4
-rw-r--r--app/presenters/ci/build_runner_presenter.rb10
-rw-r--r--app/presenters/ci/pipeline_presenter.rb10
-rw-r--r--app/presenters/clusterable_presenter.rb58
-rw-r--r--app/presenters/clusters/cluster_presenter.rb10
-rw-r--r--app/presenters/commit_status_presenter.rb15
-rw-r--r--app/presenters/conversational_development_index/metric_presenter.rb2
-rw-r--r--app/presenters/group_clusterable_presenter.rb36
-rw-r--r--app/presenters/merge_request_presenter.rb16
-rw-r--r--app/presenters/project_clusterable_presenter.rb31
-rw-r--r--app/presenters/project_presenter.rb198
-rw-r--r--app/presenters/projects/settings/deploy_keys_presenter.rb8
-rw-r--r--app/serializers/build_action_entity.rb10
-rw-r--r--app/serializers/build_details_entity.rb63
-rw-r--r--app/serializers/commit_entity.rb40
-rw-r--r--app/serializers/current_user_entity.rb8
-rw-r--r--app/serializers/deployment_entity.rb1
-rw-r--r--app/serializers/detailed_status_entity.rb35
-rw-r--r--app/serializers/diff_file_entity.rb11
-rw-r--r--app/serializers/diff_line_entity.rb14
-rw-r--r--app/serializers/diff_line_parallel_entity.rb6
-rw-r--r--app/serializers/diff_line_serializer.rb5
-rw-r--r--app/serializers/diff_viewer_entity.rb7
-rw-r--r--app/serializers/diffs_entity.rb13
-rw-r--r--app/serializers/discussion_entity.rb5
-rw-r--r--app/serializers/environment_serializer.rb2
-rw-r--r--app/serializers/environment_status_entity.rb67
-rw-r--r--app/serializers/environment_status_serializer.rb5
-rw-r--r--app/serializers/group_child_entity.rb2
-rw-r--r--app/serializers/group_entity.rb2
-rw-r--r--app/serializers/issue_entity.rb2
-rw-r--r--app/serializers/job_entity.rb29
-rw-r--r--app/serializers/job_group_entity.rb2
-rw-r--r--app/serializers/merge_request_user_entity.rb2
-rw-r--r--app/serializers/merge_request_widget_entity.rb5
-rw-r--r--app/serializers/move_to_project_entity.rb6
-rw-r--r--app/serializers/move_to_project_serializer.rb5
-rw-r--r--app/serializers/note_entity.rb10
-rw-r--r--app/serializers/pipeline_details_entity.rb1
-rw-r--r--app/serializers/pipeline_entity.rb2
-rw-r--r--app/serializers/pipeline_serializer.rb3
-rw-r--r--app/serializers/project_note_entity.rb4
-rw-r--r--app/serializers/runner_entity.rb7
-rw-r--r--app/serializers/stage_entity.rb18
-rw-r--r--app/serializers/status_entity.rb21
-rw-r--r--app/serializers/trigger_variable_entity.rb7
-rw-r--r--app/serializers/user_preference_entity.rb14
-rw-r--r--app/services/application_settings/update_service.rb8
-rw-r--r--app/services/applications/create_service.rb3
-rw-r--r--app/services/audit_event_service.rb26
-rw-r--r--app/services/auth/container_registry_authentication_service.rb2
-rw-r--r--app/services/boards/create_service.rb2
-rw-r--r--app/services/boards/issues/list_service.rb17
-rw-r--r--app/services/boards/issues/move_service.rb10
-rw-r--r--app/services/boards/lists/destroy_service.rb2
-rw-r--r--app/services/boards/lists/list_service.rb2
-rw-r--r--app/services/boards/lists/move_service.rb4
-rw-r--r--app/services/boards/visits/create_service.rb17
-rw-r--r--app/services/boards/visits/latest_service.rb17
-rw-r--r--app/services/chat_names/find_user_service.rb2
-rw-r--r--app/services/ci/compare_test_reports_service.rb2
-rw-r--r--app/services/ci/create_pipeline_service.rb4
-rw-r--r--app/services/ci/destroy_pipeline_service.rb13
-rw-r--r--app/services/ci/enqueue_build_service.rb8
-rw-r--r--app/services/ci/ensure_stage_service.rb2
-rw-r--r--app/services/ci/extract_sections_from_build_trace_service.rb2
-rw-r--r--app/services/ci/fetch_kubernetes_token_service.rb75
-rw-r--r--app/services/ci/process_build_service.rb45
-rw-r--r--app/services/ci/process_pipeline_service.rb42
-rw-r--r--app/services/ci/register_job_service.rb17
-rw-r--r--app/services/ci/retry_build_service.rb2
-rw-r--r--app/services/ci/run_scheduled_build_service.rb13
-rw-r--r--app/services/clusters/applications/check_installation_progress_service.rb11
-rw-r--r--app/services/clusters/applications/create_service.rb78
-rw-r--r--app/services/clusters/applications/install_service.rb10
-rw-r--r--app/services/clusters/applications/schedule_installation_service.rb10
-rw-r--r--app/services/clusters/create_service.rb42
-rw-r--r--app/services/clusters/gcp/finalize_creation_service.rb66
-rw-r--r--app/services/clusters/gcp/kubernetes.rb14
-rw-r--r--app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb52
-rw-r--r--app/services/clusters/gcp/kubernetes/create_service_account_service.rb101
-rw-r--r--app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb29
-rw-r--r--app/services/clusters/gcp/provision_service.rb4
-rw-r--r--app/services/clusters/update_service.rb8
-rw-r--r--app/services/cohorts_service.rb2
-rw-r--r--app/services/commits/change_service.rb8
-rw-r--r--app/services/commits/commit_patch_service.rb61
-rw-r--r--app/services/commits/create_service.rb7
-rw-r--r--app/services/concerns/issues/resolve_discussions.rb2
-rw-r--r--app/services/create_deployment_service.rb74
-rw-r--r--app/services/create_release_service.rb2
-rw-r--r--app/services/delete_merged_branches_service.rb6
-rw-r--r--app/services/emails/base_service.rb2
-rw-r--r--app/services/emails/create_service.rb7
-rw-r--r--app/services/files/base_service.rb6
-rw-r--r--app/services/files/multi_service.rb2
-rw-r--r--app/services/git_push_service.rb17
-rw-r--r--app/services/groups/destroy_service.rb14
-rw-r--r--app/services/groups/transfer_service.rb4
-rw-r--r--app/services/groups/update_service.rb6
-rw-r--r--app/services/import_export_clean_up_service.rb2
-rw-r--r--app/services/issuable/bulk_update_service.rb2
-rw-r--r--app/services/issuable/clone/attributes_rewriter.rb62
-rw-r--r--app/services/issuable/clone/base_service.rb60
-rw-r--r--app/services/issuable/clone/content_rewriter.rb65
-rw-r--r--app/services/issuable/common_system_notes_service.rb9
-rw-r--r--app/services/issuable_base_service.rb29
-rw-r--r--app/services/issues/base_service.rb2
-rw-r--r--app/services/issues/fetch_referenced_merge_requests_service.rb14
-rw-r--r--app/services/issues/move_service.rb142
-rw-r--r--app/services/issues/referenced_merge_requests_service.rb72
-rw-r--r--app/services/issues/related_branches_service.rb26
-rw-r--r--app/services/issues/reopen_service.rb2
-rw-r--r--app/services/issues/update_service.rb31
-rw-r--r--app/services/keys/destroy_service.rb2
-rw-r--r--app/services/labels/find_or_create_service.rb2
-rw-r--r--app/services/labels/promote_service.rb25
-rw-r--r--app/services/labels/transfer_service.rb24
-rw-r--r--app/services/lfs/file_transformer.rb2
-rw-r--r--app/services/lfs/lock_file_service.rb2
-rw-r--r--app/services/lfs/locks_finder_service.rb2
-rw-r--r--app/services/lfs/unlock_file_service.rb2
-rw-r--r--app/services/members/base_service.rb2
-rw-r--r--app/services/merge_requests/base_service.rb8
-rw-r--r--app/services/merge_requests/build_service.rb45
-rw-r--r--app/services/merge_requests/create_from_issue_service.rb13
-rw-r--r--app/services/merge_requests/create_service.rb2
-rw-r--r--app/services/merge_requests/delete_non_latest_diffs_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb5
-rw-r--r--app/services/merge_requests/refresh_service.rb82
-rw-r--r--app/services/merge_requests/reload_diffs_service.rb19
-rw-r--r--app/services/merge_requests/update_service.rb14
-rw-r--r--app/services/milestones/destroy_service.rb22
-rw-r--r--app/services/milestones/promote_service.rb10
-rw-r--r--app/services/milestones/update_service.rb2
-rw-r--r--app/services/notes/base_service.rb13
-rw-r--r--app/services/notes/build_service.rb6
-rw-r--r--app/services/notes/create_service.rb3
-rw-r--r--app/services/notes/destroy_service.rb4
-rw-r--r--app/services/notification_recipient_service.rb30
-rw-r--r--app/services/notification_service.rb47
-rw-r--r--app/services/preview_markdown_service.rb6
-rw-r--r--app/services/projects/after_rename_service.rb135
-rw-r--r--app/services/projects/auto_devops/disable_service.rb41
-rw-r--r--app/services/projects/autocomplete_service.rb33
-rw-r--r--app/services/projects/base_move_relations_service.rb2
-rw-r--r--app/services/projects/batch_forks_count_service.rb2
-rw-r--r--app/services/projects/batch_open_issues_count_service.rb2
-rw-r--r--app/services/projects/container_repository/destroy_service.rb15
-rw-r--r--app/services/projects/create_service.rb23
-rw-r--r--app/services/projects/destroy_service.rb28
-rw-r--r--app/services/projects/detect_repository_languages_service.rb6
-rw-r--r--app/services/projects/disable_deploy_key_service.rb13
-rw-r--r--app/services/projects/enable_deploy_key_service.rb12
-rw-r--r--app/services/projects/fork_service.rb62
-rw-r--r--app/services/projects/forks_count_service.rb7
-rw-r--r--app/services/projects/gitlab_projects_import_service.rb2
-rw-r--r--app/services/projects/hashed_storage/migrate_repository_service.rb4
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_import_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_link_service.rb2
-rw-r--r--app/services/projects/move_deploy_keys_projects_service.rb4
-rw-r--r--app/services/projects/move_forks_service.rb15
-rw-r--r--app/services/projects/move_lfs_objects_projects_service.rb4
-rw-r--r--app/services/projects/move_notification_settings_service.rb4
-rw-r--r--app/services/projects/move_project_authorizations_service.rb4
-rw-r--r--app/services/projects/move_project_group_links_service.rb6
-rw-r--r--app/services/projects/move_project_members_service.rb6
-rw-r--r--app/services/projects/open_issues_count_service.rb4
-rw-r--r--app/services/projects/propagate_service_template.rb4
-rw-r--r--app/services/projects/transfer_service.rb7
-rw-r--r--app/services/projects/unlink_fork_service.rb3
-rw-r--r--app/services/projects/update_pages_configuration_service.rb8
-rw-r--r--app/services/projects/update_remote_mirror_service.rb5
-rw-r--r--app/services/projects/update_service.rb16
-rw-r--r--app/services/protected_branches/legacy_api_update_service.rb4
-rw-r--r--app/services/quick_actions/interpret_service.rb98
-rw-r--r--app/services/quick_actions/target_service.rb4
-rw-r--r--app/services/resource_events/change_labels_service.rb3
-rw-r--r--app/services/resource_events/merge_into_notes_service.rb57
-rw-r--r--app/services/search_service.rb4
-rw-r--r--app/services/spam_check_service.rb2
-rw-r--r--app/services/submit_usage_ping_service.rb1
-rw-r--r--app/services/submodules/update_service.rb38
-rw-r--r--app/services/system_note_service.rb69
-rw-r--r--app/services/tags/destroy_service.rb2
-rw-r--r--app/services/todo_service.rb16
-rw-r--r--app/services/todos/destroy/base_service.rb4
-rw-r--r--app/services/todos/destroy/confidential_issue_service.rb6
-rw-r--r--app/services/todos/destroy/entity_leave_service.rb20
-rw-r--r--app/services/todos/destroy/group_private_service.rb4
-rw-r--r--app/services/todos/destroy/private_features_service.rb10
-rw-r--r--app/services/todos/destroy/project_private_service.rb4
-rw-r--r--app/services/update_deployment_service.rb53
-rw-r--r--app/services/update_release_service.rb2
-rw-r--r--app/services/users/build_service.rb20
-rw-r--r--app/services/users/destroy_service.rb7
-rw-r--r--app/services/users/last_push_event_service.rb2
-rw-r--r--app/services/users/migrate_to_ghost_user_service.rb4
-rw-r--r--app/services/users/respond_to_terms_service.rb2
-rw-r--r--app/services/web_hook_service.rb8
-rw-r--r--app/services/wikis/create_attachment_service.rb79
-rw-r--r--app/uploaders/avatar_uploader.rb4
-rw-r--r--app/uploaders/file_uploader.rb16
-rw-r--r--app/uploaders/gitlab_uploader.rb10
-rw-r--r--app/uploaders/job_artifact_uploader.rb19
-rw-r--r--app/uploaders/legacy_artifact_uploader.rb2
-rw-r--r--app/uploaders/lfs_object_uploader.rb2
-rw-r--r--app/uploaders/namespace_file_uploader.rb22
-rw-r--r--app/uploaders/records_uploads.rb6
-rw-r--r--app/uploaders/uploader_helper.rb27
-rw-r--r--app/validators/branch_filter_validator.rb37
-rw-r--r--app/validators/js_regex_validator.rb17
-rw-r--r--app/validators/url_validator.rb14
-rw-r--r--app/validators/variable_duplicates_validator.rb2
-rw-r--r--app/views/abuse_reports/new.html.haml14
-rw-r--r--app/views/admin/appearances/_form.html.haml8
-rw-r--r--app/views/admin/appearances/preview_sign_in.html.haml2
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml18
-rw-r--r--app/views/admin/application_settings/_background_jobs.html.haml27
-rw-r--r--app/views/admin/application_settings/_ci_cd.html.haml10
-rw-r--r--app/views/admin/application_settings/_diff_limits.html.haml16
-rw-r--r--app/views/admin/application_settings/_email.html.haml6
-rw-r--r--app/views/admin/application_settings/_influx.html.haml2
-rw-r--r--app/views/admin/application_settings/_koding.html.haml22
-rw-r--r--app/views/admin/application_settings/_repository_mirrors_form.html.haml4
-rw-r--r--app/views/admin/application_settings/_repository_storage.html.haml31
-rw-r--r--app/views/admin/application_settings/_signin.html.haml2
-rw-r--r--app/views/admin/application_settings/_usage.html.haml31
-rw-r--r--app/views/admin/application_settings/ci_cd.html.haml26
-rw-r--r--app/views/admin/application_settings/integrations.html.haml31
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml50
-rw-r--r--app/views/admin/application_settings/network.html.haml36
-rw-r--r--app/views/admin/application_settings/preferences.html.haml58
-rw-r--r--app/views/admin/application_settings/reporting.html.haml36
-rw-r--r--app/views/admin/application_settings/repository.html.haml36
-rw-r--r--app/views/admin/application_settings/show.html.haml315
-rw-r--r--app/views/admin/applications/_form.html.haml6
-rw-r--r--app/views/admin/applications/index.html.haml2
-rw-r--r--app/views/admin/applications/show.html.haml21
-rw-r--r--app/views/admin/background_jobs/show.html.haml35
-rw-r--r--app/views/admin/broadcast_messages/_form.html.haml4
-rw-r--r--app/views/admin/dashboard/index.html.haml8
-rw-r--r--app/views/admin/deploy_keys/edit.html.haml2
-rw-r--r--app/views/admin/deploy_keys/index.html.haml2
-rw-r--r--app/views/admin/deploy_keys/new.html.haml2
-rw-r--r--app/views/admin/groups/_form.html.haml8
-rw-r--r--app/views/admin/groups/index.html.haml2
-rw-r--r--app/views/admin/groups/show.html.haml11
-rw-r--r--app/views/admin/health_check/_failing_storages.html.haml15
-rw-r--r--app/views/admin/health_check/show.html.haml3
-rw-r--r--app/views/admin/hook_logs/show.html.haml3
-rw-r--r--app/views/admin/hooks/edit.html.haml2
-rw-r--r--app/views/admin/hooks/index.html.haml2
-rw-r--r--app/views/admin/identities/_form.html.haml2
-rw-r--r--app/views/admin/identities/index.html.haml2
-rw-r--r--app/views/admin/impersonation_tokens/index.html.haml5
-rw-r--r--app/views/admin/labels/_form.html.haml2
-rw-r--r--app/views/admin/labels/index.html.haml2
-rw-r--r--app/views/admin/projects/index.html.haml2
-rw-r--r--app/views/admin/projects/show.html.haml6
-rw-r--r--app/views/admin/runners/_runner.html.haml127
-rw-r--r--app/views/admin/runners/_sort_dropdown.html.haml11
-rw-r--r--app/views/admin/runners/index.html.haml171
-rw-r--r--app/views/admin/services/_form.html.haml2
-rw-r--r--app/views/admin/spam_logs/index.html.haml2
-rw-r--r--app/views/admin/users/_access_levels.html.haml16
-rw-r--r--app/views/admin/users/_form.html.haml37
-rw-r--r--app/views/admin/users/index.html.haml2
-rw-r--r--app/views/admin/users/projects.html.haml2
-rw-r--r--app/views/admin/users/show.html.haml4
-rw-r--r--app/views/award_emoji/_awards_block.html.haml4
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml6
-rw-r--r--app/views/ci/runner/_how_to_setup_shared_runner.html.haml3
-rw-r--r--app/views/ci/runner/_how_to_setup_specific_runner.html.haml26
-rw-r--r--app/views/ci/status/_dropdown_graph_badge.html.haml4
-rw-r--r--app/views/ci/variables/_index.html.haml4
-rw-r--r--app/views/clusters/clusters/_advanced_settings.html.haml15
-rw-r--r--app/views/clusters/clusters/_banner.html.haml9
-rw-r--r--app/views/clusters/clusters/_buttons.html.haml4
-rw-r--r--app/views/clusters/clusters/_cluster.html.haml16
-rw-r--r--app/views/clusters/clusters/_empty_state.html.haml14
-rw-r--r--app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml (renamed from app/views/projects/clusters/_gcp_signup_offer_banner.html.haml)0
-rw-r--r--app/views/clusters/clusters/_integration_form.html.haml31
-rw-r--r--app/views/clusters/clusters/_sidebar.html.haml6
-rw-r--r--app/views/clusters/clusters/gcp/_form.html.haml74
-rw-r--r--app/views/clusters/clusters/gcp/_header.html.haml (renamed from app/views/projects/clusters/gcp/_header.html.haml)0
-rw-r--r--app/views/clusters/clusters/gcp/_show.html.haml50
-rw-r--r--app/views/clusters/clusters/index.html.haml23
-rw-r--r--app/views/clusters/clusters/new.html.haml36
-rw-r--r--app/views/clusters/clusters/show.html.haml55
-rw-r--r--app/views/clusters/clusters/user/_form.html.haml39
-rw-r--r--app/views/clusters/clusters/user/_header.html.haml (renamed from app/views/projects/clusters/user/_header.html.haml)0
-rw-r--r--app/views/clusters/clusters/user/_show.html.haml39
-rw-r--r--app/views/dashboard/_activity_head.html.haml3
-rw-r--r--app/views/dashboard/_groups_head.html.haml9
-rw-r--r--app/views/dashboard/_projects_head.html.haml9
-rw-r--r--app/views/dashboard/_snippets_head.html.haml11
-rw-r--r--app/views/dashboard/issues.atom.builder2
-rw-r--r--app/views/dashboard/issues.html.haml12
-rw-r--r--app/views/dashboard/merge_requests.html.haml13
-rw-r--r--app/views/dashboard/milestones/index.html.haml12
-rw-r--r--app/views/dashboard/snippets/index.html.haml5
-rw-r--r--app/views/dashboard/todos/index.html.haml3
-rw-r--r--app/views/devise/passwords/edit.html.haml6
-rw-r--r--app/views/devise/sessions/_new_base.html.haml10
-rw-r--r--app/views/devise/sessions/_new_crowd.html.haml2
-rw-r--r--app/views/devise/sessions/_new_ldap.html.haml6
-rw-r--r--app/views/devise/sessions/two_factor.html.haml2
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml27
-rw-r--r--app/views/devise/shared/_signin_box.html.haml6
-rw-r--r--app/views/devise/shared/_signup_box.html.haml31
-rw-r--r--app/views/devise/shared/_tabs_ldap.html.haml11
-rw-r--r--app/views/devise/shared/_tabs_normal.html.haml4
-rw-r--r--app/views/discussions/_diff_discussion.html.haml3
-rw-r--r--app/views/discussions/_diff_with_notes.html.haml2
-rw-r--r--app/views/discussions/_discussion.html.haml8
-rw-r--r--app/views/discussions/_parallel_diff_discussion.html.haml12
-rw-r--r--app/views/doorkeeper/applications/_form.html.haml2
-rw-r--r--app/views/doorkeeper/applications/index.html.haml5
-rw-r--r--app/views/doorkeeper/applications/show.html.haml19
-rw-r--r--app/views/doorkeeper/authorized_applications/_delete_form.html.haml1
-rw-r--r--app/views/errors/precondition_failed.html.haml8
-rw-r--r--app/views/events/_event.html.haml4
-rw-r--r--app/views/events/_event_push.atom.haml2
-rw-r--r--app/views/events/_event_scope.html.haml2
-rw-r--r--app/views/events/event/_common.html.haml27
-rw-r--r--app/views/events/event/_created_project.html.haml10
-rw-r--r--app/views/events/event/_note.html.haml10
-rw-r--r--app/views/events/event/_private.html.haml11
-rw-r--r--app/views/events/event/_push.html.haml10
-rw-r--r--app/views/explore/groups/index.html.haml2
-rw-r--r--app/views/groups/_archived_projects.html.haml8
-rw-r--r--app/views/groups/_children.html.haml4
-rw-r--r--app/views/groups/_group_admin_settings.html.haml6
-rw-r--r--app/views/groups/_shared_projects.html.haml8
-rw-r--r--app/views/groups/_subgroups_and_projects.html.haml8
-rw-r--r--app/views/groups/edit.html.haml34
-rw-r--r--app/views/groups/group_members/_new_group_member.html.haml2
-rw-r--r--app/views/groups/issues.atom.builder2
-rw-r--r--app/views/groups/labels/index.html.haml35
-rw-r--r--app/views/groups/milestones/_form.html.haml4
-rw-r--r--app/views/groups/milestones/index.html.haml4
-rw-r--r--app/views/groups/new.html.haml38
-rw-r--r--app/views/groups/runners/_group_runners.html.haml4
-rw-r--r--app/views/groups/settings/_advanced.html.haml24
-rw-r--r--app/views/groups/settings/_general.html.haml48
-rw-r--r--app/views/groups/settings/_lfs.html.haml15
-rw-r--r--app/views/groups/settings/_permissions.html.haml37
-rw-r--r--app/views/groups/settings/_two_factor_auth.html.haml16
-rw-r--r--app/views/groups/settings/ci_cd/show.html.haml2
-rw-r--r--app/views/groups/show.html.haml47
-rw-r--r--app/views/help/index.html.haml3
-rw-r--r--app/views/help/instance_configuration/_gitlab_pages.html.haml2
-rw-r--r--app/views/help/ui.html.haml2
-rw-r--r--app/views/ide/index.html.haml2
-rw-r--r--app/views/import/_githubish_status.html.haml2
-rw-r--r--app/views/import/bitbucket/status.html.haml2
-rw-r--r--app/views/import/bitbucket_server/status.html.haml4
-rw-r--r--app/views/import/fogbugz/new.html.haml2
-rw-r--r--app/views/import/fogbugz/new_user_map.html.haml2
-rw-r--r--app/views/import/gitea/new.html.haml2
-rw-r--r--app/views/import/gitlab_projects/new.html.haml11
-rw-r--r--app/views/import/google_code/new.html.haml2
-rw-r--r--app/views/import/google_code/new_user_map.html.haml2
-rw-r--r--app/views/instance_statistics/cohorts/_cohorts_table.html.haml2
-rw-r--r--app/views/instance_statistics/cohorts/index.html.haml16
-rw-r--r--app/views/instance_statistics/conversational_development_index/_disabled.html.haml17
-rw-r--r--app/views/instance_statistics/conversational_development_index/_no_data.html.haml2
-rw-r--r--app/views/instance_statistics/conversational_development_index/index.html.haml9
-rw-r--r--app/views/issues/_issues_calendar.ics.ruby2
-rw-r--r--app/views/koding/index.html.haml6
-rw-r--r--app/views/layouts/_flash.html.haml2
-rw-r--r--app/views/layouts/_page.html.haml6
-rw-r--r--app/views/layouts/_search.html.haml4
-rw-r--r--app/views/layouts/application.html.haml2
-rw-r--r--app/views/layouts/dashboard.html.haml1
-rw-r--r--app/views/layouts/explore.html.haml6
-rw-r--r--app/views/layouts/fullscreen.html.haml14
-rw-r--r--app/views/layouts/group_settings.html.haml4
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml16
-rw-r--r--app/views/layouts/header/_default.html.haml34
-rw-r--r--app/views/layouts/header/_help_dropdown.html.haml6
-rw-r--r--app/views/layouts/koding.html.haml5
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml3
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml56
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml41
-rw-r--r--app/views/layouts/nav/sidebar/_instance_statistics.html.haml23
-rw-r--r--app/views/layouts/nav/sidebar/_profile.html.haml23
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml24
-rw-r--r--app/views/layouts/nav_only.html.haml14
-rw-r--r--app/views/layouts/terms.html.haml29
-rw-r--r--app/views/notify/_failed_builds.html.haml32
-rw-r--r--app/views/notify/autodevops_disabled_email.html.haml49
-rw-r--r--app/views/notify/autodevops_disabled_email.text.erb20
-rw-r--r--app/views/notify/changed_milestone_issue_email.html.haml3
-rw-r--r--app/views/notify/changed_milestone_issue_email.text.erb1
-rw-r--r--app/views/notify/changed_milestone_merge_request_email.html.haml3
-rw-r--r--app/views/notify/changed_milestone_merge_request_email.text.erb1
-rw-r--r--app/views/notify/new_merge_request_email.html.haml2
-rw-r--r--app/views/notify/new_merge_request_email.text.erb2
-rw-r--r--app/views/notify/pipeline_failed_email.html.haml35
-rw-r--r--app/views/notify/removed_milestone_issue_email.html.haml2
-rw-r--r--app/views/notify/removed_milestone_issue_email.text.erb1
-rw-r--r--app/views/notify/removed_milestone_merge_request_email.html.haml2
-rw-r--r--app/views/notify/removed_milestone_merge_request_email.text.erb1
-rw-r--r--app/views/profiles/emails/index.html.haml10
-rw-r--r--app/views/profiles/gpg_keys/_form.html.haml2
-rw-r--r--app/views/profiles/keys/_form.html.haml8
-rw-r--r--app/views/profiles/keys/_key_details.html.haml2
-rw-r--r--app/views/profiles/passwords/edit.html.haml2
-rw-r--r--app/views/profiles/passwords/new.html.haml4
-rw-r--r--app/views/profiles/personal_access_tokens/index.html.haml12
-rw-r--r--app/views/profiles/preferences/show.html.haml6
-rw-r--r--app/views/profiles/show.html.haml78
-rw-r--r--app/views/profiles/two_factor_auths/_codes.html.haml6
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml6
-rw-r--r--app/views/projects/_commit_button.html.haml2
-rw-r--r--app/views/projects/_flash_messages.html.haml1
-rw-r--r--app/views/projects/_fork_suggestion.html.haml2
-rw-r--r--app/views/projects/_home_panel.html.haml84
-rw-r--r--app/views/projects/_import_project_pane.html.haml26
-rw-r--r--app/views/projects/_issuable_by_email.html.haml11
-rw-r--r--app/views/projects/_md_preview.html.haml22
-rw-r--r--app/views/projects/_merge_request_merge_method_settings.html.haml1
-rw-r--r--app/views/projects/_new_project_fields.html.haml43
-rw-r--r--app/views/projects/_project_templates.html.haml2
-rw-r--r--app/views/projects/_readme.html.haml2
-rw-r--r--app/views/projects/_stat_anchor_list.html.haml2
-rw-r--r--app/views/projects/_wiki.html.haml18
-rw-r--r--app/views/projects/artifacts/browse.html.haml2
-rw-r--r--app/views/projects/artifacts/file.html.haml2
-rw-r--r--app/views/projects/blob/_blob.html.haml1
-rw-r--r--app/views/projects/blob/_new_dir.html.haml2
-rw-r--r--app/views/projects/blob/_template_selectors.html.haml10
-rw-r--r--app/views/projects/blob/_upload.html.haml2
-rw-r--r--app/views/projects/blob/edit.html.haml4
-rw-r--r--app/views/projects/blob/new.html.haml2
-rw-r--r--app/views/projects/blob/preview.html.haml2
-rw-r--r--app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml4
-rw-r--r--app/views/projects/blob/viewers/_highlight_embed.html.haml4
-rw-r--r--app/views/projects/blob/viewers/_markup.html.haml6
-rw-r--r--app/views/projects/blob/viewers/_text.html.haml2
-rw-r--r--app/views/projects/branches/_panel.html.haml3
-rw-r--r--app/views/projects/branches/index.html.haml2
-rw-r--r--app/views/projects/branches/new.html.haml2
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml4
-rw-r--r--app/views/projects/buttons/_fork.html.haml30
-rw-r--r--app/views/projects/buttons/_koding.html.haml3
-rw-r--r--app/views/projects/buttons/_star.html.haml30
-rw-r--r--app/views/projects/ci/builds/_build.html.haml24
-rw-r--r--app/views/projects/clusters/_advanced_settings.html.haml15
-rw-r--r--app/views/projects/clusters/_banner.html.haml14
-rw-r--r--app/views/projects/clusters/_cluster.html.haml24
-rw-r--r--app/views/projects/clusters/_empty_state.html.haml12
-rw-r--r--app/views/projects/clusters/_integration_form.html.haml45
-rw-r--r--app/views/projects/clusters/_sidebar.html.haml9
-rw-r--r--app/views/projects/clusters/gcp/_form.html.haml65
-rw-r--r--app/views/projects/clusters/gcp/_show.html.haml41
-rw-r--r--app/views/projects/clusters/index.html.haml24
-rw-r--r--app/views/projects/clusters/new.html.haml36
-rw-r--r--app/views/projects/clusters/show.html.haml51
-rw-r--r--app/views/projects/clusters/user/_form.html.haml29
-rw-r--r--app/views/projects/clusters/user/_show.html.haml30
-rw-r--r--app/views/projects/commit/_change.html.haml2
-rw-r--r--app/views/projects/commits/_commit.html.haml6
-rw-r--r--app/views/projects/commits/_commit_list.html.haml5
-rw-r--r--app/views/projects/commits/_commits.html.haml3
-rw-r--r--app/views/projects/compare/_form.html.haml2
-rw-r--r--app/views/projects/default_branch/_show.html.haml21
-rw-r--r--app/views/projects/deploy_keys/_form.html.haml2
-rw-r--r--app/views/projects/deploy_keys/edit.html.haml2
-rw-r--r--app/views/projects/deploy_tokens/_form.html.haml10
-rw-r--r--app/views/projects/deploy_tokens/_index.html.haml2
-rw-r--r--app/views/projects/deploy_tokens/_new_deploy_token.html.haml6
-rw-r--r--app/views/projects/deployments/_rollback.haml2
-rw-r--r--app/views/projects/diffs/_line.html.haml4
-rw-r--r--app/views/projects/diffs/_parallel_view.html.haml5
-rw-r--r--app/views/projects/diffs/_single_image_diff.html.haml2
-rw-r--r--app/views/projects/diffs/_stats.html.haml4
-rw-r--r--app/views/projects/diffs/_text_file.html.haml2
-rw-r--r--app/views/projects/edit.html.haml33
-rw-r--r--app/views/projects/empty.html.haml20
-rw-r--r--app/views/projects/environments/_external_url.html.haml4
-rw-r--r--app/views/projects/environments/_form.html.haml15
-rw-r--r--app/views/projects/environments/_metrics_button.html.haml4
-rw-r--r--app/views/projects/environments/edit.html.haml4
-rw-r--r--app/views/projects/environments/empty.html.haml18
-rw-r--r--app/views/projects/environments/folder.html.haml2
-rw-r--r--app/views/projects/environments/index.html.haml4
-rw-r--r--app/views/projects/environments/metrics.html.haml9
-rw-r--r--app/views/projects/environments/new.html.haml6
-rw-r--r--app/views/projects/environments/show.html.haml31
-rw-r--r--app/views/projects/environments/terminal.html.haml6
-rw-r--r--app/views/projects/find_file/show.html.haml2
-rw-r--r--app/views/projects/forks/_fork_button.html.haml4
-rw-r--r--app/views/projects/forks/index.html.haml4
-rw-r--r--app/views/projects/graphs/charts.html.haml2
-rw-r--r--app/views/projects/hook_logs/show.html.haml2
-rw-r--r--app/views/projects/hooks/_index.html.haml2
-rw-r--r--app/views/projects/hooks/edit.html.haml2
-rw-r--r--app/views/projects/imports/new.html.haml2
-rw-r--r--app/views/projects/issues/_discussion.html.haml4
-rw-r--r--app/views/projects/issues/_issue.html.haml6
-rw-r--r--app/views/projects/issues/_nav_btns.html.haml2
-rw-r--r--app/views/projects/issues/_new_branch.html.haml3
-rw-r--r--app/views/projects/issues/index.atom.builder2
-rw-r--r--app/views/projects/issues/new.html.haml3
-rw-r--r--app/views/projects/issues/show.html.haml13
-rw-r--r--app/views/projects/jobs/_empty_state.html.haml18
-rw-r--r--app/views/projects/jobs/_empty_states.html.haml9
-rw-r--r--app/views/projects/jobs/_header.html.haml10
-rw-r--r--app/views/projects/jobs/_sidebar.html.haml95
-rw-r--r--app/views/projects/jobs/index.html.haml2
-rw-r--r--app/views/projects/jobs/show.html.haml102
-rw-r--r--app/views/projects/labels/index.html.haml27
-rw-r--r--app/views/projects/mattermosts/_team_selection.html.haml2
-rw-r--r--app/views/projects/merge_requests/_form.html.haml2
-rw-r--r--app/views/projects/merge_requests/_how_to_merge.html.haml8
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml6
-rw-r--r--app/views/projects/merge_requests/_mr_title.html.haml4
-rw-r--r--app/views/projects/merge_requests/_nav_btns.html.haml2
-rw-r--r--app/views/projects/merge_requests/creations/_new_compare.html.haml2
-rw-r--r--app/views/projects/merge_requests/creations/_new_submit.html.haml4
-rw-r--r--app/views/projects/merge_requests/creations/new.html.haml3
-rw-r--r--app/views/projects/merge_requests/diffs/_commit_widget.html.haml4
-rw-r--r--app/views/projects/merge_requests/diffs/_diffs.html.haml2
-rw-r--r--app/views/projects/merge_requests/show.html.haml13
-rw-r--r--app/views/projects/milestones/_deprecation_message.html.haml7
-rw-r--r--app/views/projects/milestones/_form.html.haml4
-rw-r--r--app/views/projects/milestones/index.html.haml2
-rw-r--r--app/views/projects/milestones/show.html.haml14
-rw-r--r--app/views/projects/mirrors/_instructions.html.haml4
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml4
-rw-r--r--app/views/projects/new.html.haml10
-rw-r--r--app/views/projects/notes/_actions.html.haml3
-rw-r--r--app/views/projects/notes/_more_actions_dropdown.html.haml5
-rw-r--r--app/views/projects/pages/_https_only.html.haml10
-rw-r--r--app/views/projects/pages/_list.html.haml8
-rw-r--r--app/views/projects/pages/show.html.haml2
-rw-r--r--app/views/projects/pages_domains/edit.html.haml2
-rw-r--r--app/views/projects/pages_domains/new.html.haml2
-rw-r--r--app/views/projects/pipeline_schedules/_form.html.haml2
-rw-r--r--app/views/projects/pipeline_schedules/index.html.haml2
-rw-r--r--app/views/projects/pipelines/_info.html.haml8
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml51
-rw-r--r--app/views/projects/pipelines/charts.html.haml3
-rw-r--r--app/views/projects/pipelines/new.html.haml2
-rw-r--r--app/views/projects/pipelines/show.html.haml10
-rw-r--r--app/views/projects/project_members/_new_project_group.html.haml20
-rw-r--r--app/views/projects/project_members/_new_project_member.html.haml4
-rw-r--r--app/views/projects/project_members/_new_shared_group.html.haml20
-rw-r--r--app/views/projects/project_members/_team.html.haml2
-rw-r--r--app/views/projects/project_members/import.html.haml2
-rw-r--r--app/views/projects/project_members/index.html.haml16
-rw-r--r--app/views/projects/project_templates/_built_in_templates.html.haml4
-rw-r--r--app/views/projects/protected_branches/shared/_branches_list.html.haml3
-rw-r--r--app/views/projects/protected_branches/shared/_create_protected_branch.html.haml5
-rw-r--r--app/views/projects/protected_tags/shared/_create_protected_tag.html.haml5
-rw-r--r--app/views/projects/protected_tags/shared/_tags_list.html.haml3
-rw-r--r--app/views/projects/registry/repositories/index.html.haml3
-rw-r--r--app/views/projects/releases/edit.html.haml2
-rw-r--r--app/views/projects/runners/_group_runners.html.haml2
-rw-r--r--app/views/projects/runners/_runner.html.haml2
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml32
-rw-r--r--app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_configuration_banner.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_metrics.html.haml12
-rw-r--r--app/views/projects/services/slack_slash_commands/_help.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml10
-rw-r--r--app/views/projects/settings/ci_cd/_badge.html.haml4
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml18
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml2
-rw-r--r--app/views/projects/settings/repository/show.html.haml1
-rw-r--r--app/views/projects/show.html.haml16
-rw-r--r--app/views/projects/snippets/_actions.html.haml2
-rw-r--r--app/views/projects/snippets/index.html.haml2
-rw-r--r--app/views/projects/tags/_tag.atom.builder19
-rw-r--r--app/views/projects/tags/index.atom.builder7
-rw-r--r--app/views/projects/tags/index.html.haml6
-rw-r--r--app/views/projects/tags/new.html.haml5
-rw-r--r--app/views/projects/tree/_blob_item.html.haml12
-rw-r--r--app/views/projects/tree/_spinner.html.haml3
-rw-r--r--app/views/projects/tree/_submodule_item.html.haml6
-rw-r--r--app/views/projects/tree/_tree_commit_column.html.haml2
-rw-r--r--app/views/projects/tree/_tree_content.html.haml2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml2
-rw-r--r--app/views/projects/tree/_tree_item.html.haml9
-rw-r--r--app/views/projects/tree/_tree_row.html.haml33
-rw-r--r--app/views/projects/triggers/_form.html.haml2
-rw-r--r--app/views/projects/triggers/_index.html.haml3
-rw-r--r--app/views/projects/update.js.haml2
-rw-r--r--app/views/projects/wikis/_form.html.haml10
-rw-r--r--app/views/projects/wikis/_main_links.html.haml2
-rw-r--r--app/views/projects/wikis/_new.html.haml2
-rw-r--r--app/views/projects/wikis/_pages_wiki_page.html.haml3
-rw-r--r--app/views/projects/wikis/_sidebar.html.haml2
-rw-r--r--app/views/projects/wikis/edit.html.haml14
-rw-r--r--app/views/projects/wikis/show.html.haml7
-rw-r--r--app/views/search/results/_blob.html.haml2
-rw-r--r--app/views/search/results/_snippet_blob.html.haml4
-rw-r--r--app/views/shared/_allow_request_access.html.haml6
-rw-r--r--app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml9
-rw-r--r--app/views/shared/_clone_panel.html.haml6
-rw-r--r--app/views/shared/_event_filter.html.haml12
-rw-r--r--app/views/shared/_field.html.haml2
-rw-r--r--app/views/shared/_file_highlight.html.haml6
-rw-r--r--app/views/shared/_group_form.html.haml45
-rw-r--r--app/views/shared/_label.html.haml47
-rw-r--r--app/views/shared/_labels_row.html.haml5
-rw-r--r--app/views/shared/_mini_pipeline_graph.html.haml1
-rw-r--r--app/views/shared/_mobile_clone_panel.html.haml13
-rw-r--r--app/views/shared/_new_project_item_select.html.haml4
-rw-r--r--app/views/shared/_old_visibility_level.html.haml6
-rw-r--r--app/views/shared/_personal_access_tokens_created_container.html.haml14
-rw-r--r--app/views/shared/_personal_access_tokens_form.html.haml2
-rw-r--r--app/views/shared/_personal_access_tokens_table.html.haml6
-rw-r--r--app/views/shared/_ping_consent.html.haml12
-rw-r--r--app/views/shared/_recaptcha_form.html.haml2
-rw-r--r--app/views/shared/_ref_switcher.html.haml2
-rw-r--r--app/views/shared/_user_dropdown_contributing_link.html.haml2
-rw-r--r--app/views/shared/_visibility_level.html.haml30
-rw-r--r--app/views/shared/_visibility_radios.html.haml2
-rw-r--r--app/views/shared/boards/_show.html.haml4
-rw-r--r--app/views/shared/boards/components/_board.html.haml15
-rw-r--r--app/views/shared/boards/components/sidebar/_labels.html.haml4
-rw-r--r--app/views/shared/empty_states/_labels.html.haml9
-rw-r--r--app/views/shared/empty_states/_merge_requests.html.haml2
-rw-r--r--app/views/shared/empty_states/_wikis.html.haml6
-rw-r--r--app/views/shared/groups/_empty_state.html.haml7
-rw-r--r--app/views/shared/groups/_search_form.html.haml4
-rw-r--r--app/views/shared/icons/_icon_status_scheduled.svg1
-rw-r--r--app/views/shared/icons/_icon_status_scheduled_borderless.svg1
-rw-r--r--app/views/shared/issuable/_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/_board_create_list_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/_close_reopen_button.html.haml22
-rw-r--r--app/views/shared/issuable/_filter.html.haml34
-rw-r--r--app/views/shared/issuable/_form.html.haml7
-rw-r--r--app/views/shared/issuable/_label_dropdown.html.haml5
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml69
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml8
-rw-r--r--app/views/shared/issuable/_sidebar_todo.html.haml4
-rw-r--r--app/views/shared/issuable/form/_metadata.html.haml3
-rw-r--r--app/views/shared/issuable/form/_title.html.haml2
-rw-r--r--app/views/shared/labels/_form.html.haml10
-rw-r--r--app/views/shared/labels/_nav.html.haml20
-rw-r--r--app/views/shared/labels/_sort_dropdown.html.haml9
-rw-r--r--app/views/shared/members/_access_request_buttons.html.haml4
-rw-r--r--app/views/shared/members/_member.html.haml4
-rw-r--r--app/views/shared/milestones/_delete_button.html.haml14
-rw-r--r--app/views/shared/milestones/_milestone.html.haml5
-rw-r--r--app/views/shared/milestones/_top.html.haml5
-rw-r--r--app/views/shared/notes/_comment_button.html.haml2
-rw-r--r--app/views/shared/notes/_edit_form.html.haml2
-rw-r--r--app/views/shared/notes/_note.html.haml2
-rw-r--r--app/views/shared/notes/_notes_with_form.html.haml4
-rw-r--r--app/views/shared/notifications/_button.html.haml4
-rw-r--r--app/views/shared/notifications/_custom_notifications.html.haml2
-rw-r--r--app/views/shared/projects/_list.html.haml3
-rw-r--r--app/views/shared/projects/_project.html.haml2
-rw-r--r--app/views/shared/projects/_search_form.html.haml2
-rw-r--r--app/views/shared/runners/_form.html.haml30
-rw-r--r--app/views/shared/runners/_runner_description.html.haml2
-rw-r--r--app/views/shared/runners/show.html.haml2
-rw-r--r--app/views/shared/snippets/_form.html.haml6
-rw-r--r--app/views/shared/web_hooks/_form.html.haml1
-rw-r--r--app/views/sherlock/queries/_general.html.haml2
-rw-r--r--app/views/sherlock/transactions/_queries.html.haml2
-rw-r--r--app/views/snippets/_actions.html.haml2
-rw-r--r--app/views/snippets/new.html.haml12
-rw-r--r--app/views/snippets/notes/_actions.html.haml3
-rw-r--r--app/views/u2f/_authenticate.html.haml10
-rw-r--r--app/views/u2f/_register.html.haml22
-rw-r--r--app/views/users/_overview.html.haml30
-rw-r--r--app/views/users/calendar_activities.html.haml42
-rw-r--r--app/views/users/show.html.haml42
-rw-r--r--app/workers/admin_email_worker.rb2
-rw-r--r--app/workers/all_queues.yml7
-rw-r--r--app/workers/archive_trace_worker.rb2
-rw-r--r--app/workers/authorized_projects_worker.rb2
-rw-r--r--app/workers/auto_devops/disable_worker.rb35
-rw-r--r--app/workers/background_migration_worker.rb14
-rw-r--r--app/workers/build_coverage_worker.rb2
-rw-r--r--app/workers/build_finished_worker.rb4
-rw-r--r--app/workers/build_hooks_worker.rb2
-rw-r--r--app/workers/build_queue_worker.rb2
-rw-r--r--app/workers/build_success_worker.rb18
-rw-r--r--app/workers/build_trace_sections_worker.rb2
-rw-r--r--app/workers/ci/archive_traces_cron_worker.rb2
-rw-r--r--app/workers/ci/build_schedule_worker.rb19
-rw-r--r--app/workers/ci/build_trace_chunk_flush_worker.rb2
-rw-r--r--app/workers/cluster_platform_configure_worker.rb22
-rw-r--r--app/workers/cluster_provision_worker.rb2
-rw-r--r--app/workers/concerns/application_worker.rb2
-rw-r--r--app/workers/concerns/auto_devops_queue.rb9
-rw-r--r--app/workers/concerns/gitlab/github_import/rescheduling_methods.rb2
-rw-r--r--app/workers/concerns/gitlab/github_import/stage_methods.rb2
-rw-r--r--app/workers/concerns/new_issuable.rb4
-rw-r--r--app/workers/concerns/waitable_worker.rb2
-rw-r--r--app/workers/create_gpg_signature_worker.rb2
-rw-r--r--app/workers/delete_container_repository_worker.rb36
-rw-r--r--app/workers/delete_diff_files_worker.rb2
-rw-r--r--app/workers/deployments/success_worker.rb17
-rw-r--r--app/workers/detect_repository_languages_worker.rb4
-rw-r--r--app/workers/email_receiver_worker.rb2
-rw-r--r--app/workers/expire_build_artifacts_worker.rb2
-rw-r--r--app/workers/expire_build_instance_artifacts_worker.rb4
-rw-r--r--app/workers/expire_job_cache_worker.rb2
-rw-r--r--app/workers/expire_pipeline_cache_worker.rb2
-rw-r--r--app/workers/gitlab/github_import/advance_stage_worker.rb4
-rw-r--r--app/workers/gitlab/github_import/refresh_import_jid_worker.rb2
-rw-r--r--app/workers/invalid_gpg_signature_update_worker.rb2
-rw-r--r--app/workers/issue_due_scheduler_worker.rb2
-rw-r--r--app/workers/mail_scheduler/issue_due_worker.rb2
-rw-r--r--app/workers/namespaceless_project_destroy_worker.rb2
-rw-r--r--app/workers/new_merge_request_worker.rb2
-rw-r--r--app/workers/new_note_worker.rb2
-rw-r--r--app/workers/object_storage/migrate_uploads_worker.rb4
-rw-r--r--app/workers/pages_domain_verification_worker.rb2
-rw-r--r--app/workers/pages_worker.rb2
-rw-r--r--app/workers/pipeline_hooks_worker.rb2
-rw-r--r--app/workers/pipeline_metrics_worker.rb4
-rw-r--r--app/workers/pipeline_notification_worker.rb2
-rw-r--r--app/workers/pipeline_process_worker.rb2
-rw-r--r--app/workers/pipeline_schedule_worker.rb2
-rw-r--r--app/workers/pipeline_success_worker.rb2
-rw-r--r--app/workers/pipeline_update_worker.rb2
-rw-r--r--app/workers/post_receive.rb13
-rw-r--r--app/workers/process_commit_worker.rb6
-rw-r--r--app/workers/project_cache_worker.rb2
-rw-r--r--app/workers/project_migrate_hashed_storage_worker.rb2
-rw-r--r--app/workers/project_service_worker.rb6
-rw-r--r--app/workers/propagate_service_template_worker.rb2
-rw-r--r--app/workers/prune_old_events_worker.rb9
-rw-r--r--app/workers/prune_web_hook_logs_worker.rb2
-rw-r--r--app/workers/reactive_caching_worker.rb2
-rw-r--r--app/workers/remove_expired_group_links_worker.rb2
-rw-r--r--app/workers/remove_old_web_hook_logs_worker.rb2
-rw-r--r--app/workers/repository_check/batch_worker.rb6
-rw-r--r--app/workers/repository_check/clear_worker.rb2
-rw-r--r--app/workers/repository_check/single_repository_worker.rb2
-rw-r--r--app/workers/run_pipeline_schedule_worker.rb2
-rw-r--r--app/workers/stage_update_worker.rb2
-rw-r--r--app/workers/stuck_ci_jobs_worker.rb32
-rw-r--r--app/workers/stuck_import_jobs_worker.rb50
-rw-r--r--app/workers/stuck_merge_jobs_worker.rb6
-rw-r--r--app/workers/update_head_pipeline_for_merge_request_worker.rb2
-rw-r--r--app/workers/update_merge_requests_worker.rb2
-rwxr-xr-xbin/pkgr_before_precompile.sh2
-rwxr-xr-xbin/profile-url2
-rwxr-xr-xbin/rails2
-rwxr-xr-xbin/rake2
-rwxr-xr-xbin/rspec2
-rwxr-xr-xbin/secpick16
-rwxr-xr-xbin/setup4
-rwxr-xr-xbin/storage_check11
-rwxr-xr-xbin/web5
-rwxr-xr-xbin/web_puma63
-rw-r--r--changelogs/archive.md16
-rw-r--r--changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml4
-rw-r--r--changelogs/unreleased/1756-set-iid-via-api.yml5
-rw-r--r--changelogs/unreleased/18933-render-index-as-readme.yml5
-rw-r--r--changelogs/unreleased/21480-parallel-job-keyword-mvc.yml5
-rw-r--r--changelogs/unreleased/21970-fix-bamboo-results.yml5
-rw-r--r--changelogs/unreleased/22311-fix-duplicated-key-in-license-management-job.yml5
-rw-r--r--changelogs/unreleased/22717-single-letter-identifier-external-issue-tracker.yml5
-rw-r--r--changelogs/unreleased/23705-add-single-file-download-in-repo.yml5
-rw-r--r--changelogs/unreleased/25140-disable-stop-button.yml5
-rw-r--r--changelogs/unreleased/25990-improve-web-terminal.yml5
-rw-r--r--changelogs/unreleased/25990-interactive-web-terminals-authorization.yml5
-rw-r--r--changelogs/unreleased/25990-web-terminal-improvements.yml5
-rw-r--r--changelogs/unreleased/26723-discussion-filters.yml5
-rw-r--r--changelogs/unreleased/27231-add-license-data-to-projects-endpoint.yml5
-rw-r--r--changelogs/unreleased/27456-improve-feedback-when-dev-cannot-push-to-empty-repo.yml5
-rw-r--r--changelogs/unreleased/2747-protected-environments-backend-ce.yml5
-rw-r--r--changelogs/unreleased/28249-add-pagination.yml5
-rw-r--r--changelogs/unreleased/28682-can-merge-branch-before-build-is-started.yml5
-rw-r--r--changelogs/unreleased/28930-add-project-reference-filter.yml5
-rw-r--r--changelogs/unreleased/29278-commits-page-tooltips.yml5
-rw-r--r--changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml5
-rw-r--r--changelogs/unreleased/32783-api-all-members-with-ancestors.yml6
-rw-r--r--changelogs/unreleased/32821-better-error-message-add-invalid-user-to-project.yml5
-rw-r--r--changelogs/unreleased/32959-update-todo-icon.yml5
-rw-r--r--changelogs/unreleased/34572-ssh-certificates.yml5
-rw-r--r--changelogs/unreleased/34758-create-group-clusters.yml5
-rw-r--r--changelogs/unreleased/34758-group-cluster-controller.yml5
-rw-r--r--changelogs/unreleased/35952-keep-admin-settings-open-after-submit.yml5
-rw-r--r--changelogs/unreleased/36409-frontend-for-clarifying-the-usefulness-of-the-search-bar.yml5
-rw-r--r--changelogs/unreleased/37727-fix-file-delete-redirect.yml6
-rw-r--r--changelogs/unreleased/38304-username-API-call-case-sensitive.yml5
-rw-r--r--changelogs/unreleased/38604-add-private-profile.yml5
-rw-r--r--changelogs/unreleased/40085-add-a-create_merge_request-quick-action.yml5
-rw-r--r--changelogs/unreleased/40372-prometheus-dashboard-broken-on-firefox.yml5
-rw-r--r--changelogs/unreleased/40973-disable-rack-attack-by-default.yml5
-rw-r--r--changelogs/unreleased/41416-making-instance-wide-data-tools-more-accessible.yml5
-rw-r--r--changelogs/unreleased/41545-gitlab-merge-request-status-could-not-connect-to-the-ci-server-please-check-your-settings-and-try-again.yml5
-rw-r--r--changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml5
-rw-r--r--changelogs/unreleased/41784-monitoring-graph-popovers.yml5
-rw-r--r--changelogs/unreleased/41875-allow-pipelines-to-be-deleted-by-project-owners.yml5
-rw-r--r--changelogs/unreleased/41996-copy-to-clipboard-tooltip-appears-under-modal.yml5
-rw-r--r--changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml5
-rw-r--r--changelogs/unreleased/42611-removed-branch-link.yml5
-rw-r--r--changelogs/unreleased/42790-improve-feedback-for-internal-git-access-checks-timeouts.yml5
-rw-r--r--changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml5
-rw-r--r--changelogs/unreleased/43312-remove_user_activity_workers.yml5
-rw-r--r--changelogs/unreleased/43422-Update-images-in-group-docs.yml5
-rw-r--r--changelogs/unreleased/43521-keep-personal-emails-private.yml5
-rw-r--r--changelogs/unreleased/44012-filter-reactions-none-any.yml5
-rw-r--r--changelogs/unreleased/44127-board-label-edit-drop-down-is-showing-incorrect-selected-labels-summary.yml5
-rw-r--r--changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml5
-rw-r--r--changelogs/unreleased/45068-no-longer-require-a-deploy-to-start-prometheus-monitoring.yml5
-rw-r--r--changelogs/unreleased/45318-junit-FE.yml5
-rw-r--r--changelogs/unreleased/45318-vuex-store.yml5
-rw-r--r--changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml5
-rw-r--r--changelogs/unreleased/45663-tag-quick-action-on-commit-comments.yml5
-rw-r--r--changelogs/unreleased/45669-table-in-jobs-on-pipeline.yml5
-rw-r--r--changelogs/unreleased/46165-web-ide-branch-picker.yml5
-rw-r--r--changelogs/unreleased/46535-orphaned-uploads.yml5
-rw-r--r--changelogs/unreleased/46703-group-dashboard-line-height-is-too-tall-for-group-names.yml5
-rw-r--r--changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml5
-rw-r--r--changelogs/unreleased/46884-remove-card-title.yml5
-rw-r--r--changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml5
-rw-r--r--changelogs/unreleased/46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects.yml5
-rw-r--r--changelogs/unreleased/47008-issue-board-card-design.yml5
-rw-r--r--changelogs/unreleased/47156-improve-auto-devops-settings.yml5
-rw-r--r--changelogs/unreleased/47419-Fix-breadcrumbs.yml5
-rw-r--r--changelogs/unreleased/47548-monospace-commit-messages.yml5
-rw-r--r--changelogs/unreleased/47728-mr-api-documentation-changes.yml5
-rw-r--r--changelogs/unreleased/47752-buttons-on-new-file-page-wrap-outside-of-container-for-long-branch-names.yml5
-rw-r--r--changelogs/unreleased/47768-web-ide-redesign-header.yml5
-rw-r--r--changelogs/unreleased/48036-fix-web-ide-blob-crash.yml5
-rw-r--r--changelogs/unreleased/48055-web-ide-resize-handles.yml5
-rw-r--r--changelogs/unreleased/48098-mutual-auth-cluster-applications.yml6
-rw-r--r--changelogs/unreleased/48246-osw-load-diffs-improvement.yml5
-rw-r--r--changelogs/unreleased/48320-cancel-a-created-job.yml5
-rw-r--r--changelogs/unreleased/48419-charts-with-long-label-appear-oversized.yml5
-rw-r--r--changelogs/unreleased/48456-fix-system-level-labels-admin-ui.yml5
-rw-r--r--changelogs/unreleased/48475-gitlab-pages-settings-regressions.yml5
-rw-r--r--changelogs/unreleased/48494-fix-merge-request-buttons-spacing.yml5
-rw-r--r--changelogs/unreleased/48537-update-avatar-only-via-api.yml5
-rw-r--r--changelogs/unreleased/48542-code-link.yml5
-rw-r--r--changelogs/unreleased/48617-promoting-milestone.yml5
-rw-r--r--changelogs/unreleased/48636-new-mr-card-styles.yml5
-rw-r--r--changelogs/unreleased/48657-persist-auto-devops-banner-dismissal-per-user-cookie.yml5
-rw-r--r--changelogs/unreleased/48684-sort-projects-by-stars-in-groups.yml4
-rw-r--r--changelogs/unreleased/48731-show-empty-state-on-wiki-only-projects.yml6
-rw-r--r--changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml5
-rw-r--r--changelogs/unreleased/48804-redesign-gcp-banner.yml5
-rw-r--r--changelogs/unreleased/48823-copy-gfm.yml5
-rw-r--r--changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml6
-rw-r--r--changelogs/unreleased/48889-message-for-were-merged-into.yml5
-rw-r--r--changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml5
-rw-r--r--changelogs/unreleased/48934.yml5
-rw-r--r--changelogs/unreleased/48976-fix-sti-background-migration.yml6
-rw-r--r--changelogs/unreleased/49025-docs-kubernetes-tiller.yml5
-rw-r--r--changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml5
-rw-r--r--changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml5
-rw-r--r--changelogs/unreleased/49161-disable-toggle-comments.yml5
-rw-r--r--changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml5
-rw-r--r--changelogs/unreleased/49291-fix-memory-graph-component-typo.yml5
-rw-r--r--changelogs/unreleased/49324-add-support-for-tar-gz-autodevops-charts.yml5
-rw-r--r--changelogs/unreleased/49364-fix-broadcast-margin.yml5
-rw-r--r--changelogs/unreleased/49375-move-help-popover.yml5
-rw-r--r--changelogs/unreleased/49403-redesign-activity-feed.yml4
-rw-r--r--changelogs/unreleased/49417-improve-settings-pages-design-by-prioritizing-content-group-settings.yml5
-rw-r--r--changelogs/unreleased/49479-hide-unmerged-env-perf-stats.yml5
-rw-r--r--changelogs/unreleased/49499-list-of-projects-not-loading-when-trying-to-create-an-issue-from-a-board-typeerror.yml5
-rw-r--r--changelogs/unreleased/49591-use-cached-readme-blobs.yml5
-rw-r--r--changelogs/unreleased/49701-sorting-by-name-on-milestones-page-error.yml5
-rw-r--r--changelogs/unreleased/49726-upgrade-helm-to-2-11.yml5
-rw-r--r--changelogs/unreleased/49747-update-poll-2xx.yml5
-rw-r--r--changelogs/unreleased/49776-pipeline-job-log-page-uses-too-much-cpu-for-loading-animation.yml5
-rw-r--r--changelogs/unreleased/49830-use-helm-272.yml5
-rw-r--r--changelogs/unreleased/49835-increase-width.yml5
-rw-r--r--changelogs/unreleased/49851-link-to-runners.yml6
-rw-r--r--changelogs/unreleased/49854-recover-mr-regression-fixes-safe-1.yml5
-rw-r--r--changelogs/unreleased/49854-recover-mr-regression-fixes-safe-2.yml5
-rw-r--r--changelogs/unreleased/49854-recover-mr-regression-fixes-safe-3.yml5
-rw-r--r--changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml5
-rw-r--r--changelogs/unreleased/49899-merge-request-e-mail-link-has-full-url.yml5
-rw-r--r--changelogs/unreleased/49966-improve-junit-fe.yml5
-rw-r--r--changelogs/unreleased/50063-add-missing-i18n-strings-to-issue-boards.yml5
-rw-r--r--changelogs/unreleased/50101-stuck-component.yml5
-rw-r--r--changelogs/unreleased/50126-blocked-user-card.yml5
-rw-r--r--changelogs/unreleased/50185-fix-broken-file-name-navigation.yml5
-rw-r--r--changelogs/unreleased/50243-auto-devops-behind-a-proxy.yml6
-rw-r--r--changelogs/unreleased/50728-re-arrange-help-related-user-menu-items-into-new-help-menu.yml5
-rw-r--r--changelogs/unreleased/50904-job-log.yml5
-rw-r--r--changelogs/unreleased/50962-create-new-group-rename-form-fields-and-update-ui.yml5
-rw-r--r--changelogs/unreleased/51259-ci-cd-gitlab-ui-1.yml5
-rw-r--r--changelogs/unreleased/51259-ci-cd-gitlab-ui.yml5
-rw-r--r--changelogs/unreleased/51259-ci-cd-tooltips.yml6
-rw-r--r--changelogs/unreleased/51306-fix-inaccessible-dropdown-for-codeless-projects.yml5
-rw-r--r--changelogs/unreleased/51335-fail-early-when-user-cannot-be-identified.yml5
-rw-r--r--changelogs/unreleased/51386-broken-border-reports.yml5
-rw-r--r--changelogs/unreleased/51527-xss-in-mr-source-branch.yml5
-rw-r--r--changelogs/unreleased/51620-cannot-add-label-to-issue-from-board.yml4
-rw-r--r--changelogs/unreleased/51716-add-kubernetes-namespace-background-migration.yml5
-rw-r--r--changelogs/unreleased/51716-add-kubernetes-namespace-model.yml5
-rw-r--r--changelogs/unreleased/51716-create-kube-namespace.yml5
-rw-r--r--changelogs/unreleased/51955-change-single-item-breadcrumbs-to-page-titles.yml5
-rw-r--r--changelogs/unreleased/51959-branch-and-tag-name-links.yml5
-rw-r--r--changelogs/unreleased/51972-prometheus-not-showing-as-installed-even-though-it-is.yml5
-rw-r--r--changelogs/unreleased/52059-filter-milestone-by-none-any.yml5
-rw-r--r--changelogs/unreleased/52115-Link-button-in-markdown-editor-should-recognize-URLs.yml5
-rw-r--r--changelogs/unreleased/52122-fix-broken-whitespace-button.yml5
-rw-r--r--changelogs/unreleased/52147-loading-state.yml5
-rw-r--r--changelogs/unreleased/52193-Pipeline-graph-is-not-vertically-aligned-in-commit-page.yml5
-rw-r--r--changelogs/unreleased/52202-consider-moving-isjobstuck-verification-to-backend.yml5
-rw-r--r--changelogs/unreleased/52299-follow-up-from-resolve-add-status-message-from-within-user-menu.yml5
-rw-r--r--changelogs/unreleased/52300-pool-repositories.yml5
-rw-r--r--changelogs/unreleased/52353-keyboard-navigation-project-slug-is-not-focused-on-new-project-page.yml5
-rw-r--r--changelogs/unreleased/52367-cleanup-web-hooks-columns.yml5
-rw-r--r--changelogs/unreleased/52382-filter-milestone-api-none-any.yml5
-rw-r--r--changelogs/unreleased/52383-ui-filter-assignee-none-any.yml5
-rw-r--r--changelogs/unreleased/52384-api-filter-assignee-none-any.yml5
-rw-r--r--changelogs/unreleased/52385-search-bar-for-dashboard-list.yml5
-rw-r--r--changelogs/unreleased/52408-pip-cache-dir-to-cache-python-dependencies.yml5
-rw-r--r--changelogs/unreleased/52421-show-canary-no-canary-in-the-performance-bar.yml5
-rw-r--r--changelogs/unreleased/52477-add-iid-headers-to-emails.yml5
-rw-r--r--changelogs/unreleased/52527-harden-wiki-against-missing-last-version.yml5
-rw-r--r--changelogs/unreleased/52545-guest-create-issue-in-group-board.yml5
-rw-r--r--changelogs/unreleased/52548-links-in-tabs-of-the-labels-index-pages-ends-with-html.yml5
-rw-r--r--changelogs/unreleased/52559-applications-api-get-delete.yml5
-rw-r--r--changelogs/unreleased/52686-project-slug-does-not-auto-populate-in-ie11.yml5
-rw-r--r--changelogs/unreleased/52692-catch-redirect-loops.yml5
-rw-r--r--changelogs/unreleased/52712-further-ui-improvements-to-profile-overview-tab.yml5
-rw-r--r--changelogs/unreleased/52767-more-chaos-for-gitlab.yml5
-rw-r--r--changelogs/unreleased/52771-ldap-users-can-t-choose-private-or-internal-when-creating-a-new-group.yml5
-rw-r--r--changelogs/unreleased/52772-assign-me-quick-action-doesn-t-work-if-there-is-extra-white-space.yml5
-rw-r--r--changelogs/unreleased/52780-stale-pipeline-status-cache-for-_project-after-disabling-pipelines.yml5
-rw-r--r--changelogs/unreleased/52840-fix-runners-details-page.yml5
-rw-r--r--changelogs/unreleased/52886-fix-broken-master.yml5
-rw-r--r--changelogs/unreleased/52925-scheduled-pipelines-ui-problems.yml5
-rw-r--r--changelogs/unreleased/52940-fix-internal-email-pattern-not-respected.yml5
-rw-r--r--changelogs/unreleased/52993-ldap-rename_provider-rake-task-broken.yml5
-rw-r--r--changelogs/unreleased/53013-duplicate-escape.yml5
-rw-r--r--changelogs/unreleased/53023-endless-scroll-loader-is-visible-on-user-profile-overview-page.yml4
-rw-r--r--changelogs/unreleased/53052-mg-fix-broken-ie11.yml5
-rw-r--r--changelogs/unreleased/53055-combine-date-util-functions.yml5
-rw-r--r--changelogs/unreleased/53133-jobs-list.yml5
-rw-r--r--changelogs/unreleased/53155-structured-logs-params-array.yml5
-rw-r--r--changelogs/unreleased/53227-empty-list.yml6
-rw-r--r--changelogs/unreleased/53230-remove_personal_access_tokens_finder_find_by_method.yml5
-rw-r--r--changelogs/unreleased/53270-remove-mousetrap-rails.yml5
-rw-r--r--changelogs/unreleased/53273-update-moment-to-2-22-2.yml5
-rw-r--r--changelogs/unreleased/53289-update-haml_lint-to-0-28-0.yml5
-rw-r--r--changelogs/unreleased/53290-incorrect-project-list-order-select-default-label.yml5
-rw-r--r--changelogs/unreleased/53291-update-ffaker-to-2-10-0.yml5
-rw-r--r--changelogs/unreleased/53362-allow-concurrency-in-puma.yml5
-rw-r--r--changelogs/unreleased/53450-wrong-value-for-kubernetes_version-variable.yml5
-rw-r--r--changelogs/unreleased/53533-fix-broken-link.yml5
-rw-r--r--changelogs/unreleased/53535-sticky-archived.yml5
-rw-r--r--changelogs/unreleased/53626-update-config-map-on-install-retry.yml5
-rw-r--r--changelogs/unreleased/53636-fix-rendering-of-any-user-filter.yml5
-rw-r--r--changelogs/unreleased/53640-follow-up-from-resolve-redesign-activity-feed.yml4
-rw-r--r--changelogs/unreleased/53700-hashed-storagemigration.yml5
-rw-r--r--changelogs/unreleased/53816-empty-label-menu-if-not-logged-in.yml5
-rw-r--r--changelogs/unreleased/53879-kube-token-nil.yml5
-rw-r--r--changelogs/unreleased/53888-missing-favicon.yml5
-rw-r--r--changelogs/unreleased/53972-fix-fill-shards.yml5
-rw-r--r--changelogs/unreleased/54002-activity-feed-missing-padding-in-event-note-when-a-branch-is-deleted.yml5
-rw-r--r--changelogs/unreleased/54004-update-asana-to-0-8-1.yml5
-rw-r--r--changelogs/unreleased/54010-update-asciidoctor-to-1-5-8.yml5
-rw-r--r--changelogs/unreleased/54015-Markdown-Editor-improve-Cursor-placement.yml5
-rw-r--r--changelogs/unreleased/54021-empty-button.yml5
-rw-r--r--changelogs/unreleased/54093-the-default_value_for-gem-doesn-t-handle-actioncontroller-parameters-correctly.yml7
-rw-r--r--changelogs/unreleased/6500-fix-misaligned-approvers-dropdown.yml5
-rw-r--r--changelogs/unreleased/6860-FE-instance-level-project-templates.yml5
-rw-r--r--changelogs/unreleased/7737-ci-pipeline-view-slowed-down-massivly-if-security-tabs-has-many-entries-ee.yml5
-rw-r--r--changelogs/unreleased/Fix-pipeline-redirect.yml5
-rw-r--r--changelogs/unreleased/ab-45608-stuck-mr-query.yml5
-rw-r--r--changelogs/unreleased/ac-post-merge-pipeline.yml5
-rw-r--r--changelogs/unreleased/accept-rf3-2822-compliant-addresses.yml5
-rw-r--r--changelogs/unreleased/add-action-to-deployment.yml5
-rw-r--r--changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml5
-rw-r--r--changelogs/unreleased/add-failure-reason-for-execution-timeout.yml5
-rw-r--r--changelogs/unreleased/add-gl-link-to-markdown-header.yml5
-rw-r--r--changelogs/unreleased/add-homepage-link-to-status-pages.yml5
-rw-r--r--changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml5
-rw-r--r--changelogs/unreleased/add-new-kubernetes-spec-helpers.yml5
-rw-r--r--changelogs/unreleased/add-role-binding-to-kubeclient.yml5
-rw-r--r--changelogs/unreleased/add-scheduled-flag-to-job-entity.yml5
-rw-r--r--changelogs/unreleased/add-total-time-flat-printer-for-profiling.yml6
-rw-r--r--changelogs/unreleased/add_google_noto_color_emoji_font.yml5
-rw-r--r--changelogs/unreleased/added-glob-for-ci-changes-detection.yml5
-rw-r--r--changelogs/unreleased/an-gitaly-version-0-133-0.yml5
-rw-r--r--changelogs/unreleased/an-multithreading.yml5
-rw-r--r--changelogs/unreleased/api-minimal-access-level.yml5
-rw-r--r--changelogs/unreleased/artifact-format-v2-with-parser.yml5
-rw-r--r--changelogs/unreleased/artifact-format-v2.yml5
-rw-r--r--changelogs/unreleased/ashmckenzie-hmac-token-decode-and-tests.yml5
-rw-r--r--changelogs/unreleased/auto-devops-support-for-group-security-dashboard.yml5
-rw-r--r--changelogs/unreleased/auto_devops_kubernetes_active.yml5
-rw-r--r--changelogs/unreleased/avoid-lock-when-introduce-new-failure-reason.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-add-discord-service.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-bump-mermaid.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml5
-rw-r--r--changelogs/unreleased/bvl-graphql-wip-mutation.yml5
-rw-r--r--changelogs/unreleased/bvl-merge-base-api.yml5
-rw-r--r--changelogs/unreleased/bvl-merge-base-multiple-revisions.yml5
-rw-r--r--changelogs/unreleased/bvl-patches-via-mail.yml5
-rw-r--r--changelogs/unreleased/bvl-preload-user-status-for-events.yml5
-rw-r--r--changelogs/unreleased/bvl-user-status-message-35463.yml5
-rw-r--r--changelogs/unreleased/bw-automatically-navigate-to-last-board-visited.yml5
-rw-r--r--changelogs/unreleased/ccr-43034_issues_controller_100_queries.yml5
-rw-r--r--changelogs/unreleased/ccr-51052_keep_labels_on_issue.yml5
-rw-r--r--changelogs/unreleased/ccr-51520_change_milestone_email.yml5
-rw-r--r--changelogs/unreleased/ce-53347_fix_impersonation_tokens.yml5
-rw-r--r--changelogs/unreleased/ce-5666-backport.yml5
-rw-r--r--changelogs/unreleased/ce-5666-optimize_querying_manageable_groups.yml5
-rw-r--r--changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml5
-rw-r--r--changelogs/unreleased/change-branch-font-type-in-tag-creation.yml5
-rw-r--r--changelogs/unreleased/ci-builds-status-index.yml5
-rw-r--r--changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml5
-rw-r--r--changelogs/unreleased/cr-add-group-milestone-to-dashboard.yml5
-rw-r--r--changelogs/unreleased/cr-add-path-of-group-milestone.yml5
-rw-r--r--changelogs/unreleased/custom_wiki_sidebar.yml5
-rw-r--r--changelogs/unreleased/da-fix-does-not-import-projects-over-ssh.yml5
-rw-r--r--changelogs/unreleased/diff-expand-all-button.yml5
-rw-r--r--changelogs/unreleased/diff-stats-perf-bar.yml5
-rw-r--r--changelogs/unreleased/disallow-retry-of-old-builds.yml5
-rw-r--r--changelogs/unreleased/discussion-perf-improvement.yml5
-rw-r--r--changelogs/unreleased/dm-api-merge-requests-index-merged-at.yml5
-rw-r--r--changelogs/unreleased/dm-commit-email-select-options.yml5
-rw-r--r--changelogs/unreleased/docs-minor-aws-fixes.yml5
-rw-r--r--changelogs/unreleased/drop-allow_overflow-option-duration_in_numbers.yml5
-rw-r--r--changelogs/unreleased/drop-default-value-status-deployments.yml5
-rw-r--r--changelogs/unreleased/drop-gcp-cluster-table.yml5
-rw-r--r--changelogs/unreleased/dz-labels-search.yml5
-rw-r--r--changelogs/unreleased/dz-manifest-import.yml5
-rw-r--r--changelogs/unreleased/enable-frozen-string-lib-gitlab.yml5
-rw-r--r--changelogs/unreleased/even-more-frozen-string-lib.yml5
-rw-r--r--changelogs/unreleased/fast_project_blob_path.yml5
-rw-r--r--changelogs/unreleased/fe-ac-review-app-changes-33418.yml5
-rw-r--r--changelogs/unreleased/feat-add-default-avatar-to-group.yml5
-rw-r--r--changelogs/unreleased/feature-gb-email-delivery-metrics.yml5
-rw-r--r--changelogs/unreleased/feature-gb-improve-include-config-errors-reporting.yml5
-rw-r--r--changelogs/unreleased/feature-gb-login-activity-metrics.yml5
-rw-r--r--changelogs/unreleased/feature-improved-branch-filter-sorting.yml6
-rw-r--r--changelogs/unreleased/features-show-project-id-on-home-panel.yml5
-rw-r--r--changelogs/unreleased/fix-53298.yml5
-rw-r--r--changelogs/unreleased/fix-base64-encoded-file-uploads.yml5
-rw-r--r--changelogs/unreleased/fix-deployment-metrics-in-mr-widget.yml6
-rw-r--r--changelogs/unreleased/fix-diff-note.yml5
-rw-r--r--changelogs/unreleased/fix-email-confirmation-addtional-email.yml5
-rw-r--r--changelogs/unreleased/fix-error-handling-bugs-in-kubernetes-integration.yml5
-rw-r--r--changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml5
-rw-r--r--changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml5
-rw-r--r--changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml5
-rw-r--r--changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml5
-rw-r--r--changelogs/unreleased/fix-multiple-scopes.yml5
-rw-r--r--changelogs/unreleased/fix-not-render-emoji.yml5
-rw-r--r--changelogs/unreleased/fix-pipeline-fixture-seeder.yml5
-rw-r--r--changelogs/unreleased/fix-project-api-archived.yml5
-rw-r--r--changelogs/unreleased/fix-prometheus-updated-status.yml5
-rw-r--r--changelogs/unreleased/fix-search-bar.yml5
-rw-r--r--changelogs/unreleased/fix-storage-size-for-artifacts-change.yml5
-rw-r--r--changelogs/unreleased/fix-stuck-import-jobs-query-performance-issue.yml5
-rw-r--r--changelogs/unreleased/fix-tags-for-envs.yml5
-rw-r--r--changelogs/unreleased/fj-37736-improve-performance-post-receive-create-gpg-siganture-worker.yml5
-rw-r--r--changelogs/unreleased/fj-41213-api-update-submodule-commit.yml5
-rw-r--r--changelogs/unreleased/fj-48123-fix-gitlab-import.yml5
-rw-r--r--changelogs/unreleased/fj-49014-wiki-search-error.yml5
-rw-r--r--changelogs/unreleased/fj-49512-fix-gitlab-git-pages-encoding.yml5
-rw-r--r--changelogs/unreleased/fj-49802-bug-api-set-http-headers.yml5
-rw-r--r--changelogs/unreleased/fj-50890-fix-commit-message-wiki-new-page.yml5
-rw-r--r--changelogs/unreleased/fj-52406-wiki-file-content-disposition.yml5
-rw-r--r--changelogs/unreleased/fj-bump-gitaly-0-129-0.yml5
-rw-r--r--changelogs/unreleased/fl-missing-i18n.yml5
-rw-r--r--changelogs/unreleased/fl-reduce-ee-conflicts-reports-code.yml5
-rw-r--r--changelogs/unreleased/fl-update-svgs.yml5
-rw-r--r--changelogs/unreleased/floating-avarage-commit-numbers.yml5
-rw-r--r--changelogs/unreleased/frozen-string-danger.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-models-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-models.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-serializers.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-services.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-vestigial.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner-even-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-lib-gitlab-ci-remain.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-lib-gitlab-ci.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-lib-gitlab.yml5
-rw-r--r--changelogs/unreleased/frozen-string-lib-gitlab-even-even-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-lib-gitlab-even-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-lib-gitlab-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-vestigial.yml5
-rw-r--r--changelogs/unreleased/full-list-of-vulnerabilities-5239.yml5
-rw-r--r--changelogs/unreleased/git-rerere-link-doc-update.yml5
-rw-r--r--changelogs/unreleased/gl-ui-loading-icon.yml5
-rw-r--r--changelogs/unreleased/gl-ui-modal.yml5
-rw-r--r--changelogs/unreleased/gl-ui-pagination.yml5
-rw-r--r--changelogs/unreleased/gl-ui-progress-bar.yml5
-rw-r--r--changelogs/unreleased/gl-ui-tooltip.yml5
-rw-r--r--changelogs/unreleased/gt-add-transparent-background-to-markdown-header-tabs.yml5
-rw-r--r--changelogs/unreleased/gt-align-sign-in-button.yml5
-rw-r--r--changelogs/unreleased/gt-change-breadcrumb-title-for-contribution-charts.yml5
-rw-r--r--changelogs/unreleased/gt-fix-ide-typos-in-props.yml5
-rw-r--r--changelogs/unreleased/gt-fix-quick-links-button-styles.yml5
-rw-r--r--changelogs/unreleased/gt-fix-typo-in-notebook-props.yml5
-rw-r--r--changelogs/unreleased/gt-fix-typos-in-lib.yml5
-rw-r--r--changelogs/unreleased/gt-remove-empty-spec-describe-blocks.yml5
-rw-r--r--changelogs/unreleased/gt-remove-prometheus-configuration-help-text.yml5
-rw-r--r--changelogs/unreleased/gt-remove-unused-project-method.yml5
-rw-r--r--changelogs/unreleased/gt-rename-diffs-store-variable.yml5
-rw-r--r--changelogs/unreleased/gt-truncate-milestone-title-on-collapsed-sidebar.yml5
-rw-r--r--changelogs/unreleased/gt-update-environments-empty-state.yml5
-rw-r--r--changelogs/unreleased/gt-update-project-and-group-labels-empty-state.yml5
-rw-r--r--changelogs/unreleased/gt-update-wiki-empty-state.yml5
-rw-r--r--changelogs/unreleased/gt-use-merge-request-prefix-in-event-feed-title.yml5
-rw-r--r--changelogs/unreleased/hangouts_chat_integration.yml5
-rw-r--r--changelogs/unreleased/ide-codesandbox-poc.yml5
-rw-r--r--changelogs/unreleased/ide-delete-entries.yml5
-rw-r--r--changelogs/unreleased/ide-file-templates-clear.yml5
-rw-r--r--changelogs/unreleased/ide-header-buttons-tooltip.yml5
-rw-r--r--changelogs/unreleased/ide-open-empty-merge-request.yml5
-rw-r--r--changelogs/unreleased/ide-pipeline-icon-open.yml5
-rw-r--r--changelogs/unreleased/ide-rename-files.yml5
-rw-r--r--changelogs/unreleased/ide-row-dropdown-design-update.yml5
-rw-r--r--changelogs/unreleased/ide-warn-staged-files.yml5
-rw-r--r--changelogs/unreleased/ignore-environment-validation-failure.yml5
-rw-r--r--changelogs/unreleased/improve-junit-support-be.yml5
-rw-r--r--changelogs/unreleased/improve-metadata-access-performance.yml5
-rw-r--r--changelogs/unreleased/introduce-knative-support.yml5
-rw-r--r--changelogs/unreleased/issue_43602.yml5
-rw-r--r--changelogs/unreleased/issue_44821.yml5
-rw-r--r--changelogs/unreleased/issue_47709.yml5
-rw-r--r--changelogs/unreleased/issue_49936.yml5
-rw-r--r--changelogs/unreleased/issue_51323.yml5
-rw-r--r--changelogs/unreleased/jivl-fix-bar-char-transient-spec-failure.yml5
-rw-r--r--changelogs/unreleased/jprovazn-resource-events.yml5
-rw-r--r--changelogs/unreleased/jr-archive-hook.yml5
-rw-r--r--changelogs/unreleased/jramsay-42673-commit-tooltip.yml5
-rw-r--r--changelogs/unreleased/jupyter-image.yml5
-rw-r--r--changelogs/unreleased/kinolaev-master-patch-91872.yml5
-rw-r--r--changelogs/unreleased/kp-6927-epic-dates-from-milestone.yml5
-rw-r--r--changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml5
-rw-r--r--changelogs/unreleased/kubernetes-http-response-code.yml5
-rw-r--r--changelogs/unreleased/leipert-fix-pipelines-view.yml5
-rw-r--r--changelogs/unreleased/lfs-project-attribute-alias.yml5
-rw-r--r--changelogs/unreleased/max_retries_when.yml5
-rw-r--r--changelogs/unreleased/mk-add-local-project-uploads-cleanup-task.yml5
-rw-r--r--changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml5
-rw-r--r--changelogs/unreleased/more-frozen-string-enable-lib.yml5
-rw-r--r--changelogs/unreleased/mr-creation-source-project-filtering.yml5
-rw-r--r--changelogs/unreleased/mr-file-list.yml5
-rw-r--r--changelogs/unreleased/mr-image-commenting.yml5
-rw-r--r--changelogs/unreleased/mr-tree-filter-path-name.yml5
-rw-r--r--changelogs/unreleased/osw-comment-on-any-line-on-diffs.yml5
-rw-r--r--changelogs/unreleased/osw-fallback-on-blank-refs.yml5
-rw-r--r--changelogs/unreleased/osw-fix-missing-and-duplicated-milestones-on-list.yml5
-rw-r--r--changelogs/unreleased/osw-fix-n-plus-1-for-mrs-without-merge-info.yml5
-rw-r--r--changelogs/unreleased/pl-json-gon.yml5
-rw-r--r--changelogs/unreleased/pl-uprade-prometheus-alertmanager.yml5
-rw-r--r--changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml5
-rw-r--r--changelogs/unreleased/project-visibility-tooltip.yml5
-rw-r--r--changelogs/unreleased/project_identicon_fix.yml5
-rw-r--r--changelogs/unreleased/rails5-active-record-class-value.yml5
-rw-r--r--changelogs/unreleased/rails5-deprecated-uniq.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48977.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-delete-blob.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-deployment-spec.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-flaky-spec-user-uses-shortcuts.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-revert-modal-spec.yml5
-rw-r--r--changelogs/unreleased/rails5-gpg-permit-concurrent.yml5
-rw-r--r--changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml5
-rw-r--r--changelogs/unreleased/rails5-mysql-milliseconds-deployment-spec.yml5
-rw-r--r--changelogs/unreleased/rails5-mysql-rename-column.yml5
-rw-r--r--changelogs/unreleased/rails5-mysql-schedule-build.yml5
-rw-r--r--changelogs/unreleased/rails5-update-gemfile-lock-2.yml5
-rw-r--r--changelogs/unreleased/rails5-update-gemfile-lock.yml5
-rw-r--r--changelogs/unreleased/rails5-update-rouge.yml5
-rw-r--r--changelogs/unreleased/rails5-user-status-spec.yml5
-rw-r--r--changelogs/unreleased/rake-gitaly-check.yml5
-rw-r--r--changelogs/unreleased/ravlen-deploy-tokens-display-update.yml5
-rw-r--r--changelogs/unreleased/ravlen-rename-secret-variables-in-codebase.yml5
-rw-r--r--changelogs/unreleased/redact-links-dev.yml5
-rw-r--r--changelogs/unreleased/refactor-snippets-finder.yml5
-rw-r--r--changelogs/unreleased/regen-2fa-codes.yml5
-rw-r--r--changelogs/unreleased/related_mrs.yml5
-rw-r--r--changelogs/unreleased/remove-asset-sync.yml5
-rw-r--r--changelogs/unreleased/remove-ci_enable_scheduled_build-feature-flag.yml5
-rw-r--r--changelogs/unreleased/remove-duplicate-primary-button-in-dashboard-snippets.yml5
-rw-r--r--changelogs/unreleased/remove-experimental-label-from-cluster-views.yml5
-rw-r--r--changelogs/unreleased/rename-scheduled-label-badges.yml5
-rw-r--r--changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml5
-rw-r--r--changelogs/unreleased/replace-i-to-icons-in-vue-components.yml5
-rw-r--r--changelogs/unreleased/replace-snake-case-css-classes.yml5
-rw-r--r--changelogs/unreleased/replace-tooltip-in-markdown-component.yml5
-rw-r--r--changelogs/unreleased/rouge-3-2-0.yml5
-rw-r--r--changelogs/unreleased/rs-cherry-pick-api.yml5
-rw-r--r--changelogs/unreleased/rs-revert-api.yml5
-rw-r--r--changelogs/unreleased/runner-features.yml5
-rw-r--r--changelogs/unreleased/runners-max-timeout-param.yml5
-rw-r--r--changelogs/unreleased/rz_fix_milestone_count.yml5
-rw-r--r--changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml5
-rw-r--r--changelogs/unreleased/scheduled-manual-jobs-environment-play-buttons.yml5
-rw-r--r--changelogs/unreleased/security-2717-fix-issue-title-xss.yml5
-rw-r--r--changelogs/unreleased/security-2717-xss-username-autocomplete.yml5
-rw-r--r--changelogs/unreleased/security-51113-hash_personal_access_tokens.yml5
-rw-r--r--changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml5
-rw-r--r--changelogs/unreleased/security-ide-branch-name-xss.yml5
-rw-r--r--changelogs/unreleased/security-kubeclient-ssrf.yml5
-rw-r--r--changelogs/unreleased/sh-53180-append-path.yml5
-rw-r--r--changelogs/unreleased/sh-add-audit-logging-json-ce.yml5
-rw-r--r--changelogs/unreleased/sh-associate-rakefile-ruby.yml5
-rw-r--r--changelogs/unreleased/sh-bump-gems-security.yml5
-rw-r--r--changelogs/unreleased/sh-bump-gitaly-0-117.yml5
-rw-r--r--changelogs/unreleased/sh-bump-haml-5-0-4.yml5
-rw-r--r--changelogs/unreleased/sh-bump-ruby-2-5-3.yml5
-rw-r--r--changelogs/unreleased/sh-bump-rugged-0-27-4.yml5
-rw-r--r--changelogs/unreleased/sh-bump-sanitize-4-6-6.yml5
-rw-r--r--changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml5
-rw-r--r--changelogs/unreleased/sh-fix-admin-jobs-controller-timing-out.yml5
-rw-r--r--changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml5
-rw-r--r--changelogs/unreleased/sh-fix-commit-signatures-error.yml5
-rw-r--r--changelogs/unreleased/sh-fix-hipchat-ssrf.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-47797-ce.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-49133.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-52176.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-52649.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-53153.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-53783-ce.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-54189.yml5
-rw-r--r--changelogs/unreleased/sh-fix-search-relative-urls.yml5
-rw-r--r--changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml5
-rw-r--r--changelogs/unreleased/sh-fix-wiki-security-issue-53072.yml5
-rw-r--r--changelogs/unreleased/sh-freeze-banzai-filter-strings.yml5
-rw-r--r--changelogs/unreleased/sh-handle-colons-in-url-passwords.yml5
-rw-r--r--changelogs/unreleased/sh-handle-invalid-comparison.yml5
-rw-r--r--changelogs/unreleased/sh-include-rbtrace.yml5
-rw-r--r--changelogs/unreleased/sh-lfs-fix-content-type.yml5
-rw-r--r--changelogs/unreleased/sh-limit-unauthenticated-session-times.yml5
-rw-r--r--changelogs/unreleased/sh-normalize-urls.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-merge-request-project-lookup.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-mr-commit-sha-lookup.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-reload-diffs-service.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-wiki-empty-check.yml5
-rw-r--r--changelogs/unreleased/sh-pages-eof-error.yml5
-rw-r--r--changelogs/unreleased/sh-paginate-bitbucket-server-imports.yml5
-rw-r--r--changelogs/unreleased/sh-remove-banzai-instrumentation.yml5
-rw-r--r--changelogs/unreleased/sh-remove-koding.yml5
-rw-r--r--changelogs/unreleased/sh-remove-local-sidekiq-admin-check.yml5
-rw-r--r--changelogs/unreleased/sh-simplify-liveness-check.yml5
-rw-r--r--changelogs/unreleased/sh-strip-github-pat-whitespace.yml5
-rw-r--r--changelogs/unreleased/sh-support-users-find-by-confirmed-emails.yml5
-rw-r--r--changelogs/unreleased/sh-use-nakayoshi-fork.yml5
-rw-r--r--changelogs/unreleased/sh-use-nokogiri-xml-backend.yml5
-rw-r--r--changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml5
-rw-r--r--changelogs/unreleased/stateful_deployments.yml5
-rw-r--r--changelogs/unreleased/stop-dynamic-routable-creation.yml5
-rw-r--r--changelogs/unreleased/support-license-management-and-performance.yml5
-rw-r--r--changelogs/unreleased/switch-rails.yml5
-rw-r--r--changelogs/unreleased/tc-index-lfs-objects-file-store.yml5
-rw-r--r--changelogs/unreleased/tc-index-uploads-file-store.yml5
-rw-r--r--changelogs/unreleased/tc-reorder-mail-notify-references.yml5
-rw-r--r--changelogs/unreleased/test-usage-ping-in-timeout-case.yml5
-rw-r--r--changelogs/unreleased/todos-visibility-change.yml5
-rw-r--r--changelogs/unreleased/todos-visibility-migration.yml5
-rw-r--r--changelogs/unreleased/toggle-password-cluster.yml5
-rw-r--r--changelogs/unreleased/toggle-sidebar-alignment.yml5
-rw-r--r--changelogs/unreleased/top_level_clusters_controller.yml6
-rw-r--r--changelogs/unreleased/triggermesh-phase2-serverless.yml5
-rw-r--r--changelogs/unreleased/tweak-sql-buckets.yml5
-rw-r--r--changelogs/unreleased/tz-mr-port-memory-fixes.yml5
-rw-r--r--changelogs/unreleased/update-card-body-style.yml5
-rw-r--r--changelogs/unreleased/update-issue-closing-pattern.yml5
-rw-r--r--changelogs/unreleased/update-readme-ruby-version.yml5
-rw-r--r--changelogs/unreleased/update-runner-chart-to-0-1-34.yml5
-rw-r--r--changelogs/unreleased/update-runner-chart-to-0-1-35.yml5
-rw-r--r--changelogs/unreleased/update-specific-runners-help-url.yml5
-rw-r--r--changelogs/unreleased/update_license_management_job.yml5
-rw-r--r--changelogs/unreleased/upgrade-hamlit-for-ruby25.yml5
-rw-r--r--changelogs/unreleased/upgrade-workhorse-7-1-0.yml5
-rw-r--r--changelogs/unreleased/use-raw-file-format.yml5
-rw-r--r--changelogs/unreleased/validate-foreign-keys-being-indexed.yml5
-rw-r--r--changelogs/unreleased/visual-improvements-language-bar.yml5
-rw-r--r--changelogs/unreleased/winh-delayed-jobs-dynamic-timer.yml5
-rw-r--r--changelogs/unreleased/winh-fix-gpg-regressions.yml5
-rw-r--r--changelogs/unreleased/winh-job-list-dynamic-timer.yml5
-rw-r--r--changelogs/unreleased/winh-pipeline-actions-dynamic-timer.yml5
-rw-r--r--changelogs/unreleased/winh-restyle-user-status.yml5
-rw-r--r--changelogs/unreleased/winh-stop-all-environments.yml5
-rw-r--r--changelogs/unreleased/winh-tree-view-gpg.yml5
-rw-r--r--changelogs/unreleased/winh-upgrade-grape-path-helpers.yml5
-rw-r--r--changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml5
-rw-r--r--changelogs/unreleased/zj-bump-gitaly-0-128.yml5
-rw-r--r--changelogs/unreleased/zj-circuit-breaker-removal.yml5
-rw-r--r--changelogs/unreleased/zj-remove-broken-storage.yml5
-rw-r--r--changelogs/unreleased/zj-remove-git-rake-tasks.yml5
-rw-r--r--changelogs/unreleased/zj-remove-linguist.yml5
-rw-r--r--changelogs/unreleased/zj-repository-languages.yml5
-rw-r--r--config/application.rb20
-rw-r--r--config/boot.rb4
-rw-r--r--config/dependency_decisions.yml50
-rw-r--r--config/environment.rb6
-rw-r--r--config/environments/development.rb2
-rw-r--r--config/environments/production.rb2
-rw-r--r--config/environments/test.rb4
-rw-r--r--config/gitlab.yml.example23
-rw-r--r--config/initializers/0_as_concern.rb22
-rw-r--r--config/initializers/0_post_deployment_migrations.rb12
-rw-r--r--config/initializers/1_settings.rb7
-rw-r--r--config/initializers/7_prometheus_metrics.rb22
-rw-r--r--config/initializers/8_metrics.rb22
-rw-r--r--config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb2
-rw-r--r--config/initializers/active_record_lifecycle.rb23
-rw-r--r--config/initializers/active_record_verbose_query_logs.rb4
-rw-r--r--config/initializers/asset_sync.rb31
-rw-r--r--config/initializers/carrierwave_patch.rb29
-rw-r--r--config/initializers/devise.rb2
-rw-r--r--config/initializers/fill_shards.rb3
-rw-r--r--config/initializers/fog_google_https_private_urls.rb4
-rw-r--r--config/initializers/gettext_rails_i18n_patch.rb15
-rw-r--r--config/initializers/gollum.rb28
-rw-r--r--config/initializers/hipchat_client_patch.rb14
-rw-r--r--config/initializers/kubeclient.rb21
-rw-r--r--config/initializers/lograge.rb3
-rw-r--r--config/initializers/macos.rb13
-rw-r--r--config/initializers/mysql_set_length_for_binary_indexes.rb50
-rw-r--r--config/initializers/peek.rb1
-rw-r--r--config/initializers/postgresql_opclasses_support.rb54
-rw-r--r--config/initializers/rbtrace.rb9
-rw-r--r--config/initializers/routing_draw.rb8
-rw-r--r--config/initializers/sidekiq.rb20
-rw-r--r--config/initializers/static_files.rb23
-rw-r--r--config/initializers/warden.rb5
-rw-r--r--config/karma.config.js22
-rw-r--r--config/locales/en.yml2
-rw-r--r--config/prometheus/additional_metrics.yml177
-rw-r--r--config/prometheus/common_metrics.yml215
-rw-r--r--config/puma.example.development.rb77
-rw-r--r--config/routes.rb36
-rw-r--r--config/routes/admin.rb11
-rw-r--r--config/routes/group.rb15
-rw-r--r--config/routes/instance_statistics.rb2
-rw-r--r--config/routes/project.rb26
-rw-r--r--config/routes/user.rb1
-rw-r--r--config/routes/wiki.rb2
-rw-r--r--config/sidekiq_queues.yml3
-rw-r--r--config/unicorn.rb.example37
-rw-r--r--config/unicorn.rb.example.development67
-rw-r--r--config/webpack.config.js2
-rw-r--r--danger/changelog/Dangerfile6
-rw-r--r--danger/commit_messages/Dangerfile236
-rw-r--r--danger/database/Dangerfile4
-rw-r--r--danger/documentation/Dangerfile49
-rw-r--r--danger/eslint/Dangerfile29
-rw-r--r--danger/metadata/Dangerfile9
-rw-r--r--danger/plugins/helper.rb34
-rw-r--r--danger/prettier/Dangerfile39
-rw-r--r--danger/specs/Dangerfile6
-rw-r--r--db/fixtures/development/04_project.rb10
-rw-r--r--db/fixtures/development/09_issues.rb3
-rw-r--r--db/fixtures/development/14_pipelines.rb75
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb24
-rw-r--r--db/fixtures/development/99_common_metrics.rb5
-rw-r--r--db/fixtures/production/999_common_metrics.rb5
-rw-r--r--db/importers/common_metrics_importer.rb103
-rw-r--r--db/migrate/20160712171823_remove_award_emojis_with_no_user.rb2
-rw-r--r--db/migrate/20170222111732_create_gpg_keys.rb6
-rw-r--r--db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb2
-rw-r--r--db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb8
-rw-r--r--db/migrate/20170613154149_create_gpg_signatures.rb6
-rw-r--r--db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb5
-rw-r--r--db/migrate/20170827123848_add_index_on_merge_request_diff_commit_sha.rb2
-rw-r--r--db/migrate/20170927161718_create_gpg_key_subkeys.rb6
-rw-r--r--db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb26
-rw-r--r--db/migrate/20180101160629_create_prometheus_metrics.rb18
-rw-r--r--db/migrate/20180101160630_change_project_id_for_prometheus_metrics.rb11
-rw-r--r--db/migrate/20180113220114_rework_redirect_routes_indexes.rb66
-rw-r--r--db/migrate/20180228172924_add_include_private_contributions_to_users.rb7
-rw-r--r--db/migrate/20180308125206_add_user_internal_regex_to_application_setting.rb13
-rw-r--r--db/migrate/20180403035759_create_project_ci_cd_settings.rb16
-rw-r--r--db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb4
-rw-r--r--db/migrate/20180420010616_cleanup_build_stage_migration.rb76
-rw-r--r--db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb16
-rw-r--r--db/migrate/20180504195842_project_name_lower_index.rb18
-rw-r--r--db/migrate/20180607071808_add_push_events_branch_filter_to_web_hooks.rb12
-rw-r--r--db/migrate/20180702124358_remove_orphaned_routes.rb20
-rw-r--r--db/migrate/20180711103851_drop_duplicate_protected_tags.rb45
-rw-r--r--db/migrate/20180711103922_add_protected_tags_index.rb18
-rw-r--r--db/migrate/20180720023512_add_receive_max_input_size_to_application_settings.rb11
-rw-r--r--db/migrate/20180808162000_add_user_show_add_ssh_key_message_to_application_settings.rb19
-rw-r--r--db/migrate/20180813101999_change_default_of_auto_devops_instance_wide.rb15
-rw-r--r--db/migrate/20180813102000_enable_auto_devops_instance_wide_for_everyone.rb15
-rw-r--r--db/migrate/20180814153625_add_commit_email_to_users.rb33
-rw-r--r--db/migrate/20180815040323_add_authorization_type_to_cluster_platforms_kubernetes.rb11
-rw-r--r--db/migrate/20180815160409_add_file_location_to_ci_job_artifacts.rb9
-rw-r--r--db/migrate/20180815170510_add_partial_index_to_ci_builds_artifacts_file.rb16
-rw-r--r--db/migrate/20180815175440_add_index_on_list_type.rb16
-rw-r--r--db/migrate/20180831164904_fix_prometheus_metric_query_limits.rb19
-rw-r--r--db/migrate/20180831164905_add_common_to_prometheus_metrics.rb17
-rw-r--r--db/migrate/20180831164907_add_index_on_common_for_prometheus_metrics.rb17
-rw-r--r--db/migrate/20180831164908_add_identifier_to_prometheus_metric.rb11
-rw-r--r--db/migrate/20180831164909_add_index_for_identifier_to_prometheus_metric.rb17
-rw-r--r--db/migrate/20180831164910_import_common_metrics.rb17
-rw-r--r--db/migrate/20180901171833_add_project_config_source_status_index_to_pipeline.rb17
-rw-r--r--db/migrate/20180901200537_add_resource_label_event_reference_fields.rb11
-rw-r--r--db/migrate/20180906101639_add_user_ping_consent_to_application_settings.rb19
-rw-r--r--db/migrate/20180907015926_add_legacy_abac_to_cluster_providers_gcp.rb17
-rw-r--r--db/migrate/20180910115836_add_attr_encrypted_columns_to_web_hook.rb15
-rw-r--r--db/migrate/20180910153412_add_token_digest_to_personal_access_tokens.rb19
-rw-r--r--db/migrate/20180910153413_add_index_to_token_digest_on_personal_access_tokens.rb17
-rw-r--r--db/migrate/20180912111628_add_knative_application.rb20
-rw-r--r--db/migrate/20180916011959_add_index_pipelines_project_id_source.rb20
-rw-r--r--db/migrate/20180924141949_add_diff_max_patch_bytes_to_application_settings.rb25
-rw-r--r--db/migrate/20180924190739_add_scheduled_at_to_ci_builds.rb9
-rw-r--r--db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb18
-rw-r--r--db/migrate/20180925200829_create_user_preferences.rb31
-rw-r--r--db/migrate/20180927073410_add_index_to_project_deploy_tokens_deploy_token_id.rb18
-rw-r--r--db/migrate/20181002172433_remove_restricted_todos_with_cte.rb32
-rw-r--r--db/migrate/20181005110927_add_index_to_lfs_objects_file_store.rb17
-rw-r--r--db/migrate/20181005125926_add_index_to_uploads_store.rb17
-rw-r--r--db/migrate/20181009190428_create_clusters_kubernetes_namespaces.rb24
-rw-r--r--db/migrate/20181010235606_create_board_project_recent_visits.rb19
-rw-r--r--db/migrate/20181014203236_create_cluster_groups.rb17
-rw-r--r--db/migrate/20181015155839_add_finished_at_to_deployments.rb15
-rw-r--r--db/migrate/20181016141739_add_status_to_deployments.rb29
-rw-r--r--db/migrate/20181016152238_create_board_group_recent_visits.rb20
-rw-r--r--db/migrate/20181017001059_add_cluster_type_to_clusters.rb18
-rw-r--r--db/migrate/20181019032400_add_shards_table.rb11
-rw-r--r--db/migrate/20181019032408_add_repositories_table.rb15
-rw-r--r--db/migrate/20181019105553_add_projects_pool_repository_id_foreign_key.rb22
-rw-r--r--db/migrate/20181022135539_add_index_on_status_to_deployments.rb19
-rw-r--r--db/migrate/20181023104858_add_archive_builds_duration_to_application_settings.rb11
-rw-r--r--db/migrate/20181023144439_add_partial_index_for_legacy_successful_deployments.rb18
-rw-r--r--db/migrate/20181025115728_add_private_commit_email_hostname_to_application_settings.rb11
-rw-r--r--db/migrate/20181026143227_migrate_snippets_access_level_default_value.rb42
-rw-r--r--db/migrate/20181030154446_add_missing_indexes_for_foreign_keys.rb65
-rw-r--r--db/migrate/20181031190558_drop_fk_gcp_clusters_table.rb37
-rw-r--r--db/migrate/20181031190559_drop_gcp_clusters_table.rb53
-rw-r--r--db/migrate/20181101144347_add_index_for_stuck_mr_query.rb16
-rw-r--r--db/migrate/20181106135939_add_index_to_deployments.rb17
-rw-r--r--db/migrate/20181112103239_drop_default_value_on_status_deployments.rb14
-rw-r--r--db/migrate/prometheus_metrics_limits_to_mysql.rb12
-rw-r--r--db/optional_migrations/composite_primary_keys.rb14
-rw-r--r--db/post_migrate/20161221153951_rename_reserved_project_names.rb8
-rw-r--r--db/post_migrate/20170313133418_rename_more_reserved_project_names.rb8
-rw-r--r--db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb6
-rw-r--r--db/post_migrate/20170503004427_update_retried_for_ci_build.rb6
-rw-r--r--db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb18
-rw-r--r--db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb16
-rw-r--r--db/post_migrate/20170526185842_migrate_pipeline_stages.rb24
-rw-r--r--db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb14
-rw-r--r--db/post_migrate/20170711145558_migrate_stages_statuses.rb8
-rw-r--r--db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb2
-rw-r--r--db/post_migrate/20171207150343_remove_soft_removed_objects.rb12
-rw-r--r--db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb40
-rw-r--r--db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb16
-rw-r--r--db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb8
-rw-r--r--db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb14
-rw-r--r--db/post_migrate/20180420080616_schedule_stages_index_migration.rb14
-rw-r--r--db/post_migrate/20180604123514_cleanup_stages_position_migration.rb36
-rw-r--r--db/post_migrate/20180723130817_delete_inconsistent_internal_id_records.rb47
-rw-r--r--db/post_migrate/20180809195358_migrate_null_wiki_access_levels.rb32
-rw-r--r--db/post_migrate/20180816161409_migrate_legacy_artifacts_to_job_artifacts.rb32
-rw-r--r--db/post_migrate/20180816193530_rename_login_root_namespaces.rb22
-rw-r--r--db/post_migrate/20180826111825_recalculate_site_statistics.rb27
-rw-r--r--db/post_migrate/20180906051323_remove_orphaned_label_links.rb43
-rw-r--r--db/post_migrate/20180913051323_consume_remaining_diff_files_deletion_jobs.rb23
-rw-r--r--db/post_migrate/20180913142237_schedule_digest_personal_access_tokens.rb28
-rw-r--r--db/post_migrate/20180914162043_encrypt_web_hooks_columns.rb33
-rw-r--r--db/post_migrate/20180914201132_remove_sidekiq_throttling_from_application_settings.rb17
-rw-r--r--db/post_migrate/20180916014356_populate_external_pipeline_source.rb33
-rw-r--r--db/post_migrate/20180917172041_remove_wikis_count_from_site_statistics.rb6
-rw-r--r--db/post_migrate/20181008145341_steal_encrypt_columns.rb15
-rw-r--r--db/post_migrate/20181008145359_remove_web_hooks_token_and_url.rb10
-rw-r--r--db/post_migrate/20181008200441_remove_circuit_breaker.rb30
-rw-r--r--db/post_migrate/20181013005024_remove_koding_from_application_settings.rb17
-rw-r--r--db/post_migrate/20181014121030_enqueue_redact_links.rb65
-rw-r--r--db/post_migrate/20181022173835_enqueue_populate_cluster_kubernetes_namespace.rb18
-rw-r--r--db/post_migrate/20181030135124_fill_empty_finished_at_in_deployments.rb27
-rw-r--r--db/post_migrate/20181105201455_steal_fill_store_upload.rb31
-rw-r--r--db/post_migrate/20181107054254_remove_restricted_todos_again.rb32
-rw-r--r--db/schema.rb1077
-rw-r--r--doc/README.md6
-rw-r--r--doc/administration/auth/authentiq.md36
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.pngbin27877 -> 11991 bytes
-rw-r--r--doc/administration/auth/img/crowd_application.pngbin55811 -> 17172 bytes
-rw-r--r--doc/administration/auth/img/crowd_application_authorisation.pngbin126994 -> 37026 bytes
-rw-r--r--doc/administration/auth/ldap.md39
-rw-r--r--doc/administration/auth/okta.md2
-rw-r--r--doc/administration/compliance.md18
-rw-r--r--doc/administration/container_registry.md29
-rw-r--r--doc/administration/custom_hooks.md8
-rw-r--r--doc/administration/external_database.md2
-rw-r--r--doc/administration/git_protocol.md103
-rw-r--r--doc/administration/gitaly/index.md19
-rw-r--r--doc/administration/high_availability/README.md2
-rw-r--r--doc/administration/high_availability/database.md2
-rw-r--r--doc/administration/high_availability/gitlab.md12
-rw-r--r--doc/administration/high_availability/nfs.md61
-rw-r--r--doc/administration/high_availability/redis.md81
-rw-r--r--doc/administration/high_availability/redis_source.md8
-rw-r--r--doc/administration/img/circuitbreaker_config.pngbin99523 -> 0 bytes
-rw-r--r--doc/administration/img/custom_hooks_error_msg.pngbin44922 -> 44892 bytes
-rw-r--r--doc/administration/img/failing_storage.pngbin48281 -> 0 bytes
-rw-r--r--doc/administration/img/integration/plantuml-example.pngbin33034 -> 12559 bytes
-rw-r--r--doc/administration/img/repository_storages_admin_ui.pngbin70416 -> 20439 bytes
-rw-r--r--doc/administration/index.md16
-rw-r--r--doc/administration/integration/koding.md247
-rw-r--r--doc/administration/integration/plantuml.md15
-rw-r--r--doc/administration/integration/terminal.md3
-rw-r--r--doc/administration/issue_closing_pattern.md4
-rw-r--r--doc/administration/job_artifacts.md65
-rw-r--r--doc/administration/job_traces.md73
-rw-r--r--doc/administration/logs.md54
-rw-r--r--doc/administration/monitoring/performance/img/grafana_dashboard_import.pngbin11836 -> 11835 bytes
-rw-r--r--doc/administration/monitoring/performance/img/grafana_data_source_configuration.pngbin14700 -> 14695 bytes
-rw-r--r--doc/administration/monitoring/performance/img/grafana_data_source_empty.pngbin11963 -> 11960 bytes
-rw-r--r--doc/administration/monitoring/performance/img/grafana_save_icon.pngbin4619 -> 4598 bytes
-rw-r--r--doc/administration/monitoring/performance/img/performance_bar.pngbin344274 -> 99331 bytes
-rw-r--r--doc/administration/monitoring/performance/img/performance_bar_configuration_settings.pngbin20385 -> 16455 bytes
-rw-r--r--doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.pngbin278693 -> 91275 bytes
-rw-r--r--doc/administration/monitoring/performance/img/performance_bar_line_profiling.pngbin161313 -> 93063 bytes
-rw-r--r--doc/administration/monitoring/performance/img/performance_bar_sql_queries.pngbin165124 -> 128337 bytes
-rw-r--r--doc/administration/monitoring/performance/img/request_profile_result.pngbin3236 -> 3216 bytes
-rw-r--r--doc/administration/monitoring/performance/img/request_profiling_token.pngbin10229 -> 10217 bytes
-rw-r--r--doc/administration/monitoring/performance/influxdb_configuration.md6
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md4
-rw-r--r--doc/administration/monitoring/prometheus/index.md147
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md16
-rw-r--r--doc/administration/operations/filesystem_benchmarking.md55
-rw-r--r--doc/administration/operations/img/sidekiq_job_throttling.pngbin32229 -> 0 bytes
-rw-r--r--doc/administration/operations/img/write_to_authorized_keys_setting.pngbin94218 -> 29192 bytes
-rw-r--r--doc/administration/operations/index.md6
-rw-r--r--doc/administration/operations/moving_repositories.md26
-rw-r--r--doc/administration/operations/sidekiq_job_throttling.md33
-rw-r--r--doc/administration/operations/ssh_certificates.md21
-rw-r--r--doc/administration/pages/index.md123
-rw-r--r--doc/administration/pages/source.md186
-rw-r--r--doc/administration/raketasks/github_import.md32
-rw-r--r--doc/administration/raketasks/maintenance.md11
-rw-r--r--doc/administration/raketasks/project_import_export.md7
-rw-r--r--doc/administration/raketasks/uploads/migrate.md37
-rw-r--r--doc/administration/reply_by_email.md2
-rw-r--r--doc/administration/reply_by_email_postfix_setup.md4
-rw-r--r--doc/administration/repository_checks.md5
-rw-r--r--doc/administration/repository_storage_paths.md97
-rw-r--r--doc/administration/repository_storage_types.md5
-rw-r--r--doc/administration/troubleshooting/debug.md6
-rw-r--r--doc/administration/troubleshooting/sidekiq.md16
-rw-r--r--doc/administration/uploads.md113
-rw-r--r--doc/api/README.md50
-rw-r--r--doc/api/applications.md51
-rw-r--r--doc/api/avatar.md2
-rw-r--r--doc/api/boards.md2
-rw-r--r--doc/api/branches.md5
-rw-r--r--doc/api/commits.md57
-rw-r--r--doc/api/deployments.md45
-rw-r--r--doc/api/discussions.md16
-rw-r--r--doc/api/events.md20
-rw-r--r--doc/api/group_boards.md2
-rw-r--r--doc/api/group_milestones.md13
-rw-r--r--doc/api/groups.md15
-rw-r--r--doc/api/issues.md107
-rw-r--r--doc/api/jobs.md161
-rw-r--r--doc/api/keys.md8
-rw-r--r--doc/api/members.md2
-rw-r--r--doc/api/merge_requests.md767
-rw-r--r--doc/api/milestones.md2
-rw-r--r--doc/api/notes.md4
-rw-r--r--doc/api/notification_settings.md2
-rw-r--r--doc/api/oauth2.md8
-rw-r--r--doc/api/pipelines.md17
-rw-r--r--doc/api/project_templates.md139
-rw-r--r--doc/api/projects.md64
-rw-r--r--doc/api/protected_branches.md2
-rw-r--r--doc/api/protected_tags.md128
-rw-r--r--doc/api/repositories.md5
-rw-r--r--doc/api/repository_files.md21
-rw-r--r--doc/api/repository_storage_health.md75
-rw-r--r--doc/api/repository_submodules.md49
-rw-r--r--doc/api/resource_label_events.md175
-rw-r--r--doc/api/runners.md26
-rw-r--r--doc/api/services.md84
-rw-r--r--doc/api/settings.md238
-rw-r--r--doc/api/system_hooks.md5
-rw-r--r--doc/api/tags.md24
-rw-r--r--doc/api/templates/dockerfiles.md113
-rw-r--r--doc/api/templates/gitignores.md520
-rw-r--r--doc/api/templates/gitlab_ci_ymls.md75
-rw-r--r--doc/api/users.md59
-rw-r--r--doc/api/v3_to_v4.md4
-rw-r--r--doc/api/wikis.md44
-rw-r--r--doc/ci/README.md4
-rw-r--r--doc/ci/autodeploy/img/auto_deploy_btn.pngbin46825 -> 16779 bytes
-rw-r--r--doc/ci/autodeploy/img/auto_deploy_button.pngbin43441 -> 13321 bytes
-rw-r--r--doc/ci/autodeploy/img/auto_deploy_dropdown.pngbin75456 -> 28357 bytes
-rw-r--r--doc/ci/autodeploy/img/auto_monitoring.pngbin89206 -> 56765 bytes
-rw-r--r--doc/ci/autodeploy/img/guide_connect_cluster.pngbin38724 -> 15225 bytes
-rw-r--r--doc/ci/autodeploy/img/guide_integration.pngbin44263 -> 15042 bytes
-rw-r--r--doc/ci/autodeploy/img/guide_secret.pngbin16233 -> 4803 bytes
-rw-r--r--doc/ci/autodeploy/quick_start_guide.md10
-rw-r--r--doc/ci/caching/img/clear_runners_cache.pngbin16029 -> 16020 bytes
-rw-r--r--doc/ci/caching/index.md172
-rw-r--r--doc/ci/docker/README.md1
-rw-r--r--doc/ci/docker/using_docker_build.md137
-rw-r--r--doc/ci/docker/using_docker_images.md95
-rw-r--r--doc/ci/docker/using_kaniko.md59
-rw-r--r--doc/ci/environments.md89
-rw-r--r--doc/ci/examples/README.md10
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md4
-rw-r--r--doc/ci/examples/browser_performance.md75
-rw-r--r--doc/ci/examples/code_quality.md82
-rw-r--r--doc/ci/examples/container_scanning.md85
-rw-r--r--doc/ci/examples/dast.md67
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.pngbin49735 -> 0 bytes
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_variables.pngbin0 -> 28170 bytes
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md4
-rw-r--r--doc/ci/examples/deployment/README.md10
-rw-r--r--doc/ci/examples/deployment/composer-npm-deploy.md26
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.pngbin22046 -> 9300 bytes
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.pngbin27620 -> 15160 bytes
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.pngbin18789 -> 9985 bytes
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md4
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_checkbox.pngbin4730 -> 0 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.pngbin56091 -> 23936 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.pngbin339666 -> 105954 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.pngbin185393 -> 79265 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.pngbin134742 -> 56126 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.pngbin172664 -> 70582 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.pngbin119955 -> 50117 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.pngbin141393 -> 59140 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/secret_variables_page.pngbin233764 -> 0 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/img/variables_page.pngbin0 -> 27538 bytes
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md32
-rw-r--r--doc/ci/examples/php.md4
-rw-r--r--doc/ci/examples/test-and-deploy-python-application-to-heroku.md12
-rw-r--r--doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md11
-rw-r--r--doc/ci/examples/test-scala-application.md4
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.pngbin62101 -> 23148 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.pngbin16112 -> 6602 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.pngbin73726 -> 25235 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.pngbin40381 -> 15794 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.pngbin29968 -> 12345 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.pngbin46405 -> 19183 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md10
-rw-r--r--doc/ci/git_submodules.md19
-rw-r--r--doc/ci/img/deployments_view.pngbin61088 -> 23352 bytes
-rw-r--r--doc/ci/img/environments_available.pngbin21089 -> 8464 bytes
-rw-r--r--doc/ci/img/environments_dynamic_groups.pngbin58239 -> 21902 bytes
-rw-r--r--doc/ci/img/environments_link_url_mr.pngbin34361 -> 14346 bytes
-rw-r--r--doc/ci/img/environments_manual_action_deployments.pngbin32748 -> 12635 bytes
-rw-r--r--doc/ci/img/environments_manual_action_environments.pngbin24191 -> 9485 bytes
-rw-r--r--doc/ci/img/environments_manual_action_jobs.pngbin19919 -> 8446 bytes
-rw-r--r--doc/ci/img/environments_manual_action_pipelines.pngbin38974 -> 14979 bytes
-rw-r--r--doc/ci/img/environments_manual_action_single_pipeline.pngbin23381 -> 10273 bytes
-rw-r--r--doc/ci/img/environments_monitoring.pngbin76086 -> 45153 bytes
-rw-r--r--doc/ci/img/environments_mr_review_app.pngbin30991 -> 13394 bytes
-rw-r--r--doc/ci/img/environments_terminal_button_on_index.pngbin29162 -> 11080 bytes
-rw-r--r--doc/ci/img/environments_terminal_button_on_show.pngbin17811 -> 7642 bytes
-rw-r--r--doc/ci/img/environments_terminal_page.pngbin117863 -> 28166 bytes
-rw-r--r--doc/ci/img/job_failure_reason.pngbin5346 -> 5288 bytes
-rw-r--r--doc/ci/img/junit_test_report.pngbin0 -> 9572 bytes
-rw-r--r--doc/ci/img/pipeline_incremental_rollout.pngbin0 -> 4794 bytes
-rw-r--r--doc/ci/img/pipelines_grouped.pngbin12937 -> 12888 bytes
-rw-r--r--doc/ci/img/pipelines_index.pngbin36299 -> 14896 bytes
-rw-r--r--doc/ci/img/pipelines_mini_graph.pngbin15404 -> 4671 bytes
-rw-r--r--doc/ci/img/pipelines_mini_graph_simple.pngbin1637 -> 961 bytes
-rw-r--r--doc/ci/img/view_on_env_blob.pngbin111663 -> 11889 bytes
-rw-r--r--doc/ci/img/view_on_env_mr.pngbin1005195 -> 295181 bytes
-rw-r--r--doc/ci/img/view_on_mr_widget.pngbin0 -> 21969 bytes
-rw-r--r--doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.pngbin0 -> 35571 bytes
-rw-r--r--doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.pngbin0 -> 23364 bytes
-rw-r--r--doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.pngbin0 -> 55677 bytes
-rw-r--r--doc/ci/interactive_web_terminal/index.md54
-rw-r--r--doc/ci/junit_test_reports.md170
-rw-r--r--doc/ci/pipelines.md62
-rw-r--r--doc/ci/quick_start/README.md4
-rw-r--r--doc/ci/quick_start/img/build_log.pngbin35261 -> 35256 bytes
-rw-r--r--doc/ci/quick_start/img/builds_status.pngbin19127 -> 19107 bytes
-rw-r--r--doc/ci/quick_start/img/new_commit.pngbin5584 -> 5541 bytes
-rw-r--r--doc/ci/review_apps/img/continuous-delivery-review-apps.svg48
-rw-r--r--doc/ci/review_apps/img/review_apps_preview_in_mr.pngbin11723 -> 29800 bytes
-rw-r--r--doc/ci/review_apps/index.md164
-rw-r--r--doc/ci/runners/README.md19
-rw-r--r--doc/ci/runners/img/protected_runners_check_box.pngbin8584 -> 3963 bytes
-rw-r--r--doc/ci/runners/img/shared_runner_ip_address.pngbin69821 -> 20276 bytes
-rw-r--r--doc/ci/runners/img/specific_runner_ip_address.pngbin42055 -> 12209 bytes
-rw-r--r--doc/ci/services/mysql.md2
-rw-r--r--doc/ci/ssh_keys/README.md4
-rw-r--r--doc/ci/triggers/README.md34
-rw-r--r--doc/ci/triggers/img/builds_page.pngbin20383 -> 20366 bytes
-rw-r--r--doc/ci/triggers/img/trigger_single_build.pngbin6585 -> 6584 bytes
-rw-r--r--doc/ci/variables/README.md32
-rw-r--r--doc/ci/variables/img/secret_variables.pngbin32886 -> 0 bytes
-rw-r--r--doc/ci/variables/img/variables.pngbin0 -> 116263 bytes
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md39
-rw-r--r--doc/ci/yaml/README.md694
-rw-r--r--doc/customization/branded_login_page/custom_sign_in.pngbin79288 -> 79276 bytes
-rw-r--r--doc/customization/branded_page_and_email_header/appearance.pngbin10253 -> 5625 bytes
-rw-r--r--doc/customization/branded_page_and_email_header/custom_brand_header.pngbin10014 -> 5279 bytes
-rw-r--r--doc/customization/branded_page_and_email_header/custom_email_header.pngbin37472 -> 14671 bytes
-rw-r--r--doc/customization/favicon/appearance.pngbin52245 -> 28933 bytes
-rw-r--r--doc/customization/favicon/custom_favicon.pngbin60083 -> 31600 bytes
-rw-r--r--doc/customization/new_project_page/appearance_settings.pngbin71178 -> 30473 bytes
-rw-r--r--doc/customization/new_project_page/custom_new_project_page.pngbin164962 -> 69523 bytes
-rw-r--r--doc/customization/new_project_page/default_new_project_page.pngbin146906 -> 62717 bytes
-rw-r--r--doc/development/README.md27
-rw-r--r--doc/development/adding_database_indexes.md6
-rw-r--r--doc/development/api_graphql_styleguide.md2
-rw-r--r--doc/development/architecture.md191
-rw-r--r--doc/development/automatic_ce_ee_merge.md38
-rw-r--r--doc/development/background_migrations.md40
-rw-r--r--doc/development/build_test_package.md13
-rw-r--r--doc/development/changelog.md21
-rw-r--r--doc/development/chaos_endpoints.md117
-rw-r--r--doc/development/code_review.md215
-rw-r--r--doc/development/contributing/community_roles.md16
-rw-r--r--doc/development/contributing/design.md54
-rw-r--r--doc/development/contributing/index.md199
-rw-r--r--doc/development/contributing/issue_workflow.md186
-rw-r--r--doc/development/contributing/merge_request_workflow.md81
-rw-r--r--doc/development/contributing/style_guides.md40
-rw-r--r--doc/development/database_debugging.md2
-rw-r--r--doc/development/database_helpers.md63
-rw-r--r--doc/development/database_merge_request_checklist.md10
-rw-r--r--doc/development/diffs.md68
-rw-r--r--doc/development/documentation/img/manual_build_docs.pngbin14867 -> 14855 bytes
-rw-r--r--doc/development/documentation/index.md508
-rw-r--r--doc/development/documentation/structure.md190
-rw-r--r--doc/development/documentation/styleguide.md359
-rw-r--r--doc/development/documentation/workflow.md185
-rw-r--r--doc/development/ee_features.md284
-rw-r--r--doc/development/fe_guide/components.md2
-rw-r--r--doc/development/fe_guide/icons.md4
-rw-r--r--doc/development/fe_guide/img/boards_diagram.pngbin30538 -> 9518 bytes
-rw-r--r--doc/development/fe_guide/img/gl-modal.pngbin25893 -> 8767 bytes
-rw-r--r--doc/development/fe_guide/performance.md3
-rw-r--r--doc/development/fe_guide/style_guide_js.md647
-rw-r--r--doc/development/fe_guide/style_guide_scss.md6
-rw-r--r--doc/development/fe_guide/vue.md8
-rw-r--r--doc/development/fe_guide/vuex.md35
-rw-r--r--doc/development/feature_flags.md95
-rw-r--r--doc/development/file_storage.md7
-rw-r--r--doc/development/gitaly.md59
-rw-r--r--doc/development/github_importer.md18
-rw-r--r--doc/development/gitlab_architecture_diagram.pngbin61667 -> 34253 bytes
-rw-r--r--doc/development/gotchas.md6
-rw-r--r--doc/development/i18n/index.md2
-rw-r--r--doc/development/i18n/merging_translations.md60
-rw-r--r--doc/development/i18n/proofreader.md8
-rw-r--r--doc/development/img/trigger_ss1.pngbin106261 -> 35756 bytes
-rw-r--r--doc/development/img/trigger_ss2.pngbin106671 -> 36082 bytes
-rw-r--r--doc/development/instrumentation.md12
-rw-r--r--doc/development/licensing.md2
-rw-r--r--doc/development/logging.md144
-rw-r--r--doc/development/merge_request_performance_guidelines.md5
-rw-r--r--doc/development/migration_style_guide.md7
-rw-r--r--doc/development/module_with_instance_variables.md2
-rw-r--r--doc/development/new_fe_guide/development/performance.md2
-rw-r--r--doc/development/new_fe_guide/development/testing.md295
-rw-r--r--doc/development/new_fe_guide/index.md4
-rw-r--r--doc/development/new_fe_guide/modules/dirty_submit.md23
-rw-r--r--doc/development/new_fe_guide/modules/index.md5
-rw-r--r--doc/development/new_fe_guide/style/javascript.md213
-rw-r--r--doc/development/newlines_styleguide.md4
-rw-r--r--doc/development/ordering_table_columns.md73
-rw-r--r--doc/development/performance.md33
-rw-r--r--doc/development/permissions.md63
-rw-r--r--doc/development/post_deployment_migrations.md6
-rw-r--r--doc/development/prometheus_metrics.md48
-rw-r--r--doc/development/query_count_limits.md4
-rw-r--r--doc/development/rake_tasks.md2
-rw-r--r--doc/development/reusing_abstractions.md182
-rw-r--r--doc/development/rolling_out_changes_using_feature_flags.md190
-rw-r--r--doc/development/sidekiq_debugging.md3
-rw-r--r--doc/development/swapping_tables.md4
-rw-r--r--doc/development/switching_to_rails5.md27
-rw-r--r--doc/development/testing_guide/best_practices.md155
-rw-r--r--doc/development/testing_guide/ci.md30
-rw-r--r--doc/development/testing_guide/end_to_end_tests.md69
-rw-r--r--doc/development/testing_guide/index.md6
-rw-r--r--doc/development/testing_guide/review_apps.md91
-rw-r--r--doc/development/testing_guide/smoke.md19
-rw-r--r--doc/development/testing_guide/testing_levels.md93
-rw-r--r--doc/development/ui_guide.md10
-rw-r--r--doc/development/understanding_explain_plans.md676
-rw-r--r--doc/development/utilities.md4
-rw-r--r--doc/development/ux_guide/img/button-close--active.pngbin1385 -> 1120 bytes
-rw-r--r--doc/development/ux_guide/img/button-close--hover.pngbin1015 -> 765 bytes
-rw-r--r--doc/development/ux_guide/img/button-close--resting.pngbin1271 -> 915 bytes
-rw-r--r--doc/development/ux_guide/img/button-danger--active.pngbin1450 -> 1117 bytes
-rw-r--r--doc/development/ux_guide/img/button-danger--hover.pngbin1095 -> 797 bytes
-rw-r--r--doc/development/ux_guide/img/button-danger--resting.pngbin1376 -> 932 bytes
-rw-r--r--doc/development/ux_guide/img/button-info--active.pngbin1442 -> 1109 bytes
-rw-r--r--doc/development/ux_guide/img/button-info--hover.pngbin1079 -> 795 bytes
-rw-r--r--doc/development/ux_guide/img/button-info--resting.pngbin1296 -> 930 bytes
-rw-r--r--doc/development/ux_guide/img/button-spam--active.pngbin1435 -> 1107 bytes
-rw-r--r--doc/development/ux_guide/img/button-spam--hover.pngbin1108 -> 777 bytes
-rw-r--r--doc/development/ux_guide/img/button-spam--resting.pngbin1377 -> 941 bytes
-rw-r--r--doc/development/ux_guide/img/button-success--active.pngbin1510 -> 1153 bytes
-rw-r--r--doc/development/ux_guide/img/button-success--hover.pngbin1151 -> 892 bytes
-rw-r--r--doc/development/ux_guide/img/button-success--resting.pngbin1447 -> 1045 bytes
-rw-r--r--doc/development/ux_guide/img/button-success-secondary--active.pngbin1466 -> 1107 bytes
-rw-r--r--doc/development/ux_guide/img/button-success-secondary--hover.pngbin1091 -> 774 bytes
-rw-r--r--doc/development/ux_guide/img/button-success-secondary--resting.pngbin1394 -> 945 bytes
-rw-r--r--doc/development/ux_guide/img/button-warning--active.pngbin1388 -> 1124 bytes
-rw-r--r--doc/development/ux_guide/img/button-warning--hover.pngbin1040 -> 790 bytes
-rw-r--r--doc/development/ux_guide/img/button-warning--resting.pngbin1296 -> 925 bytes
-rw-r--r--doc/development/ux_guide/img/color-blue.pngbin3555 -> 1751 bytes
-rw-r--r--doc/development/ux_guide/img/color-green.pngbin3852 -> 1916 bytes
-rw-r--r--doc/development/ux_guide/img/color-grey.pngbin3523 -> 1681 bytes
-rw-r--r--doc/development/ux_guide/img/color-orange.pngbin4480 -> 2094 bytes
-rw-r--r--doc/development/ux_guide/img/color-red.pngbin3550 -> 1739 bytes
-rw-r--r--doc/development/ux_guide/img/components-anchorlinks.pngbin30089 -> 16457 bytes
-rw-r--r--doc/development/ux_guide/img/components-coverblock.pngbin10141 -> 10110 bytes
-rw-r--r--doc/development/ux_guide/img/components-dateexact.pngbin4161 -> 4157 bytes
-rw-r--r--doc/development/ux_guide/img/components-fileholder.pngbin3938 -> 3884 bytes
-rw-r--r--doc/development/ux_guide/img/components-horizontalform.pngbin4327 -> 4310 bytes
-rw-r--r--doc/development/ux_guide/img/components-listinsidepanel.pngbin3449 -> 3380 bytes
-rw-r--r--doc/development/ux_guide/img/components-listwithhover.pngbin2860 -> 2722 bytes
-rw-r--r--doc/development/ux_guide/img/components-referencehover.pngbin6948 -> 6890 bytes
-rw-r--r--doc/development/ux_guide/img/components-referenceissues.pngbin10009 -> 10002 bytes
-rw-r--r--doc/development/ux_guide/img/components-referencelabels.pngbin4108 -> 4099 bytes
-rw-r--r--doc/development/ux_guide/img/components-referencemilestone.pngbin2417 -> 2412 bytes
-rw-r--r--doc/development/ux_guide/img/components-referencemrs.pngbin8859 -> 8857 bytes
-rw-r--r--doc/development/ux_guide/img/components-referencepeople.pngbin5607 -> 5600 bytes
-rw-r--r--doc/development/ux_guide/img/components-searchbox.pngbin5292 -> 3596 bytes
-rw-r--r--doc/development/ux_guide/img/components-searchboxscoped.pngbin9668 -> 6027 bytes
-rw-r--r--doc/development/ux_guide/img/components-simplelist.pngbin2781 -> 2776 bytes
-rw-r--r--doc/development/ux_guide/img/features-contextualnav.pngbin5912 -> 5911 bytes
-rw-r--r--doc/development/ux_guide/img/features-emptystates.pngbin61664 -> 61644 bytes
-rw-r--r--doc/development/ux_guide/img/icon-add.pngbin317 -> 239 bytes
-rw-r--r--doc/development/ux_guide/img/icon-close.pngbin501 -> 301 bytes
-rw-r--r--doc/development/ux_guide/img/icon-edit.pngbin546 -> 315 bytes
-rw-r--r--doc/development/ux_guide/img/icon-notification.pngbin543 -> 379 bytes
-rw-r--r--doc/development/ux_guide/img/icon-rss.pngbin834 -> 528 bytes
-rw-r--r--doc/development/ux_guide/img/icon-spec.pngbin13889 -> 7523 bytes
-rw-r--r--doc/development/ux_guide/img/icon-subscribe.pngbin760 -> 537 bytes
-rw-r--r--doc/development/ux_guide/img/icon-trash.pngbin398 -> 281 bytes
-rw-r--r--doc/development/ux_guide/img/illustrations-caps-do.pngbin3775 -> 3671 bytes
-rw-r--r--doc/development/ux_guide/img/illustrations-caps-don't.pngbin3922 -> 3624 bytes
-rw-r--r--doc/development/ux_guide/img/james-mackey.pngbin32552 -> 11869 bytes
-rw-r--r--doc/development/ux_guide/img/karolina-plaskaty.pngbin29553 -> 10355 bytes
-rw-r--r--doc/development/ux_guide/img/matthieu-poirier.pngbin32819 -> 11483 bytes
-rw-r--r--doc/development/ux_guide/img/modals-general-confimation-dialog.pngbin51205 -> 15650 bytes
-rw-r--r--doc/development/ux_guide/img/modals-layout-for-modals.pngbin68203 -> 20060 bytes
-rw-r--r--doc/development/ux_guide/img/modals-special-confimation-dialog.pngbin89978 -> 31419 bytes
-rw-r--r--doc/development/ux_guide/img/modals-three-buttons.pngbin54927 -> 17457 bytes
-rw-r--r--doc/development/ux_guide/img/nazim-ramesh.pngbin27475 -> 10488 bytes
-rw-r--r--doc/development/ux_guide/img/popover-placement-above.pngbin68451 -> 20184 bytes
-rw-r--r--doc/development/ux_guide/img/popover-placement-below.pngbin63368 -> 19967 bytes
-rw-r--r--doc/development/ux_guide/img/surfaces-contentitemtitle.pngbin5142 -> 5139 bytes
-rw-r--r--doc/development/ux_guide/img/surfaces-systeminformationblock.pngbin10423 -> 10422 bytes
-rw-r--r--doc/development/ux_guide/img/surfaces-ux.pngbin4029 -> 4017 bytes
-rw-r--r--doc/development/ux_guide/img/tooltip-placement.pngbin2071 -> 2066 bytes
-rw-r--r--doc/development/ux_guide/img/tooltip-usage.pngbin5994 -> 5985 bytes
-rw-r--r--doc/development/ux_guide/tips.md18
-rw-r--r--doc/development/ux_guide/users.md22
-rw-r--r--doc/development/what_requires_downtime.md2
-rw-r--r--doc/gitlab-basics/add-image.md2
-rw-r--r--doc/gitlab-basics/command-line-commands.md3
-rw-r--r--doc/gitlab-basics/create-project.md6
-rw-r--r--doc/gitlab-basics/create-your-ssh-keys.md2
-rw-r--r--doc/gitlab-basics/img/create_new_project_info.pngbin77490 -> 71608 bytes
-rw-r--r--doc/gitlab-basics/img/fork_new.pngbin10722 -> 10572 bytes
-rw-r--r--doc/gitlab-basics/img/merge_request_select_branch.pngbin16668 -> 16654 bytes
-rw-r--r--doc/gitlab-basics/img/profile_settings.pngbin5842 -> 2842 bytes
-rw-r--r--doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.pngbin13447 -> 13436 bytes
-rw-r--r--doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.pngbin24639 -> 10534 bytes
-rw-r--r--doc/gitlab-basics/img/profile_settings_ssh_keys_title.pngbin1872 -> 1867 bytes
-rw-r--r--doc/img/devops_lifecycle.pngbin65043 -> 18611 bytes
-rw-r--r--doc/install/README.md6
-rw-r--r--doc/install/aws/img/add_tags.pngbin0 -> 17834 bytes
-rw-r--r--doc/install/aws/img/associate_subnet_gateway.pngbin0 -> 16522 bytes
-rw-r--r--doc/install/aws/img/associate_subnet_gateway_2.pngbin0 -> 10617 bytes
-rw-r--r--doc/install/aws/img/aws_diagram.pngbin0 -> 502497 bytes
-rw-r--r--doc/install/aws/img/choose_ami.pngbin0 -> 4892 bytes
-rw-r--r--doc/install/aws/img/create_gateway.pngbin0 -> 13927 bytes
-rw-r--r--doc/install/aws/img/create_route_table.pngbin0 -> 8293 bytes
-rw-r--r--doc/install/aws/img/create_security_group.pngbin0 -> 12594 bytes
-rw-r--r--doc/install/aws/img/create_subnet.pngbin0 -> 16679 bytes
-rw-r--r--doc/install/aws/img/create_vpc.pngbin0 -> 15613 bytes
-rw-r--r--doc/install/aws/img/ec_az.pngbin0 -> 10476 bytes
-rw-r--r--doc/install/aws/img/ec_subnet.pngbin0 -> 23517 bytes
-rw-r--r--doc/install/aws/img/policies.pngbin0 -> 39723 bytes
-rw-r--r--doc/install/aws/img/rds_subnet_group.pngbin0 -> 30107 bytes
-rw-r--r--doc/install/aws/index.md655
-rw-r--r--doc/install/azure/index.md42
-rw-r--r--doc/install/database_mysql.md52
-rw-r--r--doc/install/digitaloceandocker.md102
-rw-r--r--doc/install/google_cloud_platform/img/boot_disk.pngbin124175 -> 37786 bytes
-rw-r--r--doc/install/google_cloud_platform/img/first_signin.pngbin69516 -> 48215 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_landing.pngbin15025 -> 9978 bytes
-rw-r--r--doc/install/google_cloud_platform/img/launch_vm.pngbin47201 -> 33906 bytes
-rw-r--r--doc/install/google_cloud_platform/img/ssh_terminal.pngbin94161 -> 59212 bytes
-rw-r--r--doc/install/google_cloud_platform/img/vm_created.pngbin13809 -> 9903 bytes
-rw-r--r--doc/install/google_cloud_platform/img/vm_details.pngbin76519 -> 50191 bytes
-rw-r--r--doc/install/installation.md66
-rw-r--r--doc/install/kubernetes/gitlab_chart.md114
-rw-r--r--doc/install/kubernetes/gitlab_omnibus.md143
-rw-r--r--doc/install/kubernetes/gitlab_runner_chart.md4
-rw-r--r--doc/install/kubernetes/index.md53
-rw-r--r--doc/install/kubernetes/preparation/connect.md18
-rw-r--r--doc/install/kubernetes/preparation/networking.md8
-rw-r--r--doc/install/kubernetes/preparation/rbac.md20
-rw-r--r--doc/install/kubernetes/preparation/tiller.md9
-rw-r--r--doc/install/openshift_and_gitlab/img/add-gitlab-to-project.pngbin37386 -> 14619 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/add-to-project.pngbin21672 -> 8901 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/create-project-ui.pngbin22290 -> 9151 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/gitlab-logs.pngbin70858 -> 26240 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/gitlab-overview.pngbin106432 -> 42908 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/gitlab-running.pngbin107993 -> 42045 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/gitlab-scale.pngbin36628 -> 15002 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/gitlab-settings.pngbin111366 -> 42221 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/no-resources.pngbin34669 -> 12465 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/openshift-infra-project.pngbin95725 -> 37117 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/pods-overview.pngbin106861 -> 42617 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/rc-name.pngbin51390 -> 20571 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/running-pods.pngbin29818 -> 11942 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/storage-volumes.pngbin49584 -> 20007 bytes
-rw-r--r--doc/install/openshift_and_gitlab/img/web-console.pngbin34774 -> 12717 bytes
-rw-r--r--doc/install/openshift_and_gitlab/index.md105
-rw-r--r--doc/install/relative_url.md2
-rw-r--r--doc/install/requirements.md22
-rw-r--r--doc/integration/README.md2
-rw-r--r--doc/integration/akismet.md16
-rw-r--r--doc/integration/auth0.md2
-rw-r--r--doc/integration/azure.md2
-rw-r--r--doc/integration/bitbucket.md1
-rw-r--r--doc/integration/github.md4
-rw-r--r--doc/integration/gmail_action_buttons_for_gitlab.md2
-rw-r--r--doc/integration/google.md2
-rw-r--r--doc/integration/img/bitbucket_oauth_keys.pngbin5149 -> 5146 bytes
-rw-r--r--doc/integration/img/enable_trello_powerup.pngbin17905 -> 17791 bytes
-rw-r--r--doc/integration/img/enabled-oauth-sign-in-sources.pngbin13304 -> 13303 bytes
-rw-r--r--doc/integration/img/facebook_api_keys.pngbin42308 -> 42290 bytes
-rw-r--r--doc/integration/img/facebook_website_url.pngbin9620 -> 9615 bytes
-rw-r--r--doc/integration/img/gitlab_app.pngbin56480 -> 32020 bytes
-rw-r--r--doc/integration/img/google_app.pngbin19168 -> 18853 bytes
-rw-r--r--doc/integration/img/oauth_provider_admin_application.pngbin17082 -> 17071 bytes
-rw-r--r--doc/integration/img/oauth_provider_application_form.pngbin12566 -> 12553 bytes
-rw-r--r--doc/integration/img/oauth_provider_application_id_secret.pngbin15293 -> 15280 bytes
-rw-r--r--doc/integration/img/oauth_provider_authorized_application.pngbin14668 -> 14657 bytes
-rw-r--r--doc/integration/img/submit_issue.pngbin45962 -> 45771 bytes
-rw-r--r--doc/integration/img/twitter_app_api_keys.pngbin24577 -> 24570 bytes
-rw-r--r--doc/integration/oauth2_generic.md26
-rw-r--r--doc/integration/omniauth.md70
-rw-r--r--doc/integration/recaptcha.md20
-rw-r--r--doc/integration/saml.md24
-rw-r--r--doc/monitoring/performance/img/grafana_dashboard_import.pngbin11836 -> 11835 bytes
-rw-r--r--doc/monitoring/performance/img/grafana_data_source_configuration.pngbin14700 -> 14695 bytes
-rw-r--r--doc/monitoring/performance/img/grafana_data_source_empty.pngbin11963 -> 11960 bytes
-rw-r--r--doc/monitoring/performance/img/grafana_save_icon.pngbin4619 -> 4598 bytes
-rw-r--r--doc/monitoring/performance/img/metrics_gitlab_configuration_settings.pngbin21387 -> 21382 bytes
-rw-r--r--doc/policy/maintenance.md5
-rw-r--r--doc/public_access/img/restrict_visibility_levels.pngbin24593 -> 18825 bytes
-rw-r--r--doc/raketasks/backup_hrz.pngbin11444 -> 11441 bytes
-rw-r--r--doc/raketasks/backup_restore.md329
-rw-r--r--doc/raketasks/import.md72
-rw-r--r--doc/security/README.md1
-rw-r--r--doc/security/img/outbound_requests_section.pngbin18064 -> 7314 bytes
-rw-r--r--doc/security/img/ssh_keys_restrictions_settings.pngbin68496 -> 19571 bytes
-rw-r--r--doc/security/img/two_factor_authentication_group_settings.pngbin44874 -> 19495 bytes
-rw-r--r--doc/security/img/two_factor_authentication_settings.pngbin9941 -> 9936 bytes
-rw-r--r--doc/security/rack_attack.md18
-rw-r--r--doc/security/reset_root_password.md2
-rw-r--r--doc/security/two_factor_authentication.md6
-rw-r--r--doc/security/unlock_user.md31
-rw-r--r--doc/security/user_email_confirmation.md4
-rw-r--r--doc/ssh/README.md259
-rw-r--r--doc/topics/authentication/index.md10
-rw-r--r--doc/topics/autodevops/img/guide_environments.pngbin8570 -> 8434 bytes
-rw-r--r--doc/topics/autodevops/img/guide_ide_commit.pngbin22035 -> 22029 bytes
-rw-r--r--doc/topics/autodevops/index.md250
-rw-r--r--doc/topics/autodevops/quick_start_guide.md21
-rw-r--r--doc/topics/git/numerous_undo_possibilities_in_git/img/branching.pngbin26245 -> 12744 bytes
-rw-r--r--doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.pngbin43609 -> 21836 bytes
-rw-r--r--doc/topics/git/numerous_undo_possibilities_in_git/img/revert.pngbin28112 -> 13243 bytes
-rw-r--r--doc/topics/git/numerous_undo_possibilities_in_git/index.md138
-rw-r--r--doc/topics/git/troubleshooting_git.md15
-rw-r--r--doc/university/README.md23
-rw-r--r--doc/university/bookclub/booklist.md2
-rw-r--r--doc/university/glossary/README.md6
-rw-r--r--doc/university/high-availability/aws/README.md42
-rw-r--r--doc/university/high-availability/aws/img/auto-scaling-det.pngbin29970 -> 29967 bytes
-rw-r--r--doc/university/high-availability/aws/img/db-subnet-group.pngbin29306 -> 29298 bytes
-rw-r--r--doc/university/high-availability/aws/img/ig.pngbin8149 -> 8140 bytes
-rw-r--r--doc/university/high-availability/aws/img/instance_specs.pngbin11525 -> 11522 bytes
-rw-r--r--doc/university/high-availability/aws/img/new_vpc.pngbin15696 -> 15691 bytes
-rw-r--r--doc/university/high-availability/aws/img/policies.pngbin39845 -> 39723 bytes
-rw-r--r--doc/university/high-availability/aws/img/rds-net-opt.pngbin16347 -> 16340 bytes
-rw-r--r--doc/university/high-availability/aws/img/rds-sec-group.pngbin11584 -> 11579 bytes
-rw-r--r--doc/university/high-availability/aws/img/reference-arch.pngbin183997 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/reference-arch2.pngbin0 -> 184033 bytes
-rw-r--r--doc/university/high-availability/aws/img/subnet.pngbin17077 -> 17070 bytes
-rw-r--r--doc/university/support/README.md44
-rw-r--r--doc/university/training/end-user/README.md73
-rw-r--r--doc/university/training/gitlab_flow/production_branch.pngbin7293 -> 7291 bytes
-rw-r--r--doc/university/training/gitlab_flow/release_branches.pngbin12775 -> 12765 bytes
-rw-r--r--doc/university/training/topics/additional_resources.md12
-rw-r--r--doc/university/training/topics/bisect.md14
-rw-r--r--doc/university/training/topics/env_setup.md4
-rw-r--r--doc/university/training/topics/getting_started.md30
-rw-r--r--doc/university/training/topics/git_add.md38
-rw-r--r--doc/university/training/topics/git_log.md45
-rw-r--r--doc/university/training/topics/merge_conflicts.md18
-rw-r--r--doc/university/training/topics/rollback_commits.md46
-rw-r--r--doc/university/training/topics/stash.md70
-rw-r--r--doc/university/training/topics/tags.md2
-rw-r--r--doc/university/training/topics/unstage.md27
-rw-r--r--doc/university/training/user_training.md267
-rw-r--r--doc/update/10.7-to-10.8.md20
-rw-r--r--doc/update/11.2-to-11.3.md378
-rw-r--r--doc/update/11.3-to-11.4.md378
-rw-r--r--doc/update/11.4-to-11.5.md390
-rw-r--r--doc/update/4.2-to-5.0.md2
-rw-r--r--doc/update/7.5-to-7.6.md2
-rw-r--r--doc/update/7.6-to-7.7.md2
-rw-r--r--doc/update/7.7-to-7.8.md2
-rw-r--r--doc/update/7.8-to-7.9.md2
-rw-r--r--doc/update/7.9-to-7.10.md2
-rw-r--r--doc/update/8.17-to-9.0.md74
-rw-r--r--doc/update/9.0-to-9.1.md76
-rw-r--r--doc/update/9.4-to-9.5.md2
-rw-r--r--doc/update/9.5-to-10.0.md2
-rw-r--r--doc/update/README.md11
-rw-r--r--doc/update/patch_versions.md2
-rw-r--r--doc/user/admin_area/diff_limits.md21
-rw-r--r--doc/user/admin_area/img/admin_area_settings_button.pngbin0 -> 7993 bytes
-rw-r--r--doc/user/admin_area/img/cohorts.pngbin439635 -> 0 bytes
-rw-r--r--doc/user/admin_area/monitoring/convdev.md32
-rw-r--r--doc/user/admin_area/monitoring/health_check.md14
-rw-r--r--doc/user/admin_area/monitoring/img/convdev_index.pngbin119743 -> 0 bytes
-rw-r--r--doc/user/admin_area/monitoring/img/health_check_token.pngbin4923 -> 4863 bytes
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md75
-rw-r--r--doc/user/admin_area/settings/email.md17
-rw-r--r--doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.pngbin14656 -> 5360 bytes
-rw-r--r--doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.pngbin12917 -> 4771 bytes
-rw-r--r--doc/user/admin_area/settings/img/admin_area_settings_button.pngbin4403 -> 0 bytes
-rw-r--r--doc/user/admin_area/settings/img/domain_blacklist.pngbin13606 -> 13601 bytes
-rw-r--r--doc/user/admin_area/settings/img/restricted_url.pngbin18202 -> 18191 bytes
-rw-r--r--doc/user/admin_area/settings/img/update-available.pngbin89748 -> 26769 bytes
-rw-r--r--doc/user/admin_area/settings/index.md22
-rw-r--r--doc/user/admin_area/settings/terms.md8
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md25
-rw-r--r--doc/user/admin_area/user_cohorts.md40
-rw-r--r--doc/user/award_emojis.md10
-rw-r--r--doc/user/discussions/img/automatically_resolve_outdated_discussions.pngbin117604 -> 38001 bytes
-rw-r--r--doc/user/discussions/img/btn_new_issue_for_all_discussions.pngbin29007 -> 9555 bytes
-rw-r--r--doc/user/discussions/img/discussion_comment.pngbin57189 -> 37351 bytes
-rw-r--r--doc/user/discussions/img/discussion_lock_system_notes.pngbin50200 -> 14288 bytes
-rw-r--r--doc/user/discussions/img/discussion_view.pngbin73821 -> 73807 bytes
-rw-r--r--doc/user/discussions/img/lock_form_member.pngbin100581 -> 23780 bytes
-rw-r--r--doc/user/discussions/img/lock_form_non_member.pngbin37432 -> 8362 bytes
-rw-r--r--doc/user/discussions/img/new_issue_for_discussion.pngbin39563 -> 11324 bytes
-rw-r--r--doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.pngbin4962 -> 4944 bytes
-rw-r--r--doc/user/discussions/img/preview_issue_for_discussion.pngbin82412 -> 26965 bytes
-rw-r--r--doc/user/discussions/img/preview_issue_for_discussions.pngbin143871 -> 35211 bytes
-rw-r--r--doc/user/discussions/img/resolve_comment_button.pngbin4722 -> 4713 bytes
-rw-r--r--doc/user/discussions/img/resolve_discussion_issue_notice.pngbin10307 -> 4186 bytes
-rw-r--r--doc/user/discussions/img/resolve_discussion_open_issue.pngbin20967 -> 7514 bytes
-rw-r--r--doc/user/discussions/img/turn_off_lock.pngbin31580 -> 9626 bytes
-rw-r--r--doc/user/discussions/img/turn_on_lock.pngbin34839 -> 10409 bytes
-rw-r--r--doc/user/discussions/index.md10
-rw-r--r--doc/user/gitlab_com/index.md15
-rw-r--r--doc/user/group/img/access_requests_management.pngbin11186 -> 22616 bytes
-rw-r--r--doc/user/group/img/add_new_members.pngbin67235 -> 66523 bytes
-rw-r--r--doc/user/group/img/create_new_group_info.pngbin105173 -> 51072 bytes
-rw-r--r--doc/user/group/img/create_new_project_from_group.pngbin3194 -> 37234 bytes
-rw-r--r--doc/user/group/img/group_settings.pngbin28821 -> 51335 bytes
-rw-r--r--doc/user/group/img/groups.pngbin249533 -> 61507 bytes
-rw-r--r--doc/user/group/img/membership_lock.pngbin17333 -> 4820 bytes
-rw-r--r--doc/user/group/img/new_group_form.pngbin114515 -> 32510 bytes
-rw-r--r--doc/user/group/img/new_group_from_groups.pngbin109302 -> 25256 bytes
-rw-r--r--doc/user/group/img/new_group_from_other_pages.pngbin90423 -> 21593 bytes
-rw-r--r--doc/user/group/img/request_access_button.pngbin35917 -> 36258 bytes
-rw-r--r--doc/user/group/img/select_group_dropdown.pngbin3489 -> 74893 bytes
-rw-r--r--doc/user/group/img/share_with_group_lock.pngbin21541 -> 7493 bytes
-rw-r--r--doc/user/group/img/withdraw_access_request_button.pngbin36413 -> 36782 bytes
-rw-r--r--doc/user/group/index.md24
-rw-r--r--doc/user/group/subgroups/img/group_members.pngbin48240 -> 18009 bytes
-rw-r--r--doc/user/group/subgroups/img/mention_subgroups.pngbin39666 -> 14783 bytes
-rw-r--r--doc/user/group/subgroups/index.md32
-rw-r--r--doc/user/img/award_emoji_comment_picker.pngbin72883 -> 72847 bytes
-rw-r--r--doc/user/img/award_emoji_select.pngbin17827 -> 17554 bytes
-rw-r--r--doc/user/img/award_emoji_votes_sort_options.pngbin99941 -> 40489 bytes
-rw-r--r--doc/user/img/color_inline_colorchip_render_gfm.pngbin0 -> 11534 bytes
-rw-r--r--doc/user/img/markdown_logo.pngbin4421 -> 4398 bytes
-rw-r--r--doc/user/img/math_inline_sup_render_gfm.pngbin0 -> 1359 bytes
-rw-r--r--doc/user/img/mermaid_diagram_render_gfm.pngbin0 -> 4587 bytes
-rw-r--r--doc/user/img/task_list_ordered_render_gfm.pngbin0 -> 6247 bytes
-rw-r--r--doc/user/img/unordered_check_list_render_gfm.pngbin0 -> 6207 bytes
-rw-r--r--doc/user/index.md6
-rw-r--r--doc/user/instance_statistics/convdev.md27
-rw-r--r--doc/user/instance_statistics/img/cohorts.pngbin0 -> 59494 bytes
-rw-r--r--doc/user/instance_statistics/img/convdev_index.pngbin0 -> 86358 bytes
-rw-r--r--doc/user/instance_statistics/img/instance_statistics_button.pngbin0 -> 9462 bytes
-rw-r--r--doc/user/instance_statistics/index.md16
-rw-r--r--doc/user/instance_statistics/user_cohorts.md27
-rw-r--r--doc/user/markdown.md252
-rw-r--r--doc/user/permissions.md30
-rw-r--r--doc/user/profile/account/delete_account.md3
-rw-r--r--doc/user/profile/account/two_factor_authentication.md38
-rw-r--r--doc/user/profile/img/active_sessions_list.pngbin41649 -> 22266 bytes
-rw-r--r--doc/user/profile/img/personal_access_tokens.pngbin18555 -> 18553 bytes
-rw-r--r--doc/user/profile/img/profil-preferences-navigation-theme.pngbin16403 -> 16397 bytes
-rw-r--r--doc/user/profile/index.md60
-rw-r--r--doc/user/profile/personal_access_tokens.md8
-rw-r--r--doc/user/project/badges.md4
-rw-r--r--doc/user/project/bulk_editing.md11
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/img/add_cluster.pngbin77046 -> 59516 bytes
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/img/create_dns.pngbin29185 -> 23923 bytes
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/img/create_project.pngbin43429 -> 30568 bytes
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.pngbin115299 -> 82157 bytes
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/img/environment.pngbin31644 -> 20339 bytes
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/img/new_project.pngbin10309 -> 0 bytes
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/img/pipeline.pngbin26500 -> 15288 bytes
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/img/rbac.pngbin0 -> 15960 bytes
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/index.md259
-rw-r--r--doc/user/project/clusters/index.md175
-rw-r--r--doc/user/project/clusters/runbooks/img/authorize-jupyter.pngbin0 -> 126425 bytes
-rw-r--r--doc/user/project/clusters/runbooks/img/demo-runbook.pngbin0 -> 132436 bytes
-rw-r--r--doc/user/project/clusters/runbooks/img/gitlab-variables.pngbin0 -> 179611 bytes
-rw-r--r--doc/user/project/clusters/runbooks/img/helm-install.pngbin0 -> 201348 bytes
-rw-r--r--doc/user/project/clusters/runbooks/img/ingress-install.pngbin0 -> 140880 bytes
-rw-r--r--doc/user/project/clusters/runbooks/img/jupyterhub-install.pngbin0 -> 116775 bytes
-rw-r--r--doc/user/project/clusters/runbooks/img/postgres-query.pngbin0 -> 209435 bytes
-rw-r--r--doc/user/project/clusters/runbooks/img/sample-runbook.pngbin0 -> 145728 bytes
-rw-r--r--doc/user/project/clusters/runbooks/index.md137
-rw-r--r--doc/user/project/clusters/serverless/img/deploy-stage.pngbin0 -> 12029 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/dns-entry.pngbin0 -> 56600 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/install-knative.pngbin0 -> 102861 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/knative-app.pngbin0 -> 28998 bytes
-rw-r--r--doc/user/project/clusters/serverless/index.md137
-rw-r--r--doc/user/project/container_registry.md57
-rw-r--r--doc/user/project/cycle_analytics.md4
-rw-r--r--doc/user/project/deploy_tokens/img/deploy_tokens.pngbin75650 -> 23087 bytes
-rw-r--r--doc/user/project/deploy_tokens/index.md45
-rw-r--r--doc/user/project/img/bulk-editing.pngbin197686 -> 197667 bytes
-rw-r--r--doc/user/project/img/cycle_analytics_landing_page.pngbin42117 -> 42114 bytes
-rw-r--r--doc/user/project/img/issue_board.pngbin100684 -> 327718 bytes
-rw-r--r--doc/user/project/img/issue_board_assignee_lists.pngbin134674 -> 134635 bytes
-rw-r--r--doc/user/project/img/issue_board_creation.pngbin108674 -> 108615 bytes
-rw-r--r--doc/user/project/img/issue_board_edit_button.pngbin108168 -> 108114 bytes
-rw-r--r--doc/user/project/img/issue_board_milestone_lists.pngbin0 -> 58740 bytes
-rw-r--r--doc/user/project/img/issue_board_move_issue_card_list.pngbin13670 -> 13592 bytes
-rw-r--r--doc/user/project/img/issue_board_summed_weights.pngbin0 -> 26691 bytes
-rw-r--r--doc/user/project/img/issue_board_view_scope.pngbin63542 -> 63529 bytes
-rw-r--r--doc/user/project/img/issue_boards_add_issues_modal.pngbin12421 -> 12415 bytes
-rw-r--r--doc/user/project/img/issue_boards_core.pngbin0 -> 61230 bytes
-rw-r--r--doc/user/project/img/issue_boards_multiple.pngbin6092 -> 6086 bytes
-rw-r--r--doc/user/project/img/issue_boards_premium.pngbin0 -> 72434 bytes
-rw-r--r--doc/user/project/img/issue_boards_remove_issue.pngbin39357 -> 39347 bytes
-rw-r--r--doc/user/project/img/koding_build-in-progress.pngbin21953 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_build-logs.pngbin91364 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_build-success.pngbin73008 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_commit-koding.yml.pngbin86043 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_different-stack-on-mr-try.pngbin93404 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_edit-on-ide.pngbin90701 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_enable-koding.pngbin20303 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_landing.pngbin81010 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_open-gitlab-from-koding.pngbin10851 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_run-in-ide.pngbin22179 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_run-mr-in-ide.pngbin93780 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_set-up-ide.pngbin54062 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_stack-import.pngbin137608 -> 0 bytes
-rw-r--r--doc/user/project/img/koding_start-build.pngbin27926 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_generate_default.pngbin65549 -> 25797 bytes
-rw-r--r--doc/user/project/img/labels_group_issues.pngbin264573 -> 92070 bytes
-rw-r--r--doc/user/project/img/labels_list.pngbin207736 -> 71323 bytes
-rw-r--r--doc/user/project/img/labels_prioritized.pngbin156020 -> 56353 bytes
-rw-r--r--doc/user/project/img/labels_project_list_search.pngbin175669 -> 100066 bytes
-rw-r--r--doc/user/project/img/labels_promotion.pngbin121824 -> 44021 bytes
-rw-r--r--doc/user/project/img/labels_sidebar.pngbin31123 -> 12109 bytes
-rw-r--r--doc/user/project/img/labels_sidebar_assign.pngbin28033 -> 11024 bytes
-rw-r--r--doc/user/project/img/labels_sidebar_inline.pngbin28423 -> 11083 bytes
-rw-r--r--doc/user/project/img/labels_sort_label_priority.pngbin110154 -> 42263 bytes
-rw-r--r--doc/user/project/img/labels_sort_priority.pngbin108780 -> 41486 bytes
-rw-r--r--doc/user/project/img/labels_subscriptions.pngbin87502 -> 31716 bytes
-rw-r--r--doc/user/project/img/priority_sort_order.pngbin102242 -> 69978 bytes
-rw-r--r--doc/user/project/img/project_overview_badges.pngbin40188 -> 12486 bytes
-rw-r--r--doc/user/project/img/project_repository_settings.pngbin17872 -> 7511 bytes
-rw-r--r--doc/user/project/img/protected_branches_delete.pngbin21510 -> 21507 bytes
-rw-r--r--doc/user/project/img/protected_branches_devs_can_push.pngbin34888 -> 11221 bytes
-rw-r--r--doc/user/project/img/protected_branches_error_ui.pngbin13125 -> 13117 bytes
-rw-r--r--doc/user/project/img/protected_branches_list.pngbin6937 -> 6933 bytes
-rw-r--r--doc/user/project/img/protected_branches_page.pngbin7205 -> 7199 bytes
-rw-r--r--doc/user/project/img/protected_tag_matches.pngbin85305 -> 23054 bytes
-rw-r--r--doc/user/project/img/protected_tags_list.pngbin24490 -> 7227 bytes
-rw-r--r--doc/user/project/img/protected_tags_page.pngbin56112 -> 13813 bytes
-rw-r--r--doc/user/project/img/protected_tags_permissions_dropdown.pngbin26514 -> 7770 bytes
-rw-r--r--doc/user/project/import/clearcase.md2
-rw-r--r--doc/user/project/import/github.md31
-rw-r--r--doc/user/project/import/img/bitbucket_server_import_credentials.pngbin40566 -> 13781 bytes
-rw-r--r--doc/user/project/import/img/bitbucket_server_import_select_project.pngbin56750 -> 19427 bytes
-rw-r--r--doc/user/project/import/img/fogbugz_import_login.pngbin13751 -> 13452 bytes
-rw-r--r--doc/user/project/import/img/fogbugz_import_select_fogbogz.pngbin12289 -> 12283 bytes
-rw-r--r--doc/user/project/import/img/fogbugz_import_select_project.pngbin20905 -> 20335 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_gitea_new_import.pngbin15561 -> 15518 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_github_select_auth_method.pngbin17612 -> 17611 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_new_project_page.pngbin81639 -> 30489 bytes
-rw-r--r--doc/user/project/import/img/manifest_upload.pngbin12079 -> 0 bytes
-rw-r--r--doc/user/project/import/index.md5
-rw-r--r--doc/user/project/import/manifest.md70
-rw-r--r--doc/user/project/import/svn.md9
-rw-r--r--doc/user/project/integrations/bamboo.md4
-rw-r--r--doc/user/project/integrations/bugzilla.md5
-rw-r--r--doc/user/project/integrations/discord_notifications.md29
-rw-r--r--doc/user/project/integrations/hangouts_chat.md4
-rw-r--r--doc/user/project/integrations/img/hangouts_chat_configuration.pngbin101788 -> 44655 bytes
-rw-r--r--doc/user/project/integrations/img/issue_configuration.pngbin20288 -> 11882 bytes
-rw-r--r--doc/user/project/integrations/img/jira_api_token.pngbin0 -> 160587 bytes
-rw-r--r--doc/user/project/integrations/img/jira_api_token_menu.pngbin0 -> 68564 bytes
-rw-r--r--doc/user/project/integrations/img/jira_group_access.pngbin19235 -> 19147 bytes
-rw-r--r--doc/user/project/integrations/img/jira_project_name.pngbin26685 -> 26680 bytes
-rw-r--r--doc/user/project/integrations/img/jira_project_settings.pngbin32791 -> 14149 bytes
-rw-r--r--doc/user/project/integrations/img/jira_service.pngbin37869 -> 36976 bytes
-rw-r--r--doc/user/project/integrations/img/jira_service_close_comment.pngbin11893 -> 11890 bytes
-rw-r--r--doc/user/project/integrations/img/jira_service_page.pngbin193364 -> 74893 bytes
-rw-r--r--doc/user/project/integrations/img/jira_user_management_link.pngbin23921 -> 23906 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_bot_auth.pngbin8676 -> 8669 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_bot_available_commands.pngbin4647 -> 4642 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_configuration.pngbin249592 -> 101151 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_console_integrations.pngbin314642 -> 114618 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_gitlab_token.pngbin3688 -> 3673 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_goto_console.pngbin7754 -> 7746 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_slash_command_configuration.pngbin24169 -> 24162 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_team_integrations.pngbin4766 -> 4757 bytes
-rw-r--r--doc/user/project/integrations/img/merge_request_performance.pngbin60194 -> 40917 bytes
-rw-r--r--doc/user/project/integrations/img/microsoft_teams_configuration.pngbin350592 -> 96509 bytes
-rw-r--r--doc/user/project/integrations/img/project_services.pngbin25753 -> 11109 bytes
-rw-r--r--doc/user/project/integrations/img/prometheus_dashboard.pngbin26112 -> 12650 bytes
-rw-r--r--doc/user/project/integrations/img/prometheus_deploy.pngbin27258 -> 8413 bytes
-rw-r--r--doc/user/project/integrations/img/prometheus_yaml_deploy.pngbin23567 -> 9456 bytes
-rw-r--r--doc/user/project/integrations/img/redmine_configuration.pngbin10266 -> 10254 bytes
-rw-r--r--doc/user/project/integrations/img/services_templates_redmine_example.pngbin8608 -> 8336 bytes
-rw-r--r--doc/user/project/integrations/img/slack_configuration.pngbin229050 -> 92179 bytes
-rw-r--r--doc/user/project/integrations/img/webhook_logs.pngbin132319 -> 38687 bytes
-rw-r--r--doc/user/project/integrations/img/webhook_testing.pngbin191267 -> 55578 bytes
-rw-r--r--doc/user/project/integrations/img/webhooks_ssl.pngbin27799 -> 58529 bytes
-rw-r--r--doc/user/project/integrations/jira.md259
-rw-r--r--doc/user/project/integrations/jira_cloud_configuration.md18
-rw-r--r--doc/user/project/integrations/jira_server_configuration.md53
-rw-r--r--doc/user/project/integrations/mattermost.md8
-rw-r--r--doc/user/project/integrations/mattermost_slash_commands.md2
-rw-r--r--doc/user/project/integrations/microsoft_teams.md4
-rw-r--r--doc/user/project/integrations/mock_ci.md2
-rw-r--r--doc/user/project/integrations/project_services.md2
-rw-r--r--doc/user/project/integrations/prometheus.md2
-rw-r--r--doc/user/project/integrations/prometheus_library/metrics.md6
-rw-r--r--doc/user/project/integrations/redmine.md7
-rw-r--r--doc/user/project/integrations/services_templates.md6
-rw-r--r--doc/user/project/integrations/webhooks.md74
-rw-r--r--doc/user/project/issue_board.md308
-rw-r--r--doc/user/project/issues/automatic_issue_closing.md9
-rw-r--r--doc/user/project/issues/create_new_issue.md18
-rw-r--r--doc/user/project/issues/img/confidential_issues_index_page.pngbin107117 -> 30634 bytes
-rw-r--r--doc/user/project/issues/img/delete_issue.pngbin49894 -> 13973 bytes
-rw-r--r--doc/user/project/issues/img/due_dates_create.pngbin6992 -> 6895 bytes
-rw-r--r--doc/user/project/issues/img/group_issues_list_view.pngbin127781 -> 46595 bytes
-rw-r--r--doc/user/project/issues/img/issue_board.pngbin56253 -> 55931 bytes
-rw-r--r--doc/user/project/issues/img/issue_template.pngbin25022 -> 25019 bytes
-rw-r--r--doc/user/project/issues/img/new_issue_from_email.pngbin13461 -> 4259 bytes
-rw-r--r--doc/user/project/issues/img/new_issue_from_issue_board.pngbin57427 -> 20063 bytes
-rw-r--r--doc/user/project/issues/img/new_issue_from_projects_dashboard.pngbin23685 -> 9674 bytes
-rw-r--r--doc/user/project/issues/img/project_issues_list_view.pngbin196071 -> 77189 bytes
-rw-r--r--doc/user/project/issues/img/sidebar_confidential_issue.pngbin10210 -> 4648 bytes
-rw-r--r--doc/user/project/issues/img/sidebar_move_issue.pngbin50132 -> 50003 bytes
-rw-r--r--doc/user/project/issues/img/sidebar_not_confidential_issue.pngbin8163 -> 4081 bytes
-rw-r--r--doc/user/project/issues/img/turn_off_confidentiality.pngbin27307 -> 10851 bytes
-rw-r--r--doc/user/project/issues/img/turn_on_confidentiality.pngbin33499 -> 13513 bytes
-rw-r--r--doc/user/project/issues/issues_functionalities.md11
-rw-r--r--doc/user/project/koding.md131
-rw-r--r--doc/user/project/members/img/access_requests_management.pngbin11018 -> 11005 bytes
-rw-r--r--doc/user/project/members/img/add_new_user_to_project_settings.pngbin11046 -> 11004 bytes
-rw-r--r--doc/user/project/members/img/add_user_email_accept.pngbin16890 -> 16878 bytes
-rw-r--r--doc/user/project/members/img/add_user_import_members_from_another_project.pngbin25343 -> 25333 bytes
-rw-r--r--doc/user/project/members/img/add_user_members_menu.pngbin28994 -> 28988 bytes
-rw-r--r--doc/user/project/members/img/max_access_level.pngbin34718 -> 34710 bytes
-rw-r--r--doc/user/project/members/img/request_access_button.pngbin25281 -> 25271 bytes
-rw-r--r--doc/user/project/members/img/withdraw_access_request_button.pngbin26135 -> 26123 bytes
-rw-r--r--doc/user/project/merge_requests/cherry_pick_changes.md8
-rw-r--r--doc/user/project/merge_requests/img/allow_collaboration.pngbin39513 -> 21522 bytes
-rw-r--r--doc/user/project/merge_requests/img/cherry_pick_changes_commit.pngbin13604 -> 13568 bytes
-rw-r--r--doc/user/project/merge_requests/img/cherry_pick_changes_mr.pngbin16494 -> 7214 bytes
-rw-r--r--doc/user/project/merge_requests/img/comment-on-any-diff-line.pngbin0 -> 177323 bytes
-rw-r--r--doc/user/project/merge_requests/img/create_from_email.pngbin152975 -> 55777 bytes
-rw-r--r--doc/user/project/merge_requests/img/filter_wip_merge_requests.pngbin0 -> 17346 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_conflict_editor.pngbin50422 -> 50286 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_request.pngbin748131 -> 386345 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_request_diff_file_navigation.pngbin244736 -> 68704 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_request_pipeline.pngbin0 -> 31046 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_request_widget.pngbin11039 -> 11036 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.pngbin22791 -> 10186 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.pngbin5251 -> 5237 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.pngbin25783 -> 6491 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.pngbin69953 -> 21397 bytes
-rw-r--r--doc/user/project/merge_requests/img/project_merge_requests_list_view.pngbin325819 -> 171866 bytes
-rw-r--r--doc/user/project/merge_requests/img/remove_source_branch_status.pngbin32649 -> 32586 bytes
-rw-r--r--doc/user/project/merge_requests/img/revert_changes_commit.pngbin95655 -> 95647 bytes
-rw-r--r--doc/user/project/merge_requests/img/revert_changes_mr.pngbin104972 -> 104954 bytes
-rw-r--r--doc/user/project/merge_requests/img/squash_edit_form.pngbin4232 -> 4231 bytes
-rw-r--r--doc/user/project/merge_requests/img/squash_mr_commits.pngbin85635 -> 31491 bytes
-rw-r--r--doc/user/project/merge_requests/img/squash_mr_widget.pngbin6496 -> 3573 bytes
-rw-r--r--doc/user/project/merge_requests/img/squash_squashed_commit.pngbin63371 -> 23726 bytes
-rw-r--r--doc/user/project/merge_requests/img/versions.pngbin23629 -> 22763 bytes
-rw-r--r--doc/user/project/merge_requests/img/versions_compare.pngbin17228 -> 17017 bytes
-rw-r--r--doc/user/project/merge_requests/img/versions_dropdown.pngbin13887 -> 13827 bytes
-rw-r--r--doc/user/project/merge_requests/img/versions_system_note.pngbin7136 -> 7126 bytes
-rw-r--r--doc/user/project/merge_requests/img/wip_blocked_accept_button.pngbin8071 -> 4152 bytes
-rw-r--r--doc/user/project/merge_requests/img/wip_mark_as_wip.pngbin17081 -> 7961 bytes
-rw-r--r--doc/user/project/merge_requests/img/wip_unmark_as_wip.pngbin18585 -> 8424 bytes
-rw-r--r--doc/user/project/merge_requests/index.md95
-rw-r--r--doc/user/project/merge_requests/versions.md14
-rw-r--r--doc/user/project/merge_requests/work_in_progress_merge_requests.md11
-rw-r--r--doc/user/project/milestones/img/milestones_new_group_milestone.pngbin276831 -> 144554 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_new_project_milestone.pngbin257285 -> 133541 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_project_milestone_page.pngbin489382 -> 270005 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_promote_milestone.pngbin76864 -> 49288 bytes
-rw-r--r--doc/user/project/milestones/index.md23
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md22
-rw-r--r--doc/user/project/pages/getting_started_part_three.md42
-rw-r--r--doc/user/project/pages/getting_started_part_two.md2
-rw-r--r--doc/user/project/pages/img/dns_add_new_a_record_example_updated.pngbin10578 -> 0 bytes
-rw-r--r--doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.pngbin0 -> 3701 bytes
-rw-r--r--doc/user/project/pages/img/icons/click.pngbin0 -> 10148 bytes
-rw-r--r--doc/user/project/pages/img/icons/cogs.pngbin0 -> 9670 bytes
-rw-r--r--doc/user/project/pages/img/icons/fork.pngbin0 -> 9597 bytes
-rw-r--r--doc/user/project/pages/img/icons/free.pngbin0 -> 8689 bytes
-rw-r--r--doc/user/project/pages/img/icons/lock.pngbin0 -> 7892 bytes
-rw-r--r--doc/user/project/pages/img/icons/monitor.pngbin0 -> 5039 bytes
-rw-r--r--doc/user/project/pages/img/icons/terminal.pngbin0 -> 4972 bytes
-rw-r--r--doc/user/project/pages/img/pages_create_project.pngbin6063 -> 6062 bytes
-rw-r--r--doc/user/project/pages/img/pages_dns_details.pngbin5351 -> 5350 bytes
-rw-r--r--doc/user/project/pages/img/pages_multiple_domains.pngbin12936 -> 12930 bytes
-rw-r--r--doc/user/project/pages/img/pages_remove.pngbin3810 -> 3777 bytes
-rw-r--r--doc/user/project/pages/img/pages_upload_cert.pngbin22907 -> 22888 bytes
-rw-r--r--doc/user/project/pages/img/ssgs_pages.pngbin0 -> 12010 bytes
-rw-r--r--doc/user/project/pages/img/verify_your_domain.pngbin30163 -> 12082 bytes
-rw-r--r--doc/user/project/pages/index.md194
-rw-r--r--doc/user/project/pipelines/img/job_artifacts_pipelines_page.pngbin16550 -> 16403 bytes
-rw-r--r--doc/user/project/pipelines/img/pipeline_schedule_play.pngbin39142 -> 11400 bytes
-rw-r--r--doc/user/project/pipelines/img/pipeline_schedule_variables.pngbin13478 -> 5360 bytes
-rw-r--r--doc/user/project/pipelines/img/pipeline_schedules_list.pngbin38034 -> 12948 bytes
-rw-r--r--doc/user/project/pipelines/img/pipeline_schedules_new_form.pngbin72501 -> 23290 bytes
-rw-r--r--doc/user/project/pipelines/img/pipeline_schedules_ownership.pngbin12043 -> 5004 bytes
-rw-r--r--doc/user/project/pipelines/job_artifacts.md54
-rw-r--r--doc/user/project/pipelines/schedules.md10
-rw-r--r--doc/user/project/pipelines/settings.md4
-rw-r--r--doc/user/project/protected_branches.md2
-rw-r--r--doc/user/project/protected_tags.md2
-rw-r--r--doc/user/project/quick_actions.md127
-rw-r--r--doc/user/project/repository/branches/img/branch_filter_search_box.pngbin0 -> 83225 bytes
-rw-r--r--doc/user/project/repository/branches/img/delete_merged_branches.pngbin42891 -> 42886 bytes
-rw-r--r--doc/user/project/repository/branches/index.md25
-rw-r--r--doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.pngbin24514 -> 13008 bytes
-rw-r--r--doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.pngbin4403 -> 4366 bytes
-rw-r--r--doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.pngbin113801 -> 42290 bytes
-rw-r--r--doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.pngbin12924 -> 7188 bytes
-rw-r--r--doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.pngbin20652 -> 9771 bytes
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md21
-rw-r--r--doc/user/project/repository/img/compare_branches.pngbin206831 -> 131046 bytes
-rw-r--r--doc/user/project/repository/img/repository_languages.pngbin0 -> 26516 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_new_branch_dropdown.pngbin10386 -> 10324 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_new_branch_from_issue.pngbin2720 -> 2715 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_new_branch_page.pngbin6034 -> 5886 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_new_directory_dialog.pngbin7323 -> 7157 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_new_directory_dropdown.pngbin9918 -> 9916 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_new_file_dropdown.pngbin10233 -> 10152 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_new_push_widget.pngbin3395 -> 3388 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_new_tag_dropdown.pngbin9796 -> 9706 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_start_new_merge_request.pngbin4060 -> 4049 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_template_dropdown_buttons.pngbin5634 -> 5629 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_template_dropdown_first_file.pngbin8846 -> 8844 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_upload_file_dialog.pngbin12558 -> 12553 bytes
-rw-r--r--doc/user/project/repository/img/web_editor_upload_file_dropdown.pngbin10291 -> 10200 bytes
-rw-r--r--doc/user/project/repository/index.md61
-rw-r--r--doc/user/project/repository/reducing_the_repo_size_using_git.md17
-rw-r--r--doc/user/project/repository/web_editor.md4
-rw-r--r--doc/user/project/settings/img/import_export_download_export.pngbin24482 -> 24397 bytes
-rw-r--r--doc/user/project/settings/img/import_export_export_button.pngbin24122 -> 24118 bytes
-rw-r--r--doc/user/project/settings/img/import_export_new_project.pngbin13083 -> 13082 bytes
-rw-r--r--doc/user/project/settings/img/import_export_select_file.pngbin13713 -> 13514 bytes
-rw-r--r--doc/user/project/settings/img/settings_edit_button.pngbin6901 -> 6897 bytes
-rw-r--r--doc/user/project/settings/img/sharing_and_permissions_settings.pngbin143341 -> 50602 bytes
-rw-r--r--doc/user/project/settings/index.md2
-rw-r--r--doc/user/project/web_ide/img/open_web_ide.pngbin28574 -> 28571 bytes
-rw-r--r--doc/user/project/web_ide/index.md9
-rw-r--r--doc/user/project/wiki/img/wiki_create_home_page.pngbin12422 -> 12421 bytes
-rw-r--r--doc/user/project/wiki/img/wiki_create_new_page.pngbin38105 -> 14957 bytes
-rw-r--r--doc/user/project/wiki/img/wiki_create_new_page_modal.pngbin13189 -> 5831 bytes
-rw-r--r--doc/user/project/wiki/img/wiki_move_page_1.pngbin54550 -> 17270 bytes
-rw-r--r--doc/user/project/wiki/img/wiki_move_page_2.pngbin33535 -> 10571 bytes
-rw-r--r--doc/user/project/wiki/img/wiki_page_history.pngbin26478 -> 12101 bytes
-rw-r--r--doc/user/project/wiki/img/wiki_sidebar.pngbin7440 -> 3178 bytes
-rw-r--r--doc/user/project/wiki/index.md13
-rw-r--r--doc/user/reserved_names.md7
-rw-r--r--doc/user/search/img/dashboard_links.pngbin0 -> 27164 bytes
-rw-r--r--doc/user/search/img/issue_search_by_term.pngbin127492 -> 35648 bytes
-rw-r--r--doc/user/search/img/issue_search_filter.pngbin69559 -> 27091 bytes
-rw-r--r--doc/user/search/img/issues_assigned_to_you.pngbin49079 -> 50433 bytes
-rw-r--r--doc/user/search/img/issues_filter_none_any.pngbin0 -> 27717 bytes
-rw-r--r--doc/user/search/img/issues_mrs_shortcut.pngbin61888 -> 26706 bytes
-rw-r--r--doc/user/search/img/left_menu_bar.pngbin37433 -> 0 bytes
-rw-r--r--doc/user/search/img/project_search.pngbin89002 -> 42139 bytes
-rw-r--r--doc/user/search/index.md26
-rw-r--r--doc/workflow/ci_mr.pngbin12034 -> 12024 bytes
-rw-r--r--doc/workflow/environment_branches.pngbin12364 -> 12354 bytes
-rw-r--r--doc/workflow/forking/branch_select.pngbin15424 -> 15410 bytes
-rw-r--r--doc/workflow/forking/merge_request.pngbin16332 -> 16329 bytes
-rw-r--r--doc/workflow/git_pull.pngbin28749 -> 28701 bytes
-rw-r--r--doc/workflow/gitlab_flow.pngbin47432 -> 47430 bytes
-rw-r--r--doc/workflow/good_commit.pngbin8742 -> 8740 bytes
-rw-r--r--doc/workflow/img/file_finder_find_button.pngbin14567 -> 14565 bytes
-rw-r--r--doc/workflow/img/forking_workflow_fork_button.pngbin12973 -> 12962 bytes
-rw-r--r--doc/workflow/img/forking_workflow_path_taken_error.pngbin10103 -> 10092 bytes
-rw-r--r--doc/workflow/img/notification_group_settings.pngbin171784 -> 54362 bytes
-rw-r--r--doc/workflow/img/notification_project_settings.pngbin167548 -> 58864 bytes
-rw-r--r--doc/workflow/img/repository_mirroring_force_update.pngbin0 -> 45730 bytes
-rw-r--r--doc/workflow/img/repository_mirroring_pull_settings_lower.pngbin0 -> 58056 bytes
-rw-r--r--doc/workflow/img/repository_mirroring_pull_settings_upper.pngbin0 -> 64118 bytes
-rw-r--r--doc/workflow/img/repository_mirroring_push_settings.pngbin0 -> 50526 bytes
-rw-r--r--doc/workflow/img/todo_list_item.pngbin18777 -> 18776 bytes
-rw-r--r--doc/workflow/img/todos_add_todo_sidebar.pngbin42360 -> 17524 bytes
-rw-r--r--doc/workflow/img/todos_mark_done_sidebar.pngbin42317 -> 17619 bytes
-rw-r--r--doc/workflow/lfs/lfs_administration.md5
-rw-r--r--doc/workflow/lfs/manage_large_binaries_with_git_lfs.md15
-rw-r--r--doc/workflow/merge_request.pngbin47240 -> 47225 bytes
-rw-r--r--doc/workflow/messy_flow.pngbin11665 -> 11663 bytes
-rw-r--r--doc/workflow/mr_inline_comments.pngbin52519 -> 52503 bytes
-rw-r--r--doc/workflow/notifications.md47
-rw-r--r--doc/workflow/production_branch.pngbin7264 -> 7262 bytes
-rw-r--r--doc/workflow/rebase.pngbin29009 -> 28939 bytes
-rw-r--r--doc/workflow/release_branches.pngbin12746 -> 12736 bytes
-rw-r--r--doc/workflow/releases/new_tag.pngbin42456 -> 42439 bytes
-rw-r--r--doc/workflow/repository_mirroring.md551
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_detect_host_keys.pngbin61463 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_diverged_branch.pngbin22668 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_diverged_branch_push.pngbin9512 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_github_edit_personal_access_token.pngbin20739 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository.pngbin16538 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository_update_now.pngbin16765 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_hard_failed_main.pngbin47943 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_hard_failed_settings.pngbin53279 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_new_project.pngbin20364 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_pull_advanced_host_keys.pngbin115796 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_pull_settings.pngbin100470 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_pull_settings_for_ssh.pngbin69467 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_push_settings.pngbin18226 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_ssh_host_keys_verified.pngbin23724 -> 0 bytes
-rw-r--r--doc/workflow/repository_mirroring/repository_mirroring_ssh_public_key_authentication.pngbin82456 -> 0 bytes
-rw-r--r--doc/workflow/shortcuts.md2
-rw-r--r--doc/workflow/time-tracking/time-tracking-example.pngbin48350 -> 14564 bytes
-rw-r--r--doc/workflow/time-tracking/time-tracking-sidebar.pngbin19467 -> 9068 bytes
-rw-r--r--doc/workflow/timezone.md1
-rw-r--r--doc/workflow/todos.md2
-rw-r--r--lib/after_commit_queue.rb2
-rw-r--r--lib/api/access_requests.rb8
-rw-r--r--lib/api/api.rb18
-rw-r--r--lib/api/api_guard.rb4
-rw-r--r--lib/api/applications.rb18
-rw-r--r--lib/api/avatar.rb2
-rw-r--r--lib/api/award_emoji.rb6
-rw-r--r--lib/api/badges.rb2
-rw-r--r--lib/api/boards.rb2
-rw-r--r--lib/api/boards_responses.rb4
-rw-r--r--lib/api/branches.rb24
-rw-r--r--lib/api/broadcast_messages.rb2
-rw-r--r--lib/api/circuit_breakers.rb19
-rw-r--r--lib/api/commit_statuses.rb6
-rw-r--r--lib/api/commits.rb80
-rw-r--r--lib/api/custom_attributes_endpoints.rb8
-rw-r--r--lib/api/deploy_keys.rb10
-rw-r--r--lib/api/deployments.rb4
-rw-r--r--lib/api/discussions.rb6
-rw-r--r--lib/api/entities.rb147
-rw-r--r--lib/api/environments.rb2
-rw-r--r--lib/api/events.rb37
-rw-r--r--lib/api/features.rb6
-rw-r--r--lib/api/files.rb14
-rw-r--r--lib/api/group_boards.rb2
-rw-r--r--lib/api/group_milestones.rb16
-rw-r--r--lib/api/group_variables.rb8
-rw-r--r--lib/api/groups.rb4
-rw-r--r--lib/api/helpers.rb41
-rw-r--r--lib/api/helpers/badges_helpers.rb2
-rw-r--r--lib/api/helpers/common_helpers.rb2
-rw-r--r--lib/api/helpers/custom_attributes.rb4
-rw-r--r--lib/api/helpers/custom_validators.rb15
-rw-r--r--lib/api/helpers/headers_helpers.rb2
-rw-r--r--lib/api/helpers/internal_helpers.rb2
-rw-r--r--lib/api/helpers/members_helpers.rb6
-rw-r--r--lib/api/helpers/notes_helpers.rb7
-rw-r--r--lib/api/helpers/pagination.rb6
-rw-r--r--lib/api/helpers/project_snapshots_helpers.rb2
-rw-r--r--lib/api/helpers/projects_helpers.rb3
-rw-r--r--lib/api/helpers/related_resources_helpers.rb2
-rw-r--r--lib/api/helpers/runner.rb2
-rw-r--r--lib/api/internal.rb83
-rw-r--r--lib/api/issues.rb69
-rw-r--r--lib/api/job_artifacts.rb4
-rw-r--r--lib/api/jobs.rb14
-rw-r--r--lib/api/keys.rb2
-rw-r--r--lib/api/labels.rb8
-rw-r--r--lib/api/lint.rb2
-rw-r--r--lib/api/markdown.rb9
-rw-r--r--lib/api/members.rb14
-rw-r--r--lib/api/merge_request_diffs.rb2
-rw-r--r--lib/api/merge_requests.rb14
-rw-r--r--lib/api/milestone_responses.rb2
-rw-r--r--lib/api/namespaces.rb2
-rw-r--r--lib/api/notes.rb4
-rw-r--r--lib/api/notification_settings.rb10
-rw-r--r--lib/api/pages_domains.rb6
-rw-r--r--lib/api/pagination_params.rb2
-rw-r--r--lib/api/pipeline_schedules.rb10
-rw-r--r--lib/api/pipelines.rb19
-rw-r--r--lib/api/project_export.rb10
-rw-r--r--lib/api/project_hooks.rb5
-rw-r--r--lib/api/project_import.rb2
-rw-r--r--lib/api/project_milestones.rb5
-rw-r--r--lib/api/project_snapshots.rb2
-rw-r--r--lib/api/project_snippets.rb14
-rw-r--r--lib/api/project_templates.rb59
-rw-r--r--lib/api/projects.rb23
-rw-r--r--lib/api/projects_relation_builder.rb4
-rw-r--r--lib/api/protected_branches.rb14
-rw-r--r--lib/api/protected_tags.rb87
-rw-r--r--lib/api/repositories.rb11
-rw-r--r--lib/api/resource_label_events.rb55
-rw-r--r--lib/api/runner.rb7
-rw-r--r--lib/api/runners.rb52
-rw-r--r--lib/api/scope.rb2
-rw-r--r--lib/api/search.rb2
-rw-r--r--lib/api/services.rb26
-rw-r--r--lib/api/settings.rb13
-rw-r--r--lib/api/sidekiq_metrics.rb2
-rw-r--r--lib/api/snippets.rb12
-rw-r--r--lib/api/submodules.rb47
-rw-r--r--lib/api/subscriptions.rb2
-rw-r--r--lib/api/system_hooks.rb4
-rw-r--r--lib/api/tags.rb2
-rw-r--r--lib/api/templates.rb54
-rw-r--r--lib/api/time_tracking_endpoints.rb2
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/api/triggers.rb6
-rw-r--r--lib/api/users.rb81
-rw-r--r--lib/api/validations/types/safe_file.rb15
-rw-r--r--lib/api/variables.rb8
-rw-r--r--lib/api/version.rb2
-rw-r--r--lib/api/wikis.rb33
-rw-r--r--lib/backup.rb2
-rw-r--r--lib/backup/artifacts.rb2
-rw-r--r--lib/backup/builds.rb2
-rw-r--r--lib/backup/database.rb2
-rw-r--r--lib/backup/files.rb2
-rw-r--r--lib/backup/helper.rb2
-rw-r--r--lib/backup/lfs.rb2
-rw-r--r--lib/backup/manager.rb5
-rw-r--r--lib/backup/pages.rb2
-rw-r--r--lib/backup/registry.rb2
-rw-r--r--lib/backup/repository.rb2
-rw-r--r--lib/backup/uploads.rb2
-rw-r--r--lib/banzai.rb9
-rw-r--r--lib/banzai/color_parser.rb2
-rw-r--r--lib/banzai/commit_renderer.rb2
-rw-r--r--lib/banzai/cross_project_reference.rb3
-rw-r--r--lib/banzai/filter.rb2
-rw-r--r--lib/banzai/filter/absolute_link_filter.rb1
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb4
-rw-r--r--lib/banzai/filter/epic_reference_filter.rb6
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb4
-rw-r--r--lib/banzai/filter/issuable_state_filter.rb11
-rw-r--r--lib/banzai/filter/label_reference_filter.rb2
-rw-r--r--lib/banzai/filter/markdown_engines/common_mark.rb2
-rw-r--r--lib/banzai/filter/markdown_engines/redcarpet.rb2
-rw-r--r--lib/banzai/filter/relative_link_filter.rb6
-rw-r--r--lib/banzai/filter/spaced_link_filter.rb98
-rw-r--r--lib/banzai/filter/wiki_link_filter.rb10
-rw-r--r--lib/banzai/filter/wiki_link_filter/rewriter.rb23
-rw-r--r--lib/banzai/filter_array.rb2
-rw-r--r--lib/banzai/issuable_extractor.rb43
-rw-r--r--lib/banzai/object_renderer.rb3
-rw-r--r--lib/banzai/pipeline.rb2
-rw-r--r--lib/banzai/pipeline/ascii_doc_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/atom_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/base_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/broadcast_message_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/combined_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/commit_description_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/description_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/email_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/full_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb3
-rw-r--r--lib/banzai/pipeline/label_pipeline.rb14
-rw-r--r--lib/banzai/pipeline/markup_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/note_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/plain_markdown_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/post_process_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/pre_process_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/relative_link_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/single_line_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/wiki_pipeline.rb2
-rw-r--r--lib/banzai/querying.rb2
-rw-r--r--lib/banzai/redactor.rb10
-rw-r--r--lib/banzai/reference_extractor.rb2
-rw-r--r--lib/banzai/reference_parser.rb2
-rw-r--r--lib/banzai/reference_parser/base_parser.rb8
-rw-r--r--lib/banzai/reference_parser/commit_parser.rb2
-rw-r--r--lib/banzai/reference_parser/commit_range_parser.rb2
-rw-r--r--lib/banzai/reference_parser/directly_addressed_user_parser.rb2
-rw-r--r--lib/banzai/reference_parser/epic_parser.rb2
-rw-r--r--lib/banzai/reference_parser/external_issue_parser.rb2
-rw-r--r--lib/banzai/reference_parser/issuable_parser.rb2
-rw-r--r--lib/banzai/reference_parser/issue_parser.rb2
-rw-r--r--lib/banzai/reference_parser/label_parser.rb2
-rw-r--r--lib/banzai/reference_parser/merge_request_parser.rb7
-rw-r--r--lib/banzai/reference_parser/milestone_parser.rb2
-rw-r--r--lib/banzai/reference_parser/snippet_parser.rb2
-rw-r--r--lib/banzai/reference_parser/user_parser.rb2
-rw-r--r--lib/banzai/renderer.rb2
-rw-r--r--lib/banzai/renderer/common_mark/html.rb16
-rw-r--r--lib/banzai/renderer/redcarpet/html.rb2
-rw-r--r--lib/banzai/request_store_reference_cache.rb6
-rw-r--r--lib/bitbucket/client.rb2
-rw-r--r--lib/bitbucket/collection.rb2
-rw-r--r--lib/bitbucket/connection.rb2
-rw-r--r--lib/bitbucket/error/unauthorized.rb2
-rw-r--r--lib/bitbucket/page.rb2
-rw-r--r--lib/bitbucket/paginator.rb2
-rw-r--r--lib/bitbucket/representation/base.rb2
-rw-r--r--lib/bitbucket/representation/comment.rb2
-rw-r--r--lib/bitbucket/representation/issue.rb2
-rw-r--r--lib/bitbucket/representation/pull_request.rb2
-rw-r--r--lib/bitbucket/representation/pull_request_comment.rb2
-rw-r--r--lib/bitbucket/representation/repo.rb2
-rw-r--r--lib/bitbucket/representation/user.rb2
-rw-r--r--lib/bitbucket_server/client.rb8
-rw-r--r--lib/bitbucket_server/collection.rb24
-rw-r--r--lib/bitbucket_server/connection.rb22
-rw-r--r--lib/bitbucket_server/paginator.rb27
-rw-r--r--lib/carrier_wave_string_file.rb2
-rw-r--r--lib/constraints/feature_constrainer.rb2
-rw-r--r--lib/constraints/group_url_constrainer.rb2
-rw-r--r--lib/constraints/project_url_constrainer.rb2
-rw-r--r--lib/constraints/user_url_constrainer.rb2
-rw-r--r--lib/container_registry/blob.rb2
-rw-r--r--lib/container_registry/client.rb2
-rw-r--r--lib/container_registry/config.rb2
-rw-r--r--lib/container_registry/path.rb6
-rw-r--r--lib/container_registry/registry.rb2
-rw-r--r--lib/container_registry/tag.rb4
-rw-r--r--lib/declarative_policy.rb4
-rw-r--r--lib/declarative_policy/base.rb2
-rw-r--r--lib/declarative_policy/cache.rb2
-rw-r--r--lib/declarative_policy/condition.rb2
-rw-r--r--lib/declarative_policy/delegate_dsl.rb2
-rw-r--r--lib/declarative_policy/policy_dsl.rb2
-rw-r--r--lib/declarative_policy/preferred_scope.rb5
-rw-r--r--lib/declarative_policy/rule.rb2
-rw-r--r--lib/declarative_policy/rule_dsl.rb2
-rw-r--r--lib/declarative_policy/runner.rb2
-rw-r--r--lib/declarative_policy/step.rb2
-rw-r--r--lib/event_filter.rb86
-rw-r--r--lib/expand_variables.rb2
-rw-r--r--lib/extracts_path.rb16
-rw-r--r--lib/feature.rb29
-rw-r--r--lib/file_size_validator.rb4
-rw-r--r--lib/flowdock/git.rb67
-rw-r--r--lib/flowdock/git/builder.rb145
-rw-r--r--lib/forever.rb2
-rw-r--r--lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb2
-rw-r--r--lib/gitaly/server.rb2
-rw-r--r--lib/gitlab.rb10
-rw-r--r--lib/gitlab/access.rb2
-rw-r--r--lib/gitlab/action_rate_limiter.rb2
-rw-r--r--lib/gitlab/allowable.rb2
-rw-r--r--lib/gitlab/app_logger.rb2
-rw-r--r--lib/gitlab/asciidoc.rb2
-rw-r--r--lib/gitlab/audit_json_logger.rb9
-rw-r--r--lib/gitlab/auth.rb8
-rw-r--r--lib/gitlab/auth/activity.rb2
-rw-r--r--lib/gitlab/auth/database/authentication.rb2
-rw-r--r--lib/gitlab/auth/ip_rate_limiter.rb2
-rw-r--r--lib/gitlab/auth/ldap/access.rb32
-rw-r--r--lib/gitlab/auth/ldap/adapter.rb2
-rw-r--r--lib/gitlab/auth/ldap/auth_hash.rb2
-rw-r--r--lib/gitlab/auth/ldap/authentication.rb2
-rw-r--r--lib/gitlab/auth/ldap/config.rb2
-rw-r--r--lib/gitlab/auth/ldap/dn.rb1
-rw-r--r--lib/gitlab/auth/ldap/ldap_connection_error.rb2
-rw-r--r--lib/gitlab/auth/ldap/person.rb2
-rw-r--r--lib/gitlab/auth/ldap/user.rb4
-rw-r--r--lib/gitlab/auth/o_auth/auth_hash.rb4
-rw-r--r--lib/gitlab/auth/o_auth/authentication.rb2
-rw-r--r--lib/gitlab/auth/o_auth/identity_linker.rb2
-rw-r--r--lib/gitlab/auth/o_auth/provider.rb3
-rw-r--r--lib/gitlab/auth/o_auth/session.rb2
-rw-r--r--lib/gitlab/auth/o_auth/user.rb6
-rw-r--r--lib/gitlab/auth/omniauth_identity_linker_base.rb4
-rw-r--r--lib/gitlab/auth/request_authenticator.rb2
-rw-r--r--lib/gitlab/auth/result.rb5
-rw-r--r--lib/gitlab/auth/saml/auth_hash.rb4
-rw-r--r--lib/gitlab/auth/saml/config.rb2
-rw-r--r--lib/gitlab/auth/saml/identity_linker.rb2
-rw-r--r--lib/gitlab/auth/saml/user.rb2
-rw-r--r--lib/gitlab/auth/too_many_ips.rb2
-rw-r--r--lib/gitlab/auth/unique_ips_limiter.rb2
-rw-r--r--lib/gitlab/auth/user_access_denied_reason.rb2
-rw-r--r--lib/gitlab/auth/user_auth_finders.rb4
-rw-r--r--lib/gitlab/background_migration.rb2
-rw-r--r--lib/gitlab/background_migration/digest_column.rb25
-rw-r--r--lib/gitlab/background_migration/encrypt_columns.rb80
-rw-r--r--lib/gitlab/background_migration/migrate_legacy_artifacts.rb126
-rw-r--r--lib/gitlab/background_migration/models/encrypt_columns/web_hook.rb28
-rw-r--r--lib/gitlab/background_migration/populate_cluster_kubernetes_namespace_table.rb82
-rw-r--r--lib/gitlab/background_migration/populate_external_pipeline_source.rb50
-rw-r--r--lib/gitlab/background_migration/redact_links.rb51
-rw-r--r--lib/gitlab/background_migration/redact_links/redactable.rb21
-rw-r--r--lib/gitlab/background_migration/remove_restricted_todos.rb86
-rw-r--r--lib/gitlab/background_migration/set_confidential_note_events_on_services.rb4
-rw-r--r--lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb4
-rw-r--r--lib/gitlab/badge/base.rb2
-rw-r--r--lib/gitlab/badge/coverage/metadata.rb2
-rw-r--r--lib/gitlab/badge/coverage/report.rb4
-rw-r--r--lib/gitlab/badge/coverage/template.rb2
-rw-r--r--lib/gitlab/badge/metadata.rb2
-rw-r--r--lib/gitlab/badge/pipeline/metadata.rb2
-rw-r--r--lib/gitlab/badge/pipeline/status.rb4
-rw-r--r--lib/gitlab/badge/pipeline/template.rb2
-rw-r--r--lib/gitlab/badge/template.rb2
-rw-r--r--lib/gitlab/bare_repository_import/importer.rb7
-rw-r--r--lib/gitlab/bare_repository_import/repository.rb7
-rw-r--r--lib/gitlab/base_doorkeeper_controller.rb2
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb6
-rw-r--r--lib/gitlab/bitbucket_import/project_creator.rb2
-rw-r--r--lib/gitlab/bitbucket_server_import/importer.rb83
-rw-r--r--lib/gitlab/bitbucket_server_import/project_creator.rb2
-rw-r--r--lib/gitlab/blame.rb5
-rw-r--r--lib/gitlab/blob_helper.rb147
-rw-r--r--lib/gitlab/build_access.rb2
-rw-r--r--lib/gitlab/cache/ci/project_pipeline_status.rb10
-rw-r--r--lib/gitlab/cache/request_cache.rb6
-rw-r--r--lib/gitlab/changes_list.rb2
-rw-r--r--lib/gitlab/chat_name_token.rb2
-rw-r--r--lib/gitlab/checks/change_access.rb112
-rw-r--r--lib/gitlab/checks/commit_check.rb8
-rw-r--r--lib/gitlab/checks/force_push.rb2
-rw-r--r--lib/gitlab/checks/lfs_integrity.rb9
-rw-r--r--lib/gitlab/checks/matching_merge_request.rb4
-rw-r--r--lib/gitlab/checks/post_push_message.rb2
-rw-r--r--lib/gitlab/checks/project_created.rb2
-rw-r--r--lib/gitlab/checks/project_moved.rb2
-rw-r--r--lib/gitlab/checks/timed_logger.rb83
-rw-r--r--lib/gitlab/ci/ansi2html.rb4
-rw-r--r--lib/gitlab/ci/build/artifacts/adapters/gzip_stream.rb50
-rw-r--r--lib/gitlab/ci/build/artifacts/adapters/raw_stream.rb29
-rw-r--r--lib/gitlab/ci/build/artifacts/gzip_file_adapter.rb46
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata.rb9
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata/entry.rb4
-rw-r--r--lib/gitlab/ci/build/artifacts/path.rb2
-rw-r--r--lib/gitlab/ci/build/credentials/base.rb2
-rw-r--r--lib/gitlab/ci/build/credentials/factory.rb2
-rw-r--r--lib/gitlab/ci/build/credentials/registry.rb2
-rw-r--r--lib/gitlab/ci/build/image.rb2
-rw-r--r--lib/gitlab/ci/build/policy.rb2
-rw-r--r--lib/gitlab/ci/build/policy/changes.rb25
-rw-r--r--lib/gitlab/ci/build/policy/kubernetes.rb2
-rw-r--r--lib/gitlab/ci/build/policy/refs.rb2
-rw-r--r--lib/gitlab/ci/build/policy/specification.rb2
-rw-r--r--lib/gitlab/ci/build/policy/variables.rb2
-rw-r--r--lib/gitlab/ci/build/step.rb2
-rw-r--r--lib/gitlab/ci/charts.rb8
-rw-r--r--lib/gitlab/ci/config.rb33
-rw-r--r--lib/gitlab/ci/config/entry/artifacts.rb2
-rw-r--r--lib/gitlab/ci/config/entry/attributable.rb2
-rw-r--r--lib/gitlab/ci/config/entry/boolean.rb2
-rw-r--r--lib/gitlab/ci/config/entry/cache.rb2
-rw-r--r--lib/gitlab/ci/config/entry/commands.rb2
-rw-r--r--lib/gitlab/ci/config/entry/configurable.rb6
-rw-r--r--lib/gitlab/ci/config/entry/coverage.rb2
-rw-r--r--lib/gitlab/ci/config/entry/environment.rb2
-rw-r--r--lib/gitlab/ci/config/entry/factory.rb2
-rw-r--r--lib/gitlab/ci/config/entry/global.rb4
-rw-r--r--lib/gitlab/ci/config/entry/hidden.rb2
-rw-r--r--lib/gitlab/ci/config/entry/image.rb2
-rw-r--r--lib/gitlab/ci/config/entry/job.rb44
-rw-r--r--lib/gitlab/ci/config/entry/jobs.rb4
-rw-r--r--lib/gitlab/ci/config/entry/key.rb2
-rw-r--r--lib/gitlab/ci/config/entry/legacy_validation_helpers.rb11
-rw-r--r--lib/gitlab/ci/config/entry/node.rb2
-rw-r--r--lib/gitlab/ci/config/entry/paths.rb2
-rw-r--r--lib/gitlab/ci/config/entry/policy.rb8
-rw-r--r--lib/gitlab/ci/config/entry/reports.rb11
-rw-r--r--lib/gitlab/ci/config/entry/retry.rb90
-rw-r--r--lib/gitlab/ci/config/entry/script.rb2
-rw-r--r--lib/gitlab/ci/config/entry/service.rb2
-rw-r--r--lib/gitlab/ci/config/entry/services.rb2
-rw-r--r--lib/gitlab/ci/config/entry/simplifiable.rb2
-rw-r--r--lib/gitlab/ci/config/entry/stage.rb2
-rw-r--r--lib/gitlab/ci/config/entry/stages.rb2
-rw-r--r--lib/gitlab/ci/config/entry/undefined.rb2
-rw-r--r--lib/gitlab/ci/config/entry/unspecified.rb2
-rw-r--r--lib/gitlab/ci/config/entry/validatable.rb2
-rw-r--r--lib/gitlab/ci/config/entry/validator.rb2
-rw-r--r--lib/gitlab/ci/config/entry/validators.rb32
-rw-r--r--lib/gitlab/ci/config/entry/variables.rb2
-rw-r--r--lib/gitlab/ci/config/extendable.rb29
-rw-r--r--lib/gitlab/ci/config/extendable/entry.rb95
-rw-r--r--lib/gitlab/ci/config/external/file/base.rb75
-rw-r--r--lib/gitlab/ci/config/external/file/local.rb42
-rw-r--r--lib/gitlab/ci/config/external/file/remote.rb49
-rw-r--r--lib/gitlab/ci/config/external/mapper.rb33
-rw-r--r--lib/gitlab/ci/config/external/processor.rb50
-rw-r--r--lib/gitlab/ci/config/loader.rb2
-rw-r--r--lib/gitlab/ci/config/normalizer.rb65
-rw-r--r--lib/gitlab/ci/cron_parser.rb2
-rw-r--r--lib/gitlab/ci/mask_secret.rb4
-rw-r--r--lib/gitlab/ci/model.rb2
-rw-r--r--lib/gitlab/ci/parsers.rb9
-rw-r--r--lib/gitlab/ci/parsers/junit.rb69
-rw-r--r--lib/gitlab/ci/parsers/test.rb21
-rw-r--r--lib/gitlab/ci/parsers/test/junit.rb70
-rw-r--r--lib/gitlab/ci/pipeline/chain/base.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/build.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb5
-rw-r--r--lib/gitlab/ci/pipeline/chain/create.rb17
-rw-r--r--lib/gitlab/ci/pipeline/chain/helpers.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/populate.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/sequence.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/skip.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/abilities.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/config.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/repository.rb2
-rw-r--r--lib/gitlab/ci/pipeline/duration.rb6
-rw-r--r--lib/gitlab/ci/pipeline/expression.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/base.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/equals.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/matches.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/null.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/operator.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/string.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/value.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/variable.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexer.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/parser.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/statement.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/token.rb2
-rw-r--r--lib/gitlab/ci/pipeline/seed/base.rb2
-rw-r--r--lib/gitlab/ci/pipeline/seed/build.rb2
-rw-r--r--lib/gitlab/ci/pipeline/seed/stage.rb2
-rw-r--r--lib/gitlab/ci/reports/test_case.rb2
-rw-r--r--lib/gitlab/ci/reports/test_reports.rb8
-rw-r--r--lib/gitlab/ci/reports/test_reports_comparer.rb4
-rw-r--r--lib/gitlab/ci/reports/test_suite.rb4
-rw-r--r--lib/gitlab/ci/reports/test_suite_comparer.rb2
-rw-r--r--lib/gitlab/ci/status/build/action.rb2
-rw-r--r--lib/gitlab/ci/status/build/cancelable.rb2
-rw-r--r--lib/gitlab/ci/status/build/canceled.rb2
-rw-r--r--lib/gitlab/ci/status/build/common.rb2
-rw-r--r--lib/gitlab/ci/status/build/created.rb2
-rw-r--r--lib/gitlab/ci/status/build/erased.rb2
-rw-r--r--lib/gitlab/ci/status/build/factory.rb4
-rw-r--r--lib/gitlab/ci/status/build/failed.rb17
-rw-r--r--lib/gitlab/ci/status/build/failed_allowed.rb2
-rw-r--r--lib/gitlab/ci/status/build/manual.rb2
-rw-r--r--lib/gitlab/ci/status/build/pending.rb2
-rw-r--r--lib/gitlab/ci/status/build/play.rb2
-rw-r--r--lib/gitlab/ci/status/build/retried.rb2
-rw-r--r--lib/gitlab/ci/status/build/retryable.rb2
-rw-r--r--lib/gitlab/ci/status/build/scheduled.rb31
-rw-r--r--lib/gitlab/ci/status/build/skipped.rb2
-rw-r--r--lib/gitlab/ci/status/build/stop.rb2
-rw-r--r--lib/gitlab/ci/status/build/unschedule.rb43
-rw-r--r--lib/gitlab/ci/status/canceled.rb2
-rw-r--r--lib/gitlab/ci/status/core.rb2
-rw-r--r--lib/gitlab/ci/status/created.rb2
-rw-r--r--lib/gitlab/ci/status/extended.rb2
-rw-r--r--lib/gitlab/ci/status/external/common.rb2
-rw-r--r--lib/gitlab/ci/status/external/factory.rb2
-rw-r--r--lib/gitlab/ci/status/factory.rb2
-rw-r--r--lib/gitlab/ci/status/failed.rb2
-rw-r--r--lib/gitlab/ci/status/group/common.rb2
-rw-r--r--lib/gitlab/ci/status/group/factory.rb2
-rw-r--r--lib/gitlab/ci/status/manual.rb2
-rw-r--r--lib/gitlab/ci/status/pending.rb2
-rw-r--r--lib/gitlab/ci/status/pipeline/blocked.rb2
-rw-r--r--lib/gitlab/ci/status/pipeline/common.rb2
-rw-r--r--lib/gitlab/ci/status/pipeline/delayed.rb23
-rw-r--r--lib/gitlab/ci/status/pipeline/factory.rb3
-rw-r--r--lib/gitlab/ci/status/running.rb2
-rw-r--r--lib/gitlab/ci/status/scheduled.rb25
-rw-r--r--lib/gitlab/ci/status/skipped.rb2
-rw-r--r--lib/gitlab/ci/status/stage/common.rb2
-rw-r--r--lib/gitlab/ci/status/stage/factory.rb2
-rw-r--r--lib/gitlab/ci/status/success.rb2
-rw-r--r--lib/gitlab/ci/status/success_warning.rb2
-rw-r--r--lib/gitlab/ci/templates/Android.gitlab-ci.yml45
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml930
-rw-r--r--lib/gitlab/ci/templates/Bash.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Bash.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/C++.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/C++.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Chef.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Chef.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Clojure.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Clojure.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Crystal.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Django.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Django.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Docker.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Docker.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Elixir.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Elixir.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Go.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Go.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Gradle.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Gradle.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Grails.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Grails.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Julia.gitlab-ci.yml76
-rw-r--r--lib/gitlab/ci/templates/LaTeX.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/LaTeX.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Laravel.gitlab-ci.yml85
-rw-r--r--lib/gitlab/ci/templates/Maven.gitlab-ci.yml102
-rw-r--r--lib/gitlab/ci/templates/Mono.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Mono.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Nodejs.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/PHP.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/PHP.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Packer.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Packer.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Brunch.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Doxygen.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Gatsby.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/HTML.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Harp.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Hexo.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Hugo.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Hyde.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/JBake.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml42
-rw-r--r--lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Lektor.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Metalsmith.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Middleman.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Nanoc.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Octopress.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Pages/Pelican.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Pages/Pelican.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Python.gitlab-ci.yml51
-rw-r--r--lib/gitlab/ci/templates/Ruby.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Rust.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Rust.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Scala.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Scala.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/Swift.gitlab-ci.yml30
-rw-r--r--lib/gitlab/ci/templates/Terraform.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/Terraform.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/templates/dotNET.gitlab-ci.yml (renamed from vendor/gitlab-ci-yml/dotNET.gitlab-ci.yml)0
-rw-r--r--lib/gitlab/ci/trace.rb2
-rw-r--r--lib/gitlab/ci/trace/chunked_io.rb23
-rw-r--r--lib/gitlab/ci/trace/section_parser.rb2
-rw-r--r--lib/gitlab/ci/trace/stream.rb5
-rw-r--r--lib/gitlab/ci/variables/collection.rb2
-rw-r--r--lib/gitlab/ci/variables/collection/item.rb6
-rw-r--r--lib/gitlab/ci/yaml_processor.rb11
-rw-r--r--lib/gitlab/ci_access.rb2
-rw-r--r--lib/gitlab/cleanup/project_uploads.rb6
-rw-r--r--lib/gitlab/cleanup/remote_uploads.rb2
-rw-r--r--lib/gitlab/closing_issue_extractor.rb2
-rw-r--r--lib/gitlab/cluster/lifecycle_events.rb99
-rw-r--r--lib/gitlab/cluster/puma_worker_killer_initializer.rb36
-rw-r--r--lib/gitlab/color_schemes.rb2
-rw-r--r--lib/gitlab/config_helper.rb2
-rw-r--r--lib/gitlab/conflict/file.rb39
-rw-r--r--lib/gitlab/conflict/file_collection.rb2
-rw-r--r--lib/gitlab/contributions_calendar.rb27
-rw-r--r--lib/gitlab/contributor.rb2
-rw-r--r--lib/gitlab/cross_project_access.rb2
-rw-r--r--lib/gitlab/cross_project_access/check_collection.rb2
-rw-r--r--lib/gitlab/cross_project_access/check_info.rb2
-rw-r--r--lib/gitlab/cross_project_access/class_methods.rb2
-rw-r--r--lib/gitlab/crypto_helper.rb30
-rw-r--r--lib/gitlab/current_settings.rb12
-rw-r--r--lib/gitlab/cycle_analytics/base_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/base_query.rb2
-rw-r--r--lib/gitlab/cycle_analytics/base_stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/code_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/code_stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/issue_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/issue_stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/metrics_tables.rb2
-rw-r--r--lib/gitlab/cycle_analytics/permissions.rb2
-rw-r--r--lib/gitlab/cycle_analytics/plan_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/plan_stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/production_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/production_helper.rb2
-rw-r--r--lib/gitlab/cycle_analytics/production_stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/review_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/review_stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/stage_summary.rb2
-rw-r--r--lib/gitlab/cycle_analytics/staging_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/staging_stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/summary/base.rb2
-rw-r--r--lib/gitlab/cycle_analytics/summary/commit.rb2
-rw-r--r--lib/gitlab/cycle_analytics/summary/deploy.rb2
-rw-r--r--lib/gitlab/cycle_analytics/summary/issue.rb2
-rw-r--r--lib/gitlab/cycle_analytics/test_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/test_stage.rb2
-rw-r--r--lib/gitlab/cycle_analytics/updater.rb2
-rw-r--r--lib/gitlab/cycle_analytics/usage_data.rb2
-rw-r--r--lib/gitlab/daemon.rb2
-rw-r--r--lib/gitlab/data_builder/build.rb3
-rw-r--r--lib/gitlab/data_builder/note.rb2
-rw-r--r--lib/gitlab/data_builder/pipeline.rb5
-rw-r--r--lib/gitlab/data_builder/push.rb10
-rw-r--r--lib/gitlab/data_builder/repository.rb2
-rw-r--r--lib/gitlab/data_builder/wiki_page.rb2
-rw-r--r--lib/gitlab/database.rb28
-rw-r--r--lib/gitlab/database/arel_methods.rb2
-rw-r--r--lib/gitlab/database/count.rb2
-rw-r--r--lib/gitlab/database/date_time.rb2
-rw-r--r--lib/gitlab/database/grant.rb8
-rw-r--r--lib/gitlab/database/median.rb2
-rw-r--r--lib/gitlab/database/migration_helpers.rb113
-rw-r--r--lib/gitlab/database/multi_threaded_migration.rb2
-rw-r--r--lib/gitlab/database/read_only_relation.rb2
-rw-r--r--lib/gitlab/database/rename_reserved_paths_migration/v1.rb2
-rw-r--r--lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb2
-rw-r--r--lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb2
-rw-r--r--lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb2
-rw-r--r--lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb2
-rw-r--r--lib/gitlab/database/sha_attribute.rb2
-rw-r--r--lib/gitlab/database/subquery.rb24
-rw-r--r--lib/gitlab/dependency_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/base_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/cartfile_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/cocoapods.rb2
-rw-r--r--lib/gitlab/dependency_linker/composer_json_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/gemfile_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/gemspec_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/godeps_json_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/json_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/method_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/package_json_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/podfile_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/podspec_json_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/podspec_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/requirements_txt_linker.rb2
-rw-r--r--lib/gitlab/diff/diff_refs.rb2
-rw-r--r--lib/gitlab/diff/file.rb32
-rw-r--r--lib/gitlab/diff/file_collection/base.rb44
-rw-r--r--lib/gitlab/diff/file_collection/commit.rb2
-rw-r--r--lib/gitlab/diff/file_collection/compare.rb2
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff.rb65
-rw-r--r--lib/gitlab/diff/formatters/base_formatter.rb2
-rw-r--r--lib/gitlab/diff/formatters/image_formatter.rb2
-rw-r--r--lib/gitlab/diff/formatters/text_formatter.rb2
-rw-r--r--lib/gitlab/diff/highlight.rb8
-rw-r--r--lib/gitlab/diff/highlight_cache.rb68
-rw-r--r--lib/gitlab/diff/image_point.rb2
-rw-r--r--lib/gitlab/diff/inline_diff.rb6
-rw-r--r--lib/gitlab/diff/inline_diff_markdown_marker.rb2
-rw-r--r--lib/gitlab/diff/inline_diff_marker.rb2
-rw-r--r--lib/gitlab/diff/line.rb32
-rw-r--r--lib/gitlab/diff/line_mapper.rb2
-rw-r--r--lib/gitlab/diff/lines_unfolder.rb235
-rw-r--r--lib/gitlab/diff/parallel_diff.rb2
-rw-r--r--lib/gitlab/diff/parser.rb2
-rw-r--r--lib/gitlab/diff/position.rb42
-rw-r--r--lib/gitlab/diff/position_tracer.rb4
-rw-r--r--lib/gitlab/downtime_check.rb2
-rw-r--r--lib/gitlab/downtime_check/message.rb8
-rw-r--r--lib/gitlab/ee_compat_check.rb12
-rw-r--r--lib/gitlab/email/attachment_uploader.rb2
-rw-r--r--lib/gitlab/email/handler.rb25
-rw-r--r--lib/gitlab/email/handler/base_handler.rb2
-rw-r--r--lib/gitlab/email/handler/create_issue_handler.rb4
-rw-r--r--lib/gitlab/email/handler/create_merge_request_handler.rb54
-rw-r--r--lib/gitlab/email/handler/create_note_handler.rb2
-rw-r--r--lib/gitlab/email/handler/reply_processing.rb2
-rw-r--r--lib/gitlab/email/handler/unsubscribe_handler.rb2
-rw-r--r--lib/gitlab/email/hook/additional_headers_interceptor.rb2
-rw-r--r--lib/gitlab/email/hook/delivery_metrics_observer.rb2
-rw-r--r--lib/gitlab/email/hook/disable_email_interceptor.rb2
-rw-r--r--lib/gitlab/email/hook/email_template_interceptor.rb2
-rw-r--r--lib/gitlab/email/html_parser.rb2
-rw-r--r--lib/gitlab/email/message/repository_push.rb6
-rw-r--r--lib/gitlab/email/receiver.rb3
-rw-r--r--lib/gitlab/email/reply_parser.rb2
-rw-r--r--lib/gitlab/emoji.rb2
-rw-r--r--lib/gitlab/encoding_helper.rb4
-rw-r--r--lib/gitlab/environment.rb2
-rw-r--r--lib/gitlab/environment_logger.rb2
-rw-r--r--lib/gitlab/etag_caching/middleware.rb2
-rw-r--r--lib/gitlab/etag_caching/router.rb2
-rw-r--r--lib/gitlab/etag_caching/store.rb2
-rw-r--r--lib/gitlab/exclusive_lease.rb2
-rw-r--r--lib/gitlab/exclusive_lease_helpers.rb2
-rw-r--r--lib/gitlab/fake_application_settings.rb26
-rw-r--r--lib/gitlab/favicon.rb4
-rw-r--r--lib/gitlab/file_detector.rb7
-rw-r--r--lib/gitlab/file_finder.rb2
-rw-r--r--lib/gitlab/file_markdown_link_builder.rb23
-rw-r--r--lib/gitlab/file_type_detection.rb43
-rw-r--r--lib/gitlab/fogbugz_import/client.rb2
-rw-r--r--lib/gitlab/fogbugz_import/importer.rb6
-rw-r--r--lib/gitlab/fogbugz_import/project_creator.rb2
-rw-r--r--lib/gitlab/fogbugz_import/repository.rb2
-rw-r--r--lib/gitlab/gfm/reference_rewriter.rb24
-rw-r--r--lib/gitlab/gfm/uploads_rewriter.rb7
-rw-r--r--lib/gitlab/git.rb2
-rw-r--r--lib/gitlab/git/attributes_at_ref_parser.rb2
-rw-r--r--lib/gitlab/git/attributes_parser.rb2
-rw-r--r--lib/gitlab/git/blame.rb2
-rw-r--r--lib/gitlab/git/blob.rb13
-rw-r--r--lib/gitlab/git/blob_snippet.rb34
-rw-r--r--lib/gitlab/git/branch.rb2
-rw-r--r--lib/gitlab/git/commit.rb23
-rw-r--r--lib/gitlab/git/commit_stats.rb6
-rw-r--r--lib/gitlab/git/committer_with_hooks.rb47
-rw-r--r--lib/gitlab/git/compare.rb2
-rw-r--r--lib/gitlab/git/conflict/file.rb2
-rw-r--r--lib/gitlab/git/conflict/parser.rb2
-rw-r--r--lib/gitlab/git/conflict/resolution.rb2
-rw-r--r--lib/gitlab/git/conflict/resolver.rb8
-rw-r--r--lib/gitlab/git/diff.rb139
-rw-r--r--lib/gitlab/git/diff_collection.rb7
-rw-r--r--lib/gitlab/git/diff_stats_collection.rb34
-rw-r--r--lib/gitlab/git/gitlab_projects.rb253
-rw-r--r--lib/gitlab/git/gitmodules_parser.rb2
-rw-r--r--lib/gitlab/git/hook.rb108
-rw-r--r--lib/gitlab/git/hook_env.rb12
-rw-r--r--lib/gitlab/git/hooks_service.rb35
-rw-r--r--lib/gitlab/git/index.rb150
-rw-r--r--lib/gitlab/git/lfs_changes.rb6
-rw-r--r--lib/gitlab/git/lfs_pointer_file.rb2
-rw-r--r--lib/gitlab/git/operation_service.rb175
-rw-r--r--lib/gitlab/git/patches/collection.rb33
-rw-r--r--lib/gitlab/git/patches/commit_patches.rb31
-rw-r--r--lib/gitlab/git/patches/patch.rb19
-rw-r--r--lib/gitlab/git/path_helper.rb2
-rw-r--r--lib/gitlab/git/popen.rb112
-rw-r--r--lib/gitlab/git/pre_receive_error.rb2
-rw-r--r--lib/gitlab/git/push.rb56
-rw-r--r--lib/gitlab/git/raw_diff_change.rb2
-rw-r--r--lib/gitlab/git/ref.rb9
-rw-r--r--lib/gitlab/git/remote_mirror.rb6
-rw-r--r--lib/gitlab/git/remote_repository.rb2
-rw-r--r--lib/gitlab/git/repository.rb356
-rw-r--r--lib/gitlab/git/repository_mirroring.rb31
-rw-r--r--lib/gitlab/git/storage.rb25
-rw-r--r--lib/gitlab/git/storage/checker.rb120
-rw-r--r--lib/gitlab/git/storage/circuit_breaker.rb78
-rw-r--r--lib/gitlab/git/storage/circuit_breaker_settings.rb37
-rw-r--r--lib/gitlab/git/storage/failure_info.rb39
-rw-r--r--lib/gitlab/git/storage/forked_storage_check.rb65
-rw-r--r--lib/gitlab/git/storage/health.rb90
-rw-r--r--lib/gitlab/git/storage/null_circuit_breaker.rb50
-rw-r--r--lib/gitlab/git/tag.rb2
-rw-r--r--lib/gitlab/git/tree.rb50
-rw-r--r--lib/gitlab/git/user.rb4
-rw-r--r--lib/gitlab/git/util.rb2
-rw-r--r--lib/gitlab/git/version.rb4
-rw-r--r--lib/gitlab/git/wiki.rb110
-rw-r--r--lib/gitlab/git/wiki_file.rb19
-rw-r--r--lib/gitlab/git/wiki_page.rb30
-rw-r--r--lib/gitlab/git/wiki_page_version.rb7
-rw-r--r--lib/gitlab/git/wraps_gitaly_errors.rb17
-rw-r--r--lib/gitlab/git_access.rb38
-rw-r--r--lib/gitlab/git_access_result/custom_action.rb25
-rw-r--r--lib/gitlab/git_access_result/success.rb8
-rw-r--r--lib/gitlab/git_access_wiki.rb2
-rw-r--r--lib/gitlab/git_logger.rb2
-rw-r--r--lib/gitlab/git_post_receive.rb4
-rw-r--r--lib/gitlab/git_ref_validator.rb2
-rw-r--r--lib/gitlab/gitaly_client.rb84
-rw-r--r--lib/gitlab/gitaly_client/attributes_bag.rb2
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb23
-rw-r--r--lib/gitlab/gitaly_client/blobs_stitcher.rb2
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb62
-rw-r--r--lib/gitlab/gitaly_client/conflict_files_stitcher.rb4
-rw-r--r--lib/gitlab/gitaly_client/conflicts_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/diff.rb4
-rw-r--r--lib/gitlab/gitaly_client/diff_stitcher.rb4
-rw-r--r--lib/gitlab/gitaly_client/health_check_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/namespace_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/notification_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb54
-rw-r--r--lib/gitlab/gitaly_client/queue_enumerator.rb2
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb4
-rw-r--r--lib/gitlab/gitaly_client/remote_service.rb16
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb8
-rw-r--r--lib/gitlab/gitaly_client/server_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/storage_service.rb10
-rw-r--r--lib/gitlab/gitaly_client/storage_settings.rb4
-rw-r--r--lib/gitlab/gitaly_client/util.rb2
-rw-r--r--lib/gitlab/gitaly_client/wiki_file.rb2
-rw-r--r--lib/gitlab/gitaly_client/wiki_page.rb2
-rw-r--r--lib/gitlab/gitaly_client/wiki_service.rb8
-rw-r--r--lib/gitlab/github_import.rb20
-rw-r--r--lib/gitlab/github_import/bulk_importing.rb4
-rw-r--r--lib/gitlab/github_import/importer/diff_note_importer.rb2
-rw-r--r--lib/gitlab/github_import/importer/issue_importer.rb10
-rw-r--r--lib/gitlab/github_import/importer/labels_importer.rb2
-rw-r--r--lib/gitlab/github_import/importer/milestones_importer.rb14
-rw-r--r--lib/gitlab/github_import/importer/note_importer.rb2
-rw-r--r--lib/gitlab/github_import/importer/pull_request_importer.rb88
-rw-r--r--lib/gitlab/github_import/importer/releases_importer.rb2
-rw-r--r--lib/gitlab/github_import/importer/repository_importer.rb2
-rw-r--r--lib/gitlab/github_import/label_finder.rb2
-rw-r--r--lib/gitlab/github_import/milestone_finder.rb2
-rw-r--r--lib/gitlab/github_import/representation/expose_attribute.rb2
-rw-r--r--lib/gitlab/github_import/user_finder.rb4
-rw-r--r--lib/gitlab/gitlab_import/client.rb2
-rw-r--r--lib/gitlab/gitlab_import/importer.rb14
-rw-r--r--lib/gitlab/gitlab_import/project_creator.rb2
-rw-r--r--lib/gitlab/gl_id.rb2
-rw-r--r--lib/gitlab/gl_repository.rb4
-rw-r--r--lib/gitlab/gon_helper.rb22
-rw-r--r--lib/gitlab/google_code_import/client.rb2
-rw-r--r--lib/gitlab/google_code_import/importer.rb6
-rw-r--r--lib/gitlab/google_code_import/project_creator.rb2
-rw-r--r--lib/gitlab/google_code_import/repository.rb2
-rw-r--r--lib/gitlab/gpg.rb2
-rw-r--r--lib/gitlab/gpg/commit.rb6
-rw-r--r--lib/gitlab/gpg/invalid_gpg_signature_updater.rb4
-rw-r--r--lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb12
-rw-r--r--lib/gitlab/grape_logging/loggers/perf_logger.rb14
-rw-r--r--lib/gitlab/grape_logging/loggers/queue_duration_logger.rb2
-rw-r--r--lib/gitlab/grape_logging/loggers/route_logger.rb25
-rw-r--r--lib/gitlab/grape_logging/loggers/user_logger.rb2
-rw-r--r--lib/gitlab/graphql.rb2
-rw-r--r--lib/gitlab/graphql/authorize.rb2
-rw-r--r--lib/gitlab/graphql/authorize/authorize_resource.rb2
-rw-r--r--lib/gitlab/graphql/authorize/instrumentation.rb2
-rw-r--r--lib/gitlab/graphql/connections.rb2
-rw-r--r--lib/gitlab/graphql/connections/keyset_connection.rb6
-rw-r--r--lib/gitlab/graphql/errors.rb2
-rw-r--r--lib/gitlab/graphql/expose_permissions.rb2
-rw-r--r--lib/gitlab/graphql/mount_mutation.rb2
-rw-r--r--lib/gitlab/graphql/present.rb2
-rw-r--r--lib/gitlab/graphql/present/instrumentation.rb2
-rw-r--r--lib/gitlab/graphql/variables.rb2
-rw-r--r--lib/gitlab/graphs/commits.rb2
-rw-r--r--lib/gitlab/group_hierarchy.rb20
-rw-r--r--lib/gitlab/hashed_storage/migrator.rb6
-rw-r--r--lib/gitlab/hashed_storage/rake_helper.rb10
-rw-r--r--lib/gitlab/health_checks/base_abstract_check.rb2
-rw-r--r--lib/gitlab/health_checks/db_check.rb2
-rw-r--r--lib/gitlab/health_checks/gitaly_check.rb2
-rw-r--r--lib/gitlab/health_checks/metric.rb5
-rw-r--r--lib/gitlab/health_checks/prometheus_text_format.rb2
-rw-r--r--lib/gitlab/health_checks/redis/cache_check.rb4
-rw-r--r--lib/gitlab/health_checks/redis/queues_check.rb4
-rw-r--r--lib/gitlab/health_checks/redis/redis_check.rb2
-rw-r--r--lib/gitlab/health_checks/redis/shared_state_check.rb4
-rw-r--r--lib/gitlab/health_checks/result.rb5
-rw-r--r--lib/gitlab/health_checks/simple_abstract_check.rb2
-rw-r--r--lib/gitlab/highlight.rb33
-rw-r--r--lib/gitlab/hook_data/base_builder.rb13
-rw-r--r--lib/gitlab/hook_data/issuable_builder.rb4
-rw-r--r--lib/gitlab/hook_data/issue_builder.rb68
-rw-r--r--lib/gitlab/hook_data/merge_request_builder.rb62
-rw-r--r--lib/gitlab/hook_data/note_builder.rb2
-rw-r--r--lib/gitlab/hook_data/wiki_page_builder.rb2
-rw-r--r--lib/gitlab/http.rb9
-rw-r--r--lib/gitlab/http_io.rb27
-rw-r--r--lib/gitlab/i18n.rb6
-rw-r--r--lib/gitlab/i18n/metadata_entry.rb2
-rw-r--r--lib/gitlab/i18n/po_linter.rb2
-rw-r--r--lib/gitlab/i18n/translation_entry.rb2
-rw-r--r--lib/gitlab/identifier.rb25
-rw-r--r--lib/gitlab/import/database_helpers.rb27
-rw-r--r--lib/gitlab/import/logger.rb11
-rw-r--r--lib/gitlab/import/merge_request_creator.rb40
-rw-r--r--lib/gitlab/import/merge_request_helpers.rb74
-rw-r--r--lib/gitlab/import_export.rb6
-rw-r--r--lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb8
-rw-r--r--lib/gitlab/import_export/after_export_strategies/download_notification_strategy.rb2
-rw-r--r--lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb18
-rw-r--r--lib/gitlab/import_export/after_export_strategy_builder.rb2
-rw-r--r--lib/gitlab/import_export/attribute_cleaner.rb2
-rw-r--r--lib/gitlab/import_export/attributes_finder.rb2
-rw-r--r--lib/gitlab/import_export/avatar_restorer.rb4
-rw-r--r--lib/gitlab/import_export/avatar_saver.rb13
-rw-r--r--lib/gitlab/import_export/command_line_util.rb2
-rw-r--r--lib/gitlab/import_export/error.rb2
-rw-r--r--lib/gitlab/import_export/file_importer.rb2
-rw-r--r--lib/gitlab/import_export/group_project_object_builder.rb2
-rw-r--r--lib/gitlab/import_export/hash_util.rb2
-rw-r--r--lib/gitlab/import_export/import_export.yml23
-rw-r--r--lib/gitlab/import_export/importer.rb4
-rw-r--r--lib/gitlab/import_export/json_hash_builder.rb2
-rw-r--r--lib/gitlab/import_export/lfs_restorer.rb2
-rw-r--r--lib/gitlab/import_export/lfs_saver.rb2
-rw-r--r--lib/gitlab/import_export/members_mapper.rb4
-rw-r--r--lib/gitlab/import_export/merge_request_parser.rb2
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb26
-rw-r--r--lib/gitlab/import_export/project_tree_saver.rb2
-rw-r--r--lib/gitlab/import_export/reader.rb2
-rw-r--r--lib/gitlab/import_export/relation_factory.rb7
-rw-r--r--lib/gitlab/import_export/repo_restorer.rb2
-rw-r--r--lib/gitlab/import_export/repo_saver.rb2
-rw-r--r--lib/gitlab/import_export/saver.rb18
-rw-r--r--lib/gitlab/import_export/shared.rb2
-rw-r--r--lib/gitlab/import_export/statistics_restorer.rb2
-rw-r--r--lib/gitlab/import_export/uploads_manager.rb30
-rw-r--r--lib/gitlab/import_export/uploads_restorer.rb26
-rw-r--r--lib/gitlab/import_export/uploads_saver.rb4
-rw-r--r--lib/gitlab/import_export/version_checker.rb2
-rw-r--r--lib/gitlab/import_export/version_saver.rb2
-rw-r--r--lib/gitlab/import_export/wiki_repo_saver.rb2
-rw-r--r--lib/gitlab/import_export/wiki_restorer.rb2
-rw-r--r--lib/gitlab/import_formatter.rb2
-rw-r--r--lib/gitlab/import_sources.rb2
-rw-r--r--lib/gitlab/incoming_email.rb2
-rw-r--r--lib/gitlab/insecure_key_fingerprint.rb2
-rw-r--r--lib/gitlab/issuable_metadata.rb2
-rw-r--r--lib/gitlab/issuable_sorter.rb2
-rw-r--r--lib/gitlab/issuables_count_for_state.rb11
-rw-r--r--lib/gitlab/issues_labels.rb2
-rw-r--r--lib/gitlab/job_waiter.rb2
-rw-r--r--lib/gitlab/json_logger.rb2
-rw-r--r--lib/gitlab/kubernetes.rb2
-rw-r--r--lib/gitlab/kubernetes/cluster_role_binding.rb37
-rw-r--r--lib/gitlab/kubernetes/config_map.rb2
-rw-r--r--lib/gitlab/kubernetes/helm.rb8
-rw-r--r--lib/gitlab/kubernetes/helm/api.rb72
-rw-r--r--lib/gitlab/kubernetes/helm/base_command.rb30
-rw-r--r--lib/gitlab/kubernetes/helm/client_command.rb28
-rw-r--r--lib/gitlab/kubernetes/helm/init_command.rb62
-rw-r--r--lib/gitlab/kubernetes/helm/install_command.rb77
-rw-r--r--lib/gitlab/kubernetes/helm/pod.rb10
-rw-r--r--lib/gitlab/kubernetes/helm/upgrade_command.rb65
-rw-r--r--lib/gitlab/kubernetes/kube_client.rb103
-rw-r--r--lib/gitlab/kubernetes/namespace.rb6
-rw-r--r--lib/gitlab/kubernetes/pod.rb2
-rw-r--r--lib/gitlab/kubernetes/role_binding.rb48
-rw-r--r--lib/gitlab/kubernetes/service_account.rb27
-rw-r--r--lib/gitlab/kubernetes/service_account_token.rb36
-rw-r--r--lib/gitlab/language_data.rb33
-rw-r--r--lib/gitlab/language_detection.rb2
-rw-r--r--lib/gitlab/lazy.rb2
-rw-r--r--lib/gitlab/legacy_github_import/base_formatter.rb4
-rw-r--r--lib/gitlab/legacy_github_import/branch_formatter.rb2
-rw-r--r--lib/gitlab/legacy_github_import/client.rb2
-rw-r--r--lib/gitlab/legacy_github_import/comment_formatter.rb2
-rw-r--r--lib/gitlab/legacy_github_import/importer.rb8
-rw-r--r--lib/gitlab/legacy_github_import/issuable_formatter.rb4
-rw-r--r--lib/gitlab/legacy_github_import/issue_formatter.rb2
-rw-r--r--lib/gitlab/legacy_github_import/label_formatter.rb4
-rw-r--r--lib/gitlab/legacy_github_import/milestone_formatter.rb2
-rw-r--r--lib/gitlab/legacy_github_import/project_creator.rb2
-rw-r--r--lib/gitlab/legacy_github_import/pull_request_formatter.rb2
-rw-r--r--lib/gitlab/legacy_github_import/release_formatter.rb2
-rw-r--r--lib/gitlab/legacy_github_import/user_formatter.rb4
-rw-r--r--lib/gitlab/legacy_github_import/wiki_formatter.rb2
-rw-r--r--lib/gitlab/lfs_token.rb2
-rw-r--r--lib/gitlab/logger.rb4
-rw-r--r--lib/gitlab/mail_room.rb2
-rw-r--r--lib/gitlab/manifest_import/manifest.rb4
-rw-r--r--lib/gitlab/manifest_import/project_creator.rb2
-rw-r--r--lib/gitlab/markup_helper.rb15
-rw-r--r--lib/gitlab/metrics.rb2
-rw-r--r--lib/gitlab/metrics/background_transaction.rb2
-rw-r--r--lib/gitlab/metrics/delta.rb2
-rw-r--r--lib/gitlab/metrics/influx_db.rb4
-rw-r--r--lib/gitlab/metrics/instrumentation.rb2
-rw-r--r--lib/gitlab/metrics/method_call.rb2
-rw-r--r--lib/gitlab/metrics/methods.rb2
-rw-r--r--lib/gitlab/metrics/methods/metric_options.rb2
-rw-r--r--lib/gitlab/metrics/metric.rb2
-rw-r--r--lib/gitlab/metrics/null_metric.rb2
-rw-r--r--lib/gitlab/metrics/prometheus.rb2
-rw-r--r--lib/gitlab/metrics/rack_middleware.rb2
-rw-r--r--lib/gitlab/metrics/requests_rack_middleware.rb2
-rw-r--r--lib/gitlab/metrics/samplers/base_sampler.rb2
-rw-r--r--lib/gitlab/metrics/samplers/influx_sampler.rb2
-rw-r--r--lib/gitlab/metrics/samplers/ruby_sampler.rb2
-rw-r--r--lib/gitlab/metrics/samplers/unicorn_sampler.rb2
-rw-r--r--lib/gitlab/metrics/sidekiq_metrics_exporter.rb2
-rw-r--r--lib/gitlab/metrics/sidekiq_middleware.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/action_view.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/active_record.rb8
-rw-r--r--lib/gitlab/metrics/subscribers/rails_cache.rb2
-rw-r--r--lib/gitlab/metrics/system.rb2
-rw-r--r--lib/gitlab/metrics/transaction.rb2
-rw-r--r--lib/gitlab/metrics/web_transaction.rb4
-rw-r--r--lib/gitlab/middleware/go.rb4
-rw-r--r--lib/gitlab/middleware/multipart.rb12
-rw-r--r--lib/gitlab/middleware/rails_queue_duration.rb2
-rw-r--r--lib/gitlab/middleware/read_only.rb2
-rw-r--r--lib/gitlab/middleware/read_only/controller.rb2
-rw-r--r--lib/gitlab/middleware/release_env.rb5
-rw-r--r--lib/gitlab/middleware/static.rb2
-rw-r--r--lib/gitlab/multi_collection_paginator.rb4
-rw-r--r--lib/gitlab/namespace_sanitizer.rb9
-rw-r--r--lib/gitlab/null_request_store.rb41
-rw-r--r--lib/gitlab/omniauth_initializer.rb2
-rw-r--r--lib/gitlab/optimistic_locking.rb2
-rw-r--r--lib/gitlab/other_markup.rb2
-rw-r--r--lib/gitlab/otp_key_rotator.rb6
-rw-r--r--lib/gitlab/pages.rb2
-rw-r--r--lib/gitlab/pages_client.rb2
-rw-r--r--lib/gitlab/pages_transfer.rb2
-rw-r--r--lib/gitlab/patch/draw_route.rb38
-rw-r--r--lib/gitlab/patch/prependable.rb65
-rw-r--r--lib/gitlab/path_regex.rb6
-rw-r--r--lib/gitlab/performance_bar.rb4
-rw-r--r--lib/gitlab/performance_bar/peek_query_tracker.rb2
-rw-r--r--lib/gitlab/plugin.rb2
-rw-r--r--lib/gitlab/plugin_logger.rb2
-rw-r--r--lib/gitlab/polling_interval.rb2
-rw-r--r--lib/gitlab/popen.rb4
-rw-r--r--lib/gitlab/private_commit_email.rb32
-rw-r--r--lib/gitlab/profiler.rb6
-rw-r--r--lib/gitlab/project_authorizations/with_nested_groups.rb4
-rw-r--r--lib/gitlab/project_authorizations/without_nested_groups.rb4
-rw-r--r--lib/gitlab/project_search_results.rb13
-rw-r--r--lib/gitlab/project_service_logger.rb9
-rw-r--r--lib/gitlab/project_template.rb2
-rw-r--r--lib/gitlab/project_transfer.rb2
-rw-r--r--lib/gitlab/prometheus/additional_metrics_parser.rb2
-rw-r--r--lib/gitlab/prometheus/metric_group.rb5
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb2
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb2
-rw-r--r--lib/gitlab/prometheus/queries/deployment_query.rb2
-rw-r--r--lib/gitlab/prometheus/queries/environment_query.rb2
-rw-r--r--lib/gitlab/prometheus_client.rb2
-rw-r--r--lib/gitlab/protocol_access.rb2
-rw-r--r--lib/gitlab/proxy_http_connection_adapter.rb4
-rw-r--r--lib/gitlab/query_limiting.rb2
-rw-r--r--lib/gitlab/quick_actions/command_definition.rb16
-rw-r--r--lib/gitlab/quick_actions/dsl.rb6
-rw-r--r--lib/gitlab/quick_actions/extractor.rb14
-rw-r--r--lib/gitlab/recaptcha.rb2
-rw-r--r--lib/gitlab/reference_counter.rb2
-rw-r--r--lib/gitlab/reference_extractor.rb2
-rw-r--r--lib/gitlab/regex.rb2
-rw-r--r--lib/gitlab/repo_path.rb2
-rw-r--r--lib/gitlab/repository_cache.rb24
-rw-r--r--lib/gitlab/repository_cache_adapter.rb183
-rw-r--r--lib/gitlab/repository_check_logger.rb2
-rw-r--r--lib/gitlab/request_context.rb6
-rw-r--r--lib/gitlab/request_forgery_protection.rb2
-rw-r--r--lib/gitlab/request_profiler.rb2
-rw-r--r--lib/gitlab/route_map.rb2
-rw-r--r--lib/gitlab/routing.rb4
-rw-r--r--lib/gitlab/safe_request_store.rb23
-rw-r--r--lib/gitlab/search_results.rb35
-rw-r--r--lib/gitlab/seeder.rb2
-rw-r--r--lib/gitlab/sentry.rb16
-rw-r--r--lib/gitlab/setup_helper.rb20
-rw-r--r--lib/gitlab/shard_health_cache.rb2
-rw-r--r--lib/gitlab/shell.rb32
-rw-r--r--lib/gitlab/shell_adapter.rb2
-rw-r--r--lib/gitlab/sherlock.rb2
-rw-r--r--lib/gitlab/sidekiq_config.rb2
-rw-r--r--lib/gitlab/sidekiq_logger.rb2
-rw-r--r--lib/gitlab/sidekiq_status.rb2
-rw-r--r--lib/gitlab/sidekiq_throttler.rb25
-rw-r--r--lib/gitlab/sidekiq_versioning.rb2
-rw-r--r--lib/gitlab/slash_commands/base_command.rb2
-rw-r--r--lib/gitlab/slash_commands/deploy.rb2
-rw-r--r--lib/gitlab/slash_commands/issue_new.rb2
-rw-r--r--lib/gitlab/slash_commands/issue_search.rb2
-rw-r--r--lib/gitlab/snippet_search_results.rb6
-rw-r--r--lib/gitlab/ssh_public_key.rb4
-rw-r--r--lib/gitlab/storage_check.rb11
-rw-r--r--lib/gitlab/storage_check/cli.rb71
-rw-r--r--lib/gitlab/storage_check/gitlab_caller.rb39
-rw-r--r--lib/gitlab/storage_check/option_parser.rb39
-rw-r--r--lib/gitlab/storage_check/response.rb77
-rw-r--r--lib/gitlab/string_placeholder_replacer.rb2
-rw-r--r--lib/gitlab/string_range_marker.rb2
-rw-r--r--lib/gitlab/string_regex_marker.rb4
-rw-r--r--lib/gitlab/task_helpers.rb4
-rw-r--r--lib/gitlab/tcp_checker.rb2
-rw-r--r--lib/gitlab/template/base_template.rb17
-rw-r--r--lib/gitlab/template/finders/base_template_finder.rb2
-rw-r--r--lib/gitlab/template/finders/global_template_finder.rb2
-rw-r--r--lib/gitlab/template/finders/repo_template_finder.rb12
-rw-r--r--lib/gitlab/template/gitlab_ci_yml_template.rb2
-rw-r--r--lib/gitlab/template_helper.rb19
-rw-r--r--lib/gitlab/temporarily_allow.rb8
-rw-r--r--lib/gitlab/testing/request_inspector_middleware.rb6
-rw-r--r--lib/gitlab/themes.rb2
-rw-r--r--lib/gitlab/time_tracking_formatter.rb2
-rw-r--r--lib/gitlab/timeless.rb2
-rw-r--r--lib/gitlab/tree_summary.rb121
-rw-r--r--lib/gitlab/untrusted_regexp.rb2
-rw-r--r--lib/gitlab/update_path_error.rb2
-rw-r--r--lib/gitlab/upgrader.rb2
-rw-r--r--lib/gitlab/uploads_transfer.rb2
-rw-r--r--lib/gitlab/url_blocker.rb17
-rw-r--r--lib/gitlab/url_builder.rb2
-rw-r--r--lib/gitlab/url_sanitizer.rb4
-rw-r--r--lib/gitlab/usage_data.rb131
-rw-r--r--lib/gitlab/user_access.rb2
-rw-r--r--lib/gitlab/user_extractor.rb53
-rw-r--r--lib/gitlab/utils.rb7
-rw-r--r--lib/gitlab/utils/override.rb12
-rw-r--r--lib/gitlab/verify/uploads.rb2
-rw-r--r--lib/gitlab/version_info.rb2
-rw-r--r--lib/gitlab/view/presenter/base.rb4
-rw-r--r--lib/gitlab/visibility_level.rb2
-rw-r--r--lib/gitlab/web_ide_commits_counter.rb17
-rw-r--r--lib/gitlab/wiki_file_finder.rb2
-rw-r--r--lib/gitlab/workhorse.rb15
-rw-r--r--lib/google_api/auth.rb4
-rw-r--r--lib/google_api/cloud_platform/client.rb6
-rw-r--r--lib/gt_one_coercion.rb2
-rw-r--r--lib/haml_lint/inline_javascript.rb11
-rw-r--r--lib/json_web_token/hmac_token.rb28
-rw-r--r--lib/json_web_token/rsa_token.rb2
-rw-r--r--lib/json_web_token/token.rb11
-rw-r--r--lib/mattermost/client.rb2
-rw-r--r--lib/mattermost/command.rb2
-rw-r--r--lib/mattermost/error.rb2
-rw-r--r--lib/mattermost/session.rb2
-rw-r--r--lib/mattermost/team.rb2
-rw-r--r--lib/microsoft_teams/activity.rb2
-rw-r--r--lib/microsoft_teams/notifier.rb2
-rw-r--r--lib/milestone_array.rb2
-rw-r--r--lib/mysql_zero_date.rb2
-rw-r--r--lib/object_storage/direct_upload.rb14
-rw-r--r--lib/omni_auth/strategies/bitbucket.rb2
-rw-r--r--lib/omni_auth/strategies/jwt.rb2
-rw-r--r--lib/peek/rblineprof/custom_controller_helpers.rb6
-rw-r--r--lib/peek/views/gitaly.rb2
-rw-r--r--lib/peek/views/host.rb7
-rw-r--r--lib/quality/helm_client.rb113
-rw-r--r--lib/quality/kubernetes_client.rb42
-rw-r--r--lib/rouge/formatters/html_gitlab.rb2
-rw-r--r--lib/rouge/plugins/common_mark.rb2
-rw-r--r--lib/rspec_flaky/config.rb2
-rw-r--r--lib/rspec_flaky/example.rb2
-rw-r--r--lib/rspec_flaky/flaky_example.rb2
-rw-r--r--lib/rspec_flaky/flaky_examples_collection.rb2
-rw-r--r--lib/rspec_flaky/listener.rb2
-rw-r--r--lib/rspec_flaky/report.rb2
-rw-r--r--lib/static_model.rb4
-rw-r--r--lib/support/nginx/gitlab4
-rw-r--r--lib/support/nginx/gitlab-ssl4
-rw-r--r--lib/system_check.rb2
-rw-r--r--lib/system_check/app/active_users_check.rb2
-rw-r--r--lib/system_check/app/database_config_exists_check.rb2
-rw-r--r--lib/system_check/app/git_config_check.rb2
-rw-r--r--lib/system_check/app/git_user_default_ssh_config_check.rb2
-rw-r--r--lib/system_check/app/git_version_check.rb2
-rw-r--r--lib/system_check/app/gitlab_config_exists_check.rb2
-rw-r--r--lib/system_check/app/gitlab_config_up_to_date_check.rb2
-rw-r--r--lib/system_check/app/init_script_exists_check.rb2
-rw-r--r--lib/system_check/app/init_script_up_to_date_check.rb2
-rw-r--r--lib/system_check/app/log_writable_check.rb2
-rw-r--r--lib/system_check/app/migrations_are_up_check.rb2
-rw-r--r--lib/system_check/app/orphaned_group_members_check.rb2
-rw-r--r--lib/system_check/app/projects_have_namespace_check.rb2
-rw-r--r--lib/system_check/app/redis_version_check.rb2
-rw-r--r--lib/system_check/app/ruby_version_check.rb2
-rw-r--r--lib/system_check/app/tmp_writable_check.rb2
-rw-r--r--lib/system_check/app/uploads_directory_exists_check.rb2
-rw-r--r--lib/system_check/app/uploads_path_permission_check.rb2
-rw-r--r--lib/system_check/app/uploads_path_tmp_permission_check.rb2
-rw-r--r--lib/system_check/base_check.rb2
-rw-r--r--lib/system_check/helpers.rb2
-rw-r--r--lib/system_check/incoming_email/foreman_configured_check.rb2
-rw-r--r--lib/system_check/incoming_email/imap_authentication_check.rb4
-rw-r--r--lib/system_check/incoming_email/initd_configured_check.rb2
-rw-r--r--lib/system_check/incoming_email/mail_room_running_check.rb2
-rw-r--r--lib/system_check/orphans/namespace_check.rb2
-rw-r--r--lib/system_check/orphans/repository_check.rb2
-rw-r--r--lib/system_check/simple_executor.rb2
-rw-r--r--lib/tasks/flay.rake9
-rw-r--r--lib/tasks/gemojione.rake2
-rw-r--r--lib/tasks/gettext.rake13
-rw-r--r--lib/tasks/gitlab/artifacts/migrate.rake2
-rw-r--r--lib/tasks/gitlab/check.rake174
-rw-r--r--lib/tasks/gitlab/cleanup.rake89
-rw-r--r--lib/tasks/gitlab/db.rake2
-rw-r--r--lib/tasks/gitlab/gitaly.rake24
-rw-r--r--lib/tasks/gitlab/ldap.rake2
-rw-r--r--lib/tasks/gitlab/shell.rake46
-rw-r--r--lib/tasks/gitlab/site_statistics.rake15
-rw-r--r--lib/tasks/gitlab/traces.rake17
-rw-r--r--lib/tasks/gitlab/update_templates.rake8
-rw-r--r--lib/tasks/gitlab/uploads/migrate.rake26
-rw-r--r--lib/tasks/haml-lint.rake11
-rw-r--r--lib/tasks/import.rake2
-rw-r--r--lib/tasks/lint.rake1
-rw-r--r--lib/tasks/migrate/add_limits_mysql.rake2
-rw-r--r--lib/tasks/tokens.rake14
-rw-r--r--lib/unfold_form.rb2
-rw-r--r--lib/uploaded_file.rb2
-rw-r--r--lib/version_check.rb2
-rw-r--r--locale/ar_SA/gitlab.po9452
-rw-r--r--locale/bg/gitlab.po1762
-rw-r--r--locale/ca_ES/gitlab.po9208
-rw-r--r--locale/cs_CZ/gitlab.po1900
-rw-r--r--locale/da_DK/gitlab.po9208
-rw-r--r--locale/de/gitlab.po1762
-rw-r--r--locale/en/gitlab.po3
-rw-r--r--locale/eo/gitlab.po1762
-rw-r--r--locale/es/gitlab.po1766
-rw-r--r--locale/et_EE/gitlab.po9208
-rw-r--r--locale/fil_PH/gitlab.po1758
-rw-r--r--locale/fr/gitlab.po2942
-rw-r--r--locale/gitlab.pot1457
-rw-r--r--locale/gl_ES/gitlab.po1760
-rw-r--r--locale/he_IL/gitlab.po9330
-rw-r--r--locale/id_ID/gitlab.po1733
-rw-r--r--locale/it/gitlab.po1762
-rw-r--r--locale/ja/gitlab.po2679
-rw-r--r--locale/ko/gitlab.po2333
-rw-r--r--locale/mn_MN/gitlab.po9208
-rw-r--r--locale/nb_NO/gitlab.po9208
-rw-r--r--locale/nl_NL/gitlab.po1862
-rw-r--r--locale/pl_PL/gitlab.po1808
-rw-r--r--locale/pt_BR/gitlab.po2632
-rw-r--r--locale/ro_RO/gitlab.po9269
-rw-r--r--locale/ru/gitlab.po1832
-rw-r--r--locale/sq_AL/gitlab.po9208
-rw-r--r--locale/tr_TR/gitlab.po1758
-rw-r--r--locale/uk/gitlab.po2618
-rw-r--r--locale/unfound_translations.rb16
-rw-r--r--locale/zh_CN/gitlab.po2347
-rw-r--r--locale/zh_HK/gitlab.po1737
-rw-r--r--locale/zh_TW/gitlab.po1819
-rw-r--r--package.json84
-rw-r--r--public/robots.txt2
-rw-r--r--qa/Dockerfile12
-rw-r--r--qa/Gemfile.lock6
-rw-r--r--qa/README.md18
-rw-r--r--qa/qa.rb142
-rw-r--r--qa/qa/factory/base.rb60
-rw-r--r--qa/qa/factory/dependency.rb39
-rw-r--r--qa/qa/factory/product.rb30
-rw-r--r--qa/qa/factory/repository/project_push.rb36
-rw-r--r--qa/qa/factory/repository/push.rb71
-rw-r--r--qa/qa/factory/repository/wiki_push.rb32
-rw-r--r--qa/qa/factory/resource/branch.rb77
-rw-r--r--qa/qa/factory/resource/deploy_key.rb43
-rw-r--r--qa/qa/factory/resource/file.rb34
-rw-r--r--qa/qa/factory/resource/fork.rb24
-rw-r--r--qa/qa/factory/resource/group.rb35
-rw-r--r--qa/qa/factory/resource/issue.rb32
-rw-r--r--qa/qa/factory/resource/kubernetes_cluster.rb59
-rw-r--r--qa/qa/factory/resource/merge_request.rb65
-rw-r--r--qa/qa/factory/resource/merge_request_from_fork.rb24
-rw-r--r--qa/qa/factory/resource/personal_access_token.rb27
-rw-r--r--qa/qa/factory/resource/project.rb47
-rw-r--r--qa/qa/factory/resource/project_imported_from_github.rb37
-rw-r--r--qa/qa/factory/resource/project_milestone.rb36
-rw-r--r--qa/qa/factory/resource/runner.rb47
-rw-r--r--qa/qa/factory/resource/sandbox.rb34
-rw-r--r--qa/qa/factory/resource/secret_variable.rb28
-rw-r--r--qa/qa/factory/resource/user.rb34
-rw-r--r--qa/qa/factory/resource/wiki.rb25
-rw-r--r--qa/qa/factory/settings/hashed_storage.rb24
-rw-r--r--qa/qa/git/repository.rb154
-rw-r--r--qa/qa/page/README.md40
-rw-r--r--qa/qa/page/admin/menu.rb47
-rw-r--r--qa/qa/page/admin/settings/component/repository_storage.rb26
-rw-r--r--qa/qa/page/admin/settings/main.rb21
-rw-r--r--qa/qa/page/admin/settings/repository.rb23
-rw-r--r--qa/qa/page/admin/settings/repository_storage.rb23
-rw-r--r--qa/qa/page/base.rb32
-rw-r--r--qa/qa/page/component/clone_panel.rb52
-rw-r--r--qa/qa/page/component/dropdown_filter.rb18
-rw-r--r--qa/qa/page/component/groups_filter.rb39
-rw-r--r--qa/qa/page/component/issuable/common.rb35
-rw-r--r--qa/qa/page/component/users_select.rb14
-rw-r--r--qa/qa/page/dashboard/groups.rb18
-rw-r--r--qa/qa/page/dashboard/projects.rb5
-rw-r--r--qa/qa/page/file/form.rb34
-rw-r--r--qa/qa/page/file/shared/commit_message.rb2
-rw-r--r--qa/qa/page/file/show.rb6
-rw-r--r--qa/qa/page/group/new.rb10
-rw-r--r--qa/qa/page/group/show.rb49
-rw-r--r--qa/qa/page/issuable/sidebar.rb4
-rw-r--r--qa/qa/page/label/index.rb15
-rw-r--r--qa/qa/page/label/new.rb30
-rw-r--r--qa/qa/page/layout/banner.rb2
-rw-r--r--qa/qa/page/main/login.rb129
-rw-r--r--qa/qa/page/main/menu.rb91
-rw-r--r--qa/qa/page/main/oauth.rb2
-rw-r--r--qa/qa/page/main/sign_up.rb38
-rw-r--r--qa/qa/page/menu/admin.rb15
-rw-r--r--qa/qa/page/menu/main.rb88
-rw-r--r--qa/qa/page/menu/profile.rb27
-rw-r--r--qa/qa/page/menu/side.rb124
-rw-r--r--qa/qa/page/merge_request/new.rb10
-rw-r--r--qa/qa/page/merge_request/show.rb104
-rw-r--r--qa/qa/page/profile/menu.rb36
-rw-r--r--qa/qa/page/profile/personal_access_tokens.rb10
-rw-r--r--qa/qa/page/profile/ssh_keys.rb34
-rw-r--r--qa/qa/page/project/activity.rb2
-rw-r--r--qa/qa/page/project/fork/new.rb2
-rw-r--r--qa/qa/page/project/import/github.rb12
-rw-r--r--qa/qa/page/project/issue/index.rb2
-rw-r--r--qa/qa/page/project/issue/new.rb6
-rw-r--r--qa/qa/page/project/issue/show.rb40
-rw-r--r--qa/qa/page/project/job/show.rb25
-rw-r--r--qa/qa/page/project/menu.rb168
-rw-r--r--qa/qa/page/project/new.rb17
-rw-r--r--qa/qa/page/project/operations/environments/index.rb23
-rw-r--r--qa/qa/page/project/operations/environments/show.rb23
-rw-r--r--qa/qa/page/project/operations/kubernetes/add.rb4
-rw-r--r--qa/qa/page/project/operations/kubernetes/add_existing.rb17
-rw-r--r--qa/qa/page/project/operations/kubernetes/index.rb4
-rw-r--r--qa/qa/page/project/operations/kubernetes/show.rb8
-rw-r--r--qa/qa/page/project/pipeline/index.rb2
-rw-r--r--qa/qa/page/project/pipeline/show.rb12
-rw-r--r--qa/qa/page/project/settings/advanced.rb6
-rw-r--r--qa/qa/page/project/settings/ci_cd.rb14
-rw-r--r--qa/qa/page/project/settings/ci_variables.rb52
-rw-r--r--qa/qa/page/project/settings/common.rb2
-rw-r--r--qa/qa/page/project/settings/deploy_keys.rb12
-rw-r--r--qa/qa/page/project/settings/deploy_tokens.rb64
-rw-r--r--qa/qa/page/project/settings/members.rb27
-rw-r--r--qa/qa/page/project/settings/repository.rb6
-rw-r--r--qa/qa/page/project/settings/runners.rb6
-rw-r--r--qa/qa/page/project/settings/secret_variables.rb46
-rw-r--r--qa/qa/page/project/show.rb40
-rw-r--r--qa/qa/page/project/web_ide/edit.rb79
-rw-r--r--qa/qa/page/project/wiki/edit.rb6
-rw-r--r--qa/qa/page/project/wiki/new.rb12
-rw-r--r--qa/qa/page/project/wiki/show.rb6
-rw-r--r--qa/qa/page/shared/clone_panel.rb50
-rw-r--r--qa/qa/page/view.rb4
-rw-r--r--qa/qa/resource/README.md392
-rw-r--r--qa/qa/resource/api_fabricator.rb101
-rw-r--r--qa/qa/resource/base.rb154
-rw-r--r--qa/qa/resource/branch.rb77
-rw-r--r--qa/qa/resource/ci_variable.rb30
-rw-r--r--qa/qa/resource/deploy_key.rb43
-rw-r--r--qa/qa/resource/deploy_token.rb50
-rw-r--r--qa/qa/resource/file.rb36
-rw-r--r--qa/qa/resource/fork.rb43
-rw-r--r--qa/qa/resource/group.rb68
-rw-r--r--qa/qa/resource/issue.rb30
-rw-r--r--qa/qa/resource/kubernetes_cluster.rb57
-rw-r--r--qa/qa/resource/label.rb39
-rw-r--r--qa/qa/resource/merge_request.rb75
-rw-r--r--qa/qa/resource/merge_request_from_fork.rb31
-rw-r--r--qa/qa/resource/personal_access_token.rb27
-rw-r--r--qa/qa/resource/project.rb80
-rw-r--r--qa/qa/resource/project_imported_from_github.rb36
-rw-r--r--qa/qa/resource/project_milestone.rb36
-rw-r--r--qa/qa/resource/repository/project_push.rb44
-rw-r--r--qa/qa/resource/repository/push.rb93
-rw-r--r--qa/qa/resource/repository/wiki_push.rb36
-rw-r--r--qa/qa/resource/runner.rb49
-rw-r--r--qa/qa/resource/sandbox.rb60
-rw-r--r--qa/qa/resource/settings/hashed_storage.rb26
-rw-r--r--qa/qa/resource/ssh_key.rb26
-rw-r--r--qa/qa/resource/user.rb103
-rw-r--r--qa/qa/resource/wiki.rb30
-rw-r--r--qa/qa/runtime/api/client.rb29
-rw-r--r--qa/qa/runtime/browser.rb4
-rw-r--r--qa/qa/runtime/env.rb99
-rw-r--r--qa/qa/runtime/fixtures.rb19
-rw-r--r--qa/qa/runtime/logger.rb25
-rw-r--r--qa/qa/runtime/path.rb13
-rw-r--r--qa/qa/runtime/user.rb24
-rw-r--r--qa/qa/scenario/taggable.rb17
-rw-r--r--qa/qa/scenario/template.rb33
-rw-r--r--qa/qa/scenario/test/instance.rb30
-rw-r--r--qa/qa/scenario/test/instance/all.rb15
-rw-r--r--qa/qa/scenario/test/instance/smoke.rb17
-rw-r--r--qa/qa/scenario/test/integration/github.rb2
-rw-r--r--qa/qa/scenario/test/integration/instance_saml.rb13
-rw-r--r--qa/qa/scenario/test/integration/kubernetes.rb2
-rw-r--r--qa/qa/scenario/test/integration/ldap.rb11
-rw-r--r--qa/qa/scenario/test/integration/ldap_no_tls.rb13
-rw-r--r--qa/qa/scenario/test/integration/ldap_tls.rb13
-rw-r--r--qa/qa/scenario/test/integration/mattermost.rb4
-rw-r--r--qa/qa/scenario/test/integration/object_storage.rb13
-rw-r--r--qa/qa/scenario/test/sanity/framework.rb19
-rw-r--r--qa/qa/service/kubernetes_cluster.rb72
-rw-r--r--qa/qa/service/shellout.rb6
-rw-r--r--qa/qa/specs/features/api/1_manage/.gitkeep (renamed from qa/spec/runtime/api_request_spec.rb)0
-rw-r--r--qa/qa/specs/features/api/1_manage/users_spec.rb35
-rw-r--r--qa/qa/specs/features/api/2_plan/.gitkeep0
-rw-r--r--qa/qa/specs/features/api/3_create/repository/files_spec.rb61
-rw-r--r--qa/qa/specs/features/api/4_verify/.gitkeep0
-rw-r--r--qa/qa/specs/features/api/5_package/.gitkeep0
-rw-r--r--qa/qa/specs/features/api/6_release/.gitkeep0
-rw-r--r--qa/qa/specs/features/api/7_configure/.gitkeep0
-rw-r--r--qa/qa/specs/features/api/basics_spec.rb61
-rw-r--r--qa/qa/specs/features/api/users_spec.rb41
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb19
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb21
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb30
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb26
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb24
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb110
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb24
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb51
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb39
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb67
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb25
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb46
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb55
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb77
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb31
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb61
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb58
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb49
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb84
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb32
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb23
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb70
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb40
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb88
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb49
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb28
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb87
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb32
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/.gitkeep0
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb23
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb109
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb23
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb85
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb21
-rw-r--r--qa/qa/specs/features/login/ldap_spec.rb19
-rw-r--r--qa/qa/specs/features/mattermost/group_create_spec.rb17
-rw-r--r--qa/qa/specs/features/mattermost/login_spec.rb17
-rw-r--r--qa/qa/specs/features/merge_request/create_spec.rb32
-rw-r--r--qa/qa/specs/features/merge_request/rebase_spec.rb39
-rw-r--r--qa/qa/specs/features/merge_request/squash_spec.rb50
-rw-r--r--qa/qa/specs/features/project/activity_spec.rb20
-rw-r--r--qa/qa/specs/features/project/add_deploy_key_spec.rb19
-rw-r--r--qa/qa/specs/features/project/add_secret_variable_spec.rb24
-rw-r--r--qa/qa/specs/features/project/auto_devops_spec.rb57
-rw-r--r--qa/qa/specs/features/project/create_issue_spec.rb18
-rw-r--r--qa/qa/specs/features/project/create_spec.rb22
-rw-r--r--qa/qa/specs/features/project/deploy_key_clone_spec.rb105
-rw-r--r--qa/qa/specs/features/project/file_spec.rb54
-rw-r--r--qa/qa/specs/features/project/fork_project_spec.rb23
-rw-r--r--qa/qa/specs/features/project/import_from_github_spec.rb106
-rw-r--r--qa/qa/specs/features/project/pipelines_spec.rb102
-rw-r--r--qa/qa/specs/features/project/wikis_spec.rb45
-rw-r--r--qa/qa/specs/features/repository/clone_spec.rb58
-rw-r--r--qa/qa/specs/features/repository/protected_branches_spec.rb66
-rw-r--r--qa/qa/specs/features/repository/push_spec.rb21
-rw-r--r--qa/qa/specs/features/sanity/framework_spec.rb21
-rw-r--r--qa/qa/specs/runner.rb19
-rw-r--r--qa/qa/support/page/logging.rb100
-rw-r--r--qa/qa/vendor/saml_idp/page/base.rb14
-rw-r--r--qa/qa/vendor/saml_idp/page/login.rb19
-rw-r--r--qa/spec/factory/base_spec.rb132
-rw-r--r--qa/spec/factory/dependency_spec.rb72
-rw-r--r--qa/spec/factory/product_spec.rb39
-rw-r--r--qa/spec/factory/resource/user_spec.rb36
-rw-r--r--qa/spec/git/repository_spec.rb45
-rw-r--r--qa/spec/page/base_spec.rb6
-rw-r--r--qa/spec/page/logging_spec.rb94
-rw-r--r--qa/spec/page/validator_spec.rb2
-rw-r--r--qa/spec/page/view_spec.rb4
-rw-r--r--qa/spec/resource/api_fabricator_spec.rb161
-rw-r--r--qa/spec/resource/base_spec.rb246
-rw-r--r--qa/spec/resource/repository/push_spec.rb26
-rw-r--r--qa/spec/runtime/api/client_spec.rb25
-rw-r--r--qa/spec/runtime/api/request_spec.rb18
-rw-r--r--qa/spec/runtime/env_spec.rb164
-rw-r--r--qa/spec/runtime/logger_spec.rb33
-rw-r--r--qa/spec/scenario/test/instance/all_spec.rb3
-rw-r--r--qa/spec/scenario/test/instance/smoke_spec.rb5
-rw-r--r--qa/spec/scenario/test/instance_spec.rb45
-rw-r--r--qa/spec/scenario/test/integration/github_spec.rb21
-rw-r--r--qa/spec/scenario/test/integration/instance_saml_spec.rb9
-rw-r--r--qa/spec/scenario/test/integration/kubernetes_spec.rb9
-rw-r--r--qa/spec/scenario/test/integration/ldap_spec.rb17
-rw-r--r--qa/spec/scenario/test/integration/mattermost_spec.rb18
-rw-r--r--qa/spec/scenario/test/integration/object_storage_spec.rb9
-rw-r--r--qa/spec/scenario/test/sanity/framework_spec.rb5
-rw-r--r--qa/spec/spec_helper.rb4
-rw-r--r--qa/spec/specs/runner_spec.rb99
-rw-r--r--qa/spec/support/shared_examples/scenario_shared_examples.rb49
-rw-r--r--rubocop/code_reuse_helpers.rb156
-rw-r--r--rubocop/cop/avoid_route_redirect_leading_slash.rb52
-rw-r--r--rubocop/cop/code_reuse/active_record.rb169
-rw-r--r--rubocop/cop/code_reuse/finder.rb39
-rw-r--r--rubocop/cop/code_reuse/presenter.rb41
-rw-r--r--rubocop/cop/code_reuse/serializer.rb41
-rw-r--r--rubocop/cop/code_reuse/service_class.rb39
-rw-r--r--rubocop/cop/code_reuse/worker.rb56
-rw-r--r--rubocop/cop/destroy_all.rb22
-rw-r--r--rubocop/cop/gitlab/union.rb27
-rw-r--r--rubocop/cop/group_public_or_visible_to_user.rb22
-rw-r--r--rubocop/cop/line_break_around_conditional_block.rb13
-rw-r--r--rubocop/cop/prefer_class_methods_over_module.rb73
-rw-r--r--rubocop/cop/qa/element_with_pattern.rb39
-rw-r--r--rubocop/cop/ruby_interpolation_in_translation.rb28
-rw-r--r--rubocop/qa_helpers.rb11
-rw-r--r--rubocop/rubocop.rb13
-rwxr-xr-xscripts/build_assets_image27
-rw-r--r--scripts/frontend/extract_gettext_all.js72
-rw-r--r--scripts/frontend/frontend_script_utils.js3
-rw-r--r--scripts/frontend/prettier.js206
-rwxr-xr-xscripts/lint-doc.sh2
-rwxr-xr-xscripts/lint-rugged14
-rwxr-xr-xscripts/rails4-gemfile-lock-check19
-rwxr-xr-xscripts/rails5-gemfile-lock-check19
-rwxr-xr-xscripts/review_apps/automated_cleanup.rb161
-rwxr-xr-xscripts/review_apps/review-apps.sh231
-rw-r--r--scripts/schema_changed.sh13
-rwxr-xr-xscripts/static-analysis1
-rwxr-xr-xscripts/trigger-build242
-rwxr-xr-xscripts/trigger-build-docs6
-rw-r--r--spec/bin/changelog_spec.rb1
-rw-r--r--spec/bin/storage_check_spec.rb13
-rw-r--r--spec/config/settings_spec.rb9
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb25
-rw-r--r--spec/controllers/admin/health_check_controller_spec.rb12
-rw-r--r--spec/controllers/admin/projects_controller_spec.rb11
-rw-r--r--spec/controllers/application_controller_spec.rb180
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb13
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb68
-rw-r--r--spec/controllers/concerns/issuable_collections_spec.rb43
-rw-r--r--spec/controllers/concerns/send_file_upload_spec.rb52
-rw-r--r--spec/controllers/dashboard/milestones_controller_spec.rb17
-rw-r--r--spec/controllers/groups/boards_controller_spec.rb59
-rw-r--r--spec/controllers/groups/clusters/applications_controller_spec.rb87
-rw-r--r--spec/controllers/groups/clusters_controller_spec.rb574
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb15
-rw-r--r--spec/controllers/groups/settings/ci_cd_controller_spec.rb14
-rw-r--r--spec/controllers/groups_controller_spec.rb49
-rw-r--r--spec/controllers/health_controller_spec.rb42
-rw-r--r--spec/controllers/import/bitbucket_server_controller_spec.rb11
-rw-r--r--spec/controllers/import/gitlab_projects_controller_spec.rb2
-rw-r--r--spec/controllers/instance_statistics/cohorts_controller_spec.rb14
-rw-r--r--spec/controllers/notification_settings_controller_spec.rb9
-rw-r--r--spec/controllers/oauth/applications_controller_spec.rb34
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb2
-rw-r--r--spec/controllers/profiles/keys_controller_spec.rb2
-rw-r--r--spec/controllers/profiles/personal_access_tokens_controller_spec.rb6
-rw-r--r--spec/controllers/projects/artifacts_controller_spec.rb63
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb53
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb76
-rw-r--r--spec/controllers/projects/boards_controller_spec.rb57
-rw-r--r--spec/controllers/projects/clusters/applications_controller_spec.rb2
-rw-r--r--spec/controllers/projects/clusters_controller_spec.rb378
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb3
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb24
-rw-r--r--spec/controllers/projects/deploy_keys_controller_spec.rb149
-rw-r--r--spec/controllers/projects/deployments_controller_spec.rb12
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb2
-rw-r--r--spec/controllers/projects/hooks_controller_spec.rb1
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb19
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb374
-rw-r--r--spec/controllers/projects/merge_requests/conflicts_controller_spec.rb1
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb49
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb122
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/projects/mirrors_controller_spec.rb63
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb41
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb46
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb101
-rw-r--r--spec/controllers/projects/registry/repositories_controller_spec.rb5
-rw-r--r--spec/controllers/projects/releases_controller_spec.rb2
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb13
-rw-r--r--spec/controllers/projects/uploads_controller_spec.rb14
-rw-r--r--spec/controllers/projects/wikis_controller_spec.rb56
-rw-r--r--spec/controllers/projects_controller_spec.rb43
-rw-r--r--spec/controllers/registrations_controller_spec.rb2
-rw-r--r--spec/controllers/root_controller_spec.rb4
-rw-r--r--spec/controllers/sessions_controller_spec.rb2
-rw-r--r--spec/controllers/uploads_controller_spec.rb25
-rw-r--r--spec/db/development/import_common_metrics_spec.rb15
-rw-r--r--spec/db/importers/common_metrics_importer_spec.rb131
-rw-r--r--spec/db/production/import_common_metrics_spec.rb15
-rw-r--r--spec/db/schema_spec.rb96
-rw-r--r--spec/factories/board_group_recent_visit.rb9
-rw-r--r--spec/factories/board_project_recent_visit.rb9
-rw-r--r--spec/factories/broadcast_messages.rb12
-rw-r--r--spec/factories/ci/builds.rb57
-rw-r--r--spec/factories/ci/job_artifacts.rb43
-rw-r--r--spec/factories/ci/pipelines.rb12
-rw-r--r--spec/factories/ci/runners.rb12
-rw-r--r--spec/factories/clusters/applications/helm.rb22
-rw-r--r--spec/factories/clusters/clusters.rb15
-rw-r--r--spec/factories/clusters/kubernetes_namespaces.rb18
-rw-r--r--spec/factories/clusters/platforms/kubernetes.rb9
-rw-r--r--spec/factories/clusters/projects.rb8
-rw-r--r--spec/factories/commit_statuses.rb4
-rw-r--r--spec/factories/deployments.rb31
-rw-r--r--spec/factories/emails.rb3
-rw-r--r--spec/factories/environments.rb1
-rw-r--r--spec/factories/events.rb2
-rw-r--r--spec/factories/forked_project_links.rb15
-rw-r--r--spec/factories/gpg_keys.rb4
-rw-r--r--spec/factories/group_members.rb2
-rw-r--r--spec/factories/issues.rb4
-rw-r--r--spec/factories/merge_request_diff_files.rb47
-rw-r--r--spec/factories/merge_request_diffs.rb13
-rw-r--r--spec/factories/merge_requests.rb16
-rw-r--r--spec/factories/milestones.rb2
-rw-r--r--spec/factories/notes.rb2
-rw-r--r--spec/factories/oauth_access_grants.rb2
-rw-r--r--spec/factories/project_auto_devops.rb12
-rw-r--r--spec/factories/project_group_links.rb1
-rw-r--r--spec/factories/project_members.rb2
-rw-r--r--spec/factories/projects.rb63
-rw-r--r--spec/factories/prometheus_metrics.rb18
-rw-r--r--spec/factories/resource_label_events.rb7
-rw-r--r--spec/factories/services.rb11
-rw-r--r--spec/factories/site_statistics.rb1
-rw-r--r--spec/factories/todos.rb2
-rw-r--r--spec/factories/uploads.rb15
-rw-r--r--spec/factories/user_preferences.rb12
-rw-r--r--spec/factories/users.rb8
-rw-r--r--spec/fast_spec_helper.rb2
-rw-r--r--spec/features/admin/admin_groups_spec.rb28
-rw-r--r--spec/features/admin/admin_health_check_spec.rb26
-rw-r--r--spec/features/admin/admin_manage_applications_spec.rb4
-rw-r--r--spec/features/admin/admin_runners_spec.rb167
-rw-r--r--spec/features/admin/admin_settings_spec.rb496
-rw-r--r--spec/features/admin/admin_users_impersonation_tokens_spec.rb5
-rw-r--r--spec/features/admin/admin_users_spec.rb63
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb2
-rw-r--r--spec/features/admin/dashboard_spec.rb28
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb12
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb2
-rw-r--r--spec/features/boards/boards_spec.rb2
-rw-r--r--spec/features/boards/issue_ordering_spec.rb4
-rw-r--r--spec/features/boards/modal_filter_spec.rb2
-rw-r--r--spec/features/boards/new_issue_spec.rb10
-rw-r--r--spec/features/boards/reload_boards_on_browser_back_spec.rb48
-rw-r--r--spec/features/calendar_spec.rb21
-rw-r--r--spec/features/commits/user_uses_quick_actions_spec.rb (renamed from spec/features/commits/user_uses_slash_commands_spec.rb)0
-rw-r--r--spec/features/commits_spec.rb60
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb2
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb2
-rw-r--r--spec/features/dashboard/group_spec.rb10
-rw-r--r--spec/features/dashboard/groups_list_spec.rb4
-rw-r--r--spec/features/dashboard/issuables_counter_spec.rb4
-rw-r--r--spec/features/dashboard/issues_filter_spec.rb31
-rw-r--r--spec/features/dashboard/issues_spec.rb27
-rw-r--r--spec/features/dashboard/label_filter_spec.rb15
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb15
-rw-r--r--spec/features/dashboard/milestone_filter_spec.rb77
-rw-r--r--spec/features/dashboard/milestones_spec.rb6
-rw-r--r--spec/features/dashboard/project_member_activity_index_spec.rb16
-rw-r--r--spec/features/dashboard/projects_spec.rb8
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb8
-rw-r--r--spec/features/explore/new_menu_spec.rb8
-rw-r--r--spec/features/groups/board_sidebar_spec.rb45
-rw-r--r--spec/features/groups/group_settings_spec.rb18
-rw-r--r--spec/features/groups/labels/search_labels_spec.rb48
-rw-r--r--spec/features/groups/labels/sort_labels_spec.rb48
-rw-r--r--spec/features/groups/labels/subscription_spec.rb50
-rw-r--r--spec/features/groups/milestone_spec.rb12
-rw-r--r--spec/features/groups/settings/ci_cd_spec.rb39
-rw-r--r--spec/features/groups/settings/group_badges_spec.rb2
-rw-r--r--spec/features/groups/share_lock_spec.rb4
-rw-r--r--spec/features/groups/show_spec.rb21
-rw-r--r--spec/features/groups_spec.rb41
-rw-r--r--spec/features/import/manifest_import_spec.rb2
-rw-r--r--spec/features/instance_statistics/cohorts_spec.rb2
-rw-r--r--spec/features/instance_statistics/conversational_development_index_spec.rb20
-rw-r--r--spec/features/instance_statistics/instance_statistics.rb23
-rw-r--r--spec/features/issuables/close_reopen_report_toggle_spec.rb40
-rw-r--r--spec/features/issues/award_emoji_spec.rb146
-rw-r--r--spec/features/issues/award_spec.rb51
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb12
-rw-r--r--spec/features/issues/filtered_search/dropdown_emoji_spec.rb22
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb18
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb10
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/visual_tokens_spec.rb2
-rw-r--r--spec/features/issues/form_spec.rb2
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb49
-rw-r--r--spec/features/issues/issue_detail_spec.rb17
-rw-r--r--spec/features/issues/notes_on_issues_spec.rb23
-rw-r--r--spec/features/issues/resource_label_events_spec.rb67
-rw-r--r--spec/features/issues/rss_spec.rb (renamed from spec/features/projects/issues/rss_spec.rb)0
-rw-r--r--spec/features/issues/user_comments_on_issue_spec.rb (renamed from spec/features/projects/issues/user_comments_on_issue_spec.rb)0
-rw-r--r--spec/features/issues/user_creates_branch_and_merge_request_spec.rb8
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb90
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb25
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb347
-rw-r--r--spec/features/issues/user_sees_breadcrumb_links_spec.rb18
-rw-r--r--spec/features/issues/user_sorts_issues_spec.rb63
-rw-r--r--spec/features/issues/user_toggles_subscription_spec.rb (renamed from spec/features/projects/issues/user_toggles_subscription_spec.rb)0
-rw-r--r--spec/features/issues/user_uses_quick_actions_spec.rb365
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb307
-rw-r--r--spec/features/issues/user_views_issue_spec.rb50
-rw-r--r--spec/features/issues/user_views_issues_spec.rb (renamed from spec/features/projects/issues/user_views_issues_spec.rb)0
-rw-r--r--spec/features/labels_hierarchy_spec.rb2
-rw-r--r--spec/features/markdown/markdown_spec.rb6
-rw-r--r--spec/features/merge_request/user_accepts_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_accepts_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_closes_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_closes_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_comments_on_commit_spec.rb (renamed from spec/features/projects/merge_requests/user_comments_on_commit_spec.rb)0
-rw-r--r--spec/features/merge_request/user_comments_on_diff_spec.rb173
-rw-r--r--spec/features/merge_request/user_comments_on_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb9
-rw-r--r--spec/features/merge_request/user_creates_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_creates_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_creates_mr_spec.rb25
-rw-r--r--spec/features/merge_request/user_edits_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_edits_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_edits_mr_spec.rb4
-rw-r--r--spec/features/merge_request/user_manages_subscription_spec.rb (renamed from spec/features/projects/merge_requests/user_manages_subscription_spec.rb)0
-rw-r--r--spec/features/merge_request/user_merges_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_merges_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb35
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb4
-rw-r--r--spec/features/merge_request/user_rebases_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_rebases_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_reopens_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_resolves_conflicts_spec.rb18
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb79
-rw-r--r--spec/features/merge_request/user_reverts_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_reverts_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb17
-rw-r--r--spec/features/merge_request/user_sees_breadcrumb_links_spec.rb18
-rw-r--r--spec/features/merge_request/user_sees_deployment_widget_spec.rb81
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb55
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb32
-rw-r--r--spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb8
-rw-r--r--spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb8
-rw-r--r--spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb8
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb3
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb10
-rw-r--r--spec/features/merge_request/user_uses_quick_actions_spec.rb (renamed from spec/features/merge_request/user_uses_slash_commands_spec.rb)0
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb60
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb92
-rw-r--r--spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb (renamed from spec/features/projects/merge_requests/user_views_user_status_on_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_requests/user_sorts_merge_requests_spec.rb92
-rw-r--r--spec/features/merge_requests/user_views_all_merge_requests_spec.rb (renamed from spec/features/projects/merge_requests/user_views_all_merge_requests_spec.rb)0
-rw-r--r--spec/features/merge_requests/user_views_closed_merge_requests_spec.rb (renamed from spec/features/projects/merge_requests/user_views_closed_merge_requests_spec.rb)0
-rw-r--r--spec/features/merge_requests/user_views_merged_merge_requests_spec.rb (renamed from spec/features/projects/merge_requests/user_views_merged_merge_requests_spec.rb)0
-rw-r--r--spec/features/merge_requests/user_views_open_merge_requests_spec.rb (renamed from spec/features/projects/merge_requests/user_views_open_merge_requests_spec.rb)0
-rw-r--r--spec/features/milestones/user_creates_milestone_spec.rb2
-rw-r--r--spec/features/milestones/user_deletes_milestone_spec.rb44
-rw-r--r--spec/features/profiles/personal_access_tokens_spec.rb3
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb252
-rw-r--r--spec/features/profiles/user_manages_applications_spec.rb4
-rw-r--r--spec/features/projects/activity/user_sees_activity_spec.rb14
-rw-r--r--spec/features/projects/activity/user_sees_private_activity_spec.rb35
-rw-r--r--spec/features/projects/artifacts/user_downloads_artifacts_spec.rb14
-rw-r--r--spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb172
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb2
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb56
-rw-r--r--spec/features/projects/blobs/edit_spec.rb34
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb53
-rw-r--r--spec/features/projects/clusters/user_spec.rb40
-rw-r--r--spec/features/projects/clusters_spec.rb31
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb8
-rw-r--r--spec/features/projects/commit/comments/user_adds_comment_spec.rb10
-rw-r--r--spec/features/projects/commit/user_comments_on_commit_spec.rb6
-rw-r--r--spec/features/projects/environments/environment_spec.rb75
-rw-r--r--spec/features/projects/environments/environments_spec.rb99
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb2
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb2
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb4
-rw-r--r--spec/features/projects/files/user_deletes_files_spec.rb2
-rw-r--r--spec/features/projects/fork_spec.rb12
-rw-r--r--spec/features/projects/import_export/export_file_spec.rb5
-rw-r--r--spec/features/projects/import_export/import_file_object_storage_spec.rb103
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb21
-rw-r--r--spec/features/projects/import_export/namespace_export_file_spec.rb68
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin3368 -> 3360 bytes
-rw-r--r--spec/features/projects/issues/user_creates_issue_spec.rb90
-rw-r--r--spec/features/projects/issues/user_edits_issue_spec.rb25
-rw-r--r--spec/features/projects/issues/user_sorts_issues_spec.rb39
-rw-r--r--spec/features/projects/issues/user_views_issue_spec.rb50
-rw-r--r--spec/features/projects/jobs/permissions_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb16
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb412
-rw-r--r--spec/features/projects/labels/sort_labels_spec.rb48
-rw-r--r--spec/features/projects/members/invite_group_spec.rb195
-rw-r--r--spec/features/projects/members/share_with_group_spec.rb196
-rw-r--r--spec/features/projects/merge_requests/user_comments_on_diff_spec.rb173
-rw-r--r--spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb63
-rw-r--r--spec/features/projects/merge_requests/user_views_diffs_spec.rb58
-rw-r--r--spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb92
-rw-r--r--spec/features/projects/new_project_spec.rb29
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb123
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb58
-rw-r--r--spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb2
-rw-r--r--spec/features/projects/settings/integration_settings_spec.rb1
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb24
-rw-r--r--spec/features/projects/settings/project_badges_spec.rb2
-rw-r--r--spec/features/projects/settings/user_changes_default_branch_spec.rb29
-rw-r--r--spec/features/projects/settings/user_manages_group_links_spec.rb6
-rw-r--r--spec/features/projects/settings/user_tags_project_spec.rb14
-rw-r--r--spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb61
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb231
-rw-r--r--spec/features/projects/tags/user_views_tags_spec.rb69
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb2
-rw-r--r--spec/features/projects/tree/create_file_spec.rb2
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb16
-rw-r--r--spec/features/projects/user_creates_project_spec.rb5
-rw-r--r--spec/features/projects/view_on_env_spec.rb2
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb28
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_deletes_wiki_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb26
-rw-r--r--spec/features/projects/wiki/user_views_wiki_empty_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb33
-rw-r--r--spec/features/projects_spec.rb91
-rw-r--r--spec/features/runners_spec.rb4
-rw-r--r--spec/features/search/user_searches_for_wiki_pages_spec.rb2
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb16
-rw-r--r--spec/features/search/user_uses_search_filters_spec.rb4
-rw-r--r--spec/features/security/dashboard_access_spec.rb14
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb2
-rw-r--r--spec/features/snippets/show_spec.rb44
-rw-r--r--spec/features/snippets/user_sees_breadcrumb_links.rb17
-rw-r--r--spec/features/u2f_spec.rb14
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_group_spec.rb2
-rw-r--r--spec/features/usage_stats_consent_spec.rb45
-rw-r--r--spec/features/user_sorts_things_spec.rb2
-rw-r--r--spec/features/users/login_spec.rb24
-rw-r--r--spec/features/users/overview_spec.rb150
-rw-r--r--spec/features/users/show_spec.rb2
-rw-r--r--spec/finders/admin/runners_finder_spec.rb73
-rw-r--r--spec/finders/applications_finder_spec.rb22
-rw-r--r--spec/finders/autocomplete/group_finder_spec.rb58
-rw-r--r--spec/finders/autocomplete/move_to_project_finder_spec.rb109
-rw-r--r--spec/finders/autocomplete/project_finder_spec.rb55
-rw-r--r--spec/finders/autocomplete/users_finder_spec.rb112
-rw-r--r--spec/finders/autocomplete_users_finder_spec.rb112
-rw-r--r--spec/finders/awarded_emoji_finder_spec.rb25
-rw-r--r--spec/finders/branches_finder_spec.rb33
-rw-r--r--spec/finders/contributed_projects_finder_spec.rb10
-rw-r--r--spec/finders/environments_finder_spec.rb16
-rw-r--r--spec/finders/fork_projects_finder_spec.rb15
-rw-r--r--spec/finders/group_descendants_finder_spec.rb16
-rw-r--r--spec/finders/group_labels_finder_spec.rb42
-rw-r--r--spec/finders/issues_finder_spec.rb72
-rw-r--r--spec/finders/labels_finder_spec.rb10
-rw-r--r--spec/finders/license_template_finder_spec.rb49
-rw-r--r--spec/finders/merge_requests_finder_spec.rb84
-rw-r--r--spec/finders/move_to_project_finder_spec.rb97
-rw-r--r--spec/finders/notes_finder_spec.rb29
-rw-r--r--spec/finders/pending_todos_finder_spec.rb63
-rw-r--r--spec/finders/personal_access_tokens_finder_spec.rb8
-rw-r--r--spec/finders/pipelines_finder_spec.rb2
-rw-r--r--spec/finders/projects_finder_spec.rb7
-rw-r--r--spec/finders/snippets_finder_spec.rb45
-rw-r--r--spec/finders/template_finder_spec.rb51
-rw-r--r--spec/finders/todos_finder_spec.rb17
-rw-r--r--spec/finders/user_finder_spec.rb179
-rw-r--r--spec/finders/user_recent_events_finder_spec.rb46
-rw-r--r--spec/finders/users_finder_spec.rb6
-rw-r--r--spec/finders/users_with_pending_todos_finder_spec.rb19
-rw-r--r--spec/fixtures/api/schemas/ci_detailed_status.json24
-rw-r--r--spec/fixtures/api/schemas/deployment.json97
-rw-r--r--spec/fixtures/api/schemas/entities/commit.json26
-rw-r--r--spec/fixtures/api/schemas/entities/diff_line.json14
-rw-r--r--spec/fixtures/api/schemas/entities/diff_line_parallel.json11
-rw-r--r--spec/fixtures/api/schemas/entities/diff_viewer.json8
-rw-r--r--spec/fixtures/api/schemas/entities/issue_board.json39
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json1
-rw-r--r--spec/fixtures/api/schemas/entities/note_user_entity.json21
-rw-r--r--spec/fixtures/api/schemas/entities/user.json12
-rw-r--r--spec/fixtures/api/schemas/environment.json38
-rw-r--r--spec/fixtures/api/schemas/http_method.json5
-rw-r--r--spec/fixtures/api/schemas/issue.json2
-rw-r--r--spec/fixtures/api/schemas/job.json24
-rw-r--r--spec/fixtures/api/schemas/job/artifact.json11
-rw-r--r--spec/fixtures/api/schemas/job/deployment_status.json25
-rw-r--r--spec/fixtures/api/schemas/job/job.json35
-rw-r--r--spec/fixtures/api/schemas/job/job_details.json24
-rw-r--r--spec/fixtures/api/schemas/job/runner.json17
-rw-r--r--spec/fixtures/api/schemas/job/runners.json12
-rw-r--r--spec/fixtures/api/schemas/job/trigger.json28
-rw-r--r--spec/fixtures/api/schemas/pipeline_stage.json9
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/branch.json2
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/license.json30
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/merge_requests.json26
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/template.json12
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/template_list.json15
-rw-r--r--spec/fixtures/api/schemas/status/action.json22
-rw-r--r--spec/fixtures/api/schemas/status/ci_detailed_status.json26
-rw-r--r--spec/fixtures/api/schemas/status/illustration.json19
-rw-r--r--spec/fixtures/api/schemas/types/nullable_string.json6
-rw-r--r--spec/fixtures/authentication/saml2_response.xml56
-rw-r--r--spec/fixtures/codequality/codequality.json1
-rw-r--r--spec/fixtures/codequality/codequality.json.gzbin0 -> 101478 bytes
-rw-r--r--spec/fixtures/emails/merge_request_multiple_patches.eml181
-rw-r--r--spec/fixtures/emails/merge_request_with_conflicting_patch.eml45
-rw-r--r--spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml44
-rw-r--r--spec/fixtures/emails/valid_merge_request_with_patch.eml151
-rw-r--r--spec/fixtures/git-cheat-sheet.pdf130423
-rw-r--r--spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml10
-rw-r--r--spec/fixtures/patchfiles/0001-A-commit-from-a-patch.patch19
-rw-r--r--spec/fixtures/patchfiles/0001-This-does-not-apply-to-the-feature-branch.patch23
-rw-r--r--spec/fixtures/security-reports/feature-branch.zipbin0 -> 7163 bytes
-rw-r--r--spec/fixtures/security-reports/feature-branch/gl-container-scanning-report.json18
-rw-r--r--spec/fixtures/security-reports/feature-branch/gl-dast-report.json40
-rw-r--r--spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json46
-rw-r--r--spec/fixtures/security-reports/feature-branch/gl-license-management-report.json242
-rw-r--r--spec/fixtures/security-reports/feature-branch/gl-sast-report.json944
-rw-r--r--spec/fixtures/security-reports/master.zipbin0 -> 6710 bytes
-rw-r--r--spec/fixtures/security-reports/master/gl-container-scanning-report.json18
-rw-r--r--spec/fixtures/security-reports/master/gl-dast-report.json40
-rw-r--r--spec/fixtures/security-reports/master/gl-dependency-scanning-report.json35
-rw-r--r--spec/fixtures/security-reports/master/gl-license-management-report.json150
-rw-r--r--spec/fixtures/security-reports/master/gl-sast-report.json944
-rw-r--r--spec/fixtures/ssh_host_example_key.pub2
-rw-r--r--spec/fixtures/trace/sample_trace24
-rw-r--r--spec/fixtures/valid.po3
-rw-r--r--spec/graphql/types/permission_types/project_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb63
-rw-r--r--spec/helpers/auth_helper_spec.rb10
-rw-r--r--spec/helpers/auto_devops_helper_spec.rb10
-rw-r--r--spec/helpers/avatars_helper_spec.rb24
-rw-r--r--spec/helpers/blob_helper_spec.rb58
-rw-r--r--spec/helpers/button_helper_spec.rb26
-rw-r--r--spec/helpers/commits_helper_spec.rb2
-rw-r--r--spec/helpers/events_helper_spec.rb69
-rw-r--r--spec/helpers/icons_helper_spec.rb20
-rw-r--r--spec/helpers/import_helper_spec.rb10
-rw-r--r--spec/helpers/issuables_helper_spec.rb2
-rw-r--r--spec/helpers/labels_helper_spec.rb25
-rw-r--r--spec/helpers/markup_helper_spec.rb69
-rw-r--r--spec/helpers/namespaces_helper_spec.rb5
-rw-r--r--spec/helpers/preferences_helper_spec.rb7
-rw-r--r--spec/helpers/profiles_helper_spec.rb34
-rw-r--r--spec/helpers/projects_helper_spec.rb12
-rw-r--r--spec/helpers/search_helper_spec.rb35
-rw-r--r--spec/helpers/storage_health_helper_spec.rb20
-rw-r--r--spec/helpers/submodule_helper_spec.rb75
-rw-r--r--spec/helpers/tab_helper_spec.rb76
-rw-r--r--spec/helpers/time_helper_spec.rb23
-rw-r--r--spec/helpers/tree_helper_spec.rb45
-rw-r--r--spec/helpers/users_helper_spec.rb24
-rw-r--r--spec/helpers/visibility_level_helper_spec.rb12
-rw-r--r--spec/javascripts/.eslintrc.yml2
-rw-r--r--spec/javascripts/ajax_loading_spinner_spec.js9
-rw-r--r--spec/javascripts/api_spec.js64
-rw-r--r--spec/javascripts/avatar_helper_spec.js2
-rw-r--r--spec/javascripts/awards_handler_spec.js692
-rw-r--r--spec/javascripts/badges/components/badge_form_spec.js153
-rw-r--r--spec/javascripts/badges/components/badge_list_row_spec.js5
-rw-r--r--spec/javascripts/badges/components/badge_list_spec.js3
-rw-r--r--spec/javascripts/badges/components/badge_settings_spec.js9
-rw-r--r--spec/javascripts/badges/components/badge_spec.js3
-rw-r--r--spec/javascripts/badges/dummy_badge.js3
-rw-r--r--spec/javascripts/badges/store/actions_spec.js7
-rw-r--r--spec/javascripts/behaviors/autosize_spec.js1
-rw-r--r--spec/javascripts/behaviors/bind_in_out_spec.js113
-rw-r--r--spec/javascripts/behaviors/copy_as_gfm_spec.js6
-rw-r--r--spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js4
-rw-r--r--spec/javascripts/behaviors/markdown/highlight_current_user_spec.js55
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js19
-rw-r--r--spec/javascripts/behaviors/requires_input_spec.js30
-rw-r--r--spec/javascripts/behaviors/secret_values_spec.js36
-rw-r--r--spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js109
-rw-r--r--spec/javascripts/blob/3d_viewer/mesh_object_spec.js20
-rw-r--r--spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js14
-rw-r--r--spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js18
-rw-r--r--spec/javascripts/blob/blob_file_dropzone_spec.js2
-rw-r--r--spec/javascripts/blob/blob_fork_suggestion_spec.js5
-rw-r--r--spec/javascripts/blob/notebook/index_spec.js86
-rw-r--r--spec/javascripts/blob/pdf/index_spec.js32
-rw-r--r--spec/javascripts/blob/sketch/index_spec.js108
-rw-r--r--spec/javascripts/blob/viewer/index_spec.js48
-rw-r--r--spec/javascripts/boards/board_blank_state_spec.js84
-rw-r--r--spec/javascripts/boards/board_card_spec.js65
-rw-r--r--spec/javascripts/boards/board_list_spec.js100
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js35
-rw-r--r--spec/javascripts/boards/boards_store_spec.js133
-rw-r--r--spec/javascripts/boards/components/board_spec.js41
-rw-r--r--spec/javascripts/boards/components/issue_due_date_spec.js64
-rw-r--r--spec/javascripts/boards/components/issue_time_estimate_spec.js40
-rw-r--r--spec/javascripts/boards/issue_card_spec.js26
-rw-r--r--spec/javascripts/boards/issue_spec.js52
-rw-r--r--spec/javascripts/boards/list_spec.js107
-rw-r--r--spec/javascripts/boards/mock_data.js5
-rw-r--r--spec/javascripts/boards/modal_store_spec.js2
-rw-r--r--spec/javascripts/boards/utils/query_data_spec.js27
-rw-r--r--spec/javascripts/bootstrap_jquery_spec.js74
-rw-r--r--spec/javascripts/bootstrap_linked_tabs_spec.js87
-rw-r--r--spec/javascripts/breakpoints_spec.js6
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js66
-rw-r--r--spec/javascripts/ci_variable_list/ci_variable_list_spec.js26
-rw-r--r--spec/javascripts/ci_variable_list/native_form_variable_list_spec.js10
-rw-r--r--spec/javascripts/close_reopen_report_toggle_spec.js10
-rw-r--r--spec/javascripts/clusters/clusters_bundle_spec.js213
-rw-r--r--spec/javascripts/clusters/components/application_row_spec.js41
-rw-r--r--spec/javascripts/clusters/components/applications_spec.js12
-rw-r--r--spec/javascripts/clusters/services/mock_data.js128
-rw-r--r--spec/javascripts/clusters/stores/clusters_store_spec.js20
-rw-r--r--spec/javascripts/collapsed_sidebar_todo_spec.js50
-rw-r--r--spec/javascripts/comment_type_toggle_spec.js114
-rw-r--r--spec/javascripts/commit/commit_pipeline_status_component_spec.js35
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js37
-rw-r--r--spec/javascripts/commit_merge_requests_spec.js11
-rw-r--r--spec/javascripts/create_item_dropdown_spec.js49
-rw-r--r--spec/javascripts/create_merge_request_dropdown_spec.js1
-rw-r--r--spec/javascripts/cycle_analytics/banner_spec.js23
-rw-r--r--spec/javascripts/datetime_utility_spec.js170
-rw-r--r--spec/javascripts/deploy_keys/components/app_spec.js7
-rw-r--r--spec/javascripts/deploy_keys/components/key_spec.js3
-rw-r--r--spec/javascripts/diff_comments_store_spec.js12
-rw-r--r--spec/javascripts/diffs/components/app_spec.js83
-rw-r--r--spec/javascripts/diffs/components/changed_files_spec.js105
-rw-r--r--spec/javascripts/diffs/components/commit_item_spec.js164
-rw-r--r--spec/javascripts/diffs/components/commit_widget_spec.js24
-rw-r--r--spec/javascripts/diffs/components/compare_versions_spec.js126
-rw-r--r--spec/javascripts/diffs/components/diff_content_spec.js82
-rw-r--r--spec/javascripts/diffs/components/diff_discussions_spec.js78
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js126
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js66
-rw-r--r--spec/javascripts/diffs/components/diff_gutter_avatars_spec.js2
-rw-r--r--spec/javascripts/diffs/components/diff_line_gutter_content_spec.js58
-rw-r--r--spec/javascripts/diffs/components/diff_line_note_form_spec.js39
-rw-r--r--spec/javascripts/diffs/components/file_row_stats_spec.js33
-rw-r--r--spec/javascripts/diffs/components/image_diff_overlay_spec.js146
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js13
-rw-r--r--spec/javascripts/diffs/components/parallel_diff_view_spec.js10
-rw-r--r--spec/javascripts/diffs/components/tree_list_spec.js188
-rw-r--r--spec/javascripts/diffs/create_diffs_store.js15
-rw-r--r--spec/javascripts/diffs/mock_data/diff_discussions.js47
-rw-r--r--spec/javascripts/diffs/mock_data/diff_file.js259
-rw-r--r--spec/javascripts/diffs/mock_data/diff_with_commit.js7
-rw-r--r--spec/javascripts/diffs/mock_data/merge_request_diffs.js42
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js490
-rw-r--r--spec/javascripts/diffs/store/getters_spec.js135
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js286
-rw-r--r--spec/javascripts/diffs/store/utils_spec.js438
-rw-r--r--spec/javascripts/dirty_submit/dirty_submit_collection_spec.js29
-rw-r--r--spec/javascripts/dirty_submit/dirty_submit_factory_spec.js18
-rw-r--r--spec/javascripts/dirty_submit/dirty_submit_form_spec.js36
-rw-r--r--spec/javascripts/dirty_submit/helper.js31
-rw-r--r--spec/javascripts/droplab/drop_down_spec.js251
-rw-r--r--spec/javascripts/droplab/hook_spec.js34
-rw-r--r--spec/javascripts/droplab/plugins/ajax_filter_spec.js10
-rw-r--r--spec/javascripts/droplab/plugins/ajax_spec.js7
-rw-r--r--spec/javascripts/droplab/plugins/input_setter_spec.js100
-rw-r--r--spec/javascripts/dropzone_input_spec.js66
-rw-r--r--spec/javascripts/emoji_spec.js62
-rw-r--r--spec/javascripts/environments/emtpy_state_spec.js23
-rw-r--r--spec/javascripts/environments/environment_actions_spec.js123
-rw-r--r--spec/javascripts/environments/environment_item_spec.js56
-rw-r--r--spec/javascripts/environments/environments_app_spec.js104
-rw-r--r--spec/javascripts/environments/environments_store_spec.js15
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js104
-rw-r--r--spec/javascripts/feature_highlight/feature_highlight_helper_spec.js17
-rw-r--r--spec/javascripts/feature_highlight/feature_highlight_spec.js8
-rw-r--r--spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js32
-rw-r--r--spec/javascripts/filtered_search/dropdown_user_spec.js13
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js117
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js52
-rw-r--r--spec/javascripts/filtered_search/filtered_search_token_keys_spec.js105
-rw-r--r--spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js42
-rw-r--r--spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js258
-rw-r--r--spec/javascripts/filtered_search/recent_searches_root_spec.js2
-rw-r--r--spec/javascripts/filtered_search/services/recent_searches_service_spec.js27
-rw-r--r--spec/javascripts/filtered_search/stores/recent_searches_store_spec.js10
-rw-r--r--spec/javascripts/fixtures/admin_users.rb29
-rw-r--r--spec/javascripts/fixtures/application_settings.rb34
-rw-r--r--spec/javascripts/fixtures/groups.rb10
-rw-r--r--spec/javascripts/fixtures/jobs.rb23
-rw-r--r--spec/javascripts/fixtures/merge_requests_diffs.rb14
-rw-r--r--spec/javascripts/fixtures/projects.rb10
-rw-r--r--spec/javascripts/flash_spec.js146
-rw-r--r--spec/javascripts/fly_out_nav_spec.js127
-rw-r--r--spec/javascripts/frequent_items/components/app_spec.js3
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js6
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_list_spec.js6
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js1
-rw-r--r--spec/javascripts/gfm_auto_complete_spec.js100
-rw-r--r--spec/javascripts/gl_dropdown_spec.js106
-rw-r--r--spec/javascripts/gl_field_errors_spec.js41
-rw-r--r--spec/javascripts/gl_form_spec.js7
-rw-r--r--spec/javascripts/gpg_badges_spec.js2
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_graph_spec.js106
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_spec.js4
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_util_spec.js245
-rw-r--r--spec/javascripts/groups/components/app_spec.js130
-rw-r--r--spec/javascripts/groups/components/group_folder_spec.js3
-rw-r--r--spec/javascripts/groups/components/group_item_spec.js15
-rw-r--r--spec/javascripts/groups/components/groups_spec.js17
-rw-r--r--spec/javascripts/groups/components/item_actions_spec.js9
-rw-r--r--spec/javascripts/groups/components/item_caret_spec.js3
-rw-r--r--spec/javascripts/groups/components/item_stats_spec.js25
-rw-r--r--spec/javascripts/groups/components/item_stats_value_spec.js6
-rw-r--r--spec/javascripts/groups/components/item_type_icon_spec.js5
-rw-r--r--spec/javascripts/groups/mock_data.js3
-rw-r--r--spec/javascripts/groups/service/groups_service_spec.js3
-rw-r--r--spec/javascripts/groups/store/groups_store_spec.js29
-rw-r--r--spec/javascripts/header_spec.js5
-rw-r--r--spec/javascripts/helpers/class_spec_helper_spec.js6
-rw-r--r--spec/javascripts/helpers/locale_helper.js2
-rw-r--r--spec/javascripts/helpers/set_timeout_promise_helper.js7
-rw-r--r--spec/javascripts/helpers/user_mock_data_helper.js14
-rw-r--r--spec/javascripts/helpers/vue_mount_component_helper.js10
-rw-r--r--spec/javascripts/helpers/vue_resource_helper.js3
-rw-r--r--spec/javascripts/ide/components/branches/item_spec.js5
-rw-r--r--spec/javascripts/ide/components/branches/search_list_spec.js5
-rw-r--r--spec/javascripts/ide/components/changed_file_icon_spec.js46
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_item_spec.js6
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/message_field_spec.js23
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js4
-rw-r--r--spec/javascripts/ide/components/file_finder/index_spec.js9
-rw-r--r--spec/javascripts/ide/components/file_row_extra_spec.js159
-rw-r--r--spec/javascripts/ide/components/file_templates/bar_spec.js117
-rw-r--r--spec/javascripts/ide/components/file_templates/dropdown_spec.js201
-rw-r--r--spec/javascripts/ide/components/ide_spec.js9
-rw-r--r--spec/javascripts/ide/components/ide_status_bar_spec.js10
-rw-r--r--spec/javascripts/ide/components/jobs/detail_spec.js3
-rw-r--r--spec/javascripts/ide/components/merge_requests/item_spec.js5
-rw-r--r--spec/javascripts/ide/components/merge_requests/list_spec.js5
-rw-r--r--spec/javascripts/ide/components/new_dropdown/index_spec.js1
-rw-r--r--spec/javascripts/ide/components/new_dropdown/upload_spec.js57
-rw-r--r--spec/javascripts/ide/components/panes/right_spec.js24
-rw-r--r--spec/javascripts/ide/components/preview/clientside_spec.js3
-rw-r--r--spec/javascripts/ide/components/repo_commit_section_spec.js59
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js16
-rw-r--r--spec/javascripts/ide/components/repo_file_spec.js124
-rw-r--r--spec/javascripts/ide/components/repo_loading_file_spec.js63
-rw-r--r--spec/javascripts/ide/components/repo_tab_spec.js4
-rw-r--r--spec/javascripts/ide/components/shared/tokened_input_spec.js5
-rw-r--r--spec/javascripts/ide/helpers.js4
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js35
-rw-r--r--spec/javascripts/ide/stores/actions/merge_request_spec.js36
-rw-r--r--spec/javascripts/ide/stores/actions/project_spec.js5
-rw-r--r--spec/javascripts/ide/stores/actions/tree_spec.js1
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js28
-rw-r--r--spec/javascripts/ide/stores/modules/branches/actions_spec.js1
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js28
-rw-r--r--spec/javascripts/ide/stores/modules/file_templates/actions_spec.js441
-rw-r--r--spec/javascripts/ide/stores/modules/file_templates/getters_spec.js59
-rw-r--r--spec/javascripts/ide/stores/modules/file_templates/mutations_spec.js69
-rw-r--r--spec/javascripts/ide/stores/modules/pane/actions_spec.js66
-rw-r--r--spec/javascripts/ide/stores/modules/pane/getters_spec.js55
-rw-r--r--spec/javascripts/ide/stores/modules/pane/mutations_spec.js42
-rw-r--r--spec/javascripts/ide/stores/modules/pipelines/actions_spec.js12
-rw-r--r--spec/javascripts/ide/stores/mutations/merge_request_spec.js2
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js35
-rw-r--r--spec/javascripts/image_diff/helpers/badge_helper_spec.js1
-rw-r--r--spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js7
-rw-r--r--spec/javascripts/image_diff/helpers/dom_helper_spec.js2
-rw-r--r--spec/javascripts/image_diff/helpers/utils_helper_spec.js18
-rw-r--r--spec/javascripts/image_diff/image_badge_spec.js30
-rw-r--r--spec/javascripts/image_diff/image_diff_spec.js20
-rw-r--r--spec/javascripts/image_diff/init_discussion_tab_spec.js15
-rw-r--r--spec/javascripts/image_diff/replaced_image_diff_spec.js95
-rw-r--r--spec/javascripts/importer_status_spec.js64
-rw-r--r--spec/javascripts/integrations/integration_settings_form_spec.js82
-rw-r--r--spec/javascripts/issuable_spec.js5
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js275
-rw-r--r--spec/javascripts/issue_show/components/description_spec.js49
-rw-r--r--spec/javascripts/issue_show/components/edit_actions_spec.js72
-rw-r--r--spec/javascripts/issue_show/components/fields/description_spec.js24
-rw-r--r--spec/javascripts/issue_show/components/fields/description_template_spec.js16
-rw-r--r--spec/javascripts/issue_show/components/fields/title_spec.js12
-rw-r--r--spec/javascripts/issue_show/components/form_spec.js23
-rw-r--r--spec/javascripts/issue_show/components/title_spec.js21
-rw-r--r--spec/javascripts/issue_show/index_spec.js19
-rw-r--r--spec/javascripts/issue_spec.js20
-rw-r--r--spec/javascripts/job_spec.js314
-rw-r--r--spec/javascripts/jobs/components/artifacts_block_spec.js120
-rw-r--r--spec/javascripts/jobs/components/commit_block_spec.js88
-rw-r--r--spec/javascripts/jobs/components/empty_state_spec.js93
-rw-r--r--spec/javascripts/jobs/components/environments_block_spec.js149
-rw-r--r--spec/javascripts/jobs/components/erased_block_spec.js56
-rw-r--r--spec/javascripts/jobs/components/job_app_spec.js639
-rw-r--r--spec/javascripts/jobs/components/job_container_item_spec.js99
-rw-r--r--spec/javascripts/jobs/components/job_log_controllers_spec.js208
-rw-r--r--spec/javascripts/jobs/components/job_log_spec.js65
-rw-r--r--spec/javascripts/jobs/components/jobs_container_spec.js131
-rw-r--r--spec/javascripts/jobs/components/sidebar_detail_row_spec.js61
-rw-r--r--spec/javascripts/jobs/components/sidebar_spec.js189
-rw-r--r--spec/javascripts/jobs/components/stages_dropdown_spec.js59
-rw-r--r--spec/javascripts/jobs/components/stuck_block_spec.js81
-rw-r--r--spec/javascripts/jobs/components/trigger_block_spec.js76
-rw-r--r--spec/javascripts/jobs/header_spec.js98
-rw-r--r--spec/javascripts/jobs/job_details_mediator_spec.js39
-rw-r--r--spec/javascripts/jobs/job_store_spec.js26
-rw-r--r--spec/javascripts/jobs/mixins/delayed_job_mixin_spec.js93
-rw-r--r--spec/javascripts/jobs/mock_data.js1053
-rw-r--r--spec/javascripts/jobs/sidebar_detail_row_spec.js61
-rw-r--r--spec/javascripts/jobs/sidebar_details_block_spec.js139
-rw-r--r--spec/javascripts/jobs/store/actions_spec.js512
-rw-r--r--spec/javascripts/jobs/store/getters_spec.js212
-rw-r--r--spec/javascripts/jobs/store/helpers.js6
-rw-r--r--spec/javascripts/jobs/store/mutations_spec.js230
-rw-r--r--spec/javascripts/jobs/stuck_block_spec.js81
-rw-r--r--spec/javascripts/labels_issue_sidebar_spec.js134
-rw-r--r--spec/javascripts/labels_select_spec.js14
-rw-r--r--spec/javascripts/landing_spec.js70
-rw-r--r--spec/javascripts/lazy_loader_spec.js193
-rw-r--r--spec/javascripts/lib/utils/accessor_spec.js14
-rw-r--r--spec/javascripts/lib/utils/ajax_cache_spec.js66
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js260
-rw-r--r--spec/javascripts/lib/utils/csrf_token_spec.js5
-rw-r--r--spec/javascripts/lib/utils/datefix_spec.js27
-rw-r--r--spec/javascripts/lib/utils/datetime_utility_spec.js378
-rw-r--r--spec/javascripts/lib/utils/dom_utils_spec.js2
-rw-r--r--spec/javascripts/lib/utils/mock_data.js9
-rw-r--r--spec/javascripts/lib/utils/navigation_utility_spec.js23
-rw-r--r--spec/javascripts/lib/utils/number_utility_spec.js12
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js28
-rw-r--r--spec/javascripts/lib/utils/sticky_spec.js29
-rw-r--r--spec/javascripts/lib/utils/text_markdown_spec.js173
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js8
-rw-r--r--spec/javascripts/lib/utils/users_cache_spec.js52
-rw-r--r--spec/javascripts/line_highlighter_spec.js417
-rw-r--r--spec/javascripts/locale/ensure_single_line_spec.js38
-rw-r--r--spec/javascripts/merge_request_spec.js185
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js8
-rw-r--r--spec/javascripts/mini_pipeline_graph_dropdown_spec.js12
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js96
-rw-r--r--spec/javascripts/monitoring/dashboard_state_spec.js28
-rw-r--r--spec/javascripts/monitoring/graph/deployment_spec.js6
-rw-r--r--spec/javascripts/monitoring/graph/flag_spec.js35
-rw-r--r--spec/javascripts/monitoring/graph/legend_spec.js2
-rw-r--r--spec/javascripts/monitoring/graph/track_info_spec.js2
-rw-r--r--spec/javascripts/monitoring/graph/track_line_spec.js2
-rw-r--r--spec/javascripts/monitoring/graph_path_spec.js6
-rw-r--r--spec/javascripts/monitoring/graph_spec.js24
-rw-r--r--spec/javascripts/monitoring/mock_data.js1
-rw-r--r--spec/javascripts/monitoring/monitoring_store_spec.js4
-rw-r--r--spec/javascripts/monitoring/utils/multiple_time_series_spec.js2
-rw-r--r--spec/javascripts/new_branch_spec.js355
-rw-r--r--spec/javascripts/notebook/cells/code_spec.js4
-rw-r--r--spec/javascripts/notebook/cells/markdown_spec.js30
-rw-r--r--spec/javascripts/notebook/cells/output/html_sanitize_tests.js8
-rw-r--r--spec/javascripts/notebook/cells/output/html_spec.js2
-rw-r--r--spec/javascripts/notebook/cells/output/index_spec.js14
-rw-r--r--spec/javascripts/notebook/cells/prompt_spec.js4
-rw-r--r--spec/javascripts/notebook/index_spec.js10
-rw-r--r--spec/javascripts/notes/components/comment_form_spec.js12
-rw-r--r--spec/javascripts/notes/components/diff_with_note_spec.js7
-rw-r--r--spec/javascripts/notes/components/discussion_filter_spec.js86
-rw-r--r--spec/javascripts/notes/components/note_actions_spec.js20
-rw-r--r--spec/javascripts/notes/components/note_app_spec.js9
-rw-r--r--spec/javascripts/notes/components/note_awards_list_spec.js4
-rw-r--r--spec/javascripts/notes/components/note_form_spec.js20
-rw-r--r--spec/javascripts/notes/components/note_header_spec.js14
-rw-r--r--spec/javascripts/notes/components/noteable_discussion_spec.js39
-rw-r--r--spec/javascripts/notes/components/noteable_note_spec.js1
-rw-r--r--spec/javascripts/notes/components/toggle_replies_widget_spec.js78
-rw-r--r--spec/javascripts/notes/helpers.js2
-rw-r--r--spec/javascripts/notes/mock_data.js87
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js208
-rw-r--r--spec/javascripts/notes/stores/collapse_utils_spec.js13
-rw-r--r--spec/javascripts/notes/stores/getters_spec.js1
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js98
-rw-r--r--spec/javascripts/notes_spec.js1593
-rw-r--r--spec/javascripts/oauth_remember_me_spec.js9
-rw-r--r--spec/javascripts/pager_spec.js5
-rw-r--r--spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js14
-rw-r--r--spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js36
-rw-r--r--spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js20
-rw-r--r--spec/javascripts/pages/admin/users/new/index_spec.js43
-rw-r--r--spec/javascripts/pages/labels/components/promote_label_modal_spec.js36
-rw-r--r--spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js43
-rw-r--r--spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js36
-rw-r--r--spec/javascripts/pages/profiles/show/emoji_menu_spec.js2
-rw-r--r--spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js74
-rw-r--r--spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js8
-rw-r--r--spec/javascripts/pdf/index_spec.js6
-rw-r--r--spec/javascripts/pdf/page_spec.js63
-rw-r--r--spec/javascripts/performance_bar/components/detailed_metric_spec.js8
-rw-r--r--spec/javascripts/performance_bar/components/request_selector_spec.js3
-rw-r--r--spec/javascripts/performance_bar/index_spec.js5
-rw-r--r--spec/javascripts/performance_bar/services/performance_bar_service_spec.js68
-rw-r--r--spec/javascripts/pipelines/blank_state_spec.js14
-rw-r--r--spec/javascripts/pipelines/empty_state_spec.js27
-rw-r--r--spec/javascripts/pipelines/graph/action_component_spec.js18
-rw-r--r--spec/javascripts/pipelines/graph/dropdown_job_component_spec.js93
-rw-r--r--spec/javascripts/pipelines/graph/graph_component_spec.js8
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js184
-rw-r--r--spec/javascripts/pipelines/graph/job_group_dropdown_spec.js85
-rw-r--r--spec/javascripts/pipelines/graph/job_item_spec.js195
-rw-r--r--spec/javascripts/pipelines/graph/stage_column_component_spec.js17
-rw-r--r--spec/javascripts/pipelines/header_component_spec.js9
-rw-r--r--spec/javascripts/pipelines/nav_controls_spec.js12
-rw-r--r--spec/javascripts/pipelines/pipeline_store_spec.js1
-rw-r--r--spec/javascripts/pipelines/pipeline_url_spec.js8
-rw-r--r--spec/javascripts/pipelines/pipelines_actions_spec.js110
-rw-r--r--spec/javascripts/pipelines/pipelines_artifacts_spec.js16
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js204
-rw-r--r--spec/javascripts/pipelines/pipelines_store_spec.js5
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js22
-rw-r--r--spec/javascripts/pipelines/pipelines_table_spec.js20
-rw-r--r--spec/javascripts/pipelines/stage_spec.js16
-rw-r--r--spec/javascripts/pipelines_spec.js4
-rw-r--r--spec/javascripts/polyfills/element_spec.js2
-rw-r--r--spec/javascripts/pretty_time_spec.js133
-rw-r--r--spec/javascripts/profile/account/components/delete_account_modal_spec.js16
-rw-r--r--spec/javascripts/profile/account/components/update_username_spec.js7
-rw-r--r--spec/javascripts/project_select_combo_button_spec.js56
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js22
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js62
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js22
-rw-r--r--spec/javascripts/projects/project_new_spec.js35
-rw-r--r--spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js18
-rw-r--r--spec/javascripts/raven/raven_config_spec.js2
-rw-r--r--spec/javascripts/read_more_spec.js23
-rw-r--r--spec/javascripts/registry/components/app_spec.js53
-rw-r--r--spec/javascripts/registry/components/collapsible_container_spec.js16
-rw-r--r--spec/javascripts/registry/components/table_registry_spec.js14
-rw-r--r--spec/javascripts/registry/getters_spec.js41
-rw-r--r--spec/javascripts/registry/mock_data.js3
-rw-r--r--spec/javascripts/registry/stores/mutations_spec.js4
-rw-r--r--spec/javascripts/reports/components/grouped_test_reports_app_spec.js8
-rw-r--r--spec/javascripts/reports/components/modal_spec.js17
-rw-r--r--spec/javascripts/reports/components/report_link_spec.js6
-rw-r--r--spec/javascripts/reports/components/report_section_spec.js5
-rw-r--r--spec/javascripts/reports/components/test_issue_body_spec.js1
-rw-r--r--spec/javascripts/reports/store/mutations_spec.js2
-rw-r--r--spec/javascripts/right_sidebar_spec.js169
-rw-r--r--spec/javascripts/search_autocomplete_spec.js27
-rw-r--r--spec/javascripts/search_spec.js10
-rw-r--r--spec/javascripts/settings_panels_spec.js30
-rw-r--r--spec/javascripts/shared/popover_spec.js34
-rw-r--r--spec/javascripts/shortcuts_dashboard_navigation_spec.js23
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js106
-rw-r--r--spec/javascripts/shortcuts_spec.js2
-rw-r--r--spec/javascripts/sidebar/assignees_spec.js94
-rw-r--r--spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js48
-rw-r--r--spec/javascripts/sidebar/confidential_edit_buttons_spec.js12
-rw-r--r--spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js12
-rw-r--r--spec/javascripts/sidebar/confidential_issue_sidebar_spec.js32
-rw-r--r--spec/javascripts/sidebar/lock/edit_form_buttons_spec.js12
-rw-r--r--spec/javascripts/sidebar/lock/edit_form_spec.js12
-rw-r--r--spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js28
-rw-r--r--spec/javascripts/sidebar/participants_spec.js17
-rw-r--r--spec/javascripts/sidebar/sidebar_assignees_spec.js16
-rw-r--r--spec/javascripts/sidebar/sidebar_mediator_spec.js33
-rw-r--r--spec/javascripts/sidebar/sidebar_move_issue_spec.js24
-rw-r--r--spec/javascripts/sidebar/sidebar_store_spec.js18
-rw-r--r--spec/javascripts/sidebar/sidebar_subscriptions_spec.js4
-rw-r--r--spec/javascripts/sidebar/subscriptions_spec.js16
-rw-r--r--spec/javascripts/sidebar/todo_spec.js15
-rw-r--r--spec/javascripts/signin_tabs_memoizer_spec.js240
-rw-r--r--spec/javascripts/smart_interval_spec.js43
-rw-r--r--spec/javascripts/syntax_highlight_spec.js27
-rw-r--r--spec/javascripts/test_bundle.js4
-rw-r--r--spec/javascripts/todos_spec.js4
-rw-r--r--spec/javascripts/toggle_buttons_spec.js18
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js36
-rw-r--r--spec/javascripts/u2f/mock_u2f_device.js14
-rw-r--r--spec/javascripts/u2f/register_spec.js31
-rw-r--r--spec/javascripts/u2f/util_spec.js32
-rw-r--r--spec/javascripts/version_check_image_spec.js14
-rw-r--r--spec/javascripts/vue_mr_widget/components/deployment_spec.js185
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js10
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js8
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js72
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js55
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js32
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js79
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js13
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js5
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js4
-rw-r--r--spec/javascripts/vue_mr_widget/components/review_app_link_spec.js38
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js6
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js6
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js4
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js55
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js10
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js56
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js60
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js27
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js1
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js4
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js5
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js6
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js6
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js136
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js6
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js18
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js22
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js4
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js297
-rw-r--r--spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js14
-rw-r--r--spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js10
-rw-r--r--spec/javascripts/vue_shared/components/bar_chart_spec.js6
-rw-r--r--spec/javascripts/vue_shared/components/changed_file_icon_spec.js46
-rw-r--r--spec/javascripts/vue_shared/components/ci_badge_link_spec.js6
-rw-r--r--spec/javascripts/vue_shared/components/clipboard_button_spec.js3
-rw-r--r--spec/javascripts/vue_shared/components/commit_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/deprecated_modal_spec.js12
-rw-r--r--spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js5
-rw-r--r--spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js38
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js10
-rw-r--r--spec/javascripts/vue_shared/components/expand_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/file_icon_spec.js26
-rw-r--r--spec/javascripts/vue_shared/components/file_row_spec.js110
-rw-r--r--spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js91
-rw-r--r--spec/javascripts/vue_shared/components/gl_countdown_spec.js77
-rw-r--r--spec/javascripts/vue_shared/components/gl_modal_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/header_ci_component_spec.js12
-rw-r--r--spec/javascripts/vue_shared/components/icon_spec.js17
-rw-r--r--spec/javascripts/vue_shared/components/identicon_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/issue/issue_warning_spec.js17
-rw-r--r--spec/javascripts/vue_shared/components/loading_button_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/loading_icon_spec.js54
-rw-r--r--spec/javascripts/vue_shared/components/markdown/field_spec.js81
-rw-r--r--spec/javascripts/vue_shared/components/markdown/header_spec.js32
-rw-r--r--spec/javascripts/vue_shared/components/markdown/toolbar_spec.js9
-rw-r--r--spec/javascripts/vue_shared/components/memory_graph_spec.js16
-rw-r--r--spec/javascripts/vue_shared/components/navigation_tabs_spec.js5
-rw-r--r--spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/notes/system_note_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/pagination_links_spec.js59
-rw-r--r--spec/javascripts/vue_shared/components/panel_resizer_spec.js27
-rw-r--r--spec/javascripts/vue_shared/components/pikaday_spec.js1
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js1
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js34
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js16
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js22
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js12
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js6
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js5
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js1
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js8
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js23
-rw-r--r--spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js49
-rw-r--r--spec/javascripts/vue_shared/components/smart_virtual_list_spec.js83
-rw-r--r--spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js29
-rw-r--r--spec/javascripts/vue_shared/components/table_pagination_spec.js27
-rw-r--r--spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/toggle_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js156
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js57
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js59
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js10
-rw-r--r--spec/javascripts/vue_shared/directives/tooltip_spec.js45
-rw-r--r--spec/javascripts/vue_shared/translate_spec.js213
-rw-r--r--spec/javascripts/zen_mode_spec.js7
-rw-r--r--spec/lib/api/helpers/custom_validators_spec.rb64
-rw-r--r--spec/lib/api/helpers/pagination_spec.rb2
-rw-r--r--spec/lib/backup/manager_spec.rb4
-rw-r--r--spec/lib/backup/repository_spec.rb4
-rw-r--r--spec/lib/banzai/cross_project_reference_spec.rb19
-rw-r--r--spec/lib/banzai/filter/autolink_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb1
-rw-r--r--spec/lib/banzai/filter/external_issue_reference_filter_spec.rb34
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb23
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb7
-rw-r--r--spec/lib/banzai/filter/spaced_link_filter_spec.rb96
-rw-r--r--spec/lib/banzai/filter/wiki_link_filter_spec.rb40
-rw-r--r--spec/lib/banzai/pipeline/gfm_pipeline_spec.rb18
-rw-r--r--spec/lib/banzai/pipeline/wiki_pipeline_spec.rb35
-rw-r--r--spec/lib/banzai/reference_parser/base_parser_spec.rb3
-rw-r--r--spec/lib/banzai/reference_parser/commit_parser_spec.rb18
-rw-r--r--spec/lib/bitbucket_server/client_spec.rb12
-rw-r--r--spec/lib/bitbucket_server/collection_spec.rb29
-rw-r--r--spec/lib/bitbucket_server/paginator_spec.rb10
-rw-r--r--spec/lib/container_registry/blob_spec.rb2
-rw-r--r--spec/lib/event_filter_spec.rb131
-rw-r--r--spec/lib/feature_spec.rb88
-rw-r--r--spec/lib/forever_spec.rb2
-rw-r--r--spec/lib/gitaly/server_spec.rb8
-rw-r--r--spec/lib/gitlab/auth/ldap/access_spec.rb152
-rw-r--r--spec/lib/gitlab/auth/o_auth/provider_spec.rb42
-rw-r--r--spec/lib/gitlab/auth/saml/auth_hash_spec.rb11
-rw-r--r--spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/digest_column_spec.rb46
-rw-r--r--spec/lib/gitlab/background_migration/encrypt_columns_spec.rb69
-rw-r--r--spec/lib/gitlab/background_migration/migrate_legacy_artifacts_spec.rb156
-rw-r--r--spec/lib/gitlab/background_migration/populate_cluster_kubernetes_namespace_table_spec.rb97
-rw-r--r--spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb62
-rw-r--r--spec/lib/gitlab/background_migration/redact_links_spec.rb96
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb4
-rw-r--r--spec/lib/gitlab/bitbucket_import/project_creator_spec.rb4
-rw-r--r--spec/lib/gitlab/blob_helper_spec.rb125
-rw-r--r--spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb26
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb18
-rw-r--r--spec/lib/gitlab/checks/lfs_integrity_spec.rb5
-rw-r--r--spec/lib/gitlab/checks/timed_logger_spec.rb63
-rw-r--r--spec/lib/gitlab/ci/ansi2html_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb56
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb47
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb56
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/build/policy/changes_spec.rb119
-rw-r--r--spec/lib/gitlab/ci/build/policy/refs_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/build/policy/variables_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/entry/artifacts_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/global_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb110
-rw-r--r--spec/lib/gitlab/ci/config/entry/policy_spec.rb22
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports_spec.rb63
-rw-r--r--spec/lib/gitlab/ci/config/entry/retry_spec.rb236
-rw-r--r--spec/lib/gitlab/ci/config/extendable/entry_spec.rb227
-rw-r--r--spec/lib/gitlab/ci/config/extendable_spec.rb228
-rw-r--r--spec/lib/gitlab/ci/config/external/file/base_spec.rb52
-rw-r--r--spec/lib/gitlab/ci/config/external/file/local_spec.rb78
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb157
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb98
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb190
-rw-r--r--spec/lib/gitlab/ci/config/normalizer_spec.rb66
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb281
-rw-r--r--spec/lib/gitlab/ci/parsers/junit_spec.rb118
-rw-r--r--spec/lib/gitlab/ci/parsers/test/junit_spec.rb172
-rw-r--r--spec/lib/gitlab/ci/parsers/test_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/parsers_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/status/build/factory_spec.rb51
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/status/build/retried_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/scheduled_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/status/build/unschedule_spec.rb94
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb42
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/factory_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/status/scheduled_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/templates/templates_spec.rb11
-rw-r--r--spec/lib/gitlab/ci/variables/collection/item_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb122
-rw-r--r--spec/lib/gitlab/cleanup/project_uploads_spec.rb4
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb7
-rw-r--r--spec/lib/gitlab/conflict/file_spec.rb13
-rw-r--r--spec/lib/gitlab/contributions_calendar_spec.rb17
-rw-r--r--spec/lib/gitlab/cross_project_access/check_info_spec.rb4
-rw-r--r--spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb6
-rw-r--r--spec/lib/gitlab/data_builder/build_spec.rb1
-rw-r--r--spec/lib/gitlab/data_builder/pipeline_spec.rb47
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb93
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb2
-rw-r--r--spec/lib/gitlab/database/subquery_spec.rb17
-rw-r--r--spec/lib/gitlab/diff/file_collection/commit_spec.rb15
-rw-r--r--spec/lib/gitlab/diff/file_collection/compare_spec.rb29
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb8
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb110
-rw-r--r--spec/lib/gitlab/diff/highlight_cache_spec.rb70
-rw-r--r--spec/lib/gitlab/diff/highlight_spec.rb38
-rw-r--r--spec/lib/gitlab/diff/line_spec.rb22
-rw-r--r--spec/lib/gitlab/diff/lines_unfolder_spec.rb750
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb92
-rw-r--r--spec/lib/gitlab/diff/position_tracer_spec.rb2
-rw-r--r--spec/lib/gitlab/email/handler/create_issue_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb71
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/email/handler_spec.rb4
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb12
-rw-r--r--spec/lib/gitlab/favicon_spec.rb1
-rw-r--r--spec/lib/gitlab/file_detector_spec.rb25
-rw-r--r--spec/lib/gitlab/file_markdown_link_builder_spec.rb80
-rw-r--r--spec/lib/gitlab/file_type_detection_spec.rb82
-rw-r--r--spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/attributes_parser_spec.rb4
-rw-r--r--spec/lib/gitlab/git/blob_snippet_spec.rb19
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb17
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb8
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb33
-rw-r--r--spec/lib/gitlab/git/committer_with_hooks_spec.rb156
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb135
-rw-r--r--spec/lib/gitlab/git/diff_stats_collection_spec.rb32
-rw-r--r--spec/lib/gitlab/git/gitlab_projects_spec.rb321
-rw-r--r--spec/lib/gitlab/git/hook_env_spec.rb20
-rw-r--r--spec/lib/gitlab/git/hook_spec.rb111
-rw-r--r--spec/lib/gitlab/git/hooks_service_spec.rb50
-rw-r--r--spec/lib/gitlab/git/index_spec.rb239
-rw-r--r--spec/lib/gitlab/git/lfs_changes_spec.rb4
-rw-r--r--spec/lib/gitlab/git/patches/collection_spec.rb28
-rw-r--r--spec/lib/gitlab/git/patches/commit_patches_spec.rb49
-rw-r--r--spec/lib/gitlab/git/patches/patch_spec.rb16
-rw-r--r--spec/lib/gitlab/git/popen_spec.rb179
-rw-r--r--spec/lib/gitlab/git/push_spec.rb166
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb1027
-rw-r--r--spec/lib/gitlab/git/storage/checker_spec.rb132
-rw-r--r--spec/lib/gitlab/git/storage/circuit_breaker_spec.rb187
-rw-r--r--spec/lib/gitlab/git/storage/failure_info_spec.rb70
-rw-r--r--spec/lib/gitlab/git/storage/forked_storage_check_spec.rb73
-rw-r--r--spec/lib/gitlab/git/storage/health_spec.rb58
-rw-r--r--spec/lib/gitlab/git/storage/null_circuit_breaker_spec.rb70
-rw-r--r--spec/lib/gitlab/git/tag_spec.rb2
-rw-r--r--spec/lib/gitlab/git/user_spec.rb15
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb47
-rw-r--r--spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb28
-rw-r--r--spec/lib/gitlab/git_access_spec.rb39
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb34
-rw-r--r--spec/lib/gitlab/gitaly_client/diff_spec.rb6
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb33
-rw-r--r--spec/lib/gitlab/gitaly_client/remote_service_spec.rb20
-rw-r--r--spec/lib/gitlab/gitaly_client/repository_service_spec.rb61
-rw-r--r--spec/lib/gitlab/gitaly_client/wiki_service_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/bulk_importing_spec.rb12
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_importer_spec.rb28
-rw-r--r--spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb14
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb25
-rw-r--r--spec/lib/gitlab/github_import_spec.rb33
-rw-r--r--spec/lib/gitlab/gon_helper_spec.rb32
-rw-r--r--spec/lib/gitlab/gpg_spec.rb4
-rw-r--r--spec/lib/gitlab/highlight_spec.rb100
-rw-r--r--spec/lib/gitlab/hook_data/base_builder_spec.rb129
-rw-r--r--spec/lib/gitlab/hook_data/issue_builder_spec.rb5
-rw-r--r--spec/lib/gitlab/hook_data/merge_request_builder_spec.rb7
-rw-r--r--spec/lib/gitlab/http_spec.rb26
-rw-r--r--spec/lib/gitlab/identifier_spec.rb49
-rw-r--r--spec/lib/gitlab/import/database_helpers_spec.rb46
-rw-r--r--spec/lib/gitlab/import/merge_request_creator_spec.rb43
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb105
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb3
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb31
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml16
-rw-r--r--spec/lib/gitlab/import_export/attribute_configuration_spec.rb15
-rw-r--r--spec/lib/gitlab/import_export/avatar_restorer_spec.rb33
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb5
-rw-r--r--spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb89
-rw-r--r--spec/lib/gitlab/import_export/file_importer_spec.rb12
-rw-r--r--spec/lib/gitlab/import_export/importer_object_storage_spec.rb115
-rw-r--r--spec/lib/gitlab/import_export/importer_spec.rb25
-rw-r--r--spec/lib/gitlab/import_export/model_configuration_spec.rb42
-rw-r--r--spec/lib/gitlab/import_export/project.json47
-rw-r--r--spec/lib/gitlab/import_export/project.light.json22
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb27
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb11
-rw-r--r--spec/lib/gitlab/import_export/relation_factory_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/repo_restorer_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml24
-rw-r--r--spec/lib/gitlab/import_export/saver_spec.rb24
-rw-r--r--spec/lib/gitlab/import_export/uploads_manager_spec.rb61
-rw-r--r--spec/lib/gitlab/import_export/uploads_restorer_spec.rb10
-rw-r--r--spec/lib/gitlab/import_export/uploads_saver_spec.rb1
-rw-r--r--spec/lib/gitlab/kubernetes/cluster_role_binding_spec.rb35
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb180
-rw-r--r--spec/lib/gitlab/kubernetes/helm/base_command_spec.rb20
-rw-r--r--spec/lib/gitlab/kubernetes/helm/init_command_spec.rb130
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb218
-rw-r--r--spec/lib/gitlab/kubernetes/helm/pod_spec.rb19
-rw-r--r--spec/lib/gitlab/kubernetes/helm/upgrade_command_spec.rb140
-rw-r--r--spec/lib/gitlab/kubernetes/kube_client_spec.rb186
-rw-r--r--spec/lib/gitlab/kubernetes/namespace_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/role_binding_spec.rb48
-rw-r--r--spec/lib/gitlab/kubernetes/service_account_spec.rb24
-rw-r--r--spec/lib/gitlab/kubernetes/service_account_token_spec.rb35
-rw-r--r--spec/lib/gitlab/language_data_spec.rb22
-rw-r--r--spec/lib/gitlab/metrics/subscribers/active_record_spec.rb59
-rw-r--r--spec/lib/gitlab/middleware/multipart_spec.rb20
-rw-r--r--spec/lib/gitlab/middleware/read_only_spec.rb2
-rw-r--r--spec/lib/gitlab/null_request_store_spec.rb75
-rw-r--r--spec/lib/gitlab/patch/draw_route_spec.rb30
-rw-r--r--spec/lib/gitlab/patch/prependable_spec.rb234
-rw-r--r--spec/lib/gitlab/private_commit_email_spec.rb51
-rw-r--r--spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/prometheus/metric_group_spec.rb41
-rw-r--r--spec/lib/gitlab/quick_actions/command_definition_spec.rb13
-rw-r--r--spec/lib/gitlab/quick_actions/dsl_spec.rb8
-rw-r--r--spec/lib/gitlab/quick_actions/extractor_spec.rb19
-rw-r--r--spec/lib/gitlab/repository_cache_adapter_spec.rb138
-rw-r--r--spec/lib/gitlab/repository_cache_spec.rb97
-rw-r--r--spec/lib/gitlab/safe_request_store_spec.rb245
-rw-r--r--spec/lib/gitlab/sentry_spec.rb24
-rw-r--r--spec/lib/gitlab/shell_spec.rb125
-rw-r--r--spec/lib/gitlab/sidekiq_throttler_spec.rb44
-rw-r--r--spec/lib/gitlab/slash_commands/command_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/deploy_spec.rb2
-rw-r--r--spec/lib/gitlab/storage_check/cli_spec.rb19
-rw-r--r--spec/lib/gitlab/storage_check/gitlab_caller_spec.rb46
-rw-r--r--spec/lib/gitlab/storage_check/option_parser_spec.rb31
-rw-r--r--spec/lib/gitlab/storage_check/response_spec.rb54
-rw-r--r--spec/lib/gitlab/template/finders/repo_template_finders_spec.rb37
-rw-r--r--spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb2
-rw-r--r--spec/lib/gitlab/tree_summary_spec.rb202
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb45
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb37
-rw-r--r--spec/lib/gitlab/user_extractor_spec.rb58
-rw-r--r--spec/lib/gitlab/utils_spec.rb23
-rw-r--r--spec/lib/gitlab/version_info_spec.rb3
-rw-r--r--spec/lib/gitlab/view/presenter/base_spec.rb2
-rw-r--r--spec/lib/gitlab/web_ide_commits_counter_spec.rb19
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb16
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb47
-rw-r--r--spec/lib/json_web_token/hmac_token_spec.rb133
-rw-r--r--spec/lib/mattermost/session_spec.rb4
-rw-r--r--spec/lib/microsoft_teams/notifier_spec.rb2
-rw-r--r--spec/lib/object_storage/direct_upload_spec.rb12
-rw-r--r--spec/lib/quality/helm_client_spec.rb111
-rw-r--r--spec/lib/quality/kubernetes_client_spec.rb33
-rw-r--r--spec/lib/system_check/simple_executor_spec.rb9
-rw-r--r--spec/mailers/emails/auto_devops_spec.rb32
-rw-r--r--spec/migrations/add_pages_access_level_to_project_feature_spec.rb30
-rw-r--r--spec/migrations/delete_inconsistent_internal_id_records_spec.rb134
-rw-r--r--spec/migrations/drop_duplicate_protected_tags_spec.rb40
-rw-r--r--spec/migrations/enqueue_redact_links_spec.rb42
-rw-r--r--spec/migrations/fill_empty_finished_at_in_deployments_spec.rb70
-rw-r--r--spec/migrations/import_common_metrics_spec.rb16
-rw-r--r--spec/migrations/migrate_legacy_artifacts_to_job_artifacts_spec.rb73
-rw-r--r--spec/migrations/migrate_null_wiki_access_levels_spec.rb29
-rw-r--r--spec/migrations/migrate_old_artifacts_spec.rb2
-rw-r--r--spec/migrations/remove_orphaned_label_links_spec.rb40
-rw-r--r--spec/migrations/rename_more_reserved_project_names_spec.rb11
-rw-r--r--spec/migrations/rename_reserved_project_names_spec.rb11
-rw-r--r--spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb2
-rw-r--r--spec/migrations/schedule_digest_personal_access_tokens_spec.rb46
-rw-r--r--spec/migrations/steal_fill_store_upload_spec.rb40
-rw-r--r--spec/models/application_setting_spec.rb113
-rw-r--r--spec/models/award_emoji_spec.rb23
-rw-r--r--spec/models/blob_viewer/gitlab_ci_yml_spec.rb10
-rw-r--r--spec/models/blob_viewer/package_json_spec.rb21
-rw-r--r--spec/models/board_group_recent_visit_spec.rb64
-rw-r--r--spec/models/board_project_recent_visit_spec.rb64
-rw-r--r--spec/models/ci/build_spec.rb993
-rw-r--r--spec/models/ci/job_artifact_spec.rb70
-rw-r--r--spec/models/ci/pipeline_spec.rb192
-rw-r--r--spec/models/ci/pipeline_variable_spec.rb9
-rw-r--r--spec/models/ci/runner_spec.rb31
-rw-r--r--spec/models/ci/stage_spec.rb24
-rw-r--r--spec/models/clusters/applications/helm_spec.rb14
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb35
-rw-r--r--spec/models/clusters/applications/jupyter_spec.rb40
-rw-r--r--spec/models/clusters/applications/knative_spec.rb77
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb82
-rw-r--r--spec/models/clusters/applications/runner_spec.rb41
-rw-r--r--spec/models/clusters/cluster_spec.rb104
-rw-r--r--spec/models/clusters/group_spec.rb8
-rw-r--r--spec/models/clusters/kubernetes_namespace_spec.rb123
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb198
-rw-r--r--spec/models/clusters/project_spec.rb2
-rw-r--r--spec/models/clusters/providers/gcp_spec.rb18
-rw-r--r--spec/models/commit_spec.rb40
-rw-r--r--spec/models/commit_status_spec.rb20
-rw-r--r--spec/models/compare_spec.rb29
-rw-r--r--spec/models/concerns/avatarable_spec.rb20
-rw-r--r--spec/models/concerns/awardable_spec.rb29
-rw-r--r--spec/models/concerns/blob_language_from_git_attributes_spec.rb25
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb5
-rw-r--r--spec/models/concerns/cacheable_attributes_spec.rb4
-rw-r--r--spec/models/concerns/case_sensitivity_spec.rb202
-rw-r--r--spec/models/concerns/deployable_spec.rb74
-rw-r--r--spec/models/concerns/each_batch_spec.rb51
-rw-r--r--spec/models/concerns/from_union_spec.rb40
-rw-r--r--spec/models/concerns/has_status_spec.rb52
-rw-r--r--spec/models/concerns/issuable_spec.rb4
-rw-r--r--spec/models/concerns/optionally_search_spec.rb44
-rw-r--r--spec/models/concerns/redactable_spec.rb69
-rw-r--r--spec/models/concerns/relative_positioning_spec.rb34
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb272
-rw-r--r--spec/models/concerns/triggerable_hooks_spec.rb24
-rw-r--r--spec/models/deployment_spec.rb215
-rw-r--r--spec/models/environment_spec.rb146
-rw-r--r--spec/models/environment_status_spec.rb165
-rw-r--r--spec/models/event_collection_spec.rb2
-rw-r--r--spec/models/event_spec.rb124
-rw-r--r--spec/models/fork_network_member_spec.rb2
-rw-r--r--spec/models/forked_project_link_spec.rb68
-rw-r--r--spec/models/global_milestone_spec.rb35
-rw-r--r--spec/models/group_spec.rb60
-rw-r--r--spec/models/hooks/active_hook_filter_spec.rb68
-rw-r--r--spec/models/hooks/system_hook_spec.rb4
-rw-r--r--spec/models/hooks/web_hook_spec.rb26
-rw-r--r--spec/models/instance_configuration_spec.rb8
-rw-r--r--spec/models/internal_id_spec.rb7
-rw-r--r--spec/models/issue_spec.rb155
-rw-r--r--spec/models/label_note_spec.rb23
-rw-r--r--spec/models/label_spec.rb36
-rw-r--r--spec/models/lfs_object_spec.rb21
-rw-r--r--spec/models/license_template_spec.rb59
-rw-r--r--spec/models/merge_request_diff_spec.rb46
-rw-r--r--spec/models/merge_request_spec.rb120
-rw-r--r--spec/models/milestone_spec.rb55
-rw-r--r--spec/models/namespace_spec.rb72
-rw-r--r--spec/models/note_spec.rb91
-rw-r--r--spec/models/notification_setting_spec.rb36
-rw-r--r--spec/models/personal_access_token_spec.rb28
-rw-r--r--spec/models/postgresql/replication_slot_spec.rb20
-rw-r--r--spec/models/project_auto_devops_spec.rb33
-rw-r--r--spec/models/project_feature_spec.rb73
-rw-r--r--spec/models/project_group_link_spec.rb2
-rw-r--r--spec/models/project_services/bamboo_service_spec.rb3
-rw-r--r--spec/models/project_services/chat_message/merge_message_spec.rb25
-rw-r--r--spec/models/project_services/chat_message/push_message_spec.rb65
-rw-r--r--spec/models/project_services/chat_notification_service_spec.rb50
-rw-r--r--spec/models/project_services/discord_service_spec.rb11
-rw-r--r--spec/models/project_services/gemnasium_service_spec.rb74
-rw-r--r--spec/models/project_services/hangouts_chat_service_spec.rb249
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb18
-rw-r--r--spec/models/project_services/jira_service_spec.rb255
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb6
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb23
-rw-r--r--spec/models/project_spec.rb693
-rw-r--r--spec/models/project_wiki_spec.rb33
-rw-r--r--spec/models/prometheus_metric_spec.rb98
-rw-r--r--spec/models/remote_mirror_spec.rb18
-rw-r--r--spec/models/repository_spec.rb483
-rw-r--r--spec/models/resource_label_event_spec.rb52
-rw-r--r--spec/models/service_spec.rb27
-rw-r--r--spec/models/shard_spec.rb50
-rw-r--r--spec/models/site_statistic_spec.rb2
-rw-r--r--spec/models/snippet_spec.rb211
-rw-r--r--spec/models/ssh_host_key_spec.rb164
-rw-r--r--spec/models/todo_spec.rb125
-rw-r--r--spec/models/upload_spec.rb64
-rw-r--r--spec/models/user_preference_spec.rb53
-rw-r--r--spec/models/user_spec.rb416
-rw-r--r--spec/models/wiki_page_spec.rb55
-rw-r--r--spec/policies/ci/pipeline_policy_spec.rb18
-rw-r--r--spec/policies/clusters/cluster_policy_spec.rb42
-rw-r--r--spec/policies/group_policy_spec.rb11
-rw-r--r--spec/policies/issue_policy_spec.rb42
-rw-r--r--spec/policies/project_policy_spec.rb5
-rw-r--r--spec/presenters/blob_presenter_spec.rb44
-rw-r--r--spec/presenters/ci/build_presenter_spec.rb50
-rw-r--r--spec/presenters/ci/build_runner_presenter_spec.rb53
-rw-r--r--spec/presenters/clusterable_presenter_spec.rb17
-rw-r--r--spec/presenters/clusters/cluster_presenter_spec.rb21
-rw-r--r--spec/presenters/group_clusterable_presenter_spec.rb77
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb9
-rw-r--r--spec/presenters/project_clusterable_presenter_spec.rb77
-rw-r--r--spec/presenters/project_presenter_spec.rb178
-rw-r--r--spec/rack_servers/configs/config.ru12
-rw-r--r--spec/rack_servers/configs/puma.rb32
-rw-r--r--spec/rack_servers/puma_spec.rb84
-rw-r--r--spec/rack_servers/unicorn_spec.rb105
-rw-r--r--spec/requests/api/applications_spec.rb70
-rw-r--r--spec/requests/api/award_emoji_spec.rb12
-rw-r--r--spec/requests/api/circuit_breakers_spec.rb19
-rw-r--r--spec/requests/api/commits_spec.rb222
-rw-r--r--spec/requests/api/deployments_spec.rb12
-rw-r--r--spec/requests/api/events_spec.rb48
-rw-r--r--spec/requests/api/files_spec.rb58
-rw-r--r--spec/requests/api/groups_spec.rb14
-rw-r--r--spec/requests/api/helpers_spec.rb16
-rw-r--r--spec/requests/api/internal_spec.rb114
-rw-r--r--spec/requests/api/issues_spec.rb186
-rw-r--r--spec/requests/api/jobs_spec.rb76
-rw-r--r--spec/requests/api/labels_spec.rb8
-rw-r--r--spec/requests/api/markdown_spec.rb46
-rw-r--r--spec/requests/api/merge_requests_spec.rb64
-rw-r--r--spec/requests/api/pages/internal_access_spec.rb88
-rw-r--r--spec/requests/api/pages/private_access_spec.rb88
-rw-r--r--spec/requests/api/pages/public_access_spec.rb88
-rw-r--r--spec/requests/api/pipelines_spec.rb77
-rw-r--r--spec/requests/api/project_export_spec.rb33
-rw-r--r--spec/requests/api/project_hooks_spec.rb19
-rw-r--r--spec/requests/api/project_import_spec.rb3
-rw-r--r--spec/requests/api/project_milestones_spec.rb13
-rw-r--r--spec/requests/api/project_snippets_spec.rb16
-rw-r--r--spec/requests/api/project_templates_spec.rb145
-rw-r--r--spec/requests/api/projects_spec.rb282
-rw-r--r--spec/requests/api/protected_tags_spec.rb202
-rw-r--r--spec/requests/api/redacted_events_spec.rb68
-rw-r--r--spec/requests/api/repositories_spec.rb4
-rw-r--r--spec/requests/api/resource_label_events_spec.rb75
-rw-r--r--spec/requests/api/runner_spec.rb18
-rw-r--r--spec/requests/api/runners_spec.rb221
-rw-r--r--spec/requests/api/settings_spec.rb25
-rw-r--r--spec/requests/api/submodules_spec.rb99
-rw-r--r--spec/requests/api/templates_spec.rb3
-rw-r--r--spec/requests/api/users_spec.rb102
-rw-r--r--spec/requests/api/wikis_spec.rb134
-rw-r--r--spec/requests/openid_connect_spec.rb2
-rw-r--r--spec/routing/admin_routing_spec.rb4
-rw-r--r--spec/routing/project_routing_spec.rb17
-rw-r--r--spec/routing/routing_spec.rb7
-rw-r--r--spec/rubocop/code_reuse_helpers_spec.rb249
-rw-r--r--spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb32
-rw-r--r--spec/rubocop/cop/code_reuse/active_record_spec.rb138
-rw-r--r--spec/rubocop/cop/code_reuse/finder_spec.rb77
-rw-r--r--spec/rubocop/cop/code_reuse/presenter_spec.rb117
-rw-r--r--spec/rubocop/cop/code_reuse/serializer_spec.rb117
-rw-r--r--spec/rubocop/cop/code_reuse/service_class_spec.rb89
-rw-r--r--spec/rubocop/cop/code_reuse/worker_spec.rb104
-rw-r--r--spec/rubocop/cop/destroy_all_spec.rb43
-rw-r--r--spec/rubocop/cop/gitlab/union_spec.rb25
-rw-r--r--spec/rubocop/cop/group_public_or_visible_to_user_spec.rb28
-rw-r--r--spec/rubocop/cop/line_break_around_conditional_block_spec.rb16
-rw-r--r--spec/rubocop/cop/prefer_class_methods_over_module_spec.rb98
-rw-r--r--spec/rubocop/cop/qa/element_with_pattern_spec.rb50
-rw-r--r--spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb68
-rw-r--r--spec/rubocop/qa_helpers_spec.rb37
-rw-r--r--spec/serializers/build_action_entity_spec.rb16
-rw-r--r--spec/serializers/build_serializer_spec.rb2
-rw-r--r--spec/serializers/commit_entity_spec.rb59
-rw-r--r--spec/serializers/deployment_entity_spec.rb22
-rw-r--r--spec/serializers/deployment_serializer_spec.rb35
-rw-r--r--spec/serializers/detailed_status_entity_spec.rb24
-rw-r--r--spec/serializers/diff_file_entity_spec.rb22
-rw-r--r--spec/serializers/diff_line_entity_spec.rb45
-rw-r--r--spec/serializers/diff_line_serializer_spec.rb25
-rw-r--r--spec/serializers/diff_viewer_entity_spec.rb19
-rw-r--r--spec/serializers/discussion_entity_spec.rb7
-rw-r--r--spec/serializers/environment_serializer_spec.rb3
-rw-r--r--spec/serializers/environment_status_entity_spec.rb79
-rw-r--r--spec/serializers/job_entity_spec.rb17
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb42
-rw-r--r--spec/serializers/move_to_project_entity_spec.rb19
-rw-r--r--spec/serializers/move_to_project_serializer_spec.rb14
-rw-r--r--spec/serializers/pipeline_details_entity_spec.rb2
-rw-r--r--spec/serializers/status_entity_spec.rb24
-rw-r--r--spec/services/application_settings/update_service_spec.rb3
-rw-r--r--spec/services/applications/create_service_spec.rb13
-rw-r--r--spec/services/audit_event_service_spec.rb26
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb2
-rw-r--r--spec/services/boards/issues/list_service_spec.rb12
-rw-r--r--spec/services/boards/visits/create_service_spec.rb53
-rw-r--r--spec/services/boards/visits/latest_service_spec.rb47
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb93
-rw-r--r--spec/services/ci/destroy_pipeline_service_spec.rb60
-rw-r--r--spec/services/ci/enqueue_build_service_spec.rb16
-rw-r--r--spec/services/ci/fetch_kubernetes_token_service_spec.rb64
-rw-r--r--spec/services/ci/process_build_service_spec.rb117
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb213
-rw-r--r--spec/services/ci/register_job_service_spec.rb14
-rw-r--r--spec/services/ci/retry_build_service_spec.rb25
-rw-r--r--spec/services/ci/run_scheduled_build_service_spec.rb62
-rw-r--r--spec/services/clusters/applications/check_installation_progress_service_spec.rb33
-rw-r--r--spec/services/clusters/applications/create_service_spec.rb130
-rw-r--r--spec/services/clusters/applications/install_service_spec.rb2
-rw-r--r--spec/services/clusters/applications/schedule_installation_service_spec.rb7
-rw-r--r--spec/services/clusters/create_service_spec.rb31
-rw-r--r--spec/services/clusters/gcp/fetch_operation_service_spec.rb2
-rw-r--r--spec/services/clusters/gcp/finalize_creation_service_spec.rb225
-rw-r--r--spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb115
-rw-r--r--spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb166
-rw-r--r--spec/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service_spec.rb62
-rw-r--r--spec/services/clusters/gcp/provision_service_spec.rb2
-rw-r--r--spec/services/clusters/update_service_spec.rb36
-rw-r--r--spec/services/commits/commit_patch_service_spec.rb92
-rw-r--r--spec/services/create_deployment_service_spec.rb335
-rw-r--r--spec/services/files/create_service_spec.rb16
-rw-r--r--spec/services/files/delete_service_spec.rb11
-rw-r--r--spec/services/files/multi_service_spec.rb36
-rw-r--r--spec/services/files/update_service_spec.rb11
-rw-r--r--spec/services/git_push_service_spec.rb52
-rw-r--r--spec/services/git_tag_push_service_spec.rb5
-rw-r--r--spec/services/groups/destroy_service_spec.rb11
-rw-r--r--spec/services/groups/transfer_service_spec.rb6
-rw-r--r--spec/services/groups/update_service_spec.rb6
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb2
-rw-r--r--spec/services/issuable/clone/attributes_rewriter_spec.rb79
-rw-r--r--spec/services/issuable/clone/content_rewriter_spec.rb153
-rw-r--r--spec/services/issuable/common_system_notes_service_spec.rb13
-rw-r--r--spec/services/issues/fetch_referenced_merge_requests_service_spec.rb35
-rw-r--r--spec/services/issues/move_service_spec.rb257
-rw-r--r--spec/services/issues/referenced_merge_requests_service_spec.rb133
-rw-r--r--spec/services/issues/related_branches_service_spec.rb39
-rw-r--r--spec/services/issues/update_service_spec.rb57
-rw-r--r--spec/services/merge_requests/build_service_spec.rb45
-rw-r--r--spec/services/merge_requests/create_from_issue_service_spec.rb10
-rw-r--r--spec/services/merge_requests/create_service_spec.rb2
-rw-r--r--spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb2
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb2
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb60
-rw-r--r--spec/services/merge_requests/reload_diffs_service_spec.rb33
-rw-r--r--spec/services/merge_requests/squash_service_spec.rb6
-rw-r--r--spec/services/merge_requests/update_service_spec.rb59
-rw-r--r--spec/services/milestones/destroy_service_spec.rb30
-rw-r--r--spec/services/notes/build_service_spec.rb15
-rw-r--r--spec/services/notes/create_service_spec.rb51
-rw-r--r--spec/services/notes/destroy_service_spec.rb33
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb2
-rw-r--r--spec/services/notification_recipient_service_spec.rb47
-rw-r--r--spec/services/notification_service_spec.rb177
-rw-r--r--spec/services/preview_markdown_service_spec.rb7
-rw-r--r--spec/services/projects/after_import_service_spec.rb4
-rw-r--r--spec/services/projects/after_rename_service_spec.rb198
-rw-r--r--spec/services/projects/auto_devops/disable_service_spec.rb100
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb4
-rw-r--r--spec/services/projects/container_repository/destroy_service_spec.rb41
-rw-r--r--spec/services/projects/create_service_spec.rb17
-rw-r--r--spec/services/projects/destroy_service_spec.rb26
-rw-r--r--spec/services/projects/detect_repository_languages_service_spec.rb4
-rw-r--r--spec/services/projects/fork_service_spec.rb33
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb6
-rw-r--r--spec/services/projects/import_service_spec.rb4
-rw-r--r--spec/services/projects/transfer_service_spec.rb37
-rw-r--r--spec/services/projects/unlink_fork_service_spec.rb9
-rw-r--r--spec/services/projects/update_remote_mirror_service_spec.rb100
-rw-r--r--spec/services/projects/update_service_spec.rb50
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb206
-rw-r--r--spec/services/resource_events/change_labels_service_spec.rb8
-rw-r--r--spec/services/resource_events/merge_into_notes_service_spec.rb79
-rw-r--r--spec/services/submodules/update_service_spec.rb212
-rw-r--r--spec/services/system_hooks_service_spec.rb2
-rw-r--r--spec/services/system_note_service_spec.rb81
-rw-r--r--spec/services/todo_service_spec.rb4
-rw-r--r--spec/services/update_deployment_service_spec.rb217
-rw-r--r--spec/services/users/build_service_spec.rb151
-rw-r--r--spec/services/users/destroy_service_spec.rb39
-rw-r--r--spec/services/web_hook_service_spec.rb2
-rw-r--r--spec/services/wikis/create_attachment_service_spec.rb224
-rw-r--r--spec/spec_helper.rb22
-rw-r--r--spec/support/api/milestones_shared_examples.rb26
-rw-r--r--spec/support/capybara.rb2
-rw-r--r--spec/support/controllers/githubish_import_controller_shared_examples.rb12
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb14
-rw-r--r--spec/support/features/issuable_quick_actions_shared_examples.rb389
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb306
-rw-r--r--spec/support/features/reportable_note_shared_examples.rb4
-rw-r--r--spec/support/features/variable_list_shared_examples.rb2
-rw-r--r--spec/support/helpers/ci_artifact_metadata_generator.rb48
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb25
-rw-r--r--spec/support/helpers/devise_helpers.rb9
-rw-r--r--spec/support/helpers/filter_item_select_helper.rb19
-rw-r--r--spec/support/helpers/filtered_search_helpers.rb8
-rw-r--r--spec/support/helpers/git_helpers.rb17
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb105
-rw-r--r--spec/support/helpers/ldap_helpers.rb17
-rw-r--r--spec/support/helpers/markdown_feature.rb8
-rw-r--r--spec/support/helpers/migrations_helpers.rb26
-rw-r--r--spec/support/helpers/project_forks_helper.rb4
-rw-r--r--spec/support/helpers/reference_parser_helpers.rb6
-rw-r--r--spec/support/helpers/seed_helper.rb20
-rw-r--r--spec/support/helpers/stub_configuration.rb7
-rw-r--r--spec/support/helpers/stub_feature_flags.rb7
-rw-r--r--spec/support/helpers/test_env.rb29
-rw-r--r--spec/support/helpers/test_request_helpers.rb11
-rw-r--r--spec/support/helpers/user_login_helper.rb26
-rw-r--r--spec/support/helpers/wiki_helpers.rb13
-rw-r--r--spec/support/import_export/export_file_helper.rb2
-rw-r--r--spec/support/rspec.rb2
-rw-r--r--spec/support/services/clusters/create_service_shared.rb4
-rw-r--r--spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb66
-rw-r--r--spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb89
-rw-r--r--spec/support/shared_examples/diff_file_collections.rb47
-rw-r--r--spec/support/shared_examples/dirty_submit_form_shared_examples.rb24
-rw-r--r--spec/support/shared_examples/fast_destroy_all.rb4
-rw-r--r--spec/support/shared_examples/features/editable_merge_request_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/helm_generated_script.rb10
-rw-r--r--spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/chat_service_spec.rb242
-rw-r--r--spec/support/shared_examples/models/cluster_application_core_shared_examples.rb56
-rw-r--r--spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb25
-rw-r--r--spec/support/shared_examples/models/cluster_application_status_shared_examples.rb113
-rw-r--r--spec/support/shared_examples/models/label_note_shared_examples.rb109
-rw-r--r--spec/support/shared_examples/notify_shared_examples.rb17
-rw-r--r--spec/support/shared_examples/requests/api/merge_requests_list.rb7
-rw-r--r--spec/support/shared_examples/requests/api/notes.rb82
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_move_service.rb12
-rw-r--r--spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/wiki_file_attachments_examples.rb88
-rw-r--r--spec/support/sidekiq.rb20
-rw-r--r--spec/support/stored_repositories.rb23
-rw-r--r--spec/support/stub_version.rb8
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb2
-rw-r--r--spec/tasks/gitlab/cleanup_rake_spec.rb35
-rw-r--r--spec/tasks/gitlab/db_rake_spec.rb33
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb67
-rw-r--r--spec/tasks/gitlab/site_statistics_rake_spec.rb23
-rw-r--r--spec/tasks/gitlab/traces_rake_spec.rb112
-rw-r--r--spec/tasks/gitlab/uploads/migrate_rake_spec.rb8
-rw-r--r--spec/unicorn/unicorn_spec.rb114
-rw-r--r--spec/uploaders/avatar_uploader_spec.rb8
-rw-r--r--spec/uploaders/file_uploader_spec.rb27
-rw-r--r--spec/uploaders/job_artifact_uploader_spec.rb47
-rw-r--r--spec/uploaders/namespace_file_uploader_spec.rb78
-rw-r--r--spec/uploaders/uploader_helper_spec.rb25
-rw-r--r--spec/validators/branch_filter_validator_spec.rb42
-rw-r--r--spec/validators/js_regex_validator_spec.rb27
-rw-r--r--spec/validators/url_validator_spec.rb15
-rw-r--r--spec/views/help/index.html.haml_spec.rb43
-rw-r--r--spec/views/projects/_home_panel.html.haml_spec.rb6
-rw-r--r--spec/views/projects/blob/_viewer.html.haml_spec.rb2
-rw-r--r--spec/views/projects/jobs/show.html.haml_spec.rb191
-rw-r--r--spec/views/projects/merge_requests/_commits.html.haml_spec.rb13
-rw-r--r--spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb16
-rw-r--r--spec/views/projects/merge_requests/edit.html.haml_spec.rb1
-rw-r--r--spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb8
-rw-r--r--spec/views/projects/tree/_blob_item.html.haml_spec.rb40
-rw-r--r--spec/views/projects/tree/_tree_row.html.haml_spec.rb37
-rw-r--r--spec/views/shared/runners/show.html.haml_spec.rb155
-rw-r--r--spec/workers/auto_devops/disable_worker_spec.rb57
-rw-r--r--spec/workers/build_success_worker_spec.rb54
-rw-r--r--spec/workers/ci/build_schedule_worker_spec.rb40
-rw-r--r--spec/workers/cluster_platform_configure_worker_spec.rb47
-rw-r--r--spec/workers/cluster_provision_worker_spec.rb9
-rw-r--r--spec/workers/delete_container_repository_worker_spec.rb33
-rw-r--r--spec/workers/deployments/success_worker_spec.rb36
-rw-r--r--spec/workers/email_receiver_worker_spec.rb15
-rw-r--r--spec/workers/emails_on_push_worker_spec.rb2
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb6
-rw-r--r--spec/workers/namespaceless_project_destroy_worker_spec.rb4
-rw-r--r--spec/workers/post_receive_spec.rb133
-rw-r--r--spec/workers/project_destroy_worker_spec.rb7
-rw-r--r--spec/workers/project_service_worker_spec.rb25
-rw-r--r--spec/workers/prune_old_events_worker_spec.rb20
-rw-r--r--spec/workers/rebase_worker_spec.rb12
-rw-r--r--spec/workers/repository_check/batch_worker_spec.rb2
-rw-r--r--spec/workers/repository_check/single_repository_worker_spec.rb2
-rw-r--r--spec/workers/repository_fork_worker_spec.rb32
-rw-r--r--spec/workers/repository_remove_remote_worker_spec.rb5
-rw-r--r--spec/workers/stuck_ci_jobs_worker_spec.rb41
-rw-r--r--spec/workers/stuck_import_jobs_worker_spec.rb22
-rw-r--r--vendor/Dockerfile/Node-alpine.Dockerfile18
-rw-r--r--vendor/Dockerfile/OpenJDK.Dockerfile14
-rw-r--r--vendor/Dockerfile/Ruby-alpine.Dockerfile6
-rw-r--r--vendor/gitignore/Android.gitignore4
-rw-r--r--vendor/gitignore/Delphi.gitignore3
-rw-r--r--vendor/gitignore/Elixir.gitignore1
-rw-r--r--vendor/gitignore/Global/Diff.gitignore2
-rw-r--r--vendor/gitignore/Global/Images.gitignore63
-rw-r--r--vendor/gitignore/Global/JetBrains.gitignore6
-rw-r--r--vendor/gitignore/Global/MicrosoftOffice.gitignore3
-rw-r--r--vendor/gitignore/Global/NetBeans.gitignore2
-rw-r--r--vendor/gitignore/Global/PSoCCreator.gitignore18
-rw-r--r--vendor/gitignore/Global/Xcode.gitignore74
-rw-r--r--vendor/gitignore/KiCad.gitignore1
-rw-r--r--vendor/gitignore/Laravel.gitignore2
-rw-r--r--vendor/gitignore/Magento.gitignore2
-rw-r--r--vendor/gitignore/Node.gitignore5
-rw-r--r--vendor/gitignore/Processing.gitignore2
-rw-r--r--vendor/gitignore/Python.gitignore10
-rw-r--r--vendor/gitignore/Rails.gitignore13
-rw-r--r--vendor/gitignore/Swift.gitignore7
-rw-r--r--vendor/gitignore/Symfony.gitignore4
-rw-r--r--vendor/gitignore/TeX.gitignore9
-rw-r--r--vendor/gitignore/Terraform.gitignore11
-rw-r--r--vendor/gitignore/Unity.gitignore1
-rw-r--r--vendor/gitignore/VisualStudio.gitignore6
-rw-r--r--vendor/gitlab-ci-yml/.gitlab-ci.yml4
-rw-r--r--vendor/gitlab-ci-yml/Android.gitlab-ci.yml51
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml833
-rw-r--r--vendor/gitlab-ci-yml/CONTRIBUTING.md46
-rw-r--r--vendor/gitlab-ci-yml/Julia.gitlab-ci.yml54
-rw-r--r--vendor/gitlab-ci-yml/LICENSE25
-rw-r--r--vendor/gitlab-ci-yml/Laravel.gitlab-ci.yml82
-rw-r--r--vendor/gitlab-ci-yml/Maven.gitlab-ci.yml102
-rw-r--r--vendor/gitlab-ci-yml/Python.gitlab-ci.yml51
-rw-r--r--vendor/gitlab-ci-yml/Swift.gitlab-ci.yml30
-rw-r--r--vendor/jupyter/values.yaml4
-rwxr-xr-xvendor/languages.yml5488
-rw-r--r--vendor/licenses.csv963
-rw-r--r--vendor/prometheus/values.yaml4
-rw-r--r--yarn.lock5625
7106 files changed, 406527 insertions, 76492 deletions
diff --git a/.babelrc b/.babelrc
deleted file mode 100644
index 50d85f58d69..00000000000
--- a/.babelrc
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "presets": [["latest", { "es2015": { "modules": false } }], "stage-2"],
- "env": {
- "karma": {
- "plugins": ["rewire"]
- },
- "coverage": {
- "plugins": [
- [
- "istanbul",
- {
- "exclude": ["spec/javascripts/**/*", "app/assets/javascripts/locale/**/app.js"]
- }
- ],
- [
- "transform-define",
- {
- "process.env.BABEL_ENV": "coverage"
- }
- ],
- "rewire"
- ]
- }
- }
-}
diff --git a/.babelrc.js b/.babelrc.js
new file mode 100644
index 00000000000..27caf378b99
--- /dev/null
+++ b/.babelrc.js
@@ -0,0 +1,38 @@
+const BABEL_ENV = process.env.BABEL_ENV || process.env.NODE_ENV || null;
+
+const presets = [
+ [
+ '@babel/preset-env',
+ {
+ modules: false,
+ targets: {
+ ie: '11',
+ },
+ },
+ ],
+];
+
+// include stage 3 proposals
+const plugins = [
+ '@babel/plugin-syntax-dynamic-import',
+ '@babel/plugin-syntax-import-meta',
+ '@babel/plugin-proposal-class-properties',
+ '@babel/plugin-proposal-json-strings',
+];
+
+// add code coverage tooling if necessary
+if (BABEL_ENV === 'coverage') {
+ plugins.push([
+ 'babel-plugin-istanbul',
+ {
+ exclude: ['spec/javascripts/**/*', 'app/assets/javascripts/locale/**/app.js'],
+ },
+ ]);
+}
+
+// add rewire support when running tests
+if (BABEL_ENV === 'karma' || BABEL_ENV === 'coverage') {
+ plugins.push('babel-plugin-rewire');
+}
+
+module.exports = { presets, plugins };
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 77b1b72fe68..fe0d5f5dce3 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -1,71 +1,37 @@
----
-env:
- browser: true
- es6: true
extends:
- - airbnb-base
- - plugin:vue/recommended
+ - '@gitlab'
globals:
__webpack_public_path__: true
gl: false
gon: false
localStorage: false
-parserOptions:
- parser: babel-eslint
plugins:
- - filenames
- import
- html
- - promise
settings:
html/html-extensions:
- - ".html"
- - ".html.raw"
+ - '.html'
+ - '.html.raw'
import/resolver:
webpack:
- config: "./config/webpack.config.js"
+ config: './config/webpack.config.js'
rules:
- filenames/match-regex:
- - error
- - "^[a-z0-9_]+$"
import/no-commonjs: error
- no-multiple-empty-lines:
- - error
- - max: 1
- promise/catch-or-return: error
no-underscore-dangle:
- error
- allow:
- - __
- - _links
- no-mixed-operators: off
- vue/html-self-closing:
- - error
- - html:
- void: always
- normal: never
- component: always
- svg: always
- math: always
- ## Conflicting rules with prettier:
- space-before-function-paren: off
- curly: off
- arrow-parens: off
- function-paren-newline: off
- object-curly-newline: off
- padded-blocks: off
- # Disabled for now, to make the eslint 3 -> eslint 4 update smoother
- ## Indent rule. We are using the old for now: https://eslint.org/docs/user-guide/migrating-to-4.0.0#indent-rewrite
- indent: off
- indent-legacy:
+ - __
+ - _links
+ # Disabled for now, to make the airbnb-base 12.1.0 -> 13.1.0 update smoother
+ no-else-return:
- error
- - 2
- - SwitchCase: 1
- VariableDeclarator: 1
- outerIIFEBody: 1
- FunctionDeclaration:
- parameters: 1
- body: 1
- FunctionExpression:
- parameters: 1
- body: 1
+ - allowElseIf: true
+ import/no-useless-path-segments: off
+ lines-between-class-members: off
+ # Disabled for now, to make the plugin-vue 4.5 -> 5.0 update smoother
+ vue/html-closing-bracket-newline: off
+ vue/html-closing-bracket-spacing: off
+ vue/no-confusing-v-for-v-if: error
+ vue/no-unused-components: off
+ vue/no-use-v-if-with-v-for: off
+ vue/no-v-html: off
diff --git a/.flayignore b/.flayignore
deleted file mode 100644
index 4b6f7ba693a..00000000000
--- a/.flayignore
+++ /dev/null
@@ -1,34 +0,0 @@
-*.erb
-lib/gitlab/sanitizers/svg/whitelist.rb
-lib/gitlab/diff/position_tracer.rb
-app/controllers/projects/approver_groups_controller.rb
-app/controllers/projects/approvers_controller.rb
-app/controllers/projects/protected_branches/merge_access_levels_controller.rb
-app/controllers/projects/protected_branches/push_access_levels_controller.rb
-app/controllers/projects/protected_tags/create_access_levels_controller.rb
-app/policies/project_policy.rb
-app/models/concerns/relative_positioning.rb
-app/workers/stuck_merge_jobs_worker.rb
-lib/gitlab/redis/*.rb
-lib/gitlab/gitaly_client/operation_service.rb
-app/models/project_services/packagist_service.rb
-lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
-lib/gitlab/background_migration/*
-app/models/project_services/kubernetes_service.rb
-lib/gitlab/workhorse.rb
-lib/gitlab/ci/trace/chunked_io.rb
-lib/gitlab/gitaly_client/ref_service.rb
-lib/gitlab/gitaly_client/commit_service.rb
-lib/gitlab/git/commit.rb
-lib/gitlab/git/tag.rb
-
-ee/db/**/*
-ee/app/serializers/ee/merge_request_widget_entity.rb
-ee/lib/api/epics.rb
-ee/lib/api/geo_nodes.rb
-ee/lib/ee/api/group_boards.rb
-ee/lib/ee/api/boards.rb
-ee/lib/ee/gitlab/ldap/sync/admin_users.rb
-ee/app/workers/geo/file_download_dispatch_worker/job_artifact_job_finder.rb
-ee/app/workers/geo/file_download_dispatch_worker/lfs_object_job_finder.rb
-ee/spec/**/*
diff --git a/.gitattributes b/.gitattributes
index f1c41c9bb76..7282c9e61b1 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,2 @@
Dangerfile gitlab-language=ruby
+db/schema.rb merge=merge_db_schema
diff --git a/.gitignore b/.gitignore
index 9a42a663fb4..aecaae95b8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@ eslint-report.html
/config/redis.queues.yml
/config/redis.shared_state.yml
/config/unicorn.rb
+/config/puma.rb
/config/secrets.yml
/config/sidekiq.yml
/config/registry.key
@@ -77,3 +78,5 @@ eslint-report.html
/plugins/*
/.gitlab_pages_secret
package-lock.json
+/junit_rspec.xml
+/junit_karma.xml
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fd02d72b4c2..0c662f42111 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.5.3-golang-1.9-git-2.18-chrome-69.0-node-10.x-yarn-1.12-postgresql-9.6-graphicsmagick-1.3.29"
.dedicated-runner: &dedicated-runner
retry: 1
@@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git
- gitlab-org
.default-cache: &default-cache
- key: "ruby-2.4.4-debian-stretch-with-yarn"
+ key: "debian-stretch-ruby-2.5.3-node-10.x"
paths:
- vendor/ruby
- .yarn-cache/
@@ -32,6 +32,7 @@ variables:
GET_SOURCES_ATTEMPTS: "3"
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json
FLAKY_RSPEC_SUITE_REPORT_PATH: rspec_flaky/report-suite.json
+ BUILD_ASSETS_IMAGE: "false"
before_script:
- bundle --version
@@ -67,10 +68,7 @@ stages:
.use-pg: &use-pg
services:
- # As of Jan 2018, we don't have a strong reason to upgrade to 9.6 for CI yet,
- # so using the least common denominator ensures backwards compatibility
- # (as many users are still using 9.2).
- - postgres:9.2
+ - postgres:9.6
- redis:alpine
.use-mysql: &use-mysql
@@ -78,24 +76,21 @@ stages:
- mysql:5.7
- redis:alpine
-.rails5-variables: &rails5-variables
- script:
- - export RAILS5=${RAILS5}
- - export BUNDLE_GEMFILE=${BUNDLE_GEMFILE}
-
-.rails5: &rails5
- allow_failure: true
- only:
+.rails4: &rails4
+ allow_failure: false
+ except:
variables:
- - $CI_COMMIT_REF_NAME =~ /rails5/
- - $RAILS5_ENABLED
+ - $CI_COMMIT_REF_NAME =~ /(^docs[\/-].*|.*-docs$)/
+ - $CI_COMMIT_REF_NAME =~ /(^qa[\/-].*|.*-qa$)/
+ - $CI_COMMIT_REF_NAME =~ /norails4/
+ - $RAILS5_DISABLED
variables:
- BUNDLE_GEMFILE: "Gemfile.rails5"
- RAILS5: "true"
+ BUNDLE_GEMFILE: "Gemfile.rails4"
+ RAILS5: "false"
# Skip all jobs except the ones that begin with 'docs/'.
# Used for commits including ONLY documentation changes.
-# https://docs.gitlab.com/ce/development/writing_documentation.html#testing
+# https://docs.gitlab.com/ce/development/documentation/#testing
.except-docs: &except-docs
except:
- /(^docs[\/-].*|.*-docs$)/
@@ -129,9 +124,8 @@ stages:
<<: *except-docs-and-qa
.single-script-job: &single-script-job
- image: ruby:2.4-alpine
- before_script: []
- stage: build
+ image: ruby:2.5-alpine
+ stage: test
cache: {}
dependencies: []
variables: &single-script-job-variables
@@ -143,7 +137,7 @@ stages:
- export SCRIPT_NAME="${SCRIPT_NAME:-$CI_JOB_NAME}"
- apk add --update openssl
- wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/$SCRIPT_NAME
- - chmod 755 $SCRIPT_NAME
+ - chmod 755 $(basename $SCRIPT_NAME)
.rake-exec: &rake-exec
<<: *dedicated-no-docs-no-db-pull-cache-job
@@ -154,24 +148,22 @@ stages:
<<: *dedicated-runner
<<: *except-docs-and-qa
<<: *pull-cache
- <<: *rails5-variables
stage: test
script:
- JOB_NAME=( $CI_JOB_NAME )
- - export CI_NODE_INDEX=${JOB_NAME[-2]}
- - export CI_NODE_TOTAL=${JOB_NAME[-1]}
- - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
+ - TEST_TOOL=${JOB_NAME[0]}
+ - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true
- export SUITE_FLAKY_RSPEC_REPORT_PATH=${FLAKY_RSPEC_SUITE_REPORT_PATH}
- - export FLAKY_RSPEC_REPORT_PATH=rspec_flaky/all_${JOB_NAME[0]}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- - export NEW_FLAKY_RSPEC_REPORT_PATH=rspec_flaky/new_${JOB_NAME[0]}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
+ - export FLAKY_RSPEC_REPORT_PATH=rspec_flaky/all_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
+ - export NEW_FLAKY_RSPEC_REPORT_PATH=rspec_flaky/new_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export FLAKY_RSPEC_GENERATE_REPORT=true
- export CACHE_CLASSES=true
- cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
- '[[ -f $FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_REPORT_PATH}'
- '[[ -f $NEW_FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${NEW_FLAKY_RSPEC_REPORT_PATH}'
- scripts/gitaly-test-spawn
- - knapsack rspec "--color --format documentation"
+ - knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml"
artifacts:
expire_in: 31d
when: always
@@ -180,22 +172,24 @@ stages:
- knapsack/
- rspec_flaky/
- tmp/capybara/
+ reports:
+ junit: junit_rspec.xml
.rspec-metadata-pg: &rspec-metadata-pg
<<: *rspec-metadata
<<: *use-pg
-.rspec-metadata-pg-rails5: &rspec-metadata-pg-rails5
+.rspec-metadata-pg-rails4: &rspec-metadata-pg-rails4
<<: *rspec-metadata-pg
- <<: *rails5
+ <<: *rails4
.rspec-metadata-mysql: &rspec-metadata-mysql
<<: *rspec-metadata
<<: *use-mysql
-.rspec-metadata-mysql-rails5: &rspec-metadata-mysql-rails5
+.rspec-metadata-mysql-rails4: &rspec-metadata-mysql-rails4
<<: *rspec-metadata-mysql
- <<: *rails5
+ <<: *rails4
.only-canonical-masters: &only-canonical-masters
only:
@@ -235,6 +229,8 @@ stages:
script:
- git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v9.3.0
- git checkout -f FETCH_HEAD
+ - sed -i "s/gem 'oj', '~> 2.17.4'//" Gemfile
+ - bundle update google-protobuf grpc
- bundle install $BUNDLE_INSTALL_FLAGS
- date
- cp config/gitlab.yml.example config/gitlab.yml
@@ -257,6 +253,7 @@ package-and-qa:
SCRIPT_NAME: trigger-build
retry: 0
script:
+ - gem install gitlab --no-document
- ./$SCRIPT_NAME omnibus
when: manual
only:
@@ -272,7 +269,7 @@ package-and-qa:
SCRIPT_NAME: trigger-build-docs
environment:
name: review-docs/$CI_COMMIT_REF_SLUG
- # DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are secret variables
+ # DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are CI variables
# Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693
url: http://$CI_ENVIRONMENT_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
on_stop: review-docs-cleanup
@@ -283,7 +280,7 @@ review-docs-deploy-manual:
<<: *review-docs
stage: build
script:
- - gem install gitlab --no-ri --no-rdoc
+ - gem install gitlab --no-document
- ./$SCRIPT_NAME deploy
when: manual
only:
@@ -297,7 +294,7 @@ review-docs-deploy:
<<: *review-docs
stage: post-test
script:
- - gem install gitlab --no-ri --no-rdoc
+ - gem install gitlab --no-document
- ./$SCRIPT_NAME deploy
only:
- /(^docs[\/-].*|.*-docs$)/@gitlab-org/gitlab-ce
@@ -311,25 +308,29 @@ review-docs-cleanup:
environment:
name: review-docs/$CI_COMMIT_REF_SLUG
action: stop
- when: manual
script:
- - gem install gitlab --no-ri --no-rdoc
+ - gem install gitlab --no-document
- ./$SCRIPT_NAME cleanup
+ when: manual
+ only:
+ - branches@gitlab-org/gitlab-ce
+ - branches@gitlab-org/gitlab-ee
##
# Trigger a docker image build in CNG (Cloud Native GitLab) repository
#
cloud-native-image:
- image: ruby:2.4-alpine
+ image: ruby:2.5-alpine
before_script: []
- stage: build
+ dependencies: []
+ stage: post-test
allow_failure: true
variables:
GIT_DEPTH: "1"
cache: {}
script:
- - gem install gitlab --no-ri --no-rdoc
- - BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN scripts/trigger-build cng
+ - gem install gitlab --no-document
+ - CNG_PROJECT_PATH="gitlab-org/build/CNG" BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN ./scripts/trigger-build cng
only:
- tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
@@ -361,7 +362,7 @@ update-tests-metadata:
- rspec_flaky/
policy: push
script:
- - retry gem install fog-aws mime-types activesupport --no-ri --no-rdoc
+ - retry gem install fog-aws mime-types activesupport --no-document
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
- scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json
- FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH}
@@ -372,7 +373,7 @@ update-tests-metadata:
flaky-examples-check:
<<: *dedicated-runner
- image: ruby:2.4-alpine
+ image: ruby:2.5-alpine
services: []
before_script: []
variables:
@@ -430,7 +431,7 @@ setup-test-env:
script:
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
- scripts/gitaly-test-build # Do not use 'bundle exec' here
- - BUNDLE_GEMFILE=Gemfile.rails5 bundle install $BUNDLE_INSTALL_FLAGS
+ - BUNDLE_GEMFILE=Gemfile.rails4 bundle install $BUNDLE_INSTALL_FLAGS
artifacts:
expire_in: 7d
paths:
@@ -439,10 +440,10 @@ setup-test-env:
- vendor/gitaly-ruby
danger-review:
+ <<: *pull-cache
image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger
stage: test
allow_failure: true
- cache: {}
dependencies: []
before_script: []
only:
@@ -456,131 +457,25 @@ danger-review:
- $CI_COMMIT_REF_NAME =~ /.*-stable(-ee)?-prepare-.*/
script:
- git version
+ - node --version
+ - yarn install --frozen-lockfile --cache-folder .yarn-cache
- danger --fail-on-errors=true
-rspec-pg 0 30: *rspec-metadata-pg
-rspec-pg 1 30: *rspec-metadata-pg
-rspec-pg 2 30: *rspec-metadata-pg
-rspec-pg 3 30: *rspec-metadata-pg
-rspec-pg 4 30: *rspec-metadata-pg
-rspec-pg 5 30: *rspec-metadata-pg
-rspec-pg 6 30: *rspec-metadata-pg
-rspec-pg 7 30: *rspec-metadata-pg
-rspec-pg 8 30: *rspec-metadata-pg
-rspec-pg 9 30: *rspec-metadata-pg
-rspec-pg 10 30: *rspec-metadata-pg
-rspec-pg 11 30: *rspec-metadata-pg
-rspec-pg 12 30: *rspec-metadata-pg
-rspec-pg 13 30: *rspec-metadata-pg
-rspec-pg 14 30: *rspec-metadata-pg
-rspec-pg 15 30: *rspec-metadata-pg
-rspec-pg 16 30: *rspec-metadata-pg
-rspec-pg 17 30: *rspec-metadata-pg
-rspec-pg 18 30: *rspec-metadata-pg
-rspec-pg 19 30: *rspec-metadata-pg
-rspec-pg 20 30: *rspec-metadata-pg
-rspec-pg 21 30: *rspec-metadata-pg
-rspec-pg 22 30: *rspec-metadata-pg
-rspec-pg 23 30: *rspec-metadata-pg
-rspec-pg 24 30: *rspec-metadata-pg
-rspec-pg 25 30: *rspec-metadata-pg
-rspec-pg 26 30: *rspec-metadata-pg
-rspec-pg 27 30: *rspec-metadata-pg
-rspec-pg 28 30: *rspec-metadata-pg
-rspec-pg 29 30: *rspec-metadata-pg
-
-rspec-mysql 0 30: *rspec-metadata-mysql
-rspec-mysql 1 30: *rspec-metadata-mysql
-rspec-mysql 2 30: *rspec-metadata-mysql
-rspec-mysql 3 30: *rspec-metadata-mysql
-rspec-mysql 4 30: *rspec-metadata-mysql
-rspec-mysql 5 30: *rspec-metadata-mysql
-rspec-mysql 6 30: *rspec-metadata-mysql
-rspec-mysql 7 30: *rspec-metadata-mysql
-rspec-mysql 8 30: *rspec-metadata-mysql
-rspec-mysql 9 30: *rspec-metadata-mysql
-rspec-mysql 10 30: *rspec-metadata-mysql
-rspec-mysql 11 30: *rspec-metadata-mysql
-rspec-mysql 12 30: *rspec-metadata-mysql
-rspec-mysql 13 30: *rspec-metadata-mysql
-rspec-mysql 14 30: *rspec-metadata-mysql
-rspec-mysql 15 30: *rspec-metadata-mysql
-rspec-mysql 16 30: *rspec-metadata-mysql
-rspec-mysql 17 30: *rspec-metadata-mysql
-rspec-mysql 18 30: *rspec-metadata-mysql
-rspec-mysql 19 30: *rspec-metadata-mysql
-rspec-mysql 20 30: *rspec-metadata-mysql
-rspec-mysql 21 30: *rspec-metadata-mysql
-rspec-mysql 22 30: *rspec-metadata-mysql
-rspec-mysql 23 30: *rspec-metadata-mysql
-rspec-mysql 24 30: *rspec-metadata-mysql
-rspec-mysql 25 30: *rspec-metadata-mysql
-rspec-mysql 26 30: *rspec-metadata-mysql
-rspec-mysql 27 30: *rspec-metadata-mysql
-rspec-mysql 28 30: *rspec-metadata-mysql
-rspec-mysql 29 30: *rspec-metadata-mysql
-
-rspec-pg-rails5 0 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 1 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 2 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 3 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 4 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 5 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 6 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 7 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 8 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 9 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 10 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 11 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 12 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 13 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 14 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 15 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 16 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 17 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 18 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 19 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 20 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 21 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 22 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 23 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 24 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 25 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 26 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 27 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 28 30: *rspec-metadata-pg-rails5
-rspec-pg-rails5 29 30: *rspec-metadata-pg-rails5
-
-rspec-mysql-rails5 0 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 1 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 2 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 3 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 4 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 5 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 6 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 7 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 8 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 9 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 10 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 11 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 12 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 13 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 14 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 15 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 16 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 17 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 18 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 19 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 20 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 21 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 22 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 23 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 24 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 25 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 26 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 27 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 28 30: *rspec-metadata-mysql-rails5
-rspec-mysql-rails5 29 30: *rspec-metadata-mysql-rails5
+rspec-pg:
+ <<: *rspec-metadata-pg
+ parallel: 50
+
+rspec-mysql:
+ <<: *rspec-metadata-mysql
+ parallel: 50
+
+rspec-pg-rails4:
+ <<: *rspec-metadata-pg-rails4
+ parallel: 50
+
+rspec-mysql-rails4:
+ <<: *rspec-metadata-mysql-rails4
+ parallel: 50
static-analysis:
<<: *dedicated-no-docs-no-db-pull-cache-job
@@ -590,7 +485,7 @@ static-analysis:
script:
- scripts/static-analysis
cache:
- key: "ruby-2.4.4-debian-stretch-with-yarn-and-rubocop"
+ key: "debian-stretch-ruby-2.5.3-node-10.x-and-rubocop"
paths:
- vendor/ruby
- .yarn-cache/
@@ -602,7 +497,7 @@ static-analysis:
docs lint:
<<: *dedicated-runner
<<: *except-qa
- image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
+ image: "registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-docs-lint"
stage: test
cache: {}
dependencies: []
@@ -610,8 +505,8 @@ docs lint:
script:
- scripts/lint-doc.sh
- scripts/lint-changelog-yaml
- - mv doc/ /nanoc/content/
- - cd /nanoc
+ - mv doc/ /tmp/gitlab-docs/content/
+ - cd /tmp/gitlab-docs
# Build HTML from Markdown
- bundle exec nanoc
# Check the internal links
@@ -626,14 +521,15 @@ downtime_check:
- /(^docs[\/-].*|.*-docs$)/
- /(^qa[\/-].*|.*-qa$)/
-rails5_gemfile_lock_check:
+rails4_gemfile_lock_check:
<<: *dedicated-no-docs-no-db-pull-cache-job
<<: *except-docs-and-qa
script:
- - scripts/rails5-gemfile-lock-check
+ - scripts/rails4-gemfile-lock-check
ee_compat_check:
<<: *rake-exec
+ dependencies: []
except:
- master
- tags
@@ -696,34 +592,45 @@ gitlab:setup-mysql:
# Frontend-related jobs
gitlab:assets:compile:
<<: *dedicated-no-docs-and-no-qa-pull-cache-job
+ image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.5.3-git-2.18-chrome-69.0-node-8.x-yarn-1.2-graphicsmagick-1.3.29-docker-18.06.1
dependencies: []
+ services:
+ - docker:stable-dind
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
SETUP_DB: "false"
SKIP_STORAGE_VALIDATION: "true"
WEBPACK_REPORT: "true"
- NO_COMPRESSION: "true"
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584
+ DOCKER_DRIVER: overlay2
+ DOCKER_HOST: tcp://docker:2375
script:
- date
- yarn install --frozen-lockfile --production --cache-folder .yarn-cache
- date
- free -m
- bundle exec rake gitlab:assets:compile
+ - scripts/build_assets_image
artifacts:
name: webpack-report
expire_in: 31d
paths:
- webpack-report/
+ - public/assets/
+ tags:
+ - docker
karma:
- <<: *dedicated-no-docs-and-no-qa-pull-cache-job
+ <<: *dedicated-no-docs-pull-cache-job
<<: *use-pg
dependencies:
- compile-assets
- setup-test-env
+ variables:
+ # we override the max_old_space_size to prevent OOM errors
+ NODE_OPTIONS: --max_old_space_size=3584
script:
- export BABEL_ENV=coverage CHROME_LOG_FILE=chrome_debug.log
- date
@@ -738,8 +645,10 @@ karma:
paths:
- chrome_debug.log
- coverage-javascript/
+ reports:
+ junit: junit_karma.xml
-codequality:
+code_quality:
<<: *dedicated-no-docs-no-db-pull-cache-job
image: docker:stable
allow_failure: true
@@ -757,9 +666,14 @@ codequality:
script:
# Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- - docker run --env SOURCE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
+ - docker run
+ --env SOURCE_CODE="$PWD"
+ --volume "$PWD":/code
+ --volume /var/run/docker.sock:/var/run/docker.sock
+ "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
- paths: [codeclimate.json]
+ reports:
+ codequality: gl-code-quality-report.json
expire_in: 1 week
sast:
@@ -783,7 +697,8 @@ sast:
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
artifacts:
- paths: [gl-sast-report.json]
+ reports:
+ sast: gl-sast-report.json
dependency_scanning:
<<: *dedicated-no-docs-no-db-pull-cache-job
@@ -805,7 +720,8 @@ dependency_scanning:
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
artifacts:
- paths: [gl-dependency-scanning-report.json]
+ reports:
+ dependency_scanning: gl-dependency-scanning-report.json
qa:internal:
<<: *dedicated-no-docs-no-db-pull-cache-job
@@ -823,6 +739,39 @@ qa:selectors:
- bundle install
- bundle exec bin/qa Test::Sanity::Selectors
+.qa-frontend-node: &qa-frontend-node
+ stage: test
+ variables:
+ NODE_OPTIONS: --max_old_space_size=3584
+ cache:
+ key: "$CI_JOB_NAME"
+ paths:
+ - .yarn-cache/
+ dependencies: []
+ before_script: []
+ script:
+ - date
+ - yarn install --frozen-lockfile --cache-folder .yarn-cache
+ - date
+ - yarn run webpack-prod
+
+qa-frontend-node:6:
+ <<: *qa-frontend-node
+ image: node:6-alpine
+
+qa-frontend-node:8:
+ <<: *qa-frontend-node
+ image: node:8-alpine
+
+qa-frontend-node:10:
+ <<: *qa-frontend-node
+ image: node:10-alpine
+
+qa-frontend-node:latest:
+ <<: *qa-frontend-node
+ image: node:alpine
+ allow_failure: true
+
coverage:
# Don't include dedicated-no-docs-no-db-pull-cache-job here since we need to
# download artifacts from all the rspec jobs instead of from setup-test-env only
@@ -845,9 +794,7 @@ coverage:
lint:javascript:report:
<<: *dedicated-no-docs-and-no-qa-pull-cache-job
stage: post-test
- dependencies:
- - compile-assets
- - setup-test-env
+ dependencies: []
before_script: []
script:
- date
@@ -874,6 +821,8 @@ pages:
- mv coverage-javascript/ public/coverage-javascript/ || true
- mv eslint-report.html public/ || true
- mv webpack-report/ public/webpack-report/ || true
+ - cp .public/assets/application-*.css public/application.css || true
+ - cp .public/assets/application-*.css.gz public/application.css.gz || true
artifacts:
paths:
- public
@@ -901,6 +850,7 @@ gitlab_git_test:
variables:
SETUP_DB: "false"
before_script: []
+ dependencies: []
cache: {}
script:
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
@@ -911,8 +861,98 @@ no_ee_check:
variables:
SETUP_DB: "false"
before_script: []
+ dependencies: []
cache: {}
script:
- scripts/no-ee-check
only:
- //@gitlab-org/gitlab-ce
+
+# GitLab Review apps
+review:
+ image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
+ stage: test
+ allow_failure: true
+ before_script:
+ - gem install gitlab --no-document
+ variables:
+ GIT_DEPTH: "1"
+ HOST_SUFFIX: "$CI_ENVIRONMENT_SLUG"
+ DOMAIN: "-$CI_ENVIRONMENT_SLUG.$REVIEW_APPS_DOMAIN"
+ GITLAB_HELM_CHART_REF: "master"
+ script:
+ - export GITLAB_SHELL_VERSION=$(<GITLAB_SHELL_VERSION)
+ - export GITALY_VERSION=$(<GITALY_SERVER_VERSION)
+ - export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION)
+ - source ./scripts/review_apps/review-apps.sh
+ - BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng
+ - check_kube_domain
+ - download_gitlab_chart
+ - ensure_namespace
+ - install_tiller
+ - install_external_dns
+ - deploy
+ environment:
+ name: review/$CI_COMMIT_REF_NAME
+ url: https://gitlab-$CI_ENVIRONMENT_SLUG.$REVIEW_APPS_DOMAIN
+ on_stop: stop_review
+ only:
+ refs:
+ - branches@gitlab-org/gitlab-ce
+ - branches@gitlab-org/gitlab-ee
+ kubernetes: active
+ except:
+ refs:
+ - master
+ - /(^docs[\/-].*|.*-docs$)/
+
+stop_review:
+ <<: *single-script-job
+ image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
+ stage: test
+ allow_failure: true
+ cache: {}
+ dependencies: []
+ variables:
+ SCRIPT_NAME: "review_apps/review-apps.sh"
+ script:
+ - source $(basename "${SCRIPT_NAME}")
+ - delete
+ - cleanup
+ when: manual
+ environment:
+ name: review/$CI_COMMIT_REF_NAME
+ action: stop
+ only:
+ refs:
+ - branches@gitlab-org/gitlab-ce
+ - branches@gitlab-org/gitlab-ee
+ kubernetes: active
+ except:
+ - master
+ - /(^docs[\/-].*|.*-docs$)/
+
+schedule:review_apps_cleanup:
+ <<: *dedicated-no-docs-pull-cache-job
+ image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
+ stage: build
+ allow_failure: true
+ cache: {}
+ dependencies: []
+ before_script:
+ - gem install gitlab --no-document
+ variables:
+ GIT_DEPTH: "1"
+ script:
+ - ruby -rrubygems scripts/review_apps/automated_cleanup.rb
+ environment:
+ name: review/auto-cleanup
+ action: stop
+ only:
+ refs:
+ - schedules@gitlab-org/gitlab-ce
+ - schedules@gitlab-org/gitlab-ee
+ kubernetes: active
+ except:
+ - tags
+ - /(^docs[\/-].*|.*-docs$)/
diff --git a/.gitlab/CODEOWNERS.disabled b/.gitlab/CODEOWNERS.disabled
new file mode 100644
index 00000000000..a4b773b15a9
--- /dev/null
+++ b/.gitlab/CODEOWNERS.disabled
@@ -0,0 +1,20 @@
+# Backend Maintainers are the default for all ruby files
+*.rb @ayufan @DouweM @dzaporozhets @grzesiek @nick.thomas @rspeicher @rymai @smcgivern
+*.rake @ayufan @DouweM @dzaporozhets @grzesiek @nick.thomas @rspeicher @rymai @smcgivern
+
+# Technical writing team are the default reviewers for everything in `doc/`
+/doc/ @axil @marcia
+
+# Frontend maintainers should see everything in `app/assets/`
+app/assets/ @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann
+*.scss @annabeldunstone @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann
+
+# Someone from the database team should review changes in `db/`
+db/ @abrandl @NikolayS
+
+# Feature specific owners
+/ee/lib/gitlab/code_owners/ @reprazent
+/ee/lib/ee/gitlab/auth/ldap/ @dblessing @mkozono
+/lib/gitlab/auth/ldap/ @dblessing @mkozono
+/lib/gitlab/ci/templates/ @nolith @zj
+/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah
diff --git a/.gitlab/issue_templates/Acceptance_Testing.md b/.gitlab/issue_templates/Acceptance_Testing.md
new file mode 100644
index 00000000000..f1fbb96ce61
--- /dev/null
+++ b/.gitlab/issue_templates/Acceptance_Testing.md
@@ -0,0 +1,100 @@
+## Details
+- **Feature Toggle Name**: `FEATURE_NAME`
+- **Required GitLab Version**: `vX.X`
+
+--------------------------------------------------------------------------------
+
+## 1. Preparation
+
+- [ ] **Controllers and workers**:
+ 1. Please link to dashboards of the workers, and the controllers and actions that can be impacted
+ 2. ...
+ 3. ...
+
+## 2. Development Trial
+
+#### Check Dev Server Versions
+- [ ] GitLab: https://dev.gitlab.org/help
+
+#### Enable on `dev.gitlab.org`:
+- [ ] `/chatops feature set FEATURE_NAME true --dev` in [`#dev-gitlab`](https://gitlab.slack.com/messages/C6WQ87MU3)
+
+Then leave running while monitoring and performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Dev Sentry](https://sentry.gitlap.com/gitlab/devgitlaborg/?query=is%3Aunresolved)
+
+## 2. Staging Trial
+
+#### Check Staging Server Versions
+- [ ] GitLab: https://staging.gitlab.com/help
+
+#### Enable on `staging.gitlab.com`
+- [ ] `/chatops run feature set FEATURE_NAME true --staging` in [`#development`](https://gitlab.slack.com/messages/C02PF508L/)
+
+Then leave running while monitoring for at least **15 minutes** while performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Sentry](https://sentry.gitlap.com/gitlab/gitlabcom/?query=is%3Aunresolved)
+
+## 4. Production Server Version Check
+
+- [ ] GitLab: https://gitlab.com/help
+
+## 5. Initial Impact Check
+
+- [ ] Enable for a subset of users, when using percentage gates: 1%.
+
+Then leave running while monitoring for at least **15 minutes** while performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Sentry](https://sentry.gitlap.com/gitlab/gitlabcom/?query=is%3Aunresolved)
+
+## 6. Low Impact Check
+
+- [ ] Enable for a bigger subset of users, when using percentage gates: 10%.
+
+Then leave running while monitoring for at least **30 minutes** while performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Sentry](https://sentry.gitlap.com/gitlab/gitlabcom/?query=is%3Aunresolved)
+
+## 7. Mid Impact Trial
+
+- [ ] Enable for a big subset of users, when using percentage gates: 50%.
+
+Then leave running while monitoring for at least **12 hours** while performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Sentry](https://sentry.gitlap.com/gitlab/gitlabcom/?query=is%3Aunresolved)
+
+## 8. Full Impact Trial
+
+- [ ] Enable for all users: `/chatops run feature set FEATURE_NAME true
+
+Then leave running while monitoring for at least **1 week**.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Dev Sentry](https://sentry.gitlap.com/gitlab/devgitlaborg/?query=is%3Aunresolved)
+
+#### Success?
+
+- [ ] Remove the feature gate from the code, and close this issue with that MR.
diff --git a/.gitlab/issue_templates/Documentation.md b/.gitlab/issue_templates/Documentation.md
new file mode 100644
index 00000000000..b33ed5bcaa8
--- /dev/null
+++ b/.gitlab/issue_templates/Documentation.md
@@ -0,0 +1,54 @@
+<!--See the general documentation guidelines https://docs.gitlab.com/ee/development/documentation -->
+
+<!-- Mention "documentation" or "docs" in the issue title -->
+
+<!-- Use this description template for new docs or updates to existing docs. -->
+
+<!-- Check the documentation structure guidelines for guidance: https://docs.gitlab.com/ee/development/documentation/structure.html-->
+
+- [ ] Documents Feature A <!-- feature name -->
+- [ ] Follow-up from: #XXX, !YYY <!-- Mention related issues, MRs, and epics when available -->
+
+## New doc or update?
+
+<!-- Mark either of these boxes: -->
+
+- [ ] New documentation
+- [ ] Update existing documentation
+
+## Checklists
+
+### Product Manager
+
+<!-- Reference: https://docs.gitlab.com/ee/development/documentation/workflow.html#1-product-manager-s-role-in-the-documentation-process -->
+
+- [ ] Add the correct labels
+- [ ] Add the correct milestone
+- [ ] Indicate the correct document/directory for this feature <!-- (ping the tech writers for help if you're not sure) -->
+- [ ] Fill the doc blurb below
+
+#### Documentation blurb
+
+<!-- Documentation template: https://docs.gitlab.com/ee/development/documentation/structure.html#documentation-template-for-new-docs -->
+
+- Doc **title**
+
+ <!-- write the doc title here -->
+
+- Feature **overview/description**
+
+ <!-- Write the feature overview here -->
+
+- Feature **use cases**
+
+ <!-- Write the use cases here -->
+
+### Developer
+
+<!-- Reference: https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process -->
+
+- [ ] Copy the doc blurb above and paste it into the doc
+- [ ] Write the tutorial - explain how to use the feature
+- [ ] Submit the MR using the appropriate MR description template
+
+/label ~Documentation
diff --git a/.gitlab/issue_templates/Security developer workflow.md b/.gitlab/issue_templates/Security developer workflow.md
index c1f702e9385..69cf7fe1548 100644
--- a/.gitlab/issue_templates/Security developer workflow.md
+++ b/.gitlab/issue_templates/Security developer workflow.md
@@ -12,23 +12,23 @@ Set the title to: `[Security] Description of the original issue`
- [ ] Link to the original issue adding it to the [links section](#links)
- [ ] Run `scripts/security-harness` in the CE, EE, and/or Omnibus to prevent pushing to any remote besides `dev.gitlab.org`
- [ ] Create an MR targetting `org` `master`, prefixing your branch with `security-`
-- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]`
+- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]`
- [ ] Add a link to the MR to the [links section](#links)
- [ ] Add a link to an EE MR if required
- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**.
-- [ ] Assign the MR to a RM once is reviewed and ready to be merged. Check the [RM list] to see who to ping.
#### Backports
- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases
- [ ] At this point, it might be easy to squash the commits from the MR into one
- - You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [seckpick documentation]
+ - You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [secpick documentation]
- [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable)
- [ ] Create each MR targetting the security branch `security-X-Y`
- [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR
-- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager.
+- [ ] Add the ~"Merge into Security" label to all of the MRs.
+- [ ] Make sure all MRs have a link in the [links section](#links)
-[seckpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script
+[secpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script
#### Documentation and final details
@@ -68,4 +68,4 @@ Set the title to: `[Security] Description of the original issue`
[security process for developers]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md
[RM list]: https://about.gitlab.com/release-managers/
-/label ~security
+/label ~security
diff --git a/.gitlab/issue_templates/Test plan.md b/.gitlab/issue_templates/Test plan.md
new file mode 100644
index 00000000000..a3c3f4a6509
--- /dev/null
+++ b/.gitlab/issue_templates/Test plan.md
@@ -0,0 +1,96 @@
+# Test Plan
+
+<!-- This issue outlines testing activities related to a particular issue or epic.
+
+[Here is an example test plan](https://gitlab.com/gitlab-org/gitlab-ce/issues/50353)
+
+This and other comments should be removed as you write the plan -->
+
+## Introduction
+
+<!-- Briefly outline what is being tested
+
+Mention the issue(s) this test plan is related to -->
+
+## Scope
+
+<!-- State any limits on aspects of the feature being tested
+Outline the types of data to be included
+Outline the types of tests to be performed (functional, security, performance,
+database, automated, etc) -->
+
+## ACC Matrix
+
+<!-- Use the matrix below as a template to identify the Attributes, Components, and
+Capabilities relevant to the scope of this test plan. Add or remove Attributes
+and Components as required and list Capabilities in the next section
+
+Attributes (columns) are adverbs or adjectives that describe (at a high level)
+the qualities testing is meant to ensure Components have.
+
+Components (rows) are nouns that define major parts of the product being tested.
+
+Capabilities link Attributes and Components. They are what your product needs to
+do to make sure a Component fulfills an Attribute
+
+For more information see the [Google Testing Blog article about the 10 minute
+test plan](https://testing.googleblog.com/2011/09/10-minute-test-plan.html) and
+[this wiki page from an open-source tool that implements the ACC
+model](https://code.google.com/archive/p/test-analytics/wikis/AccExplained.wiki). -->
+
+| | Secure | Responsive | Intuitive | Reliable |
+|------------|:------:|:----------:|:---------:|:--------:|
+| Admin | | | | |
+| Groups | | | | |
+| Project | | | | |
+| Repository | | | | |
+| Issues | | | | |
+| MRs | | | | |
+| CI/CD | | | | |
+| Ops | | | | |
+| Registry | | | | |
+| Wiki | | | | |
+| Snippets | | | | |
+| Settings | | | | |
+| Tracking | | | | |
+| API | | | | |
+
+## Capabilities
+
+<!-- Use the ACC matrix above to help you identify Capabilities at each relevant
+intersection of Components and Attributes.
+
+Some features might be simple enough that they only involve one Component, while
+more complex features could involve multiple or even all.
+
+Example (from https://gitlab.com/gitlab-org/gitlab-ce/issues/50353):
+* Repository is
+ * Intuitive
+ * It's easy to select the desired file template
+ * It doesn't require unnecessary actions to save the change
+ * It's easy to undo the change after selecting a template
+ * Responsive
+ * The list of templates can be restricted to allow a user to find a specific template among many
+ * Once a template is selected the file content updates quickly and smoothly
+-->
+
+## Test Plan
+
+<!-- If the scope is small enough you may not need to write a list of tests to
+perform. It might be enough to use the Capabilities to guide your testing.
+
+If the feature is more complex, especially if it involves multiple Components,
+briefly outline a set of tests here. When identifying tests to perform be sure
+to consider risk. Note inherent/known levels of risk so that testing can focus
+on high risk areas first.
+
+New end-to-end and integration tests (Selenium and API) should be added to the
+[Test Coverage sheet](https://docs.google.com/spreadsheets/d/1RlLfXGboJmNVIPP9jgFV5sXIACGfdcFq1tKd7xnlb74/)
+
+Please note if automated tests already exist.
+
+When adding new automated tests, please keep [testing levels](https://docs.gitlab.com/ce/development/testing_guide/testing_levels.html)
+in mind.
+-->
+
+/label ~Quality ~"test plan"
diff --git a/.gitlab/merge_request_templates/Change documentation location.md b/.gitlab/merge_request_templates/Change documentation location.md
new file mode 100644
index 00000000000..b4a6d2bd3b4
--- /dev/null
+++ b/.gitlab/merge_request_templates/Change documentation location.md
@@ -0,0 +1,32 @@
+<!--See the general Documentation guidelines https://docs.gitlab.com/ee/development/documentation/ -->
+
+<!-- Use this description template for changing documentation location. For new docs or updates to existing docs, use the "Documentation" template -->
+
+## What does this MR do?
+
+<!-- Briefly describe what this MR is about -->
+
+## Related issues
+
+<!-- Mention the issue(s) this MR closes or is related to -->
+
+Closes
+
+## Moving docs to a new location?
+
+Read the guidelines:
+https://docs.gitlab.com/ce/development/documentation/index.html#changing-document-location
+
+- [ ] Make sure the old link is not removed and has its contents replaced with
+ a link to the new location.
+- [ ] Make sure internal links pointing to the document in question are not broken.
+- [ ] Search and replace any links referring to old docs in GitLab Rails app,
+ specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
+- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/writing_documentation.html#redirections-for-pages-with-disqus-comments)
+ to the new document if there are any Disqus comments on the old document thread.
+- [ ] Update the link in `features.yml` (if applicable)
+- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
+ with the changes as well (https://docs.gitlab.com/ce/development/writing_documentation.html#cherry-picking-from-ce-to-ee).
+- [ ] Ping one of the technical writers for review.
+
+/label ~Documentation
diff --git a/.gitlab/merge_request_templates/Database changes.md b/.gitlab/merge_request_templates/Database changes.md
index e636ec313df..354393b60e0 100644
--- a/.gitlab/merge_request_templates/Database changes.md
+++ b/.gitlab/merge_request_templates/Database changes.md
@@ -1,8 +1,23 @@
-Add a description of your merge request here. Merge requests without an adequate
-description will not be reviewed until one is added.
+## What does this MR do?
+
+<!--
+Describe in detail what your merge request does, why it does that, etc. Merge
+requests without an adequate description will not be reviewed until one is
+added.
+
+Please also keep this description up-to-date with any discussion that takes
+place so that reviewers can understand your intent. This is especially
+important if they didn't participate in the discussion.
+
+Make sure to remove this comment when you are done.
+-->
+
+Add a description of your merge request here.
## Database checklist
+- [ ] Conforms to the [database guides](https://docs.gitlab.com/ee/development/README.html#databases-guides)
+
When adding migrations:
- [ ] Updated `db/schema.rb`
@@ -35,16 +50,9 @@ When removing columns, tables, indexes or other structures:
- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/index.html#contributing-to-docs)
-- [ ] [API support added](https://docs.gitlab.com/ee/development/api_styleguide.html)
- [ ] [Tests added for this feature/bug](https://docs.gitlab.com/ee/development/testing_guide/index.html)
-- Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
- - [ ] Has been reviewed by a Backend [maintainer](https://about.gitlab.com/handbook/engineering/#maintainer)
- - [ ] Has been reviewed by a Database [specialist](https://about.gitlab.com/team/structure/#specialist)
+- [ ] Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
- [ ] Conforms to the [merge request performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
- [ ] Conforms to the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
-- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
-- [ ] [Internationalization required/considered](https://docs.gitlab.com/ee/development/i18n/index.html)
-- [ ] For a paid feature, have we considered GitLab.com plans, how it works for groups, and is there a design for promoting it to users who aren't on the correct plan?
-- [ ] [End-to-end tests](https://docs.gitlab.com/ee/development/testing_guide/end_to_end_tests.html#testing-code-in-merge-requests) pass (`package-and-qa` manual pipeline job)
/label ~database
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index 531035b3766..8b7e7119790 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -1,4 +1,8 @@
-<!--See the general Documentation guidelines https://docs.gitlab.com/ee/development/documentation/index.html -->
+<!--See the general documentation guidelines https://docs.gitlab.com/ee/development/documentation -->
+
+<!-- Mention "documentation" or "docs" in the MR title -->
+
+<!-- Use this description template for new docs or updates to existing docs. For changing documentation location use the "Change documentation location" template -->
## What does this MR do?
@@ -10,20 +14,20 @@
Closes
-## Moving docs to a new location?
-
-Read the guidelines:
-https://docs.gitlab.com/ee/development/documentation/#changing-document-location
-
-- [ ] Make sure the old link is not removed and has its contents replaced with
- a link to the new location.
-- [ ] Make sure internal links pointing to the document in question are not broken.
-- [ ] Search and replace any links referring to the old docs in the GitLab Rails app,
- specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
-- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
- to the new document if there are any Disqus comments on the old document thread.
-- [ ] If working on CE and the `ee-compat-check` jobs fails, [submit an MR to EE
- with the changes](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee) as well.
-- [ ] Ping one of the technical writers for review.
+## Author's checklist
+
+- [ ] [Apply the correct labels and milestone](https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process)
+- [ ] Crosslink the document from the higher-level index
+- [ ] Crosslink the document from other subject-related docs
+- [ ] Feature moving tiers? Make sure the change is also reflected in [`features.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
+- [ ] Correctly apply the product [badges](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) and [tiers](https://docs.gitlab.com/ee/development/documentation/styleguide.html#gitlab-versions-and-tiers)
+- [ ] [Port the MR to EE (or backport from CE)](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee): _always recommended, required when the `ee-compat-check` job fails_
+
+## Review checklist
+
+- [ ] Your team's review (required)
+- [ ] PM's review (recommended, but not a blocker)
+- [ ] Technical writer's review (required)
+- [ ] Merge the EE-MR first, CE-MR afterwards
/label ~Documentation
diff --git a/.haml-lint.yml b/.haml-lint.yml
index 32c7de0fb78..bad918ef35d 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -70,14 +70,15 @@ linters:
enabled: false
RuboCop:
- enabled: false
+ enabled: true
# These cops are incredibly noisy when it comes to HAML templates, so we
# ignore them.
ignored_cops:
- - Lint/BlockAlignment
- - Lint/EndAlignment
+ - Layout/BlockAlignment
+ - Layout/EndAlignment
- Lint/Void
- Metrics/LineLength
+ - Naming/FileName
- Style/AlignParameters
- Style/BlockNesting
- Style/ElseAlignment
@@ -91,6 +92,51 @@ linters:
- Style/TrailingWhitespace
- Style/WhileUntilModifier
+ # These cops should eventually get enabled
+ - Cop/LineBreakAfterGuardClauses
+ - Cop/LineBreakAroundConditionalBlock
+ - Cop/ProjectPathHelper
+ - GitlabSecurity/PublicSend
+ - Layout/LeadingCommentSpace
+ - Layout/SpaceAfterColon
+ - Layout/SpaceAfterComma
+ - Layout/SpaceAroundOperators
+ - Layout/SpaceBeforeBlockBraces
+ - Layout/SpaceBeforeComma
+ - Layout/SpaceBeforeFirstArg
+ - Layout/SpaceInsideArrayLiteralBrackets
+ - Layout/SpaceInsideHashLiteralBraces
+ - Layout/SpaceInsideStringInterpolation
+ - Layout/TrailingBlankLines
+ - Lint/BooleanSymbol
+ - Lint/LiteralInInterpolation
+ - Lint/ParenthesesAsGroupedExpression
+ - Lint/RedundantWithIndex
+ - Lint/Syntax
+ - Metrics/BlockNesting
+ - Naming/VariableName
+ - Performance/RedundantMatch
+ - Performance/StringReplacement
+ - Rails/Presence
+ - Rails/RequestReferer
+ - Style/AndOr
+ - Style/ColonMethodCall
+ - Style/ConditionalAssignment
+ - Style/HashSyntax
+ - Style/IdenticalConditionalBranches
+ - Style/NegatedIf
+ - Style/NestedTernaryOperator
+ - Style/Not
+ - Style/ParenthesesAroundCondition
+ - Style/RedundantParentheses
+ - Style/SelfAssignment
+ - Style/Semicolon
+ - Style/TernaryParentheses
+ - Style/TrailingCommaInHashLiteral
+ - Style/UnlessElse
+ - Style/WordArray
+ - Style/ZeroLengthPredicate
+
RubyComments:
enabled: true
diff --git a/.nvmrc b/.nvmrc
index dba04c1e178..db24ab967fb 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-8.11.3
+10.13.0
diff --git a/.prettierignore b/.prettierignore
index b674ccd50cf..dc9e572ab54 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -3,3 +3,7 @@
/public/
/vendor/
/tmp/
+
+# ignore stylesheets for now as this clashes with our linter
+*.css
+*.scss
diff --git a/.rubocop.yml b/.rubocop.yml
index a586190319b..a95ded8af1c 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -3,7 +3,9 @@ inherit_gem:
- rubocop-default.yml
inherit_from: .rubocop_todo.yml
-require: ./rubocop/rubocop
+require:
+ - ./rubocop/rubocop
+ - rubocop-rspec
AllCops:
TargetRailsVersion: 4.2
@@ -35,6 +37,33 @@ Style/MutableConstant:
Style/SafeNavigation:
Enabled: false
+# Frozen String Literal
+Style/FrozenStringLiteralComment:
+ Enabled: true
+ Exclude:
+ - 'config.ru'
+ - 'Dangerfile'
+ - 'Gemfile'
+ - 'Rakefile'
+ - 'app/views/**/*'
+ - 'config/**/*'
+ - 'danger/**/*'
+ - 'db/**/*'
+ - 'ee/**/*'
+ - 'lib/gitlab/**/*'
+ - 'lib/tasks/**/*'
+ - 'qa/**/*'
+ - 'rubocop/**/*'
+ - 'scripts/**/*'
+ - 'spec/**/*'
+
+RSpec/FilePath:
+ Exclude:
+ - 'qa/**/*'
+ - 'spec/javascripts/fixtures/*'
+ - 'ee/spec/javascripts/fixtures/*'
+ - 'spec/requests/api/v3/*'
+
Naming/FileName:
ExpectMatchingDefinition: true
Exclude:
@@ -47,13 +76,20 @@ Naming/FileName:
- 'qa/qa/specs/**/*'
- 'qa/bin/*'
- 'config/**/*'
+ - 'ee/config/**/*'
- 'lib/generators/**/*'
+ - 'locale/unfound_translations.rb'
+ - 'ee/locale/unfound_translations.rb'
- 'ee/lib/generators/**/*'
+ - 'qa/qa/scenario/test/integration/ldap_no_tls.rb'
+ - 'qa/qa/scenario/test/integration/ldap_tls.rb'
+
IgnoreExecutableScripts: true
AllowedAcronyms:
- EE
- JSON
- LDAP
+ - SAML
- IO
- HMAC
- QA
@@ -109,7 +145,7 @@ Naming/FileName:
- XSRF
- XSS
-# Gitlab ###################################################################
+# GitLab ###################################################################
Gitlab/ModuleWithInstanceVariables:
Enable: true
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 54e3b8217d8..571df7534cb 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -10,24 +10,6 @@
Capybara/CurrentPathExpectation:
Enabled: false
-# Offense count: 23
-FactoryBot/DynamicAttributeDefinedStatically:
- Exclude:
- - 'spec/factories/broadcast_messages.rb'
- - 'spec/factories/ci/builds.rb'
- - 'spec/factories/ci/runners.rb'
- - 'spec/factories/clusters/applications/helm.rb'
- - 'spec/factories/clusters/platforms/kubernetes.rb'
- - 'spec/factories/emails.rb'
- - 'spec/factories/gpg_keys.rb'
- - 'spec/factories/group_members.rb'
- - 'spec/factories/merge_requests.rb'
- - 'spec/factories/notes.rb'
- - 'spec/factories/oauth_access_grants.rb'
- - 'spec/factories/project_members.rb'
- - 'spec/factories/todos.rb'
- - 'spec/factories/uploads.rb'
-
# Offense count: 167
# Cop supports --auto-correct.
Layout/EmptyLinesAroundArguments:
@@ -53,20 +35,6 @@ Layout/IndentArray:
Layout/IndentHash:
Enabled: false
-# Offense count: 11
-# Cop supports --auto-correct.
-# Configuration parameters: AllowForAlignment.
-Layout/SpaceBeforeFirstArg:
- Exclude:
- - 'config/routes/project.rb'
- - 'db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb'
- - 'features/steps/project/source/browse_files.rb'
- - 'features/steps/project/source/markdown_render.rb'
- - 'lib/api/runners.rb'
- - 'spec/features/search/user_uses_search_filters_spec.rb'
- - 'spec/routing/project_routing_spec.rb'
- - 'spec/services/system_note_service_spec.rb'
-
# Offense count: 93
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
@@ -74,15 +42,6 @@ Layout/SpaceBeforeFirstArg:
Layout/SpaceInLambdaLiteral:
Enabled: false
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets.
-# SupportedStyles: space, no_space, compact
-# SupportedStylesForEmptyBrackets: space, no_space
-Layout/SpaceInsideArrayLiteralBrackets:
- Exclude:
- - 'spec/lib/gitlab/import_export/relation_factory_spec.rb'
-
# Offense count: 327
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
@@ -96,14 +55,6 @@ Layout/SpaceInsideBlockBraces:
Layout/SpaceInsideParens:
Enabled: false
-# Offense count: 14
-# Cop supports --auto-correct.
-Layout/SpaceInsidePercentLiteralDelimiters:
- Exclude:
- - 'lib/gitlab/git_access.rb'
- - 'lib/gitlab/health_checks/fs_shards_check.rb'
- - 'spec/lib/gitlab/health_checks/fs_shards_check_spec.rb'
-
# Offense count: 26
Lint/DuplicateMethods:
Exclude:
@@ -135,31 +86,11 @@ Lint/InterpolationCheck:
Lint/MissingCopEnableDirective:
Enabled: false
-# Offense count: 2
-Lint/NestedPercentLiteral:
- Exclude:
- - 'lib/gitlab/git/repository.rb'
- - 'spec/support/shared_examples/email_format_shared_examples.rb'
-
# Offense count: 1
Lint/ReturnInVoidContext:
Exclude:
- 'app/models/project.rb'
-# Offense count: 1
-# Configuration parameters: IgnoreImplicitReferences.
-Lint/ShadowedArgument:
- Exclude:
- - 'lib/gitlab/database/sha_attribute.rb'
-
-# Offense count: 3
-# Cop supports --auto-correct.
-Lint/UnneededRequireStatement:
- Exclude:
- - 'db/post_migrate/20161221153951_rename_reserved_project_names.rb'
- - 'db/post_migrate/20170313133418_rename_more_reserved_project_names.rb'
- - 'lib/declarative_policy.rb'
-
# Offense count: 9
Lint/UriEscapeUnescape:
Exclude:
@@ -199,16 +130,6 @@ Naming/HeredocDelimiterCase:
Naming/HeredocDelimiterNaming:
Enabled: false
-# Offense count: 1
-Performance/UnfreezeString:
- Exclude:
- - 'features/steps/project/commits/commits.rb'
-
-# Offense count: 1
-# Cop supports --auto-correct.
-Performance/UriDefaultParser:
- Exclude:
- - 'lib/gitlab/url_sanitizer.rb'
# Offense count: 3821
# Configuration parameters: Prefixes.
@@ -445,7 +366,6 @@ Style/Dir:
# Cop supports --auto-correct.
Style/EachWithObject:
Exclude:
- - 'config/initializers/gollum.rb'
- 'lib/expand_variables.rb'
- 'lib/gitlab/ci/ansi2html.rb'
- 'lib/gitlab/ee_compat_check.rb'
@@ -808,20 +728,6 @@ Style/UnlessElse:
Style/UnneededInterpolation:
Enabled: false
-# Offense count: 11
-# Cop supports --auto-correct.
-Style/ZeroLengthPredicate:
- Exclude:
- - 'app/models/deploy_key.rb'
- - 'app/models/network/commit.rb'
- - 'app/models/network/graph.rb'
- - 'app/models/project_services/asana_service.rb'
- - 'app/services/boards/create_service.rb'
- - 'app/services/merge_requests/conflicts/list_service.rb'
- - 'lib/declarative_policy/dsl.rb'
- - 'lib/extracts_path.rb'
- - 'lib/gitlab/git/repository.rb'
-
# Offense count: 22840
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
diff --git a/.ruby-version b/.ruby-version
index 79a614418f7..aedc15bb0c6 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.4.4
+2.5.3
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7be28a9ac0e..54ea014a679 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,951 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.4.6 (2018-11-18)
+
+### Security (1 change)
+
+- Escape user fullname while rendering autocomplete template to prevent XSS.
+
+
+## 11.4.5 (2018-11-04)
+
+### Fixed (4 changes, 1 of them is from the community)
+
+- fix link to enable usage ping from convdev index. !22545 (Anand Capur)
+- Update gitlab-ui dependency to 1.8.0-hotfix.1 to fix IE11 bug.
+- Remove duplicate escape in job sidebar.
+- Fixed merge request fill tree toggling not respecting fluid width preference.
+
+### Other (1 change)
+
+- Fix stage dropdown not rendering in different languages.
+
+
+## 11.4.4 (2018-10-30)
+
+### Security (1 change)
+
+- Monkey kubeclient to not follow any redirects.
+
+
+## 11.4.3 (2018-10-26)
+
+- No changes.
+
+## 11.4.2 (2018-10-25)
+
+### Security (5 changes)
+
+- Escape entity title while autocomplete template rendering to prevent XSS. !2571
+- Persist only SHA digest of PersonalAccessToken#token.
+- Redact personal tokens in unsubscribe links.
+- Block loopback addresses in UrlBlocker.
+- Validate Wiki attachments are valid temporary files.
+
+
+## 11.4.1 (2018-10-23)
+
+### Security (2 changes)
+
+- Fix XSS in merge request source branch name.
+- Prevent SSRF attacks in HipChat integration.
+
+
+## 11.4.0 (2018-10-22)
+
+### Security (9 changes)
+
+- Filter user sensitive data from discussions JSON. !2536
+- Encrypt webhook tokens and URLs in the database. !21645
+- Redact confidential events in the API.
+- Set timeout for syntax highlighting.
+- Sanitize JSON data properly to fix XSS on Issue details page.
+- Markdown API no longer displays confidential title references unless authorized.
+- Properly filter private references from system notes.
+- Fix stored XSS in merge requests from imported repository.
+- Fix xss vulnerability sourced from package.json.
+
+### Removed (2 changes)
+
+- Remove background job throttling feature. !21748
+- Remove sidekiq info from performance bar.
+
+### Fixed (68 changes, 18 of them are from the community)
+
+- Fixes 500 for cherry pick API with empty branch name. !21501 (Jacopo Beschi @jacopo-beschi)
+- Fix sorting by priority or popularity on group issues page, when also searching issue content. !21521
+- Fix vertical alignment of text in diffs. !21573
+- Fix performance bar modal position. !21577
+- Bump KaTeX version to 0.9.0. !21625
+- Correctly show legacy diff notes in the merge request changes tab. !21652
+- Synchronize the default branch when updating a remote mirror. !21653
+- Filter group milestones based on user membership. !21660
+- Fix double title in merge request chat messages. !21670 (Kukovskii Vladimir)
+- Delete container repository tags outside of transaction. !21679
+- Images are no longer displayed in Todo descriptions. !21704
+- Fixed merge request widget discussion state not updating after resolving discussions. !21705
+- Vendor Auto-DevOps.gitlab-ci.yml to fix bug where the deploy job does not wait for Deployment to complete. !21713
+- Use Reliable Sidekiq fetch. !21715
+- No longer show open issues from archived projects in group issue board. !21721
+- Issue and MR count now ignores archived projects. !21721
+- Fix resizing of monitoring dashboard. !21730
+- Fix object storage uploads not working with AWS v2. !21731
+- Don't ignore first action when assign and unassign quick actions are used in the same comment. !21749
+- Align form labels following Bootstrap 4 docs. !21752
+- Respect the user commit email in more places. !21773
+- Use stats RPC when comparing diffs. !21778
+- Show commit details for selected commit in MR diffs. !21784
+- Resolve "Geo: Does not mark repositories as missing on primary due to stale cache". !21789
+- Fix leading slash in redirects and add rubocop cop. !21828 (Sanad Liaquat)
+- Fix activity titles for MRs in chat notification services. !21834
+- Hides Close Merge request btn on merged Merge request. !21840 (Jacopo Beschi @jacopo-beschi)
+- Doesn't synchronize the default branch for push mirrors. !21861
+- Fix broken styling when issue board is collapsed. !21868 (Andrea Leone)
+- Set a header for custom error pages to prevent them from being intercepted by gitlab-workhorse. !21870 (David Piegza)
+- Fix resolved discussions being unresolved when commented on. !21881
+- Fix timeout when running the RemoveRestrictedTodos background migration. !21893
+- Enable the ability to use the force env for rebuilding authorized_keys during a restore. !21896
+- Fix link handling for issue cards to avoid too sensitive drag events. !21910 (Johann Hubert Sonntagbauer)
+- Guard against a login attempt with invalid CSRF token. !21934
+- Allow setting user's organization and location attributes through the API by adding them to the list of allowed parameters. !21938 (Alexis Reigel)
+- Includes commit stats in POST project commits API. !21968 (Jacopo Beschi @jacopo-beschi)
+- Fix loading issue on some merge request discussion. !21982
+- Prevent Error 500s with invalid relative links. !22001
+- Fix stale issue boards after browser back. !22006 (Johann Hubert Sonntagbauer)
+- Filter issues without an Assignee via the API. !22009 (Eva Kadlecová)
+- Fixes modal button alignment. !22024 (Jacopo Beschi @jacopo-beschi)
+- Fix rendering placeholder notes. !22078
+- Instance Configuration page now displays correct SSH fingerprints. !22081
+- Fix showing diff file header for renamed files. !22089
+- Fix LFS uploaded images not being rendered. !22092
+- Fix the issue where long environment names aren't being truncated, causing the environment name to overlap into the column next to it. !22104
+- Trim whitespace when inviting a new user by email. !22119 (Jacopo Beschi @jacopo-beschi)
+- Fix incorrect parent path on group settings page. !22142
+- Update copy to clipboard button data for application secret. !22268 (George Tsiolis)
+- Improve MR file tree in smaller screens. !22273
+- Fix project deletion when there is a export available. !22276
+- Fixes stuck block URL linking to documentation instead of settings page. !22286
+- Fix caching issue with pipelines URL. !22293
+- Fix erased block not being rendered when job was erased. !22294
+- Load correct stage in the stages dropdown. !22317
+- Fixes close/reopen quick actions preview for issues and merge_requests. !22343 (Jacopo Beschi @jacopo-beschi)
+- Allow Issue and Merge Request sidebar to be toggled from collapsed state. !22353
+- Fix filter bar height bug when a tag is added.
+- Fix the state of the Done button when there is an error in the GitLab Todos section. (marcos8896)
+- Fix wrong text color of help text in merge request creation. (Gerard Montemayor)
+- Add borders and white background to markdown tables.
+- Fixed mention autocomplete in edit merge request.
+- Fix long webhook URL overflow for custom integration. (Kukovskii Vladimir)
+- Fixed file templates not fully being fetched in Web IDE.
+- Fixes performance bar looking for a key in a undefined prop.
+- Hides sidebar for job page in mobile.
+- Fixes triggered/created labeled in job header.
+
+### Changed (26 changes, 4 of them are from the community)
+
+- Enable unauthenticated access to public SSH keys via the API. !20118 (Ronald Claveau)
+- Support Kubernetes RBAC for GitLab Managed Apps when creating new clusters. !21401
+- Highlight current user in comments. !21406
+- Excludes project marked from deletion to projects API. !21542 (Jacopo Beschi @jacopo-beschi)
+- Improve install flow of Kubernetes cluster apps. !21567
+- Move including external files in .gitlab-ci.yml from Starter to Libre. !21603
+- Simplify runner registration token resetting. !21658
+- Filter any parameters ending with "key" in logs. !21688
+- Ensure the schema is loaded with post_migrations included. !21689
+- Updated icons used in filtered search dropdowns. !21694
+- Enable omniauth by default. !21700
+- Vendor Auto-DevOps.gitlab-ci.yml to refactor registry_login. !21714 (Laurent Goderre @LaurentGoderre)
+- Add Gitaly diff stats RPC client. !21732
+- Allow user to revoke an authorized application even if User OAuth applications setting is disabled in admin settings. !21835
+- Change vertical margin of page titles to 16px. !21888
+- Preserve order of project tags list. !21897
+- Avoid close icon leaving the modal header. !21904
+- Allow /copy_metadata for new issues and MRs. !21953
+- Link to the tag for a version on the help page instead of to the commit. !22015
+- Show SHA for pre-release versions on the help page. !22026
+- Use local tiller for Auto DevOps. !22036
+- Remove 'rbac_clusters' feature flag. !22096
+- Increased retained event data by extending events pruner timeframe to 2 years. !22145
+- Add installation type to backup information file. !22150
+- Remove duplicate button from the markdown header toolbar. !22192 (George Tsiolis)
+- Update to Rouge 3.3.0 including frozen string literals for improved memory usage.
+
+### Performance (17 changes, 6 of them are from the community)
+
+- Enable frozen string in app/controllers/**/*.rb.
+- Improve lazy image loading performance by using IntersectionObserver where available. !21565
+- Adds support for Gitaly ListLastCommitsForTree RPC in order to make bulk-fetch of commits more performant. !21921
+- Dont create license_management build when not included in license. !21958
+- Skip creating auto devops jobs for sast, container_scanning, dast, dependency_scanning when not licensed. !21959
+- Reduce queries needed to compute notification recipients. !22050
+- Banzai label ref finder - minimize SQL calls by sharing context more aggresively. !22070
+- Removes expensive dead code on main MR page request. !22153
+- Lazy load xterm custom colors css.
+- Mitigate N+1 queries when parsing commit references in comments.
+- Enable more frozen string in app/controllers/. (gfyoung)
+- Increase performance when creating discussions on diff.
+- Enable frozen string in lib/api and lib/backup. (gfyoung)
+- Enable frozen string in vestigial files. (gfyoung)
+- Enable frozen string for app/helpers/**/*.rb. (gfyoung)
+- Enable frozen string in app/graphql + app/finders. (gfyoung)
+- Enable even more frozen string in app/controllers. (gfyoung)
+
+### Added (37 changes, 21 of them are from the community)
+
+- Allow file templates to be requested at the project level. !7776
+- Add /lock and /unlock quick actions. !15197 (Mehdi Lahmam (@mehlah))
+- Added search functionality for Work In Progress (WIP) merge requests. !18119 (Chantal Rollison)
+- pipeline webhook event now contain pipeline variables. !18171 (Pierre Tardy)
+- Add markdown header toolbar button to insert table. !18480 (George Tsiolis)
+- Add link button to markdown editor toolbar. !18579 (Jan Beckmann)
+- Add access control to GitLab pages and make it possible to enable/disable it in project settings. !18589 (Tuomo Ala-Vannesluoma)
+- Add a filter bar to the admin runners view and add a state filter. !19625 (Alexis Reigel)
+- Add a type filter to the admin runners view. !19649 (Alexis Reigel)
+- Allow user to choose the email used for commits made through GitLab's UI. !21213 (Joshua Campbell)
+- Add autocomplete drop down filter for project snippets. !21458 (Fabian Schneider)
+- Allow events filter to be set in the URL in addition to cookie. !21557 (Igor @igas)
+- Adds a initialize_with_readme parameter to POST /projects. !21617 (Steve)
+- Add ability to skip user email confirmation with API. !21630
+- Add sorting for labels on labels page. !21642
+- Set user status from within user menu. !21643
+- Copy nurtch demo notebooks at Jupyter startup. !21698 (Amit Rathi)
+- Allows to sort projects by most stars. !21762 (Jacopo Beschi @jacopo-beschi)
+- Allow pipelines to schedule delayed job runs. !21767
+- Added tree of changed files to merge request diffs. !21833
+- Add GitLab version components to CI environment variables. !21853
+- Allows to chmod file with commits API. !21866 (Jacopo Beschi @jacopo-beschi)
+- Make single diff patch limit configurable. !21886
+- Extend reports feature to support Security Products. !21892
+- Adds the user's public_email attribute to the API. !21909 (Alexis Reigel)
+- Update all gitlab CI templates from gitlab-org/gitlab-ci-yml. !21929
+- Add support for setting the public email through the api. !21938 (Alexis Reigel)
+- Support db migration and initialization for Auto DevOps. !21955
+- Add subscribe filter to group and project labels pages. !21965
+- Add support for pipeline only/except policy for modified paths. !21981
+- Docs for Project/Groups members API with inherited members. !21984 (Jacopo Beschi @jacopo-beschi)
+- Adds Web IDE commits to usage ping. !22007
+- Add timed incremental rollout to Auto DevOps. !22023
+- Show percentage of language detection on the language bar. !22056 (Johann Hubert Sonntagbauer)
+- Allows to filter issues by Any milestone in the API. !22080 (Jacopo Beschi @jacopo-beschi)
+- Add button to download 2FA codes. (Luke Picciau)
+- Render log artifact files in GitLab.
+
+### Other (42 changes, 16 of them are from the community)
+
+- Send deployment information in job API. !21307
+- Split admin settings into multiple sub pages. !21467
+- Remove Rugged and shell code from Gitlab::Git. !21488
+- Add trigger information in job API. !21495
+- Add empty state illustration information in job API. !21532
+- Add retried jobs to pipeline stage. !21558
+- Rails 5: fix issue move service In rails 5, the attributes method for an enum returns the name instead of the database integer. !21616 (Jasper Maes)
+- Expose project runners in job API. !21618
+- create from template: hide checkbox for initializing repository with readme. !21646
+- Adds new 'Overview' tab on user profile page. !21663
+- Add clean-up phase for ScheduleDiffFilesDeletion migration. !21734
+- Prevents private profile help link from toggling checkbox. !21757
+- Make AutoDevOps work behind proxy. !21775 (Sergej - @kinolaev)
+- Use Vue components and new API to render Artifacts, Trigger Variables and Commit blocks on Job page. !21777
+- Add wrapper rake task to migrate all uploads to OS. !21779
+- Retroactively fill pipeline source for external pipelines. !21814
+- Rename squash before merge vue component. !21851 (George Tsiolis)
+- Fix merge request header margins. !21878
+- Fix committer typo. !21899 (George Tsiolis)
+- Adds an extra width to the responsive tables. !21928
+- Expose has_trace in job API. !21950
+- Rename block scope local variable in table pagination spec. !21969 (George Tsiolis)
+- Fix blue, orange, and red color inconsistencies. !21972
+- Update operations metrics empty state. !21974 (George Tsiolis)
+- Improve empty project placeholder for non-members and members without write access. !21977 (George Tsiolis)
+- Add copy to clipboard button for application id and secret. !21978 (George Tsiolis)
+- Add link component to UserAvatarLink component. !21986 (George Tsiolis)
+- Add link component to DownloadViewer component. !21987 (George Tsiolis)
+- Rephrase 2FA and TOTP documentation and view. !21998 (Marc Schwede)
+- Update project path on project name autofill. !22016
+- Improve logging when username update fails due to registry tags. !22038
+- Align collapsed sidebar avatar container. !22044 (George Tsiolis)
+- Rails5: fix artifacts controller download spec Rails5 has params[:file_type] as '' if file_type is included as nil in the request. !22123 (Jasper Maes)
+- Hide pagination for personal projects on profile overview tab. !22321
+- Extracts scroll position check into reusable functions.
+- Uses Vuex store in job details page and removes old mediator pattern.
+- Render 412 when invalid UTF-8 parameters are passed to controller.
+- Renders Job show page in new Vue app.
+- Add link to User Snippets in breadcrumbs of New User Snippet page. (J.D. Bean)
+- Log project services errors when executing async.
+- Update docs regarding frozen string. (gfyoung)
+- Check frozen string in style builds. (gfyoung)
+
+
+## 11.3.10 (2018-11-18)
+
+### Security (1 change)
+
+- Escape user fullname while rendering autocomplete template to prevent XSS.
+
+
+## 11.3.9 (2018-10-31)
+
+### Security (1 change)
+
+- Monkey kubeclient to not follow any redirects.
+
+
+## 11.3.8 (2018-10-27)
+
+- No changes.
+
+## 11.3.7 (2018-10-26)
+
+### Security (6 changes)
+
+- Escape entity title while autocomplete template rendering to prevent XSS. !2557
+- Persist only SHA digest of PersonalAccessToken#token.
+- Fix XSS in merge request source branch name.
+- Redact personal tokens in unsubscribe links.
+- Prevent SSRF attacks in HipChat integration.
+- Validate Wiki attachments are valid temporary files.
+
+
+## 11.3.6 (2018-10-17)
+
+- No changes.
+
+## 11.3.5 (2018-10-15)
+
+### Fixed (2 changes)
+
+- Fix loading issue on some merge request discussion. !21982
+- Fix project deletion when there is a export available. !22276
+
+
+## 11.3.3 (2018-10-04)
+
+- No changes.
+
+## 11.3.2 (2018-10-03)
+
+### Fixed (4 changes)
+
+- Fix NULL pipeline import problem and pipeline user mapping issue. !21875
+- Fix migration to avoid an exception during upgrade. !22055
+- Fixes admin runners table not wrapping content.
+- Fix Error 500 when forking projects with Gravatar disabled.
+
+### Other (1 change)
+
+- Removes the 'required' attribute from the 'project name' field. !21770
+
+
+## 11.3.1 (2018-09-26)
+
+### Security (6 changes)
+
+- Redact confidential events in the API.
+- Set timeout for syntax highlighting.
+- Sanitize JSON data properly to fix XSS on Issue details page.
+- Fix stored XSS in merge requests from imported repository.
+- Fix xss vulnerability sourced from package.json.
+- Block loopback addresses in UrlBlocker.
+
+
+## 11.3.0 (2018-09-22)
+
+### Security (5 changes, 1 of them is from the community)
+
+- Disable the Sidekiq Admin Rack session. !21441
+- Set issuable_sort, diff_view, and perf_bar_enabled cookies to secure when possible. !21442
+- Update rubyzip to 1.2.2 (CVE-2018-1000544). !21460 (Takuya Noguchi)
+- Fixed persistent XSS rendering/escaping of diff location lines.
+- Block link-local addresses in URLBlocker.
+
+### Removed (1 change)
+
+- Remove Gemnasium service. !21185
+
+### Fixed (83 changes, 24 of them are from the community)
+
+- Hide PAT creation advice for HTTP clone if PAT exists. !18208 (George Thomas @thegeorgeous)
+- Allow spaces in wiki markdown links when using CommonMark. !20417
+- disable_statement_timeout no longer leak to other migrations. !20503
+- Events API now requires the read_user or api scope. !20627 (Warren Parad)
+- Fix If-Check the result that a function was executed several times. !20640 (Max Dicker)
+- Add migration to cleanup internal_ids inconsistency. !20926
+- Fix fallback logic for automatic MR title assignment. !20930 (Franz Liedke)
+- Fixed bug when the project logo file is stored in LFS. !20948
+- Fix buttons on the new file page wrapping outside of the container. !21015
+- Solve tooltip appears under modal. !21017
+- Fix Bitbucket Cloud importer omitting replies. !21076
+- Fix pipeline fixture seeder. !21088
+- Fix blocked user card style. !21095
+- Fix empty merge requests not opening in the Web IDE. !21102
+- Fix label list item container height when there is no label description. !21106
+- Fixes input alignment in user admin form with errors. !21108 (Jacopo Beschi @jacopo-beschi)
+- Rails5 fix specs duplicate key value violates unique constraint 'index_gpg_signatures_on_commit_sha'. !21119 (Jasper Maes)
+- Add gitlab theme to spam logs pagination. !21145
+- Split remembering sorting for issues and merge requests. !21153 (Jacopo Beschi @jacopo-beschi)
+- Fix git submodule link for subgroup projects with relative path. !21154
+- Fix: Project deletion may not log audit events during group deletion. !21162
+- Fix 1px cutoff of emojis. !21180 (gfyoung)
+- Auto-DevOps.gitlab-ci.yml: update glibc package to 2.28. !21191 (sgerrand)
+- Show google icon in audit log. !21207 (Jan Beckmann)
+- Fix bin/secpick error and security branch prefixing. !21210
+- Importing a project no longer fails when visibility level holds a string value type. !21242
+- Fix attachments not displaying inline with Google Cloud Storage. !21265
+- Fix IDE issues with persistent banners. !21283
+- Fix "Confidential comments" button not saving in project hooks. !21289
+- Bump fog-google to 1.7.0 and google-api-client to 0.23.0. !21295
+- Don't use arguments keyword in gettext script. !21296 (gfyoung)
+- Fix breadcrumb link to issues on new issue page. !21305 (J.D. Bean)
+- Show '< 1%' when percent value evaluated is less than 1 on Stacked Progress Bar. !21306
+- API: Catch empty commit messages. !21322 (Robert Schilling)
+- Fix SQL error when sorting 2FA-enabled users by name in admin area. !21324
+- API: Catch empty code content for project snippets. !21325 (Robert Schilling)
+- Avoid nil safe message. !21326 (Yi Siliang)
+- Allow date parameters on Issues, Notes, and Discussions API for group owners. !21342 (Florent Dubois)
+- Fix remote mirrors failing if Git remotes have not been added. !21351
+- Removing a group no longer triggers hooks for project deletion twice. !21366
+- Use slugs for default project path and sanitize names before import. !21367
+- Vertically centres landscape avatars. !21371 (Vicary Archangel)
+- Fix Web IDE unable to commit to same file twice. !21372
+- Fix project transfer name validation issues causing a redirect loop. !21408
+- Fix Error 500s due to encoding issues when Wiki hooks fire. !21414
+- Rails 5: include opclasses in rails 5 schema dump. !21416 (Jasper Maes)
+- Bump GitLab Pages to v1.1.0. !21419
+- Fix links in RSS feed elements. !21424 (Marc Schwede)
+- Allow gaps in multiseries metrics charts. !21427
+- Auto-DevOps.gitlab-ci.yml: fix redeploying deleted app gives helm error. !21429
+- Use sample data for push event when no commits created. !21440 (Takuya Noguchi)
+- Fix importers not assigning a new default group. !21456
+- Fix edge cases of JUnitParser. !21469
+- Fix breadcrumb link to merge requests on new merge request page. !21502 (J.D. Bean)
+- Handle database statement timeouts in usage ping. !21523
+- Handles exception during file upload - replaces the stack trace with a small error message. !21528
+- Fix closing issue default pattern. !21531 (Samuele Kaplun)
+- Fix outdated discussions being shown on Merge Request Changes tab. !21543
+- Remove orphaned label links. !21552
+- Delete a container registry asynchronously. !21553
+- Make MR diff file filter input Clear button functional. !21556
+- Replace white spaces in wiki attachments file names. !21569
+- API: Use find_branch! in all places. !21614 (Robert Schilling)
+- Fixes double +/- on inline diff view. !21634
+- Fix broken exports when they include a projet avatar. !21649
+- Fix workhorse temp path for namespace uploads. !21650
+- Fixed resolved discussions not toggling expanded state on changes tab. !21676
+- Update GitLab Shell to v8.3.2. !21701
+- Fix absent Click to Expand link on diffs not rendered on first load of Merge Requests Changes tab. !21716
+- Update GitLab Shell to v8.3.3. !21750
+- Fix import error when archive does not have the correct extension. !21765
+- Fixed IDE deleting new files creating wrong state.
+- Does not collapse runners section when using pagination.
+- Fix Emojis cutting in the right way. (Alexander Popov)
+- Fix NamespaceUploader.base_dir for remote uploads.
+- Increase width of checkout branch modal box.
+- Fixes SVGs for empty states in job page overflowing on mobile.
+- Fix checkboxes on runner admin settings - The labels are now clickable.
+- Fixed IDE file row scrolling into view when hovering.
+- Accept upload files in public/uplaods/tmp when using accelerated uploads.
+- Include correct CSS file for xterm in environments page.
+- Increase padding in code blocks.
+- Fix: Project deletion may not log audit events during user deletion.
+
+### Changed (32 changes, 5 of them are from the community)
+
+- Add default avatar to group. !17271 (George Tsiolis)
+- Allow project owners to set up forking relation through API. !18104
+- Limit navbar search for current project or group for small viewports. !18634 (George Tsiolis)
+- Add Noto Color Emoji font support. !19036 (Alexander Popov)
+- Update design of project overview page. !20536
+- Improve visuals of language bar on projects. !21006
+- Migrate NULL wiki_access_level to correct number so we count active wikis correctly. !21030
+- Support a custom action, such as proxying to another server, after /api/v4/internal/allowed check succeeds. !21034
+- Remove storage path dependency of gitaly install task. !21101
+- Support Kubernetes RBAC for GitLab Managed Apps when adding a existing cluster. !21127
+- Change 'Backlog' list title to 'Open' in Issue Boards. !21131
+- Enable Auto DevOps Instance Wide Default. !21157
+- Allow author to vote on their own issue and MRs. !21203
+- Truncate branch names and update "commits behind" text in MR page. !21206
+- Adds count for different board list types (label lists, assignee lists, and milestone lists) to usage statistics. !21208
+- Render files (`.md`) and wikis using CommonMark. !21228
+- Show deprecation message on project milestone page for category tabs. !21236
+- Remove redundant header from metrics page. !21282
+- Add default parameter to branches API. !21294 (Riccardo Padovani)
+- Restrict reopening locked issues for non authorized issue authors. !21299
+- Send back required object storage PUT headers in /uploads/authorize API. !21319
+- Display default status emoji if only message is entered. !21330
+- Move badge settings to general settings. !21333
+- Move project settings for default branch under "Repository". !21380
+- Import all common metrics into database. !21459
+- Improved commit panel in Web IDE. !21471
+- Administrative cleanup rake tasks now leverage Gitaly. !21588
+- Remove health check feature flag in BackgroundMigrationWorker.
+- Expose user's id in /admin/users/ show page. (Eva Kadlecova)
+- Improved styling of top bar in IDE job trace pane.
+- Make terminal button more visible.
+- Shows download artifacts button for pipelines on small screens.
+
+### Performance (13 changes, 2 of them are from the community)
+
+- Enable frozen string in rest of app/models/**/*.rb.
+- Add background migrations for legacy artifacts. !18615
+- Optimize querying User#manageable_groups. !21050
+- Incremental rendering with Vue on merge request page. !21063
+- Remove redundant ci_builds (status) index. !21070
+- Enable frozen in app/mailers/**/*.rb. !21147 (gfyoung)
+- Improve performance when fetching related merge requests for an issue. !21237
+- Speed up diff comparisons by limiting number of commit messages rendered. !21335
+- Write diff highlighting cache upon MR creation (refactors caching). !21489
+- Bulk-render commit titles in the tree view to improve performance. !21500
+- Enable frozen string in vestigial app files. (gfyoung)
+- Disable project avatar validation if avatar has not changed.
+- Bitbucket Server importer: Eliminate most idle-in-transaction issues.
+
+### Added (41 changes, 17 of them are from the community)
+
+- API: Protected tags. !14986 (Robert Schilling)
+- Include private contributions to contributions calendar. !17296 (George Tsiolis)
+- Add an option to whitelist users based on email address as internal when the "New user set to external" setting is enabled. !17711 (Roger Rüttimann)
+- Overhaul listing of projects in the group overview page. !20262
+- Add the ability to reference projects in comments and other markdown text. !20285 (Reuben Pereira)
+- Add branch filter to project webhooks. !20338 (Duana Saskia)
+- Allows to cancel a Created job. !20635 (Jacopo Beschi @jacopo-beschi)
+- First Improvements made to the contributor on-boarding experience. !20682 (Eddie Stubbington)
+- `/tag` quick action on Commit comments. !20694 (Peter Leitzen)
+- Allow admins to configure the maximum Git push size. !20758
+- Expose all artifacts sizes in jobs api. !20821 (Peter Marko)
+- Get the merge base of two refs through the API. !20929
+- Add ability to suppress the global "You won't be able to use SSH" message. !21027 (Ævar Arnfjörð Bjarmason)
+- API: Add expiration date for shared projects to the project entity. !21104 (Robert Schilling)
+- Added tooltips to tree list header. !21138
+- #47845 Add failure_reason to job webhook. !21143 (matemaciek)
+- Vendor Auto-DevOps.gitlab-ci.yml with new proxy env vars passed through to docker. !21159 (kinolaev)
+- Disable Auto DevOps for project upon first pipeline failure. !21172
+- Add rake command to migrate archived traces from local storage to object storage. !21193
+- Add Czech as an available language. !21201
+- Add Galician as an available language. !21202
+- Add support for extendable CI/CD config with. !21243
+- Disable Web IDE button if user is not allowed to push the source branch. !21288
+- Feature flag to disable Hashed Storage migration when renaming a repository. !21291
+- Store wiki uploads inside git repository. !21362
+- Adds Rubocop rule to enforce class_methods over module ClassMethods. !21379 (Jacopo Beschi @jacopo-beschi)
+- Merge request copies all associated issue labels and milestone on creation. !21383
+- Add group name badge under group milestone. !21384
+- Adds diverged_commits_count field to GET api/v4/projects/:project_id/merge_requests/:merge_request_iid. !21405 (Jacopo Beschi @jacopo-beschi)
+- Update Import/Export to only use new storage uploaders logic. !21409
+- Ask user explicitly about usage stats agreement on single user deployments. !21423
+- Added atom feed for tags. !21428
+- Add search to a group labels page. !21480
+- Display banner on project page if AutoDevOps is implicitly enabled. !21503
+- Recognize 'UNLICENSE' license files. !21508 (J.D. Bean)
+- Add git_v2 feature flag. !21520
+- Added file templates to the Web IDE.
+- Enabled multiple file uploads in the Web IDE.
+- Allow to delete group milestones.
+- Use separate model for tracking resource label changes and render label system notes based on data from this model.
+- Add system note when due date is changed. (Eva Kadlecova)
+
+### Other (48 changes, 16 of them are from the community)
+
+- Remove extra spaces from MR discussion notes. !18946 (Takuya Noguchi)
+- Add an example of the configuration of archive trace cron worker in gitlab.yml.example. !20583
+- Add target branch name to cherrypick confirmation message. !20846 (George Andrinopoulos)
+- CE Port of Protected Environments backend. !20859
+- Added missing i18n strings to issue boards lables dropdown. !21081
+- Combines emoji award spec files into single user_interacts_with_awards_in_issue_spec.rb file. !21126 (Nate Geslin)
+- Clarify current runners online text. !21151 (Ben Bodenmiller)
+- Rails5: Enable verbose query logs. !21231 (Jasper Maes)
+- Update presentation for SSO providers on log in page. !21233
+- Make margin of user status emoji consistent. !21268
+- Move usage ping payload from User Cohorts page to admin application settings. !21343
+- Add JSON logging for Bitbucket Server importer. !21378
+- Re-add project name field on "Create new project" page. !21386
+- Rails 5: replace removed silence_stream. !21387 (Jasper Maes)
+- Rails5 update Gemfile.rails5.lock. !21388 (Jasper Maes)
+- Rails5: fix can't quote ActiveSupport::HashWithIndifferentAccess. !21397 (Jasper Maes)
+- Don't show flash messages for performance bar errors. !21411
+- Backport schema_changed.sh from EE which prints the diff if the schema is different. !21422 (Jasper Maes)
+- Remove unused CSS part in mobile framework. !21439 (Takuya Noguchi)
+- Bump unauthenticated session time from 1 hour to 2 hours. !21453
+- Run review-docs-cleanup job for gitlab-org repos only. !21463 (Takuya Noguchi)
+- Rails 5: support schema t.index for mysql. !21485 (Jasper Maes)
+- Add route information to lograge structured logging for API logs. !21487
+- Add gitaly_calls attribute to API logs. !21496
+- Ignore irrelevant sql commands in metrics. !21498
+- Rails 5: fix hashed_path? method that looks up file_location that doesn't exist when running certain migration specs. !21510 (Jasper Maes)
+- Explicit hashed path check for trace, prevents background migration from accessing file_location column that doesn't exist. !21533 (Jasper Maes)
+- Add terminal_path to job API response. !21537
+- Add User-Agent to production_json.log. !21546
+- Make cluster page settings easier to read. !21550
+- Remove striped table styling of Find files and Admin Area Applications views. !21560 (Andreas Kämmerle)
+- Update ffi to 1.9.25. !21561 (Takuya Noguchi)
+- Send max_patch_bytes to Gitaly via Gitaly::CommitDiffRequest. !21575
+- Add margin between username and subsequent text in issuable header. !21697
+- Send artifact information in job API. !50460
+- Reduce differences between CE and EE code base in reports components.
+- Move project services log to a separate file.
+- Creates vue component for job log top bar with controllers.
+- Creates Vue component for trigger variables block in job log page.
+- Creates Vvue component for warning block about stuck runners.
+- Creates vue component for job log trace.
+- Creates vue component for erased block on job view.
+- Creates vue component for environments information in job log view.
+- Upgrade Monaco editor.
+- Creates empty state vue component for job view.
+- Creates vue component for commit block in job log page.
+- Creates vue components for stage dropdowns and job list container for job log view.
+- Creates Vue component for artifacts block on job page.
+
+
+## 11.2.8 (2018-10-31)
+
+### Security (1 change)
+
+- Monkey kubeclient to not follow any redirects.
+
+
+## 11.2.7 (2018-10-27)
+
+- No changes.
+
+## 11.2.6 (2018-10-26)
+
+### Security (5 changes)
+
+- Escape entity title while autocomplete template rendering to prevent XSS. !2558
+- Fix XSS in merge request source branch name.
+- Redact personal tokens in unsubscribe links.
+- Persist only SHA digest of PersonalAccessToken#token.
+- Prevent SSRF attacks in HipChat integration.
+
+
+## 11.2.5 (2018-10-05)
+
+### Security (3 changes)
+
+- Filter user sensitive data from discussions JSON. !2538
+- Properly filter private references from system notes.
+- Markdown API no longer displays confidential title references unless authorized.
+
+
+## 11.2.4 (2018-09-26)
+
+### Security (6 changes)
+
+- Redact confidential events in the API.
+- Set timeout for syntax highlighting.
+- Sanitize JSON data properly to fix XSS on Issue details page.
+- Fix stored XSS in merge requests from imported repository.
+- Fix xss vulnerability sourced from package.json.
+- Block loopback addresses in UrlBlocker.
+
+
+## 11.2.3 (2018-08-28)
+
+### Fixed (1 change)
+
+- Fixed cache invalidation issue with diff lines from 11.2.2.
+
+## 11.2.2 (2018-08-27)
+
+### Security (3 changes)
+
+- Fixed persistent XSS rendering/escaping of diff location lines.
+- Adding CSRF protection to Hooks resend action.
+- Block link-local addresses in URLBlocker.
+
+
+## 11.2.1 (2018-08-22)
+
+### Fixed (2 changes)
+
+- Fix wrong commit count in push event payload. !21338
+- Fix broken Git over HTTP clones with LDAP users. !21352
+
+### Performance (1 change)
+
+- Eliminate unnecessary and duplicate system hook fires. !21337
+
+
+## 11.2.0 (2018-08-22)
+
+### Security (5 changes)
+
+- Bump Gitaly to 0.117.1 for Rouge update. !21277
+- Fix symlink vulnerability in project import.
+- Bump rugged to 0.27.4 for security fixes.
+- Fixed XSS in branch name in Web IDE.
+- Adding CSRF protection to Hooks test action.
+
+### Removed (1 change)
+
+- Remove gitlab:user:check_repos, gitlab:check_repo, gitlab:git:prune, gitlab:git:gc, and gitlab:git:repack. !20806
+
+### Fixed (81 changes, 26 of them are from the community)
+
+- Fix namespace move callback behavior, especially to fix Geo replication of namespace moves during certain exceptions. !19297
+- Fix breadcrumbs in Admin/User interface. !19608 (Robin Naundorf)
+- Remove changes_count from MR API documentation where necessary. !19745 (Jan Beckmann)
+- Fix email confirmation bug when user adds additional email to account. !20084 (muhammadn)
+- Add support for daylight savings time to pipleline schedules. !20145
+- Fixing milestone date change when editing. !20279 (Orlando Del Aguila)
+- Add missing maximum_timeout parameter. !20355 (gfyoung)
+- [Rails5] Fix 'Invalid single-table inheritance type: Group is not a subclass of Gitlab::BackgroundMigration::FixCrossProjectLabelLinks::Namespace'. !20462 (@blackst0ne)
+- Rails5 fix mysql milliseconds problem in specs. !20464 (Jasper Maes)
+- Update Gemfile.rails5.lock with latest Gemfile.lock changes. !20466 (Jasper Maes)
+- Rails5 mysql fix milliseconds problem in pull request importer spec. !20475 (Jasper Maes)
+- Rails5 MySQL fix rename_column as part of cleanup_concurrent_column_type_change. !20514 (Jasper Maes)
+- Process commits as normal in forks when the upstream project is deleted. !20534
+- Fix project visibility tooltip. !20535 (Jamie Schembri)
+- Fix archived parameter for projects API. !20566 (Peter Marko)
+- Limit maximum project build timeout setting to 1 month. !20591
+- Fix GitLab project imports not loading due to API timeouts. !20599
+- Avoid process deadlock in popen by consuming input pipes. !20600
+- Disable SAML and Bitbucket if OmniAuth is disabled. !20608
+- Support multiple scopes when authing container registry scopes. !20617
+- Adds the ability to view group milestones on the dashboard milestone page. !20618
+- Allow issues API to receive an internal ID (iid) on create. !20626 (Jamie Schembri)
+- Fix typo in CSS transform property for Memory Graph component. !20650
+- Update design for system metrics popovers. !20655
+- Toggle Show / Hide Button for Kubernetes Password. !20659 (gfyoung)
+- Board label edit dropdown shows incorrect selected labels summary. !20673
+- Resolve "Unable to save user profile update with Safari". !20676
+- Escape username and password in UrlSanitizer#full_url. !20684
+- Remove background color from card-body style. !20689 (George Tsiolis)
+- Update total storage size when changing size of artifacts. !20697 (Peter Marko)
+- Rails5 fix user sees revert modal spec. !20706 (Jasper Maes)
+- Fix Web IDE crashing on directories named 'blob'. !20712
+- Fix accessing imported pipeline builds. !20713
+- Fixed bug with invalid repository reference using the wiki search. !20722
+- Resolve Copy diff file path as GFM is broken. !20725
+- Chart versions for applications installed by one click install buttons should be version locked. !20765
+- Fix misalignment of broadcast message on login page. !20794 (Robin Naundorf)
+- Fix Vue datatype errors for markdownVersion parsing. !20800
+- Fix authorization for interactive web terminals. !20811
+- Increase width of Web IDE sidebar resize handles. !20818
+- Fix new MR card styles. !20822
+- Fix link color in markdown code brackets. !20841
+- Rails5 update Gemfile.rails5.lock. !20858 (Jasper Maes)
+- fix height of full-width Metrics charts on large screens. !20866
+- Fix sorting by name on milestones page. !20881
+- Permit concurrent loads in gpg keychain mutex. !20894 (Jasper Maes)
+- Prevent editing and updating wiki pages with non UTF-8 encoding via web interface. !20906
+- Retrieve merge request closing issues from database cache. !20911
+- Fix LFS uploads not working with git-lfs 2.5.0. !20923
+- Fix bug setting http headers in Files API. !20938
+- Rails5: fix flaky spec. !20953 (Jasper Maes)
+- Fixed list of projects not loading in group boards. !20955
+- Fix autosave and ESC confirmation issues for MR discussions. !20968
+- Fix navigation to First and Next discussion on MR Changes tab. !20968
+- Fix rendering of the context lines in MR diffs page. !20968
+- fix error caused when using the search bar while unauthenticated. !20970
+- Fix GPG status badge loading regressions. !20987
+- Ensure links in notifications footer are not escaped. !21000
+- Rails5: update Rails5 lock for forgotten gem rouge. !21010 (Jasper Maes)
+- Fix UI error whereby prometheus application status is updated. !21029
+- Solves group dashboard line height is too tall for group names. !21033
+- Fix rendering of pipeline failure view when directly navigationg to it. !21043
+- Fix missing and duplicates on project milestone listing page. !21058
+- Fix merge requests not showing any diff files for big patches. !21125
+- Auto-DevOps.gitlab-ci.yml: Update glibc package signing key URL. !21182 (sgerrand)
+- Fix issue stopping Instance Statistics javascript to be executed. !21211
+- Fix broken JavaScript in IE11. !21214
+- Improve JUnit test reports in merge request widgets. !49966
+- Properly handle colons in URL passwords.
+- Renders test reports for resolved failures and resets error state.
+- Fix handling of annotated tags when Gitaly is not in use.
+- Fix serialization of LegacyDiffNote.
+- Escapes milestone and label's names on flash notice when promoting them.
+- Allow to toggle notifications for issues due soon.
+- Sanitize git URL in import errors. (Jamie Schembri)
+- Add missing predefined variable and fix docs.
+- Allow updating a project's avatar without other params. (Jamie Schembri)
+- Fix the UI for listing system-level labels.
+- Update hamlit to fix ruby 2.5 incompatibilities, fixes #42045. (Matthew Dawson)
+- Fix updated_at if created_at is set for Note API.
+- Fix search bar text input alignment.
+
+### Changed (32 changes, 7 of them are from the community)
+
+- Rack attack is now disabled by default. !16669
+- Include full image URL in webhooks for uploaded images. !18109 (Satish Perala)
+- Enable hashed storage for all newly created or renamed projects. !19747
+- Support manually stopping any environment from the UI. !20077
+- Close revert and cherry pick modal on escape keypress. !20341 (George Tsiolis)
+- Adds with_projects optional parameter to GET /groups/:id API endpoint. !20494
+- Improve feedback when a developer is unable to push to an empty repository. !20519
+- Display GPG status on repository and blob pages. !20524
+- Updated design of new entry dropdown in Web IDE. !20526
+- UX improvements to top nav search bar. !20537
+- Update issue closing pattern. !20554 (George Tsiolis)
+- Add merge request header branch actions left margin. !20643 (George Tsiolis)
+- Rubix, scikit-learn, tensorflow & other useful libraries pre-installed with JupyterHub. !20714 (Amit Rathi)
+- Show decimal place up to single digit in Stacked Progress Bar. !20776
+- Wrap job name on pipeline job sidebar. !20804 (George Tsiolis)
+- Redesign Web IDE back button and context header. !20850
+- Removes "show all" on reports and adds an actionButtons slot. !20855
+- Put fallback reply-key address first in the References header. !20871
+- Allow non-admins to view instance statistics (if permitted by the instance admins). !20874
+- Adds the project and group name to the return type for project and group milestones. !20890
+- Restyle status message input on profile settings. !20903
+- Ensure installed Helm Tiller For GitLab Managed Apps Is protected by mutual auth. !20928
+- Allow multiple JIRA transition ids. !20939
+- Use Helm 2.7.2 for GitLab Managed Apps. !20956
+- Create branch and MR picker for Web IDE. !20978
+- Update commit message styles with monospace font and overflow-x. !20988
+- Update to Rouge 3.2.0, including Terraform and Crystal lexer and bug fixes. !20991
+- Update design of project templates. !21012
+- Update to Rouge 3.2.1, which includes a critical fix to the Perl Lexer. !21263
+- Add a 10 ms bucket for SQL timings.
+- Show one digit after dot in commit_per_day value in charts page. (msdundar)
+- Redesign GCP offer banner.
+
+### Performance (30 changes, 10 of them are from the community)
+
+- Stop dynamically creating project and namespace routes. !20313
+- Tracking the number of repositories and wikis with a cached counter for site-wide statistics. !20413
+- Optimize ProjectWiki#empty? check. !20573
+- Delete UserActivities and related workers. !20597
+- Enable frozen string in app/services/**/*.rb. !20656 (gfyoung)
+- Enable more frozen string in app/services/**/*.rb. !20677 (gfyoung)
+- Limit the TTL for anonymous sessions to 1 hour. !20700
+- Enable even more frozen string in app/services/**/*.rb. !20702 (gfyoung)
+- Enable frozen string in app/serializers/**/*.rb. !20726 (gfyoung)
+- Enable frozen string in newly added files to previously processed directories. !20763 (gfyoung)
+- Use limit parameter to retrieve Wikis from Gitaly. !20764
+- Add Dangerfile for frozen_string_literal. !20767 (gfyoung)
+- Remove method instrumentation for Banzai filters and reference parsers. !20770
+- Enable frozen strings in lib/banzai/filter/*.rb. !20775
+- Enable frozen strings in remaining lib/banzai/filter/*.rb files. !20777
+- DNS prefetching if asset_host for CDN hosting is set. !20781
+- Bump nokogiri to 1.8.4 and sanitize to 4.6.6 for performance. !20795
+- Enable frozen string in app/presenters and app/policies. !20819 (gfyoung)
+- Bump haml gem to 5.0.4. !20847
+- Enable frozen string in app/models/*.rb. !20851 (gfyoung)
+- Performing Commit GPG signature calculation in bulk. !20870
+- Fix /admin/jobs failing to load due to statement timeout. !20909
+- refactor pipeline job log animation to reduce CPU usage. !20915
+- Improve performance when fetching collapsed diffs and commenting in merge requests. !20940
+- Enable frozen string for app/models/**/*.rb. !21001 (gfyoung)
+- Don't set gon variables in JSON requests. !21016 (Peter Leitzen)
+- Improve performance and memory footprint of Changes tab of Merge Requests. !21028
+- Avoid N+1 on MRs page when metrics merging date cannot be found. !21053
+- Bump Gitaly to 0.117.0. !21055
+- Access metadata directly from Object Storage.
+
+### Added (41 changes, 18 of them are from the community)
+
+- Show repository languages for projects. !19480
+- Adds API endpoint /api/v4/(project/group)/:id/members/all to list also inherited members. !19748 (Jacopo Beschi @jacopo-beschi)
+- Added live preview for JavaScript projects in the Web IDE. !19764
+- Add support for SSH certificate authentication. !19911 (Ævar Arnfjörð Bjarmason)
+- Add Hangouts Chat integration. !20290 (Kukovskii Vladimir)
+- Add ability to import multiple repositories by uploading a manifest file. !20304
+- Show Project ID on project home panel. !20305 (Tuğçe Nur Taş)
+- Add an option to have a private profile on GitLab. !20387 (jxterry)
+- Extend gitlab-ci.yml to request junit.xml test reports. !20390
+- Add the first mutations for merge requests to GraphQL. !20443
+- Add /-/health basic health check endpoint. !20456
+- Add filter for minimal access level in groups and projects API. !20478 (Marko, Peter)
+- Add download button for single file (including raw files) in repository. !20480 (Kia Mei Somabes)
+- Gitaly Servers link into Admin > Overview navigation menu. !20550
+- Adds foreign key to notification_settings.user_id. !20567 (Jacopo Beschi @jacopo-beschi)
+- JUnit XML Test Summary In MR widget. !20576
+- Cleans up display of Deploy Tokens to match Personal Access Tokens. !20578 (Marcel Amirault)
+- Users can set a status message and emoji. !20614 (niedermyer & davamr)
+- Add emails delivery Prometheus metrics. !20638
+- Verify runner feature set. !20664
+- Add more comprehensive metrics tracking authentication activity. !20668
+- Add support for tar.gz AUTO_DEVOPS_CHART charts (#49324). !20691 (@kondi1)
+- Adds Vuex store for reports section in MR widget. !20709
+- Redirect commits to root if no ref is provided (31576). !20738 (Kia Mei Somabes)
+- Search for labels by title or description on project labels page. !20749
+- Add object storage logic to project import. !20773
+- Enable renaming files and folders in Web IDE. !20835
+- Warn user when reload IDE with staged changes. !20857
+- Add local project uploads cleanup task. !20863
+- Improve error message when adding invalid user to a project. !20885 (Jacopo Beschi @jacopo-beschi)
+- Add link to homepage on static http status pages (404, 500, etc). !20898 (Jason Funk)
+- Clean orphaned files in object storage. !20918
+- Adds frontend support to render test reports on the MR widget. !20936
+- Trigger system hooks when project is archived/unarchived. !20995
+- Custom Wiki Sidebar Support Issue 14995. (Josh Sooter)
+- Emails on push recipients now accepts formats like John Doe <johndoe@example.com>. (George Thomas)
+- Add new model for tracking label events.
+- Improve danger confirmation modals by focusing input field. (Jamie Schembri)
+- Clicking CI icon in Web IDE now opens up pipelines panel.
+- Enabled deletion of files in the Web IDE.
+- Added button to regenerate 2FA codes. (Luke Picciau)
+
+### Other (26 changes, 7 of them are from the community)
+
+- Update specific runners help URL. !20213 (George Tsiolis)
+- Enable frozen string in apps/uploaders/*.rb. !20401 (gfyoung)
+- Update docs of Helm Tiller. !20515 (Takuya Noguchi)
+- Persist 'Auto DevOps' banner dismissal globally. !20540
+- Move xterm to a node dependency and remove it from vendor's folder. !20588
+- Upgrade grape-path-helpers to 1.0.6. !20601
+- Delete todos when user loses access to read the target. !20665
+- Remove tooltips from commit author avatar and name in commit lists. !20674
+- Allow cloning LFS repositories through DeployTokens. !20729
+- Replace 'Sidekiq::Testing.inline!' with 'perform_enqueued_jobs'. !20768 (@blackst0ne)
+- Replace author_link snake case in stylesheets, specs, and helpers. !20797 (George Tsiolis)
+- Replace snake case in SCSS variables. !20799 (George Tsiolis)
+- Add rbtrace to Gemfile. !20831
+- Add support for searching users by confirmed e-mails. !20893
+- Changes poll.js to keep polling on any 2xx http status code. !20904
+- Remove todos of users without access to targets migration. !20927
+- Improve and simplify Auto DevOps settings flow. !20946
+- Keep admin settings sections open after submitting forms. !21040
+- CE port of "List groups with developer maintainer access on project creation". !21051
+- Update git rerere link in docs. !21060 (gfyoung)
+- Add 'tabindex' attribute support on Icon component to show BS4 popover on trigger type 'focus'. !21066
+- Add a Gitlab::Profiler.print_by_total_time convenience method for profiling from a Rails console.
+- Automatically expand runner's settings block when linking to the runner's settings page.
+- Increases title column on modal for reports.
+- Disables toggle comments button if diff has no discussions.
+- Moves help_popover component to a common location.
+
+
+## 11.1.8 (2018-10-05)
+
+### Security (3 changes)
+
+- Filter user sensitive data from discussions JSON. !2539
+- Properly filter private references from system notes.
+- Markdown API no longer displays confidential title references unless authorized.
+
+
+## 11.1.7 (2018-09-26)
+
+### Security (6 changes)
+
+- Redact confidential events in the API.
+- Set timeout for syntax highlighting.
+- Sanitize JSON data properly to fix XSS on Issue details page.
+- Fix stored XSS in merge requests from imported repository.
+- Fix xss vulnerability sourced from package.json.
+- Block loopback addresses in UrlBlocker.
+
+
+## 11.1.6 (2018-08-28)
+
+### Fixed (1 change)
+
+- Fixed cache invalidation issue with diff lines from 11.2.2.
+
+## 11.1.5 (2018-08-27)
+
+### Security (3 changes)
+
+- Fixed persistent XSS rendering/escaping of diff location lines.
+- Adding CSRF protection to Hooks resend action.
+- Block link-local addresses in URLBlocker.
+
+### Fixed (1 change, 1 of them is from the community)
+
+- Sanitize git URL in import errors. (Jamie Schembri)
+
+
## 11.1.4 (2018-07-30)
### Fixed (4 changes, 1 of them is from the community)
@@ -284,6 +1229,19 @@ entry.
- Use monospaced font for MR diff commit link ref on GFM.
+## 11.0.6 (2018-08-27)
+
+### Security (3 changes)
+
+- Fixed persistent XSS rendering/escaping of diff location lines.
+- Adding CSRF protection to Hooks resend action.
+- Block link-local addresses in URLBlocker.
+
+### Fixed (1 change, 1 of them is from the community)
+
+- Sanitize git URL in import errors. (Jamie Schembri)
+
+
## 11.0.5 (2018-07-26)
### Security (4 changes)
@@ -580,7 +1538,7 @@ entry.
- Use the default strings of timeago.js for timeago. !19350 (Takuya Noguchi)
- Update selenium-webdriver to 3.12.0. !19351 (Takuya Noguchi)
- Include username in output when testing SSH to GitLab. !19358
-- Update screenshot in Gitlab.com integration documentation. !19433 (Tuğçe Nur Taş)
+- Update screenshot in GitLab.com integration documentation. !19433 (Tuğçe Nur Taş)
- Users can accept terms during registration. !19583
- Fix issue count on sidebar.
- Add merge requests list endpoint for groups.
@@ -710,7 +1668,7 @@ entry.
- Make toggle markdown preview shortcut only toggle selected field.
- Verifiy if pipeline has commit idetails and render information in MR widget when branch is deleted.
- Fixed inconsistent protected branch pill baseline.
-- Fix setting Gitlab metrics content types.
+- Fix setting GitLab metrics content types.
- Display only generic message on merge error to avoid exposing any potentially sensitive or user unfriendly backend messages.
- Fix label links update on project transfer.
- Breaks commit not found message in pipelines table.
@@ -1080,7 +2038,7 @@ entry.
- Add 'Assigned Issues' and 'Assigned Merge Requests' as dashboard view choices for users. !17860 (Elias Werberich)
- Extend API for importing a project export with overwrite support. !17883
- Create Deploy Tokens to allow permanent access to repository and registry. !17894
-- Detect commit message trailers and link users properly to their accounts on Gitlab. !17919 (cousine)
+- Detect commit message trailers and link users properly to their accounts on GitLab. !17919 (cousine)
- Adds cancel btn to new pages domain page. !18026 (Jacopo Beschi @jacopo-beschi)
- API: Add parameter merge_method to projects. !18031 (Jan Beckmann)
- Introduce simpler env vars for auto devops REPLICAS and CANARY_REPLICAS #41436. !18036
@@ -2528,7 +3486,7 @@ entry.
- [FIXED] Fix broken wiki pages that link to a wiki file. !15019
- [FIXED] Don't rename paths that were freed up when upgrading. !15029
- [FIXED] Fix bitbucket login. !15051
-- [FIXED] Update gitaly in Gitlab 10.1 to 0.43.1 for temp file cleanup. !15055
+- [FIXED] Update gitaly in GitLab 10.1 to 0.43.1 for temp file cleanup. !15055
- [FIXED] Use the correct visibility attribute for projects in system hooks. !15065
- [FIXED] Normalize LDAP DN when looking up identity.
- [FIXED] Adds callback functions for initial request in clusters page.
@@ -4238,7 +5196,7 @@ entry.
- Make user mentions case-insensitive. !10285 (blackst0ne)
- Update rugged to 0.25.1.1. !10286 (Elan Ruusamäe)
- Handle parsing OpenBSD ps output properly to display sidekiq infos on admin->monitoring->background. !10303 (Sebastian Reitenbach)
-- Log errors during generating of Gitlab Pages to debug log. !10335 (Danilo Bargen)
+- Log errors during generating of GitLab Pages to debug log. !10335 (Danilo Bargen)
- Update issue board cards design. !10353
- Tags can be protected, restricting creation of matching tags by user role. !10356
- Set GIT_TERMINAL_PROMPT env variable in initializer. !10372
@@ -4651,7 +5609,7 @@ entry.
- Restore keyboard shortcuts for "Activity" and "Charts". !9680
- Added commit array to Syshook json. !9685 (Gabriele Pongelli)
- Document ability to list issues with no labels using API. !9697 (Vignesh Ravichandran)
-- Fix typo in Gitlab config file. !9702 (medied)
+- Fix typo in GitLab config file. !9702 (medied)
- Fix json response in branches controller. !9710 (George Andrinopoulos)
- Refactor dropdown_assignee_spec. !9711 (George Andrinopoulos)
- Delete artifacts for pages unless expiry date is specified. !9716
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e68e3b9cab0..2dc8ac40dd4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -64,182 +64,56 @@ As of July 2018, all the documentation for contributing to the GitLab project ha
## Contribute to GitLab
-For a first-time step-by-step guide to the contribution process, see
-["Contributing to GitLab"](https://about.gitlab.com/contributing/).
-
-Thank you for your interest in contributing to GitLab. This guide details how
-to contribute to GitLab in a way that is efficient for everyone.
-
-Looking for something to work on? Look for issues with the label [Accepting Merge Requests](#i-want-to-contribute).
-
-GitLab comes into two flavors, GitLab Community Edition (CE) our free and open
-source edition, and GitLab Enterprise Edition (EE) which is our commercial
-edition. Throughout this guide you will see references to CE and EE for
-abbreviation.
-
-If you have read this guide and want to know how the GitLab [core team]
-operates please see [the GitLab contributing process](PROCESS.md).
-
-- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
+This [documentation](doc/development/contributing/index.md#contribute-to-gitlab) has been moved.
## Security vulnerability disclosure
-Please report suspected security vulnerabilities in private to
-`support@gitlab.com`, also see the
-[disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/).
-Please do **NOT** create publicly viewable issues for suspected security
-vulnerabilities.
-
-## Code of conduct
-
-As contributors and maintainers of this project, we pledge to respect all
-people who contribute through reporting issues, posting feature requests,
-updating documentation, submitting pull requests or patches, and other
-activities.
-
-We are committed to making participation in this project a harassment-free
-experience for everyone, regardless of level of experience, gender, gender
-identity and expression, sexual orientation, disability, personal appearance,
-body size, race, ethnicity, age, or religion.
-
-Examples of unacceptable behavior by participants include the use of sexual
-language or imagery, derogatory comments or personal attacks, trolling, public
-or private harassment, insults, or other unprofessional conduct.
+This [documentation](doc/development/contributing/index.md#security-vulnerability-disclosure) has been moved.
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct. Project maintainers who do not
-follow the Code of Conduct may be removed from the project team.
+## Code of Conduct
-This code of conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community.
-
-Instances of abusive, harassing, or otherwise unacceptable behavior can be
-reported by emailing `contact@gitlab.com`.
-
-This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
-available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
+This [documentation](https://about.gitlab.com/contributing/code-of-conduct/) has been moved.
## Closing policy for issues and merge requests
-GitLab is a popular open source project and the capacity to deal with issues
-and merge requests is limited. Out of respect for our volunteers, issues and
-merge requests not in line with the guidelines listed in this document may be
-closed without notice.
-
-Please treat our volunteers with courtesy and respect, it will go a long way
-towards getting your issue resolved.
-
-Issues and merge requests should be in English and contain appropriate language
-for audiences of all ages.
-
-If a contributor is no longer actively working on a submitted merge request
-we can decide that the merge request will be finished by one of our
-[Merge request coaches][team] or close the merge request. We make this decision
-based on how important the change is for our product vision. If a Merge request
-coach is going to finish the merge request we assign the
-~"coach will finish" label.
+This [documentation](doc/development/contributing/index.md#closing-policy-for-issues-and-merge-requests) has been moved.
## Helping others
-Please help other GitLab users when you can.
-The methods people will use to seek help can be found on the [getting help page][getting-help].
-
-Sign up for the mailing list, answer GitLab questions on StackOverflow or
-respond in the IRC channel. You can also sign up on [CodeTriage][codetriage] to help with
-the remaining issues on the GitHub issue tracker.
+This [documentation](doc/development/contributing/index.md#helping-others) has been moved.
## I want to contribute!
-If you want to contribute to GitLab [issues with the label `Accepting Merge Requests` and small weight][accepting-mrs-weight]
-is a great place to start. Issues with a lower weight (1 or 2) are deemed
-suitable for beginners. These issues will be of reasonable size and challenge,
-for anyone to start contributing to GitLab. If you have any questions or need help visit [Getting Help](https://about.gitlab.com/getting-help/#discussion) to
-learn how to communicate with GitLab. If you're looking for a Gitter or Slack channel
-please consider we favor
-[asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real time communication. Thanks for your contribution!
+This [documentation](doc/development/contributing/index.md#i-want-to-contribute) has been moved.
## Contribution Flow
-When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty.
-
-When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged.
-
-When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance.
-
-Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future.
-
-GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches.
-
-GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach.
-
-When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it.
-
-When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process.
-
-[core team]: https://about.gitlab.com/core-team/
-[team]: https://about.gitlab.com/team/
-[getting-help]: https://about.gitlab.com/getting-help/
-[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
-[accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc
-[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
-[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
-[google-group]: https://groups.google.com/forum/#!forum/gitlabhq
-[stackoverflow]: https://stackoverflow.com/questions/tagged/gitlab
-[fpl]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=feature+proposal
-[accepting-mrs-ce]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests
-[accepting-mrs-ee]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Accepting+Merge+Requests
-[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests
-[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
-[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
-[closed-merge-requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed
-[definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
-[contributor-covenant]: http://contributor-covenant.org
-[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
-[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
-[changelog]: doc/development/changelog.md "Generate a changelog entry"
-[doc-guidelines]: doc/development/documentation/index.md "Documentation guidelines"
-[js-styleguide]: doc/development/fe_guide/style_guide_js.md "JavaScript styleguide"
-[scss-styleguide]: doc/development/fe_guide/style_guide_scss.md "SCSS styleguide"
-[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
-[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
-[license-finder-doc]: doc/development/licensing.md
-[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
-[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
-[testing]: doc/development/testing_guide/index.md
-[us-english]: https://en.wikipedia.org/wiki/American_English
-
+This [documentation](doc/development/contributing/index.md) has been moved.
## Workflow labels
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Type labels
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Subject labels
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Team labels
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Release Scoping labels
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Priority labels
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Severity labels
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
@@ -248,17 +122,14 @@ This [documentation](doc/development/contributing/issue_workflow.md) has been mo
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Label for community contributors
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
## Implement design & UI elements
This [documentation](doc/development/contributing/design.md) has been moved.
-
## Issue tracker
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
@@ -267,7 +138,6 @@ This [documentation](doc/development/contributing/issue_workflow.md) has been mo
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Feature proposals
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
@@ -276,32 +146,26 @@ This [documentation](doc/development/contributing/issue_workflow.md) has been mo
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Issue weight
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Regression issues
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Technical and UX debt
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
### Stewardship
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-
## Merge requests
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
-
### Merge request guidelines
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
@@ -311,11 +175,9 @@ This [documentation](doc/development/contributing/merge_request_workflow.md) has
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
-
## Definition of done
-This [documentation](doc/development/contributing/merge_request_workflow.md)) has been moved.
-
+This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
## Style guides
diff --git a/Dangerfile b/Dangerfile
index 9217610da8b..469e77b2514 100644
--- a/Dangerfile
+++ b/Dangerfile
@@ -1,7 +1,12 @@
+danger.import_plugin('danger/plugins/helper.rb')
danger.import_dangerfile(path: 'danger/metadata')
danger.import_dangerfile(path: 'danger/changes_size')
danger.import_dangerfile(path: 'danger/changelog')
danger.import_dangerfile(path: 'danger/specs')
danger.import_dangerfile(path: 'danger/gemfile')
danger.import_dangerfile(path: 'danger/database')
+danger.import_dangerfile(path: 'danger/documentation')
danger.import_dangerfile(path: 'danger/frozen_string')
+danger.import_dangerfile(path: 'danger/commit_messages')
+danger.import_dangerfile(path: 'danger/prettier')
+danger.import_dangerfile(path: 'danger/eslint')
diff --git a/Dockerfile.assets b/Dockerfile.assets
new file mode 100644
index 00000000000..403d16cc4ab
--- /dev/null
+++ b/Dockerfile.assets
@@ -0,0 +1,4 @@
+# Simple container to store assets for later use
+FROM scratch
+ADD public/assets /assets/
+CMD /bin/true
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index a38b3bd31b1..244fb7efb4c 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.117.0
+0.133.0
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 3eefcb9dd5b..f0bb29e7638 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.0.0
+1.3.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 0e79152459e..6da4de57dc6 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-8.1.1
+8.4.1
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 831446cbd27..21c8c7b46b8 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-5.1.0
+7.1.1
diff --git a/Gemfile b/Gemfile
index d9066081f74..2c0eb0cbce4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,13 +1,12 @@
# --- Special code for migrating to Rails 5.0 ---
def rails5?
- %w[1 true].include?(ENV["RAILS5"])
+ !%w[0 false].include?(ENV["RAILS5"])
end
gem_versions = {}
-gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2'
-gem_versions['default_value_for'] = rails5? ? '~> 3.0.5' : '~> 3.0.0'
-gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10'
-gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
+gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2'
+gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10'
+gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
# --- The end of special code for migrating to Rails 5.0 ---
source 'https://rubygems.org'
@@ -15,13 +14,20 @@ source 'https://rubygems.org'
gem 'rails', gem_versions['rails']
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
+# Improves copy-on-write performance for MRI
+gem 'nakayoshi_fork', '~> 0.0.4'
+
# Responders respond_to and respond_with
gem 'responders', '~> 2.0'
gem 'sprockets', '~> 3.7.0'
# Default values for AR models
-gem 'default_value_for', gem_versions['default_value_for']
+if rails5?
+ gem 'gitlab-default_value_for', '~> 3.1.1', require: 'default_value_for'
+else
+ gem 'default_value_for', '~> 3.0.0'
+end
# Supported DBs
gem 'mysql2', '~> 0.4.10', group: :mysql
@@ -68,7 +74,7 @@ gem 'u2f', '~> 0.2.1'
gem 'validates_hostname', '~> 1.0.6'
# Browser detection
-gem 'browser', '~> 2.2'
+gem 'browser', '~> 2.5'
# GPG
gem 'gpgme'
@@ -79,17 +85,8 @@ gem 'gpgme'
gem 'gitlab_omniauth-ldap', '~> 2.0.4', require: 'omniauth-ldap'
gem 'net-ldap'
-# Git Wiki
-# Required manually in config/initializers/gollum.rb to control load order
-gem 'gitlab-gollum-lib', '~> 4.2', require: false
-
-gem 'gitlab-gollum-rugged_adapter', '~> 0.4.4', require: false
-
-# Language detection
-gem 'github-linguist', '~> 5.3.3', require: 'linguist'
-
# API
-gem 'grape', '~> 1.0'
+gem 'grape', '~> 1.1'
gem 'grape-entity', '~> 0.7.1'
gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'
@@ -107,23 +104,22 @@ gem 'kaminari', '~> 1.0'
gem 'hamlit', '~> 2.8.8'
# Files attachments
-gem 'carrierwave', '~> 1.2'
+# Locked until https://github.com/carrierwaveuploader/carrierwave/pull/2332/files is merged.
+# config/initializers/carrierwave_patch.rb can be removed once that change is released.
+gem 'carrierwave', '= 1.2.3'
gem 'mini_magick'
-# Drag and Drop UI
-gem 'dropzonejs-rails', '~> 0.7.1'
-
# for backups
gem 'fog-aws', '~> 2.0.1'
gem 'fog-core', '~> 1.44'
-gem 'fog-google', '~> 1.3.3'
+gem 'fog-google', '~> 1.7.1'
gem 'fog-local', '~> 0.3'
gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1'
gem 'fog-aliyun', '~> 0.2.0'
# for Google storage
-gem 'google-api-client', '~> 0.19.8'
+gem 'google-api-client', '~> 0.23'
# for aws storage
gem 'unf', '~> 0.1.4'
@@ -134,7 +130,8 @@ gem 'seed-fu', '~> 2.3.7'
# Markdown and HTML processing
gem 'html-pipeline', '~> 2.8'
gem 'deckar01-task_list', '2.0.0'
-gem 'gitlab-markup', '~> 1.6.4'
+gem 'gitlab-markup', '~> 1.6.5'
+gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'redcarpet', '~> 3.4'
gem 'commonmarker', '~> 0.17'
gem 'RedCloth', '~> 4.3.2'
@@ -142,12 +139,13 @@ gem 'rdoc', '~> 6.0'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
-gem 'asciidoctor', '~> 1.5.6'
+gem 'asciidoctor', '~> 1.5.8'
gem 'asciidoctor-plantuml', '0.0.8'
gem 'rouge', '~> 3.1'
gem 'truncato', '~> 0.7.9'
gem 'bootstrap_form', '~> 2.7.0'
gem 'nokogiri', '~> 1.8.2'
+gem 'escape_utils', '~> 1.1'
# Calendar rendering
gem 'icalendar'
@@ -161,6 +159,11 @@ group :unicorn do
gem 'unicorn-worker-killer', '~> 0.4.4'
end
+group :puma do
+ gem 'puma', '~> 3.12', require: false
+ gem 'puma_worker_killer', require: false
+end
+
# State machine
gem 'state_machines-activerecord', '~> 0.5.1'
@@ -168,10 +171,9 @@ gem 'state_machines-activerecord', '~> 0.5.1'
gem 'acts-as-taggable-on', '~> 5.0'
# Background jobs
-gem 'sidekiq', '~> 5.1'
+gem 'sidekiq', '~> 5.2.1'
gem 'sidekiq-cron', '~> 0.6.0'
gem 'redis-namespace', '~> 1.6.0'
-gem 'sidekiq-limit_fetch', '~> 3.4', require: false
# Cron Parser
gem 'rufus-scheduler', '~> 3.4'
@@ -180,7 +182,7 @@ gem 'rufus-scheduler', '~> 3.4'
gem 'httparty', '~> 0.13.3'
# Colored output to console
-gem 'rainbow', '~> 2.2'
+gem 'rainbow', '~> 3.0'
# Progress bar
gem 'ruby-progressbar'
@@ -195,6 +197,9 @@ gem 're2', '~> 1.1.1'
gem 'version_sorter', '~> 2.1.0'
+# Export Ruby Regex to Javascript
+gem 'js_regex', '~> 2.2.1'
+
# User agent parsing
gem 'device_detector'
@@ -205,6 +210,9 @@ gem 'redis-rails', '~> 5.0.2'
gem 'redis', '~> 3.2'
gem 'connection_pool', '~> 2.0'
+# Discord integration
+gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
+
# HipChat integration
gem 'hipchat', '~> 1.5.0'
@@ -212,10 +220,7 @@ gem 'hipchat', '~> 1.5.0'
gem 'jira-ruby', '~> 1.4'
# Flowdock integration
-gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
-
-# Gemnasium integration
-gem 'gemnasium-gitlab-service', '~> 0.2'
+gem 'flowdock', '~> 0.7'
# Slack integration
gem 'slack-notifier', '~> 1.5.1'
@@ -224,7 +229,7 @@ gem 'slack-notifier', '~> 1.5.1'
gem 'hangouts-chat', '~> 0.0.5'
# Asana integration
-gem 'asana', '~> 0.6.0'
+gem 'asana', '~> 0.8.1'
# FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1'
@@ -248,9 +253,6 @@ gem 'rack-attack', '~> 4.4.1'
# Ace editor
gem 'ace-rails-ap', '~> 4.1.0'
-# Keyboard shortcuts
-gem 'mousetrap-rails', '~> 1.4.6'
-
# Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.5'
@@ -298,7 +300,7 @@ gem 'peek-mysql2', '~> 1.1.0', group: :mysql
gem 'peek-pg', '~> 1.3.0', group: :postgres
gem 'peek-rblineprof', '~> 0.2.0'
gem 'peek-redis', '~> 1.2.0'
-gem 'peek-sidekiq', '~> 1.0.3'
+gem 'gitlab-sidekiq-fetcher', require: 'sidekiq-reliable-fetch'
# Metrics
group :metrics do
@@ -319,7 +321,7 @@ group :development do
# Better errors handler
gem 'better_errors', '~> 2.1.0'
- gem 'binding_of_caller', '~> 0.7.2'
+ gem 'binding_of_caller', '~> 0.8.0'
# thin instead webrick
gem 'thin', '~> 1.7.0'
@@ -346,7 +348,7 @@ group :development, :test do
gem 'minitest', '~> 5.7.0'
# Generate Fake data
- gem 'ffaker', '~> 2.4'
+ gem 'ffaker', '~> 2.10'
gem 'capybara', '~> 2.15'
gem 'capybara-screenshot', '~> 1.0.0'
@@ -361,15 +363,14 @@ group :development, :test do
gem 'rubocop-rspec', '~> 1.22.1'
gem 'scss_lint', '~> 0.56.0', require: false
- gem 'haml_lint', '~> 0.26.0', require: false
+ gem 'haml_lint', '~> 0.28.0', require: false
gem 'simplecov', '~> 0.14.0', require: false
- gem 'flay', '~> 2.10.0', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false
- gem 'license_finder', '~> 3.1', require: false
- gem 'knapsack', '~> 1.16'
+ gem 'license_finder', '~> 5.4', require: false
+ gem 'knapsack', '~> 1.17'
gem 'activerecord_sane_schema_dumper', gem_versions['activerecord_sane_schema_dumper']
@@ -388,8 +389,9 @@ group :test do
gem 'rails-controller-testing' if rails5? # Rails5 only gem.
gem 'test_after_commit', '~> 1.1' unless rails5? # Remove this gem when migrated to rails 5.0. It's been integrated to rails 5.0.
gem 'sham_rack', '~> 1.3.6'
- gem 'concurrent-ruby', '~> 1.0.5'
+ gem 'concurrent-ruby', '~> 1.1'
gem 'test-prof', '~> 0.2.5'
+ gem 'rspec_junit_formatter'
end
gem 'octokit', '~> 4.9'
@@ -423,11 +425,10 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.112.0', require: 'gitaly'
-gem 'grpc', '~> 1.11.0'
+gem 'gitaly-proto', '~> 0.123.0', require: 'gitaly'
+gem 'grpc', '~> 1.15.0'
-# Locked until https://github.com/google/protobuf/issues/4210 is closed
-gem 'google-protobuf', '= 3.5.1'
+gem 'google-protobuf', '~> 3.6'
gem 'toml-rb', '~> 1.0.0', require: false
@@ -439,6 +440,3 @@ gem 'flipper-active_support_cache_store', '~> 0.13.0'
# Structured logging
gem 'lograge', '~> 0.5'
gem 'grape_logging', '~> 1.7'
-
-# Asset synchronization
-gem 'asset_sync', '~> 2.4'
diff --git a/Gemfile.lock b/Gemfile.lock
index 62c3b28f386..380f1dbc89f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -4,41 +4,44 @@ GEM
RedCloth (4.3.2)
abstract_type (0.0.7)
ace-rails-ap (4.1.2)
- actionmailer (4.2.10)
- actionpack (= 4.2.10)
- actionview (= 4.2.10)
- activejob (= 4.2.10)
+ actioncable (5.0.7)
+ actionpack (= 5.0.7)
+ nio4r (>= 1.2, < 3.0)
+ websocket-driver (~> 0.6.1)
+ actionmailer (5.0.7)
+ actionpack (= 5.0.7)
+ actionview (= 5.0.7)
+ activejob (= 5.0.7)
mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- actionpack (4.2.10)
- actionview (= 4.2.10)
- activesupport (= 4.2.10)
- rack (~> 1.6)
- rack-test (~> 0.6.2)
- rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-dom-testing (~> 2.0)
+ actionpack (5.0.7)
+ actionview (= 5.0.7)
+ activesupport (= 5.0.7)
+ rack (~> 2.0)
+ rack-test (~> 0.6.3)
+ rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (4.2.10)
- activesupport (= 4.2.10)
+ actionview (5.0.7)
+ activesupport (= 5.0.7)
builder (~> 3.1)
erubis (~> 2.7.0)
- rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (4.2.10)
- activesupport (= 4.2.10)
- globalid (>= 0.3.0)
- activemodel (4.2.10)
- activesupport (= 4.2.10)
- builder (~> 3.1)
- activerecord (4.2.10)
- activemodel (= 4.2.10)
- activesupport (= 4.2.10)
- arel (~> 6.0)
- activerecord_sane_schema_dumper (0.2)
- rails (>= 4, < 5)
- activesupport (4.2.10)
- i18n (~> 0.7)
+ activejob (5.0.7)
+ activesupport (= 5.0.7)
+ globalid (>= 0.3.6)
+ activemodel (5.0.7)
+ activesupport (= 5.0.7)
+ activerecord (5.0.7)
+ activemodel (= 5.0.7)
+ activesupport (= 5.0.7)
+ arel (~> 7.0)
+ activerecord_sane_schema_dumper (1.0)
+ rails (>= 5, < 6)
+ activesupport (5.0.7)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
minitest (~> 5.1)
- thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
acts-as-taggable-on (5.0.0)
activerecord (>= 4.2.8)
@@ -49,20 +52,15 @@ GEM
public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1)
akismet (2.0.0)
- arel (6.0.4)
- asana (0.6.0)
+ arel (7.1.4)
+ asana (0.8.1)
faraday (~> 0.9)
faraday_middleware (~> 0.9)
faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0)
- asciidoctor (1.5.6.2)
+ asciidoctor (1.5.8)
asciidoctor-plantuml (0.0.8)
asciidoctor (~> 1.5)
- asset_sync (2.4.0)
- activemodel (>= 4.1.0)
- fog-core
- mime-types (>= 2.99)
- unf
ast (2.4.0)
atomic (1.1.99)
attr_encrypted (3.1.0)
@@ -84,14 +82,13 @@ GEM
erubis (>= 2.6.6)
rack (>= 0.9.0)
bindata (2.4.3)
- binding_of_caller (0.7.2)
+ binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
- blankslate (2.1.2.4)
- bootsnap (1.3.1)
+ bootsnap (1.3.2)
msgpack (~> 1.0)
bootstrap_form (2.7.0)
brakeman (4.2.1)
- browser (2.2.0)
+ browser (2.5.3)
builder (3.2.3)
bullet (5.5.1)
activesupport (>= 3.0.0)
@@ -126,31 +123,29 @@ GEM
coderay (1.1.2)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
- commonmarker (0.17.8)
+ commonmarker (0.17.13)
ruby-enum (~> 0.5)
concord (0.1.5)
adamantium (~> 0.2.0)
equalizer (~> 0.0.9)
- concurrent-ruby (1.0.5)
- concurrent-ruby-ext (1.0.5)
- concurrent-ruby (= 1.0.5)
- connection_pool (2.2.1)
+ concurrent-ruby (1.1.3)
+ concurrent-ruby-ext (1.1.3)
+ concurrent-ruby (= 1.1.3)
+ connection_pool (2.2.2)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.4)
creole (0.5.0)
css_parser (1.5.0)
addressable
- daemons (1.2.3)
+ daemons (1.2.6)
database_cleaner (1.5.3)
- debug_inspector (0.0.2)
+ debug_inspector (0.0.3)
debugger-ruby_core_source (1.3.8)
deckar01-task_list (2.0.0)
html-pipeline
declarative (0.0.10)
declarative-option (0.1.0)
- default_value_for (3.0.2)
- activerecord (>= 3.2.0, < 5.1)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0)
@@ -168,6 +163,8 @@ GEM
rotp (~> 2.0)
diff-lcs (1.3)
diffy (3.1.0)
+ discordrb-webhooks-blackst0ne (3.3.0)
+ rest-client (~> 2.0)
docile (1.1.5)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
@@ -176,8 +173,6 @@ GEM
doorkeeper-openid_connect (1.5.0)
doorkeeper (~> 4.3)
json-jwt (~> 1.6)
- dropzonejs-rails (0.7.2)
- rails (> 3.1)
ed25519 (1.2.4)
email_reply_trimmer (0.1.6)
email_spec (2.2.0)
@@ -190,7 +185,7 @@ GEM
escape_utils (1.1.1)
et-orbi (1.0.3)
tzinfo
- eventmachine (1.0.8)
+ eventmachine (1.2.7)
excon (0.62.0)
execjs (2.6.0)
expression_parser (0.9.0)
@@ -208,13 +203,8 @@ GEM
multi_json
fast_blank (1.0.0)
fast_gettext (1.6.0)
- ffaker (2.4.0)
- ffi (1.9.18)
- flay (2.10.0)
- erubis (~> 2.7.0)
- path_expander (~> 1.0)
- ruby_parser (~> 3.0)
- sexp_processor (~> 4.0)
+ ffaker (2.10.0)
+ ffi (1.9.25)
flipper (0.13.0)
flipper-active_record (0.13.0)
activerecord (>= 3.2, < 6)
@@ -239,11 +229,11 @@ GEM
builder
excon (~> 0.58)
formatador (~> 0.2)
- fog-google (1.3.3)
+ fog-google (1.7.1)
fog-core
fog-json
fog-xml
- google-api-client (~> 0.19.1)
+ google-api-client (~> 0.23.0)
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
@@ -269,8 +259,6 @@ GEM
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
- gemnasium-gitlab-service (0.2.6)
- rugged (~> 0.21)
gemojione (3.3.0)
json
get_process_mem (0.2.0)
@@ -284,36 +272,14 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.112.0)
- google-protobuf (~> 3.1)
- grpc (~> 1.10)
- github-linguist (5.3.3)
- charlock_holmes (~> 0.7.5)
- escape_utils (~> 1.1.0)
- mime-types (>= 1.19)
- rugged (>= 0.25.1)
+ gitaly-proto (0.123.0)
+ grpc (~> 1.0)
github-markup (1.7.0)
- gitlab-flowdock-git-hook (1.0.1)
- flowdock (~> 0.7)
- gitlab-grit (>= 2.4.1)
- multi_json
- gitlab-gollum-lib (4.2.7.5)
- gemojione (~> 3.2)
- github-markup (~> 1.6)
- gollum-grit_adapter (~> 1.0)
- nokogiri (>= 1.6.1, < 2.0)
- rouge (~> 3.1)
- sanitize (~> 4.6.4)
- stringex (~> 2.6)
- gitlab-gollum-rugged_adapter (0.4.4.1)
- mime-types (>= 1.15)
- rugged (~> 0.25)
- gitlab-grit (2.8.2)
- charlock_holmes (~> 0.6)
- diff-lcs (~> 1.1)
- mime-types (>= 1.16)
- posix-spawn (~> 0.3)
- gitlab-markup (1.6.4)
+ gitlab-default_value_for (3.1.1)
+ activerecord (>= 3.2.0, < 6.0)
+ gitlab-markup (1.6.5)
+ gitlab-sidekiq-fetcher (0.3.0)
+ sidekiq (~> 5)
gitlab-styles (2.4.1)
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
@@ -325,33 +291,30 @@ GEM
rubyntlm (~> 0.5)
globalid (0.4.1)
activesupport (>= 4.2.0)
- gollum-grit_adapter (1.0.1)
- gitlab-grit (~> 2.7, >= 2.7.1)
gon (6.2.0)
actionpack (>= 3.0)
multi_json
request_store (>= 1.0)
- google-api-client (0.19.8)
+ google-api-client (0.23.4)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.5, < 0.7.0)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
- google-protobuf (3.5.1)
- googleapis-common-protos-types (1.0.1)
+ google-protobuf (3.6.1)
+ googleapis-common-protos-types (1.0.2)
google-protobuf (~> 3.0)
- googleauth (0.6.2)
+ googleauth (0.6.6)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
- logging (~> 2.0)
memoist (~> 0.12)
multi_json (~> 1.11)
- os (~> 0.9)
+ os (>= 0.9, < 2.0)
signet (~> 0.7)
gpgme (2.0.13)
mini_portile2 (~> 2.1)
- grape (1.0.3)
+ grape (1.1.0)
activesupport
builder
mustermann-grape (~> 1.0.0)
@@ -371,18 +334,17 @@ GEM
railties
sprockets-rails
graphql (1.8.1)
- grpc (1.11.0)
+ grpc (1.15.0)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
- googleauth (>= 0.5.1, < 0.7)
haml (5.0.4)
temple (>= 0.8.0)
tilt
- haml_lint (0.26.0)
+ haml_lint (0.28.0)
haml (>= 4.0, < 5.1)
rainbow
rake (>= 10, < 13)
- rubocop (>= 0.49.0)
+ rubocop (>= 0.50.0)
sysexits (~> 1.1)
hamlit (2.8.8)
temple (>= 0.8.0)
@@ -417,7 +379,7 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
- i18n (0.9.5)
+ i18n (1.1.1)
concurrent-ruby (~> 1.0)
icalendar (2.4.1)
ice_nine (0.11.2)
@@ -430,6 +392,8 @@ GEM
multipart-post
oauth (~> 0.5, >= 0.5.0)
jquery-atwho-rails (1.3.2)
+ js_regex (2.2.1)
+ regexp_parser (>= 0.4.11, <= 0.5.0)
json (1.8.6)
json-jwt (1.9.4)
activesupport
@@ -451,7 +415,7 @@ GEM
kaminari-core (= 1.0.1)
kaminari-core (1.0.1)
kgio (2.10.0)
- knapsack (1.16.0)
+ knapsack (1.17.0)
rake
kubeclient (3.1.0)
http (~> 2.2.2)
@@ -465,27 +429,22 @@ GEM
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
- license_finder (3.1.1)
+ license_finder (5.4.0)
bundler
- httparty
rubyzip
thor
- toml (= 0.1.2)
- with_env (> 1.0)
+ toml (= 0.2.0)
+ with_env (= 1.1.0)
xml-simple
licensee (8.9.2)
rugged (~> 0.24)
- little-plugger (1.1.4)
locale (2.1.2)
- logging (2.2.2)
- little-plugger (~> 1.1)
- multi_json (~> 1.10)
lograge (0.10.0)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
- loofah (2.2.2)
+ loofah (2.2.3)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
@@ -494,28 +453,29 @@ GEM
memoist (0.16.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
- method_source (0.9.0)
- mime-types (3.1)
+ method_source (0.9.2)
+ mime-types (3.2.2)
mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
+ mime-types-data (3.2018.0812)
mimemagic (0.3.0)
mini_magick (4.8.0)
- mini_mime (1.0.0)
+ mini_mime (1.0.1)
mini_portile2 (2.3.0)
minitest (5.7.0)
- mousetrap-rails (1.4.6)
msgpack (1.2.4)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
- mustermann (1.0.2)
+ mustermann (1.0.3)
mustermann-grape (1.0.0)
mustermann (~> 1.0.0)
mysql2 (0.4.10)
+ nakayoshi_fork (0.0.4)
net-ldap (0.16.0)
net-ssh (5.0.1)
netrc (0.11.0)
- nokogiri (1.8.4)
+ nio4r (2.3.1)
+ nokogiri (1.8.5)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@@ -585,13 +545,11 @@ GEM
org-ruby (0.9.12)
rubypants (~> 0.2)
orm_adapter (0.5.0)
- os (0.9.6)
+ os (1.0.0)
parallel (1.12.1)
- parser (2.5.1.0)
+ parser (2.5.3.0)
ast (~> 2.4.0)
- parslet (1.5.0)
- blankslate (~> 2.0)
- path_expander (1.0.2)
+ parslet (1.8.2)
peek (1.0.1)
concurrent-ruby (>= 0.9.0)
concurrent-ruby-ext (>= 0.9.0)
@@ -614,14 +572,9 @@ GEM
atomic (>= 1.0.0)
peek
redis
- peek-sidekiq (1.0.3)
- atomic (>= 1.0.0)
- peek
- sidekiq
pg (0.18.4)
po_to_json (1.0.1)
json (>= 1.6.0)
- posix-spawn (0.3.13)
powerpack (0.1.1)
premailer (1.10.4)
addressable
@@ -642,11 +595,15 @@ GEM
pry-byebug (3.4.3)
byebug (>= 9.0, < 9.1)
pry (~> 0.10)
- pry-rails (0.3.5)
- pry (>= 0.9.10)
- public_suffix (3.0.2)
+ pry-rails (0.3.6)
+ pry (>= 0.10.4)
+ public_suffix (3.0.3)
+ puma (3.12.0)
+ puma_worker_killer (0.1.0)
+ get_process_mem (~> 0.2)
+ puma (>= 2.7, < 4)
pyu-ruby-sasl (0.0.3.3)
- rack (1.6.10)
+ rack (2.0.6)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.4.1)
@@ -658,41 +615,45 @@ GEM
httpclient (>= 2.4)
multi_json (>= 1.3.6)
rack (>= 1.1)
- rack-protection (2.0.1)
+ rack-protection (2.0.4)
rack
rack-proxy (0.6.0)
rack
rack-test (0.6.3)
rack (>= 1.0)
- rails (4.2.10)
- actionmailer (= 4.2.10)
- actionpack (= 4.2.10)
- actionview (= 4.2.10)
- activejob (= 4.2.10)
- activemodel (= 4.2.10)
- activerecord (= 4.2.10)
- activesupport (= 4.2.10)
- bundler (>= 1.3.0, < 2.0)
- railties (= 4.2.10)
- sprockets-rails
+ rails (5.0.7)
+ actioncable (= 5.0.7)
+ actionmailer (= 5.0.7)
+ actionpack (= 5.0.7)
+ actionview (= 5.0.7)
+ activejob (= 5.0.7)
+ activemodel (= 5.0.7)
+ activerecord (= 5.0.7)
+ activesupport (= 5.0.7)
+ bundler (>= 1.3.0)
+ railties (= 5.0.7)
+ sprockets-rails (>= 2.0.0)
+ rails-controller-testing (1.0.2)
+ actionpack (~> 5.x, >= 5.0.1)
+ actionview (~> 5.x, >= 5.0.1)
+ activesupport (~> 5.x)
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
- rails-dom-testing (1.0.9)
- activesupport (>= 4.2.0, < 5.0)
- nokogiri (~> 1.6)
- rails-deprecated_sanitizer (>= 1.0.1)
+ rails-dom-testing (2.0.3)
+ activesupport (>= 4.2.0)
+ nokogiri (>= 1.6)
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
- rails-i18n (4.0.9)
- i18n (~> 0.7)
- railties (~> 4.0)
- railties (4.2.10)
- actionpack (= 4.2.10)
- activesupport (= 4.2.10)
+ rails-i18n (5.1.1)
+ i18n (>= 0.7, < 2)
+ railties (>= 5.0, < 6)
+ railties (5.0.7)
+ actionpack (= 5.0.7)
+ activesupport (= 5.0.7)
+ method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
- rainbow (2.2.2)
- rake
+ rainbow (3.0.0)
raindrops (0.18.0)
rake (12.3.1)
rb-fsevent (0.10.2)
@@ -727,8 +688,9 @@ GEM
redis-actionpack (>= 5.0, < 6)
redis-activesupport (>= 5.0, < 6)
redis-store (>= 1.2, < 2)
- redis-store (1.4.1)
+ redis-store (1.6.0)
redis (>= 2.2, < 5)
+ regexp_parser (0.5.0)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
@@ -741,10 +703,10 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
- retriable (3.1.1)
+ retriable (3.1.2)
rinku (2.0.0)
rotp (2.1.2)
- rouge (3.2.0)
+ rouge (3.3.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
@@ -779,6 +741,8 @@ GEM
rspec-core
rspec-set (0.1.3)
rspec-support (3.7.1)
+ rspec_junit_formatter (0.4.1)
+ rspec-core (>= 2, < 4, != 2.12.0)
rspec_profiling (0.0.5)
activerecord
pg
@@ -803,14 +767,14 @@ GEM
ruby-progressbar (1.9.0)
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
- ruby_parser (3.9.0)
- sexp_processor (~> 4.1)
+ ruby_parser (3.11.0)
+ sexp_processor (~> 4.9)
rubyntlm (0.6.2)
rubypants (0.2.0)
- rubyzip (1.2.1)
+ rubyzip (1.2.2)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
- rugged (0.27.4)
+ rugged (0.27.5)
safe_yaml (1.0.4)
sanitize (4.6.6)
crass (~> 1.0.2)
@@ -844,22 +808,19 @@ GEM
sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
- sexp_processor (4.9.0)
+ sexp_processor (4.11.0)
sham_rack (1.3.6)
rack
shoulda-matchers (3.1.2)
activesupport (>= 4.0.0)
- sidekiq (5.1.3)
- concurrent-ruby (~> 1.0)
- connection_pool (~> 2.2, >= 2.2.0)
+ sidekiq (5.2.3)
+ connection_pool (~> 2.2, >= 2.2.2)
rack-protection (>= 1.5.0)
redis (>= 3.3.5, < 5)
sidekiq-cron (0.6.0)
rufus-scheduler (>= 3.3.0)
sidekiq (>= 4.2.1)
- sidekiq-limit_fetch (3.4.0)
- sidekiq (>= 4)
- signet (0.8.1)
+ signet (0.11.0)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
@@ -871,7 +832,7 @@ GEM
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slack-notifier (1.5.1)
- spring (2.0.1)
+ spring (2.0.2)
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
@@ -892,16 +853,13 @@ GEM
state_machines-activerecord (0.5.1)
activerecord (>= 4.1, < 6.0)
state_machines-activemodel (>= 0.5.0)
- stringex (2.8.4)
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
temple (0.8.0)
test-prof (0.2.5)
- test_after_commit (1.1.0)
- activerecord (>= 3.2)
text (1.3.1)
- thin (1.7.0)
+ thin (1.7.2)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
@@ -910,8 +868,8 @@ GEM
tilt (2.0.8)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
- toml (0.1.2)
- parslet (~> 1.5.0)
+ toml (0.2.0)
+ parslet (~> 1.8.0)
toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
trollop (2.1.3)
@@ -960,8 +918,11 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
- webpack-rails (0.9.10)
+ webpack-rails (0.9.11)
railties (>= 3.2.0)
+ websocket-driver (0.6.5)
+ websocket-extensions (>= 0.1.0)
+ websocket-extensions (0.1.3)
wikicloth (0.8.1)
builder
expression_parser
@@ -977,14 +938,13 @@ PLATFORMS
DEPENDENCIES
RedCloth (~> 4.3.2)
ace-rails-ap (~> 4.1.0)
- activerecord_sane_schema_dumper (= 0.2)
+ activerecord_sane_schema_dumper (= 1.0)
acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2)
akismet (~> 2.0)
- asana (~> 0.6.0)
- asciidoctor (~> 1.5.6)
+ asana (~> 0.8.1)
+ asciidoctor (~> 1.5.8)
asciidoctor-plantuml (= 0.0.8)
- asset_sync (~> 2.4)
attr_encrypted (~> 3.1.0)
awesome_print
babosa (~> 1.0.2)
@@ -993,79 +953,77 @@ DEPENDENCIES
bcrypt_pbkdf (~> 1.0)
benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0)
- binding_of_caller (~> 0.7.2)
+ binding_of_caller (~> 0.8.0)
bootsnap (~> 1.3)
bootstrap_form (~> 2.7.0)
brakeman (~> 4.2)
- browser (~> 2.2)
+ browser (~> 2.5)
bullet (~> 5.5.0)
bundler-audit (~> 0.5.0)
capybara (~> 2.15)
capybara-screenshot (~> 1.0.0)
- carrierwave (~> 1.2)
+ carrierwave (= 1.2.3)
charlock_holmes (~> 0.7.5)
chronic (~> 0.10.2)
chronic_duration (~> 0.10.6)
commonmarker (~> 0.17)
- concurrent-ruby (~> 1.0.5)
+ concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
creole (~> 0.5.0)
database_cleaner (~> 1.5.0)
deckar01-task_list (= 2.0.0)
- default_value_for (~> 3.0.0)
device_detector
devise (~> 4.4)
devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0)
+ discordrb-webhooks-blackst0ne (~> 3.3)
doorkeeper (~> 4.3)
doorkeeper-openid_connect (~> 1.5)
- dropzonejs-rails (~> 0.7.1)
ed25519 (~> 1.2)
email_reply_trimmer (~> 0.1)
email_spec (~> 2.2.0)
+ escape_utils (~> 1.1)
factory_bot_rails (~> 4.8.2)
faraday (~> 0.12)
fast_blank
- ffaker (~> 2.4)
- flay (~> 2.10.0)
+ ffaker (~> 2.10)
flipper (~> 0.13.0)
flipper-active_record (~> 0.13.0)
flipper-active_support_cache_store (~> 0.13.0)
+ flowdock (~> 0.7)
fog-aliyun (~> 0.2.0)
fog-aws (~> 2.0.1)
fog-core (~> 1.44)
- fog-google (~> 1.3.3)
+ fog-google (~> 1.7.1)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7)
foreman (~> 0.84.0)
fuubar (~> 2.2.0)
- gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.3)
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.112.0)
- github-linguist (~> 5.3.3)
- gitlab-flowdock-git-hook (~> 1.0.1)
- gitlab-gollum-lib (~> 4.2)
- gitlab-gollum-rugged_adapter (~> 0.4.4)
- gitlab-markup (~> 1.6.4)
+ gitaly-proto (~> 0.123.0)
+ github-markup (~> 1.7.0)
+ gitlab-default_value_for (~> 3.1.1)
+ gitlab-markup (~> 1.6.5)
+ gitlab-sidekiq-fetcher
gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
- google-api-client (~> 0.19.8)
- google-protobuf (= 3.5.1)
+ google-api-client (~> 0.23)
+ google-protobuf (~> 3.6)
gpgme
- grape (~> 1.0)
+ grape (~> 1.1)
grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.8.0)
- grpc (~> 1.11.0)
- haml_lint (~> 0.26.0)
+ grpc (~> 1.15.0)
+ haml_lint (~> 0.28.0)
hamlit (~> 2.8.8)
hangouts-chat (~> 0.0.5)
hashie-forbidden_attributes
@@ -1078,13 +1036,14 @@ DEPENDENCIES
influxdb (~> 0.2)
jira-ruby (~> 1.4)
jquery-atwho-rails (~> 1.3.2)
+ js_regex (~> 2.2.1)
json-schema (~> 2.8.0)
jwt (~> 1.5.6)
kaminari (~> 1.0)
- knapsack (~> 1.16)
+ knapsack (~> 1.17)
kubeclient (~> 3.1.0)
letter_opener_web (~> 1.3.0)
- license_finder (~> 3.1)
+ license_finder (~> 5.4)
licensee (~> 8.9)
lograge (~> 0.5)
loofah (~> 2.2)
@@ -1092,8 +1051,8 @@ DEPENDENCIES
method_source (~> 0.8)
mini_magick
minitest (~> 5.7.0)
- mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.4.10)
+ nakayoshi_fork (~> 0.0.4)
net-ldap
net-ssh (~> 5.0)
nokogiri (~> 1.8.2)
@@ -1121,20 +1080,22 @@ DEPENDENCIES
peek-pg (~> 1.3.0)
peek-rblineprof (~> 0.2.0)
peek-redis (~> 1.2.0)
- peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2)
premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.9.4)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
+ puma (~> 3.12)
+ puma_worker_killer
rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.2.1)
rack-proxy (~> 0.6.0)
- rails (= 4.2.10)
+ rails (= 5.0.7)
+ rails-controller-testing
rails-deprecated_sanitizer (~> 1.0.3)
- rails-i18n (~> 4.0.9)
- rainbow (~> 2.2)
+ rails-i18n (~> 5.1)
+ rainbow (~> 3.0)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
rbtrace (~> 0.4)
@@ -1153,6 +1114,7 @@ DEPENDENCIES
rspec-rails (~> 3.7.0)
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
+ rspec_junit_formatter
rspec_profiling (~> 0.0.5)
rubocop (~> 0.54.0)
rubocop-rspec (~> 1.22.1)
@@ -1172,9 +1134,8 @@ DEPENDENCIES
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
shoulda-matchers (~> 3.1.2)
- sidekiq (~> 5.1)
+ sidekiq (~> 5.2.1)
sidekiq-cron (~> 0.6.0)
- sidekiq-limit_fetch (~> 3.4)
simple_po_parser (~> 1.1.2)
simplecov (~> 0.14.0)
slack-notifier (~> 1.5.1)
@@ -1186,7 +1147,6 @@ DEPENDENCIES
state_machines-activerecord (~> 0.5.1)
sys-filesystem (~> 1.1.6)
test-prof (~> 0.2.5)
- test_after_commit (~> 1.1)
thin (~> 1.7.0)
timecop (~> 0.8.0)
toml-rb (~> 1.0.0)
@@ -1205,4 +1165,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
- 1.16.3
+ 1.17.1
diff --git a/Gemfile.rails4 b/Gemfile.rails4
new file mode 100644
index 00000000000..0ec00e702aa
--- /dev/null
+++ b/Gemfile.rails4
@@ -0,0 +1,7 @@
+# BUNDLE_GEMFILE=Gemfile.rails4 bundle install
+
+ENV["RAILS5"] = "false"
+
+gemfile = File.expand_path("../Gemfile", __FILE__)
+
+eval(File.read(gemfile), nil, gemfile)
diff --git a/Gemfile.rails4.lock b/Gemfile.rails4.lock
new file mode 100644
index 00000000000..ead404c5f27
--- /dev/null
+++ b/Gemfile.rails4.lock
@@ -0,0 +1,1159 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ RedCloth (4.3.2)
+ abstract_type (0.0.7)
+ ace-rails-ap (4.1.2)
+ actionmailer (4.2.10)
+ actionpack (= 4.2.10)
+ actionview (= 4.2.10)
+ activejob (= 4.2.10)
+ mail (~> 2.5, >= 2.5.4)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ actionpack (4.2.10)
+ actionview (= 4.2.10)
+ activesupport (= 4.2.10)
+ rack (~> 1.6)
+ rack-test (~> 0.6.2)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ actionview (4.2.10)
+ activesupport (= 4.2.10)
+ builder (~> 3.1)
+ erubis (~> 2.7.0)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
+ activejob (4.2.10)
+ activesupport (= 4.2.10)
+ globalid (>= 0.3.0)
+ activemodel (4.2.10)
+ activesupport (= 4.2.10)
+ builder (~> 3.1)
+ activerecord (4.2.10)
+ activemodel (= 4.2.10)
+ activesupport (= 4.2.10)
+ arel (~> 6.0)
+ activerecord_sane_schema_dumper (0.2)
+ rails (>= 4, < 5)
+ activesupport (4.2.10)
+ i18n (~> 0.7)
+ minitest (~> 5.1)
+ thread_safe (~> 0.3, >= 0.3.4)
+ tzinfo (~> 1.1)
+ acts-as-taggable-on (5.0.0)
+ activerecord (>= 4.2.8)
+ adamantium (0.2.0)
+ ice_nine (~> 0.11.0)
+ memoizable (~> 0.4.0)
+ addressable (2.5.2)
+ public_suffix (>= 2.0.2, < 4.0)
+ aes_key_wrap (1.0.1)
+ akismet (2.0.0)
+ arel (6.0.4)
+ asana (0.8.1)
+ faraday (~> 0.9)
+ faraday_middleware (~> 0.9)
+ faraday_middleware-multi_json (~> 0.0)
+ oauth2 (~> 1.0)
+ asciidoctor (1.5.8)
+ asciidoctor-plantuml (0.0.8)
+ asciidoctor (~> 1.5)
+ ast (2.4.0)
+ atomic (1.1.99)
+ attr_encrypted (3.1.0)
+ encryptor (~> 3.0.0)
+ attr_required (1.0.0)
+ awesome_print (1.8.0)
+ axiom-types (0.1.1)
+ descendants_tracker (~> 0.0.4)
+ ice_nine (~> 0.11.0)
+ thread_safe (~> 0.3, >= 0.3.1)
+ babosa (1.0.2)
+ base32 (0.3.2)
+ batch-loader (1.2.1)
+ bcrypt (3.1.12)
+ bcrypt_pbkdf (1.0.0)
+ benchmark-ips (2.3.0)
+ better_errors (2.1.1)
+ coderay (>= 1.0.0)
+ erubis (>= 2.6.6)
+ rack (>= 0.9.0)
+ bindata (2.4.3)
+ binding_of_caller (0.8.0)
+ debug_inspector (>= 0.0.1)
+ bootsnap (1.3.2)
+ msgpack (~> 1.0)
+ bootstrap_form (2.7.0)
+ brakeman (4.2.1)
+ browser (2.5.3)
+ builder (3.2.3)
+ bullet (5.5.1)
+ activesupport (>= 3.0.0)
+ uniform_notifier (~> 1.10.0)
+ bundler-audit (0.5.0)
+ bundler (~> 1.2)
+ thor (~> 0.18)
+ byebug (9.0.6)
+ capybara (2.15.1)
+ addressable
+ mini_mime (>= 0.1.3)
+ nokogiri (>= 1.3.3)
+ rack (>= 1.0.0)
+ rack-test (>= 0.5.4)
+ xpath (~> 2.0)
+ capybara-screenshot (1.0.14)
+ capybara (>= 1.0, < 3)
+ launchy
+ carrierwave (1.2.3)
+ activemodel (>= 4.0.0)
+ activesupport (>= 4.0.0)
+ mime-types (>= 1.16)
+ cause (0.1)
+ charlock_holmes (0.7.6)
+ childprocess (0.9.0)
+ ffi (~> 1.0, >= 1.0.11)
+ chronic (0.10.2)
+ chronic_duration (0.10.6)
+ numerizer (~> 0.1.1)
+ chunky_png (1.3.5)
+ citrus (3.0.2)
+ coderay (1.1.2)
+ coercible (1.0.0)
+ descendants_tracker (~> 0.0.1)
+ commonmarker (0.17.13)
+ ruby-enum (~> 0.5)
+ concord (0.1.5)
+ adamantium (~> 0.2.0)
+ equalizer (~> 0.0.9)
+ concurrent-ruby (1.1.3)
+ concurrent-ruby-ext (1.1.3)
+ concurrent-ruby (= 1.1.3)
+ connection_pool (2.2.2)
+ crack (0.4.3)
+ safe_yaml (~> 1.0.0)
+ crass (1.0.4)
+ creole (0.5.0)
+ css_parser (1.5.0)
+ addressable
+ daemons (1.2.6)
+ database_cleaner (1.5.3)
+ debug_inspector (0.0.3)
+ debugger-ruby_core_source (1.3.8)
+ deckar01-task_list (2.0.0)
+ html-pipeline
+ declarative (0.0.10)
+ declarative-option (0.1.0)
+ default_value_for (3.0.2)
+ activerecord (>= 3.2.0, < 5.1)
+ descendants_tracker (0.0.4)
+ thread_safe (~> 0.3, >= 0.3.1)
+ device_detector (1.0.0)
+ devise (4.4.3)
+ bcrypt (~> 3.0)
+ orm_adapter (~> 0.1)
+ railties (>= 4.1.0, < 6.0)
+ responders
+ warden (~> 1.2.3)
+ devise-two-factor (3.0.0)
+ activesupport
+ attr_encrypted (>= 1.3, < 4, != 2)
+ devise (~> 4.0)
+ railties
+ rotp (~> 2.0)
+ diff-lcs (1.3)
+ diffy (3.1.0)
+ discordrb-webhooks-blackst0ne (3.3.0)
+ rest-client (~> 2.0)
+ docile (1.1.5)
+ domain_name (0.5.20180417)
+ unf (>= 0.0.5, < 1.0.0)
+ doorkeeper (4.3.2)
+ railties (>= 4.2)
+ doorkeeper-openid_connect (1.5.0)
+ doorkeeper (~> 4.3)
+ json-jwt (~> 1.6)
+ ed25519 (1.2.4)
+ email_reply_trimmer (0.1.6)
+ email_spec (2.2.0)
+ htmlentities (~> 4.3.3)
+ launchy (~> 2.1)
+ mail (~> 2.7)
+ encryptor (3.0.0)
+ equalizer (0.0.11)
+ erubis (2.7.0)
+ escape_utils (1.1.1)
+ et-orbi (1.0.3)
+ tzinfo
+ eventmachine (1.2.7)
+ excon (0.62.0)
+ execjs (2.6.0)
+ expression_parser (0.9.0)
+ factory_bot (4.8.2)
+ activesupport (>= 3.0.0)
+ factory_bot_rails (4.8.2)
+ factory_bot (~> 4.8.2)
+ railties (>= 3.0.0)
+ faraday (0.12.2)
+ multipart-post (>= 1.2, < 3)
+ faraday_middleware (0.12.2)
+ faraday (>= 0.7.4, < 1.0)
+ faraday_middleware-multi_json (0.0.6)
+ faraday_middleware
+ multi_json
+ fast_blank (1.0.0)
+ fast_gettext (1.6.0)
+ ffaker (2.10.0)
+ ffi (1.9.25)
+ flipper (0.13.0)
+ flipper-active_record (0.13.0)
+ activerecord (>= 3.2, < 6)
+ flipper (~> 0.13.0)
+ flipper-active_support_cache_store (0.13.0)
+ activesupport (>= 3.2, < 6)
+ flipper (~> 0.13.0)
+ flowdock (0.7.1)
+ httparty (~> 0.7)
+ multi_json
+ fog-aliyun (0.2.0)
+ fog-core (~> 1.27)
+ fog-json (~> 1.0)
+ ipaddress (~> 0.8)
+ xml-simple (~> 1.1)
+ fog-aws (2.0.1)
+ fog-core (~> 1.38)
+ fog-json (~> 1.0)
+ fog-xml (~> 0.1)
+ ipaddress (~> 0.8)
+ fog-core (1.45.0)
+ builder
+ excon (~> 0.58)
+ formatador (~> 0.2)
+ fog-google (1.7.1)
+ fog-core
+ fog-json
+ fog-xml
+ google-api-client (~> 0.23.0)
+ fog-json (1.0.2)
+ fog-core (~> 1.0)
+ multi_json (~> 1.10)
+ fog-local (0.3.1)
+ fog-core (~> 1.27)
+ fog-openstack (0.1.21)
+ fog-core (>= 1.40)
+ fog-json (>= 1.0)
+ ipaddress (>= 0.8)
+ fog-rackspace (0.1.1)
+ fog-core (>= 1.35)
+ fog-json (>= 1.0)
+ fog-xml (>= 0.1)
+ ipaddress (>= 0.8)
+ fog-xml (0.1.3)
+ fog-core
+ nokogiri (>= 1.5.11, < 2.0.0)
+ font-awesome-rails (4.7.0.1)
+ railties (>= 3.2, < 5.1)
+ foreman (0.84.0)
+ thor (~> 0.19.1)
+ formatador (0.2.5)
+ fuubar (2.2.0)
+ rspec-core (~> 3.0)
+ ruby-progressbar (~> 1.4)
+ gemojione (3.3.0)
+ json
+ get_process_mem (0.2.0)
+ gettext (3.2.9)
+ locale (>= 2.0.5)
+ text (>= 1.3.0)
+ gettext_i18n_rails (1.8.0)
+ fast_gettext (>= 0.9.0)
+ gettext_i18n_rails_js (1.3.0)
+ gettext (>= 3.0.2)
+ gettext_i18n_rails (>= 0.7.1)
+ po_to_json (>= 1.0.0)
+ rails (>= 3.2.0)
+ gitaly-proto (0.123.0)
+ grpc (~> 1.0)
+ github-markup (1.7.0)
+ gitlab-markup (1.6.5)
+ gitlab-sidekiq-fetcher (0.3.0)
+ sidekiq (~> 5)
+ gitlab-styles (2.4.1)
+ rubocop (~> 0.54.0)
+ rubocop-gitlab-security (~> 0.1.0)
+ rubocop-rspec (~> 1.19)
+ gitlab_omniauth-ldap (2.0.4)
+ net-ldap (~> 0.16)
+ omniauth (~> 1.3)
+ pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
+ rubyntlm (~> 0.5)
+ globalid (0.4.1)
+ activesupport (>= 4.2.0)
+ gon (6.2.0)
+ actionpack (>= 3.0)
+ multi_json
+ request_store (>= 1.0)
+ google-api-client (0.23.4)
+ addressable (~> 2.5, >= 2.5.1)
+ googleauth (>= 0.5, < 0.7.0)
+ httpclient (>= 2.8.1, < 3.0)
+ mime-types (~> 3.0)
+ representable (~> 3.0)
+ retriable (>= 2.0, < 4.0)
+ google-protobuf (3.6.1)
+ googleapis-common-protos-types (1.0.2)
+ google-protobuf (~> 3.0)
+ googleauth (0.6.6)
+ faraday (~> 0.12)
+ jwt (>= 1.4, < 3.0)
+ memoist (~> 0.12)
+ multi_json (~> 1.11)
+ os (>= 0.9, < 2.0)
+ signet (~> 0.7)
+ gpgme (2.0.13)
+ mini_portile2 (~> 2.1)
+ grape (1.1.0)
+ activesupport
+ builder
+ mustermann-grape (~> 1.0.0)
+ rack (>= 1.3.0)
+ rack-accept
+ virtus (>= 1.0.0)
+ grape-entity (0.7.1)
+ activesupport (>= 4.0)
+ multi_json (>= 1.3.2)
+ grape-path-helpers (1.0.6)
+ activesupport (>= 4, < 5.1)
+ grape (~> 1.0)
+ rake (~> 12)
+ grape_logging (1.7.0)
+ grape
+ graphiql-rails (1.4.10)
+ railties
+ sprockets-rails
+ graphql (1.8.1)
+ grpc (1.15.0)
+ google-protobuf (~> 3.1)
+ googleapis-common-protos-types (~> 1.0.0)
+ haml (5.0.4)
+ temple (>= 0.8.0)
+ tilt
+ haml_lint (0.28.0)
+ haml (>= 4.0, < 5.1)
+ rainbow
+ rake (>= 10, < 13)
+ rubocop (>= 0.50.0)
+ sysexits (~> 1.1)
+ hamlit (2.8.8)
+ temple (>= 0.8.0)
+ thor
+ tilt
+ hangouts-chat (0.0.5)
+ hashdiff (0.3.4)
+ hashie (3.5.7)
+ hashie-forbidden_attributes (0.1.1)
+ hashie (>= 3.0)
+ health_check (2.6.0)
+ rails (>= 4.0)
+ hipchat (1.5.2)
+ httparty
+ mimemagic
+ html-pipeline (2.8.4)
+ activesupport (>= 2)
+ nokogiri (>= 1.4)
+ html2text (0.2.0)
+ nokogiri (~> 1.6)
+ htmlentities (4.3.4)
+ http (2.2.2)
+ addressable (~> 2.3)
+ http-cookie (~> 1.0)
+ http-form_data (~> 1.0.1)
+ http_parser.rb (~> 0.6.0)
+ http-cookie (1.0.3)
+ domain_name (~> 0.5)
+ http-form_data (1.0.3)
+ http_parser.rb (0.6.0)
+ httparty (0.13.7)
+ json (~> 1.8)
+ multi_xml (>= 0.5.2)
+ httpclient (2.8.3)
+ i18n (0.9.5)
+ concurrent-ruby (~> 1.0)
+ icalendar (2.4.1)
+ ice_nine (0.11.2)
+ influxdb (0.2.3)
+ cause
+ json
+ ipaddress (0.8.3)
+ jira-ruby (1.4.1)
+ activesupport
+ multipart-post
+ oauth (~> 0.5, >= 0.5.0)
+ jquery-atwho-rails (1.3.2)
+ js_regex (2.2.1)
+ regexp_parser (>= 0.4.11, <= 0.5.0)
+ json (1.8.6)
+ json-jwt (1.9.4)
+ activesupport
+ aes_key_wrap
+ bindata
+ json-schema (2.8.0)
+ addressable (>= 2.4)
+ jwt (1.5.6)
+ kaminari (1.0.1)
+ activesupport (>= 4.1.0)
+ kaminari-actionview (= 1.0.1)
+ kaminari-activerecord (= 1.0.1)
+ kaminari-core (= 1.0.1)
+ kaminari-actionview (1.0.1)
+ actionview
+ kaminari-core (= 1.0.1)
+ kaminari-activerecord (1.0.1)
+ activerecord
+ kaminari-core (= 1.0.1)
+ kaminari-core (1.0.1)
+ kgio (2.10.0)
+ knapsack (1.17.0)
+ rake
+ kubeclient (3.1.0)
+ http (~> 2.2.2)
+ recursive-open-struct (~> 1.0, >= 1.0.4)
+ rest-client (~> 2.0)
+ launchy (2.4.3)
+ addressable (~> 2.3)
+ letter_opener (1.4.1)
+ launchy (~> 2.2)
+ letter_opener_web (1.3.0)
+ actionmailer (>= 3.2)
+ letter_opener (~> 1.0)
+ railties (>= 3.2)
+ license_finder (5.4.0)
+ bundler
+ rubyzip
+ thor
+ toml (= 0.2.0)
+ with_env (= 1.1.0)
+ xml-simple
+ licensee (8.9.2)
+ rugged (~> 0.24)
+ locale (2.1.2)
+ lograge (0.10.0)
+ actionpack (>= 4)
+ activesupport (>= 4)
+ railties (>= 4)
+ request_store (~> 1.0)
+ loofah (2.2.3)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ mail (2.7.0)
+ mini_mime (>= 0.1.1)
+ mail_room (0.9.1)
+ memoist (0.16.0)
+ memoizable (0.4.2)
+ thread_safe (~> 0.3, >= 0.3.1)
+ method_source (0.9.0)
+ mime-types (3.2.2)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2018.0812)
+ mimemagic (0.3.0)
+ mini_magick (4.8.0)
+ mini_mime (1.0.1)
+ mini_portile2 (2.3.0)
+ minitest (5.7.0)
+ msgpack (1.2.4)
+ multi_json (1.13.1)
+ multi_xml (0.6.0)
+ multipart-post (2.0.0)
+ mustermann (1.0.3)
+ mustermann-grape (1.0.0)
+ mustermann (~> 1.0.0)
+ mysql2 (0.4.10)
+ nakayoshi_fork (0.0.4)
+ net-ldap (0.16.0)
+ net-ssh (5.0.1)
+ netrc (0.11.0)
+ nokogiri (1.8.5)
+ mini_portile2 (~> 2.3.0)
+ nokogumbo (1.5.0)
+ nokogiri
+ numerizer (0.1.1)
+ oauth (0.5.4)
+ oauth2 (1.4.0)
+ faraday (>= 0.8, < 0.13)
+ jwt (~> 1.0)
+ multi_json (~> 1.3)
+ multi_xml (~> 0.5)
+ rack (>= 1.2, < 3)
+ octokit (4.9.0)
+ sawyer (~> 0.8.0, >= 0.5.3)
+ omniauth (1.8.1)
+ hashie (>= 3.4.6, < 3.6.0)
+ rack (>= 1.6.2, < 3)
+ omniauth-auth0 (2.0.0)
+ omniauth-oauth2 (~> 1.4)
+ omniauth-authentiq (0.3.3)
+ jwt (>= 1.5)
+ omniauth-oauth2 (>= 1.5)
+ omniauth-azure-oauth2 (0.0.9)
+ jwt (~> 1.0)
+ omniauth (~> 1.0)
+ omniauth-oauth2 (~> 1.4)
+ omniauth-cas3 (1.1.4)
+ addressable (~> 2.3)
+ nokogiri (~> 1.7, >= 1.7.1)
+ omniauth (~> 1.2)
+ omniauth-facebook (4.0.0)
+ omniauth-oauth2 (~> 1.2)
+ omniauth-github (1.3.0)
+ omniauth (~> 1.5)
+ omniauth-oauth2 (>= 1.4.0, < 2.0)
+ omniauth-gitlab (1.0.3)
+ omniauth (~> 1.0)
+ omniauth-oauth2 (~> 1.0)
+ omniauth-google-oauth2 (0.5.3)
+ jwt (>= 1.5)
+ omniauth (>= 1.1.1)
+ omniauth-oauth2 (>= 1.5)
+ omniauth-kerberos (0.3.0)
+ omniauth-multipassword
+ timfel-krb5-auth (~> 0.8)
+ omniauth-multipassword (0.4.2)
+ omniauth (~> 1.0)
+ omniauth-oauth (1.1.0)
+ oauth
+ omniauth (~> 1.0)
+ omniauth-oauth2 (1.5.0)
+ oauth2 (~> 1.1)
+ omniauth (~> 1.2)
+ omniauth-oauth2-generic (0.2.2)
+ omniauth-oauth2 (~> 1.0)
+ omniauth-saml (1.10.0)
+ omniauth (~> 1.3, >= 1.3.2)
+ ruby-saml (~> 1.7)
+ omniauth-shibboleth (1.3.0)
+ omniauth (>= 1.0.0)
+ omniauth-twitter (1.4.0)
+ omniauth-oauth (~> 1.1)
+ rack
+ omniauth_crowd (2.2.3)
+ activesupport
+ nokogiri (>= 1.4.4)
+ omniauth (~> 1.0)
+ org-ruby (0.9.12)
+ rubypants (~> 0.2)
+ orm_adapter (0.5.0)
+ os (1.0.0)
+ parallel (1.12.1)
+ parser (2.5.3.0)
+ ast (~> 2.4.0)
+ parslet (1.8.2)
+ peek (1.0.1)
+ concurrent-ruby (>= 0.9.0)
+ concurrent-ruby-ext (>= 0.9.0)
+ railties (>= 4.0.0)
+ peek-gc (0.0.2)
+ peek
+ peek-mysql2 (1.1.0)
+ atomic (>= 1.0.0)
+ mysql2
+ peek
+ peek-pg (1.3.0)
+ concurrent-ruby
+ concurrent-ruby-ext
+ peek
+ pg
+ peek-rblineprof (0.2.0)
+ peek
+ rblineprof
+ peek-redis (1.2.0)
+ atomic (>= 1.0.0)
+ peek
+ redis
+ pg (0.18.4)
+ po_to_json (1.0.1)
+ json (>= 1.6.0)
+ powerpack (0.1.1)
+ premailer (1.10.4)
+ addressable
+ css_parser (>= 1.4.10)
+ htmlentities (>= 4.0.0)
+ premailer-rails (1.9.7)
+ actionmailer (>= 3, < 6)
+ premailer (~> 1.7, >= 1.7.9)
+ proc_to_ast (0.1.0)
+ coderay
+ parser
+ unparser
+ procto (0.0.3)
+ prometheus-client-mmap (0.9.4)
+ pry (0.11.3)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ pry-byebug (3.4.3)
+ byebug (>= 9.0, < 9.1)
+ pry (~> 0.10)
+ pry-rails (0.3.6)
+ pry (>= 0.10.4)
+ public_suffix (3.0.3)
+ puma (3.12.0)
+ puma_worker_killer (0.1.0)
+ get_process_mem (~> 0.2)
+ puma (>= 2.7, < 4)
+ pyu-ruby-sasl (0.0.3.3)
+ rack (1.6.11)
+ rack-accept (0.4.5)
+ rack (>= 0.4)
+ rack-attack (4.4.1)
+ rack
+ rack-cors (1.0.2)
+ rack-oauth2 (1.2.3)
+ activesupport (>= 2.3)
+ attr_required (>= 0.0.5)
+ httpclient (>= 2.4)
+ multi_json (>= 1.3.6)
+ rack (>= 1.1)
+ rack-protection (2.0.4)
+ rack
+ rack-proxy (0.6.0)
+ rack
+ rack-test (0.6.3)
+ rack (>= 1.0)
+ rails (4.2.10)
+ actionmailer (= 4.2.10)
+ actionpack (= 4.2.10)
+ actionview (= 4.2.10)
+ activejob (= 4.2.10)
+ activemodel (= 4.2.10)
+ activerecord (= 4.2.10)
+ activesupport (= 4.2.10)
+ bundler (>= 1.3.0, < 2.0)
+ railties (= 4.2.10)
+ sprockets-rails
+ rails-deprecated_sanitizer (1.0.3)
+ activesupport (>= 4.2.0.alpha)
+ rails-dom-testing (1.0.9)
+ activesupport (>= 4.2.0, < 5.0)
+ nokogiri (~> 1.6)
+ rails-deprecated_sanitizer (>= 1.0.1)
+ rails-html-sanitizer (1.0.4)
+ loofah (~> 2.2, >= 2.2.2)
+ rails-i18n (4.0.9)
+ i18n (~> 0.7)
+ railties (~> 4.0)
+ railties (4.2.10)
+ actionpack (= 4.2.10)
+ activesupport (= 4.2.10)
+ rake (>= 0.8.7)
+ thor (>= 0.18.1, < 2.0)
+ rainbow (3.0.0)
+ raindrops (0.18.0)
+ rake (12.3.1)
+ rb-fsevent (0.10.2)
+ rb-inotify (0.9.10)
+ ffi (>= 0.5.0, < 2)
+ rblineprof (0.3.6)
+ debugger-ruby_core_source (~> 1.3)
+ rbtrace (0.4.10)
+ ffi (>= 1.0.6)
+ msgpack (>= 0.4.3)
+ trollop (>= 1.16.2)
+ rdoc (6.0.4)
+ re2 (1.1.1)
+ recaptcha (3.0.0)
+ json
+ recursive-open-struct (1.1.0)
+ redcarpet (3.4.0)
+ redis (3.3.5)
+ redis-actionpack (5.0.2)
+ actionpack (>= 4.0, < 6)
+ redis-rack (>= 1, < 3)
+ redis-store (>= 1.1.0, < 2)
+ redis-activesupport (5.0.4)
+ activesupport (>= 3, < 6)
+ redis-store (>= 1.3, < 2)
+ redis-namespace (1.6.0)
+ redis (>= 3.0.4)
+ redis-rack (2.0.4)
+ rack (>= 1.5, < 3)
+ redis-store (>= 1.2, < 2)
+ redis-rails (5.0.2)
+ redis-actionpack (>= 5.0, < 6)
+ redis-activesupport (>= 5.0, < 6)
+ redis-store (>= 1.2, < 2)
+ redis-store (1.6.0)
+ redis (>= 2.2, < 5)
+ regexp_parser (0.5.0)
+ representable (3.0.4)
+ declarative (< 0.1.0)
+ declarative-option (< 0.2.0)
+ uber (< 0.2.0)
+ request_store (1.3.1)
+ responders (2.4.0)
+ actionpack (>= 4.2.0, < 5.3)
+ railties (>= 4.2.0, < 5.3)
+ rest-client (2.0.2)
+ http-cookie (>= 1.0.2, < 2.0)
+ mime-types (>= 1.16, < 4.0)
+ netrc (~> 0.8)
+ retriable (3.1.2)
+ rinku (2.0.0)
+ rotp (2.1.2)
+ rouge (3.3.0)
+ rqrcode (0.7.0)
+ chunky_png
+ rqrcode-rails3 (0.1.7)
+ rqrcode (>= 0.4.2)
+ rspec (3.7.0)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-core (3.7.1)
+ rspec-support (~> 3.7.0)
+ rspec-expectations (3.7.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.7.0)
+ rspec-mocks (3.7.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.7.0)
+ rspec-parameterized (0.4.0)
+ binding_of_caller
+ parser
+ proc_to_ast
+ rspec (>= 2.13, < 4)
+ unparser
+ rspec-rails (3.7.2)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ railties (>= 3.0)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-support (~> 3.7.0)
+ rspec-retry (0.4.5)
+ rspec-core
+ rspec-set (0.1.3)
+ rspec-support (3.7.1)
+ rspec_junit_formatter (0.2.3)
+ builder (< 4)
+ rspec-core (>= 2, < 4, != 2.12.0)
+ rspec_profiling (0.0.5)
+ activerecord
+ pg
+ rails
+ sqlite3
+ rubocop (0.54.0)
+ parallel (~> 1.10)
+ parser (>= 2.5)
+ powerpack (~> 0.1)
+ rainbow (>= 2.2.2, < 4.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (~> 1.0, >= 1.0.1)
+ rubocop-gitlab-security (0.1.1)
+ rubocop (>= 0.51)
+ rubocop-rspec (1.22.2)
+ rubocop (>= 0.52.1)
+ ruby-enum (0.7.2)
+ i18n
+ ruby-fogbugz (0.2.1)
+ crack (~> 0.4)
+ ruby-prof (0.17.0)
+ ruby-progressbar (1.9.0)
+ ruby-saml (1.7.2)
+ nokogiri (>= 1.5.10)
+ ruby_parser (3.11.0)
+ sexp_processor (~> 4.9)
+ rubyntlm (0.6.2)
+ rubypants (0.2.0)
+ rubyzip (1.2.2)
+ rufus-scheduler (3.4.0)
+ et-orbi (~> 1.0)
+ rugged (0.27.5)
+ safe_yaml (1.0.4)
+ sanitize (4.6.6)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.4.4)
+ nokogumbo (~> 1.4)
+ sass (3.5.5)
+ sass-listen (~> 4.0.0)
+ sass-listen (4.0.0)
+ rb-fsevent (~> 0.9, >= 0.9.4)
+ rb-inotify (~> 0.9, >= 0.9.7)
+ sass-rails (5.0.6)
+ railties (>= 4.0.0, < 6)
+ sass (~> 3.1)
+ sprockets (>= 2.8, < 4.0)
+ sprockets-rails (>= 2.0, < 4.0)
+ tilt (>= 1.1, < 3)
+ sawyer (0.8.1)
+ addressable (>= 2.3.5, < 2.6)
+ faraday (~> 0.8, < 1.0)
+ scss_lint (0.56.0)
+ rake (>= 0.9, < 13)
+ sass (~> 3.5.3)
+ seed-fu (2.3.7)
+ activerecord (>= 3.1)
+ activesupport (>= 3.1)
+ select2-rails (3.5.9.3)
+ thor (~> 0.14)
+ selenium-webdriver (3.12.0)
+ childprocess (~> 0.5)
+ rubyzip (~> 1.2)
+ sentry-raven (2.7.2)
+ faraday (>= 0.7.6, < 1.0)
+ settingslogic (2.0.9)
+ sexp_processor (4.11.0)
+ sham_rack (1.3.6)
+ rack
+ shoulda-matchers (3.1.2)
+ activesupport (>= 4.0.0)
+ sidekiq (5.2.3)
+ connection_pool (~> 2.2, >= 2.2.2)
+ rack-protection (>= 1.5.0)
+ redis (>= 3.3.5, < 5)
+ sidekiq-cron (0.6.0)
+ rufus-scheduler (>= 3.3.0)
+ sidekiq (>= 4.2.1)
+ signet (0.11.0)
+ addressable (~> 2.3)
+ faraday (~> 0.9)
+ jwt (>= 1.5, < 3.0)
+ multi_json (~> 1.10)
+ simple_po_parser (1.1.2)
+ simplecov (0.14.1)
+ docile (~> 1.1.0)
+ json (>= 1.8, < 3)
+ simplecov-html (~> 0.10.0)
+ simplecov-html (0.10.0)
+ slack-notifier (1.5.1)
+ spring (2.0.2)
+ activesupport (>= 4.2)
+ spring-commands-rspec (1.0.4)
+ spring (>= 0.9.1)
+ sprockets (3.7.2)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
+ sprockets-rails (3.2.1)
+ actionpack (>= 4.0)
+ activesupport (>= 4.0)
+ sprockets (>= 3.0.0)
+ sqlite3 (1.3.13)
+ sshkey (1.9.0)
+ stackprof (0.2.10)
+ state_machines (0.5.0)
+ state_machines-activemodel (0.5.1)
+ activemodel (>= 4.1, < 6.0)
+ state_machines (>= 0.5.0)
+ state_machines-activerecord (0.5.1)
+ activerecord (>= 4.1, < 6.0)
+ state_machines-activemodel (>= 0.5.0)
+ sys-filesystem (1.1.6)
+ ffi
+ sysexits (1.2.0)
+ temple (0.8.0)
+ test-prof (0.2.5)
+ test_after_commit (1.1.0)
+ activerecord (>= 3.2)
+ text (1.3.1)
+ thin (1.7.2)
+ daemons (~> 1.0, >= 1.0.9)
+ eventmachine (~> 1.0, >= 1.0.4)
+ rack (>= 1, < 3)
+ thor (0.19.4)
+ thread_safe (0.3.6)
+ tilt (2.0.8)
+ timecop (0.8.1)
+ timfel-krb5-auth (0.8.3)
+ toml (0.2.0)
+ parslet (~> 1.8.0)
+ toml-rb (1.0.0)
+ citrus (~> 3.0, > 3.0)
+ trollop (2.1.3)
+ truncato (0.7.10)
+ htmlentities (~> 4.3.1)
+ nokogiri (~> 1.8.0, >= 1.7.0)
+ tzinfo (1.2.5)
+ thread_safe (~> 0.1)
+ u2f (0.2.1)
+ uber (0.1.0)
+ uglifier (2.7.2)
+ execjs (>= 0.3.0)
+ json (>= 1.8.0)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.7.5)
+ unicode-display_width (1.3.2)
+ unicorn (5.1.0)
+ kgio (~> 2.6)
+ raindrops (~> 0.7)
+ unicorn-worker-killer (0.4.4)
+ get_process_mem (~> 0)
+ unicorn (>= 4, < 6)
+ uniform_notifier (1.10.0)
+ unparser (0.2.7)
+ abstract_type (~> 0.0.7)
+ adamantium (~> 0.2.0)
+ concord (~> 0.1.5)
+ diff-lcs (~> 1.3)
+ equalizer (~> 0.0.9)
+ parser (>= 2.3.1.2, < 2.6)
+ procto (~> 0.0.2)
+ validates_hostname (1.0.6)
+ activerecord (>= 3.0)
+ activesupport (>= 3.0)
+ version_sorter (2.1.0)
+ virtus (1.0.5)
+ axiom-types (~> 0.1)
+ coercible (~> 1.0)
+ descendants_tracker (~> 0.0, >= 0.0.3)
+ equalizer (~> 0.0, >= 0.0.9)
+ vmstat (2.3.0)
+ warden (1.2.7)
+ rack (>= 1.0)
+ webmock (2.3.2)
+ addressable (>= 2.3.6)
+ crack (>= 0.3.2)
+ hashdiff
+ webpack-rails (0.9.11)
+ railties (>= 3.2.0)
+ wikicloth (0.8.1)
+ builder
+ expression_parser
+ rinku
+ with_env (1.1.0)
+ xml-simple (1.1.5)
+ xpath (2.1.0)
+ nokogiri (~> 1.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ RedCloth (~> 4.3.2)
+ ace-rails-ap (~> 4.1.0)
+ activerecord_sane_schema_dumper (= 0.2)
+ acts-as-taggable-on (~> 5.0)
+ addressable (~> 2.5.2)
+ akismet (~> 2.0)
+ asana (~> 0.8.1)
+ asciidoctor (~> 1.5.8)
+ asciidoctor-plantuml (= 0.0.8)
+ attr_encrypted (~> 3.1.0)
+ awesome_print
+ babosa (~> 1.0.2)
+ base32 (~> 0.3.0)
+ batch-loader (~> 1.2.1)
+ bcrypt_pbkdf (~> 1.0)
+ benchmark-ips (~> 2.3.0)
+ better_errors (~> 2.1.0)
+ binding_of_caller (~> 0.8.0)
+ bootsnap (~> 1.3)
+ bootstrap_form (~> 2.7.0)
+ brakeman (~> 4.2)
+ browser (~> 2.5)
+ bullet (~> 5.5.0)
+ bundler-audit (~> 0.5.0)
+ capybara (~> 2.15)
+ capybara-screenshot (~> 1.0.0)
+ carrierwave (= 1.2.3)
+ charlock_holmes (~> 0.7.5)
+ chronic (~> 0.10.2)
+ chronic_duration (~> 0.10.6)
+ commonmarker (~> 0.17)
+ concurrent-ruby (~> 1.1)
+ connection_pool (~> 2.0)
+ creole (~> 0.5.0)
+ database_cleaner (~> 1.5.0)
+ deckar01-task_list (= 2.0.0)
+ default_value_for (~> 3.0.0)
+ device_detector
+ devise (~> 4.4)
+ devise-two-factor (~> 3.0.0)
+ diffy (~> 3.1.0)
+ discordrb-webhooks-blackst0ne (~> 3.3)
+ doorkeeper (~> 4.3)
+ doorkeeper-openid_connect (~> 1.5)
+ ed25519 (~> 1.2)
+ email_reply_trimmer (~> 0.1)
+ email_spec (~> 2.2.0)
+ escape_utils (~> 1.1)
+ factory_bot_rails (~> 4.8.2)
+ faraday (~> 0.12)
+ fast_blank
+ ffaker (~> 2.10)
+ flipper (~> 0.13.0)
+ flipper-active_record (~> 0.13.0)
+ flipper-active_support_cache_store (~> 0.13.0)
+ flowdock (~> 0.7)
+ fog-aliyun (~> 0.2.0)
+ fog-aws (~> 2.0.1)
+ fog-core (~> 1.44)
+ fog-google (~> 1.7.1)
+ fog-local (~> 0.3)
+ fog-openstack (~> 0.1)
+ fog-rackspace (~> 0.1.1)
+ font-awesome-rails (~> 4.7)
+ foreman (~> 0.84.0)
+ fuubar (~> 2.2.0)
+ gemojione (~> 3.3)
+ gettext (~> 3.2.2)
+ gettext_i18n_rails (~> 1.8.0)
+ gettext_i18n_rails_js (~> 1.3)
+ gitaly-proto (~> 0.123.0)
+ github-markup (~> 1.7.0)
+ gitlab-markup (~> 1.6.5)
+ gitlab-sidekiq-fetcher
+ gitlab-styles (~> 2.4)
+ gitlab_omniauth-ldap (~> 2.0.4)
+ gon (~> 6.2)
+ google-api-client (~> 0.23)
+ google-protobuf (~> 3.6)
+ gpgme
+ grape (~> 1.1)
+ grape-entity (~> 0.7.1)
+ grape-path-helpers (~> 1.0)
+ grape_logging (~> 1.7)
+ graphiql-rails (~> 1.4.10)
+ graphql (~> 1.8.0)
+ grpc (~> 1.15.0)
+ haml_lint (~> 0.28.0)
+ hamlit (~> 2.8.8)
+ hangouts-chat (~> 0.0.5)
+ hashie-forbidden_attributes
+ health_check (~> 2.6.0)
+ hipchat (~> 1.5.0)
+ html-pipeline (~> 2.8)
+ html2text
+ httparty (~> 0.13.3)
+ icalendar
+ influxdb (~> 0.2)
+ jira-ruby (~> 1.4)
+ jquery-atwho-rails (~> 1.3.2)
+ js_regex (~> 2.2.1)
+ json-schema (~> 2.8.0)
+ jwt (~> 1.5.6)
+ kaminari (~> 1.0)
+ knapsack (~> 1.17)
+ kubeclient (~> 3.1.0)
+ letter_opener_web (~> 1.3.0)
+ license_finder (~> 5.4)
+ licensee (~> 8.9)
+ lograge (~> 0.5)
+ loofah (~> 2.2)
+ mail_room (~> 0.9.1)
+ method_source (~> 0.8)
+ mini_magick
+ minitest (~> 5.7.0)
+ mysql2 (~> 0.4.10)
+ nakayoshi_fork (~> 0.0.4)
+ net-ldap
+ net-ssh (~> 5.0)
+ nokogiri (~> 1.8.2)
+ oauth2 (~> 1.4)
+ octokit (~> 4.9)
+ omniauth (~> 1.8)
+ omniauth-auth0 (~> 2.0.0)
+ omniauth-authentiq (~> 0.3.3)
+ omniauth-azure-oauth2 (~> 0.0.9)
+ omniauth-cas3 (~> 1.1.4)
+ omniauth-facebook (~> 4.0.0)
+ omniauth-github (~> 1.3)
+ omniauth-gitlab (~> 1.0.2)
+ omniauth-google-oauth2 (~> 0.5.3)
+ omniauth-kerberos (~> 0.3.0)
+ omniauth-oauth2-generic (~> 0.2.2)
+ omniauth-saml (~> 1.10)
+ omniauth-shibboleth (~> 1.3.0)
+ omniauth-twitter (~> 1.4)
+ omniauth_crowd (~> 2.2.0)
+ org-ruby (~> 0.9.12)
+ peek (~> 1.0.1)
+ peek-gc (~> 0.0.2)
+ peek-mysql2 (~> 1.1.0)
+ peek-pg (~> 1.3.0)
+ peek-rblineprof (~> 0.2.0)
+ peek-redis (~> 1.2.0)
+ pg (~> 0.18.2)
+ premailer-rails (~> 1.9.7)
+ prometheus-client-mmap (~> 0.9.4)
+ pry-byebug (~> 3.4.1)
+ pry-rails (~> 0.3.4)
+ puma (~> 3.12)
+ puma_worker_killer
+ rack-attack (~> 4.4.1)
+ rack-cors (~> 1.0.0)
+ rack-oauth2 (~> 1.2.1)
+ rack-proxy (~> 0.6.0)
+ rails (= 4.2.10)
+ rails-deprecated_sanitizer (~> 1.0.3)
+ rails-i18n (~> 4.0.9)
+ rainbow (~> 3.0)
+ raindrops (~> 0.18)
+ rblineprof (~> 0.3.6)
+ rbtrace (~> 0.4)
+ rdoc (~> 6.0)
+ re2 (~> 1.1.1)
+ recaptcha (~> 3.0)
+ redcarpet (~> 3.4)
+ redis (~> 3.2)
+ redis-namespace (~> 1.6.0)
+ redis-rails (~> 5.0.2)
+ request_store (~> 1.3)
+ responders (~> 2.0)
+ rouge (~> 3.1)
+ rqrcode-rails3 (~> 0.1.7)
+ rspec-parameterized
+ rspec-rails (~> 3.7.0)
+ rspec-retry (~> 0.4.5)
+ rspec-set (~> 0.1.3)
+ rspec_junit_formatter
+ rspec_profiling (~> 0.0.5)
+ rubocop (~> 0.54.0)
+ rubocop-rspec (~> 1.22.1)
+ ruby-fogbugz (~> 0.2.1)
+ ruby-prof (~> 0.17.0)
+ ruby-progressbar
+ ruby_parser (~> 3.8)
+ rufus-scheduler (~> 3.4)
+ rugged (~> 0.27)
+ sanitize (~> 4.6)
+ sass-rails (~> 5.0.6)
+ scss_lint (~> 0.56.0)
+ seed-fu (~> 2.3.7)
+ select2-rails (~> 3.5.9)
+ selenium-webdriver (~> 3.12)
+ sentry-raven (~> 2.7)
+ settingslogic (~> 2.0.9)
+ sham_rack (~> 1.3.6)
+ shoulda-matchers (~> 3.1.2)
+ sidekiq (~> 5.2.1)
+ sidekiq-cron (~> 0.6.0)
+ simple_po_parser (~> 1.1.2)
+ simplecov (~> 0.14.0)
+ slack-notifier (~> 1.5.1)
+ spring (~> 2.0.0)
+ spring-commands-rspec (~> 1.0.4)
+ sprockets (~> 3.7.0)
+ sshkey (~> 1.9.0)
+ stackprof (~> 0.2.10)
+ state_machines-activerecord (~> 0.5.1)
+ sys-filesystem (~> 1.1.6)
+ test-prof (~> 0.2.5)
+ test_after_commit (~> 1.1)
+ thin (~> 1.7.0)
+ timecop (~> 0.8.0)
+ toml-rb (~> 1.0.0)
+ truncato (~> 0.7.9)
+ u2f (~> 0.2.1)
+ uglifier (~> 2.7.2)
+ unf (~> 0.1.4)
+ unicorn (~> 5.1.0)
+ unicorn-worker-killer (~> 0.4.4)
+ validates_hostname (~> 1.0.6)
+ version_sorter (~> 2.1.0)
+ virtus (~> 1.0.1)
+ vmstat (~> 2.3.0)
+ webmock (~> 2.3.2)
+ webpack-rails (~> 0.9.10)
+ wikicloth (= 0.8.1)
+
+BUNDLED WITH
+ 1.17.1
diff --git a/Gemfile.rails5 b/Gemfile.rails5
deleted file mode 100644
index 2b526b19ba0..00000000000
--- a/Gemfile.rails5
+++ /dev/null
@@ -1,7 +0,0 @@
-# BUNDLE_GEMFILE=Gemfile.rails5 bundle install
-
-ENV["RAILS5"] = "true"
-
-gemfile = File.expand_path("../Gemfile", __FILE__)
-
-eval(File.read(gemfile), nil, gemfile)
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
deleted file mode 100644
index 39305927c0f..00000000000
--- a/Gemfile.rails5.lock
+++ /dev/null
@@ -1,1220 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- RedCloth (4.3.2)
- abstract_type (0.0.7)
- ace-rails-ap (4.1.2)
- actioncable (5.0.7)
- actionpack (= 5.0.7)
- nio4r (>= 1.2, < 3.0)
- websocket-driver (~> 0.6.1)
- actionmailer (5.0.7)
- actionpack (= 5.0.7)
- actionview (= 5.0.7)
- activejob (= 5.0.7)
- mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 2.0)
- actionpack (5.0.7)
- actionview (= 5.0.7)
- activesupport (= 5.0.7)
- rack (~> 2.0)
- rack-test (~> 0.6.3)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (5.0.7)
- activesupport (= 5.0.7)
- builder (~> 3.1)
- erubis (~> 2.7.0)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (5.0.7)
- activesupport (= 5.0.7)
- globalid (>= 0.3.6)
- activemodel (5.0.7)
- activesupport (= 5.0.7)
- activerecord (5.0.7)
- activemodel (= 5.0.7)
- activesupport (= 5.0.7)
- arel (~> 7.0)
- activerecord_sane_schema_dumper (1.0)
- rails (>= 5, < 6)
- activesupport (5.0.7)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- acts-as-taggable-on (5.0.0)
- activerecord (>= 4.2.8)
- adamantium (0.2.0)
- ice_nine (~> 0.11.0)
- memoizable (~> 0.4.0)
- addressable (2.5.2)
- public_suffix (>= 2.0.2, < 4.0)
- aes_key_wrap (1.0.1)
- akismet (2.0.0)
- arel (7.1.4)
- asana (0.6.0)
- faraday (~> 0.9)
- faraday_middleware (~> 0.9)
- faraday_middleware-multi_json (~> 0.0)
- oauth2 (~> 1.0)
- asciidoctor (1.5.6.2)
- asciidoctor-plantuml (0.0.8)
- asciidoctor (~> 1.5)
- asset_sync (2.4.0)
- activemodel (>= 4.1.0)
- fog-core
- mime-types (>= 2.99)
- unf
- ast (2.4.0)
- atomic (1.1.99)
- attr_encrypted (3.1.0)
- encryptor (~> 3.0.0)
- attr_required (1.0.0)
- awesome_print (1.8.0)
- axiom-types (0.1.1)
- descendants_tracker (~> 0.0.4)
- ice_nine (~> 0.11.0)
- thread_safe (~> 0.3, >= 0.3.1)
- babosa (1.0.2)
- base32 (0.3.2)
- batch-loader (1.2.1)
- bcrypt (3.1.12)
- bcrypt_pbkdf (1.0.0)
- benchmark-ips (2.3.0)
- better_errors (2.1.1)
- coderay (>= 1.0.0)
- erubis (>= 2.6.6)
- rack (>= 0.9.0)
- bindata (2.4.3)
- binding_of_caller (0.7.2)
- debug_inspector (>= 0.0.1)
- blankslate (2.1.2.4)
- bootsnap (1.3.1)
- msgpack (~> 1.0)
- bootstrap_form (2.7.0)
- brakeman (4.2.1)
- browser (2.2.0)
- builder (3.2.3)
- bullet (5.5.1)
- activesupport (>= 3.0.0)
- uniform_notifier (~> 1.10.0)
- bundler-audit (0.5.0)
- bundler (~> 1.2)
- thor (~> 0.18)
- byebug (9.0.6)
- capybara (2.15.1)
- addressable
- mini_mime (>= 0.1.3)
- nokogiri (>= 1.3.3)
- rack (>= 1.0.0)
- rack-test (>= 0.5.4)
- xpath (~> 2.0)
- capybara-screenshot (1.0.14)
- capybara (>= 1.0, < 3)
- launchy
- carrierwave (1.2.3)
- activemodel (>= 4.0.0)
- activesupport (>= 4.0.0)
- mime-types (>= 1.16)
- cause (0.1)
- charlock_holmes (0.7.6)
- childprocess (0.9.0)
- ffi (~> 1.0, >= 1.0.11)
- chronic (0.10.2)
- chronic_duration (0.10.6)
- numerizer (~> 0.1.1)
- chunky_png (1.3.5)
- citrus (3.0.2)
- coderay (1.1.1)
- coercible (1.0.0)
- descendants_tracker (~> 0.0.1)
- commonmarker (0.17.8)
- ruby-enum (~> 0.5)
- concord (0.1.5)
- adamantium (~> 0.2.0)
- equalizer (~> 0.0.9)
- concurrent-ruby (1.0.5)
- concurrent-ruby-ext (1.0.5)
- concurrent-ruby (= 1.0.5)
- connection_pool (2.2.1)
- crack (0.4.3)
- safe_yaml (~> 1.0.0)
- crass (1.0.4)
- creole (0.5.0)
- css_parser (1.5.0)
- addressable
- daemons (1.2.3)
- database_cleaner (1.5.3)
- debug_inspector (0.0.2)
- debugger-ruby_core_source (1.3.8)
- deckar01-task_list (2.0.0)
- html-pipeline
- declarative (0.0.10)
- declarative-option (0.1.0)
- default_value_for (3.0.5)
- activerecord (>= 3.2.0, < 5.2)
- descendants_tracker (0.0.4)
- thread_safe (~> 0.3, >= 0.3.1)
- device_detector (1.0.0)
- devise (4.4.3)
- bcrypt (~> 3.0)
- orm_adapter (~> 0.1)
- railties (>= 4.1.0, < 6.0)
- responders
- warden (~> 1.2.3)
- devise-two-factor (3.0.0)
- activesupport
- attr_encrypted (>= 1.3, < 4, != 2)
- devise (~> 4.0)
- railties
- rotp (~> 2.0)
- diff-lcs (1.3)
- diffy (3.1.0)
- docile (1.1.5)
- domain_name (0.5.20180417)
- unf (>= 0.0.5, < 1.0.0)
- doorkeeper (4.3.2)
- railties (>= 4.2)
- doorkeeper-openid_connect (1.5.0)
- doorkeeper (~> 4.3)
- json-jwt (~> 1.6)
- dropzonejs-rails (0.7.2)
- rails (> 3.1)
- ed25519 (1.2.4)
- email_reply_trimmer (0.1.6)
- email_spec (2.2.0)
- htmlentities (~> 4.3.3)
- launchy (~> 2.1)
- mail (~> 2.7)
- encryptor (3.0.0)
- equalizer (0.0.11)
- erubis (2.7.0)
- escape_utils (1.1.1)
- et-orbi (1.0.3)
- tzinfo
- eventmachine (1.0.8)
- excon (0.62.0)
- execjs (2.6.0)
- expression_parser (0.9.0)
- factory_bot (4.8.2)
- activesupport (>= 3.0.0)
- factory_bot_rails (4.8.2)
- factory_bot (~> 4.8.2)
- railties (>= 3.0.0)
- faraday (0.12.2)
- multipart-post (>= 1.2, < 3)
- faraday_middleware (0.12.2)
- faraday (>= 0.7.4, < 1.0)
- faraday_middleware-multi_json (0.0.6)
- faraday_middleware
- multi_json
- fast_blank (1.0.0)
- fast_gettext (1.6.0)
- ffaker (2.4.0)
- ffi (1.9.18)
- flay (2.10.0)
- erubis (~> 2.7.0)
- path_expander (~> 1.0)
- ruby_parser (~> 3.0)
- sexp_processor (~> 4.0)
- flipper (0.13.0)
- flipper-active_record (0.13.0)
- activerecord (>= 3.2, < 6)
- flipper (~> 0.13.0)
- flipper-active_support_cache_store (0.13.0)
- activesupport (>= 3.2, < 6)
- flipper (~> 0.13.0)
- flowdock (0.7.1)
- httparty (~> 0.7)
- multi_json
- fog-aliyun (0.2.0)
- fog-core (~> 1.27)
- fog-json (~> 1.0)
- ipaddress (~> 0.8)
- xml-simple (~> 1.1)
- fog-aws (2.0.1)
- fog-core (~> 1.38)
- fog-json (~> 1.0)
- fog-xml (~> 0.1)
- ipaddress (~> 0.8)
- fog-core (1.45.0)
- builder
- excon (~> 0.58)
- formatador (~> 0.2)
- fog-google (1.3.3)
- fog-core
- fog-json
- fog-xml
- google-api-client (~> 0.19.1)
- fog-json (1.0.2)
- fog-core (~> 1.0)
- multi_json (~> 1.10)
- fog-local (0.3.1)
- fog-core (~> 1.27)
- fog-openstack (0.1.21)
- fog-core (>= 1.40)
- fog-json (>= 1.0)
- ipaddress (>= 0.8)
- fog-rackspace (0.1.1)
- fog-core (>= 1.35)
- fog-json (>= 1.0)
- fog-xml (>= 0.1)
- ipaddress (>= 0.8)
- fog-xml (0.1.3)
- fog-core
- nokogiri (>= 1.5.11, < 2.0.0)
- font-awesome-rails (4.7.0.1)
- railties (>= 3.2, < 5.1)
- foreman (0.84.0)
- thor (~> 0.19.1)
- formatador (0.2.5)
- fuubar (2.2.0)
- rspec-core (~> 3.0)
- ruby-progressbar (~> 1.4)
- gemnasium-gitlab-service (0.2.6)
- rugged (~> 0.21)
- gemojione (3.3.0)
- json
- get_process_mem (0.2.0)
- gettext (3.2.9)
- locale (>= 2.0.5)
- text (>= 1.3.0)
- gettext_i18n_rails (1.8.0)
- fast_gettext (>= 0.9.0)
- gettext_i18n_rails_js (1.3.0)
- gettext (>= 3.0.2)
- gettext_i18n_rails (>= 0.7.1)
- po_to_json (>= 1.0.0)
- rails (>= 3.2.0)
- gitaly-proto (0.112.0)
- google-protobuf (~> 3.1)
- grpc (~> 1.10)
- github-linguist (5.3.3)
- charlock_holmes (~> 0.7.5)
- escape_utils (~> 1.1.0)
- mime-types (>= 1.19)
- rugged (>= 0.25.1)
- github-markup (1.7.0)
- gitlab-flowdock-git-hook (1.0.1)
- flowdock (~> 0.7)
- gitlab-grit (>= 2.4.1)
- multi_json
- gitlab-gollum-lib (4.2.7.5)
- gemojione (~> 3.2)
- github-markup (~> 1.6)
- gollum-grit_adapter (~> 1.0)
- nokogiri (>= 1.6.1, < 2.0)
- rouge (~> 3.1)
- sanitize (~> 4.6.4)
- stringex (~> 2.6)
- gitlab-gollum-rugged_adapter (0.4.4.1)
- mime-types (>= 1.15)
- rugged (~> 0.25)
- gitlab-grit (2.8.2)
- charlock_holmes (~> 0.6)
- diff-lcs (~> 1.1)
- mime-types (>= 1.16)
- posix-spawn (~> 0.3)
- gitlab-markup (1.6.4)
- gitlab-styles (2.4.1)
- rubocop (~> 0.54.0)
- rubocop-gitlab-security (~> 0.1.0)
- rubocop-rspec (~> 1.19)
- gitlab_omniauth-ldap (2.0.4)
- net-ldap (~> 0.16)
- omniauth (~> 1.3)
- pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
- rubyntlm (~> 0.5)
- globalid (0.4.1)
- activesupport (>= 4.2.0)
- gollum-grit_adapter (1.0.1)
- gitlab-grit (~> 2.7, >= 2.7.1)
- gon (6.2.0)
- actionpack (>= 3.0)
- multi_json
- request_store (>= 1.0)
- google-api-client (0.19.8)
- addressable (~> 2.5, >= 2.5.1)
- googleauth (>= 0.5, < 0.7.0)
- httpclient (>= 2.8.1, < 3.0)
- mime-types (~> 3.0)
- representable (~> 3.0)
- retriable (>= 2.0, < 4.0)
- google-protobuf (3.5.1)
- googleapis-common-protos-types (1.0.1)
- google-protobuf (~> 3.0)
- googleauth (0.6.2)
- faraday (~> 0.12)
- jwt (>= 1.4, < 3.0)
- logging (~> 2.0)
- memoist (~> 0.12)
- multi_json (~> 1.11)
- os (~> 0.9)
- signet (~> 0.7)
- gpgme (2.0.13)
- mini_portile2 (~> 2.1)
- grape (1.0.3)
- activesupport
- builder
- mustermann-grape (~> 1.0.0)
- rack (>= 1.3.0)
- rack-accept
- virtus (>= 1.0.0)
- grape-entity (0.7.1)
- activesupport (>= 4.0)
- multi_json (>= 1.3.2)
- grape-path-helpers (1.0.6)
- activesupport (>= 4, < 5.1)
- grape (~> 1.0)
- rake (~> 12)
- grape_logging (1.7.0)
- grape
- graphiql-rails (1.4.10)
- railties
- sprockets-rails
- graphql (1.8.1)
- grpc (1.11.0)
- google-protobuf (~> 3.1)
- googleapis-common-protos-types (~> 1.0.0)
- googleauth (>= 0.5.1, < 0.7)
- haml (5.0.4)
- temple (>= 0.8.0)
- tilt
- haml_lint (0.26.0)
- haml (>= 4.0, < 5.1)
- rainbow
- rake (>= 10, < 13)
- rubocop (>= 0.49.0)
- sysexits (~> 1.1)
- hamlit (2.8.8)
- temple (>= 0.8.0)
- thor
- tilt
- hangouts-chat (0.0.5)
- hashdiff (0.3.4)
- hashie (3.5.7)
- hashie-forbidden_attributes (0.1.1)
- hashie (>= 3.0)
- health_check (2.6.0)
- rails (>= 4.0)
- hipchat (1.5.2)
- httparty
- mimemagic
- html-pipeline (2.8.4)
- activesupport (>= 2)
- nokogiri (>= 1.4)
- html2text (0.2.0)
- nokogiri (~> 1.6)
- htmlentities (4.3.4)
- http (2.2.2)
- addressable (~> 2.3)
- http-cookie (~> 1.0)
- http-form_data (~> 1.0.1)
- http_parser.rb (~> 0.6.0)
- http-cookie (1.0.3)
- domain_name (~> 0.5)
- http-form_data (1.0.3)
- http_parser.rb (0.6.0)
- httparty (0.13.7)
- json (~> 1.8)
- multi_xml (>= 0.5.2)
- httpclient (2.8.3)
- i18n (1.0.1)
- concurrent-ruby (~> 1.0)
- icalendar (2.4.1)
- ice_nine (0.11.2)
- influxdb (0.2.3)
- cause
- json
- ipaddress (0.8.3)
- jira-ruby (1.4.1)
- activesupport
- multipart-post
- oauth (~> 0.5, >= 0.5.0)
- jquery-atwho-rails (1.3.2)
- json (1.8.6)
- json-jwt (1.9.4)
- activesupport
- aes_key_wrap
- bindata
- json-schema (2.8.0)
- addressable (>= 2.4)
- jwt (1.5.6)
- kaminari (1.0.1)
- activesupport (>= 4.1.0)
- kaminari-actionview (= 1.0.1)
- kaminari-activerecord (= 1.0.1)
- kaminari-core (= 1.0.1)
- kaminari-actionview (1.0.1)
- actionview
- kaminari-core (= 1.0.1)
- kaminari-activerecord (1.0.1)
- activerecord
- kaminari-core (= 1.0.1)
- kaminari-core (1.0.1)
- kgio (2.10.0)
- knapsack (1.16.0)
- rake
- kubeclient (3.1.0)
- http (~> 2.2.2)
- recursive-open-struct (~> 1.0, >= 1.0.4)
- rest-client (~> 2.0)
- launchy (2.4.3)
- addressable (~> 2.3)
- letter_opener (1.4.1)
- launchy (~> 2.2)
- letter_opener_web (1.3.0)
- actionmailer (>= 3.2)
- letter_opener (~> 1.0)
- railties (>= 3.2)
- license_finder (3.1.1)
- bundler
- httparty
- rubyzip
- thor
- toml (= 0.1.2)
- with_env (> 1.0)
- xml-simple
- licensee (8.9.2)
- rugged (~> 0.24)
- little-plugger (1.1.4)
- locale (2.1.2)
- logging (2.2.2)
- little-plugger (~> 1.1)
- multi_json (~> 1.10)
- lograge (0.10.0)
- actionpack (>= 4)
- activesupport (>= 4)
- railties (>= 4)
- request_store (~> 1.0)
- loofah (2.2.2)
- crass (~> 1.0.2)
- nokogiri (>= 1.5.9)
- mail (2.7.0)
- mini_mime (>= 0.1.1)
- mail_room (0.9.1)
- memoist (0.16.0)
- memoizable (0.4.2)
- thread_safe (~> 0.3, >= 0.3.1)
- method_source (0.8.2)
- mime-types (3.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
- mimemagic (0.3.0)
- mini_magick (4.8.0)
- mini_mime (1.0.0)
- mini_portile2 (2.3.0)
- minitest (5.7.0)
- mousetrap-rails (1.4.6)
- msgpack (1.2.4)
- multi_json (1.13.1)
- multi_xml (0.6.0)
- multipart-post (2.0.0)
- mustermann (1.0.2)
- mustermann-grape (1.0.0)
- mustermann (~> 1.0.0)
- mysql2 (0.4.10)
- net-ldap (0.16.0)
- net-ssh (5.0.1)
- netrc (0.11.0)
- nio4r (2.3.1)
- nokogiri (1.8.4)
- mini_portile2 (~> 2.3.0)
- nokogumbo (1.5.0)
- nokogiri
- numerizer (0.1.1)
- oauth (0.5.4)
- oauth2 (1.4.0)
- faraday (>= 0.8, < 0.13)
- jwt (~> 1.0)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- octokit (4.9.0)
- sawyer (~> 0.8.0, >= 0.5.3)
- omniauth (1.8.1)
- hashie (>= 3.4.6, < 3.6.0)
- rack (>= 1.6.2, < 3)
- omniauth-auth0 (2.0.0)
- omniauth-oauth2 (~> 1.4)
- omniauth-authentiq (0.3.3)
- jwt (>= 1.5)
- omniauth-oauth2 (>= 1.5)
- omniauth-azure-oauth2 (0.0.9)
- jwt (~> 1.0)
- omniauth (~> 1.0)
- omniauth-oauth2 (~> 1.4)
- omniauth-cas3 (1.1.4)
- addressable (~> 2.3)
- nokogiri (~> 1.7, >= 1.7.1)
- omniauth (~> 1.2)
- omniauth-facebook (4.0.0)
- omniauth-oauth2 (~> 1.2)
- omniauth-github (1.3.0)
- omniauth (~> 1.5)
- omniauth-oauth2 (>= 1.4.0, < 2.0)
- omniauth-gitlab (1.0.3)
- omniauth (~> 1.0)
- omniauth-oauth2 (~> 1.0)
- omniauth-google-oauth2 (0.5.3)
- jwt (>= 1.5)
- omniauth (>= 1.1.1)
- omniauth-oauth2 (>= 1.5)
- omniauth-kerberos (0.3.0)
- omniauth-multipassword
- timfel-krb5-auth (~> 0.8)
- omniauth-multipassword (0.4.2)
- omniauth (~> 1.0)
- omniauth-oauth (1.1.0)
- oauth
- omniauth (~> 1.0)
- omniauth-oauth2 (1.5.0)
- oauth2 (~> 1.1)
- omniauth (~> 1.2)
- omniauth-oauth2-generic (0.2.2)
- omniauth-oauth2 (~> 1.0)
- omniauth-saml (1.10.0)
- omniauth (~> 1.3, >= 1.3.2)
- ruby-saml (~> 1.7)
- omniauth-shibboleth (1.3.0)
- omniauth (>= 1.0.0)
- omniauth-twitter (1.4.0)
- omniauth-oauth (~> 1.1)
- rack
- omniauth_crowd (2.2.3)
- activesupport
- nokogiri (>= 1.4.4)
- omniauth (~> 1.0)
- org-ruby (0.9.12)
- rubypants (~> 0.2)
- orm_adapter (0.5.0)
- os (0.9.6)
- parallel (1.12.1)
- parser (2.5.1.0)
- ast (~> 2.4.0)
- parslet (1.5.0)
- blankslate (~> 2.0)
- path_expander (1.0.2)
- peek (1.0.1)
- concurrent-ruby (>= 0.9.0)
- concurrent-ruby-ext (>= 0.9.0)
- railties (>= 4.0.0)
- peek-gc (0.0.2)
- peek
- peek-mysql2 (1.1.0)
- atomic (>= 1.0.0)
- mysql2
- peek
- peek-pg (1.3.0)
- concurrent-ruby
- concurrent-ruby-ext
- peek
- pg
- peek-rblineprof (0.2.0)
- peek
- rblineprof
- peek-redis (1.2.0)
- atomic (>= 1.0.0)
- peek
- redis
- peek-sidekiq (1.0.3)
- atomic (>= 1.0.0)
- peek
- sidekiq
- pg (0.18.4)
- po_to_json (1.0.1)
- json (>= 1.6.0)
- posix-spawn (0.3.13)
- powerpack (0.1.1)
- premailer (1.10.4)
- addressable
- css_parser (>= 1.4.10)
- htmlentities (>= 4.0.0)
- premailer-rails (1.9.7)
- actionmailer (>= 3, < 6)
- premailer (~> 1.7, >= 1.7.9)
- proc_to_ast (0.1.0)
- coderay
- parser
- unparser
- procto (0.0.3)
- prometheus-client-mmap (0.9.4)
- pry (0.10.4)
- coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- pry-byebug (3.4.2)
- byebug (~> 9.0)
- pry (~> 0.10)
- pry-rails (0.3.5)
- pry (>= 0.9.10)
- public_suffix (3.0.2)
- pyu-ruby-sasl (0.0.3.3)
- rack (2.0.5)
- rack-accept (0.4.5)
- rack (>= 0.4)
- rack-attack (4.4.1)
- rack
- rack-cors (1.0.2)
- rack-oauth2 (1.2.3)
- activesupport (>= 2.3)
- attr_required (>= 0.0.5)
- httpclient (>= 2.4)
- multi_json (>= 1.3.6)
- rack (>= 1.1)
- rack-protection (2.0.1)
- rack
- rack-proxy (0.6.0)
- rack
- rack-test (0.6.3)
- rack (>= 1.0)
- rails (5.0.7)
- actioncable (= 5.0.7)
- actionmailer (= 5.0.7)
- actionpack (= 5.0.7)
- actionview (= 5.0.7)
- activejob (= 5.0.7)
- activemodel (= 5.0.7)
- activerecord (= 5.0.7)
- activesupport (= 5.0.7)
- bundler (>= 1.3.0)
- railties (= 5.0.7)
- sprockets-rails (>= 2.0.0)
- rails-controller-testing (1.0.2)
- actionpack (~> 5.x, >= 5.0.1)
- actionview (~> 5.x, >= 5.0.1)
- activesupport (~> 5.x)
- rails-deprecated_sanitizer (1.0.3)
- activesupport (>= 4.2.0.alpha)
- rails-dom-testing (2.0.3)
- activesupport (>= 4.2.0)
- nokogiri (>= 1.6)
- rails-html-sanitizer (1.0.4)
- loofah (~> 2.2, >= 2.2.2)
- rails-i18n (5.1.1)
- i18n (>= 0.7, < 2)
- railties (>= 5.0, < 6)
- railties (5.0.7)
- actionpack (= 5.0.7)
- activesupport (= 5.0.7)
- method_source
- rake (>= 0.8.7)
- thor (>= 0.18.1, < 2.0)
- rainbow (2.2.2)
- rake
- raindrops (0.18.0)
- rake (12.3.1)
- rb-fsevent (0.10.2)
- rb-inotify (0.9.10)
- ffi (>= 0.5.0, < 2)
- rblineprof (0.3.6)
- debugger-ruby_core_source (~> 1.3)
- rbtrace (0.4.10)
- ffi (>= 1.0.6)
- msgpack (>= 0.4.3)
- trollop (>= 1.16.2)
- rdoc (6.0.4)
- re2 (1.1.1)
- recaptcha (3.0.0)
- json
- recursive-open-struct (1.1.0)
- redcarpet (3.4.0)
- redis (3.3.5)
- redis-actionpack (5.0.2)
- actionpack (>= 4.0, < 6)
- redis-rack (>= 1, < 3)
- redis-store (>= 1.1.0, < 2)
- redis-activesupport (5.0.4)
- activesupport (>= 3, < 6)
- redis-store (>= 1.3, < 2)
- redis-namespace (1.6.0)
- redis (>= 3.0.4)
- redis-rack (2.0.4)
- rack (>= 1.5, < 3)
- redis-store (>= 1.2, < 2)
- redis-rails (5.0.2)
- redis-actionpack (>= 5.0, < 6)
- redis-activesupport (>= 5.0, < 6)
- redis-store (>= 1.2, < 2)
- redis-store (1.4.1)
- redis (>= 2.2, < 5)
- representable (3.0.4)
- declarative (< 0.1.0)
- declarative-option (< 0.2.0)
- uber (< 0.2.0)
- request_store (1.3.1)
- responders (2.4.0)
- actionpack (>= 4.2.0, < 5.3)
- railties (>= 4.2.0, < 5.3)
- rest-client (2.0.2)
- http-cookie (>= 1.0.2, < 2.0)
- mime-types (>= 1.16, < 4.0)
- netrc (~> 0.8)
- retriable (3.1.1)
- rinku (2.0.0)
- rotp (2.1.2)
- rouge (3.2.0)
- rqrcode (0.7.0)
- chunky_png
- rqrcode-rails3 (0.1.7)
- rqrcode (>= 0.4.2)
- rspec (3.7.0)
- rspec-core (~> 3.7.0)
- rspec-expectations (~> 3.7.0)
- rspec-mocks (~> 3.7.0)
- rspec-core (3.7.1)
- rspec-support (~> 3.7.0)
- rspec-expectations (3.7.0)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.7.0)
- rspec-mocks (3.7.0)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.7.0)
- rspec-parameterized (0.4.0)
- binding_of_caller
- parser
- proc_to_ast
- rspec (>= 2.13, < 4)
- unparser
- rspec-rails (3.7.2)
- actionpack (>= 3.0)
- activesupport (>= 3.0)
- railties (>= 3.0)
- rspec-core (~> 3.7.0)
- rspec-expectations (~> 3.7.0)
- rspec-mocks (~> 3.7.0)
- rspec-support (~> 3.7.0)
- rspec-retry (0.4.5)
- rspec-core
- rspec-set (0.1.3)
- rspec-support (3.7.1)
- rspec_profiling (0.0.5)
- activerecord
- pg
- rails
- sqlite3
- rubocop (0.54.0)
- parallel (~> 1.10)
- parser (>= 2.5)
- powerpack (~> 0.1)
- rainbow (>= 2.2.2, < 4.0)
- ruby-progressbar (~> 1.7)
- unicode-display_width (~> 1.0, >= 1.0.1)
- rubocop-gitlab-security (0.1.1)
- rubocop (>= 0.51)
- rubocop-rspec (1.22.2)
- rubocop (>= 0.52.1)
- ruby-enum (0.7.2)
- i18n
- ruby-fogbugz (0.2.1)
- crack (~> 0.4)
- ruby-prof (0.17.0)
- ruby-progressbar (1.9.0)
- ruby-saml (1.7.2)
- nokogiri (>= 1.5.10)
- ruby_parser (3.9.0)
- sexp_processor (~> 4.1)
- rubyntlm (0.6.2)
- rubypants (0.2.0)
- rubyzip (1.2.1)
- rufus-scheduler (3.4.0)
- et-orbi (~> 1.0)
- rugged (0.27.2)
- safe_yaml (1.0.4)
- sanitize (4.6.6)
- crass (~> 1.0.2)
- nokogiri (>= 1.4.4)
- nokogumbo (~> 1.4)
- sass (3.5.5)
- sass-listen (~> 4.0.0)
- sass-listen (4.0.0)
- rb-fsevent (~> 0.9, >= 0.9.4)
- rb-inotify (~> 0.9, >= 0.9.7)
- sass-rails (5.0.6)
- railties (>= 4.0.0, < 6)
- sass (~> 3.1)
- sprockets (>= 2.8, < 4.0)
- sprockets-rails (>= 2.0, < 4.0)
- tilt (>= 1.1, < 3)
- sawyer (0.8.1)
- addressable (>= 2.3.5, < 2.6)
- faraday (~> 0.8, < 1.0)
- scss_lint (0.56.0)
- rake (>= 0.9, < 13)
- sass (~> 3.5.3)
- seed-fu (2.3.7)
- activerecord (>= 3.1)
- activesupport (>= 3.1)
- select2-rails (3.5.9.3)
- thor (~> 0.14)
- selenium-webdriver (3.12.0)
- childprocess (~> 0.5)
- rubyzip (~> 1.2)
- sentry-raven (2.7.2)
- faraday (>= 0.7.6, < 1.0)
- settingslogic (2.0.9)
- sexp_processor (4.9.0)
- sham_rack (1.3.6)
- rack
- shoulda-matchers (3.1.2)
- activesupport (>= 4.0.0)
- sidekiq (5.1.3)
- concurrent-ruby (~> 1.0)
- connection_pool (~> 2.2, >= 2.2.0)
- rack-protection (>= 1.5.0)
- redis (>= 3.3.5, < 5)
- sidekiq-cron (0.6.0)
- rufus-scheduler (>= 3.3.0)
- sidekiq (>= 4.2.1)
- sidekiq-limit_fetch (3.4.0)
- sidekiq (>= 4)
- signet (0.8.1)
- addressable (~> 2.3)
- faraday (~> 0.9)
- jwt (>= 1.5, < 3.0)
- multi_json (~> 1.10)
- simple_po_parser (1.1.2)
- simplecov (0.14.1)
- docile (~> 1.1.0)
- json (>= 1.8, < 3)
- simplecov-html (~> 0.10.0)
- simplecov-html (0.10.0)
- slack-notifier (1.5.1)
- slop (3.6.0)
- spring (2.0.1)
- activesupport (>= 4.2)
- spring-commands-rspec (1.0.4)
- spring (>= 0.9.1)
- sprockets (3.7.2)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.1)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
- sprockets (>= 3.0.0)
- sqlite3 (1.3.13)
- sshkey (1.9.0)
- stackprof (0.2.10)
- state_machines (0.5.0)
- state_machines-activemodel (0.5.1)
- activemodel (>= 4.1, < 6.0)
- state_machines (>= 0.5.0)
- state_machines-activerecord (0.5.1)
- activerecord (>= 4.1, < 6.0)
- state_machines-activemodel (>= 0.5.0)
- stringex (2.8.4)
- sys-filesystem (1.1.6)
- ffi
- sysexits (1.2.0)
- temple (0.8.0)
- test-prof (0.2.5)
- text (1.3.1)
- thin (1.7.0)
- daemons (~> 1.0, >= 1.0.9)
- eventmachine (~> 1.0, >= 1.0.4)
- rack (>= 1, < 3)
- thor (0.19.4)
- thread_safe (0.3.6)
- tilt (2.0.8)
- timecop (0.8.1)
- timfel-krb5-auth (0.8.3)
- toml (0.1.2)
- parslet (~> 1.5.0)
- toml-rb (1.0.0)
- citrus (~> 3.0, > 3.0)
- trollop (2.1.3)
- truncato (0.7.10)
- htmlentities (~> 4.3.1)
- nokogiri (~> 1.8.0, >= 1.7.0)
- tzinfo (1.2.5)
- thread_safe (~> 0.1)
- u2f (0.2.1)
- uber (0.1.0)
- uglifier (2.7.2)
- execjs (>= 0.3.0)
- json (>= 1.8.0)
- unf (0.1.4)
- unf_ext
- unf_ext (0.0.7.5)
- unicode-display_width (1.3.2)
- unicorn (5.1.0)
- kgio (~> 2.6)
- raindrops (~> 0.7)
- unicorn-worker-killer (0.4.4)
- get_process_mem (~> 0)
- unicorn (>= 4, < 6)
- uniform_notifier (1.10.0)
- unparser (0.2.7)
- abstract_type (~> 0.0.7)
- adamantium (~> 0.2.0)
- concord (~> 0.1.5)
- diff-lcs (~> 1.3)
- equalizer (~> 0.0.9)
- parser (>= 2.3.1.2, < 2.6)
- procto (~> 0.0.2)
- validates_hostname (1.0.6)
- activerecord (>= 3.0)
- activesupport (>= 3.0)
- version_sorter (2.1.0)
- virtus (1.0.5)
- axiom-types (~> 0.1)
- coercible (~> 1.0)
- descendants_tracker (~> 0.0, >= 0.0.3)
- equalizer (~> 0.0, >= 0.0.9)
- vmstat (2.3.0)
- warden (1.2.7)
- rack (>= 1.0)
- webmock (2.3.2)
- addressable (>= 2.3.6)
- crack (>= 0.3.2)
- hashdiff
- webpack-rails (0.9.10)
- railties (>= 3.2.0)
- websocket-driver (0.6.5)
- websocket-extensions (>= 0.1.0)
- websocket-extensions (0.1.3)
- wikicloth (0.8.1)
- builder
- expression_parser
- rinku
- with_env (1.1.0)
- xml-simple (1.1.5)
- xpath (2.1.0)
- nokogiri (~> 1.3)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- RedCloth (~> 4.3.2)
- ace-rails-ap (~> 4.1.0)
- activerecord_sane_schema_dumper (= 1.0)
- acts-as-taggable-on (~> 5.0)
- addressable (~> 2.5.2)
- akismet (~> 2.0)
- asana (~> 0.6.0)
- asciidoctor (~> 1.5.6)
- asciidoctor-plantuml (= 0.0.8)
- asset_sync (~> 2.4)
- attr_encrypted (~> 3.1.0)
- awesome_print
- babosa (~> 1.0.2)
- base32 (~> 0.3.0)
- batch-loader (~> 1.2.1)
- bcrypt_pbkdf (~> 1.0)
- benchmark-ips (~> 2.3.0)
- better_errors (~> 2.1.0)
- binding_of_caller (~> 0.7.2)
- bootsnap (~> 1.3)
- bootstrap_form (~> 2.7.0)
- brakeman (~> 4.2)
- browser (~> 2.2)
- bullet (~> 5.5.0)
- bundler-audit (~> 0.5.0)
- capybara (~> 2.15)
- capybara-screenshot (~> 1.0.0)
- carrierwave (~> 1.2)
- charlock_holmes (~> 0.7.5)
- chronic (~> 0.10.2)
- chronic_duration (~> 0.10.6)
- commonmarker (~> 0.17)
- concurrent-ruby (~> 1.0.5)
- connection_pool (~> 2.0)
- creole (~> 0.5.0)
- database_cleaner (~> 1.5.0)
- deckar01-task_list (= 2.0.0)
- default_value_for (~> 3.0.5)
- device_detector
- devise (~> 4.4)
- devise-two-factor (~> 3.0.0)
- diffy (~> 3.1.0)
- doorkeeper (~> 4.3)
- doorkeeper-openid_connect (~> 1.5)
- dropzonejs-rails (~> 0.7.1)
- ed25519 (~> 1.2)
- email_reply_trimmer (~> 0.1)
- email_spec (~> 2.2.0)
- factory_bot_rails (~> 4.8.2)
- faraday (~> 0.12)
- fast_blank
- ffaker (~> 2.4)
- flay (~> 2.10.0)
- flipper (~> 0.13.0)
- flipper-active_record (~> 0.13.0)
- flipper-active_support_cache_store (~> 0.13.0)
- fog-aliyun (~> 0.2.0)
- fog-aws (~> 2.0.1)
- fog-core (~> 1.44)
- fog-google (~> 1.3.3)
- fog-local (~> 0.3)
- fog-openstack (~> 0.1)
- fog-rackspace (~> 0.1.1)
- font-awesome-rails (~> 4.7)
- foreman (~> 0.84.0)
- fuubar (~> 2.2.0)
- gemnasium-gitlab-service (~> 0.2)
- gemojione (~> 3.3)
- gettext (~> 3.2.2)
- gettext_i18n_rails (~> 1.8.0)
- gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.112.0)
- github-linguist (~> 5.3.3)
- gitlab-flowdock-git-hook (~> 1.0.1)
- gitlab-gollum-lib (~> 4.2)
- gitlab-gollum-rugged_adapter (~> 0.4.4)
- gitlab-markup (~> 1.6.4)
- gitlab-styles (~> 2.4)
- gitlab_omniauth-ldap (~> 2.0.4)
- gon (~> 6.2)
- google-api-client (~> 0.19.8)
- google-protobuf (= 3.5.1)
- gpgme
- grape (~> 1.0)
- grape-entity (~> 0.7.1)
- grape-path-helpers (~> 1.0)
- grape_logging (~> 1.7)
- graphiql-rails (~> 1.4.10)
- graphql (~> 1.8.0)
- grpc (~> 1.11.0)
- haml_lint (~> 0.26.0)
- hamlit (~> 2.8.8)
- hangouts-chat (~> 0.0.5)
- hashie-forbidden_attributes
- health_check (~> 2.6.0)
- hipchat (~> 1.5.0)
- html-pipeline (~> 2.8)
- html2text
- httparty (~> 0.13.3)
- icalendar
- influxdb (~> 0.2)
- jira-ruby (~> 1.4)
- jquery-atwho-rails (~> 1.3.2)
- json-schema (~> 2.8.0)
- jwt (~> 1.5.6)
- kaminari (~> 1.0)
- knapsack (~> 1.16)
- kubeclient (~> 3.1.0)
- letter_opener_web (~> 1.3.0)
- license_finder (~> 3.1)
- licensee (~> 8.9)
- lograge (~> 0.5)
- loofah (~> 2.2)
- mail_room (~> 0.9.1)
- method_source (~> 0.8)
- mini_magick
- minitest (~> 5.7.0)
- mousetrap-rails (~> 1.4.6)
- mysql2 (~> 0.4.10)
- net-ldap
- net-ssh (~> 5.0)
- nokogiri (~> 1.8.2)
- oauth2 (~> 1.4)
- octokit (~> 4.9)
- omniauth (~> 1.8)
- omniauth-auth0 (~> 2.0.0)
- omniauth-authentiq (~> 0.3.3)
- omniauth-azure-oauth2 (~> 0.0.9)
- omniauth-cas3 (~> 1.1.4)
- omniauth-facebook (~> 4.0.0)
- omniauth-github (~> 1.3)
- omniauth-gitlab (~> 1.0.2)
- omniauth-google-oauth2 (~> 0.5.3)
- omniauth-kerberos (~> 0.3.0)
- omniauth-oauth2-generic (~> 0.2.2)
- omniauth-saml (~> 1.10)
- omniauth-shibboleth (~> 1.3.0)
- omniauth-twitter (~> 1.4)
- omniauth_crowd (~> 2.2.0)
- org-ruby (~> 0.9.12)
- peek (~> 1.0.1)
- peek-gc (~> 0.0.2)
- peek-mysql2 (~> 1.1.0)
- peek-pg (~> 1.3.0)
- peek-rblineprof (~> 0.2.0)
- peek-redis (~> 1.2.0)
- peek-sidekiq (~> 1.0.3)
- pg (~> 0.18.2)
- premailer-rails (~> 1.9.7)
- prometheus-client-mmap (~> 0.9.4)
- pry-byebug (~> 3.4.1)
- pry-rails (~> 0.3.4)
- rack-attack (~> 4.4.1)
- rack-cors (~> 1.0.0)
- rack-oauth2 (~> 1.2.1)
- rack-proxy (~> 0.6.0)
- rails (= 5.0.7)
- rails-controller-testing
- rails-deprecated_sanitizer (~> 1.0.3)
- rails-i18n (~> 5.1)
- rainbow (~> 2.2)
- raindrops (~> 0.18)
- rblineprof (~> 0.3.6)
- rbtrace (~> 0.4)
- rdoc (~> 6.0)
- re2 (~> 1.1.1)
- recaptcha (~> 3.0)
- redcarpet (~> 3.4)
- redis (~> 3.2)
- redis-namespace (~> 1.6.0)
- redis-rails (~> 5.0.2)
- request_store (~> 1.3)
- responders (~> 2.0)
- rouge (~> 3.1)
- rqrcode-rails3 (~> 0.1.7)
- rspec-parameterized
- rspec-rails (~> 3.7.0)
- rspec-retry (~> 0.4.5)
- rspec-set (~> 0.1.3)
- rspec_profiling (~> 0.0.5)
- rubocop (~> 0.54.0)
- rubocop-rspec (~> 1.22.1)
- ruby-fogbugz (~> 0.2.1)
- ruby-prof (~> 0.17.0)
- ruby-progressbar
- ruby_parser (~> 3.8)
- rufus-scheduler (~> 3.4)
- rugged (~> 0.27)
- sanitize (~> 4.6)
- sass-rails (~> 5.0.6)
- scss_lint (~> 0.56.0)
- seed-fu (~> 2.3.7)
- select2-rails (~> 3.5.9)
- selenium-webdriver (~> 3.12)
- sentry-raven (~> 2.7)
- settingslogic (~> 2.0.9)
- sham_rack (~> 1.3.6)
- shoulda-matchers (~> 3.1.2)
- sidekiq (~> 5.1)
- sidekiq-cron (~> 0.6.0)
- sidekiq-limit_fetch (~> 3.4)
- simple_po_parser (~> 1.1.2)
- simplecov (~> 0.14.0)
- slack-notifier (~> 1.5.1)
- spring (~> 2.0.0)
- spring-commands-rspec (~> 1.0.4)
- sprockets (~> 3.7.0)
- sshkey (~> 1.9.0)
- stackprof (~> 0.2.10)
- state_machines-activerecord (~> 0.5.1)
- sys-filesystem (~> 1.1.6)
- test-prof (~> 0.2.5)
- thin (~> 1.7.0)
- timecop (~> 0.8.0)
- toml-rb (~> 1.0.0)
- truncato (~> 0.7.9)
- u2f (~> 0.2.1)
- uglifier (~> 2.7.2)
- unf (~> 0.1.4)
- unicorn (~> 5.1.0)
- unicorn-worker-killer (~> 0.4.4)
- validates_hostname (~> 1.0.6)
- version_sorter (~> 2.1.0)
- virtus (~> 1.0.1)
- vmstat (~> 2.3.0)
- webmock (~> 2.3.2)
- webpack-rails (~> 0.9.10)
- wikicloth (= 0.8.1)
-
-BUNDLED WITH
- 1.16.2
diff --git a/LICENSE b/LICENSE
index a76372fad2c..a90ea939517 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,19 @@
Copyright GitLab B.V.
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/PROCESS.md b/PROCESS.md
index 5f50d472bd7..aadd6a321f0 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -74,14 +74,37 @@ star, smile, etc.). Some good tips about code reviews can be found in our
## Feature freeze on the 7th for the release on the 22nd
-After 7th at 23:59 (Pacific Time Zone) of each month, RC1 of the upcoming release (to be shipped on the 22nd) is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it.
-Merge requests may still be merged into master during this period,
-but they will go into the _next_ release, unless they are manually cherry-picked into the stable branch.
-
-By freezing the stable branches 2 weeks prior to a release, we reduce the risk of a last minute merge request potentially breaking things.
-
-Any release candidate that gets created after this date can become a final release,
-hence the name release candidate.
+After 7th at 23:59 (Pacific Time Zone) of each month, RC1 of the upcoming
+release (to be shipped on the 22nd) is created and deployed to GitLab.com and
+the stable branch for this release is frozen, which means master is no longer
+merged into it. Merge requests may still be merged into master during this
+period, but they will go into the _next_ release, unless they are manually
+cherry-picked into the stable branch.
+
+By freezing the stable branches 2 weeks prior to a release, we reduce the risk
+of a last minute merge request potentially breaking things.
+
+Any release candidate that gets created after this date can become a final
+release, hence the name release candidate.
+
+### Feature flags
+
+Merge requests that make changes hidden behind a feature flag, or remove an
+existing feature flag because a feature is deemed stable, may be merged (and
+picked into the stable branches) up to the 19th of the month. Such merge
+requests should have the ~"feature flag" label assigned, and don't require a
+corresponding exception request to be created.
+
+In order to build the final package and present the feature for self-hosted
+customers, the feature flag should be removed. This should happen before the
+22nd, ideally _at least_ 2 days before. That means MRs with feature
+flags being picked at the 19th would have a quite tight schedule, so picking
+these _earlier_ is preferable.
+
+While rare, release managers may decide to reject picking a change into a stable
+branch, even when feature flags are used. This might be necessary if the changes
+are deemed problematic, too invasive, or there simply isn't enough time to
+properly test how the changes behave on GitLab.com.
### Between the 1st and the 7th
@@ -116,6 +139,11 @@ target. However, if one does and falls into either of the above categories, it's
the reviewer's responsibility to manage the above communication and assignment
on behalf of the community member.
+Every new feature or change should be shipped with its corresponding documentation
+in accordance with the
+[documentation process](https://docs.gitlab.com/ee/development/documentation/workflow.html)
+and [structure](https://docs.gitlab.com/ee/development/documentation/structure.html).
+
#### What happens if these deadlines are missed?
If a small or large feature is _not_ with a maintainer or reviewer by the
@@ -141,14 +169,9 @@ and to prevent any last minute surprises.
### On the 7th
-Merge requests should still be complete, following the
-[definition of done][done]. The single exception is documentation, and this can
-only be left until after the freeze if:
+Merge requests should still be complete, following the [definition of done][done].
-* There is a follow-up issue to add documentation.
-* It is assigned to the person writing documentation for this feature, and they
- are aware of it.
-* It is in the correct milestone, with the ~Deliverable label.
+#### Feature merge requests
If a merge request is not ready, but the developers and Product Manager
responsible for the feature think it is essential that it is in the release,
@@ -164,6 +187,23 @@ information, see
[Automatic CE->EE merge][automatic_ce_ee_merge] and
[Guidelines for implementing Enterprise Edition features][ee_features].
+#### Documentation merge requests
+
+Documentation is part of the product and must be shipped with the feature.
+
+The single exception for the feature freeze is documentation, and it can
+be left to be **merged up to the 14th** if:
+
+* There is a follow-up issue to add documentation.
+* It is assigned to the developer writing documentation for this feature, and they
+ are aware of it.
+* It is in the correct milestone, with the labels ~Documentation, ~Deliverable,
+~missed-deliverable, and "pick into X.Y" applied.
+* It must be reviewed and approved by a technical writer.
+
+For more information read the process for
+[documentation shipped late](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late).
+
### After the 7th
Once the stable branch is frozen, the only MRs that can be cherry-picked into
@@ -172,8 +212,9 @@ the stable branch are:
* Fixes for [regressions](#regressions) where the affected version `xx.x` in `regression:xx.x` is the current release. See [Managing bugs](#managing-bugs) section.
* Fixes for security issues
* Fixes or improvements to automated QA scenarios
-* Documentation updates for changes in the same release
+* [Documentation updates](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late) for changes in the same release
* New or updated translations (as long as they do not touch application code)
+* Changes that are behind a feature flag and have the ~"feature flag" label
During the feature freeze all merge requests that are meant to go into the
upcoming release should have the correct milestone assigned _and_ the
@@ -206,36 +247,36 @@ Check [this guide](https://gitlab.com/gitlab-org/release/docs/blob/master/genera
A ~bug is a defect, error, failure which causes the system to behave incorrectly or prevents it from fulfilling the product requirements.
-The level of impact of a ~bug can vary from blocking a whole functionality
-or a feature usability bug. A bug should always be linked to a severity level.
+The level of impact of a ~bug can vary from blocking a whole functionality
+or a feature usability bug. A bug should always be linked to a severity level.
Refer to our [severity levels](../CONTRIBUTING.md#severity-labels)
-Whether the bug is also a regression or not, the triage process should start as soon as possible.
+Whether the bug is also a regression or not, the triage process should start as soon as possible.
Ensure that the Engineering Manager and/or the Product Manager for the relative area is involved to prioritize the work as needed.
### Regressions
A ~regression implies that a previously **verified working functionality** no longer works.
Regressions are a subset of bugs. We use the ~regression label to imply that the defect caused the functionality to regress.
-The label tells us that something worked before and it needs extra attention from Engineering and Product Managers to schedule/reschedule.
+The label tells us that something worked before and it needs extra attention from Engineering and Product Managers to schedule/reschedule.
-The regression label does not apply to ~bugs for new features for which functionality was **never verified as working**.
-These, by definition, are not regressions.
+The regression label does not apply to ~bugs for new features for which functionality was **never verified as working**.
+These, by definition, are not regressions.
A regression should always have the `regression:xx.x` label on it to designate when it was introduced.
-Regressions should be considered high priority issues that should be solved as soon as possible, especially if they have severe impact on users.
+Regressions should be considered high priority issues that should be solved as soon as possible, especially if they have severe impact on users.
### Managing bugs
-**Prioritization:** We give higher priority to regressions on features that worked in the last recent monthly release and the current release candidates.
-The two scenarios below can [bypass the exception request in the release process](https://gitlab.com/gitlab-org/release/docs/blob/master/general/exception-request/process.md#after-the-7th), where the affected regression version matches the current monthly release version.
+**Prioritization:** We give higher priority to regressions on features that worked in the last recent monthly release and the current release candidates.
+The two scenarios below can [bypass the exception request in the release process](https://gitlab.com/gitlab-org/release/docs/blob/master/general/exception-request/process.md#after-the-7th), where the affected regression version matches the current monthly release version.
* A regression which worked in the **Last monthly release**
* **Example:** In 11.0 we released a new `feature X` that is verified as working. Then in release 11.1 the feature no longer works, this is regression for 11.1. The issue should have the `regression:11.1` label.
* *Note:* When we say `the last recent monthly release`, this can refer to either the version currently running on GitLab.com, or the most recent version available in the package repositories.
* A regression which worked in the **Current release candidates**
* **Example:** In 11.1-RC3 we shipped a new feature which has been verified as working. Then in 11.1-RC5 the feature no longer works, this is regression for 11.1. The issue should have the `regression:11.1` label.
- * *Note:* Because GitLab.com runs release candidates of new releases, a regression can be reported in a release before its 'official' release date on the 22nd of the month.
+ * *Note:* Because GitLab.com runs release candidates of new releases, a regression can be reported in a release before its 'official' release date on the 22nd of the month.
When a bug is found:
1. Create an issue describing the problem in the most detailed way possible.
@@ -247,11 +288,11 @@ When a bug is found:
The counterpart Product Manager is included to weigh-in on prioritization as needed.
1. If the ~bug is **NOT** a regression:
1. The Engineering Manager decides which milestone the bug will be fixed. The appropriate milestone is applied.
-1. If the bug is a ~regression:
+1. If the bug is a ~regression:
1. Determine the release that the regression affects and add the corresponding `regression:xx.x` label.
1. If the affected release version can't be determined, add the generic ~regression label for the time being.
- 1. If the affected version `xx.x` in `regression:xx.x` is the **current release**, it's recommended to schedule the fix for the current milestone.
- 1. This falls under regressions which worked in the last release and the current RCs. More detailed explanations in the **Prioritization** section above.
+ 1. If the affected version `xx.x` in `regression:xx.x` is the **current release**, it's recommended to schedule the fix for the current milestone.
+ 1. This falls under regressions which worked in the last release and the current RCs. More detailed explanations in the **Prioritization** section above.
1. If the affected version `xx.x` in `regression:xx.x` is older than the **current release**
1. If the regression is an ~S1 severity, it's recommended to schedule the fix for the current milestone. We would like to fix the highest severity regression as soon as we can.
1. If the regression is an ~S2, ~S3 or ~S4 severity, the regression may be scheduled for later milestones at the discretion of the Engineering Manager and Product Manager.
diff --git a/README.md b/README.md
index b6e1cc9a432..133c15a83a7 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ You can access a new installation with the login **`root`** and password **`5ive
## Contributing
-GitLab is an open source project and we are very happy to accept community contributions. Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
+GitLab is an open source project and we are very happy to accept community contributions. Please refer to [Contributing to GitLab page](https://about.gitlab.com/contributing/) for more details.
## Licensing
@@ -66,7 +66,7 @@ GitLab Community Edition (CE) is available freely under the MIT Expat license.
All third party components incorporated into the GitLab Software are licensed under the original license provided by the owner of the applicable component.
-All Documentation content that resides under the doc/ directory of this repository is licensed under Creative Commons: CC BY-SA 4.0.
+All Documentation content that resides under the `doc/` directory of this repository is licensed under Creative Commons: CC BY-SA 4.0.
## Install a development environment
@@ -83,7 +83,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL/OpenSUSE
-- Ruby (MRI) 2.3
+- Ruby (MRI) 2.4
- Git 2.8.4+
- Redis 2.8+
- PostgreSQL (preferred) or MySQL
diff --git a/VERSION b/VERSION
index 53906da50f8..d6ed538af36 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-11.2.0-pre
+11.5.0-pre
diff --git a/app/assets/images/auth_buttons/auth0_64.png b/app/assets/images/auth_buttons/auth0_64.png
new file mode 100644
index 00000000000..5ad59659380
--- /dev/null
+++ b/app/assets/images/auth_buttons/auth0_64.png
Binary files differ
diff --git a/app/assets/images/auth_buttons/azure_64.png b/app/assets/images/auth_buttons/azure_64.png
index 85de7793440..168a9c81395 100644
--- a/app/assets/images/auth_buttons/azure_64.png
+++ b/app/assets/images/auth_buttons/azure_64.png
Binary files differ
diff --git a/app/assets/images/auth_buttons/bitbucket_64.png b/app/assets/images/auth_buttons/bitbucket_64.png
index b3d022a5a70..0edf7f52a11 100644
--- a/app/assets/images/auth_buttons/bitbucket_64.png
+++ b/app/assets/images/auth_buttons/bitbucket_64.png
Binary files differ
diff --git a/app/assets/images/auth_buttons/google_64.png b/app/assets/images/auth_buttons/google_64.png
index 720824230a5..389c1cd54ca 100644
--- a/app/assets/images/auth_buttons/google_64.png
+++ b/app/assets/images/auth_buttons/google_64.png
Binary files differ
diff --git a/app/assets/images/auth_buttons/jwt_64.png b/app/assets/images/auth_buttons/jwt_64.png
new file mode 100644
index 00000000000..ca97ae47002
--- /dev/null
+++ b/app/assets/images/auth_buttons/jwt_64.png
Binary files differ
diff --git a/app/assets/images/auth_buttons/shibboleth_64.png b/app/assets/images/auth_buttons/shibboleth_64.png
new file mode 100644
index 00000000000..d4c752f9400
--- /dev/null
+++ b/app/assets/images/auth_buttons/shibboleth_64.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/canary/favicon_status_scheduled.ico b/app/assets/images/ci_favicons/canary/favicon_status_scheduled.ico
new file mode 100644
index 00000000000..5444b8e41dc
--- /dev/null
+++ b/app/assets/images/ci_favicons/canary/favicon_status_scheduled.ico
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_scheduled.png b/app/assets/images/ci_favicons/favicon_status_scheduled.png
new file mode 100644
index 00000000000..d198c255fdd
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_scheduled.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/elasticsearch.png b/app/assets/images/cluster_app_logos/elasticsearch.png
new file mode 100644
index 00000000000..96e9e0ff934
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/elasticsearch.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/gitlab.png b/app/assets/images/cluster_app_logos/gitlab.png
new file mode 100644
index 00000000000..cb2195fc6a2
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/gitlab.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/helm.png b/app/assets/images/cluster_app_logos/helm.png
new file mode 100644
index 00000000000..2989cae7b93
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/helm.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/jeager.png b/app/assets/images/cluster_app_logos/jeager.png
new file mode 100644
index 00000000000..be5bf2a0c9c
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/jeager.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/jupyterhub.png b/app/assets/images/cluster_app_logos/jupyterhub.png
new file mode 100644
index 00000000000..80c7343067f
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/jupyterhub.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/knative.png b/app/assets/images/cluster_app_logos/knative.png
new file mode 100644
index 00000000000..0a2510c8549
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/knative.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/kubernetes.png b/app/assets/images/cluster_app_logos/kubernetes.png
new file mode 100644
index 00000000000..4d774909c10
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/kubernetes.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/meltano.png b/app/assets/images/cluster_app_logos/meltano.png
new file mode 100644
index 00000000000..7a2d82fbe27
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/meltano.png
Binary files differ
diff --git a/app/assets/images/cluster_app_logos/prometheus.png b/app/assets/images/cluster_app_logos/prometheus.png
new file mode 100644
index 00000000000..a8663449b88
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/prometheus.png
Binary files differ
diff --git a/app/assets/images/koding-logo.svg b/app/assets/images/koding-logo.svg
deleted file mode 100644
index ad89d684d94..00000000000
--- a/app/assets/images/koding-logo.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 14">
- <g fill="#d6d7d9">
- <path d="M8.7 0L5.3.3l3.2 6.8-3.2 6.6 3.5.3L12 6.9z"/>
- <ellipse cx="1.7" cy="11.1" rx="1.7" ry="1.7"/>
- <ellipse cx="1.7" cy="5.6" rx="1.7" ry="1.7"/>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js
index de4566bb119..05de970e387 100644
--- a/app/assets/javascripts/activities.js
+++ b/app/assets/javascripts/activities.js
@@ -6,10 +6,12 @@ import Pager from './pager';
import { localTimeAgo } from './lib/utils/datetime_utility';
export default class Activities {
- constructor() {
- Pager.init(20, true, false, data => data, this.updateTooltips);
+ constructor(container = '') {
+ this.container = container;
- $('.event-filter-link').on('click', (e) => {
+ Pager.init(20, true, false, data => data, this.updateTooltips, this.container);
+
+ $('.event-filter-link').on('click', e => {
e.preventDefault();
this.toggleFilter(e.currentTarget);
this.reloadActivities();
@@ -22,7 +24,7 @@ export default class Activities {
reloadActivities() {
$('.content_list').html('');
- Pager.init(20, true, false, data => data, this.updateTooltips);
+ Pager.init(20, true, false, data => data, this.updateTooltips, this.container);
}
toggleFilter(sender) {
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 25fe2ae553e..3f7a1ef1bfc 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -15,12 +15,11 @@ const Api = {
mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
groupLabelsPath: '/groups/:namespace_path/-/labels',
- licensePath: '/api/:version/templates/licenses/:key',
- gitignorePath: '/api/:version/templates/gitignores/:key',
- gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
- dockerfilePath: '/api/:version/templates/dockerfiles/:key',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
+ projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key',
+ projectTemplatesPath: '/api/:version/projects/:id/templates/:type',
usersPath: '/api/:version/users.json',
+ userStatusPath: '/api/:version/user/status',
commitPath: '/api/:version/projects/:id/repository/commits',
commitPipelinesPath: '/:project_id/commit/:sha/pipelines',
branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch',
@@ -194,29 +193,29 @@ const Api = {
return axios.get(url);
},
- // Return text for a specific license
- licenseText(key, data, callback) {
- const url = Api.buildUrl(Api.licensePath).replace(':key', key);
- return axios
- .get(url, {
- params: data,
- })
- .then(res => callback(res.data));
- },
+ projectTemplate(id, type, key, options, callback) {
+ const url = Api.buildUrl(this.projectTemplatePath)
+ .replace(':id', encodeURIComponent(id))
+ .replace(':type', type)
+ .replace(':key', encodeURIComponent(key));
- gitignoreText(key, callback) {
- const url = Api.buildUrl(Api.gitignorePath).replace(':key', key);
- return axios.get(url).then(({ data }) => callback(data));
- },
+ return axios.get(url, { params: options }).then(res => {
+ if (callback) callback(res.data);
- gitlabCiYml(key, callback) {
- const url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key);
- return axios.get(url).then(({ data }) => callback(data));
+ return res;
+ });
},
- dockerfileYml(key, callback) {
- const url = Api.buildUrl(Api.dockerfilePath).replace(':key', key);
- return axios.get(url).then(({ data }) => callback(data));
+ projectTemplates(id, type, params = {}, callback) {
+ const url = Api.buildUrl(this.projectTemplatesPath)
+ .replace(':id', encodeURIComponent(id))
+ .replace(':type', type);
+
+ return axios.get(url, { params }).then(res => {
+ if (callback) callback(res.data);
+
+ return res;
+ });
},
issueTemplate(namespacePath, projectPath, key, type, callback) {
@@ -265,6 +264,15 @@ const Api = {
});
},
+ postUserStatus({ emoji, message }) {
+ const url = Api.buildUrl(this.userStatusPath);
+
+ return axios.put(url, {
+ emoji,
+ message,
+ });
+ },
+
buildUrl(url) {
let urlRoot = '';
if (gon.relative_url_root != null) {
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index e34db893989..cace8bb9dba 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -42,10 +42,11 @@ export class AwardsHandler {
}
bindEvents() {
+ const $parentEl = this.targetContainerEl ? $(this.targetContainerEl) : $(document);
// If the user shows intent let's pre-build the menu
this.registerEventListener(
'one',
- $(document),
+ $parentEl,
'mouseenter focus',
this.toggleButtonSelector,
'mouseenter focus',
@@ -58,7 +59,7 @@ export class AwardsHandler {
}
},
);
- this.registerEventListener('on', $(document), 'click', this.toggleButtonSelector, e => {
+ this.registerEventListener('on', $parentEl, 'click', this.toggleButtonSelector, e => {
e.stopPropagation();
e.preventDefault();
this.showEmojiMenu($(e.currentTarget));
@@ -76,7 +77,7 @@ export class AwardsHandler {
});
const emojiButtonSelector = `.js-awards-block .js-emoji-btn, .${this.menuClass} .js-emoji-btn`;
- this.registerEventListener('on', $(document), 'click', emojiButtonSelector, e => {
+ this.registerEventListener('on', $parentEl, 'click', emojiButtonSelector, e => {
e.preventDefault();
const $target = $(e.currentTarget);
const $glEmojiElement = $target.find('gl-emoji');
@@ -109,8 +110,6 @@ export class AwardsHandler {
}
const $menu = $(`.${this.menuClass}`);
- const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent();
- const $userAuthored = this.isUserAuthored($addBtn);
if ($menu.length) {
if ($menu.is('.is-visible')) {
$addBtn.removeClass('is-active');
@@ -134,9 +133,6 @@ export class AwardsHandler {
}, 200);
});
}
-
- $thumbsBtn.toggleClass('disabled', $userAuthored);
- $thumbsBtn.prop('disabled', $userAuthored);
}
// Create the emoji menu with the first category of emojis.
@@ -173,7 +169,8 @@ export class AwardsHandler {
</div>
`;
- document.body.insertAdjacentHTML('beforeend', emojiMenuMarkup);
+ const targetEl = this.targetContainerEl ? this.targetContainerEl : document.body;
+ targetEl.insertAdjacentHTML('beforeend', emojiMenuMarkup);
this.addRemainingEmojiMenuCategories();
this.setupSearch();
@@ -255,6 +252,12 @@ export class AwardsHandler {
}
positionMenu($menu, $addBtn) {
+ if (this.targetContainerEl) {
+ return $menu.css({
+ top: `${$addBtn.outerHeight()}px`,
+ });
+ }
+
const position = $addBtn.data('position');
// The menu could potentially be off-screen or in a hidden overflow element
// So we position the element absolute in the body
@@ -364,10 +367,6 @@ export class AwardsHandler {
return $emojiButton.hasClass('active');
}
- isUserAuthored($button) {
- return $button.hasClass('js-user-authored');
- }
-
decrementCounter($emojiButton, emoji) {
const counter = $('.js-counter', $emojiButton);
const counterNumber = parseInt(counter.text(), 10);
@@ -433,9 +432,7 @@ export class AwardsHandler {
users = origTitle.trim().split(FROM_SENTENCE_REGEX);
}
users.unshift('You');
- return awardBlock
- .attr('title', this.toSentence(users))
- .tooltip('_fixTitle');
+ return awardBlock.attr('title', this.toSentence(users)).tooltip('_fixTitle');
}
createAwardButtonForVotesBlock(votesBlock, emojiName) {
@@ -474,20 +471,16 @@ export class AwardsHandler {
}
postEmoji($emojiButton, awardUrl, emoji, callback) {
- if (this.isUserAuthored($emojiButton)) {
- this.userAuthored($emojiButton);
- } else {
- axios
- .post(awardUrl, {
- name: emoji,
- })
- .then(({ data }) => {
- if (data.ok) {
- callback();
- }
- })
- .catch(() => flash(__('Something went wrong on our end.')));
- }
+ axios
+ .post(awardUrl, {
+ name: emoji,
+ })
+ .then(({ data }) => {
+ if (data.ok) {
+ callback();
+ }
+ })
+ .catch(() => flash(__('Something went wrong on our end.')));
}
findEmojiIcon(votesBlock, emoji) {
@@ -622,13 +615,11 @@ export class AwardsHandler {
let awardsHandlerPromise = null;
export default function loadAwardsHandler(reload = false) {
if (!awardsHandlerPromise || reload) {
- awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(
- Emoji => {
- const awardsHandler = new AwardsHandler(Emoji);
- awardsHandler.bindEvents();
- return awardsHandler;
- },
- );
+ awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(Emoji => {
+ const awardsHandler = new AwardsHandler(Emoji);
+ awardsHandler.bindEvents();
+ return awardsHandler;
+ });
}
return awardsHandlerPromise;
}
diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue
index 155c348286c..ca1662313c2 100644
--- a/app/assets/javascripts/badges/components/badge.vue
+++ b/app/assets/javascripts/badges/components/badge.vue
@@ -1,14 +1,14 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
+import { GlLoadingIcon } from '@gitlab/ui';
export default {
name: 'Badge',
components: {
Icon,
- LoadingIcon,
Tooltip,
+ GlLoadingIcon,
},
directives: {
Tooltip,
@@ -80,7 +80,7 @@ export default {
/>
</a>
- <loading-icon
+ <gl-loading-icon
v-show="isLoading"
:inline="true"
/>
@@ -105,8 +105,8 @@ export default {
</div>
<button
- v-tooltip
v-show="hasError"
+ v-tooltip
:title="s__('Badges|Reload badge image')"
class="btn btn-transparent btn-sm text-primary"
type="button"
diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue
index 7a13f74c570..0eff922d93c 100644
--- a/app/assets/javascripts/badges/components/badge_form.vue
+++ b/app/assets/javascripts/badges/components/badge_form.vue
@@ -4,7 +4,7 @@ import { mapActions, mapState } from 'vuex';
import createFlash from '~/flash';
import { s__, sprintf } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import createEmptyBadge from '../empty_badge';
import Badge from './badge.vue';
@@ -15,7 +15,7 @@ export default {
components: {
Badge,
LoadingButton,
- LoadingIcon,
+ GlLoadingIcon,
},
props: {
isEditing: {
@@ -23,6 +23,11 @@ export default {
required: true,
},
},
+ data() {
+ return {
+ wasValidated: false,
+ };
+ },
computed: {
...mapState([
'badgeInAddForm',
@@ -39,16 +44,6 @@ export default {
return this.badgeInAddForm;
},
- canSubmit() {
- return (
- this.badge !== null &&
- this.badge.imageUrl &&
- this.badge.imageUrl.trim() !== '' &&
- this.badge.linkUrl &&
- this.badge.linkUrl.trim() !== '' &&
- !this.isSaving
- );
- },
helpText() {
const placeholders = ['project_path', 'project_id', 'default_branch', 'commit_sha']
.map(placeholder => `<code>%{${placeholder}}</code>`)
@@ -93,11 +88,18 @@ export default {
});
},
},
- submitButtonLabel() {
- if (this.isEditing) {
- return s__('Badges|Save changes');
- }
- return s__('Badges|Add badge');
+ badgeImageUrlExample() {
+ const exampleUrl =
+ 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/badge.svg';
+ return sprintf(s__('Badges|e.g. %{exampleUrl}'), {
+ exampleUrl,
+ });
+ },
+ badgeLinkUrlExample() {
+ const exampleUrl = 'https://example.gitlab.com/%{project_path}';
+ return sprintf(s__('Badges|e.g. %{exampleUrl}'), {
+ exampleUrl,
+ });
},
},
methods: {
@@ -109,7 +111,9 @@ export default {
this.stopEditing();
},
onSubmit() {
- if (!this.canSubmit) {
+ const form = this.$el;
+ if (!form.checkValidity()) {
+ this.wasValidated = true;
return Promise.resolve();
}
@@ -117,6 +121,7 @@ export default {
return this.saveBadge()
.then(() => {
createFlash(s__('Badges|The badge was saved.'), 'notice');
+ this.wasValidated = false;
})
.catch(error => {
createFlash(
@@ -129,6 +134,7 @@ export default {
return this.addBadge()
.then(() => {
createFlash(s__('Badges|A new badge was added.'), 'notice');
+ this.wasValidated = false;
})
.catch(error => {
createFlash(
@@ -138,47 +144,58 @@ export default {
});
},
},
- badgeImageUrlPlaceholder:
- 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/<badge>.svg',
- badgeLinkUrlPlaceholder: 'https://example.gitlab.com/%{project_path}',
};
</script>
<template>
<form
- class="prepend-top-default append-bottom-default"
+ :class="{ 'was-validated': wasValidated }"
+ class="prepend-top-default append-bottom-default needs-validation"
+ novalidate
@submit.prevent.stop="onSubmit"
>
<div class="form-group">
- <label for="badge-link-url">{{ s__('Badges|Link') }}</label>
+ <label
+ for="badge-link-url"
+ class="label-bold"
+ >{{ s__('Badges|Link') }}</label>
+ <p v-html="helpText"></p>
<input
id="badge-link-url"
v-model="linkUrl"
- :placeholder="$options.badgeLinkUrlPlaceholder"
- type="text"
+ type="URL"
class="form-control"
+ required
@input="debouncedPreview"
/>
- <span
- class="form-text text-muted"
- v-html="helpText"
- ></span>
+ <div class="invalid-feedback">
+ {{ s__('Badges|Please fill in a valid URL') }}
+ </div>
+ <span class="form-text text-muted">
+ {{ badgeLinkUrlExample }}
+ </span>
</div>
<div class="form-group">
- <label for="badge-image-url">{{ s__('Badges|Badge image URL') }}</label>
+ <label
+ for="badge-image-url"
+ class="label-bold"
+ >{{ s__('Badges|Badge image URL') }}</label>
+ <p v-html="helpText"></p>
<input
id="badge-image-url"
v-model="imageUrl"
- :placeholder="$options.badgeImageUrlPlaceholder"
- type="text"
+ type="URL"
class="form-control"
+ required
@input="debouncedPreview"
/>
- <span
- class="form-text text-muted"
- v-html="helpText"
- ></span>
+ <div class="invalid-feedback">
+ {{ s__('Badges|Please fill in a valid URL') }}
+ </div>
+ <span class="form-text text-muted">
+ {{ badgeImageUrlExample }}
+ </span>
</div>
<div class="form-group">
@@ -190,7 +207,7 @@ export default {
:link-url="renderedLinkUrl"
/>
<p v-show="isRendering">
- <loading-icon
+ <gl-loading-icon
:inline="true"
/>
</p>
@@ -200,20 +217,32 @@ export default {
>{{ s__('Badges|No image to preview') }}</p>
</div>
- <div class="row-content-block">
+ <div
+ v-if="isEditing"
+ class="row-content-block"
+ >
<loading-button
- :disabled="!canSubmit"
:loading="isSaving"
- :label="submitButtonLabel"
+ :label="s__('Badges|Save changes')"
type="submit"
container-class="btn btn-success"
/>
<button
- v-if="isEditing"
class="btn btn-cancel"
type="button"
@click="onCancel"
>{{ __('Cancel') }}</button>
</div>
+ <div
+ v-else
+ class="form-group"
+ >
+ <loading-button
+ :loading="isSaving"
+ :label="s__('Badges|Add badge')"
+ type="submit"
+ container-class="btn btn-success"
+ />
+ </div>
</form>
</template>
diff --git a/app/assets/javascripts/badges/components/badge_list.vue b/app/assets/javascripts/badges/components/badge_list.vue
index 268968b63b3..5b64ea1080c 100644
--- a/app/assets/javascripts/badges/components/badge_list.vue
+++ b/app/assets/javascripts/badges/components/badge_list.vue
@@ -1,6 +1,6 @@
<script>
import { mapState } from 'vuex';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import BadgeListRow from './badge_list_row.vue';
import { GROUP_BADGE } from '../constants';
@@ -8,7 +8,7 @@ export default {
name: 'BadgeList',
components: {
BadgeListRow,
- LoadingIcon,
+ GlLoadingIcon,
},
computed: {
...mapState(['badges', 'isLoading', 'kind']),
@@ -28,13 +28,13 @@ export default {
{{ s__('Badges|Your badges') }}
<span
v-show="!isLoading"
- class="badge"
+ class="badge badge-pill"
>{{ badges.length }}</span>
</div>
- <loading-icon
+ <gl-loading-icon
v-show="isLoading"
+ :size="2"
class="card-body"
- size="2"
/>
<div
v-if="hasNoBadges"
diff --git a/app/assets/javascripts/badges/components/badge_list_row.vue b/app/assets/javascripts/badges/components/badge_list_row.vue
index 98aa00af0d7..d01465232a7 100644
--- a/app/assets/javascripts/badges/components/badge_list_row.vue
+++ b/app/assets/javascripts/badges/components/badge_list_row.vue
@@ -2,7 +2,7 @@
import { mapActions, mapState } from 'vuex';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import { PROJECT_BADGE } from '../constants';
import Badge from './badge.vue';
@@ -11,7 +11,7 @@ export default {
components: {
Badge,
Icon,
- LoadingIcon,
+ GlLoadingIcon,
},
props: {
badge: {
@@ -43,13 +43,13 @@ export default {
<badge
:image-url="badge.renderedImageUrl"
:link-url="badge.renderedLinkUrl"
- class="table-section section-30"
+ class="table-section section-40"
/>
- <span class="table-section section-50 str-truncated">{{ badge.linkUrl }}</span>
- <div class="table-section section-10">
- <span class="badge">{{ badgeKindText }}</span>
+ <span class="table-section section-30 str-truncated">{{ badge.linkUrl }}</span>
+ <div class="table-section section-15">
+ <span class="badge badge-pill">{{ badgeKindText }}</span>
</div>
- <div class="table-section section-10 table-button-footer">
+ <div class="table-section section-15 table-button-footer">
<div
v-if="canEditBadge"
class="table-action-buttons">
@@ -79,7 +79,7 @@ export default {
name="remove"
/>
</button>
- <loading-icon
+ <gl-loading-icon
v-show="badge.isDeleting"
:inline="true"
/>
diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js
index 00419e80cbb..9a33a060c76 100644
--- a/app/assets/javascripts/behaviors/copy_to_clipboard.js
+++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js
@@ -51,7 +51,7 @@ export default function initCopyToClipboard() {
* the last minute to deconstruct this JSON hash and set the `text/plain` and `text/x-gfm` copy
* data types to the intended values.
*/
- $(document).on('copy', 'body > textarea[readonly]', (e) => {
+ $(document).on('copy', 'body > textarea[readonly]', e => {
const { clipboardData } = e.originalEvent;
if (!clipboardData) return;
diff --git a/app/assets/javascripts/behaviors/details_behavior.js b/app/assets/javascripts/behaviors/details_behavior.js
index 1d63f5baeee..9bdfc21c7e4 100644
--- a/app/assets/javascripts/behaviors/details_behavior.js
+++ b/app/assets/javascripts/behaviors/details_behavior.js
@@ -2,7 +2,9 @@ import $ from 'jquery';
$(() => {
$('body').on('click', '.js-details-target', function target() {
- $(this).closest('.js-details-container').toggleClass('open');
+ $(this)
+ .closest('.js-details-container')
+ .toggleClass('open');
});
// Show details content. Hides link after click.
@@ -13,7 +15,9 @@ $(() => {
//
$('body').on('click', '.js-details-expand', function expand(e) {
e.preventDefault();
- $(this).next('.js-details-content').removeClass('hide');
+ $(this)
+ .next('.js-details-content')
+ .removeClass('hide');
$(this).hide();
const truncatedItem = $(this).siblings('.js-details-short');
diff --git a/app/assets/javascripts/behaviors/index.js b/app/assets/javascripts/behaviors/index.js
index 84fef4d8b4f..8c4eccc34a3 100644
--- a/app/assets/javascripts/behaviors/index.js
+++ b/app/assets/javascripts/behaviors/index.js
@@ -1,15 +1,19 @@
import './autosize';
import './bind_in_out';
import './markdown/render_gfm';
+import initGFMInput from './markdown/gfm_auto_complete';
import initCopyAsGFM from './markdown/copy_as_gfm';
import initCopyToClipboard from './copy_to_clipboard';
import './details_behavior';
import installGlEmojiElement from './gl_emoji';
import './quick_submit';
import './requires_input';
+import initPageShortcuts from './shortcuts';
import './toggler_behavior';
-import '../preview_markdown';
+import './preview_markdown';
installGlEmojiElement();
+initGFMInput();
initCopyAsGFM();
initCopyToClipboard();
+initPageShortcuts();
diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
index 5d7a3bed301..fe02096d903 100644
--- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
@@ -1,4 +1,4 @@
-/* eslint-disable object-shorthand, no-unused-vars, no-use-before-define, max-len, no-restricted-syntax, guard-for-in, no-continue */
+/* eslint-disable object-shorthand, no-unused-vars, no-use-before-define, no-restricted-syntax, guard-for-in, no-continue */
import $ from 'jquery';
import _ from 'underscore';
@@ -34,7 +34,7 @@ const gfmRules = {
},
},
AutolinkFilter: {
- 'a'(el, text) {
+ a(el, text) {
// Fallback on the regular MarkdownFilter's `a` handler.
if (text !== el.getAttribute('href')) return false;
@@ -60,7 +60,7 @@ const gfmRules = {
},
},
ImageLazyLoadFilter: {
- 'img'(el, text) {
+ img(el, text) {
return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`;
},
},
@@ -71,7 +71,7 @@ const gfmRules = {
return CopyAsGFM.nodeToGFM(videoEl);
},
- 'video'(el) {
+ video(el) {
return `![${el.dataset.title}](${el.getAttribute('src')})`;
},
},
@@ -118,11 +118,14 @@ const gfmRules = {
'a[name]:not([href]):empty'(el) {
return el.outerHTML;
},
- 'dl'(el, text) {
- let lines = text.replace(/\n\n/g, '\n').trim().split('\n');
+ dl(el, text) {
+ let lines = text
+ .replace(/\n\n/g, '\n')
+ .trim()
+ .split('\n');
// Add two spaces to the front of subsequent list items lines,
// or leave the line entirely blank.
- lines = lines.map((l) => {
+ lines = lines.map(l => {
const line = l.trim();
if (line.length === 0) return '';
@@ -151,27 +154,30 @@ const gfmRules = {
// Prefixes lines with 4 spaces if the code contains triple backticks
if (lang === '' && text.match(/^```/gm)) {
- return text.split('\n').map((l) => {
- const line = l.trim();
- if (line.length === 0) return '';
-
- return ` ${line}`;
- }).join('\n');
+ return text
+ .split('\n')
+ .map(l => {
+ const line = l.trim();
+ if (line.length === 0) return '';
+
+ return ` ${line}`;
+ })
+ .join('\n');
}
return `\`\`\`${lang}\n${text}\n\`\`\``;
},
'pre > code'(el, text) {
- // Don't wrap code blocks in ``
+ // Don't wrap code blocks in ``
return text;
},
},
MarkdownFilter: {
- 'br'(el) {
+ br(el) {
// Two spaces at the end of a line are turned into a BR
return ' ';
},
- 'code'(el, text) {
+ code(el, text) {
let backtickCount = 1;
const backtickMatch = text.match(/`+/);
if (backtickMatch) {
@@ -183,27 +189,31 @@ const gfmRules = {
return backticks + spaceOrNoSpace + text.trim() + spaceOrNoSpace + backticks;
},
- 'blockquote'(el, text) {
- return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n');
+ blockquote(el, text) {
+ return text
+ .trim()
+ .split('\n')
+ .map(s => `> ${s}`.trim())
+ .join('\n');
},
- 'img'(el) {
+ img(el) {
const imageSrc = el.src;
- const imageUrl = imageSrc && imageSrc !== placeholderImage ? imageSrc : (el.dataset.src || '');
+ const imageUrl = imageSrc && imageSrc !== placeholderImage ? imageSrc : el.dataset.src || '';
return `![${el.getAttribute('alt')}](${imageUrl})`;
},
'a.anchor'(el, text) {
// Don't render a Markdown link for the anchor link inside a heading
return text;
},
- 'a'(el, text) {
+ a(el, text) {
return `[${text}](${el.getAttribute('href')})`;
},
- 'li'(el, text) {
+ li(el, text) {
const lines = text.trim().split('\n');
const firstLine = `- ${lines.shift()}`;
// Add four spaces to the front of subsequent list items lines,
// or leave the line entirely blank.
- const nextLines = lines.map((s) => {
+ const nextLines = lines.map(s => {
if (s.trim().length === 0) return '';
return ` ${s}`;
@@ -211,49 +221,49 @@ const gfmRules = {
return `${firstLine}\n${nextLines.join('\n')}`;
},
- 'ul'(el, text) {
+ ul(el, text) {
return text;
},
- 'ol'(el, text) {
+ ol(el, text) {
// LIs get a `- ` prefix by default, which we replace by `1. ` for ordered lists.
- return text.replace(/^- /mg, '1. ');
+ return text.replace(/^- /gm, '1. ');
},
- 'h1'(el, text) {
+ h1(el, text) {
return `# ${text.trim()}\n`;
},
- 'h2'(el, text) {
+ h2(el, text) {
return `## ${text.trim()}\n`;
},
- 'h3'(el, text) {
+ h3(el, text) {
return `### ${text.trim()}\n`;
},
- 'h4'(el, text) {
+ h4(el, text) {
return `#### ${text.trim()}\n`;
},
- 'h5'(el, text) {
+ h5(el, text) {
return `##### ${text.trim()}\n`;
},
- 'h6'(el, text) {
+ h6(el, text) {
return `###### ${text.trim()}\n`;
},
- 'strong'(el, text) {
+ strong(el, text) {
return `**${text}**`;
},
- 'em'(el, text) {
+ em(el, text) {
return `_${text}_`;
},
- 'del'(el, text) {
+ del(el, text) {
return `~~${text}~~`;
},
- 'hr'(el) {
+ hr(el) {
// extra leading \n is to ensure that there is a blank line between
// a list followed by an hr, otherwise this breaks old redcarpet rendering
return '\n-----\n';
},
- 'p'(el, text) {
+ p(el, text) {
return `${text.trim()}\n`;
},
- 'table'(el) {
+ table(el) {
const theadEl = el.querySelector('thead');
const tbodyEl = el.querySelector('tbody');
if (!theadEl || !tbodyEl) return false;
@@ -263,8 +273,8 @@ const gfmRules = {
return [theadText, tbodyText].join('\n');
},
- 'thead'(el, text) {
- const cells = _.map(el.querySelectorAll('th'), (cell) => {
+ thead(el, text) {
+ const cells = _.map(el.querySelectorAll('th'), cell => {
let chars = CopyAsGFM.nodeToGFM(cell).length + 2;
let before = '';
@@ -296,7 +306,7 @@ const gfmRules = {
return [text, separatorRow].join('\n');
},
- 'tr'(el) {
+ tr(el) {
const cellEls = el.querySelectorAll('td, th');
if (cellEls.length === 0) return false;
@@ -315,8 +325,12 @@ export class CopyAsGFM {
const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
if (isIOS) return;
- $(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
- $(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
+ $(document).on('copy', '.md, .wiki', e => {
+ CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection);
+ });
+ $(document).on('copy', 'pre.code.highlight, .diff-content .line_content', e => {
+ CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection);
+ });
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
}
@@ -356,7 +370,7 @@ export class CopyAsGFM {
// This will break down when the actual code block contains an uneven
// number of backticks, but this is a rare edge case.
const backtickMatch = textBefore.match(/`/g);
- const insideCodeBlock = backtickMatch && (backtickMatch.length % 2) === 1;
+ const insideCodeBlock = backtickMatch && backtickMatch.length % 2 === 1;
if (insideCodeBlock) {
return text;
@@ -393,7 +407,9 @@ export class CopyAsGFM {
let lineSelector = '.line';
if (target) {
- const lineClass = ['left-side', 'right-side'].filter(name => target.classList.contains(name))[0];
+ const lineClass = ['left-side', 'right-side'].filter(name =>
+ target.classList.contains(name),
+ )[0];
if (lineClass) {
lineSelector = `.line_content.${lineClass} ${lineSelector}`;
}
@@ -436,7 +452,8 @@ export class CopyAsGFM {
return node.textContent;
}
- const respectWhitespace = respectWhitespaceParam || (node.nodeName === 'PRE' || node.nodeName === 'CODE');
+ const respectWhitespace =
+ respectWhitespaceParam || (node.nodeName === 'PRE' || node.nodeName === 'CODE');
const text = this.innerGFM(node, respectWhitespace);
diff --git a/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
new file mode 100644
index 00000000000..a303e504cc7
--- /dev/null
+++ b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
@@ -0,0 +1,19 @@
+import $ from 'jquery';
+import { convertPermissionToBoolean } from '~/lib/utils/common_utils';
+import GfmAutoComplete from '~/gfm_auto_complete';
+
+export default function initGFMInput() {
+ $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
+ const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
+ const enableGFM = convertPermissionToBoolean(el.dataset.supportsAutocomplete);
+
+ gfm.setup($(el), {
+ emojis: true,
+ members: enableGFM,
+ issues: enableGFM,
+ milestones: enableGFM,
+ mergeRequests: enableGFM,
+ labels: enableGFM,
+ });
+ });
+}
diff --git a/app/assets/javascripts/behaviors/markdown/highlight_current_user.js b/app/assets/javascripts/behaviors/markdown/highlight_current_user.js
new file mode 100644
index 00000000000..6208b3f0032
--- /dev/null
+++ b/app/assets/javascripts/behaviors/markdown/highlight_current_user.js
@@ -0,0 +1,17 @@
+/**
+ * Highlights the current user in existing elements with a user ID data attribute.
+ *
+ * @param elements DOM elements that represent user mentions
+ */
+export default function highlightCurrentUser(elements) {
+ const currentUserId = gon && gon.current_user_id;
+ if (!currentUserId) {
+ return;
+ }
+
+ elements.forEach(element => {
+ if (parseInt(element.dataset.user, 10) === currentUserId) {
+ element.classList.add('current-user');
+ }
+ });
+}
diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js
index dbff2bd4b10..a2d4331b6d1 100644
--- a/app/assets/javascripts/behaviors/markdown/render_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js
@@ -2,8 +2,9 @@ import $ from 'jquery';
import syntaxHighlight from '~/syntax_highlight';
import renderMath from './render_math';
import renderMermaid from './render_mermaid';
+import highlightCurrentUser from './highlight_current_user';
-// Render Gitlab flavoured Markdown
+// Render GitLab flavoured Markdown
//
// Delegates to syntax highlight and render math & mermaid diagrams.
//
@@ -11,6 +12,7 @@ $.fn.renderGFM = function renderGFM() {
syntaxHighlight(this.find('.js-syntax-highlight'));
renderMath(this.find('.js-render-math'));
renderMermaid(this.find('.js-render-mermaid'));
+ highlightCurrentUser(this.find('.gfm-project_member').get());
return this;
};
diff --git a/app/assets/javascripts/behaviors/markdown/render_math.js b/app/assets/javascripts/behaviors/markdown/render_math.js
index eb4e59d12b1..a68936d79e2 100644
--- a/app/assets/javascripts/behaviors/markdown/render_math.js
+++ b/app/assets/javascripts/behaviors/markdown/render_math.js
@@ -32,7 +32,9 @@ export default function renderMath($els) {
Promise.all([
import(/* webpackChunkName: 'katex' */ 'katex'),
import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
- ]).then(([katex]) => {
- renderWithKaTeX($els, katex);
- }).catch(() => flash(__('An error occurred while rendering KaTeX')));
+ ])
+ .then(([katex]) => {
+ renderWithKaTeX($els, katex);
+ })
+ .catch(() => flash(__('An error occurred while rendering KaTeX')));
}
diff --git a/app/assets/javascripts/behaviors/markdown/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
index 56b1896e9f1..720f30e18e6 100644
--- a/app/assets/javascripts/behaviors/markdown/render_mermaid.js
+++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
@@ -17,41 +17,43 @@ import flash from '~/flash';
export default function renderMermaid($els) {
if (!$els.length) return;
- import(/* webpackChunkName: 'mermaid' */ 'blackst0ne-mermaid').then((mermaid) => {
- mermaid.initialize({
- // mermaid core options
- mermaid: {
- startOnLoad: false,
- },
- // mermaidAPI options
- theme: 'neutral',
- });
+ import(/* webpackChunkName: 'mermaid' */ 'mermaid')
+ .then(mermaid => {
+ mermaid.initialize({
+ // mermaid core options
+ mermaid: {
+ startOnLoad: false,
+ },
+ // mermaidAPI options
+ theme: 'neutral',
+ });
- $els.each((i, el) => {
- const source = el.textContent;
+ $els.each((i, el) => {
+ const source = el.textContent;
- // Remove any extra spans added by the backend syntax highlighting.
- Object.assign(el, { textContent: source });
+ // Remove any extra spans added by the backend syntax highlighting.
+ Object.assign(el, { textContent: source });
- mermaid.init(undefined, el, (id) => {
- const svg = document.getElementById(id);
+ mermaid.init(undefined, el, id => {
+ const svg = document.getElementById(id);
- svg.classList.add('mermaid');
+ svg.classList.add('mermaid');
- // pre > code > svg
- svg.closest('pre').replaceWith(svg);
+ // pre > code > svg
+ svg.closest('pre').replaceWith(svg);
- // We need to add the original source into the DOM to allow Copy-as-GFM
- // to access it.
- const sourceEl = document.createElement('text');
- sourceEl.classList.add('source');
- sourceEl.setAttribute('display', 'none');
- sourceEl.textContent = source;
+ // We need to add the original source into the DOM to allow Copy-as-GFM
+ // to access it.
+ const sourceEl = document.createElement('text');
+ sourceEl.classList.add('source');
+ sourceEl.setAttribute('display', 'none');
+ sourceEl.textContent = source;
- svg.appendChild(sourceEl);
+ svg.appendChild(sourceEl);
+ });
});
+ })
+ .catch(err => {
+ flash(`Can't load mermaid module: ${err}`);
});
- }).catch((err) => {
- flash(`Can't load mermaid module: ${err}`);
- });
}
diff --git a/app/assets/javascripts/behaviors/preview_markdown.js b/app/assets/javascripts/behaviors/preview_markdown.js
new file mode 100644
index 00000000000..35f1bb6b080
--- /dev/null
+++ b/app/assets/javascripts/behaviors/preview_markdown.js
@@ -0,0 +1,230 @@
+/* eslint-disable func-names, no-var, object-shorthand, prefer-arrow-callback */
+
+import $ from 'jquery';
+import axios from '~/lib/utils/axios_utils';
+import flash from '~/flash';
+import { __ } from '~/locale';
+
+// MarkdownPreview
+//
+// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview
+// (including the explanation of quick actions), and showing a warning when
+// more than `x` users are referenced.
+//
+
+var lastTextareaPreviewed;
+var lastTextareaHeight = null;
+var markdownPreview;
+var previewButtonSelector;
+var writeButtonSelector;
+
+function MarkdownPreview() {}
+
+// Minimum number of users referenced before triggering a warning
+MarkdownPreview.prototype.referenceThreshold = 10;
+MarkdownPreview.prototype.emptyMessage = 'Nothing to preview.';
+
+MarkdownPreview.prototype.ajaxCache = {};
+
+MarkdownPreview.prototype.showPreview = function($form) {
+ var mdText;
+ var markdownVersion;
+ var url;
+ var preview = $form.find('.js-md-preview');
+ if (preview.hasClass('md-preview-loading')) {
+ return;
+ }
+
+ mdText = $form.find('textarea.markdown-area').val();
+ markdownVersion = $form.attr('data-markdown-version');
+ url = this.versionedPreviewPath(preview.data('url'), markdownVersion);
+
+ if (mdText.trim().length === 0) {
+ preview.text(this.emptyMessage);
+ this.hideReferencedUsers($form);
+ } else {
+ preview.addClass('md-preview-loading').text('Loading...');
+ this.fetchMarkdownPreview(
+ mdText,
+ url,
+ function(response) {
+ var body;
+ if (response.body.length > 0) {
+ ({ body } = response);
+ } else {
+ body = this.emptyMessage;
+ }
+
+ preview.removeClass('md-preview-loading').html(body);
+ preview.renderGFM();
+ this.renderReferencedUsers(response.references.users, $form);
+
+ if (response.references.commands) {
+ this.renderReferencedCommands(response.references.commands, $form);
+ }
+ }.bind(this),
+ );
+ }
+};
+
+MarkdownPreview.prototype.versionedPreviewPath = function(markdownPreviewPath, markdownVersion) {
+ if (typeof markdownVersion === 'undefined') {
+ return markdownPreviewPath;
+ }
+
+ return `${markdownPreviewPath}${
+ markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
+ }markdown_version=${markdownVersion}`;
+};
+
+MarkdownPreview.prototype.fetchMarkdownPreview = function(text, url, success) {
+ if (!url) {
+ return;
+ }
+ if (text === this.ajaxCache.text) {
+ success(this.ajaxCache.response);
+ return;
+ }
+ axios
+ .post(url, {
+ text,
+ })
+ .then(({ data }) => {
+ this.ajaxCache = {
+ text: text,
+ response: data,
+ };
+ success(data);
+ })
+ .catch(() => flash(__('An error occurred while fetching markdown preview')));
+};
+
+MarkdownPreview.prototype.hideReferencedUsers = function($form) {
+ $form.find('.referenced-users').hide();
+};
+
+MarkdownPreview.prototype.renderReferencedUsers = function(users, $form) {
+ var referencedUsers;
+ referencedUsers = $form.find('.referenced-users');
+ if (referencedUsers.length) {
+ if (users.length >= this.referenceThreshold) {
+ referencedUsers.show();
+ referencedUsers.find('.js-referenced-users-count').text(users.length);
+ } else {
+ referencedUsers.hide();
+ }
+ }
+};
+
+MarkdownPreview.prototype.hideReferencedCommands = function($form) {
+ $form.find('.referenced-commands').hide();
+};
+
+MarkdownPreview.prototype.renderReferencedCommands = function(commands, $form) {
+ var referencedCommands;
+ referencedCommands = $form.find('.referenced-commands');
+ if (commands.length > 0) {
+ referencedCommands.html(commands);
+ referencedCommands.show();
+ } else {
+ referencedCommands.html('');
+ referencedCommands.hide();
+ }
+};
+
+markdownPreview = new MarkdownPreview();
+
+previewButtonSelector = '.js-md-preview-button';
+writeButtonSelector = '.js-md-write-button';
+lastTextareaPreviewed = null;
+const markdownToolbar = $('.md-header-toolbar');
+
+$.fn.setupMarkdownPreview = function() {
+ var $form = $(this);
+ $form.find('textarea.markdown-area').on('input', function() {
+ markdownPreview.hideReferencedUsers($form);
+ });
+};
+
+$(document).on('markdown-preview:show', function(e, $form) {
+ if (!$form) {
+ return;
+ }
+
+ lastTextareaPreviewed = $form.find('textarea.markdown-area');
+ lastTextareaHeight = lastTextareaPreviewed.height();
+
+ // toggle tabs
+ $form
+ .find(writeButtonSelector)
+ .parent()
+ .removeClass('active');
+ $form
+ .find(previewButtonSelector)
+ .parent()
+ .addClass('active');
+
+ // toggle content
+ $form.find('.md-write-holder').hide();
+ $form.find('.md-preview-holder').show();
+ markdownToolbar.removeClass('active');
+ markdownPreview.showPreview($form);
+});
+
+$(document).on('markdown-preview:hide', function(e, $form) {
+ if (!$form) {
+ return;
+ }
+ lastTextareaPreviewed = null;
+
+ if (lastTextareaHeight) {
+ $form.find('textarea.markdown-area').height(lastTextareaHeight);
+ }
+
+ // toggle tabs
+ $form
+ .find(writeButtonSelector)
+ .parent()
+ .addClass('active');
+ $form
+ .find(previewButtonSelector)
+ .parent()
+ .removeClass('active');
+
+ // toggle content
+ $form.find('.md-write-holder').show();
+ $form.find('textarea.markdown-area').focus();
+ $form.find('.md-preview-holder').hide();
+ markdownToolbar.addClass('active');
+
+ markdownPreview.hideReferencedCommands($form);
+});
+
+$(document).on('markdown-preview:toggle', function(e, keyboardEvent) {
+ var $target;
+ $target = $(keyboardEvent.target);
+ if ($target.is('textarea.markdown-area')) {
+ $(document).triggerHandler('markdown-preview:show', [$target.closest('form')]);
+ keyboardEvent.preventDefault();
+ } else if (lastTextareaPreviewed) {
+ $target = lastTextareaPreviewed;
+ $(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]);
+ keyboardEvent.preventDefault();
+ }
+});
+
+$(document).on('click', previewButtonSelector, function(e) {
+ var $form;
+ e.preventDefault();
+ $form = $(this).closest('form');
+ $(document).triggerHandler('markdown-preview:show', [$form]);
+});
+
+$(document).on('click', writeButtonSelector, function(e) {
+ var $form;
+ e.preventDefault();
+ $form = $(this).closest('form');
+ $(document).triggerHandler('markdown-preview:hide', [$form]);
+});
+
+export default MarkdownPreview;
diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js
index b6e2781773c..c1ea67f9293 100644
--- a/app/assets/javascripts/behaviors/quick_submit.js
+++ b/app/assets/javascripts/behaviors/quick_submit.js
@@ -28,7 +28,7 @@ function keyCodeIs(e, keyCode) {
return e.keyCode === keyCode;
}
-$(document).on('keydown.quick_submit', '.js-quick-submit', (e) => {
+$(document).on('keydown.quick_submit', '.js-quick-submit', e => {
// Enter
if (!keyCodeIs(e, 13)) {
return;
@@ -55,23 +55,25 @@ $(document).on('keydown.quick_submit', '.js-quick-submit', (e) => {
// If the user tabs to a submit button on a `js-quick-submit` form, display a
// tooltip to let them know they could've used the hotkey
-$(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', function displayTooltip(e) {
- // Tab
- if (!keyCodeIs(e, 9)) {
- return;
- }
+$(document).on(
+ 'keyup.quick_submit',
+ '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]',
+ function displayTooltip(e) {
+ // Tab
+ if (!keyCodeIs(e, 9)) {
+ return;
+ }
- const $this = $(this);
- const title = isMac() ?
- 'You can also press &#8984;-Enter' :
- 'You can also press Ctrl-Enter';
+ const $this = $(this);
+ const title = isMac() ? 'You can also press &#8984;-Enter' : 'You can also press Ctrl-Enter';
- $this.tooltip({
- container: 'body',
- html: 'true',
- placement: 'top',
- title,
- trigger: 'manual',
- });
- $this.tooltip('show').one('blur click', () => $this.tooltip('hide'));
-});
+ $this.tooltip({
+ container: 'body',
+ html: 'true',
+ placement: 'top',
+ title,
+ trigger: 'manual',
+ });
+ $this.tooltip('show').one('blur click', () => $this.tooltip('hide'));
+ },
+);
diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js
index a8b6dbf0948..c09d9ccddd6 100644
--- a/app/assets/javascripts/behaviors/requires_input.js
+++ b/app/assets/javascripts/behaviors/requires_input.js
@@ -18,7 +18,8 @@ import '../commons/bootstrap';
$.fn.requiresInput = function requiresInput() {
const $form = $(this);
const $button = $('button[type=submit], input[type=submit]', $form);
- const fieldSelector = 'input[required=required], select[required=required], textarea[required=required]';
+ const fieldSelector =
+ 'input[required=required], select[required=required], textarea[required=required]';
function requireInput() {
// Collect the input values of *all* required fields
diff --git a/app/assets/javascripts/behaviors/secret_values.js b/app/assets/javascripts/behaviors/secret_values.js
index 0d6e0dbefcc..f6bf62d734e 100644
--- a/app/assets/javascripts/behaviors/secret_values.js
+++ b/app/assets/javascripts/behaviors/secret_values.js
@@ -32,16 +32,18 @@ export default class SecretValues {
updateDom(isRevealed) {
const values = this.container.querySelectorAll(this.valueSelector);
- values.forEach((value) => {
+ values.forEach(value => {
value.classList.toggle('hide', !isRevealed);
});
const placeholders = this.container.querySelectorAll(this.placeholderSelector);
- placeholders.forEach((placeholder) => {
+ placeholders.forEach(placeholder => {
placeholder.classList.toggle('hide', isRevealed);
});
- this.revealButton.textContent = isRevealed ? n__('Hide value', 'Hide values', values.length) : n__('Reveal value', 'Reveal values', values.length);
+ this.revealButton.textContent = isRevealed
+ ? n__('Hide value', 'Hide values', values.length)
+ : n__('Reveal value', 'Reveal values', values.length);
this.revealButton.dataset.secretRevealStatus = isRevealed;
}
}
diff --git a/app/assets/javascripts/behaviors/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts.js
new file mode 100644
index 00000000000..7987a533ae5
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts.js
@@ -0,0 +1,35 @@
+import Shortcuts from './shortcuts/shortcuts';
+
+export default function initPageShortcuts() {
+ const { page } = document.body.dataset;
+ const pagesWithCustomShortcuts = [
+ 'projects:activity',
+ 'projects:artifacts:browse',
+ 'projects:artifacts:file',
+ 'projects:blame:show',
+ 'projects:blob:show',
+ 'projects:commit:show',
+ 'projects:commits:show',
+ 'projects:find_file:show',
+ 'projects:issues:edit',
+ 'projects:issues:index',
+ 'projects:issues:new',
+ 'projects:issues:show',
+ 'projects:merge_requests:creations:diffs',
+ 'projects:merge_requests:creations:new',
+ 'projects:merge_requests:edit',
+ 'projects:merge_requests:index',
+ 'projects:merge_requests:show',
+ 'projects:network:show',
+ 'projects:show',
+ 'projects:tree:show',
+ 'groups:show',
+ ];
+
+ // the pages above have their own shortcuts sub-classes instantiated elsewhere
+ // TODO: replace this whitelist with something more automated/maintainable
+ if (page && !pagesWithCustomShortcuts.includes(page)) {
+ return new Shortcuts();
+ }
+ return false;
+}
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
new file mode 100644
index 00000000000..8b5a3c1c69d
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
@@ -0,0 +1,126 @@
+import $ from 'jquery';
+import Cookies from 'js-cookie';
+import Mousetrap from 'mousetrap';
+import axios from '../../lib/utils/axios_utils';
+import { refreshCurrentPage, visitUrl } from '../../lib/utils/url_utility';
+import findAndFollowLink from '../../lib/utils/navigation_utility';
+
+const defaultStopCallback = Mousetrap.stopCallback;
+Mousetrap.stopCallback = (e, element, combo) => {
+ if (['ctrl+shift+p', 'command+shift+p'].indexOf(combo) !== -1) {
+ return false;
+ }
+
+ return defaultStopCallback(e, element, combo);
+};
+
+export default class Shortcuts {
+ constructor() {
+ this.onToggleHelp = this.onToggleHelp.bind(this);
+ this.enabledHelp = [];
+
+ Mousetrap.bind('?', this.onToggleHelp);
+ Mousetrap.bind('s', Shortcuts.focusSearch);
+ Mousetrap.bind('f', this.focusFilter.bind(this));
+ Mousetrap.bind('p b', Shortcuts.onTogglePerfBar);
+
+ const findFileURL = document.body.dataset.findFile;
+
+ Mousetrap.bind('shift+t', () => findAndFollowLink('.shortcuts-todos'));
+ Mousetrap.bind('shift+a', () => findAndFollowLink('.dashboard-shortcuts-activity'));
+ Mousetrap.bind('shift+i', () => findAndFollowLink('.dashboard-shortcuts-issues'));
+ Mousetrap.bind('shift+m', () => findAndFollowLink('.dashboard-shortcuts-merge_requests'));
+ Mousetrap.bind('shift+p', () => findAndFollowLink('.dashboard-shortcuts-projects'));
+ Mousetrap.bind('shift+g', () => findAndFollowLink('.dashboard-shortcuts-groups'));
+ Mousetrap.bind('shift+l', () => findAndFollowLink('.dashboard-shortcuts-milestones'));
+ Mousetrap.bind('shift+s', () => findAndFollowLink('.dashboard-shortcuts-snippets'));
+
+ Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], Shortcuts.toggleMarkdownPreview);
+
+ if (typeof findFileURL !== 'undefined' && findFileURL !== null) {
+ Mousetrap.bind('t', () => {
+ visitUrl(findFileURL);
+ });
+ }
+
+ $(document).on('click.more_help', '.js-more-help-button', function clickMoreHelp(e) {
+ $(this).remove();
+ $('.hidden-shortcut').show();
+ e.preventDefault();
+ });
+ }
+
+ onToggleHelp(e) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+
+ Shortcuts.toggleHelp(this.enabledHelp);
+ }
+
+ static onTogglePerfBar(e) {
+ e.preventDefault();
+ const performanceBarCookieName = 'perf_bar_enabled';
+ if (Cookies.get(performanceBarCookieName) === 'true') {
+ Cookies.set(performanceBarCookieName, 'false', { path: '/' });
+ } else {
+ Cookies.set(performanceBarCookieName, 'true', { path: '/' });
+ }
+ refreshCurrentPage();
+ }
+
+ static toggleMarkdownPreview(e) {
+ // Check if short-cut was triggered while in Write Mode
+ const $target = $(e.target);
+ const $form = $target.closest('form');
+
+ if ($target.hasClass('js-note-text')) {
+ $('.js-md-preview-button', $form).focus();
+ }
+ $(document).triggerHandler('markdown-preview:toggle', [e]);
+ }
+
+ static toggleHelp(location) {
+ const $modal = $('#modal-shortcuts');
+
+ if ($modal.length) {
+ $modal.modal('toggle');
+ return null;
+ }
+
+ return axios
+ .get(gon.shortcuts_path, {
+ responseType: 'text',
+ })
+ .then(({ data }) => {
+ $.globalEval(data);
+
+ if (location && location.length > 0) {
+ const results = [];
+ for (let i = 0, len = location.length; i < len; i += 1) {
+ results.push($(location[i]).show());
+ }
+ return results;
+ }
+
+ $('.hidden-shortcut').show();
+ return $('.js-more-help-button').remove();
+ });
+ }
+
+ focusFilter(e) {
+ if (!this.filterInput) {
+ this.filterInput = $('input[type=search]', '.nav-controls');
+ }
+ this.filterInput.focus();
+ e.preventDefault();
+ }
+
+ static focusSearch(e) {
+ $('#search').focus();
+
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ }
+}
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
new file mode 100644
index 00000000000..052e33b4a2b
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
@@ -0,0 +1,26 @@
+import Mousetrap from 'mousetrap';
+import { getLocationHash, visitUrl } from '../../lib/utils/url_utility';
+import Shortcuts from './shortcuts';
+
+const defaults = {
+ skipResetBindings: false,
+ fileBlobPermalinkUrl: null,
+};
+
+export default class ShortcutsBlob extends Shortcuts {
+ constructor(opts) {
+ const options = Object.assign({}, defaults, opts);
+ super(options.skipResetBindings);
+ this.options = options;
+
+ Mousetrap.bind('y', this.moveToFilePermalink.bind(this));
+ }
+
+ moveToFilePermalink() {
+ if (this.options.fileBlobPermalinkUrl) {
+ const hash = getLocationHash();
+ const hashUrlString = hash ? `#${hash}` : '';
+ visitUrl(`${this.options.fileBlobPermalinkUrl}${hashUrlString}`);
+ }
+ }
+}
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js
index 8658081c6c2..8658081c6c2 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
new file mode 100644
index 00000000000..5e48bf5a35c
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
@@ -0,0 +1,77 @@
+import $ from 'jquery';
+import Mousetrap from 'mousetrap';
+import _ from 'underscore';
+import Sidebar from '../../right_sidebar';
+import Shortcuts from './shortcuts';
+import { CopyAsGFM } from '../markdown/copy_as_gfm';
+
+export default class ShortcutsIssuable extends Shortcuts {
+ constructor(isMergeRequest) {
+ super();
+
+ Mousetrap.bind('a', () => ShortcutsIssuable.openSidebarDropdown('assignee'));
+ Mousetrap.bind('m', () => ShortcutsIssuable.openSidebarDropdown('milestone'));
+ Mousetrap.bind('l', () => ShortcutsIssuable.openSidebarDropdown('labels'));
+ Mousetrap.bind('r', ShortcutsIssuable.replyWithSelectedText);
+ Mousetrap.bind('e', ShortcutsIssuable.editIssue);
+
+ if (isMergeRequest) {
+ this.enabledHelp.push('.hidden-shortcut.merge_requests');
+ } else {
+ this.enabledHelp.push('.hidden-shortcut.issues');
+ }
+ }
+
+ static replyWithSelectedText() {
+ const $replyField = $('.js-main-target-form .js-vue-comment-form');
+ const documentFragment = window.gl.utils.getSelectedFragment();
+
+ if (!$replyField.length) {
+ return false;
+ }
+
+ if (!documentFragment) {
+ $replyField.focus();
+ return false;
+ }
+
+ const el = CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true));
+ const selected = CopyAsGFM.nodeToGFM(el);
+
+ if (selected.trim() === '') {
+ return false;
+ }
+
+ const quote = _.map(selected.split('\n'), val => `${`> ${val}`.trim()}\n`);
+
+ // If replyField already has some content, add a newline before our quote
+ const separator = ($replyField.val().trim() !== '' && '\n\n') || '';
+ $replyField
+ .val((a, current) => `${current}${separator}${quote.join('')}\n`)
+ .trigger('input')
+ .trigger('change');
+
+ // Trigger autosize
+ const event = document.createEvent('Event');
+ event.initEvent('autosize:update', true, false);
+ $replyField.get(0).dispatchEvent(event);
+
+ // Focus the input field
+ $replyField.focus();
+
+ return false;
+ }
+
+ static editIssue() {
+ // Need to click the element as on issues, editing is inline
+ // on merge request, editing is on a different page
+ document.querySelector('.js-issuable-edit').click();
+
+ return false;
+ }
+
+ static openSidebarDropdown(name) {
+ Sidebar.instance.openDropdown(name);
+ return false;
+ }
+}
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
new file mode 100644
index 00000000000..fa9b2c9f755
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
@@ -0,0 +1,28 @@
+import Mousetrap from 'mousetrap';
+import findAndFollowLink from '../../lib/utils/navigation_utility';
+import Shortcuts from './shortcuts';
+
+export default class ShortcutsNavigation extends Shortcuts {
+ constructor() {
+ super();
+
+ Mousetrap.bind('g p', () => findAndFollowLink('.shortcuts-project'));
+ Mousetrap.bind('g v', () => findAndFollowLink('.shortcuts-project-activity'));
+ Mousetrap.bind('g f', () => findAndFollowLink('.shortcuts-tree'));
+ Mousetrap.bind('g c', () => findAndFollowLink('.shortcuts-commits'));
+ Mousetrap.bind('g j', () => findAndFollowLink('.shortcuts-builds'));
+ Mousetrap.bind('g n', () => findAndFollowLink('.shortcuts-network'));
+ Mousetrap.bind('g d', () => findAndFollowLink('.shortcuts-repository-charts'));
+ Mousetrap.bind('g i', () => findAndFollowLink('.shortcuts-issues'));
+ Mousetrap.bind('g b', () => findAndFollowLink('.shortcuts-issue-boards'));
+ Mousetrap.bind('g m', () => findAndFollowLink('.shortcuts-merge_requests'));
+ Mousetrap.bind('g w', () => findAndFollowLink('.shortcuts-wiki'));
+ Mousetrap.bind('g s', () => findAndFollowLink('.shortcuts-snippets'));
+ Mousetrap.bind('g k', () => findAndFollowLink('.shortcuts-kubernetes'));
+ Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-environments'));
+ Mousetrap.bind('g l', () => findAndFollowLink('.shortcuts-metrics'));
+ Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue'));
+
+ this.enabledHelp.push('.hidden-shortcut.project');
+ }
+}
diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js
index a88c280fa3b..a88c280fa3b 100644
--- a/app/assets/javascripts/shortcuts_network.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
new file mode 100644
index 00000000000..8b7e6a56d25
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
@@ -0,0 +1,14 @@
+import Mousetrap from 'mousetrap';
+import ShortcutsNavigation from './shortcuts_navigation';
+import findAndFollowLink from '../../lib/utils/navigation_utility';
+
+export default class ShortcutsWiki extends ShortcutsNavigation {
+ constructor() {
+ super();
+ Mousetrap.bind('e', ShortcutsWiki.editWiki);
+ }
+
+ static editWiki() {
+ findAndFollowLink('.js-wiki-edit');
+ }
+}
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js
index 4446be0e52f..ef8b8788abf 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.js
+++ b/app/assets/javascripts/behaviors/toggler_behavior.js
@@ -18,9 +18,7 @@ $(() => {
.toggleClass('fa-chevron-up', toggleState)
.toggleClass('fa-chevron-down', toggleState !== undefined ? !toggleState : undefined);
- $container
- .find('.js-toggle-content')
- .toggle(toggleState);
+ $container.find('.js-toggle-content').toggle(toggleState);
}
$('body').on('click', '.js-toggle-button', function toggleButton(e) {
diff --git a/app/assets/javascripts/blob/3d_viewer/index.js b/app/assets/javascripts/blob/3d_viewer/index.js
index 68d4ddad551..2d4f45cc365 100644
--- a/app/assets/javascripts/blob/3d_viewer/index.js
+++ b/app/assets/javascripts/blob/3d_viewer/index.js
@@ -18,27 +18,19 @@ export default class Renderer {
this.loader = new STLLoader();
this.fov = 45;
- this.camera = new THREE.PerspectiveCamera(
- this.fov,
- this.width / this.height,
- 1,
- 1000,
- );
+ this.camera = new THREE.PerspectiveCamera(this.fov, this.width / this.height, 1, 1000);
this.scene = new THREE.Scene();
this.scene.add(this.camera);
- // Setup the viewer
+ // Set up the viewer
this.setupRenderer();
this.setupGrid();
this.setupLight();
- // Setup OrbitControls
- this.controls = new OrbitControls(
- this.camera,
- this.renderer.domElement,
- );
+ // Set up OrbitControls
+ this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.minDistance = 5;
this.controls.maxDistance = 30;
this.controls.enableKeys = false;
@@ -51,47 +43,32 @@ export default class Renderer {
antialias: true,
});
- this.renderer.setClearColor(0xFFFFFF);
+ this.renderer.setClearColor(0xffffff);
this.renderer.setPixelRatio(window.devicePixelRatio);
- this.renderer.setSize(
- this.width,
- this.height,
- );
+ this.renderer.setSize(this.width, this.height);
}
setupLight() {
// Point light illuminates the object
- const pointLight = new THREE.PointLight(
- 0xFFFFFF,
- 2,
- 0,
- );
+ const pointLight = new THREE.PointLight(0xffffff, 2, 0);
pointLight.castShadow = true;
this.camera.add(pointLight);
// Ambient light illuminates the scene
- const ambientLight = new THREE.AmbientLight(
- 0xFFFFFF,
- 1,
- );
+ const ambientLight = new THREE.AmbientLight(0xffffff, 1);
this.scene.add(ambientLight);
}
setupGrid() {
- this.grid = new THREE.GridHelper(
- 20,
- 20,
- 0x000000,
- 0x000000,
- );
+ this.grid = new THREE.GridHelper(20, 20, 0x000000, 0x000000);
this.scene.add(this.grid);
}
loadFile() {
- this.loader.load(this.container.dataset.endpoint, (geo) => {
+ this.loader.load(this.container.dataset.endpoint, geo => {
const obj = new MeshObject(geo);
this.objects.push(obj);
@@ -116,30 +93,23 @@ export default class Renderer {
}
render() {
- this.renderer.render(
- this.scene,
- this.camera,
- );
+ this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.renderWrapper);
}
changeObjectMaterials(type) {
- this.objects.forEach((obj) => {
+ this.objects.forEach(obj => {
obj.changeMaterial(type);
});
}
setDefaultCameraPosition() {
const obj = this.objects[0];
- const radius = (obj.geometry.boundingSphere.radius / 1.5);
- const dist = radius / (Math.sin((this.fov * (Math.PI / 180)) / 2));
-
- this.camera.position.set(
- 0,
- dist + 1,
- dist,
- );
+ const radius = obj.geometry.boundingSphere.radius / 1.5;
+ const dist = radius / Math.sin((this.fov * (Math.PI / 180)) / 2);
+
+ this.camera.position.set(0, dist + 1, dist);
this.camera.lookAt(this.grid);
this.controls.update();
diff --git a/app/assets/javascripts/blob/3d_viewer/mesh_object.js b/app/assets/javascripts/blob/3d_viewer/mesh_object.js
index 96758884abf..cb7fcff8674 100644
--- a/app/assets/javascripts/blob/3d_viewer/mesh_object.js
+++ b/app/assets/javascripts/blob/3d_viewer/mesh_object.js
@@ -1,10 +1,6 @@
-import {
- Matrix4,
- MeshLambertMaterial,
- Mesh,
-} from 'three/build/three.module';
+import { Matrix4, MeshLambertMaterial, Mesh } from 'three/build/three.module';
-const defaultColor = 0xE24329;
+const defaultColor = 0xe24329;
const materials = {
default: new MeshLambertMaterial({
color: defaultColor,
@@ -17,10 +13,7 @@ const materials = {
export default class MeshObject extends Mesh {
constructor(geo) {
- super(
- geo,
- materials.default,
- );
+ super(geo, materials.default);
this.geometry.computeBoundingSphere();
@@ -29,13 +22,7 @@ export default class MeshObject extends Mesh {
if (this.geometry.boundingSphere.radius > 4) {
const scale = 4 / this.geometry.boundingSphere.radius;
- this.geometry.applyMatrix(
- new Matrix4().makeScale(
- scale,
- scale,
- scale,
- ),
- );
+ this.geometry.applyMatrix(new Matrix4().makeScale(scale, scale, scale));
this.geometry.computeBoundingSphere();
this.position.x = -this.geometry.boundingSphere.center.x;
diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
index 7986287f7e7..75777b910ca 100644
--- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
+++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
@@ -42,7 +42,7 @@ class BalsamiqViewer {
this.initDatabase(loadEvent.target.response);
const previews = this.getPreviews();
- previews.forEach((preview) => {
+ previews.forEach(preview => {
const renderedPreview = this.renderPreview(preview);
container.appendChild(renderedPreview);
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js
index ff1739b1679..cd3251ad1ca 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js
@@ -41,39 +41,45 @@ export default class BlobFileDropzone {
addRemoveLinks: true,
previewsContainer: '.dropzone-previews',
headers: csrf.headers,
- init: function () {
- this.on('addedfile', function () {
+ init: function() {
+ this.on('addedfile', function() {
toggleLoading(submitButton, submitButtonLoadingIcon, false);
dropzoneMessage.addClass(HIDDEN_CLASS);
- $('.dropzone-alerts').html('').hide();
+ $('.dropzone-alerts')
+ .html('')
+ .hide();
});
- this.on('removedfile', function () {
+ this.on('removedfile', function() {
toggleLoading(submitButton, submitButtonLoadingIcon, false);
dropzoneMessage.removeClass(HIDDEN_CLASS);
});
- this.on('success', function (header, response) {
+ this.on('success', function(header, response) {
$('#modal-upload-blob').modal('hide');
visitUrl(response.filePath);
});
- this.on('maxfilesexceeded', function (file) {
+ this.on('maxfilesexceeded', function(file) {
dropzoneMessage.addClass(HIDDEN_CLASS);
this.removeFile(file);
});
- this.on('sending', function (file, xhr, formData) {
+ this.on('sending', function(file, xhr, formData) {
formData.append('branch_name', form.find('.js-branch-name').val());
formData.append('create_merge_request', form.find('.js-create-merge-request').val());
formData.append('commit_message', form.find('.js-commit-message').val());
});
},
// Override behavior of adding error underneath preview
- error: function (file, errorMessage) {
- const stripped = $('<div/>').html(errorMessage).text();
- $('.dropzone-alerts').html(`Error uploading file: "${stripped}"`).show();
+ error: function(file, errorMessage) {
+ const stripped = $('<div/>')
+ .html(errorMessage)
+ .text();
+ $('.dropzone-alerts')
+ .html(`Error uploading file: "${stripped}"`)
+ .show();
this.removeFile(file);
},
});
- submitButton.on('click', (e) => {
+ submitButton.on('click', e => {
e.preventDefault();
e.stopPropagation();
if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
diff --git a/app/assets/javascripts/blob/blob_line_permalink_updater.js b/app/assets/javascripts/blob/blob_line_permalink_updater.js
index d36d9f0de2d..62f0a56ed75 100644
--- a/app/assets/javascripts/blob/blob_line_permalink_updater.js
+++ b/app/assets/javascripts/blob/blob_line_permalink_updater.js
@@ -2,17 +2,19 @@ import { getLocationHash } from '../lib/utils/url_utility';
const lineNumberRe = /^L[0-9]+/;
-const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => {
+const updateLineNumbersOnBlobPermalinks = linksToUpdate => {
const hash = getLocationHash();
if (hash && lineNumberRe.test(hash)) {
const hashUrlString = `#${hash}`;
- [].concat(Array.prototype.slice.call(linksToUpdate)).forEach((permalinkButton) => {
- const baseHref = permalinkButton.getAttribute('data-original-href') || (() => {
- const href = permalinkButton.getAttribute('href');
- permalinkButton.setAttribute('data-original-href', href);
- return href;
- })();
+ [].concat(Array.prototype.slice.call(linksToUpdate)).forEach(permalinkButton => {
+ const baseHref =
+ permalinkButton.getAttribute('data-original-href') ||
+ (() => {
+ const href = permalinkButton.getAttribute('href');
+ permalinkButton.setAttribute('data-original-href', href);
+ return href;
+ })();
permalinkButton.setAttribute('href', `${baseHref}${hashUrlString}`);
});
}
@@ -26,7 +28,7 @@ function BlobLinePermalinkUpdater(blobContentHolder, lineNumberSelector, element
}, 0);
};
- blobContentHolder.addEventListener('click', (e) => {
+ blobContentHolder.addEventListener('click', e => {
if (e.target.matches(lineNumberSelector)) {
updateBlameAndBlobPermalinkCb();
}
diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js
index ff1cbcad145..addacf29f1e 100644
--- a/app/assets/javascripts/blob/file_template_mediator.js
+++ b/app/assets/javascripts/blob/file_template_mediator.js
@@ -1,4 +1,4 @@
-/* eslint-disable class-methods-use-this */
+import Api from '~/api';
import $ from 'jquery';
import Flash from '../flash';
@@ -9,9 +9,10 @@ import GitignoreSelector from './template_selectors/gitignore_selector';
import LicenseSelector from './template_selectors/license_selector';
export default class FileTemplateMediator {
- constructor({ editor, currentAction }) {
+ constructor({ editor, currentAction, projectId }) {
this.editor = editor;
this.currentAction = currentAction;
+ this.projectId = projectId;
this.initTemplateSelectors();
this.initTemplateTypeSelector();
@@ -33,15 +34,14 @@ export default class FileTemplateMediator {
initTemplateTypeSelector() {
this.typeSelector = new FileTemplateTypeSelector({
mediator: this,
- dropdownData: this.templateSelectors
- .map((templateSelector) => {
- const cfg = templateSelector.config;
-
- return {
- name: cfg.name,
- key: cfg.key,
- };
- }),
+ dropdownData: this.templateSelectors.map(templateSelector => {
+ const cfg = templateSelector.config;
+
+ return {
+ name: cfg.name,
+ key: cfg.key,
+ };
+ }),
});
}
@@ -89,7 +89,7 @@ export default class FileTemplateMediator {
}
listenForPreviewMode() {
- this.$navLinks.on('click', 'a', (e) => {
+ this.$navLinks.on('click', 'a', e => {
const urlPieces = e.target.href.split('#');
const hash = urlPieces[1];
if (hash === 'preview') {
@@ -105,7 +105,7 @@ export default class FileTemplateMediator {
e.preventDefault();
}
- this.templateSelectors.forEach((selector) => {
+ this.templateSelectors.forEach(selector => {
if (selector.config.key === item.key) {
selector.show();
} else {
@@ -126,8 +126,8 @@ export default class FileTemplateMediator {
selector.renderLoading();
// in case undo menu is already already there
this.destroyUndoMenu();
- this.fetchFileTemplate(selector.config.endpoint, query, data)
- .then((file) => {
+ this.fetchFileTemplate(selector.config.type, query, data)
+ .then(file => {
this.showUndoMenu();
this.setEditorContent(file);
this.setFilename(selector.config.name);
@@ -138,7 +138,7 @@ export default class FileTemplateMediator {
displayMatchedTemplateSelector() {
const currentInput = this.getFilename();
- this.templateSelectors.forEach((selector) => {
+ this.templateSelectors.forEach(selector => {
const match = selector.config.pattern.test(currentInput);
if (match) {
@@ -149,15 +149,11 @@ export default class FileTemplateMediator {
});
}
- fetchFileTemplate(apiCall, query, data) {
- return new Promise((resolve) => {
+ fetchFileTemplate(type, query, data = {}) {
+ return new Promise(resolve => {
const resolveFile = file => resolve(file);
- if (!data) {
- apiCall(query, resolveFile);
- } else {
- apiCall(query, data, resolveFile);
- }
+ Api.projectTemplate(this.projectId, type, query, data, resolveFile);
});
}
diff --git a/app/assets/javascripts/blob/file_template_selector.js b/app/assets/javascripts/blob/file_template_selector.js
index 02228434a29..476901aae75 100644
--- a/app/assets/javascripts/blob/file_template_selector.js
+++ b/app/assets/javascripts/blob/file_template_selector.js
@@ -45,15 +45,11 @@ export default class FileTemplateSelector {
}
renderLoading() {
- this.$loadingIcon
- .addClass('fa-spinner fa-spin')
- .removeClass('fa-chevron-down');
+ this.$loadingIcon.addClass('fa-spinner fa-spin').removeClass('fa-chevron-down');
}
renderLoaded() {
- this.$loadingIcon
- .addClass('fa-chevron-down')
- .removeClass('fa-spinner fa-spin');
+ this.$loadingIcon.addClass('fa-chevron-down').removeClass('fa-spinner fa-spin');
}
reportSelection(options) {
diff --git a/app/assets/javascripts/blob/notebook/index.js b/app/assets/javascripts/blob/notebook/index.js
index 6f1350e80fc..071022a9a75 100644
--- a/app/assets/javascripts/blob/notebook/index.js
+++ b/app/assets/javascripts/blob/notebook/index.js
@@ -40,13 +40,14 @@ export default () => {
},
methods: {
loadFile() {
- axios.get(el.dataset.endpoint)
+ axios
+ .get(el.dataset.endpoint)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
this.json = data;
this.loading = false;
})
- .catch((e) => {
+ .catch(e => {
if (e.status !== 200) {
this.loadError = true;
}
diff --git a/app/assets/javascripts/blob/sketch/index.js b/app/assets/javascripts/blob/sketch/index.js
index 13318c58006..57c1baa9886 100644
--- a/app/assets/javascripts/blob/sketch/index.js
+++ b/app/assets/javascripts/blob/sketch/index.js
@@ -13,7 +13,7 @@ export default class SketchLoader {
return this.getZipFile()
.then(data => JSZip.loadAsync(data))
.then(asyncResult => asyncResult.files['previews/preview.png'].async('uint8array'))
- .then((content) => {
+ .then(content => {
const url = window.URL || window.webkitURL;
const blob = new Blob([new Uint8Array(content)], {
type: 'image/png',
diff --git a/app/assets/javascripts/blob/stl_viewer.js b/app/assets/javascripts/blob/stl_viewer.js
index 339906adc34..f129b6e631e 100644
--- a/app/assets/javascripts/blob/stl_viewer.js
+++ b/app/assets/javascripts/blob/stl_viewer.js
@@ -3,8 +3,8 @@ import Renderer from './3d_viewer';
export default () => {
const viewer = new Renderer(document.getElementById('js-stl-viewer'));
- [].slice.call(document.querySelectorAll('.js-material-changer')).forEach((el) => {
- el.addEventListener('click', (e) => {
+ [].slice.call(document.querySelectorAll('.js-material-changer')).forEach(el => {
+ el.addEventListener('click', e => {
const { target } = e;
e.preventDefault();
diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js
index 9dfdb06007d..37e348d93d3 100644
--- a/app/assets/javascripts/blob/template_selector.js
+++ b/app/assets/javascripts/blob/template_selector.js
@@ -66,9 +66,6 @@ export default class TemplateSelector {
// be added by all subclasses.
}
- // To be implemented on the extending class
- // e.g. Api.gitlabCiYml(query.name, file => this.setEditorContent(file));
-
setEditorContent(file, { skipFocus } = {}) {
if (!file) return;
@@ -84,14 +81,10 @@ export default class TemplateSelector {
}
startLoadingSpinner() {
- this.$dropdownIcon
- .addClass('fa-spinner fa-spin')
- .removeClass('fa-chevron-down');
+ this.$dropdownIcon.addClass('fa-spinner fa-spin').removeClass('fa-chevron-down');
}
stopLoadingSpinner() {
- this.$dropdownIcon
- .addClass('fa-chevron-down')
- .removeClass('fa-spinner fa-spin');
+ this.$dropdownIcon.addClass('fa-chevron-down').removeClass('fa-spinner fa-spin');
}
}
diff --git a/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js b/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js
index 9c41e429c8d..43f7aead8b9 100644
--- a/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js
@@ -1,5 +1,3 @@
-import Api from '../../api';
-
import FileTemplateSelector from '../file_template_selector';
export default class BlobCiYamlSelector extends FileTemplateSelector {
@@ -9,7 +7,7 @@ export default class BlobCiYamlSelector extends FileTemplateSelector {
key: 'gitlab-ci-yaml',
name: '.gitlab-ci.yml',
pattern: /(.gitlab-ci.yml)/,
- endpoint: Api.gitlabCiYml,
+ type: 'gitlab_ci_ymls',
dropdown: '.js-gitlab-ci-yml-selector',
wrapper: '.js-gitlab-ci-yml-selector-wrap',
};
diff --git a/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js b/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js
index 45fb614fe00..4718b642617 100644
--- a/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js
@@ -1,5 +1,3 @@
-import Api from '../../api';
-
import FileTemplateSelector from '../file_template_selector';
export default class DockerfileSelector extends FileTemplateSelector {
@@ -9,7 +7,7 @@ export default class DockerfileSelector extends FileTemplateSelector {
key: 'dockerfile',
name: 'Dockerfile',
pattern: /(Dockerfile)/,
- endpoint: Api.dockerfileYml,
+ type: 'dockerfiles',
dropdown: '.js-dockerfile-selector',
wrapper: '.js-dockerfile-selector-wrap',
};
diff --git a/app/assets/javascripts/blob/template_selectors/gitignore_selector.js b/app/assets/javascripts/blob/template_selectors/gitignore_selector.js
index a894953cc86..a8067ec5c84 100644
--- a/app/assets/javascripts/blob/template_selectors/gitignore_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/gitignore_selector.js
@@ -1,5 +1,3 @@
-import Api from '../../api';
-
import FileTemplateSelector from '../file_template_selector';
export default class BlobGitignoreSelector extends FileTemplateSelector {
@@ -9,7 +7,7 @@ export default class BlobGitignoreSelector extends FileTemplateSelector {
key: 'gitignore',
name: '.gitignore',
pattern: /(.gitignore)/,
- endpoint: Api.gitignoreText,
+ type: 'gitignores',
dropdown: '.js-gitignore-selector',
wrapper: '.js-gitignore-selector-wrap',
};
diff --git a/app/assets/javascripts/blob/template_selectors/license_selector.js b/app/assets/javascripts/blob/template_selectors/license_selector.js
index b7c4da0f62e..d01ab9257d6 100644
--- a/app/assets/javascripts/blob/template_selectors/license_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/license_selector.js
@@ -1,5 +1,3 @@
-import Api from '../../api';
-
import FileTemplateSelector from '../file_template_selector';
export default class BlobLicenseSelector extends FileTemplateSelector {
@@ -9,7 +7,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector {
key: 'license',
name: 'LICENSE',
pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
- endpoint: Api.licenseText,
+ type: 'licenses',
dropdown: '.js-license-selector',
wrapper: '.js-license-selector-wrap',
};
@@ -24,7 +22,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector {
search: {
fields: ['name'],
},
- clicked: (options) => {
+ clicked: options => {
const { e } = options;
const el = options.$el;
const query = options.selectedObj;
diff --git a/app/assets/javascripts/blob/template_selectors/type_selector.js b/app/assets/javascripts/blob/template_selectors/type_selector.js
index a09381014a7..db3c144cbe3 100644
--- a/app/assets/javascripts/blob/template_selectors/type_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/type_selector.js
@@ -21,5 +21,4 @@ export default class FileTemplateTypeSelector extends FileTemplateSelector {
text: item => item.name,
});
}
-
}
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index 5485248cfaf..befa1dc455f 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -22,9 +22,8 @@ export default class BlobViewer {
const viewer = document.querySelector('.blob-viewer[data-type="rich"]');
if (!viewer || !viewer.dataset.richType) return;
- const initViewer = promise => promise
- .then(module => module.default(viewer))
- .catch((error) => {
+ const initViewer = promise =>
+ promise.then(module => module.default(viewer)).catch(error => {
Flash('Error loading file viewer.');
throw error;
});
@@ -79,10 +78,9 @@ export default class BlobViewer {
initBindings() {
if (this.switcherBtns.length) {
- Array.from(this.switcherBtns)
- .forEach((el) => {
- el.addEventListener('click', this.switchViewHandler.bind(this));
- });
+ Array.from(this.switcherBtns).forEach(el => {
+ el.addEventListener('click', this.switchViewHandler.bind(this));
+ });
}
if (this.copySourceBtn) {
@@ -109,7 +107,10 @@ export default class BlobViewer {
this.copySourceBtn.setAttribute('title', 'Copy source to clipboard');
this.copySourceBtn.classList.remove('disabled');
} else if (this.activeViewer === this.simpleViewer) {
- this.copySourceBtn.setAttribute('title', 'Wait for the source to load to copy it to the clipboard');
+ this.copySourceBtn.setAttribute(
+ 'title',
+ 'Wait for the source to load to copy it to the clipboard',
+ );
this.copySourceBtn.classList.add('disabled');
} else {
this.copySourceBtn.setAttribute('title', 'Switch to the source to copy it to the clipboard');
@@ -147,15 +148,15 @@ export default class BlobViewer {
this.toggleCopyButtonState();
BlobViewer.loadViewer(newViewer)
- .then((viewer) => {
- $(viewer).renderGFM();
+ .then(viewer => {
+ $(viewer).renderGFM();
- this.$fileHolder.trigger('highlight:line');
- handleLocationHash();
+ this.$fileHolder.trigger('highlight:line');
+ handleLocationHash();
- this.toggleCopyButtonState();
- })
- .catch(() => new Flash('Error loading viewer'));
+ this.toggleCopyButtonState();
+ })
+ .catch(() => new Flash('Error loading viewer'));
}
static loadViewer(viewerParam) {
@@ -168,12 +169,11 @@ export default class BlobViewer {
viewer.setAttribute('data-loading', 'true');
- return axios.get(url)
- .then(({ data }) => {
- viewer.innerHTML = data.html;
- viewer.setAttribute('data-loaded', 'true');
+ return axios.get(url).then(({ data }) => {
+ viewer.innerHTML = data.html;
+ viewer.setAttribute('data-loaded', 'true');
- return viewer;
- });
+ return viewer;
+ });
}
}
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index a603d89b84a..ec27ae8c291 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -13,10 +13,11 @@ export default () => {
if (editBlobForm.length) {
const urlRoot = editBlobForm.data('relativeUrlRoot');
const assetsPath = editBlobForm.data('assetsPrefix');
- const blobLanguage = editBlobForm.data('blobLanguage');
+ const filePath = editBlobForm.data('blobFilename');
const currentAction = $('.js-file-title').data('currentAction');
+ const projectId = editBlobForm.data('project-id');
- new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction);
+ new EditBlob(`${urlRoot}${assetsPath}`, filePath, currentAction, projectId);
new NewCommitForm(editBlobForm);
}
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index 82a3d494b67..6e19548eed2 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -5,18 +5,20 @@ import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { __ } from '~/locale';
import TemplateSelectorMediator from '../blob/file_template_mediator';
+import getModeByFileExtension from '~/lib/utils/ace_utils';
export default class EditBlob {
- constructor(assetsPath, aceMode, currentAction) {
+ constructor(assetsPath, aceMode, currentAction, projectId) {
this.configureAceEditor(aceMode, assetsPath);
this.initModePanesAndLinks();
this.initSoftWrap();
- this.initFileSelectors(currentAction);
+ this.initFileSelectors(currentAction, projectId);
}
- configureAceEditor(aceMode, assetsPath) {
+ configureAceEditor(filePath, assetsPath) {
ace.config.set('modePath', `${assetsPath}/ace`);
ace.config.loadModule('ace/ext/searchbox');
+ ace.config.loadModule('ace/ext/modelist');
this.editor = ace.edit('editor');
@@ -25,15 +27,16 @@ export default class EditBlob {
this.editor.focus();
- if (aceMode) {
- this.editor.getSession().setMode(`ace/mode/${aceMode}`);
+ if (filePath) {
+ this.editor.getSession().setMode(getModeByFileExtension(filePath));
}
}
- initFileSelectors(currentAction) {
+ initFileSelectors(currentAction, projectId) {
this.fileTemplateMediator = new TemplateSelectorMediator({
currentAction,
editor: this.editor,
+ projectId,
});
}
@@ -60,14 +63,15 @@ export default class EditBlob {
if (paneId === '#preview') {
this.$toggleButton.hide();
- axios.post(currentLink.data('previewUrl'), {
- content: this.editor.getValue(),
- })
- .then(({ data }) => {
- currentPane.empty().append(data);
- currentPane.renderGFM();
- })
- .catch(() => createFlash(__('An error occurred previewing the blob')));
+ axios
+ .post(currentLink.data('previewUrl'), {
+ content: this.editor.getValue(),
+ })
+ .then(({ data }) => {
+ currentPane.empty().append(data);
+ currentPane.renderGFM();
+ })
+ .catch(() => createFlash(__('An error occurred previewing the blob')));
}
this.$toggleButton.show();
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 9ad451fa375..fb6e5291a61 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -1,25 +1,20 @@
-/* eslint-disable comma-dangle */
-
import Sortable from 'sortablejs';
import Vue from 'vue';
import { n__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
import AccessorUtilities from '../../lib/utils/accessor';
-import boardList from './board_list.vue';
import BoardBlankState from './board_blank_state.vue';
-import './board_delete';
-
-const Store = gl.issueBoards.BoardsStore;
-
-window.gl = window.gl || {};
-window.gl.issueBoards = window.gl.issueBoards || {};
+import BoardDelete from './board_delete';
+import BoardList from './board_list.vue';
+import boardsStore from '../stores/boards_store';
+import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
-gl.issueBoards.Board = Vue.extend({
+export default Vue.extend({
components: {
- boardList,
- 'board-delete': gl.issueBoards.BoardDelete,
BoardBlankState,
+ BoardDelete,
+ BoardList,
Icon,
},
directives: {
@@ -47,10 +42,10 @@ gl.issueBoards.Board = Vue.extend({
required: true,
},
},
- data () {
+ data() {
return {
- detailIssue: Store.detail,
- filter: Store.filter,
+ detailIssue: boardsStore.detail,
+ filter: boardsStore.filter,
};
},
computed: {
@@ -58,44 +53,47 @@ gl.issueBoards.Board = Vue.extend({
const { issuesSize } = this.list;
return `${n__('%d issue', '%d issues', issuesSize)}`;
},
+ isNewIssueShown() {
+ return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed');
+ },
},
watch: {
filter: {
handler() {
this.list.page = 1;
- this.list.getIssues(true)
- .catch(() => {
- // TODO: handle request error
- });
+ this.list.getIssues(true).catch(() => {
+ // TODO: handle request error
+ });
},
deep: true,
- }
+ },
},
- mounted () {
- this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
+ mounted() {
+ this.sortableOptions = getBoardSortableDefaultOptions({
disabled: this.disabled,
group: 'boards',
draggable: '.is-draggable',
handle: '.js-board-handle',
- onEnd: (e) => {
- gl.issueBoards.onEnd();
+ onEnd: e => {
+ sortableEnd();
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
const order = this.sortable.toArray();
- const list = Store.findList('id', parseInt(e.item.dataset.id, 10));
+ const list = boardsStore.findList('id', parseInt(e.item.dataset.id, 10));
this.$nextTick(() => {
- Store.moveList(list, order);
+ boardsStore.moveList(list, order);
});
}
- }
+ },
});
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
},
created() {
if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
- const isCollapsed = localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
+ const isCollapsed =
+ localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
this.list.isExpanded = !isCollapsed;
}
@@ -109,7 +107,10 @@ gl.issueBoards.Board = Vue.extend({
this.list.isExpanded = !this.list.isExpanded;
if (AccessorUtilities.isLocalStorageAccessSafe()) {
- localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
+ localStorage.setItem(
+ `boards.${this.boardId}.${this.list.type}.expanded`,
+ this.list.isExpanded,
+ );
}
}
},
diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue
index 286529b4d13..561a4636ef5 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.vue
+++ b/app/assets/javascripts/boards/components/board_blank_state.vue
@@ -2,8 +2,7 @@
/* global ListLabel */
import _ from 'underscore';
import Cookies from 'js-cookie';
-
-const Store = gl.issueBoards.BoardsStore;
+import boardsStore from '../stores/boards_store';
export default {
data() {
@@ -19,7 +18,7 @@ export default {
this.clearBlankState();
this.predefinedLabels.forEach((label, i) => {
- Store.addList({
+ boardsStore.addList({
title: label.title,
position: i,
list_type: 'label',
@@ -30,35 +29,34 @@ export default {
});
});
- Store.state.lists = _.sortBy(Store.state.lists, 'position');
+ boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position');
// Save the labels
- gl.boardService.generateDefaultLists()
+ gl.boardService
+ .generateDefaultLists()
.then(res => res.data)
- .then((data) => {
- data.forEach((listObj) => {
- const list = Store.findList('title', listObj.title);
+ .then(data => {
+ data.forEach(listObj => {
+ const list = boardsStore.findList('title', listObj.title);
list.id = listObj.id;
list.label.id = listObj.label.id;
- list.getIssues()
- .catch(() => {
- // TODO: handle request error
- });
+ list.getIssues().catch(() => {
+ // TODO: handle request error
+ });
});
})
.catch(() => {
- Store.removeList(undefined, 'label');
+ boardsStore.removeList(undefined, 'label');
Cookies.remove('issue_board_welcome_hidden', {
path: '',
});
- Store.addBlankState();
+ boardsStore.addBlankState();
});
},
- clearBlankState: Store.removeBlankState.bind(Store),
+ clearBlankState: boardsStore.removeBlankState.bind(boardsStore),
},
};
-
</script>
<template>
@@ -83,7 +81,7 @@ export default {
right on the way to making the most of your board.
</p>
<button
- class="btn btn-create btn-inverted btn-block"
+ class="btn btn-success btn-inverted btn-block"
type="button"
@click.stop="addDefaultLists">
Add default lists
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 0398102ad02..2f31316aa76 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -1,78 +1,77 @@
<script>
- /* eslint-disable vue/require-default-prop */
- import IssueCardInner from './issue_card_inner.vue';
- import eventHub from '../eventhub';
+/* eslint-disable vue/require-default-prop */
+import IssueCardInner from './issue_card_inner.vue';
+import eventHub from '../eventhub';
+import boardsStore from '../stores/boards_store';
- const Store = gl.issueBoards.BoardsStore;
-
- export default {
- name: 'BoardsIssueCard',
- components: {
- IssueCardInner,
+export default {
+ name: 'BoardsIssueCard',
+ components: {
+ IssueCardInner,
+ },
+ props: {
+ list: {
+ type: Object,
+ default: () => ({}),
},
- props: {
- list: {
- type: Object,
- default: () => ({}),
- },
- issue: {
- type: Object,
- default: () => ({}),
- },
- issueLinkBase: {
- type: String,
- default: '',
- },
- disabled: {
- type: Boolean,
- default: false,
- },
- index: {
- type: Number,
- default: 0,
- },
- rootPath: {
- type: String,
- default: '',
- },
- groupId: {
- type: Number,
- },
+ issue: {
+ type: Object,
+ default: () => ({}),
},
- data() {
- return {
- showDetail: false,
- detailIssue: Store.detail,
- };
+ issueLinkBase: {
+ type: String,
+ default: '',
},
- computed: {
- issueDetailVisible() {
- return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
- },
+ disabled: {
+ type: Boolean,
+ default: false,
},
- methods: {
- mouseDown() {
- this.showDetail = true;
- },
- mouseMove() {
- this.showDetail = false;
- },
- showIssue(e) {
- if (e.target.classList.contains('js-no-trigger')) return;
+ index: {
+ type: Number,
+ default: 0,
+ },
+ rootPath: {
+ type: String,
+ default: '',
+ },
+ groupId: {
+ type: Number,
+ },
+ },
+ data() {
+ return {
+ showDetail: false,
+ detailIssue: boardsStore.detail,
+ };
+ },
+ computed: {
+ issueDetailVisible() {
+ return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
+ },
+ },
+ methods: {
+ mouseDown() {
+ this.showDetail = true;
+ },
+ mouseMove() {
+ this.showDetail = false;
+ },
+ showIssue(e) {
+ if (e.target.classList.contains('js-no-trigger')) return;
- if (this.showDetail) {
- this.showDetail = false;
+ if (this.showDetail) {
+ this.showDetail = false;
- if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
- eventHub.$emit('clearDetailIssue');
- } else {
- eventHub.$emit('newDetailIssue', this.issue);
- Store.detail.list = this.list;
- }
+ if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) {
+ eventHub.$emit('clearDetailIssue');
+ } else {
+ eventHub.$emit('newDetailIssue', this.issue);
+ boardsStore.detail.list = this.list;
}
- },
+ }
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js
index c5945e8098d..a5f9d65e4d5 100644
--- a/app/assets/javascripts/boards/components/board_delete.js
+++ b/app/assets/javascripts/boards/components/board_delete.js
@@ -1,12 +1,7 @@
-/* eslint-disable comma-dangle, no-alert */
-
import $ from 'jquery';
import Vue from 'vue';
-window.gl = window.gl || {};
-window.gl.issueBoards = window.gl.issueBoards || {};
-
-gl.issueBoards.BoardDelete = Vue.extend({
+export default Vue.extend({
props: {
list: {
type: Object,
@@ -14,12 +9,13 @@ gl.issueBoards.BoardDelete = Vue.extend({
},
},
methods: {
- deleteBoard () {
+ deleteBoard() {
$(this.$el).tooltip('hide');
+ // eslint-disable-next-line no-alert
if (window.confirm('Are you sure you want to delete this list?')) {
this.list.destroy();
}
- }
- }
+ },
+ },
});
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 3e610a4088c..a04b828e277 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -1,18 +1,18 @@
<script>
import Sortable from 'sortablejs';
+import { GlLoadingIcon } from '@gitlab/ui';
import boardNewIssue from './board_new_issue.vue';
import boardCard from './board_card.vue';
import eventHub from '../eventhub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-
-const Store = gl.issueBoards.BoardsStore;
+import boardsStore from '../stores/boards_store';
+import { getBoardSortableDefaultOptions, sortableStart } from '../mixins/sortable_default_options';
export default {
name: 'BoardList',
components: {
boardCard,
boardNewIssue,
- loadingIcon,
+ GlLoadingIcon,
},
props: {
groupId: {
@@ -48,7 +48,7 @@ export default {
data() {
return {
scrollOffset: 250,
- filters: Store.state.filters,
+ filters: boardsStore.state.filters,
showCount: false,
showIssueForm: false,
};
@@ -63,13 +63,14 @@ export default {
},
issues() {
this.$nextTick(() => {
- if (this.scrollHeight() <= this.listHeight() &&
- this.list.issuesSize > this.list.issues.length) {
+ if (
+ this.scrollHeight() <= this.listHeight() &&
+ this.list.issuesSize > this.list.issues.length
+ ) {
this.list.page += 1;
- this.list.getIssues(false)
- .catch(() => {
- // TODO: handle request error
- });
+ this.list.getIssues(false).catch(() => {
+ // TODO: handle request error
+ });
}
if (this.scrollHeight() > Math.ceil(this.listHeight())) {
@@ -85,7 +86,7 @@ export default {
eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop);
},
mounted() {
- const options = gl.issueBoards.getBoardSortableDefaultOptions({
+ const options = getBoardSortableDefaultOptions({
scroll: true,
disabled: this.disabled,
filter: '.board-list-count, .is-disabled',
@@ -110,7 +111,8 @@ export default {
// So from there, we can get reference to actual container
// and thus the container type to enable Copy or Move
if (e.target) {
- const containerEl = e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
+ const containerEl =
+ e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
const toBoardType = containerEl.dataset.boardType;
const cloneActions = {
label: ['milestone', 'assignee'],
@@ -122,8 +124,9 @@ export default {
const fromBoardType = this.list.type;
// For each list we check if the destination list is
// a the list were we should clone the issue
- const shouldClone = Object.entries(cloneActions).some(entry => (
- fromBoardType === entry[0] && entry[1].includes(toBoardType)));
+ const shouldClone = Object.entries(cloneActions).some(
+ entry => fromBoardType === entry[0] && entry[1].includes(toBoardType),
+ );
if (shouldClone) {
return 'clone';
@@ -135,28 +138,36 @@ export default {
},
revertClone: true,
},
- onStart: (e) => {
+ onStart: e => {
const card = this.$refs.issue[e.oldIndex];
card.showDetail = false;
- Store.moving.list = card.list;
- Store.moving.issue = Store.moving.list.findIssue(+e.item.dataset.issueId);
+ boardsStore.moving.list = card.list;
+ boardsStore.moving.issue = boardsStore.moving.list.findIssue(+e.item.dataset.issueId);
- gl.issueBoards.onStart();
+ sortableStart();
},
- onAdd: (e) => {
- gl.issueBoards.BoardsStore
- .moveIssueToList(Store.moving.list, this.list, Store.moving.issue, e.newIndex);
+ onAdd: e => {
+ boardsStore.moveIssueToList(
+ boardsStore.moving.list,
+ this.list,
+ boardsStore.moving.issue,
+ e.newIndex,
+ );
this.$nextTick(() => {
e.item.remove();
});
},
- onUpdate: (e) => {
- const sortedArray = this.sortable.toArray()
- .filter(id => id !== '-1');
- gl.issueBoards.BoardsStore
- .moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray);
+ onUpdate: e => {
+ const sortedArray = this.sortable.toArray().filter(id => id !== '-1');
+ boardsStore.moveIssueInList(
+ this.list,
+ boardsStore.moving.issue,
+ e.oldIndex,
+ e.newIndex,
+ sortedArray,
+ );
},
onMove(e) {
return !e.related.classList.contains('board-list-count');
@@ -194,16 +205,14 @@ export default {
if (getIssues) {
this.list.loadingMore = true;
- getIssues
- .then(loadingDone)
- .catch(loadingDone);
+ getIssues.then(loadingDone).catch(loadingDone);
}
},
toggleForm() {
this.showIssueForm = !this.showIssueForm;
},
onScroll() {
- if (!this.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) {
+ if (!this.list.loadingMore && this.scrollTop() > this.scrollHeight() - this.scrollOffset) {
this.loadNextPage();
}
},
@@ -217,7 +226,7 @@ export default {
v-if="loading"
class="board-list-loading text-center"
aria-label="Loading issues">
- <loading-icon />
+ <gl-loading-icon />
</div>
<board-new-issue
v-if="list.type !== 'closed' && showIssueForm"
@@ -233,19 +242,19 @@ export default {
<board-card
v-for="(issue, index) in issues"
ref="issue"
+ :key="issue.id"
:index="index"
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
:group-id="groupId"
:root-path="rootPath"
- :disabled="disabled"
- :key="issue.id" />
+ :disabled="disabled" />
<li
v-if="showCount"
class="board-list-count text-center"
data-issue-id="-1">
- <loading-icon
+ <gl-loading-icon
v-show="list.loadingMore"
label="Loading more issues"
/>
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index 1e3cd43d1f0..2a96d1443e1 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -1,15 +1,16 @@
<script>
import $ from 'jquery';
+import { GlButton } from '@gitlab/ui';
import eventHub from '../eventhub';
import ProjectSelect from './project_select.vue';
import ListIssue from '../models/issue';
-
-const Store = gl.issueBoards.BoardsStore;
+import boardsStore from '../stores/boards_store';
export default {
name: 'BoardNewIssue',
components: {
ProjectSelect,
+ GlButton,
},
props: {
groupId: {
@@ -61,13 +62,14 @@ export default {
eventHub.$emit(`scroll-board-list-${this.list.id}`);
this.cancel();
- return this.list.newIssue(issue)
+ return this.list
+ .newIssue(issue)
.then(() => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions
$(this.$refs.submitButton).enable();
- Store.detail.issue = issue;
- Store.detail.list = this.list;
+ boardsStore.detail.issue = issue;
+ boardsStore.detail.list = this.list;
})
.catch(() => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions
@@ -110,9 +112,9 @@ export default {
Title
</label>
<input
+ :id="list.id + '-title'"
ref="input"
v-model="title"
- :id="list.id + '-title'"
class="form-control"
type="text"
name="issue_title"
@@ -123,21 +125,23 @@ export default {
:group-id="groupId"
/>
<div class="clearfix prepend-top-10">
- <button
+ <gl-button
ref="submit-button"
:disabled="disabled"
- class="btn btn-success float-left"
+ class="float-left"
+ variant="success"
type="submit"
>
Submit issue
- </button>
- <button
- class="btn btn-default float-right"
+ </gl-button>
+ <gl-button
+ class="float-right"
type="button"
+ variant="default"
@click="cancel"
>
Cancel
- </button>
+ </gl-button>
</div>
</form>
</div>
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 109e60cbde2..e637e1f1223 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, no-new */
+/* eslint-disable no-new */
import $ from 'jquery';
import Vue from 'vue';
@@ -14,13 +14,9 @@ import IssuableContext from '../../issuable_context';
import LabelsSelect from '../../labels_select';
import Subscriptions from '../../sidebar/components/subscriptions/subscriptions.vue';
import MilestoneSelect from '../../milestone_select';
+import boardsStore from '../stores/boards_store';
-const Store = gl.issueBoards.BoardsStore;
-
-window.gl = window.gl || {};
-window.gl.issueBoards = window.gl.issueBoards || {};
-
-gl.issueBoards.BoardSidebar = Vue.extend({
+export default Vue.extend({
components: {
AssigneeTitle,
Assignees,
@@ -35,14 +31,14 @@ gl.issueBoards.BoardSidebar = Vue.extend({
},
data() {
return {
- detail: Store.detail,
+ detail: boardsStore.detail,
issue: {},
list: {},
loadingAssignees: false,
};
},
computed: {
- showSidebar () {
+ showSidebar() {
return Object.keys(this.issue).length;
},
milestoneTitle() {
@@ -55,18 +51,20 @@ gl.issueBoards.BoardSidebar = Vue.extend({
return this.issue.labels && this.issue.labels.length;
},
labelDropdownTitle() {
- return this.hasLabels ? sprintf(__('%{firstLabel} +%{labelCount} more'), {
- firstLabel: this.issue.labels[0].title,
- labelCount: this.issue.labels.length - 1
- }) : __('Label');
+ return this.hasLabels
+ ? sprintf(__('%{firstLabel} +%{labelCount} more'), {
+ firstLabel: this.issue.labels[0].title,
+ labelCount: this.issue.labels.length - 1,
+ })
+ : __('Label');
},
selectedLabels() {
return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
- }
+ },
},
watch: {
detail: {
- handler () {
+ handler() {
if (this.issue.id !== this.detail.issue.id) {
$('.block.assignee')
.find('input:not(.js-vue)[name="issue[assignee_ids][]"]')
@@ -75,17 +73,19 @@ gl.issueBoards.BoardSidebar = Vue.extend({
});
$('.js-issue-board-sidebar', this.$el).each((i, el) => {
- $(el).data('glDropdown').clearMenu();
+ $(el)
+ .data('glDropdown')
+ .clearMenu();
});
}
this.issue = this.detail.issue;
this.list = this.detail.list;
},
- deep: true
+ deep: true,
},
},
- created () {
+ created() {
// Get events from glDropdown
eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
eventHub.$on('sidebar.addAssignee', this.addAssignee);
@@ -98,7 +98,7 @@ gl.issueBoards.BoardSidebar = Vue.extend({
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
},
- mounted () {
+ mounted() {
new IssuableContext(this.currentUser);
new MilestoneSelect();
new DueDateSelectors();
@@ -106,29 +106,30 @@ gl.issueBoards.BoardSidebar = Vue.extend({
new Sidebar();
},
methods: {
- closeSidebar () {
+ closeSidebar() {
this.detail.issue = {};
},
- assignSelf () {
+ assignSelf() {
// Notify gl dropdown that we are now assigning to current user
this.$refs.assigneeBlock.dispatchEvent(new Event('assignYourself'));
this.addAssignee(this.currentUser);
this.saveAssignees();
},
- removeAssignee (a) {
- gl.issueBoards.BoardsStore.detail.issue.removeAssignee(a);
+ removeAssignee(a) {
+ boardsStore.detail.issue.removeAssignee(a);
},
- addAssignee (a) {
- gl.issueBoards.BoardsStore.detail.issue.addAssignee(a);
+ addAssignee(a) {
+ boardsStore.detail.issue.addAssignee(a);
},
- removeAllAssignees () {
- gl.issueBoards.BoardsStore.detail.issue.removeAllAssignees();
+ removeAllAssignees() {
+ boardsStore.detail.issue.removeAllAssignees();
},
- saveAssignees () {
+ saveAssignees() {
this.loadingAssignees = true;
- gl.issueBoards.BoardsStore.detail.issue.update()
+ boardsStore.detail.issue
+ .update()
.then(() => {
this.loadingAssignees = false;
})
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index d50641dc3a9..48c46ae32f1 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -1,162 +1,222 @@
<script>
- import $ from 'jquery';
- import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
- import eventHub from '../eventhub';
- import tooltip from '../../vue_shared/directives/tooltip';
+import { GlTooltipDirective } from '@gitlab/ui';
+import { sprintf, __ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
+import eventHub from '../eventhub';
+import IssueDueDate from './issue_due_date.vue';
+import IssueTimeEstimate from './issue_time_estimate.vue';
+import boardsStore from '../stores/boards_store';
- const Store = gl.issueBoards.BoardsStore;
+export default {
+ components: {
+ Icon,
+ UserAvatarLink,
+ TooltipOnTruncate,
+ IssueDueDate,
+ IssueTimeEstimate,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ issueLinkBase: {
+ type: String,
+ required: true,
+ },
+ list: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ updateFilters: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ groupId: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ limitBeforeCounter: 2,
+ maxRender: 3,
+ maxCounter: 99,
+ };
+ },
+ computed: {
+ numberOverLimit() {
+ return this.issue.assignees.length - this.limitBeforeCounter;
+ },
+ assigneeCounterTooltip() {
+ const { numberOverLimit, maxCounter } = this;
+ const count = numberOverLimit > maxCounter ? maxCounter : numberOverLimit;
+ return sprintf(__('%{count} more assignees'), { count });
+ },
+ assigneeCounterLabel() {
+ if (this.numberOverLimit > this.maxCounter) {
+ return `${this.maxCounter}+`;
+ }
- export default {
- components: {
- UserAvatarLink,
- },
- directives: {
- tooltip,
- },
- props: {
- issue: {
- type: Object,
- required: true,
- },
- issueLinkBase: {
- type: String,
- required: true,
- },
- list: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- rootPath: {
- type: String,
- required: true,
- },
- updateFilters: {
- type: Boolean,
- required: false,
- default: false,
- },
- groupId: {
- type: Number,
- required: false,
- default: null,
- },
- },
- data() {
- return {
- limitBeforeCounter: 3,
- maxRender: 4,
- maxCounter: 99,
- };
+ return `+${this.numberOverLimit}`;
+ },
+ shouldRenderCounter() {
+ if (this.issue.assignees.length <= this.maxRender) {
+ return false;
+ }
+
+ return this.issue.assignees.length > this.numberOverLimit;
+ },
+ issueId() {
+ if (this.issue.iid) {
+ return `#${this.issue.iid}`;
+ }
+ return false;
+ },
+ showLabelFooter() {
+ return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
+ },
+ issueReferencePath() {
+ const { referencePath, groupId } = this.issue;
+ return !groupId ? referencePath.split('#')[0] : null;
},
- computed: {
- numberOverLimit() {
- return this.issue.assignees.length - this.limitBeforeCounter;
- },
- assigneeCounterTooltip() {
- return `${this.assigneeCounterLabel} more`;
- },
- assigneeCounterLabel() {
- if (this.numberOverLimit > this.maxCounter) {
- return `${this.maxCounter}+`;
- }
+ },
+ methods: {
+ isIndexLessThanlimit(index) {
+ return index < this.limitBeforeCounter;
+ },
+ shouldRenderAssignee(index) {
+ // Eg. maxRender is 4,
+ // Render up to all 4 assignees if there are only 4 assigness
+ // Otherwise render up to the limitBeforeCounter
+ if (this.issue.assignees.length <= this.maxRender) {
+ return index < this.maxRender;
+ }
- return `+${this.numberOverLimit}`;
- },
- shouldRenderCounter() {
- if (this.issue.assignees.length <= this.maxRender) {
- return false;
- }
+ return index < this.limitBeforeCounter;
+ },
+ assigneeUrl(assignee) {
+ if (!assignee) return '';
+ return `${this.rootPath}${assignee.username}`;
+ },
+ avatarUrlTitle(assignee) {
+ return `Avatar for ${assignee.name}`;
+ },
+ showLabel(label) {
+ if (!label.id) return false;
+ return true;
+ },
+ filterByLabel(label) {
+ if (!this.updateFilters) return;
+ const labelTitle = encodeURIComponent(label.title);
+ const filter = `label_name[]=${labelTitle}`;
- return this.issue.assignees.length > this.numberOverLimit;
- },
- issueId() {
- if (this.issue.iid) {
- return `#${this.issue.iid}`;
- }
- return false;
- },
- showLabelFooter() {
- return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
- },
- },
- methods: {
- isIndexLessThanlimit(index) {
- return index < this.limitBeforeCounter;
- },
- shouldRenderAssignee(index) {
- // Eg. maxRender is 4,
- // Render up to all 4 assignees if there are only 4 assigness
- // Otherwise render up to the limitBeforeCounter
- if (this.issue.assignees.length <= this.maxRender) {
- return index < this.maxRender;
- }
+ this.applyFilter(filter);
+ },
+ filterByWeight(weight) {
+ if (!this.updateFilters) return;
- return index < this.limitBeforeCounter;
- },
- assigneeUrl(assignee) {
- return `${this.rootPath}${assignee.username}`;
- },
- assigneeUrlTitle(assignee) {
- return `Assigned to ${assignee.name}`;
- },
- avatarUrlTitle(assignee) {
- return `Avatar for ${assignee.name}`;
- },
- showLabel(label) {
- if (!label.id) return false;
- return true;
- },
- filterByLabel(label, e) {
- if (!this.updateFilters) return;
+ const issueWeight = encodeURIComponent(weight);
+ const filter = `weight=${issueWeight}`;
- const filterPath = gl.issueBoards.BoardsStore.filter.path.split('&');
- const labelTitle = encodeURIComponent(label.title);
- const param = `label_name[]=${labelTitle}`;
- const labelIndex = filterPath.indexOf(param);
- $(e.currentTarget).tooltip('hide');
+ this.applyFilter(filter);
+ },
+ applyFilter(filter) {
+ const filterPath = boardsStore.filter.path.split('&');
+ const filterIndex = filterPath.indexOf(filter);
- if (labelIndex === -1) {
- filterPath.push(param);
- } else {
- filterPath.splice(labelIndex, 1);
- }
+ if (filterIndex === -1) {
+ filterPath.push(filter);
+ } else {
+ filterPath.splice(filterIndex, 1);
+ }
- gl.issueBoards.BoardsStore.filter.path = filterPath.join('&');
+ boardsStore.filter.path = filterPath.join('&');
- Store.updateFiltersUrl();
+ boardsStore.updateFiltersUrl();
- eventHub.$emit('updateTokens');
- },
- labelStyle(label) {
- return {
- backgroundColor: label.color,
- color: label.textColor,
- };
- },
- },
- };
+ eventHub.$emit('updateTokens');
+ },
+ labelStyle(label) {
+ return {
+ backgroundColor: label.color,
+ color: label.textColor,
+ };
+ },
+ },
+};
</script>
<template>
<div>
<div class="board-card-header">
- <h4 class="board-card-title">
- <i
+ <h4 class="board-card-title append-bottom-0 prepend-top-0">
+ <icon
v-if="issue.confidential"
- class="fa fa-eye-slash confidential-icon"
- aria-hidden="true"
- ></i>
- <a
+ v-gl-tooltip
+ name="eye-slash"
+ :title="__('Confidential')"
+ class="confidential-icon append-right-4"
+ :aria-label="__('Confidential')"
+ /><a
:href="issue.path"
:title="issue.title"
- class="js-no-trigger">{{ issue.title }}</a>
+ class="js-no-trigger"
+ @mousemove.stop>{{ issue.title }}</a>
+ </h4>
+ </div>
+ <div
+ v-if="showLabelFooter"
+ class="board-card-labels prepend-top-4 d-flex flex-wrap"
+ >
+ <button
+ v-for="label in issue.labels"
+ v-if="showLabel(label)"
+ :key="label.id"
+ v-gl-tooltip
+ :style="labelStyle(label)"
+ :title="label.description"
+ class="badge color-label append-right-4 prepend-top-4"
+ type="button"
+ @click="filterByLabel(label)"
+ >
+ {{ label.title }}
+ </button>
+ </div>
+ <div class="board-card-footer d-flex justify-content-between align-items-end">
+ <div class="d-flex align-items-start flex-wrap-reverse board-card-number-container js-board-card-number-container">
<span
- v-if="issueId"
- class="board-card-number"
+ v-if="issue.referencePath"
+ class="board-card-number d-flex append-right-8 prepend-top-8"
>
- {{ issue.referencePath }}
+ <tooltip-on-truncate
+ v-if="issueReferencePath"
+ :title="issueReferencePath"
+ placement="bottom"
+ class="board-issue-path block-truncated bold"
+ >{{ issueReferencePath }}</tooltip-on-truncate>#{{ issue.iid }}
</span>
- </h4>
+ <span class="board-info-items prepend-top-8 d-inline-block">
+ <issue-due-date
+ v-if="issue.dueDate"
+ :date="issue.dueDate"
+ /><issue-time-estimate
+ v-if="issue.timeEstimate"
+ :estimate="issue.timeEstimate"
+ />
+ </span>
+ </div>
<div class="board-card-assignee">
<user-avatar-link
v-for="(assignee, index) in issue.assignees"
@@ -165,38 +225,26 @@
:link-href="assigneeUrl(assignee)"
:img-alt="avatarUrlTitle(assignee)"
:img-src="assignee.avatar"
- :tooltip-text="assigneeUrlTitle(assignee)"
+ :img-size="24"
class="js-no-trigger"
tooltip-placement="bottom"
- />
+ >
+ <span class="js-assignee-tooltip">
+ <span class="bold d-block">Assignee</span>
+ {{ assignee.name }}
+ <span class="text-white-50">@{{ assignee.username }}</span>
+ </span>
+ </user-avatar-link>
<span
- v-tooltip
v-if="shouldRenderCounter"
+ v-gl-tooltip
:title="assigneeCounterTooltip"
class="avatar-counter"
+ data-placement="bottom"
>
{{ assigneeCounterLabel }}
</span>
</div>
</div>
- <div
- v-if="showLabelFooter"
- class="board-card-footer"
- >
- <button
- v-tooltip
- v-for="label in issue.labels"
- v-if="showLabel(label)"
- :key="label.id"
- :style="labelStyle(label)"
- :title="label.description"
- class="badge color-label"
- type="button"
- data-container="body"
- @click="filterByLabel(label, $event)"
- >
- {{ label.title }}
- </button>
- </div>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/issue_due_date.vue b/app/assets/javascripts/boards/components/issue_due_date.vue
new file mode 100644
index 00000000000..d6c87132843
--- /dev/null
+++ b/app/assets/javascripts/boards/components/issue_due_date.vue
@@ -0,0 +1,90 @@
+<script>
+import dateFormat from 'dateformat';
+import { GlTooltip } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+import { __ } from '~/locale';
+import { getDayDifference, getTimeago, dateInWords } from '~/lib/utils/datetime_utility';
+
+export default {
+ components: {
+ Icon,
+ GlTooltip,
+ },
+ props: {
+ date: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ title() {
+ const timeago = getTimeago();
+ const { timeDifference, standardDateFormat } = this;
+ const formatedDate = standardDateFormat;
+
+ if (timeDifference >= -1 && timeDifference < 7) {
+ return `${timeago.format(this.issueDueDate)} (${formatedDate})`;
+ }
+
+ return timeago.format(this.issueDueDate);
+ },
+ body() {
+ const { timeDifference, issueDueDate, standardDateFormat } = this;
+
+ if (timeDifference === 0) {
+ return __('Today');
+ } else if (timeDifference === 1) {
+ return __('Tomorrow');
+ } else if (timeDifference === -1) {
+ return __('Yesterday');
+ } else if (timeDifference > 0 && timeDifference < 7) {
+ return dateFormat(issueDueDate, 'dddd', true);
+ }
+
+ return standardDateFormat;
+ },
+ issueDueDate() {
+ return new Date(this.date);
+ },
+ timeDifference() {
+ const today = new Date();
+ return getDayDifference(today, this.issueDueDate);
+ },
+ isPastDue() {
+ if (this.timeDifference >= 0) return false;
+ return true;
+ },
+ standardDateFormat() {
+ const today = new Date();
+ const isDueInCurrentYear = today.getFullYear() === this.issueDueDate.getFullYear();
+
+ return dateInWords(this.issueDueDate, true, isDueInCurrentYear);
+ },
+ },
+};
+</script>
+
+<template>
+ <span>
+ <span
+ ref="issueDueDate"
+ class="board-card-info card-number"
+ >
+ <icon
+ :class="{'text-danger': isPastDue, 'board-card-info-icon': true}"
+ name="calendar"
+ /><time
+ :class="{'text-danger': isPastDue}"
+ datetime="date"
+ class="board-card-info-text">{{ body }}</time>
+ </span>
+ <gl-tooltip
+ :target="() => $refs.issueDueDate"
+ placement="bottom"
+ >
+ <span class="bold">{{ __('Due date') }}</span>
+ <br />
+ <span :class="{'text-danger-muted': isPastDue}">{{ title }}</span>
+ </gl-tooltip>
+ </span>
+</template>
diff --git a/app/assets/javascripts/boards/components/issue_time_estimate.vue b/app/assets/javascripts/boards/components/issue_time_estimate.vue
new file mode 100644
index 00000000000..a9803c8ab5d
--- /dev/null
+++ b/app/assets/javascripts/boards/components/issue_time_estimate.vue
@@ -0,0 +1,48 @@
+<script>
+import { GlTooltip } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
+
+export default {
+ components: {
+ Icon,
+ GlTooltip,
+ },
+ props: {
+ estimate: {
+ type: Number,
+ required: true,
+ },
+ },
+ computed: {
+ title() {
+ return stringifyTime(parseSeconds(this.estimate), true);
+ },
+ timeEstimate() {
+ return stringifyTime(parseSeconds(this.estimate));
+ },
+ },
+};
+</script>
+
+<template>
+ <span>
+ <span
+ ref="issueTimeEstimate"
+ class="board-card-info card-number"
+ >
+ <icon
+ name="hourglass"
+ css-classes="board-card-info-icon"
+ /><time class="board-card-info-text">{{ timeEstimate }}</time>
+ </span>
+ <gl-tooltip
+ :target="() => $refs.issueTimeEstimate"
+ placement="bottom"
+ class="js-issue-time-estimate"
+ >
+ <span class="bold d-block">{{ __('Time estimate') }}</span>
+ {{ title }}
+ </gl-tooltip>
+ </span>
+</template>
diff --git a/app/assets/javascripts/boards/components/modal/empty_state.vue b/app/assets/javascripts/boards/components/modal/empty_state.vue
index dbd69f84526..795ba864545 100644
--- a/app/assets/javascripts/boards/components/modal/empty_state.vue
+++ b/app/assets/javascripts/boards/components/modal/empty_state.vue
@@ -20,7 +20,7 @@ export default {
computed: {
contents() {
const obj = {
- title: 'You haven\'t added any issues to your project yet',
+ title: "You haven't added any issues to your project yet",
content: `
An issue can be a bug, a todo or a feature request that needs to be
discussed in a project. Besides, issues are searchable and filterable.
@@ -28,7 +28,7 @@ export default {
};
if (this.activeTab === 'selected') {
- obj.title = 'You haven\'t selected any issues yet';
+ obj.title = "You haven't selected any issues yet";
obj.content = `
Go back to <strong>Open issues</strong> and select some issues
to add to your board.
diff --git a/app/assets/javascripts/boards/components/modal/footer.vue b/app/assets/javascripts/boards/components/modal/footer.vue
index d4affc8c3de..d51597ed22d 100644
--- a/app/assets/javascripts/boards/components/modal/footer.vue
+++ b/app/assets/javascripts/boards/components/modal/footer.vue
@@ -5,6 +5,7 @@ import ListsDropdown from './lists_dropdown.vue';
import { pluralize } from '../../../lib/utils/text_utility';
import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
+import boardsStore from '../../stores/boards_store';
export default {
components: {
@@ -14,7 +15,7 @@ export default {
data() {
return {
modal: ModalStore.store,
- state: gl.issueBoards.BoardsStore.state,
+ state: boardsStore.state,
};
},
computed: {
@@ -41,19 +42,17 @@ export default {
const req = this.buildUpdateRequest(list);
// Post the data to the backend
- gl.boardService
- .bulkUpdate(issueIds, req)
- .catch(() => {
- Flash(__('Failed to update issues, please try again.'));
+ gl.boardService.bulkUpdate(issueIds, req).catch(() => {
+ Flash(__('Failed to update issues, please try again.'));
- selectedIssues.forEach((issue) => {
- list.removeIssue(issue);
- list.issuesSize -= 1;
- });
+ selectedIssues.forEach(issue => {
+ list.removeIssue(issue);
+ list.issuesSize -= 1;
});
+ });
// Add the issues on the frontend
- selectedIssues.forEach((issue) => {
+ selectedIssues.forEach(issue => {
list.addIssue(issue);
list.issuesSize += 1;
});
diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue
index 979fb4d7199..fc6cefa89a9 100644
--- a/app/assets/javascripts/boards/components/modal/header.vue
+++ b/app/assets/javascripts/boards/components/modal/header.vue
@@ -1,52 +1,52 @@
<script>
- import ModalFilters from './filters';
- import ModalTabs from './tabs.vue';
- import ModalStore from '../../stores/modal_store';
- import modalMixin from '../../mixins/modal_mixins';
+import ModalFilters from './filters';
+import ModalTabs from './tabs.vue';
+import ModalStore from '../../stores/modal_store';
+import modalMixin from '../../mixins/modal_mixins';
- export default {
- components: {
- ModalTabs,
- ModalFilters,
+export default {
+ components: {
+ ModalTabs,
+ ModalFilters,
+ },
+ mixins: [modalMixin],
+ props: {
+ projectId: {
+ type: Number,
+ required: true,
},
- mixins: [modalMixin],
- props: {
- projectId: {
- type: Number,
- required: true,
- },
- milestonePath: {
- type: String,
- required: true,
- },
- labelPath: {
- type: String,
- required: true,
- },
+ milestonePath: {
+ type: String,
+ required: true,
},
- data() {
- return ModalStore.store;
+ labelPath: {
+ type: String,
+ required: true,
},
- computed: {
- selectAllText() {
- if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
- return 'Select all';
- }
+ },
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ selectAllText() {
+ if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
+ return 'Select all';
+ }
- return 'Deselect all';
- },
- showSearch() {
- return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
- },
+ return 'Deselect all';
},
- methods: {
- toggleAll() {
- this.$refs.selectAllBtn.blur();
+ showSearch() {
+ return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
+ },
+ },
+ methods: {
+ toggleAll() {
+ this.$refs.selectAllBtn.blur();
- ModalStore.toggleAll();
- },
+ ModalStore.toggleAll();
},
- };
+ },
+};
</script>
<template>
<div>
diff --git a/app/assets/javascripts/boards/components/modal/index.vue b/app/assets/javascripts/boards/components/modal/index.vue
index 33e72a6782e..fc0e3e46b88 100644
--- a/app/assets/javascripts/boards/components/modal/index.vue
+++ b/app/assets/javascripts/boards/components/modal/index.vue
@@ -1,147 +1,146 @@
<script>
- /* global ListIssue */
- import queryData from '~/boards/utils/query_data';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
- import ModalHeader from './header.vue';
- import ModalList from './list.vue';
- import ModalFooter from './footer.vue';
- import EmptyState from './empty_state.vue';
- import ModalStore from '../../stores/modal_store';
+/* global ListIssue */
+import { urlParamsToObject } from '~/lib/utils/common_utils';
+import ModalHeader from './header.vue';
+import ModalList from './list.vue';
+import ModalFooter from './footer.vue';
+import EmptyState from './empty_state.vue';
+import ModalStore from '../../stores/modal_store';
+import { GlLoadingIcon } from '@gitlab/ui';
- export default {
- components: {
- EmptyState,
- ModalHeader,
- ModalList,
- ModalFooter,
- loadingIcon,
+export default {
+ components: {
+ EmptyState,
+ ModalHeader,
+ ModalList,
+ ModalFooter,
+ GlLoadingIcon,
+ },
+ props: {
+ newIssuePath: {
+ type: String,
+ required: true,
},
- props: {
- newIssuePath: {
- type: String,
- required: true,
- },
- emptyStateSvg: {
- type: String,
- required: true,
- },
- issueLinkBase: {
- type: String,
- required: true,
- },
- rootPath: {
- type: String,
- required: true,
- },
- projectId: {
- type: Number,
- required: true,
- },
- milestonePath: {
- type: String,
- required: true,
- },
- labelPath: {
- type: String,
- required: true,
- },
+ emptyStateSvg: {
+ type: String,
+ required: true,
},
- data() {
- return ModalStore.store;
+ issueLinkBase: {
+ type: String,
+ required: true,
},
- computed: {
- showList() {
- if (this.activeTab === 'selected') {
- return this.selectedIssues.length > 0;
- }
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ projectId: {
+ type: Number,
+ required: true,
+ },
+ milestonePath: {
+ type: String,
+ required: true,
+ },
+ labelPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ showList() {
+ if (this.activeTab === 'selected') {
+ return this.selectedIssues.length > 0;
+ }
- return this.issuesCount > 0;
- },
- showEmptyState() {
- if (!this.loading && this.issuesCount === 0) {
- return true;
- }
+ return this.issuesCount > 0;
+ },
+ showEmptyState() {
+ if (!this.loading && this.issuesCount === 0) {
+ return true;
+ }
- return this.activeTab === 'selected' && this.selectedIssues.length === 0;
- },
+ return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
- watch: {
- page() {
- this.loadIssues();
- },
- showAddIssuesModal() {
- if (this.showAddIssuesModal && !this.issues.length) {
- this.loading = true;
+ },
+ watch: {
+ page() {
+ this.loadIssues();
+ },
+ showAddIssuesModal() {
+ if (this.showAddIssuesModal && !this.issues.length) {
+ this.loading = true;
+ const loadingDone = () => {
+ this.loading = false;
+ };
+
+ this.loadIssues()
+ .then(loadingDone)
+ .catch(loadingDone);
+ } else if (!this.showAddIssuesModal) {
+ this.issues = [];
+ this.selectedIssues = [];
+ this.issuesCount = false;
+ }
+ },
+ filter: {
+ handler() {
+ if (this.$el.tagName) {
+ this.page = 1;
+ this.filterLoading = true;
const loadingDone = () => {
- this.loading = false;
+ this.filterLoading = false;
};
- this.loadIssues()
+ this.loadIssues(true)
.then(loadingDone)
.catch(loadingDone);
- } else if (!this.showAddIssuesModal) {
- this.issues = [];
- this.selectedIssues = [];
- this.issuesCount = false;
}
},
- filter: {
- handler() {
- if (this.$el.tagName) {
- this.page = 1;
- this.filterLoading = true;
- const loadingDone = () => {
- this.filterLoading = false;
- };
-
- this.loadIssues(true)
- .then(loadingDone)
- .catch(loadingDone);
- }
- },
- deep: true,
- },
- },
- created() {
- this.page = 1;
+ deep: true,
},
- methods: {
- loadIssues(clearIssues = false) {
- if (!this.showAddIssuesModal) return false;
+ },
+ created() {
+ this.page = 1;
+ },
+ methods: {
+ loadIssues(clearIssues = false) {
+ if (!this.showAddIssuesModal) return false;
- return gl.boardService
- .getBacklog(
- queryData(this.filter.path, {
- page: this.page,
- per: this.perPage,
- }),
- )
- .then(res => res.data)
- .then(data => {
- if (clearIssues) {
- this.issues = [];
- }
+ return gl.boardService
+ .getBacklog({
+ ...urlParamsToObject(this.filter.path),
+ page: this.page,
+ per: this.perPage,
+ })
+ .then(res => res.data)
+ .then(data => {
+ if (clearIssues) {
+ this.issues = [];
+ }
- data.issues.forEach(issueObj => {
- const issue = new ListIssue(issueObj);
- const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
- issue.selected = !!foundSelectedIssue;
+ data.issues.forEach(issueObj => {
+ const issue = new ListIssue(issueObj);
+ const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
+ issue.selected = !!foundSelectedIssue;
- this.issues.push(issue);
- });
+ this.issues.push(issue);
+ });
- this.loadingNewPage = false;
+ this.loadingNewPage = false;
- if (!this.issuesCount) {
- this.issuesCount = data.size;
- }
- })
- .catch(() => {
- // TODO: handle request error
- });
- },
+ if (!this.issuesCount) {
+ this.issuesCount = data.size;
+ }
+ })
+ .catch(() => {
+ // TODO: handle request error
+ });
},
- };
+ },
+};
</script>
<template>
<div
@@ -169,7 +168,7 @@
class="add-issues-list text-center"
>
<div class="add-issues-list-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</section>
<modal-footer/>
diff --git a/app/assets/javascripts/boards/components/modal/list.vue b/app/assets/javascripts/boards/components/modal/list.vue
index a58b5afe970..e11f398e70d 100644
--- a/app/assets/javascripts/boards/components/modal/list.vue
+++ b/app/assets/javascripts/boards/components/modal/list.vue
@@ -1,118 +1,120 @@
<script>
- import bp from '../../../breakpoints';
- import ModalStore from '../../stores/modal_store';
- import IssueCardInner from '../issue_card_inner.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import bp from '../../../breakpoints';
+import ModalStore from '../../stores/modal_store';
+import IssueCardInner from '../issue_card_inner.vue';
- export default {
- components: {
- IssueCardInner,
+export default {
+ components: {
+ IssueCardInner,
+ Icon,
+ },
+ props: {
+ issueLinkBase: {
+ type: String,
+ required: true,
},
- props: {
- issueLinkBase: {
- type: String,
- required: true,
- },
- rootPath: {
- type: String,
- required: true,
- },
- emptyStateSvg: {
- type: String,
- required: true,
- },
+ rootPath: {
+ type: String,
+ required: true,
},
- data() {
- return ModalStore.store;
+ emptyStateSvg: {
+ type: String,
+ required: true,
},
- computed: {
- loopIssues() {
- if (this.activeTab === 'all') {
- return this.issues;
- }
+ },
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ loopIssues() {
+ if (this.activeTab === 'all') {
+ return this.issues;
+ }
- return this.selectedIssues;
- },
- groupedIssues() {
- const groups = [];
- this.loopIssues.forEach((issue, i) => {
- const index = i % this.columns;
+ return this.selectedIssues;
+ },
+ groupedIssues() {
+ const groups = [];
+ this.loopIssues.forEach((issue, i) => {
+ const index = i % this.columns;
- if (!groups[index]) {
- groups.push([]);
- }
+ if (!groups[index]) {
+ groups.push([]);
+ }
- groups[index].push(issue);
- });
+ groups[index].push(issue);
+ });
- return groups;
- },
+ return groups;
},
- watch: {
- activeTab() {
- if (this.activeTab === 'all') {
- ModalStore.purgeUnselectedIssues();
- }
- },
+ },
+ watch: {
+ activeTab() {
+ if (this.activeTab === 'all') {
+ ModalStore.purgeUnselectedIssues();
+ }
},
- mounted() {
- this.scrollHandlerWrapper = this.scrollHandler.bind(this);
- this.setColumnCountWrapper = this.setColumnCount.bind(this);
- this.setColumnCount();
+ },
+ mounted() {
+ this.scrollHandlerWrapper = this.scrollHandler.bind(this);
+ this.setColumnCountWrapper = this.setColumnCount.bind(this);
+ this.setColumnCount();
- this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
- window.addEventListener('resize', this.setColumnCountWrapper);
+ this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
+ window.addEventListener('resize', this.setColumnCountWrapper);
+ },
+ beforeDestroy() {
+ this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
+ window.removeEventListener('resize', this.setColumnCountWrapper);
+ },
+ methods: {
+ scrollHandler() {
+ const currentPage = Math.floor(this.issues.length / this.perPage);
+
+ if (
+ this.scrollTop() > this.scrollHeight() - 100 &&
+ !this.loadingNewPage &&
+ currentPage === this.page
+ ) {
+ this.loadingNewPage = true;
+ this.page += 1;
+ }
},
- beforeDestroy() {
- this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
- window.removeEventListener('resize', this.setColumnCountWrapper);
+ toggleIssue(e, issue) {
+ if (e.target.tagName !== 'A') {
+ ModalStore.toggleIssue(issue);
+ }
},
- methods: {
- scrollHandler() {
- const currentPage = Math.floor(this.issues.length / this.perPage);
-
- if (
- this.scrollTop() > this.scrollHeight() - 100 &&
- !this.loadingNewPage &&
- currentPage === this.page
- ) {
- this.loadingNewPage = true;
- this.page += 1;
- }
- },
- toggleIssue(e, issue) {
- if (e.target.tagName !== 'A') {
- ModalStore.toggleIssue(issue);
- }
- },
- listHeight() {
- return this.$refs.list.getBoundingClientRect().height;
- },
- scrollHeight() {
- return this.$refs.list.scrollHeight;
- },
- scrollTop() {
- return this.$refs.list.scrollTop + this.listHeight();
- },
- showIssue(issue) {
- if (this.activeTab === 'all') return true;
+ listHeight() {
+ return this.$refs.list.getBoundingClientRect().height;
+ },
+ scrollHeight() {
+ return this.$refs.list.scrollHeight;
+ },
+ scrollTop() {
+ return this.$refs.list.scrollTop + this.listHeight();
+ },
+ showIssue(issue) {
+ if (this.activeTab === 'all') return true;
- const index = ModalStore.selectedIssueIndex(issue);
+ const index = ModalStore.selectedIssueIndex(issue);
- return index !== -1;
- },
- setColumnCount() {
- const breakpoint = bp.getBreakpointSize();
+ return index !== -1;
+ },
+ setColumnCount() {
+ const breakpoint = bp.getBreakpointSize();
- if (breakpoint === 'lg' || breakpoint === 'md') {
- this.columns = 3;
- } else if (breakpoint === 'sm') {
- this.columns = 2;
- } else {
- this.columns = 1;
- }
- },
+ if (breakpoint === 'lg' || breakpoint === 'md') {
+ this.columns = 3;
+ } else if (breakpoint === 'sm') {
+ this.columns = 2;
+ } else {
+ this.columns = 1;
+ }
},
- };
+ },
+};
</script>
<template>
<section
@@ -147,13 +149,13 @@
:issue="issue"
:issue-link-base="issueLinkBase"
:root-path="rootPath"/>
- <span
+ <icon
v-if="issue.selected"
:aria-label="'Issue #' + issue.id + ' selected'"
+ name="mobile-issue-close"
aria-checked="true"
- class="issue-card-selected text-center">
- <i class="fa fa-check"></i>
- </span>
+ class="issue-card-selected text-center"
+ />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/boards/components/modal/lists_dropdown.vue b/app/assets/javascripts/boards/components/modal/lists_dropdown.vue
index 6a5a39099bd..c502e32d169 100644
--- a/app/assets/javascripts/boards/components/modal/lists_dropdown.vue
+++ b/app/assets/javascripts/boards/components/modal/lists_dropdown.vue
@@ -1,11 +1,18 @@
<script>
+import { GlLink } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
import ModalStore from '../../stores/modal_store';
+import boardsStore from '../../stores/boards_store';
export default {
+ components: {
+ GlLink,
+ Icon,
+ },
data() {
return {
modal: ModalStore.store,
- state: gl.issueBoards.BoardsStore.state,
+ state: boardsStore.state,
};
},
computed: {
@@ -30,7 +37,9 @@ export default {
class="dropdown-label-box">
</span>
{{ selected.title }}
- <i class="fa fa-chevron-down"></i>
+ <icon
+ name="chevron-down"
+ />
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
<ul>
@@ -38,7 +47,7 @@ export default {
v-for="(list, i) in state.lists"
v-if="list.type == 'label'"
:key="i">
- <a
+ <gl-link
:class="{ 'is-active': list.id == selected.id }"
href="#"
role="button"
@@ -48,7 +57,7 @@ export default {
class="dropdown-label-box">
</span>
{{ list.title }}
- </a>
+ </gl-link>
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/boards/components/modal/tabs.vue b/app/assets/javascripts/boards/components/modal/tabs.vue
index d926b080094..5d661590e8e 100644
--- a/app/assets/javascripts/boards/components/modal/tabs.vue
+++ b/app/assets/javascripts/boards/components/modal/tabs.vue
@@ -1,21 +1,21 @@
<script>
- import ModalStore from '../../stores/modal_store';
- import modalMixin from '../../mixins/modal_mixins';
+import ModalStore from '../../stores/modal_store';
+import modalMixin from '../../mixins/modal_mixins';
- export default {
- mixins: [modalMixin],
- data() {
- return ModalStore.store;
+export default {
+ mixins: [modalMixin],
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ selectedCount() {
+ return ModalStore.selectedCount();
},
- computed: {
- selectedCount() {
- return ModalStore.selectedCount();
- },
- },
- destroyed() {
- this.activeTab = 'all';
- },
- };
+ },
+ destroyed() {
+ this.activeTab = 'all';
+ },
+};
</script>
<template>
<div class="top-area prepend-top-10 append-bottom-10">
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index 448ab9ed135..f7016561f93 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -4,42 +4,43 @@ import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import _ from 'underscore';
import CreateLabelDropdown from '../../create_label';
+import boardsStore from '../stores/boards_store';
-window.gl = window.gl || {};
-window.gl.issueBoards = window.gl.issueBoards || {};
-
-const Store = gl.issueBoards.BoardsStore;
-
-$(document).off('created.label').on('created.label', (e, label) => {
- Store.new({
- title: label.title,
- position: Store.state.lists.length - 2,
- list_type: 'label',
- label: {
- id: label.id,
+$(document)
+ .off('created.label')
+ .on('created.label', (e, label) => {
+ boardsStore.new({
title: label.title,
- color: label.color,
- },
+ position: boardsStore.state.lists.length - 2,
+ list_type: 'label',
+ label: {
+ id: label.id,
+ title: label.title,
+ color: label.color,
+ },
+ });
});
-});
-gl.issueBoards.newListDropdownInit = () => {
- $('.js-new-board-list').each(function () {
+export default function initNewListDropdown() {
+ $('.js-new-board-list').each(function() {
const $this = $(this);
- new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespacePath'), $this.data('projectPath'));
+ new CreateLabelDropdown(
+ $this.closest('.dropdown').find('.dropdown-new-label'),
+ $this.data('namespacePath'),
+ $this.data('projectPath'),
+ );
$this.glDropdown({
data(term, callback) {
- axios.get($this.attr('data-list-labels-path'))
- .then(({ data }) => {
- callback(data);
- });
+ axios.get($this.attr('data-list-labels-path')).then(({ data }) => {
+ callback(data);
+ });
},
- renderRow (label) {
- const active = Store.findList('title', label.title);
+ renderRow(label) {
+ const active = boardsStore.findList('title', label.title);
const $li = $('<li />');
const $a = $('<a />', {
- class: (active ? `is-active js-board-list-${active.id}` : ''),
+ class: active ? `is-active js-board-list-${active.id}` : '',
text: label.title,
href: '#',
});
@@ -57,15 +58,15 @@ gl.issueBoards.newListDropdownInit = () => {
selectable: true,
multiSelect: true,
containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content',
- clicked (options) {
+ clicked(options) {
const { e } = options;
const label = options.selectedObj;
e.preventDefault();
- if (!Store.findList('title', label.title)) {
- Store.new({
+ if (!boardsStore.findList('title', label.title)) {
+ boardsStore.new({
title: label.title,
- position: Store.state.lists.length - 2,
+ position: boardsStore.state.lists.length - 2,
list_type: 'label',
label: {
id: label.id,
@@ -74,9 +75,9 @@ gl.issueBoards.newListDropdownInit = () => {
},
});
- Store.state.lists = _.sortBy(Store.state.lists, 'position');
+ boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position');
}
},
});
});
-};
+}
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index ef9844d5562..83e6e237757 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -1,14 +1,16 @@
<script>
import $ from 'jquery';
import _ from 'underscore';
+import Icon from '~/vue_shared/components/icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '../eventhub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import Api from '../../api';
export default {
name: 'BoardProjectSelect',
components: {
- loadingIcon,
+ Icon,
+ GlLoadingIcon,
},
props: {
groupId: {
@@ -46,7 +48,7 @@ export default {
selectable: true,
data: (term, callback) => {
this.loading = true;
- return Api.groupProjects(this.groupId, term, {}, projects => {
+ return Api.groupProjects(this.groupId, term, { with_issues_enabled: true }, projects => {
this.loading = false;
callback(projects);
});
@@ -54,7 +56,9 @@ export default {
renderRow(project) {
return `
<li>
- <a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}">
+ <a href='#' class='dropdown-menu-link' data-project-id="${
+ project.id
+ }" data-project-name="${project.name}">
${_.escape(project.name)}
</a>
</li>
@@ -82,11 +86,9 @@ export default {
aria-expanded="false"
>
{{ selectedProjectName }}
- <i
- class="fa fa-chevron-down"
- aria-hidden="true"
- >
- </i>
+ <icon
+ name="chevron-down"
+ />
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
<div class="dropdown-title">
@@ -96,12 +98,11 @@ export default {
type="button"
class="dropdown-title-button dropdown-menu-close"
>
- <i
- aria-hidden="true"
+ <icon
+ name="merge-request-close-m"
data-hidden="true"
- class="fa fa-times dropdown-menu-close-icon"
- >
- </i>
+ class="dropdown-menu-close-icon"
+ />
</button>
</div>
<div class="dropdown-input">
@@ -110,16 +111,15 @@ export default {
type="search"
placeholder="Search projects"
/>
- <i
- aria-hidden="true"
+ <icon
+ name="search"
+ class="dropdown-input-search"
data-hidden="true"
- class="fa fa-search dropdown-input-search"
- >
- </i>
+ />
</div>
<div class="dropdown-content"></div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
index 90d4c710daf..d681e6a431c 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
@@ -1,80 +1,77 @@
<script>
- import Vue from 'vue';
- import Flash from '../../../flash';
- import { __ } from '../../../locale';
+import Vue from 'vue';
+import Flash from '../../../flash';
+import { __ } from '../../../locale';
+import boardsStore from '../../stores/boards_store';
- const Store = gl.issueBoards.BoardsStore;
-
- export default Vue.extend({
- props: {
- issue: {
- type: Object,
- required: true,
- },
- list: {
- type: Object,
- required: true,
- },
+export default Vue.extend({
+ props: {
+ issue: {
+ type: Object,
+ required: true,
},
- computed: {
- updateUrl() {
- return this.issue.path;
- },
+ list: {
+ type: Object,
+ required: true,
},
- methods: {
- removeIssue() {
- const { issue } = this;
- const lists = issue.getLists();
- const req = this.buildPatchRequest(issue, lists);
-
- const data = {
- issue: this.seedPatchRequest(issue, req),
- };
+ },
+ computed: {
+ updateUrl() {
+ return this.issue.path;
+ },
+ },
+ methods: {
+ removeIssue() {
+ const { issue } = this;
+ const lists = issue.getLists();
+ const req = this.buildPatchRequest(issue, lists);
- if (data.issue.label_ids.length === 0) {
- data.issue.label_ids = [''];
- }
+ const data = {
+ issue: this.seedPatchRequest(issue, req),
+ };
- // Post the remove data
- Vue.http.patch(this.updateUrl, data).catch(() => {
- Flash(__('Failed to remove issue from board, please try again.'));
+ if (data.issue.label_ids.length === 0) {
+ data.issue.label_ids = [''];
+ }
- lists.forEach(list => {
- list.addIssue(issue);
- });
- });
+ // Post the remove data
+ Vue.http.patch(this.updateUrl, data).catch(() => {
+ Flash(__('Failed to remove issue from board, please try again.'));
- // Remove from the frontend store
lists.forEach(list => {
- list.removeIssue(issue);
+ list.addIssue(issue);
});
+ });
- Store.detail.issue = {};
- },
- /**
- * Build the default patch request.
- */
- buildPatchRequest(issue, lists) {
- const listLabelIds = lists.map(list => list.label.id);
+ // Remove from the frontend store
+ lists.forEach(list => {
+ list.removeIssue(issue);
+ });
- const labelIds = issue.labels
- .map(label => label.id)
- .filter(id => !listLabelIds.includes(id));
+ boardsStore.detail.issue = {};
+ },
+ /**
+ * Build the default patch request.
+ */
+ buildPatchRequest(issue, lists) {
+ const listLabelIds = lists.map(list => list.label.id);
+
+ const labelIds = issue.labels.map(label => label.id).filter(id => !listLabelIds.includes(id));
- return {
- label_ids: labelIds,
- };
- },
- /**
- * Seed the given patch request.
- *
- * (This is overridden in EE)
- */
- seedPatchRequest(issue, req) {
- return req;
- },
+ return {
+ label_ids: labelIds,
+ };
+ },
+ /**
+ * Seed the given patch request.
+ *
+ * (This is overridden in EE)
+ */
+ seedPatchRequest(issue, req) {
+ return req;
},
- });
+ },
+});
</script>
<template>
<div
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index 46d61ebbf24..c14d69c5d18 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -1,5 +1,6 @@
import FilteredSearchContainer from '../filtered_search/container';
import FilteredSearchManager from '../filtered_search/filtered_search_manager';
+import boardsStore from './stores/boards_store';
export default class FilteredSearchBoards extends FilteredSearchManager {
constructor(store, updateUrl = false, cantEdit = []) {
@@ -23,7 +24,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
this.store.path = path.substr(1);
if (this.updateUrl) {
- gl.issueBoards.BoardsStore.updateFiltersUrl();
+ boardsStore.updateFiltersUrl();
}
}
@@ -31,7 +32,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token');
// Remove all the tokens as they will be replaced by the search manager
- [].forEach.call(tokens, (el) => {
+ [].forEach.call(tokens, el => {
el.parentNode.removeChild(el);
});
@@ -49,7 +50,10 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
canEdit(tokenName, tokenValue) {
if (this.cantEdit.includes(tokenName)) return false;
- return this.cantEditWithValue.findIndex(token => token.name === tokenName &&
- token.value === tokenValue) === -1;
+ return (
+ this.cantEditWithValue.findIndex(
+ token => token.name === tokenName && token.value === tokenValue,
+ ) === -1
+ );
}
}
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index bc263cbbfea..61a3072ac27 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -9,48 +9,53 @@ import '~/vue_shared/models/assignee';
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
-import sidebarEventHub from '~/sidebar/event_hub'; // eslint-disable-line import/first
+import sidebarEventHub from '~/sidebar/event_hub';
import './models/issue';
import './models/list';
import './models/milestone';
import './models/project';
-import './stores/boards_store';
+import boardsStore from './stores/boards_store';
import ModalStore from './stores/modal_store';
import BoardService from './services/board_service';
import modalMixin from './mixins/modal_mixins';
-import './mixins/sortable_default_options';
import './filters/due_date_filters';
-import './components/board';
-import './components/board_sidebar';
-import './components/new_list_dropdown';
+import Board from './components/board';
+import BoardSidebar from './components/board_sidebar';
+import initNewListDropdown from './components/new_list_dropdown';
import BoardAddIssuesModal from './components/modal/index.vue';
-import '~/vue_shared/vue_resource_interceptor'; // eslint-disable-line import/first
+import '~/vue_shared/vue_resource_interceptor';
+import { NavigationType } from '~/lib/utils/common_utils';
+
+let issueBoardsApp;
export default () => {
const $boardApp = document.getElementById('board-app');
- const Store = gl.issueBoards.BoardsStore;
- window.gl = window.gl || {};
+ // check for browser back and trigger a hard reload to circumvent browser caching.
+ window.addEventListener('pageshow', event => {
+ const isNavTypeBackForward =
+ window.performance && window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD;
- if (gl.IssueBoardsApp) {
- gl.IssueBoardsApp.$destroy(true);
- }
+ if (event.persisted || isNavTypeBackForward) {
+ window.location.reload();
+ }
+ });
- Store.create();
+ if (issueBoardsApp) {
+ issueBoardsApp.$destroy(true);
+ }
- // hack to allow sidebar scripts like milestone_select manipulate the BoardsStore
- gl.issueBoards.boardStoreIssueSet = (...args) => Vue.set(Store.detail.issue, ...args);
- gl.issueBoards.boardStoreIssueDelete = (...args) => Vue.delete(Store.detail.issue, ...args);
+ boardsStore.create();
- gl.IssueBoardsApp = new Vue({
+ issueBoardsApp = new Vue({
el: $boardApp,
components: {
- board: gl.issueBoards.Board,
- 'board-sidebar': gl.issueBoards.BoardSidebar,
+ Board,
+ BoardSidebar,
BoardAddIssuesModal,
},
data: {
- state: Store.state,
+ state: boardsStore.state,
loading: true,
boardsEndpoint: $boardApp.dataset.boardsEndpoint,
listsEndpoint: $boardApp.dataset.listsEndpoint,
@@ -59,7 +64,7 @@ export default () => {
issueLinkBase: $boardApp.dataset.issueLinkBase,
rootPath: $boardApp.dataset.rootPath,
bulkUpdatePath: $boardApp.dataset.bulkUpdatePath,
- detailIssue: Store.detail,
+ detailIssue: boardsStore.detail,
defaultAvatar: $boardApp.dataset.defaultAvatar,
},
computed: {
@@ -74,7 +79,7 @@ export default () => {
bulkUpdatePath: this.bulkUpdatePath,
boardId: this.boardId,
});
- Store.rootPath = this.boardsEndpoint;
+ boardsStore.rootPath = this.boardsEndpoint;
eventHub.$on('updateTokens', this.updateTokens);
eventHub.$on('newDetailIssue', this.updateDetailIssue);
@@ -88,16 +93,16 @@ export default () => {
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
},
mounted() {
- this.filterManager = new FilteredSearchBoards(Store.filter, true, Store.cantEdit);
+ this.filterManager = new FilteredSearchBoards(boardsStore.filter, true, boardsStore.cantEdit);
this.filterManager.setup();
- Store.disabled = this.disabled;
+ boardsStore.disabled = this.disabled;
gl.boardService
.all()
.then(res => res.data)
.then(data => {
data.forEach(board => {
- const list = Store.addList(board, this.defaultAvatar);
+ const list = boardsStore.addList(board, this.defaultAvatar);
if (list.type === 'closed') {
list.position = Infinity;
@@ -108,7 +113,7 @@ export default () => {
this.state.lists = _.sortBy(this.state.lists, 'position');
- Store.addBlankState();
+ boardsStore.addBlankState();
this.loading = false;
})
.catch(() => {
@@ -137,13 +142,13 @@ export default () => {
});
}
- Store.detail.issue = newIssue;
+ boardsStore.detail.issue = newIssue;
},
clearDetailIssue() {
- Store.detail.issue = {};
+ boardsStore.detail.issue = {};
},
toggleSubscription(id) {
- const { issue } = Store.detail;
+ const { issue } = boardsStore.detail;
if (issue.id === id && issue.toggleSubscriptionEndpoint) {
issue.setFetchingState('subscriptions', true);
BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint)
@@ -162,26 +167,28 @@ export default () => {
},
});
- gl.IssueBoardsSearch = new Vue({
+ // eslint-disable-next-line no-new
+ new Vue({
el: document.getElementById('js-add-list'),
data: {
- filters: Store.state.filters,
+ filters: boardsStore.state.filters,
},
mounted() {
- gl.issueBoards.newListDropdownInit();
+ initNewListDropdown();
},
});
const issueBoardsModal = document.getElementById('js-add-issues-btn');
if (issueBoardsModal) {
- gl.IssueBoardsModalAddBtn = new Vue({
+ // eslint-disable-next-line no-new
+ new Vue({
el: issueBoardsModal,
mixins: [modalMixin],
data() {
return {
modal: ModalStore.store,
- store: Store.state,
+ store: boardsStore.state,
canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
};
},
@@ -229,7 +236,7 @@ export default () => {
template: `
<div class="board-extra-actions">
<button
- class="btn btn-create prepend-left-10"
+ class="btn btn-success prepend-left-10"
type="button"
data-placement="bottom"
ref="addIssuesButton"
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js b/app/assets/javascripts/boards/mixins/sortable_default_options.js
index a8df45fc473..983b28d2e67 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js
@@ -3,32 +3,33 @@
import $ from 'jquery';
import sortableConfig from '../../sortable/sortable_config';
-window.gl = window.gl || {};
-window.gl.issueBoards = window.gl.issueBoards || {};
-
-gl.issueBoards.onStart = () => {
- $('.has-tooltip').tooltip('hide')
+export function sortableStart() {
+ $('.has-tooltip')
+ .tooltip('hide')
.tooltip('disable');
document.body.classList.add('is-dragging');
-};
+}
-gl.issueBoards.onEnd = () => {
+export function sortableEnd() {
$('.has-tooltip').tooltip('enable');
document.body.classList.remove('is-dragging');
-};
+}
-gl.issueBoards.touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
+export function getBoardSortableDefaultOptions(obj) {
+ const touchEnabled =
+ 'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch);
-gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
const defaultSortOptions = Object.assign({}, sortableConfig, {
filter: '.board-delete, .btn',
- delay: gl.issueBoards.touchEnabled ? 100 : 0,
- scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
+ delay: touchEnabled ? 100 : 0,
+ scrollSensitivity: touchEnabled ? 60 : 100,
scrollSpeed: 20,
- onStart: gl.issueBoards.onStart,
- onEnd: gl.issueBoards.onEnd,
+ onStart: sortableStart,
+ onEnd: sortableEnd,
});
- Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
+ Object.keys(obj).forEach(key => {
+ defaultSortOptions[key] = obj[key];
+ });
return defaultSortOptions;
-};
+}
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index c7cfb72067c..5e0f0b07247 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-unused-vars, comma-dangle */
+/* eslint-disable no-unused-vars */
/* global ListLabel */
/* global ListMilestone */
/* global ListAssignee */
@@ -6,9 +6,10 @@
import Vue from 'vue';
import '~/vue_shared/models/label';
import IssueProject from './project';
+import boardsStore from '../stores/boards_store';
class ListIssue {
- constructor (obj, defaultAvatar) {
+ constructor(obj, defaultAvatar) {
this.id = obj.id;
this.iid = obj.iid;
this.title = obj.title;
@@ -29,6 +30,8 @@ class ListIssue {
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
this.milestone_id = obj.milestone_id;
this.project_id = obj.project_id;
+ this.timeEstimate = obj.time_estimate;
+ this.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
if (obj.project) {
this.project = new IssueProject(obj.project);
@@ -38,55 +41,55 @@ class ListIssue {
this.milestone = new ListMilestone(obj.milestone);
}
- obj.labels.forEach((label) => {
+ obj.labels.forEach(label => {
this.labels.push(new ListLabel(label));
});
this.assignees = obj.assignees.map(a => new ListAssignee(a, defaultAvatar));
}
- addLabel (label) {
+ addLabel(label) {
if (!this.findLabel(label)) {
this.labels.push(new ListLabel(label));
}
}
- findLabel (findLabel) {
+ findLabel(findLabel) {
return this.labels.filter(label => label.title === findLabel.title)[0];
}
- removeLabel (removeLabel) {
+ removeLabel(removeLabel) {
if (removeLabel) {
this.labels = this.labels.filter(label => removeLabel.title !== label.title);
}
}
- removeLabels (labels) {
+ removeLabels(labels) {
labels.forEach(this.removeLabel.bind(this));
}
- addAssignee (assignee) {
+ addAssignee(assignee) {
if (!this.findAssignee(assignee)) {
this.assignees.push(new ListAssignee(assignee));
}
}
- findAssignee (findAssignee) {
+ findAssignee(findAssignee) {
return this.assignees.filter(assignee => assignee.id === findAssignee.id)[0];
}
- removeAssignee (removeAssignee) {
+ removeAssignee(removeAssignee) {
if (removeAssignee) {
this.assignees = this.assignees.filter(assignee => assignee.id !== removeAssignee.id);
}
}
- removeAllAssignees () {
+ removeAllAssignees() {
this.assignees = [];
}
- getLists () {
- return gl.issueBoards.BoardsStore.state.lists.filter(list => list.findIssue(this.id));
+ getLists() {
+ return boardsStore.state.lists.filter(list => list.findIssue(this.id));
}
updateData(newData) {
@@ -101,14 +104,14 @@ class ListIssue {
this.isLoading[key] = value;
}
- update () {
+ update() {
const data = {
issue: {
milestone_id: this.milestone ? this.milestone.id : null,
due_date: this.dueDate,
- assignee_ids: this.assignees.length > 0 ? this.assignees.map((u) => u.id) : [0],
- label_ids: this.labels.map((label) => label.id)
- }
+ assignee_ids: this.assignees.length > 0 ? this.assignees.map(u => u.id) : [0],
+ label_ids: this.labels.map(label => label.id),
+ },
};
if (!data.issue.label_ids.length) {
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 050cbd8db48..dd3feedbc0e 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -1,9 +1,11 @@
-/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len */
+/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign */
/* global ListIssue */
+import { __ } from '~/locale';
import ListLabel from '~/vue_shared/models/label';
import ListAssignee from '~/vue_shared/models/assignee';
-import queryData from '../utils/query_data';
+import { urlParamsToObject } from '~/lib/utils/common_utils';
+import boardsStore from '../stores/boards_store';
const PER_PAGE = 20;
@@ -30,7 +32,7 @@ class List {
this.id = obj.id;
this._uid = this.guid();
this.position = obj.position;
- this.title = obj.title;
+ this.title = obj.list_type === 'backlog' ? __('Open') : obj.title;
this.type = obj.list_type;
const typeInfo = this.getTypeInfo(this.type);
@@ -88,9 +90,9 @@ class List {
}
destroy() {
- const index = gl.issueBoards.BoardsStore.state.lists.indexOf(this);
- gl.issueBoards.BoardsStore.state.lists.splice(index, 1);
- gl.issueBoards.BoardsStore.updateNewListDropdown(this.id);
+ const index = boardsStore.state.lists.indexOf(this);
+ boardsStore.state.lists.splice(index, 1);
+ boardsStore.updateNewListDropdown(this.id);
gl.boardService.destroyList(this.id).catch(() => {
// TODO: handle request error
@@ -114,7 +116,10 @@ class List {
}
getIssues(emptyIssues = true) {
- const data = queryData(gl.issueBoards.BoardsStore.filter.path, { page: this.page });
+ const data = {
+ ...urlParamsToObject(boardsStore.filter.path),
+ page: this.page,
+ };
if (this.label && data.label_name) {
data.label_name = data.label_name.filter(label => label !== this.label.title);
@@ -229,11 +234,11 @@ class List {
});
}
- getTypeInfo (type) {
+ getTypeInfo(type) {
return TYPES[type] || {};
}
- onNewIssueResponse (issue, data) {
+ onNewIssueResponse(issue, data) {
issue.id = data.id;
issue.iid = data.iid;
issue.project = data.project;
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index 029b0971f2c..3de6eb056c2 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -19,7 +19,9 @@ export default class BoardService {
}
static generateIssuePath(boardId, id) {
- return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${id ? `/${id}` : ''}`;
+ return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${
+ id ? `/${id}` : ''
+ }`;
}
all() {
@@ -54,7 +56,9 @@ export default class BoardService {
getIssuesForList(id, filter = {}) {
const data = { id };
- Object.keys(filter).forEach((key) => { data[key] = filter[key]; });
+ Object.keys(filter).forEach(key => {
+ data[key] = filter[key];
+ });
return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
}
@@ -75,7 +79,9 @@ export default class BoardService {
}
getBacklog(data) {
- return axios.get(mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`));
+ return axios.get(
+ mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`),
+ );
}
bulkUpdate(issueIds, extraData = {}) {
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 957114cf420..eefe14a1d79 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -1,15 +1,13 @@
-/* eslint-disable comma-dangle, no-shadow */
+/* eslint-disable no-shadow */
/* global List */
import $ from 'jquery';
import _ from 'underscore';
+import Vue from 'vue';
import Cookies from 'js-cookie';
import { getUrlParamsArray } from '~/lib/utils/common_utils';
-window.gl = window.gl || {};
-window.gl.issueBoards = window.gl.issueBoards || {};
-
-gl.issueBoards.BoardsStore = {
+const boardsStore = {
disabled: false,
filter: {
path: '',
@@ -22,20 +20,20 @@ gl.issueBoards.BoardsStore = {
issue: {},
list: {},
},
- create () {
+ create() {
this.state.lists = [];
this.filter.path = getUrlParamsArray().join('&');
this.detail = {
issue: {},
};
},
- addList (listObj, defaultAvatar) {
+ addList(listObj, defaultAvatar) {
const list = new List(listObj, defaultAvatar);
this.state.lists.push(list);
return list;
},
- new (listObj) {
+ new(listObj) {
const list = this.addList(listObj);
const backlogList = this.findList('type', 'backlog', 'backlog');
@@ -52,44 +50,44 @@ gl.issueBoards.BoardsStore = {
});
this.removeBlankState();
},
- updateNewListDropdown (listId) {
+ updateNewListDropdown(listId) {
$(`.js-board-list-${listId}`).removeClass('is-active');
},
- shouldAddBlankState () {
+ shouldAddBlankState() {
// Decide whether to add the blank state
- return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]);
+ return !this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0];
},
- addBlankState () {
+ addBlankState() {
if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
this.addList({
id: 'blank',
list_type: 'blank',
title: 'Welcome to your Issue Board!',
- position: 0
+ position: 0,
});
this.state.lists = _.sortBy(this.state.lists, 'position');
},
- removeBlankState () {
+ removeBlankState() {
this.removeList('blank');
Cookies.set('issue_board_welcome_hidden', 'true', {
expires: 365 * 10,
- path: ''
+ path: '',
});
},
- welcomeIsHidden () {
+ welcomeIsHidden() {
return Cookies.get('issue_board_welcome_hidden') === 'true';
},
- removeList (id, type = 'blank') {
+ removeList(id, type = 'blank') {
const list = this.findList('id', id, type);
if (!list) return;
this.state.lists = this.state.lists.filter(list => list.id !== id);
},
- moveList (listFrom, orderLists) {
+ moveList(listFrom, orderLists) {
orderLists.forEach((id, i) => {
const list = this.findList('id', parseInt(id, 10));
@@ -97,22 +95,25 @@ gl.issueBoards.BoardsStore = {
});
listFrom.update();
},
- moveIssueToList (listFrom, listTo, issue, newIndex) {
+ moveIssueToList(listFrom, listTo, issue, newIndex) {
const issueTo = listTo.findIssue(issue.id);
const issueLists = issue.getLists();
const listLabels = issueLists.map(listIssue => listIssue.label);
if (!issueTo) {
// Check if target list assignee is already present in this issue
- if ((listTo.type === 'assignee' && listFrom.type === 'assignee') &&
- issue.findAssignee(listTo.assignee)) {
+ if (
+ listTo.type === 'assignee' &&
+ listFrom.type === 'assignee' &&
+ issue.findAssignee(listTo.assignee)
+ ) {
const targetIssue = listTo.findIssue(issue.id);
targetIssue.removeAssignee(listFrom.assignee);
} else if (listTo.type === 'milestone') {
const currentMilestone = issue.milestone;
const currentLists = this.state.lists
- .filter(list => (list.type === 'milestone' && list.id !== listTo.id))
- .filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
+ .filter(list => list.type === 'milestone' && list.id !== listTo.id)
+ .filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
issue.removeMilestone(currentMilestone);
issue.addMilestone(listTo.milestone);
@@ -128,7 +129,7 @@ gl.issueBoards.BoardsStore = {
}
if (listTo.type === 'closed' && listFrom.type !== 'backlog') {
- issueLists.forEach((list) => {
+ issueLists.forEach(list => {
list.removeIssue(issue);
});
issue.removeLabels(listLabels);
@@ -146,24 +147,39 @@ gl.issueBoards.BoardsStore = {
return (
(listTo.type !== 'label' && listFrom.type === 'assignee') ||
(listTo.type !== 'assignee' && listFrom.type === 'label') ||
- (listFrom.type === 'backlog')
+ listFrom.type === 'backlog'
);
},
- moveIssueInList (list, issue, oldIndex, newIndex, idArray) {
+ moveIssueInList(list, issue, oldIndex, newIndex, idArray) {
const beforeId = parseInt(idArray[newIndex - 1], 10) || null;
const afterId = parseInt(idArray[newIndex + 1], 10) || null;
list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
},
- findList (key, val, type = 'label') {
- const filteredList = this.state.lists.filter((list) => {
- const byType = type ? (list.type === type) || (list.type === 'assignee') || (list.type === 'milestone') : true;
+ findList(key, val, type = 'label') {
+ const filteredList = this.state.lists.filter(list => {
+ const byType = type
+ ? list.type === type || list.type === 'assignee' || list.type === 'milestone'
+ : true;
return list[key] === val && byType;
});
return filteredList[0];
},
- updateFiltersUrl () {
+ updateFiltersUrl() {
window.history.pushState(null, null, `?${this.filter.path}`);
- }
+ },
};
+
+// hacks added in order to allow milestone_select to function properly
+// TODO: remove these
+
+export function boardStoreIssueSet(...args) {
+ Vue.set(boardsStore.detail.issue, ...args);
+}
+
+export function boardStoreIssueDelete(...args) {
+ Vue.delete(boardsStore.detail.issue, ...args);
+}
+
+export default boardsStore;
diff --git a/app/assets/javascripts/boards/stores/modal_store.js b/app/assets/javascripts/boards/stores/modal_store.js
index 0d9ac367a70..b7228bf7bf5 100644
--- a/app/assets/javascripts/boards/stores/modal_store.js
+++ b/app/assets/javascripts/boards/stores/modal_store.js
@@ -40,7 +40,7 @@ class ModalStore {
toggleAll() {
const select = this.selectedCount() !== this.store.issues.length;
- this.store.issues.forEach((issue) => {
+ this.store.issues.forEach(issue => {
const issueUpdate = issue;
if (issueUpdate.selected !== select) {
@@ -69,13 +69,14 @@ class ModalStore {
removeSelectedIssue(issue, forcePurge = false) {
if (this.store.activeTab === 'all' || forcePurge) {
- this.store.selectedIssues = this.store.selectedIssues
- .filter(fIssue => fIssue.id !== issue.id);
+ this.store.selectedIssues = this.store.selectedIssues.filter(
+ fIssue => fIssue.id !== issue.id,
+ );
}
}
purgeUnselectedIssues() {
- this.store.selectedIssues.forEach((issue) => {
+ this.store.selectedIssues.forEach(issue => {
if (!issue.selected) {
this.removeSelectedIssue(issue, true);
}
@@ -87,8 +88,7 @@ class ModalStore {
}
findSelectedIssue(issue) {
- return this.store.selectedIssues
- .filter(filteredIssue => filteredIssue.id === issue.id)[0];
+ return this.store.selectedIssues.filter(filteredIssue => filteredIssue.id === issue.id)[0];
}
}
diff --git a/app/assets/javascripts/boards/utils/query_data.js b/app/assets/javascripts/boards/utils/query_data.js
deleted file mode 100644
index 65315979df7..00000000000
--- a/app/assets/javascripts/boards/utils/query_data.js
+++ /dev/null
@@ -1,21 +0,0 @@
-export default (path, extraData) => path.split('&').reduce((dataParam, filterParam) => {
- if (filterParam === '') return dataParam;
-
- const data = dataParam;
- const paramSplit = filterParam.split('=');
- const paramKeyNormalized = paramSplit[0].replace('[]', '');
- const isArray = paramSplit[0].indexOf('[]');
- const value = decodeURIComponent(paramSplit[1].replace(/\+/g, ' '));
-
- if (isArray !== -1) {
- if (!data[paramKeyNormalized]) {
- data[paramKeyNormalized] = [];
- }
-
- data[paramKeyNormalized].push(value);
- } else {
- data[paramKeyNormalized] = value;
- }
-
- return data;
-}, extraData);
diff --git a/app/assets/javascripts/breadcrumb.js b/app/assets/javascripts/breadcrumb.js
index 1474d93dde6..a37838694ec 100644
--- a/app/assets/javascripts/breadcrumb.js
+++ b/app/assets/javascripts/breadcrumb.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
-export const addTooltipToEl = (el) => {
+export const addTooltipToEl = el => {
const textEl = el.querySelector('.js-breadcrumb-item-text');
if (textEl && textEl.scrollWidth > textEl.offsetWidth) {
@@ -14,17 +14,18 @@ export default () => {
const breadcrumbs = document.querySelector('.js-breadcrumbs-list');
if (breadcrumbs) {
- const topLevelLinks = [...breadcrumbs.children].filter(el => !el.classList.contains('dropdown'))
+ const topLevelLinks = [...breadcrumbs.children]
+ .filter(el => !el.classList.contains('dropdown'))
.map(el => el.querySelector('a'))
.filter(el => el);
const $expander = $('.js-breadcrumbs-collapsed-expander');
topLevelLinks.forEach(el => addTooltipToEl(el));
- $expander.closest('.dropdown')
- .on('show.bs.dropdown hide.bs.dropdown', (e) => {
- $('.js-breadcrumbs-collapsed-expander', e.currentTarget).toggleClass('open')
- .tooltip('hide');
- });
+ $expander.closest('.dropdown').on('show.bs.dropdown hide.bs.dropdown', e => {
+ $('.js-breadcrumbs-collapsed-expander', e.currentTarget)
+ .toggleClass('open')
+ .tooltip('hide');
+ });
}
};
diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js
index e338376fcaa..97a1645aa51 100644
--- a/app/assets/javascripts/build_artifacts.js
+++ b/app/assets/javascripts/build_artifacts.js
@@ -12,16 +12,16 @@ export default class BuildArtifacts {
}
// eslint-disable-next-line class-methods-use-this
disablePropagation() {
- $('.top-block').on('click', '.download', function (e) {
+ $('.top-block').on('click', '.download', function(e) {
return e.stopPropagation();
});
- return $('.tree-holder').on('click', 'tr[data-link] a', function (e) {
+ return $('.tree-holder').on('click', 'tr[data-link] a', function(e) {
return e.stopImmediatePropagation();
});
}
// eslint-disable-next-line class-methods-use-this
setupEntryClick() {
- return $('.tree-holder').on('click', 'tr[data-link]', function () {
+ return $('.tree-holder').on('click', 'tr[data-link]', function() {
visitUrl(this.dataset.link, convertPermissionToBoolean(this.dataset.externalLink));
});
}
@@ -37,11 +37,15 @@ export default class BuildArtifacts {
// We want the tooltip to show if you hover anywhere on the row
// But be placed below and in the middle of the file name
$('.js-artifact-tree-row')
- .on('mouseenter', (e) => {
- $(e.currentTarget).find('.js-artifact-tree-tooltip').tooltip('show');
+ .on('mouseenter', e => {
+ $(e.currentTarget)
+ .find('.js-artifact-tree-tooltip')
+ .tooltip('show');
})
- .on('mouseleave', (e) => {
- $(e.currentTarget).find('.js-artifact-tree-tooltip').tooltip('hide');
+ .on('mouseleave', e => {
+ $(e.currentTarget)
+ .find('.js-artifact-tree-tooltip')
+ .tooltip('hide');
});
}
}
diff --git a/app/assets/javascripts/build_variables.js b/app/assets/javascripts/build_variables.js
deleted file mode 100644
index d398e4a4c83..00000000000
--- a/app/assets/javascripts/build_variables.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import $ from 'jquery';
-
-export default function handleRevealVariables() {
- $('.js-reveal-variables')
- .off('click')
- .on('click', function click() {
- $('.js-build-variables').toggle();
- $(this).hide();
- });
-}
diff --git a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
index b33adff609f..c7a917457f3 100644
--- a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
@@ -7,11 +7,13 @@ import statusCodes from '../lib/utils/http_status';
import VariableList from './ci_variable_list';
function generateErrorBoxContent(errors) {
- const errorList = [].concat(errors).map(errorString => `
+ const errorList = [].concat(errors).map(
+ errorString => `
<li>
${_.escape(errorString)}
</li>
- `);
+ `,
+ );
return `
<p>
@@ -25,13 +27,7 @@ function generateErrorBoxContent(errors) {
// Used for the variable list on CI/CD projects/groups settings page
export default class AjaxVariableList {
- constructor({
- container,
- saveButton,
- errorBox,
- formField = 'variables',
- saveEndpoint,
- }) {
+ constructor({ container, saveButton, errorBox, formField = 'variables', saveEndpoint }) {
this.container = container;
this.saveButton = saveButton;
this.errorBox = errorBox;
@@ -51,25 +47,28 @@ export default class AjaxVariableList {
}
onSaveClicked() {
- const loadingIcon = this.saveButton.querySelector('.js-secret-variables-save-loading-icon');
+ const loadingIcon = this.saveButton.querySelector('.js-ci-variables-save-loading-icon');
loadingIcon.classList.toggle('hide', false);
this.errorBox.classList.toggle('hide', true);
// We use this to prevent a user from changing a key before we have a chance
// to match it up in `updateRowsWithPersistedVariables`
this.variableList.toggleEnableRow(false);
- return axios.patch(this.saveEndpoint, {
- variables_attributes: this.variableList.getAllData(),
- }, {
- // We want to be able to process the `res.data` from a 400 error response
- // and print the validation messages such as duplicate variable keys
- validateStatus: status => (
- status >= statusCodes.OK &&
- status < statusCodes.MULTIPLE_CHOICES
- ) ||
- status === statusCodes.BAD_REQUEST,
- })
- .then((res) => {
+ return axios
+ .patch(
+ this.saveEndpoint,
+ {
+ variables_attributes: this.variableList.getAllData(),
+ },
+ {
+ // We want to be able to process the `res.data` from a 400 error response
+ // and print the validation messages such as duplicate variable keys
+ validateStatus: status =>
+ (status >= statusCodes.OK && status < statusCodes.MULTIPLE_CHOICES) ||
+ status === statusCodes.BAD_REQUEST,
+ },
+ )
+ .then(res => {
loadingIcon.classList.toggle('hide', true);
this.variableList.toggleEnableRow(true);
@@ -90,18 +89,21 @@ export default class AjaxVariableList {
}
updateRowsWithPersistedVariables(persistedVariables = []) {
- const persistedVariableMap = [].concat(persistedVariables).reduce((variableMap, variable) => ({
- ...variableMap,
- [variable.key]: variable,
- }), {});
+ const persistedVariableMap = [].concat(persistedVariables).reduce(
+ (variableMap, variable) => ({
+ ...variableMap,
+ [variable.key]: variable,
+ }),
+ {},
+ );
- this.container.querySelectorAll('.js-row').forEach((row) => {
+ this.container.querySelectorAll('.js-row').forEach(row => {
// If we submitted a row that was destroyed, remove it so we don't try
// to destroy it again which would cause a BE error
const destroyInput = row.querySelector('.js-ci-variable-input-destroy');
if (convertPermissionToBoolean(destroyInput.value)) {
row.remove();
- // Update the ID input so any future edits and `_destroy` will apply on the BE
+ // Update the ID input so any future edits and `_destroy` will apply on the BE
} else {
const key = row.querySelector('.js-ci-variable-input-key').value;
const persistedVariable = persistedVariableMap[key];
diff --git a/app/assets/javascripts/ci_variable_list/ci_variable_list.js b/app/assets/javascripts/ci_variable_list/ci_variable_list.js
index 47efb3a8cee..7bdc18ce03e 100644
--- a/app/assets/javascripts/ci_variable_list/ci_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/ci_variable_list.js
@@ -16,10 +16,7 @@ function createEnvironmentItem(value) {
}
export default class VariableList {
- constructor({
- container,
- formField,
- }) {
+ constructor({ container, formField }) {
this.$container = $(container);
this.formField = formField;
this.environmentDropdownMap = new WeakMap();
@@ -71,7 +68,7 @@ export default class VariableList {
this.initRow(rowEl);
});
- this.$container.on('click', '.js-row-remove-button', (e) => {
+ this.$container.on('click', '.js-row-remove-button', e => {
e.preventDefault();
this.removeRow($(e.currentTarget).closest('.js-row'));
});
@@ -81,7 +78,7 @@ export default class VariableList {
.join(',');
// Remove any empty rows except the last row
- this.$container.on('blur', inputSelector, (e) => {
+ this.$container.on('blur', inputSelector, e => {
const $row = $(e.currentTarget).closest('.js-row');
if ($row.is(':not(:last-child)') && !this.checkIfRowTouched($row)) {
@@ -136,7 +133,7 @@ export default class VariableList {
$rowClone.removeAttr('data-is-persisted');
// Reset the inputs to their defaults
- Object.keys(this.inputMap).forEach((name) => {
+ Object.keys(this.inputMap).forEach(name => {
const entry = this.inputMap[name];
$rowClone.find(entry.selector).val(entry.default);
});
@@ -171,7 +168,7 @@ export default class VariableList {
}
checkIfRowTouched($row) {
- return Object.keys(this.inputMap).some((name) => {
+ return Object.keys(this.inputMap).some(name => {
const entry = this.inputMap[name];
const $el = $row.find(entry.selector);
return $el.length && $el.val() !== entry.default;
@@ -190,11 +187,14 @@ export default class VariableList {
getAllData() {
// Ignore the last empty row because we don't want to try persist
// a blank variable and run into validation problems.
- const validRows = this.$container.find('.js-row').toArray().slice(0, -1);
+ const validRows = this.$container
+ .find('.js-row')
+ .toArray()
+ .slice(0, -1);
- return validRows.map((rowEl) => {
+ return validRows.map(rowEl => {
const resultant = {};
- Object.keys(this.inputMap).forEach((name) => {
+ Object.keys(this.inputMap).forEach(name => {
const entry = this.inputMap[name];
const $input = $(rowEl).find(entry.selector);
if ($input.length) {
@@ -207,11 +207,16 @@ export default class VariableList {
}
getEnvironmentValues() {
- const valueMap = this.$container.find(this.inputMap.environment_scope.selector).toArray()
- .reduce((prevValueMap, envInput) => ({
- ...prevValueMap,
- [envInput.value]: envInput.value,
- }), {});
+ const valueMap = this.$container
+ .find(this.inputMap.environment_scope.selector)
+ .toArray()
+ .reduce(
+ (prevValueMap, envInput) => ({
+ ...prevValueMap,
+ [envInput.value]: envInput.value,
+ }),
+ {},
+ );
return Object.keys(valueMap).map(createEnvironmentItem);
}
diff --git a/app/assets/javascripts/ci_variable_list/native_form_variable_list.js b/app/assets/javascripts/ci_variable_list/native_form_variable_list.js
index 7cd5916ac9c..e7111c666a2 100644
--- a/app/assets/javascripts/ci_variable_list/native_form_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/native_form_variable_list.js
@@ -2,10 +2,7 @@ import $ from 'jquery';
import VariableList from './ci_variable_list';
// Used for the variable list on scheduled pipeline edit page
-export default function setupNativeFormVariableList({
- container,
- formField = 'variables',
-}) {
+export default function setupNativeFormVariableList({ container, formField = 'variables' }) {
const $container = $(container);
const variableList = new VariableList({
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index 0fdf0c7a389..71fc2ac7d80 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -1,19 +1,15 @@
import Visibility from 'visibilityjs';
import Vue from 'vue';
+import initDismissableCallout from '~/dismissable_callout';
import { s__, sprintf } from '../locale';
import Flash from '../flash';
import Poll from '../lib/utils/poll';
import initSettingsPanels from '../settings_panels';
import eventHub from './event_hub';
-import {
- APPLICATION_STATUS,
- REQUEST_LOADING,
- REQUEST_SUCCESS,
- REQUEST_FAILURE,
-} from './constants';
+import { APPLICATION_STATUS, REQUEST_LOADING, REQUEST_SUCCESS, REQUEST_FAILURE } from './constants';
import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store';
-import applications from './components/applications.vue';
+import Applications from './components/applications.vue';
import setupToggleButtons from '../toggle_buttons';
/**
@@ -32,8 +28,10 @@ export default class Clusters {
installIngressPath,
installRunnerPath,
installJupyterPath,
+ installKnativePath,
installPrometheusPath,
managePrometheusPath,
+ clusterType,
clusterStatus,
clusterStatusReason,
helpPath,
@@ -53,6 +51,7 @@ export default class Clusters {
installRunnerEndpoint: installRunnerPath,
installPrometheusEndpoint: installPrometheusPath,
installJupyterEndpoint: installJupyterPath,
+ installKnativeEndpoint: installKnativePath,
});
this.installApplication = this.installApplication.bind(this);
@@ -66,9 +65,10 @@ export default class Clusters {
this.showTokenButton = document.querySelector('.js-show-cluster-token');
this.tokenField = document.querySelector('.js-cluster-token');
+ initDismissableCallout('.js-cluster-security-warning');
initSettingsPanels();
setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area'));
- this.initApplications();
+ this.initApplications(clusterType);
if (this.store.state.status !== 'created') {
this.updateContainer(null, this.store.state.status, this.store.state.statusReason);
@@ -80,23 +80,21 @@ export default class Clusters {
}
}
- initApplications() {
+ initApplications(type) {
const { store } = this;
const el = document.querySelector('#js-cluster-applications');
this.applications = new Vue({
el,
- components: {
- applications,
- },
data() {
return {
state: store.state,
};
},
render(createElement) {
- return createElement('applications', {
+ return createElement(Applications, {
props: {
+ type,
applications: this.state.applications,
helpPath: this.state.helpPath,
ingressHelpPath: this.state.ingressHelpPath,
@@ -129,7 +127,8 @@ export default class Clusters {
if (!Visibility.hidden()) {
this.poll.makeRequest();
} else {
- this.service.fetchData()
+ this.service
+ .fetchData()
.then(data => this.handleSuccess(data))
.catch(() => Clusters.handleError());
}
@@ -177,15 +176,21 @@ export default class Clusters {
checkForNewInstalls(prevApplicationMap, newApplicationMap) {
const appTitles = Object.keys(newApplicationMap)
- .filter(appId => newApplicationMap[appId].status === APPLICATION_STATUS.INSTALLED &&
- prevApplicationMap[appId].status !== APPLICATION_STATUS.INSTALLED &&
- prevApplicationMap[appId].status !== null)
+ .filter(
+ appId =>
+ newApplicationMap[appId].status === APPLICATION_STATUS.INSTALLED &&
+ prevApplicationMap[appId].status !== APPLICATION_STATUS.INSTALLED &&
+ prevApplicationMap[appId].status !== null,
+ )
.map(appId => newApplicationMap[appId].title);
if (appTitles.length > 0) {
- const text = sprintf(s__('ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster'), {
- appList: appTitles.join(', '),
- });
+ const text = sprintf(
+ s__('ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster'),
+ {
+ appList: appTitles.join(', '),
+ },
+ );
Flash(text, 'notice', this.successApplicationContainer);
}
}
@@ -218,13 +223,18 @@ export default class Clusters {
this.store.updateAppProperty(appId, 'requestStatus', REQUEST_LOADING);
this.store.updateAppProperty(appId, 'requestReason', null);
- this.service.installApplication(appId, data.params)
+ this.service
+ .installApplication(appId, data.params)
.then(() => {
this.store.updateAppProperty(appId, 'requestStatus', REQUEST_SUCCESS);
})
.catch(() => {
this.store.updateAppProperty(appId, 'requestStatus', REQUEST_FAILURE);
- this.store.updateAppProperty(appId, 'requestReason', s__('ClusterIntegration|Request to begin installing failed'));
+ this.store.updateAppProperty(
+ appId,
+ 'requestReason',
+ s__('ClusterIntegration|Request to begin installing failed'),
+ );
});
}
diff --git a/app/assets/javascripts/clusters/clusters_index.js b/app/assets/javascripts/clusters/clusters_index.js
deleted file mode 100644
index 1e5c733d151..00000000000
--- a/app/assets/javascripts/clusters/clusters_index.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import createFlash from '~/flash';
-import { __ } from '~/locale';
-import setupToggleButtons from '~/toggle_buttons';
-import gcpSignupOffer from '~/clusters/components/gcp_signup_offer';
-
-import ClustersService from './services/clusters_service';
-
-export default () => {
- const clusterList = document.querySelector('.js-clusters-list');
-
- gcpSignupOffer();
-
- // The empty state won't have a clusterList
- if (clusterList) {
- setupToggleButtons(document.querySelector('.js-clusters-list'), (value, toggle) =>
- ClustersService.updateCluster(toggle.dataset.endpoint, { cluster: { enabled: value } }).catch(
- err => {
- createFlash(__('Something went wrong on our end.'));
- throw err;
- },
- ),
- );
- }
-};
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index 651f3b50236..236bb1394c8 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -1,164 +1,241 @@
<script>
- /* eslint-disable vue/require-default-prop */
- import { s__, sprintf } from '../../locale';
- import eventHub from '../event_hub';
- import loadingButton from '../../vue_shared/components/loading_button.vue';
- import {
- APPLICATION_STATUS,
- REQUEST_LOADING,
- REQUEST_SUCCESS,
- REQUEST_FAILURE,
- } from '../constants';
+/* eslint-disable vue/require-default-prop */
+import { s__, sprintf } from '../../locale';
+import eventHub from '../event_hub';
+import identicon from '../../vue_shared/components/identicon.vue';
+import loadingButton from '../../vue_shared/components/loading_button.vue';
+import {
+ APPLICATION_STATUS,
+ REQUEST_LOADING,
+ REQUEST_SUCCESS,
+ REQUEST_FAILURE,
+} from '../constants';
- export default {
- components: {
- loadingButton,
- },
- props: {
- id: {
- type: String,
- required: true,
- },
- title: {
- type: String,
- required: true,
- },
- titleLink: {
- type: String,
- required: false,
- },
- manageLink: {
- type: String,
- required: false,
- },
- status: {
- type: String,
- required: false,
- },
- statusReason: {
- type: String,
- required: false,
- },
- requestStatus: {
- type: String,
- required: false,
- },
- requestReason: {
- type: String,
- required: false,
- },
- installApplicationRequestParams: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- },
- computed: {
- isUnknownStatus() {
- return !this.isKnownStatus && this.status !== null;
- },
- isKnownStatus() {
- return Object.values(APPLICATION_STATUS).includes(this.status);
- },
- rowJsClass() {
- return `js-cluster-application-row-${this.id}`;
- },
- installButtonLoading() {
- return !this.status ||
- this.status === APPLICATION_STATUS.SCHEDULED ||
- this.status === APPLICATION_STATUS.INSTALLING ||
- this.requestStatus === REQUEST_LOADING;
- },
- installButtonDisabled() {
- // Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but
- // we already made a request to install and are just waiting for the real-time
- // to sync up.
- return ((this.status !== APPLICATION_STATUS.INSTALLABLE
- && this.status !== APPLICATION_STATUS.ERROR) ||
+export default {
+ components: {
+ loadingButton,
+ identicon,
+ },
+ props: {
+ id: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ titleLink: {
+ type: String,
+ required: false,
+ },
+ manageLink: {
+ type: String,
+ required: false,
+ },
+ logoUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ status: {
+ type: String,
+ required: false,
+ },
+ statusReason: {
+ type: String,
+ required: false,
+ },
+ requestStatus: {
+ type: String,
+ required: false,
+ },
+ requestReason: {
+ type: String,
+ required: false,
+ },
+ installApplicationRequestParams: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ computed: {
+ isUnknownStatus() {
+ return !this.isKnownStatus && this.status !== null;
+ },
+ isKnownStatus() {
+ return Object.values(APPLICATION_STATUS).includes(this.status);
+ },
+ isInstalled() {
+ return (
+ this.status === APPLICATION_STATUS.INSTALLED ||
+ this.status === APPLICATION_STATUS.UPDATED ||
+ this.status === APPLICATION_STATUS.UPDATING
+ );
+ },
+ hasLogo() {
+ return !!this.logoUrl;
+ },
+ identiconId() {
+ // generate a deterministic integer id for the identicon background
+ return this.id.charCodeAt(0);
+ },
+ rowJsClass() {
+ return `js-cluster-application-row-${this.id}`;
+ },
+ installButtonLoading() {
+ return (
+ !this.status ||
+ this.status === APPLICATION_STATUS.SCHEDULED ||
+ this.status === APPLICATION_STATUS.INSTALLING ||
+ this.requestStatus === REQUEST_LOADING
+ );
+ },
+ installButtonDisabled() {
+ // Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but
+ // we already made a request to install and are just waiting for the real-time
+ // to sync up.
+ return (
+ ((this.status !== APPLICATION_STATUS.INSTALLABLE &&
+ this.status !== APPLICATION_STATUS.ERROR) ||
this.requestStatus === REQUEST_LOADING ||
- this.requestStatus === REQUEST_SUCCESS) && this.isKnownStatus;
- },
- installButtonLabel() {
- let label;
- if (
- this.status === APPLICATION_STATUS.NOT_INSTALLABLE ||
- this.status === APPLICATION_STATUS.INSTALLABLE ||
- this.status === APPLICATION_STATUS.ERROR ||
- this.isUnknownStatus
- ) {
- label = s__('ClusterIntegration|Install');
- } else if (this.status === APPLICATION_STATUS.SCHEDULED ||
- this.status === APPLICATION_STATUS.INSTALLING) {
- label = s__('ClusterIntegration|Installing');
- } else if (this.status === APPLICATION_STATUS.INSTALLED ||
- this.status === APPLICATION_STATUS.UPDATED) {
- label = s__('ClusterIntegration|Installed');
- }
+ this.requestStatus === REQUEST_SUCCESS) &&
+ this.isKnownStatus
+ );
+ },
+ installButtonLabel() {
+ let label;
+ if (
+ this.status === APPLICATION_STATUS.NOT_INSTALLABLE ||
+ this.status === APPLICATION_STATUS.INSTALLABLE ||
+ this.status === APPLICATION_STATUS.ERROR ||
+ this.isUnknownStatus
+ ) {
+ label = s__('ClusterIntegration|Install');
+ } else if (
+ this.status === APPLICATION_STATUS.SCHEDULED ||
+ this.status === APPLICATION_STATUS.INSTALLING
+ ) {
+ label = s__('ClusterIntegration|Installing');
+ } else if (
+ this.status === APPLICATION_STATUS.INSTALLED ||
+ this.status === APPLICATION_STATUS.UPDATED ||
+ this.status === APPLICATION_STATUS.UPDATING
+ ) {
+ label = s__('ClusterIntegration|Installed');
+ }
- return label;
- },
- showManageButton() {
- return this.manageLink && this.status === APPLICATION_STATUS.INSTALLED;
- },
- manageButtonLabel() {
- return s__('ClusterIntegration|Manage');
- },
- hasError() {
- return this.status === APPLICATION_STATUS.ERROR ||
- this.requestStatus === REQUEST_FAILURE;
- },
- generalErrorDescription() {
- return sprintf(
- s__('ClusterIntegration|Something went wrong while installing %{title}'), {
- title: this.title,
- },
- );
- },
- },
- methods: {
- installClicked() {
- eventHub.$emit('installApplication', {
- id: this.id,
- params: this.installApplicationRequestParams,
- });
- },
- },
- };
+ return label;
+ },
+ showManageButton() {
+ return this.manageLink && this.status === APPLICATION_STATUS.INSTALLED;
+ },
+ manageButtonLabel() {
+ return s__('ClusterIntegration|Manage');
+ },
+ hasError() {
+ return this.status === APPLICATION_STATUS.ERROR || this.requestStatus === REQUEST_FAILURE;
+ },
+ generalErrorDescription() {
+ return sprintf(s__('ClusterIntegration|Something went wrong while installing %{title}'), {
+ title: this.title,
+ });
+ },
+ },
+ methods: {
+ installClicked() {
+ eventHub.$emit('installApplication', {
+ id: this.id,
+ params: this.installApplicationRequestParams,
+ });
+ },
+ },
+};
</script>
<template>
<div
- :class="rowJsClass"
- class="gl-responsive-table-row gl-responsive-table-row-col-span"
+ :class="[
+ rowJsClass,
+ isInstalled && 'cluster-application-installed',
+ disabled && 'cluster-application-disabled'
+ ]"
+ class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span"
>
<div
class="gl-responsive-table-row-layout"
role="row"
>
- <a
- v-if="titleLink"
- :href="titleLink"
- target="blank"
- rel="noopener noreferrer"
+ <div
+ class="table-section append-right-8 section-align-top"
role="gridcell"
- class="table-section section-15 section-align-top js-cluster-application-title"
- >
- {{ title }}
- </a>
- <span
- v-else
- class="table-section section-15 section-align-top js-cluster-application-title"
>
- {{ title }}
- </span>
+ <img
+ v-if="hasLogo"
+ :src="logoUrl"
+ :alt="`${title} logo`"
+ class="cluster-application-logo avatar s40"
+ />
+ <identicon
+ v-else
+ :entity-id="identiconId"
+ :entity-name="title"
+ size-class="s40"
+ />
+ </div>
<div
- class="table-section section-wrap"
+ class="table-section cluster-application-description section-wrap"
role="gridcell"
>
+ <strong>
+ <a
+ v-if="titleLink"
+ :href="titleLink"
+ target="blank"
+ rel="noopener noreferrer"
+ class="js-cluster-application-title"
+ >
+ {{ title }}
+ </a>
+ <span
+ v-else
+ class="js-cluster-application-title"
+ >
+ {{ title }}
+ </span>
+ </strong>
<slot name="description"></slot>
+ <div
+ v-if="hasError || isUnknownStatus"
+ class="cluster-application-error text-danger prepend-top-10"
+ >
+ <p class="js-cluster-application-general-error-message append-bottom-0">
+ {{ generalErrorDescription }}
+ </p>
+ <ul v-if="statusReason || requestReason">
+ <li
+ v-if="statusReason"
+ class="js-cluster-application-status-error-message"
+ >
+ {{ statusReason }}
+ </li>
+ <li
+ v-if="requestReason"
+ class="js-cluster-application-request-error-message"
+ >
+ {{ requestReason }}
+ </li>
+ </ul>
+ </div>
</div>
<div
- :class="{ 'section-20': showManageButton, 'section-15': !showManageButton }"
+ :class="{ 'section-25': showManageButton, 'section-15': !showManageButton }"
class="table-section table-button-footer section-align-top"
role="gridcell"
>
@@ -168,6 +245,7 @@
>
<a
:href="manageLink"
+ :class="{ disabled: disabled }"
class="btn"
>
{{ manageButtonLabel }}
@@ -176,7 +254,7 @@
<div class="btn-group table-action-buttons">
<loading-button
:loading="installButtonLoading"
- :disabled="installButtonDisabled"
+ :disabled="disabled || installButtonDisabled"
:label="installButtonLabel"
class="js-cluster-application-install-button"
@click="installClicked"
@@ -184,35 +262,5 @@
</div>
</div>
</div>
- <div
- v-if="hasError || isUnknownStatus"
- class="gl-responsive-table-row-layout"
- role="row"
- >
- <div
- class="alert alert-danger alert-block append-bottom-0 clusters-error-alert"
- role="gridcell"
- >
- <div>
- <p class="js-cluster-application-general-error-message">
- {{ generalErrorDescription }}
- </p>
- <ul v-if="statusReason || requestReason">
- <li
- v-if="statusReason"
- class="js-cluster-application-status-error-message"
- >
- {{ statusReason }}
- </li>
- <li
- v-if="requestReason"
- class="js-cluster-application-request-error-message"
- >
- {{ requestReason }}
- </li>
- </ul>
- </div>
- </div>
- </div>
</div>
</template>
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index d708a9e595a..c1026d1273a 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -1,9 +1,19 @@
<script>
import _ from 'underscore';
+import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg';
+import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
+import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
+import helmLogo from 'images/cluster_app_logos/helm.png';
+import jeagerLogo from 'images/cluster_app_logos/jeager.png';
+import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
+import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
+import knativeLogo from 'images/cluster_app_logos/knative.png';
+import meltanoLogo from 'images/cluster_app_logos/meltano.png';
+import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
-import { APPLICATION_STATUS, INGRESS } from '../constants';
+import { CLUSTER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants';
export default {
components: {
@@ -11,6 +21,11 @@ export default {
clipboardButton,
},
props: {
+ type: {
+ type: String,
+ required: false,
+ default: CLUSTER_TYPE.PROJECT,
+ },
applications: {
type: Object,
required: false,
@@ -37,21 +52,25 @@ export default {
default: '',
},
},
+ data: () => ({
+ elasticsearchLogo,
+ gitlabLogo,
+ helmLogo,
+ jeagerLogo,
+ jupyterhubLogo,
+ kubernetesLogo,
+ knativeLogo,
+ meltanoLogo,
+ prometheusLogo,
+ }),
computed: {
- generalApplicationDescription() {
- return sprintf(
- _.escape(
- s__(
- `ClusterIntegration|Install applications on your Kubernetes cluster.
- Read more about %{helpLink}`,
- ),
- ),
- {
- helpLink: `<a href="${this.helpPath}">
- ${_.escape(s__('ClusterIntegration|installing applications'))}
- </a>`,
- },
- false,
+ isProjectCluster() {
+ return this.type === CLUSTER_TYPE.PROJECT;
+ },
+ helmInstalled() {
+ return (
+ this.applications.helm.status === APPLICATION_STATUS.INSTALLED ||
+ this.applications.helm.status === APPLICATION_STATUS.UPDATED
);
},
ingressId() {
@@ -127,225 +146,298 @@ export default {
jupyterHostname() {
return this.applications.jupyter.hostname;
},
+ knativeInstalled() {
+ return this.applications.knative.status === APPLICATION_STATUS.INSTALLED;
+ },
+ },
+ created() {
+ this.helmInstallIllustration = helmInstallIllustration;
},
};
</script>
<template>
- <section
- id="cluster-applications"
- class="settings no-animate expanded"
- >
- <div class="settings-header">
- <h4>
- {{ s__('ClusterIntegration|Applications') }}
- </h4>
- <p
- class="append-bottom-0"
- v-html="generalApplicationDescription"
- >
- </p>
- </div>
+ <section id="cluster-applications">
+ <h4>
+ {{ s__('ClusterIntegration|Applications') }}
+ </h4>
+ <p class="append-bottom-0">
+ {{ s__(`ClusterIntegration|Choose which applications to install on your Kubernetes cluster.
+ Helm Tiller is required to install any of the following applications.`) }}
+ <a :href="helpPath">
+ {{ __('More information') }}
+ </a>
+ </p>
- <div class="settings-content">
- <div class="append-bottom-20">
- <application-row
- id="helm"
- :title="applications.helm.title"
- :status="applications.helm.status"
- :status-reason="applications.helm.statusReason"
- :request-status="applications.helm.requestStatus"
- :request-reason="applications.helm.requestReason"
- title-link="https://docs.helm.sh/"
- >
- <div slot="description">
- {{ s__(`ClusterIntegration|Helm streamlines installing
- and managing Kubernetes applications.
- Tiller runs inside of your Kubernetes Cluster,
- and manages releases of your charts.`) }}
- </div>
- </application-row>
- <application-row
- :id="ingressId"
- :title="applications.ingress.title"
- :status="applications.ingress.status"
- :status-reason="applications.ingress.statusReason"
- :request-status="applications.ingress.requestStatus"
- :request-reason="applications.ingress.requestReason"
- title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
+ <div class="cluster-application-list prepend-top-10">
+ <application-row
+ id="helm"
+ :logo-url="helmLogo"
+ :title="applications.helm.title"
+ :status="applications.helm.status"
+ :status-reason="applications.helm.statusReason"
+ :request-status="applications.helm.requestStatus"
+ :request-reason="applications.helm.requestReason"
+ class="rounded-top"
+ title-link="https://docs.helm.sh/"
+ >
+ <div slot="description">
+ {{ s__(`ClusterIntegration|Helm streamlines installing
+ and managing Kubernetes applications.
+ Tiller runs inside of your Kubernetes Cluster,
+ and manages releases of your charts.`) }}
+ </div>
+ </application-row>
+ <div
+ v-show="!helmInstalled"
+ class="cluster-application-warning"
+ >
+ <div
+ class="svg-container"
+ v-html="helmInstallIllustration"
>
- <div slot="description">
- <p>
- {{ s__(`ClusterIntegration|Ingress gives you a way to route
- requests to services based on the request host or path,
- centralizing a number of services into a single entrypoint.`) }}
- </p>
+ </div>
+ {{ s__(`ClusterIntegration|You must first install Helm Tiller before
+ installing the applications below`) }}
+ </div>
+ <application-row
+ :id="ingressId"
+ :logo-url="kubernetesLogo"
+ :title="applications.ingress.title"
+ :status="applications.ingress.status"
+ :status-reason="applications.ingress.statusReason"
+ :request-status="applications.ingress.requestStatus"
+ :request-reason="applications.ingress.requestReason"
+ :disabled="!helmInstalled"
+ title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
+ >
+ <div slot="description">
+ <p>
+ {{ s__(`ClusterIntegration|Ingress gives you a way to route
+ requests to services based on the request host or path,
+ centralizing a number of services into a single entrypoint.`) }}
+ </p>
- <template v-if="ingressInstalled">
- <div class="form-group">
- <label for="ingress-ip-address">
- {{ s__('ClusterIntegration|Ingress IP Address') }}
- </label>
- <div
- v-if="ingressExternalIp"
- class="input-group"
- >
- <input
- id="ingress-ip-address"
- :value="ingressExternalIp"
- type="text"
- class="form-control js-ip-address"
- readonly
- />
- <span class="input-group-append">
- <clipboard-button
- :text="ingressExternalIp"
- :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')"
- class="input-group-text js-clipboard-btn"
- />
- </span>
- </div>
+ <template v-if="ingressInstalled">
+ <div class="form-group">
+ <label for="ingress-ip-address">
+ {{ s__('ClusterIntegration|Ingress IP Address') }}
+ </label>
+ <div
+ v-if="ingressExternalIp"
+ class="input-group"
+ >
<input
- v-else
+ id="ingress-ip-address"
+ :value="ingressExternalIp"
type="text"
class="form-control js-ip-address"
readonly
- value="?"
/>
+ <span class="input-group-append">
+ <clipboard-button
+ :text="ingressExternalIp"
+ :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')"
+ class="input-group-text js-clipboard-btn"
+ />
+ </span>
</div>
+ <input
+ v-else
+ type="text"
+ class="form-control js-ip-address"
+ readonly
+ value="?"
+ />
+ </div>
- <p
- v-if="!ingressExternalIp"
- class="settings-message js-no-ip-message"
- >
- {{ s__(`ClusterIntegration|The IP address is in
- the process of being assigned. Please check your Kubernetes
- cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }}
+ <p
+ v-if="!ingressExternalIp"
+ class="settings-message js-no-ip-message"
+ >
+ {{ s__(`ClusterIntegration|The IP address is in
+ the process of being assigned. Please check your Kubernetes
+ cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }}
- <a
- :href="ingressHelpPath"
- target="_blank"
- rel="noopener noreferrer"
- >
- {{ __('More information') }}
- </a>
- </p>
+ <a
+ :href="ingressHelpPath"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {{ __('More information') }}
+ </a>
+ </p>
- <p>
- {{ s__(`ClusterIntegration|Point a wildcard DNS to this
- generated IP address in order to access
- your application after it has been deployed.`) }}
- <a
- :href="ingressDnsHelpPath"
- target="_blank"
- rel="noopener noreferrer"
- >
- {{ __('More information') }}
- </a>
- </p>
+ <p>
+ {{ s__(`ClusterIntegration|Point a wildcard DNS to this
+ generated IP address in order to access
+ your application after it has been deployed.`) }}
+ <a
+ :href="ingressDnsHelpPath"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {{ __('More information') }}
+ </a>
+ </p>
- </template>
- <div
- v-else
- v-html="ingressDescription"
- >
- </div>
- </div>
- </application-row>
- <application-row
- id="prometheus"
- :title="applications.prometheus.title"
- :manage-link="managePrometheusPath"
- :status="applications.prometheus.status"
- :status-reason="applications.prometheus.statusReason"
- :request-status="applications.prometheus.requestStatus"
- :request-reason="applications.prometheus.requestReason"
- title-link="https://prometheus.io/docs/introduction/overview/"
- >
+ </template>
<div
- slot="description"
- v-html="prometheusDescription"
+ v-html="ingressDescription"
>
</div>
- </application-row>
- <application-row
- id="runner"
- :title="applications.runner.title"
- :status="applications.runner.status"
- :status-reason="applications.runner.statusReason"
- :request-status="applications.runner.requestStatus"
- :request-reason="applications.runner.requestReason"
- title-link="https://docs.gitlab.com/runner/"
- >
- <div slot="description">
- {{ s__(`ClusterIntegration|GitLab Runner connects to this
- project's repository and executes CI/CD jobs,
- pushing results back and deploying,
- applications to production.`) }}
- </div>
- </application-row>
- <application-row
- id="jupyter"
- :title="applications.jupyter.title"
- :status="applications.jupyter.status"
- :status-reason="applications.jupyter.statusReason"
- :request-status="applications.jupyter.requestStatus"
- :request-reason="applications.jupyter.requestReason"
- :install-application-request-params="{ hostname: applications.jupyter.hostname }"
- title-link="https://jupyterhub.readthedocs.io/en/stable/"
+ </div>
+ </application-row>
+ <application-row
+ v-if="isProjectCluster"
+ id="prometheus"
+ :logo-url="prometheusLogo"
+ :title="applications.prometheus.title"
+ :manage-link="managePrometheusPath"
+ :status="applications.prometheus.status"
+ :status-reason="applications.prometheus.statusReason"
+ :request-status="applications.prometheus.requestStatus"
+ :request-reason="applications.prometheus.requestReason"
+ :disabled="!helmInstalled"
+ title-link="https://prometheus.io/docs/introduction/overview/"
+ >
+ <div
+ slot="description"
+ v-html="prometheusDescription"
>
- <div slot="description">
- <p>
- {{ s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns,
- manages, and proxies multiple instances of the single-user
- Jupyter notebook server. JupyterHub can be used to serve
- notebooks to a class of students, a corporate data science group,
- or a scientific research group.`) }}
- </p>
+ </div>
+ </application-row>
+ <application-row
+ v-if="isProjectCluster"
+ id="runner"
+ :logo-url="gitlabLogo"
+ :title="applications.runner.title"
+ :status="applications.runner.status"
+ :status-reason="applications.runner.statusReason"
+ :request-status="applications.runner.requestStatus"
+ :request-reason="applications.runner.requestReason"
+ :disabled="!helmInstalled"
+ title-link="https://docs.gitlab.com/runner/"
+ >
+ <div slot="description">
+ {{ s__(`ClusterIntegration|GitLab Runner connects to this
+ project's repository and executes CI/CD jobs,
+ pushing results back and deploying,
+ applications to production.`) }}
+ </div>
+ </application-row>
+ <application-row
+ v-if="isProjectCluster"
+ id="jupyter"
+ :logo-url="jupyterhubLogo"
+ :title="applications.jupyter.title"
+ :status="applications.jupyter.status"
+ :status-reason="applications.jupyter.statusReason"
+ :request-status="applications.jupyter.requestStatus"
+ :request-reason="applications.jupyter.requestReason"
+ :install-application-request-params="{ hostname: applications.jupyter.hostname }"
+ :disabled="!helmInstalled"
+ title-link="https://jupyterhub.readthedocs.io/en/stable/"
+ >
+ <div slot="description">
+ <p>
+ {{ s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns,
+ manages, and proxies multiple instances of the single-user
+ Jupyter notebook server. JupyterHub can be used to serve
+ notebooks to a class of students, a corporate data science group,
+ or a scientific research group.`) }}
+ </p>
- <template v-if="ingressExternalIp">
- <div class="form-group">
- <label for="jupyter-hostname">
- {{ s__('ClusterIntegration|Jupyter Hostname') }}
- </label>
+ <template v-if="ingressExternalIp">
+ <div class="form-group">
+ <label for="jupyter-hostname">
+ {{ s__('ClusterIntegration|Jupyter Hostname') }}
+ </label>
- <div class="input-group">
- <input
- v-model="applications.jupyter.hostname"
- :readonly="jupyterInstalled"
- type="text"
- class="form-control js-hostname"
+ <div class="input-group">
+ <input
+ v-model="applications.jupyter.hostname"
+ :readonly="jupyterInstalled"
+ type="text"
+ class="form-control js-hostname"
+ />
+ <span
+ class="input-group-btn"
+ >
+ <clipboard-button
+ :text="jupyterHostname"
+ :title="s__('ClusterIntegration|Copy Jupyter Hostname to clipboard')"
+ class="js-clipboard-btn"
/>
- <span
- class="input-group-btn"
- >
- <clipboard-button
- :text="jupyterHostname"
- :title="s__('ClusterIntegration|Copy Jupyter Hostname to clipboard')"
- class="js-clipboard-btn"
- />
- </span>
- </div>
+ </span>
</div>
- <p v-if="ingressInstalled">
- {{ s__(`ClusterIntegration|Replace this with your own hostname if you want.
- If you do so, point hostname to Ingress IP Address from above.`) }}
- <a
- :href="ingressDnsHelpPath"
- target="_blank"
- rel="noopener noreferrer"
- >
- {{ __('More information') }}
- </a>
- </p>
- </template>
- </div>
- </application-row>
- <!--
- NOTE: Don't forget to update `clusters.scss`
- min-height for this block and uncomment `application_spec` tests
- -->
- </div>
+ </div>
+ <p v-if="ingressInstalled">
+ {{ s__(`ClusterIntegration|Replace this with your own hostname if you want.
+ If you do so, point hostname to Ingress IP Address from above.`) }}
+ <a
+ :href="ingressDnsHelpPath"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {{ __('More information') }}
+ </a>
+ </p>
+ </template>
+ </div>
+ </application-row>
+ <application-row
+ id="knative"
+ :logo-url="knativeLogo"
+ :title="applications.knative.title"
+ :status="applications.knative.status"
+ :status-reason="applications.knative.statusReason"
+ :request-status="applications.knative.requestStatus"
+ :request-reason="applications.knative.requestReason"
+ :install-application-request-params="{ hostname: applications.knative.hostname}"
+ :disabled="!helmInstalled"
+ class="hide-bottom-border rounded-bottom"
+ title-link="https://github.com/knative/docs"
+ >
+ <div slot="description">
+ <p>
+ {{ s__(`ClusterIntegration|A Knative build extends Kubernetes
+ and utilizes existing Kubernetes primitives to provide you with
+ the ability to run on-cluster container builds from source.
+ For example, you can write a build that uses Kubernetes-native
+ resources to obtain your source code from a repository,
+ build it into container a image, and then run that image.`) }}
+ </p>
+
+ <template v-if="knativeInstalled">
+ <div class="form-group">
+ <label for="knative-domainname">
+ {{ s__('ClusterIntegration|Knative Domain Name:') }}
+ </label>
+ <input
+ id="knative-domainname"
+ v-model="applications.knative.hostname"
+ type="text"
+ class="form-control js-domainname"
+ readonly
+ />
+ </div>
+ </template>
+ <template v-else>
+ <div class="form-group">
+ <label for="knative-domainname">
+ {{ s__('ClusterIntegration|Knative Domain Name:') }}
+ </label>
+ <input
+ id="knative-domainname"
+ v-model="applications.knative.hostname"
+ type="text"
+ class="form-control js-domainname"
+ />
+ </div>
+ </template>
+ </div>
+ </application-row>
</div>
</section>
</template>
diff --git a/app/assets/javascripts/clusters/components/gcp_signup_offer.js b/app/assets/javascripts/clusters/components/gcp_signup_offer.js
deleted file mode 100644
index 8bc20a1c09f..00000000000
--- a/app/assets/javascripts/clusters/components/gcp_signup_offer.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import $ from 'jquery';
-import axios from '~/lib/utils/axios_utils';
-import { __ } from '~/locale';
-import Flash from '~/flash';
-
-export default function gcpSignupOffer() {
- const alertEl = document.querySelector('.gcp-signup-offer');
- if (!alertEl) {
- return;
- }
-
- const closeButtonEl = alertEl.getElementsByClassName('close')[0];
- const { dismissEndpoint, featureId } = closeButtonEl.dataset;
-
- closeButtonEl.addEventListener('click', () => {
- axios
- .post(dismissEndpoint, {
- feature_name: featureId,
- })
- .then(() => {
- $(alertEl).alert('close');
- })
- .catch(() => {
- Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
- });
- });
-}
diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js
index 72fc9355d82..15cf4a56138 100644
--- a/app/assets/javascripts/clusters/constants.js
+++ b/app/assets/javascripts/clusters/constants.js
@@ -1,3 +1,10 @@
+// These need to match the enum found in app/models/clusters/cluster.rb
+export const CLUSTER_TYPE = {
+ INSTANCE: 'instance_type',
+ GROUP: 'group_type',
+ PROJECT: 'project_type',
+};
+
// These need to match what is returned from the server
export const APPLICATION_STATUS = {
NOT_INSTALLABLE: 'not_installable',
@@ -6,6 +13,7 @@ export const APPLICATION_STATUS = {
INSTALLING: 'installing',
INSTALLED: 'installed',
UPDATED: 'updated',
+ UPDATING: 'updating',
ERROR: 'errored',
};
@@ -15,3 +23,4 @@ export const REQUEST_SUCCESS = 'request-success';
export const REQUEST_FAILURE = 'request-failure';
export const INGRESS = 'ingress';
export const JUPYTER = 'jupyter';
+export const KNATIVE = 'knative';
diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js
index a7d82292ba9..da562b09ee5 100644
--- a/app/assets/javascripts/clusters/services/clusters_service.js
+++ b/app/assets/javascripts/clusters/services/clusters_service.js
@@ -9,6 +9,7 @@ export default class ClusterService {
runner: this.options.installRunnerEndpoint,
prometheus: this.options.installPrometheusEndpoint,
jupyter: this.options.installJupyterEndpoint,
+ knative: this.options.installKnativeEndpoint,
};
}
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index d90db7b103c..e45da967392 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -1,5 +1,5 @@
import { s__ } from '../../locale';
-import { INGRESS, JUPYTER } from '../constants';
+import { INGRESS, JUPYTER, KNATIVE } from '../constants';
export default class ClusterStore {
constructor() {
@@ -46,6 +46,14 @@ export default class ClusterStore {
requestReason: null,
hostname: null,
},
+ knative: {
+ title: s__('ClusterIntegration|Knative'),
+ status: null,
+ statusReason: null,
+ requestStatus: null,
+ requestReason: null,
+ hostname: null,
+ },
},
};
}
@@ -76,12 +84,8 @@ export default class ClusterStore {
this.state.status = serverState.status;
this.state.statusReason = serverState.status_reason;
- serverState.applications.forEach((serverAppEntry) => {
- const {
- name: appId,
- status,
- status_reason: statusReason,
- } = serverAppEntry;
+ serverState.applications.forEach(serverAppEntry => {
+ const { name: appId, status, status_reason: statusReason } = serverAppEntry;
this.state.applications[appId] = {
...(this.state.applications[appId] || {}),
@@ -97,6 +101,9 @@ export default class ClusterStore {
(this.state.applications.ingress.externalIp
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
: '');
+ } else if (appId === KNATIVE) {
+ this.state.applications.knative.hostname =
+ serverAppEntry.hostname || this.state.applications.knative.hostname;
}
});
}
diff --git a/app/assets/javascripts/comment_type_toggle.js b/app/assets/javascripts/comment_type_toggle.js
index c74184949df..a259667bb75 100644
--- a/app/assets/javascripts/comment_type_toggle.js
+++ b/app/assets/javascripts/comment_type_toggle.js
@@ -24,36 +24,44 @@ class CommentTypeToggle {
setConfig() {
const config = {
- InputSetter: [{
- input: this.noteTypeInput,
- valueAttribute: 'data-value',
- },
- {
- input: this.submitButton,
- valueAttribute: 'data-submit-text',
- }],
+ InputSetter: [
+ {
+ input: this.noteTypeInput,
+ valueAttribute: 'data-value',
+ },
+ {
+ input: this.submitButton,
+ valueAttribute: 'data-submit-text',
+ },
+ ],
};
if (this.closeButton) {
- config.InputSetter.push({
- input: this.closeButton,
- valueAttribute: 'data-close-text',
- }, {
- input: this.closeButton,
- valueAttribute: 'data-close-text',
- inputAttribute: 'data-alternative-text',
- });
+ config.InputSetter.push(
+ {
+ input: this.closeButton,
+ valueAttribute: 'data-close-text',
+ },
+ {
+ input: this.closeButton,
+ valueAttribute: 'data-close-text',
+ inputAttribute: 'data-alternative-text',
+ },
+ );
}
if (this.reopenButton) {
- config.InputSetter.push({
- input: this.reopenButton,
- valueAttribute: 'data-reopen-text',
- }, {
- input: this.reopenButton,
- valueAttribute: 'data-reopen-text',
- inputAttribute: 'data-alternative-text',
- });
+ config.InputSetter.push(
+ {
+ input: this.reopenButton,
+ valueAttribute: 'data-reopen-text',
+ },
+ {
+ input: this.reopenButton,
+ valueAttribute: 'data-reopen-text',
+ inputAttribute: 'data-alternative-text',
+ },
+ );
}
return config;
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 410580b4c25..d4ecfa4aa93 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, no-var, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, max-len */
+/* eslint-disable func-names, no-var, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, one-var, no-unused-vars, no-return-assign, no-unused-expressions, no-sequences */
import $ from 'jquery';
@@ -9,44 +9,60 @@ const viewModes = ['two-up', 'swipe'];
export default class ImageFile {
constructor(file) {
this.file = file;
- this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
- return function(deletedWidth, deletedHeight) {
- return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
- _this.initViewModes();
-
- // Load two-up view after images are loaded
- // so that we can display the correct width and height information
- const $images = $('.two-up.view img', _this.file);
-
- $images.waitForImages(function() {
- _this.initView('two-up');
+ this.requestImageInfo(
+ $('.two-up.view .frame.deleted img', this.file),
+ (function(_this) {
+ return function(deletedWidth, deletedHeight) {
+ return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(
+ width,
+ height,
+ ) {
+ _this.initViewModes();
+
+ // Load two-up view after images are loaded
+ // so that we can display the correct width and height information
+ const $images = $('.two-up.view img', _this.file);
+
+ $images.waitForImages(function() {
+ _this.initView('two-up');
+ });
});
- });
- };
- })(this));
+ };
+ })(this),
+ );
}
initViewModes() {
const viewMode = viewModes[0];
$('.view-modes', this.file).removeClass('hide');
- $('.view-modes-menu', this.file).on('click', 'li', (function(_this) {
- return function(event) {
- if (!$(event.currentTarget).hasClass('active')) {
- return _this.activateViewMode(event.currentTarget.className);
- }
- };
- })(this));
+ $('.view-modes-menu', this.file).on(
+ 'click',
+ 'li',
+ (function(_this) {
+ return function(event) {
+ if (!$(event.currentTarget).hasClass('active')) {
+ return _this.activateViewMode(event.currentTarget.className);
+ }
+ };
+ })(this),
+ );
return this.activateViewMode(viewMode);
}
activateViewMode(viewMode) {
- $('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active');
- return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) {
- return function() {
- $(".view." + viewMode, _this.file).fadeIn(200);
- return _this.initView(viewMode);
- };
- })(this));
+ $('.view-modes-menu li', this.file)
+ .removeClass('active')
+ .filter('.' + viewMode)
+ .addClass('active');
+ return $('.view:visible:not(.' + viewMode + ')', this.file).fadeOut(
+ 200,
+ (function(_this) {
+ return function() {
+ $('.view.' + viewMode, _this.file).fadeIn(200);
+ return _this.initView(viewMode);
+ };
+ })(this),
+ );
}
initView(viewMode) {
@@ -63,135 +79,154 @@ export default class ImageFile {
$body.css('user-select', 'none');
});
- $body.off('mouseup').off('mousemove').on('mouseup', function() {
- dragging = false;
- $body.css('user-select', '');
- })
- .on('mousemove', function(e) {
- var left;
- if (!dragging) return;
-
- left = e.pageX - ($offsetEl.offset().left + padding);
-
- callback(e, left);
- });
+ $body
+ .off('mouseup')
+ .off('mousemove')
+ .on('mouseup', function() {
+ dragging = false;
+ $body.css('user-select', '');
+ })
+ .on('mousemove', function(e) {
+ var left;
+ if (!dragging) return;
+
+ left = e.pageX - ($offsetEl.offset().left + padding);
+
+ callback(e, left);
+ });
}
prepareFrames(view) {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
- $('.frame', view).each((function(_this) {
- return function(index, frame) {
- var height, width;
- width = $(frame).width();
- height = $(frame).height();
- maxWidth = width > maxWidth ? width : maxWidth;
- return maxHeight = height > maxHeight ? height : maxHeight;
- };
- })(this)).css({
- width: maxWidth,
- height: maxHeight
- });
+ $('.frame', view)
+ .each(
+ (function(_this) {
+ return function(index, frame) {
+ var height, width;
+ width = $(frame).width();
+ height = $(frame).height();
+ maxWidth = width > maxWidth ? width : maxWidth;
+ return (maxHeight = height > maxHeight ? height : maxHeight);
+ };
+ })(this),
+ )
+ .css({
+ width: maxWidth,
+ height: maxHeight,
+ });
return [maxWidth, maxHeight];
}
views = {
'two-up': function() {
- return $('.two-up.view .wrap', this.file).each((function(_this) {
- return function(index, wrap) {
- $('img', wrap).each(function() {
- var currentWidth;
- currentWidth = $(this).width();
- if (currentWidth > availWidth / 2) {
- return $(this).width(availWidth / 2);
- }
- });
- return _this.requestImageInfo($('img', wrap), function(width, height) {
- $('.image-info .meta-width', wrap).text(width + "px");
- $('.image-info .meta-height', wrap).text(height + "px");
- return $('.image-info', wrap).removeClass('hide');
- });
- };
- })(this));
+ return $('.two-up.view .wrap', this.file).each(
+ (function(_this) {
+ return function(index, wrap) {
+ $('img', wrap).each(function() {
+ var currentWidth;
+ currentWidth = $(this).width();
+ if (currentWidth > availWidth / 2) {
+ return $(this).width(availWidth / 2);
+ }
+ });
+ return _this.requestImageInfo($('img', wrap), function(width, height) {
+ $('.image-info .meta-width', wrap).text(width + 'px');
+ $('.image-info .meta-height', wrap).text(height + 'px');
+ return $('.image-info', wrap).removeClass('hide');
+ });
+ };
+ })(this),
+ );
},
- 'swipe': function() {
+ swipe() {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
- return $('.swipe.view', this.file).each((function(_this) {
- return function(index, view) {
- var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
- ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref;
- $swipeFrame = $('.swipe-frame', view);
- $swipeWrap = $('.swipe-wrap', view);
- $swipeBar = $('.swipe-bar', view);
-
- $swipeFrame.css({
- width: maxWidth + 16,
- height: maxHeight + 28
- });
- $swipeWrap.css({
- width: maxWidth + 1,
- height: maxHeight + 2
- });
- // Set swipeBar left position to match image frame
- $swipeBar.css({
- left: 1
- });
-
- wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
-
- _this.initDraggable($swipeBar, wrapPadding, function(e, left) {
- if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
- $swipeWrap.width((maxWidth + 1) - left);
- $swipeBar.css('left', left);
- }
- });
- };
- })(this));
+ return $('.swipe.view', this.file).each(
+ (function(_this) {
+ return function(index, view) {
+ var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
+ (ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
+ $swipeFrame = $('.swipe-frame', view);
+ $swipeWrap = $('.swipe-wrap', view);
+ $swipeBar = $('.swipe-bar', view);
+
+ $swipeFrame.css({
+ width: maxWidth + 16,
+ height: maxHeight + 28,
+ });
+ $swipeWrap.css({
+ width: maxWidth + 1,
+ height: maxHeight + 2,
+ });
+ // Set swipeBar left position to match image frame
+ $swipeBar.css({
+ left: 1,
+ });
+
+ wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
+
+ _this.initDraggable($swipeBar, wrapPadding, function(e, left) {
+ if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) {
+ $swipeWrap.width(maxWidth + 1 - left);
+ $swipeBar.css('left', left);
+ }
+ });
+ };
+ })(this),
+ );
},
'onion-skin': function() {
var dragTrackWidth, maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
- return $('.onion-skin.view', this.file).each((function(_this) {
- return function(index, view) {
- var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
- ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref;
- $frame = $('.onion-skin-frame', view);
- $frameAdded = $('.frame.added', view);
- $track = $('.drag-track', view);
- $dragger = $('.dragger', $track);
-
- $frame.css({
- width: maxWidth + 16,
- height: maxHeight + 28
- });
- $('.swipe-wrap', view).css({
- width: maxWidth + 1,
- height: maxHeight + 2
- });
- $dragger.css({
- left: dragTrackWidth
- });
-
- $frameAdded.css('opacity', 1);
- framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
-
- _this.initDraggable($dragger, framePadding, function(e, left) {
- var opacity = left / dragTrackWidth;
-
- if (opacity >= 0 && opacity <= 1) {
- $dragger.css('left', left);
- $frameAdded.css('opacity', opacity);
- }
- });
- };
- })(this));
- }
- }
+ return $('.onion-skin.view', this.file).each(
+ (function(_this) {
+ return function(index, view) {
+ var $frame,
+ $track,
+ $dragger,
+ $frameAdded,
+ framePadding,
+ ref,
+ dragging = false;
+ (ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
+ $frame = $('.onion-skin-frame', view);
+ $frameAdded = $('.frame.added', view);
+ $track = $('.drag-track', view);
+ $dragger = $('.dragger', $track);
+
+ $frame.css({
+ width: maxWidth + 16,
+ height: maxHeight + 28,
+ });
+ $('.swipe-wrap', view).css({
+ width: maxWidth + 1,
+ height: maxHeight + 2,
+ });
+ $dragger.css({
+ left: dragTrackWidth,
+ });
+
+ $frameAdded.css('opacity', 1);
+ framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
+
+ _this.initDraggable($dragger, framePadding, function(e, left) {
+ var opacity = left / dragTrackWidth;
+
+ if (opacity >= 0 && opacity <= 1) {
+ $dragger.css('left', left);
+ $frameAdded.css('opacity', opacity);
+ }
+ });
+ };
+ })(this),
+ );
+ },
+ };
requestImageInfo(img, callback) {
const domImg = img.get(0);
@@ -199,11 +234,14 @@ export default class ImageFile {
if (domImg.complete) {
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
} else {
- return img.on('load', (function(_this) {
- return function() {
- return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
- };
- })(this));
+ return img.on(
+ 'load',
+ (function(_this) {
+ return function() {
+ return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
+ };
+ })(this),
+ );
}
}
}
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
index 3d89bf1316e..340a93e4e66 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
@@ -19,11 +19,13 @@ export default () => {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
if (pipelineTableViewEl) {
- // Update MR and Commits tabs
- pipelineTableViewEl.addEventListener('update-pipelines-count', (event) => {
- if (event.detail.pipelines &&
+ // Update MR and Commits tabs
+ pipelineTableViewEl.addEventListener('update-pipelines-count', event => {
+ if (
+ event.detail.pipelines &&
event.detail.pipelines.count &&
- event.detail.pipelines.count.all) {
+ event.detail.pipelines.count.all
+ ) {
const badge = document.querySelector('.js-pipelines-mr-count');
badge.textContent = event.detail.pipelines.count.all;
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 95c4be64d35..82532539c9c 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -1,85 +1,92 @@
<script>
- import PipelinesService from '../../pipelines/services/pipelines_service';
- import PipelineStore from '../../pipelines/stores/pipelines_store';
- import pipelinesMixin from '../../pipelines/mixins/pipelines';
+import PipelinesService from '../../pipelines/services/pipelines_service';
+import PipelineStore from '../../pipelines/stores/pipelines_store';
+import pipelinesMixin from '../../pipelines/mixins/pipelines';
+import TablePagination from '../../vue_shared/components/table_pagination.vue';
+import { getParameterByName } from '../../lib/utils/common_utils';
+import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
- export default {
- mixins: [
- pipelinesMixin,
- ],
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- helpPagePath: {
- type: String,
- required: true,
- },
- autoDevopsHelpPath: {
- type: String,
- required: true,
- },
- errorStateSvgPath: {
- type: String,
- required: true,
- },
- viewType: {
- type: String,
- required: false,
- default: 'child',
- },
+export default {
+ components: {
+ TablePagination,
+ },
+ mixins: [pipelinesMixin, CIPaginationMixin],
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
},
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ autoDevopsHelpPath: {
+ type: String,
+ required: true,
+ },
+ errorStateSvgPath: {
+ type: String,
+ required: true,
+ },
+ viewType: {
+ type: String,
+ required: false,
+ default: 'child',
+ },
+ },
- data() {
- const store = new PipelineStore();
+ data() {
+ const store = new PipelineStore();
- return {
- store,
- state: store.state,
- };
- },
+ return {
+ store,
+ state: store.state,
+ page: getParameterByName('page') || '1',
+ requestData: {},
+ };
+ },
- computed: {
- shouldRenderTable() {
- return !this.isLoading &&
- this.state.pipelines.length > 0 &&
- !this.hasError;
- },
- shouldRenderErrorState() {
- return this.hasError && !this.isLoading;
- },
+ computed: {
+ shouldRenderTable() {
+ return !this.isLoading && this.state.pipelines.length > 0 && !this.hasError;
},
- created() {
- this.service = new PipelinesService(this.endpoint);
+ shouldRenderErrorState() {
+ return this.hasError && !this.isLoading;
},
- methods: {
- successCallback(resp) {
- // depending of the endpoint the response can either bring a `pipelines` key or not.
- const pipelines = resp.data.pipelines || resp.data;
- this.setCommonData(pipelines);
+ },
+ created() {
+ this.service = new PipelinesService(this.endpoint);
+ this.requestData = { page: this.page };
+ },
+ methods: {
+ successCallback(resp) {
+ // depending of the endpoint the response can either bring a `pipelines` key or not.
+ const pipelines = resp.data.pipelines || resp.data;
+
+ this.store.storePagination(resp.headers);
+ this.setCommonData(pipelines);
- const updatePipelinesEvent = new CustomEvent('update-pipelines-count', {
- detail: {
- pipelines: resp.data,
- },
- });
+ const updatePipelinesEvent = new CustomEvent('update-pipelines-count', {
+ detail: {
+ pipelines: resp.data,
+ },
+ });
- // notifiy to update the count in tabs
- if (this.$el.parentElement) {
- this.$el.parentElement.dispatchEvent(updatePipelinesEvent);
- }
- },
+ // notifiy to update the count in tabs
+ if (this.$el.parentElement) {
+ this.$el.parentElement.dispatchEvent(updatePipelinesEvent);
+ }
},
- };
+ },
+};
</script>
<template>
<div class="content-list pipelines">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
:label="s__('Pipelines|Loading Pipelines')"
- size="3"
+ :size="3"
class="prepend-top-20"
/>
@@ -101,5 +108,11 @@
:view-type="viewType"
/>
</div>
+
+ <table-pagination
+ v-if="shouldRenderPagination"
+ :change="onChangePage"
+ :page-info="state.pageInfo"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/commit_merge_requests.js b/app/assets/javascripts/commit_merge_requests.js
index 102b4ee8463..3a0ab119df6 100644
--- a/app/assets/javascripts/commit_merge_requests.js
+++ b/app/assets/javascripts/commit_merge_requests.js
@@ -50,7 +50,7 @@ export function createContent(mergeRequests) {
if (mergeRequests.length === 0) {
$content.text(s__('Commits|No related merge requests found'));
} else {
- mergeRequests.forEach((mergeRequest) => {
+ mergeRequests.forEach(mergeRequest => {
const $header = createHeader($content.children().length, mergeRequests.length);
const $item = createItem(mergeRequest);
$content.append($header);
@@ -64,8 +64,9 @@ export function createContent(mergeRequests) {
export function fetchCommitMergeRequests() {
const $container = $('.merge-requests');
- axios.get($container.data('projectCommitPath'))
- .then((response) => {
+ axios
+ .get($container.data('projectCommitPath'))
+ .then(response => {
const $content = createContent(response.data);
$container.html($content);
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 9a3ea7a55b6..54e2589c707 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -32,22 +32,31 @@ export default class CommitsList {
if (search === this.lastSearch) return Promise.resolve();
const commitsUrl = `${form.attr('action')}?${form.serialize()}`;
this.content.fadeTo('fast', 0.5);
- const params = form.serializeArray().reduce((acc, obj) => Object.assign(acc, {
- [obj.name]: obj.value,
- }), {});
+ const params = form.serializeArray().reduce(
+ (acc, obj) =>
+ Object.assign(acc, {
+ [obj.name]: obj.value,
+ }),
+ {},
+ );
- return axios.get(form.attr('action'), {
- params,
- })
+ return axios
+ .get(form.attr('action'), {
+ params,
+ })
.then(({ data }) => {
this.lastSearch = search;
this.content.html(data.html);
this.content.fadeTo('fast', 1.0);
// Change url so if user reload a page - search results are saved
- window.history.replaceState({
- page: commitsUrl,
- }, document.title, commitsUrl);
+ window.history.replaceState(
+ {
+ page: commitsUrl,
+ },
+ document.title,
+ commitsUrl,
+ );
})
.catch(() => {
this.content.fadeTo('fast', 1.0);
@@ -75,8 +84,15 @@ export default class CommitsList {
processedData = $processedData.not(`li.js-commit-header[data-day='${loadedShownDayFirst}']`);
// Update commits count in the previous commits header.
- commitsCount += Number($(processedData).nextUntil('li.js-commit-header').first().find('li.commit').length);
- $commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${pluralize('commit', commitsCount)}`);
+ commitsCount += Number(
+ $(processedData)
+ .nextUntil('li.js-commit-header')
+ .first()
+ .find('li.commit').length,
+ );
+ $commitsHeadersLast
+ .find('span.commits-count')
+ .text(`${commitsCount} ${pluralize('commit', commitsCount)}`);
}
localTimeAgo($processedData.find('.js-timeago'));
diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js
index 50e2949ab55..fba30aea9ae 100644
--- a/app/assets/javascripts/commons/bootstrap.js
+++ b/app/assets/javascripts/commons/bootstrap.js
@@ -5,6 +5,14 @@ import 'bootstrap';
// custom jQuery functions
$.fn.extend({
- disable() { return $(this).prop('disabled', true).addClass('disabled'); },
- enable() { return $(this).prop('disabled', false).removeClass('disabled'); },
+ disable() {
+ return $(this)
+ .prop('disabled', true)
+ .addClass('disabled');
+ },
+ enable() {
+ return $(this)
+ .prop('disabled', false)
+ .removeClass('disabled');
+ },
});
diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js
deleted file mode 100644
index 923c036f5a4..00000000000
--- a/app/assets/javascripts/commons/gitlab_ui.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Vue from 'vue';
-import progressBar from '@gitlab-org/gitlab-ui/dist/base/progress_bar';
-
-Vue.component('gl-progress-bar', progressBar);
diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js
index ea945cd3fa5..0d2fe2925d8 100644
--- a/app/assets/javascripts/commons/index.js
+++ b/app/assets/javascripts/commons/index.js
@@ -3,5 +3,4 @@ import './polyfills';
import './jquery';
import './bootstrap';
import './vue';
-import './gitlab_ui';
import '../lib/utils/axios_utils';
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index 589eeee9695..bffc025ced3 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -5,18 +5,20 @@ import 'core-js/fn/array/find-index';
import 'core-js/fn/array/from';
import 'core-js/fn/array/includes';
import 'core-js/fn/object/assign';
+import 'core-js/fn/object/values';
import 'core-js/fn/promise';
import 'core-js/fn/string/code-point-at';
import 'core-js/fn/string/from-code-point';
+import 'core-js/fn/string/includes';
import 'core-js/fn/symbol';
import 'core-js/es6/map';
import 'core-js/es6/weak-map';
// Browser polyfills
-import 'classlist-polyfill';
import 'formdata-polyfill';
import './polyfills/custom_event';
import './polyfills/element';
import './polyfills/event';
import './polyfills/nodelist';
import './polyfills/request_idle_callback';
+import './polyfills/svg';
diff --git a/app/assets/javascripts/commons/polyfills/element.js b/app/assets/javascripts/commons/polyfills/element.js
index b593bde6aa2..dde5e8f54f9 100644
--- a/app/assets/javascripts/commons/polyfills/element.js
+++ b/app/assets/javascripts/commons/polyfills/element.js
@@ -1,12 +1,17 @@
-Element.prototype.closest = Element.prototype.closest ||
+// polyfill Element.classList and DOMTokenList with classList.js
+import 'classlist-polyfill';
+
+Element.prototype.closest =
+ Element.prototype.closest ||
function closest(selector, selectedElement = this) {
if (!selectedElement) return null;
- return selectedElement.matches(selector) ?
- selectedElement :
- Element.prototype.closest(selector, selectedElement.parentElement);
+ return selectedElement.matches(selector)
+ ? selectedElement
+ : Element.prototype.closest(selector, selectedElement.parentElement);
};
-Element.prototype.matches = Element.prototype.matches ||
+Element.prototype.matches =
+ Element.prototype.matches ||
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
@@ -15,13 +20,15 @@ Element.prototype.matches = Element.prototype.matches ||
function matches(selector) {
const elms = (this.document || this.ownerDocument).querySelectorAll(selector);
let i = elms.length - 1;
- while (i >= 0 && elms.item(i) !== this) { i -= 1; }
+ while (i >= 0 && elms.item(i) !== this) {
+ i -= 1;
+ }
return i > -1;
};
// From the polyfill on MDN, https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove#Polyfill
-((arr) => {
- arr.forEach((item) => {
+(arr => {
+ arr.forEach(item => {
if (Object.prototype.hasOwnProperty.call(item, 'remove')) {
return;
}
diff --git a/app/assets/javascripts/commons/polyfills/svg.js b/app/assets/javascripts/commons/polyfills/svg.js
new file mode 100644
index 00000000000..8648a568f6f
--- /dev/null
+++ b/app/assets/javascripts/commons/polyfills/svg.js
@@ -0,0 +1,5 @@
+import svg4everybody from 'svg4everybody';
+
+// polyfill support for external SVG file references via <use xlink:href>
+// @see https://css-tricks.com/svg-use-external-source/
+svg4everybody();
diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js
index a252036d657..37a3ceb5341 100644
--- a/app/assets/javascripts/compare_autocomplete.js
+++ b/app/assets/javascripts/compare_autocomplete.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, one-var, no-var, one-var-declaration-per-line, object-shorthand, no-else-return, max-len */
+/* eslint-disable func-names, one-var, no-var, object-shorthand, no-else-return */
import $ from 'jquery';
import { __ } from './locale';
@@ -40,7 +40,7 @@ export default function initCompareAutocomplete(limitTo = null, clickHandler = (
},
selectable: true,
filterable: true,
- filterRemote: true,
+ filterRemote: !!$dropdown.data('refsUrl'),
fieldName: $dropdown.data('fieldName'),
filterInput: 'input[type="search"]',
renderRow: function(ref) {
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index b0c85c2572e..1000c310e35 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -13,19 +13,23 @@ function openConfirmDangerModal($form, text) {
$submit.disable();
$input.focus();
- $('.js-confirm-danger-input').off('input').on('input', function handleInput() {
- const confirmText = rstrip($(this).val());
- if (confirmText === confirmTextMatch) {
- $submit.enable();
- } else {
- $submit.disable();
- }
- });
- $('.js-confirm-danger-submit').off('click').on('click', () => $form.submit());
+ $('.js-confirm-danger-input')
+ .off('input')
+ .on('input', function handleInput() {
+ const confirmText = rstrip($(this).val());
+ if (confirmText === confirmTextMatch) {
+ $submit.enable();
+ } else {
+ $submit.disable();
+ }
+ });
+ $('.js-confirm-danger-submit')
+ .off('click')
+ .on('click', () => $form.submit());
}
export default function initConfirmDangerModal() {
- $(document).on('click', '.js-confirm-danger', (e) => {
+ $(document).on('click', '.js-confirm-danger', e => {
e.preventDefault();
const $btn = $(e.target);
const $form = $btn.closest('form');
diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js
index 3a50e73ad85..dff0adba25a 100644
--- a/app/assets/javascripts/contextual_sidebar.js
+++ b/app/assets/javascripts/contextual_sidebar.js
@@ -20,8 +20,11 @@ export default class ContextualSidebar {
}
bindEvents() {
- document.addEventListener('click', (e) => {
- if (!e.target.closest('.nav-sidebar') && (bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md')) {
+ document.addEventListener('click', e => {
+ if (
+ !e.target.closest('.nav-sidebar') &&
+ (bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md')
+ ) {
this.toggleCollapsedSidebar(true);
}
});
diff --git a/app/assets/javascripts/create_item_dropdown.js b/app/assets/javascripts/create_item_dropdown.js
index 8ef9aa7f529..916b190f469 100644
--- a/app/assets/javascripts/create_item_dropdown.js
+++ b/app/assets/javascripts/create_item_dropdown.js
@@ -36,7 +36,7 @@ export default class CreateItemDropdown {
},
selectable: true,
toggleLabel(selected) {
- return (selected && 'id' in selected) ? _.escape(selected.title) : this.defaultToggleLabel;
+ return selected && 'id' in selected ? _.escape(selected.title) : this.defaultToggleLabel;
},
fieldName: this.fieldName,
text(item) {
@@ -46,7 +46,7 @@ export default class CreateItemDropdown {
return _.escape(item.id);
},
onFilter: this.toggleCreateNewButton.bind(this),
- clicked: (options) => {
+ clicked: options => {
options.e.preventDefault();
this.onSelect();
},
@@ -77,9 +77,8 @@ export default class CreateItemDropdown {
getData(term, callback) {
this.getDataOption(term, (data = []) => {
// Ensure the selected item isn't already in the data to avoid duplicates
- const alreadyHasSelectedItem = this.selectedItem && data.some(item =>
- item.id === this.selectedItem.id,
- );
+ const alreadyHasSelectedItem =
+ this.selectedItem && data.some(item => item.id === this.selectedItem.id);
let uniqueData = data;
if (!alreadyHasSelectedItem) {
@@ -106,9 +105,7 @@ export default class CreateItemDropdown {
if (newValue) {
this.selectedItem = this.createNewItemFromValue(newValue);
- this.$dropdownContainer
- .find('.js-dropdown-create-new-item code')
- .text(newValue);
+ this.$dropdownContainer.find('.js-dropdown-create-new-item code').text(newValue);
}
this.toggleFooter(!newValue);
diff --git a/app/assets/javascripts/create_label.js b/app/assets/javascripts/create_label.js
index a999c21b2e9..28ca7d97314 100644
--- a/app/assets/javascripts/create_label.js
+++ b/app/assets/javascripts/create_label.js
@@ -37,7 +37,7 @@ export default class CreateLabelDropdown {
addBinding() {
const self = this;
- this.$colorSuggestions.on('click', function (e) {
+ this.$colorSuggestions.on('click', function(e) {
const $this = $(this);
self.addColorValue(e, $this);
});
@@ -47,7 +47,7 @@ export default class CreateLabelDropdown {
this.$dropdownBack.on('click', this.resetForm.bind(this));
- this.$cancelButton.on('click', function (e) {
+ this.$cancelButton.on('click', function(e) {
e.preventDefault();
e.stopPropagation();
@@ -79,13 +79,9 @@ export default class CreateLabelDropdown {
}
resetForm() {
- this.$newLabelField
- .val('')
- .trigger('change');
+ this.$newLabelField.val('').trigger('change');
- this.$newColorField
- .val('')
- .trigger('change');
+ this.$newColorField.val('').trigger('change');
this.$colorPreview
.css('background-color', '')
@@ -97,31 +93,34 @@ export default class CreateLabelDropdown {
e.preventDefault();
e.stopPropagation();
- Api.newLabel(this.namespacePath, this.projectPath, {
- title: this.$newLabelField.val(),
- color: this.$newColorField.val(),
- }, (label) => {
- this.$newLabelCreateButton.enable();
-
- if (label.message) {
- let errors;
-
- if (typeof label.message === 'string') {
- errors = label.message;
+ Api.newLabel(
+ this.namespacePath,
+ this.projectPath,
+ {
+ title: this.$newLabelField.val(),
+ color: this.$newColorField.val(),
+ },
+ label => {
+ this.$newLabelCreateButton.enable();
+
+ if (label.message) {
+ let errors;
+
+ if (typeof label.message === 'string') {
+ errors = label.message;
+ } else {
+ errors = Object.keys(label.message)
+ .map(key => `${humanize(key)} ${label.message[key].join(', ')}`)
+ .join('<br/>');
+ }
+
+ this.$newLabelError.html(errors).show();
} else {
- errors = Object.keys(label.message).map(key =>
- `${humanize(key)} ${label.message[key].join(', ')}`,
- ).join('<br/>');
- }
+ this.$dropdownBack.trigger('click');
- this.$newLabelError
- .html(errors)
- .show();
- } else {
- this.$dropdownBack.trigger('click');
-
- $(document).trigger('created.label', label);
- }
- });
+ $(document).trigger('created.label', label);
+ }
+ },
+ );
}
}
diff --git a/app/assets/javascripts/cycle_analytics/components/banner.vue b/app/assets/javascripts/cycle_analytics/components/banner.vue
index 410d4873e55..82b0f523d2e 100644
--- a/app/assets/javascripts/cycle_analytics/components/banner.vue
+++ b/app/assets/javascripts/cycle_analytics/components/banner.vue
@@ -1,24 +1,28 @@
<script>
- import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg';
+import Icon from '~/vue_shared/components/icon.vue';
+import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg';
- export default {
- props: {
- documentationLink: {
- type: String,
- required: true,
- },
+export default {
+ components: {
+ Icon,
+ },
+ props: {
+ documentationLink: {
+ type: String,
+ required: true,
},
- computed: {
- iconCycleAnalyticsSplash() {
- return iconCycleAnalyticsSplash;
- },
+ },
+ computed: {
+ iconCycleAnalyticsSplash() {
+ return iconCycleAnalyticsSplash;
},
- methods: {
- dismissOverviewDialog() {
- this.$emit('dismiss-overview-dialog');
- },
+ },
+ methods: {
+ dismissOverviewDialog() {
+ this.$emit('dismiss-overview-dialog');
},
- };
+ },
+};
</script>
<template>
<div class="landing content-block">
@@ -28,10 +32,9 @@
type="button"
@click="dismissOverviewDialog"
>
- <i
- class="fa fa-times"
- aria-hidden="true">
- </i>
+ <icon
+ name="close"
+ />
</button>
<div
class="svg-container"
diff --git a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
index b626b187651..f6a7d9962eb 100644
--- a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
@@ -1,17 +1,17 @@
<script>
- import tooltip from '../../vue_shared/directives/tooltip';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ props: {
+ count: {
+ type: Number,
+ required: true,
},
- props: {
- count: {
- type: Number,
- required: true,
- },
- },
- };
+ },
+};
</script>
<template>
<span
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue
index a71dcf78103..429fef176c3 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue
@@ -1,25 +1,25 @@
<script>
- import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
- import limitWarning from './limit_warning_component.vue';
- import totalTime from './total_time_component.vue';
+import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
+import limitWarning from './limit_warning_component.vue';
+import totalTime from './total_time_component.vue';
- export default {
- components: {
- userAvatarImage,
- limitWarning,
- totalTime,
+export default {
+ components: {
+ userAvatarImage,
+ limitWarning,
+ totalTime,
+ },
+ props: {
+ items: {
+ type: Array,
+ default: () => [],
},
- props: {
- items: {
- type: Array,
- default: () => [],
- },
- stage: {
- type: Object,
- default: () => ({}),
- },
+ stage: {
+ type: Object,
+ default: () => ({}),
},
- };
+ },
+};
</script>
<template>
<div>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_component.vue
index 312fe75dde4..56e851fa528 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_component.vue
@@ -1,25 +1,25 @@
<script>
- import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
- import limitWarning from './limit_warning_component.vue';
- import totalTime from './total_time_component.vue';
+import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
+import limitWarning from './limit_warning_component.vue';
+import totalTime from './total_time_component.vue';
- export default {
- components: {
- userAvatarImage,
- limitWarning,
- totalTime,
+export default {
+ components: {
+ userAvatarImage,
+ limitWarning,
+ totalTime,
+ },
+ props: {
+ items: {
+ type: Array,
+ default: () => [],
},
- props: {
- items: {
- type: Array,
- default: () => [],
- },
- stage: {
- type: Object,
- default: () => ({}),
- },
+ stage: {
+ type: Object,
+ default: () => ({}),
},
- };
+ },
+};
</script>
<template>
<div>
@@ -73,4 +73,3 @@
</ul>
</div>
</template>
-
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue
index cee294b4ac2..54b9da4983a 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue
@@ -1,31 +1,31 @@
<script>
- import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
- import iconCommit from '../svg/icon_commit.svg';
- import limitWarning from './limit_warning_component.vue';
- import totalTime from './total_time_component.vue';
+import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
+import iconCommit from '../svg/icon_commit.svg';
+import limitWarning from './limit_warning_component.vue';
+import totalTime from './total_time_component.vue';
- export default {
- components: {
- userAvatarImage,
- totalTime,
- limitWarning,
+export default {
+ components: {
+ userAvatarImage,
+ totalTime,
+ limitWarning,
+ },
+ props: {
+ items: {
+ type: Array,
+ default: () => [],
},
- props: {
- items: {
- type: Array,
- default: () => [],
- },
- stage: {
- type: Object,
- default: () => ({}),
- },
+ stage: {
+ type: Object,
+ default: () => ({}),
},
- computed: {
- iconCommit() {
- return iconCommit;
- },
+ },
+ computed: {
+ iconCommit() {
+ return iconCommit;
},
- };
+ },
+};
</script>
<template>
<div>
@@ -74,4 +74,3 @@
</ul>
</div>
</template>
-
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
index d4735d030fc..f9c80d237d7 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
@@ -1,27 +1,27 @@
<script>
- import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
- import limitWarning from './limit_warning_component.vue';
- import totalTime from './total_time_component.vue';
- import icon from '../../vue_shared/components/icon.vue';
+import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
+import limitWarning from './limit_warning_component.vue';
+import totalTime from './total_time_component.vue';
+import icon from '../../vue_shared/components/icon.vue';
- export default {
- components: {
- userAvatarImage,
- totalTime,
- limitWarning,
- icon,
+export default {
+ components: {
+ userAvatarImage,
+ totalTime,
+ limitWarning,
+ icon,
+ },
+ props: {
+ items: {
+ type: Array,
+ default: () => [],
},
- props: {
- items: {
- type: Array,
- default: () => [],
- },
- stage: {
- type: Object,
- default: () => ({}),
- },
+ stage: {
+ type: Object,
+ default: () => ({}),
},
- };
+ },
+};
</script>
<template>
<div>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue
index 22637485c01..e83b66eef86 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue
@@ -1,33 +1,33 @@
<script>
- import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
- import iconBranch from '../svg/icon_branch.svg';
- import limitWarning from './limit_warning_component.vue';
- import totalTime from './total_time_component.vue';
- import icon from '../../vue_shared/components/icon.vue';
+import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
+import iconBranch from '../svg/icon_branch.svg';
+import limitWarning from './limit_warning_component.vue';
+import totalTime from './total_time_component.vue';
+import icon from '../../vue_shared/components/icon.vue';
- export default {
- components: {
- userAvatarImage,
- totalTime,
- limitWarning,
- icon,
+export default {
+ components: {
+ userAvatarImage,
+ totalTime,
+ limitWarning,
+ icon,
+ },
+ props: {
+ items: {
+ type: Array,
+ default: () => [],
},
- props: {
- items: {
- type: Array,
- default: () => [],
- },
- stage: {
- type: Object,
- default: () => ({}),
- },
+ stage: {
+ type: Object,
+ default: () => ({}),
},
- computed: {
- iconBranch() {
- return iconBranch;
- },
+ },
+ computed: {
+ iconBranch() {
+ return iconBranch;
},
- };
+ },
+};
</script>
<template>
<div>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue
index a0796f299e7..a8196dc879a 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue
@@ -1,35 +1,35 @@
<script>
- import iconBuildStatus from '../svg/icon_build_status.svg';
- import iconBranch from '../svg/icon_branch.svg';
- import limitWarning from './limit_warning_component.vue';
- import totalTime from './total_time_component.vue';
- import icon from '../../vue_shared/components/icon.vue';
+import iconBuildStatus from '../svg/icon_build_status.svg';
+import iconBranch from '../svg/icon_branch.svg';
+import limitWarning from './limit_warning_component.vue';
+import totalTime from './total_time_component.vue';
+import icon from '../../vue_shared/components/icon.vue';
- export default {
- components: {
- totalTime,
- limitWarning,
- icon,
+export default {
+ components: {
+ totalTime,
+ limitWarning,
+ icon,
+ },
+ props: {
+ items: {
+ type: Array,
+ default: () => [],
},
- props: {
- items: {
- type: Array,
- default: () => [],
- },
- stage: {
- type: Object,
- default: () => ({}),
- },
+ stage: {
+ type: Object,
+ default: () => ({}),
},
- computed: {
- iconBuildStatus() {
- return iconBuildStatus;
- },
- iconBranch() {
- return iconBranch;
- },
+ },
+ computed: {
+ iconBuildStatus() {
+ return iconBuildStatus;
},
- };
+ iconBranch() {
+ return iconBranch;
+ },
+ },
+};
</script>
<template>
<div>
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.vue b/app/assets/javascripts/cycle_analytics/components/total_time_component.vue
index 7758bf0cb3f..4db50134208 100644
--- a/app/assets/javascripts/cycle_analytics/components/total_time_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.vue
@@ -1,18 +1,18 @@
<script>
- export default {
- props: {
- time: {
- type: Object,
- required: false,
- default: () => ({}),
- },
+export default {
+ props: {
+ time: {
+ type: Object,
+ required: false,
+ default: () => ({}),
},
- computed: {
- hasData() {
- return Object.keys(this.time).length;
- },
+ },
+ computed: {
+ hasData() {
+ return Object.keys(this.time).length;
},
- };
+ },
+};
</script>
<template>
<span class="total-time">
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index 1c43fc3cdc7..4de425b48e7 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -18,7 +18,8 @@ Vue.use(Translate);
export default () => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
- new Vue({ // eslint-disable-line no-new
+ // eslint-disable-next-line no-new
+ new Vue({
el: '#cycle-analytics',
name: 'CycleAnalytics',
components: {
@@ -66,14 +67,17 @@ export default () => {
const $dropdown = $('.js-ca-dropdown');
const $label = $dropdown.find('.dropdown-label');
- $dropdown.find('li a').off('click').on('click', (e) => {
- e.preventDefault();
- const $target = $(e.currentTarget);
- this.startDate = $target.data('value');
+ $dropdown
+ .find('li a')
+ .off('click')
+ .on('click', e => {
+ e.preventDefault();
+ const $target = $(e.currentTarget);
+ this.startDate = $target.data('value');
- $label.text($target.text().trim());
- this.fetchCycleAnalyticsData({ startDate: this.startDate });
- });
+ $label.text($target.text().trim());
+ this.fetchCycleAnalyticsData({ startDate: this.startDate });
+ });
},
fetchCycleAnalyticsData(options) {
const fetchOptions = options || { startDate: this.startDate };
@@ -82,7 +86,7 @@ export default () => {
this.service
.fetchCycleAnalyticsData(fetchOptions)
- .then((response) => {
+ .then(response => {
this.store.setCycleAnalyticsData(response);
this.selectDefaultStage();
this.initDropdown();
@@ -115,7 +119,7 @@ export default () => {
stage,
startDate: this.startDate,
})
- .then((response) => {
+ .then(response => {
this.isEmptyStage = !response.events.length;
this.store.setStageEvents(response.events, stage);
this.isLoadingStage = false;
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
index 4cf416c50e5..a0426301a0a 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
@@ -18,10 +18,7 @@ export default class CycleAnalyticsService {
}
fetchStageData(options) {
- const {
- stage,
- startDate,
- } = options;
+ const { stage, startDate } = options;
return this.axios
.get(`events/${stage.name}.json`, {
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
index a8cd8c20f8f..18fb57c8b4f 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
@@ -5,13 +5,27 @@ import { dasherize } from '../lib/utils/text_utility';
import DEFAULT_EVENT_OBJECTS from './default_event_objects';
const EMPTY_STAGE_TEXTS = {
- issue: __('The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.'),
- plan: __('The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.'),
- code: __('The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.'),
- test: __('The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.'),
- review: __('The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.'),
- staging: __('The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.'),
- production: __('The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.'),
+ issue: __(
+ 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
+ ),
+ plan: __(
+ 'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.',
+ ),
+ code: __(
+ 'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.',
+ ),
+ test: __(
+ 'The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.',
+ ),
+ review: __(
+ 'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.',
+ ),
+ staging: __(
+ 'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.',
+ ),
+ production: __(
+ 'The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.',
+ ),
};
export default {
@@ -31,11 +45,11 @@ export default {
newData.stages = data.stats || [];
newData.summary = data.summary || [];
- newData.summary.forEach((item) => {
+ newData.summary.forEach(item => {
item.value = item.value || '-';
});
- newData.stages.forEach((item) => {
+ newData.stages.forEach(item => {
const stageSlug = dasherize(item.name.toLowerCase());
item.active = false;
item.isUserAllowed = data.permissions[stageSlug];
@@ -53,7 +67,7 @@ export default {
this.state.hasError = state;
},
deactivateAllStages() {
- this.state.stages.forEach((stage) => {
+ this.state.stages.forEach(stage => {
stage.active = false;
});
},
@@ -67,7 +81,7 @@ export default {
decorateEvents(events, stage) {
const newEvents = [];
- events.forEach((item) => {
+ events.forEach(item => {
if (!item) return;
const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
diff --git a/app/assets/javascripts/deploy_keys/components/action_btn.vue b/app/assets/javascripts/deploy_keys/components/action_btn.vue
index 7399fc97d45..4acd21376bf 100644
--- a/app/assets/javascripts/deploy_keys/components/action_btn.vue
+++ b/app/assets/javascripts/deploy_keys/components/action_btn.vue
@@ -1,10 +1,10 @@
<script>
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '../eventhub';
export default {
components: {
- loadingIcon,
+ GlLoadingIcon,
},
props: {
deployKey: {
@@ -45,7 +45,7 @@ export default {
class="btn"
@click="doAction">
<slot></slot>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
:inline="true"
/>
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index d91e4809126..1d2ac59e20a 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -1,18 +1,18 @@
<script>
import { s__ } from '~/locale';
import Flash from '~/flash';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import eventHub from '../eventhub';
import DeployKeysService from '../service';
import DeployKeysStore from '../store';
import KeysPanel from './keys_panel.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
KeysPanel,
- LoadingIcon,
NavigationTabs,
+ GlLoadingIcon,
},
props: {
endpoint: {
@@ -97,8 +97,10 @@ export default {
.catch(() => new Flash(s__('DeployKeys|Error enabling deploy key')));
},
disableKey(deployKey, callback) {
- // eslint-disable-next-line no-alert
- if (window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))) {
+ if (
+ // eslint-disable-next-line no-alert
+ window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))
+ ) {
this.service
.disableKey(deployKey.id)
.then(this.fetchKeys)
@@ -114,10 +116,10 @@ export default {
<template>
<div class="append-bottom-default deploy-keys">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading && !hasKeys"
:label="s__('DeployKeys|Loading deploy keys')"
- size="2"
+ :size="2"
/>
<template v-else-if="hasKeys">
<div class="top-area scrolling-tabs-container inner-page-scroll-tabs">
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index f66ca070445..c05b9b1de79 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -145,8 +145,8 @@ export default {
<icon :name="firstProject.can_push ? 'lock-open' : 'lock'"/>
</a>
<a
- v-tooltip
v-if="isExpandable"
+ v-tooltip
:title="restProjectsTooltip"
class="label deploy-project-label"
@click="toggleExpanded"
@@ -154,10 +154,10 @@ export default {
<span>{{ restProjectsLabel }}</span>
</a>
<a
- v-tooltip
v-for="deployKeysProject in restProjects"
v-else-if="isExpanded"
:key="deployKeysProject.project.full_path"
+ v-tooltip
:href="deployKeysProject.project.full_path"
:title="projectTooltipTitle(deployKeysProject)"
class="label deploy-project-label"
@@ -198,8 +198,8 @@ export default {
{{ __('Enable') }}
</action-btn>
<a
- v-tooltip
v-if="deployKey.can_edit"
+ v-tooltip
:href="editDeployKeyPath"
:title="__('Edit')"
class="btn btn-default text-secondary"
@@ -208,8 +208,8 @@ export default {
<icon name="pencil"/>
</a>
<action-btn
- v-tooltip
v-if="isRemovable"
+ v-tooltip
:deploy-key="deployKey"
:title="__('Remove')"
btn-css-class="btn-danger"
@@ -219,8 +219,8 @@ export default {
<icon name="remove"/>
</action-btn>
<action-btn
- v-tooltip
v-else-if="isEnabled"
+ v-tooltip
:deploy-key="deployKey"
:title="__('Disable')"
btn-css-class="btn-warning"
diff --git a/app/assets/javascripts/deploy_keys/service/index.js b/app/assets/javascripts/deploy_keys/service/index.js
index 9dc3b21f6f6..268a37008c5 100644
--- a/app/assets/javascripts/deploy_keys/service/index.js
+++ b/app/assets/javascripts/deploy_keys/service/index.js
@@ -8,17 +8,14 @@ export default class DeployKeysService {
}
getKeys() {
- return this.axios.get()
- .then(response => response.data);
+ return this.axios.get().then(response => response.data);
}
enableKey(id) {
- return this.axios.put(`${id}/enable`)
- .then(response => response.data);
+ return this.axios.put(`${id}/enable`).then(response => response.data);
}
disableKey(id) {
- return this.axios.put(`${id}/disable`)
- .then(response => response.data);
+ return this.axios.put(`${id}/disable`).then(response => response.data);
}
}
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index a044fc1ab42..245f1a7c558 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -21,9 +21,12 @@ export default class Diff {
});
const tab = document.getElementById('diffs');
- if (!tab || (tab && tab.dataset && tab.dataset.isLocked !== '')) FilesCommentButton.init($diffFile);
+ if (!tab || (tab && tab.dataset && tab.dataset.isLocked !== ''))
+ FilesCommentButton.init($diffFile);
- const firstFile = $('.files').first().get(0);
+ const firstFile = $('.files')
+ .first()
+ .get(0);
const canCreateNote = firstFile && firstFile.hasAttribute('data-can-create-note');
$diffFile.each((index, file) => imageDiffHelper.initImageDiff(file, canCreateNote));
@@ -73,9 +76,10 @@ export default class Diff {
const view = file.data('view');
const params = { since, to, bottom, offset, unfold, view };
- axios.get(link, { params })
- .then(({ data }) => $target.parent().replaceWith(data))
- .catch(() => flash(__('An error occurred while loading diff')));
+ axios
+ .get(link, { params })
+ .then(({ data }) => $target.parent().replaceWith(data))
+ .catch(() => flash(__('An error occurred while loading diff')));
}
openAnchoredDiff(cb) {
diff --git a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
index ed24d1775f4..4ae4ceabc21 100644
--- a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
+++ b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */
+/* eslint-disable object-shorthand, func-names, no-else-return, no-lonely-if */
/* global CommentsStore */
import $ from 'jquery';
@@ -18,52 +18,56 @@ const CommentAndResolveBtn = Vue.extend({
};
},
computed: {
- showButton: function () {
+ showButton: function() {
if (this.discussion) {
return this.discussion.isResolvable();
} else {
return false;
}
},
- isDiscussionResolved: function () {
+ isDiscussionResolved: function() {
return this.discussion.isResolved();
},
- buttonText: function () {
+ buttonText: function() {
if (this.isDiscussionResolved) {
if (this.textareaIsEmpty) {
- return "Unresolve discussion";
+ return 'Unresolve discussion';
} else {
- return "Comment & unresolve discussion";
+ return 'Comment & unresolve discussion';
}
} else {
if (this.textareaIsEmpty) {
- return "Resolve discussion";
+ return 'Resolve discussion';
} else {
- return "Comment & resolve discussion";
+ return 'Comment & resolve discussion';
}
}
- }
+ },
},
created() {
if (this.discussionId) {
this.discussion = CommentsStore.state[this.discussionId];
}
},
- mounted: function () {
+ mounted: function() {
if (!this.discussionId) return;
- const $textarea = $(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`);
+ const $textarea = $(
+ `.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`,
+ );
this.textareaIsEmpty = $textarea.val() === '';
$textarea.on('input.comment-and-resolve-btn', () => {
this.textareaIsEmpty = $textarea.val() === '';
});
},
- destroyed: function () {
+ destroyed: function() {
if (!this.discussionId) return;
- $(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`).off('input.comment-and-resolve-btn');
- }
+ $(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`).off(
+ 'input.comment-and-resolve-btn',
+ );
+ },
});
Vue.component('comment-and-resolve-btn', CommentAndResolveBtn);
diff --git a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
index 5528d2a542b..5bdeaaade68 100644
--- a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
+++ b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
@@ -83,7 +83,11 @@ const DiffNoteAvatars = Vue.extend({
this.addNoCommentClass();
this.setDiscussionVisible();
- this.lineType = $(this.$el).closest('.diff-line-num').hasClass('old_line') ? 'old' : 'new';
+ this.lineType = $(this.$el)
+ .closest('.diff-line-num')
+ .hasClass('old_line')
+ ? 'old'
+ : 'new';
});
$(document).on('toggle.comments', () => {
@@ -113,20 +117,30 @@ const DiffNoteAvatars = Vue.extend({
addNoCommentClass() {
const { notesCount } = this;
- $(this.$el).closest('.js-avatar-container')
+ $(this.$el)
+ .closest('.js-avatar-container')
.toggleClass('no-comment-btn', notesCount > 0)
.nextUntil('.js-avatar-container')
.toggleClass('no-comment-btn', notesCount > 0);
},
toggleDiscussionsToggleState() {
- const $notesHolders = $(this.$el).closest('.code').find('.notes_holder');
+ const $notesHolders = $(this.$el)
+ .closest('.code')
+ .find('.notes_holder');
const $visibleNotesHolders = $notesHolders.filter(':visible');
- const $toggleDiffCommentsBtn = $(this.$el).closest('.diff-file').find('.js-toggle-diff-comments');
-
- $toggleDiffCommentsBtn.toggleClass('active', $notesHolders.length === $visibleNotesHolders.length);
+ const $toggleDiffCommentsBtn = $(this.$el)
+ .closest('.diff-file')
+ .find('.js-toggle-diff-comments');
+
+ $toggleDiffCommentsBtn.toggleClass(
+ 'active',
+ $notesHolders.length === $visibleNotesHolders.length,
+ );
},
setDiscussionVisible() {
- this.isVisible = $(`.diffs .notes[data-discussion-id="${this.discussion.id}"]`).is(':visible');
+ this.isVisible = $(`.diffs .notes[data-discussion-id="${this.discussion.id}"]`).is(
+ ':visible',
+ );
},
getTooltipText(note) {
return `${note.authorName}: ${note.noteTruncated}`;
diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
index 2b893e35b6d..8542a6e718a 100644
--- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
+++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, no-lonely-if, no-continue, brace-style, max-len, quotes */
+/* eslint-disable object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, no-lonely-if, no-continue */
/* global CommentsStore */
import $ from 'jquery';
@@ -14,24 +14,24 @@ const JumpToDiscussion = Vue.extend({
required: true,
},
},
- data: function () {
+ data: function() {
return {
discussions: CommentsStore.state,
discussion: {},
};
},
computed: {
- buttonText: function () {
+ buttonText: function() {
if (this.discussionId) {
return 'Jump to next unresolved discussion';
} else {
return 'Jump to first unresolved discussion';
}
},
- allResolved: function () {
+ allResolved: function() {
return this.unresolvedDiscussionCount === 0;
},
- showButton: function () {
+ showButton: function() {
if (this.discussionId) {
if (this.unresolvedDiscussionCount > 1) {
return true;
@@ -42,7 +42,7 @@ const JumpToDiscussion = Vue.extend({
return this.unresolvedDiscussionCount >= 1;
}
},
- lastResolvedId: function () {
+ lastResolvedId: function() {
let lastId;
for (const discussionId in this.discussions) {
const discussion = this.discussions[discussionId];
@@ -52,13 +52,13 @@ const JumpToDiscussion = Vue.extend({
}
}
return lastId;
- }
+ },
},
created() {
this.discussion = this.discussions[this.discussionId];
},
methods: {
- jumpToNextUnresolvedDiscussion: function () {
+ jumpToNextUnresolvedDiscussion: function() {
let discussionsSelector;
let discussionIdsInScope;
let firstUnresolvedDiscussionId;
@@ -68,9 +68,11 @@ const JumpToDiscussion = Vue.extend({
let jumpToFirstDiscussion = !this.discussionId;
const discussionIdsForElements = function(elements) {
- return elements.map(function() {
- return $(this).attr('data-discussion-id');
- }).toArray();
+ return elements
+ .map(function() {
+ return $(this).attr('data-discussion-id');
+ })
+ .toArray();
};
const { discussions } = this;
@@ -110,7 +112,7 @@ const JumpToDiscussion = Vue.extend({
if (!hasDiscussionsToJumpTo) {
// If there are no discussions to jump to on the current page,
- // switch to the notes tab and jump to the first disucssion there.
+ // switch to the notes tab and jump to the first discussion there.
window.mrTabs.activateTab('show');
activeTab = 'show';
jumpToFirstDiscussion = true;
@@ -144,8 +146,7 @@ const JumpToDiscussion = Vue.extend({
if (!discussion.isResolved()) {
nextUnresolvedDiscussionId = discussionId;
break;
- }
- else {
+ } else {
continue;
}
}
@@ -175,9 +176,9 @@ const JumpToDiscussion = Vue.extend({
// Resolved discussions are hidden in the diffs tab by default.
// If they are marked unresolved on the notes tab, they will still be hidden on the diffs tab.
// When jumping between unresolved discussions on the diffs tab, we show them.
- $target.closest(".content").show();
+ $target.closest('.content').show();
- const $notesHolder = $target.closest("tr.notes_holder");
+ const $notesHolder = $target.closest('tr.notes_holder');
// Image diff discussions does not use notes_holder
// so we should keep original $target value in those cases
@@ -194,7 +195,7 @@ const JumpToDiscussion = Vue.extend({
prevEl = $target.prev();
// If the discussion doesn't have 4 lines above it, we'll have to do with fewer.
- if (!prevEl.hasClass("line_holder")) {
+ if (!prevEl.hasClass('line_holder')) {
break;
}
@@ -203,9 +204,9 @@ const JumpToDiscussion = Vue.extend({
}
$.scrollTo($target, {
- offset: -150
+ offset: -150,
});
- }
+ },
},
});
diff --git a/app/assets/javascripts/diff_notes/components/resolve_count.js b/app/assets/javascripts/diff_notes/components/resolve_count.js
index e2683e09f40..d8b056096f4 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_count.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_count.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, object-shorthand, func-names */
+/* eslint-disable object-shorthand, func-names */
/* global CommentsStore */
import Vue from 'vue';
@@ -13,17 +13,17 @@ window.ResolveCount = Vue.extend({
required: true,
},
},
- data: function () {
+ data: function() {
return {
- discussions: CommentsStore.state
+ discussions: CommentsStore.state,
};
},
computed: {
- allResolved: function () {
+ allResolved: function() {
return this.resolvedDiscussionCount === this.discussionCount;
},
resolvedCountText() {
return this.discussionCount === 1 ? 'discussion' : 'discussions';
- }
- }
+ },
+ },
});
diff --git a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js
index 5ed13488788..6fcad187b35 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js
@@ -1,4 +1,4 @@
-/* eslint-disable object-shorthand, func-names, comma-dangle, no-else-return, quotes */
+/* eslint-disable object-shorthand, func-names, no-else-return */
/* global CommentsStore */
/* global ResolveService */
@@ -25,44 +25,44 @@ const ResolveDiscussionBtn = Vue.extend({
};
},
computed: {
- showButton: function () {
+ showButton: function() {
if (this.discussion) {
return this.discussion.isResolvable();
} else {
return false;
}
},
- isDiscussionResolved: function () {
+ isDiscussionResolved: function() {
if (this.discussion) {
return this.discussion.isResolved();
} else {
return false;
}
},
- buttonText: function () {
+ buttonText: function() {
if (this.isDiscussionResolved) {
- return "Unresolve discussion";
+ return 'Unresolve discussion';
} else {
- return "Resolve discussion";
+ return 'Resolve discussion';
}
},
- loading: function () {
+ loading: function() {
if (this.discussion) {
return this.discussion.loading;
} else {
return false;
}
- }
+ },
},
- created: function () {
+ created: function() {
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
this.discussion = CommentsStore.state[this.discussionId];
},
methods: {
- resolve: function () {
+ resolve: function() {
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
- }
+ },
},
});
diff --git a/app/assets/javascripts/diff_notes/mixins/discussion.js b/app/assets/javascripts/diff_notes/mixins/discussion.js
index ef35b589e58..dea64dca132 100644
--- a/app/assets/javascripts/diff_notes/mixins/discussion.js
+++ b/app/assets/javascripts/diff_notes/mixins/discussion.js
@@ -1,11 +1,11 @@
-/* eslint-disable object-shorthand, func-names, guard-for-in, no-restricted-syntax, comma-dangle, */
+/* eslint-disable object-shorthand, func-names, guard-for-in, no-restricted-syntax, */
const DiscussionMixins = {
computed: {
- discussionCount: function () {
+ discussionCount: function() {
return Object.keys(this.discussions).length;
},
- resolvedDiscussionCount: function () {
+ resolvedDiscussionCount: function() {
let resolvedCount = 0;
for (const discussionId in this.discussions) {
@@ -18,7 +18,7 @@ const DiscussionMixins = {
return resolvedCount;
},
- unresolvedDiscussionCount: function () {
+ unresolvedDiscussionCount: function() {
let unresolvedCount = 0;
for (const discussionId in this.discussions) {
@@ -30,8 +30,8 @@ const DiscussionMixins = {
}
return unresolvedCount;
- }
- }
+ },
+ },
};
export default DiscussionMixins;
diff --git a/app/assets/javascripts/diff_notes/models/discussion.js b/app/assets/javascripts/diff_notes/models/discussion.js
index 787e6d8855f..daf61e5d467 100644
--- a/app/assets/javascripts/diff_notes/models/discussion.js
+++ b/app/assets/javascripts/diff_notes/models/discussion.js
@@ -6,22 +6,22 @@ import Vue from 'vue';
import { localTimeAgo } from '../../lib/utils/datetime_utility';
class DiscussionModel {
- constructor (discussionId) {
+ constructor(discussionId) {
this.id = discussionId;
this.notes = {};
this.loading = false;
this.canResolve = false;
}
- createNote (noteObj) {
+ createNote(noteObj) {
Vue.set(this.notes, noteObj.noteId, new NoteModel(this.id, noteObj));
}
- deleteNote (noteId) {
+ deleteNote(noteId) {
Vue.delete(this.notes, noteId);
}
- getNote (noteId) {
+ getNote(noteId) {
return this.notes[noteId];
}
@@ -29,7 +29,7 @@ class DiscussionModel {
return Object.keys(this.notes).length;
}
- isResolved () {
+ isResolved() {
for (const noteId in this.notes) {
const note = this.notes[noteId];
@@ -40,7 +40,7 @@ class DiscussionModel {
return true;
}
- resolveAllNotes (resolved_by) {
+ resolveAllNotes(resolved_by) {
for (const noteId in this.notes) {
const note = this.notes[noteId];
@@ -51,7 +51,7 @@ class DiscussionModel {
}
}
- unResolveAllNotes () {
+ unResolveAllNotes() {
for (const noteId in this.notes) {
const note = this.notes[noteId];
@@ -62,7 +62,7 @@ class DiscussionModel {
}
}
- updateHeadline (data) {
+ updateHeadline(data) {
const discussionSelector = `.discussion[data-discussion-id="${this.id}"]`;
const $discussionHeadline = $(`${discussionSelector} .js-discussion-headline`);
@@ -79,7 +79,7 @@ class DiscussionModel {
}
}
- isResolvable () {
+ isResolvable() {
if (!this.canResolve) {
return false;
}
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js b/app/assets/javascripts/diff_notes/services/resolve.js
index 0b3568e432d..e69eaad4423 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js
+++ b/app/assets/javascripts/diff_notes/services/resolve.js
@@ -8,9 +8,7 @@ window.gl = window.gl || {};
class ResolveServiceClass {
constructor(root) {
- this.noteResource = Vue.resource(
- `${root}/notes{/noteId}/resolve?html=true`,
- );
+ this.noteResource = Vue.resource(`${root}/notes{/noteId}/resolve?html=true`);
this.discussionResource = Vue.resource(
`${root}/merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve?html=true`,
);
@@ -51,10 +49,7 @@ class ResolveServiceClass {
discussion.updateHeadline(data);
})
.catch(
- () =>
- new Flash(
- 'An error occurred when trying to resolve a discussion. Please try again.',
- ),
+ () => new Flash('An error occurred when trying to resolve a discussion. Please try again.'),
);
}
diff --git a/app/assets/javascripts/diff_notes/stores/comments.js b/app/assets/javascripts/diff_notes/stores/comments.js
index d7da7d974f3..060bb044f78 100644
--- a/app/assets/javascripts/diff_notes/stores/comments.js
+++ b/app/assets/javascripts/diff_notes/stores/comments.js
@@ -1,14 +1,14 @@
-/* eslint-disable object-shorthand, func-names, camelcase, no-restricted-syntax, guard-for-in, comma-dangle, max-len */
+/* eslint-disable object-shorthand, func-names, camelcase, no-restricted-syntax, guard-for-in */
/* global DiscussionModel */
import Vue from 'vue';
window.CommentsStore = {
state: {},
- get: function (discussionId, noteId) {
+ get: function(discussionId, noteId) {
return this.state[discussionId].getNote(noteId);
},
- createDiscussion: function (discussionId, canResolve) {
+ createDiscussion: function(discussionId, canResolve) {
let discussion = this.state[discussionId];
if (!this.state[discussionId]) {
discussion = new DiscussionModel(discussionId);
@@ -21,18 +21,18 @@ window.CommentsStore = {
return discussion;
},
- create: function (noteObj) {
+ create: function(noteObj) {
const discussion = this.createDiscussion(noteObj.discussionId);
discussion.createNote(noteObj);
},
- update: function (discussionId, noteId, resolved, resolved_by) {
+ update: function(discussionId, noteId, resolved, resolved_by) {
const discussion = this.state[discussionId];
const note = discussion.getNote(noteId);
note.resolved = resolved;
note.resolved_by = resolved_by;
},
- delete: function (discussionId, noteId) {
+ delete: function(discussionId, noteId) {
const discussion = this.state[discussionId];
discussion.deleteNote(noteId);
@@ -40,7 +40,7 @@ window.CommentsStore = {
Vue.delete(this.state, discussionId);
}
},
- unresolvedDiscussionIds: function () {
+ unresolvedDiscussionIds: function() {
const ids = [];
for (const discussionId in this.state) {
@@ -52,5 +52,5 @@ window.CommentsStore = {
}
return ids;
- }
+ },
};
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 7cc4e6a2c3a..881febedb7c 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -3,24 +3,26 @@ import { mapState, mapGetters, mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale';
import createFlash from '~/flash';
+import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '../../notes/event_hub';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
import CompareVersions from './compare_versions.vue';
-import ChangedFiles from './changed_files.vue';
import DiffFile from './diff_file.vue';
import NoChanges from './no_changes.vue';
import HiddenFilesWarning from './hidden_files_warning.vue';
+import CommitWidget from './commit_widget.vue';
+import TreeList from './tree_list.vue';
export default {
name: 'DiffsApp',
components: {
Icon,
- LoadingIcon,
CompareVersions,
- ChangedFiles,
DiffFile,
NoChanges,
HiddenFilesWarning,
+ CommitWidget,
+ TreeList,
+ GlLoadingIcon,
},
props: {
endpoint: {
@@ -41,6 +43,11 @@ export default {
required: true,
},
},
+ data() {
+ return {
+ assignedDiscussions: false,
+ };
+ },
computed: {
...mapState({
isLoading: state => state.diffs.isLoading,
@@ -58,8 +65,9 @@ export default {
plainDiffPath: state => state.diffs.plainDiffPath,
emailPatchPath: state => state.diffs.emailPatchPath,
}),
+ ...mapState('diffs', ['showTreeList', 'isLoading']),
...mapGetters('diffs', ['isParallelView']),
- ...mapGetters(['isNotesFetched']),
+ ...mapGetters(['isNotesFetched', 'getNoteableData']),
targetBranch() {
return {
branchName: this.targetBranchName,
@@ -86,7 +94,10 @@ export default {
return __('Show latest version');
},
canCurrentUserFork() {
- return this.currentUser.canFork === true && this.currentUser.canCreateMergeRequest;
+ return this.currentUser.can_fork === true && this.currentUser.can_create_merge_request;
+ },
+ showCompareVersions() {
+ return this.mergeRequestDiffs && this.mergeRequestDiff;
},
},
watch: {
@@ -102,6 +113,8 @@ export default {
this.adjustView();
},
+ isLoading: 'adjustView',
+ showTreeList: 'adjustView',
},
mounted() {
this.setBaseConfig({ endpoint: this.endpoint, projectPath: this.projectPath });
@@ -112,23 +125,54 @@ export default {
},
created() {
this.adjustView();
+ eventHub.$once('fetchedNotesData', this.setDiscussions);
},
methods: {
- ...mapActions('diffs', ['setBaseConfig', 'fetchDiffFiles']),
+ ...mapActions(['startTaskList']),
+ ...mapActions('diffs', [
+ 'setBaseConfig',
+ 'fetchDiffFiles',
+ 'startRenderDiffsQueue',
+ 'assignDiscussionsToDiff',
+ ]),
fetchData() {
- this.fetchDiffFiles().catch(() => {
- createFlash(__('Something went wrong on our end. Please try again!'));
- });
+ this.fetchDiffFiles()
+ .then(() => {
+ requestIdleCallback(
+ () => {
+ this.setDiscussions();
+ this.startRenderDiffsQueue();
+ },
+ { timeout: 1000 },
+ );
+ })
+ .catch(() => {
+ createFlash(__('Something went wrong on our end. Please try again!'));
+ });
if (!this.isNotesFetched) {
eventHub.$emit('fetchNotesData');
}
},
+ setDiscussions() {
+ if (this.isNotesFetched && !this.assignedDiscussions && !this.isLoading) {
+ this.assignedDiscussions = true;
+
+ requestIdleCallback(
+ () =>
+ this.assignDiscussionsToDiff()
+ .then(this.$nextTick)
+ .then(this.startTaskList),
+ { timeout: 1000 },
+ );
+ }
+ },
adjustView() {
- if (this.shouldShow && this.isParallelView) {
- window.mrTabs.expandViewContainer();
- } else {
- window.mrTabs.resetViewContainer();
+ if (this.shouldShow) {
+ this.$nextTick(() => {
+ window.mrTabs.resetViewContainer();
+ window.mrTabs.expandViewContainer(this.showTreeList);
+ });
}
},
},
@@ -141,7 +185,7 @@ export default {
v-if="isLoading"
class="loading"
>
- <loading-icon />
+ <gl-loading-icon />
</div>
<div
v-else
@@ -150,7 +194,7 @@ export default {
class="diffs tab-pane"
>
<compare-versions
- v-if="!commit && mergeRequestDiffs.length > 1"
+ v-if="showCompareVersions"
:merge-request-diffs="mergeRequestDiffs"
:merge-request-diff="mergeRequestDiff"
:start-version="startVersion"
@@ -183,22 +227,34 @@ export default {
</div>
</div>
- <changed-files
- :diff-files="diffFiles"
+ <commit-widget
+ v-if="commit"
+ :commit="commit"
/>
<div
- v-if="diffFiles.length > 0"
- class="files"
+ :data-can-create-note="getNoteableData.current_user.can_create_note"
+ class="files d-flex prepend-top-default"
>
- <diff-file
- v-for="file in diffFiles"
- :key="file.newPath"
- :file="file"
- :can-current-user-fork="canCurrentUserFork"
- />
+ <div
+ v-show="showTreeList"
+ class="diff-tree-list"
+ >
+ <tree-list />
+ </div>
+ <div
+ v-if="diffFiles.length > 0"
+ class="diff-files-holder"
+ >
+ <diff-file
+ v-for="file in diffFiles"
+ :key="file.newPath"
+ :file="file"
+ :can-current-user-fork="canCurrentUserFork"
+ />
+ </div>
+ <no-changes v-else />
</div>
- <no-changes v-else />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/changed_files.vue b/app/assets/javascripts/diffs/components/changed_files.vue
deleted file mode 100644
index 97751db1254..00000000000
--- a/app/assets/javascripts/diffs/components/changed_files.vue
+++ /dev/null
@@ -1,171 +0,0 @@
-<script>
-import { mapGetters, mapActions } from 'vuex';
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import Icon from '~/vue_shared/components/icon.vue';
-import { pluralize } from '~/lib/utils/text_utility';
-import { getParameterValues, mergeUrlParams } from '~/lib/utils/url_utility';
-import { contentTop } from '~/lib/utils/common_utils';
-import { __ } from '~/locale';
-import ChangedFilesDropdown from './changed_files_dropdown.vue';
-import changedFilesMixin from '../mixins/changed_files';
-
-export default {
- components: {
- Icon,
- ChangedFilesDropdown,
- ClipboardButton,
- },
- mixins: [changedFilesMixin],
- data() {
- return {
- isStuck: false,
- maxWidth: 'auto',
- offsetTop: 0,
- };
- },
- computed: {
- ...mapGetters('diffs', ['isInlineView', 'isParallelView', 'areAllFilesCollapsed']),
- sumAddedLines() {
- return this.sumValues('addedLines');
- },
- sumRemovedLines() {
- return this.sumValues('removedLines');
- },
- whitespaceVisible() {
- return !getParameterValues('w')[0];
- },
- toggleWhitespaceText() {
- if (this.whitespaceVisible) {
- return __('Hide whitespace changes');
- }
- return __('Show whitespace changes');
- },
- toggleWhitespacePath() {
- if (this.whitespaceVisible) {
- return mergeUrlParams({ w: 1 }, window.location.href);
- }
-
- return mergeUrlParams({ w: 0 }, window.location.href);
- },
- top() {
- return `${this.offsetTop}px`;
- },
- },
- created() {
- document.addEventListener('scroll', this.handleScroll);
- this.offsetTop = contentTop();
- },
- beforeDestroy() {
- document.removeEventListener('scroll', this.handleScroll);
- },
- methods: {
- ...mapActions('diffs', ['setInlineDiffViewType', 'setParallelDiffViewType', 'expandAllFiles']),
- pluralize,
- handleScroll() {
- if (!this.updating) {
- this.$nextTick(this.updateIsStuck);
- this.updating = true;
- }
- },
- updateIsStuck() {
- if (!this.$refs.wrapper) {
- return;
- }
-
- const scrollPosition = window.scrollY;
-
- this.isStuck = scrollPosition + this.offsetTop >= this.$refs.placeholder.offsetTop;
- this.updating = false;
- },
- sumValues(key) {
- return this.diffFiles.reduce((total, file) => total + file[key], 0);
- },
- },
-};
-</script>
-
-<template>
- <span>
- <div ref="placeholder"></div>
- <div
- ref="wrapper"
- :style="{ top }"
- :class="{'is-stuck': isStuck}"
- class="content-block oneline-block diff-files-changed diff-files-changed-merge-request
- files-changed js-diff-files-changed"
- >
- <div class="files-changed-inner">
- <div
- class="inline-parallel-buttons d-none d-md-block"
- >
- <a
- v-if="areAllFilesCollapsed"
- class="btn btn-default"
- @click="expandAllFiles"
- >
- {{ __('Expand all') }}
- </a>
- <a
- :href="toggleWhitespacePath"
- class="btn btn-default"
- >
- {{ toggleWhitespaceText }}
- </a>
- <div class="btn-group">
- <button
- id="inline-diff-btn"
- :class="{ active: isInlineView }"
- type="button"
- class="btn js-inline-diff-button"
- data-view-type="inline"
- @click="setInlineDiffViewType"
- >
- {{ __('Inline') }}
- </button>
- <button
- id="parallel-diff-btn"
- :class="{ active: isParallelView }"
- type="button"
- class="btn js-parallel-diff-button"
- data-view-type="parallel"
- @click="setParallelDiffViewType"
- >
- {{ __('Side-by-side') }}
- </button>
- </div>
- </div>
-
- <div class="commit-stat-summary dropdown">
- <changed-files-dropdown
- :diff-files="diffFiles"
- />
-
- <span
- class="js-diff-stats-additions-deletions-expanded
- diff-stats-additions-deletions-expanded"
- >
- with
- <strong class="cgreen">
- {{ pluralize(`${sumAddedLines} addition`, sumAddedLines) }}
- </strong>
- and
- <strong class="cred">
- {{ pluralize(`${sumRemovedLines} deletion`, sumRemovedLines) }}
- </strong>
- </span>
- <div
- class="js-diff-stats-additions-deletions-collapsed
- diff-stats-additions-deletions-collapsed float-right d-sm-none"
- >
- <strong class="cgreen">
- +{{ sumAddedLines }}
- </strong>
- <strong class="cred">
- -{{ sumRemovedLines }}
- </strong>
- </div>
- </div>
- </div>
- </div>
- </span>
-</template>
diff --git a/app/assets/javascripts/diffs/components/changed_files_dropdown.vue b/app/assets/javascripts/diffs/components/changed_files_dropdown.vue
deleted file mode 100644
index 045688a32bf..00000000000
--- a/app/assets/javascripts/diffs/components/changed_files_dropdown.vue
+++ /dev/null
@@ -1,126 +0,0 @@
-<script>
-import Icon from '~/vue_shared/components/icon.vue';
-import changedFilesMixin from '../mixins/changed_files';
-
-export default {
- components: {
- Icon,
- },
- mixins: [changedFilesMixin],
- data() {
- return {
- searchText: '',
- };
- },
- computed: {
- filteredDiffFiles() {
- return this.diffFiles.filter(file =>
- file.filePath.toLowerCase().includes(this.searchText.toLowerCase()),
- );
- },
- },
- methods: {
- clearSearch() {
- this.searchText = '';
- },
- },
-};
-</script>
-
-<template>
- <span>
- Showing
- <button
- class="diff-stats-summary-toggler"
- data-toggle="dropdown"
- type="button"
- aria-expanded="false"
- >
- <span>
- {{ n__('%d changed file', '%d changed files', diffFiles.length) }}
- </span>
- <icon
- class="caret-icon"
- name="chevron-down"
- />
- </button>
- <div class="dropdown-menu diff-file-changes">
- <div class="dropdown-input">
- <input
- v-model="searchText"
- type="search"
- class="dropdown-input-field"
- placeholder="Search files"
- autocomplete="off"
- />
- <i
- v-if="searchText.length === 0"
- aria-hidden="true"
- data-hidden="true"
- class="fa fa-search dropdown-input-search">
- </i>
- <i
- v-else
- role="button"
- class="fa fa-times dropdown-input-search"
- @click="clearSearch"
- ></i>
- </div>
- <div class="dropdown-content">
- <ul>
- <li
- v-for="diffFile in filteredDiffFiles"
- :key="diffFile.name"
- >
- <a
- :href="`#${diffFile.fileHash}`"
- :title="diffFile.newPath"
- class="diff-changed-file"
- >
- <icon
- :name="fileChangedIcon(diffFile)"
- :size="16"
- :class="fileChangedClass(diffFile)"
- class="diff-file-changed-icon append-right-8"
- />
- <span class="diff-changed-file-content append-right-8">
- <strong
- v-if="diffFile.blob && diffFile.blob.name"
- class="diff-changed-file-name"
- >
- {{ diffFile.blob.name }}
- </strong>
- <strong
- v-else
- class="diff-changed-blank-file-name"
- >
- {{ s__('Diffs|No file name available') }}
- </strong>
- <span class="diff-changed-file-path prepend-top-5">
- {{ truncatedDiffPath(diffFile.blob.path) }}
- </span>
- </span>
- <span class="diff-changed-stats">
- <span class="cgreen">
- +{{ diffFile.addedLines }}
- </span>
- <span class="cred">
- -{{ diffFile.removedLines }}
- </span>
- </span>
- </a>
- </li>
-
- <li
- v-show="filteredDiffFiles.length === 0"
- class="dropdown-menu-empty-item"
- >
- <a>
- {{ __('No files found') }}
- </a>
- </li>
- </ul>
- </div>
- </div>
- </span>
-</template>
diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue
new file mode 100644
index 00000000000..aa72aca1478
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/commit_item.vue
@@ -0,0 +1,133 @@
+<script>
+import tooltip from '~/vue_shared/directives/tooltip';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import CIIcon from '~/vue_shared/components/ci_icon.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
+
+/**
+ * CommitItem
+ *
+ * -----------------------------------------------------------------
+ * WARNING: Please keep changes up-to-date with the following files:
+ * - `views/projects/commits/_commit.html.haml`
+ * -----------------------------------------------------------------
+ *
+ * This Component was cloned from a HAML view. For the time being they
+ * coexist, but there is an issue to remove the duplication.
+ * https://gitlab.com/gitlab-org/gitlab-ce/issues/51613
+ *
+ */
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ UserAvatarLink,
+ Icon,
+ ClipboardButton,
+ CIIcon,
+ TimeAgoTooltip,
+ CommitPipelineStatus,
+ },
+ props: {
+ commit: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ authorName() {
+ return (this.commit.author && this.commit.author.name) || this.commit.author_name;
+ },
+ authorUrl() {
+ return (
+ (this.commit.author && this.commit.author.web_url) || `mailto:${this.commit.author_email}`
+ );
+ },
+ authorAvatar() {
+ return (
+ (this.commit.author && this.commit.author.avatar_url) || this.commit.author_gravatar_url
+ );
+ },
+ },
+};
+</script>
+
+<template>
+ <li class="commit flex-row js-toggle-container">
+ <user-avatar-link
+ :link-href="authorUrl"
+ :img-src="authorAvatar"
+ :img-alt="authorName"
+ :img-size="36"
+ class="avatar-cell d-none d-sm-block"
+ />
+ <div class="commit-detail flex-list">
+ <div class="commit-content qa-commit-content">
+ <a
+ :href="commit.commit_url"
+ class="commit-row-message item-title"
+ v-html="commit.title_html"
+ ></a>
+
+ <span class="commit-row-message d-block d-sm-none">
+ &middot;
+ {{ commit.short_id }}
+ </span>
+
+ <button
+ v-if="commit.description_html"
+ class="text-expander js-toggle-button"
+ type="button"
+ :aria-label="__('Toggle commit description')"
+ >
+ <icon
+ :size="12"
+ name="ellipsis_h"
+ />
+ </button>
+
+ <div class="commiter">
+ <a
+ :href="authorUrl"
+ v-text="authorName"
+ ></a>
+ {{ s__('CommitWidget|authored') }}
+ <time-ago-tooltip
+ :time="commit.authored_date"
+ />
+ </div>
+
+ <pre
+ v-if="commit.description_html"
+ class="commit-row-description js-toggle-content append-bottom-8"
+ v-html="commit.description_html"
+ ></pre>
+ </div>
+ <div class="commit-actions flex-row d-none d-sm-flex">
+ <div
+ v-if="commit.signature_html"
+ v-html="commit.signature_html"
+ ></div>
+ <commit-pipeline-status
+ v-if="commit.pipeline_status_path"
+ :endpoint="commit.pipeline_status_path"
+ />
+ <div class="commit-sha-group">
+ <div
+ class="label label-monospace"
+ v-text="commit.short_id"
+ ></div>
+ <clipboard-button
+ :text="commit.id"
+ :title="__('Copy commit SHA to clipboard')"
+ class="btn btn-default"
+ />
+ </div>
+ </div>
+ </div>
+ </li>
+</template>
diff --git a/app/assets/javascripts/diffs/components/commit_widget.vue b/app/assets/javascripts/diffs/components/commit_widget.vue
new file mode 100644
index 00000000000..cc8e72eb1c8
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/commit_widget.vue
@@ -0,0 +1,40 @@
+<script>
+import CommitItem from './commit_item.vue';
+
+/**
+ * CommitWidget
+ *
+ * -----------------------------------------------------------------
+ * WARNING: Please keep changes up-to-date with the following files:
+ * - `views/projects/merge_requests/diffs/_commit_widget.html.haml`
+ * -----------------------------------------------------------------
+ *
+ * This Component was cloned from a HAML view. For the time being,
+ * they coexist, but there is an issue to remove the duplication.
+ * https://gitlab.com/gitlab-org/gitlab-ce/issues/51613
+ *
+ */
+export default {
+ components: {
+ CommitItem,
+ },
+ props: {
+ commit: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="info-well prepend-top-default">
+ <div class="well-segment">
+ <ul class="blob-commit-info">
+ <commit-item
+ :commit="commit"
+ />
+ </ul>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue
index 1c9ad8e77f1..f50cb94a18f 100644
--- a/app/assets/javascripts/diffs/components/compare_versions.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions.vue
@@ -1,9 +1,18 @@
<script>
+import { mapActions, mapGetters, mapState } from 'vuex';
+import Tooltip from '@gitlab/ui/dist/directives/tooltip';
+import { __ } from '~/locale';
+import { getParameterValues, mergeUrlParams } from '~/lib/utils/url_utility';
+import Icon from '~/vue_shared/components/icon.vue';
import CompareVersionsDropdown from './compare_versions_dropdown.vue';
export default {
components: {
CompareVersionsDropdown,
+ Icon,
+ },
+ directives: {
+ Tooltip,
},
props: {
mergeRequestDiffs: {
@@ -26,30 +35,119 @@ export default {
},
},
computed: {
+ ...mapState('diffs', ['commit', 'showTreeList']),
+ ...mapGetters('diffs', ['isInlineView', 'isParallelView', 'hasCollapsedFile']),
comparableDiffs() {
return this.mergeRequestDiffs.slice(1);
},
+ toggleWhitespaceText() {
+ if (this.isWhitespaceVisible()) {
+ return __('Hide whitespace changes');
+ }
+ return __('Show whitespace changes');
+ },
+ toggleWhitespacePath() {
+ if (this.isWhitespaceVisible()) {
+ return mergeUrlParams({ w: 1 }, window.location.href);
+ }
+
+ return mergeUrlParams({ w: 0 }, window.location.href);
+ },
+ showDropdowns() {
+ return !this.commit && this.mergeRequestDiffs.length;
+ },
+ },
+ methods: {
+ ...mapActions('diffs', [
+ 'setInlineDiffViewType',
+ 'setParallelDiffViewType',
+ 'expandAllFiles',
+ 'toggleShowTreeList',
+ ]),
+ isWhitespaceVisible() {
+ return getParameterValues('w')[0] !== '1';
+ },
},
};
</script>
<template>
<div class="mr-version-controls">
- <div class="mr-version-menus-container content-block">
- Changes between
- <compare-versions-dropdown
- :other-versions="mergeRequestDiffs"
- :merge-request-version="mergeRequestDiff"
- :show-commit-count="true"
- class="mr-version-dropdown"
- />
- and
- <compare-versions-dropdown
- :other-versions="comparableDiffs"
- :start-version="startVersion"
- :target-branch="targetBranch"
- class="mr-version-compare-dropdown"
- />
+ <div
+ class="mr-version-menus-container content-block"
+ >
+ <button
+ v-tooltip.hover
+ type="button"
+ class="btn btn-default append-right-8 js-toggle-tree-list"
+ :class="{
+ active: showTreeList
+ }"
+ :title="__('Toggle file browser')"
+ @click="toggleShowTreeList"
+ >
+ <icon
+ name="hamburger"
+ />
+ </button>
+ <div
+ v-if="showDropdowns"
+ class="d-flex align-items-center compare-versions-container"
+ >
+ Changes between
+ <compare-versions-dropdown
+ :other-versions="mergeRequestDiffs"
+ :merge-request-version="mergeRequestDiff"
+ :show-commit-count="true"
+ class="mr-version-dropdown"
+ />
+ and
+ <compare-versions-dropdown
+ :other-versions="comparableDiffs"
+ :start-version="startVersion"
+ :target-branch="targetBranch"
+ class="mr-version-compare-dropdown"
+ />
+ </div>
+ <div
+ class="inline-parallel-buttons d-none d-md-flex ml-auto"
+ >
+ <a
+ v-show="hasCollapsedFile"
+ class="btn btn-default append-right-8"
+ @click="expandAllFiles"
+ >
+ {{ __('Expand all') }}
+ </a>
+ <a
+ :href="toggleWhitespacePath"
+ class="btn btn-default qa-toggle-whitespace"
+ >
+ {{ toggleWhitespaceText }}
+ </a>
+ <div class="btn-group prepend-left-8">
+ <button
+ id="inline-diff-btn"
+ :class="{ active: isInlineView }"
+ type="button"
+ class="btn js-inline-diff-button"
+ data-view-type="inline"
+ @click="setInlineDiffViewType"
+ >
+ {{ __('Inline') }}
+ </button>
+ <button
+ id="parallel-diff-btn"
+ :class="{ active: isParallelView }"
+ type="button"
+ class="btn js-parallel-diff-button"
+ data-view-type="parallel"
+ @click="setParallelDiffViewType"
+ >
+ {{ __('Side-by-side') }}
+ </button>
+ </div>
+ </div>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue b/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue
index 96cccb49378..112206e4ad6 100644
--- a/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue
@@ -56,16 +56,16 @@ export default {
methods: {
commitsText(version) {
return n__(
- `${version.commitsCount} commit,`,
- `${version.commitsCount} commits,`,
- version.commitsCount,
+ `${version.commits_count} commit,`,
+ `${version.commits_count} commits,`,
+ version.commits_count,
);
},
href(version) {
if (this.showCommitCount) {
- return version.versionPath;
+ return version.version_path;
}
- return version.comparePath;
+ return version.compare_path;
},
versionName(version) {
if (this.isLatest(version)) {
@@ -74,7 +74,7 @@ export default {
if (this.targetBranch && (this.isBase(version) || !version)) {
return this.targetBranch.branchName;
}
- return `version ${version.versionIndex}`;
+ return `version ${version.version_index}`;
},
isActive(version) {
if (!version) {
@@ -84,11 +84,11 @@ export default {
if (this.targetBranch) {
return (
(this.isBase(version) && !this.startVersion) ||
- (this.startVersion && this.startVersion.versionIndex === version.versionIndex)
+ (this.startVersion && this.startVersion.version_index === version.version_index)
);
}
- return version.versionIndex === this.mergeRequestVersion.versionIndex;
+ return version.version_index === this.mergeRequestVersion.version_index;
},
isBase(version) {
if (!version || !this.targetBranch) {
@@ -98,7 +98,7 @@ export default {
},
isLatest(version) {
return (
- this.mergeRequestVersion && version.versionIndex === this.targetVersions[0].versionIndex
+ this.mergeRequestVersion && version.version_index === this.targetVersions[0].version_index
);
},
},
@@ -108,16 +108,17 @@ export default {
<template>
<span class="dropdown inline">
<a
- class="dropdown-toggle btn btn-default"
+ class="dropdown-menu-toggle btn btn-default w-100"
data-toggle="dropdown"
aria-expanded="false"
>
<span>
{{ selectedVersionName }}
</span>
- <Icon
+ <icon
:size="12"
name="angle-down"
+ class="position-absolute"
/>
</a>
<div class="dropdown-menu dropdown-select dropdown-menu-selectable">
@@ -141,7 +142,7 @@ export default {
</div>
<div>
<small class="commit-sha">
- {{ version.truncatedCommitSha }}
+ {{ version.truncated_commit_sha }}
</small>
</div>
<div>
@@ -150,8 +151,8 @@ export default {
{{ commitsText(version) }}
</template>
<time-ago
- v-if="version.createdAt"
- :time="version.createdAt"
+ v-if="version.created_at"
+ :time="version.created_at"
class="js-timeago js-timeago-render"
/>
</small>
@@ -163,3 +164,10 @@ export default {
</div>
</span>
</template>
+
+<style>
+.dropdown {
+ min-width: 0;
+ max-height: 170px;
+}
+</style>
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 02d5be1821b..5e5fda5fba6 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -1,15 +1,22 @@
<script>
-import { mapGetters, mapState } from 'vuex';
+import { mapActions, mapGetters, mapState } from 'vuex';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
-import { diffModes } from '~/ide/constants';
import InlineDiffView from './inline_diff_view.vue';
import ParallelDiffView from './parallel_diff_view.vue';
+import NoteForm from '../../notes/components/note_form.vue';
+import ImageDiffOverlay from './image_diff_overlay.vue';
+import DiffDiscussions from './diff_discussions.vue';
+import { IMAGE_DIFF_POSITION_TYPE } from '../constants';
+import { getDiffMode } from '../store/utils';
export default {
components: {
InlineDiffView,
ParallelDiffView,
DiffViewer,
+ NoteForm,
+ DiffDiscussions,
+ ImageDiffOverlay,
},
props: {
diffFile: {
@@ -23,12 +30,37 @@ export default {
endpoint: state => state.diffs.endpoint,
}),
...mapGetters('diffs', ['isInlineView', 'isParallelView']),
+ ...mapGetters('diffs', ['getCommentFormForDiffFile']),
+ ...mapGetters(['getNoteableData', 'noteableType']),
diffMode() {
- const diffModeKey = Object.keys(diffModes).find(key => this.diffFile[`${key}File`]);
- return diffModes[diffModeKey] || diffModes.replaced;
+ return getDiffMode(this.diffFile);
},
isTextFile() {
- return this.diffFile.text;
+ return this.diffFile.viewer.name === 'text';
+ },
+ diffFileCommentForm() {
+ return this.getCommentFormForDiffFile(this.diffFile.file_hash);
+ },
+ showNotesContainer() {
+ return this.diffFile.discussions.length || this.diffFileCommentForm;
+ },
+ },
+ methods: {
+ ...mapActions('diffs', ['saveDiffDiscussion', 'closeDiffFileCommentForm']),
+ handleSaveNote(note) {
+ this.saveDiffDiscussion({
+ note,
+ formData: {
+ noteableData: this.getNoteableData,
+ noteableType: this.noteableType,
+ diffFile: this.diffFile,
+ positionType: IMAGE_DIFF_POSITION_TYPE,
+ x: this.diffFileCommentForm.x,
+ y: this.diffFileCommentForm.y,
+ width: this.diffFileCommentForm.width,
+ height: this.diffFileCommentForm.height,
+ },
+ });
},
},
};
@@ -41,22 +73,52 @@ export default {
<inline-diff-view
v-if="isInlineView"
:diff-file="diffFile"
- :diff-lines="diffFile.highlightedDiffLines || []"
+ :diff-lines="diffFile.highlighted_diff_lines || []"
/>
<parallel-diff-view
v-if="isParallelView"
:diff-file="diffFile"
- :diff-lines="diffFile.parallelDiffLines || []"
+ :diff-lines="diffFile.parallel_diff_lines || []"
/>
</template>
<diff-viewer
v-else
:diff-mode="diffMode"
- :new-path="diffFile.newPath"
- :new-sha="diffFile.diffRefs.headSha"
- :old-path="diffFile.oldPath"
- :old-sha="diffFile.diffRefs.baseSha"
- :project-path="projectPath"/>
+ :new-path="diffFile.new_path"
+ :new-sha="diffFile.diff_refs.head_sha"
+ :old-path="diffFile.old_path"
+ :old-sha="diffFile.diff_refs.base_sha"
+ :file-hash="diffFile.file_hash"
+ :project-path="projectPath"
+ >
+ <image-diff-overlay
+ slot="image-overlay"
+ :discussions="diffFile.discussions"
+ :file-hash="diffFile.file_hash"
+ :can-comment="getNoteableData.current_user.can_create_note"
+ />
+ <div
+ v-if="showNotesContainer"
+ class="note-container"
+ >
+ <diff-discussions
+ v-if="diffFile.discussions.length"
+ class="diff-file-discussions"
+ :discussions="diffFile.discussions"
+ :should-collapse-discussions="true"
+ :render-avatar-badge="true"
+ />
+ <note-form
+ v-if="diffFileCommentForm"
+ ref="noteForm"
+ :is-editing="false"
+ :save-button-title="__('Comment')"
+ class="diff-comment-form new-note discussion-form discussion-form-container"
+ @handleFormUpdate="handleSaveNote"
+ @cancelForm="closeDiffFileCommentForm(diffFile.file_hash)"
+ />
+ </div>
+ </diff-viewer>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue
index e64d5511d78..b9de487a737 100644
--- a/app/assets/javascripts/diffs/components/diff_discussions.vue
+++ b/app/assets/javascripts/diffs/components/diff_discussions.vue
@@ -1,15 +1,40 @@
<script>
+import { mapActions } from 'vuex';
+import Icon from '~/vue_shared/components/icon.vue';
import noteableDiscussion from '../../notes/components/noteable_discussion.vue';
export default {
components: {
noteableDiscussion,
+ Icon,
},
props: {
discussions: {
type: Array,
required: true,
},
+ shouldCollapseDiscussions: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ renderAvatarBadge: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ methods: {
+ ...mapActions(['toggleDiscussion']),
+ ...mapActions('diffs', ['removeDiscussionsFromDiff']),
+ deleteNoteHandler(discussion) {
+ if (discussion.notes.length <= 1) {
+ this.removeDiscussionsFromDiff(discussion);
+ }
+ },
+ isExpanded(discussion) {
+ return this.shouldCollapseDiscussions ? discussion.expanded : true;
+ },
},
};
</script>
@@ -17,21 +42,53 @@ export default {
<template>
<div>
<div
- v-for="discussion in discussions"
+ v-for="(discussion, index) in discussions"
:key="discussion.id"
- class="discussion-notes diff-discussions"
+ :class="{
+ collapsed: !isExpanded(discussion)
+ }"
+ class="discussion-notes diff-discussions position-relative"
>
<ul
:data-discussion-id="discussion.id"
class="notes"
>
+ <template v-if="shouldCollapseDiscussions">
+ <button
+ :class="{
+ 'diff-notes-collapse': discussion.expanded,
+ 'btn-transparent badge badge-pill': !discussion.expanded
+ }"
+ type="button"
+ class="js-diff-notes-toggle"
+ @click="toggleDiscussion({ discussionId: discussion.id })"
+ >
+ <icon
+ v-if="discussion.expanded"
+ name="collapse"
+ class="collapse-icon"
+ />
+ <template v-else>
+ {{ index + 1 }}
+ </template>
+ </button>
+ </template>
<noteable-discussion
+ v-show="isExpanded(discussion)"
:discussion="discussion"
- :render-header="false"
:render-diff-file="false"
:always-expanded="true"
:discussions-by-diff-order="true"
- />
+ @noteDeleted="deleteNoteHandler"
+ >
+ <span
+ v-if="renderAvatarBadge"
+ slot="avatar-badge"
+ class="badge badge-pill"
+ >
+ {{ index + 1 }}
+ </span>
+ </noteable-discussion>
</ul>
</div>
</div>
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 7e7058d8d08..5da0bfb5bfe 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -1,9 +1,9 @@
<script>
-import { mapActions } from 'vuex';
+import { mapActions, mapGetters, mapState } from 'vuex';
import _ from 'underscore';
import { __, sprintf } from '~/locale';
import createFlash from '~/flash';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import DiffFileHeader from './diff_file_header.vue';
import DiffContent from './diff_content.vue';
@@ -11,7 +11,7 @@ export default {
components: {
DiffFileHeader,
DiffContent,
- LoadingIcon,
+ GlLoadingIcon,
},
props: {
file: {
@@ -30,6 +30,9 @@ export default {
};
},
computed: {
+ ...mapState('diffs', ['currentDiffFileId']),
+ ...mapGetters(['isNotesFetched']),
+ ...mapGetters('diffs', ['getDiffFileDiscussions']),
isCollapsed() {
return this.file.collapsed || false;
},
@@ -37,25 +40,47 @@ export default {
return sprintf(
__('You can %{linkStart}view the blob%{linkEnd} instead.'),
{
- linkStart: `<a href="${_.escape(this.file.viewPath)}">`,
+ linkStart: `<a href="${_.escape(this.file.view_path)}">`,
linkEnd: '</a>',
},
false,
);
},
showExpandMessage() {
- return this.isCollapsed && !this.isLoadingCollapsedDiff && !this.file.tooLarge;
+ return (
+ this.isCollapsed ||
+ (!this.file.highlighted_diff_lines &&
+ !this.isLoadingCollapsedDiff &&
+ !this.file.too_large &&
+ this.file.text)
+ );
+ },
+ showLoadingIcon() {
+ return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
+ },
+ hasDiffLines() {
+ return (
+ this.file.highlighted_diff_lines &&
+ this.file.parallel_diff_lines &&
+ this.file.parallel_diff_lines.length > 0
+ );
+ },
+ },
+ watch: {
+ 'file.collapsed': function fileCollapsedWatch(newVal, oldVal) {
+ if (!newVal && oldVal && !this.hasDiffLines) {
+ this.handleLoadCollapsedDiff();
+ }
},
},
methods: {
- ...mapActions('diffs', ['loadCollapsedDiff']),
+ ...mapActions('diffs', ['loadCollapsedDiff', 'assignDiscussionsToDiff']),
handleToggle() {
- const { collapsed, highlightedDiffLines, parallelDiffLines } = this.file;
-
- if (collapsed && !highlightedDiffLines && !parallelDiffLines.length) {
+ if (!this.hasDiffLines) {
this.handleLoadCollapsedDiff();
} else {
this.file.collapsed = !this.file.collapsed;
+ this.file.renderIt = true;
}
},
handleLoadCollapsedDiff() {
@@ -65,6 +90,15 @@ export default {
.then(() => {
this.isLoadingCollapsedDiff = false;
this.file.collapsed = false;
+ this.file.renderIt = true;
+ })
+ .then(() => {
+ requestIdleCallback(
+ () => {
+ this.assignDiscussionsToDiff(this.getDiffFileDiscussions(this.file));
+ },
+ { timeout: 1000 },
+ );
})
.catch(() => {
this.isLoadingCollapsedDiff = false;
@@ -83,7 +117,10 @@ export default {
<template>
<div
- :id="file.fileHash"
+ :id="file.file_hash"
+ :class="{
+ 'is-active': currentDiffFileId === file.file_hash
+ }"
class="diff-file file-holder"
>
<diff-file-header
@@ -106,7 +143,7 @@ export default {
make your changes there, and submit a merge request.
</span>
<a
- :href="file.forkPath"
+ :href="file.fork_path"
class="js-fork-suggestion-button btn btn-grouped btn-inverted btn-success"
>
Fork
@@ -121,16 +158,16 @@ export default {
</div>
<diff-content
- v-if="!isCollapsed"
- :class="{ hidden: isCollapsed || file.tooLarge }"
+ v-if="!isCollapsed && file.renderIt"
+ :class="{ hidden: isCollapsed || file.too_large }"
:diff-file="file"
/>
- <loading-icon
- v-if="isLoadingCollapsedDiff"
+ <gl-loading-icon
+ v-if="showLoadingIcon"
class="diff-content loading"
/>
<div
- v-if="showExpandMessage"
+ v-else-if="showExpandMessage"
class="nothing-here-block diff-collapsed"
>
{{ __('This diff is collapsed.') }}
@@ -143,7 +180,7 @@ export default {
</a>
</div>
<div
- v-if="file.tooLarge"
+ v-if="file.too_large"
class="nothing-here-block diff-collapsed js-too-large-diff"
>
{{ __('This source diff could not be displayed because it is too large.') }}
@@ -151,3 +188,20 @@ export default {
</div>
</div>
</template>
+
+<style>
+@keyframes shadow-fade {
+ from {
+ box-shadow: 0 0 4px #919191;
+ }
+
+ to {
+ box-shadow: 0 0 0 #dfdfdf;
+ }
+}
+
+.diff-file.is-active {
+ box-shadow: 0 0 0 #dfdfdf;
+ animation: shadow-fade 1.2s 0.1s 1;
+}
+</style>
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index d3ffbe0415a..af03cec6582 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -20,6 +20,11 @@ export default {
Tooltip,
},
props: {
+ discussionPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
diffFile: {
type: Object,
required: true,
@@ -63,33 +68,32 @@ export default {
},
titleLink() {
if (this.diffFile.submodule) {
- return this.diffFile.submoduleTreeUrl || this.diffFile.submoduleLink;
+ return this.diffFile.submodule_tree_url || this.diffFile.submodule_link;
}
-
- return `#${this.diffFile.fileHash}`;
+ return this.discussionPath;
},
filePath() {
if (this.diffFile.submodule) {
- return `${this.diffFile.filePath} @ ${truncateSha(this.diffFile.blob.id)}`;
+ return `${this.diffFile.file_path} @ ${truncateSha(this.diffFile.blob.id)}`;
}
- if (this.diffFile.deletedFile) {
- return sprintf(__('%{filePath} deleted'), { filePath: this.diffFile.filePath }, false);
+ if (this.diffFile.deleted_file) {
+ return sprintf(__('%{filePath} deleted'), { filePath: this.diffFile.file_path }, false);
}
- return this.diffFile.filePath;
+ return this.diffFile.file_path;
},
titleTag() {
- return this.diffFile.fileHash ? 'a' : 'span';
+ return this.diffFile.file_hash ? 'a' : 'span';
},
isUsingLfs() {
- return this.diffFile.storedExternally && this.diffFile.externalStorage === 'lfs';
+ return this.diffFile.stored_externally && this.diffFile.external_storage === 'lfs';
},
collapseIcon() {
return this.expanded ? 'chevron-down' : 'chevron-right';
},
viewFileButtonText() {
- const truncatedContentSha = _.escape(truncateSha(this.diffFile.contentSha));
+ const truncatedContentSha = _.escape(truncateSha(this.diffFile.content_sha));
return sprintf(
s__('MergeRequests|View file @ %{commitId}'),
{
@@ -99,7 +103,7 @@ export default {
);
},
viewReplacedFileButtonText() {
- const truncatedBaseSha = _.escape(truncateSha(this.diffFile.diffRefs.baseSha));
+ const truncatedBaseSha = _.escape(truncateSha(this.diffFile.diff_refs.base_sha));
return sprintf(
s__('MergeRequests|View replaced file @ %{commitId}'),
{
@@ -109,7 +113,7 @@ export default {
);
},
gfmCopyText() {
- return `\`${this.diffFile.filePath}\``;
+ return `\`${this.diffFile.file_path}\``;
},
},
methods: {
@@ -152,7 +156,7 @@ export default {
v-once
ref="titleWrapper"
:href="titleLink"
- class="append-right-4"
+ class="append-right-4 js-title-wrapper"
>
<file-icon
:file-name="filePath"
@@ -160,29 +164,27 @@ export default {
aria-hidden="true"
css-classes="js-file-icon append-right-5"
/>
- <span v-if="diffFile.renamedFile">
+ <span v-if="diffFile.renamed_file">
<strong
v-tooltip
- :title="diffFile.oldPath"
+ :title="diffFile.old_path"
class="file-title-name"
data-container="body"
- >
- {{ diffFile.oldPath }}
- </strong>
+ v-html="diffFile.old_path_html"
+ ></strong>
→
<strong
v-tooltip
- :title="diffFile.newPath"
+ :title="diffFile.new_path"
class="file-title-name"
data-container="body"
- >
- {{ diffFile.newPath }}
- </strong>
+ v-html="diffFile.new_path_html"
+ ></strong>
</span>
<strong
- v-tooltip
v-else
+ v-tooltip
:title="filePath"
class="file-title-name"
data-container="body"
@@ -193,16 +195,16 @@ export default {
<clipboard-button
:title="__('Copy file path to clipboard')"
- :text="diffFile.filePath"
+ :text="diffFile.file_path"
:gfm="gfmCopyText"
css-class="btn-default btn-transparent btn-clipboard"
/>
<small
- v-if="diffFile.modeChanged"
+ v-if="diffFile.mode_changed"
ref="fileMode"
>
- {{ diffFile.aMode }} → {{ diffFile.bMode }}
+ {{ diffFile.a_mode }} → {{ diffFile.b_mode }}
</small>
<span
@@ -218,7 +220,7 @@ export default {
class="file-actions d-none d-sm-block"
>
<template
- v-if="diffFile.blob && diffFile.blob.readableText"
+ v-if="diffFile.blob && diffFile.blob.readable_text"
>
<button
:disabled="!diffHasDiscussions(diffFile)"
@@ -232,33 +234,33 @@ export default {
</button>
<edit-button
- v-if="!diffFile.deletedFile"
+ v-if="!diffFile.deleted_file"
:can-current-user-fork="canCurrentUserFork"
- :edit-path="diffFile.editPath"
- :can-modify-blob="diffFile.canModifyBlob"
+ :edit-path="diffFile.edit_path"
+ :can-modify-blob="diffFile.can_modify_blob"
@showForkMessage="showForkMessage"
/>
</template>
<a
- v-if="diffFile.replacedViewPath"
- :href="diffFile.replacedViewPath"
+ v-if="diffFile.replaced_view_path"
+ :href="diffFile.replaced_view_path"
class="btn view-file js-view-file"
v-html="viewReplacedFileButtonText"
>
</a>
<a
- :href="diffFile.viewPath"
+ :href="diffFile.view_path"
class="btn view-file js-view-file"
v-html="viewFileButtonText"
>
</a>
<a
+ v-if="diffFile.external_url"
v-tooltip
- v-if="diffFile.externalUrl"
- :href="diffFile.externalUrl"
- :title="`View on ${diffFile.formattedExternalUrl}`"
+ :href="diffFile.external_url"
+ :title="`View on ${diffFile.formatted_external_url}`"
target="_blank"
rel="noopener noreferrer"
class="btn btn-file-option"
diff --git a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
index 7e50a0aed84..8f8c2a31c71 100644
--- a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
+++ b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
@@ -1,19 +1,19 @@
<script>
import { mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
-import tooltip from '~/vue_shared/directives/tooltip';
import { pluralize, truncate } from '~/lib/utils/text_utility';
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
+import { GlTooltipDirective } from '@gitlab/ui';
import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants';
export default {
- directives: {
- tooltip,
- },
components: {
Icon,
UserAvatarImage,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
discussions: {
type: Array,
@@ -91,10 +91,10 @@ export default {
@click.native="toggleDiscussions"
/>
<span
- v-tooltip
v-if="moreText"
+ v-gl-tooltip
:title="moreText"
- class="diff-comments-more-count has-tooltip js-diff-comment-avatar js-diff-comment-plus"
+ class="diff-comments-more-count js-diff-comment-avatar js-diff-comment-plus"
data-container="body"
data-placement="top"
role="button"
diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
index 8ad1ea34245..8f037eeefc4 100644
--- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
@@ -13,6 +13,10 @@ export default {
Icon,
},
props: {
+ line: {
+ type: Object,
+ required: true,
+ },
fileHash: {
type: String,
required: true,
@@ -21,31 +25,16 @@ export default {
type: String,
required: true,
},
- lineType: {
- type: String,
- required: false,
- default: '',
- },
lineNumber: {
type: Number,
required: false,
default: 0,
},
- lineCode: {
- type: String,
- required: false,
- default: '',
- },
linePosition: {
type: String,
required: false,
default: '',
},
- metaData: {
- type: Object,
- required: false,
- default: () => ({}),
- },
showCommentButton: {
type: Boolean,
required: false,
@@ -76,11 +65,6 @@ export default {
required: false,
default: false,
},
- discussions: {
- type: Array,
- required: false,
- default: () => [],
- },
},
computed: {
...mapState({
@@ -89,7 +73,7 @@ export default {
}),
...mapGetters(['isLoggedIn']),
lineHref() {
- return this.lineCode ? `#${this.lineCode}` : '#';
+ return `#${this.line.line_code || ''}`;
},
shouldShowCommentButton() {
return (
@@ -103,20 +87,19 @@ export default {
);
},
hasDiscussions() {
- return this.discussions.length > 0;
+ return this.line.discussions && this.line.discussions.length > 0;
},
shouldShowAvatarsOnGutter() {
- if (!this.lineType && this.linePosition === LINE_POSITION_RIGHT) {
+ if (!this.line.type && this.linePosition === LINE_POSITION_RIGHT) {
return false;
}
-
return this.showCommentButton && this.hasDiscussions;
},
},
methods: {
...mapActions('diffs', ['loadMoreLines', 'showCommentForm']),
handleCommentButton() {
- this.showCommentForm({ lineCode: this.lineCode });
+ this.showCommentForm({ lineCode: this.line.line_code });
},
handleLoadMoreLines() {
if (this.isRequesting) {
@@ -125,8 +108,8 @@ export default {
this.isRequesting = true;
const endpoint = this.contextLinesPath;
- const oldLineNumber = this.metaData.oldPos || 0;
- const newLineNumber = this.metaData.newPos || 0;
+ const oldLineNumber = this.line.meta_data.old_pos || 0;
+ const newLineNumber = this.line.meta_data.new_pos || 0;
const offset = newLineNumber - oldLineNumber;
const bottom = this.isBottom;
const { fileHash } = this;
@@ -142,12 +125,12 @@ export default {
to = lineNumber + UNFOLD_COUNT;
} else {
const diffFile = utils.findDiffFile(this.diffFiles, this.fileHash);
- const indexForInline = utils.findIndexInInlineLines(diffFile.highlightedDiffLines, {
+ const indexForInline = utils.findIndexInInlineLines(diffFile.highlighted_diff_lines, {
oldLineNumber,
newLineNumber,
});
- const prevLine = diffFile.highlightedDiffLines[indexForInline - 2];
- const prevLineNumber = (prevLine && prevLine.newLine) || 0;
+ const prevLine = diffFile.highlighted_diff_lines[indexForInline - 2];
+ const prevLineNumber = (prevLine && prevLine.new_line) || 0;
if (since <= prevLineNumber + 1) {
since = prevLineNumber + 1;
@@ -184,7 +167,7 @@ export default {
<button
v-if="shouldShowCommentButton"
type="button"
- class="add-diff-note js-add-diff-note-button"
+ class="add-diff-note js-add-diff-note-button qa-diff-comment"
title="Add a comment to this line"
@click="handleCommentButton"
>
@@ -201,7 +184,7 @@ export default {
</a>
<diff-gutter-avatars
v-if="shouldShowAvatarsOnGutter"
- :discussions="discussions"
+ :discussions="line.discussions"
/>
</template>
</div>
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index cbe4551d06b..07f38172575 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -1,9 +1,7 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
-import createFlash from '~/flash';
import { s__ } from '~/locale';
import noteForm from '../../notes/components/note_form.vue';
-import { getNoteFormData } from '../store/utils';
import autosave from '../../notes/mixins/autosave';
import { DIFF_NOTE_TYPE } from '../constants';
@@ -21,7 +19,7 @@ export default {
type: Object,
required: true,
},
- position: {
+ linePosition: {
type: String,
required: false,
default: '',
@@ -38,6 +36,16 @@ export default {
}),
...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters(['isLoggedIn', 'noteableType', 'getNoteableData', 'getNotesDataByProp']),
+ formData() {
+ return {
+ noteableData: this.noteableData,
+ noteableType: this.noteableType,
+ noteTargetLine: this.noteTargetLine,
+ diffViewType: this.diffViewType,
+ diffFile: this.getDiffFileByHash(this.diffFileHash),
+ linePosition: this.linePosition,
+ };
+ },
},
mounted() {
if (this.isLoggedIn) {
@@ -45,15 +53,14 @@ export default {
this.noteableData.diff_head_sha,
DIFF_NOTE_TYPE,
this.noteableData.source_project_id,
- this.line.lineCode,
+ this.line.line_code,
];
this.initAutoSave(this.noteableData, keys);
}
},
methods: {
- ...mapActions('diffs', ['cancelCommentForm']),
- ...mapActions(['saveNote', 'refetchDiscussionById']),
+ ...mapActions('diffs', ['cancelCommentForm', 'assignDiscussionsToDiff', 'saveDiffDiscussion']),
handleCancelCommentForm(shouldConfirm, isDirty) {
if (shouldConfirm && isDirty) {
const msg = s__('Notes|Are you sure you want to cancel creating this comment?');
@@ -65,39 +72,16 @@ export default {
}
this.cancelCommentForm({
- lineCode: this.line.lineCode,
+ lineCode: this.line.line_code,
});
this.$nextTick(() => {
this.resetAutoSave();
});
},
handleSaveNote(note) {
- const selectedDiffFile = this.getDiffFileByHash(this.diffFileHash);
- const postData = getNoteFormData({
- note,
- noteableData: this.noteableData,
- noteableType: this.noteableType,
- noteTargetLine: this.noteTargetLine,
- diffViewType: this.diffViewType,
- diffFile: selectedDiffFile,
- linePosition: this.position,
- });
-
- this.saveNote(postData)
- .then(result => {
- const endpoint = this.getNotesDataByProp('discussionsPath');
-
- this.refetchDiscussionById({ path: endpoint, discussionId: result.discussion_id })
- .then(() => {
- this.handleCancelCommentForm();
- })
- .catch(() => {
- createFlash(s__('MergeRequests|Updating discussions failed'));
- });
- })
- .catch(() => {
- createFlash(s__('MergeRequests|Saving the comment failed'));
- });
+ return this.saveDiffDiscussion({ note, formData: this.formData }).then(() =>
+ this.handleCancelCommentForm(),
+ );
},
},
};
@@ -110,7 +94,7 @@ export default {
<note-form
ref="noteForm"
:is-editing="true"
- :line-code="line.lineCode"
+ :line-code="line.line_code"
save-button-title="Comment"
class="diff-comment-form"
@cancelForm="handleCancelCommentForm"
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
index 33bc8d9971e..0a893a57f07 100644
--- a/app/assets/javascripts/diffs/components/diff_table_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue
@@ -11,8 +11,6 @@ import {
LINE_HOVER_CLASS_NAME,
LINE_UNFOLD_CLASS_NAME,
INLINE_DIFF_VIEW_TYPE,
- LINE_POSITION_LEFT,
- LINE_POSITION_RIGHT,
} from '../constants';
export default {
@@ -67,42 +65,24 @@ export default {
required: false,
default: false,
},
- discussions: {
- type: Array,
- required: false,
- default: () => [],
- },
},
computed: {
...mapGetters(['isLoggedIn']),
- normalizedLine() {
- let normalizedLine;
-
- if (this.diffViewType === INLINE_DIFF_VIEW_TYPE) {
- normalizedLine = this.line;
- } else if (this.linePosition === LINE_POSITION_LEFT) {
- normalizedLine = this.line.left;
- } else if (this.linePosition === LINE_POSITION_RIGHT) {
- normalizedLine = this.line.right;
- }
-
- return normalizedLine;
- },
isMatchLine() {
- return this.normalizedLine.type === MATCH_LINE_TYPE;
+ return this.line.type === MATCH_LINE_TYPE;
},
isContextLine() {
- return this.normalizedLine.type === CONTEXT_LINE_TYPE;
+ return this.line.type === CONTEXT_LINE_TYPE;
},
isMetaLine() {
- const { type } = this.normalizedLine;
+ const { type } = this.line;
return (
type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
);
},
classNameMap() {
- const { type } = this.normalizedLine;
+ const { type } = this.line;
return {
[type]: type,
@@ -116,9 +96,7 @@ export default {
};
},
lineNumber() {
- const { lineType, normalizedLine } = this;
-
- return lineType === OLD_LINE_TYPE ? normalizedLine.oldLine : normalizedLine.newLine;
+ return this.lineType === OLD_LINE_TYPE ? this.line.old_line : this.line.new_line;
},
},
};
@@ -129,20 +107,17 @@ export default {
:class="classNameMap"
>
<diff-line-gutter-content
+ :line="line"
:file-hash="fileHash"
:context-lines-path="contextLinesPath"
- :line-type="normalizedLine.type"
- :line-code="normalizedLine.lineCode"
:line-position="linePosition"
:line-number="lineNumber"
- :meta-data="normalizedLine.metaData"
:show-comment-button="showCommentButton"
:is-hover="isHover"
:is-bottom="isBottom"
:is-match-line="isMatchLine"
:is-context-line="isContentLine"
:is-meta-line="isMetaLine"
- :discussions="discussions"
/>
</td>
</template>
diff --git a/app/assets/javascripts/diffs/components/file_row_stats.vue b/app/assets/javascripts/diffs/components/file_row_stats.vue
new file mode 100644
index 00000000000..105f7ebdbed
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/file_row_stats.vue
@@ -0,0 +1,30 @@
+<script>
+export default {
+ props: {
+ file: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <span
+ v-once
+ class="file-row-stats"
+ >
+ <span class="cgreen">
+ +{{ file.addedLines }}
+ </span>
+ <span class="cred">
+ -{{ file.removedLines }}
+ </span>
+ </span>
+</template>
+
+<style>
+.file-row-stats {
+ font-size: 12px;
+}
+</style>
diff --git a/app/assets/javascripts/diffs/components/image_diff_overlay.vue b/app/assets/javascripts/diffs/components/image_diff_overlay.vue
new file mode 100644
index 00000000000..ae1b0a52901
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/image_diff_overlay.vue
@@ -0,0 +1,139 @@
+<script>
+import { mapActions, mapGetters } from 'vuex';
+import _ from 'underscore';
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ name: 'ImageDiffOverlay',
+ components: {
+ Icon,
+ },
+ props: {
+ discussions: {
+ type: [Array, Object],
+ required: true,
+ },
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ canComment: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ showCommentIcon: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ badgeClass: {
+ type: String,
+ required: false,
+ default: 'badge badge-pill',
+ },
+ shouldToggleDiscussion: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+ computed: {
+ ...mapGetters('diffs', ['getDiffFileByHash', 'getCommentFormForDiffFile']),
+ currentCommentForm() {
+ return this.getCommentFormForDiffFile(this.fileHash);
+ },
+ allDiscussions() {
+ return _.isArray(this.discussions) ? this.discussions : [this.discussions];
+ },
+ },
+ methods: {
+ ...mapActions(['toggleDiscussion']),
+ ...mapActions('diffs', ['openDiffFileCommentForm']),
+ getImageDimensions() {
+ return {
+ width: this.$parent.width,
+ height: this.$parent.height,
+ };
+ },
+ getPositionForObject(meta) {
+ const { x, y, width, height } = meta;
+ const imageWidth = this.getImageDimensions().width;
+ const imageHeight = this.getImageDimensions().height;
+ const widthRatio = imageWidth / width;
+ const heightRatio = imageHeight / height;
+
+ return {
+ x: Math.round(x * widthRatio),
+ y: Math.round(y * heightRatio),
+ };
+ },
+ getPosition(discussion) {
+ const { x, y } = this.getPositionForObject(discussion.position);
+
+ return {
+ left: `${x}px`,
+ top: `${y}px`,
+ };
+ },
+ clickedImage(x, y) {
+ const { width, height } = this.getImageDimensions();
+
+ this.openDiffFileCommentForm({
+ fileHash: this.fileHash,
+ width,
+ height,
+ x,
+ y,
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="position-absolute w-100 h-100 image-diff-overlay">
+ <button
+ v-if="canComment"
+ type="button"
+ class="btn-transparent position-absolute image-diff-overlay-add-comment w-100 h-100 js-add-image-diff-note-button"
+ @click="clickedImage($event.offsetX, $event.offsetY)"
+ >
+ <span class="sr-only">
+ {{ __('Add image comment') }}
+ </span>
+ </button>
+ <button
+ v-for="(discussion, index) in allDiscussions"
+ :key="discussion.id"
+ :style="getPosition(discussion)"
+ :class="badgeClass"
+ :disabled="!shouldToggleDiscussion"
+ class="js-image-badge"
+ type="button"
+ @click="toggleDiscussion({ discussionId: discussion.id })"
+ >
+ <icon
+ v-if="showCommentIcon"
+ name="image-comment-dark"
+ />
+ <template v-else>
+ {{ index + 1 }}
+ </template>
+ </button>
+ <button
+ v-if="currentCommentForm"
+ :style="{
+ left: `${currentCommentForm.x}px`,
+ top: `${currentCommentForm.y}px`
+ }"
+ :aria-label="__('Comment form position')"
+ class="btn-transparent comment-indicator"
+ type="button"
+ >
+ <icon
+ name="image-comment-dark"
+ />
+ </button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
index caf84dc9573..b9e14c53d2c 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
@@ -21,18 +21,13 @@ export default {
type: Number,
required: true,
},
- discussions: {
- type: Array,
- required: false,
- default: () => [],
- },
},
computed: {
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
className() {
- return this.discussions.length ? '' : 'js-temp-notes-holder';
+ return this.line.discussions.length ? '' : 'js-temp-notes-holder';
},
},
};
@@ -44,17 +39,16 @@ export default {
class="notes_holder"
>
<td
- class="notes_line"
- colspan="2"
- ></td>
- <td class="notes_content">
+ class="notes_content"
+ colspan="3"
+ >
<div class="content">
<diff-discussions
- v-if="discussions.length"
- :discussions="discussions"
+ v-if="line.discussions.length"
+ :discussions="line.discussions"
/>
<diff-line-note-form
- v-if="diffLineCommentForms[line.lineCode]"
+ v-if="diffLineCommentForms[line.line_code]"
:diff-file-hash="diffFileHash"
:line="line"
:note-target-line="line"
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
index 32d65ff994f..1f4088066d1 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -1,5 +1,5 @@
<script>
-import { mapGetters } from 'vuex';
+import { mapGetters, mapActions } from 'vuex';
import DiffTableCell from './diff_table_cell.vue';
import {
NEW_LINE_TYPE,
@@ -33,11 +33,6 @@ export default {
required: false,
default: false,
},
- discussions: {
- type: Array,
- required: false,
- default: () => [],
- },
},
data() {
return {
@@ -57,9 +52,7 @@ export default {
};
},
inlineRowId() {
- const { lineCode, oldLine, newLine } = this.line;
-
- return lineCode || `${this.fileHash}_${oldLine}_${newLine}`;
+ return this.line.line_code || `${this.fileHash}_${this.line.old_line}_${this.line.new_line}`;
},
},
created() {
@@ -68,7 +61,11 @@ export default {
this.linePositionLeft = LINE_POSITION_LEFT;
this.linePositionRight = LINE_POSITION_RIGHT;
},
+ mounted() {
+ this.scrollToLineIfNeededInline(this.line);
+ },
methods: {
+ ...mapActions('diffs', ['scrollToLineIfNeededInline']),
handleMouseMove(e) {
// To show the comment icon on the gutter we need to know if we hover the line.
// Current table structure doesn't allow us to do this with CSS in both of the diff view types
@@ -94,7 +91,6 @@ export default {
:is-bottom="isBottom"
:is-hover="isHover"
:show-comment-button="true"
- :discussions="discussions"
class="diff-line-num old_line"
/>
<diff-table-cell
@@ -104,13 +100,12 @@ export default {
:line-type="newLineType"
:is-bottom="isBottom"
:is-hover="isHover"
- :discussions="discussions"
- class="diff-line-num new_line"
+ class="diff-line-num new_line qa-new-diff-line"
/>
<td
:class="line.type"
class="line_content"
- v-html="line.richText"
+ v-html="line.rich_text"
>
</td>
</tr>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index e7d789734c3..79efac89e98 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -2,7 +2,6 @@
import { mapGetters, mapState } from 'vuex';
import inlineDiffTableRow from './inline_diff_table_row.vue';
import inlineDiffCommentRow from './inline_diff_comment_row.vue';
-import { trimFirstCharOfLineContent } from '../store/utils';
export default {
components: {
@@ -20,29 +19,17 @@ export default {
},
},
computed: {
- ...mapGetters('diffs', [
- 'commitId',
- 'shouldRenderInlineCommentRow',
- 'singleDiscussionByLineCode',
- ]),
+ ...mapGetters('diffs', ['commitId', 'shouldRenderInlineCommentRow']),
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
- normalizedDiffLines() {
- return this.diffLines.map(line => (line.richText ? trimFirstCharOfLineContent(line) : line));
- },
diffLinesLength() {
- return this.normalizedDiffLines.length;
+ return this.diffLines.length;
},
userColorScheme() {
return window.gon.user_color_scheme;
},
},
- methods: {
- discussionsList(line) {
- return line.lineCode !== undefined ? this.singleDiscussionByLineCode(line.lineCode) : [];
- },
- },
};
</script>
@@ -53,23 +40,21 @@ export default {
class="code diff-wrap-lines js-syntax-highlight text-file js-diff-inline-view">
<tbody>
<template
- v-for="(line, index) in normalizedDiffLines"
+ v-for="(line, index) in diffLines"
>
<inline-diff-table-row
- :file-hash="diffFile.fileHash"
- :context-lines-path="diffFile.contextLinesPath"
+ :key="line.line_code"
+ :file-hash="diffFile.file_hash"
+ :context-lines-path="diffFile.context_lines_path"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
- :key="line.lineCode"
- :discussions="discussionsList(line)"
/>
<inline-diff-comment-row
v-if="shouldRenderInlineCommentRow(line)"
- :diff-file-hash="diffFile.fileHash"
+ :key="index"
+ :diff-file-hash="diffFile.file_hash"
:line="line"
:line-index="index"
- :key="index"
- :discussions="discussionsList(line)"
/>
</template>
</tbody>
diff --git a/app/assets/javascripts/diffs/components/no_changes.vue b/app/assets/javascripts/diffs/components/no_changes.vue
index d817157fbcd..6905630ad8c 100644
--- a/app/assets/javascripts/diffs/components/no_changes.vue
+++ b/app/assets/javascripts/diffs/components/no_changes.vue
@@ -38,7 +38,7 @@ export default {
<div class="text-center">
<a
:href="newBlobPath"
- class="btn btn-save"
+ class="btn btn-success"
>
{{ __('Create commit') }}
</a>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index 48b8feeb0b4..00c2df4dac1 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -21,51 +21,49 @@ export default {
type: Number,
required: true,
},
- leftDiscussions: {
- type: Array,
- required: false,
- default: () => [],
- },
- rightDiscussions: {
- type: Array,
- required: false,
- default: () => [],
- },
},
computed: {
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
leftLineCode() {
- return this.line.left.lineCode;
+ return this.line.left && this.line.left.line_code;
},
rightLineCode() {
- return this.line.right.lineCode;
+ return this.line.right && this.line.right.line_code;
},
hasExpandedDiscussionOnLeft() {
- const discussions = this.leftDiscussions;
-
- return discussions ? discussions.every(discussion => discussion.expanded) : false;
+ return this.line.left && this.line.left.discussions
+ ? this.line.left.discussions.every(discussion => discussion.expanded)
+ : false;
},
hasExpandedDiscussionOnRight() {
- const discussions = this.rightDiscussions;
-
- return discussions ? discussions.every(discussion => discussion.expanded) : false;
+ return this.line.right && this.line.right.discussions
+ ? this.line.right.discussions.every(discussion => discussion.expanded)
+ : false;
},
hasAnyExpandedDiscussion() {
return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight;
},
shouldRenderDiscussionsOnLeft() {
- return this.leftDiscussions && this.hasExpandedDiscussionOnLeft;
+ return this.line.left && this.line.left.discussions && this.hasExpandedDiscussionOnLeft;
},
shouldRenderDiscussionsOnRight() {
- return this.rightDiscussions && this.hasExpandedDiscussionOnRight && this.line.right.type;
+ return (
+ this.line.right &&
+ this.line.right.discussions &&
+ this.hasExpandedDiscussionOnRight &&
+ this.line.right.type
+ );
},
showRightSideCommentForm() {
- return this.line.right.type && this.diffLineCommentForms[this.rightLineCode];
+ return (
+ this.line.right && this.line.right.type && this.diffLineCommentForms[this.rightLineCode]
+ );
},
className() {
- return this.leftDiscussions.length > 0 || this.rightDiscussions.length > 0
+ return (this.left && this.line.left.discussions.length > 0) ||
+ (this.right && this.line.right.discussions.length > 0)
? ''
: 'js-temp-notes-holder';
},
@@ -78,15 +76,16 @@ export default {
:class="className"
class="notes_holder"
>
- <td class="notes_line old"></td>
- <td class="notes_content parallel old">
+ <td
+ class="notes_content parallel old"
+ colspan="2">
<div
v-if="shouldRenderDiscussionsOnLeft"
class="content"
>
<diff-discussions
- v-if="leftDiscussions.length"
- :discussions="leftDiscussions"
+ v-if="line.left.discussions.length"
+ :discussions="line.left.discussions"
/>
</div>
<diff-line-note-form
@@ -94,18 +93,19 @@ export default {
:diff-file-hash="diffFileHash"
:line="line.left"
:note-target-line="line.left"
- position="left"
+ line-position="left"
/>
</td>
- <td class="notes_line new"></td>
- <td class="notes_content parallel new">
+ <td
+ class="notes_content parallel new"
+ colspan="2">
<div
v-if="shouldRenderDiscussionsOnRight"
class="content"
>
<diff-discussions
- v-if="rightDiscussions.length"
- :discussions="rightDiscussions"
+ v-if="line.right.discussions.length"
+ :discussions="line.right.discussions"
/>
</div>
<diff-line-note-form
@@ -113,7 +113,7 @@ export default {
:diff-file-hash="diffFileHash"
:line="line.right"
:note-target-line="line.right"
- position="right"
+ line-position="right"
/>
</td>
</tr>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
index d4e54c2bd00..2d87db12fd6 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -1,6 +1,6 @@
<script>
+import { mapActions } from 'vuex';
import $ from 'jquery';
-import { mapGetters } from 'vuex';
import DiffTableCell from './diff_table_cell.vue';
import {
NEW_LINE_TYPE,
@@ -10,8 +10,7 @@ import {
OLD_NO_NEW_LINE_TYPE,
PARALLEL_DIFF_VIEW_TYPE,
NEW_NO_NEW_LINE_TYPE,
- LINE_POSITION_LEFT,
- LINE_POSITION_RIGHT,
+ EMPTY_CELL_TYPE,
} from '../constants';
export default {
@@ -36,16 +35,6 @@ export default {
required: false,
default: false,
},
- leftDiscussions: {
- type: Array,
- required: false,
- default: () => [],
- },
- rightDiscussions: {
- type: Array,
- required: false,
- default: () => [],
- },
},
data() {
return {
@@ -54,32 +43,33 @@ export default {
};
},
computed: {
- ...mapGetters('diffs', ['isParallelView']),
isContextLine() {
- return this.line.left.type === CONTEXT_LINE_TYPE;
+ return this.line.left && this.line.left.type === CONTEXT_LINE_TYPE;
},
classNameMap() {
return {
[CONTEXT_LINE_CLASS_NAME]: this.isContextLine,
- [PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView,
+ [PARALLEL_DIFF_VIEW_TYPE]: true,
};
},
parallelViewLeftLineType() {
- if (this.line.right.type === NEW_NO_NEW_LINE_TYPE) {
+ if (this.line.right && this.line.right.type === NEW_NO_NEW_LINE_TYPE) {
return OLD_NO_NEW_LINE_TYPE;
}
- return this.line.left.type;
+ return this.line.left ? this.line.left.type : EMPTY_CELL_TYPE;
},
},
created() {
this.newLineType = NEW_LINE_TYPE;
this.oldLineType = OLD_LINE_TYPE;
- this.linePositionLeft = LINE_POSITION_LEFT;
- this.linePositionRight = LINE_POSITION_RIGHT;
this.parallelDiffViewType = PARALLEL_DIFF_VIEW_TYPE;
},
+ mounted() {
+ this.scrollToLineIfNeededParallel(this.line);
+ },
methods: {
+ ...mapActions('diffs', ['scrollToLineIfNeededParallel']),
handleMouseMove(e) {
const isHover = e.type === 'mouseover';
const hoveringCell = e.target.closest('td');
@@ -116,47 +106,57 @@ export default {
@mouseover="handleMouseMove"
@mouseout="handleMouseMove"
>
- <diff-table-cell
- :file-hash="fileHash"
- :context-lines-path="contextLinesPath"
- :line="line"
- :line-type="oldLineType"
- :line-position="linePositionLeft"
- :is-bottom="isBottom"
- :is-hover="isLeftHover"
- :show-comment-button="true"
- :diff-view-type="parallelDiffViewType"
- :discussions="leftDiscussions"
- class="diff-line-num old_line"
- />
- <td
- :id="line.left.lineCode"
- :class="parallelViewLeftLineType"
- class="line_content parallel left-side"
- @mousedown.native="handleParallelLineMouseDown"
- v-html="line.left.richText"
- >
- </td>
- <diff-table-cell
- :file-hash="fileHash"
- :context-lines-path="contextLinesPath"
- :line="line"
- :line-type="newLineType"
- :line-position="linePositionRight"
- :is-bottom="isBottom"
- :is-hover="isRightHover"
- :show-comment-button="true"
- :diff-view-type="parallelDiffViewType"
- :discussions="rightDiscussions"
- class="diff-line-num new_line"
- />
- <td
- :id="line.right.lineCode"
- :class="line.right.type"
- class="line_content parallel right-side"
- @mousedown.native="handleParallelLineMouseDown"
- v-html="line.right.richText"
- >
- </td>
+ <template v-if="line.left">
+ <diff-table-cell
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
+ :line="line.left"
+ :line-type="oldLineType"
+ :is-bottom="isBottom"
+ :is-hover="isLeftHover"
+ :show-comment-button="true"
+ :diff-view-type="parallelDiffViewType"
+ line-position="left"
+ class="diff-line-num old_line"
+ />
+ <td
+ :id="line.left.line_code"
+ :class="parallelViewLeftLineType"
+ class="line_content parallel left-side"
+ @mousedown.native="handleParallelLineMouseDown"
+ v-html="line.left.rich_text"
+ >
+ </td>
+ </template>
+ <template v-else>
+ <td class="diff-line-num old_line empty-cell"></td>
+ <td class="line_content parallel left-side empty-cell"></td>
+ </template>
+ <template v-if="line.right">
+ <diff-table-cell
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
+ :line="line.right"
+ :line-type="newLineType"
+ :is-bottom="isBottom"
+ :is-hover="isRightHover"
+ :show-comment-button="true"
+ :diff-view-type="parallelDiffViewType"
+ line-position="right"
+ class="diff-line-num new_line"
+ />
+ <td
+ :id="line.right.line_code"
+ :class="line.right.type"
+ class="line_content parallel right-side"
+ @mousedown.native="handleParallelLineMouseDown"
+ v-html="line.right.rich_text"
+ >
+ </td>
+ </template>
+ <template v-else>
+ <td class="diff-line-num old_line empty-cell"></td>
+ <td class="line_content parallel right-side empty-cell"></td>
+ </template>
</tr>
</template>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 24ceb52a04a..6942f9b53e0 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -2,8 +2,6 @@
import { mapState, mapGetters } from 'vuex';
import parallelDiffTableRow from './parallel_diff_table_row.vue';
import parallelDiffCommentRow from './parallel_diff_comment_row.vue';
-import { EMPTY_CELL_TYPE } from '../constants';
-import { trimFirstCharOfLineContent } from '../store/utils';
export default {
components: {
@@ -21,46 +19,17 @@ export default {
},
},
computed: {
- ...mapGetters('diffs', [
- 'commitId',
- 'singleDiscussionByLineCode',
- 'shouldRenderParallelCommentRow',
- ]),
+ ...mapGetters('diffs', ['commitId', 'shouldRenderParallelCommentRow']),
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
- parallelDiffLines() {
- return this.diffLines.map(line => {
- const parallelLine = Object.assign({}, line);
-
- if (line.left) {
- parallelLine.left = trimFirstCharOfLineContent(line.left);
- } else {
- parallelLine.left = { type: EMPTY_CELL_TYPE };
- }
-
- if (line.right) {
- parallelLine.right = trimFirstCharOfLineContent(line.right);
- } else {
- parallelLine.right = { type: EMPTY_CELL_TYPE };
- }
-
- return parallelLine;
- });
- },
diffLinesLength() {
- return this.parallelDiffLines.length;
+ return this.diffLines.length;
},
userColorScheme() {
return window.gon.user_color_scheme;
},
},
- methods: {
- discussionsByLine(line, leftOrRight) {
- return line[leftOrRight] && line[leftOrRight].lineCode !== undefined ?
- this.singleDiscussionByLineCode(line[leftOrRight].lineCode) : [];
- },
- },
};
</script>
@@ -73,25 +42,21 @@ export default {
<table>
<tbody>
<template
- v-for="(line, index) in parallelDiffLines"
+ v-for="(line, index) in diffLines"
>
<parallel-diff-table-row
- :file-hash="diffFile.fileHash"
- :context-lines-path="diffFile.contextLinesPath"
+ :key="index"
+ :file-hash="diffFile.file_hash"
+ :context-lines-path="diffFile.context_lines_path"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
- :key="index"
- :left-discussions="discussionsByLine(line, 'left')"
- :right-discussions="discussionsByLine(line, 'right')"
/>
<parallel-diff-comment-row
v-if="shouldRenderParallelCommentRow(line)"
:key="`dcr-${index}`"
:line="line"
- :diff-file-hash="diffFile.fileHash"
+ :diff-file-hash="diffFile.file_hash"
:line-index="index"
- :left-discussions="discussionsByLine(line, 'left')"
- :right-discussions="discussionsByLine(line, 'right')"
/>
</template>
</tbody>
diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue
new file mode 100644
index 00000000000..1f82eeae6cb
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/tree_list.vue
@@ -0,0 +1,175 @@
+<script>
+import { mapActions, mapGetters, mapState } from 'vuex';
+import { GlTooltipDirective } from '@gitlab/ui';
+import { convertPermissionToBoolean } from '~/lib/utils/common_utils';
+import Icon from '~/vue_shared/components/icon.vue';
+import FileRow from '~/vue_shared/components/file_row.vue';
+import FileRowStats from './file_row_stats.vue';
+
+const treeListStorageKey = 'mr_diff_tree_list';
+
+export default {
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ components: {
+ Icon,
+ FileRow,
+ },
+ data() {
+ const treeListStored = localStorage.getItem(treeListStorageKey);
+ const renderTreeList =
+ treeListStored !== null ? convertPermissionToBoolean(treeListStored) : true;
+
+ return {
+ search: '',
+ renderTreeList,
+ focusSearch: false,
+ };
+ },
+ computed: {
+ ...mapState('diffs', ['tree', 'addedLines', 'removedLines']),
+ ...mapGetters('diffs', ['allBlobs', 'diffFilesLength']),
+ filteredTreeList() {
+ const search = this.search.toLowerCase().trim();
+
+ if (search === '') return this.renderTreeList ? this.tree : this.allBlobs;
+
+ return this.allBlobs.filter(f => f.path.toLowerCase().indexOf(search) >= 0);
+ },
+ rowDisplayTextKey() {
+ if (this.renderTreeList && this.search.trim() === '') {
+ return 'name';
+ }
+
+ return 'path';
+ },
+ },
+ methods: {
+ ...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile']),
+ clearSearch() {
+ this.search = '';
+ this.toggleFocusSearch(false);
+ },
+ toggleRenderTreeList(toggle) {
+ this.renderTreeList = toggle;
+ localStorage.setItem(treeListStorageKey, this.renderTreeList);
+ },
+ toggleFocusSearch(toggle) {
+ this.focusSearch = toggle;
+ },
+ blurSearch() {
+ if (this.search.trim() === '') {
+ this.toggleFocusSearch(false);
+ }
+ },
+ },
+ FileRowStats,
+};
+</script>
+
+<template>
+ <div class="tree-list-holder d-flex flex-column">
+ <div class="append-bottom-8 position-relative tree-list-search d-flex">
+ <div class="flex-fill d-flex">
+ <icon
+ name="search"
+ class="position-absolute tree-list-icon"
+ />
+ <input
+ v-model="search"
+ :placeholder="s__('MergeRequest|Filter files')"
+ type="search"
+ class="form-control"
+ @focus="toggleFocusSearch(true)"
+ @blur="blurSearch"
+ />
+ <button
+ v-show="search"
+ :aria-label="__('Clear search')"
+ type="button"
+ class="position-absolute bg-transparent tree-list-icon tree-list-clear-icon border-0 p-0"
+ @click="clearSearch"
+ >
+ <icon
+ name="close"
+ />
+ </button>
+ </div>
+ <div
+ v-show="!focusSearch"
+ class="btn-group prepend-left-8 tree-list-view-toggle"
+ >
+ <button
+ v-gl-tooltip.hover
+ :aria-label="__('List view')"
+ :title="__('List view')"
+ :class="{
+ active: !renderTreeList
+ }"
+ class="btn btn-default pt-0 pb-0 d-flex align-items-center"
+ type="button"
+ @click="toggleRenderTreeList(false)"
+ >
+ <icon
+ name="hamburger"
+ />
+ </button>
+ <button
+ v-gl-tooltip.hover
+ :aria-label="__('Tree view')"
+ :title="__('Tree view')"
+ :class="{
+ active: renderTreeList
+ }"
+ class="btn btn-default pt-0 pb-0 d-flex align-items-center"
+ type="button"
+ @click="toggleRenderTreeList(true)"
+ >
+ <icon
+ name="file-tree"
+ />
+ </button>
+ </div>
+ </div>
+ <div
+ class="tree-list-scroll"
+ >
+ <template v-if="filteredTreeList.length">
+ <file-row
+ v-for="file in filteredTreeList"
+ :key="file.key"
+ :file="file"
+ :level="0"
+ :hide-extra-on-tree="true"
+ :extra-component="$options.FileRowStats"
+ :show-changed-icon="true"
+ :display-text-key="rowDisplayTextKey"
+ :should-truncate-start="true"
+ @toggleTreeOpen="toggleTreeOpen"
+ @clickFile="scrollToFile"
+ />
+ </template>
+ <p
+ v-else
+ class="prepend-top-20 append-bottom-20 text-center"
+ >
+ {{ s__('MergeRequest|No files found') }}
+ </p>
+ </div>
+ <div
+ v-once
+ class="pt-3 pb-3 text-center"
+ >
+ {{ n__('%d changed file', '%d changed files', diffFilesLength) }}
+ <div>
+ <span class="cgreen">
+ {{ n__('%d addition', '%d additions', addedLines) }}
+ </span>
+ <span class="cred">
+ {{ n__('%d deleted', '%d deletions', removedLines) }}
+ </span>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 2fa8367f528..78a39baa4cb 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -7,10 +7,12 @@ export const CONTEXT_LINE_TYPE = 'context';
export const EMPTY_CELL_TYPE = 'empty-cell';
export const COMMENT_FORM_TYPE = 'commentForm';
export const DIFF_NOTE_TYPE = 'DiffNote';
+export const LEGACY_DIFF_NOTE_TYPE = 'LegacyDiffNote';
export const NOTE_TYPE = 'Note';
export const NEW_LINE_TYPE = 'new';
export const OLD_LINE_TYPE = 'old';
export const TEXT_DIFF_POSITION_TYPE = 'text';
+export const IMAGE_DIFF_POSITION_TYPE = 'image';
export const LINE_POSITION_LEFT = 'left';
export const LINE_POSITION_RIGHT = 'right';
@@ -25,3 +27,8 @@ export const CONTEXT_LINE_CLASS_NAME = 'diff-expanded';
export const UNFOLD_COUNT = 20;
export const COUNT_OF_AVATARS_IN_GUTTER = 3;
export const LENGTH_OF_AVATAR_TOOLTIP = 17;
+
+export const LINES_TO_BE_RENDERED_DIRECTLY = 100;
+export const MAX_LINES_TO_BE_RENDERED = 2000;
+
+export const MR_TREE_SHOW_KEY = 'mr_tree_show';
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index aae89109c27..06ef4207d85 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -1,6 +1,5 @@
import Vue from 'vue';
import { mapState } from 'vuex';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import diffsApp from './components/app.vue';
export default function initDiffsApp(store) {
@@ -17,9 +16,7 @@ export default function initDiffsApp(store) {
return {
endpoint: dataset.endpoint,
projectPath: dataset.projectPath,
- currentUser: convertObjectPropsToCamelCase(JSON.parse(dataset.currentUserData), {
- deep: true,
- }),
+ currentUser: JSON.parse(dataset.currentUserData) || {},
};
},
computed: {
diff --git a/app/assets/javascripts/diffs/mixins/changed_files.js b/app/assets/javascripts/diffs/mixins/changed_files.js
deleted file mode 100644
index da1339f0ffa..00000000000
--- a/app/assets/javascripts/diffs/mixins/changed_files.js
+++ /dev/null
@@ -1,38 +0,0 @@
-export default {
- props: {
- diffFiles: {
- type: Array,
- required: true,
- },
- },
- methods: {
- fileChangedIcon(diffFile) {
- if (diffFile.deletedFile) {
- return 'file-deletion';
- } else if (diffFile.newFile) {
- return 'file-addition';
- }
- return 'file-modified';
- },
- fileChangedClass(diffFile) {
- if (diffFile.deletedFile) {
- return 'cred';
- } else if (diffFile.newFile) {
- return 'cgreen';
- }
-
- return '';
- },
- truncatedDiffPath(path) {
- const maxLength = 60;
-
- if (path.length > maxLength) {
- const start = path.length - maxLength;
- const end = start + maxLength;
- return `...${path.slice(start, end)}`;
- }
-
- return path;
- },
- },
-};
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 27001142257..6354d3ce1e6 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -1,13 +1,17 @@
import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import Cookies from 'js-cookie';
+import createFlash from '~/flash';
+import { s__ } from '~/locale';
import { handleLocationHash, historyPushState } from '~/lib/utils/common_utils';
-import { mergeUrlParams } from '~/lib/utils/url_utility';
+import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility';
+import { getDiffPositionByLineCode, getNoteFormData } from './utils';
import * as types from './mutation_types';
import {
PARALLEL_DIFF_VIEW_TYPE,
INLINE_DIFF_VIEW_TYPE,
DIFF_VIEW_COOKIE_NAME,
+ MR_TREE_SHOW_KEY,
} from '../constants';
export const setBaseConfig = ({ commit }, options) => {
@@ -29,6 +33,54 @@ export const fetchDiffFiles = ({ state, commit }) => {
.then(handleLocationHash);
};
+// This is adding line discussions to the actual lines in the diff tree
+// once for parallel and once for inline mode
+export const assignDiscussionsToDiff = (
+ { commit, state, rootState },
+ discussions = rootState.notes.discussions,
+) => {
+ const diffPositionByLineCode = getDiffPositionByLineCode(state.diffFiles);
+
+ discussions.filter(discussion => discussion.diff_discussion).forEach(discussion => {
+ commit(types.SET_LINE_DISCUSSIONS_FOR_FILE, {
+ discussion,
+ diffPositionByLineCode,
+ });
+ });
+};
+
+export const removeDiscussionsFromDiff = ({ commit }, removeDiscussion) => {
+ const { file_hash, line_code, id } = removeDiscussion;
+ commit(types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, { fileHash: file_hash, lineCode: line_code, id });
+};
+
+export const startRenderDiffsQueue = ({ state, commit }) => {
+ const checkItem = () =>
+ new Promise(resolve => {
+ const nextFile = state.diffFiles.find(
+ file => !file.renderIt && (!file.collapsed || !file.text),
+ );
+
+ if (nextFile) {
+ requestAnimationFrame(() => {
+ commit(types.RENDER_FILE, nextFile);
+ });
+ requestIdleCallback(
+ () => {
+ checkItem()
+ .then(resolve)
+ .catch(() => {});
+ },
+ { timeout: 1000 },
+ );
+ } else {
+ resolve();
+ }
+ });
+
+ return checkItem();
+};
+
export const setInlineDiffViewType = ({ commit }) => {
commit(types.SET_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE);
@@ -70,6 +122,25 @@ export const loadMoreLines = ({ commit }, options) => {
});
};
+export const scrollToLineIfNeededInline = (_, line) => {
+ const hash = getLocationHash();
+
+ if (hash && line.lineCode === hash) {
+ handleLocationHash();
+ }
+};
+
+export const scrollToLineIfNeededParallel = (_, line) => {
+ const hash = getLocationHash();
+
+ if (
+ hash &&
+ ((line.left && line.left.lineCode === hash) || (line.right && line.right.lineCode === hash))
+ ) {
+ handleLocationHash();
+ }
+};
+
export const loadCollapsedDiff = ({ commit }, file) =>
axios.get(file.loadCollapsedDiffUrl).then(res => {
commit(types.ADD_COLLAPSED_DIFFS, {
@@ -96,7 +167,7 @@ export const expandAllFiles = ({ commit }) => {
export const toggleFileDiscussions = ({ getters, dispatch }, diff) => {
const discussions = getters.getDiffFileDiscussions(diff);
const shouldCloseAll = getters.diffHasAllExpandedDiscussions(diff);
- const shouldExpandAll = getters.diffHasAllCollpasedDiscussions(diff);
+ const shouldExpandAll = getters.diffHasAllCollapsedDiscussions(diff);
discussions.forEach(discussion => {
const data = { discussionId: discussion.id };
@@ -109,5 +180,51 @@ export const toggleFileDiscussions = ({ getters, dispatch }, diff) => {
});
};
+export const saveDiffDiscussion = ({ dispatch }, { note, formData }) => {
+ const postData = getNoteFormData({
+ note,
+ ...formData,
+ });
+
+ return dispatch('saveNote', postData, { root: true })
+ .then(result => dispatch('updateDiscussion', result.discussion, { root: true }))
+ .then(discussion => dispatch('assignDiscussionsToDiff', [discussion]))
+ .then(() => dispatch('closeDiffFileCommentForm', formData.diffFile.file_hash))
+ .then(() => dispatch('startTaskList', null, { root: true }))
+ .catch(() => createFlash(s__('MergeRequests|Saving the comment failed')));
+};
+
+export const toggleTreeOpen = ({ commit }, path) => {
+ commit(types.TOGGLE_FOLDER_OPEN, path);
+};
+
+export const scrollToFile = ({ state, commit }, path) => {
+ const { fileHash } = state.treeEntries[path];
+ document.location.hash = fileHash;
+
+ commit(types.UPDATE_CURRENT_DIFF_FILE_ID, fileHash);
+
+ setTimeout(() => commit(types.UPDATE_CURRENT_DIFF_FILE_ID, ''), 1000);
+};
+
+export const toggleShowTreeList = ({ commit, state }) => {
+ commit(types.TOGGLE_SHOW_TREE_LIST);
+ localStorage.setItem(MR_TREE_SHOW_KEY, state.showTreeList);
+};
+
+export const openDiffFileCommentForm = ({ commit, getters }, formData) => {
+ const form = getters.getCommentFormForDiffFile(formData.fileHash);
+
+ if (form) {
+ commit(types.UPDATE_DIFF_FILE_COMMENT_FORM, formData);
+ } else {
+ commit(types.OPEN_DIFF_FILE_COMMENT_FORM, formData);
+ }
+};
+
+export const closeDiffFileCommentForm = ({ commit }, fileHash) => {
+ commit(types.CLOSE_DIFF_FILE_COMMENT_FORM, fileHash);
+};
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index 4a47646d7fa..6a87b712b48 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -1,11 +1,10 @@
-import _ from 'underscore';
import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '../constants';
export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW_TYPE;
export const isInlineView = state => state.diffViewType === INLINE_DIFF_VIEW_TYPE;
-export const areAllFilesCollapsed = state => state.diffFiles.every(file => file.collapsed);
+export const hasCollapsedFile = state => state.diffFiles.some(file => file.collapsed);
export const commitId = state => (state.commit && state.commit.id ? state.commit.id : null);
@@ -17,18 +16,24 @@ export const commitId = state => (state.commit && state.commit.id ? state.commit
export const diffHasAllExpandedDiscussions = (state, getters) => diff => {
const discussions = getters.getDiffFileDiscussions(diff);
- return (discussions.length && discussions.every(discussion => discussion.expanded)) || false;
+ return (
+ (discussions && discussions.length && discussions.every(discussion => discussion.expanded)) ||
+ false
+ );
};
/**
- * Checks if the diff has all discussions collpased
+ * Checks if the diff has all discussions collapsed
* @param {Object} diff
* @returns {Boolean}
*/
-export const diffHasAllCollpasedDiscussions = (state, getters) => diff => {
+export const diffHasAllCollapsedDiscussions = (state, getters) => diff => {
const discussions = getters.getDiffFileDiscussions(diff);
- return (discussions.length && discussions.every(discussion => !discussion.expanded)) || false;
+ return (
+ (discussions && discussions.length && discussions.every(discussion => !discussion.expanded)) ||
+ false
+ );
};
/**
@@ -40,7 +45,9 @@ export const diffHasExpandedDiscussions = (state, getters) => diff => {
const discussions = getters.getDiffFileDiscussions(diff);
return (
- (discussions.length && discussions.find(discussion => discussion.expanded) !== undefined) ||
+ (discussions &&
+ discussions.length &&
+ discussions.find(discussion => discussion.expanded) !== undefined) ||
false
);
};
@@ -60,54 +67,53 @@ export const diffHasDiscussions = (state, getters) => diff =>
*/
export const getDiffFileDiscussions = (state, getters, rootState, rootGetters) => diff =>
rootGetters.discussions.filter(
- discussion =>
- discussion.diff_discussion && _.isEqual(discussion.diff_file.file_hash, diff.fileHash),
+ discussion => discussion.diff_discussion && discussion.diff_file.file_hash === diff.file_hash,
) || [];
-export const singleDiscussionByLineCode = (state, getters, rootState, rootGetters) => lineCode => {
- if (!lineCode || lineCode === undefined) return [];
- const discussions = rootGetters.discussionsByLineCode;
- return discussions[lineCode] || [];
-};
-
-export const shouldRenderParallelCommentRow = (state, getters) => line => {
- const leftLineCode = line.left.lineCode;
- const rightLineCode = line.right.lineCode;
- const leftDiscussions = getters.singleDiscussionByLineCode(leftLineCode);
- const rightDiscussions = getters.singleDiscussionByLineCode(rightLineCode);
- const hasDiscussion = leftDiscussions.length || rightDiscussions.length;
+export const shouldRenderParallelCommentRow = state => line => {
+ const hasDiscussion =
+ (line.left && line.left.discussions && line.left.discussions.length) ||
+ (line.right && line.right.discussions && line.right.discussions.length);
- const hasExpandedDiscussionOnLeft = leftDiscussions.length
- ? leftDiscussions.every(discussion => discussion.expanded)
- : false;
- const hasExpandedDiscussionOnRight = rightDiscussions.length
- ? rightDiscussions.every(discussion => discussion.expanded)
- : false;
+ const hasExpandedDiscussionOnLeft =
+ line.left && line.left.discussions && line.left.discussions.length
+ ? line.left.discussions.every(discussion => discussion.expanded)
+ : false;
+ const hasExpandedDiscussionOnRight =
+ line.right && line.right.discussions && line.right.discussions.length
+ ? line.right.discussions.every(discussion => discussion.expanded)
+ : false;
if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) {
return true;
}
- const hasCommentFormOnLeft = state.diffLineCommentForms[leftLineCode];
- const hasCommentFormOnRight = state.diffLineCommentForms[rightLineCode];
+ const hasCommentFormOnLeft = line.left && state.diffLineCommentForms[line.left.line_code];
+ const hasCommentFormOnRight = line.right && state.diffLineCommentForms[line.right.line_code];
return hasCommentFormOnLeft || hasCommentFormOnRight;
};
-export const shouldRenderInlineCommentRow = (state, getters) => line => {
- if (state.diffLineCommentForms[line.lineCode]) return true;
+export const shouldRenderInlineCommentRow = state => line => {
+ if (state.diffLineCommentForms[line.line_code]) return true;
- const lineDiscussions = getters.singleDiscussionByLineCode(line.lineCode);
- if (lineDiscussions.length === 0) {
+ if (!line.discussions || line.discussions.length === 0) {
return false;
}
- return lineDiscussions.every(discussion => discussion.expanded);
+ return line.discussions.every(discussion => discussion.expanded);
};
// prevent babel-plugin-rewire from generating an invalid default during karma∂ tests
export const getDiffFileByHash = state => fileHash =>
- state.diffFiles.find(file => file.fileHash === fileHash);
+ state.diffFiles.find(file => file.file_hash === fileHash);
+
+export const allBlobs = state => Object.values(state.treeEntries).filter(f => f.type === 'blob');
+
+export const diffFilesLength = state => state.diffFiles.length;
+
+export const getCommentFormForDiffFile = state => fileHash =>
+ state.commentForms.find(form => form.fileHash === fileHash);
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
index 39d90a64aab..085e255f1d3 100644
--- a/app/assets/javascripts/diffs/store/modules/diff_state.js
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -1,18 +1,29 @@
import Cookies from 'js-cookie';
import { getParameterValues } from '~/lib/utils/url_utility';
-import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants';
+import bp from '~/breakpoints';
+import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME, MR_TREE_SHOW_KEY } from '../../constants';
const viewTypeFromQueryString = getParameterValues('view')[0];
const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME);
const defaultViewType = INLINE_DIFF_VIEW_TYPE;
+const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY);
export default () => ({
isLoading: true,
endpoint: '',
basePath: '',
commit: null,
+ startVersion: null,
diffFiles: [],
mergeRequestDiffs: [],
+ mergeRequestDiff: null,
diffLineCommentForms: {},
diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
+ tree: [],
+ treeEntries: {},
+ showTreeList:
+ storedTreeShow === null ? bp.getBreakpointSize() !== 'xs' : storedTreeShow === 'true',
+ currentDiffFileId: '',
+ projectPath: '',
+ commentForms: [],
});
diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js
index 20d1ebbe049..6860e24db6b 100644
--- a/app/assets/javascripts/diffs/store/modules/index.js
+++ b/app/assets/javascripts/diffs/store/modules/index.js
@@ -3,10 +3,10 @@ import * as getters from '../getters';
import mutations from '../mutations';
import createState from './diff_state';
-export default {
+export default () => ({
namespaced: true,
state: createState(),
getters,
actions,
mutations,
-};
+});
diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js
index 2c8e1a1466f..e011031e72c 100644
--- a/app/assets/javascripts/diffs/store/mutation_types.js
+++ b/app/assets/javascripts/diffs/store/mutation_types.js
@@ -8,3 +8,13 @@ export const REMOVE_COMMENT_FORM_LINE = 'REMOVE_COMMENT_FORM_LINE';
export const ADD_CONTEXT_LINES = 'ADD_CONTEXT_LINES';
export const ADD_COLLAPSED_DIFFS = 'ADD_COLLAPSED_DIFFS';
export const EXPAND_ALL_FILES = 'EXPAND_ALL_FILES';
+export const RENDER_FILE = 'RENDER_FILE';
+export const SET_LINE_DISCUSSIONS_FOR_FILE = 'SET_LINE_DISCUSSIONS_FOR_FILE';
+export const REMOVE_LINE_DISCUSSIONS_FOR_FILE = 'REMOVE_LINE_DISCUSSIONS_FOR_FILE';
+export const TOGGLE_FOLDER_OPEN = 'TOGGLE_FOLDER_OPEN';
+export const TOGGLE_SHOW_TREE_LIST = 'TOGGLE_SHOW_TREE_LIST';
+export const UPDATE_CURRENT_DIFF_FILE_ID = 'UPDATE_CURRENT_DIFF_FILE_ID';
+
+export const OPEN_DIFF_FILE_COMMENT_FORM = 'OPEN_DIFF_FILE_COMMENT_FORM';
+export const UPDATE_DIFF_FILE_COMMENT_FORM = 'UPDATE_DIFF_FILE_COMMENT_FORM';
+export const CLOSE_DIFF_FILE_COMMENT_FORM = 'CLOSE_DIFF_FILE_COMMENT_FORM';
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index a98b2be89a3..2133cfe4825 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -1,7 +1,15 @@
import Vue from 'vue';
-import _ from 'underscore';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import { findDiffFile, addLineReferences, removeMatchLine, addContextLines } from './utils';
+import { sortTree } from '~/ide/stores/utils';
+import {
+ findDiffFile,
+ addLineReferences,
+ removeMatchLine,
+ addContextLines,
+ prepareDiffData,
+ isDiscussionApplicableToLine,
+ generateTreeList,
+} from './utils';
import * as types from './mutation_types';
export default {
@@ -15,14 +23,25 @@ export default {
},
[types.SET_DIFF_DATA](state, data) {
+ prepareDiffData(data);
+ const { tree, treeEntries } = generateTreeList(data.diff_files);
+
Object.assign(state, {
- ...convertObjectPropsToCamelCase(data, { deep: true }),
+ ...convertObjectPropsToCamelCase(data),
+ tree: sortTree(tree),
+ treeEntries,
+ });
+ },
+
+ [types.RENDER_FILE](state, file) {
+ Object.assign(file, {
+ renderIt: true,
});
},
[types.SET_MERGE_REQUEST_DIFFS](state, mergeRequestDiffs) {
Object.assign(state, {
- mergeRequestDiffs: convertObjectPropsToCamelCase(mergeRequestDiffs, { deep: true }),
+ mergeRequestDiffs,
});
},
@@ -42,13 +61,18 @@ export default {
const { lineNumbers, contextLines, fileHash } = options;
const { bottom } = options.params;
const diffFile = findDiffFile(state.diffFiles, fileHash);
- const { highlightedDiffLines, parallelDiffLines } = diffFile;
removeMatchLine(diffFile, lineNumbers, bottom);
- const lines = addLineReferences(contextLines, lineNumbers, bottom);
+
+ const lines = addLineReferences(contextLines, lineNumbers, bottom).map(line => ({
+ ...line,
+ line_code: line.line_code || `${fileHash}_${line.old_line}_${line.new_line}`,
+ discussions: line.discussions || [],
+ }));
+
addContextLines({
- inlineLines: highlightedDiffLines,
- parallelLines: parallelDiffLines,
+ inlineLines: diffFile.highlighted_diff_lines,
+ parallelLines: diffFile.parallel_diff_lines,
contextLines: lines,
bottom,
lineNumbers,
@@ -56,20 +80,147 @@ export default {
},
[types.ADD_COLLAPSED_DIFFS](state, { file, data }) {
- const normalizedData = convertObjectPropsToCamelCase(data, { deep: true });
- const [newFileData] = normalizedData.diffFiles.filter(f => f.fileHash === file.fileHash);
-
- if (newFileData) {
- const index = _.findIndex(state.diffFiles, f => f.fileHash === file.fileHash);
- state.diffFiles.splice(index, 1, newFileData);
- }
+ prepareDiffData(data);
+ const [newFileData] = data.diff_files.filter(f => f.file_hash === file.file_hash);
+ const selectedFile = state.diffFiles.find(f => f.file_hash === file.file_hash);
+ Object.assign(selectedFile, { ...newFileData });
},
[types.EXPAND_ALL_FILES](state) {
- // eslint-disable-next-line no-param-reassign
state.diffFiles = state.diffFiles.map(file => ({
...file,
collapsed: false,
}));
},
+
+ [types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode }) {
+ const { latestDiff } = state;
+
+ const discussionLineCode = discussion.line_code;
+ const fileHash = discussion.diff_file.file_hash;
+ const lineCheck = line =>
+ line.line_code === discussionLineCode &&
+ isDiscussionApplicableToLine({
+ discussion,
+ diffPosition: diffPositionByLineCode[line.line_code],
+ latestDiff,
+ });
+
+ state.diffFiles = state.diffFiles.map(diffFile => {
+ if (diffFile.file_hash === fileHash) {
+ const file = { ...diffFile };
+
+ if (file.highlighted_diff_lines) {
+ file.highlighted_diff_lines = file.highlighted_diff_lines.map(line => {
+ if (lineCheck(line)) {
+ return {
+ ...line,
+ discussions: line.discussions.concat(discussion),
+ };
+ }
+
+ return line;
+ });
+ }
+
+ if (file.parallel_diff_lines) {
+ file.parallel_diff_lines = file.parallel_diff_lines.map(line => {
+ const left = line.left && lineCheck(line.left);
+ const right = line.right && lineCheck(line.right);
+
+ if (left || right) {
+ return {
+ left: {
+ ...line.left,
+ discussions: left ? line.left.discussions.concat(discussion) : [],
+ },
+ right: {
+ ...line.right,
+ discussions: right && !left ? line.right.discussions.concat(discussion) : [],
+ },
+ };
+ }
+
+ return line;
+ });
+ }
+
+ if (!file.parallel_diff_lines || !file.highlighted_diff_lines) {
+ file.discussions = file.discussions.concat(discussion);
+ }
+
+ return file;
+ }
+
+ return diffFile;
+ });
+ },
+
+ [types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, lineCode, id }) {
+ const selectedFile = state.diffFiles.find(f => f.file_hash === fileHash);
+ if (selectedFile) {
+ if (selectedFile.parallel_diff_lines) {
+ const targetLine = selectedFile.parallel_diff_lines.find(
+ line =>
+ (line.left && line.left.line_code === lineCode) ||
+ (line.right && line.right.line_code === lineCode),
+ );
+ if (targetLine) {
+ const side = targetLine.left && targetLine.left.line_code === lineCode ? 'left' : 'right';
+
+ Object.assign(targetLine[side], {
+ discussions: [],
+ });
+ }
+ }
+
+ if (selectedFile.highlighted_diff_lines) {
+ const targetInlineLine = selectedFile.highlighted_diff_lines.find(
+ line => line.line_code === lineCode,
+ );
+
+ if (targetInlineLine) {
+ Object.assign(targetInlineLine, {
+ discussions: [],
+ });
+ }
+ }
+
+ if (selectedFile.discussions && selectedFile.discussions.length) {
+ selectedFile.discussions = selectedFile.discussions.filter(
+ discussion => discussion.id !== id,
+ );
+ }
+ }
+ },
+ [types.TOGGLE_FOLDER_OPEN](state, path) {
+ state.treeEntries[path].opened = !state.treeEntries[path].opened;
+ },
+ [types.TOGGLE_SHOW_TREE_LIST](state) {
+ state.showTreeList = !state.showTreeList;
+ },
+ [types.UPDATE_CURRENT_DIFF_FILE_ID](state, fileId) {
+ state.currentDiffFileId = fileId;
+ },
+ [types.OPEN_DIFF_FILE_COMMENT_FORM](state, formData) {
+ state.commentForms.push({
+ ...formData,
+ });
+ },
+ [types.UPDATE_DIFF_FILE_COMMENT_FORM](state, formData) {
+ const { fileHash } = formData;
+
+ state.commentForms = state.commentForms.map(form => {
+ if (form.fileHash === fileHash) {
+ return {
+ ...formData,
+ };
+ }
+
+ return form;
+ });
+ },
+ [types.CLOSE_DIFF_FILE_COMMENT_FORM](state, fileHash) {
+ state.commentForms = state.commentForms.filter(form => form.fileHash !== fileHash);
+ },
};
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 82082ac508a..d9d3c0f2ca2 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -1,17 +1,20 @@
import _ from 'underscore';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { diffModes } from '~/ide/constants';
import {
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
TEXT_DIFF_POSITION_TYPE,
+ LEGACY_DIFF_NOTE_TYPE,
DIFF_NOTE_TYPE,
NEW_LINE_TYPE,
OLD_LINE_TYPE,
MATCH_LINE_TYPE,
+ LINES_TO_BE_RENDERED_DIRECTLY,
+ MAX_LINES_TO_BE_RENDERED,
} from '../constants';
export function findDiffFile(files, hash) {
- return files.filter(file => file.fileHash === hash)[0];
+ return files.filter(file => file.file_hash === hash)[0];
}
export const getReversePosition = linePosition => {
@@ -22,7 +25,7 @@ export const getReversePosition = linePosition => {
return LINE_POSITION_RIGHT;
};
-export function getNoteFormData(params) {
+export function getFormData(params) {
const {
note,
noteableType,
@@ -31,41 +34,56 @@ export function getNoteFormData(params) {
noteTargetLine,
diffViewType,
linePosition,
+ positionType,
} = params;
const position = JSON.stringify({
- base_sha: diffFile.diffRefs.baseSha,
- start_sha: diffFile.diffRefs.startSha,
- head_sha: diffFile.diffRefs.headSha,
- old_path: diffFile.oldPath,
- new_path: diffFile.newPath,
- position_type: TEXT_DIFF_POSITION_TYPE,
- old_line: noteTargetLine.oldLine,
- new_line: noteTargetLine.newLine,
+ base_sha: diffFile.diff_refs.base_sha,
+ start_sha: diffFile.diff_refs.start_sha,
+ head_sha: diffFile.diff_refs.head_sha,
+ old_path: diffFile.old_path,
+ new_path: diffFile.new_path,
+ position_type: positionType || TEXT_DIFF_POSITION_TYPE,
+ old_line: noteTargetLine ? noteTargetLine.old_line : null,
+ new_line: noteTargetLine ? noteTargetLine.new_line : null,
+ x: params.x,
+ y: params.y,
+ width: params.width,
+ height: params.height,
});
const postData = {
view: diffViewType,
line_type: linePosition === LINE_POSITION_RIGHT ? NEW_LINE_TYPE : OLD_LINE_TYPE,
- merge_request_diff_head_sha: diffFile.diffRefs.headSha,
+ merge_request_diff_head_sha: diffFile.diff_refs.head_sha,
in_reply_to_discussion_id: '',
note_project_id: '',
target_type: noteableData.targetType,
target_id: noteableData.id,
+ return_discussion: true,
note: {
note,
position,
noteable_type: noteableType,
noteable_id: noteableData.id,
commit_id: '',
- type: DIFF_NOTE_TYPE,
- line_code: noteTargetLine.lineCode,
+ type:
+ diffFile.diff_refs.start_sha && diffFile.diff_refs.head_sha
+ ? DIFF_NOTE_TYPE
+ : LEGACY_DIFF_NOTE_TYPE,
+ line_code: noteTargetLine ? noteTargetLine.line_code : null,
},
};
+ return postData;
+}
+
+export function getNoteFormData(params) {
+ const data = getFormData(params);
+
return {
- endpoint: noteableData.create_note_path,
- data: postData,
+ endpoint: params.noteableData.create_note_path,
+ data,
};
}
@@ -74,7 +92,7 @@ export const findIndexInInlineLines = (lines, lineNumbers) => {
return _.findIndex(
lines,
- line => line.oldLine === oldLineNumber && line.newLine === newLineNumber,
+ line => line.old_line === oldLineNumber && line.new_line === newLineNumber,
);
};
@@ -86,18 +104,18 @@ export const findIndexInParallelLines = (lines, lineNumbers) => {
line =>
line.left &&
line.right &&
- line.left.oldLine === oldLineNumber &&
- line.right.newLine === newLineNumber,
+ line.left.old_line === oldLineNumber &&
+ line.right.new_line === newLineNumber,
);
};
export function removeMatchLine(diffFile, lineNumbers, bottom) {
- const indexForInline = findIndexInInlineLines(diffFile.highlightedDiffLines, lineNumbers);
- const indexForParallel = findIndexInParallelLines(diffFile.parallelDiffLines, lineNumbers);
+ const indexForInline = findIndexInInlineLines(diffFile.highlighted_diff_lines, lineNumbers);
+ const indexForParallel = findIndexInParallelLines(diffFile.parallel_diff_lines, lineNumbers);
const factor = bottom ? 1 : -1;
- diffFile.highlightedDiffLines.splice(indexForInline + factor, 1);
- diffFile.parallelDiffLines.splice(indexForParallel + factor, 1);
+ diffFile.highlighted_diff_lines.splice(indexForInline + factor, 1);
+ diffFile.parallel_diff_lines.splice(indexForParallel + factor, 1);
}
export function addLineReferences(lines, lineNumbers, bottom) {
@@ -106,18 +124,16 @@ export function addLineReferences(lines, lineNumbers, bottom) {
let matchLineIndex = -1;
const linesWithNumbers = lines.map((l, index) => {
- const line = convertObjectPropsToCamelCase(l);
-
- if (line.type === MATCH_LINE_TYPE) {
+ if (l.type === MATCH_LINE_TYPE) {
matchLineIndex = index;
} else {
- Object.assign(line, {
- oldLine: bottom ? oldLineNumber + index + 1 : oldLineNumber + index - lineCount,
- newLine: bottom ? newLineNumber + index + 1 : newLineNumber + index - lineCount,
+ Object.assign(l, {
+ old_line: bottom ? oldLineNumber + index + 1 : oldLineNumber + index - lineCount,
+ new_line: bottom ? newLineNumber + index + 1 : newLineNumber + index - lineCount,
});
}
- return line;
+ return l;
});
if (matchLineIndex > -1) {
@@ -127,9 +143,9 @@ export function addLineReferences(lines, lineNumbers, bottom) {
: linesWithNumbers[matchLineIndex + 1];
Object.assign(line, {
- metaData: {
- oldPos: targetLine.oldLine,
- newPos: targetLine.newLine,
+ meta_data: {
+ old_pos: targetLine.old_line,
+ new_pos: targetLine.new_line,
},
});
}
@@ -161,32 +177,80 @@ export function addContextLines(options) {
* @returns {Object}
*/
export function trimFirstCharOfLineContent(line = {}) {
+ // eslint-disable-next-line no-param-reassign
+ delete line.text;
+ // eslint-disable-next-line no-param-reassign
+ line.discussions = [];
+
const parsedLine = Object.assign({}, line);
- if (line.richText) {
- const firstChar = parsedLine.richText.charAt(0);
+ if (line.rich_text) {
+ const firstChar = parsedLine.rich_text.charAt(0);
if (firstChar === ' ' || firstChar === '+' || firstChar === '-') {
- parsedLine.richText = line.richText.substring(1);
+ parsedLine.rich_text = line.rich_text.substring(1);
}
}
return parsedLine;
}
-export function getDiffRefsByLineCode(diffFiles) {
- return diffFiles.reduce((acc, diffFile) => {
- const { baseSha, headSha, startSha } = diffFile.diffRefs;
- const { newPath, oldPath } = diffFile;
+// This prepares and optimizes the incoming diff data from the server
+// by setting up incremental rendering and removing unneeded data
+export function prepareDiffData(diffData) {
+ const filesLength = diffData.diff_files.length;
+ let showingLines = 0;
+ for (let i = 0; i < filesLength; i += 1) {
+ const file = diffData.diff_files[i];
+
+ if (file.parallel_diff_lines) {
+ const linesLength = file.parallel_diff_lines.length;
+ for (let u = 0; u < linesLength; u += 1) {
+ const line = file.parallel_diff_lines[u];
+ if (line.left) {
+ line.left = trimFirstCharOfLineContent(line.left);
+ }
+ if (line.right) {
+ line.right = trimFirstCharOfLineContent(line.right);
+ }
+ }
+ }
+
+ if (file.highlighted_diff_lines) {
+ const linesLength = file.highlighted_diff_lines.length;
+ for (let u = 0; u < linesLength; u += 1) {
+ const line = file.highlighted_diff_lines[u];
+ Object.assign(line, { ...trimFirstCharOfLineContent(line) });
+ }
+ showingLines += file.parallel_diff_lines.length;
+ }
+
+ Object.assign(file, {
+ renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY,
+ collapsed: file.text && showingLines > MAX_LINES_TO_BE_RENDERED,
+ discussions: [],
+ });
+ }
+}
+export function getDiffPositionByLineCode(diffFiles) {
+ return diffFiles.reduce((acc, diffFile) => {
// We can only use highlightedDiffLines to create the map of diff lines because
// highlightedDiffLines will also include every parallel diff line in it.
- if (diffFile.highlightedDiffLines) {
- diffFile.highlightedDiffLines.forEach(line => {
- const { lineCode, oldLine, newLine } = line;
-
- if (lineCode) {
- acc[lineCode] = { baseSha, headSha, startSha, newPath, oldPath, oldLine, newLine };
+ if (diffFile.highlighted_diff_lines) {
+ diffFile.highlighted_diff_lines.forEach(line => {
+ if (line.line_code) {
+ acc[line.line_code] = {
+ base_sha: diffFile.diff_refs.base_sha,
+ head_sha: diffFile.diff_refs.head_sha,
+ start_sha: diffFile.diff_refs.start_sha,
+ new_path: diffFile.new_path,
+ old_path: diffFile.old_path,
+ old_line: line.old_line,
+ new_line: line.new_line,
+ line_code: line.line_code,
+ position_type: 'text',
+ };
}
});
}
@@ -194,3 +258,69 @@ export function getDiffRefsByLineCode(diffFiles) {
return acc;
}, {});
}
+
+// This method will check whether the discussion is still applicable
+// to the diff line in question regarding different versions of the MR
+export function isDiscussionApplicableToLine({ discussion, diffPosition, latestDiff }) {
+ const { line_code, ...diffPositionCopy } = diffPosition;
+
+ if (discussion.original_position && discussion.position) {
+ const originalRefs = discussion.original_position;
+ const refs = discussion.position;
+
+ return _.isEqual(refs, diffPositionCopy) || _.isEqual(originalRefs, diffPositionCopy);
+ }
+
+ // eslint-disable-next-line
+ return latestDiff && discussion.active && line_code === discussion.line_code;
+}
+
+export const generateTreeList = files =>
+ files.reduce(
+ (acc, file) => {
+ const split = file.new_path.split('/');
+
+ split.forEach((name, i) => {
+ const parent = acc.treeEntries[split.slice(0, i).join('/')];
+ const path = `${parent ? `${parent.path}/` : ''}${name}`;
+
+ if (!acc.treeEntries[path]) {
+ const type = path === file.new_path ? 'blob' : 'tree';
+ acc.treeEntries[path] = {
+ key: path,
+ path,
+ name,
+ type,
+ tree: [],
+ };
+
+ const entry = acc.treeEntries[path];
+
+ if (type === 'blob') {
+ Object.assign(entry, {
+ changed: true,
+ tempFile: file.new_file,
+ deleted: file.deleted_file,
+ fileHash: file.file_hash,
+ addedLines: file.added_lines,
+ removedLines: file.removed_lines,
+ });
+ } else {
+ Object.assign(entry, {
+ opened: true,
+ });
+ }
+
+ (parent ? parent.tree : acc.tree).push(entry);
+ }
+ });
+
+ return acc;
+ },
+ { treeEntries: {}, tree: [] },
+ );
+
+export const getDiffMode = diffFile => {
+ const diffModeKey = Object.keys(diffModes).find(key => diffFile[`${key}_file`]);
+ return diffModes[diffModeKey] || diffModes.replaced;
+};
diff --git a/app/assets/javascripts/dirty_submit/dirty_submit_collection.js b/app/assets/javascripts/dirty_submit/dirty_submit_collection.js
new file mode 100644
index 00000000000..42b051b2270
--- /dev/null
+++ b/app/assets/javascripts/dirty_submit/dirty_submit_collection.js
@@ -0,0 +1,13 @@
+import DirtySubmitForm from './dirty_submit_form';
+
+class DirtySubmitCollection {
+ constructor(forms) {
+ this.forms = forms;
+
+ this.dirtySubmits = [];
+
+ this.forms.forEach(form => this.dirtySubmits.push(new DirtySubmitForm(form)));
+ }
+}
+
+export default DirtySubmitCollection;
diff --git a/app/assets/javascripts/dirty_submit/dirty_submit_factory.js b/app/assets/javascripts/dirty_submit/dirty_submit_factory.js
new file mode 100644
index 00000000000..95a896a7f0b
--- /dev/null
+++ b/app/assets/javascripts/dirty_submit/dirty_submit_factory.js
@@ -0,0 +1,9 @@
+import DirtySubmitCollection from './dirty_submit_collection';
+import DirtySubmitForm from './dirty_submit_form';
+
+export default function dirtySubmitFactory(formOrForms) {
+ const isCollection = formOrForms instanceof NodeList || formOrForms instanceof Array;
+ const DirtySubmitClass = isCollection ? DirtySubmitCollection : DirtySubmitForm;
+
+ return new DirtySubmitClass(formOrForms);
+}
diff --git a/app/assets/javascripts/dirty_submit/dirty_submit_form.js b/app/assets/javascripts/dirty_submit/dirty_submit_form.js
new file mode 100644
index 00000000000..d8d0fa1fac4
--- /dev/null
+++ b/app/assets/javascripts/dirty_submit/dirty_submit_form.js
@@ -0,0 +1,83 @@
+import _ from 'underscore';
+
+class DirtySubmitForm {
+ constructor(form) {
+ this.form = form;
+ this.dirtyInputs = [];
+ this.isDisabled = true;
+
+ this.init();
+ }
+
+ init() {
+ this.inputs = this.form.querySelectorAll('input, textarea, select');
+ this.submits = this.form.querySelectorAll('input[type=submit], button[type=submit]');
+
+ this.inputs.forEach(DirtySubmitForm.initInput);
+ this.toggleSubmission();
+
+ this.registerListeners();
+ }
+
+ registerListeners() {
+ const throttledUpdateDirtyInput = _.throttle(
+ event => this.updateDirtyInput(event),
+ DirtySubmitForm.THROTTLE_DURATION,
+ );
+ this.form.addEventListener('input', throttledUpdateDirtyInput);
+ this.form.addEventListener('submit', event => this.formSubmit(event));
+ }
+
+ updateDirtyInput(event) {
+ const input = event.target;
+
+ if (!input.dataset.isDirtySubmitInput) return;
+
+ this.updateDirtyInputs(input);
+ this.toggleSubmission();
+ }
+
+ updateDirtyInputs(input) {
+ const { name } = input;
+ const isDirty =
+ input.dataset.dirtySubmitOriginalValue !== DirtySubmitForm.inputCurrentValue(input);
+ const indexOfInputName = this.dirtyInputs.indexOf(name);
+ const isExisting = indexOfInputName !== -1;
+
+ if (isDirty && !isExisting) this.dirtyInputs.push(name);
+ if (!isDirty && isExisting) this.dirtyInputs.splice(indexOfInputName, 1);
+ }
+
+ toggleSubmission() {
+ this.isDisabled = this.dirtyInputs.length === 0;
+ this.submits.forEach(element => {
+ element.disabled = this.isDisabled;
+ });
+ }
+
+ formSubmit(event) {
+ if (this.isDisabled) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+
+ return !this.isDisabled;
+ }
+
+ static initInput(element) {
+ element.dataset.isDirtySubmitInput = true;
+ element.dataset.dirtySubmitOriginalValue = DirtySubmitForm.inputCurrentValue(element);
+ }
+
+ static isInputCheckable(input) {
+ return input.type === 'checkbox' || input.type === 'radio';
+ }
+
+ static inputCurrentValue(input) {
+ return DirtySubmitForm.isInputCheckable(input) ? input.checked.toString() : input.value;
+ }
+}
+
+DirtySubmitForm.THROTTLE_DURATION = 500;
+
+export default DirtySubmitForm;
diff --git a/app/assets/javascripts/dismissable_callout.js b/app/assets/javascripts/dismissable_callout.js
new file mode 100644
index 00000000000..5185b019376
--- /dev/null
+++ b/app/assets/javascripts/dismissable_callout.js
@@ -0,0 +1,27 @@
+import $ from 'jquery';
+import axios from '~/lib/utils/axios_utils';
+import { __ } from '~/locale';
+import Flash from '~/flash';
+
+export default function initDismissableCallout(alertSelector) {
+ const alertEl = document.querySelector(alertSelector);
+ if (!alertEl) {
+ return;
+ }
+
+ const closeButtonEl = alertEl.getElementsByClassName('close')[0];
+ const { dismissEndpoint, featureId } = closeButtonEl.dataset;
+
+ closeButtonEl.addEventListener('click', () => {
+ axios
+ .post(dismissEndpoint, {
+ feature_name: featureId,
+ })
+ .then(() => {
+ $(alertEl).alert('close');
+ })
+ .catch(() => {
+ Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
+ });
+ });
+}
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
deleted file mode 100644
index a5af37e80b6..00000000000
--- a/app/assets/javascripts/dispatcher.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/* eslint-disable consistent-return, no-new */
-
-import $ from 'jquery';
-import GfmAutoComplete from './gfm_auto_complete';
-import { convertPermissionToBoolean } from './lib/utils/common_utils';
-import GlFieldErrors from './gl_field_errors';
-import Shortcuts from './shortcuts';
-import SearchAutocomplete from './search_autocomplete';
-import performanceBar from './performance_bar';
-
-function initSearch() {
- // Only when search form is present
- if ($('.search').length) {
- return new SearchAutocomplete();
- }
-}
-
-function initFieldErrors() {
- $('.gl-show-field-errors').each((i, form) => {
- new GlFieldErrors(form);
- });
-}
-
-function initPageShortcuts(page) {
- const pagesWithCustomShortcuts = [
- 'projects:activity',
- 'projects:artifacts:browse',
- 'projects:artifacts:file',
- 'projects:blame:show',
- 'projects:blob:show',
- 'projects:commit:show',
- 'projects:commits:show',
- 'projects:find_file:show',
- 'projects:issues:edit',
- 'projects:issues:index',
- 'projects:issues:new',
- 'projects:issues:show',
- 'projects:merge_requests:creations:diffs',
- 'projects:merge_requests:creations:new',
- 'projects:merge_requests:edit',
- 'projects:merge_requests:index',
- 'projects:merge_requests:show',
- 'projects:network:show',
- 'projects:show',
- 'projects:tree:show',
- 'groups:show',
- ];
-
- if (pagesWithCustomShortcuts.indexOf(page) === -1) {
- new Shortcuts();
- }
-}
-
-function initGFMInput() {
- $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
- const gfm = new GfmAutoComplete(
- gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources,
- );
- const enableGFM = convertPermissionToBoolean(
- el.dataset.supportsAutocomplete,
- );
- gfm.setup($(el), {
- emojis: true,
- members: enableGFM,
- issues: enableGFM,
- milestones: enableGFM,
- mergeRequests: enableGFM,
- labels: enableGFM,
- });
- });
-}
-
-function initPerformanceBar() {
- if (document.querySelector('#js-peek')) {
- performanceBar({ container: '#js-peek' });
- }
-}
-
-export default () => {
- initSearch();
- initFieldErrors();
-
- const page = $('body').attr('data-page');
- if (page) {
- initPageShortcuts(page);
- initGFMInput();
- initPerformanceBar();
- }
-};
diff --git a/app/assets/javascripts/droplab/constants.js b/app/assets/javascripts/droplab/constants.js
index 868d47e91b3..6451af49d36 100644
--- a/app/assets/javascripts/droplab/constants.js
+++ b/app/assets/javascripts/droplab/constants.js
@@ -6,11 +6,4 @@ const IGNORE_CLASS = 'droplab-item-ignore';
// Matches `{{anything}}` and `{{ everything }}`.
const TEMPLATE_REGEX = /\{\{(.+?)\}\}/g;
-export {
- DATA_TRIGGER,
- DATA_DROPDOWN,
- SELECTED_CLASS,
- ACTIVE_CLASS,
- TEMPLATE_REGEX,
- IGNORE_CLASS,
-};
+export { DATA_TRIGGER, DATA_DROPDOWN, SELECTED_CLASS, ACTIVE_CLASS, TEMPLATE_REGEX, IGNORE_CLASS };
diff --git a/app/assets/javascripts/droplab/drop_down.js b/app/assets/javascripts/droplab/drop_down.js
index 3cc316c3f3e..ccb3d56ed8c 100644
--- a/app/assets/javascripts/droplab/drop_down.js
+++ b/app/assets/javascripts/droplab/drop_down.js
@@ -2,7 +2,7 @@ import utils from './utils';
import { SELECTED_CLASS, IGNORE_CLASS } from './constants';
class DropDown {
- constructor(list, config = { }) {
+ constructor(list, config = {}) {
this.currentIndex = 0;
this.hidden = true;
this.list = typeof list === 'string' ? document.querySelector(list) : list;
@@ -157,7 +157,7 @@ class DropDown {
static setImagesSrc(template) {
const images = [...template.querySelectorAll('img[data-src]')];
- images.forEach((image) => {
+ images.forEach(image => {
const img = image;
img.src = img.getAttribute('data-src');
diff --git a/app/assets/javascripts/droplab/drop_lab.js b/app/assets/javascripts/droplab/drop_lab.js
index 2a02ede72bf..1339e28d8b8 100644
--- a/app/assets/javascripts/droplab/drop_lab.js
+++ b/app/assets/javascripts/droplab/drop_lab.js
@@ -51,7 +51,7 @@ class DropLab {
}
processData(trigger, data, methodName) {
- this.hooks.forEach((hook) => {
+ this.hooks.forEach(hook => {
if (Array.isArray(trigger)) hook.list[methodName](trigger);
if (hook.trigger.id === trigger) hook.list[methodName](data);
@@ -78,7 +78,8 @@ class DropLab {
}
changeHookList(trigger, list, plugins, config) {
- const availableTrigger = typeof trigger === 'string' ? document.getElementById(trigger) : trigger;
+ const availableTrigger =
+ typeof trigger === 'string' ? document.getElementById(trigger) : trigger;
this.hooks.forEach((hook, i) => {
const aHook = hook;
diff --git a/app/assets/javascripts/droplab/keyboard.js b/app/assets/javascripts/droplab/keyboard.js
index 02f1b805ce4..40837ffdf8f 100644
--- a/app/assets/javascripts/droplab/keyboard.js
+++ b/app/assets/javascripts/droplab/keyboard.js
@@ -2,15 +2,18 @@
import { ACTIVE_CLASS } from './constants';
-const Keyboard = function () {
+const Keyboard = function() {
var currentKey;
var currentFocus;
var isUpArrow = false;
var isDownArrow = false;
var removeHighlight = function removeHighlight(list) {
- var itemElements = Array.prototype.slice.call(list.list.querySelectorAll('li:not(.divider):not(.hidden)'), 0);
+ var itemElements = Array.prototype.slice.call(
+ list.list.querySelectorAll('li:not(.divider):not(.hidden)'),
+ 0,
+ );
var listItems = [];
- for(var i = 0; i < itemElements.length; i++) {
+ for (var i = 0; i < itemElements.length; i++) {
var listItem = itemElements[i];
listItem.classList.remove(ACTIVE_CLASS);
@@ -23,13 +26,13 @@ const Keyboard = function () {
var setMenuForArrows = function setMenuForArrows(list) {
var listItems = removeHighlight(list);
- if(list.currentIndex>0){
- if(!listItems[list.currentIndex-1]){
- list.currentIndex = list.currentIndex-1;
+ if (list.currentIndex > 0) {
+ if (!listItems[list.currentIndex - 1]) {
+ list.currentIndex = list.currentIndex - 1;
}
- if (listItems[list.currentIndex-1]) {
- var el = listItems[list.currentIndex-1];
+ if (listItems[list.currentIndex - 1]) {
+ var el = listItems[list.currentIndex - 1];
var filterDropdownEl = el.closest('.filter-dropdown');
el.classList.add(ACTIVE_CLASS);
@@ -55,7 +58,7 @@ const Keyboard = function () {
};
var selectItem = function selectItem(list) {
var listItems = removeHighlight(list);
- var currentItem = listItems[list.currentIndex-1];
+ var currentItem = listItems[list.currentIndex - 1];
var listEvent = new CustomEvent('click.dl', {
detail: {
list: list,
@@ -65,43 +68,49 @@ const Keyboard = function () {
});
list.list.dispatchEvent(listEvent);
list.hide();
- }
+ };
- var keydown = function keydown(e){
+ var keydown = function keydown(e) {
var typedOn = e.target;
var list = e.detail.hook.list;
var currentIndex = list.currentIndex;
isUpArrow = false;
isDownArrow = false;
- if(e.detail.which){
+ if (e.detail.which) {
currentKey = e.detail.which;
- if(currentKey === 13){
+ if (currentKey === 13) {
selectItem(e.detail.hook.list);
return;
}
- if(currentKey === 38) {
+ if (currentKey === 38) {
isUpArrow = true;
}
- if(currentKey === 40) {
+ if (currentKey === 40) {
isDownArrow = true;
}
- } else if(e.detail.key) {
+ } else if (e.detail.key) {
currentKey = e.detail.key;
- if(currentKey === 'Enter'){
+ if (currentKey === 'Enter') {
selectItem(e.detail.hook.list);
return;
}
- if(currentKey === 'ArrowUp') {
+ if (currentKey === 'ArrowUp') {
isUpArrow = true;
}
- if(currentKey === 'ArrowDown') {
+ if (currentKey === 'ArrowDown') {
isDownArrow = true;
}
}
- if(isUpArrow){ currentIndex--; }
- if(isDownArrow){ currentIndex++; }
- if(currentIndex < 0){ currentIndex = 0; }
+ if (isUpArrow) {
+ currentIndex--;
+ }
+ if (isDownArrow) {
+ currentIndex++;
+ }
+ if (currentIndex < 0) {
+ currentIndex = 0;
+ }
list.currentIndex = currentIndex;
setMenuForArrows(e.detail.hook.list);
};
diff --git a/app/assets/javascripts/droplab/plugins/ajax.js b/app/assets/javascripts/droplab/plugins/ajax.js
index 267b53fa4f2..48b2a90c459 100644
--- a/app/assets/javascripts/droplab/plugins/ajax.js
+++ b/app/assets/javascripts/droplab/plugins/ajax.js
@@ -43,12 +43,12 @@ const Ajax = {
return AjaxCache.retrieve(config.endpoint)
.then(self.preprocessing.bind(null, config))
- .then((data) => self._loadData(data, config, self))
+ .then(data => self._loadData(data, config, self))
.catch(config.onError);
},
destroy: function() {
this.destroyed = true;
- }
+ },
};
export default Ajax;
diff --git a/app/assets/javascripts/droplab/plugins/ajax_filter.js b/app/assets/javascripts/droplab/plugins/ajax_filter.js
index 1db20227a16..66a52548417 100644
--- a/app/assets/javascripts/droplab/plugins/ajax_filter.js
+++ b/app/assets/javascripts/droplab/plugins/ajax_filter.js
@@ -41,8 +41,10 @@ const AjaxFilter = {
if (config.searchValueFunction) {
searchValue = config.searchValueFunction();
}
- if (config.loadingTemplate && this.hook.list.data === undefined ||
- this.hook.list.data.length === 0) {
+ if (
+ (config.loadingTemplate && this.hook.list.data === undefined) ||
+ this.hook.list.data.length === 0
+ ) {
var dynamicList = this.hook.list.list.querySelector('[data-dynamic]');
var loadingTemplate = document.createElement('div');
loadingTemplate.innerHTML = config.loadingTemplate;
@@ -61,7 +63,7 @@ const AjaxFilter = {
params[config.searchKey] = searchValue;
var url = config.endpoint + this.buildParams(params);
return AjaxCache.retrieve(url)
- .then((data) => {
+ .then(data => {
this._loadData(data, config);
if (config.onLoadingFinished) {
config.onLoadingFinished(data);
@@ -72,8 +74,7 @@ const AjaxFilter = {
_loadData(data, config) {
const list = this.hook.list;
- if (config.loadingTemplate && list.data === undefined ||
- list.data.length === 0) {
+ if ((config.loadingTemplate && list.data === undefined) || list.data.length === 0) {
const dataLoadingTemplate = list.list.querySelector('[data-loading-template]');
if (dataLoadingTemplate) {
dataLoadingTemplate.outerHTML = this.listTemplate;
@@ -81,7 +82,8 @@ const AjaxFilter = {
}
if (!this.destroyed) {
var hookListChildren = list.list.children;
- var onlyDynamicList = hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic');
+ var onlyDynamicList =
+ hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic');
if (onlyDynamicList && data.length === 0) {
list.hide();
}
@@ -100,12 +102,12 @@ const AjaxFilter = {
},
destroy: function destroy() {
- if (this.timeout)clearTimeout(this.timeout);
+ if (this.timeout) clearTimeout(this.timeout);
this.destroyed = true;
this.hook.trigger.removeEventListener('keydown.dl', this.eventWrapper.debounceTrigger);
this.hook.trigger.removeEventListener('focus', this.eventWrapper.debounceTrigger);
- }
+ },
};
export default AjaxFilter;
diff --git a/app/assets/javascripts/droplab/plugins/filter.js b/app/assets/javascripts/droplab/plugins/filter.js
index 404d707cf7a..6f1dc252d24 100644
--- a/app/assets/javascripts/droplab/plugins/filter.js
+++ b/app/assets/javascripts/droplab/plugins/filter.js
@@ -1,7 +1,7 @@
/* eslint-disable */
const Filter = {
- keydown: function(e){
+ keydown: function(e) {
if (this.destroyed) return;
var hiddenCount = 0;
@@ -14,14 +14,14 @@ const Filter = {
var matches = [];
var filterFunction;
// will only work on dynamically set data
- if(!data){
+ if (!data) {
return;
}
if (config && config.filterFunction && typeof config.filterFunction === 'function') {
filterFunction = config.filterFunction;
} else {
- filterFunction = function(o){
+ filterFunction = function(o) {
// cheap string search
o.droplab_hidden = o[config.template].toLowerCase().indexOf(value) === -1;
return o;
@@ -47,20 +47,23 @@ const Filter = {
},
debounceKeydown: function debounceKeydown(e) {
- if ([
- 13, // enter
- 16, // shift
- 17, // ctrl
- 18, // alt
- 20, // caps lock
- 37, // left arrow
- 38, // up arrow
- 39, // right arrow
- 40, // down arrow
- 91, // left window
- 92, // right window
- 93, // select
- ].indexOf(e.detail.which || e.detail.keyCode) > -1) return;
+ if (
+ [
+ 13, // enter
+ 16, // shift
+ 17, // ctrl
+ 18, // alt
+ 20, // caps lock
+ 37, // left arrow
+ 38, // up arrow
+ 39, // right arrow
+ 40, // down arrow
+ 91, // left window
+ 92, // right window
+ 93, // select
+ ].indexOf(e.detail.which || e.detail.keyCode) > -1
+ )
+ return;
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(this.keydown.bind(this, e), 200);
@@ -87,7 +90,7 @@ const Filter = {
this.hook.trigger.removeEventListener('keydown.dl', this.eventWrapper.debounceKeydown);
this.hook.trigger.removeEventListener('mousedown.dl', this.eventWrapper.debounceKeydown);
- }
+ },
};
export default Filter;
diff --git a/app/assets/javascripts/droplab/plugins/input_setter.js b/app/assets/javascripts/droplab/plugins/input_setter.js
index d01fbc5830d..6cfc738a1e3 100644
--- a/app/assets/javascripts/droplab/plugins/input_setter.js
+++ b/app/assets/javascripts/droplab/plugins/input_setter.js
@@ -36,8 +36,8 @@ const InputSetter = {
const inputAttribute = config.inputAttribute;
if (input.hasAttribute(inputAttribute)) return input.setAttribute(inputAttribute, newValue);
- if (input.tagName === 'INPUT') return input.value = newValue;
- return input.textContent = newValue;
+ if (input.tagName === 'INPUT') return (input.value = newValue);
+ return (input.textContent = newValue);
},
destroy() {
diff --git a/app/assets/javascripts/droplab/utils.js b/app/assets/javascripts/droplab/utils.js
index bfe056a0fcc..5272778ce2d 100644
--- a/app/assets/javascripts/droplab/utils.js
+++ b/app/assets/javascripts/droplab/utils.js
@@ -5,7 +5,12 @@ import { DATA_TRIGGER, DATA_DROPDOWN, TEMPLATE_REGEX } from './constants';
const utils = {
toCamelCase(attr) {
- return this.camelize(attr.split('-').slice(1).join(' '));
+ return this.camelize(
+ attr
+ .split('-')
+ .slice(1)
+ .join(' '),
+ );
},
template(templateString, data) {
@@ -17,9 +22,11 @@ const utils = {
},
camelize(str) {
- return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => {
- return index === 0 ? letter.toLowerCase() : letter.toUpperCase();
- }).replace(/\s+/g, '');
+ return str
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => {
+ return index === 0 ? letter.toLowerCase() : letter.toUpperCase();
+ })
+ .replace(/\s+/g, '');
},
closest(thisTag, stopTag) {
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 5528ad9f38d..9987fbcb6a7 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -1,12 +1,25 @@
import $ from 'jquery';
import Dropzone from 'dropzone';
import _ from 'underscore';
-import './preview_markdown';
+import './behaviors/preview_markdown';
import csrf from './lib/utils/csrf';
import axios from './lib/utils/axios_utils';
Dropzone.autoDiscover = false;
+/**
+ * Return the error message string from the given response.
+ *
+ * @param {String|Object} res
+ */
+function getErrorMessage(res) {
+ if (!res || _.isString(res)) {
+ return res;
+ }
+
+ return res.message;
+}
+
export default function dropzoneInput(form) {
const divHover = '<div class="div-dropzone-hover"></div>';
const iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
@@ -18,7 +31,7 @@ export default function dropzoneInput(form) {
const $uploadingErrorContainer = form.find('.uploading-error-container');
const $uploadingErrorMessage = form.find('.uploading-error-message');
const $uploadingProgressContainer = form.find('.uploading-progress-container');
- const uploadsPath = window.uploads_path || null;
+ const uploadsPath = form.data('uploads-path') || window.uploads_path || null;
const maxFileSize = gon.max_file_size || 10;
const formTextarea = form.find('.js-gfm-input');
let handlePaste;
@@ -42,7 +55,7 @@ export default function dropzoneInput(form) {
if (!uploadsPath) {
$formDropzone.addClass('js-invalid-dropzone');
- return;
+ return null;
}
const dropzone = $formDropzone.dropzone({
@@ -84,9 +97,7 @@ export default function dropzoneInput(form) {
// xhr object (xhr.responseText is error message).
// On error we hide the 'Attach' and 'Cancel' buttons
// and show an error.
-
- // If there's xhr error message, let's show it instead of dropzone's one.
- const message = xhr ? xhr.responseText : errorMessage;
+ const message = getErrorMessage(errorMessage || xhr.responseText);
$uploadingErrorContainer.removeClass('hide');
$uploadingErrorMessage.html(message);
@@ -125,7 +136,7 @@ export default function dropzoneInput(form) {
// removeAllFiles(true) stops uploading files (if any)
// and remove them from dropzone files queue.
- $cancelButton.on('click', (e) => {
+ $cancelButton.on('click', e => {
e.preventDefault();
e.stopPropagation();
Dropzone.forElement($formDropzone.get(0)).removeAllFiles(true);
@@ -135,8 +146,10 @@ export default function dropzoneInput(form) {
// clear dropzone files queue, change status of failed files to undefined,
// and add that files to the dropzone files queue again.
// addFile() adds file to dropzone files queue and upload it.
- $retryLink.on('click', (e) => {
- const dropzoneInstance = Dropzone.forElement(e.target.closest('.js-main-target-form').querySelector('.div-dropzone'));
+ $retryLink.on('click', e => {
+ const dropzoneInstance = Dropzone.forElement(
+ e.target.closest('.js-main-target-form').querySelector('.div-dropzone'),
+ );
const failedFiles = dropzoneInstance.files;
e.preventDefault();
@@ -145,7 +158,7 @@ export default function dropzoneInput(form) {
// uploading of files that are being uploaded at the moment.
dropzoneInstance.removeAllFiles(true);
- failedFiles.map((failedFile) => {
+ failedFiles.map(failedFile => {
const file = failedFile;
if (file.status === Dropzone.ERROR) {
@@ -157,7 +170,7 @@ export default function dropzoneInput(form) {
});
});
// eslint-disable-next-line consistent-return
- handlePaste = (event) => {
+ handlePaste = event => {
const pasteEvent = event.originalEvent;
if (pasteEvent.clipboardData && pasteEvent.clipboardData.items) {
const image = isImage(pasteEvent);
@@ -171,7 +184,7 @@ export default function dropzoneInput(form) {
}
};
- isImage = (data) => {
+ isImage = data => {
let i = 0;
while (i < data.clipboardData.items.length) {
const item = data.clipboardData.items[i];
@@ -192,8 +205,12 @@ export default function dropzoneInput(form) {
const caretStart = textarea.selectionStart;
const caretEnd = textarea.selectionEnd;
const textEnd = $(child).val().length;
- const beforeSelection = $(child).val().substring(0, caretStart);
- const afterSelection = $(child).val().substring(caretEnd, textEnd);
+ const beforeSelection = $(child)
+ .val()
+ .substring(0, caretStart);
+ const afterSelection = $(child)
+ .val()
+ .substring(caretEnd, textEnd);
$(child).val(beforeSelection + formattedText + afterSelection);
textarea.setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length);
textarea.style.height = `${textarea.scrollHeight}px`;
@@ -201,11 +218,11 @@ export default function dropzoneInput(form) {
return formTextarea.trigger('input');
};
- addFileToForm = (path) => {
+ addFileToForm = path => {
$(form).append(`<input type="hidden" name="files[]" value="${_.escape(path)}">`);
};
- getFilename = (e) => {
+ getFilename = e => {
let value;
if (window.clipboardData && window.clipboardData.getData) {
value = window.clipboardData.getData('Text');
@@ -220,7 +237,7 @@ export default function dropzoneInput(form) {
const closeSpinner = () => $uploadingProgressContainer.addClass('hide');
- const showError = (message) => {
+ const showError = message => {
$uploadingErrorContainer.removeClass('hide');
$uploadingErrorMessage.html(message);
};
@@ -241,14 +258,15 @@ export default function dropzoneInput(form) {
showSpinner();
closeAlertMessage();
- axios.post(uploadsPath, formData)
+ axios
+ .post(uploadsPath, formData)
.then(({ data }) => {
const md = data.link.markdown;
insertToTextArea(filename, md);
closeSpinner();
})
- .catch((e) => {
+ .catch(e => {
showError(e.response.data.message);
closeSpinner();
});
@@ -256,7 +274,8 @@ export default function dropzoneInput(form) {
updateAttachingMessage = (files, messageContainer) => {
let attachingMessage;
- const filesCount = files.filter(file => file.status === 'uploading' || file.status === 'queued').length;
+ const filesCount = files.filter(file => file.status === 'uploading' || file.status === 'queued')
+ .length;
// Dinamycally change uploading files text depending on files number in
// dropzone files queue.
@@ -271,7 +290,12 @@ export default function dropzoneInput(form) {
form.find('.markdown-selector').click(function onMarkdownClick(e) {
e.preventDefault();
- $(this).closest('.gfm-form').find('.div-dropzone').click();
+ $(this)
+ .closest('.gfm-form')
+ .find('.div-dropzone')
+ .click();
formTextarea.focus();
});
+
+ return Dropzone.forElement($formDropzone.get(0));
}
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index 8abd8bc581a..dbfcf8cc921 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -3,8 +3,8 @@ import Pikaday from 'pikaday';
import dateFormat from 'dateformat';
import { __ } from '~/locale';
import axios from './lib/utils/axios_utils';
-import { timeFor } from './lib/utils/datetime_utility';
-import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
+import { timeFor, parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
+import boardsStore from './boards/stores/boards_store';
class DueDateSelect {
constructor({ $dropdown, $loading } = {}) {
@@ -58,7 +58,7 @@ class DueDateSelect {
$dueDateInput.val(calendar.toString(dateText));
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
- gl.issueBoards.BoardsStore.detail.issue.dueDate = $dueDateInput.val();
+ boardsStore.detail.issue.dueDate = $dueDateInput.val();
this.updateIssueBoardIssue();
} else {
this.saveDueDate(true);
@@ -79,7 +79,7 @@ class DueDateSelect {
calendar.setDate(null);
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
- gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
+ boardsStore.detail.issue.dueDate = '';
this.updateIssueBoardIssue();
} else {
$(`input[name='${this.fieldName}']`).val('');
@@ -123,7 +123,7 @@ class DueDateSelect {
this.$loading.fadeOut();
};
- gl.issueBoards.BoardsStore.detail.issue
+ boardsStore.detail.issue
.update(this.$dropdown.attr('data-issue-update'))
.then(fadeOutLoader)
.catch(fadeOutLoader);
diff --git a/app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js b/app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js
index e9defb62cf8..c5f9fcf6358 100644
--- a/app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js
+++ b/app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js
@@ -13,9 +13,11 @@ const rainbowCodePoint = 127752; // parseInt('1F308', 16)
function isRainbowFlagEmoji(emojiUnicode) {
const characters = Array.from(emojiUnicode);
// Length 4 because flags are made of 2 characters which are surrogate pairs
- return emojiUnicode.length === 4 &&
+ return (
+ emojiUnicode.length === 4 &&
characters[0].codePointAt(0) === baseFlagCodePoint &&
- characters[1].codePointAt(0) === rainbowCodePoint;
+ characters[1].codePointAt(0) === rainbowCodePoint
+ );
}
// Chrome <57 renders keycaps oddly
@@ -26,22 +28,28 @@ function isKeycapEmoji(emojiUnicode) {
}
// Check for a skin tone variation emoji which aren't always supported
-const tone1 = 127995;// parseInt('1F3FB', 16)
-const tone5 = 127999;// parseInt('1F3FF', 16)
+const tone1 = 127995; // parseInt('1F3FB', 16)
+const tone5 = 127999; // parseInt('1F3FF', 16)
function isSkinToneComboEmoji(emojiUnicode) {
- return emojiUnicode.length > 2 && Array.from(emojiUnicode).some((char) => {
- const cp = char.codePointAt(0);
- return cp >= tone1 && cp <= tone5;
- });
+ return (
+ emojiUnicode.length > 2 &&
+ Array.from(emojiUnicode).some(char => {
+ const cp = char.codePointAt(0);
+ return cp >= tone1 && cp <= tone5;
+ })
+ );
}
// macOS supports most skin tone emoji's but
// doesn't support the skin tone versions of horse racing
-const horseRacingCodePoint = 127943;// parseInt('1F3C7', 16)
+const horseRacingCodePoint = 127943; // parseInt('1F3C7', 16)
function isHorceRacingSkinToneComboEmoji(emojiUnicode) {
const firstCharacter = Array.from(emojiUnicode)[0];
- return firstCharacter && firstCharacter.codePointAt(0) === horseRacingCodePoint &&
- isSkinToneComboEmoji(emojiUnicode);
+ return (
+ firstCharacter &&
+ firstCharacter.codePointAt(0) === horseRacingCodePoint &&
+ isSkinToneComboEmoji(emojiUnicode)
+ );
}
// Check for `family_*`, `kiss_*`, `couple_*`
@@ -52,7 +60,7 @@ const personEndCodePoint = 128105; // parseInt('1F469', 16)
function isPersonZwjEmoji(emojiUnicode) {
let hasPersonEmoji = false;
let hasZwj = false;
- Array.from(emojiUnicode).forEach((character) => {
+ Array.from(emojiUnicode).forEach(character => {
const cp = character.codePointAt(0);
if (cp === zwj) {
hasZwj = true;
@@ -80,10 +88,7 @@ function checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) {
// in `isEmojiUnicodeSupported` logic
function checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) {
const isSkinToneResult = isSkinToneComboEmoji(emojiUnicode);
- return (
- (unicodeSupportMap.skinToneModifier && isSkinToneResult) ||
- !isSkinToneResult
- );
+ return (unicodeSupportMap.skinToneModifier && isSkinToneResult) || !isSkinToneResult;
}
// Helper func so we don't have to run `isHorceRacingSkinToneComboEmoji` twice
@@ -91,8 +96,7 @@ function checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) {
function checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) {
const isHorseRacingSkinToneResult = isHorceRacingSkinToneComboEmoji(emojiUnicode);
return (
- (unicodeSupportMap.horseRacing && isHorseRacingSkinToneResult) ||
- !isHorseRacingSkinToneResult
+ (unicodeSupportMap.horseRacing && isHorseRacingSkinToneResult) || !isHorseRacingSkinToneResult
);
}
@@ -100,10 +104,7 @@ function checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnico
// in `isEmojiUnicodeSupported` logic
function checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode) {
const isPersonZwjResult = isPersonZwjEmoji(emojiUnicode);
- return (
- (unicodeSupportMap.personZwj && isPersonZwjResult) ||
- !isPersonZwjResult
- );
+ return (unicodeSupportMap.personZwj && isPersonZwjResult) || !isPersonZwjResult;
}
// Takes in a support map and determines whether
@@ -111,16 +112,20 @@ function checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode) {
//
// Combines all the edge case tests into a one-stop shop method
function isEmojiUnicodeSupported(unicodeSupportMap = {}, emojiUnicode, unicodeVersion) {
- const isOlderThanChrome57 = unicodeSupportMap.meta && unicodeSupportMap.meta.isChrome &&
+ const isOlderThanChrome57 =
+ unicodeSupportMap.meta &&
+ unicodeSupportMap.meta.isChrome &&
unicodeSupportMap.meta.chromeVersion < 57;
// For comments about each scenario, see the comments above each individual respective function
- return unicodeSupportMap[unicodeVersion] &&
+ return (
+ unicodeSupportMap[unicodeVersion] &&
!(isOlderThanChrome57 && isKeycapEmoji(emojiUnicode)) &&
checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) &&
checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) &&
checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) &&
- checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode);
+ checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode)
+ );
}
export {
diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue
index 9aa224fa407..6c0b5c1f427 100644
--- a/app/assets/javascripts/environments/components/container.vue
+++ b/app/assets/javascripts/environments/components/container.vue
@@ -1,52 +1,52 @@
<script>
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
- import tablePagination from '../../vue_shared/components/table_pagination.vue';
- import environmentTable from '../components/environments_table.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+import tablePagination from '../../vue_shared/components/table_pagination.vue';
+import environmentTable from '../components/environments_table.vue';
- export default {
- components: {
- environmentTable,
- loadingIcon,
- tablePagination,
+export default {
+ components: {
+ environmentTable,
+ tablePagination,
+ GlLoadingIcon,
+ },
+ props: {
+ isLoading: {
+ type: Boolean,
+ required: true,
},
- props: {
- isLoading: {
- type: Boolean,
- required: true,
- },
- environments: {
- type: Array,
- required: true,
- },
- pagination: {
- type: Object,
- required: true,
- },
- canCreateDeployment: {
- type: Boolean,
- required: true,
- },
- canReadEnvironment: {
- type: Boolean,
- required: true,
- },
+ environments: {
+ type: Array,
+ required: true,
},
- methods: {
- onChangePage(page) {
- this.$emit('onChangePage', page);
- },
+ pagination: {
+ type: Object,
+ required: true,
},
- };
+ canCreateDeployment: {
+ type: Boolean,
+ required: true,
+ },
+ canReadEnvironment: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ methods: {
+ onChangePage(page) {
+ this.$emit('onChangePage', page);
+ },
+ },
+};
</script>
<template>
<div class="environments-container">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="3"
class="prepend-top-default"
label="Loading environments"
- size="3"
/>
<slot name="emptyState"></slot>
diff --git a/app/assets/javascripts/environments/components/empty_state.vue b/app/assets/javascripts/environments/components/empty_state.vue
index 00e63c3467a..2360a52645b 100644
--- a/app/assets/javascripts/environments/components/empty_state.vue
+++ b/app/assets/javascripts/environments/components/empty_state.vue
@@ -1,44 +1,45 @@
<script>
- export default {
- name: 'EnvironmentsEmptyState',
- props: {
- newPath: {
- type: String,
- required: true,
- },
- canCreateEnvironment: {
- type: Boolean,
- required: true,
- },
- helpPath: {
- type: String,
- required: true,
- },
+export default {
+ name: 'EnvironmentsEmptyState',
+ props: {
+ newPath: {
+ type: String,
+ required: true,
},
- };
+ canCreateEnvironment: {
+ type: Boolean,
+ required: true,
+ },
+ helpPath: {
+ type: String,
+ required: true,
+ },
+ },
+};
</script>
<template>
- <div class="blank-state-row">
- <div class="blank-state-center">
- <h2 class="blank-state-title js-blank-state-title">
- {{ s__("Environments|You don't have any environments right now.") }}
- </h2>
+ <div class="empty-state">
+ <div class="text-content">
+ <h4 class="blank-state-title js-blank-state-title">
+ {{ s__("Environments|You don't have any environments right now") }}
+ </h4>
<p class="blank-state-text">
{{ s__(`Environments|Environments are places where
-code gets deployed, such as staging or production.`) }}
- <br />
+ code gets deployed, such as staging or production.`) }}
<a :href="helpPath">
{{ s__("Environments|Read more about environments") }}
</a>
</p>
- <a
- v-if="canCreateEnvironment"
- :href="newPath"
- class="btn btn-create js-new-environment-button"
- >
- {{ s__("Environments|New environment") }}
- </a>
+ <div class="text-center">
+ <a
+ v-if="canCreateEnvironment"
+ :href="newPath"
+ class="btn btn-success js-new-environment-button"
+ >
+ {{ s__("Environments|New environment") }}
+ </a>
+ </div>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue
index 63d83e307ee..b849772a82e 100644
--- a/app/assets/javascripts/environments/components/environment_actions.vue
+++ b/app/assets/javascripts/environments/components/environment_actions.vue
@@ -1,16 +1,18 @@
<script>
+import { s__, sprintf } from '~/locale';
+import { formatTime } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
+import { GlLoadingIcon } from '@gitlab/ui';
export default {
directives: {
tooltip,
},
components: {
- loadingIcon,
Icon,
+ GlLoadingIcon,
},
props: {
actions: {
@@ -30,10 +32,24 @@ export default {
},
},
methods: {
- onClickAction(endpoint) {
+ onClickAction(action) {
+ if (action.scheduledAt) {
+ const confirmationMessage = sprintf(
+ s__(
+ "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes.",
+ ),
+ { jobName: action.name },
+ );
+ // https://gitlab.com/gitlab-org/gitlab-ce/issues/52156
+ // eslint-disable-next-line no-alert
+ if (!window.confirm(confirmationMessage)) {
+ return;
+ }
+ }
+
this.isLoading = true;
- eventHub.$emit('postAction', { endpoint });
+ eventHub.$emit('postAction', { endpoint: action.playPath });
},
isActionDisabled(action) {
@@ -43,6 +59,11 @@ export default {
return !action.playable;
},
+
+ remainingTime(action) {
+ const remainingMilliseconds = new Date(action.scheduledAt).getTime() - Date.now();
+ return formatTime(Math.max(0, remainingMilliseconds));
+ },
},
};
</script>
@@ -56,18 +77,16 @@ export default {
:aria-label="title"
:disabled="isLoading"
type="button"
- class="dropdown btn btn-default dropdown-new js-dropdown-play-icon-container"
+ class="dropdown btn btn-default dropdown-new js-environment-actions-dropdown"
data-container="body"
data-toggle="dropdown"
>
<span>
<icon name="play" />
- <i
- class="fa fa-caret-down"
- aria-hidden="true"
- >
- </i>
- <loading-icon v-if="isLoading" />
+ <icon
+ name="chevron-down"
+ />
+ <gl-loading-icon v-if="isLoading" />
</span>
</button>
@@ -79,12 +98,19 @@ export default {
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
type="button"
- class="js-manual-action-link no-btn btn"
- @click="onClickAction(action.play_path)"
+ class="js-manual-action-link no-btn btn d-flex align-items-center"
+ @click="onClickAction(action)"
>
- <span>
+ <span class="flex-fill">
{{ action.name }}
</span>
+ <span
+ v-if="action.scheduledAt"
+ class="text-secondary"
+ >
+ <icon name="clock" />
+ {{ remainingTime(action) }}
+ </span>
</button>
</li>
</ul>
diff --git a/app/assets/javascripts/environments/components/environment_external_url.vue b/app/assets/javascripts/environments/components/environment_external_url.vue
index 7446196de13..af537cfb991 100644
--- a/app/assets/javascripts/environments/components/environment_external_url.vue
+++ b/app/assets/javascripts/environments/components/environment_external_url.vue
@@ -1,7 +1,7 @@
<script>
+import { GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
-import tooltip from '../../vue_shared/directives/tooltip';
-import { s__ } from '../../locale';
+import { s__ } from '~/locale';
/**
* Renders the external url link in environments table.
@@ -11,7 +11,7 @@ export default {
Icon,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
externalUrl: {
@@ -28,12 +28,11 @@ export default {
</script>
<template>
<a
- v-tooltip
+ v-gl-tooltip
:title="title"
:aria-label="title"
:href="externalUrl"
class="btn external-url"
- data-container="body"
target="_blank"
rel="noopener noreferrer nofollow"
>
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 11e3b781e5a..34d94490533 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -1,9 +1,10 @@
<script>
import Timeago from 'timeago.js';
import _ from 'underscore';
-import tooltip from '~/vue_shared/directives/tooltip';
+import { GlTooltipDirective } from '@gitlab/ui';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { humanize } from '~/lib/utils/text_utility';
+import Icon from '~/vue_shared/components/icon.vue';
import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue';
import StopComponent from './environment_stop.vue';
@@ -12,9 +13,10 @@ import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring.vue';
import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
/**
- * Envrionment Item Component
+ * Environment Item Component
*
* Renders a table row for each environment.
*/
@@ -24,6 +26,7 @@ export default {
components: {
UserAvatarLink,
CommitComponent,
+ Icon,
ActionsComponent,
ExternalUrlComponent,
StopComponent,
@@ -33,7 +36,7 @@ export default {
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
@@ -58,7 +61,7 @@ export default {
computed: {
/**
- * Verifies if `last_deployment` key exists in the current Envrionment.
+ * Verifies if `last_deployment` key exists in the current Environment.
* This key is required to render most of the html - this method works has
* an helper.
*
@@ -72,21 +75,6 @@ export default {
},
/**
- * Verifies is the given environment has manual actions.
- * Used to verify if we should render them or nor.
- *
- * @returns {Boolean|Undefined}
- */
- hasManualActions() {
- return (
- this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.manual_actions &&
- this.model.last_deployment.manual_actions.length > 0
- );
- },
-
- /**
* Checkes whether the environment is protected.
* (`is_protected` currently only set in EE)
*
@@ -152,23 +140,20 @@ export default {
return '';
},
- /**
- * Returns the manual actions with the name parsed.
- *
- * @returns {Array.<Object>|Undefined}
- */
- manualActions() {
- if (this.hasManualActions) {
- return this.model.last_deployment.manual_actions.map(action => {
- const parsedAction = {
- name: humanize(action.name),
- play_path: action.play_path,
- playable: action.playable,
- };
- return parsedAction;
- });
+ actions() {
+ if (!this.model || !this.model.last_deployment || !this.canCreateDeployment) {
+ return [];
}
- return [];
+
+ const { manualActions, scheduledActions } = convertObjectPropsToCamelCase(
+ this.model.last_deployment,
+ { deep: true },
+ );
+ const combinedActions = (manualActions || []).concat(scheduledActions || []);
+ return combinedActions.map(action => ({
+ ...action,
+ name: humanize(action.name),
+ }));
},
/**
@@ -441,13 +426,17 @@ export default {
displayEnvironmentActions() {
return (
- this.hasManualActions ||
+ this.actions.length > 0 ||
this.externalURL ||
this.monitoringUrl ||
this.canStopEnvironment ||
this.canRetry
);
},
+
+ folderIconName() {
+ return this.model.isOpen ? 'chevron-down' : 'chevron-right';
+ },
},
methods: {
@@ -466,7 +455,9 @@ export default {
class="gl-responsive-table-row"
role="row">
<div
- class="table-section section-wrap section-15"
+ v-gl-tooltip
+ :title="model.name"
+ class="table-section section-wrap section-15 text-truncate"
role="gridcell"
>
<div
@@ -480,9 +471,8 @@ export default {
v-if="!model.isFolder"
class="environment-name table-mobile-content">
<a
- v-tooltip
+ class="qa-environment-link"
:href="environmentPath"
- :title="model.name"
>
{{ model.name }}
</a>
@@ -493,27 +483,15 @@ export default {
role="button"
@click="onClickFolder">
- <span class="folder-icon">
- <i
- v-show="model.isOpen"
- class="fa fa-caret-down"
- aria-hidden="true"
- >
- </i>
- <i
- v-show="!model.isOpen"
- class="fa fa-caret-right"
- aria-hidden="true"
- >
- </i>
- </span>
+ <icon
+ :name="folderIconName"
+ class="folder-icon"
+ />
- <span class="folder-icon">
- <i
- class="fa fa-folder"
- aria-hidden="true">
- </i>
- </span>
+ <icon
+ name="folder"
+ class="folder-icon"
+ />
<span>
{{ model.folderName }}
@@ -624,8 +602,8 @@ export default {
/>
<actions-component
- v-if="hasManualActions && canCreateDeployment"
- :actions="manualActions"
+ v-if="actions.length > 0"
+ :actions="actions"
/>
<terminal-button-component
diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue
index ccc8419ca6d..ae4f07a71cd 100644
--- a/app/assets/javascripts/environments/components/environment_monitoring.vue
+++ b/app/assets/javascripts/environments/components/environment_monitoring.vue
@@ -2,15 +2,16 @@
/**
* Renders the Monitoring (Metrics) link in environments table.
*/
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
-import tooltip from '../../vue_shared/directives/tooltip';
export default {
components: {
Icon,
+ GlButton,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
monitoringUrl: {
@@ -26,15 +27,15 @@ export default {
};
</script>
<template>
- <a
- v-tooltip
+ <gl-button
+ v-gl-tooltip
:href="monitoringUrl"
:title="title"
:aria-label="title"
- class="btn monitoring-url d-none d-sm-none d-md-block"
- data-container="body"
+ class="monitoring-url d-none d-sm-none d-md-block"
rel="noopener noreferrer nofollow"
+ variant="default"
>
<icon name="chart" />
- </a>
+ </gl-button>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue
index 4deeef4beb9..46c77dca871 100644
--- a/app/assets/javascripts/environments/components/environment_rollback.vue
+++ b/app/assets/javascripts/environments/components/environment_rollback.vue
@@ -6,21 +6,18 @@
* Makes a post request when the button is clicked.
*/
import { s__ } from '~/locale';
+import { GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
-import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '../event_hub';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
components: {
Icon,
- LoadingIcon,
+ GlLoadingIcon,
},
-
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
-
props: {
retryUrl: {
type: String,
@@ -40,7 +37,9 @@ export default {
computed: {
title() {
- return this.isLastDeployment ? s__('Environments|Re-deploy to environment') : s__('Environments|Rollback environment');
+ return this.isLastDeployment
+ ? s__('Environments|Re-deploy to environment')
+ : s__('Environments|Rollback environment');
},
},
@@ -55,21 +54,21 @@ export default {
</script>
<template>
<button
- v-tooltip
+ v-gl-tooltip
:disabled="isLoading"
:title="title"
type="button"
class="btn d-none d-sm-none d-md-block"
@click="onClick"
>
-
<icon
v-if="isLastDeployment"
- name="repeat" />
+ name="repeat"
+ />
<icon
v-else
- name="redo"/>
-
- <loading-icon v-if="isLoading" />
+ name="redo"
+ />
+ <gl-loading-icon v-if="isLoading" />
</button>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue
index a814b3405f5..efecc128368 100644
--- a/app/assets/javascripts/environments/components/environment_stop.vue
+++ b/app/assets/javascripts/environments/components/environment_stop.vue
@@ -5,49 +5,42 @@
*/
import $ from 'jquery';
+import { GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
-import tooltip from '../../vue_shared/directives/tooltip';
export default {
components: {
Icon,
LoadingButton,
},
-
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
-
props: {
environment: {
type: Object,
required: true,
},
},
-
data() {
return {
isLoading: false,
};
},
-
computed: {
title() {
return s__('Environments|Stop environment');
},
},
-
mounted() {
eventHub.$on('stopEnvironment', this.onStopEnvironment);
},
-
beforeDestroy() {
eventHub.$off('stopEnvironment', this.onStopEnvironment);
},
-
methods: {
onClick() {
$(this.$el).tooltip('dispose');
@@ -63,12 +56,11 @@ export default {
</script>
<template>
<loading-button
- v-tooltip
+ v-gl-tooltip
:loading="isLoading"
:title="title"
:aria-label="title"
container-class="btn btn-danger d-none d-sm-none d-md-block"
- data-container="body"
data-toggle="modal"
data-target="#stop-environment-modal"
@click="onClick"
diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.vue b/app/assets/javascripts/environments/components/environment_terminal_button.vue
index 350417e5ad0..83727caad16 100644
--- a/app/assets/javascripts/environments/components/environment_terminal_button.vue
+++ b/app/assets/javascripts/environments/components/environment_terminal_button.vue
@@ -3,15 +3,15 @@
* Renders a terminal button to open a web terminal.
* Used in environments table.
*/
+import { GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
-import tooltip from '../../vue_shared/directives/tooltip';
export default {
components: {
Icon,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
terminalPath: {
@@ -29,12 +29,11 @@ export default {
</script>
<template>
<a
- v-tooltip
+ v-gl-tooltip
:title="title"
:aria-label="title"
:href="terminalPath"
class="btn terminal-button d-none d-sm-none d-md-block"
- data-container="body"
>
<icon name="terminal" />
</a>
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index 8efdfb8abe0..557b2062c64 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -1,94 +1,92 @@
<script>
- import Flash from '../../flash';
- import { s__ } from '../../locale';
- import emptyState from './empty_state.vue';
- import eventHub from '../event_hub';
- import environmentsMixin from '../mixins/environments_mixin';
- import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
- import StopEnvironmentModal from './stop_environment_modal.vue';
+import Flash from '../../flash';
+import { s__ } from '../../locale';
+import emptyState from './empty_state.vue';
+import eventHub from '../event_hub';
+import environmentsMixin from '../mixins/environments_mixin';
+import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
+import StopEnvironmentModal from './stop_environment_modal.vue';
- export default {
- components: {
- emptyState,
- StopEnvironmentModal,
- },
+export default {
+ components: {
+ emptyState,
+ StopEnvironmentModal,
+ },
- mixins: [
- CIPaginationMixin,
- environmentsMixin,
- ],
+ mixins: [CIPaginationMixin, environmentsMixin],
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- canCreateEnvironment: {
- type: Boolean,
- required: true,
- },
- canCreateDeployment: {
- type: Boolean,
- required: true,
- },
- canReadEnvironment: {
- type: Boolean,
- required: true,
- },
- cssContainerClass: {
- type: String,
- required: true,
- },
- newEnvironmentPath: {
- type: String,
- required: true,
- },
- helpPagePath: {
- type: String,
- required: true,
- },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
},
-
- created() {
- eventHub.$on('toggleFolder', this.toggleFolder);
+ canCreateEnvironment: {
+ type: Boolean,
+ required: true,
},
-
- beforeDestroy() {
- eventHub.$off('toggleFolder');
+ canCreateDeployment: {
+ type: Boolean,
+ required: true,
+ },
+ canReadEnvironment: {
+ type: Boolean,
+ required: true,
+ },
+ cssContainerClass: {
+ type: String,
+ required: true,
+ },
+ newEnvironmentPath: {
+ type: String,
+ required: true,
},
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+
+ created() {
+ eventHub.$on('toggleFolder', this.toggleFolder);
+ },
- methods: {
- toggleFolder(folder) {
- this.store.toggleFolder(folder);
+ beforeDestroy() {
+ eventHub.$off('toggleFolder');
+ },
- if (!folder.isOpen) {
- this.fetchChildEnvironments(folder, true);
- }
- },
+ methods: {
+ toggleFolder(folder) {
+ this.store.toggleFolder(folder);
- fetchChildEnvironments(folder, showLoader = false) {
- this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader);
+ if (!folder.isOpen) {
+ this.fetchChildEnvironments(folder, true);
+ }
+ },
- this.service.getFolderContent(folder.folder_path)
- .then(response => this.store.setfolderContent(folder, response.data.environments))
- .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false))
- .catch(() => {
- Flash(s__('Environments|An error occurred while fetching the environments.'));
- this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false);
- });
- },
+ fetchChildEnvironments(folder, showLoader = false) {
+ this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader);
+
+ this.service
+ .getFolderContent(folder.folder_path)
+ .then(response => this.store.setfolderContent(folder, response.data.environments))
+ .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false))
+ .catch(() => {
+ Flash(s__('Environments|An error occurred while fetching the environments.'));
+ this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false);
+ });
+ },
- successCallback(resp) {
- this.saveData(resp);
+ successCallback(resp) {
+ this.saveData(resp);
- // We need to verify if any folder is open to also update it
- const openFolders = this.store.getOpenFolders();
- if (openFolders.length) {
- openFolders.forEach(folder => this.fetchChildEnvironments(folder));
- }
- },
+ // We need to verify if any folder is open to also update it
+ const openFolders = this.store.getOpenFolders();
+ if (openFolders.length) {
+ openFolders.forEach(folder => this.fetchChildEnvironments(folder));
+ }
},
- };
+ },
+};
</script>
<template>
<div :class="cssContainerClass">
@@ -107,7 +105,7 @@
>
<a
:href="newEnvironmentPath"
- class="btn btn-create"
+ class="btn btn-success"
>
{{ s__("Environments|New environment") }}
</a>
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index 016e9f7c7b3..4eae4eec394 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -2,13 +2,13 @@
/**
* Render environments table.
*/
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import environmentItem from './environment_item.vue';
export default {
components: {
environmentItem,
- loadingIcon,
+ GlLoadingIcon,
},
props: {
@@ -85,10 +85,10 @@ export default {
:model="model">
<div
is="environment-item"
+ :key="`environment-item-${i}`"
:model="model"
:can-create-deployment="canCreateDeployment"
:can-read-environment="canReadEnvironment"
- :key="`environment-item-${i}`"
/>
<template
@@ -97,17 +97,17 @@ export default {
<div
v-if="model.isLoadingFolderContent"
:key="`loading-item-${i}`">
- <loading-icon size="2" />
+ <gl-loading-icon :size="2" />
</div>
<template v-else>
<div
is="environment-item"
v-for="(children, index) in model.children"
+ :key="`env-item-${i}-${index}`"
:model="children"
:can-create-deployment="canCreateDeployment"
:can-read-environment="canReadEnvironment"
- :key="`env-item-${i}-${index}`"
/>
<div :key="`sub-div-${i}`">
diff --git a/app/assets/javascripts/environments/components/stop_environment_modal.vue b/app/assets/javascripts/environments/components/stop_environment_modal.vue
index 657cc8cd1aa..2ebc7b5b951 100644
--- a/app/assets/javascripts/environments/components/stop_environment_modal.vue
+++ b/app/assets/javascripts/environments/components/stop_environment_modal.vue
@@ -1,7 +1,7 @@
<script>
+import { GlTooltipDirective } from '@gitlab/ui';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
-import tooltip from '~/vue_shared/directives/tooltip';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import eventHub from '../event_hub';
@@ -15,7 +15,7 @@ export default {
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
@@ -67,7 +67,7 @@ export default {
>
Stopping
<span
- v-tooltip
+ v-gl-tooltip
:title="environment.name"
class="text-truncate ml-1 mr-1 flex-fill"
>{{ environment.name }}</span>
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index de0fbdb2e91..f044d31c776 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -5,31 +5,32 @@ import Translate from '../../vue_shared/translate';
Vue.use(Translate);
-export default () => new Vue({
- el: '#environments-folder-list-view',
- components: {
- environmentsFolderApp,
- },
- data() {
- const environmentsData = document.querySelector(this.$options.el).dataset;
+export default () =>
+ new Vue({
+ el: '#environments-folder-list-view',
+ components: {
+ environmentsFolderApp,
+ },
+ data() {
+ const environmentsData = document.querySelector(this.$options.el).dataset;
- return {
- endpoint: environmentsData.endpoint,
- folderName: environmentsData.folderName,
- cssContainerClass: environmentsData.cssClass,
- canCreateDeployment: convertPermissionToBoolean(environmentsData.canCreateDeployment),
- canReadEnvironment: convertPermissionToBoolean(environmentsData.canReadEnvironment),
- };
- },
- render(createElement) {
- return createElement('environments-folder-app', {
- props: {
- endpoint: this.endpoint,
- folderName: this.folderName,
- cssContainerClass: this.cssContainerClass,
- canCreateDeployment: this.canCreateDeployment,
- canReadEnvironment: this.canReadEnvironment,
- },
- });
- },
-});
+ return {
+ endpoint: environmentsData.endpoint,
+ folderName: environmentsData.folderName,
+ cssContainerClass: environmentsData.cssClass,
+ canCreateDeployment: convertPermissionToBoolean(environmentsData.canCreateDeployment),
+ canReadEnvironment: convertPermissionToBoolean(environmentsData.canReadEnvironment),
+ };
+ },
+ render(createElement) {
+ return createElement('environments-folder-app', {
+ props: {
+ endpoint: this.endpoint,
+ folderName: this.folderName,
+ cssContainerClass: this.cssContainerClass,
+ canCreateDeployment: this.canCreateDeployment,
+ canReadEnvironment: this.canReadEnvironment,
+ },
+ });
+ },
+ });
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue
index e69bfa0b2cc..6be4845fe4c 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.vue
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue
@@ -1,46 +1,43 @@
<script>
- import environmentsMixin from '../mixins/environments_mixin';
- import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
- import StopEnvironmentModal from '../components/stop_environment_modal.vue';
+import environmentsMixin from '../mixins/environments_mixin';
+import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
+import StopEnvironmentModal from '../components/stop_environment_modal.vue';
- export default {
- components: {
- StopEnvironmentModal,
- },
+export default {
+ components: {
+ StopEnvironmentModal,
+ },
- mixins: [
- environmentsMixin,
- CIPaginationMixin,
- ],
+ mixins: [environmentsMixin, CIPaginationMixin],
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- folderName: {
- type: String,
- required: true,
- },
- cssContainerClass: {
- type: String,
- required: true,
- },
- canCreateDeployment: {
- type: Boolean,
- required: true,
- },
- canReadEnvironment: {
- type: Boolean,
- required: true,
- },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
+ },
+ folderName: {
+ type: String,
+ required: true,
+ },
+ cssContainerClass: {
+ type: String,
+ required: true,
+ },
+ canCreateDeployment: {
+ type: Boolean,
+ required: true,
+ },
+ canReadEnvironment: {
+ type: Boolean,
+ required: true,
},
- methods: {
- successCallback(resp) {
- this.saveData(resp);
- },
+ },
+ methods: {
+ successCallback(resp) {
+ this.saveData(resp);
},
- };
+ },
+};
</script>
<template>
<div :class="cssContainerClass">
diff --git a/app/assets/javascripts/environments/index.js b/app/assets/javascripts/environments/index.js
index afc4aba6554..5b6833fb15d 100644
--- a/app/assets/javascripts/environments/index.js
+++ b/app/assets/javascripts/environments/index.js
@@ -5,35 +5,36 @@ import Translate from '../vue_shared/translate';
Vue.use(Translate);
-export default () => new Vue({
- el: '#environments-list-view',
- components: {
- environmentsComponent,
- },
- data() {
- const environmentsData = document.querySelector(this.$options.el).dataset;
+export default () =>
+ new Vue({
+ el: '#environments-list-view',
+ components: {
+ environmentsComponent,
+ },
+ data() {
+ const environmentsData = document.querySelector(this.$options.el).dataset;
- return {
- endpoint: environmentsData.environmentsDataEndpoint,
- newEnvironmentPath: environmentsData.newEnvironmentPath,
- helpPagePath: environmentsData.helpPagePath,
- cssContainerClass: environmentsData.cssClass,
- canCreateEnvironment: convertPermissionToBoolean(environmentsData.canCreateEnvironment),
- canCreateDeployment: convertPermissionToBoolean(environmentsData.canCreateDeployment),
- canReadEnvironment: convertPermissionToBoolean(environmentsData.canReadEnvironment),
- };
- },
- render(createElement) {
- return createElement('environments-component', {
- props: {
- endpoint: this.endpoint,
- newEnvironmentPath: this.newEnvironmentPath,
- helpPagePath: this.helpPagePath,
- cssContainerClass: this.cssContainerClass,
- canCreateEnvironment: this.canCreateEnvironment,
- canCreateDeployment: this.canCreateDeployment,
- canReadEnvironment: this.canReadEnvironment,
- },
- });
- },
-});
+ return {
+ endpoint: environmentsData.environmentsDataEndpoint,
+ newEnvironmentPath: environmentsData.newEnvironmentPath,
+ helpPagePath: environmentsData.helpPagePath,
+ cssContainerClass: environmentsData.cssClass,
+ canCreateEnvironment: convertPermissionToBoolean(environmentsData.canCreateEnvironment),
+ canCreateDeployment: convertPermissionToBoolean(environmentsData.canCreateDeployment),
+ canReadEnvironment: convertPermissionToBoolean(environmentsData.canReadEnvironment),
+ };
+ },
+ render(createElement) {
+ return createElement('environments-component', {
+ props: {
+ endpoint: this.endpoint,
+ newEnvironmentPath: this.newEnvironmentPath,
+ helpPagePath: this.helpPagePath,
+ cssContainerClass: this.cssContainerClass,
+ canCreateEnvironment: this.canCreateEnvironment,
+ canCreateDeployment: this.canCreateDeployment,
+ canReadEnvironment: this.canReadEnvironment,
+ },
+ });
+ },
+ });
diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js
index d88624f7f8d..96dc1f07cb9 100644
--- a/app/assets/javascripts/environments/mixins/environments_mixin.js
+++ b/app/assets/javascripts/environments/mixins/environments_mixin.js
@@ -4,27 +4,22 @@
import _ from 'underscore';
import Visibility from 'visibilityjs';
import Poll from '../../lib/utils/poll';
-import {
- getParameterByName,
-} from '../../lib/utils/common_utils';
+import { getParameterByName } from '../../lib/utils/common_utils';
import { s__ } from '../../locale';
import Flash from '../../flash';
import eventHub from '../event_hub';
import EnvironmentsStore from '../stores/environments_store';
import EnvironmentsService from '../services/environments_service';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import environmentTable from '../components/environments_table.vue';
import tabs from '../../vue_shared/components/navigation_tabs.vue';
import container from '../components/container.vue';
export default {
-
components: {
environmentTable,
container,
- loadingIcon,
tabs,
tablePagination,
},
@@ -67,7 +62,8 @@ export default {
updateContent(parameters) {
this.updateInternalState(parameters);
// fetch new data
- return this.service.fetchEnvironments(this.requestData)
+ return this.service
+ .fetchEnvironments(this.requestData)
.then(response => this.successCallback(response))
.then(() => {
// restart polling
@@ -90,7 +86,8 @@ export default {
if (!this.isMakingRequest) {
this.isLoading = true;
- this.service.postAction(endpoint)
+ this.service
+ .postAction(endpoint)
.then(() => this.fetchEnvironments())
.catch(() => {
this.isLoading = false;
@@ -102,7 +99,8 @@ export default {
fetchEnvironments() {
this.isLoading = true;
- return this.service.fetchEnvironments(this.requestData)
+ return this.service
+ .fetchEnvironments(this.requestData)
.then(this.successCallback)
.catch(this.errorCallback);
},
@@ -113,7 +111,9 @@ export default {
stopEnvironment(environment) {
const endpoint = environment.stop_path;
- const errorMessage = s__('Environments|An error occurred while stopping the environment, please try again');
+ const errorMessage = s__(
+ 'Environments|An error occurred while stopping the environment, please try again',
+ );
this.postAction({ endpoint, errorMessage });
},
},
@@ -151,7 +151,7 @@ export default {
data: this.requestData,
successCallback: this.successCallback,
errorCallback: this.errorCallback,
- notificationCallback: (isMakingRequest) => {
+ notificationCallback: isMakingRequest => {
this.isMakingRequest = isMakingRequest;
},
});
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index 5ce9225a4bb..5808a2d4afa 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -34,14 +34,14 @@ export default class EnvironmentsStore {
* @returns {Array}
*/
storeEnvironments(environments = []) {
- const filteredEnvironments = environments.map((env) => {
- const oldEnvironmentState = this.state.environments
- .find((element) => {
- if (env.latest) {
- return element.id === env.latest.id;
- }
- return element.id === env.id;
- }) || {};
+ const filteredEnvironments = environments.map(env => {
+ const oldEnvironmentState =
+ this.state.environments.find(element => {
+ if (env.latest) {
+ return element.id === env.latest.id;
+ }
+ return element.id === env.id;
+ }) || {};
let filtered = {};
@@ -101,11 +101,11 @@ export default class EnvironmentsStore {
}
/**
- * Toggles folder open property for the given folder.
- *
- * @param {Object} folder
- * @return {Array}
- */
+ * Toggles folder open property for the given folder.
+ *
+ * @param {Object} folder
+ * @return {Array}
+ */
toggleFolder(folder) {
return this.updateEnvironmentProp(folder, 'isOpen', !folder.isOpen);
}
@@ -119,7 +119,7 @@ export default class EnvironmentsStore {
* @return {Object}
*/
setfolderContent(folder, environments) {
- const updatedEnvironments = environments.map((env) => {
+ const updatedEnvironments = environments.map(env => {
let updated = env;
if (env.latest) {
@@ -148,7 +148,7 @@ export default class EnvironmentsStore {
updateEnvironmentProp(environment, prop, newValue) {
const { environments } = this.state;
- const updatedEnvironments = environments.map((env) => {
+ const updatedEnvironments = environments.map(env => {
const updateEnv = Object.assign({}, env);
if (env.id === environment.id) {
updateEnv[prop] = newValue;
diff --git a/app/assets/javascripts/experimental_flags.js b/app/assets/javascripts/experimental_flags.js
index 1d60847147b..42b3fb8c6da 100644
--- a/app/assets/javascripts/experimental_flags.js
+++ b/app/assets/javascripts/experimental_flags.js
@@ -2,7 +2,7 @@ import $ from 'jquery';
import Cookies from 'js-cookie';
export default () => {
- $('.js-experiment-feature-toggle').on('change', (e) => {
+ $('.js-experiment-feature-toggle').on('change', e => {
const el = e.target;
Cookies.set(el.name, el.value, {
diff --git a/app/assets/javascripts/feature_highlight/feature_highlight.js b/app/assets/javascripts/feature_highlight/feature_highlight.js
index 2f27c9351bc..173fe7c69de 100644
--- a/app/assets/javascripts/feature_highlight/feature_highlight.js
+++ b/app/assets/javascripts/feature_highlight/feature_highlight.js
@@ -1,13 +1,6 @@
import $ from 'jquery';
-import {
- getSelector,
- inserted,
-} from './feature_highlight_helper';
-import {
- togglePopover,
- mouseenter,
- debouncedMouseleave,
-} from '../shared/popover';
+import { getSelector, inserted } from './feature_highlight_helper';
+import { togglePopover, mouseenter, debouncedMouseleave } from '../shared/popover';
export function setupFeatureHighlightPopover(id, debounceTimeout = 300) {
const $selector = $(getSelector(id));
@@ -16,7 +9,7 @@ export function setupFeatureHighlightPopover(id, debounceTimeout = 300) {
const hideOnScroll = togglePopover.bind($selector, false);
$selector
- // Setup popover
+ // Set up popover
.data('content', $popoverContent.prop('outerHTML'))
.popover({
html: true,
@@ -41,8 +34,9 @@ export function setupFeatureHighlightPopover(id, debounceTimeout = 300) {
export function findHighestPriorityFeature() {
let priorityFeature;
- const sortedFeatureEls = [].slice.call(document.querySelectorAll('.js-feature-highlight')).sort((a, b) =>
- (a.dataset.highlightPriority || 0) < (b.dataset.highlightPriority || 0));
+ const sortedFeatureEls = [].slice
+ .call(document.querySelectorAll('.js-feature-highlight'))
+ .sort((a, b) => (a.dataset.highlightPriority || 0) < (b.dataset.highlightPriority || 0));
const [priorityFeatureEl] = sortedFeatureEls;
if (priorityFeatureEl) {
diff --git a/app/assets/javascripts/feature_highlight/feature_highlight_helper.js b/app/assets/javascripts/feature_highlight/feature_highlight_helper.js
index d5b97ebb264..fd9433b625c 100644
--- a/app/assets/javascripts/feature_highlight/feature_highlight_helper.js
+++ b/app/assets/javascripts/feature_highlight/feature_highlight_helper.js
@@ -8,10 +8,17 @@ import { togglePopover } from '../shared/popover';
export const getSelector = highlightId => `.js-feature-highlight[data-highlight=${highlightId}]`;
export function dismiss(highlightId) {
- axios.post(this.attr('data-dismiss-endpoint'), {
- feature_name: highlightId,
- })
- .catch(() => Flash(__('An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again.')));
+ axios
+ .post(this.attr('data-dismiss-endpoint'), {
+ feature_name: highlightId,
+ })
+ .catch(() =>
+ Flash(
+ __(
+ 'An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again.',
+ ),
+ ),
+ );
togglePopover.call(this, false);
this.hide();
@@ -23,8 +30,7 @@ export function inserted() {
const $popover = $(this);
const dismissWrapper = dismiss.bind($popover, highlightId);
- $(`#${popoverId} .dismiss-feature-highlight`)
- .on('click', dismissWrapper);
+ $(`#${popoverId} .dismiss-feature-highlight`).on('click', dismissWrapper);
const lazyImg = $(`#${popoverId} .feature-highlight-illustration`)[0];
if (lazyImg) {
diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js
index 6a4874e1ab8..aad5647c045 100644
--- a/app/assets/javascripts/files_comment_button.js
+++ b/app/assets/javascripts/files_comment_button.js
@@ -1,5 +1,5 @@
/* Developer beware! Do not add logic to showButton or hideButton
- * that will force a reflow. Doing so will create a signficant performance
+ * that will force a reflow. Doing so will create a significant performance
* bottleneck for pages with large diffs. For a comprehensive list of what
* causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a
*/
@@ -25,13 +25,15 @@ export default {
if (!this.userCanCreateNote) {
// data-can-create-note is an empty string when true, otherwise undefined
- this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('canCreateNote') === '';
+ this.userCanCreateNote =
+ $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('canCreateNote') === '';
}
this.isParallelView = Cookies.get('diff_view') === 'parallel';
if (this.userCanCreateNote) {
- $diffFile.on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e))
+ $diffFile
+ .on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e))
.on('mouseleave', LINE_COLUMN_CLASSES, e => this.hideButton(this.isParallelView, e));
}
},
@@ -64,9 +66,11 @@ export default {
},
validateButtonParent(buttonParentElement) {
- return !buttonParentElement.classList.contains(EMPTY_CELL_CLASS) &&
+ return (
+ !buttonParentElement.classList.contains(EMPTY_CELL_CLASS) &&
!buttonParentElement.classList.contains(UNFOLDABLE_LINE_CLASS) &&
!buttonParentElement.classList.contains(NO_COMMENT_CLASS) &&
- !buttonParentElement.parentNode.classList.contains(DIFF_EXPANDED_CLASS);
+ !buttonParentElement.parentNode.classList.contains(DIFF_EXPANDED_CLASS)
+ );
},
};
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
index b17ba3c21db..64b09c8b62c 100644
--- a/app/assets/javascripts/filterable_list.js
+++ b/app/assets/javascripts/filterable_list.js
@@ -65,12 +65,15 @@ export default class FilterableList {
this.isBusy = true;
- return axios.get(this.getFilterEndpoint(), {
- params,
- }).then((res) => {
- this.onFilterSuccess(res, params);
- this.onFilterComplete();
- }).catch(() => this.onFilterComplete());
+ return axios
+ .get(this.getFilterEndpoint(), {
+ params,
+ })
+ .then(res => {
+ this.onFilterSuccess(res, params);
+ this.onFilterComplete();
+ })
+ .catch(() => this.onFilterComplete());
}
onFilterSuccess(response, queryData) {
@@ -81,9 +84,13 @@ export default class FilterableList {
// Change url so if user reload a page - search results are saved
const currentPath = this.getPagePath(queryData);
- return window.history.replaceState({
- page: currentPath,
- }, document.title, currentPath);
+ return window.history.replaceState(
+ {
+ page: currentPath,
+ },
+ document.title,
+ currentPath,
+ );
}
onFilterComplete() {
diff --git a/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js
new file mode 100644
index 00000000000..934375023ba
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js
@@ -0,0 +1,24 @@
+import FilteredSearchTokenKeys from './filtered_search_token_keys';
+
+const tokenKeys = [
+ {
+ key: 'status',
+ type: 'string',
+ param: 'status',
+ symbol: '',
+ icon: 'messages',
+ tag: 'status',
+ },
+ {
+ key: 'type',
+ type: 'string',
+ param: 'type',
+ symbol: '',
+ icon: 'cube',
+ tag: 'type',
+ },
+];
+
+const AdminRunnersFilteredSearchTokenKeys = new FilteredSearchTokenKeys(tokenKeys);
+
+export default AdminRunnersFilteredSearchTokenKeys;
diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
index a8eb8d94be3..b9bc5e6ed7f 100644
--- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
+++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
@@ -21,9 +21,11 @@ export default {
},
computed: {
processedItems() {
- return this.items.map((item) => {
- const { tokens, searchToken }
- = FilteredSearchTokenizer.processTokens(item, this.allowedKeys);
+ return this.items.map(item => {
+ const { tokens, searchToken } = FilteredSearchTokenizer.processTokens(
+ item,
+ this.allowedKeys,
+ );
const resultantTokens = tokens.map(token => ({
prefix: `${token.key}:`,
@@ -72,8 +74,8 @@ export default {
@click="onItemActivated(item.text)">
<span>
<span
- v-for="(token, index) in item.tokens"
- :key="`dropdown-token-${index}`"
+ v-for="(token, tokenIndex) in item.tokens"
+ :key="`dropdown-token-${tokenIndex}`"
class="filtered-search-history-dropdown-token"
>
<span class="name">{{ token.prefix }}</span>
diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js
index 5ddd0e5e690..d9a4d06b549 100644
--- a/app/assets/javascripts/filtered_search/dropdown_emoji.js
+++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js
@@ -24,8 +24,12 @@ export default class DropdownEmoji extends FilteredSearchDropdown {
};
import(/* webpackChunkName: 'emoji' */ '~/emoji')
- .then(({ glEmojiTag }) => { this.glEmojiTag = glEmojiTag; })
- .catch(() => { /* ignore error and leave emoji name in the search bar */ });
+ .then(({ glEmojiTag }) => {
+ this.glEmojiTag = glEmojiTag;
+ })
+ .catch(() => {
+ /* ignore error and leave emoji name in the search bar */
+ });
this.unbindEvents();
this.bindEvents();
@@ -48,7 +52,7 @@ export default class DropdownEmoji extends FilteredSearchDropdown {
}
itemClicked(e) {
- super.itemClicked(e, (selected) => {
+ super.itemClicked(e, selected => {
const name = selected.querySelector('.js-data-value').innerText.trim();
return DropdownUtils.getEscapedText(name);
});
@@ -64,16 +68,18 @@ export default class DropdownEmoji extends FilteredSearchDropdown {
// Replace empty gl-emoji tag to real content
const dropdownItems = [...this.dropdown.querySelectorAll('.filter-dropdown-item')];
- dropdownItems.forEach((dropdownItem) => {
- const name = dropdownItem.querySelector('.js-data-value').innerText;
- const emojiTag = this.glEmojiTag(name);
- const emojiElement = dropdownItem.querySelector('gl-emoji');
- emojiElement.outerHTML = emojiTag;
+ dropdownItems.forEach(dropdownItem => {
+ const valueElement = dropdownItem.querySelector('.js-data-value');
+ if (valueElement !== null) {
+ const name = valueElement.innerText;
+ const emojiTag = this.glEmojiTag(name);
+ const emojiElement = dropdownItem.querySelector('gl-emoji');
+ emojiElement.outerHTML = emojiTag;
+ }
});
}
init() {
- this.droplab
- .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
+ this.droplab.addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
}
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 184b34b7b5e..1a1135ae929 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -41,8 +41,10 @@ export default class DropdownHint extends FilteredSearchDropdown {
previousInputValues.forEach((value, index) => {
searchTerms.push(value);
- if (index === previousInputValues.length - 1
- && token.indexOf(value.toLowerCase()) !== -1) {
+ if (
+ index === previousInputValues.length - 1 &&
+ token.indexOf(value.toLowerCase()) !== -1
+ ) {
searchTerms.pop();
}
});
@@ -51,7 +53,11 @@ export default class DropdownHint extends FilteredSearchDropdown {
FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' '));
}
- FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container);
+ const key = token.replace(':', '');
+ const { uppercaseTokenName } = this.tokenKeys.searchByKey(key);
+ FilteredSearchDropdownManager.addWordToInput(key, '', false, {
+ uppercaseTokenName,
+ });
}
this.dismissDropdown();
this.dispatchInputEvent();
@@ -60,13 +66,12 @@ export default class DropdownHint extends FilteredSearchDropdown {
}
renderContent() {
- const dropdownData = this.tokenKeys.get()
- .map(tokenKey => ({
- icon: `fa-${tokenKey.icon}`,
- hint: tokenKey.key,
- tag: `:${tokenKey.tag}`,
- type: tokenKey.type,
- }));
+ const dropdownData = this.tokenKeys.get().map(tokenKey => ({
+ icon: `${gon.sprite_icons}#${tokenKey.icon}`,
+ hint: tokenKey.key,
+ tag: `:${tokenKey.tag}`,
+ type: tokenKey.type,
+ }));
this.droplab.changeHookList(this.hookId, this.dropdown, [Filter], this.config);
this.droplab.setData(this.hookId, dropdownData);
diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js
index 2ffda7e2037..0264f934914 100644
--- a/app/assets/javascripts/filtered_search/dropdown_non_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js
@@ -29,20 +29,18 @@ export default class DropdownNonUser extends FilteredSearchDropdown {
}
itemClicked(e) {
- super.itemClicked(e, (selected) => {
+ super.itemClicked(e, selected => {
const title = selected.querySelector('.js-data-value').innerText.trim();
return `${this.symbol}${DropdownUtils.getEscapedText(title)}`;
});
}
renderContent(forceShowList = false) {
- this.droplab
- .changeHookList(this.hookId, this.dropdown, [Ajax, Filter], this.config);
+ this.droplab.changeHookList(this.hookId, this.dropdown, [Ajax, Filter], this.config);
super.renderContent(forceShowList);
}
init() {
- this.droplab
- .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
+ this.droplab.addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
}
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index d36f38a70b5..d5027590bb7 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -39,8 +39,9 @@ export default class DropdownUser extends FilteredSearchDropdown {
}
itemClicked(e) {
- super.itemClicked(e,
- selected => selected.querySelector('.dropdown-light-content').innerText.trim());
+ super.itemClicked(e, selected =>
+ selected.querySelector('.dropdown-light-content').innerText.trim(),
+ );
}
renderContent(forceShowList = false) {
@@ -68,7 +69,7 @@ export default class DropdownUser extends FilteredSearchDropdown {
// Removes the first character if it is a quotation so that we can search
// with multiple words
- if (value[0] === '"' || value[0] === '\'') {
+ if (value[0] === '"' || value[0] === "'") {
value = value.slice(1);
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index 27fff488603..1b79a3320c6 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -41,7 +41,7 @@ export default class DropdownUtils {
// Removes the first character if it is a quotation so that we can search
// with multiple words
- if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
+ if ((value[0] === '"' || value[0] === "'") && title.indexOf(' ') !== -1) {
value = value.slice(1);
}
@@ -82,11 +82,13 @@ export default class DropdownUtils {
// Reduce the colors to 4
colors.length = Math.min(colors.length, 4);
- const color = colors.map((c, i) => {
- const percentFirst = Math.floor(spacing * i);
- const percentSecond = Math.floor(spacing * (i + 1));
- return `${c} ${percentFirst}%, ${c} ${percentSecond}%`;
- }).join(', ');
+ const color = colors
+ .map((c, i) => {
+ const percentFirst = Math.floor(spacing * i);
+ const percentSecond = Math.floor(spacing * (i + 1));
+ return `${c} ${percentFirst}%, ${c} ${percentSecond}%`;
+ })
+ .join(', ');
return `linear-gradient(${color})`;
}
@@ -97,17 +99,16 @@ export default class DropdownUtils {
data.forEach(DropdownUtils.mergeDuplicateLabels.bind(null, dataMap));
- Object.keys(dataMap)
- .forEach((key) => {
- const label = dataMap[key];
+ Object.keys(dataMap).forEach(key => {
+ const label = dataMap[key];
- if (label.multipleColors) {
- label.color = DropdownUtils.duplicateLabelColor(label.multipleColors);
- label.text_color = '#000000';
- }
+ if (label.multipleColors) {
+ label.color = DropdownUtils.duplicateLabelColor(label.multipleColors);
+ label.text_color = '#000000';
+ }
- results.push(label);
- });
+ results.push(label);
+ });
results.preprocessed = true;
@@ -118,8 +119,7 @@ export default class DropdownUtils {
const { input, allowedKeys } = config;
const updatedItem = item;
const searchInput = DropdownUtils.getSearchQuery(input);
- const { lastToken, tokens } =
- FilteredSearchTokenizer.processTokens(searchInput, allowedKeys);
+ const { lastToken, tokens } = FilteredSearchTokenizer.processTokens(searchInput, allowedKeys);
const lastKey = lastToken.key || lastToken || '';
const allowMultiple = item.type === 'array';
const itemInExistingTokens = tokens.some(t => t.key === item.hint);
@@ -143,7 +143,9 @@ export default class DropdownUtils {
const dataValue = selected.getAttribute('data-value');
if (dataValue) {
- FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true);
+ FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true, {
+ capitalizeTokenValue: selected.hasAttribute('data-capitalize'),
+ });
}
// Return boolean based on whether it was set
@@ -152,7 +154,10 @@ export default class DropdownUtils {
static getVisualTokenValues(visualToken) {
const tokenName = visualToken && visualToken.querySelector('.name').textContent.trim();
- let tokenValue = visualToken && visualToken.querySelector('.value') && visualToken.querySelector('.value').textContent.trim();
+ let tokenValue =
+ visualToken &&
+ visualToken.querySelector('.value') &&
+ visualToken.querySelector('.value').textContent.trim();
if (tokenName === 'label' && tokenValue) {
// remove leading symbol and wrapping quotes
tokenValue = tokenValue.replace(/^~("|')?(.*)/, '$2').replace(/("|')$/, '');
@@ -172,7 +177,7 @@ export default class DropdownUtils {
tokens.splice(inputIndex + 1);
}
- tokens.forEach((token) => {
+ tokens.forEach(token => {
if (token.classList.contains('js-visual-token')) {
const name = token.querySelector('.name');
const value = token.querySelector('.value');
@@ -192,8 +197,9 @@ export default class DropdownUtils {
values.push(name.innerText);
}
} else if (token.classList.contains('input-token')) {
- const { isLastVisualTokenValid } =
- FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ const {
+ isLastVisualTokenValid,
+ } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
const inputValue = input && input.value;
@@ -207,9 +213,7 @@ export default class DropdownUtils {
}
});
- return values
- .map(value => value.trim())
- .join(' ');
+ return values.map(value => value.trim()).join(' ');
}
static getSearchInput(filteredSearchInput) {
@@ -225,7 +229,9 @@ export default class DropdownUtils {
// Replace all spaces inside quote marks with underscores
// (will continue to match entire string until an end quote is found if any)
// This helps with matching the beginning & end of a token:key
- inputValue = inputValue.replace(/(('[^']*'{0,1})|("[^"]*"{0,1})|:\s+)/g, str => str.replace(/\s/g, '_'));
+ inputValue = inputValue.replace(/(('[^']*'{0,1})|("[^"]*"{0,1})|:\s+)/g, str =>
+ str.replace(/\s/g, '_'),
+ );
// Get the right position for the word selected
// Regex matches first space
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
index fb4ae1d17dd..146d3ba963c 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
@@ -85,12 +85,14 @@ export default class FilteredSearchDropdown {
}
dispatchInputEvent() {
- // Propogate input change to FilteredSearchDropdownManager
+ // Propagate input change to FilteredSearchDropdownManager
// so that it can determine which dropdowns to open
- this.input.dispatchEvent(new CustomEvent('input', {
- bubbles: true,
- cancelable: true,
- }));
+ this.input.dispatchEvent(
+ new CustomEvent('input', {
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
}
dispatchFormSubmitEvent() {
@@ -114,7 +116,7 @@ export default class FilteredSearchDropdown {
if (!data) return;
- const results = data.map((o) => {
+ const results = data.map(o => {
const updated = o;
updated.droplab_hidden = false;
return updated;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index 296571606d6..57ec6603d80 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -7,6 +7,7 @@ import DropdownHint from './dropdown_hint';
import DropdownEmoji from './dropdown_emoji';
import DropdownNonUser from './dropdown_non_user';
import DropdownUser from './dropdown_user';
+import NullDropdown from './null_dropdown';
import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
export default class FilteredSearchDropdownManager {
@@ -90,9 +91,24 @@ export default class FilteredSearchDropdownManager {
gl: DropdownEmoji,
element: this.container.querySelector('#js-dropdown-my-reaction'),
},
+ wip: {
+ reference: null,
+ gl: DropdownNonUser,
+ element: this.container.querySelector('#js-dropdown-wip'),
+ },
+ status: {
+ reference: null,
+ gl: NullDropdown,
+ element: this.container.querySelector('#js-dropdown-admin-runner-status'),
+ },
+ type: {
+ reference: null,
+ gl: NullDropdown,
+ element: this.container.querySelector('#js-dropdown-admin-runner-type'),
+ },
};
- supportedTokens.forEach((type) => {
+ supportedTokens.forEach(type => {
if (availableMappings[type]) {
allowedMappings[type] = availableMappings[type];
}
@@ -125,10 +141,13 @@ export default class FilteredSearchDropdownManager {
return endpoint;
}
- static addWordToInput(tokenName, tokenValue = '', clicked = false) {
+ static addWordToInput(tokenName, tokenValue = '', clicked = false, options = {}) {
+ const { uppercaseTokenName = false, capitalizeTokenValue = false } = options;
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
-
- FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue);
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue, {
+ uppercaseTokenName,
+ capitalizeTokenValue,
+ });
input.value = '';
if (clicked) {
@@ -142,13 +161,16 @@ export default class FilteredSearchDropdownManager {
updateDropdownOffset(key) {
// Always align dropdown with the input field
- let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left;
+ let offset =
+ this.filteredSearchInput.getBoundingClientRect().left -
+ this.container.querySelector('.scroll-container').getBoundingClientRect().left;
const maxInputWidth = 240;
const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
// Make sure offset never exceeds the input container
- const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
+ const offsetMaxWidth =
+ this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) {
offset = offsetMaxWidth;
}
@@ -174,8 +196,7 @@ export default class FilteredSearchDropdownManager {
const glArguments = Object.assign({}, defaultArguments, extraArguments);
// Passing glArguments to `new glClass(<arguments>)`
- mappingKey.reference =
- new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
+ mappingKey.reference = new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
}
if (firstLoad) {
@@ -202,8 +223,8 @@ export default class FilteredSearchDropdownManager {
}
const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
- const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
- && this.mapping[match.key];
+ const shouldOpenFilterDropdown =
+ match && this.currentDropdown !== match.key && this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
@@ -214,8 +235,10 @@ export default class FilteredSearchDropdownManager {
setDropdown() {
const query = DropdownUtils.getSearchQuery(true);
- const { lastToken, searchToken } =
- this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys());
+ const { lastToken, searchToken } = this.tokenizer.processTokens(
+ query,
+ this.filteredSearchTokenKeys.getKeys(),
+ );
if (this.currentDropdown) {
this.updateCurrentDropdownOffset();
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 81286c54c4c..4a2af02b40a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -1,12 +1,9 @@
import _ from 'underscore';
-import {
- getParameterByName,
- getUrlParamsArray,
-} from '~/lib/utils/common_utils';
+import { getParameterByName, getUrlParamsArray } from '~/lib/utils/common_utils';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { visitUrl } from '../lib/utils/url_utility';
import Flash from '../flash';
import FilteredSearchContainer from './container';
-import FilteredSearchTokenKeys from './filtered_search_token_keys';
import RecentSearchesRoot from './recent_searches_root';
import RecentSearchesStore from './stores/recent_searches_store';
import RecentSearchesService from './services/recent_searches_service';
@@ -23,7 +20,7 @@ export default class FilteredSearchManager {
isGroup = false,
isGroupAncestor = true,
isGroupDecendent = false,
- filteredSearchTokenKeys = FilteredSearchTokenKeys,
+ filteredSearchTokenKeys = IssuableFilteredSearchTokenKeys,
stateFiltersSelector = '.issues-state-filters',
}) {
this.isGroup = isGroup;
@@ -48,24 +45,28 @@ export default class FilteredSearchManager {
isLocalStorageAvailable: RecentSearchesService.isAvailable(),
allowedKeys: this.filteredSearchTokenKeys.getKeys(),
});
- this.searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
- const fullPath = this.searchHistoryDropdownElement ?
- this.searchHistoryDropdownElement.dataset.fullPath : 'project';
+ this.searchHistoryDropdownElement = document.querySelector(
+ '.js-filtered-search-history-dropdown',
+ );
+ const fullPath = this.searchHistoryDropdownElement
+ ? this.searchHistoryDropdownElement.dataset.fullPath
+ : 'project';
const recentSearchesKey = `${fullPath}-${this.recentsStorageKeyNames[this.page]}`;
this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
}
setup() {
// Fetch recent searches from localStorage
- this.fetchingRecentSearchesPromise = this.recentSearchesService.fetch()
- .catch((error) => {
+ this.fetchingRecentSearchesPromise = this.recentSearchesService
+ .fetch()
+ .catch(error => {
if (error.name === 'RecentSearchesServiceError') return undefined;
// eslint-disable-next-line no-new
new Flash('An error occurred while parsing recent searches');
// Gracefully fail to empty array
return [];
})
- .then((searches) => {
+ .then(searches => {
if (!searches) {
return;
}
@@ -120,7 +121,7 @@ export default class FilteredSearchManager {
if (this.stateFilters) {
this.searchStateWrapper = this.searchState.bind(this);
- this.applyToStateFilters((filterEl) => {
+ this.applyToStateFilters(filterEl => {
filterEl.addEventListener('click', this.searchStateWrapper);
});
}
@@ -128,14 +129,14 @@ export default class FilteredSearchManager {
unbindStateEvents() {
if (this.stateFilters) {
- this.applyToStateFilters((filterEl) => {
+ this.applyToStateFilters(filterEl => {
filterEl.removeEventListener('click', this.searchStateWrapper);
});
}
}
applyToStateFilters(callback) {
- this.stateFilters.querySelectorAll('a[data-state]').forEach((filterEl) => {
+ this.stateFilters.querySelectorAll('a[data-state]').forEach(filterEl => {
if (this.states.indexOf(filterEl.dataset.state) > -1) {
callback(filterEl);
}
@@ -207,7 +208,7 @@ export default class FilteredSearchManager {
let backspaceCount = 0;
// closure for keeping track of the number of backspace keystrokes
- return (e) => {
+ return e => {
// 8 = Backspace Key
// 46 = Delete Key
if (e.keyCode === 8 || e.keyCode === 46) {
@@ -274,8 +275,12 @@ export default class FilteredSearchManager {
const isElementInDynamicFilterDropdown = e.target.closest('.filter-dropdown') !== null;
const isElementInStaticFilterDropdown = e.target.closest('ul[data-dropdown]') !== null;
- if (!isElementInFilteredSearch && !isElementInDynamicFilterDropdown &&
- !isElementInStaticFilterDropdown && inputContainer) {
+ if (
+ !isElementInFilteredSearch &&
+ !isElementInDynamicFilterDropdown &&
+ !isElementInStaticFilterDropdown &&
+ inputContainer
+ ) {
inputContainer.classList.remove('focus');
}
}
@@ -368,7 +373,7 @@ export default class FilteredSearchManager {
const removeElements = [];
- [].forEach.call(this.tokensContainer.children, (t) => {
+ [].forEach.call(this.tokensContainer.children, t => {
let canClearToken = t.classList.contains('js-visual-token');
if (canClearToken) {
@@ -381,7 +386,7 @@ export default class FilteredSearchManager {
}
});
- removeElements.forEach((el) => {
+ removeElements.forEach(el => {
el.parentElement.removeChild(el);
});
@@ -397,15 +402,19 @@ export default class FilteredSearchManager {
handleInputVisualToken() {
const input = this.filteredSearchInput;
- const { tokens, searchToken }
- = this.tokenizer.processTokens(input.value, this.filteredSearchTokenKeys.getKeys());
- const { isLastVisualTokenValid }
- = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ const { tokens, searchToken } = this.tokenizer.processTokens(
+ input.value,
+ this.filteredSearchTokenKeys.getKeys(),
+ );
+ const { isLastVisualTokenValid } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (isLastVisualTokenValid) {
- tokens.forEach((t) => {
+ tokens.forEach(t => {
input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, '');
- FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`);
+ FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`, {
+ uppercaseTokenName: this.filteredSearchTokenKeys.shouldUppercaseTokenName(t.key),
+ capitalizeTokenValue: this.filteredSearchTokenKeys.shouldCapitalizeTokenValue(t.key),
+ });
});
const fragments = searchToken.split(':');
@@ -421,7 +430,10 @@ export default class FilteredSearchManager {
FilteredSearchVisualTokens.addSearchVisualToken(searchTerms);
}
- FilteredSearchVisualTokens.addFilterVisualToken(tokenKey);
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenKey, null, {
+ uppercaseTokenName: this.filteredSearchTokenKeys.shouldUppercaseTokenName(tokenKey),
+ capitalizeTokenValue: this.filteredSearchTokenKeys.shouldCapitalizeTokenValue(tokenKey),
+ });
input.value = input.value.replace(`${tokenKey}:`, '');
}
} else {
@@ -429,7 +441,10 @@ export default class FilteredSearchManager {
const valueCompletedRegex = /([~%@]{0,1}".+")|([~%@]{0,1}'.+')|^((?![~%@]')(?![~%@]")(?!')(?!")).*/g;
if (searchToken.match(valueCompletedRegex) && input.value[input.value.length - 1] === ' ') {
- FilteredSearchVisualTokens.addFilterVisualToken(searchToken);
+ const tokenKey = FilteredSearchVisualTokens.getLastTokenPartial();
+ FilteredSearchVisualTokens.addFilterVisualToken(searchToken, null, {
+ capitalizeTokenValue: this.filteredSearchTokenKeys.shouldCapitalizeTokenValue(tokenKey),
+ });
// Trim the last space as seen in the if statement above
input.value = input.value.replace(searchToken, '').trim();
@@ -444,15 +459,17 @@ export default class FilteredSearchManager {
saveCurrentSearchQuery() {
// Don't save before we have fetched the already saved searches
- this.fetchingRecentSearchesPromise.then(() => {
- const searchQuery = DropdownUtils.getSearchQuery();
- if (searchQuery.length > 0) {
- const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery);
- this.recentSearchesService.save(resultantSearches);
- }
- }).catch(() => {
- // https://gitlab.com/gitlab-org/gitlab-ce/issues/30821
- });
+ this.fetchingRecentSearchesPromise
+ .then(() => {
+ const searchQuery = DropdownUtils.getSearchQuery();
+ if (searchQuery.length > 0) {
+ const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery);
+ this.recentSearchesService.save(resultantSearches);
+ }
+ })
+ .catch(() => {
+ // https://gitlab.com/gitlab-org/gitlab-ce/issues/30821
+ });
}
// allows for modifying params array when a param can't be included in the URL (e.g. Service Desk)
@@ -466,7 +483,7 @@ export default class FilteredSearchManager {
const usernameParams = this.getUsernameParams();
let hasFilteredSearch = false;
- params.forEach((p) => {
+ params.forEach(p => {
const split = p.split('=');
const keyParam = decodeURIComponent(split[0]);
const value = split[1];
@@ -477,11 +494,9 @@ export default class FilteredSearchManager {
if (condition) {
hasFilteredSearch = true;
const canEdit = this.canEdit && this.canEdit(condition.tokenKey);
- FilteredSearchVisualTokens.addFilterVisualToken(
- condition.tokenKey,
- condition.value,
+ FilteredSearchVisualTokens.addFilterVisualToken(condition.tokenKey, condition.value, {
canEdit,
- );
+ });
} else {
// Sanitize value since URL converts spaces into +
// Replace before decode so that we know what was originally + versus the encoded +
@@ -501,15 +516,20 @@ export default class FilteredSearchManager {
if (sanitizedValue.indexOf(' ') !== -1) {
// Prefer ", but use ' if required
- quotationsToUse = sanitizedValue.indexOf('"') === -1 ? '"' : '\'';
+ quotationsToUse = sanitizedValue.indexOf('"') === -1 ? '"' : "'";
}
hasFilteredSearch = true;
const canEdit = this.canEdit && this.canEdit(sanitizedKey, sanitizedValue);
+ const { uppercaseTokenName, capitalizeTokenValue } = match;
FilteredSearchVisualTokens.addFilterVisualToken(
sanitizedKey,
`${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`,
- canEdit,
+ {
+ canEdit,
+ uppercaseTokenName,
+ capitalizeTokenValue,
+ },
);
} else if (!match && keyParam === 'assignee_id') {
const id = parseInt(value, 10);
@@ -517,7 +537,9 @@ export default class FilteredSearchManager {
hasFilteredSearch = true;
const tokenName = 'assignee';
const canEdit = this.canEdit && this.canEdit(tokenName);
- FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit);
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, {
+ canEdit,
+ });
}
} else if (!match && keyParam === 'author_id') {
const id = parseInt(value, 10);
@@ -525,7 +547,9 @@ export default class FilteredSearchManager {
hasFilteredSearch = true;
const tokenName = 'author';
const canEdit = this.canEdit && this.canEdit(tokenName);
- FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit);
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, {
+ canEdit,
+ });
}
} else if (!match && keyParam === 'search') {
hasFilteredSearch = true;
@@ -561,15 +585,19 @@ export default class FilteredSearchManager {
this.saveCurrentSearchQuery();
- const { tokens, searchToken }
- = this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys());
+ const tokenKeys = this.filteredSearchTokenKeys.getKeys();
+ const { tokens, searchToken } = this.tokenizer.processTokens(searchQuery, tokenKeys);
const currentState = state || getParameterByName('state') || 'opened';
paths.push(`state=${currentState}`);
- tokens.forEach((token) => {
- const condition = this.filteredSearchTokenKeys
- .searchByConditionKeyValue(token.key, token.value.toLowerCase());
- const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
+ tokens.forEach(token => {
+ const condition = this.filteredSearchTokenKeys.searchByConditionKeyValue(
+ token.key,
+ token.value.toLowerCase(),
+ );
+ const tokenConfig = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
+ const { param } = tokenConfig;
+
// Replace hyphen with underscore to use as request parameter
// e.g. 'my-reaction' => 'my_reaction'
const underscoredKey = token.key.replace('-', '_');
@@ -581,8 +609,14 @@ export default class FilteredSearchManager {
} else {
let tokenValue = token.value;
- if ((tokenValue[0] === '\'' && tokenValue[tokenValue.length - 1] === '\'') ||
- (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')) {
+ if (tokenConfig.lowercaseValueOnSubmit) {
+ tokenValue = tokenValue.toLowerCase();
+ }
+
+ if (
+ (tokenValue[0] === "'" && tokenValue[tokenValue.length - 1] === "'") ||
+ (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')
+ ) {
tokenValue = tokenValue.slice(1, tokenValue.length - 1);
}
@@ -593,7 +627,10 @@ export default class FilteredSearchManager {
});
if (searchToken) {
- const sanitized = searchToken.split(' ').map(t => encodeURIComponent(t)).join('+');
+ const sanitized = searchToken
+ .split(' ')
+ .map(t => encodeURIComponent(t))
+ .join('+');
paths.push(`search=${sanitized}`);
}
@@ -610,7 +647,7 @@ export default class FilteredSearchManager {
const usernamesById = {};
try {
const attribute = this.filteredSearchInput.getAttribute('data-username-params');
- JSON.parse(attribute).forEach((user) => {
+ JSON.parse(attribute).forEach(user => {
usernamesById[user.id] = user.username;
});
} catch (e) {
diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
index 087ef5cd6f2..e01dedbb57c 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
@@ -1,123 +1,89 @@
-const tokenKeys = [{
- key: 'author',
- type: 'string',
- param: 'username',
- symbol: '@',
- icon: 'pencil',
- tag: '@author',
-}, {
- key: 'assignee',
- type: 'string',
- param: 'username',
- symbol: '@',
- icon: 'user',
- tag: '@assignee',
-}, {
- key: 'milestone',
- type: 'string',
- param: 'title',
- symbol: '%',
- icon: 'clock-o',
- tag: '%milestone',
-}, {
- key: 'label',
- type: 'array',
- param: 'name[]',
- symbol: '~',
- icon: 'tag',
- tag: '~label',
-}];
-
-if (gon.current_user_id) {
- // Appending tokenkeys only logged-in
- tokenKeys.push({
- key: 'my-reaction',
- type: 'string',
- param: 'emoji',
- symbol: '',
- icon: 'thumbs-up',
- tag: 'emoji',
- });
-}
+export default class FilteredSearchTokenKeys {
+ constructor(tokenKeys = [], alternativeTokenKeys = [], conditions = []) {
+ this.tokenKeys = tokenKeys;
+ this.alternativeTokenKeys = alternativeTokenKeys;
+ this.conditions = conditions;
-const alternativeTokenKeys = [{
- key: 'label',
- type: 'string',
- param: 'name',
- symbol: '~',
-}];
-
-const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys);
-
-const conditions = [{
- url: 'assignee_id=0',
- tokenKey: 'assignee',
- value: 'none',
-}, {
- url: 'milestone_title=No+Milestone',
- tokenKey: 'milestone',
- value: 'none',
-}, {
- url: 'milestone_title=%23upcoming',
- tokenKey: 'milestone',
- value: 'upcoming',
-}, {
- url: 'milestone_title=%23started',
- tokenKey: 'milestone',
- value: 'started',
-}, {
- url: 'label_name[]=No+Label',
- tokenKey: 'label',
- value: 'none',
-}];
+ this.tokenKeysWithAlternative = this.tokenKeys.concat(this.alternativeTokenKeys);
+ }
-export default class FilteredSearchTokenKeys {
- static get() {
- return tokenKeys;
+ get() {
+ return this.tokenKeys;
+ }
+
+ getKeys() {
+ return this.tokenKeys.map(i => i.key);
+ }
+
+ getAlternatives() {
+ return this.alternativeTokenKeys;
}
- static getKeys() {
- return tokenKeys.map(i => i.key);
+ getConditions() {
+ return this.conditions;
}
- static getAlternatives() {
- return alternativeTokenKeys;
+ shouldUppercaseTokenName(tokenKey) {
+ const token = this.searchByKey(tokenKey.toLowerCase());
+ return token && token.uppercaseTokenName;
}
- static getConditions() {
- return conditions;
+ shouldCapitalizeTokenValue(tokenKey) {
+ const token = this.searchByKey(tokenKey.toLowerCase());
+ return token && token.capitalizeTokenValue;
}
- static searchByKey(key) {
- return tokenKeys.find(tokenKey => tokenKey.key === key) || null;
+ searchByKey(key) {
+ return this.tokenKeys.find(tokenKey => tokenKey.key === key) || null;
}
- static searchBySymbol(symbol) {
- return tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
+ searchBySymbol(symbol) {
+ return this.tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
}
- static searchByKeyParam(keyParam) {
- return tokenKeysWithAlternative.find((tokenKey) => {
- let tokenKeyParam = tokenKey.key;
+ searchByKeyParam(keyParam) {
+ return (
+ this.tokenKeysWithAlternative.find(tokenKey => {
+ let tokenKeyParam = tokenKey.key;
- // Replace hyphen with underscore to compare keyParam with tokenKeyParam
- // e.g. 'my-reaction' => 'my_reaction'
- tokenKeyParam = tokenKeyParam.replace('-', '_');
+ // Replace hyphen with underscore to compare keyParam with tokenKeyParam
+ // e.g. 'my-reaction' => 'my_reaction'
+ tokenKeyParam = tokenKeyParam.replace('-', '_');
- if (tokenKey.param) {
- tokenKeyParam += `_${tokenKey.param}`;
- }
+ if (tokenKey.param) {
+ tokenKeyParam += `_${tokenKey.param}`;
+ }
+
+ return keyParam === tokenKeyParam;
+ }) || null
+ );
+ }
- return keyParam === tokenKeyParam;
- }) || null;
+ searchByConditionUrl(url) {
+ return this.conditions.find(condition => condition.url === url) || null;
}
- static searchByConditionUrl(url) {
- return conditions.find(condition => condition.url === url) || null;
+ searchByConditionKeyValue(key, value) {
+ return (
+ this.conditions.find(condition => condition.tokenKey === key && condition.value === value) ||
+ null
+ );
}
- static searchByConditionKeyValue(key, value) {
- return conditions
- .find(condition => condition.tokenKey === key && condition.value === value) || null;
+ addExtraTokensForMergeRequests() {
+ const wipToken = {
+ key: 'wip',
+ type: 'string',
+ param: '',
+ symbol: '',
+ icon: 'admin',
+ tag: 'Yes or No',
+ lowercaseValueOnSubmit: true,
+ uppercaseTokenName: true,
+ capitalizeTokenValue: true,
+ };
+
+ this.tokenKeys.push(wipToken);
+ this.tokenKeysWithAlternative.push(wipToken);
}
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
index d75610f6d68..b5c4cb15aac 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
@@ -4,41 +4,48 @@ export default class FilteredSearchTokenizer {
static processTokens(input, allowedKeys) {
// Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single)
- const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
+ const tokenRegex = new RegExp(
+ `(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`,
+ 'g',
+ );
const tokens = [];
const tokenIndexes = []; // stores key+value for simple search
let lastToken = null;
- const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
- let tokenValue = v1 || v2 || v3;
- let tokenSymbol = symbol;
- let tokenIndex = '';
-
- if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
- tokenSymbol = tokenValue;
- tokenValue = '';
- }
-
- tokenIndex = `${key}:${tokenValue}`;
-
- // Prevent adding duplicates
- if (tokenIndexes.indexOf(tokenIndex) === -1) {
- tokenIndexes.push(tokenIndex);
-
- tokens.push({
- key,
- value: tokenValue || '',
- symbol: tokenSymbol || '',
- });
- }
-
- return '';
- }).replace(/\s{2,}/g, ' ').trim() || '';
+ const searchToken =
+ input
+ .replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
+ let tokenValue = v1 || v2 || v3;
+ let tokenSymbol = symbol;
+ let tokenIndex = '';
+
+ if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
+ tokenSymbol = tokenValue;
+ tokenValue = '';
+ }
+
+ tokenIndex = `${key}:${tokenValue}`;
+
+ // Prevent adding duplicates
+ if (tokenIndexes.indexOf(tokenIndex) === -1) {
+ tokenIndexes.push(tokenIndex);
+
+ tokens.push({
+ key,
+ value: tokenValue || '',
+ symbol: tokenSymbol || '',
+ });
+ }
+
+ return '';
+ })
+ .replace(/\s{2,}/g, ' ')
+ .trim() || '';
if (tokens.length > 0) {
const last = tokens[tokens.length - 1];
const lastString = `${last.key}:${last.symbol}${last.value}`;
- lastToken = input.lastIndexOf(lastString) ===
- input.length - lastString.length ? last : searchToken;
+ lastToken =
+ input.lastIndexOf(lastString) === input.length - lastString.length ? last : searchToken;
} else {
lastToken = searchToken;
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
index 56fe1ab4e90..89dcff74d0e 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -13,7 +13,10 @@ export default class FilteredSearchVisualTokens {
return {
lastVisualToken,
- isLastVisualTokenValid: lastVisualToken === null || lastVisualToken.className.indexOf('filtered-search-term') !== -1 || (lastVisualToken && lastVisualToken.querySelector('.value') !== null),
+ isLastVisualTokenValid:
+ lastVisualToken === null ||
+ lastVisualToken.className.indexOf('filtered-search-term') !== -1 ||
+ (lastVisualToken && lastVisualToken.querySelector('.value') !== null),
};
}
@@ -33,7 +36,9 @@ export default class FilteredSearchVisualTokens {
}
static unselectTokens() {
- const otherTokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token .selectable.selected');
+ const otherTokens = FilteredSearchContainer.container.querySelectorAll(
+ '.js-visual-token .selectable.selected',
+ );
[].forEach.call(otherTokens, t => t.classList.remove('selected'));
}
@@ -55,12 +60,14 @@ export default class FilteredSearchVisualTokens {
}
}
- static createVisualTokenElementHTML(canEdit = true) {
+ static createVisualTokenElementHTML(options = {}) {
+ const { canEdit = true, uppercaseTokenName = false, capitalizeTokenValue = false } = options;
+
return `
<div class="${canEdit ? 'selectable' : 'hidden'}" role="button">
- <div class="name"></div>
+ <div class="${uppercaseTokenName ? 'text-uppercase' : ''} name"></div>
<div class="value-container">
- <div class="value"></div>
+ <div class="${capitalizeTokenValue ? 'text-capitalize' : ''} value"></div>
<div class="remove-token" role="button">
<i class="fa fa-close"></i>
</div>
@@ -109,58 +116,63 @@ export default class FilteredSearchVisualTokens {
return AjaxCache.retrieve(labelsEndpoint)
.then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint))
- .then((labels) => {
- const matchingLabel = (labels || []).find(label => `~${DropdownUtils.getEscapedText(label.title)}` === tokenValue);
+ .then(labels => {
+ const matchingLabel = (labels || []).find(
+ label => `~${DropdownUtils.getEscapedText(label.title)}` === tokenValue,
+ );
if (!matchingLabel) {
return;
}
- FilteredSearchVisualTokens
- .setTokenStyle(tokenValueContainer, matchingLabel.color, matchingLabel.text_color);
+ FilteredSearchVisualTokens.setTokenStyle(
+ tokenValueContainer,
+ matchingLabel.color,
+ matchingLabel.text_color,
+ );
})
.catch(() => new Flash('An error occurred while fetching label colors.'));
}
static updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
- if (tokenValue === 'none') {
- return Promise.resolve();
- }
-
const username = tokenValue.replace(/^@/, '');
- return UsersCache.retrieve(username)
- .then((user) => {
- if (!user) {
- return;
- }
-
- /* eslint-disable no-param-reassign */
- tokenValueContainer.dataset.originalValue = tokenValue;
- tokenValueElement.innerHTML = `
+ return (
+ UsersCache.retrieve(username)
+ .then(user => {
+ if (!user) {
+ return;
+ }
+
+ /* eslint-disable no-param-reassign */
+ tokenValueContainer.dataset.originalValue = tokenValue;
+ tokenValueElement.innerHTML = `
<img class="avatar s20" src="${user.avatar_url}" alt="">
${_.escape(user.name)}
`;
- /* eslint-enable no-param-reassign */
- })
- // ignore error and leave username in the search bar
- .catch(() => { });
+ /* eslint-enable no-param-reassign */
+ })
+ // ignore error and leave username in the search bar
+ .catch(() => {})
+ );
}
static updateEmojiTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
const container = tokenValueContainer;
const element = tokenValueElement;
- return import(/* webpackChunkName: 'emoji' */ '../emoji')
- .then((Emoji) => {
- if (!Emoji.isEmojiNameValid(tokenValue)) {
- return;
- }
-
- container.dataset.originalValue = tokenValue;
- element.innerHTML = Emoji.glEmojiTag(tokenValue);
- })
- // ignore error and leave emoji name in the search bar
- .catch(() => { });
+ return (
+ import(/* webpackChunkName: 'emoji' */ '../emoji')
+ .then(Emoji => {
+ if (!Emoji.isEmojiNameValid(tokenValue)) {
+ return;
+ }
+
+ container.dataset.originalValue = tokenValue;
+ element.innerHTML = Emoji.glEmojiTag(tokenValue);
+ })
+ // ignore error and leave emoji name in the search bar
+ .catch(() => {})
+ );
}
static renderVisualTokenValue(parentElement, tokenName, tokenValue) {
@@ -168,30 +180,44 @@ export default class FilteredSearchVisualTokens {
const tokenValueElement = tokenValueContainer.querySelector('.value');
tokenValueElement.innerText = tokenValue;
+ if (tokenValue === 'none' || tokenValue === 'any') {
+ return;
+ }
+
const tokenType = tokenName.toLowerCase();
+
if (tokenType === 'label') {
FilteredSearchVisualTokens.updateLabelTokenColor(tokenValueContainer, tokenValue);
- } else if ((tokenType === 'author') || (tokenType === 'assignee')) {
+ } else if (tokenType === 'author' || tokenType === 'assignee') {
FilteredSearchVisualTokens.updateUserTokenAppearance(
- tokenValueContainer, tokenValueElement, tokenValue,
+ tokenValueContainer,
+ tokenValueElement,
+ tokenValue,
);
} else if (tokenType === 'my-reaction') {
FilteredSearchVisualTokens.updateEmojiTokenAppearance(
- tokenValueContainer, tokenValueElement, tokenValue,
+ tokenValueContainer,
+ tokenValueElement,
+ tokenValue,
);
}
}
- static addVisualTokenElement(name, value, isSearchTerm, canEdit) {
+ static addVisualTokenElement(name, value, options = {}) {
+ const { isSearchTerm = false, canEdit, uppercaseTokenName, capitalizeTokenValue } = options;
const li = document.createElement('li');
li.classList.add('js-visual-token');
li.classList.add(isSearchTerm ? 'filtered-search-term' : 'filtered-search-token');
if (value) {
- li.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML(canEdit);
+ li.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML({
+ canEdit,
+ uppercaseTokenName,
+ capitalizeTokenValue,
+ });
FilteredSearchVisualTokens.renderVisualTokenValue(li, name, value);
} else {
- li.innerHTML = '<div class="name"></div>';
+ li.innerHTML = `<div class="${uppercaseTokenName ? 'text-uppercase' : ''} name"></div>`;
}
li.querySelector('.name').innerText = name;
@@ -201,8 +227,10 @@ export default class FilteredSearchVisualTokens {
}
static addValueToPreviousVisualTokenElement(value) {
- const { lastVisualToken, isLastVisualTokenValid } =
- FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ const {
+ lastVisualToken,
+ isLastVisualTokenValid,
+ } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (!isLastVisualTokenValid && lastVisualToken.classList.contains('filtered-search-token')) {
const name = FilteredSearchVisualTokens.getLastTokenPartial();
@@ -212,20 +240,34 @@ export default class FilteredSearchVisualTokens {
}
}
- static addFilterVisualToken(tokenName, tokenValue, canEdit) {
- const { lastVisualToken, isLastVisualTokenValid }
- = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ static addFilterVisualToken(
+ tokenName,
+ tokenValue,
+ { canEdit, uppercaseTokenName = false, capitalizeTokenValue = false } = {},
+ ) {
+ const {
+ lastVisualToken,
+ isLastVisualTokenValid,
+ } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
const { addVisualTokenElement } = FilteredSearchVisualTokens;
if (isLastVisualTokenValid) {
- addVisualTokenElement(tokenName, tokenValue, false, canEdit);
+ addVisualTokenElement(tokenName, tokenValue, {
+ canEdit,
+ uppercaseTokenName,
+ capitalizeTokenValue,
+ });
} else {
const previousTokenName = lastVisualToken.querySelector('.name').innerText;
const tokensContainer = FilteredSearchContainer.container.querySelector('.tokens-container');
tokensContainer.removeChild(lastVisualToken);
const value = tokenValue || tokenName;
- addVisualTokenElement(previousTokenName, value, false, canEdit);
+ addVisualTokenElement(previousTokenName, value, {
+ canEdit,
+ uppercaseTokenName,
+ capitalizeTokenValue,
+ });
}
}
@@ -235,7 +277,9 @@ export default class FilteredSearchVisualTokens {
if (lastVisualToken && lastVisualToken.classList.contains('filtered-search-term')) {
lastVisualToken.querySelector('.name').innerText += ` ${searchTerm}`;
} else {
- FilteredSearchVisualTokens.addVisualTokenElement(searchTerm, null, true);
+ FilteredSearchVisualTokens.addVisualTokenElement(searchTerm, null, {
+ isSearchTerm: true,
+ });
}
}
@@ -278,8 +322,7 @@ export default class FilteredSearchVisualTokens {
static tokenizeInput() {
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
- const { isLastVisualTokenValid } =
- FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ const { isLastVisualTokenValid } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (input.value) {
if (isLastVisualTokenValid) {
@@ -306,7 +349,9 @@ export default class FilteredSearchVisualTokens {
let value;
if (token.classList.contains('filtered-search-token')) {
- FilteredSearchVisualTokens.addFilterVisualToken(nameElement.innerText);
+ FilteredSearchVisualTokens.addFilterVisualToken(nameElement.innerText, null, {
+ uppercaseTokenName: nameElement.classList.contains('text-uppercase'),
+ });
const valueContainerElement = token.querySelector('.value-container');
value = valueContainerElement.dataset.originalValue;
@@ -343,8 +388,7 @@ export default class FilteredSearchVisualTokens {
FilteredSearchVisualTokens.tokenizeInput();
if (!tokenContainer.lastElementChild.isEqualNode(inputLi)) {
- const { isLastVisualTokenValid } =
- FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ const { isLastVisualTokenValid } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (!isLastVisualTokenValid) {
const lastPartial = FilteredSearchVisualTokens.getLastTokenPartial();
diff --git a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
new file mode 100644
index 00000000000..bb0ecb8efe7
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
@@ -0,0 +1,113 @@
+import FilteredSearchTokenKeys from './filtered_search_token_keys';
+
+export const tokenKeys = [
+ {
+ key: 'author',
+ type: 'string',
+ param: 'username',
+ symbol: '@',
+ icon: 'pencil',
+ tag: '@author',
+ },
+ {
+ key: 'assignee',
+ type: 'string',
+ param: 'username',
+ symbol: '@',
+ icon: 'user',
+ tag: '@assignee',
+ },
+ {
+ key: 'milestone',
+ type: 'string',
+ param: 'title',
+ symbol: '%',
+ icon: 'clock',
+ tag: '%milestone',
+ },
+ {
+ key: 'label',
+ type: 'array',
+ param: 'name[]',
+ symbol: '~',
+ icon: 'labels',
+ tag: '~label',
+ },
+];
+
+if (gon.current_user_id) {
+ // Appending tokenkeys only logged-in
+ tokenKeys.push({
+ key: 'my-reaction',
+ type: 'string',
+ param: 'emoji',
+ symbol: '',
+ icon: 'thumb-up',
+ tag: 'emoji',
+ });
+}
+
+export const alternativeTokenKeys = [
+ {
+ key: 'label',
+ type: 'string',
+ param: 'name',
+ symbol: '~',
+ },
+];
+
+export const conditions = [
+ {
+ url: 'assignee_id=None',
+ tokenKey: 'assignee',
+ value: 'none',
+ },
+ {
+ url: 'assignee_id=Any',
+ tokenKey: 'assignee',
+ value: 'any',
+ },
+ {
+ url: 'milestone_title=None',
+ tokenKey: 'milestone',
+ value: 'none',
+ },
+ {
+ url: 'milestone_title=Any',
+ tokenKey: 'milestone',
+ value: 'any',
+ },
+ {
+ url: 'milestone_title=%23upcoming',
+ tokenKey: 'milestone',
+ value: 'upcoming',
+ },
+ {
+ url: 'milestone_title=%23started',
+ tokenKey: 'milestone',
+ value: 'started',
+ },
+ {
+ url: 'label_name[]=No+Label',
+ tokenKey: 'label',
+ value: 'none',
+ },
+ {
+ url: 'my_reaction_emoji=None',
+ tokenKey: 'my-reaction',
+ value: 'none',
+ },
+ {
+ url: 'my_reaction_emoji=Any',
+ tokenKey: 'my-reaction',
+ value: 'any',
+ },
+];
+
+const IssuableFilteredSearchTokenKeys = new FilteredSearchTokenKeys(
+ tokenKeys,
+ alternativeTokenKeys,
+ conditions,
+);
+
+export default IssuableFilteredSearchTokenKeys;
diff --git a/app/assets/javascripts/filtered_search/null_dropdown.js b/app/assets/javascripts/filtered_search/null_dropdown.js
new file mode 100644
index 00000000000..4cfce2a5beb
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/null_dropdown.js
@@ -0,0 +1,9 @@
+import FilteredSearchDropdown from './filtered_search_dropdown';
+
+export default class NullDropdown extends FilteredSearchDropdown {
+ renderContent(forceShowList = false) {
+ this.droplab.changeHookList(this.hookId, this.dropdown, [], this.config);
+
+ super.renderContent(forceShowList);
+ }
+}
diff --git a/app/assets/javascripts/filtered_search/recent_searches_root.js b/app/assets/javascripts/filtered_search/recent_searches_root.js
index c1efa9c86f4..6c8e77a7fe5 100644
--- a/app/assets/javascripts/filtered_search/recent_searches_root.js
+++ b/app/assets/javascripts/filtered_search/recent_searches_root.js
@@ -3,11 +3,7 @@ import RecentSearchesDropdownContent from './components/recent_searches_dropdown
import eventHub from './event_hub';
class RecentSearchesRoot {
- constructor(
- recentSearchesStore,
- recentSearchesService,
- wrapperElement,
- ) {
+ constructor(recentSearchesStore, recentSearchesService, wrapperElement) {
this.store = recentSearchesStore;
this.service = recentSearchesService;
this.wrapperElement = wrapperElement;
@@ -35,7 +31,9 @@ class RecentSearchesRoot {
components: {
RecentSearchesDropdownContent,
},
- data() { return state; },
+ data() {
+ return state;
+ },
template: `
<recent-searches-dropdown-content
:items="recentSearches"
@@ -57,7 +55,6 @@ class RecentSearchesRoot {
this.vm.$destroy();
}
}
-
}
export default RecentSearchesRoot;
diff --git a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
index aaa0c349d93..76d40bfdaf8 100644
--- a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
+++ b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
@@ -2,11 +2,14 @@ import _ from 'underscore';
class RecentSearchesStore {
constructor(initialState = {}, allowedKeys) {
- this.state = Object.assign({
- isLocalStorageAvailable: true,
- recentSearches: [],
- allowedKeys,
- }, initialState);
+ this.state = Object.assign(
+ {
+ isLocalStorageAvailable: true,
+ recentSearches: [],
+ allowedKeys,
+ },
+ initialState,
+ );
}
addRecentSearch(newSearch) {
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index a0af2875ab5..c2397842125 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -8,13 +8,19 @@ const hideFlash = (flashEl, fadeTransition = true) => {
});
}
- flashEl.addEventListener('transitionend', () => {
- flashEl.remove();
- if (document.body.classList.contains('flash-shown')) document.body.classList.remove('flash-shown');
- }, {
- once: true,
- passive: true,
- });
+ flashEl.addEventListener(
+ 'transitionend',
+ () => {
+ flashEl.remove();
+ window.dispatchEvent(new Event('resize'));
+ if (document.body.classList.contains('flash-shown'))
+ document.body.classList.remove('flash-shown');
+ },
+ {
+ once: true,
+ passive: true,
+ },
+ );
if (!fadeTransition) flashEl.dispatchEvent(new Event('transitionend'));
};
@@ -29,12 +35,14 @@ const createAction = config => `
</a>
`;
-const createFlashEl = (message, type, isInContentWrapper = false) => `
+const createFlashEl = (message, type, isFixedLayout = false) => `
<div
class="flash-${type}"
>
<div
- class="flash-text ${isInContentWrapper ? 'container-fluid container-limited' : ''}"
+ class="flash-text ${
+ isFixedLayout ? 'container-fluid container-limited limit-container-width' : ''
+ }"
>
${_.escape(message)}
</div>
@@ -68,12 +76,15 @@ const createFlash = function createFlash(
addBodyClass = false,
) {
const flashContainer = parent.querySelector('.flash-container');
+ const navigation = parent.querySelector('.content');
if (!flashContainer) return null;
- const isInContentWrapper = flashContainer.parentNode.classList.contains('content-wrapper');
+ const isFixedLayout = navigation
+ ? navigation.parentNode.classList.contains('container-limited')
+ : true;
- flashContainer.innerHTML = createFlashEl(message, type, isInContentWrapper);
+ flashContainer.innerHTML = createFlashEl(message, type, isFixedLayout);
const flashEl = flashContainer.querySelector(`.flash-${type}`);
removeFlashClickListener(flashEl, fadeTransition);
@@ -82,7 +93,9 @@ const createFlash = function createFlash(
flashEl.innerHTML += createAction(actionConfig);
if (actionConfig.clickHandler) {
- flashEl.querySelector('.flash-action').addEventListener('click', e => actionConfig.clickHandler(e));
+ flashEl
+ .querySelector('.flash-action')
+ .addEventListener('click', e => actionConfig.clickHandler(e));
}
}
@@ -93,11 +106,5 @@ const createFlash = function createFlash(
return flashContainer;
};
-export {
- createFlash as default,
- createFlashEl,
- createAction,
- hideFlash,
- removeFlashClickListener,
-};
+export { createFlash as default, createFlashEl, createAction, hideFlash, removeFlashClickListener };
window.Flash = createFlash;
diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js
index 8b4f3b05ee7..3ac00c51df4 100644
--- a/app/assets/javascripts/fly_out_nav.js
+++ b/app/assets/javascripts/fly_out_nav.js
@@ -11,9 +11,13 @@ let sidebar;
export const mousePos = [];
-export const setSidebar = (el) => { sidebar = el; };
+export const setSidebar = el => {
+ sidebar = el;
+};
export const getOpenMenu = () => currentOpenMenu;
-export const setOpenMenu = (menu = null) => { currentOpenMenu = menu; };
+export const setOpenMenu = (menu = null) => {
+ currentOpenMenu = menu;
+};
export const slope = (a, b) => (b.y - a.y) / (b.x - a.x);
@@ -21,9 +25,10 @@ let headerHeight = 50;
export const getHeaderHeight = () => headerHeight;
-export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-collapsed-desktop');
+export const isSidebarCollapsed = () =>
+ sidebar && sidebar.classList.contains('sidebar-collapsed-desktop');
-export const canShowActiveSubItems = (el) => {
+export const canShowActiveSubItems = el => {
if (el.classList.contains('active') && !isSidebarCollapsed()) {
return false;
}
@@ -31,7 +36,10 @@ export const canShowActiveSubItems = (el) => {
return true;
};
-export const canShowSubItems = () => bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg';
+export const canShowSubItems = () =>
+ bp.getBreakpointSize() === 'sm' ||
+ bp.getBreakpointSize() === 'md' ||
+ bp.getBreakpointSize() === 'lg';
export const getHideSubItemsInterval = () => {
if (!currentOpenMenu || !mousePos.length) return 0;
@@ -41,11 +49,12 @@ export const getHideSubItemsInterval = () => {
const currentMousePosY = currentMousePos.y;
const [menuTop, menuBottom] = menuCornerLocs;
- if (currentMousePosY < menuTop.y ||
- currentMousePosY > menuBottom.y) return 0;
+ if (currentMousePosY < menuTop.y || currentMousePosY > menuBottom.y) return 0;
- if (slope(prevMousePos, menuBottom) < slope(currentMousePos, menuBottom) &&
- slope(prevMousePos, menuTop) > slope(currentMousePos, menuTop)) {
+ if (
+ slope(prevMousePos, menuBottom) < slope(currentMousePos, menuBottom) &&
+ slope(prevMousePos, menuTop) > slope(currentMousePos, menuTop)
+ ) {
return HIDE_INTERVAL_TIMEOUT;
}
@@ -56,17 +65,18 @@ export const calculateTop = (boundingRect, outerHeight) => {
const windowHeight = window.innerHeight;
const bottomOverflow = windowHeight - (boundingRect.top + outerHeight);
- return bottomOverflow < 0 ? (boundingRect.top - outerHeight) + boundingRect.height :
- boundingRect.top;
+ return bottomOverflow < 0
+ ? boundingRect.top - outerHeight + boundingRect.height
+ : boundingRect.top;
};
-export const hideMenu = (el) => {
+export const hideMenu = el => {
if (!el) return;
const parentEl = el.parentNode;
- el.style.display = ''; // eslint-disable-line no-param-reassign
- el.style.transform = ''; // eslint-disable-line no-param-reassign
+ el.style.display = '';
+ el.style.transform = '';
el.classList.remove(IS_ABOVE_CLASS);
parentEl.classList.remove(IS_OVER_CLASS);
parentEl.classList.remove(IS_SHOWING_FLY_OUT_CLASS);
@@ -101,7 +111,7 @@ export const moveSubItemsToPosition = (el, subItems) => {
}
};
-export const showSubLevelItems = (el) => {
+export const showSubLevelItems = el => {
const subItems = el.querySelector('.sidebar-sub-level-items');
const isIconOnly = subItems && subItems.classList.contains('is-fly-out-only');
@@ -128,16 +138,20 @@ export const mouseEnterTopItems = (el, timeout = getHideSubItemsInterval()) => {
}, timeout);
};
-export const mouseLeaveTopItem = (el) => {
+export const mouseLeaveTopItem = el => {
const subItems = el.querySelector('.sidebar-sub-level-items');
- if (!canShowSubItems() || !canShowActiveSubItems(el) ||
- (subItems && subItems === currentOpenMenu)) return;
+ if (
+ !canShowSubItems() ||
+ !canShowActiveSubItems(el) ||
+ (subItems && subItems === currentOpenMenu)
+ )
+ return;
el.classList.remove(IS_OVER_CLASS);
};
-export const documentMouseMove = (e) => {
+export const documentMouseMove = e => {
mousePos.push({
x: e.clientX,
y: e.clientY,
@@ -146,7 +160,7 @@ export const documentMouseMove = (e) => {
if (mousePos.length > 6) mousePos.shift();
};
-export const subItemsMouseLeave = (relatedTarget) => {
+export const subItemsMouseLeave = relatedTarget => {
clearTimeout(timeoutId);
if (relatedTarget && !relatedTarget.closest(`.${IS_OVER_CLASS}`)) {
@@ -174,7 +188,7 @@ export default () => {
headerHeight = document.querySelector('.nav-sidebar').offsetTop;
- items.forEach((el) => {
+ items.forEach(el => {
const subItems = el.querySelector('.sidebar-sub-level-items');
if (subItems) {
diff --git a/app/assets/javascripts/frequent_items/components/app.vue b/app/assets/javascripts/frequent_items/components/app.vue
index 2f030de8967..1ed8254bc58 100644
--- a/app/assets/javascripts/frequent_items/components/app.vue
+++ b/app/assets/javascripts/frequent_items/components/app.vue
@@ -1,7 +1,7 @@
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import AccessorUtilities from '~/lib/utils/accessor';
+import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '../event_hub';
import store from '../store/';
import { FREQUENT_ITEMS, STORAGE_KEY } from '../constants';
@@ -13,9 +13,9 @@ import frequentItemsMixin from './frequent_items_mixin';
export default {
store,
components: {
- LoadingIcon,
FrequentItemsSearchInput,
FrequentItemsList,
+ GlLoadingIcon,
},
mixins: [frequentItemsMixin],
props: {
@@ -98,11 +98,11 @@ export default {
<frequent-items-search-input
:namespace="namespace"
/>
- <loading-icon
+ <gl-loading-icon
v-if="isLoadingItems"
:label="translations.loadingMessage"
+ :size="2"
class="loading-animation prepend-top-20"
- size="2"
/>
<div
v-if="!isLoadingItems && !hasSearchQuery"
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
index 1f1665ff7fe..2399ee15332 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -1,5 +1,5 @@
<script>
-/* eslint-disable vue/require-default-prop, vue/require-prop-types */
+/* eslint-disable vue/require-default-prop */
import Identicon from '../../vue_shared/components/identicon.vue';
export default {
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
index a6a265eb3fd..14c223c61a4 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
@@ -1,10 +1,14 @@
<script>
import _ from 'underscore';
import { mapActions } from 'vuex';
+import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub';
import frequentItemsMixin from './frequent_items_mixin';
export default {
+ components: {
+ Icon,
+ },
mixins: [frequentItemsMixin],
data() {
return {
@@ -45,11 +49,10 @@ export default {
type="search"
class="form-control"
/>
- <i
+ <icon
v-if="!searchQuery"
- class="search-icon fa fa-fw fa-search"
- aria-hidden="true"
- >
- </i>
+ name="search"
+ class="search-icon"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 73b2cd0b2c7..6f8b73564d0 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -15,6 +15,7 @@ export const defaultAutocompleteConfig = {
epics: true,
milestones: true,
labels: true,
+ snippets: true,
};
class GfmAutoComplete {
@@ -50,6 +51,7 @@ class GfmAutoComplete {
if (this.enableMap.milestones) this.setupMilestones($input);
if (this.enableMap.mergeRequests) this.setupMergeRequests($input);
if (this.enableMap.labels) this.setupLabels($input);
+ if (this.enableMap.snippets) this.setupSnippets($input);
// We don't instantiate the quick actions autocomplete for note and issue/MR edit forms
$input.filter('[data-supports-quick-actions="true"]').atwho({
@@ -92,7 +94,7 @@ class GfmAutoComplete {
...this.getDefaultCallbacks(),
beforeSave(commands) {
if (GfmAutoComplete.isLoading(commands)) return commands;
- return $.map(commands, (c) => {
+ return $.map(commands, c => {
let search = c.name;
if (c.aliases.length > 0) {
search = `${search} ${c.aliases.join(' ')}`;
@@ -149,10 +151,16 @@ class GfmAutoComplete {
// Team Members
$input.atwho({
at: '@',
+ alias: 'users',
displayTpl(value) {
let tmpl = GfmAutoComplete.Loading.template;
- if (value.username != null) {
- tmpl = GfmAutoComplete.Members.template;
+ const { avatarTag, username, title } = value;
+ if (username != null) {
+ tmpl = GfmAutoComplete.Members.templateFunction({
+ avatarTag,
+ username,
+ title,
+ });
}
return tmpl;
},
@@ -165,7 +173,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(members) {
- return $.map(members, (m) => {
+ return $.map(members, m => {
let title = '';
if (m.username == null) {
return m;
@@ -176,7 +184,9 @@ class GfmAutoComplete {
}
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase();
- const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar avatar-inline center s26"/>`;
+ const imgAvatar = `<img src="${m.avatar_url}" alt="${
+ m.username
+ }" class="avatar avatar-inline center s26"/>`;
const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
return {
@@ -199,7 +209,7 @@ class GfmAutoComplete {
displayTpl(value) {
let tmpl = GfmAutoComplete.Loading.template;
if (value.title != null) {
- tmpl = GfmAutoComplete.Issues.template;
+ tmpl = GfmAutoComplete.Issues.templateFunction(value.id, value.title);
}
return tmpl;
},
@@ -209,7 +219,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(issues) {
- return $.map(issues, (i) => {
+ return $.map(issues, i => {
if (i.title == null) {
return i;
}
@@ -242,7 +252,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(milestones) {
- return $.map(milestones, (m) => {
+ return $.map(milestones, m => {
if (m.title == null) {
return m;
}
@@ -265,7 +275,7 @@ class GfmAutoComplete {
displayTpl(value) {
let tmpl = GfmAutoComplete.Loading.template;
if (value.title != null) {
- tmpl = GfmAutoComplete.Issues.template;
+ tmpl = GfmAutoComplete.Issues.templateFunction(value.id, value.title);
}
return tmpl;
},
@@ -275,7 +285,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(merges) {
- return $.map(merges, (m) => {
+ return $.map(merges, m => {
if (m.title == null) {
return m;
}
@@ -322,13 +332,20 @@ class GfmAutoComplete {
},
matcher(flag, subtext) {
const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
- const subtextNodes = subtext.split(/\n+/g).pop().split(GfmAutoComplete.regexSubtext);
+ const subtextNodes = subtext
+ .split(/\n+/g)
+ .pop()
+ .split(GfmAutoComplete.regexSubtext);
// Check if ~ is followed by '/label', '/relabel' or '/unlabel' commands.
- command = subtextNodes.find((node) => {
- if (node === LABEL_COMMAND.LABEL ||
- node === LABEL_COMMAND.RELABEL ||
- node === LABEL_COMMAND.UNLABEL) { return node; }
+ command = subtextNodes.find(node => {
+ if (
+ node === LABEL_COMMAND.LABEL ||
+ node === LABEL_COMMAND.RELABEL ||
+ node === LABEL_COMMAND.UNLABEL
+ ) {
+ return node;
+ }
return null;
});
@@ -360,6 +377,39 @@ class GfmAutoComplete {
});
}
+ setupSnippets($input) {
+ $input.atwho({
+ at: '$',
+ alias: 'snippets',
+ searchKey: 'search',
+ displayTpl(value) {
+ let tmpl = GfmAutoComplete.Loading.template;
+ if (value.title != null) {
+ tmpl = GfmAutoComplete.Issues.templateFunction(value.id, value.title);
+ }
+ return tmpl;
+ },
+ data: GfmAutoComplete.defaultLoadingData,
+ // eslint-disable-next-line no-template-curly-in-string
+ insertTpl: '${atwho-at}${id}',
+ callbacks: {
+ ...this.getDefaultCallbacks(),
+ beforeSave(snippets) {
+ return $.map(snippets, m => {
+ if (m.title == null) {
+ return m;
+ }
+ return {
+ id: m.id,
+ title: sanitize(m.title),
+ search: `${m.id} ${m.title}`,
+ };
+ });
+ },
+ },
+ });
+ }
+
getDefaultCallbacks() {
const fetchData = this.fetchData.bind(this);
@@ -423,13 +473,17 @@ class GfmAutoComplete {
this.loadData($input, at, validEmojiNames);
GfmAutoComplete.glEmojiTag = glEmojiTag;
})
- .catch(() => { this.isLoadingData[at] = false; });
+ .catch(() => {
+ this.isLoadingData[at] = false;
+ });
} else if (dataSource) {
AjaxCache.retrieve(dataSource, true)
- .then((data) => {
+ .then(data => {
this.loadData($input, at, data);
})
- .catch(() => { this.isLoadingData[at] = false; });
+ .catch(() => {
+ this.isLoadingData[at] = false;
+ });
} else {
this.isLoadingData[at] = false;
}
@@ -462,15 +516,16 @@ class GfmAutoComplete {
}
const loadingState = GfmAutoComplete.defaultLoadingData[0];
- return dataToInspect &&
- (dataToInspect === loadingState || dataToInspect.name === loadingState);
+ return dataToInspect && (dataToInspect === loadingState || dataToInspect.name === loadingState);
}
static defaultMatcher(flag, subtext, controllers) {
// The below is taken from At.js source
// Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js
- const atSymbolsWithBar = Object.keys(controllers).join('|');
+ const atSymbolsWithBar = Object.keys(controllers)
+ .join('|')
+ .replace(/[$]/, '\\$&');
const atSymbolsWithoutBar = Object.keys(controllers).join('');
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
@@ -478,7 +533,10 @@ class GfmAutoComplete {
const accentAChar = decodeURI('%C3%80');
const accentYChar = decodeURI('%C3%BF');
- const regexp = new RegExp(`^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`, 'gi');
+ const regexp = new RegExp(
+ `^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`,
+ 'gi',
+ );
return regexp.exec(targetSubtext);
}
@@ -497,6 +555,7 @@ GfmAutoComplete.atTypeMap = {
'~': 'labels',
'%': 'milestones',
'/': 'commands',
+ $: 'snippets',
};
// Emoji
@@ -512,17 +571,20 @@ GfmAutoComplete.Emoji = {
};
// Team Members
GfmAutoComplete.Members = {
- // eslint-disable-next-line no-template-curly-in-string
- template: '<li>${avatarTag} ${username} <small>${title}</small></li>',
+ templateFunction({ avatarTag, username, title }) {
+ return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small></li>`;
+ },
};
GfmAutoComplete.Labels = {
- // eslint-disable-next-line no-template-curly-in-string
- template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
+ template:
+ // eslint-disable-next-line no-template-curly-in-string
+ '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
};
-// Issues and MergeRequests
+// Issues, MergeRequests and Snippets
GfmAutoComplete.Issues = {
- // eslint-disable-next-line no-template-curly-in-string
- template: '<li><small>${id}</small> ${title}</li>',
+ templateFunction(id, title) {
+ return `<li><small>${id}</small> ${_.escape(title)}</li>`;
+ },
};
// Milestones
GfmAutoComplete.Milestones = {
@@ -530,7 +592,8 @@ GfmAutoComplete.Milestones = {
template: '<li>${title}</li>',
};
GfmAutoComplete.Loading = {
- template: '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>',
+ template:
+ '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>',
};
export default GfmAutoComplete;
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index c3959ef3e9e..a8ac2f510a4 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, one-var-declaration-per-line, max-len, vars-on-top, wrap-iife, no-unused-vars, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */
+/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, vars-on-top, no-unused-vars, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */
/* global fuzzaldrinPlus */
import $ from 'jquery';
diff --git a/app/assets/javascripts/gl_field_error.js b/app/assets/javascripts/gl_field_error.js
index 87c6e37b9fb..a5b8c357e8a 100644
--- a/app/assets/javascripts/gl_field_error.js
+++ b/app/assets/javascripts/gl_field_error.js
@@ -116,7 +116,8 @@ export default class GlFieldError {
this.form.focusOnFirstInvalid.apply(this.form);
// For UX, wait til after first invalid submission to check each keyup
- this.inputElement.off('keyup.fieldValidator')
+ this.inputElement
+ .off('keyup.fieldValidator')
.on('keyup.fieldValidator', this.updateValidity.bind(this));
}
diff --git a/app/assets/javascripts/gl_field_errors.js b/app/assets/javascripts/gl_field_errors.js
index b9c51045b1d..d5d5954ce6a 100644
--- a/app/assets/javascripts/gl_field_errors.js
+++ b/app/assets/javascripts/gl_field_errors.js
@@ -16,16 +16,19 @@ export default class GlFieldErrors {
initValidators() {
// register selectors here as needed
const validateSelectors = [':text', ':password', '[type=email]']
- .map(selector => `input${selector}`).join(',');
+ .map(selector => `input${selector}`)
+ .join(',');
- this.state.inputs = this.form.find(validateSelectors).toArray()
+ this.state.inputs = this.form
+ .find(validateSelectors)
+ .toArray()
.filter(input => !input.classList.contains(customValidationFlag))
.map(input => new GlFieldError({ input, formErrors: this }));
this.form.on('submit', GlFieldErrors.catchInvalidFormSubmit);
}
- /* Neccessary to prevent intercept and override invalid form submit
+ /* Necessary to prevent intercept and override invalid form submit
* because Safari & iOS quietly allow form submission when form is invalid
* and prevents disabling of invalid submit button by application.js */
@@ -42,7 +45,7 @@ export default class GlFieldErrors {
/* Public method for triggering validity updates manually */
updateFormValidityState() {
- this.state.inputs.forEach((field) => {
+ this.state.inputs.forEach(field => {
if (field.state.submitted) {
field.updateValidity();
}
@@ -50,8 +53,9 @@ export default class GlFieldErrors {
}
focusOnFirstInvalid() {
- const firstInvalid = this.state.inputs
- .filter(input => !input.inputDomElement.validity.valid)[0];
+ const firstInvalid = this.state.inputs.filter(
+ input => !input.inputDomElement.validity.valid,
+ )[0];
firstInvalid.inputElement.focus();
}
}
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index c74de7ac34d..f842d2d74db 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -18,7 +18,7 @@ export default class GLForm {
});
// Before we start, we should clean up any previous data for this form
this.destroy();
- // Setup the form
+ // Set up the form
this.setupForm();
this.form.data('glForm', this);
}
@@ -39,7 +39,10 @@ export default class GLForm {
this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form');
// remove notify commit author checkbox for non-commit notes
- gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button, .js-note-new-discussion'));
+ gl.utils.disableButtonIfEmptyField(
+ this.form.find('.js-note-text'),
+ this.form.find('.js-comment-button, .js-note-new-discussion'),
+ );
this.autoComplete = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
this.autoComplete.setup(this.form.find('.js-gfm-input'), this.enableGFM);
dropzoneInput(this.form);
@@ -55,11 +58,9 @@ export default class GLForm {
}
setupAutosize() {
- this.textarea.off('autosize:resized')
- .on('autosize:resized', this.setHeightData.bind(this));
+ this.textarea.off('autosize:resized').on('autosize:resized', this.setHeightData.bind(this));
- this.textarea.off('mouseup.autosize')
- .on('mouseup.autosize', this.destroyAutosize.bind(this));
+ this.textarea.off('mouseup.autosize').on('mouseup.autosize', this.destroyAutosize.bind(this));
setTimeout(() => {
autosize(this.textarea);
@@ -91,10 +92,14 @@ export default class GLForm {
addEventListeners() {
this.textarea.on('focus', function focusTextArea() {
- $(this).closest('.md-area').addClass('is-focused');
+ $(this)
+ .closest('.md-area')
+ .addClass('is-focused');
});
this.textarea.on('blur', function blurTextArea() {
- $(this).closest('.md-area').removeClass('is-focused');
+ $(this)
+ .closest('.md-area')
+ .removeClass('is-focused');
});
}
}
diff --git a/app/assets/javascripts/group.js b/app/assets/javascripts/group.js
index 4365305c168..903c838e266 100644
--- a/app/assets/javascripts/group.js
+++ b/app/assets/javascripts/group.js
@@ -1,4 +1,5 @@
import $ from 'jquery';
+import { slugifyWithHyphens } from './lib/utils/text_utility';
export default class Group {
constructor() {
@@ -7,17 +8,18 @@ export default class Group {
this.updateHandler = this.update.bind(this);
this.resetHandler = this.reset.bind(this);
if (this.groupName.val() === '') {
- this.groupPath.on('keyup', this.updateHandler);
- this.groupName.on('keydown', this.resetHandler);
+ this.groupName.on('keyup', this.updateHandler);
+ this.groupPath.on('keydown', this.resetHandler);
}
}
update() {
- this.groupName.val(this.groupPath.val());
+ const slug = slugifyWithHyphens(this.groupName.val());
+ this.groupPath.val(slug);
}
reset() {
- this.groupPath.off('keyup', this.updateHandler);
- this.groupName.off('keydown', this.resetHandler);
+ this.groupName.off('keyup', this.updateHandler);
+ this.groupPath.off('keydown', this.resetHandler);
}
}
diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js
index beaac61e887..dcda625f587 100644
--- a/app/assets/javascripts/group_avatar.js
+++ b/app/assets/javascripts/group_avatar.js
@@ -7,8 +7,9 @@ export default function groupAvatar() {
});
$('.js-group-avatar-input').on('change', function onChangeAvatarInput() {
const form = $(this).closest('form');
- // eslint-disable-next-line no-useless-escape
- const filename = $(this).val().replace(/^.*[\\\/]/, '');
+ const filename = $(this)
+ .val()
+ .replace(/^.*[\\\/]/, ''); // eslint-disable-line no-useless-escape
return form.find('.js-avatar-filename').text(filename);
});
}
diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js
index d33e3a37580..9b74560f914 100644
--- a/app/assets/javascripts/group_label_subscription.js
+++ b/app/assets/javascripts/group_label_subscription.js
@@ -23,7 +23,8 @@ export default class GroupLabelSubscription {
event.preventDefault();
const url = this.$unsubscribeButtons.attr('data-url');
- axios.post(url)
+ axios
+ .post(url)
.then(() => {
this.toggleSubscriptionButtons();
this.$unsubscribeButtons.removeAttr('data-url');
@@ -39,7 +40,8 @@ export default class GroupLabelSubscription {
this.$unsubscribeButtons.attr('data-url', url);
- axios.post(url)
+ axios
+ .post(url)
.then(() => GroupLabelSubscription.setNewTooltip($btn))
.then(() => this.toggleSubscriptionButtons())
.catch(() => flash(__('There was an error when subscribing to this label.')));
@@ -58,6 +60,8 @@ export default class GroupLabelSubscription {
const newTitle = tooltipTitles[type];
$('.js-unsubscribe-button', $button.closest('.label-actions-list'))
- .tooltip('hide').attr('title', newTitle).tooltip('_fixTitle');
+ .tooltip('hide')
+ .attr('title', newTitle)
+ .tooltip('_fixTitle');
}
}
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue
index b0765747a36..29dc2d6a8a3 100644
--- a/app/assets/javascripts/groups/components/app.vue
+++ b/app/assets/javascripts/groups/components/app.vue
@@ -2,23 +2,34 @@
/* global Flash */
import $ from 'jquery';
-import { s__ } from '~/locale';
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { s__, sprintf } from '~/locale';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import { HIDDEN_CLASS } from '~/lib/utils/constants';
import { getParameterByName } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
+import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '../event_hub';
-import { COMMON_STR } from '../constants';
+import { COMMON_STR, CONTENT_LIST_CLASS } from '../constants';
import groupsComponent from './groups.vue';
export default {
components: {
- loadingIcon,
DeprecatedModal,
groupsComponent,
+ GlLoadingIcon,
},
props: {
+ action: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ containerId: {
+ type: String,
+ required: false,
+ default: '',
+ },
store: {
type: Object,
required: true,
@@ -56,31 +67,28 @@ export default {
? COMMON_STR.GROUP_SEARCH_EMPTY
: COMMON_STR.GROUP_PROJECT_SEARCH_EMPTY;
- eventHub.$on('fetchPage', this.fetchPage);
- eventHub.$on('toggleChildren', this.toggleChildren);
- eventHub.$on('showLeaveGroupModal', this.showLeaveGroupModal);
- eventHub.$on('updatePagination', this.updatePagination);
- eventHub.$on('updateGroups', this.updateGroups);
+ eventHub.$on(`${this.action}fetchPage`, this.fetchPage);
+ eventHub.$on(`${this.action}toggleChildren`, this.toggleChildren);
+ eventHub.$on(`${this.action}showLeaveGroupModal`, this.showLeaveGroupModal);
+ eventHub.$on(`${this.action}updatePagination`, this.updatePagination);
+ eventHub.$on(`${this.action}updateGroups`, this.updateGroups);
},
mounted() {
this.fetchAllGroups();
+
+ if (this.containerId) {
+ this.containerEl = document.getElementById(this.containerId);
+ }
},
beforeDestroy() {
- eventHub.$off('fetchPage', this.fetchPage);
- eventHub.$off('toggleChildren', this.toggleChildren);
- eventHub.$off('showLeaveGroupModal', this.showLeaveGroupModal);
- eventHub.$off('updatePagination', this.updatePagination);
- eventHub.$off('updateGroups', this.updateGroups);
+ eventHub.$off(`${this.action}fetchPage`, this.fetchPage);
+ eventHub.$off(`${this.action}toggleChildren`, this.toggleChildren);
+ eventHub.$off(`${this.action}showLeaveGroupModal`, this.showLeaveGroupModal);
+ eventHub.$off(`${this.action}updatePagination`, this.updatePagination);
+ eventHub.$off(`${this.action}updateGroups`, this.updateGroups);
},
methods: {
- fetchGroups({
- parentId,
- page,
- filterGroupsBy,
- sortBy,
- archived,
- updatePagination,
- }) {
+ fetchGroups({ parentId, page, filterGroupsBy, sortBy, archived, updatePagination }) {
return this.service
.getGroups(parentId, page, filterGroupsBy, sortBy, archived)
.then(res => {
@@ -165,13 +173,13 @@ export default {
}
},
showLeaveGroupModal(group, parentGroup) {
+ const { fullName } = group;
this.targetGroup = group;
this.targetParentGroup = parentGroup;
this.showModal = true;
- this.groupLeaveConfirmationMessage = s__(
- `GroupsTree|Are you sure you want to leave the "${
- group.fullName
- }" group?`,
+ this.groupLeaveConfirmationMessage = sprintf(
+ s__('GroupsTree|Are you sure you want to leave the "%{fullName}" group?'),
+ { fullName },
);
},
hideLeaveGroupModal() {
@@ -197,16 +205,35 @@ export default {
this.targetGroup.isBeingRemoved = false;
});
},
+ showEmptyState() {
+ const { containerEl } = this;
+ const contentListEl = containerEl.querySelector(CONTENT_LIST_CLASS);
+ const emptyStateEl = containerEl.querySelector('.empty-state');
+
+ if (contentListEl) {
+ contentListEl.remove();
+ }
+
+ if (emptyStateEl) {
+ emptyStateEl.classList.remove(HIDDEN_CLASS);
+ }
+ },
updatePagination(headers) {
this.store.setPaginationInfo(headers);
},
updateGroups(groups, fromSearch) {
- this.isSearchEmpty = groups ? groups.length === 0 : false;
+ const hasGroups = groups && groups.length > 0;
+ this.isSearchEmpty = !hasGroups;
+
if (fromSearch) {
this.store.setSearchedGroups(groups);
} else {
this.store.setGroups(groups);
}
+
+ if (this.action && !hasGroups && !fromSearch) {
+ this.showEmptyState();
+ }
},
},
};
@@ -214,11 +241,11 @@ export default {
<template>
<div>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
:label="s__('GroupsTree|Loading groups')"
+ :size="2"
class="loading-animation prepend-top-20"
- size="2"
/>
<groups-component
v-if="!isLoading"
@@ -226,6 +253,7 @@ export default {
:search-empty="isSearchEmpty"
:search-empty-message="searchEmptyMessage"
:page-info="pageInfo"
+ :action="action"
/>
<deprecated-modal
v-show="showModal"
diff --git a/app/assets/javascripts/groups/components/group_folder.vue b/app/assets/javascripts/groups/components/group_folder.vue
index 647c9d0046d..bcc7a638346 100644
--- a/app/assets/javascripts/groups/components/group_folder.vue
+++ b/app/assets/javascripts/groups/components/group_folder.vue
@@ -11,8 +11,12 @@ export default {
},
groups: {
type: Array,
+ required: true,
+ },
+ action: {
+ type: String,
required: false,
- default: () => ([]),
+ default: '',
},
},
computed: {
@@ -37,6 +41,7 @@ export default {
:key="index"
:group="group"
:parent-group="parentGroup"
+ :action="action"
/>
<li
v-if="hasMoreChildren"
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 2b9e2a929fc..44d6fa26914 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -30,6 +30,11 @@ export default {
type: Object,
required: true,
},
+ action: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
groupDomId() {
@@ -56,10 +61,12 @@ export default {
methods: {
onClickRowGroup(e) {
const NO_EXPAND_CLS = 'no-expand';
- if (!(e.target.classList.contains(NO_EXPAND_CLS) ||
- e.target.parentElement.classList.contains(NO_EXPAND_CLS))) {
+ const targetClasses = e.target.classList;
+ const parentElClasses = e.target.parentElement.classList;
+
+ if (!(targetClasses.contains(NO_EXPAND_CLS) || parentElClasses.contains(NO_EXPAND_CLS))) {
if (this.hasChildren) {
- eventHub.$emit('toggleChildren', this.group);
+ eventHub.$emit(`${this.action}toggleChildren`, this.group);
} else {
visitUrl(this.group.relativePath);
}
@@ -93,7 +100,7 @@ export default {
</div>
<div
:class="{ 'content-loading': group.isChildrenLoading }"
- class="avatar-container s24 d-none d-sm-block"
+ class="avatar-container s24 d-none d-sm-flex"
>
<a
:href="group.relativePath"
@@ -158,6 +165,7 @@ export default {
v-if="group.isOpen && hasChildren"
:parent-group="group"
:groups="group.children"
+ :action="action"
/>
</li>
</template>
diff --git a/app/assets/javascripts/groups/components/groups.vue b/app/assets/javascripts/groups/components/groups.vue
index adde8c8cdb3..81b2e5ea37b 100644
--- a/app/assets/javascripts/groups/components/groups.vue
+++ b/app/assets/javascripts/groups/components/groups.vue
@@ -1,57 +1,66 @@
<script>
- import tablePagination from '~/vue_shared/components/table_pagination.vue';
- import eventHub from '../event_hub';
- import { getParameterByName } from '../../lib/utils/common_utils';
+import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
+import eventHub from '../event_hub';
+import { getParameterByName } from '../../lib/utils/common_utils';
- export default {
- components: {
- tablePagination,
+export default {
+ components: {
+ PaginationLinks,
+ },
+ props: {
+ groups: {
+ type: Array,
+ required: true,
},
- props: {
- groups: {
- type: Array,
- required: true,
- },
- pageInfo: {
- type: Object,
- required: true,
- },
- searchEmpty: {
- type: Boolean,
- required: true,
- },
- searchEmptyMessage: {
- type: String,
- required: true,
- },
+ pageInfo: {
+ type: Object,
+ required: true,
},
- methods: {
- change(page) {
- const filterGroupsParam = getParameterByName('filter_groups');
- const sortParam = getParameterByName('sort');
- const archivedParam = getParameterByName('archived');
- eventHub.$emit('fetchPage', page, filterGroupsParam, sortParam, archivedParam);
- },
+ searchEmpty: {
+ type: Boolean,
+ required: true,
},
- };
+ searchEmptyMessage: {
+ type: String,
+ required: true,
+ },
+ action: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ methods: {
+ change(page) {
+ const filterGroupsParam = getParameterByName('filter_groups');
+ const sortParam = getParameterByName('sort');
+ const archivedParam = getParameterByName('archived');
+ eventHub.$emit(`${this.action}fetchPage`, page, filterGroupsParam, sortParam, archivedParam);
+ },
+ },
+};
</script>
<template>
- <div class="groups-list-tree-container">
+ <div class="groups-list-tree-container qa-groups-list-tree-container">
<div
v-if="searchEmpty"
class="has-no-search-results"
>
{{ searchEmptyMessage }}
</div>
- <group-folder
- v-if="!searchEmpty"
- :groups="groups"
- />
- <table-pagination
- v-if="!searchEmpty"
- :change="change"
- :page-info="pageInfo"
- />
+ <template
+ v-else
+ >
+ <group-folder
+ :groups="groups"
+ :action="action"
+ />
+ <pagination-links
+ :change="change"
+ :page-info="pageInfo"
+ class="d-flex justify-content-center prepend-top-default"
+ />
+ </template>
</div>
</template>
diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue
index 24eec4901ec..c1783d5ce25 100644
--- a/app/assets/javascripts/groups/components/item_actions.vue
+++ b/app/assets/javascripts/groups/components/item_actions.vue
@@ -21,6 +21,11 @@ export default {
type: Object,
required: true,
},
+ action: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
leaveBtnTitle() {
@@ -32,7 +37,7 @@ export default {
},
methods: {
onLeaveGroup() {
- eventHub.$emit('showLeaveGroupModal', this.group, this.parentGroup);
+ eventHub.$emit(`${this.action}showLeaveGroupModal`, this.group, this.parentGroup);
},
},
};
@@ -41,8 +46,8 @@ export default {
<template>
<div class="controls">
<a
- v-tooltip
v-if="group.canEdit"
+ v-tooltip
:href="group.editPath"
:title="editBtnTitle"
:aria-label="editBtnTitle"
@@ -52,8 +57,8 @@ export default {
<icon name="settings"/>
</a>
<a
- v-tooltip
v-if="group.canLeave"
+ v-tooltip
:href="group.leavePath"
:title="leaveBtnTitle"
:aria-label="leaveBtnTitle"
diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue
index 87ab5480c15..829924ba63c 100644
--- a/app/assets/javascripts/groups/components/item_stats.vue
+++ b/app/assets/javascripts/groups/components/item_stats.vue
@@ -1,44 +1,44 @@
<script>
- import icon from '~/vue_shared/components/icon.vue';
- import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
- import {
- ITEM_TYPE,
- VISIBILITY_TYPE_ICON,
- GROUP_VISIBILITY_TYPE,
- PROJECT_VISIBILITY_TYPE,
- } from '../constants';
- import itemStatsValue from './item_stats_value.vue';
+import icon from '~/vue_shared/components/icon.vue';
+import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import {
+ ITEM_TYPE,
+ VISIBILITY_TYPE_ICON,
+ GROUP_VISIBILITY_TYPE,
+ PROJECT_VISIBILITY_TYPE,
+} from '../constants';
+import itemStatsValue from './item_stats_value.vue';
- export default {
- components: {
- icon,
- timeAgoTooltip,
- itemStatsValue,
+export default {
+ components: {
+ icon,
+ timeAgoTooltip,
+ itemStatsValue,
+ },
+ props: {
+ item: {
+ type: Object,
+ required: true,
},
- props: {
- item: {
- type: Object,
- required: true,
- },
+ },
+ computed: {
+ visibilityIcon() {
+ return VISIBILITY_TYPE_ICON[this.item.visibility];
},
- computed: {
- visibilityIcon() {
- return VISIBILITY_TYPE_ICON[this.item.visibility];
- },
- visibilityTooltip() {
- if (this.item.type === ITEM_TYPE.GROUP) {
- return GROUP_VISIBILITY_TYPE[this.item.visibility];
- }
- return PROJECT_VISIBILITY_TYPE[this.item.visibility];
- },
- isProject() {
- return this.item.type === ITEM_TYPE.PROJECT;
- },
- isGroup() {
- return this.item.type === ITEM_TYPE.GROUP;
- },
+ visibilityTooltip() {
+ if (this.item.type === ITEM_TYPE.GROUP) {
+ return GROUP_VISIBILITY_TYPE[this.item.visibility];
+ }
+ return PROJECT_VISIBILITY_TYPE[this.item.visibility];
},
- };
+ isProject() {
+ return this.item.type === ITEM_TYPE.PROJECT;
+ },
+ isGroup() {
+ return this.item.type === ITEM_TYPE.GROUP;
+ },
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/groups/components/item_stats_value.vue b/app/assets/javascripts/groups/components/item_stats_value.vue
index ef9f2bca76c..c542ca946d3 100644
--- a/app/assets/javascripts/groups/components/item_stats_value.vue
+++ b/app/assets/javascripts/groups/components/item_stats_value.vue
@@ -1,52 +1,52 @@
<script>
- import tooltip from '~/vue_shared/directives/tooltip';
- import icon from '~/vue_shared/components/icon.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import icon from '~/vue_shared/components/icon.vue';
- export default {
- components: {
- icon,
+export default {
+ components: {
+ icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ title: {
+ type: String,
+ required: false,
+ default: '',
},
- directives: {
- tooltip,
+ cssClass: {
+ type: String,
+ required: false,
+ default: '',
},
- props: {
- title: {
- type: String,
- required: false,
- default: '',
- },
- cssClass: {
- type: String,
- required: false,
- default: '',
- },
- iconName: {
- type: String,
- required: true,
- },
- tooltipPlacement: {
- type: String,
- required: false,
- default: 'bottom',
- },
- /**
- * value could either be number or string
- * as `memberCount` is always passed as string
- * while `subgroupCount` & `projectCount`
- * are always number
- */
- value: {
- type: [Number, String],
- required: false,
- default: '',
- },
+ iconName: {
+ type: String,
+ required: true,
},
- computed: {
- isValuePresent() {
- return this.value !== '';
- },
+ tooltipPlacement: {
+ type: String,
+ required: false,
+ default: 'bottom',
},
- };
+ /**
+ * value could either be number or string
+ * as `memberCount` is always passed as string
+ * while `subgroupCount` & `projectCount`
+ * are always number
+ */
+ value: {
+ type: [Number, String],
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ isValuePresent() {
+ return this.value !== '';
+ },
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/groups/constants.js b/app/assets/javascripts/groups/constants.js
index b8baed682f5..9c246cf3ba6 100644
--- a/app/assets/javascripts/groups/constants.js
+++ b/app/assets/javascripts/groups/constants.js
@@ -2,13 +2,23 @@ import { __, s__ } from '../locale';
export const MAX_CHILDREN_COUNT = 20;
+export const ACTIVE_TAB_SUBGROUPS_AND_PROJECTS = 'subgroups_and_projects';
+export const ACTIVE_TAB_SHARED = 'shared';
+export const ACTIVE_TAB_ARCHIVED = 'archived';
+
+export const GROUPS_LIST_HOLDER_CLASS = '.js-groups-list-holder';
+export const GROUPS_FILTER_FORM_CLASS = '.js-group-filter-form';
+export const CONTENT_LIST_CLASS = '.content-list';
+
export const COMMON_STR = {
FAILURE: __('An error occurred. Please try again.'),
- LEAVE_FORBIDDEN: s__('GroupsTree|Failed to leave the group. Please make sure you are not the only owner.'),
+ LEAVE_FORBIDDEN: s__(
+ 'GroupsTree|Failed to leave the group. Please make sure you are not the only owner.',
+ ),
LEAVE_BTN_TITLE: s__('GroupsTree|Leave this group'),
EDIT_BTN_TITLE: s__('GroupsTree|Edit group'),
- GROUP_SEARCH_EMPTY: s__('GroupsTree|Sorry, no groups matched your search'),
- GROUP_PROJECT_SEARCH_EMPTY: s__('GroupsTree|Sorry, no groups or projects matched your search'),
+ GROUP_SEARCH_EMPTY: s__('GroupsTree|No groups matched your search'),
+ GROUP_PROJECT_SEARCH_EMPTY: s__('GroupsTree|No groups or projects matched your search'),
};
export const ITEM_TYPE = {
@@ -17,8 +27,12 @@ export const ITEM_TYPE = {
};
export const GROUP_VISIBILITY_TYPE = {
- public: __('Public - The group and any public projects can be viewed without any authentication.'),
- internal: __('Internal - The group and any internal projects can be viewed by any logged in user.'),
+ public: __(
+ 'Public - The group and any public projects can be viewed without any authentication.',
+ ),
+ internal: __(
+ 'Internal - The group and any internal projects can be viewed by any logged in user.',
+ ),
private: __('Private - The group and its projects can only be viewed by members.'),
};
diff --git a/app/assets/javascripts/groups/groups_filterable_list.js b/app/assets/javascripts/groups/groups_filterable_list.js
index e6db1746487..693519729ac 100644
--- a/app/assets/javascripts/groups/groups_filterable_list.js
+++ b/app/assets/javascripts/groups/groups_filterable_list.js
@@ -4,13 +4,23 @@ import eventHub from './event_hub';
import { normalizeHeaders, getParameterByName } from '../lib/utils/common_utils';
export default class GroupFilterableList extends FilterableList {
- constructor({ form, filter, holder, filterEndpoint, pagePath, dropdownSel, filterInputField }) {
+ constructor({
+ form,
+ filter,
+ holder,
+ filterEndpoint,
+ pagePath,
+ dropdownSel,
+ filterInputField,
+ action,
+ }) {
super(form, filter, holder, filterInputField);
this.form = form;
this.filterEndpoint = filterEndpoint;
this.pagePath = pagePath;
this.filterInputField = filterInputField;
this.$dropdown = $(dropdownSel);
+ this.action = action;
}
getFilterEndpoint() {
@@ -20,15 +30,16 @@ export default class GroupFilterableList extends FilterableList {
getPagePath(queryData) {
const params = queryData ? $.param(queryData) : '';
const queryString = params ? `?${params}` : '';
- return `${this.pagePath}${queryString}`;
+ const path = this.pagePath || window.location.pathname;
+ return `${path}${queryString}`;
}
bindEvents() {
super.bindEvents();
- this.onFilterOptionClikWrapper = this.onOptionClick.bind(this);
+ this.onFilterOptionClickWrapper = this.onOptionClick.bind(this);
- this.$dropdown.on('click', 'a', this.onFilterOptionClikWrapper);
+ this.$dropdown.on('click', 'a', this.onFilterOptionClickWrapper);
}
onFilterInput() {
@@ -53,7 +64,12 @@ export default class GroupFilterableList extends FilterableList {
}
setDefaultFilterOption() {
- const defaultOption = $.trim(this.$dropdown.find('.dropdown-menu li.js-filter-sort-order a').first().text());
+ const defaultOption = $.trim(
+ this.$dropdown
+ .find('.dropdown-menu li.js-filter-sort-order a')
+ .first()
+ .text(),
+ );
this.$dropdown.find('.dropdown-label').text(defaultOption);
}
@@ -65,11 +81,19 @@ export default class GroupFilterableList extends FilterableList {
// Get type of option selected from dropdown
const currentTargetClassList = e.currentTarget.parentElement.classList;
const isOptionFilterBySort = currentTargetClassList.contains('js-filter-sort-order');
- const isOptionFilterByArchivedProjects = currentTargetClassList.contains('js-filter-archived-projects');
+ const isOptionFilterByArchivedProjects = currentTargetClassList.contains(
+ 'js-filter-archived-projects',
+ );
// Get option query param, also preserve currently applied query param
- const sortParam = getParameterByName('sort', isOptionFilterBySort ? e.currentTarget.href : window.location.href);
- const archivedParam = getParameterByName('archived', isOptionFilterByArchivedProjects ? e.currentTarget.href : window.location.href);
+ const sortParam = getParameterByName(
+ 'sort',
+ isOptionFilterBySort ? e.currentTarget.href : window.location.href,
+ );
+ const archivedParam = getParameterByName(
+ 'archived',
+ isOptionFilterByArchivedProjects ? e.currentTarget.href : window.location.href,
+ );
if (sortParam) {
queryData.sort = sortParam;
@@ -86,7 +110,9 @@ export default class GroupFilterableList extends FilterableList {
this.$dropdown.find('.dropdown-label').text($.trim(e.currentTarget.text));
this.$dropdown.find('.dropdown-menu li.js-filter-sort-order a').removeClass('is-active');
} else if (isOptionFilterByArchivedProjects) {
- this.$dropdown.find('.dropdown-menu li.js-filter-archived-projects a').removeClass('is-active');
+ this.$dropdown
+ .find('.dropdown-menu li.js-filter-archived-projects a')
+ .removeClass('is-active');
}
$(e.target).addClass('is-active');
@@ -98,11 +124,19 @@ export default class GroupFilterableList extends FilterableList {
onFilterSuccess(res, queryData) {
const currentPath = this.getPagePath(queryData);
- window.history.replaceState({
- page: currentPath,
- }, document.title, currentPath);
-
- eventHub.$emit('updateGroups', res.data, Object.prototype.hasOwnProperty.call(queryData, this.filterInputField));
- eventHub.$emit('updatePagination', normalizeHeaders(res.headers));
+ window.history.replaceState(
+ {
+ page: currentPath,
+ },
+ document.title,
+ currentPath,
+ );
+
+ eventHub.$emit(
+ `${this.action}updateGroups`,
+ res.data,
+ Object.prototype.hasOwnProperty.call(queryData, this.filterInputField),
+ );
+ eventHub.$emit(`${this.action}updatePagination`, normalizeHeaders(res.headers));
}
}
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index 83a9008a94b..0f68f05b523 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -7,18 +7,26 @@ import GroupsService from './service/groups_service';
import groupsApp from './components/app.vue';
import groupFolderComponent from './components/group_folder.vue';
import groupItemComponent from './components/group_item.vue';
+import { GROUPS_LIST_HOLDER_CLASS, CONTENT_LIST_CLASS } from './constants';
Vue.use(Translate);
-export default () => {
- const el = document.getElementById('js-groups-tree');
+export default (containerId = 'js-groups-tree', endpoint, action = '') => {
+ const containerEl = document.getElementById(containerId);
+ let dataEl;
// Don't do anything if element doesn't exist (No groups)
// This is for when the user enters directly to the page via URL
- if (!el) {
+ if (!containerEl) {
return;
}
+ const el = action ? containerEl.querySelector(GROUPS_LIST_HOLDER_CLASS) : containerEl;
+
+ if (action) {
+ dataEl = containerEl.querySelector(CONTENT_LIST_CLASS);
+ }
+
Vue.component('group-folder', groupFolderComponent);
Vue.component('group-item', groupItemComponent);
@@ -29,20 +37,26 @@ export default () => {
groupsApp,
},
data() {
- const { dataset } = this.$options.el;
+ const { dataset } = dataEl || this.$options.el;
const hideProjects = dataset.hideProjects === 'true';
+ const service = new GroupsService(endpoint || dataset.endpoint);
const store = new GroupsStore(hideProjects);
- const service = new GroupsService(dataset.endpoint);
return {
+ action,
store,
service,
hideProjects,
loading: true,
+ containerId,
};
},
beforeMount() {
- const { dataset } = this.$options.el;
+ if (this.action) {
+ return;
+ }
+
+ const { dataset } = dataEl || this.$options.el;
let groupFilterList = null;
const form = document.querySelector(dataset.formSel);
const filter = document.querySelector(dataset.filterSel);
@@ -52,10 +66,11 @@ export default () => {
form,
filter,
holder,
- filterEndpoint: dataset.endpoint,
+ filterEndpoint: endpoint || dataset.endpoint,
pagePath: dataset.path,
dropdownSel: dataset.dropdownSel,
filterInputField: 'filter',
+ action: this.action,
};
groupFilterList = new GroupFilterableList(opts);
@@ -64,9 +79,11 @@ export default () => {
render(createElement) {
return createElement('groups-app', {
props: {
+ action: this.action,
store: this.store,
service: this.service,
hideProjects: this.hideProjects,
+ containerId: this.containerId,
},
});
},
diff --git a/app/assets/javascripts/groups/new_group_child.js b/app/assets/javascripts/groups/new_group_child.js
index a120d501e35..012177479c6 100644
--- a/app/assets/javascripts/groups/new_group_child.js
+++ b/app/assets/javascripts/groups/new_group_child.js
@@ -37,20 +37,22 @@ export default class NewGroupChild {
getDroplabConfig() {
return {
- InputSetter: [{
- input: this.newGroupChildButton,
- valueAttribute: 'data-value',
- inputAttribute: 'data-action',
- }, {
- input: this.newGroupChildButton,
- valueAttribute: 'data-text',
- }],
+ InputSetter: [
+ {
+ input: this.newGroupChildButton,
+ valueAttribute: 'data-value',
+ inputAttribute: 'data-action',
+ },
+ {
+ input: this.newGroupChildButton,
+ valueAttribute: 'data-text',
+ },
+ ],
};
}
bindEvents() {
- this.newGroupChildButton
- .addEventListener('click', this.onClickNewGroupChildButton.bind(this));
+ this.newGroupChildButton.addEventListener('click', this.onClickNewGroupChildButton.bind(this));
}
onClickNewGroupChildButton(e) {
diff --git a/app/assets/javascripts/groups/store/groups_store.js b/app/assets/javascripts/groups/store/groups_store.js
index 4a7569078a1..16f95d5a0cc 100644
--- a/app/assets/javascripts/groups/store/groups_store.js
+++ b/app/assets/javascripts/groups/store/groups_store.js
@@ -17,13 +17,14 @@ export default class GroupsStore {
}
setSearchedGroups(rawGroups) {
- const formatGroups = groups => groups.map((group) => {
- const formattedGroup = this.formatGroupItem(group);
- if (formattedGroup.children && formattedGroup.children.length) {
- formattedGroup.children = formatGroups(formattedGroup.children);
- }
- return formattedGroup;
- });
+ const formatGroups = groups =>
+ groups.map(group => {
+ const formattedGroup = this.formatGroupItem(group);
+ if (formattedGroup.children && formattedGroup.children.length) {
+ formattedGroup.children = formatGroups(formattedGroup.children);
+ }
+ return formattedGroup;
+ });
if (rawGroups && rawGroups.length) {
this.state.groups = formatGroups(rawGroups);
@@ -62,10 +63,10 @@ export default class GroupsStore {
formatGroupItem(rawGroupItem) {
const groupChildren = rawGroupItem.children || [];
- const groupIsOpen = (groupChildren.length > 0) || false;
- const childrenCount = this.hideProjects ?
- rawGroupItem.subgroup_count :
- rawGroupItem.children_count;
+ const groupIsOpen = groupChildren.length > 0 || false;
+ const childrenCount = this.hideProjects
+ ? rawGroupItem.subgroup_count
+ : rawGroupItem.children_count;
return {
id: rawGroupItem.id,
diff --git a/app/assets/javascripts/groups/transfer_dropdown.js b/app/assets/javascripts/groups/transfer_dropdown.js
index e0eb118ddf7..26510fcdb2a 100644
--- a/app/assets/javascripts/groups/transfer_dropdown.js
+++ b/app/assets/javascripts/groups/transfer_dropdown.js
@@ -22,7 +22,7 @@ export default class TransferDropdown {
search: { fields: ['text'] },
data: extraOptions.concat(this.data),
text: item => item.text,
- clicked: (options) => {
+ clicked: options => {
const { e } = options;
e.preventDefault();
this.assignSelected(options.selectedObj);
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index e37fc5c4be6..b4a3037c1b7 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -23,7 +23,7 @@ export default function groupsSelect() {
axios[params.type.toLowerCase()](params.url, {
params: params.data,
})
- .then((res) => {
+ .then(res => {
const results = res.data || [];
const headers = normalizeHeaders(res.headers);
const currentPage = parseInt(headers['X-PAGE'], 10) || 0;
@@ -36,7 +36,8 @@ export default function groupsSelect() {
more,
},
});
- }).catch(params.error);
+ })
+ .catch(params.error);
},
data(search, page) {
return {
@@ -68,7 +69,9 @@ export default function groupsSelect() {
}
},
formatResult(object) {
- return `<div class='group-result'> <div class='group-name'>${object.full_name}</div> <div class='group-path'>${object.full_path}</div> </div>`;
+ return `<div class='group-result'> <div class='group-name'>${
+ object.full_name
+ }</div> <div class='group-path'>${object.full_path}</div> </div>`;
},
formatSelection(object) {
return object.full_name;
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index 4ae3a714bee..175d0b8498b 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -1,5 +1,9 @@
import $ from 'jquery';
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
import { highCountTrim } from '~/lib/utils/text_utility';
+import SetStatusModalTrigger from './set_status_modal/set_status_modal_trigger.vue';
+import SetStatusModalWrapper from './set_status_modal/set_status_modal_wrapper.vue';
/**
* Updates todo counter when todos are toggled.
@@ -17,3 +21,54 @@ export default function initTodoToggle() {
$todoPendingCount.toggleClass('hidden', parsedCount === 0);
});
}
+
+document.addEventListener('DOMContentLoaded', () => {
+ const setStatusModalTriggerEl = document.querySelector('.js-set-status-modal-trigger');
+ const setStatusModalWrapperEl = document.querySelector('.js-set-status-modal-wrapper');
+
+ if (setStatusModalTriggerEl || setStatusModalWrapperEl) {
+ Vue.use(Translate);
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: setStatusModalTriggerEl,
+ data() {
+ const { hasStatus } = this.$options.el.dataset;
+
+ return {
+ hasStatus: hasStatus === 'true',
+ };
+ },
+ render(createElement) {
+ return createElement(SetStatusModalTrigger, {
+ props: {
+ hasStatus: this.hasStatus,
+ },
+ });
+ },
+ });
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: setStatusModalWrapperEl,
+ data() {
+ const { currentEmoji, currentMessage } = this.$options.el.dataset;
+
+ return {
+ currentEmoji,
+ currentMessage,
+ };
+ },
+ render(createElement) {
+ const { currentEmoji, currentMessage } = this;
+
+ return createElement(SetStatusModalWrapper, {
+ props: {
+ currentEmoji,
+ currentMessage,
+ },
+ });
+ },
+ });
+ }
+});
diff --git a/app/assets/javascripts/helpers/avatar_helper.js b/app/assets/javascripts/helpers/avatar_helper.js
index d3b1d0f11fd..35ac7b2629c 100644
--- a/app/assets/javascripts/helpers/avatar_helper.js
+++ b/app/assets/javascripts/helpers/avatar_helper.js
@@ -19,7 +19,9 @@ export function renderIdenticon(entity, options = {}) {
const bgClass = getIdenticonBackgroundClass(entity.id);
const title = getIdenticonTitle(entity.name);
- return `<div class="avatar identicon ${_.escape(sizeClass)} ${_.escape(bgClass)}">${_.escape(title)}</div>`;
+ return `<div class="avatar identicon ${_.escape(sizeClass)} ${_.escape(bgClass)}">${_.escape(
+ title,
+ )}</div>`;
}
export function renderAvatar(entity, options = {}) {
diff --git a/app/assets/javascripts/ide/components/branches/search_list.vue b/app/assets/javascripts/ide/components/branches/search_list.vue
index 6db7b9d6b0e..600e1063f89 100644
--- a/app/assets/javascripts/ide/components/branches/search_list.vue
+++ b/app/assets/javascripts/ide/components/branches/search_list.vue
@@ -1,15 +1,15 @@
<script>
import { mapActions, mapState } from 'vuex';
import _ from 'underscore';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import Item from './item.vue';
export default {
components: {
- LoadingIcon,
Item,
Icon,
+ GlLoadingIcon,
},
data() {
return {
@@ -62,8 +62,8 @@ export default {
<div class="position-relative">
<input
ref="searchInput"
- :placeholder="__('Search branches')"
v-model="search"
+ :placeholder="__('Search branches')"
type="search"
class="form-control dropdown-input-field"
@input="searchBranches"
@@ -76,10 +76,10 @@ export default {
</div>
</div>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="2"
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
- size="2"
/>
<ul
v-else
diff --git a/app/assets/javascripts/ide/components/changed_file_icon.vue b/app/assets/javascripts/ide/components/changed_file_icon.vue
deleted file mode 100644
index 720ae11aaa6..00000000000
--- a/app/assets/javascripts/ide/components/changed_file_icon.vue
+++ /dev/null
@@ -1,90 +0,0 @@
-<script>
-import tooltip from '~/vue_shared/directives/tooltip';
-import Icon from '~/vue_shared/components/icon.vue';
-import { pluralize } from '~/lib/utils/text_utility';
-import { __, sprintf } from '~/locale';
-import { getCommitIconMap } from '../utils';
-
-export default {
- components: {
- Icon,
- },
- directives: {
- tooltip,
- },
- props: {
- file: {
- type: Object,
- required: true,
- },
- showTooltip: {
- type: Boolean,
- required: false,
- default: false,
- },
- showStagedIcon: {
- type: Boolean,
- required: false,
- default: false,
- },
- forceModifiedIcon: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- changedIcon() {
- const suffix = !this.file.changed && this.file.staged && !this.showStagedIcon ? '-solid' : '';
-
- if (this.forceModifiedIcon) return `file-modified${suffix}`;
-
- return `${getCommitIconMap(this.file).icon}${suffix}`;
- },
- changedIconClass() {
- return `ide-${this.changedIcon} float-left`;
- },
- tooltipTitle() {
- if (!this.showTooltip) return undefined;
-
- const type = this.file.tempFile ? 'addition' : 'modification';
-
- if (this.file.changed && !this.file.staged) {
- return sprintf(__('Unstaged %{type}'), {
- type,
- });
- } else if (!this.file.changed && this.file.staged) {
- return sprintf(__('Staged %{type}'), {
- type,
- });
- } else if (this.file.changed && this.file.staged) {
- return sprintf(__('Unstaged and staged %{type}'), {
- type: pluralize(type),
- });
- }
-
- return undefined;
- },
- showIcon() {
- return this.file.changed || this.file.tempFile || this.file.staged || this.file.deleted;
- },
- },
-};
-</script>
-
-<template>
- <span
- v-tooltip
- :title="tooltipTitle"
- data-container="body"
- data-placement="right"
- class="ide-file-changed-icon"
- >
- <icon
- v-if="showIcon"
- :name="changedIcon"
- :size="12"
- :css-classes="changedIconClass"
- />
- </span>
-</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue
new file mode 100644
index 00000000000..b0e60edcbe5
--- /dev/null
+++ b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue
@@ -0,0 +1,79 @@
+<script>
+import $ from 'jquery';
+import { mapActions } from 'vuex';
+import { __ } from '~/locale';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue';
+
+export default {
+ components: {
+ FileIcon,
+ ChangedFileIcon,
+ },
+ props: {
+ activeFile: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ activeButtonText() {
+ return this.activeFile.staged ? __('Unstage') : __('Stage');
+ },
+ isStaged() {
+ return !this.activeFile.changed && this.activeFile.staged;
+ },
+ },
+ methods: {
+ ...mapActions(['stageChange', 'unstageChange']),
+ actionButtonClicked() {
+ if (this.activeFile.staged) {
+ this.unstageChange(this.activeFile.path);
+ } else {
+ this.stageChange(this.activeFile.path);
+ }
+ },
+ showDiscardModal() {
+ $(document.getElementById(`discard-file-${this.activeFile.path}`)).modal('show');
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="d-flex ide-commit-editor-header align-items-center">
+ <file-icon
+ :file-name="activeFile.name"
+ :size="16"
+ class="mr-2"
+ />
+ <strong class="mr-2">
+ {{ activeFile.path }}
+ </strong>
+ <changed-file-icon
+ :file="activeFile"
+ class="ml-0"
+ />
+ <div class="ml-auto">
+ <button
+ v-if="!isStaged"
+ type="button"
+ class="btn btn-remove btn-inverted append-right-8"
+ @click="showDiscardModal"
+ >
+ {{ __('Discard') }}
+ </button>
+ <button
+ :class="{
+ 'btn-success': !isStaged,
+ 'btn-warning': isStaged
+ }"
+ type="button"
+ class="btn btn-inverted"
+ @click="actionButtonClicked"
+ >
+ {{ activeButtonText }}
+ </button>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
index ee8eb206980..802827fce76 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
@@ -117,7 +117,7 @@ export default {
<button
:disabled="!hasChanges"
type="button"
- class="btn btn-primary btn-sm btn-block"
+ class="btn btn-primary btn-sm btn-block qa-begin-commit-button"
@click="toggleIsSmall"
>
{{ __('Commit…') }}
@@ -147,7 +147,7 @@ export default {
<loading-button
:loading="submitCommitLoading"
:label="commitButtonText"
- container-class="btn btn-success btn-sm float-left"
+ container-class="btn btn-success btn-sm float-left qa-commit-button"
@click="commitChanges"
/>
<button
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
index d0fb0e3d99e..3e3539e364b 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
@@ -1,7 +1,9 @@
<script>
+import $ from 'jquery';
import { mapActions } from 'vuex';
import { __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import ListItem from './list_item.vue';
@@ -9,6 +11,7 @@ export default {
components: {
Icon,
ListItem,
+ GlModal,
},
directives: {
tooltip,
@@ -56,6 +59,11 @@ export default {
type: String,
required: true,
},
+ emptyStateText: {
+ type: String,
+ required: false,
+ default: __('No changes'),
+ },
},
computed: {
titleText() {
@@ -68,11 +76,19 @@ export default {
},
},
methods: {
- ...mapActions(['stageAllChanges', 'unstageAllChanges']),
+ ...mapActions(['stageAllChanges', 'unstageAllChanges', 'discardAllChanges']),
actionBtnClicked() {
this[this.action]();
+
+ $(this.$refs.actionBtn).tooltip('hide');
+ },
+ openDiscardModal() {
+ $('#discard-all-changes').modal('show');
},
},
+ discardModalText: __(
+ "You will loose all the unstaged changes you've made in this project. This action cannot be undone.",
+ ),
};
</script>
@@ -81,27 +97,32 @@ export default {
class="ide-commit-list-container"
>
<header
- class="multi-file-commit-panel-header"
+ class="multi-file-commit-panel-header d-flex mb-0"
>
<div
- class="multi-file-commit-panel-header-title"
+ class="d-flex align-items-center flex-fill"
>
<icon
v-once
:name="iconName"
:size="18"
+ class="append-right-8"
/>
- {{ titleText }}
+ <strong>
+ {{ titleText }}
+ </strong>
<div class="d-flex ml-auto">
<button
+ ref="actionBtn"
v-tooltip
- v-show="filesLength"
+ :title="actionBtnText"
+ :aria-label="actionBtnText"
+ :disabled="!filesLength"
:class="{
- 'd-flex': filesLength
+ 'disabled-content': !filesLength
}"
- :title="actionBtnText"
type="button"
- class="btn btn-default ide-staged-action-btn p-0 order-1 align-items-center"
+ class="d-flex ide-staged-action-btn p-0 border-0 align-items-center"
data-placement="bottom"
data-container="body"
data-boundary="viewport"
@@ -109,18 +130,32 @@ export default {
>
<icon
:name="actionBtnIcon"
- :size="12"
+ :size="16"
class="ml-auto mr-auto"
/>
</button>
- <span
+ <button
+ v-if="!stagedList"
+ v-tooltip
+ :title="__('Discard all changes')"
+ :aria-label="__('Discard all changes')"
+ :disabled="!filesLength"
:class="{
- 'rounded-right': !filesLength
+ 'disabled-content': !filesLength
}"
- class="ide-commit-file-count order-0 rounded-left text-center"
+ type="button"
+ class="d-flex ide-staged-action-btn p-0 border-0 align-items-center"
+ data-placement="bottom"
+ data-container="body"
+ data-boundary="viewport"
+ @click="openDiscardModal"
>
- {{ filesLength }}
- </span>
+ <icon
+ :size="16"
+ name="remove-all"
+ class="ml-auto mr-auto"
+ />
+ </button>
</div>
</div>
</header>
@@ -143,9 +178,19 @@ export default {
</ul>
<p
v-else
- class="multi-file-commit-list form-text text-muted"
+ class="multi-file-commit-list form-text text-muted text-center"
>
- {{ __('No changes') }}
+ {{ emptyStateText }}
</p>
+ <gl-modal
+ v-if="!stagedList"
+ id="discard-all-changes"
+ :footer-primary-button-text="__('Discard all changes')"
+ :header-title-text="__('Discard all unstaged changes?')"
+ footer-primary-button-variant="danger"
+ @submit="discardAllChanges"
+ >
+ {{ $options.discardModalText }}
+ </gl-modal>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
index d376a004e84..699fa7dc937 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
@@ -38,14 +38,18 @@ export default {
return this.modifiedFilesLength ? 'multi-file-modified' : '';
},
additionsTooltip() {
- return sprintf(n__('1 %{type} addition', '%{count} %{type} additions', this.addedFilesLength), {
- type: this.title.toLowerCase(),
- count: this.addedFilesLength,
- });
+ return sprintf(
+ n__('1 %{type} addition', '%{count} %{type} additions', this.addedFilesLength),
+ {
+ type: this.title.toLowerCase(),
+ count: this.addedFilesLength,
+ },
+ );
},
modifiedTooltip() {
return sprintf(
- n__('1 %{type} modification', '%{count} %{type} modifications', this.modifiedFilesLength), {
+ n__('1 %{type} modification', '%{count} %{type} modifications', this.modifiedFilesLength),
+ {
type: this.title.toLowerCase(),
count: this.modifiedFilesLength,
},
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index 391004dcd3c..ee0e72cd05f 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -2,6 +2,7 @@
import { mapActions } from 'vuex';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
import StageButton from './stage_button.vue';
import UnstageButton from './unstage_button.vue';
import { viewerTypes } from '../../constants';
@@ -12,6 +13,7 @@ export default {
Icon,
StageButton,
UnstageButton,
+ FileIcon,
},
directives: {
tooltip,
@@ -48,7 +50,7 @@ export default {
return `${getCommitIconMap(this.file).icon}${suffix}`;
},
iconClass() {
- return `${getCommitIconMap(this.file).class} append-right-8`;
+ return `${getCommitIconMap(this.file).class} ml-auto mr-auto`;
},
fullKey() {
return `${this.keyPrefix}-${this.file.key}`;
@@ -105,17 +107,20 @@ export default {
@click="openFileInEditor"
>
<span class="multi-file-commit-list-file-path d-flex align-items-center">
- <icon
- :name="iconName"
- :size="16"
- :css-classes="iconClass"
+ <file-icon
+ :file-name="file.name"
+ class="append-right-8"
/>{{ file.name }}
</span>
+ <div class="ml-auto d-flex align-items-center">
+ <div class="d-flex align-items-center ide-commit-list-changed-icon">
+ <icon
+ :name="iconName"
+ :size="16"
+ :css-classes="iconClass"
+ />
+ </div>
+ </div>
</div>
- <component
- :is="actionComponent"
- :path="file.path"
- class="d-flex position-absolute"
- />
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
index e6044401c9f..adf4b479c97 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
@@ -1,11 +1,15 @@
<script>
+import $ from 'jquery';
import { mapActions } from 'vuex';
+import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
export default {
components: {
Icon,
+ GlModal,
},
directives: {
tooltip,
@@ -16,8 +20,19 @@ export default {
required: true,
},
},
+ computed: {
+ modalId() {
+ return `discard-file-${this.path}`;
+ },
+ modalTitle() {
+ return sprintf(__('Discard changes to %{path}?'), { path: this.path });
+ },
+ },
methods: {
...mapActions(['stageChange', 'discardFileChanges']),
+ showDiscardModal() {
+ $(document.getElementById(this.modalId)).modal('show');
+ },
},
};
</script>
@@ -25,51 +40,50 @@ export default {
<template>
<div
v-once
- class="multi-file-discard-btn dropdown"
+ class="multi-file-discard-btn d-flex"
>
<button
v-tooltip
:aria-label="__('Stage changes')"
:title="__('Stage changes')"
type="button"
- class="btn btn-blank append-right-5 d-flex align-items-center"
+ class="btn btn-blank align-items-center"
data-container="body"
data-boundary="viewport"
data-placement="bottom"
- @click.stop="stageChange(path)"
+ @click.stop.prevent="stageChange(path)"
>
<icon
- :size="12"
+ :size="16"
name="mobile-issue-close"
+ class="ml-auto mr-auto"
/>
</button>
<button
v-tooltip
- :title="__('More actions')"
+ :aria-label="__('Discard changes')"
+ :title="__('Discard changes')"
type="button"
- class="btn btn-blank d-flex align-items-center"
+ class="btn btn-blank align-items-center"
data-container="body"
data-boundary="viewport"
data-placement="bottom"
- data-toggle="dropdown"
- data-display="static"
+ @click.stop.prevent="showDiscardModal"
>
<icon
- :size="12"
- name="ellipsis_h"
+ :size="16"
+ name="remove"
+ class="ml-auto mr-auto"
/>
</button>
- <div class="dropdown-menu dropdown-menu-right">
- <ul>
- <li>
- <button
- type="button"
- @click.stop="discardFileChanges(path)"
- >
- {{ __('Discard changes') }}
- </button>
- </li>
- </ul>
- </div>
+ <gl-modal
+ :id="modalId"
+ :header-title-text="modalTitle"
+ :footer-primary-button-text="__('Discard changes')"
+ footer-primary-button-variant="danger"
+ @submit="discardFileChanges(path)"
+ >
+ {{ __("You will loose all changes you've made to this file. This action cannot be undone.") }}
+ </gl-modal>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
index 9cec73ec00e..86c40602074 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
@@ -25,22 +25,23 @@ export default {
<template>
<div
v-once
- class="multi-file-discard-btn"
+ class="multi-file-discard-btn d-flex"
>
<button
v-tooltip
:aria-label="__('Unstage changes')"
:title="__('Unstage changes')"
type="button"
- class="btn btn-blank d-flex align-items-center"
+ class="btn btn-blank align-items-center"
data-container="body"
data-boundary="viewport"
data-placement="bottom"
- @click="unstageChange(path)"
+ @click.stop.prevent="unstageChange(path)"
>
<icon
- :size="12"
- name="history"
+ :size="16"
+ name="redo"
+ class="ml-auto mr-auto"
/>
</button>
</div>
diff --git a/app/assets/javascripts/ide/components/error_message.vue b/app/assets/javascripts/ide/components/error_message.vue
index acbc98b7a7b..11fc15871ac 100644
--- a/app/assets/javascripts/ide/components/error_message.vue
+++ b/app/assets/javascripts/ide/components/error_message.vue
@@ -1,10 +1,10 @@
<script>
import { mapActions } from 'vuex';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
- LoadingIcon,
+ GlLoadingIcon,
},
props: {
message: {
@@ -59,7 +59,7 @@ export default {
@click.stop.prevent="clickAction"
>
{{ message.actionText }}
- <loading-icon
+ <gl-loading-icon
v-show="isLoading"
inline
/>
diff --git a/app/assets/javascripts/ide/components/file_finder/index.vue b/app/assets/javascripts/ide/components/file_finder/index.vue
index 0ba33053717..760ed8654ee 100644
--- a/app/assets/javascripts/ide/components/file_finder/index.vue
+++ b/app/assets/javascripts/ide/components/file_finder/index.vue
@@ -174,8 +174,8 @@ export default {
<div class="dropdown-input">
<input
ref="searchInput"
- :placeholder="__('Search files')"
v-model="searchText"
+ :placeholder="__('Search files')"
type="search"
class="dropdown-input-field"
autocomplete="off"
diff --git a/app/assets/javascripts/ide/components/file_finder/item.vue b/app/assets/javascripts/ide/components/file_finder/item.vue
index f5252ce7706..72ce37be63a 100644
--- a/app/assets/javascripts/ide/components/file_finder/item.vue
+++ b/app/assets/javascripts/ide/components/file_finder/item.vue
@@ -1,7 +1,7 @@
<script>
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import FileIcon from '../../../vue_shared/components/file_icon.vue';
-import ChangedFileIcon from '../changed_file_icon.vue';
+import ChangedFileIcon from '../../../vue_shared/components/changed_file_icon.vue';
const MAX_PATH_LENGTH = 60;
@@ -78,10 +78,10 @@ export default {
class="diff-changed-file-name"
>
<span
- v-for="(char, index) in file.name.split('')"
- :key="index + char"
+ v-for="(char, charIndex) in file.name.split('')"
+ :key="charIndex + char"
:class="{
- highlighted: nameSearchTextOccurences.indexOf(index) >= 0,
+ highlighted: nameSearchTextOccurences.indexOf(charIndex) >= 0,
}"
v-text="char"
>
@@ -91,10 +91,10 @@ export default {
class="diff-changed-file-path prepend-top-5"
>
<span
- v-for="(char, index) in pathWithEllipsis.split('')"
- :key="index + char"
+ v-for="(char, charIndex) in pathWithEllipsis.split('')"
+ :key="charIndex + char"
:class="{
- highlighted: pathSearchTextOccurences.indexOf(index) >= 0,
+ highlighted: pathSearchTextOccurences.indexOf(charIndex) >= 0,
}"
v-text="char"
>
diff --git a/app/assets/javascripts/ide/components/file_row_extra.vue b/app/assets/javascripts/ide/components/file_row_extra.vue
new file mode 100644
index 00000000000..2ad14b88410
--- /dev/null
+++ b/app/assets/javascripts/ide/components/file_row_extra.vue
@@ -0,0 +1,104 @@
+<script>
+import { mapGetters } from 'vuex';
+import { n__, __, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import Icon from '~/vue_shared/components/icon.vue';
+import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue';
+import NewDropdown from './new_dropdown/index.vue';
+import MrFileIcon from './mr_file_icon.vue';
+
+export default {
+ name: 'FileRowExtra',
+ directives: {
+ tooltip,
+ },
+ components: {
+ Icon,
+ NewDropdown,
+ ChangedFileIcon,
+ MrFileIcon,
+ },
+ props: {
+ file: {
+ type: Object,
+ required: true,
+ },
+ mouseOver: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapGetters([
+ 'getChangesInFolder',
+ 'getUnstagedFilesCountForPath',
+ 'getStagedFilesCountForPath',
+ ]),
+ folderUnstagedCount() {
+ return this.getUnstagedFilesCountForPath(this.file.path);
+ },
+ folderStagedCount() {
+ return this.getStagedFilesCountForPath(this.file.path);
+ },
+ changesCount() {
+ return this.getChangesInFolder(this.file.path);
+ },
+ folderChangesTooltip() {
+ if (this.changesCount === 0) return undefined;
+
+ if (this.folderUnstagedCount > 0 && this.folderStagedCount === 0) {
+ return n__('%d unstaged change', '%d unstaged changes', this.folderUnstagedCount);
+ } else if (this.folderUnstagedCount === 0 && this.folderStagedCount > 0) {
+ return n__('%d staged change', '%d staged changes', this.folderStagedCount);
+ }
+
+ return sprintf(__('%{unstaged} unstaged and %{staged} staged changes'), {
+ unstaged: this.folderUnstagedCount,
+ staged: this.folderStagedCount,
+ });
+ },
+ showTreeChangesCount() {
+ return this.file.type === 'tree' && this.changesCount > 0 && !this.file.opened;
+ },
+ showChangedFileIcon() {
+ return this.file.changed || this.file.tempFile || this.file.staged;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="float-right ide-file-icon-holder">
+ <mr-file-icon
+ v-if="file.mrChange"
+ />
+ <span
+ v-if="showTreeChangesCount"
+ class="ide-tree-changes"
+ >
+ {{ changesCount }}
+ <icon
+ v-tooltip
+ :title="folderChangesTooltip"
+ :size="12"
+ data-container="body"
+ data-placement="right"
+ name="file-modified"
+ css-classes="prepend-left-5 ide-file-modified"
+ />
+ </span>
+ <changed-file-icon
+ v-else-if="showChangedFileIcon"
+ :file="file"
+ :show-tooltip="true"
+ :show-staged-icon="true"
+ :force-modified-icon="true"
+ />
+ <new-dropdown
+ :type="file.type"
+ :path="file.path"
+ :mouse-over="mouseOver"
+ class="prepend-left-8"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/file_templates/bar.vue b/app/assets/javascripts/ide/components/file_templates/bar.vue
new file mode 100644
index 00000000000..3587626c580
--- /dev/null
+++ b/app/assets/javascripts/ide/components/file_templates/bar.vue
@@ -0,0 +1,80 @@
+<script>
+import { mapActions, mapGetters, mapState } from 'vuex';
+import Dropdown from './dropdown.vue';
+
+export default {
+ components: {
+ Dropdown,
+ },
+ computed: {
+ ...mapGetters(['activeFile']),
+ ...mapGetters('fileTemplates', ['templateTypes']),
+ ...mapState('fileTemplates', ['selectedTemplateType', 'updateSuccess']),
+ showTemplatesDropdown() {
+ return Object.keys(this.selectedTemplateType).length > 0;
+ },
+ },
+ watch: {
+ activeFile: 'setInitialType',
+ },
+ mounted() {
+ this.setInitialType();
+ },
+ methods: {
+ ...mapActions('fileTemplates', [
+ 'setSelectedTemplateType',
+ 'fetchTemplate',
+ 'undoFileTemplate',
+ ]),
+ setInitialType() {
+ const initialTemplateType = this.templateTypes.find(t => t.name === this.activeFile.name);
+
+ if (initialTemplateType) {
+ this.setSelectedTemplateType(initialTemplateType);
+ }
+ },
+ selectTemplateType(templateType) {
+ this.setSelectedTemplateType(templateType);
+ },
+ selectTemplate(template) {
+ this.fetchTemplate(template);
+ },
+ undo() {
+ this.undoFileTemplate();
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="d-flex align-items-center ide-file-templates qa-file-templates-bar">
+ <strong class="append-right-default">
+ {{ __('File templates') }}
+ </strong>
+ <dropdown
+ :data="templateTypes"
+ :label="selectedTemplateType.name || __('Choose a type...')"
+ class="mr-2"
+ @click="selectTemplateType"
+ />
+ <dropdown
+ v-if="showTemplatesDropdown"
+ :label="__('Choose a template...')"
+ :is-async-data="true"
+ :searchable="true"
+ :title="__('File templates')"
+ class="mr-2 qa-file-template-dropdown"
+ @click="selectTemplate"
+ />
+ <transition name="fade">
+ <button
+ v-show="updateSuccess"
+ type="button"
+ class="btn btn-default"
+ @click="undo"
+ >
+ {{ __('Undo') }}
+ </button>
+ </transition>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/file_templates/dropdown.vue b/app/assets/javascripts/ide/components/file_templates/dropdown.vue
new file mode 100644
index 00000000000..81d8d0b5132
--- /dev/null
+++ b/app/assets/javascripts/ide/components/file_templates/dropdown.vue
@@ -0,0 +1,125 @@
+<script>
+import $ from 'jquery';
+import { mapActions, mapState } from 'vuex';
+import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+
+export default {
+ components: {
+ DropdownButton,
+ GlLoadingIcon,
+ },
+ props: {
+ data: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ label: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ isAsyncData: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ searchable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ search: '',
+ };
+ },
+ computed: {
+ ...mapState('fileTemplates', ['templates', 'isLoading']),
+ outputData() {
+ return (this.isAsyncData ? this.templates : this.data).filter(t => {
+ if (!this.searchable) return true;
+
+ return t.name.toLowerCase().indexOf(this.search.toLowerCase()) >= 0;
+ });
+ },
+ showLoading() {
+ return this.isAsyncData ? this.isLoading : false;
+ },
+ },
+ mounted() {
+ $(this.$el).on('show.bs.dropdown', this.fetchTemplatesIfAsync);
+ },
+ beforeDestroy() {
+ $(this.$el).off('show.bs.dropdown', this.fetchTemplatesIfAsync);
+ },
+ methods: {
+ ...mapActions('fileTemplates', ['fetchTemplateTypes']),
+ fetchTemplatesIfAsync() {
+ if (this.isAsyncData) {
+ this.fetchTemplateTypes();
+ }
+ },
+ clickItem(item) {
+ this.$emit('click', item);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="dropdown">
+ <dropdown-button
+ :toggle-text="label"
+ data-display="static"
+ />
+ <div class="dropdown-menu pb-0">
+ <div
+ v-if="title"
+ class="dropdown-title ml-0 mr-0"
+ >
+ {{ title }}
+ </div>
+ <div
+ v-if="!showLoading && searchable"
+ class="dropdown-input"
+ >
+ <input
+ v-model="search"
+ :placeholder="__('Filter...')"
+ type="search"
+ class="dropdown-input-field qa-dropdown-filter-input"
+ />
+ <i
+ aria-hidden="true"
+ class="fa fa-search dropdown-input-search"
+ ></i>
+ </div>
+ <div class="dropdown-content">
+ <gl-loading-icon
+ v-if="showLoading"
+ :size="2"
+ />
+ <ul v-else>
+ <li
+ v-for="(item, index) in outputData"
+ :key="index"
+ >
+ <button
+ type="button"
+ @click="clickItem(item)"
+ >
+ {{ item.name }}
+ </button>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 2c8305aa0cc..0a368f6558c 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,4 +1,5 @@
<script>
+import Vue from 'vue';
import Mousetrap from 'mousetrap';
import { mapActions, mapState, mapGetters } from 'vuex';
import { __ } from '~/locale';
@@ -10,6 +11,7 @@ import RepoEditor from './repo_editor.vue';
import FindFile from './file_finder/index.vue';
import RightPane from './panes/right.vue';
import ErrorMessage from './error_message.vue';
+import CommitEditorHeader from './commit_sidebar/editor_header.vue';
const originalStopCallback = Mousetrap.stopCallback;
@@ -21,8 +23,15 @@ export default {
IdeStatusBar,
RepoEditor,
FindFile,
- RightPane,
ErrorMessage,
+ CommitEditorHeader,
+ },
+ props: {
+ rightPaneComponent: {
+ type: Vue.Component,
+ required: false,
+ default: () => RightPane,
+ },
},
computed: {
...mapState([
@@ -34,7 +43,7 @@ export default {
'currentProjectId',
'errorMessage',
]),
- ...mapGetters(['activeFile', 'hasChanges', 'someUncommitedChanges']),
+ ...mapGetters(['activeFile', 'hasChanges', 'someUncommittedChanges', 'isCommitModeActive']),
},
mounted() {
window.onbeforeunload = e => this.onBeforeUnload(e);
@@ -54,7 +63,7 @@ export default {
onBeforeUnload(e = {}) {
const returnValue = __('Are you sure you want to lose unsaved changes?');
- if (!this.someUncommitedChanges) return undefined;
+ if (!this.someUncommittedChanges) return undefined;
Object.assign(e, {
returnValue,
@@ -78,13 +87,13 @@ export default {
</script>
<template>
- <article class="ide">
+ <article class="ide position-relative d-flex flex-column align-items-stretch">
<error-message
v-if="errorMessage"
:message="errorMessage"
/>
<div
- class="ide-view"
+ class="ide-view flex-grow d-flex"
>
<find-file
v-show="fileFindVisible"
@@ -96,7 +105,12 @@ export default {
<template
v-if="activeFile"
>
+ <commit-editor-header
+ v-if="isCommitModeActive"
+ :active-file="activeFile"
+ />
<repo-tabs
+ v-else
:active-file="activeFile"
:files="openFiles"
:viewer="viewer"
@@ -136,7 +150,8 @@ export default {
</div>
</template>
</div>
- <right-pane
+ <component
+ :is="rightPaneComponent"
v-if="currentProjectId"
/>
</div>
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index 4771c58a11d..6b4849eb6f9 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -1,6 +1,6 @@
<script>
import { mapState, mapGetters } from 'vuex';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import { GlSkeletonLoading } from '@gitlab/ui';
import IdeTree from './ide_tree.vue';
import ResizablePanel from './resizable_panel.vue';
import ActivityBar from './activity_bar.vue';
@@ -13,7 +13,7 @@ import { activityBarViews } from '../constants';
export default {
components: {
- SkeletonLoadingContainer,
+ GlSkeletonLoading,
ResizablePanel,
ActivityBar,
CommitSection,
@@ -24,18 +24,12 @@ export default {
IdeProjectHeader,
},
computed: {
- ...mapState([
- 'loading',
- 'currentActivityView',
- 'changedFiles',
- 'stagedFiles',
- 'lastCommitMsg',
- ]),
- ...mapGetters(['currentProject', 'someUncommitedChanges']),
+ ...mapState(['loading', 'currentActivityView', 'changedFiles', 'stagedFiles', 'lastCommitMsg']),
+ ...mapGetters(['currentProject', 'someUncommittedChanges']),
showSuccessMessage() {
return (
this.currentActivityView === activityBarViews.edit &&
- (this.lastCommitMsg && !this.someUncommitedChanges)
+ (this.lastCommitMsg && !this.someUncommittedChanges)
);
},
},
@@ -56,7 +50,7 @@ export default {
:key="n"
class="multi-file-loading-container"
>
- <skeleton-loading-container />
+ <gl-skeleton-loading />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index 715dc1bfb42..a04d09ef374 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -50,7 +50,9 @@ export default {
this.stopPipelinePolling();
},
methods: {
- ...mapActions(['setRightPane']),
+ ...mapActions('rightPane', {
+ openRightPane: 'open',
+ }),
...mapActions('pipelines', ['fetchLatestPipeline', 'stopPipelinePolling']),
startTimer() {
this.intervalId = setInterval(() => {
@@ -88,7 +90,7 @@ export default {
<button
type="button"
class="p-0 border-0 h-50"
- @click="setRightPane($options.rightSidebarViews.pipelines)"
+ @click="openRightPane($options.rightSidebarViews.pipelines)"
>
<ci-icon
v-tooltip
diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue
index 39d46a91731..9f9e638f1aa 100644
--- a/app/assets/javascripts/ide/components/ide_tree.vue
+++ b/app/assets/javascripts/ide/components/ide_tree.vue
@@ -45,7 +45,7 @@ export default {
<new-entry-button
:label="__('New file')"
:show-label="false"
- class="d-flex border-0 p-0 mr-3"
+ class="d-flex border-0 p-0 mr-3 qa-new-file"
icon="doc-new"
@click="openNewEntryModal({ type: 'blob' })"
/>
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index 00ae5ea2c15..12ed7f86b3d 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -1,16 +1,17 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import RepoFile from './repo_file.vue';
+import { GlSkeletonLoading } from '@gitlab/ui';
+import FileRow from '~/vue_shared/components/file_row.vue';
import NavDropdown from './nav_dropdown.vue';
+import FileRowExtra from './file_row_extra.vue';
export default {
components: {
Icon,
- RepoFile,
- SkeletonLoadingContainer,
+ GlSkeletonLoading,
NavDropdown,
+ FileRow,
},
props: {
viewerType: {
@@ -34,14 +35,15 @@ export default {
this.updateViewer(this.viewerType);
},
methods: {
- ...mapActions(['updateViewer']),
+ ...mapActions(['updateViewer', 'toggleTreeOpen']),
},
+ FileRowExtra,
};
</script>
<template>
<div
- class="ide-file-list"
+ class="ide-file-list qa-file-list"
>
<template v-if="showLoading">
<div
@@ -49,7 +51,7 @@ export default {
:key="n"
class="multi-file-loading-container"
>
- <skeleton-loading-container />
+ <gl-skeleton-loading />
</div>
</template>
<template v-else>
@@ -63,11 +65,13 @@ export default {
<div
class="ide-tree-body h-100"
>
- <repo-file
+ <file-row
v-for="file in currentTree.tree"
:key="file.key"
:file="file"
:level="0"
+ :extra-component="$options.FileRowExtra"
+ @toggleTreeOpen="toggleTreeOpen"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/jobs/list.vue b/app/assets/javascripts/ide/components/jobs/list.vue
index 3b16b860ecd..e3626f60899 100644
--- a/app/assets/javascripts/ide/components/jobs/list.vue
+++ b/app/assets/javascripts/ide/components/jobs/list.vue
@@ -1,12 +1,12 @@
<script>
import { mapActions } from 'vuex';
-import LoadingIcon from '../../../vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import Stage from './stage.vue';
export default {
components: {
- LoadingIcon,
Stage,
+ GlLoadingIcon,
},
props: {
stages: {
@@ -26,10 +26,10 @@ export default {
<template>
<div>
- <loading-icon
+ <gl-loading-icon
v-if="loading && !stages.length"
+ :size="2"
class="prepend-top-default"
- size="2"
/>
<template v-else>
<stage
diff --git a/app/assets/javascripts/ide/components/jobs/stage.vue b/app/assets/javascripts/ide/components/jobs/stage.vue
index 15e881b7bc8..e4fc78afaf2 100644
--- a/app/assets/javascripts/ide/components/jobs/stage.vue
+++ b/app/assets/javascripts/ide/components/jobs/stage.vue
@@ -1,8 +1,8 @@
<script>
+import { GlLoadingIcon } from '@gitlab/ui';
import tooltip from '../../../vue_shared/directives/tooltip';
import Icon from '../../../vue_shared/components/icon.vue';
import CiIcon from '../../../vue_shared/components/ci_icon.vue';
-import LoadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Item from './item.vue';
export default {
@@ -12,8 +12,8 @@ export default {
components: {
Icon,
CiIcon,
- LoadingIcon,
Item,
+ GlLoadingIcon,
},
props: {
stage: {
@@ -71,8 +71,8 @@ export default {
:size="24"
/>
<strong
- v-tooltip="showTooltip"
ref="stageTitle"
+ v-tooltip="showTooltip"
:title="showTooltip ? stage.name : null"
data-container="body"
class="prepend-left-8 ide-stage-title"
@@ -96,7 +96,7 @@ export default {
v-show="!stage.isCollapsed"
class="card-body"
>
- <loading-icon
+ <gl-loading-icon
v-if="showLoadingIcon"
/>
<template v-else>
diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue
index fc612956688..4df29590b85 100644
--- a/app/assets/javascripts/ide/components/merge_requests/list.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/list.vue
@@ -3,7 +3,7 @@ import { mapActions, mapState } from 'vuex';
import _ from 'underscore';
import { __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import Item from './item.vue';
import TokenedInput from '../shared/tokened_input.vue';
@@ -14,10 +14,10 @@ const SEARCH_TYPES = [
export default {
components: {
- LoadingIcon,
TokenedInput,
Item,
Icon,
+ GlLoadingIcon,
},
data() {
return {
@@ -39,14 +39,10 @@ export default {
return this.hasSearchFocus && !this.search && !this.currentSearchType;
},
type() {
- return this.currentSearchType
- ? this.currentSearchType.type
- : '';
+ return this.currentSearchType ? this.currentSearchType.type : '';
},
searchTokens() {
- return this.currentSearchType
- ? [this.currentSearchType]
- : [];
+ return this.currentSearchType ? [this.currentSearchType] : [];
},
},
watch: {
@@ -98,10 +94,10 @@ export default {
</div>
</div>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="2"
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
- size="2"
/>
<template v-else>
<ul
diff --git a/app/assets/javascripts/ide/components/nav_dropdown_button.vue b/app/assets/javascripts/ide/components/nav_dropdown_button.vue
index 7f98769d484..6cee4e9a8f0 100644
--- a/app/assets/javascripts/ide/components/nav_dropdown_button.vue
+++ b/app/assets/javascripts/ide/components/nav_dropdown_button.vue
@@ -13,9 +13,7 @@ export default {
computed: {
...mapState(['currentBranchId', 'currentMergeRequestId']),
mergeRequestLabel() {
- return this.currentMergeRequestId
- ? `!${this.currentMergeRequestId}`
- : EMPTY_LABEL;
+ return this.currentMergeRequestId ? `!${this.currentMergeRequestId}` : EMPTY_LABEL;
},
branchLabel() {
return this.currentBranchId || EMPTY_LABEL;
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index e500ef0e1b5..f0a04011a3e 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -1,6 +1,7 @@
<script>
+import $ from 'jquery';
import { __ } from '~/locale';
-import { mapActions, mapState } from 'vuex';
+import { mapActions, mapState, mapGetters } from 'vuex';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { modalTypes } from '../../constants';
@@ -15,6 +16,7 @@ export default {
},
computed: {
...mapState(['entryModal']),
+ ...mapGetters('fileTemplates', ['templateTypes']),
entryName: {
get() {
if (this.entryModal.type === modalTypes.rename) {
@@ -31,7 +33,9 @@ export default {
if (this.entryModal.type === modalTypes.tree) {
return __('Create new directory');
} else if (this.entryModal.type === modalTypes.rename) {
- return this.entryModal.entry.type === modalTypes.tree ? __('Rename folder') : __('Rename file');
+ return this.entryModal.entry.type === modalTypes.tree
+ ? __('Rename folder')
+ : __('Rename file');
}
return __('Create new file');
@@ -40,11 +44,16 @@ export default {
if (this.entryModal.type === modalTypes.tree) {
return __('Create directory');
} else if (this.entryModal.type === modalTypes.rename) {
- return this.entryModal.entry.type === modalTypes.tree ? __('Rename folder') : __('Rename file');
+ return this.entryModal.entry.type === modalTypes.tree
+ ? __('Rename folder')
+ : __('Rename file');
}
return __('Create file');
},
+ isCreatingNew() {
+ return this.entryModal.type !== modalTypes.rename;
+ },
},
methods: {
...mapActions(['createTempEntry', 'renameEntry']),
@@ -61,6 +70,14 @@ export default {
});
}
},
+ createFromTemplate(template) {
+ this.createTempEntry({
+ name: template.name,
+ type: this.entryModal.type,
+ });
+
+ $('#ide-new-entry').modal('toggle');
+ },
focusInput() {
this.$refs.fieldName.focus();
},
@@ -77,6 +94,7 @@ export default {
:header-title-text="modalTitle"
:footer-primary-button-text="buttonLabel"
footer-primary-button-variant="success"
+ modal-size="lg"
@submit="submitForm"
@open="focusInput"
@closed="closedModal"
@@ -84,16 +102,35 @@ export default {
<div
class="form-group row"
>
- <label class="label-bold col-form-label col-sm-3">
+ <label class="label-bold col-form-label col-sm-2">
{{ __('Name') }}
</label>
- <div class="col-sm-9">
+ <div class="col-sm-10">
<input
ref="fieldName"
v-model="entryName"
type="text"
- class="form-control"
+ class="form-control qa-full-file-path"
+ placeholder="/dir/file_name"
/>
+ <ul
+ v-if="isCreatingNew"
+ class="prepend-top-default list-inline qa-template-list"
+ >
+ <li
+ v-for="(template, index) in templateTypes"
+ :key="index"
+ class="list-inline-item"
+ >
+ <button
+ type="button"
+ class="btn btn-missing p-1 pr-2 pl-2"
+ @click="createFromTemplate(template)"
+ >
+ {{ template.name }}
+ </button>
+ </li>
+ </ul>
</div>
</div>
</gl-modal>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
index 5b1743bb30e..ec759043efc 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
@@ -24,21 +24,33 @@ export default {
default: null,
},
},
- mounted() {
- this.$refs.fileUpload.addEventListener('change', this.openFile);
- },
- beforeDestroy() {
- this.$refs.fileUpload.removeEventListener('change', this.openFile);
- },
methods: {
- createFile(target, file, isText) {
+ isText(content, fileType) {
+ const knownBinaryFileTypes = ['image/'];
+ const knownTextFileTypes = ['text/'];
+ const isKnownBinaryFileType = knownBinaryFileTypes.find(type => fileType.includes(type));
+ const isKnownTextFileType = knownTextFileTypes.find(type => fileType.includes(type));
+ const asciiRegex = /^[ -~\t\n\r]+$/; // tests whether a string contains ascii characters only (ranges from space to tilde, tabs and new lines)
+
+ if (isKnownBinaryFileType) {
+ return false;
+ }
+
+ if (isKnownTextFileType) {
+ return true;
+ }
+
+ // if it's not a known file type, determine the type by evaluating the file contents
+ return asciiRegex.test(content);
+ },
+ createFile(target, file) {
const { name } = file;
let { result } = target;
+ const encodedContent = result.split('base64,')[1];
+ const rawContent = encodedContent ? atob(encodedContent) : '';
+ const isText = this.isText(rawContent, file.type);
- if (!isText) {
- // eslint-disable-next-line prefer-destructuring
- result = result.split('base64,')[1];
- }
+ result = isText ? rawContent : encodedContent;
this.$emit('create', {
name: `${this.path ? `${this.path}/` : ''}${name}`,
@@ -49,15 +61,9 @@ export default {
},
readFile(file) {
const reader = new FileReader();
- const isText = file.type.match(/text.*/) !== null;
- reader.addEventListener('load', e => this.createFile(e.target, file, isText), { once: true });
-
- if (isText) {
- reader.readAsText(file);
- } else {
- reader.readAsDataURL(file);
- }
+ reader.addEventListener('load', e => this.createFile(e.target, file), { once: true });
+ reader.readAsDataURL(file);
},
openFile() {
Array.from(this.$refs.fileUpload.files).forEach(file => this.readFile(file));
@@ -85,6 +91,8 @@ export default {
ref="fileUpload"
type="file"
class="hidden"
+ multiple
+ @change="openFile"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue
index 79df225c432..10aa96dffaf 100644
--- a/app/assets/javascripts/ide/components/panes/right.vue
+++ b/app/assets/javascripts/ide/components/panes/right.vue
@@ -1,5 +1,7 @@
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
+import _ from 'underscore';
+import { __ } from '~/locale';
import tooltip from '../../../vue_shared/directives/tooltip';
import Icon from '../../../vue_shared/components/icon.vue';
import { rightSidebarViews } from '../../constants';
@@ -21,28 +23,68 @@ export default {
MergeRequestInfo,
Clientside,
},
+ props: {
+ extensionTabs: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
computed: {
- ...mapState(['rightPane', 'currentMergeRequestId', 'clientsidePreviewEnabled']),
+ ...mapState(['currentMergeRequestId', 'clientsidePreviewEnabled']),
+ ...mapState('rightPane', ['isOpen', 'currentView']),
...mapGetters(['packageJson']),
- pipelinesActive() {
- return (
- this.rightPane === rightSidebarViews.pipelines ||
- this.rightPane === rightSidebarViews.jobsDetail
- );
- },
+ ...mapGetters('rightPane', ['isActiveView', 'isAliveView']),
showLivePreview() {
return this.packageJson && this.clientsidePreviewEnabled;
},
+ defaultTabs() {
+ return [
+ {
+ show: this.currentMergeRequestId,
+ title: __('Merge Request'),
+ views: [rightSidebarViews.mergeRequestInfo],
+ icon: 'text-description',
+ },
+ {
+ show: true,
+ title: __('Pipelines'),
+ views: [rightSidebarViews.pipelines, rightSidebarViews.jobsDetail],
+ icon: 'rocket',
+ },
+ {
+ show: this.showLivePreview,
+ title: __('Live preview'),
+ views: [rightSidebarViews.clientSidePreview],
+ icon: 'live-preview',
+ },
+ ];
+ },
+ tabs() {
+ return this.defaultTabs.concat(this.extensionTabs).filter(tab => tab.show);
+ },
+ tabViews() {
+ return _.flatten(this.tabs.map(tab => tab.views));
+ },
+ aliveTabViews() {
+ return this.tabViews.filter(view => this.isAliveView(view.name));
+ },
},
methods: {
- ...mapActions(['setRightPane']),
- clickTab(e, view) {
+ ...mapActions('rightPane', ['toggleOpen', 'open']),
+ clickTab(e, tab) {
e.target.blur();
- this.setRightPane(view);
+ if (this.isActiveTab(tab)) {
+ this.toggleOpen();
+ } else {
+ this.open(tab.views[0]);
+ }
+ },
+ isActiveTab(tab) {
+ return tab.views.some(view => this.isActiveView(view.name));
},
},
- rightSidebarViews,
};
</script>
@@ -51,77 +93,45 @@ export default {
class="multi-file-commit-panel ide-right-sidebar"
>
<resizable-panel
- v-if="rightPane"
+ v-show="isOpen"
:collapsible="false"
:initial-width="350"
:min-size="350"
- :class="`ide-right-sidebar-${rightPane}`"
+ :class="`ide-right-sidebar-${currentView}`"
side="right"
class="multi-file-commit-panel-inner"
>
- <component :is="rightPane" />
+ <div
+ v-for="tabView in aliveTabViews"
+ v-show="isActiveView(tabView.name)"
+ :key="tabView.name"
+ class="h-100"
+ >
+ <component :is="tabView.name" />
+ </div>
</resizable-panel>
<nav class="ide-activity-bar">
<ul class="list-unstyled">
<li
- v-if="currentMergeRequestId"
+ v-for="tab of tabs"
+ :key="tab.title"
>
<button
v-tooltip
- :title="__('Merge Request')"
- :aria-label="__('Merge Request')"
- :class="{
- active: rightPane === $options.rightSidebarViews.mergeRequestInfo
- }"
- data-container="body"
- data-placement="left"
- class="ide-sidebar-link is-right"
- type="button"
- @click="clickTab($event, $options.rightSidebarViews.mergeRequestInfo)"
- >
- <icon
- :size="16"
- name="text-description"
- />
- </button>
- </li>
- <li>
- <button
- v-tooltip
- :title="__('Pipelines')"
- :aria-label="__('Pipelines')"
- :class="{
- active: pipelinesActive
- }"
- data-container="body"
- data-placement="left"
- class="ide-sidebar-link is-right"
- type="button"
- @click="clickTab($event, $options.rightSidebarViews.pipelines)"
- >
- <icon
- :size="16"
- name="rocket"
- />
- </button>
- </li>
- <li v-if="showLivePreview">
- <button
- v-tooltip
- :title="__('Live preview')"
- :aria-label="__('Live preview')"
+ :title="tab.title"
+ :aria-label="tab.title"
:class="{
- active: rightPane === $options.rightSidebarViews.clientSidePreview
+ active: isActiveTab(tab) && isOpen
}"
data-container="body"
data-placement="left"
class="ide-sidebar-link is-right"
type="button"
- @click="clickTab($event, $options.rightSidebarViews.clientSidePreview)"
+ @click="clickTab($event, tab)"
>
<icon
:size="16"
- name="live-preview"
+ :name="tab.icon"
/>
</button>
</li>
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index 5757dfdc925..36cfcac4186 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -1,8 +1,8 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import _ from 'underscore';
+import { GlLoadingIcon } from '@gitlab/ui';
import { sprintf, __ } from '../../../locale';
-import LoadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Icon from '../../../vue_shared/components/icon.vue';
import CiIcon from '../../../vue_shared/components/ci_icon.vue';
import Tabs from '../../../vue_shared/components/tabs/tabs';
@@ -12,13 +12,13 @@ import JobsList from '../jobs/list.vue';
export default {
components: {
- LoadingIcon,
Icon,
CiIcon,
Tabs,
Tab,
JobsList,
EmptyState,
+ GlLoadingIcon,
},
computed: {
...mapState(['pipelinesEmptyStateSvgPath', 'links']),
@@ -27,7 +27,7 @@ export default {
...mapState('pipelines', ['isLoadingPipeline', 'latestPipeline', 'stages', 'isLoadingJobs']),
ciLintText() {
return sprintf(
- __('You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}'),
+ __('You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}.'),
{
linkStart: `<a href="${_.escape(this.currentProject.web_url)}/-/ci/lint">`,
linkEnd: '</a>',
@@ -50,10 +50,10 @@ export default {
<template>
<div class="ide-pipeline">
- <loading-icon
+ <gl-loading-icon
v-if="showLoadingIcon"
+ :size="2"
class="prepend-top-default"
- size="2"
/>
<template v-else-if="latestPipeline !== null">
<header
diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue
index 39a1bd1f61b..afc0cfca7d8 100644
--- a/app/assets/javascripts/ide/components/preview/clientside.vue
+++ b/app/assets/javascripts/ide/components/preview/clientside.vue
@@ -3,15 +3,15 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import _ from 'underscore';
import { Manager } from 'smooshpack';
import { listen } from 'codesandbox-api';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import Navigator from './navigator.vue';
import { packageJsonPath } from '../../constants';
import { createPathWithExt } from '../../utils';
export default {
components: {
- LoadingIcon,
Navigator,
+ GlLoadingIcon,
},
data() {
return {
@@ -177,9 +177,9 @@ export default {
{{ s__('IDE|Get started with Live Preview') }}
</a>
</div>
- <loading-icon
+ <gl-loading-icon
v-else
- size="2"
+ :size="2"
class="align-self-center mt-auto mb-auto"
/>
</div>
diff --git a/app/assets/javascripts/ide/components/preview/navigator.vue b/app/assets/javascripts/ide/components/preview/navigator.vue
index 4bf346946b6..cc6fc595b74 100644
--- a/app/assets/javascripts/ide/components/preview/navigator.vue
+++ b/app/assets/javascripts/ide/components/preview/navigator.vue
@@ -1,12 +1,12 @@
<script>
import { listen } from 'codesandbox-api';
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
Icon,
- LoadingIcon,
+ GlLoadingIcon,
},
props: {
manager: {
@@ -138,7 +138,7 @@ export default {
class="ide-navigator-location form-control bg-white"
readonly
/>
- <loading-icon
+ <gl-loading-icon
v-if="loading"
class="position-absolute ide-preview-loading-icon"
/>
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 6f1a941fbc4..5e86876c1c1 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -27,10 +27,10 @@ export default {
'unusedSeal',
]),
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
- ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges', 'activeFile']),
+ ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommittedChanges', 'activeFile']),
...mapGetters('commit', ['discardDraftButtonDisabled']),
showStageUnstageArea() {
- return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal);
+ return !!(this.someUncommittedChanges || this.lastCommitMsg || !this.unusedSeal);
},
activeFileKey() {
return this.activeFile ? this.activeFile.key : null;
@@ -95,8 +95,9 @@ export default {
:file-list="changedFiles"
:action-btn-text="__('Stage all changes')"
:active-file-key="activeFileKey"
+ :empty-state-text="__('There are no unstaged changes')"
action="stageAllChanges"
- action-btn-icon="mobile-issue-close"
+ action-btn-icon="stage-all"
item-action-component="stage-button"
class="is-first"
icon-name="unstaged"
@@ -108,8 +109,9 @@ export default {
:action-btn-text="__('Unstage all changes')"
:staged-list="true"
:active-file-key="activeFileKey"
+ :empty-state-text="__('There are no staged changes')"
action="unstageAllChanges"
- action-btn-icon="history"
+ action-btn-icon="unstage-all"
item-action-component="unstage-button"
icon-name="staged"
/>
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index f9badb01535..7b0f717962e 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -6,12 +6,14 @@ import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import { activityBarViews, viewerTypes } from '../constants';
import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
+import FileTemplatesBar from './file_templates/bar.vue';
export default {
components: {
ContentViewer,
DiffViewer,
ExternalLink,
+ FileTemplatesBar,
},
props: {
file: {
@@ -20,13 +22,10 @@ export default {
},
},
computed: {
- ...mapState([
- 'rightPanelCollapsed',
- 'viewer',
- 'panelResizing',
- 'currentActivityView',
- 'rightPane',
- ]),
+ ...mapState('rightPane', {
+ rightPaneIsOpen: 'isOpen',
+ }),
+ ...mapState(['rightPanelCollapsed', 'viewer', 'panelResizing', 'currentActivityView']),
...mapGetters([
'currentMergeRequest',
'getStagedFile',
@@ -34,6 +33,7 @@ export default {
'isCommitModeActive',
'isReviewModeActive',
]),
+ ...mapGetters('fileTemplates', ['showFileTemplatesBar']),
shouldHideEditor() {
return this.file && this.file.binary && !this.file.content;
},
@@ -96,7 +96,7 @@ export default {
this.editor.updateDimensions();
}
},
- rightPane() {
+ rightPaneIsOpen() {
this.editor.updateDimensions();
},
},
@@ -133,7 +133,6 @@ export default {
.then(() =>
this.getRawFileData({
path: this.file.path,
- baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '',
}),
)
.then(() => {
@@ -217,7 +216,7 @@ export default {
id="ide"
class="blob-viewer-container blob-editor-container"
>
- <div class="ide-mode-tabs clearfix" >
+ <div class="ide-mode-tabs clearfix">
<ul
v-if="!shouldHideEditor && isEditModeActive"
class="nav-links float-left"
@@ -250,6 +249,9 @@ export default {
:file="file"
/>
</div>
+ <file-templates-bar
+ v-if="showFileTemplatesBar(file.name)"
+ />
<div
v-show="!shouldHideEditor && file.viewMode ==='editor'"
ref="editor"
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
deleted file mode 100644
index dbdf0be2809..00000000000
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ /dev/null
@@ -1,225 +0,0 @@
-<script>
-import { mapActions, mapGetters } from 'vuex';
-import { n__, __, sprintf } from '~/locale';
-import tooltip from '~/vue_shared/directives/tooltip';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import Icon from '~/vue_shared/components/icon.vue';
-import FileIcon from '~/vue_shared/components/file_icon.vue';
-import router from '../ide_router';
-import NewDropdown from './new_dropdown/index.vue';
-import FileStatusIcon from './repo_file_status_icon.vue';
-import ChangedFileIcon from './changed_file_icon.vue';
-import MrFileIcon from './mr_file_icon.vue';
-
-export default {
- name: 'RepoFile',
- directives: {
- tooltip,
- },
- components: {
- SkeletonLoadingContainer,
- NewDropdown,
- FileStatusIcon,
- FileIcon,
- ChangedFileIcon,
- MrFileIcon,
- Icon,
- },
- props: {
- file: {
- type: Object,
- required: true,
- },
- level: {
- type: Number,
- required: true,
- },
- },
- data() {
- return {
- mouseOver: false,
- };
- },
- computed: {
- ...mapGetters([
- 'getChangesInFolder',
- 'getUnstagedFilesCountForPath',
- 'getStagedFilesCountForPath',
- ]),
- folderUnstagedCount() {
- return this.getUnstagedFilesCountForPath(this.file.path);
- },
- folderStagedCount() {
- return this.getStagedFilesCountForPath(this.file.path);
- },
- changesCount() {
- return this.getChangesInFolder(this.file.path);
- },
- folderChangesTooltip() {
- if (this.changesCount === 0) return undefined;
-
- if (this.folderUnstagedCount > 0 && this.folderStagedCount === 0) {
- return n__('%d unstaged change', '%d unstaged changes', this.folderUnstagedCount);
- } else if (this.folderUnstagedCount === 0 && this.folderStagedCount > 0) {
- return n__('%d staged change', '%d staged changes', this.folderStagedCount);
- }
-
- return sprintf(__('%{unstaged} unstaged and %{staged} staged changes'), {
- unstaged: this.folderUnstagedCount,
- staged: this.folderStagedCount,
- });
- },
- isTree() {
- return this.file.type === 'tree';
- },
- isBlob() {
- return this.file.type === 'blob';
- },
- levelIndentation() {
- return {
- marginLeft: `${this.level * 16}px`,
- };
- },
- fileClass() {
- return {
- 'file-open': this.isBlob && this.file.opened,
- 'file-active': this.isBlob && this.file.active,
- folder: this.isTree,
- 'is-open': this.file.opened,
- };
- },
- showTreeChangesCount() {
- return this.isTree && this.changesCount > 0 && !this.file.opened;
- },
- showChangedFileIcon() {
- return this.file.changed || this.file.tempFile || this.file.staged;
- },
- },
- mounted() {
- if (this.hasPathAtCurrentRoute()) {
- this.scrollIntoView(true);
- }
- },
- updated() {
- if (this.file.type === 'blob' && this.file.active) {
- this.scrollIntoView();
- }
- },
- methods: {
- ...mapActions(['toggleTreeOpen']),
- clickFile() {
- // Manual Action if a tree is selected/opened
- if (this.isTree && this.hasUrlAtCurrentRoute()) {
- this.toggleTreeOpen(this.file.path);
- }
-
- router.push(`/project${this.file.url}`);
- },
- scrollIntoView(isInit = false) {
- const block = isInit && this.isTree ? 'center' : 'nearest';
-
- this.$el.scrollIntoView({
- behavior: 'smooth',
- block,
- });
- },
- hasPathAtCurrentRoute() {
- if (!this.$router || !this.$router.currentRoute) {
- return false;
- }
-
- // - strip route up to "/-/" and ending "/"
- const routePath = this.$router.currentRoute.path
- .replace(/^.*?[/]-[/]/g, '')
- .replace(/[/]$/g, '');
-
- // - strip ending "/"
- const filePath = this.file.path.replace(/[/]$/g, '');
-
- return filePath === routePath;
- },
- hasUrlAtCurrentRoute() {
- return this.$router.currentRoute.path === `/project${this.file.url}`;
- },
- toggleHover(over) {
- this.mouseOver = over;
- },
- },
-};
-</script>
-
-<template>
- <div>
- <div
- :class="fileClass"
- class="file"
- role="button"
- @click="clickFile"
- @mouseover="toggleHover(true)"
- @mouseout="toggleHover(false)"
- >
- <div
- class="file-name"
- >
- <span
- :style="levelIndentation"
- class="ide-file-name str-truncated"
- >
- <file-icon
- :file-name="file.name"
- :loading="file.loading"
- :folder="isTree"
- :opened="file.opened"
- :size="16"
- />
- {{ file.name }}
- <file-status-icon
- :file="file"
- />
- </span>
- <span class="float-right ide-file-icon-holder">
- <mr-file-icon
- v-if="file.mrChange"
- />
- <span
- v-if="showTreeChangesCount"
- class="ide-tree-changes"
- >
- {{ changesCount }}
- <icon
- v-tooltip
- :title="folderChangesTooltip"
- :size="12"
- data-container="body"
- data-placement="right"
- name="file-modified"
- css-classes="prepend-left-5 ide-file-modified"
- />
- </span>
- <changed-file-icon
- v-else-if="showChangedFileIcon"
- :file="file"
- :show-tooltip="true"
- :show-staged-icon="true"
- :force-modified-icon="true"
- class="float-right"
- />
- </span>
- <new-dropdown
- :type="file.type"
- :path="file.path"
- :mouse-over="mouseOver"
- class="float-right prepend-left-8"
- />
- </div>
- </div>
- <template v-if="file.opened">
- <repo-file
- v-for="childFile in file.tree"
- :key="childFile.key"
- :file="childFile"
- :level="level + 1"
- />
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_file_status_icon.vue b/app/assets/javascripts/ide/components/repo_file_status_icon.vue
index 76a3333be50..97589e116c5 100644
--- a/app/assets/javascripts/ide/components/repo_file_status_icon.vue
+++ b/app/assets/javascripts/ide/components/repo_file_status_icon.vue
@@ -26,8 +26,8 @@ export default {
<template>
<span
- v-tooltip
v-if="file.file_lock"
+ v-tooltip
:title="lockTooltip"
data-container="body"
>
diff --git a/app/assets/javascripts/ide/components/repo_loading_file.vue b/app/assets/javascripts/ide/components/repo_loading_file.vue
deleted file mode 100644
index 7a5ede82253..00000000000
--- a/app/assets/javascripts/ide/components/repo_loading_file.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-<script>
- import { mapState } from 'vuex';
- import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-
- export default {
- components: {
- skeletonLoadingContainer,
- },
- computed: {
- ...mapState([
- 'leftPanelCollapsed',
- ]),
- },
- };
-</script>
-
-<template>
- <tr
- class="loading-file"
- aria-label="Loading files"
- >
- <td class="multi-file-table-col-name">
- <skeleton-loading-container
- :small="true"
- />
- </td>
- <template v-if="!leftPanelCollapsed">
- <td class="d-none d-sm-none d-md-block">
- <skeleton-loading-container
- :small="true"
- />
- </td>
-
- <td class="d-none d-sm-block">
- <skeleton-loading-container
- :small="true"
- class="animation-container-right"
- />
- </td>
- </template>
- </tr>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue
index db47b75ec5c..d621653d6fd 100644
--- a/app/assets/javascripts/ide/components/repo_tab.vue
+++ b/app/assets/javascripts/ide/components/repo_tab.vue
@@ -3,8 +3,8 @@ import { mapActions } from 'vuex';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
+import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue';
import FileStatusIcon from './repo_file_status_icon.vue';
-import ChangedFileIcon from './changed_file_icon.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/shared/tokened_input.vue b/app/assets/javascripts/ide/components/shared/tokened_input.vue
index a7a12f6785d..30010957a16 100644
--- a/app/assets/javascripts/ide/components/shared/tokened_input.vue
+++ b/app/assets/javascripts/ide/components/shared/tokened_input.vue
@@ -30,9 +30,7 @@ export default {
},
computed: {
placeholderText() {
- return this.tokens.length
- ? ''
- : this.placeholder;
+ return this.tokens.length ? '' : this.placeholder;
},
},
watch: {
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 8caa5b86a9b..3b201f006aa 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -29,10 +29,10 @@ export const diffModes = {
};
export const rightSidebarViews = {
- pipelines: 'pipelines-list',
- jobsDetail: 'jobs-detail',
- mergeRequestInfo: 'merge-request-info',
- clientSidePreview: 'clientside',
+ pipelines: { name: 'pipelines-list', keepAlive: true },
+ jobsDetail: { name: 'jobs-detail', keepAlive: false },
+ mergeRequestInfo: { name: 'merge-request-info', keepAlive: true },
+ clientSidePreview: { name: 'clientside', keepAlive: false },
};
export const stageKeys = {
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index 79e38ae911e..7a5a227db30 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -8,16 +8,25 @@ import { convertPermissionToBoolean } from '../lib/utils/common_utils';
Vue.use(Translate);
-export function initIde(el) {
+/**
+ * Initialize the IDE on the given element.
+ *
+ * @param {Element} el - The element that will contain the IDE.
+ * @param {Object} options - Extra options for the IDE (Used by EE).
+ * @param {(e:Element) => Object} options.extraInitialData -
+ * Function that returns extra properties to seed initial data.
+ * @param {Component} options.rootComponent -
+ * Component that overrides the root component.
+ */
+export function initIde(el, options = {}) {
if (!el) return null;
+ const { extraInitialData = () => ({}), rootComponent = ide } = options;
+
return new Vue({
el,
store,
router,
- components: {
- ide,
- },
created() {
this.setEmptyStateSvgs({
emptyStateSvgPath: el.dataset.emptyStateSvgPath,
@@ -32,13 +41,14 @@ export function initIde(el) {
});
this.setInitialData({
clientsidePreviewEnabled: convertPermissionToBoolean(el.dataset.clientsidePreviewEnabled),
+ ...extraInitialData(el),
});
},
methods: {
...mapActions(['setEmptyStateSvgs', 'setLinks', 'setInitialData']),
},
render(createElement) {
- return createElement('ide');
+ return createElement(rootComponent);
},
});
}
@@ -52,3 +62,18 @@ export function resetServiceWorkersPublicPath() {
const webpackAssetPath = `${relativeRootPath}/assets/webpack/`;
__webpack_public_path__ = webpackAssetPath; // eslint-disable-line camelcase
}
+
+/**
+ * Start the IDE.
+ *
+ * @param {Objects} options - Extra options for the IDE (Used by EE).
+ */
+export function startIde(options) {
+ document.addEventListener('DOMContentLoaded', () => {
+ const ideElement = document.getElementById('ide');
+ if (ideElement) {
+ resetServiceWorkersPublicPath();
+ initIde(ideElement, options);
+ }
+ });
+}
diff --git a/app/assets/javascripts/ide/lib/diff/diff.js b/app/assets/javascripts/ide/lib/diff/diff.js
index 0e37f5c4704..9b7ed68b893 100644
--- a/app/assets/javascripts/ide/lib/diff/diff.js
+++ b/app/assets/javascripts/ide/lib/diff/diff.js
@@ -11,14 +11,16 @@ export const computeDiff = (originalContent, newContent) => {
if (findOnLine) {
Object.assign(findOnLine, change, {
modified: true,
- endLineNumber: (lineNumber + change.count) - 1,
+ endLineNumber: lineNumber + change.count - 1,
});
} else if ('added' in change || 'removed' in change) {
- acc.push(Object.assign({}, change, {
- lineNumber,
- modified: undefined,
- endLineNumber: (lineNumber + change.count) - 1,
- }));
+ acc.push(
+ Object.assign({}, change, {
+ lineNumber,
+ modified: undefined,
+ endLineNumber: lineNumber + change.count - 1,
+ }),
+ );
}
if (!change.removed) {
diff --git a/app/assets/javascripts/ide/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js
index 78b2eab6399..77416a8de9d 100644
--- a/app/assets/javascripts/ide/lib/diff/diff_worker.js
+++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js
@@ -1,7 +1,7 @@
import { computeDiff } from './diff';
// eslint-disable-next-line no-restricted-globals
-self.addEventListener('message', (e) => {
+self.addEventListener('message', e => {
const { data } = e;
// eslint-disable-next-line no-restricted-globals
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index aa02dfbddc4..e10a132ab4b 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -4,6 +4,7 @@ import { visitUrl } from '~/lib/utils/url_utility';
import flash from '~/flash';
import * as types from './mutation_types';
import FilesDecoratorWorker from './workers/files_decorator_worker';
+import { stageKeys } from '../constants';
export const redirectToUrl = (_, url) => visitUrl(url);
@@ -122,14 +123,28 @@ export const scrollToTab = () => {
});
};
-export const stageAllChanges = ({ state, commit }) => {
+export const stageAllChanges = ({ state, commit, dispatch }) => {
+ const openFile = state.openFiles[0];
+
commit(types.SET_LAST_COMMIT_MSG, '');
state.changedFiles.forEach(file => commit(types.STAGE_CHANGE, file.path));
+
+ dispatch('openPendingTab', {
+ file: state.stagedFiles.find(f => f.path === openFile.path),
+ keyPrefix: stageKeys.staged,
+ });
};
-export const unstageAllChanges = ({ state, commit }) => {
+export const unstageAllChanges = ({ state, commit, dispatch }) => {
+ const openFile = state.openFiles[0];
+
state.stagedFiles.forEach(file => commit(types.UNSTAGE_CHANGE, file.path));
+
+ dispatch('openPendingTab', {
+ file: state.changedFiles.find(f => f.path === openFile.path),
+ keyPrefix: stageKeys.unstaged,
+ });
};
export const updateViewer = ({ commit }, viewer) => {
@@ -169,10 +184,6 @@ export const burstUnusedSeal = ({ state, commit }) => {
}
};
-export const setRightPane = ({ commit }, view) => {
- commit(types.SET_RIGHT_PANE, view);
-};
-
export const setLinks = ({ commit }, links) => commit(types.SET_LINKS, links);
export const setErrorMessage = ({ commit }, errorMessage) =>
@@ -206,6 +217,7 @@ export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
export const renameEntry = ({ dispatch, commit, state }, { path, name, entryPath = null }) => {
const entry = state.entries[entryPath || path];
+
commit(types.RENAME_ENTRY, { path, name, entryPath });
if (entry.type === 'tree') {
@@ -214,7 +226,7 @@ export const renameEntry = ({ dispatch, commit, state }, { path, name, entryPath
);
}
- if (!entryPath) {
+ if (!entryPath && !entry.tempFile) {
dispatch('deleteEntry', path);
}
};
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index c9795750d65..30dcf7ef4df 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -5,7 +5,7 @@ import service from '../../services';
import * as types from '../mutation_types';
import router from '../../ide_router';
import { setPageTitle } from '../utils';
-import { viewerTypes } from '../../constants';
+import { viewerTypes, stageKeys } from '../../constants';
export const closeFile = ({ commit, state, dispatch }, file) => {
const { path } = file;
@@ -92,7 +92,7 @@ export const setFileMrChange = ({ commit }, { file, mrChange }) => {
commit(types.SET_FILE_MERGE_REQUEST_CHANGE, { file, mrChange });
};
-export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) => {
+export const getRawFileData = ({ state, commit, dispatch, getters }, { path }) => {
const file = state.entries[path];
return new Promise((resolve, reject) => {
service
@@ -100,6 +100,9 @@ export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) =
.then(raw => {
if (!(file.tempFile && !file.prevPath)) commit(types.SET_FILE_RAW_DATA, { file, raw });
if (file.mrChange && file.mrChange.new_file === false) {
+ const baseSha =
+ (getters.currentMergeRequest && getters.currentMergeRequest.baseCommitSha) || '';
+
service
.getBaseRawFileData(file, baseSha)
.then(baseRaw => {
@@ -122,7 +125,7 @@ export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) =
action: payload =>
dispatch('getRawFileData', payload).then(() => dispatch('setErrorMessage', null)),
actionText: __('Please try again'),
- actionPayload: { path, baseSha },
+ actionPayload: { path },
});
reject();
});
@@ -205,8 +208,9 @@ export const discardFileChanges = ({ dispatch, state, commit, getters }, path) =
eventHub.$emit(`editor.update.model.dispose.unstaged-${file.key}`, file.content);
};
-export const stageChange = ({ commit, state }, path) => {
+export const stageChange = ({ commit, state, dispatch }, path) => {
const stagedFile = state.stagedFiles.find(f => f.path === path);
+ const openFile = state.openFiles.find(f => f.path === path);
commit(types.STAGE_CHANGE, path);
commit(types.SET_LAST_COMMIT_MSG, '');
@@ -214,21 +218,39 @@ export const stageChange = ({ commit, state }, path) => {
if (stagedFile) {
eventHub.$emit(`editor.update.model.new.content.staged-${stagedFile.key}`, stagedFile.content);
}
+
+ if (openFile && openFile.active) {
+ const file = state.stagedFiles.find(f => f.path === path);
+
+ dispatch('openPendingTab', {
+ file,
+ keyPrefix: stageKeys.staged,
+ });
+ }
};
-export const unstageChange = ({ commit }, path) => {
+export const unstageChange = ({ commit, dispatch, state }, path) => {
+ const openFile = state.openFiles.find(f => f.path === path);
+
commit(types.UNSTAGE_CHANGE, path);
+
+ if (openFile && openFile.active) {
+ const file = state.changedFiles.find(f => f.path === path);
+
+ dispatch('openPendingTab', {
+ file,
+ keyPrefix: stageKeys.unstaged,
+ });
+ }
};
-export const openPendingTab = ({ commit, getters, dispatch, state }, { file, keyPrefix }) => {
+export const openPendingTab = ({ commit, getters, state }, { file, keyPrefix }) => {
if (getters.activeFile && getters.activeFile.key === `${keyPrefix}-${file.key}`) return false;
state.openFiles.forEach(f => eventHub.$emit(`editor.update.model.dispose.${f.key}`));
commit(types.ADD_PENDING_TAB, { file, keyPrefix });
- dispatch('scrollToTab');
-
router.push(`/project/${file.projectId}/tree/${state.currentBranchId}/`);
return true;
diff --git a/app/assets/javascripts/ide/stores/actions/merge_request.js b/app/assets/javascripts/ide/stores/actions/merge_request.js
index 187f8c75d07..3ac2f8b3698 100644
--- a/app/assets/javascripts/ide/stores/actions/merge_request.js
+++ b/app/assets/javascripts/ide/stores/actions/merge_request.js
@@ -116,57 +116,57 @@ export const openMergeRequest = (
targetProjectId,
mergeRequestId,
})
- .then(mr => {
- dispatch('setCurrentBranchId', mr.source_branch);
+ .then(mr => {
+ dispatch('setCurrentBranchId', mr.source_branch);
- dispatch('getBranchData', {
- projectId,
- branchId: mr.source_branch,
- });
+ dispatch('getBranchData', {
+ projectId,
+ branchId: mr.source_branch,
+ });
- return dispatch('getFiles', {
- projectId,
- branchId: mr.source_branch,
- });
- })
- .then(() =>
- dispatch('getMergeRequestVersions', {
- projectId,
- targetProjectId,
- mergeRequestId,
- }),
- )
- .then(() =>
- dispatch('getMergeRequestChanges', {
- projectId,
- targetProjectId,
- mergeRequestId,
- }),
- )
- .then(mrChanges => {
- if (mrChanges.changes.length) {
- dispatch('updateActivityBarView', activityBarViews.review);
- }
+ return dispatch('getFiles', {
+ projectId,
+ branchId: mr.source_branch,
+ });
+ })
+ .then(() =>
+ dispatch('getMergeRequestVersions', {
+ projectId,
+ targetProjectId,
+ mergeRequestId,
+ }),
+ )
+ .then(() =>
+ dispatch('getMergeRequestChanges', {
+ projectId,
+ targetProjectId,
+ mergeRequestId,
+ }),
+ )
+ .then(mrChanges => {
+ if (mrChanges.changes.length) {
+ dispatch('updateActivityBarView', activityBarViews.review);
+ }
- mrChanges.changes.forEach((change, ind) => {
- const changeTreeEntry = state.entries[change.new_path];
+ mrChanges.changes.forEach((change, ind) => {
+ const changeTreeEntry = state.entries[change.new_path];
- if (changeTreeEntry) {
- dispatch('setFileMrChange', {
- file: changeTreeEntry,
- mrChange: change,
- });
-
- if (ind < 10) {
- dispatch('getFileData', {
- path: change.new_path,
- makeFileActive: ind === 0,
+ if (changeTreeEntry) {
+ dispatch('setFileMrChange', {
+ file: changeTreeEntry,
+ mrChange: change,
});
+
+ if (ind < 10) {
+ dispatch('getFileData', {
+ path: change.new_path,
+ makeFileActive: ind === 0,
+ });
+ }
}
- }
+ });
+ })
+ .catch(e => {
+ flash(__('Error while loading the merge request. Please try again.'));
+ throw e;
});
- })
- .catch(e => {
- flash(__('Error while loading the merge request. Please try again.'));
- throw e;
- });
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
index 543dc6c0461..2cb08ab2945 100644
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ b/app/assets/javascripts/ide/stores/actions/project.js
@@ -125,10 +125,7 @@ export const showBranchNotFoundError = ({ dispatch }, branchId) => {
});
};
-export const openBranch = (
- { dispatch, state },
- { projectId, branchId, basePath },
-) => {
+export const openBranch = ({ dispatch, state }, { projectId, branchId, basePath }) => {
dispatch('setCurrentBranchId', branchId);
dispatch('getBranchData', {
@@ -136,23 +133,20 @@ export const openBranch = (
branchId,
});
- return (
- dispatch('getFiles', {
- projectId,
- branchId,
- })
- .then(() => {
- if (basePath) {
- const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
- const treeEntryKey = Object.keys(state.entries).find(
- key => key === path && !state.entries[key].pending,
- );
- const treeEntry = state.entries[treeEntryKey];
+ return dispatch('getFiles', {
+ projectId,
+ branchId,
+ }).then(() => {
+ if (basePath) {
+ const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
+ const treeEntryKey = Object.keys(state.entries).find(
+ key => key === path && !state.entries[key].pending,
+ );
+ const treeEntry = state.entries[treeEntryKey];
- if (treeEntry) {
- dispatch('handleTreeEntryAction', treeEntry);
- }
+ if (treeEntry) {
+ dispatch('handleTreeEntryAction', treeEntry);
}
- })
- );
+ }
+ });
};
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index 709748fb530..8ad85074d6b 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -63,7 +63,7 @@ export const isEditModeActive = state => state.currentActivityView === activityB
export const isCommitModeActive = state => state.currentActivityView === activityBarViews.commit;
export const isReviewModeActive = state => state.currentActivityView === activityBarViews.review;
-export const someUncommitedChanges = state =>
+export const someUncommittedChanges = state =>
!!(state.changedFiles.length || state.stagedFiles.length);
export const getChangesInFolder = state => path => {
diff --git a/app/assets/javascripts/ide/stores/index.js b/app/assets/javascripts/ide/stores/index.js
index a601dc8f5a0..f1f544b52b2 100644
--- a/app/assets/javascripts/ide/stores/index.js
+++ b/app/assets/javascripts/ide/stores/index.js
@@ -8,6 +8,8 @@ import commitModule from './modules/commit';
import pipelines from './modules/pipelines';
import mergeRequests from './modules/merge_requests';
import branches from './modules/branches';
+import fileTemplates from './modules/file_templates';
+import paneModule from './modules/pane';
Vue.use(Vuex);
@@ -22,6 +24,8 @@ export const createStore = () =>
pipelines,
mergeRequests,
branches,
+ fileTemplates: fileTemplates(),
+ rightPane: paneModule(),
},
});
diff --git a/app/assets/javascripts/ide/stores/modules/branches/mutations.js b/app/assets/javascripts/ide/stores/modules/branches/mutations.js
index 081ec2d4c28..0a455f4500f 100644
--- a/app/assets/javascripts/ide/stores/modules/branches/mutations.js
+++ b/app/assets/javascripts/ide/stores/modules/branches/mutations.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
export default {
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 462ca45db9b..d97a950a8b2 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -30,9 +30,9 @@ export const setLastCommitMessage = ({ rootState, commit }, data) => {
const currentProject = rootState.projects[rootState.currentProjectId];
const commitStats = data.stats
? sprintf(__('with %{additions} additions, %{deletions} deletions.'), {
- additions: data.stats.additions, // eslint-disable-line indent-legacy
- deletions: data.stats.deletions, // eslint-disable-line indent-legacy
- }) // eslint-disable-line indent-legacy
+ additions: data.stats.additions,
+ deletions: data.stats.deletions,
+ })
: '';
const commitMsg = sprintf(
__('Your changes have been committed. Commit %{commitId} %{commitStats}'),
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
new file mode 100644
index 00000000000..b7090e09daf
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
@@ -0,0 +1,117 @@
+import Api from '~/api';
+import { __ } from '~/locale';
+import { normalizeHeaders } from '~/lib/utils/common_utils';
+import * as types from './mutation_types';
+import eventHub from '../../../eventhub';
+
+export const requestTemplateTypes = ({ commit }) => commit(types.REQUEST_TEMPLATE_TYPES);
+export const receiveTemplateTypesError = ({ commit, dispatch }) => {
+ commit(types.RECEIVE_TEMPLATE_TYPES_ERROR);
+ dispatch(
+ 'setErrorMessage',
+ {
+ text: __('Error loading template types.'),
+ action: () =>
+ dispatch('fetchTemplateTypes').then(() =>
+ dispatch('setErrorMessage', null, { root: true }),
+ ),
+ actionText: __('Please try again'),
+ },
+ { root: true },
+ );
+};
+export const receiveTemplateTypesSuccess = ({ commit }, templates) =>
+ commit(types.RECEIVE_TEMPLATE_TYPES_SUCCESS, templates);
+
+export const fetchTemplateTypes = ({ dispatch, state, rootState }, page = 1) => {
+ if (!Object.keys(state.selectedTemplateType).length) return Promise.reject();
+
+ dispatch('requestTemplateTypes');
+
+ return Api.projectTemplates(rootState.currentProjectId, state.selectedTemplateType.key, { page })
+ .then(({ data, headers }) => {
+ const nextPage = parseInt(normalizeHeaders(headers)['X-NEXT-PAGE'], 10);
+
+ dispatch('receiveTemplateTypesSuccess', data);
+
+ if (nextPage) {
+ dispatch('fetchTemplateTypes', nextPage);
+ }
+ })
+ .catch(() => dispatch('receiveTemplateTypesError'));
+};
+
+export const setSelectedTemplateType = ({ commit, dispatch, rootGetters }, type) => {
+ commit(types.SET_SELECTED_TEMPLATE_TYPE, type);
+
+ if (rootGetters.activeFile.prevPath === type.name) {
+ dispatch('discardFileChanges', rootGetters.activeFile.path, { root: true });
+ } else if (rootGetters.activeFile.name !== type.name) {
+ dispatch(
+ 'renameEntry',
+ {
+ path: rootGetters.activeFile.path,
+ name: type.name,
+ },
+ { root: true },
+ );
+ }
+};
+
+export const receiveTemplateError = ({ dispatch }, template) => {
+ dispatch(
+ 'setErrorMessage',
+ {
+ text: __('Error loading template.'),
+ action: payload =>
+ dispatch('fetchTemplateTypes', payload).then(() =>
+ dispatch('setErrorMessage', null, { root: true }),
+ ),
+ actionText: __('Please try again'),
+ actionPayload: template,
+ },
+ { root: true },
+ );
+};
+
+export const fetchTemplate = ({ dispatch, state, rootState }, template) => {
+ if (template.content) {
+ return dispatch('setFileTemplate', template);
+ }
+
+ return Api.projectTemplate(
+ rootState.currentProjectId,
+ state.selectedTemplateType.key,
+ template.key || template.name,
+ )
+ .then(({ data }) => {
+ dispatch('setFileTemplate', data);
+ })
+ .catch(() => dispatch('receiveTemplateError', template));
+};
+
+export const setFileTemplate = ({ dispatch, commit, rootGetters }, template) => {
+ dispatch(
+ 'changeFileContent',
+ { path: rootGetters.activeFile.path, content: template.content },
+ { root: true },
+ );
+ commit(types.SET_UPDATE_SUCCESS, true);
+ eventHub.$emit(`editor.update.model.new.content.${rootGetters.activeFile.key}`, template.content);
+};
+
+export const undoFileTemplate = ({ dispatch, commit, rootGetters }) => {
+ const file = rootGetters.activeFile;
+
+ dispatch('changeFileContent', { path: file.path, content: file.raw }, { root: true });
+ commit(types.SET_UPDATE_SUCCESS, false);
+
+ eventHub.$emit(`editor.update.model.new.content.${file.key}`, file.raw);
+
+ if (file.prevPath) {
+ dispatch('discardFileChanges', file.path, { root: true });
+ }
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/getters.js b/app/assets/javascripts/ide/stores/modules/file_templates/getters.js
new file mode 100644
index 00000000000..628babe6a01
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/getters.js
@@ -0,0 +1,26 @@
+import { activityBarViews } from '../../../constants';
+
+export const templateTypes = () => [
+ {
+ name: '.gitlab-ci.yml',
+ key: 'gitlab_ci_ymls',
+ },
+ {
+ name: '.gitignore',
+ key: 'gitignores',
+ },
+ {
+ name: 'LICENSE',
+ key: 'licenses',
+ },
+ {
+ name: 'Dockerfile',
+ key: 'dockerfiles',
+ },
+];
+
+export const showFileTemplatesBar = (_, getters, rootState) => name =>
+ getters.templateTypes.find(t => t.name === name) &&
+ rootState.currentActivityView === activityBarViews.edit;
+
+export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/index.js b/app/assets/javascripts/ide/stores/modules/file_templates/index.js
new file mode 100644
index 00000000000..383ff5db392
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/index.js
@@ -0,0 +1,12 @@
+import createState from './state';
+import * as actions from './actions';
+import * as getters from './getters';
+import mutations from './mutations';
+
+export default () => ({
+ namespaced: true,
+ actions,
+ state: createState(),
+ getters,
+ mutations,
+});
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/mutation_types.js b/app/assets/javascripts/ide/stores/modules/file_templates/mutation_types.js
new file mode 100644
index 00000000000..cf4499c0264
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/mutation_types.js
@@ -0,0 +1,7 @@
+export const REQUEST_TEMPLATE_TYPES = 'REQUEST_TEMPLATE_TYPES';
+export const RECEIVE_TEMPLATE_TYPES_ERROR = 'RECEIVE_TEMPLATE_TYPES_ERROR';
+export const RECEIVE_TEMPLATE_TYPES_SUCCESS = 'RECEIVE_TEMPLATE_TYPES_SUCCESS';
+
+export const SET_SELECTED_TEMPLATE_TYPE = 'SET_SELECTED_TEMPLATE_TYPE';
+
+export const SET_UPDATE_SUCCESS = 'SET_UPDATE_SUCCESS';
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js b/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js
new file mode 100644
index 00000000000..25a65b047f1
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js
@@ -0,0 +1,21 @@
+import * as types from './mutation_types';
+
+export default {
+ [types.REQUEST_TEMPLATE_TYPES](state) {
+ state.isLoading = true;
+ },
+ [types.RECEIVE_TEMPLATE_TYPES_ERROR](state) {
+ state.isLoading = false;
+ },
+ [types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, templates) {
+ state.isLoading = false;
+ state.templates = state.templates.concat(templates);
+ },
+ [types.SET_SELECTED_TEMPLATE_TYPE](state, type) {
+ state.selectedTemplateType = type;
+ state.templates = [];
+ },
+ [types.SET_UPDATE_SUCCESS](state, success) {
+ state.updateSuccess = success;
+ },
+};
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/state.js b/app/assets/javascripts/ide/stores/modules/file_templates/state.js
new file mode 100644
index 00000000000..bd4b7d7bc52
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/state.js
@@ -0,0 +1,6 @@
+export default () => ({
+ isLoading: false,
+ templates: [],
+ selectedTemplateType: {},
+ updateSuccess: false,
+});
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
index baa2497ec5b..4565c11a83f 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
@@ -3,8 +3,7 @@ import Api from '../../../../api';
import { scopes } from './constants';
import * as types from './mutation_types';
-export const requestMergeRequests = ({ commit }) =>
- commit(types.REQUEST_MERGE_REQUESTS);
+export const requestMergeRequests = ({ commit }) => commit(types.REQUEST_MERGE_REQUESTS);
export const receiveMergeRequestsError = ({ commit, dispatch }, { type, search }) => {
dispatch(
'setErrorMessage',
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js b/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
index 98102a68e08..0eba9c39817 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
export default {
diff --git a/app/assets/javascripts/ide/stores/modules/pane/actions.js b/app/assets/javascripts/ide/stores/modules/pane/actions.js
new file mode 100644
index 00000000000..7f5d167a14f
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/pane/actions.js
@@ -0,0 +1,30 @@
+import * as types from './mutation_types';
+
+export const toggleOpen = ({ dispatch, state }, view) => {
+ if (state.isOpen) {
+ dispatch('close');
+ } else {
+ dispatch('open', view);
+ }
+};
+
+export const open = ({ commit }, view) => {
+ commit(types.SET_OPEN, true);
+
+ if (view) {
+ const { name, keepAlive } = view;
+
+ commit(types.SET_CURRENT_VIEW, name);
+
+ if (keepAlive) {
+ commit(types.KEEP_ALIVE_VIEW, name);
+ }
+ }
+};
+
+export const close = ({ commit }) => {
+ commit(types.SET_OPEN, false);
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/pane/getters.js b/app/assets/javascripts/ide/stores/modules/pane/getters.js
new file mode 100644
index 00000000000..c346cf13689
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/pane/getters.js
@@ -0,0 +1,4 @@
+export const isActiveView = state => view => state.currentView === view;
+
+export const isAliveView = (state, getters) => view =>
+ state.keepAliveViews[view] || (state.isOpen && getters.isActiveView(view));
diff --git a/app/assets/javascripts/ide/stores/modules/pane/index.js b/app/assets/javascripts/ide/stores/modules/pane/index.js
new file mode 100644
index 00000000000..5f61cb732c8
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/pane/index.js
@@ -0,0 +1,12 @@
+import * as actions from './actions';
+import * as getters from './getters';
+import mutations from './mutations';
+import state from './state';
+
+export default () => ({
+ namespaced: true,
+ state: state(),
+ actions,
+ getters,
+ mutations,
+});
diff --git a/app/assets/javascripts/ide/stores/modules/pane/mutation_types.js b/app/assets/javascripts/ide/stores/modules/pane/mutation_types.js
new file mode 100644
index 00000000000..abdebc4d913
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/pane/mutation_types.js
@@ -0,0 +1,3 @@
+export const SET_OPEN = 'SET_OPEN';
+export const SET_CURRENT_VIEW = 'SET_CURRENT_VIEW';
+export const KEEP_ALIVE_VIEW = 'KEEP_ALIVE_VIEW';
diff --git a/app/assets/javascripts/ide/stores/modules/pane/mutations.js b/app/assets/javascripts/ide/stores/modules/pane/mutations.js
new file mode 100644
index 00000000000..c16484b4402
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/pane/mutations.js
@@ -0,0 +1,19 @@
+import * as types from './mutation_types';
+
+export default {
+ [types.SET_OPEN](state, isOpen) {
+ Object.assign(state, {
+ isOpen,
+ });
+ },
+ [types.SET_CURRENT_VIEW](state, currentView) {
+ Object.assign(state, {
+ currentView,
+ });
+ },
+ [types.KEEP_ALIVE_VIEW](state, viewName) {
+ Object.assign(state.keepAliveViews, {
+ [viewName]: true,
+ });
+ },
+};
diff --git a/app/assets/javascripts/ide/stores/modules/pane/state.js b/app/assets/javascripts/ide/stores/modules/pane/state.js
new file mode 100644
index 00000000000..353065b5735
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/pane/state.js
@@ -0,0 +1,5 @@
+export default () => ({
+ isOpen: false,
+ currentView: null,
+ keepAliveViews: {},
+});
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
index 3e67b222e66..ea2525a2f0e 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
@@ -28,7 +28,7 @@ export const receiveLatestPipelineError = ({ commit, dispatch }, err) => {
dispatch(
'setErrorMessage',
{
- text: __('An error occured whilst fetching the latest pipline.'),
+ text: __('An error occured whilst fetching the latest pipeline.'),
action: () =>
dispatch('forcePipelineRequest').then(() =>
dispatch('setErrorMessage', null, { root: true }),
@@ -113,7 +113,7 @@ export const toggleStageCollapsed = ({ commit }, stageId) =>
export const setDetailJob = ({ commit, dispatch }, job) => {
commit(types.SET_DETAIL_JOB, job);
- dispatch('setRightPane', job ? rightSidebarViews.jobsDetail : rightSidebarViews.pipelines, {
+ dispatch('rightPane/open', job ? rightSidebarViews.jobsDetail : rightSidebarViews.pipelines, {
root: true,
});
};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js
index 5a2213bbe89..b4be100cb07 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
import { normalizeJob } from './utils';
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index 5a7991d2fa7..a5f8098dc17 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -68,8 +68,6 @@ export const UPDATE_TEMP_FLAG = 'UPDATE_TEMP_FLAG';
export const TOGGLE_FILE_FINDER = 'TOGGLE_FILE_FINDER';
export const BURST_UNUSED_SEAL = 'BURST_UNUSED_SEAL';
-export const SET_RIGHT_PANE = 'SET_RIGHT_PANE';
-
export const CLEAR_PROJECTS = 'CLEAR_PROJECTS';
export const RESET_OPEN_FILES = 'RESET_OPEN_FILES';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 1eda5768709..78cdfda74f0 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-param-reassign */
+import Vue from 'vue';
import * as types from './mutation_types';
import projectMutations from './mutations/project';
import mergeRequestMutation from './mutations/merge_request';
@@ -146,13 +146,7 @@ export default {
staged: false,
prevPath: '',
moved: false,
- lastCommit: Object.assign(state.entries[file.path].lastCommit, {
- id: lastCommit.commit.id,
- url: lastCommit.commit_path,
- message: lastCommit.commit.message,
- author: lastCommit.commit.author_name,
- updatedAt: lastCommit.commit.authored_date,
- }),
+ lastCommitSha: lastCommit.commit.id,
});
if (prevPath) {
@@ -172,11 +166,6 @@ export default {
unusedSeal: false,
});
},
- [types.SET_RIGHT_PANE](state, view) {
- Object.assign(state, {
- rightPane: state.rightPane === view ? null : view,
- });
- },
[types.SET_LINKS](state, links) {
Object.assign(state, { links });
},
@@ -200,6 +189,7 @@ export default {
},
[types.DELETE_ENTRY](state, path) {
const entry = state.entries[path];
+ const { tempFile = false } = entry;
const parent = entry.parentPath
? state.entries[entry.parentPath]
: state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
@@ -209,7 +199,11 @@ export default {
parent.tree = parent.tree.filter(f => f.path !== entry.path);
if (entry.type === 'blob') {
- state.changedFiles = state.changedFiles.concat(entry);
+ if (tempFile) {
+ state.changedFiles = state.changedFiles.filter(f => f.path !== path);
+ } else {
+ state.changedFiles = state.changedFiles.concat(entry);
+ }
}
},
[types.RENAME_ENTRY](state, { path, name, entryPath = null }) {
@@ -228,7 +222,7 @@ export default {
path: newPath,
name: entryPath ? oldEntry.name : name,
tempFile: true,
- prevPath: oldEntry.path,
+ prevPath: oldEntry.tempFile ? null : oldEntry.path,
url: oldEntry.url.replace(new RegExp(`${oldEntry.path}/?$`), newPath),
tree: [],
parentPath,
@@ -247,6 +241,20 @@ export default {
if (newEntry.type === 'blob') {
state.changedFiles = state.changedFiles.concat(newEntry);
}
+
+ if (state.entries[newPath].opened) {
+ state.openFiles.push(state.entries[newPath]);
+ }
+
+ if (oldEntry.tempFile) {
+ const filterMethod = f => f.path !== oldEntry.path;
+
+ state.openFiles = state.openFiles.filter(filterMethod);
+ state.changedFiles = state.changedFiles.filter(filterMethod);
+ parent.tree = parent.tree.filter(filterMethod);
+
+ Vue.delete(state.entries, oldEntry.path);
+ }
},
...projectMutations,
...mergeRequestMutation,
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index a937fb157f8..6ca246c1d63 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-param-reassign */
import * as types from '../mutation_types';
import { sortTree } from '../utils';
import { diffModes } from '../../constants';
@@ -56,7 +55,7 @@ export default {
f => f.path === file.path && f.pending && !(f.tempFile && !f.prevPath),
);
- if (file.tempFile) {
+ if (file.tempFile && file.content === '') {
Object.assign(state.entries[file.path], {
content: raw,
});
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index 46b52fa00fc..d400b9831a9 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -23,7 +23,6 @@ export default () => ({
currentActivityView: activityBarViews.edit,
unusedSeal: true,
fileFindVisible: false,
- rightPane: null,
links: {},
errorMessage: null,
entryModal: {
diff --git a/app/assets/javascripts/image_diff/image_diff.js b/app/assets/javascripts/image_diff/image_diff.js
index fab0255c378..3587f073a00 100644
--- a/app/assets/javascripts/image_diff/image_diff.js
+++ b/app/assets/javascripts/image_diff/image_diff.js
@@ -60,8 +60,10 @@ export default class ImageDiff {
}
renderBadge(discussionEl, index) {
- const imageBadge = imageDiffHelper
- .generateBadgeFromDiscussionDOM(this.imageFrameEl, discussionEl);
+ const imageBadge = imageDiffHelper.generateBadgeFromDiscussionDOM(
+ this.imageFrameEl,
+ discussionEl,
+ );
this.imageBadges.push(imageBadge);
diff --git a/app/assets/javascripts/image_diff/init_discussion_tab.js b/app/assets/javascripts/image_diff/init_discussion_tab.js
index 2f16c6ef115..dbe4c06a4e9 100644
--- a/app/assets/javascripts/image_diff/init_discussion_tab.js
+++ b/app/assets/javascripts/image_diff/init_discussion_tab.js
@@ -8,5 +8,6 @@ export default () => {
const diffFileEls = document.querySelectorAll('.timeline-content .diff-file.js-image-file');
[...diffFileEls].forEach(diffFileEl =>
- imageDiffHelper.initImageDiff(diffFileEl, canCreateNote, renderCommentBadge));
+ imageDiffHelper.initImageDiff(diffFileEl, canCreateNote, renderCommentBadge),
+ );
};
diff --git a/app/assets/javascripts/image_diff/replaced_image_diff.js b/app/assets/javascripts/image_diff/replaced_image_diff.js
index 4abd13fb472..8d9e65155d8 100644
--- a/app/assets/javascripts/image_diff/replaced_image_diff.js
+++ b/app/assets/javascripts/image_diff/replaced_image_diff.js
@@ -26,7 +26,7 @@ export default class ReplacedImageDiff extends ImageDiff {
this.imageEls = {};
const viewTypeNames = Object.getOwnPropertyNames(viewTypes);
- viewTypeNames.forEach((viewType) => {
+ viewTypeNames.forEach(viewType => {
this.imageEls[viewType] = this.imageFrameEls[viewType].querySelector('img');
});
}
@@ -79,13 +79,12 @@ export default class ReplacedImageDiff extends ImageDiff {
// Re-render indicator in new view
if (indicator.removed) {
- const normalizedIndicator = imageDiffHelper
- .resizeCoordinatesToImageElement(this.imageEl, {
- x: indicator.x,
- y: indicator.y,
- width: indicator.image.width,
- height: indicator.image.height,
- });
+ const normalizedIndicator = imageDiffHelper.resizeCoordinatesToImageElement(this.imageEl, {
+ x: indicator.x,
+ y: indicator.y,
+ width: indicator.image.width,
+ height: indicator.image.height,
+ });
imageDiffHelper.showCommentIndicator(this.imageFrameEl, normalizedIndicator);
}
}
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
index 0035d809062..f1beb1a8ea5 100644
--- a/app/assets/javascripts/importer_status.js
+++ b/app/assets/javascripts/importer_status.js
@@ -60,66 +60,71 @@ class ImporterStatus {
attributes = Object.assign(repoData, attributes);
}
- return axios.post(this.importUrl, attributes)
- .then(({ data }) => {
- const job = $(`tr#repo_${id}`);
- job.attr('id', `project_${data.id}`);
-
- job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`);
- $('table.import-jobs tbody').prepend(job);
-
- job.addClass('table-active');
- const connectingVerb = this.ciCdOnly ? __('connecting') : __('importing');
- job.find('.import-actions').html(sprintf(
- _.escape(__('%{loadingIcon} Started')), {
- loadingIcon: `<i class="fa fa-spinner fa-spin" aria-label="${_.escape(connectingVerb)}"></i>`,
- },
- false,
- ));
- })
- .catch((error) => {
- let details = error;
-
- const $statusField = $(`#repo_${this.id} .job-status`);
- $statusField.text(__('Failed'));
-
- if (error.response && error.response.data && error.response.data.errors) {
- details = error.response.data.errors;
- }
-
- flash(__(`An error occurred while importing project: ${details}`));
- });
+ return axios
+ .post(this.importUrl, attributes)
+ .then(({ data }) => {
+ const job = $(`tr#repo_${id}`);
+ job.attr('id', `project_${data.id}`);
+
+ job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`);
+ $('table.import-jobs tbody').prepend(job);
+
+ job.addClass('table-active');
+ const connectingVerb = this.ciCdOnly ? __('connecting') : __('importing');
+ job.find('.import-actions').html(
+ sprintf(
+ _.escape(__('%{loadingIcon} Started')),
+ {
+ loadingIcon: `<i class="fa fa-spinner fa-spin" aria-label="${_.escape(
+ connectingVerb,
+ )}"></i>`,
+ },
+ false,
+ ),
+ );
+ })
+ .catch(error => {
+ let details = error;
+
+ const $statusField = $(`#repo_${this.id} .job-status`);
+ $statusField.text(__('Failed'));
+
+ if (error.response && error.response.data && error.response.data.errors) {
+ details = error.response.data.errors;
+ }
+
+ flash(sprintf(__('An error occurred while importing project: %{details}'), { details }));
+ });
}
autoUpdate() {
- return axios.get(this.jobsUrl)
- .then(({ data = [] }) => {
- data.forEach((job) => {
- const jobItem = $(`#project_${job.id}`);
- const statusField = jobItem.find('.job-status');
-
- const spinner = '<i class="fa fa-spinner fa-spin"></i>';
-
- switch (job.import_status) {
- case 'finished':
- jobItem.removeClass('table-active').addClass('table-success');
- statusField.html(`<span><i class="fa fa-check"></i> ${__('Done')}</span>`);
- break;
- case 'scheduled':
- statusField.html(`${spinner} ${__('Scheduled')}`);
- break;
- case 'started':
- statusField.html(`${spinner} ${__('Started')}`);
- break;
- case 'failed':
- statusField.html(__('Failed'));
- break;
- default:
- statusField.html(job.import_status);
- break;
- }
- });
+ return axios.get(this.jobsUrl).then(({ data = [] }) => {
+ data.forEach(job => {
+ const jobItem = $(`#project_${job.id}`);
+ const statusField = jobItem.find('.job-status');
+
+ const spinner = '<i class="fa fa-spinner fa-spin"></i>';
+
+ switch (job.import_status) {
+ case 'finished':
+ jobItem.removeClass('table-active').addClass('table-success');
+ statusField.html(`<span><i class="fa fa-check"></i> ${__('Done')}</span>`);
+ break;
+ case 'scheduled':
+ statusField.html(`${spinner} ${__('Scheduled')}`);
+ break;
+ case 'started':
+ statusField.html(`${spinner} ${__('Started')}`);
+ break;
+ case 'failed':
+ statusField.html(__('Failed'));
+ break;
+ default:
+ statusField.html(job.import_status);
+ break;
+ }
});
+ });
}
setAutoUpdate() {
@@ -141,7 +146,4 @@ function initImporterStatus() {
}
}
-export {
- initImporterStatus as default,
- ImporterStatus,
-};
+export { initImporterStatus as default, ImporterStatus };
diff --git a/app/assets/javascripts/init_changes_dropdown.js b/app/assets/javascripts/init_changes_dropdown.js
index 5c5a6e01848..e708e5d0978 100644
--- a/app/assets/javascripts/init_changes_dropdown.js
+++ b/app/assets/javascripts/init_changes_dropdown.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import { stickyMonitor } from './lib/utils/sticky';
-export default (stickyTop) => {
+export default stickyTop => {
stickyMonitor(document.querySelector('.js-diff-files-changed'), stickyTop);
$('.js-diff-stats-dropdown').glDropdown({
diff --git a/app/assets/javascripts/init_legacy_filters.js b/app/assets/javascripts/init_legacy_filters.js
deleted file mode 100644
index b6ff97d1279..00000000000
--- a/app/assets/javascripts/init_legacy_filters.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-disable no-new */
-import LabelsSelect from './labels_select';
-import subscriptionSelect from './subscription_select';
-import UsersSelect from './users_select';
-import issueStatusSelect from './issue_status_select';
-import MilestoneSelect from './milestone_select';
-
-export default () => {
- new UsersSelect();
- new LabelsSelect();
- new MilestoneSelect();
- issueStatusSelect();
- subscriptionSelect();
-};
diff --git a/app/assets/javascripts/init_notes.js b/app/assets/javascripts/init_notes.js
index 3c71258e53b..a77828e8cf2 100644
--- a/app/assets/javascripts/init_notes.js
+++ b/app/assets/javascripts/init_notes.js
@@ -2,13 +2,7 @@ import Notes from './notes';
export default () => {
const dataEl = document.querySelector('.js-notes-data');
- const {
- notesUrl,
- notesIds,
- now,
- diffView,
- enableGFM,
- } = JSON.parse(dataEl.innerHTML);
+ const { notesUrl, notesIds, now, diffView, enableGFM } = JSON.parse(dataEl.innerHTML);
// Create a singleton so that we don't need to assign
// into the window object, we can just access the current isntance with Notes.instance
diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js
index bd90d0eaa32..08b858305ab 100644
--- a/app/assets/javascripts/integrations/integration_settings_form.js
+++ b/app/assets/javascripts/integrations/integration_settings_form.js
@@ -97,7 +97,8 @@ export default class IntegrationSettingsForm {
testSettings(formData) {
this.toggleSubmitBtnState(true);
- return axios.put(this.testEndPoint, formData)
+ return axios
+ .put(this.testEndPoint, formData)
.then(({ data }) => {
if (data.error) {
let flashActions;
@@ -105,7 +106,7 @@ export default class IntegrationSettingsForm {
if (data.test_failed) {
flashActions = {
title: 'Save anyway',
- clickHandler: (e) => {
+ clickHandler: e => {
e.preventDefault();
this.$form.submit();
},
diff --git a/app/assets/javascripts/issuable/auto_width_dropdown_select.js b/app/assets/javascripts/issuable/auto_width_dropdown_select.js
index 07cf1eff279..612c524ca1c 100644
--- a/app/assets/javascripts/issuable/auto_width_dropdown_select.js
+++ b/app/assets/javascripts/issuable/auto_width_dropdown_select.js
@@ -27,7 +27,10 @@ class AutoWidthDropdownSelect {
// We have to look at the parent because
// `offsetParent` on a `display: none;` is `null`
- const offsetParentWidth = $(this).parent().offsetParent().width();
+ const offsetParentWidth = $(this)
+ .parent()
+ .offsetParent()
+ .width();
// Reset any width to let it naturally flow
$dropdown.css('width', 'auto');
if ($dropdown.outerWidth(false) > offsetParentWidth) {
diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js
index 35eaf21a836..b844e4c5e5b 100644
--- a/app/assets/javascripts/issuable_bulk_update_actions.js
+++ b/app/assets/javascripts/issuable_bulk_update_actions.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, prefer-arrow-callback, max-len, no-unused-vars */
+/* eslint-disable consistent-return, func-names, array-callback-return, prefer-arrow-callback, no-unused-vars */
import $ from 'jquery';
import _ from 'underscore';
@@ -32,11 +32,11 @@ export default {
onFormSubmitFailure() {
this.form.find('[type="submit"]').enable();
- return new Flash("Issue update failed");
+ return new Flash('Issue update failed');
},
getSelectedIssues() {
- return this.issues.has('.selected_issue:checked');
+ return this.issues.has('.selected-issuable:checked');
},
getLabelsFromSelection() {
@@ -63,7 +63,7 @@ export default {
const result = [];
const labelsToKeep = this.$labelDropdown.data('indeterminate');
- this.getLabelsFromSelection().forEach((id) => {
+ this.getLabelsFromSelection().forEach(id => {
if (labelsToKeep.indexOf(id) === -1) {
result.push(id);
}
@@ -89,8 +89,8 @@ export default {
issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(),
subscription_event: this.form.find('input[name="update[subscription_event]"]').val(),
add_label_ids: [],
- remove_label_ids: []
- }
+ remove_label_ids: [],
+ },
};
if (this.willUpdateLabels) {
formData.update.add_label_ids = this.$labelDropdown.data('marked');
@@ -110,7 +110,7 @@ export default {
getOriginalCommonIds() {
const labelIds = [];
- this.getElement('.selected_issue:checked').each((i, el) => {
+ this.getElement('.selected-issuable:checked').each((i, el) => {
labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'));
});
return _.intersection.apply(this, labelIds);
@@ -119,7 +119,7 @@ export default {
// From issuable's initial bulk selection
getOriginalMarkedIds() {
const labelIds = [];
- this.getElement('.selected_issue:checked').each((i, el) => {
+ this.getElement('.selected-issuable:checked').each((i, el) => {
labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'));
});
return _.intersection.apply(this, labelIds);
@@ -132,9 +132,9 @@ export default {
let issuableLabels = [];
// Collect unique label IDs for all checked issues
- this.getElement('.selected_issue:checked').each((i, el) => {
+ this.getElement('.selected-issuable:checked').each((i, el) => {
issuableLabels = this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels');
- issuableLabels.forEach((labelId) => {
+ issuableLabels.forEach(labelId => {
// Store unique IDs
if (uniqueIds.indexOf(labelId) === -1) {
uniqueIds.push(labelId);
diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js
index 2307c8e0d85..74150ce3a8b 100644
--- a/app/assets/javascripts/issuable_bulk_update_sidebar.js
+++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js
@@ -30,7 +30,7 @@ export default class IssuableBulkUpdateSidebar {
this.$otherFilters = $('.issues-other-filters');
this.$checkAllContainer = $('.check-all-holder');
this.$issueChecks = $('.issue-check');
- this.$issuesList = $('.selected_issue');
+ this.$issuesList = $('.selected-issuable');
this.$issuableIdsInput = $('#update_issuable_ids');
}
@@ -55,7 +55,7 @@ export default class IssuableBulkUpdateSidebar {
}
updateFormState() {
- const noCheckedIssues = !$('.selected_issue:checked').length;
+ const noCheckedIssues = !$('.selected-issuable:checked').length;
this.toggleSubmitButtonDisabled(noCheckedIssues);
this.updateSelectedIssuableIds();
@@ -123,7 +123,7 @@ export default class IssuableBulkUpdateSidebar {
}
static getCheckedIssueIds() {
- const $checkedIssues = $('.selected_issue:checked');
+ const $checkedIssues = $('.selected-issuable:checked');
if ($checkedIssues.length > 0) {
return $.map($checkedIssues, value => $(value).data('id'));
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index 0140960b367..c81a2230310 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -1,6 +1,3 @@
-/* eslint-disable no-new, no-unused-vars, consistent-return, no-else-return */
-/* global GitLab */
-
import $ from 'jquery';
import Pikaday from 'pikaday';
import Autosave from './autosave';
@@ -8,7 +5,7 @@ import UsersSelect from './users_select';
import GfmAutoComplete from './gfm_auto_complete';
import ZenMode from './zen_mode';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
-import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
+import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
export default class IssuableForm {
constructor(form) {
@@ -19,9 +16,11 @@ export default class IssuableForm {
this.handleSubmit = this.handleSubmit.bind(this);
this.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i;
- new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup();
- new UsersSelect();
- new ZenMode();
+ this.gfmAutoComplete = new GfmAutoComplete(
+ gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources,
+ ).setup();
+ this.usersSelect = new UsersSelect();
+ this.zenMode = new ZenMode();
this.titleField = this.form.find('input[name*="[title]"]');
this.descriptionField = this.form.find('textarea[name*="[description]"]');
@@ -57,8 +56,16 @@ export default class IssuableForm {
}
initAutosave() {
- new Autosave(this.titleField, [document.location.pathname, document.location.search, 'title']);
- return new Autosave(this.descriptionField, [document.location.pathname, document.location.search, 'description']);
+ this.autosave = new Autosave(this.titleField, [
+ document.location.pathname,
+ document.location.search,
+ 'title',
+ ]);
+ return new Autosave(this.descriptionField, [
+ document.location.pathname,
+ document.location.search,
+ 'description',
+ ]);
}
handleSubmit() {
@@ -74,7 +81,7 @@ export default class IssuableForm {
this.$wipExplanation = this.form.find('.js-wip-explanation');
this.$noWipExplanation = this.form.find('.js-no-wip-explanation');
if (!(this.$wipExplanation.length && this.$noWipExplanation.length)) {
- return;
+ return undefined;
}
this.form.on('click', '.js-toggle-wip', this.toggleWip);
this.titleField.on('keyup blur', this.renderWipExplanation);
@@ -89,10 +96,9 @@ export default class IssuableForm {
if (this.workInProgress()) {
this.$wipExplanation.show();
return this.$noWipExplanation.hide();
- } else {
- this.$wipExplanation.hide();
- return this.$noWipExplanation.show();
}
+ this.$wipExplanation.hide();
+ return this.$noWipExplanation.show();
}
toggleWip(event) {
@@ -110,7 +116,7 @@ export default class IssuableForm {
}
addWip() {
- this.titleField.val(`WIP: ${(this.titleField.val())}`);
+ this.titleField.val(`WIP: ${this.titleField.val()}`);
}
initTargetBranchDropdown() {
diff --git a/app/assets/javascripts/issuable_index.js b/app/assets/javascripts/issuable_index.js
index 06ec4546164..ffcbd7cf28c 100644
--- a/app/assets/javascripts/issuable_index.js
+++ b/app/assets/javascripts/issuable_index.js
@@ -26,14 +26,17 @@ export default class IssuableIndex {
static resetIncomingEmailToken() {
const $resetToken = $('.incoming-email-token-reset');
- $resetToken.on('click', (e) => {
+ $resetToken.on('click', e => {
e.preventDefault();
$resetToken.text('resetting...');
- axios.put($resetToken.attr('href'))
+ axios
+ .put($resetToken.attr('href'))
.then(({ data }) => {
- $('#issuable_email').val(data.new_address).focus();
+ $('#issuable_email')
+ .val(data.new_address)
+ .focus();
$resetToken.text('reset it');
})
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 8c225cd7d91..94b78907d9a 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-unused-vars, consistent-return, quotes, max-len */
+/* eslint-disable no-var, one-var, no-unused-vars, consistent-return */
import $ from 'jquery';
import axios from './lib/utils/axios_utils';
@@ -28,7 +28,7 @@ export default class Issue {
}
// Listen to state changes in the Vue app
- document.addEventListener('issuable_vue_app:change', (event) => {
+ document.addEventListener('issuable_vue_app:change', event => {
this.updateTopState(event.detail.isClosed, event.detail.data);
});
}
@@ -55,7 +55,13 @@ export default class Issue {
$(document).trigger('issuable:change', isClosed);
this.toggleCloseReopenButton(isClosed);
- let numProjectIssues = Number(projectIssuesCounter.first().text().trim().replace(/[^\d]/, ''));
+ let numProjectIssues = Number(
+ projectIssuesCounter
+ .first()
+ .text()
+ .trim()
+ .replace(/[^\d]/, ''),
+ );
numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
projectIssuesCounter.text(addDelimiter(numProjectIssues));
@@ -76,29 +82,34 @@ export default class Issue {
initIssueBtnEventListeners() {
const issueFailMessage = 'Unable to update this issue at this time.';
- return $(document).on('click', '.js-issuable-actions a.btn-close, .js-issuable-actions a.btn-reopen', (e) => {
- var $button, shouldSubmit, url;
- e.preventDefault();
- e.stopImmediatePropagation();
- $button = $(e.currentTarget);
- shouldSubmit = $button.hasClass('btn-comment');
- if (shouldSubmit) {
- Issue.submitNoteForm($button.closest('form'));
- }
-
- this.disableCloseReopenButton($button);
+ return $(document).on(
+ 'click',
+ '.js-issuable-actions a.btn-close, .js-issuable-actions a.btn-reopen',
+ e => {
+ var $button, shouldSubmit, url;
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ $button = $(e.currentTarget);
+ shouldSubmit = $button.hasClass('btn-comment');
+ if (shouldSubmit) {
+ Issue.submitNoteForm($button.closest('form'));
+ }
- url = $button.attr('href');
- return axios.put(url)
- .then(({ data }) => {
- const isClosed = $button.hasClass('btn-close');
- this.updateTopState(isClosed, data);
- })
- .catch(() => flash(issueFailMessage))
- .then(() => {
- this.disableCloseReopenButton($button, false);
- });
- });
+ this.disableCloseReopenButton($button);
+
+ url = $button.attr('href');
+ return axios
+ .put(url)
+ .then(({ data }) => {
+ const isClosed = $button.hasClass('btn-close');
+ this.updateTopState(isClosed, data);
+ })
+ .catch(() => flash(issueFailMessage))
+ .then(() => {
+ this.disableCloseReopenButton($button, false);
+ });
+ },
+ );
}
initCloseReopenReport() {
@@ -124,7 +135,7 @@ export default class Issue {
static submitNoteForm(form) {
var noteText;
- noteText = form.find("textarea.js-note-text").val();
+ noteText = form.find('textarea.js-note-text').val();
if (noteText && noteText.trim().length > 0) {
return form.submit();
}
@@ -133,22 +144,26 @@ export default class Issue {
static initMergeRequests() {
var $container;
$container = $('#merge-requests');
- return axios.get($container.data('url'))
+ return axios
+ .get($container.data('url'))
.then(({ data }) => {
if ('html' in data) {
$container.html(data.html);
}
- }).catch(() => flash('Failed to load referenced merge requests'));
+ })
+ .catch(() => flash('Failed to load referenced merge requests'));
}
static initRelatedBranches() {
var $container;
$container = $('#related-branches');
- return axios.get($container.data('url'))
+ return axios
+ .get($container.data('url'))
.then(({ data }) => {
if ('html' in data) {
$container.html(data.html);
}
- }).catch(() => flash('Failed to load related branches'));
+ })
+ .catch(() => flash('Failed to load related branches'));
}
}
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index ad928484952..04c1cf021d9 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -1,281 +1,279 @@
<script>
- import Visibility from 'visibilityjs';
- import { visitUrl } from '../../lib/utils/url_utility';
- import Poll from '../../lib/utils/poll';
- import eventHub from '../event_hub';
- import Service from '../services/index';
- import Store from '../stores';
- import titleComponent from './title.vue';
- import descriptionComponent from './description.vue';
- import editedComponent from './edited.vue';
- import formComponent from './form.vue';
- import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
+import Visibility from 'visibilityjs';
+import { visitUrl } from '../../lib/utils/url_utility';
+import Poll from '../../lib/utils/poll';
+import eventHub from '../event_hub';
+import Service from '../services/index';
+import Store from '../stores';
+import titleComponent from './title.vue';
+import descriptionComponent from './description.vue';
+import editedComponent from './edited.vue';
+import formComponent from './form.vue';
+import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
- export default {
- components: {
- descriptionComponent,
- titleComponent,
- editedComponent,
- formComponent,
- },
- mixins: [
- recaptchaModalImplementor,
- ],
- props: {
- endpoint: {
- required: true,
- type: String,
- },
- updateEndpoint: {
- required: true,
- type: String,
- },
- canUpdate: {
- required: true,
- type: Boolean,
- },
- canDestroy: {
- required: true,
- type: Boolean,
- },
- showInlineEditButton: {
- type: Boolean,
- required: false,
- default: true,
- },
- showDeleteButton: {
- type: Boolean,
- required: false,
- default: true,
- },
- enableAutocomplete: {
- type: Boolean,
- required: false,
- default: true,
- },
- issuableRef: {
- type: String,
- required: true,
- },
- initialTitleHtml: {
- type: String,
- required: true,
- },
- initialTitleText: {
- type: String,
- required: true,
- },
- initialDescriptionHtml: {
- type: String,
- required: false,
- default: '',
- },
- initialDescriptionText: {
- type: String,
- required: false,
- default: '',
- },
- initialTaskStatus: {
- type: String,
- required: false,
- default: '',
- },
- updatedAt: {
- type: String,
- required: false,
- default: '',
- },
- updatedByName: {
- type: String,
- required: false,
- default: '',
- },
- updatedByPath: {
- type: String,
- required: false,
- default: '',
- },
- issuableTemplates: {
- type: Array,
- required: false,
- default: () => [],
- },
- markdownPreviewPath: {
- type: String,
- required: true,
- },
- markdownDocsPath: {
- type: String,
- required: true,
- },
- markdownVersion: {
- type: Number,
- required: false,
- default: 0,
- },
- projectPath: {
- type: String,
- required: true,
- },
- projectNamespace: {
- type: String,
- required: true,
- },
- issuableType: {
- type: String,
- required: false,
- default: 'issue',
- },
- canAttachFile: {
- type: Boolean,
- required: false,
- default: true,
- },
+export default {
+ components: {
+ descriptionComponent,
+ titleComponent,
+ editedComponent,
+ formComponent,
+ },
+ mixins: [recaptchaModalImplementor],
+ props: {
+ endpoint: {
+ required: true,
+ type: String,
},
- data() {
- const store = new Store({
- titleHtml: this.initialTitleHtml,
- titleText: this.initialTitleText,
- descriptionHtml: this.initialDescriptionHtml,
- descriptionText: this.initialDescriptionText,
- updatedAt: this.updatedAt,
- updatedByName: this.updatedByName,
- updatedByPath: this.updatedByPath,
- taskStatus: this.initialTaskStatus,
- });
+ updateEndpoint: {
+ required: true,
+ type: String,
+ },
+ canUpdate: {
+ required: true,
+ type: Boolean,
+ },
+ canDestroy: {
+ required: true,
+ type: Boolean,
+ },
+ showInlineEditButton: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ showDeleteButton: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ enableAutocomplete: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ issuableRef: {
+ type: String,
+ required: true,
+ },
+ initialTitleHtml: {
+ type: String,
+ required: true,
+ },
+ initialTitleText: {
+ type: String,
+ required: true,
+ },
+ initialDescriptionHtml: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ initialDescriptionText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ initialTaskStatus: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ updatedAt: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ updatedByName: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ updatedByPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ issuableTemplates: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ markdownPreviewPath: {
+ type: String,
+ required: true,
+ },
+ markdownDocsPath: {
+ type: String,
+ required: true,
+ },
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ projectNamespace: {
+ type: String,
+ required: true,
+ },
+ issuableType: {
+ type: String,
+ required: false,
+ default: 'issue',
+ },
+ canAttachFile: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+ data() {
+ const store = new Store({
+ titleHtml: this.initialTitleHtml,
+ titleText: this.initialTitleText,
+ descriptionHtml: this.initialDescriptionHtml,
+ descriptionText: this.initialDescriptionText,
+ updatedAt: this.updatedAt,
+ updatedByName: this.updatedByName,
+ updatedByPath: this.updatedByPath,
+ taskStatus: this.initialTaskStatus,
+ });
- return {
- store,
- state: store.state,
- showForm: false,
- };
- },
- computed: {
- formState() {
- return this.store.formState;
- },
- hasUpdated() {
- return !!this.state.updatedAt;
- },
- issueChanged() {
- const descriptionChanged =
- this.initialDescriptionText !== this.store.formState.description;
- const titleChanged =
- this.initialTitleText !== this.store.formState.title;
- return descriptionChanged || titleChanged;
- },
+ return {
+ store,
+ state: store.state,
+ showForm: false,
+ };
+ },
+ computed: {
+ formState() {
+ return this.store.formState;
},
- created() {
- this.service = new Service(this.endpoint);
- this.poll = new Poll({
- resource: this.service,
- method: 'getData',
- successCallback: res => this.store.updateState(res.data),
- errorCallback(err) {
- throw new Error(err);
- },
- });
+ hasUpdated() {
+ return !!this.state.updatedAt;
+ },
+ issueChanged() {
+ const descriptionChanged = this.initialDescriptionText !== this.store.formState.description;
+ const titleChanged = this.initialTitleText !== this.store.formState.title;
+ return descriptionChanged || titleChanged;
+ },
+ },
+ created() {
+ this.service = new Service(this.endpoint);
+ this.poll = new Poll({
+ resource: this.service,
+ method: 'getData',
+ successCallback: res => this.store.updateState(res.data),
+ errorCallback(err) {
+ throw new Error(err);
+ },
+ });
+ if (!Visibility.hidden()) {
+ this.poll.makeRequest();
+ }
+
+ Visibility.change(() => {
if (!Visibility.hidden()) {
- this.poll.makeRequest();
+ this.poll.restart();
+ } else {
+ this.poll.stop();
}
+ });
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- this.poll.restart();
- } else {
- this.poll.stop();
- }
- });
+ window.addEventListener('beforeunload', this.handleBeforeUnloadEvent);
- window.addEventListener('beforeunload', this.handleBeforeUnloadEvent);
-
- eventHub.$on('delete.issuable', this.deleteIssuable);
- eventHub.$on('update.issuable', this.updateIssuable);
- eventHub.$on('close.form', this.closeForm);
- eventHub.$on('open.form', this.openForm);
- },
- beforeDestroy() {
- eventHub.$off('delete.issuable', this.deleteIssuable);
- eventHub.$off('update.issuable', this.updateIssuable);
- eventHub.$off('close.form', this.closeForm);
- eventHub.$off('open.form', this.openForm);
- window.removeEventListener('beforeunload', this.handleBeforeUnloadEvent);
- },
- methods: {
- handleBeforeUnloadEvent(e) {
- const event = e;
- if (this.showForm && this.issueChanged) {
- event.returnValue = 'Are you sure you want to lose your issue information?';
- }
- return undefined;
- },
+ eventHub.$on('delete.issuable', this.deleteIssuable);
+ eventHub.$on('update.issuable', this.updateIssuable);
+ eventHub.$on('close.form', this.closeForm);
+ eventHub.$on('open.form', this.openForm);
+ },
+ beforeDestroy() {
+ eventHub.$off('delete.issuable', this.deleteIssuable);
+ eventHub.$off('update.issuable', this.updateIssuable);
+ eventHub.$off('close.form', this.closeForm);
+ eventHub.$off('open.form', this.openForm);
+ window.removeEventListener('beforeunload', this.handleBeforeUnloadEvent);
+ },
+ methods: {
+ handleBeforeUnloadEvent(e) {
+ const event = e;
+ if (this.showForm && this.issueChanged) {
+ event.returnValue = 'Are you sure you want to lose your issue information?';
+ }
+ return undefined;
+ },
- openForm() {
- if (!this.showForm) {
- this.showForm = true;
- this.store.setFormState({
- title: this.state.titleText,
- description: this.state.descriptionText,
- lockedWarningVisible: false,
- updateLoading: false,
- });
- }
- },
- closeForm() {
- this.showForm = false;
- },
+ openForm() {
+ if (!this.showForm) {
+ this.showForm = true;
+ this.store.setFormState({
+ title: this.state.titleText,
+ description: this.state.descriptionText,
+ lockedWarningVisible: false,
+ updateLoading: false,
+ });
+ }
+ },
+ closeForm() {
+ this.showForm = false;
+ },
- updateIssuable() {
- return this.service.updateIssuable(this.store.formState)
- .then(res => res.data)
- .then(data => this.checkForSpam(data))
- .then((data) => {
- if (window.location.pathname !== data.web_url) {
- visitUrl(data.web_url);
- }
+ updateIssuable() {
+ return this.service
+ .updateIssuable(this.store.formState)
+ .then(res => res.data)
+ .then(data => this.checkForSpam(data))
+ .then(data => {
+ if (window.location.pathname !== data.web_url) {
+ visitUrl(data.web_url);
+ }
- return this.service.getData();
- })
- .then(res => res.data)
- .then((data) => {
- this.store.updateState(data);
+ return this.service.getData();
+ })
+ .then(res => res.data)
+ .then(data => {
+ this.store.updateState(data);
+ eventHub.$emit('close.form');
+ })
+ .catch(error => {
+ if (error && error.name === 'SpamError') {
+ this.openRecaptcha();
+ } else {
eventHub.$emit('close.form');
- })
- .catch((error) => {
- if (error && error.name === 'SpamError') {
- this.openRecaptcha();
- } else {
- eventHub.$emit('close.form');
- window.Flash(`Error updating ${this.issuableType}`);
- }
- });
- },
-
- closeRecaptchaModal() {
- this.store.setFormState({
- updateLoading: false,
+ window.Flash(`Error updating ${this.issuableType}`);
+ }
});
+ },
- this.closeRecaptcha();
- },
+ closeRecaptchaModal() {
+ this.store.setFormState({
+ updateLoading: false,
+ });
- deleteIssuable() {
- this.service.deleteIssuable()
- .then(res => res.data)
- .then((data) => {
- // Stop the poll so we don't get 404's with the issuable not existing
- this.poll.stop();
+ this.closeRecaptcha();
+ },
- visitUrl(data.web_url);
- })
- .catch(() => {
- eventHub.$emit('close.form');
- window.Flash(`Error deleting ${this.issuableType}`);
- });
- },
+ deleteIssuable() {
+ this.service
+ .deleteIssuable()
+ .then(res => res.data)
+ .then(data => {
+ // Stop the poll so we don't get 404's with the issuable not existing
+ this.poll.stop();
+
+ visitUrl(data.web_url);
+ })
+ .catch(() => {
+ eventHub.$emit('close.form');
+ window.Flash(`Error deleting ${this.issuableType}`);
+ });
},
- };
+ },
+};
</script>
<template>
@@ -293,6 +291,7 @@
:show-delete-button="showDeleteButton"
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
+ :issuable-type="issuableType"
/>
<recaptcha-modal
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index 1174177f561..461cb3271b7 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -1,110 +1,105 @@
<script>
- import $ from 'jquery';
- import animateMixin from '../mixins/animate';
- import TaskList from '../../task_list';
- import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
+import $ from 'jquery';
+import animateMixin from '../mixins/animate';
+import TaskList from '../../task_list';
+import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
- export default {
- mixins: [
- animateMixin,
- recaptchaModalImplementor,
- ],
+export default {
+ mixins: [animateMixin, recaptchaModalImplementor],
- props: {
- canUpdate: {
- type: Boolean,
- required: true,
- },
- descriptionHtml: {
- type: String,
- required: true,
- },
- descriptionText: {
- type: String,
- required: true,
- },
- taskStatus: {
- type: String,
- required: false,
- default: '',
- },
- issuableType: {
- type: String,
- required: false,
- default: 'issue',
- },
- updateUrl: {
- type: String,
- required: false,
- default: null,
- },
+ props: {
+ canUpdate: {
+ type: Boolean,
+ required: true,
},
- data() {
- return {
- preAnimation: false,
- pulseAnimation: false,
- };
+ descriptionHtml: {
+ type: String,
+ required: true,
},
- watch: {
- descriptionHtml() {
- this.animateChange();
+ descriptionText: {
+ type: String,
+ required: true,
+ },
+ taskStatus: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ issuableType: {
+ type: String,
+ required: false,
+ default: 'issue',
+ },
+ updateUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ preAnimation: false,
+ pulseAnimation: false,
+ };
+ },
+ watch: {
+ descriptionHtml() {
+ this.animateChange();
- this.$nextTick(() => {
- this.renderGFM();
- });
- },
- taskStatus() {
- this.updateTaskStatusText();
- },
+ this.$nextTick(() => {
+ this.renderGFM();
+ });
},
- mounted() {
- this.renderGFM();
+ taskStatus() {
this.updateTaskStatusText();
},
- methods: {
- renderGFM() {
- $(this.$refs['gfm-content']).renderGFM();
+ },
+ mounted() {
+ this.renderGFM();
+ this.updateTaskStatusText();
+ },
+ methods: {
+ renderGFM() {
+ $(this.$refs['gfm-content']).renderGFM();
- if (this.canUpdate) {
- // eslint-disable-next-line no-new
- new TaskList({
- dataType: this.issuableType,
- fieldName: 'description',
- selector: '.detail-page-description',
- onSuccess: this.taskListUpdateSuccess.bind(this),
- });
- }
- },
+ if (this.canUpdate) {
+ // eslint-disable-next-line no-new
+ new TaskList({
+ dataType: this.issuableType,
+ fieldName: 'description',
+ selector: '.detail-page-description',
+ onSuccess: this.taskListUpdateSuccess.bind(this),
+ });
+ }
+ },
- taskListUpdateSuccess(data) {
- try {
- this.checkForSpam(data);
- this.closeRecaptcha();
- } catch (error) {
- if (error && error.name === 'SpamError') this.openRecaptcha();
- }
- },
+ taskListUpdateSuccess(data) {
+ try {
+ this.checkForSpam(data);
+ this.closeRecaptcha();
+ } catch (error) {
+ if (error && error.name === 'SpamError') this.openRecaptcha();
+ }
+ },
- updateTaskStatusText() {
- const taskRegexMatches = this.taskStatus.match(/(\d+) of ((?!0)\d+)/);
- const $issuableHeader = $('.issuable-meta');
- const $tasks = $('#task_status', $issuableHeader);
- const $tasksShort = $('#task_status_short', $issuableHeader);
+ updateTaskStatusText() {
+ const taskRegexMatches = this.taskStatus.match(/(\d+) of ((?!0)\d+)/);
+ const $issuableHeader = $('.issuable-meta');
+ const $tasks = $('#task_status', $issuableHeader);
+ const $tasksShort = $('#task_status_short', $issuableHeader);
- if (taskRegexMatches) {
- $tasks.text(this.taskStatus);
- $tasksShort.text(
- `${taskRegexMatches[1]}/${taskRegexMatches[2]} task${taskRegexMatches[2] > 1 ?
- 's' :
- ''}`,
- );
- } else {
- $tasks.text('');
- $tasksShort.text('');
- }
- },
+ if (taskRegexMatches) {
+ $tasks.text(this.taskStatus);
+ $tasksShort.text(
+ `${taskRegexMatches[1]}/${taskRegexMatches[2]} task${taskRegexMatches[2] > 1 ? 's' : ''}`,
+ );
+ } else {
+ $tasks.text('');
+ $tasksShort.text('');
+ }
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue
index 597c6d69a81..5dda35d64bb 100644
--- a/app/assets/javascripts/issue_show/components/edit_actions.vue
+++ b/app/assets/javascripts/issue_show/components/edit_actions.vue
@@ -1,51 +1,64 @@
<script>
- import updateMixin from '../mixins/update';
- import eventHub from '../event_hub';
+import { __, sprintf } from '~/locale';
+import updateMixin from '../mixins/update';
+import eventHub from '../event_hub';
- export default {
- mixins: [updateMixin],
- props: {
- canDestroy: {
- type: Boolean,
- required: true,
- },
- formState: {
- type: Object,
- required: true,
- },
- showDeleteButton: {
- type: Boolean,
- required: false,
- default: true,
- },
+const issuableTypes = {
+ issue: __('Issue'),
+ epic: __('Epic'),
+};
+
+export default {
+ mixins: [updateMixin],
+ props: {
+ canDestroy: {
+ type: Boolean,
+ required: true,
+ },
+ formState: {
+ type: Object,
+ required: true,
+ },
+ showDeleteButton: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ issuableType: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ deleteLoading: false,
+ };
+ },
+ computed: {
+ isSubmitEnabled() {
+ return this.formState.title.trim() !== '';
},
- data() {
- return {
- deleteLoading: false,
- };
+ shouldShowDeleteButton() {
+ return this.canDestroy && this.showDeleteButton;
},
- computed: {
- isSubmitEnabled() {
- return this.formState.title.trim() !== '';
- },
- shouldShowDeleteButton() {
- return this.canDestroy && this.showDeleteButton;
- },
+ },
+ methods: {
+ closeForm() {
+ eventHub.$emit('close.form');
},
- methods: {
- closeForm() {
- eventHub.$emit('close.form');
- },
- deleteIssuable() {
- // eslint-disable-next-line no-alert
- if (window.confirm('Issue will be removed! Are you sure?')) {
- this.deleteLoading = true;
+ deleteIssuable() {
+ const confirmMessage = sprintf(__('%{issuableType} will be removed! Are you sure?'), {
+ issuableType: issuableTypes[this.issuableType],
+ });
+ // eslint-disable-next-line no-alert
+ if (window.confirm(confirmMessage)) {
+ this.deleteLoading = true;
- eventHub.$emit('delete.issuable');
- }
- },
+ eventHub.$emit('delete.issuable');
+ }
},
- };
+ },
+};
</script>
<template>
@@ -53,7 +66,7 @@
<button
:class="{ disabled: formState.updateLoading || !isSubmitEnabled }"
:disabled="formState.updateLoading || !isSubmitEnabled"
- class="btn btn-save float-left"
+ class="btn btn-success float-left qa-save-button"
type="submit"
@click.prevent="updateIssuable">
Save changes
@@ -73,7 +86,7 @@
v-if="shouldShowDeleteButton"
:class="{ disabled: deleteLoading }"
:disabled="deleteLoading"
- class="btn btn-danger float-right append-right-default"
+ class="btn btn-danger float-right append-right-default qa-delete-button"
type="button"
@click="deleteIssuable">
Delete
diff --git a/app/assets/javascripts/issue_show/components/edited.vue b/app/assets/javascripts/issue_show/components/edited.vue
index 05cd976f196..73ecb26c28d 100644
--- a/app/assets/javascripts/issue_show/components/edited.vue
+++ b/app/assets/javascripts/issue_show/components/edited.vue
@@ -1,33 +1,33 @@
<script>
- import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
+import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
- export default {
- components: {
- timeAgoTooltip,
+export default {
+ components: {
+ timeAgoTooltip,
+ },
+ props: {
+ updatedAt: {
+ type: String,
+ required: false,
+ default: '',
},
- props: {
- updatedAt: {
- type: String,
- required: false,
- default: '',
- },
- updatedByName: {
- type: String,
- required: false,
- default: '',
- },
- updatedByPath: {
- type: String,
- required: false,
- default: '',
- },
+ updatedByName: {
+ type: String,
+ required: false,
+ default: '',
},
- computed: {
- hasUpdatedBy() {
- return this.updatedByName && this.updatedByPath;
- },
+ updatedByPath: {
+ type: String,
+ required: false,
+ default: '',
},
- };
+ },
+ computed: {
+ hasUpdatedBy() {
+ return this.updatedByName && this.updatedByPath;
+ },
+ },
+};
</script>
<template>
@@ -53,4 +53,3 @@
</span>
</small>
</template>
-
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index 97acc5ba385..e9e96a985a7 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -1,45 +1,45 @@
<script>
- import updateMixin from '../../mixins/update';
- import markdownField from '../../../vue_shared/components/markdown/field.vue';
+import updateMixin from '../../mixins/update';
+import markdownField from '../../../vue_shared/components/markdown/field.vue';
- export default {
- components: {
- markdownField,
+export default {
+ components: {
+ markdownField,
+ },
+ mixins: [updateMixin],
+ props: {
+ formState: {
+ type: Object,
+ required: true,
},
- mixins: [updateMixin],
- props: {
- formState: {
- type: Object,
- required: true,
- },
- markdownPreviewPath: {
- type: String,
- required: true,
- },
- markdownDocsPath: {
- type: String,
- required: true,
- },
- markdownVersion: {
- type: Number,
- required: false,
- default: 0,
- },
- canAttachFile: {
- type: Boolean,
- required: false,
- default: true,
- },
- enableAutocomplete: {
- type: Boolean,
- required: false,
- default: true,
- },
+ markdownPreviewPath: {
+ type: String,
+ required: true,
},
- mounted() {
- this.$refs.textarea.focus();
+ markdownDocsPath: {
+ type: String,
+ required: true,
},
- };
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ canAttachFile: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ enableAutocomplete: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+ mounted() {
+ this.$refs.textarea.focus();
+ },
+};
</script>
<template>
@@ -61,7 +61,8 @@
ref="textarea"
slot="textarea"
v-model="formState.description"
- class="note-textarea js-gfm-input js-autosize markdown-area"
+ class="note-textarea js-gfm-input js-autosize markdown-area
+ qa-description-textarea"
data-supports-quick-actions="false"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
diff --git a/app/assets/javascripts/issue_show/components/fields/description_template.vue b/app/assets/javascripts/issue_show/components/fields/description_template.vue
index e90d9fad94e..e433bf66cfc 100644
--- a/app/assets/javascripts/issue_show/components/fields/description_template.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description_template.vue
@@ -1,46 +1,46 @@
<script>
- import $ from 'jquery';
- import IssuableTemplateSelectors from '../../../templates/issuable_template_selectors';
+import $ from 'jquery';
+import IssuableTemplateSelectors from '../../../templates/issuable_template_selectors';
- export default {
- props: {
- formState: {
- type: Object,
- required: true,
- },
- issuableTemplates: {
- type: Array,
- required: false,
- default: () => [],
- },
- projectPath: {
- type: String,
- required: true,
- },
- projectNamespace: {
- type: String,
- required: true,
- },
+export default {
+ props: {
+ formState: {
+ type: Object,
+ required: true,
},
- computed: {
- issuableTemplatesJson() {
- return JSON.stringify(this.issuableTemplates);
- },
+ issuableTemplates: {
+ type: Array,
+ required: false,
+ default: () => [],
},
- mounted() {
- // Create the editor for the template
- const editor = document.querySelector('.detail-page-description .note-textarea') || {};
- editor.setValue = (val) => {
- this.formState.description = val;
- };
- editor.getValue = () => this.formState.description;
-
- this.issuableTemplate = new IssuableTemplateSelectors({
- $dropdowns: $(this.$refs.toggle),
- editor,
- });
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ projectNamespace: {
+ type: String,
+ required: true,
},
- };
+ },
+ computed: {
+ issuableTemplatesJson() {
+ return JSON.stringify(this.issuableTemplates);
+ },
+ },
+ mounted() {
+ // Create the editor for the template
+ const editor = document.querySelector('.detail-page-description .note-textarea') || {};
+ editor.setValue = val => {
+ this.formState.description = val;
+ };
+ editor.getValue = () => this.formState.description;
+
+ this.issuableTemplate = new IssuableTemplateSelectors({
+ $dropdowns: $(this.$refs.toggle),
+ editor,
+ });
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue
index 7d1526a64b4..11f4153b8d5 100644
--- a/app/assets/javascripts/issue_show/components/fields/title.vue
+++ b/app/assets/javascripts/issue_show/components/fields/title.vue
@@ -1,15 +1,15 @@
<script>
- import updateMixin from '../../mixins/update';
+import updateMixin from '../../mixins/update';
- export default {
- mixins: [updateMixin],
- props: {
- formState: {
- type: Object,
- required: true,
- },
+export default {
+ mixins: [updateMixin],
+ props: {
+ formState: {
+ type: Object,
+ required: true,
},
- };
+ },
+};
</script>
<template>
@@ -22,7 +22,7 @@
<input
id="issuable-title"
v-model="formState.title"
- class="form-control"
+ class="form-control qa-title-input"
type="text"
placeholder="Title"
aria-label="Title"
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index e509bb52f7d..3b430d92912 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -1,75 +1,79 @@
<script>
- import lockedWarning from './locked_warning.vue';
- import titleField from './fields/title.vue';
- import descriptionField from './fields/description.vue';
- import editActions from './edit_actions.vue';
- import descriptionTemplate from './fields/description_template.vue';
+import lockedWarning from './locked_warning.vue';
+import titleField from './fields/title.vue';
+import descriptionField from './fields/description.vue';
+import editActions from './edit_actions.vue';
+import descriptionTemplate from './fields/description_template.vue';
- export default {
- components: {
- lockedWarning,
- titleField,
- descriptionField,
- descriptionTemplate,
- editActions,
+export default {
+ components: {
+ lockedWarning,
+ titleField,
+ descriptionField,
+ descriptionTemplate,
+ editActions,
+ },
+ props: {
+ canDestroy: {
+ type: Boolean,
+ required: true,
},
- props: {
- canDestroy: {
- type: Boolean,
- required: true,
- },
- formState: {
- type: Object,
- required: true,
- },
- issuableTemplates: {
- type: Array,
- required: false,
- default: () => [],
- },
- markdownPreviewPath: {
- type: String,
- required: true,
- },
- markdownDocsPath: {
- type: String,
- required: true,
- },
- markdownVersion: {
- type: Number,
- required: false,
- default: 0,
- },
- projectPath: {
- type: String,
- required: true,
- },
- projectNamespace: {
- type: String,
- required: true,
- },
- showDeleteButton: {
- type: Boolean,
- required: false,
- default: true,
- },
- canAttachFile: {
- type: Boolean,
- required: false,
- default: true,
- },
- enableAutocomplete: {
- type: Boolean,
- required: false,
- default: true,
- },
+ formState: {
+ type: Object,
+ required: true,
},
- computed: {
- hasIssuableTemplates() {
- return this.issuableTemplates.length;
- },
+ issuableTemplates: {
+ type: Array,
+ required: false,
+ default: () => [],
},
- };
+ issuableType: {
+ type: String,
+ required: true,
+ },
+ markdownPreviewPath: {
+ type: String,
+ required: true,
+ },
+ markdownDocsPath: {
+ type: String,
+ required: true,
+ },
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ projectNamespace: {
+ type: String,
+ required: true,
+ },
+ showDeleteButton: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ canAttachFile: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ enableAutocomplete: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+ computed: {
+ hasIssuableTemplates() {
+ return this.issuableTemplates.length;
+ },
+ },
+};
</script>
<template>
@@ -110,6 +114,7 @@
:form-state="formState"
:can-destroy="canDestroy"
:show-delete-button="showDeleteButton"
+ :issuable-type="issuableType"
/>
</form>
</template>
diff --git a/app/assets/javascripts/issue_show/components/locked_warning.vue b/app/assets/javascripts/issue_show/components/locked_warning.vue
index ad0d40faf32..0682c6f2a35 100644
--- a/app/assets/javascripts/issue_show/components/locked_warning.vue
+++ b/app/assets/javascripts/issue_show/components/locked_warning.vue
@@ -1,11 +1,11 @@
<script>
- export default {
- computed: {
- currentPath() {
- return window.location.pathname;
- },
+export default {
+ computed: {
+ currentPath() {
+ return window.location.pathname;
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index b5e8e0ea44b..ed26e53ac0e 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -76,10 +76,11 @@ export default {
>
</h2>
<button
- v-tooltip
v-if="showInlineEditButton && canUpdate"
+ v-tooltip
type="button"
- class="btn btn-default btn-edit btn-svg js-issuable-edit"
+ class="btn btn-default btn-edit btn-svg js-issuable-edit
+ qa-edit-button"
title="Edit title and description"
data-placement="bottom"
data-container="body"
diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js
index 75dfdedcf1b..d08e8ba0c4b 100644
--- a/app/assets/javascripts/issue_show/index.js
+++ b/app/assets/javascripts/issue_show/index.js
@@ -1,10 +1,11 @@
import Vue from 'vue';
+import sanitize from 'sanitize-html';
import issuableApp from './components/app.vue';
import '../vue_shared/vue_resource_interceptor';
-document.addEventListener('DOMContentLoaded', () => {
+export default function initIssueableApp() {
const initialDataEl = document.getElementById('js-issuable-app-initial-data');
- const props = JSON.parse(initialDataEl.innerHTML.replace(/&quot;/g, '"'));
+ const props = JSON.parse(sanitize(initialDataEl.textContent).replace(/&quot;/g, '"'));
return new Vue({
el: document.getElementById('js-issuable-app'),
@@ -17,4 +18,4 @@ document.addEventListener('DOMContentLoaded', () => {
});
},
});
-});
+}
diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js
index af8b0414266..32044d6da25 100644
--- a/app/assets/javascripts/issue_show/stores/index.js
+++ b/app/assets/javascripts/issue_show/stores/index.js
@@ -25,8 +25,10 @@ export default class Store {
}
stateShouldUpdate(data) {
- return this.state.titleText !== data.title_text ||
- this.state.descriptionText !== data.description_text;
+ return (
+ this.state.titleText !== data.title_text ||
+ this.state.descriptionText !== data.description_text
+ );
}
setFormState(state) {
diff --git a/app/assets/javascripts/job.js b/app/assets/javascripts/job.js
deleted file mode 100644
index d4f2a3ef7d3..00000000000
--- a/app/assets/javascripts/job.js
+++ /dev/null
@@ -1,213 +0,0 @@
-import $ from 'jquery';
-import _ from 'underscore';
-import { polyfillSticky } from './lib/utils/sticky';
-import axios from './lib/utils/axios_utils';
-import { visitUrl } from './lib/utils/url_utility';
-import bp from './breakpoints';
-import { numberToHumanSize } from './lib/utils/number_utils';
-import { setCiStatusFavicon } from './lib/utils/common_utils';
-import { isScrolledToBottom, scrollDown } from './lib/utils/scroll_utils';
-import LogOutputBehaviours from './lib/utils/logoutput_behaviours';
-
-export default class Job extends LogOutputBehaviours {
- constructor(options) {
- super();
- this.timeout = null;
- this.state = null;
- this.fetchingStatusFavicon = false;
- this.options = options || $('.js-build-options').data();
-
- this.pagePath = this.options.pagePath;
- this.buildStatus = this.options.buildStatus;
- this.state = this.options.logState;
- this.buildStage = this.options.buildStage;
- this.$document = $(document);
- this.$window = $(window);
- this.logBytes = 0;
- this.updateDropdown = this.updateDropdown.bind(this);
-
- this.$buildTrace = $('#build-trace');
- this.$buildRefreshAnimation = $('.js-build-refresh');
- this.$truncatedInfo = $('.js-truncated-info');
- this.$buildTraceOutput = $('.js-build-output');
- this.$topBar = $('.js-top-bar');
-
- clearTimeout(this.timeout);
-
- this.initSidebar();
- this.populateJobs(this.buildStage);
- this.updateStageDropdownText(this.buildStage);
- this.sidebarOnResize();
-
- this.$document
- .off('click', '.js-sidebar-build-toggle')
- .on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this));
-
- this.$document
- .off('click', '.stage-item')
- .on('click', '.stage-item', this.updateDropdown);
-
- this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
-
- this.$window
- .off('scroll')
- .on('scroll', () => {
- if (!isScrolledToBottom()) {
- this.toggleScrollAnimation(false);
- } else if (isScrolledToBottom() && !this.isLogComplete) {
- this.toggleScrollAnimation(true);
- }
- this.scrollThrottled();
- });
-
- this.$window
- .off('resize.build')
- .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
-
- this.initAffixTopArea();
-
- this.getBuildTrace();
- }
-
- initAffixTopArea() {
- polyfillSticky(this.$topBar);
- }
-
- scrollToBottom() {
- scrollDown();
- this.hasBeenScrolled = true;
- this.toggleScroll();
- }
-
- scrollToTop() {
- $(document).scrollTop(0);
- this.hasBeenScrolled = true;
- this.toggleScroll();
- }
-
- toggleScrollAnimation(toggle) {
- this.$scrollBottomBtn.toggleClass('animate', toggle);
- }
-
- initSidebar() {
- this.$sidebar = $('.js-build-sidebar');
- }
-
- getBuildTrace() {
- return axios.get(`${this.pagePath}/trace.json`, {
- params: { state: this.state },
- })
- .then((res) => {
- const log = res.data;
-
- if (!this.fetchingStatusFavicon) {
- this.fetchingStatusFavicon = true;
-
- setCiStatusFavicon(`${this.pagePath}/status.json`)
- .then(() => {
- this.fetchingStatusFavicon = false;
- })
- .catch(() => {
- this.fetchingStatusFavicon = false;
- });
- }
-
- if (log.state) {
- this.state = log.state;
- }
-
- this.isScrollInBottom = isScrolledToBottom();
-
- if (log.append) {
- this.$buildTraceOutput.append(log.html);
- this.logBytes += log.size;
- } else {
- this.$buildTraceOutput.html(log.html);
- this.logBytes = log.size;
- }
-
- // if the incremental sum of logBytes we received is less than the total
- // we need to show a message warning the user about that.
- if (this.logBytes < log.total) {
- // size is in bytes, we need to calculate KiB
- const size = numberToHumanSize(this.logBytes);
- $('.js-truncated-info-size').html(`${size}`);
- this.$truncatedInfo.removeClass('hidden');
- } else {
- this.$truncatedInfo.addClass('hidden');
- }
- this.isLogComplete = log.complete;
-
- if (log.complete === false) {
- this.timeout = setTimeout(() => {
- this.getBuildTrace();
- }, 4000);
- } else {
- this.$buildRefreshAnimation.remove();
- this.toggleScrollAnimation(false);
- }
-
- if (log.status !== this.buildStatus) {
- visitUrl(this.pagePath);
- }
- })
- .catch(() => {
- this.$buildRefreshAnimation.remove();
- })
- .then(() => {
- if (this.isScrollInBottom) {
- scrollDown();
- }
- })
- .then(() => this.toggleScroll());
- }
- // eslint-disable-next-line class-methods-use-this
- shouldHideSidebarForViewport() {
- const bootstrapBreakpoint = bp.getBreakpointSize();
- return bootstrapBreakpoint === 'xs';
- }
-
- toggleSidebar(shouldHide) {
- const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
- const $toggleButton = $('.js-sidebar-build-toggle-header');
-
- this.$sidebar
- .toggleClass('right-sidebar-expanded', shouldShow)
- .toggleClass('right-sidebar-collapsed', shouldHide);
-
- this.$topBar
- .toggleClass('sidebar-expanded', shouldShow)
- .toggleClass('sidebar-collapsed', shouldHide);
-
- if (this.$sidebar.hasClass('right-sidebar-expanded')) {
- $toggleButton.addClass('hidden');
- } else {
- $toggleButton.removeClass('hidden');
- }
- }
-
- sidebarOnResize() {
- this.toggleSidebar(this.shouldHideSidebarForViewport());
- }
-
- sidebarOnClick() {
- if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
- }
-
- // eslint-disable-next-line class-methods-use-this
- populateJobs(stage) {
- $('.build-job').hide();
- $(`.build-job[data-stage="${stage}"]`).show();
- }
- // eslint-disable-next-line class-methods-use-this
- updateStageDropdownText(stage) {
- $('.stage-selection').text(stage);
- }
-
- updateDropdown(e) {
- e.preventDefault();
- const stage = e.currentTarget.text;
- this.updateStageDropdownText(stage);
- this.populateJobs(stage);
- }
-}
diff --git a/app/assets/javascripts/jobs/components/artifacts_block.vue b/app/assets/javascripts/jobs/components/artifacts_block.vue
new file mode 100644
index 00000000000..932675c0fc6
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/artifacts_block.vue
@@ -0,0 +1,86 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+
+export default {
+ components: {
+ TimeagoTooltip,
+ GlLink,
+ },
+ mixins: [timeagoMixin],
+ props: {
+ artifact: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ isExpired() {
+ return this.artifact.expired;
+ },
+ // Only when the key is `false` we can render this block
+ willExpire() {
+ return this.artifact.expired === false;
+ },
+ },
+};
+</script>
+<template>
+ <div class="block">
+ <div class="title">
+ {{ s__('Job|Job artifacts') }}
+ </div>
+
+ <p
+ v-if="isExpired"
+ class="js-artifacts-removed build-detail-row"
+ >
+ {{ s__('Job|The artifacts were removed') }}
+ </p>
+
+ <p
+ v-else-if="willExpire"
+ class="js-artifacts-will-be-removed build-detail-row"
+ >
+ {{ s__('Job|The artifacts will be removed in') }}
+ </p>
+
+ <timeago-tooltip
+ v-if="artifact.expire_at"
+ :time="artifact.expire_at"
+ />
+
+ <div
+ class="btn-group d-flex"
+ role="group"
+ >
+ <gl-link
+ v-if="artifact.keep_path"
+ :href="artifact.keep_path"
+ class="js-keep-artifacts btn btn-sm btn-default"
+ data-method="post"
+ >
+ {{ s__('Job|Keep') }}
+ </gl-link>
+
+ <gl-link
+ v-if="artifact.download_path"
+ :href="artifact.download_path"
+ class="js-download-artifacts btn btn-sm btn-default"
+ download
+ rel="nofollow"
+ >
+ {{ s__('Job|Download') }}
+ </gl-link>
+
+ <gl-link
+ v-if="artifact.browse_path"
+ :href="artifact.browse_path"
+ class="js-browse-artifacts btn btn-sm btn-default"
+ >
+ {{ s__('Job|Browse') }}
+ </gl-link>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/commit_block.vue
new file mode 100644
index 00000000000..9373dbebc8a
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/commit_block.vue
@@ -0,0 +1,58 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+
+export default {
+ components: {
+ ClipboardButton,
+ GlLink,
+ },
+ props: {
+ commit: {
+ type: Object,
+ required: true,
+ },
+ mergeRequest: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ isLastBlock: {
+ type: Boolean,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div
+ :class="{
+ 'block-last': isLastBlock,
+ block: !isLastBlock
+ }">
+ <p>
+ {{ __('Commit') }}
+
+ <gl-link
+ :href="commit.commit_path"
+ class="js-commit-sha commit-sha link-commit"
+ >{{ commit.short_id }}</gl-link>
+
+ <clipboard-button
+ :text="commit.short_id"
+ :title="__('Copy commit SHA to clipboard')"
+ css-class="btn btn-clipboard btn-transparent"
+ />
+
+ <gl-link
+ v-if="mergeRequest"
+ :href="mergeRequest.path"
+ class="js-link-commit link-commit"
+ >!{{ mergeRequest.iid }}</gl-link>
+ </p>
+
+ <p class="build-light-text append-bottom-0">
+ {{ commit.title }}
+ </p>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/empty_state.vue b/app/assets/javascripts/jobs/components/empty_state.vue
new file mode 100644
index 00000000000..afc4d931a68
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/empty_state.vue
@@ -0,0 +1,81 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlLink,
+ },
+ props: {
+ illustrationPath: {
+ type: String,
+ required: true,
+ },
+ illustrationSizeClass: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ content: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ action: {
+ type: Object,
+ required: false,
+ default: null,
+ validator(value) {
+ return (
+ value === null ||
+ (Object.prototype.hasOwnProperty.call(value, 'path') &&
+ Object.prototype.hasOwnProperty.call(value, 'method') &&
+ Object.prototype.hasOwnProperty.call(value, 'button_title'))
+ );
+ },
+ },
+ },
+};
+</script>
+<template>
+ <div class="row empty-state">
+ <div class="col-12">
+ <div
+ :class="illustrationSizeClass"
+ class="svg-content"
+ >
+ <img :src="illustrationPath" />
+ </div>
+ </div>
+
+ <div class="col-12">
+ <div class="text-content">
+ <h4 class="js-job-empty-state-title text-center">
+ {{ title }}
+ </h4>
+
+ <p
+ v-if="content"
+ class="js-job-empty-state-content"
+ >
+ {{ content }}
+ </p>
+
+ <div
+ v-if="action"
+ class="text-center"
+ >
+ <gl-link
+ :href="action.path"
+ :data-method="action.method"
+ class="js-job-empty-state-action btn btn-primary"
+ >
+ {{ action.button_title }}
+ </gl-link>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/environments_block.vue b/app/assets/javascripts/jobs/components/environments_block.vue
new file mode 100644
index 00000000000..6d1eb713886
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/environments_block.vue
@@ -0,0 +1,140 @@
+<script>
+import _ from 'underscore';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import { sprintf, __ } from '../../locale';
+
+export default {
+ components: {
+ CiIcon,
+ },
+ props: {
+ deploymentStatus: {
+ type: Object,
+ required: true,
+ },
+ iconStatus: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ environment() {
+ let environmentText;
+ switch (this.deploymentStatus.status) {
+ case 'last':
+ environmentText = sprintf(
+ __('This job is the most recent deployment to %{link}.'),
+ { link: this.environmentLink },
+ false,
+ );
+ break;
+ case 'out_of_date':
+ if (this.hasLastDeployment) {
+ environmentText = sprintf(
+ __(
+ 'This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}.',
+ ),
+ {
+ environmentLink: this.environmentLink,
+ deploymentLink: this.deploymentLink(`#${this.lastDeployment.iid}`),
+ },
+ false,
+ );
+ } else {
+ environmentText = sprintf(
+ __('This job is an out-of-date deployment to %{environmentLink}.'),
+ { environmentLink: this.environmentLink },
+ false,
+ );
+ }
+
+ break;
+ case 'failed':
+ environmentText = sprintf(
+ __('The deployment of this job to %{environmentLink} did not succeed.'),
+ { environmentLink: this.environmentLink },
+ false,
+ );
+ break;
+ case 'creating':
+ if (this.hasLastDeployment) {
+ environmentText = sprintf(
+ __(
+ 'This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}.',
+ ),
+ {
+ environmentLink: this.environmentLink,
+ deploymentLink: this.deploymentLink(__('latest deployment')),
+ },
+ false,
+ );
+ } else {
+ environmentText = sprintf(
+ __('This job is creating a deployment to %{environmentLink}.'),
+ { environmentLink: this.environmentLink },
+ false,
+ );
+ }
+ break;
+ default:
+ break;
+ }
+ return environmentText;
+ },
+ environmentLink() {
+ if (this.hasEnvironment) {
+ return sprintf(
+ '%{startLink}%{name}%{endLink}',
+ {
+ startLink: `<a href="${
+ this.deploymentStatus.environment.environment_path
+ }" class="js-environment-link">`,
+ name: _.escape(this.deploymentStatus.environment.name),
+ endLink: '</a>',
+ },
+ false,
+ );
+ }
+ return '';
+ },
+ hasLastDeployment() {
+ return this.hasEnvironment && this.deploymentStatus.environment.last_deployment;
+ },
+ lastDeployment() {
+ return this.hasLastDeployment ? this.deploymentStatus.environment.last_deployment : {};
+ },
+ hasEnvironment() {
+ return !_.isEmpty(this.deploymentStatus.environment);
+ },
+ lastDeploymentPath() {
+ return !_.isEmpty(this.lastDeployment.deployable)
+ ? this.lastDeployment.deployable.build_path
+ : '';
+ },
+ },
+ methods: {
+ deploymentLink(name) {
+ return sprintf(
+ '%{startLink}%{name}%{endLink}',
+ {
+ startLink: `<a href="${this.lastDeploymentPath}" class="js-job-deployment-link">`,
+ name,
+ endLink: '</a>',
+ },
+ false,
+ );
+ },
+ },
+};
+</script>
+<template>
+ <div class="prepend-top-default js-environment-container">
+ <div class="environment-information">
+ <ci-icon :status="iconStatus"/>
+ <p
+ class="inline append-bottom-0"
+ v-html="environment"
+ ></p>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/erased_block.vue b/app/assets/javascripts/jobs/components/erased_block.vue
new file mode 100644
index 00000000000..712f564b065
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/erased_block.vue
@@ -0,0 +1,47 @@
+<script>
+import _ from 'underscore';
+import { GlLink } from '@gitlab/ui';
+import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+export default {
+ components: {
+ TimeagoTooltip,
+ GlLink,
+ },
+ props: {
+ user: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ erasedAt: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ isErasedByUser() {
+ return !_.isEmpty(this.user);
+ },
+ },
+};
+</script>
+<template>
+ <div class="prepend-top-default js-build-erased">
+ <div class="erased alert alert-warning">
+ <template v-if="isErasedByUser">
+ {{ s__("Job|Job has been erased by") }}
+ <gl-link :href="user.web_url">
+ {{ user.username }}
+ </gl-link>
+ </template>
+ <template v-else>
+ {{ s__("Job|Job has been erased") }}
+ </template>
+
+ <timeago-tooltip
+ :time="erasedAt"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue
deleted file mode 100644
index 1e7f4b2c3f7..00000000000
--- a/app/assets/javascripts/jobs/components/header.vue
+++ /dev/null
@@ -1,97 +0,0 @@
-<script>
-import ciHeader from '../../vue_shared/components/header_ci_component.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-import callout from '../../vue_shared/components/callout.vue';
-
-export default {
- name: 'JobHeaderSection',
- components: {
- ciHeader,
- loadingIcon,
- callout,
- },
- props: {
- job: {
- type: Object,
- required: true,
- },
- isLoading: {
- type: Boolean,
- required: true,
- },
- },
- data() {
- return {
- actions: this.getActions(),
- };
- },
- computed: {
- status() {
- return this.job && this.job.status;
- },
- shouldRenderContent() {
- return !this.isLoading && Object.keys(this.job).length;
- },
- shouldRenderReason() {
- return !!(this.job.status && this.job.callout_message);
- },
- /**
- * When job has not started the key will be `false`
- * When job started the key will be a string with a date.
- */
- jobStarted() {
- return !this.job.started === false;
- },
- headerTime() {
- return this.jobStarted ? this.job.started : this.job.created_at;
- },
- },
- watch: {
- job() {
- this.actions = this.getActions();
- },
- },
- methods: {
- getActions() {
- const actions = [];
-
- if (this.job.new_issue_path) {
- actions.push({
- label: 'New issue',
- path: this.job.new_issue_path,
- cssClass: 'js-new-issue btn btn-new btn-inverted d-none d-md-block d-lg-block d-xl-block',
- type: 'link',
- });
- }
- return actions;
- },
- },
-};
-</script>
-<template>
- <header>
- <div class="js-build-header build-header top-area">
- <ci-header
- v-if="shouldRenderContent"
- :status="status"
- :item-id="job.id"
- :time="headerTime"
- :user="job.user"
- :actions="actions"
- :has-sidebar-button="true"
- :should-render-triggered-label="jobStarted"
- item-name="Job"
- />
- <loading-icon
- v-if="isLoading"
- size="2"
- class="prepend-top-default append-bottom-default"
- />
- </div>
-
- <callout
- v-if="shouldRenderReason"
- :message="job.callout_message"
- />
- </header>
-</template>
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
new file mode 100644
index 00000000000..ecb809ca4de
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -0,0 +1,309 @@
+<script>
+import _ from 'underscore';
+import { mapGetters, mapState, mapActions } from 'vuex';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
+import { polyfillSticky } from '~/lib/utils/sticky';
+import bp from '~/breakpoints';
+import CiHeader from '~/vue_shared/components/header_ci_component.vue';
+import Callout from '~/vue_shared/components/callout.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import createStore from '../store';
+import EmptyState from './empty_state.vue';
+import EnvironmentsBlock from './environments_block.vue';
+import ErasedBlock from './erased_block.vue';
+import Log from './job_log.vue';
+import LogTopBar from './job_log_controllers.vue';
+import StuckBlock from './stuck_block.vue';
+import Sidebar from './sidebar.vue';
+import { sprintf } from '~/locale';
+import delayedJobMixin from '../mixins/delayed_job_mixin';
+
+export default {
+ name: 'JobPageApp',
+ store: createStore(),
+ components: {
+ CiHeader,
+ Callout,
+ EmptyState,
+ EnvironmentsBlock,
+ ErasedBlock,
+ Icon,
+ Log,
+ LogTopBar,
+ StuckBlock,
+ Sidebar,
+ GlLoadingIcon,
+ },
+ mixins: [delayedJobMixin],
+ props: {
+ runnerSettingsUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ runnerHelpUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ endpoint: {
+ type: String,
+ required: true,
+ },
+ terminalPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ pagePath: {
+ type: String,
+ required: true,
+ },
+ logState: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState([
+ 'isLoading',
+ 'job',
+ 'isSidebarOpen',
+ 'trace',
+ 'isTraceComplete',
+ 'traceSize',
+ 'isTraceSizeVisible',
+ 'isScrollBottomDisabled',
+ 'isScrollTopDisabled',
+ 'isScrolledToBottomBeforeReceivingTrace',
+ 'hasError',
+ ]),
+ ...mapGetters([
+ 'headerActions',
+ 'headerTime',
+ 'shouldRenderCalloutMessage',
+ 'shouldRenderTriggeredLabel',
+ 'hasEnvironment',
+ 'hasTrace',
+ 'emptyStateIllustration',
+ 'isScrollingDown',
+ 'emptyStateAction',
+ 'hasRunnersForProject',
+ ]),
+
+ shouldRenderContent() {
+ return !this.isLoading && !this.hasError;
+ },
+
+ emptyStateTitle() {
+ const { emptyStateIllustration, remainingTime } = this;
+ const { title } = emptyStateIllustration;
+
+ if (this.isDelayedJob) {
+ return sprintf(title, { remainingTime });
+ }
+
+ return title;
+ },
+ },
+ watch: {
+ // Once the job log is loaded,
+ // fetch the stages for the dropdown on the sidebar
+ job(newVal, oldVal) {
+ if (_.isEmpty(oldVal) && !_.isEmpty(newVal.pipeline)) {
+ this.fetchStages();
+ }
+
+ if (newVal.archived) {
+ this.$nextTick(() => {
+ if (this.$refs.sticky) {
+ polyfillSticky(this.$refs.sticky);
+ }
+ });
+ }
+ },
+ },
+ created() {
+ this.throttled = _.throttle(this.toggleScrollButtons, 100);
+
+ this.setJobEndpoint(this.endpoint);
+ this.setTraceOptions({
+ logState: this.logState,
+ pagePath: this.pagePath,
+ });
+
+ this.fetchJob();
+ this.fetchTrace();
+
+ window.addEventListener('resize', this.onResize);
+ window.addEventListener('scroll', this.updateScroll);
+ },
+ mounted() {
+ this.updateSidebar();
+ },
+ destroyed() {
+ window.removeEventListener('resize', this.onResize);
+ window.removeEventListener('scroll', this.updateScroll);
+ },
+ methods: {
+ ...mapActions([
+ 'setJobEndpoint',
+ 'setTraceOptions',
+ 'fetchJob',
+ 'fetchStages',
+ 'hideSidebar',
+ 'showSidebar',
+ 'toggleSidebar',
+ 'fetchTrace',
+ 'scrollBottom',
+ 'scrollTop',
+ 'toggleScrollButtons',
+ 'toggleScrollAnimation',
+ ]),
+ onResize() {
+ this.updateSidebar();
+ this.updateScroll();
+ },
+ updateSidebar() {
+ if (bp.getBreakpointSize() === 'xs') {
+ this.hideSidebar();
+ } else if (!this.isSidebarOpen) {
+ this.showSidebar();
+ }
+ },
+ updateScroll() {
+ if (!isScrolledToBottom()) {
+ this.toggleScrollAnimation(false);
+ } else if (this.isScrollingDown) {
+ this.toggleScrollAnimation(true);
+ }
+
+ this.throttled();
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-loading-icon
+ v-if="isLoading"
+ :size="2"
+ class="js-job-loading qa-loading-animation prepend-top-20"
+ />
+
+ <template v-else-if="shouldRenderContent">
+ <div class="js-job-content build-page">
+ <!-- Header Section -->
+ <header>
+ <div class="js-build-header build-header top-area">
+ <ci-header
+ :status="job.status"
+ :item-id="job.id"
+ :time="headerTime"
+ :user="job.user"
+ :actions="headerActions"
+ :has-sidebar-button="true"
+ :should-render-triggered-label="shouldRenderTriggeredLabel"
+ :item-name="__('Job')"
+ @clickedSidebarButton="toggleSidebar"
+ />
+ </div>
+
+ <callout
+ v-if="shouldRenderCalloutMessage"
+ :message="job.callout_message"
+ />
+ </header>
+ <!-- EO Header Section -->
+
+ <!-- Body Section -->
+ <stuck-block
+ v-if="job.stuck"
+ class="js-job-stuck"
+ :has-no-runners-for-project="hasRunnersForProject"
+ :tags="job.tags"
+ :runners-path="runnerSettingsUrl"
+ />
+
+ <environments-block
+ v-if="hasEnvironment"
+ class="js-job-environment"
+ :deployment-status="job.deployment_status"
+ :icon-status="job.status"
+ />
+
+ <erased-block
+ v-if="job.erased_at"
+ class="js-job-erased-block"
+ :user="job.erased_by"
+ :erased-at="job.erased_at"
+ />
+
+ <div
+ v-if="job.archived"
+ ref="sticky"
+ class="js-archived-job prepend-top-default archived-sticky sticky-top"
+ >
+ <icon
+ name="lock"
+ class="align-text-bottom"
+ />
+
+ {{ __('This job is archived. Only the complete pipeline can be retried.') }}
+ </div>
+ <!--job log -->
+ <div
+ v-if="hasTrace"
+ class="build-trace-container"
+ >
+ <log-top-bar
+ :class="{
+ 'sidebar-expanded': isSidebarOpen,
+ 'sidebar-collapsed': !isSidebarOpen,
+ 'has-archived-block': job.archived
+ }"
+ :erase-path="job.erase_path"
+ :size="traceSize"
+ :raw-path="job.raw_path"
+ :is-scroll-bottom-disabled="isScrollBottomDisabled"
+ :is-scroll-top-disabled="isScrollTopDisabled"
+ :is-trace-size-visible="isTraceSizeVisible"
+ :is-scrolling-down="isScrollingDown"
+ @scrollJobLogTop="scrollTop"
+ @scrollJobLogBottom="scrollBottom"
+ />
+ <log
+ :trace="trace"
+ :is-complete="isTraceComplete"
+ />
+ </div>
+ <!-- EO job log -->
+
+ <!--empty state -->
+ <empty-state
+ v-if="!hasTrace"
+ class="js-job-empty-state"
+ :illustration-path="emptyStateIllustration.image"
+ :illustration-size-class="emptyStateIllustration.size"
+ :title="emptyStateTitle"
+ :content="emptyStateIllustration.content"
+ :action="emptyStateAction"
+ />
+ <!-- EO empty state -->
+
+ <!-- EO Body Section -->
+ </div>
+ </template>
+
+ <sidebar
+ v-if="shouldRenderContent"
+ class="js-job-sidebar"
+ :class="{
+ 'right-sidebar-expanded': isSidebarOpen,
+ 'right-sidebar-collapsed': !isSidebarOpen
+ }"
+ :runner-help-url="runnerHelpUrl"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/job_container_item.vue b/app/assets/javascripts/jobs/components/job_container_item.vue
new file mode 100644
index 00000000000..80cbed422a0
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job_container_item.vue
@@ -0,0 +1,76 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import tooltip from '~/vue_shared/directives/tooltip';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
+import { sprintf } from '~/locale';
+
+export default {
+ components: {
+ CiIcon,
+ Icon,
+ GlLink,
+ },
+ directives: {
+ tooltip,
+ },
+ mixins: [delayedJobMixin],
+ props: {
+ job: {
+ type: Object,
+ required: true,
+ },
+ isActive: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ tooltipText() {
+ const { name, status } = this.job;
+ const text = `${name} - ${status.tooltip}`;
+
+ if (this.isDelayedJob) {
+ return sprintf(text, { remainingTime: this.remainingTime });
+ }
+
+ return text;
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ class="build-job"
+ :class="{
+ retried: job.retried,
+ active: isActive
+ }"
+ >
+ <gl-link
+ v-tooltip
+ :href="job.status.details_path"
+ :title="tooltipText"
+ data-boundary="viewport"
+ class="js-job-link"
+ >
+ <icon
+ v-if="isActive"
+ name="arrow-right"
+ class="js-arrow-right icon-arrow-right"
+ />
+
+ <ci-icon :status="job.status" />
+
+ <span>{{ job.name ? job.name : job.id }}</span>
+
+ <icon
+ v-if="job.retried"
+ name="retry"
+ class="js-retry-icon"
+ />
+ </gl-link>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/job_log.vue b/app/assets/javascripts/jobs/components/job_log.vue
new file mode 100644
index 00000000000..92e20e92d66
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job_log.vue
@@ -0,0 +1,61 @@
+<script>
+import { mapState, mapActions } from 'vuex';
+
+export default {
+ name: 'JobLog',
+ props: {
+ trace: {
+ type: String,
+ required: true,
+ },
+ isComplete: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['isScrolledToBottomBeforeReceivingTrace']),
+ },
+ updated() {
+ this.$nextTick(() => this.handleScrollDown());
+ },
+ mounted() {
+ this.$nextTick(() => this.handleScrollDown());
+ },
+ methods: {
+ ...mapActions(['scrollBottom']),
+ /**
+ * The job log is sent in HTML, which means we need to use `v-html` to render it
+ * Using the updated hook with $nextTick is not enough to wait for the DOM to be updated
+ * in this case because it runs before `v-html` has finished running, since there's no
+ * Vue binding.
+ * In order to scroll the page down after `v-html` has finished, we need to use setTimeout
+ */
+ handleScrollDown() {
+ if (this.isScrolledToBottomBeforeReceivingTrace) {
+ setTimeout(() => {
+ this.scrollBottom();
+ }, 0);
+ }
+ },
+ },
+};
+</script>
+<template>
+ <pre class="js-build-trace build-trace qa-build-trace">
+ <code
+ class="bash"
+ v-html="trace"
+ >
+ </code>
+
+ <div
+ v-if="!isComplete"
+ class="js-log-animation build-loader-animation"
+ >
+ <div class="dot"></div>
+ <div class="dot"></div>
+ <div class="dot"></div>
+ </div>
+ </pre>
+</template>
diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job_log_controllers.vue
new file mode 100644
index 00000000000..5e0495bb231
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job_log_controllers.vue
@@ -0,0 +1,146 @@
+<script>
+import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui';
+import { polyfillSticky } from '~/lib/utils/sticky';
+import Icon from '~/vue_shared/components/icon.vue';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { sprintf } from '~/locale';
+import scrollDown from '../svg/scroll_down.svg';
+
+export default {
+ components: {
+ Icon,
+ GlLink,
+ GlButton,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ scrollDown,
+ props: {
+ erasePath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ size: {
+ type: Number,
+ required: true,
+ },
+ rawPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ isScrollTopDisabled: {
+ type: Boolean,
+ required: true,
+ },
+ isScrollBottomDisabled: {
+ type: Boolean,
+ required: true,
+ },
+ isScrollingDown: {
+ type: Boolean,
+ required: true,
+ },
+ isTraceSizeVisible: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ jobLogSize() {
+ return sprintf('Showing last %{size} of log -', {
+ size: numberToHumanSize(this.size),
+ });
+ },
+ },
+ mounted() {
+ polyfillSticky(this.$el);
+ },
+ methods: {
+ handleScrollToTop() {
+ this.$emit('scrollJobLogTop');
+ },
+ handleScrollToBottom() {
+ this.$emit('scrollJobLogBottom');
+ },
+ },
+};
+</script>
+<template>
+ <div class="top-bar">
+ <!-- truncate information -->
+ <div class="js-truncated-info truncated-info d-none d-sm-block float-left">
+ <template v-if="isTraceSizeVisible">
+ {{ jobLogSize }}
+
+ <gl-link
+ v-if="rawPath"
+ :href="rawPath"
+ class="js-raw-link raw-link"
+ >
+ {{ s__("Job|Complete Raw") }}
+ </gl-link>
+ </template>
+ </div>
+ <!-- eo truncate information -->
+
+ <div class="controllers float-right">
+ <!-- links -->
+ <gl-link
+ v-if="rawPath"
+ v-gl-tooltip.body
+ :title="s__('Job|Show complete raw')"
+ :href="rawPath"
+ class="js-raw-link-controller controllers-buttons"
+ >
+ <icon name="doc-text" />
+ </gl-link>
+
+ <gl-link
+ v-if="erasePath"
+ v-gl-tooltip.body
+ :title="s__('Job|Erase job log')"
+ :href="erasePath"
+ :data-confirm="__('Are you sure you want to erase this build?')"
+ class="js-erase-link controllers-buttons"
+ data-method="post"
+ >
+ <icon name="remove" />
+ </gl-link>
+ <!-- eo links -->
+
+ <!-- scroll buttons -->
+ <div
+ v-gl-tooltip
+ :title="s__('Job|Scroll to top')"
+ class="controllers-buttons"
+ >
+ <gl-button
+ :disabled="isScrollTopDisabled"
+ type="button"
+ class="js-scroll-top btn-scroll btn-transparent btn-blank"
+ @click="handleScrollToTop"
+ >
+ <icon name="scroll_up" />
+ </gl-button>
+ </div>
+
+ <div
+ v-gl-tooltip
+ :title="s__('Job|Scroll to bottom')"
+ class="controllers-buttons"
+ >
+ <gl-button
+ :disabled="isScrollBottomDisabled"
+ class="js-scroll-bottom btn-scroll btn-transparent btn-blank"
+ :class="{ animate: isScrollingDown }"
+ @click="handleScrollToBottom"
+ v-html="$options.scrollDown"
+ />
+ </div>
+ <!-- eo scroll buttons -->
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/jobs_container.vue b/app/assets/javascripts/jobs/components/jobs_container.vue
new file mode 100644
index 00000000000..951bcb36600
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/jobs_container.vue
@@ -0,0 +1,35 @@
+<script>
+import JobContainerItem from './job_container_item.vue';
+
+export default {
+ components: {
+ JobContainerItem,
+ },
+
+ props: {
+ jobs: {
+ type: Array,
+ required: true,
+ },
+ jobId: {
+ type: Number,
+ required: true,
+ },
+ },
+ methods: {
+ isJobActive(currentJobId) {
+ return this.jobId === currentJobId;
+ },
+ },
+};
+</script>
+<template>
+ <div class="js-jobs-container builds-container">
+ <job-container-item
+ v-for="job in jobs"
+ :key="job.id"
+ :job="job"
+ :is-active="isJobActive(job.id)"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/sidebar.vue b/app/assets/javascripts/jobs/components/sidebar.vue
new file mode 100644
index 00000000000..21f0a1435d7
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/sidebar.vue
@@ -0,0 +1,291 @@
+<script>
+import _ from 'underscore';
+import { mapActions, mapState } from 'vuex';
+import { GlLink, GlButton } from '@gitlab/ui';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
+import Icon from '~/vue_shared/components/icon.vue';
+import DetailRow from './sidebar_detail_row.vue';
+import ArtifactsBlock from './artifacts_block.vue';
+import TriggerBlock from './trigger_block.vue';
+import CommitBlock from './commit_block.vue';
+import StagesDropdown from './stages_dropdown.vue';
+import JobsContainer from './jobs_container.vue';
+
+export default {
+ name: 'JobSidebar',
+ components: {
+ ArtifactsBlock,
+ CommitBlock,
+ DetailRow,
+ Icon,
+ TriggerBlock,
+ StagesDropdown,
+ JobsContainer,
+ GlLink,
+ GlButton,
+ },
+ mixins: [timeagoMixin],
+ props: {
+ runnerHelpUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ ...mapState(['job', 'stages', 'jobs', 'selectedStage', 'isLoadingStages']),
+ coverage() {
+ return `${this.job.coverage}%`;
+ },
+ duration() {
+ return timeIntervalInWords(this.job.duration);
+ },
+ queued() {
+ return timeIntervalInWords(this.job.queued);
+ },
+ runnerId() {
+ return `${this.job.runner.description} (#${this.job.runner.id})`;
+ },
+ retryButtonClass() {
+ let className =
+ 'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
+ className +=
+ this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
+ return className;
+ },
+ hasTimeout() {
+ return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null;
+ },
+ timeout() {
+ if (this.job.metadata == null) {
+ return '';
+ }
+
+ let t = this.job.metadata.timeout_human_readable;
+ if (this.job.metadata.timeout_source !== '') {
+ t += ` (from ${this.job.metadata.timeout_source})`;
+ }
+
+ return t;
+ },
+ renderBlock() {
+ return (
+ this.job.merge_request ||
+ this.job.duration ||
+ this.job.finished_data ||
+ this.job.erased_at ||
+ this.job.queued ||
+ this.job.runner ||
+ this.job.coverage ||
+ this.job.tags.length ||
+ this.job.cancel_path
+ );
+ },
+ hasArtifact() {
+ return !_.isEmpty(this.job.artifact);
+ },
+ hasTriggers() {
+ return !_.isEmpty(this.job.trigger);
+ },
+ hasStages() {
+ return (
+ (this.job &&
+ this.job.pipeline &&
+ this.job.pipeline.stages &&
+ this.job.pipeline.stages.length > 0) ||
+ false
+ );
+ },
+ commit() {
+ return this.job.pipeline && this.job.pipeline.commit ? this.job.pipeline.commit : {};
+ },
+ },
+ methods: {
+ ...mapActions(['fetchJobsForStage', 'toggleSidebar']),
+ },
+};
+</script>
+<template>
+ <aside
+ class="right-sidebar build-sidebar"
+ data-offset-top="101"
+ data-spy="affix"
+ >
+ <div class="sidebar-container">
+ <div class="blocks-container">
+ <div class="block">
+ <strong class="inline prepend-top-8">
+ {{ job.name }}
+ </strong>
+ <gl-link
+ v-if="job.retry_path"
+ :class="retryButtonClass"
+ :href="job.retry_path"
+ data-method="post"
+ rel="nofollow"
+ >
+ {{ __('Retry') }}
+ </gl-link>
+ <gl-link
+ v-if="job.terminal_path"
+ :href="job.terminal_path"
+ class="js-terminal-link pull-right btn btn-primary
+ btn-inverted visible-md-block visible-lg-block"
+ target="_blank"
+ >
+ {{ __('Debug') }}
+ <icon name="external-link" />
+ </gl-link>
+ <gl-button
+ :aria-label="__('Toggle Sidebar')"
+ type="button"
+ class="btn btn-blank gutter-toggle
+ float-right d-block d-md-none js-sidebar-build-toggle"
+ @click="toggleSidebar"
+ >
+ <i
+ aria-hidden="true"
+ data-hidden="true"
+ class="fa fa-angle-double-right"
+ ></i>
+ </gl-button>
+ </div>
+ <div
+ v-if="job.retry_path || job.new_issue_path"
+ class="block retry-link"
+ >
+ <gl-link
+ v-if="job.new_issue_path"
+ :href="job.new_issue_path"
+ class="js-new-issue btn btn-success btn-inverted"
+ >
+ {{ __('New issue') }}
+ </gl-link>
+ <gl-link
+ v-if="job.retry_path"
+ :href="job.retry_path"
+ class="js-retry-job btn btn-inverted-secondary"
+ data-method="post"
+ rel="nofollow"
+ >
+ {{ __('Retry') }}
+ </gl-link>
+ </div>
+ <div :class="{ block : renderBlock }">
+ <p
+ v-if="job.merge_request"
+ class="build-detail-row js-job-mr"
+ >
+ <span class="build-light-text">
+ {{ __('Merge Request:') }}
+ </span>
+ <gl-link :href="job.merge_request.path">
+ !{{ job.merge_request.iid }}
+ </gl-link>
+ </p>
+
+ <detail-row
+ v-if="job.duration"
+ :value="duration"
+ class="js-job-duration"
+ title="Duration"
+ />
+ <detail-row
+ v-if="job.finished_at"
+ :value="timeFormated(job.finished_at)"
+ class="js-job-finished"
+ title="Finished"
+ />
+ <detail-row
+ v-if="job.erased_at"
+ :value="timeFormated(job.erased_at)"
+ class="js-job-erased"
+ title="Erased"
+ />
+ <detail-row
+ v-if="job.queued"
+ :value="queued"
+ class="js-job-queued"
+ title="Queued"
+ />
+ <detail-row
+ v-if="hasTimeout"
+ :help-url="runnerHelpUrl"
+ :value="timeout"
+ class="js-job-timeout"
+ title="Timeout"
+ />
+ <detail-row
+ v-if="job.runner"
+ :value="runnerId"
+ class="js-job-runner"
+ title="Runner"
+ />
+ <detail-row
+ v-if="job.coverage"
+ :value="coverage"
+ class="js-job-coverage"
+ title="Coverage"
+ />
+ <p
+ v-if="job.tags.length"
+ class="build-detail-row js-job-tags"
+ >
+ <span class="build-light-text">
+ {{ __('Tags:') }}
+ </span>
+ <span
+ v-for="(tag, i) in job.tags"
+ :key="i"
+ class="label label-primary">
+ {{ tag }}
+ </span>
+ </p>
+
+ <div
+ v-if="job.cancel_path"
+ class="btn-group prepend-top-5"
+ role="group">
+ <gl-link
+ :href="job.cancel_path"
+ class="js-cancel-job btn btn-sm btn-default"
+ data-method="post"
+ rel="nofollow"
+ >
+ {{ __('Cancel') }}
+ </gl-link>
+ </div>
+ </div>
+
+ <artifacts-block
+ v-if="hasArtifact"
+ :artifact="job.artifact"
+ />
+ <trigger-block
+ v-if="hasTriggers"
+ :trigger="job.trigger"
+ />
+ <commit-block
+ :is-last-block="hasStages"
+ :commit="commit"
+ :merge-request="job.merge_request"
+ />
+
+ <stages-dropdown
+ v-if="!isLoadingStages"
+ :stages="stages"
+ :pipeline="job.pipeline"
+ :selected-stage="selectedStage"
+ @requestSidebarStageDropdown="fetchJobsForStage"
+ />
+ </div>
+
+ <jobs-container
+ v-if="jobs.length"
+ :jobs="jobs"
+ :job-id="job.id"
+ />
+ </div>
+ </aside>
+</template>
diff --git a/app/assets/javascripts/jobs/components/sidebar_detail_row.vue b/app/assets/javascripts/jobs/components/sidebar_detail_row.vue
index 83560a8ff0e..d143e9f586c 100644
--- a/app/assets/javascripts/jobs/components/sidebar_detail_row.vue
+++ b/app/assets/javascripts/jobs/components/sidebar_detail_row.vue
@@ -1,31 +1,36 @@
<script>
- export default {
- name: 'SidebarDetailRow',
- props: {
- title: {
- type: String,
- required: false,
- default: '',
- },
- value: {
- type: String,
- required: true,
- },
- helpUrl: {
- type: String,
- required: false,
- default: '',
- },
+import { GlLink } from '@gitlab/ui';
+
+export default {
+ name: 'SidebarDetailRow',
+ components: {
+ GlLink,
+ },
+ props: {
+ title: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ value: {
+ type: String,
+ required: true,
+ },
+ helpUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ hasTitle() {
+ return this.title.length > 0;
},
- computed: {
- hasTitle() {
- return this.title.length > 0;
- },
- hasHelpURL() {
- return this.helpUrl.length > 0;
- },
+ hasHelpURL() {
+ return this.helpUrl.length > 0;
},
- };
+ },
+};
</script>
<template>
<p class="build-detail-row">
@@ -41,7 +46,7 @@
v-if="hasHelpURL"
class="help-button float-right"
>
- <a
+ <gl-link
:href="helpUrl"
target="_blank"
rel="noopener noreferrer nofollow"
@@ -50,7 +55,7 @@
class="fa fa-question-circle"
aria-hidden="true"
></i>
- </a>
+ </gl-link>
</span>
</p>
</template>
diff --git a/app/assets/javascripts/jobs/components/sidebar_details_block.vue b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
deleted file mode 100644
index 36d4a3e2bc9..00000000000
--- a/app/assets/javascripts/jobs/components/sidebar_details_block.vue
+++ /dev/null
@@ -1,241 +0,0 @@
-<script>
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
-import timeagoMixin from '~/vue_shared/mixins/timeago';
-import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
-import Icon from '~/vue_shared/components/icon.vue';
-import DetailRow from './sidebar_detail_row.vue';
-
-export default {
- name: 'SidebarDetailsBlock',
- components: {
- DetailRow,
- LoadingIcon,
- Icon,
- },
- mixins: [timeagoMixin],
- props: {
- job: {
- type: Object,
- required: true,
- },
- isLoading: {
- type: Boolean,
- required: true,
- },
- runnerHelpUrl: {
- type: String,
- required: false,
- default: '',
- },
- terminalPath: {
- type: String,
- required: false,
- default: null,
- },
- },
- computed: {
- shouldRenderContent() {
- return !this.isLoading && Object.keys(this.job).length > 0;
- },
- coverage() {
- return `${this.job.coverage}%`;
- },
- duration() {
- return timeIntervalInWords(this.job.duration);
- },
- queued() {
- return timeIntervalInWords(this.job.queued);
- },
- runnerId() {
- return `${this.job.runner.description} (#${this.job.runner.id})`;
- },
- retryButtonClass() {
- let className =
- 'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
- className +=
- this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
- return className;
- },
- hasTimeout() {
- return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null;
- },
- timeout() {
- if (this.job.metadata == null) {
- return '';
- }
-
- let t = this.job.metadata.timeout_human_readable;
- if (this.job.metadata.timeout_source !== '') {
- t += ` (from ${this.job.metadata.timeout_source})`;
- }
-
- return t;
- },
- renderBlock() {
- return (
- this.job.merge_request ||
- this.job.duration ||
- this.job.finished_data ||
- this.job.erased_at ||
- this.job.queued ||
- this.job.runner ||
- this.job.coverage ||
- this.job.tags.length ||
- this.job.cancel_path
- );
- },
- },
-};
-</script>
-<template>
- <div>
- <div class="block">
- <strong class="inline prepend-top-8">
- {{ job.name }}
- </strong>
- <a
- v-if="job.retry_path"
- :class="retryButtonClass"
- :href="job.retry_path"
- data-method="post"
- rel="nofollow"
- >
- {{ __('Retry') }}
- </a>
- <a
- v-if="terminalPath"
- :href="terminalPath"
- class="js-terminal-link pull-right btn btn-primary
- btn-inverted visible-md-block visible-lg-block"
- target="_blank"
- >
- {{ __('Debug') }}
- <icon name="external-link" />
- </a>
- <button
- :aria-label="__('Toggle Sidebar')"
- type="button"
- class="btn btn-blank gutter-toggle float-right d-block d-md-none js-sidebar-build-toggle"
- >
- <i
- aria-hidden="true"
- data-hidden="true"
- class="fa fa-angle-double-right"
- ></i>
- </button>
- </div>
- <template v-if="shouldRenderContent">
- <div
- v-if="job.retry_path || job.new_issue_path"
- class="block retry-link"
- >
- <a
- v-if="job.new_issue_path"
- :href="job.new_issue_path"
- class="js-new-issue btn btn-new btn-inverted"
- >
- {{ __('New issue') }}
- </a>
- <a
- v-if="job.retry_path"
- :href="job.retry_path"
- class="js-retry-job btn btn-inverted-secondary"
- data-method="post"
- rel="nofollow"
- >
- {{ __('Retry') }}
- </a>
- </div>
- <div :class="{block : renderBlock }">
- <p
- v-if="job.merge_request"
- class="build-detail-row js-job-mr"
- >
- <span class="build-light-text">
- {{ __('Merge Request:') }}
- </span>
- <a :href="job.merge_request.path">
- !{{ job.merge_request.iid }}
- </a>
- </p>
-
- <detail-row
- v-if="job.duration"
- :value="duration"
- class="js-job-duration"
- title="Duration"
- />
- <detail-row
- v-if="job.finished_at"
- :value="timeFormated(job.finished_at)"
- class="js-job-finished"
- title="Finished"
- />
- <detail-row
- v-if="job.erased_at"
- :value="timeFormated(job.erased_at)"
- class="js-job-erased"
- title="Erased"
- />
- <detail-row
- v-if="job.queued"
- :value="queued"
- class="js-job-queued"
- title="Queued"
- />
- <detail-row
- v-if="hasTimeout"
- :help-url="runnerHelpUrl"
- :value="timeout"
- class="js-job-timeout"
- title="Timeout"
- />
- <detail-row
- v-if="job.runner"
- :value="runnerId"
- class="js-job-runner"
- title="Runner"
- />
- <detail-row
- v-if="job.coverage"
- :value="coverage"
- class="js-job-coverage"
- title="Coverage"
- />
- <p
- v-if="job.tags.length"
- class="build-detail-row js-job-tags"
- >
- <span class="build-light-text">
- {{ __('Tags:') }}
- </span>
- <span
- v-for="(tag, i) in job.tags"
- :key="i"
- class="label label-primary">
- {{ tag }}
- </span>
- </p>
-
- <div
- v-if="job.cancel_path"
- class="btn-group prepend-top-5"
- role="group">
- <a
- :href="job.cancel_path"
- class="js-cancel-job btn btn-sm btn-default"
- data-method="post"
- rel="nofollow"
- >
- {{ __('Cancel') }}
- </a>
- </div>
- </div>
- </template>
- <loading-icon
- v-if="isLoading"
- class="prepend-top-10"
- size="2"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue
new file mode 100644
index 00000000000..dc26b246d71
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue
@@ -0,0 +1,85 @@
+<script>
+import _ from 'underscore';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ components: {
+ CiIcon,
+ Icon,
+ },
+ props: {
+ pipeline: {
+ type: Object,
+ required: true,
+ },
+ stages: {
+ type: Array,
+ required: true,
+ },
+ selectedStage: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ hasRef() {
+ return !_.isEmpty(this.pipeline.ref);
+ },
+ },
+ methods: {
+ onStageClick(stage) {
+ this.$emit('requestSidebarStageDropdown', stage);
+ },
+ },
+};
+</script>
+<template>
+ <div class="block-last dropdown">
+ <ci-icon
+ :status="pipeline.details.status"
+ class="vertical-align-middle"
+ />
+
+ {{ __('Pipeline') }}
+ <a
+ :href="pipeline.path"
+ class="js-pipeline-path link-commit"
+ >
+ #{{ pipeline.id }}
+ </a>
+ <template v-if="hasRef">
+ {{ __('from') }}
+ <a
+ :href="pipeline.ref.path"
+ class="link-commit ref-name"
+ >
+ {{ pipeline.ref.name }}
+ </a>
+ </template>
+
+ <button
+ type="button"
+ data-toggle="dropdown"
+ class="js-selected-stage dropdown-menu-toggle prepend-top-8"
+ >
+ {{ selectedStage }}
+ <i class="fa fa-chevron-down" ></i>
+ </button>
+
+ <ul class="dropdown-menu">
+ <li
+ v-for="stage in stages"
+ :key="stage.name"
+ >
+ <button
+ type="button"
+ class="js-stage-item stage-item"
+ @click="onStageClick(stage)"
+ >
+ {{ stage.name }}
+ </button>
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/stuck_block.vue b/app/assets/javascripts/jobs/components/stuck_block.vue
index 18883fea950..2b2ebe4c3f7 100644
--- a/app/assets/javascripts/jobs/components/stuck_block.vue
+++ b/app/assets/javascripts/jobs/components/stuck_block.vue
@@ -1,8 +1,12 @@
<script>
+import { GlLink } from '@gitlab/ui';
/**
* Renders Stuck Runners block for job's view.
*/
export default {
+ components: {
+ GlLink,
+ },
props: {
hasNoRunnersForProject: {
type: Boolean,
@@ -23,15 +27,8 @@ export default {
<template>
<div class="bs-callout bs-callout-warning">
<p
- v-if="hasNoRunnersForProject"
- class="js-stuck-no-runners"
- >
- {{ s__(`Job|This job is stuck, because the project
- doesn't have any runners online assigned to it.`) }}
- </p>
- <p
- v-else-if="tags.length"
- class="js-stuck-with-tags"
+ v-if="tags.length"
+ class="js-stuck-with-tags append-bottom-0"
>
{{ s__(`This job is stuck, because you don't have
any active runners online with any of these tags assigned to them:`) }}
@@ -44,20 +41,27 @@ export default {
</span>
</p>
<p
+ v-else-if="hasNoRunnersForProject"
+ class="js-stuck-no-runners append-bottom-0"
+ >
+ {{ s__(`Job|This job is stuck, because the project
+ doesn't have any runners online assigned to it.`) }}
+ </p>
+ <p
v-else
- class="js-stuck-no-active-runner"
+ class="js-stuck-no-active-runner append-bottom-0"
>
{{ s__(`This job is stuck, because you don't
have any active runners that can run this job.`) }}
</p>
{{ __("Go to") }}
- <a
+ <gl-link
v-if="runnersPath"
:href="runnersPath"
class="js-runners-path"
>
{{ __("Runners page") }}
- </a>
+ </gl-link>
</div>
</template>
diff --git a/app/assets/javascripts/jobs/components/trigger_block.vue b/app/assets/javascripts/jobs/components/trigger_block.vue
new file mode 100644
index 00000000000..4d18f76b7ea
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/trigger_block.vue
@@ -0,0 +1,82 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlButton,
+ },
+ props: {
+ trigger: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ areVariablesVisible: false,
+ };
+ },
+ computed: {
+ hasVariables() {
+ return this.trigger.variables && this.trigger.variables.length > 0;
+ },
+ },
+ methods: {
+ revealVariables() {
+ this.areVariablesVisible = true;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="build-widget block">
+ <h4 class="title">
+ {{ __('Trigger') }}
+ </h4>
+
+ <p
+ v-if="trigger.short_token"
+ class="js-short-token"
+ >
+ <span class="build-light-text">
+ {{ __('Token') }}
+ </span>
+ {{ trigger.short_token }}
+ </p>
+
+ <p v-if="hasVariables">
+ <gl-button
+ v-if="!areVariablesVisible"
+ type="button"
+ class="btn btn-default group js-reveal-variables"
+ @click="revealVariables"
+ >
+ {{ __('Reveal Variables') }}
+ </gl-button>
+ </p>
+
+ <dl
+ v-if="areVariablesVisible"
+ class="js-build-variables trigger-build-variables"
+ >
+ <template
+ v-for="variable in trigger.variables"
+ >
+ <dt
+ :key="`${variable.key}-variable`"
+ class="js-build-variable trigger-build-variable"
+ >
+ {{ variable.key }}
+ </dt>
+
+ <dd
+ :key="`${variable.key}-value`"
+ class="js-build-value trigger-build-value"
+ >
+ {{ variable.value }}
+ </dd>
+ </template>
+ </dl>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/index.js b/app/assets/javascripts/jobs/index.js
new file mode 100644
index 00000000000..a32e945627c
--- /dev/null
+++ b/app/assets/javascripts/jobs/index.js
@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import JobApp from './components/job_app.vue';
+
+export default () => {
+ const element = document.getElementById('js-job-vue-app');
+
+ return new Vue({
+ el: element,
+ components: {
+ JobApp,
+ },
+ render(createElement) {
+ return createElement('job-app', {
+ props: {
+ runnerHelpUrl: element.dataset.runnerHelpUrl,
+ runnerSettingsUrl: element.dataset.runnerSettingsUrl,
+ endpoint: element.dataset.endpoint,
+ pagePath: element.dataset.buildOptionsPagePath,
+ logState: element.dataset.buildOptionsLogState,
+ buildStatus: element.dataset.buildOptionsBuildStatus,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js
deleted file mode 100644
index a84324f14b2..00000000000
--- a/app/assets/javascripts/jobs/job_details_bundle.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import Vue from 'vue';
-import JobMediator from './job_details_mediator';
-import jobHeader from './components/header.vue';
-import detailsBlock from './components/sidebar_details_block.vue';
-
-export default () => {
- const { dataset } = document.getElementById('js-job-details-vue');
- const mediator = new JobMediator({ endpoint: dataset.endpoint });
-
- mediator.fetchJob();
-
- // Header
- // eslint-disable-next-line no-new
- new Vue({
- el: '#js-build-header-vue',
- components: {
- jobHeader,
- },
- data() {
- return {
- mediator,
- };
- },
- mounted() {
- this.mediator.initBuildClass();
- },
- render(createElement) {
- return createElement('job-header', {
- props: {
- isLoading: this.mediator.state.isLoading,
- job: this.mediator.store.state.job,
- },
- });
- },
- });
-
- // Sidebar information block
- const detailsBlockElement = document.getElementById('js-details-block-vue');
- const detailsBlockDataset = detailsBlockElement.dataset;
- // eslint-disable-next-line
- new Vue({
- el: detailsBlockElement,
- components: {
- detailsBlock,
- },
- data() {
- return {
- mediator,
- };
- },
- render(createElement) {
- return createElement('details-block', {
- props: {
- isLoading: this.mediator.state.isLoading,
- job: this.mediator.store.state.job,
- runnerHelpUrl: dataset.runnerHelpUrl,
- terminalPath: detailsBlockDataset.terminalPath,
- },
- });
- },
- });
-};
diff --git a/app/assets/javascripts/jobs/job_details_mediator.js b/app/assets/javascripts/jobs/job_details_mediator.js
deleted file mode 100644
index 89019da9d1e..00000000000
--- a/app/assets/javascripts/jobs/job_details_mediator.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import Visibility from 'visibilityjs';
-import Flash from '../flash';
-import Poll from '../lib/utils/poll';
-import JobStore from './stores/job_store';
-import JobService from './services/job_service';
-import Job from '../job';
-import handleRevealVariables from '../build_variables';
-
-export default class JobMediator {
- constructor(options = {}) {
- this.options = options;
-
- this.store = new JobStore();
- this.service = new JobService(options.endpoint);
-
- this.state = {
- isLoading: false,
- };
- }
-
- initBuildClass() {
- this.build = new Job();
- handleRevealVariables();
- }
-
- fetchJob() {
- this.poll = new Poll({
- resource: this.service,
- method: 'getJob',
- successCallback: response => this.successCallback(response),
- errorCallback: () => this.errorCallback(),
- });
-
- if (!Visibility.hidden()) {
- this.state.isLoading = true;
- this.poll.makeRequest();
- } else {
- this.getJob();
- }
-
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- this.poll.restart();
- } else {
- this.poll.stop();
- }
- });
- }
-
- getJob() {
- return this.service
- .getJob()
- .then(response => this.successCallback(response))
- .catch(() => this.errorCallback());
- }
-
- successCallback(response) {
- this.state.isLoading = false;
- return this.store.storeJob(response.data);
- }
-
- errorCallback() {
- this.state.isLoading = false;
-
- return new Flash('An error occurred while fetching the job.');
- }
-}
diff --git a/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js b/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js
new file mode 100644
index 00000000000..8c7fb785a61
--- /dev/null
+++ b/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js
@@ -0,0 +1,50 @@
+import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility';
+
+export default {
+ data() {
+ return {
+ remainingTime: formatTime(0),
+ remainingTimeIntervalId: null,
+ };
+ },
+
+ mounted() {
+ this.startRemainingTimeInterval();
+ },
+
+ beforeDestroy() {
+ if (this.remainingTimeIntervalId) {
+ clearInterval(this.remainingTimeIntervalId);
+ }
+ },
+
+ computed: {
+ isDelayedJob() {
+ return this.job && this.job.scheduled;
+ },
+ },
+
+ watch: {
+ isDelayedJob() {
+ this.startRemainingTimeInterval();
+ },
+ },
+
+ methods: {
+ startRemainingTimeInterval() {
+ if (this.remainingTimeIntervalId) {
+ clearInterval(this.remainingTimeIntervalId);
+ }
+
+ if (this.isDelayedJob) {
+ this.updateRemainingTime();
+ this.remainingTimeIntervalId = setInterval(() => this.updateRemainingTime(), 1000);
+ }
+ },
+
+ updateRemainingTime() {
+ const remainingMilliseconds = calculateRemainingMilliseconds(this.job.scheduled_at);
+ this.remainingTime = formatTime(remainingMilliseconds);
+ },
+ },
+};
diff --git a/app/assets/javascripts/jobs/services/job_service.js b/app/assets/javascripts/jobs/services/job_service.js
deleted file mode 100644
index b746489c45c..00000000000
--- a/app/assets/javascripts/jobs/services/job_service.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import axios from '../../lib/utils/axios_utils';
-
-export default class JobService {
- constructor(endpoint) {
- this.job = endpoint;
- }
-
- getJob() {
- return axios.get(this.job);
- }
-}
diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js
new file mode 100644
index 00000000000..8045f6dc3ff
--- /dev/null
+++ b/app/assets/javascripts/jobs/store/actions.js
@@ -0,0 +1,237 @@
+import Visibility from 'visibilityjs';
+import * as types from './mutation_types';
+import axios from '~/lib/utils/axios_utils';
+import Poll from '~/lib/utils/poll';
+import { setFaviconOverlay, resetFavicon } from '~/lib/utils/common_utils';
+import flash from '~/flash';
+import { __ } from '~/locale';
+import {
+ canScroll,
+ isScrolledToBottom,
+ isScrolledToTop,
+ isScrolledToMiddle,
+ scrollDown,
+ scrollUp,
+} from '~/lib/utils/scroll_utils';
+
+export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint);
+export const setTraceOptions = ({ commit }, options) => commit(types.SET_TRACE_OPTIONS, options);
+
+export const hideSidebar = ({ commit }) => commit(types.HIDE_SIDEBAR);
+export const showSidebar = ({ commit }) => commit(types.SHOW_SIDEBAR);
+
+export const toggleSidebar = ({ dispatch, state }) => {
+ if (state.isSidebarOpen) {
+ dispatch('hideSidebar');
+ } else {
+ dispatch('showSidebar');
+ }
+};
+
+let eTagPoll;
+
+export const clearEtagPoll = () => {
+ eTagPoll = null;
+};
+
+export const stopPolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+
+export const restartPolling = () => {
+ if (eTagPoll) eTagPoll.restart();
+};
+
+export const requestJob = ({ commit }) => commit(types.REQUEST_JOB);
+
+export const fetchJob = ({ state, dispatch }) => {
+ dispatch('requestJob');
+
+ eTagPoll = new Poll({
+ resource: {
+ getJob(endpoint) {
+ return axios.get(endpoint);
+ },
+ },
+ data: state.jobEndpoint,
+ method: 'getJob',
+ successCallback: ({ data }) => dispatch('receiveJobSuccess', data),
+ errorCallback: () => dispatch('receiveJobError'),
+ });
+
+ if (!Visibility.hidden()) {
+ eTagPoll.makeRequest();
+ } else {
+ axios
+ .get(state.jobEndpoint)
+ .then(({ data }) => dispatch('receiveJobSuccess', data))
+ .catch(() => dispatch('receiveJobError'));
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ dispatch('restartPolling');
+ } else {
+ dispatch('stopPolling');
+ }
+ });
+};
+
+export const receiveJobSuccess = ({ commit }, data = {}) => {
+ commit(types.RECEIVE_JOB_SUCCESS, data);
+
+ if (data.status && data.status.favicon) {
+ setFaviconOverlay(data.status.favicon);
+ } else {
+ resetFavicon();
+ }
+};
+export const receiveJobError = ({ commit }) => {
+ commit(types.RECEIVE_JOB_ERROR);
+ flash(__('An error occurred while fetching the job.'));
+ resetFavicon();
+};
+
+/**
+ * Job's Trace
+ */
+export const scrollTop = ({ dispatch }) => {
+ scrollUp();
+ dispatch('toggleScrollButtons');
+};
+
+export const scrollBottom = ({ dispatch }) => {
+ scrollDown();
+ dispatch('toggleScrollButtons');
+};
+
+/**
+ * Responsible for toggling the disabled state of the scroll buttons
+ */
+export const toggleScrollButtons = ({ dispatch }) => {
+ if (canScroll()) {
+ if (isScrolledToMiddle()) {
+ dispatch('enableScrollTop');
+ dispatch('enableScrollBottom');
+ } else if (isScrolledToTop()) {
+ dispatch('disableScrollTop');
+ dispatch('enableScrollBottom');
+ } else if (isScrolledToBottom()) {
+ dispatch('disableScrollBottom');
+ dispatch('enableScrollTop');
+ }
+ } else {
+ dispatch('disableScrollBottom');
+ dispatch('disableScrollTop');
+ }
+};
+
+export const disableScrollBottom = ({ commit }) => commit(types.DISABLE_SCROLL_BOTTOM);
+export const disableScrollTop = ({ commit }) => commit(types.DISABLE_SCROLL_TOP);
+export const enableScrollBottom = ({ commit }) => commit(types.ENABLE_SCROLL_BOTTOM);
+export const enableScrollTop = ({ commit }) => commit(types.ENABLE_SCROLL_TOP);
+
+/**
+ * While the automatic scroll down is active,
+ * we show the scroll down button with an animation
+ */
+export const toggleScrollAnimation = ({ commit }, toggle) =>
+ commit(types.TOGGLE_SCROLL_ANIMATION, toggle);
+
+/**
+ * Responsible to handle automatic scroll
+ */
+export const toggleScrollisInBottom = ({ commit }, toggle) => {
+ commit(types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE, toggle);
+};
+
+export const requestTrace = ({ commit }) => commit(types.REQUEST_TRACE);
+
+let traceTimeout;
+export const fetchTrace = ({ dispatch, state }) =>
+ axios
+ .get(`${state.traceEndpoint}/trace.json`, {
+ params: { state: state.traceState },
+ })
+ .then(({ data }) => {
+ dispatch('toggleScrollisInBottom', isScrolledToBottom());
+ dispatch('receiveTraceSuccess', data);
+
+ if (!data.complete) {
+ traceTimeout = setTimeout(() => {
+ dispatch('fetchTrace');
+ }, 4000);
+ } else {
+ dispatch('stopPollingTrace');
+ }
+ })
+ .catch(() => dispatch('receiveTraceError'));
+
+export const stopPollingTrace = ({ commit }) => {
+ commit(types.STOP_POLLING_TRACE);
+ clearTimeout(traceTimeout);
+};
+export const receiveTraceSuccess = ({ commit }, log) => commit(types.RECEIVE_TRACE_SUCCESS, log);
+export const receiveTraceError = ({ commit }) => {
+ commit(types.RECEIVE_TRACE_ERROR);
+ clearTimeout(traceTimeout);
+ flash(__('An error occurred while fetching the job log.'));
+};
+
+/**
+ * Stages dropdown on sidebar
+ */
+export const requestStages = ({ commit }) => commit(types.REQUEST_STAGES);
+export const fetchStages = ({ state, dispatch }) => {
+ dispatch('requestStages');
+
+ axios
+ .get(`${state.job.pipeline.path}.json`)
+ .then(({ data }) => {
+ // Set selected stage
+ dispatch('receiveStagesSuccess', data.details.stages);
+ const selectedStage = data.details.stages.find(stage => stage.name === state.selectedStage);
+ dispatch('fetchJobsForStage', selectedStage);
+ })
+ .catch(() => dispatch('receiveStagesError'));
+};
+export const receiveStagesSuccess = ({ commit }, data) =>
+ commit(types.RECEIVE_STAGES_SUCCESS, data);
+export const receiveStagesError = ({ commit }) => {
+ commit(types.RECEIVE_STAGES_ERROR);
+ flash(__('An error occurred while fetching stages.'));
+};
+
+/**
+ * Jobs list on sidebar - depend on stages dropdown
+ */
+export const requestJobsForStage = ({ commit }, stage) =>
+ commit(types.REQUEST_JOBS_FOR_STAGE, stage);
+
+// On stage click, set selected stage + fetch job
+export const fetchJobsForStage = ({ dispatch }, stage) => {
+ dispatch('requestJobsForStage', stage);
+
+ axios
+ .get(stage.dropdown_path, {
+ params: {
+ retried: 1,
+ },
+ })
+ .then(({ data }) => {
+ const retriedJobs = data.retried.map(job => Object.assign({}, job, { retried: true }));
+ const jobs = data.latest_statuses.concat(retriedJobs);
+
+ dispatch('receiveJobsForStageSuccess', jobs);
+ })
+ .catch(() => dispatch('receiveJobsForStageError'));
+};
+export const receiveJobsForStageSuccess = ({ commit }, data) =>
+ commit(types.RECEIVE_JOBS_FOR_STAGE_SUCCESS, data);
+export const receiveJobsForStageError = ({ commit }) => {
+ commit(types.RECEIVE_JOBS_FOR_STAGE_ERROR);
+ flash(__('An error occurred while fetching the jobs.'));
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js
new file mode 100644
index 00000000000..35e92b0b5d9
--- /dev/null
+++ b/app/assets/javascripts/jobs/store/getters.js
@@ -0,0 +1,53 @@
+import _ from 'underscore';
+import { __ } from '~/locale';
+import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
+
+export const headerActions = state => {
+ if (state.job.new_issue_path) {
+ return [
+ {
+ label: __('New issue'),
+ path: state.job.new_issue_path,
+ cssClass:
+ 'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block',
+ type: 'link',
+ },
+ ];
+ }
+ return [];
+};
+
+export const headerTime = state => (state.job.started ? state.job.started : state.job.created_at);
+
+export const shouldRenderCalloutMessage = state =>
+ !_.isEmpty(state.job.status) && !_.isEmpty(state.job.callout_message);
+
+/**
+ * When job has not started the key will be null
+ * When job started the key will be a string with a date.
+ */
+export const shouldRenderTriggeredLabel = state => _.isString(state.job.started);
+
+export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status);
+
+/**
+ * Checks if it the job has trace.
+ * Used to check if it should render the job log or the empty state
+ * @returns {Boolean}
+ */
+export const hasTrace = state =>
+ state.job.has_trace || (!_.isEmpty(state.job.status) && state.job.status.group === 'running');
+
+export const emptyStateIllustration = state =>
+ (state.job && state.job.status && state.job.status.illustration) || {};
+
+export const emptyStateAction = state =>
+ (state.job && state.job.status && state.job.status.action) || null;
+
+export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceComplete;
+
+export const hasRunnersForProject = state =>
+ state.job.runners.available && !state.job.runners.online;
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/jobs/store/index.js b/app/assets/javascripts/jobs/store/index.js
new file mode 100644
index 00000000000..bba01426af7
--- /dev/null
+++ b/app/assets/javascripts/jobs/store/index.js
@@ -0,0 +1,16 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import state from './state';
+import * as actions from './actions';
+import * as getters from './getters';
+import mutations from './mutations';
+
+Vue.use(Vuex);
+
+export default () =>
+ new Vuex.Store({
+ actions,
+ mutations,
+ getters,
+ state: state(),
+ });
diff --git a/app/assets/javascripts/jobs/store/mutation_types.js b/app/assets/javascripts/jobs/store/mutation_types.js
new file mode 100644
index 00000000000..fd098f13e90
--- /dev/null
+++ b/app/assets/javascripts/jobs/store/mutation_types.js
@@ -0,0 +1,34 @@
+export const SET_JOB_ENDPOINT = 'SET_JOB_ENDPOINT';
+export const SET_TRACE_OPTIONS = 'SET_TRACE_OPTIONS';
+
+export const HIDE_SIDEBAR = 'HIDE_SIDEBAR';
+export const SHOW_SIDEBAR = 'SHOW_SIDEBAR';
+
+export const SCROLL_TO_TOP = 'SCROLL_TO_TOP';
+export const SCROLL_TO_BOTTOM = 'SCROLL_TO_BOTTOM';
+export const DISABLE_SCROLL_BOTTOM = 'DISABLE_SCROLL_BOTTOM';
+export const DISABLE_SCROLL_TOP = 'DISABLE_SCROLL_TOP';
+export const ENABLE_SCROLL_BOTTOM = 'ENABLE_SCROLL_BOTTOM';
+export const ENABLE_SCROLL_TOP = 'ENABLE_SCROLL_TOP';
+// TODO
+export const TOGGLE_SCROLL_ANIMATION = 'TOGGLE_SCROLL_ANIMATION';
+
+export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
+
+export const REQUEST_JOB = 'REQUEST_JOB';
+export const RECEIVE_JOB_SUCCESS = 'RECEIVE_JOB_SUCCESS';
+export const RECEIVE_JOB_ERROR = 'RECEIVE_JOB_ERROR';
+
+export const REQUEST_TRACE = 'REQUEST_TRACE';
+export const STOP_POLLING_TRACE = 'STOP_POLLING_TRACE';
+export const RECEIVE_TRACE_SUCCESS = 'RECEIVE_TRACE_SUCCESS';
+export const RECEIVE_TRACE_ERROR = 'RECEIVE_TRACE_ERROR';
+
+export const REQUEST_STAGES = 'REQUEST_STAGES';
+export const RECEIVE_STAGES_SUCCESS = 'RECEIVE_STAGES_SUCCESS';
+export const RECEIVE_STAGES_ERROR = 'RECEIVE_STAGES_ERROR';
+
+export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';
+export const REQUEST_JOBS_FOR_STAGE = 'REQUEST_JOBS_FOR_STAGE';
+export const RECEIVE_JOBS_FOR_STAGE_SUCCESS = 'RECEIVE_JOBS_FOR_STAGE_SUCCESS';
+export const RECEIVE_JOBS_FOR_STAGE_ERROR = 'RECEIVE_JOBS_FOR_STAGE_ERROR';
diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js
new file mode 100644
index 00000000000..cd440d21c1f
--- /dev/null
+++ b/app/assets/javascripts/jobs/store/mutations.js
@@ -0,0 +1,128 @@
+import * as types from './mutation_types';
+
+export default {
+ [types.SET_JOB_ENDPOINT](state, endpoint) {
+ state.jobEndpoint = endpoint;
+ },
+
+ [types.SET_TRACE_OPTIONS](state, options = {}) {
+ state.traceEndpoint = options.pagePath;
+ state.traceState = options.logState;
+ },
+
+ [types.HIDE_SIDEBAR](state) {
+ state.isSidebarOpen = false;
+ },
+ [types.SHOW_SIDEBAR](state) {
+ state.isSidebarOpen = true;
+ },
+
+ [types.RECEIVE_TRACE_SUCCESS](state, log) {
+ if (log.state) {
+ state.traceState = log.state;
+ }
+
+ if (log.append) {
+ state.trace += log.html;
+ state.traceSize += log.size;
+ } else {
+ // When the job still does not have a trace
+ // the trace response will not have a defined
+ // html or size. We keep the old value otherwise these
+ // will be set to `undefined`
+ state.trace = log.html || state.trace;
+ state.traceSize = log.size || state.traceSize;
+ }
+
+ if (state.traceSize < log.total) {
+ state.isTraceSizeVisible = true;
+ } else {
+ state.isTraceSizeVisible = false;
+ }
+
+ state.isTraceComplete = log.complete || state.isTraceComplete;
+ },
+
+ /**
+ * Will remove loading animation
+ */
+ [types.STOP_POLLING_TRACE](state) {
+ state.isTraceComplete = true;
+ },
+
+ /**
+ * Will remove loading animation
+ */
+ [types.RECEIVE_TRACE_ERROR](state) {
+ state.isTraceComplete = true;
+ },
+
+ [types.REQUEST_JOB](state) {
+ state.isLoading = true;
+ },
+ [types.RECEIVE_JOB_SUCCESS](state, job) {
+ state.hasError = false;
+ state.isLoading = false;
+ state.job = job;
+
+ /**
+ * We only update it on the first request
+ * The dropdown can be changed by the user
+ * after the first request,
+ * and we do not want to hijack that
+ */
+ if (state.selectedStage === '' && job.stage) {
+ state.selectedStage = job.stage;
+ }
+ },
+ [types.RECEIVE_JOB_ERROR](state) {
+ state.isLoading = false;
+ state.job = {};
+ state.hasError = true;
+ },
+
+ [types.ENABLE_SCROLL_TOP](state) {
+ state.isScrollTopDisabled = false;
+ },
+ [types.DISABLE_SCROLL_TOP](state) {
+ state.isScrollTopDisabled = true;
+ },
+ [types.ENABLE_SCROLL_BOTTOM](state) {
+ state.isScrollBottomDisabled = false;
+ },
+ [types.DISABLE_SCROLL_BOTTOM](state) {
+ state.isScrollBottomDisabled = true;
+ },
+ [types.TOGGLE_SCROLL_ANIMATION](state, toggle) {
+ state.isScrollingDown = toggle;
+ },
+
+ [types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE](state, toggle) {
+ state.isScrolledToBottomBeforeReceivingTrace = toggle;
+ },
+
+ [types.REQUEST_STAGES](state) {
+ state.isLoadingStages = true;
+ },
+ [types.RECEIVE_STAGES_SUCCESS](state, stages) {
+ state.isLoadingStages = false;
+ state.stages = stages;
+ },
+ [types.RECEIVE_STAGES_ERROR](state) {
+ state.isLoadingStages = false;
+ state.stages = [];
+ },
+
+ [types.REQUEST_JOBS_FOR_STAGE](state, stage) {
+ state.isLoadingJobs = true;
+ state.selectedStage = stage.name;
+ },
+ [types.RECEIVE_JOBS_FOR_STAGE_SUCCESS](state, jobs) {
+ state.isLoadingJobs = false;
+ state.jobs = jobs;
+ },
+ [types.RECEIVE_JOBS_FOR_STAGE_ERROR](state) {
+ state.isLoadingJobs = false;
+ state.jobs = [];
+ },
+};
diff --git a/app/assets/javascripts/jobs/store/state.js b/app/assets/javascripts/jobs/store/state.js
new file mode 100644
index 00000000000..04825187c99
--- /dev/null
+++ b/app/assets/javascripts/jobs/store/state.js
@@ -0,0 +1,33 @@
+export default () => ({
+ jobEndpoint: null,
+ traceEndpoint: null,
+
+ // sidebar
+ isSidebarOpen: true,
+
+ isLoading: false,
+ hasError: false,
+ job: {},
+
+ // scroll buttons state
+ isScrollBottomDisabled: true,
+ isScrollTopDisabled: true,
+
+ // Used to check if we should keep the automatic scroll
+ isScrolledToBottomBeforeReceivingTrace: true,
+
+ trace: '',
+ isTraceComplete: false,
+ traceSize: 0,
+ isTraceSizeVisible: false,
+
+ // used as a query parameter to fetch the trace
+ traceState: null,
+
+ // sidebar dropdown & list of jobs
+ isLoadingStages: false,
+ isLoadingJobs: false,
+ selectedStage: '',
+ stages: [],
+ jobs: [],
+});
diff --git a/app/assets/javascripts/jobs/stores/job_store.js b/app/assets/javascripts/jobs/stores/job_store.js
deleted file mode 100644
index 766194b8387..00000000000
--- a/app/assets/javascripts/jobs/stores/job_store.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export default class JobStore {
- constructor() {
- this.state = {
- job: {},
- };
- }
-
- storeJob(job = {}) {
- this.state.job = job;
- }
-}
diff --git a/app/assets/javascripts/jobs/svg/scroll_down.svg b/app/assets/javascripts/jobs/svg/scroll_down.svg
new file mode 100644
index 00000000000..1d22870ec09
--- /dev/null
+++ b/app/assets/javascripts/jobs/svg/scroll_down.svg
@@ -0,0 +1,5 @@
+<svg width="12" height="16" viewBox="0 0 12 16" xmlns="http://www.w3.org/2000/svg">
+ <path class="first-triangle" d="M1.048 14.155a.508.508 0 0 0-.32.105c-.091.07-.136.154-.136.25v.71c0 .095.045.178.135.249.09.07.197.105.321.105h10.043c.124 0 .23-.035.321-.105.09-.07.136-.154.136-.25v-.71c0-.095-.045-.178-.136-.249a.508.508 0 0 0-.32-.105"/>
+ <path class="second-triangle" d="M.687 8.027c-.09-.087-.122-.16-.093-.22.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 12.91a.458.458 0 0 1-.136.089h-.37a.626.626 0 0 1-.136-.09"/>
+ <path class="third-triangle" d="M.687 1.027C.597.94.565.867.594.807c.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 5.91A.458.458 0 0 1 6.257 6h-.37a.626.626 0 0 1-.136-.09"/>
+</svg>
diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js
index c10b1a2b233..062501d1d04 100644
--- a/app/assets/javascripts/label_manager.js
+++ b/app/assets/javascripts/label_manager.js
@@ -1,4 +1,4 @@
-/* eslint-disable class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, func-names, max-len */
+/* eslint-disable class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, func-names */
import $ from 'jquery';
import Sortable from 'sortablejs';
@@ -47,7 +47,10 @@ export default class LabelManager {
}
toggleEmptyState($label, $btn, action) {
- this.emptyState.classList.toggle('hidden', !!this.prioritizedLabels[0].querySelector(':scope > li'));
+ this.emptyState.classList.toggle(
+ 'hidden',
+ !!this.prioritizedLabels[0].querySelector(':scope > li'),
+ );
}
toggleLabelPriority($label, action, persistState) {
@@ -80,16 +83,14 @@ export default class LabelManager {
return;
}
if (action === 'remove') {
- axios.delete(url)
- .catch(rollbackLabelPosition);
+ axios.delete(url).catch(rollbackLabelPosition);
// Restore empty message
if (!$from.find('li').length) {
$from.find('.empty-message').removeClass('hidden');
}
} else {
- this.savePrioritySort($label, action)
- .catch(rollbackLabelPosition);
+ this.savePrioritySort($label, action).catch(rollbackLabelPosition);
}
}
@@ -102,8 +103,7 @@ export default class LabelManager {
}
onPrioritySortUpdate() {
- this.savePrioritySort()
- .catch(() => flash(this.errorMessage));
+ this.savePrioritySort().catch(() => flash(this.errorMessage));
}
savePrioritySort() {
diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js
index d85ae851706..2bc09237495 100644
--- a/app/assets/javascripts/labels.js
+++ b/app/assets/javascripts/labels.js
@@ -22,7 +22,7 @@ export default class Labels {
updateColorPreview() {
const previewColor = $('input#label_color').val();
return $('div.label-color-preview').css('background-color', previewColor);
- // Updates the the preview color with the hex-color input
+ // Updates the the preview color with the hex-color input
}
// Updates the preview color with a click on a suggested color
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 6499b919787..c0a76814102 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, dot-notation, no-empty */
+/* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, prefer-arrow-callback, one-var, no-unused-vars, prefer-template, no-new, consistent-return, object-shorthand, no-shadow, no-param-reassign, vars-on-top, no-lonely-if, no-else-return, dot-notation, no-empty */
/* global Issuable */
/* global ListLabel */
@@ -11,6 +11,7 @@ import DropdownUtils from './filtered_search/dropdown_utils';
import CreateLabelDropdown from './create_label';
import flash from './flash';
import ModalStore from './boards/stores/modal_store';
+import boardsStore from './boards/stores/boards_store';
export default class LabelsSelect {
constructor(els, options = {}) {
@@ -24,16 +25,43 @@ export default class LabelsSelect {
}
$els.each(function(i, dropdown) {
- var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer;
+ var $block,
+ $colorPreview,
+ $dropdown,
+ $form,
+ $loading,
+ $selectbox,
+ $sidebarCollapsedValue,
+ $value,
+ abilityName,
+ defaultLabel,
+ enableLabelCreateButton,
+ issueURLSplit,
+ issueUpdateURL,
+ labelUrl,
+ namespacePath,
+ projectPath,
+ saveLabelData,
+ selectedLabel,
+ showAny,
+ showNo,
+ $sidebarLabelTooltip,
+ initialSelected,
+ $toggleText,
+ fieldName,
+ useId,
+ propertyName,
+ showMenuAbove,
+ $container,
+ $dropdownContainer;
$dropdown = $(dropdown);
$dropdownContainer = $dropdown.closest('.labels-filter');
$toggleText = $dropdown.find('.dropdown-toggle-text');
namespacePath = $dropdown.data('namespacePath');
projectPath = $dropdown.data('projectPath');
- labelUrl = $dropdown.data('labels');
issueUpdateURL = $dropdown.data('issueUpdate');
selectedLabel = $dropdown.data('selected');
- if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) {
+ if (selectedLabel != null && !$dropdown.hasClass('js-multiselect')) {
selectedLabel = selectedLabel.split(',');
}
showNo = $dropdown.data('showNo');
@@ -49,26 +77,37 @@ export default class LabelsSelect {
$value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut();
fieldName = $dropdown.data('fieldName');
- useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown');
+ useId = $dropdown.is(
+ '.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown',
+ );
propertyName = useId ? 'id' : 'title';
initialSelected = $selectbox
.find('input[name="' + $dropdown.data('fieldName') + '"]')
- .map(function () {
+ .map(function() {
return this.value;
- }).get();
+ })
+ .get();
const { handleClick } = options;
$sidebarLabelTooltip.tooltip();
if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) {
- new CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), namespacePath, projectPath);
+ new CreateLabelDropdown(
+ $dropdown.closest('.dropdown').find('.dropdown-new-label'),
+ namespacePath,
+ projectPath,
+ );
}
saveLabelData = function() {
var data, selected;
- selected = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "']").map(function() {
- return this.value;
- }).get();
+ selected = $dropdown
+ .closest('.selectbox')
+ .find("input[name='" + fieldName + "']")
+ .map(function() {
+ return this.value;
+ })
+ .get();
if (_.isEqual(initialSelected, selected)) return;
initialSelected = selected;
@@ -81,7 +120,8 @@ export default class LabelsSelect {
}
$loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown');
- axios.put(issueUpdateURL, data)
+ axios
+ .put(issueUpdateURL, data)
.then(({ data }) => {
var labelCount, template, labelTooltipTitle, labelTitles, formattedLabels;
$loading.fadeOut();
@@ -95,8 +135,7 @@ export default class LabelsSelect {
issueUpdateURL,
});
labelCount = data.labels.length;
- }
- else {
+ } else {
template = '<span class="no-value">None</span>';
}
$value.removeAttr('style').html(template);
@@ -113,17 +152,14 @@ export default class LabelsSelect {
}
labelTooltipTitle = labelTitles.join(', ');
- }
- else {
+ } else {
labelTooltipTitle = __('Labels');
}
- $sidebarLabelTooltip
- .attr('title', labelTooltipTitle)
- .tooltip('_fixTitle');
+ $sidebarLabelTooltip.attr('title', labelTooltipTitle).tooltip('_fixTitle');
$('.has-tooltip', $value).tooltip({
- container: 'body'
+ container: 'body',
});
})
.catch(() => flash(__('Error saving label update.')));
@@ -131,34 +167,39 @@ export default class LabelsSelect {
$dropdown.glDropdown({
showMenuAbove: showMenuAbove,
data: function(term, callback) {
- axios.get(labelUrl)
- .then((res) => {
- let data = _.chain(res.data).groupBy(function(label) {
- return label.title;
- }).map(function(label) {
- var color;
- color = _.map(label, function(dup) {
- return dup.color;
- });
- return {
- id: label[0].id,
- title: label[0].title,
- color: color,
- duplicate: color.length > 1
- };
- }).value();
+ labelUrl = $dropdown.attr('data-labels');
+ axios
+ .get(labelUrl)
+ .then(res => {
+ let data = _.chain(res.data)
+ .groupBy(function(label) {
+ return label.title;
+ })
+ .map(function(label) {
+ var color;
+ color = _.map(label, function(dup) {
+ return dup.color;
+ });
+ return {
+ id: label[0].id,
+ title: label[0].title,
+ color: color,
+ duplicate: color.length > 1,
+ };
+ })
+ .value();
if ($dropdown.hasClass('js-extra-options')) {
var extraData = [];
if (showNo) {
extraData.unshift({
id: 0,
- title: 'No Label'
+ title: 'No Label',
});
}
if (showAny) {
extraData.unshift({
isAny: true,
- title: 'Any Label'
+ title: 'Any Label',
});
}
if (extraData.length) {
@@ -175,11 +216,22 @@ export default class LabelsSelect {
.catch(() => flash(__('Error fetching labels.')));
},
renderRow: function(label, instance) {
- var $a, $li, color, colorEl, indeterminate, removesAll, selectedClass, spacing, i, marked, dropdownName, dropdownValue;
+ var $a,
+ $li,
+ color,
+ colorEl,
+ indeterminate,
+ removesAll,
+ selectedClass,
+ spacing,
+ i,
+ marked,
+ dropdownName,
+ dropdownValue;
$li = $('<li>');
$a = $('<a href="#">');
selectedClass = [];
- removesAll = label.id <= 0 || (label.id == null);
+ removesAll = label.id <= 0 || label.id == null;
if ($dropdown.hasClass('js-filter-bulk-update')) {
indeterminate = $dropdown.data('indeterminate') || [];
marked = $dropdown.data('marked') || [];
@@ -199,9 +251,19 @@ export default class LabelsSelect {
} else {
if (this.id(label)) {
dropdownName = $dropdown.data('fieldName');
- dropdownValue = this.id(label).toString().replace(/'/g, '\\\'');
-
- if ($form.find("input[type='hidden'][name='" + dropdownName + "'][value='" + dropdownValue + "']").length) {
+ dropdownValue = this.id(label)
+ .toString()
+ .replace(/'/g, "\\'");
+
+ if (
+ $form.find(
+ "input[type='hidden'][name='" +
+ dropdownName +
+ "'][value='" +
+ dropdownValue +
+ "']",
+ ).length
+ ) {
selectedClass.push('is-active');
}
}
@@ -212,16 +274,14 @@ export default class LabelsSelect {
}
if (label.duplicate) {
color = DropdownUtils.duplicateLabelColor(label.color);
- }
- else {
+ } else {
if (label.color != null) {
[color] = label.color;
}
}
if (color) {
colorEl = "<span class='dropdown-label-box' style='background: " + color + "'></span>";
- }
- else {
+ } else {
colorEl = '';
}
// We need to identify which items are actually labels
@@ -234,7 +294,7 @@ export default class LabelsSelect {
return $li.html($a).prop('outerHTML');
},
search: {
- fields: ['title']
+ fields: ['title'],
},
selectable: true,
filterable: true,
@@ -254,25 +314,21 @@ export default class LabelsSelect {
if (selected && selected.id === 0) {
this.selected = [];
return 'No Label';
- }
- else if (isSelected) {
+ } else if (isSelected) {
this.selected.push(title);
- }
- else if (!isSelected && title) {
+ } else if (!isSelected && title) {
var index = this.selected.indexOf(title);
this.selected.splice(index, 1);
}
if (selectedLabels.length === 1) {
return selectedLabels;
- }
- else if (selectedLabels.length) {
+ } else if (selectedLabels.length) {
return sprintf(__('%{firstLabel} +%{labelCount} more'), {
firstLabel: selectedLabels[0],
- labelCount: selectedLabels.length - 1
+ labelCount: selectedLabels.length - 1,
});
- }
- else {
+ } else {
return defaultLabel;
}
},
@@ -284,10 +340,9 @@ export default class LabelsSelect {
return label.id;
}
- if ($dropdown.hasClass("js-filter-submit") && (label.isAny == null)) {
+ if ($dropdown.hasClass('js-filter-submit') && label.isAny == null) {
return label.title;
- }
- else {
+ } else {
return label.id;
}
},
@@ -309,13 +364,13 @@ export default class LabelsSelect {
}
if ($dropdown.hasClass('js-multiselect')) {
if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
- selectedLabels = $dropdown.closest('form').find("input:hidden[name='" + ($dropdown.data('fieldName')) + "']");
+ selectedLabels = $dropdown
+ .closest('form')
+ .find("input:hidden[name='" + $dropdown.data('fieldName') + "']");
Issuable.filterResults($dropdown.closest('form'));
- }
- else if ($dropdown.hasClass('js-filter-submit')) {
+ } else if ($dropdown.hasClass('js-filter-submit')) {
$dropdown.closest('form').submit();
- }
- else {
+ } else {
if (!$dropdown.hasClass('js-filter-bulk-update')) {
saveLabelData();
}
@@ -324,7 +379,7 @@ export default class LabelsSelect {
},
multiSelect: $dropdown.hasClass('js-multiselect'),
vue: $dropdown.hasClass('js-issue-board-sidebar'),
- clicked: function (clickEvent) {
+ clicked: function(clickEvent) {
const { $el, e, isMarking } = clickEvent;
const label = clickEvent.selectedObj;
@@ -338,7 +393,8 @@ export default class LabelsSelect {
isMRIndex = page === 'projects:merge_requests:index';
if ($dropdown.parent().find('.is-active:not(.dropdown-clear-active)').length) {
- $dropdown.parent()
+ $dropdown
+ .parent()
.find('.dropdown-clear-active')
.removeClass('is-active');
}
@@ -366,48 +422,43 @@ export default class LabelsSelect {
e.preventDefault();
return;
- }
- else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
+ } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
if (!$dropdown.hasClass('js-multiselect')) {
selectedLabel = label.title;
return Issuable.filterResults($dropdown.closest('form'));
}
- }
- else if ($dropdown.hasClass('js-filter-submit')) {
+ } else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
- }
- else if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
if ($el.hasClass('is-active')) {
- gl.issueBoards.BoardsStore.detail.issue.labels.push(new ListLabel({
- id: label.id,
- title: label.title,
- color: label.color[0],
- textColor: '#fff'
- }));
- }
- else {
- var { labels } = gl.issueBoards.BoardsStore.detail.issue;
- labels = labels.filter(function (selectedLabel) {
+ boardsStore.detail.issue.labels.push(
+ new ListLabel({
+ id: label.id,
+ title: label.title,
+ color: label.color[0],
+ textColor: '#fff',
+ }),
+ );
+ } else {
+ var { labels } = boardsStore.detail.issue;
+ labels = labels.filter(function(selectedLabel) {
return selectedLabel.id !== label.id;
});
- gl.issueBoards.BoardsStore.detail.issue.labels = labels;
+ boardsStore.detail.issue.labels = labels;
}
$loading.fadeIn();
- gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
+ boardsStore.detail.issue
+ .update($dropdown.attr('data-issue-update'))
.then(fadeOutLoader)
.catch(fadeOutLoader);
- }
- else if (handleClick) {
+ } else if (handleClick) {
e.preventDefault();
handleClick(label);
- }
- else {
+ } else {
if ($dropdown.hasClass('js-multiselect')) {
-
- }
- else {
+ } else {
return saveLabelData();
}
}
@@ -435,25 +486,27 @@ export default class LabelsSelect {
// so best approach is to use traditional way of
// concatenation
// see: http://2ality.com/2016/05/template-literal-whitespace.html#joining-arrays
- const tpl = _.template([
- '<% _.each(labels, function(label){ %>',
- '<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">',
- '<span class="badge label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">',
- '<%- label.title %>',
- '</span>',
- '</a>',
- '<% }); %>',
- ].join(''));
+ const tpl = _.template(
+ [
+ '<% _.each(labels, function(label){ %>',
+ '<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">',
+ '<span class="badge label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">',
+ '<%- label.title %>',
+ '</span>',
+ '</a>',
+ '<% }); %>',
+ ].join(''),
+ );
return tpl(tplData);
}
bindEvents() {
- return $('body').on('change', '.selected_issue', this.onSelectCheckboxIssue);
+ return $('body').on('change', '.selected-issuable', this.onSelectCheckboxIssue);
}
// eslint-disable-next-line class-methods-use-this
onSelectCheckboxIssue() {
- if ($('.selected_issue:checked').length) {
+ if ($('.selected-issuable:checked').length) {
return;
}
return $('.issues-bulk-update .labels-filter .dropdown-toggle-text').text('Label');
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index e3177188772..b8c3c237eb3 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -5,7 +5,9 @@ import initFlyOutNav from './fly_out_nav';
function hideEndFade($scrollingTabs) {
$scrollingTabs.each(function scrollTabsLoop() {
const $this = $(this);
- $this.siblings('.fade-right').toggleClass('scrolling', Math.round($this.width()) < $this.prop('scrollWidth'));
+ $this
+ .siblings('.fade-right')
+ .toggleClass('scrolling', Math.round($this.width()) < $this.prop('scrollWidth'));
});
}
@@ -15,38 +17,42 @@ export default function initLayoutNav() {
initFlyOutNav();
- $(document).on('init.scrolling-tabs', () => {
- const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized');
- $scrollingTabs.addClass('is-initialized');
+ $(document)
+ .on('init.scrolling-tabs', () => {
+ const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized');
+ $scrollingTabs.addClass('is-initialized');
- $(window).on('resize.nav', () => {
- hideEndFade($scrollingTabs);
- }).trigger('resize.nav');
+ $(window)
+ .on('resize.nav', () => {
+ hideEndFade($scrollingTabs);
+ })
+ .trigger('resize.nav');
- $scrollingTabs.on('scroll', function tabsScrollEvent() {
- const $this = $(this);
- const currentPosition = $this.scrollLeft();
- const maxPosition = $this.prop('scrollWidth') - $this.outerWidth();
+ $scrollingTabs.on('scroll', function tabsScrollEvent() {
+ const $this = $(this);
+ const currentPosition = $this.scrollLeft();
+ const maxPosition = $this.prop('scrollWidth') - $this.outerWidth();
- $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0);
- $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1);
- });
+ $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0);
+ $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1);
+ });
- $scrollingTabs.each(function scrollTabsEachLoop() {
- const $this = $(this);
- const scrollingTabWidth = $this.width();
- const $active = $this.find('.active');
- const activeWidth = $active.width();
+ $scrollingTabs.each(function scrollTabsEachLoop() {
+ const $this = $(this);
+ const scrollingTabWidth = $this.width();
+ const $active = $this.find('.active');
+ const activeWidth = $active.width();
- if ($active.length) {
- const offset = $active.offset().left + activeWidth;
+ if ($active.length) {
+ const offset = $active.offset().left + activeWidth;
- if (offset > scrollingTabWidth - 30) {
- const scrollLeft = (offset - (scrollingTabWidth / 2)) - (activeWidth / 2);
+ if (offset > scrollingTabWidth - 30) {
+ const scrollLeft = offset - scrollingTabWidth / 2 - activeWidth / 2;
- $this.scrollLeft(scrollLeft);
+ $this.scrollLeft(scrollLeft);
+ }
}
- }
- });
- }).trigger('init.scrolling-tabs');
+ });
+ })
+ .trigger('init.scrolling-tabs');
}
diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js
index bd2212edec7..61b4862b4e3 100644
--- a/app/assets/javascripts/lazy_loader.js
+++ b/app/assets/javascripts/lazy_loader.js
@@ -2,54 +2,114 @@ import _ from 'underscore';
export const placeholderImage =
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
-const SCROLL_THRESHOLD = 300;
+const SCROLL_THRESHOLD = 500;
export default class LazyLoader {
constructor(options = {}) {
+ this.intersectionObserver = null;
this.lazyImages = [];
this.observerNode = options.observerNode || '#content-body';
- const throttledScrollCheck = _.throttle(() => this.scrollCheck(), 300);
- const debouncedElementsInView = _.debounce(() => this.checkElementsInView(), 300);
-
- window.addEventListener('scroll', throttledScrollCheck);
- window.addEventListener('resize', debouncedElementsInView);
-
const scrollContainer = options.scrollContainer || window;
- scrollContainer.addEventListener('load', () => this.loadCheck());
+ scrollContainer.addEventListener('load', () => this.register());
+ }
+
+ static supportsIntersectionObserver() {
+ return 'IntersectionObserver' in window;
}
+
searchLazyImages() {
- const that = this;
requestIdleCallback(
() => {
- that.lazyImages = [].slice.call(document.querySelectorAll('.lazy'));
+ const lazyImages = [].slice.call(document.querySelectorAll('.lazy'));
- if (that.lazyImages.length) {
- that.checkElementsInView();
+ if (LazyLoader.supportsIntersectionObserver()) {
+ if (this.intersectionObserver) {
+ lazyImages.forEach(img => this.intersectionObserver.observe(img));
+ }
+ } else if (lazyImages.length) {
+ this.lazyImages = lazyImages;
+ this.checkElementsInView();
}
},
{ timeout: 500 },
);
}
+
startContentObserver() {
const contentNode = document.querySelector(this.observerNode) || document.querySelector('body');
-
if (contentNode) {
- const observer = new MutationObserver(() => this.searchLazyImages());
+ this.mutationObserver = new MutationObserver(() => this.searchLazyImages());
- observer.observe(contentNode, {
+ this.mutationObserver.observe(contentNode, {
childList: true,
subtree: true,
});
}
}
- loadCheck() {
- this.searchLazyImages();
+
+ stopContentObserver() {
+ if (this.mutationObserver) {
+ this.mutationObserver.takeRecords();
+ this.mutationObserver.disconnect();
+ this.mutationObserver = null;
+ }
+ }
+
+ unregister() {
+ this.stopContentObserver();
+ if (this.intersectionObserver) {
+ this.intersectionObserver.takeRecords();
+ this.intersectionObserver.disconnect();
+ this.intersectionObserver = null;
+ }
+ if (this.throttledScrollCheck) {
+ window.removeEventListener('scroll', this.throttledScrollCheck);
+ }
+ if (this.debouncedElementsInView) {
+ window.removeEventListener('resize', this.debouncedElementsInView);
+ }
+ }
+
+ register() {
+ if (LazyLoader.supportsIntersectionObserver()) {
+ this.startIntersectionObserver();
+ } else {
+ this.startLegacyObserver();
+ }
this.startContentObserver();
+ this.searchLazyImages();
}
+
+ startIntersectionObserver = () => {
+ this.throttledElementsInView = _.throttle(() => this.checkElementsInView(), 300);
+ this.intersectionObserver = new IntersectionObserver(this.onIntersection, {
+ rootMargin: `${SCROLL_THRESHOLD}px 0px`,
+ thresholds: 0.1,
+ });
+ };
+
+ onIntersection = entries => {
+ entries.forEach(entry => {
+ if (entry.isIntersecting) {
+ this.intersectionObserver.unobserve(entry.target);
+ this.lazyImages.push(entry.target);
+ }
+ });
+ this.throttledElementsInView();
+ };
+
+ startLegacyObserver() {
+ this.throttledScrollCheck = _.throttle(() => this.scrollCheck(), 300);
+ this.debouncedElementsInView = _.debounce(() => this.checkElementsInView(), 300);
+ window.addEventListener('scroll', this.throttledScrollCheck);
+ window.addEventListener('resize', this.debouncedElementsInView);
+ }
+
scrollCheck() {
requestAnimationFrame(() => this.checkElementsInView());
}
+
checkElementsInView() {
const scrollTop = window.pageYOffset;
const visHeight = scrollTop + window.innerHeight + SCROLL_THRESHOLD;
@@ -61,18 +121,29 @@ export default class LazyLoader {
const imgTop = scrollTop + imgBoundRect.top;
const imgBound = imgTop + imgBoundRect.height;
- if (scrollTop < imgBound && visHeight > imgTop) {
+ if (scrollTop <= imgBound && visHeight >= imgTop) {
requestAnimationFrame(() => {
LazyLoader.loadImage(selectedImage);
});
return false;
}
+ /*
+ If we are scrolling fast, the img we watched intersecting could have left the view port.
+ So we are going watch for new intersections.
+ */
+ if (LazyLoader.supportsIntersectionObserver()) {
+ if (this.intersectionObserver) {
+ this.intersectionObserver.observe(selectedImage);
+ }
+ return false;
+ }
return true;
}
return false;
});
}
+
static loadImage(img) {
if (img.getAttribute('data-src')) {
let imgUrl = img.getAttribute('data-src');
diff --git a/app/assets/javascripts/lib/ace.js b/app/assets/javascripts/lib/ace.js
index 9cdc0309503..e90b3d2eec7 100644
--- a/app/assets/javascripts/lib/ace.js
+++ b/app/assets/javascripts/lib/ace.js
@@ -1,3 +1,4 @@
/*= require ace/ace */
+/*= require ace/ext-modelist */
/*= require ace/ext-searchbox */
/*= require ./ace/ace_config_paths */
diff --git a/app/assets/javascripts/lib/utils/ace_utils.js b/app/assets/javascripts/lib/utils/ace_utils.js
new file mode 100644
index 00000000000..ee71ae0e61a
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/ace_utils.js
@@ -0,0 +1,6 @@
+/* global ace */
+
+export default function getModeByFileExtension(path) {
+ const modelist = ace.require('ace/ext/modelist');
+ return modelist.getModeForPath(path).mode;
+}
diff --git a/app/assets/javascripts/lib/utils/ajax_cache.js b/app/assets/javascripts/lib/utils/ajax_cache.js
index 616d8952ada..2d976dbdbbe 100644
--- a/app/assets/javascripts/lib/utils/ajax_cache.js
+++ b/app/assets/javascripts/lib/utils/ajax_cache.js
@@ -4,7 +4,7 @@ import Cache from './cache';
class AjaxCache extends Cache {
constructor() {
super();
- this.pendingRequests = { };
+ this.pendingRequests = {};
}
override(endpoint, data) {
@@ -19,12 +19,13 @@ class AjaxCache extends Cache {
let pendingRequest = this.pendingRequests[endpoint];
if (!pendingRequest) {
- pendingRequest = axios.get(endpoint)
+ pendingRequest = axios
+ .get(endpoint)
.then(({ data }) => {
this.internalStorage[endpoint] = data;
delete this.pendingRequests[endpoint];
})
- .catch((e) => {
+ .catch(e => {
const error = new Error(`${endpoint}: ${e.message}`);
error.textStatus = e.message;
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js
index 792871e2ecf..69159e2d741 100644
--- a/app/assets/javascripts/lib/utils/axios_utils.js
+++ b/app/assets/javascripts/lib/utils/axios_utils.js
@@ -7,7 +7,7 @@ axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// Maintain a global counter for active requests
// see: spec/support/wait_for_requests.rb
-axios.interceptors.request.use((config) => {
+axios.interceptors.request.use(config => {
window.activeVueResources = window.activeVueResources || 0;
window.activeVueResources += 1;
@@ -15,15 +15,18 @@ axios.interceptors.request.use((config) => {
});
// Remove the global counter
-axios.interceptors.response.use((config) => {
- window.activeVueResources -= 1;
-
- return config;
-}, (e) => {
- window.activeVueResources -= 1;
-
- return Promise.reject(e);
-});
+axios.interceptors.response.use(
+ config => {
+ window.activeVueResources -= 1;
+
+ return config;
+ },
+ e => {
+ window.activeVueResources -= 1;
+
+ return Promise.reject(e);
+ },
+);
export default axios;
diff --git a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
index c28ed04f94f..a24c71aeab1 100644
--- a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
+++ b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
@@ -93,9 +93,13 @@ export default class LinkedTabs {
const newState = `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
- window.history.replaceState({
- url: newState,
- }, document.title, newState);
+ window.history.replaceState(
+ {
+ url: newState,
+ },
+ document.title,
+ newState,
+ );
return newState;
}
diff --git a/app/assets/javascripts/lib/utils/cache.js b/app/assets/javascripts/lib/utils/cache.js
index 596bd1e388a..45048145139 100644
--- a/app/assets/javascripts/lib/utils/cache.js
+++ b/app/assets/javascripts/lib/utils/cache.js
@@ -1,6 +1,6 @@
export default class Cache {
constructor() {
- this.internalStorage = { };
+ this.internalStorage = {};
}
get(key) {
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 2f3dd6f6cbc..e14fff7a610 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -56,7 +56,8 @@ export const rstrip = val => {
return val;
};
-export const updateTooltipTitle = ($tooltipEl, newTitle) => $tooltipEl.attr('title', newTitle).tooltip('_fixTitle');
+export const updateTooltipTitle = ($tooltipEl, newTitle) =>
+ $tooltipEl.attr('title', newTitle).tooltip('_fixTitle');
export const disableButtonIfEmptyField = (fieldSelector, buttonSelector, eventName = 'input') => {
const field = $(fieldSelector);
@@ -86,6 +87,8 @@ export const handleLocationHash = () => {
const fixedTabs = document.querySelector('.js-tabs-affix');
const fixedDiffStats = document.querySelector('.js-diff-files-changed');
const fixedNav = document.querySelector('.navbar-gitlab');
+ const performanceBar = document.querySelector('#js-peek');
+ const topPadding = 8;
let adjustment = 0;
if (fixedNav) adjustment -= fixedNav.offsetHeight;
@@ -102,6 +105,14 @@ export const handleLocationHash = () => {
adjustment -= fixedDiffStats.offsetHeight;
}
+ if (performanceBar) {
+ adjustment -= performanceBar.offsetHeight;
+ }
+
+ if (isInMRPage()) {
+ adjustment -= topPadding;
+ }
+
window.scrollBy(0, adjustment);
};
@@ -131,17 +142,43 @@ export const parseUrlPathname = url => {
return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : `/${parsedUrl.pathname}`;
};
-// We can trust that each param has one & since values containing & will be encoded
-// Remove the first character of search as it is always ?
-export const getUrlParamsArray = () =>
- window.location.search
- .slice(1)
- .split('&')
+const splitPath = (path = '') => path.replace(/^\?/, '').split('&');
+
+export const urlParamsToArray = (path = '') =>
+ splitPath(path)
+ .filter(param => param.length > 0)
.map(param => {
const split = param.split('=');
return [decodeURI(split[0]), split[1]].join('=');
});
+export const getUrlParamsArray = () => urlParamsToArray(window.location.search);
+
+export const urlParamsToObject = (path = '') =>
+ splitPath(path).reduce((dataParam, filterParam) => {
+ if (filterParam === '') {
+ return dataParam;
+ }
+
+ const data = dataParam;
+ let [key, value] = filterParam.split('=');
+ const isArray = key.includes('[]');
+ key = key.replace('[]', '');
+ value = decodeURIComponent(value.replace(/\+/g, ' '));
+
+ if (isArray) {
+ if (!data[key]) {
+ data[key] = [];
+ }
+
+ data[key].push(value);
+ } else {
+ data[key] = value;
+ }
+
+ return data;
+ }, {});
+
export const isMetaKey = e => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
// Identify following special clicks
@@ -189,7 +226,7 @@ export const getParameterByName = (name, urlToParse) => {
return decodeURIComponent(results[2].replace(/\+/g, ' '));
};
-const handleSelectedRange = (range) => {
+const handleSelectedRange = range => {
const container = range.commonAncestorContainer;
// add context to fragment if needed
if (container.tagName === 'OL') {
@@ -349,8 +386,11 @@ export const objectToQueryString = (params = {}) =>
.map(param => `${param}=${params[param]}`)
.join('&');
-export const buildUrlWithCurrentLocation = param =>
- (param ? `${window.location.pathname}${param}` : window.location.pathname);
+export const buildUrlWithCurrentLocation = param => {
+ if (param) return `${window.location.pathname}${param}`;
+
+ return window.location.pathname;
+};
/**
* Based on the current location and the string parameters provided
@@ -426,7 +466,7 @@ export const backOff = (fn, timeout = 60000) => {
export const createOverlayIcon = (iconPath, overlayPath) => {
const faviconImage = document.createElement('img');
- return new Promise((resolve) => {
+ return new Promise(resolve => {
faviconImage.onload = () => {
const size = 32;
@@ -437,13 +477,29 @@ export const createOverlayIcon = (iconPath, overlayPath) => {
const context = canvas.getContext('2d');
context.clearRect(0, 0, size, size);
context.drawImage(
- faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size,
+ faviconImage,
+ 0,
+ 0,
+ faviconImage.width,
+ faviconImage.height,
+ 0,
+ 0,
+ size,
+ size,
);
const overlayImage = document.createElement('img');
overlayImage.onload = () => {
context.drawImage(
- overlayImage, 0, 0, overlayImage.width, overlayImage.height, 0, 0, size, size,
+ overlayImage,
+ 0,
+ 0,
+ overlayImage.width,
+ overlayImage.height,
+ 0,
+ 0,
+ size,
+ size,
);
const faviconWithOverlayUrl = canvas.toDataURL();
@@ -456,17 +512,21 @@ export const createOverlayIcon = (iconPath, overlayPath) => {
});
};
-export const setFaviconOverlay = (overlayPath) => {
+export const setFaviconOverlay = overlayPath => {
const faviconEl = document.getElementById('favicon');
- if (!faviconEl) { return null; }
+ if (!faviconEl) {
+ return null;
+ }
const iconPath = faviconEl.getAttribute('data-original-href');
- return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl => faviconEl.setAttribute('href', faviconWithOverlayUrl));
+ return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl =>
+ faviconEl.setAttribute('href', faviconWithOverlayUrl),
+ );
};
-export const setFavicon = (faviconPath) => {
+export const setFavicon = faviconPath => {
const faviconEl = document.getElementById('favicon');
if (faviconEl && faviconPath) {
faviconEl.setAttribute('href', faviconPath);
@@ -491,7 +551,10 @@ export const setCiStatusFavicon = pageUrl =>
}
return resetFavicon();
})
- .catch(resetFavicon);
+ .catch(error => {
+ resetFavicon();
+ throw error;
+ });
export const spriteIcon = (icon, className = '') => {
const classAttribute = className.length > 0 ? `class="${className}"` : '';
@@ -561,6 +624,17 @@ export const roundOffFloat = (number, precision = 0) => {
return Math.round(number * multiplier) / multiplier;
};
+/**
+ * Represents navigation type constants of the Performance Navigation API.
+ * Detailed explanation see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigation.
+ */
+export const NavigationType = {
+ TYPE_NAVIGATE: 0,
+ TYPE_RELOAD: 1,
+ TYPE_BACK_FORWARD: 2,
+ TYPE_RESERVED: 255,
+};
+
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/lib/utils/datefix.js b/app/assets/javascripts/lib/utils/datefix.js
deleted file mode 100644
index e98c9068367..00000000000
--- a/app/assets/javascripts/lib/utils/datefix.js
+++ /dev/null
@@ -1,29 +0,0 @@
-
-export const pad = (val, len = 2) => (`0${val}`).slice(-len);
-
-/**
- * Formats dates in Pickaday
- * @param {String} dateString Date in yyyy-mm-dd format
- * @return {Date} UTC format
- */
-export const parsePikadayDate = (dateString) => {
- const parts = dateString.split('-');
- const year = parseInt(parts[0], 10);
- const month = parseInt(parts[1] - 1, 10);
- const day = parseInt(parts[2], 10);
-
- return new Date(year, month, day);
-};
-
-/**
- * Used `onSelect` method in pickaday
- * @param {Date} date UTC format
- * @return {String} Date formated in yyyy-mm-dd
- */
-export const pikadayToString = (date) => {
- const day = pad(date.getDate());
- const month = pad(date.getMonth() + 1);
- const year = date.getFullYear();
-
- return `${year}-${month}-${day}`;
-};
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 1f66fa811ea..59007d5950e 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -1,4 +1,5 @@
import $ from 'jquery';
+import _ from 'underscore';
import timeago from 'timeago.js';
import dateFormat from 'dateformat';
import { pluralize } from './text_utility';
@@ -13,7 +14,7 @@ window.timeago = timeago;
*
* @param {Boolean} abbreviated
*/
-const getMonthNames = abbreviated => {
+export const getMonthNames = abbreviated => {
if (abbreviated) {
return [
s__('Jan'),
@@ -46,6 +47,8 @@ const getMonthNames = abbreviated => {
];
};
+export const pad = (val, len = 2) => `0${val}`.slice(-len);
+
/**
* Given a date object returns the day of the week in English
* @param {date} date
@@ -74,10 +77,10 @@ let timeagoInstance;
/**
* Sets a timeago Instance
*/
-export function getTimeago() {
+export const getTimeago = () => {
if (!timeagoInstance) {
- const localeRemaining = function getLocaleRemaining(number, index) {
- return [
+ const localeRemaining = (number, index) =>
+ [
[s__('Timeago|just now'), s__('Timeago|right now')],
[s__('Timeago|%s seconds ago'), s__('Timeago|%s seconds remaining')],
[s__('Timeago|1 minute ago'), s__('Timeago|1 minute remaining')],
@@ -93,9 +96,9 @@ export function getTimeago() {
[s__('Timeago|1 year ago'), s__('Timeago|1 year remaining')],
[s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')],
][index];
- };
- const locale = function getLocale(number, index) {
- return [
+
+ const locale = (number, index) =>
+ [
[s__('Timeago|just now'), s__('Timeago|right now')],
[s__('Timeago|%s seconds ago'), s__('Timeago|in %s seconds')],
[s__('Timeago|1 minute ago'), s__('Timeago|in 1 minute')],
@@ -111,7 +114,6 @@ export function getTimeago() {
[s__('Timeago|1 year ago'), s__('Timeago|in 1 year')],
[s__('Timeago|%s years ago'), s__('Timeago|in %s years')],
][index];
- };
timeago.register(timeagoLanguageCode, locale);
timeago.register(`${timeagoLanguageCode}-remaining`, localeRemaining);
@@ -119,7 +121,7 @@ export function getTimeago() {
}
return timeagoInstance;
-}
+};
/**
* For the given element, renders a timeago instance.
@@ -184,7 +186,7 @@ export const getDayDifference = (a, b) => {
* @param {Number} seconds
* @return {String}
*/
-export function timeIntervalInWords(intervalInSeconds) {
+export const timeIntervalInWords = intervalInSeconds => {
const secondsInteger = parseInt(intervalInSeconds, 10);
const minutes = Math.floor(secondsInteger / 60);
const seconds = secondsInteger - minutes * 60;
@@ -196,9 +198,9 @@ export function timeIntervalInWords(intervalInSeconds) {
text = `${seconds} ${pluralize('second', seconds)}`;
}
return text;
-}
+};
-export function dateInWords(date, abbreviated = false, hideYear = false) {
+export const dateInWords = (date, abbreviated = false, hideYear = false) => {
if (!date) return date;
const month = date.getMonth();
@@ -240,7 +242,7 @@ export function dateInWords(date, abbreviated = false, hideYear = false) {
}
return `${monthName} ${date.getDate()}, ${year}`;
-}
+};
/**
* Returns month name based on provided date.
@@ -370,3 +372,124 @@ window.gl.utils = {
getTimeago,
localTimeAgo,
};
+
+/**
+ * Formats milliseconds as timestamp (e.g. 01:02:03).
+ * This takes durations longer than a day into account (e.g. two days would be 48:00:00).
+ *
+ * @param milliseconds
+ * @returns {string}
+ */
+export const formatTime = milliseconds => {
+ const remainingSeconds = Math.floor(milliseconds / 1000) % 60;
+ const remainingMinutes = Math.floor(milliseconds / 1000 / 60) % 60;
+ const remainingHours = Math.floor(milliseconds / 1000 / 60 / 60);
+ let formattedTime = '';
+ if (remainingHours < 10) formattedTime += '0';
+ formattedTime += `${remainingHours}:`;
+ if (remainingMinutes < 10) formattedTime += '0';
+ formattedTime += `${remainingMinutes}:`;
+ if (remainingSeconds < 10) formattedTime += '0';
+ formattedTime += remainingSeconds;
+ return formattedTime;
+};
+
+/**
+ * Formats dates in Pickaday
+ * @param {String} dateString Date in yyyy-mm-dd format
+ * @return {Date} UTC format
+ */
+export const parsePikadayDate = dateString => {
+ const parts = dateString.split('-');
+ const year = parseInt(parts[0], 10);
+ const month = parseInt(parts[1] - 1, 10);
+ const day = parseInt(parts[2], 10);
+
+ return new Date(year, month, day);
+};
+
+/**
+ * Used `onSelect` method in pickaday
+ * @param {Date} date UTC format
+ * @return {String} Date formated in yyyy-mm-dd
+ */
+export const pikadayToString = date => {
+ const day = pad(date.getDate());
+ const month = pad(date.getMonth() + 1);
+ const year = date.getFullYear();
+
+ return `${year}-${month}-${day}`;
+};
+
+/**
+ * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # }
+ * Seconds can be negative or positive, zero or non-zero. Can be configured for any day
+ * or week length.
+ */
+export const parseSeconds = (seconds, { daysPerWeek = 5, hoursPerDay = 8 } = {}) => {
+ const DAYS_PER_WEEK = daysPerWeek;
+ const HOURS_PER_DAY = hoursPerDay;
+ const MINUTES_PER_HOUR = 60;
+ const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR;
+ const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR;
+
+ const timePeriodConstraints = {
+ weeks: MINUTES_PER_WEEK,
+ days: MINUTES_PER_DAY,
+ hours: MINUTES_PER_HOUR,
+ minutes: 1,
+ };
+
+ let unorderedMinutes = Math.abs(seconds / MINUTES_PER_HOUR);
+
+ return _.mapObject(timePeriodConstraints, minutesPerPeriod => {
+ const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod);
+
+ unorderedMinutes -= periodCount * minutesPerPeriod;
+
+ return periodCount;
+ });
+};
+
+/**
+ * Accepts a timeObject (see parseSeconds) and returns a condensed string representation of it
+ * (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included.
+ * If the 'fullNameFormat' param is passed it returns a non condensed string eg '1 week 3 days'
+ */
+export const stringifyTime = (timeObject, fullNameFormat = false) => {
+ const reducedTime = _.reduce(
+ timeObject,
+ (memo, unitValue, unitName) => {
+ const isNonZero = !!unitValue;
+
+ if (fullNameFormat && isNonZero) {
+ // Remove traling 's' if unit value is singular
+ const formatedUnitName = unitValue > 1 ? unitName : unitName.replace(/s$/, '');
+ return `${memo} ${unitValue} ${formatedUnitName}`;
+ }
+
+ return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo;
+ },
+ '',
+ ).trim();
+ return reducedTime.length ? reducedTime : '0m';
+};
+
+/**
+ * Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns
+ * the first non-zero unit/value pair.
+ */
+export const abbreviateTime = timeStr =>
+ timeStr.split(' ').filter(unitStr => unitStr.charAt(0) !== '0')[0];
+
+/**
+ * Calculates the milliseconds between now and a given date string.
+ * The result cannot become negative.
+ *
+ * @param endDate date string that the time difference is calculated for
+ * @return {number} number of milliseconds remaining until the given date
+ */
+export const calculateRemainingMilliseconds = endDate => {
+ const remainingMilliseconds = new Date(endDate).getTime() - Date.now();
+ return Math.max(remainingMilliseconds, 0);
+};
diff --git a/app/assets/javascripts/lib/utils/logoutput_behaviours.js b/app/assets/javascripts/lib/utils/logoutput_behaviours.js
index 1bf99d935ef..41b57025cc9 100644
--- a/app/assets/javascripts/lib/utils/logoutput_behaviours.js
+++ b/app/assets/javascripts/lib/utils/logoutput_behaviours.js
@@ -1,5 +1,11 @@
import $ from 'jquery';
-import { canScroll, isScrolledToBottom, toggleDisableButton } from './scroll_utils';
+import {
+ canScroll,
+ isScrolledToBottom,
+ isScrolledToTop,
+ isScrolledToMiddle,
+ toggleDisableButton,
+} from './scroll_utils';
export default class LogOutputBehaviours {
constructor() {
@@ -12,18 +18,13 @@ export default class LogOutputBehaviours {
}
toggleScroll() {
- const $document = $(document);
- const currentPosition = $document.scrollTop();
- const scrollHeight = $document.height();
-
- const windowHeight = $(window).height();
if (canScroll()) {
- if (currentPosition > 0 && scrollHeight - currentPosition !== windowHeight) {
+ if (isScrolledToMiddle()) {
// User is in the middle of the log
toggleDisableButton(this.$scrollTopBtn, false);
toggleDisableButton(this.$scrollBottomBtn, false);
- } else if (currentPosition === 0) {
+ } else if (isScrolledToTop()) {
// User is at Top of Log
toggleDisableButton(this.$scrollTopBtn, true);
diff --git a/app/assets/javascripts/lib/utils/navigation_utility.js b/app/assets/javascripts/lib/utils/navigation_utility.js
new file mode 100644
index 00000000000..1579b225e44
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/navigation_utility.js
@@ -0,0 +1,15 @@
+import { visitUrl } from './url_utility';
+
+/**
+ * Helper function that finds the href of the fiven selector and updates the location.
+ *
+ * @param {String} selector
+ */
+export default function findAndFollowLink(selector) {
+ const element = document.querySelector(selector);
+ const link = element && element.getAttribute('href');
+
+ if (link) {
+ visitUrl(link);
+ }
+}
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index 305ad3e5e26..d93873e0214 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, consistent-return, prefer-arrow-callback, no-return-assign, object-shorthand, comma-dangle, max-len */
+/* eslint-disable func-names, no-var, consistent-return, prefer-arrow-callback, no-return-assign, object-shorthand */
function notificationGranted(message, opts, onclick) {
var notification;
@@ -8,7 +8,7 @@ function notificationGranted(message, opts, onclick) {
return notification.close();
}, 8000);
- return notification.onclick = onclick || notification.close;
+ return (notification.onclick = onclick || notification.close);
}
function notifyPermissions() {
@@ -21,7 +21,7 @@ function notifyMe(message, body, icon, onclick) {
var opts;
opts = {
body: body,
- icon: icon
+ icon: icon,
};
// Let's check if the browser supports notifications
if (!('Notification' in window)) {
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index afbab59055b..2ccc51c35f7 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -7,7 +7,7 @@ import { BYTES_IN_KIB } from './constants';
* * * Show 3 digits to the right
* * For 2 digits to the left of the decimal point and X digits to the right of it
* * * Show 2 digits to the right
-*/
+ */
export function formatRelevantDigits(number) {
let digitsLeft = '';
let relevantDigits = 0;
diff --git a/app/assets/javascripts/lib/utils/pretty_time.js b/app/assets/javascripts/lib/utils/pretty_time.js
deleted file mode 100644
index b1ffd797f7e..00000000000
--- a/app/assets/javascripts/lib/utils/pretty_time.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import _ from 'underscore';
-
-/*
- * TODO: Make these methods more configurable (e.g. stringifyTime condensed or
- * non-condensed, abbreviateTimelengths)
- * */
-
-/*
- * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # }
- * Seconds can be negative or positive, zero or non-zero. Can be configured for any day
- * or week length.
-*/
-
-export function parseSeconds(seconds, { daysPerWeek = 5, hoursPerDay = 8 } = {}) {
- const DAYS_PER_WEEK = daysPerWeek;
- const HOURS_PER_DAY = hoursPerDay;
- const MINUTES_PER_HOUR = 60;
- const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR;
- const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR;
-
- const timePeriodConstraints = {
- weeks: MINUTES_PER_WEEK,
- days: MINUTES_PER_DAY,
- hours: MINUTES_PER_HOUR,
- minutes: 1,
- };
-
- let unorderedMinutes = Math.abs(seconds / MINUTES_PER_HOUR);
-
- return _.mapObject(timePeriodConstraints, (minutesPerPeriod) => {
- const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod);
-
- unorderedMinutes -= (periodCount * minutesPerPeriod);
-
- return periodCount;
- });
-}
-
-/*
-* Accepts a timeObject (see parseSeconds) and returns a condensed string representation of it
-* (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included.
-*/
-
-export function stringifyTime(timeObject) {
- const reducedTime = _.reduce(timeObject, (memo, unitValue, unitName) => {
- const isNonZero = !!unitValue;
- return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo;
- }, '').trim();
- return reducedTime.length ? reducedTime : '0m';
-}
-
-/*
-* Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns
-* the first non-zero unit/value pair.
-*/
-
-export function abbreviateTime(timeStr) {
- return timeStr.split(' ')
- .filter(unitStr => unitStr.charAt(0) !== '0')[0];
-}
-
diff --git a/app/assets/javascripts/lib/utils/regexp.js b/app/assets/javascripts/lib/utils/regexp.js
index baa0b51d59b..25b60dcd14a 100644
--- a/app/assets/javascripts/lib/utils/regexp.js
+++ b/app/assets/javascripts/lib/utils/regexp.js
@@ -5,6 +5,7 @@
// Inspired by https://github.com/mishoo/UglifyJS/blob/2bc1d02363db3798d5df41fb5059a19edca9b7eb/lib/parse-js.js#L203
// Unicode 6.1
-const unicodeLetters = '\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0527\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0\\u08A2-\\u08AC\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0977\\u0979-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F0\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA697\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7F8-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA80-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC';
+const unicodeLetters =
+ '\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0527\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0\\u08A2-\\u08AC\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0977\\u0979-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F0\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA697\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7F8-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA80-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC';
export default { unicodeLetters };
diff --git a/app/assets/javascripts/lib/utils/scroll_utils.js b/app/assets/javascripts/lib/utils/scroll_utils.js
index 9313b570863..b4da1e16f08 100644
--- a/app/assets/javascripts/lib/utils/scroll_utils.js
+++ b/app/assets/javascripts/lib/utils/scroll_utils.js
@@ -4,6 +4,7 @@ export const canScroll = () => $(document).height() > $(window).height();
/**
* Checks if the entire page is scrolled down all the way to the bottom
+ * @returns {Boolean}
*/
export const isScrolledToBottom = () => {
const $document = $(document);
@@ -16,11 +17,34 @@ export const isScrolledToBottom = () => {
return scrollHeight - currentPosition === windowHeight;
};
+/**
+ * Checks if page is scrolled to the top
+ * @returns {Boolean}
+ */
+export const isScrolledToTop = () => $(document).scrollTop() === 0;
+
export const scrollDown = () => {
const $document = $(document);
$document.scrollTop($document.height());
};
+export const scrollUp = () => {
+ $(document).scrollTop(0);
+};
+
+/**
+ * Checks if scroll position is in the middle of the page
+ * @returns {Boolean}
+ */
+export const isScrolledToMiddle = () => {
+ const $document = $(document);
+ const currentPosition = $document.scrollTop();
+ const scrollHeight = $document.height();
+ const windowHeight = $(window).height();
+
+ return currentPosition > 0 && scrollHeight - currentPosition !== windowHeight;
+};
+
export const toggleDisableButton = ($button, disable) => {
if (disable && $button.prop('disabled')) return;
$button.prop('disabled', disable);
diff --git a/app/assets/javascripts/lib/utils/simple_poll.js b/app/assets/javascripts/lib/utils/simple_poll.js
index 25ca98afbe7..473f179ad86 100644
--- a/app/assets/javascripts/lib/utils/simple_poll.js
+++ b/app/assets/javascripts/lib/utils/simple_poll.js
@@ -2,7 +2,7 @@ export default (fn, interval = 2000, timeout = 60000) => {
const startTime = Date.now();
return new Promise((resolve, reject) => {
- const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg));
+ const stop = arg => (arg instanceof Error ? reject(arg) : resolve(arg));
const next = () => {
if (Date.now() - startTime < timeout) {
setTimeout(fn.bind(null, next, stop), interval);
diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js
index 15a4dd62012..f3244301350 100644
--- a/app/assets/javascripts/lib/utils/sticky.js
+++ b/app/assets/javascripts/lib/utils/sticky.js
@@ -24,7 +24,11 @@ export const isSticky = (el, scrollY, stickyTop, insertPlaceholder) => {
} else if (top > stickyTop && el.classList.contains('is-stuck')) {
el.classList.remove('is-stuck');
- if (insertPlaceholder && el.nextElementSibling && el.nextElementSibling.classList.contains('sticky-placeholder')) {
+ if (
+ insertPlaceholder &&
+ el.nextElementSibling &&
+ el.nextElementSibling.classList.contains('sticky-placeholder')
+ ) {
el.nextElementSibling.remove();
}
}
@@ -42,11 +46,19 @@ export const isSticky = (el, scrollY, stickyTop, insertPlaceholder) => {
export const stickyMonitor = (el, stickyTop, insertPlaceholder = true) => {
if (!el) return;
- if (typeof CSS === 'undefined' || !(CSS.supports('(position: -webkit-sticky) or (position: sticky)'))) return;
+ if (
+ typeof CSS === 'undefined' ||
+ !CSS.supports('(position: -webkit-sticky) or (position: sticky)')
+ )
+ return;
- document.addEventListener('scroll', () => isSticky(el, window.scrollY, stickyTop, insertPlaceholder), {
- passive: true,
- });
+ document.addEventListener(
+ 'scroll',
+ () => isSticky(el, window.scrollY, stickyTop, insertPlaceholder),
+ {
+ passive: true,
+ },
+ );
};
/**
@@ -55,6 +67,6 @@ export const stickyMonitor = (el, stickyTop, insertPlaceholder = true) => {
* - If the current environment supports `position: sticky`, do nothing.
* - Can receive an iterable element list (NodeList, jQuery collection, etc.) or single HTMLElement.
*/
-export const polyfillSticky = (el) => {
+export const polyfillSticky = el => {
StickyFill.add(el);
};
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index ce0bc4d40e9..3618c6af7e2 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -1,19 +1,27 @@
-/* eslint-disable func-names, no-var, no-param-reassign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, max-len, consistent-return, no-unused-vars, max-len */
+/* eslint-disable func-names, no-var, no-param-reassign, one-var, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, consistent-return, no-unused-vars */
import $ from 'jquery';
import { insertText } from '~/lib/utils/common_utils';
+const LINK_TAG_PATTERN = '[{text}](url)';
+
function selectedText(text, textarea) {
return text.substring(textarea.selectionStart, textarea.selectionEnd);
}
function lineBefore(text, textarea) {
var split;
- split = text.substring(0, textarea.selectionStart).trim().split('\n');
+ split = text
+ .substring(0, textarea.selectionStart)
+ .trim()
+ .split('\n');
return split[split.length - 1];
}
function lineAfter(text, textarea) {
- return text.substring(textarea.selectionEnd).trim().split('\n')[0];
+ return text
+ .substring(textarea.selectionEnd)
+ .trim()
+ .split('\n')[0];
}
function blockTagText(text, textArea, blockTag, selected) {
@@ -27,17 +35,23 @@ function blockTagText(text, textArea, blockTag, selected) {
}
return selected;
} else {
- return blockTag + "\n" + selected + "\n" + blockTag;
+ return blockTag + '\n' + selected + '\n' + blockTag;
}
}
-function moveCursor(textArea, tag, wrapped, removedLastNewLine) {
+function moveCursor({ textArea, tag, positionBetweenTags, removedLastNewLine, select }) {
var pos;
if (!textArea.setSelectionRange) {
return;
}
+ if (select && select.length > 0) {
+ // calculate the part of the text to be selected
+ const startPosition = textArea.selectionStart - (tag.length - tag.indexOf(select));
+ const endPosition = startPosition + select.length;
+ return textArea.setSelectionRange(startPosition, endPosition);
+ }
if (textArea.selectionStart === textArea.selectionEnd) {
- if (wrapped) {
+ if (positionBetweenTags) {
pos = textArea.selectionStart - tag.length;
} else {
pos = textArea.selectionStart;
@@ -51,12 +65,33 @@ function moveCursor(textArea, tag, wrapped, removedLastNewLine) {
}
}
-export function insertMarkdownText(textArea, text, tag, blockTag, selected, wrap) {
- var textToInsert, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
+export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wrap, select }) {
+ var textToInsert,
+ selectedSplit,
+ startChar,
+ removedLastNewLine,
+ removedFirstNewLine,
+ currentLineEmpty,
+ lastNewLine;
removedLastNewLine = false;
removedFirstNewLine = false;
currentLineEmpty = false;
+ // check for link pattern and selected text is an URL
+ // if so fill in the url part instead of the text part of the pattern.
+ if (tag === LINK_TAG_PATTERN) {
+ if (URL) {
+ try {
+ const ignoredUrl = new URL(selected);
+ // valid url
+ tag = '[text]({text})';
+ select = 'text';
+ } catch (e) {
+ // ignore - no valid url
+ }
+ }
+ }
+
// Remove the first newline
if (selected.indexOf('\n') === 0) {
removedFirstNewLine = true;
@@ -82,20 +117,29 @@ export function insertMarkdownText(textArea, text, tag, blockTag, selected, wrap
startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : '';
+ const textPlaceholder = '{text}';
+
if (selectedSplit.length > 1 && (!wrap || (blockTag != null && blockTag !== ''))) {
if (blockTag != null && blockTag !== '') {
textToInsert = blockTagText(text, textArea, blockTag, selected);
} else {
- textToInsert = selectedSplit.map(function(val) {
- if (val.indexOf(tag) === 0) {
- return "" + (val.replace(tag, ''));
- } else {
- return "" + tag + val;
- }
- }).join('\n');
+ textToInsert = selectedSplit
+ .map(function(val) {
+ if (tag.indexOf(textPlaceholder) > -1) {
+ return tag.replace(textPlaceholder, val);
+ }
+ if (val.indexOf(tag) === 0) {
+ return '' + val.replace(tag, '');
+ } else {
+ return '' + tag + val;
+ }
+ })
+ .join('\n');
}
+ } else if (tag.indexOf(textPlaceholder) > -1) {
+ textToInsert = tag.replace(textPlaceholder, selected);
} else {
- textToInsert = "" + startChar + tag + selected + (wrap ? tag : ' ');
+ textToInsert = '' + startChar + tag + selected + (wrap ? tag : ' ');
}
if (removedFirstNewLine) {
@@ -107,28 +151,38 @@ export function insertMarkdownText(textArea, text, tag, blockTag, selected, wrap
}
insertText(textArea, textToInsert);
- return moveCursor(textArea, tag, wrap, removedLastNewLine);
+ return moveCursor({
+ textArea,
+ tag: tag.replace(textPlaceholder, selected),
+ positionBetweenTags: wrap && selected.length === 0,
+ removedLastNewLine,
+ select,
+ });
}
-function updateText(textArea, tag, blockTag, wrap) {
+function updateText({ textArea, tag, blockTag, wrap, select }) {
var $textArea, selected, text;
$textArea = $(textArea);
textArea = $textArea.get(0);
text = $textArea.val();
selected = selectedText(text, textArea);
$textArea.focus();
- return insertMarkdownText(textArea, text, tag, blockTag, selected, wrap);
-}
-
-function replaceRange(s, start, end, substitute) {
- return s.substring(0, start) + substitute + s.substring(end);
+ return insertMarkdownText({ textArea, text, tag, blockTag, selected, wrap, select });
}
export function addMarkdownListeners(form) {
- return $('.js-md', form).off('click').on('click', function() {
- const $this = $(this);
- return updateText($this.closest('.md-area').find('textarea'), $this.data('mdTag'), $this.data('mdBlock'), !$this.data('mdPrepend'));
- });
+ return $('.js-md', form)
+ .off('click')
+ .on('click', function() {
+ const $this = $(this);
+ return updateText({
+ textArea: $this.closest('.md-area').find('textarea'),
+ tag: $this.data('mdTag'),
+ blockTag: $this.data('mdBlock'),
+ wrap: !$this.data('mdPrepend'),
+ select: $this.data('mdSelect'),
+ });
+ });
}
export function removeMarkdownListeners(form) {
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index 2be3c97bd95..7cc7cd6d20e 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -8,7 +8,7 @@
* @returns {String}
*/
export const addDelimiter = text =>
- (text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') : text);
+ text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') : text;
/**
* Returns '99+' for numbers bigger than 99.
@@ -49,6 +49,13 @@ export const dasherize = str => str.replace(/[_\s]+/g, '-');
export const slugify = str => str.trim().toLowerCase();
/**
+ * Replaces whitespaces with hyphens and converts to lower case
+ * @param {String} str
+ * @returns {String}
+ */
+export const slugifyWithHyphens = str => str.toLowerCase().replace(/\s+/g, '-');
+
+/**
* Truncates given text
*
* @param {String} string
@@ -84,9 +91,7 @@ export function capitalizeFirstCharacter(text) {
* @return {String}
*/
export function getFirstCharacterCapitalized(text) {
- return text
- ? text.charAt(0).toUpperCase()
- : '';
+ return text ? text.charAt(0).toUpperCase() : '';
}
/**
@@ -126,10 +131,9 @@ export const convertToSentenceCase = string => {
* e.g. HelloWorld => Hello World
*
* @param {*} string
-*/
-export const splitCamelCase = string => (
+ */
+export const splitCamelCase = string =>
string
- .replace(/([A-Z]+)([A-Z][a-z])/g, ' $1 $2')
- .replace(/([a-z\d])([A-Z])/g, '$1 $2')
- .trim()
-);
+ .replace(/([A-Z]+)([A-Z][a-z])/g, ' $1 $2')
+ .replace(/([a-z\d])([A-Z])/g, '$1 $2')
+ .trim();
diff --git a/app/assets/javascripts/lib/utils/tick_formats.js b/app/assets/javascripts/lib/utils/tick_formats.js
index 0c10a85e336..af3ca714400 100644
--- a/app/assets/javascripts/lib/utils/tick_formats.js
+++ b/app/assets/javascripts/lib/utils/tick_formats.js
@@ -26,7 +26,7 @@ initDateFormats();
see also https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#tickFormat
*/
-export const dateTickFormat = (date) => {
+export const dateTickFormat = date => {
if (date.getDate() !== 1) {
return dateTimeFormats.dayFormat.format(date);
}
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 72b72f4247d..a282c2df441 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -47,9 +47,9 @@ export function removeParamQueryString(url, param) {
return urlVariables.filter(variable => variable.indexOf(param) === -1).join('&');
}
-export function removeParams(params) {
+export function removeParams(params, source = window.location.href) {
const url = document.createElement('a');
- url.href = window.location.href;
+ url.href = source;
params.forEach(param => {
url.search = removeParamQueryString(url.search, param);
diff --git a/app/assets/javascripts/lib/utils/users_cache.js b/app/assets/javascripts/lib/utils/users_cache.js
index b01ec6b81a3..c0d45e017b4 100644
--- a/app/assets/javascripts/lib/utils/users_cache.js
+++ b/app/assets/javascripts/lib/utils/users_cache.js
@@ -7,21 +7,20 @@ class UsersCache extends Cache {
return Promise.resolve(this.get(username));
}
- return Api.users('', { username })
- .then(({ data }) => {
- if (!data.length) {
- throw new Error(`User "${username}" could not be found!`);
- }
+ return Api.users('', { username }).then(({ data }) => {
+ if (!data.length) {
+ throw new Error(`User "${username}" could not be found!`);
+ }
- if (data.length > 1) {
- throw new Error(`Expected username "${username}" to be unique!`);
- }
+ if (data.length > 1) {
+ throw new Error(`Expected username "${username}" to be unique!`);
+ }
- const user = data[0];
- this.internalStorage[username] = user;
- return user;
- });
- // missing catch is intentional, error handling depends on use case
+ const user = data[0];
+ this.internalStorage[username] = user;
+ return user;
+ });
+ // missing catch is intentional, error handling depends on use case
}
}
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index 291655235d5..4db63c841a9 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, no-underscore-dangle, no-param-reassign, prefer-template, quotes, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, no-else-return, max-len */
+/* eslint-disable func-names, no-var, no-underscore-dangle, no-param-reassign, prefer-template, consistent-return, one-var, no-else-return */
import $ from 'jquery';
@@ -70,7 +70,7 @@ LineHighlighter.prototype.highlightHash = function(newHash) {
const scrollOptions = {
// Scroll to the first highlighted line on initial load
// Offset -50 for the sticky top bar, and another -100 for some context
- offset: -150
+ offset: -150,
};
if (this.options.scrollFileHolder) {
$(this.options.fileHolderSelector).scrollTo(lineSelector, scrollOptions);
@@ -85,7 +85,9 @@ LineHighlighter.prototype.clickHandler = function(event) {
var current, lineNumber, range;
event.preventDefault();
this.clearHighlight();
- lineNumber = $(event.target).closest('a').data('lineNumber');
+ lineNumber = $(event.target)
+ .closest('a')
+ .data('lineNumber');
current = this.hashToRange(this._hash);
if (!(current[0] && event.shiftKey)) {
// If there's no current selection, or there is but Shift wasn't held,
@@ -104,7 +106,7 @@ LineHighlighter.prototype.clickHandler = function(event) {
};
LineHighlighter.prototype.clearHighlight = function() {
- return $("." + this.highlightLineClass).removeClass(this.highlightLineClass);
+ return $('.' + this.highlightLineClass).removeClass(this.highlightLineClass);
};
// Convert a URL hash String into line numbers
@@ -135,7 +137,7 @@ LineHighlighter.prototype.hashToRange = function(hash) {
//
// lineNumber - Line number to highlight
LineHighlighter.prototype.highlightLine = function(lineNumber) {
- return $("#LC" + lineNumber).addClass(this.highlightLineClass);
+ return $('#LC' + lineNumber).addClass(this.highlightLineClass);
};
// Highlight all lines within a range
@@ -160,9 +162,9 @@ LineHighlighter.prototype.highlightRange = function(range) {
LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
var hash;
if (lastLineNumber) {
- hash = "#L" + firstLineNumber + "-" + lastLineNumber;
+ hash = '#L' + firstLineNumber + '-' + lastLineNumber;
} else {
- hash = "#L" + firstLineNumber;
+ hash = '#L' + firstLineNumber;
}
this._hash = hash;
return this.__setLocationHash__(hash);
@@ -172,11 +174,15 @@ LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
//
// This method is stubbed in tests.
LineHighlighter.prototype.__setLocationHash__ = function(value) {
- return window.history.pushState({
- url: value
- // We're using pushState instead of assigning location.hash directly to
- // prevent the page from scrolling on the hashchange event
- }, document.title, value);
+ return window.history.pushState(
+ {
+ url: value,
+ // We're using pushState instead of assigning location.hash directly to
+ // prevent the page from scrolling on the hashchange event
+ },
+ document.title,
+ value,
+ );
};
export default LineHighlighter;
diff --git a/app/assets/javascripts/locale/ensure_single_line.js b/app/assets/javascripts/locale/ensure_single_line.js
new file mode 100644
index 00000000000..47c52fe6c50
--- /dev/null
+++ b/app/assets/javascripts/locale/ensure_single_line.js
@@ -0,0 +1,25 @@
+/* eslint-disable import/no-commonjs */
+
+const SPLIT_REGEX = /\s*[\r\n]+\s*/;
+
+/**
+ *
+ * strips newlines from strings and replaces them with a single space
+ *
+ * @example
+ *
+ * ensureSingleLine('foo \n bar') === 'foo bar'
+ *
+ * @param {String} str
+ * @returns {String}
+ */
+module.exports = function ensureSingleLine(str) {
+ // This guard makes the function significantly faster
+ if (str.includes('\n') || str.includes('\r')) {
+ return str
+ .split(SPLIT_REGEX)
+ .filter(s => s !== '')
+ .join(' ');
+ }
+ return str;
+};
diff --git a/app/assets/javascripts/locale/index.js b/app/assets/javascripts/locale/index.js
index 2cc5fb10027..1ae3362c4bc 100644
--- a/app/assets/javascripts/locale/index.js
+++ b/app/assets/javascripts/locale/index.js
@@ -1,4 +1,5 @@
import Jed from 'jed';
+import ensureSingleLine from './ensure_single_line';
import sprintf from './sprintf';
const languageCode = () => document.querySelector('html').getAttribute('lang') || 'en';
@@ -10,7 +11,7 @@ delete window.translations;
@param text The text to be translated
@returns {String} The translated text
*/
-const gettext = locale.gettext.bind(locale);
+const gettext = text => locale.gettext.bind(locale)(ensureSingleLine(text));
/**
Translate the text with a number
@@ -23,7 +24,10 @@ const gettext = locale.gettext.bind(locale);
@returns {String} Translated text with the number replaced (eg. '2 days')
*/
const ngettext = (text, pluralText, count) => {
- const translated = locale.ngettext(text, pluralText, count).replace(/%d/g, count).split('|');
+ const translated = locale
+ .ngettext(ensureSingleLine(text), ensureSingleLine(pluralText), count)
+ .replace(/%d/g, count)
+ .split('|');
return translated[translated.length - 1];
};
@@ -40,7 +44,7 @@ const ngettext = (text, pluralText, count) => {
@returns {String} Translated context based text
*/
const pgettext = (keyOrContext, key) => {
- const normalizedKey = key ? `${keyOrContext}|${key}` : keyOrContext;
+ const normalizedKey = ensureSingleLine(key ? `${keyOrContext}|${key}` : keyOrContext);
const translated = gettext(normalizedKey).split('|');
return translated[translated.length - 1];
@@ -52,8 +56,7 @@ const pgettext = (keyOrContext, key) => {
@param formatOptions for available options, please see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
@returns {Intl.DateTimeFormat}
*/
-const createDateTimeFormat =
- formatOptions => Intl.DateTimeFormat(languageCode(), formatOptions);
+const createDateTimeFormat = formatOptions => Intl.DateTimeFormat(languageCode(), formatOptions);
export { languageCode };
export { gettext as __ };
diff --git a/app/assets/javascripts/locale/sprintf.js b/app/assets/javascripts/locale/sprintf.js
index 599104dcfa0..5246c49842e 100644
--- a/app/assets/javascripts/locale/sprintf.js
+++ b/app/assets/javascripts/locale/sprintf.js
@@ -15,7 +15,7 @@ export default (input, parameters, escapeParameters = true) => {
let output = input;
if (parameters) {
- Object.keys(parameters).forEach((parameterName) => {
+ Object.keys(parameters).forEach(parameterName => {
const parameterValue = parameters[parameterName];
const escapedParameterValue = escapeParameters ? _.escape(parameterValue) : parameterValue;
output = output.replace(new RegExp(`%{${parameterName}}`, 'g'), escapedParameterValue);
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 2718f73a830..a88b575ad99 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -2,7 +2,6 @@
import jQuery from 'jquery';
import Cookies from 'js-cookie';
-import svg4everybody from 'svg4everybody';
// bootstrap webpack, common libs, polyfills, and behaviors
import './webpack';
@@ -25,10 +24,12 @@ import initLayoutNav from './layout_nav';
import './feature_highlight/feature_highlight_options';
import LazyLoader from './lazy_loader';
import initLogoAnimation from './logo';
-import './milestone_select';
import './frequent_items';
import initBreadcrumbs from './breadcrumb';
-import initDispatcher from './dispatcher';
+import initUsagePingConsent from './usage_ping_consent';
+import initPerformanceBar from './performance_bar';
+import initSearchAutocomplete from './search_autocomplete';
+import GlFieldErrors from './gl_field_errors';
// expose jQuery as global (TODO: remove these)
window.jQuery = jQuery;
@@ -40,8 +41,6 @@ if (process.env.NODE_ENV !== 'production' && gon && gon.test_env) {
import(/* webpackMode: "eager" */ './test_utils/');
}
-svg4everybody();
-
document.addEventListener('beforeunload', () => {
// Unbind scroll events
$(document).off('scroll');
@@ -78,6 +77,10 @@ document.addEventListener('DOMContentLoaded', () => {
initImporterStatus();
initTodoToggle();
initLogoAnimation();
+ initUsagePingConsent();
+
+ if (document.querySelector('.search')) initSearchAutocomplete();
+ if (document.querySelector('#js-peek')) initPerformanceBar({ container: '#js-peek' });
// Set the default path for all cookies to GitLab's root directory
Cookies.defaults.path = gon.relative_url_root || '/';
@@ -199,7 +202,6 @@ document.addEventListener('DOMContentLoaded', () => {
$('.navbar-toggler').on('click', () => {
$('.header-content').toggleClass('menu-expanded');
- gl.lazyLoader.loadCheck();
});
// Show/hide comments on diff
@@ -268,5 +270,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
- initDispatcher();
+ // initialize field errors
+ $('.gl-show-field-errors').each((i, form) => new GlFieldErrors(form));
});
diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js
index d27922a2099..0beedcacf33 100644
--- a/app/assets/javascripts/member_expiration_date.js
+++ b/app/assets/javascripts/member_expiration_date.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import Pikaday from 'pikaday';
-import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
+import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
// Add datepickers to all `js-access-expiration-date` elements. If those elements are
// children of an element with the `clearable-input` class, and have a sibling
@@ -9,7 +9,9 @@ import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
//
export default function memberExpirationDate(selector = '.js-access-expiration-date') {
function toggleClearInput() {
- $(this).closest('.clearable-input').toggleClass('has-value', $(this).val() !== '');
+ $(this)
+ .closest('.clearable-input')
+ .toggleClass('has-value', $(this).val() !== '');
}
const inputs = $(selector);
@@ -40,7 +42,9 @@ export default function memberExpirationDate(selector = '.js-access-expiration-d
inputs.next('.js-clear-input').on('click', function clicked(event) {
event.preventDefault();
- const input = $(this).closest('.clearable-input').find(selector);
+ const input = $(this)
+ .closest('.clearable-input')
+ .find(selector);
const calendar = input.data('pikaday');
calendar.setDate(null);
diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js
index 7d0c701fd70..bd263c75a3d 100644
--- a/app/assets/javascripts/members.js
+++ b/app/assets/javascripts/members.js
@@ -7,8 +7,12 @@ export default class Members {
}
addListeners() {
- $('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this));
- $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this));
+ $('.js-member-update-control')
+ .off('change')
+ .on('change', this.formSubmit.bind(this));
+ $('.js-edit-member-form')
+ .off('ajax:success')
+ .on('ajax:success', this.formSuccess.bind(this));
gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
}
@@ -28,7 +32,7 @@ export default class Members {
toggleLabel(selected, $el) {
return $el.text();
},
- clicked: (options) => {
+ clicked: options => {
this.formSubmit(null, options.$el);
},
});
diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
index 81950515ab4..a62ebe23646 100644
--- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
+++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
@@ -1,12 +1,13 @@
-/* eslint-disable comma-dangle, quote-props, no-useless-computed-key, object-shorthand, no-param-reassign, max-len */
+/* eslint-disable no-useless-computed-key, object-shorthand, no-param-reassign */
/* global ace */
import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
+import getModeByFileExtension from '~/lib/utils/ace_utils';
-((global) => {
+(global => {
global.mergeConflicts = global.mergeConflicts || {};
global.mergeConflicts.diffFileEditor = Vue.extend({
@@ -35,10 +36,10 @@ import { __ } from '~/locale';
computed: {
classObject() {
return {
- 'saved': this.saved,
- 'is-loading': this.loading
+ saved: this.saved,
+ 'is-loading': this.loading,
};
- }
+ },
},
watch: {
['file.showEditor'](val) {
@@ -49,7 +50,7 @@ import { __ } from '~/locale';
}
this.loadEditor();
- }
+ },
},
mounted() {
if (this.file.loadEditor) {
@@ -60,7 +61,8 @@ import { __ } from '~/locale';
loadEditor() {
this.loading = true;
- axios.get(this.file.content_path)
+ axios
+ .get(this.file.content_path)
.then(({ data }) => {
const content = this.$el.querySelector('pre');
const fileContent = document.createTextNode(data.content);
@@ -71,7 +73,7 @@ import { __ } from '~/locale';
this.fileLoaded = true;
this.editor = ace.edit(content);
this.editor.$blockScrolling = Infinity; // Turn off annoying warning
- this.editor.getSession().setMode(`ace/mode/${data.blob_ace_mode}`);
+ this.editor.getSession().setMode(getModeByFileExtension(data.new_path));
this.editor.on('change', () => {
this.saveDiffResolution();
});
@@ -101,7 +103,7 @@ import { __ } from '~/locale';
},
acceptDiscardConfirmation(file) {
this.onAcceptDiscardConfirmation(file);
- }
- }
+ },
+ },
});
})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
index 69208ac2d36..c2de0379d23 100644
--- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
@@ -4,7 +4,7 @@ import Vue from 'vue';
import actionsMixin from '../mixins/line_conflict_actions';
import utilsMixin from '../mixins/line_conflict_utils';
-((global) => {
+(global => {
global.mergeConflicts = global.mergeConflicts || {};
global.mergeConflicts.parallelConflictLines = Vue.extend({
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
index 1501296ac4f..0333335de06 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
@@ -1,10 +1,10 @@
-/* eslint-disable comma-dangle, object-shorthand, no-param-reassign, camelcase, no-nested-ternary, no-continue, max-len */
+/* eslint-disable object-shorthand, no-param-reassign, camelcase, no-nested-ternary, no-continue */
import $ from 'jquery';
import Vue from 'vue';
import Cookies from 'js-cookie';
-((global) => {
+(global => {
global.mergeConflicts = global.mergeConflicts || {};
const diffViewType = Cookies.get('diff_view');
@@ -17,11 +17,11 @@ import Cookies from 'js-cookie';
const DEFAULT_RESOLVE_MODE = INTERACTIVE_RESOLVE_MODE;
const VIEW_TYPES = {
INLINE: 'inline',
- PARALLEL: 'parallel'
+ PARALLEL: 'parallel',
};
const CONFLICT_TYPES = {
TEXT: 'text',
- TEXT_EDITOR: 'text-editor'
+ TEXT_EDITOR: 'text-editor',
};
global.mergeConflicts.mergeConflictsStore = {
@@ -31,7 +31,7 @@ import Cookies from 'js-cookie';
isSubmitting: false,
isParallel: diffViewType === VIEW_TYPES.PARALLEL,
diffViewType: diffViewType,
- conflictsData: {}
+ conflictsData: {},
},
setConflictsData(data) {
@@ -47,7 +47,7 @@ import Cookies from 'js-cookie';
},
decorateFiles(files) {
- files.forEach((file) => {
+ files.forEach(file => {
file.content = '';
file.resolutionData = {};
file.promptDiscardConfirmation = false;
@@ -72,7 +72,7 @@ import Cookies from 'js-cookie';
setInlineLine(file) {
file.inlineLines = [];
- file.sections.forEach((section) => {
+ file.sections.forEach(section => {
let currentLineType = 'new';
const { conflict, lines, id } = section;
@@ -80,7 +80,7 @@ import Cookies from 'js-cookie';
file.inlineLines.push(this.getHeadHeaderLine(id));
}
- lines.forEach((line) => {
+ lines.forEach(line => {
const { type } = line;
if ((type === 'new' || type === 'old') && currentLineType !== type) {
@@ -102,7 +102,7 @@ import Cookies from 'js-cookie';
file.parallelLines = [];
const linesObj = { left: [], right: [] };
- file.sections.forEach((section) => {
+ file.sections.forEach(section => {
const { conflict, lines, id } = section;
if (conflict) {
@@ -110,7 +110,7 @@ import Cookies from 'js-cookie';
linesObj.right.push(this.getHeadHeaderLine(id));
}
- lines.forEach((line) => {
+ lines.forEach(line => {
const { type } = line;
if (conflict) {
@@ -131,10 +131,7 @@ import Cookies from 'js-cookie';
});
for (let i = 0, len = linesObj.left.length; i < len; i += 1) {
- file.parallelLines.push([
- linesObj.right[i],
- linesObj.left[i]
- ]);
+ file.parallelLines.push([linesObj.right[i], linesObj.left[i]]);
}
},
@@ -159,9 +156,9 @@ import Cookies from 'js-cookie';
const { files } = this.state.conflictsData;
let count = 0;
- files.forEach((file) => {
+ files.forEach(file => {
if (file.type === CONFLICT_TYPES.TEXT) {
- file.sections.forEach((section) => {
+ file.sections.forEach(section => {
if (section.conflict) {
count += 1;
}
@@ -198,7 +195,7 @@ import Cookies from 'js-cookie';
isHeader: true,
isHead: true,
isSelected: false,
- isUnselected: false
+ isUnselected: false,
};
},
@@ -229,7 +226,7 @@ import Cookies from 'js-cookie';
section: isHead ? 'head' : 'origin',
richText: rich_text,
isSelected: false,
- isUnselected: false
+ isUnselected: false,
};
},
@@ -243,7 +240,7 @@ import Cookies from 'js-cookie';
isHeader: true,
isOrigin: true,
isSelected: false,
- isUnselected: false
+ isUnselected: false,
};
},
@@ -290,14 +287,14 @@ import Cookies from 'js-cookie';
},
restoreFileLinesState(file) {
- file.inlineLines.forEach((line) => {
+ file.inlineLines.forEach(line => {
if (line.hasConflict || line.isHeader) {
line.isSelected = false;
line.isUnselected = false;
}
});
- file.parallelLines.forEach((lines) => {
+ file.parallelLines.forEach(lines => {
const left = lines[0];
const right = lines[1];
const isLeftMatch = left.hasConflict || left.isHeader;
@@ -354,7 +351,7 @@ import Cookies from 'js-cookie';
const initial = 'Commit to source branch';
const inProgress = 'Committing...';
- return this.state ? this.state.isSubmitting ? inProgress : initial : initial;
+ return this.state ? (this.state.isSubmitting ? inProgress : initial) : initial;
},
getCommitData() {
@@ -362,13 +359,13 @@ import Cookies from 'js-cookie';
commitData = {
commit_message: this.state.conflictsData.commitMessage,
- files: []
+ files: [],
};
- this.state.conflictsData.files.forEach((file) => {
+ this.state.conflictsData.files.forEach(file => {
const addFile = {
old_path: file.old_path,
- new_path: file.new_path
+ new_path: file.new_path,
};
if (file.type === CONFLICT_TYPES.TEXT) {
@@ -391,13 +388,13 @@ import Cookies from 'js-cookie';
handleSelected(file, sectionId, selection) {
Vue.set(file.resolutionData, sectionId, selection);
- file.inlineLines.forEach((line) => {
+ file.inlineLines.forEach(line => {
if (line.id === sectionId && (line.hasConflict || line.isHeader)) {
this.markLine(line, selection);
}
});
- file.parallelLines.forEach((lines) => {
+ file.parallelLines.forEach(lines => {
const left = lines[0];
const right = lines[1];
const hasSameId = right.id === sectionId || left.id === sectionId;
@@ -430,6 +427,6 @@ import Cookies from 'js-cookie';
fileTextTypePresent() {
return this.state.conflictsData.files.some(f => f.type === CONFLICT_TYPES.TEXT);
- }
+ },
};
})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index 7bf2c56dd5d..0deae478deb 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, comma-dangle, max-len, prefer-arrow-callback */
+/* eslint-disable func-names, no-var, no-underscore-dangle, one-var, consistent-return, prefer-arrow-callback */
import $ from 'jquery';
import { __ } from '~/locale';
@@ -16,26 +16,29 @@ function MergeRequest(opts) {
this.opts = opts != null ? opts : {};
this.submitNoteForm = this.submitNoteForm.bind(this);
this.$el = $('.merge-request');
- this.$('.show-all-commits').on('click', (function(_this) {
- return function() {
- return _this.showAllCommits();
- };
- })(this));
+ this.$('.show-all-commits').on(
+ 'click',
+ (function(_this) {
+ return function() {
+ return _this.showAllCommits();
+ };
+ })(this),
+ );
this.initTabs();
this.initMRBtnListeners();
this.initCommitMessageListeners();
this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport();
- if ($("a.btn-close").length) {
+ if ($('a.btn-close').length) {
this.taskList = new TaskList({
dataType: 'merge_request',
fieldName: 'description',
selector: '.detail-page-description',
- onSuccess: (result) => {
+ onSuccess: result => {
document.querySelector('#task_status').innerText = result.task_status;
document.querySelector('#task_status_short').innerText = result.task_status_short;
- }
+ },
});
}
}
@@ -84,7 +87,7 @@ MergeRequest.prototype.initMRBtnListeners = function() {
MergeRequest.prototype.submitNoteForm = function(form, $button) {
var noteText;
- noteText = form.find("textarea.js-note-text").val();
+ noteText = form.find('textarea.js-note-text').val();
if (noteText.trim().length > 0) {
form.submit();
$button.data('submitted', true);
@@ -122,7 +125,7 @@ MergeRequest.setStatusBoxToMerged = function() {
MergeRequest.decreaseCounter = function(by = 1) {
const $el = $('.js-merge-counter');
- const count = Math.max((parseInt($el.text().replace(/[^\d]/, ''), 10) - by), 0);
+ const count = Math.max(parseInt($el.text().replace(/[^\d]/, ''), 10) - by, 0);
$el.text(addDelimiter(count));
};
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 53d7504de35..d8255181574 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -11,14 +11,12 @@ import bp from './breakpoints';
import { parseUrlPathname, handleLocationHash, isMetaClick } from './lib/utils/common_utils';
import { isInVueNoteablePage } from './lib/utils/dom_utils';
import { getLocationHash } from './lib/utils/url_utility';
-import initDiscussionTab from './image_diff/init_discussion_tab';
import Diff from './diff';
import { localTimeAgo } from './lib/utils/datetime_utility';
import syntaxHighlight from './syntax_highlight';
import Notes from './notes';
import { polyfillSticky } from './lib/utils/sticky';
-/* eslint-disable max-len */
// MergeRequestTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
@@ -62,7 +60,6 @@ import { polyfillSticky } from './lib/utils/sticky';
// </div>
// </div>
//
-/* eslint-enable max-len */
// Store the `location` object, allowing for easier stubbing in tests
let { location } = window;
@@ -115,8 +112,9 @@ export default class MergeRequestTabs {
this.mergeRequestTabs &&
this.mergeRequestTabs.querySelector(`a[data-action='${action}']`) &&
this.mergeRequestTabs.querySelector(`a[data-action='${action}']`).click
- )
+ ) {
this.mergeRequestTabs.querySelector(`a[data-action='${action}']`).click();
+ }
this.initAffix();
}
@@ -193,9 +191,7 @@ export default class MergeRequestTabs {
if (bp.getBreakpointSize() !== 'lg') {
this.shrinkView();
}
- if (this.diffViewType() === 'parallel') {
- this.expandViewContainer();
- }
+ this.expandViewContainer();
this.destroyPipelinesView();
this.commitsTab.classList.remove('active');
} else if (action === 'pipelines') {
@@ -210,8 +206,6 @@ export default class MergeRequestTabs {
}
this.resetViewContainer();
this.destroyPipelinesView();
-
- initDiscussionTab();
}
if (this.setUrl) {
this.setCurrentAction(action);
@@ -354,7 +348,7 @@ export default class MergeRequestTabs {
localTimeAgo($('.js-timeago', 'div#diffs'));
syntaxHighlight($('#diffs .js-syntax-highlight'));
- if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) {
+ if (this.isDiffAction(this.currentAction)) {
this.expandViewContainer();
}
this.diffsLoaded = true;
@@ -407,19 +401,23 @@ export default class MergeRequestTabs {
}
diffViewType() {
- return $('.inline-parallel-buttons a.active').data('viewType');
+ return $('.inline-parallel-buttons button.active').data('viewType');
}
isDiffAction(action) {
return action === 'diffs' || action === 'new/diffs';
}
- expandViewContainer() {
+ expandViewContainer(removeLimited = true) {
const $wrapper = $('.content-wrapper .container-fluid').not('.breadcrumbs');
if (this.fixedLayoutPref === null) {
this.fixedLayoutPref = $wrapper.hasClass('container-limited');
}
- $wrapper.removeClass('container-limited');
+ if (this.diffViewType() === 'parallel' || removeLimited) {
+ $wrapper.removeClass('container-limited');
+ } else {
+ $wrapper.toggleClass('container-limited', this.fixedLayoutPref);
+ }
}
resetViewContainer() {
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index 6da04020881..f211632cf24 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -15,7 +15,7 @@ export default class Milestone {
}
bindTabsSwitching() {
- return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => {
+ return $('a[data-toggle="tab"]').on('show.bs.tab', e => {
const $target = $(e.target);
window.location.hash = $target.attr('href');
@@ -36,7 +36,8 @@ export default class Milestone {
const tabElId = $target.attr('href');
if (endpoint && !$target.hasClass('is-loaded')) {
- axios.get(endpoint)
+ axios
+ .get(endpoint)
.then(({ data }) => {
$(tabElId).html(data.html);
$target.addClass('is-loaded');
@@ -46,23 +47,28 @@ export default class Milestone {
}
static initDeprecationMessage() {
- const deprecationMesssageContainer = document.querySelector('.js-milestone-deprecation-message');
+ const deprecationMesssageContainer = document.querySelector(
+ '.js-milestone-deprecation-message',
+ );
if (!deprecationMesssageContainer) return;
- const deprecationMessage = deprecationMesssageContainer.querySelector('.js-milestone-deprecation-message-template').innerHTML;
+ const deprecationMessage = deprecationMesssageContainer.querySelector(
+ '.js-milestone-deprecation-message-template',
+ ).innerHTML;
const $popover = $('.js-popover-link', deprecationMesssageContainer);
const hideOnScroll = togglePopover.bind($popover, false);
- $popover.popover({
- content: deprecationMessage,
- html: true,
- placement: 'bottom',
- })
- .on('mouseenter', mouseenter)
- .on('mouseleave', debouncedMouseleave())
- .on('show.bs.popover', () => {
- window.addEventListener('scroll', hideOnScroll, { once: true });
- });
+ $popover
+ .popover({
+ content: deprecationMessage,
+ html: true,
+ placement: 'bottom',
+ })
+ .on('mouseenter', mouseenter)
+ .on('mouseleave', debouncedMouseleave())
+ .on('show.bs.popover', () => {
+ window.addEventListener('scroll', hideOnScroll, { once: true });
+ });
}
}
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 640a4c8260f..d32f39881dd 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
+/* eslint-disable one-var, no-unused-vars, object-shorthand, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
/* global Issuable */
/* global ListMilestone */
@@ -9,6 +9,10 @@ import '~/gl_dropdown';
import axios from './lib/utils/axios_utils';
import { timeFor } from './lib/utils/datetime_utility';
import ModalStore from './boards/stores/modal_store';
+import boardsStore, {
+ boardStoreIssueSet,
+ boardStoreIssueDelete,
+} from './boards/stores/boards_store';
export default class MilestoneSelect {
constructor(currentProject, els, options = {}) {
@@ -187,7 +191,7 @@ export default class MilestoneSelect {
return $dropdown.closest('form').submit();
} else if ($dropdown.hasClass('js-issue-board-sidebar')) {
if (selected.id !== -1 && isSelecting) {
- gl.issueBoards.boardStoreIssueSet(
+ boardStoreIssueSet(
'milestone',
new ListMilestone({
id: selected.id,
@@ -195,13 +199,13 @@ export default class MilestoneSelect {
}),
);
} else {
- gl.issueBoards.boardStoreIssueDelete('milestone');
+ boardStoreIssueDelete('milestone');
}
$dropdown.trigger('loading.gl.dropdown');
$loading.removeClass('hidden').fadeIn();
- gl.issueBoards.BoardsStore.detail.issue
+ boardsStore.detail.issue
.update($dropdown.attr('data-issue-update'))
.then(() => {
$dropdown.trigger('loaded.gl.dropdown');
diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js b/app/assets/javascripts/mini_pipeline_graph_dropdown.js
index f8257b6abab..81ab9d8be4b 100644
--- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js
+++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js
@@ -46,7 +46,7 @@ export default class MiniPipelineGraph {
$(document).on(
'click',
`${this.container} .js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item`,
- (e) => {
+ e => {
e.stopPropagation();
},
);
@@ -82,7 +82,8 @@ export default class MiniPipelineGraph {
this.renderBuildsList(button, '');
this.toggleLoading(button);
- axios.get(endpoint)
+ axios
+ .get(endpoint)
.then(({ data }) => {
this.toggleLoading(button);
this.renderBuildsList(button, data.html);
@@ -90,7 +91,11 @@ export default class MiniPipelineGraph {
})
.catch(() => {
this.toggleLoading(button);
- if ($(button).parent().hasClass('open')) {
+ if (
+ $(button)
+ .parent()
+ .hasClass('open')
+ ) {
$(button).dropdown('toggle');
}
flash('An error occurred while fetching the builds.', 'alert');
@@ -104,8 +109,8 @@ export default class MiniPipelineGraph {
* @return {type}
*/
toggleLoading(stageContainer) {
- stageContainer.parentElement.querySelector(
- `${this.dropdownListSelector} .js-builds-dropdown-loading`,
- ).classList.toggle('hidden');
+ stageContainer.parentElement
+ .querySelector(`${this.dropdownListSelector} .js-builds-dropdown-loading`)
+ .classList.toggle('hidden');
}
}
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 6afaefc56f8..98182d92c2f 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -97,33 +97,45 @@ export default {
store: new MonitoringStore(),
state: 'gettingStarted',
showEmptyState: true,
- updateAspectRatio: false,
- updatedAspectRatios: 0,
hoverData: {},
- resizeThrottled: {},
+ elWidth: 0,
};
},
+ computed: {
+ forceRedraw() {
+ return this.elWidth;
+ },
+ },
created() {
this.service = new MonitoringService({
metricsEndpoint: this.metricsEndpoint,
deploymentEndpoint: this.deploymentEndpoint,
environmentsEndpoint: this.environmentsEndpoint,
});
- eventHub.$on('toggleAspectRatio', this.toggleAspectRatio);
+ this.mutationObserverConfig = {
+ attributes: true,
+ childList: false,
+ subtree: false,
+ };
eventHub.$on('hoverChanged', this.hoverChanged);
},
beforeDestroy() {
- eventHub.$off('toggleAspectRatio', this.toggleAspectRatio);
eventHub.$off('hoverChanged', this.hoverChanged);
window.removeEventListener('resize', this.resizeThrottled, false);
+ this.sidebarMutationObserver.disconnect();
},
mounted() {
- this.resizeThrottled = _.throttle(this.resize, 600);
+ this.resizeThrottled = _.debounce(this.resize, 100);
if (!this.hasMetrics) {
this.state = 'gettingStarted';
} else {
this.getGraphsData();
window.addEventListener('resize', this.resizeThrottled, false);
+
+ const sidebarEl = document.querySelector('.nav-sidebar');
+ // The sidebar listener
+ this.sidebarMutationObserver = new MutationObserver(this.resizeThrottled);
+ this.sidebarMutationObserver.observe(sidebarEl, this.mutationObserverConfig);
}
},
methods: {
@@ -137,7 +149,7 @@ export default {
.catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))),
this.service
.getEnvironmentsData()
- .then((data) => this.store.storeEnvironmentsData(data))
+ .then(data => this.store.storeEnvironmentsData(data))
.catch(() => Flash(s__('Metrics|There was an error getting environments information.'))),
])
.then(() => {
@@ -145,6 +157,7 @@ export default {
this.state = 'noData';
return;
}
+
this.showEmptyState = false;
})
.then(this.resize)
@@ -153,14 +166,7 @@ export default {
});
},
resize() {
- this.updateAspectRatio = true;
- },
- toggleAspectRatio() {
- this.updatedAspectRatios += 1;
- if (this.store.getMetricsCount() === this.updatedAspectRatios) {
- this.updateAspectRatio = !this.updateAspectRatio;
- this.updatedAspectRatios = 0;
- }
+ this.elWidth = this.$el.clientWidth;
},
hoverChanged(data) {
this.hoverData = data;
@@ -172,7 +178,8 @@ export default {
<template>
<div
v-if="!showEmptyState"
- class="prometheus-graphs prepend-top-10"
+ :key="forceRedraw"
+ class="prometheus-graphs prepend-top-default"
>
<div class="environments d-flex align-items-center">
{{ s__('Metrics|Environment') }}
@@ -189,7 +196,10 @@ export default {
name="chevron-down"
/>
</button>
- <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
+ <div
+ v-if="store.environmentsData.length > 0"
+ class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up"
+ >
<ul>
<li
v-for="environment in store.environmentsData"
@@ -214,11 +224,10 @@ export default {
:show-panels="showPanels"
>
<graph
- v-for="(graphData, index) in groupData.metrics"
- :key="index"
+ v-for="(graphData, graphIndex) in groupData.metrics"
+ :key="graphIndex"
:graph-data="graphData"
:hover-data="hoverData"
- :update-aspect-ratio="updateAspectRatio"
:deployment-data="store.deploymentData"
:project-path="projectPath"
:tags-path="tagsPath"
diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue
index e5680a0499f..5c6e2e09e46 100644
--- a/app/assets/javascripts/monitoring/components/graph.vue
+++ b/app/assets/javascripts/monitoring/components/graph.vue
@@ -32,10 +32,6 @@ export default {
type: Object,
required: true,
},
- updateAspectRatio: {
- type: Boolean,
- required: true,
- },
deploymentData: {
type: Array,
required: true,
@@ -82,11 +78,13 @@ export default {
value: 0,
},
currentXCoordinate: 0,
- currentCoordinates: [],
+ currentCoordinates: {},
showFlag: false,
showFlagContent: false,
timeSeries: [],
+ graphDrawData: {},
realPixelRatio: 1,
+ seriesUnderMouse: [],
};
},
computed: {
@@ -109,15 +107,6 @@ export default {
},
},
watch: {
- updateAspectRatio() {
- if (this.updateAspectRatio) {
- this.graphHeight = 450;
- this.graphWidth = 600;
- this.measurements = measurements.large;
- this.draw();
- eventHub.$emit('toggleAspectRatio');
- }
- },
hoverData() {
this.positionFlag();
},
@@ -126,9 +115,13 @@ export default {
this.draw();
},
methods: {
+ showDot(path) {
+ return this.showFlagContent && this.seriesUnderMouse.includes(path);
+ },
draw() {
const breakpointSize = bp.getBreakpointSize();
const query = this.graphData.queries[0];
+ const svgWidth = this.$refs.baseSvg.getBoundingClientRect().width;
this.margin = measurements.large.margin;
if (this.smallGraph || breakpointSize === 'xs' || breakpointSize === 'sm') {
this.graphHeight = 300;
@@ -138,13 +131,13 @@ export default {
this.unitOfDisplay = query.unit || '';
this.yAxisLabel = this.graphData.y_label || 'Values';
this.legendTitle = query.label || 'Average';
- this.graphWidth = this.$refs.baseSvg.clientWidth - this.margin.left - this.margin.right;
+ this.graphWidth = svgWidth - this.margin.left - this.margin.right;
this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom;
this.baseGraphHeight = this.graphHeight - 50;
this.baseGraphWidth = this.graphWidth;
// pixel offsets inside the svg and outside are not 1:1
- this.realPixelRatio = this.$refs.baseSvg.clientWidth / this.baseGraphWidth;
+ this.realPixelRatio = svgWidth / this.baseGraphWidth;
this.renderAxesPaths();
this.formatDeployments();
@@ -155,7 +148,24 @@ export default {
point.y = e.clientY;
point = point.matrixTransform(this.$refs.graphData.getScreenCTM().inverse());
point.x += 7;
- const firstTimeSeries = this.timeSeries[0];
+
+ this.seriesUnderMouse = this.timeSeries.filter(series => {
+ const mouseX = series.timeSeriesScaleX.invert(point.x);
+ let minDistance = Infinity;
+
+ const closestTickMark = Object.keys(this.allXAxisValues).reduce((closest, x) => {
+ const distance = Math.abs(Number(new Date(x)) - Number(mouseX));
+ if (distance < minDistance) {
+ minDistance = distance;
+ return x;
+ }
+ return closest;
+ });
+
+ return series.values.find(v => v.time.toString() === closestTickMark);
+ });
+
+ const firstTimeSeries = this.seriesUnderMouse[0];
const timeValueOverlay = firstTimeSeries.timeSeriesScaleX.invert(point.x);
const overlayIndex = bisectDate(firstTimeSeries.values, timeValueOverlay, 1);
const d0 = firstTimeSeries.values[overlayIndex - 1];
@@ -172,12 +182,12 @@ export default {
});
},
renderAxesPaths() {
- this.timeSeries = createTimeSeries(
+ ({ timeSeries: this.timeSeries, graphDrawData: this.graphDrawData } = createTimeSeries(
this.graphData.queries,
this.graphWidth,
this.graphHeight,
this.graphHeightOffset,
- );
+ ));
if (_.findWhere(this.timeSeries, { renderCanary: true })) {
this.timeSeries = this.timeSeries.map(series => ({ ...series, renderCanary: true }));
@@ -190,6 +200,17 @@ export default {
axisXScale.domain(d3.extent(allValues, d => d.time));
axisYScale.domain([0, d3.max(allValues.map(d => d.value))]);
+ this.allXAxisValues = this.timeSeries.reduce((obj, series) => {
+ const seriesKeys = {};
+ series.values.forEach(v => {
+ seriesKeys[v.time] = true;
+ });
+ return {
+ ...obj,
+ ...seriesKeys,
+ };
+ }, {});
+
const xAxis = d3
.axisBottom()
.scale(axisXScale)
@@ -201,21 +222,18 @@ export default {
.scale(axisYScale)
.ticks(measurements.yTicks);
- d3
- .select(this.$refs.baseSvg)
+ d3.select(this.$refs.baseSvg)
.select('.x-axis')
.call(xAxis);
const width = this.graphWidth;
- d3
- .select(this.$refs.baseSvg)
+ d3.select(this.$refs.baseSvg)
.select('.y-axis')
.call(yAxis)
.selectAll('.tick')
.each(function createTickLines(d, i) {
if (i > 0) {
- d3
- .select(this)
+ d3.select(this)
.select('line')
.attr('x2', width)
.attr('class', 'axis-tick');
@@ -269,6 +287,10 @@ export default {
:viewBox="innerViewBox"
class="graph-data"
>
+ <slot
+ name="additionalSvgContent"
+ :graphDrawData="graphDrawData"
+ />
<graph-path
v-for="(path, index) in timeSeries"
:key="index"
@@ -277,9 +299,8 @@ export default {
:line-style="path.lineStyle"
:line-color="path.lineColor"
:area-color="path.areaColor"
- :current-coordinates="currentCoordinates[index]"
- :current-time-series-index="index"
- :show-dot="showFlagContent"
+ :current-coordinates="currentCoordinates[path.metricTag]"
+ :show-dot="showDot(path)"
/>
<graph-deployment
:deployment-data="reducedDeploymentData"
@@ -303,7 +324,7 @@ export default {
:graph-height="graphHeight"
:graph-height-offset="graphHeightOffset"
:show-flag-content="showFlagContent"
- :time-series="timeSeries"
+ :time-series="seriesUnderMouse"
:unit-of-display="unitOfDisplay"
:legend-title="legendTitle"
:deployment-flag-data="deploymentFlagData"
diff --git a/app/assets/javascripts/monitoring/components/graph/axis.vue b/app/assets/javascripts/monitoring/components/graph/axis.vue
index 8a604a51eb2..616410ec34f 100644
--- a/app/assets/javascripts/monitoring/components/graph/axis.vue
+++ b/app/assets/javascripts/monitoring/components/graph/axis.vue
@@ -38,38 +38,25 @@ export default {
computed: {
textTransform() {
const yCoordinate =
- (this.graphHeight -
- this.margin.top +
- this.measurements.axisLabelLineOffset) /
- 2 || 0;
+ (this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset) / 2 || 0;
return `translate(15, ${yCoordinate}) rotate(-90)`;
},
rectTransform() {
const yCoordinate =
- (this.graphHeight -
- this.margin.top +
- this.measurements.axisLabelLineOffset) /
- 2 +
+ (this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset) / 2 +
this.yLabelWidth / 2 || 0;
return `translate(0, ${yCoordinate}) rotate(-90)`;
},
xPosition() {
- return (
- (this.graphWidth + this.measurements.axisLabelLineOffset) / 2 -
- this.margin.right || 0
- );
+ return (this.graphWidth + this.measurements.axisLabelLineOffset) / 2 - this.margin.right || 0;
},
yPosition() {
- return (
- this.graphHeight -
- this.margin.top +
- this.measurements.axisLabelLineOffset || 0
- );
+ return this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset || 0;
},
yAxisLabelSentenceCase() {
diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue
index 1e6803abf3a..1720476480e 100644
--- a/app/assets/javascripts/monitoring/components/graph/flag.vue
+++ b/app/assets/javascripts/monitoring/components/graph/flag.vue
@@ -52,7 +52,7 @@ export default {
required: true,
},
currentCoordinates: {
- type: Array,
+ type: Object,
required: true,
},
},
@@ -91,8 +91,9 @@ export default {
},
methods: {
seriesMetricValue(seriesIndex, series) {
- const indexFromCoordinates = this.currentCoordinates[seriesIndex]
- ? this.currentCoordinates[seriesIndex].currentDataIndex : 0;
+ const indexFromCoordinates = this.currentCoordinates[series.metricTag]
+ ? this.currentCoordinates[series.metricTag].currentDataIndex
+ : 0;
const index = this.deploymentFlagData
? this.deploymentFlagData.seriesIndex
: indexFromCoordinates;
diff --git a/app/assets/javascripts/monitoring/components/graph/legend.vue b/app/assets/javascripts/monitoring/components/graph/legend.vue
index 3276f3a1ceb..ef18ae5c2c8 100644
--- a/app/assets/javascripts/monitoring/components/graph/legend.vue
+++ b/app/assets/javascripts/monitoring/components/graph/legend.vue
@@ -58,8 +58,8 @@ export default {
</td>
<template v-for="(track, trackIndex) in series.tracksLegend">
<track-line
- :track="track"
- :key="`track-line-${trackIndex}`"/>
+ :key="`track-line-${trackIndex}`"
+ :track="track"/>
<td :key="`track-info-${trackIndex}`">
<track-info
:track="track"
diff --git a/app/assets/javascripts/monitoring/components/graph/track_info.vue b/app/assets/javascripts/monitoring/components/graph/track_info.vue
index ec1c2222af9..3464067834f 100644
--- a/app/assets/javascripts/monitoring/components/graph/track_info.vue
+++ b/app/assets/javascripts/monitoring/components/graph/track_info.vue
@@ -26,4 +26,3 @@ export default {
{{ summaryMetrics }}
</span>
</template>
-
diff --git a/app/assets/javascripts/monitoring/components/graph/track_line.vue b/app/assets/javascripts/monitoring/components/graph/track_line.vue
index ba3f93b39ff..e04fd9c1f35 100644
--- a/app/assets/javascripts/monitoring/components/graph/track_line.vue
+++ b/app/assets/javascripts/monitoring/components/graph/track_line.vue
@@ -33,4 +33,3 @@ export default {
</svg>
</td>
</template>
-
diff --git a/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js b/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js
index 4f23814ff3e..87c3d969de4 100644
--- a/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js
+++ b/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js
@@ -6,7 +6,7 @@ const mixins = {
if (!this.reducedDeploymentData) return false;
let dataFound = false;
- this.reducedDeploymentData = this.reducedDeploymentData.map((d) => {
+ this.reducedDeploymentData = this.reducedDeploymentData.map(d => {
const deployment = d;
if (d.xPos >= mouseXPos - 10 && d.xPos <= mouseXPos + 10 && !dataFound) {
dataFound = d.xPos + 1;
@@ -50,19 +50,24 @@ const mixins = {
},
positionFlag() {
- const timeSeries = this.timeSeries[0];
- const hoveredDataIndex = bisectDate(timeSeries.values, this.hoverData.hoveredDate, 1);
+ const timeSeries = this.seriesUnderMouse[0];
+ if (!timeSeries) {
+ return;
+ }
+ const hoveredDataIndex = bisectDate(timeSeries.values, this.hoverData.hoveredDate);
this.currentData = timeSeries.values[hoveredDataIndex];
this.currentXCoordinate = Math.floor(timeSeries.timeSeriesScaleX(this.currentData.time));
- this.currentCoordinates = this.timeSeries.map((series) => {
- const currentDataIndex = bisectDate(series.values, this.hoverData.hoveredDate, 1);
+ this.currentCoordinates = {};
+
+ this.seriesUnderMouse.forEach(series => {
+ const currentDataIndex = bisectDate(series.values, this.hoverData.hoveredDate);
const currentData = series.values[currentDataIndex];
const currentX = Math.floor(series.timeSeriesScaleX(currentData.time));
const currentY = Math.floor(series.timeSeriesScaleY(currentData.value));
- return {
+ this.currentCoordinates[series.metricTag] = {
currentX,
currentY,
currentDataIndex,
diff --git a/app/assets/javascripts/monitoring/services/monitoring_service.js b/app/assets/javascripts/monitoring/services/monitoring_service.js
index 260d424378e..24b4acaf6da 100644
--- a/app/assets/javascripts/monitoring/services/monitoring_service.js
+++ b/app/assets/javascripts/monitoring/services/monitoring_service.js
@@ -8,18 +8,20 @@ const MAX_REQUESTS = 3;
function backOffRequest(makeRequestCallback) {
let requestCounter = 0;
return backOff((next, stop) => {
- makeRequestCallback().then((resp) => {
- if (resp.status === statusCodes.NO_CONTENT) {
- requestCounter += 1;
- if (requestCounter < MAX_REQUESTS) {
- next();
+ makeRequestCallback()
+ .then(resp => {
+ if (resp.status === statusCodes.NO_CONTENT) {
+ requestCounter += 1;
+ if (requestCounter < MAX_REQUESTS) {
+ next();
+ } else {
+ stop(new Error('Failed to connect to the prometheus server'));
+ }
} else {
- stop(new Error('Failed to connect to the prometheus server'));
+ stop(resp);
}
- } else {
- stop(resp);
- }
- }).catch(stop);
+ })
+ .catch(stop);
});
}
@@ -33,7 +35,7 @@ export default class MonitoringService {
getGraphsData() {
return backOffRequest(() => axios.get(this.metricsEndpoint))
.then(resp => resp.data)
- .then((response) => {
+ .then(response => {
if (!response || !response.data) {
throw new Error(s__('Metrics|Unexpected metrics data response from prometheus endpoint'));
}
@@ -47,22 +49,27 @@ export default class MonitoringService {
}
return backOffRequest(() => axios.get(this.deploymentEndpoint))
.then(resp => resp.data)
- .then((response) => {
+ .then(response => {
if (!response || !response.deployments) {
- throw new Error(s__('Metrics|Unexpected deployment data response from prometheus endpoint'));
+ throw new Error(
+ s__('Metrics|Unexpected deployment data response from prometheus endpoint'),
+ );
}
return response.deployments;
});
}
getEnvironmentsData() {
- return axios.get(this.environmentsEndpoint)
- .then(resp => resp.data)
- .then((response) => {
- if (!response || !response.environments) {
- throw new Error(s__('Metrics|There was an error fetching the environments data, please try again'));
- }
- return response.environments;
- });
+ return axios
+ .get(this.environmentsEndpoint)
+ .then(resp => resp.data)
+ .then(response => {
+ if (!response || !response.environments) {
+ throw new Error(
+ s__('Metrics|There was an error fetching the environments data, please try again'),
+ );
+ }
+ return response.environments;
+ });
}
}
diff --git a/app/assets/javascripts/monitoring/utils/measurements.js b/app/assets/javascripts/monitoring/utils/measurements.js
index ee866850e13..7c771f43eee 100644
--- a/app/assets/javascripts/monitoring/utils/measurements.js
+++ b/app/assets/javascripts/monitoring/utils/measurements.js
@@ -1,5 +1,6 @@
export default {
- small: { // Covers both xs and sm screen sizes
+ small: {
+ // Covers both xs and sm screen sizes
margin: {
top: 40,
right: 40,
@@ -18,7 +19,8 @@ export default {
},
axisLabelLineOffset: -20,
},
- large: { // This covers both md and lg screen sizes
+ large: {
+ // This covers both md and lg screen sizes
margin: {
top: 80,
right: 80,
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index cee39fd0559..bb24a1acdb3 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -2,7 +2,7 @@ import _ from 'underscore';
import { scaleLinear, scaleTime } from 'd3-scale';
import { line, area, curveLinear } from 'd3-shape';
import { extent, max, sum } from 'd3-array';
-import { timeMinute } from 'd3-time';
+import { timeMinute, timeSecond } from 'd3-time';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
const d3 = {
@@ -14,6 +14,7 @@ const d3 = {
extent,
max,
timeMinute,
+ timeSecond,
sum,
};
@@ -29,7 +30,7 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
-function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) {
+function queryTimeSeries(query, graphDrawData, lineStyle) {
let usedColors = [];
let renderCanary = false;
const timeSeriesParsed = [];
@@ -51,6 +52,25 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
return defaultColorPalette[pick];
}
+ function findByDate(series, time) {
+ const val = series.find(v => Math.abs(d3.timeSecond.count(time, v.time)) < 60);
+ if (val) {
+ return val.value;
+ }
+ return NaN;
+ }
+
+ // The timeseries data may have gaps in it
+ // but we need a regularly-spaced set of time/value pairs
+ // this gives us a complete range of one minute intervals
+ // offset the same amount as the original data
+ const [minX, maxX] = graphDrawData.xDom;
+ const offset = d3.timeMinute(minX) - Number(minX);
+ const datesWithoutGaps = d3.timeSecond
+ .every(60)
+ .range(d3.timeMinute.offset(minX, -1), maxX)
+ .map(d => d - offset);
+
query.result.forEach((timeSeries, timeSeriesNumber) => {
let metricTag = '';
let lineColor = '';
@@ -65,31 +85,6 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
renderCanary = true;
}
- const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
-
- const timeSeriesScaleY = d3.scaleLinear().range([graphHeight - graphHeightOffset, 0]);
-
- timeSeriesScaleX.domain(xDom);
- timeSeriesScaleX.ticks(d3.timeMinute, 60);
- timeSeriesScaleY.domain(yDom);
-
- const defined = d => !Number.isNaN(d.value) && d.value != null;
-
- const lineFunction = d3
- .line()
- .defined(defined)
- .curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate
- .x(d => timeSeriesScaleX(d.time))
- .y(d => timeSeriesScaleY(d.value));
-
- const areaFunction = d3
- .area()
- .defined(defined)
- .curve(d3.curveLinear)
- .x(d => timeSeriesScaleX(d.time))
- .y0(graphHeight - graphHeightOffset)
- .y1(d => timeSeriesScaleY(d.value));
-
const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData =
query.series != null && _.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
@@ -119,11 +114,16 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
});
}
+ const values = datesWithoutGaps.map(time => ({
+ time,
+ value: findByDate(timeSeries.values, time),
+ }));
+
timeSeriesParsed.push({
- linePath: lineFunction(timeSeries.values),
- areaPath: areaFunction(timeSeries.values),
- timeSeriesScaleX,
- timeSeriesScaleY,
+ linePath: graphDrawData.lineFunction(values),
+ areaPath: graphDrawData.areaBelowLine(values),
+ timeSeriesScaleX: graphDrawData.timeSeriesScaleX,
+ timeSeriesScaleY: graphDrawData.timeSeriesScaleY,
values: timeSeries.values,
max: maximumValue,
average: accum / timeSeries.values.length,
@@ -140,7 +140,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
return timeSeriesParsed;
}
-export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
+function xyDomain(queries) {
const allValues = queries.reduce(
(allQueryResults, query) =>
allQueryResults.concat(
@@ -152,10 +152,68 @@ export default function createTimeSeries(queries, graphWidth, graphHeight, graph
const xDom = d3.extent(allValues, d => d.time);
const yDom = [0, d3.max(allValues.map(d => d.value))];
- return queries.reduce((series, query, index) => {
+ return {
+ xDom,
+ yDom,
+ };
+}
+
+export function generateGraphDrawData(queries, graphWidth, graphHeight, graphHeightOffset) {
+ const { xDom, yDom } = xyDomain(queries);
+
+ const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
+ const timeSeriesScaleY = d3.scaleLinear().range([graphHeight - graphHeightOffset, 0]);
+
+ timeSeriesScaleX.domain(xDom);
+ timeSeriesScaleX.ticks(d3.timeMinute, 60);
+ timeSeriesScaleY.domain(yDom);
+
+ const defined = d => !Number.isNaN(d.value) && d.value != null;
+
+ const lineFunction = d3
+ .line()
+ .defined(defined)
+ .curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate
+ .x(d => timeSeriesScaleX(d.time))
+ .y(d => timeSeriesScaleY(d.value));
+
+ const areaBelowLine = d3
+ .area()
+ .defined(defined)
+ .curve(d3.curveLinear)
+ .x(d => timeSeriesScaleX(d.time))
+ .y0(graphHeight - graphHeightOffset)
+ .y1(d => timeSeriesScaleY(d.value));
+
+ const areaAboveLine = d3
+ .area()
+ .defined(defined)
+ .curve(d3.curveLinear)
+ .x(d => timeSeriesScaleX(d.time))
+ .y0(0)
+ .y1(d => timeSeriesScaleY(d.value));
+
+ return {
+ lineFunction,
+ areaBelowLine,
+ areaAboveLine,
+ xDom,
+ yDom,
+ timeSeriesScaleX,
+ timeSeriesScaleY,
+ };
+}
+
+export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
+ const graphDrawData = generateGraphDrawData(queries, graphWidth, graphHeight, graphHeightOffset);
+
+ const timeSeries = queries.reduce((series, query, index) => {
const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length];
- return series.concat(
- queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle),
- );
+ return series.concat(queryTimeSeries(query, graphDrawData, lineStyle));
}, []);
+
+ return {
+ timeSeries,
+ graphDrawData,
+ };
}
diff --git a/app/assets/javascripts/mr_notes/index.js b/app/assets/javascripts/mr_notes/index.js
index 8aabb840847..1c98683c597 100644
--- a/app/assets/javascripts/mr_notes/index.js
+++ b/app/assets/javascripts/mr_notes/index.js
@@ -4,6 +4,7 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import initDiffsApp from '../diffs';
import notesApp from '../notes/components/notes_app.vue';
import discussionCounter from '../notes/components/discussion_counter.vue';
+import initDiscussionFilters from '../notes/discussion_filters';
import store from './stores';
import MergeRequest from '../merge_request';
@@ -88,5 +89,6 @@ export default function initMrNotes() {
},
});
+ initDiscussionFilters(store);
initDiffsApp(store);
}
diff --git a/app/assets/javascripts/mr_notes/stores/index.js b/app/assets/javascripts/mr_notes/stores/index.js
index dd2019001db..c4225c8ec08 100644
--- a/app/assets/javascripts/mr_notes/stores/index.js
+++ b/app/assets/javascripts/mr_notes/stores/index.js
@@ -6,10 +6,13 @@ import mrPageModule from './modules';
Vue.use(Vuex);
-export default new Vuex.Store({
- modules: {
- page: mrPageModule,
- notes: notesModule,
- diffs: diffsModule,
- },
-});
+export const createStore = () =>
+ new Vuex.Store({
+ modules: {
+ page: mrPageModule,
+ notes: notesModule(),
+ diffs: diffsModule(),
+ },
+ });
+
+export default createStore();
diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js
index 17370edeb0c..cba6759ebf5 100644
--- a/app/assets/javascripts/namespace_select.js
+++ b/app/assets/javascripts/namespace_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, prefer-arrow-callback, max-len */
+/* eslint-disable func-names, object-shorthand, no-else-return, prefer-template, prefer-arrow-callback */
import $ from 'jquery';
import Api from './api';
@@ -14,14 +14,14 @@ export default class NamespaceSelect {
selectable: true,
filterRemote: true,
search: {
- fields: ['path']
+ fields: ['path'],
},
fieldName: fieldName,
toggleLabel: function(selected) {
if (selected.id == null) {
return selected.text;
} else {
- return selected.kind + ": " + selected.full_path;
+ return selected.kind + ': ' + selected.full_path;
}
},
data: function(term, dataCallback) {
@@ -29,7 +29,7 @@ export default class NamespaceSelect {
if (isFilter) {
const anyNamespace = {
text: 'Any namespace',
- id: null
+ id: null,
};
namespaces.unshift(anyNamespace);
namespaces.splice(1, 0, 'divider');
@@ -41,7 +41,7 @@ export default class NamespaceSelect {
if (namespace.id == null) {
return namespace.text;
} else {
- return namespace.kind + ": " + namespace.full_path;
+ return namespace.kind + ': ' + namespace.full_path;
}
},
renderRow: this.renderRow,
diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js
index 94da1be4066..d1fa9f5e2a2 100644
--- a/app/assets/javascripts/network/branch_graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, wrap-iife, quotes, comma-dangle, one-var, one-var-declaration-per-line, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len */
+/* eslint-disable func-names, no-var, one-var, no-loop-func, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase */
import $ from 'jquery';
import { __ } from '../locale';
@@ -20,7 +20,7 @@ export default (function() {
this.mtime = 0;
this.mspace = 0;
this.parents = {};
- this.colors = ["#000"];
+ this.colors = ['#000'];
this.offsetX = 150;
this.offsetY = 20;
this.unitTime = 30;
@@ -30,9 +30,10 @@ export default (function() {
}
BranchGraph.prototype.load = function() {
- axios.get(this.options.url)
+ axios
+ .get(this.options.url)
.then(({ data }) => {
- $(".loading", this.element).hide();
+ $('.loading', this.element).hide();
this.prepareData(data.days, data.commits);
this.buildGraph();
})
@@ -71,17 +72,19 @@ export default (function() {
c = ref[j];
this.mtime = Math.max(this.mtime, c.time);
this.mspace = Math.max(this.mspace, c.space);
- results.push((function() {
- var l, len1, ref1, results1;
- ref1 = c.parents;
- results1 = [];
- for (l = 0, len1 = ref1.length; l < len1; l += 1) {
- p = ref1[l];
- this.parents[p[0]] = true;
- results1.push(this.mspace = Math.max(this.mspace, p[1]));
- }
- return results1;
- }).call(this));
+ results.push(
+ function() {
+ var l, len1, ref1, results1;
+ ref1 = c.parents;
+ results1 = [];
+ for (l = 0, len1 = ref1.length; l < len1; l += 1) {
+ p = ref1[l];
+ this.parents[p[0]] = true;
+ results1.push((this.mspace = Math.max(this.mspace, p[1])));
+ }
+ return results1;
+ }.call(this),
+ );
}
return results;
};
@@ -91,11 +94,11 @@ export default (function() {
k = 0;
results = [];
while (k < this.mspace) {
- this.colors.push(Raphael.getColor(.8));
+ this.colors.push(Raphael.getColor(0.8));
// Skipping a few colors in the spectrum to get more contrast between colors
Raphael.getColor();
Raphael.getColor();
- results.push(k += 1);
+ results.push((k += 1));
}
return results;
};
@@ -104,12 +107,12 @@ export default (function() {
var cuday, cumonth, day, j, len, mm, ref;
const { r } = this;
cuday = 0;
- cumonth = "";
+ cumonth = '';
r.rect(0, 0, 40, this.barHeight).attr({
- fill: "#222"
+ fill: '#222',
});
r.rect(40, 0, 30, this.barHeight).attr({
- fill: "#444"
+ fill: '#444',
});
ref = this.days;
@@ -118,16 +121,16 @@ export default (function() {
if (cuday !== day[0] || cumonth !== day[1]) {
// Dates
r.text(55, this.offsetY + this.unitTime * mm, day[0]).attr({
- font: "12px Monaco, monospace",
- fill: "#BBB"
+ font: '12px Monaco, monospace',
+ fill: '#BBB',
});
[cuday] = day;
}
if (cumonth !== day[1]) {
// Months
r.text(20, this.offsetY + this.unitTime * mm, day[1]).attr({
- font: "12px Monaco, monospace",
- fill: "#EEE"
+ font: '12px Monaco, monospace',
+ fill: '#EEE',
});
// eslint-disable-next-line prefer-destructuring
@@ -173,11 +176,13 @@ export default (function() {
BranchGraph.prototype.bindEvents = function() {
const { element } = this;
- return $(element).scroll((function(_this) {
- return function(event) {
- return _this.renderPartialGraph();
- };
- })(this));
+ return $(element).scroll(
+ (function(_this) {
+ return function(event) {
+ return _this.renderPartialGraph();
+ };
+ })(this),
+ );
};
BranchGraph.prototype.scrollDown = function() {
@@ -219,46 +224,53 @@ export default (function() {
shortrefs = commit.refs;
// Truncate if longer than 15 chars
if (shortrefs.length > 17) {
- shortrefs = shortrefs.substr(0, 15) + "…";
+ shortrefs = shortrefs.substr(0, 15) + '…';
}
text = r.text(x + 4, y, shortrefs).attr({
- "text-anchor": "start",
- font: "10px Monaco, monospace",
- fill: "#FFF",
- title: commit.refs
+ 'text-anchor': 'start',
+ font: '10px Monaco, monospace',
+ fill: '#FFF',
+ title: commit.refs,
});
textbox = text.getBBox();
// Create rectangle based on the size of the textbox
rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr({
- fill: "#000",
- "fill-opacity": .5,
- stroke: "none"
+ fill: '#000',
+ 'fill-opacity': 0.5,
+ stroke: 'none',
});
- triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr({
- fill: "#000",
- "fill-opacity": .5,
- stroke: "none"
+ triangle = r.path(['M', x - 5, y, 'L', x - 15, y - 4, 'L', x - 15, y + 4, 'Z']).attr({
+ fill: '#000',
+ 'fill-opacity': 0.5,
+ stroke: 'none',
});
label = r.set(rect, text);
- label.transform(["t", -rect.getBBox().width - 15, 0]);
+ label.transform(['t', -rect.getBBox().width - 15, 0]);
// Set text to front
return text.toFront();
};
BranchGraph.prototype.appendAnchor = function(x, y, commit) {
const { r, top, options } = this;
- const anchor = r.circle(x, y, 10).attr({
- fill: "#000",
- opacity: 0,
- cursor: "pointer"
- }).click(function() {
- return window.open(options.commit_url.replace("%s", commit.id), "_blank");
- }).hover(function() {
- this.tooltip = r.commitTooltip(x + 5, y, commit);
- return top.push(this.tooltip.insertBefore(this));
- }, function() {
- return this.tooltip && this.tooltip.remove() && delete this.tooltip;
- });
+ const anchor = r
+ .circle(x, y, 10)
+ .attr({
+ fill: '#000',
+ opacity: 0,
+ cursor: 'pointer',
+ })
+ .click(function() {
+ return window.open(options.commit_url.replace('%s', commit.id), '_blank');
+ })
+ .hover(
+ function() {
+ this.tooltip = r.commitTooltip(x + 5, y, commit);
+ return top.push(this.tooltip.insertBefore(this));
+ },
+ function() {
+ return this.tooltip && this.tooltip.remove() && delete this.tooltip;
+ },
+ );
return top.push(anchor);
};
@@ -266,7 +278,7 @@ export default (function() {
const { r } = this;
r.circle(x, y, 3).attr({
fill: this.colors[commit.space],
- stroke: "none"
+ stroke: 'none',
});
const avatar_box_x = this.offsetX + this.unitSpace * this.mspace + 10;
@@ -274,13 +286,15 @@ export default (function() {
r.rect(avatar_box_x, avatar_box_y, 20, 20).attr({
stroke: this.colors[commit.space],
- "stroke-width": 2
+ 'stroke-width': 2,
});
r.image(commit.author.icon, avatar_box_x, avatar_box_y, 20, 20);
- return r.text(this.offsetX + this.unitSpace * this.mspace + 35, y, commit.message.split("\n")[0]).attr({
- "text-anchor": "start",
- font: "14px Monaco, monospace"
- });
+ return r
+ .text(this.offsetX + this.unitSpace * this.mspace + 35, y, commit.message.split('\n')[0])
+ .attr({
+ 'text-anchor': 'start',
+ font: '14px Monaco, monospace',
+ });
};
BranchGraph.prototype.drawLines = function(x, y, commit) {
@@ -304,30 +318,32 @@ export default (function() {
// Build line shape
if (parent[1] === commit.space) {
offset = [0, 5];
- arrow = "l-2,5,4,0,-2,-5,0,5";
+ arrow = 'l-2,5,4,0,-2,-5,0,5';
} else if (parent[1] < commit.space) {
offset = [3, 3];
- arrow = "l5,0,-2,4,-3,-4,4,2";
+ arrow = 'l5,0,-2,4,-3,-4,4,2';
} else {
offset = [-3, 3];
- arrow = "l-5,0,2,4,3,-4,-4,2";
+ arrow = 'l-5,0,2,4,3,-4,-4,2';
}
// Start point
- route = ["M", x + offset[0], y + offset[1]];
+ route = ['M', x + offset[0], y + offset[1]];
// Add arrow if not first parent
if (i > 0) {
route.push(arrow);
}
// Circumvent if overlap
if (commit.space !== parentCommit.space || commit.space !== parent[1]) {
- route.push("L", parentX2, y + 10, "L", parentX2, parentY - 5);
+ route.push('L', parentX2, y + 10, 'L', parentX2, parentY - 5);
}
// End point
- route.push("L", parentX1, parentY);
- results.push(r.path(route).attr({
- stroke: color,
- "stroke-width": 2
- }));
+ route.push('L', parentX1, parentY);
+ results.push(
+ r.path(route).attr({
+ stroke: color,
+ 'stroke-width': 2,
+ }),
+ );
}
return results;
};
@@ -337,10 +353,10 @@ export default (function() {
const { r } = this;
const x = this.offsetX + this.unitSpace * (this.mspace - commit.space);
const y = this.offsetY + this.unitTime * commit.time;
- r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr({
- fill: "#000",
- "fill-opacity": .5,
- stroke: "none"
+ r.path(['M', x + 5, y, 'L', x + 15, y + 4, 'L', x + 15, y - 4, 'Z']).attr({
+ fill: '#000',
+ 'fill-opacity': 0.5,
+ stroke: 'none',
});
// Displayed in the center
return this.element.scrollTop(y - this.graphHeight / 2);
diff --git a/app/assets/javascripts/network/raphael.js b/app/assets/javascripts/network/raphael.js
index 09dcf716148..22e06a35d91 100644
--- a/app/assets/javascripts/network/raphael.js
+++ b/app/assets/javascripts/network/raphael.js
@@ -49,7 +49,7 @@ Raphael.prototype.textWrap = function testWrap(t, width) {
const s = [];
for (let j = 0, len = words.length; j < len; j += 1) {
const word = words[j];
- if (x + (word.length * letterWidth) > width) {
+ if (x + word.length * letterWidth > width) {
s.push('\n');
x = 0;
}
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index 205d9766656..f338dbbb0a6 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, one-var, max-len, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len */
+/* eslint-disable func-names, no-var, one-var, consistent-return, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return */
import $ from 'jquery';
import RefSelectDropdown from './ref_select_dropdown';
@@ -30,24 +30,24 @@ export default class NewBranchForm {
startsWith = {
pattern: /^(\/|\.)/g,
prefix: "can't start with",
- conjunction: "or"
+ conjunction: 'or',
};
endsWith = {
pattern: /(\/|\.|\.lock)$/g,
prefix: "can't end in",
- conjunction: "or"
+ conjunction: 'or',
};
invalid = {
pattern: /(\s|~|\^|:|\?|\*|\[|\\|\.\.|@\{|\/{2,}){1}/g,
prefix: "can't contain",
- conjunction: ", "
+ conjunction: ', ',
};
single = {
pattern: /^@+$/g,
prefix: "can't be",
- conjunction: "or"
+ conjunction: 'or',
};
- return this.restrictions = [startsWith, invalid, endsWith, single];
+ return (this.restrictions = [startsWith, invalid, endsWith, single]);
}
validate() {
@@ -73,7 +73,7 @@ export default class NewBranchForm {
return "'" + value + "'";
}
});
- return restriction.prefix + " " + (formatted.join(restriction.conjunction));
+ return restriction.prefix + ' ' + formatted.join(restriction.conjunction);
};
validator = (function(_this) {
return function(errors, restriction) {
@@ -88,7 +88,7 @@ export default class NewBranchForm {
})(this);
errors = this.restrictions.reduce(validator, []);
if (errors.length > 0) {
- errorMessage = $("<span/>").text(errors.join(', '));
+ errorMessage = $('<span/>').text(errors.join(', '));
return this.branchNameError.append(errorMessage);
}
}
diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js
index 17ec20f1cc1..b142f212eb0 100644
--- a/app/assets/javascripts/new_commit_form.js
+++ b/app/assets/javascripts/new_commit_form.js
@@ -6,9 +6,7 @@ export default class NewCommitForm {
this.branchName = form.find('.js-branch-name');
this.originalBranch = form.find('.js-original-branch');
this.createMergeRequest = form.find('.js-create-merge-request');
- this.createMergeRequestContainer = form.find(
- '.js-create-merge-request-container',
- );
+ this.createMergeRequestContainer = form.find('.js-create-merge-request-container');
this.branchName.keyup(this.renderDestination);
this.renderDestination();
}
diff --git a/app/assets/javascripts/notebook/cells/code/index.vue b/app/assets/javascripts/notebook/cells/code/index.vue
index 7d2a1a33b98..0691ba64f8e 100644
--- a/app/assets/javascripts/notebook/cells/code/index.vue
+++ b/app/assets/javascripts/notebook/cells/code/index.vue
@@ -1,45 +1,45 @@
<script>
- import Prism from '../../lib/highlight';
- import Prompt from '../prompt.vue';
+import Prism from '../../lib/highlight';
+import Prompt from '../prompt.vue';
- export default {
- components: {
- prompt: Prompt,
+export default {
+ components: {
+ prompt: Prompt,
+ },
+ props: {
+ count: {
+ type: Number,
+ required: false,
+ default: 0,
},
- props: {
- count: {
- type: Number,
- required: false,
- default: 0,
- },
- codeCssClass: {
- type: String,
- required: false,
- default: '',
- },
- type: {
- type: String,
- required: true,
- },
- rawCode: {
- type: String,
- required: true,
- },
+ codeCssClass: {
+ type: String,
+ required: false,
+ default: '',
},
- computed: {
- code() {
- return this.rawCode;
- },
- promptType() {
- const type = this.type.split('put')[0];
-
- return type.charAt(0).toUpperCase() + type.slice(1);
- },
+ type: {
+ type: String,
+ required: true,
+ },
+ rawCode: {
+ type: String,
+ required: true,
},
- mounted() {
- Prism.highlightElement(this.$refs.code);
+ },
+ computed: {
+ code() {
+ return this.rawCode;
+ },
+ promptType() {
+ const type = this.type.split('put')[0];
+
+ return type.charAt(0).toUpperCase() + type.slice(1);
},
- };
+ },
+ mounted() {
+ Prism.highlightElement(this.$refs.code);
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue
index 3d09d24b6ab..5aa83db0986 100644
--- a/app/assets/javascripts/notebook/cells/markdown.vue
+++ b/app/assets/javascripts/notebook/cells/markdown.vue
@@ -1,12 +1,12 @@
<script>
- /* global katex */
- import marked from 'marked';
- import sanitize from 'sanitize-html';
- import Prompt from './prompt.vue';
+/* global katex */
+import marked from 'marked';
+import sanitize from 'sanitize-html';
+import Prompt from './prompt.vue';
- const renderer = new marked.Renderer();
+const renderer = new marked.Renderer();
- /*
+/*
Regex to match KaTex blocks.
Supports the following:
@@ -17,7 +17,7 @@
The matched text then goes through the KaTex renderer & then outputs the HTML
*/
- const katexRegexString = `(
+const katexRegexString = `(
^\\\\begin{[a-zA-Z]+}\\s
|
^\\$\\$
@@ -32,66 +32,69 @@
|
\\$
)
- `.replace(/\s/g, '').trim();
+ `
+ .replace(/\s/g, '')
+ .trim();
- renderer.paragraph = (t) => {
- let text = t;
- let inline = false;
+renderer.paragraph = t => {
+ let text = t;
+ let inline = false;
- if (typeof katex !== 'undefined') {
- const katexString = text.replace(/&amp;/g, '&')
- .replace(/&=&/g, '\\space=\\space')
- .replace(/<(\/?)em>/g, '_');
- const regex = new RegExp(katexRegexString, 'gi');
- const matchLocation = katexString.search(regex);
- const numberOfMatches = katexString.match(regex);
+ if (typeof katex !== 'undefined') {
+ const katexString = text
+ .replace(/&amp;/g, '&')
+ .replace(/&=&/g, '\\space=\\space')
+ .replace(/<(\/?)em>/g, '_');
+ const regex = new RegExp(katexRegexString, 'gi');
+ const matchLocation = katexString.search(regex);
+ const numberOfMatches = katexString.match(regex);
- if (numberOfMatches && numberOfMatches.length !== 0) {
- if (matchLocation > 0) {
- let matches = regex.exec(katexString);
- inline = true;
+ if (numberOfMatches && numberOfMatches.length !== 0) {
+ if (matchLocation > 0) {
+ let matches = regex.exec(katexString);
+ inline = true;
- while (matches !== null) {
- const renderedKatex = katex.renderToString(matches[0].replace(/\$/g, ''));
- text = `${text.replace(matches[0], ` ${renderedKatex}`)}`;
- matches = regex.exec(katexString);
- }
- } else {
- const matches = regex.exec(katexString);
- text = katex.renderToString(matches[2]);
+ while (matches !== null) {
+ const renderedKatex = katex.renderToString(matches[0].replace(/\$/g, ''));
+ text = `${text.replace(matches[0], ` ${renderedKatex}`)}`;
+ matches = regex.exec(katexString);
}
+ } else {
+ const matches = regex.exec(katexString);
+ text = katex.renderToString(matches[2]);
}
}
+ }
- return `<p class="${inline ? 'inline-katex' : ''}">${text}</p>`;
- };
+ return `<p class="${inline ? 'inline-katex' : ''}">${text}</p>`;
+};
- marked.setOptions({
- sanitize: true,
- renderer,
- });
+marked.setOptions({
+ sanitize: true,
+ renderer,
+});
- export default {
- components: {
- prompt: Prompt,
- },
- props: {
- cell: {
- type: Object,
- required: true,
- },
+export default {
+ components: {
+ prompt: Prompt,
+ },
+ props: {
+ cell: {
+ type: Object,
+ required: true,
},
- computed: {
- markdown() {
- return sanitize(marked(this.cell.source.join('').replace(/\\/g, '\\\\')), {
- allowedTags: false,
- allowedAttributes: {
- '*': ['class'],
- },
- });
- },
+ },
+ computed: {
+ markdown() {
+ return sanitize(marked(this.cell.source.join('').replace(/\\/g, '\\\\')), {
+ allowedTags: false,
+ allowedAttributes: {
+ '*': ['class'],
+ },
+ });
},
- };
+ },
+};
</script>
<template>
@@ -105,13 +108,13 @@
</template>
<style>
- .markdown .katex {
- display: block;
- text-align: center;
- }
+.markdown .katex {
+ display: block;
+ text-align: center;
+}
- .markdown .inline-katex .katex {
- display: inline;
- text-align: initial;
- }
+.markdown .inline-katex .katex {
+ display: inline;
+ text-align: initial;
+}
</style>
diff --git a/app/assets/javascripts/notebook/cells/output/html.vue b/app/assets/javascripts/notebook/cells/output/html.vue
index 0535ee7afa8..c6fc786fa76 100644
--- a/app/assets/javascripts/notebook/cells/output/html.vue
+++ b/app/assets/javascripts/notebook/cells/output/html.vue
@@ -1,30 +1,28 @@
<script>
- import sanitize from 'sanitize-html';
- import Prompt from '../prompt.vue';
+import sanitize from 'sanitize-html';
+import Prompt from '../prompt.vue';
- export default {
- components: {
- prompt: Prompt,
+export default {
+ components: {
+ prompt: Prompt,
+ },
+ props: {
+ rawCode: {
+ type: String,
+ required: true,
},
- props: {
- rawCode: {
- type: String,
- required: true,
- },
+ },
+ computed: {
+ sanitizedOutput() {
+ return sanitize(this.rawCode, {
+ allowedTags: sanitize.defaults.allowedTags.concat(['img', 'svg']),
+ allowedAttributes: {
+ img: ['src'],
+ },
+ });
},
- computed: {
- sanitizedOutput() {
- return sanitize(this.rawCode, {
- allowedTags: sanitize.defaults.allowedTags.concat([
- 'img', 'svg',
- ]),
- allowedAttributes: {
- img: ['src'],
- },
- });
- },
- },
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/notebook/cells/output/image.vue b/app/assets/javascripts/notebook/cells/output/image.vue
index 67d6c5ad12b..a17868963ce 100644
--- a/app/assets/javascripts/notebook/cells/output/image.vue
+++ b/app/assets/javascripts/notebook/cells/output/image.vue
@@ -1,21 +1,21 @@
<script>
- import Prompt from '../prompt.vue';
+import Prompt from '../prompt.vue';
- export default {
- components: {
- prompt: Prompt,
+export default {
+ components: {
+ prompt: Prompt,
+ },
+ props: {
+ outputType: {
+ type: String,
+ required: true,
},
- props: {
- outputType: {
- type: String,
- required: true,
- },
- rawCode: {
- type: String,
- required: true,
- },
+ rawCode: {
+ type: String,
+ required: true,
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/notebook/cells/output/index.vue b/app/assets/javascripts/notebook/cells/output/index.vue
index 4183b976814..bd0bcc0d819 100644
--- a/app/assets/javascripts/notebook/cells/output/index.vue
+++ b/app/assets/javascripts/notebook/cells/output/index.vue
@@ -1,78 +1,78 @@
<script>
- import CodeCell from '../code/index.vue';
- import Html from './html.vue';
- import Image from './image.vue';
+import CodeCell from '../code/index.vue';
+import Html from './html.vue';
+import Image from './image.vue';
- export default {
- components: {
- 'code-cell': CodeCell,
- 'html-output': Html,
- 'image-output': Image,
+export default {
+ components: {
+ 'code-cell': CodeCell,
+ 'html-output': Html,
+ 'image-output': Image,
+ },
+ props: {
+ codeCssClass: {
+ type: String,
+ required: false,
+ default: '',
},
- props: {
- codeCssClass: {
- type: String,
- required: false,
- default: '',
- },
- count: {
- type: Number,
- required: false,
- default: 0,
- },
- output: {
- type: Object,
- requred: true,
- default: () => ({}),
- },
+ count: {
+ type: Number,
+ required: false,
+ default: 0,
},
- computed: {
- componentName() {
- if (this.output.text) {
- return 'code-cell';
- } else if (this.output.data['image/png']) {
- return 'image-output';
- } else if (this.output.data['text/html']) {
- return 'html-output';
- } else if (this.output.data['image/svg+xml']) {
- return 'html-output';
- }
-
+ output: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+ computed: {
+ componentName() {
+ if (this.output.text) {
return 'code-cell';
- },
- rawCode() {
- if (this.output.text) {
- return this.output.text.join('');
- }
+ } else if (this.output.data['image/png']) {
+ return 'image-output';
+ } else if (this.output.data['text/html']) {
+ return 'html-output';
+ } else if (this.output.data['image/svg+xml']) {
+ return 'html-output';
+ }
- return this.dataForType(this.outputType);
- },
- outputType() {
- if (this.output.text) {
- return '';
- } else if (this.output.data['image/png']) {
- return 'image/png';
- } else if (this.output.data['text/html']) {
- return 'text/html';
- } else if (this.output.data['image/svg+xml']) {
- return 'image/svg+xml';
- }
+ return 'code-cell';
+ },
+ rawCode() {
+ if (this.output.text) {
+ return this.output.text.join('');
+ }
+
+ return this.dataForType(this.outputType);
+ },
+ outputType() {
+ if (this.output.text) {
+ return '';
+ } else if (this.output.data['image/png']) {
+ return 'image/png';
+ } else if (this.output.data['text/html']) {
+ return 'text/html';
+ } else if (this.output.data['image/svg+xml']) {
+ return 'image/svg+xml';
+ }
- return 'text/plain';
- },
+ return 'text/plain';
},
- methods: {
- dataForType(type) {
- let data = this.output.data[type];
+ },
+ methods: {
+ dataForType(type) {
+ let data = this.output.data[type];
- if (typeof data === 'object') {
- data = data.join('');
- }
+ if (typeof data === 'object') {
+ data = data.join('');
+ }
- return data;
- },
+ return data;
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/notebook/cells/prompt.vue b/app/assets/javascripts/notebook/cells/prompt.vue
index fe1fc37e1dc..d96f701ee3e 100644
--- a/app/assets/javascripts/notebook/cells/prompt.vue
+++ b/app/assets/javascripts/notebook/cells/prompt.vue
@@ -1,23 +1,23 @@
<script>
- export default {
- props: {
- type: {
- type: String,
- required: false,
- default: '',
- },
- count: {
- type: Number,
- required: false,
- default: 0,
- },
+export default {
+ props: {
+ type: {
+ type: String,
+ required: false,
+ default: '',
},
- computed: {
- hasKeys() {
- return this.type !== '' && this.count;
- },
+ count: {
+ type: Number,
+ required: false,
+ default: 0,
},
- };
+ },
+ computed: {
+ hasKeys() {
+ return this.type !== '' && this.count;
+ },
+ },
+};
</script>
<template>
@@ -29,9 +29,9 @@
</template>
<style scoped>
- .prompt {
- padding: 0 10px;
- min-width: 7em;
- font-family: monospace;
- }
+.prompt {
+ padding: 0 10px;
+ min-width: 7em;
+ font-family: monospace;
+}
</style>
diff --git a/app/assets/javascripts/notebook/index.vue b/app/assets/javascripts/notebook/index.vue
index e2e3b08c77f..c5cc8c97dda 100644
--- a/app/assets/javascripts/notebook/index.vue
+++ b/app/assets/javascripts/notebook/index.vue
@@ -1,60 +1,57 @@
<script>
- import {
- MarkdownCell,
- CodeCell,
- } from './cells';
+import { MarkdownCell, CodeCell } from './cells';
- export default {
- components: {
- 'code-cell': CodeCell,
- 'markdown-cell': MarkdownCell,
+export default {
+ components: {
+ 'code-cell': CodeCell,
+ 'markdown-cell': MarkdownCell,
+ },
+ props: {
+ notebook: {
+ type: Object,
+ required: true,
},
- props: {
- notebook: {
- type: Object,
- required: true,
- },
- codeCssClass: {
- type: String,
- required: false,
- default: '',
- },
+ codeCssClass: {
+ type: String,
+ required: false,
+ default: '',
},
- computed: {
- cells() {
- if (this.notebook.worksheets) {
- const data = {
- cells: [],
- };
+ },
+ computed: {
+ cells() {
+ if (this.notebook.worksheets) {
+ const data = {
+ cells: [],
+ };
- return this.notebook.worksheets.reduce((cellData, sheet) => {
- const cellDataCopy = cellData;
- cellDataCopy.cells = cellDataCopy.cells.concat(sheet.cells);
- return cellDataCopy;
- }, data).cells;
- }
+ return this.notebook.worksheets.reduce((cellData, sheet) => {
+ const cellDataCopy = cellData;
+ cellDataCopy.cells = cellDataCopy.cells.concat(sheet.cells);
+ return cellDataCopy;
+ }, data).cells;
+ }
- return this.notebook.cells;
- },
- hasNotebook() {
- return Object.keys(this.notebook).length;
- },
+ return this.notebook.cells;
},
- methods: {
- cellType(type) {
- return `${type}-cell`;
- },
+ hasNotebook() {
+ return Object.keys(this.notebook).length;
},
- };
+ },
+ methods: {
+ cellType(type) {
+ return `${type}-cell`;
+ },
+ },
+};
</script>
<template>
<div v-if="hasNotebook">
<component
- v-for="(cell, index) in cells"
:is="cellType(cell.cell_type)"
- :cell="cell"
+ v-for="(cell, index) in cells"
:key="index"
+ :cell="cell"
:code-css-class="codeCssClass" />
</div>
</template>
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 8124ae6201f..dfb53c986fc 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,5 +1,5 @@
-/* eslint-disable no-restricted-properties, func-names, no-var, wrap-iife, camelcase,
-no-unused-expressions, max-len, one-var, one-var-declaration-per-line, default-case,
+/* eslint-disable no-restricted-properties, func-names, no-var, camelcase,
+no-unused-expressions, one-var, default-case,
prefer-template, consistent-return, no-alert, no-return-assign,
no-param-reassign, prefer-arrow-callback, no-else-return, vars-on-top,
no-unused-vars, no-shadow, no-useless-escape, class-methods-use-this */
@@ -16,7 +16,7 @@ import 'vendor/jquery.atwho';
import AjaxCache from '~/lib/utils/ajax_cache';
import Vue from 'vue';
import syntaxHighlight from '~/syntax_highlight';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import { GlSkeletonLoading } from '@gitlab/ui';
import axios from './lib/utils/axios_utils';
import { getLocationHash } from './lib/utils/url_utility';
import Flash from './flash';
@@ -154,7 +154,11 @@ export default class Notes {
this.$wrapperEl.on('click', '.system-note-commit-list-toggler', this.toggleCommitList);
this.$wrapperEl.on('click', '.js-toggle-lazy-diff', this.loadLazyDiff);
- this.$wrapperEl.on('click', '.js-toggle-lazy-diff-retry-button', this.onClickRetryLazyLoad.bind(this));
+ this.$wrapperEl.on(
+ 'click',
+ '.js-toggle-lazy-diff-retry-button',
+ this.onClickRetryLazyLoad.bind(this),
+ );
// fetch notes when tab becomes visible
this.$wrapperEl.on('visibilitychange', this.visibilityChange);
@@ -252,9 +256,7 @@ export default class Notes {
discussionNoteForm = $textarea.closest('.js-discussion-note-form');
if (discussionNoteForm.length) {
if ($textarea.val() !== '') {
- if (
- !window.confirm('Are you sure you want to cancel creating this comment?')
- ) {
+ if (!window.confirm('Are you sure you want to cancel creating this comment?')) {
return;
}
}
@@ -266,9 +268,7 @@ export default class Notes {
originalText = $textarea.closest('form').data('originalNote');
newText = $textarea.val();
if (originalText !== newText) {
- if (
- !window.confirm('Are you sure you want to cancel editing this comment?')
- ) {
+ if (!window.confirm('Are you sure you want to cancel editing this comment?')) {
return;
}
}
@@ -631,7 +631,7 @@ export default class Notes {
*
* deactivates the submit button when text is empty
* hides the preview button when text is empty
- * setup GFM auto complete
+ * set up GFM auto complete
* show the form
*/
setupNoteForm(form, enableGFM = defaultAutocompleteConfig) {
@@ -954,7 +954,7 @@ export default class Notes {
* Note: dataHolder must have the "discussionId" and "lineCode" data attributes set.
*/
setupDiscussionNoteForm(dataHolder, form) {
- // setup note target
+ // set up note target
let diffFileData = dataHolder.closest('.text-file');
if (diffFileData.length === 0) {
@@ -1036,7 +1036,7 @@ export default class Notes {
$diffFile[0].dispatchEvent(clickEvent);
- // Setup comment form
+ // Set up comment form
let newForm;
const $noteContainer = $link.closest('.diff-viewer').find('.note-container');
const $form = $noteContainer.find('> .discussion-form');
@@ -1074,7 +1074,7 @@ export default class Notes {
addForm = false;
let lineTypeSelector = '';
rowCssToAdd =
- '<tr class="notes_holder js-temp-notes-holder"><td class="notes_line" colspan="2"></td><td class="notes_content"><div class="content"></div></td></tr>';
+ '<tr class="notes_holder js-temp-notes-holder"><td class="notes_content" colspan="3"><div class="content"></div></td></tr>';
// In parallel view, look inside the correct left/right pane
if (this.isParallelView()) {
lineTypeSelector = `.${lineType}`;
@@ -1293,10 +1293,10 @@ export default class Notes {
new Vue({
el,
components: {
- SkeletonLoadingContainer,
+ GlSkeletonLoading,
},
render(createElement) {
- return createElement('skeleton-loading-container');
+ return createElement('gl-skeleton-loading');
},
});
}
@@ -1316,8 +1316,7 @@ export default class Notes {
$retryButton.prop('disabled', true);
- return this.loadLazyDiff(e)
- .then(() => {
+ return this.loadLazyDiff(e).then(() => {
$retryButton.prop('disabled', false);
});
}
@@ -1343,18 +1342,18 @@ export default class Notes {
*/
if (url) {
return axios
- .get(url)
- .then(({ data }) => {
- // Reset state in case last request returned error
- $successContainer.removeClass('hidden');
- $errorContainer.addClass('hidden');
-
- Notes.renderDiffContent($container, data);
- })
- .catch(() => {
- $successContainer.addClass('hidden');
- $errorContainer.removeClass('hidden');
- });
+ .get(url)
+ .then(({ data }) => {
+ // Reset state in case last request returned error
+ $successContainer.removeClass('hidden');
+ $errorContainer.addClass('hidden');
+
+ Notes.renderDiffContent($container, data);
+ })
+ .catch(() => {
+ $successContainer.addClass('hidden');
+ $errorContainer.removeClass('hidden');
+ });
}
return Promise.resolve();
}
@@ -1545,12 +1544,8 @@ export default class Notes {
<div class="note-header">
<div class="note-header-info">
<a href="/${_.escape(currentUsername)}">
- <span class="d-none d-sm-inline-block">${_.escape(
- currentUsername,
- )}</span>
- <span class="note-headline-light">${_.escape(
- currentUsername,
- )}</span>
+ <span class="d-none d-sm-inline-block">${_.escape(currentUsername)}</span>
+ <span class="note-headline-light">${_.escape(currentUsername)}</span>
</a>
</div>
</div>
@@ -1565,9 +1560,7 @@ export default class Notes {
);
$tempNote.find('.d-none.d-sm-inline-block').text(_.escape(currentUserFullname));
- $tempNote
- .find('.note-headline-light')
- .text(`@${_.escape(currentUsername)}`);
+ $tempNote.find('.note-headline-light').text(`@${_.escape(currentUsername)}`);
return $tempNote;
}
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 6612bc44e0b..10e80883c00 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -6,8 +6,11 @@ import Autosize from 'autosize';
import { __, sprintf } from '~/locale';
import Flash from '../../flash';
import Autosave from '../../autosave';
-import TaskList from '../../task_list';
-import { capitalizeFirstCharacter, convertToCamelCase, splitCamelCase } from '../../lib/utils/text_utility';
+import {
+ capitalizeFirstCharacter,
+ convertToCamelCase,
+ splitCamelCase,
+} from '../../lib/utils/text_utility';
import * as constants from '../constants';
import eventHub from '../event_hub';
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
@@ -122,7 +125,9 @@ export default {
return this.getNoteableData.create_note_path;
},
issuableTypeTitle() {
- return this.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE ? 'merge request' : 'issue';
+ return this.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE
+ ? 'merge request'
+ : 'issue';
},
},
watch: {
@@ -140,7 +145,6 @@ export default {
});
this.initAutoSave();
- this.initTaskList();
},
methods: {
...mapActions([
@@ -292,13 +296,6 @@ Please check your network connection and try again.`;
]);
}
},
- initTaskList() {
- return new TaskList({
- dataType: 'note',
- fieldName: 'note',
- selector: '.notes',
- });
- },
resizeTextarea() {
this.$nextTick(() => {
Autosize.update(this.$refs.textarea);
@@ -315,10 +312,10 @@ Please check your network connection and try again.`;
v-else-if="!canCreateNote"
:issuable-type="issuableTypeTitle"
/>
- <ul
+ <div
v-else-if="canCreateNote"
class="notes notes-form timeline">
- <li class="timeline-entry">
+ <div class="timeline-entry note-form">
<div class="timeline-entry-inner">
<div class="flash-container error-alert timeline-content"></div>
<div class="timeline-icon d-none d-sm-none d-md-block">
@@ -359,7 +356,7 @@ Please check your network connection and try again.`;
:disabled="isSubmitting"
name="note[note]"
class="note-textarea js-vue-comment-form js-note-text
-js-gfm-input js-autosize markdown-area js-vue-textarea"
+js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input"
data-supports-quick-actions="true"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
@@ -374,7 +371,8 @@ js-gfm-input js-autosize markdown-area js-vue-textarea"
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown">
<button
:disabled="isSubmitButtonDisabled"
- class="btn btn-create comment-btn js-comment-button js-comment-submit-button"
+ class="btn btn-create comment-btn js-comment-button js-comment-submit-button
+ qa-comment-button"
type="submit"
@click.prevent="handleSave()">
{{ __(commentButtonTitle) }}
@@ -383,7 +381,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
:disabled="isSubmitButtonDisabled"
name="button"
type="button"
- class="btn comment-btn note-type-toggle js-note-new-discussion dropdown-toggle"
+ class="btn comment-btn note-type-toggle js-note-new-discussion dropdown-toggle qa-note-dropdown"
data-display="static"
data-toggle="dropdown"
aria-label="Open comment type dropdown">
@@ -415,7 +413,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
<li :class="{ 'droplab-item-selected': noteType === 'discussion' }">
<button
type="button"
- class="btn btn-transparent"
+ class="btn btn-transparent qa-discussion-option"
@click.prevent="setNoteType('discussion')">
<i
aria-hidden="true"
@@ -455,7 +453,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
</form>
</div>
</div>
- </li>
- </ul>
+ </div>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/notes/components/diff_file_header.vue b/app/assets/javascripts/notes/components/diff_file_header.vue
deleted file mode 100644
index fc7b52be241..00000000000
--- a/app/assets/javascripts/notes/components/diff_file_header.vue
+++ /dev/null
@@ -1,94 +0,0 @@
-<script>
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import Icon from '~/vue_shared/components/icon.vue';
-
-export default {
- components: {
- ClipboardButton,
- Icon,
- },
- props: {
- diffFile: {
- type: Object,
- required: true,
- },
- },
- computed: {
- titleTag() {
- return this.diffFile.discussionPath ? 'a' : 'span';
- },
- },
-};
-</script>
-
-<template>
- <div class="file-header-content">
- <div
- v-if="diffFile.submodule"
- >
- <span>
- <icon name="archive" />
- <strong
- class="file-title-name"
- v-html="diffFile.submoduleLink"
- ></strong>
- <clipboard-button
- :text="diffFile.submoduleLink"
- title="Copy file path to clipboard"
- css-class="btn-default btn-transparent btn-clipboard"
- />
- </span>
- </div>
- <template v-else>
- <component
- ref="titleWrapper"
- :is="titleTag"
- :href="diffFile.discussionPath"
- >
- <span v-html="diffFile.blobIcon"></span>
- <span v-if="diffFile.renamedFile">
- <strong
- :title="diffFile.oldPath"
- class="file-title-name has-tooltip"
- data-container="body"
- >
- {{ diffFile.oldPath }}
- </strong>
- &rarr;
- <strong
- :title="diffFile.newPath"
- class="file-title-name has-tooltip"
- data-container="body"
- >
- {{ diffFile.newPath }}
- </strong>
- </span>
-
- <strong
- v-else
- :title="diffFile.oldPath"
- class="file-title-name has-tooltip"
- data-container="body"
- >
- {{ diffFile.filePath }}
- <span v-if="diffFile.deletedFile">
- deleted
- </span>
- </strong>
- </component>
-
- <clipboard-button
- :text="diffFile.filePath"
- title="Copy file path to clipboard"
- css-class="btn-default btn-transparent btn-clipboard"
- />
-
- <small
- v-if="diffFile.modeChanged"
- ref="fileMode"
- >
- {{ diffFile.aMode }} → {{ diffFile.bMode }}
- </small>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 27ff7dea909..30fcb895369 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -1,15 +1,17 @@
<script>
import { mapState, mapActions } from 'vuex';
-import imageDiffHelper from '~/image_diff/helpers/index';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
+import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
+import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
+import { GlSkeletonLoading } from '@gitlab/ui';
+import { trimFirstCharOfLineContent, getDiffMode } from '~/diffs/store/utils';
export default {
components: {
DiffFileHeader,
- SkeletonLoadingContainer,
+ GlSkeletonLoading,
+ DiffViewer,
+ ImageDiffOverlay,
},
props: {
discussion: {
@@ -25,9 +27,15 @@ export default {
computed: {
...mapState({
noteableData: state => state.notes.noteableData,
+ projectPath: state => state.diffs.projectPath,
}),
+ diffMode() {
+ return getDiffMode(this.diffFile);
+ },
hasTruncatedDiffLines() {
- return this.discussion.truncatedDiffLines && this.discussion.truncatedDiffLines.length !== 0;
+ return (
+ this.discussion.truncated_diff_lines && this.discussion.truncated_diff_lines.length !== 0
+ );
},
isDiscussionsExpanded() {
return true; // TODO: @fatihacet - Fix this.
@@ -43,30 +51,24 @@ export default {
return text ? 'text-file' : 'js-image-file';
},
diffFile() {
- return convertObjectPropsToCamelCase(this.discussion.diffFile, { deep: true });
+ return this.discussion.diff_file;
},
imageDiffHtml() {
- return this.discussion.imageDiffHtml;
+ return this.discussion.image_diff_html;
},
userColorScheme() {
return window.gon.user_color_scheme;
},
normalizedDiffLines() {
- if (this.discussion.truncatedDiffLines) {
- return this.discussion.truncatedDiffLines.map(line =>
- trimFirstCharOfLineContent(convertObjectPropsToCamelCase(line)),
- );
+ if (this.discussion.truncated_diff_lines) {
+ return this.discussion.truncated_diff_lines.map(line => trimFirstCharOfLineContent(line));
}
return [];
},
},
mounted() {
- if (this.isImageDiff) {
- const canCreateNote = false;
- const renderCommentBadge = true;
- imageDiffHelper.initImageDiff(this.$refs.fileHolder, canCreateNote, renderCommentBadge);
- } else if (!this.hasTruncatedDiffLines) {
+ if (!this.hasTruncatedDiffLines) {
this.fetchDiff();
}
},
@@ -94,6 +96,7 @@ export default {
class="diff-file file-holder"
>
<diff-file-header
+ :discussion-path="discussion.discussion_path"
:diff-file="diffFile"
:can-current-user-fork="false"
:discussions-expanded="isDiscussionsExpanded"
@@ -107,15 +110,15 @@ export default {
<table>
<tr
v-for="line in normalizedDiffLines"
- :key="line.lineCode"
+ :key="line.line_code"
class="line_holder"
>
- <td class="diff-line-num old_line">{{ line.oldLine }}</td>
- <td class="diff-line-num new_line">{{ line.newLine }}</td>
+ <td class="diff-line-num old_line">{{ line.old_line }}</td>
+ <td class="diff-line-num new_line">{{ line.new_line }}</td>
<td
:class="line.type"
class="line_content"
- v-html="line.richText"
+ v-html="line.rich_text"
>
</td>
</tr>
@@ -142,16 +145,15 @@ export default {
class="line_content js-success-lazy-load"
>
<span></span>
- <skeleton-loading-container />
+ <gl-skeleton-loading />
<span></span>
</td>
</tr>
<tr class="notes_holder">
<td
- class="notes_line"
- colspan="2"
- ></td>
- <td class="notes_content">
+ class="notes_content"
+ colspan="3"
+ >
<slot></slot>
</td>
</tr>
@@ -160,7 +162,24 @@ export default {
<div
v-else
>
- <div v-html="imageDiffHtml"></div>
+ <diff-viewer
+ :diff-mode="diffMode"
+ :new-path="diffFile.new_path"
+ :new-sha="diffFile.diff_refs.head_sha"
+ :old-path="diffFile.old_path"
+ :old-sha="diffFile.diff_refs.base_sha"
+ :file-hash="diffFile.file_hash"
+ :project-path="projectPath"
+ >
+ <image-diff-overlay
+ slot="image-overlay"
+ :discussions="discussion"
+ :file-hash="diffFile.file_hash"
+ :show-comment-icon="true"
+ :should-toggle-discussion="false"
+ badge-class="image-comment-badge"
+ />
+ </diff-viewer>
<slot></slot>
</div>
</div>
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index ad6e7cf501d..a4d76a70696 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -1,9 +1,6 @@
<script>
import { mapActions, mapGetters } from 'vuex';
-import resolveSvg from 'icons/_icon_resolve_discussion.svg';
-import resolvedSvg from 'icons/_icon_status_success_solid.svg';
-import mrIssueSvg from 'icons/_icon_mr_issue.svg';
-import nextDiscussionSvg from 'icons/_next_discussion.svg';
+import Icon from '~/vue_shared/components/icon.vue';
import { pluralize } from '../../lib/utils/text_utility';
import discussionNavigation from '../mixins/discussion_navigation';
import tooltip from '../../vue_shared/directives/tooltip';
@@ -12,6 +9,9 @@ export default {
directives: {
tooltip,
},
+ components: {
+ Icon,
+ },
mixins: [discussionNavigation],
computed: {
...mapGetters([
@@ -37,12 +37,6 @@ export default {
return this.getNoteableData.create_issue_to_resolve_discussions_path;
},
},
- created() {
- this.resolveSvg = resolveSvg;
- this.resolvedSvg = resolvedSvg;
- this.mrIssueSvg = mrIssueSvg;
- this.nextDiscussionSvg = nextDiscussionSvg;
- },
methods: {
...mapActions(['expandDiscussion']),
jumpToFirstUnresolvedDiscussion() {
@@ -56,24 +50,19 @@ export default {
</script>
<template>
- <div class="line-resolve-all-container prepend-top-10">
+ <div
+ v-if="discussionCount > 0"
+ class="line-resolve-all-container prepend-top-8">
<div>
<div
- v-if="discussionCount > 0"
:class="{ 'has-next-btn': hasNextButton }"
class="line-resolve-all">
<span
:class="{ 'is-active': allResolved }"
class="line-resolve-btn is-disabled"
- type="button">
- <span
- v-if="allResolved"
- v-html="resolvedSvg"
- ></span>
- <span
- v-else
- v-html="resolveSvg"
- ></span>
+ type="button"
+ >
+ <icon name="check-circle" />
</span>
<span class="line-resolve-text">
{{ resolvedDiscussionCount }}/{{ discussionCount }} {{ countText }} resolved
@@ -89,7 +78,7 @@ export default {
:title="s__('Resolve all discussions in new issue')"
data-container="body"
class="new-issue-for-discussion btn btn-default discussion-create-issue-btn">
- <span v-html="mrIssueSvg"></span>
+ <icon name="issue-new" />
</a>
</div>
<div
@@ -102,7 +91,7 @@ export default {
data-container="body"
class="btn btn-default discussion-next-btn"
@click="jumpToFirstUnresolvedDiscussion">
- <span v-html="nextDiscussionSvg"></span>
+ <icon name="comment-next" />
</button>
</div>
</div>
diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue
new file mode 100644
index 00000000000..affa2d1b574
--- /dev/null
+++ b/app/assets/javascripts/notes/components/discussion_filter.vue
@@ -0,0 +1,96 @@
+<script>
+import $ from 'jquery';
+import { mapGetters, mapActions } from 'vuex';
+import Icon from '~/vue_shared/components/icon.vue';
+import { DISCUSSION_FILTERS_DEFAULT_VALUE, HISTORY_ONLY_FILTER_VALUE } from '../constants';
+
+export default {
+ components: {
+ Icon,
+ },
+ props: {
+ filters: {
+ type: Array,
+ required: true,
+ },
+ selectedValue: {
+ type: Number,
+ default: null,
+ required: false,
+ },
+ },
+ data() {
+ return {
+ currentValue: this.selectedValue,
+ defaultValue: DISCUSSION_FILTERS_DEFAULT_VALUE,
+ };
+ },
+ computed: {
+ ...mapGetters(['getNotesDataByProp']),
+ currentFilter() {
+ if (!this.currentValue) return this.filters[0];
+ return this.filters.find(filter => filter.value === this.currentValue);
+ },
+ },
+ mounted() {
+ this.toggleCommentsForm();
+ },
+ methods: {
+ ...mapActions(['filterDiscussion', 'setCommentsDisabled']),
+ selectFilter(value) {
+ const filter = parseInt(value, 10);
+
+ // close dropdown
+ $(this.$refs.dropdownToggle).dropdown('toggle');
+
+ if (filter === this.currentValue) return;
+ this.currentValue = filter;
+ this.filterDiscussion({ path: this.getNotesDataByProp('discussionsPath'), filter });
+ this.toggleCommentsForm();
+ },
+ toggleCommentsForm() {
+ this.setCommentsDisabled(this.currentValue === HISTORY_ONLY_FILTER_VALUE);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="discussion-filter-container d-inline-block align-bottom">
+ <button
+ id="discussion-filter-dropdown"
+ ref="dropdownToggle"
+ class="btn btn-default qa-discussion-filter"
+ data-toggle="dropdown"
+ aria-expanded="false"
+ >
+ {{ currentFilter.title }}
+ <icon name="chevron-down" />
+ </button>
+ <div
+ class="dropdown-menu dropdown-menu-selectable dropdown-menu-right"
+ aria-labelledby="discussion-filter-dropdown">
+ <div class="dropdown-content">
+ <ul>
+ <li
+ v-for="filter in filters"
+ :key="filter.value"
+ >
+ <button
+ :class="{ 'is-active': filter.value === currentValue }"
+ class="qa-filter-options"
+ type="button"
+ @click="selectFilter(filter.value)"
+ >
+ {{ filter.title }}
+ </button>
+ <div
+ v-if="filter.value === defaultValue"
+ class="dropdown-divider"
+ ></div>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index cdbbb342331..0f0d7f4661a 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -1,35 +1,31 @@
<script>
import { mapGetters } from 'vuex';
-import emojiSmiling from 'icons/_emoji_slightly_smiling_face.svg';
-import emojiSmile from 'icons/_emoji_smile.svg';
-import emojiSmiley from 'icons/_emoji_smiley.svg';
-import editSvg from 'icons/_icon_pencil.svg';
-import resolveDiscussionSvg from 'icons/_icon_resolve_discussion.svg';
-import resolvedDiscussionSvg from 'icons/_icon_status_success_solid.svg';
-import ellipsisSvg from 'icons/_ellipsis_v.svg';
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
+import { GlLoadingIcon } from '@gitlab/ui';
export default {
name: 'NoteActions',
+ components: {
+ Icon,
+ GlLoadingIcon,
+ },
directives: {
tooltip,
},
- components: {
- loadingIcon,
- },
props: {
authorId: {
type: Number,
required: true,
},
noteId: {
- type: Number,
+ type: [String, Number],
required: true,
},
noteUrl: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
accessLevel: {
type: String,
@@ -38,7 +34,8 @@ export default {
},
reportAbusePath: {
type: String,
- required: true,
+ required: false,
+ default: null,
},
canEdit: {
type: Boolean,
@@ -87,6 +84,9 @@ export default {
shouldShowActionsDropdown() {
return this.currentUserId && (this.canEdit || this.canReportAsAbuse);
},
+ showDeleteAction() {
+ return this.canDelete && !this.canReportAsAbuse && !this.noteUrl;
+ },
isAuthoredByCurrentUser() {
return this.authorId === this.currentUserId;
},
@@ -103,15 +103,6 @@ export default {
return title;
},
},
- created() {
- this.emojiSmiling = emojiSmiling;
- this.emojiSmile = emojiSmile;
- this.emojiSmiley = emojiSmiley;
- this.editSvg = editSvg;
- this.ellipsisSvg = ellipsisSvg;
- this.resolveDiscussionSvg = resolveDiscussionSvg;
- this.resolvedDiscussionSvg = resolvedDiscussionSvg;
- },
methods: {
onEdit() {
this.$emit('handleEdit');
@@ -145,16 +136,11 @@ export default {
class="line-resolve-btn note-action-button"
@click="onResolve">
<template v-if="!isResolving">
- <div
- v-if="isResolved"
- v-html="resolvedDiscussionSvg"></div>
- <div
- v-else
- v-html="resolveDiscussionSvg"></div>
+ <icon name="check-circle" />
</template>
- <loading-icon
+ <gl-loading-icon
v-else
- :inline="true"
+ inline
/>
</button>
</div>
@@ -171,19 +157,19 @@ export default {
href="#"
title="Add reaction"
>
- <loading-icon :inline="true" />
- <span
- class="link-highlight award-control-icon-neutral"
- v-html="emojiSmiling">
- </span>
- <span
- class="link-highlight award-control-icon-positive"
- v-html="emojiSmiley">
- </span>
- <span
- class="link-highlight award-control-icon-super-positive"
- v-html="emojiSmile">
- </span>
+ <gl-loading-icon inline/>
+ <icon
+ css-classes="link-highlight award-control-icon-neutral"
+ name="emoji_slightly_smiling_face"
+ />
+ <icon
+ css-classes="link-highlight award-control-icon-positive"
+ name="emoji_smiley"
+ />
+ <icon
+ css-classes="link-highlight award-control-icon-super-positive"
+ name="emoji_smiley"
+ />
</a>
</div>
<div
@@ -197,14 +183,33 @@ export default {
data-container="body"
data-placement="bottom"
@click="onEdit">
- <span
+ <icon
+ name="pencil"
+ css-classes="link-highlight"
+ />
+ </button>
+ </div>
+ <div
+ v-if="showDeleteAction"
+ class="note-actions-item"
+ >
+ <button
+ v-tooltip
+ type="button"
+ title="Delete comment"
+ class="note-action-button js-note-delete btn btn-transparent"
+ data-container="body"
+ data-placement="bottom"
+ @click="onDelete"
+ >
+ <icon
+ name="remove"
class="link-highlight"
- v-html="editSvg">
- </span>
+ />
</button>
</div>
<div
- v-if="shouldShowActionsDropdown"
+ v-else-if="shouldShowActionsDropdown"
class="dropdown more-actions note-actions-item">
<button
v-tooltip
@@ -214,24 +219,24 @@ export default {
data-toggle="dropdown"
data-container="body"
data-placement="bottom">
- <span
- class="icon"
- v-html="ellipsisSvg">
- </span>
+ <icon
+ css-classes="icon"
+ name="ellipsis_v"
+ />
</button>
<ul class="dropdown-menu more-actions-dropdown dropdown-open-left">
<li v-if="canReportAsAbuse">
<a :href="reportAbusePath">
- Report as abuse
+ {{ __('Report abuse to GitLab') }}
</a>
</li>
- <li>
+ <li v-if="noteUrl">
<button
:data-clipboard-text="noteUrl"
type="button"
- css-class="btn-default btn-transparent"
+ class="btn-default btn-transparent js-btn-copy-note-link"
>
- Copy link
+ {{ __('Copy link') }}
</button>
</li>
<li v-if="canEdit">
@@ -240,7 +245,7 @@ export default {
type="button"
@click.prevent="onDelete">
<span class="text-danger">
- Delete comment
+ {{ __('Delete comment') }}
</span>
</button>
</li>
diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue
index 225d9f18612..401bcfabbe4 100644
--- a/app/assets/javascripts/notes/components/note_awards_list.vue
+++ b/app/assets/javascripts/notes/components/note_awards_list.vue
@@ -1,13 +1,14 @@
<script>
import { mapActions, mapGetters } from 'vuex';
-import emojiSmiling from 'icons/_emoji_slightly_smiling_face.svg';
-import emojiSmile from 'icons/_emoji_smile.svg';
-import emojiSmiley from 'icons/_emoji_smiley.svg';
+import Icon from '~/vue_shared/components/icon.vue';
import Flash from '../../flash';
import { glEmojiTag } from '../../emoji';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
+ components: {
+ Icon,
+ },
directives: {
tooltip,
},
@@ -25,7 +26,7 @@ export default {
required: true,
},
noteId: {
- type: Number,
+ type: String,
required: true,
},
canAwardEmoji: {
@@ -72,66 +73,40 @@ export default {
return this.noteAuthorId === this.getUserData.id;
},
},
- created() {
- this.emojiSmiling = emojiSmiling;
- this.emojiSmile = emojiSmile;
- this.emojiSmiley = emojiSmiley;
- },
methods: {
...mapActions(['toggleAwardRequest']),
getAwardHTML(name) {
return glEmojiTag(name);
},
- getAwardClassBindings(awardList, awardName) {
+ getAwardClassBindings(awardList) {
return {
active: this.hasReactionByCurrentUser(awardList),
- disabled: !this.canInteractWithEmoji(awardList, awardName),
+ disabled: !this.canInteractWithEmoji(),
};
},
- canInteractWithEmoji(awardList, awardName) {
- let isAllowed = true;
- const restrictedEmojis = ['thumbsup', 'thumbsdown'];
-
- // Users can not add :+1: and :-1: to their own notes
- if (
- this.getUserData.id === this.noteAuthorId &&
- restrictedEmojis.indexOf(awardName) > -1
- ) {
- isAllowed = false;
- }
-
- return this.getUserData.id && isAllowed;
+ canInteractWithEmoji() {
+ return this.getUserData.id;
},
hasReactionByCurrentUser(awardList) {
- return awardList.filter(award => award.user.id === this.getUserData.id)
- .length;
+ return awardList.filter(award => award.user.id === this.getUserData.id).length;
},
awardTitle(awardsList) {
- const hasReactionByCurrentUser = this.hasReactionByCurrentUser(
- awardsList,
- );
+ const hasReactionByCurrentUser = this.hasReactionByCurrentUser(awardsList);
const TOOLTIP_NAME_COUNT = hasReactionByCurrentUser ? 9 : 10;
let awardList = awardsList;
// Filter myself from list if I am awarded.
if (hasReactionByCurrentUser) {
- awardList = awardList.filter(
- award => award.user.id !== this.getUserData.id,
- );
+ awardList = awardList.filter(award => award.user.id !== this.getUserData.id);
}
// Get only 9-10 usernames to show in tooltip text.
- const namesToShow = awardList
- .slice(0, TOOLTIP_NAME_COUNT)
- .map(award => award.user.name);
+ const namesToShow = awardList.slice(0, TOOLTIP_NAME_COUNT).map(award => award.user.name);
// Get the remaining list to use in `and x more` text.
- const remainingAwardList = awardList.slice(
- TOOLTIP_NAME_COUNT,
- awardList.length,
- );
+ const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length);
- // Add myself to the begining of the list so title will start with You.
+ // Add myself to the beginning of the list so title will start with You.
if (hasReactionByCurrentUser) {
namesToShow.unshift('You');
}
@@ -140,9 +115,7 @@ export default {
// We have 10+ awarded user, join them with comma and add `and x more`.
if (remainingAwardList.length) {
- title = `${namesToShow.join(', ')}, and ${
- remainingAwardList.length
- } more.`;
+ title = `${namesToShow.join(', ')}, and ${remainingAwardList.length} more.`;
} else if (namesToShow.length > 1) {
// Join all names with comma but not the last one, it will be added with and text.
title = namesToShow.slice(0, namesToShow.length - 1).join(', ');
@@ -182,9 +155,7 @@ export default {
awardName: parsedName,
};
- this.toggleAwardRequest(data).catch(() =>
- Flash('Something went wrong on our end.'),
- );
+ this.toggleAwardRequest(data).catch(() => Flash('Something went wrong on our end.'));
},
},
};
@@ -194,10 +165,10 @@ export default {
<div class="note-awards">
<div class="awards js-awards-block">
<button
- v-tooltip
v-for="(awardList, awardName, index) in groupedAwards"
:key="index"
- :class="getAwardClassBindings(awardList, awardName)"
+ v-tooltip
+ :class="getAwardClassBindings(awardList)"
:title="awardTitle(awardList)"
class="btn award-control"
data-boundary="viewport"
@@ -221,17 +192,14 @@ export default {
data-boundary="viewport"
data-placement="bottom"
type="button">
- <span
- class="award-control-icon award-control-icon-neutral"
- v-html="emojiSmiling">
+ <span class="award-control-icon award-control-icon-neutral">
+ <icon name="emoji_slightly_smiling_face" />
</span>
- <span
- class="award-control-icon award-control-icon-positive"
- v-html="emojiSmiley">
+ <span class="award-control-icon award-control-icon-positive">
+ <icon name="emoji_smiley" />
</span>
- <span
- class="award-control-icon award-control-icon-super-positive"
- v-html="emojiSmile">
+ <span class="award-control-icon award-control-icon-super-positive">
+ <icon name="emoji_smiley" />
</span>
<i
aria-hidden="true"
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index 6f4a0709825..9375627359c 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -4,7 +4,6 @@ import noteEditedText from './note_edited_text.vue';
import noteAwardsList from './note_awards_list.vue';
import noteAttachment from './note_attachment.vue';
import noteForm from './note_form.vue';
-import TaskList from '../../task_list';
import autosave from '../mixins/autosave';
export default {
@@ -37,14 +36,12 @@ export default {
},
mounted() {
this.renderGFM();
- this.initTaskList();
if (this.isEditing) {
this.initAutoSave(this.note);
}
},
updated() {
- this.initTaskList();
this.renderGFM();
if (this.isEditing) {
@@ -59,15 +56,6 @@ export default {
renderGFM() {
$(this.$refs['note-body']).renderGFM();
},
- initTaskList() {
- if (this.canEdit) {
- this.taskList = new TaskList({
- dataType: 'note',
- fieldName: 'note',
- selector: '.notes',
- });
- }
- },
handleFormUpdate(note, parentElement, callback) {
this.$emit('handleFormUpdate', note, parentElement, callback);
},
@@ -109,7 +97,7 @@ export default {
class="note_edited_ago"
/>
<note-awards-list
- v-if="note.award_emoji.length"
+ v-if="note.award_emoji && note.award_emoji.length"
:note-id="note.id"
:note-author-id="note.author.id"
:awards="note.award_emoji"
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index abcd4422d7c..31ee8fed984 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -20,9 +20,9 @@ export default {
default: '',
},
noteId: {
- type: Number,
+ type: [String, Number],
required: false,
- default: 0,
+ default: '',
},
markdownVersion: {
type: Number,
@@ -67,7 +67,10 @@ export default {
'getUserDataByProp',
]),
noteHash() {
- return `#note_${this.noteId}`;
+ if (this.noteId) {
+ return `#note_${this.noteId}`;
+ }
+ return '#';
},
markdownPreviewPath() {
return this.getNoteableDataByProp('preview_note_path');
@@ -99,6 +102,18 @@ export default {
},
methods: {
...mapActions(['toggleResolveNote']),
+ shouldToggleResolved(shouldResolve, beforeSubmitDiscussionState) {
+ // shouldBeResolved() checks the actual resolution state,
+ // considering batchComments (EEP), if applicable/enabled.
+ const newResolvedStateAfterUpdate =
+ this.shouldBeResolved && this.shouldBeResolved(shouldResolve);
+
+ const shouldToggleState =
+ newResolvedStateAfterUpdate !== undefined &&
+ beforeSubmitDiscussionState !== newResolvedStateAfterUpdate;
+
+ return shouldResolve || shouldToggleState;
+ },
handleUpdate(shouldResolve) {
const beforeSubmitDiscussionState = this.discussionResolved;
this.isSubmitting = true;
@@ -106,7 +121,7 @@ export default {
this.$emit('handleFormUpdate', this.updatedNoteBody, this.$refs.editNoteForm, () => {
this.isSubmitting = false;
- if (shouldResolve) {
+ if (this.shouldToggleResolved(shouldResolve, beforeSubmitDiscussionState)) {
this.resolveHandler(beforeSubmitDiscussionState);
}
});
@@ -168,11 +183,11 @@ export default {
id="note_note"
ref="textarea"
slot="textarea"
- :data-supports-quick-actions="!isEditing"
v-model="updatedNoteBody"
+ :data-supports-quick-actions="!isEditing"
name="note[note]"
class="note-textarea js-gfm-input js-note-text
-js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
+js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
@keydown.meta.enter="handleUpdate()"
@@ -185,7 +200,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<button
:disabled="isDisabled"
type="button"
- class="js-vue-issue-save btn btn-save js-comment-button "
+ class="js-vue-issue-save btn btn-success js-comment-button "
@click="handleUpdate()">
{{ saveButtonTitle }}
</button>
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index a621418cf72..dd7313d7b10 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -9,11 +9,13 @@ export default {
props: {
author: {
type: Object,
- required: true,
+ required: false,
+ default: () => ({}),
},
createdAt: {
type: String,
- required: true,
+ required: false,
+ default: null,
},
actionText: {
type: String,
@@ -21,8 +23,9 @@ export default {
default: '',
},
noteId: {
- type: Number,
- required: true,
+ type: [String, Number],
+ required: false,
+ default: null,
},
includeToggle: {
type: Boolean,
@@ -42,6 +45,9 @@ export default {
noteTimestampLink() {
return `#note_${this.noteId}`;
},
+ hasAuthor() {
+ return this.author && Object.keys(this.author).length;
+ },
},
methods: {
...mapActions(['setTargetNoteHash']),
@@ -72,7 +78,10 @@ export default {
{{ __('Toggle discussion') }}
</button>
</div>
- <a :href="author.path">
+ <a
+ v-if="hasAuthor"
+ :href="author.path"
+ >
<span class="note-header-author-name">{{ author.name }}</span>
<span
v-if="author.status_tooltip_html"
@@ -81,26 +90,32 @@ export default {
@{{ author.username }}
</span>
</a>
+ <span v-else>
+ {{ __('A deleted user') }}
+ </span>
<span class="note-headline-light">
<span class="note-headline-meta">
- <template v-if="actionText">
- {{ actionText }}
- </template>
<span class="system-note-message">
<slot></slot>
</span>
- <span class="system-note-separator">
- &middot;
- </span>
- <a
- :href="noteTimestampLink"
- class="note-timestamp system-note-separator"
- @click="updateTargetNoteHash">
- <time-ago-tooltip
- :time="createdAt"
- tooltip-placement="bottom"
- />
- </a>
+ <template
+ v-if="createdAt"
+ >
+ <span class="system-note-separator">
+ <template v-if="actionText">
+ {{ actionText }}
+ </template>
+ </span>
+ <a
+ :href="noteTimestampLink"
+ class="note-timestamp system-note-separator"
+ @click="updateTargetNoteHash">
+ <time-ago-tooltip
+ :time="createdAt"
+ tooltip-placement="bottom"
+ />
+ </a>
+ </template>
<i
class="fa fa-spinner fa-spin editing-spinner"
aria-label="Comment is being updated"
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 0fe1c16854a..7740967ccd5 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -1,16 +1,15 @@
<script>
import { mapActions, mapGetters } from 'vuex';
-import resolveDiscussionsSvg from 'icons/_icon_mr_issue.svg';
-import nextDiscussionsSvg from 'icons/_next_discussion.svg';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { truncateSha } from '~/lib/utils/text_utility';
-import systemNote from '~/vue_shared/components/notes/system_note.vue';
import { s__ } from '~/locale';
+import systemNote from '~/vue_shared/components/notes/system_note.vue';
+import icon from '~/vue_shared/components/icon.vue';
import Flash from '../../flash';
import { SYSTEM_NOTE } from '../constants';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import noteableNote from './noteable_note.vue';
import noteHeader from './note_header.vue';
+import toggleRepliesWidget from './toggle_replies_widget.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
import noteEditedText from './note_edited_text.vue';
import noteForm from './note_form.vue';
@@ -26,6 +25,7 @@ import tooltip from '../../vue_shared/directives/tooltip';
export default {
name: 'NoteableDiscussion',
components: {
+ icon,
noteableNote,
diffWithNote,
userAvatarLink,
@@ -33,6 +33,7 @@ export default {
noteSignedOutWidget,
noteEditedText,
noteForm,
+ toggleRepliesWidget,
placeholderNote,
placeholderSystemNote,
systemNote,
@@ -46,11 +47,6 @@ export default {
type: Object,
required: true,
},
- renderHeader: {
- type: Boolean,
- required: false,
- default: true,
- },
renderDiffFile: {
type: Boolean,
required: false,
@@ -72,6 +68,7 @@ export default {
isReplying: false,
isResolving: false,
resolveAsThread: true,
+ isRepliesCollapsed: (!this.discussion.diff_discussion && this.discussion.resolved) || false,
};
},
computed: {
@@ -90,17 +87,16 @@ export default {
transformedDiscussion() {
return {
...this.discussion.notes[0],
- truncatedDiffLines: this.discussion.truncated_diff_lines || [],
- truncatedDiffLinesPath: this.discussion.truncated_diff_lines_path,
- diffFile: this.discussion.diff_file,
- diffDiscussion: this.discussion.diff_discussion,
- imageDiffHtml: this.discussion.image_diff_html,
+ truncated_diff_lines: this.discussion.truncated_diff_lines || [],
+ truncated_diff_lines_path: this.discussion.truncated_diff_lines_path,
+ diff_file: this.discussion.diff_file,
+ diff_discussion: this.discussion.diff_discussion,
active: this.discussion.active,
- discussionPath: this.discussion.discussion_path,
+ discussion_path: this.discussion.discussion_path,
resolved: this.discussion.resolved,
- resolvedBy: this.discussion.resolved_by,
- resolvedByPush: this.discussion.resolved_by_push,
- resolvedAt: this.discussion.resolved_at,
+ resolved_by: this.discussion.resolved_by,
+ resolved_by_push: this.discussion.resolved_by_push,
+ resolved_at: this.discussion.resolved_at,
};
},
author() {
@@ -112,6 +108,15 @@ export default {
newNotePath() {
return this.getNoteableData.create_note_path;
},
+ hasReplies() {
+ return this.discussion.notes.length > 1;
+ },
+ initialDiscussion() {
+ return this.discussion.notes.slice(0, 1)[0];
+ },
+ replies() {
+ return this.discussion.notes.slice(1);
+ },
lastUpdatedBy() {
const { notes } = this.discussion;
@@ -131,26 +136,36 @@ export default {
return null;
},
resolvedText() {
- return this.transformedDiscussion.resolvedByPush ? 'Automatically resolved' : 'Resolved';
+ return this.transformedDiscussion.resolved_by_push ? 'Automatically resolved' : 'Resolved';
},
hasMultipleUnresolvedDiscussions() {
return this.unresolvedDiscussions.length > 1;
},
showJumpToNextDiscussion() {
- return this.hasMultipleUnresolvedDiscussions &&
- !this.isLastUnresolvedDiscussion(this.discussion.id, this.discussionsByDiffOrder);
+ return (
+ this.hasMultipleUnresolvedDiscussions &&
+ !this.isLastUnresolvedDiscussion(this.discussion.id, this.discussionsByDiffOrder)
+ );
},
shouldRenderDiffs() {
- const { diffDiscussion, diffFile } = this.transformedDiscussion;
-
- return diffDiscussion && diffFile && this.renderDiffFile;
+ return (
+ this.transformedDiscussion.diff_discussion &&
+ this.transformedDiscussion.diff_file &&
+ this.renderDiffFile
+ );
+ },
+ shouldGroupReplies() {
+ return !this.shouldRenderDiffs && !this.transformedDiscussion.diff_discussion;
+ },
+ shouldRenderHeader() {
+ return this.shouldRenderDiffs;
},
wrapperComponent() {
return this.shouldRenderDiffs ? diffWithNote : 'div';
},
wrapperComponentProps() {
if (this.shouldRenderDiffs) {
- return { discussion: convertObjectPropsToCamelCase(this.discussion) };
+ return { discussion: this.discussion };
}
return {};
@@ -158,6 +173,22 @@ export default {
wrapperClass() {
return this.isDiffDiscussion ? '' : 'card discussion-wrapper';
},
+ componentClassName() {
+ if (this.shouldRenderDiffs) {
+ if (!this.lastUpdatedAt && !this.discussion.resolved) {
+ return 'unresolved';
+ }
+ }
+
+ return '';
+ },
+ shouldShowDiscussions() {
+ const isExpanded = this.discussion.expanded;
+ const { resolved } = this.transformedDiscussion;
+ const isResolvedNonDiffDiscussion = !this.transformedDiscussion.diff_discussion && resolved;
+
+ return isExpanded || this.alwaysExpanded || isResolvedNonDiffDiscussion;
+ },
},
watch: {
isReplying() {
@@ -171,10 +202,6 @@ export default {
}
},
},
- created() {
- this.resolveDiscussionsSvg = resolveDiscussionsSvg;
- this.nextDiscussionsSvg = nextDiscussionsSvg;
- },
methods: {
...mapActions([
'saveNote',
@@ -189,6 +216,7 @@ export default {
if (note.placeholderType === SYSTEM_NOTE) {
return placeholderSystemNote;
}
+
return placeholderNote;
}
@@ -199,11 +227,14 @@ export default {
return noteableNote;
},
componentData(note) {
- return note.isPlaceholderNote ? this.discussion.notes[0] : note;
+ return note.isPlaceholderNote ? note.notes[0] : note;
},
toggleDiscussionHandler() {
this.toggleDiscussion({ discussionId: this.discussion.id });
},
+ toggleReplies() {
+ this.isRepliesCollapsed = !this.isRepliesCollapsed;
+ },
showReplyForm() {
this.isReplying = true;
},
@@ -256,35 +287,44 @@ Please check your network connection and try again.`;
});
},
jumpToNextDiscussion() {
- const nextId =
- this.nextUnresolvedDiscussionId(this.discussion.id, this.discussionsByDiffOrder);
+ const nextId = this.nextUnresolvedDiscussionId(
+ this.discussion.id,
+ this.discussionsByDiffOrder,
+ );
this.jumpToDiscussion(nextId);
},
+ deleteNoteHandler(note) {
+ this.$emit('noteDeleted', this.discussion, note);
+ },
},
};
</script>
<template>
- <li class="note note-discussion timeline-entry">
+ <li
+ class="note note-discussion timeline-entry"
+ :class="componentClassName"
+ >
<div class="timeline-entry-inner">
- <div class="timeline-icon">
- <user-avatar-link
- :link-href="author.path"
- :img-src="author.avatar_url"
- :img-alt="author.name"
- :img-size="40"
- />
- </div>
<div class="timeline-content">
<div
:data-discussion-id="transformedDiscussion.discussion_id"
class="discussion js-discussion-container"
>
<div
- v-if="renderHeader"
- class="discussion-header"
+ v-if="shouldRenderHeader"
+ class="discussion-header note-wrapper"
>
+ <div class="timeline-icon">
+ <user-avatar-link
+ v-if="author"
+ :link-href="author.path"
+ :img-src="author.avatar_url"
+ :img-alt="author.name"
+ :img-size="40"
+ />
+ </div>
<note-header
:author="author"
:created-at="transformedDiscussion.created_at"
@@ -293,9 +333,9 @@ Please check your network connection and try again.`;
:expanded="discussion.expanded"
@toggleHandler="toggleDiscussionHandler"
>
- <template v-if="transformedDiscussion.diffDiscussion">
+ <template v-if="transformedDiscussion.diff_discussion">
started a discussion on
- <a :href="transformedDiscussion.discussionPath">
+ <a :href="transformedDiscussion.discussion_path">
<template v-if="transformedDiscussion.active">
the diff
</template>
@@ -316,8 +356,8 @@ Please check your network connection and try again.`;
</note-header>
<note-edited-text
v-if="transformedDiscussion.resolved"
- :edited-at="transformedDiscussion.resolvedAt"
- :edited-by="transformedDiscussion.resolvedBy"
+ :edited-at="transformedDiscussion.resolved_at"
+ :edited-by="transformedDiscussion.resolved_by"
:action-text="resolvedText"
class-name="discussion-headline-light js-discussion-headline"
/>
@@ -330,7 +370,7 @@ Please check your network connection and try again.`;
/>
</div>
<div
- v-if="discussion.expanded || alwaysExpanded"
+ v-if="shouldShowDiscussions"
class="discussion-body">
<component
:is="wrapperComponent"
@@ -339,37 +379,70 @@ Please check your network connection and try again.`;
>
<div class="discussion-notes">
<ul class="notes">
- <component
- v-for="note in discussion.notes"
- :is="componentName(note)"
- :note="componentData(note)"
- :key="note.id"
- />
+ <template v-if="shouldGroupReplies">
+ <component
+ :is="componentName(initialDiscussion)"
+ :note="componentData(initialDiscussion)"
+ @handleDeleteNote="deleteNoteHandler"
+ >
+ <slot
+ slot="avatar-badge"
+ name="avatar-badge"
+ >
+ </slot>
+ </component>
+ <toggle-replies-widget
+ v-if="hasReplies"
+ :collapsed="isRepliesCollapsed"
+ :replies="replies"
+ @toggle="toggleReplies"
+ />
+ <template v-if="!isRepliesCollapsed">
+ <component
+ :is="componentName(note)"
+ v-for="note in replies"
+ :key="note.id"
+ :note="componentData(note)"
+ @handleDeleteNote="deleteNoteHandler"
+ />
+ </template>
+ </template>
+ <template v-else>
+ <component
+ :is="componentName(note)"
+ v-for="(note, index) in discussion.notes"
+ :key="note.id"
+ :note="componentData(note)"
+ @handleDeleteNote="deleteNoteHandler"
+ >
+ <slot
+ v-if="index === 0"
+ slot="avatar-badge"
+ name="avatar-badge"
+ >
+ </slot>
+ </component>
+ </template>
</ul>
<div
+ v-if="!isRepliesCollapsed"
:class="{ 'is-replying': isReplying }"
class="discussion-reply-holder"
>
<template v-if="!isReplying && canReply">
- <div
- class="btn-group d-flex discussion-with-resolve-btn"
- role="group">
- <div
- class="btn-group w-100"
- role="group">
- <button
- type="button"
- class="js-vue-discussion-reply btn btn-text-field mr-2"
- title="Add a reply"
- @click="showReplyForm">Reply...</button>
- </div>
- <div
- v-if="discussion.resolvable"
- class="btn-group"
- role="group">
+ <div class="discussion-with-resolve-btn">
+ <button
+ type="button"
+ class="js-vue-discussion-reply btn btn-text-field mr-sm-2 qa-discussion-reply"
+ title="Add a reply"
+ @click="showReplyForm"
+ >
+ Reply...
+ </button>
+ <div v-if="discussion.resolvable">
<button
type="button"
- class="btn btn-default mr-2"
+ class="btn btn-default mr-sm-2"
@click="resolveHandler()"
>
<i
@@ -382,7 +455,7 @@ Please check your network connection and try again.`;
</div>
<div
v-if="discussion.resolvable"
- class="btn-group discussion-actions"
+ class="btn-group discussion-actions ml-sm-2"
role="group"
>
<div
@@ -397,7 +470,7 @@ Please check your network connection and try again.`;
btn-default discussion-create-issue-btn"
data-container="body"
>
- <span v-html="resolveDiscussionsSvg"></span>
+ <icon name="issue-new" />
</a>
</div>
<div
@@ -411,7 +484,7 @@ Please check your network connection and try again.`;
data-container="body"
@click="jumpToNextDiscussion"
>
- <span v-html="nextDiscussionsSvg"></span>
+ <icon name="comment-next" />
</button>
</div>
</div>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 4ebeb5599f2..9ab91e2abe5 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -46,13 +46,14 @@ export default {
'is-requesting being-posted': this.isRequesting,
'disabled-content': this.isDeleting,
target: this.isTarget,
+ 'is-editable': this.note.current_user.can_edit,
};
},
canResolve() {
return this.note.resolvable && !!this.getUserData.id;
},
canReportAsAbuse() {
- return this.note.report_abuse_path && this.author.id !== this.getUserData.id;
+ return !!this.note.report_abuse_path && this.author.id !== this.getUserData.id;
},
noteAnchorId() {
return `note_${this.note.id}`;
@@ -81,11 +82,16 @@ export default {
...mapActions(['deleteNote', 'updateNote', 'toggleResolveNote', 'scrollToNoteIfNeeded']),
editHandler() {
this.isEditing = true;
+ this.$emit('handleEdit');
},
deleteHandler() {
+ const typeOfComment = this.note.isDraft ? 'pending comment' : 'comment';
// eslint-disable-next-line no-alert
- if (window.confirm('Are you sure you want to delete this comment?')) {
+ if (window.confirm(`Are you sure you want to delete this ${typeOfComment}?`)) {
this.isDeleting = true;
+ this.$emit('handleDeleteNote', this.note);
+
+ if (this.note.isDraft) return;
this.deleteNote(this.note)
.then(() => {
@@ -97,7 +103,20 @@ export default {
});
}
},
+ updateSuccess() {
+ this.isEditing = false;
+ this.isRequesting = false;
+ this.oldContent = null;
+ $(this.$refs.noteBody.$el).renderGFM();
+ this.$refs.noteBody.resetAutoSave();
+ this.$emit('updateSuccess');
+ },
formUpdateHandler(noteText, parentElement, callback) {
+ this.$emit('handleUpdateNote', {
+ note: this.note,
+ noteText,
+ callback: () => this.updateSuccess(),
+ });
const data = {
endpoint: this.note.path,
note: {
@@ -112,11 +131,7 @@ export default {
this.updateNote(data)
.then(() => {
- this.isEditing = false;
- this.isRequesting = false;
- this.oldContent = null;
- $(this.$refs.noteBody.$el).renderGFM();
- this.$refs.noteBody.resetAutoSave();
+ this.updateSuccess();
callback();
})
.catch(() => {
@@ -141,6 +156,7 @@ export default {
this.oldContent = null;
}
this.isEditing = false;
+ this.$emit('cancelForm');
},
recoverNoteContent(noteText) {
// we need to do this to prevent noteForm inconsistent content warning
@@ -158,7 +174,7 @@ export default {
:class="classNameBindings"
:data-award-url="note.toggle_award_path"
:data-note-id="note.id"
- class="note timeline-entry"
+ class="note timeline-entry note-wrapper"
>
<div class="timeline-entry-inner">
<div class="timeline-icon">
@@ -167,7 +183,13 @@ export default {
:img-src="author.avatar_url"
:img-alt="author.name"
:img-size="40"
- />
+ >
+ <slot
+ slot="avatar-badge"
+ name="avatar-badge"
+ >
+ </slot>
+ </user-avatar-link>
</div>
<div class="timeline-content">
<div class="note-header">
@@ -175,6 +197,7 @@ export default {
:author="author"
:created-at="note.created_at"
:note-id="note.id"
+ action-text="commented"
/>
<note-actions
:author-id="author.id"
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 9b8713b40fb..69ddfd751e0 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -10,8 +10,8 @@ import systemNote from '../../vue_shared/components/notes/system_note.vue';
import commentForm from './comment_form.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import skeletonLoadingContainer from '../../vue_shared/components/notes/skeleton_note.vue';
+import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
export default {
name: 'NotesApp',
@@ -20,7 +20,6 @@ export default {
noteableDiscussion,
systemNote,
commentForm,
- loadingIcon,
placeholderNote,
placeholderSystemNote,
},
@@ -51,11 +50,19 @@ export default {
},
data() {
return {
- isLoading: true,
+ isFetching: false,
+ currentFilter: null,
};
},
computed: {
- ...mapGetters(['isNotesFetched', 'discussions', 'getNotesDataByProp', 'discussionCount']),
+ ...mapGetters([
+ 'isNotesFetched',
+ 'discussions',
+ 'getNotesDataByProp',
+ 'discussionCount',
+ 'isLoading',
+ 'commentsDisabled',
+ ]),
noteableType() {
return this.noteableData.noteableType;
},
@@ -98,8 +105,12 @@ export default {
});
}
},
+ updated() {
+ this.$nextTick(() => highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member')));
+ },
methods: {
...mapActions({
+ setLoadingState: 'setLoadingState',
fetchDiscussions: 'fetchDiscussions',
poll: 'poll',
actionToggleAward: 'toggleAward',
@@ -111,6 +122,7 @@ export default {
setTargetNoteHash: 'setTargetNoteHash',
toggleDiscussion: 'toggleDiscussion',
setNotesFetchedState: 'setNotesFetchedState',
+ startTaskList: 'startTaskList',
}),
getComponentName(discussion) {
if (discussion.isSkeletonNote) {
@@ -131,18 +143,25 @@ export default {
return discussion.individual_note ? { note: discussion.notes[0] } : { discussion };
},
fetchNotes() {
- return this.fetchDiscussions(this.getNotesDataByProp('discussionsPath'))
+ if (this.isFetching) return null;
+
+ this.isFetching = true;
+
+ return this.fetchDiscussions({ path: this.getNotesDataByProp('discussionsPath') })
.then(() => {
this.initPolling();
})
.then(() => {
- this.isLoading = false;
+ this.setLoadingState(false);
this.setNotesFetchedState(true);
+ eventHub.$emit('fetchedNotesData');
+ this.isFetching = false;
})
.then(() => this.$nextTick())
+ .then(() => this.startTaskList())
.then(() => this.checkLocationHash())
.catch(() => {
- this.isLoading = false;
+ this.setLoadingState(false);
this.setNotesFetchedState(true);
Flash('Something went wrong while fetching comments. Please try again.');
});
@@ -188,14 +207,15 @@ export default {
class="notes main-notes-list timeline"
>
<component
- v-for="discussion in allDiscussions"
:is="getComponentName(discussion)"
- v-bind="getComponentData(discussion)"
+ v-for="discussion in allDiscussions"
:key="discussion.id"
+ v-bind="getComponentData(discussion)"
/>
</ul>
<comment-form
+ v-if="!commentsDisabled"
:noteable-type="noteableType"
:markdown-version="markdownVersion"
/>
diff --git a/app/assets/javascripts/notes/components/toggle_replies_widget.vue b/app/assets/javascripts/notes/components/toggle_replies_widget.vue
new file mode 100644
index 00000000000..78ecbbb9247
--- /dev/null
+++ b/app/assets/javascripts/notes/components/toggle_replies_widget.vue
@@ -0,0 +1,94 @@
+<script>
+import _ from 'underscore';
+import Icon from '~/vue_shared/components/icon.vue';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+export default {
+ components: {
+ Icon,
+ UserAvatarLink,
+ TimeAgoTooltip,
+ },
+ props: {
+ collapsed: {
+ type: Boolean,
+ required: true,
+ },
+ replies: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ lastReply() {
+ return this.replies[this.replies.length - 1];
+ },
+ uniqueAuthors() {
+ const authors = this.replies.map(reply => reply.author || {});
+
+ return _.uniq(authors, author => author.username);
+ },
+ className() {
+ return this.collapsed ? 'collapsed' : 'expanded';
+ },
+ },
+ methods: {
+ toggle() {
+ this.$emit('toggle');
+ },
+ },
+};
+</script>
+
+<template>
+ <li
+ :class="className"
+ class="replies-toggle"
+ >
+ <template v-if="collapsed">
+ <icon
+ name="chevron-right"
+ @click.native="toggle"
+ />
+ <div>
+ <user-avatar-link
+ v-for="author in uniqueAuthors"
+ :key="author.username"
+ :link-href="author.path"
+ :img-alt="author.name"
+ :img-src="author.avatar_url"
+ :img-size="26"
+ :tooltip-text="author.name"
+ tooltip-placement="bottom"
+ />
+ </div>
+ <button
+ class="btn btn-link js-replies-text"
+ type="button"
+ @click="toggle"
+ >
+ {{ replies.length }} {{ n__('reply', 'replies', replies.length) }}
+ </button>
+ {{ __('Last reply by') }}
+ <a
+ :href="lastReply.author.path"
+ class="btn btn-link author-link"
+ >
+ {{ lastReply.author.name }}
+ </a>
+ <time-ago-tooltip
+ :time="lastReply.created_at"
+ tooltip-placement="bottom"
+ />
+ </template>
+ <span
+ v-else
+ class="collapse-replies-btn js-collapse-replies"
+ @click="toggle"
+ >
+ <icon name="chevron-down" />
+ {{ s__('Notes|Collapse replies') }}
+ </span>
+ </li>
+</template>
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index 2c3e07c0506..3147dc64c27 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -15,6 +15,8 @@ export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
export const UNRESOLVE_NOTE_METHOD_NAME = 'delete';
export const RESOLVE_NOTE_METHOD_NAME = 'post';
export const DESCRIPTION_TYPE = 'changed the description';
+export const HISTORY_ONLY_FILTER_VALUE = 2;
+export const DISCUSSION_FILTERS_DEFAULT_VALUE = 0;
export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
diff --git a/app/assets/javascripts/notes/discussion_filters.js b/app/assets/javascripts/notes/discussion_filters.js
new file mode 100644
index 00000000000..5c5f38a3fb0
--- /dev/null
+++ b/app/assets/javascripts/notes/discussion_filters.js
@@ -0,0 +1,35 @@
+import Vue from 'vue';
+import DiscussionFilter from './components/discussion_filter.vue';
+
+export default store => {
+ const discussionFilterEl = document.getElementById('js-vue-discussion-filter');
+
+ if (discussionFilterEl) {
+ const { defaultFilter, notesFilters } = discussionFilterEl.dataset;
+ const selectedValue = defaultFilter ? parseInt(defaultFilter, 10) : null;
+ const filterValues = notesFilters ? JSON.parse(notesFilters) : {};
+ const filters = Object.keys(filterValues).map(entry => ({
+ title: entry,
+ value: filterValues[entry],
+ }));
+
+ return new Vue({
+ el: discussionFilterEl,
+ name: 'DiscussionFilter',
+ components: {
+ DiscussionFilter,
+ },
+ store,
+ render(createElement) {
+ return createElement('discussion-filter', {
+ props: {
+ filters,
+ selectedValue,
+ },
+ });
+ },
+ });
+ }
+
+ return null;
+};
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 3aef30c608c..2f715c85fa6 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -1,10 +1,13 @@
import Vue from 'vue';
import notesApp from './components/notes_app.vue';
+import initDiscussionFilters from './discussion_filters';
import createStore from './stores';
document.addEventListener('DOMContentLoaded', () => {
const store = createStore();
+ initDiscussionFilters(store);
+
return new Vue({
el: '#js-vue-notes',
components: {
diff --git a/app/assets/javascripts/notes/services/notes_service.js b/app/assets/javascripts/notes/services/notes_service.js
index f5dce94caad..47a6f07cce2 100644
--- a/app/assets/javascripts/notes/services/notes_service.js
+++ b/app/assets/javascripts/notes/services/notes_service.js
@@ -5,8 +5,9 @@ import * as constants from '../constants';
Vue.use(VueResource);
export default {
- fetchDiscussions(endpoint) {
- return Vue.http.get(endpoint);
+ fetchDiscussions(endpoint, filter) {
+ const config = filter !== undefined ? { params: { notes_filter: filter } } : null;
+ return Vue.http.get(endpoint, config);
},
deleteNote(endpoint) {
return Vue.http.delete(endpoint);
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 3eefbe11c37..5b2f0540020 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -1,6 +1,8 @@
+import Vue from 'vue';
import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import Visibility from 'visibilityjs';
+import TaskList from '../../task_list';
import Flash from '../../flash';
import Poll from '../../lib/utils/poll';
import * as types from './mutation_types';
@@ -10,6 +12,8 @@ import service from '../services/notes_service';
import loadAwardsHandler from '../../awards_handler';
import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
+import mrWidgetEventHub from '../../vue_merge_request_widget/event_hub';
+import { __ } from '~/locale';
let eTagPoll;
@@ -35,34 +39,34 @@ export const setNotesFetchedState = ({ commit }, state) =>
export const toggleDiscussion = ({ commit }, data) => commit(types.TOGGLE_DISCUSSION, data);
-export const fetchDiscussions = ({ commit }, path) =>
+export const fetchDiscussions = ({ commit }, { path, filter }) =>
service
- .fetchDiscussions(path)
+ .fetchDiscussions(path, filter)
.then(res => res.json())
.then(discussions => {
commit(types.SET_INITIAL_DISCUSSIONS, discussions);
});
-export const refetchDiscussionById = ({ commit }, { path, discussionId }) =>
- service
- .fetchDiscussions(path)
- .then(res => res.json())
- .then(discussions => {
- const selectedDiscussion = discussions.find(discussion => discussion.id === discussionId);
- if (selectedDiscussion) commit(types.UPDATE_DISCUSSION, selectedDiscussion);
- });
+export const updateDiscussion = ({ commit, state }, discussion) => {
+ commit(types.UPDATE_DISCUSSION, discussion);
-export const deleteNote = ({ commit }, note) =>
+ return utils.findNoteObjectById(state.discussions, discussion.id);
+};
+
+export const deleteNote = ({ commit, dispatch }, note) =>
service.deleteNote(note.path).then(() => {
commit(types.DELETE_NOTE, note);
+
+ dispatch('updateMergeRequestWidget');
});
-export const updateNote = ({ commit }, { endpoint, note }) =>
+export const updateNote = ({ commit, dispatch }, { endpoint, note }) =>
service
.updateNote(endpoint, note)
.then(res => res.json())
.then(res => {
commit(types.UPDATE_NOTE, res);
+ dispatch('startTaskList');
});
export const replyToDiscussion = ({ commit }, { endpoint, data }) =>
@@ -75,20 +79,23 @@ export const replyToDiscussion = ({ commit }, { endpoint, data }) =>
return res;
});
-export const createNewNote = ({ commit }, { endpoint, data }) =>
+export const createNewNote = ({ commit, dispatch }, { endpoint, data }) =>
service
.createNewNote(endpoint, data)
.then(res => res.json())
.then(res => {
if (!res.errors) {
commit(types.ADD_NEW_NOTE, res);
+
+ dispatch('updateMergeRequestWidget');
+ dispatch('startTaskList');
}
return res;
});
export const removePlaceholderNotes = ({ commit }) => commit(types.REMOVE_PLACEHOLDER_NOTES);
-export const toggleResolveNote = ({ commit }, { endpoint, isResolved, discussion }) =>
+export const toggleResolveNote = ({ commit, dispatch }, { endpoint, isResolved, discussion }) =>
service
.toggleResolveNote(endpoint, isResolved)
.then(res => res.json())
@@ -96,6 +103,8 @@ export const toggleResolveNote = ({ commit }, { endpoint, isResolved, discussion
const mutationType = discussion ? types.UPDATE_DISCUSSION : types.UPDATE_NOTE;
commit(mutationType, res);
+
+ dispatch('updateMergeRequestWidget');
});
export const closeIssue = ({ commit, dispatch, state }) => {
@@ -146,35 +155,50 @@ export const toggleIssueLocalState = ({ commit }, newState) => {
export const saveNote = ({ commit, dispatch }, noteData) => {
// For MR discussuions we need to post as `note[note]` and issue we use `note.note`.
- const note = noteData.data['note[note]'] || noteData.data.note.note;
+ // For batch comments, we use draft_note
+ const note = noteData.data.draft_note || noteData.data['note[note]'] || noteData.data.note.note;
let placeholderText = note;
const hasQuickActions = utils.hasQuickActions(placeholderText);
const replyId = noteData.data.in_reply_to_discussion_id;
- const methodToDispatch = replyId ? 'replyToDiscussion' : 'createNewNote';
+ let methodToDispatch;
+ const postData = Object.assign({}, noteData);
+ if (postData.isDraft === true) {
+ methodToDispatch = replyId
+ ? 'batchComments/addDraftToDiscussion'
+ : 'batchComments/createNewDraft';
+ if (!postData.draft_note && noteData.note) {
+ postData.draft_note = postData.note;
+ delete postData.note;
+ }
+ } else {
+ methodToDispatch = replyId ? 'replyToDiscussion' : 'createNewNote';
+ }
- commit(types.REMOVE_PLACEHOLDER_NOTES); // remove previous placeholders
$('.notes-form .flash-container').hide(); // hide previous flash notification
+ commit(types.REMOVE_PLACEHOLDER_NOTES); // remove previous placeholders
- if (hasQuickActions) {
- placeholderText = utils.stripQuickActions(placeholderText);
- }
+ if (replyId) {
+ if (hasQuickActions) {
+ placeholderText = utils.stripQuickActions(placeholderText);
+ }
- if (placeholderText.length) {
- commit(types.SHOW_PLACEHOLDER_NOTE, {
- noteBody: placeholderText,
- replyId,
- });
- }
+ if (placeholderText.length) {
+ commit(types.SHOW_PLACEHOLDER_NOTE, {
+ noteBody: placeholderText,
+ replyId,
+ });
+ }
- if (hasQuickActions) {
- commit(types.SHOW_PLACEHOLDER_NOTE, {
- isSystemNote: true,
- noteBody: utils.getQuickActionText(note),
- replyId,
- });
+ if (hasQuickActions) {
+ commit(types.SHOW_PLACEHOLDER_NOTE, {
+ isSystemNote: true,
+ noteBody: utils.getQuickActionText(note),
+ replyId,
+ });
+ }
}
- return dispatch(methodToDispatch, noteData).then(res => {
+ return dispatch(methodToDispatch, postData, { root: true }).then(res => {
const { errors } = res;
const commandsChanges = res.commands_changes;
@@ -211,7 +235,9 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
if (errors && errors.commands_only) {
Flash(errors.commands_only, 'notice', noteData.flashContainer);
}
- commit(types.REMOVE_PLACEHOLDER_NOTES);
+ if (replyId) {
+ commit(types.REMOVE_PLACEHOLDER_NOTES);
+ }
return res;
});
@@ -230,7 +256,7 @@ const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
if (discussion) {
commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
} else if (note.type === constants.DIFF_NOTE) {
- dispatch('fetchDiscussions', state.notesData.discussionsPath);
+ dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
} else {
commit(types.ADD_NEW_NOTE, note);
}
@@ -238,6 +264,8 @@ const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
commit(types.ADD_NEW_NOTE, note);
}
});
+
+ dispatch('startTaskList');
}
commit(types.SET_LAST_FETCHED_AT, resp.last_fetched_at);
@@ -313,12 +341,49 @@ export const scrollToNoteIfNeeded = (context, el) => {
};
export const fetchDiscussionDiffLines = ({ commit }, discussion) =>
- axios.get(discussion.truncatedDiffLinesPath).then(({ data }) => {
+ axios.get(discussion.truncated_diff_lines_path).then(({ data }) => {
commit(types.SET_DISCUSSION_DIFF_LINES, {
discussionId: discussion.id,
diffLines: data.truncated_diff_lines,
});
});
+export const updateMergeRequestWidget = () => {
+ mrWidgetEventHub.$emit('mr.discussion.updated');
+};
+
+export const setLoadingState = ({ commit }, data) => {
+ commit(types.SET_NOTES_LOADING_STATE, data);
+};
+
+export const filterDiscussion = ({ dispatch }, { path, filter }) => {
+ dispatch('setLoadingState', true);
+ dispatch('fetchDiscussions', { path, filter })
+ .then(() => {
+ dispatch('setLoadingState', false);
+ dispatch('setNotesFetchedState', true);
+ })
+ .catch(() => {
+ dispatch('setLoadingState', false);
+ dispatch('setNotesFetchedState', true);
+ Flash(__('Something went wrong while fetching comments. Please try again.'));
+ });
+};
+
+export const setCommentsDisabled = ({ commit }, data) => {
+ commit(types.DISABLE_COMMENTS, data);
+};
+
+export const startTaskList = ({ dispatch }) =>
+ Vue.nextTick(
+ () =>
+ new TaskList({
+ dataType: 'note',
+ fieldName: 'note',
+ selector: '.notes .is-editable',
+ onSuccess: () => dispatch('startTaskList'),
+ }),
+ );
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/notes/stores/collapse_utils.js b/app/assets/javascripts/notes/stores/collapse_utils.js
index fa4a1c56b20..bee6d4f0329 100644
--- a/app/assets/javascripts/notes/stores/collapse_utils.js
+++ b/app/assets/javascripts/notes/stores/collapse_utils.js
@@ -68,12 +68,9 @@ export const collapseSystemNotes = notes => {
lastDescriptionSystemNote = note;
lastDescriptionSystemNoteIndex = acc.length;
} else if (lastDescriptionSystemNote) {
- const timeDifferenceMinutes = getTimeDifferenceMinutes(
- lastDescriptionSystemNote,
- note,
- );
+ const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note);
- // are they less than 10 minutes appart?
+ // are they less than 10 minutes apart?
if (timeDifferenceMinutes > 10) {
// reset counter
descriptionChangedTimes = 1;
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 5b3b9f8776f..8df95c279eb 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -10,6 +10,8 @@ export const getNotesData = state => state.notesData;
export const isNotesFetched = state => state.isNotesFetched;
+export const isLoading = state => state.isLoading;
+
export const getNotesDataByProp = state => prop => state.notesData[prop];
export const getNoteableData = state => state.noteableData;
@@ -28,18 +30,6 @@ export const notesById = state =>
return acc;
}, {});
-export const discussionsByLineCode = state =>
- state.discussions.reduce((acc, note) => {
- if (note.diff_discussion && note.line_code && note.resolvable) {
- // For context about line notes: there might be multiple notes with the same line code
- const items = acc[note.line_code] || [];
- items.push(note);
-
- Object.assign(acc, { [note.line_code]: items });
- }
- return acc;
- }, {});
-
export const noteableType = state => {
const { ISSUE_NOTEABLE_TYPE, MERGE_REQUEST_NOTEABLE_TYPE, EPIC_NOTEABLE_TYPE } = constants;
@@ -82,6 +72,9 @@ export const allDiscussions = (state, getters) => {
return Object.values(resolved).concat(unresolved);
};
+export const isDiscussionResolved = (state, getters) => discussionId =>
+ getters.resolvedDiscussionsById[discussionId] !== undefined;
+
export const allResolvableDiscussions = (state, getters) =>
getters.allDiscussions.filter(d => !d.individual_note && d.resolvable);
@@ -134,8 +127,8 @@ export const unresolvedDiscussionsIdsByDiff = (state, getters) =>
const filenameComparison = a.diff_file.file_path.localeCompare(b.diff_file.file_path);
// Get the line numbers, to compare within the same file
- const aLines = [a.position.formatter.new_line, a.position.formatter.old_line];
- const bLines = [b.position.formatter.new_line, b.position.formatter.old_line];
+ const aLines = [a.position.new_line, a.position.old_line];
+ const bLines = [b.position.new_line, b.position.old_line];
return filenameComparison < 0 ||
(filenameComparison === 0 &&
@@ -199,5 +192,7 @@ export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
return getters.unresolvedDiscussionsIdsByDate[0];
};
+export const commentsDisabled = state => state.commentsDisabled;
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/notes/stores/index.js b/app/assets/javascripts/notes/stores/index.js
index 0f48b8880f4..d41b02b4a4b 100644
--- a/app/assets/javascripts/notes/stores/index.js
+++ b/app/assets/javascripts/notes/stores/index.js
@@ -1,16 +1,7 @@
import Vue from 'vue';
import Vuex from 'vuex';
-import * as actions from './actions';
-import * as getters from './getters';
-import mutations from './mutations';
-import module from './modules';
+import notesModule from './modules';
Vue.use(Vuex);
-export default () =>
- new Vuex.Store({
- state: module.state,
- actions,
- getters,
- mutations,
- });
+export default () => new Vuex.Store(notesModule());
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index b4cb9267e0f..8aea269ea7d 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -2,7 +2,7 @@ import * as actions from '../actions';
import * as getters from '../getters';
import mutations from '../mutations';
-export default {
+export default () => ({
state: {
discussions: [],
targetNoteHash: null,
@@ -11,6 +11,7 @@ export default {
// View layer
isToggleStateButtonLoading: false,
isNotesFetched: false,
+ isLoading: true,
// holds endpoints and permissions provided through haml
notesData: {
@@ -20,8 +21,9 @@ export default {
noteableData: {
current_user: {},
},
+ commentsDisabled: false,
},
actions,
getters,
mutations,
-};
+});
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index 6f374f78691..dfbf3b7b34b 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -14,6 +14,8 @@ export const UPDATE_NOTE = 'UPDATE_NOTE';
export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION';
export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES';
export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE';
+export const SET_NOTES_LOADING_STATE = 'SET_NOTES_LOADING_STATE';
+export const DISABLE_COMMENTS = 'DISABLE_COMMENTS';
// DISCUSSION
export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index ab6a95e2601..f6054e0be87 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -4,7 +4,8 @@ import * as constants from '../constants';
import { isInMRPage } from '../../lib/utils/common_utils';
export default {
- [types.ADD_NEW_NOTE](state, note) {
+ [types.ADD_NEW_NOTE](state, data) {
+ const note = data.discussion ? data.discussion.notes[0] : data;
const { discussion_id, type } = note;
const [exists] = state.discussions.filter(n => n.id === note.discussion_id);
const isDiscussion = type === constants.DISCUSSION_NOTE || type === constants.DIFF_NOTE;
@@ -54,13 +55,12 @@ export default {
[types.EXPAND_DISCUSSION](state, { discussionId }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId);
-
- discussion.expanded = true;
+ Object.assign(discussion, { expanded: true });
},
[types.COLLAPSE_DISCUSSION](state, { discussionId }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId);
- discussion.expanded = false;
+ Object.assign(discussion, { expanded: false });
},
[types.REMOVE_PLACEHOLDER_NOTES](state) {
@@ -95,10 +95,18 @@ export default {
[types.SET_USER_DATA](state, data) {
Object.assign(state, { userData: data });
},
+
[types.SET_INITIAL_DISCUSSIONS](state, discussionsData) {
const discussions = [];
discussionsData.forEach(discussion => {
+ if (discussion.diff_file) {
+ Object.assign(discussion, {
+ file_hash: discussion.diff_file.file_hash,
+ truncated_diff_lines: discussion.truncated_diff_lines || [],
+ });
+ }
+
// To support legacy notes, should be very rare case.
if (discussion.individual_note && discussion.notes.length > 1) {
discussion.notes.forEach(n => {
@@ -168,8 +176,7 @@ export default {
[types.TOGGLE_DISCUSSION](state, { discussionId }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId);
-
- discussion.expanded = !discussion.expanded;
+ Object.assign(discussion, { expanded: !discussion.expanded });
},
[types.UPDATE_NOTE](state, note) {
@@ -185,16 +192,12 @@ export default {
[types.UPDATE_DISCUSSION](state, noteData) {
const note = noteData;
- let index = 0;
-
- state.discussions.forEach((n, i) => {
- if (n.id === note.id) {
- index = i;
- }
- });
-
+ const selectedDiscussion = state.discussions.find(disc => disc.id === note.id);
note.expanded = true; // override expand flag to prevent collapse
- state.discussions.splice(index, 1, note);
+ if (note.diff_file) {
+ Object.assign(note, { file_hash: note.diff_file.file_hash });
+ }
+ Object.assign(selectedDiscussion, { ...note });
},
[types.CLOSE_ISSUE](state) {
@@ -213,14 +216,17 @@ export default {
Object.assign(state, { isNotesFetched: value });
},
+ [types.SET_NOTES_LOADING_STATE](state, value) {
+ state.isLoading = value;
+ },
+
[types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId);
- const index = state.discussions.indexOf(discussion);
- const discussionWithDiffLines = Object.assign({}, discussion, {
- truncated_diff_lines: diffLines,
- });
+ discussion.truncated_diff_lines = diffLines;
+ },
- state.discussions.splice(index, 1, discussionWithDiffLines);
+ [types.DISABLE_COMMENTS](state, value) {
+ state.commentsDisabled = value;
},
};
diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js
index a0e096ebfaf..dd57539e4d8 100644
--- a/app/assets/javascripts/notes/stores/utils.js
+++ b/app/assets/javascripts/notes/stores/utils.js
@@ -2,13 +2,11 @@ import AjaxCache from '~/lib/utils/ajax_cache';
const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
-export const findNoteObjectById = (notes, id) =>
- notes.filter(n => n.id === id)[0];
+export const findNoteObjectById = (notes, id) => notes.filter(n => n.id === id)[0];
export const getQuickActionText = note => {
let text = 'Applying command';
- const quickActions =
- AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
+ const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
const executedCommands = quickActions.filter(command => {
const commandRegex = new RegExp(`/${command.name}`);
@@ -29,5 +27,4 @@ export const getQuickActionText = note => {
export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note);
-export const stripQuickActions = note =>
- note.replace(REGEX_QUICK_ACTIONS, '').trim();
+export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim();
diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js
index 8ff8bb446ad..c4c8cf86cb0 100644
--- a/app/assets/javascripts/notifications_dropdown.js
+++ b/app/assets/javascripts/notifications_dropdown.js
@@ -18,7 +18,9 @@ export default function notificationsDropdown() {
$(document).on('ajax:success', '.notification-form', (e, data) => {
if (data.saved) {
- $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html);
+ $(e.currentTarget)
+ .closest('.js-notification-dropdown')
+ .replaceWith(data.html);
} else {
Flash('Failed to save new settings', 'alert');
}
diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js
index 00e27ca0e70..45f033f2822 100644
--- a/app/assets/javascripts/notifications_form.js
+++ b/app/assets/javascripts/notifications_form.js
@@ -22,7 +22,8 @@ export default class NotificationsForm {
// eslint-disable-next-line class-methods-use-this
showCheckboxLoadingSpinner($parent) {
- $parent.addClass('is-loading')
+ $parent
+ .addClass('is-loading')
.find('.custom-notification-event-loading')
.removeClass('fa-check')
.addClass('fa-spin fa-spinner')
@@ -38,9 +39,12 @@ export default class NotificationsForm {
.then(({ data }) => {
$checkbox.enable();
if (data.saved) {
- $parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done');
+ $parent
+ .find('.custom-notification-event-loading')
+ .toggleClass('fa-spin fa-spinner fa-check is-done');
setTimeout(() => {
- $parent.removeClass('is-loading')
+ $parent
+ .removeClass('is-loading')
.find('.custom-notification-event-loading')
.toggleClass('fa-spin fa-spinner fa-check is-done');
}, 2000);
diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js
index 86a43b66cc8..386a9b2c740 100644
--- a/app/assets/javascripts/pager.js
+++ b/app/assets/javascripts/pager.js
@@ -7,14 +7,21 @@ const ENDLESS_SCROLL_BOTTOM_PX = 400;
const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000;
export default {
- init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) {
+ init(
+ limit = 0,
+ preload = false,
+ disable = false,
+ prepareData = $.noop,
+ callback = $.noop,
+ container = '',
+ ) {
this.url = $('.content_list').data('href') || removeParams(['limit', 'offset']);
this.limit = limit;
this.offset = parseInt(getParameterByName('offset'), 10) || this.limit;
this.disable = disable;
this.prepareData = prepareData;
this.callback = callback;
- this.loading = $('.loading').first();
+ this.loading = $(`${container} .loading`).first();
if (preload) {
this.offset = 0;
this.getOld();
@@ -24,22 +31,25 @@ export default {
getOld() {
this.loading.show();
- axios.get(this.url, {
- params: {
- limit: this.limit,
- offset: this.offset,
- },
- }).then(({ data }) => {
- this.append(data.count, this.prepareData(data.html));
- this.callback();
+ axios
+ .get(this.url, {
+ params: {
+ limit: this.limit,
+ offset: this.offset,
+ },
+ })
+ .then(({ data }) => {
+ this.append(data.count, this.prepareData(data.html));
+ this.callback();
- // keep loading until we've filled the viewport height
- if (!this.disable && !this.isScrollable()) {
- this.getOld();
- } else {
- this.loading.hide();
- }
- }).catch(() => this.loading.hide());
+ // keep loading until we've filled the viewport height
+ if (!this.disable && !this.isScrollable()) {
+ this.getOld();
+ } else {
+ this.loading.hide();
+ }
+ })
+ .catch(() => this.loading.hide());
},
append(count, html) {
diff --git a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js
index 15e737fff05..d9cf62db3f7 100644
--- a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js
+++ b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js
@@ -31,7 +31,7 @@ export default class AbuseReports {
$messageCellElement.text(originalMessage);
} else {
$messageCellElement.data('messageTruncated', 'true');
- $messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`);
+ $messageCellElement.text(`${originalMessage.substr(0, MAX_MESSAGE_LENGTH - 3)}...`);
}
}
}
diff --git a/app/assets/javascripts/pages/admin/admin.js b/app/assets/javascripts/pages/admin/admin.js
index ff4d6ab15f9..4616a075729 100644
--- a/app/assets/javascripts/pages/admin/admin.js
+++ b/app/assets/javascripts/pages/admin/admin.js
@@ -23,7 +23,7 @@ export default function adminInit() {
}
});
- $('body').on('click', '.js-toggle-colors-link', (e) => {
+ $('body').on('click', '.js-toggle-colors-link', e => {
e.preventDefault();
$('.js-toggle-colors-container').toggleClass('hide');
});
@@ -33,12 +33,15 @@ export default function adminInit() {
$(this).tab('show');
});
- $('.log-bottom').on('click', (e) => {
+ $('.log-bottom').on('click', e => {
e.preventDefault();
const $visibleLog = $('.file-content:visible');
- $visibleLog.animate({
- scrollTop: $visibleLog.find('ol').height(),
- }, 'fast');
+ $visibleLog.animate(
+ {
+ scrollTop: $visibleLog.find('ol').height(),
+ },
+ 'fast',
+ );
});
$('.change-owner-link').on('click', function changeOwnerLinkClick(e) {
@@ -47,7 +50,7 @@ export default function adminInit() {
modal.show();
});
- $('.change-owner-cancel-link').on('click', (e) => {
+ $('.change-owner-cancel-link').on('click', e => {
e.preventDefault();
modal.hide();
$('.change-owner-link').show();
diff --git a/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js b/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js
new file mode 100644
index 00000000000..455c637a6b3
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js
@@ -0,0 +1,29 @@
+import { __ } from '~/locale';
+
+export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE = __('Regex pattern');
+export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE = __(
+ 'To define internal users, first enable new users set to external',
+);
+
+function setUserInternalRegexPlaceholder(checkbox) {
+ const userInternalRegex = document.getElementById(
+ 'application_setting_user_default_internal_regex',
+ );
+ if (checkbox && userInternalRegex) {
+ if (checkbox.checked) {
+ userInternalRegex.readOnly = false;
+ userInternalRegex.placeholder = PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE;
+ } else {
+ userInternalRegex.readOnly = true;
+ userInternalRegex.placeholder = PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE;
+ }
+ }
+}
+
+export default function initUserInternalRegexPlaceholder() {
+ const checkbox = document.getElementById('application_setting_user_default_external');
+ setUserInternalRegexPlaceholder(checkbox);
+ checkbox.addEventListener('change', () => {
+ setUserInternalRegexPlaceholder(checkbox);
+ });
+}
diff --git a/app/assets/javascripts/pages/admin/application_settings/index.js b/app/assets/javascripts/pages/admin/application_settings/index.js
index 48d75f5443b..47bd70537f1 100644
--- a/app/assets/javascripts/pages/admin/application_settings/index.js
+++ b/app/assets/javascripts/pages/admin/application_settings/index.js
@@ -1,6 +1,8 @@
import initSettingsPanels from '~/settings_panels';
+import projectSelect from '~/project_select';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
+ projectSelect();
});
diff --git a/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/index.js b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/index.js
new file mode 100644
index 00000000000..c40503603be
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/index.js
@@ -0,0 +1,8 @@
+import UsagePingPayload from './../usage_ping_payload';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new UsagePingPayload(
+ document.querySelector('.js-usage-ping-payload-trigger'),
+ document.querySelector('.js-usage-ping-payload'),
+ ).init();
+});
diff --git a/app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js b/app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js
new file mode 100644
index 00000000000..9a1bc46bf4a
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js
@@ -0,0 +1,62 @@
+import axios from '../../../lib/utils/axios_utils';
+import { __ } from '../../../locale';
+import flash from '../../../flash';
+
+export default class UsagePingPayload {
+ constructor(trigger, container) {
+ this.trigger = trigger;
+ this.container = container;
+ this.isVisible = false;
+ this.isInserted = false;
+ }
+
+ init() {
+ this.spinner = this.trigger.querySelector('.js-spinner');
+ this.text = this.trigger.querySelector('.js-text');
+
+ this.trigger.addEventListener('click', event => {
+ event.preventDefault();
+
+ if (this.isVisible) return this.hidePayload();
+
+ return this.requestPayload();
+ });
+ }
+
+ requestPayload() {
+ if (this.isInserted) return this.showPayload();
+
+ this.spinner.classList.add('d-inline');
+
+ return axios
+ .get(this.container.dataset.endpoint, {
+ responseType: 'text',
+ })
+ .then(({ data }) => {
+ this.spinner.classList.remove('d-inline');
+ this.insertPayload(data);
+ })
+ .catch(() => {
+ this.spinner.classList.remove('d-inline');
+ flash(__('Error fetching usage ping data.'));
+ });
+ }
+
+ hidePayload() {
+ this.isVisible = false;
+ this.container.classList.add('d-none');
+ this.text.textContent = __('Preview payload');
+ }
+
+ showPayload() {
+ this.isVisible = true;
+ this.container.classList.remove('d-none');
+ this.text.textContent = __('Hide payload');
+ }
+
+ insertPayload(data) {
+ this.isInserted = true;
+ this.container.innerHTML = data;
+ this.showPayload();
+ }
+}
diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js
index e7ceccb6f47..d5ded3f9a79 100644
--- a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js
+++ b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js
@@ -17,20 +17,24 @@ export default () => {
const previewPath = $('textarea#broadcast_message_message').data('previewPath');
- $('textarea#broadcast_message_message').on('input', _.debounce(function onMessageInput() {
- const message = $(this).val();
- if (message === '') {
- $('.js-broadcast-message-preview').text('Your message here');
- } else {
- axios.post(previewPath, {
- broadcast_message: {
- message,
- },
- })
- .then(({ data }) => {
- $('.js-broadcast-message-preview').html(data.message);
- })
- .catch(() => flash(__('An error occurred while rendering preview broadcast message')));
- }
- }, 250));
+ $('textarea#broadcast_message_message').on(
+ 'input',
+ _.debounce(function onMessageInput() {
+ const message = $(this).val();
+ if (message === '') {
+ $('.js-broadcast-message-preview').text('Your message here');
+ } else {
+ axios
+ .post(previewPath, {
+ broadcast_message: {
+ message,
+ },
+ })
+ .then(({ data }) => {
+ $('.js-broadcast-message-preview').html(data.message);
+ })
+ .catch(() => flash(__('An error occurred while rendering preview broadcast message')));
+ }
+ }, 250),
+ );
};
diff --git a/app/assets/javascripts/pages/admin/cohorts/index.js b/app/assets/javascripts/pages/admin/cohorts/index.js
deleted file mode 100644
index 2d5020dbef4..00000000000
--- a/app/assets/javascripts/pages/admin/cohorts/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import initUsagePing from './usage_ping';
-
-document.addEventListener('DOMContentLoaded', initUsagePing);
diff --git a/app/assets/javascripts/pages/admin/cohorts/usage_ping.js b/app/assets/javascripts/pages/admin/cohorts/usage_ping.js
deleted file mode 100644
index 914a9661c27..00000000000
--- a/app/assets/javascripts/pages/admin/cohorts/usage_ping.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import axios from '../../../lib/utils/axios_utils';
-import { __ } from '../../../locale';
-import flash from '../../../flash';
-
-export default function UsagePing() {
- const el = document.querySelector('.usage-data');
-
- axios.get(el.dataset.endpoint, {
- responseType: 'text',
- }).then(({ data }) => {
- el.innerHTML = data;
- }).catch(() => flash(__('Error fetching usage ping data.')));
-}
diff --git a/app/assets/javascripts/pages/admin/index.js b/app/assets/javascripts/pages/admin/index.js
index e50b61f09e2..3aa793e47b9 100644
--- a/app/assets/javascripts/pages/admin/index.js
+++ b/app/assets/javascripts/pages/admin/index.js
@@ -1,3 +1,7 @@
import initAdmin from './admin';
+import initUserInternalRegexPlaceholder from './application_settings/account_and_limits';
-document.addEventListener('DOMContentLoaded', initAdmin);
+document.addEventListener('DOMContentLoaded', () => {
+ initAdmin();
+ initUserInternalRegexPlaceholder();
+});
diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
index bc84666779e..e2fec3c7172 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
+++ b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
@@ -1,39 +1,42 @@
<script>
- import axios from '~/lib/utils/axios_utils';
- import createFlash from '~/flash';
- import GlModal from '~/vue_shared/components/gl_modal.vue';
- import { redirectTo } from '~/lib/utils/url_utility';
- import { s__ } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
+import createFlash from '~/flash';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import { redirectTo } from '~/lib/utils/url_utility';
+import { s__ } from '~/locale';
- export default {
- components: {
- GlModal,
+export default {
+ components: {
+ GlModal,
+ },
+ props: {
+ url: {
+ type: String,
+ required: true,
},
- props: {
- url: {
- type: String,
- required: true,
- },
+ },
+ computed: {
+ text() {
+ return s__(
+ 'AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.',
+ );
},
- computed: {
- text() {
- return s__('AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.');
- },
+ },
+ methods: {
+ onSubmit() {
+ return axios
+ .post(this.url)
+ .then(response => {
+ // follow the rediect to refresh the page
+ redirectTo(response.request.responseURL);
+ })
+ .catch(error => {
+ createFlash(s__('AdminArea|Stopping jobs failed'));
+ throw error;
+ });
},
- methods: {
- onSubmit() {
- return axios.post(this.url)
- .then((response) => {
- // follow the rediect to refresh the page
- redirectTo(response.request.responseURL);
- })
- .catch((error) => {
- createFlash(s__('AdminArea|Stopping jobs failed'));
- throw error;
- });
- },
- },
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/pages/admin/projects/index.js b/app/assets/javascripts/pages/admin/projects/index.js
index 31c96eb87af..d6b1e747aec 100644
--- a/app/assets/javascripts/pages/admin/projects/index.js
+++ b/app/assets/javascripts/pages/admin/projects/index.js
@@ -4,6 +4,7 @@ import NamespaceSelect from '../../../namespace_select';
document.addEventListener('DOMContentLoaded', () => {
new ProjectsList(); // eslint-disable-line no-new
- document.querySelectorAll('.js-namespace-select')
+ document
+ .querySelectorAll('.js-namespace-select')
.forEach(dropdown => new NamespaceSelect({ dropdown }));
});
diff --git a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
index ff66d3a8ac4..3c383735f4a 100644
--- a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
+++ b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
@@ -1,81 +1,84 @@
<script>
- import _ from 'underscore';
- import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
- import { s__, sprintf } from '~/locale';
+import _ from 'underscore';
+import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import { s__, sprintf } from '~/locale';
- export default {
- components: {
- DeprecatedModal,
+export default {
+ components: {
+ DeprecatedModal,
+ },
+ props: {
+ deleteProjectUrl: {
+ type: String,
+ required: false,
+ default: '',
},
- props: {
- deleteProjectUrl: {
- type: String,
- required: false,
- default: '',
- },
- projectName: {
- type: String,
- required: false,
- default: '',
- },
- csrfToken: {
- type: String,
- required: false,
- default: '',
- },
+ projectName: {
+ type: String,
+ required: false,
+ default: '',
},
- data() {
- return {
- enteredProjectName: '',
- };
+ csrfToken: {
+ type: String,
+ required: false,
+ default: '',
},
- computed: {
- title() {
- return sprintf(s__('AdminProjects|Delete Project %{projectName}?'),
- {
- projectName: `'${_.escape(this.projectName)}'`,
- },
- false,
- );
- },
- text() {
- return sprintf(s__(`AdminProjects|
+ },
+ data() {
+ return {
+ enteredProjectName: '',
+ };
+ },
+ computed: {
+ title() {
+ return sprintf(
+ s__('AdminProjects|Delete Project %{projectName}?'),
+ {
+ projectName: `'${_.escape(this.projectName)}'`,
+ },
+ false,
+ );
+ },
+ text() {
+ return sprintf(
+ s__(`AdminProjects|
You’re about to permanently delete the project %{projectName}, its repository,
and all related resources including issues, merge requests, etc.. Once you confirm and press
%{strong_start}Delete project%{strong_end}, it cannot be undone or recovered.`),
- {
- projectName: `<strong>${_.escape(this.projectName)}</strong>`,
- strong_start: '<strong>',
- strong_end: '</strong>',
- },
- false,
- );
- },
- confirmationTextLabel() {
- return sprintf(s__('AdminUsers|To confirm, type %{projectName}'),
- {
- projectName: `<code>${_.escape(this.projectName)}</code>`,
- },
- false,
- );
- },
- primaryButtonLabel() {
- return s__('AdminProjects|Delete project');
- },
- canSubmit() {
- return this.enteredProjectName === this.projectName;
- },
+ {
+ projectName: `<strong>${_.escape(this.projectName)}</strong>`,
+ strong_start: '<strong>',
+ strong_end: '</strong>',
+ },
+ false,
+ );
+ },
+ confirmationTextLabel() {
+ return sprintf(
+ s__('AdminUsers|To confirm, type %{projectName}'),
+ {
+ projectName: `<code>${_.escape(this.projectName)}</code>`,
+ },
+ false,
+ );
+ },
+ primaryButtonLabel() {
+ return s__('AdminProjects|Delete project');
+ },
+ canSubmit() {
+ return this.enteredProjectName === this.projectName;
+ },
+ },
+ methods: {
+ onCancel() {
+ this.enteredProjectName = '';
},
- methods: {
- onCancel() {
- this.enteredProjectName = '';
- },
- onSubmit() {
- this.$refs.form.submit();
- this.enteredProjectName = '';
- },
+ onSubmit() {
+ this.$refs.form.submit();
+ this.enteredProjectName = '';
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/pages/admin/projects/index/index.js b/app/assets/javascripts/pages/admin/projects/index/index.js
index ddbefec87b6..6fa8760545d 100644
--- a/app/assets/javascripts/pages/admin/projects/index/index.js
+++ b/app/assets/javascripts/pages/admin/projects/index/index.js
@@ -28,7 +28,7 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
- $(document).on('shown.bs.modal', (event) => {
+ $(document).on('shown.bs.modal', event => {
if (event.relatedTarget.classList.contains('delete-project-button')) {
const buttonProps = event.relatedTarget.dataset;
deleteModal.deleteProjectUrl = buttonProps.deleteProjectUrl;
diff --git a/app/assets/javascripts/pages/admin/runners/index.js b/app/assets/javascripts/pages/admin/runners/index.js
new file mode 100644
index 00000000000..ce8fd18b6a2
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/runners/index.js
@@ -0,0 +1,10 @@
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners_filtered_search_token_keys';
+import { FILTERED_SEARCH } from '~/pages/constants';
+
+document.addEventListener('DOMContentLoaded', () => {
+ initFilteredSearch({
+ page: FILTERED_SEARCH.ADMIN_RUNNERS,
+ filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys,
+ });
+});
diff --git a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
index d6aa4bb95d2..4b33fcc759a 100644
--- a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
+++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
@@ -1,114 +1,119 @@
<script>
- import _ from 'underscore';
- import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
- import { s__, sprintf } from '~/locale';
+import _ from 'underscore';
+import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import { s__, sprintf } from '~/locale';
- export default {
- components: {
- DeprecatedModal,
+export default {
+ components: {
+ DeprecatedModal,
+ },
+ props: {
+ deleteUserUrl: {
+ type: String,
+ required: false,
+ default: '',
},
- props: {
- deleteUserUrl: {
- type: String,
- required: false,
- default: '',
- },
- blockUserUrl: {
- type: String,
- required: false,
- default: '',
- },
- deleteContributions: {
- type: Boolean,
- required: false,
- default: false,
- },
- username: {
- type: String,
- required: false,
- default: '',
- },
- csrfToken: {
- type: String,
- required: false,
- default: '',
- },
+ blockUserUrl: {
+ type: String,
+ required: false,
+ default: '',
},
- data() {
- return {
- enteredUsername: '',
- };
+ deleteContributions: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- computed: {
- title() {
- const keepContributionsTitle = s__('AdminUsers|Delete User %{username}?');
- const deleteContributionsTitle = s__('AdminUsers|Delete User %{username} and contributions?');
+ username: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ csrfToken: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ enteredUsername: '',
+ };
+ },
+ computed: {
+ title() {
+ const keepContributionsTitle = s__('AdminUsers|Delete User %{username}?');
+ const deleteContributionsTitle = s__('AdminUsers|Delete User %{username} and contributions?');
- return sprintf(
- this.deleteContributions ? deleteContributionsTitle : keepContributionsTitle, {
- username: `'${_.escape(this.username)}'`,
- }, false);
- },
- text() {
- const keepContributionsText = s__(`AdminArea|
+ return sprintf(
+ this.deleteContributions ? deleteContributionsTitle : keepContributionsTitle,
+ {
+ username: `'${_.escape(this.username)}'`,
+ },
+ false,
+ );
+ },
+ text() {
+ const keepContributionsText = s__(`AdminArea|
You are about to permanently delete the user %{username}.
Issues, merge requests, and groups linked to them will be transferred to a system-wide "Ghost-user".
To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead.
Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered.`);
- const deleteContributionsText = s__(`AdminArea|
+ const deleteContributionsText = s__(`AdminArea|
You are about to permanently delete the user %{username}.
This will delete all of the issues, merge requests, and groups linked to them.
To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead.
Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered.`);
- return sprintf(this.deleteContributions ? deleteContributionsText : keepContributionsText,
- {
- username: `<strong>${_.escape(this.username)}</strong>`,
- strong_start: '<strong>',
- strong_end: '</strong>',
- },
- false,
- );
- },
- confirmationTextLabel() {
- return sprintf(s__('AdminUsers|To confirm, type %{username}'),
- {
- username: `<code>${_.escape(this.username)}</code>`,
- },
- false,
- );
- },
- primaryButtonLabel() {
- const keepContributionsLabel = s__('AdminUsers|Delete user');
- const deleteContributionsLabel = s__('AdminUsers|Delete user and contributions');
+ return sprintf(
+ this.deleteContributions ? deleteContributionsText : keepContributionsText,
+ {
+ username: `<strong>${_.escape(this.username)}</strong>`,
+ strong_start: '<strong>',
+ strong_end: '</strong>',
+ },
+ false,
+ );
+ },
+ confirmationTextLabel() {
+ return sprintf(
+ s__('AdminUsers|To confirm, type %{username}'),
+ {
+ username: `<code>${_.escape(this.username)}</code>`,
+ },
+ false,
+ );
+ },
+ primaryButtonLabel() {
+ const keepContributionsLabel = s__('AdminUsers|Delete user');
+ const deleteContributionsLabel = s__('AdminUsers|Delete user and contributions');
- return this.deleteContributions ? deleteContributionsLabel : keepContributionsLabel;
- },
- secondaryButtonLabel() {
- return s__('AdminUsers|Block user');
- },
- canSubmit() {
- return this.enteredUsername === this.username;
- },
+ return this.deleteContributions ? deleteContributionsLabel : keepContributionsLabel;
+ },
+ secondaryButtonLabel() {
+ return s__('AdminUsers|Block user');
},
- methods: {
- onCancel() {
- this.enteredUsername = '';
- },
- onSecondaryAction() {
- const { form } = this.$refs;
+ canSubmit() {
+ return this.enteredUsername === this.username;
+ },
+ },
+ methods: {
+ onCancel() {
+ this.enteredUsername = '';
+ },
+ onSecondaryAction() {
+ const { form } = this.$refs;
- form.action = this.blockUserUrl;
- this.$refs.method.value = 'put';
+ form.action = this.blockUserUrl;
+ this.$refs.method.value = 'put';
- form.submit();
- },
- onSubmit() {
- this.$refs.form.submit();
- this.enteredUsername = '';
- },
+ form.submit();
+ },
+ onSubmit() {
+ this.$refs.form.submit();
+ this.enteredUsername = '';
},
- };
+ },
+};
</script>
<template>
@@ -155,10 +160,7 @@
/>
</form>
</template>
- <template
- slot="secondary-button"
- slot-scope="props"
- >
+ <template slot="secondary-button">
<button
:disabled="!canSubmit"
type="button"
diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js
index 06599c3fd5f..45046688b57 100644
--- a/app/assets/javascripts/pages/admin/users/index.js
+++ b/app/assets/javascripts/pages/admin/users/index.js
@@ -32,12 +32,14 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
- $(document).on('shown.bs.modal', (event) => {
+ $(document).on('shown.bs.modal', event => {
if (event.relatedTarget.classList.contains('delete-user-button')) {
const buttonProps = event.relatedTarget.dataset;
deleteModal.deleteUserUrl = buttonProps.deleteUserUrl;
deleteModal.blockUserUrl = buttonProps.blockUserUrl;
- deleteModal.deleteContributions = event.relatedTarget.hasAttribute('data-delete-contributions');
+ deleteModal.deleteContributions = event.relatedTarget.hasAttribute(
+ 'data-delete-contributions',
+ );
deleteModal.username = buttonProps.username;
}
});
diff --git a/app/assets/javascripts/pages/admin/users/new/index.js b/app/assets/javascripts/pages/admin/users/new/index.js
new file mode 100644
index 00000000000..3e6a090cb0e
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/users/new/index.js
@@ -0,0 +1,51 @@
+import $ from 'jquery';
+
+export default class UserInternalRegexHandler {
+ constructor() {
+ this.regexPattern = $('[data-user-internal-regex-pattern]').data('user-internal-regex-pattern');
+ if (this.regexPattern && this.regexPattern !== '') {
+ this.regexOptions = $('[data-user-internal-regex-options]').data(
+ 'user-internal-regex-options',
+ );
+ this.external = $('#user_external');
+ this.warningMessage = $('#warning_external_automatically_set');
+ this.addListenerToEmailField();
+ this.addListenerToUserExternalCheckbox();
+ }
+ }
+
+ addListenerToEmailField() {
+ $('#user_email').on('input', event => {
+ this.setExternalCheckbox(event.currentTarget.value);
+ });
+ }
+
+ addListenerToUserExternalCheckbox() {
+ this.external.on('click', () => {
+ this.warningMessage.addClass('hidden');
+ });
+ }
+
+ isEmailInternal(email) {
+ const regex = new RegExp(this.regexPattern, this.regexOptions);
+ return regex.test(email);
+ }
+
+ setExternalCheckbox(email) {
+ const isChecked = this.external.prop('checked');
+ if (this.isEmailInternal(email)) {
+ if (isChecked) {
+ this.external.prop('checked', false);
+ this.warningMessage.removeClass('hidden');
+ }
+ } else if (!isChecked) {
+ this.external.prop('checked', true);
+ this.warningMessage.addClass('hidden');
+ }
+ }
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ // eslint-disable-next-line
+ new UserInternalRegexHandler();
+});
diff --git a/app/assets/javascripts/pages/constants.js b/app/assets/javascripts/pages/constants.js
index 328b6541636..5e119454ce1 100644
--- a/app/assets/javascripts/pages/constants.js
+++ b/app/assets/javascripts/pages/constants.js
@@ -3,4 +3,5 @@
export const FILTERED_SEARCH = {
MERGE_REQUESTS: 'merge_requests',
ISSUES: 'issues',
+ ADMIN_RUNNERS: 'admin/runners',
};
diff --git a/app/assets/javascripts/pages/dashboard/groups/index/index.js b/app/assets/javascripts/pages/dashboard/groups/index/index.js
index 79987642796..b9277106a71 100644
--- a/app/assets/javascripts/pages/dashboard/groups/index/index.js
+++ b/app/assets/javascripts/pages/dashboard/groups/index/index.js
@@ -1,3 +1,5 @@
import initGroupsList from '~/groups';
-document.addEventListener('DOMContentLoaded', initGroupsList);
+document.addEventListener('DOMContentLoaded', () => {
+ initGroupsList();
+});
diff --git a/app/assets/javascripts/pages/dashboard/issues/index.js b/app/assets/javascripts/pages/dashboard/issues/index.js
index c4901dd1cb6..9055738f86e 100644
--- a/app/assets/javascripts/pages/dashboard/issues/index.js
+++ b/app/assets/javascripts/pages/dashboard/issues/index.js
@@ -1,7 +1,13 @@
import projectSelect from '~/project_select';
-import initLegacyFilters from '~/init_legacy_filters';
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
+import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => {
+ initFilteredSearch({
+ page: FILTERED_SEARCH.ISSUES,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
+ });
+
projectSelect();
- initLegacyFilters();
});
diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
index c4901dd1cb6..260484726f3 100644
--- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js
+++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
@@ -1,7 +1,15 @@
import projectSelect from '~/project_select';
-import initLegacyFilters from '~/init_legacy_filters';
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
+import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => {
+ IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests();
+
+ initFilteredSearch({
+ page: FILTERED_SEARCH.MERGE_REQUESTS,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
+ });
+
projectSelect();
- initLegacyFilters();
});
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index 9aa83ce6269..1b56b97f751 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -79,10 +79,14 @@ export default class Todos {
.then(({ data }) => {
this.updateRowState(target);
this.updateBadges(data);
- }).catch(() => flash(__('Error updating todo status.')));
+ })
+ .catch(() => {
+ this.updateRowState(target, true);
+ return flash(__('Error updating todo status.'));
+ });
}
- updateRowState(target) {
+ updateRowState(target, isInactive = false) {
const row = target.closest('li');
const restoreBtn = row.querySelector('.js-undo-todo');
const doneBtn = row.querySelector('.js-done-todo');
@@ -91,7 +95,10 @@ export default class Todos {
target.removeAttribute('disabled');
target.classList.remove('disabled');
- if (target === doneBtn) {
+ if (isInactive === true) {
+ restoreBtn.classList.add('hidden');
+ doneBtn.classList.remove('hidden');
+ } else if (target === doneBtn) {
row.classList.add('done-reversible');
restoreBtn.classList.remove('hidden');
} else if (target === restoreBtn) {
@@ -112,10 +119,12 @@ export default class Todos {
axios[target.dataset.method](target.dataset.href, {
ids: this.todo_ids,
- }).then(({ data }) => {
- this.updateAllState(target, data);
- this.updateBadges(data);
- }).catch(() => flash(__('Error updating status for all todos.')));
+ })
+ .then(({ data }) => {
+ this.updateAllState(target, data);
+ this.updateBadges(data);
+ })
+ .catch(() => flash(__('Error updating status for all todos.')));
}
updateAllState(target, data) {
@@ -127,7 +136,7 @@ export default class Todos {
target.removeAttribute('disabled');
target.classList.remove('disabled');
- this.todo_ids = (target === markAllDoneBtn) ? data.updated_ids : [];
+ this.todo_ids = target === markAllDoneBtn ? data.updated_ids : [];
undoAllBtn.classList.toggle('hidden');
markAllDoneBtn.classList.toggle('hidden');
todoListContainer.classList.toggle('hidden');
diff --git a/app/assets/javascripts/pages/groups/boards/index.js b/app/assets/javascripts/pages/groups/boards/index.js
index 5cfe8723204..79c3be771d0 100644
--- a/app/assets/javascripts/pages/groups/boards/index.js
+++ b/app/assets/javascripts/pages/groups/boards/index.js
@@ -1,5 +1,5 @@
import UsersSelect from '~/users_select';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import initBoards from '~/boards';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/groups/clusters/destroy/index.js b/app/assets/javascripts/pages/groups/clusters/destroy/index.js
new file mode 100644
index 00000000000..8001d2dd1da
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/clusters/destroy/index.js
@@ -0,0 +1,5 @@
+import ClustersBundle from '~/clusters/clusters_bundle';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new ClustersBundle(); // eslint-disable-line no-new
+});
diff --git a/app/assets/javascripts/pages/groups/clusters/index/index.js b/app/assets/javascripts/pages/groups/clusters/index/index.js
new file mode 100644
index 00000000000..845a5f7042c
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/clusters/index/index.js
@@ -0,0 +1,5 @@
+import initDismissableCallout from '~/dismissable_callout';
+
+document.addEventListener('DOMContentLoaded', () => {
+ initDismissableCallout('.gcp-signup-offer');
+});
diff --git a/app/assets/javascripts/pages/groups/clusters/show/index.js b/app/assets/javascripts/pages/groups/clusters/show/index.js
new file mode 100644
index 00000000000..8001d2dd1da
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/clusters/show/index.js
@@ -0,0 +1,5 @@
+import ClustersBundle from '~/clusters/clusters_bundle';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new ClustersBundle(); // eslint-disable-line no-new
+});
diff --git a/app/assets/javascripts/pages/groups/clusters/update/index.js b/app/assets/javascripts/pages/groups/clusters/update/index.js
new file mode 100644
index 00000000000..8001d2dd1da
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/clusters/update/index.js
@@ -0,0 +1,5 @@
+import ClustersBundle from '~/clusters/clusters_bundle';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new ClustersBundle(); // eslint-disable-line no-new
+});
diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js
index 8737f537296..32b55575f95 100644
--- a/app/assets/javascripts/pages/groups/edit/index.js
+++ b/app/assets/javascripts/pages/groups/edit/index.js
@@ -2,14 +2,20 @@ import groupAvatar from '~/group_avatar';
import TransferDropdown from '~/groups/transfer_dropdown';
import initConfirmDangerModal from '~/confirm_danger_modal';
import initSettingsPanels from '~/settings_panels';
+import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory';
+import mountBadgeSettings from '~/pages/shared/mount_badge_settings';
+import { GROUP_BADGE } from '~/badges/constants';
+import projectSelect from '~/project_select';
document.addEventListener('DOMContentLoaded', () => {
groupAvatar();
new TransferDropdown(); // eslint-disable-line no-new
initConfirmDangerModal();
-});
-
-document.addEventListener('DOMContentLoaded', () => {
- // Initialize expandable settings panels
initSettingsPanels();
+ dirtySubmitFactory(
+ document.querySelectorAll('.js-general-settings-form, .js-general-permissions-form'),
+ );
+ mountBadgeSettings(GROUP_BADGE);
+
+ projectSelect();
});
diff --git a/app/assets/javascripts/pages/groups/index.js b/app/assets/javascripts/pages/groups/index.js
new file mode 100644
index 00000000000..bf80d8b8193
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/index.js
@@ -0,0 +1,16 @@
+import initDismissableCallout from '~/dismissable_callout';
+import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const { page } = document.body.dataset;
+ const newClusterViews = [
+ 'groups:clusters:new',
+ 'groups:clusters:create_gcp',
+ 'groups:clusters:create_user',
+ ];
+
+ if (newClusterViews.indexOf(page) > -1) {
+ initDismissableCallout('.gcp-signup-offer');
+ initGkeDropdowns();
+ }
+});
diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js
index 914f804fdd3..736c6a62610 100644
--- a/app/assets/javascripts/pages/groups/issues/index.js
+++ b/app/assets/javascripts/pages/groups/issues/index.js
@@ -1,11 +1,13 @@
import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
isGroupDecendent: true,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
projectSelect();
});
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js
index 1600faa3611..339ce67438a 100644
--- a/app/assets/javascripts/pages/groups/merge_requests/index.js
+++ b/app/assets/javascripts/pages/groups/merge_requests/index.js
@@ -1,11 +1,15 @@
import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => {
+ IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests();
+
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
isGroupDecendent: true,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
projectSelect();
});
diff --git a/app/assets/javascripts/pages/groups/milestones/show/index.js b/app/assets/javascripts/pages/groups/milestones/show/index.js
index 74cc4ba42c1..ebaea5ef3dc 100644
--- a/app/assets/javascripts/pages/groups/milestones/show/index.js
+++ b/app/assets/javascripts/pages/groups/milestones/show/index.js
@@ -1,8 +1,10 @@
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
+import initDeleteMilestoneModal from '~/pages/milestones/shared/delete_milestone_modal_init';
+
import Milestone from '~/milestone';
document.addEventListener('DOMContentLoaded', () => {
initMilestonesShow();
-
+ initDeleteMilestoneModal();
Milestone.initDeprecationMessage();
});
diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
index d3b2656743d..ae0a8c74964 100644
--- a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
@@ -9,7 +9,7 @@ document.addEventListener('DOMContentLoaded', () => {
// eslint-disable-next-line no-new
new AjaxVariableList({
container: variableListEl,
- saveButton: variableListEl.querySelector('.js-secret-variables-save-button'),
+ saveButton: variableListEl.querySelector('.js-ci-variables-save-button'),
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
diff --git a/app/assets/javascripts/pages/groups/show/group_tabs.js b/app/assets/javascripts/pages/groups/show/group_tabs.js
new file mode 100644
index 00000000000..c6fe61d2bd9
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/show/group_tabs.js
@@ -0,0 +1,136 @@
+import $ from 'jquery';
+import { removeParams } from '~/lib/utils/url_utility';
+import createGroupTree from '~/groups';
+import {
+ ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
+ ACTIVE_TAB_SHARED,
+ ACTIVE_TAB_ARCHIVED,
+ CONTENT_LIST_CLASS,
+ GROUPS_LIST_HOLDER_CLASS,
+ GROUPS_FILTER_FORM_CLASS,
+} from '~/groups/constants';
+import UserTabs from '~/pages/users/user_tabs';
+import GroupFilterableList from '~/groups/groups_filterable_list';
+
+export default class GroupTabs extends UserTabs {
+ constructor({ defaultAction = 'subgroups_and_projects', action, parentEl }) {
+ super({ defaultAction, action, parentEl });
+ }
+
+ bindEvents() {
+ this.$parentEl
+ .off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
+ .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event));
+ }
+
+ tabShown(event) {
+ const $target = $(event.target);
+ const action = $target.data('action') || $target.data('targetSection');
+ const source = $target.attr('href') || $target.data('targetPath');
+
+ document.querySelector(GROUPS_FILTER_FORM_CLASS).action = source;
+
+ this.setTab(action);
+ return this.setCurrentAction(source);
+ }
+
+ setTab(action) {
+ const loadableActions = [
+ ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
+ ACTIVE_TAB_SHARED,
+ ACTIVE_TAB_ARCHIVED,
+ ];
+ this.enableSearchBar(action);
+ this.action = action;
+
+ if (this.loaded[action]) {
+ return;
+ }
+
+ if (loadableActions.includes(action)) {
+ this.cleanFilterState();
+ this.loadTab(action);
+ }
+ }
+
+ loadTab(action) {
+ const elId = `js-groups-${action}-tree`;
+ const endpoint = this.getEndpoint(action);
+
+ this.toggleLoading(true);
+
+ createGroupTree(elId, endpoint, action);
+ this.loaded[action] = true;
+
+ this.toggleLoading(false);
+ }
+
+ getEndpoint(action) {
+ const { endpointsDefault, endpointsShared } = this.$parentEl.data();
+ let endpoint;
+
+ switch (action) {
+ case ACTIVE_TAB_ARCHIVED:
+ endpoint = `${endpointsDefault}?archived=only`;
+ break;
+ case ACTIVE_TAB_SHARED:
+ endpoint = endpointsShared;
+ break;
+ default:
+ // ACTIVE_TAB_SUBGROUPS_AND_PROJECTS
+ endpoint = endpointsDefault;
+ break;
+ }
+
+ return endpoint;
+ }
+
+ enableSearchBar(action) {
+ const containerEl = document.getElementById(action);
+ const form = document.querySelector(GROUPS_FILTER_FORM_CLASS);
+ const filter = form.querySelector('.js-groups-list-filter');
+ const holder = containerEl.querySelector(GROUPS_LIST_HOLDER_CLASS);
+ const dataEl = containerEl.querySelector(CONTENT_LIST_CLASS);
+ const endpoint = this.getEndpoint(action);
+
+ if (!dataEl) {
+ return;
+ }
+
+ const { dataset } = dataEl;
+ const opts = {
+ form,
+ filter,
+ holder,
+ filterEndpoint: endpoint || dataset.endpoint,
+ pagePath: null,
+ dropdownSel: '.js-group-filter-dropdown-wrap',
+ filterInputField: 'filter',
+ action,
+ };
+
+ if (!this.loaded[action]) {
+ const filterableList = new GroupFilterableList(opts);
+ filterableList.initSearch();
+ }
+ }
+
+ cleanFilterState() {
+ const values = Object.values(this.loaded);
+ const loadedTabs = values.filter(e => e === true);
+
+ if (!loadedTabs.length) {
+ return;
+ }
+
+ const newState = removeParams(['page'], window.location.search);
+
+ window.history.replaceState(
+ {
+ url: newState,
+ },
+ document.title,
+ newState,
+ );
+ }
+}
diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js
index d7b35d2b26b..3a45fd70d02 100644
--- a/app/assets/javascripts/pages/groups/show/index.js
+++ b/app/assets/javascripts/pages/groups/show/index.js
@@ -1,14 +1,22 @@
/* eslint-disable no-new */
+import { getPagePath } from '~/lib/utils/common_utils';
+import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants';
import NewGroupChild from '~/groups/new_group_child';
import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
-import ShortcutsNavigation from '~/shortcuts_navigation';
-import initGroupsList from '~/groups';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
+import GroupTabs from './group_tabs';
document.addEventListener('DOMContentLoaded', () => {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
+ const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED];
+ const paths = window.location.pathname.split('/');
+ const subpath = paths[paths.length - 1];
+ const action = loadableActions.includes(subpath) ? subpath : getPagePath(1);
+
+ new GroupTabs({ parentEl: '.groups-listing', action });
new ShortcutsNavigation();
new NotificationsForm();
notificationsDropdown();
@@ -17,6 +25,4 @@ document.addEventListener('DOMContentLoaded', () => {
if (newGroupChildWrapper) {
new NewGroupChild(newGroupChildWrapper);
}
-
- initGroupsList();
});
diff --git a/app/assets/javascripts/pages/ide/index.js b/app/assets/javascripts/pages/ide/index.js
index efadf6967aa..d192df3561e 100644
--- a/app/assets/javascripts/pages/ide/index.js
+++ b/app/assets/javascripts/pages/ide/index.js
@@ -1,9 +1,3 @@
-import { initIde, resetServiceWorkersPublicPath } from '~/ide/index';
+import { startIde } from '~/ide/index';
-document.addEventListener('DOMContentLoaded', () => {
- const ideElement = document.getElementById('ide');
- if (ideElement) {
- resetServiceWorkersPublicPath();
- initIde(ideElement);
- }
-});
+startIde();
diff --git a/app/assets/javascripts/pages/admin/conversational_development_index/show/index.js b/app/assets/javascripts/pages/instance_statistics/conversational_development_index/index.js
index c1056537f90..c1056537f90 100644
--- a/app/assets/javascripts/pages/admin/conversational_development_index/show/index.js
+++ b/app/assets/javascripts/pages/instance_statistics/conversational_development_index/index.js
diff --git a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue
index 4061c11ba8f..a4778077bc4 100644
--- a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue
+++ b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue
@@ -1,94 +1,117 @@
<script>
- import axios from '~/lib/utils/axios_utils';
+import axios from '~/lib/utils/axios_utils';
- import Flash from '~/flash';
- import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
- import { n__, s__, sprintf } from '~/locale';
- import { redirectTo } from '~/lib/utils/url_utility';
- import eventHub from '../event_hub';
+import Flash from '~/flash';
+import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import { n__, s__, sprintf } from '~/locale';
+import { redirectTo } from '~/lib/utils/url_utility';
+import eventHub from '../event_hub';
- export default {
- components: {
- DeprecatedModal,
+export default {
+ components: {
+ DeprecatedModal,
+ },
+ props: {
+ issueCount: {
+ type: Number,
+ required: true,
},
- props: {
- issueCount: {
- type: Number,
- required: true,
- },
- mergeRequestCount: {
- type: Number,
- required: true,
- },
- milestoneId: {
- type: Number,
- required: true,
- },
- milestoneTitle: {
- type: String,
- required: true,
- },
- milestoneUrl: {
- type: String,
- required: true,
- },
+ mergeRequestCount: {
+ type: Number,
+ required: true,
},
- computed: {
- text() {
- const milestoneTitle = sprintf('<strong>%{milestoneTitle}</strong>', { milestoneTitle: this.milestoneTitle });
-
- if (this.issueCount === 0 && this.mergeRequestCount === 0) {
- return sprintf(
- s__(`Milestones|
-You’re about to permanently delete the milestone %{milestoneTitle} from this project.
-%{milestoneTitle} is not currently used in any issues or merge requests.`),
- {
- milestoneTitle,
- },
- false,
- );
- }
+ milestoneId: {
+ type: Number,
+ required: true,
+ },
+ milestoneTitle: {
+ type: String,
+ required: true,
+ },
+ milestoneUrl: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ text() {
+ const milestoneTitle = sprintf('<strong>%{milestoneTitle}</strong>', {
+ milestoneTitle: this.milestoneTitle,
+ });
+ if (this.issueCount === 0 && this.mergeRequestCount === 0) {
return sprintf(
s__(`Milestones|
-You’re about to permanently delete the milestone %{milestoneTitle} from this project and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}.
-Once deleted, it cannot be undone or recovered.`),
+You’re about to permanently delete the milestone %{milestoneTitle}.
+This milestone is not currently used in any issues or merge requests.`),
{
milestoneTitle,
- issuesWithCount: n__('%d issue', '%d issues', this.issueCount),
- mergeRequestsWithCount: n__('%d merge request', '%d merge requests', this.mergeRequestCount),
},
false,
);
- },
- title() {
- return sprintf(s__('Milestones|Delete milestone %{milestoneTitle}?'), { milestoneTitle: this.milestoneTitle });
- },
- },
- methods: {
- onSubmit() {
- eventHub.$emit('deleteMilestoneModal.requestStarted', this.milestoneUrl);
+ }
- return axios.delete(this.milestoneUrl)
- .then((response) => {
- eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: true });
+ return sprintf(
+ s__(`Milestones|
+You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}.
+Once deleted, it cannot be undone or recovered.`),
+ {
+ milestoneTitle,
+ issuesWithCount: n__('%d issue', '%d issues', this.issueCount),
+ mergeRequestsWithCount: n__(
+ '%d merge request',
+ '%d merge requests',
+ this.mergeRequestCount,
+ ),
+ },
+ false,
+ );
+ },
+ title() {
+ return sprintf(s__('Milestones|Delete milestone %{milestoneTitle}?'), {
+ milestoneTitle: this.milestoneTitle,
+ });
+ },
+ },
+ methods: {
+ onSubmit() {
+ eventHub.$emit('deleteMilestoneModal.requestStarted', this.milestoneUrl);
- // follow the rediect to milestones overview page
- redirectTo(response.request.responseURL);
- })
- .catch((error) => {
- eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: false });
+ return axios
+ .delete(this.milestoneUrl)
+ .then(response => {
+ eventHub.$emit('deleteMilestoneModal.requestFinished', {
+ milestoneUrl: this.milestoneUrl,
+ successful: true,
+ });
- if (error.response && error.response.status === 404) {
- Flash(sprintf(s__('Milestones|Milestone %{milestoneTitle} was not found'), { milestoneTitle: this.milestoneTitle }));
- } else {
- Flash(sprintf(s__('Milestones|Failed to delete milestone %{milestoneTitle}'), { milestoneTitle: this.milestoneTitle }));
- }
- throw error;
+ // follow the rediect to milestones overview page
+ redirectTo(response.request.responseURL);
+ })
+ .catch(error => {
+ eventHub.$emit('deleteMilestoneModal.requestFinished', {
+ milestoneUrl: this.milestoneUrl,
+ successful: false,
});
- },
+
+ if (error.response && error.response.status === 404) {
+ Flash(
+ sprintf(s__('Milestones|Milestone %{milestoneTitle} was not found'), {
+ milestoneTitle: this.milestoneTitle,
+ }),
+ );
+ } else {
+ Flash(
+ sprintf(s__('Milestones|Failed to delete milestone %{milestoneTitle}'), {
+ milestoneTitle: this.milestoneTitle,
+ }),
+ );
+ }
+ throw error;
+ });
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
index 2c683a39f42..9d19e4a095d 100644
--- a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
+++ b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
@@ -1,54 +1,66 @@
<script>
- import axios from '~/lib/utils/axios_utils';
- import createFlash from '~/flash';
- import GlModal from '~/vue_shared/components/gl_modal.vue';
- import { s__, sprintf } from '~/locale';
- import { visitUrl } from '~/lib/utils/url_utility';
- import eventHub from '../event_hub';
+import axios from '~/lib/utils/axios_utils';
+import createFlash from '~/flash';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import { s__, sprintf } from '~/locale';
+import { visitUrl } from '~/lib/utils/url_utility';
+import eventHub from '../event_hub';
- export default {
- components: {
- GlModal,
+export default {
+ components: {
+ GlModal,
+ },
+ props: {
+ milestoneTitle: {
+ type: String,
+ required: true,
},
- props: {
- milestoneTitle: {
- type: String,
- required: true,
- },
- url: {
- type: String,
- required: true,
- },
- groupName: {
- type: String,
- required: true,
- },
+ url: {
+ type: String,
+ required: true,
},
- computed: {
- title() {
- return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), { milestoneTitle: this.milestoneTitle });
- },
- text() {
- return sprintf(s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}.
+ groupName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ title() {
+ return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), {
+ milestoneTitle: this.milestoneTitle,
+ });
+ },
+ text() {
+ return sprintf(
+ s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}.
Existing project milestones with the same title will be merged.
- This action cannot be reversed.`), { milestoneTitle: this.milestoneTitle, groupName: this.groupName });
- },
+ This action cannot be reversed.`),
+ { milestoneTitle: this.milestoneTitle, groupName: this.groupName },
+ );
},
- methods: {
- onSubmit() {
- eventHub.$emit('promoteMilestoneModal.requestStarted', this.url);
- return axios.post(this.url, { params: { format: 'json' } })
- .then((response) => {
- eventHub.$emit('promoteMilestoneModal.requestFinished', { milestoneUrl: this.url, successful: true });
- visitUrl(response.data.url);
- })
- .catch((error) => {
- eventHub.$emit('promoteMilestoneModal.requestFinished', { milestoneUrl: this.url, successful: false });
- createFlash(error);
+ },
+ methods: {
+ onSubmit() {
+ eventHub.$emit('promoteMilestoneModal.requestStarted', this.url);
+ return axios
+ .post(this.url, { params: { format: 'json' } })
+ .then(response => {
+ eventHub.$emit('promoteMilestoneModal.requestFinished', {
+ milestoneUrl: this.url,
+ successful: true,
});
- },
+ visitUrl(response.data.url);
+ })
+ .catch(error => {
+ eventHub.$emit('promoteMilestoneModal.requestFinished', {
+ milestoneUrl: this.url,
+ successful: false,
+ });
+ createFlash(error);
+ });
},
- };
+ },
+};
</script>
<template>
<gl-modal
@@ -65,4 +77,3 @@
{{ text }}
</gl-modal>
</template>
-
diff --git a/app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js b/app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js
index d51b5c221e3..1d559dc6e41 100644
--- a/app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js
+++ b/app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js
@@ -7,7 +7,9 @@ export default () => {
Vue.use(Translate);
const onRequestFinished = ({ milestoneUrl, successful }) => {
- const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`);
+ const button = document.querySelector(
+ `.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`,
+ );
if (!successful) {
button.removeAttribute('disabled');
@@ -16,14 +18,16 @@ export default () => {
button.querySelector('.js-loading-icon').classList.add('hidden');
};
- const onRequestStarted = (milestoneUrl) => {
- const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`);
+ const onRequestStarted = milestoneUrl => {
+ const button = document.querySelector(
+ `.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`,
+ );
button.setAttribute('disabled', '');
button.querySelector('.js-loading-icon').classList.remove('hidden');
eventHub.$once('deleteMilestoneModal.requestFinished', onRequestFinished);
};
- const onDeleteButtonClick = (event) => {
+ const onDeleteButtonClick = event => {
const button = event.currentTarget;
const modalProps = {
milestoneId: parseInt(button.dataset.milestoneId, 10),
@@ -37,12 +41,12 @@ export default () => {
};
const deleteMilestoneButtons = document.querySelectorAll('.js-delete-milestone-button');
- deleteMilestoneButtons.forEach((button) => {
+ deleteMilestoneButtons.forEach(button => {
button.addEventListener('click', onDeleteButtonClick);
});
eventHub.$once('deleteMilestoneModal.mounted', () => {
- deleteMilestoneButtons.forEach((button) => {
+ deleteMilestoneButtons.forEach(button => {
button.removeAttribute('disabled');
});
});
diff --git a/app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js b/app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js
index 8e79341e96a..fcc62a2b2af 100644
--- a/app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js
+++ b/app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js
@@ -7,20 +7,24 @@ Vue.use(Translate);
export default () => {
const onRequestFinished = ({ milestoneUrl, successful }) => {
- const button = document.querySelector(`.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`);
+ const button = document.querySelector(
+ `.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`,
+ );
if (!successful) {
button.removeAttribute('disabled');
}
};
- const onRequestStarted = (milestoneUrl) => {
- const button = document.querySelector(`.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`);
+ const onRequestStarted = milestoneUrl => {
+ const button = document.querySelector(
+ `.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`,
+ );
button.setAttribute('disabled', '');
eventHub.$once('promoteMilestoneModal.requestFinished', onRequestFinished);
};
- const onDeleteButtonClick = (event) => {
+ const onDeleteButtonClick = event => {
const button = event.currentTarget;
const modalProps = {
milestoneTitle: button.dataset.milestoneTitle,
@@ -32,12 +36,12 @@ export default () => {
};
const promoteMilestoneButtons = document.querySelectorAll('.js-promote-project-milestone-button');
- promoteMilestoneButtons.forEach((button) => {
+ promoteMilestoneButtons.forEach(button => {
button.addEventListener('click', onDeleteButtonClick);
});
eventHub.$once('promoteMilestoneModal.mounted', () => {
- promoteMilestoneButtons.forEach((button) => {
+ promoteMilestoneButtons.forEach(button => {
button.removeAttribute('disabled');
});
});
diff --git a/app/assets/javascripts/pages/profiles/index.js b/app/assets/javascripts/pages/profiles/index.js
index 04e50963699..883be18b336 100644
--- a/app/assets/javascripts/pages/profiles/index.js
+++ b/app/assets/javascripts/pages/profiles/index.js
@@ -3,9 +3,12 @@ import '~/profile/gl_crop';
import Profile from '~/profile/profile';
document.addEventListener('DOMContentLoaded', () => {
- $(document).on('input.ssh_key', '#key_key', function () { // eslint-disable-line func-names
+ // eslint-disable-next-line func-names
+ $(document).on('input.ssh_key', '#key_key', function() {
const $title = $('#key_title');
- const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
+ const comment = $(this)
+ .val()
+ .match(/^\S+ \S+ (.+)\n?$/);
// Extract the SSH Key title from its comment
if (comment && comment.length > 1) {
diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js
index 949219a0837..c7ce4675573 100644
--- a/app/assets/javascripts/pages/profiles/show/index.js
+++ b/app/assets/javascripts/pages/profiles/show/index.js
@@ -3,15 +3,22 @@ import createFlash from '~/flash';
import GfmAutoComplete from '~/gfm_auto_complete';
import EmojiMenu from './emoji_menu';
+const defaultStatusEmoji = 'speech_balloon';
+
document.addEventListener('DOMContentLoaded', () => {
const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu';
const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector);
const statusEmojiField = document.getElementById('js-status-emoji-field');
const statusMessageField = document.getElementById('js-status-message-field');
- const findNoEmojiPlaceholder = () => document.getElementById('js-no-emoji-placeholder');
+ const toggleNoEmojiPlaceholder = isVisible => {
+ const placeholderElement = document.getElementById('js-no-emoji-placeholder');
+ placeholderElement.classList.toggle('hidden', !isVisible);
+ };
+
+ const findStatusEmoji = () => toggleEmojiMenuButton.querySelector('gl-emoji');
const removeStatusEmoji = () => {
- const statusEmoji = toggleEmojiMenuButton.querySelector('gl-emoji');
+ const statusEmoji = findStatusEmoji();
if (statusEmoji) {
statusEmoji.remove();
}
@@ -19,7 +26,7 @@ document.addEventListener('DOMContentLoaded', () => {
const selectEmojiCallback = (emoji, emojiTag) => {
statusEmojiField.value = emoji;
- findNoEmojiPlaceholder().classList.add('hidden');
+ toggleNoEmojiPlaceholder(false);
removeStatusEmoji();
toggleEmojiMenuButton.innerHTML += emojiTag;
};
@@ -29,7 +36,7 @@ document.addEventListener('DOMContentLoaded', () => {
statusEmojiField.value = '';
statusMessageField.value = '';
removeStatusEmoji();
- findNoEmojiPlaceholder().classList.remove('hidden');
+ toggleNoEmojiPlaceholder(true);
});
const emojiAutocomplete = new GfmAutoComplete();
@@ -44,6 +51,23 @@ document.addEventListener('DOMContentLoaded', () => {
selectEmojiCallback,
);
emojiMenu.bindEvents();
+
+ const defaultEmojiTag = Emoji.glEmojiTag(defaultStatusEmoji);
+ statusMessageField.addEventListener('input', () => {
+ const hasStatusMessage = statusMessageField.value.trim() !== '';
+ const statusEmoji = findStatusEmoji();
+ if (hasStatusMessage && statusEmoji) {
+ return;
+ }
+
+ if (hasStatusMessage) {
+ toggleNoEmojiPlaceholder(false);
+ toggleEmojiMenuButton.innerHTML += defaultEmojiTag;
+ } else if (statusEmoji.dataset.name === defaultStatusEmoji) {
+ toggleNoEmojiPlaceholder(true);
+ removeStatusEmoji();
+ }
+ });
})
- .catch(() => createFlash('Failed to load emoji list!'));
+ .catch(() => createFlash('Failed to load emoji list.'));
});
diff --git a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js
index 8e8f47c21d8..417935e2ad0 100644
--- a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js
+++ b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js
@@ -5,7 +5,9 @@ document.addEventListener('DOMContentLoaded', () => {
const twoFactorNode = document.querySelector('.js-two-factor-auth');
const skippable = twoFactorNode.dataset.twoFactorSkippable === 'true';
if (skippable) {
- const button = `<a class="btn btn-sm btn-warning float-right" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`;
+ const button = `<a class="btn btn-sm btn-warning float-right" data-method="patch" href="${
+ twoFactorNode.dataset.two_factor_skip_url
+ }">Configure it later</a>`;
const flashAlert = document.querySelector('.flash-alert .container-fluid');
if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button);
}
diff --git a/app/assets/javascripts/pages/projects/activity/index.js b/app/assets/javascripts/pages/projects/activity/index.js
index 5543ad82428..d39ea3d10bf 100644
--- a/app/assets/javascripts/pages/projects/activity/index.js
+++ b/app/assets/javascripts/pages/projects/activity/index.js
@@ -1,5 +1,5 @@
import Activities from '~/activities';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
new Activities(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/artifacts/browse/index.js b/app/assets/javascripts/pages/projects/artifacts/browse/index.js
index ea7458fe9b8..26dc90a56d7 100644
--- a/app/assets/javascripts/pages/projects/artifacts/browse/index.js
+++ b/app/assets/javascripts/pages/projects/artifacts/browse/index.js
@@ -1,5 +1,5 @@
import BuildArtifacts from '~/build_artifacts';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/artifacts/file/index.js b/app/assets/javascripts/pages/projects/artifacts/file/index.js
index 8484e5e9848..249900d6cb7 100644
--- a/app/assets/javascripts/pages/projects/artifacts/file/index.js
+++ b/app/assets/javascripts/pages/projects/artifacts/file/index.js
@@ -1,5 +1,5 @@
import BlobViewer from '~/blob/viewer/index';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/boards/index.js b/app/assets/javascripts/pages/projects/boards/index.js
index 5cfe8723204..79c3be771d0 100644
--- a/app/assets/javascripts/pages/projects/boards/index.js
+++ b/app/assets/javascripts/pages/projects/boards/index.js
@@ -1,5 +1,5 @@
import UsersSelect from '~/users_select';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import initBoards from '~/boards';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js
index a9658fd1eb4..13ff47d53c2 100644
--- a/app/assets/javascripts/pages/projects/branches/new/index.js
+++ b/app/assets/javascripts/pages/projects/branches/new/index.js
@@ -1,6 +1,11 @@
import $ from 'jquery';
import NewBranchForm from '~/new_branch_form';
-document.addEventListener('DOMContentLoaded', () => (
- new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML))
-));
+document.addEventListener(
+ 'DOMContentLoaded',
+ () =>
+ new NewBranchForm(
+ $('.js-create-branch-form'),
+ JSON.parse(document.getElementById('availableRefs').innerHTML),
+ ),
+);
diff --git a/app/assets/javascripts/pages/projects/clusters/gcp/new/index.js b/app/assets/javascripts/pages/projects/clusters/gcp/new/index.js
deleted file mode 100644
index d4f34e32a48..00000000000
--- a/app/assets/javascripts/pages/projects/clusters/gcp/new/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
-
-document.addEventListener('DOMContentLoaded', () => {
- initGkeDropdowns();
-});
diff --git a/app/assets/javascripts/pages/projects/clusters/index/index.js b/app/assets/javascripts/pages/projects/clusters/index/index.js
index e4b8baede58..845a5f7042c 100644
--- a/app/assets/javascripts/pages/projects/clusters/index/index.js
+++ b/app/assets/javascripts/pages/projects/clusters/index/index.js
@@ -1,5 +1,5 @@
-import ClustersIndex from '~/clusters/clusters_index';
+import initDismissableCallout from '~/dismissable_callout';
document.addEventListener('DOMContentLoaded', () => {
- new ClustersIndex(); // eslint-disable-line no-new
+ initDismissableCallout('.gcp-signup-offer');
});
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 2e23cce11ce..f477424811d 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -3,7 +3,7 @@
import $ from 'jquery';
import Diff from '~/diff';
import ZenMode from '~/zen_mode';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
import initNotes from '~/init_notes';
import initChangesDropdown from '~/init_changes_dropdown';
diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js
index 3682020579b..ad671ce9351 100644
--- a/app/assets/javascripts/pages/projects/commits/show/index.js
+++ b/app/assets/javascripts/pages/projects/commits/show/index.js
@@ -1,6 +1,6 @@
import CommitsList from '~/commits';
import GpgBadges from '~/gpg_badges';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js
index 628913483c6..f5b1cf85e68 100644
--- a/app/assets/javascripts/pages/projects/edit/index.js
+++ b/app/assets/javascripts/pages/projects/edit/index.js
@@ -1,6 +1,8 @@
+import { PROJECT_BADGE } from '~/badges/constants';
import initSettingsPanels from '~/settings_panels';
import setupProjectEdit from '~/project_edit';
import initConfirmDangerModal from '~/confirm_danger_modal';
+import mountBadgeSettings from '~/pages/shared/mount_badge_settings';
import initProjectLoadingSpinner from '../shared/save_project_loader';
import projectAvatar from '../shared/project_avatar';
import initProjectPermissionsSettings from '../shared/permissions';
@@ -13,4 +15,5 @@ document.addEventListener('DOMContentLoaded', () => {
projectAvatar();
initProjectPermissionsSettings();
initConfirmDangerModal();
+ mountBadgeSettings(PROJECT_BADGE);
});
diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js
index 24630c2aa05..388d7d7bdda 100644
--- a/app/assets/javascripts/pages/projects/find_file/show/index.js
+++ b/app/assets/javascripts/pages/projects/find_file/show/index.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import ProjectFindFile from '~/project_find_file';
-import ShortcutsFindFile from '~/shortcuts_find_file';
+import ShortcutsFindFile from '~/behaviors/shortcuts/shortcuts_find_file';
document.addEventListener('DOMContentLoaded', () => {
const findElement = document.querySelector('.js-file-finder');
diff --git a/app/assets/javascripts/pages/projects/graphs/charts/index.js b/app/assets/javascripts/pages/projects/graphs/charts/index.js
index 80159a82bd4..3ccad513c05 100644
--- a/app/assets/javascripts/pages/projects/graphs/charts/index.js
+++ b/app/assets/javascripts/pages/projects/graphs/charts/index.js
@@ -31,14 +31,16 @@ document.addEventListener('DOMContentLoaded', () => {
const chartData = data => ({
labels: Object.keys(data),
- datasets: [{
- fillColor: 'rgba(220,220,220,0.5)',
- strokeColor: 'rgba(220,220,220,1)',
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data: _.values(data),
- }],
+ datasets: [
+ {
+ fillColor: 'rgba(220,220,220,0.5)',
+ strokeColor: 'rgba(220,220,220,1)',
+ barStrokeWidth: 1,
+ barValueSpacing: 1,
+ barDatasetSpacing: 1,
+ data: _.values(data),
+ },
+ ],
});
const hourData = chartData(projectChartData.hour);
@@ -51,7 +53,9 @@ document.addEventListener('DOMContentLoaded', () => {
responsiveChart($('#month-chart'), monthData);
const data = projectChartData.languages;
- const ctx = $('#languages-chart').get(0).getContext('2d');
+ const ctx = $('#languages-chart')
+ .get(0)
+ .getContext('2d');
const options = {
scaleOverlay: true,
responsive: true,
diff --git a/app/assets/javascripts/pages/projects/graphs/show/index.js b/app/assets/javascripts/pages/projects/graphs/show/index.js
index 71f629fbc13..f79c386b59e 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/index.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/index.js
@@ -7,7 +7,8 @@ import ContributorsStatGraph from './stat_graph_contributors';
document.addEventListener('DOMContentLoaded', () => {
const url = document.querySelector('.js-graphs-show').dataset.projectGraphPath;
- axios.get(url)
+ axios
+ .get(url)
.then(({ data }) => {
const graph = new ContributorsStatGraph();
graph.init(data);
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
index 6c1788dc160..76613394af6 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
@@ -1,9 +1,13 @@
-/* eslint-disable func-names, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign */
+/* eslint-disable func-names, no-var, one-var, camelcase, no-param-reassign, prefer-template, no-return-assign */
import $ from 'jquery';
import _ from 'underscore';
import { n__, s__, createDateTimeFormat, sprintf } from '~/locale';
-import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
+import {
+ ContributorsGraph,
+ ContributorsAuthorGraph,
+ ContributorsMasterGraph,
+} from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
export default (function() {
@@ -14,7 +18,7 @@ export default (function() {
ContributorsStatGraph.prototype.init = function(log) {
var author_commits, total_commits;
this.parsed_log = ContributorsStatGraphUtil.parse_log(log);
- this.set_current_field("commits");
+ this.set_current_field('commits');
total_commits = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field);
author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field);
this.add_master_graph(total_commits);
@@ -31,23 +35,26 @@ export default (function() {
var limited_author_data;
this.authors = [];
limited_author_data = author_data.slice(0, 100);
- return _.each(limited_author_data, (function(_this) {
- return function(d) {
- var author_graph, author_header;
- author_header = _this.create_author_header(d);
- $(".contributors-list").append(author_header);
-
- author_graph = new ContributorsAuthorGraph(d.dates);
- _this.authors[d.author_name] = author_graph;
- return author_graph.draw();
- };
- })(this));
+ return _.each(
+ limited_author_data,
+ (function(_this) {
+ return function(d) {
+ var author_graph, author_header;
+ author_header = _this.create_author_header(d);
+ $('.contributors-list').append(author_header);
+
+ author_graph = new ContributorsAuthorGraph(d.dates);
+ _this.authors[d.author_name] = author_graph;
+ return author_graph.draw();
+ };
+ })(this),
+ );
};
ContributorsStatGraph.prototype.format_author_commit_info = function(author) {
var commits;
commits = $('<span/>', {
- "class": 'graph-author-commits-count'
+ class: 'graph-author-commits-count',
});
commits.text(n__('%d commit', '%d commits', author.commits));
return $('<span/>').append(commits);
@@ -56,13 +63,13 @@ export default (function() {
ContributorsStatGraph.prototype.create_author_header = function(author) {
var author_commit_info, author_commit_info_span, author_email, author_name, list_item;
list_item = $('<li/>', {
- "class": 'person',
- style: 'display: block;'
+ class: 'person',
+ style: 'display: block;',
});
author_name = $('<h4>' + author.author_name + '</h4>');
author_email = $('<p class="graph-author-email">' + author.author_email + '</p>');
author_commit_info_span = $('<span/>', {
- "class": 'commits'
+ class: 'commits',
});
author_commit_info = this.format_author_commit_info(author);
author_commit_info_span.html(author_commit_info);
@@ -80,37 +87,41 @@ export default (function() {
};
ContributorsStatGraph.prototype.redraw_authors = function() {
- $("ol").html("");
+ $('ol').html('');
const { x_domain } = ContributorsGraph.prototype;
- const author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain);
-
- return _.each(author_commits, (function(_this) {
- return function(d) {
- _this.redraw_author_commit_info(d);
- if (_this.authors[d.author_name] != null) {
- $(_this.authors[d.author_name].list_item).appendTo("ol");
- _this.authors[d.author_name].set_data(d.dates);
- return _this.authors[d.author_name].redraw();
- }
- return '';
- };
- })(this));
+ const author_commits = ContributorsStatGraphUtil.get_author_data(
+ this.parsed_log,
+ this.field,
+ x_domain,
+ );
+
+ return _.each(
+ author_commits,
+ (function(_this) {
+ return function(d) {
+ _this.redraw_author_commit_info(d);
+ if (_this.authors[d.author_name] != null) {
+ $(_this.authors[d.author_name].list_item).appendTo('ol');
+ _this.authors[d.author_name].set_data(d.dates);
+ return _this.authors[d.author_name].redraw();
+ }
+ return '';
+ };
+ })(this),
+ );
};
ContributorsStatGraph.prototype.set_current_field = function(field) {
- return this.field = field;
+ return (this.field = field);
};
ContributorsStatGraph.prototype.change_date_header = function() {
const { x_domain } = ContributorsGraph.prototype;
- const formattedDateRange = sprintf(
- s__('ContributorsPage|%{startDate} – %{endDate}'),
- {
- startDate: this.dateFormat.format(new Date(x_domain[0])),
- endDate: this.dateFormat.format(new Date(x_domain[1])),
- },
- );
+ const formattedDateRange = sprintf(s__('ContributorsPage|%{startDate} – %{endDate}'), {
+ startDate: this.dateFormat.format(new Date(x_domain[0])),
+ endDate: this.dateFormat.format(new Date(x_domain[1])),
+ });
return $('#date_header').text(formattedDateRange);
};
@@ -120,7 +131,7 @@ export default (function() {
if ($author != null) {
author_list_item = $(this.authors[author.author_name].list_item);
author_commit_info = this.format_author_commit_info(author);
- return author_list_item.find("span").html(author_commit_info);
+ return author_list_item.find('span').html(author_commit_info);
}
return '';
};
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
index a02ec9e5f00..377dce6c746 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, max-len, no-restricted-syntax, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */
+/* eslint-disable func-names, no-restricted-syntax, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, no-return-assign, prefer-arrow-callback, prefer-template, no-else-return, no-shadow */
import $ from 'jquery';
import _ from 'underscore';
@@ -11,10 +11,32 @@ import { brushX } from 'd3-brush';
import { timeParse } from 'd3-time-format';
import { dateTickFormat } from '~/lib/utils/tick_formats';
-const d3 = { extent, max, select, scaleTime, scaleLinear, axisLeft, axisBottom, area, brushX, timeParse };
+const d3 = {
+ extent,
+ max,
+ select,
+ scaleTime,
+ scaleLinear,
+ axisLeft,
+ axisBottom,
+ area,
+ brushX,
+ timeParse,
+};
const hasProp = {}.hasOwnProperty;
-const extend = function(child, parent) { for (const key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+const extend = function(child, parent) {
+ for (const key in parent) {
+ if (hasProp.call(parent, key)) child[key] = parent[key];
+ }
+ function ctor() {
+ this.constructor = child;
+ }
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor();
+ child.__super__ = parent.prototype;
+ return child;
+};
export const ContributorsGraph = (function() {
function ContributorsGraph() {}
@@ -23,7 +45,7 @@ export const ContributorsGraph = (function() {
top: 20,
right: 10,
bottom: 30,
- left: 40
+ left: 40,
};
ContributorsGraph.prototype.x_domain = null;
@@ -33,35 +55,39 @@ export const ContributorsGraph = (function() {
ContributorsGraph.prototype.dates = [];
ContributorsGraph.prototype.determine_width = function(baseWidth, $parentElement) {
- const parentPaddingWidth = parseFloat($parentElement.css('padding-left')) + parseFloat($parentElement.css('padding-right'));
+ const parentPaddingWidth =
+ parseFloat($parentElement.css('padding-left')) +
+ parseFloat($parentElement.css('padding-right'));
const marginWidth = this.MARGIN.left + this.MARGIN.right;
return baseWidth - parentPaddingWidth - marginWidth;
};
ContributorsGraph.set_x_domain = function(data) {
- return ContributorsGraph.prototype.x_domain = data;
+ return (ContributorsGraph.prototype.x_domain = data);
};
ContributorsGraph.set_y_domain = function(data) {
- return ContributorsGraph.prototype.y_domain = [
- 0, d3.max(data, function(d) {
- return d.commits = d.commits || d.additions || d.deletions;
- })
- ];
+ return (ContributorsGraph.prototype.y_domain = [
+ 0,
+ d3.max(data, function(d) {
+ return (d.commits = d.commits || d.additions || d.deletions);
+ }),
+ ]);
};
ContributorsGraph.init_x_domain = function(data) {
- return ContributorsGraph.prototype.x_domain = d3.extent(data, function(d) {
+ return (ContributorsGraph.prototype.x_domain = d3.extent(data, function(d) {
return d.date;
- });
+ }));
};
ContributorsGraph.init_y_domain = function(data) {
- return ContributorsGraph.prototype.y_domain = [
- 0, d3.max(data, function(d) {
- return d.commits = d.commits || d.additions || d.deletions;
- })
- ];
+ return (ContributorsGraph.prototype.y_domain = [
+ 0,
+ d3.max(data, function(d) {
+ return (d.commits = d.commits || d.additions || d.deletions);
+ }),
+ ]);
};
ContributorsGraph.init_domain = function(data) {
@@ -70,7 +96,7 @@ export const ContributorsGraph = (function() {
};
ContributorsGraph.set_dates = function(data) {
- return ContributorsGraph.prototype.dates = data;
+ return (ContributorsGraph.prototype.dates = data);
};
ContributorsGraph.prototype.set_x_domain = function() {
@@ -87,20 +113,33 @@ export const ContributorsGraph = (function() {
};
ContributorsGraph.prototype.create_scale = function(width, height) {
- this.x = d3.scaleTime().range([0, width]).clamp(true);
- return this.y = d3.scaleLinear().range([height, 0]).nice();
+ this.x = d3
+ .scaleTime()
+ .range([0, width])
+ .clamp(true);
+ return (this.y = d3
+ .scaleLinear()
+ .range([height, 0])
+ .nice());
};
ContributorsGraph.prototype.draw_x_axis = function() {
- return this.svg.append("g").attr("class", "x axis").attr("transform", "translate(0, " + this.height + ")").call(this.x_axis);
+ return this.svg
+ .append('g')
+ .attr('class', 'x axis')
+ .attr('transform', 'translate(0, ' + this.height + ')')
+ .call(this.x_axis);
};
ContributorsGraph.prototype.draw_y_axis = function() {
- return this.svg.append("g").attr("class", "y axis").call(this.y_axis);
+ return this.svg
+ .append('g')
+ .attr('class', 'y axis')
+ .call(this.y_axis);
};
ContributorsGraph.prototype.set_data = function(data) {
- return this.data = data;
+ return (this.data = data);
};
return ContributorsGraph;
@@ -137,9 +176,9 @@ export const ContributorsMasterGraph = (function(superClass) {
};
ContributorsMasterGraph.prototype.parse_dates = function(data) {
- const parseDate = d3.timeParse("%Y-%m-%d");
+ const parseDate = d3.timeParse('%Y-%m-%d');
return data.forEach(function(d) {
- return d.date = parseDate(d.date);
+ return (d.date = parseDate(d.date));
});
};
@@ -148,42 +187,63 @@ export const ContributorsMasterGraph = (function(superClass) {
};
ContributorsMasterGraph.prototype.create_axes = function() {
- this.x_axis = d3.axisBottom()
+ this.x_axis = d3
+ .axisBottom()
.scale(this.x)
.tickFormat(dateTickFormat);
- return this.y_axis = d3.axisLeft().scale(this.y).ticks(5);
+ return (this.y_axis = d3
+ .axisLeft()
+ .scale(this.y)
+ .ticks(5));
};
ContributorsMasterGraph.prototype.create_svg = function() {
- this.svg = d3.select("#contributors-master")
- .append("svg")
- .attr("width", this.width + this.MARGIN.left + this.MARGIN.right)
- .attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom)
- .attr("class", "tint-box")
- .append("g")
- .attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")");
+ this.svg = d3
+ .select('#contributors-master')
+ .append('svg')
+ .attr('width', this.width + this.MARGIN.left + this.MARGIN.right)
+ .attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom)
+ .attr('class', 'tint-box')
+ .append('g')
+ .attr('transform', 'translate(' + this.MARGIN.left + ',' + this.MARGIN.top + ')');
return this.svg;
};
ContributorsMasterGraph.prototype.create_area = function(x, y) {
- return this.area = d3.area().x(function(d) {
- return x(d.date);
- }).y0(this.height).y1(function(d) {
- d.commits = d.commits || d.additions || d.deletions;
- return y(d.commits);
- });
+ return (this.area = d3
+ .area()
+ .x(function(d) {
+ return x(d.date);
+ })
+ .y0(this.height)
+ .y1(function(d) {
+ d.commits = d.commits || d.additions || d.deletions;
+ return y(d.commits);
+ }));
};
ContributorsMasterGraph.prototype.create_brush = function() {
- return this.brush = d3.brushX(this.x).extent([[this.x.range()[0], 0], [this.x.range()[1], this.height]]).on("end", this.update_content);
+ return (this.brush = d3
+ .brushX(this.x)
+ .extent([[this.x.range()[0], 0], [this.x.range()[1], this.height]])
+ .on('end', this.update_content));
};
ContributorsMasterGraph.prototype.draw_path = function(data) {
- return this.svg.append("path").datum(data).attr("class", "area").attr("d", this.area);
+ return this.svg
+ .append('path')
+ .datum(data)
+ .attr('class', 'area')
+ .attr('d', this.area);
};
ContributorsMasterGraph.prototype.add_brush = function() {
- return this.svg.append("g").attr("class", "selection").call(this.brush).selectAll("rect").attr("height", this.height);
+ return this.svg
+ .append('g')
+ .attr('class', 'selection')
+ .call(this.brush)
+ .selectAll('rect')
+ .attr('height', this.height);
};
ContributorsMasterGraph.prototype.update_content = function() {
@@ -193,7 +253,7 @@ export const ContributorsMasterGraph = (function(superClass) {
} else {
ContributorsGraph.set_x_domain(this.x_max_domain);
}
- return $("#brush_change").trigger('change');
+ return $('#brush_change').trigger('change');
};
ContributorsMasterGraph.prototype.draw = function() {
@@ -216,9 +276,9 @@ export const ContributorsMasterGraph = (function(superClass) {
this.process_dates(this.data);
ContributorsGraph.set_y_domain(this.data);
this.set_y_domain();
- this.svg.select("path").datum(this.data);
- this.svg.select("path").attr("d", this.area);
- return this.svg.select(".y.axis").call(this.y_axis);
+ this.svg.select('path').datum(this.data);
+ this.svg.select('path').attr('d', this.area);
+ return this.svg.select('.y.axis').call(this.y_axis);
};
return ContributorsMasterGraph;
@@ -252,43 +312,58 @@ export const ContributorsAuthorGraph = (function(superClass) {
};
ContributorsAuthorGraph.prototype.create_axes = function() {
- this.x_axis = d3.axisBottom()
+ this.x_axis = d3
+ .axisBottom()
.scale(this.x)
.ticks(8)
.tickFormat(dateTickFormat);
- return this.y_axis = d3.axisLeft().scale(this.y).ticks(5);
+ return (this.y_axis = d3
+ .axisLeft()
+ .scale(this.y)
+ .ticks(5));
};
ContributorsAuthorGraph.prototype.create_area = function(x, y) {
- return this.area = d3.area().x(function(d) {
- const parseDate = d3.timeParse("%Y-%m-%d");
- return x(parseDate(d));
- }).y0(this.height).y1((function(_this) {
- return function(d) {
- if (_this.data[d] != null) {
- return y(_this.data[d]);
- } else {
- return y(0);
- }
- };
- })(this));
+ return (this.area = d3
+ .area()
+ .x(function(d) {
+ const parseDate = d3.timeParse('%Y-%m-%d');
+ return x(parseDate(d));
+ })
+ .y0(this.height)
+ .y1(
+ (function(_this) {
+ return function(d) {
+ if (_this.data[d] != null) {
+ return y(_this.data[d]);
+ } else {
+ return y(0);
+ }
+ };
+ })(this),
+ ));
};
ContributorsAuthorGraph.prototype.create_svg = function() {
const persons = document.querySelectorAll('.person');
this.list_item = persons[persons.length - 1];
- this.svg = d3.select(this.list_item)
- .append("svg")
- .attr("width", this.width + this.MARGIN.left + this.MARGIN.right)
- .attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom)
- .attr("class", "spark")
- .append("g")
- .attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")");
+ this.svg = d3
+ .select(this.list_item)
+ .append('svg')
+ .attr('width', this.width + this.MARGIN.left + this.MARGIN.right)
+ .attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom)
+ .attr('class', 'spark')
+ .append('g')
+ .attr('transform', 'translate(' + this.MARGIN.left + ',' + this.MARGIN.top + ')');
return this.svg;
};
ContributorsAuthorGraph.prototype.draw_path = function(data) {
- return this.svg.append("path").datum(data).attr("class", "area-contributor").attr("d", this.area);
+ return this.svg
+ .append('path')
+ .datum(data)
+ .attr('class', 'area-contributor')
+ .attr('d', this.area);
};
ContributorsAuthorGraph.prototype.draw = function() {
@@ -304,10 +379,10 @@ export const ContributorsAuthorGraph = (function(superClass) {
ContributorsAuthorGraph.prototype.redraw = function() {
this.set_domain();
- this.svg.select("path").datum(this.dates);
- this.svg.select("path").attr("d", this.area);
- this.svg.select(".x.axis").call(this.x_axis);
- return this.svg.select(".y.axis").call(this.y_axis);
+ this.svg.select('path').datum(this.dates);
+ this.svg.select('path').attr('d', this.area);
+ this.svg.select('.x.axis').call(this.x_axis);
+ return this.svg.select('.y.axis').call(this.y_axis);
};
return ContributorsAuthorGraph;
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
index d12249bf612..988ae164955 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */
+/* eslint-disable func-names, object-shorthand, no-var, one-var, camelcase, no-param-reassign, no-return-assign, prefer-arrow-callback, consistent-return, no-unused-vars, no-cond-assign, no-else-return */
import _ from 'underscore';
export default {
@@ -26,12 +26,12 @@ export default {
by_author = _.toArray(by_author);
return {
total: total,
- by_author: by_author
+ by_author: by_author,
};
},
add_date: function(date, collection) {
collection[date] = {};
- return collection[date].date = date;
+ return (collection[date].date = date);
},
add_author: function(author, by_author, by_email) {
var data, normalized_email;
@@ -49,28 +49,28 @@ export default {
return this.store_deletions(entry, total, by_author);
},
store_commits: function(total, by_author) {
- this.add(total, "commits", 1);
- return this.add(by_author, "commits", 1);
+ this.add(total, 'commits', 1);
+ return this.add(by_author, 'commits', 1);
},
add: function(collection, field, value) {
if (collection[field] == null) {
collection[field] = 0;
}
- return collection[field] += value;
+ return (collection[field] += value);
},
store_additions: function(entry, total, by_author) {
if (entry.additions == null) {
entry.additions = 0;
}
- this.add(total, "additions", entry.additions);
- return this.add(by_author, "additions", entry.additions);
+ this.add(total, 'additions', entry.additions);
+ return this.add(by_author, 'additions', entry.additions);
},
store_deletions: function(entry, total, by_author) {
if (entry.deletions == null) {
entry.deletions = 0;
}
- this.add(total, "deletions", entry.deletions);
- return this.add(by_author, "deletions", entry.deletions);
+ this.add(total, 'deletions', entry.deletions);
+ return this.add(by_author, 'deletions', entry.deletions);
},
get_total_data: function(parsed_log, field) {
var log, total_data;
@@ -95,15 +95,18 @@ export default {
}
log = parsed_log.by_author;
author_data = [];
- _.each(log, (function(_this) {
- return function(log_entry) {
- var parsed_log_entry;
- parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range);
- if (!_.isEmpty(parsed_log_entry.dates)) {
- return author_data.push(parsed_log_entry);
- }
- };
- })(this));
+ _.each(
+ log,
+ (function(_this) {
+ return function(log_entry) {
+ var parsed_log_entry;
+ parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range);
+ if (!_.isEmpty(parsed_log_entry.dates)) {
+ return author_data.push(parsed_log_entry);
+ }
+ };
+ })(this),
+ );
return _.sortBy(author_data, function(d) {
return d[field];
}).reverse();
@@ -120,16 +123,19 @@ export default {
parsed_entry.additions = 0;
parsed_entry.deletions = 0;
- _.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) {
- return function(value, key) {
- if (_this.in_range(value.date, date_range)) {
- parsed_entry.dates[value.date] = value[field];
- parsed_entry.commits += value.commits;
- parsed_entry.additions += value.additions;
- return parsed_entry.deletions += value.deletions;
- }
- };
- })(this));
+ _.each(
+ _.omit(log_entry, 'author_name', 'author_email'),
+ (function(_this) {
+ return function(value, key) {
+ if (_this.in_range(value.date, date_range)) {
+ parsed_entry.dates[value.date] = value[field];
+ parsed_entry.commits += value.commits;
+ parsed_entry.additions += value.additions;
+ return (parsed_entry.deletions += value.deletions);
+ }
+ };
+ })(this),
+ );
return parsed_entry;
},
in_range: function(date, date_range) {
@@ -139,5 +145,5 @@ export default {
} else {
return false;
}
- }
+ },
};
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
index cc0e6553e83..5659e13981a 100644
--- a/app/assets/javascripts/pages/projects/index.js
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -1,7 +1,7 @@
-import gcpSignupOffer from '~/clusters/components/gcp_signup_offer';
+import initDismissableCallout from '~/dismissable_callout';
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
import Project from './project';
-import ShortcutsNavigation from '../../shortcuts_navigation';
+import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
const { page } = document.body.dataset;
@@ -12,7 +12,7 @@ document.addEventListener('DOMContentLoaded', () => {
];
if (newClusterViews.indexOf(page) > -1) {
- gcpSignupOffer();
+ initDismissableCallout('.gcp-signup-offer');
initGkeDropdowns();
}
diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js
index 56ab3fcdfcb..bd8afa2d5ba 100644
--- a/app/assets/javascripts/pages/projects/init_blob.js
+++ b/app/assets/javascripts/pages/projects/init_blob.js
@@ -1,7 +1,7 @@
import LineHighlighter from '~/line_highlighter';
import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater';
-import ShortcutsNavigation from '~/shortcuts_navigation';
-import ShortcutsBlob from '~/shortcuts_blob';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
+import ShortcutsBlob from '~/behaviors/shortcuts/shortcuts_blob';
import BlobForkSuggestion from '~/blob/blob_fork_suggestion';
import initBlobBundle from '~/blob_edit/blob_bundle';
@@ -16,7 +16,8 @@ export default () => {
);
const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
- const fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
+ const fileBlobPermalinkUrl =
+ fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
new ShortcutsNavigation(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/init_form.js b/app/assets/javascripts/pages/projects/init_form.js
index 9f20a3e4e46..019efe077f7 100644
--- a/app/assets/javascripts/pages/projects/init_form.js
+++ b/app/assets/javascripts/pages/projects/init_form.js
@@ -1,7 +1,7 @@
import ZenMode from '~/zen_mode';
import GLForm from '~/gl_form';
-export default function ($formEl) {
+export default function($formEl) {
new ZenMode(); // eslint-disable-line no-new
new GLForm($formEl); // eslint-disable-line no-new
}
diff --git a/app/assets/javascripts/pages/projects/issues/form.js b/app/assets/javascripts/pages/projects/issues/form.js
index b2b8e5d2300..197bfa8a394 100644
--- a/app/assets/javascripts/pages/projects/issues/form.js
+++ b/app/assets/javascripts/pages/projects/issues/form.js
@@ -5,7 +5,7 @@ import GLForm from '~/gl_form';
import IssuableForm from '~/issuable_form';
import LabelsSelect from '~/labels_select';
import MilestoneSelect from '~/milestone_select';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import IssuableTemplateSelectors from '~/templates/issuable_template_selectors';
export default () => {
diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js
index 70fdb0ef40d..a56c0bb6be8 100644
--- a/app/assets/javascripts/pages/projects/issues/index/index.js
+++ b/app/assets/javascripts/pages/projects/issues/index/index.js
@@ -1,15 +1,17 @@
/* eslint-disable no-new */
import IssuableIndex from '~/issuable_index';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import UsersSelect from '~/users_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
new IssuableIndex(ISSUABLE_INDEX.ISSUE);
diff --git a/app/assets/javascripts/pages/projects/issues/show.js b/app/assets/javascripts/pages/projects/issues/show.js
index 500fbd27340..8987c8e3f47 100644
--- a/app/assets/javascripts/pages/projects/issues/show.js
+++ b/app/assets/javascripts/pages/projects/issues/show.js
@@ -1,11 +1,12 @@
import initIssuableSidebar from '~/init_issuable_sidebar';
import Issue from '~/issue';
-import ShortcutsIssuable from '~/shortcuts_issuable';
+import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import ZenMode from '~/zen_mode';
import '~/notes/index';
-import '~/issue_show/index';
+import initIssueableApp from '~/issue_show';
-export default function () {
+export default function() {
+ initIssueableApp();
new Issue(); // eslint-disable-line no-new
new ShortcutsIssuable(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/jobs/index/index.js b/app/assets/javascripts/pages/projects/jobs/index/index.js
new file mode 100644
index 00000000000..1b57c67f16b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/jobs/index/index.js
@@ -0,0 +1,16 @@
+import Vue from 'vue';
+import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const remainingTimeElements = document.querySelectorAll('.js-remaining-time');
+ remainingTimeElements.forEach(
+ el =>
+ new Vue({
+ ...GlCountdown,
+ el,
+ propsData: {
+ endDateString: el.dateTime,
+ },
+ }),
+ );
+});
diff --git a/app/assets/javascripts/pages/projects/jobs/show/index.js b/app/assets/javascripts/pages/projects/jobs/show/index.js
index 3626f3ffec6..d57dbeb1242 100644
--- a/app/assets/javascripts/pages/projects/jobs/show/index.js
+++ b/app/assets/javascripts/pages/projects/jobs/show/index.js
@@ -1,3 +1,3 @@
-import initJobDetails from '~/jobs/job_details_bundle';
+import initJobDetails from '~/jobs';
document.addEventListener('DOMContentLoaded', initJobDetails);
diff --git a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
index 5d2247f6c6d..e8b646f3f6e 100644
--- a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
+++ b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
@@ -1,72 +1,86 @@
<script>
- import _ from 'underscore';
- import axios from '~/lib/utils/axios_utils';
- import createFlash from '~/flash';
- import GlModal from '~/vue_shared/components/gl_modal.vue';
- import { s__, sprintf } from '~/locale';
- import { visitUrl } from '~/lib/utils/url_utility';
- import eventHub from '../event_hub';
+import _ from 'underscore';
+import axios from '~/lib/utils/axios_utils';
+import createFlash from '~/flash';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import { s__, sprintf } from '~/locale';
+import { visitUrl } from '~/lib/utils/url_utility';
+import eventHub from '../event_hub';
- export default {
- components: {
- GlModal,
+export default {
+ components: {
+ GlModal,
+ },
+ props: {
+ url: {
+ type: String,
+ required: true,
},
- props: {
- url: {
- type: String,
- required: true,
- },
- labelTitle: {
- type: String,
- required: true,
- },
- labelColor: {
- type: String,
- required: true,
- },
- labelTextColor: {
- type: String,
- required: true,
- },
- groupName: {
- type: String,
- required: true,
- },
+ labelTitle: {
+ type: String,
+ required: true,
},
- computed: {
- text() {
- return sprintf(s__(`Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}.
- Existing project labels with the same title will be merged. This action cannot be reversed.`), {
+ labelColor: {
+ type: String,
+ required: true,
+ },
+ labelTextColor: {
+ type: String,
+ required: true,
+ },
+ groupName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ text() {
+ return sprintf(
+ s__(`Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}.
+ Existing project labels with the same title will be merged. This action cannot be reversed.`),
+ {
labelTitle: this.labelTitle,
groupName: this.groupName,
- });
- },
- title() {
- const label = `<span
+ },
+ );
+ },
+ title() {
+ const label = `<span
class="label color-label"
style="background-color: ${this.labelColor}; color: ${this.labelTextColor};"
>${_.escape(this.labelTitle)}</span>`;
- return sprintf(s__('Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>'), {
+ return sprintf(
+ s__('Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>'),
+ {
labelTitle: label,
- }, false);
- },
+ },
+ false,
+ );
},
- methods: {
- onSubmit() {
- eventHub.$emit('promoteLabelModal.requestStarted', this.url);
- return axios.post(this.url, { params: { format: 'json' } })
- .then((response) => {
- eventHub.$emit('promoteLabelModal.requestFinished', { labelUrl: this.url, successful: true });
- visitUrl(response.data.url);
- })
- .catch((error) => {
- eventHub.$emit('promoteLabelModal.requestFinished', { labelUrl: this.url, successful: false });
- createFlash(error);
+ },
+ methods: {
+ onSubmit() {
+ eventHub.$emit('promoteLabelModal.requestStarted', this.url);
+ return axios
+ .post(this.url, { params: { format: 'json' } })
+ .then(response => {
+ eventHub.$emit('promoteLabelModal.requestFinished', {
+ labelUrl: this.url,
+ successful: true,
+ });
+ visitUrl(response.data.url);
+ })
+ .catch(error => {
+ eventHub.$emit('promoteLabelModal.requestFinished', {
+ labelUrl: this.url,
+ successful: false,
});
- },
+ createFlash(error);
+ });
},
- };
+ },
+};
</script>
<template>
<gl-modal
diff --git a/app/assets/javascripts/pages/projects/labels/index/index.js b/app/assets/javascripts/pages/projects/labels/index/index.js
index 03cfef61311..36cf485f33d 100644
--- a/app/assets/javascripts/pages/projects/labels/index/index.js
+++ b/app/assets/javascripts/pages/projects/labels/index/index.js
@@ -10,20 +10,24 @@ const initLabelIndex = () => {
initLabels();
const onRequestFinished = ({ labelUrl, successful }) => {
- const button = document.querySelector(`.js-promote-project-label-button[data-url="${labelUrl}"]`);
+ const button = document.querySelector(
+ `.js-promote-project-label-button[data-url="${labelUrl}"]`,
+ );
if (!successful) {
button.removeAttribute('disabled');
}
};
- const onRequestStarted = (labelUrl) => {
- const button = document.querySelector(`.js-promote-project-label-button[data-url="${labelUrl}"]`);
+ const onRequestStarted = labelUrl => {
+ const button = document.querySelector(
+ `.js-promote-project-label-button[data-url="${labelUrl}"]`,
+ );
button.setAttribute('disabled', '');
eventHub.$once('promoteLabelModal.requestFinished', onRequestFinished);
};
- const onDeleteButtonClick = (event) => {
+ const onDeleteButtonClick = event => {
const button = event.currentTarget;
const modalProps = {
labelTitle: button.dataset.labelTitle,
@@ -37,12 +41,12 @@ const initLabelIndex = () => {
};
const promoteLabelButtons = document.querySelectorAll('.js-promote-project-label-button');
- promoteLabelButtons.forEach((button) => {
+ promoteLabelButtons.forEach(button => {
button.addEventListener('click', onDeleteButtonClick);
});
eventHub.$once('promoteLabelModal.mounted', () => {
- promoteLabelButtons.forEach((button) => {
+ promoteLabelButtons.forEach(button => {
button.removeAttribute('disabled');
});
});
diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
index a7aa616319f..ec39db12e74 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
@@ -1,14 +1,19 @@
import IssuableIndex from '~/issuable_index';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import UsersSelect from '~/users_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
document.addEventListener('DOMContentLoaded', () => {
+ IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests();
+
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
+
new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
new UsersSelect(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
index 3a3c21f2202..e3971618da5 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
@@ -2,7 +2,7 @@
import $ from 'jquery';
import Diff from '~/diff';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GLForm from '~/gl_form';
import IssuableForm from '~/issuable_form';
import LabelsSelect from '~/labels_select';
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
index 26ead75cec4..7bfb83a2204 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
@@ -1,6 +1,6 @@
import ZenMode from '~/zen_mode';
import initIssuableSidebar from '~/init_issuable_sidebar';
-import ShortcutsIssuable from '~/shortcuts_issuable';
+import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import { handleLocationHash } from '~/lib/utils/common_utils';
import howToMerge from '~/how_to_merge';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
diff --git a/app/assets/javascripts/pages/projects/network/network.js b/app/assets/javascripts/pages/projects/network/network.js
index 77368c47451..226d63f05c4 100644
--- a/app/assets/javascripts/pages/projects/network/network.js
+++ b/app/assets/javascripts/pages/projects/network/network.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, no-var, quotes, quote-props, prefer-template, comma-dangle, max-len */
+/* eslint-disable func-names, no-var, prefer-template */
import $ from 'jquery';
import BranchGraph from '../../../network/branch_graph';
@@ -6,13 +6,15 @@ import BranchGraph from '../../../network/branch_graph';
export default (function() {
function Network(opts) {
var vph;
- $("#filter_ref").click(function() {
- return $(this).closest('form').submit();
+ $('#filter_ref').click(function() {
+ return $(this)
+ .closest('form')
+ .submit();
});
- this.branch_graph = new BranchGraph($(".network-graph"), opts);
+ this.branch_graph = new BranchGraph($('.network-graph'), opts);
vph = $(window).height() - 250;
$('.network-graph').css({
- 'height': vph + 'px'
+ height: vph + 'px',
});
}
diff --git a/app/assets/javascripts/pages/projects/network/show/index.js b/app/assets/javascripts/pages/projects/network/show/index.js
index a0b14fed10f..9f05f63b742 100644
--- a/app/assets/javascripts/pages/projects/network/show/index.js
+++ b/app/assets/javascripts/pages/projects/network/show/index.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import ShortcutsNetwork from '../../../../shortcuts_network';
+import ShortcutsNetwork from '~/behaviors/shortcuts/shortcuts_network';
import Network from '../network';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
index 544360dcd51..6197dc8a9db 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
@@ -1,12 +1,16 @@
import Vue from 'vue';
import PipelineSchedulesCallout from '../shared/components/pipeline_schedules_callout.vue';
-document.addEventListener('DOMContentLoaded', () => new Vue({
- el: '#pipeline-schedules-callout',
- components: {
- 'pipeline-schedules-callout': PipelineSchedulesCallout,
- },
- render(createElement) {
- return createElement('pipeline-schedules-callout');
- },
-}));
+document.addEventListener(
+ 'DOMContentLoaded',
+ () =>
+ new Vue({
+ el: '#pipeline-schedules-callout',
+ components: {
+ 'pipeline-schedules-callout': PipelineSchedulesCallout,
+ },
+ render(createElement) {
+ return createElement('pipeline-schedules-callout');
+ },
+ }),
+);
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
index 0d05668b285..ab6f42d928c 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
@@ -1,63 +1,63 @@
<script>
- import _ from 'underscore';
+import _ from 'underscore';
- export default {
- props: {
- initialCronInterval: {
- type: String,
- required: false,
- default: '',
- },
- },
- data() {
- return {
- inputNameAttribute: 'schedule[cron]',
- cronInterval: this.initialCronInterval,
- cronIntervalPresets: {
- everyDay: '0 4 * * *',
- everyWeek: '0 4 * * 0',
- everyMonth: '0 4 1 * *',
- },
- cronSyntaxUrl: 'https://en.wikipedia.org/wiki/Cron',
- customInputEnabled: false,
- };
+export default {
+ props: {
+ initialCronInterval: {
+ type: String,
+ required: false,
+ default: '',
},
- computed: {
- intervalIsPreset() {
- return _.contains(this.cronIntervalPresets, this.cronInterval);
- },
- // The text input is editable when there's a custom interval, or when it's
- // a preset interval and the user clicks the 'custom' radio button
- isEditable() {
- return !!(this.customInputEnabled || !this.intervalIsPreset);
+ },
+ data() {
+ return {
+ inputNameAttribute: 'schedule[cron]',
+ cronInterval: this.initialCronInterval,
+ cronIntervalPresets: {
+ everyDay: '0 4 * * *',
+ everyWeek: '0 4 * * 0',
+ everyMonth: '0 4 1 * *',
},
+ cronSyntaxUrl: 'https://en.wikipedia.org/wiki/Cron',
+ customInputEnabled: false,
+ };
+ },
+ computed: {
+ intervalIsPreset() {
+ return _.contains(this.cronIntervalPresets, this.cronInterval);
},
- watch: {
- cronInterval() {
- // updates field validation state when model changes, as
- // glFieldError only updates on input.
- this.$nextTick(() => {
- gl.pipelineScheduleFieldErrors.updateFormValidityState();
- });
- },
+ // The text input is editable when there's a custom interval, or when it's
+ // a preset interval and the user clicks the 'custom' radio button
+ isEditable() {
+ return !!(this.customInputEnabled || !this.intervalIsPreset);
},
- created() {
- if (this.intervalIsPreset) {
- this.enableCustomInput = false;
- }
+ },
+ watch: {
+ cronInterval() {
+ // updates field validation state when model changes, as
+ // glFieldError only updates on input.
+ this.$nextTick(() => {
+ gl.pipelineScheduleFieldErrors.updateFormValidityState();
+ });
},
- methods: {
- toggleCustomInput(shouldEnable) {
- this.customInputEnabled = shouldEnable;
+ },
+ created() {
+ if (this.intervalIsPreset) {
+ this.enableCustomInput = false;
+ }
+ },
+ methods: {
+ toggleCustomInput(shouldEnable) {
+ this.customInputEnabled = shouldEnable;
- if (shouldEnable) {
- // We need to change the value so other radios don't remain selected
- // because the model (cronInterval) hasn't changed. The server trims it.
- this.cronInterval = `${this.cronInterval} `;
- }
- },
+ if (shouldEnable) {
+ // We need to change the value so other radios don't remain selected
+ // because the model (cronInterval) hasn't changed. The server trims it.
+ this.cronInterval = `${this.cronInterval} `;
+ }
},
- };
+ },
+};
</script>
<template>
@@ -147,8 +147,8 @@
<div class="cron-interval-input-wrapper">
<input
id="schedule_cron"
- :placeholder="__('Define a custom pattern with cron syntax')"
v-model="cronInterval"
+ :placeholder="__('Define a custom pattern with cron syntax')"
:name="inputNameAttribute"
:disabled="!isEditable"
class="form-control inline cron-interval-input"
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
index 77508e62cef..33fc2420e4d 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
@@ -1,31 +1,31 @@
<script>
- import Vue from 'vue';
- import Cookies from 'js-cookie';
- import Translate from '../../../../../vue_shared/translate';
- import illustrationSvg from '../icons/intro_illustration.svg';
+import Vue from 'vue';
+import Cookies from 'js-cookie';
+import Translate from '../../../../../vue_shared/translate';
+import illustrationSvg from '../icons/intro_illustration.svg';
- Vue.use(Translate);
+Vue.use(Translate);
- const cookieKey = 'pipeline_schedules_callout_dismissed';
+const cookieKey = 'pipeline_schedules_callout_dismissed';
- export default {
- name: 'PipelineSchedulesCallout',
- data() {
- return {
- docsUrl: document.getElementById('pipeline-schedules-callout').dataset.docsUrl,
- calloutDismissed: Cookies.get(cookieKey) === 'true',
- };
+export default {
+ name: 'PipelineSchedulesCallout',
+ data() {
+ return {
+ docsUrl: document.getElementById('pipeline-schedules-callout').dataset.docsUrl,
+ calloutDismissed: Cookies.get(cookieKey) === 'true',
+ };
+ },
+ created() {
+ this.illustrationSvg = illustrationSvg;
+ },
+ methods: {
+ dismissCallout() {
+ this.calloutDismissed = true;
+ Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 });
},
- created() {
- this.illustrationSvg = illustrationSvg;
- },
- methods: {
- dismissCallout() {
- this.calloutDismissed = true;
- Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 });
- },
- },
- };
+ },
+};
</script>
<template>
<div
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js
index 4ef0d11dd36..0057700c1b3 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js
@@ -26,8 +26,7 @@ export default class TargetBranchDropdown {
}
formatBranchesList() {
- return this.$dropdown.data('data')
- .map(val => ({ name: val }));
+ return this.$dropdown.data('data').map(val => ({ name: val }));
}
setDropdownToggle() {
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
index c3ac54733a3..4d494efef6c 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
@@ -11,7 +11,9 @@ Vue.use(Translate);
function initIntervalPatternInput() {
const intervalPatternMount = document.getElementById('interval-pattern-input');
- const initialCronInterval = intervalPatternMount ? intervalPatternMount.dataset.initialInterval : '';
+ const initialCronInterval = intervalPatternMount
+ ? intervalPatternMount.dataset.initialInterval
+ : '';
return new Vue({
el: intervalPatternMount,
diff --git a/app/assets/javascripts/pages/projects/pipelines/charts/index.js b/app/assets/javascripts/pages/projects/pipelines/charts/index.js
index 07b6992eba1..48353f3b4ef 100644
--- a/app/assets/javascripts/pages/projects/pipelines/charts/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/charts/index.js
@@ -7,26 +7,29 @@ const options = {
maintainAspectRatio: false,
};
-const buildChart = (chartScope) => {
+const buildChart = chartScope => {
const data = {
labels: chartScope.labels,
- datasets: [{
- fillColor: '#707070',
- strokeColor: '#707070',
- pointColor: '#707070',
- pointStrokeColor: '#EEE',
- data: chartScope.totalValues,
- },
- {
- fillColor: '#1aaa55',
- strokeColor: '#1aaa55',
- pointColor: '#1aaa55',
- pointStrokeColor: '#fff',
- data: chartScope.successValues,
- },
+ datasets: [
+ {
+ fillColor: '#707070',
+ strokeColor: '#707070',
+ pointColor: '#707070',
+ pointStrokeColor: '#EEE',
+ data: chartScope.totalValues,
+ },
+ {
+ fillColor: '#1aaa55',
+ strokeColor: '#1aaa55',
+ pointColor: '#1aaa55',
+ pointStrokeColor: '#fff',
+ data: chartScope.successValues,
+ },
],
};
- const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
+ const ctx = $(`#${chartScope.scope}Chart`)
+ .get(0)
+ .getContext('2d');
new Chart(ctx).Line(data, options);
};
@@ -36,14 +39,16 @@ document.addEventListener('DOMContentLoaded', () => {
const chartsData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
const data = {
labels: chartTimesData.labels,
- datasets: [{
- fillColor: 'rgba(220,220,220,0.5)',
- strokeColor: 'rgba(220,220,220,1)',
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data: chartTimesData.values,
- }],
+ datasets: [
+ {
+ fillColor: 'rgba(220,220,220,0.5)',
+ strokeColor: 'rgba(220,220,220,1)',
+ barStrokeWidth: 1,
+ barValueSpacing: 1,
+ barDatasetSpacing: 1,
+ data: chartTimesData.values,
+ },
+ ],
};
if (window.innerWidth < 768) {
@@ -51,7 +56,11 @@ document.addEventListener('DOMContentLoaded', () => {
options.scaleFontSize = 8;
}
- new Chart($('#build_timesChart').get(0).getContext('2d')).Bar(data, options);
+ new Chart(
+ $('#build_timesChart')
+ .get(0)
+ .getContext('2d'),
+ ).Bar(data, options);
chartsData.forEach(scope => buildChart(scope));
});
diff --git a/app/assets/javascripts/pages/projects/pipelines/index/index.js b/app/assets/javascripts/pages/projects/pipelines/index/index.js
index a84e2790680..fc337a7609b 100644
--- a/app/assets/javascripts/pages/projects/pipelines/index/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/index/index.js
@@ -6,35 +6,39 @@ import { convertPermissionToBoolean } from '../../../../lib/utils/common_utils';
Vue.use(Translate);
-document.addEventListener('DOMContentLoaded', () => new Vue({
- el: '#pipelines-list-vue',
- components: {
- pipelinesComponent,
- },
- data() {
- return {
- store: new PipelinesStore(),
- };
- },
- created() {
- this.dataset = document.querySelector(this.$options.el).dataset;
- },
- render(createElement) {
- return createElement('pipelines-component', {
- props: {
- store: this.store,
- endpoint: this.dataset.endpoint,
- helpPagePath: this.dataset.helpPagePath,
- emptyStateSvgPath: this.dataset.emptyStateSvgPath,
- errorStateSvgPath: this.dataset.errorStateSvgPath,
- noPipelinesSvgPath: this.dataset.noPipelinesSvgPath,
- autoDevopsPath: this.dataset.helpAutoDevopsPath,
- newPipelinePath: this.dataset.newPipelinePath,
- canCreatePipeline: convertPermissionToBoolean(this.dataset.canCreatePipeline),
- hasGitlabCi: convertPermissionToBoolean(this.dataset.hasGitlabCi),
- ciLintPath: this.dataset.ciLintPath,
- resetCachePath: this.dataset.resetCachePath,
+document.addEventListener(
+ 'DOMContentLoaded',
+ () =>
+ new Vue({
+ el: '#pipelines-list-vue',
+ components: {
+ pipelinesComponent,
},
- });
- },
-}));
+ data() {
+ return {
+ store: new PipelinesStore(),
+ };
+ },
+ created() {
+ this.dataset = document.querySelector(this.$options.el).dataset;
+ },
+ render(createElement) {
+ return createElement('pipelines-component', {
+ props: {
+ store: this.store,
+ endpoint: this.dataset.endpoint,
+ helpPagePath: this.dataset.helpPagePath,
+ emptyStateSvgPath: this.dataset.emptyStateSvgPath,
+ errorStateSvgPath: this.dataset.errorStateSvgPath,
+ noPipelinesSvgPath: this.dataset.noPipelinesSvgPath,
+ autoDevopsPath: this.dataset.helpAutoDevopsPath,
+ newPipelinePath: this.dataset.newPipelinePath,
+ canCreatePipeline: convertPermissionToBoolean(this.dataset.canCreatePipeline),
+ hasGitlabCi: convertPermissionToBoolean(this.dataset.hasGitlabCi),
+ ciLintPath: this.dataset.ciLintPath,
+ resetCachePath: this.dataset.resetCachePath,
+ },
+ });
+ },
+ }),
+);
diff --git a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js
index 94dfeb96e8c..ba4ae04ab3d 100644
--- a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js
+++ b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js
@@ -2,9 +2,12 @@ import Pipelines from '~/pipelines';
export default () => {
const { controllerAction } = document.querySelector('.js-pipeline-container').dataset;
- const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`;
+ const pipelineStatusUrl = `${document
+ .querySelector('.js-pipeline-tab-link a')
+ .getAttribute('href')}/status.json`;
- new Pipelines({ // eslint-disable-line no-new
+ // eslint-disable-next-line no-new
+ new Pipelines({
initTabs: true,
pipelineStatusUrl,
tabsOptions: {
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index a853624e944..a6bee49a6b1 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -1,5 +1,4 @@
-/* eslint-disable func-names, no-var, no-return-assign, one-var,
- one-var-declaration-per-line, object-shorthand, vars-on-top */
+/* eslint-disable func-names, no-var, no-return-assign, one-var, object-shorthand, vars-on-top */
import $ from 'jquery';
import Cookies from 'js-cookie';
@@ -13,40 +12,61 @@ export default class Project {
constructor() {
const $cloneOptions = $('ul.clone-options-dropdown');
const $projectCloneField = $('#project_clone');
- const $cloneBtnText = $('a.clone-dropdown-btn span');
+ const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
- const selectedCloneOption = $cloneBtnText.text().trim();
+ const selectedCloneOption = $cloneBtnLabel.text().trim();
if (selectedCloneOption.length > 0) {
$(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
}
- $('a', $cloneOptions).on('click', (e) => {
+ $('a', $cloneOptions).on('click', e => {
+ e.preventDefault();
const $this = $(e.currentTarget);
const url = $this.attr('href');
- const activeText = $this.find('.dropdown-menu-inner-title').text();
+ const cloneType = $this.data('cloneType');
- e.preventDefault();
+ $('.is-active', $cloneOptions).removeClass('is-active');
+ $(`a[data-clone-type="${cloneType}"]`).each(function() {
+ const $el = $(this);
+ const activeText = $el.find('.dropdown-menu-inner-title').text();
+ const $container = $el.closest('.project-clone-holder');
+ const $label = $container.find('.js-clone-dropdown-label');
- $('.is-active', $cloneOptions).not($this).removeClass('is-active');
- $this.toggleClass('is-active');
- $projectCloneField.val(url);
- $cloneBtnText.text(activeText);
+ $el.toggleClass('is-active');
+ $label.text(activeText);
+ });
- return $('.clone').text(url);
+ $projectCloneField.val(url);
+ $('.js-git-empty .js-clone').text(url);
});
// Ref switcher
Project.initRefSwitcher();
$('.project-refs-select').on('change', function() {
- return $(this).parents('form').submit();
+ return $(this)
+ .parents('form')
+ .submit();
});
$('.hide-no-ssh-message').on('click', function(e) {
Cookies.set('hide_no_ssh_message', 'false');
- $(this).parents('.no-ssh-key-message').remove();
+ $(this)
+ .parents('.no-ssh-key-message')
+ .remove();
return e.preventDefault();
});
$('.hide-no-password-message').on('click', function(e) {
Cookies.set('hide_no_password_message', 'false');
- $(this).parents('.no-password-message').remove();
+ $(this)
+ .parents('.no-password-message')
+ .remove();
+ return e.preventDefault();
+ });
+ $('.hide-auto-devops-implicitly-enabled-banner').on('click', function(e) {
+ const projectId = $(this).data('project-id');
+ const cookieKey = `hide_auto_devops_implicitly_enabled_banner_${projectId}`;
+ Cookies.set(cookieKey, 'false');
+ $(this)
+ .parents('.auto-devops-implicitly-enabled-banner')
+ .remove();
return e.preventDefault();
});
Project.projectSelectDropdown();
@@ -58,7 +78,7 @@ export default class Project {
}
static changeProject(url) {
- return window.location = url;
+ return (window.location = url);
}
static initRefSwitcher() {
@@ -73,14 +93,15 @@ export default class Project {
selected = $dropdown.data('selected');
return $dropdown.glDropdown({
data(term, callback) {
- axios.get($dropdown.data('refsUrl'), {
- params: {
- ref: $dropdown.data('ref'),
- search: term,
- },
- })
- .then(({ data }) => callback(data))
- .catch(() => flash(__('An error occurred while getting projects')));
+ axios
+ .get($dropdown.data('refsUrl'), {
+ params: {
+ ref: $dropdown.data('ref'),
+ search: term,
+ },
+ })
+ .then(({ data }) => callback(data))
+ .catch(() => flash(__('An error occurred while getting projects')));
},
selectable: true,
filterable: true,
diff --git a/app/assets/javascripts/pages/projects/settings/badges/index/index.js b/app/assets/javascripts/pages/projects/settings/badges/index/index.js
deleted file mode 100644
index 30469550866..00000000000
--- a/app/assets/javascripts/pages/projects/settings/badges/index/index.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import Vue from 'vue';
-import Translate from '~/vue_shared/translate';
-import { PROJECT_BADGE } from '~/badges/constants';
-import mountBadgeSettings from '~/pages/shared/mount_badge_settings';
-
-Vue.use(Translate);
-
-document.addEventListener('DOMContentLoaded', () => {
- mountBadgeSettings(PROJECT_BADGE);
-});
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
index 8f5ac3d8082..15c6fb550c1 100644
--- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -18,7 +18,7 @@ document.addEventListener('DOMContentLoaded', () => {
// eslint-disable-next-line no-new
new AjaxVariableList({
container: variableListEl,
- saveButton: variableListEl.querySelector('.js-secret-variables-save-button'),
+ saveButton: variableListEl.querySelector('.js-ci-variables-save-button'),
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
index 06101290f6c..dced839c883 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
@@ -1,73 +1,71 @@
<script>
- import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
+import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
- export default {
- components: {
- projectFeatureToggle,
- },
+export default {
+ components: {
+ projectFeatureToggle,
+ },
- model: {
- prop: 'value',
- event: 'change',
- },
+ model: {
+ prop: 'value',
+ event: 'change',
+ },
- props: {
- name: {
- type: String,
- required: false,
- default: '',
- },
- options: {
- type: Array,
- required: false,
- default: () => [],
- },
- value: {
- type: Number,
- required: false,
- default: 0,
- },
- disabledInput: {
- type: Boolean,
- required: false,
- default: false,
- },
+ props: {
+ name: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ options: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ value: {
+ type: Number,
+ required: false,
+ default: 0,
},
+ disabledInput: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
- computed: {
- featureEnabled() {
- return this.value !== 0;
- },
+ computed: {
+ featureEnabled() {
+ return this.value !== 0;
+ },
- displayOptions() {
- if (this.featureEnabled) {
- return this.options;
- }
- return [
- [0, 'Enable feature to choose access level'],
- ];
- },
+ displayOptions() {
+ if (this.featureEnabled) {
+ return this.options;
+ }
+ return [[0, 'Enable feature to choose access level']];
+ },
- displaySelectInput() {
- return this.disabledInput || !this.featureEnabled || this.displayOptions.length < 2;
- },
+ displaySelectInput() {
+ return this.disabledInput || !this.featureEnabled || this.displayOptions.length < 2;
},
+ },
- methods: {
- toggleFeature(featureEnabled) {
- if (featureEnabled === false || this.options.length < 1) {
- this.$emit('change', 0);
- } else {
- const [firstOptionValue] = this.options[this.options.length - 1];
- this.$emit('change', firstOptionValue);
- }
- },
+ methods: {
+ toggleFeature(featureEnabled) {
+ if (featureEnabled === false || this.options.length < 1) {
+ this.$emit('change', 0);
+ } else {
+ const [firstOptionValue] = this.options[this.options.length - 1];
+ this.$emit('change', firstOptionValue);
+ }
+ },
- selectOption(e) {
- this.$emit('change', Number(e.target.value));
- },
+ selectOption(e) {
+ this.$emit('change', Number(e.target.value));
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue
index 83437363af5..898d605463f 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue
@@ -1,23 +1,23 @@
<script>
- export default {
- props: {
- label: {
- type: String,
- required: false,
- default: null,
- },
- helpPath: {
- type: String,
- required: false,
- default: null,
- },
- helpText: {
- type: String,
- required: false,
- default: null,
- },
+export default {
+ props: {
+ label: {
+ type: String,
+ required: false,
+ default: null,
},
- };
+ helpPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ helpText: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index ae88b765abf..c0ec7a5dc94 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -1,174 +1,200 @@
<script>
- import projectFeatureSetting from './project_feature_setting.vue';
- import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
- import projectSettingRow from './project_setting_row.vue';
- import { visibilityOptions, visibilityLevelDescriptions } from '../constants';
- import { toggleHiddenClassBySelector } from '../external';
+import projectFeatureSetting from './project_feature_setting.vue';
+import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
+import projectSettingRow from './project_setting_row.vue';
+import { visibilityOptions, visibilityLevelDescriptions } from '../constants';
+import { toggleHiddenClassBySelector } from '../external';
- export default {
- components: {
- projectFeatureSetting,
- projectFeatureToggle,
- projectSettingRow,
- },
+export default {
+ components: {
+ projectFeatureSetting,
+ projectFeatureToggle,
+ projectSettingRow,
+ },
- props: {
- currentSettings: {
- type: Object,
- required: true,
- },
- canChangeVisibilityLevel: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowedVisibilityOptions: {
- type: Array,
- required: false,
- default: () => [0, 10, 20],
- },
- lfsAvailable: {
- type: Boolean,
- required: false,
- default: false,
- },
- registryAvailable: {
- type: Boolean,
- required: false,
- default: false,
- },
- visibilityHelpPath: {
- type: String,
- required: false,
- default: '',
- },
- lfsHelpPath: {
- type: String,
- required: false,
- default: '',
- },
- registryHelpPath: {
- type: String,
- required: false,
- default: '',
- },
+ props: {
+ currentSettings: {
+ type: Object,
+ required: true,
+ },
+ canChangeVisibilityLevel: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ allowedVisibilityOptions: {
+ type: Array,
+ required: false,
+ default: () => [0, 10, 20],
+ },
+ lfsAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ registryAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ visibilityHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ lfsHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ registryHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ pagesAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ pagesAccessControlEnabled: {
+ type: Boolean,
+ required: false,
+ default: false,
},
+ pagesHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+
+ data() {
+ const defaults = {
+ visibilityOptions,
+ visibilityLevel: visibilityOptions.PUBLIC,
+ issuesAccessLevel: 20,
+ repositoryAccessLevel: 20,
+ mergeRequestsAccessLevel: 20,
+ buildsAccessLevel: 20,
+ wikiAccessLevel: 20,
+ snippetsAccessLevel: 20,
+ pagesAccessLevel: 20,
+ containerRegistryEnabled: true,
+ lfsEnabled: true,
+ requestAccessEnabled: true,
+ highlightChangesClass: false,
+ };
- data() {
- const defaults = {
- visibilityOptions,
- visibilityLevel: visibilityOptions.PUBLIC,
- issuesAccessLevel: 20,
- repositoryAccessLevel: 20,
- mergeRequestsAccessLevel: 20,
- buildsAccessLevel: 20,
- wikiAccessLevel: 20,
- snippetsAccessLevel: 20,
- containerRegistryEnabled: true,
- lfsEnabled: true,
- requestAccessEnabled: true,
- highlightChangesClass: false,
- };
+ return { ...defaults, ...this.currentSettings };
+ },
- return { ...defaults, ...this.currentSettings };
+ computed: {
+ featureAccessLevelOptions() {
+ const options = [[10, 'Only Project Members']];
+ if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
+ options.push([20, 'Everyone With Access']);
+ }
+ return options;
},
- computed: {
- featureAccessLevelOptions() {
- const options = [
- [10, 'Only Project Members'],
- ];
- if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
- options.push([20, 'Everyone With Access']);
- }
- return options;
- },
+ repoFeatureAccessLevelOptions() {
+ return this.featureAccessLevelOptions.filter(
+ ([value]) => value <= this.repositoryAccessLevel,
+ );
+ },
- repoFeatureAccessLevelOptions() {
- return this.featureAccessLevelOptions.filter(
- ([value]) => value <= this.repositoryAccessLevel,
- );
- },
+ pagesFeatureAccessLevelOptions() {
+ if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
+ return this.featureAccessLevelOptions.concat([[30, 'Everyone']]);
+ }
+ return this.featureAccessLevelOptions;
+ },
- repositoryEnabled() {
- return this.repositoryAccessLevel > 0;
- },
+ repositoryEnabled() {
+ return this.repositoryAccessLevel > 0;
+ },
- visibilityLevelDescription() {
- return visibilityLevelDescriptions[this.visibilityLevel];
- },
+ visibilityLevelDescription() {
+ return visibilityLevelDescriptions[this.visibilityLevel];
},
+ },
- watch: {
- visibilityLevel(value, oldValue) {
- if (value === visibilityOptions.PRIVATE) {
- // when private, features are restricted to "only team members"
- this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel);
- this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel);
- this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel);
- this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
- this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
- this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
- this.highlightChanges();
- } else if (oldValue === visibilityOptions.PRIVATE) {
- // if changing away from private, make enabled features more permissive
- if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20;
- if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20;
- if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20;
- if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
- if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
- if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
- this.highlightChanges();
+ watch: {
+ visibilityLevel(value, oldValue) {
+ if (value === visibilityOptions.PRIVATE) {
+ // when private, features are restricted to "only team members"
+ this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel);
+ this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel);
+ this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel);
+ this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
+ this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
+ this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
+ if (this.pagesAccessLevel === 20) {
+ // When from Internal->Private narrow access for only members
+ this.pagesAccessLevel = 10;
}
- },
+ this.highlightChanges();
+ } else if (oldValue === visibilityOptions.PRIVATE) {
+ // if changing away from private, make enabled features more permissive
+ if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20;
+ if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20;
+ if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20;
+ if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
+ if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
+ if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
+ if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
+ this.highlightChanges();
+ }
+ },
- repositoryAccessLevel(value, oldValue) {
- if (value < oldValue) {
- // sub-features cannot have more premissive access level
- this.mergeRequestsAccessLevel = Math.min(this.mergeRequestsAccessLevel, value);
- this.buildsAccessLevel = Math.min(this.buildsAccessLevel, value);
+ repositoryAccessLevel(value, oldValue) {
+ if (value < oldValue) {
+ // sub-features cannot have more premissive access level
+ this.mergeRequestsAccessLevel = Math.min(this.mergeRequestsAccessLevel, value);
+ this.buildsAccessLevel = Math.min(this.buildsAccessLevel, value);
- if (value === 0) {
- this.containerRegistryEnabled = false;
- this.lfsEnabled = false;
- }
- } else if (oldValue === 0) {
- this.mergeRequestsAccessLevel = value;
- this.buildsAccessLevel = value;
- this.containerRegistryEnabled = true;
- this.lfsEnabled = true;
+ if (value === 0) {
+ this.containerRegistryEnabled = false;
+ this.lfsEnabled = false;
}
- },
+ } else if (oldValue === 0) {
+ this.mergeRequestsAccessLevel = value;
+ this.buildsAccessLevel = value;
+ this.containerRegistryEnabled = true;
+ this.lfsEnabled = true;
+ }
+ },
- issuesAccessLevel(value, oldValue) {
- if (value === 0) toggleHiddenClassBySelector('.issues-feature', true);
- else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false);
- },
+ issuesAccessLevel(value, oldValue) {
+ if (value === 0) toggleHiddenClassBySelector('.issues-feature', true);
+ else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false);
+ },
- mergeRequestsAccessLevel(value, oldValue) {
- if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true);
- else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false);
- },
+ mergeRequestsAccessLevel(value, oldValue) {
+ if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true);
+ else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false);
+ },
- buildsAccessLevel(value, oldValue) {
- if (value === 0) toggleHiddenClassBySelector('.builds-feature', true);
- else if (oldValue === 0) toggleHiddenClassBySelector('.builds-feature', false);
- },
+ buildsAccessLevel(value, oldValue) {
+ if (value === 0) toggleHiddenClassBySelector('.builds-feature', true);
+ else if (oldValue === 0) toggleHiddenClassBySelector('.builds-feature', false);
},
+ },
- methods: {
- highlightChanges() {
- this.highlightChangesClass = true;
- this.$nextTick(() => {
- this.highlightChangesClass = false;
- });
- },
+ methods: {
+ highlightChanges() {
+ this.highlightChangesClass = true;
+ this.$nextTick(() => {
+ this.highlightChangesClass = false;
+ });
+ },
- visibilityAllowed(option) {
- return this.allowedVisibilityOptions.includes(option);
- },
+ visibilityAllowed(option) {
+ return this.allowedVisibilityOptions.includes(option);
},
- };
+ },
+};
</script>
<template>
@@ -240,8 +266,8 @@
help-text="Lightweight issue tracking system for this project"
>
<project-feature-setting
- :options="featureAccessLevelOptions"
v-model="issuesAccessLevel"
+ :options="featureAccessLevelOptions"
name="project[project_feature_attributes][issues_access_level]"
/>
</project-setting-row>
@@ -250,8 +276,8 @@
help-text="View and edit files in this project"
>
<project-feature-setting
- :options="featureAccessLevelOptions"
v-model="repositoryAccessLevel"
+ :options="featureAccessLevelOptions"
name="project[project_feature_attributes][repository_access_level]"
/>
</project-setting-row>
@@ -261,8 +287,8 @@
help-text="Submit changes to be merged upstream"
>
<project-feature-setting
- :options="repoFeatureAccessLevelOptions"
v-model="mergeRequestsAccessLevel"
+ :options="repoFeatureAccessLevelOptions"
:disabled-input="!repositoryEnabled"
name="project[project_feature_attributes][merge_requests_access_level]"
/>
@@ -272,8 +298,8 @@
help-text="Build, test, and deploy your changes"
>
<project-feature-setting
- :options="repoFeatureAccessLevelOptions"
v-model="buildsAccessLevel"
+ :options="repoFeatureAccessLevelOptions"
:disabled-input="!repositoryEnabled"
name="project[project_feature_attributes][builds_access_level]"
/>
@@ -308,8 +334,8 @@
help-text="Pages for project documentation"
>
<project-feature-setting
- :options="featureAccessLevelOptions"
v-model="wikiAccessLevel"
+ :options="featureAccessLevelOptions"
name="project[project_feature_attributes][wiki_access_level]"
/>
</project-setting-row>
@@ -318,11 +344,23 @@
help-text="Share code pastes with others out of Git repository"
>
<project-feature-setting
- :options="featureAccessLevelOptions"
v-model="snippetsAccessLevel"
+ :options="featureAccessLevelOptions"
name="project[project_feature_attributes][snippets_access_level]"
/>
</project-setting-row>
+ <project-setting-row
+ v-if="pagesAvailable && pagesAccessControlEnabled"
+ :help-path="pagesHelpPath"
+ label="Pages"
+ help-text="Static website for the project."
+ >
+ <project-feature-setting
+ v-model="pagesAccessLevel"
+ :options="pagesFeatureAccessLevelOptions"
+ name="project[project_feature_attributes][pages_access_level]"
+ />
+ </project-setting-row>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/constants.js b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
index ce47562f259..bc5c29d12b5 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/constants.js
+++ b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
@@ -5,7 +5,9 @@ export const visibilityOptions = {
};
export const visibilityLevelDescriptions = {
- [visibilityOptions.PRIVATE]: 'The project is accessible only by members of the project. Access must be granted explicitly to each user.',
+ [visibilityOptions.PRIVATE]:
+ 'The project is accessible only by members of the project. Access must be granted explicitly to each user.',
[visibilityOptions.INTERNAL]: 'The project can be accessed by any user who is logged in.',
- [visibilityOptions.PUBLIC]: 'The project can be accessed by anyone, regardless of authentication.',
+ [visibilityOptions.PUBLIC]:
+ 'The project can be accessed by anyone, regardless of authentication.',
};
diff --git a/app/assets/javascripts/pages/projects/shared/project_avatar.js b/app/assets/javascripts/pages/projects/shared/project_avatar.js
index 447877752fe..1e69ecb481d 100644
--- a/app/assets/javascripts/pages/projects/shared/project_avatar.js
+++ b/app/assets/javascripts/pages/projects/shared/project_avatar.js
@@ -8,8 +8,9 @@ export default function projectAvatar() {
$('.js-project-avatar-input').bind('change', function onClickAvatarInput() {
const form = $(this).closest('form');
- // eslint-disable-next-line no-useless-escape
- const filename = $(this).val().replace(/^.*[\\\/]/, '');
+ const filename = $(this)
+ .val()
+ .replace(/^.*[\\\/]/, ''); // eslint-disable-line no-useless-escape
return form.find('.js-avatar-filename').text(filename);
});
}
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index b76f2f76449..7302c1ab202 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import initBlob from '~/blob_edit/blob_bundle';
-import ShortcutsNavigation from '~/shortcuts_navigation';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import NotificationsForm from '~/notifications_form';
import UserCallout from '~/user_callout';
import TreeView from '~/tree';
@@ -8,15 +8,18 @@ import BlobViewer from '~/blob/viewer/index';
import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils';
import GpgBadges from '~/gpg_badges';
+import initReadMore from '~/read_more';
import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown';
document.addEventListener('DOMContentLoaded', () => {
+ initReadMore();
new Star(); // eslint-disable-line no-new
notificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new
new NotificationsForm(); // eslint-disable-line no-new
- new UserCallout({ // eslint-disable-line no-new
+ // eslint-disable-next-line no-new
+ new UserCallout({
setCalloutPerProject: false,
className: 'js-autodevops-banner',
});
diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js
index 33d69d891d8..400aed35e32 100644
--- a/app/assets/javascripts/pages/projects/tree/show/index.js
+++ b/app/assets/javascripts/pages/projects/tree/show/index.js
@@ -4,7 +4,7 @@ import initBlob from '~/blob_edit/blob_bundle';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import GpgBadges from '~/gpg_badges';
import TreeView from '../../../../tree';
-import ShortcutsNavigation from '../../../../shortcuts_navigation';
+import ShortcutsNavigation from '../../../../behaviors/shortcuts/shortcuts_navigation';
import BlobViewer from '../../../../blob/viewer';
import NewCommitForm from '../../../../new_commit_form';
import { ajaxGet } from '../../../../lib/utils/common_utils';
diff --git a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
index 0289209ff1e..cbe85eaa590 100644
--- a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
+++ b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
@@ -1,12 +1,15 @@
<script>
import _ from 'underscore';
-import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
+import { GlModal, GlModalDirective } from '@gitlab/ui';
export default {
components: {
GlModal,
},
+ directives: {
+ 'gl-modal': GlModalDirective,
+ },
props: {
deleteWikiUrl: {
type: String,
@@ -25,6 +28,9 @@ export default {
},
},
computed: {
+ modalId() {
+ return 'delete-wiki-modal';
+ },
message() {
return s__('WikiPageConfirmDelete|Are you sure you want to delete this page?');
},
@@ -47,31 +53,41 @@ export default {
</script>
<template>
- <gl-modal
- id="delete-wiki-modal"
- :header-title-text="title"
- :footer-primary-button-text="s__('WikiPageConfirmDelete|Delete page')"
- footer-primary-button-variant="danger"
- @submit="onSubmit"
- >
- {{ message }}
- <form
- ref="form"
- :action="deleteWikiUrl"
- method="post"
- class="js-requires-input"
+ <div class="d-inline-block">
+ <button
+ v-gl-modal="modalId"
+ type="button"
+ class="btn btn-danger"
+ >
+ {{ __('Delete') }}
+ </button>
+ <gl-modal
+ :title="title"
+ :ok-title="s__('WikiPageConfirmDelete|Delete page')"
+ :modal-id="modalId"
+ title-tag="h4"
+ ok-variant="danger"
+ @ok="onSubmit"
>
- <input
- ref="method"
- type="hidden"
- name="_method"
- value="delete"
- />
- <input
- :value="csrfToken"
- type="hidden"
- name="authenticity_token"
- />
- </form>
- </gl-modal>
+ {{ message }}
+ <form
+ ref="form"
+ :action="deleteWikiUrl"
+ method="post"
+ class="js-requires-input"
+ >
+ <input
+ ref="method"
+ type="hidden"
+ name="_method"
+ value="delete"
+ />
+ <input
+ :value="csrfToken"
+ type="hidden"
+ name="authenticity_token"
+ />
+ </form>
+ </gl-modal>
+ </div>
</template>
diff --git a/app/assets/javascripts/pages/projects/wikis/index.js b/app/assets/javascripts/pages/projects/wikis/index.js
index 0a0fe3fc137..f5fd84d69ac 100644
--- a/app/assets/javascripts/pages/projects/wikis/index.js
+++ b/app/assets/javascripts/pages/projects/wikis/index.js
@@ -2,8 +2,8 @@ import $ from 'jquery';
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import csrf from '~/lib/utils/csrf';
+import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki';
import Wikis from './wikis';
-import ShortcutsWiki from '../../../shortcuts_wiki';
import ZenMode from '../../../zen_mode';
import GLForm from '../../../gl_form';
import deleteWikiModal from './components/delete_wiki_modal.vue';
@@ -14,15 +14,16 @@ document.addEventListener('DOMContentLoaded', () => {
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.wiki-form')); // eslint-disable-line no-new
- const deleteWikiButton = document.getElementById('delete-wiki-button');
+ const deleteWikiModalWrapperEl = document.getElementById('delete-wiki-modal-wrapper');
- if (deleteWikiButton) {
+ if (deleteWikiModalWrapperEl) {
Vue.use(Translate);
- const { deleteWikiUrl, pageTitle } = deleteWikiButton.dataset;
- const deleteWikiModalEl = document.getElementById('delete-wiki-modal');
- const deleteModal = new Vue({ // eslint-disable-line
- el: deleteWikiModalEl,
+ const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: deleteWikiModalWrapperEl,
data: {
deleteWikiUrl: '',
},
diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js
index e3e0ab91993..0c896c8599e 100644
--- a/app/assets/javascripts/pages/search/show/search.js
+++ b/app/assets/javascripts/pages/search/show/search.js
@@ -22,7 +22,7 @@ export default class Search {
fields: ['full_name'],
},
data(term, callback) {
- return Api.groups(term, {}, (data) => {
+ return Api.groups(term, {}, data => {
data.unshift({
full_name: 'Any',
});
@@ -37,7 +37,7 @@ export default class Search {
return obj.full_name;
},
toggleLabel(obj) {
- return `${($groupDropdown.data('defaultLabel'))} ${obj.full_name}`;
+ return `${$groupDropdown.data('defaultLabel')} ${obj.full_name}`;
},
clicked: () => Search.submitSearch(),
});
@@ -52,7 +52,7 @@ export default class Search {
},
data: (term, callback) => {
this.getProjectsData(term)
- .then((data) => {
+ .then(data => {
data.unshift({
name_with_namespace: 'Any',
});
@@ -70,7 +70,7 @@ export default class Search {
return obj.name_with_namespace;
},
toggleLabel(obj) {
- return `${($projectDropdown.data('defaultLabel'))} ${obj.name_with_namespace}`;
+ return `${$projectDropdown.data('defaultLabel')} ${obj.name_with_namespace}`;
},
clicked: () => Search.submitSearch(),
});
@@ -99,17 +99,24 @@ export default class Search {
}
clearSearchField() {
- return $(this.searchInput).val('').trigger('keyup').focus();
+ return $(this.searchInput)
+ .val('')
+ .trigger('keyup')
+ .focus();
}
getProjectsData(term) {
- return new Promise((resolve) => {
+ return new Promise(resolve => {
if (this.groupId) {
Api.groupProjects(this.groupId, term, {}, resolve);
} else {
- Api.projects(term, {
- order_by: 'id',
- }, resolve);
+ Api.projects(
+ term,
+ {
+ order_by: 'id',
+ },
+ resolve,
+ );
}
});
}
diff --git a/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js b/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js
index 1e7c29aefaa..2b8f1e8b0ef 100644
--- a/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js
+++ b/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js
@@ -20,7 +20,7 @@ export default class SigninTabsMemoizer {
bootstrap() {
const tabs = document.querySelectorAll(this.tabSelector);
if (tabs.length > 0) {
- tabs[0].addEventListener('click', (e) => {
+ tabs[0].addEventListener('click', e => {
if (e.target && e.target.nodeName === 'A') {
const anchorName = e.target.getAttribute('href');
this.saveData(anchorName);
diff --git a/app/assets/javascripts/pages/sessions/new/username_validator.js b/app/assets/javascripts/pages/sessions/new/username_validator.js
index 97cf1aeaadc..7a41805bada 100644
--- a/app/assets/javascripts/pages/sessions/new/username_validator.js
+++ b/app/assets/javascripts/pages/sessions/new/username_validator.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, consistent-return, class-methods-use-this */
+/* eslint-disable consistent-return, class-methods-use-this */
import $ from 'jquery';
import _ from 'underscore';
@@ -22,10 +22,10 @@ export default class UsernameValidator {
available: false,
valid: false,
pending: false,
- empty: true
+ empty: true,
};
- const debounceTimeout = _.debounce((username) => {
+ const debounceTimeout = _.debounce(username => {
this.validateUsername(username);
}, debounceTimeoutDuration);
@@ -81,7 +81,8 @@ export default class UsernameValidator {
this.state.pending = true;
this.state.available = false;
this.renderState();
- axios.get(`${gon.relative_url_root}/users/${username}/exists`)
+ axios
+ .get(`${gon.relative_url_root}/users/${username}/exists`)
.then(({ data }) => this.setAvailabilityState(data.exists))
.catch(() => flash(__('An error occurred while validating username')));
}
@@ -100,8 +101,7 @@ export default class UsernameValidator {
clearFieldValidationState() {
this.inputElement.siblings('p').hide();
- this.inputElement.removeClass(invalidInputClass)
- .removeClass(successInputClass);
+ this.inputElement.removeClass(invalidInputClass).removeClass(successInputClass);
}
setUnavailableState() {
diff --git a/app/assets/javascripts/pages/snippets/form.js b/app/assets/javascripts/pages/snippets/form.js
index f369c7ef9a6..8859557e62d 100644
--- a/app/assets/javascripts/pages/snippets/form.js
+++ b/app/assets/javascripts/pages/snippets/form.js
@@ -11,6 +11,7 @@ export default () => {
epics: false,
milestones: false,
labels: false,
+ snippets: false,
});
new ZenMode(); // eslint-disable-line no-new
};
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index 9892a039941..bf592ba7a3c 100644
--- a/app/assets/javascripts/pages/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
@@ -43,7 +43,15 @@ const initColorKey = () =>
.domain([0, 3]);
export default class ActivityCalendar {
- constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0, firstDayOfWeek = 0) {
+ constructor(
+ container,
+ activitiesContainer,
+ timestamps,
+ calendarActivitiesPath,
+ utcOffset = 0,
+ firstDayOfWeek = 0,
+ monthsAgo = 12,
+ ) {
this.calendarActivitiesPath = calendarActivitiesPath;
this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = '';
@@ -66,6 +74,8 @@ export default class ActivityCalendar {
];
this.months = [];
this.firstDayOfWeek = firstDayOfWeek;
+ this.activitiesContainer = activitiesContainer;
+ this.container = container;
// Loop through the timestamps to create a group of objects
// The group of objects will be grouped based on the day of the week they are
@@ -75,13 +85,13 @@ export default class ActivityCalendar {
const today = getSystemDate(utcOffset);
today.setHours(0, 0, 0, 0, 0);
- const oneYearAgo = new Date(today);
- oneYearAgo.setFullYear(today.getFullYear() - 1);
+ const timeAgo = new Date(today);
+ timeAgo.setMonth(today.getMonth() - monthsAgo);
- const days = getDayDifference(oneYearAgo, today);
+ const days = getDayDifference(timeAgo, today);
for (let i = 0; i <= days; i += 1) {
- const date = new Date(oneYearAgo);
+ const date = new Date(timeAgo);
date.setDate(date.getDate() + i);
const day = date.getDay();
@@ -280,7 +290,7 @@ export default class ActivityCalendar {
this.currentSelectedDate.getDate(),
].join('-');
- $('.user-calendar-activities').html(LOADING_HTML);
+ $(this.activitiesContainer).html(LOADING_HTML);
axios
.get(this.calendarActivitiesPath, {
@@ -289,11 +299,11 @@ export default class ActivityCalendar {
},
responseType: 'text',
})
- .then(({ data }) => $('.user-calendar-activities').html(data))
+ .then(({ data }) => $(this.activitiesContainer).html(data))
.catch(() => flash(__('An error occurred while retrieving calendar activity')));
} else {
this.currentSelectedDate = '';
- $('.user-calendar-activities').html('');
+ $(this.activitiesContainer).html('');
}
}
}
diff --git a/app/assets/javascripts/pages/users/index.js b/app/assets/javascripts/pages/users/index.js
index 6b1626b0161..a191df00dfa 100644
--- a/app/assets/javascripts/pages/users/index.js
+++ b/app/assets/javascripts/pages/users/index.js
@@ -13,10 +13,12 @@ function initUserProfile(action) {
new UserTabs({ parentEl: '.user-profile', action });
// hide project limit message
- $('.hide-project-limit-message').on('click', (e) => {
+ $('.hide-project-limit-message').on('click', e => {
e.preventDefault();
Cookies.set('hide_project_limit_message', 'false');
- $(this).parents('.project-limit-message').remove();
+ $(this)
+ .parents('.project-limit-message')
+ .remove();
});
}
diff --git a/app/assets/javascripts/pages/users/user_overview_block.js b/app/assets/javascripts/pages/users/user_overview_block.js
new file mode 100644
index 00000000000..eec2b5ca8e5
--- /dev/null
+++ b/app/assets/javascripts/pages/users/user_overview_block.js
@@ -0,0 +1,52 @@
+import axios from '~/lib/utils/axios_utils';
+
+const DEFAULT_LIMIT = 20;
+
+export default class UserOverviewBlock {
+ constructor(options = {}) {
+ this.container = options.container;
+ this.url = options.url;
+ this.requestParams = {
+ limit: DEFAULT_LIMIT,
+ ...options.requestParams,
+ };
+ this.postRenderCallback = options.postRenderCallback;
+ this.loadData();
+ }
+
+ loadData() {
+ const loadingEl = document.querySelector(`${this.container} .loading`);
+
+ loadingEl.classList.remove('hide');
+
+ axios
+ .get(this.url, {
+ params: this.requestParams,
+ })
+ .then(({ data }) => this.render(data))
+ .catch(() => loadingEl.classList.add('hide'));
+ }
+
+ render(data) {
+ const { html, count } = data;
+ const contentList = document.querySelector(`${this.container} .overview-content-list`);
+
+ contentList.innerHTML += html;
+
+ const loadingEl = document.querySelector(`${this.container} .loading`);
+
+ if (count && count > 0) {
+ document.querySelector(`${this.container} .js-view-all`).classList.remove('hide');
+ } else {
+ document
+ .querySelector(`${this.container} .nothing-here-block`)
+ .classList.add('text-left', 'p-0');
+ }
+
+ loadingEl.classList.add('hide');
+
+ if (this.postRenderCallback) {
+ this.postRenderCallback.call(this);
+ }
+ }
+}
diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js
index a2ca03536f2..aa537d4a43e 100644
--- a/app/assets/javascripts/pages/users/user_tabs.js
+++ b/app/assets/javascripts/pages/users/user_tabs.js
@@ -2,9 +2,11 @@ import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import Activities from '~/activities';
import { localTimeAgo } from '~/lib/utils/datetime_utility';
+import AjaxCache from '~/lib/utils/ajax_cache';
import { __ } from '~/locale';
import flash from '~/flash';
import ActivityCalendar from './activity_calendar';
+import UserOverviewBlock from './user_overview_block';
/**
* UserTabs
@@ -64,16 +66,22 @@ import ActivityCalendar from './activity_calendar';
const CALENDAR_TEMPLATE = `
<div class="clearfix calendar">
<div class="js-contrib-calendar"></div>
- <div class="calendar-hint">
- Summary of issues, merge requests, push events, and comments
- </div>
+ <div class="calendar-hint bottom-right"></div>
</div>
`;
+const CALENDAR_PERIOD_6_MONTHS = 6;
+const CALENDAR_PERIOD_12_MONTHS = 12;
+/* computation based on
+ * width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group);
+ * (see activity_calendar.js)
+ */
+const OVERVIEW_CALENDAR_BREAKPOINT = 918;
+
export default class UserTabs {
constructor({ defaultAction, action, parentEl }) {
this.loaded = {};
- this.defaultAction = defaultAction || 'activity';
+ this.defaultAction = defaultAction || 'overview';
this.action = action || this.defaultAction;
this.$parentEl = $(parentEl) || $(document);
this.windowLocation = window.location;
@@ -95,6 +103,12 @@ export default class UserTabs {
.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
.on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event))
.on('click', '.gl-pagination a', event => this.changeProjectsPage(event));
+
+ window.addEventListener('resize', () => this.onResize());
+ }
+
+ onResize() {
+ this.loadActivityCalendar();
}
changeProjectsPage(e) {
@@ -124,6 +138,8 @@ export default class UserTabs {
}
if (action === 'activity') {
this.loadActivities();
+ } else if (action === 'overview') {
+ this.loadOverviewTab();
}
const loadableActions = ['groups', 'contributed', 'projects', 'snippets'];
@@ -154,29 +170,69 @@ export default class UserTabs {
if (this.loaded.activity) {
return;
}
- const $calendarWrap = this.$parentEl.find('.user-calendar');
+
+ // eslint-disable-next-line no-new
+ new Activities('#activity');
+
+ this.loaded.activity = true;
+ }
+
+ loadOverviewTab() {
+ if (this.loaded.overview) {
+ return;
+ }
+
+ this.loadActivityCalendar();
+
+ UserTabs.renderMostRecentBlocks('#js-overview .activities-block', {
+ requestParams: { limit: 10 },
+ });
+ UserTabs.renderMostRecentBlocks('#js-overview .projects-block', {
+ requestParams: { limit: 10, skip_pagination: true },
+ });
+
+ this.loaded.overview = true;
+ }
+
+ static renderMostRecentBlocks(container, options) {
+ // eslint-disable-next-line no-new
+ new UserOverviewBlock({
+ container,
+ url: $(`${container} .overview-content-list`).data('href'),
+ ...options,
+ postRenderCallback: () => localTimeAgo($('.js-timeago', container)),
+ });
+ }
+
+ loadActivityCalendar() {
+ const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar');
const calendarPath = $calendarWrap.data('calendarPath');
+
+ AjaxCache.retrieve(calendarPath)
+ .then(data => UserTabs.renderActivityCalendar(data, $calendarWrap))
+ .catch(() => flash(__('There was an error loading users activity calendar.')));
+ }
+
+ static renderActivityCalendar(data, $calendarWrap) {
+ const monthsAgo = UserTabs.getVisibleCalendarPeriod($calendarWrap);
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
- let utcFormatted = 'UTC';
- if (utcOffset !== 0) {
- utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${utcOffset / 3600}`;
- }
+ const calendarHint = __('Issues, merge requests, pushes and comments.');
- axios
- .get(calendarPath)
- .then(({ data }) => {
- $calendarWrap.html(CALENDAR_TEMPLATE);
- $calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
+ $calendarWrap.html(CALENDAR_TEMPLATE);
- // eslint-disable-next-line no-new
- new ActivityCalendar('.js-contrib-calendar', data, calendarActivitiesPath, utcOffset);
- })
- .catch(() => flash(__('There was an error loading users activity calendar.')));
+ $calendarWrap.find('.calendar-hint').text(calendarHint);
// eslint-disable-next-line no-new
- new Activities();
- this.loaded.activity = true;
+ new ActivityCalendar(
+ '.tab-pane.active .js-contrib-calendar',
+ '.tab-pane.active .user-calendar-activities',
+ data,
+ calendarActivitiesPath,
+ utcOffset,
+ 0,
+ monthsAgo,
+ );
}
toggleLoading(status) {
@@ -200,4 +256,11 @@ export default class UserTabs {
getCurrentAction() {
return this.$parentEl.find('.nav-links a.active').data('action');
}
+
+ static getVisibleCalendarPeriod($calendarWrap) {
+ const width = $calendarWrap.width();
+ return width < OVERVIEW_CALENDAR_BREAKPOINT
+ ? CALENDAR_PERIOD_6_MONTHS
+ : CALENDAR_PERIOD_12_MONTHS;
+ }
}
diff --git a/app/assets/javascripts/pdf/index.vue b/app/assets/javascripts/pdf/index.vue
index 2f480ecdc69..7b079fe02d9 100644
--- a/app/assets/javascripts/pdf/index.vue
+++ b/app/assets/javascripts/pdf/index.vue
@@ -1,57 +1,58 @@
<script>
- import pdfjsLib from 'vendor/pdf';
- import workerSrc from 'vendor/pdf.worker.min';
+import pdfjsLib from 'vendor/pdf';
+import workerSrc from 'vendor/pdf.worker.min';
- import page from './page/index.vue';
+import page from './page/index.vue';
- export default {
- components: { page },
- props: {
- pdf: {
- type: [String, Uint8Array],
- required: true,
- },
+export default {
+ components: { page },
+ props: {
+ pdf: {
+ type: [String, Uint8Array],
+ required: true,
},
- data() {
- return {
- loading: false,
- pages: [],
- };
+ },
+ data() {
+ return {
+ loading: false,
+ pages: [],
+ };
+ },
+ computed: {
+ document() {
+ return typeof this.pdf === 'string' ? this.pdf : { data: this.pdf };
},
- computed: {
- document() {
- return typeof this.pdf === 'string' ? this.pdf : { data: this.pdf };
- },
- hasPDF() {
- return this.pdf && this.pdf.length > 0;
- },
+ hasPDF() {
+ return this.pdf && this.pdf.length > 0;
},
- watch: { pdf: 'load' },
- mounted() {
- pdfjsLib.PDFJS.workerSrc = workerSrc;
- if (this.hasPDF) this.load();
+ },
+ watch: { pdf: 'load' },
+ mounted() {
+ pdfjsLib.PDFJS.workerSrc = workerSrc;
+ if (this.hasPDF) this.load();
+ },
+ methods: {
+ load() {
+ this.pages = [];
+ return pdfjsLib
+ .getDocument(this.document)
+ .then(this.renderPages)
+ .then(() => this.$emit('pdflabload'))
+ .catch(error => this.$emit('pdflaberror', error))
+ .then(() => {
+ this.loading = false;
+ });
},
- methods: {
- load() {
- this.pages = [];
- return pdfjsLib.getDocument(this.document)
- .then(this.renderPages)
- .then(() => this.$emit('pdflabload'))
- .catch(error => this.$emit('pdflaberror', error))
- .then(() => { this.loading = false; });
- },
- renderPages(pdf) {
- const pagePromises = [];
- this.loading = true;
- for (let num = 1; num <= pdf.numPages; num += 1) {
- pagePromises.push(
- pdf.getPage(num).then(p => this.pages.push(p)),
- );
- }
- return Promise.all(pagePromises);
- },
+ renderPages(pdf) {
+ const pagePromises = [];
+ this.loading = true;
+ for (let num = 1; num <= pdf.numPages; num += 1) {
+ pagePromises.push(pdf.getPage(num).then(p => this.pages.push(p)));
+ }
+ return Promise.all(pagePromises);
},
- };
+ },
+};
</script>
<template>
@@ -69,9 +70,9 @@
</template>
<style>
- .pdf-viewer {
- background: url('./assets/img/bg.gif');
- display: flex;
- flex-flow: column nowrap;
- }
+.pdf-viewer {
+ background: url('./assets/img/bg.gif');
+ display: flex;
+ flex-flow: column nowrap;
+}
</style>
diff --git a/app/assets/javascripts/pdf/page/index.vue b/app/assets/javascripts/pdf/page/index.vue
index 9f06833d560..96aadf41653 100644
--- a/app/assets/javascripts/pdf/page/index.vue
+++ b/app/assets/javascripts/pdf/page/index.vue
@@ -1,44 +1,47 @@
<script>
- export default {
- props: {
- page: {
- type: Object,
- required: true,
- },
- number: {
- type: Number,
- required: true,
- },
+export default {
+ props: {
+ page: {
+ type: Object,
+ required: true,
},
- data() {
- return {
- scale: 4,
- rendering: false,
- };
+ number: {
+ type: Number,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ scale: 4,
+ rendering: false,
+ };
+ },
+ computed: {
+ viewport() {
+ return this.page.getViewport(this.scale);
},
- computed: {
- viewport() {
- return this.page.getViewport(this.scale);
- },
- context() {
- return this.$refs.canvas.getContext('2d');
- },
- renderContext() {
- return {
- canvasContext: this.context,
- viewport: this.viewport,
- };
- },
+ context() {
+ return this.$refs.canvas.getContext('2d');
},
- mounted() {
- this.$refs.canvas.height = this.viewport.height;
- this.$refs.canvas.width = this.viewport.width;
- this.rendering = true;
- this.page.render(this.renderContext)
- .then(() => { this.rendering = false; })
- .catch(error => this.$emit('pdflaberror', error));
+ renderContext() {
+ return {
+ canvasContext: this.context,
+ viewport: this.viewport,
+ };
},
- };
+ },
+ mounted() {
+ this.$refs.canvas.height = this.viewport.height;
+ this.$refs.canvas.width = this.viewport.width;
+ this.rendering = true;
+ this.page
+ .render(this.renderContext)
+ .then(() => {
+ this.rendering = false;
+ })
+ .catch(error => this.$emit('pdflaberror', error));
+ },
+};
</script>
<template>
@@ -51,20 +54,20 @@
</template>
<style>
- .pdf-page {
- margin: 8px auto 0 auto;
- border-top: 1px #ddd solid;
- border-bottom: 1px #ddd solid;
- width: 100%;
- }
+.pdf-page {
+ margin: 8px auto 0 auto;
+ border-top: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+ width: 100%;
+}
- .pdf-page:first-child {
- margin-top: 0px;
- border-top: 0px;
- }
+.pdf-page:first-child {
+ margin-top: 0px;
+ border-top: 0px;
+}
- .pdf-page:last-child {
- margin-bottom: 0px;
- border-bottom: 0px;
- }
+.pdf-page:last-child {
+ margin-bottom: 0px;
+ border-bottom: 0px;
+}
</style>
diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
index 0fdb0a080cf..300d453c174 100644
--- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
+++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+import { glEmojiTag } from '~/emoji';
import detailedMetric from './detailed_metric.vue';
import requestSelector from './request_selector.vue';
@@ -42,7 +43,7 @@ export default {
keys: ['feature', 'request'],
},
],
- simpleMetrics: ['redis', 'sidekiq'],
+ simpleMetrics: ['redis'],
data() {
return { currentRequestId: '' };
},
@@ -64,6 +65,16 @@ export default {
lineProfileModal() {
return $('#modal-peek-line-profile');
},
+ hasHost() {
+ return this.currentRequest && this.currentRequest.details && this.currentRequest.details.host;
+ },
+ birdEmoji() {
+ if (this.hasHost && this.currentRequest.details.host.canary) {
+ return glEmojiTag('baby_chick');
+ }
+
+ return '';
+ },
},
mounted() {
this.currentRequest = this.requestId;
@@ -93,9 +104,11 @@ export default {
class="view"
>
<span
- v-if="currentRequest.details"
+ v-if="hasHost"
class="current-host"
+ :class="{ 'canary' : currentRequest.details.host.canary }"
>
+ <span v-html="birdEmoji"></span>
{{ currentRequest.details.host.hostname }}
</span>
</div>
@@ -130,8 +143,8 @@ export default {
</div>
<simple-metric
v-for="metric in $options.simpleMetrics"
- :current-request="currentRequest"
:key="metric"
+ :current-request="currentRequest"
:metric="metric"
/>
<div
diff --git a/app/assets/javascripts/performance_bar/components/simple_metric.vue b/app/assets/javascripts/performance_bar/components/simple_metric.vue
index b654bc66249..7a558558c4d 100644
--- a/app/assets/javascripts/performance_bar/components/simple_metric.vue
+++ b/app/assets/javascripts/performance_bar/components/simple_metric.vue
@@ -10,6 +10,19 @@ export default {
required: true,
},
},
+ computed: {
+ duration() {
+ return (
+ this.currentRequest.details[this.metric] &&
+ this.currentRequest.details[this.metric].duration
+ );
+ },
+ calls() {
+ return (
+ this.currentRequest.details[this.metric] && this.currentRequest.details[this.metric].calls
+ );
+ },
+ },
};
</script>
<template>
@@ -21,9 +34,9 @@ export default {
v-if="currentRequest.details"
class="bold"
>
- {{ currentRequest.details[metric].duration }}
+ {{ duration }}
/
- {{ currentRequest.details[metric].calls }}
+ {{ calls }}
</span>
{{ metric }}
</div>
diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js
index 41880d27516..29bfb7ee5df 100644
--- a/app/assets/javascripts/performance_bar/index.js
+++ b/app/assets/javascripts/performance_bar/index.js
@@ -1,5 +1,4 @@
import Vue from 'vue';
-import Flash from '../flash';
import PerformanceBarService from './services/performance_bar_service';
import PerformanceBarStore from './stores/performance_bar_store';
@@ -10,8 +9,7 @@ export default ({ container }) =>
performanceBarApp: () => import('./components/performance_bar_app.vue'),
},
data() {
- const performanceBarData = document.querySelector(this.$options.el)
- .dataset;
+ const performanceBarData = document.querySelector(this.$options.el).dataset;
const store = new PerformanceBarStore();
return {
@@ -46,7 +44,8 @@ export default ({ container }) =>
this.store.addRequestDetails(requestId, res.data.data);
})
.catch(() =>
- Flash(`Error getting performance bar results for ${requestId}`),
+ // eslint-disable-next-line no-console
+ console.warn(`Error getting performance bar results for ${requestId}`),
);
},
},
diff --git a/app/assets/javascripts/performance_bar/services/performance_bar_service.js b/app/assets/javascripts/performance_bar/services/performance_bar_service.js
index bc71911ae35..3a496fa2ed8 100644
--- a/app/assets/javascripts/performance_bar/services/performance_bar_service.js
+++ b/app/assets/javascripts/performance_bar/services/performance_bar_service.js
@@ -11,13 +11,12 @@ export default class PerformanceBarService {
static registerInterceptor(peekUrl, callback) {
const interceptor = response => {
- const requestId = response.headers['x-request-id'];
- // Get the request URL from response.config for Axios, and response for
- // Vue Resource.
- const requestUrl = (response.config || response).url;
- const cachedResponse = response.headers['x-gitlab-from-cache'] === 'true';
+ const [fireCallback, requestId, requestUrl] = PerformanceBarService.callbackParams(
+ response,
+ peekUrl,
+ );
- if (requestUrl !== peekUrl && requestId && !cachedResponse) {
+ if (fireCallback) {
callback(requestId, requestUrl);
}
@@ -33,9 +32,18 @@ export default class PerformanceBarService {
static removeInterceptor(interceptor) {
axios.interceptors.response.eject(interceptor);
- Vue.http.interceptors = _.without(
- Vue.http.interceptors,
- vueResourceInterceptor,
- );
+ Vue.http.interceptors = _.without(Vue.http.interceptors, vueResourceInterceptor);
+ }
+
+ static callbackParams(response, peekUrl) {
+ const requestId = response.headers && response.headers['x-request-id'];
+ // Get the request URL from response.config for Axios, and response for
+ // Vue Resource.
+ const requestUrl = (response.config || response).url;
+ const apiRequest = requestUrl && requestUrl.match(/^\/api\//);
+ const cachedResponse = response.headers && response.headers['x-gitlab-from-cache'] === 'true';
+ const fireCallback = requestUrl !== peekUrl && requestId && !apiRequest && !cachedResponse;
+
+ return [fireCallback, requestId, requestUrl];
}
}
diff --git a/app/assets/javascripts/performance_bar/stores/performance_bar_store.js b/app/assets/javascripts/performance_bar/stores/performance_bar_store.js
index c6b2f55243c..031e774d533 100644
--- a/app/assets/javascripts/performance_bar/stores/performance_bar_store.js
+++ b/app/assets/javascripts/performance_bar/stores/performance_bar_store.js
@@ -32,8 +32,6 @@ export default class PerformanceBarStore {
}
canTrackRequest(requestUrl) {
- return (
- this.requests.filter(request => request.url === requestUrl).length < 2
- );
+ return this.requests.filter(request => request.url === requestUrl).length < 2;
}
}
diff --git a/app/assets/javascripts/pipelines/components/empty_state.vue b/app/assets/javascripts/pipelines/components/empty_state.vue
index c5a45afc634..f756c651684 100644
--- a/app/assets/javascripts/pipelines/components/empty_state.vue
+++ b/app/assets/javascripts/pipelines/components/empty_state.vue
@@ -1,6 +1,11 @@
<script>
+import { GlButton } from '@gitlab/ui';
+
export default {
name: 'PipelinesEmptyState',
+ components: {
+ GlButton,
+ },
props: {
helpPagePath: {
type: String,
@@ -41,12 +46,13 @@ export default {
</p>
<div class="text-center">
- <a
+ <gl-button
:href="helpPagePath"
- class="btn btn-primary js-get-started-pipelines"
+ variant="primary"
+ class="js-get-started-pipelines"
>
{{ s__('Pipelines|Get started with Pipelines') }}
- </a>
+ </gl-button>
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue
index b82e28a0735..3a39dfe181d 100644
--- a/app/assets/javascripts/pipelines/components/graph/action_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue
@@ -1,10 +1,9 @@
<script>
-import $ from 'jquery';
+import { GlTooltipDirective, GlButton } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import { dasherize } from '~/lib/utils/text_utility';
import { __ } from '~/locale';
import createFlash from '~/flash';
-import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
/**
@@ -20,23 +19,20 @@ import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
Icon,
+ GlButton,
},
-
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
-
props: {
tooltipText: {
type: String,
required: true,
},
-
link: {
type: String,
required: true,
},
-
actionIcon: {
type: String,
required: true,
@@ -47,7 +43,6 @@ export default {
isDisabled: false,
};
},
-
computed: {
cssClass() {
const actionIconDash = dasherize(this.actionIcon);
@@ -62,8 +57,7 @@ export default {
*
*/
onClickAction() {
- $(this.$el).tooltip('hide');
-
+ this.$root.$emit('bv::hide::tooltip', `js-ci-action-${this.link}`);
this.isDisabled = true;
axios
@@ -82,18 +76,16 @@ export default {
};
</script>
<template>
- <button
- v-tooltip
+ <gl-button
+ :id="`js-ci-action-${link}`"
+ v-gl-tooltip="{ boundary: 'viewport' }"
:title="tooltipText"
:class="cssClass"
:disabled="isDisabled"
- type="button"
class="js-ci-action btn btn-blank
btn-transparent ci-action-icon-container ci-action-icon-wrapper"
- data-container="body"
- data-boundary="viewport"
@click="onClickAction"
>
<icon :name="actionIcon"/>
- </button>
+ </gl-button>
</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
deleted file mode 100644
index 8487c8036ee..00000000000
--- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
+++ /dev/null
@@ -1,123 +0,0 @@
-<script>
-import $ from 'jquery';
-import _ from 'underscore';
-import JobNameComponent from './job_name_component.vue';
-import JobComponent from './job_component.vue';
-import tooltip from '../../../vue_shared/directives/tooltip';
-
-/**
- * Renders the dropdown for the pipeline graph.
- *
- * The following object should be provided as `job`:
- *
- * {
- * "id": 4256,
- * "name": "test",
- * "status": {
- * "icon": "status_success",
- * "text": "passed",
- * "label": "passed",
- * "group": "success",
- * "details_path": "/root/ci-mock/builds/4256",
- * "action": {
- * "icon": "retry",
- * "title": "Retry",
- * "path": "/root/ci-mock/builds/4256/retry",
- * "method": "post"
- * }
- * }
- * }
- */
-export default {
- directives: {
- tooltip,
- },
-
- components: {
- JobComponent,
- JobNameComponent,
- },
-
- props: {
- job: {
- type: Object,
- required: true,
- },
- },
-
- computed: {
- tooltipText() {
- return _.escape(`${this.job.name} - ${this.job.status.label}`);
- },
- },
-
- mounted() {
- this.stopDropdownClickPropagation();
- },
-
- methods: {
- /**
- * When the user right clicks or cmd/ctrl + click in the job name or the action icon
- * the dropdown should not be closed so we stop propagation
- * of the click event inside the dropdown.
- *
- * Since this component is rendered multiple times per page we need to guarantee we only
- * target the click event of this component.
- */
- stopDropdownClickPropagation() {
- $(
- '.js-grouped-pipeline-dropdown button, .js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item',
- this.$el,
- ).on('click', e => {
- e.stopPropagation();
- });
- },
-
- pipelineActionRequestComplete() {
- this.$emit('pipelineActionRequestComplete');
- },
- },
-};
-</script>
-<template>
- <div class="ci-job-dropdown-container dropdown dropright">
- <button
- v-tooltip
- :title="tooltipText"
- type="button"
- data-toggle="dropdown"
- data-container="body"
- data-boundary="viewport"
- data-display="static"
- class="dropdown-menu-toggle build-content"
- >
-
- <job-name-component
- :name="job.name"
- :status="job.status"
- />
-
- <span class="dropdown-counter-badge">
- {{ job.size }}
- </span>
- </button>
-
- <ul class="dropdown-menu big-pipeline-graph-dropdown-menu js-grouped-pipeline-dropdown">
- <li class="scrollable-menu">
- <ul>
- <li
- v-for="(item, i) in job.jobs"
- :key="i"
- >
- <job-component
- :dropdown-length="job.size"
- :job="item"
- css-class-job-name="mini-pipeline-graph-dropdown-item"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
- />
- </li>
- </ul>
- </li>
- </ul>
- </div>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 1952dd453f4..6c9a11c3829 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -1,12 +1,12 @@
<script>
import _ from 'underscore';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import StageColumnComponent from './stage_column_component.vue';
export default {
components: {
StageColumnComponent,
- LoadingIcon,
+ GlLoadingIcon,
},
props: {
isLoading: {
@@ -59,9 +59,9 @@ export default {
<div class="build-content middle-block js-pipeline-graph">
<div class="pipeline-visualization pipeline-graph pipeline-tab-content">
<div class="text-center">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
- size="3"
+ :size="3"
/>
</div>
@@ -70,9 +70,9 @@ export default {
class="stage-column-list">
<stage-column-component
v-for="(stage, index) in graph"
- :title="capitalizeStageName(stage.name)"
- :jobs="stage.groups"
:key="stage.name"
+ :title="capitalizeStageName(stage.name)"
+ :groups="stage.groups"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
@refreshPipelineGraph="refreshPipelineGraph"
diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue
deleted file mode 100644
index 66f95147193..00000000000
--- a/app/assets/javascripts/pipelines/components/graph/job_component.vue
+++ /dev/null
@@ -1,143 +0,0 @@
-<script>
-import _ from 'underscore';
-import ActionComponent from './action_component.vue';
-import JobNameComponent from './job_name_component.vue';
-import tooltip from '../../../vue_shared/directives/tooltip';
-
-/**
- * Renders the badge for the pipeline graph and the job's dropdown.
- *
- * The following object should be provided as `job`:
- *
- * {
- * "id": 4256,
- * "name": "test",
- * "status": {
- * "icon": "status_success",
- * "text": "passed",
- * "label": "passed",
- * "group": "success",
- * "tooltip": "passed",
- * "details_path": "/root/ci-mock/builds/4256",
- * "action": {
- * "icon": "retry",
- * "title": "Retry",
- * "path": "/root/ci-mock/builds/4256/retry",
- * "method": "post"
- * }
- * }
- * }
- */
-
-export default {
- components: {
- ActionComponent,
- JobNameComponent,
- },
- directives: {
- tooltip,
- },
- props: {
- job: {
- type: Object,
- required: true,
- },
- cssClassJobName: {
- type: String,
- required: false,
- default: '',
- },
- dropdownLength: {
- type: Number,
- required: false,
- default: Infinity,
- },
- },
- computed: {
- status() {
- return this.job && this.job.status ? this.job.status : {};
- },
-
- tooltipText() {
- const textBuilder = [];
-
- if (this.job.name) {
- textBuilder.push(_.escape(this.job.name));
- }
-
- if (this.job.name && this.status.tooltip) {
- textBuilder.push('-');
- }
-
- if (this.status.tooltip) {
- textBuilder.push(this.job.status.tooltip);
- }
-
- return textBuilder.join(' ');
- },
-
- tooltipBoundary() {
- return this.dropdownLength < 5 ? 'viewport' : null;
- },
-
- /**
- * Verifies if the provided job has an action path
- *
- * @return {Boolean}
- */
- hasAction() {
- return this.job.status && this.job.status.action && this.job.status.action.path;
- },
- },
- methods: {
- pipelineActionRequestComplete() {
- this.$emit('pipelineActionRequestComplete');
- },
- },
-};
-</script>
-<template>
- <div class="ci-job-component">
- <a
- v-tooltip
- v-if="status.has_details"
- :href="status.details_path"
- :title="tooltipText"
- :class="cssClassJobName"
- :data-boundary="tooltipBoundary"
- data-container="body"
- data-html="true"
- class="js-pipeline-graph-job-link"
- >
-
- <job-name-component
- :name="job.name"
- :status="job.status"
- />
- </a>
-
- <div
- v-tooltip
- v-else
- :title="tooltipText"
- :class="cssClassJobName"
- class="js-job-component-tooltip non-details-job-component"
- data-html="true"
- data-container="body"
- >
-
- <job-name-component
- :name="job.name"
- :status="job.status"
- />
- </div>
-
- <action-component
- v-if="hasAction"
- :tooltip-text="status.action.title"
- :link="status.action.path"
- :action-icon="status.action.icon"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
new file mode 100644
index 00000000000..2670ea29db6
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
@@ -0,0 +1,99 @@
+<script>
+import $ from 'jquery';
+import { GlTooltipDirective } from '@gitlab/ui';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import JobItem from './job_item.vue';
+
+/**
+ * Renders the dropdown for the pipeline graph.
+ *
+ * The object provided as `group` corresponds to app/serializers/job_group_entity.rb.
+ *
+ */
+export default {
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ components: {
+ JobItem,
+ CiIcon,
+ },
+ props: {
+ group: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ tooltipText() {
+ const { name, status } = this.group;
+ return `${name} - ${status.label}`;
+ },
+ },
+ mounted() {
+ this.stopDropdownClickPropagation();
+ },
+ methods: {
+ /**
+ * When the user right clicks or cmd/ctrl + click in the group name or the action icon
+ * the dropdown should not be closed so we stop propagation
+ * of the click event inside the dropdown.
+ *
+ * Since this component is rendered multiple times per page we need to guarantee we only
+ * target the click event of this component.
+ */
+ stopDropdownClickPropagation() {
+ $(
+ '.js-grouped-pipeline-dropdown button, .js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item',
+ this.$el,
+ ).on('click', e => {
+ e.stopPropagation();
+ });
+ },
+
+ pipelineActionRequestComplete() {
+ this.$emit('pipelineActionRequestComplete');
+ },
+ },
+};
+</script>
+<template>
+ <div class="ci-job-dropdown-container dropdown dropright">
+ <button
+ v-gl-tooltip.hover="{ boundary: 'viewport' }"
+ :title="tooltipText"
+ type="button"
+ data-toggle="dropdown"
+ data-display="static"
+ class="dropdown-menu-toggle build-content"
+ >
+ <ci-icon :status="group.status" />
+
+ <span class="ci-status-text">
+ {{ group.name }}
+ </span>
+
+ <span class="dropdown-counter-badge">
+ {{ group.size }}
+ </span>
+ </button>
+
+ <ul class="dropdown-menu big-pipeline-graph-dropdown-menu js-grouped-pipeline-dropdown">
+ <li class="scrollable-menu">
+ <ul>
+ <li
+ v-for="job in group.jobs"
+ :key="job.id"
+ >
+ <job-item
+ :dropdown-length="group.size"
+ :job="job"
+ css-class-job-name="mini-pipeline-graph-dropdown-item"
+ @pipelineActionRequestComplete="pipelineActionRequestComplete"
+ />
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/job_item.vue b/app/assets/javascripts/pipelines/components/graph/job_item.vue
new file mode 100644
index 00000000000..e6abf32decc
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/job_item.vue
@@ -0,0 +1,145 @@
+<script>
+import ActionComponent from './action_component.vue';
+import JobNameComponent from './job_name_component.vue';
+import { GlTooltipDirective, GlLink } from '@gitlab/ui';
+import { sprintf } from '~/locale';
+import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
+
+/**
+ * Renders the badge for the pipeline graph and the job's dropdown.
+ *
+ * The following object should be provided as `job`:
+ *
+ * {
+ * "id": 4256,
+ * "name": "test",
+ * "status": {
+ * "icon": "status_success",
+ * "text": "passed",
+ * "label": "passed",
+ * "group": "success",
+ * "tooltip": "passed",
+ * "details_path": "/root/ci-mock/builds/4256",
+ * "action": {
+ * "icon": "retry",
+ * "title": "Retry",
+ * "path": "/root/ci-mock/builds/4256/retry",
+ * "method": "post"
+ * }
+ * }
+ * }
+ */
+
+export default {
+ components: {
+ ActionComponent,
+ JobNameComponent,
+ GlLink,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ mixins: [delayedJobMixin],
+ props: {
+ job: {
+ type: Object,
+ required: true,
+ },
+ cssClassJobName: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ dropdownLength: {
+ type: Number,
+ required: false,
+ default: Infinity,
+ },
+ },
+ computed: {
+ status() {
+ return this.job && this.job.status ? this.job.status : {};
+ },
+
+ tooltipText() {
+ const textBuilder = [];
+ const { name: jobName } = this.job;
+
+ if (jobName) {
+ textBuilder.push(jobName);
+ }
+
+ const { tooltip: statusTooltip } = this.status;
+ if (jobName && statusTooltip) {
+ textBuilder.push('-');
+ }
+
+ if (statusTooltip) {
+ if (this.isDelayedJob) {
+ textBuilder.push(sprintf(statusTooltip, { remainingTime: this.remainingTime }));
+ } else {
+ textBuilder.push(statusTooltip);
+ }
+ }
+
+ return textBuilder.join(' ');
+ },
+
+ tooltipBoundary() {
+ return this.dropdownLength < 5 ? 'viewport' : null;
+ },
+ /**
+ * Verifies if the provided job has an action path
+ *
+ * @return {Boolean}
+ */
+ hasAction() {
+ return this.job.status && this.job.status.action && this.job.status.action.path;
+ },
+ },
+ methods: {
+ pipelineActionRequestComplete() {
+ this.$emit('pipelineActionRequestComplete');
+ },
+ },
+};
+</script>
+<template>
+ <div class="ci-job-component">
+ <gl-link
+ v-if="status.has_details"
+ v-gl-tooltip="{ boundary: tooltipBoundary }"
+ :href="status.details_path"
+ :title="tooltipText"
+ :class="cssClassJobName"
+ class="js-pipeline-graph-job-link"
+ >
+ <job-name-component
+ :name="job.name"
+ :status="job.status"
+ />
+ </gl-link>
+
+ <div
+ v-else
+ v-gl-tooltip
+ :title="tooltipText"
+ :class="cssClassJobName"
+ class="js-job-component-tooltip non-details-job-component"
+ >
+
+ <job-name-component
+ :name="job.name"
+ :status="job.status"
+ />
+ </div>
+
+ <action-component
+ v-if="hasAction"
+ :tooltip-text="status.action.title"
+ :link="status.action.path"
+ :action-icon="status.action.icon"
+ @pipelineActionRequestComplete="pipelineActionRequestComplete"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
index e7b2de52f76..d5f931943d5 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -1,50 +1,40 @@
<script>
import _ from 'underscore';
-import JobComponent from './job_component.vue';
-import DropdownJobComponent from './dropdown_job_component.vue';
+import JobItem from './job_item.vue';
+import JobGroupDropdown from './job_group_dropdown.vue';
export default {
components: {
- JobComponent,
- DropdownJobComponent,
+ JobItem,
+ JobGroupDropdown,
},
props: {
title: {
type: String,
required: true,
},
-
- jobs: {
+ groups: {
type: Array,
required: true,
},
-
isFirstColumn: {
type: Boolean,
required: false,
default: false,
},
-
stageConnectorClass: {
type: String,
required: false,
default: '',
},
},
-
methods: {
- firstJob(list) {
- return list[0];
+ groupId(group) {
+ return `ci-badge-${_.escape(group.name)}`;
},
-
- jobId(job) {
- return `ci-badge-${_.escape(job.name)}`;
- },
-
buildConnnectorClass(index) {
return index === 0 && !this.isFirstColumn ? 'left-connector' : '';
},
-
pipelineActionRequestComplete() {
this.$emit('refreshPipelineGraph');
},
@@ -54,35 +44,35 @@ export default {
<template>
<li
:class="stageConnectorClass"
- class="stage-column">
+ class="stage-column"
+ >
<div class="stage-name">
{{ title }}
</div>
<div class="builds-container">
<ul>
<li
- v-for="(job, index) in jobs"
- :key="job.id"
+ v-for="(group, index) in groups"
+ :id="groupId(group)"
+ :key="group.id"
:class="buildConnnectorClass(index)"
- :id="jobId(job)"
class="build"
>
<div class="curve"></div>
- <job-component
- v-if="job.size === 1"
- :job="job"
+ <job-item
+ v-if="group.size === 1"
+ :job="group.jobs[0]"
css-class-job-name="build-content"
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
- <dropdown-job-component
- v-if="job.size > 1"
- :job="job"
+ <job-group-dropdown
+ v-if="group.size > 1"
+ :group="group"
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
-
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index 001eaeaa065..ac7fa4b195e 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -1,13 +1,13 @@
<script>
+import { GlLoadingIcon } from '@gitlab/ui';
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
import eventHub from '../event_hub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
name: 'PipelineHeaderSection',
components: {
ciHeader,
- loadingIcon,
+ GlLoadingIcon,
},
props: {
pipeline: {
@@ -89,9 +89,9 @@ export default {
item-name="Pipeline"
@actionClicked="postAction"
/>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
- size="2"
+ :size="2"
class="prepend-top-default append-bottom-default"
/>
</div>
diff --git a/app/assets/javascripts/pipelines/components/nav_controls.vue b/app/assets/javascripts/pipelines/components/nav_controls.vue
index 9501afb7493..5104fe36b42 100644
--- a/app/assets/javascripts/pipelines/components/nav_controls.vue
+++ b/app/assets/javascripts/pipelines/components/nav_controls.vue
@@ -1,10 +1,13 @@
<script>
+import { GlLink, GlButton } from '@gitlab/ui';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
export default {
name: 'PipelineNavControls',
components: {
LoadingButton,
+ GlLink,
+ GlButton,
},
props: {
newPipelinePath: {
@@ -40,28 +43,29 @@ export default {
</script>
<template>
<div class="nav-controls">
- <a
+ <gl-button
v-if="newPipelinePath"
:href="newPipelinePath"
- class="btn btn-create js-run-pipeline"
+ variant="success"
+ class="js-run-pipeline"
>
{{ s__('Pipelines|Run Pipeline') }}
- </a>
+ </gl-button>
<loading-button
v-if="resetCachePath"
:loading="isResetCacheButtonLoading"
:label="s__('Pipelines|Clear Runner Caches')"
- class="btn btn-default js-clear-cache"
+ class="js-clear-cache"
@click="onClickResetCache"
/>
- <a
+ <gl-button
v-if="ciLintPath"
:href="ciLintPath"
- class="btn btn-default js-ci-lint"
+ class="js-ci-lint"
>
{{ s__('Pipelines|CI Lint') }}
- </a>
+ </gl-button>
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue
index 75db1e9ae7c..249f7b9f368 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue
@@ -1,14 +1,15 @@
<script>
-import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
-import tooltip from '../../vue_shared/directives/tooltip';
-import popover from '../../vue_shared/directives/popover';
+import { GlLink, GlTooltipDirective } from '@gitlab/ui';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import popover from '~/vue_shared/directives/popover';
export default {
components: {
- userAvatarLink,
+ UserAvatarLink,
+ GlLink,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
popover,
},
props: {
@@ -47,11 +48,12 @@ export default {
</script>
<template>
<div class="table-section section-15 d-none d-sm-none d-md-block pipeline-tags">
- <a
+ <gl-link
:href="pipeline.path"
- class="js-pipeline-url-link">
+ class="js-pipeline-url-link"
+ >
<span class="pipeline-id">#{{ pipeline.id }}</span>
- </a>
+ </gl-link>
<span>by</span>
<user-avatar-link
v-if="user"
@@ -67,37 +69,42 @@ export default {
</span>
<div class="label-container">
<span
- v-tooltip
v-if="pipeline.flags.latest"
+ v-gl-tooltip
class="js-pipeline-url-latest badge badge-success"
- title="Latest pipeline for this branch">
+ title="Latest pipeline for this branch"
+ >
latest
</span>
<span
- v-tooltip
v-if="pipeline.flags.yaml_errors"
+ v-gl-tooltip
:title="pipeline.yaml_errors"
- class="js-pipeline-url-yaml badge badge-danger">
+ class="js-pipeline-url-yaml badge badge-danger"
+ >
yaml invalid
</span>
<span
- v-tooltip
v-if="pipeline.flags.failure_reason"
+ v-gl-tooltip
:title="pipeline.failure_reason"
- class="js-pipeline-url-failure badge badge-danger">
+ class="js-pipeline-url-failure badge badge-danger"
+ >
error
</span>
- <a
- v-popover="popoverOptions"
+ <gl-link
v-if="pipeline.flags.auto_devops"
+ v-popover="popoverOptions"
tabindex="0"
class="js-pipeline-url-autodevops badge badge-info autodevops-badge"
- role="button">
+ role="button"
+ >
Auto DevOps
- </a>
+ </gl-link>
<span
v-if="pipeline.flags.stuck"
- class="js-pipeline-url-stuck badge badge-warning">
+ class="js-pipeline-url-stuck badge badge-warning"
+ >
stuck
</span>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index c9d2dc3a3c5..fcd8a54c9c1 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -155,14 +155,6 @@ export default {
);
},
- shouldRenderPagination() {
- return (
- !this.isLoading &&
- this.state.pipelines.length &&
- this.state.pageInfo.total > this.state.pageInfo.perPage
- );
- },
-
emptyTabMessage() {
const { scopes } = this.$options;
const possibleScopes = [scopes.pending, scopes.running, scopes.finished];
@@ -232,36 +224,6 @@ export default {
this.setCommonData(resp.data.pipelines);
}
},
- /**
- * Handles URL and query parameter changes.
- * When the user uses the pagination or the tabs,
- * - update URL
- * - Make API request to the server with new parameters
- * - Update the polling function
- * - Update the internal state
- */
- updateContent(parameters) {
- this.updateInternalState(parameters);
-
- // fetch new data
- return this.service
- .getPipelines(this.requestData)
- .then(response => {
- this.isLoading = false;
- this.successCallback(response);
-
- // restart polling
- this.poll.restart({ data: this.requestData });
- })
- .catch(() => {
- this.isLoading = false;
- this.errorCallback();
-
- // restart polling
- this.poll.restart({ data: this.requestData });
- });
- },
-
handleResetRunnersCache(endpoint) {
this.isResetCacheButtonLoading = true;
@@ -319,10 +281,10 @@ export default {
<div class="content-list pipelines">
- <loading-icon
+ <gl-loading-icon
v-if="stateToRender === $options.stateMap.loading"
:label="s__('Pipelines|Loading Pipelines')"
- size="3"
+ :size="3"
class="prepend-top-20"
/>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index 1c8d7303c52..112c1559f25 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -1,16 +1,19 @@
<script>
+import { GlButton, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
+import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
import eventHub from '../event_hub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-import icon from '../../vue_shared/components/icon.vue';
-import tooltip from '../../vue_shared/directives/tooltip';
+import Icon from '../../vue_shared/components/icon.vue';
export default {
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
components: {
- loadingIcon,
- icon,
+ Icon,
+ GlCountdown,
+ GlButton,
+ GlLoadingIcon,
},
props: {
actions: {
@@ -24,10 +27,24 @@ export default {
};
},
methods: {
- onClickAction(endpoint) {
+ onClickAction(action) {
+ if (action.scheduled_at) {
+ const confirmationMessage = sprintf(
+ s__(
+ "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes.",
+ ),
+ { jobName: action.name },
+ );
+ // https://gitlab.com/gitlab-org/gitlab-ce/issues/52156
+ // eslint-disable-next-line no-alert
+ if (!window.confirm(confirmationMessage)) {
+ return;
+ }
+ }
+
this.isLoading = true;
- eventHub.$emit('postAction', endpoint);
+ eventHub.$emit('postAction', action.path);
},
isActionDisabled(action) {
@@ -42,14 +59,12 @@ export default {
</script>
<template>
<div class="btn-group">
- <button
- v-tooltip
+ <gl-button
+ v-gl-tooltip
:disabled="isLoading"
- type="button"
class="dropdown-new btn btn-default js-pipeline-dropdown-manual-actions"
title="Manual job"
data-toggle="dropdown"
- data-placement="top"
aria-label="Manual job"
>
<icon
@@ -60,23 +75,29 @@ export default {
class="fa fa-caret-down"
aria-hidden="true">
</i>
- <loading-icon v-if="isLoading" />
- </button>
+ <gl-loading-icon v-if="isLoading" />
+ </gl-button>
<ul class="dropdown-menu dropdown-menu-right">
<li
- v-for="(action, i) in actions"
- :key="i"
+ v-for="action in actions"
+ :key="action.path"
>
- <button
+ <gl-button
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
- type="button"
class="js-pipeline-action-link no-btn btn"
- @click="onClickAction(action.path)"
+ @click="onClickAction(action)"
>
{{ action.name }}
- </button>
+ <span
+ v-if="action.scheduled_at"
+ class="pull-right"
+ >
+ <icon name="clock" />
+ <gl-countdown :end-date-string="action.scheduled_at" />
+ </span>
+ </gl-button>
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
index d40de95e051..d7d9eb00faa 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
@@ -1,13 +1,15 @@
<script>
-import tooltip from '../../vue_shared/directives/tooltip';
-import icon from '../../vue_shared/components/icon.vue';
+import { GlLink, GlButton, GlTooltipDirective } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
export default {
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
components: {
- icon,
+ Icon,
+ GlLink,
+ GlButton,
},
props: {
artifacts: {
@@ -22,11 +24,10 @@ export default {
class="btn-group"
role="group"
>
- <button
- v-tooltip
- class="dropdown-toggle btn btn-default build-artifacts js-pipeline-dropdown-download"
+ <gl-button
+ v-gl-tooltip
+ class="dropdown-toggle build-artifacts js-pipeline-dropdown-download"
title="Artifacts"
- data-placement="top"
data-toggle="dropdown"
aria-label="Artifacts"
>
@@ -36,18 +37,19 @@ export default {
aria-hidden="true"
>
</i>
- </button>
+ </gl-button>
<ul class="dropdown-menu dropdown-menu-right">
<li
v-for="(artifact, i) in artifacts"
- :key="i">
- <a
+ :key="i"
+ >
+ <gl-link
:href="artifact.path"
rel="nofollow"
download
>
Download {{ artifact.name }} artifacts
- </a>
+ </gl-link>
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
index 0d7324f3fb5..3339b5c13ed 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -35,7 +35,7 @@ export default {
},
data() {
return {
- pipelineId: '',
+ pipelineId: 0,
endpoint: '',
cancelingPipeline: null,
};
@@ -88,25 +88,25 @@ export default {
class="table-section section-10 js-pipeline-status pipeline-status"
role="rowheader"
>
- Status
+ {{ s__('Pipeline|Status') }}
</div>
<div
class="table-section section-15 js-pipeline-info pipeline-info"
role="rowheader"
>
- Pipeline
+ {{ s__('Pipeline|Pipeline') }}
</div>
<div
class="table-section section-20 js-pipeline-commit pipeline-commit"
role="rowheader"
>
- Commit
+ {{ s__('Pipeline|Commit') }}
</div>
<div
class="table-section section-20 js-pipeline-stages pipeline-stages"
role="rowheader"
>
- Stages
+ {{ s__('Pipeline|Stages') }}
</div>
</div>
<pipelines-table-row-component
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index 804822a3ea8..fd674a8d447 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -47,7 +47,7 @@ export default {
required: true,
},
cancelingPipeline: {
- type: String,
+ type: Number,
required: false,
default: null,
},
@@ -59,6 +59,13 @@ export default {
};
},
computed: {
+ actions() {
+ if (!this.pipeline || !this.pipeline.details) {
+ return [];
+ }
+ const { details } = this.pipeline;
+ return [...(details.manual_actions || []), ...(details.scheduled_actions || [])];
+ },
/**
* If provided, returns the commit tag.
* Needed to render the commit component column.
@@ -132,10 +139,8 @@ export default {
if (this.pipeline.ref) {
return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => {
if (prop === 'path') {
- // eslint-disable-next-line no-param-reassign
accumulator.ref_url = this.pipeline.ref[prop];
} else {
- // eslint-disable-next-line no-param-reassign
accumulator[prop] = this.pipeline.ref[prop];
}
return accumulator;
@@ -256,7 +261,7 @@ export default {
class="table-mobile-header"
role="rowheader"
>
- Status
+ {{ s__('Pipeline|Status') }}
</div>
<div class="table-mobile-content">
<ci-badge
@@ -274,8 +279,9 @@ export default {
<div class="table-section section-20">
<div
class="table-mobile-header"
- role="rowheader">
- Commit
+ role="rowheader"
+ >
+ {{ s__('Pipeline|Commit') }}
</div>
<div class="table-mobile-content">
<commit-component
@@ -293,15 +299,17 @@ export default {
<div class="table-section section-wrap section-20 stage-cell">
<div
class="table-mobile-header"
- role="rowheader">
- Stages
+ role="rowheader"
+ >
+ {{ s__('Pipeline|Stages') }}
</div>
<div class="table-mobile-content">
<template v-if="pipeline.details.stages.length > 0">
<div
v-for="(stage, index) in pipeline.details.stages"
:key="index"
- class="stage-container dropdown js-mini-pipeline-graph">
+ class="stage-container dropdown js-mini-pipeline-graph"
+ >
<pipeline-stage
:type="$options.pipelinesTable"
:stage="stage"
@@ -323,14 +331,14 @@ export default {
>
<div class="btn-group table-action-buttons">
<pipelines-actions-component
- v-if="pipeline.details.manual_actions.length"
- :actions="pipeline.details.manual_actions"
+ v-if="actions.length > 0"
+ :actions="actions"
/>
<pipelines-artifacts-component
v-if="pipeline.details.artifacts.length"
:artifacts="pipeline.details.artifacts"
- class="d-none d-sm-none d-md-block"
+ class="d-md-block"
/>
<loading-button
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index c7df69c69ed..1d62472671a 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -13,25 +13,24 @@
*/
import $ from 'jquery';
+import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '../../locale';
import Flash from '../../flash';
import axios from '../../lib/utils/axios_utils';
import eventHub from '../event_hub';
import Icon from '../../vue_shared/components/icon.vue';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
-import JobComponent from './graph/job_component.vue';
-import tooltip from '../../vue_shared/directives/tooltip';
+import JobItem from './graph/job_item.vue';
import { PIPELINES_TABLE } from '../constants';
export default {
components: {
- LoadingIcon,
Icon,
- JobComponent,
+ JobItem,
+ GlLoadingIcon,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
@@ -157,13 +156,12 @@ export default {
<template>
<div class="dropdown">
<button
- v-tooltip
id="stageDropdown"
ref="dropdown"
+ v-gl-tooltip.hover
:class="triggerButtonClass"
:title="stage.title"
class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
- data-placement="top"
data-toggle="dropdown"
data-display="static"
type="button"
@@ -191,7 +189,7 @@ export default {
class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"
aria-labelledby="stageDropdown"
>
- <loading-icon v-if="isLoading"/>
+ <gl-loading-icon v-if="isLoading"/>
<ul
v-else
class="js-builds-dropdown-list scrollable-menu"
@@ -200,7 +198,7 @@ export default {
v-for="job in dropdownContent"
:key="job.id"
>
- <job-component
+ <job-item
:dropdown-length="dropdownContent.length"
:job="job"
css-class-job-name="mini-pipeline-graph-dropdown-item"
diff --git a/app/assets/javascripts/pipelines/components/time_ago.vue b/app/assets/javascripts/pipelines/components/time_ago.vue
index cd43d78de40..bed690200b8 100644
--- a/app/assets/javascripts/pipelines/components/time_ago.vue
+++ b/app/assets/javascripts/pipelines/components/time_ago.vue
@@ -60,7 +60,7 @@ export default {
class="table-mobile-header"
role="rowheader"
>
- Duration
+ {{ s__('Pipeline|Duration') }}
</div>
<div class="table-mobile-content">
<p
@@ -87,7 +87,8 @@ export default {
v-tooltip
:title="tooltipTitle(finishedTime)"
data-placement="top"
- data-container="body">
+ data-container="body"
+ >
{{ timeFormated(finishedTime) }}
</time>
</p>
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js
index 2cb558b0dec..32bfa47e5f2 100644
--- a/app/assets/javascripts/pipelines/mixins/pipelines.js
+++ b/app/assets/javascripts/pipelines/mixins/pipelines.js
@@ -1,10 +1,10 @@
import Visibility from 'visibilityjs';
+import { GlLoadingIcon } from '@gitlab/ui';
import { __ } from '../../locale';
import Flash from '../../flash';
import Poll from '../../lib/utils/poll';
import EmptyState from '../components/empty_state.vue';
import SvgBlankState from '../components/blank_state.vue';
-import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
import PipelinesTableComponent from '../components/pipelines_table.vue';
import eventHub from '../event_hub';
import { CANCEL_REQUEST } from '../constants';
@@ -14,7 +14,7 @@ export default {
PipelinesTableComponent,
SvgBlankState,
EmptyState,
- LoadingIcon,
+ GlLoadingIcon,
},
data() {
return {
@@ -25,6 +25,15 @@ export default {
hasMadeRequest: false,
};
},
+ computed: {
+ shouldRenderPagination() {
+ return (
+ !this.isLoading &&
+ this.state.pipelines.length &&
+ this.state.pageInfo.total > this.state.pageInfo.perPage
+ );
+ },
+ },
beforeMount() {
this.poll = new Poll({
resource: this.service,
@@ -67,6 +76,35 @@ export default {
this.poll.stop();
},
methods: {
+ /**
+ * Handles URL and query parameter changes.
+ * When the user uses the pagination or the tabs,
+ * - update URL
+ * - Make API request to the server with new parameters
+ * - Update the polling function
+ * - Update the internal state
+ */
+ updateContent(parameters) {
+ this.updateInternalState(parameters);
+
+ // fetch new data
+ return this.service
+ .getPipelines(this.requestData)
+ .then(response => {
+ this.isLoading = false;
+ this.successCallback(response);
+
+ // restart polling
+ this.poll.restart({ data: this.requestData });
+ })
+ .catch(() => {
+ this.isLoading = false;
+ this.errorCallback();
+
+ // restart polling
+ this.poll.restart({ data: this.requestData });
+ });
+ },
updateTable() {
// Cancel ongoing request
if (this.isMakingRequest) {
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
deleted file mode 100644
index 0964baf8954..00000000000
--- a/app/assets/javascripts/preview_markdown.js
+++ /dev/null
@@ -1,211 +0,0 @@
-/* eslint-disable func-names, no-var, object-shorthand, prefer-arrow-callback */
-
-import $ from 'jquery';
-import axios from '~/lib/utils/axios_utils';
-import flash from '~/flash';
-import { __ } from '~/locale';
-
-// MarkdownPreview
-//
-// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview
-// (including the explanation of quick actions), and showing a warning when
-// more than `x` users are referenced.
-//
-
-var lastTextareaPreviewed;
-var lastTextareaHeight = null;
-var markdownPreview;
-var previewButtonSelector;
-var writeButtonSelector;
-
-function MarkdownPreview() {}
-
-// Minimum number of users referenced before triggering a warning
-MarkdownPreview.prototype.referenceThreshold = 10;
-MarkdownPreview.prototype.emptyMessage = 'Nothing to preview.';
-
-MarkdownPreview.prototype.ajaxCache = {};
-
-MarkdownPreview.prototype.showPreview = function ($form) {
- var mdText;
- var markdownVersion;
- var url;
- var preview = $form.find('.js-md-preview');
- if (preview.hasClass('md-preview-loading')) {
- return;
- }
-
- mdText = $form.find('textarea.markdown-area').val();
- markdownVersion = $form.attr('data-markdown-version');
- url = this.versionedPreviewPath(preview.data('url'), markdownVersion);
-
- if (mdText.trim().length === 0) {
- preview.text(this.emptyMessage);
- this.hideReferencedUsers($form);
- } else {
- preview.addClass('md-preview-loading').text('Loading...');
- this.fetchMarkdownPreview(mdText, url, (function (response) {
- var body;
- if (response.body.length > 0) {
- ({ body } = response);
- } else {
- body = this.emptyMessage;
- }
-
- preview.removeClass('md-preview-loading').html(body);
- preview.renderGFM();
- this.renderReferencedUsers(response.references.users, $form);
-
- if (response.references.commands) {
- this.renderReferencedCommands(response.references.commands, $form);
- }
- }).bind(this));
- }
-};
-
-MarkdownPreview.prototype.versionedPreviewPath = function (markdownPreviewPath, markdownVersion) {
- if (typeof markdownVersion === 'undefined') {
- return markdownPreviewPath;
- }
-
- return `${markdownPreviewPath}${markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'}markdown_version=${markdownVersion}`;
-};
-
-MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) {
- if (!url) {
- return;
- }
- if (text === this.ajaxCache.text) {
- success(this.ajaxCache.response);
- return;
- }
- axios.post(url, {
- text,
- })
- .then(({ data }) => {
- this.ajaxCache = {
- text: text,
- response: data,
- };
- success(data);
- })
- .catch(() => flash(__('An error occurred while fetching markdown preview')));
-};
-
-MarkdownPreview.prototype.hideReferencedUsers = function ($form) {
- $form.find('.referenced-users').hide();
-};
-
-MarkdownPreview.prototype.renderReferencedUsers = function (users, $form) {
- var referencedUsers;
- referencedUsers = $form.find('.referenced-users');
- if (referencedUsers.length) {
- if (users.length >= this.referenceThreshold) {
- referencedUsers.show();
- referencedUsers.find('.js-referenced-users-count').text(users.length);
- } else {
- referencedUsers.hide();
- }
- }
-};
-
-MarkdownPreview.prototype.hideReferencedCommands = function ($form) {
- $form.find('.referenced-commands').hide();
-};
-
-MarkdownPreview.prototype.renderReferencedCommands = function (commands, $form) {
- var referencedCommands;
- referencedCommands = $form.find('.referenced-commands');
- if (commands.length > 0) {
- referencedCommands.html(commands);
- referencedCommands.show();
- } else {
- referencedCommands.html('');
- referencedCommands.hide();
- }
-};
-
-markdownPreview = new MarkdownPreview();
-
-previewButtonSelector = '.js-md-preview-button';
-writeButtonSelector = '.js-md-write-button';
-lastTextareaPreviewed = null;
-const markdownToolbar = $('.md-header-toolbar');
-
-$.fn.setupMarkdownPreview = function () {
- var $form = $(this);
- $form.find('textarea.markdown-area').on('input', function () {
- markdownPreview.hideReferencedUsers($form);
- });
-};
-
-$(document).on('markdown-preview:show', function (e, $form) {
- if (!$form) {
- return;
- }
-
- lastTextareaPreviewed = $form.find('textarea.markdown-area');
- lastTextareaHeight = lastTextareaPreviewed.height();
-
- // toggle tabs
- $form.find(writeButtonSelector).parent().removeClass('active');
- $form.find(previewButtonSelector).parent().addClass('active');
-
- // toggle content
- $form.find('.md-write-holder').hide();
- $form.find('.md-preview-holder').show();
- markdownToolbar.removeClass('active');
- markdownPreview.showPreview($form);
-});
-
-$(document).on('markdown-preview:hide', function (e, $form) {
- if (!$form) {
- return;
- }
- lastTextareaPreviewed = null;
-
- if (lastTextareaHeight) {
- $form.find('textarea.markdown-area').height(lastTextareaHeight);
- }
-
- // toggle tabs
- $form.find(writeButtonSelector).parent().addClass('active');
- $form.find(previewButtonSelector).parent().removeClass('active');
-
- // toggle content
- $form.find('.md-write-holder').show();
- $form.find('textarea.markdown-area').focus();
- $form.find('.md-preview-holder').hide();
- markdownToolbar.addClass('active');
-
- markdownPreview.hideReferencedCommands($form);
-});
-
-$(document).on('markdown-preview:toggle', function (e, keyboardEvent) {
- var $target;
- $target = $(keyboardEvent.target);
- if ($target.is('textarea.markdown-area')) {
- $(document).triggerHandler('markdown-preview:show', [$target.closest('form')]);
- keyboardEvent.preventDefault();
- } else if (lastTextareaPreviewed) {
- $target = lastTextareaPreviewed;
- $(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]);
- keyboardEvent.preventDefault();
- }
-});
-
-$(document).on('click', previewButtonSelector, function (e) {
- var $form;
- e.preventDefault();
- $form = $(this).closest('form');
- $(document).triggerHandler('markdown-preview:show', [$form]);
-});
-
-$(document).on('click', writeButtonSelector, function (e) {
- var $form;
- e.preventDefault();
- $form = $(this).closest('form');
- $(document).triggerHandler('markdown-preview:hide', [$form]);
-});
-
-export default MarkdownPreview;
diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
index 974629fa2af..99b57f4c9d5 100644
--- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue
+++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
@@ -1,78 +1,78 @@
<script>
- import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
- import { __, s__, sprintf } from '~/locale';
- import csrf from '~/lib/utils/csrf';
+import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import { __, s__, sprintf } from '~/locale';
+import csrf from '~/lib/utils/csrf';
- export default {
- components: {
- DeprecatedModal,
+export default {
+ components: {
+ DeprecatedModal,
+ },
+ props: {
+ actionUrl: {
+ type: String,
+ required: true,
},
- props: {
- actionUrl: {
- type: String,
- required: true,
- },
- confirmWithPassword: {
- type: Boolean,
- required: true,
- },
- username: {
- type: String,
- required: true,
- },
+ confirmWithPassword: {
+ type: Boolean,
+ required: true,
},
- data() {
- return {
- enteredPassword: '',
- enteredUsername: '',
- };
+ username: {
+ type: String,
+ required: true,
},
- computed: {
- csrfToken() {
- return csrf.token;
- },
- inputLabel() {
- let confirmationValue;
- if (this.confirmWithPassword) {
- confirmationValue = __('password');
- } else {
- confirmationValue = __('username');
- }
+ },
+ data() {
+ return {
+ enteredPassword: '',
+ enteredUsername: '',
+ };
+ },
+ computed: {
+ csrfToken() {
+ return csrf.token;
+ },
+ inputLabel() {
+ let confirmationValue;
+ if (this.confirmWithPassword) {
+ confirmationValue = __('password');
+ } else {
+ confirmationValue = __('username');
+ }
- confirmationValue = `<code>${confirmationValue}</code>`;
+ confirmationValue = `<code>${confirmationValue}</code>`;
- return sprintf(
- s__('Profiles|Type your %{confirmationValue} to confirm:'),
- { confirmationValue },
- false,
- );
- },
- text() {
- return sprintf(
- s__(`Profiles|
+ return sprintf(
+ s__('Profiles|Type your %{confirmationValue} to confirm:'),
+ { confirmationValue },
+ false,
+ );
+ },
+ text() {
+ return sprintf(
+ s__(`Profiles|
You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account.
Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
- {
- yourAccount: `<strong>${s__('Profiles|your account')}</strong>`,
- deleteAccount: `<strong>${s__('Profiles|Delete Account')}</strong>`,
- },
- false,
- );
- },
+ {
+ yourAccount: `<strong>${s__('Profiles|your account')}</strong>`,
+ deleteAccount: `<strong>${s__('Profiles|Delete Account')}</strong>`,
+ },
+ false,
+ );
},
- methods: {
- canSubmit() {
- if (this.confirmWithPassword) {
- return this.enteredPassword !== '';
- }
+ },
+ methods: {
+ canSubmit() {
+ if (this.confirmWithPassword) {
+ return this.enteredPassword !== '';
+ }
- return this.enteredUsername === this.username;
- },
- onSubmit() {
- this.$refs.form.submit();
- },
+ return this.enteredUsername === this.username;
+ },
+ onSubmit() {
+ this.$refs.form.submit();
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js
index f641b23e519..befe91c332f 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js
@@ -1,23 +1,38 @@
-/* eslint-disable no-useless-escape, max-len, no-var, no-underscore-dangle, func-names, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */
+/* eslint-disable no-useless-escape, no-var, no-underscore-dangle, func-names, no-unused-vars, no-return-assign, object-shorthand, one-var, consistent-return, class-methods-use-this */
import $ from 'jquery';
import 'cropper';
import _ from 'underscore';
-((global) => {
+(global => {
// Matches everything but the file name
const FILENAMEREGEX = /^.*[\\\/]/;
class GitLabCrop {
- constructor(input, { filename, previewImage, modalCrop, pickImageEl, uploadImageBtn, modalCropImg,
- exportWidth = 200, exportHeight = 200, cropBoxWidth = 200, cropBoxHeight = 200 } = {}) {
+ constructor(
+ input,
+ {
+ filename,
+ previewImage,
+ modalCrop,
+ pickImageEl,
+ uploadImageBtn,
+ modalCropImg,
+ exportWidth = 200,
+ exportHeight = 200,
+ cropBoxWidth = 200,
+ cropBoxHeight = 200,
+ } = {},
+ ) {
this.onUploadImageBtnClick = this.onUploadImageBtnClick.bind(this);
this.onModalHide = this.onModalHide.bind(this);
this.onModalShow = this.onModalShow.bind(this);
this.onPickImageClick = this.onPickImageClick.bind(this);
this.fileInput = $(input);
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
- this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `${this.fileInput.attr('id')}-trigger`);
+ this.fileInput
+ .attr('name', `${this.fileInput.attr('name')}-trigger`)
+ .attr('id', `${this.fileInput.attr('id')}-trigger`);
this.exportWidth = exportWidth;
this.exportHeight = exportHeight;
this.cropBoxWidth = cropBoxWidth;
@@ -59,7 +74,7 @@ import _ from 'underscore';
btn = this;
return _this.onActionBtnClick(btn);
});
- return this.croppedImageBlob = null;
+ return (this.croppedImageBlob = null);
}
onPickImageClick() {
@@ -94,9 +109,9 @@ import _ from 'underscore';
width: cropBoxWidth,
height: cropBoxHeight,
left: (container.width - cropBoxWidth) / 2,
- top: (container.height - cropBoxHeight) / 2
+ top: (container.height - cropBoxHeight) / 2,
});
- }
+ },
});
}
@@ -116,7 +131,7 @@ import _ from 'underscore';
var data, result;
data = $(btn).data();
if (this.modalCropImg.data('cropper') && data.method) {
- return result = this.modalCropImg.cropper(data.method, data.option);
+ return (result = this.modalCropImg.cropper(data.method, data.option));
}
}
@@ -127,7 +142,7 @@ import _ from 'underscore';
readFile(input) {
var _this, reader;
_this = this;
- reader = new FileReader;
+ reader = new FileReader();
reader.onload = () => {
_this.modalCropImg.attr('src', reader.result);
return _this.modalCrop.modal('show');
@@ -145,7 +160,7 @@ import _ from 'underscore';
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
- type: 'image/png'
+ type: 'image/png',
});
}
@@ -157,11 +172,13 @@ import _ from 'underscore';
}
setBlob() {
- this.dataURL = this.modalCropImg.cropper('getCroppedCanvas', {
- width: 200,
- height: 200
- }).toDataURL('image/png');
- return this.croppedImageBlob = this.dataURLtoBlob(this.dataURL);
+ this.dataURL = this.modalCropImg
+ .cropper('getCroppedCanvas', {
+ width: 200,
+ height: 200,
+ })
+ .toDataURL('image/png');
+ return (this.croppedImageBlob = this.dataURLtoBlob(this.dataURL));
}
getBlob() {
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
index e49c67ffb5c..8704a655b28 100644
--- a/app/assets/javascripts/profile/profile.js
+++ b/app/assets/javascripts/profile/profile.js
@@ -26,11 +26,7 @@ export default class Profile {
}
bindEvents() {
- $('.js-preferences-form').on(
- 'change.preference',
- 'input[type=radio]',
- this.submitForm,
- );
+ $('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
$('#user_notification_email').on('change', this.submitForm);
$('#user_notified_of_own_activity').on('change', this.submitForm);
this.form.on('submit', this.onSubmitForm);
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 05485e352dc..60d3d83a4b2 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, prefer-template, no-unused-vars, no-return-assign */
+/* eslint-disable func-names, no-var, consistent-return, one-var, no-cond-assign, prefer-template, no-unused-vars, no-return-assign */
import $ from 'jquery';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
@@ -10,14 +10,14 @@ import { __ } from '~/locale';
const highlighter = function(element, text, matches) {
var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
lastIndex = 0;
- highlightText = "";
+ highlightText = '';
matchedChars = [];
for (j = 0, len = matches.length; j < len; j += 1) {
matchIndex = matches[j];
unmatched = text.substring(lastIndex, matchIndex);
if (unmatched) {
if (matchedChars.length) {
- element.append(matchedChars.join("").bold());
+ element.append(matchedChars.join('').bold());
}
matchedChars = [];
element.append(document.createTextNode(unmatched));
@@ -26,7 +26,7 @@ const highlighter = function(element, text, matches) {
lastIndex = matchIndex + 1;
}
if (matchedChars.length) {
- element.append(matchedChars.join("").bold());
+ element.append(matchedChars.join('').bold());
}
return element.append(document.createTextNode(text.substring(lastIndex)));
};
@@ -40,7 +40,7 @@ export default class ProjectFindFile {
this.selectRowDown = this.selectRowDown.bind(this);
this.selectRowUp = this.selectRowUp.bind(this);
this.filePaths = {};
- this.inputElement = this.element.find(".file-finder-input");
+ this.inputElement = this.element.find('.file-finder-input');
// init event
this.initEvent();
// focus text input box
@@ -50,38 +50,51 @@ export default class ProjectFindFile {
}
initEvent() {
- this.inputElement.off("keyup");
- this.inputElement.on("keyup", (function(_this) {
- return function(event) {
- var oldValue, ref, target, value;
- target = $(event.target);
- value = target.val();
- oldValue = (ref = target.data("oldValue")) != null ? ref : "";
- if (value !== oldValue) {
- target.data("oldValue", value);
- _this.findFile();
- return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus();
- }
- };
- })(this));
+ this.inputElement.off('keyup');
+ this.inputElement.on(
+ 'keyup',
+ (function(_this) {
+ return function(event) {
+ var oldValue, ref, target, value;
+ target = $(event.target);
+ value = target.val();
+ oldValue = (ref = target.data('oldValue')) != null ? ref : '';
+ if (value !== oldValue) {
+ target.data('oldValue', value);
+ _this.findFile();
+ return _this.element
+ .find('tr.tree-item')
+ .eq(0)
+ .addClass('selected')
+ .focus();
+ }
+ };
+ })(this),
+ );
}
findFile() {
var result, searchText;
searchText = this.inputElement.val();
- result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
+ result =
+ searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
return this.renderList(result, searchText);
- // find file
+ // find file
}
// files pathes load
load(url) {
- axios.get(url)
+ axios
+ .get(url)
.then(({ data }) => {
this.element.find('.loading').hide();
this.filePaths = data;
this.findFile();
- this.element.find('.files-slider tr.tree-item').eq(0).addClass('selected').focus();
+ this.element
+ .find('.files-slider tr.tree-item')
+ .eq(0)
+ .addClass('selected')
+ .focus();
})
.catch(() => flash(__('An error occurred while loading filenames')));
}
@@ -89,7 +102,7 @@ export default class ProjectFindFile {
// render result
renderList(filePaths, searchText) {
var blobItemUrl, filePath, html, i, len, matches, results;
- this.element.find(".tree-table > tbody").empty();
+ this.element.find('.tree-table > tbody').empty();
results = [];
for (i = 0, len = filePaths.length; i < len; i += 1) {
@@ -100,9 +113,9 @@ export default class ProjectFindFile {
if (searchText) {
matches = fuzzaldrinPlus.match(filePath, searchText);
}
- blobItemUrl = this.options.blobUrlTemplate + "/" + filePath;
+ blobItemUrl = this.options.blobUrlTemplate + '/' + filePath;
html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl);
- results.push(this.element.find(".tree-table > tbody").append(html));
+ results.push(this.element.find('.tree-table > tbody').append(html));
}
return results;
}
@@ -110,52 +123,56 @@ export default class ProjectFindFile {
// make tbody row html
static makeHtml(filePath, matches, blobItemUrl) {
var $tr;
- $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>");
+ $tr = $(
+ "<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>",
+ );
if (matches) {
- $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl));
+ $tr
+ .find('a')
+ .replaceWith(highlighter($tr.find('a'), filePath, matches).attr('href', blobItemUrl));
} else {
- $tr.find("a").attr("href", blobItemUrl);
- $tr.find(".str-truncated").text(filePath);
+ $tr.find('a').attr('href', blobItemUrl);
+ $tr.find('.str-truncated').text(filePath);
}
return $tr;
}
selectRow(type) {
var next, rows, selectedRow;
- rows = this.element.find(".files-slider tr.tree-item");
- selectedRow = this.element.find(".files-slider tr.tree-item.selected");
+ rows = this.element.find('.files-slider tr.tree-item');
+ selectedRow = this.element.find('.files-slider tr.tree-item.selected');
if (rows && rows.length > 0) {
if (selectedRow && selectedRow.length > 0) {
- if (type === "UP") {
+ if (type === 'UP') {
next = selectedRow.prev();
- } else if (type === "DOWN") {
+ } else if (type === 'DOWN') {
next = selectedRow.next();
}
if (next.length > 0) {
- selectedRow.removeClass("selected");
+ selectedRow.removeClass('selected');
selectedRow = next;
}
} else {
selectedRow = rows.eq(0);
}
- return selectedRow.addClass("selected").focus();
+ return selectedRow.addClass('selected').focus();
}
}
selectRowUp() {
- return this.selectRow("UP");
+ return this.selectRow('UP');
}
selectRowDown() {
- return this.selectRow("DOWN");
+ return this.selectRow('DOWN');
}
goToTree() {
- return window.location.href = this.options.treeUrl;
+ return (window.location.href = this.options.treeUrl);
}
goToBlob() {
- var $link = this.element.find(".tree-item.selected .tree-item-file-name a");
+ var $link = this.element.find('.tree-item.selected .tree-item-file-name a');
if ($link.length) {
$link.get(0).click();
diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js
index 5a0d2b642eb..a51a2a2242f 100644
--- a/app/assets/javascripts/project_import.js
+++ b/app/assets/javascripts/project_import.js
@@ -5,4 +5,3 @@ export default function projectImport() {
visitUrl(window.location.href);
}, 5000);
}
-
diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js
index 9049f87e037..d3c604dcee1 100644
--- a/app/assets/javascripts/project_label_subscription.js
+++ b/app/assets/javascripts/project_label_subscription.js
@@ -31,32 +31,35 @@ export default class ProjectLabelSubscription {
$btn.addClass('disabled');
- axios.post(url).then(() => {
- let newStatus;
- let newAction;
+ axios
+ .post(url)
+ .then(() => {
+ let newStatus;
+ let newAction;
- if (oldStatus === 'unsubscribed') {
- [newStatus, newAction] = ['subscribed', 'Unsubscribe'];
- } else {
- [newStatus, newAction] = ['unsubscribed', 'Subscribe'];
- }
+ if (oldStatus === 'unsubscribed') {
+ [newStatus, newAction] = ['subscribed', 'Unsubscribe'];
+ } else {
+ [newStatus, newAction] = ['unsubscribed', 'Subscribe'];
+ }
- $btn.removeClass('disabled');
+ $btn.removeClass('disabled');
- this.$buttons.attr('data-status', newStatus);
- this.$buttons.find('> span').text(newAction);
+ this.$buttons.attr('data-status', newStatus);
+ this.$buttons.find('> span').text(newAction);
- this.$buttons.map((i, button) => {
- const $button = $(button);
- const originalTitle = $button.attr('data-original-title');
+ this.$buttons.map((i, button) => {
+ const $button = $(button);
+ const originalTitle = $button.attr('data-original-title');
- if (originalTitle) {
- ProjectLabelSubscription.setNewTitle($button, originalTitle, newStatus, newAction);
- }
+ if (originalTitle) {
+ ProjectLabelSubscription.setNewTitle($button, originalTitle, newStatus, newAction);
+ }
- return button;
- });
- }).catch(() => flash(__('There was an error subscribing to this label.')));
+ return button;
+ });
+ })
+ .catch(() => flash(__('There was an error subscribing to this label.')));
}
static setNewTitle($button, originalTitle, newStatus) {
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index bce7556bd40..f1fff173619 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-else-return, quotes, max-len */
+/* eslint-disable func-names, no-var, object-shorthand, one-var, no-else-return */
import $ from 'jquery';
import Api from './api';
@@ -14,29 +14,30 @@ export default function projectSelect() {
this.orderBy = $(select).data('orderBy') || 'id';
this.withIssuesEnabled = $(select).data('withIssuesEnabled');
this.withMergeRequestsEnabled = $(select).data('withMergeRequestsEnabled');
+ this.allowClear = $(select).data('allowClear') || false;
- placeholder = "Search for project";
+ placeholder = 'Search for project';
if (this.includeGroups) {
- placeholder += " or group";
+ placeholder += ' or group';
}
$(select).select2({
placeholder: placeholder,
minimumInputLength: 0,
- query: (function (_this) {
- return function (query) {
+ query: (function(_this) {
+ return function(query) {
var finalCallback, projectsCallback;
- finalCallback = function (projects) {
+ finalCallback = function(projects) {
var data;
data = {
- results: projects
+ results: projects,
};
return query.callback(data);
};
if (_this.includeGroups) {
- projectsCallback = function (projects) {
+ projectsCallback = function(projects) {
var groupsCallback;
- groupsCallback = function (groups) {
+ groupsCallback = function(groups) {
var data;
data = groups.concat(projects);
return finalCallback(data);
@@ -47,17 +48,26 @@ export default function projectSelect() {
projectsCallback = finalCallback;
}
if (_this.groupId) {
- return Api.groupProjects(_this.groupId, query.term, {
- with_issues_enabled: _this.withIssuesEnabled,
- with_merge_requests_enabled: _this.withMergeRequestsEnabled,
- }, projectsCallback);
+ return Api.groupProjects(
+ _this.groupId,
+ query.term,
+ {
+ with_issues_enabled: _this.withIssuesEnabled,
+ with_merge_requests_enabled: _this.withMergeRequestsEnabled,
+ },
+ projectsCallback,
+ );
} else {
- return Api.projects(query.term, {
- order_by: _this.orderBy,
- with_issues_enabled: _this.withIssuesEnabled,
- with_merge_requests_enabled: _this.withMergeRequestsEnabled,
- membership: !_this.allProjects,
- }, projectsCallback);
+ return Api.projects(
+ query.term,
+ {
+ order_by: _this.orderBy,
+ with_issues_enabled: _this.withIssuesEnabled,
+ with_merge_requests_enabled: _this.withMergeRequestsEnabled,
+ membership: !_this.allProjects,
+ },
+ projectsCallback,
+ );
}
};
})(this),
@@ -68,10 +78,17 @@ export default function projectSelect() {
url: project.web_url,
});
},
- text: function (project) {
+ text: function(project) {
return project.name_with_namespace || project.name;
},
- dropdownCssClass: "ajax-project-dropdown"
+
+ initSelection: function(el, callback) {
+ return Api.project(el.val()).then(({ data }) => callback(data));
+ },
+
+ allowClear: this.allowClear,
+
+ dropdownCssClass: 'ajax-project-dropdown',
});
if (simpleFilter) return select;
return new ProjectSelectComboButton(select);
diff --git a/app/assets/javascripts/project_select_combo_button.js b/app/assets/javascripts/project_select_combo_button.js
index 9b404896e86..3dbac3ff942 100644
--- a/app/assets/javascripts/project_select_combo_button.js
+++ b/app/assets/javascripts/project_select_combo_button.js
@@ -14,10 +14,11 @@ export default class ProjectSelectComboButton {
}
bindEvents() {
- this.projectSelectInput.siblings('.new-project-item-select-button')
+ this.projectSelectInput
+ .siblings('.new-project-item-select-button')
.on('click', e => this.openDropdown(e));
- this.newItemBtn.on('click', (e) => {
+ this.newItemBtn.on('click', e => {
if (!this.getProjectFromLocalStorage()) {
e.preventDefault();
this.openDropdown(e);
@@ -31,14 +32,21 @@ export default class ProjectSelectComboButton {
const localStorageIsSafe = AccessorUtilities.isLocalStorageAccessSafe();
if (localStorageIsSafe) {
- this.localStorageKey = ['group', this.groupId, this.formattedText.localStorageItemType, 'recent-project'].join('-');
+ this.localStorageKey = [
+ 'group',
+ this.groupId,
+ this.formattedText.localStorageItemType,
+ 'recent-project',
+ ].join('-');
this.setBtnTextFromLocalStorage();
}
}
// eslint-disable-next-line class-methods-use-this
openDropdown(event) {
- $(event.currentTarget).siblings('.project-item-select').select2('open');
+ $(event.currentTarget)
+ .siblings('.project-item-select')
+ .select2('open');
}
selectProject() {
@@ -86,8 +94,14 @@ export default class ProjectSelectComboButton {
const defaultTextPrefix = this.resourceLabel;
// the trailing slice call depluralizes each of these strings (e.g. new-issues -> new-issue)
- const localStorageItemType = `new-${this.resourceType.split('_').join('-').slice(0, -1)}`;
- const presetTextSuffix = this.resourceType.split('_').join(' ').slice(0, -1);
+ const localStorageItemType = `new-${this.resourceType
+ .split('_')
+ .join('-')
+ .slice(0, -1)}`;
+ const presetTextSuffix = this.resourceType
+ .split('_')
+ .join(' ')
+ .slice(0, -1);
return {
localStorageItemType, // new-issue / new-merge-request
@@ -96,4 +110,3 @@ export default class ProjectSelectComboButton {
};
}
}
-
diff --git a/app/assets/javascripts/project_visibility.js b/app/assets/javascripts/project_visibility.js
index a52ac768e57..aaf6723c85c 100644
--- a/app/assets/javascripts/project_visibility.js
+++ b/app/assets/javascripts/project_visibility.js
@@ -7,7 +7,7 @@ function setVisibilityOptions(namespaceSelector) {
const selectedNamespace = namespaceSelector.options[namespaceSelector.selectedIndex];
const { name, visibility, visibilityLevel, showPath, editPath } = selectedNamespace.dataset;
- document.querySelectorAll('.visibility-level-setting .form-check').forEach((option) => {
+ document.querySelectorAll('.visibility-level-setting .form-check').forEach(option => {
const optionInput = option.querySelector('input[type=radio]');
const optionValue = optionInput ? optionInput.value : 0;
const optionTitle = option.querySelector('.option-title');
@@ -20,8 +20,7 @@ function setVisibilityOptions(namespaceSelector) {
optionInput.disabled = true;
const reason = option.querySelector('.option-disabled-reason');
if (reason) {
- reason.innerHTML =
- `This project cannot be ${optionName} because the visibility of
+ reason.innerHTML = `This project cannot be ${optionName} because the visibility of
<a href="${showPath}">${name}</a> is ${visibility}. To make this project
${optionName}, you must first <a href="${editPath}">change the visibility</a>
of the parent group.`;
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js
index c15d8ba49e1..5a3407693e5 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_dropdown_mixin.js
@@ -1,18 +1,18 @@
import _ from 'underscore';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import store from '../store';
export default {
store,
components: {
- LoadingIcon,
DropdownButton,
DropdownSearchInput,
DropdownHiddenInput,
+ GlLoadingIcon,
},
props: {
fieldId: {
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
index d4497924ad8..2c02f436b69 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
@@ -126,7 +126,7 @@ export default {
</ul>
</div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
index 08d0a122579..fc17e2fab49 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
@@ -187,7 +187,7 @@ export default {
</ul>
</div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
index b5476684c6a..ca7c79f75f0 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
@@ -100,7 +100,7 @@ export default {
</ul>
</div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/projects/project_import_gitlab_project.js b/app/assets/javascripts/projects/project_import_gitlab_project.js
index 4e20fce1460..fbef3a0b059 100644
--- a/app/assets/javascripts/projects/project_import_gitlab_project.js
+++ b/app/assets/javascripts/projects/project_import_gitlab_project.js
@@ -1,9 +1,19 @@
import $ from 'jquery';
import { getParameterValues } from '../lib/utils/url_utility';
+import projectNew from './project_new';
export default () => {
- const path = getParameterValues('path')[0];
+ const pathParam = getParameterValues('path')[0];
+ const nameParam = getParameterValues('name')[0];
+ const $projectPath = $('.js-path-name');
+ const $projectName = $('.js-project-name');
- // get the path url and append it in the inputS
- $('.js-path-name').val(path);
+ // get the path url and append it in the input
+ $projectPath.val(pathParam);
+
+ // get the project name from the URL and set it as input value
+ $projectName.val(nameParam);
+
+ // generate slug when project name changes
+ $projectName.keyup(() => projectNew.onProjectNameChange($projectName, $projectPath));
};
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 04badad0f34..998554d1be5 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -1,10 +1,13 @@
import $ from 'jquery';
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
+import { slugifyWithHyphens } from '../lib/utils/text_utility';
let hasUserDefinedProjectPath = false;
-const deriveProjectPathFromUrl = ($projectImportUrl) => {
- const $currentProjectPath = $projectImportUrl.parents('.toggle-import-form').find('#project_path');
+const deriveProjectPathFromUrl = $projectImportUrl => {
+ const $currentProjectPath = $projectImportUrl
+ .parents('.toggle-import-form')
+ .find('#project_path');
if (hasUserDefinedProjectPath) {
return;
}
@@ -29,26 +32,33 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => {
}
};
+const onProjectNameChange = ($projectNameInput, $projectPathInput) => {
+ const slug = slugifyWithHyphens($projectNameInput.val());
+ $projectPathInput.val(slug);
+};
+
const bindEvents = () => {
const $newProjectForm = $('#new_project');
const $projectImportUrl = $('#project_import_url');
- const $projectPath = $('#project_path');
+ const $projectPath = $('.tab-pane.active #project_path');
const $useTemplateBtn = $('.template-button > input');
const $projectFieldsForm = $('.project-fields-form');
const $selectedTemplateText = $('.selected-template');
const $changeTemplateBtn = $('.change-template');
const $selectedIcon = $('.selected-icon');
- const $templateProjectNameInput = $('#template-project-name #project_path');
const $pushNewProjectTipTrigger = $('.push-new-project-tip');
const $projectTemplateButtons = $('.project-templates-buttons');
+ const $projectName = $('.tab-pane.active #project_name');
if ($newProjectForm.length !== 1) {
return;
}
- $('.how_to_import_link').on('click', (e) => {
+ $('.how_to_import_link').on('click', e => {
e.preventDefault();
- $(e.currentTarget).next('.modal').show();
+ $(e.currentTarget)
+ .next('.modal')
+ .show();
});
$('.modal-header .close').on('click', () => {
@@ -57,14 +67,21 @@ const bindEvents = () => {
$('.btn_import_gitlab_project').on('click', () => {
const importHref = $('a.btn_import_gitlab_project').attr('href');
- $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`);
+ $('.btn_import_gitlab_project').attr(
+ 'href',
+ `${importHref}?namespace_id=${$(
+ '#project_namespace_id',
+ ).val()}&name=${$projectName.val()}&path=${$projectPath.val()}`,
+ );
});
if ($pushNewProjectTipTrigger) {
$pushNewProjectTipTrigger
.removeAttr('rel')
.removeAttr('target')
- .on('click', (e) => { e.preventDefault(); })
+ .on('click', e => {
+ e.preventDefault();
+ })
.popover({
title: $pushNewProjectTipTrigger.data('title'),
placement: 'bottom',
@@ -72,13 +89,15 @@ const bindEvents = () => {
content: $('.push-new-project-tip-template').html(),
})
.on('shown.bs.popover', () => {
- $(document).on('click.popover touchstart.popover', (event) => {
+ $(document).on('click.popover touchstart.popover', event => {
if ($(event.target).closest('.popover').length === 0) {
$pushNewProjectTipTrigger.trigger('click');
}
});
- const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find('.js-select-on-focus');
+ const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find(
+ '.js-select-on-focus',
+ );
addSelectOnFocusBehaviour(target);
target.focus();
@@ -110,8 +129,18 @@ const bindEvents = () => {
const selectedTemplate = templates[value];
$selectedTemplateText.text(selectedTemplate.text);
- $(selectedTemplate.icon).clone().addClass('d-block').appendTo($selectedIcon);
- $templateProjectNameInput.focus();
+ $(selectedTemplate.icon)
+ .clone()
+ .addClass('d-block')
+ .appendTo($selectedIcon);
+
+ const $activeTabProjectName = $('.tab-pane.active #project_name');
+ const $activeTabProjectPath = $('.tab-pane.active #project_path');
+ $activeTabProjectName.focus();
+ $activeTabProjectName.keyup(() => {
+ onProjectNameChange($activeTabProjectName, $activeTabProjectPath);
+ hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0;
+ });
}
$useTemplateBtn.on('change', chooseTemplate);
@@ -131,9 +160,15 @@ const bindEvents = () => {
});
$projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl));
+
+ $projectName.on('keyup change', () => {
+ onProjectNameChange($projectName, $projectPath);
+ hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
+ });
};
export default {
bindEvents,
deriveProjectPathFromUrl,
+ onProjectNameChange,
};
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index 1c1e17563a1..7400b685c7e 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -1,11 +1,11 @@
<script>
import Visibility from 'visibilityjs';
import ciIcon from '~/vue_shared/components/ci_icon.vue';
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import Poll from '~/lib/utils/poll';
import Flash from '~/flash';
import { s__, sprintf } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
+import { GlLoadingIcon } from '@gitlab/ui';
import CommitPipelineService from '../services/commit_pipeline_service';
export default {
@@ -14,7 +14,7 @@ export default {
},
components: {
ciIcon,
- loadingIcon,
+ GlLoadingIcon,
},
props: {
endpoint: {
@@ -100,10 +100,10 @@ export default {
</script>
<template>
<div class="ci-status-link">
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
+ :size="3"
label="Loading pipeline status"
- size="3"
/>
<a
v-else
diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
index 078ccbbbac2..8380cfb6c59 100644
--- a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
+++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
@@ -65,10 +65,14 @@ export default class PrometheusMetrics {
let totalMissingEnvVarMetrics = 0;
let totalExporters = 0;
- metrics.forEach((metric) => {
+ metrics.forEach(metric => {
if (metric.active_metrics > 0) {
totalExporters += 1;
- this.$monitoredMetricsList.append(`<li>${_.escape(metric.group)}<span class="badge">${_.escape(metric.active_metrics)}</span></li>`);
+ this.$monitoredMetricsList.append(
+ `<li>${_.escape(metric.group)}<span class="badge">${_.escape(
+ metric.active_metrics,
+ )}</span></li>`,
+ );
totalMonitoredMetrics += metric.active_metrics;
if (metric.metrics_missing_requirements > 0) {
this.$missingEnvVarMetricsList.append(`<li>${_.escape(metric.group)}</li>`);
@@ -78,17 +82,26 @@ export default class PrometheusMetrics {
});
if (totalMonitoredMetrics === 0) {
- const emptyCommonMetricsText = sprintf(s__('PrometheusService|<p class="text-tertiary">No <a href="%{docsUrl}">common metrics</a> were found</p>'), {
- docsUrl: this.helpMetricsPath,
- }, false);
+ const emptyCommonMetricsText = sprintf(
+ s__(
+ 'PrometheusService|<p class="text-tertiary">No <a href="%{docsUrl}">common metrics</a> were found</p>',
+ ),
+ {
+ docsUrl: this.helpMetricsPath,
+ },
+ false,
+ );
this.$monitoredMetricsEmpty.empty();
this.$monitoredMetricsEmpty.append(emptyCommonMetricsText);
this.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
} else {
- const metricsCountText = sprintf(s__('PrometheusService|%{exporters} with %{metrics} were found'), {
- exporters: n__('%d exporter', '%d exporters', totalExporters),
- metrics: n__('%d metric', '%d metrics', totalMonitoredMetrics),
- });
+ const metricsCountText = sprintf(
+ s__('PrometheusService|%{exporters} with %{metrics} were found'),
+ {
+ exporters: n__('%d exporter', '%d exporters', totalExporters),
+ metrics: n__('%d metric', '%d metrics', totalMonitoredMetrics),
+ },
+ );
this.$monitoredMetricsCount.text(metricsCountText);
this.showMonitoringMetricsPanelState(PANEL_STATE.LIST);
@@ -102,7 +115,8 @@ export default class PrometheusMetrics {
loadActiveMetrics() {
this.showMonitoringMetricsPanelState(PANEL_STATE.LOADING);
backOff((next, stop) => {
- axios.get(this.activeMetricsEndpoint)
+ axios
+ .get(this.activeMetricsEndpoint)
.then(({ data }) => {
if (data && data.success) {
stop(data);
@@ -117,7 +131,7 @@ export default class PrometheusMetrics {
})
.catch(stop);
})
- .then((res) => {
+ .then(res => {
if (res && res.data && res.data.length) {
this.populateActiveMetrics(res.data);
} else {
diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js
index b601b19e7be..48343c8ba0a 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_create.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_create.js
@@ -46,8 +46,12 @@ export default class ProtectedBranchCreate {
onSelect() {
// Enable submit button
const $branchInput = this.$form.find('input[name="protected_branch[name]"]');
- const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]');
- const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]');
+ const $allowedToMergeInput = this.$form.find(
+ 'input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]',
+ );
+ const $allowedToPushInput = this.$form.find(
+ 'input[name="protected_branch[push_access_levels_attributes][0][access_level]"]',
+ );
const completedForm = !(
$branchInput.val() &&
$allowedToMergeInput.length &&
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js
index 54560d08ad7..5bc08f60d16 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js
@@ -29,8 +29,12 @@ export default class ProtectedBranchEdit {
}
onSelect() {
- const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
- const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
+ const $allowedToMergeInput = this.$wrap.find(
+ `input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`,
+ );
+ const $allowedToPushInput = this.$wrap.find(
+ `input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`,
+ );
// Do not update if one dropdown has not selected any option
if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
@@ -38,25 +42,36 @@ export default class ProtectedBranchEdit {
this.$allowedToMergeDropdown.disable();
this.$allowedToPushDropdown.disable();
- axios.patch(this.$wrap.data('url'), {
- protected_branch: {
- merge_access_levels_attributes: [{
- id: this.$allowedToMergeDropdown.data('accessLevelId'),
- access_level: $allowedToMergeInput.val(),
- }],
- push_access_levels_attributes: [{
- id: this.$allowedToPushDropdown.data('accessLevelId'),
- access_level: $allowedToPushInput.val(),
- }],
- },
- }).then(() => {
- this.$allowedToMergeDropdown.enable();
- this.$allowedToPushDropdown.enable();
- }).catch(() => {
- this.$allowedToMergeDropdown.enable();
- this.$allowedToPushDropdown.enable();
-
- flash('Failed to update branch!', 'alert', document.querySelector('.js-protected-branches-list'));
- });
+ axios
+ .patch(this.$wrap.data('url'), {
+ protected_branch: {
+ merge_access_levels_attributes: [
+ {
+ id: this.$allowedToMergeDropdown.data('accessLevelId'),
+ access_level: $allowedToMergeInput.val(),
+ },
+ ],
+ push_access_levels_attributes: [
+ {
+ id: this.$allowedToPushDropdown.data('accessLevelId'),
+ access_level: $allowedToPushInput.val(),
+ },
+ ],
+ },
+ })
+ .then(() => {
+ this.$allowedToMergeDropdown.enable();
+ this.$allowedToPushDropdown.enable();
+ })
+ .catch(() => {
+ this.$allowedToMergeDropdown.enable();
+ this.$allowedToPushDropdown.enable();
+
+ flash(
+ 'Failed to update branch!',
+ 'alert',
+ document.querySelector('.js-protected-branches-list'),
+ );
+ });
}
}
diff --git a/app/assets/javascripts/protected_tags/protected_tag_create.js b/app/assets/javascripts/protected_tags/protected_tag_create.js
index 2f8116df0d2..fddf2674cbb 100644
--- a/app/assets/javascripts/protected_tags/protected_tag_create.js
+++ b/app/assets/javascripts/protected_tags/protected_tag_create.js
@@ -40,7 +40,9 @@ export default class ProtectedTagCreate {
const $tagInput = this.$form.find('input[name="protected_tag[name]"]');
const $allowedToCreateInput = this.$form.find('#create_access_levels_attributes');
- this.$form.find('input[type="submit"]').prop('disabled', !($tagInput.val() && $allowedToCreateInput.length));
+ this.$form
+ .find('input[type="submit"]')
+ .prop('disabled', !($tagInput.val() && $allowedToCreateInput.length));
}
static getProtectedTags(term, callback) {
diff --git a/app/assets/javascripts/protected_tags/protected_tag_edit.js b/app/assets/javascripts/protected_tags/protected_tag_edit.js
index 8687b2a4044..c52497e62f2 100644
--- a/app/assets/javascripts/protected_tags/protected_tag_edit.js
+++ b/app/assets/javascripts/protected_tags/protected_tag_edit.js
@@ -21,26 +21,33 @@ export default class ProtectedTagEdit {
}
onSelect() {
- const $allowedToCreateInput = this.$wrap.find(`input[name="${this.$allowedToCreateDropdownButton.data('fieldName')}"]`);
+ const $allowedToCreateInput = this.$wrap.find(
+ `input[name="${this.$allowedToCreateDropdownButton.data('fieldName')}"]`,
+ );
// Do not update if one dropdown has not selected any option
if (!$allowedToCreateInput.length) return;
this.$allowedToCreateDropdownButton.disable();
- axios.patch(this.$wrap.data('url'), {
- protected_tag: {
- create_access_levels_attributes: [{
- id: this.$allowedToCreateDropdownButton.data('accessLevelId'),
- access_level: $allowedToCreateInput.val(),
- }],
- },
- }).then(() => {
- this.$allowedToCreateDropdownButton.enable();
- }).catch(() => {
- this.$allowedToCreateDropdownButton.enable();
-
- flash('Failed to update tag!', 'alert', document.querySelector('.js-protected-tags-list'));
- });
+ axios
+ .patch(this.$wrap.data('url'), {
+ protected_tag: {
+ create_access_levels_attributes: [
+ {
+ id: this.$allowedToCreateDropdownButton.data('accessLevelId'),
+ access_level: $allowedToCreateInput.val(),
+ },
+ ],
+ },
+ })
+ .then(() => {
+ this.$allowedToCreateDropdownButton.enable();
+ })
+ .catch(() => {
+ this.$allowedToCreateDropdownButton.enable();
+
+ flash('Failed to update tag!', 'alert', document.querySelector('.js-protected-tags-list'));
+ });
}
}
diff --git a/app/assets/javascripts/raven/raven_config.js b/app/assets/javascripts/raven/raven_config.js
index 658caeecde1..338006ce2b9 100644
--- a/app/assets/javascripts/raven/raven_config.js
+++ b/app/assets/javascripts/raven/raven_config.js
@@ -9,7 +9,7 @@ const IGNORE_ERRORS = [
'canvas.contentDocument',
'MyApp_RemoveAllHighlights',
'http://tt.epicplay.com',
- 'Can\'t find variable: ZiteReader',
+ "Can't find variable: ZiteReader",
'jigsaw is not defined',
'ComboSearch is not defined',
'http://loading.retry.widdit.com/',
diff --git a/app/assets/javascripts/read_more.js b/app/assets/javascripts/read_more.js
new file mode 100644
index 00000000000..d2d1ac8c76a
--- /dev/null
+++ b/app/assets/javascripts/read_more.js
@@ -0,0 +1,41 @@
+/**
+ * ReadMore
+ *
+ * Adds "read more" functionality to elements.
+ *
+ * Specifically, it looks for a trigger, by default ".js-read-more-trigger", and adds the class
+ * "is-expanded" to the previous element in order to provide a click to expand functionality.
+ *
+ * This is useful for long text elements that you would like to truncate, especially for mobile.
+ *
+ * Example Markup
+ * <div class="read-more-container">
+ * <p>Some text that should be long enough to have to truncate within a specified container.</p>
+ * <p>This text will not appear in the container, as only the first line can be truncated.</p>
+ * <p>This should also not appear, if everything is working correctly!</p>
+ * </div>
+ * <button class="js-read-more-trigger">Read more</button>
+ *
+ */
+export default function initReadMore(triggerSelector = '.js-read-more-trigger') {
+ const triggerEls = document.querySelectorAll(triggerSelector);
+
+ if (!triggerEls) return;
+
+ triggerEls.forEach(triggerEl => {
+ const targetEl = triggerEl.previousElementSibling;
+
+ if (!targetEl) {
+ return;
+ }
+
+ triggerEl.addEventListener(
+ 'click',
+ e => {
+ targetEl.classList.add('is-expanded');
+ e.target.remove();
+ },
+ { once: true },
+ );
+ });
+}
diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js
index 95c5cf7b345..75bac035aca 100644
--- a/app/assets/javascripts/ref_select_dropdown.js
+++ b/app/assets/javascripts/ref_select_dropdown.js
@@ -2,7 +2,8 @@ import $ from 'jquery';
class RefSelectDropdown {
constructor($dropdownButton, availableRefs) {
- const availableRefsValue = availableRefs || JSON.parse(document.getElementById('availableRefs').innerHTML);
+ const availableRefsValue =
+ availableRefs || JSON.parse(document.getElementById('availableRefs').innerHTML);
$dropdownButton.glDropdown({
data: availableRefsValue,
filterable: true,
@@ -29,7 +30,7 @@ class RefSelectDropdown {
const $fieldInput = $(`input[name="${$dropdownButton.data('fieldName')}"]`, $dropdownContainer);
const $filterInput = $('input[type="search"]', $dropdownContainer);
- $filterInput.on('keyup', (e) => {
+ $filterInput.on('keyup', e => {
const keyCode = e.keyCode || e.which;
if (keyCode !== 13) return;
diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue
index 31f88675912..6f94f5d6d2a 100644
--- a/app/assets/javascripts/registry/components/app.vue
+++ b/app/assets/javascripts/registry/components/app.vue
@@ -1,50 +1,43 @@
<script>
- import { mapGetters, mapActions } from 'vuex';
- import Flash from '../../flash';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
- import store from '../stores';
- import collapsibleContainer from './collapsible_container.vue';
- import { errorMessages, errorMessagesTypes } from '../constants';
+import { mapGetters, mapActions } from 'vuex';
+import { GlLoadingIcon } from '@gitlab/ui';
+import Flash from '../../flash';
+import store from '../stores';
+import collapsibleContainer from './collapsible_container.vue';
+import { errorMessages, errorMessagesTypes } from '../constants';
- export default {
- name: 'RegistryListApp',
- components: {
- collapsibleContainer,
- loadingIcon,
+export default {
+ name: 'RegistryListApp',
+ components: {
+ collapsibleContainer,
+ GlLoadingIcon,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
},
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- },
- store,
- computed: {
- ...mapGetters([
- 'isLoading',
- 'repos',
- ]),
- },
- created() {
- this.setMainEndpoint(this.endpoint);
- },
- mounted() {
- this.fetchRepos()
- .catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS]));
- },
- methods: {
- ...mapActions([
- 'setMainEndpoint',
- 'fetchRepos',
- ]),
- },
- };
+ },
+ store,
+ computed: {
+ ...mapGetters(['isLoading', 'repos']),
+ },
+ created() {
+ this.setMainEndpoint(this.endpoint);
+ },
+ mounted() {
+ this.fetchRepos().catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS]));
+ },
+ methods: {
+ ...mapActions(['setMainEndpoint', 'fetchRepos']),
+ },
+};
</script>
<template>
<div>
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
- size="3"
+ :size="3"
/>
<collapsible-container
diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue
index 4116c4a0489..d85de973740 100644
--- a/app/assets/javascripts/registry/components/collapsible_container.vue
+++ b/app/assets/javascripts/registry/components/collapsible_container.vue
@@ -1,60 +1,61 @@
<script>
- import { mapActions } from 'vuex';
- import Flash from '../../flash';
- import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
- import tableRegistry from './table_registry.vue';
- import { errorMessages, errorMessagesTypes } from '../constants';
+import { mapActions } from 'vuex';
+import { GlLoadingIcon } from '@gitlab/ui';
+import Flash from '../../flash';
+import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
+import tableRegistry from './table_registry.vue';
+import { errorMessages, errorMessagesTypes } from '../constants';
+import { __ } from '../../locale';
- export default {
- name: 'CollapsibeContainerRegisty',
- components: {
- clipboardButton,
- loadingIcon,
- tableRegistry,
+export default {
+ name: 'CollapsibeContainerRegisty',
+ components: {
+ clipboardButton,
+ tableRegistry,
+ GlLoadingIcon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ repo: {
+ type: Object,
+ required: true,
},
- directives: {
- tooltip,
- },
- props: {
- repo: {
- type: Object,
- required: true,
- },
- },
- data() {
- return {
- isOpen: false,
- };
- },
- methods: {
- ...mapActions([
- 'fetchRepos',
- 'fetchList',
- 'deleteRepo',
- ]),
+ },
+ data() {
+ return {
+ isOpen: false,
+ };
+ },
+ methods: {
+ ...mapActions(['fetchRepos', 'fetchList', 'deleteRepo']),
- toggleRepo() {
- this.isOpen = !this.isOpen;
+ toggleRepo() {
+ this.isOpen = !this.isOpen;
- if (this.isOpen) {
- this.fetchList({ repo: this.repo })
- .catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY));
- }
- },
+ if (this.isOpen) {
+ this.fetchList({ repo: this.repo }).catch(() =>
+ this.showError(errorMessagesTypes.FETCH_REGISTRY),
+ );
+ }
+ },
- handleDeleteRepository() {
- this.deleteRepo(this.repo)
- .then(() => this.fetchRepos())
- .catch(() => this.showError(errorMessagesTypes.DELETE_REPO));
- },
+ handleDeleteRepository() {
+ this.deleteRepo(this.repo)
+ .then(() => {
+ Flash(__('This container registry has been scheduled for deletion.'), 'notice');
+ this.fetchRepos();
+ })
+ .catch(() => this.showError(errorMessagesTypes.DELETE_REPO));
+ },
- showError(message) {
- Flash(errorMessages[message]);
- },
+ showError(message) {
+ Flash(errorMessages[message]);
},
- };
+ },
+};
</script>
<template>
@@ -86,8 +87,8 @@
<div class="controls d-none d-sm-block float-right">
<button
- v-tooltip
v-if="repo.canDelete"
+ v-tooltip
:title="s__('ContainerRegistry|Remove repository')"
:aria-label="s__('ContainerRegistry|Remove repository')"
type="button"
@@ -103,10 +104,10 @@
</div>
</div>
- <loading-icon
+ <gl-loading-icon
v-if="repo.isLoading"
+ :size="2"
class="append-bottom-20"
- size="2"
/>
<div
diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue
index 9f4973c3490..bb6c977fc63 100644
--- a/app/assets/javascripts/registry/components/table_registry.vue
+++ b/app/assets/javascripts/registry/components/table_registry.vue
@@ -1,66 +1,62 @@
<script>
- import { mapActions } from 'vuex';
- import { n__ } from '../../locale';
- import Flash from '../../flash';
- import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
- import tablePagination from '../../vue_shared/components/table_pagination.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
- import timeagoMixin from '../../vue_shared/mixins/timeago';
- import { errorMessages, errorMessagesTypes } from '../constants';
- import { numberToHumanSize } from '../../lib/utils/number_utils';
+import { mapActions } from 'vuex';
+import { n__ } from '../../locale';
+import Flash from '../../flash';
+import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
+import tablePagination from '../../vue_shared/components/table_pagination.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
+import timeagoMixin from '../../vue_shared/mixins/timeago';
+import { errorMessages, errorMessagesTypes } from '../constants';
+import { numberToHumanSize } from '../../lib/utils/number_utils';
- export default {
- components: {
- clipboardButton,
- tablePagination,
+export default {
+ components: {
+ clipboardButton,
+ tablePagination,
+ },
+ directives: {
+ tooltip,
+ },
+ mixins: [timeagoMixin],
+ props: {
+ repo: {
+ type: Object,
+ required: true,
},
- directives: {
- tooltip,
+ },
+ computed: {
+ shouldRenderPagination() {
+ return this.repo.pagination.total > this.repo.pagination.perPage;
},
- mixins: [
- timeagoMixin,
- ],
- props: {
- repo: {
- type: Object,
- required: true,
- },
- },
- computed: {
- shouldRenderPagination() {
- return this.repo.pagination.total > this.repo.pagination.perPage;
- },
- },
- methods: {
- ...mapActions([
- 'fetchList',
- 'deleteRegistry',
- ]),
+ },
+ methods: {
+ ...mapActions(['fetchList', 'deleteRegistry']),
- layers(item) {
- return item.layers ? n__('%d layer', '%d layers', item.layers) : '';
- },
+ layers(item) {
+ return item.layers ? n__('%d layer', '%d layers', item.layers) : '';
+ },
- formatSize(size) {
- return numberToHumanSize(size);
- },
+ formatSize(size) {
+ return numberToHumanSize(size);
+ },
- handleDeleteRegistry(registry) {
- this.deleteRegistry(registry)
- .then(() => this.fetchList({ repo: this.repo }))
- .catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY));
- },
+ handleDeleteRegistry(registry) {
+ this.deleteRegistry(registry)
+ .then(() => this.fetchList({ repo: this.repo }))
+ .catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY));
+ },
- onPageChange(pageNumber) {
- this.fetchList({ repo: this.repo, page: pageNumber })
- .catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY));
- },
+ onPageChange(pageNumber) {
+ this.fetchList({ repo: this.repo, page: pageNumber }).catch(() =>
+ this.showError(errorMessagesTypes.FETCH_REGISTRY),
+ );
+ },
- showError(message) {
- Flash(errorMessages[message]);
- },
+ showError(message) {
+ Flash(errorMessages[message]);
},
- };
+ },
+};
</script>
<template>
<div>
@@ -118,8 +114,8 @@
<td class="content">
<button
- v-tooltip
v-if="item.canDelete"
+ v-tooltip
:title="s__('ContainerRegistry|Remove tag')"
:aria-label="s__('ContainerRegistry|Remove tag')"
type="button"
diff --git a/app/assets/javascripts/registry/index.js b/app/assets/javascripts/registry/index.js
index e15cd94a915..025afefe7f0 100644
--- a/app/assets/javascripts/registry/index.js
+++ b/app/assets/javascripts/registry/index.js
@@ -4,22 +4,23 @@ import Translate from '../vue_shared/translate';
Vue.use(Translate);
-export default () => new Vue({
- el: '#js-vue-registry-images',
- components: {
- registryApp,
- },
- data() {
- const { dataset } = document.querySelector(this.$options.el);
- return {
- endpoint: dataset.endpoint,
- };
- },
- render(createElement) {
- return createElement('registry-app', {
- props: {
- endpoint: this.endpoint,
- },
- });
- },
-});
+export default () =>
+ new Vue({
+ el: '#js-vue-registry-images',
+ components: {
+ registryApp,
+ },
+ data() {
+ const { dataset } = document.querySelector(this.$options.el);
+ return {
+ endpoint: dataset.endpoint,
+ };
+ },
+ render(createElement) {
+ return createElement('registry-app', {
+ props: {
+ endpoint: this.endpoint,
+ },
+ });
+ },
+ });
diff --git a/app/assets/javascripts/registry/stores/mutations.js b/app/assets/javascripts/registry/stores/mutations.js
index 208c3c39866..69c051cd2d6 100644
--- a/app/assets/javascripts/registry/stores/mutations.js
+++ b/app/assets/javascripts/registry/stores/mutations.js
@@ -2,7 +2,6 @@ import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '../../lib/utils/common_utils';
export default {
-
[types.SET_MAIN_ENDPOINT](state, endpoint) {
Object.assign(state, { endpoint });
},
diff --git a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
index 7b37f4e9a97..bd204503cc7 100644
--- a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
+++ b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
@@ -1,79 +1,72 @@
<script>
- import { mapActions, mapGetters, mapState } from 'vuex';
- import { s__ } from '~/locale';
- import { componentNames } from './issue_body';
- import ReportSection from './report_section.vue';
- import SummaryRow from './summary_row.vue';
- import IssuesList from './issues_list.vue';
- import Modal from './modal.vue';
- import createStore from '../store';
- import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils';
+import { mapActions, mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { componentNames } from './issue_body';
+import ReportSection from './report_section.vue';
+import SummaryRow from './summary_row.vue';
+import IssuesList from './issues_list.vue';
+import Modal from './modal.vue';
+import createStore from '../store';
+import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils';
- export default {
- name: 'GroupedTestReportsApp',
- store: createStore(),
- components: {
- ReportSection,
- SummaryRow,
- IssuesList,
- Modal,
+export default {
+ name: 'GroupedTestReportsApp',
+ store: createStore(),
+ components: {
+ ReportSection,
+ SummaryRow,
+ IssuesList,
+ Modal,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
},
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- },
- componentNames,
- computed: {
- ...mapState([
- 'reports',
- 'isLoading',
- 'hasError',
- 'summary',
- ]),
- ...mapState({
- modalTitle: state => state.modal.title || '',
- modalData: state => state.modal.data || {},
- }),
- ...mapGetters([
- 'summaryStatus',
- ]),
- groupedSummaryText() {
- if (this.isLoading) {
- return s__('Reports|Test summary results are being parsed');
- }
+ },
+ componentNames,
+ computed: {
+ ...mapState(['reports', 'isLoading', 'hasError', 'summary']),
+ ...mapState({
+ modalTitle: state => state.modal.title || '',
+ modalData: state => state.modal.data || {},
+ }),
+ ...mapGetters(['summaryStatus']),
+ groupedSummaryText() {
+ if (this.isLoading) {
+ return s__('Reports|Test summary results are being parsed');
+ }
- if (this.hasError) {
- return s__('Reports|Test summary failed loading results');
- }
+ if (this.hasError) {
+ return s__('Reports|Test summary failed loading results');
+ }
- return summaryTextBuilder(s__('Reports|Test summary'), this.summary);
- },
+ return summaryTextBuilder(s__('Reports|Test summary'), this.summary);
},
- created() {
- this.setEndpoint(this.endpoint);
+ },
+ created() {
+ this.setEndpoint(this.endpoint);
- this.fetchReports();
+ this.fetchReports();
+ },
+ methods: {
+ ...mapActions(['setEndpoint', 'fetchReports']),
+ reportText(report) {
+ const summary = report.summary || {};
+ return reportTextBuilder(report.name, summary);
+ },
+ getReportIcon(report) {
+ return statusIcon(report.status);
},
- methods: {
- ...mapActions(['setEndpoint', 'fetchReports']),
- reportText(report) {
- const summary = report.summary || {};
- return reportTextBuilder(report.name, summary);
- },
- getReportIcon(report) {
- return statusIcon(report.status);
- },
- shouldRenderIssuesList(report) {
- return (
- report.existing_failures.length > 0 ||
- report.new_failures.length > 0 ||
- report.resolved_failures.length > 0
- );
- },
+ shouldRenderIssuesList(report) {
+ return (
+ report.existing_failures.length > 0 ||
+ report.new_failures.length > 0 ||
+ report.resolved_failures.length > 0
+ );
},
- };
+ },
+};
</script>
<template>
<report-section
@@ -82,7 +75,7 @@
:loading-text="groupedSummaryText"
:error-text="groupedSummaryText"
:has-issues="reports.length > 0"
- class="mr-widget-border-top grouped-security-reports mr-report"
+ class="mr-widget-section grouped-security-reports mr-report"
>
<div
slot="body"
@@ -92,16 +85,16 @@
v-for="(report, i) in reports"
>
<summary-row
+ :key="`summary-row-${i}`"
:summary="reportText(report)"
:status-icon="getReportIcon(report)"
- :key="`summary-row-${i}`"
/>
<issues-list
v-if="shouldRenderIssuesList(report)"
+ :key="`issues-list-${i}`"
:unresolved-issues="report.existing_failures"
:new-issues="report.new_failures"
:resolved-issues="report.resolved_failures"
- :key="`issues-list-${i}`"
:component="$options.componentNames.TestIssueBody"
class="report-block-group-list"
/>
diff --git a/app/assets/javascripts/reports/components/issue_status_icon.vue b/app/assets/javascripts/reports/components/issue_status_icon.vue
index 85811698a37..6e143c4f98c 100644
--- a/app/assets/javascripts/reports/components/issue_status_icon.vue
+++ b/app/assets/javascripts/reports/components/issue_status_icon.vue
@@ -1,10 +1,6 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
-import {
- STATUS_FAILED,
- STATUS_NEUTRAL,
- STATUS_SUCCESS,
-} from '../constants';
+import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '../constants';
export default {
name: 'IssueStatusIcon',
diff --git a/app/assets/javascripts/reports/components/issues_list.vue b/app/assets/javascripts/reports/components/issues_list.vue
index df42201b5de..f4243522ef8 100644
--- a/app/assets/javascripts/reports/components/issues_list.vue
+++ b/app/assets/javascripts/reports/components/issues_list.vue
@@ -1,22 +1,31 @@
<script>
-import IssuesBlock from '~/reports/components/report_issues.vue';
-import {
- STATUS_SUCCESS,
- STATUS_FAILED,
- STATUS_NEUTRAL,
-} from '~/reports/constants';
+import ReportItem from '~/reports/components/report_item.vue';
+import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants';
+import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
+
+const wrapIssueWithState = (status, isNew = false) => issue => ({
+ status: issue.status || status,
+ isNew,
+ issue,
+});
/**
* Renders block of issues
*/
-
export default {
components: {
- IssuesBlock,
+ SmartVirtualList,
+ ReportItem,
},
- success: STATUS_SUCCESS,
- failed: STATUS_FAILED,
- neutral: STATUS_NEUTRAL,
+ // Typical height of a report item in px
+ typicalReportItemHeight: 32,
+ /*
+ The maximum amount of shown issues. This is calculated by
+ ( max-height of report-block-list / typicalReportItemHeight ) + some safety margin
+ We will use VirtualList if we have more items than this number.
+ For entries lower than this number, the virtual scroll list calculates the total height of the element wrongly.
+ */
+ maxShownReportItems: 20,
props: {
newIssues: {
type: Array,
@@ -44,42 +53,34 @@ export default {
default: '',
},
},
+ computed: {
+ issuesWithState() {
+ return [
+ ...this.newIssues.map(wrapIssueWithState(STATUS_FAILED, true)),
+ ...this.unresolvedIssues.map(wrapIssueWithState(STATUS_FAILED)),
+ ...this.neutralIssues.map(wrapIssueWithState(STATUS_NEUTRAL)),
+ ...this.resolvedIssues.map(wrapIssueWithState(STATUS_SUCCESS)),
+ ];
+ },
+ },
};
</script>
<template>
- <div class="report-block-container">
-
- <issues-block
- v-if="newIssues.length"
- :component="component"
- :issues="newIssues"
- class="js-mr-code-new-issues"
- status="failed"
- is-new
- />
-
- <issues-block
- v-if="unresolvedIssues.length"
- :component="component"
- :issues="unresolvedIssues"
- :status="$options.failed"
- class="js-mr-code-new-issues"
- />
-
- <issues-block
- v-if="neutralIssues.length"
- :component="component"
- :issues="neutralIssues"
- :status="$options.neutral"
- class="js-mr-code-non-issues"
- />
-
- <issues-block
- v-if="resolvedIssues.length"
+ <smart-virtual-list
+ :length="issuesWithState.length"
+ :remain="$options.maxShownReportItems"
+ :size="$options.typicalReportItemHeight"
+ class="report-block-container"
+ wtag="ul"
+ wclass="report-block-list"
+ >
+ <report-item
+ v-for="(wrapped, index) in issuesWithState"
+ :key="index"
+ :issue="wrapped.issue"
+ :status="wrapped.status"
:component="component"
- :issues="resolvedIssues"
- :status="$options.success"
- class="js-mr-code-resolved-issues"
+ :is-new="wrapped.isNew"
/>
- </div>
+ </smart-virtual-list>
</template>
diff --git a/app/assets/javascripts/reports/components/modal.vue b/app/assets/javascripts/reports/components/modal.vue
index acc5c6d85e2..5f9e4072b2d 100644
--- a/app/assets/javascripts/reports/components/modal.vue
+++ b/app/assets/javascripts/reports/components/modal.vue
@@ -1,27 +1,27 @@
<script>
- import Modal from '~/vue_shared/components/gl_modal.vue';
- import LoadingButton from '~/vue_shared/components/loading_button.vue';
- import CodeBlock from '~/vue_shared/components/code_block.vue';
- import { fieldTypes } from '../constants';
+import Modal from '~/vue_shared/components/gl_modal.vue';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import CodeBlock from '~/vue_shared/components/code_block.vue';
+import { fieldTypes } from '../constants';
- export default {
- components: {
- Modal,
- LoadingButton,
- CodeBlock,
+export default {
+ components: {
+ Modal,
+ LoadingButton,
+ CodeBlock,
+ },
+ props: {
+ title: {
+ type: String,
+ required: true,
},
- props: {
- title: {
- type: String,
- required: true,
- },
- modalData: {
- type: Object,
- required: true,
- },
+ modalData: {
+ type: Object,
+ required: true,
},
- fieldTypes,
- };
+ },
+ fieldTypes,
+};
</script>
<template>
<modal
diff --git a/app/assets/javascripts/reports/components/report_issues.vue b/app/assets/javascripts/reports/components/report_issues.vue
deleted file mode 100644
index c553a374f66..00000000000
--- a/app/assets/javascripts/reports/components/report_issues.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-<script>
-import IssueStatusIcon from '~/reports/components/issue_status_icon.vue';
-import { components, componentNames } from '~/reports/components/issue_body';
-
-export default {
- name: 'ReportIssues',
- components: {
- IssueStatusIcon,
- ...components,
- },
- props: {
- issues: {
- type: Array,
- required: true,
- },
- component: {
- type: String,
- required: false,
- default: '',
- validator: value => value === '' || Object.values(componentNames).includes(value),
- },
- // failed || success
- status: {
- type: String,
- required: true,
- },
- isNew: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-};
-</script>
-<template>
- <div>
- <ul class="report-block-list">
- <li
- v-for="(issue, index) in issues"
- :class="{ 'is-dismissed': issue.isDismissed }"
- :key="index"
- class="report-block-list-issue"
- >
- <issue-status-icon
- :status="issue.status || status"
- class="append-right-5"
- />
-
- <component
- v-if="component"
- :is="component"
- :issue="issue"
- :status="issue.status || status"
- :is-new="isNew"
- />
- </li>
- </ul>
- </div>
-</template>
diff --git a/app/assets/javascripts/reports/components/report_item.vue b/app/assets/javascripts/reports/components/report_item.vue
new file mode 100644
index 00000000000..01e6d357a21
--- /dev/null
+++ b/app/assets/javascripts/reports/components/report_item.vue
@@ -0,0 +1,53 @@
+<script>
+import IssueStatusIcon from '~/reports/components/issue_status_icon.vue';
+import { components, componentNames } from '~/reports/components/issue_body';
+
+export default {
+ name: 'ReportItem',
+ components: {
+ IssueStatusIcon,
+ ...components,
+ },
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ component: {
+ type: String,
+ required: false,
+ default: '',
+ validator: value => value === '' || Object.values(componentNames).includes(value),
+ },
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ isNew: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+};
+</script>
+<template>
+ <li
+ :class="{ 'is-dismissed': issue.isDismissed }"
+ class="report-block-list-issue"
+ >
+ <issue-status-icon
+ :status="status"
+ class="append-right-5"
+ />
+
+ <component
+ :is="component"
+ v-if="component"
+ :issue="issue"
+ :status="status"
+ :is-new="isNew"
+ />
+ </li>
+</template>
diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue
index dc609d6f90e..d196f497362 100644
--- a/app/assets/javascripts/reports/components/report_section.vue
+++ b/app/assets/javascripts/reports/components/report_section.vue
@@ -139,7 +139,7 @@ export default {
<section class="media-section">
<div class="media">
<status-icon :status="statusIconName" />
- <div class="media-body space-children d-flex flex-align-self-center">
+ <div class="media-body d-flex flex-align-self-center">
<span class="js-code-text code-text">
{{ headerText }}
diff --git a/app/assets/javascripts/reports/components/summary_row.vue b/app/assets/javascripts/reports/components/summary_row.vue
index 4456d84c968..7e73ccb6c28 100644
--- a/app/assets/javascripts/reports/components/summary_row.vue
+++ b/app/assets/javascripts/reports/components/summary_row.vue
@@ -1,7 +1,7 @@
<script>
import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Popover from '~/vue_shared/components/help_popover.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
/**
* Renders the summary row for each report
@@ -15,8 +15,8 @@ export default {
name: 'ReportSummaryRow',
components: {
CiIcon,
- LoadingIcon,
Popover,
+ GlLoadingIcon,
},
props: {
summary: {
@@ -46,7 +46,7 @@ export default {
<template>
<div class="report-block-list-issue report-block-list-issue-parent">
<div class="report-block-list-icon append-right-10 prepend-left-5">
- <loading-icon
+ <gl-loading-icon
v-if="statusIcon === 'loading'"
css-class="report-block-list-loading-icon"
/>
diff --git a/app/assets/javascripts/reports/components/test_issue_body.vue b/app/assets/javascripts/reports/components/test_issue_body.vue
index cd443a49b52..1a87822fcc3 100644
--- a/app/assets/javascripts/reports/components/test_issue_body.vue
+++ b/app/assets/javascripts/reports/components/test_issue_body.vue
@@ -1,28 +1,28 @@
<script>
- import { mapActions } from 'vuex';
+import { mapActions } from 'vuex';
- export default {
- name: 'TestIssueBody',
- props: {
- issue: {
- type: Object,
- required: true,
- },
- // failed || success
- status: {
- type: String,
- required: true,
- },
- isNew: {
- type: Boolean,
- required: false,
- default: false,
- },
+export default {
+ name: 'TestIssueBody',
+ props: {
+ issue: {
+ type: Object,
+ required: true,
},
- methods: {
- ...mapActions(['openModal']),
+ // failed || success
+ status: {
+ type: String,
+ required: true,
},
- };
+ isNew: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ methods: {
+ ...mapActions(['openModal']),
+ },
+};
</script>
<template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
diff --git a/app/assets/javascripts/reports/store/actions.js b/app/assets/javascripts/reports/store/actions.js
index acabcc1d193..db8ab5ccb80 100644
--- a/app/assets/javascripts/reports/store/actions.js
+++ b/app/assets/javascripts/reports/store/actions.js
@@ -43,9 +43,11 @@ export const fetchReports = ({ state, dispatch }) => {
},
data: state.endpoint,
method: 'getReports',
- successCallback: ({ data, status }) => dispatch('receiveReportsSuccess', {
- data, status,
- }),
+ successCallback: ({ data, status }) =>
+ dispatch('receiveReportsSuccess', {
+ data,
+ status,
+ }),
errorCallback: () => dispatch('receiveReportsError'),
});
diff --git a/app/assets/javascripts/reports/store/index.js b/app/assets/javascripts/reports/store/index.js
index 9d8f7dc3b74..467c692b438 100644
--- a/app/assets/javascripts/reports/store/index.js
+++ b/app/assets/javascripts/reports/store/index.js
@@ -7,9 +7,10 @@ import state from './state';
Vue.use(Vuex);
-export default () => new Vuex.Store({
- actions,
- mutations,
- getters,
- state: state(),
-});
+export default () =>
+ new Vuex.Store({
+ actions,
+ mutations,
+ getters,
+ state: state(),
+ });
diff --git a/app/assets/javascripts/reports/store/mutation_types.js b/app/assets/javascripts/reports/store/mutation_types.js
index 82bda31df5d..599d4862dfe 100644
--- a/app/assets/javascripts/reports/store/mutation_types.js
+++ b/app/assets/javascripts/reports/store/mutation_types.js
@@ -4,4 +4,3 @@ export const REQUEST_REPORTS = 'REQUEST_REPORTS';
export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS';
export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR';
export const SET_ISSUE_MODAL_DATA = 'SET_ISSUE_MODAL_DATA';
-
diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js
index 1983a8c9e56..2a37f5b74fa 100644
--- a/app/assets/javascripts/reports/store/mutations.js
+++ b/app/assets/javascripts/reports/store/mutations.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
export default {
@@ -20,7 +19,6 @@ export default {
state.status = response.status;
state.reports = response.suites;
-
},
[types.RECEIVE_REPORTS_ERROR](state) {
state.isLoading = false;
@@ -37,7 +35,7 @@ export default {
[types.SET_ISSUE_MODAL_DATA](state, payload) {
state.modal.title = payload.issue.name;
- Object.keys(payload.issue).forEach((key) => {
+ Object.keys(payload.issue).forEach(key => {
if (Object.prototype.hasOwnProperty.call(state.modal.data, key)) {
state.modal.data[key] = {
...state.modal.data[key],
diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js
index 4cab2e27a16..5484900276c 100644
--- a/app/assets/javascripts/reports/store/state.js
+++ b/app/assets/javascripts/reports/store/state.js
@@ -57,5 +57,4 @@ export default () => ({
},
},
},
-
});
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index b27d635c6ac..225e21ad322 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, no-else-return, no-param-reassign, max-len */
+/* eslint-disable func-names, no-var, no-unused-vars, consistent-return, one-var, prefer-template, no-else-return, no-param-reassign */
import $ from 'jquery';
import _ from 'underscore';
@@ -21,7 +21,7 @@ Sidebar.initialize = function(currentUser) {
}
};
-Sidebar.prototype.removeListeners = function () {
+Sidebar.prototype.removeListeners = function() {
this.sidebar.off('click', '.sidebar-collapsed-icon');
this.sidebar.off('hidden.gl.dropdown');
$('.dropdown').off('loading.gl.dropdown');
@@ -38,10 +38,12 @@ Sidebar.prototype.addEventListeners = function() {
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
$document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
- return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
+ return $(document)
+ .off('click', '.js-issuable-todo')
+ .on('click', '.js-issuable-todo', this.toggleTodo);
};
-Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
+Sidebar.prototype.sidebarToggleClicked = function(e, triggered) {
var $allGutterToggleIcons, $this, isExpanded, tooltipLabel;
e.preventDefault();
$this = $(this);
@@ -51,20 +53,26 @@ Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
if (isExpanded) {
$allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
- $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
- $('.layout-page').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
+ $('aside.right-sidebar')
+ .removeClass('right-sidebar-expanded')
+ .addClass('right-sidebar-collapsed');
+ $('.layout-page')
+ .removeClass('right-sidebar-expanded')
+ .addClass('right-sidebar-collapsed');
} else {
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
- $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
- $('.layout-page').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
-
- if (gl.lazyLoader) gl.lazyLoader.loadCheck();
+ $('aside.right-sidebar')
+ .removeClass('right-sidebar-collapsed')
+ .addClass('right-sidebar-expanded');
+ $('.layout-page')
+ .removeClass('right-sidebar-collapsed')
+ .addClass('right-sidebar-expanded');
}
$this.attr('data-original-title', tooltipLabel);
if (!triggered) {
- Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
+ Cookies.set('collapsed_gutter', $('.right-sidebar').hasClass('right-sidebar-collapsed'));
}
};
@@ -73,21 +81,27 @@ Sidebar.prototype.toggleTodo = function(e) {
$this = $(e.currentTarget);
ajaxType = $this.attr('data-delete-path') ? 'delete' : 'post';
if ($this.attr('data-delete-path')) {
- url = "" + ($this.attr('data-delete-path'));
+ url = '' + $this.attr('data-delete-path');
} else {
- url = "" + ($this.data('url'));
+ url = '' + $this.data('url');
}
$this.tooltip('hide');
- $('.js-issuable-todo').disable().addClass('is-loading');
+ $('.js-issuable-todo')
+ .disable()
+ .addClass('is-loading');
axios[ajaxType](url, {
issuable_id: $this.data('issuableId'),
issuable_type: $this.data('issuableType'),
- }).then(({ data }) => {
- this.todoUpdateDone(data);
- }).catch(() => flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`));
+ })
+ .then(({ data }) => {
+ this.todoUpdateDone(data);
+ })
+ .catch(() =>
+ flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`),
+ );
};
Sidebar.prototype.todoUpdateDone = function(data) {
@@ -101,7 +115,8 @@ Sidebar.prototype.todoUpdateDone = function(data) {
const $el = $(el);
const $elText = $el.find('.js-issuable-todo-inner');
- $el.removeClass('is-loading')
+ $el
+ .removeClass('is-loading')
.enable()
.attr('aria-label', $el.data(`${attrPrefix}Text`))
.attr('data-delete-path', deletePath)
@@ -121,7 +136,9 @@ Sidebar.prototype.todoUpdateDone = function(data) {
Sidebar.prototype.sidebarDropdownLoading = function(e) {
var $loading, $sidebarCollapsedIcon, i, img;
- $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon');
+ $sidebarCollapsedIcon = $(this)
+ .closest('.block')
+ .find('.sidebar-collapsed-icon');
img = $sidebarCollapsedIcon.find('img');
i = $sidebarCollapsedIcon.find('i');
$loading = $('<i class="fa fa-spinner fa-spin"></i>');
@@ -136,7 +153,9 @@ Sidebar.prototype.sidebarDropdownLoading = function(e) {
Sidebar.prototype.sidebarDropdownLoaded = function(e) {
var $sidebarCollapsedIcon, i, img;
- $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon');
+ $sidebarCollapsedIcon = $(this)
+ .closest('.block')
+ .find('.sidebar-collapsed-icon');
img = $sidebarCollapsedIcon.find('img');
$sidebarCollapsedIcon.find('i.fa-spin').remove();
i = $sidebarCollapsedIcon.find('i');
@@ -222,7 +241,7 @@ Sidebar.prototype.isOpen = function() {
};
Sidebar.prototype.getBlock = function(name) {
- return this.sidebar.find(".block." + name);
+ return this.sidebar.find('.block.' + name);
};
export default Sidebar;
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index aec09b8bc0a..0a4583b5861 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-return-assign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, object-shorthand, prefer-template, class-methods-use-this, no-lonely-if, vars-on-top, max-len */
+/* eslint-disable no-return-assign, one-var, no-var, no-unused-vars, consistent-return, object-shorthand, prefer-template, class-methods-use-this, no-lonely-if, vars-on-top */
import $ from 'jquery';
import { escape, throttle } from 'underscore';
@@ -68,7 +68,7 @@ function setSearchOptions() {
}
}
-export default class SearchAutocomplete {
+export class SearchAutocomplete {
constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) {
setSearchOptions();
this.bindEventContext();
@@ -226,7 +226,7 @@ export default class SearchAutocomplete {
icon,
text: term,
template: s__('SearchAutocomplete|in all GitLab'),
- url: `/search?search=${term}`,
+ url: `${gon.relative_url_root}/search?search=${term}`,
});
if (template) {
@@ -234,7 +234,9 @@ export default class SearchAutocomplete {
icon,
text: term,
template,
- url: `/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`,
+ url: `${
+ gon.relative_url_root
+ }/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`,
});
}
}
@@ -251,7 +253,6 @@ export default class SearchAutocomplete {
}
getCategoryContents() {
- const userId = gon.current_user_id;
const userName = gon.current_username;
const { projectOptions, groupOptions, dashboardOptions } = gl;
@@ -277,21 +278,21 @@ export default class SearchAutocomplete {
const issueItems = [
{
text: s__('SearchAutocomplete|Issues assigned to me'),
- url: `${issuesPath}/?assignee_id=${userId}`,
+ url: `${issuesPath}/?assignee_username=${userName}`,
},
{
text: s__("SearchAutocomplete|Issues I've created"),
- url: `${issuesPath}/?author_id=${userId}`,
+ url: `${issuesPath}/?author_username=${userName}`,
},
];
const mergeRequestItems = [
{
text: s__('SearchAutocomplete|Merge requests assigned to me'),
- url: `${mrPath}/?assignee_id=${userId}`,
+ url: `${mrPath}/?assignee_username=${userName}`,
},
{
text: s__("SearchAutocomplete|Merge requests I've created"),
- url: `${mrPath}/?author_id=${userId}`,
+ url: `${mrPath}/?author_username=${userName}`,
},
];
@@ -499,3 +500,7 @@ export default class SearchAutocomplete {
this.dropdownMenu.toggleClass('fade-out', !this.isScrolledUp());
}
}
+
+export default function initSearchAutocomplete(opts) {
+ return new SearchAutocomplete(opts);
+}
diff --git a/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js b/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js
new file mode 100644
index 00000000000..14a89ef9293
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js
@@ -0,0 +1,21 @@
+import { AwardsHandler } from '~/awards_handler';
+
+class EmojiMenuInModal extends AwardsHandler {
+ constructor(emoji, toggleButtonSelector, menuClass, selectEmojiCallback, targetContainerEl) {
+ super(emoji);
+
+ this.selectEmojiCallback = selectEmojiCallback;
+ this.toggleButtonSelector = toggleButtonSelector;
+ this.menuClass = menuClass;
+ this.targetContainerEl = targetContainerEl;
+
+ this.bindEvents();
+ }
+
+ postEmoji($emojiButton, awardUrl, selectedEmoji, callback) {
+ this.selectEmojiCallback(selectedEmoji, this.emoji.glEmojiTag(selectedEmoji));
+ callback();
+ }
+}
+
+export default EmojiMenuInModal;
diff --git a/app/assets/javascripts/set_status_modal/event_hub.js b/app/assets/javascripts/set_status_modal/event_hub.js
new file mode 100644
index 00000000000..0948c2e5352
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/event_hub.js
@@ -0,0 +1,3 @@
+import Vue from 'vue';
+
+export default new Vue();
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue b/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue
new file mode 100644
index 00000000000..48e5ede80f2
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue
@@ -0,0 +1,33 @@
+<script>
+import { s__ } from '~/locale';
+import eventHub from './event_hub';
+
+export default {
+ props: {
+ hasStatus: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ buttonText() {
+ return this.hasStatus ? s__('SetStatusModal|Edit status') : s__('SetStatusModal|Set status');
+ },
+ },
+ methods: {
+ openModal() {
+ eventHub.$emit('openModal');
+ },
+ },
+};
+</script>
+
+<template>
+ <button
+ type="button"
+ class="btn menu-item"
+ @click="openModal"
+ >
+ {{ buttonText }}
+ </button>
+</template>
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
new file mode 100644
index 00000000000..8a48eea5c89
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -0,0 +1,246 @@
+<script>
+import $ from 'jquery';
+import createFlash from '~/flash';
+import Icon from '~/vue_shared/components/icon.vue';
+import GfmAutoComplete from '~/gfm_auto_complete';
+import { __, s__ } from '~/locale';
+import Api from '~/api';
+import { GlModal, GlTooltipDirective } from '@gitlab/ui';
+import eventHub from './event_hub';
+import EmojiMenuInModal from './emoji_menu_in_modal';
+
+const emojiMenuClass = 'js-modal-status-emoji-menu';
+
+export default {
+ components: {
+ Icon,
+ GlModal,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ currentEmoji: {
+ type: String,
+ required: true,
+ },
+ currentMessage: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ defaultEmojiTag: '',
+ emoji: this.currentEmoji,
+ emojiMenu: null,
+ emojiTag: '',
+ isEmojiMenuVisible: false,
+ message: this.currentMessage,
+ modalId: 'set-user-status-modal',
+ noEmoji: true,
+ };
+ },
+ computed: {
+ isDirty() {
+ return this.message.length || this.emoji.length;
+ },
+ },
+ mounted() {
+ eventHub.$on('openModal', this.openModal);
+ },
+ beforeDestroy() {
+ this.emojiMenu.destroy();
+ },
+ methods: {
+ openModal() {
+ this.$root.$emit('bv::show::modal', this.modalId);
+ },
+ closeModal() {
+ this.$root.$emit('bv::hide::modal', this.modalId);
+ },
+ setupEmojiListAndAutocomplete() {
+ const toggleEmojiMenuButtonSelector = '#set-user-status-modal .js-toggle-emoji-menu';
+ const emojiAutocomplete = new GfmAutoComplete();
+ emojiAutocomplete.setup($(this.$refs.statusMessageField), { emojis: true });
+
+ import(/* webpackChunkName: 'emoji' */ '~/emoji')
+ .then(Emoji => {
+ if (this.emoji) {
+ this.emojiTag = Emoji.glEmojiTag(this.emoji);
+ }
+ this.noEmoji = this.emoji === '';
+ this.defaultEmojiTag = Emoji.glEmojiTag('speech_balloon');
+
+ this.emojiMenu = new EmojiMenuInModal(
+ Emoji,
+ toggleEmojiMenuButtonSelector,
+ emojiMenuClass,
+ this.setEmoji,
+ this.$refs.userStatusForm,
+ );
+ })
+ .catch(() => createFlash(__('Failed to load emoji list.')));
+ },
+ showEmojiMenu() {
+ this.isEmojiMenuVisible = true;
+ this.emojiMenu.showEmojiMenu($(this.$refs.toggleEmojiMenuButton));
+ },
+ hideEmojiMenu() {
+ if (!this.isEmojiMenuVisible) {
+ return;
+ }
+
+ this.isEmojiMenuVisible = false;
+ this.emojiMenu.hideMenuElement($(`.${emojiMenuClass}`));
+ },
+ setDefaultEmoji() {
+ const { emojiTag } = this;
+ const hasStatusMessage = this.message;
+ if (hasStatusMessage && emojiTag) {
+ return;
+ }
+
+ if (hasStatusMessage) {
+ this.noEmoji = false;
+ this.emojiTag = this.defaultEmojiTag;
+ } else if (emojiTag === this.defaultEmojiTag) {
+ this.noEmoji = true;
+ this.clearEmoji();
+ }
+ },
+ setEmoji(emoji, emojiTag) {
+ this.emoji = emoji;
+ this.noEmoji = false;
+ this.clearEmoji();
+ this.emojiTag = emojiTag;
+ },
+ clearEmoji() {
+ if (this.emojiTag) {
+ this.emojiTag = '';
+ }
+ },
+ clearStatusInputs() {
+ this.emoji = '';
+ this.message = '';
+ this.noEmoji = true;
+ this.clearEmoji();
+ this.hideEmojiMenu();
+ },
+ removeStatus() {
+ this.clearStatusInputs();
+ this.setStatus();
+ },
+ setStatus() {
+ const { emoji, message } = this;
+
+ Api.postUserStatus({
+ emoji,
+ message,
+ })
+ .then(this.onUpdateSuccess)
+ .catch(this.onUpdateFail);
+ },
+ onUpdateSuccess() {
+ this.closeModal();
+ window.location.reload();
+ },
+ onUpdateFail() {
+ createFlash(
+ s__("SetStatusModal|Sorry, we weren't able to set your status. Please try again later."),
+ );
+
+ this.closeModal();
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ :title="s__('SetStatusModal|Set a status')"
+ :modal-id="modalId"
+ :ok-title="s__('SetStatusModal|Set status')"
+ :cancel-title="s__('SetStatusModal|Remove status')"
+ ok-variant="success"
+ class="set-user-status-modal"
+ @shown="setupEmojiListAndAutocomplete"
+ @hide="hideEmojiMenu"
+ @ok="setStatus"
+ @cancel="removeStatus"
+ >
+ <div>
+ <input
+ v-model="emoji"
+ class="js-status-emoji-field"
+ type="hidden"
+ name="user[status][emoji]"
+ />
+ <div
+ ref="userStatusForm"
+ class="form-group position-relative m-0"
+ >
+ <div class="input-group">
+ <span class="input-group-btn">
+ <button
+ ref="toggleEmojiMenuButton"
+ v-gl-tooltip.bottom
+ :title="s__('SetStatusModal|Add status emoji')"
+ :aria-label="s__('SetStatusModal|Add status emoji')"
+ name="button"
+ type="button"
+ class="js-toggle-emoji-menu emoji-menu-toggle-button btn"
+ @click="showEmojiMenu"
+ >
+ <span v-html="emojiTag"></span>
+ <span
+ v-show="noEmoji"
+ class="js-no-emoji-placeholder no-emoji-placeholder position-relative"
+ >
+ <icon
+ name="emoji_slightly_smiling_face"
+ css-classes="award-control-icon-neutral"
+ />
+ <icon
+ name="emoji_smiley"
+ css-classes="award-control-icon-positive"
+ />
+ <icon
+ name="emoji_smile"
+ css-classes="award-control-icon-super-positive"
+ />
+ </span>
+ </button>
+ </span>
+ <input
+ ref="statusMessageField"
+ v-model="message"
+ :placeholder="s__('SetStatusModal|What\'s your status?')"
+ type="text"
+ class="form-control form-control input-lg js-status-message-field"
+ name="user[status][message]"
+ @keyup="setDefaultEmoji"
+ @keyup.enter.prevent
+ @click="hideEmojiMenu"
+ />
+ <span
+ v-show="isDirty"
+ class="input-group-btn"
+ >
+ <button
+ v-gl-tooltip.bottom
+ :title="s__('SetStatusModal|Clear status')"
+ :aria-label="s__('SetStatusModal|Clear status')"
+ name="button"
+ type="button"
+ class="js-clear-user-status-button clear-user-status btn"
+ @click="clearStatusInputs()"
+ >
+ <icon name="close" />
+ </button>
+ </span>
+ </div>
+ </div>
+ </div>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js
index 37b4a2a4c63..d22aca35e09 100644
--- a/app/assets/javascripts/settings_panels.js
+++ b/app/assets/javascripts/settings_panels.js
@@ -1,21 +1,27 @@
import $ from 'jquery';
+import { __ } from './locale';
function expandSection($section) {
- $section.find('.js-settings-toggle').text('Collapse');
- $section.find('.settings-content').off('scroll.expandSection').scrollTop(0);
+ $section.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only)').text(__('Collapse'));
+ $section
+ .find('.settings-content')
+ .off('scroll.expandSection')
+ .scrollTop(0);
$section.addClass('expanded');
if (!$section.hasClass('no-animate')) {
- $section.addClass('animating')
+ $section
+ .addClass('animating')
.one('animationend.animateSection', () => $section.removeClass('animating'));
}
}
function closeSection($section) {
- $section.find('.js-settings-toggle').text('Expand');
+ $section.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only)').text(__('Expand'));
$section.find('.settings-content').on('scroll.expandSection', () => expandSection($section));
$section.removeClass('expanded');
if (!$section.hasClass('no-animate')) {
- $section.addClass('animating')
+ $section
+ .addClass('animating')
.one('animationend.animateSection', () => $section.removeClass('animating'));
}
}
diff --git a/app/assets/javascripts/shared/milestones/form.js b/app/assets/javascripts/shared/milestones/form.js
index 8681a1776c6..0ff84dc4667 100644
--- a/app/assets/javascripts/shared/milestones/form.js
+++ b/app/assets/javascripts/shared/milestones/form.js
@@ -15,5 +15,6 @@ export default (initGFM = true) => {
epics: initGFM,
milestones: initGFM,
labels: initGFM,
+ snippets: initGFM,
});
};
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js
deleted file mode 100644
index 99c71d6524a..00000000000
--- a/app/assets/javascripts/shortcuts.js
+++ /dev/null
@@ -1,124 +0,0 @@
-import $ from 'jquery';
-import Cookies from 'js-cookie';
-import Mousetrap from 'mousetrap';
-import axios from './lib/utils/axios_utils';
-import { refreshCurrentPage, visitUrl } from './lib/utils/url_utility';
-import findAndFollowLink from './shortcuts_dashboard_navigation';
-
-const defaultStopCallback = Mousetrap.stopCallback;
-Mousetrap.stopCallback = (e, element, combo) => {
- if (['ctrl+shift+p', 'command+shift+p'].indexOf(combo) !== -1) {
- return false;
- }
-
- return defaultStopCallback(e, element, combo);
-};
-
-export default class Shortcuts {
- constructor() {
- this.onToggleHelp = this.onToggleHelp.bind(this);
- this.enabledHelp = [];
-
- Mousetrap.bind('?', this.onToggleHelp);
- Mousetrap.bind('s', Shortcuts.focusSearch);
- Mousetrap.bind('f', this.focusFilter.bind(this));
- Mousetrap.bind('p b', Shortcuts.onTogglePerfBar);
-
- const findFileURL = document.body.dataset.findFile;
-
- Mousetrap.bind('shift+t', () => findAndFollowLink('.shortcuts-todos'));
- Mousetrap.bind('shift+a', () => findAndFollowLink('.dashboard-shortcuts-activity'));
- Mousetrap.bind('shift+i', () => findAndFollowLink('.dashboard-shortcuts-issues'));
- Mousetrap.bind('shift+m', () => findAndFollowLink('.dashboard-shortcuts-merge_requests'));
- Mousetrap.bind('shift+p', () => findAndFollowLink('.dashboard-shortcuts-projects'));
- Mousetrap.bind('shift+g', () => findAndFollowLink('.dashboard-shortcuts-groups'));
- Mousetrap.bind('shift+l', () => findAndFollowLink('.dashboard-shortcuts-milestones'));
- Mousetrap.bind('shift+s', () => findAndFollowLink('.dashboard-shortcuts-snippets'));
-
- Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], Shortcuts.toggleMarkdownPreview);
-
- if (typeof findFileURL !== 'undefined' && findFileURL !== null) {
- Mousetrap.bind('t', () => {
- visitUrl(findFileURL);
- });
- }
-
- $(document).on('click.more_help', '.js-more-help-button', function clickMoreHelp(e) {
- $(this).remove();
- $('.hidden-shortcut').show();
- e.preventDefault();
- });
- }
-
- onToggleHelp(e) {
- if (e.preventDefault) {
- e.preventDefault();
- }
-
- Shortcuts.toggleHelp(this.enabledHelp);
- }
-
- static onTogglePerfBar(e) {
- e.preventDefault();
- const performanceBarCookieName = 'perf_bar_enabled';
- if (Cookies.get(performanceBarCookieName) === 'true') {
- Cookies.set(performanceBarCookieName, 'false', { path: '/' });
- } else {
- Cookies.set(performanceBarCookieName, 'true', { path: '/' });
- }
- refreshCurrentPage();
- }
-
- static toggleMarkdownPreview(e) {
- // Check if short-cut was triggered while in Write Mode
- const $target = $(e.target);
- const $form = $target.closest('form');
-
- if ($target.hasClass('js-note-text')) {
- $('.js-md-preview-button', $form).focus();
- }
- $(document).triggerHandler('markdown-preview:toggle', [e]);
- }
-
- static toggleHelp(location) {
- const $modal = $('#modal-shortcuts');
-
- if ($modal.length) {
- $modal.modal('toggle');
- return null;
- }
-
- return axios.get(gon.shortcuts_path, {
- responseType: 'text',
- }).then(({ data }) => {
- $.globalEval(data);
-
- if (location && location.length > 0) {
- const results = [];
- for (let i = 0, len = location.length; i < len; i += 1) {
- results.push($(location[i]).show());
- }
- return results;
- }
-
- $('.hidden-shortcut').show();
- return $('.js-more-help-button').remove();
- });
- }
-
- focusFilter(e) {
- if (!this.filterInput) {
- this.filterInput = $('input[type=search]', '.nav-controls');
- }
- this.filterInput.focus();
- e.preventDefault();
- }
-
- static focusSearch(e) {
- $('#search').focus();
-
- if (e.preventDefault) {
- e.preventDefault();
- }
- }
-}
diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/shortcuts_blob.js
deleted file mode 100644
index 908b9cab93d..00000000000
--- a/app/assets/javascripts/shortcuts_blob.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import Mousetrap from 'mousetrap';
-import { getLocationHash, visitUrl } from './lib/utils/url_utility';
-import Shortcuts from './shortcuts';
-
-const defaults = {
- skipResetBindings: false,
- fileBlobPermalinkUrl: null,
-};
-
-export default class ShortcutsBlob extends Shortcuts {
- constructor(opts) {
- const options = Object.assign({}, defaults, opts);
- super(options.skipResetBindings);
- this.options = options;
-
- Mousetrap.bind('y', this.moveToFilePermalink.bind(this));
- }
-
- moveToFilePermalink() {
- if (this.options.fileBlobPermalinkUrl) {
- const hash = getLocationHash();
- const hashUrlString = hash ? `#${hash}` : '';
- visitUrl(`${this.options.fileBlobPermalinkUrl}${hashUrlString}`);
- }
- }
-}
diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/shortcuts_dashboard_navigation.js
deleted file mode 100644
index 9f69f110d06..00000000000
--- a/app/assets/javascripts/shortcuts_dashboard_navigation.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { visitUrl } from './lib/utils/url_utility';
-
-/**
- * Helper function that finds the href of the fiven selector and updates the location.
- *
- * @param {String} selector
- */
-export default function findAndFollowLink(selector) {
- const element = document.querySelector(selector);
- const link = element && element.getAttribute('href');
-
- if (link) {
- visitUrl(link);
- }
-}
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
deleted file mode 100644
index e9451be31fd..00000000000
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import $ from 'jquery';
-import Mousetrap from 'mousetrap';
-import _ from 'underscore';
-import Sidebar from './right_sidebar';
-import Shortcuts from './shortcuts';
-import { CopyAsGFM } from './behaviors/markdown/copy_as_gfm';
-
-export default class ShortcutsIssuable extends Shortcuts {
- constructor(isMergeRequest) {
- super();
-
- Mousetrap.bind('a', () => ShortcutsIssuable.openSidebarDropdown('assignee'));
- Mousetrap.bind('m', () => ShortcutsIssuable.openSidebarDropdown('milestone'));
- Mousetrap.bind('l', () => ShortcutsIssuable.openSidebarDropdown('labels'));
- Mousetrap.bind('r', ShortcutsIssuable.replyWithSelectedText);
- Mousetrap.bind('e', ShortcutsIssuable.editIssue);
-
- if (isMergeRequest) {
- this.enabledHelp.push('.hidden-shortcut.merge_requests');
- } else {
- this.enabledHelp.push('.hidden-shortcut.issues');
- }
- }
-
- static replyWithSelectedText() {
- const $replyField = $('.js-main-target-form .js-vue-comment-form');
- const documentFragment = window.gl.utils.getSelectedFragment();
-
- if (!$replyField.length) {
- return false;
- }
-
- if (!documentFragment) {
- $replyField.focus();
- return false;
- }
-
- const el = CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true));
- const selected = CopyAsGFM.nodeToGFM(el);
-
- if (selected.trim() === '') {
- return false;
- }
-
- const quote = _.map(selected.split('\n'), val => `${`> ${val}`.trim()}\n`);
-
- // If replyField already has some content, add a newline before our quote
- const separator = ($replyField.val().trim() !== '' && '\n\n') || '';
- $replyField
- .val((a, current) => `${current}${separator}${quote.join('')}\n`)
- .trigger('input')
- .trigger('change');
-
- // Trigger autosize
- const event = document.createEvent('Event');
- event.initEvent('autosize:update', true, false);
- $replyField.get(0).dispatchEvent(event);
-
- // Focus the input field
- $replyField.focus();
-
- return false;
- }
-
- static editIssue() {
- // Need to click the element as on issues, editing is inline
- // on merge request, editing is on a different page
- document.querySelector('.js-issuable-edit').click();
-
- return false;
- }
-
- static openSidebarDropdown(name) {
- Sidebar.instance.openDropdown(name);
- return false;
- }
-}
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
deleted file mode 100644
index 6b595764bc5..00000000000
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import Mousetrap from 'mousetrap';
-import findAndFollowLink from './shortcuts_dashboard_navigation';
-import Shortcuts from './shortcuts';
-
-export default class ShortcutsNavigation extends Shortcuts {
- constructor() {
- super();
-
- Mousetrap.bind('g p', () => findAndFollowLink('.shortcuts-project'));
- Mousetrap.bind('g v', () => findAndFollowLink('.shortcuts-project-activity'));
- Mousetrap.bind('g f', () => findAndFollowLink('.shortcuts-tree'));
- Mousetrap.bind('g c', () => findAndFollowLink('.shortcuts-commits'));
- Mousetrap.bind('g j', () => findAndFollowLink('.shortcuts-builds'));
- Mousetrap.bind('g n', () => findAndFollowLink('.shortcuts-network'));
- Mousetrap.bind('g d', () => findAndFollowLink('.shortcuts-repository-charts'));
- Mousetrap.bind('g i', () => findAndFollowLink('.shortcuts-issues'));
- Mousetrap.bind('g b', () => findAndFollowLink('.shortcuts-issue-boards'));
- Mousetrap.bind('g m', () => findAndFollowLink('.shortcuts-merge_requests'));
- Mousetrap.bind('g w', () => findAndFollowLink('.shortcuts-wiki'));
- Mousetrap.bind('g s', () => findAndFollowLink('.shortcuts-snippets'));
- Mousetrap.bind('g k', () => findAndFollowLink('.shortcuts-kubernetes'));
- Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-environments'));
- Mousetrap.bind('g l', () => findAndFollowLink('.shortcuts-metrics'));
- Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue'));
-
- this.enabledHelp.push('.hidden-shortcut.project');
- }
-}
diff --git a/app/assets/javascripts/shortcuts_wiki.js b/app/assets/javascripts/shortcuts_wiki.js
deleted file mode 100644
index 41865dcf4ba..00000000000
--- a/app/assets/javascripts/shortcuts_wiki.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import Mousetrap from 'mousetrap';
-import ShortcutsNavigation from './shortcuts_navigation';
-import findAndFollowLink from './shortcuts_dashboard_navigation';
-
-export default class ShortcutsWiki extends ShortcutsNavigation {
- constructor() {
- super();
- Mousetrap.bind('e', ShortcutsWiki.editWiki);
- }
-
- static editWiki() {
- findAndFollowLink('.js-wiki-edit');
- }
-}
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
index dd155c133ce..f1ea6aacdb2 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -74,8 +74,8 @@ export default {
}
if (!this.users.length) {
- const emptyTooltipLabel = this.issuableType === 'issue' ?
- __('Assignee(s)') : __('Assignee');
+ const emptyTooltipLabel =
+ this.issuableType === 'issue' ? __('Assignee(s)') : __('Assignee');
names.push(emptyTooltipLabel);
}
@@ -248,4 +248,3 @@ export default {
</div>
</div>
</template>
-
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
index 123c92aff64..cfa7029b388 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
@@ -69,7 +69,8 @@ export default {
this.loading = false;
}
- this.mediator.saveAssignees(this.field)
+ this.mediator
+ .saveAssignees(this.field)
.then(setLoadingFalse.bind(this))
.catch(() => {
setLoadingFalse();
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index 2b8d6207dea..439e8a69df0 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -56,11 +56,7 @@ export default {
.update('issue', { confidential })
.then(() => window.location.reload())
.catch(() => {
- Flash(
- __(
- 'Something went wrong trying to change the confidentiality of this issue',
- ),
- );
+ Flash(__('Something went wrong trying to change the confidentiality of this issue'));
});
},
},
diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
index 5e7b8f9698f..63082654101 100644
--- a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
+++ b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
@@ -1,4 +1,5 @@
<script>
+import { __ } from '~/locale';
import $ from 'jquery';
import eventHub from '../../event_hub';
@@ -17,7 +18,7 @@ export default {
computed: {
buttonText() {
- return this.isLocked ? this.__('Unlock') : this.__('Lock');
+ return this.isLocked ? __('Unlock') : __('Lock');
},
toggleLock() {
diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
index 8bbc59f623a..48a2b9194aa 100644
--- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
@@ -1,5 +1,5 @@
<script>
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
import Flash from '~/flash';
import tooltip from '~/vue_shared/directives/tooltip';
import issuableMixin from '~/vue_shared/mixins/issuable';
@@ -34,11 +34,7 @@ export default {
required: true,
type: Object,
validator(mediatorObject) {
- return (
- mediatorObject.service &&
- mediatorObject.service.update &&
- mediatorObject.store
- );
+ return mediatorObject.service && mediatorObject.service.update && mediatorObject.store;
},
},
},
@@ -67,8 +63,7 @@ export default {
methods: {
toggleForm() {
- this.mediator.store.isLockDialogOpen = !this.mediator.store
- .isLockDialogOpen;
+ this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen;
},
updateLockedAttribute(locked) {
@@ -79,10 +74,13 @@ export default {
.then(() => window.location.reload())
.catch(() =>
Flash(
- this.__(
- `Something went wrong trying to change the locked state of this ${
- this.issuableDisplayName
- }`,
+ sprintf(
+ __(
+ 'Something went wrong trying to change the locked state of this %{issuableDisplayName}',
+ ),
+ {
+ issuableDisplayName: this.issuableDisplayName,
+ },
),
),
);
@@ -104,7 +102,6 @@ export default {
>
<icon
:name="lockIcon"
- aria-hidden="true"
class="sidebar-item-icon is-active"
/>
</div>
@@ -136,7 +133,6 @@ export default {
<icon
:size="16"
name="lock"
- aria-hidden="true"
class="sidebar-item-icon inline is-active"
/>
{{ __('Locked') }}
@@ -149,7 +145,6 @@ export default {
<icon
:size="16"
name="lock-open"
- aria-hidden="true"
class="sidebar-item-icon inline"
/>
{{ __('Unlocked') }}
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index 56d57f6aac8..43d2f754bac 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -1,80 +1,80 @@
<script>
- import { __, n__, sprintf } from '~/locale';
- import tooltip from '~/vue_shared/directives/tooltip';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
- import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
+import { __, n__, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ userAvatarImage,
+ GlLoadingIcon,
+ },
+ props: {
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- components: {
- loadingIcon,
- userAvatarImage,
+ participants: {
+ type: Array,
+ required: false,
+ default: () => [],
},
- props: {
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- participants: {
- type: Array,
- required: false,
- default: () => [],
- },
- numberOfLessParticipants: {
- type: Number,
- required: false,
- default: 7,
- },
+ numberOfLessParticipants: {
+ type: Number,
+ required: false,
+ default: 7,
},
- data() {
- return {
- isShowingMoreParticipants: false,
- };
+ },
+ data() {
+ return {
+ isShowingMoreParticipants: false,
+ };
+ },
+ computed: {
+ lessParticipants() {
+ return this.participants.slice(0, this.numberOfLessParticipants);
},
- computed: {
- lessParticipants() {
- return this.participants.slice(0, this.numberOfLessParticipants);
- },
- visibleParticipants() {
- return this.isShowingMoreParticipants ? this.participants : this.lessParticipants;
- },
- hasMoreParticipants() {
- return this.participants.length > this.numberOfLessParticipants;
- },
- toggleLabel() {
- let label = '';
- if (this.isShowingMoreParticipants) {
- label = __('- show less');
- } else {
- label = sprintf(__('+ %{moreCount} more'), {
- moreCount: this.participants.length - this.numberOfLessParticipants,
- });
- }
+ visibleParticipants() {
+ return this.isShowingMoreParticipants ? this.participants : this.lessParticipants;
+ },
+ hasMoreParticipants() {
+ return this.participants.length > this.numberOfLessParticipants;
+ },
+ toggleLabel() {
+ let label = '';
+ if (this.isShowingMoreParticipants) {
+ label = __('- show less');
+ } else {
+ label = sprintf(__('+ %{moreCount} more'), {
+ moreCount: this.participants.length - this.numberOfLessParticipants,
+ });
+ }
- return label;
- },
- participantLabel() {
- return sprintf(
- n__('%{count} participant', '%{count} participants', this.participants.length),
- { count: this.loading ? '' : this.participantCount },
- );
- },
- participantCount() {
- return this.participants.length;
- },
+ return label;
+ },
+ participantLabel() {
+ return sprintf(
+ n__('%{count} participant', '%{count} participants', this.participants.length),
+ { count: this.loading ? '' : this.participantCount },
+ );
+ },
+ participantCount() {
+ return this.participants.length;
+ },
+ },
+ methods: {
+ toggleMoreParticipants() {
+ this.isShowingMoreParticipants = !this.isShowingMoreParticipants;
},
- methods: {
- toggleMoreParticipants() {
- this.isShowingMoreParticipants = !this.isShowingMoreParticipants;
- },
- onClickCollapsedIcon() {
- this.$emit('toggleSidebar');
- },
+ onClickCollapsedIcon() {
+ this.$emit('toggleSidebar');
},
- };
+ },
+};
</script>
<template>
@@ -93,7 +93,7 @@
aria-hidden="true"
>
</i>
- <loading-icon
+ <gl-loading-icon
v-if="loading"
class="js-participants-collapsed-loading-icon"
/>
@@ -105,7 +105,7 @@
</span>
</div>
<div class="title hide-collapsed">
- <loading-icon
+ <gl-loading-icon
v-if="loading"
:inline="true"
class="js-participants-expanded-loading-icon"
diff --git a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue
index 5c1ead1a8ac..4ac515e552a 100644
--- a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue
@@ -1,23 +1,23 @@
<script>
- import Store from '../../stores/sidebar_store';
- import participants from './participants.vue';
+import Store from '../../stores/sidebar_store';
+import participants from './participants.vue';
- export default {
- components: {
- participants,
+export default {
+ components: {
+ participants,
+ },
+ props: {
+ mediator: {
+ type: Object,
+ required: true,
},
- props: {
- mediator: {
- type: Object,
- required: true,
- },
- },
- data() {
- return {
- store: new Store(),
- };
- },
- };
+ },
+ data() {
+ return {
+ store: new Store(),
+ };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue
index 385717e7c1e..95a2c8cce6e 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue
@@ -21,10 +21,9 @@ export default {
},
methods: {
onToggleSubscription() {
- this.mediator.toggleSubscription()
- .catch(() => {
- Flash(__('Error occurred when toggling the notification subscription'));
- });
+ this.mediator.toggleSubscription().catch(() => {
+ Flash(__('Error occurred when toggling the notification subscription'));
+ });
},
},
};
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
index 448c8fc3602..b6151aa6c64 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
@@ -1,74 +1,74 @@
<script>
- import { __ } from '~/locale';
- import icon from '~/vue_shared/components/icon.vue';
- import toggleButton from '~/vue_shared/components/toggle_button.vue';
- import tooltip from '~/vue_shared/directives/tooltip';
- import eventHub from '../../event_hub';
+import { __ } from '~/locale';
+import icon from '~/vue_shared/components/icon.vue';
+import toggleButton from '~/vue_shared/components/toggle_button.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import eventHub from '../../event_hub';
- const ICON_ON = 'notifications';
- const ICON_OFF = 'notifications-off';
- const LABEL_ON = __('Notifications on');
- const LABEL_OFF = __('Notifications off');
+const ICON_ON = 'notifications';
+const ICON_OFF = 'notifications-off';
+const LABEL_ON = __('Notifications on');
+const LABEL_OFF = __('Notifications off');
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ icon,
+ toggleButton,
+ },
+ props: {
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- components: {
- icon,
- toggleButton,
+ subscribed: {
+ type: Boolean,
+ required: false,
+ default: null,
},
- props: {
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- subscribed: {
- type: Boolean,
- required: false,
- default: null,
- },
- id: {
- type: Number,
- required: false,
- default: null,
- },
+ id: {
+ type: Number,
+ required: false,
+ default: null,
},
- computed: {
- showLoadingState() {
- return this.subscribed === null;
- },
- notificationIcon() {
- return this.subscribed ? ICON_ON : ICON_OFF;
- },
- notificationTooltip() {
- return this.subscribed ? LABEL_ON : LABEL_OFF;
- },
+ },
+ computed: {
+ showLoadingState() {
+ return this.subscribed === null;
},
- methods: {
- /**
- * We need to emit this event on both component & eventHub
- * for 2 dependencies;
- *
- * 1. eventHub: This component is used in Issue Boards sidebar
- * where component template is part of HAML
- * and event listeners are tied to app's eventHub.
- * 2. Component: This compone is also used in Epics in EE
- * where listeners are tied to component event.
- */
- toggleSubscription() {
- // App's eventHub event emission.
- eventHub.$emit('toggleSubscription', this.id);
+ notificationIcon() {
+ return this.subscribed ? ICON_ON : ICON_OFF;
+ },
+ notificationTooltip() {
+ return this.subscribed ? LABEL_ON : LABEL_OFF;
+ },
+ },
+ methods: {
+ /**
+ * We need to emit this event on both component & eventHub
+ * for 2 dependencies;
+ *
+ * 1. eventHub: This component is used in Issue Boards sidebar
+ * where component template is part of HAML
+ * and event listeners are tied to app's eventHub.
+ * 2. Component: This compone is also used in Epics in EE
+ * where listeners are tied to component event.
+ */
+ toggleSubscription() {
+ // App's eventHub event emission.
+ eventHub.$emit('toggleSubscription', this.id);
- // Component event emission.
- this.$emit('toggleSubscription', this.id);
- },
- onClickCollapsedIcon() {
- this.$emit('toggleSidebar');
- },
+ // Component event emission.
+ this.$emit('toggleSubscription', this.id);
+ },
+ onClickCollapsedIcon() {
+ this.$emit('toggleSidebar');
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
index 1d030c4f67f..259858e4b46 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
@@ -1,111 +1,111 @@
<script>
- import { __, sprintf } from '~/locale';
- import { abbreviateTime } from '~/lib/utils/pretty_time';
- import icon from '~/vue_shared/components/icon.vue';
- import tooltip from '~/vue_shared/directives/tooltip';
+import { __, sprintf } from '~/locale';
+import { abbreviateTime } from '~/lib/utils/datetime_utility';
+import icon from '~/vue_shared/components/icon.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
- export default {
- name: 'TimeTrackingCollapsedState',
- components: {
- icon,
+export default {
+ name: 'TimeTrackingCollapsedState',
+ components: {
+ icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ showComparisonState: {
+ type: Boolean,
+ required: true,
},
- directives: {
- tooltip,
+ showSpentOnlyState: {
+ type: Boolean,
+ required: true,
},
- props: {
- showComparisonState: {
- type: Boolean,
- required: true,
- },
- showSpentOnlyState: {
- type: Boolean,
- required: true,
- },
- showEstimateOnlyState: {
- type: Boolean,
- required: true,
- },
- showNoTimeTrackingState: {
- type: Boolean,
- required: true,
- },
- timeSpentHumanReadable: {
- type: String,
- required: false,
- default: '',
- },
- timeEstimateHumanReadable: {
- type: String,
- required: false,
- default: '',
- },
+ showEstimateOnlyState: {
+ type: Boolean,
+ required: true,
},
- computed: {
- timeSpent() {
- return this.abbreviateTime(this.timeSpentHumanReadable);
- },
- timeEstimate() {
- return this.abbreviateTime(this.timeEstimateHumanReadable);
- },
- divClass() {
- if (this.showComparisonState) {
- return 'compare';
- } else if (this.showEstimateOnlyState) {
- return 'estimate-only';
- } else if (this.showSpentOnlyState) {
- return 'spend-only';
- } else if (this.showNoTimeTrackingState) {
- return 'no-tracking';
- }
+ showNoTimeTrackingState: {
+ type: Boolean,
+ required: true,
+ },
+ timeSpentHumanReadable: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ timeEstimateHumanReadable: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ timeSpent() {
+ return this.abbreviateTime(this.timeSpentHumanReadable);
+ },
+ timeEstimate() {
+ return this.abbreviateTime(this.timeEstimateHumanReadable);
+ },
+ divClass() {
+ if (this.showComparisonState) {
+ return 'compare';
+ } else if (this.showEstimateOnlyState) {
+ return 'estimate-only';
+ } else if (this.showSpentOnlyState) {
+ return 'spend-only';
+ } else if (this.showNoTimeTrackingState) {
+ return 'no-tracking';
+ }
+ return '';
+ },
+ spanClass() {
+ if (this.showComparisonState) {
return '';
- },
- spanClass() {
- if (this.showComparisonState) {
- return '';
- } else if (this.showEstimateOnlyState || this.showSpentOnlyState) {
- return 'bold';
- } else if (this.showNoTimeTrackingState) {
- return 'no-value';
- }
+ } else if (this.showEstimateOnlyState || this.showSpentOnlyState) {
+ return 'bold';
+ } else if (this.showNoTimeTrackingState) {
+ return 'no-value';
+ }
- return '';
- },
- text() {
- if (this.showComparisonState) {
- return `${this.timeSpent} / ${this.timeEstimate}`;
- } else if (this.showEstimateOnlyState) {
- return `-- / ${this.timeEstimate}`;
- } else if (this.showSpentOnlyState) {
- return `${this.timeSpent} / --`;
- } else if (this.showNoTimeTrackingState) {
- return 'None';
- }
+ return '';
+ },
+ text() {
+ if (this.showComparisonState) {
+ return `${this.timeSpent} / ${this.timeEstimate}`;
+ } else if (this.showEstimateOnlyState) {
+ return `-- / ${this.timeEstimate}`;
+ } else if (this.showSpentOnlyState) {
+ return `${this.timeSpent} / --`;
+ } else if (this.showNoTimeTrackingState) {
+ return 'None';
+ }
- return '';
- },
- timeTrackedTooltipText() {
- let title;
- if (this.showComparisonState) {
- title = __('Time remaining');
- } else if (this.showEstimateOnlyState) {
- title = __('Estimated');
- } else if (this.showSpentOnlyState) {
- title = __('Time spent');
- }
+ return '';
+ },
+ timeTrackedTooltipText() {
+ let title;
+ if (this.showComparisonState) {
+ title = __('Time remaining');
+ } else if (this.showEstimateOnlyState) {
+ title = __('Estimated');
+ } else if (this.showSpentOnlyState) {
+ title = __('Time spent');
+ }
- return sprintf('%{title}: %{text}', ({ title, text: this.text }));
- },
- tooltipText() {
- return this.showNoTimeTrackingState ? __('Time tracking') : this.timeTrackedTooltipText;
- },
+ return sprintf('%{title}: %{text}', { title, text: this.text });
+ },
+ tooltipText() {
+ return this.showNoTimeTrackingState ? __('Time tracking') : this.timeTrackedTooltipText;
},
- methods: {
- abbreviateTime(timeStr) {
- return abbreviateTime(timeStr);
- },
+ },
+ methods: {
+ abbreviateTime(timeStr) {
+ return abbreviateTime(timeStr);
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
index dc599e1b9fc..87780411c26 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
@@ -1,9 +1,13 @@
<script>
-import { parseSeconds, stringifyTime } from '../../../lib/utils/pretty_time';
+import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
import tooltip from '../../../vue_shared/directives/tooltip';
+import { GlProgressBar } from '@gitlab/ui';
export default {
name: 'TimeTrackingComparisonPane',
+ components: {
+ GlProgressBar,
+ },
directives: {
tooltip,
},
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
index 19ec0f05a26..91909cd49b8 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
@@ -15,16 +15,22 @@ export default {
},
estimateText() {
return sprintf(
- s__('estimateCommand|%{slash_command} will update the estimated time with the latest command.'), {
+ s__(
+ 'estimateCommand|%{slash_command} will update the estimated time with the latest command.',
+ ),
+ {
slash_command: '<code>/estimate</code>',
- }, false,
+ },
+ false,
);
},
spendText() {
return sprintf(
- s__('spendCommand|%{slash_command} will update the sum of the time spent.'), {
+ s__('spendCommand|%{slash_command} will update the sum of the time spent.'),
+ {
slash_command: '<code>/spend</code>',
- }, false,
+ },
+ false,
);
},
},
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue
index 2e1d6e9643a..8e8b9f19b6e 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue
@@ -26,7 +26,7 @@ export default {
methods: {
listenForQuickActions() {
$(document).on('ajax:success', '.gfm-form', this.quickActionListened);
- eventHub.$on('timeTrackingUpdated', (data) => {
+ eventHub.$on('timeTrackingUpdated', data => {
this.quickActionListened(null, data);
});
},
@@ -34,9 +34,7 @@ export default {
const subscribedCommands = ['spend_time', 'time_estimate'];
let changedCommands;
if (data !== undefined) {
- changedCommands = data.commands_changes
- ? Object.keys(data.commands_changes)
- : [];
+ changedCommands = data.commands_changes ? Object.keys(data.commands_changes) : [];
} else {
changedCommands = [];
}
@@ -51,10 +49,10 @@ export default {
<template>
<div class="block">
<issuable-time-tracker
- :time_estimate="store.timeEstimate"
- :time_spent="store.totalTimeSpent"
- :human_time_estimate="store.humanTimeEstimate"
- :human_time_spent="store.humanTotalTimeSpent"
+ :time-estimate="store.timeEstimate"
+ :time-spent="store.totalTimeSpent"
+ :human-time-estimate="store.humanTimeEstimate"
+ :human-time-spent="store.humanTotalTimeSpent"
:root-path="store.rootPath"
/>
</div>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index ca3b9338c29..ef76dc13ce9 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -19,20 +19,20 @@ export default {
TimeTrackingHelpState,
},
props: {
- time_estimate: {
+ timeEstimate: {
type: Number,
required: true,
},
- time_spent: {
+ timeSpent: {
type: Number,
required: true,
},
- human_time_estimate: {
+ humanTimeEstimate: {
type: String,
required: false,
default: '',
},
- human_time_spent: {
+ humanTimeSpent: {
type: String,
required: false,
default: '',
@@ -48,18 +48,6 @@ export default {
};
},
computed: {
- timeSpent() {
- return this.time_spent;
- },
- timeEstimate() {
- return this.time_estimate;
- },
- timeEstimateHumanReadable() {
- return this.human_time_estimate;
- },
- timeSpentHumanReadable() {
- return this.human_time_spent;
- },
hasTimeSpent() {
return !!this.timeSpent;
},
@@ -90,10 +78,12 @@ export default {
this.showHelp = show;
},
update(data) {
- this.time_estimate = data.time_estimate;
- this.time_spent = data.time_spent;
- this.human_time_estimate = data.human_time_estimate;
- this.human_time_spent = data.human_time_spent;
+ const { timeEstimate, timeSpent, humanTimeEstimate, humanTimeSpent } = data;
+
+ this.timeEstimate = timeEstimate;
+ this.timeSpent = timeSpent;
+ this.humanTimeEstimate = humanTimeEstimate;
+ this.humanTimeSpent = humanTimeSpent;
},
},
};
@@ -110,8 +100,8 @@ export default {
:show-help-state="showHelpState"
:show-spent-only-state="showSpentOnlyState"
:show-estimate-only-state="showEstimateOnlyState"
- :time-spent-human-readable="timeSpentHumanReadable"
- :time-estimate-human-readable="timeEstimateHumanReadable"
+ :time-spent-human-readable="humanTimeSpent"
+ :time-estimate-human-readable="humanTimeEstimate"
/>
<div class="title hide-collapsed">
{{ __('Time tracking') }}
@@ -141,11 +131,11 @@ export default {
<div class="time-tracking-content hide-collapsed">
<time-tracking-estimate-only-pane
v-if="showEstimateOnlyState"
- :time-estimate-human-readable="timeEstimateHumanReadable"
+ :time-estimate-human-readable="humanTimeEstimate"
/>
<time-tracking-spent-only-pane
v-if="showSpentOnlyState"
- :time-spent-human-readable="timeSpentHumanReadable"
+ :time-spent-human-readable="humanTimeSpent"
/>
<time-tracking-no-tracking-pane
v-if="showNoTimeTrackingState"
@@ -154,8 +144,8 @@ export default {
v-if="showComparisonState"
:time-estimate="timeEstimate"
:time-spent="timeSpent"
- :time-spent-human-readable="timeSpentHumanReadable"
- :time-estimate-human-readable="timeEstimateHumanReadable"
+ :time-spent-human-readable="humanTimeSpent"
+ :time-estimate-human-readable="humanTimeEstimate"
/>
<transition name="help-state-toggle">
<time-tracking-help-state
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
index ffaed9c7193..7edef35461a 100644
--- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
@@ -1,9 +1,9 @@
<script>
import { __ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
+import { GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
const MARK_TEXT = __('Mark todo as done');
const TODO_TEXT = __('Add todo');
@@ -14,7 +14,7 @@ export default {
},
components: {
Icon,
- LoadingIcon,
+ GlLoadingIcon,
},
props: {
issuableId: {
@@ -43,9 +43,9 @@ export default {
},
computed: {
buttonClasses() {
- return this.collapsed ?
- 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state' :
- 'btn btn-default btn-todo issuable-header-btn float-right';
+ return this.collapsed
+ ? 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state'
+ : 'btn btn-default btn-todo issuable-header-btn float-right';
},
buttonLabel() {
return this.isTodo ? MARK_TEXT : TODO_TEXT;
@@ -90,7 +90,7 @@ export default {
>
{{ buttonLabel }}
</span>
- <loading-icon
+ <gl-loading-icon
v-show="isActionActive"
:inline="true"
/>
diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
index b267422cd97..225ebb61195 100644
--- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
+++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
@@ -37,7 +37,8 @@ class SidebarMoveIssue {
// Keep the dropdown open after selecting an option
shouldPropagate: false,
data: (searchTerm, callback) => {
- this.mediator.fetchAutocompleteProjects(searchTerm)
+ this.mediator
+ .fetchAutocompleteProjects(searchTerm)
.then(callback)
.catch(() => new window.Flash('An error occurred while fetching projects autocomplete.'));
},
@@ -48,7 +49,7 @@ class SidebarMoveIssue {
</a>
</li>
`,
- clicked: (options) => {
+ clicked: options => {
const project = options.selectedObj;
const selectedProjectId = options.isMarking ? project.id : 0;
this.mediator.setMoveToProjectId(selectedProjectId);
@@ -68,17 +69,12 @@ class SidebarMoveIssue {
onConfirmClicked() {
if (isValidProjectId(this.mediator.store.moveToProjectId)) {
- this.$confirmButton
- .disable()
- .addClass('is-loading');
+ this.$confirmButton.disable().addClass('is-loading');
- this.mediator.moveIssue()
- .catch(() => {
- window.Flash('An error occurred while moving the issue.');
- this.$confirmButton
- .enable()
- .removeClass('is-loading');
- });
+ this.mediator.moveIssue().catch(() => {
+ window.Flash('An error occurred while moving the issue.');
+ this.$confirmButton.enable().removeClass('is-loading');
+ });
}
}
}
diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
index b15ad0e5586..1ebdbec7bc9 100644
--- a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
@@ -7,21 +7,24 @@ export default class SidebarMilestone {
if (!el) return;
+ const { timeEstimate, timeSpent, humanTimeEstimate, humanTimeSpent } = el.dataset;
+
// eslint-disable-next-line no-new
new Vue({
el,
components: {
timeTracker,
},
- render: createElement => createElement('timeTracker', {
- props: {
- time_estimate: parseInt(el.dataset.timeEstimate, 10),
- time_spent: parseInt(el.dataset.timeSpent, 10),
- human_time_estimate: el.dataset.humanTimeEstimate,
- human_time_spent: el.dataset.humanTimeSpent,
- rootPath: '/',
- },
- }),
+ render: createElement =>
+ createElement('timeTracker', {
+ props: {
+ timeEstimate: parseInt(timeEstimate, 10),
+ timeSpent: parseInt(timeSpent, 10),
+ humanTimeEstimate,
+ humanTimeSpent,
+ rootPath: '/',
+ },
+ }),
});
}
}
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 655bf9198b7..6f8214b18ee 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -22,14 +22,15 @@ function mountAssigneesComponent(mediator) {
components: {
SidebarAssignees,
},
- render: createElement => createElement('sidebar-assignees', {
- props: {
- mediator,
- field: el.dataset.field,
- signedIn: el.hasAttribute('data-signed-in'),
- issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request',
- },
- }),
+ render: createElement =>
+ createElement('sidebar-assignees', {
+ props: {
+ mediator,
+ field: el.dataset.field,
+ signedIn: el.hasAttribute('data-signed-in'),
+ issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request',
+ },
+ }),
});
}
@@ -83,11 +84,12 @@ function mountParticipantsComponent(mediator) {
components: {
sidebarParticipants,
},
- render: createElement => createElement('sidebar-participants', {
- props: {
- mediator,
- },
- }),
+ render: createElement =>
+ createElement('sidebar-participants', {
+ props: {
+ mediator,
+ },
+ }),
});
}
@@ -102,11 +104,12 @@ function mountSubscriptionsComponent(mediator) {
components: {
sidebarSubscriptions,
},
- render: createElement => createElement('sidebar-subscriptions', {
- props: {
- mediator,
- },
- }),
+ render: createElement =>
+ createElement('sidebar-subscriptions', {
+ props: {
+ mediator,
+ },
+ }),
});
}
diff --git a/app/assets/javascripts/sidebar/services/sidebar_service.js b/app/assets/javascripts/sidebar/services/sidebar_service.js
index 37c97225bfd..cbe20f761ff 100644
--- a/app/assets/javascripts/sidebar/services/sidebar_service.js
+++ b/app/assets/javascripts/sidebar/services/sidebar_service.js
@@ -22,11 +22,15 @@ export default class SidebarService {
}
update(key, data) {
- return Vue.http.put(this.endpoint, {
- [key]: data,
- }, {
- emulateJSON: true,
- });
+ return Vue.http.put(
+ this.endpoint,
+ {
+ [key]: data,
+ },
+ {
+ emulateJSON: true,
+ },
+ );
}
getProjectsAutocomplete(searchTerm) {
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index d9ca5e46770..3e040ec8428 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -39,9 +39,10 @@ export default class SidebarMediator {
}
fetch() {
- return this.service.get()
+ return this.service
+ .get()
.then(response => response.json())
- .then((data) => {
+ .then(data => {
this.processFetchedData(data);
})
.catch(() => new Flash('Error occurred when fetching sidebar data'));
@@ -56,30 +57,33 @@ export default class SidebarMediator {
toggleSubscription() {
this.store.setFetchingState('subscriptions', true);
- return this.service.toggleSubscription()
+ return this.service
+ .toggleSubscription()
.then(() => {
this.store.setSubscribedState(!this.store.subscribed);
this.store.setFetchingState('subscriptions', false);
})
- .catch((err) => {
+ .catch(err => {
this.store.setFetchingState('subscriptions', false);
throw err;
});
}
fetchAutocompleteProjects(searchTerm) {
- return this.service.getProjectsAutocomplete(searchTerm)
+ return this.service
+ .getProjectsAutocomplete(searchTerm)
.then(response => response.json())
- .then((data) => {
+ .then(data => {
this.store.setAutocompleteProjects(data);
return this.store.autocompleteProjects;
});
}
moveIssue() {
- return this.service.moveIssue(this.store.moveToProjectId)
+ return this.service
+ .moveIssue(this.store.moveToProjectId)
.then(response => response.json())
- .then((data) => {
+ .then(data => {
if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 99c93952e2a..f2b9d75dd00 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -10,8 +10,10 @@ import syntaxHighlight from './syntax_highlight';
const WRAPPER = '<div class="diff-content"></div>';
const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
-const ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
-const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <button class="click-to-expand btn btn-link">Click to expand it.</button></div>';
+const ERROR_HTML =
+ '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
+const COLLAPSED_HTML =
+ '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <button class="click-to-expand btn btn-link">Click to expand it.</button></div>';
export default class SingleFileDiff {
constructor(file) {
@@ -23,23 +25,36 @@ export default class SingleFileDiff {
this.isOpen = !this.diffForPath;
if (this.diffForPath) {
this.collapsedContent = this.content;
- this.loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide();
+ this.loadingContent = $(WRAPPER)
+ .addClass('loading')
+ .html(LOADING_HTML)
+ .hide();
this.content = null;
this.collapsedContent.after(this.loadingContent);
this.$toggleIcon.addClass('fa-caret-right');
} else {
- this.collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide();
+ this.collapsedContent = $(WRAPPER)
+ .html(COLLAPSED_HTML)
+ .hide();
this.content.after(this.collapsedContent);
this.$toggleIcon.addClass('fa-caret-down');
}
- $('.js-file-title, .click-to-expand', this.file).on('click', (function (e) {
- this.toggleDiff($(e.target));
- }).bind(this));
+ $('.js-file-title, .click-to-expand', this.file).on(
+ 'click',
+ function(e) {
+ this.toggleDiff($(e.target));
+ }.bind(this),
+ );
}
toggleDiff($target, cb) {
- if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
+ if (
+ !$target.hasClass('js-file-title') &&
+ !$target.hasClass('click-to-expand') &&
+ !$target.hasClass('diff-toggle-caret')
+ )
+ return;
this.isOpen = !this.isOpen;
if (!this.isOpen && !this.hasError) {
this.content.hide();
@@ -65,7 +80,8 @@ export default class SingleFileDiff {
this.collapsedContent.hide();
this.loadingContent.show();
- axios.get(this.diffForPath)
+ axios
+ .get(this.diffForPath)
.then(({ data }) => {
this.loadingContent.hide();
if (data.html) {
diff --git a/app/assets/javascripts/smart_interval.js b/app/assets/javascripts/smart_interval.js
index 5e385400747..8ca590123ae 100644
--- a/app/assets/javascripts/smart_interval.js
+++ b/app/assets/javascripts/smart_interval.js
@@ -93,7 +93,9 @@ export default class SmartInterval {
destroy() {
this.cancel();
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
- $(document).off('visibilitychange').off('beforeunload');
+ $(document)
+ .off('visibilitychange')
+ .off('beforeunload');
}
/* private */
@@ -111,11 +113,12 @@ export default class SmartInterval {
triggerCallback() {
this.isLoading = true;
- this.cfg.callback()
+ this.cfg
+ .callback()
.then(() => {
this.isLoading = false;
})
- .catch((err) => {
+ .catch(err => {
this.isLoading = false;
throw err;
});
@@ -134,9 +137,9 @@ export default class SmartInterval {
handleVisibilityChange(e) {
this.state.pageVisibility = e.target.visibilityState;
- const intervalAction = this.isPageVisible() ?
- this.onVisibilityVisible :
- this.onVisibilityHidden;
+ const intervalAction = this.isPageVisible()
+ ? this.onVisibilityVisible
+ : this.onVisibilityHidden;
intervalAction.apply(this);
}
@@ -162,7 +165,9 @@ export default class SmartInterval {
this.setCurrentInterval(nextInterval);
}
- isPageVisible() { return this.state.pageVisibility === 'visible'; }
+ isPageVisible() {
+ return this.state.pageVisibility === 'visible';
+ }
stopTimer() {
const { state } = this;
@@ -170,4 +175,3 @@ export default class SmartInterval {
state.intervalId = window.clearInterval(state.intervalId);
}
}
-
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index f5a7fdae5d7..007b83e1927 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -11,10 +11,14 @@ export default class Star {
const $starSpan = $this.find('span');
const $startIcon = $this.find('svg');
- axios.post($this.data('endpoint'))
+ axios
+ .post($this.data('endpoint'))
.then(({ data }) => {
const isStarred = $starSpan.hasClass('starred');
- $this.parent().find('.star-count').text(data.star_count);
+ $this
+ .parent()
+ .find('.star-count')
+ .text(data.star_count);
if (isStarred) {
$starSpan.removeClass('starred').text(s__('StarProject|Star'));
diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js
index 48782e63b9b..edefb3735d7 100644
--- a/app/assets/javascripts/task_list.js
+++ b/app/assets/javascripts/task_list.js
@@ -26,7 +26,11 @@ export default class TaskList {
// Prevent duplicate event bindings
this.disable();
$(`${this.selector} .js-task-list-container`).taskList('enable');
- $(document).on('tasklist:changed', `${this.selector} .js-task-list-container`, this.update.bind(this));
+ $(document).on(
+ 'tasklist:changed',
+ `${this.selector} .js-task-list-container`,
+ this.update.bind(this),
+ );
}
disable() {
@@ -41,7 +45,8 @@ export default class TaskList {
[this.fieldName]: $target.val(),
};
- return axios.patch($target.data('updateUrl') || $('form.js-issuable-update').attr('action'), patchData)
+ return axios
+ .patch($target.data('updateUrl') || $('form.js-issuable-update').attr('action'), patchData)
.then(({ data }) => this.onSuccess(data))
.catch(err => this.onError(err));
}
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js
index 6fea03af46a..6065770e68d 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js
+++ b/app/assets/javascripts/templates/issuable_template_selector.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-useless-return, max-len */
+/* eslint-disable no-useless-return */
import $ from 'jquery';
import Api from '../api';
@@ -31,12 +31,18 @@ export default class IssuableTemplateSelector extends TemplateSelector {
requestFile(query) {
this.startLoadingSpinner();
- Api.issueTemplate(this.namespacePath, this.projectPath, query.name, this.issuableType, (err, currentTemplate) => {
- this.currentTemplate = currentTemplate;
- this.stopLoadingSpinner();
- if (err) return; // Error handled by global AJAX error handler
- this.setInputValueToTemplateContent();
- });
+ Api.issueTemplate(
+ this.namespacePath,
+ this.projectPath,
+ query.name,
+ this.issuableType,
+ (err, currentTemplate) => {
+ this.currentTemplate = currentTemplate;
+ this.stopLoadingSpinner();
+ if (err) return; // Error handled by global AJAX error handler
+ this.setInputValueToTemplateContent();
+ },
+ );
return;
}
diff --git a/app/assets/javascripts/terminal/terminal.js b/app/assets/javascripts/terminal/terminal.js
index 74c5bbe45a4..b24aa8a3a34 100644
--- a/app/assets/javascripts/terminal/terminal.js
+++ b/app/assets/javascripts/terminal/terminal.js
@@ -4,10 +4,14 @@ import * as fit from 'xterm/lib/addons/fit/fit';
export default class GLTerminal {
constructor(options = {}) {
- this.options = Object.assign({}, {
- cursorBlink: true,
- screenKeys: true,
- }, options);
+ this.options = Object.assign(
+ {},
+ {
+ cursorBlink: true,
+ screenKeys: true,
+ },
+ options,
+ );
this.container = document.querySelector(options.selector);
diff --git a/app/assets/javascripts/test_utils/simulate_drag.js b/app/assets/javascripts/test_utils/simulate_drag.js
index a5c18042ce7..be9ebc81c6b 100644
--- a/app/assets/javascripts/test_utils/simulate_drag.js
+++ b/app/assets/javascripts/test_utils/simulate_drag.js
@@ -4,15 +4,43 @@ function simulateEvent(el, type, options = {}) {
if (/^mouse/.test(type)) {
event = el.ownerDocument.createEvent('MouseEvents');
- event.initMouseEvent(type, true, true, el.ownerDocument.defaultView,
- options.button, options.screenX, options.screenY, options.clientX, options.clientY,
- options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
+ event.initMouseEvent(
+ type,
+ true,
+ true,
+ el.ownerDocument.defaultView,
+ options.button,
+ options.screenX,
+ options.screenY,
+ options.clientX,
+ options.clientY,
+ options.ctrlKey,
+ options.altKey,
+ options.shiftKey,
+ options.metaKey,
+ options.button,
+ el,
+ );
} else {
event = el.ownerDocument.createEvent('CustomEvent');
- event.initCustomEvent(type, true, true, el.ownerDocument.defaultView,
- options.button, options.screenX, options.screenY, options.clientX, options.clientY,
- options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
+ event.initCustomEvent(
+ type,
+ true,
+ true,
+ el.ownerDocument.defaultView,
+ options.button,
+ options.screenX,
+ options.screenY,
+ options.clientX,
+ options.clientY,
+ options.ctrlKey,
+ options.altKey,
+ options.shiftKey,
+ options.metaKey,
+ options.button,
+ el,
+ );
event.dataTransfer = {
data: {},
@@ -37,14 +65,16 @@ function simulateEvent(el, type, options = {}) {
}
function isLast(target) {
- const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
+ const el =
+ typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
const { children } = el;
return children.length - 1 === target.index;
}
function getTarget(target) {
- const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
+ const el =
+ typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
const { children } = el;
return (
@@ -58,13 +88,13 @@ function getTarget(target) {
function getRect(el) {
const rect = el.getBoundingClientRect();
const width = rect.right - rect.left;
- const height = (rect.bottom - rect.top) + 10;
+ const height = rect.bottom - rect.top + 10;
return {
x: rect.left,
y: rect.top,
- cx: rect.left + (width / 2),
- cy: rect.top + (height / 2),
+ cx: rect.left + width / 2,
+ cy: rect.top + height / 2,
w: width,
h: height,
hw: width / 2,
@@ -112,8 +142,8 @@ export default function simulateDrag(options) {
const dragInterval = setInterval(() => {
const progress = (new Date().getTime() - startTime) / duration;
- const x = (fromRect.cx + ((toRect.cx - fromRect.cx) * progress));
- const y = (fromRect.cy + ((toRect.cy - fromRect.cy) * progress));
+ const x = fromRect.cx + (toRect.cx - fromRect.cx) * progress;
+ const y = fromRect.cy + (toRect.cy - fromRect.cy) * progress;
const overEl = fromEl.ownerDocument.elementFromPoint(x, y);
simulateEvent(overEl, 'mousemove', {
diff --git a/app/assets/javascripts/test_utils/simulate_input.js b/app/assets/javascripts/test_utils/simulate_input.js
index 90c1b7cb57e..c300c806e6d 100644
--- a/app/assets/javascripts/test_utils/simulate_input.js
+++ b/app/assets/javascripts/test_utils/simulate_input.js
@@ -12,7 +12,7 @@ export default function simulateInput(target, text) {
}
if (text.length > 0) {
- Array.prototype.forEach.call(text, (char) => {
+ Array.prototype.forEach.call(text, char => {
input.value += char;
triggerEvents(input);
});
diff --git a/app/assets/javascripts/toggle_buttons.js b/app/assets/javascripts/toggle_buttons.js
index 199b14458ed..d83ffc7e211 100644
--- a/app/assets/javascripts/toggle_buttons.js
+++ b/app/assets/javascripts/toggle_buttons.js
@@ -49,7 +49,7 @@ function onToggleClicked(toggle, input, clickCallback) {
export default function setupToggleButtons(container, clickCallback = () => {}) {
const toggles = container.querySelectorAll('.js-project-feature-toggle');
- toggles.forEach((toggle) => {
+ toggles.forEach(toggle => {
const input = toggle.querySelector('.js-project-feature-toggle-input');
const isOn = convertPermissionToBoolean(input.value);
diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js
index 85123a63a45..3e659c9e7ea 100644
--- a/app/assets/javascripts/tree.js
+++ b/app/assets/javascripts/tree.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, class-methods-use-this */
+/* eslint-disable func-names, consistent-return, no-var, one-var, no-else-return, prefer-arrow-callback, class-methods-use-this */
import $ from 'jquery';
import { visitUrl } from './lib/utils/url_utility';
@@ -8,7 +8,7 @@ export default class TreeView {
this.initKeyNav();
// Code browser tree slider
// Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
- $(".tree-content-holder .tree-item").on('click', function(e) {
+ $('.tree-content-holder .tree-item').on('click', function(e) {
var $clickedEl, path;
$clickedEl = $(e.target);
path = $('.tree-item-file-name a', this).attr('href');
@@ -27,33 +27,33 @@ export default class TreeView {
initKeyNav() {
var li, liSelected;
- li = $("tr.tree-item");
+ li = $('tr.tree-item');
liSelected = null;
return $('body').keydown(function(e) {
var next, path;
- if ($("input:focus").length > 0 && (e.which === 38 || e.which === 40)) {
+ if ($('input:focus').length > 0 && (e.which === 38 || e.which === 40)) {
return false;
}
if (e.which === 40) {
if (liSelected) {
next = liSelected.next();
if (next.length > 0) {
- liSelected.removeClass("selected");
- liSelected = next.addClass("selected");
+ liSelected.removeClass('selected');
+ liSelected = next.addClass('selected');
}
} else {
- liSelected = li.eq(0).addClass("selected");
+ liSelected = li.eq(0).addClass('selected');
}
return $(liSelected).focus();
} else if (e.which === 38) {
if (liSelected) {
next = liSelected.prev();
if (next.length > 0) {
- liSelected.removeClass("selected");
- liSelected = next.addClass("selected");
+ liSelected.removeClass('selected');
+ liSelected = next.addClass('selected');
}
} else {
- liSelected = li.last().addClass("selected");
+ liSelected = li.last().addClass('selected');
}
return $(liSelected).focus();
} else if (e.which === 13) {
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index 78fd7ad441f..abfc81e681e 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -49,7 +49,7 @@ export default class U2FAuthenticate {
start() {
return importU2FLibrary()
- .then((utils) => {
+ .then(utils => {
this.u2fUtils = utils;
this.renderInProgress();
})
@@ -57,14 +57,19 @@ export default class U2FAuthenticate {
}
authenticate() {
- return this.u2fUtils.sign(this.appId, this.challenge, this.signRequests,
- (response) => {
+ return this.u2fUtils.sign(
+ this.appId,
+ this.challenge,
+ this.signRequests,
+ response => {
if (response.errorCode) {
const error = new U2FError(response.errorCode, 'authenticate');
return this.renderError(error);
}
return this.renderAuthenticated(JSON.stringify(response));
- }, 10);
+ },
+ 10,
+ );
}
renderTemplate(name, params) {
@@ -99,5 +104,4 @@ export default class U2FAuthenticate {
this.container[0].classList.add('hidden');
this.fallbackUI.classList.remove('hidden');
}
-
}
diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js
index 01e259a741d..43c814c8070 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -34,7 +34,7 @@ export default class U2FRegister {
start() {
return importU2FLibrary()
- .then((utils) => {
+ .then(utils => {
this.u2fUtils = utils;
this.renderSetup();
})
@@ -42,14 +42,19 @@ export default class U2FRegister {
}
register() {
- return this.u2fUtils.register(this.appId, this.registerRequests, this.signRequests,
- (response) => {
+ return this.u2fUtils.register(
+ this.appId,
+ this.registerRequests,
+ this.signRequests,
+ response => {
if (response.errorCode) {
const error = new U2FError(response.errorCode, 'register');
return this.renderError(error);
}
return this.renderRegistered(JSON.stringify(response));
- }, 10);
+ },
+ 10,
+ );
}
renderTemplate(name, params) {
diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js
index 5778f00332d..b706481c02f 100644
--- a/app/assets/javascripts/u2f/util.js
+++ b/app/assets/javascripts/u2f/util.js
@@ -19,11 +19,10 @@ function getChromeVersion(userAgent) {
export function canInjectU2fApi(userAgent) {
const isSupportedChrome = isChrome(userAgent) && getChromeVersion(userAgent) >= 41;
const isSupportedOpera = isOpera(userAgent) && getOperaVersion(userAgent) >= 40;
- const isMobile = (
+ const isMobile =
userAgent.indexOf('droid') >= 0 ||
userAgent.indexOf('CriOS') >= 0 ||
- /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent)
- );
+ /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
return (isSupportedChrome || isSupportedOpera) && !isMobile;
}
diff --git a/app/assets/javascripts/ui_development_kit.js b/app/assets/javascripts/ui_development_kit.js
index 9b242ea779d..be18ac5da24 100644
--- a/app/assets/javascripts/ui_development_kit.js
+++ b/app/assets/javascripts/ui_development_kit.js
@@ -4,13 +4,17 @@ import Api from './api';
export default () => {
$('#js-project-dropdown').glDropdown({
data: (term, callback) => {
- Api.projects(term, {
- order_by: 'last_activity_at',
- }, (data) => {
- callback(data);
- });
+ Api.projects(
+ term,
+ {
+ order_by: 'last_activity_at',
+ },
+ data => {
+ callback(data);
+ },
+ );
},
- text: project => (project.name_with_namespace || project.name),
+ text: project => project.name_with_namespace || project.name,
selectable: true,
fieldName: 'author_id',
filterable: true,
@@ -18,6 +22,6 @@ export default () => {
fields: ['name_with_namespace'],
},
id: data => data.id,
- isSelected: data => (data.id === 2),
+ isSelected: data => data.id === 2,
});
};
diff --git a/app/assets/javascripts/usage_ping_consent.js b/app/assets/javascripts/usage_ping_consent.js
new file mode 100644
index 00000000000..05607f09a7e
--- /dev/null
+++ b/app/assets/javascripts/usage_ping_consent.js
@@ -0,0 +1,31 @@
+import $ from 'jquery';
+import axios from './lib/utils/axios_utils';
+import Flash, { hideFlash } from './flash';
+import { convertPermissionToBoolean } from './lib/utils/common_utils';
+
+export default () => {
+ $('body').on('click', '.js-usage-consent-action', e => {
+ e.preventDefault();
+ e.stopImmediatePropagation(); // overwrite rails listener
+
+ const { url, checkEnabled, pingEnabled } = e.target.dataset;
+ const data = {
+ application_setting: {
+ version_check_enabled: convertPermissionToBoolean(checkEnabled),
+ usage_ping_enabled: convertPermissionToBoolean(pingEnabled),
+ },
+ };
+
+ const hideConsentMessage = () => hideFlash(document.querySelector('.ping-consent-message'));
+
+ axios
+ .put(url, data)
+ .then(() => {
+ hideConsentMessage();
+ })
+ .catch(() => {
+ hideConsentMessage();
+ Flash('Something went wrong. Try again later.');
+ });
+ });
+};
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index e19bbbacf4d..ce051582299 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, one-var, no-var, prefer-rest-params, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, yoda, prefer-spread, no-void, camelcase, no-param-reassign */
+/* eslint-disable func-names, one-var, no-var, prefer-rest-params, vars-on-top, prefer-arrow-callback, consistent-return, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, yoda, prefer-spread, camelcase, no-param-reassign */
/* global Issuable */
/* global emitSidebarEvent */
@@ -15,8 +15,8 @@ function UsersSelect(currentUser, els, options = {}) {
var $els;
this.users = this.users.bind(this);
this.user = this.user.bind(this);
- this.usersPath = "/autocomplete/users.json";
- this.userPath = "/autocomplete/users/:id.json";
+ this.usersPath = '/autocomplete/users.json';
+ this.userPath = '/autocomplete/users/:id.json';
if (currentUser != null) {
if (typeof currentUser === 'object') {
this.currentUser = currentUser;
@@ -33,156 +33,180 @@ function UsersSelect(currentUser, els, options = {}) {
$els = $('.js-user-search');
}
- $els.each((function(_this) {
- return function(i, dropdown) {
- var options = {};
- var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, defaultNullUser, firstUser, issueURL, selectedId, selectedIdDefault, showAnyUser, showNullUser, showMenuAbove;
- $dropdown = $(dropdown);
- options.projectId = $dropdown.data('projectId');
- options.groupId = $dropdown.data('groupId');
- options.showCurrentUser = $dropdown.data('currentUser');
- options.todoFilter = $dropdown.data('todoFilter');
- options.todoStateFilter = $dropdown.data('todoStateFilter');
- showNullUser = $dropdown.data('nullUser');
- defaultNullUser = $dropdown.data('nullUserDefault');
- showMenuAbove = $dropdown.data('showMenuAbove');
- showAnyUser = $dropdown.data('anyUser');
- firstUser = $dropdown.data('firstUser');
- options.authorId = $dropdown.data('authorId');
- defaultLabel = $dropdown.data('defaultLabel');
- issueURL = $dropdown.data('issueUpdate');
- $selectbox = $dropdown.closest('.selectbox');
- $block = $selectbox.closest('.block');
- abilityName = $dropdown.data('abilityName');
- $value = $block.find('.value');
- $collapsedSidebar = $block.find('.sidebar-collapsed-user');
- $loading = $block.find('.block-loading').fadeOut();
- selectedIdDefault = (defaultNullUser && showNullUser) ? 0 : null;
- selectedId = $dropdown.data('selected');
-
- if (selectedId === undefined) {
- selectedId = selectedIdDefault;
- }
-
- const assignYourself = function () {
- const unassignedSelected = $dropdown.closest('.selectbox')
- .find(`input[name='${$dropdown.data('fieldName')}'][value=0]`);
-
- if (unassignedSelected) {
- unassignedSelected.remove();
+ $els.each(
+ (function(_this) {
+ return function(i, dropdown) {
+ var options = {};
+ var $block,
+ $collapsedSidebar,
+ $dropdown,
+ $loading,
+ $selectbox,
+ $value,
+ abilityName,
+ assignTo,
+ assigneeTemplate,
+ collapsedAssigneeTemplate,
+ defaultLabel,
+ defaultNullUser,
+ firstUser,
+ issueURL,
+ selectedId,
+ selectedIdDefault,
+ showAnyUser,
+ showNullUser,
+ showMenuAbove;
+ $dropdown = $(dropdown);
+ options.projectId = $dropdown.data('projectId');
+ options.groupId = $dropdown.data('groupId');
+ options.showCurrentUser = $dropdown.data('currentUser');
+ options.todoFilter = $dropdown.data('todoFilter');
+ options.todoStateFilter = $dropdown.data('todoStateFilter');
+ showNullUser = $dropdown.data('nullUser');
+ defaultNullUser = $dropdown.data('nullUserDefault');
+ showMenuAbove = $dropdown.data('showMenuAbove');
+ showAnyUser = $dropdown.data('anyUser');
+ firstUser = $dropdown.data('firstUser');
+ options.authorId = $dropdown.data('authorId');
+ defaultLabel = $dropdown.data('defaultLabel');
+ issueURL = $dropdown.data('issueUpdate');
+ $selectbox = $dropdown.closest('.selectbox');
+ $block = $selectbox.closest('.block');
+ abilityName = $dropdown.data('abilityName');
+ $value = $block.find('.value');
+ $collapsedSidebar = $block.find('.sidebar-collapsed-user');
+ $loading = $block.find('.block-loading').fadeOut();
+ selectedIdDefault = defaultNullUser && showNullUser ? 0 : null;
+ selectedId = $dropdown.data('selected');
+
+ if (selectedId === undefined) {
+ selectedId = selectedIdDefault;
}
- // Save current selected user to the DOM
- const input = document.createElement('input');
- input.type = 'hidden';
- input.name = $dropdown.data('fieldName');
+ const assignYourself = function() {
+ const unassignedSelected = $dropdown
+ .closest('.selectbox')
+ .find(`input[name='${$dropdown.data('fieldName')}'][value=0]`);
- const currentUserInfo = $dropdown.data('currentUserInfo');
-
- if (currentUserInfo) {
- input.value = currentUserInfo.id;
- input.dataset.meta = _.escape(currentUserInfo.name);
- } else if (_this.currentUser) {
- input.value = _this.currentUser.id;
- }
-
- if ($selectbox) {
- $dropdown.parent().before(input);
- } else {
- $dropdown.after(input);
- }
- };
-
- if ($block[0]) {
- $block[0].addEventListener('assignYourself', assignYourself);
- }
-
- const getSelectedUserInputs = function() {
- return $selectbox
- .find(`input[name="${$dropdown.data('fieldName')}"]`);
- };
+ if (unassignedSelected) {
+ unassignedSelected.remove();
+ }
- const getSelected = function() {
- return getSelectedUserInputs()
- .map((index, input) => parseInt(input.value, 10))
- .get();
- };
+ // Save current selected user to the DOM
+ const input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = $dropdown.data('fieldName');
- const checkMaxSelect = function() {
- const maxSelect = $dropdown.data('maxSelect');
- if (maxSelect) {
- const selected = getSelected();
+ const currentUserInfo = $dropdown.data('currentUserInfo');
- if (selected.length > maxSelect) {
- const firstSelectedId = selected[0];
- const firstSelected = $dropdown.closest('.selectbox')
- .find(`input[name='${$dropdown.data('fieldName')}'][value=${firstSelectedId}]`);
+ if (currentUserInfo) {
+ input.value = currentUserInfo.id;
+ input.dataset.meta = _.escape(currentUserInfo.name);
+ } else if (_this.currentUser) {
+ input.value = _this.currentUser.id;
+ }
- firstSelected.remove();
- emitSidebarEvent('sidebar.removeAssignee', {
- id: firstSelectedId,
- });
+ if ($selectbox) {
+ $dropdown.parent().before(input);
+ } else {
+ $dropdown.after(input);
}
- }
- };
+ };
- const getMultiSelectDropdownTitle = function(selectedUser, isSelected) {
- const selectedUsers = getSelected()
- .filter(u => u !== 0);
-
- const firstUser = getSelectedUserInputs()
- .map((index, input) => ({
- name: input.dataset.meta,
- value: parseInt(input.value, 10),
- }))
- .filter(u => u.id !== 0)
- .get(0);
-
- if (selectedUsers.length === 0) {
- return 'Unassigned';
- } else if (selectedUsers.length === 1) {
- return firstUser.name;
- } else if (isSelected) {
- const otherSelected = selectedUsers.filter(s => s !== selectedUser.id);
- return `${selectedUser.name} + ${otherSelected.length} more`;
- } else {
- return `${firstUser.name} + ${selectedUsers.length - 1} more`;
+ if ($block[0]) {
+ $block[0].addEventListener('assignYourself', assignYourself);
}
- };
- $('.assign-to-me-link').on('click', (e) => {
- e.preventDefault();
- $(e.currentTarget).hide();
+ const getSelectedUserInputs = function() {
+ return $selectbox.find(`input[name="${$dropdown.data('fieldName')}"]`);
+ };
+
+ const getSelected = function() {
+ return getSelectedUserInputs()
+ .map((index, input) => parseInt(input.value, 10))
+ .get();
+ };
+
+ const checkMaxSelect = function() {
+ const maxSelect = $dropdown.data('maxSelect');
+ if (maxSelect) {
+ const selected = getSelected();
+
+ if (selected.length > maxSelect) {
+ const firstSelectedId = selected[0];
+ const firstSelected = $dropdown
+ .closest('.selectbox')
+ .find(`input[name='${$dropdown.data('fieldName')}'][value=${firstSelectedId}]`);
+
+ firstSelected.remove();
+ emitSidebarEvent('sidebar.removeAssignee', {
+ id: firstSelectedId,
+ });
+ }
+ }
+ };
+
+ const getMultiSelectDropdownTitle = function(selectedUser, isSelected) {
+ const selectedUsers = getSelected().filter(u => u !== 0);
+
+ const firstUser = getSelectedUserInputs()
+ .map((index, input) => ({
+ name: input.dataset.meta,
+ value: parseInt(input.value, 10),
+ }))
+ .filter(u => u.id !== 0)
+ .get(0);
+
+ if (selectedUsers.length === 0) {
+ return 'Unassigned';
+ } else if (selectedUsers.length === 1) {
+ return firstUser.name;
+ } else if (isSelected) {
+ const otherSelected = selectedUsers.filter(s => s !== selectedUser.id);
+ return `${selectedUser.name} + ${otherSelected.length} more`;
+ } else {
+ return `${firstUser.name} + ${selectedUsers.length - 1} more`;
+ }
+ };
- if ($dropdown.data('multiSelect')) {
- assignYourself();
- checkMaxSelect();
+ $('.assign-to-me-link').on('click', e => {
+ e.preventDefault();
+ $(e.currentTarget).hide();
- const currentUserInfo = $dropdown.data('currentUserInfo');
- $dropdown.find('.dropdown-toggle-text').text(getMultiSelectDropdownTitle(currentUserInfo)).removeClass('is-default');
- } else {
- const $input = $(`input[name="${$dropdown.data('fieldName')}"]`);
- $input.val(gon.current_user_id);
- selectedId = $input.val();
- $dropdown.find('.dropdown-toggle-text').text(gon.current_user_fullname).removeClass('is-default');
- }
- });
-
- $block.on('click', '.js-assign-yourself', (e) => {
- e.preventDefault();
- return assignTo(_this.currentUser.id);
- });
-
- assignTo = function(selected) {
- var data;
- data = {};
- data[abilityName] = {};
- data[abilityName].assignee_id = selected != null ? selected : null;
- $loading.removeClass('hidden').fadeIn();
- $dropdown.trigger('loading.gl.dropdown');
-
- return axios.put(issueURL, data)
- .then(({ data }) => {
+ if ($dropdown.data('multiSelect')) {
+ assignYourself();
+ checkMaxSelect();
+
+ const currentUserInfo = $dropdown.data('currentUserInfo');
+ $dropdown
+ .find('.dropdown-toggle-text')
+ .text(getMultiSelectDropdownTitle(currentUserInfo))
+ .removeClass('is-default');
+ } else {
+ const $input = $(`input[name="${$dropdown.data('fieldName')}"]`);
+ $input.val(gon.current_user_id);
+ selectedId = $input.val();
+ $dropdown
+ .find('.dropdown-toggle-text')
+ .text(gon.current_user_fullname)
+ .removeClass('is-default');
+ }
+ });
+
+ $block.on('click', '.js-assign-yourself', e => {
+ e.preventDefault();
+ return assignTo(_this.currentUser.id);
+ });
+
+ assignTo = function(selected) {
+ var data;
+ data = {};
+ data[abilityName] = {};
+ data[abilityName].assignee_id = selected != null ? selected : null;
+ $loading.removeClass('hidden').fadeIn();
+ $dropdown.trigger('loading.gl.dropdown');
+
+ return axios.put(issueURL, data).then(({ data }) => {
var user, tooltipTitle;
$dropdown.trigger('loaded.gl.dropdown');
$loading.fadeOut();
@@ -190,14 +214,14 @@ function UsersSelect(currentUser, els, options = {}) {
user = {
name: data.assignee.name,
username: data.assignee.username,
- avatar: data.assignee.avatar_url
+ avatar: data.assignee.avatar_url,
};
tooltipTitle = _.escape(user.name);
} else {
user = {
name: 'Unassigned',
username: '',
- avatar: ''
+ avatar: '',
};
tooltipTitle = __('Assignee');
}
@@ -205,319 +229,341 @@ function UsersSelect(currentUser, els, options = {}) {
$collapsedSidebar.attr('title', tooltipTitle).tooltip('_fixTitle');
return $collapsedSidebar.html(collapsedAssigneeTemplate(user));
});
- };
- collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <a class="author-link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>');
- assigneeTemplate = _.template('<% if (username) { %> <a class="author-link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>');
- return $dropdown.glDropdown({
- showMenuAbove: showMenuAbove,
- data: function(term, callback) {
- return _this.users(term, options, function(users) {
- // GitLabDropdownFilter returns this.instance
- // GitLabDropdownRemote returns this.options.instance
- const glDropdown = this.instance || this.options.instance;
- glDropdown.options.processData(term, users, callback);
- }.bind(this));
- },
- processData: function(term, data, callback) {
- let users = data;
-
- // Only show assigned user list when there is no search term
- if ($dropdown.hasClass('js-multiselect') && term.length === 0) {
- const selectedInputs = getSelectedUserInputs();
-
- // Potential duplicate entries when dealing with issue board
- // because issue board is also managed by vue
- const selectedUsers = _.uniq(selectedInputs, false, a => a.value)
- .filter((input) => {
- const userId = parseInt(input.value, 10);
- const inUsersArray = users.find(u => u.id === userId);
-
- return !inUsersArray && userId !== 0;
- })
- .map((input) => {
- const userId = parseInt(input.value, 10);
- const { avatarUrl, avatar_url, name, username } = input.dataset;
- return {
- avatar_url: avatarUrl || avatar_url,
- id: userId,
- name,
- username,
- };
- });
+ };
+ collapsedAssigneeTemplate = _.template(
+ '<% if( avatar ) { %> <a class="author-link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>',
+ );
+ assigneeTemplate = _.template(
+ '<% if (username) { %> <a class="author-link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>',
+ );
+ return $dropdown.glDropdown({
+ showMenuAbove: showMenuAbove,
+ data: function(term, callback) {
+ return _this.users(
+ term,
+ options,
+ function(users) {
+ // GitLabDropdownFilter returns this.instance
+ // GitLabDropdownRemote returns this.options.instance
+ const glDropdown = this.instance || this.options.instance;
+ glDropdown.options.processData(term, users, callback);
+ }.bind(this),
+ );
+ },
+ processData: function(term, data, callback) {
+ let users = data;
+
+ // Only show assigned user list when there is no search term
+ if ($dropdown.hasClass('js-multiselect') && term.length === 0) {
+ const selectedInputs = getSelectedUserInputs();
+
+ // Potential duplicate entries when dealing with issue board
+ // because issue board is also managed by vue
+ const selectedUsers = _.uniq(selectedInputs, false, a => a.value)
+ .filter(input => {
+ const userId = parseInt(input.value, 10);
+ const inUsersArray = users.find(u => u.id === userId);
+
+ return !inUsersArray && userId !== 0;
+ })
+ .map(input => {
+ const userId = parseInt(input.value, 10);
+ const { avatarUrl, avatar_url, name, username } = input.dataset;
+ return {
+ avatar_url: avatarUrl || avatar_url,
+ id: userId,
+ name,
+ username,
+ };
+ });
- users = data.concat(selectedUsers);
- }
+ users = data.concat(selectedUsers);
+ }
- let anyUser;
- let index;
- let len;
- let name;
- let obj;
- let showDivider;
- if (term.length === 0) {
- showDivider = 0;
- if (firstUser) {
- // Move current user to the front of the list
- for (index = 0, len = users.length; index < len; index += 1) {
- obj = users[index];
- if (obj.username === firstUser) {
- users.splice(index, 1);
- users.unshift(obj);
- break;
+ let anyUser;
+ let index;
+ let len;
+ let name;
+ let obj;
+ let showDivider;
+ if (term.length === 0) {
+ showDivider = 0;
+ if (firstUser) {
+ // Move current user to the front of the list
+ for (index = 0, len = users.length; index < len; index += 1) {
+ obj = users[index];
+ if (obj.username === firstUser) {
+ users.splice(index, 1);
+ users.unshift(obj);
+ break;
+ }
}
}
- }
- if (showNullUser) {
- showDivider += 1;
- users.unshift({
- beforeDivider: true,
- name: 'Unassigned',
- id: 0
- });
- }
- if (showAnyUser) {
- showDivider += 1;
- name = showAnyUser;
- if (name === true) {
- name = 'Any User';
+ if (showNullUser) {
+ showDivider += 1;
+ users.unshift({
+ beforeDivider: true,
+ name: 'Unassigned',
+ id: 0,
+ });
+ }
+ if (showAnyUser) {
+ showDivider += 1;
+ name = showAnyUser;
+ if (name === true) {
+ name = 'Any User';
+ }
+ anyUser = {
+ beforeDivider: true,
+ name: name,
+ id: null,
+ };
+ users.unshift(anyUser);
}
- anyUser = {
- beforeDivider: true,
- name: name,
- id: null
- };
- users.unshift(anyUser);
- }
- if (showDivider) {
- users.splice(showDivider, 0, 'divider');
- }
+ if (showDivider) {
+ users.splice(showDivider, 0, 'divider');
+ }
- if ($dropdown.hasClass('js-multiselect')) {
- const selected = getSelected().filter(i => i !== 0);
+ if ($dropdown.hasClass('js-multiselect')) {
+ const selected = getSelected().filter(i => i !== 0);
- if (selected.length > 0) {
- if ($dropdown.data('dropdownHeader')) {
- showDivider += 1;
- users.splice(showDivider, 0, {
- header: $dropdown.data('dropdownHeader'),
- });
- }
+ if (selected.length > 0) {
+ if ($dropdown.data('dropdownHeader')) {
+ showDivider += 1;
+ users.splice(showDivider, 0, {
+ header: $dropdown.data('dropdownHeader'),
+ });
+ }
- const selectedUsers = users
- .filter(u => selected.indexOf(u.id) !== -1)
- .sort((a, b) => a.name > b.name);
+ const selectedUsers = users
+ .filter(u => selected.indexOf(u.id) !== -1)
+ .sort((a, b) => a.name > b.name);
- users = users.filter(u => selected.indexOf(u.id) === -1);
+ users = users.filter(u => selected.indexOf(u.id) === -1);
- selectedUsers.forEach((selectedUser) => {
- showDivider += 1;
- users.splice(showDivider, 0, selectedUser);
- });
+ selectedUsers.forEach(selectedUser => {
+ showDivider += 1;
+ users.splice(showDivider, 0, selectedUser);
+ });
- users.splice(showDivider + 1, 0, 'divider');
+ users.splice(showDivider + 1, 0, 'divider');
+ }
}
}
- }
- callback(users);
- if (showMenuAbove) {
- $dropdown.data('glDropdown').positionMenuAbove();
- }
- },
- filterable: true,
- filterRemote: true,
- search: {
- fields: ['name', 'username']
- },
- selectable: true,
- fieldName: $dropdown.data('fieldName'),
- toggleLabel: function(selected, el, glDropdown) {
- const inputValue = glDropdown.filterInput.val();
-
- if (this.multiSelect && inputValue === '') {
- // Remove non-users from the fullData array
- const users = glDropdown.filteredFullData();
- const callback = glDropdown.parseData.bind(glDropdown);
-
- // Update the data model
- this.processData(inputValue, users, callback);
- }
+ callback(users);
+ if (showMenuAbove) {
+ $dropdown.data('glDropdown').positionMenuAbove();
+ }
+ },
+ filterable: true,
+ filterRemote: true,
+ search: {
+ fields: ['name', 'username'],
+ },
+ selectable: true,
+ fieldName: $dropdown.data('fieldName'),
+ toggleLabel: function(selected, el, glDropdown) {
+ const inputValue = glDropdown.filterInput.val();
+
+ if (this.multiSelect && inputValue === '') {
+ // Remove non-users from the fullData array
+ const users = glDropdown.filteredFullData();
+ const callback = glDropdown.parseData.bind(glDropdown);
+
+ // Update the data model
+ this.processData(inputValue, users, callback);
+ }
- if (this.multiSelect) {
- return getMultiSelectDropdownTitle(selected, $(el).hasClass('is-active'));
- }
+ if (this.multiSelect) {
+ return getMultiSelectDropdownTitle(selected, $(el).hasClass('is-active'));
+ }
- if (selected && 'id' in selected && $(el).hasClass('is-active')) {
- $dropdown.find('.dropdown-toggle-text').removeClass('is-default');
- if (selected.text) {
- return selected.text;
+ if (selected && 'id' in selected && $(el).hasClass('is-active')) {
+ $dropdown.find('.dropdown-toggle-text').removeClass('is-default');
+ if (selected.text) {
+ return selected.text;
+ } else {
+ return selected.name;
+ }
} else {
- return selected.name;
+ $dropdown.find('.dropdown-toggle-text').addClass('is-default');
+ return defaultLabel;
+ }
+ },
+ defaultLabel: defaultLabel,
+ hidden: function(e) {
+ if ($dropdown.hasClass('js-multiselect')) {
+ emitSidebarEvent('sidebar.saveAssignees');
}
- } else {
- $dropdown.find('.dropdown-toggle-text').addClass('is-default');
- return defaultLabel;
- }
- },
- defaultLabel: defaultLabel,
- hidden: function(e) {
- if ($dropdown.hasClass('js-multiselect')) {
- emitSidebarEvent('sidebar.saveAssignees');
- }
-
- if (!$dropdown.data('alwaysShowSelectbox')) {
- $selectbox.hide();
- // Recalculate where .value is because vue might have changed it
- $block = $selectbox.closest('.block');
- $value = $block.find('.value');
- // display:block overrides the hide-collapse rule
- $value.css('display', '');
- }
- },
- multiSelect: $dropdown.hasClass('js-multiselect'),
- inputMeta: $dropdown.data('inputMeta'),
- clicked: function(options) {
- const { $el, e, isMarking } = options;
- const user = options.selectedObj;
-
- if ($dropdown.hasClass('js-multiselect')) {
- const isActive = $el.hasClass('is-active');
- const previouslySelected = $dropdown.closest('.selectbox')
- .find("input[name='" + ($dropdown.data('fieldName')) + "'][value!=0]");
-
- // Enables support for limiting the number of users selected
- // Automatically removes the first on the list if more users are selected
- checkMaxSelect();
+ if (!$dropdown.data('alwaysShowSelectbox')) {
+ $selectbox.hide();
- if (user.beforeDivider && user.name.toLowerCase() === 'unassigned') {
- // Unassigned selected
- previouslySelected.each((index, element) => {
- const id = parseInt(element.value, 10);
- element.remove();
- });
- emitSidebarEvent('sidebar.removeAllAssignees');
- } else if (isActive) {
- // user selected
- emitSidebarEvent('sidebar.addAssignee', user);
+ // Recalculate where .value is because vue might have changed it
+ $block = $selectbox.closest('.block');
+ $value = $block.find('.value');
+ // display:block overrides the hide-collapse rule
+ $value.css('display', '');
+ }
+ },
+ multiSelect: $dropdown.hasClass('js-multiselect'),
+ inputMeta: $dropdown.data('inputMeta'),
+ clicked: function(options) {
+ const { $el, e, isMarking } = options;
+ const user = options.selectedObj;
- // Remove unassigned selection (if it was previously selected)
- const unassignedSelected = $dropdown.closest('.selectbox')
- .find("input[name='" + ($dropdown.data('fieldName')) + "'][value=0]");
+ if ($dropdown.hasClass('js-multiselect')) {
+ const isActive = $el.hasClass('is-active');
+ const previouslySelected = $dropdown
+ .closest('.selectbox')
+ .find("input[name='" + $dropdown.data('fieldName') + "'][value!=0]");
+
+ // Enables support for limiting the number of users selected
+ // Automatically removes the first on the list if more users are selected
+ checkMaxSelect();
+
+ if (user.beforeDivider && user.name.toLowerCase() === 'unassigned') {
+ // Unassigned selected
+ previouslySelected.each((index, element) => {
+ const id = parseInt(element.value, 10);
+ element.remove();
+ });
+ emitSidebarEvent('sidebar.removeAllAssignees');
+ } else if (isActive) {
+ // user selected
+ emitSidebarEvent('sidebar.addAssignee', user);
+
+ // Remove unassigned selection (if it was previously selected)
+ const unassignedSelected = $dropdown
+ .closest('.selectbox')
+ .find("input[name='" + $dropdown.data('fieldName') + "'][value=0]");
+
+ if (unassignedSelected) {
+ unassignedSelected.remove();
+ }
+ } else {
+ if (previouslySelected.length === 0) {
+ // Select unassigned because there is no more selected users
+ this.addInput($dropdown.data('fieldName'), 0, {});
+ }
- if (unassignedSelected) {
- unassignedSelected.remove();
- }
- } else {
- if (previouslySelected.length === 0) {
- // Select unassigned because there is no more selected users
- this.addInput($dropdown.data('fieldName'), 0, {});
+ // User unselected
+ emitSidebarEvent('sidebar.removeAssignee', user);
}
- // User unselected
- emitSidebarEvent('sidebar.removeAssignee', user);
+ if (getSelected().find(u => u === gon.current_user_id)) {
+ $('.assign-to-me-link').hide();
+ } else {
+ $('.assign-to-me-link').show();
+ }
}
- if (getSelected().find(u => u === gon.current_user_id)) {
- $('.assign-to-me-link').hide();
- } else {
- $('.assign-to-me-link').show();
+ var isIssueIndex, isMRIndex, page, selected;
+ page = $('body').attr('data-page');
+ isIssueIndex = page === 'projects:issues:index';
+ isMRIndex = page === page && page === 'projects:merge_requests:index';
+ if (
+ $dropdown.hasClass('js-filter-bulk-update') ||
+ $dropdown.hasClass('js-issuable-form-dropdown')
+ ) {
+ e.preventDefault();
+
+ const isSelecting = user.id !== selectedId;
+ selectedId = isSelecting ? user.id : selectedIdDefault;
+
+ if (selectedId === gon.current_user_id) {
+ $('.assign-to-me-link').hide();
+ } else {
+ $('.assign-to-me-link').show();
+ }
+ return;
+ }
+ if ($el.closest('.add-issues-modal').length) {
+ ModalStore.store.filter[$dropdown.data('fieldName')] = user.id;
+ } else if (handleClick) {
+ e.preventDefault();
+ handleClick(user, isMarking);
+ } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
+ return Issuable.filterResults($dropdown.closest('form'));
+ } else if ($dropdown.hasClass('js-filter-submit')) {
+ return $dropdown.closest('form').submit();
+ } else if (!$dropdown.hasClass('js-multiselect')) {
+ selected = $dropdown
+ .closest('.selectbox')
+ .find("input[name='" + $dropdown.data('fieldName') + "']")
+ .val();
+ return assignTo(selected);
}
- }
- var isIssueIndex, isMRIndex, page, selected;
- page = $('body').attr('data-page');
- isIssueIndex = page === 'projects:issues:index';
- isMRIndex = (page === page && page === 'projects:merge_requests:index');
- if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
- e.preventDefault();
+ // Automatically close dropdown after assignee is selected
+ // since CE has no multiple assignees
+ // EE does not have a max-select
+ if (
+ $dropdown.data('maxSelect') &&
+ getSelected().length === $dropdown.data('maxSelect')
+ ) {
+ // Close the dropdown
+ $dropdown.dropdown('toggle');
+ }
+ },
+ id: function(user) {
+ return user.id;
+ },
+ opened: function(e) {
+ const $el = $(e.currentTarget);
+ const selected = getSelected();
+ if ($dropdown.hasClass('js-issue-board-sidebar') && selected.length === 0) {
+ this.addInput($dropdown.data('fieldName'), 0, {});
+ }
+ $el.find('.is-active').removeClass('is-active');
- const isSelecting = (user.id !== selectedId);
- selectedId = isSelecting ? user.id : selectedIdDefault;
+ function highlightSelected(id) {
+ $el.find(`li[data-user-id="${id}"] .dropdown-menu-user-link`).addClass('is-active');
+ }
- if (selectedId === gon.current_user_id) {
- $('.assign-to-me-link').hide();
+ if (selected.length > 0) {
+ getSelected().forEach(selectedId => highlightSelected(selectedId));
+ } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ highlightSelected(0);
} else {
- $('.assign-to-me-link').show();
+ highlightSelected(selectedId);
}
- return;
- }
- if ($el.closest('.add-issues-modal').length) {
- ModalStore.store.filter[$dropdown.data('fieldName')] = user.id;
- } else if (handleClick) {
- e.preventDefault();
- handleClick(user, isMarking);
- } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
- return Issuable.filterResults($dropdown.closest('form'));
- } else if ($dropdown.hasClass('js-filter-submit')) {
- return $dropdown.closest('form').submit();
- } else if (!$dropdown.hasClass('js-multiselect')) {
- selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('fieldName')) + "']").val();
- return assignTo(selected);
- }
-
- // Automatically close dropdown after assignee is selected
- // since CE has no multiple assignees
- // EE does not have a max-select
- if ($dropdown.data('maxSelect') &&
- getSelected().length === $dropdown.data('maxSelect')) {
- // Close the dropdown
- $dropdown.dropdown('toggle');
- }
- },
- id: function (user) {
- return user.id;
- },
- opened: function(e) {
- const $el = $(e.currentTarget);
- const selected = getSelected();
- if ($dropdown.hasClass('js-issue-board-sidebar') && selected.length === 0) {
- this.addInput($dropdown.data('fieldName'), 0, {});
- }
- $el.find('.is-active').removeClass('is-active');
-
- function highlightSelected(id) {
- $el.find(`li[data-user-id="${id}"] .dropdown-menu-user-link`).addClass('is-active');
- }
+ },
+ updateLabel: $dropdown.data('dropdownTitle'),
+ renderRow: function(user) {
+ var avatar, img, listClosingTags, listWithName, listWithUserName, username;
+ username = user.username ? '@' + user.username : '';
+ avatar = user.avatar_url ? user.avatar_url : gon.default_avatar_url;
- if (selected.length > 0) {
- getSelected().forEach(selectedId => highlightSelected(selectedId));
- } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
- highlightSelected(0);
- } else {
- highlightSelected(selectedId);
- }
- },
- updateLabel: $dropdown.data('dropdownTitle'),
- renderRow: function(user) {
- var avatar, img, listClosingTags, listWithName, listWithUserName, username;
- username = user.username ? "@" + user.username : "";
- avatar = user.avatar_url ? user.avatar_url : gon.default_avatar_url;
+ let selected = false;
- let selected = false;
+ if (this.multiSelect) {
+ selected = getSelected().find(u => user.id === u);
- if (this.multiSelect) {
- selected = getSelected().find(u => user.id === u);
+ const { fieldName } = this;
+ const field = $dropdown
+ .closest('.selectbox')
+ .find("input[name='" + fieldName + "'][value='" + user.id + "']");
- const { fieldName } = this;
- const field = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "'][value='" + user.id + "']");
-
- if (field.length) {
- selected = true;
+ if (field.length) {
+ selected = true;
+ }
+ } else {
+ selected = user.id === selectedId;
}
- } else {
- selected = user.id === selectedId;
- }
- img = "";
- if (user.beforeDivider != null) {
- `<li><a href='#' class='${selected === true ? 'is-active' : ''}'>${_.escape(user.name)}</a></li>`;
- } else {
- img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
- }
+ img = '';
+ if (user.beforeDivider != null) {
+ `<li><a href='#' class='${selected === true ? 'is-active' : ''}'>${_.escape(
+ user.name,
+ )}</a></li>`;
+ } else {
+ img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
+ }
- return `
+ return `
<li data-user-id=${user.id}>
<a href='#' class='dropdown-menu-user-link ${selected === true ? 'is-active' : ''}'>
${img}
@@ -528,114 +574,117 @@ function UsersSelect(currentUser, els, options = {}) {
</a>
</li>
`;
- }
- });
- };
- })(this));
- $('.ajax-users-select').each((function(_this) {
- return function(i, select) {
- var firstUser, showAnyUser, showEmailUser, showNullUser;
- var options = {};
- options.skipLdap = $(select).hasClass('skip_ldap');
- options.projectId = $(select).data('projectId');
- options.groupId = $(select).data('groupId');
- options.showCurrentUser = $(select).data('currentUser');
- options.authorId = $(select).data('authorId');
- options.skipUsers = $(select).data('skipUsers');
- showNullUser = $(select).data('nullUser');
- showAnyUser = $(select).data('anyUser');
- showEmailUser = $(select).data('emailUser');
- firstUser = $(select).data('firstUser');
- return $(select).select2({
- placeholder: "Search for a user",
- multiple: $(select).hasClass('multiselect'),
- minimumInputLength: 0,
- query: function(query) {
- return _this.users(query.term, options, function(users) {
- var anyUser, data, emailUser, index, len, name, nullUser, obj, ref;
- data = {
- results: users
- };
- if (query.term.length === 0) {
- if (firstUser) {
- // Move current user to the front of the list
- ref = data.results;
-
- for (index = 0, len = ref.length; index < len; index += 1) {
- obj = ref[index];
- if (obj.username === firstUser) {
- data.results.splice(index, 1);
- data.results.unshift(obj);
- break;
+ },
+ });
+ };
+ })(this),
+ );
+ $('.ajax-users-select').each(
+ (function(_this) {
+ return function(i, select) {
+ var firstUser, showAnyUser, showEmailUser, showNullUser;
+ var options = {};
+ options.skipLdap = $(select).hasClass('skip_ldap');
+ options.projectId = $(select).data('projectId');
+ options.groupId = $(select).data('groupId');
+ options.showCurrentUser = $(select).data('currentUser');
+ options.authorId = $(select).data('authorId');
+ options.skipUsers = $(select).data('skipUsers');
+ showNullUser = $(select).data('nullUser');
+ showAnyUser = $(select).data('anyUser');
+ showEmailUser = $(select).data('emailUser');
+ firstUser = $(select).data('firstUser');
+ return $(select).select2({
+ placeholder: 'Search for a user',
+ multiple: $(select).hasClass('multiselect'),
+ minimumInputLength: 0,
+ query: function(query) {
+ return _this.users(query.term, options, function(users) {
+ var anyUser, data, emailUser, index, len, name, nullUser, obj, ref;
+ data = {
+ results: users,
+ };
+ if (query.term.length === 0) {
+ if (firstUser) {
+ // Move current user to the front of the list
+ ref = data.results;
+
+ for (index = 0, len = ref.length; index < len; index += 1) {
+ obj = ref[index];
+ if (obj.username === firstUser) {
+ data.results.splice(index, 1);
+ data.results.unshift(obj);
+ break;
+ }
}
}
- }
- if (showNullUser) {
- nullUser = {
- name: 'Unassigned',
- id: 0
- };
- data.results.unshift(nullUser);
- }
- if (showAnyUser) {
- name = showAnyUser;
- if (name === true) {
- name = 'Any User';
+ if (showNullUser) {
+ nullUser = {
+ name: 'Unassigned',
+ id: 0,
+ };
+ data.results.unshift(nullUser);
}
- anyUser = {
- name: name,
- id: null
+ if (showAnyUser) {
+ name = showAnyUser;
+ if (name === true) {
+ name = 'Any User';
+ }
+ anyUser = {
+ name: name,
+ id: null,
+ };
+ data.results.unshift(anyUser);
+ }
+ }
+ if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) {
+ var trimmed = query.term.trim();
+ emailUser = {
+ name: 'Invite "' + trimmed + '" by email',
+ username: trimmed,
+ id: trimmed,
+ invite: true,
};
- data.results.unshift(anyUser);
+ data.results.unshift(emailUser);
}
- }
- if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) {
- var trimmed = query.term.trim();
- emailUser = {
- name: "Invite \"" + query.term + "\" by email",
- username: trimmed,
- id: trimmed,
- invite: true
- };
- data.results.unshift(emailUser);
- }
- return query.callback(data);
- });
- },
- initSelection: function() {
- var args;
- args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
- return _this.initSelection.apply(_this, args);
- },
- formatResult: function() {
- var args;
- args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
- return _this.formatResult.apply(_this, args);
- },
- formatSelection: function() {
- var args;
- args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
- return _this.formatSelection.apply(_this, args);
- },
- dropdownCssClass: "ajax-users-dropdown",
- // we do not want to escape markup since we are displaying html in results
- escapeMarkup: function(m) {
- return m;
- }
- });
- };
- })(this));
+ return query.callback(data);
+ });
+ },
+ initSelection: function() {
+ var args;
+ args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
+ return _this.initSelection.apply(_this, args);
+ },
+ formatResult: function() {
+ var args;
+ args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
+ return _this.formatResult.apply(_this, args);
+ },
+ formatSelection: function() {
+ var args;
+ args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
+ return _this.formatSelection.apply(_this, args);
+ },
+ dropdownCssClass: 'ajax-users-dropdown',
+ // we do not want to escape markup since we are displaying html in results
+ escapeMarkup: function(m) {
+ return m;
+ },
+ });
+ };
+ })(this),
+ );
}
UsersSelect.prototype.initSelection = function(element, callback) {
var id, nullUser;
id = $(element).val();
- if (id === "0") {
+ if (id === '0') {
nullUser = {
- name: 'Unassigned'
+ name: 'Unassigned',
};
return callback(nullUser);
- } else if (id !== "") {
+ } else if (id !== '') {
return this.user(id, callback);
}
};
@@ -647,7 +696,21 @@ UsersSelect.prototype.formatResult = function(user) {
} else {
avatar = gon.default_avatar_url;
}
- return "<div class='user-result " + (!user.username ? 'no-username' : void 0) + "'> <div class='user-image'><img class='avatar avatar-inline s32' src='" + avatar + "'></div> <div class='user-name dropdown-menu-user-full-name'>" + _.escape(user.name) + "</div> <div class='user-username dropdown-menu-user-username'>" + (!user.invite ? "@" + _.escape(user.username) : "") + "</div> </div>";
+ return `
+ <div class='user-result'>
+ <div class='user-image'>
+ <img class='avatar avatar-inline s32' src='${avatar}'>
+ </div>
+ <div class='user-info'>
+ <div class='user-name dropdown-menu-user-full-name'>
+ ${_.escape(user.name)}
+ </div>
+ <div class='user-username dropdown-menu-user-username text-secondary'>
+ ${!user.invite ? '@' + _.escape(user.username) : ''}
+ </div>
+ </div>
+ </div>
+ `;
};
UsersSelect.prototype.formatSelection = function(user) {
@@ -662,10 +725,9 @@ UsersSelect.prototype.user = function(user_id, callback) {
var url;
url = this.buildUrl(this.userPath);
url = url.replace(':id', user_id);
- return axios.get(url)
- .then(({ data }) => {
- callback(data);
- });
+ return axios.get(url).then(({ data }) => {
+ callback(data);
+ });
};
// Return users list. Filtered by query
@@ -682,12 +744,11 @@ UsersSelect.prototype.users = function(query, options, callback) {
todo_state_filter: options.todoStateFilter || null,
current_user: options.showCurrentUser || null,
author_id: options.authorId || null,
- skip_users: options.skipUsers || null
+ skip_users: options.skipUsers || null,
};
- return axios.get(url, { params })
- .then(({ data }) => {
- callback(data);
- });
+ return axios.get(url, { params }).then(({ data }) => {
+ callback(data);
+ });
};
UsersSelect.prototype.buildUrl = function(url) {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
index 21f21232596..fe741dc60cb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -1,5 +1,8 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+import FilteredSearchDropdown from '~/vue_shared/components/filtered_search_dropdown.vue';
+import { __ } from '~/locale';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import tooltip from '../../vue_shared/directives/tooltip';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
@@ -7,6 +10,7 @@ import { visitUrl } from '../../lib/utils/url_utility';
import createFlash from '../../flash';
import MemoryUsage from './memory_usage.vue';
import StatusIcon from './mr_widget_status_icon.vue';
+import ReviewAppLink from './review_app_link.vue';
import MRWidgetService from '../services/mr_widget_service';
export default {
@@ -16,18 +20,28 @@ export default {
MemoryUsage,
StatusIcon,
Icon,
+ TooltipOnTruncate,
+ FilteredSearchDropdown,
+ ReviewAppLink,
},
directives: {
tooltip,
},
- mixins: [
- timeagoMixin,
- ],
+ mixins: [timeagoMixin],
props: {
deployment: {
type: Object,
required: true,
},
+ showMetrics: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ deployedTextMap: {
+ running: __('Deploying to'),
+ success: __('Deployed to'),
+ failed: __('Failed to deploy to'),
},
data() {
return {
@@ -48,12 +62,29 @@ export default {
return !!(this.deployment.url && this.deployment.name);
},
hasMetrics() {
- return !!(this.deployment.metrics_url);
+ return !!this.deployment.metrics_url;
+ },
+ deployedText() {
+ return this.$options.deployedTextMap[this.deployment.status];
+ },
+ isDeployInProgress() {
+ return this.deployment.status === 'running';
+ },
+ deployInProgressTooltip() {
+ return this.isDeployInProgress
+ ? __('Stopping this environment is currently not possible as a deployment is in progress')
+ : '';
+ },
+ shouldRenderDropdown() {
+ return this.deployment.changes && this.deployment.changes.length > 0;
+ },
+ showMemoryUsage() {
+ return this.hasMetrics && this.showMetrics;
},
},
methods: {
stopEnvironment() {
- const msg = 'Are you sure you want to stop this environment?';
+ const msg = __('Are you sure you want to stop this environment?');
const isConfirmed = confirm(msg); // eslint-disable-line
if (isConfirmed) {
@@ -61,7 +92,7 @@ export default {
MRWidgetService.stopEnvironment(this.deployment.stop_url)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
if (data.redirect_url) {
visitUrl(data.redirect_url);
}
@@ -83,56 +114,102 @@ export default {
<div class="ci-widget media">
<div class="media-body">
<div class="deploy-body">
- <div class="deployment-info">
+ <div class="js-deployment-info deployment-info">
<template v-if="hasDeploymentMeta">
<span>
- Deployed to
+ {{ deployedText }}
</span>
- <a
- :href="deployment.url"
- target="_blank"
- rel="noopener noreferrer nofollow"
- class="deploy-link js-deploy-meta"
+ <tooltip-on-truncate
+ :title="deployment.name"
+ truncate-target="child"
+ class="deploy-link label-truncate"
>
- {{ deployment.name }}
- </a>
+ <a
+ :href="deployment.url"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="js-deploy-meta"
+ >
+ {{ deployment.name }}
+ </a>
+ </tooltip-on-truncate>
</template>
<span
- v-tooltip
v-if="hasDeploymentTime"
+ v-tooltip
:title="deployment.deployed_at_formatted"
class="js-deploy-time"
>
{{ deployTimeago }}
</span>
<memory-usage
- v-if="hasMetrics"
+ v-if="showMemoryUsage"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div>
<div>
- <a
- v-if="hasExternalUrls"
- :href="deployment.external_url"
- target="_blank"
- rel="noopener noreferrer nofollow"
- class="deploy-link js-deploy-url btn btn-default btn-sm inline"
- >
- <span>
- View app
- <icon name="external-link" />
- </span>
- </a>
- <loading-button
+ <template v-if="hasExternalUrls">
+ <filtered-search-dropdown
+ v-if="shouldRenderDropdown"
+ class="js-mr-wigdet-deployment-dropdown inline"
+ :items="deployment.changes"
+ :main-action-link="deployment.external_url"
+ filter-key="path"
+ >
+ <template
+ slot="mainAction"
+ slot-scope="slotProps"
+ >
+ <review-app-link
+ :link="deployment.external_url"
+ :css-class="`deploy-link js-deploy-url inline ${slotProps.className}`"
+ />
+ </template>
+
+ <template
+ slot="result"
+ slot-scope="slotProps"
+ >
+ <a
+ :href="slotProps.result.external_url"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="menu-item"
+ >
+ <strong class="str-truncated-100 append-bottom-0 d-block">
+ {{ slotProps.result.path }}
+ </strong>
+
+ <p class="text-secondary str-truncated-100 append-bottom-0 d-block">
+ {{ slotProps.result.external_url }}
+ </p>
+ </a>
+ </template>
+ </filtered-search-dropdown>
+ <review-app-link
+ v-else
+ :link="deployment.external_url"
+ css-class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inlin"
+ />
+ </template>
+ <span
v-if="deployment.stop_url"
- :loading="isStopping"
- container-class="btn btn-default btn-sm inline prepend-left-4"
- title="Stop environment"
- @click="stopEnvironment"
+ v-tooltip
+ :title="deployInProgressTooltip"
+ class="d-inline-block"
+ tabindex="0"
>
- <icon name="stop" />
- </loading-button>
+ <loading-button
+ :loading="isStopping"
+ :disabled="isDeployInProgress"
+ :title="__('Stop environment')"
+ container-class="js-stop-env btn btn-default btn-sm inline prepend-left-4"
+ @click="stopEnvironment"
+ >
+ <icon name="stop" />
+ </loading-button>
+ </span>
</div>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue b/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
index 5e76f6b1cac..41dbc5c9cbb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
@@ -60,11 +60,29 @@ export default {
let memoryUsageMsg = '';
if (memoryTo > memoryFrom) {
- memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
+ memoryUsageMsg = sprintf(
+ s__(
+ 'mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB',
+ ),
+ messageProps,
+ false,
+ );
} else if (memoryTo < memoryFrom) {
- memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
+ memoryUsageMsg = sprintf(
+ s__(
+ 'mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB',
+ ),
+ messageProps,
+ false,
+ );
} else {
- memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB'), messageProps, false);
+ memoryUsageMsg = sprintf(
+ s__(
+ 'mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB',
+ ),
+ messageProps,
+ false,
+ );
}
return memoryUsageMsg;
@@ -77,7 +95,7 @@ export default {
methods: {
getMegabytes(bytesString) {
const valueInBytes = Number(bytesString).toFixed(2);
- return (bytesToMiB(valueInBytes)).toFixed(2);
+ return bytesToMiB(valueInBytes).toFixed(2);
},
computeGraphData(metrics, deploymentTime) {
this.loadingMetrics = false;
@@ -103,7 +121,7 @@ export default {
loadMetrics() {
backOff((next, stop) => {
MRWidgetService.fetchMetrics(this.metricsUrl)
- .then((res) => {
+ .then(res => {
if (res.status === statusCodes.NO_CONTENT) {
this.backOffRequestCounter += 1;
/* eslint-disable no-unused-expressions */
@@ -114,14 +132,14 @@ export default {
})
.catch(stop);
})
- .then((res) => {
+ .then(res => {
if (res.status === statusCodes.NO_CONTENT) {
return res;
}
return res.data;
})
- .then((data) => {
+ .then(data => {
this.computeGraphData(data.metrics, data.deployment_time);
return data;
})
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
index 22c2f74f900..2ad9e8be655 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
@@ -1,36 +1,36 @@
<script>
- import tooltip from '../../vue_shared/directives/tooltip';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- name: 'MrWidgetAuthor',
- directives: {
- tooltip,
+export default {
+ name: 'MrWidgetAuthor',
+ directives: {
+ tooltip,
+ },
+ props: {
+ author: {
+ type: Object,
+ required: true,
},
- props: {
- author: {
- type: Object,
- required: true,
- },
- showAuthorName: {
- type: Boolean,
- required: false,
- default: true,
- },
- showAuthorTooltip: {
- type: Boolean,
- required: false,
- default: false,
- },
+ showAuthorName: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- computed: {
- authorUrl() {
- return this.author.webUrl || this.author.web_url;
- },
- avatarUrl() {
- return this.author.avatarUrl || this.author.avatar_url;
- },
+ showAuthorTooltip: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- };
+ },
+ computed: {
+ authorUrl() {
+ return this.author.webUrl || this.author.web_url;
+ },
+ avatarUrl() {
+ return this.author.avatarUrl || this.author.avatar_url;
+ },
+ },
+};
</script>
<template>
<a
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
index ba16cb9e2c8..1d902131f49 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
@@ -1,34 +1,34 @@
<script>
- import tooltip from '~/vue_shared/directives/tooltip';
- import MrWidgetAuthor from './mr_widget_author.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import MrWidgetAuthor from './mr_widget_author.vue';
- export default {
- name: 'MrWidgetAuthorTime',
- components: {
- MrWidgetAuthor,
+export default {
+ name: 'MrWidgetAuthorTime',
+ components: {
+ MrWidgetAuthor,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ actionText: {
+ type: String,
+ required: true,
},
- directives: {
- tooltip,
+ author: {
+ type: Object,
+ required: true,
},
- props: {
- actionText: {
- type: String,
- required: true,
- },
- author: {
- type: Object,
- required: true,
- },
- dateTitle: {
- type: String,
- required: true,
- },
- dateReadable: {
- type: String,
- required: true,
- },
+ dateTitle: {
+ type: String,
+ required: true,
},
- };
+ dateReadable: {
+ type: String,
+ required: true,
+ },
+ },
+};
</script>
<template>
<h4 class="js-mr-widget-author">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
index a4c2289c590..acfdab3a015 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
@@ -1,18 +1,21 @@
<script>
-import tooltip from '~/vue_shared/directives/tooltip';
-import { n__ } from '~/locale';
+import _ from 'underscore';
+import { n__, s__, sprintf } from '~/locale';
import { mergeUrlParams, webIDEUrl } from '~/lib/utils/url_utility';
import Icon from '~/vue_shared/components/icon.vue';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
export default {
name: 'MRWidgetHeader',
- directives: {
- tooltip,
- },
components: {
Icon,
clipboardButton,
+ TooltipOnTruncate,
+ },
+ directives: {
+ tooltip,
},
props: {
mr: {
@@ -24,8 +27,18 @@ export default {
shouldShowCommitsBehindText() {
return this.mr.divergedCommitsCount > 0;
},
- commitsText() {
- return n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount);
+ commitsBehindText() {
+ return sprintf(
+ s__(
+ 'mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch',
+ ),
+ {
+ commitsBehindLinkStart: `<a href="${_.escape(this.mr.targetBranchPath)}">`,
+ commitsBehind: n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount),
+ commitsBehindLinkEnd: '</a>',
+ },
+ false,
+ );
},
branchNameClipboardData() {
// This supports code in app/assets/javascripts/copy_to_clipboard.js that
@@ -36,22 +49,27 @@ export default {
gfm: `\`${this.mr.sourceBranch}\``,
});
},
- isSourceBranchLong() {
- return this.isBranchTitleLong(this.mr.sourceBranch);
- },
- isTargetBranchLong() {
- return this.isBranchTitleLong(this.mr.targetBranch);
- },
webIdePath() {
- return mergeUrlParams({
- target_project: this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath ?
- this.mr.targetProjectFullPath : '',
- }, webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`));
+ if (this.mr.canPushToSourceBranch) {
+ return mergeUrlParams(
+ {
+ target_project:
+ this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath
+ ? this.mr.targetProjectFullPath
+ : '',
+ },
+ webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`),
+ );
+ }
+
+ return null;
},
- },
- methods: {
- isBranchTitleLong(branchTitle) {
- return branchTitle.length > 32;
+ ideButtonTitle() {
+ return !this.mr.canPushToSourceBranch
+ ? s__(
+ 'mrWidget|You are not allowed to edit this project directly. Please fork to make changes.',
+ )
+ : '';
},
},
};
@@ -65,30 +83,21 @@ export default {
<div class="normal">
<strong>
{{ s__("mrWidget|Request to merge") }}
- <span
- :class="{ 'label-truncated': isSourceBranchLong }"
- :title="isSourceBranchLong ? mr.sourceBranch : ''"
- :v-tooltip="isSourceBranchLong"
- class="label-branch js-source-branch"
- data-placement="bottom"
+ <tooltip-on-truncate
+ :title="mr.sourceBranch"
+ truncate-target="child"
+ class="label-branch label-truncate js-source-branch"
v-html="mr.sourceBranchLink"
- >
- </span>
-
- <clipboard-button
+ /><clipboard-button
:text="branchNameClipboardData"
:title="__('Copy branch name to clipboard')"
css-class="btn-default btn-transparent btn-clipboard"
/>
-
{{ s__("mrWidget|into") }}
-
- <span
- :v-tooltip="isTargetBranchLong"
- :class="{ 'label-truncatedtooltip': isTargetBranchLong }"
- :title="isTargetBranchLong ? mr.targetBranch : ''"
- class="label-branch"
- data-placement="bottom"
+ <tooltip-on-truncate
+ :title="mr.targetBranch"
+ truncate-target="child"
+ class="label-branch label-truncate"
>
<a
:href="mr.targetBranchTreePath"
@@ -96,26 +105,30 @@ export default {
>
{{ mr.targetBranch }}
</a>
- </span>
+ </tooltip-on-truncate>
</strong>
<div
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count"
+ v-html="commitsBehindText"
>
- <span class="monospace">{{ mr.sourceBranch }}</span>
- is {{ commitsText }}
- <span class="monospace">{{ mr.targetBranch }}</span>
</div>
</div>
<div
v-if="mr.isOpen"
- class="branch-actions"
+ class="branch-actions d-flex"
>
<a
v-if="!mr.sourceBranchRemoved"
+ v-tooltip
:href="webIdePath"
- class="btn btn-default inline js-web-ide d-none d-md-inline-block"
+ :title="ideButtonTitle"
+ :class="{ disabled: !mr.canPushToSourceBranch }"
+ class="btn btn-default js-web-ide d-none d-md-inline-block append-right-8"
+ data-placement="bottom"
+ tabindex="0"
+ role="button"
>
{{ s__("mrWidget|Open in Web IDE") }}
</a>
@@ -123,15 +136,15 @@ export default {
:disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info"
data-toggle="modal"
- class="btn btn-default inline js-check-out-branch"
+ class="btn btn-default js-check-out-branch append-right-default"
type="button"
>
{{ s__("mrWidget|Check out branch") }}
</button>
- <span class="dropdown prepend-left-10">
+ <span class="dropdown">
<button
type="button"
- class="btn inline dropdown-toggle"
+ class="btn dropdown-toggle"
data-toggle="dropdown"
aria-label="Download as"
aria-haspopup="true"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue
index 62b61e1f41f..37c6af13c03 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue
@@ -1,24 +1,26 @@
<script>
- import { sprintf, s__ } from '~/locale';
+import { sprintf, s__ } from '~/locale';
- export default {
- name: 'MRWidgetMergeHelp',
- props: {
- missingBranch: {
- type: String,
- required: false,
- default: '',
- },
+export default {
+ name: 'MRWidgetMergeHelp',
+ props: {
+ missingBranch: {
+ type: String,
+ required: false,
+ default: '',
},
- computed: {
- missingBranchInfo() {
- return sprintf(
- s__('mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the'),
- { branch: this.missingBranch },
- );
- },
+ },
+ computed: {
+ missingBranchInfo() {
+ return sprintf(
+ s__(
+ 'mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the',
+ ),
+ { branch: this.missingBranch },
+ );
},
- };
+ },
+};
</script>
<template>
<section class="mr-widget-help">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index 4a3fd01fa39..53608838f2f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -1,8 +1,10 @@
<script>
/* eslint-disable vue/require-default-prop */
+import { sprintf, __ } from '~/locale';
import PipelineStage from '~/pipelines/components/stage.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
export default {
name: 'MRWidgetPipeline',
@@ -10,6 +12,7 @@ export default {
PipelineStage,
CiIcon,
Icon,
+ TooltipOnTruncate,
},
props: {
pipeline: {
@@ -30,6 +33,14 @@ export default {
type: String,
required: false,
},
+ sourceBranch: {
+ type: String,
+ required: false,
+ },
+ troubleshootingDocsPath: {
+ type: String,
+ required: true,
+ },
},
computed: {
hasPipeline() {
@@ -51,6 +62,18 @@ export default {
hasCommitInfo() {
return this.pipeline.commit && Object.keys(this.pipeline.commit).length > 0;
},
+ errorText() {
+ return sprintf(
+ __(
+ 'Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}',
+ ),
+ {
+ linkStart: `<a href="${this.troubleshootingDocsPath}">`,
+ linkEnd: '</a>',
+ },
+ false,
+ );
+ },
},
};
</script>
@@ -71,8 +94,10 @@ export default {
name="status_failed_borderless"
/>
</div>
- <div class="media-body">
- Could not connect to the CI server. Please check your settings and try again
+ <div
+ class="media-body"
+ v-html="errorText"
+ >
</div>
</template>
<template v-else-if="hasPipeline">
@@ -107,11 +132,12 @@ export default {
>
{{ pipeline.commit.short_id }}</a>
on
- <span
- class="label-branch"
+ <tooltip-on-truncate
+ :title="sourceBranch"
+ truncate-target="child"
+ class="label-branch label-truncate"
v-html="sourceBranchLink"
- >
- </span>
+ />
</template>
</div>
<div
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue
index 88d0fcd70f5..cc77b96a589 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue
@@ -1,32 +1,32 @@
<script>
- import { s__ } from '~/locale';
+import { s__ } from '~/locale';
- export default {
- name: 'MRWidgetRelatedLinks',
- props: {
- relatedLinks: {
- type: Object,
- required: true,
- default: () => ({}),
- },
- state: {
- type: String,
- required: false,
- default: '',
- },
+export default {
+ name: 'MRWidgetRelatedLinks',
+ props: {
+ relatedLinks: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- computed: {
- closesText() {
- if (this.state === 'merged') {
- return s__('mrWidget|Closed');
- }
- if (this.state === 'closed') {
- return s__('mrWidget|Did not close');
- }
- return s__('mrWidget|Closes');
- },
+ state: {
+ type: String,
+ required: false,
+ default: '',
},
- };
+ },
+ computed: {
+ closesText() {
+ if (this.state === 'merged') {
+ return s__('mrWidget|Closed');
+ }
+ if (this.state === 'closed') {
+ return s__('mrWidget|Did not close');
+ }
+ return s__('mrWidget|Closes');
+ },
+ },
+};
</script>
<template>
<section class="mr-info-list mr-links">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
index 9aff95dcfec..060361a6516 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
@@ -1,35 +1,35 @@
<script>
- import ciIcon from '../../vue_shared/components/ci_icon.vue';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+import ciIcon from '../../vue_shared/components/ci_icon.vue';
- export default {
- components: {
- ciIcon,
- loadingIcon,
+export default {
+ components: {
+ ciIcon,
+ GlLoadingIcon,
+ },
+ props: {
+ status: {
+ type: String,
+ required: true,
},
- props: {
- status: {
- type: String,
- required: true,
- },
- showDisabledButton: {
- type: Boolean,
- required: false,
- default: false,
- },
+ showDisabledButton: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- computed: {
- isLoading() {
- return this.status === 'loading';
- },
- statusObj() {
- return {
- group: this.status,
- icon: `status_${this.status}`,
- };
- },
+ },
+ computed: {
+ isLoading() {
+ return this.status === 'loading';
},
- };
+ statusObj() {
+ return {
+ group: this.status,
+ icon: `status_${this.status}`,
+ };
+ },
+ },
+};
</script>
<template>
<div class="space-children d-flex append-right-10 widget-status-icon">
@@ -37,7 +37,7 @@
v-if="isLoading"
class="mr-widget-icon"
>
- <loading-icon />
+ <gl-loading-icon />
</div>
<ci-icon
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue
new file mode 100644
index 00000000000..b007d4f4dcb
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue
@@ -0,0 +1,30 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ components: {
+ Icon,
+ },
+ props: {
+ link: {
+ type: String,
+ required: true,
+ },
+ cssClass: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <a
+ :href="link"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ :class="cssClass"
+ >
+ {{ __('View app') }}
+ <icon name="external-link" />
+ </a>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue
index 56879c04d16..01f707163d4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue
@@ -1,16 +1,16 @@
<script>
- import tooltip from '../../vue_shared/directives/tooltip';
- import { __ } from '../../locale';
+import tooltip from '../../vue_shared/directives/tooltip';
+import { __ } from '../../locale';
- export default {
- directives: {
- tooltip,
- },
- created() {
- this.removesBranchText = __('<strong>Removes</strong> source branch');
- this.tooltipTitle = __('A user with write access to the source branch selected this option');
- },
- };
+export default {
+ directives: {
+ tooltip,
+ },
+ created() {
+ this.removesBranchText = __('<strong>Removes</strong> source branch');
+ this.tooltipTitle = __('A user with write access to the source branch selected this option');
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
index cfbd44d41b2..2a76a878448 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
@@ -1,12 +1,12 @@
<script>
- import statusIcon from '../mr_widget_status_icon.vue';
+import statusIcon from '../mr_widget_status_icon.vue';
- export default {
- name: 'MRWidgetArchived',
- components: {
- statusIcon,
- },
- };
+export default {
+ name: 'MRWidgetArchived',
+ components: {
+ statusIcon,
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
index 2133124347c..409fc2f2db4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
@@ -1,34 +1,34 @@
<script>
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
- import eventHub from '../../event_hub';
- import statusIcon from '../mr_widget_status_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+import eventHub from '../../event_hub';
+import statusIcon from '../mr_widget_status_icon.vue';
- export default {
- name: 'MRWidgetAutoMergeFailed',
- components: {
- statusIcon,
- loadingIcon,
+export default {
+ name: 'MRWidgetAutoMergeFailed',
+ components: {
+ statusIcon,
+ GlLoadingIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
},
- props: {
- mr: {
- type: Object,
- required: true,
- },
+ },
+ data() {
+ return {
+ isRefreshing: false,
+ };
+ },
+ methods: {
+ refreshWidget() {
+ this.isRefreshing = true;
+ eventHub.$emit('MRWidgetUpdateRequested', () => {
+ this.isRefreshing = false;
+ });
},
- data() {
- return {
- isRefreshing: false,
- };
- },
- methods: {
- refreshWidget() {
- this.isRefreshing = true;
- eventHub.$emit('MRWidgetUpdateRequested', () => {
- this.isRefreshing = false;
- });
- },
- },
- };
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
@@ -44,7 +44,7 @@
class="btn btn-sm btn-default"
@click="refreshWidget"
>
- <loading-icon
+ <gl-loading-icon
v-if="isRefreshing"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
index ae6630dcd6f..fdf0a9fd994 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
@@ -1,12 +1,12 @@
<script>
- import statusIcon from '../mr_widget_status_icon.vue';
+import statusIcon from '../mr_widget_status_icon.vue';
- export default {
- name: 'MRWidgetChecking',
- components: {
- statusIcon,
- },
- };
+export default {
+ name: 'MRWidgetChecking',
+ components: {
+ statusIcon,
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
index 25a57e520ee..f06eab95c7e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
@@ -1,23 +1,23 @@
<script>
- import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
- import statusIcon from '../mr_widget_status_icon.vue';
+import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
+import statusIcon from '../mr_widget_status_icon.vue';
- export default {
- name: 'MRWidgetClosed',
- components: {
- MrWidgetAuthorTime,
- statusIcon,
- },
- props: {
- /* TODO: This is providing all store and service down when it
+export default {
+ name: 'MRWidgetClosed',
+ components: {
+ MrWidgetAuthorTime,
+ statusIcon,
+ },
+ props: {
+ /* TODO: This is providing all store and service down when it
only needs metrics and targetBranch */
- mr: {
- type: Object,
- required: true,
- default: () => ({}),
- },
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- };
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
index dff9ec657b9..8c808296178 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
@@ -1,21 +1,21 @@
<script>
- import statusIcon from '../mr_widget_status_icon.vue';
+import statusIcon from '../mr_widget_status_icon.vue';
- export default {
- name: 'MRWidgetConflicts',
- components: {
- statusIcon,
- },
- props: {
- /* TODO: This is providing all store and service down when it
+export default {
+ name: 'MRWidgetConflicts',
+ components: {
+ statusIcon,
+ },
+ props: {
+ /* TODO: This is providing all store and service down when it
only needs a few props */
- mr: {
- type: Object,
- required: true,
- default: () => ({}),
- },
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- };
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
index 97f4196b94d..484b5600d63 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
@@ -1,82 +1,82 @@
<script>
- import Flash from '../../../flash';
- import statusIcon from '../mr_widget_status_icon.vue';
- import MrWidgetAuthor from '../../components/mr_widget_author.vue';
- import eventHub from '../../event_hub';
+import Flash from '../../../flash';
+import statusIcon from '../mr_widget_status_icon.vue';
+import MrWidgetAuthor from '../../components/mr_widget_author.vue';
+import eventHub from '../../event_hub';
- export default {
- name: 'MRWidgetMergeWhenPipelineSucceeds',
- components: {
- MrWidgetAuthor,
- statusIcon,
+export default {
+ name: 'MRWidgetMergeWhenPipelineSucceeds',
+ components: {
+ MrWidgetAuthor,
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- props: {
- mr: {
- type: Object,
- required: true,
- default: () => ({}),
- },
- service: {
- type: Object,
- required: true,
- default: () => ({}),
- },
+ service: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- data() {
- return {
- isCancellingAutoMerge: false,
- isRemovingSourceBranch: false,
- };
- },
- computed: {
- canRemoveSourceBranch() {
- const {
- shouldRemoveSourceBranch,
- canRemoveSourceBranch,
- mergeUserId,
- currentUserId,
- } = this.mr;
+ },
+ data() {
+ return {
+ isCancellingAutoMerge: false,
+ isRemovingSourceBranch: false,
+ };
+ },
+ computed: {
+ canRemoveSourceBranch() {
+ const {
+ shouldRemoveSourceBranch,
+ canRemoveSourceBranch,
+ mergeUserId,
+ currentUserId,
+ } = this.mr;
- return !shouldRemoveSourceBranch &&
- canRemoveSourceBranch &&
- mergeUserId === currentUserId;
- },
+ return !shouldRemoveSourceBranch && canRemoveSourceBranch && mergeUserId === currentUserId;
},
- methods: {
- cancelAutomaticMerge() {
- this.isCancellingAutoMerge = true;
- this.service.cancelAutomaticMerge()
- .then(res => res.data)
- .then((data) => {
- eventHub.$emit('UpdateWidgetData', data);
- })
- .catch(() => {
- this.isCancellingAutoMerge = false;
- Flash('Something went wrong. Please try again.');
- });
- },
- removeSourceBranch() {
- const options = {
- sha: this.mr.sha,
- merge_when_pipeline_succeeds: true,
- should_remove_source_branch: true,
- };
+ },
+ methods: {
+ cancelAutomaticMerge() {
+ this.isCancellingAutoMerge = true;
+ this.service
+ .cancelAutomaticMerge()
+ .then(res => res.data)
+ .then(data => {
+ eventHub.$emit('UpdateWidgetData', data);
+ })
+ .catch(() => {
+ this.isCancellingAutoMerge = false;
+ Flash('Something went wrong. Please try again.');
+ });
+ },
+ removeSourceBranch() {
+ const options = {
+ sha: this.mr.sha,
+ merge_when_pipeline_succeeds: true,
+ should_remove_source_branch: true,
+ };
- this.isRemovingSourceBranch = true;
- this.service.merge(options)
- .then(res => res.data)
- .then((data) => {
- if (data.status === 'merge_when_pipeline_succeeds') {
- eventHub.$emit('MRWidgetUpdateRequested');
- }
- })
- .catch(() => {
- this.isRemovingSourceBranch = false;
- Flash('Something went wrong. Please try again.');
- });
- },
+ this.isRemovingSourceBranch = true;
+ this.service
+ .merge(options)
+ .then(res => res.data)
+ .then(data => {
+ if (data.status === 'merge_when_pipeline_succeeds') {
+ eventHub.$emit('MRWidgetUpdateRequested');
+ }
+ })
+ .catch(() => {
+ this.isRemovingSourceBranch = false;
+ Flash('Something went wrong. Please try again.');
+ });
},
- };
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
index 1a444c04a1d..fb9ca897844 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -1,108 +1,102 @@
<script>
- import Flash from '~/flash';
- import tooltip from '~/vue_shared/directives/tooltip';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
- import { s__, __ } from '~/locale';
- import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
- import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
- import statusIcon from '../mr_widget_status_icon.vue';
- import eventHub from '../../event_hub';
+import Flash from '~/flash';
+import tooltip from '~/vue_shared/directives/tooltip';
+import { s__, __ } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
+import statusIcon from '../mr_widget_status_icon.vue';
+import eventHub from '../../event_hub';
+import { GlLoadingIcon } from '@gitlab/ui';
- export default {
- name: 'MRWidgetMerged',
- directives: {
- tooltip,
+export default {
+ name: 'MRWidgetMerged',
+ directives: {
+ tooltip,
+ },
+ components: {
+ MrWidgetAuthorTime,
+ statusIcon,
+ ClipboardButton,
+ GlLoadingIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- components: {
- MrWidgetAuthorTime,
- loadingIcon,
- statusIcon,
- ClipboardButton,
+ service: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- props: {
- mr: {
- type: Object,
- required: true,
- default: () => ({}),
- },
- service: {
- type: Object,
- required: true,
- default: () => ({}),
- },
+ },
+ data() {
+ return {
+ isMakingRequest: false,
+ };
+ },
+ computed: {
+ shouldShowRemoveSourceBranch() {
+ const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
+
+ return (
+ !sourceBranchRemoved &&
+ canRemoveSourceBranch &&
+ !this.isMakingRequest &&
+ !isRemovingSourceBranch
+ );
},
- data() {
- return {
- isMakingRequest: false,
- };
+ shouldShowSourceBranchRemoving() {
+ const { sourceBranchRemoved, isRemovingSourceBranch } = this.mr;
+ return !sourceBranchRemoved && (isRemovingSourceBranch || this.isMakingRequest);
},
- computed: {
- shouldShowRemoveSourceBranch() {
- const {
- sourceBranchRemoved,
- isRemovingSourceBranch,
- canRemoveSourceBranch,
- } = this.mr;
-
- return !sourceBranchRemoved &&
- canRemoveSourceBranch &&
- !this.isMakingRequest &&
- !isRemovingSourceBranch;
- },
- shouldShowSourceBranchRemoving() {
- const {
- sourceBranchRemoved,
- isRemovingSourceBranch,
- } = this.mr;
- return !sourceBranchRemoved &&
- (isRemovingSourceBranch || this.isMakingRequest);
- },
- shouldShowMergedButtons() {
- const {
- canRevertInCurrentMR,
- canCherryPickInCurrentMR,
- revertInForkPath,
- cherryPickInForkPath,
- } = this.mr;
+ shouldShowMergedButtons() {
+ const {
+ canRevertInCurrentMR,
+ canCherryPickInCurrentMR,
+ revertInForkPath,
+ cherryPickInForkPath,
+ } = this.mr;
- return canRevertInCurrentMR ||
- canCherryPickInCurrentMR ||
- revertInForkPath ||
- cherryPickInForkPath;
- },
- revertTitle() {
- return s__('mrWidget|Revert this merge request in a new merge request');
- },
- cherryPickTitle() {
- return s__('mrWidget|Cherry-pick this merge request in a new merge request');
- },
- revertLabel() {
- return s__('mrWidget|Revert');
- },
- cherryPickLabel() {
- return s__('mrWidget|Cherry-pick');
- },
+ return (
+ canRevertInCurrentMR || canCherryPickInCurrentMR || revertInForkPath || cherryPickInForkPath
+ );
+ },
+ revertTitle() {
+ return s__('mrWidget|Revert this merge request in a new merge request');
+ },
+ cherryPickTitle() {
+ return s__('mrWidget|Cherry-pick this merge request in a new merge request');
},
- methods: {
- removeSourceBranch() {
- this.isMakingRequest = true;
+ revertLabel() {
+ return s__('mrWidget|Revert');
+ },
+ cherryPickLabel() {
+ return s__('mrWidget|Cherry-pick');
+ },
+ },
+ methods: {
+ removeSourceBranch() {
+ this.isMakingRequest = true;
- this.service.removeSourceBranch()
- .then(res => res.data)
- .then((data) => {
- if (data.message === 'Branch was removed') {
- eventHub.$emit('MRWidgetUpdateRequested', () => {
- this.isMakingRequest = false;
- });
- }
- })
- .catch(() => {
- this.isMakingRequest = false;
- Flash(__('Something went wrong. Please try again.'));
- });
- },
+ this.service
+ .removeSourceBranch()
+ .then(res => res.data)
+ .then(data => {
+ if (data.message === 'Branch was removed') {
+ eventHub.$emit('MRWidgetUpdateRequested', () => {
+ this.isMakingRequest = false;
+ });
+ }
+ })
+ .catch(() => {
+ this.isMakingRequest = false;
+ Flash(__('Something went wrong. Please try again.'));
+ });
},
- };
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
@@ -116,8 +110,8 @@
:date-readable="mr.metrics.readableMergedAt"
/>
<a
- v-tooltip
v-if="mr.canRevertInCurrentMR"
+ v-tooltip
:title="revertTitle"
class="btn btn-close btn-sm"
href="#modal-revert-commit"
@@ -127,8 +121,8 @@
{{ revertLabel }}
</a>
<a
- v-tooltip
v-else-if="mr.revertInForkPath"
+ v-tooltip
:href="mr.revertInForkPath"
:title="revertTitle"
class="btn btn-close btn-sm"
@@ -137,8 +131,8 @@
{{ revertLabel }}
</a>
<a
- v-tooltip
v-if="mr.canCherryPickInCurrentMR"
+ v-tooltip
:title="cherryPickTitle"
class="btn btn-default btn-sm"
href="#modal-cherry-pick-commit"
@@ -148,8 +142,8 @@
{{ cherryPickLabel }}
</a>
<a
- v-tooltip
v-else-if="mr.cherryPickInForkPath"
+ v-tooltip
:href="mr.cherryPickInForkPath"
:title="cherryPickTitle"
class="btn btn-default btn-sm"
@@ -164,18 +158,20 @@
<span class="label-branch">
<a :href="mr.targetBranchPath">{{ mr.targetBranch }}</a>
</span>
- with
- <a
- :href="mr.mergeCommitPath"
- class="commit-sha js-mr-merged-commit-sha"
- v-text="mr.shortMergeCommitSha"
- >
- </a>
- <clipboard-button
- :title="__('Copy commit SHA to clipboard')"
- :text="mr.mergeCommitSha"
- css-class="btn-default btn-transparent btn-clipboard js-mr-merged-copy-sha"
- />
+ <template v-if="mr.mergeCommitSha">
+ with
+ <a
+ :href="mr.mergeCommitPath"
+ class="commit-sha js-mr-merged-commit-sha"
+ v-text="mr.shortMergeCommitSha"
+ >
+ </a>
+ <clipboard-button
+ :title="__('Copy commit SHA to clipboard')"
+ :text="mr.mergeCommitSha"
+ css-class="btn-default btn-transparent btn-clipboard js-mr-merged-copy-sha"
+ />
+ </template>
</p>
<p v-if="mr.sourceBranchRemoved">
{{ s__("mrWidget|The source branch has been removed") }}
@@ -195,7 +191,7 @@
</button>
</p>
<p v-if="shouldShowSourceBranchRemoving">
- <loading-icon :inline="true" />
+ <gl-loading-icon :inline="true" />
<span>
{{ s__("mrWidget|The source branch is being removed") }}
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
index 953ddf40a51..139e64d1878 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
@@ -1,19 +1,19 @@
<script>
- import statusIcon from '../mr_widget_status_icon.vue';
+import statusIcon from '../mr_widget_status_icon.vue';
- export default {
- name: 'MRWidgetMerging',
- components: {
- statusIcon,
+export default {
+ name: 'MRWidgetMerging',
+ components: {
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- props: {
- mr: {
- type: Object,
- required: true,
- default: () => ({}),
- },
- },
- };
+ },
+};
</script>
<template>
<div class="mr-widget-body mr-state-locked media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
index b0e96f74626..227e9b85f9d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
@@ -1,40 +1,48 @@
<script>
- import { sprintf, s__ } from '~/locale';
- import tooltip from '~/vue_shared/directives/tooltip';
- import statusIcon from '../mr_widget_status_icon.vue';
- import mrWidgetMergeHelp from '../../components/mr_widget_merge_help.vue';
+import { sprintf, s__ } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import statusIcon from '../mr_widget_status_icon.vue';
+import mrWidgetMergeHelp from '../../components/mr_widget_merge_help.vue';
- export default {
- name: 'MRWidgetMissingBranch',
- directives: {
- tooltip,
+export default {
+ name: 'MRWidgetMissingBranch',
+ directives: {
+ tooltip,
+ },
+ components: {
+ mrWidgetMergeHelp,
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
},
- components: {
- mrWidgetMergeHelp,
- statusIcon,
+ },
+ computed: {
+ missingBranchName() {
+ return this.mr.sourceBranchRemoved ? 'source' : 'target';
},
- props: {
- mr: {
- type: Object,
- required: true,
- },
- },
- computed: {
- missingBranchName() {
- return this.mr.sourceBranchRemoved ? 'source' : 'target';
- },
- missingBranchNameMessage() {
- return sprintf(s__('mrWidget| Please restore it or use a different %{missingBranchName} branch'), {
+ missingBranchNameMessage() {
+ return sprintf(
+ s__('mrWidget| Please restore it or use a different %{missingBranchName} branch'),
+ {
missingBranchName: this.missingBranchName,
- });
- },
- message() {
- return sprintf(s__('mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line'), {
+ },
+ );
+ },
+ message() {
+ return sprintf(
+ s__(
+ 'mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line',
+ ),
+ {
missingBranchName: this.missingBranchName,
- });
- },
+ },
+ );
},
- };
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
index 92eee2cf5dd..360559ae0f0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
@@ -1,12 +1,12 @@
<script>
- import StatusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
- export default {
- name: 'MRWidgetNotAllowed',
- components: {
- StatusIcon,
- },
- };
+export default {
+ name: 'MRWidgetNotAllowed',
+ components: {
+ StatusIcon,
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
index 37ee5215cea..a4eb5afb21c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
@@ -1,12 +1,12 @@
<script>
- import StatusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
- export default {
- name: 'MRWidgetPipelineBlocked',
- components: {
- StatusIcon,
- },
- };
+export default {
+ name: 'MRWidgetPipelineBlocked',
+ components: {
+ StatusIcon,
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 2d8c3d6be87..5a7f6cc36fa 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -1,87 +1,89 @@
<script>
- import simplePoll from '../../../lib/utils/simple_poll';
- import eventHub from '../../event_hub';
- import statusIcon from '../mr_widget_status_icon.vue';
- import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
- import Flash from '../../../flash';
+import { GlLoadingIcon } from '@gitlab/ui';
+import simplePoll from '../../../lib/utils/simple_poll';
+import eventHub from '../../event_hub';
+import statusIcon from '../mr_widget_status_icon.vue';
+import Flash from '../../../flash';
- export default {
- name: 'MRWidgetRebase',
- components: {
- statusIcon,
- loadingIcon,
+export default {
+ name: 'MRWidgetRebase',
+ components: {
+ statusIcon,
+ GlLoadingIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
},
- props: {
- mr: {
- type: Object,
- required: true,
- },
- service: {
- type: Object,
- required: true,
- },
+ service: {
+ type: Object,
+ required: true,
},
- data() {
- return {
- isMakingRequest: false,
- rebasingError: null,
- };
+ },
+ data() {
+ return {
+ isMakingRequest: false,
+ rebasingError: null,
+ };
+ },
+ computed: {
+ status() {
+ if (this.mr.rebaseInProgress || this.isMakingRequest) {
+ return 'loading';
+ }
+ if (!this.mr.canPushToSourceBranch && !this.mr.rebaseInProgress) {
+ return 'warning';
+ }
+ return 'success';
},
- computed: {
- status() {
- if (this.mr.rebaseInProgress || this.isMakingRequest) {
- return 'loading';
- }
- if (!this.mr.canPushToSourceBranch && !this.mr.rebaseInProgress) {
- return 'warning';
- }
- return 'success';
- },
- showDisabledButton() {
- return ['failed', 'loading'].includes(this.status);
- },
+ showDisabledButton() {
+ return ['failed', 'loading'].includes(this.status);
},
- methods: {
- rebase() {
- this.isMakingRequest = true;
- this.rebasingError = null;
+ },
+ methods: {
+ rebase() {
+ this.isMakingRequest = true;
+ this.rebasingError = null;
- this.service.rebase()
- .then(() => {
- simplePoll(this.checkRebaseStatus);
- })
- .catch((error) => {
- this.rebasingError = error.merge_error;
+ this.service
+ .rebase()
+ .then(() => {
+ simplePoll(this.checkRebaseStatus);
+ })
+ .catch(error => {
+ this.rebasingError = error.merge_error;
+ this.isMakingRequest = false;
+ Flash('Something went wrong. Please try again.');
+ });
+ },
+ checkRebaseStatus(continuePolling, stopPolling) {
+ this.service
+ .poll()
+ .then(res => res.data)
+ .then(res => {
+ if (res.rebase_in_progress) {
+ continuePolling();
+ } else {
this.isMakingRequest = false;
- Flash('Something went wrong. Please try again.');
- });
- },
- checkRebaseStatus(continuePolling, stopPolling) {
- this.service.poll()
- .then(res => res.data)
- .then((res) => {
- if (res.rebase_in_progress) {
- continuePolling();
- } else {
- this.isMakingRequest = false;
-
- if (res.merge_error && res.merge_error.length) {
- this.rebasingError = res.merge_error;
- Flash('Something went wrong. Please try again.');
- }
- eventHub.$emit('MRWidgetUpdateRequested');
- stopPolling();
+ if (res.merge_error && res.merge_error.length) {
+ this.rebasingError = res.merge_error;
+ Flash('Something went wrong. Please try again.');
}
- })
- .catch(() => {
- this.isMakingRequest = false;
- Flash('Something went wrong. Please try again.');
+
+ eventHub.$emit('MRWidgetUpdateRequested');
stopPolling();
- });
- },
+ }
+ })
+ .catch(() => {
+ this.isMakingRequest = false;
+ Flash('Something went wrong. Please try again.');
+ stopPolling();
+ });
},
- };
+ },
+};
</script>
<template>
<div class="mr-widget-body media">
@@ -115,7 +117,7 @@ js-toggle-container accept-action media space-children"
class="btn btn-sm btn-reopen btn-success qa-mr-rebase-button"
@click="rebase"
>
- <loading-icon v-if="isMakingRequest" />
+ <gl-loading-icon v-if="isMakingRequest" />
Rebase
</button>
<span
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.js
deleted file mode 100644
index bf8628d18a6..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
-The squash-before-merge button is EE only, but it's located right in the middle
-of the readyToMerge state component template.
-
-If we didn't declare this component in CE, we'd need to maintain a separate copy
-of the readyToMergeState template in EE, which is pretty big and likely to change.
-
-Instead, in CE, we declare the component, but it's hidden and is configured to do nothing.
-In EE, the configuration extends this object to add a functioning squash-before-merge
-button.
-*/
-
-export default {
- template: '',
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue
deleted file mode 100644
index 25c1044fe2b..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue
+++ /dev/null
@@ -1,63 +0,0 @@
-<script>
-import Icon from '~/vue_shared/components/icon.vue';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-import tooltip from '~/vue_shared/directives/tooltip';
-
-export default {
- components: {
- Icon,
- },
- directives: {
- tooltip,
- },
- props: {
- mr: {
- type: Object,
- required: true,
- },
- isMergeButtonDisabled: {
- type: Boolean,
- required: true,
- },
- },
- data() {
- return {
- squashBeforeMerge: this.mr.squash,
- };
- },
- methods: {
- updateSquashModel() {
- eventHub.$emit('MRWidgetUpdateSquash', this.squashBeforeMerge);
- },
- },
-};
-</script>
-
-<template>
- <div class="accept-control inline">
- <label class="merge-param-checkbox">
- <input
- :disabled="isMergeButtonDisabled"
- v-model="squashBeforeMerge"
- type="checkbox"
- name="squash"
- class="qa-squash-checkbox"
- @change="updateSquashModel"
- />
- {{ __('Squash commits') }}
- </label>
- <a
- v-tooltip
- :href="mr.squashBeforeMergeHelpPath"
- data-title="About this feature"
- data-placement="bottom"
- target="_blank"
- rel="noopener noreferrer nofollow"
- data-container="body"
- >
- <icon
- name="question-o"
- />
- </a>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
index 086dbabe77e..e73b7e410d5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
@@ -37,7 +37,7 @@ export default {
<a
v-if="mr.newBlobPath"
:href="mr.newBlobPath"
- class="btn btn-inverted btn-save">
+ class="btn btn-inverted btn-success">
Create file
</a>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index a5ca7b719a1..e7baecbcde4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -6,7 +6,7 @@ import MergeRequest from '../../../merge_request';
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
-import SquashBeforeMerge from './mr_widget_squash_before_merge.vue';
+import SquashBeforeMerge from './squash_before_merge.vue';
export default {
name: 'ReadyToMerge',
@@ -71,7 +71,12 @@ export default {
return defaultClass;
},
iconClass() {
- if (this.status === 'failed' || !this.commitMessage.length || !this.mr.isMergeAllowed || this.mr.preventMerge) {
+ if (
+ this.status === 'failed' ||
+ !this.commitMessage.length ||
+ !this.mr.isMergeAllowed ||
+ this.mr.preventMerge
+ ) {
return 'warning';
}
return 'success';
@@ -90,10 +95,12 @@ export default {
},
isMergeButtonDisabled() {
const { commitMessage } = this;
- return Boolean(!commitMessage.length
- || !this.shouldShowMergeControls()
- || this.isMakingRequest
- || this.mr.preventMerge);
+ return Boolean(
+ !commitMessage.length ||
+ !this.shouldShowMergeControls() ||
+ this.isMakingRequest ||
+ this.mr.preventMerge,
+ );
},
isRemoveSourceBranchButtonDisabled() {
return this.isMergeButtonDisabled;
@@ -140,9 +147,10 @@ export default {
};
this.isMakingRequest = true;
- this.service.merge(options)
+ this.service
+ .merge(options)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
const hasError = data.status === 'failed' || data.status === 'hook_validation_error';
if (data.status === 'merge_when_pipeline_succeeds') {
@@ -167,9 +175,10 @@ export default {
});
},
handleMergePolling(continuePolling, stopPolling) {
- this.service.poll()
+ this.service
+ .poll()
.then(res => res.data)
- .then((data) => {
+ .then(data => {
if (data.state === 'merged') {
// If state is merged we should update the widget and stop the polling
eventHub.$emit('MRWidgetUpdateRequested');
@@ -205,9 +214,10 @@ export default {
});
},
handleRemoveBranchPolling(continuePolling, stopPolling) {
- this.service.poll()
+ this.service
+ .poll()
.then(res => res.data)
- .then((data) => {
+ .then(data => {
// If source branch exists then we should continue polling
// because removing a source branch is a background task and takes time
if (data.source_branch_exists) {
@@ -255,7 +265,7 @@ export default {
data-toggle="dropdown"
aria-label="Select merge moment">
<i
- class="fa fa-chevron-down"
+ class="fa fa-chevron-down qa-merge-moment-dropdown"
aria-hidden="true"
></i>
</button>
@@ -265,7 +275,7 @@ export default {
role="menu">
<li>
<a
- class="merge_when_pipeline_succeeds"
+ class="merge_when_pipeline_succeeds qa-merge-when-pipeline-succeeds-option"
href="#"
@click.prevent="handleMergeButtonClick(true)">
<span class="media">
@@ -279,7 +289,7 @@ export default {
</li>
<li>
<a
- class="accept-merge-request"
+ class="accept-merge-request qa-merge-immediately-option"
href="#"
@click.prevent="handleMergeButtonClick(false, true)">
<span class="media">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
new file mode 100644
index 00000000000..25ad329e196
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
@@ -0,0 +1,63 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import eventHub from '~/vue_merge_request_widget/event_hub';
+import tooltip from '~/vue_shared/directives/tooltip';
+
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
+ isMergeButtonDisabled: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ squashBeforeMerge: this.mr.squash,
+ };
+ },
+ methods: {
+ updateSquashModel() {
+ eventHub.$emit('MRWidgetUpdateSquash', this.squashBeforeMerge);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="accept-control inline">
+ <label class="merge-param-checkbox">
+ <input
+ v-model="squashBeforeMerge"
+ :disabled="isMergeButtonDisabled"
+ type="checkbox"
+ name="squash"
+ class="qa-squash-checkbox"
+ @change="updateSquashModel"
+ />
+ {{ __('Squash commits') }}
+ </label>
+ <a
+ v-tooltip
+ :href="mr.squashBeforeMergeHelpPath"
+ data-title="About this feature"
+ data-placement="bottom"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ data-container="body"
+ >
+ <icon
+ name="question-o"
+ />
+ </a>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
index 89c9a41f316..9129fcbb918 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
@@ -24,9 +24,10 @@ export default {
methods: {
removeWIP() {
this.isMakingRequest = true;
- this.service.removeWIP()
+ this.service
+ .removeWIP()
.then(res => res.data)
- .then((data) => {
+ .then(data => {
eventHub.$emit('UpdateWidgetData', data);
new window.Flash('The merge request can now be merged.', 'notice'); // eslint-disable-line
$('.merge-request .detail-page-description .title').text(this.mr.title);
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
deleted file mode 100644
index 15097fa2a3f..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * This file is the centerpiece of an attempt to reduce potential conflicts
- * between the CE and EE versions of the MR widget. EE additions to the MR widget should
- * be contained in the ee/vue_merge_request_widget directory, and should **extend**
- * rather than mutate CE MR Widget code.
- *
- * This file should be the only source of conflicts between EE and CE. EE-only components should
- * imported directly where they are needed, and import paths for EE extensions of CE components
- * should overwrite import paths **without** changing the order of dependencies listed here.
- */
-
-export { default as Vue } from 'vue';
-export { default as SmartInterval } from '~/smart_interval';
-export { default as WidgetHeader } from './components/mr_widget_header.vue';
-export { default as WidgetMergeHelp } from './components/mr_widget_merge_help.vue';
-export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue';
-export { default as Deployment } from './components/deployment.vue';
-export { default as WidgetRelatedLinks } from './components/mr_widget_related_links.vue';
-export { default as MergedState } from './components/states/mr_widget_merged.vue';
-export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue';
-export { default as ClosedState } from './components/states/mr_widget_closed.vue';
-export { default as MergingState } from './components/states/mr_widget_merging.vue';
-export { default as WorkInProgressState } from './components/states/work_in_progress.vue';
-export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
-export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
-export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue';
-export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue';
-export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue';
-export { default as ReadyToMergeState } from './components/states/ready_to_merge.vue';
-export { default as ShaMismatchState } from './components/states/sha_mismatch.vue';
-export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue';
-export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue';
-export { default as PipelineFailedState } from './components/states/pipeline_failed.vue';
-export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue';
-export { default as RebaseState } from './components/states/mr_widget_rebase.vue';
-export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue';
-export { default as CheckingState } from './components/states/mr_widget_checking.vue';
-export { default as MRWidgetStore } from './stores/mr_widget_store';
-export { default as MRWidgetService } from './services/mr_widget_service';
-export { default as eventHub } from './event_hub';
-export { default as getStateKey } from './stores/get_state_key';
-export { default as stateMaps } from './stores/state_maps';
-export { default as SquashBeforeMerge } from './components/states/mr_widget_squash_before_merge.vue';
-export { default as notify } from '../lib/utils/notify';
-export { default as SourceBranchRemovalStatus } from './components/source_branch_removal_status.vue';
-
-export { default as mrWidgetOptions } from './mr_widget_options.vue';
diff --git a/app/assets/javascripts/vue_merge_request_widget/ee_switch_mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/ee_switch_mr_widget_options.js
new file mode 100644
index 00000000000..8780aa4bd1c
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/ee_switch_mr_widget_options.js
@@ -0,0 +1,3 @@
+import MRWidgetOptions from './mr_widget_options.vue';
+
+export default MRWidgetOptions;
diff --git a/app/assets/javascripts/vue_merge_request_widget/index.js b/app/assets/javascripts/vue_merge_request_widget/index.js
index 69a9132a2da..60cebbfc2b2 100644
--- a/app/assets/javascripts/vue_merge_request_widget/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/index.js
@@ -1,7 +1,5 @@
-import {
- Vue,
- mrWidgetOptions,
-} from './dependencies';
+import Vue from 'vue';
+import MrWidgetOptions from './ee_switch_mr_widget_options';
import Translate from '../vue_shared/translate';
Vue.use(Translate);
@@ -9,7 +7,7 @@ Vue.use(Translate);
export default () => {
gl.mrWidgetData.gitlabLogo = gon.gitlab_logo;
- const vm = new Vue(mrWidgetOptions);
+ const vm = new Vue(MrWidgetOptions);
window.gl.mrWidget = {
checkStatus: vm.checkStatus,
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index 80593d1f34a..3b840540657 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -1,41 +1,40 @@
<script>
-
+import _ from 'underscore';
+import { __ } from '~/locale';
import Project from '~/pages/projects/project';
import SmartInterval from '~/smart_interval';
import createFlash from '../flash';
-import {
- WidgetHeader,
- WidgetMergeHelp,
- WidgetPipeline,
- Deployment,
- WidgetRelatedLinks,
- MergedState,
- ClosedState,
- MergingState,
- RebaseState,
- WorkInProgressState,
- ArchivedState,
- ConflictsState,
- NothingToMergeState,
- MissingBranchState,
- NotAllowedState,
- ReadyToMergeState,
- ShaMismatchState,
- UnresolvedDiscussionsState,
- PipelineBlockedState,
- PipelineFailedState,
- FailedToMerge,
- MergeWhenPipelineSucceedsState,
- AutoMergeFailed,
- CheckingState,
- MRWidgetStore,
- MRWidgetService,
- eventHub,
- stateMaps,
- SquashBeforeMerge,
- notify,
- SourceBranchRemovalStatus,
-} from './dependencies';
+import WidgetHeader from './components/mr_widget_header.vue';
+import WidgetMergeHelp from './components/mr_widget_merge_help.vue';
+import WidgetPipeline from './components/mr_widget_pipeline.vue';
+import Deployment from './components/deployment.vue';
+import WidgetRelatedLinks from './components/mr_widget_related_links.vue';
+import MergedState from './components/states/mr_widget_merged.vue';
+import ClosedState from './components/states/mr_widget_closed.vue';
+import MergingState from './components/states/mr_widget_merging.vue';
+import RebaseState from './components/states/mr_widget_rebase.vue';
+import WorkInProgressState from './components/states/work_in_progress.vue';
+import ArchivedState from './components/states/mr_widget_archived.vue';
+import ConflictsState from './components/states/mr_widget_conflicts.vue';
+import NothingToMergeState from './components/states/nothing_to_merge.vue';
+import MissingBranchState from './components/states/mr_widget_missing_branch.vue';
+import NotAllowedState from './components/states/mr_widget_not_allowed.vue';
+import ReadyToMergeState from './components/states/ready_to_merge.vue';
+import ShaMismatchState from './components/states/sha_mismatch.vue';
+import UnresolvedDiscussionsState from './components/states/unresolved_discussions.vue';
+import PipelineBlockedState from './components/states/mr_widget_pipeline_blocked.vue';
+import PipelineFailedState from './components/states/pipeline_failed.vue';
+import FailedToMerge from './components/states/mr_widget_failed_to_merge.vue';
+import MergeWhenPipelineSucceedsState from './components/states/mr_widget_merge_when_pipeline_succeeds.vue';
+import AutoMergeFailed from './components/states/mr_widget_auto_merge_failed.vue';
+import CheckingState from './components/states/mr_widget_checking.vue';
+import MRWidgetStore from './stores/ee_switch_mr_widget_store';
+import MRWidgetService from './services/ee_switch_mr_widget_service';
+import eventHub from './event_hub';
+import stateMaps from './stores/ee_switch_state_maps';
+import SquashBeforeMerge from './components/states/squash_before_merge.vue';
+import notify from '~/lib/utils/notify';
+import SourceBranchRemovalStatus from './components/source_branch_removal_status.vue';
import GroupedTestReportsApp from '../reports/components/grouped_test_reports_app.vue';
import { setFaviconOverlay } from '../lib/utils/common_utils';
@@ -83,6 +82,7 @@ export default {
const service = this.createService(store);
return {
mr: store,
+ state: store.state,
service,
};
},
@@ -100,16 +100,45 @@ export default {
return !!this.mr.relatedLinks && !this.mr.isNothingToMergeState;
},
shouldRenderSourceBranchRemovalStatus() {
- return !this.mr.canRemoveSourceBranch && this.mr.shouldRemoveSourceBranch &&
- (!this.mr.isNothingToMergeState && !this.mr.isMergedState);
+ return (
+ !this.mr.canRemoveSourceBranch &&
+ this.mr.shouldRemoveSourceBranch &&
+ (!this.mr.isNothingToMergeState && !this.mr.isMergedState)
+ );
+ },
+ shouldRenderMergedPipeline() {
+ return this.mr.state === 'merged' && !_.isEmpty(this.mr.mergePipeline);
+ },
+ },
+ watch: {
+ state(newVal, oldVal) {
+ if (newVal !== oldVal && this.shouldRenderMergedPipeline) {
+ // init polling
+ this.initPostMergeDeploymentsPolling();
+ }
},
},
created() {
this.initPolling();
this.bindEventHubListeners();
+ eventHub.$on('mr.discussion.updated', this.checkStatus);
},
mounted() {
- this.handleMounted();
+ this.setFaviconHelper();
+ this.initDeploymentsPolling();
+
+ if (this.shouldRenderMergedPipeline) {
+ this.initPostMergeDeploymentsPolling();
+ }
+ },
+ beforeDestroy() {
+ eventHub.$off('mr.discussion.updated', this.checkStatus);
+ this.pollingInterval.destroy();
+ this.deploymentsInterval.destroy();
+
+ if (this.postMergeDeploymentsInterval) {
+ this.postMergeDeploymentsInterval.destroy();
+ }
},
methods: {
createService(store) {
@@ -127,9 +156,10 @@ export default {
return new MRWidgetService(endpoints);
},
checkStatus(cb) {
- return this.service.checkStatus()
+ return this.service
+ .checkStatus()
.then(res => res.data)
- .then((data) => {
+ .then(data => {
this.handleNotification(data);
this.mr.setData(data);
this.setFaviconHelper();
@@ -138,7 +168,13 @@ export default {
cb.call(null, data);
}
})
- .catch(() => createFlash('Something went wrong. Please try again.'));
+ .catch(() => createFlash(__('Something went wrong. Please try again.')));
+ },
+ setFaviconHelper() {
+ if (this.mr.ciStatusFaviconPath) {
+ return setFaviconOverlay(this.mr.ciStatusFaviconPath);
+ }
+ return Promise.resolve();
},
initPolling() {
this.pollingInterval = new SmartInterval({
@@ -150,8 +186,14 @@ export default {
});
},
initDeploymentsPolling() {
- this.deploymentsInterval = new SmartInterval({
- callback: this.fetchDeployments,
+ this.deploymentsInterval = this.deploymentsPoll(this.fetchPreMergeDeployments);
+ },
+ initPostMergeDeploymentsPolling() {
+ this.postMergeDeploymentsInterval = this.deploymentsPoll(this.fetchPostMergeDeployments);
+ },
+ deploymentsPoll(callback) {
+ return new SmartInterval({
+ callback,
startingInterval: 30000,
maxInterval: 120000,
hiddenInterval: 240000,
@@ -159,27 +201,38 @@ export default {
immediateExecution: true,
});
},
- setFaviconHelper() {
- if (this.mr.ciStatusFaviconPath) {
- return setFaviconOverlay(this.mr.ciStatusFaviconPath);
- }
- return Promise.resolve();
+ fetchDeployments(target) {
+ return this.service.fetchDeployments(target);
},
- fetchDeployments() {
- return this.service.fetchDeployments()
- .then(res => res.data)
- .then((data) => {
+ fetchPreMergeDeployments() {
+ return this.fetchDeployments()
+ .then(({ data }) => {
if (data.length) {
this.mr.deployments = data;
}
})
- .catch(() => {
- createFlash('Something went wrong while fetching the environments for this merge request. Please try again.');
- });
+ .catch(() => this.throwDeploymentsError());
+ },
+ fetchPostMergeDeployments() {
+ return this.fetchDeployments('merge_commit')
+ .then(({ data }) => {
+ if (data.length) {
+ this.mr.postMergeDeployments = data;
+ }
+ })
+ .catch(() => this.throwDeploymentsError());
+ },
+ throwDeploymentsError() {
+ createFlash(
+ __(
+ 'Something went wrong while fetching the environments for this merge request. Please try again.',
+ ),
+ );
},
fetchActionsContent() {
- this.service.fetchMergeActionsContent()
- .then((res) => {
+ this.service
+ .fetchMergeActionsContent()
+ .then(res => {
if (res.data) {
const el = document.createElement('div');
el.innerHTML = res.data;
@@ -187,7 +240,7 @@ export default {
Project.initRefSwitcher();
}
})
- .catch(() => createFlash('Something went wrong. Please try again.'));
+ .catch(() => createFlash(__('Something went wrong. Please try again.')));
},
handleNotification(data) {
if (data.ci_status === this.mr.ciStatus) return;
@@ -206,22 +259,22 @@ export default {
this.pollingInterval.stopTimer();
},
bindEventHubListeners() {
- eventHub.$on('MRWidgetUpdateRequested', (cb) => {
+ eventHub.$on('MRWidgetUpdateRequested', cb => {
this.checkStatus(cb);
});
// `params` should be an Array contains a Boolean, like `[true]`
// Passing parameter as Boolean didn't work.
- eventHub.$on('SetBranchRemoveFlag', (params) => {
+ eventHub.$on('SetBranchRemoveFlag', params => {
[this.mr.isRemovingSourceBranch] = params;
});
- eventHub.$on('FailedToMerge', (mergeError) => {
+ eventHub.$on('FailedToMerge', mergeError => {
this.mr.state = 'failedToMerge';
this.mr.mergeError = mergeError;
});
- eventHub.$on('UpdateWidgetData', (data) => {
+ eventHub.$on('UpdateWidgetData', data => {
this.mr.setData(data);
});
@@ -237,10 +290,6 @@ export default {
this.stopPolling();
});
},
- handleMounted() {
- this.setFaviconHelper();
- this.initDeploymentsPolling();
- },
},
};
</script>
@@ -254,18 +303,24 @@ export default {
:pipeline="mr.pipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
+ :source-branch="mr.sourceBranch"
:source-branch-link="mr.sourceBranchLink"
+ :troubleshooting-docs-path="mr.troubleshootingDocsPath"
/>
<deployment
v-for="deployment in mr.deployments"
- :key="deployment.id"
+ :key="`pre-merge-deploy-${deployment.id}`"
+ class="js-pre-merge-deploy"
:deployment="deployment"
+ :show-metrics="false"
/>
<div class="mr-section-container">
<grouped-test-reports-app
v-if="mr.testResultsPath"
+ class="js-reports-container"
:endpoint="mr.testResultsPath"
/>
+
<div class="mr-widget-section">
<component
:is="componentName"
@@ -297,5 +352,24 @@ export default {
<mr-widget-merge-help />
</div>
</div>
+
+ <template v-if="shouldRenderMergedPipeline">
+ <mr-widget-pipeline
+ class="js-post-merge-pipeline prepend-top-default"
+ :pipeline="mr.mergePipeline"
+ :ci-status="mr.ciStatus"
+ :has-ci="mr.hasCI"
+ :source-branch="mr.targetBranch"
+ :source-branch-link="mr.targetBranch"
+ :troubleshooting-docs-path="mr.troubleshootingDocsPath"
+ />
+ <deployment
+ v-for="postMergeDeployment in mr.postMergeDeployments"
+ :key="`post-merge-deploy-${postMergeDeployment.id}`"
+ :deployment="postMergeDeployment"
+ :show-metrics="true"
+ class="js-post-deployment"
+ />
+ </template>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/services/ee_switch_mr_widget_service.js b/app/assets/javascripts/vue_merge_request_widget/services/ee_switch_mr_widget_service.js
new file mode 100644
index 00000000000..ea2aabb78fe
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/services/ee_switch_mr_widget_service.js
@@ -0,0 +1,3 @@
+import MRWidgetService from './mr_widget_service';
+
+export default MRWidgetService;
diff --git a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js
index fecbfec2214..0bb70bfd658 100644
--- a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js
+++ b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js
@@ -21,8 +21,12 @@ export default class MRWidgetService {
return axios.delete(this.endpoints.sourceBranchPath);
}
- fetchDeployments() {
- return axios.get(this.endpoints.ciEnvironmentsStatusPath);
+ fetchDeployments(targetParam) {
+ return axios.get(this.endpoints.ciEnvironmentsStatusPath, {
+ params: {
+ environment_target: targetParam,
+ },
+ });
}
poll() {
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_get_state_key.js b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_get_state_key.js
new file mode 100644
index 00000000000..ebef30e3eab
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_get_state_key.js
@@ -0,0 +1,3 @@
+import getStateKey from './get_state_key';
+
+export default getStateKey;
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_mr_widget_store.js
new file mode 100644
index 00000000000..92a07c53f2d
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_mr_widget_store.js
@@ -0,0 +1,3 @@
+import MergeRequestStore from './mr_widget_store';
+
+export default MergeRequestStore;
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_state_maps.js b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_state_maps.js
new file mode 100644
index 00000000000..50cf9503ea7
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_state_maps.js
@@ -0,0 +1,3 @@
+import stateMaps from './state_maps';
+
+export default stateMaps;
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index 672e5280b5e..5c9a7133a6e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -1,5 +1,5 @@
import Timeago from 'timeago.js';
-import { getStateKey } from '../dependencies';
+import getStateKey from './ee_switch_get_state_key';
import { stateKey } from './state_maps';
import { formatDate } from '../../lib/utils/datetime_utility';
@@ -18,6 +18,7 @@ export default class MergeRequestStore {
this.squash = data.squash;
this.squashBeforeMergeHelpPath =
this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path;
+ this.troubleshootingDocsPath = this.troubleshootingDocsPath || data.troubleshooting_docs_path;
this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true;
this.iid = data.iid;
@@ -32,7 +33,9 @@ export default class MergeRequestStore {
this.commitsCount = data.commits_count;
this.divergedCommitsCount = data.diverged_commits_count;
this.pipeline = data.pipeline || {};
+ this.mergePipeline = data.merge_pipeline || {};
this.deployments = this.deployments || data.deployments || [];
+ this.postMergeDeployments = this.postMergeDeployments || [];
this.initRebase(data);
if (data.issues_links) {
diff --git a/app/assets/javascripts/vue_shared/components/bar_chart.vue b/app/assets/javascripts/vue_shared/components/bar_chart.vue
index 3ced4eb691a..690dd794ba4 100644
--- a/app/assets/javascripts/vue_shared/components/bar_chart.vue
+++ b/app/assets/javascripts/vue_shared/components/bar_chart.vue
@@ -118,7 +118,9 @@ export default {
this.rectYAxisLabelDims.height != null ? this.rectYAxisLabelDims.height / 2 : 0;
const yCoord = this.vbHeight / 2 + rectWidth - 5;
- return `translate(${this.minX + this.yAxisTextTransformPadding}, ${yCoord}) rotate(-${this.yAxisTextRotation})`;
+ return `translate(${this.minX + this.yAxisTextTransformPadding}, ${yCoord}) rotate(-${
+ this.yAxisTextRotation
+ })`;
},
},
mounted() {
@@ -207,8 +209,7 @@ export default {
renderedYAxis.selectAll('.tick').each(function createTickLines(d, i) {
if (i > 0) {
- d3
- .select(this)
+ d3.select(this)
.select('line')
.attr('x2', width)
.attr('class', 'axis-tick');
@@ -217,8 +218,7 @@ export default {
// Add the panning capabilities
if (this.isPanAvailable) {
- d3
- .select(this.$refs.baseSvg)
+ d3.select(this.$refs.baseSvg)
.call(this.zoom)
.on('wheel.zoom', null); // This disables the pan of the graph with the scroll of the mouse wheel
}
@@ -291,8 +291,8 @@ export default {
<template
v-for="(data, index) in graphData">
<rect
- v-tooltip
:key="index"
+ v-tooltip
:width="xScale.bandwidth()"
:x="xScale(data.name)"
:y="yScale(data.value)"
diff --git a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue
new file mode 100644
index 00000000000..c5fbaf87b00
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue
@@ -0,0 +1,110 @@
+<script>
+import { GlTooltipDirective } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+import { pluralize } from '~/lib/utils/text_utility';
+import { __, sprintf } from '~/locale';
+import { getCommitIconMap } from '~/ide/utils';
+
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ file: {
+ type: Object,
+ required: true,
+ },
+ showTooltip: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ showStagedIcon: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ forceModifiedIcon: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ size: {
+ type: Number,
+ required: false,
+ default: 12,
+ },
+ },
+ computed: {
+ changedIcon() {
+ const suffix = !this.file.changed && this.file.staged && !this.showStagedIcon ? '-solid' : '';
+
+ if (this.forceModifiedIcon) return `file-modified${suffix}`;
+
+ return `${getCommitIconMap(this.file).icon}${suffix}`;
+ },
+ changedIconClass() {
+ return `${this.changedIcon} float-left d-block`;
+ },
+ tooltipTitle() {
+ if (!this.showTooltip) return undefined;
+
+ const type = this.file.tempFile ? 'addition' : 'modification';
+
+ if (this.file.changed && !this.file.staged) {
+ return sprintf(__('Unstaged %{type}'), {
+ type,
+ });
+ } else if (!this.file.changed && this.file.staged) {
+ return sprintf(__('Staged %{type}'), {
+ type,
+ });
+ } else if (this.file.changed && this.file.staged) {
+ return sprintf(__('Unstaged and staged %{type}'), {
+ type: pluralize(type),
+ });
+ }
+
+ return undefined;
+ },
+ showIcon() {
+ return this.file.changed || this.file.tempFile || this.file.staged || this.file.deleted;
+ },
+ },
+};
+</script>
+
+<template>
+ <span
+ v-gl-tooltip.right
+ :title="tooltipTitle"
+ class="file-changed-icon ml-auto"
+ >
+ <icon
+ v-if="showIcon"
+ :name="changedIcon"
+ :size="size"
+ :css-classes="changedIconClass"
+ />
+ </span>
+</template>
+
+<style>
+.file-addition,
+.file-addition-solid {
+ color: #1aaa55;
+}
+
+.file-modified,
+.file-modified-solid {
+ color: #fc9403;
+}
+
+.file-deletion,
+.file-deletion-solid {
+ color: #db3b21;
+}
+</style>
diff --git a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
index a2518e2a611..b0962684430 100644
--- a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
@@ -1,6 +1,6 @@
<script>
+import { GlTooltipDirective } from '@gitlab/ui';
import CiIcon from './ci_icon.vue';
-import tooltip from '../directives/tooltip';
/**
* Renders CI Badge link with CI icon and status text based on
* API response shared between all places where it is used.
@@ -27,7 +27,7 @@ export default {
CiIcon,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
status: {
@@ -43,14 +43,14 @@ export default {
computed: {
cssClass() {
const className = this.status.group;
- return className ? `ci-status ci-${className}` : 'ci-status';
+ return className ? `ci-status ci-${className} qa-status-badge` : 'ci-status qa-status-badge';
},
},
};
</script>
<template>
<a
- v-tooltip
+ v-gl-tooltip
:href="status.details_path"
:class="cssClass"
:title="!showText ? status.text : ''"
diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
index 945a33d9622..671b4909839 100644
--- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue
+++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
@@ -12,20 +12,18 @@
* css-class="btn-transparent"
* />
*/
-import tooltip from '../directives/tooltip';
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import Icon from '../components/icon.vue';
export default {
name: 'ClipboardButton',
-
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
-
components: {
+ GlButton,
Icon,
},
-
props: {
text: {
type: String,
@@ -68,16 +66,12 @@ export default {
</script>
<template>
- <button
- v-tooltip
+ <gl-button
+ v-gl-tooltip="{ placement: tooltipPlacement, container: tooltipContainer }"
:class="cssClass"
:title="title"
:data-clipboard-text="clipboardText"
- :data-container="tooltipContainer"
- :data-placement="tooltipPlacement"
- type="button"
- class="btn"
>
<icon name="duplicate" />
- </button>
+ </gl-button>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
index 13bca99dcb3..420bd25b496 100644
--- a/app/assets/javascripts/vue_shared/components/commit.vue
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -1,11 +1,11 @@
<script>
+import { GlTooltipDirective } from '@gitlab/ui';
import UserAvatarLink from './user_avatar/user_avatar_link.vue';
-import tooltip from '../directives/tooltip';
import Icon from '../../vue_shared/components/icon.vue';
export default {
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
components: {
UserAvatarLink,
@@ -13,7 +13,7 @@ export default {
},
props: {
/**
- * Indicates the existance of a tag.
+ * Indicates the existence of a tag.
* Used to render the correct icon, if true will render `fa-tag` icon,
* if false will render a svg sprite fork icon
*/
@@ -124,11 +124,10 @@ export default {
</div>
<a
- v-tooltip
+ v-gl-tooltip
:href="commitRef.ref_url"
:title="commitRef.name"
class="ref-name"
- data-container="body"
>
{{ commitRef.name }}
</a>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
index f1ef50d0e3d..97bdd9915c5 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
@@ -1,9 +1,11 @@
<script>
+import { GlLink } from '@gitlab/ui';
import Icon from '../../icon.vue';
import { numberToHumanSize } from '../../../../lib/utils/number_utils';
export default {
components: {
+ GlLink,
Icon,
},
props: {
@@ -37,7 +39,7 @@ export default {
({{ fileSizeReadable }})
</template>
</p>
- <a
+ <gl-link
:href="path"
class="btn btn-default"
rel="nofollow"
@@ -49,7 +51,7 @@ export default {
css-classes="float-left append-right-8"
/>
{{ __('Download') }}
- </a>
+ </gl-link>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
index 8163947cd0c..05f9f960934 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
@@ -17,19 +17,30 @@ export default {
type: Boolean,
default: true,
},
+ innerCssClasses: {
+ type: [Array, Object, String],
+ required: false,
+ default: '',
+ },
},
data() {
return {
width: 0,
height: 0,
- isZoomable: false,
- isZoomed: false,
+ isLoaded: false,
};
},
computed: {
fileSizeReadable() {
return numberToHumanSize(this.fileSize);
},
+
+ hasFileSize() {
+ return this.fileSize > 0;
+ },
+ hasDimensions() {
+ return this.width && this.height;
+ },
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeThrottled, false);
@@ -48,51 +59,51 @@ export default {
const { contentImg } = this.$refs;
if (contentImg) {
- this.isZoomable =
- contentImg.naturalWidth > contentImg.width ||
- contentImg.naturalHeight > contentImg.height;
-
this.width = contentImg.naturalWidth;
this.height = contentImg.naturalHeight;
- this.$emit('imgLoaded', {
- width: this.width,
- height: this.height,
- renderedWidth: contentImg.clientWidth,
- renderedHeight: contentImg.clientHeight,
+ this.$nextTick(() => {
+ this.isLoaded = true;
+
+ this.$emit('imgLoaded', {
+ width: this.width,
+ height: this.height,
+ renderedWidth: contentImg.clientWidth,
+ renderedHeight: contentImg.clientHeight,
+ });
});
}
},
- onImgClick() {
- if (this.isZoomable) this.isZoomed = !this.isZoomed;
- },
},
};
</script>
<template>
- <div class="file-container">
- <div class="file-content image_file">
+ <div>
+ <div
+ :class="innerCssClasses"
+ class="position-relative"
+ >
<img
ref="contentImg"
- :class="{ 'is-zoomable': isZoomable, 'is-zoomed': isZoomed }"
:src="path"
- :alt="path"
@load="onImgLoad"
- @click="onImgClick"/>
- <p
- v-if="renderInfo"
- class="file-info prepend-top-10">
- <template v-if="fileSize>0">
- {{ fileSizeReadable }}
- </template>
- <template v-if="fileSize>0 && width && height">
- |
- </template>
- <template v-if="width && height">
- W: {{ width }} | H: {{ height }}
- </template>
- </p>
+ />
+ <slot name="image-overlay"></slot>
</div>
+ <p
+ v-if="renderInfo"
+ class="image-info"
+ >
+ <template v-if="hasFileSize">
+ {{ fileSizeReadable }}
+ </template>
+ <template v-if="hasFileSize && hasDimensions">
+ |
+ </template>
+ <template v-if="hasDimensions">
+ <strong>W</strong>: {{ width }} | <strong>H</strong>: {{ height }}
+ </template>
+ </p>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index a10deb93f0f..a084cfdf612 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -2,14 +2,14 @@
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import $ from 'jquery';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import { GlSkeletonLoading } from '@gitlab/ui';
const { CancelToken } = axios;
let axiosSource;
export default {
components: {
- SkeletonLoadingContainer,
+ GlSkeletonLoading,
},
props: {
content: {
@@ -81,7 +81,7 @@ export default {
<div
ref="markdown-preview"
class="md md-previewer">
- <skeleton-loading-container v-if="isLoading" />
+ <gl-skeleton-loading v-if="isLoading" />
<div
v-else
v-html="previewContent">
diff --git a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
index 9c1e5c68649..4d63296e332 100644
--- a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
@@ -1,85 +1,85 @@
<script>
- /* eslint-disable vue/require-default-prop */
- export default {
- name: 'DeprecatedModal', // use GlModal instead
+/* eslint-disable vue/require-default-prop */
+export default {
+ name: 'DeprecatedModal', // use GlModal instead
- props: {
- id: {
- type: String,
- required: false,
- },
- title: {
- type: String,
- required: false,
- },
- text: {
- type: String,
- required: false,
- },
- hideFooter: {
- type: Boolean,
- required: false,
- default: false,
- },
- kind: {
- type: String,
- required: false,
- default: 'primary',
- },
- modalDialogClass: {
- type: String,
- required: false,
- default: '',
- },
- closeKind: {
- type: String,
- required: false,
- default: 'default',
- },
- closeButtonLabel: {
- type: String,
- required: false,
- default: 'Cancel',
- },
- primaryButtonLabel: {
- type: String,
- required: false,
- default: '',
- },
- secondaryButtonLabel: {
- type: String,
- required: false,
- default: '',
- },
- submitDisabled: {
- type: Boolean,
- required: false,
- default: false,
- },
+ props: {
+ id: {
+ type: String,
+ required: false,
},
+ title: {
+ type: String,
+ required: false,
+ },
+ text: {
+ type: String,
+ required: false,
+ },
+ hideFooter: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ kind: {
+ type: String,
+ required: false,
+ default: 'primary',
+ },
+ modalDialogClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ closeKind: {
+ type: String,
+ required: false,
+ default: 'default',
+ },
+ closeButtonLabel: {
+ type: String,
+ required: false,
+ default: 'Cancel',
+ },
+ primaryButtonLabel: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ secondaryButtonLabel: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ submitDisabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
- computed: {
- btnKindClass() {
- return {
- [`btn-${this.kind}`]: true,
- };
- },
- btnCancelKindClass() {
- return {
- [`btn-${this.closeKind}`]: true,
- };
- },
+ computed: {
+ btnKindClass() {
+ return {
+ [`btn-${this.kind}`]: true,
+ };
+ },
+ btnCancelKindClass() {
+ return {
+ [`btn-${this.closeKind}`]: true,
+ };
},
+ },
- methods: {
- emitCancel(event) {
- this.$emit('cancel', event);
- },
- emitSubmit(event) {
- this.$emit('submit', event);
- },
+ methods: {
+ emitCancel(event) {
+ this.$emit('cancel', event);
+ },
+ emitSubmit(event) {
+ this.$emit('submit', event);
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
index d3cbe3c7e74..9c3f3e7f7a9 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
@@ -46,7 +46,7 @@ export default {
}
},
basePath() {
- // We might get the project path from rails with the relative url already setup
+ // We might get the project path from rails with the relative url already set up
return this.projectPath.indexOf('/') === 0 ? '' : `${gon.relative_url_root}/`;
},
fullOldPath() {
@@ -69,6 +69,13 @@ export default {
:new-path="fullNewPath"
:old-path="fullOldPath"
:project-path="projectPath"
- />
+ >
+ <slot
+ slot="image-overlay"
+ name="image-overlay"
+ >
+ </slot>
+ </component>
+ <slot></slot>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue
index 38e881d17a2..cd0c1e850af 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue
@@ -15,11 +15,6 @@ export default {
type: String,
required: true,
},
- projectPath: {
- type: String,
- required: false,
- default: '',
- },
},
data() {
return {
@@ -120,7 +115,6 @@ export default {
key="onionOldImg"
:render-info="false"
:path="oldPath"
- :project-path="projectPath"
@imgLoaded="onionOldImgLoaded"
/>
</div>
@@ -136,9 +130,14 @@ export default {
key="onionNewImg"
:render-info="false"
:path="newPath"
- :project-path="projectPath"
@imgLoaded="onionNewImgLoaded"
- />
+ >
+ <slot
+ slot="image-overlay"
+ name="image-overlay"
+ >
+ </slot>
+ </image-viewer>
</div>
<div class="controls">
<div class="transparent"></div>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
index 86366c799a2..57f373fca21 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
@@ -16,11 +16,6 @@ export default {
type: String,
required: true,
},
- projectPath: {
- type: String,
- required: false,
- default: '',
- },
},
data() {
return {
@@ -30,7 +25,7 @@ export default {
swipeMaxWidth: undefined,
swipeMaxHeight: undefined,
swipeBarPos: 1,
- swipeWrapWidth: undefined,
+ swipeWrapWidth: 0,
};
},
computed: {
@@ -68,7 +63,7 @@ export default {
leftValue = clientWidth - spaceLeft;
}
- this.swipeWrapWidth = this.swipeMaxWidth - leftValue;
+ this.swipeWrapWidth = (leftValue / clientWidth) * 100;
this.swipeBarPos = leftValue;
},
startDrag() {
@@ -86,7 +81,6 @@ export default {
// Add 2 for border width
this.swipeMaxWidth =
Math.max(this.swipeOldImgInfo.renderedWidth, this.swipeNewImgInfo.renderedWidth) + 2;
- this.swipeWrapWidth = this.swipeMaxWidth;
this.swipeMaxHeight =
Math.max(this.swipeOldImgInfo.renderedHeight, this.swipeNewImgInfo.renderedHeight) + 2;
@@ -112,37 +106,38 @@ export default {
<div class="swipe view">
<div
ref="swipeFrame"
- :style="{
- 'width': swipeMaxPixelWidth,
- 'height': swipeMaxPixelHeight,
- }"
class="swipe-frame">
- <div class="frame deleted">
- <image-viewer
- key="swipeOldImg"
- ref="swipeOldImg"
- :render-info="false"
- :path="oldPath"
- :project-path="projectPath"
- @imgLoaded="swipeOldImgLoaded"
- />
- </div>
+ <image-viewer
+ key="swipeOldImg"
+ ref="swipeOldImg"
+ :render-info="false"
+ :path="oldPath"
+ class="frame deleted"
+ @imgLoaded="swipeOldImgLoaded"
+ />
<div
ref="swipeWrap"
:style="{
- 'width': swipeWrapPixelWidth,
- 'height': swipeMaxPixelHeight,
+ width: `${swipeWrapWidth}%`
}"
- class="swipe-wrap">
- <div class="frame added">
- <image-viewer
- key="swipeNewImg"
- :render-info="false"
- :path="newPath"
- :project-path="projectPath"
- @imgLoaded="swipeNewImgLoaded"
- />
- </div>
+ class="swipe-wrap"
+ >
+ <image-viewer
+ key="swipeNewImg"
+ :render-info="false"
+ :path="newPath"
+ :style="{
+ width: swipeMaxPixelWidth
+ }"
+ class="frame added"
+ @imgLoaded="swipeNewImgLoaded"
+ >
+ <slot
+ slot="image-overlay"
+ name="image-overlay"
+ >
+ </slot>
+ </image-viewer>
</div>
<span
ref="swipeBar"
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue
index 9c19266ecdf..aafa88f4ff1 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue
@@ -14,28 +14,29 @@ export default {
type: String,
required: true,
},
- projectPath: {
- type: String,
- required: false,
- default: '',
- },
},
};
</script>
<template>
- <div class="two-up view row">
- <div class="col-sm-6 frame deleted">
- <image-viewer
- :path="oldPath"
- :project-path="projectPath"
- />
- </div>
- <div class="col-sm-6 frame added">
- <image-viewer
- :path="newPath"
- :project-path="projectPath"
- />
- </div>
+ <div class="two-up view d-flex">
+ <image-viewer
+ :path="oldPath"
+ :render-info="true"
+ inner-css-classes="frame deleted"
+ class="wrap w-50"
+ />
+ <image-viewer
+ :path="newPath"
+ :render-info="true"
+ :inner-css-classes="['frame', 'added']"
+ class="wrap w-50"
+ >
+ <slot
+ slot="image-overlay"
+ name="image-overlay"
+ >
+ </slot>
+ </image-viewer>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue
index 1af85283277..d7f24c1afc5 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue
@@ -8,9 +8,6 @@ import { diffModes, imageViewMode } from '../constants';
export default {
components: {
ImageViewer,
- TwoUpViewer,
- SwipeViewer,
- OnionSkinViewer,
},
props: {
diffMode: {
@@ -25,17 +22,35 @@ export default {
type: String,
required: true,
},
- projectPath: {
- type: String,
- required: false,
- default: '',
- },
},
data() {
return {
mode: imageViewMode.twoup,
};
},
+ computed: {
+ imageViewComponent() {
+ switch (this.mode) {
+ case imageViewMode.twoup:
+ return TwoUpViewer;
+ case imageViewMode.swipe:
+ return SwipeViewer;
+ case imageViewMode.onion:
+ return OnionSkinViewer;
+ default:
+ return undefined;
+ }
+ },
+ isNew() {
+ return this.diffMode === diffModes.new;
+ },
+ isRenamed() {
+ return this.diffMode === diffModes.renamed;
+ },
+ imagePath() {
+ return this.isNew || this.isRenamed ? this.newPath : this.oldPath;
+ },
+ },
methods: {
changeMode(newMode) {
this.mode = newMode;
@@ -52,15 +67,16 @@ export default {
v-if="diffMode === $options.diffModes.replaced"
class="diff-viewer">
<div class="image js-replaced-image">
- <two-up-viewer
- v-if="mode === $options.imageViewMode.twoup"
- v-bind="$props"/>
- <swipe-viewer
- v-else-if="mode === $options.imageViewMode.swipe"
- v-bind="$props"/>
- <onion-skin-viewer
- v-else-if="mode === $options.imageViewMode.onion"
- v-bind="$props"/>
+ <component
+ :is="imageViewComponent"
+ v-bind="$props"
+ >
+ <slot
+ slot="image-overlay"
+ name="image-overlay"
+ >
+ </slot>
+ </component>
</div>
<div class="view-modes">
<ul class="view-modes-menu">
@@ -87,23 +103,27 @@ export default {
</li>
</ul>
</div>
- <div class="note-container"></div>
- </div>
- <div
- v-else-if="diffMode === $options.diffModes.new"
- class="diff-viewer added">
- <image-viewer
- :path="newPath"
- :project-path="projectPath"
- />
</div>
<div
v-else
- class="diff-viewer deleted">
- <image-viewer
- :path="oldPath"
- :project-path="projectPath"
- />
+ class="diff-viewer"
+ >
+ <div class="image">
+ <image-viewer
+ :path="imagePath"
+ :inner-css-classes="['frame', {
+ 'added': isNew,
+ 'deleted': diffMode === $options.diffModes.deleted
+ }]"
+ >
+ <slot
+ v-if="isNew || isRenamed"
+ slot="image-overlay"
+ name="image-overlay"
+ >
+ </slot>
+ </image-viewer>
+ </div>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
index af5ebcdc40a..0f4effda79f 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
@@ -1,10 +1,10 @@
<script>
import { __ } from '~/locale';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
- LoadingIcon,
+ GlLoadingIcon,
},
props: {
isDisabled: {
@@ -34,7 +34,7 @@ export default {
data-toggle="dropdown"
aria-expanded="false"
>
- <loading-icon
+ <gl-loading-icon
v-show="isLoading"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue
index 878c805ada5..545be568ad3 100644
--- a/app/assets/javascripts/vue_shared/components/file_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/file_icon.vue
@@ -1,6 +1,6 @@
<script>
+import { GlLoadingIcon } from '@gitlab/ui';
import getIconForFile from './file_icon/file_icon_map';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
/* This is a re-usable vue component for rendering a svg sprite
@@ -17,8 +17,8 @@ import icon from '../../vue_shared/components/icon.vue';
*/
export default {
components: {
- loadingIcon,
icon,
+ GlLoadingIcon,
},
props: {
fileName: {
@@ -84,7 +84,7 @@ export default {
:size="size"
css-classes="folder-icon"
/>
- <loading-icon
+ <gl-loading-icon
v-if="loading"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
index 9bca1993ccc..bffaa096210 100644
--- a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
+++ b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
@@ -549,6 +549,7 @@ const fileNameIcons = {
jenkinsfile: 'jenkins',
'firebase.json': 'firebase',
'.firebaserc': 'firebase',
+ Rakefile: 'ruby',
'rollup.config.js': 'rollup',
'rollup.config.ts': 'rollup',
'rollup-config.js': 'rollup',
@@ -583,7 +584,5 @@ const fileNameIcons = {
};
export default function getIconForFile(name) {
- return fileNameIcons[name] ||
- fileExtensionIcons[name ? name.split('.').pop() : ''] ||
- '';
+ return fileNameIcons[name] || fileExtensionIcons[name ? name.split('.').pop() : ''] || '';
}
diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue
new file mode 100644
index 00000000000..2d89a156117
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/file_row.vue
@@ -0,0 +1,269 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue';
+
+export default {
+ name: 'FileRow',
+ components: {
+ FileIcon,
+ Icon,
+ ChangedFileIcon,
+ },
+ props: {
+ file: {
+ type: Object,
+ required: true,
+ },
+ level: {
+ type: Number,
+ required: true,
+ },
+ extraComponent: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ hideExtraOnTree: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ showChangedIcon: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ displayTextKey: {
+ type: String,
+ required: false,
+ default: 'name',
+ },
+ shouldTruncateStart: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ mouseOver: false,
+ truncateStart: 0,
+ };
+ },
+ computed: {
+ isTree() {
+ return this.file.type === 'tree';
+ },
+ isBlob() {
+ return this.file.type === 'blob';
+ },
+ levelIndentation() {
+ return {
+ marginLeft: `${this.level * 16}px`,
+ };
+ },
+ fileClass() {
+ return {
+ 'file-open': this.isBlob && this.file.opened,
+ 'is-active': this.isBlob && this.file.active,
+ folder: this.isTree,
+ 'is-open': this.file.opened,
+ };
+ },
+ outputText() {
+ const text = this.file[this.displayTextKey];
+
+ if (this.truncateStart === 0) {
+ return text;
+ }
+
+ return `...${text.substring(this.truncateStart, text.length)}`;
+ },
+ },
+ watch: {
+ 'file.active': function fileActiveWatch(active) {
+ if (this.file.type === 'blob' && active) {
+ this.scrollIntoView();
+ }
+ },
+ },
+ mounted() {
+ if (this.hasPathAtCurrentRoute()) {
+ this.scrollIntoView(true);
+ }
+
+ if (this.shouldTruncateStart) {
+ const { scrollWidth, offsetWidth } = this.$refs.textOutput;
+ const textOverflow = scrollWidth - offsetWidth;
+
+ if (textOverflow > 0) {
+ this.truncateStart = Math.ceil(textOverflow / 5) + 3;
+ }
+ }
+ },
+ methods: {
+ toggleTreeOpen(path) {
+ this.$emit('toggleTreeOpen', path);
+ },
+ clickedFile(path) {
+ this.$emit('clickFile', path);
+ },
+ clickFile() {
+ // Manual Action if a tree is selected/opened
+ if (this.isTree && this.hasUrlAtCurrentRoute()) {
+ this.toggleTreeOpen(this.file.path);
+ }
+
+ if (this.$router) this.$router.push(`/project${this.file.url}`);
+
+ if (this.isBlob) this.clickedFile(this.file.path);
+ },
+ scrollIntoView(isInit = false) {
+ const block = isInit && this.isTree ? 'center' : 'nearest';
+
+ this.$el.scrollIntoView({
+ behavior: 'smooth',
+ block,
+ });
+ },
+ hasPathAtCurrentRoute() {
+ if (!this.$router || !this.$router.currentRoute) {
+ return false;
+ }
+
+ // - strip route up to "/-/" and ending "/"
+ const routePath = this.$router.currentRoute.path
+ .replace(/^.*?[/]-[/]/g, '')
+ .replace(/[/]$/g, '');
+
+ // - strip ending "/"
+ const filePath = this.file.path.replace(/[/]$/g, '');
+
+ return filePath === routePath;
+ },
+ hasUrlAtCurrentRoute() {
+ if (!this.$router || !this.$router.currentRoute) return true;
+
+ return this.$router.currentRoute.path === `/project${this.file.url}`;
+ },
+ toggleHover(over) {
+ this.mouseOver = over;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div
+ :class="fileClass"
+ class="file-row"
+ role="button"
+ @click="clickFile"
+ @mouseover="toggleHover(true)"
+ @mouseout="toggleHover(false)"
+ >
+ <div
+ class="file-row-name-container"
+ >
+ <span
+ ref="textOutput"
+ :style="levelIndentation"
+ class="file-row-name str-truncated"
+ >
+ <file-icon
+ v-if="!showChangedIcon || file.type === 'tree'"
+ :file-name="file.name"
+ :loading="file.loading"
+ :folder="isTree"
+ :opened="file.opened"
+ :size="16"
+ />
+ <changed-file-icon
+ v-else
+ :file="file"
+ :size="16"
+ class="append-right-5"
+ />
+ {{ outputText }}
+ </span>
+ <component
+ :is="extraComponent"
+ v-if="extraComponent && !(hideExtraOnTree && file.type === 'tree')"
+ :file="file"
+ :mouse-over="mouseOver"
+ />
+ </div>
+ </div>
+ <template v-if="file.opened">
+ <file-row
+ v-for="childFile in file.tree"
+ :key="childFile.key"
+ :file="childFile"
+ :level="level + 1"
+ :hide-extra-on-tree="hideExtraOnTree"
+ :extra-component="extraComponent"
+ :show-changed-icon="showChangedIcon"
+ :display-text-key="displayTextKey"
+ :should-truncate-start="shouldTruncateStart"
+ @toggleTreeOpen="toggleTreeOpen"
+ @clickFile="clickedFile"
+ />
+ </template>
+ </div>
+</template>
+
+<style>
+.file-row {
+ display: flex;
+ align-items: center;
+ height: 32px;
+ padding: 4px 8px;
+ margin-left: -8px;
+ margin-right: -8px;
+ border-radius: 3px;
+ text-align: left;
+ cursor: pointer;
+}
+
+.file-row:hover,
+.file-row:focus {
+ background: #f2f2f2;
+}
+
+.file-row:active {
+ background: #dfdfdf;
+}
+
+.file-row.is-active {
+ background: #f2f2f2;
+}
+
+.file-row-name-container {
+ display: flex;
+ width: 100%;
+ align-items: center;
+ overflow: visible;
+}
+
+.file-row-name {
+ display: inline-block;
+ flex: 1;
+ max-width: inherit;
+ height: 18px;
+ line-height: 16px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-row-name svg {
+ margin-right: 2px;
+ vertical-align: middle;
+}
+
+.file-row-name .loading-container {
+ display: inline-block;
+ margin-right: 4px;
+}
+</style>
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
new file mode 100644
index 00000000000..388a2f4ca36
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
@@ -0,0 +1,145 @@
+<script>
+import $ from 'jquery';
+import Icon from '~/vue_shared/components/icon.vue';
+/**
+ * Renders a split dropdown with
+ * an input that allows to search through the given
+ * array of options.
+ */
+export default {
+ name: 'FilteredSearchDropdown',
+ components: {
+ Icon,
+ },
+ props: {
+ title: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ buttonType: {
+ required: false,
+ validator: value =>
+ ['primary', 'default', 'secondary', 'success', 'info', 'warning', 'danger'].indexOf(
+ value,
+ ) !== -1,
+ default: 'default',
+ },
+ size: {
+ required: false,
+ type: String,
+ default: 'sm',
+ },
+ items: {
+ type: Array,
+ required: true,
+ },
+ visibleItems: {
+ type: Number,
+ required: false,
+ default: 5,
+ },
+ filterKey: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ filter: '',
+ };
+ },
+ computed: {
+ className() {
+ return `btn btn-${this.buttonType} btn-${this.size}`;
+ },
+ filteredResults() {
+ if (this.filter !== '') {
+ return this.items.filter(
+ item =>
+ item[this.filterKey] &&
+ item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()),
+ );
+ }
+
+ return this.items.slice(0, this.visibleItems);
+ },
+ },
+ mounted() {
+ /**
+ * Resets the filter every time the user closes the dropdown
+ */
+ $(this.$el)
+ .on('shown.bs.dropdown', () => {
+ this.$nextTick(() => this.$refs.searchInput.focus());
+ })
+ .on('hidden.bs.dropdown', () => {
+ this.filter = '';
+ });
+ },
+};
+</script>
+<template>
+ <div class="dropdown">
+ <div class="btn-group">
+ <slot
+ name="mainAction"
+ :class-name="className"
+ >
+ <button
+ type="button"
+ :class="className"
+ >
+ {{ title }}
+ </button>
+ </slot>
+
+ <button
+ type="button"
+ :class="className"
+ class="dropdown-toggle dropdown-toggle-split"
+ data-toggle="dropdown"
+ aria-haspopup="true"
+ aria-expanded="false"
+ aria-label="Expand dropdown"
+ >
+ <icon
+ name="angle-down"
+ :size="12"
+ />
+ </button>
+ <div class="dropdown-menu dropdown-menu-right">
+ <div class="dropdown-input">
+ <input
+ ref="searchInput"
+ v-model="filter"
+ type="search"
+ placeholder="Filter"
+ class="js-filtered-dropdown-input dropdown-input-field"
+ />
+ <icon
+ class="dropdown-input-search"
+ name="search"
+ />
+ </div>
+
+ <div class="dropdown-content">
+ <ul>
+ <li
+ v-for="(result, i) in filteredResults"
+ :key="i"
+ class="js-filtered-dropdown-result"
+ >
+ <slot
+ name="result"
+ :result="result"
+ >
+ {{ result[filterKey] }}
+ </slot>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/gl_countdown.vue b/app/assets/javascripts/vue_shared/components/gl_countdown.vue
new file mode 100644
index 00000000000..97f7998f461
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/gl_countdown.vue
@@ -0,0 +1,53 @@
+<script>
+import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility';
+import { GlTooltipDirective } from '@gitlab/ui';
+
+/**
+ * Counts down to a given end date.
+ */
+export default {
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ endDateString: {
+ type: String,
+ required: true,
+ validator(value) {
+ return !Number.isNaN(new Date(value).getTime());
+ },
+ },
+ },
+
+ data() {
+ return {
+ remainingTime: formatTime(0),
+ countdownUpdateIntervalId: null,
+ };
+ },
+
+ mounted() {
+ const updateRemainingTime = () => {
+ const remainingMilliseconds = calculateRemainingMilliseconds(this.endDateString);
+ this.remainingTime = formatTime(remainingMilliseconds);
+ };
+
+ updateRemainingTime();
+ this.countdownUpdateIntervalId = window.setInterval(updateRemainingTime, 1000);
+ },
+
+ beforeDestroy() {
+ window.clearInterval(this.countdownUpdateIntervalId);
+ },
+};
+</script>
+
+<template>
+ <time
+ v-gl-tooltip
+ :datetime="endDateString"
+ :title="endDateString"
+ >
+ {{ remainingTime }}
+ </time>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
index b023c5cfeb1..b5444d43ded 100644
--- a/app/assets/javascripts/vue_shared/components/gl_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -41,10 +41,14 @@ export default {
},
},
mounted() {
- $(this.$el).on('shown.bs.modal', this.opened).on('hidden.bs.modal', this.closed);
+ $(this.$el)
+ .on('shown.bs.modal', this.opened)
+ .on('hidden.bs.modal', this.closed);
},
beforeDestroy() {
- $(this.$el).off('shown.bs.modal', this.opened).off('hidden.bs.modal', this.closed);
+ $(this.$el)
+ .off('shown.bs.modal', this.opened)
+ .off('hidden.bs.modal', this.closed);
},
methods: {
emitCancel(event) {
@@ -103,7 +107,7 @@ export default {
<slot name="footer">
<button
type="button"
- class="btn js-modal-cancel-action"
+ class="btn js-modal-cancel-action qa-modal-cancel-button"
data-dismiss="modal"
@click="emitCancel($event)"
>
@@ -112,7 +116,7 @@ export default {
<button
:class="`btn-${footerPrimaryButtonVariant}`"
type="button"
- class="btn js-modal-primary-action"
+ class="btn js-modal-primary-action qa-modal-primary-button"
data-dismiss="modal"
@click="emitSubmit($event)"
>
diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
index 49fbce75110..1a91a8b81e3 100644
--- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue
+++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
@@ -1,9 +1,9 @@
<script>
+import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui';
import CiIconBadge from './ci_badge_link.vue';
-import LoadingIcon from './loading_icon.vue';
import TimeagoTooltip from './time_ago_tooltip.vue';
-import tooltip from '../directives/tooltip';
import UserAvatarImage from './user_avatar/user_avatar_image.vue';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
/**
* Renders header component for job and pipeline page based on UI mockups
@@ -15,12 +15,14 @@ import UserAvatarImage from './user_avatar/user_avatar_image.vue';
export default {
components: {
CiIconBadge,
- LoadingIcon,
TimeagoTooltip,
UserAvatarImage,
+ GlLink,
+ GlButton,
+ LoadingButton,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
status: {
@@ -71,6 +73,9 @@ export default {
onClickAction(action) {
this.$emit('actionClicked', action);
},
+ onClickSidebarButton() {
+ this.$emit('clickedSidebarButton');
+ },
},
};
</script>
@@ -97,8 +102,8 @@ export default {
by
<template v-if="user">
- <a
- v-tooltip
+ <gl-link
+ v-gl-tooltip
:href="user.path"
:title="user.email"
class="js-user-link commit-committer-link"
@@ -112,7 +117,7 @@ export default {
/>
{{ user.name }}
- </a>
+ </gl-link>
<span
v-if="user.status_tooltip_html"
v-html="user.status_tooltip_html"></span>
@@ -126,58 +131,51 @@ export default {
<template
v-for="(action, i) in actions"
>
- <a
+ <gl-link
v-if="action.type === 'link'"
+ :key="i"
:href="action.path"
:class="action.cssClass"
- :key="i"
>
{{ action.label }}
- </a>
+ </gl-link>
- <a
+ <gl-link
v-else-if="action.type === 'ujs-link'"
+ :key="i"
:href="action.path"
:class="action.cssClass"
- :key="i"
data-method="post"
rel="nofollow"
>
{{ action.label }}
- </a>
+ </gl-link>
- <button
+ <loading-button
v-else-if="action.type === 'button'"
+ :key="i"
+ :loading="action.isLoading"
:disabled="action.isLoading"
:class="action.cssClass"
- :key="i"
- type="button"
+ container-class="d-inline"
+ :label="action.label"
@click="onClickAction(action)"
- >
- {{ action.label }}
- <i
- v-show="action.isLoading"
- class="fa fa-spin fa-spinner"
- aria-hidden="true"
- >
- </i>
- </button>
+ />
</template>
- <button
- v-if="hasSidebarButton"
- id="toggleSidebar"
- type="button"
- class="btn btn-default d-block d-sm-none d-md-none
+ </section>
+ <gl-button
+ v-if="hasSidebarButton"
+ id="toggleSidebar"
+ class="d-block d-sm-none
sidebar-toggle-btn js-sidebar-build-toggle js-sidebar-build-toggle-header"
- aria-label="Toggle Sidebar"
+ @click="onClickSidebarButton"
+ >
+ <i
+ class="fa fa-angle-double-left"
+ aria-hidden="true"
+ aria-labelledby="toggleSidebar"
>
- <i
- class="fa fa-angle-double-left"
- aria-hidden="true"
- aria-labelledby="toggleSidebar"
- >
- </i>
- </button>
- </section>
+ </i>
+ </gl-button>
</header>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/icon.vue b/app/assets/javascripts/vue_shared/components/icon.vue
index 5e0e7315e99..a25841fc02f 100644
--- a/app/assets/javascripts/vue_shared/components/icon.vue
+++ b/app/assets/javascripts/vue_shared/components/icon.vue
@@ -1,7 +1,6 @@
<script>
-
// only allow classes in images.scss e.g. s12
-const validSizes = [8, 10, 12, 16, 18, 24, 32, 48, 72];
+const validSizes = [8, 10, 12, 14, 16, 18, 24, 32, 48, 72];
let iconValidator = () => true;
/*
@@ -9,7 +8,7 @@ let iconValidator = () => true;
*/
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line global-require
- const data = require('@gitlab-org/gitlab-svgs/dist/icons.json');
+ const data = require('@gitlab/svgs/dist/icons.json');
const { icons } = data;
iconValidator = value => {
if (icons.includes(value)) {
@@ -105,6 +104,7 @@ export default {
:x="x"
:y="y"
:tabindex="tabIndex"
+ aria-hidden="true"
>
<use v-bind="{ 'xlink:href':spriteHref }"/>
</svg>
diff --git a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
index ca8ce554588..dc88749c18f 100644
--- a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
@@ -1,34 +1,34 @@
<script>
- import icon from '../../../vue_shared/components/icon.vue';
+import icon from '../../../vue_shared/components/icon.vue';
- export default {
- components: {
- icon,
+export default {
+ components: {
+ icon,
+ },
+ props: {
+ isLocked: {
+ type: Boolean,
+ default: false,
+ required: false,
},
- props: {
- isLocked: {
- type: Boolean,
- default: false,
- required: false,
- },
- isConfidential: {
- type: Boolean,
- default: false,
- required: false,
- },
+ isConfidential: {
+ type: Boolean,
+ default: false,
+ required: false,
},
- computed: {
- warningIcon() {
- if (this.isConfidential) return 'eye-slash';
- if (this.isLocked) return 'lock';
+ },
+ computed: {
+ warningIcon() {
+ if (this.isConfidential) return 'eye-slash';
+ if (this.isLocked) return 'lock';
- return '';
- },
- isLockedAndConfidential() {
- return this.isConfidential && this.isLocked;
- },
+ return '';
},
- };
+ isLockedAndConfidential() {
+ return this.isConfidential && this.isLocked;
+ },
+ },
+};
</script>
<template>
<div class="issuable-note-warning">
@@ -37,7 +37,6 @@
:name="warningIcon"
:size="16"
class="icon inline"
- aria-hidden="true"
/>
<span v-if="isLockedAndConfidential">
diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue
index 2ff0c056b9c..9bae8a32a8c 100644
--- a/app/assets/javascripts/vue_shared/components/loading_button.vue
+++ b/app/assets/javascripts/vue_shared/components/loading_button.vue
@@ -1,6 +1,7 @@
<script>
- /* eslint-disable vue/require-default-prop */
- /* This is a re-usable vue component for rendering a button
+import { GlLoadingIcon } from '@gitlab/ui';
+/* eslint-disable vue/require-default-prop */
+/* This is a re-usable vue component for rendering a button
that will probably be sending off ajax requests and need
to show the loading status by setting the `loading` option.
This can also be used for initial page load when you don't
@@ -17,39 +18,37 @@
*/
- import loadingIcon from './loading_icon.vue';
-
- export default {
- components: {
- loadingIcon,
+export default {
+ components: {
+ GlLoadingIcon,
+ },
+ props: {
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ label: {
+ type: String,
+ required: false,
},
- props: {
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- disabled: {
- type: Boolean,
- required: false,
- default: false,
- },
- label: {
- type: String,
- required: false,
- },
- containerClass: {
- type: [String, Array, Object],
- required: false,
- default: 'btn btn-align-content',
- },
+ containerClass: {
+ type: [String, Array, Object],
+ required: false,
+ default: 'btn btn-align-content',
},
- methods: {
- onClick(e) {
- this.$emit('click', e);
- },
+ },
+ methods: {
+ onClick(e) {
+ this.$emit('click', e);
},
- };
+ },
+};
</script>
<template>
@@ -60,7 +59,7 @@
@click="onClick"
>
<transition name="fade">
- <loading-icon
+ <gl-loading-icon
v-if="loading"
:inline="true"
:class="{
diff --git a/app/assets/javascripts/vue_shared/components/loading_icon.vue b/app/assets/javascripts/vue_shared/components/loading_icon.vue
deleted file mode 100644
index db22c5f02cd..00000000000
--- a/app/assets/javascripts/vue_shared/components/loading_icon.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-<script>
- export default {
- props: {
- label: {
- type: String,
- required: false,
- default: 'Loading',
- },
-
- size: {
- type: String,
- required: false,
- default: '1',
- },
-
- inline: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- computed: {
- rootElementType() {
- return this.inline ? 'span' : 'div';
- },
- cssClass() {
- return `fa-${this.size}x`;
- },
- },
- };
-</script>
-<template>
- <component
- :is="rootElementType"
- class="loading-container text-center">
- <i
- :class="cssClass"
- :aria-label="label"
- class="fa fa-spin fa-spinner"
- aria-hidden="true"
- >
- </i>
- </component>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index d62537021ca..4687de62614 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -1,140 +1,141 @@
<script>
- import $ from 'jquery';
- import { s__ } from '~/locale';
- import Flash from '../../../flash';
- import GLForm from '../../../gl_form';
- import markdownHeader from './header.vue';
- import markdownToolbar from './toolbar.vue';
- import icon from '../icon.vue';
+import $ from 'jquery';
+import { s__ } from '~/locale';
+import Flash from '../../../flash';
+import GLForm from '../../../gl_form';
+import markdownHeader from './header.vue';
+import markdownToolbar from './toolbar.vue';
+import icon from '../icon.vue';
- export default {
- components: {
- markdownHeader,
- markdownToolbar,
- icon,
+export default {
+ components: {
+ markdownHeader,
+ markdownToolbar,
+ icon,
+ },
+ props: {
+ markdownPreviewPath: {
+ type: String,
+ required: false,
+ default: '',
},
- props: {
- markdownPreviewPath: {
- type: String,
- required: false,
- default: '',
- },
- markdownDocsPath: {
- type: String,
- required: true,
- },
- markdownVersion: {
- type: Number,
- required: false,
- default: 0,
- },
- addSpacingClasses: {
- type: Boolean,
- required: false,
- default: true,
- },
- quickActionsDocsPath: {
- type: String,
- required: false,
- default: '',
- },
- canAttachFile: {
- type: Boolean,
- required: false,
- default: true,
- },
- enableAutocomplete: {
- type: Boolean,
- required: false,
- default: true,
- },
+ markdownDocsPath: {
+ type: String,
+ required: true,
},
- data() {
- return {
- markdownPreview: '',
- referencedCommands: '',
- referencedUsers: '',
- markdownPreviewLoading: false,
- previewMarkdown: false,
- };
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
},
- computed: {
- shouldShowReferencedUsers() {
- const referencedUsersThreshold = 10;
- return this.referencedUsers.length >= referencedUsersThreshold;
- },
+ addSpacingClasses: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- mounted() {
- /*
- GLForm class handles all the toolbar buttons
- */
- return new GLForm($(this.$refs['gl-form']), {
- emojis: this.enableAutocomplete,
- members: this.enableAutocomplete,
- issues: this.enableAutocomplete,
- mergeRequests: this.enableAutocomplete,
- epics: this.enableAutocomplete,
- milestones: this.enableAutocomplete,
- labels: this.enableAutocomplete,
- });
+ quickActionsDocsPath: {
+ type: String,
+ required: false,
+ default: '',
},
- beforeDestroy() {
- const glForm = $(this.$refs['gl-form']).data('glForm');
- if (glForm) {
- glForm.destroy();
- }
+ canAttachFile: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ enableAutocomplete: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- methods: {
- showPreviewTab() {
- if (this.previewMarkdown) return;
+ },
+ data() {
+ return {
+ markdownPreview: '',
+ referencedCommands: '',
+ referencedUsers: '',
+ markdownPreviewLoading: false,
+ previewMarkdown: false,
+ };
+ },
+ computed: {
+ shouldShowReferencedUsers() {
+ const referencedUsersThreshold = 10;
+ return this.referencedUsers.length >= referencedUsersThreshold;
+ },
+ },
+ mounted() {
+ /*
+ GLForm class handles all the toolbar buttons
+ */
+ return new GLForm($(this.$refs['gl-form']), {
+ emojis: this.enableAutocomplete,
+ members: this.enableAutocomplete,
+ issues: this.enableAutocomplete,
+ mergeRequests: this.enableAutocomplete,
+ epics: this.enableAutocomplete,
+ milestones: this.enableAutocomplete,
+ labels: this.enableAutocomplete,
+ snippets: this.enableAutocomplete,
+ });
+ },
+ beforeDestroy() {
+ const glForm = $(this.$refs['gl-form']).data('glForm');
+ if (glForm) {
+ glForm.destroy();
+ }
+ },
+ methods: {
+ showPreviewTab() {
+ if (this.previewMarkdown) return;
- this.previewMarkdown = true;
+ this.previewMarkdown = true;
- /*
+ /*
Can't use `$refs` as the component is technically in the parent component
so we access the VNode & then get the element
*/
- const text = this.$slots.textarea[0].elm.value;
+ const text = this.$slots.textarea[0].elm.value;
- if (text) {
- this.markdownPreviewLoading = true;
- this.$http
- .post(this.versionedPreviewPath(), { text })
- .then(resp => resp.json())
- .then(data => this.renderMarkdown(data))
- .catch(() => new Flash(s__('Error loading markdown preview')));
- } else {
- this.renderMarkdown();
- }
- },
+ if (text) {
+ this.markdownPreviewLoading = true;
+ this.$http
+ .post(this.versionedPreviewPath(), { text })
+ .then(resp => resp.json())
+ .then(data => this.renderMarkdown(data))
+ .catch(() => new Flash(s__('Error loading markdown preview')));
+ } else {
+ this.renderMarkdown();
+ }
+ },
- showWriteTab() {
- this.markdownPreview = '';
- this.previewMarkdown = false;
- },
+ showWriteTab() {
+ this.markdownPreview = '';
+ this.previewMarkdown = false;
+ },
- renderMarkdown(data = {}) {
- this.markdownPreviewLoading = false;
- this.markdownPreview = data.body || 'Nothing to preview.';
+ renderMarkdown(data = {}) {
+ this.markdownPreviewLoading = false;
+ this.markdownPreview = data.body || 'Nothing to preview.';
- if (data.references) {
- this.referencedCommands = data.references.commands;
- this.referencedUsers = data.references.users;
- }
+ if (data.references) {
+ this.referencedCommands = data.references.commands;
+ this.referencedUsers = data.references.users;
+ }
- this.$nextTick(() => {
- $(this.$refs['markdown-preview']).renderGFM();
- });
- },
+ this.$nextTick(() => {
+ $(this.$refs['markdown-preview']).renderGFM();
+ });
+ },
- versionedPreviewPath() {
- const { markdownPreviewPath, markdownVersion } = this;
- return `${markdownPreviewPath}${
- markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
- }markdown_version=${markdownVersion}`;
- },
+ versionedPreviewPath() {
+ const { markdownPreviewPath, markdownVersion } = this;
+ return `${markdownPreviewPath}${
+ markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
+ }markdown_version=${markdownVersion}`;
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index 8c22f3f6536..ca9e57a9b00 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -1,53 +1,64 @@
<script>
- import $ from 'jquery';
- import tooltip from '../../directives/tooltip';
- import toolbarButton from './toolbar_button.vue';
- import icon from '../icon.vue';
+import $ from 'jquery';
+import { GlTooltipDirective } from '@gitlab/ui';
+import ToolbarButton from './toolbar_button.vue';
+import Icon from '../icon.vue';
- export default {
- directives: {
- tooltip,
+export default {
+ components: {
+ ToolbarButton,
+ Icon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ previewMarkdown: {
+ type: Boolean,
+ required: true,
},
- components: {
- toolbarButton,
- icon,
+ },
+ computed: {
+ mdTable() {
+ return [
+ '| header | header |',
+ '| ------ | ------ |',
+ '| cell | cell |',
+ '| cell | cell |',
+ ].join('\n');
},
- props: {
- previewMarkdown: {
- type: Boolean,
- required: true,
- },
+ },
+ mounted() {
+ $(document).on('markdown-preview:show.vue', this.previewMarkdownTab);
+ $(document).on('markdown-preview:hide.vue', this.writeMarkdownTab);
+ },
+ beforeDestroy() {
+ $(document).off('markdown-preview:show.vue', this.previewMarkdownTab);
+ $(document).off('markdown-preview:hide.vue', this.writeMarkdownTab);
+ },
+ methods: {
+ isValid(form) {
+ return (
+ !form ||
+ (form.find('.js-vue-markdown-field').length && $(this.$el).closest('form')[0] === form[0])
+ );
},
- mounted() {
- $(document).on('markdown-preview:show.vue', this.previewMarkdownTab);
- $(document).on('markdown-preview:hide.vue', this.writeMarkdownTab);
- },
- beforeDestroy() {
- $(document).off('markdown-preview:show.vue', this.previewMarkdownTab);
- $(document).off('markdown-preview:hide.vue', this.writeMarkdownTab);
- },
- methods: {
- isValid(form) {
- return !form ||
- form.find('.js-vue-markdown-field').length &&
- $(this.$el).closest('form')[0] === form[0];
- },
- previewMarkdownTab(event, form) {
- if (event.target.blur) event.target.blur();
- if (!this.isValid(form)) return;
+ previewMarkdownTab(event, form) {
+ if (event.target.blur) event.target.blur();
+ if (!this.isValid(form)) return;
- this.$emit('preview-markdown');
- },
+ this.$emit('preview-markdown');
+ },
- writeMarkdownTab(event, form) {
- if (event.target.blur) event.target.blur();
- if (!this.isValid(form)) return;
+ writeMarkdownTab(event, form) {
+ if (event.target.blur) event.target.blur();
+ if (!this.isValid(form)) return;
- this.$emit('write-markdown');
- },
+ this.$emit('write-markdown');
},
- };
+ },
+};
</script>
<template>
@@ -57,27 +68,27 @@
:class="{ active: !previewMarkdown }"
class="md-header-tab"
>
- <a
+ <button
class="js-write-link"
- href="#md-write-holder"
tabindex="-1"
- @click.prevent="writeMarkdownTab($event)"
+ type="button"
+ @click="writeMarkdownTab($event)"
>
Write
- </a>
+ </button>
</li>
<li
:class="{ active: previewMarkdown }"
class="md-header-tab"
>
- <a
+ <button
class="js-preview-link js-md-preview-button"
- href="#md-preview-holder"
tabindex="-1"
- @click.prevent="previewMarkdownTab($event)"
+ type="button"
+ @click="previewMarkdownTab($event)"
>
Preview
- </a>
+ </button>
</li>
<li
:class="{ active: !previewMarkdown }"
@@ -106,6 +117,12 @@
icon="code"
/>
<toolbar-button
+ tag="[{text}](url)"
+ tag-select="url"
+ button-title="Add a link"
+ icon="link"
+ />
+ <toolbar-button
:prepend="true"
tag="* "
button-title="Add a bullet list"
@@ -123,8 +140,14 @@
button-title="Add a task list"
icon="task-done"
/>
+ <toolbar-button
+ :tag="mdTable"
+ :prepend="true"
+ :button-title="__('Add a table')"
+ icon="table"
+ />
<button
- v-tooltip
+ v-gl-tooltip
aria-label="Go full screen"
class="toolbar-btn toolbar-fullscreen-btn js-zen-enter"
data-container="body"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
index d63318f3da6..3cb48023002 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
@@ -1,57 +1,62 @@
<script>
- export default {
- props: {
- markdownDocsPath: {
- type: String,
- required: true,
- },
- quickActionsDocsPath: {
- type: String,
- required: false,
- default: '',
- },
- canAttachFile: {
- type: Boolean,
- required: false,
- default: true,
- },
+import { GlLink } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlLink,
+ },
+ props: {
+ markdownDocsPath: {
+ type: String,
+ required: true,
+ },
+ quickActionsDocsPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ canAttachFile: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- computed: {
- hasQuickActionsDocsPath() {
- return this.quickActionsDocsPath !== '';
- },
+ },
+ computed: {
+ hasQuickActionsDocsPath() {
+ return this.quickActionsDocsPath !== '';
},
- };
+ },
+};
</script>
<template>
<div class="comment-toolbar clearfix">
<div class="toolbar-text">
<template v-if="!hasQuickActionsDocsPath && markdownDocsPath">
- <a
+ <gl-link
:href="markdownDocsPath"
target="_blank"
tabindex="-1"
>
Markdown is supported
- </a>
+ </gl-link>
</template>
<template v-if="hasQuickActionsDocsPath && markdownDocsPath">
- <a
+ <gl-link
:href="markdownDocsPath"
target="_blank"
tabindex="-1"
>
Markdown
- </a>
+ </gl-link>
and
- <a
+ <gl-link
:href="quickActionsDocsPath"
target="_blank"
tabindex="-1"
>
quick actions
- </a>
+ </gl-link>
are supported
</template>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
index 9f1e009efdd..13af4b627de 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
@@ -1,45 +1,51 @@
<script>
- import tooltip from '../../directives/tooltip';
- import icon from '../icon.vue';
+import { GlTooltipDirective } from '@gitlab/ui';
+import Icon from '../icon.vue';
- export default {
- components: {
- icon,
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ buttonTitle: {
+ type: String,
+ required: true,
},
- directives: {
- tooltip,
+ icon: {
+ type: String,
+ required: true,
},
- props: {
- buttonTitle: {
- type: String,
- required: true,
- },
- icon: {
- type: String,
- required: true,
- },
- tag: {
- type: String,
- required: true,
- },
- tagBlock: {
- type: String,
- required: false,
- default: '',
- },
- prepend: {
- type: Boolean,
- required: false,
- default: false,
- },
+ tag: {
+ type: String,
+ required: true,
},
- };
+ tagBlock: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ tagSelect: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ prepend: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+};
</script>
<template>
<button
- v-tooltip
+ v-gl-tooltip
:data-md-tag="tag"
+ :data-md-select="tagSelect"
:data-md-block="tagBlock"
:data-md-prepend="prepend"
:title="buttonTitle"
diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.vue b/app/assets/javascripts/vue_shared/components/memory_graph.vue
index 552a92541be..964dedb38c4 100644
--- a/app/assets/javascripts/vue_shared/components/memory_graph.vue
+++ b/app/assets/javascripts/vue_shared/components/memory_graph.vue
@@ -41,7 +41,8 @@ export default {
// Find metric timestamp which is closest to deploymentTime
timestampDiff = Math.abs(metricTimestamps[0] - median);
metricTimestamps.forEach((timestamp, index) => {
- if (index === 0) { // Skip first element
+ if (index === 0) {
+ // Skip first element
return;
}
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
index 38115f268bb..dcad79e521d 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
@@ -1,41 +1,39 @@
<script>
- /**
- * Common component to render a placeholder note and user information.
- *
- * This component needs to be used with a vuex store.
- * That vuex store needs to have a `getUserData` getter that contains
- * {
- * path: String,
- * avatar_url: String,
- * name: String,
- * username: String,
- * }
- *
- * @example
- * <placeholder-note
- * :note="{body: 'This is a note'}"
- * />
- */
- import { mapGetters } from 'vuex';
- import userAvatarLink from '../user_avatar/user_avatar_link.vue';
+/**
+ * Common component to render a placeholder note and user information.
+ *
+ * This component needs to be used with a vuex store.
+ * That vuex store needs to have a `getUserData` getter that contains
+ * {
+ * path: String,
+ * avatar_url: String,
+ * name: String,
+ * username: String,
+ * }
+ *
+ * @example
+ * <placeholder-note
+ * :note="{body: 'This is a note'}"
+ * />
+ */
+import { mapGetters } from 'vuex';
+import userAvatarLink from '../user_avatar/user_avatar_link.vue';
- export default {
- name: 'PlaceholderNote',
- components: {
- userAvatarLink,
+export default {
+ name: 'PlaceholderNote',
+ components: {
+ userAvatarLink,
+ },
+ props: {
+ note: {
+ type: Object,
+ required: true,
},
- props: {
- note: {
- type: Object,
- required: true,
- },
- },
- computed: {
- ...mapGetters([
- 'getUserData',
- ]),
- },
- };
+ },
+ computed: {
+ ...mapGetters(['getUserData']),
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_system_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_system_note.vue
index 95e2b38e292..674f923478d 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_system_note.vue
@@ -1,21 +1,21 @@
<script>
- /**
- * Common component to render a placeholder system note.
- *
- * @example
- * <placeholder-system-note
- * :note="{ body: 'Commands are being applied'}"
- * />
- */
- export default {
- name: 'PlaceholderSystemNote',
- props: {
- note: {
- type: Object,
- required: true,
- },
+/**
+ * Common component to render a placeholder system note.
+ *
+ * @example
+ * <placeholder-system-note
+ * :note="{ body: 'Commands are being applied'}"
+ * />
+ */
+export default {
+ name: 'PlaceholderSystemNote',
+ props: {
+ note: {
+ type: Object,
+ required: true,
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue
index 2eb6c20b2c0..2dcd161b4fb 100644
--- a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue
@@ -1,25 +1,25 @@
+<script>
+import { GlSkeletonLoading } from '@gitlab/ui';
+
+export default {
+ name: 'SkeletonNote',
+ components: {
+ GlSkeletonLoading,
+ },
+};
+</script>
+
<template>
- <li class="timeline-entry note">
+ <li class="timeline-entry note note-wrapper">
<div class="timeline-entry-inner">
<div class="timeline-icon">
</div>
<div class="timeline-content">
<div class="note-header"></div>
<div class="note-body">
- <skeleton-loading-container />
+ <gl-skeleton-loading />
</div>
</div>
</div>
</li>
</template>
-
-<script>
-import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-
-export default {
- name: 'SkeletonNote',
- components: {
- skeletonLoadingContainer,
- },
-};
-</script>
diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
index 2122d0a508e..6a44e6a29ed 100644
--- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
@@ -76,7 +76,7 @@ export default {
<li
:id="noteAnchorId"
:class="{ target: isTargetNote }"
- class="note system-note timeline-entry">
+ class="note system-note timeline-entry note-wrapper">
<div class="timeline-entry-inner">
<div
class="timeline-icon"
@@ -109,7 +109,7 @@ export default {
class="system-note-commit-list-toggler flex-row"
@click="expanded = !expanded"
>
- <Icon
+ <icon
:name="toggleIcon"
:size="8"
class="append-right-5"
diff --git a/app/assets/javascripts/vue_shared/components/pagination_links.vue b/app/assets/javascripts/vue_shared/components/pagination_links.vue
new file mode 100644
index 00000000000..0b44f8578cb
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/pagination_links.vue
@@ -0,0 +1,38 @@
+<script>
+import { GlPagination } from '@gitlab/ui';
+import { s__ } from '../../locale';
+
+export default {
+ components: {
+ GlPagination,
+ },
+ props: {
+ change: {
+ type: Function,
+ required: true,
+ },
+ pageInfo: {
+ type: Object,
+ required: true,
+ },
+ },
+ firstText: s__('Pagination|« First'),
+ prevText: s__('Pagination|Prev'),
+ nextText: s__('Pagination|Next'),
+ lastText: s__('Pagination|Last »'),
+};
+</script>
+
+<template>
+ <gl-pagination
+ v-bind="$attrs"
+ :change="change"
+ :page="pageInfo.page"
+ :per-page="pageInfo.perPage"
+ :total-items="pageInfo.total"
+ :first-text="$options.firstText"
+ :prev-text="$options.prevText"
+ :next-text="$options.nextText"
+ :last-text="$options.lastText"
+ />
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
index 7947ae1e4da..bf736a378dd 100644
--- a/app/assets/javascripts/vue_shared/components/panel_resizer.vue
+++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
@@ -1,90 +1,90 @@
<script>
- export default {
- props: {
- startSize: {
- type: Number,
- required: true,
- },
- side: {
- type: String,
- required: true,
- },
- minSize: {
- type: Number,
- required: false,
- default: 0,
- },
- maxSize: {
- type: Number,
- required: false,
- default: Number.MAX_VALUE,
- },
- enabled: {
- type: Boolean,
- required: false,
- default: true,
- },
+export default {
+ props: {
+ startSize: {
+ type: Number,
+ required: true,
},
- data() {
- return {
- size: this.startSize,
- };
+ side: {
+ type: String,
+ required: true,
},
- computed: {
- className() {
- return `drag-${this.side}`;
- },
- cursorStyle() {
- if (this.enabled) {
- return { cursor: 'ew-resize' };
- }
- return {};
- },
+ minSize: {
+ type: Number,
+ required: false,
+ default: 0,
},
- methods: {
- resetSize(e) {
- e.preventDefault();
- this.$emit('resize-start', this.size);
+ maxSize: {
+ type: Number,
+ required: false,
+ default: Number.MAX_VALUE,
+ },
+ enabled: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+ data() {
+ return {
+ size: this.startSize,
+ };
+ },
+ computed: {
+ className() {
+ return `drag-${this.side}`;
+ },
+ cursorStyle() {
+ if (this.enabled) {
+ return { cursor: 'ew-resize' };
+ }
+ return {};
+ },
+ },
+ methods: {
+ resetSize(e) {
+ e.preventDefault();
+ this.$emit('resize-start', this.size);
- this.size = this.startSize;
- this.$emit('update:size', this.size);
+ this.size = this.startSize;
+ this.$emit('update:size', this.size);
- // End resizing on next tick so that listeners can react to DOM changes
- this.$nextTick(() => {
- this.$emit('resize-end', this.size);
- });
- },
- startDrag(e) {
- if (this.enabled) {
- e.preventDefault();
- this.startPos = e.clientX;
- this.currentStartSize = this.size;
- document.addEventListener('mousemove', this.drag);
- document.addEventListener('mouseup', this.endDrag, { once: true });
- this.$emit('resize-start', this.size);
- }
- },
- drag(e) {
+ // End resizing on next tick so that listeners can react to DOM changes
+ this.$nextTick(() => {
+ this.$emit('resize-end', this.size);
+ });
+ },
+ startDrag(e) {
+ if (this.enabled) {
e.preventDefault();
- let moved = e.clientX - this.startPos;
- if (this.side === 'left') moved = -moved;
- let newSize = this.currentStartSize + moved;
- if (newSize < this.minSize) {
- newSize = this.minSize;
- } else if (newSize > this.maxSize) {
- newSize = this.maxSize;
- }
- this.size = newSize;
+ this.startPos = e.clientX;
+ this.currentStartSize = this.size;
+ document.addEventListener('mousemove', this.drag);
+ document.addEventListener('mouseup', this.endDrag, { once: true });
+ this.$emit('resize-start', this.size);
+ }
+ },
+ drag(e) {
+ e.preventDefault();
+ let moved = e.clientX - this.startPos;
+ if (this.side === 'left') moved = -moved;
+ let newSize = this.currentStartSize + moved;
+ if (newSize < this.minSize) {
+ newSize = this.minSize;
+ } else if (newSize > this.maxSize) {
+ newSize = this.maxSize;
+ }
+ this.size = newSize;
- this.$emit('update:size', newSize);
- },
- endDrag(e) {
- e.preventDefault();
- document.removeEventListener('mousemove', this.drag);
- this.$emit('resize-end', this.size);
- },
+ this.$emit('update:size', newSize);
+ },
+ endDrag(e) {
+ e.preventDefault();
+ document.removeEventListener('mousemove', this.drag);
+ this.$emit('resize-end', this.size);
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/pikaday.vue b/app/assets/javascripts/vue_shared/components/pikaday.vue
index bfeece12077..26c99aecae4 100644
--- a/app/assets/javascripts/vue_shared/components/pikaday.vue
+++ b/app/assets/javascripts/vue_shared/components/pikaday.vue
@@ -1,62 +1,62 @@
<script>
- import Pikaday from 'pikaday';
- import { parsePikadayDate, pikadayToString } from '../../lib/utils/datefix';
+import Pikaday from 'pikaday';
+import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility';
- export default {
- name: 'DatePicker',
- props: {
- label: {
- type: String,
- required: false,
- default: 'Date picker',
- },
- selectedDate: {
- type: Date,
- required: false,
- default: null,
- },
- minDate: {
- type: Date,
- required: false,
- default: null,
- },
- maxDate: {
- type: Date,
- required: false,
- default: null,
- },
+export default {
+ name: 'DatePicker',
+ props: {
+ label: {
+ type: String,
+ required: false,
+ default: 'Date picker',
},
- mounted() {
- this.calendar = new Pikaday({
- field: this.$el.querySelector('.dropdown-menu-toggle'),
- theme: 'gitlab-theme animate-picker',
- format: 'yyyy-mm-dd',
- container: this.$el,
- defaultDate: this.selectedDate,
- setDefaultDate: !!this.selectedDate,
- minDate: this.minDate,
- maxDate: this.maxDate,
- parse: dateString => parsePikadayDate(dateString),
- toString: date => pikadayToString(date),
- onSelect: this.selected.bind(this),
- onClose: this.toggled.bind(this),
- });
-
- this.$el.append(this.calendar.el);
- this.calendar.show();
+ selectedDate: {
+ type: Date,
+ required: false,
+ default: null,
+ },
+ minDate: {
+ type: Date,
+ required: false,
+ default: null,
},
- beforeDestroy() {
- this.calendar.destroy();
+ maxDate: {
+ type: Date,
+ required: false,
+ default: null,
+ },
+ },
+ mounted() {
+ this.calendar = new Pikaday({
+ field: this.$el.querySelector('.dropdown-menu-toggle'),
+ theme: 'gitlab-theme animate-picker',
+ format: 'yyyy-mm-dd',
+ container: this.$el,
+ defaultDate: this.selectedDate,
+ setDefaultDate: !!this.selectedDate,
+ minDate: this.minDate,
+ maxDate: this.maxDate,
+ parse: dateString => parsePikadayDate(dateString),
+ toString: date => pikadayToString(date),
+ onSelect: this.selected.bind(this),
+ onClose: this.toggled.bind(this),
+ });
+
+ this.$el.append(this.calendar.el);
+ this.calendar.show();
+ },
+ beforeDestroy() {
+ this.calendar.destroy();
+ },
+ methods: {
+ selected(dateText) {
+ this.$emit('newDateSelected', this.calendar.toString(dateText));
},
- methods: {
- selected(dateText) {
- this.$emit('newDateSelected', this.calendar.toString(dateText));
- },
- toggled() {
- this.$emit('hidePicker');
- },
+ toggled() {
+ this.$emit('hidePicker');
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
index 97ca4d93bd7..1a2fd2ad985 100644
--- a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
+++ b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
@@ -1,6 +1,5 @@
<script>
-
- /* This is a re-usable vue component for rendering a project avatar that
+/* This is a re-usable vue component for rendering a project avatar that
does not need to link to the project's profile. The image and an optional
tooltip can be configured by props passed to this component.
@@ -16,70 +15,70 @@
*/
- import defaultAvatarUrl from 'images/no_avatar.png';
- import { placeholderImage } from '../../../lazy_loader';
- import tooltip from '../../directives/tooltip';
+import defaultAvatarUrl from 'images/no_avatar.png';
+import { placeholderImage } from '../../../lazy_loader';
+import tooltip from '../../directives/tooltip';
- export default {
- name: 'ProjectAvatarImage',
- directives: {
- tooltip,
+export default {
+ name: 'ProjectAvatarImage',
+ directives: {
+ tooltip,
+ },
+ props: {
+ lazy: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ imgSrc: {
+ type: String,
+ required: false,
+ default: defaultAvatarUrl,
+ },
+ cssClasses: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ imgAlt: {
+ type: String,
+ required: false,
+ default: 'project avatar',
+ },
+ size: {
+ type: Number,
+ required: false,
+ default: 20,
+ },
+ tooltipText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ tooltipPlacement: {
+ type: String,
+ required: false,
+ default: 'top',
+ },
+ },
+ computed: {
+ // API response sends null when gravatar is disabled and
+ // we provide an empty string when we use it inside project avatar link.
+ // In both cases we should render the defaultAvatarUrl
+ sanitizedSource() {
+ return this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc;
+ },
+ resultantSrcAttribute() {
+ return this.lazy ? placeholderImage : this.sanitizedSource;
},
- props: {
- lazy: {
- type: Boolean,
- required: false,
- default: false,
- },
- imgSrc: {
- type: String,
- required: false,
- default: defaultAvatarUrl,
- },
- cssClasses: {
- type: String,
- required: false,
- default: '',
- },
- imgAlt: {
- type: String,
- required: false,
- default: 'project avatar',
- },
- size: {
- type: Number,
- required: false,
- default: 20,
- },
- tooltipText: {
- type: String,
- required: false,
- default: '',
- },
- tooltipPlacement: {
- type: String,
- required: false,
- default: 'top',
- },
+ tooltipContainer() {
+ return this.tooltipText ? 'body' : null;
},
- computed: {
- // API response sends null when gravatar is disabled and
- // we provide an empty string when we use it inside project avatar link.
- // In both cases we should render the defaultAvatarUrl
- sanitizedSource() {
- return this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc;
- },
- resultantSrcAttribute() {
- return this.lazy ? placeholderImage : this.sanitizedSource;
- },
- tooltipContainer() {
- return this.tooltipText ? 'body' : null;
- },
- avatarSizeClass() {
- return `s${this.size}`;
- },
+ avatarSizeClass() {
+ return `s${this.size}`;
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
index a2a9a5e6987..09394847b10 100644
--- a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
@@ -1,67 +1,67 @@
<script>
- import DeprecatedModal from './deprecated_modal.vue';
+import DeprecatedModal from './deprecated_modal.vue';
- export default {
- name: 'RecaptchaModal',
+export default {
+ name: 'RecaptchaModal',
- components: {
- DeprecatedModal,
- },
+ components: {
+ DeprecatedModal,
+ },
- props: {
- html: {
- type: String,
- required: false,
- default: '',
- },
+ props: {
+ html: {
+ type: String,
+ required: false,
+ default: '',
},
+ },
- data() {
- return {
- script: {},
- scriptSrc: 'https://www.google.com/recaptcha/api.js',
- };
- },
+ data() {
+ return {
+ script: {},
+ scriptSrc: 'https://www.google.com/recaptcha/api.js',
+ };
+ },
- watch: {
- html() {
- this.appendRecaptchaScript();
- },
+ watch: {
+ html() {
+ this.appendRecaptchaScript();
},
+ },
- mounted() {
- window.recaptchaDialogCallback = this.submit.bind(this);
- },
+ mounted() {
+ window.recaptchaDialogCallback = this.submit.bind(this);
+ },
- methods: {
- appendRecaptchaScript() {
- this.removeRecaptchaScript();
+ methods: {
+ appendRecaptchaScript() {
+ this.removeRecaptchaScript();
- const script = document.createElement('script');
- script.src = this.scriptSrc;
- script.classList.add('js-recaptcha-script');
- script.async = true;
- script.defer = true;
+ const script = document.createElement('script');
+ script.src = this.scriptSrc;
+ script.classList.add('js-recaptcha-script');
+ script.async = true;
+ script.defer = true;
- this.script = script;
+ this.script = script;
- document.body.appendChild(script);
- },
+ document.body.appendChild(script);
+ },
- removeRecaptchaScript() {
- if (this.script instanceof Element) this.script.remove();
- },
+ removeRecaptchaScript() {
+ if (this.script instanceof Element) this.script.remove();
+ },
- close() {
- this.removeRecaptchaScript();
- this.$emit('close');
- },
+ close() {
+ this.removeRecaptchaScript();
+ this.$emit('close');
+ },
- submit() {
- this.$el.querySelector('form').submit();
- },
+ submit() {
+ this.$el.querySelector('form').submit();
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
index 7f1eb6bcec4..5841db52704 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
@@ -1,34 +1,50 @@
<script>
- export default {
- name: 'CollapsedCalendarIcon',
- props: {
- containerClass: {
- type: String,
- required: false,
- default: '',
- },
- text: {
- type: String,
- required: false,
- default: '',
- },
- showIcon: {
- type: Boolean,
- required: false,
- default: true,
- },
+import tooltip from '~/vue_shared/directives/tooltip';
+
+export default {
+ name: 'CollapsedCalendarIcon',
+ directives: {
+ tooltip,
+ },
+ props: {
+ containerClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ text: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ showIcon: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ tooltipText: {
+ type: String,
+ required: false,
+ default: '',
},
- methods: {
- click() {
- this.$emit('click');
- },
+ },
+ methods: {
+ click() {
+ this.$emit('click');
},
- };
+ },
+};
</script>
<template>
<div
+ v-tooltip
:class="containerClass"
+ :title="tooltipText"
+ data-container="body"
+ data-placement="left"
+ data-html="true"
+ data-boundary="viewport"
@click="click"
>
<i
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
index dac438a702d..174c29809ac 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
@@ -1,88 +1,87 @@
<script>
- import { dateInWords } from '../../../lib/utils/datetime_utility';
- import toggleSidebar from './toggle_sidebar.vue';
- import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
+import { __ } from '~/locale';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { dateInWords, timeFor } from '~/lib/utils/datetime_utility';
+import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
- export default {
- name: 'SidebarCollapsedGroupedDatePicker',
- components: {
- toggleSidebar,
- collapsedCalendarIcon,
+export default {
+ name: 'SidebarCollapsedGroupedDatePicker',
+ components: {
+ collapsedCalendarIcon,
+ },
+ mixins: [timeagoMixin],
+ props: {
+ collapsed: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- props: {
- collapsed: {
- type: Boolean,
- required: false,
- default: true,
- },
- showToggleSidebar: {
- type: Boolean,
- required: false,
- default: false,
- },
- minDate: {
- type: Date,
- required: false,
- default: null,
- },
- maxDate: {
- type: Date,
- required: false,
- default: null,
- },
- disableClickableIcons: {
- type: Boolean,
- required: false,
- default: false,
- },
+ minDate: {
+ type: Date,
+ required: false,
+ default: null,
},
- computed: {
- hasMinAndMaxDates() {
- return this.minDate && this.maxDate;
- },
- hasNoMinAndMaxDates() {
- return !this.minDate && !this.maxDate;
- },
- showMinDateBlock() {
- return this.minDate || this.hasNoMinAndMaxDates;
- },
- showFromText() {
- return !this.maxDate && this.minDate;
- },
- iconClass() {
- const disabledClass = this.disableClickableIcons ? 'disabled' : '';
- return `block sidebar-collapsed-icon calendar-icon ${disabledClass}`;
- },
+ maxDate: {
+ type: Date,
+ required: false,
+ default: null,
},
- methods: {
- toggleSidebar() {
- this.$emit('toggleCollapse');
- },
- dateText(dateType = 'min') {
- const date = this[`${dateType}Date`];
- const dateWords = dateInWords(date, true);
- const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords;
+ disableClickableIcons: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ hasMinAndMaxDates() {
+ return this.minDate && this.maxDate;
+ },
+ hasNoMinAndMaxDates() {
+ return !this.minDate && !this.maxDate;
+ },
+ showMinDateBlock() {
+ return this.minDate || this.hasNoMinAndMaxDates;
+ },
+ showFromText() {
+ return !this.maxDate && this.minDate;
+ },
+ iconClass() {
+ const disabledClass = this.disableClickableIcons ? 'disabled' : '';
+ return `sidebar-collapsed-icon calendar-icon ${disabledClass}`;
+ },
+ },
+ methods: {
+ toggleSidebar() {
+ this.$emit('toggleCollapse');
+ },
+ dateText(dateType = 'min') {
+ const date = this[`${dateType}Date`];
+ const dateWords = dateInWords(date, true);
+ const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords;
+
+ return date ? parsedDateWords : __('None');
+ },
+ tooltipText(dateType = 'min') {
+ const defaultText = dateType === 'min' ? __('Start date') : __('Due date');
+ const date = this[`${dateType}Date`];
+ const timeAgo = dateType === 'min' ? this.timeFormated(date) : timeFor(date);
+ const dateText = date ? [this.dateText(dateType), `(${timeAgo})`].join(' ') : '';
- return date ? parsedDateWords : 'None';
- },
+ if (date) {
+ return [defaultText, dateText].join('<br />');
+ }
+ return __('Start and due date');
},
- };
+ },
+};
</script>
<template>
<div class="block sidebar-grouped-item">
- <div
- v-if="showToggleSidebar"
- class="issuable-sidebar-header"
- >
- <toggle-sidebar
- :collapsed="collapsed"
- @toggle="toggleSidebar"
- />
- </div>
<collapsed-calendar-icon
v-if="showMinDateBlock"
:container-class="iconClass"
+ :tooltip-text="tooltipText('min')"
@click="toggleSidebar"
>
<span class="sidebar-collapsed-value">
@@ -99,7 +98,7 @@
<collapsed-calendar-icon
v-if="maxDate"
:container-class="iconClass"
- :show-icon="!minDate"
+ :tooltip-text="tooltipText('max')"
@click="toggleSidebar"
>
<span class="sidebar-collapsed-value">
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
index 74998a4787d..6aa880603b9 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
@@ -1,98 +1,98 @@
<script>
- import datePicker from '../pikaday.vue';
- import loadingIcon from '../loading_icon.vue';
- import toggleSidebar from './toggle_sidebar.vue';
- import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
- import { dateInWords } from '../../../lib/utils/datetime_utility';
+import { GlLoadingIcon } from '@gitlab/ui';
+import datePicker from '../pikaday.vue';
+import toggleSidebar from './toggle_sidebar.vue';
+import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
+import { dateInWords } from '../../../lib/utils/datetime_utility';
- export default {
- name: 'SidebarDatePicker',
- components: {
- datePicker,
- toggleSidebar,
- loadingIcon,
- collapsedCalendarIcon,
- },
- props: {
- blockClass: {
- type: String,
- required: false,
- default: '',
- },
- collapsed: {
- type: Boolean,
- required: false,
- default: true,
- },
- showToggleSidebar: {
- type: Boolean,
- required: false,
- default: false,
- },
- isLoading: {
- type: Boolean,
- required: false,
- default: false,
- },
- editable: {
- type: Boolean,
- required: false,
- default: false,
- },
- label: {
- type: String,
- required: false,
- default: 'Date picker',
- },
- selectedDate: {
- type: Date,
- required: false,
- default: null,
- },
- minDate: {
- type: Date,
- required: false,
- default: null,
- },
- maxDate: {
- type: Date,
- required: false,
- default: null,
- },
- },
- data() {
- return {
- editing: false,
- };
- },
- computed: {
- selectedAndEditable() {
- return this.selectedDate && this.editable;
- },
- selectedDateWords() {
- return dateInWords(this.selectedDate, true);
- },
- collapsedText() {
- return this.selectedDateWords ? this.selectedDateWords : 'None';
- },
- },
- methods: {
- stopEditing() {
- this.editing = false;
- },
- toggleDatePicker() {
- this.editing = !this.editing;
- },
- newDateSelected(date = null) {
- this.date = date;
- this.editing = false;
- this.$emit('saveDate', date);
- },
- toggleSidebar() {
- this.$emit('toggleCollapse');
- },
- },
- };
+export default {
+ name: 'SidebarDatePicker',
+ components: {
+ datePicker,
+ toggleSidebar,
+ collapsedCalendarIcon,
+ GlLoadingIcon,
+ },
+ props: {
+ blockClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ collapsed: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ showToggleSidebar: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ editable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ label: {
+ type: String,
+ required: false,
+ default: 'Date picker',
+ },
+ selectedDate: {
+ type: Date,
+ required: false,
+ default: null,
+ },
+ minDate: {
+ type: Date,
+ required: false,
+ default: null,
+ },
+ maxDate: {
+ type: Date,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ editing: false,
+ };
+ },
+ computed: {
+ selectedAndEditable() {
+ return this.selectedDate && this.editable;
+ },
+ selectedDateWords() {
+ return dateInWords(this.selectedDate, true);
+ },
+ collapsedText() {
+ return this.selectedDateWords ? this.selectedDateWords : 'None';
+ },
+ },
+ methods: {
+ stopEditing() {
+ this.editing = false;
+ },
+ toggleDatePicker() {
+ this.editing = !this.editing;
+ },
+ newDateSelected(date = null) {
+ this.date = date;
+ this.editing = false;
+ this.$emit('saveDate', date);
+ },
+ toggleSidebar() {
+ this.$emit('toggleCollapse');
+ },
+ },
+};
</script>
<template>
@@ -112,7 +112,7 @@
/>
<div class="title">
{{ label }}
- <loading-icon
+ <gl-loading-icon
v-if="isLoading"
:inline="true"
/>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
index a3fc358130f..98b8b6460fe 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
@@ -3,8 +3,8 @@ import $ from 'jquery';
import { __ } from '~/locale';
import LabelsSelect from '~/labels_select';
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
-import LoadingIcon from '../../loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
import DropdownTitle from './dropdown_title.vue';
import DropdownValue from './dropdown_value.vue';
import DropdownValueCollapsed from './dropdown_value_collapsed.vue';
@@ -16,7 +16,6 @@ import DropdownCreateLabel from './dropdown_create_label.vue';
export default {
components: {
- LoadingIcon,
DropdownTitle,
DropdownValue,
DropdownValueCollapsed,
@@ -26,6 +25,7 @@ export default {
DropdownSearchInput,
DropdownFooter,
DropdownCreateLabel,
+ GlLoadingIcon,
},
props: {
showCreate: {
@@ -164,7 +164,7 @@ dropdown-menu-labels dropdown-menu-selectable"
<dropdown-search-input/>
<div class="dropdown-content"></div>
<div class="dropdown-loading">
- <loading-icon />
+ <gl-loading-icon />
</div>
<dropdown-footer
v-if="showCreate"
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
index af297f3c408..0d5fc07e6e3 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
@@ -14,7 +14,10 @@ export default {
},
computed: {
labelsList() {
- const labelsString = this.labels.slice(0, 5).map(label => label.title).join(', ');
+ const labelsString = this.labels
+ .slice(0, 5)
+ .map(label => label.title)
+ .join(', ');
if (this.labels.length > 5) {
return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
diff --git a/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue b/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue
deleted file mode 100644
index 4a5ffbe5d5a..00000000000
--- a/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-<script>
- export default {
- props: {
- small: {
- type: Boolean,
- required: false,
- default: false,
- },
- lines: {
- type: Number,
- required: false,
- default: 3,
- },
- },
- computed: {
- lineClasses() {
- return new Array(this.lines).fill().map((_, i) => `skeleton-line-${i + 1}`);
- },
- },
- };
-</script>
-
-<template>
- <div
- :class="{
- 'animation-container-small': small,
- }"
- class="animation-container"
- >
- <div
- v-for="(css, index) in lineClasses"
- :key="index"
- :class="css"
- >
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue b/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue
new file mode 100644
index 00000000000..63034a45f77
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue
@@ -0,0 +1,42 @@
+<script>
+import VirtualList from 'vue-virtual-scroll-list';
+
+export default {
+ name: 'SmartVirtualList',
+ components: { VirtualList },
+ props: {
+ size: { type: Number, required: true },
+ length: { type: Number, required: true },
+ remain: { type: Number, required: true },
+ rtag: { type: String, default: 'div' },
+ wtag: { type: String, default: 'div' },
+ wclass: { type: String, default: null },
+ },
+};
+</script>
+<template>
+ <virtual-list
+ v-if="length > remain"
+ v-bind="$attrs"
+ :size="remain"
+ :remain="remain"
+ :rtag="rtag"
+ :wtag="wtag"
+ :wclass="wclass"
+ class="js-virtual-list"
+ >
+ <slot></slot>
+ </virtual-list>
+ <component
+ :is="rtag"
+ v-else
+ class="js-plain-element"
+ >
+ <component
+ :is="wtag"
+ :class="wclass"
+ >
+ <slot></slot>
+ </component>
+ </component>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
index f44d361c47e..cd3ee544344 100644
--- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
+++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
@@ -71,7 +71,11 @@ export default {
},
methods: {
getPercent(count) {
- return roundOffFloat((count / this.totalCount) * 100, 1);
+ const percent = roundOffFloat((count / this.totalCount) * 100, 1);
+ if (percent > 0 && percent < 1) {
+ return '< 1';
+ }
+ return percent;
},
barStyle(percent) {
return `width: ${percent}%;`;
@@ -95,8 +99,8 @@ export default {
{{ __("Not available") }}
</span>
<span
- v-tooltip
v-if="successPercent"
+ v-tooltip
:title="successTooltip"
:style="successBarStyle"
class="status-green"
@@ -105,8 +109,8 @@ export default {
{{ successPercent }}%
</span>
<span
- v-tooltip
v-if="neutralPercent"
+ v-tooltip
:title="neutralTooltip"
:style="neutralBarStyle"
class="status-neutral"
@@ -115,8 +119,8 @@ export default {
{{ neutralPercent }}%
</span>
<span
- v-tooltip
v-if="failurePercent"
+ v-tooltip
:title="failureTooltip"
:style="failureBarStyle"
class="status-red"
diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue
index 8e9621c956f..03a5a078879 100644
--- a/app/assets/javascripts/vue_shared/components/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue
@@ -1,17 +1,17 @@
<script>
- import { s__ } from '../../locale';
-
- const PAGINATION_UI_BUTTON_LIMIT = 4;
- const UI_LIMIT = 6;
- const SPREAD = '...';
- const PREV = s__('Pagination|Prev');
- const NEXT = s__('Pagination|Next');
- const FIRST = s__('Pagination|« First');
- const LAST = s__('Pagination|Last »');
-
- export default {
- props: {
- /**
+import { s__ } from '../../locale';
+
+const PAGINATION_UI_BUTTON_LIMIT = 4;
+const UI_LIMIT = 6;
+const SPREAD = '...';
+const PREV = s__('Pagination|Prev');
+const NEXT = s__('Pagination|Next');
+const FIRST = s__('Pagination|« First');
+const LAST = s__('Pagination|Last »');
+
+export default {
+ props: {
+ /**
This function will take the information given by the pagination component
Here is an example `change` method:
@@ -20,12 +20,12 @@
gl.utils.visitUrl(`?page=${pagenum}`);
},
*/
- change: {
- type: Function,
- required: true,
- },
+ change: {
+ type: Function,
+ required: true,
+ },
- /**
+ /**
pageInfo will come from the headers of the API call
in the `.then` clause of the VueResource API call
there should be a function that contructs the pageInfo for this component
@@ -41,94 +41,94 @@
previousPage: +headers['X-Prev-Page'],
});
*/
- pageInfo: {
- type: Object,
- required: true,
- },
+ pageInfo: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ prev() {
+ return this.pageInfo.previousPage;
+ },
+ next() {
+ return this.pageInfo.nextPage;
+ },
+ getItems() {
+ const total = this.pageInfo.totalPages;
+ const { page } = this.pageInfo;
+ const items = [];
+
+ if (page > 1) {
+ items.push({ title: FIRST, first: true });
+ }
+
+ if (page > 1) {
+ items.push({ title: PREV, prev: true });
+ } else {
+ items.push({ title: PREV, disabled: true, prev: true });
+ }
+
+ if (page > UI_LIMIT) items.push({ title: SPREAD, separator: true });
+
+ const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
+ const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total);
+
+ for (let i = start; i <= end; i += 1) {
+ const isActive = i === page;
+ items.push({ title: i, active: isActive, page: true });
+ }
+
+ if (total - page > PAGINATION_UI_BUTTON_LIMIT) {
+ items.push({ title: SPREAD, separator: true, page: true });
+ }
+
+ if (page === total) {
+ items.push({ title: NEXT, disabled: true, next: true });
+ } else if (total - page >= 1) {
+ items.push({ title: NEXT, next: true });
+ }
+
+ if (total - page >= 1) {
+ items.push({ title: LAST, last: true });
+ }
+
+ return items;
+ },
+ showPagination() {
+ return this.pageInfo.totalPages > 1;
},
- computed: {
- prev() {
- return this.pageInfo.previousPage;
- },
- next() {
- return this.pageInfo.nextPage;
- },
- getItems() {
- const total = this.pageInfo.totalPages;
- const { page } = this.pageInfo;
- const items = [];
-
- if (page > 1) {
- items.push({ title: FIRST, first: true });
- }
-
- if (page > 1) {
- items.push({ title: PREV, prev: true });
- } else {
- items.push({ title: PREV, disabled: true, prev: true });
- }
-
- if (page > UI_LIMIT) items.push({ title: SPREAD, separator: true });
-
- const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
- const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total);
-
- for (let i = start; i <= end; i += 1) {
- const isActive = i === page;
- items.push({ title: i, active: isActive, page: true });
- }
-
- if (total - page > PAGINATION_UI_BUTTON_LIMIT) {
- items.push({ title: SPREAD, separator: true, page: true });
- }
-
- if (page === total) {
- items.push({ title: NEXT, disabled: true, next: true });
- } else if (total - page >= 1) {
- items.push({ title: NEXT, next: true });
- }
-
- if (total - page >= 1) {
- items.push({ title: LAST, last: true });
- }
-
- return items;
- },
- showPagination() {
- return this.pageInfo.totalPages > 1;
- },
+ },
+ methods: {
+ changePage(text, isDisabled) {
+ if (isDisabled) return;
+
+ const { totalPages, nextPage, previousPage } = this.pageInfo;
+
+ switch (text) {
+ case SPREAD:
+ break;
+ case LAST:
+ this.change(totalPages);
+ break;
+ case NEXT:
+ this.change(nextPage);
+ break;
+ case PREV:
+ this.change(previousPage);
+ break;
+ case FIRST:
+ this.change(1);
+ break;
+ default:
+ this.change(+text);
+ break;
+ }
},
- methods: {
- changePage(text, isDisabled) {
- if (isDisabled) return;
-
- const { totalPages, nextPage, previousPage } = this.pageInfo;
-
- switch (text) {
- case SPREAD:
- break;
- case LAST:
- this.change(totalPages);
- break;
- case NEXT:
- this.change(nextPage);
- break;
- case PREV:
- this.change(previousPage);
- break;
- case FIRST:
- this.change(1);
- break;
- default:
- this.change(+text);
- break;
- }
- },
- hideOnSmallScreen(item) {
- return !item.first && !item.last && !item.next && !item.prev && !item.active;
- },
+ hideOnSmallScreen(item) {
+ return !item.first && !item.last && !item.next && !item.prev && !item.active;
},
- };
+ },
+};
</script>
<template>
<div
diff --git a/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue b/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
index 368eeb6c453..d760263929a 100644
--- a/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
+++ b/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
@@ -11,9 +11,7 @@ export default {
directives: {
tooltip,
},
- mixins: [
- timeagoMixin,
- ],
+ mixins: [timeagoMixin],
props: {
time: {
type: String,
diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue
index a897300b62b..5d1c92c3b3d 100644
--- a/app/assets/javascripts/vue_shared/components/toggle_button.vue
+++ b/app/assets/javascripts/vue_shared/components/toggle_button.vue
@@ -1,62 +1,62 @@
<script>
- import { s__ } from '../../locale';
- import icon from './icon.vue';
- import loadingIcon from './loading_icon.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { s__ } from '../../locale';
+import icon from './icon.vue';
- const ICON_ON = 'status_success_borderless';
- const ICON_OFF = 'status_failed_borderless';
- const LABEL_ON = s__('ToggleButton|Toggle Status: ON');
- const LABEL_OFF = s__('ToggleButton|Toggle Status: OFF');
+const ICON_ON = 'status_success_borderless';
+const ICON_OFF = 'status_failed_borderless';
+const LABEL_ON = s__('ToggleButton|Toggle Status: ON');
+const LABEL_OFF = s__('ToggleButton|Toggle Status: OFF');
- export default {
- components: {
- icon,
- loadingIcon,
- },
+export default {
+ components: {
+ icon,
+ GlLoadingIcon,
+ },
- model: {
- prop: 'value',
- event: 'change',
- },
+ model: {
+ prop: 'value',
+ event: 'change',
+ },
- props: {
- name: {
- type: String,
- required: false,
- default: null,
- },
- value: {
- type: Boolean,
- required: false,
- default: null,
- },
- disabledInput: {
- type: Boolean,
- required: false,
- default: false,
- },
- isLoading: {
- type: Boolean,
- required: false,
- default: false,
- },
+ props: {
+ name: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ value: {
+ type: Boolean,
+ required: false,
+ default: null,
},
+ disabledInput: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
- computed: {
- toggleIcon() {
- return this.value ? ICON_ON : ICON_OFF;
- },
- ariaLabel() {
- return this.value ? LABEL_ON : LABEL_OFF;
- },
+ computed: {
+ toggleIcon() {
+ return this.value ? ICON_ON : ICON_OFF;
+ },
+ ariaLabel() {
+ return this.value ? LABEL_ON : LABEL_OFF;
},
+ },
- methods: {
- toggleFeature() {
- if (!this.disabledInput) this.$emit('change', !this.value);
- },
+ methods: {
+ toggleFeature() {
+ if (!this.disabledInput) this.$emit('change', !this.value);
},
- };
+ },
+};
</script>
<template>
@@ -78,7 +78,7 @@
class="project-feature-toggle"
@click="toggleFeature"
>
- <loadingIcon class="loading-icon" />
+ <gl-loading-icon class="loading-icon" />
<span class="toggle-icon">
<icon
:name="toggleIcon"
diff --git a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue
new file mode 100644
index 00000000000..d5b58574123
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue
@@ -0,0 +1,67 @@
+<script>
+import _ from 'underscore';
+import tooltip from '../directives/tooltip';
+
+export default {
+ directives: {
+ tooltip,
+ },
+ props: {
+ title: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ placement: {
+ type: String,
+ required: false,
+ default: 'top',
+ },
+ truncateTarget: {
+ type: [String, Function],
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ showTooltip: false,
+ };
+ },
+ mounted() {
+ const target = this.selectTarget();
+
+ if (target && target.scrollWidth > target.offsetWidth) {
+ this.showTooltip = true;
+ }
+ },
+ methods: {
+ selectTarget() {
+ if (_.isFunction(this.truncateTarget)) {
+ return this.truncateTarget(this.$el);
+ } else if (this.truncateTarget === 'child') {
+ return this.$el.childNodes[0];
+ }
+
+ return this.$el;
+ },
+ },
+};
+</script>
+
+<template>
+ <span
+ v-if="showTooltip"
+ v-tooltip
+ :title="title"
+ :data-placement="placement"
+ class="js-show-tooltip"
+ >
+ <slot></slot>
+ </span>
+ <span
+ v-else
+ >
+ <slot></slot>
+ </span>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
index 7737b9f2697..c78d98ccd9e 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
@@ -15,14 +15,14 @@
*/
+import { GlTooltip } from '@gitlab/ui';
import defaultAvatarUrl from 'images/no_avatar.png';
import { placeholderImage } from '../../../lazy_loader';
-import tooltip from '../../directives/tooltip';
export default {
name: 'UserAvatarImage',
- directives: {
- tooltip,
+ components: {
+ GlTooltip,
},
props: {
lazy: {
@@ -73,9 +73,6 @@ export default {
resultantSrcAttribute() {
return this.lazy ? placeholderImage : this.sanitizedSource;
},
- tooltipContainer() {
- return this.tooltipText ? 'body' : null;
- },
avatarSizeClass() {
return `s${this.size}`;
},
@@ -84,22 +81,30 @@ export default {
</script>
<template>
- <img
- v-tooltip
- :class="{
- lazy: lazy,
- [avatarSizeClass]: true,
- [cssClasses]: true
- }"
- :src="resultantSrcAttribute"
- :width="size"
- :height="size"
- :alt="imgAlt"
- :data-src="sanitizedSource"
- :data-container="tooltipContainer"
- :data-placement="tooltipPlacement"
- :title="tooltipText"
- class="avatar"
- data-boundary="window"
- />
+ <span>
+ <img
+ ref="userAvatarImage"
+ :class="{
+ lazy: lazy,
+ [avatarSizeClass]: true,
+ [cssClasses]: true
+ }"
+ :src="resultantSrcAttribute"
+ :width="size"
+ :height="size"
+ :alt="imgAlt"
+ :data-src="sanitizedSource"
+ class="avatar"
+ />
+ <gl-tooltip
+ :target="() => $refs.userAvatarImage"
+ :placement="tooltipPlacement"
+ boundary="window"
+ class="js-user-avatar-image-toolip"
+ >
+ <slot>
+ {{ tooltipText }}
+ </slot>
+ </gl-tooltip>
+ </span>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
index 01c36fec41a..6dd519ea56d 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
@@ -1,5 +1,4 @@
<script>
-
/* This is a re-usable vue component for rendering a user avatar wrapped in
a clickable link (likely to the user's profile). The link, image, and
tooltip can be configured by props passed to this component.
@@ -18,16 +17,17 @@
*/
+import { GlLink, GlTooltipDirective } from '@gitlab/ui';
import userAvatarImage from './user_avatar_image.vue';
-import tooltip from '../../directives/tooltip';
export default {
name: 'UserAvatarLink',
components: {
+ GlLink,
userAvatarImage,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
linkHref: {
@@ -83,7 +83,7 @@ export default {
</script>
<template>
- <a
+ <gl-link
:href="linkHref"
class="user-avatar-link">
<user-avatar-image
@@ -93,11 +93,14 @@ export default {
:size="imgSize"
:tooltip-text="avatarTooltipText"
:tooltip-placement="tooltipPlacement"
- /><span
- v-tooltip
+ >
+ <slot></slot>
+ </user-avatar-image><span
v-if="shouldShowUsername"
+ v-gl-tooltip
:title="tooltipText"
:tooltip-placement="tooltipPlacement"
- >{{ username }}</span>
- </a>
+ class="js-user-avatar-link-username"
+ >{{ username }}</span><slot name="avatar-badge"></slot>
+ </gl-link>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_svg.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_svg.vue
index ef3b16edf5f..8e460566d09 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_svg.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_svg.vue
@@ -1,5 +1,4 @@
<script>
-
/* This is a re-usable vue component for rendering a user avatar svg (typically
for a blank state). It will receive styles comparable to the user avatar,
but no image is loaded, it isn't wrapped in a link, and tooltips aren't supported.
@@ -42,4 +41,3 @@ export default {
v-html="svg"
/>
</template>
-
diff --git a/app/assets/javascripts/vue_shared/directives/tooltip.js b/app/assets/javascripts/vue_shared/directives/tooltip.js
index 4f2412ce520..549d27e96d9 100644
--- a/app/assets/javascripts/vue_shared/directives/tooltip.js
+++ b/app/assets/javascripts/vue_shared/directives/tooltip.js
@@ -9,6 +9,14 @@ export default {
componentUpdated(el) {
$(el).tooltip('_fixTitle');
+
+ // update visible tooltips
+ const tooltipInstance = $(el).data('bs.tooltip');
+ const tip = tooltipInstance.getTipElement();
+ tooltipInstance.setElementContent(
+ $(tip.querySelectorAll('.tooltip-inner')),
+ tooltipInstance.getTitle(),
+ );
},
unbind(el) {
diff --git a/app/assets/javascripts/vue_shared/mixins/ci_pagination_api_mixin.js b/app/assets/javascripts/vue_shared/mixins/ci_pagination_api_mixin.js
index f94cc670edf..f9e3f3df0cc 100644
--- a/app/assets/javascripts/vue_shared/mixins/ci_pagination_api_mixin.js
+++ b/app/assets/javascripts/vue_shared/mixins/ci_pagination_api_mixin.js
@@ -4,10 +4,7 @@
*
* Components need to have `scope`, `page` and `requestData`
*/
-import {
- historyPushState,
- buildUrlWithCurrentLocation,
-} from '../../lib/utils/common_utils';
+import { historyPushState, buildUrlWithCurrentLocation } from '../../lib/utils/common_utils';
export default {
methods: {
@@ -17,19 +14,28 @@ export default {
onChangePage(page) {
/* URLS parameters are strings, we need to parse to match types */
- this.updateContent({ scope: this.scope, page: Number(page).toString() });
+ const params = {
+ page: Number(page).toString(),
+ };
+
+ if (this.scope) {
+ params.scope = this.scope;
+ }
+ this.updateContent(params);
},
updateInternalState(parameters) {
// stop polling
this.poll.stop();
- const queryString = Object.keys(parameters).map((parameter) => {
- const value = parameters[parameter];
- // update internal state for UI
- this[parameter] = value;
- return `${parameter}=${encodeURIComponent(value)}`;
- }).join('&');
+ const queryString = Object.keys(parameters)
+ .map(parameter => {
+ const value = parameters[parameter];
+ // update internal state for UI
+ this[parameter] = value;
+ return `${parameter}=${encodeURIComponent(value)}`;
+ })
+ .join('&');
// update polling parameters
this.requestData = parameters;
diff --git a/app/assets/javascripts/vue_shared/models/label.js b/app/assets/javascripts/vue_shared/models/label.js
index d29c7fe973a..2d2732d0661 100644
--- a/app/assets/javascripts/vue_shared/models/label.js
+++ b/app/assets/javascripts/vue_shared/models/label.js
@@ -6,7 +6,7 @@ export default class ListLabel {
this.color = obj.color;
this.textColor = obj.text_color;
this.description = obj.description;
- this.priority = (obj.priority !== null) ? obj.priority : Infinity;
+ this.priority = obj.priority !== null ? obj.priority : Infinity;
}
}
diff --git a/app/assets/javascripts/vue_shared/translate.js b/app/assets/javascripts/vue_shared/translate.js
index 48c63373b77..e0baf03acc3 100644
--- a/app/assets/javascripts/vue_shared/translate.js
+++ b/app/assets/javascripts/vue_shared/translate.js
@@ -1,11 +1,6 @@
-import {
- __,
- n__,
- s__,
- sprintf,
-} from '../locale';
+import { __, n__, s__, sprintf } from '../locale';
-export default (Vue) => {
+export default Vue => {
Vue.mixin({
methods: {
/**
diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
index 73b9131e5ba..754025207c8 100644
--- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
+++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
@@ -21,14 +21,14 @@ Vue.http.interceptors.push((request, next) => {
Vue.http.interceptors.push((request, next) => {
request.headers.set(csrf.headerKey, csrf.token);
- next((response) => {
+ next(response => {
// Headers object has a `forEach` property that iterates through all values.
const headers = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
-
+ // eslint-disable-next-line no-param-reassign
response.headers = headers;
});
});
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index 0138c9be803..e98c4d7bf7a 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, max-len, class-methods-use-this */
+/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, class-methods-use-this */
// Zen Mode (full screen) textarea
//
@@ -47,16 +47,26 @@ export default class ZenMode {
e.preventDefault();
return $(e.currentTarget).trigger('zen_mode:leave');
});
- $(document).on('zen_mode:enter', (function(_this) {
- return function(e) {
- return _this.enter($(e.target).closest('.md-area').find('.zen-backdrop'));
- };
- })(this));
- $(document).on('zen_mode:leave', (function(_this) {
- return function(e) {
- return _this.exit();
- };
- })(this));
+ $(document).on(
+ 'zen_mode:enter',
+ (function(_this) {
+ return function(e) {
+ return _this.enter(
+ $(e.target)
+ .closest('.md-area')
+ .find('.zen-backdrop'),
+ );
+ };
+ })(this),
+ );
+ $(document).on(
+ 'zen_mode:leave',
+ (function(_this) {
+ return function(e) {
+ return _this.exit();
+ };
+ })(this),
+ );
$(document).on('keydown', function(e) {
// Esc
if (e.keyCode === 27) {
@@ -93,7 +103,7 @@ export default class ZenMode {
scrollTo(zen_area) {
return $.scrollTo(zen_area, 0, {
- offset: -150
+ offset: -150,
});
}
}
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index f2950308019..bd1cca69c03 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -5,7 +5,6 @@
*= require jquery.atwho
*= require select2
*= require_self
- *= require dropzone/basic
*= require cropper.css
*/
@@ -18,6 +17,7 @@
*/
@import "../../../node_modules/pikaday/scss/pikaday";
+@import "../../../node_modules/dropzone/dist/basic";
/*
* GitLab UI framework
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index 056d4b7207a..1e00aa4ff7e 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -4,11 +4,11 @@
$text-color: $gl-text-color;
-$brand-primary: $gl-primary;
-$brand-success: $gl-success;
-$brand-info: $gl-info;
-$brand-warning: $gl-warning;
-$brand-danger: $gl-danger;
+$brand-primary: $blue-500;
+$brand-success: $green-500;
+$brand-info: $blue-500;
+$brand-warning: $orange-500;
+$brand-danger: $red-500;
$border-radius-base: 3px !default;
@@ -85,7 +85,7 @@ strong {
}
a {
- color: $gl-link-color;
+ color: $blue-600;
}
hr {
@@ -93,7 +93,6 @@ hr {
}
.form-group.row .col-form-label {
- padding-top: 0;
// Bootstrap 4 aligns labels to the left
// for horizontal forms
@include media-breakpoint-up(md) {
@@ -239,10 +238,6 @@ h3.popover-header {
}
.card {
- .card-title {
- margin-bottom: 0;
- }
-
&.card-without-border {
@extend .border-0;
}
@@ -256,13 +251,6 @@ h3.popover-header {
}
}
-.card-header {
- h3.card-title,
- h4.card-title {
- margin-top: 0;
- }
-}
-
.nav-tabs {
// Override bootstrap's default border
border-bottom: 0;
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index b1a20c06910..4041f2b4479 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -27,7 +27,6 @@
@import 'framework/header';
@import 'framework/highlight';
@import 'framework/issue_box';
-@import 'framework/jquery';
@import 'framework/lists';
@import 'framework/logo';
@import 'framework/markdown_area';
@@ -52,6 +51,7 @@
@import 'framework/blank';
@import 'framework/wells';
@import 'framework/page_header';
+@import 'framework/page_title';
@import 'framework/awards';
@import 'framework/images';
@import 'framework/broadcast_messages';
@@ -64,3 +64,4 @@
@import 'framework/ci_variable_list';
@import 'framework/feature_highlight';
@import 'framework/terms';
+@import 'framework/read_more';
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index 4c7c399a3ca..fcf282a7d7c 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -8,7 +8,7 @@
float: left;
margin-right: 15px;
border-radius: $avatar-radius;
- border: 1px solid $avatar-border;
+ border: 1px solid $gray-normal;
&.s16 { @include avatar-size(16px, 6px); }
&.s18 { @include avatar-size(18px, 6px); }
&.s19 { @include avatar-size(19px, 6px); }
@@ -36,7 +36,7 @@
width: 40px;
height: 40px;
padding: 0;
- background: $avatar-background;
+ background: $gray-lightest;
overflow: hidden;
&.avatar-inline {
@@ -62,15 +62,15 @@
}
&:not([href]):hover {
- border-color: darken($avatar-border, 10%);
+ border-color: darken($gray-normal, 10%);
}
}
.identicon {
text-align: center;
vertical-align: top;
- color: $identicon-fg-color;
- background-color: $identicon-gray;
+ color: $gl-gray-700;
+ background-color: $gray-darker;
// Sizes
&.s16 { font-size: 12px; line-height: 1.33; }
@@ -94,7 +94,7 @@
&.bg4 { background-color: $identicon-blue; }
&.bg5 { background-color: $identicon-teal; }
&.bg6 { background-color: $identicon-orange; }
- &.bg7 { background-color: $identicon-gray; }
+ &.bg7 { background-color: $gray-darker; }
}
.avatar-container {
@@ -104,6 +104,7 @@
a {
width: 100%;
+ height: 100%;
display: flex;
}
@@ -122,7 +123,7 @@
.avatar-counter {
background-color: $gray-darkest;
color: $white-light;
- border: 1px solid $avatar-border;
+ border: 1px solid $gray-normal;
border-radius: 1em;
font-family: $regular-font;
font-size: 9px;
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index 8d11b92cf88..7a95db5976d 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -141,17 +141,14 @@
&:hover,
&:active,
&.is-active {
- background-color: $row-hover;
- border-color: $row-hover-border;
+ background-color: $blue-50;
+ border-color: $blue-200;
box-shadow: none;
outline: 0;
.award-control-icon svg {
background: $award-emoji-positive-add-bg;
-
- path {
- fill: $award-emoji-positive-add-lines;
- }
+ fill: $award-emoji-positive-add-lines;
}
.award-control-icon-neutral {
@@ -229,8 +226,8 @@
svg {
margin-bottom: 1px;
- height: 18px;
- width: 18px;
+ height: $default-icon-size;
+ width: $default-icon-size;
border-radius: 50%;
path {
diff --git a/app/assets/stylesheets/framework/badges.scss b/app/assets/stylesheets/framework/badges.scss
index 57df9b969c3..c6060161dec 100644
--- a/app/assets/stylesheets/framework/badges.scss
+++ b/app/assets/stylesheets/framework/badges.scss
@@ -1,6 +1,6 @@
.badge.badge-pill {
font-weight: $gl-font-weight-normal;
background-color: $badge-bg;
- color: $badge-color;
+ color: $gl-text-color-secondary;
vertical-align: baseline;
}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 7145a76db6d..43b7c26b272 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -283,18 +283,20 @@
.dismiss-button {
position: absolute;
- right: 6px;
- top: 6px;
+ right: $gl-padding-8;
+ top: $gl-padding-8;
cursor: pointer;
- color: $blue-300;
+ color: $blue-500;
z-index: 1;
border: 0;
background-color: transparent;
+ padding: $gl-padding-8;
+ line-height: 0;
&:hover,
&:focus {
border: 0;
- color: $blue-400;
+ color: $blue-700;
}
}
@@ -346,6 +348,7 @@
@include media-breakpoint-down(xs) {
width: 100%;
+ margin: $btn-side-margin 0;
}
}
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index ea4798fcefd..219fd99b097 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -147,17 +147,12 @@
}
&.btn-success,
- &.btn-new,
- &.btn-create,
- &.btn-save {
+ &.btn-register {
@include btn-green;
}
&.btn-inverted {
- &.btn-success,
- &.btn-new,
- &.btn-create,
- &.btn-save {
+ &.btn-success {
@include btn-outline($white-light, $green-600, $green-500, $green-500, $white-light, $green-600, $green-600, $green-700);
}
@@ -165,6 +160,10 @@
@include btn-outline($white-light, $red-500, $red-500, $red-500, $white-light, $red-600, $red-600, $red-700);
}
+ &.btn-warning {
+ @include btn-outline($white-light, $orange-500, $orange-500, $orange-500, $white-light, $orange-600, $orange-600, $orange-700);
+ }
+
&.btn-primary,
&.btn-info {
@include btn-outline($white-light, $blue-500, $blue-500, $blue-500, $white-light, $blue-600, $blue-600, $blue-700);
@@ -172,8 +171,7 @@
}
&.btn-info,
- &.btn-primary,
- &.btn-register {
+ &.btn-primary {
@include btn-blue;
}
@@ -209,6 +207,10 @@
@include btn-with-margin;
}
+ &.btn-icon {
+ color: $gl-gray-700;
+ }
+
.fa-caret-down,
.fa-chevron-down {
margin-left: 5px;
@@ -220,6 +222,25 @@
}
}
+ &.btn-text-field {
+ width: 100%;
+ text-align: left;
+ padding: 6px 16px;
+ border-color: $border-color;
+ color: $gray-darkest;
+ background-color: $gray-light;
+
+ &:hover,
+ &:active,
+ &:focus {
+ cursor: text;
+ box-shadow: none;
+ border-color: lighten($blue-300, 20%);
+ color: $gray-darkest;
+ background-color: $gray-light;
+ }
+ }
+
&.dot-highlight::after {
content: '';
background-color: $blue-500;
@@ -248,7 +269,7 @@
.btn-terminal {
svg {
height: 14px;
- width: 18px;
+ width: $default-icon-size;
}
}
@@ -337,35 +358,20 @@
}
}
-.btn-text-field {
- width: 100%;
- text-align: left;
- padding: 6px 16px;
- border-color: $border-color;
- color: $gray-darkest;
- background-color: $gray-light;
-
- &:hover,
- &:active,
- &:focus {
- cursor: text;
- box-shadow: none;
- border-color: lighten($blue-300, 20%);
- color: $gray-darkest;
- background-color: $gray-light;
- }
-}
-
.btn-build {
margin-left: 10px;
i {
color: $gl-text-color-secondary;
}
+
+ svg {
+ fill: $gl-text-color-secondary;
+ }
}
.clone-dropdown-btn a {
- color: $dropdown-link-color;
+ color: $gl-gray-700;
&:hover {
text-decoration: none;
@@ -434,7 +440,7 @@
&:hover,
&:active,
&:focus {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: none;
}
}
@@ -445,21 +451,21 @@
&:hover,
&:active,
&:focus {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: none;
}
}
}
.btn-missing {
- color: $notes-light-color;
+ color: $gl-text-color-secondary;
border: 1px dashed $border-gray-normal-dashed;
border-radius: $border-radius-default;
&:hover,
&:active,
&:focus {
- color: $notes-light-color;
+ color: $gl-text-color-secondary;
background-color: $white-normal;
}
}
diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index 0b9dff64b0b..9638fee6078 100644
--- a/app/assets/stylesheets/framework/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
@@ -1,8 +1,7 @@
-.calender-block {
+.calendar-block {
padding-left: 0;
padding-right: 0;
border-top: 0;
- direction: rtl;
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
overflow-x: auto;
@@ -42,10 +41,13 @@
}
.calendar-hint {
- margin-top: -23px;
- float: right;
font-size: 12px;
- direction: ltr;
+
+ &.bottom-right {
+ direction: ltr;
+ margin-top: -23px;
+ float: right;
+ }
}
.pika-single.gitlab-theme {
diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss
index 1bd94c0acba..bdd7f09d926 100644
--- a/app/assets/stylesheets/framework/callout.scss
+++ b/app/assets/stylesheets/framework/callout.scss
@@ -25,25 +25,25 @@
/* Variations */
.bs-callout-danger {
- background-color: $callout-danger-bg;
- border-color: $callout-danger-border;
- color: $callout-danger-color;
+ background-color: $red-100;
+ border-color: $red-200;
+ color: $red-700;
}
.bs-callout-warning {
- background-color: $callout-warning-bg;
- border-color: $callout-warning-border;
- color: $callout-warning-color;
+ background-color: $orange-100;
+ border-color: $orange-200;
+ color: $orange-700;
}
.bs-callout-info {
- background-color: $callout-info-bg;
- border-color: $callout-info-border;
- color: $callout-info-color;
+ background-color: $blue-100;
+ border-color: $blue-200;
+ color: $blue-700;
}
.bs-callout-success {
- background-color: $callout-success-bg;
- border-color: $callout-success-border;
- color: $callout-success-color;
+ background-color: $green-100;
+ border-color: $green-200;
+ color: $green-700;
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index af17210f341..626c8f92d1d 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -1,8 +1,8 @@
/** COLORS **/
-.cgray { color: $common-gray; }
+.cgray { color: $gl-text-color; }
.clgray { color: $common-gray-light; }
-.cred { color: $common-red; }
-.cgreen { color: $common-green; }
+.cred { color: $red-500; }
+.cgreen { color: $green-600; }
.cdark { color: $common-gray-dark; }
.text-plain,
@@ -33,6 +33,11 @@
color: $brand-danger;
}
+.text-danger-muted,
+.text-danger-muted:hover {
+ color: $red-300;
+}
+
.text-warning,
.text-warning:hover {
color: $brand-warning;
@@ -43,11 +48,11 @@
color: $brand-info;
}
-.hint { font-style: italic; color: $hint-color; }
-.light { color: $common-gray; }
+.hint { font-style: italic; color: $gl-gray-400; }
+.light { color: $gl-text-color; }
.slead {
- color: $common-gray;
+ color: $gl-text-color;
font-size: 14px;
margin-bottom: 12px;
font-weight: $gl-font-weight-normal;
@@ -70,13 +75,6 @@ pre {
padding: 0;
}
- &.card.card-body-pre {
- border: 1px solid $well-pre-bg;
- background: $gray-light;
- border-radius: 0;
- color: $well-pre-color;
- }
-
&.wrap {
word-break: break-word;
white-space: pre-wrap;
@@ -114,11 +112,11 @@ hr {
.item-title { font-weight: $gl-font-weight-bold; }
.author-link {
- color: $gl-link-color;
+ color: $blue-600;
}
-.back-link {
- font-size: 14px;
+.author-link:hover {
+ text-decoration: none;
}
table {
@@ -127,39 +125,18 @@ table {
top: -2px;
margin-right: 3px;
}
-
- td.permission-x {
- background: $table-permission-x-bg !important;
- text-align: center;
- }
}
.loading {
margin: 20px auto;
height: 40px;
- color: $loading-color;
+ color: $gl-gray-700;
font-size: 32px;
text-align: center;
}
-span.update-author {
- display: block;
- color: $update-author-color;
- font-weight: $gl-font-weight-normal;
- font-style: italic;
-
- strong {
- font-weight: $gl-font-weight-bold;
- font-style: normal;
- }
-}
-
-.field_with_errors {
- display: inline;
-}
-
p.time {
- color: $time-color;
+ color: $gl-gray-400;
font-size: 90%;
margin: 30px 3px 3px 2px;
}
@@ -193,43 +170,14 @@ li.note {
background-color: inherit;
}
-.project_member_show {
- td:first-child {
- color: $project-member-show-color;
- }
-}
-
-.rss-icon {
- img {
- width: 24px;
- vertical-align: top;
- }
-
- strong {
- line-height: 24px;
- }
-}
-
.show-suppressed-diff,
.show-all-commits {
cursor: pointer;
}
-.git_error_tips {
- @extend .col-lg-6;
- text-align: left;
- margin-top: 40px;
-
- pre {
- background: $white-light;
- border: 0;
- font-size: 12px;
- }
-}
-
.error-message {
padding: 10px;
- background: $error-bg;
+ background: $red-400;
margin: 0;
color: $white-light;
@@ -240,11 +188,11 @@ li.note {
}
.warning_message {
- border-left: 4px solid $warning-message-border;
- color: $warning-message-color;
+ border-left: 4px solid $orange-200;
+ color: $orange-700;
padding: 10px;
margin-bottom: 10px;
- background: $warning-message-bg;
+ background: $orange-100;
padding-left: 20px;
&.centered {
@@ -254,7 +202,7 @@ li.note {
.gitlab-promo {
a {
- color: $gl-promo-color;
+ color: $gl-gray-350;
margin-right: 30px;
}
}
@@ -267,19 +215,6 @@ li.note {
}
}
-.control-group {
- .controls {
- span {
- &.descr {
- position: relative;
- top: 2px;
- left: 5px;
- color: $control-group-descr-color;
- }
- }
- }
-}
-
img.emoji {
height: 20px;
vertical-align: top;
@@ -298,12 +233,6 @@ img.emoji {
margin-bottom: 10px;
}
-.side-filters {
- fieldset {
- margin-bottom: 15px;
- }
-}
-
.footer-links {
margin-bottom: 20px;
@@ -325,39 +254,11 @@ img.emoji {
text-align: center;
}
-.header-with-avatar {
- h3 {
- margin: 0;
- font-weight: $gl-font-weight-bold;
- }
-
- .username {
- font-size: 18px;
- color: $username-color;
- margin-top: 8px;
- }
-
- .description {
- font-size: $gl-font-size;
- color: $description-color;
- margin-top: 8px;
- }
-}
-
-.profiler-results {
- top: 73px !important;
-
- .profiler-button,
- .profiler-controls {
- border-color: $profiler-border !important;
- }
-}
-
.dropzone .dz-preview .dz-progress {
border-color: $border-color !important;
.dz-upload {
- background: $gl-success !important;
+ background: $green-500 !important;
}
}
@@ -391,16 +292,6 @@ img.emoji {
}
}
-.content-separator {
- margin-left: -$gl-padding;
- margin-right: -$gl-padding;
- border-top: 1px solid $border-color;
-}
-
-.hide-bottom-border {
- border-bottom: 0 !important;
-}
-
.gl-accessibility {
&:focus {
display: flex;
@@ -438,9 +329,28 @@ img.emoji {
word-wrap: break-word;
}
+.checkbox-icon-inline-wrapper {
+ .checkbox {
+ display: inline;
+
+ label {
+ display: inline;
+ }
+ }
+}
+
+.outline-0 {
+ outline: 0;
+
+ &:focus {
+ outline: 0;
+ }
+}
+
/** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; }
.prepend-top-2 { margin-top: 2px; }
+.prepend-top-4 { margin-top: $gl-padding-4; }
.prepend-top-5 { margin-top: 5px; }
.prepend-top-8 { margin-top: $grid-size; }
.prepend-top-10 { margin-top: 10px; }
@@ -461,6 +371,7 @@ img.emoji {
.append-right-default { margin-right: $gl-padding; }
.append-right-20 { margin-right: 20px; }
.append-bottom-0 { margin-bottom: 0; }
+.append-bottom-4 { margin-bottom: $gl-padding-4; }
.append-bottom-5 { margin-bottom: 5px; }
.append-bottom-8 { margin-bottom: $grid-size; }
.append-bottom-10 { margin-bottom: 10px; }
@@ -473,3 +384,5 @@ img.emoji {
.flex-align-self-center { align-self: center; }
.flex-grow { flex-grow: 1; }
.flex-no-shrink { flex-shrink: 0; }
+.mw-460 { max-width: 460px; }
+.ws-initial { white-space: initial; }
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index e2bbcc67a67..6f103e4e89a 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -9,8 +9,7 @@
padding-left: $contextual-sidebar-width;
}
- .issues-bulk-update.right-sidebar.right-sidebar-expanded
- .issuable-sidebar-header {
+ .issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header {
padding: 10px 0 15px;
}
}
@@ -75,7 +74,7 @@
.nav-sidebar {
transition: width $sidebar-transition-duration, left $sidebar-transition-duration;
position: fixed;
- z-index: 400;
+ z-index: 600;
width: $contextual-sidebar-width;
top: $header-height;
bottom: 0;
@@ -86,8 +85,7 @@
&:not(.sidebar-collapsed-desktop) {
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
- box-shadow: inset -1px 0 0 $border-color,
- 2px 1px 3px $dropdown-shadow-color;
+ box-shadow: inset -1px 0 0 $border-color, 2px 1px 3px $dropdown-shadow-color;
}
}
@@ -113,7 +111,7 @@
}
.avatar-container {
- margin-right: 0;
+ margin: 0 auto;
}
}
@@ -324,15 +322,15 @@
width: $contextual-sidebar-width - 1px;
transition: width $sidebar-transition-duration;
position: fixed;
+ height: $toggle-sidebar-height;
bottom: 0;
- padding: $gl-padding;
+ padding: 0 $gl-padding;
background-color: $gray-light;
border: 0;
border-top: 1px solid $border-color;
color: $gl-text-color-secondary;
display: flex;
align-items: center;
- line-height: 1;
svg {
margin-right: 8px;
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 83bc3776178..dca89981d81 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -144,14 +144,21 @@
top: 11px;
right: 8px;
}
+
+ .ic-chevron-down {
+ position: absolute;
+ top: $gl-padding-8;
+ right: $gl-padding-8;
+ color: $gray-darkest;
+ }
}
@mixin dropdown-item-hover {
- background-color: $dropdown-item-hover-bg;
+ background-color: $gray-darker;
color: $gl-text-color;
outline: 0;
- // make sure the text color is not overriden
+ // make sure the text color is not overridden
&.text-danger {
color: $brand-danger;
}
@@ -177,7 +184,7 @@
text-align: left;
width: 100%;
- // make sure the text color is not overriden
+ // make sure the text color is not overridden
&.text-danger {
color: $brand-danger;
}
@@ -195,7 +202,7 @@
text-decoration: none;
.badge.badge-pill {
- background-color: darken($dropdown-link-hover-bg, 5%);
+ background-color: darken($blue-50, 5%);
}
}
@@ -233,7 +240,7 @@
font-weight: $gl-font-weight-normal;
padding: 8px 0;
background-color: $white-light;
- border: 1px solid $dropdown-border-color;
+ border: 1px solid $border-color;
border-radius: $border-radius-base;
box-shadow: 0 2px 4px $dropdown-shadow-color;
@@ -561,6 +568,10 @@
top: -1px;
}
+.dropdown-menu-close-icon {
+ vertical-align: middle;
+}
+
.dropdown-menu-back {
left: 7px;
top: 2px;
@@ -572,9 +583,10 @@
padding: 0 10px;
.fa,
- .input-icon {
+ .input-icon,
+ .ic-search {
position: absolute;
- top: 10px;
+ top: $gl-padding-8;
right: 20px;
color: $dropdown-input-fa-color;
font-size: 12px;
@@ -607,25 +619,25 @@
width: 100%;
min-height: 30px;
padding: 0 7px;
- color: $dropdown-input-color;
+ color: $gl-gray-700;
line-height: 30px;
border: 1px solid $dropdown-divider-color;
border-radius: 2px;
outline: 0;
&:focus {
- color: $dropdown-link-color;
+ color: $gl-gray-700;
border-color: $blue-300;
box-shadow: 0 0 4px $dropdown-input-focus-shadow;
~ .fa {
- color: $dropdown-link-color;
+ color: $gl-gray-700;
}
}
&:hover {
~ .fa {
- color: $dropdown-link-color;
+ color: $gl-gray-700;
}
}
}
@@ -874,7 +886,7 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu {
overflow-y: auto;
li.section-empty.section-failure {
- color: $callout-danger-color;
+ color: $red-700;
}
.frequent-items-list-item-container a {
@@ -890,7 +902,7 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu {
position: absolute;
top: 13px;
right: 25px;
- color: $md-area-border;
+ color: $gray-100;
}
}
@@ -929,7 +941,7 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu {
&:hover {
.frequent-items-item-avatar-container .avatar {
- border-color: $md-area-border;
+ border-color: $gray-100;
}
}
diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index a8ec1e1145a..be85e03430e 100644
--- a/app/assets/stylesheets/framework/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -3,6 +3,16 @@ gl-emoji {
display: inline-flex;
vertical-align: middle;
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
- font-size: 1.5em;
- line-height: 0.9;
+ font-size: 1.4em;
+ line-height: 1em;
+}
+
+.user-status-emoji {
+ margin-right: $gl-padding-4;
+
+ gl-emoji {
+ font-size: 1em;
+ line-height: 16px;
+ vertical-align: baseline;
+ }
}
diff --git a/app/assets/stylesheets/framework/feature_highlight.scss b/app/assets/stylesheets/framework/feature_highlight.scss
index cad915bc86f..85cabf43e9e 100644
--- a/app/assets/stylesheets/framework/feature_highlight.scss
+++ b/app/assets/stylesheets/framework/feature_highlight.scss
@@ -72,11 +72,11 @@
.feature-highlight-popover {
width: 240px;
padding: 0;
- border: 1px solid $dropdown-border-color;
+ border: 1px solid $border-color;
box-shadow: 0 2px 4px $dropdown-shadow-color;
&.right > .arrow {
- border-right-color: $dropdown-border-color;
+ border-right-color: $border-color;
}
.popover-body {
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 00eac1688f2..037a5adfb7e 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -36,7 +36,6 @@
text-align: left;
padding: 10px $gl-padding;
word-wrap: break-word;
- border-radius: $border-radius-default $border-radius-default 0 0;
&.file-title-clear {
padding-left: 0;
@@ -112,7 +111,7 @@
&.image_file,
&.video {
- background: $file-image-bg;
+ background: $gray-darker;
text-align: center;
padding: 30px;
@@ -131,7 +130,7 @@
}
&.blob-no-preview {
- background: $blob-bg;
+ background: $gray-darker;
text-shadow: 0 1px 2px $white-light;
padding: 100px 0;
}
@@ -146,7 +145,7 @@
}
tr {
- border-bottom: 1px solid $blame-border;
+ border-bottom: 1px solid $gray-darker;
&:last-child {
border-bottom: 0;
@@ -184,7 +183,7 @@
&.line-numbers {
float: none;
- border-left: 1px solid $blame-line-numbers-border;
+ border-left: 1px solid $gl-gray-100;
i {
float: none;
@@ -211,7 +210,7 @@
}
&.logs {
- background: $logs-bg;
+ background: $gray-darker;
max-height: 700px;
overflow-y: auto;
@@ -233,7 +232,7 @@
}
&:hover {
- background: $row-hover;
+ background: $blue-50;
}
}
}
@@ -286,19 +285,19 @@ span.idiff {
.new-file {
a {
- color: $gl-text-green;
+ color: $green-600;
}
}
.renamed-file {
a {
- color: $gl-text-orange;
+ color: $orange-600;
}
}
.deleted-file {
a {
- color: $gl-text-red;
+ color: $red-500;
}
}
@@ -312,11 +311,11 @@ span.idiff {
text-decoration: none;
.new-file {
- color: $notify-new-file;
+ color: $green-600;
}
.deleted-file {
- color: $notify-deleted-file;
+ color: $red-700;
}
}
}
@@ -416,7 +415,6 @@ span.idiff {
}
.preview-container {
- height: 100%;
overflow: auto;
.file-container {
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 9b09ed0ed0a..d5693a5d1a1 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -92,8 +92,8 @@
display: -webkit-flex;
display: flex;
flex-shrink: 0;
- margin-top: 5px;
- margin-bottom: 5px;
+ margin-top: 4px;
+ margin-bottom: 4px;
.selectable {
display: -webkit-flex;
@@ -206,7 +206,7 @@
&.focus,
&.focus:hover {
border-color: $blue-300;
- box-shadow: 0 0 4px $search-input-focus-shadow-color;
+ box-shadow: 0 0 4px $dropdown-input-focus-shadow;
}
gl-emoji {
@@ -216,8 +216,8 @@
vertical-align: inherit;
img {
- height: 18px;
- width: 18px;
+ height: $default-icon-size;
+ width: $default-icon-size;
}
}
@@ -389,9 +389,8 @@
.btn {
text-overflow: ellipsis;
- .fa {
- width: 15px;
- line-height: $line-height-base;
+ svg {
+ margin-right: $gl-padding-8;
}
.dropdown-label-box {
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index e4bcb92876d..7a4c3914fb0 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -16,10 +16,10 @@
color: $gl-text-color;
a {
- color: $gl-link-color;
+ color: $blue-600;
&:hover {
- color: $gl-link-hover-color;
+ color: $blue-800;
text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 437fcff5c62..afd888af672 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -8,7 +8,7 @@ input {
input[type='text'].danger {
background: $input-danger-bg !important;
- border-color: $input-danger-border;
+ border-color: $red-400;
text-shadow: 0 1px 1px $white-light;
}
@@ -170,7 +170,7 @@ label {
}
.form-control::-webkit-input-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
.input-group {
diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
index d2ba76f5160..50d4298d418 100644
--- a/app/assets/stylesheets/framework/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -11,6 +11,10 @@
padding: 0 2px;
background-color: $blue-100;
border-radius: $border-radius-default;
+
+ &.current-user {
+ background-color: $orange-100;
+ }
}
.gfm-color_chip {
diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss
index 50ebc6d0dd1..b8bb9e1e07b 100644
--- a/app/assets/stylesheets/framework/gitlab_theme.scss
+++ b/app/assets/stylesheets/framework/gitlab_theme.scss
@@ -161,6 +161,7 @@
.nav-links li {
&.active a,
+ &.md-header-tab.active button,
a.active {
border-bottom: 2px solid $active-tab-border;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index e7e13d35d8e..d1ce3a582bb 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -529,9 +529,7 @@
}
.header-user {
- .dropdown-menu {
- width: auto;
- min-width: unset;
+ &.show .dropdown-menu {
margin-top: 4px;
color: $gl-text-color;
left: auto;
@@ -542,6 +540,22 @@
.user-name {
display: block;
}
+
+ .user-status {
+ margin-right: 0;
+ max-width: 240px;
+ font-size: $gl-font-size-small;
+
+ gl-emoji {
+ font-size: $gl-font-size-small;
+ }
+
+ .user-status-emoji {
+ gl-emoji {
+ font-size: $gl-font-size;
+ }
+ }
+ }
}
svg {
@@ -554,7 +568,7 @@
float: left;
margin-right: 5px;
border-radius: 50%;
- border: 1px solid $avatar-border;
+ border: 1px solid $gray-normal;
}
.with-performance-bar .navbar-gitlab {
@@ -573,3 +587,24 @@
}
}
}
+
+.set-user-status-modal {
+ .modal-body {
+ min-height: unset;
+ }
+
+ .input-lg {
+ max-width: unset;
+ }
+
+ .no-emoji-placeholder,
+ .clear-user-status {
+ svg {
+ fill: $gl-text-color-secondary;
+ }
+ }
+
+ .emoji-menu-toggle-button {
+ @include emoji-menu-toggle-button;
+ }
+}
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index d1f7ff4438b..abd26e38d18 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -11,7 +11,7 @@
.ci-status-icon-failed {
svg {
- fill: $gl-danger;
+ fill: $red-500;
}
&.add-border {
@@ -64,6 +64,7 @@
}
}
+.ci-status-icon-scheduled,
.ci-status-icon-manual {
svg {
fill: $gl-text-color;
diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss
index f878ec1ca91..a20920e2503 100644
--- a/app/assets/stylesheets/framework/images.scss
+++ b/app/assets/stylesheets/framework/images.scss
@@ -25,7 +25,7 @@
&.svg-#{$width} {
img,
svg {
- width: #{$width + 'px'};
+ max-width: #{$width + 'px'};
}
}
}
@@ -39,7 +39,7 @@
svg {
fill: currentColor;
- $svg-sizes: 8 10 12 16 18 24 32 48 72;
+ $svg-sizes: 8 10 12 14 16 18 24 32 48 72;
@each $svg-size in $svg-sizes {
&.s#{$svg-size} {
@include svg-size(#{$svg-size}px);
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 86de88729ee..2d672e62e08 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -26,12 +26,12 @@
&.status-box-closed,
&.status-box-mr-closed {
- background-color: $gl-danger;
+ background-color: $red-500;
}
&.status-box-issue-closed,
&.status-box-mr-merged {
- background-color: $gl-primary;
+ background-color: $blue-500;
}
&.status-box-open {
@@ -39,7 +39,7 @@
}
&.status-box-expired {
- background-color: $issue-status-expired;
+ background-color: $orange-500;
}
&.status-box-upcoming {
diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss
deleted file mode 100644
index d1360a0c0eb..00000000000
--- a/app/assets/stylesheets/framework/jquery.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.ui-widget {
- font-family: $regular-font;
- font-size: $font-size-base;
-
- .ui-state-default {
- border: 1px solid $white-light;
- background: $white-light;
- color: $jq-ui-default-color;
- }
-
- .ui-state-highlight {
- border: 0;
- background: transparent;
- }
-}
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index 52b5f059f20..9218df9b40f 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -69,10 +69,14 @@ body {
float: right;
}
- /* Center alert text and alert action links on smaller screens */
- @include media-breakpoint-down(sm) {
- .alert {
- text-align: center;
+ .flex-alert {
+ @include media-breakpoint-up(lg) {
+ display: flex;
+
+ .alert-message {
+ flex: 1;
+ padding-right: 40px;
+ }
}
.alert-link-group {
@@ -80,6 +84,13 @@ body {
}
}
+ @include media-breakpoint-down(sm) {
+ .alert-link-group {
+ float: none;
+ margin-top: $gl-padding-8;
+ }
+ }
+
/* Stripe the background colors so that adjacent alert-warnings are distinct from one another */
.alert-warning {
transition: background-color 0.15s, border-color 0.15s;
@@ -111,3 +122,42 @@ body {
.with-performance-bar .layout-page {
margin-top: $header-height + $performance-bar-height;
}
+
+.fullscreen-layout {
+ padding-top: 0;
+ height: 100vh;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ overflow: hidden;
+
+ > #js-peek,
+ > .navbar-gitlab {
+ position: static;
+ top: auto;
+ }
+
+ .flash-container {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+
+ .alert-wrapper .flash-container .flash-alert:last-child,
+ .alert-wrapper .flash-container .flash-notice:last-child {
+ margin-bottom: 0;
+ }
+
+ .content-wrapper {
+ margin-top: 0;
+ padding-bottom: 0;
+ flex: 1;
+ min-height: 0;
+ }
+
+ &.flash-shown {
+ .content-wrapper {
+ margin-top: 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 4b67eab05b3..d9d4a210f5f 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -21,11 +21,11 @@
}
&.disabled {
- color: $list-text-disabled-color;
+ color: $gl-text-color-tertiary;
}
&:not(.ui-sort-disabled):hover {
- background: $row-hover;
+ background: $blue-50;
}
&.unstyled {
@@ -35,12 +35,12 @@
}
&.warning-row {
- background-color: $list-warning-row-bg;
- border-color: $list-warning-row-border;
- color: $list-warning-row-color;
+ background-color: $orange-100;
+ border-color: $orange-200;
+ color: $orange-700;
&:hover {
- background: $list-warning-row-bg;
+ background: $orange-100;
}
}
@@ -73,7 +73,7 @@
}
.card.card-body-title {
- font-size: $list-font-size;
+ font-size: $gl-font-size;
line-height: 18px;
}
}
@@ -109,8 +109,9 @@ ul.content-list {
li {
border-color: $white-normal;
- font-size: $list-font-size;
- color: $list-text-color;
+ font-size: $gl-font-size;
+ color: $gl-text-color;
+ word-break: break-word;
&.no-description {
.title {
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 7290a174668..3142f94b192 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -72,6 +72,7 @@
.md-header-tab {
@include media-breakpoint-down(xs) {
flex: 1;
+ flex-direction: column;
width: 100%;
border-bottom: 1px solid $border-color;
text-align: center;
@@ -122,7 +123,7 @@
.markdown-area {
border-radius: 0;
background: $white-light;
- border: 1px solid $md-area-border;
+ border: 1px solid $gray-100;
min-height: 140px;
max-height: 500px;
padding: 5px;
@@ -179,7 +180,7 @@
&:hover,
&:focus {
svg {
- fill: $gl-link-color;
+ fill: $blue-600;
}
}
}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 98bf26a5222..9837b1a6bd0 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -12,6 +12,15 @@
max-width: $max-width;
}
+/**
+ * Mixin for fixed width container
+ */
+@mixin fixed-width-container {
+ max-width: $limited-layout-width - ($gl-padding * 2);
+ margin-left: auto;
+ margin-right: auto;
+}
+
/*
* Mixin for markdown tables
*/
@@ -20,20 +29,24 @@
display: inline-block;
overflow-x: auto;
border: 0;
- border-color: $md-area-border;
+ border-color: $gl-gray-100;
@supports (width: fit-content) {
display: block;
width: fit-content;
}
+ tbody {
+ background-color: $white-light;
+ }
+
tr {
th {
- border-bottom: solid 2px $md-area-border;
+ border-bottom: solid 2px $gl-gray-100;
}
td {
- border-color: $md-area-border;
+ border-color: $gl-gray-100;
}
}
}
@@ -50,7 +63,7 @@
@include clearfix;
padding: 10px 0;
- border-bottom: 1px solid $list-border-light;
+ border-bottom: 1px solid $gray-darker;
display: block;
margin: 0;
@@ -237,6 +250,101 @@
max-width: 100%;
}
+/*
+* Mixin that handles the container for the job logs (CI/CD and kubernetes pod logs)
+*/
+@mixin build-trace {
+ background: $black;
+ color: $gray-darkest;
+ white-space: pre;
+ overflow-x: auto;
+ font-size: 12px;
+ border-radius: 0;
+ border: 0;
+ padding: $grid-size;
+
+ .bash {
+ display: block;
+ }
+
+ &.build-trace-rounded {
+ border-radius: $border-radius-base;
+ }
+}
+
+@mixin build-trace-top-bar($height) {
+ height: $height;
+ min-height: $height;
+ background: $gray-light;
+ border: 1px solid $border-color;
+ color: $gl-text-color;
+ position: sticky;
+ position: -webkit-sticky;
+ top: $header-height;
+ padding: $grid-size;
+
+ .with-performance-bar & {
+ top: $header-height + $performance-bar-height;
+ }
+}
+
+/*
+* Mixin that handles the position of the controls placed on the top bar
+*/
+@mixin build-controllers($control-font-size, $flex-direction, $with-grow, $flex-grow-size, $svg-display: 'block', $svg-top: '2px') {
+ display: flex;
+ font-size: $control-font-size;
+ justify-content: $flex-direction;
+ align-items: center;
+ align-self: baseline;
+ @if $with-grow {
+ flex-grow: $flex-grow-size;
+ }
+
+ svg {
+ width: 15px;
+ height: 15px;
+ display: $svg-display;
+ fill: $gl-text-color;
+ top: $svg-top;
+ }
+
+ .controllers-buttons {
+ color: $gl-text-color;
+ margin: 0 $grid-size;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+
+ .btn-scroll.animate {
+ .first-triangle {
+ animation: blinking-scroll-button 1s ease infinite;
+ animation-delay: 0.3s;
+ }
+
+ .second-triangle {
+ animation: blinking-scroll-button 1s ease infinite;
+ animation-delay: 0.2s;
+ }
+
+ .third-triangle {
+ animation: blinking-scroll-button 1s ease infinite;
+ }
+
+ &:disabled {
+ opacity: 1;
+ }
+ }
+
+ .btn-scroll:disabled,
+ .btn-refresh:disabled {
+ opacity: 0.35;
+ cursor: not-allowed;
+ }
+}
+
@mixin build-loader-animation {
position: relative;
white-space: initial;
@@ -266,3 +374,59 @@
border-radius: 50%;
}
}
+
+@mixin emoji-menu-toggle-button {
+ line-height: 1;
+ padding: 0;
+ min-width: 16px;
+ color: $gray-darkest;
+ fill: $gray-darkest;
+
+ .fa {
+ position: relative;
+ font-size: 16px;
+ }
+
+ svg {
+ @include btn-svg;
+ margin: 0;
+ }
+
+ .award-control-icon-positive,
+ .award-control-icon-super-positive {
+ position: absolute;
+ top: 0;
+ left: 0;
+ opacity: 0;
+ }
+
+ &:hover,
+ &.is-active {
+ .danger-highlight {
+ color: $red-500;
+ }
+
+ .link-highlight {
+ color: $blue-600;
+ fill: $blue-600;
+ }
+
+ .award-control-icon-neutral {
+ opacity: 0;
+ }
+
+ .award-control-icon-positive {
+ opacity: 1;
+ }
+ }
+
+ &.is-active {
+ .award-control-icon-positive {
+ opacity: 0;
+ }
+
+ .award-control-icon-super-positive {
+ opacity: 1;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index 6244fb86fea..6d20c46b99d 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -4,11 +4,6 @@
margin-top: 20px;
}
- .container-fluid {
- padding-left: 5px;
- padding-right: 5px;
- }
-
.nav-links > li > a {
padding: 10px;
font-size: 12px;
@@ -49,12 +44,8 @@
.project-repo-buttons {
display: block;
- .count-buttons .btn {
- margin: 0 10px;
- }
-
- .count-buttons .count-with-arrow {
- display: none;
+ .count-buttons .count-badge {
+ margin-top: $gl-padding-8;
}
}
}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index 7d53a631cdf..7e30747963a 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -19,6 +19,17 @@
}
}
+ // leave enough space for the close icon
+ .modal-title {
+ &.mw-100,
+ &.w-100 {
+ // after upgrading to Bootstrap 4.2 we can use $modal-header-padding-x here
+ // https://github.com/twbs/bootstrap/pull/26976
+ margin-right: -2rem;
+ padding-right: 2rem;
+ }
+ }
+
.page-title {
margin-top: 0;
}
@@ -59,7 +70,7 @@
}
@include media-breakpoint-up(sm) {
- .btn:first-of-type {
+ .btn:nth-child(1) {
margin-left: auto;
}
}
diff --git a/app/assets/stylesheets/framework/page_title.scss b/app/assets/stylesheets/framework/page_title.scss
new file mode 100644
index 00000000000..e8302953a63
--- /dev/null
+++ b/app/assets/stylesheets/framework/page_title.scss
@@ -0,0 +1,18 @@
+.page-title-holder {
+ @extend .d-flex;
+ @extend .align-items-center;
+
+ padding-top: $gl-padding-top;
+ border-bottom: 1px solid $border-color;
+
+ .page-title {
+ margin: $gl-padding 0;
+ font-size: 1.75em;
+ font-weight: $gl-font-weight-bold;
+ color: $gl-text-color;
+ }
+
+ .page-title-controls {
+ margin-left: auto;
+ }
+}
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index 5ca4d944d73..3a117106cff 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -53,8 +53,3 @@
margin-top: $gl-padding;
}
}
-
-.card-title {
- font-size: inherit;
- line-height: inherit;
-}
diff --git a/app/assets/stylesheets/framework/read_more.scss b/app/assets/stylesheets/framework/read_more.scss
new file mode 100644
index 00000000000..b84b6e0b256
--- /dev/null
+++ b/app/assets/stylesheets/framework/read_more.scss
@@ -0,0 +1,13 @@
+.read-more-container {
+ @include media-breakpoint-down(md) {
+ &:not(.is-expanded) {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ > * {
+ display: inline;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/responsive_tables.scss b/app/assets/stylesheets/framework/responsive_tables.scss
index 764bebd82c6..29a9c076cdf 100644
--- a/app/assets/stylesheets/framework/responsive_tables.scss
+++ b/app/assets/stylesheets/framework/responsive_tables.scss
@@ -39,7 +39,7 @@
.table-section {
white-space: nowrap;
- $section-widths: 10 15 20 25 30 40 50 100;
+ $section-widths: 5 10 15 20 25 30 40 50 60 100;
@each $width in $section-widths {
&.section-#{$width} {
flex: 0 0 #{$width + '%'};
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 8bab8cf36b1..de9e7c37695 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -1,6 +1,6 @@
// For tabbed navigation links, scrolling tabs, etc. For all top/main navigation,
// please check nav.scss
-.nav-links {
+.nav-links:not(.quick-links) {
display: flex;
padding: 0;
margin: 0;
@@ -8,15 +8,17 @@
height: auto;
border-bottom: 1px solid $border-color;
- li {
+ li:not(.md-header-toolbar) {
display: flex;
- a {
+ a,
+ button {
padding: $gl-btn-padding;
padding-bottom: 11px;
font-size: 14px;
line-height: 28px;
color: $gl-text-color-secondary;
+ border: 0;
border-bottom: 2px solid transparent;
white-space: nowrap;
@@ -33,7 +35,13 @@
}
}
+ button {
+ padding-top: 0;
+ background-color: transparent;
+ }
+
&.active a,
+ &.active button,
a.active {
color: $black;
font-weight: $gl-font-weight-bold;
@@ -42,6 +50,10 @@
color: $black;
}
}
+
+ &.md-header-tab button {
+ line-height: 19px;
+ }
}
}
@@ -94,7 +106,7 @@
display: inline-block;
float: right;
text-align: right;
- padding: 11px 0;
+ padding: $gl-padding-8 0;
margin-bottom: 0;
> .btn,
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 88d2f0aaf85..7f0edd88dfb 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -58,7 +58,7 @@
.select2-drop.select2-drop-above {
box-shadow: 0 2px 4px $dropdown-shadow-color;
border-radius: $border-radius-base;
- border: 1px solid $dropdown-border-color;
+ border: 1px solid $border-color;
min-width: 175px;
color: $gl-text-color;
z-index: 999;
@@ -69,7 +69,7 @@
}
.select2-drop.select2-drop-above.select2-drop-active {
- border-top: 1px solid $dropdown-border-color;
+ border-top: 1px solid $border-color;
margin-top: -6px;
}
@@ -193,7 +193,7 @@
color: $gl-text-color;
.select2-result-label {
- background: $dropdown-item-hover-bg;
+ background: $gray-darker;
}
}
@@ -237,12 +237,14 @@
}
.group-path {
- color: $group-path-color;
+ color: $gl-gray-400;
}
}
.user-result {
min-height: 24px;
+ display: flex;
+ align-items: center;
.user-image {
float: left;
@@ -257,7 +259,7 @@
.namespace-result {
.namespace-kind {
- color: $namespace-kind-color;
+ color: $gl-gray-350;
font-weight: $gl-font-weight-normal;
}
diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss
index 7152ef9bcfd..36ab38f1c9d 100644
--- a/app/assets/stylesheets/framework/snippets.scss
+++ b/app/assets/stylesheets/framework/snippets.scss
@@ -45,7 +45,7 @@
}
}
-.snippet-scope-menu .btn-new {
+.snippet-scope-menu .btn-success {
margin-top: 15px;
}
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 339388392df..6954e6599b1 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -147,3 +147,9 @@ table {
}
}
}
+
+.top-area + .content-list {
+ th {
+ border-top: 0;
+ }
+}
diff --git a/app/assets/stylesheets/framework/terms.scss b/app/assets/stylesheets/framework/terms.scss
index 7cda674e5c8..3f4be8829d7 100644
--- a/app/assets/stylesheets/framework/terms.scss
+++ b/app/assets/stylesheets/framework/terms.scss
@@ -19,17 +19,12 @@
justify-content: space-between;
line-height: $line-height-base;
- .card-title {
+ .logo-text {
+ width: 55px;
+ height: 24px;
display: flex;
- align-items: center;
-
- .logo-text {
- width: 55px;
- height: 24px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
+ flex-direction: column;
+ justify-content: center;
}
.navbar-collapse {
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index dfb145debe7..4a311da1675 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -1,7 +1,7 @@
.timeline {
- @include basic-list;
margin: 0;
padding: 0;
+ list-style: none;
&::before {
@include notes-media('max', map-get($grid-breakpoints, sm)) {
@@ -26,10 +26,8 @@
}
.timeline-entry {
- border-color: $white-normal;
color: $gl-text-color;
- border-bottom: 1px solid $border-white-light;
- background: $white-light;
+ background-color: $white-light;
.timeline-entry-inner {
position: relative;
diff --git a/app/assets/stylesheets/framework/toggle.scss b/app/assets/stylesheets/framework/toggle.scss
index 20394cc1e52..8258da07e4d 100644
--- a/app/assets/stylesheets/framework/toggle.scss
+++ b/app/assets/stylesheets/framework/toggle.scss
@@ -31,7 +31,7 @@
height: 24px;
cursor: pointer;
user-select: none;
- background: $feature-toggle-color-disabled;
+ background: $gl-gray-400;
border-radius: 12px;
padding: 3px;
transition: all .4s ease;
@@ -56,12 +56,12 @@
&,
.toggle-icon-svg {
- width: 18px;
- height: 18px;
+ width: $default-icon-size;
+ height: $default-icon-size;
}
.toggle-icon-svg {
- fill: $feature-toggle-color-disabled;
+ fill: $gl-gray-400;
}
.toggle-status-checked {
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 473ca408c04..e261bd7c0ca 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -7,7 +7,7 @@
}
a {
- color: $md-link-color;
+ color: $blue-600;
}
img:not(.emoji) {
@@ -34,7 +34,7 @@
margin-bottom: 0;
}
- *:first-child:not(.katex-display) {
+ *:first-child {
margin-top: 0;
}
@@ -61,12 +61,12 @@
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
- color: $kdb-color;
+ color: $gl-gray-700;
vertical-align: middle;
background-color: $kdb-bg;
border-width: 1px;
border-style: solid;
- border-color: $kdb-border $kdb-border $kdb-border-bottom;
+ border-color: $gl-gray-200 $gl-gray-200 $kdb-border-bottom;
border-image: none;
border-radius: 3px;
box-shadow: 0 -1px 0 $kdb-shadow inset;
@@ -180,7 +180,7 @@
}
a > code {
- color: $gl-link-color;
+ color: $blue-600;
}
dd {
@@ -286,7 +286,7 @@ body {
}
.page-title {
- margin-top: $gl-padding;
+ margin: #{2 * $grid-size} 0;
line-height: 1.3;
font-size: 1.25em;
font-weight: $gl-font-weight-bold;
@@ -327,7 +327,7 @@ h6 {
pre {
font-family: $monospace-font;
display: block;
- padding: $gl-padding-8;
+ padding: $gl-padding-8 $input-horizontal-padding;
margin: 0 0 $gl-padding-8;
font-size: 13px;
word-break: break-all;
@@ -423,25 +423,25 @@ h4 {
input,
textarea {
&::-webkit-input-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
// support firefox 19+ vendor prefix
&::-moz-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
opacity: 1; // FF defaults to 0.54
}
// scss-lint:disable PseudoElement
// support Edge vendor prefix
&::-ms-input-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
// scss-lint:disable PseudoElement
// support IE vendor prefix
&:-ms-input-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index d2ea314f176..f4540146a25 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -10,6 +10,7 @@ $sidebar-breakpoint: 1024px;
$default-transition-duration: 0.15s;
$contextual-sidebar-width: 220px;
$contextual-sidebar-collapsed-width: 50px;
+$toggle-sidebar-height: 48px;
/*
* Color schema
@@ -31,6 +32,14 @@ $gray-dark: darken($gray-light, $darken-dark-factor);
$gray-darker: #eee;
$gray-darkest: #c4c4c4;
+$gl-gray-100: #dddddd;
+$gl-gray-200: #cccccc;
+$gl-gray-350: #aaaaaa;
+$gl-gray-400: #999999;
+$gl-gray-500: #777777;
+$gl-gray-600: #666666;
+$gl-gray-700: #555555;
+
$green-50: #f1fdf6;
$green-100: #dcf5e7;
$green-200: #b3e6c8;
@@ -47,7 +56,7 @@ $blue-50: #f6fafe;
$blue-100: #e4f0fb;
$blue-200: #b8d6f4;
$blue-300: #73afea;
-$blue-400: #2e87e0;
+$blue-400: #418cd8;
$blue-500: #1f78d1;
$blue-600: #1b69b6;
$blue-700: #17599c;
@@ -59,7 +68,7 @@ $orange-50: #fffaf4;
$orange-100: #fff1de;
$orange-200: #fed69f;
$orange-300: #fdbc60;
-$orange-400: #fca121;
+$orange-400: #fca429;
$orange-500: #fc9403;
$orange-600: #de7e00;
$orange-700: #c26700;
@@ -70,7 +79,7 @@ $orange-950: #592800;
$red-50: #fef6f5;
$red-100: #fbe5e1;
$red-200: #f2b4a9;
-$red-300: #e67664;
+$red-300: #ea8271;
$red-400: #e05842;
$red-500: #db3b21;
$red-600: #c0341d;
@@ -186,6 +195,8 @@ $well-light-text-color: #5b6169;
* Text
*/
$gl-font-size: 14px;
+$gl-font-size-xs: 11px;
+$gl-font-size-small: 12px;
$gl-font-weight-normal: 400;
$gl-font-weight-bold: 600;
$gl-text-color: #2e2e2e;
@@ -195,39 +206,16 @@ $gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, 0.85);
$gl-text-color-disabled: #919191;
-$gl-text-green: $green-600;
-$gl-text-green-hover: $green-700;
-$gl-text-red: $red-500;
-$gl-text-orange: $orange-600;
-$gl-link-color: $blue-600;
-$gl-link-hover-color: $blue-800;
$gl-grayish-blue: #7f8fa4;
-$gl-gray: $gl-text-color;
$gl-gray-dark: #313236;
$gl-gray-light: #5c5c5c;
$gl-header-color: #4c4e54;
-$gl-header-nav-hover-color: #434343;
-$placeholder-text-color: $gl-text-color-tertiary;
/*
* Lists
*/
-$list-font-size: $gl-font-size;
-$list-title-color: $gl-text-color;
-$list-text-color: $gl-text-color;
-$list-text-disabled-color: $gl-text-color-tertiary;
-$list-border-light: #eee;
$list-border: rgba(0, 0, 0, 0.05);
$list-text-height: 42px;
-$list-warning-row-bg: $orange-100;
-$list-warning-row-border: $orange-200;
-$list-warning-row-color: $orange-700;
-
-/*
- * Markdown
- */
-$md-link-color: $gl-link-color;
-$md-area-border: #ddd;
/*
* Code
@@ -254,41 +242,26 @@ $gl-vert-padding: 6px;
$gl-padding-top: 10px;
$gl-sidebar-padding: 22px;
$gl-bar-padding: 3px;
+$input-horizontal-padding: 12px;
/*
* Misc
*/
-$row-hover: $blue-50;
-$row-hover-border: $blue-200;
-$progress-color: #c0392b;
$header-height: 40px;
$ide-statusbar-height: 25px;
$fixed-layout-width: 1280px;
$limited-layout-width: 990px;
$container-text-max-width: 540px;
$gl-avatar-size: 40px;
-$error-exclamation-point: $red-500;
$border-radius-default: 4px;
$border-radius-small: 2px;
-$settings-icon-size: 18px;
-$provider-btn-not-active-color: $blue-500;
-$link-underline-blue: $blue-500;
-$active-item-blue: $blue-500;
+$default-icon-size: 18px;
$layout-link-gray: #7e7c7c;
$btn-side-margin: 10px;
$btn-sm-side-margin: 7px;
$btn-margin-5: 5px;
-$issue-status-expired: $orange-500;
-$issuable-sidebar-color: $gl-text-color-secondary;
$sidebar-block-hover-color: #ebebeb;
-$group-path-color: #999;
-$namespace-kind-color: #aaa;
-$panel-heading-link-color: #777;
-$graph-author-email-color: #777;
$count-arrow-border: #dce0e5;
-$save-project-loader-color: #555;
-$divergence-graph-bar-bg: #ccc;
-$divergence-graph-separator-bg: #ccc;
$general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232);
@@ -296,29 +269,14 @@ $performance-bar-height: 35px;
$flash-height: 52px;
$context-header-height: 60px;
$breadcrumb-min-height: 48px;
+$project-title-row-height: 24px;
+$gl-line-height: 16px;
/*
* Common component specific colors
*/
-$hint-color: #999;
-$well-pre-bg: #eee;
-$well-pre-color: #555;
-$loading-color: #555;
-$update-author-color: #999;
$user-mention-bg: rgba($blue-500, 0.044);
$user-mention-bg-hover: rgba($blue-500, 0.15);
-$time-color: #999;
-$project-member-show-color: #aaa;
-$gl-promo-color: #aaa;
-$error-bg: $red-400;
-$warning-message-bg: $orange-100;
-$warning-message-border: $orange-200;
-$warning-message-color: $orange-700;
-$control-group-descr-color: #666;
-$table-permission-x-bg: #d9edf7;
-$username-color: #666;
-$description-color: #666;
-$profiler-border: #eee;
/* tanuki logo colors */
$tanuki-red: #e24329;
@@ -328,19 +286,9 @@ $tanuki-yellow: #fca326;
/*
* State colors:
*/
-$gl-primary: $blue-500;
-$gl-success: $green-500;
-$gl-success-focus: rgba($gl-success, 0.4);
-$gl-info: $blue-500;
-$gl-warning: $orange-500;
-$gl-danger: $red-500;
+$green-500-focus: rgba($green-500, 0.4);
$gl-btn-active-background: rgba(0, 0, 0, 0.16);
$gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background;
-// Bootstrap override states
-$success: $gl-success;
-$info: $gl-info;
-$warning: $gl-warning;
-$danger: $gl-danger;
/*
* Commit Diff Colors
@@ -359,11 +307,8 @@ $line-select-yellow: #fcf8e7;
$line-select-yellow-dark: #f0e2bd;
$dark-diff-match-bg: rgba(255, 255, 255, 0.3);
$dark-diff-match-color: rgba(255, 255, 255, 0.1);
-$file-mode-changed: #777;
-$file-mode-changed: #777;
-$diff-image-info-color: grey;
-$diff-swipe-border: #999;
-$diff-view-modes-color: grey;
+$diff-image-info-color: gray;
+$diff-view-modes-color: gray;
$diff-view-modes-border: #c1c1c1;
$diff-jagged-border-gradient-color: darken($white-normal, 8%);
@@ -373,7 +318,8 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%);
$monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono',
'Courier New', 'andale mono', 'lucida console', monospace;
$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
- 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+ 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
+ 'Noto Color Emoji';
/*
* Dropdowns
@@ -382,20 +328,15 @@ $dropdown-width: 300px;
$dropdown-min-height: 40px;
$dropdown-max-height: 312px;
$dropdown-vertical-offset: 4px;
-$dropdown-link-color: #555;
-$dropdown-link-hover-bg: $row-hover;
$dropdown-empty-row-bg: rgba(#000, 0.04);
-$dropdown-border-color: $border-color;
$dropdown-shadow-color: rgba(#000, 0.1);
$dropdown-divider-color: rgba(#000, 0.1);
$dropdown-title-btn-color: #bfbfbf;
-$dropdown-input-color: #555;
$dropdown-input-fa-color: #c7c7c7;
$dropdown-input-focus-shadow: rgba($blue-300, 0.4);
$dropdown-loading-bg: rgba(#fff, 0.6);
$dropdown-chevron-size: 10px;
$dropdown-toggle-active-border-color: darken($border-color, 14%);
-$dropdown-item-hover-bg: $gray-darker;
$dropdown-fade-mask-height: 32px;
$dropdown-member-form-control-width: 163px;
@@ -403,7 +344,6 @@ $dropdown-member-form-control-width: 163px;
* Filtered Search
*/
$filtered-search-term-shadow-color: rgba(0, 0, 0, 0.09);
-$dropdown-hover-color: $blue-400;
/*
* Contextual Sidebar
@@ -418,7 +358,7 @@ $sidebar-milestone-toggle-bottom-margin: 10px;
* Buttons
*/
$btn-active-gray: #ececec;
-$btn-active-gray-light: e4e7ed;
+$btn-active-gray-light: #e4e7ed;
$btn-white-active: #848484;
$gl-btn-padding: 10px;
$gl-btn-line-height: 16px;
@@ -429,7 +369,6 @@ $gl-btn-horz-padding: 12px;
* Badges
*/
$badge-bg: rgba(0, 0, 0, 0.07);
-$badge-color: $gl-text-color-secondary;
/*
* Pagination
@@ -437,21 +376,12 @@ $badge-color: $gl-text-color-secondary;
$pagination-padding-y: 6px;
$pagination-padding-x: 16px;
$pagination-line-height: 20px;
-$pagination-border-color: $border-color;
-$pagination-active-bg: $blue-600;
-$pagination-active-border-color: $blue-600;
-$pagination-hover-bg: $blue-50;
-$pagination-hover-border-color: $border-color;
-$pagination-hover-color: $gl-text-color;
$pagination-disabled-color: #cdcdcd;
-$pagination-disabled-bg: $gray-light;
-$pagination-disabled-border-color: $border-color;
/*
* Status icons
*/
$status-icon-size: 22px;
-$status-icon-margin: $gl-btn-padding;
/*
* Award emoji
@@ -464,28 +394,19 @@ $award-emoji-positive-add-lines: #bb9c13;
* Search Box
*/
$search-input-border-color: rgba($blue-400, 0.8);
-$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 240px;
$search-input-active-width: 320px;
-$location-badge-active-bg: $blue-500;
$location-icon-color: #e7e9ed;
/*
* Notes
*/
-$notes-light-color: $gl-text-color-secondary;
$note-disabled-comment-color: #b2b2b2;
$note-targe3-outside: #fffff0;
$note-targe3-inside: #ffffd3;
-$note-line2-border: #ddd;
$note-icon-gutter-width: 55px;
/*
-* Zen
-*/
-$zen-control-color: #555;
-
-/*
* Identicon
*/
$identicon-red: #ffebee;
@@ -494,8 +415,6 @@ $identicon-indigo: #e8eaf6;
$identicon-blue: #e3f2fd;
$identicon-teal: #e0f2f1;
$identicon-orange: #fbe9e7;
-$identicon-gray: $gray-darker;
-$identicon-fg-color: #555555;
/*
* Calendar
@@ -510,7 +429,6 @@ $calendar-user-contrib-text: #959494;
$cycle-analytics-box-padding: 30px;
$cycle-analytics-box-text-color: #8c8c8c;
$cycle-analytics-big-font: 19px;
-$cycle-analytics-dark-text: $gl-text-color;
$cycle-analytics-light-gray: #bfbfbf;
$cycle-analytics-dismiss-icon-color: #b2b2b2;
@@ -523,7 +441,7 @@ $ci-skipped-color: #888;
* Boards
*/
$issue-boards-font-size: 14px;
-$issue-boards-card-shadow: rgba(186, 186, 186, 0.5);
+$issue-boards-card-shadow: rgba(0, 0, 0, 0.1);
/*
The following heights are used in boards.scss and are used for calculation of the board height.
They probably should be derived in a smarter way.
@@ -538,9 +456,6 @@ $issue-board-list-difference-md: $issue-board-list-difference-sm + $issue-boards
* Avatar
*/
$avatar-radius: 50%;
-$avatar-border: $gray-normal;
-$avatar-border-hover: $gray-darker;
-$avatar-background: $gray-lightest;
$gl-avatar-size: 40px;
/*
@@ -556,22 +471,6 @@ $blame-blue: #254e77;
$builds-trace-bg: #111;
/*
-* Callout
-*/
-$callout-danger-bg: $red-100;
-$callout-danger-border: $red-200;
-$callout-danger-color: $red-700;
-$callout-warning-bg: $orange-100;
-$callout-warning-border: $orange-200;
-$callout-warning-color: $orange-700;
-$callout-info-bg: $blue-100;
-$callout-info-border: $blue-200;
-$callout-info-color: $blue-700;
-$callout-success-bg: $green-100;
-$callout-success-border: $green-200;
-$callout-success-color: $green-700;
-
-/*
* Commit Page
*/
$commit-max-width-marker-color: rgba(0, 0, 0, 0);
@@ -580,32 +479,12 @@ $commit-message-text-area-bg: rgba(0, 0, 0, 0);
/*
* Common
*/
-$common-gray: $gl-text-color;
$common-gray-light: #bbb;
$common-gray-dark: #444;
-$common-red: $gl-text-red;
-$common-green: $gl-text-green;
-
-/*
-* Editor
-*/
-$editor-cancel-color: $red-600;
-
-/*
-* Events
-*/
-$events-pre-color: #777;
-$events-note-icon-color: #777;
-$events-body-border: #ddd;
/*
* Files
*/
-$file-image-bg: #eee;
-$blob-bg: #eee;
-$blame-border: #eee;
-$blame-line-numbers-border: #ddd;
-$logs-bg: #eee;
$logs-li-color: #888;
$logs-p-color: #333;
@@ -614,7 +493,6 @@ $logs-p-color: #333;
*/
$input-height: 34px;
$input-danger-bg: #f2dede;
-$input-danger-border: $red-400;
$input-group-addon-bg: #f7f8fa;
$gl-field-focus-shadow: rgba(0, 0, 0, 0.075);
$gl-field-focus-shadow-error: rgba($red-500, 0.6);
@@ -625,8 +503,6 @@ $input-short-md-width: 280px;
* Help
*/
$document-index-color: #888;
-$help-shortcut-color: #999;
-$help-shortcut-mapping-color: #555;
$help-shortcut-header-color: #333;
/*
@@ -637,12 +513,6 @@ $issues-today-border: #e1e8d5;
$compare-display-color: #888;
/*
-* jQuery UI
-*/
-$jq-ui-border: #ddd;
-$jq-ui-default-color: #777;
-
-/*
* Label
*/
$label-font-size: 12px;
@@ -661,61 +531,24 @@ $fade-mask-transition-duration: 0.1s;
$fade-mask-transition-curve: ease-in-out;
/*
-* Lint
-*/
-$lint-incorrect-color: $red-500;
-$lint-correct-color: $green-500;
-
-/*
* Login
*/
$login-brand-holder-color: #888;
-$login-devise-error-color: $red-700;
-
-/*
-* Nav
-*/
-$nav-link-gray: #959494;
-$nav-badge-bg: #eee;
-$nav-toggle-gray: #666;
-
-/*
-* Notify
-*/
-$notify-details: #777;
-$notify-footer: #777;
-$notify-new-file: $green-600;
-$notify-deleted-file: $red-700;
/*
* Projects
*/
$project-option-descr-color: #54565b;
-$project-breadcrumb-color: #999;
-$project-private-forks-notice-odd: $green-600;
$project-network-controls-color: #888;
$feature-toggle-color: #fff;
$feature-toggle-text-color: #fff;
-$feature-toggle-color-disabled: #999;
$feature-toggle-color-enabled: #4a8bee;
/*
-* Runners
-*/
-$runner-state-shared-bg: $green-400;
-$runner-state-specific-bg: $blue-400;
-$runner-status-online-color: $green-600;
-$runner-status-offline-color: $gray-darkest;
-$runner-status-paused-color: $red-500;
-
-/*
Stat Graph
*/
$stat-graph-common-bg: #f3f3f3;
-$stat-graph-area-fill: $green-500;
-$stat-graph-axis-fill: #aaa;
-$stat-graph-orange-fill: $orange-500;
$stat-graph-selection-fill: #333;
$stat-graph-selection-stroke: #333;
@@ -726,18 +559,9 @@ $select2-drop-shadow1: rgba(76, 86, 103, 0.247059);
$select2-drop-shadow2: rgba(31, 37, 50, 0.317647);
/*
-* Todo
-*/
-$todo-alert-blue: $blue-500;
-$todo-body-pre-color: #777;
-$todo-body-border: #ddd;
-
-/*
* Typography
*/
$kdb-bg: #fcfcfc;
-$kdb-color: #555;
-$kdb-border: #ccc;
$kdb-border-bottom: #bbb;
$kdb-shadow: #bbb;
$body-text-shadow: rgba(255, 255, 255, 0.01);
@@ -746,12 +570,10 @@ $body-text-shadow: rgba(255, 255, 255, 0.01);
* UI Dev Kit
*/
$ui-dev-kit-example-color: #bbb;
-$ui-dev-kit-example-border: #ddd;
/*
Pipeline Graph
*/
-$stage-hover-bg: $gray-darker;
$ci-action-icon-size: 22px;
$ci-action-icon-size-lg: 24px;
$pipeline-dropdown-line-height: 20px;
@@ -779,23 +601,15 @@ Animation Functions
$dropdown-animation-timing: cubic-bezier(0.23, 1, 0.32, 1);
/*
-Convdev Index
-*/
-$color-high-score: $green-400;
-$color-average-score: $orange-400;
-$color-low-score: $red-400;
-
-/*
Performance Bar
*/
-$perf-bar-text: #999;
$perf-bar-production: #222;
$perf-bar-staging: #291430;
$perf-bar-development: #4c1210;
$perf-bar-bucket-bg: #111;
-$perf-bar-bucket-color: #ccc;
$perf-bar-bucket-box-shadow-from: rgba($white-light, 0.2);
$perf-bar-bucket-box-shadow-to: rgba($black, 0.25);
+$perf-bar-canary-text: $orange-400;
/*
Issuable warning
@@ -826,9 +640,9 @@ Modals
*/
$modal-body-height: 134px;
+$priority-label-empty-state-width: 114px;
+
/*
-Prometheus
+Issues Analytics
*/
-$prometheus-table-row-highlight-color: $theme-gray-100;
-
-$priority-label-empty-state-width: 114px;
+$issues-analytics-popover-boarder-color: rgba(0, 0, 0, 0.15);
diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss
index b9c343fa2e9..759b4f333ca 100644
--- a/app/assets/stylesheets/framework/variables_overrides.scss
+++ b/app/assets/stylesheets/framework/variables_overrides.scss
@@ -14,3 +14,8 @@ $btn-line-height: 20px;
$table-accent-bg: $gray-light;
$card-border-color: $border-color;
$card-cap-bg: $gray-light;
+$success: $green-500;
+$info: $blue-500;
+$warning: $orange-500;
+$danger: $red-500;
+$zindex-modal-backdrop: 1040;
diff --git a/app/assets/stylesheets/framework/zen.scss b/app/assets/stylesheets/framework/zen.scss
index dbd3144b9b4..a4fbd9c073f 100644
--- a/app/assets/stylesheets/framework/zen.scss
+++ b/app/assets/stylesheets/framework/zen.scss
@@ -35,7 +35,7 @@
.zen-control {
padding: 0;
- color: $zen-control-color;
+ color: $gl-gray-700;
background: none;
border: 0;
}
@@ -44,7 +44,7 @@
color: $gl-text-color-secondary;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss
index a81e5eb5ebf..f24c80bd81c 100644
--- a/app/assets/stylesheets/notify.scss
+++ b/app/assets/stylesheets/notify.scss
@@ -7,12 +7,12 @@ img {
p.details {
font-style: italic;
- color: $notify-details;
+ color: $gl-gray-500;
}
.footer > p {
font-size: small;
- color: $notify-footer;
+ color: $gl-gray-500;
}
pre.commit-message {
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index 2b8163b8c68..07d82e984ba 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -7,6 +7,8 @@ $ide-context-header-padding: 10px;
$ide-project-avatar-end: $ide-context-header-padding + 48px;
$ide-tree-padding: $gl-padding;
$ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
+$ide-commit-row-height: 32px;
+$ide-commit-header-height: 48px;
.project-refs-form,
.project-refs-target-form {
@@ -28,11 +30,10 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
.ide-view {
position: relative;
- display: flex;
- height: calc(100vh - #{$header-height});
margin-top: 0;
padding-bottom: $ide-statusbar-height;
color: $gl-text-color;
+ min-height: 0; // firefox fix
&.is-collapsed {
.ide-file-list {
@@ -50,85 +51,11 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
display: flex;
flex-direction: column;
flex: 1;
- min-height: 0;
-
- .file {
- height: 32px;
- cursor: pointer;
-
- &.file-active {
- background: $theme-gray-100;
- }
-
- .ide-file-name {
- flex: 1;
- white-space: nowrap;
- text-overflow: ellipsis;
- max-width: inherit;
- line-height: 16px;
- display: inline-block;
- height: 18px;
-
- svg {
- vertical-align: middle;
- margin-right: 2px;
- }
-
- .loading-container {
- margin-right: 4px;
- display: inline-block;
- }
- }
-
- .ide-file-icon-holder {
- display: flex;
- align-items: center;
- color: $theme-gray-700;
- }
-
- .ide-file-changed-icon {
- margin-left: auto;
-
- > svg {
- display: block;
- }
- }
-
- .ide-new-btn {
- display: none;
-
- .btn {
- padding: 2px 5px;
- }
- }
-
- &:hover,
- &:focus {
- .ide-new-btn {
- display: block;
- }
- }
-
- .folder-icon {
- fill: $gl-text-color-secondary;
- }
- }
+ min-height: 0; // firefox fix
a {
color: $gl-text-color;
}
-
- th {
- position: sticky;
- top: 0;
- }
-}
-
-.file-name {
- display: flex;
- overflow: visible;
- align-items: center;
- width: 100%;
}
.multi-file-loading-container {
@@ -357,7 +284,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
.multi-file-editor-holder {
height: 100%;
- min-height: 0;
+ min-height: 0; // firefox fix
&.is-readonly,
.editor.original {
@@ -546,7 +473,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
border-left: 1px solid $white-dark;
border-top: 1px solid $white-dark;
border-top-left-radius: $border-radius-small;
- min-height: 0;
+ min-height: 0; // firefox fix
}
}
@@ -568,24 +495,11 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
.multi-file-commit-panel-header {
- display: flex;
- align-items: center;
- margin-bottom: 0;
+ height: $ide-commit-header-height;
border-bottom: 1px solid $white-dark;
padding: 12px 0;
}
-.multi-file-commit-panel-header-title {
- display: flex;
- flex: 1;
- align-items: center;
-
- svg {
- margin-right: $gl-btn-padding;
- color: $theme-gray-700;
- }
-}
-
.multi-file-commit-panel-collapse-btn {
border-left: 1px solid $white-dark;
margin-left: auto;
@@ -595,8 +509,6 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
flex: 1;
overflow: auto;
padding: $grid-size 0;
- margin-left: -$grid-size;
- margin-right: -$grid-size;
min-height: 60px;
&.form-text.text-muted {
@@ -605,21 +517,6 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
}
-.ide-file-addition,
-.ide-file-addition-solid {
- color: $green-500;
-}
-
-.ide-file-modified,
-.ide-file-modified-solid {
- color: $orange-500;
-}
-
-.ide-file-deletion,
-.ide-file-deletion-solid {
- color: $red-500;
-}
-
.multi-file-commit-list-collapsed {
display: flex;
flex-direction: column;
@@ -639,8 +536,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
}
-.multi-file-commit-list-path,
-.ide-file-list .file {
+.multi-file-commit-list-path {
display: flex;
align-items: center;
margin-left: -$grid-size;
@@ -648,29 +544,31 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
padding: $grid-size / 2 $grid-size;
border-radius: $border-radius-default;
text-align: left;
+ cursor: pointer;
+ height: $ide-commit-row-height;
+ padding-right: 0;
&:hover,
&:focus {
background: $theme-gray-100;
+
+ outline: 0;
+
+ .multi-file-discard-btn {
+ > .btn {
+ display: flex;
+ }
+ }
}
&:active {
background: $theme-gray-200;
}
-}
-
-.multi-file-commit-list-path {
- cursor: pointer;
&.is-active {
background-color: $white-normal;
}
- &:hover,
- &:focus {
- outline: 0;
- }
-
svg {
min-width: 16px;
vertical-align: middle;
@@ -680,6 +578,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
.multi-file-commit-list-file-path {
@include str-truncated(calc(100% - 30px));
+ user-select: none;
&:active {
text-decoration: none;
@@ -687,9 +586,11 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
.multi-file-discard-btn {
- top: 4px;
- right: 8px;
- bottom: 4px;
+ > .btn {
+ display: none;
+ width: $ide-commit-row-height;
+ height: $ide-commit-row-height;
+ }
svg {
top: 0;
@@ -758,7 +659,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
.ide-loading {
display: flex;
- height: 100vh;
+ height: 100%;
align-items: center;
justify-content: center;
}
@@ -772,60 +673,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
.ide {
overflow: hidden;
-
- &.nav-only {
- padding-top: $header-height;
-
- .with-performance-bar & {
- padding-top: $header-height + $performance-bar-height;
- }
-
- .flash-container {
- margin-top: 0;
- margin-bottom: 0;
- }
-
- .alert-wrapper .flash-container .flash-alert:last-child,
- .alert-wrapper .flash-container .flash-notice:last-child {
- margin-bottom: 0;
- }
-
- .content-wrapper {
- margin-top: 0;
- padding-bottom: 0;
- }
-
- &.flash-shown {
- .content-wrapper {
- margin-top: 0;
- }
-
- .ide-view {
- height: calc(100vh - #{$header-height + $flash-height});
- }
- }
- }
-}
-
-.with-performance-bar .ide.nav-only {
- .flash-container {
- margin-top: 0;
- }
-
- .content-wrapper {
- margin-top: 0;
- padding-bottom: 0;
- }
-
- .ide-view {
- height: calc(100vh - #{$header-height + $performance-bar-height});
- }
-
- &.flash-shown {
- .ide-view {
- height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height});
- }
- }
+ flex: 1;
}
.drag-handle {
@@ -861,10 +709,9 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
.ide-staged-action-btn {
- width: 22px;
- margin-left: -1px;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
+ width: $ide-commit-row-height;
+ height: $ide-commit-row-height;
+ color: inherit;
> svg {
top: 0;
@@ -1158,7 +1005,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
a {
- color: $gl-link-color;
+ color: $blue-600;
}
}
@@ -1199,7 +1046,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
.avatar-container {
- flex: initial;
+ flex: 0 0 auto;
margin-right: 0;
}
@@ -1209,7 +1056,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
.ide-context-body {
- min-height: 0;
+ min-height: 0; // firefox fix
}
.ide-sidebar-project-title {
@@ -1293,6 +1140,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
&.build-page .top-bar {
top: 0;
+ height: auto;
font-size: 12px;
border-top-right-radius: $border-radius-default;
}
@@ -1454,9 +1302,17 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
}
-.ide-new-btn .dropdown.show .ide-entry-dropdown-toggle {
- color: $white-normal;
- background-color: $blue-500;
+.ide-new-btn {
+ display: none;
+
+ .btn {
+ padding: 2px 5px;
+ }
+
+ .dropdown.show .ide-entry-dropdown-toggle {
+ color: $white-normal;
+ background-color: $blue-500;
+ }
}
.ide-preview-header {
@@ -1495,3 +1351,46 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
top: 50%;
transform: translateY(-50%);
}
+
+.ide-file-templates {
+ padding: $grid-size $gl-padding;
+ background-color: $gray-light;
+ border-bottom: 1px solid $white-dark;
+
+ .dropdown {
+ min-width: 180px;
+ }
+
+ .dropdown-content {
+ max-height: 222px;
+ }
+}
+
+.ide-commit-editor-header {
+ height: 65px;
+ padding: 8px 16px;
+ background-color: $theme-gray-50;
+ box-shadow: inset 0 -1px $white-dark;
+}
+
+.ide-commit-list-changed-icon {
+ width: $ide-commit-row-height;
+ height: $ide-commit-row-height;
+}
+
+.ide-file-icon-holder {
+ display: flex;
+ align-items: center;
+ color: $theme-gray-700;
+}
+
+.file-row:hover,
+.file-row:focus {
+ .ide-new-btn {
+ display: block;
+ }
+
+ .folder-icon {
+ fill: $gl-text-color-secondary;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/xterm.scss b/app/assets/stylesheets/page_bundles/xterm.scss
new file mode 100644
index 00000000000..7f040ac9b96
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/xterm.scss
@@ -0,0 +1,1455 @@
+@import 'framework/variables';
+
+.build-page {
+ // color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg
+ // see also: https://gist.github.com/jasonm23/2868981
+
+ $black: #000;
+ $red: #ea1010;
+ $green: #009900;
+ $yellow: #999900;
+ $blue: #0073e6;
+ $magenta: #d411d4;
+ $cyan: #009999;
+ $white: #ccc;
+ $l-black: #373b41;
+ $l-red: #ff6161;
+ $l-green: #00d600;
+ $l-yellow: #bdbd00;
+ $l-blue: #5797ff;
+ $l-magenta: #d96dd9;
+ $l-cyan: #00bdbd;
+ $l-white: #fff;
+
+ /*
+ * xterm colors
+ */
+ $xterm-fg-0: $black;
+ $xterm-fg-1: #800000;
+ $xterm-fg-2: #008000;
+ $xterm-fg-3: #808000;
+ $xterm-fg-4: #000080;
+ $xterm-fg-5: #800080;
+ $xterm-fg-6: #008080;
+ $xterm-fg-7: #c0c0c0;
+ $xterm-fg-8: #808080;
+ $xterm-fg-9: #f00;
+ $xterm-fg-10: #0f0;
+ $xterm-fg-11: #ff0;
+ $xterm-fg-12: #00f;
+ $xterm-fg-13: #f0f;
+ $xterm-fg-14: #0ff;
+ $xterm-fg-15: $white-light;
+ $xterm-fg-16: $black;
+ $xterm-fg-17: #00005f;
+ $xterm-fg-18: #000087;
+ $xterm-fg-19: #0000af;
+ $xterm-fg-20: #0000d7;
+ $xterm-fg-21: #00f;
+ $xterm-fg-22: #005f00;
+ $xterm-fg-23: #005f5f;
+ $xterm-fg-24: #005f87;
+ $xterm-fg-25: #005faf;
+ $xterm-fg-26: #005fd7;
+ $xterm-fg-27: #005fff;
+ $xterm-fg-28: #008700;
+ $xterm-fg-29: #00875f;
+ $xterm-fg-30: #008787;
+ $xterm-fg-31: #0087af;
+ $xterm-fg-32: #0087d7;
+ $xterm-fg-33: #0087ff;
+ $xterm-fg-34: #00af00;
+ $xterm-fg-35: #00af5f;
+ $xterm-fg-36: #00af87;
+ $xterm-fg-37: #00afaf;
+ $xterm-fg-38: #00afd7;
+ $xterm-fg-39: #00afff;
+ $xterm-fg-40: #00d700;
+ $xterm-fg-41: #00d75f;
+ $xterm-fg-42: #00d787;
+ $xterm-fg-43: #00d7af;
+ $xterm-fg-44: #00d7d7;
+ $xterm-fg-45: #00d7ff;
+ $xterm-fg-46: #0f0;
+ $xterm-fg-47: #00ff5f;
+ $xterm-fg-48: #00ff87;
+ $xterm-fg-49: #00ffaf;
+ $xterm-fg-50: #00ffd7;
+ $xterm-fg-51: #0ff;
+ $xterm-fg-52: #5f0000;
+ $xterm-fg-53: #5f005f;
+ $xterm-fg-54: #5f0087;
+ $xterm-fg-55: #5f00af;
+ $xterm-fg-56: #5f00d7;
+ $xterm-fg-57: #5f00ff;
+ $xterm-fg-58: #5f5f00;
+ $xterm-fg-59: #5f5f5f;
+ $xterm-fg-60: #5f5f87;
+ $xterm-fg-61: #5f5faf;
+ $xterm-fg-62: #5f5fd7;
+ $xterm-fg-63: #5f5fff;
+ $xterm-fg-64: #5f8700;
+ $xterm-fg-65: #5f875f;
+ $xterm-fg-66: #5f8787;
+ $xterm-fg-67: #5f87af;
+ $xterm-fg-68: #5f87d7;
+ $xterm-fg-69: #5f87ff;
+ $xterm-fg-70: #5faf00;
+ $xterm-fg-71: #5faf5f;
+ $xterm-fg-72: #5faf87;
+ $xterm-fg-73: #5fafaf;
+ $xterm-fg-74: #5fafd7;
+ $xterm-fg-75: #5fafff;
+ $xterm-fg-76: #5fd700;
+ $xterm-fg-77: #5fd75f;
+ $xterm-fg-78: #5fd787;
+ $xterm-fg-79: #5fd7af;
+ $xterm-fg-80: #5fd7d7;
+ $xterm-fg-81: #5fd7ff;
+ $xterm-fg-82: #5fff00;
+ $xterm-fg-83: #5fff5f;
+ $xterm-fg-84: #5fff87;
+ $xterm-fg-85: #5fffaf;
+ $xterm-fg-86: #5fffd7;
+ $xterm-fg-87: #5fffff;
+ $xterm-fg-88: #870000;
+ $xterm-fg-89: #87005f;
+ $xterm-fg-90: #870087;
+ $xterm-fg-91: #8700af;
+ $xterm-fg-92: #8700d7;
+ $xterm-fg-93: #8700ff;
+ $xterm-fg-94: #875f00;
+ $xterm-fg-95: #875f5f;
+ $xterm-fg-96: #875f87;
+ $xterm-fg-97: #875faf;
+ $xterm-fg-98: #875fd7;
+ $xterm-fg-99: #875fff;
+ $xterm-fg-100: #878700;
+ $xterm-fg-101: #87875f;
+ $xterm-fg-102: #878787;
+ $xterm-fg-103: #8787af;
+ $xterm-fg-104: #8787d7;
+ $xterm-fg-105: #8787ff;
+ $xterm-fg-106: #87af00;
+ $xterm-fg-107: #87af5f;
+ $xterm-fg-108: #87af87;
+ $xterm-fg-109: #87afaf;
+ $xterm-fg-110: #87afd7;
+ $xterm-fg-111: #87afff;
+ $xterm-fg-112: #87d700;
+ $xterm-fg-113: #87d75f;
+ $xterm-fg-114: #87d787;
+ $xterm-fg-115: #87d7af;
+ $xterm-fg-116: #87d7d7;
+ $xterm-fg-117: #87d7ff;
+ $xterm-fg-118: #87ff00;
+ $xterm-fg-119: #87ff5f;
+ $xterm-fg-120: #87ff87;
+ $xterm-fg-121: #87ffaf;
+ $xterm-fg-122: #87ffd7;
+ $xterm-fg-123: #87ffff;
+ $xterm-fg-124: #af0000;
+ $xterm-fg-125: #af005f;
+ $xterm-fg-126: #af0087;
+ $xterm-fg-127: #af00af;
+ $xterm-fg-128: #af00d7;
+ $xterm-fg-129: #af00ff;
+ $xterm-fg-130: #af5f00;
+ $xterm-fg-131: #af5f5f;
+ $xterm-fg-132: #af5f87;
+ $xterm-fg-133: #af5faf;
+ $xterm-fg-134: #af5fd7;
+ $xterm-fg-135: #af5fff;
+ $xterm-fg-136: #af8700;
+ $xterm-fg-137: #af875f;
+ $xterm-fg-138: #af8787;
+ $xterm-fg-139: #af87af;
+ $xterm-fg-140: #af87d7;
+ $xterm-fg-141: #af87ff;
+ $xterm-fg-142: #afaf00;
+ $xterm-fg-143: #afaf5f;
+ $xterm-fg-144: #afaf87;
+ $xterm-fg-145: #afafaf;
+ $xterm-fg-146: #afafd7;
+ $xterm-fg-147: #afafff;
+ $xterm-fg-148: #afd700;
+ $xterm-fg-149: #afd75f;
+ $xterm-fg-150: #afd787;
+ $xterm-fg-151: #afd7af;
+ $xterm-fg-152: #afd7d7;
+ $xterm-fg-153: #afd7ff;
+ $xterm-fg-154: #afff00;
+ $xterm-fg-155: #afff5f;
+ $xterm-fg-156: #afff87;
+ $xterm-fg-157: #afffaf;
+ $xterm-fg-158: #afffd7;
+ $xterm-fg-159: #afffff;
+ $xterm-fg-160: #d70000;
+ $xterm-fg-161: #d7005f;
+ $xterm-fg-162: #d70087;
+ $xterm-fg-163: #d700af;
+ $xterm-fg-164: #d700d7;
+ $xterm-fg-165: #d700ff;
+ $xterm-fg-166: #d75f00;
+ $xterm-fg-167: #d75f5f;
+ $xterm-fg-168: #d75f87;
+ $xterm-fg-169: #d75faf;
+ $xterm-fg-170: #d75fd7;
+ $xterm-fg-171: #d75fff;
+ $xterm-fg-172: #d78700;
+ $xterm-fg-173: #d7875f;
+ $xterm-fg-174: #d78787;
+ $xterm-fg-175: #d787af;
+ $xterm-fg-176: #d787d7;
+ $xterm-fg-177: #d787ff;
+ $xterm-fg-178: #d7af00;
+ $xterm-fg-179: #d7af5f;
+ $xterm-fg-180: #d7af87;
+ $xterm-fg-181: #d7afaf;
+ $xterm-fg-182: #d7afd7;
+ $xterm-fg-183: #d7afff;
+ $xterm-fg-184: #d7d700;
+ $xterm-fg-185: #d7d75f;
+ $xterm-fg-186: #d7d787;
+ $xterm-fg-187: #d7d7af;
+ $xterm-fg-188: #d7d7d7;
+ $xterm-fg-189: #d7d7ff;
+ $xterm-fg-190: #d7ff00;
+ $xterm-fg-191: #d7ff5f;
+ $xterm-fg-192: #d7ff87;
+ $xterm-fg-193: #d7ffaf;
+ $xterm-fg-194: #d7ffd7;
+ $xterm-fg-195: #d7ffff;
+ $xterm-fg-196: #f00;
+ $xterm-fg-197: #ff005f;
+ $xterm-fg-198: #ff0087;
+ $xterm-fg-199: #ff00af;
+ $xterm-fg-200: #ff00d7;
+ $xterm-fg-201: #f0f;
+ $xterm-fg-202: #ff5f00;
+ $xterm-fg-203: #ff5f5f;
+ $xterm-fg-204: #ff5f87;
+ $xterm-fg-205: #ff5faf;
+ $xterm-fg-206: #ff5fd7;
+ $xterm-fg-207: #ff5fff;
+ $xterm-fg-208: #ff8700;
+ $xterm-fg-209: #ff875f;
+ $xterm-fg-210: #ff8787;
+ $xterm-fg-211: #ff87af;
+ $xterm-fg-212: #ff87d7;
+ $xterm-fg-213: #ff87ff;
+ $xterm-fg-214: #ffaf00;
+ $xterm-fg-215: #ffaf5f;
+ $xterm-fg-216: #ffaf87;
+ $xterm-fg-217: #ffafaf;
+ $xterm-fg-218: #ffafd7;
+ $xterm-fg-219: #ffafff;
+ $xterm-fg-220: #ffd700;
+ $xterm-fg-221: #ffd75f;
+ $xterm-fg-222: #ffd787;
+ $xterm-fg-223: #ffd7af;
+ $xterm-fg-224: #ffd7d7;
+ $xterm-fg-225: #ffd7ff;
+ $xterm-fg-226: #ff0;
+ $xterm-fg-227: #ffff5f;
+ $xterm-fg-228: #ffff87;
+ $xterm-fg-229: #ffffaf;
+ $xterm-fg-230: #ffffd7;
+ $xterm-fg-231: $white-light;
+ $xterm-fg-232: #080808;
+ $xterm-fg-233: #121212;
+ $xterm-fg-234: #1c1c1c;
+ $xterm-fg-235: #262626;
+ $xterm-fg-236: #303030;
+ $xterm-fg-237: #3a3a3a;
+ $xterm-fg-238: #444;
+ $xterm-fg-239: #4e4e4e;
+ $xterm-fg-240: #585858;
+ $xterm-fg-241: #626262;
+ $xterm-fg-242: #6c6c6c;
+ $xterm-fg-243: #767676;
+ $xterm-fg-244: #808080;
+ $xterm-fg-245: #8a8a8a;
+ $xterm-fg-246: #949494;
+ $xterm-fg-247: #9e9e9e;
+ $xterm-fg-248: #a8a8a8;
+ $xterm-fg-249: #b2b2b2;
+ $xterm-fg-250: #bcbcbc;
+ $xterm-fg-251: #c6c6c6;
+ $xterm-fg-252: #d0d0d0;
+ $xterm-fg-253: #dadada;
+ $xterm-fg-254: #e4e4e4;
+ $xterm-fg-255: #eee;
+
+ .term-bold {
+ font-weight: $gl-font-weight-bold;
+ }
+
+ .term-italic {
+ font-style: italic;
+ }
+
+ .term-conceal {
+ visibility: hidden;
+ }
+
+ .term-underline {
+ text-decoration: underline;
+ }
+
+ .term-cross {
+ text-decoration: line-through;
+ }
+
+ .term-fg-black {
+ color: $black;
+ }
+
+ .term-fg-red {
+ color: $red;
+ }
+
+ .term-fg-green {
+ color: $green;
+ }
+
+ .term-fg-yellow {
+ color: $yellow;
+ }
+
+ .term-fg-blue {
+ color: $blue;
+ }
+
+ .term-fg-magenta {
+ color: $magenta;
+ }
+
+ .term-fg-cyan {
+ color: $cyan;
+ }
+
+ .term-fg-white {
+ color: $white;
+ }
+
+ .term-fg-l-black {
+ color: $l-black;
+ }
+
+ .term-fg-l-red {
+ color: $l-red;
+ }
+
+ .term-fg-l-green {
+ color: $l-green;
+ }
+
+ .term-fg-l-yellow {
+ color: $l-yellow;
+ }
+
+ .term-fg-l-blue {
+ color: $l-blue;
+ }
+
+ .term-fg-l-magenta {
+ color: $l-magenta;
+ }
+
+ .term-fg-l-cyan {
+ color: $l-cyan;
+ }
+
+ .term-fg-l-white {
+ color: $l-white;
+ }
+
+ .term-bg-black {
+ background-color: $black;
+ }
+
+ .term-bg-red {
+ background-color: $red;
+ }
+
+ .term-bg-green {
+ background-color: $green;
+ }
+
+ .term-bg-yellow {
+ background-color: $yellow;
+ }
+
+ .term-bg-blue {
+ background-color: $blue;
+ }
+
+ .term-bg-magenta {
+ background-color: $magenta;
+ }
+
+ .term-bg-cyan {
+ background-color: $cyan;
+ }
+
+ .term-bg-white {
+ background-color: $white;
+ }
+
+ .term-bg-l-black {
+ background-color: $l-black;
+ }
+
+ .term-bg-l-red {
+ background-color: $l-red;
+ }
+
+ .term-bg-l-green {
+ background-color: $l-green;
+ }
+
+ .term-bg-l-yellow {
+ background-color: $l-yellow;
+ }
+
+ .term-bg-l-blue {
+ background-color: $l-blue;
+ }
+
+ .term-bg-l-magenta {
+ background-color: $l-magenta;
+ }
+
+ .term-bg-l-cyan {
+ background-color: $l-cyan;
+ }
+
+ .term-bg-l-white {
+ background-color: $l-white;
+ }
+
+ .xterm-fg-0 {
+ color: $xterm-fg-0;
+ }
+
+ .xterm-fg-1 {
+ color: $xterm-fg-1;
+ }
+
+ .xterm-fg-2 {
+ color: $xterm-fg-2;
+ }
+
+ .xterm-fg-3 {
+ color: $xterm-fg-3;
+ }
+
+ .xterm-fg-4 {
+ color: $xterm-fg-4;
+ }
+
+ .xterm-fg-5 {
+ color: $xterm-fg-5;
+ }
+
+ .xterm-fg-6 {
+ color: $xterm-fg-6;
+ }
+
+ .xterm-fg-7 {
+ color: $xterm-fg-7;
+ }
+
+ .xterm-fg-8 {
+ color: $xterm-fg-8;
+ }
+
+ .xterm-fg-9 {
+ color: $xterm-fg-9;
+ }
+
+ .xterm-fg-10 {
+ color: $xterm-fg-10;
+ }
+
+ .xterm-fg-11 {
+ color: $xterm-fg-11;
+ }
+
+ .xterm-fg-12 {
+ color: $xterm-fg-12;
+ }
+
+ .xterm-fg-13 {
+ color: $xterm-fg-13;
+ }
+
+ .xterm-fg-14 {
+ color: $xterm-fg-14;
+ }
+
+ .xterm-fg-15 {
+ color: $white-light;
+ }
+
+ .xterm-fg-16 {
+ color: $black;
+ }
+
+ .xterm-fg-17 {
+ color: $xterm-fg-17;
+ }
+
+ .xterm-fg-18 {
+ color: $xterm-fg-18;
+ }
+
+ .xterm-fg-19 {
+ color: $xterm-fg-19;
+ }
+
+ .xterm-fg-20 {
+ color: $xterm-fg-20;
+ }
+
+ .xterm-fg-21 {
+ color: $xterm-fg-21;
+ }
+
+ .xterm-fg-22 {
+ color: $xterm-fg-22;
+ }
+
+ .xterm-fg-23 {
+ color: $xterm-fg-23;
+ }
+
+ .xterm-fg-24 {
+ color: $xterm-fg-24;
+ }
+
+ .xterm-fg-25 {
+ color: $xterm-fg-25;
+ }
+
+ .xterm-fg-26 {
+ color: $xterm-fg-26;
+ }
+
+ .xterm-fg-27 {
+ color: $xterm-fg-27;
+ }
+
+ .xterm-fg-28 {
+ color: $xterm-fg-28;
+ }
+
+ .xterm-fg-29 {
+ color: $xterm-fg-29;
+ }
+
+ .xterm-fg-30 {
+ color: $xterm-fg-30;
+ }
+
+ .xterm-fg-31 {
+ color: $xterm-fg-31;
+ }
+
+ .xterm-fg-32 {
+ color: $xterm-fg-32;
+ }
+
+ .xterm-fg-33 {
+ color: $xterm-fg-33;
+ }
+
+ .xterm-fg-34 {
+ color: $xterm-fg-34;
+ }
+
+ .xterm-fg-35 {
+ color: $xterm-fg-35;
+ }
+
+ .xterm-fg-36 {
+ color: $xterm-fg-36;
+ }
+
+ .xterm-fg-37 {
+ color: $xterm-fg-37;
+ }
+
+ .xterm-fg-38 {
+ color: $xterm-fg-38;
+ }
+
+ .xterm-fg-39 {
+ color: $xterm-fg-39;
+ }
+
+ .xterm-fg-40 {
+ color: $xterm-fg-40;
+ }
+
+ .xterm-fg-41 {
+ color: $xterm-fg-41;
+ }
+
+ .xterm-fg-42 {
+ color: $xterm-fg-42;
+ }
+
+ .xterm-fg-43 {
+ color: $xterm-fg-43;
+ }
+
+ .xterm-fg-44 {
+ color: $xterm-fg-44;
+ }
+
+ .xterm-fg-45 {
+ color: $xterm-fg-45;
+ }
+
+ .xterm-fg-46 {
+ color: $xterm-fg-46;
+ }
+
+ .xterm-fg-47 {
+ color: $xterm-fg-47;
+ }
+
+ .xterm-fg-48 {
+ color: $xterm-fg-48;
+ }
+
+ .xterm-fg-49 {
+ color: $xterm-fg-49;
+ }
+
+ .xterm-fg-50 {
+ color: $xterm-fg-50;
+ }
+
+ .xterm-fg-51 {
+ color: $xterm-fg-51;
+ }
+
+ .xterm-fg-52 {
+ color: $xterm-fg-52;
+ }
+
+ .xterm-fg-53 {
+ color: $xterm-fg-53;
+ }
+
+ .xterm-fg-54 {
+ color: $xterm-fg-54;
+ }
+
+ .xterm-fg-55 {
+ color: $xterm-fg-55;
+ }
+
+ .xterm-fg-56 {
+ color: $xterm-fg-56;
+ }
+
+ .xterm-fg-57 {
+ color: $xterm-fg-57;
+ }
+
+ .xterm-fg-58 {
+ color: $xterm-fg-58;
+ }
+
+ .xterm-fg-59 {
+ color: $xterm-fg-59;
+ }
+
+ .xterm-fg-60 {
+ color: $xterm-fg-60;
+ }
+
+ .xterm-fg-61 {
+ color: $xterm-fg-61;
+ }
+
+ .xterm-fg-62 {
+ color: $xterm-fg-62;
+ }
+
+ .xterm-fg-63 {
+ color: $xterm-fg-63;
+ }
+
+ .xterm-fg-64 {
+ color: $xterm-fg-64;
+ }
+
+ .xterm-fg-65 {
+ color: $xterm-fg-65;
+ }
+
+ .xterm-fg-66 {
+ color: $xterm-fg-66;
+ }
+
+ .xterm-fg-67 {
+ color: $xterm-fg-67;
+ }
+
+ .xterm-fg-68 {
+ color: $xterm-fg-68;
+ }
+
+ .xterm-fg-69 {
+ color: $xterm-fg-69;
+ }
+
+ .xterm-fg-70 {
+ color: $xterm-fg-70;
+ }
+
+ .xterm-fg-71 {
+ color: $xterm-fg-71;
+ }
+
+ .xterm-fg-72 {
+ color: $xterm-fg-72;
+ }
+
+ .xterm-fg-73 {
+ color: $xterm-fg-73;
+ }
+
+ .xterm-fg-74 {
+ color: $xterm-fg-74;
+ }
+
+ .xterm-fg-75 {
+ color: $xterm-fg-75;
+ }
+
+ .xterm-fg-76 {
+ color: $xterm-fg-76;
+ }
+
+ .xterm-fg-77 {
+ color: $xterm-fg-77;
+ }
+
+ .xterm-fg-78 {
+ color: $xterm-fg-78;
+ }
+
+ .xterm-fg-79 {
+ color: $xterm-fg-79;
+ }
+
+ .xterm-fg-80 {
+ color: $xterm-fg-80;
+ }
+
+ .xterm-fg-81 {
+ color: $xterm-fg-81;
+ }
+
+ .xterm-fg-82 {
+ color: $xterm-fg-82;
+ }
+
+ .xterm-fg-83 {
+ color: $xterm-fg-83;
+ }
+
+ .xterm-fg-84 {
+ color: $xterm-fg-84;
+ }
+
+ .xterm-fg-85 {
+ color: $xterm-fg-85;
+ }
+
+ .xterm-fg-86 {
+ color: $xterm-fg-86;
+ }
+
+ .xterm-fg-87 {
+ color: $xterm-fg-87;
+ }
+
+ .xterm-fg-88 {
+ color: $xterm-fg-88;
+ }
+
+ .xterm-fg-89 {
+ color: $xterm-fg-89;
+ }
+
+ .xterm-fg-90 {
+ color: $xterm-fg-90;
+ }
+
+ .xterm-fg-91 {
+ color: $xterm-fg-91;
+ }
+
+ .xterm-fg-92 {
+ color: $xterm-fg-92;
+ }
+
+ .xterm-fg-93 {
+ color: $xterm-fg-93;
+ }
+
+ .xterm-fg-94 {
+ color: $xterm-fg-94;
+ }
+
+ .xterm-fg-95 {
+ color: $xterm-fg-95;
+ }
+
+ .xterm-fg-96 {
+ color: $xterm-fg-96;
+ }
+
+ .xterm-fg-97 {
+ color: $xterm-fg-97;
+ }
+
+ .xterm-fg-98 {
+ color: $xterm-fg-98;
+ }
+
+ .xterm-fg-99 {
+ color: $xterm-fg-99;
+ }
+
+ .xterm-fg-100 {
+ color: $xterm-fg-100;
+ }
+
+ .xterm-fg-101 {
+ color: $xterm-fg-101;
+ }
+
+ .xterm-fg-102 {
+ color: $xterm-fg-102;
+ }
+
+ .xterm-fg-103 {
+ color: $xterm-fg-103;
+ }
+
+ .xterm-fg-104 {
+ color: $xterm-fg-104;
+ }
+
+ .xterm-fg-105 {
+ color: $xterm-fg-105;
+ }
+
+ .xterm-fg-106 {
+ color: $xterm-fg-106;
+ }
+
+ .xterm-fg-107 {
+ color: $xterm-fg-107;
+ }
+
+ .xterm-fg-108 {
+ color: $xterm-fg-108;
+ }
+
+ .xterm-fg-109 {
+ color: $xterm-fg-109;
+ }
+
+ .xterm-fg-110 {
+ color: $xterm-fg-110;
+ }
+
+ .xterm-fg-111 {
+ color: $xterm-fg-111;
+ }
+
+ .xterm-fg-112 {
+ color: $xterm-fg-112;
+ }
+
+ .xterm-fg-113 {
+ color: $xterm-fg-113;
+ }
+
+ .xterm-fg-114 {
+ color: $xterm-fg-114;
+ }
+
+ .xterm-fg-115 {
+ color: $xterm-fg-115;
+ }
+
+ .xterm-fg-116 {
+ color: $xterm-fg-116;
+ }
+
+ .xterm-fg-117 {
+ color: $xterm-fg-117;
+ }
+
+ .xterm-fg-118 {
+ color: $xterm-fg-118;
+ }
+
+ .xterm-fg-119 {
+ color: $xterm-fg-119;
+ }
+
+ .xterm-fg-120 {
+ color: $xterm-fg-120;
+ }
+
+ .xterm-fg-121 {
+ color: $xterm-fg-121;
+ }
+
+ .xterm-fg-122 {
+ color: $xterm-fg-122;
+ }
+
+ .xterm-fg-123 {
+ color: $xterm-fg-123;
+ }
+
+ .xterm-fg-124 {
+ color: $xterm-fg-124;
+ }
+
+ .xterm-fg-125 {
+ color: $xterm-fg-125;
+ }
+
+ .xterm-fg-126 {
+ color: $xterm-fg-126;
+ }
+
+ .xterm-fg-127 {
+ color: $xterm-fg-127;
+ }
+
+ .xterm-fg-128 {
+ color: $xterm-fg-128;
+ }
+
+ .xterm-fg-129 {
+ color: $xterm-fg-129;
+ }
+
+ .xterm-fg-130 {
+ color: $xterm-fg-130;
+ }
+
+ .xterm-fg-131 {
+ color: $xterm-fg-131;
+ }
+
+ .xterm-fg-132 {
+ color: $xterm-fg-132;
+ }
+
+ .xterm-fg-133 {
+ color: $xterm-fg-133;
+ }
+
+ .xterm-fg-134 {
+ color: $xterm-fg-134;
+ }
+
+ .xterm-fg-135 {
+ color: $xterm-fg-135;
+ }
+
+ .xterm-fg-136 {
+ color: $xterm-fg-136;
+ }
+
+ .xterm-fg-137 {
+ color: $xterm-fg-137;
+ }
+
+ .xterm-fg-138 {
+ color: $xterm-fg-138;
+ }
+
+ .xterm-fg-139 {
+ color: $xterm-fg-139;
+ }
+
+ .xterm-fg-140 {
+ color: $xterm-fg-140;
+ }
+
+ .xterm-fg-141 {
+ color: $xterm-fg-141;
+ }
+
+ .xterm-fg-142 {
+ color: $xterm-fg-142;
+ }
+
+ .xterm-fg-143 {
+ color: $xterm-fg-143;
+ }
+
+ .xterm-fg-144 {
+ color: $xterm-fg-144;
+ }
+
+ .xterm-fg-145 {
+ color: $xterm-fg-145;
+ }
+
+ .xterm-fg-146 {
+ color: $xterm-fg-146;
+ }
+
+ .xterm-fg-147 {
+ color: $xterm-fg-147;
+ }
+
+ .xterm-fg-148 {
+ color: $xterm-fg-148;
+ }
+
+ .xterm-fg-149 {
+ color: $xterm-fg-149;
+ }
+
+ .xterm-fg-150 {
+ color: $xterm-fg-150;
+ }
+
+ .xterm-fg-151 {
+ color: $xterm-fg-151;
+ }
+
+ .xterm-fg-152 {
+ color: $xterm-fg-152;
+ }
+
+ .xterm-fg-153 {
+ color: $xterm-fg-153;
+ }
+
+ .xterm-fg-154 {
+ color: $xterm-fg-154;
+ }
+
+ .xterm-fg-155 {
+ color: $xterm-fg-155;
+ }
+
+ .xterm-fg-156 {
+ color: $xterm-fg-156;
+ }
+
+ .xterm-fg-157 {
+ color: $xterm-fg-157;
+ }
+
+ .xterm-fg-158 {
+ color: $xterm-fg-158;
+ }
+
+ .xterm-fg-159 {
+ color: $xterm-fg-159;
+ }
+
+ .xterm-fg-160 {
+ color: $xterm-fg-160;
+ }
+
+ .xterm-fg-161 {
+ color: $xterm-fg-161;
+ }
+
+ .xterm-fg-162 {
+ color: $xterm-fg-162;
+ }
+
+ .xterm-fg-163 {
+ color: $xterm-fg-163;
+ }
+
+ .xterm-fg-164 {
+ color: $xterm-fg-164;
+ }
+
+ .xterm-fg-165 {
+ color: $xterm-fg-165;
+ }
+
+ .xterm-fg-166 {
+ color: $xterm-fg-166;
+ }
+
+ .xterm-fg-167 {
+ color: $xterm-fg-167;
+ }
+
+ .xterm-fg-168 {
+ color: $xterm-fg-168;
+ }
+
+ .xterm-fg-169 {
+ color: $xterm-fg-169;
+ }
+
+ .xterm-fg-170 {
+ color: $xterm-fg-170;
+ }
+
+ .xterm-fg-171 {
+ color: $xterm-fg-171;
+ }
+
+ .xterm-fg-172 {
+ color: $xterm-fg-172;
+ }
+
+ .xterm-fg-173 {
+ color: $xterm-fg-173;
+ }
+
+ .xterm-fg-174 {
+ color: $xterm-fg-174;
+ }
+
+ .xterm-fg-175 {
+ color: $xterm-fg-175;
+ }
+
+ .xterm-fg-176 {
+ color: $xterm-fg-176;
+ }
+
+ .xterm-fg-177 {
+ color: $xterm-fg-177;
+ }
+
+ .xterm-fg-178 {
+ color: $xterm-fg-178;
+ }
+
+ .xterm-fg-179 {
+ color: $xterm-fg-179;
+ }
+
+ .xterm-fg-180 {
+ color: $xterm-fg-180;
+ }
+
+ .xterm-fg-181 {
+ color: $xterm-fg-181;
+ }
+
+ .xterm-fg-182 {
+ color: $xterm-fg-182;
+ }
+
+ .xterm-fg-183 {
+ color: $xterm-fg-183;
+ }
+
+ .xterm-fg-184 {
+ color: $xterm-fg-184;
+ }
+
+ .xterm-fg-185 {
+ color: $xterm-fg-185;
+ }
+
+ .xterm-fg-186 {
+ color: $xterm-fg-186;
+ }
+
+ .xterm-fg-187 {
+ color: $xterm-fg-187;
+ }
+
+ .xterm-fg-188 {
+ color: $xterm-fg-188;
+ }
+
+ .xterm-fg-189 {
+ color: $xterm-fg-189;
+ }
+
+ .xterm-fg-190 {
+ color: $xterm-fg-190;
+ }
+
+ .xterm-fg-191 {
+ color: $xterm-fg-191;
+ }
+
+ .xterm-fg-192 {
+ color: $xterm-fg-192;
+ }
+
+ .xterm-fg-193 {
+ color: $xterm-fg-193;
+ }
+
+ .xterm-fg-194 {
+ color: $xterm-fg-194;
+ }
+
+ .xterm-fg-195 {
+ color: $xterm-fg-195;
+ }
+
+ .xterm-fg-196 {
+ color: $xterm-fg-196;
+ }
+
+ .xterm-fg-197 {
+ color: $xterm-fg-197;
+ }
+
+ .xterm-fg-198 {
+ color: $xterm-fg-198;
+ }
+
+ .xterm-fg-199 {
+ color: $xterm-fg-199;
+ }
+
+ .xterm-fg-200 {
+ color: $xterm-fg-200;
+ }
+
+ .xterm-fg-201 {
+ color: $xterm-fg-201;
+ }
+
+ .xterm-fg-202 {
+ color: $xterm-fg-202;
+ }
+
+ .xterm-fg-203 {
+ color: $xterm-fg-203;
+ }
+
+ .xterm-fg-204 {
+ color: $xterm-fg-204;
+ }
+
+ .xterm-fg-205 {
+ color: $xterm-fg-205;
+ }
+
+ .xterm-fg-206 {
+ color: $xterm-fg-206;
+ }
+
+ .xterm-fg-207 {
+ color: $xterm-fg-207;
+ }
+
+ .xterm-fg-208 {
+ color: $xterm-fg-208;
+ }
+
+ .xterm-fg-209 {
+ color: $xterm-fg-209;
+ }
+
+ .xterm-fg-210 {
+ color: $xterm-fg-210;
+ }
+
+ .xterm-fg-211 {
+ color: $xterm-fg-211;
+ }
+
+ .xterm-fg-212 {
+ color: $xterm-fg-212;
+ }
+
+ .xterm-fg-213 {
+ color: $xterm-fg-213;
+ }
+
+ .xterm-fg-214 {
+ color: $xterm-fg-214;
+ }
+
+ .xterm-fg-215 {
+ color: $xterm-fg-215;
+ }
+
+ .xterm-fg-216 {
+ color: $xterm-fg-216;
+ }
+
+ .xterm-fg-217 {
+ color: $xterm-fg-217;
+ }
+
+ .xterm-fg-218 {
+ color: $xterm-fg-218;
+ }
+
+ .xterm-fg-219 {
+ color: $xterm-fg-219;
+ }
+
+ .xterm-fg-220 {
+ color: $xterm-fg-220;
+ }
+
+ .xterm-fg-221 {
+ color: $xterm-fg-221;
+ }
+
+ .xterm-fg-222 {
+ color: $xterm-fg-222;
+ }
+
+ .xterm-fg-223 {
+ color: $xterm-fg-223;
+ }
+
+ .xterm-fg-224 {
+ color: $xterm-fg-224;
+ }
+
+ .xterm-fg-225 {
+ color: $xterm-fg-225;
+ }
+
+ .xterm-fg-226 {
+ color: $xterm-fg-226;
+ }
+
+ .xterm-fg-227 {
+ color: $xterm-fg-227;
+ }
+
+ .xterm-fg-228 {
+ color: $xterm-fg-228;
+ }
+
+ .xterm-fg-229 {
+ color: $xterm-fg-229;
+ }
+
+ .xterm-fg-230 {
+ color: $xterm-fg-230;
+ }
+
+ .xterm-fg-231 {
+ color: $xterm-fg-231;
+ }
+
+ .xterm-fg-232 {
+ color: $xterm-fg-232;
+ }
+
+ .xterm-fg-233 {
+ color: $xterm-fg-233;
+ }
+
+ .xterm-fg-234 {
+ color: $xterm-fg-234;
+ }
+
+ .xterm-fg-235 {
+ color: $xterm-fg-235;
+ }
+
+ .xterm-fg-236 {
+ color: $xterm-fg-236;
+ }
+
+ .xterm-fg-237 {
+ color: $xterm-fg-237;
+ }
+
+ .xterm-fg-238 {
+ color: $xterm-fg-238;
+ }
+
+ .xterm-fg-239 {
+ color: $xterm-fg-239;
+ }
+
+ .xterm-fg-240 {
+ color: $xterm-fg-240;
+ }
+
+ .xterm-fg-241 {
+ color: $xterm-fg-241;
+ }
+
+ .xterm-fg-242 {
+ color: $xterm-fg-242;
+ }
+
+ .xterm-fg-243 {
+ color: $xterm-fg-243;
+ }
+
+ .xterm-fg-244 {
+ color: $xterm-fg-244;
+ }
+
+ .xterm-fg-245 {
+ color: $xterm-fg-245;
+ }
+
+ .xterm-fg-246 {
+ color: $xterm-fg-246;
+ }
+
+ .xterm-fg-247 {
+ color: $xterm-fg-247;
+ }
+
+ .xterm-fg-248 {
+ color: $xterm-fg-248;
+ }
+
+ .xterm-fg-249 {
+ color: $xterm-fg-249;
+ }
+
+ .xterm-fg-250 {
+ color: $xterm-fg-250;
+ }
+
+ .xterm-fg-251 {
+ color: $xterm-fg-251;
+ }
+
+ .xterm-fg-252 {
+ color: $xterm-fg-252;
+ }
+
+ .xterm-fg-253 {
+ color: $xterm-fg-253;
+ }
+
+ .xterm-fg-254 {
+ color: $xterm-fg-254;
+ }
+
+ .xterm-fg-255 {
+ color: $xterm-fg-255;
+ }
+}
diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss
index 6c555aee20a..f0acb78f731 100644
--- a/app/assets/stylesheets/pages/admin.scss
+++ b/app/assets/stylesheets/pages/admin.scss
@@ -4,3 +4,7 @@
padding-bottom: 46px;
}
}
+
+.usage-data {
+ max-height: 400px;
+}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index a68b47b1d02..c6074eb9df4 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -90,20 +90,14 @@
}
.with-performance-bar & {
- height: calc(
- 100vh - #{$issue-board-list-difference-xs} - #{$performance-bar-height}
- );
+ height: calc(100vh - #{$issue-board-list-difference-xs} - #{$performance-bar-height});
@include media-breakpoint-only(sm) {
- height: calc(
- 100vh - #{$issue-board-list-difference-sm} - #{$performance-bar-height}
- );
+ height: calc(100vh - #{$issue-board-list-difference-sm} - #{$performance-bar-height});
}
@include media-breakpoint-up(md) {
- height: calc(
- 100vh - #{$issue-board-list-difference-md} - #{$performance-bar-height}
- );
+ height: calc(100vh - #{$issue-board-list-difference-md} - #{$performance-bar-height});
}
}
}
@@ -136,15 +130,23 @@
right: 0;
bottom: 0;
left: 0;
+
+ button {
+ display: none;
+ }
}
.board-title {
padding: 0;
border-bottom: 0;
+ justify-content: center;
> span {
+ width: 100%;
+ margin-top: -12px;
display: block;
- transform: rotate(90deg) translate(35px, 10px);
+ transform: rotate(90deg) translate(35px, 0);
+ overflow: initial;
}
}
@@ -225,7 +227,7 @@
outline: 0;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
}
@@ -263,9 +265,9 @@
height: 100%;
width: 100%;
margin-bottom: 0;
- padding: 5px;
+ padding: $gl-padding-4;
list-style: none;
- overflow-y: scroll;
+ overflow-y: auto;
overflow-x: hidden;
}
@@ -276,132 +278,140 @@
.board-card {
position: relative;
- padding: 11px 10px 11px $gl-padding;
+ padding: $gl-padding;
background: $white-light;
border-radius: $border-radius-default;
+ border: 1px solid $theme-gray-200;
box-shadow: 0 1px 2px $issue-boards-card-shadow;
list-style: none;
+ line-height: $gl-padding;
&:not(:last-child) {
- margin-bottom: 5px;
+ margin-bottom: $gl-padding-8;
}
&.is-active,
&.is-active .board-card-assignee:hover a {
- background-color: $row-hover;
+ background-color: $blue-50;
}
.badge {
border: 0;
outline: 0;
+
+ &:hover {
+ text-decoration: underline;
+ }
+
+ @include media-breakpoint-down(lg) {
+ font-size: $gl-font-size-xs;
+ padding-left: $gl-padding-4;
+ padding-right: $gl-padding-4;
+ font-weight: $gl-font-weight-bold;
+ }
+ }
+
+ svg {
+ vertical-align: top;
}
.confidential-icon {
- position: relative;
- top: 1px;
- margin-right: 5px;
+ color: $orange-600;
+ cursor: help;
+ }
+
+ @include media-breakpoint-down(md) {
+ padding: $gl-padding-8;
}
}
.board-card-title {
@include overflow-break-word();
- margin: 0 30px 0 0;
font-size: 1em;
- line-height: inherit;
a {
color: $gl-text-color;
- margin-right: 2px;
+ }
+
+ @include media-breakpoint-down(md) {
+ font-size: $label-font-size;
}
}
.board-card-header {
display: flex;
- min-height: 20px;
-
- .board-card-assignee {
- display: flex;
- justify-content: flex-end;
- position: absolute;
- right: 15px;
- height: 20px;
- width: 20px;
+}
- .avatar-counter {
- display: none;
- vertical-align: middle;
- min-width: 20px;
- line-height: 19px;
- height: 20px;
- padding-left: 2px;
- padding-right: 2px;
- border-radius: 2em;
- }
+.board-card-assignee {
+ display: flex;
+ margin-top: -$gl-padding-4;
+ margin-bottom: -$gl-padding-4;
+
+ .avatar-counter {
+ vertical-align: middle;
+ line-height: $gl-padding-24;
+ min-width: $gl-padding-24;
+ height: $gl-padding-24;
+ border-radius: $gl-padding-24;
+ background-color: $gl-text-color-tertiary;
+ font-size: $gl-font-size-xs;
+ cursor: help;
+ font-weight: $gl-font-weight-bold;
+ margin-left: -$gl-padding-4;
+ border: 0;
+ padding: 0 $gl-padding-4;
- img {
- vertical-align: top;
+ @include media-breakpoint-down(md) {
+ min-width: auto;
+ height: $gl-padding;
+ border-radius: $gl-padding;
+ line-height: $gl-padding;
}
+ }
- a {
- position: relative;
- margin-left: -15px;
- }
+ img {
+ vertical-align: top;
+ }
- a:nth-child(1) {
- z-index: 3;
- }
+ .user-avatar-link:not(:only-child) {
+ margin-left: -$gl-padding-4;
- a:nth-child(2) {
+ &:nth-of-type(1) {
z-index: 2;
}
- a:nth-child(3) {
+ &:nth-of-type(2) {
z-index: 1;
}
+ }
- a:nth-child(4) {
- display: none;
- }
-
- &:hover {
- .avatar-counter {
- display: inline-block;
- }
-
- a {
- position: static;
- background-color: $white-light;
- transition: background-color 0s;
- margin-left: auto;
-
- &:nth-child(4) {
- display: block;
- }
+ .avatar {
+ margin: 0;
- &:first-child:not(:only-child) {
- box-shadow: -10px 0 10px 1px $white-light;
- }
- }
+ @include media-breakpoint-down(md) {
+ width: $gl-padding;
+ height: $gl-padding;
}
}
- .avatar {
- margin: 0;
+ @include media-breakpoint-down(md) {
+ margin-top: 0;
+ margin-bottom: 0;
}
}
-.board-card-footer {
- margin: 0 0 5px;
+.board-card-number {
+ font-size: $gl-font-size-xs;
+ color: $gl-text-color-secondary;
+ overflow: hidden;
- .badge {
- margin-top: 5px;
- margin-right: 6px;
+ @include media-breakpoint-up(md) {
+ font-size: $label-font-size;
}
}
-.board-card-number {
- font-size: 12px;
- color: $gl-text-color-secondary;
+.board-card-number-container {
+ overflow: hidden;
}
.issue-boards-search {
@@ -467,8 +477,7 @@
.right-sidebar.right-sidebar-expanded {
&.boards-sidebar-slide-enter-active,
&.boards-sidebar-slide-leave-active {
- transition: width $sidebar-transition-duration,
- padding $sidebar-transition-duration;
+ transition: width $sidebar-transition-duration, padding $sidebar-transition-duration;
}
&.boards-sidebar-slide-enter,
@@ -643,3 +652,36 @@
}
}
}
+
+.board-card-info {
+ color: $gl-text-color-secondary;
+ white-space: nowrap;
+ margin-right: $gl-padding-8;
+
+ &:not(.board-card-weight) {
+ cursor: help;
+ }
+
+ &.board-card-weight {
+ color: $gl-text-color;
+ cursor: pointer;
+
+ &:hover {
+ color: initial;
+ text-decoration: underline;
+ }
+ }
+
+ .board-card-info-icon {
+ color: $theme-gray-600;
+ margin-right: $gl-padding-4;
+ }
+
+ @include media-breakpoint-down(md) {
+ font-size: $label-font-size;
+ }
+}
+
+.board-issue-path.js-show-tooltip {
+ cursor: help;
+}
diff --git a/app/assets/stylesheets/pages/branches.scss b/app/assets/stylesheets/pages/branches.scss
index 49fe50977f5..38fec3f0aa8 100644
--- a/app/assets/stylesheets/pages/branches.scss
+++ b/app/assets/stylesheets/pages/branches.scss
@@ -23,7 +23,7 @@
.bar {
position: absolute;
height: 4px;
- background-color: $divergence-graph-bar-bg;
+ background-color: $gl-gray-200;
}
.bar-behind {
@@ -61,7 +61,7 @@
height: 18px;
margin: 5px 0 0;
float: left;
- background-color: $divergence-graph-separator-bg;
+ background-color: $gl-gray-200;
}
}
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index e8158cd7f6b..81cb519883b 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -50,35 +50,33 @@
position: relative;
}
- .build-trace {
- background: $black;
- color: $gray-darkest;
- white-space: pre;
- overflow-x: auto;
- font-size: 12px;
- border-radius: 0;
- border: 0;
- padding: $grid-size;
- .bash {
- display: block;
- }
+ .build-trace {
+ @include build-trace();
+ }
- &.build-trace-rounded {
- border-radius: $border-radius-base;
+ .archived-sticky {
+ top: $header-height;
+ border-radius: 2px 2px 0 0;
+ color: $orange-600;
+ background-color: $orange-100;
+ border: 1px solid $border-gray-normal;
+ border-bottom: 0;
+ padding: 3px 12px;
+ margin: auto;
+ align-items: center;
+
+ .with-performance-bar & {
+ top: $header-height + $performance-bar-height;
}
}
.top-bar {
- height: 35px;
- min-height: 35px;
- background: $gray-light;
- border: 1px solid $border-color;
- color: $gl-text-color;
- position: sticky;
- position: -webkit-sticky;
- top: $header-height;
- padding: $grid-size;
+ @include build-trace-top-bar(35px);
+
+ &.has-archived-block {
+ top: $header-height + $performance-bar-height + 28px;
+ }
&.affix {
top: $header-height;
@@ -116,50 +114,7 @@
}
.controllers {
- display: flex;
- font-size: 15px;
- justify-content: center;
- align-items: center;
-
- svg {
- height: 15px;
- display: block;
- fill: $gl-text-color;
- }
-
- .controllers-buttons {
- color: $gl-text-color;
- margin: 0 $grid-size;
-
- &:last-child {
- margin-right: 0;
- }
- }
-
- .btn-scroll.animate {
- .first-triangle {
- animation: blinking-scroll-button 1s ease infinite;
- animation-delay: 0.3s;
- }
-
- .second-triangle {
- animation: blinking-scroll-button 1s ease infinite;
- animation-delay: 0.2s;
- }
-
- .third-triangle {
- animation: blinking-scroll-button 1s ease infinite;
- }
-
- &:disabled {
- opacity: 1;
- }
- }
-
- .btn-scroll:disabled {
- opacity: 0.35;
- cursor: not-allowed;
- }
+ @include build-controllers(15px, center, false, 0, inline, 0);
}
}
@@ -179,16 +134,13 @@
.build-loader-animation {
@include build-loader-animation;
+ float: left;
}
}
.with-performance-bar .build-page {
- .top-bar {
+ .top-bar.affix {
top: $header-height + $performance-bar-height;
-
- &.affix {
- top: $header-height + $performance-bar-height;
- }
}
}
@@ -221,7 +173,7 @@
color: $gl-text-color;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: none;
}
}
@@ -328,23 +280,6 @@
}
}
- .build-dropdown {
- margin: $gl-padding 0;
- padding: 0;
-
- .dropdown-menu-toggle {
- margin-top: #{$gl-padding / 2};
- }
-
- svg {
- position: relative;
- top: 3px;
- margin-right: 3px;
- width: 14px;
- height: 14px;
- }
- }
-
.builds-container {
background-color: $white-light;
border-top: 1px solid $border-color;
@@ -381,15 +316,11 @@
position: absolute;
left: 15px;
top: 20px;
- display: none;
+ display: block;
}
&.active {
font-weight: $gl-font-weight-bold;
-
- .icon-arrow-right {
- display: block;
- }
}
&.retried {
@@ -397,7 +328,7 @@
}
&:hover {
- background-color: $dropdown-item-hover-bg;
+ background-color: $gray-darker;
}
.icon-retry {
diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss
index 0f22fe21143..ad12cd101b6 100644
--- a/app/assets/stylesheets/pages/clusters.scss
+++ b/app/assets/stylesheets/pages/clusters.scss
@@ -4,9 +4,66 @@
}
}
-.cluster-applications-table {
- // Wait for the Vue to kick-in and render the applications block
- min-height: 628px;
+.cluster-application-row {
+ background: $gray-lighter;
+
+ &.cluster-application-installed {
+ background: none;
+ }
+
+ .settings-message {
+ padding: $gl-vert-padding $gl-padding-8;
+ }
+}
+
+@media (min-width: map-get($grid-breakpoints, md)) {
+ .cluster-application-list {
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+ }
+
+ .cluster-application-row {
+ border-bottom: 1px solid $border-color;
+ padding: $gl-padding;
+
+ &:last-child {
+ border-bottom: 0;
+ border-bottom-left-radius: calc(#{$border-radius-default} - 1px);
+ border-bottom-right-radius: calc(#{$border-radius-default} - 1px);
+ }
+ }
+}
+
+.cluster-application-logo {
+ border: 3px solid $white-light;
+ box-shadow: 0 0 0 1px $gray-normal;
+
+ &.avatar:hover {
+ border-color: $white-light;
+ }
+}
+
+.cluster-application-warning {
+ font-weight: bold;
+ text-align: center;
+ padding: $gl-padding;
+ border-bottom: 1px solid $white-normal;
+
+ .svg-container {
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: $gl-padding-8;
+ width: 40px;
+ height: 40px;
+ }
+}
+
+.cluster-application-description {
+ flex: 1;
+}
+
+.cluster-application-disabled {
+ opacity: 0.5;
}
.clusters-dropdown-menu {
@@ -22,6 +79,10 @@
padding: $gl-padding-top $gl-padding;
}
+ .card {
+ margin-bottom: $gl-vert-padding;
+ }
+
.empty-state .svg-content img {
width: 145px;
}
@@ -29,6 +90,31 @@
.top-area .nav-controls > .btn.btn-add-cluster {
margin-right: 0;
}
+
+ .clusters-table {
+ background-color: $gray-light;
+ padding: $gl-padding-8;
+ }
+
+ .badge-light {
+ background-color: $white-normal;
+ }
+
+ .gl-responsive-table-row {
+ padding: $gl-padding;
+ border: 0;
+
+ &.table-row-header {
+ background-color: none;
+ border: 0;
+ font-weight: bold;
+ color: $gl-gray-500;
+ }
+ }
+}
+
+.cluster-warning {
+ @include alert-variant(theme-color-level('warning', $alert-bg-level), theme-color-level('warning', $alert-border-level), theme-color-level('warning', $alert-color-level));
}
.gcp-signup-offer {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index bce83bf0dd0..11966931a6c 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -59,7 +59,7 @@
vertical-align: middle;
.stage-cell .stage-container {
- margin: 3px 3px 3px 0;
+ margin: 0 3px 3px 0;
}
.stage-container:last-child {
@@ -223,6 +223,7 @@
}
}
+.clipboard-group,
.commit-sha-group {
display: inline-flex;
@@ -279,7 +280,7 @@
}
&.autodevops-link {
- color: $gl-link-color;
+ color: $blue-600;
}
}
@@ -321,7 +322,7 @@
}
.commit-sha {
- color: $gl-link-color;
+ color: $blue-600;
}
.commit-row-message {
diff --git a/app/assets/stylesheets/pages/convdev_index.scss b/app/assets/stylesheets/pages/convdev_index.scss
index bd338326154..52fcdf4a405 100644
--- a/app/assets/stylesheets/pages/convdev_index.scss
+++ b/app/assets/stylesheets/pages/convdev_index.scss
@@ -80,7 +80,7 @@ $space-between-cards: 8px;
}
.convdev-card-low {
- border-top-color: $color-low-score;
+ border-top-color: $red-400;
.board-card-score-big {
background-color: $red-50;
@@ -88,7 +88,7 @@ $space-between-cards: 8px;
}
.convdev-card-average {
- border-top-color: $color-average-score;
+ border-top-color: $orange-400;
.board-card-score-big {
background-color: $orange-50;
@@ -96,7 +96,7 @@ $space-between-cards: 8px;
}
.convdev-card-high {
- border-top-color: $color-high-score;
+ border-top-color: $green-400;
.board-card-score-big {
background-color: $green-50;
@@ -243,13 +243,13 @@ $space-between-cards: 8px;
}
.convdev-high-score {
- color: $color-high-score;
+ color: $green-400;
}
.convdev-average-score {
- color: $color-average-score;
+ color: $orange-400;
}
.convdev-low-score {
- color: $color-low-score;
+ color: $red-400;
}
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index e2c0a7a6225..ec2108b15be 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -21,7 +21,7 @@
}
}
- svg {
+ .svg-container svg {
width: 136px;
height: 136px;
}
@@ -165,7 +165,7 @@
border-right-color: transparent;
border-top-color: $border-color;
border-bottom-color: $border-color;
- box-shadow: inset 2px 0 0 0 $active-item-blue;
+ box-shadow: inset 2px 0 0 0 $blue-500;
.stage-name {
font-weight: $gl-font-weight-bold;
@@ -285,7 +285,7 @@
.total-time {
font-size: $cycle-analytics-big-font;
- color: $cycle-analytics-dark-text;
+ color: $gl-text-color;
span {
color: $gl-text-color;
@@ -360,7 +360,7 @@
}
.commit-sha {
- color: $gl-link-color;
+ color: $blue-600;
line-height: 1.3;
vertical-align: top;
font-weight: $gl-font-weight-normal;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 591e21243ed..0163c795074 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -25,17 +25,13 @@
color: $gl-text-color;
border-radius: 0 0 3px 3px;
- .code {
- padding: 0;
- }
-
.unfold {
cursor: pointer;
}
.file-mode-changed {
padding: 10px;
- color: $file-mode-changed;
+ color: $gl-gray-500;
}
.suppressed-container {
@@ -63,6 +59,7 @@
margin: 0;
padding: 0;
table-layout: fixed;
+ border-radius: 0 0 $border-radius-default $border-radius-default;
.diff-line-num {
width: 50px;
@@ -76,6 +73,7 @@
.line_holder td {
line-height: $code-line-height;
font-size: $code-font-size;
+ vertical-align: top;
&.noteable_line {
position: relative;
@@ -186,7 +184,7 @@
}
.image {
- background: $file-image-bg;
+ background: $gray-darker;
text-align: center;
padding: 30px;
@@ -248,26 +246,13 @@
.swipe-wrap {
overflow: hidden;
- border-left: 1px solid $diff-swipe-border;
+ border-left: 1px solid $gl-gray-400;
position: absolute;
display: block;
top: 13px;
right: 7px;
}
- .frame {
- top: 0;
- right: 0;
- position: absolute;
-
- &.deleted {
- margin: 0;
- display: block;
- top: 13px;
- right: 7px;
- }
- }
-
.swipe-bar {
display: block;
height: 100%;
@@ -424,28 +409,20 @@
.diff-file-container {
.frame.deleted {
- border: 0;
+ border: 1px solid $deleted;
background-color: inherit;
-
- .image_file img {
- border: 1px solid $deleted;
- }
}
.frame.added {
- border: 0;
+ border: 1px solid $added;
background-color: inherit;
-
- .image_file img {
- border: 1px solid $added;
- }
}
.swipe.view,
.onion-skin.view {
.swipe-wrap {
top: 0;
- right: 0;
+ left: 0;
}
.frame.deleted {
@@ -484,6 +461,11 @@
bottom: -25px;
}
}
+
+ .discussion-notes .discussion-notes {
+ margin-left: 0;
+ border-left: 0;
+ }
}
.file-content .diff-file {
@@ -511,13 +493,13 @@
padding: 0;
background-color: transparent;
border: 0;
- color: $gl-link-color;
+ color: $blue-600;
font-weight: $gl-font-weight-bold;
&:hover,
&:focus {
outline: none;
- color: $gl-link-hover-color;
+ color: $blue-800;
}
.caret-icon {
@@ -574,8 +556,6 @@
}
.files {
- margin-top: 1px;
-
.diff-file:last-child {
margin-bottom: 0;
}
@@ -753,6 +733,10 @@
left: $gl-padding;
}
+ .dropdown-input .dropdown-input-search {
+ pointer-events: all;
+ }
+
.diff-changed-file {
display: flex;
padding-top: 8px;
@@ -805,7 +789,7 @@
// double jagged line divider
.discussion-notes + .discussion-notes::before,
- .discussion-notes + .discussion-form::before {
+ .diff-file-discussions + .discussion-form::before {
content: '';
position: relative;
display: block;
@@ -845,6 +829,17 @@
background-repeat: repeat;
}
+ .diff-file-discussions + .discussion-form {
+ padding: $gl-padding;
+
+ &::before {
+ width: auto;
+ margin-left: -$gl-padding;
+ margin-right: -$gl-padding;
+ margin-bottom: $gl-padding;
+ }
+ }
+
.notes {
position: relative;
}
@@ -856,7 +851,7 @@
}
.diff-file .note-container > .new-note,
-.note-container .discussion-notes {
+.note-container .discussion-notes.diff-discussions {
margin-left: 100px;
border-left: 1px solid $white-normal;
}
@@ -871,11 +866,13 @@
}
}
-.files:not([data-can-create-note]) .frame {
+.files:not([data-can-create-note="true"]) .frame {
cursor: auto;
}
-.frame.click-to-comment {
+.frame,
+.frame.click-to-comment,
+.btn-transparent.image-diff-overlay-add-comment {
position: relative;
cursor: image-url('illustrations/image_comment_light_cursor.svg')
$image-comment-cursor-left-offset $image-comment-cursor-top-offset,
@@ -911,6 +908,7 @@
.frame .badge.badge-pill,
.image-diff-avatar-link .badge.badge-pill,
+.user-avatar-link .badge.badge-pill,
.notes > .badge.badge-pill {
position: absolute;
background-color: $blue-400;
@@ -945,7 +943,8 @@
}
}
-.image-diff-avatar-link {
+.image-diff-avatar-link,
+.user-avatar-link {
position: relative;
.badge.badge-pill,
@@ -986,3 +985,102 @@
.discussion-body .image .frame {
position: relative;
}
+
+.diff-tree-list {
+ width: 320px;
+}
+
+.diff-files-holder {
+ flex: 1;
+ min-width: 0;
+}
+
+.compare-versions-container {
+ min-width: 0;
+}
+
+.tree-list-holder {
+ position: -webkit-sticky;
+ position: sticky;
+ top: 100px;
+ max-height: calc(100vh - 100px);
+ padding-right: $gl-padding;
+
+ .file-row {
+ margin-left: 0;
+ margin-right: 0;
+ }
+
+ .with-performance-bar & {
+ top: 135px;
+ max-height: calc(100vh - 135px);
+ }
+}
+
+.tree-list-scroll {
+ max-height: 100%;
+ padding-top: $grid-size;
+ padding-bottom: $grid-size;
+ border-top: 1px solid $border-color;
+ border-bottom: 1px solid $border-color;
+ overflow-y: scroll;
+ overflow-x: auto;
+}
+
+.tree-list-search {
+ flex: 0 0 34px;
+
+ .form-control {
+ padding-left: 30px;
+ }
+}
+
+.tree-list-icon {
+ top: 50%;
+ left: 10px;
+ transform: translateY(-50%);
+
+ &,
+ svg {
+ fill: $gl-text-color-tertiary;
+ }
+}
+
+.tree-list-clear-icon {
+ right: 10px;
+ left: auto;
+ line-height: 0;
+}
+
+@media (max-width: map-get($grid-breakpoints, md)-1) {
+ .diffs .files {
+ @include fixed-width-container;
+ flex-direction: column;
+
+ .diff-tree-list {
+ width: 100%;
+ }
+
+ .tree-list-holder {
+ max-height: calc(50px + 50vh);
+ padding-right: 0;
+ }
+ }
+}
+
+.tree-list-view-toggle {
+ svg {
+ top: 0;
+ }
+}
+
+.image-diff-overlay,
+.image-diff-overlay-add-comment {
+ top: 0;
+ left: 0;
+
+ &:active,
+ &:focus {
+ outline: 0;
+ }
+}
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 892da152b5f..04570c057d1 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -23,10 +23,10 @@
}
.cancel-btn {
- color: $editor-cancel-color;
+ color: $red-600;
&:hover {
- color: $editor-cancel-color;
+ color: $red-600;
}
}
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 8a074017344..347fcad771a 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -44,11 +44,6 @@
margin: 0;
}
- .icon-play {
- height: 13px;
- width: 12px;
- }
-
.external-url,
.dropdown-new {
color: $gl-text-color-secondary;
@@ -90,6 +85,7 @@
margin-right: 3px;
color: $gl-text-color-secondary;
display: inline-block;
+ vertical-align: text-top;
.fa:nth-child(1) {
margin-right: 3px;
@@ -153,7 +149,7 @@
.x-axis path,
.y-axis path {
- stroke: $stat-graph-axis-fill;
+ stroke: $gl-gray-350;
}
.label-x-axis-line,
@@ -163,7 +159,7 @@
.y-axis {
line {
- stroke: $stat-graph-axis-fill;
+ stroke: $gl-gray-350;
stroke-width: 1;
}
}
@@ -365,7 +361,7 @@
}
.arrow-shadow {
- content: "";
+ content: '';
position: absolute;
width: 7px;
height: 7px;
@@ -479,10 +475,10 @@
.deploy-info-text-link {
font-family: $monospace-font;
- fill: $gl-link-color;
+ fill: $blue-600;
&:hover {
- fill: $gl-link-hover-color;
+ fill: $blue-800;
}
}
@@ -501,5 +497,5 @@
}
.prometheus-table-row-highlight {
- background-color: $prometheus-table-row-highlight-color;
+ background-color: $theme-gray-100;
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index f79586b68b9..618f23d81b1 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -4,41 +4,29 @@
*/
.event-item {
font-size: $gl-font-size;
- padding: $gl-padding-top 0 $gl-padding-top 40px;
+ padding: $gl-padding 0 $gl-padding 56px;
border-bottom: 1px solid $white-normal;
- color: $list-text-color;
+ color: $gl-text-color-secondary;
position: relative;
-
- &.event-inline {
- .system-note-image {
- top: 20px;
- }
-
- .user-avatar {
- top: 14px;
- }
-
- .event-title,
- .event-item-timestamp {
- line-height: 40px;
- }
- }
-
- a {
- color: $gl-text-color;
- }
+ line-height: $gl-line-height;
.system-note-image {
position: absolute;
left: 0;
- top: 14px;
svg {
- width: 20px;
- height: 20px;
fill: $gl-text-color-secondary;
}
+ }
+ .system-note-image-inline {
+ svg {
+ fill: $gl-text-color-secondary;
+ }
+ }
+
+ .system-note-image,
+ .system-note-image-inline {
&.opened-icon,
&.created-icon {
svg {
@@ -53,16 +41,35 @@
&.accepted-icon svg {
fill: $blue-300;
}
+
+ &.commented-on-icon svg {
+ fill: $blue-600;
+ }
+ }
+
+ .event-user-info {
+ margin-bottom: $gl-padding-8;
+
+ .author_name {
+ a {
+ color: $gl-text-color;
+ font-weight: $gl-font-weight-bold;
+ }
+ }
}
.event-title {
- @include str-truncated(calc(100% - 174px));
- font-weight: $gl-font-weight-bold;
- color: $list-text-color;
+ .event-type {
+ &::first-letter {
+ text-transform: capitalize;
+ }
+ }
}
.event-body {
+ margin-top: $gl-padding-8;
margin-right: 174px;
+ color: $gl-text-color;
.event-note {
word-wrap: break-word;
@@ -87,12 +94,12 @@
border: 0;
background: $gray-light;
border-radius: 0;
- color: $events-pre-color;
+ color: $gl-gray-500;
overflow: hidden;
}
.note-image-attach {
- margin-top: 4px;
+ margin-top: $gl-padding-4;
margin-left: 0;
max-width: 200px;
float: none;
@@ -104,10 +111,9 @@
}
.event-note-icon {
- color: $events-pre-color;
+ color: $gl-gray-500;
float: left;
font-size: $gl-font-size;
- line-height: 16px;
margin-right: 5px;
}
}
@@ -127,7 +133,9 @@
}
}
- &:last-child { border: 0; }
+ &:last-child {
+ border: 0;
+ }
.event_commits {
li {
@@ -154,7 +162,6 @@
.event-item-timestamp {
float: right;
- line-height: 22px;
}
}
@@ -177,10 +184,8 @@
.event-item {
padding-left: 0;
- &.event-inline {
- .event-title {
- line-height: 20px;
- }
+ .event-user-info {
+ margin-bottom: $gl-padding-4;
}
.event-title {
@@ -194,7 +199,8 @@
}
.event-body {
- margin: 0;
+ margin-top: $gl-padding-4;
+ margin-right: 0;
padding-left: 0;
}
diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss
index 49d8a5d959b..4fb1a956fab 100644
--- a/app/assets/stylesheets/pages/graph.scss
+++ b/app/assets/stylesheets/pages/graph.scss
@@ -20,15 +20,15 @@
.graphs {
.graph-author-email {
float: right;
- color: $graph-author-email-color;
+ color: $gl-gray-500;
}
.graph-additions {
- color: $gl-text-green;
+ color: $green-600;
}
.graph-deletions {
- color: $gl-text-red;
+ color: $red-500;
}
}
@@ -58,7 +58,7 @@
.y-axis-label {
line {
- stroke: $stat-graph-axis-fill;
+ stroke: $gl-gray-350;
}
text {
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index fa8a0f26b5d..f0cb81e0bc3 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -3,7 +3,6 @@
}
.dashboard .side .card .card-header .input-group {
-
.form-control {
height: 42px;
}
@@ -30,14 +29,15 @@
}
}
+.group-nav-container .group-search,
.group-nav-container .nav-controls {
display: flex;
align-items: flex-start;
- padding: $gl-padding-top 0;
- border-bottom: 1px solid $border-color;
+ padding: $gl-padding-top 0 0;
.group-filter-form {
- flex: 1;
+ flex: 1 1 auto;
+ margin-right: $gl-padding-8;
}
.dropdown-menu-right {
@@ -106,7 +106,7 @@
&,
.dropdown,
.dropdown .dropdown-toggle,
- .btn-new {
+ .btn-success {
display: block;
}
@@ -118,7 +118,7 @@
.group-filter-form,
.dropdown .dropdown-toggle,
- .btn-new {
+ .btn-success {
width: 100%;
}
@@ -136,6 +136,10 @@
flex: 1;
}
+ .dropdown-toggle {
+ width: auto;
+ }
+
.dropdown-menu {
width: 100%;
max-width: inherit;
@@ -145,38 +149,14 @@
}
}
-.groups-empty-state {
- padding: 50px 100px;
- overflow: hidden;
-
- @include media-breakpoint-down(sm) {
- padding: 50px 0;
- }
-
- svg {
- float: right;
-
- @include media-breakpoint-down(sm) {
- float: none;
- display: block;
- width: 250px;
- position: relative;
- left: 50%;
- margin-left: -125px;
- }
- }
-
- .text-content {
- float: left;
- width: 460px;
- margin-top: 120px;
+.group-nav-container .group-search {
+ padding: $gl-padding 0;
+ border-bottom: 1px solid $border-color;
+}
- @include media-breakpoint-down(sm) {
- float: none;
- margin-top: 60px;
- width: auto;
- text-align: center;
- }
+.groups-listing {
+ .group-list-tree .group-row:first-child {
+ border-top: 0;
}
}
@@ -211,7 +191,7 @@
}
}
- svg {
+ .svg-container svg {
width: 62px;
height: 50px;
}
@@ -278,12 +258,12 @@
}
&::after {
- content: "";
+ content: '';
position: absolute;
height: 100%;
width: 100%;
background-color: transparent;
- border: 2px outset $kdb-border;
+ border: 2px outset $gl-gray-200;
border-radius: 50%;
animation: spin-avatar 3s infinite linear;
}
@@ -346,7 +326,7 @@
position: relative;
&::before {
- content: "";
+ content: '';
display: block;
width: 10px;
height: 0;
@@ -385,8 +365,8 @@
padding: $gl-padding-top;
&:hover {
- border-color: $row-hover-border;
- background-color: $row-hover;
+ border-color: $blue-200;
+ background-color: $blue-50;
cursor: pointer;
}
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index 0350fe5752e..2c23f31c240 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -1,6 +1,6 @@
.shortcut-mappings {
font-size: 12px;
- color: $help-shortcut-mapping-color;
+ color: $gl-gray-700;
tbody:first-child tr:first-child {
padding-top: 0;
@@ -22,7 +22,7 @@
.shortcut {
padding-right: 10px;
- color: $help-shortcut-color;
+ color: $gl-gray-400;
text-align: right;
white-space: nowrap;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 8e78d9f65eb..38851de6401 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -1,8 +1,6 @@
// Limit MR description for side-by-side diff view
.fixed-width-container {
- max-width: $limited-layout-width - ($gl-padding * 2);
- margin-left: auto;
- margin-right: auto;
+ @include fixed-width-container;
}
.issuable-warning-icon {
@@ -49,12 +47,6 @@
@extend .fixed-width-container;
}
}
-
- .diffs {
- .mr-version-controls {
- @extend .fixed-width-container;
- }
- }
}
.issuable-details {
@@ -141,10 +133,10 @@
color: inherit;
&:hover {
- color: $gl-link-hover-color;
+ color: $blue-800;
.avatar {
- border-color: rgba($avatar-border, .2);
+ border-color: rgba($gray-normal, .2);
}
}
@@ -231,7 +223,7 @@
}
a.edit-link:not([href]):hover {
- color: rgba($avatar-border, .2);
+ color: rgba($gray-normal, .2);
}
.lock-edit, // uses same style, different js behaviour
@@ -241,7 +233,7 @@
&:hover {
text-decoration: underline;
- color: $gl-link-hover-color;
+ color: $blue-800;
}
}
}
@@ -329,7 +321,7 @@
}
.btn-secondary-hover-link:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
.sidebar-collapsed-icon {
@@ -423,10 +415,10 @@
width: 100%;
text-align: center;
margin-bottom: 10px;
- color: $issuable-sidebar-color;
+ color: $gl-text-color-secondary;
svg {
- fill: $issuable-sidebar-color;
+ fill: $gl-text-color-secondary;
}
&:hover:not(.disabled),
@@ -448,8 +440,8 @@
}
.todo-undone {
- color: $gl-link-color;
- fill: $gl-link-color;
+ color: $blue-600;
+ fill: $blue-600;
}
.author {
@@ -457,14 +449,14 @@
}
.avatar-counter:hover {
- color: $issuable-sidebar-color;
- border-color: $issuable-sidebar-color;
+ color: $gl-text-color-secondary;
+ border-color: $gl-text-color-secondary;
}
.btn-clipboard {
border: 0;
background: transparent;
- color: $issuable-sidebar-color;
+ color: $gl-text-color-secondary;
&:hover {
color: $gl-text-color;
@@ -701,6 +693,11 @@
align-self: center;
overflow: hidden;
text-overflow: ellipsis;
+
+ .user-status-emoji {
+ margin-left: $gl-padding-4;
+ margin-right: 0;
+ }
}
.js-issuable-selector-wrap {
@@ -721,15 +718,19 @@
display: flex;
}
- .issue-info-container {
+ .issuable-info-container {
-webkit-flex: 1;
flex: 1;
display: flex;
padding-right: $gl-padding;
- .issue-main-info {
+ .issuable-main-info {
flex: 1 auto;
margin-right: 10px;
+
+ .issue-weight-icon {
+ vertical-align: sub;
+ }
}
.issuable-meta {
@@ -763,7 +764,7 @@
margin-bottom: 10px;
min-width: 15px;
- .selected_issue {
+ .selected-issuable {
vertical-align: text-top;
}
}
@@ -795,7 +796,7 @@
}
.issuable-list li,
-.issue-info-container .controls {
+.issuable-info-container .controls {
.avatar-counter {
display: inline-block;
vertical-align: middle;
@@ -821,7 +822,7 @@
svg {
width: 16px;
height: 16px;
- fill: $issuable-sidebar-color;
+ fill: $gl-text-color-secondary;
}
&:hover svg {
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 212e5979273..8ea34f5d19d 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -157,7 +157,7 @@ ul.related-merge-requests > li {
.issuable-email-modal-btn {
padding: 0;
- color: $gl-link-color;
+ color: $blue-600;
background-color: transparent;
border: 0;
outline: 0;
@@ -185,12 +185,22 @@ ul.related-merge-requests > li {
}
.new-branch-col {
- padding-top: 10px;
+ font-size: 0;
+
+ .discussion-filter-container {
+ &:not(:only-child) {
+ margin-right: $gl-padding-8;
+ }
+
+ @include media-breakpoint-down(md) {
+ margin-top: $gl-padding-8;
+ }
+ }
}
.create-mr-dropdown-wrap {
.ref::selection {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
.dropdown {
@@ -205,6 +215,10 @@ ul.related-merge-requests > li {
.btn-group:not(.hidden) {
display: flex;
+
+ @include media-breakpoint-down(md) {
+ margin-top: $gl-padding-8;
+ }
}
.js-create-merge-request {
@@ -251,7 +265,6 @@ ul.related-merge-requests > li {
.new-branch-col {
padding-top: 0;
- text-align: right;
align-self: center;
}
@@ -262,3 +275,9 @@ ul.related-merge-requests > li {
}
}
}
+
+@include media-breakpoint-up(lg) {
+ .new-branch-col {
+ text-align: right;
+ }
+}
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index b25dc4f419a..d2b9470be69 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -67,7 +67,7 @@
.dropdown-labels-error {
padding: 5px 10px;
margin-bottom: 10px;
- background-color: $gl-danger;
+ background-color: $red-500;
color: $white-light;
}
@@ -114,10 +114,10 @@
}
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
&.remove-row {
- color: $gl-danger;
+ color: $red-500;
}
}
}
@@ -343,10 +343,10 @@
&.remove-row {
&:hover {
- color: $gl-text-red;
+ color: $red-500;
svg {
- fill: $gl-text-red;
+ fill: $red-500;
}
}
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 8a4a2caa6c9..fa0ab1a3bae 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -100,6 +100,22 @@
p {
margin: 0;
}
+
+ .omniauth-btn {
+ margin-bottom: $gl-padding;
+ width: 48%;
+ padding: $gl-padding-8;
+
+ @include media-breakpoint-down(md) {
+ width: 100%;
+ }
+
+ img {
+ width: $default-icon-size;
+ height: $default-icon-size;
+ margin-right: $gl-padding;
+ }
+ }
}
.new-session-tabs {
@@ -169,10 +185,6 @@
}
}
- label {
- font-weight: $gl-font-weight-normal;
- }
-
.submit-container {
margin-top: 16px;
}
@@ -186,7 +198,7 @@
h2 {
margin-top: 0;
font-size: 14px;
- color: $login-devise-error-color;
+ color: $red-700;
}
}
}
@@ -200,15 +212,6 @@
}
}
-.oauth-image-link {
- margin-right: 10px;
-
- img {
- width: 32px;
- height: 32px;
- }
-}
-
.devise-layout-html {
margin: 0;
padding: 0;
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index 5fdb2b4a90a..99609a96976 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -4,7 +4,7 @@
}
.users-project-form {
- .btn-create {
+ .btn-success {
margin-right: 10px;
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 621321101cd..fa6afbf81de 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -47,7 +47,6 @@
}
}
-
.mr-widget-heading {
position: relative;
border: 1px solid $border-color;
@@ -195,12 +194,12 @@
.ci-widget-content {
display: flex;
align-items: center;
+ flex: 1;
}
}
.mr-widget-icon {
font-size: 22px;
- margin-right: $status-icon-margin;
}
.ci-status-icon svg {
@@ -222,6 +221,7 @@
.normal {
flex: 1;
+ flex-basis: auto;
}
.capitalize {
@@ -235,22 +235,23 @@
font-weight: normal;
overflow: hidden;
word-break: break-all;
+ }
- &.label-truncated {
- position: relative;
- display: inline-block;
- width: 250px;
- margin-bottom: -3px;
- white-space: nowrap;
- text-overflow: clip;
- line-height: 14px;
-
- &::after {
- position: absolute;
- content: '...';
- right: 0;
- font-family: $regular-font;
- background-color: $gray-light;
+ .deploy-link,
+ .label-branch {
+ &.label-truncate {
+ // NOTE: This selector targets its children because some of the HTML comes from
+ // 'source_branch_link'. Once this external HTML is no longer used, we could
+ // simplify this.
+ > a,
+ > span {
+ display: inline-block;
+ max-width: 12.5em;
+ margin-bottom: -3px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ line-height: 14px;
+ overflow: hidden;
}
}
}
@@ -281,7 +282,7 @@
margin-bottom: 0;
&.has-conflicts .fa-exclamation-triangle {
- color: $gl-warning;
+ color: $orange-500;
}
time {
@@ -313,7 +314,7 @@
}
.danger {
- color: $gl-danger;
+ color: $red-500;
}
.spacing,
@@ -452,12 +453,12 @@
.mr-list {
.merge-request {
- padding: 10px 0 10px 15px;
+ padding: 10px 0 10px 15px;
position: relative;
display: -webkit-flex;
display: flex;
- .issue-info-container {
+ .issuable-info-container {
-webkit-flex: 1;
flex: 1;
}
@@ -466,7 +467,6 @@
margin-bottom: 2px;
.ci-status-link {
-
svg {
height: 16px;
width: 16px;
@@ -514,7 +514,7 @@
}
.mr-links {
- padding-left: $status-icon-size + $status-icon-margin;
+ padding-left: $status-icon-size + $gl-btn-padding;
}
.mr-info-list {
@@ -582,7 +582,7 @@
@include media-breakpoint-down(md) {
flex-direction: column;
- align-items: flex-start;
+ align-items: stretch;
.branch-actions {
margin-top: 16px;
@@ -593,13 +593,13 @@
.branch-actions {
align-self: center;
margin-left: $gl-padding;
+ white-space: nowrap;
}
}
}
.diverged-commits-count {
color: $gl-text-color-secondary;
- font-size: 12px;
}
}
@@ -696,7 +696,6 @@
.table-holder {
.ci-table {
-
th {
background-color: $white-light;
color: $gl-text-color-secondary;
@@ -720,6 +719,17 @@
align-items: center;
padding: 16px;
z-index: 199;
+ white-space: nowrap;
+
+ .dropdown-menu-toggle {
+ width: auto;
+ max-width: 170px;
+
+ svg {
+ top: 10px;
+ right: 8px;
+ }
+ }
}
.content-block {
@@ -762,7 +772,7 @@
&.affix {
left: 0;
- transition: right .15s;
+ transition: right 0.15s;
@include media-breakpoint-down(xs) {
right: 0;
@@ -808,9 +818,17 @@
display: flex;
justify-content: space-between;
- @include media-breakpoint-down(xs) {
+ @include media-breakpoint-down(md) {
flex-direction: column-reverse;
}
+
+ .discussion-filter-container {
+ margin-top: $gl-padding-8;
+
+ &:not(:only-child) {
+ padding-right: $gl-padding-8;
+ }
+ }
}
.limit-container-width:not(.container-limited) {
@@ -871,7 +889,7 @@
}
> *:not(:last-child) {
- margin-right: .3em;
+ margin-right: 0.3em;
}
svg {
@@ -894,6 +912,10 @@
.btn svg {
fill: $theme-gray-700;
}
+
+ .dropdown-menu {
+ width: 400px;
+ }
}
// Hack alert: we've rewritten `btn` class in a way that
@@ -904,10 +926,10 @@
&[disabled] {
cursor: not-allowed;
box-shadow: none;
- opacity: .65;
+ opacity: 0.65;
&:hover {
- color: $file-mode-changed;
+ color: $gl-gray-500;
text-decoration: none;
}
}
@@ -918,7 +940,7 @@
flex: 1;
flex-direction: row;
- @include media-breakpoint-down(md) {
+ @include media-breakpoint-down(sm) {
flex-direction: column;
.stage-cell .stage-container {
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 46437ce5841..1e92582d6d9 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -30,7 +30,7 @@
.milestone-progress {
a {
- color: $gl-link-color;
+ color: $blue-600;
}
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 8acd64ca1a1..97b3f696139 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -2,7 +2,7 @@
* Note Form
*/
.comment-btn {
- @extend .btn-create;
+ @extend .btn-success;
}
.diff-file .diff-content {
@@ -74,13 +74,13 @@
}
&.is-dropzone-hover {
- border-color: $gl-success;
+ border-color: $green-500;
box-shadow: 0 0 2px $black-transparent,
- 0 0 4px $gl-success-focus;
+ 0 0 4px $green-500-focus;
.comment-toolbar,
.nav-links {
- border-color: $gl-success;
+ border-color: $green-500;
}
}
}
@@ -176,8 +176,10 @@
background-color: $white-light;
}
-.discussion-form-container {
- padding: $gl-padding-top $gl-padding $gl-padding;
+table {
+ .discussion-form-container {
+ padding: $gl-padding-top $gl-padding $gl-padding;
+ }
}
.discussion-notes .disabled-comment {
@@ -239,6 +241,7 @@
.discussion-reply-holder {
background-color: $white-light;
padding: 10px 16px;
+ border-radius: 0 0 3px 3px;
&.is-replying {
padding-bottom: $gl-padding;
@@ -247,10 +250,15 @@
}
.discussion-with-resolve-btn {
+ @include media-breakpoint-up(sm) {
+ display: flex;
+ }
+
+
.discussion-actions {
display: table;
- .btn-default path {
+ svg {
fill: $gray-darkest;
}
@@ -270,6 +278,12 @@
.btn {
width: 100%;
}
+
+ .btn-text-field {
+ @include media-breakpoint-down(xs) {
+ margin-bottom: $gl-padding-8;
+ }
+ }
}
.discussion-notes-count {
@@ -306,7 +320,7 @@
&:hover,
&:focus {
- color: $gl-link-color;
+ color: $blue-600;
outline: 0;
}
@@ -340,6 +354,8 @@
}
.note-form-actions {
+ color: $gl-text-color;
+
@include media-breakpoint-down(xs) {
.btn {
float: none;
@@ -424,7 +440,7 @@
.uploading-error-icon,
.uploading-error-message {
- color: $gl-text-red;
+ color: $red-500;
}
.uploading-error-message {
@@ -443,7 +459,7 @@
.attach-new-file,
.button-attach-file,
.retry-uploading-link {
- color: $gl-link-color;
+ color: $blue-600;
padding: 0;
background: none;
border: 0;
@@ -452,5 +468,5 @@
}
.markdown-selector {
- color: $gl-link-color;
+ color: $blue-600;
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index c369d89d63c..085ff27e6ef 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -1,26 +1,139 @@
-/**
- * Notes
- */
-
-@-webkit-keyframes targe3-note {
- from {
- background: $note-targe3-outside;
+$system-note-icon-size: 32px;
+$system-note-svg-size: 16px;
+$note-form-margin-left: 72px;
+
+@mixin vertical-line($left) {
+ &::before {
+ content: '';
+ border-left: 2px solid $theme-gray-100;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: $left;
}
+}
- 50% {
- background: $note-targe3-inside;
- }
+@mixin outline-comment() {
+ margin: $gl-padding;
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+}
+
+.note-wrapper {
+ padding: $gl-padding;
- to {
- background: $note-targe3-outside;
+ &.outlined {
+ @include outline-comment();
}
}
-ul.notes {
+.main-notes-list {
+ @include vertical-line(36px);
+}
+
+.notes {
display: block;
list-style: none;
margin: 0;
padding: 0;
+ position: relative;
+
+ &.timeline > .timeline-entry {
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+ margin: $gl-padding 0;
+
+ &.system-note,
+ &.note-form {
+ border: 0;
+ }
+
+ &.note-form {
+ margin-left: 0;
+
+ @include notes-media('min', map-get($grid-breakpoints, md)) {
+ margin-left: $note-form-margin-left;
+ }
+
+ .timeline-icon {
+ @include notes-media('min', map-get($grid-breakpoints, sm)) {
+ margin-left: -$note-icon-gutter-width;
+ }
+ }
+
+ .timeline-content {
+ margin-left: 0;
+ }
+ }
+
+ .notes_content {
+ border: 0;
+ border-top: 1px solid $border-color;
+ }
+ }
+
+ > .note-discussion {
+ .card {
+ border: 0;
+ }
+
+ li.note {
+ border-bottom: 1px solid $border-color;
+ }
+ }
+
+ .replies-toggle {
+ background-color: $gray-light;
+ padding: $gl-padding-8 $gl-padding;
+
+ .collapse-replies-btn:hover {
+ color: $blue-600;
+ }
+
+ &.expanded {
+ border-bottom: 1px solid $border-color;
+
+ span {
+ cursor: pointer;
+ }
+
+ svg {
+ position: relative;
+ top: 3px;
+ }
+ }
+
+ &.collapsed {
+ color: $gl-text-color-secondary;
+
+ svg {
+ float: left;
+ position: relative;
+ top: $gl-padding-4;
+ margin-right: $gl-padding-8;
+ cursor: pointer;
+ }
+
+ img {
+ margin: -2px 4px 0 0;
+ }
+
+ .author-link {
+ color: $gl-text-color;
+ }
+ }
+
+ .user-avatar-link {
+ &:last-child img {
+ margin-right: $gl-padding-8;
+ }
+ }
+
+ .btn-link {
+ border: 0;
+ vertical-align: baseline;
+ }
+ }
.note-created-ago,
.note-updated-at {
@@ -28,8 +141,6 @@ ul.notes {
}
.discussion-body {
- padding-top: 8px;
-
.card {
margin-bottom: 0;
}
@@ -46,21 +157,10 @@ ul.notes {
}
> li {
- // .timeline-entry
- padding: 0;
display: block;
position: relative;
border-bottom: 0;
- @include notes-media('min', map-get($grid-breakpoints, sm)) {
- padding-left: $note-icon-gutter-width;
- }
-
- .timeline-entry-inner {
- padding: $gl-padding $gl-btn-padding;
- border-bottom: 1px solid $white-normal;
- }
-
&:target,
&.target {
border-bottom: 1px solid $white-normal;
@@ -75,27 +175,14 @@ ul.notes {
}
}
- .timeline-icon {
- @include notes-media('min', map-get($grid-breakpoints, sm)) {
- margin-left: -$note-icon-gutter-width;
- }
- }
-
- .timeline-content {
- margin-left: $note-icon-gutter-width;
-
- @include notes-media('min', map-get($grid-breakpoints, sm)) {
- margin-left: 0;
- }
- }
-
&.being-posted {
pointer-events: none;
opacity: 0.5;
+ padding: $gl-padding;
.dummy-avatar {
- background-color: $kdb-border;
- border: 1px solid darken($kdb-border, 25%);
+ background-color: $gl-gray-200;
+ border: 1px solid darken($gl-gray-200, 25%);
}
.note-headline-light,
@@ -104,12 +191,6 @@ ul.notes {
}
}
- &.note-discussion {
- .timeline-entry-inner {
- padding: $gl-padding 10px;
- }
- }
-
.editing-spinner {
display: none;
}
@@ -191,8 +272,9 @@ ul.notes {
}
.system-note {
- font-size: 14px;
- clear: both;
+ padding: 6px 21px;
+ margin: $gl-padding-24 0;
+ background-color: transparent;
.note-header-info {
padding-bottom: 0;
@@ -210,7 +292,7 @@ ul.notes {
}
a {
- color: $gl-link-color;
+ color: $blue-600;
}
p {
@@ -225,17 +307,21 @@ ul.notes {
.timeline-icon {
float: left;
-
- @include notes-media('min', map-get($grid-breakpoints, sm)) {
- margin-left: 0;
- width: auto;
- }
+ display: flex;
+ align-items: center;
+ background-color: $white-light;
+ width: $system-note-icon-size;
+ height: $system-note-icon-size;
+ border: 1px solid $border-color;
+ border-radius: $system-note-icon-size;
+ margin: -6px $gl-padding 0 0;
svg {
- width: 16px;
- height: 16px;
+ width: $system-note-svg-size;
+ height: $system-note-svg-size;
fill: $gray-darkest;
- margin-top: 2px;
+ display: block;
+ margin: 0 auto;
}
}
@@ -253,14 +339,14 @@ ul.notes {
overflow: hidden;
.system-note-commit-list-toggler {
- color: $gl-link-color;
+ color: $blue-600;
padding: 10px 0 0;
cursor: pointer;
position: relative;
z-index: 2;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: underline;
}
}
@@ -302,10 +388,18 @@ ul.notes {
.discussion-body .diff-file {
.file-title {
cursor: default;
+ line-height: 42px;
+ padding: 0 $gl-padding;
+ border-top: 1px solid $border-color;
+ border-radius: 0;
&:hover {
background-color: $gray-light;
}
+
+ .btn-clipboard {
+ top: 10px;
+ }
}
.line_content {
@@ -313,6 +407,24 @@ ul.notes {
}
}
+.tab-pane.notes {
+ .diff-file .notes .system-note {
+ margin: 0;
+ }
+}
+
+.tab-pane.diffs {
+ .system-note {
+ padding: 0 $gl-padding;
+ margin-left: 20px;
+ }
+
+ .notes > .note-discussion li.note.system-note {
+ border-bottom: 0;
+ padding: 0 $gl-padding;
+ }
+}
+
.diff-file {
.is-over {
.add-diff-note {
@@ -320,6 +432,17 @@ ul.notes {
}
}
+ .discussion-notes {
+ &:not(:last-child) {
+ margin-bottom: 0;
+ }
+
+ .system-note {
+ background-color: $white-light;
+ padding: $gl-padding;
+ }
+ }
+
// Merge request notes in diffs
// Diff is inline
.notes_content .note-header .note-headline-light {
@@ -334,22 +457,7 @@ ul.notes {
border: 1px solid $white-normal;
border-left: 0;
- &.notes_line {
- vertical-align: middle;
- text-align: center;
- padding: 10px 0;
- background: $gray-light;
- color: $text-color;
- }
-
- &.notes_line2 {
- text-align: center;
- padding: 10px 0;
- border-left: 1px solid $note-line2-border !important;
- }
-
&.notes_content {
- background-color: $gray-light;
border-width: 1px 0;
padding: 0;
vertical-align: top;
@@ -357,17 +465,9 @@ ul.notes {
&.parallel {
border-width: 1px;
- }
-
- .discussion-notes {
- &:not(:first-child) {
- border-top: 1px solid $white-normal;
- margin-top: 20px;
- }
- &:not(:last-child) {
- border-bottom: 1px solid $white-normal;
- margin-bottom: 20px;
+ &.new {
+ border-right-width: 0;
}
}
@@ -384,13 +484,54 @@ ul.notes {
}
}
+.diffs {
+ .discussion-notes {
+ margin-left: 0;
+ border-left: 0;
+
+ .notes {
+ position: relative;
+ @include vertical-line(52px);
+ }
+ }
+
+ .note-wrapper {
+ @include outline-comment();
+
+ &.system-note {
+ border: 0;
+ margin-left: 20px;
+ }
+ }
+
+ .discussion-reply-holder {
+ border-radius: 0 0 $border-radius-default $border-radius-default;
+ border-top: 1px solid $border-color;
+ position: relative;
+ }
+}
+
+.commit-diff {
+ .notes {
+ @include vertical-line(52px);
+ }
+
+ .notes_content {
+ background-color: $white-light;
+ }
+
+ .discussion-reply-holder {
+ border-top: 1px solid $border-color;
+ }
+}
+
.discussion-header,
.note-header-info {
a {
color: inherit;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
&:focus,
@@ -409,7 +550,17 @@ ul.notes {
}
.discussion-header {
- font-size: 14px;
+ min-height: 72px;
+
+ .note-header-info {
+ padding-bottom: 0;
+ }
+}
+
+.unresolved {
+ .note-header-info {
+ margin-top: $gl-padding-8;
+ }
}
.note-header {
@@ -419,7 +570,7 @@ ul.notes {
.note-header-info {
min-width: 0;
- padding-bottom: 8px;
+ padding-bottom: $gl-padding-8;
&.discussion {
padding-bottom: 0;
@@ -446,12 +597,12 @@ ul.notes {
.note-headline-light,
.discussion-headline-light {
- color: $notes-light-color;
+ color: $gl-text-color-secondary;
}
.discussion-headline-light {
a {
- color: $gl-link-color;
+ color: $blue-600;
}
}
@@ -478,12 +629,20 @@ ul.notes {
.discussion-actions {
float: right;
- margin-left: 10px;
color: $gray-darkest;
+ @include media-breakpoint-down(xs) {
+ width: 100%;
+ margin: $gl-padding-8 0;
+ }
+
.btn-group > .discussion-next-btn {
margin-left: -1px;
}
+
+ svg {
+ height: 15px;
+ }
}
.note-actions {
@@ -533,59 +692,7 @@ ul.notes {
}
.note-action-button {
- line-height: 1;
- padding: 0;
- min-width: 16px;
- color: $gray-darkest;
- fill: $gray-darkest;
-
- .fa {
- position: relative;
- font-size: 16px;
- }
-
- svg {
- @include btn-svg;
- margin: 0;
- }
-
- .award-control-icon-positive,
- .award-control-icon-super-positive {
- position: absolute;
- top: 0;
- left: 0;
- opacity: 0;
- }
-
- &:hover,
- &.is-active {
- .danger-highlight {
- color: $gl-text-red;
- }
-
- .link-highlight {
- color: $gl-link-color;
- fill: $gl-link-color;
- }
-
- .award-control-icon-neutral {
- opacity: 0;
- }
-
- .award-control-icon-positive {
- opacity: 1;
- }
- }
-
- &.is-active {
- .award-control-icon-positive {
- opacity: 0;
- }
-
- .award-control-icon-super-positive {
- opacity: 1;
- }
- }
+ @include emoji-menu-toggle-button;
}
.discussion-toggle-button {
@@ -597,13 +704,13 @@ ul.notes {
transition: color 0.1s linear;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
&:focus {
text-decoration: underline;
outline: none;
- color: $gl-link-color;
+ color: $blue-600;
}
.fa {
@@ -647,19 +754,6 @@ ul.notes {
z-index: 10;
}
-.discussion-body,
-.diff-file {
- .notes .note {
- border-bottom: 1px solid $white-normal;
-
- .timeline-entry-inner {
- padding-left: $gl-padding;
- padding-right: $gl-padding;
- border-bottom: 0;
- }
- }
-}
-
.disabled-comment {
background-color: $gray-light;
border-radius: $border-radius-base;
@@ -673,14 +767,13 @@ ul.notes {
}
a {
- color: $gl-link-color;
+ color: $blue-600;
}
}
.line-resolve-all-container {
@include notes-media('min', map-get($grid-breakpoints, sm)) {
margin-right: 0;
- padding-left: $gl-padding;
}
> div {
@@ -697,7 +790,7 @@ ul.notes {
}
.btn {
- svg path {
+ svg {
fill: $gray-darkest;
}
@@ -722,7 +815,7 @@ ul.notes {
.line-resolve-all {
vertical-align: middle;
display: inline-block;
- padding: 5px 10px 6px;
+ padding: 6px 10px;
background-color: $gray-light;
border: 1px solid $border-color;
border-radius: $border-radius-default;
@@ -759,16 +852,16 @@ ul.notes {
&:not(.is-disabled) {
&:hover,
&:focus {
- color: $gl-text-green;
+ color: $green-600;
}
}
&.is-active {
- color: $gl-text-green;
+ color: $green-600;
&:hover,
&:focus {
- color: $gl-text-green-hover;
+ color: $green-700;
}
}
@@ -804,7 +897,7 @@ ul.notes {
padding-top: 0;
.discussion-wrapper {
- border-color: transparent;
+ border: 0;
}
}
}
@@ -818,3 +911,43 @@ ul.notes {
margin-top: 4px;
}
}
+
+.discussion-filter-container {
+
+ .btn > svg {
+ width: $gl-col-padding;
+ height: $gl-col-padding;
+ }
+
+ .dropdown-menu {
+ margin-bottom: $gl-padding-4;
+
+ @include media-breakpoint-down(md) {
+ margin-left: $btn-side-margin + $contextual-sidebar-collapsed-width;
+ }
+
+ @include media-breakpoint-down(xs) {
+ margin-left: $btn-side-margin;
+ }
+ }
+}
+
+//This needs to be deleted when Snippet/Commit comments are convered to Vue
+// See https://gitlab.com/gitlab-org/gitlab-ce/issues/53918#note_117038785
+.unstyled-comments {
+
+ .discussion-header {
+ padding: $gl-padding;
+ border-bottom: 1px solid $border-color;
+ }
+
+ .note-wrapper.outlined {
+ margin: 0;
+ border: 0;
+ border-radius: 0;
+ }
+
+ .discussion-form-container {
+ padding: $gl-padding;
+ }
+}
diff --git a/app/assets/stylesheets/pages/pages.scss b/app/assets/stylesheets/pages/pages.scss
index fb42dee66d2..374227fe16a 100644
--- a/app/assets/stylesheets/pages/pages.scss
+++ b/app/assets/stylesheets/pages/pages.scss
@@ -1,7 +1,5 @@
.pages-domain-list {
&-item {
- position: relative;
- display: flex;
align-items: center;
.domain-status {
@@ -44,8 +42,9 @@
}
:first-child {
- border-bottom-left-radius: $border-radius-default;
- border-top-left-radius: $border-radius-default;
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+ line-height: $gl-line-height;
}
:not(:first-child) {
diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss
index 86e70955389..617b3db2fae 100644
--- a/app/assets/stylesheets/pages/pipeline_schedules.scss
+++ b/app/assets/stylesheets/pages/pipeline_schedules.scss
@@ -39,10 +39,6 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
-
- svg {
- vertical-align: middle;
- }
}
.next-run-cell {
@@ -52,6 +48,10 @@
a {
color: $text-color;
}
+
+ svg {
+ vertical-align: middle;
+ }
}
.pipeline-schedules-user-callout {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index b68c89c25d8..14395cc59b0 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -19,7 +19,7 @@
background-color: $white-light;
&:hover {
- background-color: $stage-hover-bg;
+ background-color: $gray-darker;
border: 1px solid $dropdown-toggle-active-border-color;
color: $gl-text-color;
}
@@ -175,7 +175,7 @@
}
.commit-sha {
- color: $gl-link-color;
+ color: $blue-600;
}
.badge {
@@ -595,7 +595,7 @@
a.build-content:hover,
button.build-content:hover {
- background-color: $stage-hover-bg;
+ background-color: $gray-darker;
border: 1px solid $dropdown-toggle-active-border-color;
}
@@ -668,7 +668,7 @@
display: block;
&:hover {
- background-color: $stage-hover-bg;
+ background-color: $gray-darker;
border: 1px solid $dropdown-toggle-active-border-color;
svg {
@@ -760,6 +760,7 @@
}
&.ci-status-icon-canceled,
+ &.ci-status-icon-scheduled,
&.ci-status-icon-disabled,
&.ci-status-icon-not-found,
&.ci-status-icon-manual {
@@ -835,7 +836,7 @@ button.mini-pipeline-graph-dropdown-toggle {
display: block;
&:hover {
- background-color: $stage-hover-bg;
+ background-color: $gray-darker;
border: 1px solid $dropdown-toggle-active-border-color;
svg {
@@ -934,7 +935,7 @@ button.mini-pipeline-graph-dropdown-toggle {
&:focus {
outline: none;
text-decoration: none;
- background-color: $stage-hover-bg;
+ background-color: $gray-darker;
}
}
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index b45e305897c..1d691d1d8b8 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -20,7 +20,7 @@
.account-btn-link,
.profile-settings-sidebar a,
.settings-sidebar a {
- color: $md-link-color;
+ color: $blue-600;
}
.private-tokens-reset div.reset-action:not(:first-child) {
@@ -81,14 +81,14 @@
// Middle dot divider between each element in a list of items.
.middle-dot-divider {
&::after {
- content: "\00B7"; // Middle Dot
+ content: '\00B7'; // Middle Dot
padding: 0 6px;
font-weight: $gl-font-weight-bold;
}
&:last-child {
&::after {
- content: "";
+ content: '';
padding: 0;
}
}
@@ -137,7 +137,7 @@
.profile-settings-content {
a {
- color: $md-link-color;
+ color: $blue-600;
}
}
@@ -170,7 +170,7 @@
background-color: $gray-light;
&.not-active {
- color: $provider-btn-not-active-color;
+ color: $blue-500;
}
}
@@ -191,7 +191,6 @@
@include media-breakpoint-down(xs) {
width: auto;
}
-
}
.profile-crop-image-container {
@@ -215,7 +214,6 @@
}
}
-
.user-profile {
.cover-controls a {
margin-left: 5px;
@@ -242,6 +240,12 @@
left: 0;
}
+ .activities-block {
+ .event-item {
+ padding-left: 40px;
+ }
+ }
+
@include media-breakpoint-down(xs) {
.cover-block {
padding-top: 20px;
@@ -269,6 +273,12 @@
margin-right: 0;
}
}
+
+ .activities-block {
+ .event-item {
+ padding-left: 0;
+ }
+ }
}
}
@@ -279,6 +289,10 @@ table.u2f-registrations {
}
}
+.codes {
+ padding-top: 14px;
+}
+
.oauth-application-show {
.scope-name {
font-weight: $gl-font-weight-bold;
@@ -414,7 +428,7 @@ table.u2f-registrations {
}
&.unverified {
- @include status-color($gray-dark, color("gray"), $common-gray-dark);
+ @include status-color($gray-dark, color('gray'), $common-gray-dark);
}
}
}
@@ -427,7 +441,7 @@ table.u2f-registrations {
}
.emoji-menu-toggle-button {
- @extend .note-action-button;
+ @include emoji-menu-toggle-button;
.no-emoji-placeholder {
position: relative;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 6d9f415e869..da3d8aa53ad 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -115,7 +115,7 @@
.project-feature-controls {
display: flex;
align-items: center;
- margin: 8px 0;
+ margin: $gl-padding-8 0;
max-width: 432px;
.toggle-wrapper {
@@ -144,12 +144,8 @@
.group-home-panel {
padding-top: 24px;
padding-bottom: 24px;
+ border-bottom: 1px solid $border-color;
- @include media-breakpoint-up(sm) {
- border-bottom: 1px solid $border-color;
- }
-
- .project-avatar,
.group-avatar {
float: none;
margin: 0 auto;
@@ -175,7 +171,6 @@
}
}
- .project-home-desc,
.group-home-desc {
margin-left: auto;
margin-right: auto;
@@ -199,6 +194,62 @@
}
}
+.project-home-panel {
+ padding-top: $gl-padding-8;
+ padding-bottom: $gl-padding-24;
+
+ .project-title-row {
+ margin-right: $gl-padding-8;
+ }
+
+ .project-avatar {
+ width: $project-title-row-height;
+ height: $project-title-row-height;
+ flex-shrink: 0;
+ flex-basis: $project-title-row-height;
+ margin: 0 $gl-padding-8 0 0;
+ }
+
+ .project-title {
+ font-size: 20px;
+ line-height: $project-title-row-height;
+ font-weight: bold;
+ }
+
+ .project-metadata {
+ font-weight: normal;
+ font-size: 14px;
+ line-height: $gl-btn-line-height;
+ color: $gl-text-color-secondary;
+
+ .icon {
+ margin-right: $gl-padding-4;
+ font-size: 16px;
+ }
+
+ .project-visibility,
+ .project-license,
+ .project-tag-list {
+ margin-right: $gl-padding-8;
+ }
+
+ .project-license {
+ .btn {
+ line-height: 0;
+ border-width: 0;
+ }
+ }
+
+ .project-tag-list,
+ .project-license {
+ .icon {
+ position: relative;
+ top: 2px;
+ }
+ }
+ }
+}
+
.nav > .project-repo-buttons {
margin-top: 0;
}
@@ -206,8 +257,6 @@
.project-repo-buttons,
.group-buttons {
.btn {
- padding: 3px 10px;
-
&:last-child {
margin-left: 0;
}
@@ -222,11 +271,15 @@
.fa-caret-down {
margin-left: 3px;
+
+ &.dropdown-btn-icon {
+ margin-left: 0;
+ }
}
}
.project-action-button {
- margin: 15px 5px 0;
+ margin: $gl-padding $gl-padding-8 0 0;
vertical-align: top;
}
@@ -243,82 +296,45 @@
.count-buttons {
display: inline-block;
vertical-align: top;
- margin-top: 15px;
- }
+ margin-top: $gl-padding;
- .project-clone-holder {
- display: inline-block;
- margin: 15px 5px 0 0;
+ .count-badge {
+ height: $input-height;
- input {
- height: 28px;
+ .icon {
+ top: -1px;
+ }
}
- }
- .count-with-arrow {
- display: inline-block;
- position: relative;
- margin-left: 4px;
+ .count-badge-count,
+ .count-badge-button {
+ border: 1px solid $border-color;
+ line-height: 1;
+ }
- .arrow {
- &::before {
- content: '';
- display: inline-block;
- position: absolute;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
- top: 50%;
- left: 0;
- margin-top: -6px;
- border-width: 7px 5px 7px 0;
- border-right-color: $count-arrow-border;
- pointer-events: none;
- }
+ .count,
+ .count-badge-button {
+ color: $gl-text-color;
+ }
- &::after {
- content: '';
- position: absolute;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
- top: 50%;
- left: 1px;
- margin-top: -9px;
- border-width: 10px 7px 10px 0;
- border-right-color: $white-light;
- pointer-events: none;
- }
+ .count-badge-count {
+ padding: 0 12px;
+ border-right: 0;
+ border-radius: $border-radius-base 0 0 $border-radius-base;
+ background: $gray-light;
}
- .count {
- @include btn-white;
- display: inline-block;
- background: $white-light;
- border-radius: 2px;
- border-width: 1px;
- border-style: solid;
- font-size: 13px;
- font-weight: $gl-font-weight-bold;
- line-height: 13px;
- letter-spacing: 0.4px;
- padding: 6px 14px;
- text-align: center;
- vertical-align: middle;
- touch-action: manipulation;
- background-image: none;
- white-space: nowrap;
- margin: 0 10px 0 4px;
+ .count-badge-button {
+ border-radius: 0 $border-radius-base $border-radius-base 0;
+ }
+ }
- a {
- color: inherit;
- }
+ .project-clone-holder {
+ display: inline-block;
+ margin: $gl-padding $gl-padding-8 0 0;
- &:hover {
- background: $white-light;
- }
+ input {
+ height: $input-height;
}
}
@@ -333,6 +349,14 @@
min-width: 320px;
}
}
+
+ .mobile-git-clone {
+ margin-top: $gl-padding-8;
+
+ .dropdown-menu-inner-content {
+ @extend .monospace;
+ }
+ }
}
.split-one {
@@ -347,7 +371,7 @@
.save-project-loader {
margin-top: 50px;
margin-bottom: 50px;
- color: $save-project-loader-color;
+ color: $gl-gray-700;
}
.transfer-project .select2-container {
@@ -388,29 +412,29 @@
line-height: $gl-btn-line-height;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
}
}
.vs-public {
- color: $gl-primary;
+ color: $blue-500;
}
.vs-internal {
- color: $gl-warning;
+ color: $orange-500;
}
.vs-private {
- color: $gl-success;
+ color: $green-500;
}
.lfs-enabled {
- color: $gl-success;
+ color: $green-500;
}
.lfs-disabled {
- color: $gl-warning;
+ color: $orange-500;
}
.breadcrumb.repo-breadcrumb {
@@ -423,7 +447,7 @@
> li + li::before {
padding: 0 3px;
- color: $project-breadcrumb-color;
+ color: $gl-gray-400;
}
a {
@@ -449,8 +473,8 @@
&:hover:not(.disabled),
&.forked {
- background-color: $row-hover;
- border-color: $row-hover-border;
+ background-color: $blue-50;
+ border-color: $blue-200;
}
.avatar-container,
@@ -511,7 +535,6 @@
.controls {
margin-left: auto;
}
-
}
.choose-template {
@@ -574,7 +597,7 @@
flex-wrap: wrap;
.btn {
- padding: 8px;
+ padding: $gl-padding-8;
margin-right: 10px;
}
@@ -651,7 +674,7 @@
left: -10px;
top: 50%;
z-index: 10;
- padding: 8px 0;
+ padding: $gl-padding-8 0;
text-align: center;
background-color: $white-light;
color: $gl-text-color-tertiary;
@@ -665,7 +688,7 @@
left: 50%;
top: 0;
transform: translateX(-50%);
- padding: 0 8px;
+ padding: 0 $gl-padding-8;
}
}
@@ -699,17 +722,51 @@
.project-stats {
font-size: 0;
text-align: center;
- max-width: 100%;
border-bottom: 1px solid $border-color;
- .nav {
- margin-top: $gl-padding-8;
- margin-bottom: $gl-padding-8;
+ .scrolling-tabs-container {
+ .scrolling-tabs {
+ margin-top: $gl-padding-8;
+ margin-bottom: $gl-padding-8;
+ flex-wrap: wrap;
+ border-bottom: 0;
+ }
+
+ .fade-left,
+ .fade-right {
+ top: 0;
+ height: 100%;
+ .fa {
+ top: 50%;
+ margin-top: -$gl-padding-8;
+ }
+ }
+
+ .nav {
+ flex-basis: 100%;
+
+ + .nav {
+ margin: $gl-padding-8 0;
+ }
+ }
+
+ @include media-breakpoint-down(md) {
+ flex-direction: column;
+
+ .nav {
+ flex-wrap: nowrap;
+ }
+
+ .nav:first-child {
+ margin-right: $gl-padding-8;
+ }
+ }
+ }
+
+ .nav {
> li {
display: inline-block;
- margin-top: $gl-padding-4;
- margin-bottom: $gl-padding-4;
&:not(:last-child) {
margin-right: $gl-padding;
@@ -731,14 +788,18 @@
background-color: transparent;
font-size: $gl-font-size;
line-height: $gl-btn-line-height;
- color: $notes-light-color;
+ color: $gl-text-color-secondary;
+ white-space: nowrap;
}
.stat-link {
+ border-bottom: 0;
+
&:hover,
&:focus {
color: $gl-text-color;
text-decoration: underline;
+ border-bottom: 0;
}
}
@@ -769,6 +830,14 @@
}
}
+.repository-language-bar-tooltip-language {
+ font-weight: $gl-font-weight-bold;
+}
+
+.repository-language-bar-tooltip-share {
+ color: $theme-gray-400;
+}
+
pre.light-well {
border-color: $well-light-border;
}
@@ -868,7 +937,7 @@ pre.light-well {
}
.git-clone-holder {
- width: 380px;
+ width: 320px;
.btn-clipboard {
border: 1px solid $border-color;
@@ -894,13 +963,13 @@ pre.light-well {
.cannot-be-merged,
.cannot-be-merged:hover {
- color: $error-exclamation-point;
+ color: $red-500;
margin-top: 2px;
}
.private-forks-notice .private-fork-icon {
i:nth-child(1) {
- color: $project-private-forks-notice-odd;
+ color: $green-600;
}
i:nth-child(2) {
@@ -961,7 +1030,7 @@ pre.light-well {
margin-left: 5px;
&.is-done {
- color: $gl-text-green;
+ color: $green-600;
}
}
@@ -1128,12 +1197,12 @@ pre.light-well {
.project-ci-body {
.incorrect-syntax {
font-size: 18px;
- color: $lint-incorrect-color;
+ color: $red-500;
}
.correct-syntax {
font-size: 18px;
- color: $lint-correct-color;
+ color: $green-500;
}
}
diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss
index 2734faec558..59f01f3e958 100644
--- a/app/assets/stylesheets/pages/runners.scss
+++ b/app/assets/stylesheets/pages/runners.scss
@@ -4,24 +4,24 @@
color: $white-light;
&.runner-state-shared {
- background: $runner-state-shared-bg;
+ background: $green-400;
}
&.runner-state-specific {
- background: $runner-state-specific-bg;
+ background: $blue-400;
}
}
.runner-status-online {
- color: $runner-status-online-color;
+ color: $green-600;
}
.runner-status-offline {
- color: $runner-status-offline-color;
+ color: $gray-darkest;
}
.runner-status-paused {
- color: $runner-status-paused-color;
+ color: $red-500;
}
.runner {
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index c9405004c38..04151b1cd59 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -24,12 +24,12 @@ $search-avatar-size: 16px;
.form-control:hover,
:not[readonly] {
border-color: lighten($blue-300, 20%);
- box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
+ box-shadow: 0 0 4px lighten($dropdown-input-focus-shadow, 20%);
}
input[type='checkbox']:hover {
- box-shadow: 0 0 2px 2px lighten($search-input-focus-shadow-color, 20%),
- 0 0 0 1px lighten($search-input-focus-shadow-color, 20%);
+ box-shadow: 0 0 2px 2px lighten($dropdown-input-focus-shadow, 20%),
+ 0 0 0 1px lighten($dropdown-input-focus-shadow, 20%);
}
.search {
@@ -181,7 +181,7 @@ input[type='checkbox']:hover {
width: $search-avatar-size;
height: $search-avatar-size;
border-radius: 50%;
- border: 1px solid $avatar-border;
+ border: 1px solid $gray-normal;
}
}
@@ -218,7 +218,7 @@ input[type='checkbox']:hover {
}
.btn-search,
- .btn-new {
+ .btn-success {
width: 100%;
margin-top: 5px;
@@ -259,6 +259,6 @@ input[type='checkbox']:hover {
&:hover,
&:focus {
- color: $gl-link-color;
+ color: $blue-600;
}
}
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index fb03970f64f..ccfa4e00a5b 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -42,6 +42,10 @@
margin-top: 0;
}
+ .settings-title {
+ cursor: pointer;
+ }
+
button {
position: absolute;
top: 20px;
@@ -106,25 +110,25 @@
.settings-list-icon {
color: $gl-text-color-secondary;
- font-size: $settings-icon-size;
+ font-size: $default-icon-size;
line-height: 42px;
}
.settings-message {
padding: 5px;
line-height: 1.3;
- color: $warning-message-color;
- background-color: $warning-message-bg;
- border: 1px solid $warning-message-border;
+ color: $orange-700;
+ background-color: $orange-100;
+ border: 1px solid $orange-200;
border-radius: $border-radius-base;
}
.warning-title {
- color: $gl-warning;
+ color: $orange-500;
}
.danger-title {
- color: $gl-danger;
+ color: $red-500;
}
.integration-settings-form {
@@ -249,7 +253,7 @@
}
.loading-metrics .metrics-load-spinner {
- color: $loading-color;
+ color: $gl-gray-700;
}
.metrics-list {
@@ -303,7 +307,7 @@
}
.mirror-error-badge {
- background-color: $error-bg;
+ background-color: $red-400;
border-radius: $border-radius-default;
color: $white-light;
}
diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss
index 3f6f5f06075..d331edaa302 100644
--- a/app/assets/stylesheets/pages/stat_graph.scss
+++ b/app/assets/stylesheets/pages/stat_graph.scss
@@ -5,7 +5,7 @@
}
.area {
- fill: $stat-graph-area-fill;
+ fill: $green-500;
fill-opacity: 0.5;
}
@@ -54,7 +54,7 @@
}
.area-contributor {
- fill: $stat-graph-orange-fill;
+ fill: $orange-500;
}
}
}
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 620297e589d..7d59dd3b5d1 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -27,6 +27,7 @@
&.ci-canceled,
&.ci-disabled,
+ &.ci-scheduled,
&.ci-manual {
color: $gl-text-color;
border-color: $gl-text-color;
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 010a2c05a1c..3fc37e20c36 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -12,8 +12,8 @@
flex-direction: row;
&:hover {
- background-color: $row-hover;
- border-color: $row-hover-border;
+ background-color: $blue-50;
+ border-color: $blue-200;
cursor: pointer;
}
@@ -22,7 +22,7 @@
border-bottom: 1px solid transparent;
&:hover {
- border-color: $row-hover-border;
+ border-color: $blue-200;
}
}
@@ -143,7 +143,7 @@
border: 0;
background: $gray-light;
border-radius: 0;
- color: $todo-body-pre-color;
+ color: $gl-gray-500;
margin: 0 20px;
overflow: hidden;
}
@@ -205,7 +205,7 @@
.todo-body {
margin: 0;
- border-left: 2px solid $todo-body-border;
+ border-left: 2px solid $gl-gray-100;
padding-left: 10px;
}
}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 1cc26d40ba9..dc5ca78ff58 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -111,9 +111,9 @@
&:hover:not(.tree-truncated-warning) {
td {
- background-color: $row-hover;
- border-top: 1px solid $row-hover-border;
- border-bottom: 1px solid $row-hover-border;
+ background-color: $blue-50;
+ border-top: 1px solid $blue-200;
+ border-bottom: 1px solid $blue-200;
cursor: pointer;
}
}
@@ -229,7 +229,7 @@
.upload-link {
font-weight: $gl-font-weight-normal;
- color: $md-link-color;
+ color: $blue-600;
}
.repo-charts {
diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss
index 48ac5b21db8..84c617c7ec0 100644
--- a/app/assets/stylesheets/pages/ui_dev_kit.scss
+++ b/app/assets/stylesheets/pages/ui_dev_kit.scss
@@ -6,7 +6,7 @@
.example {
padding: 15px;
- border: 1px dashed $ui-dev-kit-example-border;
+ border: 1px dashed $gl-gray-100;
margin-bottom: 15px;
&::before {
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
deleted file mode 100644
index 7d40c61da26..00000000000
--- a/app/assets/stylesheets/pages/xterm.scss
+++ /dev/null
@@ -1,1453 +0,0 @@
-.build-page {
- // color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg
- // see also: https://gist.github.com/jasonm23/2868981
-
- $black: #000;
- $red: #ea1010;
- $green: #009900;
- $yellow: #999900;
- $blue: #0073e6;
- $magenta: #d411d4;
- $cyan: #009999;
- $white: #ccc;
- $l-black: #373b41;
- $l-red: #ff6161;
- $l-green: #00d600;
- $l-yellow: #bdbd00;
- $l-blue: #5797ff;
- $l-magenta: #d96dd9;
- $l-cyan: #00bdbd;
- $l-white: #fff;
-
- /*
- * xterm colors
- */
- $xterm-fg-0: $black;
- $xterm-fg-1: #800000;
- $xterm-fg-2: #008000;
- $xterm-fg-3: #808000;
- $xterm-fg-4: #000080;
- $xterm-fg-5: #800080;
- $xterm-fg-6: #008080;
- $xterm-fg-7: #c0c0c0;
- $xterm-fg-8: #808080;
- $xterm-fg-9: #f00;
- $xterm-fg-10: #0f0;
- $xterm-fg-11: #ff0;
- $xterm-fg-12: #00f;
- $xterm-fg-13: #f0f;
- $xterm-fg-14: #0ff;
- $xterm-fg-15: $white-light;
- $xterm-fg-16: $black;
- $xterm-fg-17: #00005f;
- $xterm-fg-18: #000087;
- $xterm-fg-19: #0000af;
- $xterm-fg-20: #0000d7;
- $xterm-fg-21: #00f;
- $xterm-fg-22: #005f00;
- $xterm-fg-23: #005f5f;
- $xterm-fg-24: #005f87;
- $xterm-fg-25: #005faf;
- $xterm-fg-26: #005fd7;
- $xterm-fg-27: #005fff;
- $xterm-fg-28: #008700;
- $xterm-fg-29: #00875f;
- $xterm-fg-30: #008787;
- $xterm-fg-31: #0087af;
- $xterm-fg-32: #0087d7;
- $xterm-fg-33: #0087ff;
- $xterm-fg-34: #00af00;
- $xterm-fg-35: #00af5f;
- $xterm-fg-36: #00af87;
- $xterm-fg-37: #00afaf;
- $xterm-fg-38: #00afd7;
- $xterm-fg-39: #00afff;
- $xterm-fg-40: #00d700;
- $xterm-fg-41: #00d75f;
- $xterm-fg-42: #00d787;
- $xterm-fg-43: #00d7af;
- $xterm-fg-44: #00d7d7;
- $xterm-fg-45: #00d7ff;
- $xterm-fg-46: #0f0;
- $xterm-fg-47: #00ff5f;
- $xterm-fg-48: #00ff87;
- $xterm-fg-49: #00ffaf;
- $xterm-fg-50: #00ffd7;
- $xterm-fg-51: #0ff;
- $xterm-fg-52: #5f0000;
- $xterm-fg-53: #5f005f;
- $xterm-fg-54: #5f0087;
- $xterm-fg-55: #5f00af;
- $xterm-fg-56: #5f00d7;
- $xterm-fg-57: #5f00ff;
- $xterm-fg-58: #5f5f00;
- $xterm-fg-59: #5f5f5f;
- $xterm-fg-60: #5f5f87;
- $xterm-fg-61: #5f5faf;
- $xterm-fg-62: #5f5fd7;
- $xterm-fg-63: #5f5fff;
- $xterm-fg-64: #5f8700;
- $xterm-fg-65: #5f875f;
- $xterm-fg-66: #5f8787;
- $xterm-fg-67: #5f87af;
- $xterm-fg-68: #5f87d7;
- $xterm-fg-69: #5f87ff;
- $xterm-fg-70: #5faf00;
- $xterm-fg-71: #5faf5f;
- $xterm-fg-72: #5faf87;
- $xterm-fg-73: #5fafaf;
- $xterm-fg-74: #5fafd7;
- $xterm-fg-75: #5fafff;
- $xterm-fg-76: #5fd700;
- $xterm-fg-77: #5fd75f;
- $xterm-fg-78: #5fd787;
- $xterm-fg-79: #5fd7af;
- $xterm-fg-80: #5fd7d7;
- $xterm-fg-81: #5fd7ff;
- $xterm-fg-82: #5fff00;
- $xterm-fg-83: #5fff5f;
- $xterm-fg-84: #5fff87;
- $xterm-fg-85: #5fffaf;
- $xterm-fg-86: #5fffd7;
- $xterm-fg-87: #5fffff;
- $xterm-fg-88: #870000;
- $xterm-fg-89: #87005f;
- $xterm-fg-90: #870087;
- $xterm-fg-91: #8700af;
- $xterm-fg-92: #8700d7;
- $xterm-fg-93: #8700ff;
- $xterm-fg-94: #875f00;
- $xterm-fg-95: #875f5f;
- $xterm-fg-96: #875f87;
- $xterm-fg-97: #875faf;
- $xterm-fg-98: #875fd7;
- $xterm-fg-99: #875fff;
- $xterm-fg-100: #878700;
- $xterm-fg-101: #87875f;
- $xterm-fg-102: #878787;
- $xterm-fg-103: #8787af;
- $xterm-fg-104: #8787d7;
- $xterm-fg-105: #8787ff;
- $xterm-fg-106: #87af00;
- $xterm-fg-107: #87af5f;
- $xterm-fg-108: #87af87;
- $xterm-fg-109: #87afaf;
- $xterm-fg-110: #87afd7;
- $xterm-fg-111: #87afff;
- $xterm-fg-112: #87d700;
- $xterm-fg-113: #87d75f;
- $xterm-fg-114: #87d787;
- $xterm-fg-115: #87d7af;
- $xterm-fg-116: #87d7d7;
- $xterm-fg-117: #87d7ff;
- $xterm-fg-118: #87ff00;
- $xterm-fg-119: #87ff5f;
- $xterm-fg-120: #87ff87;
- $xterm-fg-121: #87ffaf;
- $xterm-fg-122: #87ffd7;
- $xterm-fg-123: #87ffff;
- $xterm-fg-124: #af0000;
- $xterm-fg-125: #af005f;
- $xterm-fg-126: #af0087;
- $xterm-fg-127: #af00af;
- $xterm-fg-128: #af00d7;
- $xterm-fg-129: #af00ff;
- $xterm-fg-130: #af5f00;
- $xterm-fg-131: #af5f5f;
- $xterm-fg-132: #af5f87;
- $xterm-fg-133: #af5faf;
- $xterm-fg-134: #af5fd7;
- $xterm-fg-135: #af5fff;
- $xterm-fg-136: #af8700;
- $xterm-fg-137: #af875f;
- $xterm-fg-138: #af8787;
- $xterm-fg-139: #af87af;
- $xterm-fg-140: #af87d7;
- $xterm-fg-141: #af87ff;
- $xterm-fg-142: #afaf00;
- $xterm-fg-143: #afaf5f;
- $xterm-fg-144: #afaf87;
- $xterm-fg-145: #afafaf;
- $xterm-fg-146: #afafd7;
- $xterm-fg-147: #afafff;
- $xterm-fg-148: #afd700;
- $xterm-fg-149: #afd75f;
- $xterm-fg-150: #afd787;
- $xterm-fg-151: #afd7af;
- $xterm-fg-152: #afd7d7;
- $xterm-fg-153: #afd7ff;
- $xterm-fg-154: #afff00;
- $xterm-fg-155: #afff5f;
- $xterm-fg-156: #afff87;
- $xterm-fg-157: #afffaf;
- $xterm-fg-158: #afffd7;
- $xterm-fg-159: #afffff;
- $xterm-fg-160: #d70000;
- $xterm-fg-161: #d7005f;
- $xterm-fg-162: #d70087;
- $xterm-fg-163: #d700af;
- $xterm-fg-164: #d700d7;
- $xterm-fg-165: #d700ff;
- $xterm-fg-166: #d75f00;
- $xterm-fg-167: #d75f5f;
- $xterm-fg-168: #d75f87;
- $xterm-fg-169: #d75faf;
- $xterm-fg-170: #d75fd7;
- $xterm-fg-171: #d75fff;
- $xterm-fg-172: #d78700;
- $xterm-fg-173: #d7875f;
- $xterm-fg-174: #d78787;
- $xterm-fg-175: #d787af;
- $xterm-fg-176: #d787d7;
- $xterm-fg-177: #d787ff;
- $xterm-fg-178: #d7af00;
- $xterm-fg-179: #d7af5f;
- $xterm-fg-180: #d7af87;
- $xterm-fg-181: #d7afaf;
- $xterm-fg-182: #d7afd7;
- $xterm-fg-183: #d7afff;
- $xterm-fg-184: #d7d700;
- $xterm-fg-185: #d7d75f;
- $xterm-fg-186: #d7d787;
- $xterm-fg-187: #d7d7af;
- $xterm-fg-188: #d7d7d7;
- $xterm-fg-189: #d7d7ff;
- $xterm-fg-190: #d7ff00;
- $xterm-fg-191: #d7ff5f;
- $xterm-fg-192: #d7ff87;
- $xterm-fg-193: #d7ffaf;
- $xterm-fg-194: #d7ffd7;
- $xterm-fg-195: #d7ffff;
- $xterm-fg-196: #f00;
- $xterm-fg-197: #ff005f;
- $xterm-fg-198: #ff0087;
- $xterm-fg-199: #ff00af;
- $xterm-fg-200: #ff00d7;
- $xterm-fg-201: #f0f;
- $xterm-fg-202: #ff5f00;
- $xterm-fg-203: #ff5f5f;
- $xterm-fg-204: #ff5f87;
- $xterm-fg-205: #ff5faf;
- $xterm-fg-206: #ff5fd7;
- $xterm-fg-207: #ff5fff;
- $xterm-fg-208: #ff8700;
- $xterm-fg-209: #ff875f;
- $xterm-fg-210: #ff8787;
- $xterm-fg-211: #ff87af;
- $xterm-fg-212: #ff87d7;
- $xterm-fg-213: #ff87ff;
- $xterm-fg-214: #ffaf00;
- $xterm-fg-215: #ffaf5f;
- $xterm-fg-216: #ffaf87;
- $xterm-fg-217: #ffafaf;
- $xterm-fg-218: #ffafd7;
- $xterm-fg-219: #ffafff;
- $xterm-fg-220: #ffd700;
- $xterm-fg-221: #ffd75f;
- $xterm-fg-222: #ffd787;
- $xterm-fg-223: #ffd7af;
- $xterm-fg-224: #ffd7d7;
- $xterm-fg-225: #ffd7ff;
- $xterm-fg-226: #ff0;
- $xterm-fg-227: #ffff5f;
- $xterm-fg-228: #ffff87;
- $xterm-fg-229: #ffffaf;
- $xterm-fg-230: #ffffd7;
- $xterm-fg-231: $white-light;
- $xterm-fg-232: #080808;
- $xterm-fg-233: #121212;
- $xterm-fg-234: #1c1c1c;
- $xterm-fg-235: #262626;
- $xterm-fg-236: #303030;
- $xterm-fg-237: #3a3a3a;
- $xterm-fg-238: #444;
- $xterm-fg-239: #4e4e4e;
- $xterm-fg-240: #585858;
- $xterm-fg-241: #626262;
- $xterm-fg-242: #6c6c6c;
- $xterm-fg-243: #767676;
- $xterm-fg-244: #808080;
- $xterm-fg-245: #8a8a8a;
- $xterm-fg-246: #949494;
- $xterm-fg-247: #9e9e9e;
- $xterm-fg-248: #a8a8a8;
- $xterm-fg-249: #b2b2b2;
- $xterm-fg-250: #bcbcbc;
- $xterm-fg-251: #c6c6c6;
- $xterm-fg-252: #d0d0d0;
- $xterm-fg-253: #dadada;
- $xterm-fg-254: #e4e4e4;
- $xterm-fg-255: #eee;
-
- .term-bold {
- font-weight: $gl-font-weight-bold;
- }
-
- .term-italic {
- font-style: italic;
- }
-
- .term-conceal {
- visibility: hidden;
- }
-
- .term-underline {
- text-decoration: underline;
- }
-
- .term-cross {
- text-decoration: line-through;
- }
-
- .term-fg-black {
- color: $black;
- }
-
- .term-fg-red {
- color: $red;
- }
-
- .term-fg-green {
- color: $green;
- }
-
- .term-fg-yellow {
- color: $yellow;
- }
-
- .term-fg-blue {
- color: $blue;
- }
-
- .term-fg-magenta {
- color: $magenta;
- }
-
- .term-fg-cyan {
- color: $cyan;
- }
-
- .term-fg-white {
- color: $white;
- }
-
- .term-fg-l-black {
- color: $l-black;
- }
-
- .term-fg-l-red {
- color: $l-red;
- }
-
- .term-fg-l-green {
- color: $l-green;
- }
-
- .term-fg-l-yellow {
- color: $l-yellow;
- }
-
- .term-fg-l-blue {
- color: $l-blue;
- }
-
- .term-fg-l-magenta {
- color: $l-magenta;
- }
-
- .term-fg-l-cyan {
- color: $l-cyan;
- }
-
- .term-fg-l-white {
- color: $l-white;
- }
-
- .term-bg-black {
- background-color: $black;
- }
-
- .term-bg-red {
- background-color: $red;
- }
-
- .term-bg-green {
- background-color: $green;
- }
-
- .term-bg-yellow {
- background-color: $yellow;
- }
-
- .term-bg-blue {
- background-color: $blue;
- }
-
- .term-bg-magenta {
- background-color: $magenta;
- }
-
- .term-bg-cyan {
- background-color: $cyan;
- }
-
- .term-bg-white {
- background-color: $white;
- }
-
- .term-bg-l-black {
- background-color: $l-black;
- }
-
- .term-bg-l-red {
- background-color: $l-red;
- }
-
- .term-bg-l-green {
- background-color: $l-green;
- }
-
- .term-bg-l-yellow {
- background-color: $l-yellow;
- }
-
- .term-bg-l-blue {
- background-color: $l-blue;
- }
-
- .term-bg-l-magenta {
- background-color: $l-magenta;
- }
-
- .term-bg-l-cyan {
- background-color: $l-cyan;
- }
-
- .term-bg-l-white {
- background-color: $l-white;
- }
-
- .xterm-fg-0 {
- color: $xterm-fg-0;
- }
-
- .xterm-fg-1 {
- color: $xterm-fg-1;
- }
-
- .xterm-fg-2 {
- color: $xterm-fg-2;
- }
-
- .xterm-fg-3 {
- color: $xterm-fg-3;
- }
-
- .xterm-fg-4 {
- color: $xterm-fg-4;
- }
-
- .xterm-fg-5 {
- color: $xterm-fg-5;
- }
-
- .xterm-fg-6 {
- color: $xterm-fg-6;
- }
-
- .xterm-fg-7 {
- color: $xterm-fg-7;
- }
-
- .xterm-fg-8 {
- color: $xterm-fg-8;
- }
-
- .xterm-fg-9 {
- color: $xterm-fg-9;
- }
-
- .xterm-fg-10 {
- color: $xterm-fg-10;
- }
-
- .xterm-fg-11 {
- color: $xterm-fg-11;
- }
-
- .xterm-fg-12 {
- color: $xterm-fg-12;
- }
-
- .xterm-fg-13 {
- color: $xterm-fg-13;
- }
-
- .xterm-fg-14 {
- color: $xterm-fg-14;
- }
-
- .xterm-fg-15 {
- color: $white-light;
- }
-
- .xterm-fg-16 {
- color: $black;
- }
-
- .xterm-fg-17 {
- color: $xterm-fg-17;
- }
-
- .xterm-fg-18 {
- color: $xterm-fg-18;
- }
-
- .xterm-fg-19 {
- color: $xterm-fg-19;
- }
-
- .xterm-fg-20 {
- color: $xterm-fg-20;
- }
-
- .xterm-fg-21 {
- color: $xterm-fg-21;
- }
-
- .xterm-fg-22 {
- color: $xterm-fg-22;
- }
-
- .xterm-fg-23 {
- color: $xterm-fg-23;
- }
-
- .xterm-fg-24 {
- color: $xterm-fg-24;
- }
-
- .xterm-fg-25 {
- color: $xterm-fg-25;
- }
-
- .xterm-fg-26 {
- color: $xterm-fg-26;
- }
-
- .xterm-fg-27 {
- color: $xterm-fg-27;
- }
-
- .xterm-fg-28 {
- color: $xterm-fg-28;
- }
-
- .xterm-fg-29 {
- color: $xterm-fg-29;
- }
-
- .xterm-fg-30 {
- color: $xterm-fg-30;
- }
-
- .xterm-fg-31 {
- color: $xterm-fg-31;
- }
-
- .xterm-fg-32 {
- color: $xterm-fg-32;
- }
-
- .xterm-fg-33 {
- color: $xterm-fg-33;
- }
-
- .xterm-fg-34 {
- color: $xterm-fg-34;
- }
-
- .xterm-fg-35 {
- color: $xterm-fg-35;
- }
-
- .xterm-fg-36 {
- color: $xterm-fg-36;
- }
-
- .xterm-fg-37 {
- color: $xterm-fg-37;
- }
-
- .xterm-fg-38 {
- color: $xterm-fg-38;
- }
-
- .xterm-fg-39 {
- color: $xterm-fg-39;
- }
-
- .xterm-fg-40 {
- color: $xterm-fg-40;
- }
-
- .xterm-fg-41 {
- color: $xterm-fg-41;
- }
-
- .xterm-fg-42 {
- color: $xterm-fg-42;
- }
-
- .xterm-fg-43 {
- color: $xterm-fg-43;
- }
-
- .xterm-fg-44 {
- color: $xterm-fg-44;
- }
-
- .xterm-fg-45 {
- color: $xterm-fg-45;
- }
-
- .xterm-fg-46 {
- color: $xterm-fg-46;
- }
-
- .xterm-fg-47 {
- color: $xterm-fg-47;
- }
-
- .xterm-fg-48 {
- color: $xterm-fg-48;
- }
-
- .xterm-fg-49 {
- color: $xterm-fg-49;
- }
-
- .xterm-fg-50 {
- color: $xterm-fg-50;
- }
-
- .xterm-fg-51 {
- color: $xterm-fg-51;
- }
-
- .xterm-fg-52 {
- color: $xterm-fg-52;
- }
-
- .xterm-fg-53 {
- color: $xterm-fg-53;
- }
-
- .xterm-fg-54 {
- color: $xterm-fg-54;
- }
-
- .xterm-fg-55 {
- color: $xterm-fg-55;
- }
-
- .xterm-fg-56 {
- color: $xterm-fg-56;
- }
-
- .xterm-fg-57 {
- color: $xterm-fg-57;
- }
-
- .xterm-fg-58 {
- color: $xterm-fg-58;
- }
-
- .xterm-fg-59 {
- color: $xterm-fg-59;
- }
-
- .xterm-fg-60 {
- color: $xterm-fg-60;
- }
-
- .xterm-fg-61 {
- color: $xterm-fg-61;
- }
-
- .xterm-fg-62 {
- color: $xterm-fg-62;
- }
-
- .xterm-fg-63 {
- color: $xterm-fg-63;
- }
-
- .xterm-fg-64 {
- color: $xterm-fg-64;
- }
-
- .xterm-fg-65 {
- color: $xterm-fg-65;
- }
-
- .xterm-fg-66 {
- color: $xterm-fg-66;
- }
-
- .xterm-fg-67 {
- color: $xterm-fg-67;
- }
-
- .xterm-fg-68 {
- color: $xterm-fg-68;
- }
-
- .xterm-fg-69 {
- color: $xterm-fg-69;
- }
-
- .xterm-fg-70 {
- color: $xterm-fg-70;
- }
-
- .xterm-fg-71 {
- color: $xterm-fg-71;
- }
-
- .xterm-fg-72 {
- color: $xterm-fg-72;
- }
-
- .xterm-fg-73 {
- color: $xterm-fg-73;
- }
-
- .xterm-fg-74 {
- color: $xterm-fg-74;
- }
-
- .xterm-fg-75 {
- color: $xterm-fg-75;
- }
-
- .xterm-fg-76 {
- color: $xterm-fg-76;
- }
-
- .xterm-fg-77 {
- color: $xterm-fg-77;
- }
-
- .xterm-fg-78 {
- color: $xterm-fg-78;
- }
-
- .xterm-fg-79 {
- color: $xterm-fg-79;
- }
-
- .xterm-fg-80 {
- color: $xterm-fg-80;
- }
-
- .xterm-fg-81 {
- color: $xterm-fg-81;
- }
-
- .xterm-fg-82 {
- color: $xterm-fg-82;
- }
-
- .xterm-fg-83 {
- color: $xterm-fg-83;
- }
-
- .xterm-fg-84 {
- color: $xterm-fg-84;
- }
-
- .xterm-fg-85 {
- color: $xterm-fg-85;
- }
-
- .xterm-fg-86 {
- color: $xterm-fg-86;
- }
-
- .xterm-fg-87 {
- color: $xterm-fg-87;
- }
-
- .xterm-fg-88 {
- color: $xterm-fg-88;
- }
-
- .xterm-fg-89 {
- color: $xterm-fg-89;
- }
-
- .xterm-fg-90 {
- color: $xterm-fg-90;
- }
-
- .xterm-fg-91 {
- color: $xterm-fg-91;
- }
-
- .xterm-fg-92 {
- color: $xterm-fg-92;
- }
-
- .xterm-fg-93 {
- color: $xterm-fg-93;
- }
-
- .xterm-fg-94 {
- color: $xterm-fg-94;
- }
-
- .xterm-fg-95 {
- color: $xterm-fg-95;
- }
-
- .xterm-fg-96 {
- color: $xterm-fg-96;
- }
-
- .xterm-fg-97 {
- color: $xterm-fg-97;
- }
-
- .xterm-fg-98 {
- color: $xterm-fg-98;
- }
-
- .xterm-fg-99 {
- color: $xterm-fg-99;
- }
-
- .xterm-fg-100 {
- color: $xterm-fg-100;
- }
-
- .xterm-fg-101 {
- color: $xterm-fg-101;
- }
-
- .xterm-fg-102 {
- color: $xterm-fg-102;
- }
-
- .xterm-fg-103 {
- color: $xterm-fg-103;
- }
-
- .xterm-fg-104 {
- color: $xterm-fg-104;
- }
-
- .xterm-fg-105 {
- color: $xterm-fg-105;
- }
-
- .xterm-fg-106 {
- color: $xterm-fg-106;
- }
-
- .xterm-fg-107 {
- color: $xterm-fg-107;
- }
-
- .xterm-fg-108 {
- color: $xterm-fg-108;
- }
-
- .xterm-fg-109 {
- color: $xterm-fg-109;
- }
-
- .xterm-fg-110 {
- color: $xterm-fg-110;
- }
-
- .xterm-fg-111 {
- color: $xterm-fg-111;
- }
-
- .xterm-fg-112 {
- color: $xterm-fg-112;
- }
-
- .xterm-fg-113 {
- color: $xterm-fg-113;
- }
-
- .xterm-fg-114 {
- color: $xterm-fg-114;
- }
-
- .xterm-fg-115 {
- color: $xterm-fg-115;
- }
-
- .xterm-fg-116 {
- color: $xterm-fg-116;
- }
-
- .xterm-fg-117 {
- color: $xterm-fg-117;
- }
-
- .xterm-fg-118 {
- color: $xterm-fg-118;
- }
-
- .xterm-fg-119 {
- color: $xterm-fg-119;
- }
-
- .xterm-fg-120 {
- color: $xterm-fg-120;
- }
-
- .xterm-fg-121 {
- color: $xterm-fg-121;
- }
-
- .xterm-fg-122 {
- color: $xterm-fg-122;
- }
-
- .xterm-fg-123 {
- color: $xterm-fg-123;
- }
-
- .xterm-fg-124 {
- color: $xterm-fg-124;
- }
-
- .xterm-fg-125 {
- color: $xterm-fg-125;
- }
-
- .xterm-fg-126 {
- color: $xterm-fg-126;
- }
-
- .xterm-fg-127 {
- color: $xterm-fg-127;
- }
-
- .xterm-fg-128 {
- color: $xterm-fg-128;
- }
-
- .xterm-fg-129 {
- color: $xterm-fg-129;
- }
-
- .xterm-fg-130 {
- color: $xterm-fg-130;
- }
-
- .xterm-fg-131 {
- color: $xterm-fg-131;
- }
-
- .xterm-fg-132 {
- color: $xterm-fg-132;
- }
-
- .xterm-fg-133 {
- color: $xterm-fg-133;
- }
-
- .xterm-fg-134 {
- color: $xterm-fg-134;
- }
-
- .xterm-fg-135 {
- color: $xterm-fg-135;
- }
-
- .xterm-fg-136 {
- color: $xterm-fg-136;
- }
-
- .xterm-fg-137 {
- color: $xterm-fg-137;
- }
-
- .xterm-fg-138 {
- color: $xterm-fg-138;
- }
-
- .xterm-fg-139 {
- color: $xterm-fg-139;
- }
-
- .xterm-fg-140 {
- color: $xterm-fg-140;
- }
-
- .xterm-fg-141 {
- color: $xterm-fg-141;
- }
-
- .xterm-fg-142 {
- color: $xterm-fg-142;
- }
-
- .xterm-fg-143 {
- color: $xterm-fg-143;
- }
-
- .xterm-fg-144 {
- color: $xterm-fg-144;
- }
-
- .xterm-fg-145 {
- color: $xterm-fg-145;
- }
-
- .xterm-fg-146 {
- color: $xterm-fg-146;
- }
-
- .xterm-fg-147 {
- color: $xterm-fg-147;
- }
-
- .xterm-fg-148 {
- color: $xterm-fg-148;
- }
-
- .xterm-fg-149 {
- color: $xterm-fg-149;
- }
-
- .xterm-fg-150 {
- color: $xterm-fg-150;
- }
-
- .xterm-fg-151 {
- color: $xterm-fg-151;
- }
-
- .xterm-fg-152 {
- color: $xterm-fg-152;
- }
-
- .xterm-fg-153 {
- color: $xterm-fg-153;
- }
-
- .xterm-fg-154 {
- color: $xterm-fg-154;
- }
-
- .xterm-fg-155 {
- color: $xterm-fg-155;
- }
-
- .xterm-fg-156 {
- color: $xterm-fg-156;
- }
-
- .xterm-fg-157 {
- color: $xterm-fg-157;
- }
-
- .xterm-fg-158 {
- color: $xterm-fg-158;
- }
-
- .xterm-fg-159 {
- color: $xterm-fg-159;
- }
-
- .xterm-fg-160 {
- color: $xterm-fg-160;
- }
-
- .xterm-fg-161 {
- color: $xterm-fg-161;
- }
-
- .xterm-fg-162 {
- color: $xterm-fg-162;
- }
-
- .xterm-fg-163 {
- color: $xterm-fg-163;
- }
-
- .xterm-fg-164 {
- color: $xterm-fg-164;
- }
-
- .xterm-fg-165 {
- color: $xterm-fg-165;
- }
-
- .xterm-fg-166 {
- color: $xterm-fg-166;
- }
-
- .xterm-fg-167 {
- color: $xterm-fg-167;
- }
-
- .xterm-fg-168 {
- color: $xterm-fg-168;
- }
-
- .xterm-fg-169 {
- color: $xterm-fg-169;
- }
-
- .xterm-fg-170 {
- color: $xterm-fg-170;
- }
-
- .xterm-fg-171 {
- color: $xterm-fg-171;
- }
-
- .xterm-fg-172 {
- color: $xterm-fg-172;
- }
-
- .xterm-fg-173 {
- color: $xterm-fg-173;
- }
-
- .xterm-fg-174 {
- color: $xterm-fg-174;
- }
-
- .xterm-fg-175 {
- color: $xterm-fg-175;
- }
-
- .xterm-fg-176 {
- color: $xterm-fg-176;
- }
-
- .xterm-fg-177 {
- color: $xterm-fg-177;
- }
-
- .xterm-fg-178 {
- color: $xterm-fg-178;
- }
-
- .xterm-fg-179 {
- color: $xterm-fg-179;
- }
-
- .xterm-fg-180 {
- color: $xterm-fg-180;
- }
-
- .xterm-fg-181 {
- color: $xterm-fg-181;
- }
-
- .xterm-fg-182 {
- color: $xterm-fg-182;
- }
-
- .xterm-fg-183 {
- color: $xterm-fg-183;
- }
-
- .xterm-fg-184 {
- color: $xterm-fg-184;
- }
-
- .xterm-fg-185 {
- color: $xterm-fg-185;
- }
-
- .xterm-fg-186 {
- color: $xterm-fg-186;
- }
-
- .xterm-fg-187 {
- color: $xterm-fg-187;
- }
-
- .xterm-fg-188 {
- color: $xterm-fg-188;
- }
-
- .xterm-fg-189 {
- color: $xterm-fg-189;
- }
-
- .xterm-fg-190 {
- color: $xterm-fg-190;
- }
-
- .xterm-fg-191 {
- color: $xterm-fg-191;
- }
-
- .xterm-fg-192 {
- color: $xterm-fg-192;
- }
-
- .xterm-fg-193 {
- color: $xterm-fg-193;
- }
-
- .xterm-fg-194 {
- color: $xterm-fg-194;
- }
-
- .xterm-fg-195 {
- color: $xterm-fg-195;
- }
-
- .xterm-fg-196 {
- color: $xterm-fg-196;
- }
-
- .xterm-fg-197 {
- color: $xterm-fg-197;
- }
-
- .xterm-fg-198 {
- color: $xterm-fg-198;
- }
-
- .xterm-fg-199 {
- color: $xterm-fg-199;
- }
-
- .xterm-fg-200 {
- color: $xterm-fg-200;
- }
-
- .xterm-fg-201 {
- color: $xterm-fg-201;
- }
-
- .xterm-fg-202 {
- color: $xterm-fg-202;
- }
-
- .xterm-fg-203 {
- color: $xterm-fg-203;
- }
-
- .xterm-fg-204 {
- color: $xterm-fg-204;
- }
-
- .xterm-fg-205 {
- color: $xterm-fg-205;
- }
-
- .xterm-fg-206 {
- color: $xterm-fg-206;
- }
-
- .xterm-fg-207 {
- color: $xterm-fg-207;
- }
-
- .xterm-fg-208 {
- color: $xterm-fg-208;
- }
-
- .xterm-fg-209 {
- color: $xterm-fg-209;
- }
-
- .xterm-fg-210 {
- color: $xterm-fg-210;
- }
-
- .xterm-fg-211 {
- color: $xterm-fg-211;
- }
-
- .xterm-fg-212 {
- color: $xterm-fg-212;
- }
-
- .xterm-fg-213 {
- color: $xterm-fg-213;
- }
-
- .xterm-fg-214 {
- color: $xterm-fg-214;
- }
-
- .xterm-fg-215 {
- color: $xterm-fg-215;
- }
-
- .xterm-fg-216 {
- color: $xterm-fg-216;
- }
-
- .xterm-fg-217 {
- color: $xterm-fg-217;
- }
-
- .xterm-fg-218 {
- color: $xterm-fg-218;
- }
-
- .xterm-fg-219 {
- color: $xterm-fg-219;
- }
-
- .xterm-fg-220 {
- color: $xterm-fg-220;
- }
-
- .xterm-fg-221 {
- color: $xterm-fg-221;
- }
-
- .xterm-fg-222 {
- color: $xterm-fg-222;
- }
-
- .xterm-fg-223 {
- color: $xterm-fg-223;
- }
-
- .xterm-fg-224 {
- color: $xterm-fg-224;
- }
-
- .xterm-fg-225 {
- color: $xterm-fg-225;
- }
-
- .xterm-fg-226 {
- color: $xterm-fg-226;
- }
-
- .xterm-fg-227 {
- color: $xterm-fg-227;
- }
-
- .xterm-fg-228 {
- color: $xterm-fg-228;
- }
-
- .xterm-fg-229 {
- color: $xterm-fg-229;
- }
-
- .xterm-fg-230 {
- color: $xterm-fg-230;
- }
-
- .xterm-fg-231 {
- color: $xterm-fg-231;
- }
-
- .xterm-fg-232 {
- color: $xterm-fg-232;
- }
-
- .xterm-fg-233 {
- color: $xterm-fg-233;
- }
-
- .xterm-fg-234 {
- color: $xterm-fg-234;
- }
-
- .xterm-fg-235 {
- color: $xterm-fg-235;
- }
-
- .xterm-fg-236 {
- color: $xterm-fg-236;
- }
-
- .xterm-fg-237 {
- color: $xterm-fg-237;
- }
-
- .xterm-fg-238 {
- color: $xterm-fg-238;
- }
-
- .xterm-fg-239 {
- color: $xterm-fg-239;
- }
-
- .xterm-fg-240 {
- color: $xterm-fg-240;
- }
-
- .xterm-fg-241 {
- color: $xterm-fg-241;
- }
-
- .xterm-fg-242 {
- color: $xterm-fg-242;
- }
-
- .xterm-fg-243 {
- color: $xterm-fg-243;
- }
-
- .xterm-fg-244 {
- color: $xterm-fg-244;
- }
-
- .xterm-fg-245 {
- color: $xterm-fg-245;
- }
-
- .xterm-fg-246 {
- color: $xterm-fg-246;
- }
-
- .xterm-fg-247 {
- color: $xterm-fg-247;
- }
-
- .xterm-fg-248 {
- color: $xterm-fg-248;
- }
-
- .xterm-fg-249 {
- color: $xterm-fg-249;
- }
-
- .xterm-fg-250 {
- color: $xterm-fg-250;
- }
-
- .xterm-fg-251 {
- color: $xterm-fg-251;
- }
-
- .xterm-fg-252 {
- color: $xterm-fg-252;
- }
-
- .xterm-fg-253 {
- color: $xterm-fg-253;
- }
-
- .xterm-fg-254 {
- color: $xterm-fg-254;
- }
-
- .xterm-fg-255 {
- color: $xterm-fg-255;
- }
-}
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 57d43beaf21..9c01a2f8bda 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -1,4 +1,5 @@
@import 'framework/variables';
+@import 'framework/variables_overrides';
@import 'peek/views/rblineprof';
#js-peek {
@@ -6,15 +7,15 @@
left: 0;
top: 0;
width: 100%;
- z-index: 1039;
+ z-index: #{$zindex-modal-backdrop + 1};
height: $performance-bar-height;
background: $black;
line-height: $performance-bar-height;
- color: $perf-bar-text;
+ color: $gl-gray-400;
select {
- color: $perf-bar-text;
+ color: $gl-gray-400;
width: 200px;
}
@@ -53,7 +54,7 @@
padding: 4px 6px;
font-family: Consolas, 'Liberation Mono', Courier, monospace;
line-height: 1;
- color: $perf-bar-bucket-color;
+ color: $gl-gray-200;
border-radius: 3px;
box-shadow: 0 1px 0 $perf-bar-bucket-box-shadow-from,
inset 0 1px 2px $perf-bar-bucket-box-shadow-to;
@@ -67,6 +68,10 @@
}
}
+ .current-host.canary {
+ color: $perf-bar-canary-text;
+ }
+
strong {
color: $white-light;
}
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb
index ed13ead63f9..68e14f0c2e5 100644
--- a/app/controllers/abuse_reports_controller.rb
+++ b/app/controllers/abuse_reports_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AbuseReportsController < ApplicationController
before_action :set_user, only: [:new]
@@ -30,6 +32,7 @@ class AbuseReportsController < ApplicationController
))
end
+ # rubocop: disable CodeReuse/ActiveRecord
def set_user
@user = User.find_by(id: params[:user_id])
@@ -39,4 +42,5 @@ class AbuseReportsController < ApplicationController
redirect_to @user, alert: "Cannot create the abuse report. This user has been blocked."
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb
index dc9a6df5f75..d5537023b26 100644
--- a/app/controllers/admin/abuse_reports_controller.rb
+++ b/app/controllers/admin/abuse_reports_controller.rb
@@ -1,8 +1,12 @@
+# frozen_string_literal: true
+
class Admin::AbuseReportsController < Admin::ApplicationController
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@abuse_reports = AbuseReport.order(id: :desc).page(params[:page])
@abuse_reports.includes(:reporter, :user)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def destroy
abuse_report = AbuseReport.find(params[:id])
diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb
index 9aaec905734..e3226c86b0b 100644
--- a/app/controllers/admin/appearances_controller.rb
+++ b/app/controllers/admin/appearances_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::AppearancesController < Admin::ApplicationController
before_action :set_appearance, except: :create
@@ -31,21 +33,21 @@ class Admin::AppearancesController < Admin::ApplicationController
@appearance.save
- redirect_to admin_appearances_path, notice: 'Logo was succesfully removed.'
+ redirect_to admin_appearances_path, notice: 'Logo was successfully removed.'
end
def header_logos
@appearance.remove_header_logo!
@appearance.save
- redirect_to admin_appearances_path, notice: 'Header logo was succesfully removed.'
+ redirect_to admin_appearances_path, notice: 'Header logo was successfully removed.'
end
def favicon
@appearance.remove_favicon!
@appearance.save
- redirect_to admin_appearances_path, notice: 'Favicon was succesfully removed.'
+ redirect_to admin_appearances_path, notice: 'Favicon was successfully removed.'
end
private
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb
index a4648b33cfa..ef182b981f1 100644
--- a/app/controllers/admin/application_controller.rb
+++ b/app/controllers/admin/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Provides a base class for Admin controllers to subclass
#
# Automatically sets the layout and ensures an administrator is logged in
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 9723e400574..8f683ca06ad 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -1,19 +1,58 @@
+# frozen_string_literal: true
+
class Admin::ApplicationSettingsController < Admin::ApplicationController
+ include InternalRedirect
before_action :set_application_setting
def show
end
+ def integrations
+ end
+
+ def repository
+ end
+
+ def templates
+ end
+
+ def ci_cd
+ end
+
+ def reporting
+ end
+
+ def metrics_and_profiling
+ end
+
+ def network
+ end
+
+ def geo
+ end
+
+ def preferences
+ end
+
def update
successful = ApplicationSettings::UpdateService
.new(@application_setting, current_user, application_setting_params)
.execute
- if successful
- redirect_to admin_application_settings_path,
- notice: 'Application settings saved successfully'
- else
- render :show
+ if recheck_user_consent?
+ session[:ask_for_usage_stats_consent] = current_user.requires_usage_stats_consent?
+ end
+
+ redirect_path = referer_path(request) || admin_application_settings_path
+
+ respond_to do |format|
+ if successful
+ format.json { head :ok }
+ format.html { redirect_to redirect_path, notice: 'Application settings saved successfully' }
+ else
+ format.json { head :bad_request }
+ format.html { render :show }
+ end
end
end
@@ -22,14 +61,15 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
format.html do
usage_data_json = JSON.pretty_generate(Gitlab::UsageData.data)
- render html: Gitlab::Highlight.highlight('payload.json', usage_data_json)
+ render html: Gitlab::Highlight.highlight('payload.json', usage_data_json, language: 'json')
end
format.json { render json: Gitlab::UsageData.to_json }
end
end
- def reset_runners_token
+ def reset_registration_token
@application_setting.reset_runners_registration_token!
+
flash[:notice] = 'New runners registration token has been generated!'
redirect_to admin_runners_path
end
@@ -76,14 +116,20 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
)
end
+ def recheck_user_consent?
+ return false unless session[:ask_for_usage_stats_consent]
+ return false unless params[:application_setting]
+
+ params[:application_setting].key?(:usage_ping_enabled) || params[:application_setting].key?(:version_check_enabled)
+ end
+
def visible_application_setting_attributes
ApplicationSettingsHelper.visible_attributes + [
:domain_blacklist_file,
disabled_oauth_sign_in_sources: [],
import_sources: [],
repository_storages: [],
- restricted_visibility_levels: [],
- sidekiq_throttling_queues: []
+ restricted_visibility_levels: []
]
end
end
diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb
index 5be23c76a95..6fc336714b6 100644
--- a/app/controllers/admin/applications_controller.rb
+++ b/app/controllers/admin/applications_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::ApplicationsController < Admin::ApplicationController
include OauthApplications
@@ -5,7 +7,7 @@ class Admin::ApplicationsController < Admin::ApplicationController
before_action :load_scopes, only: [:new, :create, :edit, :update]
def index
- @applications = Doorkeeper::Application.where("owner_id IS NULL")
+ @applications = ApplicationsFinder.new.execute
end
def show
@@ -46,7 +48,7 @@ class Admin::ApplicationsController < Admin::ApplicationController
private
def set_application
- @application = Doorkeeper::Application.where("owner_id IS NULL").find(params[:id])
+ @application = ApplicationsFinder.new(id: params[:id]).execute
end
# Only allow a trusted parameter "white list" through.
diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb
index 5f90ad7137d..fc877142418 100644
--- a/app/controllers/admin/background_jobs_controller.rb
+++ b/app/controllers/admin/background_jobs_controller.rb
@@ -1,7 +1,4 @@
+# frozen_string_literal: true
+
class Admin::BackgroundJobsController < Admin::ApplicationController
- def show
- ps_output, _ = Gitlab::Popen.popen(%W(ps ww -U #{Gitlab.config.gitlab.user} -o pid,pcpu,pmem,stat,start,command))
- @sidekiq_processes = ps_output.split("\n").grep(/sidekiq \d+\.\d+\.\d+/)
- @concurrency = Sidekiq.options[:concurrency]
- end
end
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index a9109a1d4d0..a91d9a534cd 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -1,12 +1,16 @@
+# frozen_string_literal: true
+
class Admin::BroadcastMessagesController < Admin::ApplicationController
include BroadcastMessagesHelper
before_action :finder, only: [:edit, :update, :destroy]
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@broadcast_messages = BroadcastMessage.order(ends_at: :desc).page(params[:page])
@broadcast_message = BroadcastMessage.new
end
+ # rubocop: enable CodeReuse/ActiveRecord
def edit
end
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 737942f3eb2..23cc9ee247a 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -1,13 +1,17 @@
+# frozen_string_literal: true
+
class Admin::DashboardController < Admin::ApplicationController
include CountHelper
- COUNTED_ITEMS = [Project, User, Group, ForkedProjectLink, Issue, MergeRequest,
- Note, Snippet, Key, Milestone].freeze
+ COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue,
+ MergeRequest, Note, Snippet, Key, Milestone].freeze
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@counts = Gitlab::Database::Count.approximate_counts(COUNTED_ITEMS)
@projects = Project.order_id_desc.without_deleted.with_route.limit(10)
@users = User.order_id_desc.limit(10)
@groups = Group.order_id_desc.with_route.limit(10)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index 5c2025c1988..49ce275ad14 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::DeployKeysController < Admin::ApplicationController
before_action :deploy_keys, only: [:index]
before_action :deploy_key, only: [:destroy, :edit, :update]
diff --git a/app/controllers/admin/gitaly_servers_controller.rb b/app/controllers/admin/gitaly_servers_controller.rb
index 11c4dfe3d8d..0a5566bfe70 100644
--- a/app/controllers/admin/gitaly_servers_controller.rb
+++ b/app/controllers/admin/gitaly_servers_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::GitalyServersController < Admin::ApplicationController
def index
@gitaly_servers = Gitaly::Server.all
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index d7a5b745d3f..46e85e1424f 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::GroupsController < Admin::ApplicationController
include MembersPresentation
@@ -10,6 +12,7 @@ class Admin::GroupsController < Admin::ApplicationController
@groups = @groups.page(params[:page])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def show
@group = Group.with_statistics.joins(:route).group('routes.path').find_by_full_path(params[:id])
@members = present_members(
@@ -18,6 +21,7 @@ class Admin::GroupsController < Admin::ApplicationController
AccessRequestsFinder.new(@group).execute(current_user))
@projects = @group.projects.with_statistics.page(params[:projects_page])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def new
@group = Group.new
diff --git a/app/controllers/admin/health_check_controller.rb b/app/controllers/admin/health_check_controller.rb
index 61247b280b3..25cc241e5b0 100644
--- a/app/controllers/admin/health_check_controller.rb
+++ b/app/controllers/admin/health_check_controller.rb
@@ -1,12 +1,7 @@
+# frozen_string_literal: true
+
class Admin::HealthCheckController < Admin::ApplicationController
def show
@errors = HealthCheck::Utils.process_checks(['standard'])
- @failing_storage_statuses = Gitlab::Git::Storage::Health.for_failing_storages
- end
-
- def reset_storage_health
- Gitlab::Git::Storage::FailureInfo.reset_all!
- redirect_to admin_health_check_path,
- notice: _('Git storage health information has been reset')
end
end
diff --git a/app/controllers/admin/hook_logs_controller.rb b/app/controllers/admin/hook_logs_controller.rb
index 3017f96c26f..8301b3aa880 100644
--- a/app/controllers/admin/hook_logs_controller.rb
+++ b/app/controllers/admin/hook_logs_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::HookLogsController < Admin::ApplicationController
include HooksExecution
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index a98c355c7ba..d0abdec50ae 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::HooksController < Admin::ApplicationController
include HooksExecution
diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb
index ceb45865804..b51c2f678ca 100644
--- a/app/controllers/admin/identities_controller.rb
+++ b/app/controllers/admin/identities_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::IdentitiesController < Admin::ApplicationController
before_action :user
before_action :identity, except: [:index, :new, :create]
@@ -44,9 +46,11 @@ class Admin::IdentitiesController < Admin::ApplicationController
protected
+ # rubocop: disable CodeReuse/ActiveRecord
def user
@user ||= User.find_by!(username: params[:user_id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def identity
@identity ||= user.identities.find(params[:id])
diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb
index a7b562b1d8e..706bcc1e549 100644
--- a/app/controllers/admin/impersonation_tokens_controller.rb
+++ b/app/controllers/admin/impersonation_tokens_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::ImpersonationTokensController < Admin::ApplicationController
before_action :user
@@ -9,6 +11,7 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
@impersonation_token = finder.build(impersonation_token_params)
if @impersonation_token.save
+ PersonalAccessToken.redis_store!(current_user.id, @impersonation_token.token)
redirect_to admin_user_impersonation_tokens_path, notice: "A new impersonation token has been created."
else
set_index_vars
@@ -30,9 +33,11 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
private
+ # rubocop: disable CodeReuse/ActiveRecord
def user
@user ||= User.find_by!(username: params[:user_id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def finder(options = {})
PersonalAccessTokensFinder.new({ user: user, impersonation: true }.merge(options))
@@ -42,11 +47,15 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
params.require(:personal_access_token).permit(:name, :expires_at, :impersonation, scopes: [])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def set_index_vars
@scopes = Gitlab::Auth.available_scopes(current_user)
@impersonation_token ||= finder.build
@inactive_impersonation_tokens = finder(state: 'inactive').execute
@active_impersonation_tokens = finder(state: 'active').execute.order(:expires_at)
+
+ @new_impersonation_token = PersonalAccessToken.redis_getdel(current_user.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb
index d2f947d2c66..08d7e3b4fa2 100644
--- a/app/controllers/admin/impersonations_controller.rb
+++ b/app/controllers/admin/impersonations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::ImpersonationsController < Admin::ApplicationController
skip_before_action :authenticate_admin!
before_action :authenticate_impersonator!
diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb
index e355d5fdea7..0c1afdc3d3b 100644
--- a/app/controllers/admin/jobs_controller.rb
+++ b/app/controllers/admin/jobs_controller.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
class Admin::JobsController < Admin::ApplicationController
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@scope = params[:scope]
@all_builds = Ci::Build
@@ -16,6 +19,7 @@ class Admin::JobsController < Admin::ApplicationController
end
@builds = @builds.page(params[:page]).per(30)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def cancel_all
Ci::Build.running_or_pending.each(&:cancel)
diff --git a/app/controllers/admin/keys_controller.rb b/app/controllers/admin/keys_controller.rb
index 0b76193a90e..4e9262ccc96 100644
--- a/app/controllers/admin/keys_controller.rb
+++ b/app/controllers/admin/keys_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::KeysController < Admin::ApplicationController
before_action :user, only: [:show, :destroy]
@@ -24,9 +26,11 @@ class Admin::KeysController < Admin::ApplicationController
protected
+ # rubocop: disable CodeReuse/ActiveRecord
def user
@user ||= User.find_by!(username: params[:user_id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def key_params
params.require(:user_id, :id)
diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb
index 7eb8f758807..aa5eae7a474 100644
--- a/app/controllers/admin/labels_controller.rb
+++ b/app/controllers/admin/labels_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::LabelsController < Admin::ApplicationController
before_action :set_label, only: [:show, :edit, :update, :destroy]
diff --git a/app/controllers/admin/logs_controller.rb b/app/controllers/admin/logs_controller.rb
index 12a27cede75..06b0e6a15a3 100644
--- a/app/controllers/admin/logs_controller.rb
+++ b/app/controllers/admin/logs_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::LogsController < Admin::ApplicationController
before_action :loggers
@@ -12,7 +14,8 @@ class Admin::LogsController < Admin::ApplicationController
Gitlab::GitLogger,
Gitlab::EnvironmentLogger,
Gitlab::SidekiqLogger,
- Gitlab::RepositoryCheckLogger
+ Gitlab::RepositoryCheckLogger,
+ Gitlab::ProjectServiceLogger
]
end
end
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 3afe66c3566..550f29a58d2 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::ProjectsController < Admin::ApplicationController
include MembersPresentation
@@ -19,6 +21,7 @@ class Admin::ProjectsController < Admin::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def show
if @group
@group_members = present_members(
@@ -30,7 +33,9 @@ class Admin::ProjectsController < Admin::ApplicationController
@requesters = present_members(
AccessRequestsFinder.new(@project).execute(current_user))
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def transfer
namespace = Namespace.find_by(id: params[:new_namespace_id])
::Projects::TransferService.new(@project, current_user, params.dup).execute(namespace)
@@ -38,6 +43,7 @@ class Admin::ProjectsController < Admin::ApplicationController
@project.reload
redirect_to admin_project_path(@project)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def repository_check
RepositoryCheck::SingleRepositoryWorker.perform_async(@project.id)
diff --git a/app/controllers/admin/requests_profiles_controller.rb b/app/controllers/admin/requests_profiles_controller.rb
index a478176e138..64d74ae4231 100644
--- a/app/controllers/admin/requests_profiles_controller.rb
+++ b/app/controllers/admin/requests_profiles_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::RequestsProfilesController < Admin::ApplicationController
def index
@profile_token = Gitlab::RequestProfiler.profile_token
diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb
index 51d5799cd89..774ce04d079 100644
--- a/app/controllers/admin/runner_projects_controller.rb
+++ b/app/controllers/admin/runner_projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::RunnerProjectsController < Admin::ApplicationController
before_action :project, only: [:create]
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 6c76c55a9d4..0b6ff491c66 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -1,12 +1,13 @@
+# frozen_string_literal: true
+
class Admin::RunnersController < Admin::ApplicationController
before_action :runner, except: :index
def index
- sort = params[:sort] == 'contacted_asc' ? { contacted_at: :asc } : { id: :desc }
- @runners = Ci::Runner.order(sort)
- @runners = @runners.search(params[:search]) if params[:search].present?
- @runners = @runners.page(params[:page]).per(30)
- @active_runners_cnt = Ci::Runner.online.count
+ finder = Admin::RunnersFinder.new(params: params)
+ @runners = finder.execute
+ @active_runners_count = Ci::Runner.online.count
+ @sort = finder.sort_key
end
def show
@@ -57,6 +58,7 @@ class Admin::RunnersController < Admin::ApplicationController
params.require(:runner).permit(Ci::Runner::FORM_EDITABLE)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def assign_builds_and_projects
@builds = runner.builds.order('id DESC').first(30)
@projects =
@@ -69,4 +71,5 @@ class Admin::RunnersController < Admin::ApplicationController
@projects = @projects.where.not(id: runner.projects.select(:id)) if runner.projects.any?
@projects = @projects.page(params[:page]).per(30)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index 91a36af34f3..c455930c044 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -30,16 +30,20 @@ class Admin::ServicesController < Admin::ApplicationController
private
+ # rubocop: disable CodeReuse/ActiveRecord
def services_templates
Service.available_services_names.map do |service_name|
service_template = "#{service_name}_service".camelize.constantize
service_template.where(template: true).first_or_create
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def service
@service ||= Service.where(id: params[:id], template: true).first
end
+ # rubocop: enable CodeReuse/ActiveRecord
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42430')
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index d52d67a67a5..18d22c95b61 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
class Admin::SpamLogsController < Admin::ApplicationController
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@spam_logs = SpamLog.order(id: :desc).page(params[:page])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def destroy
spam_log = SpamLog.find(params[:id])
diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb
index 99039724521..244fc2b31bb 100644
--- a/app/controllers/admin/system_info_controller.rb
+++ b/app/controllers/admin/system_info_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::SystemInfoController < Admin::ApplicationController
EXCLUDED_MOUNT_OPTIONS = [
'nobrowse',
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index a51a8c3ed4a..b783c0e2a6f 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::UsersController < Admin::ApplicationController
before_action :user, except: [:index, :new, :create]
@@ -174,9 +176,11 @@ class Admin::UsersController < Admin::ApplicationController
user == current_user
end
+ # rubocop: disable CodeReuse/ActiveRecord
def user
@user ||= User.find_by!(username: params[:id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def redirect_back_or_admin_user(options = {})
redirect_back_or_default(default: default_route, options: options)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e5b38898a67..b839da7770d 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'gon'
require 'fogbugz'
@@ -10,6 +12,9 @@ class ApplicationController < ActionController::Base
include WorkhorseHelper
include EnforcesTwoFactorAuthentication
include WithPerformanceBar
+ # this can be removed after switching to rails 5
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/51908
+ include InvalidUTF8ErrorHandler unless Gitlab.rails5?
before_action :authenticate_sessionless_user!
before_action :authenticate_user!
@@ -22,6 +27,7 @@ class ApplicationController < ActionController::Base
before_action :add_gon_variables, unless: [:peek_request?, :json_request?]
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :require_email, unless: :devise_controller?
+ before_action :set_usage_stats_consent_flag
around_action :set_locale
@@ -40,6 +46,8 @@ class ApplicationController < ActionController::Base
:git_import_enabled?, :gitlab_project_import_enabled?,
:manifest_import_enabled?
+ DEFAULT_GITLAB_CACHE_CONTROL = "#{ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL}, no-store".freeze
+
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
render "errors/encoding", layout: "errors", status: 500
@@ -62,8 +70,7 @@ class ApplicationController < ActionController::Base
head :forbidden, retry_after: Gitlab::Auth::UniqueIpsLimiter.config.unique_ips_limit_time_window
end
- rescue_from Gitlab::Git::Storage::Inaccessible, GRPC::Unavailable, Gitlab::Git::CommandError do |exception|
- Raven.capture_exception(exception) if sentry_enabled?
+ rescue_from GRPC::Unavailable, Gitlab::Git::CommandError do |exception|
log_exception(exception)
headers['Retry-After'] = exception.retry_after if exception.respond_to?(:retry_after)
@@ -105,11 +112,21 @@ class ApplicationController < ActionController::Base
request.env['rack.session.options'][:expire_after] = Settings.gitlab['unauthenticated_session_expire_delay']
end
+ def render(*args)
+ super.tap do
+ # Set a header for custom error pages to prevent them from being intercepted by gitlab-workhorse
+ if response.content_type == 'text/html' && (400..599).cover?(response.status)
+ response.headers['X-GitLab-Custom-Error'] = '1'
+ end
+ end
+ end
+
protected
def append_info_to_payload(payload)
super
+ payload[:ua] = request.env["HTTP_USER_AGENT"]
payload[:remote_ip] = request.remote_ip
logged_user = auth_user
@@ -164,11 +181,11 @@ class ApplicationController < ActionController::Base
Ability.allowed?(object, action, subject)
end
- def access_denied!(message = nil)
+ def access_denied!(message = nil, status = nil)
# If we display a custom access denied message to the user, we don't want to
# hide existence of the resource, rather tell them they cannot access it using
# the provided message
- status = message.present? ? :forbidden : :not_found
+ status ||= message.present? ? :forbidden : :not_found
respond_to do |format|
format.any { head status }
@@ -229,6 +246,13 @@ class ApplicationController < ActionController::Base
headers['X-XSS-Protection'] = '1; mode=block'
headers['X-UA-Compatible'] = 'IE=edge'
headers['X-Content-Type-Options'] = 'nosniff'
+
+ if current_user
+ # Adds `no-store` to the DEFAULT_CACHE_CONTROL, to prevent security
+ # concerns due to caching private data.
+ headers['Cache-Control'] = DEFAULT_GITLAB_CACHE_CONTROL
+ headers["Pragma"] = "no-cache" # HTTP 1.0 compatibility
+ end
end
def validate_user_service_ticket!
@@ -268,9 +292,10 @@ class ApplicationController < ActionController::Base
end
def event_filter
- # Split using comma to maintain backward compatibility Ex/ "filter1,filter2"
- filters = cookies['event_filter'].split(',')[0] if cookies['event_filter'].present?
- @event_filter ||= EventFilter.new(filters)
+ @event_filter ||=
+ EventFilter.new(params[:event_filter].presence || cookies[:event_filter]).tap do |new_event_filter|
+ cookies[:event_filter] = new_event_filter.filter
+ end
end
# JSON for infinite scroll via Pager object
@@ -433,4 +458,29 @@ class ApplicationController < ActionController::Base
!(peek_request? || devise_controller?)
end
+
+ def set_usage_stats_consent_flag
+ return unless current_user
+ return if sessionless_user?
+ return if session.has_key?(:ask_for_usage_stats_consent)
+
+ session[:ask_for_usage_stats_consent] = current_user.requires_usage_stats_consent?
+
+ if session[:ask_for_usage_stats_consent]
+ disable_usage_stats
+ end
+ end
+
+ def disable_usage_stats
+ application_setting_params = {
+ usage_ping_enabled: false,
+ version_check_enabled: false,
+ skip_usage_stats_user: true
+ }
+ settings = Gitlab::CurrentSettings.current_application_settings
+
+ ApplicationSettings::UpdateService
+ .new(settings, current_user, application_setting_params)
+ .execute
+ end
end
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index 86bade49ec9..0d5c8657c9e 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -1,67 +1,41 @@
-class AutocompleteController < ApplicationController
- AWARD_EMOJI_MAX = 100
+# frozen_string_literal: true
+class AutocompleteController < ApplicationController
skip_before_action :authenticate_user!, only: [:users, :award_emojis]
- before_action :load_project, only: [:users]
- before_action :load_group, only: [:users]
def users
- @users = AutocompleteUsersFinder.new(params: params, current_user: current_user, project: @project, group: @group).execute
-
- render json: UserSerializer.new.represent(@users)
- end
+ project = Autocomplete::ProjectFinder
+ .new(current_user, params)
+ .execute
- def user
- @user = User.find(params[:id])
- render json: UserSerializer.new.represent(@user)
- end
+ group = Autocomplete::GroupFinder
+ .new(current_user, project, params)
+ .execute
- def projects
- project = Project.find_by_id(params[:project_id])
- projects = projects_finder.execute(project, search: params[:search], offset_id: params[:offset_id])
+ users = Autocomplete::UsersFinder
+ .new(params: params, current_user: current_user, project: project, group: group)
+ .execute
- render json: projects.to_json(only: [:id, :name_with_namespace], methods: :name_with_namespace)
+ render json: UserSerializer.new.represent(users)
end
- def award_emojis
- emoji_with_count = AwardEmoji
- .limit(AWARD_EMOJI_MAX)
- .where(user: current_user)
- .group(:name)
- .order('count_all DESC, name ASC')
- .count
-
- # Transform from hash to array to guarantee json order
- # e.g. { 'thumbsup' => 2, 'thumbsdown' = 1 }
- # => [{ name: 'thumbsup' }, { name: 'thumbsdown' }]
- render json: emoji_with_count.map { |k, v| { name: k } }
- end
-
- private
-
- def load_group
- @group ||= begin
- if @project.blank? && params[:group_id].present?
- group = Group.find(params[:group_id])
- return render_404 unless can?(current_user, :read_group, group)
+ def user
+ user = UserFinder.new(params[:id]).find_by_id!
- group
- end
- end
+ render json: UserSerializer.new.represent(user)
end
- def load_project
- @project ||= begin
- if params[:project_id].present?
- project = Project.find(params[:project_id])
- return render_404 unless can?(current_user, :read_project, project)
+ # Displays projects to use for the dropdown when moving a resource from one
+ # project to another.
+ def projects
+ projects = Autocomplete::MoveToProjectFinder
+ .new(current_user, params)
+ .execute
- project
- end
- end
+ render json: MoveToProjectSerializer.new.represent(projects)
end
- def projects_finder
- MoveToProjectFinder.new(current_user)
+ def award_emojis
+ render json: AwardedEmojiFinder.new(current_user).execute
end
end
diff --git a/app/controllers/boards/application_controller.rb b/app/controllers/boards/application_controller.rb
index b2675025fc0..eab908ba5ed 100644
--- a/app/controllers/boards/application_controller.rb
+++ b/app/controllers/boards/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class ApplicationController < ::ApplicationController
respond_to :json
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
index 7dd19f87ef5..7f874687212 100644
--- a/app/controllers/boards/issues_controller.rb
+++ b/app/controllers/boards/issues_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class IssuesController < Boards::ApplicationController
include BoardsResponses
@@ -11,20 +13,27 @@ module Boards
before_action :authorize_update_issue, only: [:update]
skip_before_action :authenticate_user!, only: [:index]
+ # rubocop: disable CodeReuse/ActiveRecord
def index
list_service = Boards::Issues::ListService.new(board_parent, current_user, filter_params)
issues = list_service.execute
issues = issues.page(params[:page]).per(params[:per] || 20).without_count
- make_sure_position_is_set(issues) if Gitlab::Database.read_write?
- issues = issues.preload(:project,
- :milestone,
+ Issue.move_to_end(issues) if Gitlab::Database.read_write?
+ issues = issues.preload(:milestone,
:assignees,
+ project: [
+ :route,
+ {
+ namespace: [:route]
+ }
+ ],
labels: [:priorities],
notes: [:award_emoji, :author]
)
render_issues(issues, list_service.metadata)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def create
service = Boards::Issues::CreateService.new(board_parent, project, current_user, issue_params)
@@ -56,12 +65,6 @@ module Boards
render json: data
end
- def make_sure_position_is_set(issues)
- issues.each do |issue|
- issue.move_to_end && issue.save unless issue.relative_position
- end
- end
-
def issue
@issue ||= issues_finder.find(params[:id])
end
diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb
index e8b5934f2a9..ccd02144671 100644
--- a/app/controllers/boards/lists_controller.rb
+++ b/app/controllers/boards/lists_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class ListsController < Boards::ApplicationController
include BoardsResponses
diff --git a/app/controllers/chaos_controller.rb b/app/controllers/chaos_controller.rb
new file mode 100644
index 00000000000..b4f46cddbe9
--- /dev/null
+++ b/app/controllers/chaos_controller.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+class ChaosController < ActionController::Base
+ before_action :validate_request
+
+ def leakmem
+ memory_mb = (params[:memory_mb]&.to_i || 100)
+ duration_s = (params[:duration_s]&.to_i || 30).seconds
+
+ start = Time.now
+ retainer = []
+ # Add `n` 1mb chunks of memory to the retainer array
+ memory_mb.times { retainer << "x" * 1.megabyte }
+
+ duration_taken = (Time.now - start).seconds
+ Kernel.sleep duration_s - duration_taken if duration_s > duration_taken
+
+ render text: "OK", content_type: 'text/plain'
+ end
+
+ def cpuspin
+ duration_s = (params[:duration_s]&.to_i || 30).seconds
+ end_time = Time.now + duration_s.seconds
+
+ rand while Time.now < end_time
+
+ render text: "OK", content_type: 'text/plain'
+ end
+
+ def sleep
+ duration_s = (params[:duration_s]&.to_i || 30).seconds
+ Kernel.sleep duration_s
+
+ render text: "OK", content_type: 'text/plain'
+ end
+
+ def kill
+ Process.kill("KILL", Process.pid)
+ end
+
+ private
+
+ def validate_request
+ secret = ENV['GITLAB_CHAOS_SECRET']
+ # GITLAB_CHAOS_SECRET is required unless you're running in Development mode
+ if !secret && !Rails.env.development?
+ render text: "chaos misconfigured: please configure GITLAB_CHAOS_SECRET when using GITLAB_ENABLE_CHAOS_ENDPOINTS outside of a development environment", content_type: 'text/plain', status: 500
+ end
+
+ return unless secret
+
+ unless request.headers["HTTP_X_CHAOS_SECRET"] == secret
+ render text: "To experience chaos, please set X-Chaos-Secret header", content_type: 'text/plain', status: 401
+ end
+ end
+end
diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb
index 738a6a5173e..99ce24bd435 100644
--- a/app/controllers/ci/lints_controller.rb
+++ b/app/controllers/ci/lints_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class LintsController < ::ApplicationController
before_action :authenticate_user!
diff --git a/app/controllers/clusters/applications_controller.rb b/app/controllers/clusters/applications_controller.rb
new file mode 100644
index 00000000000..250f42f3096
--- /dev/null
+++ b/app/controllers/clusters/applications_controller.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class Clusters::ApplicationsController < Clusters::BaseController
+ before_action :cluster
+ before_action :authorize_create_cluster!, only: [:create]
+
+ def create
+ Clusters::Applications::CreateService
+ .new(@cluster, current_user, create_cluster_application_params)
+ .execute(request)
+
+ head :no_content
+ rescue Clusters::Applications::CreateService::InvalidApplicationError
+ render_404
+ rescue StandardError
+ head :bad_request
+ end
+
+ private
+
+ def cluster
+ @cluster ||= clusterable.clusters.find(params[:id]) || render_404
+ end
+
+ def create_cluster_application_params
+ params.permit(:application, :hostname)
+ end
+end
diff --git a/app/controllers/clusters/base_controller.rb b/app/controllers/clusters/base_controller.rb
new file mode 100644
index 00000000000..ef42f7c4074
--- /dev/null
+++ b/app/controllers/clusters/base_controller.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+class Clusters::BaseController < ApplicationController
+ include RoutableActions
+
+ skip_before_action :authenticate_user!
+ before_action :authorize_read_cluster!
+
+ helper_method :clusterable
+
+ private
+
+ def cluster
+ @cluster ||= clusterable.clusters.find(params[:id])
+ .present(current_user: current_user)
+ end
+
+ def authorize_update_cluster!
+ access_denied! unless can?(current_user, :update_cluster, cluster)
+ end
+
+ def authorize_admin_cluster!
+ access_denied! unless can?(current_user, :admin_cluster, cluster)
+ end
+
+ def authorize_read_cluster!
+ access_denied! unless can?(current_user, :read_cluster, clusterable)
+ end
+
+ def authorize_create_cluster!
+ access_denied! unless can?(current_user, :create_cluster, clusterable)
+ end
+
+ def clusterable
+ raise NotImplementedError
+ end
+end
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
new file mode 100644
index 00000000000..2e9c77ae55c
--- /dev/null
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -0,0 +1,218 @@
+# frozen_string_literal: true
+
+class Clusters::ClustersController < Clusters::BaseController
+ include RoutableActions
+
+ before_action :cluster, except: [:index, :new, :create_gcp, :create_user]
+ before_action :generate_gcp_authorize_url, only: [:new]
+ before_action :validate_gcp_token, only: [:new]
+ before_action :gcp_cluster, only: [:new]
+ before_action :user_cluster, only: [:new]
+ before_action :authorize_create_cluster!, only: [:new]
+ before_action :authorize_update_cluster!, only: [:update]
+ before_action :authorize_admin_cluster!, only: [:destroy]
+ before_action :update_applications_status, only: [:cluster_status]
+
+ helper_method :token_in_session
+
+ STATUS_POLLING_INTERVAL = 10_000
+
+ def index
+ clusters = ClustersFinder.new(clusterable, current_user, :all).execute
+ @clusters = clusters.page(params[:page]).per(20)
+ end
+
+ def new
+ end
+
+ # Overridding ActionController::Metal#status is NOT a good idea
+ def cluster_status
+ respond_to do |format|
+ format.json do
+ Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL)
+
+ render json: ClusterSerializer
+ .new(current_user: @current_user)
+ .represent_status(@cluster)
+ end
+ end
+ end
+
+ def show
+ end
+
+ def update
+ Clusters::UpdateService
+ .new(current_user, update_params)
+ .execute(cluster)
+
+ if cluster.valid?
+ respond_to do |format|
+ format.json do
+ head :no_content
+ end
+ format.html do
+ flash[:notice] = _('Kubernetes cluster was successfully updated.')
+ redirect_to cluster.show_path
+ end
+ end
+ else
+ respond_to do |format|
+ format.json { head :bad_request }
+ format.html { render :show }
+ end
+ end
+ end
+
+ def destroy
+ if cluster.destroy
+ flash[:notice] = _('Kubernetes cluster integration was successfully removed.')
+ redirect_to clusterable.index_path, status: :found
+ else
+ flash[:notice] = _('Kubernetes cluster integration was not removed.')
+ render :show
+ end
+ end
+
+ def create_gcp
+ @gcp_cluster = ::Clusters::CreateService
+ .new(current_user, create_gcp_cluster_params)
+ .execute(access_token: token_in_session)
+ .present(current_user: current_user)
+
+ if @gcp_cluster.persisted?
+ redirect_to @gcp_cluster.show_path
+ else
+ generate_gcp_authorize_url
+ validate_gcp_token
+ user_cluster
+
+ render :new, locals: { active_tab: 'gcp' }
+ end
+ end
+
+ def create_user
+ @user_cluster = ::Clusters::CreateService
+ .new(current_user, create_user_cluster_params)
+ .execute(access_token: token_in_session)
+ .present(current_user: current_user)
+
+ if @user_cluster.persisted?
+ redirect_to @user_cluster.show_path
+ else
+ generate_gcp_authorize_url
+ validate_gcp_token
+ gcp_cluster
+
+ render :new, locals: { active_tab: 'user' }
+ end
+ end
+
+ private
+
+ def update_params
+ if cluster.managed?
+ params.require(:cluster).permit(
+ :enabled,
+ :environment_scope,
+ platform_kubernetes_attributes: [
+ :namespace
+ ]
+ )
+ else
+ params.require(:cluster).permit(
+ :enabled,
+ :name,
+ :environment_scope,
+ platform_kubernetes_attributes: [
+ :api_url,
+ :token,
+ :ca_cert,
+ :namespace
+ ]
+ )
+ end
+ end
+
+ def create_gcp_cluster_params
+ params.require(:cluster).permit(
+ :enabled,
+ :name,
+ :environment_scope,
+ provider_gcp_attributes: [
+ :gcp_project_id,
+ :zone,
+ :num_nodes,
+ :machine_type,
+ :legacy_abac
+ ]).merge(
+ provider_type: :gcp,
+ platform_type: :kubernetes,
+ clusterable: clusterable.subject
+ )
+ end
+
+ def create_user_cluster_params
+ params.require(:cluster).permit(
+ :enabled,
+ :name,
+ :environment_scope,
+ platform_kubernetes_attributes: [
+ :namespace,
+ :api_url,
+ :token,
+ :ca_cert,
+ :authorization_type
+ ]).merge(
+ provider_type: :user,
+ platform_type: :kubernetes,
+ clusterable: clusterable.subject
+ )
+ end
+
+ def generate_gcp_authorize_url
+ state = generate_session_key_redirect(clusterable.new_path.to_s)
+
+ @authorize_url = GoogleApi::CloudPlatform::Client.new(
+ nil, callback_google_api_auth_url,
+ state: state).authorize_url
+ rescue GoogleApi::Auth::ConfigMissingError
+ # no-op
+ end
+
+ def gcp_cluster
+ @gcp_cluster = ::Clusters::Cluster.new.tap do |cluster|
+ cluster.build_provider_gcp
+ end.present(current_user: current_user)
+ end
+
+ def user_cluster
+ @user_cluster = ::Clusters::Cluster.new.tap do |cluster|
+ cluster.build_platform_kubernetes
+ end.present(current_user: current_user)
+ end
+
+ def validate_gcp_token
+ @valid_gcp_token = GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
+ .validate_token(expires_at_in_session)
+ end
+
+ def token_in_session
+ session[GoogleApi::CloudPlatform::Client.session_key_for_token]
+ end
+
+ def expires_at_in_session
+ @expires_at_in_session ||=
+ session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at]
+ end
+
+ def generate_session_key_redirect(uri)
+ GoogleApi::CloudPlatform::Client.new_session_key_for_redirect_uri do |key|
+ session[key] = uri
+ end
+ end
+
+ def update_applications_status
+ @cluster.applications.each(&:schedule_status_update)
+ end
+end
diff --git a/app/controllers/concerns/accepts_pending_invitations.rb b/app/controllers/concerns/accepts_pending_invitations.rb
index 6e8aef52b52..cb66c1a055d 100644
--- a/app/controllers/concerns/accepts_pending_invitations.rb
+++ b/app/controllers/concerns/accepts_pending_invitations.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AcceptsPendingInvitations
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index dfa1da7872c..5507328f8ae 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == AuthenticatesWithTwoFactor
#
# Controller concern to handle two-factor authentication
@@ -88,6 +90,7 @@ module AuthenticatesWithTwoFactor
# Setup in preparation of communication with a U2F (universal 2nd factor) device
# Actual communication is performed using a Javascript API
+ # rubocop: disable CodeReuse/ActiveRecord
def setup_u2f_authentication(user)
key_handles = user.u2f_registrations.pluck(:key_handle)
u2f = U2F::U2F.new(u2f_app_id)
@@ -99,4 +102,5 @@ module AuthenticatesWithTwoFactor
sign_requests: sign_requests })
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb
index da830ec2cb1..3cdf4ddf8bb 100644
--- a/app/controllers/concerns/boards_responses.rb
+++ b/app/controllers/concerns/boards_responses.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BoardsResponses
include Gitlab::Utils::StrongMemoize
@@ -48,7 +50,10 @@ module BoardsResponses
end
def authorize_create_issue
- authorize_action_for!(project, :admin_issue)
+ list = List.find(issue_params[:list_id])
+ action = list.backlog? ? :create_issue : :admin_issue
+
+ authorize_action_for!(project, action)
end
def authorize_admin_list
diff --git a/app/controllers/concerns/checks_collaboration.rb b/app/controllers/concerns/checks_collaboration.rb
index 81367663a06..1fa82f7dcd4 100644
--- a/app/controllers/concerns/checks_collaboration.rb
+++ b/app/controllers/concerns/checks_collaboration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChecksCollaboration
def can_collaborate_with_project?(project, ref: nil)
return true if can?(current_user, :push_code, project)
diff --git a/app/controllers/concerns/continue_params.rb b/app/controllers/concerns/continue_params.rb
index 8b7355974df..f0e6adf4dec 100644
--- a/app/controllers/concerns/continue_params.rb
+++ b/app/controllers/concerns/continue_params.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContinueParams
include InternalRedirect
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/controller_with_cross_project_access_check.rb b/app/controllers/concerns/controller_with_cross_project_access_check.rb
index a45c3384578..3f72f092683 100644
--- a/app/controllers/concerns/controller_with_cross_project_access_check.rb
+++ b/app/controllers/concerns/controller_with_cross_project_access_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ControllerWithCrossProjectAccessCheck
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index b26a76d2b62..b3777fd2b0f 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CreatesCommit
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize
@@ -65,7 +67,7 @@ module CreatesCommit
flash[:notice] = nil
else
target = different_project? ? "project" : "branch"
- flash[:notice] << " You can now submit a merge request to get this change into the original #{target}."
+ flash[:notice] = flash[:notice] + " You can now submit a merge request to get this change into the original #{target}."
end
end
end
@@ -99,6 +101,7 @@ module CreatesCommit
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_request_exists?
strong_memoize(:merge_request) do
MergeRequestsFinder.new(current_user, project_id: @project.id)
@@ -110,6 +113,7 @@ module CreatesCommit
target_branch: @start_branch)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def different_project?
diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb
index 1ab107168c0..c1ef848e1e7 100644
--- a/app/controllers/concerns/cycle_analytics_params.rb
+++ b/app/controllers/concerns/cycle_analytics_params.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CycleAnalyticsParams
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/diff_for_path.rb b/app/controllers/concerns/diff_for_path.rb
index d5388c4cd20..6be7a2a18a2 100644
--- a/app/controllers/concerns/diff_for_path.rb
+++ b/app/controllers/concerns/diff_for_path.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffForPath
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/enforces_two_factor_authentication.rb b/app/controllers/concerns/enforces_two_factor_authentication.rb
index 997af4ab9e9..71bdef8ce03 100644
--- a/app/controllers/concerns/enforces_two_factor_authentication.rb
+++ b/app/controllers/concerns/enforces_two_factor_authentication.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == EnforcesTwoFactorAuthentication
#
# Controller concern to enforce two-factor authentication requirements
@@ -24,6 +26,7 @@ module EnforcesTwoFactorAuthentication
current_user.try(:require_two_factor_authentication_from_group?)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def two_factor_authentication_reason(global: -> {}, group: -> {})
if two_factor_authentication_required?
if Gitlab::CurrentSettings.require_two_factor_authentication?
@@ -34,6 +37,7 @@ module EnforcesTwoFactorAuthentication
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def two_factor_grace_period
periods = [Gitlab::CurrentSettings.two_factor_grace_period]
diff --git a/app/controllers/concerns/group_tree.rb b/app/controllers/concerns/group_tree.rb
index 6ec6897e707..4f56346832c 100644
--- a/app/controllers/concerns/group_tree.rb
+++ b/app/controllers/concerns/group_tree.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
module GroupTree
# rubocop:disable Gitlab/ModuleWithInstanceVariables
+ # rubocop: disable CodeReuse/ActiveRecord
def render_group_tree(groups)
groups = groups.sort_by_attribute(@sort = params[:sort])
@@ -23,7 +26,9 @@ module GroupTree
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def filtered_groups_with_ancestors(groups)
filtered_groups = groups.search(params[:filter]).page(params[:page])
@@ -40,4 +45,5 @@ module GroupTree
filtered_groups
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/concerns/hooks_execution.rb b/app/controllers/concerns/hooks_execution.rb
index a22e46b4860..e8add1f4055 100644
--- a/app/controllers/concerns/hooks_execution.rb
+++ b/app/controllers/concerns/hooks_execution.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module HooksExecution
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/internal_redirect.rb b/app/controllers/concerns/internal_redirect.rb
index 10b9852e329..6785e6972d0 100644
--- a/app/controllers/concerns/internal_redirect.rb
+++ b/app/controllers/concerns/internal_redirect.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module InternalRedirect
extend ActiveSupport::Concern
@@ -36,4 +38,10 @@ module InternalRedirect
path_with_query = [uri.path, uri.query].compact.join('?')
[path_with_query, uri.fragment].compact.join("#")
end
+
+ def referer_path(request)
+ return unless request.referer.presence
+
+ URI(request.referer).path
+ end
end
diff --git a/app/controllers/concerns/invalid_utf8_error_handler.rb b/app/controllers/concerns/invalid_utf8_error_handler.rb
new file mode 100644
index 00000000000..44c6d6b0da0
--- /dev/null
+++ b/app/controllers/concerns/invalid_utf8_error_handler.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module InvalidUTF8ErrorHandler
+ extend ActiveSupport::Concern
+
+ included do
+ rescue_from ArgumentError, with: :handle_invalid_utf8
+ end
+
+ private
+
+ def handle_invalid_utf8(error)
+ if error.message == "invalid byte sequence in UTF-8"
+ render_412
+ else
+ raise(error)
+ end
+ end
+
+ def render_412
+ respond_to do |format|
+ format.html { render "errors/precondition_failed", layout: "errors", status: 412 }
+ format.js { render json: { error: 'Invalid UTF-8' }, status: :precondition_failed, content_type: 'application/json' }
+ format.any { head :precondition_failed }
+ end
+ end
+end
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index 37e03d70b6f..ad9cc0925b7 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
module IssuableActions
extend ActiveSupport::Concern
+ include Gitlab::Utils::StrongMemoize
included do
before_action :labels, only: [:show, :new, :edit]
@@ -89,12 +92,18 @@ module IssuableActions
render json: { notice: "#{quantity} #{resource_name.pluralize(quantity)} updated" }
end
+ # rubocop: disable CodeReuse/ActiveRecord
def discussions
notes = issuable.discussion_notes
.inc_relations_for_view
+ .with_notes_filter(notes_filter)
.includes(:noteable)
.fresh
+ if notes_filter != UserPreference::NOTES_FILTERS[:only_comments]
+ notes = ResourceEvents::MergeIntoNotesService.new(issuable, current_user).execute(notes)
+ end
+
notes = prepare_notes_for_rendering(notes)
notes = notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }
@@ -102,9 +111,36 @@ module IssuableActions
render json: discussion_serializer.represent(discussions, context: self)
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ def notes_filter
+ strong_memoize(:notes_filter) do
+ notes_filter_param = params[:notes_filter]&.to_i
+
+ # GitLab Geo does not expect database UPDATE or INSERT statements to happen
+ # on GET requests.
+ # This is just a fail-safe in case notes_filter is sent via GET request in GitLab Geo.
+ if Gitlab::Database.read_only?
+ notes_filter_param || current_user&.notes_filter_for(issuable)
+ else
+ notes_filter = current_user&.set_notes_filter(notes_filter_param, issuable) || notes_filter_param
+
+ # We need to invalidate the cache for polling notes otherwise it will
+ # ignore the filter.
+ # The ideal would be to invalidate the cache for each user.
+ issuable.expire_note_etag_cache if notes_filter_updated?
+
+ notes_filter
+ end
+ end
+ end
+
+ def notes_filter_updated?
+ current_user&.user_preference&.previous_changes&.any?
+ end
+
def discussion_serializer
DiscussionSerializer.new(project: project, noteable: issuable, current_user: current_user, note_entity: ProjectNoteEntity)
end
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 2ef2ee76855..34a8c50fcbd 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
module IssuableCollections
extend ActiveSupport::Concern
+ include CookiesHelper
include SortingHelper
include Gitlab::IssuableMetadata
include Gitlab::Utils::StrongMemoize
@@ -47,9 +50,11 @@ module IssuableCollections
false
end
+ # rubocop: disable CodeReuse/ActiveRecord
def issuables_collection
finder.execute.preload(preload_for_collection)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def redirect_out_of_range(total_pages)
return false if total_pages.nil? || total_pages.zero?
@@ -76,42 +81,51 @@ module IssuableCollections
end
def issuable_finder_for(finder_class)
- finder_class.new(current_user, filter_params)
+ finder_class.new(current_user, finder_options)
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
- def filter_params
- set_sort_order_from_cookie
- set_default_state
+ def finder_options
+ params[:state] = default_state if params[:state].blank?
- # Skip irrelevant Rails routing params
- @filter_params = params.dup.except(:controller, :action, :namespace_id)
- @filter_params[:sort] ||= default_sort_order
+ options = {
+ scope: params[:scope],
+ state: params[:state],
+ sort: set_sort_order_from_cookie || default_sort_order
+ }
- @sort = @filter_params[:sort]
+ # Used by view to highlight active option
+ @sort = options[:sort]
if @project
- @filter_params[:project_id] = @project.id
+ options[:project_id] = @project.id
elsif @group
- @filter_params[:group_id] = @group.id
- @filter_params[:include_subgroups] = true
- @filter_params[:use_cte_for_search] = true
+ options[:group_id] = @group.id
+ options[:include_subgroups] = true
+ options[:use_cte_for_search] = true
end
- @filter_params.permit(finder_type.valid_params)
+ params.permit(finder_type.valid_params).merge(options)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
- def set_default_state
- params[:state] = 'opened' if params[:state].blank?
+ def default_state
+ 'opened'
end
def set_sort_order_from_cookie
- key = 'issuable_sort'
+ sort_param = params[:sort] if params[:sort].present?
+ # fallback to legacy cookie value for backward compatibility
+ sort_param ||= cookies['issuable_sort']
+ sort_param ||= cookies[remember_sorting_key]
+
+ sort_value = update_cookie_value(sort_param)
+ set_secure_cookie(remember_sorting_key, sort_value)
+ sort_value
+ end
- cookies[key] = params[:sort] if params[:sort].present?
- cookies[key] = update_cookie_value(cookies[key])
- params[:sort] = cookies[key]
+ def remember_sorting_key
+ @remember_sorting_key ||= "#{collection_type.downcase}_sort"
end
def default_sort_order
@@ -140,16 +154,14 @@ module IssuableCollections
end
def finder
- strong_memoize(:finder) do
- issuable_finder_for(finder_type)
- end
+ @finder ||= issuable_finder_for(finder_type)
end
def collection_type
- @collection_type ||= case finder
- when IssuesFinder
+ @collection_type ||= case finder_type.name
+ when 'IssuesFinder'
'Issue'
- when MergeRequestsFinder
+ when 'MergeRequestsFinder'
'MergeRequest'
end
end
diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb
index 9d58656773d..a75590457d6 100644
--- a/app/controllers/concerns/issues_action.rb
+++ b/app/controllers/concerns/issues_action.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module IssuesAction
extend ActiveSupport::Concern
include IssuableCollections
diff --git a/app/controllers/concerns/issues_calendar.rb b/app/controllers/concerns/issues_calendar.rb
index 671a204621d..1fdfde4c869 100644
--- a/app/controllers/concerns/issues_calendar.rb
+++ b/app/controllers/concerns/issues_calendar.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
module IssuesCalendar
extend ActiveSupport::Concern
# rubocop:disable Gitlab/ModuleWithInstanceVariables
+ # rubocop: disable CodeReuse/ActiveRecord
def render_issues_calendar(issuables)
@issues = issuables
.non_archived
@@ -20,5 +23,6 @@ module IssuesCalendar
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
diff --git a/app/controllers/concerns/labels_as_hash.rb b/app/controllers/concerns/labels_as_hash.rb
new file mode 100644
index 00000000000..1171aa9cf44
--- /dev/null
+++ b/app/controllers/concerns/labels_as_hash.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module LabelsAsHash
+ extend ActiveSupport::Concern
+
+ def labels_as_hash(target = nil, params = {})
+ available_labels = LabelsFinder.new(
+ current_user,
+ params
+ ).execute
+
+ label_hashes = available_labels.as_json(only: [:title, :color])
+
+ if target&.respond_to?(:labels)
+ already_set_labels = available_labels & target.labels
+ if already_set_labels.present?
+ titles = already_set_labels.map(&:title)
+ label_hashes.each do |hash|
+ if titles.include?(hash['title'])
+ hash[:set] = true
+ end
+ end
+ end
+ end
+
+ label_hashes
+ end
+end
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 4584ff782a3..9576eb14fdd 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This concern assumes:
# - a `#project` accessor
# - a `#user` accessor
diff --git a/app/controllers/concerns/members_presentation.rb b/app/controllers/concerns/members_presentation.rb
index 215e0bdf3cb..0a9d3d86245 100644
--- a/app/controllers/concerns/members_presentation.rb
+++ b/app/controllers/concerns/members_presentation.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MembersPresentation
extend ActiveSupport::Concern
@@ -11,9 +13,6 @@ module MembersPresentation
end
def preload_associations(members)
- ActiveRecord::Associations::Preloader.new.preload(members, :user)
- ActiveRecord::Associations::Preloader.new.preload(members, :source)
- ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :status)
- ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :u2f_registrations)
+ MembersPreloader.new(members).preload_all
end
end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 409e6d4c4d2..ca713192c9e 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MembershipActions
include MembersPresentation
extend ActiveSupport::Concern
@@ -57,6 +59,7 @@ module MembershipActions
redirect_to members_page_url
end
+ # rubocop: disable CodeReuse/ActiveRecord
def leave
member = membershipable.members_and_requesters.find_by!(user_id: current_user.id)
Members::DestroyService.new(current_user).execute(member)
@@ -77,6 +80,7 @@ module MembershipActions
format.json { render json: { notice: notice } }
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def resend_invite
member = membershipable.members.find(params[:id])
diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb
index b70db99b157..ed10f32512e 100644
--- a/app/controllers/concerns/merge_requests_action.rb
+++ b/app/controllers/concerns/merge_requests_action.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequestsAction
extend ActiveSupport::Concern
include IssuableCollections
@@ -17,7 +19,7 @@ module MergeRequestsAction
(MergeRequestsFinder if action_name == 'merge_requests')
end
- def filter_params
+ def finder_options
super.merge(non_archived: true)
end
end
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index d92cf8b4894..eccbe35577b 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MilestoneActions
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index 5127db3f5fb..777b147e2dd 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module NotesActions
include RendersNotes
include Gitlab::Utils::StrongMemoize
@@ -15,8 +17,16 @@ module NotesActions
notes_json = { notes: [], last_fetched_at: current_fetched_at }
- notes = notes_finder.execute
- .inc_relations_for_view
+ notes = notes_finder
+ .execute
+ .inc_relations_for_view
+
+ if notes_filter != UserPreference::NOTES_FILTERS[:only_comments]
+ notes =
+ ResourceEvents::MergeIntoNotesService
+ .new(noteable, current_user, last_fetched_at: current_fetched_at)
+ .execute(notes)
+ end
notes = prepare_notes_for_rendering(notes)
notes = notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }
@@ -40,12 +50,26 @@ module NotesActions
@note = Notes::CreateService.new(note_project, current_user, create_params).execute
- if @note.is_a?(Note)
- prepare_notes_for_rendering([@note], noteable)
- end
-
respond_to do |format|
- format.json { render json: note_json(@note) }
+ format.json do
+ json = {
+ commands_changes: @note.commands_changes
+ }
+
+ if @note.persisted? && return_discussion?
+ json[:valid] = true
+
+ discussion = @note.discussion
+ prepare_notes_for_rendering(discussion.notes)
+ json[:discussion] = discussion_serializer.represent(discussion, context: self)
+ else
+ prepare_notes_for_rendering([@note])
+
+ json.merge!(note_json(@note))
+ end
+
+ render json: json
+ end
format.html { redirect_back_or_default }
end
end
@@ -54,10 +78,7 @@ module NotesActions
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def update
@note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
-
- if @note.is_a?(Note)
- prepare_notes_for_rendering([@note])
- end
+ prepare_notes_for_rendering([@note])
respond_to do |format|
format.json { render json: note_json(@note) }
@@ -88,14 +109,17 @@ module NotesActions
end
def note_json(note)
- attrs = {
- commands_changes: note.commands_changes
- }
+ attrs = {}
if note.persisted?
attrs[:valid] = true
- if use_note_serializer?
+ if return_discussion?
+ discussion = note.discussion
+ prepare_notes_for_rendering(discussion.notes)
+
+ attrs[:discussion] = discussion_serializer.represent(discussion, context: self)
+ elsif use_note_serializer?
attrs.merge!(note_serializer.represent(note))
else
attrs.merge!(
@@ -207,6 +231,10 @@ module NotesActions
request.headers['X-Last-Fetched-At']
end
+ def notes_filter
+ current_user&.notes_filter_for(params[:target_type])
+ end
+
def notes_finder
@notes_finder ||= NotesFinder.new(project, current_user, finder_params)
end
@@ -215,6 +243,10 @@ module NotesActions
ProjectNoteSerializer.new(project: project, noteable: noteable, current_user: current_user)
end
+ def discussion_serializer
+ DiscussionSerializer.new(project: project, noteable: noteable, current_user: current_user, note_entity: ProjectNoteEntity)
+ end
+
def note_project
strong_memoize(:note_project) do
next nil unless project
@@ -234,6 +266,10 @@ module NotesActions
end
end
+ def return_discussion?
+ Gitlab::Utils.to_boolean(params[:return_discussion])
+ end
+
def use_note_serializer?
return false if params['html']
diff --git a/app/controllers/concerns/oauth_applications.rb b/app/controllers/concerns/oauth_applications.rb
index f0a68f23566..d97e22df472 100644
--- a/app/controllers/concerns/oauth_applications.rb
+++ b/app/controllers/concerns/oauth_applications.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module OauthApplications
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/params_backward_compatibility.rb b/app/controllers/concerns/params_backward_compatibility.rb
index b0e3d9c7b34..c972d6e3161 100644
--- a/app/controllers/concerns/params_backward_compatibility.rb
+++ b/app/controllers/concerns/params_backward_compatibility.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ParamsBackwardCompatibility
private
diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb
index 99123fcb3b0..c61b9fabe9e 100644
--- a/app/controllers/concerns/preview_markdown.rb
+++ b/app/controllers/concerns/preview_markdown.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module PreviewMarkdown
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/project_unauthorized.rb b/app/controllers/concerns/project_unauthorized.rb
new file mode 100644
index 00000000000..f59440dbc59
--- /dev/null
+++ b/app/controllers/concerns/project_unauthorized.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module ProjectUnauthorized
+ extend ActiveSupport::Concern
+
+ # EE would override this
+ def project_unauthorized_proc
+ # no-op
+ end
+end
diff --git a/app/controllers/concerns/renders_blob.rb b/app/controllers/concerns/renders_blob.rb
index ba7adcfea86..b8026c7a01d 100644
--- a/app/controllers/concerns/renders_blob.rb
+++ b/app/controllers/concerns/renders_blob.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RendersBlob
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/renders_commits.rb b/app/controllers/concerns/renders_commits.rb
index fb41dc1e8a8..f48e0586211 100644
--- a/app/controllers/concerns/renders_commits.rb
+++ b/app/controllers/concerns/renders_commits.rb
@@ -1,4 +1,26 @@
+# frozen_string_literal: true
+
module RendersCommits
+ def limited_commits(commits)
+ if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
+ [
+ commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE),
+ commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE
+ ]
+ else
+ [commits, 0]
+ end
+ end
+
+ # This is used as a helper method in a controller.
+ # rubocop: disable Gitlab/ModuleWithInstanceVariables
+ def set_commits_for_rendering(commits)
+ @total_commit_count = commits.size
+ limited, @hidden_commit_count = limited_commits(commits)
+ prepare_commits_for_rendering(limited)
+ end
+ # rubocop: enable Gitlab/ModuleWithInstanceVariables
+
def prepare_commits_for_rendering(commits)
Banzai::CommitRenderer.render(commits, @project, current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
diff --git a/app/controllers/concerns/renders_member_access.rb b/app/controllers/concerns/renders_member_access.rb
index d640378c24d..955ac1a1bc8 100644
--- a/app/controllers/concerns/renders_member_access.rb
+++ b/app/controllers/concerns/renders_member_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RendersMemberAccess
def prepare_groups_for_rendering(groups)
preload_max_member_access_for_collection(Group, groups)
@@ -13,6 +15,7 @@ module RendersMemberAccess
private
+ # rubocop: disable CodeReuse/ActiveRecord
def preload_max_member_access_for_collection(klass, collection)
return if !current_user || collection.blank?
@@ -20,4 +23,5 @@ module RendersMemberAccess
current_user.public_send(method_name, collection.ids) # rubocop:disable GitlabSecurity/PublicSend
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb
index cf04023080a..ce36da6b715 100644
--- a/app/controllers/concerns/renders_notes.rb
+++ b/app/controllers/concerns/renders_notes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RendersNotes
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def prepare_notes_for_rendering(notes, noteable = nil)
@@ -20,9 +22,11 @@ module RendersNotes
project.team.max_member_access_for_user_ids(user_ids)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def preload_noteable_for_regular_notes(notes)
ActiveRecord::Associations::Preloader.new.preload(notes.reject(&:for_commit?), :noteable)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def preload_first_time_contribution_for_authors(noteable, notes)
return unless noteable.is_a?(Issuable) && noteable.first_contribution?
@@ -30,7 +34,9 @@ module RendersNotes
notes.each {|n| n.specialize_for_first_contribution!(noteable)}
end
+ # rubocop: disable CodeReuse/ActiveRecord
def preload_author_status(notes)
ActiveRecord::Associations::Preloader.new.preload(notes, { author: :status })
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/concerns/repository_settings_redirect.rb b/app/controllers/concerns/repository_settings_redirect.rb
index f3db3cd563b..0f18735c29e 100644
--- a/app/controllers/concerns/repository_settings_redirect.rb
+++ b/app/controllers/concerns/repository_settings_redirect.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RepositorySettingsRedirect
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb
index 88d1b34bb06..426f224d26b 100644
--- a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb
+++ b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RequiresWhitelistedMonitoringClient
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/routable_actions.rb b/app/controllers/concerns/routable_actions.rb
index 0931bdf4c04..5624eb3aa45 100644
--- a/app/controllers/concerns/routable_actions.rb
+++ b/app/controllers/concerns/routable_actions.rb
@@ -1,23 +1,27 @@
+# frozen_string_literal: true
+
module RoutableActions
extend ActiveSupport::Concern
- def find_routable!(routable_klass, requested_full_path, extra_authorization_proc: nil)
+ def find_routable!(routable_klass, requested_full_path, extra_authorization_proc: nil, not_found_or_authorized_proc: nil)
routable = routable_klass.find_by_full_path(requested_full_path, follow_redirects: request.get?)
if routable_authorized?(routable, extra_authorization_proc)
ensure_canonical_path(routable, requested_full_path)
routable
else
- handle_not_found_or_authorized(routable)
+ if not_found_or_authorized_proc
+ not_found_or_authorized_proc.call(routable)
+ end
+
+ route_not_found unless performed?
+
nil
end
end
- # This is overridden in gitlab-ee.
- def handle_not_found_or_authorized(_routable)
- route_not_found
- end
-
def routable_authorized?(routable, extra_authorization_proc)
+ return false unless routable
+
action = :"read_#{routable.class.to_s.underscore}"
return false unless can?(current_user, action, routable)
diff --git a/app/controllers/concerns/send_file_upload.rb b/app/controllers/concerns/send_file_upload.rb
index 237c93daee8..515a9eede8e 100644
--- a/app/controllers/concerns/send_file_upload.rb
+++ b/app/controllers/concerns/send_file_upload.rb
@@ -1,7 +1,13 @@
+# frozen_string_literal: true
+
module SendFileUpload
- def send_upload(file_upload, send_params: {}, redirect_params: {}, attachment: nil, disposition: 'attachment')
+ def send_upload(file_upload, send_params: {}, redirect_params: {}, attachment: nil, proxy: false, disposition: 'attachment')
if attachment
- redirect_params[:query] = { "response-content-disposition" => "#{disposition};filename=#{attachment.inspect}" }
+ # Response-Content-Type will not override an existing Content-Type in
+ # Google Cloud Storage, so the metadata needs to be cleared on GCS for
+ # this to work. However, this override works with AWS.
+ redirect_params[:query] = { "response-content-disposition" => "#{disposition};filename=#{attachment.inspect}",
+ "response-content-type" => guess_content_type(attachment) }
# By default, Rails will send uploads with an extension of .js with a
# content-type of text/javascript, which will trigger Rails'
# cross-origin JavaScript protection.
@@ -11,11 +17,21 @@ module SendFileUpload
if file_upload.file_storage?
send_file file_upload.path, send_params
- elsif file_upload.class.proxy_download_enabled?
+ elsif file_upload.class.proxy_download_enabled? || proxy
headers.store(*Gitlab::Workhorse.send_url(file_upload.url(**redirect_params)))
head :ok
else
redirect_to file_upload.url(**redirect_params)
end
end
+
+ def guess_content_type(filename)
+ types = MIME::Types.type_for(filename)
+
+ if types.present?
+ types.first.content_type
+ else
+ "application/octet-stream"
+ end
+ end
end
diff --git a/app/controllers/concerns/sends_blob.rb b/app/controllers/concerns/sends_blob.rb
new file mode 100644
index 00000000000..8ecdaced9f5
--- /dev/null
+++ b/app/controllers/concerns/sends_blob.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module SendsBlob
+ extend ActiveSupport::Concern
+
+ included do
+ include BlobHelper
+ include SendFileUpload
+ end
+
+ def send_blob(repository, blob, params = {})
+ if blob
+ headers['X-Content-Type-Options'] = 'nosniff'
+
+ return if cached_blob?(blob)
+
+ if blob.stored_externally?
+ send_lfs_object(blob)
+ else
+ send_git_blob(repository, blob, params)
+ end
+ else
+ render_404
+ end
+ end
+
+ private
+
+ def cached_blob?(blob)
+ stale = stale?(etag: blob.id) # The #stale? method sets cache headers.
+
+ # Because we are opinionated we set the cache headers ourselves.
+ response.cache_control[:public] = project.public?
+
+ response.cache_control[:max_age] =
+ if @ref && @commit && @ref == @commit.id # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ # This is a link to a commit by its commit SHA. That means that the blob
+ # is immutable. The only reason to invalidate the cache is if the commit
+ # was deleted or if the user lost access to the repository.
+ Blob::CACHE_TIME_IMMUTABLE
+ else
+ # A branch or tag points at this blob. That means that the expected blob
+ # value may change over time.
+ Blob::CACHE_TIME
+ end
+
+ response.etag = blob.id
+ !stale
+ end
+
+ def send_lfs_object(blob)
+ lfs_object = find_lfs_object(blob)
+
+ if lfs_object && lfs_object.project_allowed_access?(project)
+ send_upload(lfs_object.file, attachment: blob.name)
+ else
+ render_404
+ end
+ end
+
+ def find_lfs_object(blob)
+ lfs_object = LfsObject.find_by_oid(blob.lfs_oid)
+ if lfs_object && lfs_object.file.exists?
+ lfs_object
+ else
+ nil
+ end
+ end
+end
diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb
index c1acb50b76c..8bd93a349ef 100644
--- a/app/controllers/concerns/service_params.rb
+++ b/app/controllers/concerns/service_params.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ServiceParams
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index 120614739aa..8c22490700c 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SnippetsActions
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb
index 922aa58a00f..c3a1b12af84 100644
--- a/app/controllers/concerns/spammable_actions.rb
+++ b/app/controllers/concerns/spammable_actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SpammableActions
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/todos_actions.rb b/app/controllers/concerns/todos_actions.rb
index c0acdb3498d..78b65f7961b 100644
--- a/app/controllers/concerns/todos_actions.rb
+++ b/app/controllers/concerns/todos_actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TodosActions
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/toggle_award_emoji.rb b/app/controllers/concerns/toggle_award_emoji.rb
index ba5b7d33f87..97b343f8b1a 100644
--- a/app/controllers/concerns/toggle_award_emoji.rb
+++ b/app/controllers/concerns/toggle_award_emoji.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ToggleAwardEmoji
extend ActiveSupport::Concern
@@ -5,7 +7,7 @@ module ToggleAwardEmoji
authenticate_user!
name = params.require(:name)
- if awardable.user_can_award?(current_user, name)
+ if awardable.user_can_award?(current_user)
awardable.toggle_award_emoji(name, current_user)
todoable = to_todoable(awardable)
diff --git a/app/controllers/concerns/toggle_subscription_action.rb b/app/controllers/concerns/toggle_subscription_action.rb
index 776583579e8..e613bfaeef2 100644
--- a/app/controllers/concerns/toggle_subscription_action.rb
+++ b/app/controllers/concerns/toggle_subscription_action.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ToggleSubscriptionAction
extend ActiveSupport::Concern
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index 434459a225a..7a1c7abfb8f 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module UploadsActions
extend ActiveSupport::Concern
@@ -53,6 +55,8 @@ module UploadsActions
maximum_size: Gitlab::CurrentSettings.max_attachment_size.megabytes.to_i)
render json: authorized
+ rescue SocketError
+ render json: "Error uploading file", status: :internal_server_error
end
private
@@ -87,6 +91,7 @@ module UploadsActions
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def build_uploader_from_upload
return unless uploader = build_uploader
@@ -94,6 +99,7 @@ module UploadsActions
upload = Upload.find_by(uploader: uploader_class.to_s, path: upload_paths)
upload&.build_uploader
end
+ # rubocop: enable CodeReuse/ActiveRecord
def build_uploader_from_params
return unless uploader = build_uploader
diff --git a/app/controllers/concerns/with_performance_bar.rb b/app/controllers/concerns/with_performance_bar.rb
index 6a8b1a4de7b..77c3d476ac6 100644
--- a/app/controllers/concerns/with_performance_bar.rb
+++ b/app/controllers/concerns/with_performance_bar.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WithPerformanceBar
extend ActiveSupport::Concern
@@ -8,11 +10,7 @@ module WithPerformanceBar
def peek_enabled?
return false unless Gitlab::PerformanceBar.enabled?(current_user)
- if RequestStore.active?
- RequestStore.fetch(:peek_enabled) { cookie_or_default_value }
- else
- cookie_or_default_value
- end
+ Gitlab::SafeRequestStore.fetch(:peek_enabled) { cookie_or_default_value }
end
private
diff --git a/app/controllers/concerns/workhorse_request.rb b/app/controllers/concerns/workhorse_request.rb
index 43c0f1b173c..028f10e866a 100644
--- a/app/controllers/concerns/workhorse_request.rb
+++ b/app/controllers/concerns/workhorse_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WorkhorseRequest
extend ActiveSupport::Concern
diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb
index 7bc46a6ccc0..2c4aab67448 100644
--- a/app/controllers/confirmations_controller.rb
+++ b/app/controllers/confirmations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ConfirmationsController < Devise::ConfirmationsController
include AcceptsPendingInvitations
@@ -20,7 +22,7 @@ class ConfirmationsController < Devise::ConfirmationsController
after_sign_in(resource)
else
Gitlab::AppLogger.info("Email Confirmed: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip}")
- flash[:notice] += " Please sign in."
+ flash[:notice] = flash[:notice] + " Please sign in."
new_session_path(:user, anchor: 'login-pane')
end
end
diff --git a/app/controllers/dashboard/application_controller.rb b/app/controllers/dashboard/application_controller.rb
index 9fb5c525425..cee0753a021 100644
--- a/app/controllers/dashboard/application_controller.rb
+++ b/app/controllers/dashboard/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::ApplicationController < ApplicationController
include ControllerWithCrossProjectAccessCheck
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index 79f563bef86..f82cde8e10a 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::GroupsController < Dashboard::ApplicationController
include GroupTree
diff --git a/app/controllers/dashboard/labels_controller.rb b/app/controllers/dashboard/labels_controller.rb
index 9dcb3a0eb6d..89d87c2d5c8 100644
--- a/app/controllers/dashboard/labels_controller.rb
+++ b/app/controllers/dashboard/labels_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::LabelsController < Dashboard::ApplicationController
def index
respond_to do |format|
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 0469e7e1e1f..3802aa5f40f 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -1,13 +1,16 @@
+# frozen_string_literal: true
+
class Dashboard::MilestonesController < Dashboard::ApplicationController
include MilestoneActions
before_action :projects
+ before_action :groups, only: :index
before_action :milestone, only: [:show, :merge_requests, :participants, :labels]
def index
respond_to do |format|
format.html do
- @milestone_states = GlobalMilestone.states_count(@projects)
+ @milestone_states = Milestone.states_count(@projects.select(:id), @groups.select(:id))
@milestones = Kaminari.paginate_array(milestones).page(params[:page])
end
format.json do
@@ -22,7 +25,7 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
private
def group_milestones
- groups = GroupsFinder.new(current_user, all_available: true).execute
+ groups = GroupsFinder.new(current_user, all_available: false).execute
DashboardGroupMilestone.build_collection(groups)
end
@@ -40,4 +43,8 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
@milestone = DashboardMilestone.build(@projects, params[:title])
render_404 unless @milestone
end
+
+ def groups
+ @groups ||= GroupsFinder.new(current_user, state_all: true).execute
+ end
end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index ccfcbbdc776..e9686ed8d06 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::ProjectsController < Dashboard::ApplicationController
include ParamsBackwardCompatibility
include RendersMemberAccess
@@ -23,6 +25,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def starred
@projects = load_projects(params.merge(starred: true))
.includes(:forked_from_project, :tags)
@@ -38,6 +41,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -46,6 +50,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@sort = params[:sort]
end
+ # rubocop: disable CodeReuse/ActiveRecord
def load_projects(finder_params)
projects = ProjectsFinder
.new(params: finder_params, current_user: current_user)
@@ -55,6 +60,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
prepare_projects_for_rendering(projects)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def load_events
projects = load_projects(params.merge(non_public: true))
diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb
index 0ba97e4fd59..161c22046f9 100644
--- a/app/controllers/dashboard/snippets_controller.rb
+++ b/app/controllers/dashboard/snippets_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::SnippetsController < Dashboard::ApplicationController
skip_cross_project_access_check :index
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index bd7111e28bc..b82caf30a91 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
@@ -73,6 +75,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
params.permit(:action_id, :author_id, :project_id, :type, :sort, :state, :group_id)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def redirect_out_of_range(todos)
total_pages =
if todo_params.except(:sort, :page).empty?
@@ -91,4 +94,5 @@ class Dashboard::TodosController < Dashboard::ApplicationController
out_of_range
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index ff133001b84..4ce9be44403 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -1,14 +1,9 @@
+# frozen_string_literal: true
+
class DashboardController < Dashboard::ApplicationController
include IssuesAction
include MergeRequestsAction
- FILTER_PARAMS = [
- :author_id,
- :assignee_id,
- :milestone_title,
- :label_name
- ].freeze
-
before_action :event_filter, only: :activity
before_action :projects, only: [:issues, :merge_requests]
before_action :set_show_full_reference, only: [:issues, :merge_requests]
@@ -38,7 +33,7 @@ class DashboardController < Dashboard::ApplicationController
end
@events = EventCollection
- .new(projects, offset: params[:offset].to_i, filter: @event_filter)
+ .new(projects, offset: params[:offset].to_i, filter: event_filter)
.to_a
Events::RenderService.new(current_user).execute(@events)
@@ -49,10 +44,13 @@ class DashboardController < Dashboard::ApplicationController
end
def check_filters_presence!
- @no_filters_set = FILTER_PARAMS.none? { |k| params.key?(k) }
+ @no_filters_set = finder_type.scalar_params.none? { |k| params.key?(k) }
return unless @no_filters_set
+ # Call to set selected `state` and `sort` options in view
+ finder_options
+
respond_to do |format|
format.html { render }
format.atom { head :bad_request }
diff --git a/app/controllers/explore/application_controller.rb b/app/controllers/explore/application_controller.rb
index baf54520b9c..8eee3742d89 100644
--- a/app/controllers/explore/application_controller.rb
+++ b/app/controllers/explore/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Explore::ApplicationController < ApplicationController
skip_before_action :authenticate_user!
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index fa0a0f68fbc..67db797b80a 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Explore::GroupsController < Explore::ApplicationController
include GroupTree
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index c7273606a85..7ecbc32cf4e 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Explore::ProjectsController < Explore::ApplicationController
include ParamsBackwardCompatibility
include RendersMemberAccess
@@ -34,6 +36,7 @@ class Explore::ProjectsController < Explore::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def starred
@projects = load_projects.reorder('star_count DESC')
@@ -46,9 +49,11 @@ class Explore::ProjectsController < Explore::ApplicationController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ # rubocop: disable CodeReuse/ActiveRecord
def load_projects
projects = ProjectsFinder.new(current_user: current_user, params: params)
.execute
@@ -58,4 +63,5 @@ class Explore::ProjectsController < Explore::ApplicationController
prepare_projects_for_rendering(projects)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb
index d3f0e033068..76ed142c939 100644
--- a/app/controllers/explore/snippets_controller.rb
+++ b/app/controllers/explore/snippets_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Explore::SnippetsController < Explore::ApplicationController
def index
@snippets = SnippetsFinder.new(current_user).execute
diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb
index 5551057ff55..dd9f5af61b3 100644
--- a/app/controllers/google_api/authorizations_controller.rb
+++ b/app/controllers/google_api/authorizations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GoogleApi
class AuthorizationsController < ApplicationController
def callback
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index 0a1cf169aca..a1ec144410b 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GraphqlController < ApplicationController
# Unauthenticated users have access to the API for public data
skip_before_action :authenticate_user!
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index 62213561898..5f92333c2c3 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::ApplicationController < ApplicationController
include RoutableActions
include ControllerWithCrossProjectAccessCheck
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 35a61b359c8..8e4dc2bb6e9 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::AvatarsController < Groups::ApplicationController
before_action :authorize_admin_group!
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index e892d1f8dbf..cdc6f53df8e 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
class Groups::BoardsController < Groups::ApplicationController
include BoardsResponses
before_action :assign_endpoint_vars
before_action :boards, only: :index
+ before_action :redirect_to_recent_board, only: :index
def index
respond_with_boards
@@ -11,6 +14,9 @@ class Groups::BoardsController < Groups::ApplicationController
def show
@board = boards.find(params[:id])
+ # add/update the board in the recent visited table
+ Boards::Visits::CreateService.new(@board.group, current_user).execute(@board) if request.format.html?
+
respond_with_board
end
@@ -29,4 +35,18 @@ class Groups::BoardsController < Groups::ApplicationController
def serialize_as_json(resource)
resource.as_json(only: [:id])
end
+
+ def includes_board?(board_id)
+ boards.any? { |board| board.id == board_id }
+ end
+
+ def redirect_to_recent_board
+ return if request.format.json?
+
+ recently_visited = Boards::Visits::LatestService.new(group, current_user).execute
+
+ if recently_visited && includes_board?(recently_visited.board_id)
+ redirect_to(group_board_path(id: recently_visited.board_id), status: :found)
+ end
+ end
end
diff --git a/app/controllers/groups/children_controller.rb b/app/controllers/groups/children_controller.rb
index 0e8125d6113..d549f793ad7 100644
--- a/app/controllers/groups/children_controller.rb
+++ b/app/controllers/groups/children_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class ChildrenController < Groups::ApplicationController
before_action :group
diff --git a/app/controllers/groups/clusters/applications_controller.rb b/app/controllers/groups/clusters/applications_controller.rb
new file mode 100644
index 00000000000..8dd8a01cf40
--- /dev/null
+++ b/app/controllers/groups/clusters/applications_controller.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class Groups::Clusters::ApplicationsController < Clusters::ApplicationsController
+ include ControllerWithCrossProjectAccessCheck
+
+ prepend_before_action :group
+ requires_cross_project_access
+
+ private
+
+ def clusterable
+ @clusterable ||= ClusterablePresenter.fabricate(group, current_user: current_user)
+ end
+
+ def group
+ @group ||= find_routable!(Group, params[:group_id] || params[:id])
+ end
+end
diff --git a/app/controllers/groups/clusters_controller.rb b/app/controllers/groups/clusters_controller.rb
new file mode 100644
index 00000000000..50c44b7a58b
--- /dev/null
+++ b/app/controllers/groups/clusters_controller.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class Groups::ClustersController < Clusters::ClustersController
+ include ControllerWithCrossProjectAccessCheck
+
+ prepend_before_action :check_group_clusters_feature_flag!
+ prepend_before_action :group
+ requires_cross_project_access
+
+ layout 'group'
+
+ private
+
+ def clusterable
+ @clusterable ||= ClusterablePresenter.fabricate(group, current_user: current_user)
+ end
+
+ def group
+ @group ||= find_routable!(Group, params[:group_id] || params[:id])
+ end
+
+ def check_group_clusters_feature_flag!
+ render_404 unless Feature.enabled?(:group_clusters)
+ end
+end
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 7dc51f4c357..0bc082246a1 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::GroupMembersController < Groups::ApplicationController
include MembershipActions
include MembersPresentation
diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb
index 863f50e8e66..26768c628ca 100644
--- a/app/controllers/groups/labels_controller.rb
+++ b/app/controllers/groups/labels_controller.rb
@@ -1,8 +1,9 @@
+# frozen_string_literal: true
+
class Groups::LabelsController < Groups::ApplicationController
include ToggleSubscriptionAction
before_action :label, only: [:edit, :update, :destroy]
- before_action :available_labels, only: [:index]
before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy]
before_action :save_previous_label_path, only: [:edit]
@@ -11,10 +12,11 @@ class Groups::LabelsController < Groups::ApplicationController
def index
respond_to do |format|
format.html do
- @labels = @group.labels.page(params[:page])
+ @labels = GroupLabelsFinder
+ .new(current_user, @group, params.merge(sort: sort)).execute
end
format.json do
- render json: LabelSerializer.new.represent_appearance(@available_labels)
+ render json: LabelSerializer.new.represent_appearance(available_labels)
end
end
end
@@ -113,7 +115,11 @@ class Groups::LabelsController < Groups::ApplicationController
group_id: @group.id,
only_group_labels: params[:only_group_labels],
include_ancestor_groups: params[:include_ancestor_groups],
- include_descendant_groups: params[:include_descendant_groups]
- ).execute
+ include_descendant_groups: params[:include_descendant_groups],
+ search: params[:search]).execute
+ end
+
+ def sort
+ @sort ||= params[:sort] || 'name_asc'
end
end
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 9bd51de7e97..b42116b0f36 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -1,14 +1,16 @@
+# frozen_string_literal: true
+
class Groups::MilestonesController < Groups::ApplicationController
include MilestoneActions
before_action :group_projects
- before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels]
- before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update]
+ before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy]
+ before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy]
def index
respond_to do |format|
format.html do
- @milestone_states = GlobalMilestone.states_count(group_projects, group)
+ @milestone_states = Milestone.states_count(group_projects, [group])
@milestones = Kaminari.paginate_array(milestones).page(params[:page])
end
format.json do
@@ -56,10 +58,21 @@ class Groups::MilestonesController < Groups::ApplicationController
redirect_to milestone_path
end
+ def destroy
+ return render_404 if @milestone.legacy_group_milestone?
+
+ Milestones::DestroyService.new(group, current_user).execute(@milestone)
+
+ respond_to do |format|
+ format.html { redirect_to group_milestones_path(group), status: :see_other }
+ format.js { head :ok }
+ end
+ end
+
private
def authorize_admin_milestones!
- return render_404 unless can?(current_user, :admin_milestones, group)
+ return render_404 unless can?(current_user, :admin_milestone, group)
end
def milestone_params
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index 1036b4e6ed3..dd8fbf7a029 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::RunnersController < Groups::ApplicationController
# Proper policies should be implemented per
# https://gitlab.com/gitlab-org/gitlab-ce/issues/45894
diff --git a/app/controllers/groups/settings/badges_controller.rb b/app/controllers/groups/settings/badges_controller.rb
deleted file mode 100644
index ccbd0a3bc02..00000000000
--- a/app/controllers/groups/settings/badges_controller.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module Groups
- module Settings
- class BadgesController < Groups::ApplicationController
- include API::Helpers::RelatedResourcesHelpers
-
- before_action :authorize_admin_group!
-
- def index
- @badge_api_endpoint = expose_url(api_v4_groups_badges_path(id: @group.id))
- end
- end
- end
-end
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index 4bf6a2a3ad1..c1dcc463de7 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
module Settings
class CiCdController < Groups::ApplicationController
@@ -5,12 +7,19 @@ module Groups
before_action :authorize_admin_pipeline!
def show
- define_secret_variables
+ define_ci_variables
+ end
+
+ def reset_registration_token
+ @group.reset_runners_token!
+
+ flash[:notice] = 'New runners registration token has been generated!'
+ redirect_to group_settings_ci_cd_path
end
private
- def define_secret_variables
+ def define_ci_variables
@variable = Ci::GroupVariable.new(group: group)
.present(current_user: current_user)
@variables = group.variables.order_key_asc
diff --git a/app/controllers/groups/shared_projects_controller.rb b/app/controllers/groups/shared_projects_controller.rb
index 7dec1f5f402..30b7bfc70ae 100644
--- a/app/controllers/groups/shared_projects_controller.rb
+++ b/app/controllers/groups/shared_projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class SharedProjectsController < Groups::ApplicationController
respond_to :json
diff --git a/app/controllers/groups/uploads_controller.rb b/app/controllers/groups/uploads_controller.rb
index 74760194a1f..7e5cdae0ce3 100644
--- a/app/controllers/groups/uploads_controller.rb
+++ b/app/controllers/groups/uploads_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::UploadsController < Groups::ApplicationController
include UploadsActions
include WorkhorseRequest
diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb
index 4d8a20de017..4f641de0357 100644
--- a/app/controllers/groups/variables_controller.rb
+++ b/app/controllers/groups/variables_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class VariablesController < Groups::ApplicationController
before_action :authorize_admin_build!
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 83169636ccf..062c8c4e9e1 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
class GroupsController < Groups::ApplicationController
+ include API::Helpers::RelatedResourcesHelpers
include IssuesAction
include MergeRequestsAction
include ParamsBackwardCompatibility
@@ -16,7 +19,7 @@ class GroupsController < Groups::ApplicationController
before_action :group_projects, only: [:projects, :activity, :issues, :merge_requests]
before_action :event_filter, only: [:activity]
- before_action :user_actions, only: [:show, :subgroups]
+ before_action :user_actions, only: [:show]
skip_cross_project_access_check :index, :new, :create, :edit, :update,
:destroy, :projects
@@ -52,11 +55,7 @@ class GroupsController < Groups::ApplicationController
def show
respond_to do |format|
- format.html do
- @has_children = GroupDescendantsFinder.new(current_user: current_user,
- parent_group: @group,
- params: params).has_children?
- end
+ format.html
format.atom do
load_events
@@ -77,6 +76,7 @@ class GroupsController < Groups::ApplicationController
end
def edit
+ @badge_api_endpoint = expose_url(api_v4_groups_badges_path(id: @group.id))
end
def projects
@@ -99,6 +99,7 @@ class GroupsController < Groups::ApplicationController
redirect_to root_path, status: 302, alert: "Group '#{@group.name}' was scheduled for deletion."
end
+ # rubocop: disable CodeReuse/ActiveRecord
def transfer
parent_group = Group.find_by(id: params[:new_parent_group_id])
service = ::Groups::TransferService.new(@group, current_user)
@@ -111,9 +112,11 @@ class GroupsController < Groups::ApplicationController
render :edit
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
protected
+ # rubocop: disable CodeReuse/ActiveRecord
def authorize_create_group!
allowed = if params[:parent_id].present?
parent = Group.find_by(id: params[:parent_id])
@@ -124,6 +127,7 @@ class GroupsController < Groups::ApplicationController
render_404 unless allowed
end
+ # rubocop: enable CodeReuse/ActiveRecord
def determine_layout
if [:new, :create].include?(action_name.to_sym)
@@ -158,6 +162,7 @@ class GroupsController < Groups::ApplicationController
]
end
+ # rubocop: disable CodeReuse/ActiveRecord
def load_events
params[:sort] ||= 'latest_activity_desc'
@@ -177,6 +182,7 @@ class GroupsController < Groups::ApplicationController
.new(current_user)
.execute(@events, atom_request: request.format.atom?)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def user_actions
if current_user
diff --git a/app/controllers/health_check_controller.rb b/app/controllers/health_check_controller.rb
index c3d18991fd4..a2abed7ba4e 100644
--- a/app/controllers/health_check_controller.rb
+++ b/app/controllers/health_check_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class HealthCheckController < HealthCheck::HealthCheckController
include RequiresWhitelistedMonitoringClient
end
diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb
index 3fedd5bfb29..dc9a52f8da5 100644
--- a/app/controllers/health_controller.rb
+++ b/app/controllers/health_controller.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
class HealthController < ActionController::Base
- protect_from_forgery with: :exception, except: :storage_check, prepend: true
+ protect_from_forgery with: :exception, prepend: true
include RequiresWhitelistedMonitoringClient
CHECKS = [
@@ -23,15 +25,6 @@ class HealthController < ActionController::Base
render_check_results(results)
end
- def storage_check
- results = Gitlab::Git::Storage::Checker.check_all
-
- render json: {
- check_interval: Gitlab::CurrentSettings.current_application_settings.circuitbreaker_check_interval,
- results: results
- }
- end
-
private
def render_check_results(results)
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index a394521698c..e5a1fc9d6ff 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class HelpController < ApplicationController
skip_before_action :authenticate_user!
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
index 1ff25a45398..eeeebe430a7 100644
--- a/app/controllers/ide_controller.rb
+++ b/app/controllers/ide_controller.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
class IdeController < ApplicationController
- layout 'nav_only'
+ layout 'fullscreen'
def index
end
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index 5766c6924cd..042b6b1264f 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -1,16 +1,22 @@
+# frozen_string_literal: true
+
class Import::BaseController < ApplicationController
private
+ # rubocop: disable CodeReuse/ActiveRecord
def find_already_added_projects(import_type)
current_user.created_projects.where(import_type: import_type).includes(:import_state)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def find_jobs(import_type)
current_user.created_projects
.includes(:import_state)
.where(import_type: import_type)
.to_json(only: [:id], methods: [:import_status])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_or_create_namespace(names, owner)
names = params[:target_namespace].presence || names
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
index fa31933e778..1b30b4dda36 100644
--- a/app/controllers/import/bitbucket_controller.rb
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::BitbucketController < Import::BaseController
before_action :verify_bitbucket_import_enabled
before_action :bitbucket_auth, except: :callback
@@ -16,6 +18,7 @@ class Import::BitbucketController < Import::BaseController
redirect_to status_import_bitbucket_url
end
+ # rubocop: disable CodeReuse/ActiveRecord
def status
bitbucket_client = Bitbucket::Client.new(credentials)
repos = bitbucket_client.repos
@@ -27,6 +30,7 @@ class Import::BitbucketController < Import::BaseController
@repos.to_a.reject! { |repo| already_added_projects_names.include?(repo.full_name) }
end
+ # rubocop: enable CodeReuse/ActiveRecord
def jobs
render json: find_jobs('bitbucket')
diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb
index 798daeca6c9..575c40d5f6f 100644
--- a/app/controllers/import/bitbucket_server_controller.rb
+++ b/app/controllers/import/bitbucket_server_controller.rb
@@ -52,20 +52,22 @@ class Import::BitbucketServerController < Import::BaseController
redirect_to status_import_bitbucket_server_path
end
+ # rubocop: disable CodeReuse/ActiveRecord
def status
- repos = bitbucket_client.repos
+ @collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page)
+ @repos, @incompatible_repos = @collection.partition { |repo| repo.valid? }
- @repos, @incompatible_repos = repos.partition { |repo| repo.valid? }
-
- @already_added_projects = find_already_added_projects('bitbucket_server')
+ # Use the import URL to filter beyond what BaseService#find_already_added_projects
+ @already_added_projects = filter_added_projects('bitbucket_server', @repos.map(&:browse_url))
already_added_projects_names = @already_added_projects.pluck(:import_source)
- @repos.to_a.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
+ @repos.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
rescue BitbucketServer::Connection::ConnectionError, BitbucketServer::Client::ServerError => e
flash[:alert] = "Unable to connect to server: #{e}"
clear_session_data
redirect_to new_import_bitbucket_server_path
end
+ # rubocop: enable CodeReuse/ActiveRecord
def jobs
render json: find_jobs('bitbucket_server')
@@ -73,6 +75,12 @@ class Import::BitbucketServerController < Import::BaseController
private
+ # rubocop: disable CodeReuse/ActiveRecord
+ def filter_added_projects(import_type, import_sources)
+ current_user.created_projects.where(import_type: import_type, import_source: import_sources).includes(:import_state)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def bitbucket_client
@bitbucket_client ||= BitbucketServer::Client.new(credentials)
end
@@ -128,4 +136,12 @@ class Import::BitbucketServerController < Import::BaseController
password: session[personal_access_token_key]
}
end
+
+ def page_offset
+ [0, params[:page].to_i].max
+ end
+
+ def limit_per_page
+ BitbucketServer::Paginator::PAGE_LENGTH
+ end
end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index 2d665e05ac3..5a439e6de78 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::FogbugzController < Import::BaseController
before_action :verify_fogbugz_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map]
@@ -39,6 +41,7 @@ class Import::FogbugzController < Import::BaseController
redirect_to status_import_fogbugz_path
end
+ # rubocop: disable CodeReuse/ActiveRecord
def status
unless client.valid?
return redirect_to new_import_fogbugz_path
@@ -51,6 +54,7 @@ class Import::FogbugzController < Import::BaseController
@repos.reject! { |repo| already_added_projects_names.include? repo.name }
end
+ # rubocop: enable CodeReuse/ActiveRecord
def jobs
render json: find_jobs('fogbugz')
diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb
index fbd851c64a7..f067ef625aa 100644
--- a/app/controllers/import/gitea_controller.rb
+++ b/app/controllers/import/gitea_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GiteaController < Import::GithubController
def new
if session[access_token_key].present? && session[host_key].present?
@@ -21,7 +23,7 @@ class Import::GiteaController < Import::GithubController
:"#{provider}_host_url"
end
- # Overriden methods
+ # Overridden methods
def provider
:gitea
end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index c9870332c0f..58565aaf8c9 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GithubController < Import::BaseController
before_action :verify_import_enabled
before_action :provider_auth, only: [:status, :jobs, :create]
@@ -18,10 +20,11 @@ class Import::GithubController < Import::BaseController
end
def personal_access_token
- session[access_token_key] = params[:personal_access_token]
+ session[access_token_key] = params[:personal_access_token]&.strip
redirect_to status_import_url
end
+ # rubocop: disable CodeReuse/ActiveRecord
def status
@repos = client.repos
@already_added_projects = find_already_added_projects(provider)
@@ -29,6 +32,7 @@ class Import::GithubController < Import::BaseController
@repos.reject! { |repo| already_added_projects_names.include? repo.full_name }
end
+ # rubocop: enable CodeReuse/ActiveRecord
def jobs
render json: find_jobs(provider)
@@ -99,14 +103,16 @@ class Import::GithubController < Import::BaseController
{ github_access_token: session[access_token_key] }
end
- # The following methods are overriden in subclasses
+ # The following methods are overridden in subclasses
def provider
:github
end
+ # rubocop: disable CodeReuse/ActiveRecord
def logged_in_with_provider?
current_user.identities.exists?(provider: provider)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def provider_auth
if session[access_token_key].blank?
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
index 53f70446d95..498de0b07b8 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GitlabController < Import::BaseController
MAX_PROJECT_PAGES = 15
PER_PAGE_PROJECTS = 100
@@ -12,6 +14,7 @@ class Import::GitlabController < Import::BaseController
redirect_to status_import_gitlab_url
end
+ # rubocop: disable CodeReuse/ActiveRecord
def status
@repos = client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
@@ -20,6 +23,7 @@ class Import::GitlabController < Import::BaseController
@repos = @repos.to_a.reject { |repo| already_added_projects_names.include? repo["path_with_namespace"] }
end
+ # rubocop: enable CodeReuse/ActiveRecord
def jobs
render json: find_jobs('gitlab')
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index f22df992fe9..354fba5d204 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GitlabProjectsController < Import::BaseController
before_action :whitelist_query_limiting, only: [:create]
before_action :verify_gitlab_project_import_enabled
@@ -11,7 +13,7 @@ class Import::GitlabProjectsController < Import::BaseController
def create
unless file_is_valid?
- return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
+ return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive (ending in .gz)." })
end
@project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute
@@ -29,7 +31,11 @@ class Import::GitlabProjectsController < Import::BaseController
private
def file_is_valid?
- project_params[:file] && project_params[:file].respond_to?(:read)
+ return false unless project_params[:file] && project_params[:file].respond_to?(:read)
+
+ filename = project_params[:file].original_filename
+
+ ImportExportUploader::EXTENSION_WHITELIST.include?(File.extname(filename).delete('.'))
end
def verify_gitlab_project_import_enabled
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
index 3bce27e810a..331f06c3dd6 100644
--- a/app/controllers/import/google_code_controller.rb
+++ b/app/controllers/import/google_code_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GoogleCodeController < Import::BaseController
before_action :verify_google_code_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map]
@@ -65,6 +67,7 @@ class Import::GoogleCodeController < Import::BaseController
redirect_to status_import_google_code_path
end
+ # rubocop: disable CodeReuse/ActiveRecord
def status
unless client.valid?
return redirect_to new_import_google_code_path
@@ -78,6 +81,7 @@ class Import::GoogleCodeController < Import::BaseController
@repos.reject! { |repo| already_added_projects_names.include? repo.name }
end
+ # rubocop: enable CodeReuse/ActiveRecord
def jobs
render json: find_jobs('google_code')
diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb
index e5a719fa0df..320cd45b925 100644
--- a/app/controllers/import/manifest_controller.rb
+++ b/app/controllers/import/manifest_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::ManifestController < Import::BaseController
before_action :whitelist_query_limiting, only: [:create]
before_action :verify_import_enabled
@@ -6,6 +8,7 @@ class Import::ManifestController < Import::BaseController
def new
end
+ # rubocop: disable CodeReuse/ActiveRecord
def status
@already_added_projects = find_already_added_projects
already_added_import_urls = @already_added_projects.pluck(:import_url)
@@ -14,6 +17,7 @@ class Import::ManifestController < Import::BaseController
already_added_import_urls.include?(repository[:url])
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def upload
group = Group.find(params[:group_id])
@@ -64,9 +68,11 @@ class Import::ManifestController < Import::BaseController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def group
@group ||= Group.find_by(id: session[:manifest_import_group_id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def repositories
@repositories ||= session[:manifest_import_repositories]
@@ -76,12 +82,14 @@ class Import::ManifestController < Import::BaseController
find_already_added_projects.to_json(only: [:id], methods: [:import_status])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_already_added_projects
group.all_projects
.where(import_type: 'manifest')
.where(creator_id: current_user)
.includes(:import_state)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def verify_import_enabled
render_404 unless manifest_import_enabled?
diff --git a/app/controllers/instance_statistics/cohorts_controller.rb b/app/controllers/instance_statistics/cohorts_controller.rb
index 7eba0a5ecdd..4b4e39db2e1 100644
--- a/app/controllers/instance_statistics/cohorts_controller.rb
+++ b/app/controllers/instance_statistics/cohorts_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class InstanceStatistics::CohortsController < InstanceStatistics::ApplicationController
+ before_action :authenticate_usage_ping_enabled_or_admin!
+
def index
if Gitlab::CurrentSettings.usage_ping_enabled
cohorts_results = Rails.cache.fetch('cohorts', expires_in: 1.day) do
@@ -10,4 +12,8 @@ class InstanceStatistics::CohortsController < InstanceStatistics::ApplicationCon
@cohorts = CohortsSerializer.new.represent(cohorts_results)
end
end
+
+ def authenticate_usage_ping_enabled_or_admin!
+ render_404 unless Gitlab::CurrentSettings.usage_ping_enabled || current_user.admin?
+ end
end
diff --git a/app/controllers/instance_statistics/conversational_development_index_controller.rb b/app/controllers/instance_statistics/conversational_development_index_controller.rb
index d6d2191849f..306c16d559c 100644
--- a/app/controllers/instance_statistics/conversational_development_index_controller.rb
+++ b/app/controllers/instance_statistics/conversational_development_index_controller.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
class InstanceStatistics::ConversationalDevelopmentIndexController < InstanceStatistics::ApplicationController
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@metric = ConversationalDevelopmentIndex::Metric.order(:created_at).last&.present
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index 025d8270b7c..315d1375e02 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class InvitesController < ApplicationController
before_action :member
skip_before_action :authenticate_user!, only: :decline
@@ -50,9 +52,9 @@ class InvitesController < ApplicationController
def authenticate_user!
return if current_user
- notice = "To accept this invitation, sign in"
- notice << " or create an account" if Gitlab::CurrentSettings.allow_signup?
- notice << "."
+ notice = ["To accept this invitation, sign in"]
+ notice << "or create an account" if Gitlab::CurrentSettings.allow_signup?
+ notice = notice.join(' ') + "."
store_location_for :user, request.fullpath
redirect_to new_user_session_path, notice: notice
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index d172aee5436..f9008a5b67e 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class JwtController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
diff --git a/app/controllers/koding_controller.rb b/app/controllers/koding_controller.rb
deleted file mode 100644
index 745abf3c0f5..00000000000
--- a/app/controllers/koding_controller.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class KodingController < ApplicationController
- before_action :check_integration!
- layout 'koding'
-
- def index
- path = File.join(Rails.root, 'doc/user/project/koding.md')
- @markdown = File.read(path)
- end
-
- private
-
- def check_integration!
- render_404 unless Gitlab::CurrentSettings.koding_enabled?
- end
-end
diff --git a/app/controllers/ldap/omniauth_callbacks_controller.rb b/app/controllers/ldap/omniauth_callbacks_controller.rb
index fb24edb8602..5e872804448 100644
--- a/app/controllers/ldap/omniauth_callbacks_controller.rb
+++ b/app/controllers/ldap/omniauth_callbacks_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Ldap::OmniauthCallbacksController < OmniauthCallbacksController
extend ::Gitlab::Utils::Override
diff --git a/app/controllers/metrics_controller.rb b/app/controllers/metrics_controller.rb
index 0400ffcfee5..7353be478e1 100644
--- a/app/controllers/metrics_controller.rb
+++ b/app/controllers/metrics_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MetricsController < ActionController::Base
include RequiresWhitelistedMonitoringClient
diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb
index ed20302487c..84dce74ace8 100644
--- a/app/controllers/notification_settings_controller.rb
+++ b/app/controllers/notification_settings_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NotificationSettingsController < ApplicationController
before_action :authenticate_user!
@@ -5,14 +7,14 @@ class NotificationSettingsController < ApplicationController
return render_404 unless can_read?(resource)
@notification_setting = current_user.notification_settings_for(resource)
- @saved = @notification_setting.update(notification_setting_params)
+ @saved = @notification_setting.update(notification_setting_params_for(resource))
render_response
end
def update
@notification_setting = current_user.notification_settings.find(params[:id])
- @saved = @notification_setting.update(notification_setting_params)
+ @saved = @notification_setting.update(notification_setting_params_for(@notification_setting.source))
render_response
end
@@ -42,8 +44,8 @@ class NotificationSettingsController < ApplicationController
}
end
- def notification_setting_params
- allowed_fields = NotificationSetting::EMAIL_EVENTS.dup
+ def notification_setting_params_for(source)
+ allowed_fields = NotificationSetting.email_events(source).dup
allowed_fields << :level
params.require(:notification_setting).permit(allowed_fields)
end
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index a1fe02dc852..b50f140dc80 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include Gitlab::GonHelper
include Gitlab::Allowable
include PageLayoutHelper
include OauthApplications
- before_action :verify_user_oauth_applications_enabled
+ before_action :verify_user_oauth_applications_enabled, except: :index
before_action :authenticate_user!
before_action :add_gon_variables
before_action :load_scopes, only: [:index, :create, :edit]
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index 05190103767..705389749d8 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
layout 'profile'
- # Overriden from Doorkeeper::AuthorizationsController to
+ # Overridden from Doorkeeper::AuthorizationsController to
# include the call to session.delete
def new
if pre_auth.authorizable?
diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb
index 656107d2b26..a59ade559b3 100644
--- a/app/controllers/oauth/authorized_applications_controller.rb
+++ b/app/controllers/oauth/authorized_applications_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
include PageLayoutHelper
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 1547d4b5972..30be50d4595 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
include Devise::Controllers::Rememberable
@@ -135,14 +137,13 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def handle_signup_error
label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider'])
- message = "Signing in using your #{label} account without a pre-existing GitLab account is not allowed."
+ message = ["Signing in using your #{label} account without a pre-existing GitLab account is not allowed."]
if Gitlab::CurrentSettings.allow_signup?
- message << " Create a GitLab account first, and then connect it to your #{label} account."
+ message << "Create a GitLab account first, and then connect it to your #{label} account."
end
- flash[:notice] = message
-
+ flash[:notice] = message.join(' ')
redirect_to new_user_session_path
end
diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb
index 331583c49e6..2912a22411e 100644
--- a/app/controllers/passwords_controller.rb
+++ b/app/controllers/passwords_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PasswordsController < Devise::PasswordsController
skip_before_action :require_no_authentication, only: [:edit, :update]
@@ -5,6 +7,7 @@ class PasswordsController < Devise::PasswordsController
before_action :check_password_authentication_available, only: [:create]
before_action :throttle_reset, only: [:create]
+ # rubocop: disable CodeReuse/ActiveRecord
def edit
super
reset_password_token = Devise.token_generator.digest(
@@ -24,6 +27,7 @@ class PasswordsController < Devise::PasswordsController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update
super do |resource|
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index 7d1aa8d1ce0..cb3180f4196 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::AccountsController < Profiles::ApplicationController
include AuthHelper
@@ -5,6 +7,7 @@ class Profiles::AccountsController < Profiles::ApplicationController
@user = current_user
end
+ # rubocop: disable CodeReuse/ActiveRecord
def unlink
provider = params[:provider]
identity = current_user.identities.find_by(provider: provider)
@@ -19,4 +22,5 @@ class Profiles::AccountsController < Profiles::ApplicationController
redirect_to profile_account_path
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb
index f1e77d68acd..efe7ede5efa 100644
--- a/app/controllers/profiles/active_sessions_controller.rb
+++ b/app/controllers/profiles/active_sessions_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::ActiveSessionsController < Profiles::ApplicationController
def index
@sessions = ActiveSession.list(current_user)
diff --git a/app/controllers/profiles/application_controller.rb b/app/controllers/profiles/application_controller.rb
index c8be288b9a0..52b046ef64f 100644
--- a/app/controllers/profiles/application_controller.rb
+++ b/app/controllers/profiles/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::ApplicationController < ApplicationController
layout 'profile'
end
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index 4f030ded80f..3378a09628c 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::AvatarsController < Profiles::ApplicationController
def destroy
@user = current_user
diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb
index a186c5f36a8..2e78b9e6dc7 100644
--- a/app/controllers/profiles/chat_names_controller.rb
+++ b/app/controllers/profiles/chat_names_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::ChatNamesController < Profiles::ApplicationController
before_action :chat_name_token, only: [:new]
before_action :chat_name_params, only: [:new, :create, :deny]
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index a39824ec9c8..503eda250b4 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::EmailsController < Profiles::ApplicationController
before_action :find_email, only: [:destroy, :resend_confirmation_instructions]
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index c32507756e8..8c34a66c374 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::GpgKeysController < Profiles::ApplicationController
before_action :set_gpg_key, only: [:destroy, :revoke]
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 6035258667e..3c3dc03a4ee 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::KeysController < Profiles::ApplicationController
skip_before_action :authenticate_user!, only: [:get_keys]
@@ -36,7 +38,7 @@ class Profiles::KeysController < Profiles::ApplicationController
def get_keys
if params[:username].present?
begin
- user = User.find_by_username(params[:username])
+ user = UserFinder.new(params[:username]).find_by_username
if user.present?
render text: user.all_ssh_keys.join("\n"), content_type: "text/plain"
else
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 8a38ba65d4c..b719b70c56e 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -1,10 +1,14 @@
+# frozen_string_literal: true
+
class Profiles::NotificationsController < Profiles::ApplicationController
+ # rubocop: disable CodeReuse/ActiveRecord
def show
@user = current_user
@group_notifications = current_user.notification_settings.for_groups.order(:id)
@project_notifications = current_user.notification_settings.for_projects.order(:id)
@global_notification_setting = current_user.global_notification_setting
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update
result = Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index b8ccc6e3c99..a0391d677c4 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::PasswordsController < Profiles::ApplicationController
skip_before_action :check_password_expiration, only: [:new, :create]
skip_before_action :check_two_factor_requirement, only: [:new, :create]
diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb
index 346eab4ba19..4b6ec2697b7 100644
--- a/app/controllers/profiles/personal_access_tokens_controller.rb
+++ b/app/controllers/profiles/personal_access_tokens_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
def index
set_index_vars
@@ -38,6 +40,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
params.require(:personal_access_token).permit(:name, :expires_at, scopes: [])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def set_index_vars
@scopes = Gitlab::Auth.available_scopes(current_user)
@@ -46,4 +49,5 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
@new_personal_access_token = PersonalAccessToken.redis_getdel(current_user.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index ed0f98179eb..37ac11dc6a1 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::PreferencesController < Profiles::ApplicationController
before_action :user
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 29ff18a1219..ba94196b2f9 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
skip_before_action :check_two_factor_requirement
@@ -30,7 +32,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
unless two_factor_grace_period_expired?
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
- flash.now[:alert] << " You need to do this before #{l(grace_period_deadline)}."
+ flash.now[:alert] = flash.now[:alert] + " You need to do this before #{l(grace_period_deadline)}."
end
end
diff --git a/app/controllers/profiles/u2f_registrations_controller.rb b/app/controllers/profiles/u2f_registrations_controller.rb
index e3d7737f44a..e6a154fb6aa 100644
--- a/app/controllers/profiles/u2f_registrations_controller.rb
+++ b/app/controllers/profiles/u2f_registrations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::U2fRegistrationsController < Profiles::ApplicationController
def destroy
u2f_registration = current_user.u2f_registrations.find(params[:id])
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 6f50cbb4a36..15248d2d08f 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProfilesController < Profiles::ApplicationController
include ActionView::Helpers::SanitizeHelper
@@ -44,11 +46,13 @@ class ProfilesController < Profiles::ApplicationController
redirect_to profile_personal_access_tokens_path
end
+ # rubocop: disable CodeReuse/ActiveRecord
def audit_log
@events = AuditEvent.where(entity_type: "User", entity_id: current_user.id)
.order("created_at DESC")
.page(params[:page])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update_username
result = Users::UpdateService.new(current_user, user: @user, username: username_param).execute
@@ -94,6 +98,7 @@ class ProfilesController < Profiles::ApplicationController
:location,
:name,
:public_email,
+ :commit_email,
:skype,
:twitter,
:username,
@@ -101,6 +106,7 @@ class ProfilesController < Profiles::ApplicationController
:organization,
:preferred_language,
:private_profile,
+ :include_private_contributions,
status: [:emoji, :message]
)
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index b4f814fd3a4..e0677ce3fbc 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -1,5 +1,9 @@
+# frozen_string_literal: true
+
class Projects::ApplicationController < ApplicationController
+ include CookiesHelper
include RoutableActions
+ include ProjectUnauthorized
include ChecksCollaboration
skip_before_action :authenticate_user!
@@ -18,7 +22,7 @@ class Projects::ApplicationController < ApplicationController
path = File.join(params[:namespace_id], params[:project_id] || params[:id])
auth_proc = ->(project) { !project.pending_delete? }
- @project = find_routable!(Project, path, extra_authorization_proc: auth_proc)
+ @project = find_routable!(Project, path, extra_authorization_proc: auth_proc, not_found_or_authorized_proc: project_unauthorized_proc)
end
def build_canonical_path(project)
@@ -74,7 +78,7 @@ class Projects::ApplicationController < ApplicationController
end
def apply_diff_view_cookie!
- cookies.permanent[:diff_view] = params.delete(:view) if params[:view].present?
+ set_secure_cookie(:diff_view, params.delete(:view), permanent: true) if params[:view].present?
end
def require_pages_enabled!
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index 6484a713f8e..ae9c17802b9 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ArtifactsController < Projects::ApplicationController
include ExtractsPath
include RendersBlob
@@ -8,11 +10,13 @@ class Projects::ArtifactsController < Projects::ApplicationController
before_action :authorize_update_build!, only: [:keep]
before_action :extract_ref_name_and_path
before_action :set_request_format, only: [:file]
- before_action :validate_artifacts!
+ before_action :validate_artifacts!, except: [:download]
before_action :entry, only: [:file]
def download
- send_upload(artifacts_file, attachment: artifacts_file.filename)
+ return render_404 unless artifacts_file
+
+ send_upload(artifacts_file, attachment: artifacts_file.filename, proxy: params[:proxy])
end
def browse
@@ -82,19 +86,23 @@ class Projects::ArtifactsController < Projects::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def build_from_id
project.builds.find_by(id: params[:job_id]) if params[:job_id]
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def build_from_ref
return unless @ref_name
builds = project.latest_successful_builds_for(@ref_name)
builds.find_by(name: params[:job])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def artifacts_file
- @artifacts_file ||= build.artifacts_file
+ @artifacts_file ||= build&.artifacts_file_for_type(params[:file_type] || :archive)
end
def entry
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index a8f73ed5cb0..9c130af8394 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -1,34 +1,38 @@
-class Projects::AutocompleteSourcesController < Projects::ApplicationController
- before_action :load_autocomplete_service, except: [:members]
+# frozen_string_literal: true
+class Projects::AutocompleteSourcesController < Projects::ApplicationController
def members
render json: ::Projects::ParticipantsService.new(@project, current_user).execute(target)
end
def issues
- render json: @autocomplete_service.issues
+ render json: autocomplete_service.issues
end
def merge_requests
- render json: @autocomplete_service.merge_requests
+ render json: autocomplete_service.merge_requests
end
def labels
- render json: @autocomplete_service.labels_as_hash(target)
+ render json: autocomplete_service.labels_as_hash(target)
end
def milestones
- render json: @autocomplete_service.milestones
+ render json: autocomplete_service.milestones
end
def commands
- render json: @autocomplete_service.commands(target, params[:type])
+ render json: autocomplete_service.commands(target, params[:type])
+ end
+
+ def snippets
+ render json: autocomplete_service.snippets
end
private
- def load_autocomplete_service
- @autocomplete_service = ::Projects::AutocompleteService.new(@project, current_user)
+ def autocomplete_service
+ @autocomplete_service ||= ::Projects::AutocompleteService.new(@project, current_user)
end
def target
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 53fdc5843b5..1f4a25f82e9 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -1,24 +1,18 @@
+# frozen_string_literal: true
+
class Projects::AvatarsController < Projects::ApplicationController
- include BlobHelper
+ include SendsBlob
before_action :authorize_admin_project!, only: [:destroy]
def show
@blob = @repository.blob_at_branch(@repository.root_ref, @project.avatar_in_git)
- if @blob
- headers['X-Content-Type-Options'] = 'nosniff'
-
- return if cached_blob?
- send_git_blob @repository, @blob
- else
- render_404
- end
+ send_blob(@repository, @blob)
end
def destroy
@project.remove_avatar!
-
@project.save
redirect_to edit_project_path(@project, anchor: 'js-general-project-settings'), status: :found
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index 06ba73d8e8d..c24bf211760 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::BadgesController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_admin_project!, only: [:index]
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index 6461eeac11c..9076bdb9f04 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Controller for viewing a file's blame
class Projects::BlameController < Projects::ApplicationController
include ExtractsPath
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index ebc61264b39..2a6fe3b9c97 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Controller for viewing a file's blame
class Projects::BlobController < Projects::ApplicationController
include ExtractsPath
@@ -7,7 +9,7 @@ class Projects::BlobController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper
prepend_before_action :authenticate_user!, only: [:edit]
- before_action :set_request_format, only: [:edit, :show, :update]
+ before_action :set_request_format, only: [:edit, :show, :update, :destroy]
before_action :require_non_empty_project, except: [:new, :create]
before_action :authorize_download_code!
@@ -81,7 +83,7 @@ class Projects::BlobController < Projects::ApplicationController
def destroy
create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
- success_path: -> { project_tree_path(@project, @branch_name) },
+ success_path: -> { after_delete_path },
failure_view: :show,
failure_path: project_blob_path(@project, @id))
end
@@ -90,7 +92,7 @@ class Projects::BlobController < Projects::ApplicationController
apply_diff_view_cookie!
@blob.load_all_data!
- @lines = Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: @repository).lines
+ @lines = @blob.present.highlight.lines
@form = UnfoldForm.new(params)
@@ -120,14 +122,14 @@ class Projects::BlobController < Projects::ApplicationController
@lines.map! do |line|
# These are marked as context lines but are loaded from blobs.
# We also have context lines loaded from diffs in other places.
- diff_line = Gitlab::Diff::Line.new(line, 'context', nil, nil, nil)
+ diff_line = Gitlab::Diff::Line.new(line, nil, nil, nil, nil)
diff_line.rich_text = line
diff_line
end
add_match_line
- render json: @lines
+ render json: DiffLineSerializer.new.represent(@lines)
end
def add_match_line
@@ -177,6 +179,7 @@ class Projects::BlobController < Projects::ApplicationController
render_404
end
+ # rubocop: disable CodeReuse/ActiveRecord
def after_edit_path
from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).find_by(iid: params[:from_merge_request_iid])
if from_merge_request && @branch_name == @ref
@@ -186,6 +189,16 @@ class Projects::BlobController < Projects::ApplicationController
project_blob_path(@project, File.join(@branch_name, @path))
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def after_delete_path
+ branch = BranchesFinder.new(@repository, search: @ref).execute.first
+ if @repository.tree(branch.target, tree_path).entries.empty?
+ project_tree_path(@project, @ref)
+ else
+ project_tree_path(@project, File.join(@ref, tree_path))
+ end
+ end
def editor_variables
@branch_name = params[:branch_name]
@@ -251,9 +264,6 @@ class Projects::BlobController < Projects::ApplicationController
def show_json
set_last_commit_sha
- path_segments = @path.split('/')
- path_segments.pop
- tree_path = path_segments.join('/')
json = {
id: @blob.id,
@@ -279,4 +289,8 @@ class Projects::BlobController < Projects::ApplicationController
render json: json
end
+
+ def tree_path
+ @path.rpartition('/').first
+ end
end
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index e7354a9e1f7..8189b5d182a 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::BoardsController < Projects::ApplicationController
include BoardsResponses
include IssuableCollections
@@ -6,6 +8,7 @@ class Projects::BoardsController < Projects::ApplicationController
before_action :authorize_read_board!, only: [:index, :show]
before_action :boards, only: :index
before_action :assign_endpoint_vars
+ before_action :redirect_to_recent_board, only: :index
def index
respond_with_boards
@@ -14,6 +17,9 @@ class Projects::BoardsController < Projects::ApplicationController
def show
@board = boards.find(params[:id])
+ # add/update the board in the recent visited table
+ Boards::Visits::CreateService.new(@board.project, current_user).execute(@board) if request.format.html?
+
respond_with_board
end
@@ -31,10 +37,24 @@ class Projects::BoardsController < Projects::ApplicationController
end
def authorize_read_board!
- return access_denied! unless can?(current_user, :read_board, project)
+ access_denied! unless can?(current_user, :read_board, project)
end
def serialize_as_json(resource)
resource.as_json(only: [:id])
end
+
+ def includes_board?(board_id)
+ boards.any? { |board| board.id == board_id }
+ end
+
+ def redirect_to_recent_board
+ return if request.format.json?
+
+ recently_visited = Boards::Visits::LatestService.new(project, current_user).execute
+
+ if recently_visited && includes_board?(recently_visited.board_id)
+ redirect_to(namespace_project_board_path(id: recently_visited.board_id), status: :found)
+ end
+ end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index d1dc9fe9600..b7750f4517b 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::BranchesController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper
include SortingHelper
@@ -48,6 +50,7 @@ class Projects::BranchesController < Projects::ApplicationController
@branches = @repository.recent_branches
end
+ # rubocop: disable CodeReuse/ActiveRecord
def create
branch_name = sanitize(strip_tags(params[:branch_name]))
branch_name = Addressable::URI.unescape(branch_name)
@@ -88,6 +91,7 @@ class Projects::BranchesController < Projects::ApplicationController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def destroy
@branch_name = Addressable::URI.unescape(params[:id])
diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb
index b45e5d7ff43..7d4d566499c 100644
--- a/app/controllers/projects/build_artifacts_controller.rb
+++ b/app/controllers/projects/build_artifacts_controller.rb
@@ -1,13 +1,15 @@
+# frozen_string_literal: true
+
class Projects::BuildArtifactsController < Projects::ApplicationController
include ExtractsPath
include RendersBlob
before_action :authorize_read_build!
before_action :extract_ref_name_and_path
- before_action :validate_artifacts!
+ before_action :validate_artifacts!, except: [:download]
def download
- redirect_to download_project_job_artifacts_path(project, job)
+ redirect_to download_project_job_artifacts_path(project, job, params: request.query_parameters)
end
def browse
@@ -42,14 +44,18 @@ class Projects::BuildArtifactsController < Projects::ApplicationController
@job ||= job_from_id || job_from_ref
end
+ # rubocop: disable CodeReuse/ActiveRecord
def job_from_id
project.builds.find_by(id: params[:build_id]) if params[:build_id]
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def job_from_ref
return unless @ref_name
jobs = project.latest_successful_builds_for(@ref_name)
jobs.find_by(name: params[:job])
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 230b072dcea..6b3d70cb720 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::BuildsController < Projects::ApplicationController
before_action :authorize_read_build!
diff --git a/app/controllers/projects/ci/lints_controller.rb b/app/controllers/projects/ci/lints_controller.rb
index a2185572a20..2090af0a111 100644
--- a/app/controllers/projects/ci/lints_controller.rb
+++ b/app/controllers/projects/ci/lints_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::Ci::LintsController < Projects::ApplicationController
before_action :authorize_create_pipeline!
diff --git a/app/controllers/projects/clusters/applications_controller.rb b/app/controllers/projects/clusters/applications_controller.rb
index a5c82caa897..c7b6218d007 100644
--- a/app/controllers/projects/clusters/applications_controller.rb
+++ b/app/controllers/projects/clusters/applications_controller.rb
@@ -1,47 +1,17 @@
-class Projects::Clusters::ApplicationsController < Projects::ApplicationController
- before_action :cluster
- before_action :application_class, only: [:create]
- before_action :authorize_read_cluster!
- before_action :authorize_create_cluster!, only: [:create]
+# frozen_string_literal: true
- def create
- application = @application_class.find_or_initialize_by(cluster: @cluster)
+class Projects::Clusters::ApplicationsController < Clusters::ApplicationsController
+ include ProjectUnauthorized
- if application.has_attribute?(:hostname)
- application.hostname = params[:hostname]
- end
-
- if application.respond_to?(:oauth_application)
- application.oauth_application = create_oauth_application(application)
- end
-
- application.save!
-
- Clusters::Applications::ScheduleInstallationService.new(project, current_user).execute(application)
-
- head :no_content
- rescue StandardError
- head :bad_request
- end
+ prepend_before_action :project
private
- def cluster
- @cluster ||= project.clusters.find(params[:id]) || render_404
- end
-
- def application_class
- @application_class ||= Clusters::Cluster::APPLICATIONS[params[:application]] || render_404
+ def clusterable
+ @clusterable ||= ClusterablePresenter.fabricate(project, current_user: current_user)
end
- def create_oauth_application(application)
- oauth_application_params = {
- name: params[:application],
- redirect_uri: application.callback_url,
- scopes: 'api read_user openid',
- owner: current_user
- }
-
- Applications::CreateService.new(current_user, oauth_application_params).execute(request)
+ def project
+ @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), not_found_or_authorized_proc: project_unauthorized_proc)
end
end
diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb
index 358fe59618b..feda6deeaa6 100644
--- a/app/controllers/projects/clusters_controller.rb
+++ b/app/controllers/projects/clusters_controller.rb
@@ -1,220 +1,24 @@
-class Projects::ClustersController < Projects::ApplicationController
- before_action :cluster, except: [:index, :new, :create_gcp, :create_user]
- before_action :authorize_read_cluster!
- before_action :generate_gcp_authorize_url, only: [:new]
- before_action :validate_gcp_token, only: [:new]
- before_action :gcp_cluster, only: [:new]
- before_action :user_cluster, only: [:new]
- before_action :authorize_create_cluster!, only: [:new]
- before_action :authorize_update_cluster!, only: [:update]
- before_action :authorize_admin_cluster!, only: [:destroy]
- before_action :update_applications_status, only: [:status]
- helper_method :token_in_session
+# frozen_string_literal: true
- STATUS_POLLING_INTERVAL = 10_000
+class Projects::ClustersController < Clusters::ClustersController
+ include ProjectUnauthorized
- def index
- clusters = ClustersFinder.new(project, current_user, :all).execute
- @clusters = clusters.page(params[:page]).per(20)
- end
-
- def new
- end
-
- def status
- respond_to do |format|
- format.json do
- Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL)
-
- render json: ClusterSerializer
- .new(project: @project, current_user: @current_user)
- .represent_status(@cluster)
- end
- end
- end
-
- def show
- end
-
- def update
- Clusters::UpdateService
- .new(project, current_user, update_params)
- .execute(cluster)
-
- if cluster.valid?
- respond_to do |format|
- format.json do
- head :no_content
- end
- format.html do
- flash[:notice] = _('Kubernetes cluster was successfully updated.')
- redirect_to project_cluster_path(project, cluster)
- end
- end
- else
- respond_to do |format|
- format.json { head :bad_request }
- format.html { render :show }
- end
- end
- end
+ prepend_before_action :project
+ before_action :repository
- def destroy
- if cluster.destroy
- flash[:notice] = _('Kubernetes cluster integration was successfully removed.')
- redirect_to project_clusters_path(project), status: :found
- else
- flash[:notice] = _('Kubernetes cluster integration was not removed.')
- render :show
- end
- end
-
- def create_gcp
- @gcp_cluster = ::Clusters::CreateService
- .new(project, current_user, create_gcp_cluster_params)
- .execute(token_in_session)
-
- if @gcp_cluster.persisted?
- redirect_to project_cluster_path(project, @gcp_cluster)
- else
- generate_gcp_authorize_url
- validate_gcp_token
- user_cluster
-
- render :new, locals: { active_tab: 'gcp' }
- end
- end
-
- def create_user
- @user_cluster = ::Clusters::CreateService
- .new(project, current_user, create_user_cluster_params)
- .execute(token_in_session)
-
- if @user_cluster.persisted?
- redirect_to project_cluster_path(project, @user_cluster)
- else
- generate_gcp_authorize_url
- validate_gcp_token
- gcp_cluster
-
- render :new, locals: { active_tab: 'user' }
- end
- end
+ layout 'project'
private
- def cluster
- @cluster ||= project.clusters.find(params[:id])
- .present(current_user: current_user)
- end
-
- def update_params
- if cluster.managed?
- params.require(:cluster).permit(
- :enabled,
- :environment_scope,
- platform_kubernetes_attributes: [
- :namespace
- ]
- )
- else
- params.require(:cluster).permit(
- :enabled,
- :name,
- :environment_scope,
- platform_kubernetes_attributes: [
- :api_url,
- :token,
- :ca_cert,
- :namespace
- ]
- )
- end
- end
-
- def create_gcp_cluster_params
- params.require(:cluster).permit(
- :enabled,
- :name,
- :environment_scope,
- provider_gcp_attributes: [
- :gcp_project_id,
- :zone,
- :num_nodes,
- :machine_type
- ]).merge(
- provider_type: :gcp,
- platform_type: :kubernetes
- )
- end
-
- def create_user_cluster_params
- params.require(:cluster).permit(
- :enabled,
- :name,
- :environment_scope,
- platform_kubernetes_attributes: [
- :namespace,
- :api_url,
- :token,
- :ca_cert
- ]).merge(
- provider_type: :user,
- platform_type: :kubernetes
- )
- end
-
- def generate_gcp_authorize_url
- state = generate_session_key_redirect(new_project_cluster_path(@project).to_s)
-
- @authorize_url = GoogleApi::CloudPlatform::Client.new(
- nil, callback_google_api_auth_url,
- state: state).authorize_url
- rescue GoogleApi::Auth::ConfigMissingError
- # no-op
- end
-
- def gcp_cluster
- @gcp_cluster = ::Clusters::Cluster.new.tap do |cluster|
- cluster.build_provider_gcp
- end
- end
-
- def user_cluster
- @user_cluster = ::Clusters::Cluster.new.tap do |cluster|
- cluster.build_platform_kubernetes
- end
- end
-
- def validate_gcp_token
- @valid_gcp_token = GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
- .validate_token(expires_at_in_session)
- end
-
- def token_in_session
- session[GoogleApi::CloudPlatform::Client.session_key_for_token]
- end
-
- def expires_at_in_session
- @expires_at_in_session ||=
- session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at]
- end
-
- def generate_session_key_redirect(uri)
- GoogleApi::CloudPlatform::Client.new_session_key_for_redirect_uri do |key|
- session[key] = uri
- end
- end
-
- def authorize_update_cluster!
- access_denied! unless can?(current_user, :update_cluster, cluster)
+ def clusterable
+ @clusterable ||= ClusterablePresenter.fabricate(project, current_user: current_user)
end
- def authorize_admin_cluster!
- access_denied! unless can?(current_user, :admin_cluster, cluster)
+ def project
+ @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), not_found_or_authorized_proc: project_unauthorized_proc)
end
- def update_applications_status
- @cluster.applications.each(&:schedule_status_update)
+ def repository
+ @repository ||= project.repository
end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 44b176d304e..32fc5140366 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Controller for a specific Commit
#
# Not to be confused with CommitsController, plural.
@@ -38,9 +40,10 @@ class Projects::CommitController < Projects::ApplicationController
render_diff_for_path(@commit.diffs(diff_options))
end
+ # rubocop: disable CodeReuse/ActiveRecord
def pipelines
@pipelines = @commit.pipelines.order(id: :desc)
- @pipelines = @pipelines.where(ref: params[:ref]) if params[:ref]
+ @pipelines = @pipelines.where(ref: params[:ref]).page(params[:page]).per(30) if params[:ref]
respond_to do |format|
format.html
@@ -50,6 +53,7 @@ class Projects::CommitController < Projects::ApplicationController
render json: {
pipelines: PipelineSerializer
.new(project: @project, current_user: @current_user)
+ .with_pagination(request, response)
.represent(@pipelines),
count: {
all: @pipelines.count
@@ -58,6 +62,7 @@ class Projects::CommitController < Projects::ApplicationController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def merge_requests
@merge_requests = @commit.merge_requests.map do |mr|
@@ -101,7 +106,7 @@ class Projects::CommitController < Projects::ApplicationController
@branch_name = create_new_branch? ? @commit.cherry_pick_branch_name : @start_branch
- create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked.",
+ create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked into #{@branch_name}.",
success_path: -> { successful_change_path }, failure_path: failed_change_path)
end
@@ -144,6 +149,7 @@ class Projects::CommitController < Projects::ApplicationController
@environment = EnvironmentsFinder.new(@project, current_user, commit: @commit).execute.last
end
+ # rubocop: disable CodeReuse/ActiveRecord
def define_note_vars
@noteable = @commit
@note = @project.build_commit_note(commit)
@@ -176,6 +182,7 @@ class Projects::CommitController < Projects::ApplicationController
@notes = (@grouped_diff_discussions.values.flatten + @discussions).flat_map(&:notes)
@notes = prepare_notes_for_rendering(@notes, @commit)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def assign_change_commit_vars
@start_branch = params[:start_branch]
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index 36faea8056e..84a2a461da7 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "base64"
class Projects::CommitsController < Projects::ApplicationController
@@ -15,6 +17,7 @@ class Projects::CommitsController < Projects::ApplicationController
redirect_to project_commits_path(@project, @project.default_branch)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def show
@merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
.find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
@@ -32,6 +35,7 @@ class Projects::CommitsController < Projects::ApplicationController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def signatures
respond_to do |format|
@@ -63,7 +67,7 @@ class Projects::CommitsController < Projects::ApplicationController
end
@commits = @commits.with_pipeline_status
- @commits = prepare_commits_for_rendering(@commits)
+ @commits = set_commits_for_rendering(@commits)
end
# Rails 5 sets request.format from the extension.
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index f93e500a07a..2917925947f 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'addressable/uri'
class Projects::CompareController < Projects::ApplicationController
@@ -14,6 +16,8 @@ class Projects::CompareController < Projects::ApplicationController
before_action :define_diff_notes_disabled, only: [:show, :diff_for_path]
before_action :define_commits, only: [:show, :diff_for_path, :signatures]
before_action :merge_request, only: [:index, :show]
+ # Validation
+ before_action :validate_refs!
def index
end
@@ -61,6 +65,21 @@ class Projects::CompareController < Projects::ApplicationController
private
+ def valid_ref?(ref_name)
+ return true unless ref_name.present?
+
+ Gitlab::GitRefValidator.validate(ref_name)
+ end
+
+ def validate_refs!
+ valid = [head_ref, start_ref].map { |ref| valid_ref?(ref) }
+
+ return if valid.all?
+
+ flash[:alert] = "Invalid branch name"
+ redirect_to project_compare_index_path(@project)
+ end
+
def compare
return @compare if defined?(@compare)
@@ -78,7 +97,7 @@ class Projects::CompareController < Projects::ApplicationController
end
def define_commits
- @commits = compare.present? ? prepare_commits_for_rendering(compare.commits) : []
+ @commits = compare.present? ? set_commits_for_rendering(@compare.commits) : []
end
def define_diffs
@@ -96,8 +115,10 @@ class Projects::CompareController < Projects::ApplicationController
@diff_notes_disabled = compare.present?
end
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_request
@merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
.find_by(source_project: @project, source_branch: head_ref, target_branch: start_ref)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb
index 26f3c114108..fb43356ff10 100644
--- a/app/controllers/projects/cycle_analytics/events_controller.rb
+++ b/app/controllers/projects/cycle_analytics/events_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module CycleAnalytics
class EventsController < Projects::ApplicationController
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index d1b8fd80c4e..8c071496ba9 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::CycleAnalyticsController < Projects::ApplicationController
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TextHelper
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 28fea322334..0a593bd35b6 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::DeployKeysController < Projects::ApplicationController
include RepositorySettingsRedirect
respond_to :html
@@ -44,7 +46,9 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def enable
- Projects::EnableDeployKeyService.new(@project, current_user, params).execute
+ key = Projects::EnableDeployKeyService.new(@project, current_user, params).execute
+
+ return render_404 unless key
respond_to do |format|
format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
@@ -53,10 +57,9 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def disable
- deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id])
- return render_404 unless deploy_key_project
+ deploy_key_project = Projects::DisableDeployKeyService.new(@project, current_user, params).execute
- deploy_key_project.destroy!
+ return render_404 unless deploy_key_project
respond_to do |format|
format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
diff --git a/app/controllers/projects/deploy_tokens_controller.rb b/app/controllers/projects/deploy_tokens_controller.rb
index 83abda64fe0..830b1f4fe4a 100644
--- a/app/controllers/projects/deploy_tokens_controller.rb
+++ b/app/controllers/projects/deploy_tokens_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::DeployTokensController < Projects::ApplicationController
before_action :authorize_admin_project!
diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb
index b68cdc39cb8..0a009477d61 100644
--- a/app/controllers/projects/deployments_controller.rb
+++ b/app/controllers/projects/deployments_controller.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
class Projects::DeploymentsController < Projects::ApplicationController
before_action :authorize_read_environment!
before_action :authorize_read_deployment!
+ # rubocop: disable CodeReuse/ActiveRecord
def index
deployments = environment.deployments.reorder(created_at: :desc)
deployments = deployments.where('created_at > ?', params[:after].to_time) if params[:after]&.to_time
@@ -9,6 +12,7 @@ class Projects::DeploymentsController < Projects::ApplicationController
render json: { deployments: DeploymentSerializer.new(project: project)
.represent_concise(deployments) }
end
+ # rubocop: enable CodeReuse/ActiveRecord
def metrics
return render_404 unless deployment.has_metrics?
@@ -41,9 +45,11 @@ class Projects::DeploymentsController < Projects::ApplicationController
private
+ # rubocop: disable CodeReuse/ActiveRecord
def deployment
@deployment ||= environment.deployments.find_by(iid: params[:id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def environment
@environment ||= project.environments.find(params[:environment_id])
diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb
index 78b9d53a780..b62606067c0 100644
--- a/app/controllers/projects/discussions_controller.rb
+++ b/app/controllers/projects/discussions_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::DiscussionsController < Projects::ApplicationController
include NotesHelper
include RendersNotes
@@ -50,9 +52,11 @@ class Projects::DiscussionsController < Projects::ApplicationController
}
end
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_request
@merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).find_by!(iid: params[:merge_request_id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def discussion
@discussion ||= @merge_request.find_discussion(params[:id]) || render_404
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 68353e6a210..de10783df1a 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::EnvironmentsController < Projects::ApplicationController
layout 'project'
before_action :authorize_read_environment!
@@ -31,6 +33,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def folder
folder_environments = project.environments.where(environment_type: params[:id])
@environments = folder_environments.with_state(params[:scope] || :available)
@@ -51,10 +54,13 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def show
@deployments = environment.deployments.order(id: :desc).page(params[:page])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def new
@environment = project.environments.new
diff --git a/app/controllers/projects/find_file_controller.rb b/app/controllers/projects/find_file_controller.rb
index cf53ad0a670..c026e9ff332 100644
--- a/app/controllers/projects/find_file_controller.rb
+++ b/app/controllers/projects/find_file_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Controller for viewing a repository's file structure
class Projects::FindFileController < Projects::ApplicationController
include ExtractsPath
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index f43bba18d81..7a1700a206a 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ForksController < Projects::ApplicationController
include ContinueParams
@@ -7,6 +9,7 @@ class Projects::ForksController < Projects::ApplicationController
before_action :authorize_download_code!
before_action :authenticate_user!, only: [:new, :create]
+ # rubocop: disable CodeReuse/ActiveRecord
def index
base_query = project.forks.includes(:creator)
@@ -27,12 +30,14 @@ class Projects::ForksController < Projects::ApplicationController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def new
@namespaces = current_user.manageable_namespaces
@namespaces.delete(@project.namespace)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def create
namespace = Namespace.find(params[:namespace_key])
@@ -55,6 +60,7 @@ class Projects::ForksController < Projects::ApplicationController
render :error
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42335')
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index a52814e6e52..d439db97252 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpClientController < Projects::ApplicationController
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index 1dcf837f78e..c0aa39d87c6 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::GitHttpController < Projects::GitHttpClientController
include WorkhorseRequest
@@ -6,6 +8,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
rescue_from Gitlab::GitAccess::UnauthorizedError, with: :render_403
rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404
rescue_from Gitlab::GitAccess::ProjectCreationError, with: :render_422
+ rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
@@ -60,6 +63,10 @@ class Projects::GitHttpController < Projects::GitHttpClientController
render plain: exception.message, status: :unprocessable_entity
end
+ def render_503(exception)
+ render plain: exception.message, status: :service_unavailable
+ end
+
def access
@access ||= access_klass.new(access_actor, project,
'http', authentication_abilities: authentication_abilities,
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 475d4c86294..925b6ed9bfd 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::GraphsController < Projects::ApplicationController
include ExtractsPath
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index bc5f38f3c2b..7c713c19762 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::GroupLinksController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_admin_project!
diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb
index 745e89fc843..a7afc3d77a5 100644
--- a/app/controllers/projects/hook_logs_controller.rb
+++ b/app/controllers/projects/hook_logs_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::HookLogsController < Projects::ApplicationController
include HooksExecution
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 2da2aad9b33..bc84418b79f 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::HooksController < Projects::ApplicationController
include HooksExecution
@@ -66,6 +68,7 @@ class Projects::HooksController < Projects::ApplicationController
:enable_ssl_verification,
:token,
:url,
+ :push_events_branch_filter,
*ProjectHook.triggers.values
)
end
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index 49aa32119ef..e55065c5817 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ImportsController < Projects::ApplicationController
include ContinueParams
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index ef8159aa553..308f666394c 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::IssuesController < Projects::ApplicationController
include RendersNotes
include ToggleSubscriptionAction
@@ -7,12 +9,25 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuesCalendar
include SpammableActions
- prepend_before_action :authenticate_user!, only: [:new]
+ def self.authenticate_user_only_actions
+ %i[new]
+ end
+
+ def self.issue_except_actions
+ %i[index calendar new create bulk_update]
+ end
+
+ def self.set_issuables_index_only_actions
+ %i[index calendar]
+ end
+
+ prepend_before_action :authenticate_user!, only: authenticate_user_only_actions
before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update]
before_action :check_issues_available!
- before_action :issue, except: [:index, :calendar, :new, :create, :bulk_update]
- before_action :set_issuables_index, only: [:index, :calendar]
+ before_action :issue, except: issue_except_actions
+
+ before_action :set_issuables_index, only: set_issuables_index_only_actions
# Allow write(create) issue
before_action :authorize_create_issue!, only: [:new, :create]
@@ -113,7 +128,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def referenced_merge_requests
- @merge_requests, @closed_by_merge_requests = ::Issues::FetchReferencedMergeRequestsService.new(project, current_user).execute(issue)
+ @merge_requests, @closed_by_merge_requests = ::Issues::ReferencedMergeRequestsService.new(project, current_user).execute(issue)
respond_to do |format|
format.json do
@@ -125,7 +140,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def related_branches
- @related_branches = @issue.related_branches(current_user)
+ @related_branches = Issues::RelatedBranchesService.new(project, current_user).execute(issue)
respond_to do |format|
format.json do
@@ -161,6 +176,7 @@ class Projects::IssuesController < Projects::ApplicationController
protected
+ # rubocop: disable CodeReuse/ActiveRecord
def issue
return @issue if defined?(@issue)
@@ -172,6 +188,7 @@ class Projects::IssuesController < Projects::ApplicationController
@issue
end
+ # rubocop: enable CodeReuse/ActiveRecord
alias_method :subscribable_resource, :issue
alias_method :issuable, :issue
alias_method :awardable, :issue
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index e69faae754a..3ecf94c008e 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
class Projects::JobsController < Projects::ApplicationController
include SendFileUpload
+ include ContinueParams
before_action :build, except: [:index, :cancel_all]
before_action :authorize_read_build!
@@ -11,6 +14,7 @@ class Projects::JobsController < Projects::ApplicationController
layout 'project'
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@scope = params[:scope]
@all_builds = project.builds.relevant
@@ -33,6 +37,7 @@ class Projects::JobsController < Projects::ApplicationController
])
@builds = @builds.page(params[:page]).per(30).without_count
end
+ # rubocop: enable CodeReuse/ActiveRecord
def cancel_all
return access_denied! unless can?(current_user, :update_build, project)
@@ -44,6 +49,7 @@ class Projects::JobsController < Projects::ApplicationController
redirect_to project_jobs_path(project)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def show
@pipeline = @build.pipeline
@builds = @pipeline.builds
@@ -61,6 +67,7 @@ class Projects::JobsController < Projects::ApplicationController
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def trace
build.trace.read do |stream|
@@ -101,6 +108,18 @@ class Projects::JobsController < Projects::ApplicationController
return respond_422 unless @build.cancelable?
@build.cancel
+
+ if continue_params
+ redirect_to continue_params[:to]
+ else
+ redirect_to builds_project_pipeline_path(@project, @build.pipeline.id)
+ end
+ end
+
+ def unschedule
+ return respond_422 unless @build.scheduled?
+
+ @build.unschedule!
redirect_to build_path(@build)
end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 8a2bce6e7b5..640038818f2 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::LabelsController < Projects::ApplicationController
include ToggleSubscriptionAction
@@ -90,6 +92,7 @@ class Projects::LabelsController < Projects::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def set_priorities
Label.transaction do
available_labels_ids = @available_labels.where(id: params[:label_ids]).pluck(:id)
@@ -105,6 +108,7 @@ class Projects::LabelsController < Projects::ApplicationController
format.json { render json: { message: 'success' } }
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def promote
promote_service = Labels::PromoteService.new(@project, @current_user)
@@ -136,12 +140,7 @@ class Projects::LabelsController < Projects::ApplicationController
end
def flash_notice_for(label, group)
- notice = ''.html_safe
- notice << label.title
- notice << ' promoted to '
- notice << view_context.link_to('<u>group label</u>'.html_safe, group_labels_path(group))
- notice << '.'
- notice
+ ''.html_safe + "#{label.title} promoted to " + view_context.link_to('<u>group label</u>'.html_safe, group_labels_path(group)) + '.'
end
protected
@@ -163,7 +162,13 @@ class Projects::LabelsController < Projects::ApplicationController
LabelsFinder.new(current_user,
project_id: @project.id,
include_ancestor_groups: params[:include_ancestor_groups],
- search: params[:search]).execute
+ search: params[:search],
+ subscribed: params[:subscribed],
+ sort: sort).execute
+ end
+
+ def sort
+ @sort ||= params[:sort] || 'name_asc'
end
def authorize_admin_labels!
diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb
index a01351ba292..be40077d389 100644
--- a/app/controllers/projects/lfs_api_controller.rb
+++ b/app/controllers/projects/lfs_api_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::LfsApiController < Projects::GitHttpClientController
include LfsRequest
@@ -41,11 +43,13 @@ class Projects::LfsApiController < Projects::GitHttpClientController
params[:operation] == 'upload'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def existing_oids
@existing_oids ||= begin
project.all_lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def download_objects!
objects.each do |object|
diff --git a/app/controllers/projects/lfs_locks_api_controller.rb b/app/controllers/projects/lfs_locks_api_controller.rb
index 3fff0fd69ae..fc67cd72faa 100644
--- a/app/controllers/projects/lfs_locks_api_controller.rb
+++ b/app/controllers/projects/lfs_locks_api_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::LfsLocksApiController < Projects::GitHttpClientController
include LfsRequest
diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb
index dd7e673ec75..babeee48ef3 100644
--- a/app/controllers/projects/lfs_storage_controller.rb
+++ b/app/controllers/projects/lfs_storage_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::LfsStorageController < Projects::GitHttpClientController
include LfsRequest
include WorkhorseRequest
@@ -56,6 +58,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
params[:size].to_i
end
+ # rubocop: disable CodeReuse/ActiveRecord
def store_file!(oid, size)
object = LfsObject.find_by(oid: oid, size: size)
unless object&.file&.exists?
@@ -66,6 +69,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
link_to_project!(object)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def create_file!(oid, size)
uploaded_file = UploadedFile.from_params(
@@ -75,9 +79,11 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
LfsObject.create!(oid: oid, size: size, file: uploaded_file)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def link_to_project!(object)
if object && !object.projects.exists?(storage_project.id)
object.lfs_objects_projects.create!(project: storage_project)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/projects/mattermosts_controller.rb b/app/controllers/projects/mattermosts_controller.rb
index 0f6add3e287..085b1bc1498 100644
--- a/app/controllers/projects/mattermosts_controller.rb
+++ b/app/controllers/projects/mattermosts_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::MattermostsController < Projects::ApplicationController
include TriggersHelper
include ActionView::Helpers::AssetUrlHelper
diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb
index fead81dd472..368ee89ff5c 100644
--- a/app/controllers/projects/merge_requests/application_controller.rb
+++ b/app/controllers/projects/merge_requests/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::MergeRequests::ApplicationController < Projects::ApplicationController
before_action :check_merge_requests_available!
before_action :merge_request
@@ -5,9 +7,11 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
private
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_request
@issuable = @merge_request ||= @project.merge_requests.includes(author: :status).find_by!(iid: params[:id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def merge_request_params
params.require(:merge_request).permit(merge_request_params_attributes)
diff --git a/app/controllers/projects/merge_requests/conflicts_controller.rb b/app/controllers/projects/merge_requests/conflicts_controller.rb
index 366524b0783..ac1969adc6e 100644
--- a/app/controllers/projects/merge_requests/conflicts_controller.rb
+++ b/app/controllers/projects/merge_requests/conflicts_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::ApplicationController
include IssuableActions
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index 81129456ad8..5639402a1e9 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::MergeRequests::CreationsController < Projects::MergeRequests::ApplicationController
include DiffForPath
include DiffHelper
@@ -101,14 +103,19 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
@target_project = @merge_request.target_project
@source_project = @merge_request.source_project
- @commits = prepare_commits_for_rendering(@merge_request.commits)
+ @commits = set_commits_for_rendering(@merge_request.commits)
@commit = @merge_request.diff_head_commit
+ # FIXME: We have to assign a presenter to another instance variable
+ # due to class_name checks being made with issuable classes
+ @mr_presenter = @merge_request.present(current_user: current_user)
+
@labels = LabelsFinder.new(current_user, project_id: @project.id).execute
set_pipeline_variables
end
+ # rubocop: disable CodeReuse/ActiveRecord
def selected_target_project
if @project.id.to_s == params[:target_project_id] || !@project.forked?
@project
@@ -119,6 +126,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
@project.forked_from_project
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42384')
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index 48e02581d54..b3d77335c2a 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::MergeRequests::DiffsController < Projects::MergeRequests::ApplicationController
include DiffForPath
include DiffHelper
@@ -20,8 +22,22 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
def render_diffs
@environment = @merge_request.environments_for(current_user).last
+ notes_grouped_by_path = renderable_notes.group_by { |note| note.position.file_path }
+
+ @diffs.diff_files.each do |diff_file|
+ notes = notes_grouped_by_path.fetch(diff_file.file_path, [])
+ notes.each { |note| diff_file.unfold_diff_lines(note.position) }
+ end
- render json: DiffsSerializer.new(current_user: current_user).represent(@diffs, additional_attributes)
+ @diffs.write_cache
+
+ request = {
+ current_user: current_user,
+ project: @merge_request.project,
+ render: ->(partial, locals) { view_to_html_string(partial, locals) }
+ }
+
+ render json: DiffsSerializer.new(request).represent(@diffs, additional_attributes)
end
def define_diff_vars
@@ -32,13 +48,16 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
@diffs = @compare.diffs(diff_options)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def commit
return nil unless commit_id = params[:commit_id].presence
return nil unless @merge_request.all_commits.exists?(sha: commit_id)
@commit ||= @project.commit(commit_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def find_merge_request_diff_compare
@merge_request_diff =
if diff_id = params[:diff_id].presence
@@ -66,6 +85,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
@merge_request_diff
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def additional_attributes
{
@@ -94,4 +114,10 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
@grouped_diff_discussions = @merge_request.grouped_diff_discussions(@compare.diff_refs)
@notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes), @merge_request)
end
+
+ def renderable_notes
+ define_diff_comment_vars unless @notes
+
+ @notes
+ end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 1b069fe507b..23d16fed7b9 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationController
include ToggleSubscriptionAction
include IssuableActions
@@ -42,12 +44,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@noteable = @merge_request
@commits_count = @merge_request.commits_count
- # TODO cleanup- Fatih Simon Create an issue to remove these after the refactoring
- # we no longer render notes here. I see it will require a small frontend refactoring,
- # since we gather some data from this collection.
- @discussions = @merge_request.discussions
- @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
-
labels
set_pipeline_variables
@@ -79,19 +75,20 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
# Get commits from repository
# or from cache if already merged
@commits =
- prepare_commits_for_rendering(@merge_request.commits.with_pipeline_status)
+ set_commits_for_rendering(@merge_request.commits.with_pipeline_status)
render json: { html: view_to_html_string('projects/merge_requests/_commits') }
end
def pipelines
- @pipelines = @merge_request.all_pipelines
+ @pipelines = @merge_request.all_pipelines.page(params[:page]).per(30)
Gitlab::PollingInterval.set_header(response, interval: 10_000)
render json: {
pipelines: PipelineSerializer
.new(project: @project, current_user: @current_user)
+ .with_pagination(request, response)
.represent(@pipelines),
count: {
all: @pipelines.count
@@ -169,7 +166,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def merge
- return access_denied! unless @merge_request.can_be_merged_by?(current_user)
+ access_check_result = merge_access_check
+
+ return access_check_result if access_check_result
status = merge!
@@ -202,43 +201,13 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def ci_environments_status
- environments =
- begin
- @merge_request.environments_for(current_user).map do |environment|
- project = environment.project
- deployment = environment.first_deployment_for(@merge_request.diff_head_sha)
-
- stop_url =
- if can?(current_user, :stop_environment, environment)
- stop_project_environment_path(project, environment)
- end
-
- metrics_url =
- if can?(current_user, :read_environment, environment) && environment.has_metrics?
- metrics_project_environment_deployment_path(environment.project, environment, deployment)
- end
-
- metrics_monitoring_url =
- if can?(current_user, :read_environment, environment)
- environment_metrics_path(environment)
- end
-
- {
- id: environment.id,
- name: environment.name,
- url: project_environment_path(project, environment),
- metrics_url: metrics_url,
- metrics_monitoring_url: metrics_monitoring_url,
- stop_url: stop_url,
- external_url: environment.external_url,
- external_url_formatted: environment.formatted_external_url,
- deployed_at: deployment.try(:created_at),
- deployed_at_formatted: deployment.try(:formatted_deployment_time)
- }
- end.compact
- end
+ environments = if ci_environments_status_on_merge_result?
+ EnvironmentStatus.after_merge_request(@merge_request, current_user)
+ else
+ EnvironmentStatus.for_merge_request(@merge_request, current_user)
+ end
- render json: environments
+ render json: EnvironmentStatusSerializer.new(current_user: current_user).represent(environments)
end
def rebase
@@ -274,6 +243,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
private
+ def ci_environments_status_on_merge_result?
+ params[:environment_target] == 'merge_commit'
+ end
+
def target_branch_missing?
@merge_request.has_no_commits? && !@merge_request.target_branch_exists?
end
@@ -289,6 +262,12 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
return :failed
end
+ merge_service = ::MergeRequests::MergeService.new(@project, current_user, merge_params)
+
+ unless merge_service.hooks_validation_pass?(@merge_request)
+ return :hook_validation_error
+ end
+
return :sha_mismatch if params[:sha] != @merge_request.diff_head_sha
@merge_request.update(merge_error: nil, squash: merge_params.fetch(:squash, false))
@@ -330,6 +309,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@source_project = @merge_request.source_project
@target_project = @merge_request.target_project
@target_branches = @merge_request.target_project.repository.branch_names
+ @noteable = @merge_request
+
+ # FIXME: We have to assign a presenter to another instance variable
+ # due to class_name checks being made with issuable classes
+ @mr_presenter = @merge_request.present(current_user: current_user)
end
def finder_type
@@ -346,6 +330,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
access_denied! unless access_check
end
+ def merge_access_check
+ access_denied! unless @merge_request.can_be_merged_by?(current_user)
+ end
+
def whitelist_query_limiting
# Also see https://gitlab.com/gitlab-org/gitlab-ce/issues/42441
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42438')
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index b9b3dcd5a85..20998c97730 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::MilestonesController < Projects::ApplicationController
include Gitlab::Utils::StrongMemoize
include MilestoneActions
@@ -91,12 +93,7 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def flash_notice_for(milestone, group)
- notice = ''.html_safe
- notice << milestone.title
- notice << ' promoted to '
- notice << view_context.link_to('<u>group milestone</u>'.html_safe, group_milestone_path(group, milestone.iid))
- notice << '.'
- notice
+ ''.html_safe + "#{milestone.title} promoted to " + view_context.link_to('<u>group milestone</u>'.html_safe, group_milestone_path(group, milestone.iid)) + '.'
end
def destroy
@@ -118,9 +115,11 @@ class Projects::MilestonesController < Projects::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def milestone
@milestone ||= @project.milestones.find_by!(iid: params[:id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def authorize_admin_milestone!
return render_404 unless can?(current_user, :admin_milestone, @project)
diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb
index 3739608e4c0..53176978416 100644
--- a/app/controllers/projects/mirrors_controller.rb
+++ b/app/controllers/projects/mirrors_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::MirrorsController < Projects::ApplicationController
include RepositorySettingsRedirect
@@ -42,6 +44,22 @@ class Projects::MirrorsController < Projects::ApplicationController
redirect_to_repository_settings(project, anchor: 'js-push-remote-settings')
end
+ def ssh_host_keys
+ lookup = SshHostKey.new(project: project, url: params[:ssh_url], compare_host_keys: params[:compare_host_keys])
+
+ if lookup.error.present?
+ # Failed to read keys
+ render json: { message: lookup.error }, status: :bad_request
+ elsif lookup.known_hosts.nil?
+ # Still working, come back later
+ render body: nil, status: :no_content
+ else
+ render json: lookup
+ end
+ rescue ArgumentError => err
+ render json: { message: err.message }, status: :bad_request
+ end
+
private
def remote_mirror
diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb
index 35fec229db7..ad2466a8588 100644
--- a/app/controllers/projects/network_controller.rb
+++ b/app/controllers/projects/network_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::NetworkController < Projects::ApplicationController
include ExtractsPath
include ApplicationHelper
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 21e2145b73b..3152a38fd8e 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::NotesController < Projects::ApplicationController
include RendersNotes
include NotesActions
@@ -66,7 +68,7 @@ class Projects::NotesController < Projects::ApplicationController
alias_method :awardable, :note
def finder_params
- params.merge(last_fetched_at: last_fetched_at)
+ params.merge(last_fetched_at: last_fetched_at, notes_filter: notes_filter)
end
def authorize_admin_note!
diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb
index cae6e2c40b8..c1ad6707c97 100644
--- a/app/controllers/projects/pages_controller.rb
+++ b/app/controllers/projects/pages_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::PagesController < Projects::ApplicationController
layout 'project_settings'
@@ -5,13 +7,15 @@ class Projects::PagesController < Projects::ApplicationController
before_action :authorize_read_pages!, only: [:show]
before_action :authorize_update_pages!, except: [:show]
+ # rubocop: disable CodeReuse/ActiveRecord
def show
@domains = @project.pages_domains.order(:domain)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def destroy
project.remove_pages
- project.pages_domains.destroy_all
+ project.pages_domains.destroy_all # rubocop: disable DestroyAll
respond_to do |format|
format.html do
diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb
index 4856be61e88..439ec9b1731 100644
--- a/app/controllers/projects/pages_domains_controller.rb
+++ b/app/controllers/projects/pages_domains_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::PagesDomainsController < Projects::ApplicationController
layout 'project_settings'
@@ -70,7 +72,9 @@ class Projects::PagesDomainsController < Projects::ApplicationController
params.require(:pages_domain).permit(:key, :certificate)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def domain
@domain ||= @project.pages_domains.find_by!(domain: params[:id].to_s)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb
index aeda7b3edf5..acf56f0eb6a 100644
--- a/app/controllers/projects/pipeline_schedules_controller.rb
+++ b/app/controllers/projects/pipeline_schedules_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::PipelineSchedulesController < Projects::ApplicationController
before_action :schedule, except: [:index, :new, :create]
@@ -8,12 +10,14 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
before_action :authorize_update_pipeline_schedule!, except: [:index, :new, :create, :play]
before_action :authorize_admin_pipeline_schedule!, only: [:destroy]
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@scope = params[:scope]
@all_schedules = PipelineSchedulesFinder.new(@project).execute
@schedules = PipelineSchedulesFinder.new(@project).execute(scope: params[:scope])
.includes(:last_pipeline)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def new
@schedule = project.pipeline_schedules.new
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index b5db646bf57..53b29d4146e 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::PipelinesController < Projects::ApplicationController
before_action :whitelist_query_limiting, only: [:create, :retry]
before_action :pipeline, except: [:index, :new, :create, :charts]
@@ -96,7 +98,7 @@ class Projects::PipelinesController < Projects::ApplicationController
render json: StageSerializer
.new(project: @project, current_user: @current_user)
- .represent(@stage, details: true)
+ .represent(@stage, details: true, retried: params[:retried])
end
# TODO: This endpoint is used by mini-pipeline-graph
@@ -159,6 +161,7 @@ class Projects::PipelinesController < Projects::ApplicationController
params.require(:pipeline).permit(:ref, variables_attributes: %i[key secret_value])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def pipeline
@pipeline ||= project
.pipelines
@@ -166,6 +169,7 @@ class Projects::PipelinesController < Projects::ApplicationController
.find_by!(id: params[:id])
.present(current_user: current_user)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def whitelist_query_limiting
# Also see https://gitlab.com/gitlab-org/gitlab-ce/issues/42343
diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb
index 73c613b26f3..192e6d38f36 100644
--- a/app/controllers/projects/pipelines_settings_controller.rb
+++ b/app/controllers/projects/pipelines_settings_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::PipelinesSettingsController < Projects::ApplicationController
before_action :authorize_admin_pipeline!
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index cfa5e72af64..8938cfbad54 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ProjectMembersController < Projects::ApplicationController
include MembershipActions
include MembersPresentation
@@ -6,6 +8,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
# Authorize
before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access]
+ # rubocop: disable CodeReuse/ActiveRecord
def index
@sort = params[:sort].presence || sort_value_name
@group_links = @project.project_group_links
@@ -25,6 +28,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
@requesters = present_members(AccessRequestsFinder.new(@project).execute(current_user))
@project_member = @project.project_members.new
end
+ # rubocop: enable CodeReuse/ActiveRecord
def import
@projects = current_user.authorized_projects.order_id_desc
diff --git a/app/controllers/projects/prometheus/metrics_controller.rb b/app/controllers/projects/prometheus/metrics_controller.rb
index c6b6243b553..3a9f9aab4a5 100644
--- a/app/controllers/projects/prometheus/metrics_controller.rb
+++ b/app/controllers/projects/prometheus/metrics_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module Prometheus
class MetricsController < Projects::ApplicationController
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index 64954ac9a42..a860be83e95 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ProtectedBranchesController < Projects::ProtectedRefsController
protected
diff --git a/app/controllers/projects/protected_refs_controller.rb b/app/controllers/projects/protected_refs_controller.rb
index cc62ce2f11b..3a3a29ddd0d 100644
--- a/app/controllers/projects/protected_refs_controller.rb
+++ b/app/controllers/projects/protected_refs_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ProtectedRefsController < Projects::ApplicationController
include RepositorySettingsRedirect
diff --git a/app/controllers/projects/protected_tags_controller.rb b/app/controllers/projects/protected_tags_controller.rb
index 198c938ff35..01cedba95ac 100644
--- a/app/controllers/projects/protected_tags_controller.rb
+++ b/app/controllers/projects/protected_tags_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ProtectedTagsController < Projects::ProtectedRefsController
protected
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 1cba0011304..42ae5b0ef3c 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -1,8 +1,9 @@
+# frozen_string_literal: true
+
# Controller for viewing a file's raw
class Projects::RawController < Projects::ApplicationController
include ExtractsPath
- include BlobHelper
- include SendFileUpload
+ include SendsBlob
before_action :require_non_empty_project
before_action :assign_ref_vars
@@ -10,39 +11,7 @@ class Projects::RawController < Projects::ApplicationController
def show
@blob = @repository.blob_at(@commit.id, @path)
- if @blob
- headers['X-Content-Type-Options'] = 'nosniff'
-
- return if cached_blob?
-
- if @blob.stored_externally?
- send_lfs_object
- else
- send_git_blob @repository, @blob, inline: (params[:inline] != 'false')
- end
- else
- render_404
- end
- end
-
- private
-
- def send_lfs_object
- lfs_object = find_lfs_object
-
- if lfs_object && lfs_object.project_allowed_access?(@project)
- send_upload(lfs_object.file, attachment: @blob.name)
- else
- render_404
- end
- end
- def find_lfs_object
- lfs_object = LfsObject.find_by_oid(@blob.lfs_oid)
- if lfs_object && lfs_object.file.exists?
- lfs_object
- else
- nil
- end
+ send_blob(@repository, @blob, inline: (params[:inline] != 'false'))
end
end
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 48a09e1ddb8..b97fbe19bbf 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::RefsController < Projects::ApplicationController
include ExtractsPath
include TreeHelper
@@ -36,54 +38,47 @@ class Projects::RefsController < Projects::ApplicationController
end
def logs_tree
- @offset = if params[:offset].present?
- params[:offset].to_i
- else
- 0
- end
-
- @limit = 25
-
- @path = params[:path]
-
- contents = []
- contents.push(*tree.trees)
- contents.push(*tree.blobs)
- contents.push(*tree.submodules)
+ summary = ::Gitlab::TreeSummary.new(
+ @commit,
+ @project,
+ path: @path,
+ offset: params[:offset],
+ limit: 25
+ )
- # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37433
- @logs = Gitlab::GitalyClient.allow_n_plus_1_calls do
- contents[@offset, @limit].to_a.map do |content|
- file = @path ? File.join(@path, content.name) : content.name
- last_commit = @repo.last_commit_for_path(@commit.id, file)
- commit_path = project_commit_path(@project, last_commit) if last_commit
- {
- file_name: content.name,
- commit: last_commit,
- type: content.type,
- commit_path: commit_path
- }
- end
- end
-
- offset = (@offset + @limit)
- if contents.size > offset
- @more_log_url = logs_file_project_ref_path(@project, @ref, @path || '', offset: offset)
- end
+ @logs, commits = summary.summarize
+ @more_log_url = more_url(summary.next_offset) if summary.more?
respond_to do |format|
format.html { render_404 }
format.json do
- response.headers["More-Logs-Url"] = @more_log_url
-
+ response.headers["More-Logs-Url"] = @more_log_url if summary.more?
render json: @logs
end
- format.js
+
+ # The commit titles must be rendered and redacted before being shown.
+ # Doing it here allows us to apply performance optimizations that avoid
+ # N+1 problems
+ format.js do
+ prerender_commit_full_titles!(commits)
+ end
end
end
private
+ def more_url(offset)
+ logs_file_project_ref_path(@project, @ref, @path, offset: offset)
+ end
+
+ def prerender_commit_full_titles!(commits)
+ # Preload commit authors as they are used in rendering
+ commits.each(&:lazy_author)
+
+ renderer = Banzai::ObjectRenderer.new(user: current_user, default_project: @project)
+ renderer.render(commits, :full_title)
+ end
+
def validate_ref_id
return not_found! if params[:id].present? && params[:id] !~ Gitlab::PathRegex.git_reference_regex
end
diff --git a/app/controllers/projects/registry/application_controller.rb b/app/controllers/projects/registry/application_controller.rb
index a56f9c58726..2f891d78c91 100644
--- a/app/controllers/projects/registry/application_controller.rb
+++ b/app/controllers/projects/registry/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module Registry
class ApplicationController < Projects::ApplicationController
diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb
index 32c0fc6d14a..6d60117c37d 100644
--- a/app/controllers/projects/registry/repositories_controller.rb
+++ b/app/controllers/projects/registry/repositories_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module Registry
class RepositoriesController < ::Projects::Registry::ApplicationController
@@ -18,14 +20,10 @@ module Projects
end
def destroy
- if image.destroy
- respond_to do |format|
- format.json { head :no_content }
- end
- else
- respond_to do |format|
- format.json { head :bad_request }
- end
+ DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id)
+
+ respond_to do |format|
+ format.json { head :no_content }
end
end
@@ -41,10 +39,10 @@ module Projects
# Needed to maintain a backwards compatibility.
#
def ensure_root_container_repository!
- ContainerRegistry::Path.new(@project.full_path).tap do |path|
+ ::ContainerRegistry::Path.new(@project.full_path).tap do |path|
break if path.has_repository?
- ContainerRepository.build_from_path(path).tap do |repository|
+ ::ContainerRepository.build_from_path(path).tap do |repository|
repository.save! if repository.has_tags?
end
end
diff --git a/app/controllers/projects/registry/tags_controller.rb b/app/controllers/projects/registry/tags_controller.rb
index e602aa3f393..567d750caae 100644
--- a/app/controllers/projects/registry/tags_controller.rb
+++ b/app/controllers/projects/registry/tags_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module Registry
class TagsController < ::Projects::Registry::ApplicationController
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index 19e09b3af6f..55827075896 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ReleasesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
@@ -28,9 +30,11 @@ class Projects::ReleasesController < Projects::ApplicationController
@tag ||= @repository.find_tag(params[:tag_id])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def release
@release ||= @project.releases.find_or_initialize_by(tag: @tag.name)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def release_params
params.require(:release).permit(:description)
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index ecb2ece7532..4eeaeb860ee 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::RepositoriesController < Projects::ApplicationController
include ExtractsPath
diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb
index c098c82081e..cbeb32fd610 100644
--- a/app/controllers/projects/runner_projects_controller.rb
+++ b/app/controllers/projects/runner_projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::RunnerProjectsController < Projects::ApplicationController
before_action :authorize_admin_build!
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index d118cec977c..91f40b90aa8 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::RunnersController < Projects::ApplicationController
before_action :authorize_admin_build!
before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index d55046047ae..f1c9d0d0f77 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::ServicesController < Projects::ApplicationController
include ServiceParams
diff --git a/app/controllers/projects/settings/badges_controller.rb b/app/controllers/projects/settings/badges_controller.rb
deleted file mode 100644
index 7887bee49c5..00000000000
--- a/app/controllers/projects/settings/badges_controller.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module Projects
- module Settings
- class BadgesController < Projects::ApplicationController
- include API::Helpers::RelatedResourcesHelpers
-
- before_action :authorize_admin_project!
-
- def index
- @badge_api_endpoint = expose_url(api_v4_projects_badges_path(id: @project.id))
- end
- end
- end
-end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 322ec096ffb..75e590f3f33 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module Settings
class CiCdController < Projects::ApplicationController
@@ -34,6 +36,13 @@ module Projects
end
end
+ def reset_registration_token
+ @project.reset_runners_token!
+
+ flash[:notice] = 'New runners registration token has been generated!'
+ redirect_to namespace_project_settings_ci_cd_path
+ end
+
private
def update_params
@@ -59,7 +68,7 @@ module Projects
def define_variables
define_runners_variables
- define_secret_variables
+ define_ci_variables
define_triggers_variables
define_badges_variables
define_auto_devops_variables
@@ -81,7 +90,7 @@ module Projects
@group_runners = ::Ci::Runner.belonging_to_parent_group_of_project(@project.id)
end
- def define_secret_variables
+ def define_ci_variables
@variable = ::Ci::Variable.new(project: project)
.present(current_user: current_user)
@variables = project.variables.order_key_asc
diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb
index d9fecfecc40..388fcb32c35 100644
--- a/app/controllers/projects/settings/integrations_controller.rb
+++ b/app/controllers/projects/settings/integrations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module Settings
class IntegrationsController < Projects::ApplicationController
diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb
index 4697af4f26a..1d76c90d4eb 100644
--- a/app/controllers/projects/settings/repository_controller.rb
+++ b/app/controllers/projects/settings/repository_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module Settings
class RepositoryController < Projects::ApplicationController
@@ -31,6 +33,7 @@ module Projects
render 'show'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def define_protected_refs
@protected_branches = @project.protected_branches.order(:name).page(params[:page])
@protected_tags = @project.protected_tags.order(:name).page(params[:page])
@@ -42,6 +45,7 @@ module Projects
load_gon_index
end
+ # rubocop: enable CodeReuse/ActiveRecord
def remote_mirror
@remote_mirror = project.remote_mirrors.first_or_initialize
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 7c03d8ce827..a44acb12bdf 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::SnippetsController < Projects::ApplicationController
include RendersNotes
include ToggleAwardEmoji
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index b17753222a0..c8442ff3592 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::TagsController < Projects::ApplicationController
include SortingHelper
@@ -7,6 +9,7 @@ class Projects::TagsController < Projects::ApplicationController
before_action :authorize_push_code!, only: [:new, :create]
before_action :authorize_admin_project!, only: [:destroy]
+ # rubocop: disable CodeReuse/ActiveRecord
def index
params[:sort] = params[:sort].presence || sort_value_recently_updated
@@ -17,8 +20,15 @@ class Projects::TagsController < Projects::ApplicationController
tag_names = @tags.map(&:name)
@tags_pipelines = @project.pipelines.latest_successful_for_refs(tag_names)
@releases = project.releases.where(tag: tag_names)
+
+ respond_to do |format|
+ format.html
+ format.atom { render layout: 'xml.atom' }
+ end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def show
@tag = @repository.find_tag(params[:id])
@@ -27,6 +37,7 @@ class Projects::TagsController < Projects::ApplicationController
@release = @project.releases.find_or_initialize_by(tag: @tag.name)
@commit = @repository.commit(@tag.dereferenced_target)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def create
result = Tags::CreateService.new(@project, current_user)
diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb
index 52d6fb82093..7ceea4e5b96 100644
--- a/app/controllers/projects/templates_controller.rb
+++ b/app/controllers/projects/templates_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::TemplatesController < Projects::ApplicationController
before_action :authenticate_user!, :get_template_class
diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb
index 93fb9da6510..0b11ee9edc0 100644
--- a/app/controllers/projects/todos_controller.rb
+++ b/app/controllers/projects/todos_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::TodosController < Projects::ApplicationController
include Gitlab::Utils::StrongMemoize
include TodosActions
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index ee9b5458282..3fe300dcfc0 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController
include ExtractsPath
diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb
index cb12b707087..f5fdfb8accc 100644
--- a/app/controllers/projects/triggers_controller.rb
+++ b/app/controllers/projects/triggers_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::TriggersController < Projects::ApplicationController
before_action :authorize_admin_build!
before_action :authorize_manage_trigger!, except: [:index, :create]
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
index 7a85046164c..4ffcc2ac805 100644
--- a/app/controllers/projects/uploads_controller.rb
+++ b/app/controllers/projects/uploads_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::UploadsController < Projects::ApplicationController
include UploadsActions
include WorkhorseRequest
diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb
index bf09ea7e4d8..bb658bfcc19 100644
--- a/app/controllers/projects/variables_controller.rb
+++ b/app/controllers/projects/variables_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Projects::VariablesController < Projects::ApplicationController
before_action :authorize_admin_build!
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index da7aeb26a75..88dd111132b 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
class Projects::WikisController < Projects::ApplicationController
include PreviewMarkdown
+ include SendsBlob
include Gitlab::Utils::StrongMemoize
before_action :authorize_read_wiki!
@@ -24,16 +27,8 @@ class Projects::WikisController < Projects::ApplicationController
set_encoding_error unless valid_encoding?
render 'show'
- elsif file = @project_wiki.find_file(params[:id], params[:version_id])
- response.headers['Content-Security-Policy'] = "default-src 'none'"
- response.headers['X-Content-Security-Policy'] = "default-src 'none'"
-
- send_data(
- file.raw_data,
- type: file.mime_type,
- disposition: 'inline',
- filename: file.name
- )
+ elsif file_blob
+ send_blob(@project_wiki.repository, file_blob)
elsif can?(current_user, :create_wiki, @project) && view_param == 'create'
@page = build_page(title: params[:id])
@@ -162,4 +157,14 @@ class Projects::WikisController < Projects::ApplicationController
def set_encoding_error
flash.now[:notice] = "The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository."
end
+
+ def file_blob
+ strong_memoize(:file_blob) do
+ commit = @project_wiki.repository.commit(@project_wiki.default_branch)
+
+ next unless commit
+
+ @project_wiki.repository.blob_at(commit.id, params[:id])
+ end
+ end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index e9ae8c13142..7f4a9f5151b 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
class ProjectsController < Projects::ApplicationController
+ include API::Helpers::RelatedResourcesHelpers
include IssuableCollections
include ExtractsPath
include PreviewMarkdown
@@ -13,6 +16,7 @@ class ProjectsController < Projects::ApplicationController
before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
before_action :lfs_blob_ids, only: [:show], if: [:repo_exists?, :project_view_files?]
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
+ before_action :present_project, only: [:edit]
# Authorize
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
@@ -24,14 +28,17 @@ class ProjectsController < Projects::ApplicationController
redirect_to(current_user ? root_path : explore_root_path)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def new
namespace = Namespace.find_by(id: params[:namespace_id]) if params[:namespace_id]
return access_denied! if namespace && !can?(current_user, :create_projects, namespace)
@project = Project.new(namespace_id: namespace&.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def edit
+ @badge_api_endpoint = expose_url(api_v4_projects_badges_path(id: @project.id))
render 'edit'
end
@@ -73,6 +80,7 @@ class ProjectsController < Projects::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def transfer
return access_denied! unless can?(current_user, :change_namespace, @project)
@@ -83,6 +91,7 @@ class ProjectsController < Projects::ApplicationController
flash[:alert] = @project.errors[:new_namespace].first
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def remove_fork
return access_denied! unless can?(current_user, :remove_fork_project, @project)
@@ -189,10 +198,8 @@ class ProjectsController < Projects::ApplicationController
end
def download_export
- if export_project_object_storage?
- send_upload(@project.import_export_upload.export_file)
- elsif export_project_path
- send_file export_project_path, disposition: 'attachment'
+ if @project.export_file_exists?
+ send_upload(@project.export_file, attachment: @project.export_file.filename)
else
redirect_to(
edit_project_path(@project, anchor: 'js-export-project'),
@@ -231,6 +238,7 @@ class ProjectsController < Projects::ApplicationController
}
end
+ # rubocop: disable CodeReuse/ActiveRecord
def refs
find_refs = params['find']
@@ -265,9 +273,10 @@ class ProjectsController < Projects::ApplicationController
render json: options.to_json
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Render project landing depending of which features are available
- # So if page is not availble in the list it renders the next page
+ # So if page is not available in the list it renders the next page
#
# pages list order: repository readme, wiki home, issues list, customize workflow
def render_landing_page
@@ -303,6 +312,7 @@ class ProjectsController < Projects::ApplicationController
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def load_events
projects = Project.where(id: @project.id)
@@ -312,6 +322,7 @@ class ProjectsController < Projects::ApplicationController
Events::RenderService.new(current_user).execute(@events, atom_request: request.format.atom?)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def project_params
params.require(:project)
@@ -355,6 +366,7 @@ class ProjectsController < Projects::ApplicationController
repository_access_level
snippets_access_level
wiki_access_level
+ pages_access_level
]
]
end
@@ -424,11 +436,7 @@ class ProjectsController < Projects::ApplicationController
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42440')
end
- def export_project_path
- @export_project_path ||= @project.export_project_path
- end
-
- def export_project_object_storage?
- @project.export_project_object_exists?
+ def present_project
+ @project = @project.present(current_user: current_user)
end
end
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index e6d6965036e..8b8d87524a8 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RegistrationsController < Devise::RegistrationsController
include Recaptcha::Verify
include AcceptsPendingInvitations
diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb
index 651b82f04f4..7b6657e1196 100644
--- a/app/controllers/root_controller.rb
+++ b/app/controllers/root_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# RootController
#
# This controller exists solely to handle requests to `root_url`. When a user is
@@ -43,9 +45,9 @@ class RootController < Dashboard::ProjectsController
when 'todos'
redirect_to(dashboard_todos_path)
when 'issues'
- redirect_to(issues_dashboard_path(assignee_id: current_user.id))
+ redirect_to(issues_dashboard_path(assignee_username: current_user.username))
when 'merge_requests'
- redirect_to(merge_requests_dashboard_path(assignee_id: current_user.id))
+ redirect_to(merge_requests_dashboard_path(assignee_username: current_user.username))
end
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 983f888b8ec..1b22907c10f 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SearchController < ApplicationController
include ControllerWithCrossProjectAccessCheck
include SearchHelper
@@ -31,6 +33,7 @@ class SearchController < ApplicationController
check_single_commit_result
end
+ # rubocop: disable CodeReuse/ActiveRecord
def autocomplete
term = params[:term]
@@ -43,6 +46,7 @@ class SearchController < ApplicationController
render json: search_autocomplete_opts(term).to_json
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/controllers/sent_notifications_controller.rb b/app/controllers/sent_notifications_controller.rb
index 93a71103a09..2b76921ebd8 100644
--- a/app/controllers/sent_notifications_controller.rb
+++ b/app/controllers/sent_notifications_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SentNotificationsController < ApplicationController
skip_before_action :authenticate_user!
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index ab8e2e35b98..643eb75c83c 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SessionsController < Devise::SessionsController
include InternalRedirect
include AuthenticatesWithTwoFactor
@@ -107,6 +109,7 @@ class SessionsController < Devise::SessionsController
# Handle an "initial setup" state, where there's only one user, it's an admin,
# and they require a password change.
+ # rubocop: disable CodeReuse/ActiveRecord
def check_initial_setup
return unless User.limit(2).count == 1 # Count as much 2 to know if we have exactly one
@@ -121,6 +124,7 @@ class SessionsController < Devise::SessionsController
redirect_to edit_user_password_path(reset_password_token: @token),
notice: "Please create a password for your new account."
end
+ # rubocop: enable CodeReuse/ActiveRecord
def user_params
params.require(:user).permit(:login, :password, :remember_me, :otp_attempt, :device_response)
diff --git a/app/controllers/sherlock/application_controller.rb b/app/controllers/sherlock/application_controller.rb
index 6bdd3568a78..c048254d348 100644
--- a/app/controllers/sherlock/application_controller.rb
+++ b/app/controllers/sherlock/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Sherlock
class ApplicationController < ::ApplicationController
before_action :find_transaction
diff --git a/app/controllers/sherlock/file_samples_controller.rb b/app/controllers/sherlock/file_samples_controller.rb
index 0c3bc100106..900446bb75a 100644
--- a/app/controllers/sherlock/file_samples_controller.rb
+++ b/app/controllers/sherlock/file_samples_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Sherlock
class FileSamplesController < Sherlock::ApplicationController
def show
diff --git a/app/controllers/sherlock/queries_controller.rb b/app/controllers/sherlock/queries_controller.rb
index 63b26aab1a4..49a25c682b5 100644
--- a/app/controllers/sherlock/queries_controller.rb
+++ b/app/controllers/sherlock/queries_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Sherlock
class QueriesController < Sherlock::ApplicationController
def show
diff --git a/app/controllers/sherlock/transactions_controller.rb b/app/controllers/sherlock/transactions_controller.rb
index ae4953c3259..46e382e594e 100644
--- a/app/controllers/sherlock/transactions_controller.rb
+++ b/app/controllers/sherlock/transactions_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Sherlock
class TransactionsController < Sherlock::ApplicationController
def index
diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb
index 217da89a1fd..091bcb1253d 100644
--- a/app/controllers/snippets/notes_controller.rb
+++ b/app/controllers/snippets/notes_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Snippets::NotesController < ApplicationController
include NotesActions
include ToggleAwardEmoji
@@ -17,9 +19,11 @@ class Snippets::NotesController < ApplicationController
nil
end
+ # rubocop: disable CodeReuse/ActiveRecord
def snippet
PersonalSnippet.find_by(id: params[:snippet_id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
alias_method :noteable, :snippet
def note_params
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index dcf18c1f751..dd9bf17cf0c 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SnippetsController < ApplicationController
include RendersNotes
include ToggleAwardEmoji
@@ -26,9 +28,7 @@ class SnippetsController < ApplicationController
def index
if params[:username].present?
- @user = User.find_by(username: params[:username])
-
- return render_404 unless @user
+ @user = UserFinder.new(params[:username]).find_by_username!
@snippets = SnippetsFinder.new(current_user, author: @user, scope: params[:scope])
.execute.page(params[:page])
@@ -94,9 +94,11 @@ class SnippetsController < ApplicationController
protected
+ # rubocop: disable CodeReuse/ActiveRecord
def snippet
@snippet ||= PersonalSnippet.inc_relations_for_view.find_by(id: params[:id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
alias_method :awardable, :snippet
alias_method :spammable, :snippet
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index 3d227b0a955..fa5d84633b5 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UploadsController < ApplicationController
include UploadsActions
diff --git a/app/controllers/user_callouts_controller.rb b/app/controllers/user_callouts_controller.rb
index 18cde4a7b1a..ebf1dd8ca02 100644
--- a/app/controllers/user_callouts_controller.rb
+++ b/app/controllers/user_callouts_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserCalloutsController < ApplicationController
def create
if ensure_callout.persisted?
@@ -13,9 +15,11 @@ class UserCalloutsController < ApplicationController
private
+ # rubocop: disable CodeReuse/ActiveRecord
def ensure_callout
current_user.callouts.find_or_create_by(feature_name: UserCallout.feature_names[feature_name])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def feature_name
params.require(:feature_name)
diff --git a/app/controllers/users/terms_controller.rb b/app/controllers/users/terms_controller.rb
index 1b1560a2a00..3c16d934b4d 100644
--- a/app/controllers/users/terms_controller.rb
+++ b/app/controllers/users/terms_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
class TermsController < ApplicationController
include InternalRedirect
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 2f65f4a7403..5b70c69d7f4 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UsersController < ApplicationController
include RoutableActions
include RendersMemberAccess
@@ -27,11 +29,17 @@ class UsersController < ApplicationController
format.json do
load_events
- pager_json("events/_events", @events.count)
+ pager_json("events/_events", @events.count, events: @events)
end
end
end
+ def activity
+ respond_to do |format|
+ format.html { render 'show' }
+ end
+ end
+
def groups
load_groups
@@ -48,12 +56,12 @@ class UsersController < ApplicationController
def projects
load_projects
+ skip_pagination = Gitlab::Utils.to_boolean(params[:skip_pagination])
+
respond_to do |format|
format.html { render 'show' }
format.json do
- render json: {
- html: view_to_html_string("shared/projects/_list", projects: @projects)
- }
+ pager_json("shared/projects/_list", @projects.count, projects: @projects, skip_pagination: skip_pagination)
end
end
end
@@ -123,6 +131,7 @@ class UsersController < ApplicationController
@projects =
PersonalProjectsFinder.new(user).execute(current_user)
.page(params[:page])
+ .per(params[:limit])
prepare_projects_for_rendering(@projects)
end
diff --git a/app/finders/access_requests_finder.rb b/app/finders/access_requests_finder.rb
index b6ee49df99b..2cc8a978877 100644
--- a/app/finders/access_requests_finder.rb
+++ b/app/finders/access_requests_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AccessRequestsFinder
attr_accessor :source
diff --git a/app/finders/admin/projects_finder.rb b/app/finders/admin/projects_finder.rb
index 543bf1a1415..e2b9b0b44c1 100644
--- a/app/finders/admin/projects_finder.rb
+++ b/app/finders/admin/projects_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::ProjectsFinder
attr_reader :params, :current_user
@@ -6,6 +8,7 @@ class Admin::ProjectsFinder
@current_user = current_user
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
items = Project.without_deleted.with_statistics.with_route
items = by_namespace_id(items)
@@ -19,6 +22,7 @@ class Admin::ProjectsFinder
items = items.includes(namespace: [:owner, :route])
sort(items).page(params[:page])
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -26,9 +30,11 @@ class Admin::ProjectsFinder
params[:namespace_id].present? ? items.in_namespace(params[:namespace_id]) : items
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_visibilty_level(items)
params[:visibility_level].present? ? items.where(visibility_level: params[:visibility_level]) : items
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_with_push(items)
params[:with_push].present? ? items.with_push : items
@@ -38,9 +44,11 @@ class Admin::ProjectsFinder
params[:abandoned].present? ? items.abandoned : items
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_last_repository_check_failed(items)
params[:last_repository_check_failed].present? ? items.where(last_repository_check_failed: true) : items
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_archived(items)
if params[:archived] == 'only'
diff --git a/app/finders/admin/runners_finder.rb b/app/finders/admin/runners_finder.rb
new file mode 100644
index 00000000000..fbb1cfc5c66
--- /dev/null
+++ b/app/finders/admin/runners_finder.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+class Admin::RunnersFinder < UnionFinder
+ NUMBER_OF_RUNNERS_PER_PAGE = 30
+
+ def initialize(params:)
+ @params = params
+ end
+
+ def execute
+ search!
+ filter_by_status!
+ filter_by_runner_type!
+ sort!
+ paginate!
+
+ @runners
+ end
+
+ def sort_key
+ if @params[:sort] == 'contacted_asc'
+ 'contacted_asc'
+ else
+ 'created_date'
+ end
+ end
+
+ private
+
+ def search!
+ @runners =
+ if @params[:search].present?
+ Ci::Runner.search(@params[:search])
+ else
+ Ci::Runner.all
+ end
+ end
+
+ def filter_by_status!
+ filter_by!(:status_status, Ci::Runner::AVAILABLE_STATUSES)
+ end
+
+ def filter_by_runner_type!
+ filter_by!(:type_type, Ci::Runner::AVAILABLE_TYPES)
+ end
+
+ def sort!
+ @runners = @runners.order_by(sort_key)
+ end
+
+ def paginate!
+ @runners = @runners.page(@params[:page]).per(NUMBER_OF_RUNNERS_PER_PAGE)
+ end
+
+ def filter_by!(scope_name, available_scopes)
+ scope = @params[scope_name]
+
+ if scope.present? && available_scopes.include?(scope)
+ @runners = @runners.public_send(scope) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+end
diff --git a/app/finders/applications_finder.rb b/app/finders/applications_finder.rb
new file mode 100644
index 00000000000..3ded90f3fd5
--- /dev/null
+++ b/app/finders/applications_finder.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class ApplicationsFinder
+ attr_reader :params
+
+ def initialize(params = {})
+ @params = params
+ end
+
+ def execute
+ applications = Doorkeeper::Application.where(owner_id: nil) # rubocop: disable CodeReuse/ActiveRecord
+ by_id(applications)
+ end
+
+ private
+
+ def by_id(applications)
+ return applications unless params[:id]
+
+ Doorkeeper::Application.find_by(id: params[:id]) # rubocop: disable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/finders/autocomplete/group_finder.rb b/app/finders/autocomplete/group_finder.rb
new file mode 100644
index 00000000000..dd97ac4c817
--- /dev/null
+++ b/app/finders/autocomplete/group_finder.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Autocomplete
+ # Finder for retrieving a group to use for autocomplete data sources.
+ class GroupFinder
+ attr_reader :current_user, :project, :group_id
+
+ # current_user - The currently logged in user, if any.
+ # project - The Project (if any) to use for the autocomplete data sources.
+ # params - A Hash containing parameters to use for finding the project.
+ #
+ # The following parameters are supported:
+ #
+ # * group_id: The ID of the group to find.
+ def initialize(current_user = nil, project = nil, params = {})
+ @current_user = current_user
+ @project = project
+ @group_id = params[:group_id]
+ end
+
+ # Attempts to find a Group based on the current group ID.
+ def execute
+ return unless project.blank? && group_id.present?
+
+ group = Group.find(group_id)
+
+ # This removes the need for using `return render_404` and similar patterns
+ # in controllers that use this finder.
+ unless Ability.allowed?(current_user, :read_group, group)
+ raise ActiveRecord::RecordNotFound
+ .new("Could not find a Group with ID #{group_id}")
+ end
+
+ group
+ end
+ end
+end
diff --git a/app/finders/autocomplete/move_to_project_finder.rb b/app/finders/autocomplete/move_to_project_finder.rb
new file mode 100644
index 00000000000..edaf74c5f92
--- /dev/null
+++ b/app/finders/autocomplete/move_to_project_finder.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Autocomplete
+ # Finder that retrieves a list of projects that an issue can be moved to.
+ class MoveToProjectFinder
+ attr_reader :current_user, :search, :project_id, :offset_id
+
+ # current_user - The User object of the user that wants to view the list of
+ # projects.
+ #
+ # params - A Hash containing additional parameters to set.
+ #
+ # The following parameters can be set (as Symbols):
+ #
+ # * search: An optional search query to apply to the list of projects.
+ # * project_id: The ID of a project to exclude from the returned relation.
+ # * offset_id: The ID of a project to use for pagination. When given, only
+ # projects with a lower ID are included in the list.
+ def initialize(current_user, params = {})
+ @current_user = current_user
+ @search = params[:search]
+ @project_id = params[:project_id]
+ @offset_id = params[:offset_id]
+ end
+
+ def execute
+ current_user
+ .projects_where_can_admin_issues
+ .optionally_search(search)
+ .excluding_project(project_id)
+ .paginate_in_descending_order_using_id(before: offset_id)
+ .eager_load_namespace_and_owner
+ end
+ end
+end
diff --git a/app/finders/autocomplete/project_finder.rb b/app/finders/autocomplete/project_finder.rb
new file mode 100644
index 00000000000..3a4696f4c2e
--- /dev/null
+++ b/app/finders/autocomplete/project_finder.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Autocomplete
+ # Finder for retrieving a project to use for autocomplete data sources.
+ class ProjectFinder
+ attr_reader :current_user, :project_id
+
+ # current_user - The currently logged in user, if any.
+ # params - A Hash containing parameters to use for finding the project.
+ #
+ # The following parameters are supported:
+ #
+ # * project_id: The ID of the project to find.
+ def initialize(current_user = nil, params = {})
+ @current_user = current_user
+ @project_id = params[:project_id]
+ end
+
+ # Attempts to find a Project based on the current project ID.
+ def execute
+ return if project_id.blank?
+
+ project = Project.find(project_id)
+
+ # This removes the need for using `return render_404` and similar patterns
+ # in controllers that use this finder.
+ unless Ability.allowed?(current_user, :read_project, project)
+ raise ActiveRecord::RecordNotFound
+ .new("Could not find a Project with ID #{project_id}")
+ end
+
+ project
+ end
+ end
+end
diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb
new file mode 100644
index 00000000000..45955783be9
--- /dev/null
+++ b/app/finders/autocomplete/users_finder.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+module Autocomplete
+ class UsersFinder
+ # The number of users to display in the results is hardcoded to 20, and
+ # pagination is not supported. This ensures that performance remains
+ # consistent and removes the need for implementing keyset pagination to
+ # ensure good performance.
+ LIMIT = 20
+
+ attr_reader :current_user, :project, :group, :search, :skip_users,
+ :author_id, :todo_filter, :todo_state_filter,
+ :filter_by_current_user
+
+ def initialize(params:, current_user:, project:, group:)
+ @current_user = current_user
+ @project = project
+ @group = group
+ @search = params[:search]
+ @skip_users = params[:skip_users]
+ @author_id = params[:author_id]
+ @todo_filter = params[:todo_filter]
+ @todo_state_filter = params[:todo_state_filter]
+ @filter_by_current_user = params[:current_user]
+ end
+
+ def execute
+ items = limited_users
+
+ if search.blank?
+ # Include current user if available to filter by "Me"
+ items.unshift(current_user) if prepend_current_user?
+
+ if prepend_author? && (author = User.find_by_id(author_id))
+ items.unshift(author)
+ end
+ end
+
+ items.uniq
+ end
+
+ private
+
+ # Returns the users based on the input parameters, as an Array.
+ #
+ # This method is separate so it is easier to extend in EE.
+ # rubocop: disable CodeReuse/ActiveRecord
+ def limited_users
+ # When changing the order of these method calls, make sure that
+ # reorder_by_name() is called _before_ optionally_search(), otherwise
+ # reorder_by_name will break the ORDER BY applied in optionally_search().
+ find_users
+ .active
+ .reorder_by_name
+ .optionally_search(search)
+ .where_not_in(skip_users)
+ .limit_to_todo_authors(
+ user: current_user,
+ with_todos: todo_filter,
+ todo_state: todo_state_filter
+ )
+ .limit(LIMIT)
+ .to_a
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def prepend_current_user?
+ filter_by_current_user.present? && current_user
+ end
+
+ def prepend_author?
+ author_id.present? && current_user
+ end
+
+ def find_users
+ if project
+ project.authorized_users.union_with_user(author_id)
+ elsif group
+ group.users_with_parents
+ elsif current_user
+ User.all
+ else
+ User.none
+ end
+ end
+ end
+end
diff --git a/app/finders/autocomplete_users_finder.rb b/app/finders/autocomplete_users_finder.rb
deleted file mode 100644
index e8a03947f59..00000000000
--- a/app/finders/autocomplete_users_finder.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-class AutocompleteUsersFinder
- # The number of users to display in the results is hardcoded to 20, and
- # pagination is not supported. This ensures that performance remains
- # consistent and removes the need for implementing keyset pagination to ensure
- # good performance.
- LIMIT = 20
-
- attr_reader :current_user, :project, :group, :search, :skip_users,
- :author_id, :params
-
- def initialize(params:, current_user:, project:, group:)
- @current_user = current_user
- @project = project
- @group = group
- @search = params[:search]
- @skip_users = params[:skip_users]
- @author_id = params[:author_id]
- @params = params
- end
-
- def execute
- items = find_users
- items = items.active
- items = items.reorder(:name)
- items = items.search(search) if search.present?
- items = items.where.not(id: skip_users) if skip_users.present?
- items = items.limit(LIMIT)
-
- if params[:todo_filter].present? && current_user
- items = items.todo_authors(current_user.id, params[:todo_state_filter])
- end
-
- if search.blank?
- # Include current user if available to filter by "Me"
- if params[:current_user].present? && current_user
- items = [current_user, *items].uniq
- end
-
- if author_id.present? && current_user
- author = User.find_by_id(author_id)
- items = [author, *items].uniq if author
- end
- end
-
- items
- end
-
- private
-
- def find_users
- return users_from_project if project
- return group.users_with_parents if group
- return User.all if current_user
-
- User.none
- end
-
- def users_from_project
- if author_id.present?
- union = Gitlab::SQL::Union
- .new([project.authorized_users, User.where(id: author_id)])
-
- User.from("(#{union.to_sql}) #{User.table_name}")
- else
- project.authorized_users
- end
- end
-end
diff --git a/app/finders/awarded_emoji_finder.rb b/app/finders/awarded_emoji_finder.rb
new file mode 100644
index 00000000000..f0cc17f3b26
--- /dev/null
+++ b/app/finders/awarded_emoji_finder.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+# Class for retrieving information about emoji awarded _by_ a particular user.
+class AwardedEmojiFinder
+ attr_reader :current_user
+
+ # current_user - The User to generate the data for.
+ def initialize(current_user = nil)
+ @current_user = current_user
+ end
+
+ def execute
+ return [] unless current_user
+
+ # We want the resulting data set to be an Array containing the emoji names
+ # in descending order, based on how often they were awarded.
+ AwardEmoji
+ .award_counts_for_user(current_user)
+ .map { |name, _| { name: name } }
+ end
+end
diff --git a/app/finders/branches_finder.rb b/app/finders/branches_finder.rb
index 8bb1366867c..45d5591e81b 100644
--- a/app/finders/branches_finder.rb
+++ b/app/finders/branches_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BranchesFinder
def initialize(repository, params = {})
@repository = repository
@@ -5,8 +7,9 @@ class BranchesFinder
end
def execute
- branches = @repository.branches_sorted_by(sort)
- filter_by_name(branches)
+ branches = repository.branches_sorted_by(sort)
+ branches = by_search(branches)
+ branches
end
private
@@ -21,11 +24,39 @@ class BranchesFinder
@params[:sort].presence || 'name'
end
- def filter_by_name(branches)
- if search
- branches.select { |branch| branch.name.upcase.include?(search.upcase) }
+ def by_search(branches)
+ return branches unless search
+
+ case search
+ when ->(v) { v.starts_with?('^') }
+ filter_branches_with_prefix(branches, search.slice(1..-1).upcase)
+ when ->(v) { v.ends_with?('$') }
+ filter_branches_with_suffix(branches, search.chop.upcase)
else
- branches
+ matches = filter_branches_by_name(branches, search.upcase)
+ set_exact_match_as_first_result(matches, search)
end
end
+
+ def filter_branches_with_prefix(branches, prefix)
+ branches.select { |branch| branch.name.upcase.starts_with?(prefix) }
+ end
+
+ def filter_branches_with_suffix(branches, suffix)
+ branches.select { |branch| branch.name.upcase.ends_with?(suffix) }
+ end
+
+ def filter_branches_by_name(branches, term)
+ branches.select { |branch| branch.name.upcase.include?(term) }
+ end
+
+ def set_exact_match_as_first_result(matches, term)
+ exact_match_index = find_exact_match_index(matches, term)
+ matches.insert(0, matches.delete_at(exact_match_index)) if exact_match_index
+ matches
+ end
+
+ def find_exact_match_index(matches, term)
+ matches.index { |branch| branch.name.casecmp(term) == 0 }
+ end
end
diff --git a/app/finders/clusters_finder.rb b/app/finders/clusters_finder.rb
index c13f98257bf..0cce493b73e 100644
--- a/app/finders/clusters_finder.rb
+++ b/app/finders/clusters_finder.rb
@@ -1,18 +1,20 @@
+# frozen_string_literal: true
+
class ClustersFinder
- def initialize(project, user, scope)
- @project = project
+ def initialize(clusterable, user, scope)
+ @clusterable = clusterable
@user = user
@scope = scope || :active
end
def execute
- clusters = project.clusters
+ clusters = clusterable.clusters
filter_by_scope(clusters)
end
private
- attr_reader :project, :user, :scope
+ attr_reader :clusterable, :user, :scope
def filter_by_scope(clusters)
case scope.to_sym
diff --git a/app/finders/concerns/created_at_filter.rb b/app/finders/concerns/created_at_filter.rb
index ac9ac77732c..6b5863a5c53 100644
--- a/app/finders/concerns/created_at_filter.rb
+++ b/app/finders/concerns/created_at_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CreatedAtFilter
def by_created_at(items)
items = items.created_before(params[:created_before]) if params[:created_before].present?
diff --git a/app/finders/concerns/custom_attributes_filter.rb b/app/finders/concerns/custom_attributes_filter.rb
index 5bbf9ca242d..825c3a6b5b7 100644
--- a/app/finders/concerns/custom_attributes_filter.rb
+++ b/app/finders/concerns/custom_attributes_filter.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
module CustomAttributesFilter
+ # rubocop: disable CodeReuse/ActiveRecord
def by_custom_attributes(items)
return items unless params[:custom_attributes].is_a?(Hash)
return items unless Ability.allowed?(current_user, :read_custom_attribute)
@@ -17,4 +20,5 @@ module CustomAttributesFilter
scope.where('EXISTS (?)', custom_attributes.where(key: key, value: value))
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/concerns/finder_methods.rb b/app/finders/concerns/finder_methods.rb
index 2e905fa5750..5290313585f 100644
--- a/app/finders/concerns/finder_methods.rb
+++ b/app/finders/concerns/finder_methods.rb
@@ -1,11 +1,17 @@
+# frozen_string_literal: true
+
module FinderMethods
+ # rubocop: disable CodeReuse/ActiveRecord
def find_by!(*args)
raise_not_found_unless_authorized execute.find_by!(*args)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def find_by(*args)
if_authorized execute.find_by(*args)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find(*args)
raise_not_found_unless_authorized model.find(*args)
diff --git a/app/finders/concerns/finder_with_cross_project_access.rb b/app/finders/concerns/finder_with_cross_project_access.rb
index 92bf98d7cd2..220f62bcc7f 100644
--- a/app/finders/concerns/finder_with_cross_project_access.rb
+++ b/app/finders/concerns/finder_with_cross_project_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Module to prepend into finders to specify wether or not the finder requires
# cross project access
#
diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb
index a685719555c..c1ef9dfefa7 100644
--- a/app/finders/contributed_projects_finder.rb
+++ b/app/finders/contributed_projects_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContributedProjectsFinder < UnionFinder
def initialize(user)
@user = user
@@ -10,11 +12,13 @@ class ContributedProjectsFinder < UnionFinder
# visible by this user.
#
# Returns an ActiveRecord::Relation.
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(current_user = nil)
segments = all_projects(current_user)
find_union(segments, Project).includes(:namespace).order_id_desc
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/finders/environments_finder.rb b/app/finders/environments_finder.rb
index a59f8c1efa3..419be46fafe 100644
--- a/app/finders/environments_finder.rb
+++ b/app/finders/environments_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EnvironmentsFinder
attr_reader :project, :current_user, :params
@@ -5,6 +7,7 @@ class EnvironmentsFinder
@project, @current_user, @params = project, current_user, params
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
deployments = project.deployments
deployments =
@@ -42,6 +45,7 @@ class EnvironmentsFinder
environments
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/finders/events_finder.rb b/app/finders/events_finder.rb
index 8676925a540..8df01f1dad9 100644
--- a/app/finders/events_finder.rb
+++ b/app/finders/events_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EventsFinder
prepend FinderMethods
prepend FinderWithCrossProjectAccess
@@ -10,6 +12,7 @@ class EventsFinder
# Arguments:
# source - which user or project to looks for events on
# current_user - only return events for projects visible to this user
+ # WARNING: does not consider project feature visibility!
# params:
# action: string
# target_type: string
@@ -36,32 +39,42 @@ class EventsFinder
private
+ # rubocop: disable CodeReuse/ActiveRecord
def by_current_user_access(events)
- events.merge(ProjectsFinder.new(current_user: current_user).execute)
+ events.merge(ProjectsFinder.new(current_user: current_user).execute) # rubocop: disable CodeReuse/Finder
.joins(:project)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_action(events)
return events unless Event::ACTIONS[params[:action]]
events.where(action: Event::ACTIONS[params[:action]])
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_target_type(events)
return events unless Event::TARGET_TYPES[params[:target_type]]
- events.where(target_type: Event::TARGET_TYPES[params[:target_type]])
+ events.where(target_type: Event::TARGET_TYPES[params[:target_type]].name)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_created_at_before(events)
return events unless params[:before]
events.where('events.created_at < ?', params[:before].beginning_of_day)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_created_at_after(events)
return events unless params[:after]
events.where('events.created_at > ?', params[:after].end_of_day)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/fork_projects_finder.rb b/app/finders/fork_projects_finder.rb
index 28d1b31868e..03ace7e8057 100644
--- a/app/finders/fork_projects_finder.rb
+++ b/app/finders/fork_projects_finder.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
class ForkProjectsFinder < ProjectsFinder
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(project, params: {}, current_user: nil)
project_ids = project.forks.includes(:creator).select(:id)
super(params: params, current_user: current_user, project_ids_relation: project_ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb
index 051ea108e06..a9ce5be13f3 100644
--- a/app/finders/group_descendants_finder.rb
+++ b/app/finders/group_descendants_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# GroupDescendantsFinder
#
# Used to find and filter all subgroups and projects of a passed parent group
@@ -61,12 +63,16 @@ class GroupDescendantsFinder
end
def direct_child_groups
+ # rubocop: disable CodeReuse/Finder
GroupsFinder.new(current_user,
parent: parent_group,
all_available: true).execute
+ # rubocop: enable CodeReuse/Finder
end
+ # rubocop: disable CodeReuse/ActiveRecord
def all_visible_descendant_groups
+ # rubocop: disable CodeReuse/Finder
groups_table = Group.arel_table
visible_to_user = groups_table[:visibility_level]
.in(Gitlab::VisibilityLevel.levels_for_user(current_user))
@@ -84,7 +90,9 @@ class GroupDescendantsFinder
hierarchy_for_parent
.descendants
.where(visible_to_user)
+ # rubocop: enable CodeReuse/Finder
end
+ # rubocop: enable CodeReuse/ActiveRecord
def subgroups_matching_filter
all_visible_descendant_groups
@@ -101,18 +109,22 @@ class GroupDescendantsFinder
#
# So when searching 'project', on the 'subgroup' page we want to preload
# 'nested-group' but not 'subgroup' or 'root'
+ # rubocop: disable CodeReuse/ActiveRecord
def ancestors_of_groups(base_for_ancestors)
group_ids = base_for_ancestors.except(:select, :sort).select(:id)
Gitlab::GroupHierarchy.new(Group.where(id: group_ids))
.base_and_ancestors(upto: parent_group.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def ancestors_of_filtered_projects
projects_to_load_ancestors_of = projects.where.not(namespace: parent_group)
groups_to_load_ancestors_of = Group.where(id: projects_to_load_ancestors_of.select(:namespace_id))
ancestors_of_groups(groups_to_load_ancestors_of)
.with_selects_for_list(archived: params[:archived])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def ancestors_of_filtered_subgroups
ancestors_of_groups(subgroups)
@@ -133,21 +145,27 @@ class GroupDescendantsFinder
groups.with_selects_for_list(archived: params[:archived]).order_by(sort)
end
+ # rubocop: disable CodeReuse/Finder
def direct_child_projects
- GroupProjectsFinder.new(group: parent_group, current_user: current_user, params: params)
+ GroupProjectsFinder.new(group: parent_group, current_user: current_user, params: params, options: { only_owned: true })
.execute
end
+ # rubocop: enable CodeReuse/Finder
# Finds all projects nested under `parent_group` or any of its descendant
# groups
+ # rubocop: disable CodeReuse/ActiveRecord
def projects_matching_filter
+ # rubocop: disable CodeReuse/Finder
projects_nested_in_group = Project.where(namespace_id: hierarchy_for_parent.base_and_descendants.select(:id))
params_with_search = params.merge(search: params[:filter])
ProjectsFinder.new(params: params_with_search,
current_user: current_user,
project_ids_relation: projects_nested_in_group).execute
+ # rubocop: enable CodeReuse/Finder
end
+ # rubocop: enable CodeReuse/ActiveRecord
def projects
projects = if params[:filter]
@@ -160,10 +178,12 @@ class GroupDescendantsFinder
end
def sort
- params.fetch(:sort, 'id_asc')
+ params.fetch(:sort, 'created_desc')
end
+ # rubocop: disable CodeReuse/ActiveRecord
def hierarchy_for_parent
@hierarchy ||= Gitlab::GroupHierarchy.new(Group.where(id: parent_group.id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/group_finder.rb b/app/finders/group_finder.rb
index 24c84d2d1aa..d2ad8a372b1 100644
--- a/app/finders/group_finder.rb
+++ b/app/finders/group_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupFinder
include Gitlab::Allowable
@@ -5,6 +7,7 @@ class GroupFinder
@current_user = current_user
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(*params)
group = Group.find_by(*params)
@@ -14,4 +17,5 @@ class GroupFinder
nil
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/group_labels_finder.rb b/app/finders/group_labels_finder.rb
new file mode 100644
index 00000000000..a668a0f0fae
--- /dev/null
+++ b/app/finders/group_labels_finder.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class GroupLabelsFinder
+ attr_reader :current_user, :group, :params
+
+ def initialize(current_user, group, params = {})
+ @current_user = current_user
+ @group = group
+ @params = params
+ end
+
+ def execute
+ group.labels
+ .optionally_subscribed_by(subscriber_id)
+ .optionally_search(params[:search])
+ .order_by(params[:sort])
+ .page(params[:page])
+ end
+
+ private
+
+ def subscriber_id
+ current_user&.id if subscribed?
+ end
+
+ def subscribed?
+ params[:subscribed] == 'true'
+ end
+end
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index 2a656c0d31c..eebc67cfa9e 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
class GroupMembersFinder
def initialize(group)
@group = group
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(include_descendants: false)
group_members = @group.members
wheres = []
@@ -29,4 +32,5 @@ class GroupMembersFinder
GroupMember.where(wheres.join(' OR '))
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb
index b6bdb2b7b0f..4155b6af8da 100644
--- a/app/finders/group_projects_finder.rb
+++ b/app/finders/group_projects_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# GroupProjectsFinder
#
# Used to filter Projects by set of params
@@ -82,6 +84,7 @@ class GroupProjectsFinder < ProjectsFinder
options.fetch(:include_subgroups, false)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def owned_projects
if include_subgroups?
Project.where(namespace_id: group.self_and_descendants.select(:id))
@@ -89,6 +92,7 @@ class GroupProjectsFinder < ProjectsFinder
group.projects
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def shared_projects
group.shared_projects
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index 0eeba1d2428..ea954f98220 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# GroupsFinder
#
# Used to filter Groups by a set of params
@@ -58,6 +60,7 @@ class GroupsFinder < UnionFinder
current_user.groups
end
+ # rubocop: disable CodeReuse/ActiveRecord
def groups_with_min_access_level
groups = current_user
.groups
@@ -67,12 +70,15 @@ class GroupsFinder < UnionFinder
.new(groups)
.base_and_descendants
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_parent(groups)
return groups unless params[:parent]
groups.where(parent: params[:parent])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def owned_groups
current_user&.owned_groups || Group.none
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 372e2a96c2c..fdc630cbf72 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# IssuableFinder
#
# Used to filter Issues and MergeRequests collections by set of params
@@ -12,7 +14,9 @@
# project_id: integer
# milestone_title: string
# author_id: integer
-# assignee_id: integer
+# author_username: string
+# assignee_id: integer or 'None' or 'Any'
+# assignee_username: string
# search: string
# label_name: string
# sort: string
@@ -32,6 +36,11 @@ class IssuableFinder
requires_cross_project_access unless: -> { project? }
+ # This is used as a common filter for None / Any
+ FILTER_NONE = 'none'.freeze
+ FILTER_ANY = 'any'.freeze
+
+ # This is accepted as a deprecated filter and is also used in unassigning users
NONE = '0'.freeze
attr_accessor :current_user, :params
@@ -42,25 +51,15 @@ class IssuableFinder
assignee_username
author_id
author_username
- authorized_only
- group_id
- iids
label_name
milestone_title
my_reaction_emoji
- non_archived
- project_id
- scope
search
- sort
- state
- include_subgroups
- use_cte_for_search
]
end
def self.array_params
- @array_params ||= { label_name: [], iids: [], assignee_username: [] }
+ @array_params ||= { label_name: [], assignee_username: [] }
end
def self.valid_params
@@ -109,6 +108,7 @@ class IssuableFinder
# (even if that query is slower than any of the individual state queries) and
# grouping and counting within that query.
#
+ # rubocop: disable CodeReuse/ActiveRecord
def count_by_state
count_params = params.merge(state: nil, sort: nil)
finder = self.class.new(current_user, count_params)
@@ -125,13 +125,14 @@ class IssuableFinder
labels_count = 1 if use_cte_for_search?
finder.execute.reorder(nil).group(:state).count.each do |key, value|
- counts[Array(key).last.to_sym] += value / labels_count
+ counts[count_key(key)] += value / labels_count
end
counts[:all] = counts.values.sum
counts.with_indifferent_access
end
+ # rubocop: enable CodeReuse/ActiveRecord
def group
return @group if defined?(@group)
@@ -157,6 +158,7 @@ class IssuableFinder
@project = project
end
+ # rubocop: disable CodeReuse/ActiveRecord
def projects(items = nil)
return @projects = project if project?
@@ -165,13 +167,14 @@ class IssuableFinder
current_user.authorized_projects
elsif group
finder_options = { include_subgroups: params[:include_subgroups], only_owned: true }
- GroupProjectsFinder.new(group: group, current_user: current_user, options: finder_options).execute
+ GroupProjectsFinder.new(group: group, current_user: current_user, options: finder_options).execute # rubocop: disable CodeReuse/Finder
else
- ProjectsFinder.new(current_user: current_user).execute
+ ProjectsFinder.new(current_user: current_user).execute # rubocop: disable CodeReuse/Finder
end
@projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def search
params[:search].presence
@@ -181,10 +184,6 @@ class IssuableFinder
params[:milestone_title].present?
end
- def filter_by_no_milestone?
- milestones? && params[:milestone_title] == Milestone::None.title
- end
-
def milestones
return @milestones if defined?(@milestones)
@@ -200,7 +199,7 @@ class IssuableFinder
search_params =
{ title: params[:milestone_title], project_ids: project_id, group_ids: group_id }
- MilestonesFinder.new(search_params).execute
+ MilestonesFinder.new(search_params).execute # rubocop: disable CodeReuse/Finder
else
Milestone.none
end
@@ -219,25 +218,21 @@ class IssuableFinder
@labels =
if labels? && !filter_by_no_label?
- LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute(skip_authorization: true)
+ LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute(skip_authorization: true) # rubocop: disable CodeReuse/Finder
else
Label.none
end
end
def assignee_id?
- params[:assignee_id].present? && params[:assignee_id] != NONE
+ params[:assignee_id].present?
end
def assignee_username?
- params[:assignee_username].present? && params[:assignee_username] != NONE
- end
-
- def no_assignee?
- # Assignee_id takes precedence over assignee_username
- params[:assignee_id] == NONE || params[:assignee_username] == NONE
+ params[:assignee_username].present?
end
+ # rubocop: disable CodeReuse/ActiveRecord
def assignee
return @assignee if defined?(@assignee)
@@ -245,11 +240,12 @@ class IssuableFinder
if assignee_id?
User.find_by(id: params[:assignee_id])
elsif assignee_username?
- User.find_by(username: params[:assignee_username])
+ User.find_by_username(params[:assignee_username])
else
nil
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def author_id?
params[:author_id].present? && params[:author_id] != NONE
@@ -264,6 +260,7 @@ class IssuableFinder
params[:author_id] == NONE || params[:author_username] == NONE
end
+ # rubocop: disable CodeReuse/ActiveRecord
def author
return @author if defined?(@author)
@@ -271,11 +268,12 @@ class IssuableFinder
if author_id?
User.find_by(id: params[:author_id])
elsif author_username?
- User.find_by(username: params[:author_username])
+ User.find_by_username(params[:author_username])
else
nil
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -283,6 +281,11 @@ class IssuableFinder
klass.all
end
+ def count_key(value)
+ Array(value).last.to_sym
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
def by_scope(items)
return items.none if current_user_related? && !current_user
@@ -295,6 +298,7 @@ class IssuableFinder
items
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_updated_at(items)
items = items.updated_after(params[:updated_after]) if params[:updated_after].present?
@@ -303,6 +307,7 @@ class IssuableFinder
items
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_state(items)
case params[:state].to_s
when 'closed'
@@ -317,12 +322,14 @@ class IssuableFinder
items
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_group(items)
# Selection by group is already covered by `by_project` and `projects`
items
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_project(items)
items =
if project?
@@ -335,14 +342,17 @@ class IssuableFinder
items
end
+ # rubocop: enable CodeReuse/ActiveRecord
def use_cte_for_search?
return false unless search
return false unless Gitlab::Database.postgresql?
+ return false unless Feature.enabled?(:use_cte_for_group_issues_search, default_enabled: true)
params[:use_cte_for_search]
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_search(items)
return items unless search
@@ -355,29 +365,48 @@ class IssuableFinder
items.full_search(search)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_iids(items)
params[:iids].present? ? items.where(iid: params[:iids]) : items
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def sort(items)
# Ensure we always have an explicit sort order (instead of inheriting
# multiple orders when combining ActiveRecord::Relation objects).
params[:sort] ? items.sort_by_attribute(params[:sort], excluded_labels: label_names) : items.reorder(id: :desc)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_assignee(items)
- if assignee
- items = items.where(assignee_id: assignee.id)
- elsif no_assignee?
- items = items.where(assignee_id: nil)
+ if filter_by_no_assignee?
+ items.where(assignee_id: nil)
+ elsif filter_by_any_assignee?
+ items.where('assignee_id IS NOT NULL')
+ elsif assignee
+ items.where(assignee_id: assignee.id)
elsif assignee_id? || assignee_username? # assignee not found
- items = items.none
+ items.none
+ else
+ items
end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
- items
+ def filter_by_no_assignee?
+ # Assignee_id takes precedence over assignee_username
+ [NONE, FILTER_NONE].include?(params[:assignee_id].to_s.downcase) || params[:assignee_username].to_s == NONE
end
+ def filter_by_any_assignee?
+ params[:assignee_id].to_s.downcase == FILTER_ANY
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
def by_author(items)
if author
items = items.where(author_id: author.id)
@@ -389,19 +418,15 @@ class IssuableFinder
items
end
+ # rubocop: enable CodeReuse/ActiveRecord
- def filter_by_upcoming_milestone?
- params[:milestone_title] == Milestone::Upcoming.name
- end
-
- def filter_by_started_milestone?
- params[:milestone_title] == Milestone::Started.name
- end
-
+ # rubocop: disable CodeReuse/ActiveRecord
def by_milestone(items)
if milestones?
if filter_by_no_milestone?
items = items.left_joins_milestones.where(milestone_id: [-1, nil])
+ elsif filter_by_any_milestone?
+ items = items.any_milestone
elsif filter_by_upcoming_milestone?
upcoming_ids = Milestone.upcoming_ids_by_projects(projects(items))
items = items.left_joins_milestones.where(milestone_id: upcoming_ids)
@@ -414,6 +439,25 @@ class IssuableFinder
items
end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def filter_by_no_milestone?
+ # Accepts `No Milestone` for compatibility
+ params[:milestone_title].to_s.downcase == FILTER_NONE || params[:milestone_title] == Milestone::None.title
+ end
+
+ def filter_by_any_milestone?
+ # Accepts `Any Milestone` for compatibility
+ params[:milestone_title].to_s.downcase == FILTER_ANY || params[:milestone_title] == Milestone::Any.title
+ end
+
+ def filter_by_upcoming_milestone?
+ params[:milestone_title] == Milestone::Upcoming.name
+ end
+
+ def filter_by_started_milestone?
+ params[:milestone_title] == Milestone::Started.name
+ end
def by_label(items)
return items unless labels?
@@ -430,12 +474,27 @@ class IssuableFinder
def by_my_reaction_emoji(items)
if params[:my_reaction_emoji].present? && current_user
- items = items.awarded(current_user, params[:my_reaction_emoji])
+ items =
+ if filter_by_no_reaction?
+ items.not_awarded(current_user)
+ elsif filter_by_any_reaction?
+ items.awarded(current_user)
+ else
+ items.awarded(current_user, params[:my_reaction_emoji])
+ end
end
items
end
+ def filter_by_no_reaction?
+ params[:my_reaction_emoji].to_s.downcase == FILTER_NONE
+ end
+
+ def filter_by_any_reaction?
+ params[:my_reaction_emoji].to_s.downcase == FILTER_ANY
+ end
+
def label_names
if labels?
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 24a6b9349a0..45e494725d7 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Finders::Issues class
#
# Used to filter Issues collections by set of params
@@ -29,10 +31,13 @@ class IssuesFinder < IssuableFinder
@scalar_params ||= super + [:due_date]
end
+ # rubocop: disable CodeReuse/ActiveRecord
def klass
Issue.includes(:author)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def with_confidentiality_access_check
return Issue.all if user_can_see_all_confidential_issues?
return Issue.where('issues.confidential IS NOT TRUE') if user_cannot_see_confidential_issues?
@@ -46,6 +51,7 @@ class IssuesFinder < IssuableFinder
user_id: current_user.id,
project_ids: current_user.authorized_projects(CONFIDENTIAL_ACCESS_LEVEL).select(:id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -114,9 +120,13 @@ class IssuesFinder < IssuableFinder
return @user_can_see_all_confidential_issues = true if current_user.full_private_access?
@user_can_see_all_confidential_issues =
- project? &&
- project &&
- project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
+ if project? && project
+ project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
+ elsif group
+ group.max_member_access_for_user(current_user) >= CONFIDENTIAL_ACCESS_LEVEL
+ else
+ false
+ end
end
def user_cannot_see_confidential_issues?
@@ -126,10 +136,12 @@ class IssuesFinder < IssuableFinder
end
def by_assignee(items)
- if assignee
- items.assigned_to(assignee)
- elsif no_assignee?
+ if filter_by_no_assignee?
items.unassigned
+ elsif filter_by_any_assignee?
+ items.assigned
+ elsif assignee
+ items.assigned_to(assignee)
elsif assignee_id? || assignee_username? # assignee not found
items.none
else
diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb
index 47174980258..4d8128dd824 100644
--- a/app/finders/joined_groups_finder.rb
+++ b/app/finders/joined_groups_finder.rb
@@ -1,4 +1,6 @@
-class JoinedGroupsFinder < UnionFinder
+# frozen_string_literal: true
+
+class JoinedGroupsFinder
def initialize(user)
@user = user
end
@@ -6,19 +8,8 @@ class JoinedGroupsFinder < UnionFinder
# Finds the groups of the source user, optionally limited to those visible to
# the current user.
def execute(current_user = nil)
- segments = all_groups(current_user)
-
- find_union(segments, Group).order_id_desc
- end
-
- private
-
- def all_groups(current_user)
- groups = []
-
- groups << @user.authorized_groups.visible_to_user(current_user) if current_user
- groups << @user.authorized_groups.public_to_user(current_user)
-
- groups
+ @user.authorized_groups
+ .public_or_visible_to_user(current_user)
+ .order_id_desc
end
end
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index 1d05bf28438..e523942ea4c 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LabelsFinder < UnionFinder
prepend FinderWithCrossProjectAccess
include FinderMethods
@@ -14,6 +16,7 @@ class LabelsFinder < UnionFinder
@skip_authorization = skip_authorization
items = find_union(label_ids, Label) || Label.none
items = with_title(items)
+ items = by_subscription(items)
items = by_search(items)
sort(items)
end
@@ -22,6 +25,7 @@ class LabelsFinder < UnionFinder
attr_reader :current_user, :params, :skip_authorization
+ # rubocop: disable CodeReuse/ActiveRecord
def label_ids
label_ids = []
@@ -52,17 +56,26 @@ class LabelsFinder < UnionFinder
label_ids
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def sort(items)
- items.reorder(title: :asc)
+ if params[:sort]
+ items.order_by(params[:sort])
+ else
+ items.reorder(title: :asc)
+ end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def with_title(items)
return items if title.nil?
return items.none if title.blank?
items.where(title: title)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_search(labels)
return labels unless search?
@@ -70,6 +83,18 @@ class LabelsFinder < UnionFinder
labels.search(params[:search])
end
+ def by_subscription(labels)
+ labels.optionally_subscribed_by(subscriber_id)
+ end
+
+ def subscriber_id
+ current_user&.id if subscribed?
+ end
+
+ def subscribed?
+ params[:subscribed] == 'true'
+ end
+
# Gets redacted array of group ids
# which can include the ancestors and descendants of the requested group.
def group_ids_for(group)
@@ -102,7 +127,7 @@ class LabelsFinder < UnionFinder
end
def project?
- params[:project_id].present?
+ params[:project].present? || params[:project_id].present?
end
def projects?
@@ -125,7 +150,7 @@ class LabelsFinder < UnionFinder
return @project if defined?(@project)
if project?
- @project = Project.find(params[:project_id])
+ @project = params[:project] || Project.find(params[:project_id])
@project = nil unless authorized_to_read_labels?(@project)
else
@project = nil
@@ -134,13 +159,14 @@ class LabelsFinder < UnionFinder
@project
end
+ # rubocop: disable CodeReuse/ActiveRecord
def projects
return @projects if defined?(@projects)
@projects = if skip_authorization
Project.all
else
- ProjectsFinder.new(params: { non_archived: true }, current_user: current_user).execute
+ ProjectsFinder.new(params: { non_archived: true }, current_user: current_user).execute # rubocop: disable CodeReuse/Finder
end
@projects = @projects.in_namespace(params[:group_id]) if group?
@@ -149,6 +175,7 @@ class LabelsFinder < UnionFinder
@projects
end
+ # rubocop: enable CodeReuse/ActiveRecord
def authorized_to_read_labels?(label_parent)
return true if skip_authorization
diff --git a/app/finders/license_template_finder.rb b/app/finders/license_template_finder.rb
new file mode 100644
index 00000000000..d735a4c1d69
--- /dev/null
+++ b/app/finders/license_template_finder.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+# LicenseTemplateFinder
+#
+# Used to find license templates, which may come from a variety of external
+# sources
+#
+# Params can be any of the following:
+# popular: boolean. When set to true, only "popular" licenses are shown. When
+# false, all licenses except popular ones are shown. When nil (the
+# default), *all* licenses will be shown.
+# name: string. If set, return a single license matching that name (or nil)
+class LicenseTemplateFinder
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :project, :params
+
+ def initialize(project, params = {})
+ @project = project
+ @params = params
+ end
+
+ def execute
+ if params[:name]
+ vendored_licenses.find { |template| template.key == params[:name] }
+ else
+ vendored_licenses
+ end
+ end
+
+ private
+
+ def vendored_licenses
+ strong_memoize(:vendored_licenses) do
+ Licensee::License.all(featured: popular_only?).map do |license|
+ LicenseTemplate.new(
+ key: license.key,
+ name: license.name,
+ nickname: license.nickname,
+ category: (license.featured? ? :Popular : :Other),
+ content: license.content,
+ url: license.url,
+ meta: license.meta
+ )
+ end
+ end
+ end
+
+ def popular_only?
+ params.fetch(:popular, nil)
+ end
+end
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
index 4c893ae2de6..f90a7868102 100644
--- a/app/finders/members_finder.rb
+++ b/app/finders/members_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MembersFinder
attr_reader :project, :current_user, :group
@@ -7,15 +9,16 @@ class MembersFinder
@group = project.group
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(include_descendants: false)
project_members = project.project_members
project_members = project_members.non_invite unless can?(current_user, :admin_project, project)
if group
- group_members = GroupMembersFinder.new(group).execute(include_descendants: include_descendants)
+ group_members = GroupMembersFinder.new(group).execute(include_descendants: include_descendants) # rubocop: disable CodeReuse/Finder
group_members = group_members.non_invite
- union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false)
+ union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false) # rubocop: disable Gitlab/Union
sql = distinct_on(union)
@@ -24,6 +27,7 @@ class MembersFinder
project_members
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def can?(*args)
Ability.allowed?(*args)
diff --git a/app/finders/merge_request_target_project_finder.rb b/app/finders/merge_request_target_project_finder.rb
index 188ec447a94..5f0589f6c8b 100644
--- a/app/finders/merge_request_target_project_finder.rb
+++ b/app/finders/merge_request_target_project_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestTargetProjectFinder
include FinderMethods
@@ -8,6 +10,7 @@ class MergeRequestTargetProjectFinder
@source_project = source_project
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
if @source_project.fork_network
@source_project.fork_network.projects
@@ -18,4 +21,5 @@ class MergeRequestTargetProjectFinder
Project.where(id: source_project)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 40089c082c1..e190d5d90c9 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Finders::MergeRequest class
#
# Used to filter MergeRequests collections by set of params
@@ -25,13 +27,17 @@
# updated_before: datetime
#
class MergeRequestsFinder < IssuableFinder
+ def self.scalar_params
+ @scalar_params ||= super + [:wip]
+ end
+
def klass
MergeRequest
end
def filter_items(_items)
items = by_source_branch(super)
-
+ items = by_wip(items)
by_target_branch(items)
end
@@ -41,19 +47,38 @@ class MergeRequestsFinder < IssuableFinder
@source_branch ||= params[:source_branch].presence
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_source_branch(items)
return items unless source_branch
items.where(source_branch: source_branch)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def target_branch
@target_branch ||= params[:target_branch].presence
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_target_branch(items)
return items unless target_branch
items.where(target_branch: target_branch)
end
+
+ def by_wip(items)
+ if params[:wip] == 'yes'
+ items.where(wip_match(items.arel_table))
+ elsif params[:wip] == 'no'
+ items.where.not(wip_match(items.arel_table))
+ else
+ items
+ end
+ end
+
+ def wip_match(table)
+ table[:title].matches('WIP:%')
+ .or(table[:title].matches('WIP %'))
+ .or(table[:title].matches('[WIP]%'))
+ end
end
diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb
index f5d2b9f253a..9c477978f60 100644
--- a/app/finders/milestones_finder.rb
+++ b/app/finders/milestones_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Search for milestones
#
# params - Hash
@@ -35,6 +37,7 @@ class MilestonesFinder
items.for_projects_and_groups(project_ids, group_ids)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_title(items)
if params[:title]
items.where(title: params[:title])
@@ -42,13 +45,16 @@ class MilestonesFinder
items
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_state(items)
Milestone.filter_by_state(items, params[:state])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def order(items)
order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC')
items.reorder(order_statement).order('title ASC')
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/move_to_project_finder.rb b/app/finders/move_to_project_finder.rb
deleted file mode 100644
index 038d5565a1e..00000000000
--- a/app/finders/move_to_project_finder.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-class MoveToProjectFinder
- PAGE_SIZE = 50
-
- def initialize(user)
- @user = user
- end
-
- def execute(from_project, search: nil, offset_id: nil)
- projects = @user.projects_where_can_admin_issues
- projects = projects.search(search) if search.present?
- projects = projects.excluding_project(from_project)
- projects = projects.order_id_desc
-
- # infinite scroll using offset
- projects = projects.where('projects.id < ?', offset_id) if offset_id.present?
- projects = projects.limit(PAGE_SIZE)
-
- # to ask for Project#name_with_namespace
- projects.includes(namespace: :owner)
- end
-end
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 9b7a35fb3b5..817aac8b5d5 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NotesFinder
FETCH_OVERLAP = 5.seconds
@@ -22,6 +24,8 @@ class NotesFinder
def execute
notes = init_collection
notes = since_fetch_at(notes)
+ notes = notes.with_notes_filter(@params[:notes_filter]) if notes_filter?
+
notes.fresh
end
@@ -65,21 +69,23 @@ class NotesFinder
@params[:target_type]
end
+ # rubocop: disable CodeReuse/ActiveRecord
def notes_of_any_type
types = %w(commit issue merge_request snippet)
note_relations = types.map { |t| notes_for_type(t) }
note_relations.map! { |notes| search(notes) }
- UnionFinder.new.find_union(note_relations, Note.includes(:author))
+ UnionFinder.new.find_union(note_relations, Note.includes(:author)) # rubocop: disable CodeReuse/Finder
end
+ # rubocop: enable CodeReuse/ActiveRecord
def noteables_for_type(noteable_type)
case noteable_type
when "issue"
- IssuesFinder.new(@current_user, project_id: @project.id).execute
+ IssuesFinder.new(@current_user, project_id: @project.id).execute # rubocop: disable CodeReuse/Finder
when "merge_request"
- MergeRequestsFinder.new(@current_user, project_id: @project.id).execute
+ MergeRequestsFinder.new(@current_user, project_id: @project.id).execute # rubocop: disable CodeReuse/Finder
when "snippet", "project_snippet"
- SnippetsFinder.new(@current_user, project: @project).execute
+ SnippetsFinder.new(@current_user, project: @project).execute # rubocop: disable CodeReuse/Finder
when "personal_snippet"
PersonalSnippet.all
else
@@ -87,6 +93,7 @@ class NotesFinder
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def notes_for_type(noteable_type)
if noteable_type == "commit"
if Ability.allowed?(@current_user, :download_code, @project)
@@ -99,6 +106,7 @@ class NotesFinder
@project.notes.where(noteable_type: finder.base_class.name, noteable_id: finder.reorder(nil))
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def notes_on_target
if target.respond_to?(:related_notes)
@@ -128,4 +136,8 @@ class NotesFinder
last_fetched_at = Time.at(@params.fetch(:last_fetched_at, 0).to_i)
notes.updated_after(last_fetched_at - FETCH_OVERLAP)
end
+
+ def notes_filter?
+ @params[:notes_filter].present?
+ end
end
diff --git a/app/finders/pending_todos_finder.rb b/app/finders/pending_todos_finder.rb
new file mode 100644
index 00000000000..c21d90c9182
--- /dev/null
+++ b/app/finders/pending_todos_finder.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+# Finder for retrieving the pending todos of a user, optionally filtered using
+# various fields.
+#
+# While this finder is a bit more verbose compared to use
+# `where(params.slice(...))`, it allows us to decouple the input parameters from
+# the actual column names. For example, if we ever decide to use separate
+# columns for target types (e.g. `issue_id`, `merge_request_id`, etc), we no
+# longer need to change _everything_ that uses this finder. Instead, we just
+# change the various `by_*` methods in this finder, without having to touch
+# everything that uses it.
+class PendingTodosFinder
+ attr_reader :current_user, :params
+
+ # current_user - The user to retrieve the todos for.
+ # params - A Hash containing columns and values to use for filtering todos.
+ def initialize(current_user, params = {})
+ @current_user = current_user
+ @params = params
+ end
+
+ def execute
+ todos = current_user.todos.pending
+ todos = by_project(todos)
+ todos = by_target_id(todos)
+ todos = by_target_type(todos)
+ todos = by_commit_id(todos)
+
+ todos
+ end
+
+ def by_project(todos)
+ if (id = params[:project_id])
+ todos.for_project(id)
+ else
+ todos
+ end
+ end
+
+ def by_target_id(todos)
+ if (id = params[:target_id])
+ todos.for_target(id)
+ else
+ todos
+ end
+ end
+
+ def by_target_type(todos)
+ if (type = params[:target_type])
+ todos.for_type(type)
+ else
+ todos
+ end
+ end
+
+ def by_commit_id(todos)
+ if (id = params[:commit_id])
+ todos.for_commit(id)
+ else
+ todos
+ end
+ end
+end
diff --git a/app/finders/personal_access_tokens_finder.rb b/app/finders/personal_access_tokens_finder.rb
index d975f354a88..bd95dcd323f 100644
--- a/app/finders/personal_access_tokens_finder.rb
+++ b/app/finders/personal_access_tokens_finder.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
class PersonalAccessTokensFinder
attr_accessor :params
- delegate :build, :find, :find_by, to: :execute
+ delegate :build, :find, :find_by_id, :find_by_token, to: :execute
def initialize(params = {})
@params = params
@@ -16,11 +18,13 @@ class PersonalAccessTokensFinder
private
+ # rubocop: disable CodeReuse/ActiveRecord
def by_user(tokens)
return tokens unless @params[:user]
tokens.where(user: @params[:user])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_impersonation(tokens)
case @params[:impersonation]
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index a56a3a1e1a9..20f5b221a89 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PersonalProjectsFinder < UnionFinder
include Gitlab::Allowable
@@ -15,6 +17,7 @@ class PersonalProjectsFinder < UnionFinder
# min_access_level: integer
#
# Returns an ActiveRecord::Relation.
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(current_user = nil)
return Project.none unless can?(current_user, :read_user_profile, @user)
@@ -22,6 +25,7 @@ class PersonalProjectsFinder < UnionFinder
find_union(segments, Project).includes(:namespace).order_updated_desc
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/finders/pipeline_schedules_finder.rb b/app/finders/pipeline_schedules_finder.rb
index 2ac4289fbbe..3beee608268 100644
--- a/app/finders/pipeline_schedules_finder.rb
+++ b/app/finders/pipeline_schedules_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineSchedulesFinder
attr_reader :project, :pipeline_schedules
@@ -6,6 +8,7 @@ class PipelineSchedulesFinder
@pipeline_schedules = project.pipeline_schedules
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(scope: nil)
scoped_schedules =
case scope
@@ -19,4 +22,5 @@ class PipelineSchedulesFinder
scoped_schedules.order(id: :desc)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb
index a99a889a7e9..35d0e1acce5 100644
--- a/app/finders/pipelines_finder.rb
+++ b/app/finders/pipelines_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelinesFinder
attr_reader :project, :pipelines, :params, :current_user
@@ -28,13 +30,17 @@ class PipelinesFinder
private
+ # rubocop: disable CodeReuse/ActiveRecord
def ids_for_ref(refs)
pipelines.where(ref: refs).group(:ref).select('max(id)')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def from_ids(ids)
pipelines.unscoped.where(id: ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def branches
project.repository.branch_names
@@ -61,12 +67,15 @@ class PipelinesFinder
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_status(items)
return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
items.where(status: params[:status])
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_ref(items)
if params[:ref].present?
items.where(ref: params[:ref])
@@ -74,7 +83,9 @@ class PipelinesFinder
items
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_sha(items)
if params[:sha].present?
items.where(sha: params[:sha])
@@ -82,7 +93,9 @@ class PipelinesFinder
items
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_name(items)
if params[:name].present?
items.joins(:user).where(users: { name: params[:name] })
@@ -90,7 +103,9 @@ class PipelinesFinder
items
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_username(items)
if params[:username].present?
items.joins(:user).where(users: { username: params[:username] })
@@ -98,7 +113,9 @@ class PipelinesFinder
items
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_yaml_errors(items)
case Gitlab::Utils.to_boolean(params[:yaml_errors])
when true
@@ -109,7 +126,9 @@ class PipelinesFinder
items
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def sort_items(items)
order_by = if ALLOWED_INDEXED_COLUMNS.include?(params[:order_by])
params[:order_by]
@@ -125,4 +144,5 @@ class PipelinesFinder
items.order(order_by => sort)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index cac6643eff3..93d3c991846 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# ProjectsFinder
#
# Used to filter Projects by set of params
@@ -35,21 +37,12 @@ class ProjectsFinder < UnionFinder
user = params.delete(:user)
collection =
if user
- PersonalProjectsFinder.new(user, finder_params).execute(current_user)
+ PersonalProjectsFinder.new(user, finder_params).execute(current_user) # rubocop: disable CodeReuse/Finder
else
init_collection
end
- collection = by_ids(collection)
- collection = by_personal(collection)
- collection = by_starred(collection)
- collection = by_trending(collection)
- collection = by_visibilty_level(collection)
- collection = by_tags(collection)
- collection = by_search(collection)
- collection = by_archived(collection)
- collection = by_custom_attributes(collection)
-
+ collection = filter_projects(collection)
sort(collection)
end
@@ -63,6 +56,22 @@ class ProjectsFinder < UnionFinder
end
end
+ # EE would override this to add more filters
+ def filter_projects(collection)
+ collection = by_ids(collection)
+ collection = by_personal(collection)
+ collection = by_starred(collection)
+ collection = by_trending(collection)
+ collection = by_visibilty_level(collection)
+ collection = by_tags(collection)
+ collection = by_search(collection)
+ collection = by_archived(collection)
+ collection = by_custom_attributes(collection)
+ collection = by_deleted_status(collection)
+ collection
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
def collection_with_user
if owned_projects?
current_user.owned_projects
@@ -76,6 +85,7 @@ class ProjectsFinder < UnionFinder
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Builds a collection for an anonymous user.
def collection_without_user
@@ -98,9 +108,11 @@ class ProjectsFinder < UnionFinder
params[:min_access_level].present?
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_ids(items)
project_ids_relation ? items.where(id: project_ids_relation) : items
end
+ # rubocop: enable CodeReuse/ActiveRecord
def union(items)
find_union(items, Project).with_route
@@ -118,9 +130,11 @@ class ProjectsFinder < UnionFinder
params[:trending].present? ? items.trending : items
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_visibilty_level(items)
params[:visibility_level].present? ? items.where(visibility_level: params[:visibility_level]) : items
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_tags(items)
params[:tag].present? ? items.tagged_with(params[:tag]) : items
@@ -131,6 +145,10 @@ class ProjectsFinder < UnionFinder
params[:search].present? ? items.search(params[:search]) : items
end
+ def by_deleted_status(items)
+ params[:without_deleted].present? ? items.without_deleted : items
+ end
+
def sort(items)
params[:sort].present? ? items.sort_by_attribute(params[:sort]) : items.order_id_desc
end
diff --git a/app/finders/runner_jobs_finder.rb b/app/finders/runner_jobs_finder.rb
index 52340f94523..4fca4ec94f3 100644
--- a/app/finders/runner_jobs_finder.rb
+++ b/app/finders/runner_jobs_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RunnerJobsFinder
attr_reader :runner, :params
@@ -14,9 +16,11 @@ class RunnerJobsFinder
private
+ # rubocop: disable CodeReuse/ActiveRecord
def by_status(items)
return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
items.where(status: params[:status])
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 9d3772d7541..d3774746cb8 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -1,130 +1,149 @@
-# Snippets Finder
+# frozen_string_literal: true
+
+# Finder for retrieving snippets that a user can see, optionally scoped to a
+# project or snippets author.
+#
+# Basic usage:
+#
+# user = User.find(1)
+#
+# SnippetsFinder.new(user).execute
+#
+# To limit the snippets to a specific project, supply the `project:` option:
+#
+# user = User.find(1)
+# project = Project.find(1)
+#
+# SnippetsFinder.new(user, project: project).execute
+#
+# Limiting snippets to an author can be done by supplying the `author:` option:
+#
+# user = User.find(1)
+# project = Project.find(1)
+#
+# SnippetsFinder.new(user, author: user).execute
#
-# Used to filter Snippets collections by a set of params
+# To filter snippets using a specific visibility level, you can provide the
+# `scope:` option:
#
-# Arguments.
+# user = User.find(1)
+# project = Project.find(1)
#
-# current_user - The current user, nil also can be used.
-# params:
-# visibility (integer) - Individual snippet visibility: Public(20), internal(10) or private(0).
-# project (Project) - Project related.
-# author (User) - Author related.
+# SnippetsFinder.new(user, author: user, scope: :are_public).execute
#
-# params are optional
+# Valid `scope:` values are:
+#
+# * `:are_private`
+# * `:are_internal`
+# * `:are_public`
+#
+# Any other value will be ignored.
class SnippetsFinder < UnionFinder
- include Gitlab::Allowable
include FinderMethods
- attr_accessor :current_user, :project, :params
+ attr_accessor :current_user, :project, :author, :scope
- def initialize(current_user, params = {})
+ def initialize(current_user = nil, params = {})
@current_user = current_user
- @params = params
@project = params[:project]
- end
-
- def execute
- items = init_collection
- items = by_author(items)
- items = by_visibility(items)
-
- items.fresh
- end
-
- private
-
- def init_collection
- if project.present?
- authorized_snippets_from_project
- else
- authorized_snippets
+ @author = params[:author]
+ @scope = params[:scope].to_s
+
+ if project && author
+ raise(
+ ArgumentError,
+ 'Filtering by both an author and a project is not supported, ' \
+ 'as this finder is not optimised for this use case'
+ )
end
end
- def authorized_snippets_from_project
- if can?(current_user, :read_project_snippet, project)
- if project.team.member?(current_user)
- project.snippets
+ def execute
+ base =
+ if project
+ snippets_for_a_single_project
else
- project.snippets.public_to_user(current_user)
+ snippets_for_multiple_projects
end
- else
- Snippet.none
- end
- end
- def authorized_snippets
- # This query was intentionally converted to a raw one to get it work in Rails 5.0.
- # In Rails 5.0 and 5.1 there's a bug: https://github.com/rails/arel/issues/531
- # Please convert it back when on rails 5.2 as it works again as expected since 5.2.
- Snippet.where("#{feature_available_projects} OR #{not_project_related}")
- .public_or_visible_to_user(current_user)
+ base.with_optional_visibility(visibility_from_scope).fresh
end
- # Returns a collection of projects that is either public or visible to the
- # logged in user.
+ # Produces a query that retrieves snippets from multiple projects.
#
- # A caller must pass in a block to modify individual parts of
- # the query, e.g. to apply .with_feature_available_for_user on top of it.
- # This is useful for performance as we can stick those additional filters
- # at the bottom of e.g. the UNION.
- def projects_for_user
- return yield(Project.public_to_user) unless current_user
-
- # If the current_user is allowed to see all projects,
- # we can shortcut and just return.
- return yield(Project.all) if current_user.full_private_access?
-
- authorized_projects = yield(Project.where('EXISTS (?)', current_user.authorizations_for_projects))
-
- levels = Gitlab::VisibilityLevel.levels_for_user(current_user)
- visible_projects = yield(Project.where(visibility_level: levels))
-
- # We use a UNION here instead of OR clauses since this results in better
- # performance.
- union = Gitlab::SQL::Union.new([authorized_projects.select('projects.id'), visible_projects.select('projects.id')])
+ # The resulting query will, depending on the user's permissions, include the
+ # following collections of snippets:
+ #
+ # 1. Snippets that don't belong to any project.
+ # 2. Snippets of projects that are visible to the current user (e.g. snippets
+ # in public projects).
+ # 3. Snippets of projects that the current user is a member of.
+ #
+ # Each collection is constructed in isolation, allowing for greater control
+ # over the resulting SQL query.
+ def snippets_for_multiple_projects
+ queries = [global_snippets]
+
+ if Ability.allowed?(current_user, :read_cross_project)
+ queries << snippets_of_visible_projects
+ queries << snippets_of_authorized_projects if current_user
+ end
- Project.from("(#{union.to_sql}) AS #{Project.table_name}")
+ find_union(queries, Snippet)
end
- def feature_available_projects
- # Don't return any project related snippets if the user cannot read cross project
- return table[:id].eq(nil).to_sql unless Ability.allowed?(current_user, :read_cross_project)
-
- projects = projects_for_user do |part|
- part.with_feature_available_for_user(:snippets, current_user)
- end.select(:id)
-
- # This query was intentionally converted to a raw one to get it work in Rails 5.0.
- # In Rails 5.0 and 5.1 there's a bug: https://github.com/rails/arel/issues/531
- # Please convert it back when on rails 5.2 as it works again as expected since 5.2.
- "snippets.project_id IN (#{projects.to_sql})"
+ def snippets_for_a_single_project
+ Snippet.for_project_with_user(project, current_user)
end
- def not_project_related
- table[:project_id].eq(nil).to_sql
+ def global_snippets
+ snippets_for_author_or_visible_to_user.only_global_snippets
end
- def table
- Snippet.arel_table
+ # Returns the snippets that the current user (logged in or not) can view.
+ def snippets_of_visible_projects
+ snippets_for_author_or_visible_to_user
+ .only_include_projects_visible_to(current_user)
+ .only_include_projects_with_snippets_enabled
end
- def by_visibility(items)
- visibility = params[:visibility] || visibility_from_scope
-
- return items unless visibility
+ # Returns the snippets that the currently logged in user has access to by
+ # being a member of the project the snippets belong to.
+ #
+ # This method requires that `current_user` returns a `User` instead of `nil`,
+ # and is optimised for this specific scenario.
+ def snippets_of_authorized_projects
+ base = author ? snippets_for_author : Snippet.all
+
+ base
+ .only_include_projects_with_snippets_enabled(include_private: true)
+ .only_include_authorized_projects(current_user)
+ end
- items.where(visibility_level: visibility)
+ def snippets_for_author_or_visible_to_user
+ if author
+ snippets_for_author
+ elsif current_user
+ Snippet.visible_to_or_authored_by(current_user)
+ else
+ Snippet.public_to_user
+ end
end
- def by_author(items)
- return items unless params[:author]
+ def snippets_for_author
+ base = author.snippets
- items.where(author_id: params[:author].id)
+ if author == current_user
+ # If the current user is also the author of all snippets, then we can
+ # include private snippets.
+ base
+ else
+ base.public_to_user(current_user)
+ end
end
def visibility_from_scope
- case params[:scope].to_s
+ case scope
when 'are_private'
Snippet::PRIVATE
when 'are_internal'
diff --git a/app/finders/tags_finder.rb b/app/finders/tags_finder.rb
index b474f0805dc..2ffd46245e9 100644
--- a/app/finders/tags_finder.rb
+++ b/app/finders/tags_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TagsFinder
def initialize(repository, params)
@repository = repository
diff --git a/app/finders/template_finder.rb b/app/finders/template_finder.rb
new file mode 100644
index 00000000000..3e483716064
--- /dev/null
+++ b/app/finders/template_finder.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class TemplateFinder
+ include Gitlab::Utils::StrongMemoize
+
+ VENDORED_TEMPLATES = HashWithIndifferentAccess.new(
+ dockerfiles: ::Gitlab::Template::DockerfileTemplate,
+ gitignores: ::Gitlab::Template::GitignoreTemplate,
+ gitlab_ci_ymls: ::Gitlab::Template::GitlabCiYmlTemplate
+ ).freeze
+
+ class << self
+ def build(type, project, params = {})
+ if type.to_s == 'licenses'
+ LicenseTemplateFinder.new(project, params) # rubocop: disable CodeReuse/Finder
+ else
+ new(type, project, params)
+ end
+ end
+ end
+
+ attr_reader :type, :project, :params
+
+ attr_reader :vendored_templates
+ private :vendored_templates
+
+ def initialize(type, project, params = {})
+ @type = type
+ @project = project
+ @params = params
+
+ @vendored_templates = VENDORED_TEMPLATES.fetch(type)
+ end
+
+ def execute
+ if params[:name]
+ vendored_templates.find(params[:name])
+ else
+ vendored_templates.all
+ end
+ end
+end
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 6e9c8ea6fde..d001e18fea9 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# TodosFinder
#
# Used to filter Todos by set of params
@@ -21,6 +23,8 @@ class TodosFinder
NONE = '0'.freeze
+ TODO_TYPES = Set.new(%w(Issue MergeRequest Epic)).freeze
+
attr_accessor :current_user, :params
def initialize(current_user, params = {})
@@ -43,6 +47,13 @@ class TodosFinder
sort(items)
end
+ # Returns `true` if the current user has any todos for the given target.
+ #
+ # target - The value of the `target_type` column, such as `Issue`.
+ def any_for_target?(target)
+ current_user.todos.any_for_target?(target)
+ end
+
private
def action_id?
@@ -70,14 +81,11 @@ class TodosFinder
end
def author
- return @author if defined?(@author)
-
- @author =
+ strong_memoize(:author) do
if author? && params[:author_id] != NONE
User.find(params[:author_id])
- else
- nil
end
+ end
end
def project?
@@ -89,17 +97,9 @@ class TodosFinder
end
def project
- return @project if defined?(@project)
-
- if project?
- @project = Project.find(params[:project_id])
-
- @project = nil if @project.pending_delete?
- else
- @project = nil
+ strong_memoize(:project) do
+ Project.find_without_deleted(params[:project_id]) if project?
end
-
- @project
end
def group
@@ -109,7 +109,7 @@ class TodosFinder
end
def type?
- type.present? && %w(Issue MergeRequest Epic).include?(type)
+ type.present? && TODO_TYPES.include?(type)
end
def type
@@ -117,57 +117,55 @@ class TodosFinder
end
def sort(items)
- params[:sort] ? items.sort_by_attribute(params[:sort]) : items.order_id_desc
+ if params[:sort]
+ items.sort_by_attribute(params[:sort])
+ else
+ items.order_id_desc
+ end
end
def by_action(items)
if action?
- items = items.where(action: to_action_id)
+ items.for_action(to_action_id)
+ else
+ items
end
-
- items
end
def by_action_id(items)
if action_id?
- items = items.where(action: action_id)
+ items.for_action(action_id)
+ else
+ items
end
-
- items
end
def by_author(items)
if author?
- items = items.where(author_id: author.try(:id))
+ items.for_author(author)
+ else
+ items
end
-
- items
end
def by_project(items)
if project?
- items = items.where(project: project)
+ items.for_project(project)
+ else
+ items
end
-
- items
end
def by_group(items)
if group?
- groups = group.self_and_descendants
- project_todos = items.where(project_id: Project.where(group: groups).select(:id))
- group_todos = items.where(group_id: groups.select(:id))
-
- union = Gitlab::SQL::Union.new([project_todos, group_todos])
- items = Todo.from("(#{union.to_sql}) #{Todo.table_name}")
+ items.for_group_and_descendants(group)
+ else
+ items
end
-
- items
end
def by_state(items)
- case params[:state].to_s
- when 'done'
+ if params[:state].to_s == 'done'
items.done
else
items.pending
@@ -176,9 +174,9 @@ class TodosFinder
def by_type(items)
if type?
- items = items.where(target_type: type)
+ items.for_type(type)
+ else
+ items
end
-
- items
end
end
diff --git a/app/finders/union_finder.rb b/app/finders/union_finder.rb
index 33cd1a491f3..c3b02f7e52f 100644
--- a/app/finders/union_finder.rb
+++ b/app/finders/union_finder.rb
@@ -1,9 +1,13 @@
+# frozen_string_literal: true
+
class UnionFinder
def find_union(segments, klass)
- if segments.length > 1
- union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
+ unless klass < FromUnion
+ raise TypeError, "#{klass.inspect} must include the FromUnion module"
+ end
- klass.where("#{klass.table_name}.id IN (#{union.to_sql})")
+ if segments.length > 1
+ klass.from_union(segments)
else
segments.first
end
diff --git a/app/finders/user_finder.rb b/app/finders/user_finder.rb
new file mode 100644
index 00000000000..556be4c4338
--- /dev/null
+++ b/app/finders/user_finder.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+# A simple finding for obtaining a single User.
+#
+# While using `User.find_by` directly is straightforward, it can lead to a lot
+# of code duplication. Sometimes we just want to find a user by an ID, other
+# times we may want to exclude blocked user. By using this finder (and extending
+# it whenever necessary) we can keep this logic in one place.
+class UserFinder
+ def initialize(username_or_id)
+ @username_or_id = username_or_id
+ end
+
+ # Tries to find a User by id, returning nil if none could be found.
+ def find_by_id
+ User.find_by_id(@username_or_id)
+ end
+
+ # Tries to find a User by id, raising a `ActiveRecord::RecordNotFound` if it could
+ # not be found.
+ def find_by_id!
+ User.find(@username_or_id)
+ end
+
+ # Tries to find a User by username, returning nil if none could be found.
+ def find_by_username
+ User.find_by_username(@username_or_id)
+ end
+
+ # Tries to find a User by username, raising a `ActiveRecord::RecordNotFound` if it could
+ # not be found.
+ def find_by_username!
+ User.find_by_username!(@username_or_id)
+ end
+
+ # Tries to find a User by username or id, returning nil if none could be found.
+ def find_by_id_or_username
+ if input_is_id?
+ find_by_id
+ else
+ find_by_username
+ end
+ end
+
+ # Tries to find a User by username or id, raising a `ActiveRecord::RecordNotFound` if it could
+ # not be found.
+ def find_by_id_or_username!
+ if input_is_id?
+ find_by_id!
+ else
+ find_by_username!
+ end
+ end
+
+ def input_is_id?
+ @username_or_id.is_a?(Numeric) || @username_or_id =~ /^\d+$/
+ end
+end
diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb
index 876f086a3ef..3f2e813d381 100644
--- a/app/finders/user_recent_events_finder.rb
+++ b/app/finders/user_recent_events_finder.rb
@@ -1,6 +1,9 @@
+# frozen_string_literal: true
+
# Get user activity feed for projects common for a user and a logged in user
#
# - current_user: The user viewing the events
+# WARNING: does not consider project feature visibility!
# - user: The user for which to load the events
# - params:
# - offset: The page of events to return
@@ -21,17 +24,20 @@ class UserRecentEventsFinder
@params = params
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
return Event.none unless can?(current_user, :read_user_profile, target_user)
recent_events(params[:offset] || 0)
.joins(:project)
.with_associations
- .limit_recent(LIMIT, params[:offset])
+ .limit_recent(params[:limit].presence || LIMIT, params[:offset])
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ # rubocop: disable CodeReuse/ActiveRecord
def recent_events(offset)
sql = <<~SQL
(#{projects}) AS projects_for_join
@@ -42,26 +48,15 @@ class UserRecentEventsFinder
# Workaround for https://github.com/rails/rails/issues/24193
Event.from([Arel.sql(sql)])
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def target_events
Event.where(author: target_user)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def projects
- # Compile a list of projects `current_user` interacted with
- # and `target_user` is allowed to see.
-
- authorized = target_user
- .project_interactions
- .joins(:project_authorizations)
- .where(project_authorizations: { user: current_user })
- .select(:id)
-
- visible = target_user
- .project_interactions
- .where(visibility_level: Gitlab::VisibilityLevel.levels_for_user(current_user))
- .select(:id)
-
- Gitlab::SQL::Union.new([authorized, visible]).to_sql
+ target_user.project_interactions.to_sql
end
end
diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb
index 65824a51919..81ae50c0bd1 100644
--- a/app/finders/users_finder.rb
+++ b/app/finders/users_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# UsersFinder
#
# Used to filter users by set of params
@@ -44,7 +46,7 @@ class UsersFinder
def by_username(users)
return users unless params[:username]
- users.where(username: params[:username])
+ users.by_username(params[:username])
end
def by_search(users)
@@ -65,18 +67,22 @@ class UsersFinder
users.active
end
+ # rubocop: disable CodeReuse/ActiveRecord
def by_external_identity(users)
return users unless current_user&.admin? && params[:extern_uid] && params[:provider]
users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid]))
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def by_external(users)
return users = users.where.not(external: true) unless current_user&.admin?
return users unless params[:external]
users.external
end
+ # rubocop: enable CodeReuse/ActiveRecord
def by_2fa(users)
case params[:two_factor]
diff --git a/app/finders/users_with_pending_todos_finder.rb b/app/finders/users_with_pending_todos_finder.rb
new file mode 100644
index 00000000000..461bd92a366
--- /dev/null
+++ b/app/finders/users_with_pending_todos_finder.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# Finder that given a target (e.g. an issue) finds all the users that have
+# pending todos for said target.
+class UsersWithPendingTodosFinder
+ attr_reader :target
+
+ # target - The target, such as an Issue or MergeRequest.
+ def initialize(target)
+ @target = target
+ end
+
+ def execute
+ User.for_todos(target.todos.pending)
+ end
+end
diff --git a/app/graphql/functions/base_function.rb b/app/graphql/functions/base_function.rb
index 42fb8f99acc..2512ecbd255 100644
--- a/app/graphql/functions/base_function.rb
+++ b/app/graphql/functions/base_function.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Functions
class BaseFunction < GraphQL::Function
end
diff --git a/app/graphql/functions/echo.rb b/app/graphql/functions/echo.rb
index e5bf109b8d7..3104486faac 100644
--- a/app/graphql/functions/echo.rb
+++ b/app/graphql/functions/echo.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Functions
class Echo < BaseFunction
argument :text, GraphQL::STRING_TYPE
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index 8755a1a62e7..06d26309b5b 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitlabSchema < GraphQL::Schema
use BatchLoader::GraphQL
use Gitlab::Graphql::Authorize
diff --git a/app/graphql/mutations/concerns/mutations/resolves_project.rb b/app/graphql/mutations/concerns/mutations/resolves_project.rb
index 0dd1f264a52..da9814e88b0 100644
--- a/app/graphql/mutations/concerns/mutations/resolves_project.rb
+++ b/app/graphql/mutations/concerns/mutations/resolves_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mutations
module ResolvesProject
extend ActiveSupport::Concern
diff --git a/app/graphql/mutations/merge_requests/base.rb b/app/graphql/mutations/merge_requests/base.rb
index 2149e72e2df..54f01c99d78 100644
--- a/app/graphql/mutations/merge_requests/base.rb
+++ b/app/graphql/mutations/merge_requests/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mutations
module MergeRequests
class Base < BaseMutation
diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb
index 89b7f9dad6f..459933af9d3 100644
--- a/app/graphql/resolvers/base_resolver.rb
+++ b/app/graphql/resolvers/base_resolver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Resolvers
class BaseResolver < GraphQL::Schema::Resolver
end
diff --git a/app/graphql/resolvers/concerns/resolves_pipelines.rb b/app/graphql/resolvers/concerns/resolves_pipelines.rb
index 9ec45378d8e..8fd26d85994 100644
--- a/app/graphql/resolvers/concerns/resolves_pipelines.rb
+++ b/app/graphql/resolvers/concerns/resolves_pipelines.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ResolvesPipelines
extend ActiveSupport::Concern
diff --git a/app/graphql/resolvers/full_path_resolver.rb b/app/graphql/resolvers/full_path_resolver.rb
index 4eb28aaed6c..8d3da33e8d2 100644
--- a/app/graphql/resolvers/full_path_resolver.rb
+++ b/app/graphql/resolvers/full_path_resolver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Resolvers
module FullPathResolver
extend ActiveSupport::Concern
diff --git a/app/graphql/resolvers/merge_request_pipelines_resolver.rb b/app/graphql/resolvers/merge_request_pipelines_resolver.rb
index 00b51ee1381..b371f1335f8 100644
--- a/app/graphql/resolvers/merge_request_pipelines_resolver.rb
+++ b/app/graphql/resolvers/merge_request_pipelines_resolver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Resolvers
class MergeRequestPipelinesResolver < BaseResolver
include ::ResolvesPipelines
diff --git a/app/graphql/resolvers/merge_request_resolver.rb b/app/graphql/resolvers/merge_request_resolver.rb
index 9f2d348e95f..b87c95217f7 100644
--- a/app/graphql/resolvers/merge_request_resolver.rb
+++ b/app/graphql/resolvers/merge_request_resolver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Resolvers
class MergeRequestResolver < BaseResolver
argument :iid, GraphQL::ID_TYPE,
@@ -8,6 +10,7 @@ module Resolvers
alias_method :project, :object
+ # rubocop: disable CodeReuse/ActiveRecord
def resolve(iid:)
return unless project.present?
@@ -16,5 +19,6 @@ module Resolvers
results.each { |mr| loader.call(mr.iid.to_s, mr) }
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/graphql/resolvers/project_pipelines_resolver.rb b/app/graphql/resolvers/project_pipelines_resolver.rb
index 7f175a3b26c..86094c46c2a 100644
--- a/app/graphql/resolvers/project_pipelines_resolver.rb
+++ b/app/graphql/resolvers/project_pipelines_resolver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Resolvers
class ProjectPipelinesResolver < BaseResolver
include ResolvesPipelines
diff --git a/app/graphql/resolvers/project_resolver.rb b/app/graphql/resolvers/project_resolver.rb
index ec115bad896..ac7c9b0ce2e 100644
--- a/app/graphql/resolvers/project_resolver.rb
+++ b/app/graphql/resolvers/project_resolver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Resolvers
class ProjectResolver < BaseResolver
prepend FullPathResolver
diff --git a/app/graphql/types/base_enum.rb b/app/graphql/types/base_enum.rb
index b45a845f74f..cf43fea45e6 100644
--- a/app/graphql/types/base_enum.rb
+++ b/app/graphql/types/base_enum.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class BaseEnum < GraphQL::Schema::Enum
end
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index c5740a334d7..2b2ea64c00b 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class BaseField < GraphQL::Schema::Field
prepend Gitlab::Graphql::Authorize
diff --git a/app/graphql/types/base_input_object.rb b/app/graphql/types/base_input_object.rb
index 309e336e6c8..aebed035d3b 100644
--- a/app/graphql/types/base_input_object.rb
+++ b/app/graphql/types/base_input_object.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class BaseInputObject < GraphQL::Schema::InputObject
end
diff --git a/app/graphql/types/base_interface.rb b/app/graphql/types/base_interface.rb
index 69e72dc5808..3451a195c33 100644
--- a/app/graphql/types/base_interface.rb
+++ b/app/graphql/types/base_interface.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
module BaseInterface
include GraphQL::Schema::Interface
diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb
index 754adf4c04d..82b78abd573 100644
--- a/app/graphql/types/base_object.rb
+++ b/app/graphql/types/base_object.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class BaseObject < GraphQL::Schema::Object
prepend Gitlab::Graphql::Present
diff --git a/app/graphql/types/base_scalar.rb b/app/graphql/types/base_scalar.rb
index c0aa38be239..719bc808f47 100644
--- a/app/graphql/types/base_scalar.rb
+++ b/app/graphql/types/base_scalar.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class BaseScalar < GraphQL::Schema::Scalar
end
diff --git a/app/graphql/types/base_union.rb b/app/graphql/types/base_union.rb
index 36337fc6ee5..30a5668c0bb 100644
--- a/app/graphql/types/base_union.rb
+++ b/app/graphql/types/base_union.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class BaseUnion < GraphQL::Schema::Union
end
diff --git a/app/graphql/types/ci/pipeline_status_enum.rb b/app/graphql/types/ci/pipeline_status_enum.rb
index 2c12e5001d8..c19ddf5bb25 100644
--- a/app/graphql/types/ci/pipeline_status_enum.rb
+++ b/app/graphql/types/ci/pipeline_status_enum.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
module Ci
class PipelineStatusEnum < BaseEnum
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index bbb7d9354d0..2bbffad4563 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
module Ci
class PipelineType < BaseObject
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 88cd2adc6dc..fb740b6fb1c 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class MergeRequestType < BaseObject
expose_permissions Types::PermissionTypes::MergeRequest
diff --git a/app/graphql/types/permission_types/base_permission_type.rb b/app/graphql/types/permission_types/base_permission_type.rb
index 934ed572e56..26a71e2bfbb 100644
--- a/app/graphql/types/permission_types/base_permission_type.rb
+++ b/app/graphql/types/permission_types/base_permission_type.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
module PermissionTypes
class BasePermissionType < BaseObject
diff --git a/app/graphql/types/permission_types/ci/pipeline.rb b/app/graphql/types/permission_types/ci/pipeline.rb
index 942539c7cf7..73e44a33eba 100644
--- a/app/graphql/types/permission_types/ci/pipeline.rb
+++ b/app/graphql/types/permission_types/ci/pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
module PermissionTypes
module Ci
diff --git a/app/graphql/types/permission_types/merge_request.rb b/app/graphql/types/permission_types/merge_request.rb
index 5c21f6ee9c6..13995d3ea8f 100644
--- a/app/graphql/types/permission_types/merge_request.rb
+++ b/app/graphql/types/permission_types/merge_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
module PermissionTypes
class MergeRequest < BasePermissionType
diff --git a/app/graphql/types/permission_types/project.rb b/app/graphql/types/permission_types/project.rb
index 755699a4415..ab37c282fe5 100644
--- a/app/graphql/types/permission_types/project.rb
+++ b/app/graphql/types/permission_types/project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
module PermissionTypes
class Project < BasePermissionType
@@ -14,7 +16,7 @@ module Types
:create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages,
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
- :create_pages, :destroy_pages
+ :create_pages, :destroy_pages, :read_pages_content
end
end
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 97707215b4e..7b879608b34 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class ProjectType < BaseObject
expose_permissions Types::PermissionTypes::Project
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 010ec2d7942..7c41716b82a 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class QueryType < BaseObject
graphql_name 'Query'
diff --git a/app/graphql/types/time_type.rb b/app/graphql/types/time_type.rb
index 2333d82ad1e..f045a50e672 100644
--- a/app/graphql/types/time_type.rb
+++ b/app/graphql/types/time_type.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Types
class TimeType < BaseScalar
graphql_name 'Time'
diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb
index 5d27d30eaa3..a4f19480539 100644
--- a/app/helpers/accounts_helper.rb
+++ b/app/helpers/accounts_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AccountsHelper
def incoming_email_token_enabled?
current_user.incoming_email_token && Gitlab::IncomingEmail.supports_issue_creation?
diff --git a/app/helpers/active_sessions_helper.rb b/app/helpers/active_sessions_helper.rb
index 97b6dac67c5..84aa1160f12 100644
--- a/app/helpers/active_sessions_helper.rb
+++ b/app/helpers/active_sessions_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveSessionsHelper
# Maps a device type as defined in `ActiveSession` to an svg icon name and
# outputs the icon html.
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index f48db024e3f..ed13c5cfdd6 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AppearancesHelper
def brand_title
current_appearance&.title.presence || 'GitLab Community Edition'
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 0190aa90763..74042f0bae8 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,15 +1,27 @@
+# frozen_string_literal: true
+
require 'digest/md5'
require 'uri'
module ApplicationHelper
# See https://docs.gitlab.com/ee/development/ee_features.html#code-in-app-views
+ # rubocop: disable CodeReuse/ActiveRecord
def render_if_exists(partial, locals = {})
- render(partial, locals) if lookup_context.exists?(partial, [], true)
+ render(partial, locals) if partial_exists?(partial)
+ end
+
+ def partial_exists?(partial)
+ lookup_context.exists?(partial, [], true)
+ end
+
+ def template_exists?(template)
+ lookup_context.exists?(template, [], false)
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Check if a particular controller is the current one
#
- # args - One or more controller names to check
+ # args - One or more controller names to check (using path notation when inside namespaces)
#
# Examples
#
@@ -17,6 +29,11 @@ module ApplicationHelper
# current_controller?(:tree) # => true
# current_controller?(:commits) # => false
# current_controller?(:commits, :tree) # => true
+ #
+ # # On Admin::ApplicationController
+ # current_controller?(:application) # => true
+ # current_controller?('admin/application') # => true
+ # current_controller?('gitlab/application') # => false
def current_controller?(*args)
args.any? do |v|
v.to_s.downcase == controller.controller_name || v.to_s.downcase == controller.controller_path
@@ -49,6 +66,7 @@ module ApplicationHelper
# Define whenever show last push event
# with suggestion to create MR
+ # rubocop: disable CodeReuse/ActiveRecord
def show_last_push_widget?(event)
# Skip if event is not about added or modified non-master branch
return false unless event && event.last_push_to_non_root? && !event.rm_ref?
@@ -66,6 +84,7 @@ module ApplicationHelper
true
end
+ # rubocop: enable CodeReuse/ActiveRecord
def hexdigest(string)
Digest::SHA1.hexdigest string
@@ -106,11 +125,11 @@ module ApplicationHelper
#
# Returns an HTML-safe String
def time_ago_with_tooltip(time, placement: 'top', html_class: '', short_format: false)
- css_classes = short_format ? 'js-short-timeago' : 'js-timeago'
- css_classes << " #{html_class}" unless html_class.blank?
+ css_classes = [short_format ? 'js-short-timeago' : 'js-timeago']
+ css_classes << html_class unless html_class.blank?
element = content_tag :time, l(time, format: "%b %d, %Y"),
- class: css_classes,
+ class: css_classes.join(' '),
title: l(time.to_time.in_time_zone, format: :timeago_tooltip),
datetime: time.to_time.getutc.iso8601,
data: {
@@ -154,17 +173,7 @@ module ApplicationHelper
without = options.delete(:without)
add_label = options.delete(:label)
- exist_opts = {
- state: params[:state],
- scope: params[:scope],
- milestone_title: params[:milestone_title],
- assignee_id: params[:assignee_id],
- author_id: params[:author_id],
- search: params[:search],
- label_name: params[:label_name]
- }
-
- options = exist_opts.merge(options)
+ options = request.query_parameters.merge(options)
if without.present?
without.each do |key|
@@ -273,7 +282,8 @@ module ApplicationHelper
mergeRequests: merge_requests_project_autocomplete_sources_path(object),
labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
milestones: milestones_project_autocomplete_sources_path(object),
- commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id])
+ commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
+ snippets: snippets_project_autocomplete_sources_path(object)
}
end
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 2bdf2c2c120..086bb38ce9a 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationSettingsHelper
extend self
@@ -5,7 +7,6 @@ module ApplicationSettingsHelper
:gravatar_enabled?,
:password_authentication_enabled_for_web?,
:akismet_enabled?,
- :koding_enabled?,
to: :'Gitlab::CurrentSettings.current_application_settings'
def user_oauth_applications?
@@ -73,12 +74,12 @@ module ApplicationSettingsHelper
def oauth_providers_checkboxes
button_based_providers.map do |source|
disabled = Gitlab::CurrentSettings.disabled_oauth_sign_in_sources.include?(source.to_s)
- css_class = 'btn'
- css_class << ' active' unless disabled
+ css_class = ['btn']
+ css_class << 'active' unless disabled
checkbox_name = 'application_setting[enabled_oauth_sign_in_sources][]'
name = Gitlab::Auth::OAuth::Provider.label_for(source)
- label_tag(checkbox_name, class: css_class) do
+ label_tag(checkbox_name, class: css_class.join(' ')) do
check_box_tag(checkbox_name, source, !disabled,
autocomplete: 'off',
id: name.tr(' ', '_')) + name
@@ -106,41 +107,6 @@ module ApplicationSettingsHelper
options_for_select(options, selected)
end
- def sidekiq_queue_options_for_select
- options_for_select(Sidekiq::Queue.all.map(&:name), @application_setting.sidekiq_throttling_queues)
- end
-
- def circuitbreaker_failure_count_help_text
- health_link = link_to(s_('AdminHealthPageLink|health page'), admin_health_check_path)
- api_link = link_to(s_('CircuitBreakerApiLink|circuitbreaker api'), help_page_path("api/repository_storage_health"))
- message = _("The number of failures of after which GitLab will completely "\
- "prevent access to the storage. The number of failures can be "\
- "reset in the admin interface: %{link_to_health_page} or using "\
- "the %{api_documentation_link}.")
- message = message % { link_to_health_page: health_link, api_documentation_link: api_link }
-
- message.html_safe
- end
-
- def circuitbreaker_access_retries_help_text
- _('The number of attempts GitLab will make to access a storage.')
- end
-
- def circuitbreaker_failure_reset_time_help_text
- _("The time in seconds GitLab will keep failure information. When no "\
- "failures occur during this time, information about the mount is reset.")
- end
-
- def circuitbreaker_storage_timeout_help_text
- _("The time in seconds GitLab will try to access storage. After this time a "\
- "timeout error will be raised.")
- end
-
- def circuitbreaker_check_interval_help_text
- _("The time in seconds between storage checks. When a previous check did "\
- "complete yet, GitLab will skip a check.")
- end
-
def visible_attributes
[
:admin_notification_email,
@@ -149,14 +115,10 @@ module ApplicationSettingsHelper
:akismet_api_key,
:akismet_enabled,
:allow_local_requests_from_hooks_and_services,
+ :archive_builds_in_human_readable,
:authorized_keys_enabled,
:auto_devops_enabled,
:auto_devops_domain,
- :circuitbreaker_access_retries,
- :circuitbreaker_check_interval,
- :circuitbreaker_failure_count_threshold,
- :circuitbreaker_failure_reset_time,
- :circuitbreaker_storage_timeout,
:clientside_sentry_dsn,
:clientside_sentry_enabled,
:container_registry_token_expire_delay,
@@ -193,8 +155,6 @@ module ApplicationSettingsHelper
:housekeeping_incremental_repack_period,
:html_emails_enabled,
:import_sources,
- :koding_enabled,
- :koding_url,
:max_artifacts_size,
:max_attachment_size,
:max_pages_size,
@@ -220,6 +180,7 @@ module ApplicationSettingsHelper
:recaptcha_enabled,
:recaptcha_private_key,
:recaptcha_site_key,
+ :receive_max_input_size,
:repository_checks_enabled,
:repository_storages,
:require_two_factor_authentication,
@@ -231,9 +192,6 @@ module ApplicationSettingsHelper
:session_expire_delay,
:shared_runners_enabled,
:shared_runners_text,
- :sidekiq_throttling_enabled,
- :sidekiq_throttling_factor,
- :sidekiq_throttling_queues,
:sign_in_text,
:signup_enabled,
:terminal_max_session_time,
@@ -254,9 +212,17 @@ module ApplicationSettingsHelper
:usage_ping_enabled,
:instance_statistics_visibility_private,
:user_default_external,
+ :user_show_add_ssh_key_message,
+ :user_default_internal_regex,
:user_oauth_applications,
:version_check_enabled,
- :web_ide_clientside_preview_enabled
+ :web_ide_clientside_preview_enabled,
+ :diff_max_patch_bytes,
+ :commit_email_hostname
]
end
+
+ def expanded_by_default?
+ Rails.env.test?
+ end
end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index 18f0979fc86..44f85e9c0f8 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze
LDAP_PROVIDER = /\Aldap/
@@ -22,6 +24,23 @@ module AuthHelper
Gitlab::Auth::OAuth::Provider.label_for(name)
end
+ def form_based_provider_priority
+ ['crowd', /^ldap/, 'kerberos']
+ end
+
+ def form_based_provider_with_highest_priority
+ @form_based_provider_with_highest_priority ||= begin
+ form_based_provider_priority.each do |provider_regexp|
+ highest_priority = form_based_providers.find { |provider| provider.match?(provider_regexp) }
+ break highest_priority unless highest_priority.nil?
+ end
+ end
+ end
+
+ def form_based_auth_provider_has_active_class?(provider)
+ form_based_provider_with_highest_priority == provider
+ end
+
def form_based_provider?(name)
[LDAP_PROVIDER, 'crowd'].any? { |pattern| pattern === name.to_s }
end
@@ -64,9 +83,11 @@ module AuthHelper
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def auth_active?(provider)
current_user.identities.exists?(provider: provider.to_s)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def unlink_allowed?(provider)
%w(saml cas3).exclude?(provider.to_s)
diff --git a/app/helpers/auto_devops_helper.rb b/app/helpers/auto_devops_helper.rb
index 7b076728685..516c8a353ea 100644
--- a/app/helpers/auto_devops_helper.rb
+++ b/app/helpers/auto_devops_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AutoDevopsHelper
def show_auto_devops_callout?(project)
Feature.get(:auto_devops_banner_disabled).off? &&
@@ -24,6 +26,7 @@ module AutoDevopsHelper
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def cluster_ingress_ip(project)
project
.cluster_ingresses
@@ -32,6 +35,7 @@ module AutoDevopsHelper
.pluck(:external_ip)
.first
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
index 494f785e305..5906ddabee4 100644
--- a/app/helpers/avatars_helper.rb
+++ b/app/helpers/avatars_helper.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
module AvatarsHelper
- def project_icon(project_id, options = {})
- source_icon(Project, project_id, options)
+ def project_icon(project, options = {})
+ source_icon(project, options)
end
- def group_icon(group_id, options = {})
- source_icon(Group, group_id, options)
+ def group_icon(group, options = {})
+ source_icon(group, options)
end
# Takes both user and email and returns the avatar_icon by
@@ -20,7 +22,7 @@ module AvatarsHelper
end
def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true)
- user = User.find_by_any_email(email.try(:downcase))
+ user = User.find_by_any_email(email)
if user
avatar_icon_for_user(user, size, scale, only_path: only_path)
else
@@ -108,16 +110,11 @@ module AvatarsHelper
private
- def source_icon(klass, source_id, options = {})
- source =
- if source_id.respond_to?(:avatar_url)
- source_id
- else
- klass.find_by_full_path(source_id)
- end
+ def source_icon(source, options = {})
+ avatar_url = source.try(:avatar_url)
- if source.avatar_url
- image_tag source.avatar_url, options
+ if avatar_url
+ image_tag avatar_url, options
else
source_identicon(source, options)
end
@@ -125,9 +122,9 @@ module AvatarsHelper
def source_identicon(source, options = {})
bg_key = (source.id % 7) + 1
- options[:class] ||= ''
- options[:class] << ' identicon'
- options[:class] << " bg#{bg_key}"
+
+ options[:class] =
+ [*options[:class], "identicon bg#{bg_key}"].join(' ')
content_tag(:div, class: options[:class].strip) do
source.name[0, 1].upcase
diff --git a/app/helpers/award_emoji_helper.rb b/app/helpers/award_emoji_helper.rb
index 86b19368cfd..b97a95629f7 100644
--- a/app/helpers/award_emoji_helper.rb
+++ b/app/helpers/award_emoji_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AwardEmojiHelper
def toggle_award_url(awardable)
return url_for([:toggle_award_emoji, awardable]) unless @project || awardable.is_a?(Note)
diff --git a/app/helpers/blame_helper.rb b/app/helpers/blame_helper.rb
index 089d9e3e387..82c74e2416d 100644
--- a/app/helpers/blame_helper.rb
+++ b/app/helpers/blame_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlameHelper
def age_map_duration(blame_groups, project)
now = Time.zone.now
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 7eb45ddd117..638744a1426 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,7 +1,8 @@
+# frozen_string_literal: true
+
module BlobHelper
- def highlight(blob_name, blob_content, repository: nil, plain: false)
- plain ||= blob_content.length > Blob::MAXIMUM_TEXT_HIGHLIGHT_SIZE
- highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository)
+ def highlight(file_name, file_content, language: nil, plain: false)
+ highlighted = Gitlab::Highlight.highlight(file_name, file_content, plain: plain, language: language)
raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>)
end
@@ -148,7 +149,9 @@ module BlobHelper
# example of Javascript) we tell the browser of the victim not to
# execute untrusted data.
def safe_content_type(blob)
- if blob.text?
+ if blob.extension == 'svg'
+ blob.mime_type
+ elsif blob.text?
'text/plain; charset=utf-8'
elsif blob.image?
blob.content_type
@@ -157,60 +160,50 @@ module BlobHelper
end
end
- def cached_blob?
- stale = stale?(etag: @blob.id) # The #stale? method sets cache headers.
-
- # Because we are opionated we set the cache headers ourselves.
- response.cache_control[:public] = @project.public?
+ def content_disposition(blob, inline)
+ return 'attachment' if blob.extension == 'svg'
- response.cache_control[:max_age] =
- if @ref && @commit && @ref == @commit.id
- # This is a link to a commit by its commit SHA. That means that the blob
- # is immutable. The only reason to invalidate the cache is if the commit
- # was deleted or if the user lost access to the repository.
- Blob::CACHE_TIME_IMMUTABLE
- else
- # A branch or tag points at this blob. That means that the expected blob
- # value may change over time.
- Blob::CACHE_TIME
- end
-
- response.etag = @blob.id
- !stale
+ inline ? 'inline' : 'attachment'
end
- def licenses_for_select
- return @licenses_for_select if defined?(@licenses_for_select)
+ def ref_project
+ @ref_project ||= @target_project || @project
+ end
- licenses = Licensee::License.all
+ def template_dropdown_names(items)
+ grouped = items.group_by(&:category)
+ categories = grouped.keys
- @licenses_for_select = {
- Popular: licenses.select(&:featured).map { |license| { name: license.name, id: license.key } },
- Other: licenses.reject(&:featured).map { |license| { name: license.name, id: license.key } }
- }
+ categories.each_with_object({}) do |category, hash|
+ hash[category] = grouped[category].map do |item|
+ { name: item.name, id: item.key }
+ end
+ end
end
+ private :template_dropdown_names
- def ref_project
- @ref_project ||= @target_project || @project
+ def licenses_for_select(project)
+ @licenses_for_select ||= template_dropdown_names(TemplateFinder.build(:licenses, project).execute)
end
- def gitignore_names
- @gitignore_names ||= Gitlab::Template::GitignoreTemplate.dropdown_names
+ def gitignore_names(project)
+ @gitignore_names ||= template_dropdown_names(TemplateFinder.build(:gitignores, project).execute)
end
- def gitlab_ci_ymls
- @gitlab_ci_ymls ||= Gitlab::Template::GitlabCiYmlTemplate.dropdown_names(params[:context])
+ def gitlab_ci_ymls(project)
+ @gitlab_ci_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_ymls, project).execute)
end
- def dockerfile_names
- @dockerfile_names ||= Gitlab::Template::DockerfileTemplate.dropdown_names
+ def dockerfile_names(project)
+ @dockerfile_names ||= template_dropdown_names(TemplateFinder.build(:dockerfiles, project).execute)
end
- def blob_editor_paths
+ def blob_editor_paths(project)
{
'relative-url-root' => Rails.application.config.relative_url_root,
'assets-prefix' => Gitlab::Application.config.assets.prefix,
- 'blob-language' => @blob && @blob.language.try(:ace_mode)
+ 'blob-filename' => @blob && @blob.path,
+ 'project-id' => project.id
}
end
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index af878bcf9a0..be1e7016a1e 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BoardsHelper
def board
@board ||= @board || @boards.first
@@ -57,8 +59,8 @@ module BoardsHelper
{
toggle: "dropdown",
- list_labels_path: labels_filter_path(true, include_ancestor_groups: true),
- labels: labels_filter_path(true, include_descendant_groups: include_descendant_groups),
+ list_labels_path: labels_filter_path_with_defaults(only_group_labels: true, include_ancestor_groups: true),
+ labels: labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: include_descendant_groups),
labels_endpoint: @labels_endpoint,
namespace_path: @namespace_path,
project_path: @project&.path,
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
index 07b1fc3d7cf..eadf48205fc 100644
--- a/app/helpers/branches_helper.rb
+++ b/app/helpers/branches_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BranchesHelper
def project_branches
options_for_select(@project.repository.branch_names, @project.default_branch)
diff --git a/app/helpers/breadcrumbs_helper.rb b/app/helpers/breadcrumbs_helper.rb
index e88fe6bcd7e..b067376cea0 100644
--- a/app/helpers/breadcrumbs_helper.rb
+++ b/app/helpers/breadcrumbs_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BreadcrumbsHelper
def add_to_breadcrumbs(text, link)
@breadcrumbs_extra_links ||= []
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
index 0a15c29cfb5..289cb44f1e8 100644
--- a/app/helpers/broadcast_messages_helper.rb
+++ b/app/helpers/broadcast_messages_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BroadcastMessagesHelper
def broadcast_message(message)
return unless message.present?
@@ -8,18 +10,17 @@ module BroadcastMessagesHelper
end
def broadcast_message_style(broadcast_message)
- style = ''
+ style = []
if broadcast_message.color.present?
style << "background-color: #{broadcast_message.color}"
- style << '; ' if broadcast_message.font.present?
end
if broadcast_message.font.present?
style << "color: #{broadcast_message.font}"
end
- style
+ style.join('; ')
end
def broadcast_message_status(broadcast_message)
diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb
index 4ec63fdaffc..3c8caec3fe5 100644
--- a/app/helpers/builds_helper.rb
+++ b/app/helpers/builds_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BuildsHelper
def build_summary(build, skip: false)
if build.has_trace?
@@ -12,10 +14,10 @@ module BuildsHelper
end
def sidebar_build_class(build, current_build)
- build_class = ''
- build_class += ' active' if build.id === current_build.id
- build_class += ' retried' if build.retried?
- build_class
+ build_class = []
+ build_class << 'active' if build.id === current_build.id
+ build_class << 'retried' if build.retried?
+ build_class.join(' ')
end
def javascript_build_options
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 0171a880164..7f071d55a6b 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ButtonHelper
# Output a "Copy to Clipboard" button
#
@@ -61,31 +63,36 @@ module ButtonHelper
dropdown_description = http_dropdown_description(protocol)
append_url = project.http_url_to_repo if append_link
- dropdown_item_with_description(protocol, dropdown_description, href: append_url)
+ dropdown_item_with_description(protocol, dropdown_description, href: append_url, data: { clone_type: 'http' })
end
def http_dropdown_description(protocol)
if current_user.try(:require_password_creation_for_git?)
_("Set a password on your account to pull or push via %{protocol}.") % { protocol: protocol }
- else
+ elsif current_user.try(:require_personal_access_token_creation_for_git_auth?)
_("Create a personal access token on your account to pull or push via %{protocol}.") % { protocol: protocol }
end
end
def ssh_clone_button(project, append_link: true)
- dropdown_description = _("You won't be able to pull or push project code via SSH until you add an SSH key to your profile") if current_user.try(:require_ssh_key?)
+ if Gitlab::CurrentSettings.user_show_add_ssh_key_message? &&
+ current_user.try(:require_ssh_key?)
+ dropdown_description = _("You won't be able to pull or push project code via SSH until you add an SSH key to your profile")
+ end
+
append_url = project.ssh_url_to_repo if append_link
- dropdown_item_with_description('SSH', dropdown_description, href: append_url)
+ dropdown_item_with_description('SSH', dropdown_description, href: append_url, data: { clone_type: 'ssh' })
end
- def dropdown_item_with_description(title, description, href: nil)
+ def dropdown_item_with_description(title, description, href: nil, data: nil)
button_content = content_tag(:strong, title, class: 'dropdown-menu-inner-title')
button_content << content_tag(:span, description, class: 'dropdown-menu-inner-content') if description
content_tag (href ? :a : :span),
(href ? button_content : title),
class: "#{title.downcase}-selector",
- href: (href if href)
+ href: (href if href),
+ data: (data if data)
end
end
diff --git a/app/helpers/calendar_helper.rb b/app/helpers/calendar_helper.rb
index c54b91b0ce5..ad4116fc3da 100644
--- a/app/helpers/calendar_helper.rb
+++ b/app/helpers/calendar_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CalendarHelper
def calendar_url_options
{ format: :ics,
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 330959e536d..923a06a0512 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# DEPRECATED
#
@@ -18,6 +20,8 @@ module CiStatusHelper
'passed with warnings'
when 'manual'
'waiting for manual action'
+ when 'scheduled'
+ 'waiting for delayed job'
else
status
end
@@ -37,6 +41,8 @@ module CiStatusHelper
s_('CiStatusText|passed')
when 'manual'
s_('CiStatusText|blocked')
+ when 'scheduled'
+ s_('CiStatusText|delayed')
else
# All states are already being translated inside the detailed statuses:
# :running => Gitlab::Ci::Status::Running
@@ -81,6 +87,8 @@ module CiStatusHelper
'status_skipped'
when 'manual'
'status_manual'
+ when 'scheduled'
+ 'status_scheduled'
else
'status_canceled'
end
@@ -121,11 +129,6 @@ module CiStatusHelper
render_status_with_link('pipeline', pipeline.status, path, tooltip_placement: tooltip_placement)
end
- def no_runners_for_project?(project)
- project.runners.blank? &&
- Ci::Runner.instance_type.blank?
- end
-
def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16)
klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}"
title = "#{type.titleize}: #{ci_label_for_status(status)}"
diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb
index 8fd0b6f14c6..916dcb1a308 100644
--- a/app/helpers/clusters_helper.rb
+++ b/app/helpers/clusters_helper.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
module ClustersHelper
- def has_multiple_clusters?(project)
+ # EE overrides this
+ def has_multiple_clusters?
false
end
@@ -8,7 +11,7 @@ module ClustersHelper
return unless show_gcp_signup_offer?
content_tag :section, class: 'no-animate expanded' do
- render 'projects/clusters/gcp_signup_offer_banner'
+ render 'clusters/clusters/gcp_signup_offer_banner'
end
end
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 89fe90fd801..d52cfd6e37a 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CommitsHelper
# Returns a link to the commit author. If the author has a matching user and
# is a member of the current @project it will link to the team member page.
@@ -210,17 +212,6 @@ module CommitsHelper
Sanitize.clean(string, remove_contents: true)
end
- def limited_commits(commits)
- if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
- [
- commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE),
- commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE
- ]
- else
- [commits, 0]
- end
- end
-
def commit_path(project, commit, merge_request: nil)
if merge_request&.persisted?
diffs_project_merge_request_path(project, merge_request, commit_id: commit.id)
diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb
index 2df5b5d1695..9ece8b0bc5b 100644
--- a/app/helpers/compare_helper.rb
+++ b/app/helpers/compare_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CompareHelper
def create_mr_button?(from = params[:from], to = params[:to], project = @project)
from.present? &&
diff --git a/app/helpers/components_helper.rb b/app/helpers/components_helper.rb
index 8893209b314..d0ef86851ad 100644
--- a/app/helpers/components_helper.rb
+++ b/app/helpers/components_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ComponentsHelper
def gitlab_workhorse_version
if request.headers['Gitlab-Workhorse'].present?
diff --git a/app/helpers/conversational_development_index_helper.rb b/app/helpers/conversational_development_index_helper.rb
index 1ff54415811..37e5bb325fb 100644
--- a/app/helpers/conversational_development_index_helper.rb
+++ b/app/helpers/conversational_development_index_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ConversationalDevelopmentIndexHelper
def score_level(score)
if score < 33.33
diff --git a/app/helpers/cookies_helper.rb b/app/helpers/cookies_helper.rb
new file mode 100644
index 00000000000..3a7e9987190
--- /dev/null
+++ b/app/helpers/cookies_helper.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module CookiesHelper
+ def set_secure_cookie(key, value, httponly: false, permanent: false)
+ cookie_jar = permanent ? cookies.permanent : cookies
+
+ cookie_jar[key] = { value: value, secure: Gitlab.config.gitlab.https, httponly: httponly }
+ end
+end
diff --git a/app/helpers/count_helper.rb b/app/helpers/count_helper.rb
index 5cd98f40f78..13839474e1f 100644
--- a/app/helpers/count_helper.rb
+++ b/app/helpers/count_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CountHelper
def approximate_count_with_delimiters(count_data, model)
count = count_data[model]
@@ -6,4 +8,18 @@ module CountHelper
number_with_delimiter(count)
end
+
+ # This will approximate the fork count by checking all counting all fork network
+ # memberships, and deducting 1 for each root of the fork network.
+ # This might be inacurate as the root of the fork network might have been deleted.
+ #
+ # This makes querying this information a lot more effecient and it should be
+ # accurate enough for the instance wide statistics
+ def approximate_fork_count_with_delimiters(count_data)
+ fork_network_count = count_data[ForkNetwork]
+ fork_network_member_count = count_data[ForkNetworkMember]
+ approximate_fork_count = fork_network_member_count - fork_network_count
+
+ number_with_delimiter(approximate_fork_count)
+ end
end
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index 19aa55a8d49..d90ef8903a7 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
module DashboardHelper
def assigned_issues_dashboard_path
- issues_dashboard_path(assignee_id: current_user.id)
+ issues_dashboard_path(assignee_username: current_user.username)
end
def assigned_mrs_dashboard_path
- merge_requests_dashboard_path(assignee_id: current_user.id)
+ merge_requests_dashboard_path(assignee_username: current_user.username)
end
def dashboard_nav_links
diff --git a/app/helpers/defer_script_tag_helper.rb b/app/helpers/defer_script_tag_helper.rb
index e1567556e5e..d91c6d52683 100644
--- a/app/helpers/defer_script_tag_helper.rb
+++ b/app/helpers/defer_script_tag_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeferScriptTagHelper
# Override the default ActionView `javascript_include_tag` helper to support page specific deferred loading
def javascript_include_tag(*sources)
diff --git a/app/helpers/deploy_tokens_helper.rb b/app/helpers/deploy_tokens_helper.rb
index bd921322476..80a5bb44c69 100644
--- a/app/helpers/deploy_tokens_helper.rb
+++ b/app/helpers/deploy_tokens_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeployTokensHelper
def expand_deploy_tokens_section?(deploy_token)
deploy_token.persisted? ||
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 1bb82fd8150..b6844d36052 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffHelper
def mark_inline_diffs(old_line, new_line)
old_diffs, new_diffs = Gitlab::Diff::InlineDiff.new(old_line, new_line).inline_diffs
@@ -39,7 +41,8 @@ module DiffHelper
line_num_class = %w[diff-line-num unfold js-unfold]
line_num_class << 'js-unfold-bottom' if bottom
- html = ''
+ html = []
+
if old_pos
html << content_tag(:td, '...', class: [*line_num_class, 'old_line'], data: { linenumber: old_pos })
html << content_tag(:td, text, class: [*content_line_class, 'left-side']) if view == :parallel
@@ -50,7 +53,7 @@ module DiffHelper
html << content_tag(:td, text, class: [*content_line_class, ('right-side' if view == :parallel)])
end
- html.html_safe
+ html.join.html_safe
end
def diff_line_content(line)
@@ -210,14 +213,14 @@ module DiffHelper
params[:w] == '1'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def params_with_whitespace
hide_whitespace? ? request.query_parameters.except(:w) : request.query_parameters.merge(w: 1)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def toggle_whitespace_link(url, options)
- options[:class] ||= ''
- options[:class] << ' btn btn-default'
-
+ options[:class] = [*options[:class], 'btn btn-default'].join(' ')
link_to "#{hide_whitespace? ? 'Show' : 'Hide'} whitespace changes", url, class: options[:class]
end
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index 5a2360b4661..4b6c5b215e8 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DropdownsHelper
def dropdown_tag(toggle_text, options: {}, &block)
content_tag :div, class: "dropdown #{options[:wrapper_class] if options.key?(:wrapper_class)}" do
@@ -10,7 +12,7 @@ module DropdownsHelper
dropdown_output = dropdown_toggle(toggle_text, data_attr, options)
dropdown_output << content_tag(:div, class: "dropdown-menu dropdown-select #{options[:dropdown_class] if options.key?(:dropdown_class)}") do
- output = ""
+ output = []
if options.key?(:title)
output << dropdown_title(options[:title])
@@ -31,8 +33,7 @@ module DropdownsHelper
end
output << dropdown_loading
-
- output.html_safe
+ output.join.html_safe
end
dropdown_output.html_safe
@@ -50,7 +51,7 @@ module DropdownsHelper
def dropdown_title(title, options: {})
content_tag :div, class: "dropdown-title" do
- title_output = ""
+ title_output = []
if options.fetch(:back, false)
title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-back", aria: { label: "Go back" }, type: "button") do
@@ -66,7 +67,7 @@ module DropdownsHelper
end
end
- title_output.html_safe
+ title_output.join.html_safe
end
end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index c86a26ac30f..2d2e89a2a50 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EmailsHelper
include AppearancesHelper
@@ -49,8 +51,8 @@ module EmailsHelper
def reset_token_expire_message
link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email))
- msg = "This link is valid for #{password_reset_token_valid_time}. "
- msg << "After it expires, you can #{link_tag}."
+ "This link is valid for #{password_reset_token_valid_time}. " \
+ "After it expires, you can #{link_tag}."
end
def header_logo
diff --git a/app/helpers/emoji_helper.rb b/app/helpers/emoji_helper.rb
index 482f68f412b..51b7fd7f352 100644
--- a/app/helpers/emoji_helper.rb
+++ b/app/helpers/emoji_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EmojiHelper
def emoji_icon(*args)
raw Gitlab::Emoji.gl_emoji_tag(*args)
diff --git a/app/helpers/environment_helper.rb b/app/helpers/environment_helper.rb
index 1e78a189c08..2b7320817ed 100644
--- a/app/helpers/environment_helper.rb
+++ b/app/helpers/environment_helper.rb
@@ -1,9 +1,13 @@
+# frozen_string_literal: true
+
module EnvironmentHelper
+ # rubocop: disable CodeReuse/ActiveRecord
def environment_for_build(project, build)
return unless build.environment
project.environments.find_by(name: build.expanded_environment_name)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def environment_link_for_build(project, build)
environment = environment_for_build(project, build)
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index c005ecbb56b..7b22bc8f98f 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EnvironmentsHelper
def environments_list_data
{
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index cb6f709c604..3ce2398f1de 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EventsHelper
ICON_NAMES_BY_EVENT_TYPE = {
'pushed to' => 'commit',
@@ -19,7 +21,7 @@ module EventsHelper
name = self_added ? 'You' : author.name
link_to name, user_path(author.username), title: name
else
- event.author_name
+ escape_once(event.author_name)
end
end
@@ -89,7 +91,14 @@ module EventsHelper
words << "##{event.target_iid}" if event.target_iid
words << "in"
elsif event.target
- words << "##{event.target_iid}:"
+ prefix =
+ if event.merge_request?
+ MergeRequest.reference_prefix
+ else
+ Issue.reference_prefix
+ end
+
+ words << "#{prefix}#{event.target_iid}:" if event.target_iid
words << event.target.title if event.target.respond_to?(:title)
words << "at"
end
@@ -110,10 +119,12 @@ module EventsHelper
event.note_target)
elsif event.note?
if event.note_target
- event_note_target_path(event)
+ event_note_target_url(event)
end
elsif event.push?
push_event_feed_url(event)
+ elsif event.created_project?
+ project_url(event.project)
end
end
@@ -145,28 +156,24 @@ module EventsHelper
end
end
- def event_note_target_path(event)
+ def event_note_target_url(event)
if event.commit_note?
- project_commit_path(event.project, event.note_target, anchor: dom_id(event.target))
+ project_commit_url(event.project, event.note_target, anchor: dom_id(event.target))
elsif event.project_snippet_note?
- project_snippet_path(event.project, event.note_target, anchor: dom_id(event.target))
+ project_snippet_url(event.project, event.note_target, anchor: dom_id(event.target))
else
- polymorphic_path([event.project.namespace.becomes(Namespace),
- event.project, event.note_target],
+ polymorphic_url([event.project.namespace.becomes(Namespace),
+ event.project, event.note_target],
anchor: dom_id(event.target))
end
end
def event_note_title_html(event)
if event.note_target
- text = raw("#{event.note_target_type} ") +
- if event.commit_note?
- content_tag(:span, event.note_target_reference, class: 'commit-sha')
- else
- event.note_target_reference
- end
-
- link_to(text, event_note_target_path(event), title: event.target_title, class: 'has-tooltip')
+ capture do
+ concat content_tag(:span, event.note_target_type, class: "event-target-type append-right-4")
+ concat link_to(event.note_target_reference, event_note_target_url(event), title: event.target_title, class: 'has-tooltip event-target-link append-right-4')
+ end
else
content_tag(:strong, '(deleted)')
end
@@ -179,17 +186,9 @@ module EventsHelper
"--broken encoding"
end
- def event_row_class(event)
- if event.body?
- "event-block"
- else
- "event-inline"
- end
- end
-
- def icon_for_event(note)
+ def icon_for_event(note, size: 24)
icon_name = ICON_NAMES_BY_EVENT_TYPE[note]
- sprite_icon(icon_name) if icon_name
+ sprite_icon(icon_name, size: size) if icon_name
end
def icon_for_profile_event(event)
@@ -199,8 +198,24 @@ module EventsHelper
end
else
content_tag :div, class: 'system-note-image user-avatar' do
- author_avatar(event, size: 32)
+ author_avatar(event, size: 40)
end
end
end
+
+ def inline_event_icon(event)
+ unless current_path?('users#show')
+ content_tag :span, class: "system-note-image-inline d-none d-sm-flex append-right-4 #{event.action_name.parameterize}-icon align-self-center" do
+ icon_for_event(event.action_name, size: 14)
+ end
+ end
+ end
+
+ def event_user_info(event)
+ content_tag(:div, class: "event-user-info") do
+ concat content_tag(:span, link_to_author(event), class: "author_name")
+ concat "&nbsp;".html_safe
+ concat content_tag(:span, event.author.to_reference, class: "username")
+ end
+ end
end
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index f062a91a166..62be591ec47 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ExploreHelper
def filter_projects_path(options = {})
exist_opts = {
diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb
index 8cf890b74a8..e36d63b2946 100644
--- a/app/helpers/external_wiki_helper.rb
+++ b/app/helpers/external_wiki_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ExternalWikiHelper
def get_project_wiki_path(project)
external_wiki_service = project.external_wiki
diff --git a/app/helpers/favicon_helper.rb b/app/helpers/favicon_helper.rb
index 3a5342a8d9d..4a809731d97 100644
--- a/app/helpers/favicon_helper.rb
+++ b/app/helpers/favicon_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module FaviconHelper
def favicon_extension_whitelist
FaviconUploader::EXTENSION_WHITELIST
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
index 905e2002592..5705ee54cee 100644
--- a/app/helpers/form_helper.rb
+++ b/app/helpers/form_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module FormHelper
def form_errors(model, type: 'form')
return unless model.errors.any?
diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb
index 8ab394384f3..5edc6dcf454 100644
--- a/app/helpers/git_helper.rb
+++ b/app/helpers/git_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GitHelper
def strip_gpg_signature(text)
text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "")
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index 61e12b0f31e..04cf43be452 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Shorter routing method for some project items
module GitlabRoutingHelper
extend ActiveSupport::Concern
diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb
index 1022070ab6f..49b15cde009 100644
--- a/app/helpers/graph_helper.rb
+++ b/app/helpers/graph_helper.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
module GraphHelper
def refs(repo, commit)
- refs = commit.ref_names(repo).join(' ')
+ refs = [commit.ref_names(repo).join(' ')]
# append note count
notes_count = @graph.notes[commit.id]
refs << "[#{pluralize(notes_count, 'note')}]" if notes_count > 0
- refs
+ refs.join
end
def parents_zip_spaces(parents, parent_spaces)
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 5b51d2f2425..e9b9b9b7721 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -1,4 +1,15 @@
+# frozen_string_literal: true
+
module GroupsHelper
+ def group_overview_nav_link_paths
+ %w[
+ groups#show
+ groups#activity
+ groups#subgroups
+ analytics#show
+ ]
+ end
+
def group_nav_link_paths
%w[groups#projects groups#edit badges#index ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]
end
@@ -43,22 +54,22 @@ module GroupsHelper
def group_title(group, name = nil, url = nil)
@has_group_title = true
- full_title = ''
+ full_title = []
group.ancestors.reverse.each_with_index do |parent, index|
if index > 0
add_to_breadcrumb_dropdown(group_title_link(parent, hidable: false, show_avatar: true, for_dropdown: true), location: :before)
else
- full_title += breadcrumb_list_item group_title_link(parent, hidable: false)
+ full_title << breadcrumb_list_item(group_title_link(parent, hidable: false))
end
end
- full_title += render "layouts/nav/breadcrumbs/collapsed_dropdown", location: :before, title: _("Show parent subgroups")
+ full_title << render("layouts/nav/breadcrumbs/collapsed_dropdown", location: :before, title: _("Show parent subgroups"))
- full_title += breadcrumb_list_item group_title_link(group)
- full_title += ' &middot; '.html_safe + link_to(simple_sanitize(name), url, class: 'group-path breadcrumb-item-text js-breadcrumb-item-text') if name
+ full_title << breadcrumb_list_item(group_title_link(group))
+ full_title << ' &middot; '.html_safe + link_to(simple_sanitize(name), url, class: 'group-path breadcrumb-item-text js-breadcrumb-item-text') if name
- full_title.html_safe
+ full_title.join.html_safe
end
def projects_lfs_status(group)
@@ -129,6 +140,10 @@ module GroupsHelper
can?(current_user, "read_group_#{resource}".to_sym, @group)
end
+ if can?(current_user, :read_cluster, @group) && Feature.enabled?(:group_clusters)
+ links << :kubernetes
+ end
+
if can?(current_user, :admin_group, @group)
links << :settings
end
@@ -138,15 +153,8 @@ module GroupsHelper
def group_title_link(group, hidable: false, show_avatar: false, for_dropdown: false)
link_to(group_path(group), class: "group-path #{'breadcrumb-item-text' unless for_dropdown} js-breadcrumb-item-text #{'hidable' if hidable}") do
- output =
- if (group.try(:avatar_url) || show_avatar) && !Rails.env.test?
- group_icon(group, class: "avatar-tile", width: 15, height: 15)
- else
- ""
- end
-
- output << simple_sanitize(group.name)
- output.html_safe
+ icon = group_icon(group, class: "avatar-tile", width: 15, height: 15) if (group.try(:avatar_url) || show_avatar) && !Rails.env.test?
+ [icon, simple_sanitize(group.name)].join.html_safe
end
end
diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb
index 0a356ba55d2..c4b39939192 100644
--- a/app/helpers/hooks_helper.rb
+++ b/app/helpers/hooks_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module HooksHelper
def link_to_test_hook(hook, trigger)
path = case hook
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 41084ec686f..b0f63de2fb8 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'json'
module IconsHelper
@@ -47,9 +49,10 @@ module IconsHelper
end
end
- css_classes = size ? "s#{size}" : ""
- css_classes << " #{css_class}" unless css_class.blank?
- content_tag(:svg, content_tag(:use, "", { "xlink:href" => "#{sprite_icon_path}##{icon_name}" } ), class: css_classes.empty? ? nil : css_classes)
+ css_classes = []
+ css_classes << "s#{size}" if size
+ css_classes << "#{css_class}" unless css_class.blank?
+ content_tag(:svg, content_tag(:use, "", { "xlink:href" => "#{sprite_icon_path}##{icon_name}" } ), class: css_classes.empty? ? nil : css_classes.join(' '))
end
def external_snippet_icon(name)
@@ -62,16 +65,18 @@ module IconsHelper
names = "key"
when "two-factor"
names = "key"
+ when "google_oauth2"
+ names = "google"
end
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
end
def spinner(text = nil, visible = false)
- css_class = 'loading'
- css_class << ' hide' unless visible
+ css_class = ['loading']
+ css_class << 'hide' unless visible
- content_tag :div, class: css_class do
+ content_tag :div, class: css_class.join(' ') do
icon('spinner spin') + text
end
end
@@ -84,7 +89,7 @@ module IconsHelper
end
end
- def visibility_level_icon(level, fw: true)
+ def visibility_level_icon(level, fw: true, options: {})
name =
case level
when Gitlab::VisibilityLevel::PRIVATE
@@ -95,14 +100,17 @@ module IconsHelper
'globe'
end
- name << " fw" if fw
+ name = [name]
+ name << "fw" if fw
- icon(name)
+ icon(name.join(' '), options)
end
def file_type_icon_class(type, mode, name)
if type == 'folder'
icon_class = 'folder'
+ elsif type == 'archive'
+ icon_class = 'archive'
elsif mode == '120000'
icon_class = 'share'
else
@@ -147,6 +155,6 @@ module IconsHelper
private
def known_sprites
- @known_sprites ||= JSON.parse(File.read(Rails.root.join('node_modules/@gitlab-org/gitlab-svgs/dist/icons.json')))['icons']
+ @known_sprites ||= JSON.parse(File.read(Rails.root.join('node_modules/@gitlab/svgs/dist/icons.json')))['icons']
end
end
diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb
index 4664b1728c4..49171df1433 100644
--- a/app/helpers/import_helper.rb
+++ b/app/helpers/import_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ImportHelper
include ::Gitlab::Utils::StrongMemoize
@@ -5,6 +7,10 @@ module ImportHelper
false
end
+ def sanitize_project_name(name)
+ name.gsub(/[^\w\-]/, '-')
+ end
+
def import_project_target(owner, name)
namespace = current_user.can_create_group? ? owner : current_user.namespace_path
"#{namespace}/#{name}"
@@ -77,7 +83,7 @@ module ImportHelper
private
def github_project_url(full_path)
- URI.join(github_root_url, full_path).to_s
+ Gitlab::Utils.append_path(github_root_url, full_path)
end
def github_root_url
@@ -89,6 +95,6 @@ module ImportHelper
end
def gitea_project_url(full_path)
- URI.join(@gitea_host_url, full_path).to_s
+ Gitlab::Utils.append_path(@gitea_host_url, full_path)
end
end
diff --git a/app/helpers/instance_configuration_helper.rb b/app/helpers/instance_configuration_helper.rb
index cee319f20bc..f695be32743 100644
--- a/app/helpers/instance_configuration_helper.rb
+++ b/app/helpers/instance_configuration_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module InstanceConfigurationHelper
def instance_configuration_cell_html(value, &block)
return '-' unless value.to_s.presence
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index c84ed8091c3..6069640b9c8 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module IssuablesHelper
include GitlabRoutingHelper
@@ -105,6 +107,7 @@ module IssuablesHelper
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def user_dropdown_label(user_id, default_label)
return default_label if user_id.nil?
return "Unassigned" if user_id == "0"
@@ -117,7 +120,9 @@ module IssuablesHelper
default_label
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def project_dropdown_label(project_id, default_label)
return default_label if project_id.nil?
return "Any project" if project_id == "0"
@@ -130,7 +135,9 @@ module IssuablesHelper
default_label
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def group_dropdown_label(group_id, default_label)
return default_label if group_id.nil?
return "Any group" if group_id == "0"
@@ -143,6 +150,7 @@ module IssuablesHelper
default_label
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def milestone_dropdown_label(milestone_title, default_label = "Milestone")
title =
@@ -167,33 +175,35 @@ module IssuablesHelper
end
def issuable_meta(issuable, project, text)
- output = ""
+ output = []
output << "Opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe
+
output << content_tag(:strong) do
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "d-none d-sm-inline", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "d-block d-sm-none")
if status = user_status(issuable.author)
- author_output << "&ensp; #{status}".html_safe
+ author_output << "#{status}".html_safe
end
author_output
end
- output << "&ensp;".html_safe
output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip', title: _('1st contribution!'))
- output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block")
+ output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block prepend-left-8")
output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none")
- output.html_safe
+ output.join.html_safe
end
+ # rubocop: disable CodeReuse/ActiveRecord
def issuable_todo(issuable)
if current_user
current_user.todos.find_by(target: issuable, state: :pending)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def issuable_labels_tooltip(labels, limit: 5)
first, last = labels.partition.with_index { |_, i| i < limit }
@@ -317,11 +327,15 @@ module IssuablesHelper
end
def issuable_button_visibility(issuable, closed)
+ return 'hidden' if issuable_button_hidden?(issuable, closed)
+ end
+
+ def issuable_button_hidden?(issuable, closed)
case issuable
when Issue
- issue_button_visibility(issuable, closed)
+ issue_button_hidden?(issuable, closed)
when MergeRequest
- merge_request_button_visibility(issuable, closed)
+ merge_request_button_hidden?(issuable, closed)
end
end
@@ -372,8 +386,8 @@ module IssuablesHelper
{
todo_text: "Add todo",
mark_text: "Mark todo as done",
- todo_icon: (is_collapsed ? icon('plus-square') : nil),
- mark_icon: (is_collapsed ? icon('check-square', class: 'todo-undone') : nil),
+ todo_icon: (is_collapsed ? sprite_icon('todo-add') : nil),
+ mark_icon: (is_collapsed ? sprite_icon('todo-done', css_class: 'todo-undone') : nil),
issuable_id: issuable.id,
issuable_type: issuable.class.name.underscore,
url: project_todos_path(@project),
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 96dc7ae1185..957ab06b0ca 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
module IssuesHelper
def issue_css_classes(issue)
- classes = "issue"
- classes << " closed" if issue.closed?
- classes << " today" if issue.today?
- classes
+ classes = ["issue"]
+ classes << "closed" if issue.closed?
+ classes << "today" if issue.today?
+ classes.join(' ')
end
# Returns an OpenStruct object suitable for use by <tt>options_from_collection_for_select</tt>
@@ -62,7 +64,11 @@ module IssuesHelper
end
def issue_button_visibility(issue, closed)
- return 'hidden' if issue.closed? == closed
+ return 'hidden' if issue_button_hidden?(issue, closed)
+ end
+
+ def issue_button_hidden?(issue, closed)
+ issue.closed? == closed || (!closed && issue.discussion_locked)
end
def confidential_icon(issue)
@@ -92,14 +98,6 @@ module IssuesHelper
end
end
- def award_user_authored_class(award)
- if award == 'thumbsdown' || award == 'thumbsup'
- 'user-authored js-user-authored'
- else
- ''
- end
- end
-
def awards_sort(awards)
awards.sort_by do |award, award_emojis|
if award == "thumbsup"
@@ -113,8 +111,8 @@ module IssuesHelper
end
def link_to_discussions_to_resolve(merge_request, single_discussion = nil)
- link_text = merge_request.to_reference
- link_text += " (discussion #{single_discussion.first_note.id})" if single_discussion
+ link_text = [merge_request.to_reference]
+ link_text << "(discussion #{single_discussion.first_note.id})" if single_discussion
path = if single_discussion
Gitlab::UrlBuilder.build(single_discussion.first_note)
@@ -123,7 +121,7 @@ module IssuesHelper
project_merge_request_path(project, merge_request)
end
- link_to link_text, path
+ link_to link_text.join(' '), path
end
def show_new_issue_link?(project)
diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb
index cd4075b340d..7cb6da26236 100644
--- a/app/helpers/javascript_helper.rb
+++ b/app/helpers/javascript_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module JavascriptHelper
def page_specific_javascript_tag(js)
javascript_include_tag asset_path(js)
diff --git a/app/helpers/kerberos_spnego_helper.rb b/app/helpers/kerberos_spnego_helper.rb
index f5b0aa7549a..c0eb8f83f56 100644
--- a/app/helpers/kerberos_spnego_helper.rb
+++ b/app/helpers/kerberos_spnego_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module KerberosSpnegoHelper
def allow_basic_auth?
true # different behavior in GitLab Enterprise Edition
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index c7df25cecef..39f661b5f0c 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module LabelsHelper
extend self
include ActionView::Helpers::TagHelper
@@ -129,20 +131,26 @@ module LabelsHelper
end
end
- def labels_filter_path(only_group_labels = false, include_ancestor_groups: true, include_descendant_groups: false)
- project = @target_project || @project
-
+ def labels_filter_path_with_defaults(only_group_labels: false, include_ancestor_groups: true, include_descendant_groups: false)
options = {}
options[:include_ancestor_groups] = include_ancestor_groups if include_ancestor_groups
options[:include_descendant_groups] = include_descendant_groups if include_descendant_groups
+ options[:only_group_labels] = only_group_labels if only_group_labels && @group
+ options[:format] = :json
+
+ labels_filter_path(options)
+ end
+
+ def labels_filter_path(options = {})
+ project = @target_project || @project
+ format = options.delete(:format)
if project
- project_labels_path(project, :json, options)
+ project_labels_path(project, format, options)
elsif @group
- options[:only_group_labels] = only_group_labels if only_group_labels
- group_labels_path(@group, :json, options)
+ group_labels_path(@group, format, options)
else
- dashboard_labels_path(:json)
+ dashboard_labels_path(format, options)
end
end
diff --git a/app/helpers/lazy_image_tag_helper.rb b/app/helpers/lazy_image_tag_helper.rb
index 603b9438e35..ac987a04895 100644
--- a/app/helpers/lazy_image_tag_helper.rb
+++ b/app/helpers/lazy_image_tag_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module LazyImageTagHelper
def placeholder_image
"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
@@ -11,9 +13,11 @@ module LazyImageTagHelper
options[:data] ||= {}
options[:data][:src] = path_to_image(source)
- options[:class] ||= ""
- options[:class] << " lazy"
+ # options[:class] can be either String or Array.
+ klass_opts = Array.wrap(options[:class])
+ klass_opts << "lazy"
+ options[:class] = klass_opts.join(' ')
source = placeholder_image
end
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index cbb971cf8b7..0d638b850b4 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'nokogiri'
module MarkupHelper
@@ -72,14 +74,21 @@ module MarkupHelper
# the tag contents are truncated without removing the closing tag.
def first_line_in_markdown(object, attribute, max_chars = nil, options = {})
md = markdown_field(object, attribute, options)
+ return nil unless md.present?
- text = truncate_visible(md, max_chars || md.length) if md.present?
+ tags = %w(a gl-emoji b pre code p span)
+ tags << 'img' if options[:allow_images]
- sanitize(
+ text = truncate_visible(md, max_chars || md.length)
+ text = sanitize(
text,
- tags: %w(a img gl-emoji b pre code p span),
+ tags: tags,
attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-src', 'data-name', 'data-unicode-version']
)
+
+ # since <img> tags are stripped, this can leave empty <a> tags hanging around
+ # (as our markdown wraps images in links)
+ options[:allow_images] ? text : strip_empty_link_tags(text).html_safe
end
def markdown(text, context = {})
@@ -107,23 +116,23 @@ module MarkupHelper
def markup(file_name, text, context = {})
context[:project] ||= @project
- context[:markdown_engine] ||= :redcarpet
+ context[:markdown_engine] ||= :redcarpet unless commonmark_for_repositories_enabled?
html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
prepare_for_rendering(html, context)
end
- def render_wiki_content(wiki_page)
+ def render_wiki_content(wiki_page, context = {})
text = wiki_page.content
return '' unless text.present?
- context = {
+ context.merge!(
pipeline: :wiki,
project: @project,
project_wiki: @project_wiki,
page_slug: wiki_page.slug,
- issuable_state_filter_enabled: true,
- markdown_engine: :redcarpet
- }
+ issuable_state_filter_enabled: true
+ )
+ context[:markdown_engine] ||= :redcarpet unless commonmark_for_repositories_enabled?
html =
case wiki_page.format
@@ -178,6 +187,10 @@ module MarkupHelper
end
end
+ def commonmark_for_repositories_enabled?
+ Feature.enabled?(:commonmark_for_repositories, default_enabled: true)
+ end
+
private
# Return +text+, truncated to +max_chars+ characters, excluding any HTML
@@ -229,6 +242,16 @@ module MarkupHelper
end
end
+ def strip_empty_link_tags(text)
+ scrubber = Loofah::Scrubber.new do |node|
+ node.remove if node.name == 'a' && node.content.blank?
+ end
+
+ # Use `Loofah` directly instead of `sanitize`
+ # as we still use the `rails-deprecated_sanitizer` gem
+ Loofah.fragment(text).scrub!(scrubber).to_s
+ end
+
def markdown_toolbar_button(options = {})
data = options[:data].merge({ container: 'body' })
content_tag :button,
diff --git a/app/helpers/mattermost_helper.rb b/app/helpers/mattermost_helper.rb
index 27ff4051c8d..b211fe5076a 100644
--- a/app/helpers/mattermost_helper.rb
+++ b/app/helpers/mattermost_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MattermostHelper
def mattermost_teams_options(teams)
teams.map do |team|
diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb
index a3129cac2b1..5a21403bc5e 100644
--- a/app/helpers/members_helper.rb
+++ b/app/helpers/members_helper.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
module MembersHelper
def remove_member_message(member, user: nil)
user = current_user if defined?(current_user)
+ text = 'Are you sure you want to'
- text = 'Are you sure you want to '
action =
if member.request?
if member.user == user
@@ -16,13 +18,12 @@ module MembersHelper
"remove #{member.user.name} from"
end
- text << action << " the #{member.source.human_name} #{member.real_source_type.humanize(capitalize: false)}?"
+ "#{text} #{action} the #{member.source.human_name} #{member.real_source_type.humanize(capitalize: false)}?"
end
def remove_member_title(member)
- text = " from #{member.real_source_type.humanize(capitalize: false)}"
-
- text.prepend(member.request? ? 'Deny access request' : 'Remove user')
+ action = member.request? ? 'Deny access request' : 'Remove user'
+ "#{action} from #{member.real_source_type.humanize(capitalize: false)}"
end
def leave_confirmation_message(member_source)
@@ -32,9 +33,6 @@ module MembersHelper
def filter_group_project_member_path(options = {})
options = params.slice(:search, :sort).merge(options)
-
- path = request.path
- path << "?#{options.to_param}"
- path
+ "#{request.path}?#{options.to_param}"
end
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 097be8a0643..23d7aa427bb 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequestsHelper
def new_mr_path_from_push_event(event)
target_project = event.project.default_merge_request_target
@@ -19,10 +21,10 @@ module MergeRequestsHelper
end
def mr_css_classes(mr)
- classes = "merge-request"
- classes << " closed" if mr.closed?
- classes << " merged" if mr.merged?
- classes
+ classes = ["merge-request"]
+ classes << "closed" if mr.closed?
+ classes << "merged" if mr.merged?
+ classes.join(' ')
end
def ci_build_details_path(merge_request)
@@ -78,7 +80,11 @@ module MergeRequestsHelper
end
def merge_request_button_visibility(merge_request, closed)
- return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork?
+ return 'hidden' if merge_request_button_hidden?(merge_request, closed)
+ end
+
+ def merge_request_button_hidden?(merge_request, closed)
+ merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork?
end
def merge_request_version_path(project, merge_request, merge_request_diff, start_sha = nil)
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 95da8f00aff..94a030d9d57 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MilestonesHelper
include EntityDateHelper
@@ -51,6 +53,7 @@ module MilestonesHelper
# Returns count of milestones for different states
# Uses explicit hash keys as the 'opened' state URL params differs from the db value
# and we need to add the total
+ # rubocop: disable CodeReuse/ActiveRecord
def milestone_counts(milestones)
counts = milestones.reorder(nil).group(:state).count
@@ -60,6 +63,7 @@ module MilestonesHelper
all: counts.values.sum || 0
}
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Show 'active' class if provided GET param matches check
# `or_blank` allows the function to return 'active' when given an empty param
@@ -119,20 +123,18 @@ module MilestonesHelper
title = date_type == :start ? "Start date" : "End date"
if date
- time_ago = time_ago_in_words(date)
- time_ago.slice!("about ")
-
- time_ago << if date.past?
- " ago"
- else
- " remaining"
- end
+ time_ago = time_ago_in_words(date).sub("about ", "")
+ state = if date.past?
+ "ago"
+ else
+ "remaining"
+ end
content = [
title,
"<br />",
date.to_s(:medium),
- "(#{time_ago})"
+ "(#{time_ago} #{state})"
].join(" ")
content.html_safe
diff --git a/app/helpers/milestones_routing_helper.rb b/app/helpers/milestones_routing_helper.rb
index a0b2616f224..a49b561533a 100644
--- a/app/helpers/milestones_routing_helper.rb
+++ b/app/helpers/milestones_routing_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MilestonesRoutingHelper
def milestone_path(milestone, *args)
if milestone.group_milestone?
diff --git a/app/helpers/mirror_helper.rb b/app/helpers/mirror_helper.rb
index 93ed22513ac..a4025730397 100644
--- a/app/helpers/mirror_helper.rb
+++ b/app/helpers/mirror_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MirrorHelper
def mirrors_form_data_attributes
{ project_mirror_endpoint: project_mirror_path(@project) }
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 30585cb403d..6c65e573307 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
module NamespacesHelper
def namespace_id_from(params)
params.dig(:project, :namespace_id) || params[:namespace_id]
end
+ # rubocop: disable CodeReuse/ActiveRecord
def namespaces_options(selected = :current_user, display_path: false, groups: nil, extra_group: nil, groups_only: false)
groups ||= current_user.manageable_groups
.eager_load(:route)
@@ -40,6 +43,7 @@ module NamespacesHelper
grouped_options_for_select(options, selected_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def namespace_icon(namespace, size = 40)
if namespace.is_a?(Group)
@@ -53,14 +57,16 @@ module NamespacesHelper
# Many importers create a temporary Group, so use the real
# group if one exists by that name to prevent duplicates.
+ # rubocop: disable CodeReuse/ActiveRecord
def dedup_extra_group(extra_group)
unless extra_group.persisted?
- existing_group = Group.find_by(name: extra_group.name)
+ existing_group = Group.find_by(path: extra_group.path)
extra_group = existing_group if existing_group&.persisted?
end
extra_group
end
+ # rubocop: enable CodeReuse/ActiveRecord
def options_for_group(namespaces, display_path:, type:)
group_label = type.pluralize
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index a84a39235d8..a7fe8c3d59c 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module NavHelper
def header_links
@header_links ||= get_header_links
@@ -17,10 +19,7 @@ module NavHelper
end
def page_gutter_class
- if current_path?('merge_requests#show') ||
- current_path?('projects/merge_requests/conflicts#show') ||
- current_path?('issues#show') ||
- current_path?('milestones#show')
+ if page_has_markdown?
if cookies[:collapsed_gutter] == 'true'
%w[page-gutter right-sidebar-collapsed]
@@ -48,6 +47,17 @@ module NavHelper
class_names
end
+ def show_separator?
+ Gitlab::Sherlock.enabled? || can?(current_user, :read_instance_statistics)
+ end
+
+ def page_has_markdown?
+ current_path?('merge_requests#show') ||
+ current_path?('projects/merge_requests/conflicts#show') ||
+ current_path?('issues#show') ||
+ current_path?('milestones#show')
+ end
+
private
def get_header_links
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 5404ead44f3..033686823a2 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module NotesHelper
def note_target_fields(note)
if note.noteable
@@ -108,7 +110,7 @@ module NotesHelper
end
def noteable_note_url(note)
- Gitlab::UrlBuilder.build(note)
+ Gitlab::UrlBuilder.build(note) if note.id
end
def form_resources
@@ -140,7 +142,7 @@ module NotesHelper
def initial_notes_data(autocomplete)
{
notesUrl: notes_url,
- notesIds: @notes.map(&:id),
+ notesIds: @noteable.notes.pluck(:id), # rubocop: disable CodeReuse/ActiveRecord
now: Time.now.to_i,
diffView: diff_view,
enableGFM: {
@@ -176,7 +178,7 @@ module NotesHelper
notesPath: notes_url,
totalNotes: issuable.discussions.length,
lastFetchedAt: Time.now.to_i
- }.to_json
+ }
end
def discussion_resolved_intro(discussion)
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index 3e42063224e..5318ab4ddef 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module NotificationsHelper
include IconsHelper
@@ -83,21 +85,11 @@ module NotificationsHelper
end
def notification_event_name(event)
- # All values from NotificationSetting::EMAIL_EVENTS
+ # All values from NotificationSetting.email_events
case event
when :success_pipeline
s_('NotificationEvent|Successful pipeline')
else
- N_('NotificationEvent|New note')
- N_('NotificationEvent|New issue')
- N_('NotificationEvent|Reopen issue')
- N_('NotificationEvent|Close issue')
- N_('NotificationEvent|Reassign issue')
- N_('NotificationEvent|New merge request')
- N_('NotificationEvent|Close merge request')
- N_('NotificationEvent|Reassign merge request')
- N_('NotificationEvent|Merge merge request')
- N_('NotificationEvent|Failed pipeline')
s_(event.to_s.humanize)
end
end
diff --git a/app/helpers/numbers_helper.rb b/app/helpers/numbers_helper.rb
index 45bd3606076..3c0b11c4d32 100644
--- a/app/helpers/numbers_helper.rb
+++ b/app/helpers/numbers_helper.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
module NumbersHelper
+ # rubocop: disable CodeReuse/ActiveRecord
def limited_counter_with_delimiter(resource, **options)
limit = options.fetch(:limit, 1000).to_i
count = resource.limit(limit + 1).count(:all)
@@ -8,4 +11,5 @@ module NumbersHelper
number_with_delimiter(count, options)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index 68d892393ef..5038dcf9746 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module PageLayoutHelper
def page_title(*titles)
@page_title ||= []
@@ -8,7 +10,7 @@ module PageLayoutHelper
@breadcrumb_title = @page_title.last
end
- # Segments are seperated by middot
+ # Segments are separated by middot
@page_title.join(" · ")
end
@@ -65,14 +67,14 @@ module PageLayoutHelper
end
def page_card_meta_tags
- tags = ''
+ tags = []
page_card_attributes.each_with_index do |pair, i|
tags << tag(:meta, property: "twitter:label#{i + 1}", content: pair[0])
tags << tag(:meta, property: "twitter:data#{i + 1}", content: pair[1])
end
- tags.html_safe
+ tags.join.html_safe
end
def header_title(title = nil, title_url = nil)
@@ -115,16 +117,16 @@ module PageLayoutHelper
end
def container_class
- css_class = "container-fluid"
+ css_class = ["container-fluid"]
unless fluid_layout
- css_class += " container-limited"
+ css_class << "container-limited"
end
if blank_container
- css_class += " container-blank"
+ css_class << "container-blank"
end
- css_class
+ css_class.join(' ')
end
end
diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb
index 83dd76a01dd..d05153c9d4b 100644
--- a/app/helpers/pagination_helper.rb
+++ b/app/helpers/pagination_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module PaginationHelper
def paginate_collection(collection, remote: nil)
if collection.is_a?(Kaminari::PaginatableWithoutCount)
diff --git a/app/helpers/performance_bar_helper.rb b/app/helpers/performance_bar_helper.rb
index d24efe37f5f..7518cec160c 100644
--- a/app/helpers/performance_bar_helper.rb
+++ b/app/helpers/performance_bar_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module PerformanceBarHelper
# This is a hack since using `alias_method :performance_bar_enabled?, :peek_enabled?`
# in WithPerformanceBar breaks tests (but works in the browser).
diff --git a/app/helpers/pipeline_schedules_helper.rb b/app/helpers/pipeline_schedules_helper.rb
index 4b9f6bd2caf..0e166106b32 100644
--- a/app/helpers/pipeline_schedules_helper.rb
+++ b/app/helpers/pipeline_schedules_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module PipelineSchedulesHelper
def timezone_data
ActiveSupport::TimeZone.all.map do |timezone|
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index fb523cb865b..f4f46b0fe96 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Helper methods for per-User preferences
module PreferencesHelper
def layout_choices
@@ -16,22 +18,20 @@ module PreferencesHelper
groups: _("Your Groups"),
todos: _("Your Todos"),
issues: _("Assigned Issues"),
- merge_requests: _("Assigned Merge Requests")
+ merge_requests: _("Assigned Merge Requests"),
+ operations: _("Operations Dashboard")
}.with_indifferent_access.freeze
# Returns an Array usable by a select field for more user-friendly option text
def dashboard_choices
- defined = User.dashboards
+ dashboards = User.dashboards.keys
- if defined.size != DASHBOARD_CHOICES.size
- # Ensure that anyone adding new options updates this method too
- raise "`User` defines #{defined.size} dashboard choices," \
- " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
- else
- defined.map do |key, _|
- # Use `fetch` so `KeyError` gets raised when a key is missing
- [DASHBOARD_CHOICES.fetch(key), key]
- end
+ validate_dashboard_choices!(dashboards)
+ dashboards -= excluded_dashboard_choices
+
+ dashboards.map do |key|
+ # Use `fetch` so `KeyError` gets raised when a key is missing
+ [DASHBOARD_CHOICES.fetch(key), key]
end
end
@@ -50,4 +50,20 @@ module PreferencesHelper
def user_color_scheme
Gitlab::ColorSchemes.for_user(current_user).css_class
end
+
+ private
+
+ # Ensure that anyone adding new options updates `DASHBOARD_CHOICES` too
+ def validate_dashboard_choices!(user_dashboards)
+ if user_dashboards.size != DASHBOARD_CHOICES.size
+ raise "`User` defines #{user_dashboards.size} dashboard choices," \
+ " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
+ end
+ end
+
+ # List of dashboard choice to be excluded from CE.
+ # EE would override this.
+ def excluded_dashboard_choices
+ ['operations']
+ end
end
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
index e7aa92e6e5c..df318de740a 100644
--- a/app/helpers/profiles_helper.rb
+++ b/app/helpers/profiles_helper.rb
@@ -1,4 +1,20 @@
+# frozen_string_literal: true
+
module ProfilesHelper
+ def commit_email_select_options(user)
+ private_email = user.private_commit_email
+ verified_emails = user.verified_emails - [private_email]
+
+ [
+ [s_("Profiles|Use a private email - %{email}").html_safe % { email: private_email }, Gitlab::PrivateCommitEmail::TOKEN],
+ *verified_emails
+ ]
+ end
+
+ def selected_commit_email(user)
+ user.read_attribute(:commit_email) || user.commit_email
+ end
+
def attribute_provider_label(attribute)
user_synced_attributes_metadata = current_user.user_synced_attributes_metadata
if user_synced_attributes_metadata&.synced?(attribute)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index aaf9dff43ee..0a7f930110a 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProjectsHelper
def link_to_project(project)
link_to [project.namespace.becomes(Namespace), project], title: h(project.name) do
@@ -50,7 +52,7 @@ module ProjectsHelper
return "(deleted)" unless author
- author_html = ""
+ author_html = []
# Build avatar image tag
author_html << link_to_member_avatar(author, opts) if opts[:avatar]
@@ -60,7 +62,7 @@ module ProjectsHelper
author_html << capture(&block) if block
- author_html = author_html.html_safe
+ author_html = author_html.join.html_safe
if opts[:name]
link_to(author_html, user_path(author), class: "author-link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
@@ -80,15 +82,8 @@ module ProjectsHelper
end
project_link = link_to project_path(project) do
- output =
- if project.avatar_url && !Rails.env.test?
- project_icon(project, alt: project.name, class: 'avatar-tile', width: 15, height: 15)
- else
- ""
- end
-
- output << content_tag("span", simple_sanitize(project.name), class: "breadcrumb-item-text js-breadcrumb-item-text")
- output.html_safe
+ icon = project_icon(project, alt: project.name, class: 'avatar-tile', width: 15, height: 15) if project.avatar_url && !Rails.env.test?
+ [icon, content_tag("span", simple_sanitize(project.name), class: "breadcrumb-item-text js-breadcrumb-item-text")].join.html_safe
end
namespace_link = breadcrumb_list_item(namespace_link) unless project.group
@@ -192,7 +187,10 @@ module ProjectsHelper
end
def show_no_ssh_key_message?
- cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key?
+ Gitlab::CurrentSettings.user_show_add_ssh_key_message? &&
+ cookies[:hide_no_ssh_message].blank? &&
+ !current_user.hide_no_ssh_key &&
+ current_user.require_ssh_key?
end
def show_no_password_message?
@@ -200,6 +198,14 @@ module ProjectsHelper
current_user.require_extra_setup_for_git_auth?
end
+ def show_auto_devops_implicitly_enabled_banner?(project)
+ cookie_key = "hide_auto_devops_implicitly_enabled_banner_#{project.id}"
+
+ project.has_auto_devops_implicitly_enabled? &&
+ cookies[cookie_key.to_sym].blank? &&
+ (project.owner == current_user || project.team.maintainer?(current_user))
+ end
+
def link_to_set_password
if current_user.require_password_creation_for_git?
link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path
@@ -216,6 +222,7 @@ module ProjectsHelper
#
# If no limit is applied we'll just issue a COUNT since the result set could
# be too large to load into memory.
+ # rubocop: disable CodeReuse/ActiveRecord
def any_projects?(projects)
return projects.any? if projects.is_a?(Array)
@@ -225,6 +232,7 @@ module ProjectsHelper
projects.except(:offset).any?
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def show_projects?(projects, params)
!!(params[:personal] || params[:name] || any_projects?(projects))
@@ -249,6 +257,10 @@ module ProjectsHelper
"xcode://clone?repo=#{CGI.escape(default_url_to_repo(project))}"
end
+ def legacy_render_context(params)
+ params[:legacy_render] ? { markdown_engine: :redcarpet } : {}
+ end
+
private
def get_project_nav_tabs(project, current_user)
@@ -348,6 +360,10 @@ module ProjectsHelper
end
end
+ def default_clone_label
+ _("Copy %{protocol} clone URL") % { protocol: default_clone_protocol.upcase }
+ end
+
def default_clone_protocol
if allowed_protocols_present?
enabled_protocol
@@ -372,22 +388,6 @@ module ProjectsHelper
end
end
- def koding_project_url(project = nil, branch = nil, sha = nil)
- if project
- import_path = "/Home/Stacks/import"
-
- repo = project.full_path
- branch ||= project.default_branch
- sha ||= project.commit.short_id
-
- path = "#{import_path}?repo=#{repo}&branch=#{branch}&sha=#{sha}"
-
- return URI.join(Gitlab::CurrentSettings.koding_url, path).to_s
- end
-
- Gitlab::CurrentSettings.koding_url
- end
-
def project_wiki_path_with_version(proj, page, version, is_newest)
url_params = is_newest ? {} : { version_id: version }
project_wiki_path(proj, page, url_params)
@@ -438,13 +438,14 @@ module ProjectsHelper
buildsAccessLevel: feature.builds_access_level,
wikiAccessLevel: feature.wiki_access_level,
snippetsAccessLevel: feature.snippets_access_level,
+ pagesAccessLevel: feature.pages_access_level,
containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled
}
end
def project_permissions_panel_data(project)
- data = {
+ {
currentSettings: project_permissions_settings(project),
canChangeVisibilityLevel: can_change_visibility_level?(project, current_user),
allowedVisibilityOptions: project_allowed_visibility_levels(project),
@@ -452,10 +453,15 @@ module ProjectsHelper
registryAvailable: Gitlab.config.registry.enabled,
registryHelpPath: help_page_path('user/project/container_registry'),
lfsAvailable: Gitlab.config.lfs.enabled,
- lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'),
+ pagesAvailable: Gitlab.config.pages.enabled,
+ pagesAccessControlEnabled: Gitlab.config.pages.access_control,
+ pagesHelpPath: help_page_path('user/project/pages/index.md')
}
+ end
- data.to_json.html_safe
+ def project_permissions_panel_data_json(project)
+ project_permissions_panel_data(project).to_json.html_safe
end
def project_allowed_visibility_levels(project)
@@ -534,4 +540,13 @@ module ProjectsHelper
network
]
end
+
+ def sidebar_operations_paths
+ %w[
+ environments
+ clusters
+ user
+ gcp
+ ]
+ end
end
diff --git a/app/helpers/repository_languages_helper.rb b/app/helpers/repository_languages_helper.rb
index 9a842cf5ce0..cf7eee7fff3 100644
--- a/app/helpers/repository_languages_helper.rb
+++ b/app/helpers/repository_languages_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RepositoryLanguagesHelper
def repository_languages_bar(languages)
return if languages.none?
@@ -11,6 +13,7 @@ module RepositoryLanguagesHelper
content_tag :div, nil,
class: "progress-bar has-tooltip",
style: "width: #{lang.share}%; background-color:#{lang.color}",
- title: lang.name
+ data: { html: true },
+ title: "<span class=\"repository-language-bar-tooltip-language\">#{escape_javascript(lang.name)}</span>&nbsp;<span class=\"repository-language-bar-tooltip-share\">#{lang.share.round(1)}%</span>"
end
end
diff --git a/app/helpers/rss_helper.rb b/app/helpers/rss_helper.rb
index 7d4fa83a67a..67c7d244f11 100644
--- a/app/helpers/rss_helper.rb
+++ b/app/helpers/rss_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RssHelper
def rss_url_options
{ format: :atom, feed_token: current_user.try(:feed_token) }
diff --git a/app/helpers/runners_helper.rb b/app/helpers/runners_helper.rb
index 9fb42487a75..cb21f922401 100644
--- a/app/helpers/runners_helper.rb
+++ b/app/helpers/runners_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RunnersHelper
def runner_status_icon(runner)
status = runner.status
diff --git a/app/helpers/safe_params_helper.rb b/app/helpers/safe_params_helper.rb
index b568e8810cc..18bbf3347a8 100644
--- a/app/helpers/safe_params_helper.rb
+++ b/app/helpers/safe_params_helper.rb
@@ -1,6 +1,9 @@
+# frozen_string_literal: true
+
module SafeParamsHelper
# Rails 5.0 requires to permit `params` if they're used in url helpers.
# Use this helper when generating links with `params.merge(...)`
+ # rubocop: disable CodeReuse/ActiveRecord
def safe_params
if params.respond_to?(:permit!)
params.except(:host, :port, :protocol).permit!
@@ -8,4 +11,5 @@ module SafeParamsHelper
params
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 98074a4c0c5..80cc568820a 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SearchHelper
def search_autocomplete_opts(term)
return unless current_user
@@ -99,6 +101,7 @@ module SearchHelper
end
# Autocomplete results for the current user's groups
+ # rubocop: disable CodeReuse/ActiveRecord
def groups_autocomplete(term, limit = 5)
current_user.authorized_groups.order_id_desc.search(term).limit(limit).map do |group|
{
@@ -110,8 +113,10 @@ module SearchHelper
}
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Autocomplete results for the current user's projects
+ # rubocop: disable CodeReuse/ActiveRecord
def projects_autocomplete(term, limit = 5)
current_user.authorized_projects.order_id_desc.search_by_title(term)
.sorted_by_stars.non_archived.limit(limit).map do |p|
@@ -125,6 +130,7 @@ module SearchHelper
}
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def search_result_sanitize(str)
Sanitize.clean(str)
@@ -157,15 +163,26 @@ module SearchHelper
if @project.present?
opts[:data]['project-id'] = @project.id
opts[:data]['base-endpoint'] = project_path(@project)
- else
- # Group context
+ elsif @group.present?
opts[:data]['group-id'] = @group.id
opts[:data]['base-endpoint'] = group_canonical_path(@group)
+ else
+ opts[:data]['base-endpoint'] = root_dashboard_path
end
opts
end
+ def search_history_storage_prefix
+ if @project.present?
+ @project.full_path
+ elsif @group.present?
+ @group.full_path
+ else
+ 'dashboard'
+ end
+ end
+
# Sanitize a HTML field for search display. Most tags are stripped out and the
# maximum length is set to 200 characters.
def search_md_sanitize(object, field)
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
index 6cefcde558a..cf60696ef39 100644
--- a/app/helpers/selects_helper.rb
+++ b/app/helpers/selects_helper.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
module SelectsHelper
def users_select_tag(id, opts = {})
- css_class = "ajax-users-select "
- css_class << "multiselect " if opts[:multiple]
- css_class << "skip_ldap " if opts[:skip_ldap]
+ css_class = ["ajax-users-select"]
+ css_class << "multiselect" if opts[:multiple]
+ css_class << "skip_ldap" if opts[:skip_ldap]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
html = {
- class: css_class,
+ class: css_class.join(' '),
data: users_select_data_attributes(opts)
}
@@ -24,20 +26,21 @@ module SelectsHelper
end
def groups_select_tag(id, opts = {})
- opts[:class] ||= ''
- opts[:class] << ' ajax-groups-select'
+ classes = Array.wrap(opts[:class])
+ classes << 'ajax-groups-select'
+
+ opts[:class] = classes.join(' ')
+
select2_tag(id, opts)
end
def namespace_select_tag(id, opts = {})
- opts[:class] ||= ''
- opts[:class] << ' ajax-namespace-select'
+ opts[:class] = [*opts[:class], 'ajax-namespace-select'].join(' ')
select2_tag(id, opts)
end
def project_select_tag(id, opts = {})
- opts[:class] ||= ''
- opts[:class] << ' ajax-project-select'
+ opts[:class] = [*opts[:class], 'ajax-project-select'].join(' ')
unless opts.delete(:scope) == :all
if @group
@@ -57,7 +60,10 @@ module SelectsHelper
end
def select2_tag(id, opts = {})
- opts[:class] << ' multiselect' if opts[:multiple]
+ klass_opts = [opts[:class]]
+ klass_opts << 'multiselect' if opts[:multiple]
+
+ opts[:class] = klass_opts.join(' ')
value = opts[:selected] || ''
hidden_field_tag(id, value, opts)
diff --git a/app/helpers/sentry_helper.rb b/app/helpers/sentry_helper.rb
index 3d255df66a0..d53eaef9952 100644
--- a/app/helpers/sentry_helper.rb
+++ b/app/helpers/sentry_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SentryHelper
def sentry_enabled?
Gitlab::Sentry.enabled?
diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb
index f872990122e..d4b50b7ecfb 100644
--- a/app/helpers/services_helper.rb
+++ b/app/helpers/services_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ServicesHelper
def service_event_description(event)
case event
@@ -30,7 +32,7 @@ module ServicesHelper
end
def service_save_button(service)
- button_tag(class: 'btn btn-save', type: 'submit', disabled: service.deprecated?) do
+ button_tag(class: 'btn btn-success', type: 'submit', disabled: service.deprecated?) do
icon('spinner spin', class: 'hidden js-btn-spinner') +
content_tag(:span, 'Save changes', class: 'js-btn-label')
end
diff --git a/app/helpers/sidekiq_helper.rb b/app/helpers/sidekiq_helper.rb
index 50aeb7f4b82..32bf3526571 100644
--- a/app/helpers/sidekiq_helper.rb
+++ b/app/helpers/sidekiq_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SidekiqHelper
SIDEKIQ_PS_REGEXP = %r{\A
(?<pid>\d+)\s+
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index a05640773ad..c7d31f3469d 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SnippetsHelper
def reliable_snippet_path(snippet, opts = nil)
if snippet.project_id?
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 36a311dfa8a..8ed2a2ec9f4 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SortingHelper
def sort_options_hash
{
@@ -22,7 +24,8 @@ module SortingHelper
sort_value_recently_updated => sort_title_recently_updated,
sort_value_popularity => sort_title_popularity,
sort_value_priority => sort_title_priority,
- sort_value_upvotes => sort_title_upvotes
+ sort_value_upvotes => sort_title_upvotes,
+ sort_value_contacted_date => sort_title_contacted_date
}
end
@@ -32,7 +35,8 @@ module SortingHelper
sort_value_name => sort_title_name,
sort_value_oldest_activity => sort_title_oldest_activity,
sort_value_oldest_created => sort_title_oldest_created,
- sort_value_recently_created => sort_title_recently_created
+ sort_value_recently_created => sort_title_recently_created,
+ sort_value_most_stars => sort_title_most_stars
}
if current_controller?('admin/projects')
@@ -44,15 +48,21 @@ module SortingHelper
def groups_sort_options_hash
{
- sort_value_name => sort_title_name,
- sort_value_name_desc => sort_title_name_desc,
+ sort_value_name => sort_title_name,
+ sort_value_name_desc => sort_title_name_desc,
sort_value_recently_created => sort_title_recently_created,
- sort_value_oldest_created => sort_title_oldest_created,
+ sort_value_oldest_created => sort_title_oldest_created,
sort_value_recently_updated => sort_title_recently_updated,
- sort_value_oldest_updated => sort_title_oldest_updated
+ sort_value_oldest_updated => sort_title_oldest_updated
}
end
+ def subgroups_sort_options_hash
+ groups_sort_options_hash.merge(
+ sort_value_most_stars => sort_title_most_stars
+ )
+ end
+
def admin_groups_sort_options_hash
groups_sort_options_hash.merge(
sort_value_largest_group => sort_title_largest_group
@@ -99,6 +109,17 @@ module SortingHelper
}
end
+ def label_sort_options_hash
+ {
+ sort_value_name => sort_title_name,
+ sort_value_name_desc => sort_title_name_desc,
+ sort_value_recently_created => sort_title_recently_created,
+ sort_value_oldest_created => sort_title_oldest_created,
+ sort_value_recently_updated => sort_title_recently_updated,
+ sort_value_oldest_updated => sort_title_oldest_updated
+ }
+ end
+
def sortable_item(item, path, sorted_by)
link_to item, path, class: sorted_by == item ? 'is-active' : ''
end
@@ -228,6 +249,14 @@ module SortingHelper
s_('SortOptions|Most popular')
end
+ def sort_title_contacted_date
+ s_('SortOptions|Last Contact')
+ end
+
+ def sort_title_most_stars
+ s_('SortOptions|Most stars')
+ end
+
# Values.
def sort_value_access_level_asc
'access_level_asc'
@@ -348,4 +377,12 @@ module SortingHelper
def sort_value_upvotes
'upvotes_desc'
end
+
+ def sort_value_contacted_date
+ 'contacted_asc'
+ end
+
+ def sort_value_most_stars
+ 'stars_desc'
+ end
end
diff --git a/app/helpers/storage_health_helper.rb b/app/helpers/storage_health_helper.rb
deleted file mode 100644
index b76c1228220..00000000000
--- a/app/helpers/storage_health_helper.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module StorageHealthHelper
- def failing_storage_health_message(storage_health)
- storage_name = content_tag(:strong, h(storage_health.storage_name))
- host_names = h(storage_health.failing_on_hosts.to_sentence)
- translation_params = { storage_name: storage_name,
- host_names: host_names,
- failed_attempts: storage_health.total_failures }
-
- translation = n_('%{storage_name}: failed storage access attempt on host:',
- '%{storage_name}: %{failed_attempts} failed storage access attempts:',
- storage_health.total_failures) % translation_params
-
- translation.html_safe
- end
-
- def message_for_circuit_breaker(circuit_breaker)
- maximum_failures = circuit_breaker.failure_count_threshold
- current_failures = circuit_breaker.failure_count
-
- translation_params = { number_of_failures: current_failures,
- maximum_failures: maximum_failures }
-
- if circuit_breaker.circuit_broken?
- s_("%{number_of_failures} of %{maximum_failures} failures. GitLab will not "\
- "retry automatically. Reset storage information when the problem is "\
- "resolved.") % translation_params
- else
- _("%{number_of_failures} of %{maximum_failures} failures. GitLab will "\
- "allow access on the next attempt.") % translation_params
- end
- end
-end
diff --git a/app/helpers/storage_helper.rb b/app/helpers/storage_helper.rb
index e19c67a37ca..be8761db562 100644
--- a/app/helpers/storage_helper.rb
+++ b/app/helpers/storage_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module StorageHelper
def storage_counter(size_in_bytes)
precision = size_in_bytes < 1.megabyte ? 0 : 1
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index ebfde993456..164c69ca50b 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SubmoduleHelper
extend self
@@ -64,8 +66,7 @@ module SubmoduleHelper
end
def relative_self_url?(url)
- # (./)?(../repo.git) || (./)?(../../project/repo.git) )
- url =~ %r{\A((\./)?(\.\./))(?!(\.\.)|(.*/)).*(\.git)?\z} || url =~ %r{\A((\./)?(\.\./){2})(?!(\.\.))([^/]*)/(?!(\.\.)|(.*/)).*(\.git)?\z}
+ url.start_with?('../', './')
end
def standard_links(host, namespace, project, commit)
@@ -73,25 +74,29 @@ module SubmoduleHelper
[base, [base, '/tree/', commit].join('')]
end
- def relative_self_links(url, commit, project)
- url.rstrip!
- # Map relative links to a namespace and project
- # For example:
- # ../bar.git -> same namespace, repo bar
- # ../foo/bar.git -> namespace foo, repo bar
- # ../../foo/bar/baz.git -> namespace bar, repo baz
- components = url.split('/')
- base = components.pop.gsub(/.git$/, '')
- namespace = components.pop.gsub(/^\.\.$/, '')
-
- if namespace.empty?
- namespace = project.namespace.full_path
+ def relative_self_links(relative_path, commit, project)
+ relative_path.rstrip!
+ absolute_project_path = "/" + project.full_path
+
+ # Resolve `relative_path` to target path
+ # Assuming `absolute_project_path` is `/g1/p1`:
+ # ../p2.git -> /g1/p2
+ # ../g2/p3.git -> /g1/g2/p3
+ # ../../g3/g4/p4.git -> /g3/g4/p4
+ submodule_project_path = File.absolute_path(relative_path, absolute_project_path)
+ target_namespace_path = File.dirname(submodule_project_path)
+
+ if target_namespace_path == '/' || target_namespace_path.start_with?(absolute_project_path)
+ return [nil, nil]
end
+ target_namespace_path.sub!(%r{^/}, '')
+ submodule_base = File.basename(submodule_project_path, '.git')
+
begin
[
- namespace_project_path(namespace, base),
- namespace_project_tree_path(namespace, base, commit)
+ namespace_project_path(target_namespace_path, submodule_base),
+ namespace_project_tree_path(target_namespace_path, submodule_base, commit)
]
rescue ActionController::UrlGenerationError
[nil, nil]
diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb
index 5b4a141dbcf..ac4e8f54260 100644
--- a/app/helpers/system_note_helper.rb
+++ b/app/helpers/system_note_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemNoteHelper
ICON_NAMES_BY_ACTION = {
'commit' => 'commit',
@@ -21,7 +23,8 @@ module SystemNoteHelper
'outdated' => 'pencil-square',
'duplicate' => 'issue-duplicate',
'locked' => 'lock',
- 'unlocked' => 'lock-open'
+ 'unlocked' => 'lock-open',
+ 'due_date' => 'calendar'
}.freeze
def system_note_icon_name(note)
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index ee701076a14..d91f0f78db7 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TabHelper
# Navigation link helper
#
@@ -6,7 +8,7 @@ module TabHelper
# element is the value passed to the block.
#
# options - The options hash used to determine if the element is "active" (default: {})
- # :controller - One or more controller names to check (optional).
+ # :controller - One or more controller names to check, use path notation when namespaced (optional).
# :action - One or more action names to check (optional).
# :path - A shorthand path, such as 'dashboard#index', to check (optional).
# :html_options - Extra options to be passed to the list element (optional).
@@ -40,6 +42,20 @@ module TabHelper
# nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" }
# # => '<li class="home active">Hello</li>'
#
+ # # For namespaced controllers like Admin::AppearancesController#show
+ #
+ # # Controller and namespace matches
+ # nav_link(controller: 'admin/appearances') { "Hello" }
+ # # => '<li class="active">Hello</li>'
+ #
+ # # Controller and namespace matches but action doesn't
+ # nav_link(controller: 'admin/appearances', action: :edit) { "Hello" }
+ # # => '<li>Hello</li>'
+ #
+ # # Shorthand path with namespace
+ # nav_link(path: 'admin/appearances#show') { "Hello"}
+ # # => '<li class="active">Hello</li>'
+ #
# Returns a list item element String
def nav_link(options = {}, &block)
klass = active_nav_link?(options) ? 'active' : ''
@@ -47,9 +63,7 @@ module TabHelper
# Add our custom class into the html_options, which may or may not exist
# and which may or may not already have a :class key
o = options.delete(:html_options) || {}
- o[:class] ||= ''
- o[:class] += ' ' + klass
- o[:class].strip!
+ o[:class] = [*o[:class], klass].join(' ').strip
if block_given?
content_tag(:li, capture(&block), o)
diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb
index d000d6b1c0a..de0b92b6fd7 100644
--- a/app/helpers/tags_helper.rb
+++ b/app/helpers/tags_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TagsHelper
def tag_path(tag)
"/tags/#{tag}"
@@ -14,12 +16,13 @@ module TagsHelper
end
def tag_list(project)
- html = ''
+ html = []
+
project.tag_list.each do |tag|
html << link_to(tag, tag_path(tag))
end
- html.html_safe
+ html.join.html_safe
end
def protected_tag?(project, tag)
diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb
index 336385f6798..719c351242c 100644
--- a/app/helpers/time_helper.rb
+++ b/app/helpers/time_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TimeHelper
def time_interval_in_words(interval_in_seconds)
interval_in_seconds = interval_in_seconds.to_i
@@ -19,9 +21,15 @@ module TimeHelper
"#{from.to_s(:short)} - #{to.to_s(:short)}"
end
- def duration_in_numbers(duration)
- time_format = duration < 1.hour ? "%M:%S" : "%H:%M:%S"
+ def duration_in_numbers(duration_in_seconds)
+ seconds = duration_in_seconds % 1.minute
+ minutes = (duration_in_seconds / 1.minute) % (1.hour / 1.minute)
+ hours = duration_in_seconds / 1.hour
- Time.at(duration).utc.strftime(time_format)
+ if hours == 0
+ "%02d:%02d" % [minutes, seconds]
+ else
+ "%02d:%02d:%02d" % [hours, minutes, seconds]
+ end
end
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 7cd74358168..6bd78336ed3 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TodosHelper
def todos_pending_count
@todos_pending_count ||= current_user.todos_pending_count
@@ -94,9 +96,7 @@ module TodosHelper
end
end
- path = request.path
- path << "?#{options.to_param}"
- path
+ "#{request.path}?#{options.to_param}"
end
def todo_actions_options
@@ -152,10 +152,11 @@ module TodosHelper
''
end
- html = "&middot; ".html_safe
- html << content_tag(:span, class: css_class) do
+ content = content_tag(:span, class: css_class) do
"Due #{is_due_today ? "today" : todo.target.due_date.to_s(:medium)}"
end
+
+ "&middot; #{content}".html_safe
end
private
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index dc42caa70e5..78a11616d4c 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TreeHelper
FILE_LIMIT = 1_000
@@ -5,10 +7,11 @@ module TreeHelper
# their corresponding partials
#
# tree - A `Tree` object for the current tree
+ # rubocop: disable CodeReuse/ActiveRecord
def render_tree(tree)
# Sort submodules and folders together by name ahead of files
folders, files, submodules = tree.trees, tree.blobs, tree.submodules
- tree = ''
+ tree = []
items = (folders + submodules).sort_by(&:name) + files
if items.size > FILE_LIMIT
@@ -18,8 +21,9 @@ module TreeHelper
end
tree << render(partial: 'projects/tree/tree_row', collection: items) if items.present?
- tree.html_safe
+ tree.join.html_safe
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Return an image icon depending on the file type and mode
#
@@ -27,11 +31,21 @@ module TreeHelper
# mode - File unix mode
# name - File name
def tree_icon(type, mode, name)
- icon("#{file_type_icon_class(type, mode, name)} fw")
+ icon([file_type_icon_class(type, mode, name), 'fw'])
+ end
+
+ # Using Rails `*_path` methods can be slow, especially when generating
+ # many paths, as with a repository tree that has thousands of items.
+ def fast_project_blob_path(project, blob_path)
+ Addressable::URI.escape(
+ File.join(relative_url_root, project.path_with_namespace, 'blob', blob_path)
+ )
end
- def tree_hex_class(content)
- "file_#{hexdigest(content.name)}"
+ def fast_project_tree_path(project, tree_path)
+ Addressable::URI.escape(
+ File.join(relative_url_root, project.path_with_namespace, 'tree', tree_path)
+ )
end
# Simple shortcut to File.join
@@ -122,6 +136,7 @@ module TreeHelper
end
# returns the relative path of the first subdir that doesn't have only one directory descendant
+ # rubocop: disable CodeReuse/ActiveRecord
def flatten_tree(root_path, tree)
return tree.flat_path.sub(%r{\A#{Regexp.escape(root_path)}/}, '') if tree.flat_path.present?
@@ -132,8 +147,13 @@ module TreeHelper
return tree.name
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def selected_branch
@branch_name || tree_edit_branch
end
+
+ def relative_url_root
+ Gitlab.config.gitlab.relative_url_root.presence || '/'
+ end
end
diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb
index ce435ca2241..5cfdc0971f0 100644
--- a/app/helpers/triggers_helper.rb
+++ b/app/helpers/triggers_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TriggersHelper
def builds_trigger_url(project_id, ref: nil)
if ref.nil?
diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb
index da5fe25c07d..4aba48061ba 100644
--- a/app/helpers/user_callouts_helper.rb
+++ b/app/helpers/user_callouts_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module UserCalloutsHelper
GKE_CLUSTER_INTEGRATION = 'gke_cluster_integration'.freeze
GCP_SIGNUP_OFFER = 'gcp_signup_offer'.freeze
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index ceea4384f91..42b533ad772 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module UsersHelper
def user_link(user)
link_to(user.name, user_path(user),
@@ -23,6 +25,17 @@ module UsersHelper
profile_tabs.include?(tab)
end
+ def user_internal_regex_data
+ settings = Gitlab::CurrentSettings.current_application_settings
+
+ pattern, options = if settings.user_default_internal_regex_enabled?
+ regex = settings.user_default_internal_regex_instance
+ JsRegex.new(regex).to_h.slice(:source, :options).values
+ end
+
+ { user_internal_regex_pattern: pattern, user_internal_regex_options: options }
+ end
+
def current_user_menu_items
@current_user_menu_items ||= get_current_user_menu_items
end
@@ -63,7 +76,7 @@ module UsersHelper
tabs = []
if can?(current_user, :read_user_profile, @user)
- tabs += [:activity, :groups, :contributed, :projects, :snippets]
+ tabs += [:overview, :activity, :groups, :contributed, :projects, :snippets]
end
tabs
diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb
index c20753ece72..ab77b149072 100644
--- a/app/helpers/version_check_helper.rb
+++ b/app/helpers/version_check_helper.rb
@@ -1,8 +1,25 @@
+# frozen_string_literal: true
+
module VersionCheckHelper
def version_status_badge
- if Rails.env.production? && Gitlab::CurrentSettings.version_check_enabled
- image_url = VersionCheck.new.url
- image_tag image_url, class: 'js-version-status-badge'
+ return unless Rails.env.production?
+ return unless Gitlab::CurrentSettings.version_check_enabled
+ return if User.single_user&.requires_usage_stats_consent?
+
+ image_url = VersionCheck.new.url
+ image_tag image_url, class: 'js-version-status-badge'
+ end
+
+ def link_to_version
+ if Gitlab.pre_release?
+ commit_link = link_to(Gitlab.revision, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', source_code_project, Gitlab.revision))
+ [Gitlab::VERSION, content_tag(:small, commit_link)].join(' ').html_safe
+ else
+ link_to Gitlab::VERSION, Gitlab::COM_URL + namespace_project_tag_path('gitlab-org', source_code_project, "v#{Gitlab::VERSION}")
end
end
+
+ def source_code_project
+ 'gitlab-ce'
+ end
end
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index cf2fe5a2019..e690350a0d1 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module VisibilityLevelHelper
def visibility_level_color(level)
case level
@@ -82,7 +84,7 @@ module VisibilityLevelHelper
def disallowed_project_visibility_level_description(level, project)
level_name = Gitlab::VisibilityLevel.level_name(level).downcase
reasons = []
- instructions = ''
+ instructions = []
unless project.visibility_level_allowed_as_fork?(level)
reasons << "the fork source project has lower visibility"
@@ -96,7 +98,7 @@ module VisibilityLevelHelper
end
reasons = reasons.any? ? ' because ' + reasons.to_sentence : ''
- "This project cannot be #{level_name}#{reasons}.#{instructions}".html_safe
+ "This project cannot be #{level_name}#{reasons}.#{instructions.join}".html_safe
end
# Note: these messages closely mirror the form validation strings found in the group
@@ -104,7 +106,7 @@ module VisibilityLevelHelper
def disallowed_group_visibility_level_description(level, group)
level_name = Gitlab::VisibilityLevel.level_name(level).downcase
reasons = []
- instructions = ''
+ instructions = []
unless group.visibility_level_allowed_by_projects?(level)
reasons << "it contains projects with higher visibility"
@@ -122,7 +124,7 @@ module VisibilityLevelHelper
end
reasons = reasons.any? ? ' because ' + reasons.to_sentence : ''
- "This group cannot be #{level_name}#{reasons}.#{instructions}".html_safe
+ "This group cannot be #{level_name}#{reasons}.#{instructions.join}".html_safe
end
def visibility_icon_description(form_model)
@@ -138,7 +140,7 @@ module VisibilityLevelHelper
end
def project_visibility_icon_description(level)
- "#{visibility_level_label(level)} - #{project_visibility_level_description(level)}"
+ "#{project_visibility_level_description(level)}"
end
def visibility_level_label(level)
diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb
index 72f6b397046..345ddcf023a 100644
--- a/app/helpers/webpack_helper.rb
+++ b/app/helpers/webpack_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WebpackHelper
def webpack_bundle_tag(bundle)
javascript_include_tag(*webpack_entrypoint_paths(bundle))
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
index 41f9eedd4bd..647f34e57ed 100644
--- a/app/helpers/wiki_helper.rb
+++ b/app/helpers/wiki_helper.rb
@@ -1,4 +1,8 @@
+# frozen_string_literal: true
+
module WikiHelper
+ include API::Helpers::RelatedResourcesHelpers
+
# Produces a pure text breadcrumb for a given page.
#
# page_slug - The slug of a WikiPage object.
@@ -39,4 +43,8 @@ module WikiHelper
end
end
end
+
+ def wiki_attachment_upload_url
+ expose_url(api_v4_projects_wikis_attachments_path(id: @project.id))
+ end
end
diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb
index fd1d78bd9b8..49c08dce96c 100644
--- a/app/helpers/workhorse_helper.rb
+++ b/app/helpers/workhorse_helper.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
# Helpers to send Git blobs, diffs, patches or archives through Workhorse.
# Workhorse will also serve files when using `send_file`.
module WorkhorseHelper
# Send a Git blob through Workhorse
def send_git_blob(repository, blob, inline: true)
headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
- headers['Content-Disposition'] = inline ? 'inline' : 'attachment'
+ headers['Content-Disposition'] = content_disposition(blob, inline)
headers['Content-Type'] = safe_content_type(blob)
render plain: ""
end
diff --git a/app/mailers/abuse_report_mailer.rb b/app/mailers/abuse_report_mailer.rb
index fe5f68ba3d5..e032f568913 100644
--- a/app/mailers/abuse_report_mailer.rb
+++ b/app/mailers/abuse_report_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AbuseReportMailer < BaseMailer
def notify(abuse_report_id)
return unless deliverable?
diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb
index 654468bc7fe..5fd209c4761 100644
--- a/app/mailers/base_mailer.rb
+++ b/app/mailers/base_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseMailer < ActionMailer::Base
around_action :render_with_default_locale
diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb
index 962570a0efd..7aa75ee30e6 100644
--- a/app/mailers/devise_mailer.rb
+++ b/app/mailers/devise_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeviseMailer < Devise::Mailer
default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>"
default reply_to: Gitlab.config.gitlab.email_reply_to
@@ -9,8 +11,9 @@ class DeviseMailer < Devise::Mailer
protected
def subject_for(key)
- subject = super
- subject << " | #{Gitlab.config.gitlab.email_subject_suffix}" if Gitlab.config.gitlab.email_subject_suffix.present?
- subject
+ subject = [super]
+ subject << Gitlab.config.gitlab.email_subject_suffix if Gitlab.config.gitlab.email_subject_suffix.present?
+
+ subject.join(' | ')
end
end
diff --git a/app/mailers/email_rejection_mailer.rb b/app/mailers/email_rejection_mailer.rb
index 76db31a4c45..45fc5a6c383 100644
--- a/app/mailers/email_rejection_mailer.rb
+++ b/app/mailers/email_rejection_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailRejectionMailer < BaseMailer
def rejection(reason, original_raw, can_retry = false)
@reason = reason
diff --git a/app/mailers/emails/auto_devops.rb b/app/mailers/emails/auto_devops.rb
new file mode 100644
index 00000000000..9705a3052d4
--- /dev/null
+++ b/app/mailers/emails/auto_devops.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Emails
+ module AutoDevops
+ def autodevops_disabled_email(pipeline, recipient)
+ @pipeline = pipeline
+ @project = pipeline.project
+
+ add_project_headers
+
+ mail(to: recipient,
+ subject: auto_devops_disabled_subject(@project.name)) do |format|
+ format.html { render layout: 'mailer' }
+ format.text { render layout: 'mailer' }
+ end
+ end
+
+ private
+
+ def auto_devops_disabled_subject(project_name)
+ subject("Auto DevOps pipeline was disabled for #{project_name}")
+ end
+ end
+end
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index 392cc0bee03..93b51fb1774 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Issues
def new_issue_email(recipient_id, issue_id, reason = nil)
@@ -17,6 +19,7 @@ module Emails
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
end
+ # rubocop: disable CodeReuse/ActiveRecord
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_ids, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
@@ -25,6 +28,7 @@ module Emails
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
end
+ # rubocop: enable CodeReuse/ActiveRecord
def closed_issue_email(recipient_id, issue_id, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
@@ -41,6 +45,20 @@ module Emails
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
end
+ def removed_milestone_issue_email(recipient_id, issue_id, updated_by_user_id, reason = nil)
+ setup_issue_mail(issue_id, recipient_id)
+
+ mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
+ end
+
+ def changed_milestone_issue_email(recipient_id, issue_id, milestone, updated_by_user_id, reason = nil)
+ setup_issue_mail(issue_id, recipient_id)
+
+ @milestone = milestone
+ @milestone_url = milestone_url(@milestone)
+ mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
+ end
+
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index 75cf56a51f2..91dfdf58982 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Members
extend ActiveSupport::Concern
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index 70509e9066d..6524d0c2087 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -1,13 +1,16 @@
+# frozen_string_literal: true
+
module Emails
module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id, reason = nil)
- setup_merge_request_mail(merge_request_id, recipient_id)
+ setup_merge_request_mail(merge_request_id, recipient_id, present: true)
mail_new_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
end
def new_mention_in_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
- setup_merge_request_mail(merge_request_id, recipient_id)
+ setup_merge_request_mail(merge_request_id, recipient_id, present: true)
+
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
@@ -20,12 +23,14 @@ module Emails
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
+ # rubocop: disable CodeReuse/ActiveRecord
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
+ # rubocop: enable CodeReuse/ActiveRecord
def relabeled_merge_request_email(recipient_id, merge_request_id, label_names, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@@ -35,6 +40,20 @@ module Emails
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
+ def removed_milestone_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
+ setup_merge_request_mail(merge_request_id, recipient_id)
+
+ mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
+ end
+
+ def changed_milestone_merge_request_email(recipient_id, merge_request_id, milestone, updated_by_user_id, reason = nil)
+ setup_merge_request_mail(merge_request_id, recipient_id)
+
+ @milestone = milestone
+ @milestone_url = milestone_url(@milestone)
+ mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
+ end
+
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@@ -71,11 +90,16 @@ module Emails
private
- def setup_merge_request_mail(merge_request_id, recipient_id)
+ def setup_merge_request_mail(merge_request_id, recipient_id, present: false)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
+ if present
+ recipient = User.find(recipient_id)
+ @mr_presenter = @merge_request.present(current_user: recipient)
+ end
+
@sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
end
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index d9a6fe2a41e..d3284e90568 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Notes
def note_commit_email(recipient_id, note_id)
diff --git a/app/mailers/emails/pages_domains.rb b/app/mailers/emails/pages_domains.rb
index 0027dfdc36b..ce449237ef6 100644
--- a/app/mailers/emails/pages_domains.rb
+++ b/app/mailers/emails/pages_domains.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module PagesDomains
def pages_domain_enabled_email(domain, recipient)
diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb
index f9f45ab987b..31e183640ad 100644
--- a/app/mailers/emails/pipelines.rb
+++ b/app/mailers/emails/pipelines.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Pipelines
def pipeline_success_email(pipeline, recipients)
@@ -39,10 +41,10 @@ module Emails
end
def pipeline_subject(status)
- commit = @pipeline.short_sha
- commit << " in #{@merge_request.to_reference}" if @merge_request
+ commit = [@pipeline.short_sha]
+ commit << "in #{@merge_request.to_reference}" if @merge_request
- subject("Pipeline ##{@pipeline.id} has #{status} for #{@pipeline.ref}", commit)
+ subject("Pipeline ##{@pipeline.id} has #{status} for #{@pipeline.ref}", commit.join(' '))
end
end
end
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index 4f5edeb9bda..2ea1aea1f51 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Profile
def new_user_email(user_id, token = nil)
@@ -7,6 +9,7 @@ module Emails
mail(to: @user.notification_email, subject: subject("Account was created for you"))
end
+ # rubocop: disable CodeReuse/ActiveRecord
def new_ssh_key_email(key_id)
@key = Key.find_by(id: key_id)
@@ -16,7 +19,9 @@ module Emails
@target_url = user_url(@user)
mail(to: @user.notification_email, subject: subject("SSH key was added to your account"))
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def new_gpg_key_email(gpg_key_id)
@gpg_key = GpgKey.find_by(id: gpg_key_id)
@@ -26,5 +31,6 @@ module Emails
@target_url = user_url(@user)
mail(to: @user.notification_email, subject: subject("GPG key was added to your account"))
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 761d873c01c..d7e6c2ba7b2 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Projects
def project_was_moved_email(project_id, user_id, old_path_with_namespace)
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 0e1e39501f5..662f3e00047 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Notify < BaseMailer
include ActionDispatch::Routing::PolymorphicRoutes
include GitlabRoutingHelper
@@ -10,6 +12,7 @@ class Notify < BaseMailer
include Emails::Profile
include Emails::Pipelines
include Emails::Members
+ include Emails::AutoDevops
helper MergeRequestsHelper
helper DiffHelper
@@ -92,12 +95,14 @@ class Notify < BaseMailer
# >> subject('Lorem ipsum', 'Dolor sit amet')
# => "Lorem ipsum | Dolor sit amet"
def subject(*extra)
- subject = ""
- subject << "#{@project.name} | " if @project
- subject << "#{@group.name} | " if @group
- subject << extra.join(' | ') if extra.present?
- subject << " | #{Gitlab.config.gitlab.email_subject_suffix}" if Gitlab.config.gitlab.email_subject_suffix.present?
- subject
+ subject = []
+
+ subject << @project.name if @project
+ subject << @group.name if @group
+ subject.concat(extra) if extra.present?
+ subject << Gitlab.config.gitlab.email_subject_suffix if Gitlab.config.gitlab.email_subject_suffix.present?
+
+ subject.join(' | ')
end
# Return a string suitable for inclusion in the 'Message-Id' mail header.
@@ -113,6 +118,7 @@ class Notify < BaseMailer
add_unsubscription_headers_and_links
headers["X-GitLab-#{model.class.name}-ID"] = model.id
+ headers["X-GitLab-#{model.class.name}-IID"] = model.iid if model.respond_to?(:iid)
headers['X-GitLab-Reply-Key'] = reply_key
@reason = headers['X-GitLab-NotificationReason']
diff --git a/app/mailers/previews/devise_mailer_preview.rb b/app/mailers/previews/devise_mailer_preview.rb
index d6588efc486..3b9ef0d3ac0 100644
--- a/app/mailers/previews/devise_mailer_preview.rb
+++ b/app/mailers/previews/devise_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeviseMailerPreview < ActionMailer::Preview
def confirmation_instructions_for_signup
DeviseMailer.confirmation_instructions(unsaved_user, 'faketoken', {})
diff --git a/app/mailers/previews/email_rejection_mailer_preview.rb b/app/mailers/previews/email_rejection_mailer_preview.rb
index 639e8471232..402066151ef 100644
--- a/app/mailers/previews/email_rejection_mailer_preview.rb
+++ b/app/mailers/previews/email_rejection_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailRejectionMailerPreview < ActionMailer::Preview
def rejection
EmailRejectionMailer.rejection("some rejection reason", "From: someone@example.com\nraw email here").message
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index 3615cde8026..e7e8d96eca4 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NotifyPreview < ActionMailer::Preview
def note_merge_request_email_for_individual_note
note_email(:note_merge_request_email) do
@@ -7,7 +9,7 @@ class NotifyPreview < ActionMailer::Preview
In this notification email, we expect to see:
- The note contents (that's what you're looking at)
- - A link to view this note on Gitlab
+ - A link to view this note on GitLab
- An explanation for why the user is receiving this notification
MD
@@ -24,7 +26,7 @@ class NotifyPreview < ActionMailer::Preview
- A line saying who started this discussion
- The note contents (that's what you're looking at)
- - A link to view this discussion on Gitlab
+ - A link to view this discussion on GitLab
- An explanation for why the user is receiving this notification
MD
@@ -42,7 +44,7 @@ class NotifyPreview < ActionMailer::Preview
- A line saying who started this discussion and on what file
- The diff
- The note contents (that's what you're looking at)
- - A link to view this discussion on Gitlab
+ - A link to view this discussion on GitLab
- An explanation for why the user is receiving this notification
MD
@@ -66,6 +68,14 @@ class NotifyPreview < ActionMailer::Preview
Notify.issue_status_changed_email(user.id, issue.id, 'closed', user.id).message
end
+ def removed_milestone_issue_email
+ Notify.removed_milestone_issue_email(user.id, issue.id, user.id)
+ end
+
+ def changed_milestone_issue_email
+ Notify.changed_milestone_issue_email(user.id, issue.id, milestone, user.id)
+ end
+
def closed_merge_request_email
Notify.closed_merge_request_email(user.id, issue.id, user.id).message
end
@@ -78,6 +88,14 @@ class NotifyPreview < ActionMailer::Preview
Notify.merged_merge_request_email(user.id, merge_request.id, user.id).message
end
+ def removed_milestone_merge_request_email
+ Notify.removed_milestone_merge_request_email(user.id, merge_request.id, user.id)
+ end
+
+ def changed_milestone_merge_request_email
+ Notify.changed_milestone_merge_request_email(user.id, merge_request.id, milestone, user.id)
+ end
+
def member_access_denied_email
Notify.member_access_denied_email('project', project.id, user.id).message
end
@@ -123,6 +141,10 @@ class NotifyPreview < ActionMailer::Preview
Notify.pipeline_failed_email(pipeline, pipeline.user.try(:email))
end
+ def autodevops_disabled_email
+ Notify.autodevops_disabled_email(pipeline, user.email).message
+ end
+
private
def project
@@ -137,6 +159,10 @@ class NotifyPreview < ActionMailer::Preview
@merge_request ||= project.merge_requests.first
end
+ def milestone
+ @milestone ||= issue.milestone
+ end
+
def pipeline
@pipeline = Ci::Pipeline.last
end
diff --git a/app/mailers/previews/repository_check_mailer_preview.rb b/app/mailers/previews/repository_check_mailer_preview.rb
index 19d4eab1805..834d7594719 100644
--- a/app/mailers/previews/repository_check_mailer_preview.rb
+++ b/app/mailers/previews/repository_check_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryCheckMailerPreview < ActionMailer::Preview
def notify
RepositoryCheckMailer.notify(3).message
diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb
index 22a9f5da646..145169be8a6 100644
--- a/app/mailers/repository_check_mailer.rb
+++ b/app/mailers/repository_check_mailer.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
class RepositoryCheckMailer < BaseMailer
+ # rubocop: disable CodeReuse/ActiveRecord
def notify(failed_count)
@message =
if failed_count == 1
@@ -12,4 +15,5 @@ class RepositoryCheckMailer < BaseMailer
subject: "GitLab Admin | #{@message}"
)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index a853106e5bd..1466407d0d1 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -74,7 +74,7 @@ class Ability
end
def policy_for(user, subject = :global)
- cache = RequestStore.active? ? RequestStore : {}
+ cache = Gitlab::SafeRequestStore.active? ? Gitlab::SafeRequestStore : {}
DeclarativePolicy.policy_for(user, subject, cache: cache)
end
diff --git a/app/models/application_record.rb b/app/models/application_record.rb
new file mode 100644
index 00000000000..71fbba5b328
--- /dev/null
+++ b/app/models/application_record.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ApplicationRecord < ActiveRecord::Base
+ self.abstract_class = true
+end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index bbe7811841a..207ffae873a 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -4,6 +4,8 @@ class ApplicationSetting < ActiveRecord::Base
include CacheableAttributes
include CacheMarkdownField
include TokenAuthenticatable
+ include IgnorableColumn
+ include ChronicDurationAttribute
add_authentication_token_field :runners_registration_token
add_authentication_token_field :health_check_access_token
@@ -26,7 +28,14 @@ class ApplicationSetting < ActiveRecord::Base
serialize :domain_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize
serialize :domain_blacklist, Array # rubocop:disable Cop/ActiveRecordSerialize
serialize :repository_storages # rubocop:disable Cop/ActiveRecordSerialize
- serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiveRecordSerialize
+
+ ignore_column :circuitbreaker_failure_count_threshold
+ ignore_column :circuitbreaker_failure_reset_time
+ ignore_column :circuitbreaker_storage_timeout
+ ignore_column :circuitbreaker_access_retries
+ ignore_column :circuitbreaker_check_interval
+ ignore_column :koding_url
+ ignore_column :koding_enabled
cache_markdown_field :sign_in_text
cache_markdown_field :help_page_text
@@ -37,6 +46,8 @@ class ApplicationSetting < ActiveRecord::Base
default_value_for :id, 1
+ chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds
+
validates :uuid, presence: true
validates :session_expire_delay,
@@ -94,10 +105,6 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
if: :unique_ips_limit_enabled
- validates :koding_url,
- presence: true,
- if: :koding_enabled
-
validates :plantuml_url,
presence: true,
if: :plantuml_enabled
@@ -131,15 +138,6 @@ class ApplicationSetting < ActiveRecord::Base
presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' },
if: :domain_blacklist_enabled?
- validates :sidekiq_throttling_factor,
- numericality: { greater_than: 0, less_than: 1 },
- presence: { message: 'Throttling factor cannot be empty if Sidekiq Throttling is enabled.' },
- if: :sidekiq_throttling_enabled?
-
- validates :sidekiq_throttling_queues,
- presence: { message: 'Queues to throttle cannot be empty if Sidekiq Throttling is enabled.' },
- if: :sidekiq_throttling_enabled?
-
validates :housekeeping_incremental_repack_period,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
@@ -160,17 +158,6 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
numericality: { greater_than_or_equal_to: 0 }
- validates :circuitbreaker_failure_count_threshold,
- :circuitbreaker_failure_reset_time,
- :circuitbreaker_storage_timeout,
- :circuitbreaker_check_interval,
- presence: true,
- numericality: { only_integer: true, greater_than_or_equal_to: 0 }
-
- validates :circuitbreaker_access_retries,
- presence: true,
- numericality: { only_integer: true, greater_than_or_equal_to: 1 }
-
validates :gitaly_timeout_default,
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
@@ -192,6 +179,20 @@ class ApplicationSetting < ActiveRecord::Base
numericality: { less_than_or_equal_to: :gitaly_timeout_default },
if: :gitaly_timeout_default
+ validates :diff_max_patch_bytes,
+ presence: true,
+ numericality: { only_integer: true,
+ greater_than_or_equal_to: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
+ less_than_or_equal_to: Gitlab::Git::Diff::MAX_PATCH_BYTES_UPPER_BOUND }
+
+ validates :user_default_internal_regex, js_regex: true, allow_nil: true
+
+ validates :commit_email_hostname, format: { with: /\A[^@]+\z/ }
+
+ validates :archive_builds_in_seconds,
+ allow_nil: true,
+ numericality: { only_integer: true, greater_than_or_equal_to: 1.day.seconds }
+
SUPPORTED_KEY_TYPES.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
end
@@ -217,6 +218,7 @@ class ApplicationSetting < ActiveRecord::Base
validate :terms_exist, if: :enforce_terms?
before_validation :ensure_uuid!
+ before_validation :strip_sentry_values
before_save :ensure_runners_registration_token
before_save :ensure_health_check_access_token
@@ -257,8 +259,6 @@ class ApplicationSetting < ActiveRecord::Base
housekeeping_gc_period: 200,
housekeeping_incremental_repack_period: 10,
import_sources: Settings.gitlab['import_sources'],
- koding_enabled: false,
- koding_url: nil,
max_artifacts_size: Settings.artifacts['max_size'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
mirror_available: true,
@@ -279,7 +279,6 @@ class ApplicationSetting < ActiveRecord::Base
send_user_confirmation_email: false,
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
shared_runners_text: nil,
- sidekiq_throttling_enabled: false,
sign_in_text: nil,
signup_enabled: Settings.gitlab['signup_enabled'],
terminal_max_session_time: 0,
@@ -298,10 +297,19 @@ class ApplicationSetting < ActiveRecord::Base
unique_ips_limit_time_window: 3600,
usage_ping_enabled: Settings.gitlab['usage_ping_enabled'],
instance_statistics_visibility_private: false,
- user_default_external: false
+ user_default_external: false,
+ user_default_internal_regex: nil,
+ user_show_add_ssh_key_message: true,
+ usage_stats_set_by_user_id: nil,
+ diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
+ commit_email_hostname: default_commit_email_hostname
}
end
+ def self.default_commit_email_hostname
+ "users.noreply.#{Gitlab.config.gitlab.host}"
+ end
+
def self.create_from_defaults
create(defaults)
end
@@ -322,10 +330,6 @@ class ApplicationSetting < ActiveRecord::Base
::Gitlab::Database.cached_column_exists?(:application_settings, :help_page_support_url)
end
- def sidekiq_throttling_column_exists?
- ::Gitlab::Database.cached_column_exists?(:application_settings, :sidekiq_throttling_enabled)
- end
-
def disabled_oauth_sign_in_sources=(sources)
sources = (sources || []).map(&:to_s) & Devise.omniauth_providers.map(&:to_s)
super(sources)
@@ -361,6 +365,10 @@ class ApplicationSetting < ActiveRecord::Base
Array(read_attribute(:repository_storages))
end
+ def commit_email_hostname
+ super.presence || self.class.default_commit_email_hostname
+ end
+
def default_project_visibility=(level)
super(Gitlab::VisibilityLevel.level_value(level))
end
@@ -377,6 +385,11 @@ class ApplicationSetting < ActiveRecord::Base
super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) })
end
+ def strip_sentry_values
+ sentry_dsn.strip! if sentry_dsn.present?
+ clientside_sentry_dsn.strip! if clientside_sentry_dsn.present?
+ end
+
def performance_bar_allowed_group
Group.find_by_id(performance_bar_allowed_group_id)
end
@@ -400,12 +413,6 @@ class ApplicationSetting < ActiveRecord::Base
ensure_health_check_access_token!
end
- def sidekiq_throttling_enabled?
- return false unless sidekiq_throttling_column_exists?
-
- sidekiq_throttling_enabled
- end
-
def usage_ping_can_be_configured?
Settings.gitlab.usage_ping_enabled
end
@@ -434,6 +441,14 @@ class ApplicationSetting < ActiveRecord::Base
password_authentication_enabled_for_web? || password_authentication_enabled_for_git?
end
+ def user_default_internal_regex_enabled?
+ user_default_external? && user_default_internal_regex.present?
+ end
+
+ def user_default_internal_regex_instance
+ Regexp.new(user_default_internal_regex, Regexp::IGNORECASE)
+ end
+
delegate :terms, to: :latest_terms, allow_nil: true
def latest_terms
@latest_terms ||= Term.latest
@@ -444,6 +459,10 @@ class ApplicationSetting < ActiveRecord::Base
latest_terms
end
+ def archive_builds_older_than
+ archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds
+ end
+
private
def ensure_uuid!
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
index 99c7866d636..ddc516ccb60 100644
--- a/app/models/award_emoji.rb
+++ b/app/models/award_emoji.rb
@@ -28,6 +28,23 @@ class AwardEmoji < ActiveRecord::Base
.where('name IN (?) AND awardable_type = ? AND awardable_id IN (?)', [DOWNVOTE_NAME, UPVOTE_NAME], type, ids)
.group('name', 'awardable_id')
end
+
+ # Returns the top 100 emoji awarded by the given user.
+ #
+ # The returned value is a Hash mapping emoji names to the number of times
+ # they were awarded:
+ #
+ # { 'thumbsup' => 2, 'thumbsdown' => 1 }
+ #
+ # user - The User to get the awards for.
+ # limt - The maximum number of emoji to return.
+ def award_counts_for_user(user, limit = 100)
+ limit(limit)
+ .where(user: user)
+ .group(:name)
+ .order('count_all DESC, name ASC')
+ .count
+ end
end
def downvote?
diff --git a/app/models/badge.rb b/app/models/badge.rb
index 7e3b6b659e4..f016654206b 100644
--- a/app/models/badge.rb
+++ b/app/models/badge.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Badge < ActiveRecord::Base
+ include FromUnion
+
# This structure sets the placeholders that the urls
# can have. This hash also sets which action to ask when
# the placeholder is found.
diff --git a/app/models/blob.rb b/app/models/blob.rb
index acc64ffca67..4f310e70f4f 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
-# Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects
+# Blob is a Rails-specific wrapper around Gitlab::Git::Blob, SnippetBlob and Ci::ArtifactBlob
class Blob < SimpleDelegator
+ include Presentable
+ include BlobLanguageFromGitAttributes
+
CACHE_TIME = 60 # Cache raw blobs referred to by a (mutable) ref for 1 minute
CACHE_TIME_IMMUTABLE = 3600 # Cache blobs referred to by an immutable reference for 1 hour
- MAXIMUM_TEXT_HIGHLIGHT_SIZE = 1.megabyte
-
# Finding a viewer for a blob happens based only on extension and whether the
# blob is binary or text, which means 1 blob should only be matched by 1 viewer,
# and the order of these viewers doesn't really matter.
@@ -121,10 +122,6 @@ class Blob < SimpleDelegator
end
end
- def no_highlighting?
- raw_size && raw_size > MAXIMUM_TEXT_HIGHLIGHT_SIZE
- end
-
def empty?
raw_size == 0
end
@@ -162,7 +159,7 @@ class Blob < SimpleDelegator
if stored_externally?
if rich_viewer
rich_viewer.binary?
- elsif Linguist::Language.find_by_extension(name).any?
+ elsif known_extension?
false
elsif _mime_type
_mime_type.binary?
diff --git a/app/models/blob_viewer/gitlab_ci_yml.rb b/app/models/blob_viewer/gitlab_ci_yml.rb
index 1a86f04b1b9..655241c2808 100644
--- a/app/models/blob_viewer/gitlab_ci_yml.rb
+++ b/app/models/blob_viewer/gitlab_ci_yml.rb
@@ -10,16 +10,16 @@ module BlobViewer
self.file_types = %i(gitlab_ci)
self.binary = false
- def validation_message
+ def validation_message(project, sha)
return @validation_message if defined?(@validation_message)
prepare!
- @validation_message = Gitlab::Ci::YamlProcessor.validation_message(blob.data)
+ @validation_message = Gitlab::Ci::YamlProcessor.validation_message(blob.data, { project: project, sha: sha })
end
- def valid?
- validation_message.blank?
+ def valid?(project, sha)
+ validation_message(project, sha).blank?
end
end
end
diff --git a/app/models/blob_viewer/package_json.rb b/app/models/blob_viewer/package_json.rb
index d12dd93ce2e..7cae60a74d6 100644
--- a/app/models/blob_viewer/package_json.rb
+++ b/app/models/blob_viewer/package_json.rb
@@ -33,7 +33,8 @@ module BlobViewer
end
def homepage
- json_data['homepage']
+ url = json_data['homepage']
+ url if Gitlab::UrlSanitizer.valid?(url)
end
def npm_url
diff --git a/app/models/board_group_recent_visit.rb b/app/models/board_group_recent_visit.rb
new file mode 100644
index 00000000000..92abbb67222
--- /dev/null
+++ b/app/models/board_group_recent_visit.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# Tracks which boards in a specific group a user has visited
+class BoardGroupRecentVisit < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :group
+ belongs_to :board
+
+ validates :user, presence: true
+ validates :group, presence: true
+ validates :board, presence: true
+
+ scope :by_user_group, -> (user, group) { where(user: user, group: group).order(:updated_at) }
+
+ def self.visited!(user, board)
+ visit = find_or_create_by(user: user, group: board.group, board: board)
+ visit.touch if visit.updated_at < Time.now
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+
+ def self.latest(user, group)
+ by_user_group(user, group).last
+ end
+end
diff --git a/app/models/board_project_recent_visit.rb b/app/models/board_project_recent_visit.rb
new file mode 100644
index 00000000000..7cffff906d8
--- /dev/null
+++ b/app/models/board_project_recent_visit.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# Tracks which boards in a specific project a user has visited
+class BoardProjectRecentVisit < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :project
+ belongs_to :board
+
+ validates :user, presence: true
+ validates :project, presence: true
+ validates :board, presence: true
+
+ scope :by_user_project, -> (user, project) { where(user: user, project: project).order(:updated_at) }
+
+ def self.visited!(user, board)
+ visit = find_or_create_by(user: user, project: board.project, board: board)
+ visit.touch if visit.updated_at < Time.now
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+
+ def self.latest(user, project)
+ by_user_project(user, project).last
+ end
+end
diff --git a/app/models/ci/artifact_blob.rb b/app/models/ci/artifact_blob.rb
index cd0b31482d2..d87d6a5cb2f 100644
--- a/app/models/ci/artifact_blob.rb
+++ b/app/models/ci/artifact_blob.rb
@@ -4,7 +4,7 @@ module Ci
class ArtifactBlob
include BlobLike
- EXTENSIONS_SERVED_BY_PAGES = %w[.html .htm .txt .json].freeze
+ EXTENSIONS_SERVED_BY_PAGES = %w[.html .htm .txt .json .log].freeze
attr_reader :entry
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 3c69677baf0..d60861dc95f 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -9,19 +9,18 @@ module Ci
include Presentable
include Importable
include Gitlab::Utils::StrongMemoize
+ include Deployable
belongs_to :project, inverse_of: :builds
belongs_to :runner
belongs_to :trigger_request
belongs_to :erased_by, class_name: 'User'
- has_many :deployments, as: :deployable
-
RUNNER_FEATURES = {
upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? }
}.freeze
- has_one :last_deployment, -> { order('deployments.id DESC') }, as: :deployable, class_name: 'Deployment'
+ has_one :deployment, as: :deployable, class_name: 'Deployment'
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id
@@ -40,6 +39,7 @@ module Ci
delegate :url, to: :runner_session, prefix: true, allow_nil: true
delegate :terminal_specification, to: :runner_session, allow_nil: true
delegate :gitlab_deploy_token, to: :project
+ delegate :trigger_short_token, to: :trigger_request, allow_nil: true
##
# The "environment" field for builds is a String, and is the unexpanded name!
@@ -67,26 +67,38 @@ module Ci
'', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').archive)
end
+ scope :with_existing_job_artifacts, ->(query) do
+ where('EXISTS (?)', ::Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').merge(query))
+ end
+
+ scope :with_archived_trace, ->() do
+ with_existing_job_artifacts(Ci::JobArtifact.trace)
+ end
+
scope :without_archived_trace, ->() do
where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace)
end
scope :with_test_reports, ->() do
- includes(:job_artifacts_junit) # Prevent N+1 problem when iterating each ci_job_artifact row
- .where('EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').test_reports)
+ with_existing_job_artifacts(Ci::JobArtifact.test_reports)
+ .eager_load_job_artifacts
end
+ scope :eager_load_job_artifacts, -> { includes(:job_artifacts) }
+
scope :with_artifacts_stored_locally, -> { with_artifacts_archive.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
+ scope :with_archived_trace_stored_locally, -> { with_archived_trace.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
- scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) }
+ scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + %i[manual]) }
+ scope :scheduled_actions, ->() { where(when: :delayed, status: COMPLETED_STATUSES + %i[scheduled]) }
scope :ref_protected, -> { where(protected: true) }
scope :with_live_trace, -> { where('EXISTS (?)', Ci::BuildTraceChunk.where('ci_builds.id = ci_build_trace_chunks.build_id').select(1)) }
scope :matches_tag_ids, -> (tag_ids) do
matcher = ::ActsAsTaggableOn::Tagging
- .where(taggable_type: CommitStatus)
+ .where(taggable_type: CommitStatus.name)
.where(context: 'tags')
.where('taggable_id = ci_builds.id')
.where.not(tag_id: tag_ids).select('1')
@@ -96,7 +108,7 @@ module Ci
scope :with_any_tags, -> do
matcher = ::ActsAsTaggableOn::Tagging
- .where(taggable_type: CommitStatus)
+ .where(taggable_type: CommitStatus.name)
.where(context: 'tags')
.where('taggable_id = ci_builds.id').select('1')
@@ -134,9 +146,11 @@ module Ci
end
def retry(build, current_user)
+ # rubocop: disable CodeReuse/ServiceClass
Ci::RetryBuildService
.new(build.project, current_user)
.execute(build)
+ # rubocop: enable CodeReuse/ServiceClass
end
end
@@ -145,6 +159,34 @@ module Ci
transition created: :manual
end
+ event :schedule do
+ transition created: :scheduled
+ end
+
+ event :unschedule do
+ transition scheduled: :manual
+ end
+
+ event :enqueue_scheduled do
+ transition scheduled: :pending, if: ->(build) do
+ build.scheduled_at && build.scheduled_at < Time.now
+ end
+ end
+
+ before_transition scheduled: any do |build|
+ build.scheduled_at = nil
+ end
+
+ before_transition created: :scheduled do |build|
+ build.scheduled_at = build.options_scheduled_at
+ end
+
+ after_transition created: :scheduled do |build|
+ build.run_after_commit do
+ Ci::BuildScheduleWorker.perform_at(build.scheduled_at, build.id)
+ end
+ end
+
after_transition any => [:pending] do |build|
build.run_after_commit do
BuildQueueWorker.perform_async(id)
@@ -152,6 +194,8 @@ module Ci
end
after_transition pending: :running do |build|
+ build.deployment&.run
+
build.run_after_commit do
BuildHooksWorker.perform_async(id)
end
@@ -164,6 +208,8 @@ module Ci
end
after_transition any => [:success] do |build|
+ build.deployment&.succeed
+
build.run_after_commit do
BuildSuccessWorker.perform_async(id)
PagesWorker.perform_async(:deploy, id) if build.pages_generator?
@@ -172,9 +218,10 @@ module Ci
before_transition any => [:failed] do |build|
next unless build.project
- next if build.retries_max.zero?
- if build.retries_count < build.retries_max
+ build.deployment&.drop
+
+ if build.retry_failure?
begin
Ci::Build.retry(build, build.user)
rescue Gitlab::Access::AccessDeniedError => ex
@@ -190,6 +237,10 @@ module Ci
after_transition running: any do |build|
Ci::BuildRunnerSession.where(build: build).delete_all
end
+
+ after_transition any => [:skipped, :canceled] do |build|
+ build.deployment&.cancel
+ end
end
def ensure_metadata
@@ -202,35 +253,65 @@ module Ci
.fabricate!
end
- def other_actions
+ def other_manual_actions
pipeline.manual_actions.where.not(name: name)
end
+ def other_scheduled_actions
+ pipeline.scheduled_actions.where.not(name: name)
+ end
+
def pages_generator?
Gitlab.config.pages.enabled &&
self.name == 'pages'
end
+ # degenerated build is one that cannot be run by Runner
+ def degenerated?
+ self.options.nil?
+ end
+
+ def degenerate!
+ self.update!(options: nil, yaml_variables: nil, commands: nil)
+ end
+
+ def archived?
+ return true if degenerated?
+
+ archive_builds_older_than = Gitlab::CurrentSettings.current_application_settings.archive_builds_older_than
+ archive_builds_older_than.present? && created_at < archive_builds_older_than
+ end
+
def playable?
- action? && (manual? || retryable?)
+ action? && !archived? && (manual? || scheduled? || retryable?)
+ end
+
+ def schedulable?
+ self.when == 'delayed' && options[:start_in].present?
+ end
+
+ def options_scheduled_at
+ ChronicDuration.parse(options[:start_in])&.seconds&.from_now
end
def action?
- self.when == 'manual'
+ %w[manual delayed].include?(self.when)
end
+ # rubocop: disable CodeReuse/ServiceClass
def play(current_user)
Ci::PlayBuildService
.new(project, current_user)
.execute(self)
end
+ # rubocop: enable CodeReuse/ServiceClass
def cancelable?
active? || created?
end
def retryable?
- success? || failed? || canceled?
+ !archived? && (success? || failed? || canceled?)
end
def retries_count
@@ -238,7 +319,17 @@ module Ci
end
def retries_max
- self.options.fetch(:retry, 0).to_i
+ normalized_retry.fetch(:max, 0)
+ end
+
+ def retry_when
+ normalized_retry.fetch(:when, ['always'])
+ end
+
+ def retry_failure?
+ return false if retries_max.zero? || retries_count >= retries_max
+
+ retry_when.include?('always') || retry_when.include?(failure_reason.to_s)
end
def latest?
@@ -269,8 +360,12 @@ module Ci
self.options.fetch(:environment, {}).fetch(:action, 'start') if self.options
end
+ def has_deployment?
+ !!self.deployment
+ end
+
def outdated_deployment?
- success? && !last_deployment.try(:last?)
+ success? && !deployment.try(:last?)
end
def depends_on_builds
@@ -285,6 +380,10 @@ module Ci
user == current_user
end
+ def on_stop
+ options&.dig(:environment, :on_stop)
+ end
+
# A slugified version of the build ref, suitable for inclusion in URLs and
# domain names. Rules:
#
@@ -365,7 +464,9 @@ module Ci
end
def repo_url
- auth = "gitlab-ci-token:#{ensure_token!}@"
+ return unless token
+
+ auth = "gitlab-ci-token:#{token}@"
project.http_url_to_repo.sub(%r{^https?://}) do |prefix|
prefix + auth
end
@@ -380,9 +481,11 @@ module Ci
update(coverage: coverage) if coverage.present?
end
+ # rubocop: disable CodeReuse/ServiceClass
def parse_trace_sections!
ExtractSectionsFromBuildTraceService.new(project, user).execute(self)
end
+ # rubocop: enable CodeReuse/ServiceClass
def trace
Gitlab::Ci::Trace.new(self)
@@ -392,8 +495,8 @@ module Ci
trace.exist?
end
- def has_test_reports?
- job_artifacts.test_reports.any?
+ def has_job_artifacts?
+ job_artifacts.any?
end
def has_old_trace?
@@ -457,28 +560,23 @@ module Ci
end
end
- def erase_artifacts!
- remove_artifacts_file!
- remove_artifacts_metadata!
- save
- end
-
- def erase_test_reports!
- # TODO: Use fast_destroy_all in the context of https://gitlab.com/gitlab-org/gitlab-ce/issues/35240
- job_artifacts_junit&.destroy
+ # and use that for `ExpireBuildInstanceArtifactsWorker`?
+ def erase_erasable_artifacts!
+ job_artifacts.erasable.destroy_all # rubocop: disable DestroyAll
+ erase_old_artifacts!
end
def erase(opts = {})
return false unless erasable?
- erase_artifacts!
- erase_test_reports!
+ job_artifacts.destroy_all # rubocop: disable DestroyAll
+ erase_old_artifacts!
erase_trace!
update_erased!(opts[:erased_by])
end
def erasable?
- complete? && (artifacts? || has_test_reports? || has_trace?)
+ complete? && (artifacts? || has_job_artifacts? || has_trace?)
end
def erased?
@@ -509,6 +607,13 @@ module Ci
self.job_artifacts.update_all(expire_at: nil)
end
+ def artifacts_file_for_type(type)
+ file = job_artifacts.find_by(file_type: Ci::JobArtifact.file_types[type])&.file
+ # TODO: to be removed once legacy artifacts is removed
+ file ||= legacy_artifacts_file if type == :archive
+ file
+ end
+
def coverage_regex
super || project.try(:build_coverage_regex)
end
@@ -535,11 +640,11 @@ module Ci
def secret_group_variables
return [] unless project.group
- project.group.secret_variables_for(ref, project)
+ project.group.ci_variables_for(ref, project)
end
def secret_project_variables(environment: persisted_environment)
- project.secret_variables_for(ref: ref, environment: environment)
+ project.ci_variables_for(ref: ref, environment: environment)
end
def steps
@@ -622,7 +727,7 @@ module Ci
trace = trace.dup
Gitlab::Ci::MaskSecret.mask!(trace, project.runners_token) if project
- Gitlab::Ci::MaskSecret.mask!(trace, token)
+ Gitlab::Ci::MaskSecret.mask!(trace, token) if token
trace
end
@@ -636,22 +741,55 @@ module Ci
def collect_test_reports!(test_reports)
test_reports.get_suite(group_name).tap do |test_suite|
- each_test_report do |file_type, blob|
- Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, test_suite)
+ each_report(Ci::JobArtifact::TEST_REPORT_FILE_TYPES) do |file_type, blob|
+ Gitlab::Ci::Parsers::Test.fabricate!(file_type).parse!(blob, test_suite)
end
end
end
+ # Virtual deployment status depending on the environment status.
+ def deployment_status
+ return nil unless starts_environment?
+
+ if success?
+ return successful_deployment_status
+ elsif failed?
+ return :failed
+ end
+
+ :creating
+ end
+
private
- def each_test_report
- Ci::JobArtifact::TEST_REPORT_FILE_TYPES.each do |file_type|
- public_send("job_artifacts_#{file_type}").each_blob do |blob| # rubocop:disable GitlabSecurity/PublicSend
- yield file_type, blob
+ def erase_old_artifacts!
+ # TODO: To be removed once we get rid of
+ remove_artifacts_file!
+ remove_artifacts_metadata!
+ save
+ end
+
+ def successful_deployment_status
+ if deployment&.last?
+ :last
+ else
+ :out_of_date
+ end
+ end
+
+ def each_report(report_types)
+ job_artifacts_for_types(report_types).each do |report_artifact|
+ report_artifact.each_blob do |blob|
+ yield report_artifact.file_type, blob
end
end
end
+ def job_artifacts_for_types(report_types)
+ # Use select to leverage cached associations and avoid N+1 queries
+ job_artifacts.select { |artifact| artifact.file_type.in?(report_types) }
+ end
+
def update_artifacts_size
self.artifacts_size = legacy_artifacts_file&.size
end
@@ -678,23 +816,26 @@ module Ci
.concat(pipeline.persisted_variables)
.append(key: 'CI_JOB_ID', value: id.to_s)
.append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self))
- .append(key: 'CI_JOB_TOKEN', value: token, public: false)
+ .append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false)
.append(key: 'CI_BUILD_ID', value: id.to_s)
- .append(key: 'CI_BUILD_TOKEN', value: token, public: false)
+ .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false)
.append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER)
- .append(key: 'CI_REGISTRY_PASSWORD', value: token, public: false)
- .append(key: 'CI_REPOSITORY_URL', value: repo_url, public: false)
+ .append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false)
+ .append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false)
.concat(deploy_token_variables)
end
end
- def predefined_variables
+ def predefined_variables # rubocop:disable Metrics/AbcSize
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI', value: 'true')
variables.append(key: 'GITLAB_CI', value: 'true')
variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(','))
variables.append(key: 'CI_SERVER_NAME', value: 'GitLab')
variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
+ variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s)
+ variables.append(key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s)
+ variables.append(key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s)
variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision)
variables.append(key: 'CI_JOB_NAME', value: name)
variables.append(key: 'CI_JOB_STAGE', value: stage)
@@ -705,6 +846,8 @@ module Ci
variables.append(key: "CI_COMMIT_TAG", value: ref) if tag?
variables.append(key: "CI_PIPELINE_TRIGGERED", value: 'true') if trigger_request
variables.append(key: "CI_JOB_MANUAL", value: 'true') if action?
+ variables.append(key: "CI_NODE_INDEX", value: self.options[:instance].to_s) if self.options&.include?(:instance)
+ variables.append(key: "CI_NODE_TOTAL", value: (self.options&.dig(:parallel) || 1).to_s)
variables.concat(legacy_variables)
end
end
@@ -749,6 +892,16 @@ module Ci
options&.dig(:environment, :url) || persisted_environment&.external_url
end
+ # The format of the retry option changed in GitLab 11.5: Before it was
+ # integer only, after it is a hash. New builds are created with the new
+ # format, but builds created before GitLab 11.5 and saved in database still
+ # have the old integer only format. This method returns the retry option
+ # normalized as a hash in 11.5+ format.
+ def normalized_retry
+ value = options&.dig(:retry)
+ value.is_a?(Integer) ? { max: value } : value.to_h
+ end
+
def build_attributes_from_config
return {} unless pipeline.config_processor
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index d7c5f29be96..11c88200c37 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -9,8 +9,38 @@ module Ci
NotSupportedAdapterError = Class.new(StandardError)
TEST_REPORT_FILE_TYPES = %w[junit].freeze
- DEFAULT_FILE_NAMES = { junit: 'junit.xml' }.freeze
- TYPE_AND_FORMAT_PAIRS = { archive: :zip, metadata: :gzip, trace: :raw, junit: :gzip }.freeze
+ NON_ERASABLE_FILE_TYPES = %w[trace].freeze
+ DEFAULT_FILE_NAMES = {
+ archive: nil,
+ metadata: nil,
+ trace: nil,
+ junit: 'junit.xml',
+ codequality: 'gl-code-quality-report.json',
+ sast: 'gl-sast-report.json',
+ dependency_scanning: 'gl-dependency-scanning-report.json',
+ container_scanning: 'gl-container-scanning-report.json',
+ dast: 'gl-dast-report.json',
+ license_management: 'gl-license-management-report.json',
+ performance: 'performance.json'
+ }.freeze
+
+ TYPE_AND_FORMAT_PAIRS = {
+ archive: :zip,
+ metadata: :gzip,
+ trace: :raw,
+ junit: :gzip,
+
+ # All these file formats use `raw` as we need to store them uncompressed
+ # for Frontend to fetch the files and do analysis
+ # When they will be only used by backend, they can be `gzipped`.
+ codequality: :raw,
+ sast: :raw,
+ dependency_scanning: :raw,
+ container_scanning: :raw,
+ dast: :raw,
+ license_management: :raw,
+ performance: :raw
+ }.freeze
belongs_to :project
belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
@@ -27,19 +57,36 @@ module Ci
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
+ scope :with_file_types, -> (file_types) do
+ types = self.file_types.select { |file_type| file_types.include?(file_type) }.values
+
+ where(file_type: types)
+ end
+
scope :test_reports, -> do
- types = self.file_types.select { |file_type| TEST_REPORT_FILE_TYPES.include?(file_type) }.values
+ with_file_types(TEST_REPORT_FILE_TYPES)
+ end
+
+ scope :erasable, -> do
+ types = self.file_types.reject { |file_type| NON_ERASABLE_FILE_TYPES.include?(file_type) }.values
where(file_type: types)
end
- delegate :exists?, :open, to: :file
+ delegate :filename, :exists?, :open, to: :file
enum file_type: {
archive: 1,
metadata: 2,
trace: 3,
- junit: 4
+ junit: 4,
+ sast: 5, ## EE-specific
+ dependency_scanning: 6, ## EE-specific
+ container_scanning: 7, ## EE-specific
+ dast: 8, ## EE-specific
+ codequality: 9, ## EE-specific
+ license_management: 10, ## EE-specific
+ performance: 11 ## EE-specific
}
enum file_format: {
@@ -48,8 +95,23 @@ module Ci
gzip: 3
}
+ # `file_location` indicates where actual files are stored.
+ # Ideally, actual files should be stored in the same directory, and use the same
+ # convention to generate its path. However, sometimes we can't do so due to backward-compatibility.
+ #
+ # legacy_path ... The actual file is stored at a path consists of a timestamp
+ # and raw project/model IDs. Those rows were migrated from
+ # `ci_builds.artifacts_file` and `ci_builds.artifacts_metadata`
+ # hashed_path ... The actual file is stored at a path consists of a SHA2 based on the project ID.
+ # This is the default value.
+ enum file_location: {
+ legacy_path: 1,
+ hashed_path: 2
+ }
+
FILE_FORMAT_ADAPTERS = {
- gzip: Gitlab::Ci::Build::Artifacts::GzipFileAdapter
+ gzip: Gitlab::Ci::Build::Artifacts::Adapters::GzipStream,
+ raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream
}.freeze
def valid_file_format?
@@ -72,6 +134,12 @@ module Ci
[nil, ::JobArtifactUploader::Store::LOCAL].include?(self.file_store)
end
+ def hashed_path?
+ return true if trace? # ArchiveLegacyTraces background migration might not have `file_location` column
+
+ super || self.file_location.nil?
+ end
+
def expire_in
expire_at - Time.now if expire_at
end
@@ -108,7 +176,7 @@ module Ci
end
def update_project_statistics_after_destroy
- update_project_statistics(-self.size)
+ update_project_statistics(-self.size.to_i)
end
def update_project_statistics(difference)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index e4aed76f611..9512ba42f67 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -35,6 +35,7 @@ module Ci
has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus'
has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
+ has_many :scheduled_actions, -> { latest.scheduled_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :artifacts, -> { latest.with_artifacts_not_expired.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id'
@@ -57,15 +58,9 @@ module Ci
after_create :keep_around_commits, unless: :importing?
- enum_with_nil source: {
- unknown: nil,
- push: 1,
- web: 2,
- trigger: 3,
- schedule: 4,
- api: 5,
- external: 6
- }
+ # We use `Ci::PipelineEnums.sources` here so that EE can more easily extend
+ # this `Hash` with new values.
+ enum_with_nil source: ::Ci::PipelineEnums.sources
enum_with_nil config_source: {
unknown_source: nil,
@@ -73,14 +68,13 @@ module Ci
auto_devops_source: 2
}
- enum failure_reason: {
- unknown_failure: 0,
- config_error: 1
- }
+ # We use `Ci::PipelineEnums.failure_reasons` here so that EE can more easily
+ # extend this `Hash` with new values.
+ enum failure_reason: ::Ci::PipelineEnums.failure_reasons
state_machine :status, initial: :created do
event :enqueue do
- transition [:created, :skipped] => :pending
+ transition [:created, :skipped, :scheduled] => :pending
transition [:success, :failed, :canceled] => :running
end
@@ -108,6 +102,10 @@ module Ci
transition any - [:manual] => :manual
end
+ event :delay do
+ transition any - [:scheduled] => :scheduled
+ end
+
# IMPORTANT
# Do not add any operations to this state_machine
# Create a separate worker for each new operation
@@ -161,6 +159,12 @@ module Ci
PipelineNotificationWorker.perform_async(pipeline.id)
end
end
+
+ after_transition any => [:failed] do |pipeline|
+ next unless pipeline.auto_devops_source?
+
+ pipeline.run_after_commit { AutoDevops::DisableWorker.perform_async(pipeline.id) }
+ end
end
scope :internal, -> { where(source: internal_sources) }
@@ -170,22 +174,31 @@ module Ci
#
# ref - The name (or names) of the branch(es)/tag(s) to limit the list of
# pipelines to.
- def self.newest_first(ref = nil)
+ # limit - This limits a backlog search, default to 100.
+ def self.newest_first(ref: nil, limit: 100)
relation = order(id: :desc)
+ relation = relation.where(ref: ref) if ref
- ref ? relation.where(ref: ref) : relation
+ if limit
+ ids = relation.limit(limit).select(:id)
+ # MySQL does not support limit in subquery
+ ids = ids.pluck(:id) if Gitlab::Database.mysql?
+ relation = relation.where(id: ids)
+ end
+
+ relation
end
def self.latest_status(ref = nil)
- newest_first(ref).pluck(:status).first
+ newest_first(ref: ref).pluck(:status).first
end
def self.latest_successful_for(ref)
- newest_first(ref).success.take
+ newest_first(ref: ref).success.take
end
def self.latest_successful_for_refs(refs)
- relation = newest_first(refs).success
+ relation = newest_first(ref: refs).success
relation.each_with_object({}) do |pipeline, hash|
hash[pipeline.ref] ||= pipeline
@@ -227,6 +240,10 @@ module Ci
end
end
+ def self.latest_successful_ids_per_project
+ success.group(:project_id).select('max(id) as id')
+ end
+
def self.truncate_sha(sha)
sha[0...8]
end
@@ -257,6 +274,12 @@ module Ci
stage unless stage.statuses_count.zero?
end
+ def ref_exists?
+ project.repository.ref_exists?(git_ref)
+ rescue Gitlab::Git::Repository::NoRepository
+ false
+ end
+
##
# TODO We do not completely switch to persisted stages because of
# race conditions with setting statuses gitlab-ce#23257.
@@ -381,10 +404,12 @@ module Ci
end
end
+ # rubocop: disable CodeReuse/ServiceClass
def retry_failed(current_user)
Ci::RetryPipelineService.new(project, current_user)
.execute(self)
end
+ # rubocop: enable CodeReuse/ServiceClass
def mark_as_processable_after_stage(stage_idx)
builds.skipped.after_stage(stage_idx).find_each(&:process)
@@ -458,7 +483,7 @@ module Ci
return @config_processor if defined?(@config_processor)
@config_processor ||= begin
- Gitlab::Ci::YamlProcessor.new(ci_yaml_file)
+ ::Gitlab::Ci::YamlProcessor.new(ci_yaml_file, { project: project, sha: sha })
rescue Gitlab::Ci::YamlProcessor::ValidationError => e
self.yaml_errors = e.message
nil
@@ -519,9 +544,11 @@ module Ci
project.notes.for_commit_id(sha)
end
+ # rubocop: disable CodeReuse/ServiceClass
def process!
Ci::ProcessPipelineService.new(project, user).execute(self)
end
+ # rubocop: enable CodeReuse/ServiceClass
def update_status
retry_optimistic_lock(self) do
@@ -534,6 +561,7 @@ module Ci
when 'canceled' then cancel
when 'skipped' then skip
when 'manual' then block
+ when 'scheduled' then delay
else
raise HasStatus::UnknownStatusError,
"Unknown status `#{latest_builds_status}`"
@@ -617,6 +645,22 @@ module Ci
end
end
+ def branch_updated?
+ strong_memoize(:branch_updated) do
+ push_details.branch_updated?
+ end
+ end
+
+ def modified_paths
+ strong_memoize(:modified_paths) do
+ push_details.modified_paths
+ end
+ end
+
+ def default_branch?
+ ref == project.default_branch
+ end
+
private
def ci_yaml_from_repo
@@ -640,6 +684,22 @@ module Ci
Gitlab::DataBuilder::Pipeline.build(self)
end
+ def push_details
+ strong_memoize(:push_details) do
+ Gitlab::Git::Push.new(project, before_sha, sha, git_ref)
+ end
+ end
+
+ def git_ref
+ if branch?
+ Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s
+ elsif tag?
+ Gitlab::Git::TAG_REF_PREFIX + ref.to_s
+ else
+ raise ArgumentError, 'Invalid pipeline type!'
+ end
+ end
+
def latest_builds_status
return 'failed' unless yaml_errors.blank?
@@ -649,8 +709,7 @@ module Ci
def keep_around_commits
return unless project
- project.repository.keep_around(self.sha)
- project.repository.keep_around(self.before_sha)
+ project.repository.keep_around(self.sha, self.before_sha)
end
def valid_source
diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb
new file mode 100644
index 00000000000..8d8d16e2ec1
--- /dev/null
+++ b/app/models/ci/pipeline_enums.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Ci
+ module PipelineEnums
+ # Returns the `Hash` to use for creating the `failure_reason` enum for
+ # `Ci::Pipeline`.
+ def self.failure_reasons
+ {
+ unknown_failure: 0,
+ config_error: 1
+ }
+ end
+
+ # Returns the `Hash` to use for creating the `sources` enum for
+ # `Ci::Pipeline`.
+ def self.sources
+ {
+ unknown: nil,
+ push: 1,
+ web: 2,
+ trigger: 3,
+ schedule: 4,
+ api: 5,
+ external: 6
+ }
+ end
+ end
+end
diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb
index 017ec0b145a..08514d6af4e 100644
--- a/app/models/ci/pipeline_variable.rb
+++ b/app/models/ci/pipeline_variable.rb
@@ -10,5 +10,9 @@ module Ci
alias_attribute :secret_value, :value
validates :key, uniqueness: { scope: :pipeline_id }
+
+ def hook_attrs
+ { key: key, value: value }
+ end
end
end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index f41955f43e7..31330d0682e 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -7,11 +7,26 @@ module Ci
include IgnorableColumn
include RedisCacheable
include ChronicDurationAttribute
+ include FromUnion
+
+ enum access_level: {
+ not_protected: 0,
+ ref_protected: 1
+ }
+
+ enum runner_type: {
+ instance_type: 1,
+ group_type: 2,
+ project_type: 3
+ }
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour
UPDATE_DB_RUNNER_INFO_EVERY = 40.minutes
- AVAILABLE_SCOPES = %w[specific shared active paused online].freeze
+ AVAILABLE_TYPES_LEGACY = %w[specific shared].freeze
+ AVAILABLE_TYPES = runner_types.keys.freeze
+ AVAILABLE_STATUSES = %w[active paused online offline].freeze
+ AVAILABLE_SCOPES = (AVAILABLE_TYPES_LEGACY + AVAILABLE_TYPES + AVAILABLE_STATUSES).freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze
ignore_column :is_shared
@@ -29,6 +44,13 @@ module Ci
scope :active, -> { where(active: true) }
scope :paused, -> { where(active: false) }
scope :online, -> { where('contacted_at > ?', contact_time_deadline) }
+ # The following query using negation is cheaper than using `contacted_at <= ?`
+ # because there are less runners online than have been created. The
+ # resulting query is quickly finding online ones and then uses the regular
+ # indexed search and rejects the ones that are in the previous set. If we
+ # did `contacted_at <= ?` the query would effectively have to do a seq
+ # scan.
+ scope :offline, -> { where.not(id: online) }
scope :ordered, -> { order(id: :desc) }
# BACKWARD COMPATIBILITY: There are needed to maintain compatibility with `AVAILABLE_SCOPES` used by `lib/api/runners.rb`
@@ -48,21 +70,32 @@ module Ci
}
scope :owned_or_instance_wide, -> (project_id) do
- union = Gitlab::SQL::Union.new(
- [belonging_to_project(project_id), belonging_to_parent_group_of_project(project_id), instance_type],
+ from_union(
+ [
+ belonging_to_project(project_id),
+ belonging_to_parent_group_of_project(project_id),
+ instance_type
+ ],
remove_duplicates: false
)
- from("(#{union.to_sql}) ci_runners")
end
scope :assignable_for, ->(project) do
# FIXME: That `to_sql` is needed to workaround a weird Rails bug.
# Without that, placeholders would miss one and couldn't match.
+ #
+ # We use "unscoped" here so that any current Ci::Runner filters don't
+ # apply to the inner query, which is not necessary.
+ exclude_runners = unscoped { project.runners.select(:id) }.to_sql
+
where(locked: false)
- .where.not("ci_runners.id IN (#{project.runners.select(:id).to_sql})")
+ .where.not("ci_runners.id IN (#{exclude_runners})")
.project_type
end
+ scope :order_contacted_at_asc, -> { order(contacted_at: :asc) }
+ scope :order_created_at_desc, -> { order(created_at: :desc) }
+
validate :tag_constraints
validates :access_level, presence: true
validates :runner_type, presence: true
@@ -76,17 +109,6 @@ module Ci
after_destroy :cleanup_runner_queue
- enum access_level: {
- not_protected: 0,
- ref_protected: 1
- }
-
- enum runner_type: {
- instance_type: 1,
- group_type: 2,
- project_type: 3
- }
-
cached_attr_reader :version, :revision, :platform, :architecture, :ip_address, :contacted_at
chronic_duration_attr :maximum_timeout_human_readable, :maximum_timeout
@@ -115,6 +137,14 @@ module Ci
ONLINE_CONTACT_TIMEOUT.ago
end
+ def self.order_by(order)
+ if order == 'contacted_asc'
+ order_contacted_at_asc
+ else
+ order_created_at_desc
+ end
+ end
+
def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank?
end
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 511ded55dc3..58f3fe2460a 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -65,6 +65,10 @@ module Ci
event :block do
transition any - [:manual] => :manual
end
+
+ event :delay do
+ transition any - [:scheduled] => :scheduled
+ end
end
def update_status
@@ -77,6 +81,7 @@ module Ci
when 'failed' then drop
when 'canceled' then cancel
when 'manual' then block
+ when 'scheduled' then delay
when 'skipped', nil then skip
else
raise HasStatus::UnknownStatusError,
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index 913936a0bcb..0b52c690e93 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -8,6 +8,8 @@ module Ci
belongs_to :pipeline, foreign_key: :commit_id
has_many :builds
+ delegate :short_token, to: :trigger, prefix: true, allow_nil: true
+
# We switched to Ci::PipelineVariable from Ci::TriggerRequest.variables.
# Ci::TriggerRequest doesn't save variables anymore.
validates :variables, absence: true
diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb
index 55bbf7cae7e..423071ec024 100644
--- a/app/models/clusters/applications/helm.rb
+++ b/app/models/clusters/applications/helm.rb
@@ -32,7 +32,8 @@ module Clusters
def install_command
Gitlab::Kubernetes::Helm::InitCommand.new(
name: name,
- files: files
+ files: files,
+ rbac: cluster.platform_kubernetes_rbac?
)
end
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index 93f654e0638..bd0286ee3f9 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -39,6 +39,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
+ rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files
)
diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb
index ef1c76c03bd..e43a0fd1786 100644
--- a/app/models/clusters/applications/jupyter.rb
+++ b/app/models/clusters/applications/jupyter.rb
@@ -19,7 +19,7 @@ module Clusters
def set_initial_status
return unless not_installable?
- if cluster&.application_ingress_installed? && cluster.application_ingress.external_ip
+ if cluster&.application_ingress_available? && cluster.application_ingress.external_ip
self.status = 'installable'
end
end
@@ -40,6 +40,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
+ rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files,
repository: repository
@@ -72,6 +73,11 @@ module Clusters
"clientSecret" => oauth_application.secret,
"callbackUrl" => callback_url
}
+ },
+ "singleuser" => {
+ "extraEnv" => {
+ "GITLAB_CLUSTER_ID" => cluster.id
+ }
}
}
end
diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb
new file mode 100644
index 00000000000..c66d5ce54db
--- /dev/null
+++ b/app/models/clusters/applications/knative.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Applications
+ class Knative < ActiveRecord::Base
+ VERSION = '0.1.3'.freeze
+ REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze
+
+ # This is required for helm version <= 2.10.x in order to support
+ # Setting up CRDs
+ ISTIO_CRDS = 'https://storage.googleapis.com/triggermesh-charts/istio-crds.yaml'.freeze
+
+ self.table_name = 'clusters_applications_knative'
+
+ include ::Clusters::Concerns::ApplicationCore
+ include ::Clusters::Concerns::ApplicationStatus
+ include ::Clusters::Concerns::ApplicationVersion
+ include ::Clusters::Concerns::ApplicationData
+
+ default_value_for :version, VERSION
+
+ validates :hostname, presence: true, hostname: true
+
+ def chart
+ 'knative/knative'
+ end
+
+ def values
+ { "domain" => hostname }.to_yaml
+ end
+
+ def install_command
+ Gitlab::Kubernetes::Helm::InstallCommand.new(
+ name: name,
+ version: VERSION,
+ rbac: cluster.platform_kubernetes_rbac?,
+ chart: chart,
+ files: files,
+ repository: REPOSITORY,
+ preinstall: install_script
+ )
+ end
+
+ def client
+ cluster.platform_kubernetes.kubeclient.knative_client
+ end
+
+ private
+
+ def install_script
+ ["/usr/bin/kubectl apply -f #{ISTIO_CRDS}"]
+ end
+ end
+ end
+end
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 88399dbbb95..46d0388a464 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -48,6 +48,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
+ rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files
)
@@ -71,7 +72,7 @@ module Clusters
private
def kube_client
- cluster&.kubeclient
+ cluster&.kubeclient&.core_client
end
end
end
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index bde255723c8..b311f5e0617 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ActiveRecord::Base
- VERSION = '0.1.31'.freeze
+ VERSION = '0.1.35'.freeze
self.table_name = 'clusters_applications_runners'
@@ -33,6 +33,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
+ rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files,
repository: repository
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 7cf75403ab6..0ba056e57d4 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -3,6 +3,7 @@
module Clusters
class Cluster < ActiveRecord::Base
include Presentable
+ include Gitlab::Utils::StrongMemoize
self.table_name = 'clusters'
@@ -11,7 +12,8 @@ module Clusters
Applications::Ingress.application_name => Applications::Ingress,
Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner,
- Applications::Jupyter.application_name => Applications::Jupyter
+ Applications::Jupyter.application_name => Applications::Jupyter,
+ Applications::Knative.application_name => Applications::Knative
}.freeze
DEFAULT_ENVIRONMENT = '*'.freeze
@@ -19,31 +21,51 @@ module Clusters
has_many :cluster_projects, class_name: 'Clusters::Project'
has_many :projects, through: :cluster_projects, class_name: '::Project'
+ has_one :cluster_project, -> { order(id: :desc) }, class_name: 'Clusters::Project'
+
+ has_many :cluster_groups, class_name: 'Clusters::Group'
+ has_many :groups, through: :cluster_groups, class_name: '::Group'
# we force autosave to happen when we save `Cluster` model
has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true
- has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', autosave: true
+ has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', inverse_of: :cluster, autosave: true
has_one :application_helm, class_name: 'Clusters::Applications::Helm'
has_one :application_ingress, class_name: 'Clusters::Applications::Ingress'
has_one :application_prometheus, class_name: 'Clusters::Applications::Prometheus'
has_one :application_runner, class_name: 'Clusters::Applications::Runner'
has_one :application_jupyter, class_name: 'Clusters::Applications::Jupyter'
+ has_one :application_knative, class_name: 'Clusters::Applications::Knative'
+
+ has_many :kubernetes_namespaces
+ has_one :kubernetes_namespace, -> { order(id: :desc) }, class_name: 'Clusters::KubernetesNamespace'
accepts_nested_attributes_for :provider_gcp, update_only: true
accepts_nested_attributes_for :platform_kubernetes, update_only: true
validates :name, cluster_name: true
+ validates :cluster_type, presence: true
validate :restrict_modification, on: :update
+ validate :no_groups, unless: :group_type?
+ validate :no_projects, unless: :project_type?
+
delegate :status, to: :provider, allow_nil: true
delegate :status_reason, to: :provider, allow_nil: true
delegate :on_creation?, to: :provider, allow_nil: true
delegate :active?, to: :platform_kubernetes, prefix: true, allow_nil: true
- delegate :installed?, to: :application_helm, prefix: true, allow_nil: true
- delegate :installed?, to: :application_ingress, prefix: true, allow_nil: true
+ delegate :rbac?, to: :platform_kubernetes, prefix: true, allow_nil: true
+ delegate :available?, to: :application_helm, prefix: true, allow_nil: true
+ delegate :available?, to: :application_ingress, prefix: true, allow_nil: true
+ delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true
+
+ enum cluster_type: {
+ instance_type: 1,
+ group_type: 2,
+ project_type: 3
+ }
enum platform_type: {
kubernetes: 1
@@ -80,7 +102,8 @@ module Clusters
application_ingress || build_application_ingress,
application_prometheus || build_application_prometheus,
application_runner || build_application_runner,
- application_jupyter || build_application_jupyter
+ application_jupyter || build_application_jupyter,
+ application_knative || build_application_knative
]
end
@@ -97,16 +120,34 @@ module Clusters
end
def first_project
- return @first_project if defined?(@first_project)
-
- @first_project = projects.first
+ strong_memoize(:first_project) do
+ projects.first
+ end
end
alias_method :project, :first_project
+ def first_group
+ strong_memoize(:first_group) do
+ groups.first
+ end
+ end
+ alias_method :group, :first_group
+
def kubeclient
platform_kubernetes.kubeclient if kubernetes?
end
+ def find_or_initialize_kubernetes_namespace(cluster_project)
+ kubernetes_namespaces.find_or_initialize_by(
+ project: cluster_project.project,
+ cluster_project: cluster_project
+ )
+ end
+
+ def allow_user_defined_namespace?
+ project_type?
+ end
+
private
def restrict_modification
@@ -117,5 +158,17 @@ module Clusters
true
end
+
+ def no_groups
+ if groups.any?
+ errors.add(:cluster, 'cannot have groups assigned')
+ end
+ end
+
+ def no_projects
+ if projects.any?
+ errors.add(:cluster, 'cannot have projects assigned')
+ end
+ end
end
end
diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb
index e3deedfb036..683b45331f6 100644
--- a/app/models/clusters/concerns/application_core.rb
+++ b/app/models/clusters/concerns/application_core.rb
@@ -15,7 +15,7 @@ module Clusters
def set_initial_status
return unless not_installable?
- self.status = 'installable' if cluster&.application_helm_installed?
+ self.status = 'installable' if cluster&.application_helm_available?
end
def self.application_name
diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb
index d4d3859dfd5..0e74cce29b7 100644
--- a/app/models/clusters/concerns/application_status.rb
+++ b/app/models/clusters/concerns/application_status.rb
@@ -15,6 +15,9 @@ module Clusters
state :scheduled, value: 1
state :installing, value: 2
state :installed, value: 3
+ state :updating, value: 4
+ state :updated, value: 5
+ state :update_errored, value: 6
event :make_scheduled do
transition [:installable, :errored] => :scheduled
@@ -32,6 +35,18 @@ module Clusters
transition any => :errored
end
+ event :make_updating do
+ transition [:installed, :updated, :update_errored] => :updating
+ end
+
+ event :make_updated do
+ transition [:updating] => :updated
+ end
+
+ event :make_update_errored do
+ transition any => :update_errored
+ end
+
before_transition any => [:scheduled] do |app_status, _|
app_status.status_reason = nil
end
@@ -40,8 +55,28 @@ module Clusters
status_reason = transition.args.first
app_status.status_reason = status_reason if status_reason
end
+
+ before_transition any => [:updating] do |app_status, _|
+ app_status.status_reason = nil
+ end
+
+ before_transition any => [:update_errored] do |app_status, transition|
+ status_reason = transition.args.first
+ app_status.status_reason = status_reason if status_reason
+ end
+
+ before_transition any => [:installed, :updated] do |app_status, _|
+ # When installing any application we are also performing an update
+ # of tiller (see Gitlab::Kubernetes::Helm::ClientCommand) so
+ # therefore we need to reflect that in the database.
+ app_status.cluster.application_helm.update!(version: Gitlab::Kubernetes::Helm::HELM_VERSION)
+ end
end
end
+
+ def available?
+ installed? || updated?
+ end
end
end
end
diff --git a/app/models/clusters/group.rb b/app/models/clusters/group.rb
new file mode 100644
index 00000000000..2b08a9e47f0
--- /dev/null
+++ b/app/models/clusters/group.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Clusters
+ class Group < ActiveRecord::Base
+ self.table_name = 'cluster_groups'
+
+ belongs_to :cluster, class_name: 'Clusters::Cluster'
+ belongs_to :group, class_name: '::Group'
+ end
+end
diff --git a/app/models/clusters/kubernetes_namespace.rb b/app/models/clusters/kubernetes_namespace.rb
new file mode 100644
index 00000000000..34f5e38ff79
--- /dev/null
+++ b/app/models/clusters/kubernetes_namespace.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+module Clusters
+ class KubernetesNamespace < ActiveRecord::Base
+ include Gitlab::Kubernetes
+
+ self.table_name = 'clusters_kubernetes_namespaces'
+
+ belongs_to :cluster_project, class_name: 'Clusters::Project'
+ belongs_to :cluster, class_name: 'Clusters::Cluster'
+ belongs_to :project, class_name: '::Project'
+ has_one :platform_kubernetes, through: :cluster
+
+ before_validation :set_defaults
+
+ validates :namespace, presence: true
+ validates :namespace, uniqueness: { scope: :cluster_id }
+
+ validates :service_account_name, presence: true
+
+ delegate :ca_pem, to: :platform_kubernetes, allow_nil: true
+ delegate :api_url, to: :platform_kubernetes, allow_nil: true
+
+ attr_encrypted :service_account_token,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_truncated,
+ algorithm: 'aes-256-cbc'
+
+ scope :has_service_account_token, -> { where.not(encrypted_service_account_token: nil) }
+
+ def token_name
+ "#{namespace}-token"
+ end
+
+ def predefined_variables
+ config = YAML.dump(kubeconfig)
+
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ variables
+ .append(key: 'KUBE_SERVICE_ACCOUNT', value: service_account_name.to_s)
+ .append(key: 'KUBE_NAMESPACE', value: namespace.to_s)
+ .append(key: 'KUBE_TOKEN', value: service_account_token.to_s, public: false)
+ .append(key: 'KUBECONFIG', value: config, public: false, file: true)
+ end
+ end
+
+ def set_defaults
+ self.namespace ||= default_platform_kubernetes_namespace
+ self.namespace ||= default_project_namespace
+ self.service_account_name ||= default_service_account_name
+ end
+
+ private
+
+ def default_service_account_name
+ return unless namespace
+
+ "#{namespace}-service-account"
+ end
+
+ def default_platform_kubernetes_namespace
+ platform_kubernetes&.namespace.presence
+ end
+
+ def default_project_namespace
+ Gitlab::NamespaceSanitizer.sanitize(project_slug) if project_slug
+ end
+
+ def project_slug
+ return unless project
+
+ "#{project.path}-#{project.id}".downcase
+ end
+
+ def kubeconfig
+ to_kubeconfig(
+ url: api_url,
+ namespace: namespace,
+ token: service_account_token,
+ ca_pem: ca_pem)
+ end
+ end
+end
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index e6ddca0d5d0..3c5d7756eec 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -5,6 +5,10 @@ module Clusters
class Kubernetes < ActiveRecord::Base
include Gitlab::Kubernetes
include ReactiveCaching
+ include EnumWithNil
+ include AfterCommitQueue
+
+ RESERVED_NAMESPACES = %w(gitlab-managed-apps).freeze
self.table_name = 'cluster_platforms_kubernetes'
self.reactive_cache_key = ->(kubernetes) { [kubernetes.class.model_name.singular, kubernetes.id] }
@@ -22,6 +26,7 @@ module Clusters
algorithm: 'aes-256-cbc'
before_validation :enforce_namespace_to_lower_case
+ before_validation :enforce_ca_whitespace_trimming
validates :namespace,
allow_blank: true,
@@ -31,6 +36,10 @@ module Clusters
message: Gitlab::Regex.kubernetes_namespace_regex_message
}
+ validates :namespace, exclusion: { in: RESERVED_NAMESPACES }
+
+ validate :no_namespace, unless: :allow_user_defined_namespace?
+
# We expect to be `active?` only when enabled and cluster is created (the api_url is assigned)
validates :api_url, url: true, presence: true
validates :token, presence: true
@@ -38,15 +47,24 @@ module Clusters
validate :prevent_modification, on: :update
after_save :clear_reactive_cache!
+ after_update :update_kubernetes_namespace
alias_attribute :ca_pem, :ca_cert
delegate :project, to: :cluster, allow_nil: true
delegate :enabled?, to: :cluster, allow_nil: true
delegate :managed?, to: :cluster, allow_nil: true
+ delegate :allow_user_defined_namespace?, to: :cluster, allow_nil: true
+ delegate :kubernetes_namespace, to: :cluster
alias_method :active?, :enabled?
+ enum_with_nil authorization_type: {
+ unknown_authorization: nil,
+ rbac: 1,
+ abac: 2
+ }
+
def actual_namespace
if namespace.present?
namespace
@@ -55,21 +73,31 @@ module Clusters
end
end
- def predefined_variables
- config = YAML.dump(kubeconfig)
-
+ def predefined_variables(project:)
Gitlab::Ci::Variables::Collection.new.tap do |variables|
- variables
- .append(key: 'KUBE_URL', value: api_url)
- .append(key: 'KUBE_TOKEN', value: token, public: false)
- .append(key: 'KUBE_NAMESPACE', value: actual_namespace)
- .append(key: 'KUBECONFIG', value: config, public: false, file: true)
+ variables.append(key: 'KUBE_URL', value: api_url)
if ca_pem.present?
variables
.append(key: 'KUBE_CA_PEM', value: ca_pem)
.append(key: 'KUBE_CA_PEM_FILE', value: ca_pem, file: true)
end
+
+ if kubernetes_namespace = cluster.kubernetes_namespaces.has_service_account_token.find_by(project: project)
+ variables.concat(kubernetes_namespace.predefined_variables)
+ else
+ # From 11.5, every Clusters::Project should have at least one
+ # Clusters::KubernetesNamespace, so once migration has been completed,
+ # this 'else' branch will be removed. For more information, please see
+ # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22433
+ config = YAML.dump(kubeconfig)
+
+ variables
+ .append(key: 'KUBE_URL', value: api_url)
+ .append(key: 'KUBE_TOKEN', value: token, public: false)
+ .append(key: 'KUBE_NAMESPACE', value: actual_namespace)
+ .append(key: 'KUBECONFIG', value: config, public: false, file: true)
+ end
end
end
@@ -95,7 +123,7 @@ module Clusters
end
def kubeclient
- @kubeclient ||= build_kubeclient!
+ @kubeclient ||= build_kube_client!
end
private
@@ -109,22 +137,31 @@ module Clusters
end
def default_namespace
+ kubernetes_namespace&.namespace.presence || fallback_default_namespace
+ end
+
+ # DEPRECATED
+ #
+ # On 11.4 Clusters::KubernetesNamespace was introduced, this model will allow to
+ # have multiple namespaces per project. This method will be removed after migration
+ # has been completed.
+ def fallback_default_namespace
return unless project
slug = "#{project.path}-#{project.id}".downcase
- slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
+ Gitlab::NamespaceSanitizer.sanitize(slug)
end
- def build_kubeclient!(api_path: 'api', api_version: 'v1')
- raise "Incomplete settings" unless api_url && actual_namespace
+ def build_kube_client!
+ raise "Incomplete settings" unless api_url
+ raise "No namespace" if cluster.project_type? && actual_namespace.empty? # can probably remove this line once we remove #actual_namespace
unless (username && password) || token
raise "Either username/password or token is required to access API"
end
- ::Kubeclient::Client.new(
- join_api_url(api_path),
- api_version,
+ Gitlab::Kubernetes::KubeClient.new(
+ api_url,
auth_options: kubeclient_auth_options,
ssl_options: kubeclient_ssl_options,
http_proxy_uri: ENV['http_proxy']
@@ -133,12 +170,10 @@ module Clusters
# Returns a hash of all pods in the namespace
def read_pods
- kubeclient = build_kubeclient!
+ kubeclient = build_kube_client!
kubeclient.get_pods(namespace: actual_namespace).as_json
- rescue Kubeclient::HttpError => err
- raise err unless err.error_code == 404
-
+ rescue Kubeclient::ResourceNotFoundError
[]
end
@@ -157,15 +192,6 @@ module Clusters
{ bearer_token: token }
end
- def join_api_url(api_path)
- url = URI.parse(api_url)
- prefix = url.path.sub(%r{/+\z}, '')
-
- url.path = [prefix, api_path].join("/")
-
- url.to_s
- end
-
def terminal_auth
{
token: token,
@@ -178,6 +204,17 @@ module Clusters
self.namespace = self.namespace&.downcase
end
+ def enforce_ca_whitespace_trimming
+ self.ca_pem = self.ca_pem&.strip
+ self.token = self.token&.strip
+ end
+
+ def no_namespace
+ if namespace
+ errors.add(:namespace, 'only allowed for project cluster')
+ end
+ end
+
def prevent_modification
return unless managed?
@@ -188,6 +225,14 @@ module Clusters
true
end
+
+ def update_kubernetes_namespace
+ return unless namespace_changed?
+
+ run_after_commit do
+ ClusterPlatformConfigureWorker.perform_async(cluster_id)
+ end
+ end
end
end
end
diff --git a/app/models/clusters/project.rb b/app/models/clusters/project.rb
index 839ce796081..15092b1c9d2 100644
--- a/app/models/clusters/project.rb
+++ b/app/models/clusters/project.rb
@@ -6,5 +6,8 @@ module Clusters
belongs_to :cluster, class_name: 'Clusters::Cluster'
belongs_to :project, class_name: '::Project'
+
+ has_many :kubernetes_namespaces, class_name: 'Clusters::KubernetesNamespace', foreign_key: :cluster_project_id
+ has_one :kubernetes_namespace, -> { order(id: :desc) }, class_name: 'Clusters::KubernetesNamespace', foreign_key: :cluster_project_id
end
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 27fbdc3e386..546fcc54a15 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -22,6 +22,7 @@ class Commit
attr_accessor :project, :author
attr_accessor :redacted_description_html
attr_accessor :redacted_title_html
+ attr_accessor :redacted_full_title_html
attr_reader :gpg_commit
DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines]
@@ -193,6 +194,7 @@ class Commit
# otherwise returns commit message without first line
def description
return safe_message if full_title.length >= 100
+ return no_commit_message if safe_message.blank?
safe_message.split("\n", 2)[1].try(:chomp)
end
@@ -228,24 +230,13 @@ class Commit
def lazy_author
BatchLoader.for(author_email.downcase).batch do |emails, loader|
- # A Hash that maps user Emails to the corresponding User objects. The
- # Emails at this point are the _primary_ Emails of the Users.
- users_for_emails = User
- .by_any_email(emails)
- .each_with_object({}) { |user, hash| hash[user.email] = user }
-
- users_for_ids = users_for_emails
- .values
- .each_with_object({}) { |user, hash| hash[user.id] = user }
-
- # Some commits may have used an alternative Email address. In this case we
- # need to query the "emails" table to map those addresses to User objects.
- Email
- .where(email: emails - users_for_emails.keys)
- .pluck(:email, :user_id)
- .each { |(email, id)| users_for_emails[email] = users_for_ids[id] }
-
- users_for_emails.each { |email, user| loader.call(email, user) }
+ users = User.by_any_email(emails).includes(:emails)
+
+ emails.each do |email|
+ user = users.find { |u| u.any_email?(email) }
+
+ loader.call(email, user)
+ end
end
end
@@ -258,7 +249,7 @@ class Commit
request_cache(:author) { author_email.downcase }
def committer
- @committer ||= User.find_by_any_email(committer_email.downcase)
+ @committer ||= User.find_by_any_email(committer_email)
end
def parents
@@ -317,7 +308,11 @@ class Commit
def status(ref = nil)
return @statuses[ref] if @statuses.key?(ref)
- @statuses[ref] = project.pipelines.latest_status_per_commit(id, ref)[id]
+ @statuses[ref] = status_for_project(ref, project)
+ end
+
+ def status_for_project(ref, pipeline_project)
+ pipeline_project.pipelines.latest_status_per_commit(id, ref)[id]
end
def set_status_for_ref(ref, status)
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index b65d7672973..0f50bd39131 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -42,15 +42,9 @@ class CommitStatus < ActiveRecord::Base
scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
scope :after_stage, -> (index) { where('stage_idx > ?', index) }
- enum_with_nil failure_reason: {
- unknown_failure: nil,
- script_failure: 1,
- api_failure: 2,
- stuck_or_timeout_failure: 3,
- runner_system_failure: 4,
- missing_dependency_failure: 5,
- runner_unsupported: 6
- }
+ # We use `CommitStatusEnums.failure_reasons` here so that EE can more easily
+ # extend this `Hash` with new values.
+ enum_with_nil failure_reason: ::CommitStatusEnums.failure_reasons
##
# We still create some CommitStatuses outside of CreatePipelineService.
@@ -58,9 +52,11 @@ class CommitStatus < ActiveRecord::Base
# These are pages deployments and external statuses.
#
before_create unless: :importing? do
+ # rubocop: disable CodeReuse/ServiceClass
Ci::EnsureStageService.new(project, user).execute(self) do |stage|
self.run_after_commit { StageUpdateWorker.perform_async(stage.id) }
end
+ # rubocop: enable CodeReuse/ServiceClass
end
state_machine :status do
@@ -69,7 +65,7 @@ class CommitStatus < ActiveRecord::Base
end
event :enqueue do
- transition [:created, :skipped, :manual] => :pending
+ transition [:created, :skipped, :manual, :scheduled] => :pending
end
event :run do
@@ -81,7 +77,7 @@ class CommitStatus < ActiveRecord::Base
end
event :drop do
- transition [:created, :pending, :running] => :failed
+ transition [:created, :pending, :running, :scheduled] => :failed
end
event :success do
@@ -89,10 +85,10 @@ class CommitStatus < ActiveRecord::Base
end
event :cancel do
- transition [:created, :pending, :running, :manual] => :canceled
+ transition [:created, :pending, :running, :manual, :scheduled] => :canceled
end
- before_transition [:created, :skipped, :manual] => :pending do |commit_status|
+ before_transition [:created, :skipped, :manual, :scheduled] => :pending do |commit_status|
commit_status.queued_at = Time.now
end
@@ -106,7 +102,7 @@ class CommitStatus < ActiveRecord::Base
before_transition any => :failed do |commit_status, transition|
failure_reason = transition.args.first
- commit_status.failure_reason = failure_reason
+ commit_status.failure_reason = CommitStatus.failure_reasons[failure_reason]
end
after_transition do |commit_status, transition|
@@ -130,10 +126,12 @@ class CommitStatus < ActiveRecord::Base
after_transition any => :failed do |commit_status|
next unless commit_status.project
+ # rubocop: disable CodeReuse/ServiceClass
commit_status.run_after_commit do
MergeRequests::AddTodoWhenBuildFailsService
.new(project, nil).execute(self)
end
+ # rubocop: enable CodeReuse/ServiceClass
end
end
@@ -161,16 +159,18 @@ class CommitStatus < ActiveRecord::Base
false
end
- # To be overriden when inherrited from
def retryable?
false
end
- # To be overriden when inherrited from
def cancelable?
false
end
+ def archived?
+ false
+ end
+
def stuck?
false
end
diff --git a/app/models/commit_status_enums.rb b/app/models/commit_status_enums.rb
new file mode 100644
index 00000000000..152105d9429
--- /dev/null
+++ b/app/models/commit_status_enums.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module CommitStatusEnums
+ # Returns the Hash to use for creating the `failure_reason` enum for
+ # `CommitStatus`.
+ def self.failure_reasons
+ {
+ unknown_failure: nil,
+ script_failure: 1,
+ api_failure: 2,
+ stuck_or_timeout_failure: 3,
+ runner_system_failure: 4,
+ missing_dependency_failure: 5,
+ runner_unsupported: 6,
+ stale_schedule: 7,
+ job_execution_timeout: 8,
+ archived_failure: 9
+ }
+ end
+end
diff --git a/app/models/compare.rb b/app/models/compare.rb
index b2d46ada831..f1ed84ab5a5 100644
--- a/app/models/compare.rb
+++ b/app/models/compare.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'set'
+
class Compare
include Gitlab::Utils::StrongMemoize
@@ -77,4 +79,13 @@ class Compare
head_sha: head_commit_sha
)
end
+
+ def modified_paths
+ paths = Set.new
+ diffs.diff_files.each do |diff|
+ paths.add diff.old_path
+ paths.add diff.new_path
+ end
+ paths.to_a
+ end
end
diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb
index 7f6d48d972c..4e15b60ccd1 100644
--- a/app/models/concerns/atomic_internal_id.rb
+++ b/app/models/concerns/atomic_internal_id.rb
@@ -26,7 +26,7 @@
module AtomicInternalId
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def has_internal_id(column, scope:, init:, presence: true) # rubocop:disable Naming/PredicateName
# We require init here to retain the ability to recalculate in the absence of a
# InternaLId record (we may delete records in `internal_ids` for example).
diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb
index c0233661a9b..b42236c1fa2 100644
--- a/app/models/concerns/avatarable.rb
+++ b/app/models/concerns/avatarable.rb
@@ -9,7 +9,7 @@ module Avatarable
include Gitlab::Utils::StrongMemoize
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
- validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
+ validates :avatar, file_size: { maximum: 200.kilobytes.to_i }, if: :avatar_changed?
mount_uploader :avatar, AvatarUploader
@@ -86,7 +86,7 @@ module Avatarable
params[:model].upload_paths(params[:identifier])
end
- Upload.where(uploader: AvatarUploader, path: paths).find_each do |upload|
+ Upload.where(uploader: AvatarUploader.name, path: paths).find_each do |upload|
model = model_class.instantiate('id' => upload.model_id)
loader.call({ model: model, identifier: File.basename(upload.path) }, upload)
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
index dd07f389fa5..60b7ec2815c 100644
--- a/app/models/concerns/awardable.rb
+++ b/app/models/concerns/awardable.rb
@@ -12,14 +12,14 @@ module Awardable
end
end
- module ClassMethods
- def awarded(user, name)
+ class_methods do
+ def awarded(user, name = nil)
sql = <<~EOL
EXISTS (
SELECT TRUE
FROM award_emoji
WHERE user_id = :user_id AND
- name = :name AND
+ #{"name = :name AND" if name.present?}
awardable_type = :awardable_type AND
awardable_id = #{self.arel_table.name}.id
)
@@ -28,6 +28,20 @@ module Awardable
where(sql, user_id: user.id, name: name, awardable_type: self.name)
end
+ def not_awarded(user)
+ sql = <<~EOL
+ NOT EXISTS (
+ SELECT TRUE
+ FROM award_emoji
+ WHERE user_id = :user_id AND
+ awardable_type = :awardable_type AND
+ awardable_id = #{self.arel_table.name}.id
+ )
+ EOL
+
+ where(sql, user_id: user.id, awardable_type: self.name)
+ end
+
def order_upvotes_desc
order_votes_desc(AwardEmoji::UPVOTE_NAME)
end
@@ -76,12 +90,8 @@ module Awardable
true
end
- def awardable_votes?(name)
- AwardEmoji::UPVOTE_NAME == name || AwardEmoji::DOWNVOTE_NAME == name
- end
-
- def user_can_award?(current_user, name)
- awardable_by_user?(current_user, name) && Ability.allowed?(current_user, :award_emoji, self)
+ def user_can_award?(current_user)
+ Ability.allowed?(current_user, :award_emoji, self)
end
def user_authored?(current_user)
@@ -101,7 +111,7 @@ module Awardable
end
def remove_award_emoji(name, current_user)
- award_emoji.where(name: name, user: current_user).destroy_all
+ award_emoji.where(name: name, user: current_user).destroy_all # rubocop: disable DestroyAll
end
def toggle_award_emoji(emoji_name, current_user)
@@ -117,12 +127,4 @@ module Awardable
def normalize_name(name)
Gitlab::Emoji.normalize_emoji_name(name)
end
-
- def awardable_by_user?(current_user, name)
- if user_authored?(current_user)
- !awardable_votes?(normalize_name(name))
- else
- true
- end
- end
end
diff --git a/app/models/concerns/blob_language_from_git_attributes.rb b/app/models/concerns/blob_language_from_git_attributes.rb
new file mode 100644
index 00000000000..70213d22147
--- /dev/null
+++ b/app/models/concerns/blob_language_from_git_attributes.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+# Applicable for blob classes with project attribute
+module BlobLanguageFromGitAttributes
+ extend ActiveSupport::Concern
+
+ def language_from_gitattributes
+ return nil unless project
+
+ repository = project.repository
+ repository.gitattribute(path, 'gitlab-language')
+ end
+end
diff --git a/app/models/concerns/blob_like.rb b/app/models/concerns/blob_like.rb
index e96fefe81c4..f20f01486a5 100644
--- a/app/models/concerns/blob_like.rb
+++ b/app/models/concerns/blob_like.rb
@@ -2,7 +2,7 @@
module BlobLike
extend ActiveSupport::Concern
- include Linguist::BlobHelper
+ include Gitlab::BlobHelper
def id
raise NotImplementedError
diff --git a/app/models/concerns/bulk_member_access_load.rb b/app/models/concerns/bulk_member_access_load.rb
index c4346d5dd17..041ed3755e0 100644
--- a/app/models/concerns/bulk_member_access_load.rb
+++ b/app/models/concerns/bulk_member_access_load.rb
@@ -16,9 +16,9 @@ module BulkMemberAccessLoad
key = max_member_access_for_resource_key(resource_klass, memoization_index)
access = {}
- if RequestStore.active?
- RequestStore.store[key] ||= {}
- access = RequestStore.store[key]
+ if Gitlab::SafeRequestStore.active?
+ Gitlab::SafeRequestStore[key] ||= {}
+ access = Gitlab::SafeRequestStore[key]
end
# Look up only the IDs we need
diff --git a/app/models/concerns/cacheable_attributes.rb b/app/models/concerns/cacheable_attributes.rb
index 62b78c3611c..75592bb63e2 100644
--- a/app/models/concerns/cacheable_attributes.rb
+++ b/app/models/concerns/cacheable_attributes.rb
@@ -12,12 +12,12 @@ module CacheableAttributes
"#{name}:#{Gitlab::VERSION}:#{Rails.version}".freeze
end
- # Can be overriden
+ # Can be overridden
def current_without_cache
last
end
- # Can be overriden
+ # Can be overridden
def defaults
{}
end
@@ -27,11 +27,7 @@ module CacheableAttributes
end
def cached
- if RequestStore.active?
- RequestStore[:"#{name}_cached_attributes"] ||= retrieve_from_cache
- else
- retrieve_from_cache
- end
+ Gitlab::SafeRequestStore[:"#{name}_cached_attributes"] ||= retrieve_from_cache
end
def retrieve_from_cache
diff --git a/app/models/concerns/case_sensitivity.rb b/app/models/concerns/case_sensitivity.rb
index 0ba542b75ab..c93b6589ee7 100644
--- a/app/models/concerns/case_sensitivity.rb
+++ b/app/models/concerns/case_sensitivity.rb
@@ -4,28 +4,51 @@
module CaseSensitivity
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
# Queries the given columns regardless of the casing used.
#
# Unlike other ActiveRecord methods this method only operates on a Hash.
def iwhere(params)
- criteria = self
- cast_lower = Gitlab::Database.postgresql?
+ criteria = self
params.each do |key, value|
- column = ActiveRecord::Base.connection.quote_table_name(key)
+ criteria = case value
+ when Array
+ criteria.where(value_in(key, value))
+ else
+ criteria.where(value_equal(key, value))
+ end
+ end
+
+ criteria
+ end
- condition =
- if cast_lower
- "LOWER(#{column}) = LOWER(:value)"
- else
- "#{column} = :value"
- end
+ private
+
+ def value_equal(column, value)
+ lower_value = lower_value(value)
+
+ lower_column(arel_table[column]).eq(lower_value).to_sql
+ end
- criteria = criteria.where(condition, value: value)
+ def value_in(column, values)
+ lower_values = values.map do |value|
+ lower_value(value)
end
- criteria
+ lower_column(arel_table[column]).in(lower_values).to_sql
+ end
+
+ def lower_value(value)
+ return value if Gitlab::Database.mysql?
+
+ Arel::Nodes::NamedFunction.new('LOWER', [Arel::Nodes.build_quoted(value)])
+ end
+
+ def lower_column(column)
+ return column if Gitlab::Database.mysql?
+
+ column.lower
end
end
end
diff --git a/app/models/concerns/deployable.rb b/app/models/concerns/deployable.rb
new file mode 100644
index 00000000000..bc12b06b5af
--- /dev/null
+++ b/app/models/concerns/deployable.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Deployable
+ extend ActiveSupport::Concern
+
+ included do
+ after_create :create_deployment
+
+ def create_deployment
+ return unless starts_environment? && !has_deployment?
+
+ environment = project.environments.find_or_create_by(
+ name: expanded_environment_name
+ )
+
+ # If we failed to persist envirionment record by validation error, such as name with invalid character,
+ # the job will fall back to a non-environment job.
+ return unless environment.persisted?
+
+ create_deployment!(
+ project_id: environment.project_id,
+ environment: environment,
+ ref: ref,
+ tag: tag,
+ sha: sha,
+ user: user,
+ on_stop: on_stop)
+ end
+ end
+end
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index 91052013592..e57a3383544 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -42,6 +42,7 @@ module DeploymentPlatform
{
name: 'kubernetes-template',
projects: [self],
+ cluster_type: :project_type,
provider_type: :user,
platform_type: :kubernetes,
platform_kubernetes_attributes: platform_kubernetes_attributes_from_service_template
diff --git a/app/models/concerns/diff_positionable_note.rb b/app/models/concerns/diff_positionable_note.rb
new file mode 100644
index 00000000000..b61bf29e6ad
--- /dev/null
+++ b/app/models/concerns/diff_positionable_note.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+module DiffPositionableNote
+ extend ActiveSupport::Concern
+
+ included do
+ before_validation :set_original_position, on: :create
+ before_validation :update_position, on: :create, if: :on_text?
+
+ serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
+ serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
+ serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
+ end
+
+ %i(original_position position change_position).each do |meth|
+ define_method "#{meth}=" do |new_position|
+ if new_position.is_a?(String)
+ new_position = JSON.parse(new_position) rescue nil
+ end
+
+ if new_position.is_a?(Hash)
+ new_position = new_position.with_indifferent_access
+ new_position = Gitlab::Diff::Position.new(new_position)
+ end
+
+ return if new_position == read_attribute(meth)
+
+ super(new_position)
+ end
+ end
+
+ def on_text?
+ position&.position_type == "text"
+ end
+
+ def on_image?
+ position&.position_type == "image"
+ end
+
+ def supported?
+ for_commit? || self.noteable.has_complete_diff_refs?
+ end
+
+ def active?(diff_refs = nil)
+ return false unless supported?
+ return true if for_commit?
+
+ diff_refs ||= noteable.diff_refs
+
+ self.position.diff_refs == diff_refs
+ end
+
+ def set_original_position
+ return unless position
+
+ self.original_position = self.position.dup unless self.original_position&.complete?
+ end
+
+ def update_position
+ return unless supported?
+ return if for_commit?
+
+ return if active?
+ return unless position
+
+ tracer = Gitlab::Diff::PositionTracer.new(
+ project: self.project,
+ old_diff_refs: self.position.diff_refs,
+ new_diff_refs: self.noteable.diff_refs,
+ paths: self.position.paths
+ )
+
+ result = tracer.trace(self.position)
+ return unless result
+
+ if result[:outdated]
+ self.change_position = result[:position]
+ else
+ self.position = result[:position]
+ end
+ end
+end
diff --git a/app/models/concerns/each_batch.rb b/app/models/concerns/each_batch.rb
index a9e14cb55eb..6314b46a7e3 100644
--- a/app/models/concerns/each_batch.rb
+++ b/app/models/concerns/each_batch.rb
@@ -3,7 +3,7 @@
module EachBatch
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
# Iterates over the rows in a relation in batches, similar to Rails'
# `in_batches` but in a more efficient way.
#
@@ -39,7 +39,15 @@ module EachBatch
#
# of - The number of rows to retrieve per batch.
# column - The column to use for ordering the batches.
- def each_batch(of: 1000, column: primary_key)
+ # order_hint - An optional column to append to the `ORDER BY id`
+ # clause to help the query planner. PostgreSQL might perform badly
+ # with a LIMIT 1 because the planner is guessing that scanning the
+ # index in ID order will come across the desired row in less time
+ # it will take the planner than using another index. The
+ # order_hint does not affect the search results. For example,
+ # `ORDER BY id ASC, updated_at ASC` means the same thing as `ORDER
+ # BY id ASC`.
+ def each_batch(of: 1000, column: primary_key, order_hint: nil)
unless column
raise ArgumentError,
'the column: argument must be set to a column name to use for ordering rows'
@@ -48,7 +56,9 @@ module EachBatch
start = except(:select)
.select(column)
.reorder(column => :asc)
- .take
+
+ start = start.order(order_hint) if order_hint
+ start = start.take
return unless start
@@ -60,6 +70,9 @@ module EachBatch
.select(column)
.where(arel_table[column].gteq(start_id))
.reorder(column => :asc)
+
+ stop = stop.order(order_hint) if order_hint
+ stop = stop
.offset(of)
.limit(1)
.take
diff --git a/app/models/concerns/fast_destroy_all.rb b/app/models/concerns/fast_destroy_all.rb
index 65ed46ea202..2bfa7da6c1c 100644
--- a/app/models/concerns/fast_destroy_all.rb
+++ b/app/models/concerns/fast_destroy_all.rb
@@ -7,7 +7,7 @@
# `delete_all` is efficient as it deletes all rows with a single `DELETE` query.
#
# It's better to use `delete_all` as our best practice, however,
-# if external data (e.g. ObjectStorage, FileStorage or Redis) are assosiated with database records,
+# if external data (e.g. ObjectStorage, FileStorage or Redis) are associated with database records,
# it is difficult to accomplish it.
#
# This module defines a format to use `delete_all` and delete associated external data.
@@ -34,7 +34,7 @@ module FastDestroyAll
included do
before_destroy do
- raise ForbiddenActionError, '`destroy` and `destroy_all` are forbbiden. Please use `fast_destroy_all`'
+ raise ForbiddenActionError, '`destroy` and `destroy_all` are forbidden. Please use `fast_destroy_all`'
end
end
diff --git a/app/models/concerns/from_union.rb b/app/models/concerns/from_union.rb
new file mode 100644
index 00000000000..9b8595b1211
--- /dev/null
+++ b/app/models/concerns/from_union.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module FromUnion
+ extend ActiveSupport::Concern
+
+ class_methods do
+ # Produces a query that uses a FROM to select data using a UNION.
+ #
+ # Using a FROM for a UNION has in the past lead to better query plans. As
+ # such, we generally recommend this pattern instead of using a WHERE IN.
+ #
+ # Example:
+ # users = User.from_union([User.where(id: 1), User.where(id: 2)])
+ #
+ # This would produce the following SQL query:
+ #
+ # SELECT *
+ # FROM (
+ # SELECT *
+ # FROM users
+ # WHERE id = 1
+ #
+ # UNION
+ #
+ # SELECT *
+ # FROM users
+ # WHERE id = 2
+ # ) users;
+ #
+ # members - An Array of ActiveRecord::Relation objects to use in the UNION.
+ #
+ # remove_duplicates - A boolean indicating if duplicate entries should be
+ # removed. Defaults to true.
+ #
+ # alias_as - The alias to use for the sub query. Defaults to the name of the
+ # table of the current model.
+ # rubocop: disable Gitlab/Union
+ def from_union(members, remove_duplicates: true, alias_as: table_name)
+ union = Gitlab::SQL::Union
+ .new(members, remove_duplicates: remove_duplicates)
+ .to_sql
+
+ # This pattern is necessary as a bug in Rails 4 can cause the use of
+ # `from("string here").includes(:foo)` to break ActiveRecord. This is
+ # fixed in https://github.com/rails/rails/pull/25374, which is released as
+ # part of Rails 5.
+ from([Arel.sql("(#{union}) #{alias_as}")])
+ end
+ # rubocop: enable Gitlab/Union
+ end
+end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index b3960cbad1a..b92643f87f8 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -4,14 +4,15 @@ module HasStatus
extend ActiveSupport::Concern
DEFAULT_STATUS = 'created'.freeze
- BLOCKED_STATUS = 'manual'.freeze
- AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped manual].freeze
- STARTED_STATUSES = %w[running success failed skipped manual].freeze
+ BLOCKED_STATUS = %w[manual scheduled].freeze
+ AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped manual scheduled].freeze
+ STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
ACTIVE_STATUSES = %w[pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
- ORDERED_STATUSES = %w[failed pending running manual canceled success skipped created].freeze
+ ORDERED_STATUSES = %w[failed pending running manual scheduled canceled success skipped created].freeze
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
- failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze
+ failed: 4, canceled: 5, skipped: 6, manual: 7,
+ scheduled: 8 }.freeze
UnknownStatusError = Class.new(StandardError)
@@ -24,6 +25,7 @@ module HasStatus
created = scope_relevant.created.select('count(*)').to_sql
success = scope_relevant.success.select('count(*)').to_sql
manual = scope_relevant.manual.select('count(*)').to_sql
+ scheduled = scope_relevant.scheduled.select('count(*)').to_sql
pending = scope_relevant.pending.select('count(*)').to_sql
running = scope_relevant.running.select('count(*)').to_sql
skipped = scope_relevant.skipped.select('count(*)').to_sql
@@ -40,6 +42,7 @@ module HasStatus
WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{running})+(#{pending})>0 THEN 'running'
WHEN (#{manual})>0 THEN 'manual'
+ WHEN (#{scheduled})>0 THEN 'scheduled'
WHEN (#{created})>0 THEN 'running'
ELSE 'failed'
END)"
@@ -74,6 +77,7 @@ module HasStatus
state :canceled, value: 'canceled'
state :skipped, value: 'skipped'
state :manual, value: 'manual'
+ state :scheduled, value: 'scheduled'
end
scope :created, -> { where(status: 'created') }
@@ -85,6 +89,7 @@ module HasStatus
scope :canceled, -> { where(status: 'canceled') }
scope :skipped, -> { where(status: 'skipped') }
scope :manual, -> { where(status: 'manual') }
+ scope :scheduled, -> { where(status: 'scheduled') }
scope :alive, -> { where(status: [:created, :pending, :running]) }
scope :created_or_pending, -> { where(status: [:created, :pending]) }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
@@ -92,7 +97,7 @@ module HasStatus
scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) }
scope :cancelable, -> do
- where(status: [:running, :pending, :created])
+ where(status: [:running, :pending, :created, :scheduled])
end
end
@@ -109,7 +114,7 @@ module HasStatus
end
def blocked?
- BLOCKED_STATUS == status
+ BLOCKED_STATUS.include?(status)
end
private
diff --git a/app/models/concerns/ignorable_column.rb b/app/models/concerns/ignorable_column.rb
index 2b074c1921c..5c1f7dfcd2a 100644
--- a/app/models/concerns/ignorable_column.rb
+++ b/app/models/concerns/ignorable_column.rb
@@ -14,7 +14,7 @@
module IgnorableColumn
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def columns
super.reject { |column| ignored_columns.include?(column.name) }
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index e8072145551..69c5affe142 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -9,6 +9,7 @@
module Issuable
extend ActiveSupport::Concern
include Gitlab::SQL::Pattern
+ include Redactable
include CacheMarkdownField
include Participable
include Mentionable
@@ -32,6 +33,8 @@ module Issuable
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description, issuable_state_filter_enabled: true
+ redact_field :description
+
belongs_to :author, class_name: "User"
belongs_to :updated_by, class_name: "User"
belongs_to :last_edited_by, class_name: 'User'
@@ -49,7 +52,7 @@ module Issuable
end
end
- has_many :label_links, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :label_links, as: :target, dependent: :destroy, inverse_of: :target # rubocop:disable Cop/ActiveRecordDependent
has_many :labels, through: :label_links
has_many :todos, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@@ -76,6 +79,7 @@ module Issuable
scope :recent, -> { reorder(id: :desc) }
scope :of_projects, ->(ids) { where(project_id: ids) }
scope :of_milestones, ->(ids) { where(milestone_id: ids) }
+ scope :any_milestone, -> { where('milestone_id IS NOT NULL') }
scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
scope :opened, -> { with_state(:opened) }
scope :only_opened, -> { with_state(:opened) }
@@ -109,16 +113,12 @@ module Issuable
false
end
- def etag_caching_enabled?
- false
- end
-
def has_multiple_assignees?
assignees.count > 1
end
end
- module ClassMethods
+ class_methods do
# Searches for records with a matching title.
#
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
@@ -363,7 +363,7 @@ module Issuable
end
##
- # Overriden in MergeRequest
+ # Overridden in MergeRequest
#
def wipless_title_changed(old_title)
old_title != title
diff --git a/app/models/concerns/loaded_in_group_list.rb b/app/models/concerns/loaded_in_group_list.rb
index a2233eb2997..fc15c6d55ed 100644
--- a/app/models/concerns/loaded_in_group_list.rb
+++ b/app/models/concerns/loaded_in_group_list.rb
@@ -3,7 +3,7 @@
module LoadedInGroupList
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def with_counts(archived:)
selects_including_counts = [
'namespaces.*',
diff --git a/app/models/concerns/manual_inverse_association.rb b/app/models/concerns/manual_inverse_association.rb
index d0d781dc15f..e18edd33ba7 100644
--- a/app/models/concerns/manual_inverse_association.rb
+++ b/app/models/concerns/manual_inverse_association.rb
@@ -3,7 +3,7 @@
module ManualInverseAssociation
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def manual_inverse_association(association, inverse)
define_method(association) do |*args|
super(*args).tap do |value|
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 7e7eccb1c27..0d88b34fb48 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -10,7 +10,7 @@
module Mentionable
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
# Indicate which attributes of the Mentionable to search for GFM references.
def attr_mentionable(attr, options = {})
attr = attr.to_s
@@ -61,7 +61,7 @@ module Mentionable
cache_key: [self, attr],
author: author,
skip_project_check: skip_project_check?
- )
+ ).merge(mentionable_params)
extractor.analyze(text, options)
end
@@ -86,21 +86,20 @@ module Mentionable
return [] unless matches_cross_reference_regex?
refs = all_references(current_user)
- refs = (refs.issues + refs.merge_requests + refs.commits)
# We're using this method instead of Array diffing because that requires
# both of the object's `hash` values to be the same, which may not be the
# case for otherwise identical Commit objects.
- refs.reject { |ref| ref == local_reference }
+ extracted_mentionables(refs).reject { |ref| ref == local_reference }
end
# Uses regex to quickly determine if mentionables might be referenced
# Allows heavy processing to be skipped
def matches_cross_reference_regex?
reference_pattern = if !project || project.default_issues_tracker?
- ReferenceRegexes::DEFAULT_PATTERN
+ ReferenceRegexes.default_pattern
else
- ReferenceRegexes::EXTERNAL_PATTERN
+ ReferenceRegexes.external_pattern
end
self.class.mentionable_attrs.any? do |attr, _|
@@ -134,6 +133,10 @@ module Mentionable
private
+ def extracted_mentionables(refs)
+ refs.issues + refs.merge_requests + refs.commits
+ end
+
# Returns a Hash of changed mentionable fields
#
# Preference is given to the `changes` Hash, but falls back to
@@ -161,4 +164,8 @@ module Mentionable
def skip_project_check?
false
end
+
+ def mentionable_params
+ {}
+ end
end
diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb
index f6fd28bac33..b8fb3f71925 100644
--- a/app/models/concerns/mentionable/reference_regexes.rb
+++ b/app/models/concerns/mentionable/reference_regexes.rb
@@ -2,23 +2,35 @@
module Mentionable
module ReferenceRegexes
+ extend Gitlab::Utils::StrongMemoize
+
def self.reference_pattern(link_patterns, issue_pattern)
Regexp.union(link_patterns,
issue_pattern,
- Commit.reference_pattern,
- MergeRequest.reference_pattern)
+ *other_patterns)
+ end
+
+ def self.other_patterns
+ [
+ Commit.reference_pattern,
+ MergeRequest.reference_pattern
+ ]
end
- DEFAULT_PATTERN = begin
- issue_pattern = Issue.reference_pattern
- link_patterns = Regexp.union([Issue, Commit, MergeRequest].map(&:link_reference_pattern))
- reference_pattern(link_patterns, issue_pattern)
+ def self.default_pattern
+ strong_memoize(:default_pattern) do
+ issue_pattern = Issue.reference_pattern
+ link_patterns = Regexp.union([Issue, Commit, MergeRequest, Epic].map(&:link_reference_pattern).compact)
+ reference_pattern(link_patterns, issue_pattern)
+ end
end
- EXTERNAL_PATTERN = begin
- issue_pattern = IssueTrackerService.reference_pattern
- link_patterns = URI.regexp(%w(http https))
- reference_pattern(link_patterns, issue_pattern)
+ def self.external_pattern
+ strong_memoize(:external_pattern) do
+ issue_pattern = IssueTrackerService.reference_pattern
+ link_patterns = URI.regexp(%w(http https))
+ reference_pattern(link_patterns, issue_pattern)
+ end
end
end
end
diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb
index ce778eae271..eb315058c3a 100644
--- a/app/models/concerns/noteable.rb
+++ b/app/models/concerns/noteable.rb
@@ -23,7 +23,7 @@ module Noteable
end
def supports_discussions?
- DiscussionNote::NOTEABLE_TYPES.include?(base_class_name)
+ DiscussionNote.noteable_types.include?(base_class_name)
end
def discussions_rendered_on_frontend?
@@ -82,4 +82,23 @@ module Noteable
def lockable?
[MergeRequest, Issue].include?(self.class)
end
+
+ def etag_caching_enabled?
+ false
+ end
+
+ def expire_note_etag_cache
+ return unless discussions_rendered_on_frontend?
+ return unless etag_caching_enabled?
+
+ Gitlab::EtagCaching::Store.new.touch(note_etag_key)
+ end
+
+ def note_etag_key
+ Gitlab::Routing.url_helpers.project_noteable_notes_path(
+ project,
+ target_type: self.class.name.underscore,
+ target_id: id
+ )
+ end
end
diff --git a/app/models/concerns/optionally_search.rb b/app/models/concerns/optionally_search.rb
new file mode 100644
index 00000000000..4093429e372
--- /dev/null
+++ b/app/models/concerns/optionally_search.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module OptionallySearch
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def search(*)
+ raise(
+ NotImplementedError,
+ 'Your model must implement the "search" class method'
+ )
+ end
+
+ # Optionally limits a result set to those matching the given search query.
+ def optionally_search(query = nil)
+ query.present? ? search(query) : all
+ end
+ end
+end
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 1f6c42f3b3a..614c3242874 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -26,7 +26,7 @@
module Participable
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
# Adds a list of participant attributes. Attributes can either be symbols or
# Procs.
#
diff --git a/app/models/concerns/project_services_loggable.rb b/app/models/concerns/project_services_loggable.rb
new file mode 100644
index 00000000000..fecd77cdc98
--- /dev/null
+++ b/app/models/concerns/project_services_loggable.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module ProjectServicesLoggable
+ def log_info(message, params = {})
+ message = build_message(message, params)
+
+ logger.info(message)
+ end
+
+ def log_error(message, params = {})
+ message = build_message(message, params)
+
+ logger.error(message)
+ end
+
+ def build_message(message, params = {})
+ {
+ service_class: self.class.name,
+ project_id: project.id,
+ project_path: project.full_path,
+ message: message
+ }.merge(params)
+ end
+
+ def logger
+ Gitlab::ProjectServiceLogger
+ end
+end
diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb
index 744f7f48dc8..58761fce952 100644
--- a/app/models/concerns/protected_branch_access.rb
+++ b/app/models/concerns/protected_branch_access.rb
@@ -2,18 +2,17 @@
module ProtectedBranchAccess
extend ActiveSupport::Concern
+ include ProtectedRefAccess
included do
- include ProtectedRefAccess
-
belongs_to :protected_branch
delegate :project, to: :protected_branch
+ end
- def check_access(user)
- return false if access_level == Gitlab::Access::NO_ACCESS
+ def check_access(user)
+ return false if access_level == Gitlab::Access::NO_ACCESS
- super
- end
+ super
end
end
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
index e62e680af6e..af387c99f3d 100644
--- a/app/models/concerns/protected_ref.rb
+++ b/app/models/concerns/protected_ref.rb
@@ -50,14 +50,20 @@ module ProtectedRef
.map(&:"#{action}_access_levels").flatten
end
+ # Returns all protected refs that match the given ref name.
+ # This checks all records from the scope built up so far, and does
+ # _not_ return a relation.
+ #
+ # This method optionally takes in a list of `protected_refs` to search
+ # through, to avoid calling out to the database.
def matching(ref_name, protected_refs: nil)
- ProtectedRefMatcher.matching(self, ref_name, protected_refs: protected_refs)
+ (protected_refs || self.all).select { |protected_ref| protected_ref.matches?(ref_name) }
end
end
private
def ref_matcher
- @ref_matcher ||= ProtectedRefMatcher.new(self)
+ @ref_matcher ||= RefMatcher.new(self.name)
end
end
diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb
index efa666fb3f2..583751ea6ac 100644
--- a/app/models/concerns/protected_ref_access.rb
+++ b/app/models/concerns/protected_ref_access.rb
@@ -3,18 +3,22 @@
module ProtectedRefAccess
extend ActiveSupport::Concern
- ALLOWED_ACCESS_LEVELS = [
- Gitlab::Access::MAINTAINER,
- Gitlab::Access::DEVELOPER,
- Gitlab::Access::NO_ACCESS
- ].freeze
-
HUMAN_ACCESS_LEVELS = {
Gitlab::Access::MAINTAINER => "Maintainers".freeze,
Gitlab::Access::DEVELOPER => "Developers + Maintainers".freeze,
Gitlab::Access::NO_ACCESS => "No one".freeze
}.freeze
+ class_methods do
+ def allowed_access_levels
+ [
+ Gitlab::Access::MAINTAINER,
+ Gitlab::Access::DEVELOPER,
+ Gitlab::Access::NO_ACCESS
+ ]
+ end
+ end
+
included do
scope :master, -> { maintainer } # @deprecated
scope :maintainer, -> { where(access_level: Gitlab::Access::MAINTAINER) }
@@ -26,7 +30,7 @@ module ProtectedRefAccess
scope :for_group, -> { where.not(group_id: nil) }
validates :access_level, presence: true, if: :role?, inclusion: {
- in: ALLOWED_ACCESS_LEVELS
+ in: self.allowed_access_levels
}
end
diff --git a/app/models/concerns/protected_tag_access.rb b/app/models/concerns/protected_tag_access.rb
index 04bd54d6b1c..3f5696c0749 100644
--- a/app/models/concerns/protected_tag_access.rb
+++ b/app/models/concerns/protected_tag_access.rb
@@ -2,10 +2,9 @@
module ProtectedTagAccess
extend ActiveSupport::Concern
+ include ProtectedRefAccess
included do
- include ProtectedRefAccess
-
belongs_to :protected_tag
delegate :project, to: :protected_tag
diff --git a/app/models/concerns/redactable.rb b/app/models/concerns/redactable.rb
new file mode 100644
index 00000000000..5ad96d6cc46
--- /dev/null
+++ b/app/models/concerns/redactable.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+# This module searches and redacts sensitive information in
+# redactable fields. Currently only unsubscribe link is redacted.
+# Add following lines into your model:
+#
+# include Redactable
+# redact_field :foo
+#
+module Redactable
+ extend ActiveSupport::Concern
+
+ UNSUBSCRIBE_PATTERN = %r{/sent_notifications/\h{32}/unsubscribe}
+
+ class_methods do
+ def redact_field(field)
+ before_validation do
+ redact_field!(field) if attribute_changed?(field)
+ end
+ end
+ end
+
+ private
+
+ def redact_field!(field)
+ text = public_send(field) # rubocop:disable GitlabSecurity/PublicSend
+ return unless text.present?
+
+ redacted = text.gsub(UNSUBSCRIBE_PATTERN, '/sent_notifications/REDACTED/unsubscribe')
+
+ public_send("#{field}=", redacted) # rubocop:disable GitlabSecurity/PublicSend
+ end
+end
diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb
index 468eaf68883..58143a32fdc 100644
--- a/app/models/concerns/referable.rb
+++ b/app/models/concerns/referable.rb
@@ -40,7 +40,7 @@ module Referable
end
end
- module ClassMethods
+ class_methods do
# The character that prefixes the actual reference identifier
#
# This should be overridden by the including class.
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb
index 85229cded5d..045bf392ac8 100644
--- a/app/models/concerns/relative_positioning.rb
+++ b/app/models/concerns/relative_positioning.rb
@@ -12,6 +12,49 @@ module RelativePositioning
after_save :save_positionable_neighbours
end
+ class_methods do
+ def move_to_end(objects)
+ parent_ids = objects.map(&:parent_ids).flatten.uniq
+ max_relative_position = in_parents(parent_ids).maximum(:relative_position) || START_POSITION
+ objects = objects.reject(&:relative_position)
+
+ self.transaction do
+ objects.each do |object|
+ relative_position = position_between(max_relative_position, MAX_POSITION)
+ object.relative_position = relative_position
+ max_relative_position = relative_position
+ object.save
+ end
+ end
+ end
+
+ # This method takes two integer values (positions) and
+ # calculates the position between them. The range is huge as
+ # the maximum integer value is 2147483647. We are incrementing position by IDEAL_DISTANCE * 2 every time
+ # when we have enough space. If distance is less then IDEAL_DISTANCE we are calculating an average number
+ def position_between(pos_before, pos_after)
+ pos_before ||= MIN_POSITION
+ pos_after ||= MAX_POSITION
+
+ pos_before, pos_after = [pos_before, pos_after].sort
+
+ halfway = (pos_after + pos_before) / 2
+ distance_to_halfway = pos_after - halfway
+
+ if distance_to_halfway < IDEAL_DISTANCE
+ halfway
+ else
+ if pos_before == MIN_POSITION
+ pos_after - IDEAL_DISTANCE
+ elsif pos_after == MAX_POSITION
+ pos_before + IDEAL_DISTANCE
+ else
+ halfway
+ end
+ end
+ end
+ end
+
def min_relative_position
self.class.in_parents(parent_ids).minimum(:relative_position)
end
@@ -57,7 +100,7 @@ module RelativePositioning
@positionable_neighbours = [before] # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
- self.relative_position = position_between(before.relative_position, after.relative_position)
+ self.relative_position = self.class.position_between(before.relative_position, after.relative_position)
end
def move_after(before = self)
@@ -72,7 +115,7 @@ module RelativePositioning
pos_after = issue_to_move.relative_position
end
- self.relative_position = position_between(pos_before, pos_after)
+ self.relative_position = self.class.position_between(pos_before, pos_after)
end
def move_before(after = self)
@@ -87,15 +130,15 @@ module RelativePositioning
pos_before = issue_to_move.relative_position
end
- self.relative_position = position_between(pos_before, pos_after)
+ self.relative_position = self.class.position_between(pos_before, pos_after)
end
def move_to_end
- self.relative_position = position_between(max_relative_position || START_POSITION, MAX_POSITION)
+ self.relative_position = self.class.position_between(max_relative_position || START_POSITION, MAX_POSITION)
end
def move_to_start
- self.relative_position = position_between(min_relative_position || START_POSITION, MIN_POSITION)
+ self.relative_position = self.class.position_between(min_relative_position || START_POSITION, MIN_POSITION)
end
# Indicates if there is an issue that should be shifted to free the place
@@ -112,32 +155,6 @@ module RelativePositioning
private
- # This method takes two integer values (positions) and
- # calculates the position between them. The range is huge as
- # the maximum integer value is 2147483647. We are incrementing position by IDEAL_DISTANCE * 2 every time
- # when we have enough space. If distance is less then IDEAL_DISTANCE we are calculating an average number
- def position_between(pos_before, pos_after)
- pos_before ||= MIN_POSITION
- pos_after ||= MAX_POSITION
-
- pos_before, pos_after = [pos_before, pos_after].sort
-
- halfway = (pos_after + pos_before) / 2
- distance_to_halfway = pos_after - halfway
-
- if distance_to_halfway < IDEAL_DISTANCE
- halfway
- else
- if pos_before == MIN_POSITION
- pos_after - IDEAL_DISTANCE
- elsif pos_after == MAX_POSITION
- pos_before + IDEAL_DISTANCE
- else
- halfway
- end
- end
- end
-
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def save_positionable_neighbours
return unless @positionable_neighbours
diff --git a/app/models/concerns/resolvable_note.rb b/app/models/concerns/resolvable_note.rb
index f47e20229f1..16ea330701d 100644
--- a/app/models/concerns/resolvable_note.rb
+++ b/app/models/concerns/resolvable_note.rb
@@ -20,7 +20,7 @@ module ResolvableNote
scope :unresolved, -> { resolvable.where(resolved_at: nil) }
end
- module ClassMethods
+ class_methods do
# This method must be kept in sync with `#resolve!`
def resolve!(current_user)
unresolved.update_all(resolved_at: Time.now, resolved_by_id: current_user.id)
diff --git a/app/models/concerns/select_for_project_authorization.rb b/app/models/concerns/select_for_project_authorization.rb
index 39306179eb8..333c9118aa5 100644
--- a/app/models/concerns/select_for_project_authorization.rb
+++ b/app/models/concerns/select_for_project_authorization.rb
@@ -3,7 +3,7 @@
module SelectForProjectAuthorization
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def select_for_project_authorization
select("projects.id AS project_id, members.access_level")
end
diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb
index c322c356db2..e51b4e22c96 100644
--- a/app/models/concerns/sha_attribute.rb
+++ b/app/models/concerns/sha_attribute.rb
@@ -3,7 +3,7 @@
module ShaAttribute
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def sha_attribute(name)
return if ENV['STATIC_VERIFICATION']
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 501bd1bb83c..29e48f0c5f7 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -19,7 +19,7 @@ module Sortable
scope :order_name_desc, -> { reorder(Arel::Nodes::Descending.new(arel_table[:name].lower)) }
end
- module ClassMethods
+ class_methods do
def order_by(method)
case method.to_s
when 'created_asc' then order_created_asc
diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb
index c6e3dc385fe..3ff4b4046d3 100644
--- a/app/models/concerns/spammable.rb
+++ b/app/models/concerns/spammable.rb
@@ -3,7 +3,7 @@
module Spammable
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def attr_spammable(attr, options = {})
spammable_attrs << [attr.to_s, options]
end
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index 3b745657a9e..af699eeebce 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -5,8 +5,10 @@ module Storage
extend ActiveSupport::Concern
def move_dir
- if any_project_has_container_registry_tags?
- raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
+ proj_with_tags = first_project_with_container_registry_tags
+
+ if proj_with_tags
+ raise Gitlab::UpdatePathError.new("Namespace #{name} (#{id}) cannot be moved because at least one project (e.g. #{proj_with_tags.name} (#{proj_with_tags.id})) has tags in container registry")
end
parent_was = if parent_changed? && parent_id_was.present?
@@ -25,8 +27,6 @@ module Storage
Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
end
- remove_exports!
-
# If repositories moved successfully we need to
# send update instructions to users.
# However we cannot allow rollback since we moved namespace dir
@@ -94,15 +94,13 @@ module Storage
if gitlab_shell.mv_namespace(repository_storage, full_path, new_path)
Gitlab::AppLogger.info %Q(Namespace directory "#{full_path}" moved to "#{new_path}")
- # Remove namespace directroy async with delay so
+ # Remove namespace directory async with delay so
# GitLab has time to remove all projects first
run_after_commit do
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage, new_path)
end
end
end
-
- remove_exports!
end
def remove_legacy_exports!
diff --git a/app/models/concerns/strip_attribute.rb b/app/models/concerns/strip_attribute.rb
index 344f677a3f3..c9f5ba7793d 100644
--- a/app/models/concerns/strip_attribute.rb
+++ b/app/models/concerns/strip_attribute.rb
@@ -14,7 +14,7 @@
module StripAttribute
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def strip_attributes(*attrs)
strip_attrs.concat(attrs)
end
diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb
index 1d0a61364b0..92a5c1112af 100644
--- a/app/models/concerns/subscribable.rb
+++ b/app/models/concerns/subscribable.rb
@@ -31,9 +31,11 @@ module Subscribable
end
def subscribers(project)
- subscriptions_available(project)
- .where(subscribed: true)
- .map(&:user)
+ relation = subscriptions_available(project)
+ .where(subscribed: true)
+ .select(:user_id)
+
+ User.where(id: relation)
end
def toggle_subscription(user, project = nil)
diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb
index 522b65e4205..23a43aec677 100644
--- a/app/models/concerns/token_authenticatable.rb
+++ b/app/models/concerns/token_authenticatable.rb
@@ -5,57 +5,53 @@ module TokenAuthenticatable
private
- def write_new_token(token_field)
- new_token = generate_available_token(token_field)
- write_attribute(token_field, new_token)
- end
-
- def generate_available_token(token_field)
- loop do
- token = generate_token(token_field)
- break token unless self.class.unscoped.find_by(token_field => token)
- end
- end
-
- def generate_token(token_field)
- Devise.friendly_token
- end
-
class_methods do
- def authentication_token_fields
- @token_fields || []
- end
-
private # rubocop:disable Lint/UselessAccessModifier
- def add_authentication_token_field(token_field)
+ def add_authentication_token_field(token_field, options = {})
@token_fields = [] unless @token_fields
+ unique = options.fetch(:unique, true)
+
+ if @token_fields.include?(token_field)
+ raise ArgumentError.new("#{token_field} already configured via add_authentication_token_field")
+ end
+
@token_fields << token_field
- define_singleton_method("find_by_#{token_field}") do |token|
- find_by(token_field => token) if token
+ attr_accessor :cleartext_tokens
+
+ strategy = if options[:digest]
+ TokenAuthenticatableStrategies::Digest.new(self, token_field, options)
+ else
+ TokenAuthenticatableStrategies::Insecure.new(self, token_field, options)
+ end
+
+ if unique
+ define_singleton_method("find_by_#{token_field}") do |token|
+ strategy.find_token_authenticatable(token)
+ end
end
- define_method("ensure_#{token_field}") do
- current_token = read_attribute(token_field)
- current_token.blank? ? write_new_token(token_field) : current_token
+ define_method(token_field) do
+ strategy.get_token(self)
end
define_method("set_#{token_field}") do |token|
- write_attribute(token_field, token) if token
+ strategy.set_token(self, token)
+ end
+
+ define_method("ensure_#{token_field}") do
+ strategy.ensure_token(self)
end
# Returns a token, but only saves when the database is in read & write mode
define_method("ensure_#{token_field}!") do
- send("reset_#{token_field}!") if read_attribute(token_field).blank? # rubocop:disable GitlabSecurity/PublicSend
-
- read_attribute(token_field)
+ strategy.ensure_token!(self)
end
# Resets the token, but only saves when the database is in read & write mode
define_method("reset_#{token_field}!") do
- write_new_token(token_field)
- save! if Gitlab::Database.read_write?
+ strategy.reset_token!(self)
end
end
end
diff --git a/app/models/concerns/token_authenticatable_strategies/base.rb b/app/models/concerns/token_authenticatable_strategies/base.rb
new file mode 100644
index 00000000000..413721d3e6c
--- /dev/null
+++ b/app/models/concerns/token_authenticatable_strategies/base.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+module TokenAuthenticatableStrategies
+ class Base
+ def initialize(klass, token_field, options)
+ @klass = klass
+ @token_field = token_field
+ @options = options
+ end
+
+ def find_token_authenticatable(instance, unscoped = false)
+ raise NotImplementedError
+ end
+
+ def get_token(instance)
+ raise NotImplementedError
+ end
+
+ def set_token(instance)
+ raise NotImplementedError
+ end
+
+ def ensure_token(instance)
+ write_new_token(instance) unless token_set?(instance)
+ end
+
+ # Returns a token, but only saves when the database is in read & write mode
+ def ensure_token!(instance)
+ reset_token!(instance) unless token_set?(instance)
+ get_token(instance)
+ end
+
+ # Resets the token, but only saves when the database is in read & write mode
+ def reset_token!(instance)
+ write_new_token(instance)
+ instance.save! if Gitlab::Database.read_write?
+ end
+
+ protected
+
+ def write_new_token(instance)
+ new_token = generate_available_token
+ set_token(instance, new_token)
+ end
+
+ def unique
+ @options.fetch(:unique, true)
+ end
+
+ def generate_available_token
+ loop do
+ token = generate_token
+ break token unless unique && find_token_authenticatable(token, true)
+ end
+ end
+
+ def generate_token
+ @options[:token_generator] ? @options[:token_generator].call : Devise.friendly_token
+ end
+
+ def relation(unscoped)
+ unscoped ? @klass.unscoped : @klass
+ end
+
+ def token_set?(instance)
+ raise NotImplementedError
+ end
+
+ def token_field_name
+ @token_field
+ end
+ end
+end
diff --git a/app/models/concerns/token_authenticatable_strategies/digest.rb b/app/models/concerns/token_authenticatable_strategies/digest.rb
new file mode 100644
index 00000000000..9926662ed66
--- /dev/null
+++ b/app/models/concerns/token_authenticatable_strategies/digest.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module TokenAuthenticatableStrategies
+ class Digest < Base
+ def find_token_authenticatable(token, unscoped = false)
+ return unless token
+
+ token_authenticatable = relation(unscoped).find_by(token_field_name => Gitlab::CryptoHelper.sha256(token))
+
+ if @options[:fallback]
+ token_authenticatable ||= fallback_strategy.find_token_authenticatable(token)
+ end
+
+ token_authenticatable
+ end
+
+ def get_token(instance)
+ token = instance.cleartext_tokens&.[](@token_field)
+ token ||= fallback_strategy.get_token(instance) if @options[:fallback]
+
+ token
+ end
+
+ def set_token(instance, token)
+ return unless token
+
+ instance.cleartext_tokens ||= {}
+ instance.cleartext_tokens[@token_field] = token
+ instance[token_field_name] = Gitlab::CryptoHelper.sha256(token)
+ instance[@token_field] = nil if @options[:fallback]
+ end
+
+ protected
+
+ def fallback_strategy
+ @fallback_strategy ||= TokenAuthenticatableStrategies::Insecure.new(@klass, @token_field, @options)
+ end
+
+ def token_set?(instance)
+ token_digest = instance.read_attribute(token_field_name)
+ token_digest ||= instance.read_attribute(@token_field) if @options[:fallback]
+
+ token_digest.present?
+ end
+
+ def token_field_name
+ "#{@token_field}_digest"
+ end
+ end
+end
diff --git a/app/models/concerns/token_authenticatable_strategies/insecure.rb b/app/models/concerns/token_authenticatable_strategies/insecure.rb
new file mode 100644
index 00000000000..5f915259521
--- /dev/null
+++ b/app/models/concerns/token_authenticatable_strategies/insecure.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module TokenAuthenticatableStrategies
+ class Insecure < Base
+ def find_token_authenticatable(token, unscoped = false)
+ relation(unscoped).find_by(@token_field => token) if token
+ end
+
+ def get_token(instance)
+ instance.read_attribute(@token_field)
+ end
+
+ def set_token(instance, token)
+ instance[@token_field] = token if token
+ end
+
+ protected
+
+ def token_set?(instance)
+ instance.read_attribute(@token_field).present?
+ end
+ end
+end
diff --git a/app/models/concerns/triggerable_hooks.rb b/app/models/concerns/triggerable_hooks.rb
index f55ab2fcaf3..c52baa0524c 100644
--- a/app/models/concerns/triggerable_hooks.rb
+++ b/app/models/concerns/triggerable_hooks.rb
@@ -6,6 +6,7 @@ module TriggerableHooks
push_hooks: :push_events,
tag_push_hooks: :tag_push_events,
issue_hooks: :issues_events,
+ confidential_note_hooks: :confidential_note_events,
confidential_issue_hooks: :confidential_issues_events,
note_hooks: :note_events,
merge_request_hooks: :merge_requests_events,
@@ -28,6 +29,12 @@ module TriggerableHooks
public_send(trigger) # rubocop:disable GitlabSecurity/PublicSend
end
+ def select_active(hooks_scope, data)
+ select do |hook|
+ ActiveHookFilter.new(hook).matches?(hooks_scope, data)
+ end
+ end
+
private
def triggerable_hooks(hooks)
diff --git a/app/models/concerns/with_uploads.rb b/app/models/concerns/with_uploads.rb
index e231af5368d..2bdef2a40e4 100644
--- a/app/models/concerns/with_uploads.rb
+++ b/app/models/concerns/with_uploads.rb
@@ -2,7 +2,7 @@
# Mounted uploaders are destroyed by carrierwave's after_commit
# hook. This hook fetches upload location (local vs remote) from
-# Upload model. So it's neccessary to make sure that during that
+# Upload model. So it's necessary to make sure that during that
# after_commit hook model's associated uploads are not deleted yet.
# IOW we can not use dependent: :destroy :
# has_many :uploads, as: :model, dependent: :destroy
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index 41413854d5c..2c08a8e1acf 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -8,8 +8,7 @@ class ContainerRepository < ActiveRecord::Base
delegate :client, to: :registry
- before_destroy :delete_tags!
-
+ # rubocop: disable CodeReuse/ServiceClass
def registry
@registry ||= begin
token = Auth::ContainerRegistryAuthenticationService.full_access_token(path)
@@ -20,6 +19,7 @@ class ContainerRepository < ActiveRecord::Base
ContainerRegistry::Registry.new(url, token: token, path: host_port)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def path
@path ||= [project.full_path, name]
diff --git a/app/models/dashboard_group_milestone.rb b/app/models/dashboard_group_milestone.rb
index 13807d43265..32e8104125c 100644
--- a/app/models/dashboard_group_milestone.rb
+++ b/app/models/dashboard_group_milestone.rb
@@ -13,7 +13,11 @@ class DashboardGroupMilestone < GlobalMilestone
end
def self.build_collection(groups)
- MilestonesFinder.new(group_ids: groups.pluck(:id)).execute.map { |m| new(m) }
+ Milestone.of_groups(groups.select(:id))
+ .reorder_by_due_date_asc
+ .order_by_name_asc
+ .active
+ .map { |m| new(m) }
end
override :group_milestone?
diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb
index fd5d7726fb6..db501b4b506 100644
--- a/app/models/deploy_key.rb
+++ b/app/models/deploy_key.rb
@@ -18,7 +18,7 @@ class DeployKey < Key
end
def orphaned?
- self.deploy_keys_projects.length == 0
+ self.deploy_keys_projects.empty?
end
def almost_orphaned?
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index 0b2eedf3631..e3524305346 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -4,6 +4,7 @@ class DeployToken < ActiveRecord::Base
include Expirable
include TokenAuthenticatable
include PolicyActor
+ include Gitlab::Utils::StrongMemoize
add_authentication_token_field :token
AVAILABLE_SCOPES = %i(read_repository read_registry).freeze
@@ -49,7 +50,9 @@ class DeployToken < ActiveRecord::Base
# to a single project, later we're going to extend
# that to be for multiple projects and namespaces.
def project
- projects.first
+ strong_memoize(:project) do
+ projects.first
+ end
end
def expires_at
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 6962b54441b..811e623b7f7 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -3,21 +3,68 @@
class Deployment < ActiveRecord::Base
include AtomicInternalId
include IidRoutes
+ include AfterCommitQueue
belongs_to :project, required: true
belongs_to :environment, required: true
belongs_to :user
belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
- has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.deployments&.maximum(:iid) }
+ has_internal_id :iid, scope: :project, init: ->(s) do
+ Deployment.where(project: s.project).maximum(:iid) if s&.project
+ end
validates :sha, presence: true
validates :ref, presence: true
delegate :name, to: :environment, prefix: true
- after_create :create_ref
- after_create :invalidate_cache
+ scope :for_environment, -> (environment) { where(environment_id: environment) }
+
+ state_machine :status, initial: :created do
+ event :run do
+ transition created: :running
+ end
+
+ event :succeed do
+ transition any - [:success] => :success
+ end
+
+ event :drop do
+ transition any - [:failed] => :failed
+ end
+
+ event :cancel do
+ transition any - [:canceled] => :canceled
+ end
+
+ before_transition any => [:success, :failed, :canceled] do |deployment|
+ deployment.finished_at = Time.now
+ end
+
+ after_transition any => :success do |deployment|
+ deployment.run_after_commit do
+ Deployments::SuccessWorker.perform_async(id)
+ end
+ end
+ end
+
+ enum status: {
+ created: 0,
+ running: 1,
+ success: 2,
+ failed: 3,
+ canceled: 4
+ }
+
+ def self.last_for_environment(environment)
+ ids = self
+ .for_environment(environment)
+ .select('MAX(id) AS id')
+ .group(:environment_id)
+ .map(&:id)
+ find(ids)
+ end
def commit
project.commit(sha)
@@ -44,7 +91,11 @@ class Deployment < ActiveRecord::Base
end
def manual_actions
- @manual_actions ||= deployable.try(:other_actions)
+ @manual_actions ||= deployable.try(:other_manual_actions)
+ end
+
+ def scheduled_actions
+ @scheduled_actions ||= deployable.try(:other_scheduled_actions)
end
def includes_commit?(commit)
@@ -54,15 +105,15 @@ class Deployment < ActiveRecord::Base
end
def update_merge_request_metrics!
- return unless environment.update_merge_request_metrics?
+ return unless environment.update_merge_request_metrics? && success?
merge_requests = project.merge_requests
.joins(:metrics)
.where(target_branch: self.ref, merge_request_metrics: { first_deployed_to_production_at: nil })
- .where("merge_request_metrics.merged_at <= ?", self.created_at)
+ .where("merge_request_metrics.merged_at <= ?", finished_at)
if previous_deployment
- merge_requests = merge_requests.where("merge_request_metrics.merged_at >= ?", previous_deployment.created_at)
+ merge_requests = merge_requests.where("merge_request_metrics.merged_at >= ?", previous_deployment.finished_at)
end
# Need to use `map` instead of `select` because MySQL doesn't allow `SELECT`ing from the same table
@@ -76,7 +127,7 @@ class Deployment < ActiveRecord::Base
MergeRequest::Metrics
.where(merge_request_id: merge_request_ids, first_deployed_to_production_at: nil)
- .update_all(first_deployed_to_production_at: self.created_at)
+ .update_all(first_deployed_to_production_at: finished_at)
end
def previous_deployment
@@ -94,26 +145,36 @@ class Deployment < ActiveRecord::Base
@stop_action ||= manual_actions.find_by(name: on_stop)
end
+ def finished_at
+ read_attribute(:finished_at) || legacy_finished_at
+ end
+
+ def deployed_at
+ return unless success?
+
+ finished_at
+ end
+
def formatted_deployment_time
- created_at.to_time.in_time_zone.to_s(:medium)
+ deployed_at&.to_time&.in_time_zone&.to_s(:medium)
end
def has_metrics?
- prometheus_adapter&.can_query?
+ prometheus_adapter&.can_query? && success?
end
def metrics
return {} unless has_metrics?
metrics = prometheus_adapter.query(:deployment, self)
- metrics&.merge(deployment_time: created_at.to_i) || {}
+ metrics&.merge(deployment_time: finished_at.to_i) || {}
end
def additional_metrics
return {} unless has_metrics?
metrics = prometheus_adapter.query(:additional_metrics_deployment, self)
- metrics&.merge(deployment_time: created_at.to_i) || {}
+ metrics&.merge(deployment_time: finished_at.to_i) || {}
end
private
@@ -125,4 +186,8 @@ class Deployment < ActiveRecord::Base
def ref_path
File.join(environment.ref_path, 'deployments', iid.to_s)
end
+
+ def legacy_finished_at
+ self.created_at if success? && !read_attribute(:finished_at)
+ end
end
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 58d949315e0..c32008aa9c7 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -5,24 +5,21 @@
# A note of this type can be resolvable.
class DiffNote < Note
include NoteOnDiff
+ include DiffPositionableNote
include Gitlab::Utils::StrongMemoize
- NOTEABLE_TYPES = %w(MergeRequest Commit).freeze
-
- serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
- serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
- serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
+ def self.noteable_types
+ %w(MergeRequest Commit)
+ end
validates :original_position, presence: true
validates :position, presence: true
validates :line_code, presence: true, line_code: true, if: :on_text?
- validates :noteable_type, inclusion: { in: NOTEABLE_TYPES }
+ validates :noteable_type, inclusion: { in: noteable_types }
validate :positions_complete
validate :verify_supported
validate :diff_refs_match_commit, if: :for_commit?
- before_validation :set_original_position, on: :create
- before_validation :update_position, on: :create, if: :on_text?
before_validation :set_line_code, if: :on_text?
after_save :keep_around_commits
after_commit :create_diff_file, on: :create
@@ -31,31 +28,6 @@ class DiffNote < Note
DiffDiscussion
end
- %i(original_position position change_position).each do |meth|
- define_method "#{meth}=" do |new_position|
- if new_position.is_a?(String)
- new_position = JSON.parse(new_position) rescue nil
- end
-
- if new_position.is_a?(Hash)
- new_position = new_position.with_indifferent_access
- new_position = Gitlab::Diff::Position.new(new_position)
- end
-
- return if new_position == read_attribute(meth)
-
- super(new_position)
- end
- end
-
- def on_text?
- position.position_type == "text"
- end
-
- def on_image?
- position.position_type == "image"
- end
-
def create_diff_file
return unless should_create_diff_file?
@@ -87,15 +59,6 @@ class DiffNote < Note
self.diff_file.line_code(self.diff_line)
end
- def active?(diff_refs = nil)
- return false unless supported?
- return true if for_commit?
-
- diff_refs ||= noteable.diff_refs
-
- self.position.diff_refs == diff_refs
- end
-
def created_at_diff?(diff_refs)
return false unless supported?
return true if for_commit?
@@ -103,6 +66,10 @@ class DiffNote < Note
self.original_position.diff_refs == diff_refs
end
+ def discussion_first_note?
+ self == discussion.first_note
+ end
+
private
def enqueue_diff_file_creation_job
@@ -115,63 +82,43 @@ class DiffNote < Note
end
def should_create_diff_file?
- on_text? && note_diff_file.nil? && self == discussion.first_note
+ on_text? && note_diff_file.nil? && discussion_first_note?
end
def fetch_diff_file
- if note_diff_file
- diff = Gitlab::Git::Diff.new(note_diff_file.to_hash)
- Gitlab::Diff::File.new(diff,
- repository: project.repository,
- diff_refs: original_position.diff_refs)
- elsif created_at_diff?(noteable.diff_refs)
- # We're able to use the already persisted diffs (Postgres) if we're
- # presenting a "current version" of the MR discussion diff.
- # So no need to make an extra Gitaly diff request for it.
- # As an extra benefit, the returned `diff_file` already
- # has `highlighted_diff_lines` data set from Redis on
- # `Diff::FileCollection::MergeRequestDiff`.
- noteable.diffs(paths: original_position.paths, expanded: true).diff_files.first
- else
- original_position.diff_file(self.project.repository)
- end
+ file =
+ if note_diff_file
+ diff = Gitlab::Git::Diff.new(note_diff_file.to_hash)
+ Gitlab::Diff::File.new(diff,
+ repository: project.repository,
+ diff_refs: original_position.diff_refs)
+ elsif created_at_diff?(noteable.diff_refs)
+ # We're able to use the already persisted diffs (Postgres) if we're
+ # presenting a "current version" of the MR discussion diff.
+ # So no need to make an extra Gitaly diff request for it.
+ # As an extra benefit, the returned `diff_file` already
+ # has `highlighted_diff_lines` data set from Redis on
+ # `Diff::FileCollection::MergeRequestDiff`.
+ noteable.diffs(original_position.diff_options).diff_files.first
+ else
+ original_position.diff_file(self.project.repository)
+ end
+
+ # Since persisted diff files already have its content "unfolded"
+ # there's no need to make it pass through the unfolding process.
+ file&.unfold_diff_lines(position) unless note_diff_file
+
+ file
end
def supported?
for_commit? || self.noteable.has_complete_diff_refs?
end
- def set_original_position
- self.original_position = self.position.dup unless self.original_position&.complete?
- end
-
def set_line_code
self.line_code = self.position.line_code(self.project.repository)
end
- def update_position
- return unless supported?
- return if for_commit?
-
- return if active?
-
- tracer = Gitlab::Diff::PositionTracer.new(
- project: self.project,
- old_diff_refs: self.position.diff_refs,
- new_diff_refs: self.noteable.diff_refs,
- paths: self.position.paths
- )
-
- result = tracer.trace(self.position)
- return unless result
-
- if result[:outdated]
- self.change_position = result[:position]
- else
- self.position = result[:position]
- end
- end
-
def verify_supported
return if supported?
@@ -191,14 +138,18 @@ class DiffNote < Note
end
def keep_around_commits
- project.repository.keep_around(self.original_position.base_sha)
- project.repository.keep_around(self.original_position.start_sha)
- project.repository.keep_around(self.original_position.head_sha)
+ shas = [
+ self.original_position.base_sha,
+ self.original_position.start_sha,
+ self.original_position.head_sha
+ ]
if self.position != self.original_position
- project.repository.keep_around(self.position.base_sha)
- project.repository.keep_around(self.position.start_sha)
- project.repository.keep_around(self.position.head_sha)
+ shas << self.position.base_sha
+ shas << self.position.start_sha
+ shas << self.position.head_sha
end
+
+ project.repository.keep_around(*shas)
end
end
diff --git a/app/models/discussion_note.rb b/app/models/discussion_note.rb
index 89d86aaed66..142cbdcdfa6 100644
--- a/app/models/discussion_note.rb
+++ b/app/models/discussion_note.rb
@@ -5,9 +5,11 @@
# A note of this type can be resolvable.
class DiscussionNote < Note
# Names of all implementers of `Noteable` that support discussions.
- NOTEABLE_TYPES = %w(MergeRequest Issue Commit Snippet).freeze
+ def self.noteable_types
+ %w(MergeRequest Issue Commit Snippet)
+ end
- validates :noteable_type, inclusion: { in: NOTEABLE_TYPES }
+ validates :noteable_type, inclusion: { in: noteable_types }
def discussion_class(*)
Discussion
diff --git a/app/models/environment.rb b/app/models/environment.rb
index c8d1d378ae0..934828946b9 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -8,9 +8,9 @@ class Environment < ActiveRecord::Base
belongs_to :project, required: true
- has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :deployments, -> { success }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
- has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment'
+ has_one :last_deployment, -> { success.order('deployments.id DESC') }, class_name: 'Deployment'
before_validation :nullify_external_url
before_validation :generate_slug, if: ->(env) { env.slug.blank? }
@@ -48,6 +48,9 @@ class Environment < ActiveRecord::Base
order(Gitlab::Database.nulls_first_order("(#{max_deployment_id_sql})", 'ASC'))
end
scope :in_review_folder, -> { where(environment_type: "review") }
+ scope :for_name, -> (name) { where(name: name) }
+ scope :for_project, -> (project) { where(project_id: project) }
+ scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
state_machine :state, initial: :available do
event :start do
@@ -147,7 +150,7 @@ class Environment < ActiveRecord::Base
end
def has_metrics?
- prometheus_adapter&.can_query? && available? && last_deployment.present?
+ prometheus_adapter&.can_query? && available?
end
def metrics
@@ -158,9 +161,11 @@ class Environment < ActiveRecord::Base
prometheus_adapter.query(:additional_metrics_environment, self) if has_metrics?
end
+ # rubocop: disable CodeReuse/ServiceClass
def prometheus_adapter
@prometheus_adapter ||= Prometheus::AdapterService.new(project, deployment_platform).prometheus_adapter
end
+ # rubocop: enable CodeReuse/ServiceClass
def slug
super.presence || generate_slug
diff --git a/app/models/environment_status.rb b/app/models/environment_status.rb
new file mode 100644
index 00000000000..7078496ff52
--- /dev/null
+++ b/app/models/environment_status.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+class EnvironmentStatus
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :environment, :merge_request, :sha
+
+ delegate :id, to: :environment
+ delegate :name, to: :environment
+ delegate :project, to: :environment
+ delegate :deployed_at, to: :deployment, allow_nil: true
+
+ def self.for_merge_request(mr, user)
+ build_environments_status(mr, user, mr.diff_head_sha)
+ end
+
+ def self.after_merge_request(mr, user)
+ return [] unless mr.merged?
+
+ build_environments_status(mr, user, mr.merge_commit_sha)
+ end
+
+ def initialize(environment, merge_request, sha)
+ @environment = environment
+ @merge_request = merge_request
+ @sha = sha
+ end
+
+ def deployment
+ strong_memoize(:deployment) do
+ Deployment.where(environment: environment).find_by_sha(sha)
+ end
+ end
+
+ def changes
+ return [] if project.route_map_for(sha).nil?
+
+ changed_files.map { |file| build_change(file) }.compact
+ end
+
+ def changed_files
+ merge_request.merge_request_diff
+ .merge_request_diff_files.where(deleted_file: false)
+ end
+
+ ##
+ # Since frontend has not supported all statuses yet, BE has to
+ # proxy some status to a supported status.
+ def status
+ return unless deployment
+
+ case deployment.status
+ when 'created'
+ 'running'
+ when 'canceled'
+ 'failed'
+ else
+ deployment.status
+ end
+ end
+
+ private
+
+ PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze
+
+ def build_change(file)
+ public_path = project.public_path_for_source_path(file.new_path, sha)
+ return if public_path.nil?
+
+ ext = File.extname(public_path)
+ return if ext.present? && ext !~ PAGE_EXTENSIONS
+
+ {
+ path: public_path,
+ external_url: environment.external_url_for(file.new_path, sha)
+ }
+ end
+
+ def self.build_environments_status(mr, user, sha)
+ Environment.where(project_id: [mr.source_project_id, mr.target_project_id])
+ .available
+ .with_deployment(sha).map do |environment|
+ next unless Ability.allowed?(user, :read_environment, environment)
+
+ EnvironmentStatus.new(environment, mr, sha)
+ end.compact
+ end
+ private_class_method :build_environments_status
+end
diff --git a/app/models/epic.rb b/app/models/epic.rb
index f027993376c..ccd10593434 100644
--- a/app/models/epic.rb
+++ b/app/models/epic.rb
@@ -3,6 +3,10 @@
# Placeholder class for model that is implemented in EE
# It reserves '&' as a reference prefix, but the table does not exists in CE
class Epic < ActiveRecord::Base
+ def self.link_reference_pattern
+ nil
+ end
+
def self.reference_prefix
'&'
end
diff --git a/app/models/event.rb b/app/models/event.rb
index ba28866e8e6..2e690f8c013 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -3,6 +3,7 @@
class Event < ActiveRecord::Base
include Sortable
include IgnorableColumn
+ include FromUnion
default_scope { reorder(nil) }
CREATED = 1
@@ -147,21 +148,31 @@ class Event < ActiveRecord::Base
end
end
+ # rubocop:disable Metrics/CyclomaticComplexity
+ # rubocop:disable Metrics/PerceivedComplexity
def visible_to_user?(user = nil)
if push? || commit_note?
Ability.allowed?(user, :download_code, project)
elsif membership_changed?
- true
+ Ability.allowed?(user, :read_project, project)
elsif created_project?
- true
+ Ability.allowed?(user, :read_project, project)
elsif issue? || issue_note?
Ability.allowed?(user, :read_issue, note? ? note_target : target)
elsif merge_request? || merge_request_note?
Ability.allowed?(user, :read_merge_request, note? ? note_target : target)
+ elsif personal_snippet_note?
+ Ability.allowed?(user, :read_personal_snippet, note_target)
+ elsif project_snippet_note?
+ Ability.allowed?(user, :read_project_snippet, note_target)
+ elsif milestone?
+ Ability.allowed?(user, :read_milestone, project)
else
- milestone?
+ false # No other event types are visible
end
end
+ # rubocop:enable Metrics/PerceivedComplexity
+ # rubocop:enable Metrics/CyclomaticComplexity
def project_name
if project
@@ -303,6 +314,10 @@ class Event < ActiveRecord::Base
note? && target && target.for_snippet?
end
+ def personal_snippet_note?
+ note? && target && target.for_personal_snippet?
+ end
+
def note_target
target.noteable
end
diff --git a/app/models/forked_project_link.rb b/app/models/forked_project_link.rb
deleted file mode 100644
index 0f7067238cd..00000000000
--- a/app/models/forked_project_link.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-class ForkedProjectLink < ActiveRecord::Base
- belongs_to :forked_to_project, -> { where.not(pending_delete: true) }, class_name: 'Project'
- belongs_to :forked_from_project, -> { where.not(pending_delete: true) }, class_name: 'Project'
-end
diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb
index 6e23e811b0e..085ffd16c6a 100644
--- a/app/models/global_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -17,7 +17,7 @@ class GlobalMilestone
params =
{ project_ids: projects.map(&:id), state: params[:state] }
- child_milestones = MilestonesFinder.new(params).execute
+ child_milestones = MilestonesFinder.new(params).execute # rubocop: disable CodeReuse/Finder
milestones = child_milestones.select(:id, :title).group_by(&:title).map do |title, grouped|
milestones_relation = Milestone.where(id: grouped.map(&:id))
@@ -34,50 +34,6 @@ class GlobalMilestone
new(title, child_milestones)
end
- def self.states_count(projects, group = nil)
- legacy_group_milestones_count = legacy_group_milestone_states_count(projects)
- group_milestones_count = group_milestones_states_count(group)
-
- legacy_group_milestones_count.merge(group_milestones_count) do |k, legacy_group_milestones_count, group_milestones_count|
- legacy_group_milestones_count + group_milestones_count
- end
- end
-
- def self.group_milestones_states_count(group)
- return STATE_COUNT_HASH unless group
-
- params = { group_ids: [group.id], state: 'all' }
-
- relation = MilestonesFinder.new(params).execute
- grouped_by_state = relation.reorder(nil).group(:state).count
-
- {
- opened: grouped_by_state['active'] || 0,
- closed: grouped_by_state['closed'] || 0,
- all: grouped_by_state.values.sum
- }
- end
-
- # Counts the legacy group milestones which must be grouped by title
- def self.legacy_group_milestone_states_count(projects)
- return STATE_COUNT_HASH unless projects
-
- params = { project_ids: projects.map(&:id), state: 'all' }
-
- relation = MilestonesFinder.new(params).execute
- project_milestones_by_state_and_title = relation.reorder(nil).group(:state, :title).count
-
- opened = count_by_state(project_milestones_by_state_and_title, 'active')
- closed = count_by_state(project_milestones_by_state_and_title, 'closed')
- all = project_milestones_by_state_and_title.map { |(_, title), _| title }.uniq.count
-
- {
- opened: opened,
- closed: closed,
- all: all
- }
- end
-
def self.count_by_state(milestones_by_state_and_title, state)
milestones_by_state_and_title.count do |(milestone_state, _), _|
milestone_state == state
diff --git a/app/models/group.rb b/app/models/group.rb
index 106a1f4a94c..adb9169cfcd 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -41,6 +41,9 @@ class Group < Namespace
has_many :boards
has_many :badges, class_name: 'GroupBadge'
+ has_many :cluster_groups, class_name: 'Clusters::Group'
+ has_many :clusters, through: :cluster_groups, class_name: 'Clusters::Cluster'
+
has_many :todos
accepts_nested_attributes_for :variables, allow_destroy: true
@@ -82,8 +85,17 @@ class Group < Namespace
User.reference_pattern
end
- def visible_to_user(user)
- where(id: user.authorized_groups.select(:id).reorder(nil))
+ # WARNING: This method should never be used on its own
+ # please do make sure the number of rows you are filtering is small
+ # enough for this query
+ def public_or_visible_to_user(user)
+ return public_to_user unless user
+
+ public_for_user = public_to_user_arel(user)
+ visible_for_user = visible_to_user_arel(user)
+ public_or_visible = public_for_user.or(visible_for_user)
+
+ where(public_or_visible)
end
def select_for_project_authorization
@@ -95,6 +107,23 @@ class Group < Namespace
super
end
end
+
+ private
+
+ def public_to_user_arel(user)
+ self.arel_table[:visibility_level]
+ .in(Gitlab::VisibilityLevel.levels_for_user(user))
+ end
+
+ def visible_to_user_arel(user)
+ groups_table = self.arel_table
+ authorized_groups = user.authorized_groups.as('authorized')
+
+ groups_table.project(1)
+ .from(authorized_groups)
+ .where(authorized_groups[:id].eq(groups_table[:id]))
+ .exists
+ end
end
# Overrides notification_settings has_many association
@@ -236,14 +265,18 @@ class Group < Namespace
system_hook_service.execute_hooks_for(self, :destroy)
end
+ # rubocop: disable CodeReuse/ServiceClass
def system_hook_service
SystemHooksService.new
end
+ # rubocop: enable CodeReuse/ServiceClass
+ # rubocop: disable CodeReuse/ServiceClass
def refresh_members_authorized_projects(blocking: true)
UserProjectAccessChangedService.new(user_ids_for_project_authorizations)
.execute(blocking: blocking)
end
+ # rubocop: enable CodeReuse/ServiceClass
def user_ids_for_project_authorizations
members_with_parents.pluck(:user_id)
@@ -300,14 +333,12 @@ class Group < Namespace
# 3. They belong to a sub-group or project in such sub-group
# 4. They belong to an ancestor group
def direct_and_indirect_users
- union = Gitlab::SQL::Union.new([
+ User.from_union([
User
.where(id: direct_and_indirect_members.select(:user_id))
.reorder(nil),
project_users_with_descendants
])
-
- User.from("(#{union.to_sql}) #{User.table_name}")
end
# Returns all users that are members of projects
@@ -338,7 +369,7 @@ class Group < Namespace
}
end
- def secret_variables_for(ref, project)
+ def ci_variables_for(ref, project)
list_of_ids = [self] + ancestors
variables = Ci::GroupVariable.where(group: list_of_ids)
variables = variables.unprotected unless project.protected_for?(ref)
diff --git a/app/models/hooks/active_hook_filter.rb b/app/models/hooks/active_hook_filter.rb
new file mode 100644
index 00000000000..283e2d680f4
--- /dev/null
+++ b/app/models/hooks/active_hook_filter.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class ActiveHookFilter
+ def initialize(hook)
+ @hook = hook
+ @push_events_filter_matcher = RefMatcher.new(@hook.push_events_branch_filter)
+ end
+
+ def matches?(hooks_scope, data)
+ return true if hooks_scope != :push_hooks
+ return true if @hook.push_events_branch_filter.blank?
+
+ branch_name = Gitlab::Git.branch_name(data[:ref])
+ @push_events_filter_matcher.matches?(branch_name)
+ end
+end
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index bda82a116a1..8f305dd7c22 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -4,7 +4,9 @@ class ServiceHook < WebHook
belongs_to :service
validates :service, presence: true
- def execute(data)
- WebHookService.new(self, data, 'service_hook').execute
+ # rubocop: disable CodeReuse/ServiceClass
+ def execute(data, hook_name = 'service_hook')
+ WebHookService.new(self, data, hook_name).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
end
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index f18aadefa5c..b2fb79bc7ed 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -3,20 +3,35 @@
class WebHook < ActiveRecord::Base
include Sortable
+ attr_encrypted :token,
+ mode: :per_attribute_iv,
+ algorithm: 'aes-256-gcm',
+ key: Settings.attr_encrypted_db_key_base_truncated
+
+ attr_encrypted :url,
+ mode: :per_attribute_iv,
+ algorithm: 'aes-256-gcm',
+ key: Settings.attr_encrypted_db_key_base_truncated
+
has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
validates :url, presence: true, public_url: { allow_localhost: lambda(&:allow_local_requests?),
allow_local_network: lambda(&:allow_local_requests?) }
validates :token, format: { without: /\n/ }
+ validates :push_events_branch_filter, branch_filter: true
+ # rubocop: disable CodeReuse/ServiceClass
def execute(data, hook_name)
WebHookService.new(self, data, hook_name).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
+ # rubocop: disable CodeReuse/ServiceClass
def async_execute(data, hook_name)
WebHookService.new(self, data, hook_name).async_execute
end
+ # rubocop: enable CodeReuse/ServiceClass
# Allow urls pointing localhost and the local network
def allow_local_requests?
diff --git a/app/models/identity.rb b/app/models/identity.rb
index f5a13dbd6f2..d63dd432426 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -1,18 +1,14 @@
# frozen_string_literal: true
class Identity < ActiveRecord::Base
- def self.uniqueness_scope
- :provider
- end
-
include Sortable
include CaseSensitivity
belongs_to :user
validates :provider, presence: true
- validates :extern_uid, allow_blank: true, uniqueness: { scope: uniqueness_scope, case_sensitive: false }
- validates :user_id, uniqueness: { scope: uniqueness_scope }
+ validates :extern_uid, allow_blank: true, uniqueness: { scope: UniquenessScopes.scopes, case_sensitive: false }
+ validates :user_id, uniqueness: { scope: UniquenessScopes.scopes }
before_save :ensure_normalized_extern_uid, if: :extern_uid_changed?
after_destroy :clear_user_synced_attributes, if: :user_synced_attributes_metadata_from_provider?
diff --git a/app/models/identity/uniqueness_scopes.rb b/app/models/identity/uniqueness_scopes.rb
new file mode 100644
index 00000000000..674b735903f
--- /dev/null
+++ b/app/models/identity/uniqueness_scopes.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class Identity < ActiveRecord::Base
+ # This module and method are defined in a separate file to allow EE to
+ # redefine the `scopes` method before it is used in the `Identity` model.
+ module UniquenessScopes
+ def self.scopes
+ [:provider]
+ end
+ end
+end
diff --git a/app/models/instance_configuration.rb b/app/models/instance_configuration.rb
index 7d8ce0bbd05..11289887e00 100644
--- a/app/models/instance_configuration.rb
+++ b/app/models/instance_configuration.rb
@@ -64,10 +64,10 @@ class InstanceConfiguration
end
def ssh_algorithm_md5(ssh_file_content)
- OpenSSL::Digest::MD5.hexdigest(ssh_file_content).scan(/../).join(':')
+ Gitlab::SSHPublicKey.new(ssh_file_content).fingerprint
end
def ssh_algorithm_sha256(ssh_file_content)
- OpenSSL::Digest::SHA256.hexdigest(ssh_file_content)
+ Gitlab::SSHPublicKey.new(ssh_file_content).fingerprint('SHA256')
end
end
diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb
index 4eb211eff61..e7168d49db9 100644
--- a/app/models/internal_id.rb
+++ b/app/models/internal_id.rb
@@ -111,7 +111,7 @@ class InternalId < ActiveRecord::Base
# Generates next internal id and returns it
def generate
- subject.transaction do
+ InternalId.transaction do
# Create a record in internal_ids if one does not yet exist
# and increment its last value
#
@@ -125,7 +125,7 @@ class InternalId < ActiveRecord::Base
#
# Note this will acquire a ROW SHARE lock on the InternalId record
def track_greatest(new_value)
- subject.transaction do
+ InternalId.transaction do
(lookup || create_record).track_greatest_and_save!(new_value)
end
end
@@ -148,7 +148,7 @@ class InternalId < ActiveRecord::Base
# violation. We can safely roll-back the nested transaction and perform
# a lookup instead to retrieve the record.
def create_record
- subject.transaction(requires_new: true) do
+ InternalId.transaction(requires_new: true) do
InternalId.create!(
**scope,
usage: usage_value,
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 94cf12f3c2b..0de5e434b02 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -170,39 +170,6 @@ class Issue < ActiveRecord::Base
"#{project.to_reference(from, full: full)}#{reference}"
end
- def referenced_merge_requests(current_user = nil)
- ext = all_references(current_user)
-
- notes_with_associations.each do |object|
- object.all_references(current_user, extractor: ext)
- end
-
- merge_requests = ext.merge_requests.sort_by(&:iid)
-
- cross_project_filter = -> (merge_requests) do
- merge_requests.select { |mr| mr.target_project == project }
- end
-
- Ability.merge_requests_readable_by_user(
- merge_requests, current_user,
- filters: {
- read_cross_project: cross_project_filter
- }
- )
- end
-
- # All branches containing the current issue's ID, except for
- # those with a merge request open referencing the current issue.
- def related_branches(current_user)
- branches_with_iid = project.repository.branch_names.select do |branch|
- branch =~ /\A#{iid}-(?!\d+-stable)/i
- end
-
- branches_with_merge_request = self.referenced_merge_requests(current_user).map(&:source_branch)
-
- branches_with_iid - branches_with_merge_request
- end
-
def suggested_branch_name
return to_branch_name unless project.repository.branch_exists?(to_branch_name)
@@ -225,26 +192,6 @@ class Issue < ActiveRecord::Base
project
end
- # From all notes on this issue, we'll select the system notes about linked
- # merge requests. Of those, the MRs closing `self` are returned.
- def closed_by_merge_requests(current_user = nil)
- return [] unless open?
-
- ext = all_references(current_user)
-
- notes.system.each do |note|
- note.all_references(current_user, extractor: ext)
- end
-
- merge_requests = ext.merge_requests.select(&:open?)
- if merge_requests.any?
- ids = MergeRequestsClosingIssues.where(merge_request_id: merge_requests.map(&:id), issue_id: id).pluck(:merge_request_id)
- merge_requests.select { |mr| mr.id.in?(ids) }
- else
- []
- end
- end
-
def moved?
!moved_to.nil?
end
@@ -293,7 +240,8 @@ class Issue < ActiveRecord::Base
reference_path: issue_reference,
real_path: url_helper.project_issue_path(project, self),
issue_sidebar_endpoint: url_helper.project_issue_path(project, self, format: :json, serializer: 'sidebar'),
- toggle_subscription_endpoint: url_helper.toggle_subscription_project_issue_path(project, self)
+ toggle_subscription_endpoint: url_helper.toggle_subscription_project_issue_path(project, self),
+ assignable_labels_endpoint: url_helper.project_labels_path(project, format: :json, include_ancestor_groups: true)
)
end
@@ -315,9 +263,11 @@ class Issue < ActiveRecord::Base
true
end
+ # rubocop: disable CodeReuse/ServiceClass
def update_project_counter_caches
Projects::OpenIssuesCountService.new(project).refresh_cache
end
+ # rubocop: enable CodeReuse/ServiceClass
private
diff --git a/app/models/key.rb b/app/models/key.rb
index 3bb0d2f6f9c..8f93418b88b 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -34,6 +34,10 @@ class Key < ActiveRecord::Base
after_destroy :post_destroy_hook
after_destroy :refresh_user_cache
+ def self.regular_keys
+ where(type: ['Key', nil])
+ end
+
def key=(value)
write_attribute(:key, value.present? ? Gitlab::SSHPublicKey.sanitize(value) : nil)
@@ -55,9 +59,11 @@ class Key < ActiveRecord::Base
"key-#{id}"
end
+ # rubocop: disable CodeReuse/ServiceClass
def update_last_used_at
Keys::LastUsedService.new(self).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
def add_to_shell
GitlabShellWorker.perform_async(
@@ -67,9 +73,11 @@ class Key < ActiveRecord::Base
)
end
+ # rubocop: disable CodeReuse/ServiceClass
def post_create_hook
SystemHooksService.new.execute_hooks_for(self, :create)
end
+ # rubocop: enable CodeReuse/ServiceClass
def remove_from_shell
GitlabShellWorker.perform_async(
@@ -79,15 +87,19 @@ class Key < ActiveRecord::Base
)
end
+ # rubocop: disable CodeReuse/ServiceClass
def refresh_user_cache
return unless user
Users::KeysCountService.new(user).refresh_cache
end
+ # rubocop: enable CodeReuse/ServiceClass
+ # rubocop: disable CodeReuse/ServiceClass
def post_destroy_hook
SystemHooksService.new.execute_hooks_for(self, :destroy)
end
+ # rubocop: enable CodeReuse/ServiceClass
def public_key
@public_key ||= Gitlab::SSHPublicKey.new(key)
diff --git a/app/models/label.rb b/app/models/label.rb
index 96c1515b41a..165e4a8f3e5 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -5,6 +5,9 @@ class Label < ActiveRecord::Base
include Referable
include Subscribable
include Gitlab::SQL::Pattern
+ include OptionallySearch
+ include Sortable
+ include FromUnion
# Represents a "No Label" state used for filtering Issues and Merge
# Requests that have no label assigned.
@@ -38,8 +41,11 @@ class Label < ActiveRecord::Base
scope :templates, -> { where(template: true) }
scope :with_title, ->(title) { where(title: title) }
scope :with_lists_and_board, -> { joins(lists: :board).merge(List.movable) }
- scope :on_group_boards, ->(group_id) { with_lists_and_board.where(boards: { group_id: group_id }) }
scope :on_project_boards, ->(project_id) { with_lists_and_board.where(boards: { project_id: project_id }) }
+ scope :on_board, ->(board_id) { with_lists_and_board.where(boards: { id: board_id }) }
+ scope :order_name_asc, -> { reorder(title: :asc) }
+ scope :order_name_desc, -> { reorder(title: :desc) }
+ scope :subscribed_by, ->(user_id) { joins(:subscriptions).where(subscriptions: { user_id: user_id, subscribed: true }) }
def self.prioritized(project)
joins(:priorities)
@@ -69,6 +75,14 @@ class Label < ActiveRecord::Base
joins(label_priorities)
end
+ def self.optionally_subscribed_by(user_id)
+ if user_id
+ subscribed_by(user_id)
+ else
+ all
+ end
+ end
+
alias_attribute :name, :title
def self.reference_prefix
diff --git a/app/models/label_link.rb b/app/models/label_link.rb
index 779657b25d5..1d93a55e8e9 100644
--- a/app/models/label_link.rb
+++ b/app/models/label_link.rb
@@ -3,7 +3,7 @@
class LabelLink < ActiveRecord::Base
include Importable
- belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
+ belongs_to :target, polymorphic: true, inverse_of: :label_links # rubocop:disable Cop/PolymorphicAssociations
belongs_to :label
validates :target, presence: true, unless: :importing?
diff --git a/app/models/label_note.rb b/app/models/label_note.rb
new file mode 100644
index 00000000000..680952cf421
--- /dev/null
+++ b/app/models/label_note.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+class LabelNote < Note
+ attr_accessor :resource_parent
+ attr_reader :events
+
+ def self.from_events(events, resource: nil, resource_parent: nil)
+ resource ||= events.first.issuable
+
+ attrs = {
+ system: true,
+ author: events.first.user,
+ created_at: events.first.created_at,
+ discussion_id: events.first.discussion_id,
+ noteable: resource,
+ system_note_metadata: SystemNoteMetadata.new(action: 'label'),
+ events: events,
+ resource_parent: resource_parent
+ }
+
+ if resource_parent.is_a?(Project)
+ attrs[:project_id] = resource_parent.id
+ end
+
+ LabelNote.new(attrs)
+ end
+
+ def events=(events)
+ @events = events
+
+ update_outdated_markdown
+ end
+
+ def cached_html_up_to_date?(markdown_field)
+ true
+ end
+
+ def note
+ @note ||= note_text
+ end
+
+ def note_html
+ @note_html ||= "<p dir=\"auto\">#{note_text(html: true)}</p>"
+ end
+
+ def project
+ resource_parent if resource_parent.is_a?(Project)
+ end
+
+ def group
+ resource_parent if resource_parent.is_a?(Group)
+ end
+
+ private
+
+ def update_outdated_markdown
+ events.each do |event|
+ if event.outdated_markdown?
+ event.refresh_invalid_reference
+ end
+ end
+ end
+
+ def note_text(html: false)
+ added = labels_str('added', label_refs_by_action('add', html))
+ removed = labels_str('removed', label_refs_by_action('remove', html))
+
+ [added, removed].compact.join(' and ')
+ end
+
+ # returns string containing added/removed labels including
+ # count of deleted labels:
+ #
+ # added ~1 ~2 + 1 deleted label
+ # added 3 deleted labels
+ # added ~1 ~2 labels
+ def labels_str(prefix, label_refs)
+ existing_refs = label_refs.select { |ref| ref.present? }.sort
+ refs_str = existing_refs.empty? ? nil : existing_refs.join(' ')
+
+ deleted = label_refs.count - existing_refs.count
+ deleted_str = deleted == 0 ? nil : "#{deleted} deleted"
+
+ return nil unless refs_str || deleted_str
+
+ label_list_str = [refs_str, deleted_str].compact.join(' + ')
+ suffix = 'label'.pluralize(deleted > 0 ? deleted : existing_refs.count)
+
+ "#{prefix} #{label_list_str} #{suffix}"
+ end
+
+ def label_refs_by_action(action, html)
+ field = html ? :reference_html : :reference
+
+ events.select { |e| e.action == action }.map(&field)
+ end
+end
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 20f9b18e4ca..00dec6bb92b 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -20,11 +20,7 @@ class LegacyDiffNote < Note
end
def project_repository
- if RequestStore.active?
- RequestStore.fetch("project:#{project_id}:repository") { self.project.repository }
- else
- self.project.repository
- end
+ Gitlab::SafeRequestStore.fetch("project:#{project_id}:repository") { self.project.repository }
end
def diff_file_hash
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index 2a1a4ef48b7..69c563545bb 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -7,7 +7,7 @@ class LfsObject < ActiveRecord::Base
has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :lfs_objects_projects
- scope :with_files_stored_locally, -> { where(file_store: [nil, LfsObjectUploader::Store::LOCAL]) }
+ scope :with_files_stored_locally, -> { where(file_store: LfsObjectUploader::Store::LOCAL) }
validates :oid, presence: true, uniqueness: true
@@ -26,14 +26,16 @@ class LfsObject < ActiveRecord::Base
end
def local_store?
- [nil, LfsObjectUploader::Store::LOCAL].include?(self.file_store)
+ file_store == LfsObjectUploader::Store::LOCAL
end
+ # rubocop: disable DestroyAll
def self.destroy_unreferenced
joins("LEFT JOIN lfs_objects_projects ON lfs_objects_projects.lfs_object_id = #{table_name}.id")
.where(lfs_objects_projects: { id: nil })
.destroy_all
end
+ # rubocop: enable DestroyAll
def self.calculate_oid(path)
Digest::SHA256.file(path).hexdigest
diff --git a/app/models/license_template.rb b/app/models/license_template.rb
new file mode 100644
index 00000000000..73e403f98b4
--- /dev/null
+++ b/app/models/license_template.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+class LicenseTemplate
+ PROJECT_TEMPLATE_REGEX =
+ %r{[\<\{\[]
+ (project|description|
+ one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
+ [\>\}\]]}xi.freeze
+ YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
+ FULLNAME_TEMPLATE_REGEX =
+ %r{[\<\{\[]
+ (fullname|name\sof\s(author|copyright\sowner))
+ [\>\}\]]}xi.freeze
+
+ attr_reader :key, :name, :category, :nickname, :url, :meta
+
+ def initialize(key:, name:, category:, content:, nickname: nil, url: nil, meta: {})
+ @key = key
+ @name = name
+ @category = category
+ @content = content
+ @nickname = nickname
+ @url = url
+ @meta = meta
+ end
+
+ def popular?
+ category == :Popular
+ end
+ alias_method :featured?, :popular?
+
+ # Returns the text of the license
+ def content
+ if @content.respond_to?(:call)
+ @content = @content.call
+ else
+ @content
+ end
+ end
+
+ # Populate placeholders in the LicenseTemplate content
+ def resolve!(project_name: nil, fullname: nil, year: Time.now.year.to_s)
+ # Ensure the string isn't shared with any other instance of LicenseTemplate
+ new_content = content.dup
+ new_content.gsub!(YEAR_TEMPLATE_REGEX, year) if year.present?
+ new_content.gsub!(PROJECT_TEMPLATE_REGEX, project_name) if project_name.present?
+ new_content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname.present?
+
+ @content = new_content
+
+ self
+ end
+end
diff --git a/app/models/list.rb b/app/models/list.rb
index 1a30acc83cf..029685be927 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -15,6 +15,7 @@ class List < ActiveRecord::Base
scope :destroyable, -> { where(list_type: list_types.slice(*destroyable_types).values) }
scope :movable, -> { where(list_type: list_types.slice(*movable_types).values) }
+ scope :preload_associations, -> { preload(:board, :label) }
class << self
def destroyable_types
diff --git a/app/models/member.rb b/app/models/member.rb
index 05c0bc8cb97..bc8ac14d148 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -103,7 +103,7 @@ class Member < ActiveRecord::Base
def filter_by_2fa(value)
case value
when 'enabled'
- left_join_users.merge(User.with_two_factor_indistinct)
+ left_join_users.merge(User.with_two_factor)
when 'disabled'
left_join_users.merge(User.without_two_factor)
else
@@ -145,17 +145,20 @@ class Member < ActiveRecord::Base
end
def add_user(source, user, access_level, existing_members: nil, current_user: nil, expires_at: nil, ldap: false)
+ # rubocop: disable CodeReuse/ServiceClass
# `user` can be either a User object, User ID or an email to be invited
member = retrieve_member(source, user, existing_members)
access_level = retrieve_access_level(access_level)
return member unless can_update_member?(current_user, member)
- member.attributes = {
- created_by: member.created_by || current_user,
- access_level: access_level,
- expires_at: expires_at
- }
+ set_member_attributes(
+ member,
+ access_level,
+ current_user: current_user,
+ expires_at: expires_at,
+ ldap: ldap
+ )
if member.request?
::Members::ApproveAccessRequestService.new(
@@ -171,6 +174,19 @@ class Member < ActiveRecord::Base
end
member
+ # rubocop: enable CodeReuse/ServiceClass
+ end
+
+ # Populates the attributes of a member.
+ #
+ # This logic resides in a separate method so that EE can extend this logic,
+ # without having to patch the `add_user` method directly.
+ def set_member_attributes(member, access_level, current_user: nil, expires_at: nil, ldap: false)
+ member.attributes = {
+ created_by: member.created_by || current_user,
+ access_level: access_level,
+ expires_at: expires_at
+ }
end
def add_users(source, users, access_level, current_user: nil, expires_at: nil)
@@ -339,12 +355,14 @@ class Member < ActiveRecord::Base
@notification_setting ||= user&.notification_settings_for(source)
end
+ # rubocop: disable CodeReuse/ServiceClass
def notifiable?(type, opts = {})
# always notify when there isn't a user yet
return true if user.blank?
NotificationRecipientService.notifiable?(user, type, notifiable_options.merge(opts))
end
+ # rubocop: enable CodeReuse/ServiceClass
private
@@ -374,6 +392,7 @@ class Member < ActiveRecord::Base
# in a transaction. Doing so can lead to the job running before the
# transaction has been committed, resulting in the job either throwing an
# error or not doing any meaningful work.
+ # rubocop: disable CodeReuse/ServiceClass
def refresh_member_authorized_projects
# If user/source is being destroyed, project access are going to be
# destroyed eventually because of DB foreign keys, so we shouldn't bother
@@ -382,6 +401,7 @@ class Member < ActiveRecord::Base
UserProjectAccessChangedService.new(user_id).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
def after_accept_invite
post_create_hook
@@ -395,13 +415,17 @@ class Member < ActiveRecord::Base
post_create_hook
end
+ # rubocop: disable CodeReuse/ServiceClass
def system_hook_service
SystemHooksService.new
end
+ # rubocop: enable CodeReuse/ServiceClass
+ # rubocop: disable CodeReuse/ServiceClass
def notification_service
NotificationService.new
end
+ # rubocop: enable CodeReuse/ServiceClass
def notifiable_options
{}
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 0154fe5aeba..537f2a3a231 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -138,7 +138,9 @@ class ProjectMember < Member
super
end
+ # rubocop: disable CodeReuse/ServiceClass
def event_service
EventCreateService.new
end
+ # rubocop: enable CodeReuse/ServiceClass
end
diff --git a/app/models/members_preloader.rb b/app/models/members_preloader.rb
new file mode 100644
index 00000000000..33855191ca8
--- /dev/null
+++ b/app/models/members_preloader.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class MembersPreloader
+ attr_reader :members
+
+ def initialize(members)
+ @members = members
+ end
+
+ def preload_all
+ ActiveRecord::Associations::Preloader.new.preload(members, :user)
+ ActiveRecord::Associations::Preloader.new.preload(members, :source)
+ ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :status)
+ ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :u2f_registrations)
+ end
+end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 396647a14ae..92add079a02 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -6,6 +6,7 @@ class MergeRequest < ActiveRecord::Base
include Issuable
include Noteable
include Referable
+ include Presentable
include IgnorableColumn
include TimeTrackable
include ManualInverseAssociation
@@ -14,6 +15,7 @@ class MergeRequest < ActiveRecord::Base
include Gitlab::Utils::StrongMemoize
include LabelEventable
include ReactiveCaching
+ include FromUnion
self.reactive_cache_key = ->(model) { [model.project.id, model.iid] }
self.reactive_cache_refresh_interval = 10.minutes
@@ -137,12 +139,14 @@ class MergeRequest < ActiveRecord::Base
Gitlab::Timeless.timeless(merge_request, &block)
end
+ # rubocop: disable CodeReuse/ServiceClass
after_transition unchecked: :cannot_be_merged do |merge_request, transition|
if merge_request.notify_conflict?
NotificationService.new.merge_request_unmergeable(merge_request)
TodoService.new.merge_request_became_unmergeable(merge_request)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def check_state?(merge_status)
[:unchecked, :cannot_be_merged_recheck].include?(merge_status.to_sym)
@@ -200,6 +204,12 @@ class MergeRequest < ActiveRecord::Base
head_pipeline&.sha == diff_head_sha ? head_pipeline : nil
end
+ def merge_pipeline
+ return unless merged?
+
+ target_project.pipeline_for(target_branch, merge_commit_sha)
+ end
+
# Pattern used to extract `!123` merge request references from text
#
# This pattern supports cross-project references.
@@ -235,11 +245,10 @@ class MergeRequest < ActiveRecord::Base
def self.in_projects(relation)
# unscoping unnecessary conditions that'll be applied
# when executing `where("merge_requests.id IN (#{union.to_sql})")`
- source = unscoped.where(source_project_id: relation).select(:id)
- target = unscoped.where(target_project_id: relation).select(:id)
- union = Gitlab::SQL::Union.new([source, target])
+ source = unscoped.where(source_project_id: relation)
+ target = unscoped.where(target_project_id: relation)
- where("merge_requests.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
+ from_union([source, target])
end
# This is used after project import, to reset the IDs to the correct
@@ -258,7 +267,7 @@ class MergeRequest < ActiveRecord::Base
end
end
- WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
+ WIP_REGEX = /\A*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
def self.work_in_progress?(title)
!!(title =~ WIP_REGEX)
@@ -344,6 +353,15 @@ class MergeRequest < ActiveRecord::Base
end
end
+ # Returns true if there are commits that match at least one commit SHA.
+ def includes_any_commits?(shas)
+ if persisted?
+ merge_request_diff.commits_by_shas(shas).exists?
+ else
+ (commit_shas & shas).present?
+ end
+ end
+
# Calls `MergeWorker` to proceed with the merge process and
# updates `merge_jid` with the MergeWorker#jid.
# This helps tracking enqueued and ongoing merge jobs.
@@ -391,6 +409,18 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff&.real_size || diffs.real_size
end
+ def modified_paths(past_merge_request_diff: nil)
+ diffs = if past_merge_request_diff
+ past_merge_request_diff
+ elsif compare
+ compare
+ else
+ self.merge_request_diff
+ end
+
+ diffs.modified_paths
+ end
+
def diff_base_commit
if persisted?
merge_request_diff.base_commit
@@ -623,11 +653,13 @@ class MergeRequest < ActiveRecord::Base
end
end
+ # rubocop: disable CodeReuse/ServiceClass
def reload_diff(current_user = nil)
return unless open?
MergeRequests::ReloadDiffsService.new(self, current_user).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
def check_if_can_be_merged
return unless self.class.state_machines[:merge_status].check_state?(merge_status) && Gitlab::Database.read_write?
@@ -736,11 +768,8 @@ class MergeRequest < ActiveRecord::Base
# compared to using OR statements. We're using UNION ALL since the queries
# used won't produce any duplicates (e.g. a note for a commit can't also be
# a note for an MR).
- union = Gitlab::SQL::Union
- .new([notes, commit_notes], remove_duplicates: false)
- .to_sql
-
- Note.from("(#{union}) #{Note.table_name}")
+ Note
+ .from_union([notes, commit_notes], remove_duplicates: false)
.includes(:noteable)
end
@@ -937,7 +966,6 @@ class MergeRequest < ActiveRecord::Base
def mergeable_ci_state?
return true unless project.only_allow_merge_if_pipeline_succeeds?
- return true unless head_pipeline
actual_head_pipeline&.success? || actual_head_pipeline&.skipped?
end
@@ -1036,6 +1064,7 @@ class MergeRequest < ActiveRecord::Base
actual_head_pipeline&.has_test_reports?
end
+ # rubocop: disable CodeReuse/ServiceClass
def compare_test_reports
unless has_test_reports?
return { status: :error, status_reason: 'This merge request does not have test reports' }
@@ -1050,7 +1079,9 @@ class MergeRequest < ActiveRecord::Base
data
end || { status: :parsing }
end
+ # rubocop: enable CodeReuse/ServiceClass
+ # rubocop: disable CodeReuse/ServiceClass
def calculate_reactive_cache(identifier, *args)
case identifier.to_sym
when :compare_test_results
@@ -1060,6 +1091,7 @@ class MergeRequest < ActiveRecord::Base
raise NotImplementedError, "Unknown identifier: #{identifier}"
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def all_commits
# MySQL doesn't support LIMIT in a subquery.
@@ -1125,6 +1157,7 @@ class MergeRequest < ActiveRecord::Base
diff_refs && diff_refs.complete?
end
+ # rubocop: disable CodeReuse/ServiceClass
def update_diff_discussion_positions(old_diff_refs:, new_diff_refs:, current_user: nil)
return unless has_complete_diff_refs?
return if new_diff_refs == old_diff_refs
@@ -1154,6 +1187,7 @@ class MergeRequest < ActiveRecord::Base
.execute(self)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def keep_around_commit
project.repository.keep_around(self.merge_commit_sha)
@@ -1189,9 +1223,11 @@ class MergeRequest < ActiveRecord::Base
true
end
+ # rubocop: disable CodeReuse/ServiceClass
def update_project_counter_caches
Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache
end
+ # rubocop: enable CodeReuse/ServiceClass
def first_contribution?
return false if project.team.max_member_access(author_id) > Gitlab::Access::GUEST
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index d9393b4e545..6f1beede6f9 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -6,6 +6,7 @@ class MergeRequestDiff < ActiveRecord::Base
include ManualInverseAssociation
include IgnorableColumn
include EachBatch
+ include Gitlab::Utils::StrongMemoize
# Don't display more than 100 commits at once
COMMITS_SAFE_SIZE = 100
@@ -140,6 +141,12 @@ class MergeRequestDiff < ActiveRecord::Base
merge_request_diff_commits.map(&:sha)
end
+ def commits_by_shas(shas)
+ return MergeRequestDiffCommit.none unless shas.present?
+
+ merge_request_diff_commits.where(sha: shas)
+ end
+
def diff_refs=(new_diff_refs)
self.base_commit_sha = new_diff_refs&.base_sha
self.start_commit_sha = new_diff_refs&.start_sha
@@ -219,12 +226,20 @@ class MergeRequestDiff < ActiveRecord::Base
self.id == merge_request.latest_merge_request_diff_id
end
+ # rubocop: disable CodeReuse/ServiceClass
def compare_with(sha)
# When compare merge request versions we want diff A..B instead of A...B
# so we handle cases when user does squash and rebase of the commits between versions.
# For this reason we set straight to true by default.
CompareService.new(project, head_commit_sha).execute(project, sha, straight: true)
end
+ # rubocop: enable CodeReuse/ServiceClass
+
+ def modified_paths
+ strong_memoize(:modified_paths) do
+ merge_request_diff_files.pluck(:new_path, :old_path).flatten.uniq
+ end
+ end
private
@@ -314,9 +329,7 @@ class MergeRequestDiff < ActiveRecord::Base
def keep_around_commits
[repository, merge_request.source_project.repository].uniq.each do |repo|
- repo.keep_around(start_commit_sha)
- repo.keep_around(head_commit_sha)
- repo.keep_around(base_commit_sha)
+ repo.keep_around(start_commit_sha, head_commit_sha, base_commit_sha)
end
end
end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index cb1def1b422..3cc8e2c44bb 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -46,6 +46,9 @@ class Milestone < ActiveRecord::Base
where(conditions.reduce(:or))
end
+ scope :order_by_name_asc, -> { order(Arel::Nodes::Ascending.new(arel_table[:title].lower)) }
+ scope :reorder_by_due_date_asc, -> { reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC')) }
+
validates :group, presence: true, unless: :project
validates :project, presence: true, unless: :group
@@ -142,14 +145,14 @@ class Milestone < ActiveRecord::Base
end
def participants
- User.joins(assigned_issues: :milestone).where("milestones.id = ?", id).uniq
+ User.joins(assigned_issues: :milestone).where("milestones.id = ?", id).distinct
end
def self.sort_by_attribute(method)
sorted =
case method.to_s
when 'due_date_asc'
- reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC'))
+ reorder_by_due_date_asc
when 'due_date_desc'
reorder(Gitlab::Database.nulls_last_order('due_date', 'DESC'))
when 'name_asc'
@@ -167,6 +170,22 @@ class Milestone < ActiveRecord::Base
sorted.with_order_id_desc
end
+ def self.states_count(projects, groups = nil)
+ return STATE_COUNT_HASH unless projects || groups
+
+ counts = Milestone
+ .for_projects_and_groups(projects&.map(&:id), groups&.map(&:id))
+ .reorder(nil)
+ .group(:state)
+ .count
+
+ {
+ opened: counts['active'] || 0,
+ closed: counts['closed'] || 0,
+ all: counts.values.sum
+ }
+ end
+
##
# Returns the String necessary to reference this Milestone in Markdown. Group
# milestones only support name references, and do not support cross-project
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index b974309aeb6..4a6627d3ca1 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -10,6 +10,8 @@ class Namespace < ActiveRecord::Base
include Storage::LegacyNamespace
include Gitlab::SQL::Pattern
include IgnorableColumn
+ include FeatureGate
+ include FromUnion
ignore_column :deleted_at
@@ -81,7 +83,7 @@ class Namespace < ActiveRecord::Base
find_by('lower(path) = :value', value: path.downcase)
end
- # Case insensetive search for namespace by path or name
+ # Case insensitive search for namespace by path or name
def find_by_path_or_name(path)
find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
end
@@ -124,7 +126,6 @@ class Namespace < ActiveRecord::Base
def to_param
full_path
end
- alias_method :flipper_id, :to_param
def human_name
owner_name
@@ -134,6 +135,10 @@ class Namespace < ActiveRecord::Base
all_projects.any?(&:has_container_registry_tags?)
end
+ def first_project_with_container_registry_tags
+ all_projects.find(&:has_container_registry_tags?)
+ end
+
def send_update_instructions
projects.each do |project|
project.send_move_instructions("#{full_path_was}/#{project.path}")
@@ -147,8 +152,8 @@ class Namespace < ActiveRecord::Base
def find_fork_of(project)
return nil unless project.fork_network
- if RequestStore.active?
- forks_in_namespace = RequestStore.fetch("namespaces:#{id}:forked_projects") do
+ if Gitlab::SafeRequestStore.active?
+ forks_in_namespace = Gitlab::SafeRequestStore.fetch("namespaces:#{id}:forked_projects") do
Hash.new do |found_forks, project|
found_forks[project] = project.fork_network.find_forks_in(projects).first
end
@@ -227,6 +232,12 @@ class Namespace < ActiveRecord::Base
Project.inside_path(full_path)
end
+ # Includes pipelines from this namespace and pipelines from all subgroups
+ # that belongs to this namespace
+ def all_pipelines
+ Ci::Pipeline.where(project: all_projects)
+ end
+
def has_parent?
parent.present?
end
@@ -253,18 +264,6 @@ class Namespace < ActiveRecord::Base
end
end
- # Exports belonging to projects with legacy storage are placed in a common
- # subdirectory of the namespace, so a simple `rm -rf` is sufficient to remove
- # them.
- #
- # Exports of projects using hashed storage are placed in a location defined
- # only by the project ID, so each must be removed individually.
- def remove_exports!
- remove_legacy_exports!
-
- all_projects.with_storage_feature(:repository).find_each(&:remove_exports)
- end
-
def refresh_project_authorizations
owner.refresh_authorized_projects
end
diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb
index 6c5a4c56377..1b2369aab18 100644
--- a/app/models/network/commit.rb
+++ b/app/models/network/commit.rb
@@ -18,7 +18,7 @@ module Network
end
def space
- if @spaces.size > 0
+ if @spaces.present?
@spaces.first
else
0
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 1431dfefc55..6da3bb7bfb7 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -81,7 +81,7 @@ module Network
skip = 0
while offset == -1
tmp_commits = find_commits(skip)
- if tmp_commits.size > 0
+ if tmp_commits.present?
index = tmp_commits.index do |c|
c.id == @commit.id
end
@@ -218,7 +218,7 @@ module Network
def get_space_base(leaves)
space_base = 1
parents = leaves.last.parents(@map)
- if parents.size > 0
+ if parents.present?
if parents.first.space > 0
space_base = parents.first.space
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 2e343b8f9f8..592efb714f3 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -10,6 +10,7 @@ class Note < ActiveRecord::Base
include Awardable
include Importable
include FasterCacheKeys
+ include Redactable
include CacheMarkdownField
include AfterCommitQueue
include ResolvableNote
@@ -17,6 +18,7 @@ class Note < ActiveRecord::Base
include Editable
include Gitlab::SQL::Pattern
include ThrottledTouch
+ include FromUnion
module SpecialRole
FIRST_TIME_CONTRIBUTOR = :first_time_contributor
@@ -32,6 +34,8 @@ class Note < ActiveRecord::Base
cache_markdown_field :note, pipeline: :note, issuable_state_filter_enabled: true
+ redact_field :note
+
# Aliases to make application_helper#edited_time_ago_with_tooltip helper work properly with notes.
# See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10392/diffs#note_28719102
alias_attribute :last_edited_at, :updated_at
@@ -41,8 +45,10 @@ class Note < ActiveRecord::Base
# Banzai::ObjectRenderer.
attr_accessor :redacted_note_html
- # An Array containing the number of visible references as generated by
- # Banzai::ObjectRenderer
+ # Total of all references as generated by Banzai::ObjectRenderer
+ attr_accessor :total_reference_count
+
+ # Number of user visible references as generated by Banzai::ObjectRenderer
attr_accessor :user_visible_reference_count
# Attribute used to store the attributes that have been changed by quick actions.
@@ -107,6 +113,17 @@ class Note < ActiveRecord::Base
:system_note_metadata, :note_diff_file)
end
+ scope :with_notes_filter, -> (notes_filter) do
+ case notes_filter
+ when UserPreference::NOTES_FILTERS[:only_comments]
+ user
+ when UserPreference::NOTES_FILTERS[:only_activity]
+ system
+ else
+ all
+ end
+ end
+
scope :diff_notes, -> { where(type: %w(LegacyDiffNote DiffNote)) }
scope :new_diff_notes, -> { where(type: 'DiffNote') }
scope :non_diff_notes, -> { where(type: ['Note', 'DiscussionNote', nil]) }
@@ -181,6 +198,7 @@ class Note < ActiveRecord::Base
end
end
+ # rubocop: disable CodeReuse/ServiceClass
def cross_reference?
return unless system?
@@ -190,6 +208,7 @@ class Note < ActiveRecord::Base
SystemNoteService.cross_reference?(note)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def diff_note?
false
@@ -285,15 +304,7 @@ class Note < ActiveRecord::Base
end
def cross_reference_not_visible_for?(user)
- cross_reference? && !has_referenced_mentionables?(user)
- end
-
- def has_referenced_mentionables?(user)
- if user_visible_reference_count.present?
- user_visible_reference_count > 0
- else
- referenced_mentionables(user).any?
- end
+ cross_reference? && !all_referenced_mentionables_allowed?(user)
end
def award_emoji?
@@ -389,18 +400,7 @@ class Note < ActiveRecord::Base
end
def expire_etag_cache
- return unless noteable&.discussions_rendered_on_frontend?
- return unless noteable&.etag_caching_enabled?
-
- Gitlab::EtagCaching::Store.new.touch(etag_key)
- end
-
- def etag_key
- Gitlab::Routing.url_helpers.project_noteable_notes_path(
- project,
- target_type: noteable_type.underscore,
- target_id: noteable_id
- )
+ noteable&.expire_note_etag_cache
end
def touch(*args)
@@ -474,9 +474,18 @@ class Note < ActiveRecord::Base
self.discussion_id ||= discussion_class.discussion_id(self)
end
+ def all_referenced_mentionables_allowed?(user)
+ if user_visible_reference_count.present? && total_reference_count.present?
+ # if they are not equal, then there are private/confidential references as well
+ user_visible_reference_count > 0 && user_visible_reference_count == total_reference_count
+ else
+ referenced_mentionables(user).any?
+ end
+ end
+
def force_cross_reference_regex_check?
return unless system?
- SystemNoteMetadata::TYPES_WITH_CROSS_REFERENCES.include?(system_note_metadata&.action)
+ system_note_metadata&.cross_reference_types&.include?(system_note_metadata&.action)
end
end
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 1df3a51a7fc..1600acfc575 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -45,6 +45,15 @@ class NotificationSetting < ActiveRecord::Base
:success_pipeline
].freeze
+ # Update unfound_translations.rb when events are changed
+ def self.email_events(source = nil)
+ EMAIL_EVENTS
+ end
+
+ def email_events
+ self.class.email_events(source)
+ end
+
EXCLUDED_PARTICIPATING_EVENTS = [
:success_pipeline
].freeze
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index 7739a3894d3..7a33ade826b 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -140,9 +140,11 @@ class PagesDomain < ActiveRecord::Base
self.verification_code = SecureRandom.hex(16)
end
+ # rubocop: disable CodeReuse/ServiceClass
def update_daemon
::Projects::UpdatePagesConfigurationService.new(project).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
def pages_config_changed?
project_id_changed? ||
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 207146479c0..73a58f2420e 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -3,7 +3,7 @@
class PersonalAccessToken < ActiveRecord::Base
include Expirable
include TokenAuthenticatable
- add_authentication_token_field :token
+ add_authentication_token_field :token, digest: true, fallback: true
REDIS_EXPIRY_TIME = 3.minutes
@@ -33,16 +33,22 @@ class PersonalAccessToken < ActiveRecord::Base
def self.redis_getdel(user_id)
Gitlab::Redis::SharedState.with do |redis|
- token = redis.get(redis_shared_state_key(user_id))
+ encrypted_token = redis.get(redis_shared_state_key(user_id))
redis.del(redis_shared_state_key(user_id))
- token
+ begin
+ Gitlab::CryptoHelper.aes256_gcm_decrypt(encrypted_token)
+ rescue => ex
+ logger.warn "Failed to decrypt PersonalAccessToken value stored in Redis for User ##{user_id}: #{ex.class}"
+ encrypted_token
+ end
end
end
def self.redis_store!(user_id, token)
+ encrypted_token = Gitlab::CryptoHelper.aes256_gcm_encrypt(token)
+
Gitlab::Redis::SharedState.with do |redis|
- redis.set(redis_shared_state_key(user_id), token, ex: REDIS_EXPIRY_TIME)
- token
+ redis.set(redis_shared_state_key(user_id), encrypted_token, ex: REDIS_EXPIRY_TIME)
end
end
diff --git a/app/models/pool_repository.rb b/app/models/pool_repository.rb
new file mode 100644
index 00000000000..8ef74539209
--- /dev/null
+++ b/app/models/pool_repository.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class PoolRepository < ActiveRecord::Base
+ POOL_PREFIX = '@pools'
+
+ belongs_to :shard
+ validates :shard, presence: true
+
+ # For now, only pool repositories are tracked in the database. However, we may
+ # want to add other repository types in the future
+ self.table_name = 'repositories'
+
+ has_many :pool_member_projects, class_name: 'Project', foreign_key: :pool_repository_id
+
+ def shard_name
+ shard&.name
+ end
+
+ def shard_name=(name)
+ self.shard = Shard.by_name(name)
+ end
+end
diff --git a/app/models/postgresql/replication_slot.rb b/app/models/postgresql/replication_slot.rb
index 70c7432e6b5..e264fe88e47 100644
--- a/app/models/postgresql/replication_slot.rb
+++ b/app/models/postgresql/replication_slot.rb
@@ -4,6 +4,15 @@ module Postgresql
class ReplicationSlot < ActiveRecord::Base
self.table_name = 'pg_replication_slots'
+ # Returns true if there are any replication slots in use.
+ # PostgreSQL-compatible databases such as Aurora don't support
+ # replication slots, so this will return false as well.
+ def self.in_use?
+ transaction { exists? }
+ rescue ActiveRecord::StatementInvalid
+ false
+ end
+
# Returns true if the lag observed across all replication slots exceeds a
# given threshold.
#
@@ -11,6 +20,8 @@ module Postgresql
# statistics it takes between 1 and 5 seconds to replicate around
# 100 MB of data.
def self.lag_too_great?(max = 100.megabytes)
+ return false unless in_use?
+
lag_function = "#{Gitlab::Database.pg_wal_lsn_diff}" \
"(#{Gitlab::Database.pg_current_wal_insert_lsn}(), restart_lsn)::bigint"
diff --git a/app/models/project.rb b/app/models/project.rb
index 7735f23cb9e..4d1917b9ab2 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -27,6 +27,9 @@ class Project < ActiveRecord::Base
include FastDestroyAll::Helpers
include WithUploads
include BatchDestroyDependentAssociations
+ include FeatureGate
+ include OptionallySearch
+ include FromUnion
extend Gitlab::Cache::RequestCache
extend Gitlab::ConfigHelper
@@ -46,14 +49,17 @@ class Project < ActiveRecord::Base
attachments: 2
}.freeze
- # Valids ports to import from
- VALID_IMPORT_PORTS = [22, 80, 443].freeze
+ VALID_IMPORT_PORTS = [80, 443].freeze
+ VALID_IMPORT_PROTOCOLS = %w(http https git).freeze
+
+ VALID_MIRROR_PORTS = [22, 80, 443].freeze
+ VALID_MIRROR_PROTOCOLS = %w(http https ssh git).freeze
cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
- :merge_requests_enabled?, :issues_enabled?, to: :project_feature,
- allow_nil: true
+ :merge_requests_enabled?, :issues_enabled?, :pages_enabled?, :public_pages?,
+ to: :project_feature, allow_nil: true
delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage
@@ -83,15 +89,13 @@ class Project < ActiveRecord::Base
after_create :create_project_feature, unless: :project_feature
after_create -> { SiteStatistic.track(STATISTICS_ATTRIBUTE) }
- before_destroy ->(project) { project.project_feature.untrack_statistics_for_deletion! }
- after_destroy -> { SiteStatistic.untrack(STATISTICS_ATTRIBUTE) }
+ before_destroy -> { SiteStatistic.untrack(STATISTICS_ATTRIBUTE) }
after_create :create_ci_cd_settings,
unless: :ci_cd_settings,
if: proc { ProjectCiCdSetting.available? }
- after_create :set_last_activity_at
- after_create :set_last_repository_updated_at
+ after_create :set_timestamps_for_create
after_update :update_forks_visibility_level
before_destroy :remove_private_deploy_keys
@@ -109,7 +113,7 @@ class Project < ActiveRecord::Base
after_create :ensure_storage_path_exists
after_save :ensure_storage_path_exists, if: :namespace_id_changed?
- acts_as_taggable
+ acts_as_ordered_taggable
attr_accessor :old_path_with_namespace
attr_accessor :template_name
@@ -119,6 +123,7 @@ class Project < ActiveRecord::Base
alias_attribute :title, :name
# Relations
+ belongs_to :pool_repository
belongs_to :creator, class_name: 'User'
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
belongs_to :namespace
@@ -130,6 +135,7 @@ class Project < ActiveRecord::Base
# Project services
has_one :campfire_service
+ has_one :discord_service
has_one :drone_ci_service
has_one :emails_on_push_service
has_one :pipelines_email_service
@@ -139,7 +145,6 @@ class Project < ActiveRecord::Base
has_one :flowdock_service
has_one :assembla_service
has_one :asana_service
- has_one :gemnasium_service
has_one :mattermost_slash_commands_service
has_one :mattermost_service
has_one :slack_slash_commands_service
@@ -163,26 +168,21 @@ class Project < ActiveRecord::Base
has_one :packagist_service
has_one :hangouts_chat_service
- # TODO: replace these relations with the fork network versions
- has_one :forked_project_link, foreign_key: "forked_to_project_id"
- has_one :forked_from_project, through: :forked_project_link
-
- has_many :forked_project_links, foreign_key: "forked_from_project_id"
- has_many :forks, through: :forked_project_links, source: :forked_to_project
- # TODO: replace these relations with the fork network versions
-
has_one :root_of_fork_network,
foreign_key: 'root_project_id',
inverse_of: :root_project,
class_name: 'ForkNetwork'
has_one :fork_network_member
has_one :fork_network, through: :fork_network_member
+ has_one :forked_from_project, through: :fork_network_member
+ has_many :forked_to_members, class_name: 'ForkNetworkMember', foreign_key: 'forked_from_project_id'
+ has_many :forks, through: :forked_to_members, source: :project, inverse_of: :forked_from_project
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
# Merge Requests for target project should be removed with it
- has_many :merge_requests, foreign_key: 'target_project_id'
+ has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
has_many :source_of_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
has_many :issues
has_many :labels, class_name: 'ProjectLabel'
@@ -232,6 +232,8 @@ class Project < ActiveRecord::Base
has_many :clusters, through: :cluster_project, class_name: 'Clusters::Cluster'
has_many :cluster_ingresses, through: :clusters, source: :application_ingress, class_name: 'Clusters::Applications::Ingress'
+ has_many :prometheus_metrics
+
# Container repositories need to remove data from the container registry,
# which is not managed by the DB. Hence we're still using dependent: :destroy
# here.
@@ -253,7 +255,7 @@ class Project < ActiveRecord::Base
has_many :variables, class_name: 'Ci::Variable'
has_many :triggers, class_name: 'Ci::Trigger'
has_many :environments
- has_many :deployments
+ has_many :deployments, -> { success }
has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule'
has_many :project_deploy_tokens
has_many :deploy_tokens, through: :project_deploy_tokens
@@ -302,10 +304,10 @@ class Project < ActiveRecord::Base
validates :namespace, presence: true
validates :name, uniqueness: { scope: :namespace_id }
- validates :import_url, url: { protocols: %w(http https ssh git),
+ validates :import_url, url: { protocols: ->(project) { project.persisted? ? VALID_MIRROR_PROTOCOLS : VALID_IMPORT_PROTOCOLS },
+ ports: ->(project) { project.persisted? ? VALID_MIRROR_PORTS : VALID_IMPORT_PORTS },
allow_localhost: false,
- enforce_user: true,
- ports: VALID_IMPORT_PORTS }, if: [:external_import?, :import_url_changed?]
+ enforce_user: true }, if: [:external_import?, :import_url_changed?]
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create
validate :check_repository_path_availability, on: :update, if: ->(project) { project.renamed? }
@@ -328,7 +330,7 @@ class Project < ActiveRecord::Base
# last_activity_at is throttled every minute, but last_repository_updated_at is updated with every push
scope :sorted_by_activity, -> { reorder("GREATEST(COALESCE(last_activity_at, '1970-01-01'), COALESCE(last_repository_updated_at, '1970-01-01')) DESC") }
- scope :sorted_by_stars, -> { reorder('projects.star_count DESC') }
+ scope :sorted_by_stars, -> { reorder(star_count: :desc) }
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
@@ -353,7 +355,7 @@ class Project < ActiveRecord::Base
# "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) {
access_level_attribute = ProjectFeature.access_level_attribute(feature)
- with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED] })
+ with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED, ProjectFeature::PUBLIC] })
}
# Picks a feature where the level is exactly that given.
@@ -383,6 +385,33 @@ class Project < ActiveRecord::Base
only_integer: true,
message: 'needs to be beetween 10 minutes and 1 month' }
+ # Returns a project, if it is not about to be removed.
+ #
+ # id - The ID of the project to retrieve.
+ def self.find_without_deleted(id)
+ without_deleted.find_by_id(id)
+ end
+
+ # Paginates a collection using a `WHERE id < ?` condition.
+ #
+ # before - A project ID to use for filtering out projects with an equal or
+ # greater ID. If no ID is given, all projects are included.
+ #
+ # limit - The maximum number of rows to include.
+ def self.paginate_in_descending_order_using_id(
+ before: nil,
+ limit: Kaminari.config.default_per_page
+ )
+ relation = order_id_desc.limit(limit)
+ relation = relation.where('projects.id < ?', before) if before
+
+ relation
+ end
+
+ def self.eager_load_namespace_and_owner
+ includes(namespace: :owner)
+ end
+
# Returns a collection of projects that is either public or visible to the
# logged in user.
def self.public_or_visible_to_user(user = nil)
@@ -395,15 +424,15 @@ class Project < ActiveRecord::Base
end
end
- # project features may be "disabled", "internal" or "enabled". If "internal",
+ # project features may be "disabled", "internal", "enabled" or "public". If "internal",
# they are only available to team members. This scope returns projects where
- # the feature is either enabled, or internal with permission for the user.
+ # the feature is either public, enabled, or internal with permission for the user.
#
# This method uses an optimised version of `with_feature_access_level` for
# logged in users to more efficiently get private projects with the given
# feature.
def self.with_feature_available_for_user(feature, user)
- visible = [nil, ProjectFeature::ENABLED]
+ visible = [nil, ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
if user&.admin?
with_feature_enabled(feature)
@@ -427,6 +456,7 @@ class Project < ActiveRecord::Base
scope :joins_import_state, -> { joins("LEFT JOIN project_mirror_data import_state ON import_state.project_id = projects.id") }
scope :import_started, -> { joins_import_state.where("import_state.status = 'started' OR projects.import_status = 'started'") }
+ scope :for_group, -> (group) { where(group: group) }
class << self
# Searches for a list of projects based on the query given in `query`.
@@ -458,6 +488,8 @@ class Project < ActiveRecord::Base
reorder(last_activity_at: :desc)
when 'latest_activity_asc'
reorder(last_activity_at: :asc)
+ when 'stars_desc'
+ sorted_by_stars
else
order_by(method)
end
@@ -517,20 +549,23 @@ class Project < ActiveRecord::Base
self[:lfs_enabled] && Gitlab.config.lfs.enabled
end
+ alias_method :lfs_enabled, :lfs_enabled?
+
def auto_devops_enabled?
if auto_devops&.enabled.nil?
- Gitlab::CurrentSettings.auto_devops_enabled?
+ has_auto_devops_implicitly_enabled?
else
auto_devops.enabled?
end
end
def has_auto_devops_implicitly_enabled?
- auto_devops&.enabled.nil? && Gitlab::CurrentSettings.auto_devops_enabled?
+ auto_devops&.enabled.nil? &&
+ (Gitlab::CurrentSettings.auto_devops_enabled? || Feature.enabled?(:force_autodevops_on_by_default, self))
end
def has_auto_devops_implicitly_disabled?
- auto_devops&.enabled.nil? && !Gitlab::CurrentSettings.auto_devops_enabled?
+ auto_devops&.enabled.nil? && !(Gitlab::CurrentSettings.auto_devops_enabled? || Feature.enabled?(:force_autodevops_on_by_default, self))
end
def empty_repo?
@@ -546,7 +581,6 @@ class Project < ActiveRecord::Base
end
def cleanup
- @repository&.cleanup
@repository = nil
end
@@ -632,7 +666,7 @@ class Project < ActiveRecord::Base
remove_import_data
end
- # This method is overriden in EE::Project model
+ # This method is overridden in EE::Project model
def remove_import_data
import_data&.destroy
end
@@ -657,6 +691,8 @@ class Project < ActiveRecord::Base
else
super
end
+ rescue
+ super
end
def valid_import_url?
@@ -1057,31 +1093,13 @@ class Project < ActiveRecord::Base
end
def find_or_initialize_services(exceptions: [])
- services_templates = Service.where(template: true)
-
available_services_names = Service.available_services_names - exceptions
available_services = available_services_names.map do |service_name|
- service = find_service(services, service_name)
-
- if service
- service
- else
- # We should check if template for the service exists
- template = find_service(services_templates, service_name)
-
- if template.nil?
- # If no template, we should create an instance. Ex `build_gitlab_ci_service`
- public_send("build_#{service_name}_service") # rubocop:disable GitlabSecurity/PublicSend
- else
- Service.build_from_template(id, template)
- end
- end
+ find_or_initialize_service(service_name)
end
- available_services.reject do |service|
- disabled_services.include?(service.to_param)
- end
+ available_services.compact
end
def disabled_services
@@ -1089,15 +1107,30 @@ class Project < ActiveRecord::Base
end
def find_or_initialize_service(name)
- find_or_initialize_services.find { |service| service.to_param == name }
+ return if disabled_services.include?(name)
+
+ service = find_service(services, name)
+ return service if service
+
+ # We should check if template for the service exists
+ template = find_service(services_templates, name)
+
+ if template
+ Service.build_from_template(id, template)
+ else
+ # If no template, we should create an instance. Ex `build_gitlab_ci_service`
+ public_send("build_#{name}_service") # rubocop:disable GitlabSecurity/PublicSend
+ end
end
+ # rubocop: disable CodeReuse/ServiceClass
def create_labels
Label.templates.each do |label|
params = label.attributes.except('id', 'template', 'created_at', 'updated_at')
Labels::FindOrCreateService.new(nil, self, params).execute(skip_authorization: true)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def find_service(list, name)
list.find { |service| service.to_param == name }
@@ -1145,6 +1178,7 @@ class Project < ActiveRecord::Base
end
end
+ # rubocop: disable CodeReuse/ServiceClass
def send_move_instructions(old_path_with_namespace)
# New project path needs to be committed to the DB or notification will
# retrieve stale information
@@ -1152,6 +1186,7 @@ class Project < ActiveRecord::Base
NotificationService.new.project_was_moved(self, old_path_with_namespace)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def owner
if group
@@ -1161,15 +1196,16 @@ class Project < ActiveRecord::Base
end
end
+ # rubocop: disable CodeReuse/ServiceClass
def execute_hooks(data, hooks_scope = :push_hooks)
run_after_commit_or_now do
- hooks.hooks_for(hooks_scope).each do |hook|
+ hooks.hooks_for(hooks_scope).select_active(hooks_scope, data).each do |hook|
hook.async_execute(data, hooks_scope.to_s)
end
-
SystemHooksService.new.execute_hooks(data, hooks_scope)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def execute_services(data, hooks_scope = :push_hooks)
# Call only service hooks that are active for this scope
@@ -1214,12 +1250,7 @@ class Project < ActiveRecord::Base
end
def forked?
- return true if fork_network && fork_network.root_project != self
-
- # TODO: Use only the above conditional using the `fork_network`
- # This is the old conditional that looks at the `forked_project_link`, we
- # fall back to this while we're migrating the new models
- !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
+ fork_network && fork_network.root_project != self
end
def fork_source
@@ -1335,6 +1366,18 @@ class Project < ActiveRecord::Base
end
end
+ # Filters `users` to return only authorized users of the project
+ def members_among(users)
+ if users.is_a?(ActiveRecord::Relation) && !users.loaded?
+ authorized_users.merge(users)
+ else
+ return [] if users.empty?
+
+ user_ids = authorized_users.where(users: { id: users.map(&:id) }).pluck(:id)
+ users.select { |user| user_ids.include?(user.id) }
+ end
+ end
+
def default_branch
@default_branch ||= repository.root_ref if repository.exists?
end
@@ -1466,8 +1509,7 @@ class Project < ActiveRecord::Base
end
def all_runners
- union = Gitlab::SQL::Union.new([runners, group_runners, shared_runners])
- Ci::Runner.from("(#{union.to_sql}) ci_runners")
+ Ci::Runner.from_union([runners, group_runners, shared_runners])
end
def active_runners
@@ -1484,20 +1526,22 @@ class Project < ActiveRecord::Base
self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
end
+ # rubocop: disable CodeReuse/ServiceClass
def open_issues_count(current_user = nil)
Projects::OpenIssuesCountService.new(self, current_user).count
end
+ # rubocop: enable CodeReuse/ServiceClass
+ # rubocop: disable CodeReuse/ServiceClass
def open_merge_requests_count
Projects::OpenMergeRequestsCountService.new(self).count
end
+ # rubocop: enable CodeReuse/ServiceClass
def visibility_level_allowed_as_fork?(level = self.visibility_level)
return true unless forked?
- # self.forked_from_project will be nil before the project is saved, so
- # we need to go through the relation
- original_project = forked_project_link&.forked_from_project
+ original_project = fork_source
return true unless original_project
level <= original_project.visibility_level
@@ -1571,6 +1615,7 @@ class Project < ActiveRecord::Base
end
# TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal?
+ # rubocop: disable CodeReuse/ServiceClass
def remove_pages
# Projects with a missing namespace cannot have their pages removed
return unless namespace
@@ -1586,34 +1631,7 @@ class Project < ActiveRecord::Base
PagesWorker.perform_in(5.minutes, :remove, namespace.full_path, temp_path)
end
end
-
- def rename_repo
- path_before = previous_changes['path'].first
- full_path_before = full_path_was
- full_path_after = build_full_path
-
- Gitlab::AppLogger.info("Attempting to rename #{full_path_was} -> #{full_path_after}")
-
- if has_container_registry_tags?
- Gitlab::AppLogger.info("Project #{full_path_was} cannot be renamed because container registry tags are present!")
-
- # we currently don't support renaming repository if it contains images in container registry
- raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
- end
-
- expire_caches_before_rename(full_path_before)
-
- if rename_or_migrate_repository!
- Gitlab::AppLogger.info("Project was renamed: #{full_path_before} -> #{full_path_after}")
- after_rename_repository(full_path_before, path_before)
- else
- Gitlab::AppLogger.info("Repository could not be renamed: #{full_path_before} -> #{full_path_after}")
-
- # if we cannot move namespace directory we should rollback
- # db changes in order to prevent out of sync between db and fs
- raise StandardError.new('Repository cannot be renamed')
- end
- end
+ # rubocop: enable CodeReuse/ServiceClass
def write_repository_config(gl_full_path: full_path)
# We'd need to keep track of project full path otherwise directory tree
@@ -1646,6 +1664,7 @@ class Project < ActiveRecord::Base
end
end
+ # rubocop: disable CodeReuse/ServiceClass
def after_create_default_branch
return unless default_branch
@@ -1666,6 +1685,7 @@ class Project < ActiveRecord::Base
ProtectedBranches::CreateService.new(self, creator, params).execute(skip_authorization: true)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def remove_import_jid
return unless import_jid
@@ -1713,16 +1733,12 @@ class Project < ActiveRecord::Base
import_export_shared.archive_path
end
- def export_project_path
- Dir.glob("#{export_path}/*export.tar.gz").max_by { |f| File.ctime(f) }
- end
-
def export_status
if export_in_progress?
:started
elsif after_export_in_progress?
:after_export_action
- elsif export_project_path || export_project_object_exists?
+ elsif export_file_exists?
:finished
else
:none
@@ -1737,21 +1753,19 @@ class Project < ActiveRecord::Base
import_export_shared.after_export_in_progress?
end
- def remove_exports(path = export_path)
- if path.present?
- FileUtils.rm_rf(path)
- elsif export_project_object_exists?
- import_export_upload.remove_export_file!
- import_export_upload.save
- end
+ def remove_exports
+ return unless export_file_exists?
+
+ import_export_upload.remove_export_file!
+ import_export_upload.save unless import_export_upload.destroyed?
end
- def remove_exported_project_file
- remove_exports(export_project_path)
+ def export_file_exists?
+ export_file&.file
end
- def export_project_object_exists?
- Gitlab::ImportExport.object_storage? && import_export_upload&.export_file&.file
+ def export_file
+ import_export_upload&.export_file
end
def full_path_slug
@@ -1798,7 +1812,7 @@ class Project < ActiveRecord::Base
.first
end
- def secret_variables_for(ref:, environment: nil)
+ def ci_variables_for(ref:, environment: nil)
# EE would use the environment
if protected_for?(ref)
variables
@@ -1816,7 +1830,7 @@ class Project < ActiveRecord::Base
end
def deployment_variables(environment: nil)
- deployment_platform(environment: environment)&.predefined_variables || []
+ deployment_platform(environment: environment)&.predefined_variables(project: self) || []
end
def auto_devops_variables
@@ -1889,10 +1903,6 @@ class Project < ActiveRecord::Base
false
end
- def issue_board_milestone_available?(user = nil)
- feature_available?(:issue_board_milestone, user)
- end
-
def full_path_was
File.join(namespace.full_path, previous_changes['path'].first)
end
@@ -1902,9 +1912,11 @@ class Project < ActiveRecord::Base
# @deprecated cannot remove yet because it has an index with its name in elasticsearch
alias_method :path_with_namespace, :full_path
+ # rubocop: disable CodeReuse/ServiceClass
def forks_count
Projects::ForksCountService.new(self).count
end
+ # rubocop: enable CodeReuse/ServiceClass
def legacy_storage?
[nil, 0].include?(self.storage_version)
@@ -1952,7 +1964,7 @@ class Project < ActiveRecord::Base
end
def migrate_to_hashed_storage!
- return if hashed_storage?(:repository)
+ return unless storage_upgradable?
update!(repository_read_only: true)
@@ -1991,12 +2003,10 @@ class Project < ActiveRecord::Base
def badges
return project_badges unless group
- group_badges_rel = GroupBadge.where(group: group.self_and_ancestors)
-
- union = Gitlab::SQL::Union.new([project_badges.select(:id),
- group_badges_rel.select(:id)])
-
- Badge.where("id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
+ Badge.from_union([
+ project_badges,
+ GroupBadge.where(group: group.self_and_ancestors)
+ ])
end
def merge_requests_allowing_push_to_user(user)
@@ -2047,41 +2057,6 @@ class Project < ActiveRecord::Base
auto_cancel_pending_pipelines == 'enabled'
end
- private
-
- def rename_or_migrate_repository!
- if Gitlab::CurrentSettings.hashed_storage_enabled? && storage_version != LATEST_STORAGE_VERSION
- ::Projects::HashedStorageMigrationService.new(self, full_path_was).execute
- else
- storage.rename_repo
- end
- end
-
- def after_rename_repository(full_path_before, path_before)
- execute_rename_repository_hooks!(full_path_before)
-
- write_repository_config
-
- # We need to check if project had been rolled out to move resource to hashed storage or not and decide
- # if we need execute any take action or no-op.
- unless hashed_storage?(:attachments)
- Gitlab::UploadsTransfer.new.rename_project(path_before, self.path, namespace.full_path)
- end
-
- Gitlab::PagesTransfer.new.rename_project(path_before, self.path, namespace.full_path)
- end
-
- def execute_rename_repository_hooks!(full_path_before)
- # When we import a project overwriting the original project, there
- # is a move operation. In that case we don't want to send the instructions.
- send_move_instructions(full_path_before) unless import_started?
-
- self.old_path_with_namespace = full_path_before
- SystemHooksService.new.execute_hooks_for(self, :rename)
-
- reload_repository!
- end
-
def storage
@storage ||=
if hashed_storage?(:repository)
@@ -2091,6 +2066,16 @@ class Project < ActiveRecord::Base
end
end
+ def storage_upgradable?
+ storage_version != LATEST_STORAGE_VERSION
+ end
+
+ def snippets_visible?(user = nil)
+ Ability.allowed?(user, :read_project_snippet, self)
+ end
+
+ private
+
def use_hashed_storage
if self.new_record? && Gitlab::CurrentSettings.hashed_storage_enabled
self.storage_version = LATEST_STORAGE_VERSION
@@ -2118,13 +2103,8 @@ class Project < ActiveRecord::Base
gitlab_shell.exists?(repository_storage, "#{disk_path}.git")
end
- # set last_activity_at to the same as created_at
- def set_last_activity_at
- update_column(:last_activity_at, self.created_at)
- end
-
- def set_last_repository_updated_at
- update_column(:last_repository_updated_at, self.created_at)
+ def set_timestamps_for_create
+ update_columns(last_activity_at: self.created_at, last_repository_updated_at: self.created_at)
end
def cross_namespace_reference?(from)
@@ -2220,12 +2200,12 @@ class Project < ActiveRecord::Base
end
end
- if RequestStore.active?
- RequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_collaboration") do
- check_access.call
- end
- else
+ Gitlab::SafeRequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_collaboration") do
check_access.call
end
end
+
+ def services_templates
+ @services_templates ||= Service.where(template: true)
+ end
end
diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb
index 746bb4584c9..2c590008db2 100644
--- a/app/models/project_authorization.rb
+++ b/app/models/project_authorization.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class ProjectAuthorization < ActiveRecord::Base
+ include FromUnion
+
belongs_to :user
belongs_to :project
@@ -8,9 +10,9 @@ class ProjectAuthorization < ActiveRecord::Base
validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
validates :user, uniqueness: { scope: [:project, :access_level] }, presence: true
- def self.select_from_union(union)
- select(['project_id', 'MAX(access_level) AS access_level'])
- .from("(#{union.to_sql}) #{ProjectAuthorization.table_name}")
+ def self.select_from_union(relations)
+ from_union(relations)
+ .select(['project_id', 'MAX(access_level) AS access_level'])
.group(:project_id)
end
diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb
index 155400d1a43..2253ad7b543 100644
--- a/app/models/project_auto_devops.rb
+++ b/app/models/project_auto_devops.rb
@@ -5,7 +5,8 @@ class ProjectAutoDevops < ActiveRecord::Base
enum deploy_strategy: {
continuous: 0,
- manual: 1
+ manual: 1,
+ timed_incremental: 2
}
scope :enabled, -> { where(enabled: true) }
@@ -30,10 +31,7 @@ class ProjectAutoDevops < ActiveRecord::Base
value: domain.presence || instance_domain)
end
- if manual?
- variables.append(key: 'STAGING_ENABLED', value: '1')
- variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1')
- end
+ variables.concat(deployment_strategy_default_variables)
end
end
@@ -47,12 +45,20 @@ class ProjectAutoDevops < ActiveRecord::Base
end
def needs_to_create_deploy_token?
- auto_devops_enabled? &&
+ project.auto_devops_enabled? &&
!project.public? &&
!project.deploy_tokens.find_by(name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME).present?
end
- def auto_devops_enabled?
- Gitlab::CurrentSettings.auto_devops_enabled? || enabled?
+ def deployment_strategy_default_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ if manual?
+ variables.append(key: 'STAGING_ENABLED', value: '1')
+ variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1') # deprecated
+ variables.append(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'manual')
+ elsif timed_incremental?
+ variables.append(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'timed')
+ end
+ end
end
end
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index d74cb2506ba..39f2b8fe0de 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -13,15 +13,16 @@ class ProjectFeature < ActiveRecord::Base
# Disabled: not enabled for anyone
# Private: enabled only for team members
# Enabled: enabled for everyone able to access the project
+ # Public: enabled for everyone (only allowed for pages)
#
# Permission levels
DISABLED = 0
PRIVATE = 10
ENABLED = 20
+ PUBLIC = 30
- FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze
- STATISTICS_ATTRIBUTE = 'wikis_count'.freeze
+ FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze
class << self
def access_level_attribute(feature)
@@ -47,6 +48,7 @@ class ProjectFeature < ActiveRecord::Base
validates :project, presence: true
validate :repository_children_level
+ validate :allowed_access_levels
default_value_for :builds_access_level, value: ENABLED, allows_nil: false
default_value_for :issues_access_level, value: ENABLED, allows_nil: false
@@ -55,10 +57,10 @@ class ProjectFeature < ActiveRecord::Base
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
- after_create ->(model) { SiteStatistic.track(STATISTICS_ATTRIBUTE) if model.wiki_enabled? }
- after_update :update_site_statistics
-
def feature_available?(feature, user)
+ # This feature might not be behind a feature flag at all, so default to true
+ return false unless ::Feature.enabled?(feature, user, default_enabled: true)
+
get_permission(user, access_level(feature))
end
@@ -82,30 +84,18 @@ class ProjectFeature < ActiveRecord::Base
issues_access_level > DISABLED
end
- # This is a workaround for the removal hooks not been triggered when removing a Project.
- #
- # ProjectFeature is removed using database cascade index rule.
- # This method is called by Project model when deletion starts.
- def untrack_statistics_for_deletion!
- return unless wiki_enabled?
-
- SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
+ def pages_enabled?
+ pages_access_level > DISABLED
end
- private
-
- def update_site_statistics
- return unless wiki_access_level_changed?
+ def public_pages?
+ return true unless Gitlab.config.pages.access_control
- if self.wiki_access_level_was == DISABLED
- # possible new states are PRIVATE / ENABLED, both should be tracked
- SiteStatistic.track(STATISTICS_ATTRIBUTE)
- elsif self.wiki_access_level == DISABLED
- # old state was either PRIVATE / ENABLED, only untrack if new state is DISABLED
- SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
- end
+ pages_access_level == PUBLIC || pages_access_level == ENABLED && project.public?
end
+ private
+
# Validates builds and merge requests access level
# which cannot be higher than repository access level
def repository_children_level
@@ -118,6 +108,17 @@ class ProjectFeature < ActiveRecord::Base
%i(merge_requests_access_level builds_access_level).each(&validator)
end
+ # Validates access level for other than pages cannot be PUBLIC
+ def allowed_access_levels
+ validator = lambda do |field|
+ level = public_send(field) || ProjectFeature::ENABLED # rubocop:disable GitlabSecurity/PublicSend
+ not_allowed = level > ProjectFeature::ENABLED
+ self.errors.add(field, "cannot have public visibility level") if not_allowed
+ end
+
+ (FEATURES - %i(pages)).each {|f| validator.call("#{f}_access_level")}
+ end
+
def get_permission(user, level)
case level
when DISABLED
@@ -126,6 +127,8 @@ class ProjectFeature < ActiveRecord::Base
user && (project.team.member?(user) || user.full_private_access?)
when ENABLED
true
+ when PUBLIC
+ true
else
true
end
diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb
index 89ed09af96a..7126bb66d80 100644
--- a/app/models/project_import_state.rb
+++ b/app/models/project_import_state.rb
@@ -48,10 +48,25 @@ class ProjectImportState < ActiveRecord::Base
project.reset_cache_and_import_attrs
if Gitlab::ImportSources.importer_names.include?(project.import_type) && project.repo_exists?
+ # rubocop: disable CodeReuse/ServiceClass
state.run_after_commit do
Projects::AfterImportService.new(project).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
end
end
end
+
+ def mark_as_failed(error_message)
+ original_errors = errors.dup
+ sanitized_message = Gitlab::UrlSanitizer.sanitize(error_message)
+
+ fail_op
+
+ update_column(:last_error, sanitized_message)
+ rescue ActiveRecord::ActiveRecordError => e
+ Rails.logger.error("Error setting import status to failed: #{e.message}. Original error: #{sanitized_message}")
+ ensure
+ @errors = original_errors
+ end
end
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb
index 35c19049c04..cc5f1207653 100644
--- a/app/models/project_services/asana_service.rb
+++ b/app/models/project_services/asana_service.rb
@@ -65,7 +65,7 @@ http://app.asana.com/-/account_api'
# check the branch restriction is poplulated and branch is not included
branch = Gitlab::Git.ref_name(data[:ref])
branch_restriction = restrict_to_branch.to_s
- if branch_restriction.length > 0 && branch_restriction.index(branch).nil?
+ if branch_restriction.present? && branch_restriction.index(branch).nil?
return
end
@@ -101,7 +101,7 @@ http://app.asana.com/-/account_api'
task.update(completed: true)
end
rescue => e
- Rails.logger.error(e.message)
+ log_error(e.message)
next
end
end
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index d502423726c..a252052200a 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -80,24 +80,31 @@ class BambooService < CiService
private
+ def get_build_result_index
+ # When Bamboo returns multiple results for a given changeset, arbitrarily assume the most relevant result to be the last one.
+ -1
+ end
+
def read_build_page(response)
- if response.code != 200 || response['results']['results']['size'] == '0'
- # If actual build link can't be determined, send user to build summary page.
- URI.join("#{bamboo_url}/", "browse/#{build_key}").to_s
- else
- # If actual build link is available, go to build result page.
- result_key = response['results']['results']['result']['planResultKey']['key']
- URI.join("#{bamboo_url}/", "browse/#{result_key}").to_s
- end
+ key =
+ if response.code != 200 || response.dig('results', 'results', 'size') == '0'
+ # If actual build link can't be determined, send user to build summary page.
+ build_key
+ else
+ # If actual build link is available, go to build result page.
+ response.dig('results', 'results', 'result', get_build_result_index, 'planResultKey', 'key')
+ end
+
+ build_url("browse/#{key}")
end
def read_commit_status(response)
return :error unless response.code == 200 || response.code == 404
- status = if response.code == 404 || response['results']['results']['size'] == '0'
+ status = if response.code == 404 || response.dig('results', 'results', 'size') == '0'
'Pending'
else
- response['results']['results']['result']['buildState']
+ response.dig('results', 'results', 'result', get_build_result_index, 'buildState')
end
if status.include?('Success')
@@ -112,7 +119,7 @@ class BambooService < CiService
end
def build_url(path)
- URI.join("#{bamboo_url}/", path).to_s
+ Gitlab::Utils.append_path(bamboo_url, path)
end
def get_path(path, query_params = {})
diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb
index 58631e09538..6b7a35aaa75 100644
--- a/app/models/project_services/chat_message/merge_message.rb
+++ b/app/models/project_services/chat_message/merge_message.rb
@@ -26,7 +26,7 @@ module ChatMessage
def activity
{
- title: "Merge Request #{state} by #{user_combined_name}",
+ title: "Merge Request #{state_or_action_text} by #{user_combined_name}",
subtitle: "in #{project_link}",
text: merge_request_link,
image: user_avatar
@@ -48,7 +48,7 @@ module ChatMessage
end
def merge_request_message
- "#{user_combined_name} #{state} #{merge_request_link} in #{project_link}: #{title}"
+ "#{user_combined_name} #{state_or_action_text} #{merge_request_link} in #{project_link}"
end
def merge_request_link
@@ -62,5 +62,10 @@ module ChatMessage
def merge_request_url
"#{project_url}/merge_requests/#{merge_request_iid}"
end
+
+ # overridden in EE
+ def state_or_action_text
+ state
+ end
end
end
diff --git a/app/models/project_services/chat_message/push_message.rb b/app/models/project_services/chat_message/push_message.rb
index 82be33a12a1..5dd0414b7e6 100644
--- a/app/models/project_services/chat_message/push_message.rb
+++ b/app/models/project_services/chat_message/push_message.rb
@@ -26,16 +26,8 @@ module ChatMessage
end
def activity
- action = if new_branch?
- "created"
- elsif removed_branch?
- "removed"
- else
- "pushed to"
- end
-
{
- title: "#{user_combined_name} #{action} #{ref_type}",
+ title: humanized_action(short: true),
subtitle: "in #{project_link}",
text: compare_link,
image: user_avatar
@@ -44,32 +36,21 @@ module ChatMessage
private
+ def humanized_action(short: false)
+ action, ref_link, target_link = compose_action_details
+ text = [user_combined_name, action, ref_type, ref_link]
+ text << target_link unless short
+ text.join(' ')
+ end
+
def message
- if new_branch?
- new_branch_message
- elsif removed_branch?
- removed_branch_message
- else
- push_message
- end
+ humanized_action
end
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
- def new_branch_message
- "#{user_combined_name} pushed new #{ref_type} #{branch_link} to #{project_link}"
- end
-
- def removed_branch_message
- "#{user_combined_name} removed #{ref_type} #{ref} from #{project_link}"
- end
-
- def push_message
- "#{user_combined_name} pushed to #{ref_type} #{branch_link} of #{project_link} (#{compare_link})"
- end
-
def commit_messages
commits.map { |commit| compose_commit_message(commit) }.join("\n\n")
end
@@ -115,6 +96,16 @@ module ChatMessage
"[Compare changes](#{compare_url})"
end
+ def compose_action_details
+ if new_branch?
+ ['pushed new', branch_link, "to #{project_link}"]
+ elsif removed_branch?
+ ['removed', ref, "from #{project_link}"]
+ else
+ ['pushed to', branch_link, "of #{project_link} (#{compare_link})"]
+ end
+ end
+
def attachment_color
'#345'
end
diff --git a/app/models/project_services/discord_service.rb b/app/models/project_services/discord_service.rb
new file mode 100644
index 00000000000..21afd14dbff
--- /dev/null
+++ b/app/models/project_services/discord_service.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require "discordrb/webhooks"
+
+class DiscordService < ChatNotificationService
+ def title
+ "Discord Notifications"
+ end
+
+ def description
+ "Receive event notifications in Discord"
+ end
+
+ def self.to_param
+ "discord"
+ end
+
+ def help
+ "This service sends notifications about project events to Discord channels.<br />
+ To set up this service:
+ <ol>
+ <li><a href='https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks'>Setup a custom Incoming Webhook</a>.</li>
+ <li>Paste the <strong>Webhook URL</strong> into the field below.</li>
+ <li>Select events below to enable notifications.</li>
+ </ol>"
+ end
+
+ def event_field(event)
+ # No-op.
+ end
+
+ def default_channel_placeholder
+ # No-op.
+ end
+
+ def default_fields
+ [
+ { type: "text", name: "webhook", placeholder: "e.g. https://discordapp.com/api/webhooks/…" },
+ { type: "checkbox", name: "notify_only_broken_pipelines" },
+ { type: "checkbox", name: "notify_only_default_branch" }
+ ]
+ end
+
+ private
+
+ def notify(message, opts)
+ client = Discordrb::Webhooks::Client.new(url: webhook)
+
+ client.execute do |builder|
+ builder.content = message.pretext
+ end
+ end
+
+ def custom_data(data)
+ super(data).merge(markdown: true)
+ end
+end
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index 158ae0bf255..5ccc2f019cb 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -39,11 +39,9 @@ class DroneCiService < CiService
end
def commit_status_path(sha, ref)
- url = [drone_url,
- "gitlab/#{project.full_path}/commits/#{sha}",
- "?branch=#{URI.encode(ref.to_s)}&access_token=#{token}"]
-
- URI.join(*url).to_s
+ Gitlab::Utils.append_path(
+ drone_url,
+ "gitlab/#{project.full_path}/commits/#{sha}?branch=#{URI.encode(ref.to_s)}&access_token=#{token}")
end
def commit_status(sha, ref)
@@ -74,11 +72,9 @@ class DroneCiService < CiService
end
def build_page(sha, ref)
- url = [drone_url,
- "gitlab/#{project.full_path}/redirect/commits/#{sha}",
- "?branch=#{URI.encode(ref.to_s)}"]
-
- URI.join(*url).to_s
+ Gitlab::Utils.append_path(
+ drone_url,
+ "gitlab/#{project.full_path}/redirect/commits/#{sha}?branch=#{URI.encode(ref.to_s)}")
end
def title
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index 2545df06f6b..76624263aab 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -1,53 +1,5 @@
# frozen_string_literal: true
-require "flowdock-git-hook"
-
-# Flow dock depends on Grit to compute the number of commits between two given
-# commits. To make this depend on Gitaly, a monkey patch is applied
-module Flowdock
- class Git
- # pass down a Repository all the way down
- def repo
- @options[:repo]
- end
-
- def config
- {}
- end
-
- def messages
- Git::Builder.new(repo: repo,
- ref: @ref,
- before: @from,
- after: @to,
- commit_url: @commit_url,
- branch_url: @branch_url,
- diff_url: @diff_url,
- repo_url: @repo_url,
- repo_name: @repo_name,
- permanent_refs: @permanent_refs,
- tags: tags
- ).to_hashes
- end
-
- class Builder
- def commits
- @repo.commits_between(@before, @after).map do |commit|
- {
- url: @opts[:commit_url] ? @opts[:commit_url] % [commit.sha] : nil,
- id: commit.sha,
- message: commit.message,
- author: {
- name: commit.author_name,
- email: commit.author_email
- }
- }
- end
- end
- end
- end
-end
-
class FlowdockService < Service
prop_accessor :token
validates :token, presence: true, if: :activated?
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
deleted file mode 100644
index 67a92c441b1..00000000000
--- a/app/models/project_services/gemnasium_service.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: true
-
-require "gemnasium/gitlab_service"
-
-class GemnasiumService < Service
- prop_accessor :token, :api_key
- validates :token, :api_key, presence: true, if: :activated?
- validate :deprecation_validation
-
- def title
- 'Gemnasium'
- end
-
- def description
- 'Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities.'
- end
-
- def self.to_param
- 'gemnasium'
- end
-
- def fields
- [
- { type: 'text', name: 'api_key', placeholder: 'Your personal API KEY on gemnasium.com ', required: true },
- { type: 'text', name: 'token', placeholder: 'The project\'s slug on gemnasium.com', required: true }
- ]
- end
-
- def self.supported_events
- %w(push)
- end
-
- def deprecated?
- true
- end
-
- def deprecation_message
- "Gemnasium has been acquired by GitLab in January 2018. Since May 15, 2018, the service provided by Gemnasium is no longer available."
- end
-
- def deprecation_validation
- errors[:base] << deprecation_message
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- # Gitaly: this class will be removed https://gitlab.com/gitlab-org/gitlab-ee/issues/6010
- repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.path_to_repo
- end
-
- Gemnasium::GitlabService.execute(
- ref: data[:ref],
- before: data[:before],
- after: data[:after],
- token: token,
- api_key: api_key,
- repo: repo_path
- )
- end
-end
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 66012f0da99..a69b7b4c4b6 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -149,7 +149,7 @@ class HipchatService < Service
context.merge!(options)
- html = Banzai.post_process(Banzai.render(text, context), context)
+ html = Banzai.render_and_post_process(text, context)
sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt])
sanitized_html.truncate(200, separator: ' ', omission: '...')
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index a783a314071..a15780c14f9 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -104,7 +104,7 @@ class IrkerService < Service
new_recipient = URI.join(default_irc_uri, '/', recipient).to_s
uri = consider_uri(URI.parse(new_recipient))
rescue
- Rails.logger.error("Unable to create a valid URL from #{default_irc_uri} and #{recipient}")
+ log_error("Unable to create a valid URL", default_irc_uri: default_irc_uri, recipient: recipient)
end
end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index c7520d766a8..f54497fc6d8 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -9,12 +9,12 @@ class IssueTrackerService < Service
# Override this method on services that uses different patterns
# This pattern does not support cross-project references
# The other code assumes that this pattern is a superset of all
- # overriden patterns. See ReferenceRegexes::EXTERNAL_PATTERN
+ # overridden patterns. See ReferenceRegexes.external_pattern
def self.reference_pattern(only_long: false)
if only_long
- /(\b[A-Z][A-Z0-9_]+-)(?<issue>\d+)/
+ /(\b[A-Z][A-Z0-9_]*-)(?<issue>\d+)/
else
- /(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)/
+ /(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})(?<issue>\d+)/
end
end
@@ -88,7 +88,7 @@ class IssueTrackerService < Service
rescue Gitlab::HTTP::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error
message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
end
- Rails.logger.info(message)
+ log_info(message)
result
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index cc98b3f5a41..9066a0b7f1d 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -14,6 +14,9 @@ class JiraService < IssueTrackerService
format: { with: Gitlab::Regex.jira_transition_id_regex, message: "transition ids can have only numbers which can be split with , or ;" },
allow_blank: true
+ # JIRA cloud version is deprecating authentication via username and password.
+ # We should use username/password for JIRA server and email/api_token for JIRA cloud,
+ # for more information check: https://gitlab.com/gitlab-org/gitlab-ce/issues/49936.
prop_accessor :username, :password, :url, :api_url, :jira_issue_transition_id, :title, :description
before_update :reset_password
@@ -51,7 +54,7 @@ class JiraService < IssueTrackerService
{
username: self.username,
password: self.password,
- site: URI.join(url, '/').to_s,
+ site: URI.join(url, '/').to_s, # Intended to find the root
context_path: url.path.chomp('/'),
auth_type: :basic,
read_timeout: 120,
@@ -95,8 +98,8 @@ class JiraService < IssueTrackerService
[
{ type: 'text', name: 'url', title: 'Web URL', placeholder: 'https://jira.example.com', required: true },
{ type: 'text', name: 'api_url', title: 'JIRA API URL', placeholder: 'If different from Web URL' },
- { type: 'text', name: 'username', placeholder: '', required: true },
- { type: 'password', name: 'password', placeholder: '', required: true },
+ { type: 'text', name: 'username', title: 'Username or Email', placeholder: 'Use a username for server version and an email for cloud version', required: true },
+ { type: 'password', name: 'password', title: 'Password or API token', placeholder: 'Use a password for server version and an API token for cloud version', required: true },
{ type: 'text', name: 'jira_issue_transition_id', title: 'Transition ID(s)', placeholder: 'Use , or ; to separate multiple transition IDs' }
]
end
@@ -205,7 +208,7 @@ class JiraService < IssueTrackerService
begin
issue.transitions.build.save!(transition: { id: transition_id })
rescue => error
- Rails.logger.info "#{self.class.name} Issue Transition failed message ERROR: #{client_url} - #{error.message}"
+ log_error("Issue transition failed", error: error.message, client_url: client_url)
return false
end
end
@@ -257,9 +260,8 @@ class JiraService < IssueTrackerService
new_remote_link.save!(remote_link_props)
end
- result_message = "#{self.class.name} SUCCESS: Successfully posted to #{client_url}."
- Rails.logger.info(result_message)
- result_message
+ log_info("Successfully posted", client_url: client_url)
+ "SUCCESS: Successfully posted to http://jira.example.net."
end
end
@@ -317,7 +319,7 @@ class JiraService < IssueTrackerService
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, URI::InvalidURIError, JIRA::HTTPError, OpenSSL::SSL::SSLError => e
@error = e.message
- Rails.logger.info "#{self.class.name} Send message ERROR: #{client_url} - #{@error}"
+ log_error("Error sending message", client_url: client_url, error: @error)
nil
end
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index bda1f67b8ff..c52a531e5fe 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -96,15 +96,20 @@ class KubernetesService < DeploymentService
# Check we can connect to the Kubernetes API
def test(*args)
- kubeclient = build_kubeclient!
+ kubeclient = build_kube_client!
- kubeclient.discover
- { success: kubeclient.discovered, result: "Checked API discovery endpoint" }
+ kubeclient.core_client.discover
+ { success: kubeclient.core_client.discovered, result: "Checked API discovery endpoint" }
rescue => err
{ success: false, result: err }
end
- def predefined_variables
+ # Project param was added on
+ # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22011,
+ # as a way to keep this service compatible with
+ # Clusters::Platforms::Kubernetes, it won't be used on this method
+ # as it's only needed for Clusters::Cluster.
+ def predefined_variables(project:)
config = YAML.dump(kubeconfig)
Gitlab::Ci::Variables::Collection.new.tap do |variables|
@@ -144,7 +149,7 @@ class KubernetesService < DeploymentService
end
def kubeclient
- @kubeclient ||= build_kubeclient!
+ @kubeclient ||= build_kube_client!
end
def deprecated?
@@ -182,12 +187,11 @@ class KubernetesService < DeploymentService
slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
end
- def build_kubeclient!(api_path: 'api', api_version: 'v1')
+ def build_kube_client!
raise "Incomplete settings" unless api_url && actual_namespace && token
- ::Kubeclient::Client.new(
- join_api_url(api_path),
- api_version,
+ Gitlab::Kubernetes::KubeClient.new(
+ api_url,
auth_options: kubeclient_auth_options,
ssl_options: kubeclient_ssl_options,
http_proxy_uri: ENV['http_proxy']
@@ -196,12 +200,10 @@ class KubernetesService < DeploymentService
# Returns a hash of all pods in the namespace
def read_pods
- kubeclient = build_kubeclient!
+ kubeclient = build_kube_client!
kubeclient.get_pods(namespace: actual_namespace).as_json
- rescue Kubeclient::HttpError => err
- raise err unless err.error_code == 404
-
+ rescue Kubeclient::ResourceNotFoundError
[]
end
@@ -220,15 +222,6 @@ class KubernetesService < DeploymentService
{ bearer_token: token }
end
- def join_api_url(api_path)
- url = URI.parse(api_url)
- prefix = url.path.sub(%r{/+\z}, '')
-
- url.path = [prefix, api_path].join("/")
-
- url.to_s
- end
-
def terminal_auth
{
token: token,
diff --git a/app/models/project_services/microsoft_teams_service.rb b/app/models/project_services/microsoft_teams_service.rb
index 5b0e5fed092..c34078f13c1 100644
--- a/app/models/project_services/microsoft_teams_service.rb
+++ b/app/models/project_services/microsoft_teams_service.rb
@@ -17,7 +17,7 @@ class MicrosoftTeamsService < ChatNotificationService
'This service sends notifications about projects events to Microsoft Teams channels.<br />
To set up this service:
<ol>
- <li><a href="https://msdn.microsoft.com/en-us/microsoft-teams/connectors">Getting started with 365 Office Connectors For Microsoft Teams</a>.</li>
+ <li><a href="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors/connectors-using#setting-up-a-custom-incoming-webhook">Setup a custom Incoming Webhook using Office 365 Connectors For Microsoft Teams</a>.</li>
<li>Paste the <strong>Webhook URL</strong> into the field below.</li>
<li>Select events below to enable notifications.</li>
</ol>'
diff --git a/app/models/project_services/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb
index 6883976f0c8..d8bba58dcbf 100644
--- a/app/models/project_services/mock_ci_service.rb
+++ b/app/models/project_services/mock_ci_service.rb
@@ -34,10 +34,9 @@ class MockCiService < CiService
# http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c
#
def build_page(sha, ref)
- url = [mock_service_url,
- "#{project.namespace.path}/#{project.path}/status/#{sha}"]
-
- URI.join(*url).to_s
+ Gitlab::Utils.append_path(
+ mock_service_url,
+ "#{project.namespace.path}/#{project.path}/status/#{sha}")
end
# Return string with build status or :error symbol
@@ -61,10 +60,9 @@ class MockCiService < CiService
end
def commit_status_path(sha)
- url = [mock_service_url,
- "#{project.namespace.path}/#{project.path}/status/#{sha}.json"]
-
- URI.join(*url).to_s
+ Gitlab::Utils.append_path(
+ mock_service_url,
+ "#{project.namespace.path}/#{project.path}/status/#{sha}.json")
end
def read_commit_status(response)
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index 509e5b6089b..211e5c3fcbf 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -26,7 +26,7 @@ class PrometheusService < MonitoringService
end
def editable?
- manual_configuration? || !prometheus_installed?
+ manual_configuration? || !prometheus_available?
end
def title
@@ -56,7 +56,6 @@ class PrometheusService < MonitoringService
name: 'api_url',
title: 'API URL',
placeholder: s_('PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/'),
- help: s_('PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server.'),
required: true
}
]
@@ -75,17 +74,17 @@ class PrometheusService < MonitoringService
RestClient::Resource.new(api_url) if api_url && manual_configuration? && active?
end
- def prometheus_installed?
+ def prometheus_available?
return false if template?
return false unless project
- project.clusters.enabled.any? { |cluster| cluster.application_prometheus&.installed? }
+ project.clusters.enabled.any? { |cluster| cluster.application_prometheus_available? }
end
private
def synchronize_service_state
- self.active = prometheus_installed? || manual_configuration?
+ self.active = prometheus_available? || manual_configuration?
true
end
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
index e3ab60adefd..bfabc6d262c 100644
--- a/app/models/project_services/slash_commands_service.rb
+++ b/app/models/project_services/slash_commands_service.rb
@@ -44,11 +44,15 @@ class SlashCommandsService < Service
private
+ # rubocop: disable CodeReuse/ServiceClass
def find_chat_user(params)
ChatNames::FindUserService.new(self, params).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
+ # rubocop: disable CodeReuse/ServiceClass
def authorize_chat_name_url(params)
ChatNames::AuthorizeUserService.new(self, params).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
end
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index eeeff5e802a..b8e17087db5 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -132,7 +132,7 @@ class TeamcityService < CiService
end
def build_url(path)
- URI.join("#{teamcity_url}/", path).to_s
+ Gitlab::Utils.append_path(teamcity_url, path)
end
def get_path(path)
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index f4b3421f04b..559e4f99294 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -80,7 +80,7 @@ class ProjectWiki
pages(limit: 1).empty?
end
- # Returns an Array of Gitlab WikiPage instances or an
+ # Returns an Array of GitLab WikiPage instances or an
# empty Array if this Wiki has no pages.
def pages(limit: 0)
wiki.pages(limit: limit).map { |page| WikiPage.new(self, page, true) }
@@ -184,11 +184,12 @@ class ProjectWiki
def commit_details(action, message = nil, title = nil)
commit_message = message || default_message(action, title)
+ git_user = Gitlab::Git::User.from_gitlab(@user)
Gitlab::Git::Wiki::CommitDetails.new(@user.id,
- @user.username,
- @user.name,
- @user.email,
+ git_user.username,
+ git_user.name,
+ git_user.email,
commit_message)
end
diff --git a/app/models/prometheus_metric.rb b/app/models/prometheus_metric.rb
new file mode 100644
index 00000000000..ce2db9cb44c
--- /dev/null
+++ b/app/models/prometheus_metric.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+class PrometheusMetric < ActiveRecord::Base
+ belongs_to :project, validate: true, inverse_of: :prometheus_metrics
+
+ enum group: {
+ # built-in groups
+ nginx_ingress: -1,
+ ha_proxy: -2,
+ aws_elb: -3,
+ nginx: -4,
+ kubernetes: -5,
+
+ # custom/user groups
+ business: 0,
+ response: 1,
+ system: 2
+ }
+
+ validates :title, presence: true
+ validates :query, presence: true
+ validates :group, presence: true
+ validates :y_label, presence: true
+ validates :unit, presence: true
+
+ validates :project, presence: true, unless: :common?
+ validates :project, absence: true, if: :common?
+
+ scope :common, -> { where(common: true) }
+
+ GROUP_TITLES = {
+ # built-in groups
+ nginx_ingress: _('Response metrics (NGINX Ingress)'),
+ ha_proxy: _('Response metrics (HA Proxy)'),
+ aws_elb: _('Response metrics (AWS ELB)'),
+ nginx: _('Response metrics (NGINX)'),
+ kubernetes: _('System metrics (Kubernetes)'),
+
+ # custom/user groups
+ business: _('Business metrics (Custom)'),
+ response: _('Response metrics (Custom)'),
+ system: _('System metrics (Custom)')
+ }.freeze
+
+ REQUIRED_METRICS = {
+ nginx_ingress: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg),
+ ha_proxy: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total),
+ aws_elb: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum),
+ nginx: %w(nginx_server_requests nginx_server_requestMsec),
+ kubernetes: %w(container_memory_usage_bytes container_cpu_usage_seconds_total)
+ }.freeze
+
+ def group_title
+ GROUP_TITLES[group.to_sym]
+ end
+
+ def required_metrics
+ REQUIRED_METRICS[group.to_sym].to_a.map(&:to_s)
+ end
+
+ def to_query_metric
+ Gitlab::Prometheus::Metric.new(id: id, title: title, required_metrics: required_metrics, weight: 0, y_label: y_label, queries: queries)
+ end
+
+ def queries
+ [
+ {
+ query_range: query,
+ unit: unit,
+ label: legend,
+ series: query_series
+ }.compact
+ ]
+ end
+
+ def query_series
+ case legend
+ when 'Status Code'
+ [{
+ label: 'status_code',
+ when: [
+ { value: '2xx', color: 'green' },
+ { value: '4xx', color: 'orange' },
+ { value: '5xx', color: 'red' }
+ ]
+ }]
+ end
+ end
+end
diff --git a/app/models/protected_ref_matcher.rb b/app/models/protected_ref_matcher.rb
deleted file mode 100644
index bfa9180ac93..00000000000
--- a/app/models/protected_ref_matcher.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# frozen_string_literal: true
-
-class ProtectedRefMatcher
- def initialize(protected_ref)
- @protected_ref = protected_ref
- end
-
- # Returns all protected refs that match the given ref name.
- # This checks all records from the scope built up so far, and does
- # _not_ return a relation.
- #
- # This method optionally takes in a list of `protected_refs` to search
- # through, to avoid calling out to the database.
- def self.matching(type, ref_name, protected_refs: nil)
- (protected_refs || type.all).select { |protected_ref| protected_ref.matches?(ref_name) }
- end
-
- # Returns all branches/tags (among the given list of refs [`Gitlab::Git::Branch`])
- # that match the current protected ref.
- def matching(refs)
- refs.select { |ref| @protected_ref.matches?(ref.name) }
- end
-
- # Checks if the protected ref matches the given ref name.
- def matches?(ref_name)
- return false if @protected_ref.name.blank?
-
- exact_match?(ref_name) || wildcard_match?(ref_name)
- end
-
- # Checks if this protected ref contains a wildcard
- def wildcard?
- @protected_ref.name && @protected_ref.name.include?('*')
- end
-
- protected
-
- def exact_match?(ref_name)
- @protected_ref.name == ref_name
- end
-
- def wildcard_match?(ref_name)
- return false unless wildcard?
-
- wildcard_regex === ref_name
- end
-
- def wildcard_regex
- @wildcard_regex ||= begin
- name = @protected_ref.name.gsub('*', 'STAR_DONT_ESCAPE')
- quoted_name = Regexp.quote(name)
- regex_string = quoted_name.gsub('STAR_DONT_ESCAPE', '.*?')
- /\A#{regex_string}\z/
- end
- end
-end
diff --git a/app/models/protected_tag.rb b/app/models/protected_tag.rb
index a36f0d36262..94746141945 100644
--- a/app/models/protected_tag.rb
+++ b/app/models/protected_tag.rb
@@ -4,6 +4,8 @@ class ProtectedTag < ActiveRecord::Base
include Gitlab::ShellAdapter
include ProtectedRef
+ validates :name, uniqueness: { scope: :project_id }
+
protected_ref_access_levels :create
def self.protected?(project, ref_name)
diff --git a/app/models/ref_matcher.rb b/app/models/ref_matcher.rb
new file mode 100644
index 00000000000..fa7d2c0f06c
--- /dev/null
+++ b/app/models/ref_matcher.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+class RefMatcher
+ def initialize(ref_name_or_pattern)
+ @ref_name_or_pattern = ref_name_or_pattern
+ end
+
+ # Returns all branches/tags (among the given list of refs [`Gitlab::Git::Branch`])
+ # that match the current protected ref.
+ def matching(refs)
+ refs.select { |ref| matches?(ref.name) }
+ end
+
+ # Checks if the protected ref matches the given ref name.
+ def matches?(ref_name)
+ return false if @ref_name_or_pattern.blank?
+
+ exact_match?(ref_name) || wildcard_match?(ref_name)
+ end
+
+ # Checks if this protected ref contains a wildcard
+ def wildcard?
+ @ref_name_or_pattern && @ref_name_or_pattern.include?('*')
+ end
+
+ protected
+
+ def exact_match?(ref_name)
+ @ref_name_or_pattern == ref_name
+ end
+
+ def wildcard_match?(ref_name)
+ return false unless wildcard?
+
+ wildcard_regex === ref_name
+ end
+
+ def wildcard_regex
+ @wildcard_regex ||= begin
+ name = @ref_name_or_pattern.gsub('*', 'STAR_DONT_ESCAPE')
+ quoted_name = Regexp.quote(name)
+ regex_string = quoted_name.gsub('STAR_DONT_ESCAPE', '.*?')
+ /\A#{regex_string}\z/
+ end
+ end
+end
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index 833faf3bc82..c1f53b5da4f 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -150,6 +150,15 @@ class RemoteMirror < ActiveRecord::Base
result.to_s
end
+ def ensure_remote!
+ return unless project
+ return unless remote_name && url
+
+ # If this fails or the remote already exists, we won't know due to
+ # https://gitlab.com/gitlab-org/gitaly/issues/1317
+ project.repository.add_remote(remote_name, url)
+ end
+
private
def raw
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 69f375dc6f3..fff6d4be275 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -36,7 +36,7 @@ class Repository
# For example, for entry `:commit_count` there's a method called `commit_count` which
# stores its data in the `commit_count` cache key.
CACHED_METHODS = %i(size commit_count rendered_readme contribution_guide
- changelog license_blob license_key gitignore koding_yml
+ changelog license_blob license_key gitignore
gitlab_ci_yml branch_names tag_names branch_count
tag_count avatar exists? root_ref has_visible_content?
issue_template_names merge_request_template_names xcode_project?).freeze
@@ -53,7 +53,6 @@ class Repository
license: %i(license_blob license_key license),
contributing: :contribution_guide,
gitignore: :gitignore,
- koding: :koding_yml,
gitlab_ci: :gitlab_ci_yml,
avatar: :avatar,
issue_template: :issue_template_names,
@@ -81,10 +80,6 @@ class Repository
alias_method :raw, :raw_repository
- def cleanup
- @raw_repository&.cleanup
- end
-
# Don't use this! It's going away. Use Gitaly to read or write from repos.
def path_to_repo
@path_to_repo ||=
@@ -247,15 +242,22 @@ class Repository
# Git GC will delete commits from the repository that are no longer in any
# branches or tags, but we want to keep some of these commits around, for
# example if they have comments or CI builds.
- def keep_around(sha)
- return unless sha.present? && commit_by(oid: sha)
+ #
+ # For Geo's sake, pass in multiple shas rather than calling it multiple times,
+ # to avoid unnecessary syncing.
+ def keep_around(*shas)
+ shas.each do |sha|
+ begin
+ next unless sha.present? && commit_by(oid: sha)
- return if kept_around?(sha)
+ next if kept_around?(sha)
- # This will still fail if the file is corrupted (e.g. 0 bytes)
- raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
- rescue Gitlab::Git::CommandError => ex
- Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}"
+ # This will still fail if the file is corrupted (e.g. 0 bytes)
+ raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
+ rescue Gitlab::Git::CommandError => ex
+ Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}"
+ end
+ end
end
def kept_around?(sha)
@@ -485,7 +487,20 @@ class Repository
end
def blob_at(sha, path)
- Blob.decorate(raw_repository.blob_at(sha, path), project)
+ blob = Blob.decorate(raw_repository.blob_at(sha, path), project)
+
+ # Don't attempt to return a special result if there is no blob at all
+ return unless blob
+
+ # Don't attempt to return a special result unless we're looking at HEAD
+ return blob unless head_commit&.sha == sha
+
+ case path
+ when head_tree&.readme_path
+ ReadmeBlob.new(blob, self)
+ else
+ blob
+ end
rescue Gitlab::Git::Repository::NoRepository
nil
end
@@ -507,7 +522,7 @@ class Repository
raw_repository.exists?
end
- cache_method :exists?
+ cache_method_asymmetrically :exists?
# We don't need to cache the output of this method because both exists? and
# has_visible_content? are already memoized and cached. There's no guarantee
@@ -567,13 +582,16 @@ class Repository
cache_method :merge_request_template_names, fallback: []
def readme
- if readme = tree(:head)&.readme
- ReadmeBlob.new(readme, self)
- end
+ head_tree&.readme
end
def rendered_readme
- MarkupHelper.markup_unsafe(readme.name, readme.data, project: project, markdown_engine: :redcarpet) if readme
+ return unless readme
+
+ context = { project: project }
+ context[:markdown_engine] = :redcarpet unless MarkupHelper.commonmark_for_repositories_enabled?
+
+ MarkupHelper.markup_unsafe(readme.name, readme.data, context)
end
cache_method :rendered_readme
@@ -604,18 +622,13 @@ class Repository
Licensee::License.new(license_key)
end
- cache_method :license, memoize_only: true
+ memoize_method :license
def gitignore
file_on_head(:gitignore)
end
cache_method :gitignore
- def koding_yml
- file_on_head(:koding)
- end
- cache_method :koding_yml
-
def gitlab_ci_yml
file_on_head(:gitlab_ci)
end
@@ -660,6 +673,14 @@ class Repository
end
end
+ def list_last_commits_for_tree(sha, path, offset: 0, limit: 25)
+ commits = raw_repository.list_last_commits_for_tree(sha, path, offset: offset, limit: limit)
+
+ commits.each do |path, commit|
+ commits[path] = ::Commit.new(commit, @project)
+ end
+ end
+
def last_commit_for_path(sha, path)
commit = raw_repository.last_commit_for_path(sha, path)
::Commit.new(commit, @project) if commit
@@ -865,10 +886,12 @@ class Repository
delegate :merged_branch_names, to: :raw_repository
- def merge_base(first_commit_id, second_commit_id)
- first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
- second_commit_id = commit(second_commit_id).try(:id) || second_commit_id
- raw_repository.merge_base(first_commit_id, second_commit_id)
+ def merge_base(*commits_or_ids)
+ commit_ids = commits_or_ids.map do |commit_or_id|
+ commit_or_id.is_a?(::Commit) ? commit_or_id.id : commit_or_id
+ end
+
+ raw_repository.merge_base(*commit_ids)
end
def ancestor?(ancestor_id, descendant_id)
@@ -889,10 +912,6 @@ class Repository
async_remove_remote(remote_name) if tmp_remote_name
end
- def fetch_remote(remote, forced: false, ssh_auth: nil, no_tags: false, prune: true)
- gitlab_shell.fetch_remote(raw_repository, remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, prune: prune)
- end
-
def async_remove_remote(remote_name)
return unless remote_name
@@ -987,14 +1006,6 @@ class Repository
remote_branch: merge_request.target_branch)
end
- def blob_data_at(sha, path)
- blob = blob_at(sha, path)
- return unless blob
-
- blob.load_all_data!
- blob.data
- end
-
def squash(user, merge_request)
raw.squash(user, merge_request.id, branch: merge_request.target_branch,
start_sha: merge_request.diff_start_sha,
@@ -1003,6 +1014,26 @@ class Repository
message: merge_request.title)
end
+ def update_submodule(user, submodule, commit_sha, message:, branch:)
+ with_cache_hooks do
+ raw.update_submodule(
+ user: user,
+ submodule: submodule,
+ commit_sha: commit_sha,
+ branch: branch,
+ message: message
+ )
+ end
+ end
+
+ def blob_data_at(sha, path)
+ blob = blob_at(sha, path)
+ return unless blob
+
+ blob.load_all_data!
+ blob.data
+ end
+
private
# TODO Generice finder, later split this on finders by Ref or Oid
@@ -1018,7 +1049,19 @@ class Repository
end
def cache
- @cache ||= Gitlab::RepositoryCache.new(self)
+ @cache ||= if is_wiki
+ Gitlab::RepositoryCache.new(self, extra_namespace: 'wiki')
+ else
+ Gitlab::RepositoryCache.new(self)
+ end
+ end
+
+ def request_store_cache
+ @request_store_cache ||= if is_wiki
+ Gitlab::RepositoryCache.new(self, extra_namespace: 'wiki', backend: Gitlab::SafeRequestStore)
+ else
+ Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore)
+ end
end
def tags_sorted_by_committed_date
diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb
index 42c255fcd1e..3fd96b9dc18 100644
--- a/app/models/resource_label_event.rb
+++ b/app/models/resource_label_event.rb
@@ -3,33 +3,122 @@
# This model is not used yet, it will be used for:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/48483
class ResourceLabelEvent < ActiveRecord::Base
+ include Importable
+ include Gitlab::Utils::StrongMemoize
+ include CacheMarkdownField
+
+ cache_markdown_field :reference
+
belongs_to :user
belongs_to :issue
belongs_to :merge_request
belongs_to :label
- validates :user, presence: true, on: :create
- validates :label, presence: true, on: :create
+ scope :created_after, ->(time) { where('created_at > ?', time) }
+
+ validates :user, presence: { unless: :importing? }, on: :create
+ validates :label, presence: { unless: :importing? }, on: :create
validate :exactly_one_issuable
+ after_save :expire_etag_cache
+ after_destroy :expire_etag_cache
+
enum action: {
add: 1,
remove: 2
}
- def self.issuable_columns
- %i(issue_id merge_request_id).freeze
+ def self.issuable_attrs
+ %i(issue merge_request).freeze
end
def issuable
issue || merge_request
end
+ # create same discussion id for all actions with the same user and time
+ def discussion_id(resource = nil)
+ strong_memoize(:discussion_id) do
+ Digest::SHA1.hexdigest([self.class.name, created_at, user_id].join("-"))
+ end
+ end
+
+ def project
+ issuable.project
+ end
+
+ def group
+ issuable.group if issuable.respond_to?(:group)
+ end
+
+ def outdated_markdown?
+ return true if label_id.nil? && reference.present?
+
+ reference.nil? || latest_cached_markdown_version != cached_markdown_version
+ end
+
+ def banzai_render_context(field)
+ super.merge(pipeline: 'label', only_path: true)
+ end
+
+ def refresh_invalid_reference
+ # label_id could be nullified on label delete
+ self.reference = '' if label_id.nil?
+
+ # reference is not set for events which were not rendered yet
+ self.reference ||= label_reference
+
+ if changed?
+ save
+ elsif invalidated_markdown_cache?
+ refresh_markdown_cache!
+ end
+ end
+
private
+ def label_reference
+ if local_label?
+ label.to_reference(format: :id)
+ elsif label.is_a?(GroupLabel)
+ label.to_reference(label.group, target_project: resource_parent, format: :id)
+ else
+ label.to_reference(resource_parent, format: :id)
+ end
+ end
+
def exactly_one_issuable
- if self.class.issuable_columns.count { |attr| self[attr] } != 1
- errors.add(:base, "Exactly one of #{self.class.issuable_columns.join(', ')} is required")
+ issuable_count = self.class.issuable_attrs.count { |attr| self["#{attr}_id"] }
+
+ return true if issuable_count == 1
+
+ # if none of issuable IDs is set, check explicitly if nested issuable
+ # object is set, this is used during project import
+ if issuable_count == 0 && importing?
+ issuable_count = self.class.issuable_attrs.count { |attr| self.public_send(attr) } # rubocop:disable GitlabSecurity/PublicSend
+
+ return true if issuable_count == 1
end
+
+ errors.add(:base, "Exactly one of #{self.class.issuable_attrs.join(', ')} is required")
+ end
+
+ def expire_etag_cache
+ issuable.expire_note_etag_cache
+ end
+
+ def local_label?
+ params = { include_ancestor_groups: true }
+ if resource_parent.is_a?(Project)
+ params[:project_id] = resource_parent.id
+ else
+ params[:group_id] = resource_parent.id
+ end
+
+ LabelsFinder.new(nil, params).execute(skip_authorization: true).where(id: label.id).any?
+ end
+
+ def resource_parent
+ issuable.project || issuable.group
end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 140058771ee..5b8bf6e7cf0 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -5,6 +5,7 @@
class Service < ActiveRecord::Base
include Sortable
include Importable
+ include ProjectServicesLoggable
serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize
@@ -252,6 +253,7 @@ class Service < ActiveRecord::Base
bugzilla
campfire
custom_issue_tracker
+ discord
drone_ci
emails_on_push
external_wiki
diff --git a/app/models/shard.rb b/app/models/shard.rb
new file mode 100644
index 00000000000..2e75bc91df0
--- /dev/null
+++ b/app/models/shard.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class Shard < ActiveRecord::Base
+ # Store shard names from the configuration file in the database. This is not a
+ # list of active shards - we just want to assign an immutable, unique ID to
+ # every shard name for easy indexing / referencing.
+ def self.populate!
+ return unless table_exists?
+
+ # The GitLab config does not change for the lifecycle of the process
+ in_config = Gitlab.config.repositories.storages.keys.map(&:to_s)
+ in_db = all.pluck(:name)
+
+ # This may race with other processes creating shards at the same time, but
+ # `by_name` will handle that correctly
+ missing = in_config - in_db
+ missing.map { |name| by_name(name) }
+ end
+
+ def self.by_name(name)
+ find_or_create_by(name: name)
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+end
diff --git a/app/models/site_statistic.rb b/app/models/site_statistic.rb
index daac1c57db9..3a7912ed53a 100644
--- a/app/models/site_statistic.rb
+++ b/app/models/site_statistic.rb
@@ -4,7 +4,7 @@ class SiteStatistic < ActiveRecord::Base
# prevents the creation of multiple rows
default_value_for :id, 1
- COUNTER_ATTRIBUTES = %w(repositories_count wikis_count).freeze
+ COUNTER_ATTRIBUTES = %w(repositories_count).freeze
REQUIRED_SCHEMA_VERSION = 20180629153018
# Tracks specific attribute
@@ -49,7 +49,7 @@ class SiteStatistic < ActiveRecord::Base
#
# @return [SiteStatistic] record with tracked information
def self.fetch
- SiteStatistic.transaction(requires_new: true) do
+ transaction(requires_new: true) do
SiteStatistic.first_or_create!
end
rescue ActiveRecord::RecordNotUnique
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 5b394e3fa79..11856b55902 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -2,6 +2,7 @@
class Snippet < ActiveRecord::Base
include Gitlab::VisibilityLevel
+ include Redactable
include CacheMarkdownField
include Noteable
include Participable
@@ -12,11 +13,14 @@ class Snippet < ActiveRecord::Base
include Spammable
include Editable
include Gitlab::SQL::Pattern
+ include FromUnion
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description
cache_markdown_field :content
+ redact_field :description
+
# Aliases to make application_helper#edited_time_ago_with_tooltip helper work properly with snippets.
# See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10392/diffs#note_28719102
alias_attribute :last_edited_at, :updated_at
@@ -59,6 +63,62 @@ class Snippet < ActiveRecord::Base
attr_spammable :title, spam_title: true
attr_spammable :content, spam_description: true
+ def self.with_optional_visibility(value = nil)
+ if value
+ where(visibility_level: value)
+ else
+ all
+ end
+ end
+
+ def self.only_global_snippets
+ where(project_id: nil)
+ end
+
+ def self.only_include_projects_visible_to(current_user = nil)
+ levels = Gitlab::VisibilityLevel.levels_for_user(current_user)
+
+ joins(:project).where('projects.visibility_level IN (?)', levels)
+ end
+
+ def self.only_include_projects_with_snippets_enabled(include_private: false)
+ column = ProjectFeature.access_level_attribute(:snippets)
+ levels = [ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
+
+ levels << ProjectFeature::PRIVATE if include_private
+
+ joins(project: :project_feature)
+ .where(project_features: { column => levels })
+ end
+
+ def self.only_include_authorized_projects(current_user)
+ where(
+ 'EXISTS (?)',
+ ProjectAuthorization
+ .select(1)
+ .where('project_id = snippets.project_id')
+ .where(user_id: current_user.id)
+ )
+ end
+
+ def self.for_project_with_user(project, user = nil)
+ return none unless project.snippets_visible?(user)
+
+ if user && project.team.member?(user)
+ project.snippets
+ else
+ project.snippets.public_to_user(user)
+ end
+ end
+
+ def self.visible_to_or_authored_by(user)
+ where(
+ 'snippets.visibility_level IN (?) OR snippets.author_id = ?',
+ Gitlab::VisibilityLevel.levels_for_user(user),
+ user.id
+ )
+ end
+
def self.reference_prefix
'$'
end
@@ -77,27 +137,6 @@ class Snippet < ActiveRecord::Base
@link_reference_pattern ||= super("snippets", /(?<snippet>\d+)/)
end
- # Returns a collection of snippets that are either public or visible to the
- # logged in user.
- #
- # This method does not verify the user actually has the access to the project
- # the snippet is in, so it should be only used on a relation that's already scoped
- # for project access
- def self.public_or_visible_to_user(user = nil)
- if user
- authorized = user
- .project_authorizations
- .select(1)
- .where('project_authorizations.project_id = snippets.project_id')
-
- levels = Gitlab::VisibilityLevel.levels_for_user(user)
-
- where('EXISTS (?) OR snippets.visibility_level IN (?) or snippets.author_id = (?)', authorized, levels, user.id)
- else
- public_to_user
- end
- end
-
def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{id}"
diff --git a/app/models/ssh_host_key.rb b/app/models/ssh_host_key.rb
new file mode 100644
index 00000000000..b6844dbe870
--- /dev/null
+++ b/app/models/ssh_host_key.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+# Detected SSH host keys are transiently stored in Redis
+class SshHostKey
+ class Fingerprint < Gitlab::SSHPublicKey
+ attr_reader :index
+
+ def initialize(key, index: nil)
+ super(key)
+
+ @index = index
+ end
+
+ def as_json(*)
+ { bits: bits, fingerprint: fingerprint, type: type, index: index }
+ end
+ end
+
+ include ReactiveCaching
+
+ self.reactive_cache_key = ->(key) { [key.class.to_s, key.id] }
+
+ # Do not refresh the data in the background - it is not expected to change.
+ # This is achieved by making the lifetime shorter than the refresh interval.
+ self.reactive_cache_refresh_interval = 15.minutes
+ self.reactive_cache_lifetime = 10.minutes
+
+ def self.find_by(opts = {})
+ return nil unless opts.key?(:id)
+
+ project_id, url = opts[:id].split(':', 2)
+ project = Project.find_by(id: project_id)
+
+ project.presence && new(project: project, url: url)
+ end
+
+ def self.fingerprint_host_keys(data)
+ return [] unless data.is_a?(String)
+
+ data
+ .each_line
+ .each_with_index
+ .map { |line, index| Fingerprint.new(line, index: index) }
+ .select(&:valid?)
+ end
+
+ attr_reader :project, :url, :compare_host_keys
+
+ def initialize(project:, url:, compare_host_keys: nil)
+ @project = project
+ @url = normalize_url(url)
+ @compare_host_keys = compare_host_keys
+ end
+
+ def id
+ [project.id, url].join(':')
+ end
+
+ def as_json(*)
+ {
+ host_keys_changed: host_keys_changed?,
+ fingerprints: fingerprints,
+ known_hosts: known_hosts
+ }
+ end
+
+ def known_hosts
+ with_reactive_cache { |data| data[:known_hosts] }
+ end
+
+ def fingerprints
+ @fingerprints ||= self.class.fingerprint_host_keys(known_hosts)
+ end
+
+ # Returns true if the known_hosts data differs from the version passed in at
+ # initialization as `compare_host_keys`. Comments, ordering, etc, is ignored
+ def host_keys_changed?
+ cleanup(known_hosts) != cleanup(compare_host_keys)
+ end
+
+ def error
+ with_reactive_cache { |data| data[:error] }
+ end
+
+ def calculate_reactive_cache
+ known_hosts, errors, status =
+ Open3.popen3({}, *%W[ssh-keyscan -T 5 -p #{url.port} -f-]) do |stdin, stdout, stderr, wait_thr|
+ stdin.puts(url.host)
+ stdin.close
+
+ [
+ cleanup(stdout.read),
+ cleanup(stderr.read),
+ wait_thr.value
+ ]
+ end
+
+ # ssh-keyscan returns an exit code 0 in several error conditions, such as an
+ # unknown hostname, so check both STDERR and the exit code
+ if status.success? && !errors.present?
+ { known_hosts: known_hosts }
+ else
+ Rails.logger.debug("Failed to detect SSH host keys for #{id}: #{errors}")
+
+ { error: 'Failed to detect SSH host keys' }
+ end
+ end
+
+ private
+
+ # Remove comments and duplicate entries
+ def cleanup(data)
+ data
+ .to_s
+ .each_line
+ .reject { |line| line.start_with?('#') || line.chomp.empty? }
+ .uniq
+ .sort
+ .join
+ end
+
+ def normalize_url(url)
+ full_url = ::Addressable::URI.parse(url)
+ raise ArgumentError.new("Invalid URL") unless full_url&.scheme == 'ssh'
+
+ Addressable::URI.parse("ssh://#{full_url.host}:#{full_url.inferred_port}")
+ rescue Addressable::URI::InvalidURIError
+ raise ArgumentError.new("Invalid URL")
+ end
+end
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index 376ef673ca8..d555ebe5322 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -9,13 +9,14 @@ class SystemNoteMetadata < ActiveRecord::Base
TYPES_WITH_CROSS_REFERENCES = %w[
commit cross_reference
close duplicate
+ moved
].freeze
ICON_TYPES = %w[
commit description merge confidential visible label assignee cross_reference
title time_tracking branch milestone discussion task moved
opened closed merged duplicate locked unlocked
- outdated tag
+ outdated tag due_date
].freeze
validates :note, presence: true
@@ -26,4 +27,8 @@ class SystemNoteMetadata < ActiveRecord::Base
def icon_types
ICON_TYPES
end
+
+ def cross_reference_types
+ TYPES_WITH_CROSS_REFERENCES
+ end
end
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 48d92ad04b3..7b64615f699 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -2,6 +2,7 @@
class Todo < ActiveRecord::Base
include Sortable
+ include FromUnion
ASSIGNED = 1
MENTIONED = 2
@@ -39,6 +40,13 @@ class Todo < ActiveRecord::Base
scope :pending, -> { with_state(:pending) }
scope :done, -> { with_state(:done) }
+ scope :for_action, -> (action) { where(action: action) }
+ scope :for_author, -> (author) { where(author: author) }
+ scope :for_project, -> (project) { where(project: project) }
+ scope :for_group, -> (group) { where(group: group) }
+ scope :for_type, -> (type) { where(target_type: type) }
+ scope :for_target, -> (id) { where(target_id: id) }
+ scope :for_commit, -> (id) { where(commit_id: id) }
state_machine :state, initial: :pending do
event :done do
@@ -52,6 +60,42 @@ class Todo < ActiveRecord::Base
after_save :keep_around_commit, if: :commit_id
class << self
+ # Returns all todos for the given group and its descendants.
+ #
+ # group - A `Group` to retrieve todos for.
+ #
+ # Returns an `ActiveRecord::Relation`.
+ def for_group_and_descendants(group)
+ groups = group.self_and_descendants
+
+ from_union([
+ for_project(Project.for_group(groups)),
+ for_group(groups)
+ ])
+ end
+
+ # Returns `true` if the current user has any todos for the given target.
+ #
+ # target - The value of the `target_type` column, such as `Issue`.
+ def any_for_target?(target)
+ exists?(target: target)
+ end
+
+ # Updates the state of a relation of todos to the new state.
+ #
+ # new_state - The new state of the todos.
+ #
+ # Returns an `Array` containing the IDs of the updated todos.
+ def update_state(new_state)
+ # Only update those that are not really on that state
+ base = where.not(state: new_state).except(:order)
+ ids = base.pluck(:id)
+
+ base.update_all(state: new_state)
+
+ ids
+ end
+
# Priority sorting isn't displayed in the dropdown, because we don't show
# milestones, but still show something if the user has a URL with that
# selected.
diff --git a/app/models/tree.rb b/app/models/tree.rb
index 3641c33254c..cd385872171 100644
--- a/app/models/tree.rb
+++ b/app/models/tree.rb
@@ -2,6 +2,7 @@
class Tree
include Gitlab::MarkupHelper
+ include Gitlab::Utils::StrongMemoize
attr_accessor :repository, :sha, :path, :entries
@@ -16,32 +17,36 @@ class Tree
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive)
end
- def readme
- return @readme if defined?(@readme)
-
- available_readmes = blobs.select do |blob|
- Gitlab::FileDetector.type_of(blob.name) == :readme
- end
-
- previewable_readmes = available_readmes.select do |blob|
- previewable?(blob.name)
- end
-
- plain_readmes = available_readmes.select do |blob|
- plain?(blob.name)
+ def readme_path
+ strong_memoize(:readme_path) do
+ available_readmes = blobs.select do |blob|
+ Gitlab::FileDetector.type_of(blob.name) == :readme
+ end
+
+ previewable_readmes = available_readmes.select do |blob|
+ previewable?(blob.name)
+ end
+
+ plain_readmes = available_readmes.select do |blob|
+ plain?(blob.name)
+ end
+
+ # Prioritize previewable over plain readmes
+ entry = previewable_readmes.first || plain_readmes.first
+ next nil unless entry
+
+ if path == '/'
+ entry.name
+ else
+ File.join(path, entry.name)
+ end
end
+ end
- # Prioritize previewable over plain readmes
- readme_tree = previewable_readmes.first || plain_readmes.first
-
- # Return if we can't preview any of them
- if readme_tree.nil?
- return @readme = nil
+ def readme
+ strong_memoize(:readme) do
+ repository.blob_at(sha, readme_path) if readme_path
end
-
- readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name)
-
- @readme = repository.blob_at(sha, readme_path)
end
def trees
diff --git a/app/models/upload.rb b/app/models/upload.rb
index 23bc9ca42fc..e01e9c6a4f0 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -11,7 +11,8 @@ class Upload < ActiveRecord::Base
validates :model, presence: true
validates :uploader, presence: true
- scope :with_files_stored_locally, -> { where(store: [nil, ObjectStorage::Store::LOCAL]) }
+ scope :with_files_stored_locally, -> { where(store: ObjectStorage::Store::LOCAL) }
+ scope :with_files_stored_remotely, -> { where(store: ObjectStorage::Store::REMOTE) }
before_save :calculate_checksum!, if: :foreground_checksummable?
after_commit :schedule_checksum, if: :checksummable?
@@ -46,7 +47,18 @@ class Upload < ActiveRecord::Base
end
def exist?
- File.exist?(absolute_path)
+ exist = File.exist?(absolute_path)
+
+ # Help sysadmins find missing upload files
+ if persisted? && !exist
+ if Gitlab::Sentry.enabled?
+ Raven.capture_message("Upload file does not exist", extra: self.attributes)
+ end
+
+ Gitlab::Metrics.counter(:upload_file_does_not_exist_total, 'The number of times an upload record could not find its file').increment
+ end
+
+ exist
end
def uploader_context
@@ -57,8 +69,6 @@ class Upload < ActiveRecord::Base
end
def local?
- return true if store.nil?
-
store == ObjectStorage::Store::LOCAL
end
diff --git a/app/models/user.rb b/app/models/user.rb
index fb19de4b980..01eba7e0426 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -19,6 +19,8 @@ class User < ActiveRecord::Base
include BulkMemberAccessLoad
include BlocksJsonSerialization
include WithUploads
+ include OptionallySearch
+ include FromUnion
DEFAULT_NOTIFICATION_LEVEL = :participating
@@ -26,7 +28,7 @@ class User < ActiveRecord::Base
ignore_column :email_provider
ignore_column :authentication_token
- add_authentication_token_field :incoming_email_token
+ add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) }
add_authentication_token_field :feed_token
default_value_for :admin, false
@@ -60,6 +62,7 @@ class User < ActiveRecord::Base
# Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour
+ # rubocop: disable CodeReuse/ServiceClass
def update_tracked_fields!(request)
return if Gitlab::Database.read_only?
@@ -70,6 +73,7 @@ class User < ActiveRecord::Base
Users::UpdateService.new(self, user: self).execute(validate: false)
end
+ # rubocop: enable CodeReuse/ServiceClass
attr_accessor :force_random_password
@@ -84,7 +88,7 @@ class User < ActiveRecord::Base
has_one :namespace, -> { where(type: nil) }, dependent: :destroy, foreign_key: :owner_id, inverse_of: :owner, autosave: true # rubocop:disable Cop/ActiveRecordDependent
# Profile
- has_many :keys, -> { where(type: ['Key', nil]) }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :keys, -> { regular_keys }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :nullify # rubocop:disable Cop/ActiveRecordDependent
has_many :gpg_keys
@@ -148,6 +152,7 @@ class User < ActiveRecord::Base
belongs_to :accepted_term, class_name: 'ApplicationSetting::Term'
has_one :status, class_name: 'UserStatus'
+ has_one :user_preference
#
# Validations
@@ -158,6 +163,7 @@ class User < ActiveRecord::Base
validates :notification_email, presence: true
validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
+ validates :commit_email, email: true, allow_nil: true, if: ->(user) { user.commit_email != user.email }
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit,
presence: true,
@@ -170,12 +176,15 @@ class User < ActiveRecord::Base
validate :unique_email, if: :email_changed?
validate :owns_notification_email, if: :notification_email_changed?
validate :owns_public_email, if: :public_email_changed?
+ validate :owns_commit_email, if: :commit_email_changed?
validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
before_validation :sanitize_attrs
before_validation :set_notification_email, if: :new_record?
before_validation :set_public_email, if: :public_email_changed?
+ before_validation :set_commit_email, if: :commit_email_changed?
before_save :set_public_email, if: :public_email_changed? # in case validation is skipped
+ before_save :set_commit_email, if: :commit_email_changed? # in case validation is skipped
before_save :ensure_incoming_email_token
before_save :ensure_user_rights_and_limits, if: ->(user) { user.new_record? || user.external_changed? }
before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) }
@@ -209,13 +218,15 @@ class User < ActiveRecord::Base
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
- enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity, :groups, :todos, :issues, :merge_requests]
+ enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity, :groups, :todos, :issues, :merge_requests, :operations]
# User's Project preference
# Note: When adding an option, it MUST go on the end of the array.
enum project_view: [:readme, :activity, :files]
delegate :path, to: :namespace, allow_nil: true, prefix: true
+ delegate :notes_filter_for, to: :user_preference
+ delegate :set_notes_filter, to: :user_preference
state_machine :state, initial: :active do
event :block do
@@ -253,18 +264,51 @@ class User < ActiveRecord::Base
scope :external, -> { where(external: true) }
scope :active, -> { with_state(:active).non_internal }
scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) }
- scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) }
scope :order_recent_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'DESC')) }
scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
+ scope :by_username, -> (usernames) { iwhere(username: Array(usernames).map(&:to_s)) }
+ scope :for_todos, -> (todos) { where(id: todos.select(:user_id)) }
- def self.with_two_factor_indistinct
- joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
- .where("u2f.id IS NOT NULL OR users.otp_required_for_login = ?", true)
+ # Limits the users to those that have TODOs, optionally in the given state.
+ #
+ # user - The user to get the todos for.
+ #
+ # with_todos - If we should limit the result set to users that are the
+ # authors of todos.
+ #
+ # todo_state - An optional state to require the todos to be in.
+ def self.limit_to_todo_authors(user: nil, with_todos: false, todo_state: nil)
+ if user && with_todos
+ where(id: Todo.where(user: user, state: todo_state).select(:author_id))
+ else
+ all
+ end
+ end
+
+ # Returns a relation that optionally includes the given user.
+ #
+ # user_id - The ID of the user to include.
+ def self.union_with_user(user_id = nil)
+ if user_id.present?
+ # We use "unscoped" here so that any inner conditions are not repeated for
+ # the outer query, which would be redundant.
+ User.unscoped.from_union([all, User.unscoped.where(id: user_id)])
+ else
+ all
+ end
end
def self.with_two_factor
- with_two_factor_indistinct.distinct(arel_table[:id])
+ with_u2f_registrations = <<-SQL
+ EXISTS (
+ SELECT *
+ FROM u2f_registrations AS u2f
+ WHERE u2f.user_id = users.id
+ ) OR users.otp_required_for_login = ?
+ SQL
+
+ where(with_u2f_registrations, true)
end
def self.without_two_factor
@@ -303,19 +347,36 @@ class User < ActiveRecord::Base
# Find a User by their primary email or any associated secondary email
def find_by_any_email(email, confirmed: false)
+ return unless email
+
by_any_email(email, confirmed: confirmed).take
end
- # Returns a relation containing all the users for the given Email address
- def by_any_email(email, confirmed: false)
- users = where(email: email)
- users = users.confirmed if confirmed
+ # Returns a relation containing all the users for the given email addresses
+ #
+ # @param emails [String, Array<String>] email addresses to check
+ # @param confirmed [Boolean] Only return users where the email is confirmed
+ def by_any_email(emails, confirmed: false)
+ emails = Array(emails).map(&:downcase)
+
+ from_users = where(email: emails)
+ from_users = from_users.confirmed if confirmed
+
+ from_emails = joins(:emails).where(emails: { email: emails })
+ from_emails = from_emails.confirmed if confirmed
+
+ items = [from_users, from_emails]
+
+ user_ids = Gitlab::PrivateCommitEmail.user_ids_for_emails(emails)
+ items << where(id: user_ids) if user_ids.present?
+
+ from_union(items)
+ end
- emails = joins(:emails).where(emails: { email: email })
- emails = emails.confirmed if confirmed
- union = Gitlab::SQL::Union.new([users, emails])
+ def find_by_private_commit_email(email)
+ user_id = Gitlab::PrivateCommitEmail.user_id_for_email(email)
- from("(#{union.to_sql}) #{table_name}")
+ find_by(id: user_id)
end
def filter(filter_name)
@@ -365,6 +426,18 @@ class User < ActiveRecord::Base
).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
end
+ # Limits the result set to users _not_ in the given query/list of IDs.
+ #
+ # users - The list of users to ignore. This can be an
+ # `ActiveRecord::Relation`, or an Array.
+ def where_not_in(users = nil)
+ users ? where.not(id: users) : all
+ end
+
+ def reorder_by_name
+ reorder(:name)
+ end
+
# searches user by given pattern
# it compares name, email, username fields and user's secondary emails with given pattern
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
@@ -398,17 +471,11 @@ class User < ActiveRecord::Base
end
def find_by_username(username)
- iwhere(username: username).take
+ by_username(username).take
end
def find_by_username!(username)
- iwhere(username: username).take!
- end
-
- def find_by_personal_access_token(token_string)
- return unless token_string
-
- PersonalAccessTokensFinder.new(state: 'active').find_by(token: token_string)&.user
+ by_username(username).take!
end
# Returns a user for the given SSH key.
@@ -443,6 +510,16 @@ class User < ActiveRecord::Base
u.name = 'Ghost User'
end
end
+
+ # Return true if there is only single non-internal user in the deployment,
+ # ghost user is ignored.
+ def single_user?
+ User.non_internal.limit(2).count == 1
+ end
+
+ def single_user
+ User.non_internal.first if single_user?
+ end
end
def full_path
@@ -516,7 +593,7 @@ class User < ActiveRecord::Base
otp_grace_period_started_at: nil,
otp_backup_codes: nil
)
- self.u2f_registrations.destroy_all
+ self.u2f_registrations.destroy_all # rubocop: disable DestroyAll
end
end
@@ -560,6 +637,40 @@ class User < ActiveRecord::Base
errors.add(:public_email, "is not an email you own") unless all_emails.include?(public_email)
end
+ def owns_commit_email
+ return if read_attribute(:commit_email).blank?
+
+ errors.add(:commit_email, "is not an email you own") unless verified_emails.include?(commit_email)
+ end
+
+ # Define commit_email-related attribute methods explicitly instead of relying
+ # on ActiveRecord to provide them. Some of the specs use the current state of
+ # the model code but an older database schema, so we need to guard against the
+ # possibility of the commit_email column not existing.
+
+ def commit_email
+ return self.email unless has_attribute?(:commit_email)
+
+ if super == Gitlab::PrivateCommitEmail::TOKEN
+ return private_commit_email
+ end
+
+ # The commit email is the same as the primary email if undefined
+ super.presence || self.email
+ end
+
+ def commit_email=(email)
+ super if has_attribute?(:commit_email)
+ end
+
+ def commit_email_changed?
+ has_attribute?(:commit_email) && super
+ end
+
+ def private_commit_email
+ Gitlab::PrivateCommitEmail.for_user(self)
+ end
+
# see if the new email is already a verified secondary email
def check_for_verified_email
skip_reconfirmation! if emails.confirmed.where(email: self.email).any?
@@ -570,6 +681,7 @@ class User < ActiveRecord::Base
# hash and `_was` variables getting munged.
# By using an `after_commit` instead of `after_update`, we avoid the recursive callback
# scenario, though it then requires us to use the `previous_changes` hash
+ # rubocop: disable CodeReuse/ServiceClass
def update_emails_with_primary_email(previous_email)
primary_email_record = emails.find_by(email: email)
Emails::DestroyService.new(self, user: self).execute(primary_email_record) if primary_email_record
@@ -578,6 +690,7 @@ class User < ActiveRecord::Base
# have access to the original confirmation values at this point, so just set confirmed_at
Emails::CreateService.new(self, user: self, email: previous_email).execute(confirmed_at: confirmed_at)
end
+ # rubocop: enable CodeReuse/ServiceClass
def update_invalid_gpg_signatures
gpg_keys.each(&:update_invalid_gpg_signatures)
@@ -585,10 +698,12 @@ class User < ActiveRecord::Base
# Returns the groups a user has access to, either through a membership or a project authorization
def authorized_groups
- union = Gitlab::SQL::Union
- .new([groups.select(:id), authorized_projects.select(:namespace_id)])
-
- Group.where("namespaces.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
+ Group.unscoped do
+ Group.from_union([
+ groups,
+ authorized_projects.joins(:namespace).select('namespaces.*')
+ ])
+ end
end
# Returns the groups a user is a member of, either directly or through a parent group
@@ -606,9 +721,11 @@ class User < ActiveRecord::Base
all_expanded_groups.where(require_two_factor_authentication: true)
end
+ # rubocop: disable CodeReuse/ServiceClass
def refresh_authorized_projects
Users::RefreshAuthorizedProjectsService.new(self).execute
end
+ # rubocop: enable CodeReuse/ServiceClass
def remove_project_authorizations(project_ids)
project_authorizations.where(project_id: project_ids).delete_all
@@ -651,7 +768,15 @@ class User < ActiveRecord::Base
end
def owned_projects
- @owned_projects ||= Project.from("(#{owned_projects_union.to_sql}) AS projects")
+ @owned_projects ||= Project.from_union(
+ [
+ Project.where(namespace: namespace),
+ Project.joins(:project_authorizations)
+ .where("projects.namespace_id <> ?", namespace.id)
+ .where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER })
+ ],
+ remove_duplicates: false
+ )
end
# Returns projects which user can admin issues on (for example to move an issue to that project).
@@ -661,11 +786,13 @@ class User < ActiveRecord::Base
authorized_projects(Gitlab::Access::REPORTER).non_archived.with_issues_enabled
end
+ # rubocop: disable CodeReuse/ServiceClass
def require_ssh_key?
count = Users::KeysCountService.new(self).count
count.zero? && Gitlab::ProtocolAccess.allowed?('ssh')
end
+ # rubocop: enable CodeReuse/ServiceClass
def require_password_creation_for_web?
allow_password_authentication_for_web? && password_automatically_set?
@@ -729,6 +856,7 @@ class User < ActiveRecord::Base
projects_limit - personal_projects_count
end
+ # rubocop: disable CodeReuse/ServiceClass
def recent_push(project = nil)
service = Users::LastPushEventService.new(self)
@@ -738,6 +866,7 @@ class User < ActiveRecord::Base
service.last_event_for_user
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def several_namespaces?
owned_groups.any? || maintainers_groups.any?
@@ -806,10 +935,17 @@ class User < ActiveRecord::Base
end
end
+ def set_commit_email
+ if commit_email.blank? || verified_emails.exclude?(commit_email)
+ self.commit_email = nil
+ end
+ end
+
def update_secondary_emails!
set_notification_email
set_public_email
- save if notification_email_changed? || public_email_changed?
+ set_commit_email
+ save if notification_email_changed? || public_email_changed? || commit_email_changed?
end
def set_projects_limit
@@ -825,12 +961,17 @@ class User < ActiveRecord::Base
if !Gitlab.config.ldap.enabled
false
elsif ldap_user?
- !last_credential_check_at || (last_credential_check_at + 1.hour) < Time.now
+ !last_credential_check_at || (last_credential_check_at + ldap_sync_time) < Time.now
else
false
end
end
+ def ldap_sync_time
+ # This number resides in this method so it can be redefined in EE.
+ 1.hour
+ end
+
def try_obtain_ldap_lease
# After obtaining this lease LDAP checks will be blocked for 600 seconds
# (10 minutes) for this user.
@@ -875,9 +1016,11 @@ class User < ActiveRecord::Base
email.start_with?('temp-email-for-oauth')
end
+ # rubocop: disable CodeReuse/ServiceClass
def avatar_url(size: nil, scale: 2, **args)
GravatarService.new.execute(email, size, scale, username: username)
end
+ # rubocop: enable CodeReuse/ServiceClass
def primary_email_verified?
confirmed? && !temp_oauth_email?
@@ -896,6 +1039,7 @@ class User < ActiveRecord::Base
def all_emails
all_emails = []
all_emails << email unless temp_oauth_email?
+ all_emails << private_commit_email
all_emails.concat(emails.map(&:email))
all_emails
end
@@ -903,13 +1047,29 @@ class User < ActiveRecord::Base
def verified_emails
verified_emails = []
verified_emails << email if primary_email_verified?
+ verified_emails << private_commit_email
verified_emails.concat(emails.confirmed.pluck(:email))
verified_emails
end
+ def any_email?(check_email)
+ downcased = check_email.downcase
+
+ # handle the outdated private commit email case
+ return true if persisted? &&
+ id == Gitlab::PrivateCommitEmail.user_id_for_email(downcased)
+
+ all_emails.include?(check_email.downcase)
+ end
+
def verified_email?(check_email)
downcased = check_email.downcase
- email == downcased ? primary_email_verified? : emails.confirmed.where(email: downcased).exists?
+
+ # handle the outdated private commit email case
+ return true if persisted? &&
+ id == Gitlab::PrivateCommitEmail.user_id_for_email(downcased)
+
+ verified_emails.include?(check_email.downcase)
end
def hook_attrs
@@ -943,26 +1103,32 @@ class User < ActiveRecord::Base
system_hook_service.execute_hooks_for(self, :destroy)
end
+ # rubocop: disable CodeReuse/ServiceClass
def remove_key_cache
Users::KeysCountService.new(self).delete_cache
end
+ # rubocop: enable CodeReuse/ServiceClass
def delete_async(deleted_by:, params: {})
block if params[:hard_delete]
DeleteUserWorker.perform_async(deleted_by.id, id, params.to_h)
end
+ # rubocop: disable CodeReuse/ServiceClass
def notification_service
NotificationService.new
end
+ # rubocop: enable CodeReuse/ServiceClass
def log_info(message)
Gitlab::AppLogger.info message
end
+ # rubocop: disable CodeReuse/ServiceClass
def system_hook_service
SystemHooksService.new
end
+ # rubocop: enable CodeReuse/ServiceClass
def starred?(project)
starred_projects.exists?(project.id)
@@ -1014,7 +1180,7 @@ class User < ActiveRecord::Base
events = Event.select(:project_id)
.contributions.where(author_id: self)
.where("created_at > ?", Time.now - 1.year)
- .uniq
+ .distinct
.reorder(nil)
Project.where(id: events)
@@ -1026,17 +1192,17 @@ class User < ActiveRecord::Base
def ci_owned_runners
@ci_owned_runners ||= begin
- project_runner_ids = Ci::RunnerProject
+ project_runners = Ci::RunnerProject
.where(project: authorized_projects(Gitlab::Access::MAINTAINER))
- .select(:runner_id)
+ .joins(:runner)
+ .select('ci_runners.*')
- group_runner_ids = Ci::RunnerNamespace
+ group_runners = Ci::RunnerNamespace
.where(namespace_id: owned_or_maintainers_groups.select(:id))
- .select(:runner_id)
+ .joins(:runner)
+ .select('ci_runners.*')
- union = Gitlab::SQL::Union.new([project_runner_ids, group_runner_ids])
-
- Ci::Runner.where("ci_runners.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
+ Ci::Runner.from_union([project_runners, group_runners])
end
end
@@ -1064,13 +1230,13 @@ class User < ActiveRecord::Base
def assigned_open_merge_requests_count(force: false)
Rails.cache.fetch(['users', id, 'assigned_open_merge_requests_count'], force: force, expires_in: 20.minutes) do
- MergeRequestsFinder.new(self, assignee_id: self.id, state: 'opened').execute.count
+ MergeRequestsFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count
end
end
def assigned_open_issues_count(force: false)
Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force, expires_in: 20.minutes) do
- IssuesFinder.new(self, assignee_id: self.id, state: 'opened').execute.count
+ IssuesFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count
end
end
@@ -1131,6 +1297,7 @@ class User < ActiveRecord::Base
# See:
# <https://github.com/plataformatec/devise/blob/v4.0.0/lib/devise/models/lockable.rb#L92>
#
+ # rubocop: disable CodeReuse/ServiceClass
def increment_failed_attempts!
return if ::Gitlab::Database.read_only?
@@ -1143,6 +1310,7 @@ class User < ActiveRecord::Base
Users::UpdateService.new(self, user: self).execute(validate: false)
end
end
+ # rubocop: enable CodeReuse/ServiceClass
def access_level
if admin?
@@ -1240,6 +1408,19 @@ class User < ActiveRecord::Base
!terms_accepted?
end
+ def requires_usage_stats_consent?
+ !consented_usage_stats? && 7.days.ago > self.created_at && !has_current_license? && User.single_user?
+ end
+
+ # Avoid migrations only building user preference object when needed.
+ def user_preference
+ super.presence || build_user_preference
+ end
+
+ def todos_limited_to(ids)
+ todos.where(id: ids)
+ end
+
# @deprecated
alias_method :owned_or_masters_groups, :owned_or_maintainers_groups
@@ -1254,13 +1435,12 @@ class User < ActiveRecord::Base
private
- def owned_projects_union
- Gitlab::SQL::Union.new([
- Project.where(namespace: namespace),
- Project.joins(:project_authorizations)
- .where("projects.namespace_id <> ?", namespace.id)
- .where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER })
- ], remove_duplicates: false)
+ def has_current_license?
+ false
+ end
+
+ def consented_usage_stats?
+ Gitlab::CurrentSettings.usage_stats_set_by_user_id == self.id
end
# Added according to https://github.com/plataformatec/devise/blob/7df57d5081f9884849ca15e4fde179ef164a575f/README.md#activejob-integration
@@ -1326,15 +1506,6 @@ class User < ActiveRecord::Base
end
end
- def generate_token(token_field)
- if token_field == :incoming_email_token
- # Needs to be all lowercase and alphanumeric because it's gonna be used in an email address.
- SecureRandom.hex.to_i(16).to_s(36)
- else
- super
- end
- end
-
def self.unique_internal(scope, username, email_pattern, &block)
scope.first || create_unique_internal(scope, username, email_pattern, &block)
end
@@ -1371,7 +1542,7 @@ class User < ActiveRecord::Base
&creation_block
)
- Users::UpdateService.new(user, user: user).execute(validate: false)
+ Users::UpdateService.new(user, user: user).execute(validate: false) # rubocop: disable CodeReuse/ServiceClass
user
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb
index 97e955ace36..76e7bc06b4e 100644
--- a/app/models/user_callout.rb
+++ b/app/models/user_callout.rb
@@ -3,10 +3,9 @@
class UserCallout < ActiveRecord::Base
belongs_to :user
- enum feature_name: {
- gke_cluster_integration: 1,
- gcp_signup_offer: 2
- }
+ # We use `UserCalloutEnums.feature_names` here so that EE can more easily
+ # extend this `Hash` with new values.
+ enum feature_name: ::UserCalloutEnums.feature_names
validates :user, presence: true
validates :feature_name,
diff --git a/app/models/user_callout_enums.rb b/app/models/user_callout_enums.rb
new file mode 100644
index 00000000000..b9373ae6166
--- /dev/null
+++ b/app/models/user_callout_enums.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module UserCalloutEnums
+ # Returns the `Hash` to use for the `feature_name` enum in the `UserCallout`
+ # model.
+ #
+ # This method is separate from the `UserCallout` model so that it can be
+ # extended by EE.
+ def self.feature_names
+ {
+ gke_cluster_integration: 1,
+ gcp_signup_offer: 2,
+ cluster_security_warning: 3
+ }
+ end
+end
diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb
new file mode 100644
index 00000000000..32d0407800f
--- /dev/null
+++ b/app/models/user_preference.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+class UserPreference < ActiveRecord::Base
+ # We could use enums, but Rails 4 doesn't support multiple
+ # enum options with same name for multiple fields, also it creates
+ # extra methods that aren't really needed here.
+ NOTES_FILTERS = { all_notes: 0, only_comments: 1, only_activity: 2 }.freeze
+
+ belongs_to :user
+
+ validates :issue_notes_filter, :merge_request_notes_filter, inclusion: { in: NOTES_FILTERS.values }, presence: true
+
+ class << self
+ def notes_filters
+ {
+ s_('Notes|Show all activity') => NOTES_FILTERS[:all_notes],
+ s_('Notes|Show comments only') => NOTES_FILTERS[:only_comments],
+ s_('Notes|Show history only') => NOTES_FILTERS[:only_activity]
+ }
+ end
+ end
+
+ def set_notes_filter(filter_id, issuable)
+ # No need to update the column if the value is already set.
+ if filter_id && NOTES_FILTERS.values.include?(filter_id)
+ field = notes_filter_field_for(issuable)
+ self[field] = filter_id
+
+ save if attribute_changed?(field)
+ end
+
+ notes_filter_for(issuable)
+ end
+
+ # Returns the current discussion filter for a given issuable
+ # or issuable type.
+ def notes_filter_for(resource)
+ self[notes_filter_field_for(resource)]
+ end
+
+ private
+
+ def notes_filter_field_for(resource)
+ field_key =
+ if resource.is_a?(Issuable)
+ resource.model_name.param_key
+ else
+ resource
+ end
+
+ "#{field_key}_notes_filter"
+ end
+end
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 33790afc35e..7769c3d71c0 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -51,14 +51,14 @@ class WikiPage
validates :title, presence: true
validates :content, presence: true
- # The Gitlab ProjectWiki instance.
+ # The GitLab ProjectWiki instance.
attr_reader :wiki
# The raw Gitlab::Git::WikiPage instance.
attr_reader :page
# The attributes Hash used for storing and validating
- # new Page values before writing to the Gollum repository.
+ # new Page values before writing to the raw repository.
attr_accessor :attributes
def hook_attrs
@@ -111,10 +111,7 @@ class WikiPage
# The processed/formatted content of this page.
def formatted_content
- # Assuming @page exists, nil formatted_data means we didn't load it
- # before hand (i.e. page was fetched by Gitaly), so we fetch it separately.
- # If the page was fetched by Gollum, formatted_data would've been a String.
- @attributes[:formatted_content] ||= @page&.formatted_data || @wiki.page_formatted_data(@page)
+ @attributes[:formatted_content] ||= @wiki.page_formatted_data(@page)
end
# The markup format for the page.
@@ -127,7 +124,7 @@ class WikiPage
version.try(:message)
end
- # The Gitlab Commit instance for this page.
+ # The GitLab Commit instance for this page.
def version
return nil unless persisted?
@@ -154,16 +151,12 @@ class WikiPage
last_version&.sha
end
- # Returns the Date that this latest version was
- # created on.
- def created_at
- @page.version.date
- end
-
# Returns boolean True or False if this instance
# is an old version of the page.
def historical?
- @page.historical? && last_version.sha != version.sha
+ return false unless last_commit_sha && version
+
+ @page.historical? && last_commit_sha != version.sha
end
# Returns boolean True or False if this instance
@@ -196,7 +189,7 @@ class WikiPage
update_attributes(attrs)
save(page_details: title) do
- wiki.create_page(title, content, format, message)
+ wiki.create_page(title, content, format, attrs[:message])
end
end
diff --git a/app/policies/application_setting/term_policy.rb b/app/policies/application_setting/term_policy.rb
index 17f00f33d35..c0d2ceaa349 100644
--- a/app/policies/application_setting/term_policy.rb
+++ b/app/policies/application_setting/term_policy.rb
@@ -19,6 +19,7 @@ class ApplicationSetting
rule { terms_accepted }.prevent :accept_terms
+ # rubocop: disable CodeReuse/ActiveRecord
def agreement
strong_memoize(:agreement) do
next nil if @user.nil? || @subject.nil?
@@ -26,5 +27,6 @@ class ApplicationSetting
@user.term_agreements.find_by(term: @subject)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb
index 3858b29c82c..0ca3e696f46 100644
--- a/app/policies/ci/build_policy.rb
+++ b/app/policies/ci/build_policy.rb
@@ -20,12 +20,17 @@ module Ci
@subject.project.branch_allows_collaboration?(@user, @subject.ref)
end
+ condition(:archived, scope: :subject) do
+ @subject.archived?
+ end
+
condition(:terminal, scope: :subject) do
@subject.has_terminal?
end
- rule { protected_ref }.policy do
+ rule { protected_ref | archived }.policy do
prevent :update_build
+ prevent :update_commit_status
prevent :erase_build
end
diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb
index f9623587957..e42d78f47c5 100644
--- a/app/policies/ci/pipeline_policy.rb
+++ b/app/policies/ci/pipeline_policy.rb
@@ -16,6 +16,10 @@ module Ci
enable :update_pipeline
end
+ rule { can?(:owner_access) }.policy do
+ enable :destroy_pipeline
+ end
+
def ref_protected?(user, project, tag, ref)
access = ::Gitlab::UserAccess.new(user, project: project)
diff --git a/app/policies/ci/runner_policy.rb b/app/policies/ci/runner_policy.rb
index c44f22b6ad3..de76b7b2b5b 100644
--- a/app/policies/ci/runner_policy.rb
+++ b/app/policies/ci/runner_policy.rb
@@ -5,7 +5,9 @@ module Ci
with_options scope: :subject, score: 0
condition(:locked, scope: :subject) { @subject.locked? }
+ # rubocop: disable CodeReuse/ActiveRecord
condition(:owned_runner) { @user.ci_owned_runners.exists?(@subject.id) }
+ # rubocop: enable CodeReuse/ActiveRecord
rule { anonymous }.prevent_all
diff --git a/app/policies/clusters/cluster_policy.rb b/app/policies/clusters/cluster_policy.rb
index 147943a3d6c..d6d590687e2 100644
--- a/app/policies/clusters/cluster_policy.rb
+++ b/app/policies/clusters/cluster_policy.rb
@@ -4,11 +4,7 @@ module Clusters
class ClusterPolicy < BasePolicy
alias_method :cluster, :subject
+ delegate { cluster.group }
delegate { cluster.project }
-
- rule { can?(:maintainer_access) }.policy do
- enable :update_cluster
- enable :admin_cluster
- end
end
end
diff --git a/app/policies/deploy_key_policy.rb b/app/policies/deploy_key_policy.rb
index 204c54a5b20..7f0ec011e79 100644
--- a/app/policies/deploy_key_policy.rb
+++ b/app/policies/deploy_key_policy.rb
@@ -4,7 +4,9 @@ class DeployKeyPolicy < BasePolicy
with_options scope: :subject, score: 0
condition(:private_deploy_key) { @subject.private? }
+ # rubocop: disable CodeReuse/ActiveRecord
condition(:has_deploy_key) { @user.project_deploy_keys.exists?(id: @subject.id) }
+ # rubocop: enable CodeReuse/ActiveRecord
rule { anonymous }.prevent_all
diff --git a/app/policies/deployment_policy.rb b/app/policies/deployment_policy.rb
index 56ac898b6ab..d4f2f3c52b1 100644
--- a/app/policies/deployment_policy.rb
+++ b/app/policies/deployment_policy.rb
@@ -2,4 +2,13 @@
class DeploymentPolicy < BasePolicy
delegate { @subject.project }
+
+ condition(:can_retry_deployable) do
+ can?(:update_build, @subject.deployable)
+ end
+
+ rule { ~can_retry_deployable }.policy do
+ prevent :create_deployment
+ prevent :update_deployment
+ end
end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index a8d7a05f509..6b4e56ef5e4 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -53,7 +53,7 @@ class GroupPolicy < BasePolicy
rule { has_access }.enable :read_namespace
- rule { developer }.enable :admin_milestones
+ rule { developer }.enable :admin_milestone
rule { reporter }.policy do
enable :admin_label
@@ -65,6 +65,10 @@ class GroupPolicy < BasePolicy
enable :create_projects
enable :admin_pipeline
enable :admin_build
+ enable :read_cluster
+ enable :create_cluster
+ enable :update_cluster
+ enable :admin_cluster
end
rule { owner }.policy do
@@ -72,6 +76,8 @@ class GroupPolicy < BasePolicy
enable :admin_namespace
enable :admin_group_member
enable :change_visibility_level
+
+ enable :set_note_created_at
end
rule { can?(:read_nested_project_resources) }.policy do
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
index 198bb168d85..6d8b575102e 100644
--- a/app/policies/issuable_policy.rb
+++ b/app/policies/issuable_policy.rb
@@ -14,6 +14,7 @@ class IssuablePolicy < BasePolicy
rule { assignee_or_author }.policy do
enable :read_issue
enable :update_issue
+ enable :reopen_issue
enable :read_merge_request
enable :update_merge_request
end
diff --git a/app/policies/issue_policy.rb b/app/policies/issue_policy.rb
index 94b5f37c682..a0706eaa46c 100644
--- a/app/policies/issue_policy.rb
+++ b/app/policies/issue_policy.rb
@@ -19,4 +19,8 @@ class IssuePolicy < IssuablePolicy
prevent :update_issue
prevent :admin_issue
end
+
+ rule { locked }.policy do
+ prevent :reopen_issue
+ end
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 00c58f15013..1c082945299 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -110,6 +110,7 @@ class ProjectPolicy < BasePolicy
snippets
wiki
builds
+ pages
]
features.each do |f|
@@ -143,6 +144,10 @@ class ProjectPolicy < BasePolicy
enable :destroy_merge_request
enable :destroy_issue
enable :remove_pages
+
+ enable :set_issue_iid
+ enable :set_issue_created_at
+ enable :set_note_created_at
end
rule { can?(:guest_access) }.policy do
@@ -163,6 +168,7 @@ class ProjectPolicy < BasePolicy
enable :upload_file
enable :read_cycle_analytics
enable :award_emoji
+ enable :read_pages_content
end
# These abilities are not allowed to admins that are not members of the project,
@@ -176,6 +182,7 @@ class ProjectPolicy < BasePolicy
enable :fork_project
enable :create_project_snippet
enable :update_issue
+ enable :reopen_issue
enable :admin_issue
enable :admin_label
enable :admin_list
@@ -251,6 +258,8 @@ class ProjectPolicy < BasePolicy
enable :update_pages
enable :read_cluster
enable :create_cluster
+ enable :update_cluster
+ enable :admin_cluster
enable :create_environment_terminal
end
@@ -281,6 +290,8 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:merge_request))
end
+ rule { pages_disabled }.prevent :read_pages_content
+
rule { issues_disabled & merge_requests_disabled }.policy do
prevent(*create_read_update_admin_destroy(:label))
prevent(*create_read_update_admin_destroy(:milestone))
@@ -340,6 +351,7 @@ class ProjectPolicy < BasePolicy
enable :download_code
enable :download_wiki_code
enable :read_cycle_analytics
+ enable :read_pages_content
# NOTE: may be overridden by IssuePolicy
enable :read_issue
@@ -385,7 +397,11 @@ class ProjectPolicy < BasePolicy
greedy_load_subject ||= !@user.persisted?
if greedy_load_subject
- project.team.members.include?(user)
+ # We want to load all the members with one query. Calling #include? on
+ # project.team.members will perform a separate query for each user, unless
+ # project.team.members was loaded before somewhere else. Calling #to_a
+ # ensures it's always loaded before checking for membership.
+ project.team.members.to_a.include?(user)
else
# otherwise we just make a specific query for
# this particular user.
@@ -393,6 +409,7 @@ class ProjectPolicy < BasePolicy
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def project_group_member?
return false if @user.nil?
@@ -402,6 +419,7 @@ class ProjectPolicy < BasePolicy
project.group.requesters.exists?(user_id: @user.id)
)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def team_access_level
return -1 if @user.nil?
diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb
new file mode 100644
index 00000000000..6323c1b3389
--- /dev/null
+++ b/app/presenters/blob_presenter.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class BlobPresenter < Gitlab::View::Presenter::Simple
+ presents :blob
+
+ def highlight(plain: nil)
+ blob.load_all_data! if blob.respond_to?(:load_all_data!)
+
+ Gitlab::Highlight.highlight(
+ blob.path,
+ blob.data,
+ language: blob.language_from_gitattributes,
+ plain: plain
+ )
+ end
+end
diff --git a/app/presenters/ci/build_presenter.rb b/app/presenters/ci/build_presenter.rb
index 5331cdf632b..33056a809b7 100644
--- a/app/presenters/ci/build_presenter.rb
+++ b/app/presenters/ci/build_presenter.rb
@@ -35,6 +35,10 @@ module Ci
"#{subject.name} - #{detailed_status.status_tooltip}"
end
+ def execute_in
+ scheduled? && scheduled_at && [0, scheduled_at - Time.now].max
+ end
+
private
def tooltip_for_badge
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
index 880218e2727..300f85e1e9d 100644
--- a/app/presenters/ci/build_runner_presenter.rb
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -30,12 +30,12 @@ module Ci
def create_reports(reports, expire_in:)
return unless reports&.any?
- reports.map do |k, v|
+ reports.map do |report_type, report_paths|
{
- artifact_type: k.to_sym,
- artifact_format: :gzip,
- name: ::Ci::JobArtifact::DEFAULT_FILE_NAMES[k.to_sym],
- paths: v,
+ artifact_type: report_type.to_sym,
+ artifact_format: ::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS.fetch(report_type.to_sym),
+ name: ::Ci::JobArtifact::DEFAULT_FILE_NAMES.fetch(report_type.to_sym),
+ paths: report_paths,
when: 'always',
expire_in: expire_in
}
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index 93a38f92073..57daf04efc6 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -4,9 +4,11 @@ module Ci
class PipelinePresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
- FAILURE_REASONS = {
- config_error: 'CI/CD YAML configuration error!'
- }.freeze
+ # We use a class method here instead of a constant, allowing EE to redefine
+ # the returned `Hash` more easily.
+ def self.failure_reasons
+ { config_error: 'CI/CD YAML configuration error!' }
+ end
presents :pipeline
@@ -21,7 +23,7 @@ module Ci
def failure_reason
return unless pipeline.failure_reason?
- FAILURE_REASONS[pipeline.failure_reason.to_sym] ||
+ self.class.failure_reasons[pipeline.failure_reason.to_sym] ||
pipeline.failure_reason
end
diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb
new file mode 100644
index 00000000000..9cc137aa3bd
--- /dev/null
+++ b/app/presenters/clusterable_presenter.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+class ClusterablePresenter < Gitlab::View::Presenter::Delegated
+ presents :clusterable
+
+ def self.fabricate(clusterable, **attributes)
+ presenter_class = "#{clusterable.class.name}ClusterablePresenter".constantize
+ attributes_with_presenter_class = attributes.merge(presenter_class: presenter_class)
+
+ Gitlab::View::Presenter::Factory
+ .new(clusterable, attributes_with_presenter_class)
+ .fabricate!
+ end
+
+ def can_create_cluster?
+ can?(current_user, :create_cluster, clusterable)
+ end
+
+ def index_path
+ polymorphic_path([clusterable, :clusters])
+ end
+
+ def new_path
+ new_polymorphic_path([clusterable, :cluster])
+ end
+
+ def create_user_clusters_path
+ polymorphic_path([clusterable, :clusters], action: :create_user)
+ end
+
+ def create_gcp_clusters_path
+ polymorphic_path([clusterable, :clusters], action: :create_gcp)
+ end
+
+ def cluster_status_cluster_path(cluster, params = {})
+ raise NotImplementedError
+ end
+
+ def install_applications_cluster_path(cluster, application)
+ raise NotImplementedError
+ end
+
+ def cluster_path(cluster, params = {})
+ raise NotImplementedError
+ end
+
+ def empty_state_help_text
+ nil
+ end
+
+ def sidebar_text
+ raise NotImplementedError
+ end
+
+ def learn_more_link
+ raise NotImplementedError
+ end
+end
diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb
index dfdd8e82f97..7e6eccb648c 100644
--- a/app/presenters/clusters/cluster_presenter.rb
+++ b/app/presenters/clusters/cluster_presenter.rb
@@ -11,5 +11,15 @@ module Clusters
def can_toggle_cluster?
can?(current_user, :update_cluster, cluster) && created?
end
+
+ def show_path
+ if cluster.project_type?
+ project_cluster_path(project, cluster)
+ elsif cluster.group_type?
+ group_cluster_path(group, cluster)
+ else
+ raise NotImplementedError
+ end
+ end
end
end
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index a08f34e2335..0cd77da6303 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -8,13 +8,22 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
stuck_or_timeout_failure: 'There has been a timeout failure or the job got stuck. Check your timeout limits or try again',
runner_system_failure: 'There has been a runner system failure, please try again',
missing_dependency_failure: 'There has been a missing dependency failure',
- runner_unsupported: 'Your runner is outdated, please upgrade your runner'
+ runner_unsupported: 'Your runner is outdated, please upgrade your runner',
+ stale_schedule: 'Delayed job could not be executed by some reason, please try again',
+ job_execution_timeout: 'The script exceeded the maximum execution time set for the job',
+ archived_failure: 'The job is archived and cannot be run'
}.freeze
+ private_constant :CALLOUT_FAILURE_MESSAGES
+
presents :build
+ def self.callout_failure_messages
+ CALLOUT_FAILURE_MESSAGES
+ end
+
def callout_failure_message
- CALLOUT_FAILURE_MESSAGES.fetch(failure_reason.to_sym)
+ self.class.callout_failure_messages.fetch(failure_reason.to_sym)
end
def recoverable?
@@ -22,6 +31,6 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
end
def unrecoverable?
- script_failure? || missing_dependency_failure?
+ script_failure? || missing_dependency_failure? || archived_failure?
end
end
diff --git a/app/presenters/conversational_development_index/metric_presenter.rb b/app/presenters/conversational_development_index/metric_presenter.rb
index e0312c6f431..9639b84cf56 100644
--- a/app/presenters/conversational_development_index/metric_presenter.rb
+++ b/app/presenters/conversational_development_index/metric_presenter.rb
@@ -139,8 +139,10 @@ module ConversationalDevelopmentIndex
]
end
+ # rubocop: disable CodeReuse/ActiveRecord
def average_percentage_score
cards.sum(&:percentage_score) / cards.size.to_f
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/presenters/group_clusterable_presenter.rb b/app/presenters/group_clusterable_presenter.rb
new file mode 100644
index 00000000000..d963c188559
--- /dev/null
+++ b/app/presenters/group_clusterable_presenter.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+class GroupClusterablePresenter < ClusterablePresenter
+ extend ::Gitlab::Utils::Override
+ include ActionView::Helpers::UrlHelper
+
+ override :cluster_status_cluster_path
+ def cluster_status_cluster_path(cluster, params = {})
+ cluster_status_group_cluster_path(clusterable, cluster, params)
+ end
+
+ override :install_applications_cluster_path
+ def install_applications_cluster_path(cluster, application)
+ install_applications_group_cluster_path(clusterable, cluster, application)
+ end
+
+ override :cluster_path
+ def cluster_path(cluster, params = {})
+ group_cluster_path(clusterable, cluster, params)
+ end
+
+ override :empty_state_help_text
+ def empty_state_help_text
+ s_('ClusterIntegration|Adding an integration to your group will share the cluster across all your projects.')
+ end
+
+ override :sidebar_text
+ def sidebar_text
+ s_('ClusterIntegration|Adding a Kubernetes cluster to your group will automatically share the cluster across all your projects. Use review apps, deploy your applications, and easily run your pipelines for all projects using the same cluster.')
+ end
+
+ override :learn_more_link
+ def learn_more_link
+ link_to(s_('ClusterIntegration|Learn more about group Kubernetes clusters'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ end
+end
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index 8c4eac3c31d..1db6c9eff36 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -108,16 +108,10 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
namespace = source_project_namespace
branch = source_branch
- if source_branch_exists?
- namespace = link_to(namespace, project_path(source_project))
- branch = link_to(branch, project_tree_path(source_project, source_branch))
- end
+ namespace_link = source_branch_exists? ? link_to(namespace, project_path(source_project)) : ERB::Util.html_escape(namespace)
+ branch_link = source_branch_exists? ? link_to(branch, project_tree_path(source_project, source_branch)) : ERB::Util.html_escape(branch)
- if for_fork?
- namespace + ":" + branch
- else
- branch
- end
+ for_fork? ? "#{namespace_link}:#{branch_link}" : branch_link
end
def closing_issues_links
@@ -142,6 +136,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
def assign_to_closing_issues_link
+ # rubocop: disable CodeReuse/ServiceClass
issues = MergeRequests::AssignIssuesService.new(project,
current_user,
merge_request: merge_request,
@@ -152,6 +147,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
pluralize_this_issue = issues.count > 1 ? "these issues" : "this issue"
link_to "Assign yourself to #{pluralize_this_issue}", path, method: :post
end
+ # rubocop: enable CodeReuse/ServiceClass
end
def can_revert_on_current_merge_request?
@@ -202,7 +198,9 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
def conflicts
+ # rubocop: disable CodeReuse/ServiceClass
@conflicts ||= MergeRequests::Conflicts::ListService.new(merge_request)
+ # rubocop: enable CodeReuse/ServiceClass
end
def closing_issues
diff --git a/app/presenters/project_clusterable_presenter.rb b/app/presenters/project_clusterable_presenter.rb
new file mode 100644
index 00000000000..63e69b91b11
--- /dev/null
+++ b/app/presenters/project_clusterable_presenter.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class ProjectClusterablePresenter < ClusterablePresenter
+ extend ::Gitlab::Utils::Override
+ include ActionView::Helpers::UrlHelper
+
+ override :cluster_status_cluster_path
+ def cluster_status_cluster_path(cluster, params = {})
+ cluster_status_project_cluster_path(clusterable, cluster, params)
+ end
+
+ override :install_applications_cluster_path
+ def install_applications_cluster_path(cluster, application)
+ install_applications_project_cluster_path(clusterable, cluster, application)
+ end
+
+ override :cluster_path
+ def cluster_path(cluster, params = {})
+ project_cluster_path(clusterable, cluster, params)
+ end
+
+ override :sidebar_text
+ def sidebar_text
+ s_('ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way.')
+ end
+
+ override :learn_more_link
+ def learn_more_link
+ link_to(s_('ClusterIntegration|Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ end
+end
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 4c2f33213d6..d61124fa787 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -11,16 +11,18 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
presents :project
+ AnchorData = Struct.new(:enabled, :label, :link, :class_modifier)
+ MAX_TAGS_TO_SHOW = 3
+
def statistics_anchors(show_auto_devops_callout:)
[
+ readme_anchor_data,
+ changelog_anchor_data,
+ contribution_guide_anchor_data,
files_anchor_data,
commits_anchor_data,
branches_anchor_data,
tags_anchor_data,
- readme_anchor_data,
- changelog_anchor_data,
- license_anchor_data,
- contribution_guide_anchor_data,
gitlab_ci_anchor_data,
autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
kubernetes_cluster_anchor_data
@@ -31,17 +33,19 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
[
readme_anchor_data,
changelog_anchor_data,
- license_anchor_data,
contribution_guide_anchor_data,
autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
kubernetes_cluster_anchor_data,
- gitlab_ci_anchor_data,
- koding_anchor_data
+ gitlab_ci_anchor_data
].compact.reject { |item| item.enabled }
end
def empty_repo_statistics_anchors
[
+ files_anchor_data,
+ commits_anchor_data,
+ branches_anchor_data,
+ tags_anchor_data,
autodevops_anchor_data,
kubernetes_cluster_anchor_data
].compact.select { |item| item.enabled }
@@ -51,7 +55,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
[
new_file_anchor_data,
readme_anchor_data,
- license_anchor_data,
autodevops_anchor_data,
kubernetes_cluster_anchor_data
].compact.reject { |item| item.enabled }
@@ -121,43 +124,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
add_special_file_path(file_name: 'README.md')
end
- def add_koding_stack_path
- project_new_blob_path(
- project,
- default_branch || 'master',
- file_name: '.koding.yml',
- commit_message: "Add Koding stack script",
- content: <<-CONTENT.strip_heredoc
- provider:
- aws:
- access_key: '${var.aws_access_key}'
- secret_key: '${var.aws_secret_key}'
- resource:
- aws_instance:
- #{project.path}-vm:
- instance_type: t2.nano
- user_data: |-
-
- # Created by GitLab UI for :>
-
- echo _KD_NOTIFY_@Installing Base packages...@
-
- apt-get update -y
- apt-get install git -y
-
- echo _KD_NOTIFY_@Cloning #{project.name}...@
-
- export KODING_USER=${var.koding_user_username}
- export REPO_URL=#{root_url}${var.koding_queryString_repo}.git
- export BRANCH=${var.koding_queryString_branch}
-
- sudo -i -u $KODING_USER git clone $REPO_URL -b $BRANCH
-
- echo _KD_NOTIFY_@#{project.name} cloned.@
- CONTENT
- )
- end
-
def license_short_name
license = repository.license
license&.nickname || license&.name || 'LICENSE'
@@ -182,95 +148,101 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def files_anchor_data
- OpenStruct.new(enabled: true,
- label: _('Files (%{human_size})') % { human_size: storage_counter(statistics.total_repository_size) },
- link: project_tree_path(project))
+ AnchorData.new(true,
+ _('Files (%{human_size})') % { human_size: storage_counter(statistics.total_repository_size) },
+ empty_repo? ? nil : project_tree_path(project))
end
def commits_anchor_data
- OpenStruct.new(enabled: true,
- label: n_('Commit (%{commit_count})', 'Commits (%{commit_count})', statistics.commit_count) % { commit_count: number_with_delimiter(statistics.commit_count) },
- link: project_commits_path(project, repository.root_ref))
+ AnchorData.new(true,
+ n_('Commit (%{commit_count})', 'Commits (%{commit_count})', statistics.commit_count) % { commit_count: number_with_delimiter(statistics.commit_count) },
+ empty_repo? ? nil : project_commits_path(project, repository.root_ref))
end
def branches_anchor_data
- OpenStruct.new(enabled: true,
- label: n_('Branch (%{branch_count})', 'Branches (%{branch_count})', repository.branch_count) % { branch_count: number_with_delimiter(repository.branch_count) },
- link: project_branches_path(project))
+ AnchorData.new(true,
+ n_('Branch (%{branch_count})', 'Branches (%{branch_count})', repository.branch_count) % { branch_count: number_with_delimiter(repository.branch_count) },
+ empty_repo? ? nil : project_branches_path(project))
end
def tags_anchor_data
- OpenStruct.new(enabled: true,
- label: n_('Tag (%{tag_count})', 'Tags (%{tag_count})', repository.tag_count) % { tag_count: number_with_delimiter(repository.tag_count) },
- link: project_tags_path(project))
+ AnchorData.new(true,
+ n_('Tag (%{tag_count})', 'Tags (%{tag_count})', repository.tag_count) % { tag_count: number_with_delimiter(repository.tag_count) },
+ empty_repo? ? nil : project_tags_path(project))
end
def new_file_anchor_data
if current_user && can_current_user_push_to_default_branch?
- OpenStruct.new(enabled: false,
- label: _('New file'),
- link: project_new_blob_path(project, default_branch || 'master'),
- class_modifier: 'new')
+ AnchorData.new(false,
+ _('New file'),
+ project_new_blob_path(project, default_branch || 'master'),
+ 'success')
end
end
def readme_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.readme.nil?
- OpenStruct.new(enabled: false,
- label: _('Add Readme'),
- link: add_readme_path)
+ AnchorData.new(false,
+ _('Add Readme'),
+ add_readme_path)
elsif repository.readme
- OpenStruct.new(enabled: true,
- label: _('Readme'),
- link: default_view != 'readme' ? readme_path : '#readme')
+ AnchorData.new(true,
+ _('Readme'),
+ default_view != 'readme' ? readme_path : '#readme')
end
end
def changelog_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.changelog.blank?
- OpenStruct.new(enabled: false,
- label: _('Add Changelog'),
- link: add_changelog_path)
+ AnchorData.new(false,
+ _('Add Changelog'),
+ add_changelog_path)
elsif repository.changelog.present?
- OpenStruct.new(enabled: true,
- label: _('Changelog'),
- link: changelog_path)
+ AnchorData.new(true,
+ _('Changelog'),
+ changelog_path)
end
end
def license_anchor_data
- if current_user && can_current_user_push_to_default_branch? && repository.license_blob.blank?
- OpenStruct.new(enabled: false,
- label: _('Add License'),
- link: add_license_path)
- elsif repository.license_blob.present?
- OpenStruct.new(enabled: true,
- label: license_short_name,
- link: license_path)
+ if repository.license_blob.present?
+ AnchorData.new(true,
+ license_short_name,
+ license_path)
+ else
+ if current_user && can_current_user_push_to_default_branch?
+ AnchorData.new(false,
+ _('Add license'),
+ add_license_path)
+ else
+ AnchorData.new(false,
+ _('No license. All rights reserved'),
+ nil)
+ end
end
end
def contribution_guide_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.contribution_guide.blank?
- OpenStruct.new(enabled: false,
- label: _('Add Contribution guide'),
- link: add_contribution_guide_path)
+ AnchorData.new(false,
+ _('Add Contribution guide'),
+ add_contribution_guide_path)
elsif repository.contribution_guide.present?
- OpenStruct.new(enabled: true,
- label: _('Contribution guide'),
- link: contribution_guide_path)
+ AnchorData.new(true,
+ _('Contribution guide'),
+ contribution_guide_path)
end
end
def autodevops_anchor_data(show_auto_devops_callout: false)
if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
- OpenStruct.new(enabled: auto_devops_enabled?,
- label: auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
- link: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ AnchorData.new(auto_devops_enabled?,
+ auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
+ project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
elsif auto_devops_enabled?
- OpenStruct.new(enabled: true,
- label: _('Auto DevOps enabled'),
- link: nil)
+ AnchorData.new(true,
+ _('Auto DevOps enabled'),
+ nil)
end
end
@@ -282,32 +254,40 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
cluster_link = new_project_cluster_path(project)
end
- OpenStruct.new(enabled: !clusters.empty?,
- label: clusters.empty? ? _('Add Kubernetes cluster') : _('Kubernetes configured'),
- link: cluster_link)
+ AnchorData.new(!clusters.empty?,
+ clusters.empty? ? _('Add Kubernetes cluster') : _('Kubernetes configured'),
+ cluster_link)
end
end
def gitlab_ci_anchor_data
if current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled?
- OpenStruct.new(enabled: false,
- label: _('Set up CI/CD'),
- link: add_ci_yml_path)
+ AnchorData.new(false,
+ _('Set up CI/CD'),
+ add_ci_yml_path)
elsif repository.gitlab_ci_yml.present?
- OpenStruct.new(enabled: true,
- label: _('CI/CD configuration'),
- link: ci_configuration_path)
+ AnchorData.new(true,
+ _('CI/CD configuration'),
+ ci_configuration_path)
end
end
- def koding_anchor_data
- if current_user && can_current_user_push_code? && koding_enabled? && repository.koding_yml.blank?
- OpenStruct.new(enabled: false,
- label: _('Set up Koding'),
- link: add_koding_stack_path)
+ def tags_to_show
+ project.tag_list.take(MAX_TAGS_TO_SHOW) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ def count_of_extra_tags_not_shown
+ if project.tag_list.count > MAX_TAGS_TO_SHOW
+ project.tag_list.count - MAX_TAGS_TO_SHOW
+ else
+ 0
end
end
+ def has_extra_tags?
+ count_of_extra_tags_not_shown > 0
+ end
+
private
def filename_path(filename)
@@ -337,8 +317,4 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
branch_name: branch_name
)
end
-
- def koding_enabled?
- Gitlab::CurrentSettings.koding_enabled?
- end
end
diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb
index 28eaef00a12..85518c9a3a4 100644
--- a/app/presenters/projects/settings/deploy_keys_presenter.rb
+++ b/app/presenters/projects/settings/deploy_keys_presenter.rb
@@ -12,9 +12,11 @@ module Projects
@key ||= DeployKey.new.tap { |dk| dk.deploy_keys_projects.build }
end
+ # rubocop: disable CodeReuse/ActiveRecord
def enabled_keys
@enabled_keys ||= project.deploy_keys.includes(:projects)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def any_keys_enabled?
enabled_keys.any?
@@ -24,14 +26,17 @@ module Projects
@available_keys ||= current_user.accessible_deploy_keys - enabled_keys
end
+ # rubocop: disable CodeReuse/ActiveRecord
def available_project_keys
@available_project_keys ||= current_user.project_deploy_keys.includes(:projects) - enabled_keys
end
+ # rubocop: enable CodeReuse/ActiveRecord
def key_available?(deploy_key)
available_keys.include?(deploy_key)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def available_public_keys
return @available_public_keys if defined?(@available_public_keys)
@@ -41,9 +46,10 @@ module Projects
# in @available_project_keys.
@available_public_keys -= available_project_keys
end
+ # rubocop: enable CodeReuse/ActiveRecord
def as_json
- serializer = DeployKeySerializer.new
+ serializer = DeployKeySerializer.new # rubocop: disable CodeReuse/Serializer
opts = { user: current_user }
{
diff --git a/app/serializers/build_action_entity.rb b/app/serializers/build_action_entity.rb
index f9da3f63911..95833c3528f 100644
--- a/app/serializers/build_action_entity.rb
+++ b/app/serializers/build_action_entity.rb
@@ -12,6 +12,12 @@ class BuildActionEntity < Grape::Entity
end
expose :playable?, as: :playable
+ expose :scheduled?, as: :scheduled
+ expose :scheduled_at, if: -> (*) { scheduled? }
+
+ expose :unschedule_path, if: -> (build) { build.scheduled? } do |build|
+ unschedule_project_job_path(build.project, build)
+ end
private
@@ -20,4 +26,8 @@ class BuildActionEntity < Grape::Entity
def playable?
build.playable? && can?(request.current_user, :update_build, build)
end
+
+ def scheduled?
+ build.scheduled?
+ end
end
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index b887b99d31c..9ddce0d2c80 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -3,17 +3,52 @@
class BuildDetailsEntity < JobEntity
expose :coverage, :erased_at, :duration
expose :tag_list, as: :tags
+ expose :has_trace?, as: :has_trace
+ expose :stage
+ expose :stuck?, as: :stuck
expose :user, using: UserEntity
expose :runner, using: RunnerEntity
expose :pipeline, using: PipelineEntity
+ expose :deployment_status, if: -> (*) { build.starts_environment? } do
+ expose :deployment_status, as: :status
+
+ expose :persisted_environment, as: :environment, with: EnvironmentEntity
+ end
+
expose :metadata, using: BuildMetadataEntity
+ expose :artifact, if: -> (*) { can?(current_user, :read_build, build) } do
+ expose :download_path, if: -> (*) { build.artifacts? } do |build|
+ download_project_job_artifacts_path(project, build)
+ end
+
+ expose :browse_path, if: -> (*) { build.browsable_artifacts? } do |build|
+ browse_project_job_artifacts_path(project, build)
+ end
+
+ expose :keep_path, if: -> (*) { build.has_expiring_artifacts? && can?(current_user, :update_build, build) } do |build|
+ keep_project_job_artifacts_path(project, build)
+ end
+
+ expose :expire_at, if: -> (*) { build.artifacts_expire_at.present? } do |build|
+ build.artifacts_expire_at
+ end
+
+ expose :expired, if: -> (*) { build.artifacts_expire_at.present? } do |build|
+ build.artifacts_expired?
+ end
+ end
+
expose :erased_by, if: -> (*) { build.erased? }, using: UserEntity
expose :erase_path, if: -> (*) { build.erasable? && can?(current_user, :erase_build, build) } do |build|
erase_project_job_path(project, build)
end
+ expose :terminal_path, if: -> (*) { can_create_build_terminal? } do |build|
+ terminal_project_job_path(project, build)
+ end
+
expose :merge_request, if: -> (*) { can?(current_user, :read_merge_request, build.merge_request) } do
expose :iid do |build|
build.merge_request.iid
@@ -33,6 +68,26 @@ class BuildDetailsEntity < JobEntity
raw_project_job_path(project, build)
end
+ expose :trigger, if: -> (*) { build.trigger_request } do
+ expose :trigger_short_token, as: :short_token
+
+ expose :trigger_variables, as: :variables, using: TriggerVariableEntity
+ end
+
+ expose :runners do
+ expose :online do |build|
+ build.any_runners_online?
+ end
+
+ expose :available do |build|
+ project.any_runners?
+ end
+
+ expose :settings_path, if: -> (*) { can_admin_build? } do |build|
+ project_runners_path(project)
+ end
+ end
+
private
def build_failed_issue_options
@@ -47,4 +102,12 @@ class BuildDetailsEntity < JobEntity
def project
build.project
end
+
+ def can_create_build_terminal?
+ can?(current_user, :create_build_terminal, build) && build.has_terminal?
+ end
+
+ def can_admin_build?
+ can?(request.current_user, :admin_build, project)
+ end
end
diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb
index b3287c66554..a94e32478ce 100644
--- a/app/serializers/commit_entity.rb
+++ b/app/serializers/commit_entity.rb
@@ -1,19 +1,49 @@
# frozen_string_literal: true
class CommitEntity < API::Entities::Commit
+ include MarkupHelper
include RequestAwareEntity
expose :author, using: UserEntity
expose :author_gravatar_url do |commit|
- GravatarService.new.execute(commit.author_email)
+ GravatarService.new.execute(commit.author_email) # rubocop: disable CodeReuse/ServiceClass
end
- expose :commit_url do |commit|
- project_commit_url(request.project, commit)
+ expose :commit_url do |commit, options|
+ project_commit_url(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
- expose :commit_path do |commit|
- project_commit_path(request.project, commit)
+ expose :commit_path do |commit, options|
+ project_commit_path(request.project, commit, params: options.fetch(:commit_url_params, {}))
+ end
+
+ expose :description_html, if: { type: :full } do |commit|
+ markdown_field(commit, :description)
+ end
+
+ expose :title_html, if: { type: :full } do |commit|
+ markdown_field(commit, :title)
+ end
+
+ expose :signature_html, if: { type: :full } do |commit|
+ render('projects/commit/_signature', signature: commit.signature) if commit.has_signature?
+ end
+
+ expose :pipeline_status_path, if: { type: :full } do |commit, options|
+ pipeline_ref = options[:pipeline_ref]
+ pipeline_project = options[:pipeline_project] || commit.project
+ next unless pipeline_ref && pipeline_project
+
+ status = commit.status_for_project(pipeline_ref, pipeline_project)
+ next unless status
+
+ pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref)
+ end
+
+ def render(*args)
+ return unless request.respond_to?(:render) && request.render.respond_to?(:call)
+
+ request.render.call(*args)
end
end
diff --git a/app/serializers/current_user_entity.rb b/app/serializers/current_user_entity.rb
new file mode 100644
index 00000000000..71d14e727dd
--- /dev/null
+++ b/app/serializers/current_user_entity.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+# Always use this entity when rendering data for current user
+# for attributes that does not need to be visible to other users
+# like user preferences.
+class CurrentUserEntity < UserEntity
+ expose :user_preference, using: UserPreferenceEntity
+end
diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb
index 344148a1fb7..aa1d9e6292c 100644
--- a/app/serializers/deployment_entity.rb
+++ b/app/serializers/deployment_entity.rb
@@ -25,4 +25,5 @@ class DeploymentEntity < Grape::Entity
expose :commit, using: CommitEntity
expose :deployable, using: JobEntity
expose :manual_actions, using: JobEntity
+ expose :scheduled_actions, using: JobEntity
end
diff --git a/app/serializers/detailed_status_entity.rb b/app/serializers/detailed_status_entity.rb
new file mode 100644
index 00000000000..da994d78286
--- /dev/null
+++ b/app/serializers/detailed_status_entity.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class DetailedStatusEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :icon, :text, :label, :group
+ expose :status_tooltip, as: :tooltip
+ expose :has_details?, as: :has_details
+ expose :details_path
+
+ expose :illustration do |status|
+ begin
+ illustration = {
+ image: ActionController::Base.helpers.image_path(status.illustration[:image])
+ }
+ illustration = status.illustration.merge(illustration)
+
+ illustration
+ rescue NotImplementedError
+ # ignored
+ end
+ end
+
+ expose :favicon do |status|
+ Gitlab::Favicon.status_overlay(status.favicon)
+ end
+
+ expose :action, if: -> (status, _) { status.has_action? } do
+ expose :action_icon, as: :icon
+ expose :action_title, as: :title
+ expose :action_path, as: :path
+ expose :action_method, as: :method
+ expose :action_button_title, as: :button_title
+ end
+end
diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb
index 79844c9210a..63ea8e8f95f 100644
--- a/app/serializers/diff_file_entity.rb
+++ b/app/serializers/diff_file_entity.rb
@@ -2,7 +2,6 @@
class DiffFileEntity < Grape::Entity
include RequestAwareEntity
- include BlobHelper
include CommitsHelper
include DiffHelper
include SubmoduleHelper
@@ -85,7 +84,7 @@ class DiffFileEntity < Grape::Entity
end
expose :old_path_html do |diff_file|
- old_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path)
+ old_path, _ = mark_inline_diffs(diff_file.old_path, diff_file.new_path)
old_path
end
@@ -117,6 +116,10 @@ class DiffFileEntity < Grape::Entity
project_blob_path(project, tree_join(diff_file.content_sha, diff_file.new_path))
end
+ expose :viewer, using: DiffViewerEntity do |diff_file|
+ diff_file.rich_viewer || diff_file.simple_viewer
+ end
+
expose :replaced_view_path, if: -> (_, options) { options[:merge_request] } do |diff_file|
image_diff = diff_file.rich_viewer && diff_file.rich_viewer.partial_name == 'image'
image_replaced = diff_file.old_content_sha && diff_file.old_content_sha != diff_file.content_sha
@@ -136,12 +139,12 @@ class DiffFileEntity < Grape::Entity
end
# Used for inline diffs
- expose :highlighted_diff_lines, if: -> (diff_file, _) { diff_file.text? } do |diff_file|
+ expose :highlighted_diff_lines, using: DiffLineEntity, if: -> (diff_file, _) { diff_file.text? } do |diff_file|
diff_file.diff_lines_for_serializer
end
# Used for parallel diffs
- expose :parallel_diff_lines, if: -> (diff_file, _) { diff_file.text? }
+ expose :parallel_diff_lines, using: DiffLineParallelEntity, if: -> (diff_file, _) { diff_file.text? }
def current_user
request.current_user
diff --git a/app/serializers/diff_line_entity.rb b/app/serializers/diff_line_entity.rb
new file mode 100644
index 00000000000..942714b7787
--- /dev/null
+++ b/app/serializers/diff_line_entity.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class DiffLineEntity < Grape::Entity
+ expose :line_code
+ expose :type
+ expose :old_line
+ expose :new_line
+ expose :text
+ expose :meta_positions, as: :meta_data
+
+ expose :rich_text do |line|
+ ERB::Util.html_escape(line.rich_text || line.text)
+ end
+end
diff --git a/app/serializers/diff_line_parallel_entity.rb b/app/serializers/diff_line_parallel_entity.rb
new file mode 100644
index 00000000000..0438a67d51b
--- /dev/null
+++ b/app/serializers/diff_line_parallel_entity.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class DiffLineParallelEntity < Grape::Entity
+ expose :left, using: DiffLineEntity
+ expose :right, using: DiffLineEntity
+end
diff --git a/app/serializers/diff_line_serializer.rb b/app/serializers/diff_line_serializer.rb
new file mode 100644
index 00000000000..7f1f2d9aa7c
--- /dev/null
+++ b/app/serializers/diff_line_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class DiffLineSerializer < BaseSerializer
+ entity DiffLineEntity
+end
diff --git a/app/serializers/diff_viewer_entity.rb b/app/serializers/diff_viewer_entity.rb
new file mode 100644
index 00000000000..27fba03cb3f
--- /dev/null
+++ b/app/serializers/diff_viewer_entity.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class DiffViewerEntity < Grape::Entity
+ # Partial name refers directly to a Rails feature, let's avoid
+ # using this on the frontend.
+ expose :partial_name, as: :name
+end
diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb
index f75ace14d9c..b51e4a7e6d0 100644
--- a/app/serializers/diffs_entity.rb
+++ b/app/serializers/diffs_entity.rb
@@ -15,8 +15,13 @@ class DiffsEntity < Grape::Entity
merge_request&.target_branch
end
- expose :commit do |diffs|
- options[:commit]
+ expose :commit do |diffs, options|
+ CommitEntity.represent options[:commit], options.merge(
+ type: :full,
+ commit_url_params: { merge_request_iid: merge_request&.iid },
+ pipeline_ref: merge_request&.source_branch,
+ pipeline_project: merge_request&.source_project
+ )
end
expose :merge_request_diff, using: MergeRequestDiffEntity do |diffs|
@@ -35,13 +40,17 @@ class DiffsEntity < Grape::Entity
diffs_project_merge_request_path(merge_request&.project, merge_request)
end
+ # rubocop: disable CodeReuse/ActiveRecord
expose :added_lines do |diffs|
diffs.diff_files.sum(&:added_lines)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
expose :removed_lines do |diffs|
diffs.diff_files.sum(&:removed_lines)
end
+ # rubocop: enable CodeReuse/ActiveRecord
expose :render_overflow_warning do |diffs|
render_overflow_warning?(diffs.diff_files)
diff --git a/app/serializers/discussion_entity.rb b/app/serializers/discussion_entity.rb
index b8321037fa5..b6786a0d597 100644
--- a/app/serializers/discussion_entity.rb
+++ b/app/serializers/discussion_entity.rb
@@ -6,6 +6,7 @@ class DiscussionEntity < Grape::Entity
expose :id, :reply_id
expose :position, if: -> (d, _) { d.diff_discussion? && !d.legacy_diff_discussion? }
+ expose :original_position, if: -> (d, _) { d.diff_discussion? && !d.legacy_diff_discussion? }
expose :line_code, if: -> (d, _) { d.diff_discussion? }
expose :expanded?, as: :expanded
expose :active?, as: :active, if: -> (d, _) { d.diff_discussion? }
@@ -26,7 +27,7 @@ class DiscussionEntity < Grape::Entity
expose :resolved?, as: :resolved
expose :resolved_by_push?, as: :resolved_by_push
- expose :resolved_by
+ expose :resolved_by, using: NoteUserEntity
expose :resolved_at
expose :resolve_path, if: -> (d, _) { d.resolvable? } do |discussion|
resolve_project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion.id)
@@ -43,7 +44,7 @@ class DiscussionEntity < Grape::Entity
project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion)
end
- expose :truncated_diff_lines, if: -> (d, _) { d.diff_discussion? && d.on_text? && (d.expanded? || render_truncated_diff_lines?) }
+ expose :truncated_diff_lines, using: DiffLineEntity, if: -> (d, _) { d.diff_discussion? && d.on_text? && (d.expanded? || render_truncated_diff_lines?) }
expose :image_diff_html, if: -> (d, _) { d.diff_discussion? && d.on_image? } do |discussion|
diff_file = discussion.diff_file
diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb
index dc1686c30c4..598ce5f9e4f 100644
--- a/app/serializers/environment_serializer.rb
+++ b/app/serializers/environment_serializer.rb
@@ -29,6 +29,7 @@ class EnvironmentSerializer < BaseSerializer
private
+ # rubocop: disable CodeReuse/ActiveRecord
def itemize(resource)
items = resource.order('folder ASC')
.group('COALESCE(environment_type, name)')
@@ -46,4 +47,5 @@ class EnvironmentSerializer < BaseSerializer
Item.new(item.folder, item.size, environments[item.last_id])
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/serializers/environment_status_entity.rb b/app/serializers/environment_status_entity.rb
new file mode 100644
index 00000000000..f6321b9e520
--- /dev/null
+++ b/app/serializers/environment_status_entity.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+class EnvironmentStatusEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :id
+ expose :name
+ expose :status
+
+ expose :url do |es|
+ project_environment_path(es.project, es.environment)
+ end
+
+ expose :metrics_url, if: ->(*) { can_read_environment? && deployment.has_metrics? } do |es|
+ metrics_project_environment_deployment_path(es.project, es.environment, es.deployment)
+ end
+
+ expose :metrics_monitoring_url, if: ->(*) { can_read_environment? } do |es|
+ environment_metrics_path(es.environment)
+ end
+
+ expose :stop_url, if: ->(*) { can_stop_environment? } do |es|
+ stop_project_environment_path(es.project, es.environment)
+ end
+
+ expose :external_url do |es|
+ es.environment.external_url
+ end
+
+ expose :external_url_formatted do |es|
+ es.environment.formatted_external_url
+ end
+
+ expose :deployed_at
+
+ expose :deployed_at_formatted do |es|
+ es.deployment.try(:formatted_deployment_time)
+ end
+
+ expose :changes
+
+ private
+
+ def environment
+ object.environment
+ end
+
+ def deployment
+ object.deployment
+ end
+
+ def project
+ object.environment.project
+ end
+
+ def current_user
+ request.current_user
+ end
+
+ def can_read_environment?
+ can?(current_user, :read_environment, environment)
+ end
+
+ def can_stop_environment?
+ can?(current_user, :stop_environment, environment)
+ end
+end
diff --git a/app/serializers/environment_status_serializer.rb b/app/serializers/environment_status_serializer.rb
new file mode 100644
index 00000000000..f8d37934763
--- /dev/null
+++ b/app/serializers/environment_status_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class EnvironmentStatusSerializer < BaseSerializer
+ entity EnvironmentStatusEntity
+end
diff --git a/app/serializers/group_child_entity.rb b/app/serializers/group_child_entity.rb
index f6804fe7f6a..20d7032c970 100644
--- a/app/serializers/group_child_entity.rb
+++ b/app/serializers/group_child_entity.rb
@@ -66,11 +66,13 @@ class GroupChildEntity < Grape::Entity
private
+ # rubocop: disable CodeReuse/ActiveRecord
def membership
return unless request.current_user
@membership ||= request.current_user.members.find_by(source: object)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def project?
object.is_a?(Project)
diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb
index c46c342ee5d..0e1bc9a6b3d 100644
--- a/app/serializers/group_entity.rb
+++ b/app/serializers/group_entity.rb
@@ -17,9 +17,11 @@ class GroupEntity < Grape::Entity
end
expose :permissions do
+ # rubocop: disable CodeReuse/ActiveRecord
expose :human_group_access do |group, options|
group.group_members.find_by(user_id: request.current_user)&.human_access
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
expose :edit_path do |group|
diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb
index 16a477c92fa..c3f7d4651fb 100644
--- a/app/serializers/issue_entity.rb
+++ b/app/serializers/issue_entity.rb
@@ -42,6 +42,6 @@ class IssueEntity < IssuableEntity
end
expose :preview_note_path do |issue|
- preview_markdown_path(issue.project, quick_actions_target_type: 'Issue', quick_actions_target_id: issue.id)
+ preview_markdown_path(issue.project, quick_actions_target_type: 'Issue', quick_actions_target_id: issue.iid)
end
end
diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb
index 7bc1d87dea5..d0099ae77f2 100644
--- a/app/serializers/job_entity.rb
+++ b/app/serializers/job_entity.rb
@@ -7,9 +7,10 @@ class JobEntity < Grape::Entity
expose :name
expose :started?, as: :started
+ expose :archived?, as: :archived
expose :build_path do |build|
- build.target_url || path_to(:namespace_project_job, build)
+ build_path(build)
end
expose :retry_path, if: -> (*) { retryable? } do |build|
@@ -17,17 +18,27 @@ class JobEntity < Grape::Entity
end
expose :cancel_path, if: -> (*) { cancelable? } do |build|
- path_to(:cancel_namespace_project_job, build)
+ path_to(
+ :cancel_namespace_project_job,
+ build,
+ { continue: { to: build_path(build) } }
+ )
end
expose :play_path, if: -> (*) { playable? } do |build|
path_to(:play_namespace_project_job, build)
end
+ expose :unschedule_path, if: -> (*) { scheduled? } do |build|
+ path_to(:unschedule_namespace_project_job, build)
+ end
+
expose :playable?, as: :playable
+ expose :scheduled?, as: :scheduled
+ expose :scheduled_at, if: -> (*) { scheduled? }
expose :created_at
expose :updated_at
- expose :detailed_status, as: :status, with: StatusEntity
+ expose :detailed_status, as: :status, with: DetailedStatusEntity
expose :callout_message, if: -> (*) { failed? && !build.script_failure? }
expose :recoverable, if: -> (*) { failed? }
@@ -47,12 +58,20 @@ class JobEntity < Grape::Entity
build.playable? && can?(request.current_user, :update_build, build)
end
+ def scheduled?
+ build.scheduled?
+ end
+
def detailed_status
build.detailed_status(request.current_user)
end
- def path_to(route, build)
- send("#{route}_path", build.project.namespace, build.project, build) # rubocop:disable GitlabSecurity/PublicSend
+ def path_to(route, build, params = {})
+ send("#{route}_path", build.project.namespace, build.project, build, params) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ def build_path(build)
+ build.target_url || path_to(:namespace_project_job, build)
end
def failed?
diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb
index 0941a9d36be..0db7624b3f7 100644
--- a/app/serializers/job_group_entity.rb
+++ b/app/serializers/job_group_entity.rb
@@ -5,7 +5,7 @@ class JobGroupEntity < Grape::Entity
expose :name
expose :size
- expose :detailed_status, as: :status, with: StatusEntity
+ expose :detailed_status, as: :status, with: DetailedStatusEntity
expose :jobs, with: JobEntity
private
diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb
index fd2d2897113..53257b0602c 100644
--- a/app/serializers/merge_request_user_entity.rb
+++ b/app/serializers/merge_request_user_entity.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class MergeRequestUserEntity < UserEntity
+class MergeRequestUserEntity < CurrentUserEntity
include RequestAwareEntity
include BlobHelper
include TreeHelper
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index f55d448235a..f33a1654d5e 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -55,6 +55,7 @@ class MergeRequestWidgetEntity < IssuableEntity
expose :merge_commit_message
expose :actual_head_pipeline, with: PipelineDetailsEntity, as: :pipeline
+ expose :merge_pipeline, with: PipelineDetailsEntity, if: ->(mr, _) { mr.merged? && can?(request.current_user, :read_pipeline, mr.target_project)}
# Booleans
expose :merge_ongoing?, as: :merge_ongoing
@@ -222,7 +223,7 @@ class MergeRequestWidgetEntity < IssuableEntity
end
expose :preview_note_path do |merge_request|
- preview_markdown_path(merge_request.project, quick_actions_target_type: 'MergeRequest', quick_actions_target_id: merge_request.id)
+ preview_markdown_path(merge_request.project, quick_actions_target_type: 'MergeRequest', quick_actions_target_id: merge_request.iid)
end
expose :merge_commit_path do |merge_request|
@@ -243,7 +244,7 @@ class MergeRequestWidgetEntity < IssuableEntity
def presenter(merge_request)
@presenters ||= {}
- @presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user)
+ @presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user) # rubocop: disable CodeReuse/Presenter
end
# Once SchedulePopulateMergeRequestMetricsWithEventsData fully runs,
diff --git a/app/serializers/move_to_project_entity.rb b/app/serializers/move_to_project_entity.rb
new file mode 100644
index 00000000000..dac1124b0b3
--- /dev/null
+++ b/app/serializers/move_to_project_entity.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class MoveToProjectEntity < Grape::Entity
+ expose :id
+ expose :name_with_namespace
+end
diff --git a/app/serializers/move_to_project_serializer.rb b/app/serializers/move_to_project_serializer.rb
new file mode 100644
index 00000000000..6a59317505c
--- /dev/null
+++ b/app/serializers/move_to_project_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class MoveToProjectSerializer < BaseSerializer
+ entity MoveToProjectEntity
+end
diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb
index daa5c24d0f5..c6d27817411 100644
--- a/app/serializers/note_entity.rb
+++ b/app/serializers/note_entity.rb
@@ -4,6 +4,12 @@ class NoteEntity < API::Entities::Note
include RequestAwareEntity
include NotesHelper
+ expose :id do |note|
+ # resource events are represented as notes too, but don't
+ # have ID, discussion ID is used for them instead
+ note.id ? note.id.to_s : note.discussion_id
+ end
+
expose :type
expose :author, using: NoteUserEntity
@@ -46,8 +52,8 @@ class NoteEntity < API::Entities::Note
expose :emoji_awardable?, as: :emoji_awardable
expose :award_emoji, if: -> (note, _) { note.emoji_awardable? }, using: AwardEmojiEntity
- expose :report_abuse_path do |note|
- new_abuse_report_path(user_id: note.author.id, ref_url: Gitlab::UrlBuilder.build(note))
+ expose :report_abuse_path, if: -> (note, _) { note.author_id } do |note|
+ new_abuse_report_path(user_id: note.author_id, ref_url: Gitlab::UrlBuilder.build(note))
end
expose :noteable_note_url do |note|
diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb
index 3b56767f774..d78ad4af4dc 100644
--- a/app/serializers/pipeline_details_entity.rb
+++ b/app/serializers/pipeline_details_entity.rb
@@ -5,5 +5,6 @@ class PipelineDetailsEntity < PipelineEntity
expose :ordered_stages, as: :stages, using: StageEntity
expose :artifacts, using: BuildArtifactEntity
expose :manual_actions, using: BuildActionEntity
+ expose :scheduled_actions, using: BuildActionEntity
end
end
diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb
index 6cf1925adda..aef838409e0 100644
--- a/app/serializers/pipeline_entity.rb
+++ b/app/serializers/pipeline_entity.rb
@@ -30,7 +30,7 @@ class PipelineEntity < Grape::Entity
end
expose :details do
- expose :detailed_status, as: :status, with: StatusEntity
+ expose :detailed_status, as: :status, with: DetailedStatusEntity
expose :duration
expose :finished_at
end
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index 3205578b83e..7451433a841 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -4,6 +4,7 @@ class PipelineSerializer < BaseSerializer
include WithPagination
entity PipelineDetailsEntity
+ # rubocop: disable CodeReuse/ActiveRecord
def represent(resource, opts = {})
if resource.is_a?(ActiveRecord::Relation)
resource = resource.preload([
@@ -12,6 +13,7 @@ class PipelineSerializer < BaseSerializer
:cancelable_statuses,
:trigger_requests,
:manual_actions,
+ :scheduled_actions,
:artifacts,
{
pending_builds: :project,
@@ -33,6 +35,7 @@ class PipelineSerializer < BaseSerializer
super(resource, opts)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def represent_status(resource)
return {} unless resource.present?
diff --git a/app/serializers/project_note_entity.rb b/app/serializers/project_note_entity.rb
index d7c4d0aacc6..f6cdea1d8b5 100644
--- a/app/serializers/project_note_entity.rb
+++ b/app/serializers/project_note_entity.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class ProjectNoteEntity < NoteEntity
- expose :human_access do |note|
+ expose :human_access, if: -> (note, _) { note.project.present? } do |note|
note.project.team.human_max_access(note.author_id)
end
@@ -9,7 +9,7 @@ class ProjectNoteEntity < NoteEntity
toggle_award_emoji_project_note_path(note.project, note.id)
end
- expose :path do |note|
+ expose :path, if: -> (note, _) { note.id } do |note|
project_note_path(note.project, note)
end
diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb
index 04ec80e0efa..97e5b336a35 100644
--- a/app/serializers/runner_entity.rb
+++ b/app/serializers/runner_entity.rb
@@ -5,8 +5,7 @@ class RunnerEntity < Grape::Entity
expose :id, :description
- expose :edit_path,
- if: -> (*) { can?(request.current_user, :admin_build, project) && runner.project_type? } do |runner|
+ expose :edit_path, if: -> (*) { can_edit_runner? } do |runner|
edit_project_runner_path(project, runner)
end
@@ -17,4 +16,8 @@ class RunnerEntity < Grape::Entity
def project
request.project
end
+
+ def can_edit_runner?
+ can?(request.current_user, :update_runner, runner) && runner.project_type?
+ end
end
diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb
index 00e6d32ee3a..029dd3d0684 100644
--- a/app/serializers/stage_entity.rb
+++ b/app/serializers/stage_entity.rb
@@ -19,7 +19,13 @@ class StageEntity < Grape::Entity
latest_statuses
end
- expose :detailed_status, as: :status, with: StatusEntity
+ expose :retried,
+ if: -> (_, opts) { opts[:retried] },
+ with: JobEntity do |stage|
+ retried_statuses
+ end
+
+ expose :detailed_status, as: :status, with: DetailedStatusEntity
expose :path do |stage|
project_pipeline_path(
@@ -48,9 +54,19 @@ class StageEntity < Grape::Entity
@grouped_statuses ||= stage.statuses.latest_ordered.group_by(&:status)
end
+ def grouped_retried_statuses
+ @grouped_retried_statuses ||= stage.statuses.retried_ordered.group_by(&:status)
+ end
+
def latest_statuses
HasStatus::ORDERED_STATUSES.map do |ordered_status|
grouped_statuses.fetch(ordered_status, [])
end.flatten
end
+
+ def retried_statuses
+ HasStatus::ORDERED_STATUSES.map do |ordered_status|
+ grouped_retried_statuses.fetch(ordered_status, [])
+ end.flatten
+ end
end
diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb
deleted file mode 100644
index 306c30f0323..00000000000
--- a/app/serializers/status_entity.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-class StatusEntity < Grape::Entity
- include RequestAwareEntity
-
- expose :icon, :text, :label, :group
- expose :status_tooltip, as: :tooltip
- expose :has_details?, as: :has_details
- expose :details_path
-
- expose :favicon do |status|
- Gitlab::Favicon.status_overlay(status.favicon)
- end
-
- expose :action, if: -> (status, _) { status.has_action? } do
- expose :action_icon, as: :icon
- expose :action_title, as: :title
- expose :action_path, as: :path
- expose :action_method, as: :method
- end
-end
diff --git a/app/serializers/trigger_variable_entity.rb b/app/serializers/trigger_variable_entity.rb
new file mode 100644
index 00000000000..56203113631
--- /dev/null
+++ b/app/serializers/trigger_variable_entity.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class TriggerVariableEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :key, :value, :public
+end
diff --git a/app/serializers/user_preference_entity.rb b/app/serializers/user_preference_entity.rb
new file mode 100644
index 00000000000..b99f80424db
--- /dev/null
+++ b/app/serializers/user_preference_entity.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class UserPreferenceEntity < Grape::Entity
+ expose :issue_notes_filter
+ expose :merge_request_notes_filter
+
+ expose :notes_filters do |user_preference|
+ UserPreference.notes_filters
+ end
+
+ expose :default_notes_filter do |user_preference|
+ UserPreference::NOTES_FILTERS[:all_notes]
+ end
+end
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index 19cf34e2ac4..2e4643ed668 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -11,11 +11,19 @@ module ApplicationSettings
params[:performance_bar_allowed_group_id] = performance_bar_allowed_group_id
end
+ if usage_stats_updated? && !params.delete(:skip_usage_stats_user)
+ params[:usage_stats_set_by_user_id] = current_user.id
+ end
+
@application_setting.update(@params)
end
private
+ def usage_stats_updated?
+ params.key?(:usage_ping_enabled) || params.key?(:version_check_enabled)
+ end
+
def update_terms(terms)
return unless terms.present?
diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb
index 7db90c0b3c6..b6c30da4d3a 100644
--- a/app/services/applications/create_service.rb
+++ b/app/services/applications/create_service.rb
@@ -2,11 +2,14 @@
module Applications
class CreateService
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(current_user, params)
@current_user = current_user
@params = params.except(:ip_address)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # EE would override and use `request` arg
def execute(request)
Doorkeeper::Application.create(@params)
end
diff --git a/app/services/audit_event_service.rb b/app/services/audit_event_service.rb
index 4c5e22bdd7e..201048aaba5 100644
--- a/app/services/audit_event_service.rb
+++ b/app/services/audit_event_service.rb
@@ -17,11 +17,29 @@ class AuditEventService
end
def security_event
- SecurityEvent.create(
+ log_security_event_to_file
+ log_security_event_to_database
+ end
+
+ private
+
+ def base_payload
+ {
author_id: @author.id,
entity_id: @entity.id,
- entity_type: @entity.class.name,
- details: @details
- )
+ entity_type: @entity.class.name
+ }
+ end
+
+ def file_logger
+ @file_logger ||= Gitlab::AuditJsonLogger.build
+ end
+
+ def log_security_event_to_file
+ file_logger.info(base_payload.merge(@details))
+ end
+
+ def log_security_event_to_database
+ SecurityEvent.create(base_payload.merge(details: @details))
end
end
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 893b37b831a..f764536e762 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -99,7 +99,7 @@ module Auth
##
# Because we do not have two way communication with registry yet,
# we create a container repository image resource when push to the
- # registry is successfuly authorized.
+ # registry is successfully authorized.
#
def ensure_container_repository!(path, actions)
return if path.has_repository?
diff --git a/app/services/boards/create_service.rb b/app/services/boards/create_service.rb
index 4caf5ffa3cb..1b796cef3e2 100644
--- a/app/services/boards/create_service.rb
+++ b/app/services/boards/create_service.rb
@@ -9,7 +9,7 @@ module Boards
private
def can_create_board?
- parent.boards.size == 0
+ parent.boards.empty?
end
def create_board!
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index 0db1418b37a..0b69661bbd0 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -9,6 +9,7 @@ module Boards
fetch_issues.order_by_position_and_priority
end
+ # rubocop: disable CodeReuse/ActiveRecord
def metadata
keys = metadata_fields.keys
columns = metadata_fields.values_at(*keys).join(', ')
@@ -16,6 +17,7 @@ module Boards
Hash[keys.zip(results.flatten)]
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -24,6 +26,7 @@ module Boards
end
# We memoize the query here since the finder methods we use are quite complex. This does not memoize the result of the query.
+ # rubocop: disable CodeReuse/ActiveRecord
def fetch_issues
strong_memoize(:fetch_issues) do
issues = IssuesFinder.new(current_user, filter_params).execute
@@ -31,6 +34,7 @@ module Boards
filter(issues).reorder(nil)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def filter(issues)
issues = without_board_labels(issues) unless list&.movable? || list&.closed?
@@ -52,6 +56,7 @@ module Boards
set_parent
set_state
set_scope
+ set_non_archived
params
end
@@ -72,24 +77,36 @@ module Boards
params[:include_subgroups] = board.group_board?
end
+ def set_non_archived
+ params[:non_archived] = parent.is_a?(Group)
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
def board_label_ids
@board_label_ids ||= board.lists.movable.pluck(:label_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def without_board_labels(issues)
return issues unless board_label_ids.any?
issues.where.not('EXISTS (?)', issues_label_links.limit(1))
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def issues_label_links
LabelLink.where("label_links.target_type = 'Issue' AND label_links.target_id = issues.id").where(label_id: board_label_ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def with_list_label(issues)
issues.where('EXISTS (?)', LabelLink.where("label_links.target_type = 'Issue' AND label_links.target_id = issues.id")
.where("label_links.label_id = ?", list.label_id).limit(1))
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb
index 6fd8a23b2a1..43a26f4264e 100644
--- a/app/services/boards/issues/move_service.rb
+++ b/app/services/boards/issues/move_service.rb
@@ -21,13 +21,17 @@ module Boards
moving_from_list != moving_to_list
end
+ # rubocop: disable CodeReuse/ActiveRecord
def moving_from_list
@moving_from_list ||= board.lists.find_by(id: params[:from_list_id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def moving_to_list
@moving_to_list ||= board.lists.find_by(id: params[:to_list_id])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update(issue)
::Issues::UpdateService.new(issue.project, current_user, issue_params(issue)).execute(issue)
@@ -61,18 +65,18 @@ module Boards
[moving_to_list.label_id].compact
end
+ # rubocop: disable CodeReuse/ActiveRecord
def remove_label_ids
label_ids =
if moving_to_list.movable?
moving_from_list.label_id
- elsif board.group_board?
- ::Label.on_group_boards(parent.id).pluck(:label_id)
else
- ::Label.on_project_boards(parent.id).pluck(:label_id)
+ ::Label.on_board(board.id).pluck(:label_id)
end
Array(label_ids).compact
end
+ # rubocop: enable CodeReuse/ActiveRecord
def move_between_ids
return unless params[:move_after_id] || params[:move_before_id]
diff --git a/app/services/boards/lists/destroy_service.rb b/app/services/boards/lists/destroy_service.rb
index e12d4f46e19..609c430caed 100644
--- a/app/services/boards/lists/destroy_service.rb
+++ b/app/services/boards/lists/destroy_service.rb
@@ -18,10 +18,12 @@ module Boards
attr_reader :board
+ # rubocop: disable CodeReuse/ActiveRecord
def decrement_higher_lists(list)
board.lists.movable.where('position > ?', list.position)
.update_all('position = position - 1')
end
+ # rubocop: enable CodeReuse/ActiveRecord
def remove_list(list)
list.destroy
diff --git a/app/services/boards/lists/list_service.rb b/app/services/boards/lists/list_service.rb
index e10eb52e041..5cf5f14a55b 100644
--- a/app/services/boards/lists/list_service.rb
+++ b/app/services/boards/lists/list_service.rb
@@ -6,7 +6,7 @@ module Boards
def execute(board)
board.lists.create(list_type: :backlog) unless board.lists.backlog.exists?
- board.lists
+ board.lists.preload_associations
end
end
end
diff --git a/app/services/boards/lists/move_service.rb b/app/services/boards/lists/move_service.rb
index 27a36051662..93f81837d1a 100644
--- a/app/services/boards/lists/move_service.rb
+++ b/app/services/boards/lists/move_service.rb
@@ -34,17 +34,21 @@ module Boards
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def decrement_intermediate_lists
board.lists.movable.where('position > ?', old_position)
.where('position <= ?', new_position)
.update_all('position = position - 1')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def increment_intermediate_lists
board.lists.movable.where('position >= ?', new_position)
.where('position < ?', old_position)
.update_all('position = position + 1')
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update_list_position(list)
list.update_attribute(:position, new_position)
diff --git a/app/services/boards/visits/create_service.rb b/app/services/boards/visits/create_service.rb
new file mode 100644
index 00000000000..e2adf755511
--- /dev/null
+++ b/app/services/boards/visits/create_service.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Boards
+ module Visits
+ class CreateService < Boards::BaseService
+ def execute(board)
+ return unless current_user && Gitlab::Database.read_write?
+
+ if parent.is_a?(Group)
+ BoardGroupRecentVisit.visited!(current_user, board)
+ else
+ BoardProjectRecentVisit.visited!(current_user, board)
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/boards/visits/latest_service.rb b/app/services/boards/visits/latest_service.rb
new file mode 100644
index 00000000000..9e4c77a6317
--- /dev/null
+++ b/app/services/boards/visits/latest_service.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Boards
+ module Visits
+ class LatestService < Boards::BaseService
+ def execute
+ return nil unless current_user
+
+ if parent.is_a?(Group)
+ BoardGroupRecentVisit.latest(current_user, parent)
+ else
+ BoardProjectRecentVisit.latest(current_user, parent)
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb
index 854b191c45c..c91738fa4c7 100644
--- a/app/services/chat_names/find_user_service.rb
+++ b/app/services/chat_names/find_user_service.rb
@@ -17,6 +17,7 @@ module ChatNames
private
+ # rubocop: disable CodeReuse/ActiveRecord
def find_chat_name
ChatName.find_by(
service: @service,
@@ -24,5 +25,6 @@ module ChatNames
chat_id: @params[:user_id]
)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/ci/compare_test_reports_service.rb b/app/services/ci/compare_test_reports_service.rb
index ec25e934a27..2293f95f56b 100644
--- a/app/services/ci/compare_test_reports_service.rb
+++ b/app/services/ci/compare_test_reports_service.rb
@@ -3,6 +3,7 @@
module Ci
class CompareTestReportsService < ::BaseService
def execute(base_pipeline, head_pipeline)
+ # rubocop: disable CodeReuse/Serializer
comparer = Gitlab::Ci::Reports::TestReportsComparer
.new(base_pipeline&.test_reports, head_pipeline.test_reports)
@@ -19,6 +20,7 @@ module Ci
key: key(base_pipeline, head_pipeline),
status_reason: e.message
}
+ # rubocop: enable CodeReuse/Serializer
end
def latest?(base_pipeline, head_pipeline, data)
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 85df8bcff8c..92a8438ab2f 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -65,6 +65,7 @@ module Ci
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def auto_cancelable_pipelines
project.pipelines
.where(ref: pipeline.ref)
@@ -72,6 +73,7 @@ module Ci
.where.not(sha: project.commit(pipeline.ref).try(:id))
.created_or_pending
end
+ # rubocop: enable CodeReuse/ActiveRecord
def pipeline_created_counter
@pipeline_created_counter ||= Gitlab::Metrics
@@ -84,8 +86,10 @@ module Ci
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def related_merge_requests
pipeline.project.source_of_merge_requests.opened.where(source_branch: pipeline.ref)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb
new file mode 100644
index 00000000000..13f892aabb8
--- /dev/null
+++ b/app/services/ci/destroy_pipeline_service.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Ci
+ class DestroyPipelineService < BaseService
+ def execute(pipeline)
+ raise Gitlab::Access::AccessDeniedError unless can?(current_user, :destroy_pipeline, pipeline)
+
+ AuditEventService.new(current_user, pipeline).security_event
+
+ pipeline.destroy!
+ end
+ end
+end
diff --git a/app/services/ci/enqueue_build_service.rb b/app/services/ci/enqueue_build_service.rb
deleted file mode 100644
index 8140651d980..00000000000
--- a/app/services/ci/enqueue_build_service.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-module Ci
- class EnqueueBuildService < BaseService
- def execute(build)
- build.enqueue
- end
- end
-end
diff --git a/app/services/ci/ensure_stage_service.rb b/app/services/ci/ensure_stage_service.rb
index 3d0e39d1b9f..cbb3a2e4709 100644
--- a/app/services/ci/ensure_stage_service.rb
+++ b/app/services/ci/ensure_stage_service.rb
@@ -38,9 +38,11 @@ module Ci
EOS
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_stage
@build.pipeline.stages.find_by(name: @build.stage)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def create_stage
Ci::Stage.create!(name: @build.stage,
diff --git a/app/services/ci/extract_sections_from_build_trace_service.rb b/app/services/ci/extract_sections_from_build_trace_service.rb
index 693f6d55be3..97f9918fdb7 100644
--- a/app/services/ci/extract_sections_from_build_trace_service.rb
+++ b/app/services/ci/extract_sections_from_build_trace_service.rb
@@ -11,11 +11,13 @@ module Ci
private
+ # rubocop: disable CodeReuse/ActiveRecord
def find_or_create_name(name)
project.build_trace_section_names.find_or_create_by!(name: name)
rescue ActiveRecord::RecordInvalid
project.build_trace_section_names.find_by!(name: name)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def extract_sections(build)
build.trace.extract_sections.map do |attr|
diff --git a/app/services/ci/fetch_kubernetes_token_service.rb b/app/services/ci/fetch_kubernetes_token_service.rb
deleted file mode 100644
index 15eda56cac6..00000000000
--- a/app/services/ci/fetch_kubernetes_token_service.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-# frozen_string_literal: true
-
-##
-# TODO:
-# Almost components in this class were copied from app/models/project_services/kubernetes_service.rb
-# We should dry up those classes not to repeat the same code.
-# Maybe we should have a special facility (e.g. lib/kubernetes_api) to maintain all Kubernetes API caller.
-module Ci
- class FetchKubernetesTokenService
- attr_reader :api_url, :ca_pem, :username, :password
-
- def initialize(api_url, ca_pem, username, password)
- @api_url = api_url
- @ca_pem = ca_pem
- @username = username
- @password = password
- end
-
- def execute
- read_secrets.each do |secret|
- name = secret.dig('metadata', 'name')
- if /default-token/ =~ name
- token_base64 = secret.dig('data', 'token')
- return Base64.decode64(token_base64) if token_base64
- end
- end
-
- nil
- end
-
- private
-
- def read_secrets
- kubeclient = build_kubeclient!
-
- kubeclient.get_secrets.as_json
- rescue Kubeclient::HttpError => err
- raise err unless err.error_code == 404
-
- []
- end
-
- def build_kubeclient!(api_path: 'api', api_version: 'v1')
- raise "Incomplete settings" unless api_url && username && password
-
- ::Kubeclient::Client.new(
- join_api_url(api_path),
- api_version,
- auth_options: { username: username, password: password },
- ssl_options: kubeclient_ssl_options,
- http_proxy_uri: ENV['http_proxy']
- )
- end
-
- def join_api_url(api_path)
- url = URI.parse(api_url)
- prefix = url.path.sub(%r{/+\z}, '')
-
- url.path = [prefix, api_path].join("/")
-
- url.to_s
- end
-
- def kubeclient_ssl_options
- opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
-
- if ca_pem.present?
- opts[:cert_store] = OpenSSL::X509::Store.new
- opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
- end
-
- opts
- end
- end
-end
diff --git a/app/services/ci/process_build_service.rb b/app/services/ci/process_build_service.rb
new file mode 100644
index 00000000000..d9f8e7cb452
--- /dev/null
+++ b/app/services/ci/process_build_service.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Ci
+ class ProcessBuildService < BaseService
+ def execute(build, current_status)
+ if valid_statuses_for_when(build.when).include?(current_status)
+ if build.schedulable?
+ build.schedule
+ elsif build.action?
+ build.actionize
+ else
+ enqueue(build)
+ end
+
+ true
+ else
+ build.skip
+ false
+ end
+ end
+
+ private
+
+ def enqueue(build)
+ build.enqueue
+ end
+
+ def valid_statuses_for_when(value)
+ case value
+ when 'on_success'
+ %w[success skipped]
+ when 'on_failure'
+ %w[failed]
+ when 'always'
+ %w[success failed skipped]
+ when 'manual'
+ %w[success skipped]
+ when 'delayed'
+ %w[success skipped]
+ else
+ []
+ end
+ end
+ end
+end
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index cafee76a33c..446188347df 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -24,53 +24,35 @@ module Ci
def process_stage(index)
current_status = status_for_prior_stages(index)
- return if HasStatus::BLOCKED_STATUS == current_status
+ return if HasStatus::BLOCKED_STATUS.include?(current_status)
if HasStatus::COMPLETED_STATUSES.include?(current_status)
created_builds_in_stage(index).select do |build|
Gitlab::OptimisticLocking.retry_lock(build) do |subject|
- process_build(subject, current_status)
+ Ci::ProcessBuildService.new(project, @user)
+ .execute(build, current_status)
end
end
end
end
- def process_build(build, current_status)
- if valid_statuses_for_when(build.when).include?(current_status)
- build.action? ? build.actionize : enqueue_build(build)
- true
- else
- build.skip
- false
- end
- end
-
- def valid_statuses_for_when(value)
- case value
- when 'on_success'
- %w[success skipped]
- when 'on_failure'
- %w[failed]
- when 'always'
- %w[success failed skipped]
- when 'manual'
- %w[success skipped]
- else
- []
- end
- end
-
+ # rubocop: disable CodeReuse/ActiveRecord
def status_for_prior_stages(index)
pipeline.builds.where('stage_idx < ?', index).latest.status || 'success'
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def stage_indexes_of_created_builds
created_builds.order(:stage_idx).pluck('distinct stage_idx')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def created_builds_in_stage(index)
created_builds.where(stage_idx: index)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def created_builds
pipeline.builds.created
@@ -80,6 +62,7 @@ module Ci
# This replicates what is db/post_migrate/20170416103934_upate_retried_for_ci_build.rb
# and ensures that functionality will not be broken before migration is run
# this updates only when there are data that needs to be updated, there are two groups with no retried flag
+ # rubocop: disable CodeReuse/ActiveRecord
def update_retried
# find the latest builds for each name
latest_statuses = pipeline.statuses.latest
@@ -93,9 +76,6 @@ module Ci
.where.not(id: latest_statuses.map(&:first))
.update_all(retried: true) if latest_statuses.any?
end
-
- def enqueue_build(build)
- Ci::EnqueueBuildService.new(project, @user).execute(build)
- end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index 11f85627faf..e06f1c05843 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -15,6 +15,7 @@ module Ci
@runner = runner
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(params = {})
builds =
if runner.instance_type?
@@ -63,6 +64,7 @@ module Ci
register_failure
Result.new(nil, valid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -80,10 +82,16 @@ module Ci
return false
end
+ if build.archived?
+ build.drop!(:archived_failure)
+ return false
+ end
+
build.run!
true
end
+ # rubocop: disable CodeReuse/ActiveRecord
def builds_for_shared_runner
new_builds.
# don't run projects which have not enabled shared runners and builds
@@ -97,11 +105,15 @@ module Ci
joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.project_id=project_builds.project_id")
.order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def builds_for_project_runner
new_builds.where(project: runner.projects.without_deleted.with_builds_enabled).order('id ASC')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def builds_for_group_runner
# Workaround for weird Rails bug, that makes `runner.groups.to_sql` to return `runner_id = NULL`
groups = ::Group.joins(:runner_namespaces).merge(runner.runner_namespaces)
@@ -113,11 +125,14 @@ module Ci
.without_deleted
new_builds.where(project: projects).order('id ASC')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def running_builds_for_shared_runners
Ci::Build.running.where(runner: Ci::Runner.instance_type)
.group(:project_id).select(:project_id, 'count(*) AS running_builds')
end
+ # rubocop: enable CodeReuse/ActiveRecord
def new_builds
builds = Ci::Build.pending.unstarted
@@ -138,6 +153,7 @@ module Ci
attempt_counter.increment
end
+ # rubocop: disable CodeReuse/ActiveRecord
def jobs_running_for_project(job)
return '+Inf' unless runner.instance_type?
@@ -146,6 +162,7 @@ module Ci
.limit(JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET + 1).count - 1
running_jobs_count < JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET ? running_jobs_count : "#{JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET}+"
end
+ # rubocop: enable CodeReuse/ActiveRecord
def failed_attempt_counter
@failed_attempt_counter ||= Gitlab::Metrics.counter(:job_register_attempts_failed_total, "Counts the times a runner tries to register a job")
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 6ceb59e4780..218f1e63d08 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -19,6 +19,7 @@ module Ci
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def reprocess!(build)
unless can?(current_user, :update_build, build)
raise Gitlab::Access::AccessDeniedError
@@ -41,5 +42,6 @@ module Ci
project.builds.create!(Hash[attributes])
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/ci/run_scheduled_build_service.rb b/app/services/ci/run_scheduled_build_service.rb
new file mode 100644
index 00000000000..8e4a628296f
--- /dev/null
+++ b/app/services/ci/run_scheduled_build_service.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Ci
+ class RunScheduledBuildService < ::BaseService
+ def execute(build)
+ unless can?(current_user, :update_build, build)
+ raise Gitlab::Access::AccessDeniedError
+ end
+
+ build.enqueue_scheduled!
+ end
+ end
+end
diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb
index 35f5cff0e0c..ca0f7b30053 100644
--- a/app/services/clusters/applications/check_installation_progress_service.rb
+++ b/app/services/clusters/applications/check_installation_progress_service.rb
@@ -14,8 +14,10 @@ module Clusters
else
check_timeout
end
- rescue Kubeclient::HttpError => ke
- app.make_errored!("Kubernetes error: #{ke.message}") unless app.errored?
+ rescue Kubeclient::HttpError => e
+ Rails.logger.error("Kubernetes error: #{e.error_code} #{e.message}")
+ Gitlab::Sentry.track_acceptable_exception(e, extra: { scope: 'kubernetes', app_id: app.id })
+ app.make_errored!("Kubernetes error: #{e.error_code}") unless app.errored?
end
private
@@ -27,7 +29,7 @@ module Clusters
end
def on_failed
- app.make_errored!(installation_errors || 'Installation silently failed')
+ app.make_errored!('Installation failed')
ensure
remove_installation_pod
end
@@ -51,7 +53,8 @@ module Clusters
def remove_installation_pod
helm_api.delete_pod!(install_command.pod_name)
- rescue
+ rescue => e
+ Rails.logger.error("Kubernetes error: #{e.class.name} #{e.message}")
# no-op
end
diff --git a/app/services/clusters/applications/create_service.rb b/app/services/clusters/applications/create_service.rb
new file mode 100644
index 00000000000..844807c2581
--- /dev/null
+++ b/app/services/clusters/applications/create_service.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Applications
+ class CreateService
+ InvalidApplicationError = Class.new(StandardError)
+
+ attr_reader :cluster, :current_user, :params
+
+ def initialize(cluster, user, params = {})
+ @cluster = cluster
+ @current_user = user
+ @params = params.dup
+ end
+
+ def execute(request)
+ create_application.tap do |application|
+ if application.has_attribute?(:hostname)
+ application.hostname = params[:hostname]
+ end
+
+ if application.respond_to?(:oauth_application)
+ application.oauth_application = create_oauth_application(application, request)
+ end
+
+ application.save!
+
+ Clusters::Applications::ScheduleInstallationService.new(application).execute
+ end
+ end
+
+ private
+
+ def create_application
+ builder.call(@cluster)
+ end
+
+ def builder
+ builders[application_name] || raise(InvalidApplicationError, "invalid application: #{application_name}")
+ end
+
+ def builders
+ {
+ "helm" => -> (cluster) { cluster.application_helm || cluster.build_application_helm },
+ "ingress" => -> (cluster) { cluster.application_ingress || cluster.build_application_ingress }
+ }.tap do |hash|
+ hash.merge!(project_builders) if cluster.project_type?
+ end
+ end
+
+ # These applications will need extra configuration to enable them to work
+ # with groups of projects
+ def project_builders
+ {
+ "prometheus" => -> (cluster) { cluster.application_prometheus || cluster.build_application_prometheus },
+ "runner" => -> (cluster) { cluster.application_runner || cluster.build_application_runner },
+ "jupyter" => -> (cluster) { cluster.application_jupyter || cluster.build_application_jupyter },
+ "knative" => -> (cluster) { cluster.application_knative || cluster.build_application_knative }
+ }
+ end
+
+ def application_name
+ params[:application]
+ end
+
+ def create_oauth_application(application, request)
+ oauth_application_params = {
+ name: params[:application],
+ redirect_uri: application.callback_url,
+ scopes: 'api read_user openid',
+ owner: current_user
+ }
+
+ ::Applications::CreateService.new(current_user, oauth_application_params).execute(request)
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/applications/install_service.rb b/app/services/clusters/applications/install_service.rb
index 7e3c0e77a83..f4385748c43 100644
--- a/app/services/clusters/applications/install_service.rb
+++ b/app/services/clusters/applications/install_service.rb
@@ -12,10 +12,14 @@ module Clusters
ClusterWaitForAppInstallationWorker.perform_in(
ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id)
- rescue Kubeclient::HttpError => ke
- app.make_errored!("Kubernetes error: #{ke.message}")
+ rescue Kubeclient::HttpError => e
+ Rails.logger.error("Kubernetes error: #{e.error_code} #{e.message}")
+ Gitlab::Sentry.track_acceptable_exception(e, extra: { scope: 'kubernetes', app_id: app.id })
+ app.make_errored!("Kubernetes error: #{e.error_code}")
rescue StandardError => e
- app.make_errored!("Can't start installation process. #{e.message}")
+ Rails.logger.error "Can't start installation process: #{e.class.name} #{e.message}"
+ Gitlab::Sentry.track_acceptable_exception(e, extra: { scope: 'kubernetes', app_id: app.id })
+ app.make_errored!("Can't start installation process.")
end
end
end
diff --git a/app/services/clusters/applications/schedule_installation_service.rb b/app/services/clusters/applications/schedule_installation_service.rb
index 4ead4f619c8..d75ba70c27e 100644
--- a/app/services/clusters/applications/schedule_installation_service.rb
+++ b/app/services/clusters/applications/schedule_installation_service.rb
@@ -2,8 +2,14 @@
module Clusters
module Applications
- class ScheduleInstallationService < ::BaseService
- def execute(application)
+ class ScheduleInstallationService
+ attr_reader :application
+
+ def initialize(application)
+ @application = application
+ end
+
+ def execute
application.make_scheduled!
ClusterInstallAppWorker.perform_async(application.name, application.id)
diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb
index e3e0cfa462c..5a9da053780 100644
--- a/app/services/clusters/create_service.rb
+++ b/app/services/clusters/create_service.rb
@@ -1,37 +1,51 @@
# frozen_string_literal: true
module Clusters
- class CreateService < BaseService
- attr_reader :access_token
+ class CreateService
+ attr_reader :current_user, :params
- def execute(access_token = nil)
- @access_token = access_token
+ def initialize(user = nil, params = {})
+ @current_user, @params = user, params.dup
+ end
+
+ def execute(access_token: nil)
+ raise ArgumentError, 'Unknown clusterable provided' unless clusterable
+ raise ArgumentError, _('Instance does not support multiple Kubernetes clusters') unless can_create_cluster?
- raise ArgumentError.new(_('Instance does not support multiple Kubernetes clusters')) unless can_create_cluster?
+ cluster_params = params.merge(user: current_user).merge(clusterable_params)
+ cluster_params[:provider_gcp_attributes].try do |provider|
+ provider[:access_token] = access_token
+ end
- create_cluster.tap do |cluster|
+ create_cluster(cluster_params).tap do |cluster|
ClusterProvisionWorker.perform_async(cluster.id) if cluster.persisted?
end
end
private
- def create_cluster
+ def create_cluster(cluster_params)
Clusters::Cluster.create(cluster_params)
end
- def cluster_params
- return @cluster_params if defined?(@cluster_params)
+ def clusterable
+ @clusterable ||= params.delete(:clusterable)
+ end
- params[:provider_gcp_attributes].try do |provider|
- provider[:access_token] = access_token
+ def clusterable_params
+ case clusterable
+ when ::Project
+ { cluster_type: :project_type, projects: [clusterable] }
+ when ::Group
+ { cluster_type: :group_type, groups: [clusterable] }
+ else
+ raise NotImplementedError
end
-
- @cluster_params = params.merge(user: current_user, projects: [project])
end
+ # EE would override this method
def can_create_cluster?
- project.clusters.empty?
+ clusterable.clusters.empty?
end
end
end
diff --git a/app/services/clusters/gcp/finalize_creation_service.rb b/app/services/clusters/gcp/finalize_creation_service.rb
index 264419501dc..3df43657fa0 100644
--- a/app/services/clusters/gcp/finalize_creation_service.rb
+++ b/app/services/clusters/gcp/finalize_creation_service.rb
@@ -9,17 +9,28 @@ module Clusters
@provider = provider
configure_provider
+ create_gitlab_service_account!
configure_kubernetes
-
cluster.save!
+ configure_project_service_account
+
rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e
provider.make_errored!("Failed to request to CloudPlatform; #{e.message}")
+ rescue Kubeclient::HttpError => e
+ provider.make_errored!("Failed to run Kubeclient: #{e.message}")
rescue ActiveRecord::RecordInvalid => e
provider.make_errored!("Failed to configure Google Kubernetes Engine Cluster: #{e.message}")
end
private
+ def create_gitlab_service_account!
+ Clusters::Gcp::Kubernetes::CreateServiceAccountService.gitlab_creator(
+ kube_client,
+ rbac: create_rbac_cluster?
+ ).execute
+ end
+
def configure_provider
provider.endpoint = gke_cluster.endpoint
provider.status_event = :make_created
@@ -32,15 +43,64 @@ module Clusters
ca_cert: Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
username: gke_cluster.master_auth.username,
password: gke_cluster.master_auth.password,
+ authorization_type: authorization_type,
token: request_kubernetes_token)
end
def request_kubernetes_token
- Ci::FetchKubernetesTokenService.new(
+ Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(
+ kube_client,
+ Clusters::Gcp::Kubernetes::GITLAB_ADMIN_TOKEN_NAME,
+ Clusters::Gcp::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAMESPACE
+ ).execute
+ end
+
+ def configure_project_service_account
+ kubernetes_namespace = cluster.find_or_initialize_kubernetes_namespace(cluster.cluster_project)
+
+ Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
+ cluster: cluster,
+ kubernetes_namespace: kubernetes_namespace
+ ).execute
+ end
+
+ def authorization_type
+ create_rbac_cluster? ? 'rbac' : 'abac'
+ end
+
+ def create_rbac_cluster?
+ !provider.legacy_abac?
+ end
+
+ def kube_client
+ @kube_client ||= build_kube_client!(
'https://' + gke_cluster.endpoint,
Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
gke_cluster.master_auth.username,
- gke_cluster.master_auth.password).execute
+ gke_cluster.master_auth.password
+ )
+ end
+
+ def build_kube_client!(api_url, ca_pem, username, password)
+ raise "Incomplete settings" unless api_url && username && password
+
+ Gitlab::Kubernetes::KubeClient.new(
+ api_url,
+ auth_options: { username: username, password: password },
+ ssl_options: kubeclient_ssl_options(ca_pem),
+ http_proxy_uri: ENV['http_proxy']
+ )
+ end
+
+ def kubeclient_ssl_options(ca_pem)
+ opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
+
+ if ca_pem.present?
+ opts[:cert_store] = OpenSSL::X509::Store.new
+ opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
+ end
+
+ opts
end
def gke_cluster
diff --git a/app/services/clusters/gcp/kubernetes.rb b/app/services/clusters/gcp/kubernetes.rb
new file mode 100644
index 00000000000..90ed529670c
--- /dev/null
+++ b/app/services/clusters/gcp/kubernetes.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Gcp
+ module Kubernetes
+ GITLAB_SERVICE_ACCOUNT_NAME = 'gitlab'
+ GITLAB_SERVICE_ACCOUNT_NAMESPACE = 'default'
+ GITLAB_ADMIN_TOKEN_NAME = 'gitlab-token'
+ GITLAB_CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin'
+ GITLAB_CLUSTER_ROLE_NAME = 'cluster-admin'
+ PROJECT_CLUSTER_ROLE_NAME = 'edit'
+ end
+ end
+end
diff --git a/app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb b/app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb
new file mode 100644
index 00000000000..b31426556f6
--- /dev/null
+++ b/app/services/clusters/gcp/kubernetes/create_or_update_namespace_service.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Gcp
+ module Kubernetes
+ class CreateOrUpdateNamespaceService
+ def initialize(cluster:, kubernetes_namespace:)
+ @cluster = cluster
+ @kubernetes_namespace = kubernetes_namespace
+ @platform = cluster.platform
+ end
+
+ def execute
+ configure_kubernetes_namespace
+ create_project_service_account
+ configure_kubernetes_token
+
+ kubernetes_namespace.save!
+ end
+
+ private
+
+ attr_reader :cluster, :kubernetes_namespace, :platform
+
+ def configure_kubernetes_namespace
+ kubernetes_namespace.set_defaults
+ end
+
+ def create_project_service_account
+ Clusters::Gcp::Kubernetes::CreateServiceAccountService.namespace_creator(
+ platform.kubeclient,
+ service_account_name: kubernetes_namespace.service_account_name,
+ service_account_namespace: kubernetes_namespace.namespace,
+ rbac: platform.rbac?
+ ).execute
+ end
+
+ def configure_kubernetes_token
+ kubernetes_namespace.service_account_token = fetch_service_account_token
+ end
+
+ def fetch_service_account_token
+ Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(
+ platform.kubeclient,
+ kubernetes_namespace.token_name,
+ kubernetes_namespace.namespace
+ ).execute
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/gcp/kubernetes/create_service_account_service.rb b/app/services/clusters/gcp/kubernetes/create_service_account_service.rb
new file mode 100644
index 00000000000..dfc4bf7a358
--- /dev/null
+++ b/app/services/clusters/gcp/kubernetes/create_service_account_service.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Gcp
+ module Kubernetes
+ class CreateServiceAccountService
+ def initialize(kubeclient, service_account_name:, service_account_namespace:, token_name:, rbac:, namespace_creator: false, role_binding_name: nil)
+ @kubeclient = kubeclient
+ @service_account_name = service_account_name
+ @service_account_namespace = service_account_namespace
+ @token_name = token_name
+ @rbac = rbac
+ @namespace_creator = namespace_creator
+ @role_binding_name = role_binding_name
+ end
+
+ def self.gitlab_creator(kubeclient, rbac:)
+ self.new(
+ kubeclient,
+ service_account_name: Clusters::Gcp::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAME,
+ service_account_namespace: Clusters::Gcp::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAMESPACE,
+ token_name: Clusters::Gcp::Kubernetes::GITLAB_ADMIN_TOKEN_NAME,
+ rbac: rbac
+ )
+ end
+
+ def self.namespace_creator(kubeclient, service_account_name:, service_account_namespace:, rbac:)
+ self.new(
+ kubeclient,
+ service_account_name: service_account_name,
+ service_account_namespace: service_account_namespace,
+ token_name: "#{service_account_namespace}-token",
+ rbac: rbac,
+ namespace_creator: true,
+ role_binding_name: "gitlab-#{service_account_namespace}"
+ )
+ end
+
+ def execute
+ ensure_project_namespace_exists if namespace_creator
+ kubeclient.create_service_account(service_account_resource)
+ kubeclient.create_secret(service_account_token_resource)
+ create_role_or_cluster_role_binding if rbac
+ end
+
+ private
+
+ attr_reader :kubeclient, :service_account_name, :service_account_namespace, :token_name, :rbac, :namespace_creator, :role_binding_name
+
+ def ensure_project_namespace_exists
+ Gitlab::Kubernetes::Namespace.new(
+ service_account_namespace,
+ kubeclient
+ ).ensure_exists!
+ end
+
+ def create_role_or_cluster_role_binding
+ if namespace_creator
+ kubeclient.create_role_binding(role_binding_resource)
+ else
+ kubeclient.create_cluster_role_binding(cluster_role_binding_resource)
+ end
+ end
+
+ def service_account_resource
+ Gitlab::Kubernetes::ServiceAccount.new(
+ service_account_name,
+ service_account_namespace
+ ).generate
+ end
+
+ def service_account_token_resource
+ Gitlab::Kubernetes::ServiceAccountToken.new(
+ token_name,
+ service_account_name,
+ service_account_namespace
+ ).generate
+ end
+
+ def cluster_role_binding_resource
+ subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: service_account_namespace }]
+
+ Gitlab::Kubernetes::ClusterRoleBinding.new(
+ Clusters::Gcp::Kubernetes::GITLAB_CLUSTER_ROLE_BINDING_NAME,
+ Clusters::Gcp::Kubernetes::GITLAB_CLUSTER_ROLE_NAME,
+ subjects
+ ).generate
+ end
+
+ def role_binding_resource
+ Gitlab::Kubernetes::RoleBinding.new(
+ name: role_binding_name,
+ role_name: Clusters::Gcp::Kubernetes::PROJECT_CLUSTER_ROLE_NAME,
+ namespace: service_account_namespace,
+ service_account_name: service_account_name
+ ).generate
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb b/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb
new file mode 100644
index 00000000000..4ad04ab801e
--- /dev/null
+++ b/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Gcp
+ module Kubernetes
+ class FetchKubernetesTokenService
+ attr_reader :kubeclient, :service_account_token_name, :namespace
+
+ def initialize(kubeclient, service_account_token_name, namespace)
+ @kubeclient = kubeclient
+ @service_account_token_name = service_account_token_name
+ @namespace = namespace
+ end
+
+ def execute
+ token_base64 = get_secret&.dig('data', 'token')
+ Base64.decode64(token_base64) if token_base64
+ end
+
+ private
+
+ def get_secret
+ kubeclient.get_secret(service_account_token_name, namespace).as_json
+ rescue Kubeclient::ResourceNotFoundError
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/gcp/provision_service.rb b/app/services/clusters/gcp/provision_service.rb
index ab1bf9c64f6..80040511ec2 100644
--- a/app/services/clusters/gcp/provision_service.rb
+++ b/app/services/clusters/gcp/provision_service.rb
@@ -27,7 +27,9 @@ module Clusters
provider.zone,
provider.cluster.name,
provider.num_nodes,
- machine_type: provider.machine_type)
+ machine_type: provider.machine_type,
+ legacy_abac: provider.legacy_abac
+ )
unless operation.status == 'PENDING' || operation.status == 'RUNNING'
return provider.make_errored!("Operation status is unexpected; #{operation.status_message}")
diff --git a/app/services/clusters/update_service.rb b/app/services/clusters/update_service.rb
index 98fdeec4fb1..25d26e761b1 100644
--- a/app/services/clusters/update_service.rb
+++ b/app/services/clusters/update_service.rb
@@ -1,7 +1,13 @@
# frozen_string_literal: true
module Clusters
- class UpdateService < BaseService
+ class UpdateService
+ attr_reader :current_user, :params
+
+ def initialize(user = nil, params = {})
+ @current_user, @params = user, params.dup
+ end
+
def execute(cluster)
cluster.update(params)
end
diff --git a/app/services/cohorts_service.rb b/app/services/cohorts_service.rb
index 7a14e97f749..6d466c2fc9c 100644
--- a/app/services/cohorts_service.rb
+++ b/app/services/cohorts_service.rb
@@ -78,6 +78,7 @@ class CohortsService
# created_at_month can never be nil, but last_activity_on_month can (when a
# user has never logged in, just been created). This covers the last
# MONTHS_INCLUDED months.
+ # rubocop: disable CodeReuse/ActiveRecord
def counts_by_month
@counts_by_month ||=
begin
@@ -91,6 +92,7 @@ class CohortsService
.count
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def column_to_date(column)
if Gitlab::Database.postgresql?
diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb
index 2fbd442fc2e..fbf71f02837 100644
--- a/app/services/commits/change_service.rb
+++ b/app/services/commits/change_service.rb
@@ -24,8 +24,12 @@ module Commits
start_project: @start_project,
start_branch_name: @start_branch)
rescue Gitlab::Git::Repository::CreateTreeError
- error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically.
- This #{@commit.change_type_title(current_user)} may already have been #{action.to_s.dasherize}ed, or a more recent commit may have updated some of its content."
+ act = action.to_s.dasherize
+ type = @commit.change_type_title(current_user)
+
+ error_msg = "Sorry, we cannot #{act} this #{type} automatically. " \
+ "This #{type} may already have been #{act}ed, or a more recent " \
+ "commit may have updated some of its content."
raise ChangeError, error_msg
end
end
diff --git a/app/services/commits/commit_patch_service.rb b/app/services/commits/commit_patch_service.rb
new file mode 100644
index 00000000000..9253cfaac20
--- /dev/null
+++ b/app/services/commits/commit_patch_service.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module Commits
+ class CommitPatchService < CreateService
+ # Requires:
+ # - project: `Project` to be committed into
+ # - user: `User` that will be the committer
+ # - params:
+ # - branch_name: `String` the branch that will be committed into
+ # - start_branch: `String` the branch that will will started from
+ # - patches: `Gitlab::Git::Patches::Collection` that contains the patches
+ def initialize(*args)
+ super
+
+ @patches = Gitlab::Git::Patches::Collection.new(Array(params[:patches]))
+ end
+
+ private
+
+ def new_branch?
+ !repository.branch_exists?(@branch_name)
+ end
+
+ def create_commit!
+ if @start_branch && new_branch?
+ prepare_branch!
+ end
+
+ Gitlab::Git::Patches::CommitPatches
+ .new(current_user, project.repository, @branch_name, @patches)
+ .commit
+ end
+
+ def prepare_branch!
+ branch_result = CreateBranchService.new(project, current_user)
+ .execute(@branch_name, @start_branch)
+
+ if branch_result[:status] != :success
+ raise ChangeError, branch_result[:message]
+ end
+ end
+
+ # Overridden from the Commits::CreateService, to skip some validations we
+ # don't need:
+ # - validate_on_branch!
+ # Not needed, the patches are applied on top of HEAD if the branch did not
+ # exist
+ # - validate_branch_existence!
+ # Not needed because we continue applying patches on the branch if it
+ # already existed, and create it if it did not exist.
+ def validate!
+ validate_patches!
+ validate_new_branch_name! if new_branch?
+ validate_permissions!
+ end
+
+ def validate_patches!
+ raise_error("Patches are too big") unless @patches.valid_size?
+ end
+ end
+end
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index 3ce9acc833c..34593e12bd5 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -19,7 +19,12 @@ module Commits
new_commit = create_commit!
success(result: new_commit)
- rescue ValidationError, ChangeError, Gitlab::Git::Index::IndexError, Gitlab::Git::CommitError, Gitlab::Git::PreReceiveError => ex
+ rescue ValidationError,
+ ChangeError,
+ Gitlab::Git::Index::IndexError,
+ Gitlab::Git::CommitError,
+ Gitlab::Git::PreReceiveError,
+ Gitlab::Git::CommandError => ex
error(ex.message)
end
diff --git a/app/services/concerns/issues/resolve_discussions.rb b/app/services/concerns/issues/resolve_discussions.rb
index 1563ed965df..f0e9862ca30 100644
--- a/app/services/concerns/issues/resolve_discussions.rb
+++ b/app/services/concerns/issues/resolve_discussions.rb
@@ -13,12 +13,14 @@ module Issues
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_request_to_resolve_discussions_of
strong_memoize(:merge_request_to_resolve_discussions_of) do
MergeRequestsFinder.new(current_user, project_id: project.id)
.find_by(iid: merge_request_to_resolve_discussions_of_iid)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def discussions_to_resolve
return [] unless merge_request_to_resolve_discussions_of
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
deleted file mode 100644
index bb3f605da28..00000000000
--- a/app/services/create_deployment_service.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-# frozen_string_literal: true
-
-class CreateDeploymentService
- attr_reader :job
-
- delegate :expanded_environment_name,
- :variables,
- :project,
- to: :job
-
- def initialize(job)
- @job = job
- end
-
- def execute
- return unless executable?
-
- ActiveRecord::Base.transaction do
- environment.external_url = expanded_environment_url if
- expanded_environment_url
-
- environment.fire_state_event(action)
-
- break unless environment.save
- break if environment.stopped?
-
- deploy.tap(&:update_merge_request_metrics!)
- end
- end
-
- private
-
- def executable?
- project && job.environment.present? && environment
- end
-
- def deploy
- project.deployments.create(
- environment: environment,
- ref: job.ref,
- tag: job.tag,
- sha: job.sha,
- user: job.user,
- deployable: job,
- on_stop: on_stop)
- end
-
- def environment
- @environment ||= job.persisted_environment
- end
-
- def environment_options
- @environment_options ||= job.options&.dig(:environment) || {}
- end
-
- def expanded_environment_url
- return @expanded_environment_url if defined?(@expanded_environment_url)
-
- @expanded_environment_url =
- ExpandVariables.expand(environment_url, variables) if environment_url
- end
-
- def environment_url
- environment_options[:url]
- end
-
- def on_stop
- environment_options[:on_stop]
- end
-
- def action
- environment_options[:action] || 'start'
- end
-end
diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb
index 09c68390007..8d1fdbe11c3 100644
--- a/app/services/create_release_service.rb
+++ b/app/services/create_release_service.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class CreateReleaseService < BaseService
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(tag_name, release_description)
repository = project.repository
existing_tag = repository.find_tag(tag_name)
@@ -21,6 +22,7 @@ class CreateReleaseService < BaseService
error('Tag does not exist', 404)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def success(release)
super().merge(release: release)
diff --git a/app/services/delete_merged_branches_service.rb b/app/services/delete_merged_branches_service.rb
index ff3e4783fe3..80de897e94b 100644
--- a/app/services/delete_merged_branches_service.rb
+++ b/app/services/delete_merged_branches_service.rb
@@ -21,10 +21,12 @@ class DeleteMergedBranchesService < BaseService
private
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_request_branch_names
# reorder(nil) is necessary for SELECT DISTINCT because default scope adds an ORDER BY
- source_names = project.origin_merge_requests.opened.reorder(nil).uniq.pluck(:source_branch)
- target_names = project.merge_requests.opened.reorder(nil).uniq.pluck(:target_branch)
+ source_names = project.origin_merge_requests.opened.reorder(nil).distinct.pluck(:source_branch)
+ target_names = project.merge_requests.opened.reorder(nil).distinct.pluck(:target_branch)
(source_names + target_names).uniq
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/services/emails/base_service.rb b/app/services/emails/base_service.rb
index ba7b689a9af..988215ffc78 100644
--- a/app/services/emails/base_service.rb
+++ b/app/services/emails/base_service.rb
@@ -2,6 +2,8 @@
module Emails
class BaseService
+ attr_reader :current_user
+
def initialize(current_user, params = {})
@current_user, @params = current_user, params.dup
@user = params.delete(:user)
diff --git a/app/services/emails/create_service.rb b/app/services/emails/create_service.rb
index acf575e24e5..56925a724fe 100644
--- a/app/services/emails/create_service.rb
+++ b/app/services/emails/create_service.rb
@@ -3,7 +3,12 @@
module Emails
class CreateService < ::Emails::BaseService
def execute(extra_params = {})
- @user.emails.create(@params.merge(extra_params))
+ skip_confirmation = @params.delete(:skip_confirmation)
+
+ email = @user.emails.create(@params.merge(extra_params))
+
+ email&.confirm if skip_confirmation && current_user.admin?
+ email
end
end
end
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index 025f093a428..39e614d6569 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -7,8 +7,10 @@ module Files
def initialize(*args)
super
- @author_email = params[:author_email]
- @author_name = params[:author_name]
+ git_user = Gitlab::Git::User.from_gitlab(current_user) if current_user.present?
+
+ @author_email = params[:author_email] || git_user&.email
+ @author_name = params[:author_name] || git_user&.name
@commit_message = params[:commit_message]
@last_commit_sha = params[:last_commit_sha]
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index 08088f8c592..c9d3ee31d82 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -2,7 +2,7 @@
module Files
class MultiService < Files::BaseService
- UPDATE_FILE_ACTIONS = %w(update move delete).freeze
+ UPDATE_FILE_ACTIONS = %w(update move delete chmod).freeze
def create_commit!
transformer = Lfs::FileTransformer.new(project, @branch_name)
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 637c1df4ad9..f1883877d56 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -94,6 +94,7 @@ class GitPushService < BaseService
ProjectCacheWorker.perform_async(project.id, types, [:commit_count, :repository_size])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def update_signatures
commit_shas = last_pushed_commits.map(&:sha)
@@ -108,6 +109,7 @@ class GitPushService < BaseService
CreateGpgSignatureWorker.perform_async(commit_shas, project.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Schedules processing of commit messages.
def process_commit_messages
@@ -140,7 +142,6 @@ class GitPushService < BaseService
EventCreateService.new.push(project, current_user, build_push_data)
Ci::CreatePipelineService.new(project, current_user, build_push_data).execute(:push)
- SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks)
project.execute_hooks(build_push_data.dup, :push_hooks)
project.execute_services(build_push_data.dup, :push_hooks)
@@ -159,7 +160,7 @@ class GitPushService < BaseService
end
def process_default_branch
- offset = [push_commits_count - PROCESS_COMMIT_LIMIT, 0].max
+ offset = [push_commits_count_for_ref - PROCESS_COMMIT_LIMIT, 0].max
@push_commits = project.repository.commits(params[:newrev], offset: offset, limit: PROCESS_COMMIT_LIMIT)
project.after_create_default_branch
@@ -173,7 +174,7 @@ class GitPushService < BaseService
params[:newrev],
params[:ref],
@push_commits,
- commits_count: push_commits_count)
+ commits_count: commits_count)
end
def push_to_existing_branch?
@@ -214,8 +215,14 @@ class GitPushService < BaseService
end
end
- def push_commits_count
- strong_memoize(:push_commits_count) do
+ def commits_count
+ return push_commits_count_for_ref if default_branch? && push_to_new_branch?
+
+ Array(@push_commits).size
+ end
+
+ def push_commits_count_for_ref
+ strong_memoize(:push_commits_count_for_ref) do
project.repository.commit_count_for_ref(params[:ref])
end
end
diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb
index c4554ce45fb..641111aeadc 100644
--- a/app/services/groups/destroy_service.rb
+++ b/app/services/groups/destroy_service.rb
@@ -2,21 +2,26 @@
module Groups
class DestroyService < Groups::BaseService
+ DestroyError = Class.new(StandardError)
+
def async_execute
job_id = GroupDestroyWorker.perform_async(group.id, current_user.id)
Rails.logger.info("User #{current_user.id} scheduled a deletion of group ID #{group.id} with job ID #{job_id}")
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
group.prepare_for_destroy
- group.projects.each do |project|
+ group.projects.includes(:project_feature).each do |project|
# Execute the destruction of the models immediately to ensure atomic cleanup.
- # Skip repository removal because we remove directory with namespace
- # that contain all these repositories
- ::Projects::DestroyService.new(project, current_user, skip_repo: project.legacy_storage?).execute
+ success = ::Projects::DestroyService.new(project, current_user).execute
+ raise DestroyError, "Project #{project.id} can't be deleted" unless success
end
+ # reload the relation to prevent triggering destroy hooks on the projects again
+ group.projects.reload
+
group.children.each do |group|
# This needs to be synchronous since the namespace gets destroyed below
DestroyService.new(group, current_user).execute
@@ -26,5 +31,6 @@ module Groups
group.destroy
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index ea7576077f3..5efa746dfb9 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -64,9 +64,11 @@ module Groups
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def namespace_with_same_path?
Namespace.exists?(path: @group.path, parent: @new_parent_group)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update_group_attributes
if @new_parent_group && @new_parent_group.visibility_level < @group.visibility_level
@@ -78,6 +80,7 @@ module Groups
@group.save!
end
+ # rubocop: disable CodeReuse/ActiveRecord
def update_children_and_projects_visibility
descendants = @group.descendants.where("visibility_level > ?", @new_parent_group.visibility_level)
@@ -90,6 +93,7 @@ module Groups
.where("visibility_level > ?", @new_parent_group.visibility_level)
.update_all(visibility_level: @new_parent_group.visibility_level)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def raise_transfer_error(message)
raise TransferError, ERROR_MESSAGES[message]
diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb
index fe47aa2f140..0bf0e967dcc 100644
--- a/app/services/groups/update_service.rb
+++ b/app/services/groups/update_service.rb
@@ -14,9 +14,11 @@ module Groups
group.assign_attributes(params)
begin
- after_update if group.save
+ success = group.save
- true
+ after_update if success
+
+ success
rescue Gitlab::UpdatePathError => e
group.errors.add(:base, e.message)
diff --git a/app/services/import_export_clean_up_service.rb b/app/services/import_export_clean_up_service.rb
index e75a951944e..3ecb51b60d0 100644
--- a/app/services/import_export_clean_up_service.rb
+++ b/app/services/import_export_clean_up_service.rb
@@ -26,10 +26,12 @@ class ImportExportCleanUpService
Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete))
end
+ # rubocop: disable CodeReuse/ActiveRecord
def clean_up_export_object_files
ImportExportUpload.where('updated_at < ?', mmin.minutes.ago).each do |upload|
upload.remove_export_file!
upload.save!
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/services/issuable/bulk_update_service.rb b/app/services/issuable/bulk_update_service.rb
index 051d5ba881d..c4beddf2294 100644
--- a/app/services/issuable/bulk_update_service.rb
+++ b/app/services/issuable/bulk_update_service.rb
@@ -2,6 +2,7 @@
module Issuable
class BulkUpdateService < IssuableBaseService
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(type)
model_class = type.classify.constantize
update_class = type.classify.pluralize.constantize::UpdateService
@@ -28,6 +29,7 @@ module Issuable
success: !items.count.zero?
}
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/services/issuable/clone/attributes_rewriter.rb b/app/services/issuable/clone/attributes_rewriter.rb
new file mode 100644
index 00000000000..0300cc0d8d3
--- /dev/null
+++ b/app/services/issuable/clone/attributes_rewriter.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module Issuable
+ module Clone
+ class AttributesRewriter < ::Issuable::Clone::BaseService
+ def initialize(current_user, original_entity, new_entity)
+ @current_user = current_user
+ @original_entity = original_entity
+ @new_entity = new_entity
+ end
+
+ def execute
+ new_entity.update(milestone: cloneable_milestone, labels: cloneable_labels)
+ copy_resource_label_events
+ end
+
+ private
+
+ def cloneable_milestone
+ title = original_entity.milestone&.title
+ return unless title
+
+ params = { title: title, project_ids: new_entity.project&.id, group_ids: group&.id }
+
+ milestones = MilestonesFinder.new(params).execute
+ milestones.first
+ end
+
+ def cloneable_labels
+ params = {
+ project_id: new_entity.project&.id,
+ group_id: group&.id,
+ title: original_entity.labels.select(:title),
+ include_ancestor_groups: true
+ }
+
+ params[:only_group_labels] = true if new_parent.is_a?(Group)
+
+ LabelsFinder.new(current_user, params).execute
+ end
+
+ def copy_resource_label_events
+ original_entity.resource_label_events.find_in_batches do |batch|
+ events = batch.map do |event|
+ entity_key = new_entity.is_a?(Issue) ? 'issue_id' : 'epic_id'
+ # rubocop: disable CodeReuse/ActiveRecord
+ event.attributes
+ .except('id', 'reference', 'reference_html')
+ .merge(entity_key => new_entity.id, 'action' => ResourceLabelEvent.actions[event.action])
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+
+ Gitlab::Database.bulk_insert(ResourceLabelEvent.table_name, events)
+ end
+ end
+
+ def entity_key
+ new_entity.class.name.parameterize('_').foreign_key
+ end
+ end
+ end
+end
diff --git a/app/services/issuable/clone/base_service.rb b/app/services/issuable/clone/base_service.rb
new file mode 100644
index 00000000000..42dd9c666f5
--- /dev/null
+++ b/app/services/issuable/clone/base_service.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Issuable
+ module Clone
+ class BaseService < IssuableBaseService
+ attr_reader :original_entity, :new_entity
+
+ alias_method :old_project, :project
+
+ def execute(original_entity, new_project = nil)
+ @original_entity = original_entity
+
+ # Using transaction because of a high resources footprint
+ # on rewriting notes (unfolding references)
+ #
+ ActiveRecord::Base.transaction do
+ @new_entity = create_new_entity
+
+ update_new_entity
+ update_old_entity
+ create_notes
+ end
+ end
+
+ private
+
+ def update_new_entity
+ rewriters = [ContentRewriter, AttributesRewriter]
+
+ rewriters.each do |rewriter|
+ rewriter.new(current_user, original_entity, new_entity).execute
+ end
+ end
+
+ def update_old_entity
+ close_issue
+ end
+
+ def create_notes
+ add_note_from
+ add_note_to
+ end
+
+ def close_issue
+ close_service = Issues::CloseService.new(old_project, current_user)
+ close_service.execute(original_entity, notifications: false, system_note: false)
+ end
+
+ def new_parent
+ new_entity.project ? new_entity.project : new_entity.group
+ end
+
+ def group
+ if new_entity.project&.group && current_user.can?(:read_group, new_entity.project.group)
+ new_entity.project.group
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/issuable/clone/content_rewriter.rb b/app/services/issuable/clone/content_rewriter.rb
new file mode 100644
index 00000000000..e1e0b75085d
--- /dev/null
+++ b/app/services/issuable/clone/content_rewriter.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Issuable
+ module Clone
+ class ContentRewriter < ::Issuable::Clone::BaseService
+ def initialize(current_user, original_entity, new_entity)
+ @current_user = current_user
+ @original_entity = original_entity
+ @new_entity = new_entity
+ @project = original_entity.project
+ end
+
+ def execute
+ rewrite_description
+ rewrite_award_emoji(original_entity, new_entity)
+ rewrite_notes
+ end
+
+ private
+
+ def rewrite_description
+ new_entity.update(description: rewrite_content(original_entity.description))
+ end
+
+ def rewrite_notes
+ original_entity.notes_with_associations.find_each do |note|
+ new_note = note.dup
+ new_params = {
+ project: new_entity.project, noteable: new_entity,
+ note: rewrite_content(new_note.note),
+ created_at: note.created_at,
+ updated_at: note.updated_at
+ }
+
+ if note.system_note_metadata
+ new_params[:system_note_metadata] = note.system_note_metadata.dup
+ end
+
+ new_note.update(new_params)
+
+ rewrite_award_emoji(note, new_note)
+ end
+ end
+
+ def rewrite_content(content)
+ return unless content
+
+ rewriters = [Gitlab::Gfm::ReferenceRewriter, Gitlab::Gfm::UploadsRewriter]
+
+ rewriters.inject(content) do |text, klass|
+ rewriter = klass.new(text, old_project, current_user)
+ rewriter.rewrite(new_parent)
+ end
+ end
+
+ def rewrite_award_emoji(old_awardable, new_awardable)
+ old_awardable.award_emoji.each do |award|
+ new_award = award.dup
+ new_award.awardable = new_awardable
+ new_award.save
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb
index 028b350ca07..765de9c66b0 100644
--- a/app/services/issuable/common_system_notes_service.rb
+++ b/app/services/issuable/common_system_notes_service.rb
@@ -17,6 +17,7 @@ module Issuable
create_labels_note(old_labels) if issuable.labels != old_labels
create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked')
create_milestone_note if issuable.previous_changes.include?('milestone_id')
+ create_due_date_note if issuable.previous_changes.include?('due_date')
end
private
@@ -55,7 +56,9 @@ module Issuable
added_labels = issuable.labels - old_labels
removed_labels = old_labels - issuable.labels
- SystemNoteService.change_label(issuable, issuable.project, current_user, added_labels, removed_labels)
+ ResourceEvents::ChangeLabelsService
+ .new(issuable, current_user)
+ .execute(added_labels: added_labels, removed_labels: removed_labels)
end
def create_title_change_note(old_title)
@@ -88,6 +91,10 @@ module Issuable
SystemNoteService.change_milestone(issuable, issuable.project, current_user, issuable.milestone)
end
+ def create_due_date_note
+ SystemNoteService.change_due_date(issuable, issuable.project, current_user, issuable.due_date)
+ end
+
def create_discussion_lock_note
SystemNoteService.discussion_lock(issuable, current_user)
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 7d60c65bb79..e32e262ac31 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -3,6 +3,14 @@
class IssuableBaseService < BaseService
private
+ attr_accessor :params, :skip_milestone_email
+
+ def initialize(project, user = nil, params = {})
+ super
+
+ @skip_milestone_email = @params.delete(:skip_milestone_email)
+ end
+
def filter_params(issuable)
ability_name = :"admin_#{issuable.to_ability_name}"
@@ -68,11 +76,13 @@ class IssuableBaseService < BaseService
find_or_create_label_ids
end
+ # rubocop: disable CodeReuse/ActiveRecord
def filter_labels_in_param(key)
return if params[key].to_a.empty?
params[key] = available_labels.where(id: params[key]).pluck(:id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_or_create_label_ids
labels = params.delete(:labels)
@@ -116,12 +126,12 @@ class IssuableBaseService < BaseService
merge_quick_actions_into_params!(issuable)
end
- def merge_quick_actions_into_params!(issuable)
+ def merge_quick_actions_into_params!(issuable, only: nil)
original_description = params.fetch(:description, issuable.description)
description, command_params =
QuickActions::InterpretService.new(project, current_user)
- .execute(original_description, issuable)
+ .execute(original_description, issuable, only: only)
# Avoid a description already set on an issuable to be overwritten by a nil
params[:description] = description if description
@@ -129,28 +139,19 @@ class IssuableBaseService < BaseService
params.merge!(command_params)
end
- def create_issuable(issuable, attributes, label_ids:)
- issuable.with_transaction_returning_status do
- if issuable.save
- issuable.update(label_ids: label_ids)
- end
- end
- end
-
def create(issuable)
handle_quick_actions_on_create(issuable)
filter_params(issuable)
params.delete(:state_event)
params[:author] ||= current_user
-
- label_ids = process_label_ids(params)
+ params[:label_ids] = issuable.label_ids.to_a + process_label_ids(params)
issuable.assign_attributes(params)
before_create(issuable)
- if params.present? && create_issuable(issuable, params, label_ids: label_ids)
+ if issuable.save
after_create(issuable)
execute_hooks(issuable)
invalidate_cache_counts(issuable, users: issuable.assignees)
@@ -256,6 +257,7 @@ class IssuableBaseService < BaseService
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def change_todo(issuable)
case params.delete(:todo_event)
when 'add'
@@ -265,6 +267,7 @@ class IssuableBaseService < BaseService
todo_service.mark_todos_as_done_by_ids(todo, current_user) if todo
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def toggle_award(issuable)
award = params.delete(:emoji_award)
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 25389a946bb..ef08adf4f92 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -31,6 +31,7 @@ module Issues
issue.project.execute_services(issue_data, hooks_scope)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def filter_assignee(issuable)
return if params[:assignee_ids].blank?
@@ -48,6 +49,7 @@ module Issues
params.delete(:assignee_ids)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update_project_counter_caches?(issue)
super || issue.confidential_changed?
diff --git a/app/services/issues/fetch_referenced_merge_requests_service.rb b/app/services/issues/fetch_referenced_merge_requests_service.rb
deleted file mode 100644
index 5e84f3c81c9..00000000000
--- a/app/services/issues/fetch_referenced_merge_requests_service.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-module Issues
- class FetchReferencedMergeRequestsService < Issues::BaseService
- def execute(issue)
- referenced_merge_requests = issue.referenced_merge_requests(current_user)
- referenced_merge_requests = Gitlab::IssuableSorter.sort(project, referenced_merge_requests) { |i| i.iid.to_s }
- closed_by_merge_requests = issue.closed_by_merge_requests(current_user)
- closed_by_merge_requests = Gitlab::IssuableSorter.sort(project, closed_by_merge_requests) { |i| i.iid.to_s }
-
- [referenced_merge_requests, closed_by_merge_requests]
- end
- end
-end
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index 841bce9949e..41b6a96b005 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -1,148 +1,66 @@
# frozen_string_literal: true
module Issues
- class MoveService < Issues::BaseService
+ class MoveService < Issuable::Clone::BaseService
MoveError = Class.new(StandardError)
- def execute(issue, new_project)
- @old_issue = issue
- @old_project = @project
- @new_project = new_project
+ def execute(issue, target_project)
+ @target_project = target_project
- unless issue.can_move?(current_user, new_project)
+ unless issue.can_move?(current_user, @target_project)
raise MoveError, 'Cannot move issue due to insufficient permissions!'
end
- if @project == new_project
+ if @project == @target_project
raise MoveError, 'Cannot move issue to project it originates from!'
end
- # Using transaction because of a high resources footprint
- # on rewriting notes (unfolding references)
- #
- ActiveRecord::Base.transaction do
- @new_issue = create_new_issue
-
- update_new_issue
- update_old_issue
- end
+ super
notify_participants
- @new_issue
+ new_entity
end
private
- def update_new_issue
- rewrite_notes
- rewrite_issue_award_emoji
- add_note_moved_from
- end
+ def update_old_entity
+ super
- def update_old_issue
- add_note_moved_to
- close_issue
mark_as_moved
end
- def create_new_issue
- new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids,
- milestone_id: cloneable_milestone_id,
- project: @new_project, author: @old_issue.author,
- description: rewrite_content(@old_issue.description),
- assignee_ids: @old_issue.assignee_ids }
-
- new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params)
- CreateService.new(@new_project, @current_user, new_params).execute
- end
-
- def cloneable_label_ids
- params = {
- project_id: @new_project.id,
- title: @old_issue.labels.pluck(:title),
- include_ancestor_groups: true
- }
-
- LabelsFinder.new(current_user, params).execute.pluck(:id)
- end
-
- def cloneable_milestone_id
- title = @old_issue.milestone&.title
- return unless title
-
- if @new_project.group && can?(current_user, :read_group, @new_project.group)
- group_id = @new_project.group.id
- end
-
- params =
- { title: title, project_ids: @new_project.id, group_ids: group_id }
-
- milestones = MilestonesFinder.new(params).execute
- milestones.first&.id
- end
-
- def rewrite_notes
- @old_issue.notes_with_associations.find_each do |note|
- new_note = note.dup
- new_params = { project: @new_project, noteable: @new_issue,
- note: rewrite_content(new_note.note),
- created_at: note.created_at,
- updated_at: note.updated_at }
-
- new_note.update(new_params)
-
- rewrite_award_emoji(note, new_note)
- end
- end
-
- def rewrite_issue_award_emoji
- rewrite_award_emoji(@old_issue, @new_issue)
+ def create_new_entity
+ new_params = {
+ id: nil,
+ iid: nil,
+ project: @target_project,
+ author: original_entity.author,
+ assignee_ids: original_entity.assignee_ids
+ }
+
+ new_params = original_entity.serializable_hash.symbolize_keys.merge(new_params)
+ CreateService.new(@target_project, @current_user, new_params).execute
end
- def rewrite_award_emoji(old_awardable, new_awardable)
- old_awardable.award_emoji.each do |award|
- new_award = award.dup
- new_award.awardable = new_awardable
- new_award.save
- end
- end
-
- def rewrite_content(content)
- return unless content
-
- rewriters = [Gitlab::Gfm::ReferenceRewriter,
- Gitlab::Gfm::UploadsRewriter]
-
- rewriters.inject(content) do |text, klass|
- rewriter = klass.new(text, @old_project, @current_user)
- rewriter.rewrite(@new_project)
- end
+ def mark_as_moved
+ original_entity.update(moved_to: new_entity)
end
- def close_issue
- close_service = CloseService.new(@old_project, @current_user)
- close_service.execute(@old_issue, notifications: false, system_note: false)
+ def notify_participants
+ notification_service.async.issue_moved(original_entity, new_entity, @current_user)
end
- def add_note_moved_from
- SystemNoteService.noteable_moved(@new_issue, @new_project,
- @old_issue, @current_user,
+ def add_note_from
+ SystemNoteService.noteable_moved(new_entity, @target_project,
+ original_entity, current_user,
direction: :from)
end
- def add_note_moved_to
- SystemNoteService.noteable_moved(@old_issue, @old_project,
- @new_issue, @current_user,
+ def add_note_to
+ SystemNoteService.noteable_moved(original_entity, old_project,
+ new_entity, current_user,
direction: :to)
end
-
- def mark_as_moved
- @old_issue.update(moved_to: @new_issue)
- end
-
- def notify_participants
- notification_service.async.issue_moved(@old_issue, @new_issue, @current_user)
- end
end
end
diff --git a/app/services/issues/referenced_merge_requests_service.rb b/app/services/issues/referenced_merge_requests_service.rb
new file mode 100644
index 00000000000..a69cd324b1e
--- /dev/null
+++ b/app/services/issues/referenced_merge_requests_service.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Issues
+ class ReferencedMergeRequestsService < Issues::BaseService
+ # rubocop: disable CodeReuse/ActiveRecord
+ def execute(issue)
+ referenced = referenced_merge_requests(issue)
+ closed_by = closed_by_merge_requests(issue)
+ preloader = ActiveRecord::Associations::Preloader.new
+
+ preloader.preload(referenced + closed_by,
+ head_pipeline: { project: [:route, { namespace: :route }] })
+
+ [sort_by_iid(referenced), sort_by_iid(closed_by)]
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def referenced_merge_requests(issue)
+ merge_requests = extract_merge_requests(issue)
+
+ cross_project_filter = -> (merge_requests) do
+ merge_requests.select { |mr| mr.target_project == project }
+ end
+
+ Ability.merge_requests_readable_by_user(
+ merge_requests,
+ current_user,
+ filters: {
+ read_cross_project: cross_project_filter
+ }
+ )
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def closed_by_merge_requests(issue)
+ return [] unless issue.open?
+
+ merge_requests = extract_merge_requests(issue, filter: :system).select(&:open?)
+
+ return [] if merge_requests.empty?
+
+ ids = MergeRequestsClosingIssues.where(merge_request_id: merge_requests.map(&:id), issue_id: issue.id).pluck(:merge_request_id)
+ merge_requests.select { |mr| mr.id.in?(ids) }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ def extract_merge_requests(issue, filter: nil)
+ ext = issue.all_references(current_user)
+ notes = issue_notes(issue)
+ notes = notes.select(&filter) if filter
+
+ notes.each do |note|
+ note.all_references(current_user, extractor: ext)
+ end
+
+ ext.merge_requests
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def issue_notes(issue)
+ @issue_notes ||= {}
+ @issue_notes[issue] ||= issue.notes.includes(:author)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def sort_by_iid(merge_requests)
+ Gitlab::IssuableSorter.sort(project, merge_requests) { |mr| mr.iid.to_s }
+ end
+ end
+end
diff --git a/app/services/issues/related_branches_service.rb b/app/services/issues/related_branches_service.rb
new file mode 100644
index 00000000000..76af482b7ac
--- /dev/null
+++ b/app/services/issues/related_branches_service.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# This service fetches all branches containing the current issue's ID, except for
+# those with a merge request open referencing the current issue.
+module Issues
+ class RelatedBranchesService < Issues::BaseService
+ def execute(issue)
+ branches_with_iid_of(issue) - branches_with_merge_request_for(issue)
+ end
+
+ private
+
+ def branches_with_merge_request_for(issue)
+ Issues::ReferencedMergeRequestsService
+ .new(project, current_user)
+ .referenced_merge_requests(issue)
+ .map(&:source_branch)
+ end
+
+ def branches_with_iid_of(issue)
+ project.repository.branch_names.select do |branch|
+ branch =~ /\A#{issue.iid}-(?!\d+-stable)/i
+ end
+ end
+ end
+end
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index 3bd53f9ccdc..56d59b235a7 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -3,7 +3,7 @@
module Issues
class ReopenService < Issues::BaseService
def execute(issue)
- return issue unless can?(current_user, :update_issue, issue)
+ return issue unless can?(current_user, :reopen_issue, issue)
if issue.reopen
event_service.reopen_issue(issue, current_user)
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index faa4c8a5a4f..a1d0cc0e568 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -11,6 +11,12 @@ module Issues
move_issue_to_new_project(issue) || update(issue)
end
+ def update(issue)
+ create_merge_request_from_quick_action
+
+ super
+ end
+
def before_update(issue)
spam_check(issue, current_user)
end
@@ -48,6 +54,8 @@ module Issues
notification_service.async.relabeled_issue(issue, added_labels, current_user)
end
+ handle_milestone_change(issue)
+
added_mentions = issue.mentioned_users - old_mentioned_users
if added_mentions.present?
@@ -67,6 +75,7 @@ module Issues
issue.move_between(issue_before, issue_after)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def change_issue_duplicate(issue)
canonical_issue_id = params.delete(:canonical_issue_id)
canonical_issue = IssuesFinder.new(current_user).find_by(id: canonical_issue_id)
@@ -75,6 +84,7 @@ module Issues
Issues::DuplicateService.new(project, current_user).execute(issue, canonical_issue)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def move_issue_to_new_project(issue)
target_project = params.delete(:target_project)
@@ -89,6 +99,26 @@ module Issues
private
+ def create_merge_request_from_quick_action
+ create_merge_request_params = params.delete(:create_merge_request)
+ return unless create_merge_request_params
+
+ MergeRequests::CreateFromIssueService.new(project, current_user, create_merge_request_params).execute
+ end
+
+ def handle_milestone_change(issue)
+ return if skip_milestone_email
+
+ return unless issue.previous_changes.include?('milestone_id')
+
+ if issue.milestone.nil?
+ notification_service.async.removed_milestone_issue(issue, current_user)
+ else
+ notification_service.async.changed_milestone_issue(issue, issue.milestone, current_user)
+ end
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
def get_issue_if_allowed(id, board_group_id = nil)
return unless id
@@ -101,6 +131,7 @@ module Issues
issue if can?(current_user, :update_issue, issue)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def create_confidentiality_note(issue)
SystemNoteService.change_issue_confidentiality(issue, issue.project, current_user)
diff --git a/app/services/keys/destroy_service.rb b/app/services/keys/destroy_service.rb
index e2ae4047941..159455f80f3 100644
--- a/app/services/keys/destroy_service.rb
+++ b/app/services/keys/destroy_service.rb
@@ -6,7 +6,7 @@ module Keys
key.destroy if destroy_possible?(key)
end
- # overriden in EE::Keys::DestroyService
+ # overridden in EE::Keys::DestroyService
def destroy_possible?(key)
true
end
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index e4486764a4d..628873519d7 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -29,6 +29,7 @@ module Labels
# Only creates the label if current_user can do so, if the label does not exist
# and the user can not create the label, nil is returned
+ # rubocop: disable CodeReuse/ActiveRecord
def find_or_create_label
new_label = available_labels.find_by(title: title)
@@ -39,6 +40,7 @@ module Labels
new_label
end
+ # rubocop: enable CodeReuse/ActiveRecord
def title
params[:title] || params[:name]
diff --git a/app/services/labels/promote_service.rb b/app/services/labels/promote_service.rb
index c0463052821..f30ad706c63 100644
--- a/app/services/labels/promote_service.rb
+++ b/app/services/labels/promote_service.rb
@@ -4,6 +4,7 @@ module Labels
class PromoteService < BaseService
BATCH_SIZE = 1000
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(label)
return unless project.group &&
label.is_a?(ProjectLabel)
@@ -13,6 +14,7 @@ module Labels
label_ids_for_merge(new_label).find_in_batches(batch_size: BATCH_SIZE) do |batched_ids|
update_issuables(new_label, batched_ids)
+ update_resource_label_events(new_label, batched_ids)
update_issue_board_lists(new_label, batched_ids)
update_priorities(new_label, batched_ids)
subscribe_users(new_label, batched_ids)
@@ -26,9 +28,11 @@ module Labels
new_label
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ # rubocop: disable CodeReuse/ActiveRecord
def subscribe_users(new_label, label_ids)
# users can be subscribed to multiple labels that will be merged into the group one
# we want to keep only one subscription / user
@@ -37,7 +41,9 @@ module Labels
.pluck('MAX(id)')
Subscription.where(id: ids_to_update).update_all(subscribable_id: new_label.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def label_ids_for_merge(new_label)
LabelsFinder
.new(current_user, title: new_label.title, group_id: project.group.id)
@@ -45,28 +51,45 @@ module Labels
.where.not(id: new_label)
.select(:id) # Can't use pluck() to avoid object-creation because of the batching
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def update_issuables(new_label, label_ids)
LabelLink
.where(label: label_ids)
.update_all(label_id: new_label)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
+ def update_resource_label_events(new_label, label_ids)
+ ResourceLabelEvent
+ .where(label: label_ids)
+ .update_all(label_id: new_label)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
def update_issue_board_lists(new_label, label_ids)
List
.where(label: label_ids)
.update_all(label_id: new_label)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def update_priorities(new_label, label_ids)
LabelPriority
.where(label: label_ids)
.update_all(label_id: new_label)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def update_project_labels(label_ids)
- Label.where(id: label_ids).destroy_all
+ Label.where(id: label_ids).destroy_all # rubocop: disable DestroyAll
end
+ # rubocop: enable CodeReuse/ActiveRecord
def clone_label_to_group_label(label)
params = label.attributes.slice('title', 'description', 'color')
diff --git a/app/services/labels/transfer_service.rb b/app/services/labels/transfer_service.rb
index 1bd8d9fc325..9cbc9fef529 100644
--- a/app/services/labels/transfer_service.rb
+++ b/app/services/labels/transfer_service.rb
@@ -32,16 +32,19 @@ module Labels
attr_reader :current_user, :old_group, :project
+ # rubocop: disable CodeReuse/ActiveRecord
def labels_to_transfer
- label_ids = []
- label_ids << group_labels_applied_to_issues.select(:id)
- label_ids << group_labels_applied_to_merge_requests.select(:id)
-
- union = Gitlab::SQL::Union.new(label_ids)
-
- Label.where("labels.id IN (#{union.to_sql})").reorder(nil).uniq # rubocop:disable GitlabSecurity/SqlInjection
+ Label
+ .from_union([
+ group_labels_applied_to_issues,
+ group_labels_applied_to_merge_requests
+ ])
+ .reorder(nil)
+ .distinct
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def group_labels_applied_to_issues
Label.joins(:issues)
.where(
@@ -49,7 +52,9 @@ module Labels
labels: { type: 'GroupLabel', group_id: old_group.id }
)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def group_labels_applied_to_merge_requests
Label.joins(:merge_requests)
.where(
@@ -57,6 +62,7 @@ module Labels
labels: { type: 'GroupLabel', group_id: old_group.id }
)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_or_create_label!(label)
params = label.attributes.slice('title', 'description', 'color')
@@ -65,6 +71,7 @@ module Labels
new_label.id
end
+ # rubocop: disable CodeReuse/ActiveRecord
def update_label_links(labels, old_label_id:, new_label_id:)
# use 'labels' relation to get label_link ids only of issues/MRs
# in the project being transferred.
@@ -76,10 +83,13 @@ module Labels
LabelLink.where(id: link_ids, label_id: old_label_id)
.update_all(label_id: new_label_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def update_label_priorities(old_label_id:, new_label_id:)
LabelPriority.where(project_id: project.id, label_id: old_label_id)
.update_all(label_id: new_label_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/lfs/file_transformer.rb b/app/services/lfs/file_transformer.rb
index c8eccb8e6cd..6ecf583cb6a 100644
--- a/app/services/lfs/file_transformer.rb
+++ b/app/services/lfs/file_transformer.rb
@@ -55,11 +55,13 @@ module Lfs
@cached_attributes ||= Gitlab::Git::AttributesAtRefParser.new(repository, branch_name)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def create_lfs_object!(lfs_pointer_file, file_content)
LfsObject.find_or_create_by(oid: lfs_pointer_file.sha256, size: lfs_pointer_file.size) do |lfs_object|
lfs_object.file = CarrierWaveStringFile.new(file_content)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def link_lfs_object!(lfs_object)
project.lfs_objects << lfs_object
diff --git a/app/services/lfs/lock_file_service.rb b/app/services/lfs/lock_file_service.rb
index 78434909d68..c7730d24bdc 100644
--- a/app/services/lfs/lock_file_service.rb
+++ b/app/services/lfs/lock_file_service.rb
@@ -18,9 +18,11 @@ module Lfs
private
+ # rubocop: disable CodeReuse/ActiveRecord
def current_lock
project.lfs_file_locks.find_by(path: params[:path])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def create_lock!
lock = project.lfs_file_locks.create!(user: current_user,
diff --git a/app/services/lfs/locks_finder_service.rb b/app/services/lfs/locks_finder_service.rb
index d52cf0e3cc4..4a5b2a52921 100644
--- a/app/services/lfs/locks_finder_service.rb
+++ b/app/services/lfs/locks_finder_service.rb
@@ -10,10 +10,12 @@ module Lfs
private
+ # rubocop: disable CodeReuse/ActiveRecord
def find_locks
options = params.slice(:id, :path).compact.symbolize_keys
project.lfs_file_locks.where(options)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/lfs/unlock_file_service.rb b/app/services/lfs/unlock_file_service.rb
index 4d1443bf772..a42916d86bb 100644
--- a/app/services/lfs/unlock_file_service.rb
+++ b/app/services/lfs/unlock_file_service.rb
@@ -32,6 +32,7 @@ module Lfs
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def lock
return @lock if defined?(@lock)
@@ -41,5 +42,6 @@ module Lfs
project.lfs_file_locks.find_by!(path: params[:path])
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/members/base_service.rb b/app/services/members/base_service.rb
index 8248f1441d7..d734571f835 100644
--- a/app/services/members/base_service.rb
+++ b/app/services/members/base_service.rb
@@ -10,7 +10,7 @@ module Members
end
def after_execute(args)
- # overriden in EE::Members modules
+ # overridden in EE::Members modules
end
private
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index e6dd0e12a3a..28c3219b37b 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -55,13 +55,15 @@ module MergeRequests
end
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_requests_for(source_branch, mr_states: [:opened])
- MergeRequest
+ @project.source_of_merge_requests
.with_state(mr_states)
- .where(source_branch: source_branch, source_project_id: @project.id)
- .preload(:source_project) # we don't need a #includes since we're just preloading for the #select
+ .where(source_branch: source_branch)
+ .preload(:source_project) # we don't need #includes since we're just preloading for the #select
.select(&:source_project)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def pipeline_merge_requests(pipeline)
merge_requests_for(pipeline.ref).each do |merge_request|
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index bc988eb2a26..6c69452e2ab 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -6,8 +6,12 @@ module MergeRequests
def execute
@params_issue_iid = params.delete(:issue_iid)
+ self.merge_request = MergeRequest.new
+ # TODO: this should handle all quick actions that don't have side effects
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/53658
+ merge_quick_actions_into_params!(merge_request, only: [:target_branch])
+ merge_request.assign_attributes(params)
- self.merge_request = MergeRequest.new(params)
merge_request.author = current_user
merge_request.compare_commits = []
merge_request.source_project = find_source_project
@@ -20,6 +24,8 @@ module MergeRequests
if merge_request.can_be_created
compare_branches
assign_title_and_description
+ assign_labels
+ assign_milestone
end
merge_request
@@ -128,14 +134,27 @@ module MergeRequests
#
def assign_title_and_description
assign_title_and_description_from_single_commit
- assign_title_from_issue if target_project.issues_enabled? || target_project.external_issue_tracker
-
+ merge_request.title ||= title_from_issue if target_project.issues_enabled? || target_project.external_issue_tracker
merge_request.title ||= source_branch.titleize.humanize
merge_request.title = wip_title if compare_commits.empty?
append_closes_description
end
+ def assign_labels
+ return unless target_project.issues_enabled? && issue
+ return if merge_request.label_ids&.any?
+
+ merge_request.label_ids = issue.try(:label_ids)
+ end
+
+ def assign_milestone
+ return unless target_project.issues_enabled? && issue
+ return if merge_request.milestone_id.present?
+
+ merge_request.milestone_id = issue.try(:milestone_id)
+ end
+
def append_closes_description
return unless issue&.to_reference.present?
@@ -159,20 +178,18 @@ module MergeRequests
merge_request.description ||= commit.description.try(:strip)
end
- def assign_title_from_issue
+ def title_from_issue
return unless issue
- merge_request.title = "Resolve \"#{issue.title}\"" if issue.is_a?(Issue)
+ return "Resolve \"#{issue.title}\"" if issue.is_a?(Issue)
- return if merge_request.title.present?
+ return if issue_iid.blank?
- if issue_iid.present?
- title_parts = ["Resolve #{issue.to_reference}"]
- branch_title = source_branch.downcase.remove(issue_iid.downcase).titleize.humanize
+ title_parts = ["Resolve #{issue.to_reference}"]
+ branch_title = source_branch.downcase.remove(issue_iid.downcase).titleize.humanize
- title_parts << "\"#{branch_title}\"" if branch_title.present?
- merge_request.title = title_parts.join(' ')
- end
+ title_parts << "\"#{branch_title}\"" if branch_title.present?
+ title_parts.join(' ')
end
def issue_iid
@@ -188,7 +205,9 @@ module MergeRequests
end
def issue
- @issue ||= target_project.get_issue(issue_iid, current_user)
+ strong_memoize(:issue) do
+ target_project.get_issue(issue_iid, current_user)
+ end
end
end
end
diff --git a/app/services/merge_requests/create_from_issue_service.rb b/app/services/merge_requests/create_from_issue_service.rb
index fd91dc4acd0..e69791872cc 100644
--- a/app/services/merge_requests/create_from_issue_service.rb
+++ b/app/services/merge_requests/create_from_issue_service.rb
@@ -16,27 +16,29 @@ module MergeRequests
def execute
return error('Invalid issue iid') unless @issue_iid.present? && issue.present?
- params[:label_ids] = issue.label_ids if issue.label_ids.any?
-
result = CreateBranchService.new(project, current_user).execute(branch_name, ref)
return result if result[:status] == :error
- SystemNoteService.new_issue_branch(issue, project, current_user, branch_name)
-
new_merge_request = create(merge_request)
if new_merge_request.valid?
+ SystemNoteService.new_merge_request(issue, project, current_user, new_merge_request)
+
success(new_merge_request)
else
+ SystemNoteService.new_issue_branch(issue, project, current_user, branch_name)
+
error(new_merge_request.errors)
end
end
private
+ # rubocop: disable CodeReuse/ActiveRecord
def issue
@issue ||= IssuesFinder.new(current_user, project_id: project.id).find_by(iid: @issue_iid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def branch_name
@branch ||= @branch_name || issue.to_branch_name
@@ -58,8 +60,7 @@ module MergeRequests
source_project_id: project.id,
source_branch: branch_name,
target_project_id: project.id,
- target_branch: ref,
- milestone_id: issue.milestone_id
+ target_branch: ref
}
end
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index c36a2ecbfe3..6081a7d1de0 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -49,6 +49,7 @@ module MergeRequests
merge_request.update(head_pipeline_id: pipeline.id) if pipeline
end
+ # rubocop: disable CodeReuse/ActiveRecord
def head_pipeline_for(merge_request)
return unless merge_request.source_project
@@ -59,6 +60,7 @@ module MergeRequests
pipelines.order(id: :desc).first
end
+ # rubocop: enable CodeReuse/ActiveRecord
def set_projects!
# @project is used to determine whether the user can set the merge request's
diff --git a/app/services/merge_requests/delete_non_latest_diffs_service.rb b/app/services/merge_requests/delete_non_latest_diffs_service.rb
index 2a8ea316921..d5929446122 100644
--- a/app/services/merge_requests/delete_non_latest_diffs_service.rb
+++ b/app/services/merge_requests/delete_non_latest_diffs_service.rb
@@ -8,6 +8,7 @@ module MergeRequests
@merge_request = merge_request
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
diffs = @merge_request.non_latest_diffs.with_files
@@ -16,5 +17,6 @@ module MergeRequests
DeleteDiffFilesWorker.bulk_perform_in(index * 5.minutes, ids)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index fb44f809c41..70a67baa01c 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -49,6 +49,11 @@ module MergeRequests
end
end
+ # Overridden in EE.
+ def hooks_validation_pass?(_merge_request)
+ true
+ end
+
private
def error_check!
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 48da796505f..5fe48da1cd6 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -2,18 +2,18 @@
module MergeRequests
class RefreshService < MergeRequests::BaseService
+ attr_reader :push
+
def execute(oldrev, newrev, ref)
- return true unless Gitlab::Git.branch_ref?(ref)
+ @push = Gitlab::Git::Push.new(@project, oldrev, newrev, ref)
+ return true unless @push.branch_push?
- do_execute(oldrev, newrev, ref)
+ refresh_merge_requests!
end
private
- def do_execute(oldrev, newrev, ref)
- @oldrev, @newrev = oldrev, newrev
- @branch_name = Gitlab::Git.ref_name(ref)
-
+ def refresh_merge_requests!
Gitlab::GitalyClient.allow_n_plus_1_calls(&method(:find_new_commits))
# Be sure to close outstanding MRs before reloading them to avoid generating an
# empty diff during a manual merge
@@ -25,7 +25,7 @@ module MergeRequests
cache_merge_requests_closing_issues
# Leave a system note if a branch was deleted/added
- if branch_added? || branch_removed?
+ if @push.branch_added? || @push.branch_removed?
comment_mr_branch_presence_changed
end
@@ -51,10 +51,13 @@ module MergeRequests
# and close if push to master include last commit from merge request
# We need this to close(as merged) merge requests that were merged into
# target branch manually
+ # rubocop: disable CodeReuse/ActiveRecord
def post_merge_manually_merged
commit_ids = @commits.map(&:id)
- merge_requests = @project.merge_requests.preload(:latest_merge_request_diff).opened.where(target_branch: @branch_name).to_a
- merge_requests = merge_requests.select(&:diff_head_commit)
+ merge_requests = @project.merge_requests.opened
+ .preload(:latest_merge_request_diff)
+ .where(target_branch: @push.branch_name).to_a
+ .select(&:diff_head_commit)
merge_requests = merge_requests.select do |merge_request|
commit_ids.include?(merge_request.diff_head_sha) &&
@@ -67,30 +70,25 @@ module MergeRequests
.execute(merge_request)
end
end
-
- def force_push?
- Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
- end
+ # rubocop: enable CodeReuse/ActiveRecord
# Refresh merge request diff if we push to source or target branch of merge request
# Note: we should update merge requests from forks too
+ # rubocop: disable CodeReuse/ActiveRecord
def reload_merge_requests
merge_requests = @project.merge_requests.opened
- .by_source_or_target_branch(@branch_name).to_a
+ .by_source_or_target_branch(@push.branch_name).to_a
# Fork merge requests
merge_requests += MergeRequest.opened
- .where(source_branch: @branch_name, source_project: @project)
+ .where(source_branch: @push.branch_name, source_project: @project)
.where.not(target_project: @project).to_a
filter_merge_requests(merge_requests).each do |merge_request|
- if merge_request.source_branch == @branch_name || force_push?
+ if branch_and_project_match?(merge_request) || @push.force_push?
+ merge_request.reload_diff(current_user)
+ elsif merge_request.includes_any_commits?(push_commit_ids)
merge_request.reload_diff(current_user)
- else
- mr_commit_ids = merge_request.commit_shas
- push_commit_ids = @commits.map(&:id)
- matches = mr_commit_ids & push_commit_ids
- merge_request.reload_diff(current_user) if matches.any?
end
merge_request.mark_as_unchecked
@@ -101,6 +99,16 @@ module MergeRequests
# @source_merge_requests diffs (for MergeRequest#commit_shas for instance).
merge_requests_for_source_branch(reload: true)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def push_commit_ids
+ @push_commit_ids ||= @commits.map(&:id)
+ end
+
+ def branch_and_project_match?(merge_request)
+ merge_request.source_project == @project &&
+ merge_request.source_branch == @push.branch_name
+ end
def reset_merge_when_pipeline_succeeds
merge_requests_for_source_branch.each(&:reset_merge_when_pipeline_succeeds)
@@ -113,7 +121,7 @@ module MergeRequests
end
def find_new_commits
- if branch_added?
+ if @push.branch_added?
@commits = []
merge_request = merge_requests_for_source_branch.first
@@ -122,28 +130,28 @@ module MergeRequests
begin
# Since any number of commits could have been made to the restored branch,
# find the common root to see what has been added.
- common_ref = @project.repository.merge_base(merge_request.diff_head_sha, @newrev)
+ common_ref = @project.repository.merge_base(merge_request.diff_head_sha, @push.newrev)
# If the a commit no longer exists in this repo, gitlab_git throws
# a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
- @commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
+ @commits = @project.repository.commits_between(common_ref, @push.newrev) if common_ref
rescue
end
- elsif branch_removed?
+ elsif @push.branch_removed?
# No commits for a deleted branch.
@commits = []
else
- @commits = @project.repository.commits_between(@oldrev, @newrev)
+ @commits = @project.repository.commits_between(@push.oldrev, @push.newrev)
end
end
# Add comment about branches being deleted or added to merge requests
def comment_mr_branch_presence_changed
- presence = branch_added? ? :add : :delete
+ presence = @push.branch_added? ? :add : :delete
merge_requests_for_source_branch.each do |merge_request|
SystemNoteService.change_branch_presence(
merge_request, merge_request.project, @current_user,
- :source, @branch_name, presence)
+ :source, @push.branch_name, presence)
end
end
@@ -160,7 +168,7 @@ module MergeRequests
SystemNoteService.add_commits(merge_request, merge_request.project,
@current_user, new_commits,
- existing_commits, @oldrev)
+ existing_commits, @push.oldrev)
notification_service.push_to_merge_request(merge_request, @current_user, new_commits: new_commits, existing_commits: existing_commits)
end
@@ -191,17 +199,19 @@ module MergeRequests
# Call merge request webhook with update branches
def execute_mr_web_hooks
merge_requests_for_source_branch.each do |merge_request|
- execute_hooks(merge_request, 'update', old_rev: @oldrev)
+ execute_hooks(merge_request, 'update', old_rev: @push.oldrev)
end
end
# If the merge requests closes any issues, save this information in the
# `MergeRequestsClosingIssues` model (as a performance optimization).
+ # rubocop: disable CodeReuse/ActiveRecord
def cache_merge_requests_closing_issues
- @project.merge_requests.where(source_branch: @branch_name).each do |merge_request|
+ @project.merge_requests.where(source_branch: @push.branch_name).each do |merge_request|
merge_request.cache_merge_request_closes_issues!(@current_user)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def filter_merge_requests(merge_requests)
merge_requests.uniq.select(&:source_project)
@@ -209,15 +219,7 @@ module MergeRequests
def merge_requests_for_source_branch(reload: false)
@source_merge_requests = nil if reload
- @source_merge_requests ||= merge_requests_for(@branch_name)
- end
-
- def branch_added?
- Gitlab::Git.blank_ref?(@oldrev)
- end
-
- def branch_removed?
- Gitlab::Git.blank_ref?(@newrev)
+ @source_merge_requests ||= merge_requests_for(@push.branch_name)
end
end
end
diff --git a/app/services/merge_requests/reload_diffs_service.rb b/app/services/merge_requests/reload_diffs_service.rb
index 8d85dc9eb5f..c64b2e99b52 100644
--- a/app/services/merge_requests/reload_diffs_service.rb
+++ b/app/services/merge_requests/reload_diffs_service.rb
@@ -27,19 +27,26 @@ module MergeRequests
current_user: current_user)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def clear_cache(new_diff)
- # Executing the iteration we cache highlighted diffs for each diff file of
- # MergeRequestDiff.
- new_diff.diffs_collection.diff_files.to_a
-
# Remove cache for all diffs on this MR. Do not use the association on the
# model, as that will interfere with other actions happening when
# reloading the diff.
- MergeRequestDiff.where(merge_request: merge_request).each do |merge_request_diff|
+ MergeRequestDiff
+ .where(merge_request: merge_request)
+ .preload(merge_request: :target_project)
+ .find_each do |merge_request_diff|
next if merge_request_diff == new_diff
- merge_request_diff.diffs_collection.clear_cache!
+ cacheable_collection(merge_request_diff).clear_cache
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def cacheable_collection(diff)
+ # There are scenarios where we don't need to request Diff Stats.
+ # Mainly when clearing / writing diff caches.
+ diff.diffs(include_stats: false)
+ end
end
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index b112edbce7f..aacaf10d09c 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -58,6 +58,8 @@ module MergeRequests
merge_request.mark_as_unchecked
end
+ handle_milestone_change(merge_request)
+
added_labels = merge_request.labels - old_labels
if added_labels.present?
notification_service.async.relabeled_merge_request(
@@ -105,6 +107,18 @@ module MergeRequests
private
+ def handle_milestone_change(merge_request)
+ return if skip_milestone_email
+
+ return unless merge_request.previous_changes.include?('milestone_id')
+
+ if merge_request.milestone.nil?
+ notification_service.async.removed_milestone_merge_request(merge_request, current_user)
+ else
+ notification_service.async.changed_milestone_merge_request(merge_request, merge_request.milestone, current_user)
+ end
+ end
+
def create_branch_change_note(issuable, branch_type, old_branch, new_branch)
SystemNoteService.change_branch(
issuable, issuable.project, current_user, branch_type,
diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb
index 15c04525075..87c7a282081 100644
--- a/app/services/milestones/destroy_service.rb
+++ b/app/services/milestones/destroy_service.rb
@@ -3,10 +3,8 @@
module Milestones
class DestroyService < Milestones::BaseService
def execute(milestone)
- return unless milestone.project_milestone?
-
Milestone.transaction do
- update_params = { milestone: nil }
+ update_params = { milestone: nil, skip_milestone_email: true }
milestone.issues.each do |issue|
Issues::UpdateService.new(parent, current_user, update_params).execute(issue)
@@ -16,15 +14,21 @@ module Milestones
MergeRequests::UpdateService.new(parent, current_user, update_params).execute(merge_request)
end
- event_service.destroy_milestone(milestone, current_user)
-
- Event.for_milestone_id(milestone.id).each do |event|
- event.target_id = nil
- event.save
- end
+ log_destroy_event_for(milestone)
milestone.destroy
end
end
+
+ def log_destroy_event_for(milestone)
+ return if milestone.group_milestone?
+
+ event_service.destroy_milestone(milestone, current_user)
+
+ Event.for_milestone_id(milestone.id).each do |event|
+ event.target_id = nil
+ event.save
+ end
+ end
end
end
diff --git a/app/services/milestones/promote_service.rb b/app/services/milestones/promote_service.rb
index 37aa6d3a9bc..39071b5dc14 100644
--- a/app/services/milestones/promote_service.rb
+++ b/app/services/milestones/promote_service.rb
@@ -26,6 +26,7 @@ module Milestones
private
+ # rubocop: disable CodeReuse/ActiveRecord
def milestone_ids_for_merge(group_milestone)
# Pluck need to be used here instead of select so the array of ids
# is persistent after old milestones gets deleted.
@@ -35,6 +36,7 @@ module Milestones
milestones.pluck(:id)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def move_children_to_group_milestone(group_milestone)
milestone_ids_for_merge(group_milestone).in_groups_of(100, false) do |milestone_ids|
@@ -59,6 +61,7 @@ module Milestones
milestone
end
+ # rubocop: disable CodeReuse/ActiveRecord
def update_children(group_milestone, milestone_ids)
issues = Issue.where(project_id: group_project_ids, milestone_id: milestone_ids)
merge_requests = MergeRequest.where(source_project_id: group_project_ids, milestone_id: milestone_ids)
@@ -67,18 +70,23 @@ module Milestones
issuable_collection.update_all(milestone_id: group_milestone.id)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def group
@group ||= parent.group || raise_error('Project does not belong to a group.')
end
+ # rubocop: disable CodeReuse/ActiveRecord
def destroy_old_milestones(milestone)
- Milestone.where(id: milestone_ids_for_merge(milestone)).destroy_all
+ Milestone.where(id: milestone_ids_for_merge(milestone)).destroy_all # rubocop: disable DestroyAll
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def group_project_ids
@group_project_ids ||= group.projects.pluck(:id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def raise_error(message)
raise PromoteMilestoneError, "Promotion failed - #{message}"
diff --git a/app/services/milestones/update_service.rb b/app/services/milestones/update_service.rb
index 81b20943bab..01ab8b37bac 100644
--- a/app/services/milestones/update_service.rb
+++ b/app/services/milestones/update_service.rb
@@ -2,6 +2,7 @@
module Milestones
class UpdateService < Milestones::BaseService
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(milestone)
state = params[:state_event]
@@ -18,5 +19,6 @@ module Milestones
milestone
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/notes/base_service.rb b/app/services/notes/base_service.rb
new file mode 100644
index 00000000000..c1260837c12
--- /dev/null
+++ b/app/services/notes/base_service.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Notes
+ class BaseService < ::BaseService
+ def clear_noteable_diffs_cache(note)
+ if note.is_a?(DiffNote) &&
+ note.discussion_first_note? &&
+ note.position.unfolded_diff?(project.repository)
+ note.noteable.diffs.clear_cache
+ end
+ end
+ end
+end
diff --git a/app/services/notes/build_service.rb b/app/services/notes/build_service.rb
index df5fe65de3c..7b92fe6fe14 100644
--- a/app/services/notes/build_service.rb
+++ b/app/services/notes/build_service.rb
@@ -3,6 +3,7 @@
module Notes
class BuildService < ::BaseService
def execute
+ should_resolve = false
in_reply_to_discussion_id = params.delete(:in_reply_to_discussion_id)
if in_reply_to_discussion_id.present?
@@ -15,12 +16,17 @@ module Notes
end
params.merge!(discussion.reply_attributes)
+ should_resolve = discussion.resolved?
end
note = Note.new(params)
note.project = project
note.author = current_user
+ if should_resolve
+ note.resolve_without_save(current_user)
+ end
+
note
end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 049e6c5a871..e03789e3ca9 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Notes
- class CreateService < ::BaseService
+ class CreateService < ::Notes::BaseService
def execute
merge_request_diff_head_sha = params.delete(:merge_request_diff_head_sha)
@@ -35,6 +35,7 @@ module Notes
if !only_commands && note.save
todo_service.new_note(note, current_user)
+ clear_noteable_diffs_cache(note)
end
if command_params.present?
diff --git a/app/services/notes/destroy_service.rb b/app/services/notes/destroy_service.rb
index 64e9accd97f..fa0c2c5c86b 100644
--- a/app/services/notes/destroy_service.rb
+++ b/app/services/notes/destroy_service.rb
@@ -1,11 +1,13 @@
# frozen_string_literal: true
module Notes
- class DestroyService < BaseService
+ class DestroyService < ::Notes::BaseService
def execute(note)
TodoService.new.destroy_target(note) do |note|
note.destroy
end
+
+ clear_noteable_diffs_cache(note)
end
end
end
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 4389fd89538..9c236d7f41d 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -58,6 +58,7 @@ module NotificationRecipientService
@recipients ||= []
end
+ # rubocop: disable CodeReuse/ActiveRecord
def add_recipients(users, type, reason)
if users.is_a?(ActiveRecord::Relation)
users = users.includes(:notification_settings)
@@ -66,10 +67,13 @@ module NotificationRecipientService
users = Array(users).compact
recipients.concat(users.map { |u| make_recipient(u, type, reason) })
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def user_scope
User.includes(:notification_settings)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def make_recipient(user, type, reason)
NotificationRecipient.new(
@@ -112,6 +116,7 @@ module NotificationRecipientService
end
# Get project/group users with CUSTOM notification level
+ # rubocop: disable CodeReuse/ActiveRecord
def add_custom_notifications
user_ids = []
@@ -128,9 +133,10 @@ module NotificationRecipientService
add_recipients(user_scope.where(id: user_ids), :watch, nil)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def add_project_watchers
- add_recipients(project_watchers, :watch, nil)
+ add_recipients(project_watchers, :watch, nil) if project
end
def add_group_watchers
@@ -138,6 +144,7 @@ module NotificationRecipientService
end
# Get project users with WATCH notification level
+ # rubocop: disable CodeReuse/ActiveRecord
def project_watchers
project_members_ids = user_ids_notifiable_on(project)
@@ -151,7 +158,9 @@ module NotificationRecipientService
user_scope.where(id: user_ids_with_project_setting.concat(user_ids_with_group_setting).uniq)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def group_watchers
user_ids_with_group_global = user_ids_notifiable_on(group, :global)
user_ids = user_ids_with_global_level_watch(user_ids_with_group_global)
@@ -159,6 +168,7 @@ module NotificationRecipientService
user_scope.where(id: user_ids_with_group_setting)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def add_subscribed_users
return unless target.respond_to? :subscribers
@@ -166,6 +176,7 @@ module NotificationRecipientService
add_recipients(target.subscribers(project), :subscription, nil)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def user_ids_notifiable_on(resource, notification_level = nil)
return [] unless resource
@@ -177,6 +188,7 @@ module NotificationRecipientService
scope.pluck(:user_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Build a list of user_ids based on project notification settings
def select_project_members_ids(global_setting, user_ids_global_level_watch)
@@ -194,14 +206,19 @@ module NotificationRecipientService
uids + (global_setting & user_ids_global_level_watch) - project_members
end
+ # rubocop: disable CodeReuse/ActiveRecord
def user_ids_with_global_level_watch(ids)
settings_with_global_level_of(:watch, ids).pluck(:user_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def user_ids_with_global_level_custom(ids, action)
settings_with_global_level_of(:custom, ids).pluck(:user_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def settings_with_global_level_of(level, ids)
NotificationSetting.where(
user_id: ids,
@@ -209,6 +226,7 @@ module NotificationRecipientService
level: NotificationSetting.levels[level]
)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def add_labels_subscribers(labels: nil)
return unless target.respond_to? :labels
@@ -220,6 +238,8 @@ module NotificationRecipientService
end
class Default < Base
+ MENTION_TYPE_ACTIONS = [:new_issue, :new_merge_request].freeze
+
attr_reader :target
attr_reader :current_user
attr_reader :action
@@ -252,7 +272,7 @@ module NotificationRecipientService
add_subscribed_users
- if [:new_issue, :new_merge_request].include?(custom_action)
+ if self.class.mention_type_actions.include?(custom_action)
# These will all be participants as well, but adding with the :mention
# type ensures that users with the mention notification level will
# receive them, too.
@@ -279,10 +299,14 @@ module NotificationRecipientService
end
# Build event key to search on custom notification level
- # Check NotificationSetting::EMAIL_EVENTS
+ # Check NotificationSetting.email_events
def custom_action
@custom_action ||= "#{action}_#{target.class.model_name.name.underscore}".to_sym
end
+
+ def self.mention_type_actions
+ MENTION_TYPE_ACTIONS.dup
+ end
end
class NewNote < Base
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 4511c500fca..fb9c18ea75d 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -129,6 +129,14 @@ class NotificationService
relabeled_resource_email(issue, added_labels, current_user, :relabeled_issue_email)
end
+ def removed_milestone_issue(issue, current_user)
+ removed_milestone_resource_email(issue, current_user, :removed_milestone_issue_email)
+ end
+
+ def changed_milestone_issue(issue, new_milestone, current_user)
+ changed_milestone_resource_email(issue, new_milestone, current_user, :changed_milestone_issue_email)
+ end
+
# When create a merge request we should send an email to:
#
# * mr author
@@ -138,7 +146,6 @@ class NotificationService
# * users with custom level checked with "new merge request"
#
# In EE, approvers of the merge request are also included
- #
def new_merge_request(merge_request, current_user)
new_resource_email(merge_request, :new_merge_request_email)
end
@@ -208,6 +215,14 @@ class NotificationService
relabeled_resource_email(merge_request, added_labels, current_user, :relabeled_merge_request_email)
end
+ def removed_milestone_merge_request(merge_request, current_user)
+ removed_milestone_resource_email(merge_request, current_user, :removed_milestone_merge_request_email)
+ end
+
+ def changed_milestone_merge_request(merge_request, new_milestone, current_user)
+ changed_milestone_resource_email(merge_request, new_milestone, current_user, :changed_milestone_merge_request_email)
+ end
+
def close_mr(merge_request, current_user)
close_resource_email(merge_request, current_user, :closed_merge_request_email)
end
@@ -407,6 +422,12 @@ class NotificationService
end
end
+ def autodevops_disabled(pipeline, recipients)
+ recipients.each do |recipient|
+ mailer.autodevops_disabled_email(pipeline, recipient).deliver_later
+ end
+ end
+
def pages_domain_verification_succeeded(domain)
recipients_for_pages_domain(domain).each do |user|
mailer.pages_domain_verification_succeeded_email(domain, user).deliver_later
@@ -494,6 +515,30 @@ class NotificationService
end
end
+ def removed_milestone_resource_email(target, current_user, method)
+ recipients = NotificationRecipientService.build_recipients(
+ target,
+ current_user,
+ action: 'removed_milestone'
+ )
+
+ recipients.each do |recipient|
+ mailer.send(method, recipient.user.id, target.id, current_user.id).deliver_later
+ end
+ end
+
+ def changed_milestone_resource_email(target, milestone, current_user, method)
+ recipients = NotificationRecipientService.build_recipients(
+ target,
+ current_user,
+ action: 'changed_milestone'
+ )
+
+ recipients.each do |recipient|
+ mailer.send(method, recipient.user.id, target.id, milestone, current_user.id).deliver_later
+ end
+ end
+
def reopen_resource_email(target, current_user, method, status)
recipients = NotificationRecipientService.build_recipients(target, current_user, action: "reopen")
diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb
index 11b996ed4b6..de8757006f1 100644
--- a/app/services/preview_markdown_service.rb
+++ b/app/services/preview_markdown_service.rb
@@ -43,6 +43,10 @@ class PreviewMarkdownService < BaseService
end
def markdown_engine
- CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i)
+ if params[:legacy_render]
+ :redcarpet
+ else
+ CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i)
+ end
end
end
diff --git a/app/services/projects/after_rename_service.rb b/app/services/projects/after_rename_service.rb
new file mode 100644
index 00000000000..4131da44f5a
--- /dev/null
+++ b/app/services/projects/after_rename_service.rb
@@ -0,0 +1,135 @@
+# frozen_string_literal: true
+
+module Projects
+ # Service class for performing operations that should take place after a
+ # project has been renamed.
+ #
+ # Example usage:
+ #
+ # project = Project.find(42)
+ #
+ # project.update(...)
+ #
+ # Projects::AfterRenameService.new(project).execute
+ class AfterRenameService
+ attr_reader :project, :full_path_before, :full_path_after, :path_before
+
+ RenameFailedError = Class.new(StandardError)
+
+ # @param [Project] project The Project of the repository to rename.
+ def initialize(project)
+ @project = project
+
+ # The full path of the namespace + project, before the rename took place.
+ @full_path_before = project.full_path_was
+
+ # The full path of the namespace + project, after the rename took place.
+ @full_path_after = project.build_full_path
+
+ # The path of just the project, before the rename took place.
+ @path_before = project.path_was
+ end
+
+ def execute
+ first_ensure_no_registry_tags_are_present
+ expire_caches_before_rename
+ rename_or_migrate_repository!
+ send_move_instructions
+ execute_system_hooks
+ update_repository_configuration
+ rename_transferred_documents
+ log_completion
+ end
+
+ def first_ensure_no_registry_tags_are_present
+ return unless project.has_container_registry_tags?
+
+ raise RenameFailedError.new(
+ "Project #{full_path_before} cannot be renamed because images are " \
+ "present in its container registry"
+ )
+ end
+
+ def expire_caches_before_rename
+ project.expire_caches_before_rename(full_path_before)
+ end
+
+ def rename_or_migrate_repository!
+ success =
+ if migrate_to_hashed_storage?
+ ::Projects::HashedStorageMigrationService
+ .new(project, full_path_before)
+ .execute
+ else
+ project.storage.rename_repo
+ end
+
+ rename_failed! unless success
+ end
+
+ def send_move_instructions
+ return unless send_move_instructions?
+
+ project.send_move_instructions(full_path_before)
+ end
+
+ def execute_system_hooks
+ project.old_path_with_namespace = full_path_before
+ SystemHooksService.new.execute_hooks_for(project, :rename)
+ end
+
+ def update_repository_configuration
+ project.reload_repository!
+ project.write_repository_config
+ end
+
+ def rename_transferred_documents
+ if rename_uploads?
+ Gitlab::UploadsTransfer
+ .new
+ .rename_project(path_before, project_path, namespace_full_path)
+ end
+
+ Gitlab::PagesTransfer
+ .new
+ .rename_project(path_before, project_path, namespace_full_path)
+ end
+
+ def log_completion
+ Gitlab::AppLogger.info(
+ "Project #{project.id} has been renamed from " \
+ "#{full_path_before} to #{full_path_after}"
+ )
+ end
+
+ def migrate_to_hashed_storage?
+ Gitlab::CurrentSettings.hashed_storage_enabled? &&
+ project.storage_upgradable? &&
+ Feature.disabled?(:skip_hashed_storage_upgrade)
+ end
+
+ def send_move_instructions?
+ !project.import_started?
+ end
+
+ def rename_uploads?
+ !project.hashed_storage?(:attachments)
+ end
+
+ def project_path
+ project.path
+ end
+
+ def namespace_full_path
+ project.namespace.full_path
+ end
+
+ def rename_failed!
+ error = "Repository #{full_path_before} could not be renamed to #{full_path_after}"
+
+ Gitlab::AppLogger.error(error)
+
+ raise RenameFailedError.new(error)
+ end
+ end
+end
diff --git a/app/services/projects/auto_devops/disable_service.rb b/app/services/projects/auto_devops/disable_service.rb
new file mode 100644
index 00000000000..1b578a3c5ce
--- /dev/null
+++ b/app/services/projects/auto_devops/disable_service.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Projects
+ module AutoDevops
+ class DisableService < BaseService
+ def execute
+ return false unless implicitly_enabled_and_first_pipeline_failure?
+
+ disable_auto_devops
+ end
+
+ private
+
+ def implicitly_enabled_and_first_pipeline_failure?
+ project.has_auto_devops_implicitly_enabled? &&
+ first_pipeline_failure?
+ end
+
+ # We're using `limit` to optimize `auto_devops pipeline` query,
+ # since we only care about the first element, and using only `.count`
+ # is an expensive operation. See
+ # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21172#note_99037378
+ # for more context.
+ # rubocop: disable CodeReuse/ActiveRecord
+ def first_pipeline_failure?
+ auto_devops_pipelines.success.limit(1).count.zero? &&
+ auto_devops_pipelines.failed.limit(1).count.nonzero?
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def disable_auto_devops
+ project.auto_devops_attributes = { enabled: false }
+ project.save!
+ end
+
+ def auto_devops_pipelines
+ @auto_devops_pipelines ||= project.pipelines.auto_devops_source
+ end
+ end
+ end
+end
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 5286b92ab6b..61f6402a810 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -2,6 +2,7 @@
module Projects
class AutocompleteService < BaseService
+ include LabelsAsHash
def issues
IssuesFinder.new(current_user, project_id: project.id, state: 'opened').execute.select([:iid, :title])
end
@@ -22,34 +23,18 @@ module Projects
MergeRequestsFinder.new(current_user, project_id: project.id, state: 'opened').execute.select([:iid, :title])
end
- def labels_as_hash(target = nil)
- available_labels = LabelsFinder.new(
- current_user,
- project_id: project.id,
- include_ancestor_groups: true
- ).execute
-
- label_hashes = available_labels.as_json(only: [:title, :color])
-
- if target&.respond_to?(:labels)
- already_set_labels = available_labels & target.labels
- if already_set_labels.present?
- titles = already_set_labels.map(&:title)
- label_hashes.each do |hash|
- if titles.include?(hash['title'])
- hash[:set] = true
- end
- end
- end
- end
-
- label_hashes
- end
-
def commands(noteable, type)
return [] unless noteable
QuickActions::InterpretService.new(project, current_user).available_commands(noteable)
end
+
+ def snippets
+ SnippetsFinder.new(current_user, project: project).execute.select([:id, :title])
+ end
+
+ def labels_as_hash(target)
+ super(target, project_id: project.id, include_ancestor_groups: true)
+ end
end
end
diff --git a/app/services/projects/base_move_relations_service.rb b/app/services/projects/base_move_relations_service.rb
index 78cc2869b72..24dec1f3a45 100644
--- a/app/services/projects/base_move_relations_service.rb
+++ b/app/services/projects/base_move_relations_service.rb
@@ -13,6 +13,7 @@ module Projects
private
+ # rubocop: disable CodeReuse/ActiveRecord
def prepare_relation(relation, id_param = :id)
if Gitlab::Database.postgresql?
relation
@@ -20,5 +21,6 @@ module Projects
relation.model.where("#{id_param}": relation.pluck(id_param))
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/batch_forks_count_service.rb b/app/services/projects/batch_forks_count_service.rb
index 9bf369df999..6467744a435 100644
--- a/app/services/projects/batch_forks_count_service.rb
+++ b/app/services/projects/batch_forks_count_service.rb
@@ -5,6 +5,7 @@
# because the service use maps to retrieve the project ids
module Projects
class BatchForksCountService < Projects::BatchCountService
+ # rubocop: disable CodeReuse/ActiveRecord
def global_count
@global_count ||= begin
count_service.query(project_ids)
@@ -12,6 +13,7 @@ module Projects
.count
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def count_service
::Projects::ForksCountService
diff --git a/app/services/projects/batch_open_issues_count_service.rb b/app/services/projects/batch_open_issues_count_service.rb
index d375fcf9dbd..d6ff2291af8 100644
--- a/app/services/projects/batch_open_issues_count_service.rb
+++ b/app/services/projects/batch_open_issues_count_service.rb
@@ -5,11 +5,13 @@
# because the service use maps to retrieve the project ids
module Projects
class BatchOpenIssuesCountService < Projects::BatchCountService
+ # rubocop: disable CodeReuse/ActiveRecord
def global_count
@global_count ||= begin
count_service.query(project_ids).group(:project_id).count
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def count_service
::Projects::OpenIssuesCountService
diff --git a/app/services/projects/container_repository/destroy_service.rb b/app/services/projects/container_repository/destroy_service.rb
new file mode 100644
index 00000000000..1f5af7970d6
--- /dev/null
+++ b/app/services/projects/container_repository/destroy_service.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Projects
+ module ContainerRepository
+ class DestroyService < BaseService
+ def execute(container_repository)
+ return false unless can?(current_user, :update_container_image, project)
+
+ # Delete tags outside of the transaction to avoid hitting an idle-in-transaction timeout
+ container_repository.delete_tags!
+ container_repository.destroy
+ end
+ end
+ end
+end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 02a3a3eb096..20bfe5af7a1 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -13,8 +13,8 @@ module Projects
return ::Projects::CreateFromTemplateService.new(current_user, params).execute
end
- forked_from_project_id = params.delete(:forked_from_project_id)
import_data = params.delete(:import_data)
+ relations_block = params.delete(:relations_block)
@project = Project.new(params)
@@ -24,11 +24,6 @@ module Projects
return @project
end
- unless allowed_fork?(forked_from_project_id)
- @project.errors.add(:forked_from_project_id, 'is forbidden')
- return @project
- end
-
set_project_name_from_path
# get namespace id
@@ -47,6 +42,7 @@ module Projects
@project.namespace_id = current_user.namespace_id
end
+ relations_block&.call(@project)
yield(@project) if block_given?
# If the block added errors, don't try to save the project
@@ -54,10 +50,6 @@ module Projects
@project.creator = current_user
- if forked_from_project_id
- @project.build_forked_project_link(forked_from_project_id: forked_from_project_id)
- end
-
save_project_and_import_data(import_data)
after_create_actions if @project.persisted?
@@ -79,17 +71,12 @@ module Projects
@project.errors.add(:namespace, "is not valid")
end
- def allowed_fork?(source_project_id)
- return true if source_project_id.nil?
-
- source_project = Project.find_by(id: source_project_id)
- current_user.can?(:fork_project, source_project)
- end
-
+ # rubocop: disable CodeReuse/ActiveRecord
def allowed_namespace?(user, namespace_id)
namespace = Namespace.find_by(id: namespace_id)
current_user.can?(:create_projects, namespace)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def after_create_actions
log_info("#{@project.owner.name} created a new project \"#{@project.full_name}\"")
@@ -167,12 +154,14 @@ module Projects
@project
end
+ # rubocop: disable CodeReuse/ActiveRecord
def create_services_from_active_templates(project)
Service.where(template: true, active: true).each do |template|
service = Service.build_from_template(project.id, template)
service.save!
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def set_project_name_from_path
# Set project name from path
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 46a8a5e4d98..210571b6b4e 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -83,9 +83,6 @@ module Projects
end
def remove_repository(path)
- # Skip repository removal. We use this flag when remove user or group
- return true if params[:skip_repo] == true
-
# There is a possibility project does not have repository or wiki
return true unless repo_exists?(path)
@@ -110,15 +107,19 @@ module Projects
mv_repository(old_path, new_path)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def repo_exists?(path)
gitlab_shell.exists?(project.repository_storage, path + '.git')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def mv_repository(from_path, to_path)
return true unless gitlab_shell.exists?(project.repository_storage, from_path + '.git')
gitlab_shell.mv_repository(project.repository_storage, from_path, to_path)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def attempt_rollback(project, message)
return unless project
@@ -132,11 +133,11 @@ module Projects
end
def attempt_destroy_transaction(project)
- Project.transaction do
- unless remove_legacy_registry_tags
- raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
- end
+ unless remove_registry_tags
+ raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
+ end
+ Project.transaction do
log_destroy_event
trash_repositories!
@@ -155,6 +156,17 @@ module Projects
log_info("Attempting to destroy #{project.full_path} (#{project.id})")
end
+ def remove_registry_tags
+ return false unless remove_legacy_registry_tags
+
+ project.container_repositories.find_each do |container_repository|
+ service = Projects::ContainerRepository::DestroyService.new(project, current_user)
+ service.execute(container_repository)
+ end
+
+ true
+ end
+
##
# This method makes sure that we correctly remove registry tags
# for legacy image repository (when repository path equals project path).
@@ -162,7 +174,7 @@ module Projects
def remove_legacy_registry_tags
return true unless Gitlab.config.registry.enabled
- ContainerRepository.build_root_repository(project).tap do |repository|
+ ::ContainerRepository.build_root_repository(project).tap do |repository|
break repository.has_tags? ? repository.delete_tags! : true
end
end
diff --git a/app/services/projects/detect_repository_languages_service.rb b/app/services/projects/detect_repository_languages_service.rb
index 3488b9ce47e..4a837a4fb6a 100644
--- a/app/services/projects/detect_repository_languages_service.rb
+++ b/app/services/projects/detect_repository_languages_service.rb
@@ -4,6 +4,7 @@ module Projects
class DetectRepositoryLanguagesService < BaseService
attr_reader :detected_repository_languages, :programming_languages
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
repository_languages = project.repository_languages
detection = Gitlab::LanguageDetection.new(repository, repository_languages)
@@ -28,9 +29,11 @@ module Projects
project.repository_languages.reload
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ # rubocop: disable CodeReuse/ActiveRecord
def ensure_programming_languages(detection)
existing_languages = ProgrammingLanguage.where(name: detection.languages)
return existing_languages if detection.languages.size == existing_languages.size
@@ -42,7 +45,9 @@ module Projects
existing_languages + created_languages
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def create_language(name, color)
ProgrammingLanguage.transaction do
ProgrammingLanguage.where(name: name).first_or_create(color: color)
@@ -50,5 +55,6 @@ module Projects
rescue ActiveRecord::RecordNotUnique
retry
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/disable_deploy_key_service.rb b/app/services/projects/disable_deploy_key_service.rb
new file mode 100644
index 00000000000..e483c0708c4
--- /dev/null
+++ b/app/services/projects/disable_deploy_key_service.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Projects
+ class DisableDeployKeyService < BaseService
+ def execute
+ # rubocop: disable CodeReuse/ActiveRecord
+ deploy_key_project = project.deploy_keys_projects.find_by(deploy_key_id: params[:id])
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ deploy_key_project&.destroy!
+ end
+ end
+end
diff --git a/app/services/projects/enable_deploy_key_service.rb b/app/services/projects/enable_deploy_key_service.rb
index b7c172028e9..38219cacee9 100644
--- a/app/services/projects/enable_deploy_key_service.rb
+++ b/app/services/projects/enable_deploy_key_service.rb
@@ -3,7 +3,9 @@
module Projects
class EnableDeployKeyService < BaseService
def execute
- key = accessible_keys.find_by(id: params[:key_id] || params[:id])
+ key_id = params[:key_id] || params[:id]
+ key = find_accessible_key(key_id)
+
return unless key
unless project.deploy_keys.include?(key)
@@ -15,8 +17,12 @@ module Projects
private
- def accessible_keys
- current_user.accessible_deploy_keys
+ def find_accessible_key(key_id)
+ if current_user.admin?
+ DeployKey.find_by_id(key_id)
+ else
+ current_user.accessible_deploy_keys.find_by_id(key_id)
+ end
end
end
end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 33ad2120a75..8dc0e044875 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -12,23 +12,42 @@ module Projects
private
+ def allowed_fork?
+ current_user.can?(:fork_project, @project)
+ end
+
def link_existing_project(fork_to_project)
return if fork_to_project.forked?
- link_fork_network(fork_to_project)
+ build_fork_network_member(fork_to_project)
+
+ if link_fork_network(fork_to_project)
+ # A forked project stores its LFS objects in the `forked_from_project`.
+ # So the LFS objects become inaccessible, and therefore delete them from
+ # the database so they'll get cleaned up.
+ #
+ # TODO: refactor this to get the correct lfs objects when implementing
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/39769
+ fork_to_project.lfs_objects_projects.delete_all
- fork_to_project
+ fork_to_project
+ end
end
def fork_new_project
new_params = {
- forked_from_project_id: @project.id,
visibility_level: allowed_visibility_level,
description: @project.description,
name: @project.name,
path: @project.path,
shared_runners_enabled: @project.shared_runners_enabled,
- namespace_id: target_namespace.id
+ namespace_id: target_namespace.id,
+ fork_network: fork_network,
+ # We need to assign the fork network membership after the project has
+ # been instantiated to avoid ActiveRecord trying to create it when
+ # initializing the project, as that would cause a foreign key constraint
+ # exception.
+ relations_block: -> (project) { build_fork_network_member(project) }
}
if @project.avatar.present? && @project.avatar.image?
@@ -38,38 +57,35 @@ module Projects
new_project = CreateService.new(current_user, new_params).execute
return new_project unless new_project.persisted?
+ # Set the forked_from_project relation after saving to avoid having to
+ # reload the project to reset the association information and cause an
+ # extra query.
+ new_project.forked_from_project = @project
+
builds_access_level = @project.project_feature.builds_access_level
new_project.project_feature.update(builds_access_level: builds_access_level)
- link_fork_network(new_project)
-
new_project
end
def fork_network
- if @project.fork_network
- @project.fork_network
- elsif forked_from_project = @project.forked_from_project
- # TODO: remove this case when all background migrations have completed
- # this only happens when a project had a `forked_project_link` that was
- # not migrated to the `fork_network` relation
- forked_from_project.fork_network || forked_from_project.create_root_of_fork_network
+ @fork_network ||= @project.fork_network || @project.build_root_of_fork_network
+ end
+
+ def build_fork_network_member(fork_to_project)
+ if allowed_fork?
+ fork_to_project.build_fork_network_member(forked_from_project: @project,
+ fork_network: fork_network)
else
- @project.create_root_of_fork_network
+ fork_to_project.errors.add(:forked_from_project_id, 'is forbidden')
end
end
def link_fork_network(fork_to_project)
- fork_network.fork_network_members.create(project: fork_to_project,
- forked_from_project: @project)
-
- # TODO: remove this when ForkedProjectLink model is removed
- unless fork_to_project.forked_project_link
- fork_to_project.create_forked_project_link(forked_to_project: fork_to_project,
- forked_from_project: @project)
- end
+ return if fork_to_project.errors.any?
- refresh_forks_count
+ fork_to_project.fork_network_member.save &&
+ refresh_forks_count
end
def refresh_forks_count
diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb
index b570c6d4754..ca85e2dc281 100644
--- a/app/services/projects/forks_count_service.rb
+++ b/app/services/projects/forks_count_service.rb
@@ -7,11 +7,10 @@ module Projects
'forks_count'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def self.query(project_ids)
- # We can't directly change ForkedProjectLink to ForkNetworkMember here
- # Nowadays, when a call using v3 to projects/:id/fork is made,
- # the relationship to ForkNetworkMember is not updated
- ForkedProjectLink.where(forked_from_project: project_ids)
+ ForkNetworkMember.where(forked_from_project: project_ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb
index 044afa1d5e1..a315adf42f0 100644
--- a/app/services/projects/gitlab_projects_import_service.rb
+++ b/app/services/projects/gitlab_projects_import_service.rb
@@ -32,11 +32,13 @@ module Projects
Project.find_by_full_path("#{current_namespace.full_path}/#{params[:path]}").present?
end
+ # rubocop: disable CodeReuse/ActiveRecord
def current_namespace
strong_memoize(:current_namespace) do
Namespace.find_by(id: params[:namespace_id])
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def overwrite?
strong_memoize(:overwrite) do
diff --git a/app/services/projects/hashed_storage/migrate_repository_service.rb b/app/services/projects/hashed_storage/migrate_repository_service.rb
index 641d46e6591..4462d504071 100644
--- a/app/services/projects/hashed_storage/migrate_repository_service.rb
+++ b/app/services/projects/hashed_storage/migrate_repository_service.rb
@@ -47,10 +47,13 @@ module Projects
private
+ # rubocop: disable CodeReuse/ActiveRecord
def has_wiki?
gitlab_shell.exists?(project.repository_storage, "#{old_wiki_disk_path}.git")
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def move_repository(from_name, to_name)
from_exists = gitlab_shell.exists?(project.repository_storage, "#{from_name}.git")
to_exists = gitlab_shell.exists?(project.repository_storage, "#{to_name}.git")
@@ -67,6 +70,7 @@ module Projects
gitlab_shell.mv_repository(project.repository_storage, from_name, to_name)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def rollback_folder_move
move_repository(new_disk_path, old_disk_path)
diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb
index 7d4fa4e08df..1c4a8d05be6 100644
--- a/app/services/projects/lfs_pointers/lfs_download_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_service.rb
@@ -4,6 +4,7 @@
module Projects
module LfsPointers
class LfsDownloadService < BaseService
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(oid, url)
return unless project&.lfs_enabled? && oid.present? && url.present?
@@ -20,6 +21,7 @@ module Projects
rescue StandardError => e
Rails.logger.error("LFS file with oid #{oid} could't be downloaded from #{sanitized_uri.sanitized_url}: #{e.message}")
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/services/projects/lfs_pointers/lfs_import_service.rb b/app/services/projects/lfs_pointers/lfs_import_service.rb
index 97ce681a911..9215fa0a7bf 100644
--- a/app/services/projects/lfs_pointers/lfs_import_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_import_service.rb
@@ -41,6 +41,7 @@ module Projects
project.update(lfs_enabled: false)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def get_download_links
existent_lfs = LfsListService.new(project).execute
linked_oids = LfsLinkService.new(project).execute(existent_lfs.keys)
@@ -50,6 +51,7 @@ module Projects
LfsDownloadLinkListService.new(project, remote_uri: current_endpoint_uri).execute(not_linked_lfs)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def lfsconfig_endpoint_uri
strong_memoize(:lfsconfig_endpoint_uri) do
diff --git a/app/services/projects/lfs_pointers/lfs_link_service.rb b/app/services/projects/lfs_pointers/lfs_link_service.rb
index a2eba8e124e..8401f3d1d89 100644
--- a/app/services/projects/lfs_pointers/lfs_link_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_link_service.rb
@@ -16,6 +16,7 @@ module Projects
private
+ # rubocop: disable CodeReuse/ActiveRecord
def link_existing_lfs_objects(oids)
existent_lfs_objects = LfsObject.where(oid: oids)
@@ -26,6 +27,7 @@ module Projects
existent_lfs_objects.pluck(:oid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/services/projects/move_deploy_keys_projects_service.rb b/app/services/projects/move_deploy_keys_projects_service.rb
index 40a22837eaf..b6a3af8c7b8 100644
--- a/app/services/projects/move_deploy_keys_projects_service.rb
+++ b/app/services/projects/move_deploy_keys_projects_service.rb
@@ -20,14 +20,16 @@ module Projects
.update_all(project_id: @project.id)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def non_existent_deploy_keys_projects
source_project.deploy_keys_projects
.joins(:deploy_key)
.where.not(keys: { fingerprint: @project.deploy_keys.select(:fingerprint) })
end
+ # rubocop: enable CodeReuse/ActiveRecord
def remove_remaining_deploy_keys_projects
- source_project.deploy_keys_projects.destroy_all
+ source_project.deploy_keys_projects.destroy_all # rubocop: disable DestroyAll
end
end
end
diff --git a/app/services/projects/move_forks_service.rb b/app/services/projects/move_forks_service.rb
index 076a7a50aa9..33f0bab12c9 100644
--- a/app/services/projects/move_forks_service.rb
+++ b/app/services/projects/move_forks_service.rb
@@ -6,7 +6,6 @@ module Projects
return unless super && source_project.fork_network
Project.transaction(requires_new: true) do
- move_forked_project_links
move_fork_network_members
update_root_project
refresh_forks_count
@@ -17,25 +16,19 @@ module Projects
private
- def move_forked_project_links
- # Update ancestor
- ForkedProjectLink.where(forked_to_project: source_project)
- .update_all(forked_to_project_id: @project.id)
-
- # Update the descendants
- ForkedProjectLink.where(forked_from_project: source_project)
- .update_all(forked_from_project_id: @project.id)
- end
-
+ # rubocop: disable CodeReuse/ActiveRecord
def move_fork_network_members
ForkNetworkMember.where(project: source_project).update_all(project_id: @project.id)
ForkNetworkMember.where(forked_from_project: source_project).update_all(forked_from_project_id: @project.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def update_root_project
# Update root network project
ForkNetwork.where(root_project: source_project).update_all(root_project_id: @project.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def refresh_forks_count
Projects::ForksCountService.new(@project).refresh_cache
diff --git a/app/services/projects/move_lfs_objects_projects_service.rb b/app/services/projects/move_lfs_objects_projects_service.rb
index a5099519594..308a54ad06e 100644
--- a/app/services/projects/move_lfs_objects_projects_service.rb
+++ b/app/services/projects/move_lfs_objects_projects_service.rb
@@ -21,11 +21,13 @@ module Projects
end
def remove_remaining_lfs_objects_project
- source_project.lfs_objects_projects.destroy_all
+ source_project.lfs_objects_projects.destroy_all # rubocop: disable DestroyAll
end
+ # rubocop: disable CodeReuse/ActiveRecord
def non_existent_lfs_objects_projects
source_project.lfs_objects_projects.where.not(lfs_object: @project.lfs_objects)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/move_notification_settings_service.rb b/app/services/projects/move_notification_settings_service.rb
index 746605d56f1..e740c44bd26 100644
--- a/app/services/projects/move_notification_settings_service.rb
+++ b/app/services/projects/move_notification_settings_service.rb
@@ -22,7 +22,7 @@ module Projects
# Remove remaining notification settings from source_project
def remove_remaining_notification_settings
- source_project.notification_settings.destroy_all
+ source_project.notification_settings.destroy_all # rubocop: disable DestroyAll
end
# Get users of current notification_settings
@@ -31,10 +31,12 @@ module Projects
end
# Look for notification_settings in source_project that are not in the target project
+ # rubocop: disable CodeReuse/ActiveRecord
def non_existent_notifications
source_project.notification_settings
.select(:id)
.where.not(user_id: users_in_target_project)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/move_project_authorizations_service.rb b/app/services/projects/move_project_authorizations_service.rb
index 60f2af88e99..2985ba89014 100644
--- a/app/services/projects/move_project_authorizations_service.rb
+++ b/app/services/projects/move_project_authorizations_service.rb
@@ -3,7 +3,7 @@
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
-# the authorizations if neccessary
+# the authorizations if necessary
module Projects
class MoveProjectAuthorizationsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
@@ -33,10 +33,12 @@ module Projects
end
# Look for authorizations in source_project that are not in the target project
+ # rubocop: disable CodeReuse/ActiveRecord
def non_existent_authorization
source_project.project_authorizations
.select(:user_id)
.where.not(user: @project.authorized_users)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/move_project_group_links_service.rb b/app/services/projects/move_project_group_links_service.rb
index d9038030f7e..36afcd0c503 100644
--- a/app/services/projects/move_project_group_links_service.rb
+++ b/app/services/projects/move_project_group_links_service.rb
@@ -3,7 +3,7 @@
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
-# the authorizations if neccessary
+# the authorizations if necessary
module Projects
class MoveProjectGroupLinksService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
@@ -26,7 +26,7 @@ module Projects
# Remove remaining project group links from source_project
def remove_remaining_project_group_links
- source_project.reload.project_group_links.destroy_all
+ source_project.reload.project_group_links.destroy_all # rubocop: disable DestroyAll
end
def group_links_in_target_project
@@ -34,9 +34,11 @@ module Projects
end
# Look for groups in source_project that are not in the target project
+ # rubocop: disable CodeReuse/ActiveRecord
def non_existent_group_links
source_project.project_group_links
.where.not(group_id: group_links_in_target_project)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/move_project_members_service.rb b/app/services/projects/move_project_members_service.rb
index bb0c0d10242..faf389241d2 100644
--- a/app/services/projects/move_project_members_service.rb
+++ b/app/services/projects/move_project_members_service.rb
@@ -3,7 +3,7 @@
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
-# the authorizations if neccessary
+# the authorizations if necessary
module Projects
class MoveProjectMembersService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
@@ -25,7 +25,7 @@ module Projects
def remove_remaining_members
# Remove remaining members and authorizations from source_project
- source_project.project_members.destroy_all
+ source_project.project_members.destroy_all # rubocop: disable DestroyAll
end
def project_members_in_target_project
@@ -33,10 +33,12 @@ module Projects
end
# Look for members in source_project that are not in the target project
+ # rubocop: disable CodeReuse/ActiveRecord
def non_existent_members
source_project.members
.select(:id)
.where.not(user_id: @project.project_members.select(:user_id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb
index 5d6620c3c54..ee9884e9042 100644
--- a/app/services/projects/open_issues_count_service.rb
+++ b/app/services/projects/open_issues_count_service.rb
@@ -42,6 +42,7 @@ module Projects
cache_key(TOTAL_COUNT_KEY)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def refresh_cache(&block)
if block_given?
super(&block)
@@ -59,11 +60,13 @@ module Projects
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# We only show total issues count for reporters
# which are allowed to view confidential issues
# This will still show a discrepancy on issues number but should be less than before.
# Check https://gitlab.com/gitlab-org/gitlab-ce/issues/38418 description.
+ # rubocop: disable CodeReuse/ActiveRecord
def self.query(projects, public_only: true)
if public_only
Issue.opened.public_only.where(project: projects)
@@ -71,5 +74,6 @@ module Projects
Issue.opened.where(project: projects)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/projects/propagate_service_template.rb b/app/services/projects/propagate_service_template.rb
index fdfa91801ab..633a263af7b 100644
--- a/app/services/projects/propagate_service_template.rb
+++ b/app/services/projects/propagate_service_template.rb
@@ -70,6 +70,7 @@ module Projects
)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def service_hash
@service_hash ||=
begin
@@ -83,7 +84,9 @@ module Projects
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def run_callbacks(batch)
if active_external_issue_tracker?
Project.where(id: batch).update_all(has_external_issue_tracker: true)
@@ -93,6 +96,7 @@ module Projects
Project.where(id: batch).update_all(has_external_wiki: true)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def active_external_issue_tracker?
@template.issue_tracker? && !@template.default
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index c2a0c5fa7f3..9d40ab166ff 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -37,14 +37,15 @@ module Projects
private
+ # rubocop: disable CodeReuse/ActiveRecord
def transfer(project)
@old_path = project.full_path
@old_group = project.group
@new_path = File.join(@new_namespace.try(:full_path) || '', project.path)
@old_namespace = project.namespace
- if Project.where(path: project.path, namespace_id: @new_namespace.try(:id)).exists?
- raise TransferError.new("Project with same path in target namespace already exists")
+ if Project.where(namespace_id: @new_namespace.try(:id)).where('path = ? or name = ?', project.path, project.name).exists?
+ raise TransferError.new("Project with same name or path in target namespace already exists")
end
if project.has_container_registry_tags?
@@ -54,6 +55,7 @@ module Projects
attempt_transfer_transaction
end
+ # rubocop: enable CodeReuse/ActiveRecord
def attempt_transfer_transaction
Project.transaction do
@@ -118,6 +120,7 @@ module Projects
def rollback_side_effects
rollback_folder_move
+ project.reload
update_namespace_and_visibility(@old_namespace)
write_repository_config(@old_path)
end
diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb
index 2c0d91fe34f..1b8a920268f 100644
--- a/app/services/projects/unlink_fork_service.rb
+++ b/app/services/projects/unlink_fork_service.rb
@@ -2,6 +2,7 @@
module Projects
class UnlinkForkService < BaseService
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
return unless @project.forked?
@@ -24,8 +25,8 @@ module Projects
end
@project.fork_network_member.destroy
- @project.forked_project_link.destroy
end
+ # rubocop: enable CodeReuse/ActiveRecord
def refresh_forks_count(project)
Projects::ForksCountService.new(project).refresh_cache
diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb
index efbd4c7b323..abf40b3ad7a 100644
--- a/app/services/projects/update_pages_configuration_service.rb
+++ b/app/services/projects/update_pages_configuration_service.rb
@@ -21,7 +21,9 @@ module Projects
def pages_config
{
domains: pages_domains_config,
- https_only: project.pages_https_only?
+ https_only: project.pages_https_only?,
+ id: project.project_id,
+ access_control: !project.public_pages?
}
end
@@ -31,7 +33,9 @@ module Projects
domain: domain.domain,
certificate: domain.certificate,
key: domain.key,
- https_only: project.pages_https_only? && domain.https?
+ https_only: project.pages_https_only? && domain.https?,
+ id: project.project_id,
+ access_control: !project.public_pages?
}
end
end
diff --git a/app/services/projects/update_remote_mirror_service.rb b/app/services/projects/update_remote_mirror_service.rb
index 4651f7c4f8f..9d0877d1ab2 100644
--- a/app/services/projects/update_remote_mirror_service.rb
+++ b/app/services/projects/update_remote_mirror_service.rb
@@ -5,11 +5,12 @@ module Projects
attr_reader :errors
def execute(remote_mirror)
- @errors = []
-
return success unless remote_mirror.enabled?
+ errors = []
+
begin
+ remote_mirror.ensure_remote!
repository.fetch_remote(remote_mirror.remote_name, no_tags: true)
opts = {}
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 97f181ccea8..93e48fc0199 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -6,6 +6,7 @@ module Projects
ValidationError = Class.new(StandardError)
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
validate!
@@ -26,11 +27,12 @@ module Projects
rescue ValidationError => e
error(e.message)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def run_auto_devops_pipeline?
return false if project.repository.gitlab_ci_yml || !project.auto_devops&.previous_changes&.include?('enabled')
- project.auto_devops.enabled? || (project.auto_devops.enabled.nil? && Gitlab::CurrentSettings.auto_devops_enabled?)
+ project.auto_devops_enabled?
end
private
@@ -65,12 +67,16 @@ module Projects
end
if project.previous_changes.include?('path')
- project.rename_repo
+ AfterRenameService.new(project).execute
else
system_hook_service.execute_hooks_for(project, :update)
end
- update_pages_config if changing_pages_https_only?
+ update_pages_config if changing_pages_related_config?
+ end
+
+ def changing_pages_related_config?
+ changing_pages_https_only? || changing_pages_access_level?
end
def update_failed!
@@ -100,6 +106,10 @@ module Projects
params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
end
+ def changing_pages_access_level?
+ params.dig(:project_feature_attributes, :pages_access_level)
+ end
+
def ensure_wiki_exists
ProjectWiki.new(project, project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError
diff --git a/app/services/protected_branches/legacy_api_update_service.rb b/app/services/protected_branches/legacy_api_update_service.rb
index 1f6bbe72f85..da8bf2ce02a 100644
--- a/app/services/protected_branches/legacy_api_update_service.rb
+++ b/app/services/protected_branches/legacy_api_update_service.rb
@@ -38,11 +38,11 @@ module ProtectedBranches
def delete_redundant_access_levels
unless @developers_can_merge.nil?
- @protected_branch.merge_access_levels.destroy_all
+ @protected_branch.merge_access_levels.destroy_all # rubocop: disable DestroyAll
end
unless @developers_can_push.nil?
- @protected_branch.push_access_levels.destroy_all
+ @protected_branch.push_access_levels.destroy_all # rubocop: disable DestroyAll
end
end
end
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 8838ed06324..d248b10f41e 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -23,13 +23,13 @@ module QuickActions
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and hash of changes to be applied to a record.
- def execute(content, issuable)
+ def execute(content, issuable, only: nil)
return [content, {}] unless current_user.can?(:use_quick_actions)
@issuable = issuable
@updates = {}
- content, commands = extractor.extract_commands(content)
+ content, commands = extractor.extract_commands(content, only: only)
extract_updates(commands)
[content, @updates]
@@ -111,10 +111,12 @@ module QuickActions
end
desc 'Assign'
+ # rubocop: disable CodeReuse/ActiveRecord
explanation do |users|
users = issuable.allows_multiple_assignees? ? users : users.take(1)
"Assigns #{users.map(&:to_reference).to_sentence}."
end
+ # rubocop: enable CodeReuse/ActiveRecord
params do
issuable.allows_multiple_assignees? ? '@user1 @user2' : '@user'
end
@@ -127,12 +129,12 @@ module QuickActions
command :assign do |users|
next if users.empty?
- @updates[:assignee_ids] =
- if issuable.allows_multiple_assignees?
- issuable.assignees.pluck(:id) + users.map(&:id)
- else
- [users.first.id]
- end
+ if issuable.allows_multiple_assignees?
+ @updates[:assignee_ids] ||= issuable.assignees.map(&:id)
+ @updates[:assignee_ids] += users.map(&:id)
+ else
+ @updates[:assignee_ids] = [users.first.id]
+ end
end
desc do
@@ -161,12 +163,12 @@ module QuickActions
extract_users(unassign_param) if issuable.allows_multiple_assignees?
end
command :unassign do |users = nil|
- @updates[:assignee_ids] =
- if users&.any?
- issuable.assignees.pluck(:id) - users.map(&:id)
- else
- []
- end
+ if issuable.allows_multiple_assignees? && users&.any?
+ @updates[:assignee_ids] ||= issuable.assignees.map(&:id)
+ @updates[:assignee_ids] -= users.map(&:id)
+ else
+ @updates[:assignee_ids] = []
+ end
end
desc 'Set milestone'
@@ -208,9 +210,14 @@ module QuickActions
end
params '~label1 ~"label 2"'
condition do
- available_labels = LabelsFinder.new(current_user, project_id: project.id, include_ancestor_groups: true).execute
+ if project
+ available_labels = LabelsFinder
+ .new(current_user, project_id: project.id, include_ancestor_groups: true)
+ .execute
+ end
- current_user.can?(:"admin_#{issuable.to_ability_name}", project) &&
+ project &&
+ current_user.can?(:"admin_#{issuable.to_ability_name}", project) &&
available_labels.any?
end
command :label do |labels_param|
@@ -284,7 +291,7 @@ module QuickActions
end
params '#issue | !merge_request'
condition do
- issuable.persisted? &&
+ [MergeRequest, Issue].include?(issuable.class) &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
end
parse_params do |issuable_param|
@@ -402,7 +409,7 @@ module QuickActions
match[1] if match
end
command :award do |name|
- if name && issuable.user_can_award?(current_user, name)
+ if name && issuable.user_can_award?(current_user)
@updates[:emoji_award] = name
end
end
@@ -426,14 +433,14 @@ module QuickActions
end
end
- desc 'Add or substract spent time'
+ desc 'Add or subtract spent time'
explanation do |time_spent, time_spent_date|
if time_spent
if time_spent > 0
verb = 'Adds'
value = time_spent
else
- verb = 'Substracts'
+ verb = 'Subtracts'
value = -time_spent
end
@@ -442,7 +449,8 @@ module QuickActions
end
params '<time(1h30m | -1h30m)> <date(YYYY-MM-DD)>'
condition do
- current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
+ issuable.is_a?(TimeTrackable) &&
+ current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
end
parse_params do |raw_time_date|
Gitlab::QuickActions::SpendTimeAndDateSeparator.new(raw_time_date).execute
@@ -489,6 +497,30 @@ module QuickActions
"#{comment} #{TABLEFLIP}"
end
+ desc "Lock the discussion"
+ explanation "Locks the discussion"
+ condition do
+ [MergeRequest, Issue].include?(issuable.class) &&
+ issuable.persisted? &&
+ !issuable.discussion_locked? &&
+ current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
+ end
+ command :lock do
+ @updates[:discussion_locked] = true
+ end
+
+ desc "Unlock the discussion"
+ explanation "Unlocks the discussion"
+ condition do
+ [MergeRequest, Issue].include?(issuable.class) &&
+ issuable.persisted? &&
+ issuable.discussion_locked? &&
+ current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
+ end
+ command :unlock do
+ @updates[:discussion_locked] = false
+ end
+
# This is a dummy command, so that it appears in the autocomplete commands
desc 'CC'
params '@user'
@@ -522,6 +554,7 @@ module QuickActions
current_user.can?(:"update_#{issuable.to_ability_name}", issuable) &&
issuable.project.boards.count == 1
end
+ # rubocop: disable CodeReuse/ActiveRecord
command :board_move do |target_list_name|
label_ids = find_label_ids(target_list_name)
@@ -536,6 +569,7 @@ module QuickActions
@updates[:add_label_ids] = [label_id]
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Mark this issue as a duplicate of another issue'
explanation do |duplicate_reference|
@@ -601,6 +635,23 @@ module QuickActions
@updates[:tag_message] = message
end
+ desc 'Create a merge request.'
+ explanation do |branch_name = nil|
+ branch_text = branch_name ? "branch '#{branch_name}'" : 'a branch'
+ "Creates #{branch_text} and a merge request to resolve this issue"
+ end
+ params "<branch name>"
+ condition do
+ issuable.is_a?(Issue) && current_user.can?(:create_merge_request_in, project) && current_user.can?(:push_code, project)
+ end
+ command :create_merge_request do |branch_name = nil|
+ @updates[:create_merge_request] = {
+ branch_name: branch_name,
+ issue_iid: issuable.iid
+ }
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
def extract_users(params)
return [] if params.nil?
@@ -608,7 +659,7 @@ module QuickActions
if users.empty?
users =
- if params == 'me'
+ if params.strip == 'me'
[current_user]
else
User.where(username: params.split(' ').map(&:strip))
@@ -617,6 +668,7 @@ module QuickActions
users
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_milestones(project, params = {})
MilestonesFinder.new(params.merge(project_ids: [project.id], group_ids: [project.group&.id])).execute
@@ -653,6 +705,7 @@ module QuickActions
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def extract_references(arg, type)
ext = Gitlab::ReferenceExtractor.new(project, current_user)
@@ -660,5 +713,6 @@ module QuickActions
ext.references(type)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/quick_actions/target_service.rb b/app/services/quick_actions/target_service.rb
index d8ba52c6e50..69464c3c1ae 100644
--- a/app/services/quick_actions/target_service.rb
+++ b/app/services/quick_actions/target_service.rb
@@ -15,13 +15,17 @@ module QuickActions
private
+ # rubocop: disable CodeReuse/ActiveRecord
def issue(type_id)
IssuesFinder.new(current_user, project_id: project.id).find_by(iid: type_id) || project.issues.build
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_request(type_id)
MergeRequestsFinder.new(current_user, project_id: project.id).find_by(iid: type_id) || project.merge_requests.build
end
+ # rubocop: enable CodeReuse/ActiveRecord
def commit(type_id)
project.commit(type_id)
diff --git a/app/services/resource_events/change_labels_service.rb b/app/services/resource_events/change_labels_service.rb
index 8edb0ddb3ed..039d6e2ebad 100644
--- a/app/services/resource_events/change_labels_service.rb
+++ b/app/services/resource_events/change_labels_service.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-# This service is not used yet, it will be used for:
-# https://gitlab.com/gitlab-org/gitlab-ce/issues/48483
module ResourceEvents
class ChangeLabelsService
attr_reader :resource, :user
@@ -25,6 +23,7 @@ module ResourceEvents
end
Gitlab::Database.bulk_insert(ResourceLabelEvent.table_name, labels)
+ resource.expire_note_etag_cache
end
private
diff --git a/app/services/resource_events/merge_into_notes_service.rb b/app/services/resource_events/merge_into_notes_service.rb
new file mode 100644
index 00000000000..7504773a002
--- /dev/null
+++ b/app/services/resource_events/merge_into_notes_service.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+# We store events about issuable label changes in a separate table (not as
+# other system notes), but we still want to display notes about label changes
+# as classic system notes in UI. This service generates "synthetic" notes for
+# label event changes and merges them with classic notes and sorts them by
+# creation time.
+
+module ResourceEvents
+ class MergeIntoNotesService
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :resource, :current_user, :params
+
+ def initialize(resource, current_user, params = {})
+ @resource = resource
+ @current_user = current_user
+ @params = params
+ end
+
+ def execute(notes = [])
+ (notes + label_notes).sort_by { |n| n.created_at }
+ end
+
+ private
+
+ def label_notes
+ label_events_by_discussion_id.map do |discussion_id, events|
+ LabelNote.from_events(events, resource: resource, resource_parent: resource_parent)
+ end
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def label_events_by_discussion_id
+ return [] unless resource.respond_to?(:resource_label_events)
+
+ events = resource.resource_label_events.includes(:label, user: :status)
+ events = since_fetch_at(events)
+
+ events.group_by { |event| event.discussion_id }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def since_fetch_at(events)
+ return events unless params[:last_fetched_at].present?
+
+ last_fetched_at = Time.at(params.fetch(:last_fetched_at).to_i)
+ events.created_after(last_fetched_at - NotesFinder::FETCH_OVERLAP)
+ end
+
+ def resource_parent
+ strong_memoize(:resource_parent) do
+ resource.project || resource.group
+ end
+ end
+ end
+end
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 1b707d79b43..e0cbfac2420 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -8,6 +8,7 @@ class SearchService
@params = params.dup
end
+ # rubocop: disable CodeReuse/ActiveRecord
def project
return @project if defined?(@project)
@@ -19,7 +20,9 @@ class SearchService
nil
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def group
return @group if defined?(@group)
@@ -31,6 +34,7 @@ class SearchService
nil
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def show_snippets?
return @show_snippets if defined?(@show_snippets)
diff --git a/app/services/spam_check_service.rb b/app/services/spam_check_service.rb
index 895261925ba..51d300d4f1d 100644
--- a/app/services/spam_check_service.rb
+++ b/app/services/spam_check_service.rb
@@ -22,6 +22,7 @@ module SpamCheckService
# a dirty instance, which means it should be already assigned with the new
# attribute values.
# rubocop:disable Gitlab/ModuleWithInstanceVariables
+ # rubocop: disable CodeReuse/ActiveRecord
def spam_check(spammable, user)
spam_service = SpamService.new(spammable, @request)
@@ -29,5 +30,6 @@ module SpamCheckService
user.spam_logs.find_by(id: @spam_log_id)&.update!(recaptcha_verified: true)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb
index 93c2e222963..62222d3fd2a 100644
--- a/app/services/submit_usage_ping_service.rb
+++ b/app/services/submit_usage_ping_service.rb
@@ -15,6 +15,7 @@ class SubmitUsagePingService
def execute
return false unless Gitlab::CurrentSettings.usage_ping_enabled?
+ return false if User.single_user&.requires_usage_stats_consent?
response = Gitlab::HTTP.post(
URL,
diff --git a/app/services/submodules/update_service.rb b/app/services/submodules/update_service.rb
new file mode 100644
index 00000000000..a6011a920bd
--- /dev/null
+++ b/app/services/submodules/update_service.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Submodules
+ class UpdateService < Commits::CreateService
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(*args)
+ super
+
+ @start_branch = @branch_name
+ @commit_sha = params[:commit_sha].presence
+ @submodule = params[:submodule].presence
+ @commit_message = params[:commit_message].presence || "Update submodule #{@submodule} with oid #{@commit_sha}"
+ end
+
+ def validate!
+ super
+
+ raise ValidationError, 'The repository is empty' if repository.empty?
+ end
+
+ def execute
+ super
+ rescue StandardError => e
+ error(e.message)
+ end
+
+ def create_commit!
+ repository.update_submodule(current_user,
+ @submodule,
+ @commit_sha,
+ message: @commit_message,
+ branch: @branch_name)
+ rescue ArgumentError, TypeError
+ raise ValidationError, 'Invalid parameters'
+ end
+ end
+end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index dda89830179..ec6c306227b 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -98,66 +98,45 @@ module SystemNoteService
create_note(NoteSummary.new(issue, project, author, body, action: 'assignee'))
end
- # Called when one or more labels on a Noteable are added and/or removed
+ # Called when the milestone of a Noteable is changed
#
- # noteable - Noteable object
- # project - Project owning noteable
- # author - User performing the change
- # added_labels - Array of Labels added
- # removed_labels - Array of Labels removed
+ # noteable - Noteable object
+ # project - Project owning noteable
+ # author - User performing the change
+ # milestone - Milestone being assigned, or nil
#
# Example Note text:
#
- # "added ~1 and removed ~2 ~3 labels"
- #
- # "added ~4 label"
+ # "removed milestone"
#
- # "removed ~5 label"
+ # "changed milestone to 7.11"
#
# Returns the created Note object
- def change_label(noteable, project, author, added_labels, removed_labels)
- labels_count = added_labels.count + removed_labels.count
-
- references = ->(label) { label.to_reference(format: :id) }
- added_labels = added_labels.map(&references).join(' ')
- removed_labels = removed_labels.map(&references).join(' ')
-
- text_parts = []
-
- if added_labels.present?
- text_parts << "added #{added_labels}"
- text_parts << 'and' if removed_labels.present?
- end
-
- if removed_labels.present?
- text_parts << "removed #{removed_labels}"
- end
-
- text_parts << 'label'.pluralize(labels_count)
- body = text_parts.join(' ')
+ def change_milestone(noteable, project, author, milestone)
+ format = milestone&.group_milestone? ? :name : :iid
+ body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}"
- create_note(NoteSummary.new(noteable, project, author, body, action: 'label'))
+ create_note(NoteSummary.new(noteable, project, author, body, action: 'milestone'))
end
- # Called when the milestone of a Noteable is changed
+ # Called when the due_date of a Noteable is changed
#
# noteable - Noteable object
# project - Project owning noteable
# author - User performing the change
- # milestone - Milestone being assigned, or nil
+ # due_date - Due date being assigned, or nil
#
# Example Note text:
#
- # "removed milestone"
+ # "removed due date"
#
- # "changed milestone to 7.11"
+ # "changed due date to September 20, 2018"
#
# Returns the created Note object
- def change_milestone(noteable, project, author, milestone)
- format = milestone&.group_milestone? ? :name : :iid
- body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}"
+ def change_due_date(noteable, project, author, due_date)
+ body = due_date ? "changed due date to #{due_date.to_s(:long)}" : 'removed due date'
- create_note(NoteSummary.new(noteable, project, author, body, action: 'milestone'))
+ create_note(NoteSummary.new(noteable, project, author, body, action: 'due_date'))
end
# Called when the estimated time of a Noteable is changed
@@ -232,7 +211,7 @@ module SystemNoteService
# "closed via bc17db76"
#
# Returns the created Note object
- def change_status(noteable, project, author, status, source)
+ def change_status(noteable, project, author, status, source = nil)
body = status.dup
body << " via #{source.gfm_reference(project)}" if source
@@ -428,11 +407,17 @@ module SystemNoteService
def new_issue_branch(issue, project, author, branch)
link = url_helpers.project_compare_url(project, from: project.default_branch, to: branch)
- body = "created branch [`#{branch}`](#{link})"
+ body = "created branch [`#{branch}`](#{link}) to address this issue"
create_note(NoteSummary.new(issue, project, author, body, action: 'branch'))
end
+ def new_merge_request(issue, project, author, merge_request)
+ body = "created merge request #{merge_request.to_reference} to address this issue"
+
+ create_note(NoteSummary.new(issue, project, author, body, action: 'merge'))
+ end
+
# Called when a Mentionable references a Noteable
#
# noteable - Noteable object being referenced
@@ -601,6 +586,7 @@ module SystemNoteService
private
+ # rubocop: disable CodeReuse/ActiveRecord
def notes_for_mentioner(mentioner, noteable, notes)
if mentioner.is_a?(Commit)
text = "#{cross_reference_note_prefix}%#{mentioner.to_reference(nil)}"
@@ -611,6 +597,7 @@ module SystemNoteService
notes.where(note: [text, text.capitalize])
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def create_note(note_summary)
note = Note.create(note_summary.note.merge(system: true))
diff --git a/app/services/tags/destroy_service.rb b/app/services/tags/destroy_service.rb
index 800268485a4..6bfef09ac54 100644
--- a/app/services/tags/destroy_service.rb
+++ b/app/services/tags/destroy_service.rb
@@ -2,6 +2,7 @@
module Tags
class DestroyService < BaseService
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(tag_name)
repository = project.repository
tag = repository.find_tag(tag_name)
@@ -26,6 +27,7 @@ module Tags
rescue Gitlab::Git::PreReceiveError => ex
error(ex.message)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def error(message, return_code = 400)
super(message).merge(return_code: return_code)
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 0df61ad3bce..f357dc37fe7 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -42,7 +42,7 @@ class TodoService
# updates the todo counts for those users.
#
def destroy_target(target)
- todo_users = User.where(id: target.todos.pending.select(:user_id)).to_a
+ todo_users = UsersWithPendingTodosFinder.new(target).execute.to_a
yield target
@@ -199,21 +199,20 @@ class TodoService
end
def todo_exist?(issuable, current_user)
- TodosFinder.new(current_user).execute.exists?(target: issuable)
+ TodosFinder.new(current_user).any_for_target?(issuable)
end
private
def todos_by_ids(ids, current_user)
- current_user.todos.where(id: Array(ids))
+ current_user.todos_limited_to(Array(ids))
end
def update_todos_state(todos, current_user, state)
- # Only update those that are not really on that state
- todos = todos.where.not(state: state)
- todos_ids = todos.pluck(:id)
- todos.unscope(:order).update_all(state: state)
+ todos_ids = todos.update_state(state)
+
current_user.update_todos_count_cache
+
todos_ids
end
@@ -341,7 +340,6 @@ class TodoService
end
def pending_todos(user, criteria = {})
- valid_keys = [:project_id, :target_id, :target_type, :commit_id]
- user.todos.pending.where(criteria.slice(*valid_keys))
+ PendingTodosFinder.new(user, criteria).execute
end
end
diff --git a/app/services/todos/destroy/base_service.rb b/app/services/todos/destroy/base_service.rb
index aeb60e50c64..f3f1dbb5698 100644
--- a/app/services/todos/destroy/base_service.rb
+++ b/app/services/todos/destroy/base_service.rb
@@ -11,13 +11,17 @@ module Todos
private
+ # rubocop: disable CodeReuse/ActiveRecord
def without_authorized(items)
items.where('user_id NOT IN (?)', authorized_users)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def authorized_users
ProjectAuthorization.select(:user_id).where(project_id: project_ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def todos
raise NotImplementedError
diff --git a/app/services/todos/destroy/confidential_issue_service.rb b/app/services/todos/destroy/confidential_issue_service.rb
index efec0f22da5..6276e332448 100644
--- a/app/services/todos/destroy/confidential_issue_service.rb
+++ b/app/services/todos/destroy/confidential_issue_service.rb
@@ -7,18 +7,22 @@ module Todos
attr_reader :issue
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(issue_id)
@issue = Issue.find_by(id: issue_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
override :todos
+ # rubocop: disable CodeReuse/ActiveRecord
def todos
Todo.where(target: issue)
.where('user_id != ?', issue.author_id)
.where('user_id NOT IN (?)', issue.assignees.select(:id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
override :todos_to_remove?
def todos_to_remove?
@@ -31,11 +35,13 @@ module Todos
end
override :authorized_users
+ # rubocop: disable CodeReuse/ActiveRecord
def authorized_users
ProjectAuthorization.select(:user_id)
.where(project_id: project_ids)
.where('access_level >= ?', Gitlab::Access::REPORTER)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/services/todos/destroy/entity_leave_service.rb b/app/services/todos/destroy/entity_leave_service.rb
index 4cb9d08713d..ebfb20132d0 100644
--- a/app/services/todos/destroy/entity_leave_service.rb
+++ b/app/services/todos/destroy/entity_leave_service.rb
@@ -7,6 +7,7 @@ module Todos
attr_reader :user, :entity
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(user_id, entity_id, entity_type)
unless %w(Group Project).include?(entity_type)
raise ArgumentError.new("#{entity_type} is not an entity user can leave")
@@ -15,6 +16,7 @@ module Todos
@user = User.find_by(id: user_id)
@entity = entity_type.constantize.find_by(id: entity_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def execute
return unless entity && user
@@ -40,21 +42,28 @@ module Todos
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def remove_confidential_issue_todos
Todo.where(
- target_id: confidential_issues.select(:id), target_type: Issue, user_id: user.id
+ target_id: confidential_issues.select(:id), target_type: Issue.name, user_id: user.id
).delete_all
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def remove_project_todos
Todo.where(project_id: non_authorized_projects, user_id: user.id).delete_all
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def remove_group_todos
Todo.where(group_id: non_authorized_groups, user_id: user.id).delete_all
end
+ # rubocop: enable CodeReuse/ActiveRecord
override :project_ids
+ # rubocop: disable CodeReuse/ActiveRecord
def project_ids
condition = case entity
when Project
@@ -65,22 +74,29 @@ module Todos
Project.where(condition).select(:id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def non_authorized_projects
project_ids.where('id NOT IN (?)', user.authorized_projects.select(:id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def non_authorized_groups
return [] unless entity.is_a?(Namespace)
entity.self_and_descendants.select(:id)
.where('id NOT IN (?)', GroupsFinder.new(user).execute.select(:id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def non_member_groups
entity.self_and_descendants.select(:id)
.where('id NOT IN (?)', user.membership_groups.select(:id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
def user_has_reporter_access?
return unless entity.is_a?(Namespace)
@@ -88,6 +104,7 @@ module Todos
entity.member?(User.find(user.id), Gitlab::Access::REPORTER)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def confidential_issues
assigned_ids = IssueAssignee.select(:issue_id).where(user_id: user.id)
authorized_reporter_projects = user
@@ -98,6 +115,7 @@ module Todos
.where('author_id != ?', user.id)
.where('id NOT IN (?)', assigned_ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/services/todos/destroy/group_private_service.rb b/app/services/todos/destroy/group_private_service.rb
index f67f1d40597..d7ecbb952aa 100644
--- a/app/services/todos/destroy/group_private_service.rb
+++ b/app/services/todos/destroy/group_private_service.rb
@@ -7,16 +7,20 @@ module Todos
attr_reader :group
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(group_id)
@group = Group.find_by(id: group_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
override :todos
+ # rubocop: disable CodeReuse/ActiveRecord
def todos
Todo.where(group_id: group.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
override :authorized_users
def authorized_users
diff --git a/app/services/todos/destroy/private_features_service.rb b/app/services/todos/destroy/private_features_service.rb
index 7e204885b31..bd49519d694 100644
--- a/app/services/todos/destroy/private_features_service.rb
+++ b/app/services/todos/destroy/private_features_service.rb
@@ -10,18 +10,20 @@ module Todos
@user_id = user_id
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute
ProjectFeature.where(project_id: project_ids).each do |project_features|
target_types = []
- target_types << Issue if private?(project_features.issues_access_level)
- target_types << MergeRequest if private?(project_features.merge_requests_access_level)
- target_types << Commit if private?(project_features.repository_access_level)
+ target_types << Issue.name if private?(project_features.issues_access_level)
+ target_types << MergeRequest.name if private?(project_features.merge_requests_access_level)
+ target_types << Commit.name if private?(project_features.repository_access_level)
next if target_types.empty?
remove_todos(project_features.project_id, target_types)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -29,6 +31,7 @@ module Todos
feature_level == ProjectFeature::PRIVATE
end
+ # rubocop: disable CodeReuse/ActiveRecord
def remove_todos(project_id, target_types)
items = Todo.where(project_id: project_id)
items = items.where(user_id: user_id) if user_id
@@ -37,6 +40,7 @@ module Todos
.where(target_type: target_types)
.delete_all
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/services/todos/destroy/project_private_service.rb b/app/services/todos/destroy/project_private_service.rb
index ae8fab3ffca..e00d10c3780 100644
--- a/app/services/todos/destroy/project_private_service.rb
+++ b/app/services/todos/destroy/project_private_service.rb
@@ -7,16 +7,20 @@ module Todos
attr_reader :project
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(project_id)
@project = Project.find_by(id: project_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
override :todos
+ # rubocop: disable CodeReuse/ActiveRecord
def todos
Todo.where(project_id: project.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
override :project_ids
def project_ids
diff --git a/app/services/update_deployment_service.rb b/app/services/update_deployment_service.rb
new file mode 100644
index 00000000000..aa7fcca1e2a
--- /dev/null
+++ b/app/services/update_deployment_service.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+class UpdateDeploymentService
+ attr_reader :deployment
+ attr_reader :deployable
+
+ delegate :environment, to: :deployment
+ delegate :variables, to: :deployable
+
+ def initialize(deployment)
+ @deployment = deployment
+ @deployable = deployment.deployable
+ end
+
+ def execute
+ deployment.create_ref
+ deployment.invalidate_cache
+
+ ActiveRecord::Base.transaction do
+ environment.external_url = expanded_environment_url if
+ expanded_environment_url
+
+ environment.fire_state_event(action)
+
+ break unless environment.save
+ break if environment.stopped?
+
+ deployment.tap(&:update_merge_request_metrics!)
+ end
+ end
+
+ private
+
+ def environment_options
+ @environment_options ||= deployable.options&.dig(:environment) || {}
+ end
+
+ def expanded_environment_url
+ return @expanded_environment_url if defined?(@expanded_environment_url)
+ return unless environment_url
+
+ @expanded_environment_url =
+ ExpandVariables.expand(environment_url, variables)
+ end
+
+ def environment_url
+ environment_options[:url]
+ end
+
+ def action
+ environment_options[:action] || 'start'
+ end
+end
diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb
index 422ba668e35..e2228ca026c 100644
--- a/app/services/update_release_service.rb
+++ b/app/services/update_release_service.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class UpdateReleaseService < BaseService
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(tag_name, release_description)
repository = project.repository
existing_tag = repository.find_tag(tag_name)
@@ -19,6 +20,7 @@ class UpdateReleaseService < BaseService
error('Tag does not exist', 404)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def success(release)
super().merge(release: release)
diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb
index acc2fa153ae..3f503f3da28 100644
--- a/app/services/users/build_service.rb
+++ b/app/services/users/build_service.rb
@@ -2,6 +2,10 @@
module Users
class BuildService < BaseService
+ delegate :user_default_internal_regex_enabled?,
+ :user_default_internal_regex_instance,
+ to: :'Gitlab::CurrentSettings.current_application_settings'
+
def initialize(current_user, params = {})
@current_user = current_user
@params = params.dup
@@ -24,7 +28,7 @@ module Users
identity_attrs = params.slice(:extern_uid, :provider)
- if identity_attrs.any?
+ unless identity_attrs.empty?
user.identities.build(identity_attrs)
end
@@ -51,7 +55,6 @@ module Users
:force_random_password,
:hide_no_password,
:hide_no_ssh_key,
- :key_id,
:linkedin,
:name,
:password,
@@ -65,7 +68,10 @@ module Users
:twitter,
:username,
:website_url,
- :private_profile
+ :private_profile,
+ :organization,
+ :location,
+ :public_email
]
end
@@ -99,11 +105,19 @@ module Users
end
end
+ if user_default_internal_regex_enabled? && !user_params.key?(:external)
+ user_params[:external] = user_external?
+ end
+
user_params
end
def skip_user_confirmation_email_from_setting
!Gitlab::CurrentSettings.send_user_confirmation_email
end
+
+ def user_external?
+ user_default_internal_regex_instance.match(params[:email]).nil?
+ end
end
end
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb
index 4bc78b5b64e..73fa6089945 100644
--- a/app/services/users/destroy_service.rb
+++ b/app/services/users/destroy_service.rb
@@ -2,6 +2,8 @@
module Users
class DestroyService
+ DestroyError = Class.new(StandardError)
+
attr_accessor :current_user
def initialize(current_user)
@@ -46,9 +48,8 @@ module Users
namespace.prepare_for_destroy
user.personal_projects.each do |project|
- # Skip repository removal because we remove directory with namespace
- # that contain all this repositories
- ::Projects::DestroyService.new(project, current_user, skip_repo: project.legacy_storage?).execute
+ success = ::Projects::DestroyService.new(project, current_user).execute
+ raise DestroyError, "Project #{project.id} can't be deleted" unless success
end
yield(user) if block_given?
diff --git a/app/services/users/last_push_event_service.rb b/app/services/users/last_push_event_service.rb
index a9c9497520b..b3980b8e32c 100644
--- a/app/services/users/last_push_event_service.rb
+++ b/app/services/users/last_push_event_service.rb
@@ -58,11 +58,13 @@ module Users
private
+ # rubocop: disable CodeReuse/ActiveRecord
def find_event_in_database(id)
PushEvent
.without_existing_merge_requests
.find_by(id: id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def user_cache_key
"last-push-event/#{@user.id}"
diff --git a/app/services/users/migrate_to_ghost_user_service.rb b/app/services/users/migrate_to_ghost_user_service.rb
index 4d47078bf43..04fd6e37501 100644
--- a/app/services/users/migrate_to_ghost_user_service.rb
+++ b/app/services/users/migrate_to_ghost_user_service.rb
@@ -54,15 +54,19 @@ module Users
migrate_award_emoji
end
+ # rubocop: disable CodeReuse/ActiveRecord
def migrate_issues
user.issues.update_all(author_id: ghost_user.id)
Issue.where(last_edited_by_id: user.id).update_all(last_edited_by_id: ghost_user.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def migrate_merge_requests
user.merge_requests.update_all(author_id: ghost_user.id)
MergeRequest.where(merge_user_id: user.id).update_all(merge_user_id: ghost_user.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def migrate_notes
user.notes.update_all(author_id: ghost_user.id)
diff --git a/app/services/users/respond_to_terms_service.rb b/app/services/users/respond_to_terms_service.rb
index 9efa3b285a8..254480304f9 100644
--- a/app/services/users/respond_to_terms_service.rb
+++ b/app/services/users/respond_to_terms_service.rb
@@ -6,6 +6,7 @@ module Users
@user, @term = user, term
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(accepted:)
agreement = @user.term_agreements.find_or_initialize_by(term: @term)
agreement.accepted = accepted
@@ -16,6 +17,7 @@ module Users
agreement
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 34724e0250d..1fee8bfcd31 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -22,7 +22,7 @@ class WebHookService
end
def execute
- start_time = Time.now
+ start_time = Gitlab::Metrics::System.monotonic_time
response = if parsed_url.userinfo.blank?
make_request(hook.url)
@@ -35,7 +35,7 @@ class WebHookService
url: hook.url,
request_data: data,
response: response,
- execution_duration: Time.now - start_time
+ execution_duration: Gitlab::Metrics::System.monotonic_time - start_time
)
{
@@ -43,13 +43,13 @@ class WebHookService
http_status: response.code,
message: response.to_s
}
- rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError => e
+ rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError, Gitlab::HTTP::RedirectionTooDeep => e
log_execution(
trigger: hook_name,
url: hook.url,
request_data: data,
response: InternalErrorResponse.new,
- execution_duration: Time.now - start_time,
+ execution_duration: Gitlab::Metrics::System.monotonic_time - start_time,
error_message: e.to_s
)
diff --git a/app/services/wikis/create_attachment_service.rb b/app/services/wikis/create_attachment_service.rb
new file mode 100644
index 00000000000..df31ad7c8ea
--- /dev/null
+++ b/app/services/wikis/create_attachment_service.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+module Wikis
+ class CreateAttachmentService < Files::CreateService
+ ATTACHMENT_PATH = 'uploads'.freeze
+ MAX_FILENAME_LENGTH = 255
+
+ delegate :wiki, to: :project
+ delegate :repository, to: :wiki
+
+ def initialize(*args)
+ super
+
+ @file_name = clean_file_name(params[:file_name])
+ @file_path = File.join(ATTACHMENT_PATH, SecureRandom.hex, @file_name) if @file_name
+ @commit_message ||= "Upload attachment #{@file_name}"
+ @branch_name ||= wiki.default_branch
+ end
+
+ def create_commit!
+ commit_result(create_transformed_commit(@file_content))
+ end
+
+ private
+
+ def clean_file_name(file_name)
+ return unless file_name.present?
+
+ file_name = truncate_file_name(file_name)
+ # CommonMark does not allow Urls with whitespaces, so we have to replace them
+ # Using the same regex Carrierwave use to replace invalid characters
+ file_name.gsub(CarrierWave::SanitizedFile.sanitize_regexp, '_')
+ end
+
+ def truncate_file_name(file_name)
+ return file_name if file_name.length <= MAX_FILENAME_LENGTH
+
+ extension = File.extname(file_name)
+ truncate_at = MAX_FILENAME_LENGTH - extension.length - 1
+ base_name = File.basename(file_name, extension)[0..truncate_at]
+ base_name + extension
+ end
+
+ def validate!
+ validate_file_name!
+ validate_permissions!
+ end
+
+ def validate_file_name!
+ raise_error('The file name cannot be empty') unless @file_name
+ end
+
+ def validate_permissions!
+ unless can?(current_user, :create_wiki, project)
+ raise_error('You are not allowed to push to the wiki')
+ end
+ end
+
+ def create_transformed_commit(content)
+ repository.create_file(
+ current_user,
+ @file_path,
+ content,
+ message: @commit_message,
+ branch_name: @branch_name,
+ author_email: @author_email,
+ author_name: @author_name)
+ end
+
+ def commit_result(commit_id)
+ {
+ file_name: @file_name,
+ file_path: @file_path,
+ branch: @branch_name,
+ commit: commit_id
+ }
+ end
+ end
+end
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index b29ef57b071..c0165759203 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -18,6 +18,10 @@ class AvatarUploader < GitlabUploader
false
end
+ def absolute_path
+ self.class.absolute_path(model.avatar.upload)
+ end
+
private
def dynamic_segment
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index b1365659834..e90599f2505 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -122,12 +122,6 @@ class FileUploader < GitlabUploader
}
end
- def markdown_link
- markdown = +"[#{markdown_name}](#{secure_url})"
- markdown.prepend("!") if image_or_video? || dangerous?
- markdown
- end
-
def to_h
{
alt: markdown_name,
@@ -155,9 +149,9 @@ class FileUploader < GitlabUploader
# return a new uploader with a file copy on another project
def self.copy_to(uploader, to_project)
- moved = uploader.dup.tap do |u|
- u.model = to_project
- end
+ moved = self.new(to_project)
+ moved.object_store = uploader.object_store
+ moved.filename = uploader.filename
moved.copy_file(uploader.file)
moved
@@ -192,10 +186,6 @@ class FileUploader < GitlabUploader
storage.delete_dir!(store_dir) # only remove when empty
end
- def markdown_name
- (image_or_video? ? File.basename(filename, File.extname(filename)) : filename).gsub("]", "\\]")
- end
-
def identifier
@identifier ||= filename
end
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
index 719bd6ef418..cefcd3d3f5a 100644
--- a/app/uploaders/gitlab_uploader.rb
+++ b/app/uploaders/gitlab_uploader.rb
@@ -63,6 +63,12 @@ class GitlabUploader < CarrierWave::Uploader::Base
super || file&.filename
end
+ def relative_path
+ return path if pathname.relative?
+
+ pathname.relative_path_from(Pathname.new(root))
+ end
+
def model_valid?
!!model
end
@@ -115,4 +121,8 @@ class GitlabUploader < CarrierWave::Uploader::Base
# the cache directory.
File.join(work_dir, cache_id, version_name.to_s, for_file)
end
+
+ def pathname
+ @pathname ||= Pathname.new(path)
+ end
end
diff --git a/app/uploaders/job_artifact_uploader.rb b/app/uploaders/job_artifact_uploader.rb
index f6af023e0f9..400f0b3dcc6 100644
--- a/app/uploaders/job_artifact_uploader.rb
+++ b/app/uploaders/job_artifact_uploader.rb
@@ -5,9 +5,12 @@ class JobArtifactUploader < GitlabUploader
include ObjectStorage::Concern
ObjectNotReadyError = Class.new(StandardError)
+ UnknownFileLocationError = Class.new(StandardError)
storage_options Gitlab.config.artifacts
+ alias_method :upload, :model
+
def cached_size
return model.size if model.size.present? && !model.file_changed?
@@ -23,10 +26,22 @@ class JobArtifactUploader < GitlabUploader
def dynamic_segment
raise ObjectNotReadyError, 'JobArtifact is not ready' unless model.id
- creation_date = model.created_at.utc.strftime('%Y_%m_%d')
+ if model.hashed_path?
+ hashed_path
+ elsif model.legacy_path?
+ legacy_path
+ else
+ raise UnknownFileLocationError
+ end
+ end
+ def hashed_path
File.join(disk_hash[0..1], disk_hash[2..3], disk_hash,
- creation_date, model.job_id.to_s, model.id.to_s)
+ model.created_at.utc.strftime('%Y_%m_%d'), model.job_id.to_s, model.id.to_s)
+ end
+
+ def legacy_path
+ File.join(model.created_at.utc.strftime('%Y_%m'), model.project_id.to_s, model.job_id.to_s)
end
def disk_hash
diff --git a/app/uploaders/legacy_artifact_uploader.rb b/app/uploaders/legacy_artifact_uploader.rb
index b4d0d752016..a9afc104ed1 100644
--- a/app/uploaders/legacy_artifact_uploader.rb
+++ b/app/uploaders/legacy_artifact_uploader.rb
@@ -8,6 +8,8 @@ class LegacyArtifactUploader < GitlabUploader
storage_options Gitlab.config.artifacts
+ alias_method :upload, :model
+
def store_dir
dynamic_segment
end
diff --git a/app/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb
index f3d32e6b39d..0a966f3d44f 100644
--- a/app/uploaders/lfs_object_uploader.rb
+++ b/app/uploaders/lfs_object_uploader.rb
@@ -6,6 +6,8 @@ class LfsObjectUploader < GitlabUploader
storage_options Gitlab.config.lfs
+ alias_method :upload, :model
+
def filename
model.oid[4..-1]
end
diff --git a/app/uploaders/namespace_file_uploader.rb b/app/uploaders/namespace_file_uploader.rb
index 52969762b7d..4965bd7f057 100644
--- a/app/uploaders/namespace_file_uploader.rb
+++ b/app/uploaders/namespace_file_uploader.rb
@@ -6,23 +6,27 @@ class NamespaceFileUploader < FileUploader
options.storage_path
end
- def self.base_dir(model, _store = nil)
- File.join(options.base_dir, 'namespace', model_path_segment(model))
+ def self.base_dir(model, store = nil)
+ base_dirs(model)[store || Store::LOCAL]
+ end
+
+ def self.base_dirs(model)
+ {
+ Store::LOCAL => File.join(options.base_dir, 'namespace', model_path_segment(model)),
+ Store::REMOTE => File.join('namespace', model_path_segment(model))
+ }
end
def self.model_path_segment(model)
File.join(model.id.to_s)
end
+ def self.workhorse_local_upload_path
+ File.join(options.storage_path, 'uploads', TMP_UPLOAD_PATH)
+ end
+
# Re-Override
def store_dir
store_dirs[object_store]
end
-
- def store_dirs
- {
- Store::LOCAL => File.join(base_dir, dynamic_segment),
- Store::REMOTE => File.join('namespace', self.class.model_path_segment(model), dynamic_segment)
- }
- end
end
diff --git a/app/uploaders/records_uploads.rb b/app/uploaders/records_uploads.rb
index 5795065ae11..0efca895a50 100644
--- a/app/uploaders/records_uploads.rb
+++ b/app/uploaders/records_uploads.rb
@@ -18,6 +18,7 @@ module RecordsUploads
# `Tempfile` object the callback gets.
#
# Called `after :store`
+ # rubocop: disable CodeReuse/ActiveRecord
def record_upload(_tempfile = nil)
return unless model
return unless file && file.exists?
@@ -29,6 +30,7 @@ module RecordsUploads
self.upload = build_upload.tap(&:save!)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def upload_path
File.join(store_dir, filename.to_s)
@@ -36,9 +38,11 @@ module RecordsUploads
private
+ # rubocop: disable CodeReuse/ActiveRecord
def uploads
Upload.order(id: :desc).where(uploader: self.class.to_s)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def build_upload
Upload.new(
@@ -53,11 +57,13 @@ module RecordsUploads
# Before removing an attachment, destroy any Upload records at the same path
#
# Called `before :remove`
+ # rubocop: disable CodeReuse/ActiveRecord
def destroy_upload(*args)
return unless file && file.exists?
self.upload = nil
uploads.where(path: upload_path).delete_all
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb
index 2a2b54a9270..e8a2dce7755 100644
--- a/app/uploaders/uploader_helper.rb
+++ b/app/uploaders/uploader_helper.rb
@@ -2,32 +2,7 @@
# Extra methods for uploader
module UploaderHelper
- IMAGE_EXT = %w[png jpg jpeg gif bmp tiff ico].freeze
- # We recommend using the .mp4 format over .mov. Videos in .mov format can
- # still be used but you really need to make sure they are served with the
- # proper MIME type video/mp4 and not video/quicktime or your videos won't play
- # on IE >= 9.
- # http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html
- VIDEO_EXT = %w[mp4 m4v mov webm ogv].freeze
- # These extension types can contain dangerous code and should only be embedded inline with
- # proper filtering. They should always be tagged as "Content-Disposition: attachment", not "inline".
- DANGEROUS_EXT = %w[svg].freeze
-
- def image?
- extension_match?(IMAGE_EXT)
- end
-
- def video?
- extension_match?(VIDEO_EXT)
- end
-
- def image_or_video?
- image? || video?
- end
-
- def dangerous?
- extension_match?(DANGEROUS_EXT)
- end
+ include Gitlab::FileMarkdownLinkBuilder
private
diff --git a/app/validators/branch_filter_validator.rb b/app/validators/branch_filter_validator.rb
new file mode 100644
index 00000000000..6a0899be850
--- /dev/null
+++ b/app/validators/branch_filter_validator.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+# BranchFilterValidator
+#
+# Custom validator for branch names. Squishes whitespace and ignores empty
+# string. This only checks that a string is a valid git branch name. It does
+# not check whether a branch already exists.
+#
+# Example:
+#
+# class Webhook < ActiveRecord::Base
+# validates :push_events_branch_filter, branch_name: true
+# end
+#
+class BranchFilterValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ value.squish! unless value.nil?
+
+ if value.present?
+ value_without_wildcards = value.tr('*', 'x')
+
+ unless Gitlab::GitRefValidator.validate(value_without_wildcards)
+ record.errors[attribute] << "is not a valid branch name"
+ end
+
+ unless value.length <= 4000
+ record.errors[attribute] << "is longer than the allowed length of 4000 characters."
+ end
+ end
+ end
+
+ private
+
+ def contains_wildcard?(value)
+ value.include?('*')
+ end
+end
diff --git a/app/validators/js_regex_validator.rb b/app/validators/js_regex_validator.rb
new file mode 100644
index 00000000000..be715967b4a
--- /dev/null
+++ b/app/validators/js_regex_validator.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class JsRegexValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ return true if value.blank?
+
+ parsed_regex = JsRegex.new(Regexp.new(value, Regexp::IGNORECASE))
+
+ if parsed_regex.source.empty?
+ record.errors.add(attribute, "Regex Pattern #{value} can not be expressed in Javascript")
+ else
+ parsed_regex.warnings.each { |warning| record.errors.add(attribute, warning) }
+ end
+ rescue RegexpError => regex_error
+ record.errors.add(attribute, regex_error.to_s)
+ end
+end
diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb
index faaf1283078..216acf79cbd 100644
--- a/app/validators/url_validator.rb
+++ b/app/validators/url_validator.rb
@@ -41,12 +41,13 @@ class UrlValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
@record = record
- if value.present?
- value.strip!
- else
+ unless value.present?
record.errors.add(attribute, 'must be a valid URL')
+ return
end
+ value = strip_value!(record, attribute, value)
+
Gitlab::UrlBlocker.validate!(value, blocker_args)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
record.errors.add(attribute, "is blocked: #{e.message}")
@@ -54,6 +55,13 @@ class UrlValidator < ActiveModel::EachValidator
private
+ def strip_value!(record, attribute, value)
+ new_value = value.strip
+ return value if new_value == value
+
+ record.public_send("#{attribute}=", new_value) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
def default_options
# By default the validator doesn't block any url based on the ip address
{
diff --git a/app/validators/variable_duplicates_validator.rb b/app/validators/variable_duplicates_validator.rb
index 90193e85f2a..d36a56e81b9 100644
--- a/app/validators/variable_duplicates_validator.rb
+++ b/app/validators/variable_duplicates_validator.rb
@@ -21,6 +21,7 @@ class VariableDuplicatesValidator < ActiveModel::EachValidator
private
+ # rubocop: disable CodeReuse/ActiveRecord
def validate_duplicates(record, attribute, values)
duplicates = values.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first)
if duplicates.any?
@@ -29,4 +30,5 @@ class VariableDuplicatesValidator < ActiveModel::EachValidator
record.errors.add(attribute, error_message)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml
index 278ad210543..84c3dfd8b91 100644
--- a/app/views/abuse_reports/new.html.haml
+++ b/app/views/abuse_reports/new.html.haml
@@ -1,6 +1,10 @@
-- page_title "Report abuse"
-%h3.page-title Report abuse
-%p Please use this form to report users who create spam issues, comments or behave inappropriately.
+- page_title _("Report abuse to GitLab")
+%h3.page-title
+ = _('Report abuse to GitLab')
+%p
+ = _('Please use this form to report users to GitLab who create spam issues, comments or behave inappropriately.')
+%p
+ = _("A member of GitLab's abuse team will review your report as soon as possible.")
%hr
= form_for @abuse_report, html: { class: 'js-quick-submit js-requires-input'} do |f|
= form_errors(@abuse_report)
@@ -16,7 +20,7 @@
.col-sm-10
= f.text_area :message, class: "form-control", rows: 2, required: true, value: sanitize(@ref_url)
.form-text.text-muted
- Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment.
+ = _('Explain the problem. If appropriate, provide a link to the relevant issue or comment.')
.form-actions
- = f.submit "Send report", class: "btn btn-create"
+ = f.submit "Send report", class: "btn btn-success"
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index a0861870ba4..cb67079853e 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -5,7 +5,7 @@
%legend
Navigation bar:
.form-group.row
- = f.label :header_logo, 'Header logo', class: 'col-sm-2 col-form-label'
+ = f.label :header_logo, 'Header logo', class: 'col-sm-2 col-form-label pt-0'
.col-sm-10
- if @appearance.header_logo?
= image_tag @appearance.header_logo_url, class: 'appearance-light-logo-preview'
@@ -22,7 +22,7 @@
%legend
Favicon:
.form-group.row
- = f.label :favicon, 'Favicon', class: 'col-sm-2 col-form-label'
+ = f.label :favicon, 'Favicon', class: 'col-sm-2 col-form-label pt-0'
.col-sm-10
- if @appearance.favicon?
= image_tag @appearance.favicon_url, class: 'appearance-light-logo-preview'
@@ -51,7 +51,7 @@
.hint
Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('user/markdown'), target: '_blank'}.
.form-group.row
- = f.label :logo, class: 'col-sm-2 col-form-label'
+ = f.label :logo, class: 'col-sm-2 col-form-label pt-0'
.col-sm-10
- if @appearance.logo?
= image_tag @appearance.logo_url, class: 'appearance-logo-preview'
@@ -75,7 +75,7 @@
Guidelines parsed with #{link_to "GitLab Flavored Markdown", help_page_path('user/markdown'), target: '_blank'}.
.form-actions
- = f.submit 'Save', class: 'btn btn-save append-right-10'
+ = f.submit 'Save', class: 'btn btn-success append-right-10'
- if @appearance.persisted?
Preview last save:
= link_to 'Sign-in page', preview_sign_in_admin_appearances_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
diff --git a/app/views/admin/appearances/preview_sign_in.html.haml b/app/views/admin/appearances/preview_sign_in.html.haml
index 1af7dd5bb67..2cd95071c73 100644
--- a/app/views/admin/appearances/preview_sign_in.html.haml
+++ b/app/views/admin/appearances/preview_sign_in.html.haml
@@ -8,5 +8,5 @@
= label_tag :password
= password_field_tag :password, nil, class: "form-control bottom", title: 'This field is required.'
.form-group
- = button_tag "Sign in", class: "btn-create btn"
+ = button_tag "Sign in", class: "btn-success btn"
diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml
index 7c8243a7a90..10bc3452d8b 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -14,7 +14,10 @@
= f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'label-bold'
= f.number_field :max_attachment_size, class: 'form-control'
.form-group
- = f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-bold'
+ = f.label :receive_max_input_size, 'Maximum push size (MB)', class: 'label-light'
+ = f.number_field :receive_max_input_size, class: 'form-control'
+ .form-group
+ = f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-light'
= f.number_field :session_expire_delay, class: 'form-control'
%span.form-text.text-muted#session_expire_delay_help_block GitLab restart is required to apply changes
.form-group
@@ -29,5 +32,18 @@
= f.check_box :user_default_external, class: 'form-check-input'
= f.label :user_default_external, class: 'form-check-label' do
Newly registered users will by default be external
+ .prepend-top-10
+ = _('Internal users')
+ = f.text_field :user_default_internal_regex, placeholder: _('Regex pattern'), class: 'form-control prepend-top-5'
+ .help-block
+ = _('Specify an e-mail address regex pattern to identify default internal users.')
+ = link_to _('More information'), help_page_path('user/permissions', anchor: 'external-users-permissions'),
+ target: '_blank'
+ .form-group
+ = f.label :user_show_add_ssh_key_message, 'Prompt users to upload SSH keys', class: 'label-bold'
+ .form-check
+ = f.check_box :user_show_add_ssh_key_message, class: 'form-check-input'
+ = f.label :user_show_add_ssh_key_message, class: 'form-check-label' do
+ Inform users without uploaded SSH keys that they can't push over SSH until one is added
= f.submit 'Save changes', class: 'btn btn-success'
diff --git a/app/views/admin/application_settings/_background_jobs.html.haml b/app/views/admin/application_settings/_background_jobs.html.haml
deleted file mode 100644
index 7d1a64b645a..00000000000
--- a/app/views/admin/application_settings/_background_jobs.html.haml
+++ /dev/null
@@ -1,27 +0,0 @@
-= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-background-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting)
-
- %fieldset
- %p
- These settings require a
- = link_to 'restart', help_page_path('administration/restart_gitlab')
- to take effect.
- .form-group
- .form-check
- = f.check_box :sidekiq_throttling_enabled, class: 'form-check-input'
- = f.label :sidekiq_throttling_enabled, class: 'form-check-label' do
- Enable Sidekiq Job Throttling
- .form-text.text-muted
- Limit the amount of resources slow running jobs are assigned.
- .form-group
- = f.label :sidekiq_throttling_queues, 'Sidekiq queues to throttle', class: 'label-bold'
- = f.select :sidekiq_throttling_queues, sidekiq_queue_options_for_select, { include_hidden: false }, multiple: true, class: 'select2 select-wide', data: { field: 'sidekiq_throttling_queues' }
- .form-text.text-muted
- Choose which queues you wish to throttle.
- .form-group
- = f.label :sidekiq_throttling_factor, 'Throttling Factor', class: 'label-bold'
- = f.number_field :sidekiq_throttling_factor, class: 'form-control', min: '0.01', max: '0.99', step: '0.01'
- .form-text.text-muted
- The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.
-
- = f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index 5037017e38a..0d42094fc89 100644
--- a/app/views/admin/application_settings/_ci_cd.html.haml
+++ b/app/views/admin/application_settings/_ci_cd.html.haml
@@ -38,6 +38,16 @@
.form-text.text-muted
Set the default expiration time for each job's artifacts.
0 for unlimited.
+ The default unit is in seconds, but you can define an alternative. For example:
+ <code>4 mins 2 sec</code>, <code>2h42min</code>.
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
+ .form-group
+ = f.label :archive_builds_in_human_readable, 'Archive jobs', class: 'label-bold'
+ = f.text_field :archive_builds_in_human_readable, class: 'form-control', placeholder: 'never'
+ .form-text.text-muted
+ Set the duration for which the jobs will be considered as old and expired.
+ Once that time passes, the jobs will be archived and no longer able to be
+ retried. Make it empty to never expire jobs. It has to be no less than 1 day,
+ for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_diff_limits.html.haml b/app/views/admin/application_settings/_diff_limits.html.haml
new file mode 100644
index 00000000000..408e569fe07
--- /dev/null
+++ b/app/views/admin/application_settings/_diff_limits.html.haml
@@ -0,0 +1,16 @@
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-merge-request-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
+
+ %fieldset
+ .form-group
+ = f.label :diff_max_patch_bytes, 'Maximum diff patch size (Bytes)', class: 'label-light'
+ = f.number_field :diff_max_patch_bytes, class: 'form-control'
+ %span.form-text.text-muted
+ Diff files surpassing this limit will be presented as 'too large'
+ and won't be expandable.
+
+ = link_to icon('question-circle'),
+ help_page_path('user/admin_area/diff_limits',
+ anchor: 'maximum-diff-patch-size')
+
+ = f.submit _('Save changes'), class: 'btn btn-success'
diff --git a/app/views/admin/application_settings/_email.html.haml b/app/views/admin/application_settings/_email.html.haml
index 86339e61215..60a6be731ea 100644
--- a/app/views/admin/application_settings/_email.html.haml
+++ b/app/views/admin/application_settings/_email.html.haml
@@ -20,5 +20,11 @@
By default GitLab sends emails in HTML and plain text formats so mail
clients can choose what format to use. Disable this option if you only
want to send emails in plain text format.
+ .form-group
+ = f.label :commit_email_hostname, _('Custom hostname (for private commit emails)'), class: 'label-bold'
+ = f.text_field :commit_email_hostname, class: 'form-control'
+ .form-text.text-muted
+ - commit_email_hostname_docs_link = link_to _('Learn more'), help_page_path('user/admin_area/settings/email', anchor: 'custom-private-commit-email-hostname'), target: '_blank'
+ = _("This setting will update the hostname that is used to generate private commit emails. %{learn_more}").html_safe % { learn_more: commit_email_hostname_docs_link }
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_influx.html.haml b/app/views/admin/application_settings/_influx.html.haml
index a1eeacd8290..dc5cbb8fa94 100644
--- a/app/views/admin/application_settings/_influx.html.haml
+++ b/app/views/admin/application_settings/_influx.html.haml
@@ -3,7 +3,7 @@
%fieldset
%p
- Setup InfluxDB to measure a wide variety of statistics like the time spent
+ Set up InfluxDB to measure a wide variety of statistics like the time spent
in running SQL queries. These settings require a
= link_to 'restart', help_page_path('administration/restart_gitlab')
to take effect.
diff --git a/app/views/admin/application_settings/_koding.html.haml b/app/views/admin/application_settings/_koding.html.haml
deleted file mode 100644
index 8b635b08abd..00000000000
--- a/app/views/admin/application_settings/_koding.html.haml
+++ /dev/null
@@ -1,22 +0,0 @@
-= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-koding-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting)
-
- %fieldset
- .form-group
- .form-check
- = f.check_box :koding_enabled, class: 'form-check-input'
- = f.label :koding_enabled, class: 'form-check-label' do
- Enable Koding
- .form-text.text-muted
- Koding integration has been deprecated since GitLab 10.0. If you disable your Koding integration, you will not be able to enable it again.
- .form-group
- = f.label :koding_url, 'Koding URL', class: 'label-bold'
- = f.text_field :koding_url, class: 'form-control', placeholder: 'http://gitlab.your-koding-instance.com:8090'
- .form-text.text-muted
- Koding has integration enabled out of the box for the
- %strong gitlab
- team, and you need to provide that team's URL here. Learn more in the
- = succeed "." do
- = link_to "Koding administration documentation", help_page_path("administration/integration/koding")
-
- = f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_repository_mirrors_form.html.haml b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
index c94f4c74820..615aa6317b0 100644
--- a/app/views/admin/application_settings/_repository_mirrors_form.html.haml
+++ b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
@@ -7,9 +7,9 @@
.form-check
= f.check_box :mirror_available, class: 'form-check-input'
= f.label :mirror_available, class: 'form-check-label' do
- Allow mirrors to be setup for projects
+ Allow mirrors to be set up for projects
%span.form-text.text-muted
- If disabled, only admins will be able to setup mirrors in projects.
+ If disabled, only admins will be able to set up mirrors in projects.
= link_to icon('question-circle'), help_page_path('workflow/repository_mirroring')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_repository_storage.html.haml b/app/views/admin/application_settings/_repository_storage.html.haml
index 4523332493b..c6c29ed1f21 100644
--- a/app/views/admin/application_settings/_repository_storage.html.haml
+++ b/app/views/admin/application_settings/_repository_storage.html.haml
@@ -5,7 +5,7 @@
.sub-section
.form-group
.form-check
- = f.check_box :hashed_storage_enabled, class: 'form-check-input'
+ = f.check_box :hashed_storage_enabled, class: 'form-check-input qa-hashed-storage-checkbox'
= f.label :hashed_storage_enabled, class: 'form-check-label' do
Use hashed storage paths for newly created and renamed projects
.form-text.text-muted
@@ -20,32 +20,5 @@
Manage repository storage paths. Learn more in the
= succeed "." do
= link_to "repository storages documentation", help_page_path("administration/repository_storage_paths")
- .sub-section
- %h4 Circuit breaker
- .form-group
- = f.label :circuitbreaker_check_interval, _('Check interval'), class: 'label-bold'
- = f.number_field :circuitbreaker_check_interval, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_check_interval_help_text
- .form-group
- = f.label :circuitbreaker_access_retries, _('Number of access attempts'), class: 'label-bold'
- = f.number_field :circuitbreaker_access_retries, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_access_retries_help_text
- .form-group
- = f.label :circuitbreaker_storage_timeout, _('Seconds to wait for a storage access attempt'), class: 'label-bold'
- = f.number_field :circuitbreaker_storage_timeout, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_storage_timeout_help_text
- .form-group
- = f.label :circuitbreaker_failure_count_threshold, _('Maximum git storage failures'), class: 'label-bold'
- = f.number_field :circuitbreaker_failure_count_threshold, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_failure_count_help_text
- .form-group
- = f.label :circuitbreaker_failure_reset_time, _('Seconds before reseting failure information'), class: 'label-bold'
- = f.number_field :circuitbreaker_failure_reset_time, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_failure_reset_time_help_text
- = f.submit 'Save changes', class: "btn btn-success"
+ = f.submit 'Save changes', class: "btn btn-success qa-save-changes-button"
diff --git a/app/views/admin/application_settings/_signin.html.haml b/app/views/admin/application_settings/_signin.html.haml
index 635a6751e5b..5f36358f599 100644
--- a/app/views/admin/application_settings/_signin.html.haml
+++ b/app/views/admin/application_settings/_signin.html.haml
@@ -31,7 +31,7 @@
.form-check
= f.check_box :require_two_factor_authentication, class: 'form-check-input'
= f.label :require_two_factor_authentication, class: 'form-check-label' do
- Require all users to setup Two-factor authentication
+ Require all users to set up Two-factor authentication
.form-group
= f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'label-bold'
= f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
diff --git a/app/views/admin/application_settings/_usage.html.haml b/app/views/admin/application_settings/_usage.html.haml
index 2495defb6a7..788595877ea 100644
--- a/app/views/admin/application_settings/_usage.html.haml
+++ b/app/views/admin/application_settings/_usage.html.haml
@@ -2,7 +2,7 @@
= form_errors(@application_setting)
%fieldset
- .form-group
+ .form-group.mb-2
.form-check
= f.check_box :version_check_enabled, class: 'form-check-input'
= f.label :version_check_enabled, class: 'form-check-label' do
@@ -16,23 +16,26 @@
.form-check
= f.check_box :usage_ping_enabled, disabled: !can_be_configured, class: 'form-check-input'
= f.label :usage_ping_enabled, class: 'form-check-label' do
- Enable usage ping
+ = _('Enable usage ping')
.form-text.text-muted
- if can_be_configured
- To help improve GitLab and its user experience, GitLab will
- periodically collect usage information.
- = link_to 'Learn more', help_page_path("user/admin_area/settings/usage_statistics", anchor: "usage-ping")
- about what information is shared with GitLab Inc. Visit
- = link_to _('Cohorts'), instance_statistics_cohorts_path(anchor: 'usage-ping')
- to see the JSON payload sent.
+ %p.mb-2= _('To help improve GitLab and its user experience, GitLab will periodically collect usage information.')
+
+ - usage_ping_path = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'usage-ping')
+ - usage_ping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: usage_ping_path }
+ %p.mb-2= s_('%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc.').html_safe % { usage_ping_link_start: usage_ping_link_start, usage_ping_link_end: '</a>'.html_safe }
+
+ %button.btn.js-usage-ping-payload-trigger{ type: 'button' }
+ .js-spinner.d-none= icon('spinner spin')
+ .js-text.d-inline= _('Preview payload')
+ %pre.usage-data.js-usage-ping-payload.js-syntax-highlight.code.highlight.mt-2.d-none{ data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
- else
- The usage ping is disabled, and cannot be configured through this
- form. For more information, see the documentation on
- = succeed '.' do
- = link_to 'deactivating the usage ping', help_page_path('user/admin_area/settings/usage_statistics', anchor: 'deactivate-the-usage-ping')
- .form-group
+ = _('The usage ping is disabled, and cannot be configured through this form.')
+ - deactivating_usage_ping_path = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'deactivate-the-usage-ping')
+ - deactivating_usage_ping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: deactivating_usage_ping_path }
+ = s_('For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}.').html_safe % { deactivating_usage_ping_link_start: deactivating_usage_ping_link_start, deactivating_usage_ping_link_end: '</a>'.html_safe }
+ .form-group.mt-3
= f.label :instance_statistics_visibility_private, _('Instance Statistics visibility')
= f.select :instance_statistics_visibility_private, options_for_select({_('All users') => false, _('Only admins') => true}, Gitlab::CurrentSettings.instance_statistics_visibility_private?), {}, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
-
diff --git a/app/views/admin/application_settings/ci_cd.html.haml b/app/views/admin/application_settings/ci_cd.html.haml
new file mode 100644
index 00000000000..db24c9982f7
--- /dev/null
+++ b/app/views/admin/application_settings/ci_cd.html.haml
@@ -0,0 +1,26 @@
+- breadcrumb_title _("CI/CD")
+- page_title _("CI/CD")
+- @content_class = "limit-container-width" unless fluid_layout
+
+%section.settings.as-ci-cd.no-animate#js-ci-cd-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Continuous Integration and Deployment')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Auto DevOps, runners and job artifacts')
+ .settings-content
+ = render 'ci_cd'
+
+- if Gitlab.config.registry.enabled
+ %section.settings.as-registry.no-animate#js-registry-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Container Registry')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Various container registry settings.')
+ .settings-content
+ = render 'registry'
diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml
new file mode 100644
index 00000000000..310e86b1377
--- /dev/null
+++ b/app/views/admin/application_settings/integrations.html.haml
@@ -0,0 +1,31 @@
+- breadcrumb_title _("Integrations")
+- page_title _("Integrations")
+- @content_class = "limit-container-width" unless fluid_layout
+
+= render_if_exists 'admin/application_settings/elasticsearch_form', expanded: expanded_by_default?
+
+%section.settings.as-plantuml.no-animate#js-plantuml-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('PlantUML')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Allow rendering of PlantUML diagrams in Asciidoc documents.')
+ .settings-content
+ = render 'plantuml'
+
+= render_if_exists 'admin/application_settings/slack', expanded: expanded_by_default?
+
+%section.settings.as-third-party-offers.no-animate#js-third-party-offers-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Third party offers')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Control the display of third party offers.')
+ .settings-content
+ = render 'third_party_offers', application_setting: @application_setting
+
+= render_if_exists 'admin/application_settings/snowplow', expanded: expanded_by_default?
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
new file mode 100644
index 00000000000..f50aca32bdf
--- /dev/null
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -0,0 +1,50 @@
+- breadcrumb_title _("Metrics and profiling")
+- page_title _("Metrics and profiling")
+- @content_class = "limit-container-width" unless fluid_layout
+
+%section.settings.as-influx.no-animate#js-influx-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Metrics - Influx')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Enable and configure InfluxDB metrics.')
+ .settings-content
+ = render 'influx'
+
+%section.settings.as-prometheus.no-animate#js-prometheus-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Metrics - Prometheus')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Enable and configure Prometheus metrics.')
+ .settings-content
+ = render 'prometheus'
+
+%section.settings.as-performance-bar.no-animate#js-performance-bar-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Profiling - Performance bar')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Enable the Performance Bar for a given group.')
+ = link_to icon('question-circle'), help_page_path('administration/monitoring/performance/performance_bar')
+ .settings-content
+ = render 'performance_bar'
+
+%section.settings.as-usage.no-animate#js-usage-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header#usage-statistics
+ %h4
+ = _('Usage statistics')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Enable or disable version check and usage ping.')
+ .settings-content
+ = render 'usage'
+
+= render_if_exists 'admin/application_settings/pseudonymizer_settings', expanded: expanded_by_default?
diff --git a/app/views/admin/application_settings/network.html.haml b/app/views/admin/application_settings/network.html.haml
new file mode 100644
index 00000000000..26fd745f45f
--- /dev/null
+++ b/app/views/admin/application_settings/network.html.haml
@@ -0,0 +1,36 @@
+- breadcrumb_title _("Network")
+- page_title _("Network")
+- @content_class = "limit-container-width" unless fluid_layout
+
+%section.settings.as-performance.no-animate#js-performance-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Performance optimization')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Various settings that affect GitLab performance.')
+ .settings-content
+ = render 'performance'
+
+%section.settings.as-ip-limits.no-animate#js-ip-limits-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('User and IP Rate Limits')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Configure limits for web and API requests.')
+ .settings-content
+ = render 'ip_limits'
+
+%section.settings.as-outbound.no-animate#js-outbound-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Outbound requests')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Allow requests to the local network from hooks and services.')
+ .settings-content
+ = render 'outbound'
diff --git a/app/views/admin/application_settings/preferences.html.haml b/app/views/admin/application_settings/preferences.html.haml
new file mode 100644
index 00000000000..00000b86ab7
--- /dev/null
+++ b/app/views/admin/application_settings/preferences.html.haml
@@ -0,0 +1,58 @@
+- breadcrumb_title _("Preferences")
+- page_title _("Preferences")
+- @content_class = "limit-container-width" unless fluid_layout
+
+%section.settings.as-email.no-animate#js-email-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Email')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Various email settings.')
+ .settings-content
+ = render 'email'
+
+%section.settings.as-help-page.no-animate#js-help-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Help page')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Help page text and support page url.')
+ .settings-content
+ = render 'help_page'
+
+%section.settings.as-pages.no-animate#js-pages-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Pages')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Size and domain settings for static websites')
+ .settings-content
+ = render 'pages'
+
+%section.settings.as-realtime.no-animate#js-realtime-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Real-time features')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Change this value to influence how frequently the GitLab UI polls for updates.')
+ .settings-content
+ = render 'realtime'
+
+%section.settings.as-gitaly.no-animate#js-gitaly-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Gitaly')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Configure Gitaly timeouts.')
+ .settings-content
+ = render 'gitaly'
diff --git a/app/views/admin/application_settings/reporting.html.haml b/app/views/admin/application_settings/reporting.html.haml
new file mode 100644
index 00000000000..1c2d9ccdb2d
--- /dev/null
+++ b/app/views/admin/application_settings/reporting.html.haml
@@ -0,0 +1,36 @@
+- breadcrumb_title _("Reporting")
+- page_title _("Reporting")
+- @content_class = "limit-container-width" unless fluid_layout
+
+%section.settings.as-spam.no-animate#js-spam-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Spam and Anti-bot Protection')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Enable reCAPTCHA or Akismet and set IP limits.')
+ .settings-content
+ = render 'spam'
+
+%section.settings.as-abuse.no-animate#js-abuse-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Abuse reports')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Set notification email for abuse reports.')
+ .settings-content
+ = render 'abuse'
+
+%section.settings.as-logging.no-animate#js-logging-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Error Reporting and Logging')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Enable Sentry for error reporting and logging.')
+ .settings-content
+ = render 'logging'
diff --git a/app/views/admin/application_settings/repository.html.haml b/app/views/admin/application_settings/repository.html.haml
new file mode 100644
index 00000000000..b50a0dd5a18
--- /dev/null
+++ b/app/views/admin/application_settings/repository.html.haml
@@ -0,0 +1,36 @@
+- breadcrumb_title _("Repository")
+- page_title _("Repository")
+- @content_class = "limit-container-width" unless fluid_layout
+
+%section.settings.as-mirror.no-animate#js-mirror-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Repository mirror')
+ %button.btn.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? 'Collapse' : 'Expand'
+ %p
+ = _('Configure push mirrors.')
+ .settings-content
+ = render partial: 'repository_mirrors_form'
+
+%section.settings.qa-repository-storage-settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Repository storage')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Configure storage path settings.')
+ .settings-content
+ = render 'repository_storage'
+
+%section.settings.as-repository-check.no-animate#js-repository-check-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Repository maintenance')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Configure automatic git checks and housekeeping on repositories.')
+ .settings-content
+ = render 'repository_check'
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index 258d50ad676..65e4723afe6 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -1,349 +1,92 @@
-- breadcrumb_title "Settings"
-- page_title "Settings"
+- breadcrumb_title _("Settings")
+- page_title _("Settings")
- @content_class = "limit-container-width" unless fluid_layout
-- expanded = Rails.env.test?
-%section.settings.as-visibility-access.no-animate#js-visibility-settings{ class: ('expanded' if expanded) }
+%section.settings.as-visibility-access.no-animate#js-visibility-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Visibility and access controls')
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ = expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Set default and restrict visibility levels. Configure import sources and git access protocol.')
.settings-content
= render 'visibility_and_access'
-%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded) }
+%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Account and limit')
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ = expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Session expiration, projects limit and attachment size.')
.settings-content
= render 'account_and_limit'
-%section.settings.as-signup.no-animate#js-signup-settings{ class: ('expanded' if expanded) }
+%section.settings.as-diff-limits.no-animate#js-merge-request-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Diff limits')
+ %button.btn.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Diff content limits')
+ .settings-content
+ = render 'diff_limits'
+
+%section.settings.as-signup.no-animate#js-signup-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Sign-up restrictions')
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ = expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Configure the way a user creates a new account.')
.settings-content
= render 'signup'
-%section.settings.as-signin.no-animate#js-signin-settings{ class: ('expanded' if expanded) }
+%section.settings.as-signin.no-animate#js-signin-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Sign-in restrictions')
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ = expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Set requirements for a user to sign-in. Enable mandatory two-factor authentication.')
.settings-content
= render 'signin'
-%section.settings.as-terms.no-animate#js-terms-settings{ class: ('expanded' if expanded) }
+%section.settings.as-terms.no-animate#js-terms-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Terms of Service and Privacy Policy')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ = expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Include a Terms of Service agreement and Privacy Policy that all users must accept.')
.settings-content
= render 'terms'
-%section.settings.as-help-page.no-animate#js-help-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Help page')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Help page text and support page url.')
- .settings-content
- = render 'help_page'
-
-%section.settings.as-pages.no-animate#js-pages-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Pages')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Size and domain settings for static websites')
- .settings-content
- = render 'pages'
-
-%section.settings.as-ci-cd.no-animate#js-ci-cd-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Continuous Integration and Deployment')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Auto DevOps, runners and job artifacts')
- .settings-content
- = render 'ci_cd'
-
-%section.settings.as-influx.no-animate#js-influx-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Metrics - Influx')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Enable and configure InfluxDB metrics.')
- .settings-content
- = render 'influx'
-
-%section.settings.as-prometheus.no-animate#js-prometheus-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Metrics - Prometheus')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Enable and configure Prometheus metrics.')
- .settings-content
- = render 'prometheus'
-
-%section.settings.as-performance-bar.no-animate#js-performance-bar-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Profiling - Performance bar')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Enable the Performance Bar for a given group.')
- = link_to icon('question-circle'), help_page_path('administration/monitoring/performance/performance_bar')
- .settings-content
- = render 'performance_bar'
-
-%section.settings.as-background.no-animate#js-background-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Background jobs')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Configure Sidekiq job throttling.')
- .settings-content
- = render 'background_jobs'
-
-%section.settings.as-spam.no-animate#js-spam-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Spam and Anti-bot Protection')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Enable reCAPTCHA or Akismet and set IP limits.')
- .settings-content
- = render 'spam'
-
-%section.settings.as-abuse.no-animate#js-abuse-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Abuse reports')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Set notification email for abuse reports.')
- .settings-content
- = render 'abuse'
-
-%section.settings.as-logging.no-animate#js-logging-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Error Reporting and Logging')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Enable Sentry for error reporting and logging.')
- .settings-content
- = render 'logging'
-
-%section.qa-repository-storage-settings.settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Repository storage')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Configure storage path and circuit breaker settings.')
- .settings-content
- = render 'repository_storage'
-
-%section.settings.as-repository-check.no-animate#js-repository-check-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Repository maintenance')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Configure automatic git checks and housekeeping on repositories.')
- .settings-content
- = render 'repository_check'
-
-- if Gitlab.config.registry.enabled
- %section.settings.as-registry.no-animate#js-registry-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Container Registry')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Various container registry settings.')
- .settings-content
- = render 'registry'
-
-- if koding_enabled?
- %section.settings.as-koding.no-animate#js-koding-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Koding')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Online IDE integration settings.')
- .settings-content
- = render 'koding'
+= render_if_exists 'admin/application_settings/external_authorization_service_form', expanded: expanded_by_default?
-%section.settings.as-plantuml.no-animate#js-plantuml-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('PlantUML')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Allow rendering of PlantUML diagrams in Asciidoc documents.')
- .settings-content
- = render 'plantuml'
-
-%section.settings.as-usage.no-animate#js-usage-settings{ class: ('expanded' if expanded) }
- .settings-header#usage-statistics
- %h4
- = _('Usage statistics')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Enable or disable version check and usage ping.')
- .settings-content
- = render 'usage'
-
-%section.settings.as-email.no-animate#js-email-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Email')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Various email settings.')
- .settings-content
- = render 'email'
-
-%section.settings.as-gitaly.no-animate#js-gitaly-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Gitaly')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Configure Gitaly timeouts.')
- .settings-content
- = render 'gitaly'
-
-%section.settings.as-terminal.no-animate#js-terminal-settings{ class: ('expanded' if expanded) }
+%section.settings.as-terminal.no-animate#js-terminal-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Web terminal')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ = expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Set max session time for web terminal.')
.settings-content
= render 'terminal'
-%section.settings.as-realtime.no-animate#js-realtime-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Real-time features')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Change this value to influence how frequently the GitLab UI polls for updates.')
- .settings-content
- = render 'realtime'
-
-%section.settings.as-performance.no-animate#js-performance-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Performance optimization')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Various settings that affect GitLab performance.')
- .settings-content
- = render 'performance'
-
-%section.settings.as-ip-limits.no-animate#js-ip-limits-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('User and IP Rate Limits')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Configure limits for web and API requests.')
- .settings-content
- = render 'ip_limits'
-
-%section.settings.as-outbound.no-animate#js-outbound-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Outbound requests')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Allow requests to the local network from hooks and services.')
- .settings-content
- = render 'outbound'
-
-%section.settings.as-mirror.no-animate#js-mirror-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Repository mirror')
- %button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
- %p
- = _('Configure push mirrors.')
- .settings-content
- = render partial: 'repository_mirrors_form'
-
-%section.settings.as-third-party-offers.no-animate#js-third-party-offers-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Third party offers')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Control the display of third party offers.')
- .settings-content
- = render 'third_party_offers', application_setting: @application_setting
-
-= render_if_exists 'admin/application_settings/custom_templates_form', expanded: expanded
-
-%section.settings.no-animate#js-web-ide-settings{ class: ('expanded' if expanded) }
+%section.settings.no-animate#js-web-ide-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Web IDE')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ = expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Manage Web IDE features')
.settings-content
@@ -360,5 +103,3 @@
= s_('IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation.')
= f.submit _('Save changes'), class: "btn btn-success"
-
-= render_if_exists 'admin/application_settings/pseudonymizer_settings', expanded: expanded
diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml
index 7f14cddebd8..12690343f6e 100644
--- a/app/views/admin/applications/_form.html.haml
+++ b/app/views/admin/applications/_form.html.haml
@@ -21,17 +21,17 @@
for local tests
= content_tag :div, class: 'form-group row' do
- = f.label :trusted, class: 'col-sm-2 col-form-label'
+ = f.label :trusted, class: 'col-sm-2 col-form-label pt-0'
.col-sm-10
= f.check_box :trusted
%span.form-text.text-muted
Trusted applications are automatically authorized on GitLab OAuth flow.
.form-group.row
- = f.label :scopes, class: 'col-sm-2 col-form-label'
+ = f.label :scopes, class: 'col-sm-2 col-form-label pt-0'
.col-sm-10
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
.form-actions
- = f.submit 'Submit', class: "btn btn-save wide"
+ = f.submit 'Submit', class: "btn btn-success wide"
= link_to "Cancel", admin_applications_path, class: "btn btn-cancel"
diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml
index 94d33fa6489..2cdf98075d1 100644
--- a/app/views/admin/applications/index.html.haml
+++ b/app/views/admin/applications/index.html.haml
@@ -5,7 +5,7 @@
System OAuth applications don't belong to any user and can only be managed by admins
%hr
%p= link_to 'New application', new_admin_application_path, class: 'btn btn-success'
-%table.table.table-striped
+%table.table
%thead
%tr
%th Name
diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml
index 593a6d816e3..df3eeba907c 100644
--- a/app/views/admin/applications/show.html.haml
+++ b/app/views/admin/applications/show.html.haml
@@ -1,4 +1,5 @@
- page_title @application.name, "Applications"
+
%h3.page-title
Application: #{@application.name}
@@ -6,23 +7,29 @@
%table.table
%tr
%td
- Application Id
+ = _('Application ID')
%td
- %code#application_id= @application.uid
+ .clipboard-group
+ .input-group
+ %input.label.label-monospace{ id: "application_id", type: "text", autocomplete: 'off', value: @application.uid, readonly: true }
+ .input-group-append
+ = clipboard_button(target: '#application_id', title: _("Copy ID to clipboard"), class: "btn btn btn-default")
%tr
%td
- Secret:
+ = _('Secret')
%td
- %code#secret= @application.secret
-
+ .clipboard-group
+ .input-group
+ %input.label.label-monospace{ id: "secret", type: "text", autocomplete: 'off', value: @application.secret, readonly: true }
+ .input-group-append
+ = clipboard_button(target: '#secret', title: _("Copy secret to clipboard"), class: "btn btn btn-default")
%tr
%td
- Callback url
+ = _('Callback URL')
%td
- @application.redirect_uri.split.each do |uri|
%div
%span.monospace= uri
-
%tr
%td
Trusted
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index faa5854bb40..a0a00ac5d96 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -6,40 +6,5 @@
%p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
%hr
-
- .card
- .card-header Sidekiq running processes
- .card-body
- - if @sidekiq_processes.empty?
- %h4.cred
- %i.fa.fa-exclamation-triangle
- There are no running sidekiq processes. Please restart GitLab
- - else
- .table-holder
- %table.table
- %thead
- %th USER
- %th PID
- %th CPU
- %th MEM
- %th STATE
- %th START
- %th COMMAND
- %tbody
- - @sidekiq_processes.each do |process|
- %tr
- %td= gitlab_config.user
- - parse_sidekiq_ps(process).each do |value|
- %td= value
- .clearfix
- %p
- %i.fa.fa-exclamation-circle
- If '[#{@concurrency} of #{@concurrency} busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
- %p
- %i.fa.fa-exclamation-circle
- If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{gitlab_config.user} -f sidekiq) and restart GitLab.
-
-
-
.card
%iframe{ src: sidekiq_path, width: '100%', height: 970, style: "border: 0" }
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index 7f34357f147..c465d9f51d6 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -36,6 +36,6 @@
= f.datetime_select :ends_at, {}, class: 'form-control form-control-inline'
.form-actions
- if @broadcast_message.persisted?
- = f.submit "Update broadcast message", class: "btn btn-create"
+ = f.submit "Update broadcast message", class: "btn btn-success"
- else
- = f.submit "Add broadcast message", class: "btn btn-create"
+ = f.submit "Add broadcast message", class: "btn btn-success"
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index fac61f9d249..7ac79cc77f5 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -14,7 +14,7 @@
Projects:
= approximate_count_with_delimiters(@counts, Project)
%hr
- = link_to('New project', new_project_path, class: "btn btn-new")
+ = link_to('New project', new_project_path, class: "btn btn-success")
.col-sm-4
.info-well.dark-well
.well-segment.well-centered
@@ -24,7 +24,7 @@
= approximate_count_with_delimiters(@counts, User)
= render_if_exists 'admin/dashboard/users_statistics'
%hr
- = link_to 'New user', new_admin_user_path, class: "btn btn-new"
+ = link_to 'New user', new_admin_user_path, class: "btn btn-success"
.col-sm-4
.info-well.dark-well
.well-segment.well-centered
@@ -33,7 +33,7 @@
Groups:
= approximate_count_with_delimiters(@counts, Group)
%hr
- = link_to 'New group', new_admin_group_path, class: "btn btn-new"
+ = link_to 'New group', new_admin_group_path, class: "btn btn-success"
.row
.col-md-4
.info-well
@@ -42,7 +42,7 @@
%p
Forks
%span.light.float-right
- = approximate_count_with_delimiters(@counts, ForkedProjectLink)
+ = approximate_fork_count_with_delimiters(@counts)
%p
Issues
%span.light.float-right
diff --git a/app/views/admin/deploy_keys/edit.html.haml b/app/views/admin/deploy_keys/edit.html.haml
index b50adef362f..7c04ef03947 100644
--- a/app/views/admin/deploy_keys/edit.html.haml
+++ b/app/views/admin/deploy_keys/edit.html.haml
@@ -6,5 +6,5 @@
= form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f|
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
.form-actions
- = f.submit 'Save changes', class: 'btn-save btn'
+ = f.submit 'Save changes', class: 'btn-success btn'
= link_to 'Cancel', admin_deploy_keys_path, class: 'btn btn-cancel'
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 52ab8bae119..01013be06d6 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -3,7 +3,7 @@
%h3.page-title.deploy-keys-title
Public deploy keys (#{@deploy_keys.count})
.float-right
- = link_to 'New deploy key', new_admin_deploy_key_path, class: 'btn btn-new btn-sm btn-inverted'
+ = link_to 'New deploy key', new_admin_deploy_key_path, class: 'btn btn-success btn-sm btn-inverted'
- if @deploy_keys.any?
.table-holder.deploy-keys-list
diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml
index d4f8e340b69..9a563a5bc78 100644
--- a/app/views/admin/deploy_keys/new.html.haml
+++ b/app/views/admin/deploy_keys/new.html.haml
@@ -6,5 +6,5 @@
= form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f|
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
.form-actions
- = f.submit 'Create', class: 'btn-create btn'
+ = f.submit 'Create', class: 'btn-success btn'
= link_to 'Cancel', admin_deploy_keys_path, class: 'btn btn-cancel'
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index a3773e90cfb..5e05568e384 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -10,11 +10,11 @@
.col-sm-10
= render 'shared/choose_group_avatar_button', f: f
- = render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
+ = render 'shared/old_visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group, with_label: false
.form-group.row
.offset-sm-2.col-sm-10
- = render 'shared/allow_request_access', form: f
+ = render 'shared/allow_request_access', form: f, bold_label: true
= render 'groups/group_admin_settings', f: f
@@ -26,12 +26,12 @@
.alert.alert-info
= render 'shared/group_tips'
.form-actions
- = f.submit _('Create group'), class: "btn btn-create"
+ = f.submit _('Create group'), class: "btn btn-success"
= link_to _('Cancel'), admin_groups_path, class: "btn btn-cancel"
- else
.form-actions
- = f.submit _('Save changes'), class: "btn btn-save"
+ = f.submit _('Save changes'), class: "btn btn-success"
= link_to _('Cancel'), admin_group_path(@group), class: "btn btn-cancel"
= render_if_exists 'ldap_group_links/ldap_syncrhonizations', group: @group
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 6a9b85b4109..cb833ffd9ac 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -12,7 +12,7 @@
= search_field_tag :name, project_name, class: "form-control search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: 'Search by name'
= icon("search", class: "search-icon")
= render "shared/groups/dropdown", options_hash: admin_groups_sort_options_hash
- = link_to new_admin_group_path, class: "btn btn-new" do
+ = link_to new_admin_group_path, class: "btn btn-success" do
= _('New group')
%ul.content-list
= render @groups
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 72b068ea6b5..5f205d1bcbc 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -63,10 +63,9 @@
.card
.card-header
- %h3.card-title
- = _('Projects')
- %span.badge.badge-pill
- #{@group.projects.count}
+ = _('Projects')
+ %span.badge.badge-pill
+ #{@group.projects.count}
%ul.content-list
- @projects.each do |project|
%li
@@ -111,7 +110,7 @@
.prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
- = button_tag _('Add users to group'), class: "btn btn-create"
+ = button_tag _('Add users to group'), class: "btn btn-success"
= render 'shared/members/requests', membership_source: @group, requesters: @requesters, force_mobile_view: true
.card
@@ -119,7 +118,7 @@
= _("<strong>%{group_name}</strong> group members").html_safe % { group_name: @group.name }
%span.badge.badge-pill= @group.members.size
.float-right
- = link_to icon('pencil-square-o', text: _('Manage access')), polymorphic_url([@group, :members]), class: "btn btn-sm"
+ = link_to icon('pencil-square-o', text: _('Manage access')), group_group_members_path(@group), class: "btn btn-sm"
%ul.content-list.group-users-list.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false }
.card-footer
diff --git a/app/views/admin/health_check/_failing_storages.html.haml b/app/views/admin/health_check/_failing_storages.html.haml
deleted file mode 100644
index 6830201538d..00000000000
--- a/app/views/admin/health_check/_failing_storages.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-- if failing_storages.any?
- = _('There are problems accessing Git storage: ')
- %ul
- - failing_storages.each do |storage_health|
- %li
- = failing_storage_health_message(storage_health)
- %ul
- - storage_health.failing_circuit_breakers.each do |circuit_breaker|
- %li
- #{circuit_breaker.hostname}: #{message_for_circuit_breaker(circuit_breaker)}
-
- = _("Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again.")
- .prepend-top-10
- = button_to _("Reset git storage health information"), reset_storage_health_admin_health_check_path,
- method: :post, class: 'btn btn-default'
diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml
index d51ac854b04..0f5e97e288a 100644
--- a/app/views/admin/health_check/show.html.haml
+++ b/app/views/admin/health_check/show.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
- page_title _('Health Check')
-- no_errors = @errors.blank? && @failing_storage_statuses.blank?
+- no_errors = @errors.blank?
%div{ class: container_class }
%h3.page-title= page_title
@@ -39,4 +39,3 @@
#{ s_('HealthCheck|No Health Problems Detected') }
- else
= @errors
- = render partial: 'failing_storages', object: @failing_storage_statuses
diff --git a/app/views/admin/hook_logs/show.html.haml b/app/views/admin/hook_logs/show.html.haml
index 2eb3ac85722..86729dbe7bc 100644
--- a/app/views/admin/hook_logs/show.html.haml
+++ b/app/views/admin/hook_logs/show.html.haml
@@ -4,7 +4,6 @@
%hr
-= link_to 'Resend Request', retry_admin_hook_hook_log_path(@hook, @hook_log), class: "btn btn-default float-right prepend-left-10"
+= link_to 'Resend Request', retry_admin_hook_hook_log_path(@hook, @hook_log), method: :post, class: "btn btn-default float-right prepend-left-10"
= render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log }
-
diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml
index b9a650e1f1f..486d0477f20 100644
--- a/app/views/admin/hooks/edit.html.haml
+++ b/app/views/admin/hooks/edit.html.haml
@@ -12,7 +12,7 @@
= form_for @hook, as: :hook, url: admin_hook_path do |f|
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions
- = f.submit 'Save changes', class: 'btn btn-create'
+ = f.submit 'Save changes', class: 'btn btn-success'
= render 'shared/web_hooks/test_button', triggers: SystemHook.triggers, hook: @hook
= link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove float-right', data: { confirm: 'Are you sure?' }
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index 87f9b0e86a7..5d462d7b732 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -10,7 +10,7 @@
.col-lg-8.append-bottom-default
= form_for @hook, as: :hook, url: admin_hooks_path do |f|
= render partial: 'form', locals: { form: f, hook: @hook }
- = f.submit 'Add system hook', class: 'btn btn-create'
+ = f.submit 'Add system hook', class: 'btn btn-success'
%hr
diff --git a/app/views/admin/identities/_form.html.haml b/app/views/admin/identities/_form.html.haml
index 946d868da01..3ab7990d9e2 100644
--- a/app/views/admin/identities/_form.html.haml
+++ b/app/views/admin/identities/_form.html.haml
@@ -12,5 +12,5 @@
= f.text_field :extern_uid, class: 'form-control', required: true
.form-actions
- = f.submit _('Save changes'), class: "btn btn-save"
+ = f.submit _('Save changes'), class: "btn btn-success"
diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml
index df3df159947..9543bbcf977 100644
--- a/app/views/admin/identities/index.html.haml
+++ b/app/views/admin/identities/index.html.haml
@@ -3,7 +3,7 @@
- page_title _("Identities"), @user.name, _("Users")
= render 'admin/users/head'
-= link_to _('New identity'), new_admin_user_identity_path, class: 'float-right btn btn-new'
+= link_to _('New identity'), new_admin_user_identity_path, class: 'float-right btn btn-success'
- if @identities.present?
.table-holder
%table.table
diff --git a/app/views/admin/impersonation_tokens/index.html.haml b/app/views/admin/impersonation_tokens/index.html.haml
index 9e490713ef3..8e869fb4b71 100644
--- a/app/views/admin/impersonation_tokens/index.html.haml
+++ b/app/views/admin/impersonation_tokens/index.html.haml
@@ -5,6 +5,11 @@
.row.prepend-top-default
.col-lg-12
+ - if @new_impersonation_token
+ = render "shared/personal_access_tokens_created_container", new_token_value: @new_impersonation_token,
+ container_title: 'Your New Impersonation Token',
+ clipboard_button_title: 'Copy impersonation token to clipboard'
+
= render "shared/personal_access_tokens_form", path: admin_user_impersonation_tokens_path, impersonation: true, token: @impersonation_token, scopes: @scopes
= render "shared/personal_access_tokens_table", impersonation: true, active_tokens: @active_impersonation_tokens, inactive_tokens: @inactive_impersonation_tokens
diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml
index ee2d4c8430a..5e7b4817461 100644
--- a/app/views/admin/labels/_form.html.haml
+++ b/app/views/admin/labels/_form.html.haml
@@ -27,5 +27,5 @@
&nbsp;
.form-actions
- = f.submit _('Save'), class: 'btn btn-save js-save-button'
+ = f.submit _('Save'), class: 'btn btn-success js-save-button'
= link_to _("Cancel"), admin_labels_path, class: 'btn btn-cancel'
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
index f1b8658f84e..5a5b3d18c5f 100644
--- a/app/views/admin/labels/index.html.haml
+++ b/app/views/admin/labels/index.html.haml
@@ -1,7 +1,7 @@
- page_title _("Labels")
%div
- = link_to new_admin_label_path, class: "float-right btn btn-nr btn-new" do
+ = link_to new_admin_label_path, class: "float-right btn btn-nr btn-success" do
= _('New label')
%h3.page-title
= _('Labels')
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 57de792f92d..46bb57c78a8 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -21,7 +21,7 @@
= dropdown_content
= dropdown_loading
= render 'shared/projects/dropdown'
- = link_to new_project_path, class: 'btn btn-new' do
+ = link_to new_project_path, class: 'btn btn-success' do
New Project
= button_tag "Search", class: "btn btn-primary btn-search hide"
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index ccba1c461fc..03cce4745aa 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -1,6 +1,8 @@
- add_to_breadcrumbs "Projects", admin_projects_path
- breadcrumb_title @project.full_name
- page_title @project.full_name, "Projects"
+- @content_class = "admin-projects"
+
%h3.page-title
Project: #{@project.full_name}
= link_to edit_project_path(@project), class: "btn btn-nr float-right" do
@@ -110,6 +112,8 @@
= visibility_level_icon(@project.visibility_level)
= visibility_level_label(@project.visibility_level)
+ = render_if_exists 'admin/projects/geo_status_widget', locals: { project: @project }
+
.card
.card-header
Transfer project
@@ -179,7 +183,7 @@
project members
%span.badge.badge-pill= @project.users.size
.float-right
- = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@project, :members]), class: "btn btn-sm"
+ = link_to icon('pencil-square-o', text: 'Manage access'), project_project_members_path(@project), class: "btn btn-sm"
%ul.content-list.project_members.members-list
= render partial: 'shared/members/member', collection: @project_members, as: :member, locals: { show_controls: false }
.card-footer
diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml
index 43937b01339..e4fc2985087 100644
--- a/app/views/admin/runners/_runner.html.haml
+++ b/app/views/admin/runners/_runner.html.haml
@@ -1,51 +1,78 @@
-%tr{ id: dom_id(runner) }
- %td
- - if runner.instance_type?
- %span.badge.badge-success shared
- - elsif runner.group_type?
- %span.badge.badge-success group
- - else
- %span.badge.badge-info specific
- - if runner.locked?
- %span.badge.badge-warning locked
- - unless runner.active?
- %span.badge.badge-danger paused
-
- %td
- = link_to admin_runner_path(runner) do
- = runner.short_sha
- %td
- = runner.description
- %td
- = runner.version
- %td
- = runner.ip_address
- %td
- - if runner.instance_type? || runner.group_type?
- n/a
- - else
- = runner.projects.count(:all)
- %td
- #{runner.builds.count(:all)}
- %td
- - runner.tag_list.sort.each do |tag|
- %span.badge.badge-primary
- = tag
- %td
- - if runner.contacted_at
- = time_ago_with_tooltip runner.contacted_at
- - else
- Never
- %td.admin-runner-btn-group-cell
- .float-right.btn-group
- = link_to admin_runner_path(runner), class: 'btn btn-sm btn-default has-tooltip', title: 'Edit', ref: 'tooltip', aria: { label: 'Edit' }, data: { placement: 'top', container: 'body'} do
- = icon('pencil')
- &nbsp;
- - if runner.active?
- = link_to [:pause, :admin, runner], method: :get, class: 'btn btn-sm btn-default has-tooltip', title: 'Pause', ref: 'tooltip', aria: { label: 'Pause' }, data: { placement: 'top', container: 'body', confirm: "Are you sure?" } do
- = icon('pause')
+.gl-responsive-table-row{ id: dom_id(runner) }
+ .table-section.section-10.section-wrap
+ .table-mobile-header{ role: 'rowheader' }= _('Type')
+ .table-mobile-content
+ - if runner.instance_type?
+ %span.badge.badge-success shared
+ - elsif runner.group_type?
+ %span.badge.badge-success group
- else
- = link_to [:resume, :admin, runner], method: :get, class: 'btn btn-default btn-sm has-tooltip', title: 'Resume', ref: 'tooltip', aria: { label: 'Resume' }, data: { placement: 'top', container: 'body'} do
- = icon('play')
- = link_to [:admin, runner], method: :delete, class: 'btn btn-danger btn-sm has-tooltip', title: 'Remove', ref: 'tooltip', aria: { label: 'Remove' }, data: { placement: 'top', container: 'body', confirm: "Are you sure?" } do
- = icon('remove')
+ %span.badge.badge-info specific
+ - if runner.locked?
+ %span.badge.badge-warning locked
+ - unless runner.active?
+ %span.badge.badge-danger paused
+
+ .table-section.section-10
+ .table-mobile-header{ role: 'rowheader' }= _('Runner token')
+ .table-mobile-content
+ = link_to runner.short_sha, admin_runner_path(runner)
+
+ .table-section.section-15
+ .table-mobile-header{ role: 'rowheader' }= _('Description')
+ .table-mobile-content.str-truncated.has-tooltip{ title: runner.description }
+ = runner.description
+
+ .table-section.section-15
+ .table-mobile-header{ role: 'rowheader' }= _('Version')
+ .table-mobile-content.str-truncated.has-tooltip{ title: runner.version }
+ = runner.version
+
+ .table-section.section-10
+ .table-mobile-header{ role: 'rowheader' }= _('IP Address')
+ .table-mobile-content
+ = runner.ip_address
+
+ .table-section.section-5
+ .table-mobile-header{ role: 'rowheader' }= _('Projects')
+ .table-mobile-content
+ - if runner.instance_type? || runner.group_type?
+ = _('n/a')
+ - else
+ = runner.projects.count(:all)
+
+ .table-section.section-5
+ .table-mobile-header{ role: 'rowheader' }= _('Jobs')
+ .table-mobile-content
+ = runner.builds.count(:all)
+
+ .table-section.section-10.section-wrap
+ .table-mobile-header{ role: 'rowheader' }= _('Tags')
+ .table-mobile-content
+ - runner.tag_list.sort.each do |tag|
+ %span.badge.badge-primary
+ = tag
+
+ .table-section.section-10
+ .table-mobile-header{ role: 'rowheader' }= _('Last contact')
+ .table-mobile-content
+ - if runner.contacted_at
+ = time_ago_with_tooltip runner.contacted_at
+ - else
+ = _('Never')
+
+ .table-section.table-button-footer.section-10
+ .btn-group.table-action-buttons
+ .btn-group
+ = link_to admin_runner_path(runner), class: 'btn btn-default has-tooltip', title: _('Edit'), ref: 'tooltip', aria: { label: _('Edit') }, data: { placement: 'top', container: 'body'} do
+ = icon('pencil')
+ .btn-group
+ - if runner.active?
+ = link_to [:pause, :admin, runner], method: :get, class: 'btn btn-default has-tooltip', title: _('Pause'), ref: 'tooltip', aria: { label: _('Pause') }, data: { placement: 'top', container: 'body', confirm: _('Are you sure?') } do
+ = icon('pause')
+ - else
+ = link_to [:resume, :admin, runner], method: :get, class: 'btn btn-default has-tooltip', title: _('Resume'), ref: 'tooltip', aria: { label: _('Resume') }, data: { placement: 'top', container: 'body'} do
+ = icon('play')
+ .btn-group
+ = link_to [:admin, runner], method: :delete, class: 'btn btn-danger has-tooltip', title: _('Remove'), ref: 'tooltip', aria: { label: _('Remove') }, data: { placement: 'top', container: 'body', confirm: _('Are you sure?') } do
+ = icon('remove')
diff --git a/app/views/admin/runners/_sort_dropdown.html.haml b/app/views/admin/runners/_sort_dropdown.html.haml
new file mode 100644
index 00000000000..b201e6bf10e
--- /dev/null
+++ b/app/views/admin/runners/_sort_dropdown.html.haml
@@ -0,0 +1,11 @@
+- sorted_by = sort_options_hash[@sort]
+
+.dropdown.inline.prepend-left-10
+ %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
+ = sorted_by
+ = icon('chevron-down')
+ %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
+ %li
+ = sortable_item(sort_title_created_date, page_filter_path(sort: sort_value_created_date, label: true), sorted_by)
+ = sortable_item(sort_title_contacted_date, page_filter_path(sort: sort_value_contacted_date, label: true), sorted_by)
+
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 8dfd176f1b7..e9e4e0847d3 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -1,77 +1,122 @@
-- breadcrumb_title "Runners"
+- breadcrumb_title _('Runners')
- @no_container = true
%div{ class: container_class }
- .bs-callout
- %p
- A 'Runner' is a process which runs a job.
- You can setup as many Runners as you need.
- %br
- Runners can be placed on separate users, servers, even on your local machine.
- %br
+ .row
+ .col-sm-6
+ .bs-callout
+ %p
+ = (_"A 'Runner' is a process which runs a job. You can set up as many Runners as you need.")
+ %br
+ = _('Runners can be placed on separate users, servers, even on your local machine.')
+ %br
- %div
- %span Each Runner can be in one of the following states:
- %ul
- %li
- %span.badge.badge-success shared
- \- Runner runs jobs from all unassigned projects
- %li
- %span.badge.badge-success group
- \- Runner runs jobs from all unassigned projects in its group
- %li
- %span.badge.badge-info specific
- \- Runner runs jobs from assigned projects
- %li
- %span.badge.badge-warning locked
- \- Runner cannot be assigned to other projects
- %li
- %span.badge.badge-danger paused
- \- Runner will not receive any new jobs
+ %div
+ %span= _('Each Runner can be in one of the following states:')
+ %ul
+ %li
+ %span.badge.badge-success shared
+ \-
+ = _('Runner runs jobs from all unassigned projects')
+ %li
+ %span.badge.badge-success group
+ \-
+ = _('Runner runs jobs from all unassigned projects in its group')
+ %li
+ %span.badge.badge-info specific
+ \-
+ = _('Runner runs jobs from assigned projects')
+ %li
+ %span.badge.badge-warning locked
+ \-
+ = _('Runner cannot be assigned to other projects')
+ %li
+ %span.badge.badge-danger paused
+ \-
+ = _('Runner will not receive any new jobs')
- .bs-callout.clearfix
- .float-left
- %p
- You can reset runners registration token by pressing a button below.
- .prepend-top-10
- = button_to _("Reset runners registration token"), reset_runners_token_admin_application_settings_path,
- method: :put, class: 'btn btn-default',
- data: { confirm: _("Are you sure you want to reset registration token?") }
+ .col-sm-6
+ .bs-callout
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token,
+ type: 'shared',
+ reset_token_url: reset_registration_token_admin_application_settings_path }
- = render partial: 'ci/runner/how_to_setup_shared_runner',
- locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token }
+ .row
+ .col-sm-9
+ = form_tag admin_runners_path, id: 'runners-search', method: :get, class: 'filter-form js-filter-form' do
+ .filtered-search-wrapper
+ .filtered-search-box
+ = dropdown_tag(custom_icon('icon_history'),
+ options: { wrapper_class: 'filtered-search-history-dropdown-wrapper',
+ toggle_class: 'filtered-search-history-dropdown-toggle-button',
+ dropdown_class: 'filtered-search-history-dropdown',
+ content_class: 'filtered-search-history-dropdown-content',
+ title: _('Recent searches') }) do
+ .js-filtered-search-history-dropdown{ data: { full_path: admin_runners_path } }
+ .filtered-search-box-input-container.droplab-dropdown
+ .scroll-container
+ %ul.tokens-container.list-unstyled
+ %li.input-token
+ %input.form-control.filtered-search{ { id: 'filtered-search-runners', placeholder: _('Search or filter results...') } }
+ #js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { action: 'submit' } }
+ = button_tag class: %w[btn btn-link] do
+ = sprite_icon('search')
+ %span
+ = _('Press Enter or click to search')
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
+ %li.filter-dropdown-item
+ = button_tag class: %w[btn btn-link] do
+ -# Encapsulate static class name `{{icon}}` inside #{} to bypass
+ -# haml lint's ClassAttributeWithStaticValue
+ %svg
+ %use{ 'xlink:href': "#{'{{icon}}'}" }
+ %span.js-filter-hint
+ {{hint}}
+ %span.js-filter-tag.dropdown-light-content
+ {{tag}}
- .append-bottom-20.clearfix
- .float-left
- = form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do
- .form-group
- = search_field_tag :search, params[:search], class: 'form-control input-short', placeholder: 'Runner description or token', spellcheck: false
- = submit_tag 'Search', class: 'btn'
+ #js-dropdown-admin-runner-status.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ - Ci::Runner::AVAILABLE_STATUSES.each do |status|
+ %li.filter-dropdown-item{ data: { value: status } }
+ = button_tag class: %w[btn btn-link] do
+ = status.titleize
- .float-right.light
- Runners with last contact more than a minute ago: #{@active_runners_cnt}
+ #js-dropdown-admin-runner-type.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ - Ci::Runner::AVAILABLE_TYPES.each do |runner_type|
+ %li.filter-dropdown-item{ data: { value: runner_type } }
+ = button_tag class: %w[btn btn-link] do
+ = runner_type.titleize
- %br
+ = button_tag class: %w[clear-search hidden] do
+ = icon('times')
+ .filter-dropdown-container
+ = render 'sort_dropdown'
+
+ .col-sm-3.text-right-lg
+ = _('Runners currently online: %{active_runners_count}') % { active_runners_count: @active_runners_count }
- if @runners.any?
- .runners-content
+ .runners-content.content-list
.table-holder
- %table.table
- %thead
- %tr
- %th Type
- %th Runner token
- %th Description
- %th Version
- %th IP Address
- %th Projects
- %th Jobs
- %th Tags
- %th= link_to 'Last contact', admin_runners_path(safe_params.slice(:search).merge(sort: 'contacted_asc'))
- %th
+ .gl-responsive-table-row.table-row-header{ role: 'row' }
+ .table-section.section-10{ role: 'rowheader' }= _('Type')
+ .table-section.section-10{ role: 'rowheader' }= _('Runner token')
+ .table-section.section-15{ role: 'rowheader' }= _('Description')
+ .table-section.section-15{ role: 'rowheader' }= _('Version')
+ .table-section.section-10{ role: 'rowheader' }= _('IP Address')
+ .table-section.section-5{ role: 'rowheader' }= _('Projects')
+ .table-section.section-5{ role: 'rowheader' }= _('Jobs')
+ .table-section.section-10{ role: 'rowheader' }= _('Tags')
+ .table-section.section-10{ role: 'rowheader' }= _('Last contact')
+ .table-section.section-10{ role: 'rowheader' }
- - @runners.each do |runner|
- = render "admin/runners/runner", runner: runner
- = paginate @runners, theme: "gitlab"
+ - @runners.each do |runner|
+ = render 'admin/runners/runner', runner: runner
+ = paginate @runners, theme: 'gitlab'
- else
- .nothing-here-block No runners found
+ .nothing-here-block= _('No runners found')
diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml
index 993006e8745..1798b44bbb7 100644
--- a/app/views/admin/services/_form.html.haml
+++ b/app/views/admin/services/_form.html.haml
@@ -7,4 +7,4 @@
= render 'shared/service_settings', form: form, subject: @service
.footer-block.row-content-block
- = form.submit 'Save', class: 'btn btn-save'
+ = form.submit 'Save', class: 'btn btn-success'
diff --git a/app/views/admin/spam_logs/index.html.haml b/app/views/admin/spam_logs/index.html.haml
index 8aaa6379730..b45d3e4823b 100644
--- a/app/views/admin/spam_logs/index.html.haml
+++ b/app/views/admin/spam_logs/index.html.haml
@@ -17,6 +17,6 @@
%th Primary Action
%th
= render @spam_logs
- = paginate @spam_logs
+ = paginate @spam_logs, theme: 'gitlab'
- else
%h4 There are no Spam Logs
diff --git a/app/views/admin/users/_access_levels.html.haml b/app/views/admin/users/_access_levels.html.haml
index 04acc5f8423..12e24ddef02 100644
--- a/app/views/admin/users/_access_levels.html.haml
+++ b/app/views/admin/users/_access_levels.html.haml
@@ -1,15 +1,18 @@
%fieldset
%legend Access
.form-group.row
- = f.label :projects_limit, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :projects_limit, class: 'col-form-label'
.col-sm-10= f.number_field :projects_limit, min: 0, max: Gitlab::Database::MAX_INT_VALUE, class: 'form-control'
.form-group.row
- = f.label :can_create_group, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :can_create_group, class: 'col-form-label'
.col-sm-10= f.check_box :can_create_group
.form-group.row
- = f.label :access_level, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :access_level, class: 'col-form-label'
.col-sm-10
- editing_current_user = (current_user == @user)
@@ -29,9 +32,14 @@
You cannot remove your own admin rights.
.form-group.row
- = f.label :external, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :external, class: 'col-form-label'
+ .hidden{ data: user_internal_regex_data }
.col-sm-10
= f.check_box :external do
External
%p.light
External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects or groups.
+ %row.hidden#warning_external_automatically_set.hidden
+ .badge.badge-warning.text-white
+ = _('Automatically marked as default internal user')
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index 58be07fc83e..296ef073144 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -5,17 +5,20 @@
%fieldset
%legend Account
.form-group.row
- = f.label :name, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :name, class: 'col-form-label'
.col-sm-10
= f.text_field :name, required: true, autocomplete: 'off', class: 'form-control'
%span.help-inline * required
.form-group.row
- = f.label :username, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :username, class: 'col-form-label'
.col-sm-10
= f.text_field :username, required: true, autocomplete: 'off', autocorrect: 'off', autocapitalize: 'off', spellcheck: false, class: 'form-control'
%span.help-inline * required
.form-group.row
- = f.label :email, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :email, class: 'col-form-label'
.col-sm-10
= f.text_field :email, required: true, autocomplete: 'off', class: 'form-control'
%span.help-inline * required
@@ -24,7 +27,8 @@
%fieldset
%legend Password
.form-group.row
- = f.label :password, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :password, class: 'col-form-label'
.col-sm-10
%strong
Reset link will be generated and sent to the user.
@@ -34,10 +38,12 @@
%fieldset
%legend Password
.form-group.row
- = f.label :password, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :password, class: 'col-form-label'
.col-sm-10= f.password_field :password, disabled: f.object.force_random_password, class: 'form-control'
.form-group.row
- = f.label :password_confirmation, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :password_confirmation, class: 'col-form-label'
.col-sm-10= f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control'
= render partial: 'access_levels', locals: { f: f }
@@ -45,27 +51,32 @@
%fieldset
%legend Profile
.form-group.row
- = f.label :avatar, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :avatar, class: 'col-form-label'
.col-sm-10
= f.file_field :avatar
.form-group.row
- = f.label :skype, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :skype, class: 'col-form-label'
.col-sm-10= f.text_field :skype, class: 'form-control'
.form-group.row
- = f.label :linkedin, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :linkedin, class: 'col-form-label'
.col-sm-10= f.text_field :linkedin, class: 'form-control'
.form-group.row
- = f.label :twitter, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :twitter, class: 'col-form-label'
.col-sm-10= f.text_field :twitter, class: 'form-control'
.form-group.row
- = f.label :website_url, 'Website', class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :website_url, 'Website', class: 'col-form-label'
.col-sm-10= f.text_field :website_url, class: 'form-control'
.form-actions
- if @user.new_record?
- = f.submit 'Create user', class: "btn btn-create"
+ = f.submit 'Create user', class: "btn btn-success"
= link_to 'Cancel', admin_users_path, class: "btn btn-cancel"
- else
- = f.submit 'Save changes', class: "btn btn-save"
+ = f.submit 'Save changes', class: "btn btn-success"
= link_to 'Cancel', admin_user_path(@user), class: "btn btn-cancel"
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index faeb82656ba..f910e90d6ca 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -31,7 +31,7 @@
= sort_title_recently_updated
= link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do
= sort_title_oldest_updated
- = link_to 'New user', new_admin_user_path, class: 'btn btn-new btn-search'
+ = link_to 'New user', new_admin_user_path, class: 'btn btn-success btn-search'
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
.fade-left
diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml
index 3d39c1da408..e6da81831ab 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -7,7 +7,7 @@
.card
.card-header Group projects
%ul.hover-list
- - @user.group_members.includes(:source).each do |group_member|
+ - @user.group_members.includes(:source).each do |group_member| # rubocop: disable CodeReuse/ActiveRecord
- group = group_member.group
%li.group_member
%strong= link_to group.name, admin_group_path(group)
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 029efadd75d..a74e052707f 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -39,6 +39,10 @@
%strong= email.email
= link_to remove_email_admin_user_path(@user, email), data: { confirm: "Are you sure you want to remove #{email.email}?" }, method: :delete, class: "btn-sm btn btn-remove float-right", title: 'Remove secondary email', id: "remove_email_#{email.id}" do
%i.fa.fa-times
+ %li
+ %span.light ID:
+ %strong
+ = @user.id
%li.two-factor-status
%span.light Two-factor Authentication:
diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml
index 8ca9fb4512e..a758a63dfb3 100644
--- a/app/views/award_emoji/_awards_block.html.haml
+++ b/app/views/award_emoji/_awards_block.html.haml
@@ -1,9 +1,8 @@
- grouped_emojis = awardable.grouped_awards(with_thumbs: inline)
-- user_authored = awardable.user_authored?(current_user)
.awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } }
- awards_sort(grouped_emojis).each do |emoji, awards|
%button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button",
- class: [(award_state_class(awardable, awards, current_user)), (award_user_authored_class(emoji) if user_authored)],
+ class: [(award_state_class(awardable, awards, current_user))],
data: { placement: "bottom", title: award_user_list(awards, current_user) } }
= emoji_icon(emoji)
%span.award-control-text.js-counter
@@ -13,7 +12,6 @@
.award-menu-holder.js-award-holder
%button.btn.award-control.has-tooltip.js-add-award{ type: 'button',
'aria-label': _('Add reaction'),
- class: ("js-user-authored" if user_authored),
data: { title: _('Add reaction'), placement: "bottom" } }
%span{ class: "award-control-icon award-control-icon-neutral" }= custom_icon('emoji_slightly_smiling_face')
%span{ class: "award-control-icon award-control-icon-positive" }= custom_icon('emoji_smiley')
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index 13f96b9747c..4307060d748 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -1,6 +1,6 @@
- link = link_to _("Install GitLab Runner"), 'https://docs.gitlab.com/runner/install/', target: '_blank'
.append-bottom-10
- %h4= _("Setup a #{type} Runner manually")
+ %h4= _("Set up a %{type} Runner manually") % { type: type }
%ol
%li
@@ -13,5 +13,9 @@
= _("Use the following registration token during setup:")
%code#registration_token= registration_token
= clipboard_button(target: '#registration_token', title: _("Copy token to clipboard"), class: "btn-transparent btn-clipboard")
+ .prepend-top-10.append-bottom-10
+ = button_to _("Reset runners registration token"), reset_token_url,
+ method: :put, class: 'btn btn-default',
+ data: { confirm: _("Are you sure you want to reset registration token?") }
%li
= _("Start the Runner!")
diff --git a/app/views/ci/runner/_how_to_setup_shared_runner.html.haml b/app/views/ci/runner/_how_to_setup_shared_runner.html.haml
deleted file mode 100644
index 2a190cb9250..00000000000
--- a/app/views/ci/runner/_how_to_setup_shared_runner.html.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-.bs-callout.help-callout
- = render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: registration_token, type: 'shared' }
diff --git a/app/views/ci/runner/_how_to_setup_specific_runner.html.haml b/app/views/ci/runner/_how_to_setup_specific_runner.html.haml
deleted file mode 100644
index e765a353fe4..00000000000
--- a/app/views/ci/runner/_how_to_setup_specific_runner.html.haml
+++ /dev/null
@@ -1,26 +0,0 @@
-.bs-callout.help-callout
- .append-bottom-10
- %h4= _('Setup a specific Runner automatically')
-
- %p
- - link_to_help_page = link_to(_('Learn more about Kubernetes'),
- help_page_path('user/project/clusters/index'),
- target: '_blank',
- rel: 'noopener noreferrer')
-
- = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page }
-
- %ol
- %li
- = _('Click the button below to begin the install process by navigating to the Kubernetes page')
- %li
- = _('Select an existing Kubernetes cluster or create a new one')
- %li
- = _('From the Kubernetes cluster details view, install Runner from the applications list')
-
- = link_to _('Install Runner on Kubernetes'),
- project_clusters_path(@project),
- class: 'btn btn-info'
- %hr
- = render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: registration_token, type: 'specific' }
diff --git a/app/views/ci/status/_dropdown_graph_badge.html.haml b/app/views/ci/status/_dropdown_graph_badge.html.haml
index 8b0463db000..9de9143e8b1 100644
--- a/app/views/ci/status/_dropdown_graph_badge.html.haml
+++ b/app/views/ci/status/_dropdown_graph_badge.html.haml
@@ -6,12 +6,12 @@
- tooltip = "#{subject.name} - #{status.status_tooltip}"
- if status.has_details?
- = link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, html: 'true', container: 'body' } do
+ = link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do
%span{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text= subject.name
- else
- .menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' } }
+ .menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', title: tooltip, container: 'body' } }
%span{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text= subject.name
diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml
index e402801a776..f34305e94fa 100644
--- a/app/views/ci/variables/_index.html.haml
+++ b/app/views/ci/variables/_index.html.haml
@@ -9,8 +9,8 @@
= render 'ci/variables/variable_row', form_field: 'variables', variable: variable
= render 'ci/variables/variable_row', form_field: 'variables'
.prepend-top-20
- %button.btn.btn-success.js-secret-variables-save-button{ type: 'button' }
- %span.hide.js-secret-variables-save-loading-icon
+ %button.btn.btn-success.js-ci-variables-save-button{ type: 'button' }
+ %span.hide.js-ci-variables-save-loading-icon
= icon('spinner spin')
= _('Save variables')
%button.btn.btn-info.btn-inverted.prepend-left-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: "#{@variables.size == 0}" } }
diff --git a/app/views/clusters/clusters/_advanced_settings.html.haml b/app/views/clusters/clusters/_advanced_settings.html.haml
new file mode 100644
index 00000000000..7037c80aa6b
--- /dev/null
+++ b/app/views/clusters/clusters/_advanced_settings.html.haml
@@ -0,0 +1,15 @@
+- if can?(current_user, :admin_cluster, @cluster)
+ - if @cluster.managed?
+ .append-bottom-20
+ %label.append-bottom-10
+ = s_('ClusterIntegration|Google Kubernetes Engine')
+ %p
+ - link_gke = link_to(s_('ClusterIntegration|Google Kubernetes Engine'), @cluster.gke_cluster_url, target: '_blank', rel: 'noopener noreferrer')
+ = s_('ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}').html_safe % { link_gke: link_gke }
+
+ .sub-section.form-group
+ %h4.text-danger
+ = s_('ClusterIntegration|Remove Kubernetes cluster integration')
+ %p
+ = s_("ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster.")
+ = link_to(s_('ClusterIntegration|Remove integration'), clusterable.cluster_path(@cluster), method: :delete, class: 'btn btn-danger', data: { confirm: s_("ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster.")})
diff --git a/app/views/clusters/clusters/_banner.html.haml b/app/views/clusters/clusters/_banner.html.haml
new file mode 100644
index 00000000000..160c5f009a7
--- /dev/null
+++ b/app/views/clusters/clusters/_banner.html.haml
@@ -0,0 +1,9 @@
+.hidden.js-cluster-error.bs-callout.bs-callout-danger{ role: 'alert' }
+ = s_('ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine')
+ %p.js-error-reason
+
+.hidden.js-cluster-creating.bs-callout.bs-callout-info{ role: 'alert' }
+ = s_('ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine...')
+
+.hidden.js-cluster-success.bs-callout.bs-callout-success{ role: 'alert' }
+ = s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details")
diff --git a/app/views/clusters/clusters/_buttons.html.haml b/app/views/clusters/clusters/_buttons.html.haml
new file mode 100644
index 00000000000..db2e247e341
--- /dev/null
+++ b/app/views/clusters/clusters/_buttons.html.haml
@@ -0,0 +1,4 @@
+-# This partial is overridden in EE
+.nav-controls
+ %span.btn.btn-add-cluster.disabled.js-add-cluster
+ = s_("ClusterIntegration|Add Kubernetes cluster")
diff --git a/app/views/clusters/clusters/_cluster.html.haml b/app/views/clusters/clusters/_cluster.html.haml
new file mode 100644
index 00000000000..adeca013749
--- /dev/null
+++ b/app/views/clusters/clusters/_cluster.html.haml
@@ -0,0 +1,16 @@
+.card
+ .card-body.gl-responsive-table-row
+ .table-section.section-60
+ .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Kubernetes cluster")
+ .table-mobile-content
+ = link_to cluster.name, cluster.show_path
+ - unless cluster.enabled?
+ %span.badge.badge-danger Connection disabled
+ .table-section.section-25
+ .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Environment scope")
+ .table-mobile-content= cluster.environment_scope
+ .table-section.section-15.text-right
+ .table-mobile-header{ role: "rowheader" }
+ .table-mobile-content
+ %span.badge.badge-light
+ = cluster.project_type? ? s_("ClusterIntegration|Project cluster") : s_("ClusterIntegration|Group cluster")
diff --git a/app/views/clusters/clusters/_empty_state.html.haml b/app/views/clusters/clusters/_empty_state.html.haml
new file mode 100644
index 00000000000..c926ec258f0
--- /dev/null
+++ b/app/views/clusters/clusters/_empty_state.html.haml
@@ -0,0 +1,14 @@
+.row.empty-state
+ .col-12
+ .svg-content= image_tag 'illustrations/clusters_empty.svg'
+ .col-12
+ .text-content
+ %h4.text-center= s_('ClusterIntegration|Integrate Kubernetes cluster automation')
+ %p
+ = s_('ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way.')
+ = clusterable.empty_state_help_text
+ = clusterable.learn_more_link
+
+ - if clusterable.can_create_cluster?
+ .text-center
+ = link_to s_('ClusterIntegration|Add Kubernetes cluster'), clusterable.new_path, class: 'btn btn-success'
diff --git a/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
index 73b11d509d3..73b11d509d3 100644
--- a/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
+++ b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
diff --git a/app/views/clusters/clusters/_integration_form.html.haml b/app/views/clusters/clusters/_integration_form.html.haml
new file mode 100644
index 00000000000..5e451f60c9d
--- /dev/null
+++ b/app/views/clusters/clusters/_integration_form.html.haml
@@ -0,0 +1,31 @@
+= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster do |field|
+ = form_errors(@cluster)
+ .form-group
+ %h5= s_('ClusterIntegration|Integration status')
+ %label.append-bottom-0.js-cluster-enable-toggle-area
+ %button{ type: 'button',
+ class: "js-project-feature-toggle project-feature-toggle #{'is-checked' if @cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}",
+ "aria-label": s_("ClusterIntegration|Toggle Kubernetes cluster"),
+ disabled: !can?(current_user, :update_cluster, @cluster) }
+ = field.hidden_field :enabled, { class: 'js-project-feature-toggle-input'}
+ %span.toggle-icon
+ = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
+ = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
+ .form-text.text-muted= s_('ClusterIntegration|Enable or disable GitLab\'s connection to your Kubernetes cluster.')
+
+ - if has_multiple_clusters?
+ .form-group
+ %h5= s_('ClusterIntegration|Environment scope')
+ = field.text_field :environment_scope, class: 'col-md-6 form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Environment scope')
+ .form-text.text-muted= s_("ClusterIntegration|Choose which of your environments will use this cluster.")
+
+ - if can?(current_user, :update_cluster, @cluster)
+ .form-group
+ = field.submit _('Save changes'), class: 'btn btn-success'
+
+ - unless has_multiple_clusters?
+ %h5= s_('ClusterIntegration|Environment scope')
+ %p
+ %code *
+ is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster.
+ = link_to 'More information', ('https://docs.gitlab.com/ee/user/project/clusters/#setting-the-environment-scope')
diff --git a/app/views/clusters/clusters/_sidebar.html.haml b/app/views/clusters/clusters/_sidebar.html.haml
new file mode 100644
index 00000000000..6e4415c21a9
--- /dev/null
+++ b/app/views/clusters/clusters/_sidebar.html.haml
@@ -0,0 +1,6 @@
+%h4.prepend-top-0
+ = s_('ClusterIntegration|Add a Kubernetes cluster integration')
+%p
+ = clusterable.sidebar_text
+%p
+ = clusterable.learn_more_link
diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml
new file mode 100644
index 00000000000..8ed4666e79a
--- /dev/null
+++ b/app/views/clusters/clusters/gcp/_form.html.haml
@@ -0,0 +1,74 @@
+= javascript_include_tag 'https://apis.google.com/js/api.js'
+- external_link_icon = icon('external-link')
+- zones_link_url = 'https://cloud.google.com/compute/docs/regions-zones/regions-zones'
+- machine_type_link_url = 'https://cloud.google.com/compute/docs/machine-types'
+- pricing_link_url = 'https://cloud.google.com/compute/pricing#machinetype'
+- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
+- help_link_end = ' %{external_link_icon}</a>'.html_safe % { external_link_icon: external_link_icon }
+
+%p
+ - link_to_help_page = link_to(s_('ClusterIntegration|help page'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ = s_('ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration.').html_safe % { link_to_help_page: link_to_help_page}
+
+%p= link_to('Select a different Google account', @authorize_url)
+
+= form_for @gcp_cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: clusterable.create_gcp_clusters_path, as: :cluster do |field|
+ = form_errors(@gcp_cluster)
+ .form-group
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
+ = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
+ .form-group
+ = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
+ = field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?, placeholder: s_('ClusterIntegration|Environment scope')
+
+ = field.fields_for :provider_gcp, @gcp_cluster.provider_gcp do |provider_gcp_field|
+ .form-group
+ = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-bold'
+ .js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } }
+ = provider_gcp_field.hidden_field :gcp_project_id
+ .dropdown
+ %button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
+ %span.dropdown-toggle-text
+ = _('Select project')
+ = icon('chevron-down')
+ %span.form-text.text-muted &nbsp;
+
+ .form-group
+ = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone'), class: 'label-bold'
+ .js-gcp-zone-dropdown-entry-point
+ = provider_gcp_field.hidden_field :zone
+ .dropdown
+ %button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
+ %span.dropdown-toggle-text
+ = _('Select project to choose zone')
+ = icon('chevron-down')
+ %p.form-text.text-muted
+ = s_('ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: zones_link_url }, help_link_end: help_link_end }
+
+ .form-group
+ = provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-bold'
+ = provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3'
+
+ .form-group
+ = provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-bold'
+ .js-gcp-machine-type-dropdown-entry-point
+ = provider_gcp_field.hidden_field :machine_type
+ .dropdown
+ %button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
+ %span.dropdown-toggle-text
+ = _('Select project and zone to choose machine type')
+ = icon('chevron-down')
+ %p.form-text.text-muted
+ = s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
+
+ .form-group
+ .form-check
+ = provider_gcp_field.check_box :legacy_abac, { class: 'form-check-input' }, false, true
+ = provider_gcp_field.label :legacy_abac, s_('ClusterIntegration|RBAC-enabled cluster'), class: 'form-check-label label-bold'
+ .form-text.text-muted
+ = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
+ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
+ = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-core-only'), target: '_blank'
+
+ .form-group
+ = field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
diff --git a/app/views/projects/clusters/gcp/_header.html.haml b/app/views/clusters/clusters/gcp/_header.html.haml
index a2ad3cd64df..a2ad3cd64df 100644
--- a/app/views/projects/clusters/gcp/_header.html.haml
+++ b/app/views/clusters/clusters/gcp/_header.html.haml
diff --git a/app/views/clusters/clusters/gcp/_show.html.haml b/app/views/clusters/clusters/gcp/_show.html.haml
new file mode 100644
index 00000000000..e9f05eaf453
--- /dev/null
+++ b/app/views/clusters/clusters/gcp/_show.html.haml
@@ -0,0 +1,50 @@
+.form-group
+ %label.append-bottom-10{ for: 'cluster-name' }
+ = s_('ClusterIntegration|Kubernetes cluster name')
+ .input-group
+ %input.form-control.cluster-name.js-select-on-focus{ value: @cluster.name, readonly: true }
+ %span.input-group-append
+ = clipboard_button(text: @cluster.name, title: s_('ClusterIntegration|Copy Kubernetes cluster name'), class: 'input-group-text btn-default')
+
+= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster do |field|
+ = form_errors(@cluster)
+
+ = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
+ .form-group
+ = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
+ .input-group
+ = platform_kubernetes_field.text_field :api_url, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|API URL'), readonly: true
+ %span.input-group-append
+ = clipboard_button(text: @cluster.platform_kubernetes.api_url, title: s_('ClusterIntegration|Copy API URL'), class: 'input-group-text btn-default')
+
+ .form-group
+ = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
+ .input-group
+ = platform_kubernetes_field.text_area :ca_cert, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)'), readonly: true
+ %span.input-group-append.clipboard-addon
+ = clipboard_button(text: @cluster.platform_kubernetes.ca_cert, title: s_('ClusterIntegration|Copy CA Certificate'), class: 'input-group-text btn-blank')
+
+ .form-group
+ = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
+ .input-group
+ = platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token js-select-on-focus', type: 'password', placeholder: s_('ClusterIntegration|Token'), readonly: true
+ %span.input-group-append
+ %button.btn.btn-default.input-group-text.js-show-cluster-token{ type: 'button' }
+ = s_('ClusterIntegration|Show')
+ = clipboard_button(text: @cluster.platform_kubernetes.token, title: s_('ClusterIntegration|Copy Token'), class: 'btn-default')
+
+ - if @cluster.allow_user_defined_namespace?
+ .form-group
+ = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
+ = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
+
+ .form-group
+ .form-check
+ = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
+ = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster'), class: 'form-check-label label-bold'
+ .form-text.text-muted
+ = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
+ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
+
+ .form-group
+ = field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
diff --git a/app/views/clusters/clusters/index.html.haml b/app/views/clusters/clusters/index.html.haml
new file mode 100644
index 00000000000..ad6d1d856d6
--- /dev/null
+++ b/app/views/clusters/clusters/index.html.haml
@@ -0,0 +1,23 @@
+- breadcrumb_title 'Kubernetes'
+- page_title "Kubernetes Clusters"
+
+= render_gcp_signup_offer
+
+.clusters-container
+ - if @clusters.empty?
+ = render "empty_state"
+ - else
+ .top-area.adjust
+ .nav-text
+ = s_("ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project")
+ = render 'clusters/clusters/buttons'
+ .clusters-table.js-clusters-list
+ .gl-responsive-table-row.table-row-header{ role: "row" }
+ .table-section.section-60{ role: "rowheader" }
+ = s_("ClusterIntegration|Kubernetes cluster")
+ .table-section.section-30{ role: "rowheader" }
+ = s_("ClusterIntegration|Environment scope")
+ .table-section.section-10{ role: "rowheader" }
+ - @clusters.each do |cluster|
+ = render "cluster", cluster: cluster.present(current_user: current_user)
+ = paginate @clusters, theme: "gitlab"
diff --git a/app/views/clusters/clusters/new.html.haml b/app/views/clusters/clusters/new.html.haml
new file mode 100644
index 00000000000..eeeef6bd824
--- /dev/null
+++ b/app/views/clusters/clusters/new.html.haml
@@ -0,0 +1,36 @@
+- breadcrumb_title 'Kubernetes'
+- page_title _("Kubernetes Cluster")
+- active_tab = local_assigns.fetch(:active_tab, 'gcp')
+= javascript_include_tag 'https://apis.google.com/js/api.js'
+
+= render_gcp_signup_offer
+
+.row.prepend-top-default
+ .col-md-3
+ = render 'sidebar'
+ .col-md-9.js-toggle-container
+ %ul.nav-links.nav-tabs.gitlab-tabs.nav{ role: 'tablist' }
+ %li.nav-item{ role: 'presentation' }
+ %a.nav-link{ href: '#create-gcp-cluster-pane', id: 'create-gcp-cluster-tab', class: active_when(active_tab == 'gcp'), data: { toggle: 'tab' }, role: 'tab' }
+ %span Create new Cluster on GKE
+ %li.nav-item{ role: 'presentation' }
+ %a.nav-link{ href: '#add-user-cluster-pane', id: 'add-user-cluster-tab', class: active_when(active_tab == 'user'), data: { toggle: 'tab' }, role: 'tab' }
+ %span Add existing cluster
+
+ .tab-content.gitlab-tab-content
+ .tab-pane{ id: 'create-gcp-cluster-pane', class: active_when(active_tab == 'gcp'), role: 'tabpanel' }
+ = render 'clusters/clusters/gcp/header'
+ - if @valid_gcp_token
+ = render 'clusters/clusters/gcp/form'
+ - elsif @authorize_url
+ .signin-with-google
+ = link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px'), @authorize_url)
+ = _('or')
+ = link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer')
+ - else
+ - link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer')
+ = s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link }
+
+ .tab-pane{ id: 'add-user-cluster-pane', class: active_when(active_tab == 'user'), role: 'tabpanel' }
+ = render 'clusters/clusters/user/header'
+ = render 'clusters/clusters/user/form'
diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml
new file mode 100644
index 00000000000..8a7f7a5c978
--- /dev/null
+++ b/app/views/clusters/clusters/show.html.haml
@@ -0,0 +1,55 @@
+- @content_class = "limit-container-width" unless fluid_layout
+- add_to_breadcrumbs "Kubernetes Clusters", clusterable.index_path
+- breadcrumb_title @cluster.name
+- page_title _("Kubernetes Cluster")
+- manage_prometheus_path = edit_project_service_path(@cluster.project, 'prometheus') if @project
+
+- expanded = Rails.env.test?
+
+- status_path = clusterable.cluster_status_cluster_path(@cluster.id, format: :json) if can?(current_user, :admin_cluster, @cluster)
+.edit-cluster-form.js-edit-cluster-form{ data: { status_path: status_path,
+ install_helm_path: clusterable.install_applications_cluster_path(@cluster, :helm),
+ install_ingress_path: clusterable.install_applications_cluster_path(@cluster, :ingress),
+ install_prometheus_path: clusterable.install_applications_cluster_path(@cluster, :prometheus),
+ install_runner_path: clusterable.install_applications_cluster_path(@cluster, :runner),
+ install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
+ install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
+ toggle_status: @cluster.enabled? ? 'true': 'false',
+ cluster_type: @cluster.cluster_type,
+ cluster_status: @cluster.status_name,
+ cluster_status_reason: @cluster.status_reason,
+ help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
+ ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-ip-address'),
+ ingress_dns_help_path: help_page_path('topics/autodevops/quick_start_guide.md', anchor: 'point-dns-at-cluster-ip'),
+ manage_prometheus_path: manage_prometheus_path } }
+
+ .js-cluster-application-notice
+ .flash-container
+
+ %section#cluster-integration
+ %h4= @cluster.name
+ = render 'banner'
+ = render 'integration_form'
+
+ .cluster-applications-table#js-cluster-applications
+
+ %section.settings#js-cluster-details{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4= s_('ClusterIntegration|Kubernetes cluster details')
+ %button.btn.js-settings-toggle{ type: 'button' }
+ = expanded ? 'Collapse' : 'Expand'
+ %p= s_('ClusterIntegration|See and edit the details for your Kubernetes cluster')
+ .settings-content
+ - if @cluster.managed?
+ = render 'clusters/clusters/gcp/show'
+ - else
+ = render 'clusters/clusters/user/show'
+
+ %section.settings.no-animate#js-cluster-advanced-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4= _('Advanced settings')
+ %button.btn.js-settings-toggle{ type: 'button' }
+ = expanded ? 'Collapse' : 'Expand'
+ %p= s_("ClusterIntegration|Advanced options on this Kubernetes cluster's integration")
+ .settings-content
+ = render 'advanced_settings'
diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml
new file mode 100644
index 00000000000..9793c77fc2b
--- /dev/null
+++ b/app/views/clusters/clusters/user/_form.html.haml
@@ -0,0 +1,39 @@
+= form_for @user_cluster, url: clusterable.create_user_clusters_path, as: :cluster do |field|
+ = form_errors(@user_cluster)
+ .form-group
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
+ = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
+ - if has_multiple_clusters?
+ .form-group
+ = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
+ = field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope')
+
+ = field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field|
+ .form-group
+ = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold'
+ = platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
+
+ .form-group
+ = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold'
+ = platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
+
+ .form-group
+ = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-bold'
+ = platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off'
+
+ - if @user_cluster.allow_user_defined_namespace?
+ .form-group
+ = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
+ = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
+
+ .form-group
+ .form-check
+ = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input qa-rbac-checkbox' }, 'rbac', 'abac'
+ = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster'), class: 'form-check-label label-bold'
+ .form-text.text-muted
+ = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
+ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
+ = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-core-only'), target: '_blank'
+
+ .form-group
+ = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
diff --git a/app/views/projects/clusters/user/_header.html.haml b/app/views/clusters/clusters/user/_header.html.haml
index 749177fa6c1..749177fa6c1 100644
--- a/app/views/projects/clusters/user/_header.html.haml
+++ b/app/views/clusters/clusters/user/_header.html.haml
diff --git a/app/views/clusters/clusters/user/_show.html.haml b/app/views/clusters/clusters/user/_show.html.haml
new file mode 100644
index 00000000000..cac8e72edd3
--- /dev/null
+++ b/app/views/clusters/clusters/user/_show.html.haml
@@ -0,0 +1,39 @@
+= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster do |field|
+ = form_errors(@cluster)
+ .form-group
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
+ = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
+
+ = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
+ .form-group
+ = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold'
+ = platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
+
+ .form-group
+ = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold'
+ = platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
+
+ .form-group
+ = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-bold'
+ .input-group
+ = platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token', type: 'password', placeholder: s_('ClusterIntegration|Token'), autocomplete: 'off'
+ %span.input-group-append.clipboard-addon
+ .input-group-text
+ %button.js-show-cluster-token.btn-blank{ type: 'button' }
+ = s_('ClusterIntegration|Show')
+
+ - if @cluster.allow_user_defined_namespace?
+ .form-group
+ = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
+ = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
+
+ .form-group
+ .form-check
+ = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
+ = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster'), class: 'form-check-label label-bold'
+ .form-text.text-muted
+ = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
+ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
+
+ .form-group
+ = field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
diff --git a/app/views/dashboard/_activity_head.html.haml b/app/views/dashboard/_activity_head.html.haml
index 7503548fa3d..ec1a3fef435 100644
--- a/app/views/dashboard/_activity_head.html.haml
+++ b/app/views/dashboard/_activity_head.html.haml
@@ -1,3 +1,6 @@
+.page-title-holder
+ %h1.page-title= _('Activity')
+
.top-area
%ul.nav-links.nav.nav-tabs
%li{ class: active_when(params[:filter].nil?) }>
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index d8f1e50544c..8ab5dc37f34 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -1,3 +1,10 @@
+.page-title-holder
+ %h1.page-title= _('Groups')
+
+ - if current_user.can_create_group?
+ .page-title-controls
+ = link_to _("New group"), new_group_path, class: "btn btn-success"
+
.top-area
%ul.nav-links.mobile-separator.nav.nav-tabs
= nav_link(page: dashboard_groups_path) do
@@ -9,5 +16,3 @@
.nav-controls
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
- - if current_user.can_create_group?
- = link_to _("New group"), new_group_path, class: "btn btn-new"
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 9b1d9b659f9..1050945b15a 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -1,6 +1,13 @@
= content_for :flash_message do
= render 'shared/project_limit'
+.page-title-holder
+ %h1.page-title= _('Projects')
+
+ - if current_user.can_create_project?
+ .page-title-controls
+ = link_to "New project", new_project_path, class: "btn btn-success"
+
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
@@ -18,5 +25,3 @@
.nav-controls
= render 'shared/projects/search_form'
= render 'shared/projects/dropdown'
- - if current_user.can_create_project?
- = link_to "New project", new_project_path, class: "btn btn-new"
diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml
index e7e323a8683..8d99f84755a 100644
--- a/app/views/dashboard/_snippets_head.html.haml
+++ b/app/views/dashboard/_snippets_head.html.haml
@@ -1,3 +1,10 @@
+.page-title-holder
+ %h1.page-title= _('Snippets')
+
+ - if current_user
+ .page-title-controls
+ = link_to "New snippet", new_snippet_path, class: "btn btn-success", title: "New snippet"
+
.top-area
%ul.nav-links.nav.nav-tabs
= nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do
@@ -6,7 +13,3 @@
= nav_link(page: explore_snippets_path) do
= link_to explore_snippets_path, title: 'Explore snippets', data: {placement: 'right'} do
Explore snippets
-
- - if current_user
- .nav-controls.d-none.d-sm-block
- = link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet"
diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder
index d7b6fb9a4a1..6034389b897 100644
--- a/app/views/dashboard/issues.atom.builder
+++ b/app/views/dashboard/issues.atom.builder
@@ -1,3 +1,4 @@
+# rubocop: disable CodeReuse/ActiveRecord
xml.title "#{current_user.name} issues"
xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml"
xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html"
@@ -5,3 +6,4 @@ xml.id issues_dashboard_url
xml.updated @issues.first.updated_at.xmlschema if @issues.reorder(nil).any?
xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
+# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 86a21e24ac9..fdd5c19d562 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,16 +1,22 @@
- @hide_top_links = true
- page_title _("Issues")
-- @breadcrumb_link = issues_dashboard_path(assignee_id: current_user.id)
+- @breadcrumb_link = issues_dashboard_path(assignee_username: current_user.username)
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
+.page-title-holder
+ %h1.page-title= _('Issues')
+
+ - if current_user
+ .page-title-controls
+ = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues', type: :issues
+
.top-area
= render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
.nav-controls
= render 'shared/issuable/feed_buttons'
- = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues', type: :issues
-= render 'shared/issuable/filter', type: :issues
+= render 'shared/issuable/search_bar', type: :issues
- if current_user && @no_filters_set
= render 'shared/dashboard/no_filter_selected'
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 61aae31be60..77cfa1271df 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -1,13 +1,18 @@
- @hide_top_links = true
- page_title _("Merge Requests")
-- @breadcrumb_link = merge_requests_dashboard_path(assignee_id: current_user.id)
+- @breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username)
+
+.page-title-holder
+ %h1.page-title= _('Merge Requests')
+
+ - if current_user
+ .page-title-controls
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests', type: :merge_requests
.top-area
= render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set
- .nav-controls
- = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests', type: :merge_requests
-= render 'shared/issuable/filter', type: :merge_requests
+= render 'shared/issuable/search_bar', type: :merge_requests
- if current_user && @no_filters_set
= render 'shared/dashboard/no_filter_selected'
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index f66e2b40d76..ae0e38bf0ee 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -2,12 +2,18 @@
- page_title 'Milestones'
- header_title 'Milestones', dashboard_milestones_path
+.page-title-holder
+ %h1.page-title= _('Milestones')
+
+ - if current_user
+ .page-title-controls
+ = render 'shared/new_project_item_select',
+ path: 'milestones/new', label: 'New milestone',
+ include_groups: true, type: :milestones
+
.top-area
= render 'shared/milestones_filter', counts: @milestone_states
- .nav-controls
- = render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true, type: :milestones
-
.milestones
%ul.content-list
- if @milestones.blank?
diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml
index 4391624196b..6eb067da95c 100644
--- a/app/views/dashboard/snippets/index.html.haml
+++ b/app/views/dashboard/snippets/index.html.haml
@@ -5,9 +5,4 @@
= render 'dashboard/snippets_head'
= render partial: 'snippets/snippets_scope_menu', locals: { include_private: true }
-.d-block.d-sm-none
- &nbsp;
- = link_to new_snippet_path, class: "btn btn-new btn-block", title: "New snippet" do
- New snippet
-
= render partial: 'snippets/snippets', locals: { link_project: true }
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 8b3974d97f8..d2593179f17 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -2,6 +2,9 @@
- page_title "Todos"
- header_title "Todos", dashboard_todos_path
+.page-title-holder
+ %h1.page-title= _('Todos')
+
- if current_user.todos.any?
.top-area
%ul.nav-links.mobile-separator.nav.nav-tabs
diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
index 35dafb3e980..4b8ad5acd5b 100644
--- a/app/views/devise/passwords/edit.html.haml
+++ b/app/views/devise/passwords/edit.html.haml
@@ -7,12 +7,12 @@
= f.hidden_field :reset_password_token
.form-group
= f.label 'New password', for: "user_password"
- = f.password_field :password, class: "form-control top", required: true, title: 'This field is required'
+ = f.password_field :password, class: "form-control top qa-password-field", required: true, title: 'This field is required'
.form-group
= f.label 'Confirm new password', for: "user_password_confirmation"
- = f.password_field :password_confirmation, class: "form-control bottom", title: 'This field is required', required: true
+ = f.password_field :password_confirmation, class: "form-control bottom qa-password-confirmation", title: 'This field is required', required: true
.clearfix
- = f.submit "Change your password", class: "btn btn-primary"
+ = f.submit "Change your password", class: "btn btn-primary qa-change-password-button"
.clearfix.prepend-top-20
%p
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 0ee563ac066..7dacd0b1d72 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -1,10 +1,10 @@
= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive'}) do |f|
.form-group
- = f.label "Username or email", for: "user_login"
- = f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required."
+ = f.label "Username or email", for: "user_login", class: 'label-bold'
+ = f.text_field :login, class: "form-control top qa-login-field", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required."
.form-group
- = f.label :password
- = f.password_field :password, class: "form-control bottom", required: true, title: "This field is required."
+ = f.label :password, class: 'label-bold'
+ = f.password_field :password, class: "form-control bottom qa-password-field", required: true, title: "This field is required."
- if devise_mapping.rememberable?
.remember-me
%label{ for: "user_remember_me" }
@@ -17,4 +17,4 @@
= recaptcha_tags
.submit-container.move-submit-down
- = f.submit "Sign in", class: "btn btn-save"
+ = f.submit "Sign in", class: "btn btn-success qa-sign-in-button"
diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml
index 36ff42090be..131544ac0c0 100644
--- a/app/views/devise/sessions/_new_crowd.html.haml
+++ b/app/views/devise/sessions/_new_crowd.html.haml
@@ -10,4 +10,4 @@
%label{ for: "remember_me" }
= check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me
- = submit_tag "Sign in", class: "btn-save btn"
+ = submit_tag "Sign in", class: "btn-success btn"
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index 6bf7349f602..796c0cadda8 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -1,13 +1,13 @@
= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "gl-show-field-errors") do
.form-group
= label_tag :username, "#{server['label']} Username"
- = text_field_tag :username, nil, { class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true }
+ = text_field_tag :username, nil, { class: "form-control top qa-username-field", title: "This field is required.", autofocus: "autofocus", required: true }
.form-group
= label_tag :password
- = password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true }
+ = password_field_tag :password, nil, { class: "form-control bottom qa-password-field", title: "This field is required.", required: true }
- if devise_mapping.rememberable?
.remember-me
%label{ for: "remember_me" }
= check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me
- = submit_tag "Sign in", class: "btn-save btn"
+ = submit_tag "Sign in", class: "btn-success btn qa-sign-in-button"
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index ba168c4eab8..fefdf5f9531 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -11,7 +11,7 @@
= f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.'
%p.form-text.text-muted.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
.prepend-top-20
- = f.submit "Verify code", class: "btn btn-save"
+ = f.submit "Verify code", class: "btn btn-success"
- if @user.two_factor_u2f_enabled?
= render "u2f/authenticate", locals: { params: params, resource: resource, resource_name: resource_name }
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index 3723814debe..12271ee5adb 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -1,14 +1,17 @@
-.omniauth-container
- %p
- %span.light
- Sign in with &nbsp;
- - providers = enabled_button_based_providers
+.omniauth-container.prepend-top-15
+ %label.label-bold.d-block
+ Sign in with
+ - providers = enabled_button_based_providers
+ .d-flex.justify-content-between.flex-wrap
- providers.each do |provider|
- %span.light
- - has_icon = provider_has_icon?(provider)
- = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}"
- %fieldset.prepend-top-10.remember-me
- %label
- = check_box_tag :remember_me, nil, false, class: 'remember-me-checkbox'
+ - has_icon = provider_has_icon?(provider)
+ = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'btn d-flex align-items-center omniauth-btn text-left oauth-login qa-saml-login-button', id: "oauth-login-#{provider}" do
+ - if has_icon
+ = provider_image_tag(provider)
%span
- Remember me
+ = label_for_provider(provider)
+ %fieldset.remember-me
+ %label
+ = check_box_tag :remember_me, nil, false, class: 'remember-me-checkbox'
+ %span
+ Remember me
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index 5ddb3ece1cb..ec968e435cd 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -1,10 +1,10 @@
- if form_based_providers.any?
- if crowd_enabled?
- .login-box.tab-pane.active{ id: "crowd", role: 'tabpanel' }
+ .login-box.tab-pane{ id: "crowd", role: 'tabpanel', class: active_when(form_based_auth_provider_has_active_class?(:crowd)) }
.login-body
= render 'devise/sessions/new_crowd'
- @ldap_servers.each_with_index do |server, i|
- .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && !crowd_enabled?) }
+ .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && form_based_auth_provider_has_active_class?(:ldapmain)) }
.login-body
= render 'devise/sessions/new_ldap', server: server
- if password_authentication_enabled_for_web?
@@ -12,6 +12,8 @@
.login-body
= render 'devise/sessions/new_base'
+ = render_if_exists 'devise/sessions/new_smartcard'
+
- elsif password_authentication_enabled_for_web?
.login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' }
.login-body
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index ee7369f54a9..004a3528d4b 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -4,27 +4,27 @@
.devise-errors
= devise_error_messages!
.form-group
- = f.label :name, 'Full name'
- = f.text_field :name, class: "form-control top", required: true, title: "This field is required."
+ = f.label :name, 'Full name', class: 'label-bold'
+ = f.text_field :name, class: "form-control top qa-new-user-name", required: true, title: "This field is required."
.username.form-group
- = f.label :username
- = f.text_field :username, class: "form-control middle", pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: 'Please create a username with only alphanumeric characters.'
+ = f.label :username, class: 'label-bold'
+ = f.text_field :username, class: "form-control middle qa-new-user-username", pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: 'Please create a username with only alphanumeric characters.'
%p.validation-error.hide Username is already taken.
%p.validation-success.hide Username is available.
%p.validation-pending.hide Checking username availability...
.form-group
- = f.label :email
- = f.email_field :email, class: "form-control middle", required: true, title: "Please provide a valid email address."
+ = f.label :email, class: 'label-bold'
+ = f.email_field :email, class: "form-control middle qa-new-user-email", required: true, title: "Please provide a valid email address."
.form-group
- = f.label :email_confirmation
- = f.email_field :email_confirmation, class: "form-control middle", required: true, title: "Please retype the email address."
+ = f.label :email_confirmation, class: 'label-bold'
+ = f.email_field :email_confirmation, class: "form-control middle qa-new-user-email-confirmation", required: true, title: "Please retype the email address."
.form-group.append-bottom-20#password-strength
- = f.label :password
- = f.password_field :password, class: "form-control bottom", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters."
- %p.gl-field-hint Minimum length is #{@minimum_password_length} characters
+ = f.label :password, class: 'label-bold'
+ = f.password_field :password, class: "form-control bottom qa-new-user-password", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters."
+ %p.gl-field-hint.text-secondary Minimum length is #{@minimum_password_length} characters
- if Gitlab::CurrentSettings.current_application_settings.enforce_terms?
.form-group
- = check_box_tag :terms_opt_in, '1', false, required: true
+ = check_box_tag :terms_opt_in, '1', false, required: true, class: 'qa-new-user-accept-terms'
= label_tag :terms_opt_in do
- terms_link = link_to s_("I accept the|Terms of Service and Privacy Policy"), terms_path, target: "_blank"
- accept_terms_label = _("I accept the %{terms_link}") % { terms_link: terms_link }
@@ -33,9 +33,4 @@
- if Gitlab::Recaptcha.enabled?
= recaptcha_tags
.submit-container
- = f.submit "Register", class: "btn-register btn"
-.clearfix.submit-container
- %p
- %span.light Didn't receive a confirmation email?
- = succeed '.' do
- = link_to "Request a new one", new_confirmation_path(:user)
+ = f.submit "Register", class: "btn-register btn qa-new-user-register-button"
diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml
index 58c585a29ff..aee05b6c81c 100644
--- a/app/views/devise/shared/_tabs_ldap.html.haml
+++ b/app/views/devise/shared/_tabs_ldap.html.haml
@@ -1,13 +1,16 @@
%ul.nav-links.new-session-tabs.nav-tabs.nav{ class: ('custom-provider-tabs' if form_based_providers.any?) }
- if crowd_enabled?
%li.nav-item
- = link_to "Crowd", "#crowd", class: 'nav-link active', 'data-toggle' => 'tab'
+ = link_to "Crowd", "#crowd", class: "nav-link #{active_when(form_based_auth_provider_has_active_class?(:crowd))}", 'data-toggle' => 'tab'
- @ldap_servers.each_with_index do |server, i|
%li.nav-item
- = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && !crowd_enabled?)}", 'data-toggle' => 'tab'
+ = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && form_based_auth_provider_has_active_class?(:ldapmain))} qa-ldap-tab", 'data-toggle' => 'tab'
+
+ = render_if_exists 'devise/shared/tab_smartcard'
+
- if password_authentication_enabled_for_web?
%li.nav-item
- = link_to 'Standard', '#login-pane', class: 'nav-link', 'data-toggle' => 'tab'
+ = link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab'
- if allow_signup?
%li.nav-item
- = link_to 'Register', '#register-pane', class: 'nav-link', 'data-toggle' => 'tab'
+ = link_to 'Register', '#register-pane', class: 'nav-link qa-register-tab', 'data-toggle' => 'tab'
diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml
index 284d4fa1b89..8745a4e9d3e 100644
--- a/app/views/devise/shared/_tabs_normal.html.haml
+++ b/app/views/devise/shared/_tabs_normal.html.haml
@@ -1,6 +1,6 @@
%ul.nav-links.new-session-tabs.nav-tabs.nav{ role: 'tablist' }
%li.nav-item{ role: 'presentation' }
- %a.nav-link.active{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in
+ %a.nav-link.qa-sign-in-tab.active{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in
- if allow_signup?
%li.nav-item{ role: 'presentation' }
- %a.nav-link{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register
+ %a.nav-link.qa-register-tab{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register
diff --git a/app/views/discussions/_diff_discussion.html.haml b/app/views/discussions/_diff_discussion.html.haml
index 4b6c4581eb3..6b8dd156874 100644
--- a/app/views/discussions/_diff_discussion.html.haml
+++ b/app/views/discussions/_diff_discussion.html.haml
@@ -4,7 +4,6 @@
-# Text diff discussions
- expanded = local_assigns.fetch(:expanded, true)
%tr.notes_holder{ class: ('hide' unless expanded) }
- %td.notes_line{ colspan: 2 }
- %td.notes_content
+ %td.notes_content{ colspan: 3 }
.content{ class: ('hide' unless expanded) }
= render partial: "discussions/notes", collection: discussions, as: :discussion, locals: { disable_collapse_class: true }
diff --git a/app/views/discussions/_diff_with_notes.html.haml b/app/views/discussions/_diff_with_notes.html.haml
index 646e89e9bd1..44c898e0fac 100644
--- a/app/views/discussions/_diff_with_notes.html.haml
+++ b/app/views/discussions/_diff_with_notes.html.haml
@@ -1,6 +1,4 @@
- diff_file = discussion.diff_file
-- blob = discussion.blob
-- discussions = { discussion.original_line_code => [discussion] }
- diff_file_class = diff_file.text? ? 'text-file' : 'js-image-file'
- diff_data = {}
- expanded = discussion.expanded? || local_assigns.fetch(:expanded, nil)
diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml
index 1765251c93d..10187129a33 100644
--- a/app/views/discussions/_discussion.html.haml
+++ b/app/views/discussions/_discussion.html.haml
@@ -1,12 +1,12 @@
- expanded = discussion.expanded?
-%li.note.note-discussion.timeline-entry
+%li.note.note-discussion.timeline-entry.unstyled-comments
.timeline-entry-inner
- .timeline-icon
- = link_to user_path(discussion.author) do
- = image_tag avatar_icon_for_user(discussion.author), class: "avatar s40"
.timeline-content
.discussion.js-toggle-container{ data: { discussion_id: discussion.id } }
.discussion-header
+ .timeline-icon
+ = link_to user_path(discussion.author) do
+ = image_tag avatar_icon_for_user(discussion.author), class: "avatar s40"
.discussion-actions
%button.note-action-button.discussion-toggle-button.js-toggle-button{ type: "button", class: ("js-toggle-lazy-diff" unless expanded) }
- if expanded
diff --git a/app/views/discussions/_parallel_diff_discussion.html.haml b/app/views/discussions/_parallel_diff_discussion.html.haml
index 079d9083dff..2e621c4082d 100644
--- a/app/views/discussions/_parallel_diff_discussion.html.haml
+++ b/app/views/discussions/_parallel_diff_discussion.html.haml
@@ -1,21 +1,17 @@
- expanded = [*discussions_left, *discussions_right].any?(&:expanded?)
%tr.notes_holder{ class: ('hide' unless expanded) }
- if discussions_left
- %td.notes_line.old
- %td.notes_content.parallel.old
+ %td.notes_content.parallel.old{ colspan: 2 }
.content{ class: ('hide' unless discussions_left.any?(&:expanded?)) }
= render partial: "discussions/notes", collection: discussions_left, as: :discussion, line_type: 'old', locals: { disable_collapse_class: true }
- else
- %td.notes_line.old= ("")
- %td.notes_content.parallel.old
+ %td.notes_content.parallel.old{ colspan: 2 }
.content
- if discussions_right
- %td.notes_line.new
- %td.notes_content.parallel.new
+ %td.notes_content.parallel.new{ colspan: 2 }
.content{ class: ('hide' unless discussions_right.any?(&:expanded?)) }
= render partial: "discussions/notes", collection: discussions_right, as: :discussion, line_type: 'new', locals: { disable_collapse_class: true }
- else
- %td.notes_line.new= ("")
- %td.notes_content.parallel.new
+ %td.notes_content.parallel.new{ colspan: 2 }
.content
diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml
index 0bc057a8864..78904f550c7 100644
--- a/app/views/doorkeeper/applications/_form.html.haml
+++ b/app/views/doorkeeper/applications/_form.html.haml
@@ -20,4 +20,4 @@
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
.prepend-top-default
- = f.submit _('Save application'), class: "btn btn-create"
+ = f.submit _('Save application'), class: "btn btn-success"
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index ab3a1b100ce..1f5c70a6c6e 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -16,6 +16,9 @@
= _('Add new application')
= render 'form', application: @application
%hr
+ - else
+ .bs-callout.bs-callout-disabled
+ = _('Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission')
- if user_oauth_applications?
.oauth-applications
%h5
@@ -62,7 +65,7 @@
%th
%tbody
- @authorized_apps.each do |app|
- - token = app.authorized_tokens.order('created_at desc').first
+ - token = app.authorized_tokens.order('created_at desc').first # rubocop: disable CodeReuse/ActiveRecord
%tr{ id: "application_#{app.id}" }
%td= app.name
%td= token.created_at
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
index bb76ac6d5f6..cac00f9c854 100644
--- a/app/views/doorkeeper/applications/show.html.haml
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -10,18 +10,25 @@
%table.table
%tr
%td
- = _('Application Id')
+ = _('Application ID')
%td
- %code#application_id= @application.uid
+ .clipboard-group
+ .input-group
+ %input.label.label-monospace{ id: "application_id", type: "text", autocomplete: 'off', value: @application.uid, readonly: true }
+ .input-group-append
+ = clipboard_button(target: '#application_id', title: _("Copy ID to clipboard"), class: "btn btn btn-default")
%tr
%td
- = _('Secret:')
+ = _('Secret')
%td
- %code#secret= @application.secret
-
+ .clipboard-group
+ .input-group
+ %input.label.label-monospace{ id: "secret", type: "text", autocomplete: 'off', value: @application.secret, readonly: true }
+ .input-group-append
+ = clipboard_button(target: '#secret', title: _("Copy secret to clipboard"), class: "btn btn btn-default")
%tr
%td
- = _('Callback url')
+ = _('Callback URL')
%td
- @application.redirect_uri.split.each do |uri|
%div
diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
index 08f2442f025..69cc510e9c1 100644
--- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
@@ -1,4 +1,3 @@
-- submit_btn_css ||= 'btn btn-link btn-remove'
- if defined?(token)
- path = oauth_authorized_application_path(0, token_id: token)
- else
diff --git a/app/views/errors/precondition_failed.html.haml b/app/views/errors/precondition_failed.html.haml
new file mode 100644
index 00000000000..aa3869f33a9
--- /dev/null
+++ b/app/views/errors/precondition_failed.html.haml
@@ -0,0 +1,8 @@
+- content_for(:title, 'Encoding Error')
+%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
+ %h1
+ 412
+.container
+ %h3 Precondition failed
+ %hr
+ %p Page can't be loaded because of invalid parameters.
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 53a33adc14d..2fcb1d1fd2b 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -1,5 +1,5 @@
- if event.visible_to_user?(current_user)
- .event-item{ class: event_row_class(event) }
+ .event-item
.event-item-timestamp
#{time_ago_with_tooltip(event.created_at)}
@@ -11,3 +11,5 @@
= render "events/event/note", event: event
- else
= render "events/event/common", event: event
+- elsif @user&.include_private_contributions?
+ = render "events/event/private", event: event
diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml
index bc1d32607e4..c5b033b1185 100644
--- a/app/views/events/_event_push.atom.haml
+++ b/app/views/events/_event_push.atom.haml
@@ -1,7 +1,7 @@
%div{ xmlns: "http://www.w3.org/1999/xhtml" }
%p
%strong= event.author_name
- = link_to "(#{truncate_sha(event.commit_id)})", project_commit_path(event.project, event.commit_id)
+ = link_to "(#{truncate_sha(event.commit_id)})", event_feed_url(event)
%i
at
= event.created_at.to_s(:short)
diff --git a/app/views/events/_event_scope.html.haml b/app/views/events/_event_scope.html.haml
index 8f7da7d8c4f..98941722434 100644
--- a/app/views/events/_event_scope.html.haml
+++ b/app/views/events/_event_scope.html.haml
@@ -1,7 +1,7 @@
%span.event-scope
= event_preposition(event)
- if event.project
- = link_to_project event.project
+ = link_to_project(event.project)
- else
= event.project_name
diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml
index 01e72862114..96d6553a2ac 100644
--- a/app/views/events/event/_common.html.haml
+++ b/app/views/events/event/_common.html.haml
@@ -1,20 +1,19 @@
= icon_for_profile_event(event)
-.event-title
- %span.author_name= link_to_author event
- %span{ class: event.action_name }
+= event_user_info(event)
+
+.event-title.d-flex.flex-wrap
+ = inline_event_icon(event)
- if event.target
- = event.action_name
- %strong
- = link_to [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title do
- = event.target_type.titleize.downcase
- = event.target.reference_link_text
+ %span.event-type.d-inline-block.append-right-4{ class: event.action_name }
+ = event.action_name
+ %span.event-target-type.append-right-4= event.target_type.titleize.downcase
+ = link_to [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip event-target-link append-right-4', title: event.target_title do
+ = event.target.reference_link_text
+ - unless event.milestone?
+ %span.event-target-title.append-right-4= "&quot;".html_safe + event.target.title + "&quot".html_safe
- else
- = event_action_name(event)
+ %span.event-type.d-inline-block.append-right-4{ class: event.action_name }
+ = event_action_name(event)
= render "events/event_scope", event: event
-
-- if event.target.respond_to?(:title)
- .event-body
- .event-note
- = event.target.title
diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml
index d8e59be57bb..2f156603414 100644
--- a/app/views/events/event/_created_project.html.haml
+++ b/app/views/events/event/_created_project.html.haml
@@ -1,11 +1,13 @@
= icon_for_profile_event(event)
-.event-title
- %span.author_name= link_to_author event
- %span{ class: event.action_name }
+= event_user_info(event)
+
+.event-title.d-flex.flex-wrap
+ = inline_event_icon(event)
+ %span.event-type.d-inline-block.append-right-4{ class: event.action_name }
= event_action_name(event)
- if event.project
- = link_to_project event.project
+ = link_to_project(event.project)
- else
= event.project_name
diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml
index de6383e4097..fb0d2c3b8b0 100644
--- a/app/views/events/event/_note.html.haml
+++ b/app/views/events/event/_note.html.haml
@@ -1,9 +1,13 @@
= icon_for_profile_event(event)
-.event-title
- %span.author_name= link_to_author event
- = event.action_name
+= event_user_info(event)
+
+.event-title.d-flex.flex-wrap
+ = inline_event_icon(event)
+ %span.event-type.d-inline-block.append-right-4{ class: event.action_name }
+ = event.action_name
= event_note_title_html(event)
+ %span.event-target-title.append-right-4= "&quot;".html_safe + event.target.title + "&quot".html_safe
= render "events/event_scope", event: event
diff --git a/app/views/events/event/_private.html.haml b/app/views/events/event/_private.html.haml
new file mode 100644
index 00000000000..d91f30c07cb
--- /dev/null
+++ b/app/views/events/event/_private.html.haml
@@ -0,0 +1,11 @@
+.event-item
+ .event-item-timestamp
+ = time_ago_with_tooltip(event.created_at)
+
+ .system-note-image= sprite_icon('eye-slash', size: 24, css_class: 'icon')
+
+ = event_user_info(event)
+
+ .event-title.d-flex.flex-wrap
+ = inline_event_icon(event)
+ = s_('Profiles|Made a private contribution')
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index 85f2d00bde3..69914fccc48 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -2,10 +2,12 @@
= icon_for_profile_event(event)
-.event-title
- %span.author_name= link_to_author event
- %span.pushed #{event.action_name} #{event.ref_type}
- %strong
+= event_user_info(event)
+
+.event-title.d-flex.flex-wrap
+ = inline_event_icon(event)
+ %span.event-type.d-inline-block.append-right-4.pushed #{event.action_name} #{event.ref_type}
+ %span.append-right-4
- commits_link = project_commits_path(project, event.ref_name)
- should_link = event.tag? ? project.repository.tag_exists?(event.ref_name) : project.repository.branch_exists?(event.ref_name)
= link_to_if should_link, event.ref_name, commits_link, class: 'ref-name'
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 387c37b7a91..a3eafc61d0a 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -10,7 +10,7 @@
- if cookies[:explore_groups_landing_dismissed] != 'true'
.explore-groups.landing.content-block.js-explore-groups-landing.hide
- %button.dismiss-button{ type: 'button', 'aria-label' => _('Dismiss') }= icon('times')
+ %button.dismiss-button{ type: 'button', 'aria-label' => _('Dismiss') }= sprite_icon('close', size: 16)
.svg-container
= custom_icon('icon_explore_groups_splash')
.inner-content
diff --git a/app/views/groups/_archived_projects.html.haml b/app/views/groups/_archived_projects.html.haml
new file mode 100644
index 00000000000..ed79f5790f0
--- /dev/null
+++ b/app/views/groups/_archived_projects.html.haml
@@ -0,0 +1,8 @@
+#js-groups-archived-tree
+ .empty-state.text-center.hidden
+ %p= _("There are no archived projects yet")
+
+ %ul.content-list{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
+ .js-groups-list-holder
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/groups/_children.html.haml b/app/views/groups/_children.html.haml
deleted file mode 100644
index 742b40784d3..00000000000
--- a/app/views/groups/_children.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-.js-groups-list-holder
- #js-groups-tree{ data: { hide_projects: 'false', group_id: group.id, endpoint: group_children_path(group, format: :json), path: group_path(group), form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
- .loading-container.text-center
- = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml
index f7cc62c6929..ff59013ed67 100644
--- a/app/views/groups/_group_admin_settings.html.haml
+++ b/app/views/groups/_group_admin_settings.html.haml
@@ -1,5 +1,5 @@
.form-group.row
- = f.label :lfs_enabled, 'Large File Storage', class: 'col-form-label col-sm-2'
+ = f.label :lfs_enabled, 'Large File Storage', class: 'col-form-label col-sm-2 pt-0'
.col-sm-10
.form-check
= f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input'
@@ -11,13 +11,13 @@
%span.descr This setting can be overridden in each project.
.form-group.row
- = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2'
+ = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2 pt-0'
.col-sm-10
.form-check
= f.check_box :require_two_factor_authentication, class: 'form-check-input'
= f.label :require_two_factor_authentication, class: 'form-check-label' do
%strong
- Require all users in this group to setup Two-factor authentication
+ Require all users in this group to set up Two-factor authentication
= link_to icon('question-circle'), help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group')
.form-group.row
.offset-sm-2.col-sm-10
diff --git a/app/views/groups/_shared_projects.html.haml b/app/views/groups/_shared_projects.html.haml
new file mode 100644
index 00000000000..4eb8367f633
--- /dev/null
+++ b/app/views/groups/_shared_projects.html.haml
@@ -0,0 +1,8 @@
+#js-groups-shared-tree
+ .empty-state.text-center.hidden
+ %p= _("There are no projects shared with this group yet")
+
+ %ul.content-list{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
+ .js-groups-list-holder
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/groups/_subgroups_and_projects.html.haml b/app/views/groups/_subgroups_and_projects.html.haml
new file mode 100644
index 00000000000..d53c8026df8
--- /dev/null
+++ b/app/views/groups/_subgroups_and_projects.html.haml
@@ -0,0 +1,8 @@
+#js-groups-subgroups_and_projects-tree
+ .empty-state.hidden
+ = render "shared/groups/empty_state"
+
+ %ul.content-list{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
+ .js-groups-list-holder
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index cae2df4699e..869c54d89ea 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -3,32 +3,46 @@
- expanded = Rails.env.test?
-%section.settings.gs-general.no-animate#js-general-settings{ class: ('expanded' if expanded) }
+%section.settings.gs-general.no-animate#js-general-settings{ class: ('expanded') }
.settings-header
- %h4
- = _('General')
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
+ = _('Naming, visibility')
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ = _('Collapse')
%p
- = _('Update your group name, description, avatar, and other general settings.')
+ = _('Update your group name, description, avatar, and visibility.')
.settings-content
= render 'groups/settings/general'
%section.settings.gs-permissions.no-animate#js-permissions-settings{ class: ('expanded' if expanded) }
.settings-header
- %h4
- = _('Permissions')
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
+ = _('Permissions, LFS, 2FA')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _('Enable or disable certain group features and choose access levels.')
+ = _('Advanced permissions, Large File Storage and Two-Factor authentication settings.')
.settings-content
= render 'groups/settings/permissions'
+%section.settings.no-animate#js-badge-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
+ = s_('GroupSettings|Badges')
+ %button.btn.js-settings-toggle{ type: 'button' }
+ = expanded ? 'Collapse' : 'Expand'
+ %p
+ = s_('GroupSettings|Customize your group badges.')
+ = link_to s_('GroupSettings|Learn more about badges.'), help_page_path('user/project/badges')
+ .settings-content
+ = render 'shared/badges/badge_settings'
+
+= render_if_exists 'groups/templates_setting', expanded: expanded
+
%section.settings.gs-advanced.no-animate#js-advanced-settings{ class: ('expanded' if expanded) }
.settings-header
- %h4
- = _('Advanced')
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
+ = _('Path, transfer, remove')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
diff --git a/app/views/groups/group_members/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml
index aa03f8365f9..04683ec5a9a 100644
--- a/app/views/groups/group_members/_new_group_member.html.haml
+++ b/app/views/groups/group_members/_new_group_member.html.haml
@@ -19,4 +19,4 @@
On this date, the member(s) will automatically lose access to this group and all of its projects.
.col-md-2
- = f.submit 'Add to group', class: "btn btn-create btn-block"
+ = f.submit 'Add to group', class: "btn btn-success btn-block"
diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder
index 2a385b661e5..2fd96c9d158 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -1,3 +1,4 @@
+# rubocop: disable CodeReuse/ActiveRecord
xml.title "#{@group.name} issues"
xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml"
xml.link href: issues_group_url, rel: "alternate", type: "text/html"
@@ -5,3 +6,4 @@ xml.id issues_group_url
xml.updated @issues.first.updated_at.xmlschema if @issues.reorder(nil).any?
xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
+# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index db7eaff6658..4df3d831942 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -1,29 +1,36 @@
- @no_container = true
- page_title "Labels"
- can_admin_label = can?(current_user, :admin_label, @group)
-- hide_class = ''
-- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
- issuables = ['issues', 'merge requests']
+- search = params[:search]
+- subscribed = params[:subscribed]
+- labels_or_filters = @labels.exists? || search.present? || subscribed.present?
-- if can_admin_label
+- if @labels.present? && can_admin_label
- content_for(:header_content) do
.nav-controls
- = link_to _('New label'), new_group_label_path(@group), class: "btn btn-new"
+ = link_to _('New label'), new_group_label_path(@group), class: "btn btn-success"
-- if @labels.exists?
+- if labels_or_filters
#promote-label-modal
%div{ class: container_class }
- .top-area.adjust
- .nav-text
- = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence }
+ = render 'shared/labels/nav'
.labels-container.prepend-top-5
- .other-labels
- - if can_admin_label
- %h5{ class: ('hide' if hide) } Labels
- %ul.content-list.manage-labels-list.js-other-labels
- = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: false }
- = paginate @labels, theme: 'gitlab'
+ - if @labels.any?
+ .text-muted
+ = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence }
+ .other-labels
+ %h5= _('Labels')
+ %ul.content-list.manage-labels-list.js-other-labels
+ = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: false }
+ = paginate @labels, theme: 'gitlab'
+ - elsif search.present?
+ .nothing-here-block
+ = _('No labels with such name or description')
+ - elsif subscribed.present?
+ .nothing-here-block
+ = _('You do not have any subscriptions yet')
- else
= render 'shared/empty_states/labels'
diff --git a/app/views/groups/milestones/_form.html.haml b/app/views/groups/milestones/_form.html.haml
index 6d35457a0ec..39e3af5f6d2 100644
--- a/app/views/groups/milestones/_form.html.haml
+++ b/app/views/groups/milestones/_form.html.haml
@@ -19,9 +19,9 @@
.form-actions
- if @milestone.new_record?
- = f.submit 'Create milestone', class: "btn-create btn"
+ = f.submit 'Create milestone', class: "btn-success btn"
= link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel"
- else
- = f.submit 'Update milestone', class: "btn-create btn"
+ = f.submit 'Update milestone', class: "btn-success btn"
= link_to "Cancel", group_milestone_path(@group, @milestone), class: "btn btn-cancel"
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index f5f621507b8..af4fe8f2ef8 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -5,8 +5,8 @@
.nav-controls
= render 'shared/milestones_sort_dropdown'
- - if can?(current_user, :admin_milestones, @group)
- = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new"
+ - if can?(current_user, :admin_milestone, @group)
+ = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-success"
.milestones
%ul.content-list
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 53f54db1ddf..51dcc9d0cda 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -1,13 +1,12 @@
-- @breadcrumb_link = dashboard_groups_path
-- breadcrumb_title "Groups"
+- @hide_breadcrumbs = true
- @hide_top_links = true
-- page_title 'New Group'
-- header_title "Groups", dashboard_groups_path
+- page_title _('New Group')
+- header_title _("Groups"), dashboard_groups_path
+.page-title-holder
+ %h1.page-title= _('New group')
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
- %h4.prepend-top-0
- = _('New group')
%p
- group_docs_path = help_page_path('user/group/index')
- group_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: group_docs_path }
@@ -16,25 +15,30 @@
- subgroup_docs_path = help_page_path('user/group/subgroups/index')
- subgroup_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: subgroup_docs_path }
= s_('Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}.').html_safe % { subgroup_docs_link_start: subgroup_docs_link_start, subgroup_docs_link_end: '</a>'.html_safe }
+ %p
+ = _('Projects that belong to a group are prefixed with the group namespace. Existing projects may be moved into a group.')
.col-lg-9
= form_for @group, html: { class: 'group-form gl-show-field-errors' } do |f|
= form_errors(@group)
= render 'shared/group_form', f: f, autofocus: true
- .form-group.row.group-description-holder
- = f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2'
- .col-sm-10
- = render 'shared/choose_group_avatar_button', f: f
-
- = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group
+ .row
+ .form-group.group-description-holder.col-sm-12
+ = f.label :avatar, _("Group avatar"), class: 'label-bold'
+ %div
+ = render 'shared/choose_group_avatar_button', f: f
- = render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled
+ .form-group.col-sm-12
+ %label.label-bold
+ = _('Visibility level')
+ %p
+ = _('Who will be able to see this group?')
+ = link_to _('View the documentation'), help_page_path("public_access/public_access"), target: '_blank'
+ = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
- .form-group.row
- .offset-sm-2.col-sm-10
- = render 'shared/group_tips'
+ = render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled
.form-actions
- = f.submit 'Create group', class: "btn btn-create"
+ = f.submit 'Create group', class: "btn btn-success"
= link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel'
diff --git a/app/views/groups/runners/_group_runners.html.haml b/app/views/groups/runners/_group_runners.html.haml
index e6c089c3494..bcfb6d99716 100644
--- a/app/views/groups/runners/_group_runners.html.haml
+++ b/app/views/groups/runners/_group_runners.html.haml
@@ -11,7 +11,9 @@
-# https://gitlab.com/gitlab-org/gitlab-ce/issues/45894
- if can?(current_user, :admin_pipeline, @group)
= render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: @group.runners_token, type: 'group' }
+ locals: { registration_token: @group.runners_token,
+ type: 'group',
+ reset_token_url: reset_registration_token_group_settings_ci_cd_path }
- if @group.runners.empty?
%h4.underlined-title
diff --git a/app/views/groups/settings/_advanced.html.haml b/app/views/groups/settings/_advanced.html.haml
index b7c673db705..5d211d0e186 100644
--- a/app/views/groups/settings/_advanced.html.haml
+++ b/app/views/groups/settings/_advanced.html.haml
@@ -12,8 +12,8 @@
.group-root-path.input-group-prepend.has-tooltip{ title: group_path(@group), :'data-placement' => 'bottom' }
.input-group-text
%span>= root_url
- - if parent
- %strong= parent.full_path + '/'
+ - if @group.parent
+ %strong= @group.parent.full_path + '/'
= f.hidden_field :parent_id
= f.text_field :path, placeholder: 'open-source', class: 'form-control',
autofocus: local_assigns[:autofocus] || false, required: true,
@@ -23,16 +23,6 @@
= f.submit 'Change group path', class: 'btn btn-warning'
-.sub-section
- %h4.danger-title Remove group
- = form_tag(@group, method: :delete) do
- %p
- Removing group will cause all child projects and resources to be removed.
- %br
- %strong Removed group can not be restored!
-
- = button_to 'Remove group', '#', class: 'btn btn-remove js-confirm-danger', data: { 'confirm-danger-message' => remove_group_message(@group) }
-
- if supports_nested_groups?
.sub-section
%h4.warning-title Transfer group
@@ -47,3 +37,13 @@
%li You will need to update your local repositories to point to the new location.
%li If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.
= f.submit 'Transfer group', class: 'btn btn-warning'
+
+.sub-section
+ %h4.danger-title= _('Remove group')
+ = form_tag(@group, method: :delete) do
+ %p
+ = _('Removing group will cause all child projects and resources to be removed.')
+ %br
+ %strong= _('Removed group can not be restored!')
+
+ = button_to _('Remove group'), '#', class: 'btn btn-remove js-confirm-danger', data: { 'confirm-danger-message' => remove_group_message(@group) }
diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml
index 0e225fe33a5..0424ece037d 100644
--- a/app/views/groups/settings/_general.html.haml
+++ b/app/views/groups/settings/_general.html.haml
@@ -1,39 +1,33 @@
-= form_for @group, html: { multipart: true, class: 'gl-show-field-errors' }, authenticity_token: true do |f|
+= form_for @group, html: { multipart: true, class: 'gl-show-field-errors js-general-settings-form' }, authenticity_token: true do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-general-settings' }
= form_errors(@group)
%fieldset
.row
- .form-group.col-md-9
- = f.label :name, class: 'label-bold' do
- Group name
+ .form-group.col-md-5
+ = f.label :name, _('Group name'), class: 'label-bold'
= f.text_field :name, class: 'form-control'
- .form-group.col-md-3
- = f.label :id, class: 'label-bold' do
- Group ID
- = f.text_field :id, class: 'form-control', readonly: true
+ .form-group.col-md-7
+ = f.label :id, _('Group ID'), class: 'label-bold'
+ = f.text_field :id, class: 'form-control w-auto', readonly: true
- .form-group
- = f.label :description, class: 'label-bold' do
- Group description
- %span.light (optional)
- = f.text_area :description, class: 'form-control', rows: 3, maxlength: 250
+ .row.prepend-top-8
+ .form-group.col-md-9.append-bottom-0
+ = f.label :description, _('Group description (optional)'), class: 'label-bold'
+ = f.text_area :description, class: 'form-control', rows: 3, maxlength: 250
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group
- .form-group.row
- .col-sm-12
- .avatar-container.s160
- = group_icon(@group, alt: '', class: 'avatar group-avatar s160')
- %p.light
- - if @group.avatar?
- You can change the group avatar here
- - else
- You can upload a group avatar here
- = render 'shared/choose_group_avatar_button', f: f
- - if @group.avatar?
- %hr
- = link_to _('Remove avatar'), group_avatar_path(@group.to_param), data: { confirm: _('Avatar will be removed. Are you sure?')}, method: :delete, class: 'btn btn-danger btn-inverted'
+ .form-group.prepend-top-default.append-bottom-20
+ .avatar-container.s90
+ = group_icon(@group, alt: '', class: 'avatar group-avatar s90')
+ = f.label :avatar, _('Group avatar'), class: 'label-bold d-block'
+ = render 'shared/choose_group_avatar_button', f: f
+ - if @group.avatar?
+ %hr
+ = link_to _('Remove avatar'), group_avatar_path(@group.to_param), data: { confirm: _('Avatar will be removed. Are you sure?')}, method: :delete, class: 'btn btn-danger btn-inverted'
- = f.submit 'Save group', class: 'btn btn-success'
+ = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
+
+ = f.submit _('Save changes'), class: 'btn btn-success mt-4 js-dirty-submit'
diff --git a/app/views/groups/settings/_lfs.html.haml b/app/views/groups/settings/_lfs.html.haml
new file mode 100644
index 00000000000..4674d561c12
--- /dev/null
+++ b/app/views/groups/settings/_lfs.html.haml
@@ -0,0 +1,15 @@
+- docs_link_url = help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+- docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: docs_link_url }
+
+%h5= _('Large File Storage')
+
+%p= s_('Check the %{docs_link_start}documentation%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: '</a>'.html_safe }
+
+.form-group.append-bottom-default
+ .form-check
+ = f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input'
+ = f.label :lfs_enabled, class: 'form-check-label' do
+ %span
+ = _('Allow projects within this group to use Git LFS')
+ %br/
+ %span.text-muted= _('This setting can be overridden in each project.')
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index ffce2d4b14f..6b0a6e7ed99 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -1,29 +1,24 @@
-= form_for @group, html: { multipart: true, class: 'gl-show-field-errors' }, authenticity_token: true do |f|
+= form_for @group, html: { multipart: true, class: 'gl-show-field-errors js-general-permissions-form' }, authenticity_token: true do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-permissions-settings' }
= form_errors(@group)
%fieldset
- = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
+ %h5= _('Permissions')
+ .form-group
+ = render 'shared/allow_request_access', form: f
- .form-group.row
- .offset-sm-2.col-sm-10
- = render 'shared/allow_request_access', form: f
-
- .form-group.row
- %label.col-form-label.col-sm-2
- = s_('GroupSettings|Share with group lock')
- .col-sm-10
- .form-check
- = f.check_box :share_with_group_lock, disabled: !can_change_share_with_group_lock?(@group), class: 'form-check-input'
- = f.label :share_with_group_lock, class: 'form-check-label' do
- %strong
- - group_link = link_to @group.name, group_path(@group)
- = s_('GroupSettings|Prevent sharing a project within %{group} with other groups').html_safe % { group: group_link }
- %br
- %span.descr= share_with_group_lock_help_text(@group)
-
- = render 'groups/group_admin_settings', f: f
+ .form-group.append-bottom-default
+ .form-check
+ = f.check_box :share_with_group_lock, disabled: !can_change_share_with_group_lock?(@group), class: 'form-check-input'
+ = f.label :share_with_group_lock, class: 'form-check-label' do
+ %span
+ - group_link = link_to @group.name, group_path(@group)
+ = s_('GroupSettings|Prevent sharing a project within %{group} with other groups').html_safe % { group: group_link }
+ %br
+ %span.descr.text-muted= share_with_group_lock_help_text(@group)
+ = render 'groups/settings/lfs', f: f
+ = render 'groups/settings/two_factor_auth', f: f
= render_if_exists 'groups/member_lock_setting', f: f, group: @group
- = f.submit 'Save group', class: 'btn btn-success'
+ = f.submit _('Save changes'), class: 'btn btn-success prepend-top-default js-dirty-submit'
diff --git a/app/views/groups/settings/_two_factor_auth.html.haml b/app/views/groups/settings/_two_factor_auth.html.haml
new file mode 100644
index 00000000000..5d3f1cbb279
--- /dev/null
+++ b/app/views/groups/settings/_two_factor_auth.html.haml
@@ -0,0 +1,16 @@
+- docs_link_url = help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group')
+- docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: docs_link_url }
+
+%h5= _('Two-factor authentication')
+
+%p= s_('Check the %{docs_link_start}documentation%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: '</a>'.html_safe }
+
+.form-group
+ .form-check
+ = f.check_box :require_two_factor_authentication, class: 'form-check-input'
+ = f.label :require_two_factor_authentication, class: 'form-check-label' do
+ %span= _('Require all users in this group to setup Two-factor authentication')
+.form-group
+ = f.label :two_factor_grace_period, _('Time before enforced'), class: 'label-bold'
+ = f.text_field :two_factor_grace_period, class: 'form-control form-control-sm w-auto'
+ .form-text.text-muted= _('Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication')
diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml
index 647948c7dff..a5e6abdba52 100644
--- a/app/views/groups/settings/ci_cd/show.html.haml
+++ b/app/views/groups/settings/ci_cd/show.html.haml
@@ -3,7 +3,7 @@
- expanded = Rails.env.test?
-%section.settings#secret-variables.no-animate{ class: ('expanded' if expanded) }
+%section.settings#ci-variables.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Variables')
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 5a88619f769..cc294f6a931 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -7,21 +7,20 @@
= render 'groups/home_panel'
-.groups-header{ class: container_class }
- .group-nav-container
- .nav-controls.clearfix
+.groups-listing{ class: container_class, data: { endpoints: { default: group_children_path(@group, format: :json), shared: group_shared_projects_path(@group, format: :json) } } }
+ .top-area.group-nav-container
+ .group-search
= render "shared/groups/search_form"
- = render "shared/groups/dropdown", show_archive_options: true
- if can? current_user, :create_projects, @group
- new_project_label = _("New project")
- new_subgroup_label = _("New subgroup")
- if can_create_subgroups
- .btn-group.new-project-subgroup.droplab-dropdown.js-new-project-subgroup{ data: { project_path: new_project_path(namespace_id: @group.id), subgroup_path: new_group_path(parent_id: @group.id) } }
- %input.btn.btn-success.dropdown-primary.js-new-group-child{ type: "button", value: new_project_label, data: { action: "new-project" } }
- %button.btn.btn-success.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { "dropdown-trigger" => "#new-project-or-subgroup-dropdown", 'display' => 'static' } }
+ .btn-group.new-project-subgroup.droplab-dropdown.js-new-project-subgroup.qa-new-project-or-subgroup-dropdown{ data: { project_path: new_project_path(namespace_id: @group.id), subgroup_path: new_group_path(parent_id: @group.id) } }
+ %input.btn.btn-success.dropdown-primary.js-new-group-child.qa-new-in-group-button{ type: "button", value: new_project_label, data: { action: "new-project" } }
+ %button.btn.btn-success.dropdown-toggle.js-dropdown-toggle.qa-new-project-or-subgroup-dropdown-toggle{ type: "button", data: { "dropdown-trigger" => "#new-project-or-subgroup-dropdown", 'display' => 'static' } }
= icon("caret-down", class: "dropdown-btn-icon")
%ul#new-project-or-subgroup-dropdown.dropdown-menu.dropdown-menu-right{ data: { dropdown: true } }
- %li.droplab-item-selected{ role: "button", data: { value: "new-project", text: new_project_label } }
+ %li.droplab-item-selected.qa-new-project-option{ role: "button", data: { value: "new-project", text: new_project_label } }
.menu-item
.icon-container
= icon("check", class: "list-item-checkmark")
@@ -29,7 +28,7 @@
%strong= new_project_label
%span= s_("GroupsTree|Create a project in this group.")
%li.divider.droplap-item-ignore
- %li{ role: "button", data: { value: "new-subgroup", text: new_subgroup_label } }
+ %li.qa-new-subgroup-option{ role: "button", data: { value: "new-subgroup", text: new_subgroup_label } }
.menu-item
.icon-container
= icon("check", class: "list-item-checkmark")
@@ -39,7 +38,29 @@
- else
= link_to new_project_label, new_project_path(namespace_id: @group.id), class: "btn btn-success"
- - if params[:filter].blank? && !@has_children
- = render "shared/groups/empty_state"
- - else
- = render "children", children: @children, group: @group
+ .scrolling-tabs-container.inner-page-scroll-tabs
+ .fade-left= icon('angle-left')
+ .fade-right= icon('angle-right')
+ %ul.nav-links.scrolling-tabs.mobile-separator.nav.nav-tabs
+ %li.js-subgroups_and_projects-tab
+ = link_to group_path, data: { target: 'div#subgroups_and_projects', action: 'subgroups_and_projects', toggle: 'tab'} do
+ = _("Subgroups and projects")
+ %li.js-shared-tab
+ = link_to group_shared_path, data: { target: 'div#shared', action: 'shared', toggle: 'tab'} do
+ = _("Shared projects")
+ %li.js-archived-tab
+ = link_to group_archived_path, data: { target: 'div#archived', action: 'archived', toggle: 'tab'} do
+ = _("Archived projects")
+
+ .nav-controls
+ = render "shared/groups/dropdown", options_hash: subgroups_sort_options_hash
+
+ .tab-content
+ #subgroups_and_projects.tab-pane
+ = render "subgroups_and_projects", group: @group
+
+ #shared.tab-pane
+ = render "shared_projects", group: @group
+
+ #archived.tab-pane
+ = render "archived_projects", group: @group
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 7a66bac09cb..dfa5d820ce9 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -7,8 +7,7 @@
GitLab
Community Edition
- if user_signed_in?
- %span= Gitlab::VERSION
- %small= link_to Gitlab.revision, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', 'gitlab-ce', Gitlab.revision)
+ %span= link_to_version
= version_status_badge
%hr
diff --git a/app/views/help/instance_configuration/_gitlab_pages.html.haml b/app/views/help/instance_configuration/_gitlab_pages.html.haml
index bdd77730dcc..94c25edaf82 100644
--- a/app/views/help/instance_configuration/_gitlab_pages.html.haml
+++ b/app/views/help/instance_configuration/_gitlab_pages.html.haml
@@ -8,7 +8,7 @@
%p
Below are the settings for
- = succeed('.') { link_to('Gitlab Pages', gitlab_pages[:url], target: '_blank') }
+ = succeed('.') { link_to('GitLab Pages', gitlab_pages[:url], target: '_blank') }
.table-responsive
%table
%thead
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index b32b602ceb3..506f580b246 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -189,7 +189,7 @@
%li
= link_to 'Sort by date', '#'
- = link_to 'New issue', '#', class: 'btn btn-new btn-inverted'
+ = link_to 'New issue', '#', class: 'btn btn-success btn-inverted'
.lead
Only nav links without button and search
diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml
index 4cae9c51acc..d8bd37fe986 100644
--- a/app/views/ide/index.html.haml
+++ b/app/views/ide/index.html.haml
@@ -1,4 +1,4 @@
-- @body_class = 'ide'
+- @body_class = 'ide-layout'
- page_title 'IDE'
- content_for :page_specific_javascripts do
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index f0d1e837317..f4a29ed18dc 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -45,7 +45,7 @@
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend
.input-group-text /
- = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
+ = text_field_tag :path, sanitize_project_name(repo.name), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
= has_ci_cd_only_params? ? _('Connect') : _('Import')
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index a75b7aa9dd2..3b1b5e55302 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -63,7 +63,7 @@
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend
.input-group-text /
- = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
+ = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
%td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do
= _('Import')
diff --git a/app/views/import/bitbucket_server/status.html.haml b/app/views/import/bitbucket_server/status.html.haml
index 3d05a5e696f..56d4f2ba881 100644
--- a/app/views/import/bitbucket_server/status.html.haml
+++ b/app/views/import/bitbucket_server/status.html.haml
@@ -61,7 +61,7 @@
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend
.input-group-text /
- = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
+ = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
%td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do
Import
@@ -84,4 +84,6 @@
= link_to 'import flow', status_import_bitbucket_server_path
again.
+= paginate_without_count(@collection)
+
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_server_path}", import_path: "#{import_bitbucket_server_path}" } }
diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml
index b54b1af1e0c..626080c284b 100644
--- a/app/views/import/fogbugz/new.html.haml
+++ b/app/views/import/fogbugz/new.html.haml
@@ -21,4 +21,4 @@
.col-md-4
= password_field_tag :password, nil, class: 'form-control'
.form-actions
- = submit_tag _('Continue to the next step'), class: 'btn btn-create'
+ = submit_tag _('Continue to the next step'), class: 'btn btn-success'
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index ff2f989c509..8ed9dc68bb3 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -39,4 +39,4 @@
scope: :all, email_user: true, selected: user[:gitlab_user])
.form-actions
- = submit_tag _('Continue to the next step'), class: 'btn btn-create'
+ = submit_tag _('Continue to the next step'), class: 'btn btn-success'
diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml
index 2b3102f9af9..a88b04eccbb 100644
--- a/app/views/import/gitea/new.html.haml
+++ b/app/views/import/gitea/new.html.haml
@@ -19,4 +19,4 @@
.col-sm-4
= text_field_tag :personal_access_token, nil, class: 'form-control'
.form-actions
- = submit_tag _('List Your Gitea Repositories'), class: 'btn btn-create'
+ = submit_tag _('List Your Gitea Repositories'), class: 'btn btn-success'
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index 4225ee19217..5e4595d930b 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -8,8 +8,11 @@
= form_tag import_gitlab_project_path, class: 'new_project', multipart: true do
.row
+ .form-group.project-name.col-sm-12
+ = label_tag :name, _('Project name'), class: 'label-bold'
+ = text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control input-lg", autofocus: true
.form-group.col-12.col-sm-6
- = label_tag :namespace_id, 'Project path', class: 'label-bold'
+ = label_tag :namespace_id, _('Project URL'), class: 'label-bold'
.form-group
.input-group
- if current_user.can_select_namespace?
@@ -24,8 +27,8 @@
#{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-12.col-sm-6.project-path
- = label_tag :path, _('Project name'), class: 'label-bold'
- = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
+ = label_tag :path, _('Project slug'), class: 'label-bold'
+ = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, required: true
.row
.form-group.col-md-12
@@ -38,5 +41,5 @@
= file_field_tag :file, class: ''
.row
.form-actions.col-sm-12
- = submit_tag _('Import project'), class: 'btn btn-create'
+ = submit_tag _('Import project'), class: 'btn btn-success'
= link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index fd6e4726fc5..7a6ad28f0aa 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -59,4 +59,4 @@
= _('Yes, let me map Google Code users to full names or GitLab users.')
%li
%p
- = submit_tag _('Continue to the next step'), class: "btn btn-create"
+ = submit_tag _('Continue to the next step'), class: "btn btn-success"
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
index baaaf6bdc63..f523b993aa7 100644
--- a/app/views/import/google_code/new_user_map.html.haml
+++ b/app/views/import/google_code/new_user_map.html.haml
@@ -33,4 +33,4 @@
= text_area_tag :user_map, JSON.pretty_generate(@user_map), class: 'form-control', rows: 15
.form-actions
- = submit_tag _('Continue to the next step'), class: "btn btn-create"
+ = submit_tag _('Continue to the next step'), class: "btn btn-success"
diff --git a/app/views/instance_statistics/cohorts/_cohorts_table.html.haml b/app/views/instance_statistics/cohorts/_cohorts_table.html.haml
index 701a4e62b39..6a7c999bff3 100644
--- a/app/views/instance_statistics/cohorts/_cohorts_table.html.haml
+++ b/app/views/instance_statistics/cohorts/_cohorts_table.html.haml
@@ -3,7 +3,7 @@
User cohorts are shown for the last #{@cohorts[:months_included]}
months. Only users with activity are counted in the cohort total; inactive
users are counted separately.
- = link_to icon('question-circle'), help_page_path('user/admin_area/user_cohorts', anchor: 'cohorts'), title: 'About this feature', target: '_blank'
+ = link_to icon('question-circle'), help_page_path('user/instance_statistics/user_cohorts', anchor: 'cohorts'), title: 'About this feature', target: '_blank'
.table-holder
%table.table
diff --git a/app/views/instance_statistics/cohorts/index.html.haml b/app/views/instance_statistics/cohorts/index.html.haml
index 5e9a8c083af..e135bab10d8 100644
--- a/app/views/instance_statistics/cohorts/index.html.haml
+++ b/app/views/instance_statistics/cohorts/index.html.haml
@@ -1,16 +1,16 @@
-- breadcrumb_title "Cohorts"
+- breadcrumb_title _("Cohorts")
- @no_container = true
%div{ class: container_class }
- if @cohorts
= render 'cohorts_table'
- = render 'usage_ping'
- else
.bs-callout.bs-callout-warning.clearfix
%p
- User cohorts are only shown when the
- = link_to 'usage ping', help_page_path('user/admin_area/settings/usage_statistics', anchor: 'usage-ping'), target: '_blank'
- is enabled. To enable it and see user cohorts,
- visit
- = succeed '.' do
- = link_to 'application settings', admin_application_settings_path(anchor: 'usage-statistics')
+ - usage_ping_path = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'usage-ping')
+ - usage_ping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: usage_ping_path }
+ = s_('User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled.').html_safe % { usage_ping_link_start: usage_ping_link_start, usage_ping_link_end: '</a>'.html_safe }
+ - if current_user.admin?
+ - application_settings_path = admin_application_settings_path(anchor: 'usage-statistics')
+ - application_settings_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: application_settings_path }
+ = s_('To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}.').html_safe % { application_settings_link_start: application_settings_link_start, application_settings_link_end: '</a>'.html_safe }
diff --git a/app/views/instance_statistics/conversational_development_index/_disabled.html.haml b/app/views/instance_statistics/conversational_development_index/_disabled.html.haml
index 0a741b50960..b854e15d36f 100644
--- a/app/views/instance_statistics/conversational_development_index/_disabled.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/_disabled.html.haml
@@ -1,9 +1,14 @@
.container.convdev-empty
.col-sm-12.justify-content-center.text-center
= custom_icon('convdev_no_index')
- %h4 Usage ping is not enabled
- %p
- ConvDev is only shown when the
- = link_to 'usage ping', help_page_path('user/admin_area/settings/usage_statistics'), target: '_blank'
- is enabled. Enable usage ping to get an overview of how you are using GitLab from a feature perspective
- = link_to 'Enable usage ping', admin_application_settings_path(anchor: 'usage-statistics'), class: 'btn btn-primary'
+ %h4= _('Usage ping is not enabled')
+ - if !current_user.admin?
+ %p
+ - usage_ping_path = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'usage-ping')
+ - usage_ping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: usage_ping_path }
+ = s_('In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}.').html_safe % { usage_ping_link_start: usage_ping_link_start, usage_ping_link_end: '</a>'.html_safe }
+ - if current_user.admin?
+ %p
+ = _('Enable usage ping to get an overview of how you are using GitLab from a feature perspective.')
+ - if current_user.admin?
+ = link_to _('Enable usage ping'), metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), class: 'btn btn-primary'
diff --git a/app/views/instance_statistics/conversational_development_index/_no_data.html.haml b/app/views/instance_statistics/conversational_development_index/_no_data.html.haml
index d69c46194b4..dd795aee135 100644
--- a/app/views/instance_statistics/conversational_development_index/_no_data.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/_no_data.html.haml
@@ -4,4 +4,4 @@
%h4 Data is still calculating...
%p
In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index.
- = link_to 'Learn more', help_page_path('user/admin_area/monitoring/convdev'), target: '_blank'
+ = link_to 'Learn more', help_page_path('user/instance_statistics/convdev'), target: '_blank'
diff --git a/app/views/instance_statistics/conversational_development_index/index.html.haml b/app/views/instance_statistics/conversational_development_index/index.html.haml
index e3d1aa31dc2..1e7db4982d6 100644
--- a/app/views/instance_statistics/conversational_development_index/index.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/index.html.haml
@@ -1,12 +1,13 @@
- @no_container = true
-- page_title 'ConvDev Index'
+- page_title _('ConvDev Index')
+- usage_ping_enabled = Gitlab::CurrentSettings.usage_ping_enabled
.container
- - if show_callout?('convdev_intro_callout_dismissed')
+ - if usage_ping_enabled && show_callout?('convdev_intro_callout_dismissed')
= render 'callout'
.prepend-top-default
- - if !Gitlab::CurrentSettings.usage_ping_enabled
+ - if !usage_ping_enabled
= render 'disabled'
- elsif @metric.blank?
= render 'no_data'
@@ -19,7 +20,7 @@
index
%br
score
- = link_to icon('question-circle', 'aria-hidden' => 'true'), help_page_path('user/admin_area/monitoring/convdev')
+ = link_to icon('question-circle', 'aria-hidden' => 'true'), help_page_path('user/instance_statistics/convdev')
.convdev-cards.board-card-container
- @metric.cards.each do |card|
diff --git a/app/views/issues/_issues_calendar.ics.ruby b/app/views/issues/_issues_calendar.ics.ruby
index 3563635d33d..73ab8489e0c 100644
--- a/app/views/issues/_issues_calendar.ics.ruby
+++ b/app/views/issues/_issues_calendar.ics.ruby
@@ -2,6 +2,7 @@ cal = Icalendar::Calendar.new
cal.prodid = '-//GitLab//NONSGML GitLab//EN'
cal.x_wr_calname = 'GitLab Issues'
+# rubocop: disable CodeReuse/ActiveRecord
@issues.includes(project: :namespace).each do |issue|
cal.event do |event|
event.dtstart = Icalendar::Values::Date.new(issue.due_date)
@@ -11,5 +12,6 @@ cal.x_wr_calname = 'GitLab Issues'
event.transp = 'TRANSPARENT'
end
end
+# rubocop: enable CodeReuse/ActiveRecord
cal.to_ical
diff --git a/app/views/koding/index.html.haml b/app/views/koding/index.html.haml
deleted file mode 100644
index bb7f9ba7ae4..00000000000
--- a/app/views/koding/index.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-.row-content-block.second-block.center
- %p
- = icon('circle', class: 'cgreen')
- Integration is active for
- = link_to koding_project_url, target: '_blank', rel: 'noopener noreferrer' do
- #{Gitlab::CurrentSettings.koding_url}
diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml
index 8bd5708d490..2cdaa85bdaa 100644
--- a/app/views/layouts/_flash.html.haml
+++ b/app/views/layouts/_flash.html.haml
@@ -6,5 +6,5 @@
-# Don't show a flash message if the message is nil
- if value
%div{ class: "flash-#{key}" }
- %div{ class: "#{container_class} #{extra_flash_class}" }
+ %div{ class: "#{(container_class unless fluid_layout)} #{(extra_flash_class unless @no_container)} #{@content_class}" }
%span= value
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index f67a8878c80..1b2a4cd6780 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -6,11 +6,13 @@
.mobile-overlay
.alert-wrapper
= render "layouts/broadcast"
- = render 'layouts/header/read_only_banner'
+ = render "layouts/header/read_only_banner"
= yield :flash_message
+ = render "shared/ping_consent"
- unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs"
- = render "layouts/flash"
+ = render "layouts/flash", extra_flash_class: 'limit-container-width'
+ .d-flex
%div{ class: "#{(container_class unless @no_container)} #{@content_class}" }
.content{ id: "content-body" }
= yield
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 9a7a67cfa83..a86972d8cf3 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -1,7 +1,3 @@
-- if controller.controller_path =~ /^groups/ && @group.persisted?
- - label = _('This group')
-- if controller.controller_path =~ /^projects/ && @project.persisted?
- - label = _('This project')
- if @group && @group.persisted? && @group.path
- group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) }
- if @project && @project.persisted?
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 0ca34b276a7..1f4d24d996c 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -4,7 +4,7 @@
%body{ class: "#{user_application_theme} #{@body_class}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
= render "layouts/init_auto_complete" if @gfm_form
= render 'peek/bar'
- = render "layouts/header/default"
+ = render partial: "layouts/header/default", locals: { project: @project, group: @group }
= render 'layouts/page', sidebar: sidebar, nav: nav
= yield :scripts_body
diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml
index 489ef245a4d..c10be282952 100644
--- a/app/views/layouts/dashboard.html.haml
+++ b/app/views/layouts/dashboard.html.haml
@@ -1,5 +1,6 @@
- page_title _("Dashboard")
- header_title _("Dashboard"), root_path unless header_title
- sidebar "dashboard"
+- @hide_breadcrumbs = true
= render template: "layouts/application"
diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml
index 2ab9e55441b..24751ab4e06 100644
--- a/app/views/layouts/explore.html.haml
+++ b/app/views/layouts/explore.html.haml
@@ -1,5 +1,7 @@
-- page_title = _("Explore")
+- page_title _("Explore")
+- @hide_breadcrumbs = true
+
- unless current_user
- - header_title = _("Explore GitLab"), explore_root_path
+ - header_title _("Explore GitLab"), explore_root_path
= render template: "layouts/application"
diff --git a/app/views/layouts/fullscreen.html.haml b/app/views/layouts/fullscreen.html.haml
new file mode 100644
index 00000000000..e29f646ed4f
--- /dev/null
+++ b/app/views/layouts/fullscreen.html.haml
@@ -0,0 +1,14 @@
+!!! 5
+%html{ lang: I18n.locale, class: page_class }
+ = render "layouts/head"
+ %body{ class: "#{user_application_theme} #{@body_class} fullscreen-layout", data: { page: body_data_page } }
+ = render 'peek/bar'
+ = render partial: "layouts/header/default", locals: { project: @project, group: @group }
+ = render 'shared/outdated_browser'
+ .mobile-overlay
+ .alert-wrapper
+ = render "layouts/broadcast"
+ = yield :flash_message
+ = render "layouts/flash"
+ .content-wrapper{ id: "content-body", class: "d-flex flex-column align-items-stretch" }
+ = yield
diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml
index 14c5f0ce04c..9db78ec58e4 100644
--- a/app/views/layouts/group_settings.html.haml
+++ b/app/views/layouts/group_settings.html.haml
@@ -1,4 +1,4 @@
-- page_title = _("Settings")
-- nav "group"
+- page_title _("Settings")
+- nav "group"
= render template: "layouts/group"
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 9ed05d6e3d0..4f3e4031fe3 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -5,19 +5,23 @@
.user-name.bold
= current_user.name
= current_user.to_reference
+ - if current_user.status
+ .user-status.d-flex.align-items-center.prepend-top-2.has-tooltip{ title: current_user.status.message_html, data: { html: 'true', placement: 'bottom' } }
+ %span.user-status-emoji.d-flex.align-items-center
+ = emoji_icon current_user.status.emoji
+ %span.user-status-message.str-truncated
+ = current_user.status.message_html.html_safe
%li.divider
+ - if can?(current_user, :update_user_status, current_user)
+ %li
+ .js-set-status-modal-trigger{ data: { has_status: current_user.status.present? ? 'true' : 'false' } }
- if current_user_menu?(:profile)
%li
= link_to s_("CurrentUser|Profile"), current_user, class: 'profile-link', data: { user: current_user.username }
- if current_user_menu?(:settings)
%li
= link_to s_("CurrentUser|Settings"), profile_path
- - if current_user_menu?(:help)
- %li
- = link_to _("Help"), help_path
- - if current_user_menu?(:help) || current_user_menu?(:settings) || current_user_menu?(:profile)
- %li.divider
- = render 'shared/user_dropdown_contributing_link'
- if current_user_menu?(:sign_out)
+ %li.divider
%li
= link_to _("Sign out"), destroy_user_session_path, class: "sign-out-link"
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index e8d31992149..474ef25cef7 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -1,4 +1,11 @@
-%header.navbar.navbar-gitlab.qa-navbar.navbar-expand-sm
+- if project
+ - search_path_url = search_path(project_id: project.id)
+- elsif group
+ - search_path_url = search_path(group_id: group.id)
+- else
+ - search_path_url = search_path
+
+%header.navbar.navbar-gitlab.qa-navbar.navbar-expand-sm.js-navbar
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
.container-fluid
.header-content
@@ -24,29 +31,34 @@
%li.nav-item.d-none.d-sm-none.d-md-block.m-auto
= render 'layouts/search' unless current_controller?(:search)
%li.nav-item.d-inline-block.d-sm-none.d-md-none
- = link_to search_path, title: _('Search'), aria: { label: _("Search") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to search_path_url, title: _('Search'), aria: { label: _('Search') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('search', size: 16)
-
- if header_link?(:issues)
= nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do
- = link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues', aria: { label: _("Issues") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues', aria: { label: _('Issues') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('issues', size: 16)
- issues_count = assigned_issuables_count(:issues)
%span.badge.badge-pill.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count)
- if header_link?(:merge_requests)
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do
- = link_to assigned_mrs_dashboard_path, title: _('Merge requests'), class: 'dashboard-shortcuts-merge_requests', aria: { label: _("Merge requests") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to assigned_mrs_dashboard_path, title: _('Merge requests'), class: 'dashboard-shortcuts-merge_requests', aria: { label: _('Merge requests') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('git-merge', size: 16)
- merge_requests_count = assigned_issuables_count(:merge_requests)
%span.badge.badge-pill.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) }
= number_with_delimiter(merge_requests_count)
- if header_link?(:todos)
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
- = link_to dashboard_todos_path, title: _('Todos'), aria: { label: _("Todos") }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to dashboard_todos_path, title: _('Todos'), aria: { label: _('Todos') }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('todo-done', size: 16)
%span.badge.badge-pill.todos-count{ class: ('hidden' if todos_pending_count.zero?) }
= todos_count_format(todos_pending_count)
+ %li.nav-item.header-help.dropdown
+ = link_to help_path, class: 'header-help-dropdown-toggle', data: { toggle: "dropdown" } do
+ = sprite_icon('question', size: 16)
+ = sprite_icon('angle-down', css_class: 'caret-down')
+ .dropdown-menu.dropdown-menu-right
+ = render 'layouts/header/help_dropdown'
- if header_link?(:user_dropdown)
%li.nav-item.header-user.dropdown
= link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do
@@ -56,16 +68,18 @@
= render 'layouts/header/current_user_dropdown'
- if header_link?(:admin_impersonation)
%li.nav-item.impersonation
- = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: _("Stop impersonation"), aria: { label: _('Stop impersonation') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
+ = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: _('Stop impersonation'), aria: { label: _('Stop impersonation') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('user-secret')
- if header_link?(:sign_in)
- %li.nav-item
+ %li.nav-item.m-auto
%div
- sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
= link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
-
%button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
- %span.sr-only= _("Toggle navigation")
+ %span.sr-only= _('Toggle navigation')
= sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon js-navbar-toggle-right')
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
+
+- if can?(current_user, :update_user_status, current_user)
+ .js-set-status-modal-wrapper{ data: { current_emoji: current_user.status.present? ? current_user.status.emoji : '', current_message: current_user.status.present? ? current_user.status.message : '' } }
diff --git a/app/views/layouts/header/_help_dropdown.html.haml b/app/views/layouts/header/_help_dropdown.html.haml
new file mode 100644
index 00000000000..953c0e7f46c
--- /dev/null
+++ b/app/views/layouts/header/_help_dropdown.html.haml
@@ -0,0 +1,6 @@
+%ul
+ - if current_user_menu?(:help)
+ %li
+ = link_to _("Help"), help_path
+ - if current_user_menu?(:help) || current_user_menu?(:settings) || current_user_menu?(:profile)
+ = render 'shared/user_dropdown_contributing_link'
diff --git a/app/views/layouts/koding.html.haml b/app/views/layouts/koding.html.haml
deleted file mode 100644
index 45ccd38f687..00000000000
--- a/app/views/layouts/koding.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- page_title _("Koding")
-- page_description _("Koding Dashboard")
-- header_title _("Koding"), koding_path
-
-= render template: "layouts/application"
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 5e467c862ab..ea5f2b166b4 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -64,8 +64,9 @@
= link_to '#', class: 'dashboard-shortcuts-web-ide', title: _('Web IDE') do
= _('Web IDE')
- - if Gitlab::Sherlock.enabled? || can?(current_user, :read_instance_statistics)
+ - if show_separator?
%li.line-separator.d-none.d-sm-block
+ = render_if_exists 'dashboard/operations/nav_link'
- if can?(current_user, :read_instance_statistics)
= nav_link(controller: [:conversational_development_index, :cohorts]) do
= link_to instance_statistics_root_path, title: _('Instance Statistics'), aria: { label: _('Instance Statistics') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index ff25b040913..5f15ba87729 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -1,4 +1,4 @@
-.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
+.nav-sidebar.qa-admin-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
= link_to admin_root_path, title: _('Admin Overview') do
@@ -7,14 +7,14 @@
.sidebar-context-title
= _('Admin Area')
%ul.sidebar-top-level-items
- = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers), html_options: {class: 'home'}) do
+ = nav_link(controller: %w(dashboard admin admin/projects users groups jobs runners gitaly_servers), html_options: {class: 'home'}) do
= link_to admin_root_path, class: 'shortcuts-tree' do
.nav-icon-container
= sprite_icon('overview')
%span.nav-item-name
= _('Overview')
%ul.sidebar-sub-level-items
- = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers), html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: %w(dashboard admin admin/projects users groups jobs runners gitaly_servers), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_root_path do
%strong.fly-out-top-item-name
= _('Overview')
@@ -23,7 +23,7 @@
= link_to admin_root_path, title: _('Overview') do
%span
= _('Dashboard')
- = nav_link(controller: [:admin, :projects]) do
+ = nav_link(controller: [:admin, 'admin/projects']) do
= link_to admin_projects_path, title: _('Projects') do
%span
= _('Projects')
@@ -197,12 +197,56 @@
= link_to admin_application_settings_path do
.nav-icon-container
= sprite_icon('settings')
- %span.nav-item-name
+ %span.nav-item-name.qa-admin-settings-item
= _('Settings')
- %ul.sidebar-sub-level-items.is-fly-out-only
+
+ %ul.sidebar-sub-level-items.qa-admin-sidebar-submenu
= nav_link(controller: :application_settings, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_application_settings_path do
%strong.fly-out-top-item-name
= _('Settings')
+ %li.divider.fly-out-top-item
+ = nav_link(path: 'application_settings#show') do
+ = link_to admin_application_settings_path, title: _('General') do
+ %span
+ = _('General')
+ = nav_link(path: 'application_settings#integrations') do
+ = link_to integrations_admin_application_settings_path, title: _('Integrations') do
+ %span
+ = _('Integrations')
+ = nav_link(path: 'application_settings#repository') do
+ = link_to repository_admin_application_settings_path, title: _('Repository'), class: 'qa-admin-settings-repository-item' do
+ %span
+ = _('Repository')
+ - if template_exists?('admin/application_settings/templates')
+ = nav_link(path: 'application_settings#templates') do
+ = link_to templates_admin_application_settings_path, title: _('Templates') do
+ %span
+ = _('Templates')
+ = nav_link(path: 'application_settings#ci_cd') do
+ = link_to ci_cd_admin_application_settings_path, title: _('CI/CD') do
+ %span
+ = _('CI/CD')
+ = nav_link(path: 'application_settings#reporting') do
+ = link_to reporting_admin_application_settings_path, title: _('Reporting') do
+ %span
+ = _('Reporting')
+ = nav_link(path: 'application_settings#metrics_and_profiling') do
+ = link_to metrics_and_profiling_admin_application_settings_path, title: _('Metrics and profiling') do
+ %span
+ = _('Metrics and profiling')
+ = nav_link(path: 'application_settings#network') do
+ = link_to network_admin_application_settings_path, title: _('Network') do
+ %span
+ = _('Network')
+ - if template_exists?('admin/application_settings/geo')
+ = nav_link(path: 'application_settings#geo') do
+ = link_to geo_admin_application_settings_path, title: _('Geo') do
+ %span
+ = _('Geo')
+ = nav_link(path: 'application_settings#preferences') do
+ = link_to preferences_admin_application_settings_path, title: _('Preferences') do
+ %span
+ = _('Preferences')
= render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index d471dd84550..3cd5168c1f7 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -10,9 +10,9 @@
= group_icon(@group, class: "avatar s40 avatar-tile")
.sidebar-context-title
= @group.name
- %ul.sidebar-top-level-items
+ %ul.sidebar-top-level-items.qa-group-sidebar
- if group_sidebar_link?(:overview)
- = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do
+ = nav_link(path: group_overview_nav_link_paths, html_options: { class: 'home' }) do
= link_to group_path(@group) do
.nav-icon-container
= sprite_icon('home')
@@ -36,6 +36,16 @@
%span
= _('Activity')
+ = render_if_exists 'groups/sidebar/security_dashboard'
+
+ - if group_sidebar_link?(:contribution_analytics)
+ = nav_link(path: 'analytics#show') do
+ = link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do
+ %span
+ Contribution Analytics
+
+ = render_if_exists "layouts/nav/ee/epic_link", group: @group
+
- if group_sidebar_link?(:issues)
= nav_link(path: issues_sub_menu_items) do
= link_to issues_group_path(@group) do
@@ -64,6 +74,8 @@
%span
= boards_link_text
+ = render_if_exists 'layouts/nav/issues_analytics_link'
+
- if group_sidebar_link?(:labels)
= nav_link(path: 'labels#index') do
= link_to group_labels_path(@group), title: _('Labels') do
@@ -104,14 +116,27 @@
%strong.fly-out-top-item-name
= _('Members')
+ - if group_sidebar_link?(:kubernetes)
+ = nav_link(controller: [:clusters]) do
+ = link_to group_clusters_path(@group) do
+ .nav-icon-container
+ = sprite_icon('cloud-gear')
+ %span.nav-item-name
+ = _('Kubernetes')
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(controller: [:clusters], html_options: { class: "fly-out-top-item" } ) do
+ = link_to group_clusters_path(@group), title: _('Kubernetes'), class: 'shortcuts-kubernetes' do
+ %strong.fly-out-top-item-name
+ = _('Kubernetes')
+
- if group_sidebar_link?(:settings)
= nav_link(path: group_nav_link_paths) do
= link_to edit_group_path(@group) do
.nav-icon-container
= sprite_icon('settings')
- %span.nav-item-name
+ %span.nav-item-name.qa-group-settings-item
= _('Settings')
- %ul.sidebar-sub-level-items
+ %ul.sidebar-sub-level-items.qa-group-sidebar-submenu
= nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show], html_options: { class: "fly-out-top-item" } ) do
= link_to edit_group_path(@group) do
%strong.fly-out-top-item-name
@@ -122,12 +147,6 @@
%span
= _('General')
- = nav_link(controller: :badges) do
- = link_to group_settings_badges_path(@group), title: _('Project Badges') do
- %span
- = _('Project Badges')
-
-
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group), title: _('Projects') do
%span
@@ -138,4 +157,6 @@
%span
= _('CI / CD')
+ = render_if_exists "groups/ee/settings_nav"
+
= render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_instance_statistics.html.haml b/app/views/layouts/nav/sidebar/_instance_statistics.html.haml
index b8ff448f261..57180f27146 100644
--- a/app/views/layouts/nav/sidebar/_instance_statistics.html.haml
+++ b/app/views/layouts/nav/sidebar/_instance_statistics.html.haml
@@ -18,16 +18,17 @@
%strong.fly-out-top-item-name
= _('ConvDev Index')
- = nav_link(controller: :cohorts) do
- = link_to instance_statistics_cohorts_path do
- .nav-icon-container
- = sprite_icon('users')
- %span.nav-item-name
- = _('Cohorts')
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :cohorts, html_options: { class: "fly-out-top-item" } ) do
- = link_to instance_statistics_cohorts_path do
- %strong.fly-out-top-item-name
- = _('Cohorts')
+ - if Gitlab::CurrentSettings.usage_ping_enabled
+ = nav_link(controller: :cohorts) do
+ = link_to instance_statistics_cohorts_path do
+ .nav-icon-container
+ = sprite_icon('users')
+ %span.nav-item-name
+ = _('Cohorts')
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(controller: :cohorts, html_options: { class: "fly-out-top-item" } ) do
+ = link_to instance_statistics_cohorts_path do
+ %strong.fly-out-top-item-name
+ = _('Cohorts')
= render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index d65f153b451..69167edb1df 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -28,18 +28,17 @@
= link_to profile_account_path do
%strong.fly-out-top-item-name
= _('Account')
- - if Gitlab::CurrentSettings.user_oauth_applications?
- = nav_link(controller: 'oauth/applications') do
- = link_to applications_profile_path do
- .nav-icon-container
- = sprite_icon('applications')
- %span.nav-item-name
- = _('Applications')
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" } ) do
- = link_to applications_profile_path do
- %strong.fly-out-top-item-name
- = _('Applications')
+ = nav_link(controller: 'oauth/applications') do
+ = link_to applications_profile_path do
+ .nav-icon-container
+ = sprite_icon('applications')
+ %span.nav-item-name
+ = _('Applications')
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" } ) do
+ = link_to applications_profile_path do
+ %strong.fly-out-top-item-name
+ = _('Applications')
= nav_link(controller: :chat_names) do
= link_to profile_chat_names_path do
.nav-icon-container
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 34f47806205..ab15889a465 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -86,7 +86,7 @@
- if project_nav_tab? :issues
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
- = link_to project_issues_path(@project), class: 'shortcuts-issues' do
+ = link_to project_issues_path(@project), class: 'shortcuts-issues qa-issues-item' do
.nav-icon-container
= sprite_icon('issues')
%span.nav-item-name
@@ -115,7 +115,7 @@
= boards_link_text
= nav_link(controller: :labels) do
- = link_to project_labels_path(@project), title: _('Labels') do
+ = link_to project_labels_path(@project), title: _('Labels'), class: 'qa-labels-link' do
%span
= _('Labels')
@@ -158,7 +158,7 @@
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts]) do
- = link_to project_pipelines_path(@project), class: 'shortcuts-pipelines' do
+ = link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines' do
.nav-icon-container
= sprite_icon('rocket')
%span.nav-item-name
@@ -195,7 +195,7 @@
= _('Charts')
- if project_nav_tab? :operations
- = nav_link(controller: [:environments, :clusters, :user, :gcp]) do
+ = nav_link(controller: sidebar_operations_paths) do
= link_to metrics_project_environments_path(@project), class: 'shortcuts-operations' do
.nav-icon-container
= sprite_icon('cloud-gear')
@@ -203,7 +203,7 @@
= _('Operations')
%ul.sidebar-sub-level-items
- = nav_link(controller: [:environments, :clusters, :user, :gcp], html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: sidebar_operations_paths, html_options: { class: "fly-out-top-item" } ) do
= link_to metrics_project_environments_path(@project) do
%strong.fly-out-top-item-name
= _('Operations')
@@ -215,8 +215,10 @@
%span
= _('Metrics')
+ = render_if_exists "layouts/nav/sidebar/tracing_link"
+
= nav_link(controller: :environments, action: [:index, :folder, :show, :new, :edit, :create, :update, :stop, :terminal]) do
- = link_to project_environments_path(@project), title: _('Environments'), class: 'shortcuts-environments' do
+ = link_to project_environments_path(@project), title: _('Environments'), class: 'shortcuts-environments qa-operations-environments-link' do
%span
= _('Environments')
@@ -245,7 +247,7 @@
= link_to _('Auto DevOps'), help_page_path('topics/autodevops/index.md')
%span= _('uses Kubernetes clusters to deploy your code!')
%hr
- %button.btn.btn-create.btn-sm.dismiss-feature-highlight{ type: 'button' }
+ %button.btn.btn-success.btn-sm.dismiss-feature-highlight{ type: 'button' }
%span= _("Got it!")
= sprite_icon('thumb-up')
@@ -309,15 +311,10 @@
%span
= _('General')
= nav_link(controller: :project_members) do
- = link_to project_project_members_path(@project), title: _('Members') do
+ = link_to project_project_members_path(@project), title: _('Members'), class: 'qa-link-members-settings' do
%span
= _('Members')
- if can_edit
- = nav_link(controller: :badges) do
- = link_to project_settings_badges_path(@project), title: _('Badges') do
- %span
- = _('Badges')
- - if can_edit
= nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do
= link_to project_settings_integrations_path(@project), title: _('Integrations') do
%span
@@ -331,6 +328,7 @@
= link_to project_settings_ci_cd_path(@project), title: _('CI / CD') do
%span
= _('CI / CD')
+ = render_if_exists 'projects/sidebar/settings_operations'
- if @project.pages_available?
= nav_link(controller: :pages) do
= link_to project_pages_path(@project), title: _('Pages') do
diff --git a/app/views/layouts/nav_only.html.haml b/app/views/layouts/nav_only.html.haml
deleted file mode 100644
index 0811211f7b2..00000000000
--- a/app/views/layouts/nav_only.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-!!! 5
-%html{ lang: I18n.locale, class: page_class }
- = render "layouts/head"
- %body{ class: "#{user_application_theme} #{@body_class} nav-only", data: { page: body_data_page } }
- = render 'peek/bar'
- = render "layouts/header/default"
- = render 'shared/outdated_browser'
- .mobile-overlay
- .alert-wrapper
- = render "layouts/broadcast"
- = yield :flash_message
- = render "layouts/flash"
- .content{ id: "content-body" }
- = yield
diff --git a/app/views/layouts/terms.html.haml b/app/views/layouts/terms.html.haml
index 977eb350365..cdad617f006 100644
--- a/app/views/layouts/terms.html.haml
+++ b/app/views/layouts/terms.html.haml
@@ -16,19 +16,18 @@
.content{ id: "content-body" }
.card
.card-header
- .card-title
- = brand_header_logo
- - logo_text = brand_header_logo_type
- - if logo_text.present?
- %span.logo-text.prepend-left-8
- = logo_text
- - if header_link?(:user_dropdown)
- .navbar-collapse
- %ul.nav.navbar-nav
- %li.header-user.dropdown
- = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do
- = image_tag avatar_icon_for_user(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar"
- = sprite_icon('angle-down', css_class: 'caret-down')
- .dropdown-menu.dropdown-menu-right
- = render 'layouts/header/current_user_dropdown'
+ = brand_header_logo
+ - logo_text = brand_header_logo_type
+ - if logo_text.present?
+ %span.logo-text.prepend-left-8
+ = logo_text
+ - if header_link?(:user_dropdown)
+ .navbar-collapse
+ %ul.nav.navbar-nav
+ %li.header-user.dropdown
+ = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do
+ = image_tag avatar_icon_for_user(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar"
+ = sprite_icon('angle-down', css_class: 'caret-down')
+ .dropdown-menu.dropdown-menu-right
+ = render 'layouts/header/current_user_dropdown'
= yield
diff --git a/app/views/notify/_failed_builds.html.haml b/app/views/notify/_failed_builds.html.haml
new file mode 100644
index 00000000000..7c563bb016c
--- /dev/null
+++ b/app/views/notify/_failed_builds.html.haml
@@ -0,0 +1,32 @@
+%tr
+ %td{ colspan: 2, style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #333333; font-size: 14px; font-weight: 400; line-height: 1.4; padding: 0 8px 16px; text-align: center;" }
+ had
+ = failed.size
+ failed
+ #{'build'.pluralize(failed.size)}.
+%tr.table-warning
+ %td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; border: 1px solid #ededed; border-bottom: 0; border-radius: 4px 4px 0 0; overflow: hidden; background-color: #fdf4f6; color: #d22852; font-size: 14px; line-height: 1.4; text-align: center; padding: 8px 16px;" }
+ Logs may contain sensitive data. Please consider before forwarding this email.
+%tr.section
+ %td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 0 16px; border: 1px solid #ededed; border-radius: 4px; overflow: hidden; border-top: 0; border-radius: 0 0 4px 4px;" }
+ %table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width: 100%; border-collapse: collapse;" }
+ %tbody
+ - failed.each do |build|
+ %tr.build-state
+ %td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 16px 0; color: #8c8c8c; font-weight: 500; font-size: 14px;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse: collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #d22f57; font-weight: 500; font-size: 16px; vertical-align: middle; padding-right: 8px; line-height: 10px" }
+ %img{ alt: "✖", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display: block;", width: "10" }/
+ %td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #8c8c8c; font-weight: 500; font-size: 14px; vertical-align: middle;" }
+ = build.stage
+ %td{ align: "right", style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 16px 0; color: #8c8c8c; font-weight: 500; font-size: 14px;" }
+ = render "notify/links/#{build.to_partial_path}", pipeline: pipeline, build: build
+ %tr.build-log
+ - if build.has_trace?
+ %td{ colspan: "2", style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 0 0 16px;" }
+ %pre{ style: "font-family: Monaco,'Lucida Console','Courier New',Courier,monospace; background-color: #fafafa; border-radius: 4px; overflow: hidden; white-space: pre-wrap; word-break: break-all; font-size:13px; line-height: 1.4; padding: 16px 8px; color: #333333; margin: 0;" }
+ = build.trace.html(last_lines: 10).html_safe
+ - else
+ %td{ colspan: "2" }
diff --git a/app/views/notify/autodevops_disabled_email.html.haml b/app/views/notify/autodevops_disabled_email.html.haml
new file mode 100644
index 00000000000..65a2f75a3e2
--- /dev/null
+++ b/app/views/notify/autodevops_disabled_email.html.haml
@@ -0,0 +1,49 @@
+%tr.alert
+ %td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 8px 16px; border-radius: 4px; font-size: 14px; line-height: 1.3; text-align: center; overflow: hidden; background-color: #d22f57; color: #ffffff;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse: collapse; margin: 0 auto;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; vertical-align: middle; color: #ffffff; text-align: center;" }
+ Auto DevOps pipeline was disabled for #{@project.name}
+
+%tr.pre-section
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; color: #333333; font-size: 14px; font-weight: 400; line-height: 1.7; padding: 16px 8px 0;" }
+ The Auto DevOps pipeline failed for pipeline
+ %a{ href: pipeline_url(@pipeline), style: "color: #1b69b6; text-decoration:none;" }
+ = "\##{@pipeline.iid}"
+ and has been disabled for
+ %a{ href: project_url(@project), style: "color: #1b69b6; text-decoration: none;" }
+ = @project.name + "."
+ In order to use the Auto DevOps pipeline with your project, please review the
+ %a{ href: 'https://docs.gitlab.com/ee/topics/autodevops/#currently-supported-languages', style: "color:#1b69b6;text-decoration:none;" } currently supported languages,
+ adjust your project accordingly, and turn on the Auto DevOps pipeline within your
+ %a{ href: project_settings_ci_cd_url(@project), style: "color: #1b69b6; text-decoration: none;" }
+ CI/CD project settings.
+
+%tr.pre-section
+ %td{ style: 'text-align: center;border-bottom:1px solid #ededed' }
+ %a{ href: 'https://docs.gitlab.com/ee/topics/autodevops/', style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %button{ type: 'button', style: 'border-color: #dfdfdf; border-style: solid; border-width: 1px; border-radius: 4px; font-size: 14px; padding: 8px 16px; background-color:#fff; margin: 8px 0; cursor: pointer;' }
+ Learn more about Auto DevOps
+
+%tr.pre-section
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; color: #333333; font-size: 14px; font-weight: 400; line-height: 1.4; padding: 16px 8px; text-align: center;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
+ %tbody
+ %tr
+ %td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-size:14px; font-weight:500;line-height: 1.4; vertical-align: baseline;" }
+ Pipeline
+ %a{ href: pipeline_url(@pipeline), style: "color: #1b69b6; text-decoration: none;" }
+ = "\##{@pipeline.id}"
+ triggered by
+ - if @pipeline.user
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; font-size: 15px; line-height: 1.4; vertical-align: middle; padding-right: 8px; padding-left:8px", width: "24" }
+ %img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display: block; border-radius: 12px; margin: -2px 0;", width: "24", alt: "" }/
+ %td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-size: 14px; font-weight: 500; line-height: 1.4; vertical-align: baseline;" }
+ %a.muted{ href: user_url(@pipeline.user), style: "color: #333333; text-decoration: none;" }
+ = @pipeline.user.name
+ - else
+ %td{ style: "font-family: 'Menlo','Liberation Mono','Consolas','DejaVu Sans Mono','Ubuntu Mono','Courier New','andale mono','lucida console',monospace; font-size: 14px; line-height: 1.4; vertical-align: baseline; padding:0 8px;" }
+ API
+
+= render 'notify/failed_builds', pipeline: @pipeline, failed: @pipeline.statuses.latest.failed
diff --git a/app/views/notify/autodevops_disabled_email.text.erb b/app/views/notify/autodevops_disabled_email.text.erb
new file mode 100644
index 00000000000..695780c3145
--- /dev/null
+++ b/app/views/notify/autodevops_disabled_email.text.erb
@@ -0,0 +1,20 @@
+Auto DevOps pipeline was disabled for <%= @project.name %>
+
+The Auto DevOps pipeline failed for pipeline <%= @pipeline.iid %> (<%= pipeline_url(@pipeline) %>) and has been disabled for <%= @project.name %>. In order to use the Auto DevOps pipeline with your project, please review the currently supported languagues (https://docs.gitlab.com/ee/topics/autodevops/#currently-supported-languages), adjust your project accordingly, and turn on the Auto DevOps pipeline within your CI/CD project settings (<%= project_settings_ci_cd_url(@project) %>).
+
+<% if @pipeline.user -%>
+ Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by <%= @pipeline.user.name %> ( <%= user_url(@pipeline.user) %> )
+<% else -%>
+ Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by API
+<% end -%>
+<% failed = @pipeline.statuses.latest.failed -%>
+had <%= failed.size %> failed <%= 'build'.pluralize(failed.size) %>.
+
+<% failed.each do |build| -%>
+ <%= render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build %>
+ Stage: <%= build.stage %>
+ Name: <%= build.name %>
+ <% if build.has_trace? -%>
+ Trace: <%= build.trace.raw(last_lines: 10) %>
+ <% end -%>
+<% end -%>
diff --git a/app/views/notify/changed_milestone_issue_email.html.haml b/app/views/notify/changed_milestone_issue_email.html.haml
new file mode 100644
index 00000000000..7d5425fc72d
--- /dev/null
+++ b/app/views/notify/changed_milestone_issue_email.html.haml
@@ -0,0 +1,3 @@
+%p
+ Milestone changed to
+ %strong= link_to(@milestone.name, @milestone_url)
diff --git a/app/views/notify/changed_milestone_issue_email.text.erb b/app/views/notify/changed_milestone_issue_email.text.erb
new file mode 100644
index 00000000000..c5fc0b61518
--- /dev/null
+++ b/app/views/notify/changed_milestone_issue_email.text.erb
@@ -0,0 +1 @@
+Milestone changed to <%= @milestone.name %> ( <%= @milestone_url %> )
diff --git a/app/views/notify/changed_milestone_merge_request_email.html.haml b/app/views/notify/changed_milestone_merge_request_email.html.haml
new file mode 100644
index 00000000000..7d5425fc72d
--- /dev/null
+++ b/app/views/notify/changed_milestone_merge_request_email.html.haml
@@ -0,0 +1,3 @@
+%p
+ Milestone changed to
+ %strong= link_to(@milestone.name, @milestone_url)
diff --git a/app/views/notify/changed_milestone_merge_request_email.text.erb b/app/views/notify/changed_milestone_merge_request_email.text.erb
new file mode 100644
index 00000000000..c5fc0b61518
--- /dev/null
+++ b/app/views/notify/changed_milestone_merge_request_email.text.erb
@@ -0,0 +1 @@
+Milestone changed to <%= @milestone.name %> ( <%= @milestone_url %> )
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index dd6a84e503d..5acd45b74a7 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -9,7 +9,7 @@
%p
Assignee: #{@merge_request.assignee_name}
-= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request
+= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
- if @merge_request.description
%div
diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb
index d5b8f8d764f..754f4bca1cd 100644
--- a/app/views/notify/new_merge_request_email.text.erb
+++ b/app/views/notify/new_merge_request_email.text.erb
@@ -5,6 +5,6 @@ New Merge Request <%= @merge_request.to_reference %>
<%= merge_path_description(@merge_request, 'to') %>
Author: <%= @merge_request.author_name %>
Assignee: <%= @merge_request.assignee_name %>
-<%= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request %>
+<%= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter %>
<%= @merge_request.description %>
diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml
index baafaa6e3a0..86dcca4a447 100644
--- a/app/views/notify/pipeline_failed_email.html.haml
+++ b/app/views/notify/pipeline_failed_email.html.haml
@@ -107,36 +107,5 @@
- else
%td{ style: "font-family:'Menlo','Liberation Mono','Consolas','DejaVu Sans Mono','Ubuntu Mono','Courier New','andale mono','lucida console',monospace;font-size:14px;line-height:1.4;vertical-align:baseline;padding:0 5px;" }
API
-- failed = @pipeline.statuses.latest.failed
-%tr
- %td{ colspan: 2, style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:300;line-height:1.4;padding:15px 5px;text-align:center;" }
- had
- = failed.size
- failed
- #{'build'.pluralize(failed.size)}.
-%tr.table-warning
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;" }
- Logs may contain sensitive data. Please consider before forwarding this email.
-%tr.section
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;" }
- %table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;" }
- %tbody
- - failed.each do |build|
- %tr.build-state
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#d22f57;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;line-height:10px" }
- %img{ alt: "✖", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;" }
- = build.stage
- %td{ align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" }
- = render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build
- %tr.build-log
- - if build.has_trace?
- %td{ colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;" }
- %pre{ style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;" }
- = build.trace.html(last_lines: 10).html_safe
- - else
- %td{ colspan: "2" }
+
+= render 'notify/failed_builds', pipeline: @pipeline, failed: @pipeline.statuses.latest.failed
diff --git a/app/views/notify/removed_milestone_issue_email.html.haml b/app/views/notify/removed_milestone_issue_email.html.haml
new file mode 100644
index 00000000000..7e9205b6491
--- /dev/null
+++ b/app/views/notify/removed_milestone_issue_email.html.haml
@@ -0,0 +1,2 @@
+%p
+ Milestone removed
diff --git a/app/views/notify/removed_milestone_issue_email.text.erb b/app/views/notify/removed_milestone_issue_email.text.erb
new file mode 100644
index 00000000000..0b83ed7a4c5
--- /dev/null
+++ b/app/views/notify/removed_milestone_issue_email.text.erb
@@ -0,0 +1 @@
+Milestone removed
diff --git a/app/views/notify/removed_milestone_merge_request_email.html.haml b/app/views/notify/removed_milestone_merge_request_email.html.haml
new file mode 100644
index 00000000000..7e9205b6491
--- /dev/null
+++ b/app/views/notify/removed_milestone_merge_request_email.html.haml
@@ -0,0 +1,2 @@
+%p
+ Milestone removed
diff --git a/app/views/notify/removed_milestone_merge_request_email.text.erb b/app/views/notify/removed_milestone_merge_request_email.text.erb
new file mode 100644
index 00000000000..0b83ed7a4c5
--- /dev/null
+++ b/app/views/notify/removed_milestone_merge_request_email.text.erb
@@ -0,0 +1 @@
+Milestone removed
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index 04a19ab14dd..1823f191fb3 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -15,14 +15,16 @@
= f.label :email, class: 'label-bold'
= f.text_field :email, class: 'form-control'
.prepend-top-default
- = f.submit 'Add email address', class: 'btn btn-create'
+ = f.submit 'Add email address', class: 'btn btn-success'
%hr
%h4.prepend-top-0
Linked emails (#{@emails.count + 1})
.account-well.append-bottom-default
%ul
%li
- Your Primary Email will be used for avatar detection and web based operations, such as edits and merges.
+ Your Primary Email will be used for avatar detection.
+ %li
+ Your Commit Email will be used for web based operations, such as edits and merges.
%li
Your Notification Email will be used for account notifications.
%li
@@ -34,6 +36,8 @@
= render partial: 'shared/email_with_badge', locals: { email: @primary_email, verified: current_user.confirmed? }
%span.float-right
%span.badge.badge-success Primary email
+ - if @primary_email === current_user.commit_email
+ %span.badge.badge-info Commit email
- if @primary_email === current_user.public_email
%span.badge.badge-info Public email
- if @primary_email === current_user.notification_email
@@ -42,6 +46,8 @@
%li
= render partial: 'shared/email_with_badge', locals: { email: email.email, verified: email.confirmed? }
%span.float-right
+ - if email.email === current_user.commit_email
+ %span.badge.badge-info Commit email
- if email.email === current_user.public_email
%span.badge.badge-info Public email
- if email.email === current_user.notification_email
diff --git a/app/views/profiles/gpg_keys/_form.html.haml b/app/views/profiles/gpg_keys/_form.html.haml
index aa9b0aad034..6c4cb614a2b 100644
--- a/app/views/profiles/gpg_keys/_form.html.haml
+++ b/app/views/profiles/gpg_keys/_form.html.haml
@@ -7,4 +7,4 @@
= f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: "Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'."
.prepend-top-default
- = f.submit 'Add key', class: "btn btn-create"
+ = f.submit 'Add key', class: "btn btn-success"
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index 5207921d6fe..21eef08983c 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -5,10 +5,10 @@
.form-group
= f.label :key, class: 'label-bold'
%p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key.")
- = f.text_area :key, class: "form-control js-add-ssh-key-validation-input", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"')
+ = f.text_area :key, class: "form-control js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"')
.form-group
= f.label :title, class: 'label-bold'
- = f.text_field :title, class: "form-control input-lg", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
+ = f.text_field :title, class: "form-control input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
%p.form-text.text-muted= _('Name your individual key via a title')
.js-add-ssh-key-validation-warning.hide
@@ -16,7 +16,7 @@
%strong= _('Oops, are you sure?')
%p= s_("Profiles|This doesn't look like a public SSH key, are you sure you want to add it?")
- %button.btn.btn-create.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
+ %button.btn.btn-success.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
.prepend-top-default
- = f.submit s_('Profiles|Add key'), class: "btn btn-create js-add-ssh-key-validation-original-submit"
+ = f.submit s_('Profiles|Add key'), class: "btn btn-success js-add-ssh-key-validation-original-submit qa-add-key-button"
diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml
index 2ac514d3f6f..88473c7f72d 100644
--- a/app/views/profiles/keys/_key_details.html.haml
+++ b/app/views/profiles/keys/_key_details.html.haml
@@ -24,4 +24,4 @@
= @key.key
.col-md-12
.float-right
- = link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key"
+ = link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key qa-delete-key-button"
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 9c8cc9c059b..0b4b9841ea1 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -29,6 +29,6 @@
= f.label :password_confirmation, class: 'label-bold'
= f.password_field :password_confirmation, required: true, class: 'form-control'
.prepend-top-default.append-bottom-default
- = f.submit 'Save password', class: "btn btn-create append-right-10"
+ = f.submit 'Save password', class: "btn btn-success append-right-10"
- unless @user.password_automatically_set?
= link_to "I forgot my password", reset_profile_password_path, method: :put, class: "account-btn-link"
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index 2176d7f8a31..d265f3c44ba 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -1,6 +1,6 @@
- page_title "New Password"
- header_title "New Password"
-%h3.page-title Setup new password
+%h3.page-title Set up new password
%hr
= form_for @user, url: profile_password_path, method: :post do |f|
%p.slead
@@ -22,4 +22,4 @@
.col-sm-10
= f.password_field :password_confirmation, required: true, class: 'form-control'
.form-actions
- = f.submit 'Set new password', class: "btn btn-create"
+ = f.submit 'Set new password', class: "btn btn-success"
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index c10d4ea1a4d..c1e1eaff942 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -14,17 +14,7 @@
.col-lg-8
- if @new_personal_access_token
- .created-personal-access-token-container
- %h5.prepend-top-0
- Your New Personal Access Token
- .form-group
- .input-group
- = text_field_tag 'created-personal-access-token', @new_personal_access_token, readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-personal-access-token-help-block"
- %span.input-group-append
- = clipboard_button(text: @new_personal_access_token, title: "Copy personal access token to clipboard", placement: "left", class: "input-group-text btn-default btn-clipboard")
- %span#created-personal-access-token-help-block.form-text.text-muted.text-danger Make sure you save it - you won't be able to access it again.
-
- %hr
+ = render "shared/personal_access_tokens_created_container", new_token_value: @new_personal_access_token
= render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index fd6dd74e1c5..7c378633667 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -46,7 +46,7 @@
Layout width
= f.select :layout, layout_choices, {}, class: 'form-control'
.form-text.text-muted
- Choose between fixed (max. 1200px) and fluid (100%) application layout.
+ Choose between fixed (max. 1280px) and fluid (100%) application layout.
.form-group
= f.label :dashboard, class: 'label-bold' do
Default dashboard
@@ -56,6 +56,6 @@
Project overview content
= f.select :project_view, project_view_choices, {}, class: 'form-control'
.form-text.text-muted
- Choose what content you want to see on a project’s overview page
+ Choose what content you want to see on a project’s overview page.
.form-group
- = f.submit 'Save changes', class: 'btn btn-save'
+ = f.submit 'Save changes', class: 'btn btn-success'
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 6f08a294c5d..2603c558c0f 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,5 +1,6 @@
-- breadcrumb_title "Edit Profile"
+- breadcrumb_title s_("Profiles|Edit Profile")
- @content_class = "limit-container-width" unless fluid_layout
+- gravatar_link = link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host
= bootstrap_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user prepend-top-default js-quick-submit' }, authenticity_token: true do |f|
= form_errors(@user)
@@ -7,34 +8,36 @@
.row
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
- Public Avatar
+ = s_("Profiles|Public Avatar")
%p
- if @user.avatar?
- You can change your avatar here
- if gravatar_enabled?
- or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
+ = s_("Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}").html_safe % { gravatar_link: gravatar_link }
+ - else
+ = s_("Profiles|You can change your avatar here")
- else
- You can upload an avatar here
- if gravatar_enabled?
- or change it at #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
+ = s_("Profiles|You can upload your avatar here or change it at %{gravatar_link}").html_safe % { gravatar_link: gravatar_link }
+ - else
+ = s_("Profiles|You can upload your avatar here")
.col-lg-8
.clearfix.avatar-image.append-bottom-default
= link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
= image_tag avatar_icon_for_user(@user, 160), alt: '', class: 'avatar s160'
- %h5.prepend-top-0= _("Upload new avatar")
+ %h5.prepend-top-0= s_("Profiles|Upload new avatar")
.prepend-top-5.append-bottom-10
- %button.btn.js-choose-user-avatar-button{ type: 'button' }= _("Choose file...")
- %span.avatar-file-name.prepend-left-default.js-avatar-filename= _("No file chosen")
+ %button.btn.js-choose-user-avatar-button{ type: 'button' }= s_("Profiles|Choose file...")
+ %span.avatar-file-name.prepend-left-default.js-avatar-filename= s_("Profiles|No file chosen")
= f.file_field_without_bootstrap :avatar, class: 'js-user-avatar-input hidden', accept: 'image/*'
- .form-text.text-muted= _("The maximum file size allowed is 200KB.")
+ .form-text.text-muted= s_("Profiles|The maximum file size allowed is 200KB.")
- if @user.avatar?
%hr
- = link_to _('Remove avatar'), profile_avatar_path, data: { confirm: _('Avatar will be removed. Are you sure?') }, method: :delete, class: 'btn btn-danger btn-inverted'
+ = link_to s_("Profiles|Remove avatar"), profile_avatar_path, data: { confirm: s_("Profiles|Avatar will be removed. Are you sure?") }, method: :delete, class: 'btn btn-danger btn-inverted'
%hr
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0= s_("User|Current status")
+ %h4.prepend-top-0= s_("Profiles|Current status")
%p= s_("Profiles|This emoji and message will appear on your profile and throughout the interface.")
.col-lg-8
= f.fields_for :status, @user.status do |status_form|
@@ -66,62 +69,71 @@
.row
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
- Main settings
+ = s_("Profiles|Main settings")
%p
- This information will appear on your profile.
+ = s_("Profiles|This information will appear on your profile.")
- if current_user.ldap_user?
- Some options are unavailable for LDAP accounts
+ = s_("Profiles|Some options are unavailable for LDAP accounts")
.col-lg-8
.row
- if @user.read_only_attribute?(:name)
= f.text_field :name, required: true, readonly: true, wrapper: { class: 'col-md-9' },
- help: "Your name was automatically set based on your #{ attribute_provider_label(:name) } account, so people you know can recognize you."
+ help: s_("Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you.") % { provider_label: attribute_provider_label(:name) }
- else
- = f.text_field :name, required: true, wrapper: { class: 'col-md-9' }, help: "Enter your name, so people you know can recognize you."
+ = f.text_field :name, label: 'Full name', required: true, wrapper: { class: 'col-md-9' }, help: "Enter your name, so people you know can recognize you."
= f.text_field :id, readonly: true, label: 'User ID', wrapper: { class: 'col-md-3' }
- if @user.read_only_attribute?(:email)
- = f.text_field :email, required: true, readonly: true, help: "Your email address was automatically set based on your #{ attribute_provider_label(:email) } account."
+ = f.text_field :email, required: true, readonly: true, help: s_("Profiles|Your email address was automatically set based on your %{provider_label} account.") % { provider_label: attribute_provider_label(:email) }
- else
= f.text_field :email, required: true, value: (@user.email unless @user.temp_oauth_email?),
help: user_email_help_text(@user)
= f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email),
- { help: 'This email will be displayed on your public profile.', include_blank: 'Do not show on profile' },
+ { help: s_("Profiles|This email will be displayed on your public profile."), include_blank: s_("Profiles|Do not show on profile") },
+ control_class: 'select2'
+ - commit_email_docs_link = link_to s_('Profiles|Learn more'), help_page_path('user/profile/index', anchor: 'commit-email', target: '_blank')
+ = f.select :commit_email, options_for_select(commit_email_select_options(@user), selected: selected_commit_email(@user)),
+ { help: s_("Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}").html_safe % { learn_more: commit_email_docs_link } },
control_class: 'select2'
= f.select :preferred_language, Gitlab::I18n::AVAILABLE_LANGUAGES.map { |value, label| [label, value] },
- { help: 'This feature is experimental and translations are not complete yet.' },
+ { help: s_("Profiles|This feature is experimental and translations are not complete yet.") },
control_class: 'select2'
= f.text_field :skype
= f.text_field :linkedin
= f.text_field :twitter
- = f.text_field :website_url, label: 'Website'
+ = f.text_field :website_url, label: s_("Profiles|Website")
- if @user.read_only_attribute?(:location)
- = f.text_field :location, readonly: true, help: "Your location was automatically set based on your #{ attribute_provider_label(:location) } account."
+ = f.text_field :location, readonly: true, help: s_("Profiles|Your location was automatically set based on your %{provider_label} account.") % { provider_label: attribute_provider_label(:location) }
- else
= f.text_field :location
= f.text_field :organization
- = f.text_area :bio, rows: 4, maxlength: 250, help: 'Tell us about yourself in fewer than 250 characters.'
+ = f.text_area :bio, rows: 4, maxlength: 250, help: s_("Profiles|Tell us about yourself in fewer than 250 characters.")
%hr
- %h5 Private profile
- - private_profile_label = capture do
- Don't display activity-related personal information on your profile
+ %h5= ("Private profile")
+ .checkbox-icon-inline-wrapper
+ - private_profile_label = capture do
+ = s_("Profiles|Don't display activity-related personal information on your profiles")
+ = f.check_box :private_profile, label: private_profile_label
= link_to icon('question-circle'), help_page_path('user/profile/index.md', anchor: 'private-profile')
- = f.check_box :private_profile, label: private_profile_label
+ %h5= s_("Profiles|Private contributions")
+ = f.check_box :include_private_contributions, label: 'Include private contributions on my profile'
+ .help-block
+ = s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information.")
.prepend-top-default.append-bottom-default
- = f.submit 'Update profile settings', class: 'btn btn-success'
- = link_to 'Cancel', user_path(current_user), class: 'btn btn-cancel'
+ = f.submit s_("Profiles|Update profile settings"), class: 'btn btn-success'
+ = link_to _("Cancel"), user_path(current_user), class: 'btn btn-cancel'
.modal.modal-profile-crop
.modal-dialog
.modal-content
.modal-header
%h4.modal-title
- Position and size your new avatar
- %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
+ = s_("Profiles|Position and size your new avatar")
+ %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _("Close") }
%span{ "aria-hidden": true } &times;
.modal-body
.profile-crop-image-container
- %img.modal-profile-crop-image{ alt: 'Avatar cropper' }
+ %img.modal-profile-crop-image{ alt: s_("Profiles|Avatar cropper") }
.crop-controls
.btn-group
%button.btn.btn-primary{ data: { method: 'zoom', option: '0.1' } }
@@ -130,4 +142,4 @@
%span.fa.fa-search-minus
.modal-footer
%button.btn.btn-primary.js-upload-user-avatar{ type: 'button' }
- Set new profile picture
+ = s_("Profiles|Set new profile picture")
diff --git a/app/views/profiles/two_factor_auths/_codes.html.haml b/app/views/profiles/two_factor_auths/_codes.html.haml
index 93722d7b034..759d39cf5f5 100644
--- a/app/views/profiles/two_factor_auths/_codes.html.haml
+++ b/app/views/profiles/two_factor_auths/_codes.html.haml
@@ -1,5 +1,5 @@
%p.slead
- Should you ever lose your phone, each of these recovery codes can be used one
+ Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one
time each to regain access to your account. Please save them in a safe place, or you
%b will
lose access to your account.
@@ -10,4 +10,6 @@
%li
%span.monospace= code
-= link_to 'Proceed', profile_account_path, class: 'btn btn-success'
+.d-flex
+ = link_to 'Proceed', profile_account_path, class: 'btn btn-success append-right-10'
+ = link_to 'Download codes', "data:text/plain;charset=utf-8,#{CGI.escape(@codes.join("\n"))}", download: "gitlab-recovery-codes.txt", class: 'btn btn-default'
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index cd10b8758f6..94ec0cc5db8 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -6,13 +6,13 @@
.row.prepend-top-default
.col-lg-4
%h4.prepend-top-0
- Register Two-Factor Authentication App
+ Register Two-Factor Authenticator
%p
- Use an app on your mobile device to enable two-factor authentication (2FA).
+ Use an one time password authenticator on your mobile device or computer to enable two-factor authentication (2FA).
.col-lg-8
- if current_user.two_factor_otp_enabled?
%p
- You've already enabled two-factor authentication using mobile authenticator applications. In order to register a different device, you must first disable two-factor authentication.
+ You've already enabled two-factor authentication using one time password authenticators. In order to register a different device, you must first disable two-factor authentication.
%p
If you lose your recovery codes you can generate new ones, invalidating all previous codes.
%div
diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml
index b387e38c1a6..1e27c71d20d 100644
--- a/app/views/projects/_commit_button.html.haml
+++ b/app/views/projects/_commit_button.html.haml
@@ -1,5 +1,5 @@
.form-actions
- = button_tag 'Commit changes', class: 'btn commit-btn js-commit-button btn-create'
+ = button_tag 'Commit changes', class: 'btn commit-btn js-commit-button btn-success'
= link_to 'Cancel', cancel_path,
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
diff --git a/app/views/projects/_flash_messages.html.haml b/app/views/projects/_flash_messages.html.haml
index 0175b519867..7a5fff96676 100644
--- a/app/views/projects/_flash_messages.html.haml
+++ b/app/views/projects/_flash_messages.html.haml
@@ -5,3 +5,4 @@
- if current_user && can?(current_user, :download_code, project)
= render 'shared/no_ssh'
= render 'shared/no_password'
+ = render 'shared/auto_devops_implicitly_enabled_banner', project: project
diff --git a/app/views/projects/_fork_suggestion.html.haml b/app/views/projects/_fork_suggestion.html.haml
index c855bfaf067..0b616a0c1ce 100644
--- a/app/views/projects/_fork_suggestion.html.haml
+++ b/app/views/projects/_fork_suggestion.html.haml
@@ -6,6 +6,6 @@
edit
files in this project directly. Please fork this project,
make your changes there, and submit a merge request.
- = link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-new'
+ = link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-success'
%button.js-cancel-fork-suggestion-button.btn.btn-grouped{ type: 'button' }
Cancel
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index fbe88ec9618..dcef4dd5b69 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,17 +1,35 @@
- empty_repo = @project.empty_repo?
-- fork_network = @project.fork_network
-.project-home-panel.text-center{ class: ("empty-project" if empty_repo) }
+- license = @project.license_anchor_data
+.project-home-panel{ class: ("empty-project" if empty_repo) }
.limit-container-width{ class: container_class }
- .avatar-container.s70.project-avatar
- = project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile', width: 70, height: 70)
- %h1.project-title.qa-project-name
- = @project.name
- %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
- = visibility_level_icon(@project.visibility_level, fw: false)
+ .project-header.d-flex.flex-row.flex-wrap.align-items-center.append-bottom-8
+ .project-title-row.d-flex.align-items-center
+ .avatar-container.project-avatar.float-none
+ = project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s24', width: 24, height: 24)
+ %h1.project-title.d-flex.align-items-baseline.qa-project-name
+ = @project.name
+ .project-metadata.d-flex.flex-row.flex-wrap.align-items-baseline
+ .project-visibility.d-inline-flex.align-items-baseline.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
+ = visibility_level_icon(@project.visibility_level, fw: false, options: {class: 'icon'})
+ = visibility_level_label(@project.visibility_level)
+ - if license.present?
+ .project-license.d-inline-flex.align-items-baseline
+ = link_to_if license.link, sprite_icon('scale', size: 16, css_class: 'icon') + license.label, license.link, class: license.enabled ? 'btn btn-link btn-secondary-hover-link' : 'btn btn-link'
+ - if @project.tag_list.present?
+ .project-tag-list.d-inline-flex.align-items-baseline.has-tooltip{ data: { container: 'body' }, title: @project.has_extra_tags? ? @project.tag_list.join(', ') : nil }
+ = sprite_icon('tag', size: 16, css_class: 'icon')
+ = @project.tags_to_show
+ - if @project.has_extra_tags?
+ = _("+ %{count} more") % { count: @project.count_of_extra_tags_not_shown }
.project-home-desc
- if @project.description.present?
- = markdown_field(@project, :description)
+ .project-description
+ .project-description-markdown.read-more-container
+ = markdown_field(@project, :description)
+ %button.btn.btn-blank.btn-link.text-secondary.js-read-more-trigger.text-secondary.d-lg-none{ type: "button" }
+ = _("Read more")
+
- if can?(current_user, :read_project, @project)
.text-secondary.prepend-top-8
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
@@ -26,34 +44,40 @@
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_source_name(@project) }
- .project-badges.prepend-top-default.append-bottom-default
- - @project.badges.each do |badge|
- %a.append-right-8{ href: badge.rendered_link_url(@project),
- target: '_blank',
- rel: 'noopener noreferrer' }>
- %img.project-badge{ src: badge.rendered_image_url(@project),
- 'aria-hidden': true,
- alt: '' }>
-
- .project-repo-buttons
- .count-buttons
+ - if @project.badges.present?
+ .project-badges.prepend-top-default.append-bottom-default
+ - @project.badges.each do |badge|
+ %a.append-right-8{ href: badge.rendered_link_url(@project),
+ target: '_blank',
+ rel: 'noopener noreferrer' }>
+ %img.project-badge{ src: badge.rendered_image_url(@project),
+ 'aria-hidden': true,
+ alt: 'Project badge' }>
+
+ .project-repo-buttons.d-inline-flex.flex-wrap
+ .count-buttons.d-inline-flex
= render 'projects/buttons/star'
= render 'projects/buttons/fork'
- %span.d-none.d-sm-inline
- - if can?(current_user, :download_code, @project)
- .project-clone-holder
- = render "shared/clone_panel"
+ - if can?(current_user, :download_code, @project)
+ .project-clone-holder.d-inline-flex.d-sm-none
+ = render "shared/mobile_clone_panel"
- - if show_xcode_link?(@project)
- .project-action-button.project-xcode.inline
- = render "projects/buttons/xcode_link"
+ .project-clone-holder.d-none.d-sm-inline-flex
+ = render "shared/clone_panel"
- - if current_user
- - if can?(current_user, :download_code, @project)
+ - if show_xcode_link?(@project)
+ .project-action-button.project-xcode.inline
+ = render "projects/buttons/xcode_link"
+
+ - if current_user
+ - if can?(current_user, :download_code, @project)
+ .d-none.d-sm-inline-flex
= render 'projects/buttons/download', project: @project, ref: @ref
+ .d-none.d-sm-inline-flex
= render 'projects/buttons/dropdown'
- = render 'projects/buttons/koding'
+ .d-none.d-sm-inline-flex
= render 'shared/notifications/button', notification_setting: @notification_setting
+ .d-none.d-sm-inline-flex
= render 'shared/members/access_request_buttons', source: @project
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 70e1c557547..2b425f18389 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -1,4 +1,5 @@
- active_tab = local_assigns.fetch(:active_tab, 'blank')
+- track_label = local_assigns.fetch(:track_label, 'import_project')
.project-import
.form-group.import-btn-container.clearfix
@@ -7,60 +8,63 @@
.import-buttons
- if gitlab_project_import_enabled?
.import_gitlab_project.has-tooltip{ data: { container: 'body' } }
- = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
+ = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitlab_export" } do
= icon('gitlab', text: 'GitLab export')
- if github_import_enabled?
%div
- = link_to new_import_github_path, class: 'btn js-import-github' do
+ = link_to new_import_github_path, class: 'btn js-import-github', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "github" } do
= icon('github', text: 'GitHub')
- if bitbucket_import_enabled?
%div
- = link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}" do
+ = link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}",
+ data: { track_label: "#{track_label}", track_event: "click_button", track_property: "bitbucket_cloud" } do
= icon('bitbucket', text: 'Bitbucket Cloud')
- unless bitbucket_import_configured?
= render 'bitbucket_import_modal'
- if bitbucket_server_import_enabled?
%div
- = link_to status_import_bitbucket_server_path, class: "btn import_bitbucket" do
+ = link_to status_import_bitbucket_server_path, class: "btn import_bitbucket",
+ data: { track_label: "#{track_label}", track_event: "click_button", track_property: "bitbucket_server" } do
= icon('bitbucket-square', text: 'Bitbucket Server')
%div
- if gitlab_import_enabled?
%div
- = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}" do
+ = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}",
+ data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitlab_com" } do
= icon('gitlab', text: 'GitLab.com')
- unless gitlab_import_configured?
= render 'gitlab_import_modal'
- if google_code_import_enabled?
%div
- = link_to new_import_google_code_path, class: 'btn import_google_code' do
+ = link_to new_import_google_code_path, class: 'btn import_google_code', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "google_code" } do
= icon('google', text: 'Google Code')
- if fogbugz_import_enabled?
%div
- = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
+ = link_to new_import_fogbugz_path, class: 'btn import_fogbugz', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "fogbugz" } do
= icon('bug', text: 'Fogbugz')
- if gitea_import_enabled?
%div
- = link_to new_import_gitea_path, class: 'btn import_gitea' do
+ = link_to new_import_gitea_path, class: 'btn import_gitea', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitea" } do
= custom_icon('go_logo')
Gitea
- if git_import_enabled?
%div
- %button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' } }
+ %button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active', data: { toggle_open_class: 'active', track_label: "#{track_label}" , track_event: "click_button", track_property: "repo_url" } } }
= icon('git', text: 'Repo by URL')
- if manifest_import_enabled?
%div
- = link_to new_import_manifest_path, class: 'btn import_manifest' do
+ = link_to new_import_manifest_path, class: 'btn import_manifest', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "manifest_file" } do
= icon('file-text-o', text: 'Manifest file')
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
= form_for @project, html: { class: 'new_project' } do |f|
%hr
= render "shared/import_form", f: f
- = render 'new_project_fields', f: f, project_name_id: "import-url-name"
+ = render 'new_project_fields', f: f, project_name_id: "import-url-name", hide_init_with_readme: true, track_label: track_label
diff --git a/app/views/projects/_issuable_by_email.html.haml b/app/views/projects/_issuable_by_email.html.haml
index 22adf5b4008..d59191a6f87 100644
--- a/app/views/projects/_issuable_by_email.html.haml
+++ b/app/views/projects/_issuable_by_email.html.haml
@@ -19,9 +19,16 @@
= text_field_tag :issuable_email, email, class: "monospace js-select-on-focus form-control", readonly: true
.input-group-append
= clipboard_button(target: '#issuable_email', class: 'btn btn-clipboard input-group-text btn-transparent d-none d-sm-block')
+
+ - if issuable_type == 'issue'
+ - enter_title_text = _('Enter the issue title')
+ - enter_description_text = _('Enter the issue description')
+ - else
+ - enter_title_text = _('Enter the merge request title')
+ - enter_description_text = _('Enter the merge request description')
= mail_to email, class: 'btn btn-clipboard btn-transparent',
- subject: _("Enter the #{name} title"),
- body: _("Enter the #{name} description"),
+ subject: enter_title_text,
+ body: enter_description_text,
title: _('Send email'),
data: { toggle: 'tooltip', placement: 'bottom' } do
= sprite_icon('mail')
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 8fb6aa55436..0f709c65d0e 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -11,21 +11,23 @@
.md-header
%ul.nav.nav-tabs.nav-links.clearfix
%li.md-header-tab.active
- %a.js-md-write-button{ href: "#md-write-holder", tabindex: -1 }
+ %button.js-md-write-button{ tabindex: -1 }
Write
%li.md-header-tab
- %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
+ %button.js-md-preview-button{ tabindex: -1 }
Preview
%li.md-header-toolbar.active
- = markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: "Add bold text" })
- = markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: "Add italic text" })
- = markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
- = markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" })
- = markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
- = markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
- = markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
- %button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: "Go full screen", data: { container: "body" } }
+ = markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: s_("MarkdownToolbar|Add bold text") })
+ = markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: s_("MarkdownToolbar|Add italic text") })
+ = markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: s_("MarkdownToolbar|Insert a quote") })
+ = markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: s_("MarkdownToolbar|Insert code") })
+ = markdown_toolbar_button({ icon: "link", data: { "md-tag" => "[{text}](url)", "md-select" => "url" }, title: s_("MarkdownToolbar|Add a link") })
+ = markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a bullet list") })
+ = markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a numbered list") })
+ = markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a task list") })
+ = markdown_toolbar_button({ icon: "table", data: { "md-tag" => "| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a table") })
+ %button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: s_("MarkdownToolbar|Go full screen"), data: { container: "body" } }
= sprite_icon("screen-full")
.md-write-holder
diff --git a/app/views/projects/_merge_request_merge_method_settings.html.haml b/app/views/projects/_merge_request_merge_method_settings.html.haml
index 540e996e4d8..935581643cd 100644
--- a/app/views/projects/_merge_request_merge_method_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_method_settings.html.haml
@@ -1,5 +1,4 @@
- form = local_assigns.fetch(:form)
-- project = local_assigns.fetch(:project)
.form-group
= label_tag :merge_method_merge, class: 'label-bold' do
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index ad8c7911fad..ba7d3154326 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -1,12 +1,17 @@
- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
- ci_cd_only = local_assigns.fetch(:ci_cd_only, false)
+- hide_init_with_readme = local_assigns.fetch(:hide_init_with_readme, false)
+- track_label = local_assigns.fetch(:track_label, 'blank_project')
.row{ id: project_name_id }
= f.hidden_field :ci_cd_only, value: ci_cd_only
+ .form-group.project-name.col-sm-12
+ = f.label :name, class: 'label-bold' do
+ %span= _("Project name")
+ = f.text_field :name, placeholder: "My awesome project", class: "form-control input-lg", autofocus: true, data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_name", track_value: "" }
.form-group.project-path.col-sm-6
= f.label :namespace_id, class: 'label-bold' do
- %span
- Project path
+ %span= s_("Project URL")
.input-group
- if current_user.can_select_namespace?
.input-group-prepend.has-tooltip{ title: root_url }
@@ -18,7 +23,7 @@
display_path: true,
extra_group: namespace_id),
{},
- { class: 'select2 js-select-namespace qa-project-namespace-select', tabindex: 1})
+ { class: 'select2 js-select-namespace qa-project-namespace-select', tabindex: 1, data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_path", track_value: "" }})
- else
.input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
@@ -27,34 +32,34 @@
= f.hidden_field :namespace_id, value: current_user.namespace_id
.form-group.project-path.col-sm-6
= f.label :path, class: 'label-bold' do
- %span
- Project name
- = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
+ %span= _("Project slug")
+ = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", required: true
- if current_user.can_create_group?
.form-text.text-muted
Want to house several dependent projects under the same namespace?
- = link_to "Create a group", new_group_path
+ = link_to "Create a group.", new_group_path
.form-group
= f.label :description, class: 'label-bold' do
Project description
%span (optional)
- = f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250
+ = f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250, data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_description", track_value: "" }
= f.label :visibility_level, class: 'label-bold' do
Visibility Level
= link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer'
= render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false
-.form-group.row.initialize-with-readme-setting
- %div{ :class => "col-sm-12" }
- .form-check
- = check_box_tag 'project[initialize_with_readme]', '1', false, class: 'form-check-input'
- = label_tag 'project[initialize_with_readme]', class: 'form-check-label' do
- .option-title
- %strong Initialize repository with a README
- .option-description
- Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.
+- if !hide_init_with_readme
+ .form-group.row.initialize-with-readme-setting
+ %div{ :class => "col-sm-12" }
+ .form-check
+ = check_box_tag 'project[initialize_with_readme]', '1', false, class: 'form-check-input', data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "init_with_readme" }
+ = label_tag 'project[initialize_with_readme]', class: 'form-check-label' do
+ .option-title
+ %strong Initialize repository with a README
+ .option-description
+ Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.
-= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
-= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
+= f.submit 'Create project', class: "btn btn-success project-submit", data: { track_label: "#{track_label}", track_event: "click_button", track_property: "create_project", track_value: "" }
+= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "cancel" }
diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml
index e90a6355214..98fdb1d7a0b 100644
--- a/app/views/projects/_project_templates.html.haml
+++ b/app/views/projects/_project_templates.html.haml
@@ -5,4 +5,4 @@
.project-fields-form
= render 'projects/project_templates/project_fields_form'
- = render 'projects/new_project_fields', f: f, project_name_id: "template-project-name"
+ = render 'projects/new_project_fields', f: f, project_name_id: "template-project-name", hide_init_with_readme: true, track_label: "create_from_template"
diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml
index 705338c083e..32624ac225b 100644
--- a/app/views/projects/_readme.html.haml
+++ b/app/views/projects/_readme.html.haml
@@ -20,4 +20,4 @@
distributed with computer software, forming part of its documentation.
GitLab will render it here instead of this message.
%p
- = link_to "Add Readme", @project.add_readme_path, class: 'btn btn-new'
+ = link_to "Add Readme", @project.add_readme_path, class: 'btn btn-success'
diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml
index 15ec58289e3..4cf49f3cf62 100644
--- a/app/views/projects/_stat_anchor_list.html.haml
+++ b/app/views/projects/_stat_anchor_list.html.haml
@@ -1,7 +1,7 @@
- anchors = local_assigns.fetch(:anchors, [])
- return unless anchors.any?
-%ul.nav.justify-content-center
+%ul.nav
- anchors.each do |anchor|
%li.nav-item
= link_to_if anchor.link, anchor.label, anchor.link, class: anchor.enabled ? 'nav-link stat-link' : "nav-link btn btn-#{anchor.class_modifier || 'missing'}" do
diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml
index 5646dc464f8..45e1d32980c 100644
--- a/app/views/projects/_wiki.html.haml
+++ b/app/views/projects/_wiki.html.haml
@@ -2,17 +2,17 @@
%div{ class: container_class }
.prepend-top-default.append-bottom-default
.wiki
- = render_wiki_content(@wiki_home)
+ = render_wiki_content(@wiki_home, legacy_render_context(params))
- else
- can_create_wiki = can?(current_user, :create_wiki, @project)
- .project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] }
- .text-center{ class: container_class }
+ .landing{ class: [('row-content-block row p-0 align-items-center' if can_create_wiki), ('content-block' unless can_create_wiki)] }
+ .col-12.col-md-3.p-0
+ .svg-content
+ = image_tag 'illustrations/wiki_login_empty.svg'
+ .col-12.col-md-9.text-center.text-md-left.pl-md-0.pl-sm-3.mb-4
%h4
- This project does not have a wiki homepage yet
+ = _("This project does not have a wiki homepage yet")
- if can_create_wiki
%p
- Add a homepage to your wiki that contains information about your project
- %p
- We recommend you
- = link_to "add a homepage", project_wiki_path(@project, :home)
- to your project's wiki and GitLab will show it here instead of this message.
+ = _("Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message.")
+ = link_to _("Create your first page"), project_wiki_path(@project, :home) + '?view=create', class: "btn btn-primary"
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index 87b165e581a..09295940529 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -1,7 +1,7 @@
- breadcrumb_title _('Artifacts')
- page_title @path.presence, 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs'
-= render "projects/jobs/header", show_controls: false
+= render "projects/jobs/header"
- add_to_breadcrumbs(s_('CICD|Jobs'), project_jobs_path(@project))
- add_to_breadcrumbs("##{@build.id}", project_jobs_path(@project))
diff --git a/app/views/projects/artifacts/file.html.haml b/app/views/projects/artifacts/file.html.haml
index f7174d6b2c6..808b4acc8f3 100644
--- a/app/views/projects/artifacts/file.html.haml
+++ b/app/views/projects/artifacts/file.html.haml
@@ -1,6 +1,6 @@
- page_title @path, 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs'
-= render "projects/jobs/header", show_controls: false
+= render "projects/jobs/header"
.tree-holder
.nav-block
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index a4b1b496b69..cf273aab108 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -5,6 +5,7 @@
%ul.blob-commit-info
= render 'projects/commits/commit', commit: @last_commit, project: @project, ref: @ref
+ = render_if_exists 'projects/blob/owners', blob: blob
= render "projects/blob/auxiliary_viewer", blob: blob
#blob-content-holder.blob-content-holder
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index 6f3a691518b..e9010dc63fc 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -15,7 +15,7 @@
= render 'shared/new_commit_form', placeholder: _("Add new directory")
.form-actions
- = submit_tag _("Create directory"), class: 'btn btn-create'
+ = submit_tag _("Create directory"), class: 'btn btn-success'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
= render 'shared/projects/edit_information'
diff --git a/app/views/projects/blob/_template_selectors.html.haml b/app/views/projects/blob/_template_selectors.html.haml
index 5b092427496..bd46f5a4349 100644
--- a/app/views/projects/blob/_template_selectors.html.haml
+++ b/app/views/projects/blob/_template_selectors.html.haml
@@ -3,15 +3,15 @@
Template
.template-selector-dropdowns-wrap
.template-type-selector.js-template-type-selector-wrap.hidden
- = dropdown_tag("Choose type", options: { toggle_class: 'js-template-type-selector', title: "Choose a template type" } )
+ = dropdown_tag("Choose type", options: { toggle_class: 'js-template-type-selector qa-template-type-dropdown', title: "Choose a template type" } )
.license-selector.js-license-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag("Apply a license template", options: { toggle_class: 'js-license-selector', title: "Apply a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } )
+ = dropdown_tag("Apply a license template", options: { toggle_class: 'js-license-selector qa-license-dropdown', title: "Apply a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select(@project), project: @project.name, fullname: @project.namespace.human_name } } )
.gitignore-selector.js-gitignore-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag("Apply a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } )
+ = dropdown_tag("Apply a .gitignore template", options: { toggle_class: 'js-gitignore-selector qa-gitignore-dropdown', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitignore_names(@project) } } )
.gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag("Apply a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } )
+ = dropdown_tag("Apply a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector qa-gitlab-ci-yml-dropdown', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls(@project) } } )
.dockerfile-selector.js-dockerfile-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag("Apply a Dockerfile template", options: { toggle_class: 'js-dockerfile-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names } } )
+ = dropdown_tag("Apply a Dockerfile template", options: { toggle_class: 'js-dockerfile-selector qa-dockerfile-dropdown', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names(@project) } } )
.template-selectors-undo-menu.hidden
%span.text-info Template applied
%button.btn.btn-sm.btn-info Undo
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index 0a5c73c9037..d2b3c8ef96b 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -20,7 +20,7 @@
= render 'shared/new_commit_form', placeholder: placeholder
.form-actions
- = button_tag class: 'btn btn-create btn-upload-file', id: 'submit-all', type: 'button' do
+ = button_tag class: 'btn btn-success btn-upload-file', id: 'submit-all', type: 'button' do
= icon('spin spinner', class: 'js-loading-icon hidden' )
= button_title
= link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 27cf040da7c..3f2d96b70e5 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -21,10 +21,10 @@
Write
%li
- = link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id) do
+ = link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id, legacy_render: params[:legacy_render]) do
= editing_preview_title(@blob.name)
- = form_tag(project_update_blob_path(@project, @id), method: :put, class: 'js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths) do
+ = form_tag(project_update_blob_path(@project, @id), method: :put, class: 'js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths(@project)) do
= render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
= hidden_field_tag 'last_commit_sha', @last_commit_sha
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 39442564a2b..4be87b9e074 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -7,7 +7,7 @@
New file
= render 'template_selectors'
.file-editor
- = form_tag(project_create_blob_path(@project, @id), method: :post, class: 'js-edit-blob-form js-new-blob-form js-quick-submit js-requires-input', data: blob_editor_paths) do
+ = form_tag(project_create_blob_path(@project, @id), method: :post, class: 'js-edit-blob-form js-new-blob-form js-quick-submit js-requires-input', data: blob_editor_paths(@project)) do
= render 'projects/blob/editor', ref: @ref
= render 'shared/new_commit_form', placeholder: "Add new file"
diff --git a/app/views/projects/blob/preview.html.haml b/app/views/projects/blob/preview.html.haml
index da2cef17e8a..eb65cd90ea8 100644
--- a/app/views/projects/blob/preview.html.haml
+++ b/app/views/projects/blob/preview.html.haml
@@ -2,7 +2,7 @@
.diff-content
- if markup?(@blob.name)
.file-content.wiki
- = markup(@blob.name, @content)
+ = markup(@blob.name, @content, legacy_render_context(params))
- else
.file-content.code.js-syntax-highlight
- unless @diff_lines.empty?
diff --git a/app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml b/app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml
index 28c5be6ebf3..5be7cc7f25a 100644
--- a/app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml
+++ b/app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml
@@ -1,9 +1,9 @@
-- if viewer.valid?
+- if viewer.valid?(@project, @commit.sha)
= icon('check fw')
This GitLab CI configuration is valid.
- else
= icon('warning fw')
This GitLab CI configuration is invalid:
- = viewer.validation_message
+ = viewer.validation_message(@project, @commit.sha)
= link_to 'Learn more', help_page_path('ci/yaml/README')
diff --git a/app/views/projects/blob/viewers/_highlight_embed.html.haml b/app/views/projects/blob/viewers/_highlight_embed.html.haml
index 9bd4ef6ad0b..6a631fef1a9 100644
--- a/app/views/projects/blob/viewers/_highlight_embed.html.haml
+++ b/app/views/projects/blob/viewers/_highlight_embed.html.haml
@@ -4,4 +4,6 @@
- blob.data.each_line.each_with_index do |_, index|
%span.diff-line-num= index + 1
.blob-content{ data: { blob_id: blob.id } }
- = highlight(blob.path, blob.data, repository: nil, plain: blob.no_highlighting?)
+ %pre.code.highlight
+ %code
+ = blob.present.highlight
diff --git a/app/views/projects/blob/viewers/_markup.html.haml b/app/views/projects/blob/viewers/_markup.html.haml
index 230305b488d..bd12cadf240 100644
--- a/app/views/projects/blob/viewers/_markup.html.haml
+++ b/app/views/projects/blob/viewers/_markup.html.haml
@@ -1,4 +1,6 @@
- blob = viewer.blob
-- rendered_markup = blob.rendered_markup if blob.respond_to?(:rendered_markup)
+- context = legacy_render_context(params)
+- unless context[:markdown_engine] == :redcarpet
+ - context[:rendered] = blob.rendered_markup if blob.respond_to?(:rendered_markup)
.file-content.wiki
- = markup(blob.name, blob.data, rendered: rendered_markup)
+ = markup(blob.name, blob.data, context)
diff --git a/app/views/projects/blob/viewers/_text.html.haml b/app/views/projects/blob/viewers/_text.html.haml
index a91df321ca0..26ad23da436 100644
--- a/app/views/projects/blob/viewers/_text.html.haml
+++ b/app/views/projects/blob/viewers/_text.html.haml
@@ -1 +1 @@
-= render 'shared/file_highlight', blob: viewer.blob, repository: @repository
+= render 'shared/file_highlight', blob: viewer.blob
diff --git a/app/views/projects/branches/_panel.html.haml b/app/views/projects/branches/_panel.html.haml
index 398f76d379a..0e4b119bb54 100644
--- a/app/views/projects/branches/_panel.html.haml
+++ b/app/views/projects/branches/_panel.html.haml
@@ -9,8 +9,7 @@
.card.prepend-top-10
.card-header
- %h4.card-title
- = panel_title
+ = panel_title
%ul.content-list.all-branches
- branches.first(overview_max_branches).each do |branch|
= render "projects/branches/branch", branch: branch, merged: project.repository.merged_to_root_ref?(branch)
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index d6568c9f64a..ca867961f6b 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -41,7 +41,7 @@
data: { confirm: s_('Branches|Deleting the merged branches cannot be undone. Are you sure?'),
container: 'body' } do
= s_('Branches|Delete merged branches')
- = link_to new_project_branch_path(@project), class: 'btn btn-create' do
+ = link_to new_project_branch_path(@project), class: 'btn btn-success' do
= s_('Branches|New branch')
- if can?(current_user, :admin_project, @project)
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 65b414c8af2..500536a5dbc 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -26,7 +26,7 @@
= render 'shared/ref_dropdown', dropdown_class: 'wide'
.form-text.text-muted Existing branch name, tag, or commit SHA
.form-actions
- = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3
+ = button_tag 'Create branch', class: 'btn btn-success', tabindex: 3
= link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel'
-# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index 8b9c52f0802..45515fb492f 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -8,7 +8,7 @@
- if show_menu
.project-action-button.dropdown.inline
- %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' }
+ %a.btn.dropdown-toggle.has-tooltip.qa-create-new-dropdown{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' }
= icon('plus')
= icon("caret-down")
%ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
@@ -28,7 +28,7 @@
%li.dropdown-header= _('This repository')
- if can_push_code
- %li= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master')
+ %li.qa-new-file-option= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master')
- unless @project.empty_repo?
%li= link_to _('New branch'), new_project_branch_path(@project)
%li= link_to _('New tag'), new_project_tag_path(@project)
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index f880556a9f7..8da27ca7cb3 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -1,17 +1,17 @@
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project)
- - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do
- = custom_icon('icon_fork')
- %span= s_('GoToYourFork|Fork')
- - else
- - can_create_fork = current_user.can?(:create_fork)
- = link_to new_project_fork_path(@project),
- class: "btn btn-default #{'has-tooltip disabled' unless can_create_fork}",
- title: (_('You have reached your project limit') unless can_create_fork) do
- = custom_icon('icon_fork')
- %span= s_('CreateNewFork|Fork')
- .count-with-arrow
- %span.arrow
- = link_to project_forks_path(@project), title: n_('Fork', 'Forks', @project.forks_count), class: 'count' do
- = @project.forks_count
+ .count-badge.d-inline-flex.align-item-stretch.append-right-8
+ %span.fork-count.count-badge-count.d-flex.align-items-center
+ = link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do
+ = @project.forks_count
+ - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn' do
+ = sprite_icon('fork', { css_class: 'icon' })
+ %span= s_('ProjectOverview|Fork')
+ - else
+ - can_create_fork = current_user.can?(:create_fork)
+ = link_to new_project_fork_path(@project),
+ class: "btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn #{'has-tooltip disabled' unless can_create_fork}",
+ title: (s_('ProjectOverview|You have reached your project limit') unless can_create_fork) do
+ = sprite_icon('fork', { css_class: 'icon' })
+ %span= s_('ProjectOverview|Fork')
diff --git a/app/views/projects/buttons/_koding.html.haml b/app/views/projects/buttons/_koding.html.haml
deleted file mode 100644
index e665ca61da8..00000000000
--- a/app/views/projects/buttons/_koding.html.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-- if koding_enabled? && current_user && @repository.koding_yml && @project.can_current_user_push_code?
- = link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank', rel: 'noopener noreferrer' do
- _('Run in IDE (Koding)')
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index a2dc2730ecc..0d04ecb3a58 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,21 +1,19 @@
- if current_user
- %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }>
- - if current_user.starred?(@project)
- = sprite_icon('star')
- %span.starred= _('Unstar')
- - else
- = sprite_icon('star-o')
- %span= s_('StarProject|Star')
- .count-with-arrow
- %span.arrow
- %span.count.star-count
+ .count-badge.d-inline-flex.align-item-stretch.append-right-8
+ %span.star-count.count-badge-count.d-flex.align-items-center
= @project.star_count
+ %button.count-badge-button.btn.btn-default.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
+ - if current_user.starred?(@project)
+ = sprite_icon('star', { css_class: 'icon' })
+ %span.starred= s_('ProjectOverview|Unstar')
+ - else
+ = sprite_icon('star-o', { css_class: 'icon' })
+ %span= s_('ProjectOverview|Star')
- else
- = link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: _('You must sign in to star a project') do
- = sprite_icon('star')
- #{ s_('StarProject|Star') }
- .count-with-arrow
- %span.arrow
- %span.count
+ .count-badge.d-inline-flex.align-item-stretch.append-right-8
+ %span.star-count.count-badge-count.d-flex.align-items-center
= @project.star_count
+ = link_to new_user_session_path, class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
+ = sprite_icon('star-o', { css_class: 'icon' })
+ %span= s_('ProjectOverview|Star')
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 44c1453e239..0b10c66777a 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -47,7 +47,9 @@
%span.badge.badge-info triggered
- if job.try(:allow_failure)
%span.badge.badge-danger allowed to fail
- - if job.action?
+ - if job.schedulable?
+ %span.badge.badge-info= s_('DelayedJobs|delayed')
+ - elsif job.action?
%span.badge.badge-info manual
- if pipeline_link
@@ -99,8 +101,26 @@
= sprite_icon('download')
- if can?(current_user, :update_build, job)
- if job.active?
- = link_to cancel_project_job_path(job.project, job, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do
+ = link_to cancel_project_job_path(job.project, job, continue: { to: request.fullpath }), method: :post, title: 'Cancel', class: 'btn btn-build' do
= icon('remove', class: 'cred')
+ - elsif job.scheduled?
+ .btn-group
+ .btn.btn-default{ disabled: true }
+ = sprite_icon('planning')
+ %time.js-remaining-time{ datetime: job.scheduled_at.utc.iso8601 }
+ = duration_in_numbers(job.execute_in)
+ - confirmation_message = s_("DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes.") % { job_name: job.name }
+ = link_to play_project_job_path(job.project, job, return_to: request.original_url),
+ method: :post,
+ title: s_('DelayedJobs|Start now'),
+ class: 'btn btn-default btn-build has-tooltip',
+ data: { confirm: confirmation_message } do
+ = sprite_icon('play')
+ = link_to unschedule_project_job_path(job.project, job, return_to: request.original_url),
+ method: :post,
+ title: s_('DelayedJobs|Unschedule'),
+ class: 'btn btn-default btn-build has-tooltip' do
+ = sprite_icon('time-out')
- elsif allow_retry
- if job.playable? && !admin && can?(current_user, :update_build, job)
= link_to play_project_job_path(job.project, job, return_to: request.original_url), method: :post, title: 'Play', class: 'btn btn-build' do
diff --git a/app/views/projects/clusters/_advanced_settings.html.haml b/app/views/projects/clusters/_advanced_settings.html.haml
deleted file mode 100644
index 243e8cd9ba0..00000000000
--- a/app/views/projects/clusters/_advanced_settings.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-- if can?(current_user, :admin_cluster, @cluster)
- - if @cluster.managed?
- .append-bottom-20
- %label.append-bottom-10
- = s_('ClusterIntegration|Google Kubernetes Engine')
- %p
- - link_gke = link_to(s_('ClusterIntegration|Google Kubernetes Engine'), @cluster.gke_cluster_url, target: '_blank', rel: 'noopener noreferrer')
- = s_('ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}').html_safe % { link_gke: link_gke }
-
- .sub-section.form-group
- %h4.text-danger
- = s_('ClusterIntegration|Remove Kubernetes cluster integration')
- %p
- = s_("ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster.")
- = link_to(s_('ClusterIntegration|Remove integration'), namespace_project_cluster_path(@project.namespace, @project, @cluster.id), method: :delete, class: 'btn btn-danger', data: { confirm: s_("ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster.")})
diff --git a/app/views/projects/clusters/_banner.html.haml b/app/views/projects/clusters/_banner.html.haml
deleted file mode 100644
index f18caa3f4ac..00000000000
--- a/app/views/projects/clusters/_banner.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-%h4= s_('ClusterIntegration|Kubernetes cluster integration')
-
-.settings-content
- .hidden.js-cluster-error.alert.alert-danger.alert-block.append-bottom-10{ role: 'alert' }
- = s_('ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine')
- %p.js-error-reason
-
- .hidden.js-cluster-creating.alert.alert-info.alert-block.append-bottom-10{ role: 'alert' }
- = s_('ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine...')
-
- .hidden.js-cluster-success.alert.alert-success.alert-block.append-bottom-10{ role: 'alert' }
- = s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details")
-
- %p= s_('ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab')
diff --git a/app/views/projects/clusters/_cluster.html.haml b/app/views/projects/clusters/_cluster.html.haml
deleted file mode 100644
index 2d7f7c6b1fb..00000000000
--- a/app/views/projects/clusters/_cluster.html.haml
+++ /dev/null
@@ -1,24 +0,0 @@
-.gl-responsive-table-row
- .table-section.section-30
- .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Kubernetes cluster")
- .table-mobile-content
- = link_to cluster.name, namespace_project_cluster_path(@project.namespace, @project, cluster)
- .table-section.section-30
- .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Environment scope")
- .table-mobile-content= cluster.environment_scope
- .table-section.section-30
- .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Project namespace")
- .table-mobile-content= cluster.platform_kubernetes&.actual_namespace
- .table-section.section-10
- .table-mobile-header{ role: "rowheader" }
- .table-mobile-content
- %button.js-project-feature-toggle.project-feature-toggle{ type: "button",
- class: "#{'is-checked' if cluster.enabled?} #{'is-disabled' if !cluster.can_toggle_cluster?}",
- "aria-label": s_("ClusterIntegration|Toggle Kubernetes Cluster"),
- disabled: !cluster.can_toggle_cluster?,
- data: { endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } }
- %input.js-project-feature-toggle-input{ type: "hidden", value: cluster.enabled? }
- = icon("spinner spin", class: "loading-icon")
- %span.toggle-icon
- = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
- = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
diff --git a/app/views/projects/clusters/_empty_state.html.haml b/app/views/projects/clusters/_empty_state.html.haml
deleted file mode 100644
index b8a3556a206..00000000000
--- a/app/views/projects/clusters/_empty_state.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-.row.empty-state
- .col-12
- .svg-content= image_tag 'illustrations/clusters_empty.svg'
- .col-12
- .text-content
- %h4.text-center= s_('ClusterIntegration|Integrate Kubernetes cluster automation')
- - link_to_help_page = link_to(_('Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
- %p= s_('ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page}
-
- - if can?(current_user, :create_cluster, @project)
- .text-center
- = link_to s_('ClusterIntegration|Add Kubernetes cluster'), new_project_cluster_path(@project), class: 'btn btn-success'
diff --git a/app/views/projects/clusters/_integration_form.html.haml b/app/views/projects/clusters/_integration_form.html.haml
deleted file mode 100644
index b46b45fea49..00000000000
--- a/app/views/projects/clusters/_integration_form.html.haml
+++ /dev/null
@@ -1,45 +0,0 @@
-= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
- = form_errors(@cluster)
- .form-group
- %h5= s_('ClusterIntegration|Integration status')
- %p
- - if @cluster.enabled?
- - if can?(current_user, :update_cluster, @cluster)
- = s_('ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab\'s connection to it.')
- - else
- = s_('ClusterIntegration|Kubernetes cluster integration is enabled for this project.')
- - else
- = s_('ClusterIntegration|Kubernetes cluster integration is disabled for this project.')
- %label.append-bottom-0.js-cluster-enable-toggle-area
- %button{ type: 'button',
- class: "js-project-feature-toggle project-feature-toggle #{'is-checked' if @cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}",
- "aria-label": s_("ClusterIntegration|Toggle Kubernetes cluster"),
- disabled: !can?(current_user, :update_cluster, @cluster) }
- = field.hidden_field :enabled, { class: 'js-project-feature-toggle-input'}
- %span.toggle-icon
- = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
- = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
-
- - if has_multiple_clusters?(@project)
- .form-group
- %h5= s_('ClusterIntegration|Environment scope')
- %p
- = s_("ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster.")
- = link_to s_("ClusterIntegration|Learn more about environments"), help_page_path('ci/environments')
- = field.text_field :environment_scope, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Environment scope')
-
- - if can?(current_user, :update_cluster, @cluster)
- .form-group
- = field.submit _('Save changes'), class: 'btn btn-success'
-
- - unless has_multiple_clusters?(@project)
- %h5= s_('ClusterIntegration|Environment scope')
- %p
- %code *
- is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster.
- = link_to 'More information', ('https://docs.gitlab.com/ee/user/project/clusters/#setting-the-environment-scope')
-
- %h5= s_('ClusterIntegration|Security')
- %p
- = s_("ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application.")
- = link_to s_("ClusterIntegration|Learn more about security configuration"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications')
diff --git a/app/views/projects/clusters/_sidebar.html.haml b/app/views/projects/clusters/_sidebar.html.haml
deleted file mode 100644
index 3d10348212f..00000000000
--- a/app/views/projects/clusters/_sidebar.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-- clusters_help_url = help_page_path('user/project/clusters/index.md')
-- help_link_start = "<a href=\"%{url}\" target=\"_blank\" rel=\"noopener noreferrer\">".html_safe
-- help_link_end = '</a>'.html_safe
-%h4.prepend-top-0
- = s_('ClusterIntegration|Kubernetes cluster integration')
-%p
- = s_('ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way.')
-%p
- = s_('ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: clusters_help_url }, help_link_end: help_link_end }
diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml
deleted file mode 100644
index 9133de6559d..00000000000
--- a/app/views/projects/clusters/gcp/_form.html.haml
+++ /dev/null
@@ -1,65 +0,0 @@
-= javascript_include_tag 'https://apis.google.com/js/api.js'
-- external_link_icon = icon('external-link')
-- zones_link_url = 'https://cloud.google.com/compute/docs/regions-zones/regions-zones'
-- machine_type_link_url = 'https://cloud.google.com/compute/docs/machine-types'
-- pricing_link_url = 'https://cloud.google.com/compute/pricing#machinetype'
-- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
-- help_link_end = ' %{external_link_icon}</a>'.html_safe % { external_link_icon: external_link_icon }
-
-%p
- - link_to_help_page = link_to(s_('ClusterIntegration|help page'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
- = s_('ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration.').html_safe % { link_to_help_page: link_to_help_page}
-
-%p= link_to('Select a different Google account', @authorize_url)
-
-= form_for @gcp_cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: create_gcp_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
- = form_errors(@gcp_cluster)
- .form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
- = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
- .form-group
- = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
- = field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
-
- = field.fields_for :provider_gcp, @gcp_cluster.provider_gcp do |provider_gcp_field|
- .form-group
- = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-bold'
- .js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } }
- = provider_gcp_field.hidden_field :gcp_project_id
- .dropdown
- %button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
- %span.dropdown-toggle-text
- = _('Select project')
- = icon('chevron-down')
- %span.form-text.text-muted &nbsp;
-
- .form-group
- = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone'), class: 'label-bold'
- .js-gcp-zone-dropdown-entry-point
- = provider_gcp_field.hidden_field :zone
- .dropdown
- %button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
- %span.dropdown-toggle-text
- = _('Select project to choose zone')
- = icon('chevron-down')
- %p.form-text.text-muted
- = s_('ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: zones_link_url }, help_link_end: help_link_end }
-
- .form-group
- = provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-bold'
- = provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3'
-
- .form-group
- = provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-bold'
- .js-gcp-machine-type-dropdown-entry-point
- = provider_gcp_field.hidden_field :machine_type
- .dropdown
- %button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
- %span.dropdown-toggle-text
- = _('Select project and zone to choose machine type')
- = icon('chevron-down')
- %p.form-text.text-muted
- = s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
-
- .form-group
- = field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
diff --git a/app/views/projects/clusters/gcp/_show.html.haml b/app/views/projects/clusters/gcp/_show.html.haml
deleted file mode 100644
index 877e0cc876c..00000000000
--- a/app/views/projects/clusters/gcp/_show.html.haml
+++ /dev/null
@@ -1,41 +0,0 @@
-.form-group
- %label.append-bottom-10{ for: 'cluster-name' }
- = s_('ClusterIntegration|Kubernetes cluster name')
- .input-group
- %input.form-control.cluster-name.js-select-on-focus{ value: @cluster.name, readonly: true }
- %span.input-group-append
- = clipboard_button(text: @cluster.name, title: s_('ClusterIntegration|Copy Kubernetes cluster name'), class: 'input-group-text btn-default')
-
-= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
- = form_errors(@cluster)
-
- = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
- .form-group
- = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
- .input-group
- = platform_kubernetes_field.text_field :api_url, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|API URL'), readonly: true
- %span.input-group-append
- = clipboard_button(text: @cluster.platform_kubernetes.api_url, title: s_('ClusterIntegration|Copy API URL'), class: 'input-group-text btn-default')
-
- .form-group
- = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
- .input-group
- = platform_kubernetes_field.text_area :ca_cert, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)'), readonly: true
- %span.input-group-append.clipboard-addon
- = clipboard_button(text: @cluster.platform_kubernetes.ca_cert, title: s_('ClusterIntegration|Copy CA Certificate'), class: 'input-group-text btn-blank')
-
- .form-group
- = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
- .input-group
- = platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token js-select-on-focus', type: 'password', placeholder: s_('ClusterIntegration|Token'), readonly: true
- %span.input-group-append
- %button.btn.btn-default.input-group-text.js-show-cluster-token{ type: 'button' }
- = s_('ClusterIntegration|Show')
- = clipboard_button(text: @cluster.platform_kubernetes.token, title: s_('ClusterIntegration|Copy Token'), class: 'btn-default')
-
- .form-group
- = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
- = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
-
- .form-group
- = field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
diff --git a/app/views/projects/clusters/index.html.haml b/app/views/projects/clusters/index.html.haml
deleted file mode 100644
index a55de84b5cd..00000000000
--- a/app/views/projects/clusters/index.html.haml
+++ /dev/null
@@ -1,24 +0,0 @@
-- breadcrumb_title 'Kubernetes'
-- page_title "Kubernetes Clusters"
-
-= render_gcp_signup_offer
-
-.clusters-container
- - if @clusters.empty?
- = render "empty_state"
- - else
- .top-area.adjust
- .nav-text
- = s_("ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project")
- .ci-table.js-clusters-list
- .gl-responsive-table-row.table-row-header{ role: "row" }
- .table-section.section-30{ role: "rowheader" }
- = s_("ClusterIntegration|Kubernetes cluster")
- .table-section.section-30{ role: "rowheader" }
- = s_("ClusterIntegration|Environment scope")
- .table-section.section-30{ role: "rowheader" }
- = s_("ClusterIntegration|Project namespace")
- .table-section.section-10{ role: "rowheader" }
- - @clusters.each do |cluster|
- = render "cluster", cluster: cluster.present(current_user: current_user)
- = paginate @clusters, theme: "gitlab"
diff --git a/app/views/projects/clusters/new.html.haml b/app/views/projects/clusters/new.html.haml
deleted file mode 100644
index a38003f5750..00000000000
--- a/app/views/projects/clusters/new.html.haml
+++ /dev/null
@@ -1,36 +0,0 @@
-- breadcrumb_title 'Kubernetes'
-- page_title _("Kubernetes Cluster")
-- active_tab = local_assigns.fetch(:active_tab, 'gcp')
-= javascript_include_tag 'https://apis.google.com/js/api.js'
-
-= render_gcp_signup_offer
-
-.row.prepend-top-default
- .col-md-3
- = render 'sidebar'
- .col-md-9.js-toggle-container
- %ul.nav-links.nav-tabs.gitlab-tabs.nav{ role: 'tablist' }
- %li.nav-item{ role: 'presentation' }
- %a.nav-link{ href: '#create-gcp-cluster-pane', id: 'create-gcp-cluster-tab', class: active_when(active_tab == 'gcp'), data: { toggle: 'tab' }, role: 'tab' }
- %span Create new Cluster on GKE
- %li.nav-item{ role: 'presentation' }
- %a.nav-link{ href: '#add-user-cluster-pane', id: 'add-user-cluster-tab', class: active_when(active_tab == 'user'), data: { toggle: 'tab' }, role: 'tab' }
- %span Add existing cluster
-
- .tab-content.gitlab-tab-content
- .tab-pane{ id: 'create-gcp-cluster-pane', class: active_when(active_tab == 'gcp'), role: 'tabpanel' }
- = render 'projects/clusters/gcp/header'
- - if @valid_gcp_token
- = render 'projects/clusters/gcp/form'
- - elsif @authorize_url
- .signin-with-google
- = link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px'), @authorize_url)
- = _('or')
- = link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer')
- - else
- - link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer')
- = s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link }
-
- .tab-pane{ id: 'add-user-cluster-pane', class: active_when(active_tab == 'user'), role: 'tabpanel' }
- = render 'projects/clusters/user/header'
- = render 'projects/clusters/user/form'
diff --git a/app/views/projects/clusters/show.html.haml b/app/views/projects/clusters/show.html.haml
deleted file mode 100644
index 08d2deff6f8..00000000000
--- a/app/views/projects/clusters/show.html.haml
+++ /dev/null
@@ -1,51 +0,0 @@
-- @content_class = "limit-container-width" unless fluid_layout
-- add_to_breadcrumbs "Kubernetes Clusters", project_clusters_path(@project)
-- breadcrumb_title @cluster.name
-- page_title _("Kubernetes Cluster")
-
-- expanded = Rails.env.test?
-
-- status_path = status_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster.id, format: :json) if can?(current_user, :admin_cluster, @cluster)
-.edit-cluster-form.js-edit-cluster-form{ data: { status_path: status_path,
- install_helm_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :helm),
- install_ingress_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :ingress),
- install_prometheus_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :prometheus),
- install_runner_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :runner),
- install_jupyter_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :jupyter),
- toggle_status: @cluster.enabled? ? 'true': 'false',
- cluster_status: @cluster.status_name,
- cluster_status_reason: @cluster.status_reason,
- help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
- ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-ip-address'),
- ingress_dns_help_path: help_page_path('topics/autodevops/quick_start_guide.md', anchor: 'point-dns-at-cluster-ip'),
- manage_prometheus_path: edit_project_service_path(@cluster.project, 'prometheus') } }
-
- .js-cluster-application-notice
- .flash-container
-
- %section.settings.no-animate.expanded#cluster-integration
- = render 'banner'
- = render 'integration_form'
-
- .cluster-applications-table#js-cluster-applications
-
- %section.settings#js-cluster-details{ class: ('expanded' if expanded) }
- .settings-header
- %h4= s_('ClusterIntegration|Kubernetes cluster details')
- %button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
- %p= s_('ClusterIntegration|See and edit the details for your Kubernetes cluster')
- .settings-content
- - if @cluster.managed?
- = render 'projects/clusters/gcp/show'
- - else
- = render 'projects/clusters/user/show'
-
- %section.settings.no-animate#js-cluster-advanced-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4= _('Advanced settings')
- %button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
- %p= s_("ClusterIntegration|Advanced options on this Kubernetes cluster's integration")
- .settings-content
- = render 'advanced_settings'
diff --git a/app/views/projects/clusters/user/_form.html.haml b/app/views/projects/clusters/user/_form.html.haml
deleted file mode 100644
index e8ef0008802..00000000000
--- a/app/views/projects/clusters/user/_form.html.haml
+++ /dev/null
@@ -1,29 +0,0 @@
-= form_for @user_cluster, url: create_user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
- = form_errors(@user_cluster)
- .form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
- = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
- - if has_multiple_clusters?(@project)
- .form-group
- = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
- = field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope')
-
- = field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field|
- .form-group
- = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold'
- = platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
-
- .form-group
- = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold'
- = platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
-
- .form-group
- = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-bold'
- = platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off'
-
- .form-group
- = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
- = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
-
- .form-group
- = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
diff --git a/app/views/projects/clusters/user/_show.html.haml b/app/views/projects/clusters/user/_show.html.haml
deleted file mode 100644
index 20a07d6695e..00000000000
--- a/app/views/projects/clusters/user/_show.html.haml
+++ /dev/null
@@ -1,30 +0,0 @@
-= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
- = form_errors(@cluster)
- .form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
- = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
-
- = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
- .form-group
- = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold'
- = platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
-
- .form-group
- = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold'
- = platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
-
- .form-group
- = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-bold'
- .input-group
- = platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token', type: 'password', placeholder: s_('ClusterIntegration|Token'), autocomplete: 'off'
- %span.input-group-append.clipboard-addon
- .input-group-text
- %button.js-show-cluster-token.btn-blank{ type: 'button' }
- = s_('ClusterIntegration|Show')
-
- .form-group
- = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
- = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
-
- .form-group
- = field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index afd70ef5774..e71615dd1c5 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -33,7 +33,7 @@
- else
= hidden_field_tag 'create_merge_request', 1, id: nil
.form-actions
- = submit_tag label, class: 'btn btn-create'
+ = submit_tag label, class: 'btn btn-success'
= link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
= render 'shared/projects/edit_information'
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 7951a5ddc9e..c6789e32dbe 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -1,3 +1,7 @@
+-#-----------------------------------------------------------------
+ WARNING: Please keep changes up-to-date with the following files:
+ - `assets/javascripts/diffs/components/commit_item.vue`
+-#-----------------------------------------------------------------
- view_details = local_assigns.fetch(:view_details, false)
- merge_request = local_assigns.fetch(:merge_request, nil)
- project = local_assigns.fetch(:project) { merge_request&.project }
@@ -37,7 +41,7 @@
%button.text-expander.js-toggle-button
= sprite_icon('ellipsis_h', size: 12)
- .commiter
+ .committer
- commit_author_link = commit_author_link(commit, avatar: false, size: 24)
- commit_timeago = time_ago_with_tooltip(commit.authored_date, placement: 'bottom')
- commit_text = _('%{commit_author_link} authored %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml
index 8f8eb2c3d5a..6ed65d07202 100644
--- a/app/views/projects/commits/_commit_list.html.haml
+++ b/app/views/projects/commits/_commit_list.html.haml
@@ -1,9 +1,10 @@
-- commits, hidden = limited_commits(@commits)
+- commits = @commits
+- hidden = @hidden_commit_count
- commits = Commit.decorate(commits, @project)
.card
.card-header
- Commits (#{@commits.count})
+ Commits (#{@total_commit_count})
- if hidden > 0
%ul.content-list
- commits.each do |commit|
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index ac6852751be..ec05ff50f25 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -2,7 +2,8 @@
- project = local_assigns.fetch(:project) { merge_request&.project }
- ref = local_assigns.fetch(:ref) { merge_request&.source_branch }
-- commits, hidden = limited_commits(@commits)
+- commits = @commits
+- hidden = @hidden_commit_count
- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
%li.commit-header.js-commit-header{ data: { day: day } }
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index 07112c98804..d24ee4a3251 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -22,7 +22,7 @@
.dropdown-toggle-text.str-truncated= params[:from] || _("Select branch/tag")
= render 'shared/ref_dropdown'
&nbsp;
- = button_tag s_("CompareBranches|Compare"), class: "btn btn-create commits-compare-btn"
+ = button_tag s_("CompareBranches|Compare"), class: "btn btn-success commits-compare-btn"
- if @merge_request.present?
= link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'prepend-left-10 btn'
- elsif create_mr_button?
diff --git a/app/views/projects/default_branch/_show.html.haml b/app/views/projects/default_branch/_show.html.haml
new file mode 100644
index 00000000000..ff6a9d49a61
--- /dev/null
+++ b/app/views/projects/default_branch/_show.html.haml
@@ -0,0 +1,21 @@
+- expanded = Rails.env.test?
+
+%section.settings.no-animate#default-branch-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4= _('Default Branch')
+ %button.btn.js-settings-toggle
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _('Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one.')
+
+ .settings-content
+ - if @project.empty_repo?
+ .text-secondary
+ = _('A default branch cannot be chosen for an empty project.')
+ - else
+ = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, anchor: 'default-branch-settings' }, authenticity_token: true do |f|
+ %fieldset
+ .form-group
+ = f.label :default_branch, "Default Branch", class: 'label-bold'
+ = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'})
+ = f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index f8ab0c1ec54..568930595a2 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -21,4 +21,4 @@
Allow this key to push to repository as well? (Default only allows pull access.)
.form-group.row
- = f.submit "Add key", class: "btn-create btn"
+ = f.submit "Add key", class: "btn-success btn"
diff --git a/app/views/projects/deploy_keys/edit.html.haml b/app/views/projects/deploy_keys/edit.html.haml
index e009b6fef0e..3e7872ebc1c 100644
--- a/app/views/projects/deploy_keys/edit.html.haml
+++ b/app/views/projects/deploy_keys/edit.html.haml
@@ -6,5 +6,5 @@
= form_for [@project.namespace.becomes(Namespace), @project, @deploy_key], html: { class: 'js-requires-input' } do |f|
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
.form-actions
- = f.submit 'Save changes', class: 'btn-save btn'
+ = f.submit 'Save changes', class: 'btn-success btn'
= link_to 'Cancel', project_settings_repository_path(@project), class: 'btn btn-cancel'
diff --git a/app/views/projects/deploy_tokens/_form.html.haml b/app/views/projects/deploy_tokens/_form.html.haml
index 8b7535397bc..5412fcbc9d8 100644
--- a/app/views/projects/deploy_tokens/_form.html.haml
+++ b/app/views/projects/deploy_tokens/_form.html.haml
@@ -6,24 +6,24 @@
.form-group
= f.label :name, class: 'label-bold'
- = f.text_field :name, class: 'form-control', required: true
+ = f.text_field :name, class: 'form-control qa-deploy-token-name', required: true
.form-group
= f.label :expires_at, class: 'label-bold'
- = f.text_field :expires_at, class: 'datepicker form-control', value: f.object.expires_at
+ = f.text_field :expires_at, class: 'datepicker form-control qa-deploy-token-expires-at', value: f.object.expires_at
.form-group
= f.label :scopes, class: 'label-bold'
%fieldset.form-group.form-check
- = f.check_box :read_repository, class: 'form-check-input'
+ = f.check_box :read_repository, class: 'form-check-input qa-deploy-token-read-repository'
= label_tag ("deploy_token_read_repository"), 'read_repository', class: 'label-bold form-check-label'
.text-secondary= s_('DeployTokens|Allows read-only access to the repository')
- if container_registry_enabled?(project)
%fieldset.form-group.form-check
- = f.check_box :read_registry, class: 'form-check-input'
+ = f.check_box :read_registry, class: 'form-check-input qa-deploy-token-read-registry'
= label_tag ("deploy_token_read_registry"), 'read_registry', class: 'label-bold form-check-label'
.text-secondary= s_('DeployTokens|Allows read-only access to the registry images')
.prepend-top-default
- = f.submit s_('DeployTokens|Create deploy token'), class: 'btn btn-success'
+ = f.submit s_('DeployTokens|Create deploy token'), class: 'btn btn-success qa-create-deploy-token'
diff --git a/app/views/projects/deploy_tokens/_index.html.haml b/app/views/projects/deploy_tokens/_index.html.haml
index 33faab0c510..4619522cfaf 100644
--- a/app/views/projects/deploy_tokens/_index.html.haml
+++ b/app/views/projects/deploy_tokens/_index.html.haml
@@ -1,6 +1,6 @@
- expanded = expand_deploy_tokens_section?(@new_deploy_token)
-%section.settings.no-animate#js-deploy-tokens{ class: ('expanded' if expanded) }
+%section.qa-deploy-tokens-settings.settings.no-animate#js-deploy-tokens{ class: ('expanded' if expanded) }
.settings-header
%h4= s_('DeployTokens|Deploy Tokens')
%button.btn.js-settings-toggle.qa-expand-deploy-keys{ type: 'button' }
diff --git a/app/views/projects/deploy_tokens/_new_deploy_token.html.haml b/app/views/projects/deploy_tokens/_new_deploy_token.html.haml
index 5dd9ffba074..c805ee73acc 100644
--- a/app/views/projects/deploy_tokens/_new_deploy_token.html.haml
+++ b/app/views/projects/deploy_tokens/_new_deploy_token.html.haml
@@ -1,18 +1,18 @@
-.created-deploy-token-container.info-well
+.qa-created-deploy-token-section.created-deploy-token-container.info-well
.well-segment
%h5.prepend-top-0
= s_('DeployTokens|Your New Deploy Token')
.form-group
.input-group
- = text_field_tag 'deploy-token-user', deploy_token.username, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
+ = text_field_tag 'deploy-token-user', deploy_token.username, readonly: true, class: 'deploy-token-field form-control js-select-on-focus qa-deploy-token-user'
.input-group-append
= clipboard_button(text: deploy_token.username, title: s_('DeployTokens|Copy username to clipboard'), placement: 'left')
%span.deploy-token-help-block.prepend-top-5.text-success= s_("DeployTokens|Use this username as a login.")
.form-group
.input-group
- = text_field_tag 'deploy-token', deploy_token.token, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
+ = text_field_tag 'deploy-token', deploy_token.token, readonly: true, class: 'deploy-token-field form-control js-select-on-focus qa-deploy-token'
.input-group-append
= clipboard_button(text: deploy_token.token, title: s_('DeployTokens|Copy deploy token to clipboard'), placement: 'left')
%span.deploy-token-help-block.prepend-top-5.text-danger= s_("DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again.")
diff --git a/app/views/projects/deployments/_rollback.haml b/app/views/projects/deployments/_rollback.haml
index 281e042c915..1bd538a08ff 100644
--- a/app/views/projects/deployments/_rollback.haml
+++ b/app/views/projects/deployments/_rollback.haml
@@ -1,4 +1,4 @@
-- if can?(current_user, :create_deployment, deployment) && deployment.deployable
+- if can?(current_user, :create_deployment, deployment)
- tooltip = deployment.last? ? s_('Environments|Re-deploy to environment') : s_('Environments|Rollback environment')
= link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build has-tooltip', title: tooltip do
- if deployment.last?
diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml
index cd0fb21f8a7..ffdca500abe 100644
--- a/app/views/projects/diffs/_line.html.haml
+++ b/app/views/projects/diffs/_line.html.haml
@@ -32,9 +32,9 @@
%a{ href: "##{line_code}", data: { linenumber: link_text } }
%td.line_content.noteable_line{ class: type }<
- if email
- %pre= line.text
+ %pre= line.rich_text
- else
- = diff_line_content(line.text)
+ = diff_line_content(line.rich_text)
- if line_discussions&.any?
- discussion_expanded = local_assigns.fetch(:discussion_expanded, line_discussions.any?(&:expanded?))
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index 1f0ca211074..4b1d4b3ea17 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -5,7 +5,6 @@
- diff_file.parallel_diff_lines.each do |line|
- left = line[:left]
- right = line[:right]
- - last_line = right.new_pos if right
- discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file)
%tr.line_holder.parallel
- if left
@@ -24,7 +23,7 @@
- discussion_left = discussions_left.try(:first)
- if discussion_left && discussion_left.resolvable?
%diff-note-avatars{ "discussion-id" => discussion_left.id }
- %td.line_content.parallel.noteable_line.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.text)
+ %td.line_content.parallel.noteable_line.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.rich_text)
- else
%td.old_line.diff-line-num.empty-cell
%td.line_content.parallel.left-side
@@ -45,7 +44,7 @@
- discussion_right = discussions_right.try(:first)
- if discussion_right && discussion_right.resolvable?
%diff-note-avatars{ "discussion-id" => discussion_right.id }
- %td.line_content.parallel.noteable_line.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.text)
+ %td.line_content.parallel.noteable_line.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.rich_text)
- else
%td.old_line.diff-line-num.empty-cell
%td.line_content.parallel.right-side
diff --git a/app/views/projects/diffs/_single_image_diff.html.haml b/app/views/projects/diffs/_single_image_diff.html.haml
index 12be8beab39..454f814795a 100644
--- a/app/views/projects/diffs/_single_image_diff.html.haml
+++ b/app/views/projects/diffs/_single_image_diff.html.haml
@@ -1,7 +1,5 @@
- blob = diff_file.blob
-- old_blob = diff_file.old_blob
- blob_raw_url = diff_file_blob_raw_url(diff_file)
-- old_blob_raw_url = diff_file_old_blob_raw_url(diff_file)
- click_to_comment = local_assigns.fetch(:click_to_comment, true)
- diff_view_data = local_assigns.fetch(:diff_view_data, '')
- class_name = ''
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
index aa1112c3313..229a4574eeb 100644
--- a/app/views/projects/diffs/_stats.html.haml
+++ b/app/views/projects/diffs/_stats.html.haml
@@ -1,5 +1,5 @@
-- sum_added_lines = diff_files.sum(&:added_lines)
-- sum_removed_lines = diff_files.sum(&:removed_lines)
+- sum_added_lines = diff_files.sum(&:added_lines) # rubocop: disable CodeReuse/ActiveRecord
+- sum_removed_lines = diff_files.sum(&:removed_lines) # rubocop: disable CodeReuse/ActiveRecord
.commit-stat-summary.dropdown
Showing
%button.diff-stats-summary-toggler.js-diff-stats-dropdown{ type: "button", data: { toggle: "dropdown", display: "static" } }<
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index e8a5e63e59e..bc9f6c71fa8 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -3,7 +3,7 @@
.suppressed-container
%a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
-%table.text-file.diff-wrap-lines.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' }
+%table.text-file.diff-wrap-lines.code.js-syntax-highlight.commit-diff{ data: diff_view_data, class: too_big ? 'hide' : '' }
= render partial: "projects/diffs/line",
collection: diff_file.highlighted_diff_lines,
as: :line,
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 30544dde451..de768696fe9 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -36,23 +36,18 @@
= render_if_exists 'projects/classification_policy_settings', f: f
- - unless @project.empty_repo?
- .form-group
- = f.label :default_branch, "Default Branch", class: 'label-bold'
- = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'})
-
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :project
.form-group
= f.label :tag_list, "Tags", class: 'label-bold'
- = f.text_field :tag_list, value: @project.tag_list.sort.join(', '), maxlength: 2000, class: "form-control"
+ = f.text_field :tag_list, value: @project.tag_list.join(', '), maxlength: 2000, class: "form-control"
%p.form-text.text-muted Separate tags with commas.
%fieldset.features
%h5.prepend-top-0= _("Project avatar")
.form-group
- if @project.avatar?
.avatar-container.s160.append-bottom-15
- = project_icon(@project.full_path, alt: '', class: 'avatar project-avatar s160', width: 160, height: 160)
+ = project_icon(@project, alt: '', class: 'avatar project-avatar s160', width: 160, height: 160)
- if @project.avatar_in_git
%p.light
= _("Project avatar in repository: %{link}").html_safe % { link: @project.avatar_in_git }
@@ -64,7 +59,7 @@
- if @project.avatar?
%hr
= link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted"
- = f.submit 'Save changes', class: "btn btn-success js-btn-save-general-project-settings"
+ = f.submit 'Save changes', class: "btn btn-success js-btn-success-general-project-settings"
%section.settings.sharing-permissions.no-animate#js-shared-permissions{ class: ('expanded' if expanded) }
.settings-header
@@ -78,9 +73,9 @@
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' }
-# haml-lint:disable InlineJavaScript
- %script.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project)
+ %script.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data_json(@project)
.js-project-permissions-form
- = f.submit 'Save changes', class: "btn btn-save"
+ = f.submit 'Save changes', class: "btn btn-success"
= render_if_exists 'projects/issues_settings'
@@ -98,10 +93,22 @@
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-settings' }
= render 'projects/merge_request_settings', form: f
- = f.submit 'Save changes', class: "btn btn-save qa-save-merge-request-changes"
+ = f.submit 'Save changes', class: "btn btn-success qa-save-merge-request-changes"
= render_if_exists 'projects/service_desk_settings'
+ %section.settings.no-animate{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = s_('ProjectSettings|Badges')
+ %button.btn.js-settings-toggle{ type: 'button' }
+ = expanded ? 'Collapse' : 'Expand'
+ %p
+ = s_('ProjectSettings|Customize your project badges.')
+ = link_to s_('ProjectSettings|Learn more about badges.'), help_page_path('user/project/badges')
+ .settings-content
+ = render 'shared/badges/badge_settings'
+
= render 'export', project: @project
%section.qa-advanced-settings.settings.advanced-settings.no-animate#js-project-advanced-settings{ class: ('expanded' if expanded) }
@@ -141,7 +148,7 @@
= link_to 'Archive project', archive_project_path(@project),
data: { confirm: "Are you sure that you want to archive this project?" },
method: :post, class: "btn btn-warning"
- .sub-section.rename-respository
+ .sub-section.rename-repository
%h4.warning-title
Rename repository
= render 'projects/errors'
@@ -158,7 +165,7 @@
.input-group
.input-group-prepend
.input-group-text
- #{URI.join(root_url, @project.namespace.full_path)}/
+ #{Gitlab::Utils.append_path(root_url, @project.namespace.full_path)}/
= f.text_field :path, class: 'form-control'
%ul
%li Be careful. Renaming a project's repository can have unintended side effects.
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index d47dc3d8143..936900a0087 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -9,7 +9,7 @@
.project-empty-note-panel
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
.prepend-top-20
- %h4
+ %h4.append-bottom-20
= _('The repository for this project is empty')
- if @project.can_current_user_push_code?
@@ -32,9 +32,13 @@
= _('Otherwise it is recommended you start with one of the options below.')
.prepend-top-20
-%nav.project-stats{ class: container_class }
- = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
- = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
+%nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
+ .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
+ .fade-left= icon('angle-left')
+ .fade-right= icon('angle-right')
+ .nav-links.scrolling-tabs.quick-links
+ = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
+ = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
- if can?(current_user, :push_code, @project)
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
@@ -42,7 +46,7 @@
.empty_wrapper
%h3#repo-command-line-instructions.page-title-empty
Command line instructions
- .git-empty
+ .git-empty.js-git-empty
%fieldset
%h5 Git global setup
%pre.bg-light
@@ -54,7 +58,7 @@
%h5 Create a new repository
%pre.bg-light
:preserve
- git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
+ git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
cd #{h @project.path}
touch README.md
git add README.md
@@ -69,7 +73,7 @@
:preserve
cd existing_folder
git init
- git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
+ git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
git add .
git commit -m "Initial commit"
- if @project.can_current_user_push_to_default_branch?
@@ -82,7 +86,7 @@
:preserve
cd existing_repo
git remote rename origin old-origin
- git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
+ git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
- if @project.can_current_user_push_to_default_branch?
%span><
git push -u origin --all
diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml
index 4694bc39d54..82567f88ccc 100644
--- a/app/views/projects/environments/_external_url.html.haml
+++ b/app/views/projects/environments/_external_url.html.haml
@@ -1,4 +1,4 @@
- if environment.external_url && can?(current_user, :read_environment, environment)
- = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip', title: s_('Environments|Open live environment') do
+ = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip qa-view-deployment', title: s_('Environments|Open live environment') do
= sprite_icon('external-link')
- View deployment
+ = _("View deployment")
diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml
index 0586dbdf0e2..cbd5c54cecc 100644
--- a/app/views/projects/environments/_form.html.haml
+++ b/app/views/projects/environments/_form.html.haml
@@ -1,22 +1,21 @@
.row.prepend-top-default.append-bottom-default
.col-lg-3
%h4.prepend-top-0
- Environments
+ = _("Environments")
%p
- Environments allow you to track deployments of your application
- = succeed "." do
- = link_to "Read more about environments", help_page_path("ci/environments")
+ - link_to_read_more = link_to(_("Read more about environments"), help_page_path("ci/environments"))
+ = _("Environments allow you to track deployments of your application %{link_to_read_more}.").html_safe % { link_to_read_more: link_to_read_more }
= form_for [@project.namespace.becomes(Namespace), @project, @environment], html: { class: 'col-lg-9' } do |f|
= form_errors(@environment)
.form-group
- = f.label :name, 'Name', class: 'label-bold'
+ = f.label :name, _('Name'), class: 'label-bold'
= f.text_field :name, required: true, class: 'form-control'
.form-group
- = f.label :external_url, 'External URL', class: 'label-bold'
+ = f.label :external_url, _('External URL'), class: 'label-bold'
= f.url_field :external_url, class: 'form-control'
.form-actions
- = f.submit 'Save', class: 'btn btn-save'
- = link_to 'Cancel', project_environments_path(@project), class: 'btn btn-cancel'
+ = f.submit _('Save'), class: 'btn btn-save'
+ = link_to _('Cancel'), project_environments_path(@project), class: 'btn btn-cancel'
diff --git a/app/views/projects/environments/_metrics_button.html.haml b/app/views/projects/environments/_metrics_button.html.haml
index a4b27575095..c4f19ea79e7 100644
--- a/app/views/projects/environments/_metrics_button.html.haml
+++ b/app/views/projects/environments/_metrics_button.html.haml
@@ -2,6 +2,6 @@
- return unless can?(current_user, :read_environment, environment)
-= link_to environment_metrics_path(environment), title: 'See metrics', class: 'btn metrics-button' do
+= link_to environment_metrics_path(environment), title: _('See metrics'), class: 'btn metrics-button' do
= sprite_icon('chart')
- Monitoring
+ = _("Monitoring")
diff --git a/app/views/projects/environments/edit.html.haml b/app/views/projects/environments/edit.html.haml
index d6ff3f729b4..d581bd3aeab 100644
--- a/app/views/projects/environments/edit.html.haml
+++ b/app/views/projects/environments/edit.html.haml
@@ -1,8 +1,8 @@
- @no_container = true
-- page_title "Edit", @environment.name, "Environments"
+- page_title _("Edit"), @environment.name, _("Environments")
%div{ class: container_class }
%h3.page-title
- Edit environment
+ = _('Edit environment')
%hr
= render 'form'
diff --git a/app/views/projects/environments/empty.html.haml b/app/views/projects/environments/empty.html.haml
index 1413930ebdb..129dbbf4e56 100644
--- a/app/views/projects/environments/empty.html.haml
+++ b/app/views/projects/environments/empty.html.haml
@@ -1,14 +1,14 @@
- page_title _("Metrics")
-.row
+.row.empty-state
.col-sm-12
.svg-content
= image_tag 'illustrations/operations_metrics_empty.svg'
-.row.empty-environments
- .col-sm-12.text-center
- %h4
- = s_('Metrics|No deployed environments')
- .state-description
- = s_('Metrics|Check out the CI/CD documentation on deploying to an environment')
- .prepend-top-10
- = link_to s_("Metrics|Learn about environments"), help_page_path('ci/environments'), class: 'btn btn-success'
+ .col-12
+ .text-content
+ %h4.text-center
+ = s_('Metrics|No deployed environments')
+ %p.state-description
+ = s_('Metrics|Check out the CI/CD documentation on deploying to an environment')
+ .text-center
+ = link_to s_("Metrics|Learn about environments"), help_page_path('ci/environments'), class: 'btn btn-success'
diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml
index 1ac7dab6775..b7e1cf85cb7 100644
--- a/app/views/projects/environments/folder.html.haml
+++ b/app/views/projects/environments/folder.html.haml
@@ -1,5 +1,5 @@
- @no_container = true
-- page_title "Environments"
+- page_title _("Environments")
#environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json),
"folder-name" => @folder,
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
index 7ebe617766f..6c0ad34c486 100644
--- a/app/views/projects/environments/index.html.haml
+++ b/app/views/projects/environments/index.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
-- page_title "Environments"
-- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
+- page_title _("Environments")
+- add_to_breadcrumbs(_("Pipelines"), project_pipelines_path(@project))
#environments-list-view{ data: { environments_data: environments_list_data,
"can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index af86b8e8e67..7b847a85686 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -1,12 +1,5 @@
- @no_container = true
-- page_title "Metrics for environment", @environment.name
+- page_title _("Metrics for environment"), @environment.name
.prometheus-container{ class: container_class }
- .top-area
- .row
- .col-sm-6
- %h3
- Environment:
- = link_to @environment.name, environment_path(@environment)
-
#prometheus-graphs{ data: metrics_data(@project, @environment) }
diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml
index 62b08e85e22..c1067fdff78 100644
--- a/app/views/projects/environments/new.html.haml
+++ b/app/views/projects/environments/new.html.haml
@@ -1,9 +1,9 @@
- @no_container = true
-- breadcrumb_title "Environments"
-- page_title 'New Environment'
+- breadcrumb_title _("Environments")
+- page_title _("New Environment")
%div{ class: container_class }
%h3.page-title
- New environment
+ = _("New environment")
%hr
= render 'form'
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index c7890b37381..d59b2d4fb01 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -1,7 +1,7 @@
- @no_container = true
-- add_to_breadcrumbs "Environments", project_environments_path(@project)
+- add_to_breadcrumbs _("Environments"), project_environments_path(@project)
- breadcrumb_title @environment.name
-- page_title "Environments"
+- page_title _("Environments")
%div{ class: container_class }
- if can?(current_user, :stop_environment, @environment)
@@ -10,7 +10,7 @@
.modal-content
.modal-header
%h4.modal-title.d-flex.mw-100
- Stopping
+ = s_("Environments|Stopping")
%span.has-tooltip.text-truncate.ml-1.mr-1.flex-fill{ title: @environment.name, data: { container: '#stop-environment-modal' } }
= @environment.name
?
@@ -40,7 +40,7 @@
= render 'projects/environments/external_url', environment: @environment
= render 'projects/environments/metrics_button', environment: @environment
- if can?(current_user, :update_environment, @environment)
- = link_to 'Edit', edit_project_environment_path(@project, @environment), class: 'btn'
+ = link_to _('Edit'), edit_project_environment_path(@project, @environment), class: 'btn'
- if can?(current_user, :stop_environment, @environment)
= button_tag class: 'btn btn-danger', type: 'button', data: { toggle: 'modal',
target: '#stop-environment-modal' } do
@@ -49,23 +49,22 @@
.environments-container
- if @deployments.blank?
- .blank-state-row
- .blank-state-center
- %h2.blank-state-title
- You don't have any deployments right now.
+ .empty-state
+ .text-content
+ %h4.state-title
+ = _("You don't have any deployments right now.")
%p.blank-state-text
- Define environments in the deploy stage(s) in
- %code .gitlab-ci.yml
- to track deployments here.
- = link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success"
+ = _("Define environments in the deploy stage(s) in <code>.gitlab-ci.yml</code> to track deployments here.").html_safe
+ .text-center
+ = link_to _("Read more"), help_page_path("ci/environments"), class: "btn btn-success"
- else
.table-holder
.ci-table.environments{ role: 'grid' }
.gl-responsive-table-row.table-row-header{ role: 'row' }
- .table-section.section-10{ role: 'columnheader' } ID
- .table-section.section-30{ role: 'columnheader' } Commit
- .table-section.section-25{ role: 'columnheader' } Job
- .table-section.section-15{ role: 'columnheader' } Created
+ .table-section.section-10{ role: 'columnheader' }= _('ID')
+ .table-section.section-30{ role: 'columnheader' }= _('Commit')
+ .table-section.section-25{ role: 'columnheader' }= _('Job')
+ .table-section.section-15{ role: 'columnheader' }= _('Created')
= render @deployments
diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml
index 5b680189bc8..e837d3d56ac 100644
--- a/app/views/projects/environments/terminal.html.haml
+++ b/app/views/projects/environments/terminal.html.haml
@@ -1,15 +1,15 @@
- @no_container = true
-- page_title "Terminal for environment", @environment.name
+- page_title _("Terminal for environment"), @environment.name
- content_for :page_specific_javascripts do
- = stylesheet_link_tag "xterm/xterm"
+ = stylesheet_link_tag "xterm.css"
%div{ class: container_class }
.top-area
.row
.col-sm-6
%h3.page-title
- Terminal for environment
+ = _("Terminal for environment")
= @environment.name
.col-sm-6
diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml
index a966bfb2dd9..996c7b1b960 100644
--- a/app/views/projects/find_file/show.html.haml
+++ b/app/views/projects/find_file/show.html.haml
@@ -13,6 +13,6 @@
.tree-content-holder
.table-holder
- %table.table.files-slider{ class: "table_#{@hex_path} tree-table table-striped" }
+ %table.table.files-slider{ class: "table_#{@hex_path} tree-table" }
%tbody
= spinner nil, true
diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml
index 12cf40bb65f..a69146513d8 100644
--- a/app/views/projects/forks/_fork_button.html.haml
+++ b/app/views/projects/forks/_fork_button.html.haml
@@ -5,7 +5,7 @@
.bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default.forked
= link_to project_path(forked_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
- = project_icon(namespace, class: "avatar s100 identicon")
+ = group_icon(namespace, class: "avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
@@ -18,7 +18,7 @@
class: ("disabled has-tooltip" unless can_create_project),
title: (_('You have reached your project limit') unless can_create_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
- = project_icon(namespace, class: "avatar s100 identicon")
+ = group_icon(namespace, class: "avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index 57afc7ac9c3..b44ea89510b 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -30,11 +30,11 @@
- if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-success' do
= sprite_icon('fork', size: 12)
%span Fork
- else
- = link_to new_project_fork_path(@project), title: "Fork project", class: 'btn btn-new' do
+ = link_to new_project_fork_path(@project), title: "Fork project", class: 'btn btn-success' do
= sprite_icon('fork', size: 12)
%span Fork
diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml
index 3f1974d05f4..b0e22a35fe1 100644
--- a/app/views/projects/graphs/charts.html.haml
+++ b/app/views/projects/graphs/charts.html.haml
@@ -1,5 +1,5 @@
- @no_container = true
-- page_title "Charts"
+- page_title _("Contribution Charts")
.repo-charts{ class: container_class }
%h4.sub-header
diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml
index e51efa85df0..bd8ca5e7d70 100644
--- a/app/views/projects/hook_logs/show.html.haml
+++ b/app/views/projects/hook_logs/show.html.haml
@@ -4,6 +4,6 @@
Request details
.col-lg-9
- = link_to 'Resend Request', retry_project_hook_hook_log_path(@project, @hook, @hook_log), class: "btn btn-default float-right prepend-left-10"
+ = link_to 'Resend Request', retry_project_hook_hook_log_path(@project, @hook, @hook_log), method: :post, class: "btn btn-default float-right prepend-left-10"
= render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log }
diff --git a/app/views/projects/hooks/_index.html.haml b/app/views/projects/hooks/_index.html.haml
index 5990582fd55..0ab7863b77c 100644
--- a/app/views/projects/hooks/_index.html.haml
+++ b/app/views/projects/hooks/_index.html.haml
@@ -9,7 +9,7 @@
.col-lg-8.append-bottom-default
= form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
- = f.submit 'Add webhook', class: 'btn btn-create'
+ = f.submit 'Add webhook', class: 'btn btn-success'
%hr
%h5.prepend-top-default
diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml
index c31aef60453..57311284e11 100644
--- a/app/views/projects/hooks/edit.html.haml
+++ b/app/views/projects/hooks/edit.html.haml
@@ -11,7 +11,7 @@
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: project_hook_path(@project, @hook) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
- = f.submit 'Save changes', class: 'btn btn-create'
+ = f.submit 'Save changes', class: 'btn btn-success'
= render 'shared/web_hooks/test_button', triggers: ProjectHook.triggers, hook: @hook
= link_to 'Remove', project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove float-right', data: { confirm: 'Are you sure?' }
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index 8ce822c43b7..1c50cfbde85 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -16,4 +16,4 @@
= render "shared/import_form", f: f
.form-actions
- = f.submit 'Start import', class: "btn btn-create", tabindex: 4
+ = f.submit 'Start import', class: "btn btn-success", tabindex: 4
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 665968a64e1..4917f4b8903 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -6,8 +6,8 @@
= link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, format: 'json'), data: {original_text: "Close issue", alternative_text: "Comment & close issue"}, class: "btn btn-nr btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
%section.js-vue-notes-event
- #js-vue-notes{ data: { notes_data: notes_data(@issue),
+ #js-vue-notes{ data: { notes_data: notes_data(@issue).to_json,
noteable_data: serialize_issuable(@issue),
noteable_type: 'Issue',
target_type: 'issue',
- current_user_data: UserSerializer.new.represent(current_user, only_path: true).to_json } }
+ current_user_data: UserSerializer.new.represent(current_user, {only_path: true}, CurrentUserEntity).to_json } }
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 8a14146cb87..31c72f2f759 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -2,9 +2,9 @@
.issue-box
- if @can_bulk_update
.issue-check.hidden
- = check_box_tag dom_id(issue, "selected"), nil, false, 'data-id' => issue.id, class: "selected_issue"
- .issue-info-container
- .issue-main-info
+ = check_box_tag dom_id(issue, "selected"), nil, false, 'data-id' => issue.id, class: "selected-issuable"
+ .issuable-info-container
+ .issuable-main-info
.issue-title.title
%span.issue-title-text
- if issue.confidential?
diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml
index 0dd2d2e6c5d..e4a0d4b8479 100644
--- a/app/views/projects/issues/_nav_btns.html.haml
+++ b/app/views/projects/issues/_nav_btns.html.haml
@@ -6,6 +6,6 @@
= link_to "New issue", new_project_issue_path(@project,
issue: { assignee_id: finder.assignee.try(:id),
milestone_id: finder.milestones.first.try(:id) }),
- class: "btn btn-new",
+ class: "btn btn-success",
title: "New issue",
id: "new_issue_link"
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index a678cb6f058..5374f4a1de0 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -8,12 +8,13 @@
- create_branch_path = project_branches_path(@project, branch_name: @issue.to_branch_name, ref: @project.default_branch, issue_iid: @issue.iid)
- refs_path = refs_namespace_project_path(@project.namespace, @project, search: '')
- .create-mr-dropdown-wrap{ data: { can_create_path: can_create_path, create_mr_path: create_mr_path, create_branch_path: create_branch_path, refs_path: refs_path } }
+ .create-mr-dropdown-wrap.d-inline-block{ data: { can_create_path: can_create_path, create_mr_path: create_mr_path, create_branch_path: create_branch_path, refs_path: refs_path } }
.btn-group.unavailable
%button.btn.btn-grouped{ type: 'button', disabled: 'disabled' }
= icon('spinner', class: 'fa-spin')
%span.text
Checking branch availability…
+
.btn-group.available.hidden
%button.btn.js-create-merge-request.btn-success.btn-inverted{ type: 'button', data: { action: data_action } }
= value
diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder
index 6330245954e..6566866be82 100644
--- a/app/views/projects/issues/index.atom.builder
+++ b/app/views/projects/issues/index.atom.builder
@@ -1,3 +1,4 @@
+# rubocop: disable CodeReuse/ActiveRecord
xml.title "#{@project.name} issues"
xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml"
xml.link href: project_issues_url(@project), rel: "alternate", type: "text/html"
@@ -5,3 +6,4 @@ xml.id project_issues_url(@project)
xml.updated @issues.first.updated_at.xmlschema if @issues.reorder(nil).any?
xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
+# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml
index 60fe442014f..9a081a42b6f 100644
--- a/app/views/projects/issues/new.html.haml
+++ b/app/views/projects/issues/new.html.haml
@@ -1,4 +1,5 @@
-- breadcrumb_title "Issues"
+- add_to_breadcrumbs "Issues", project_issues_path(@project)
+- breadcrumb_title "New"
- page_title "New Issue"
%h3.page-title
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 2d036bd4e3e..b50b3ca207b 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -6,6 +6,7 @@
- page_card_attributes @issue.card_attributes
- can_update_issue = can?(current_user, :update_issue, @issue)
+- can_reopen_issue = can?(current_user, :reopen_issue, @issue)
- can_report_spam = @issue.submittable_as_spam_by?(current_user)
- can_create_issue = show_new_issue_link?(@project)
@@ -40,6 +41,7 @@
%li= link_to 'Report abuse', new_abuse_report_path(user_id: @issue.author.id, ref_url: issue_url(@issue))
- if can_update_issue
%li= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), class: "btn-close js-btn-issue-action #{issue_button_visibility(@issue, true)}", title: 'Close issue'
+ - if can_reopen_issue
%li= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), class: "btn-reopen js-btn-issue-action #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
- if can_report_spam
%li= link_to 'Submit as spam', mark_as_spam_project_issue_path(@project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
@@ -48,12 +50,12 @@
%li.divider
%li= link_to 'New issue', new_project_issue_path(@project), title: 'New issue', id: 'new_issue_link'
- = render 'shared/issuable/close_reopen_button', issuable: @issue, can_update: can_update_issue
+ = render 'shared/issuable/close_reopen_button', issuable: @issue, can_update: can_update_issue, can_reopen: can_reopen_issue
- if can_report_spam
= link_to 'Submit as spam', mark_as_spam_project_issue_path(@project, @issue), method: :post, class: 'd-none d-sm-none d-md-block btn btn-grouped btn-spam', title: 'Submit as spam'
- if can_create_issue
- = link_to new_project_issue_path(@project), class: 'd-none d-sm-none d-md-block btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do
+ = link_to new_project_issue_path(@project), class: 'd-none d-sm-none d-md-block btn btn-grouped new-issue-link btn-success btn-inverted', title: 'New issue', id: 'new_issue_link' do
New issue
.issue-details.issuable-details
@@ -75,11 +77,12 @@
#related-branches{ data: { url: related_branches_project_issue_path(@project, @issue) } }
// This element is filled in using JavaScript.
- .content-block.emoji-block
+ .content-block.emoji-block.emoji-block-sticky
.row
- .col-sm-8.js-noteable-awards
+ .col-md-12.col-lg-6.js-noteable-awards
= render 'award_emoji/awards_block', awardable: @issue, inline: true
- .col-sm-4.new-branch-col
+ .col-md-12.col-lg-6.new-branch-col
+ #js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(@issue), notes_filters: UserPreference.notes_filters.to_json } }
= render 'new_branch' unless @issue.confidential?
%section.issuable-discussion
diff --git a/app/views/projects/jobs/_empty_state.html.haml b/app/views/projects/jobs/_empty_state.html.haml
deleted file mode 100644
index ea552c73c92..00000000000
--- a/app/views/projects/jobs/_empty_state.html.haml
+++ /dev/null
@@ -1,18 +0,0 @@
-- illustration = local_assigns.fetch(:illustration)
-- illustration_size = local_assigns.fetch(:illustration_size)
-- title = local_assigns.fetch(:title)
-- content = local_assigns.fetch(:content, nil)
-- action = local_assigns.fetch(:action, nil)
-
-.row.empty-state
- .col-12
- .svg-content{ class: illustration_size }
- = image_tag illustration
- .col-12
- .text-content
- %h4.text-center= title
- - if content
- %p= content
- - if action
- .text-center
- = action
diff --git a/app/views/projects/jobs/_empty_states.html.haml b/app/views/projects/jobs/_empty_states.html.haml
deleted file mode 100644
index e5198d047df..00000000000
--- a/app/views/projects/jobs/_empty_states.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-- detailed_status = @build.detailed_status(current_user)
-- illustration = detailed_status.illustration
-
-= render 'empty_state',
- illustration: illustration[:image],
- illustration_size: illustration[:size],
- title: illustration[:title],
- content: illustration[:content],
- action: detailed_status.has_action? ? link_to(detailed_status.action_button_title, detailed_status.action_path, method: detailed_status.action_method, class: 'btn btn-primary', title: detailed_status.action_button_title) : nil
diff --git a/app/views/projects/jobs/_header.html.haml b/app/views/projects/jobs/_header.html.haml
index b83e8dddccb..018ff093475 100644
--- a/app/views/projects/jobs/_header.html.haml
+++ b/app/views/projects/jobs/_header.html.haml
@@ -1,4 +1,3 @@
-- show_controls = local_assigns.fetch(:show_controls, true)
- pipeline = @build.pipeline
.content-block.build-header.top-area.page-content-header
@@ -20,12 +19,3 @@
= render "projects/jobs/user" if @build.user
= time_ago_with_tooltip(@build.created_at)
-
- - if show_controls
- .nav-controls
- - if can?(current_user, :create_issue, @project) && @build.failed?
- = link_to "New issue", new_project_issue_path(@project, issue: build_failed_issue_options), class: 'btn btn-new btn-inverted'
- - if can?(current_user, :update_build, @build) && @build.retryable?
- = link_to "Retry job", retry_project_job_path(@project, @build), class: 'btn btn-inverted-secondary', method: :post
- %button.btn.btn-default.float-right.d-block.d-sm-none.d-md-none.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
- = icon('angle-double-left')
diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml
deleted file mode 100644
index 86b2b8bf2f7..00000000000
--- a/app/views/projects/jobs/_sidebar.html.haml
+++ /dev/null
@@ -1,95 +0,0 @@
-%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" } }
- .sidebar-container
- .blocks-container
- #js-details-block-vue{ data: { terminal_path: can?(current_user, :create_build_terminal, @build) && @build.has_terminal? ? terminal_project_job_path(@project, @build) : nil } }
-
- - if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
- .block
- .title
- Job artifacts
- - if @build.artifacts_expired?
- %p.build-detail-row
- The artifacts were removed
- #{time_ago_with_tooltip(@build.artifacts_expire_at)}
- - elsif @build.has_expiring_artifacts?
- %p.build-detail-row
- The artifacts will be removed
- #{time_ago_with_tooltip(@build.artifacts_expire_at)}
-
- - if @build.artifacts?
- .btn-group.d-flex{ role: :group }
- - if @build.has_expiring_artifacts? && can?(current_user, :update_build, @build)
- = link_to keep_project_job_artifacts_path(@project, @build), class: 'btn btn-sm btn-default', method: :post do
- Keep
-
- = link_to download_project_job_artifacts_path(@project, @build), rel: 'nofollow', download: '', class: 'btn btn-sm btn-default' do
- Download
-
- - if @build.browsable_artifacts?
- = link_to browse_project_job_artifacts_path(@project, @build), class: 'btn btn-sm btn-default' do
- Browse
-
- - if @build.trigger_request
- .build-widget.block
- %h4.title
- Trigger
-
- - if @build.trigger_request&.trigger&.short_token
- %p
- %span.build-light-text Token:
- #{@build.trigger_request.trigger.short_token}
-
- - if @build.trigger_variables.any?
- %p
- %button.btn.group.js-reveal-variables Reveal Variables
-
- %dl.js-build-variables.trigger-build-variables.hide
- - @build.trigger_variables.each do |trigger_variable|
- %dt.js-build-variable.trigger-build-variable= trigger_variable[:key]
- %dd.js-build-value.trigger-build-value= trigger_variable[:value]
-
- %div{ class: (@build.pipeline.stages_count > 1 ? "block" : "block-last") }
- %p
- Commit
- = link_to @build.pipeline.short_sha, project_commit_path(@project, @build.pipeline.sha), class: 'commit-sha link-commit'
- = clipboard_button(text: @build.pipeline.short_sha, title: "Copy commit SHA to clipboard")
- - if @build.merge_request
- in
- = link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request), class: 'link-commit'
-
- %p.build-light-text.append-bottom-0
- #{@build.pipeline.git_commit_title}
-
- - if @build.pipeline.stages_count > 1
- .block-last.dropdown.build-dropdown
- %div
- %span{ class: "ci-status-icon-#{@build.pipeline.status}" }
- = ci_icon_for_status(@build.pipeline.status)
- Pipeline
- = link_to "##{@build.pipeline.id}", project_pipeline_path(@project, @build.pipeline), class: 'link-commit'
- from
- = link_to "#{@build.pipeline.ref}", project_ref_path(@project, @build.pipeline.ref), class: 'link-commit ref-name'
- %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
- %span.stage-selection More
- = icon('chevron-down')
- %ul.dropdown-menu
- - @build.pipeline.legacy_stages.each do |stage|
- %li
- %a.stage-item= stage.name
-
- .builds-container
- - HasStatus::ORDERED_STATUSES.each do |build_status|
- - builds.select{|build| build.status == build_status}.each do |build|
- .build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } }
- - tooltip = sanitize(build.tooltip_message.dup)
- = link_to(project_job_path(@project, build), data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' }) do
- = sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right')
- %span{ class: "ci-status-icon-#{build.status}" }
- = ci_icon_for_status(build.status)
- %span
- - if build.name
- = build.name
- - else
- = build.id
- - if build.retried?
- = sprite_icon('retry', size:16, css_class: 'icon-retry')
diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml
index fe1c338b634..59592abcf6a 100644
--- a/app/views/projects/jobs/index.html.haml
+++ b/app/views/projects/jobs/index.html.haml
@@ -8,7 +8,7 @@
.nav-controls
- if can?(current_user, :update_build, @project)
- - if @all_builds.running_or_pending.limit(1).any?
+ - if @all_builds.running_or_pending.limit(1).any? # rubocop: disable CodeReuse/ActiveRecord
= link_to 'Cancel running', cancel_all_project_jobs_path(@project),
data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml
index 078f40c4477..475bae887ec 100644
--- a/app/views/projects/jobs/show.html.haml
+++ b/app/views/projects/jobs/show.html.haml
@@ -1,97 +1,13 @@
- @no_container = true
-- add_to_breadcrumbs "Jobs", project_jobs_path(@project)
+- add_to_breadcrumbs _("Jobs"), project_jobs_path(@project)
- breadcrumb_title "##{@build.id}"
-- page_title "#{@build.name} (##{@build.id})", "Jobs"
+- page_title "#{@build.name} (##{@build.id})", _("Jobs")
-%div{ class: container_class }
- .build-page.js-build-page
- #js-build-header-vue
- - if @build.stuck?
- - unless @build.any_runners_online?
- .bs-callout.bs-callout-warning.js-build-stuck
- %p
- - if no_runners_for_project?(@build.project)
- This job is stuck, because the project doesn't have any runners online assigned to it.
- - elsif @build.tags.any?
- This job is stuck, because you don't have any active runners online with any of these tags assigned to them:
- - @build.tags.each do |tag|
- %span.badge.badge-primary
- = tag
- - else
- This job is stuck, because you don't have any active runners that can run this job.
-
- %br
- Go to
- = link_to project_runners_path(@build.project, anchor: 'js-runners-settings') do
- Runners page
-
- - if @build.starts_environment?
- .prepend-top-default.js-environment-container
- .environment-information
- - if @build.outdated_deployment?
- = ci_icon_for_status('success_with_warnings')
- - else
- = ci_icon_for_status(@build.status)
-
- - environment = environment_for_build(@build.project, @build)
- - if @build.success? && @build.last_deployment.present?
- - if @build.last_deployment.last?
- This job is the most recent deployment to #{environment_link_for_build(@build.project, @build)}.
- - else
- This job is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}.
- View the most recent deployment #{deployment_link(environment.last_deployment)}.
- - elsif @build.complete? && !@build.success?
- The deployment of this job to #{environment_link_for_build(@build.project, @build)} did not succeed.
- - else
- This job is creating a deployment to #{environment_link_for_build(@build.project, @build)}
- - if environment.try(:last_deployment)
- and will overwrite the #{deployment_link(environment.last_deployment, text: 'latest deployment')}
-
- - if @build.erased?
- .prepend-top-default.js-build-erased
- .erased.alert.alert-warning
- - if @build.erased_by_user?
- Job has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)}
- - else
- Job has been erased #{time_ago_with_tooltip(@build.erased_at)}
-
- - if @build.running? || @build.has_trace?
- .build-trace-container.prepend-top-default
- .top-bar.js-top-bar
- .js-truncated-info.truncated-info.d-none.d-sm-block.float-left.hidden<
- Showing last
- %span.js-truncated-info-size.truncated-info-size><
- of log -
- %a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw
+- content_for :page_specific_javascripts do
+ = stylesheet_link_tag 'page_bundles/xterm'
- .controllers.float-right
- - if @build.has_trace?
- = link_to raw_project_job_path(@project, @build),
- title: 'Show complete raw',
- data: { placement: 'top', container: 'body' },
- class: 'js-raw-link-controller has-tooltip controllers-buttons' do
- = icon('file-text-o')
-
- - if @build.erasable? && can?(current_user, :erase_build, @build)
- = link_to erase_project_job_path(@project, @build),
- method: :post,
- data: { confirm: 'Are you sure you want to erase this build?', placement: 'top', container: 'body' },
- title: 'Erase job log',
- class: 'has-tooltip js-erase-link controllers-buttons' do
- = icon('trash')
- .has-tooltip.controllers-buttons{ title: 'Scroll to top', data: { placement: 'top', container: 'body'} }
- %button.js-scroll-up.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true }
- = custom_icon('scroll_up')
- .has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} }
- %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true }
- = custom_icon('scroll_down')
-
- = render 'shared/builds/build_output'
- - else
- = render "empty_states"
-
- = render "sidebar", builds: @builds
-
-.js-build-options{ data: javascript_build_options }
-
-#js-job-details-vue{ data: { endpoint: project_job_path(@project, @build, format: :json), runner_help_url: help_page_path('ci/runners/README.html', anchor: 'setting-maximum-job-timeout-for-a-runner') } }
+%div{ class: container_class }
+ #js-job-vue-app{ data: { endpoint: project_job_path(@project, @build, format: :json),
+ runner_help_url: help_page_path('ci/runners/README.html', anchor: 'setting-maximum-job-timeout-for-a-runner'),
+ runner_settings_url: project_runners_path(@build.project, anchor: 'js-runners-settings'),
+ build_options: javascript_build_options } }
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 768ce9bd103..2c6484c2c99 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,33 +1,26 @@
- @no_container = true
- page_title "Labels"
- can_admin_label = can?(current_user, :admin_label, @project)
-- hide_class = ''
- search = params[:search]
+- subscribed = params[:subscribed]
+- labels_or_filters = @labels.exists? || @prioritized_labels.exists? || search.present? || subscribed.present?
-- if can_admin_label
+- if @labels.present? && can_admin_label
- content_for(:header_content) do
.nav-controls
- = link_to _('New label'), new_project_label_path(@project), class: "btn btn-new"
+ = link_to _('New label'), new_project_label_path(@project), class: "btn btn-success qa-label-create-new"
-- if @labels.exists? || @prioritized_labels.exists? || search.present?
+- if labels_or_filters
#promote-label-modal
%div{ class: container_class }
- .top-area.adjust
- .nav-text
- = _('Labels can be applied to issues and merge requests.')
-
- .nav-controls
- = form_tag project_labels_path(@project), method: :get do
- .input-group
- = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false }
- %span.input-group-append
- %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') }
- = icon("search")
+ = render 'shared/labels/nav'
.labels-container.prepend-top-10
- if can_admin_label
- if search.blank?
%p.text-muted
+ = _('Labels can be applied to issues and merge requests.')
+ %br
= _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.')
-# Only show it in the first page
- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
@@ -59,7 +52,9 @@
- else
.nothing-here-block
= _('No labels with such name or description')
-
+ - elsif subscribed.present?
+ .nothing-here-block
+ = _('You do not have any subscriptions yet')
- else
= render 'shared/empty_states/labels'
diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml
index 37c09f12f63..d0a7f89df31 100644
--- a/app/views/projects/mattermosts/_team_selection.html.haml
+++ b/app/views/projects/mattermosts/_team_selection.html.haml
@@ -43,4 +43,4 @@
.clearfix
.float-right
= link_to 'Cancel', edit_project_service_path(@project, @service), class: 'btn btn-lg'
- = f.submit 'Install', class: 'btn btn-save btn-lg'
+ = f.submit 'Install', class: 'btn btn-success btn-lg'
diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml
index 5a59f956cb5..13b967beba1 100644
--- a/app/views/projects/merge_requests/_form.html.haml
+++ b/app/views/projects/merge_requests/_form.html.haml
@@ -1,4 +1,4 @@
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request],
html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' },
data: { markdown_version: @merge_request.cached_markdown_version } do |f|
- = render 'shared/issuable/form', f: f, issuable: @merge_request
+ = render 'shared/issuable/form', f: f, issuable: @merge_request, presenter: @mr_presenter
diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml
index 62dd21ef6e0..15499c89ffb 100644
--- a/app/views/projects/merge_requests/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/_how_to_merge.html.haml
@@ -1,5 +1,5 @@
#modal_merge_info.modal{ tabindex: '-1' }
- .modal-dialog
+ .modal-dialog.modal-lg
.modal-content
.modal-header
%h3.modal-title Check out, review, and merge locally
@@ -30,11 +30,13 @@
%pre.dark#merge-info-3
- if @merge_request.for_fork?
:preserve
- git checkout #{h @merge_request.target_branch}
+ git fetch origin
+ git checkout origin/#{h @merge_request.target_branch}
git merge --no-ff #{h @merge_request.source_project_path}-#{h @merge_request.source_branch}
- else
:preserve
- git checkout #{h @merge_request.target_branch}
+ git fetch origin
+ git checkout origin/#{h @merge_request.target_branch}
git merge --no-ff #{h @merge_request.source_branch}
%p
%strong Step 4.
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index cd3d896fff2..faa070d0389 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -1,10 +1,10 @@
%li{ id: dom_id(merge_request), class: mr_css_classes(merge_request), data: { labels: merge_request.label_ids, id: merge_request.id } }
- if @can_bulk_update
.issue-check.hidden
- = check_box_tag dom_id(merge_request, "selected"), nil, false, 'data-id' => merge_request.id, class: "selected_issue"
+ = check_box_tag dom_id(merge_request, "selected"), nil, false, 'data-id' => merge_request.id, class: "selected-issuable"
- .issue-info-container
- .issue-main-info
+ .issuable-info-container
+ .issuable-main-info
.merge-request-title.title
%span.merge-request-title-text
= link_to merge_request.title, merge_request_path(merge_request)
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index a58179091ae..3cd83feb842 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -37,6 +37,6 @@
= link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request'
- if can_update_merge_request
- = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-sm-none d-md-block btn btn-grouped js-issuable-edit"
+ = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-sm-none d-md-block btn btn-grouped js-issuable-edit qa-edit-button"
- = render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request
+ = render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request, can_reopen: can_update_merge_request
diff --git a/app/views/projects/merge_requests/_nav_btns.html.haml b/app/views/projects/merge_requests/_nav_btns.html.haml
index e73dab8ad4a..b7498216334 100644
--- a/app/views/projects/merge_requests/_nav_btns.html.haml
+++ b/app/views/projects/merge_requests/_nav_btns.html.haml
@@ -1,5 +1,5 @@
- if @can_bulk_update
= button_tag "Edit merge requests", class: "btn append-right-10 js-bulk-update-toggle"
- if merge_project
- = link_to new_merge_request_path, class: "btn btn-new", title: "New merge request" do
+ = link_to new_merge_request_path, class: "btn btn-success", title: "New merge request" do
New merge request
diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml
index afa7eb06cb4..1fd71a38472 100644
--- a/app/views/projects/merge_requests/creations/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml
@@ -61,4 +61,4 @@
- if @merge_request.errors.any?
= form_errors(@merge_request)
- = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn"
+ = f.submit 'Compare branches and continue', class: "btn btn-success mr-compare-btn"
diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml
index f7a5d85500f..464f8fa65e9 100644
--- a/app/views/projects/merge_requests/creations/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml
@@ -11,7 +11,7 @@
= link_to 'Change branches', mr_change_branches_path(@merge_request)
%hr
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
- = render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits
+ = render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits, presenter: @mr_presenter
= f.hidden_field :source_project_id
= f.hidden_field :source_branch
= f.hidden_field :target_project_id
@@ -33,7 +33,7 @@
%li.commits-tab.new-tab
= link_to url_for(safe_params), data: {target: 'div#commits', action: 'new', toggle: 'tabvue'} do
Commits
- %span.badge.badge-pill= @commits.size
+ %span.badge.badge-pill= @total_commit_count
- if @pipelines.any?
%li.builds-tab
= link_to url_for(safe_params.merge(action: 'pipelines')), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tabvue'} do
diff --git a/app/views/projects/merge_requests/creations/new.html.haml b/app/views/projects/merge_requests/creations/new.html.haml
index 3220512d60d..0f618826305 100644
--- a/app/views/projects/merge_requests/creations/new.html.haml
+++ b/app/views/projects/merge_requests/creations/new.html.haml
@@ -1,4 +1,5 @@
-- breadcrumb_title "Merge Requests"
+- add_to_breadcrumbs "Merge Requests", project_merge_requests_path(@project)
+- breadcrumb_title "New"
- page_title "New Merge Request"
- if @merge_request.can_be_created && !params[:change_branches]
diff --git a/app/views/projects/merge_requests/diffs/_commit_widget.html.haml b/app/views/projects/merge_requests/diffs/_commit_widget.html.haml
index dab95b97346..066c8d5dba6 100644
--- a/app/views/projects/merge_requests/diffs/_commit_widget.html.haml
+++ b/app/views/projects/merge_requests/diffs/_commit_widget.html.haml
@@ -1,3 +1,7 @@
+-#-----------------------------------------------------------------
+ WARNING: Please keep changes up-to-date with the following files:
+ - `assets/javascripts/diffs/components/commit_widget.vue`
+-#-----------------------------------------------------------------
- if @commit
.info-well.d-none.d-sm-block.prepend-top-default
.well-segment
diff --git a/app/views/projects/merge_requests/diffs/_diffs.html.haml b/app/views/projects/merge_requests/diffs/_diffs.html.haml
index bf3df0abf86..9ebd91dea0b 100644
--- a/app/views/projects/merge_requests/diffs/_diffs.html.haml
+++ b/app/views/projects/merge_requests/diffs/_diffs.html.haml
@@ -14,7 +14,7 @@
%span.ref-name= @merge_request.source_branch
and
%span.ref-name= @merge_request.target_branch
- .text-center= link_to 'Create commit', project_new_blob_path(@project, @merge_request.source_branch), class: 'btn btn-save'
+ .text-center= link_to 'Create commit', project_new_blob_path(@project, @merge_request.source_branch), class: 'btn btn-success'
- else
- diff_viewable = @merge_request_diff ? @merge_request_diff.viewable? : true
- if diff_viewable
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index b23baa22d8b..515499956a2 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -21,6 +21,7 @@
window.gl.mrWidgetData = #{serialize_issuable(@merge_request, serializer: 'widget')}
window.gl.mrWidgetData.squash_before_merge_help_path = '#{help_page_path("user/project/merge_requests/squash_and_merge")}';
+ window.gl.mrWidgetData.troubleshooting_docs_path = '#{help_page_path('user/project/merge_requests', anchor: 'troubleshooting')}';
#js-vue-mr-widget.mr-widget
@@ -33,7 +34,7 @@
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.merge-request-tabs.nav-tabs.nav.nav-links.scrolling-tabs
- %li.notes-tab
+ %li.notes-tab.qa-notes-tab
= tab_link_for @merge_request, :show, force_link: @commit.present? do
Discussion
%span.badge.badge-pill= @merge_request.related_notes.user.count
@@ -47,12 +48,14 @@
= tab_link_for @merge_request, :pipelines do
Pipelines
%span.badge.badge-pill.js-pipelines-mr-count= @pipelines.size
- %li.diffs-tab
+ %li.diffs-tab.qa-diffs-tab
= tab_link_for @merge_request, :diffs do
Changes
%span.badge.badge-pill= @merge_request.diff_size
-
- #js-vue-discussion-counter
+ .d-inline-flex.flex-wrap
+ #js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(@merge_request),
+ notes_filters: UserPreference.notes_filters.to_json } }
+ #js-vue-discussion-counter
.tab-content#diff-notes-app
#notes.notes.tab-pane.voting_notes
@@ -60,7 +63,7 @@
%section.col-md-12
%script.js-notes-data{ type: "application/json" }= initial_notes_data(true).to_json.html_safe
.issuable-discussion.js-vue-notes-event
- #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request),
+ #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json,
noteable_data: serialize_issuable(@merge_request),
noteable_type: 'MergeRequest',
target_type: 'merge_request',
diff --git a/app/views/projects/milestones/_deprecation_message.html.haml b/app/views/projects/milestones/_deprecation_message.html.haml
new file mode 100644
index 00000000000..b2cca3690d6
--- /dev/null
+++ b/app/views/projects/milestones/_deprecation_message.html.haml
@@ -0,0 +1,7 @@
+.banner-callout.compact.milestone-deprecation-message.prepend-top-20
+ .banner-graphic= image_tag 'illustrations/milestone_removing-page.svg'
+ .banner-body.prepend-left-10.append-right-10
+ %h5.banner-title.prepend-top-0
+ = _('The tabs below will be removed in a future version')
+ %p.milestone-banner-text
+ = _('Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}.').html_safe % { issue_boards_url: link_to(_('issue boards'), help_page_url('user/project/issue_board'), target: '_blank', rel: 'noopener noreferrer'), gitlab_issues_url: link_to(_('GitLab’s issue tracker'), 'https://gitlab.com/gitlab-org/gitlab-ce/issues', target: '_blank', rel: 'noopener noreferrer') }
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 28f0a167128..ebd3229e42b 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -20,8 +20,8 @@
.form-actions
- if @milestone.new_record?
- = f.submit 'Create milestone', class: "btn-create btn qa-milestone-create-button"
+ = f.submit 'Create milestone', class: "btn-success btn qa-milestone-create-button"
= link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel"
- else
- = f.submit 'Save changes', class: "btn-save btn"
+ = f.submit 'Save changes', class: "btn-success btn"
= link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn btn-cancel"
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index 26d2ea8447b..57f3c640696 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -8,7 +8,7 @@
.nav-controls
= render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestone, @project)
- = link_to new_project_milestone_path(@project), class: "btn btn-new qa-new-project-milestone", title: 'New milestone' do
+ = link_to new_project_milestone_path(@project), class: "btn btn-success qa-new-project-milestone", title: 'New milestone' do
New milestone
.milestones
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 2a9e20c2caa..5859de61d71 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -43,18 +43,7 @@
- else
= link_to 'Reopen milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
- %button.js-delete-milestone-button.btn.btn-grouped.btn-danger{ data: { toggle: 'modal',
- target: '#delete-milestone-modal',
- milestone_id: @milestone.id,
- milestone_title: markdown_field(@milestone, :title),
- milestone_url: project_milestone_path(@project, @milestone),
- milestone_issue_count: @milestone.issues.count,
- milestone_merge_request_count: @milestone.merge_requests.count },
- disabled: true }
- = _('Delete')
- = icon('spin spinner', class: 'js-loading-icon hidden' )
-
- #delete-milestone-modal
+ = render 'shared/milestones/delete_button'
%a.btn.btn-default.btn-grouped.float-right.d-block.d-sm-none.js-sidebar-toggle{ href: "#" }
= icon('angle-double-left')
@@ -78,5 +67,6 @@
.alert.alert-success.prepend-top-default
%span All issues for this milestone are closed. You may close this milestone now.
+ = render 'deprecation_message'
= render 'shared/milestones/tabs', milestone: @milestone
= render 'shared/milestones/sidebar', milestone: @milestone, project: @project, affix_offset: 153
diff --git a/app/views/projects/mirrors/_instructions.html.haml b/app/views/projects/mirrors/_instructions.html.haml
index 3d811be3fe3..35a6885318a 100644
--- a/app/views/projects/mirrors/_instructions.html.haml
+++ b/app/views/projects/mirrors/_instructions.html.haml
@@ -4,7 +4,9 @@
= _('The repository must be accessible over <code>http://</code>,
<code>https://</code>, <code>ssh://</code> and <code>git://</code>.').html_safe
%li= _('Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>.').html_safe
- %li= _('The update action will time out after 15 minutes. For big repositories, use a clone/push combination.')
+ %li
+ - minutes = Gitlab.config.gitlab_shell.git_timeout / 60
+ = _("The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination.") % { number_of_minutes: minutes }
%li= _('The Git LFS objects will <strong>not</strong> be synced.').html_safe
%li
= _('This user will be the author of all events in the activity feed that are the result of an update,
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index 53387b3a50c..d523df1cd90 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -1,7 +1,7 @@
- expanded = Rails.env.test?
- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
-%section.settings.project-mirror-settings.js-mirror-settings.no-animate{ class: ('expanded' if expanded) }
+%section.settings.project-mirror-settings.js-mirror-settings.no-animate#js-push-remote-settings{ class: ('expanded' if expanded) }
.settings-header
%h4= _('Mirroring repositories')
%button.btn.js-settings-toggle
@@ -32,7 +32,7 @@
= link_to icon('question-circle'), help_page_path('user/project/protected_branches')
.panel-footer
- = f.submit _('Mirror repository'), class: 'btn btn-create', name: :update_remote_mirror
+ = f.submit _('Mirror repository'), class: 'btn btn-success', name: :update_remote_mirror
.panel.panel-default
.table-responsive
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 6c363345e38..eede8704564 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -1,9 +1,7 @@
-- @breadcrumb_link = dashboard_projects_path
-- breadcrumb_title "Projects"
+- @hide_breadcrumbs = true
- @hide_top_links = true
- page_title 'New Project'
- header_title "Projects", dashboard_projects_path
-- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
- active_tab = local_assigns.fetch(:active_tab, 'blank')
.project-edit-container
@@ -30,15 +28,15 @@
.col-lg-9.js-toggle-container
%ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' }
%li.nav-item{ role: 'presentation' }
- %a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' }
+ %a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab', track_label: 'blank_project', track_event: "click_tab" }, role: 'tab' }
%span.d-none.d-sm-block Blank project
%span.d-block.d-sm-none Blank
%li.nav-item{ role: 'presentation' }
- %a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' }
+ %a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab', track_label: 'create_from_template', track_event: "click_tab" }, role: 'tab' }
%span.d-none.d-sm-block Create from template
%span.d-block.d-sm-none Template
%li.nav-item{ role: 'presentation' }
- %a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
+ %a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab', track_label: 'import_project', track_event: "click_tab" }, role: 'tab' }
%span.d-none.d-sm-block Import project
%span.d-block.d-sm-none Import
diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml
index b4fe1cabdfd..eb6838cec8d 100644
--- a/app/views/projects/notes/_actions.html.haml
+++ b/app/views/projects/notes/_actions.html.haml
@@ -38,9 +38,8 @@
- if can?(current_user, :award_emoji, note)
- if note.emoji_awardable?
- - user_authored = note.user_authored?(current_user)
.note-actions-item
- = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored} has-tooltip btn btn-transparent", data: { position: 'right', container: 'body' } do
+ = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji} has-tooltip btn btn-transparent", data: { position: 'right', container: 'body' } do
= icon('spinner spin')
%span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face')
%span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley')
diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml
index 88085c7185b..8de84f82e9f 100644
--- a/app/views/projects/notes/_more_actions_dropdown.html.haml
+++ b/app/views/projects/notes/_more_actions_dropdown.html.haml
@@ -11,8 +11,9 @@
- unless is_current_user
%li
= link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
- Report as abuse
+ = _('Report abuse to GitLab')
- if note_editable
%li
= link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
- %span.text-danger Delete comment
+ %span.text-danger
+ = _('Delete comment')
diff --git a/app/views/projects/pages/_https_only.html.haml b/app/views/projects/pages/_https_only.html.haml
index 57345edb90b..ce3ef29c32e 100644
--- a/app/views/projects/pages/_https_only.html.haml
+++ b/app/views/projects/pages/_https_only.html.haml
@@ -1,9 +1,9 @@
= form_for @project, url: namespace_project_pages_path(@project.namespace.becomes(Namespace), @project), html: { class: 'inline', title: pages_https_only_title } do |f|
- = f.check_box :pages_https_only, class: 'float-left', disabled: pages_https_only_disabled?
-
- .prepend-left-20
- = f.label :pages_https_only, class: pages_https_only_label_class do
- %strong Force domains with SSL certificates to use HTTPS
+ .form-group
+ .form-check
+ = f.check_box :pages_https_only, class: 'form-check-input', disabled: pages_https_only_disabled?
+ = f.label :pages_https_only, class: pages_https_only_label_class do
+ %strong Force domains with SSL certificates to use HTTPS
- unless pages_https_only_disabled?
.prepend-top-10
diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml
index e7178f9160c..2427b4d7611 100644
--- a/app/views/projects/pages/_list.html.haml
+++ b/app/views/projects/pages/_list.html.haml
@@ -4,9 +4,9 @@
.card
.card-header
Domains (#{@domains.count})
- %ul.content-list.pages-domain-list{ class: ("has-verification-status" if verification_enabled) }
+ %ul.list-group.list-group-flush.pages-domain-list{ class: ("has-verification-status" if verification_enabled) }
- @domains.each do |domain|
- %li.pages-domain-list-item.unstyled
+ %li.pages-domain-list-item.list-group-item.d-flex.justify-content-between
- if verification_enabled
- tooltip, status = domain.unverified? ? [_('Unverified'), 'failed'] : [_('Verified'), 'success']
.domain-status.ci-status-icon.has-tooltip{ class: "ci-status-icon-#{status}", title: tooltip }
@@ -16,7 +16,7 @@
= domain.url
= icon('external-link')
- if domain.subject
- %p
+ %div
%span.badge.badge-gray Certificate: #{domain.subject}
- if domain.expired?
%span.badge.badge-danger Expired
@@ -24,6 +24,6 @@
= link_to 'Details', project_pages_domain_path(@project, domain), class: "btn btn-sm btn-grouped"
= link_to 'Remove', project_pages_domain_path(@project, domain), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
- if verification_enabled && domain.unverified?
- %li.warning-row
+ %li.list-group-item.bs-callout-warning
#{domain.domain} is not verified. To learn how to verify ownership, visit your
#{link_to 'domain details', project_pages_domain_path(@project, domain)}.
diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml
index 7e1a3b9bea6..88ab486a248 100644
--- a/app/views/projects/pages/show.html.haml
+++ b/app/views/projects/pages/show.html.haml
@@ -4,7 +4,7 @@
Pages
- if can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https)
- = link_to new_project_pages_domain_path(@project), class: 'btn btn-new float-right', title: 'New Domain' do
+ = link_to new_project_pages_domain_path(@project), class: 'btn btn-success float-right', title: 'New Domain' do
New Domain
%p.light
diff --git a/app/views/projects/pages_domains/edit.html.haml b/app/views/projects/pages_domains/edit.html.haml
index ee70de22f13..342b1482df7 100644
--- a/app/views/projects/pages_domains/edit.html.haml
+++ b/app/views/projects/pages_domains/edit.html.haml
@@ -8,4 +8,4 @@
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
.form-actions
- = f.submit 'Save Changes', class: "btn btn-save"
+ = f.submit 'Save Changes', class: "btn btn-success"
diff --git a/app/views/projects/pages_domains/new.html.haml b/app/views/projects/pages_domains/new.html.haml
index 376ce3f68aa..94ad1470052 100644
--- a/app/views/projects/pages_domains/new.html.haml
+++ b/app/views/projects/pages_domains/new.html.haml
@@ -7,6 +7,6 @@
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
.form-actions
- = f.submit 'Create New Domain', class: "btn btn-save"
+ = f.submit 'Create New Domain', class: "btn btn-success"
.float-right
= link_to _('Cancel'), project_pages_path(@project), class: 'btn btn-cancel'
diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml
index 9a981d53ab6..259979417e0 100644
--- a/app/views/projects/pipeline_schedules/_form.html.haml
+++ b/app/views/projects/pipeline_schedules/_form.html.haml
@@ -39,5 +39,5 @@
= f.check_box :active, required: false, value: @schedule.active?
= _('Active')
.footer-block.row-content-block
- = f.submit _('Save pipeline schedule'), class: 'btn btn-create', tabindex: 3
+ = f.submit _('Save pipeline schedule'), class: 'btn btn-success', tabindex: 3
= link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn btn-cancel'
diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml
index 3677666070e..0580c15ad15 100644
--- a/app/views/projects/pipeline_schedules/index.html.haml
+++ b/app/views/projects/pipeline_schedules/index.html.haml
@@ -11,7 +11,7 @@
- if can?(current_user, :create_pipeline_schedule, @project)
.nav-controls
- = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do
+ = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-success' do
%span= _('New schedule')
- if @schedules.present?
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index ccb83148ded..2575efc0981 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -13,7 +13,11 @@
= pluralize @pipeline.total_size, "job"
- if @pipeline.ref
from
- = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name"
+ - if @pipeline.ref_exists?
+ = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name"
+ - else
+ %span.ref-name
+ = @pipeline.ref
- if @pipeline.duration
in
= time_interval_in_words(@pipeline.duration)
@@ -30,5 +34,3 @@
%span.js-details-content.hide
= link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full"
= clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
-
- = render_if_exists "projects/pipelines/info_extension", pipeline: @pipeline
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index c63ff070f70..66e202103a9 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -19,30 +19,23 @@
#js-pipeline-graph-vue
#js-tab-builds.tab-pane
- - if pipeline.yaml_errors.present?
- .bs-callout.bs-callout-danger
- %h4 Found errors in your .gitlab-ci.yml:
- %ul
- - pipeline.yaml_errors.split(",").each do |error|
- %li= error
- You can also test your .gitlab-ci.yml in the #{link_to "Lint", project_ci_lint_path(@project)}
+ - if pipeline.legacy_stages.present?
+ .table-holder.pipeline-holder
+ %table.table.ci-table.pipeline
+ %thead
+ %tr
+ %th Status
+ %th Job ID
+ %th Name
+ %th
+ %th Coverage
+ %th
+ = render partial: "projects/stage/stage", collection: pipeline.legacy_stages, as: :stage
- - if pipeline.project.builds_enabled? && !pipeline.ci_yaml_file
+ - elsif pipeline.project.builds_enabled? && !pipeline.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
- .table-holder.pipeline-holder
- %table.table.ci-table.pipeline
- %thead
- %tr
- %th Status
- %th Job ID
- %th Name
- %th
- %th Coverage
- %th
- = render partial: "projects/stage/stage", collection: pipeline.legacy_stages, as: :stage
-
- if @pipeline.failed_builds.present?
#js-tab-failures.build-failures.tab-pane.build-page
%table.table.responsive-table.ci-table.responsive-table-sm-rounded
@@ -68,12 +61,14 @@
%td.responsive-table-cell.build-failure{ data: { column: _("Failure")} }
= build.present.callout_failure_message
%td.responsive-table-cell.build-actions
- = link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'btn btn-build' do
- = icon('repeat')
- %tr.build-trace-row.responsive-table-border-end
- %td
- %td.responsive-table-cell.build-trace-container{ colspan: 4 }
- %pre.build-trace.build-trace-rounded
- %code.bash.js-build-output
- = build_summary(build)
+ - if can?(current_user, :update_build, job)
+ = link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'btn btn-build' do
+ = icon('repeat')
+ - if can?(current_user, :read_build, job)
+ %tr.build-trace-row.responsive-table-border-end
+ %td
+ %td.responsive-table-cell.build-trace-container{ colspan: 4 }
+ %pre.build-trace.build-trace-rounded
+ %code.bash.js-build-output
+ = build_summary(build)
= render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml
index a86cb14960a..ec17eddba79 100644
--- a/app/views/projects/pipelines/charts.html.haml
+++ b/app/views/projects/pipelines/charts.html.haml
@@ -1,6 +1,5 @@
- @no_container = true
-- breadcrumb_title "CI / CD Charts"
-- page_title _("Charts"), _("Pipelines")
+- page_title _("CI / CD Charts")
%div{ class: container_class }
.sub-header-block
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index c13e3194340..5b6823da1f6 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -1,5 +1,5 @@
- breadcrumb_title "Pipelines"
-- page_title = s_("Pipeline|Run Pipeline")
+- page_title s_("Pipeline|Run Pipeline")
- settings_link = link_to _('CI/CD settings'), project_settings_ci_cd_path(@project)
%h3.page-title
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index ff0ed3ed30d..193d437dad1 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -9,6 +9,14 @@
- if @pipeline.commit.present?
= render "projects/pipelines/info", commit: @pipeline.commit
- = render "projects/pipelines/with_tabs", pipeline: @pipeline
+ - if @pipeline.builds.empty? && @pipeline.yaml_errors.present?
+ .bs-callout.bs-callout-danger
+ %h4 Found errors in your .gitlab-ci.yml:
+ %ul
+ - @pipeline.yaml_errors.split(",").each do |error|
+ %li= error
+ You can test your .gitlab-ci.yml in #{link_to "CI Lint", project_ci_lint_path(@project)}.
+ - else
+ = render "projects/pipelines/with_tabs", pipeline: @pipeline
.js-pipeline-details-vue{ data: { endpoint: project_pipeline_path(@project, @pipeline, format: :json) } }
diff --git a/app/views/projects/project_members/_new_project_group.html.haml b/app/views/projects/project_members/_new_project_group.html.haml
new file mode 100644
index 00000000000..74570769117
--- /dev/null
+++ b/app/views/projects/project_members/_new_project_group.html.haml
@@ -0,0 +1,20 @@
+.row
+ .col-sm-12
+ = form_tag project_group_links_path(@project), class: 'js-requires-input', method: :post do
+ .form-group
+ = label_tag :link_group_id, _("Select a group to invite"), class: "label-bold"
+ = groups_select_tag(:link_group_id, data: { skip_groups: @skip_groups }, class: "input-clamp", required: true)
+ .form-group
+ = label_tag :link_group_access, _("Max access level"), class: "label-bold"
+ .select-wrapper
+ = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
+ = icon('chevron-down')
+ .form-text.text-muted.append-bottom-10
+ = link_to _("Read more"), help_page_path("user/permissions"), class: "vlink"
+ about role permissions
+ .form-group
+ = label_tag :expires_at, _('Access expiration date'), class: 'label-bold'
+ .clearable-input
+ = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date-groups', placeholder: _('Expiration date'), id: 'expires_at_groups'
+ %i.clear-icon.js-clear-input
+ = submit_tag _("Invite"), class: "btn btn-success"
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index 6272687be1c..5e21442bb60 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -3,7 +3,7 @@
= form_for @project_member, as: :project_member, url: project_project_members_path(@project), html: { class: 'users-project-form' } do |f|
.form-group
= label_tag :user_ids, "Select members to invite", class: "label-bold"
- = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
+ = users_select_tag(:user_ids, multiple: true, class: "input-clamp qa-member-select-input", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
.form-group
= label_tag :access_level, "Choose a role permission", class: "label-bold"
.select-wrapper
@@ -17,5 +17,5 @@
= label_tag :expires_at, 'Access expiration date', class: 'label-bold'
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
%i.clear-icon.js-clear-input
- = f.submit "Add to project", class: "btn btn-create"
+ = f.submit "Add to project", class: "btn btn-success qa-add-member-button"
= link_to "Import", import_project_project_members_path(@project), class: "btn btn-default", title: "Import members from another project"
diff --git a/app/views/projects/project_members/_new_shared_group.html.haml b/app/views/projects/project_members/_new_shared_group.html.haml
deleted file mode 100644
index d7227c32833..00000000000
--- a/app/views/projects/project_members/_new_shared_group.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-.row
- .col-sm-12
- = form_tag project_group_links_path(@project), class: 'js-requires-input', method: :post do
- .form-group
- = label_tag :link_group_id, "Select a group to share with", class: "label-bold"
- = groups_select_tag(:link_group_id, data: { skip_groups: @skip_groups }, class: "input-clamp", required: true)
- .form-group
- = label_tag :link_group_access, "Max access level", class: "label-bold"
- .select-wrapper
- = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
- = icon('chevron-down')
- .form-text.text-muted.append-bottom-10
- = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
- about role permissions
- .form-group
- = label_tag :expires_at, 'Access expiration date', class: 'label-bold'
- .clearable-input
- = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date-groups', placeholder: 'Expiration date', id: 'expires_at_groups'
- %i.clear-icon.js-clear-input
- = submit_tag "Share", class: "btn btn-create"
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index 0c5a187f208..9682f8ac922 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -14,5 +14,5 @@
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
= render 'shared/members/sort_dropdown'
- %ul.content-list.members-list
+ %ul.content-list.members-list.qa-members-list
= render partial: 'shared/members/member', collection: members, as: :member
diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml
index 6a52e72bfd8..8b93e81cd31 100644
--- a/app/views/projects/project_members/import.html.haml
+++ b/app/views/projects/project_members/import.html.haml
@@ -11,5 +11,5 @@
.col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(@projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true)
.form-actions
- = button_tag 'Import project members', class: "btn btn-create"
+ = button_tag 'Import project members', class: "btn btn-success"
= link_to "Cancel", project_project_members_path(@project), class: "btn btn-cancel"
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 9716322f8a1..14ed3345765 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -6,9 +6,9 @@
Project members
- if can?(current_user, :admin_project_member, @project)
%p
- You can add a new member to
+ You can invite a new member to
%strong= @project.name
- or share it with another group.
+ or invite another group.
- else
%p
Members can be added by project
@@ -19,16 +19,16 @@
- if can?(current_user, :admin_project_member, @project)
%ul.nav-links.nav.nav-tabs.gitlab-tabs{ role: 'tablist' }
%li.nav-tab{ role: 'presentation' }
- %a.nav-link.active{ href: '#add-member-pane', id: 'add-member-tab', data: { toggle: 'tab' }, role: 'tab' } Add member
+ %a.nav-link.active{ href: '#invite-member-pane', id: 'invite-member-tab', data: { toggle: 'tab' }, role: 'tab' } Invite member
- if @project.allowed_to_share_with_group?
%li.nav-tab{ role: 'presentation' }
- %a.nav-link{ href: '#share-with-group-pane', id: 'share-with-group-tab', data: { toggle: 'tab' }, role: 'tab' } Share with group
+ %a.nav-link{ href: '#invite-group-pane', id: 'invite-group-tab', data: { toggle: 'tab' }, role: 'tab' } Invite group
.tab-content.gitlab-tab-content
- .tab-pane.active{ id: 'add-member-pane', role: 'tabpanel' }
- = render 'projects/project_members/new_project_member', tab_title: 'Add member'
- .tab-pane{ id: 'share-with-group-pane', role: 'tabpanel' }
- = render 'projects/project_members/new_shared_group', tab_title: 'Share with group'
+ .tab-pane.active{ id: 'invite-member-pane', role: 'tabpanel' }
+ = render 'projects/project_members/new_project_member', tab_title: 'Invite member'
+ .tab-pane{ id: 'invite-group-pane', role: 'tabpanel' }
+ = render 'projects/project_members/new_project_group', tab_title: 'Invite group'
= render 'shared/members/requests', membership_source: @project, requesters: @requesters
.clearfix
diff --git a/app/views/projects/project_templates/_built_in_templates.html.haml b/app/views/projects/project_templates/_built_in_templates.html.haml
index e7636099be6..233c3adba0e 100644
--- a/app/views/projects/project_templates/_built_in_templates.html.haml
+++ b/app/views/projects/project_templates/_built_in_templates.html.haml
@@ -10,8 +10,8 @@
= template.description
.controls.d-flex.align-items-center
%label.btn.btn-success.template-button.choose-template.append-right-10.append-bottom-0{ for: template.name }
- %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name }
+ %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name, data: { track_label: "create_from_template", track_property: "template_use", track_event: "click_button" } }
%span
= _("Use template")
- %a.btn.btn-default{ href: template.preview, rel: 'noopener noreferrer', target: '_blank' }
+ %a.btn.btn-default{ href: template.preview, rel: 'noopener noreferrer', target: '_blank', data: { track_label: "create_from_template", track_property: "template_preview", track_event: "click_button", track_value: template.name } }
= _("Preview")
diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml
index 9a06eca89bb..1913d06a6f8 100644
--- a/app/views/projects/protected_branches/shared/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml
@@ -1,8 +1,7 @@
.protected-branches-list.js-protected-branches-list.qa-protected-branches-list
- if @protected_branches.empty?
.card-header.bg-white
- %h3.card-title.mb-0
- Protected branch (#{@protected_branches_count})
+ Protected branch (#{@protected_branches_count})
%p.settings-message.text-center
There are currently no protected branches, protect a branch with the form above.
- else
diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
index df2dcf19ed4..d617d85afc2 100644
--- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
@@ -2,8 +2,7 @@
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' }
.card
.card-header
- %h3.card-title
- Protect a branch
+ Protect a branch
.card-body
= form_errors(@protected_branch)
.form-group.row
@@ -30,4 +29,4 @@
= yield :push_access_levels
.card-footer
- = f.submit 'Protect', class: 'btn-create btn', disabled: true
+ = f.submit 'Protect', class: 'btn-success btn', disabled: true
diff --git a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
index f98781b77f4..cbf1938664c 100644
--- a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
+++ b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
@@ -2,8 +2,7 @@
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-tags-settings' }
.card
.card-header
- %h3.card-title
- Protect a tag
+ Protect a tag
.card-body
= form_errors(@protected_tag)
.form-group.row
@@ -26,4 +25,4 @@
= yield :create_access_levels
.card-footer
- = f.submit 'Protect', class: 'btn-create btn', disabled: true
+ = f.submit 'Protect', class: 'btn-success btn', disabled: true
diff --git a/app/views/projects/protected_tags/shared/_tags_list.html.haml b/app/views/projects/protected_tags/shared/_tags_list.html.haml
index c3081d75fb4..382ea848243 100644
--- a/app/views/projects/protected_tags/shared/_tags_list.html.haml
+++ b/app/views/projects/protected_tags/shared/_tags_list.html.haml
@@ -1,8 +1,7 @@
.protected-tags-list.js-protected-tags-list
- if @protected_tags.empty?
.card-header
- %h3.card-title
- Protected tag (#{@protected_tags_count})
+ Protected tag (#{@protected_tags_count})
%p.settings-message.text-center
There are currently no protected tags, protect a tag with the form above.
- else
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index 0426f2215ad..db1f15f96b8 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -18,8 +18,7 @@
.col-lg-12
.card
.card-header
- %h4.card-title
- = s_('ContainerRegistry|How to use the Container Registry')
+ = s_('ContainerRegistry|How to use the Container Registry')
.card-body
%p
- link_token = link_to(_('personal access token'), help_page_path('user/profile/account/two_factor_authentication', anchor: 'personal-access-tokens'), target: '_blank')
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
index 8093cc2c2d7..52c6c7ec424 100644
--- a/app/views/projects/releases/edit.html.haml
+++ b/app/views/projects/releases/edit.html.haml
@@ -19,5 +19,5 @@
= render 'shared/notes/hints'
.error-alert
.prepend-top-default
- = f.submit 'Save changes', class: 'btn btn-save'
+ = f.submit 'Save changes', class: 'btn btn-success'
= link_to "Cancel", project_tag_path(@project, @tag.name), class: "btn btn-default btn-cancel"
diff --git a/app/views/projects/runners/_group_runners.html.haml b/app/views/projects/runners/_group_runners.html.haml
index 86de71c732b..a6c16c70313 100644
--- a/app/views/projects/runners/_group_runners.html.haml
+++ b/app/views/projects/runners/_group_runners.html.haml
@@ -28,7 +28,7 @@
- group_link = link_to _('Group CI/CD settings'), group_settings_ci_cd_path(@project.group)
= _('Group maintainers can register group runners in the %{link}').html_safe % { link: group_link }
- else
- = _('Ask your group maintainer to setup a group Runner.')
+ = _('Ask your group maintainer to set up a group Runner.')
- else
%h4.underlined-title
diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml
index 6ee83fae25e..548977d6a80 100644
--- a/app/views/projects/runners/_runner.html.haml
+++ b/app/views/projects/runners/_runner.html.haml
@@ -24,7 +24,7 @@
- if runner.belongs_to_one_project?
= link_to _('Remove Runner'), project_runner_path(@project, runner), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn btn-danger btn-sm'
- else
- - runner_project = @project.runner_projects.find_by(runner_id: runner)
+ - runner_project = @project.runner_projects.find_by(runner_id: runner) # rubocop: disable CodeReuse/ActiveRecord
= link_to _('Disable for this project'), project_runner_project_path(@project, runner_project), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn btn-danger btn-sm'
- elsif runner.project_type?
= form_for [@project.namespace.becomes(Namespace), @project, @project.runner_projects.new] do |f|
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 6c11ce3b394..ec503cd8bef 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -1,8 +1,34 @@
%h3
= _('Specific Runners')
-= render partial: 'ci/runner/how_to_setup_specific_runner',
- locals: { registration_token: @project.runners_token }
+.bs-callout.help-callout
+ .append-bottom-10
+ %h4= _('Set up a specific Runner automatically')
+
+ %p
+ - link_to_help_page = link_to(_('Learn more about Kubernetes'),
+ help_page_path('user/project/clusters/index'),
+ target: '_blank',
+ rel: 'noopener noreferrer')
+
+ = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page }
+
+ %ol
+ %li
+ = _('Click the button below to begin the install process by navigating to the Kubernetes page')
+ %li
+ = _('Select an existing Kubernetes cluster or create a new one')
+ %li
+ = _('From the Kubernetes cluster details view, install Runner from the applications list')
+
+ = link_to _('Install Runner on Kubernetes'),
+ project_clusters_path(@project),
+ class: 'btn btn-info'
+ %hr
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: @project.runners_token,
+ type: 'specific',
+ reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path }
- if @project_runners.any?
%h4.underlined-title Runners activated for this project
@@ -13,4 +39,4 @@
%h4.underlined-title Available specific runners
%ul.bordered-list.available-specific-runners
= render partial: 'projects/runners/runner', collection: @assignable_runners, as: :runner
- = paginate @assignable_runners, theme: "gitlab"
+ = paginate @assignable_runners, theme: "gitlab", :params => { :anchor => '#js-runners-settings' }
diff --git a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
index 9314804c5dd..9409418bbcc 100644
--- a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
+++ b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
@@ -1,6 +1,6 @@
- run_actions_text = "Perform common operations on GitLab project: #{@project.full_name}"
-%p To setup this service:
+%p To set up this service:
%ul.list-unstyled.indent-list
%li
1.
diff --git a/app/views/projects/services/prometheus/_configuration_banner.html.haml b/app/views/projects/services/prometheus/_configuration_banner.html.haml
index 898b55e4b39..dfcb1c5d240 100644
--- a/app/views/projects/services/prometheus/_configuration_banner.html.haml
+++ b/app/views/projects/services/prometheus/_configuration_banner.html.haml
@@ -7,7 +7,7 @@
- else
.container-fluid
.row
- - if service.prometheus_installed?
+ - if service.prometheus_available?
.col-sm-2
.svg-container
= image_tag 'illustrations/monitoring/getting_started.svg'
diff --git a/app/views/projects/services/prometheus/_metrics.html.haml b/app/views/projects/services/prometheus/_metrics.html.haml
index 98d64fafe86..597c029f755 100644
--- a/app/views/projects/services/prometheus/_metrics.html.haml
+++ b/app/views/projects/services/prometheus/_metrics.html.haml
@@ -2,9 +2,8 @@
.card.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(project, :json), metrics_help_path: help_page_path('user/project/integrations/prometheus_library/metrics') } }
.card-header
- %h3.card-title
- = s_('PrometheusService|Common metrics')
- %span.badge.badge-pill.js-monitored-count 0
+ = s_('PrometheusService|Common metrics')
+ %span.badge.badge-pill.js-monitored-count 0
.card-body
.loading-metrics.js-loading-metrics
%p.prepend-top-10.prepend-left-10
@@ -17,10 +16,9 @@
.card.hidden.js-panel-missing-env-vars
.card-header
- %h3.card-title
- = icon('caret-right lg fw', class: 'panel-toggle js-panel-toggle', 'aria-label' => 'Toggle panel')
- = s_('PrometheusService|Missing environment variable')
- %span.badge.badge-pill.js-env-var-count 0
+ = icon('caret-right lg fw', class: 'panel-toggle js-panel-toggle', 'aria-label' => 'Toggle panel')
+ = s_('PrometheusService|Missing environment variable')
+ %span.badge.badge-pill.js-env-var-count 0
.card-body.hidden
.flash-container
.flash-notice
diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml
index f25d2ecdfb1..9a7004f89c0 100644
--- a/app/views/projects/services/slack_slash_commands/_help.html.haml
+++ b/app/views/projects/services/slack_slash_commands/_help.html.haml
@@ -14,7 +14,7 @@
by entering
%kbd.inline /&lt;command&gt; help
- unless @service.template?
- %p To setup this service:
+ %p To set up this service:
%ul.list-unstyled.indent-list
%li
1.
diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
index ab9ba5c7569..5ec5a06396e 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -5,7 +5,6 @@
%fieldset.builds-feature.js-auto-devops-settings
.form-group
- message = auto_devops_warning_message(@project)
- - ci_file_formatted = '<code>.gitlab-ci.yml</code>'.html_safe
- if message
%p.auto-devops-warning-message.settings-message.text-center
= message.html_safe
@@ -40,10 +39,17 @@
= form.label :deploy_strategy_continuous, class: 'form-check-label' do
= s_('CICD|Continuous deployment to production')
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-deploy'), target: '_blank'
+
+ .form-check
+ = form.radio_button :deploy_strategy, 'timed_incremental', class: 'form-check-input'
+ = form.label :deploy_strategy_timed_incremental, class: 'form-check-label' do
+ = s_('CICD|Continuous deployment to production using timed incremental rollout')
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'timed-incremental-rollout-to-production'), target: '_blank'
+
.form-check
= form.radio_button :deploy_strategy, 'manual', class: 'form-check-input'
= form.label :deploy_strategy_manual, class: 'form-check-label' do
= s_('CICD|Automatic deployment to staging, manual deployment to production')
- = link_to icon('question-circle'), help_page_path('ci/environments.md', anchor: 'manually-deploying-to-environments'), target: '_blank'
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'incremental-rollout-to-production'), target: '_blank'
= f.submit _('Save changes'), class: "btn btn-success prepend-top-15"
diff --git a/app/views/projects/settings/ci_cd/_badge.html.haml b/app/views/projects/settings/ci_cd/_badge.html.haml
index d14360913a4..82c8ec088e5 100644
--- a/app/views/projects/settings/ci_cd/_badge.html.haml
+++ b/app/views/projects/settings/ci_cd/_badge.html.haml
@@ -15,14 +15,14 @@
.col-md-2.text-center
Markdown
.col-md-10.code.js-syntax-highlight
- = highlight('.md', badge.to_markdown)
+ = highlight('.md', badge.to_markdown, language: 'markdown')
.row
%hr
.row
.col-md-2.text-center
HTML
.col-md-10.code.js-syntax-highlight
- = highlight('.html', badge.to_html)
+ = highlight('.html', badge.to_html, language: 'html')
.row
%hr
.row
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index 434aed2f603..621b7922072 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -3,21 +3,11 @@
= form_for @project, url: project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings') do |f|
= form_errors(@project)
%fieldset.builds-feature
- .form-group.append-bottom-default.js-secret-runner-token
- = f.label :runners_token, _("Runner token"), class: 'label-bold'
- .form-control.js-secret-value-placeholder
- = '*' * 20
- = f.text_field :runners_token, class: "form-control hide js-secret-value", placeholder: 'xEeFCaDAB89'
- %p.form-text.text-muted= _("The secure token used by the Runner to checkout the project")
- %button.btn.btn-info.prepend-top-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: 'false' } }
- = _('Reveal value')
-
- %hr
.form-group
%h5.prepend-top-0
= _("Git strategy for pipelines")
%p
- = _("Choose between <code>clone</code> or <code>fetch</code> to get the recent application code")
+ = _("Choose between <code>clone</code> or <code>fetch</code> to get the recent application code").html_safe
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'git-strategy'), target: '_blank'
.form-check
= f.radio_button :build_allow_git_fetch, 'false', { class: 'form-check-input' }
@@ -47,7 +37,7 @@
= f.label :ci_config_path, _('Custom CI config path'), class: 'label-bold'
= f.text_field :ci_config_path, class: 'form-control', placeholder: '.gitlab-ci.yml'
%p.form-text.text-muted
- = _("The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>")
+ = _("The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>").html_safe
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'custom-ci-config-path'), target: '_blank'
%hr
@@ -101,7 +91,7 @@
%code \(\d+.\d+\%\) covered
%li
pytest-cov (Python) -
- %code \d+\%\s*$
+ %code ^TOTAL\s+\d+\s+\d+\s+(\d+\%)$
%li
phpunit --coverage-text --colors=never (PHP) -
%code ^\s*Lines:\s*\d+.\d+\%
@@ -121,7 +111,7 @@
go test -cover (Go)
%code coverage: \d+.\d+% of statements
- = f.submit _('Save changes'), class: "btn btn-save"
+ = f.submit _('Save changes'), class: "btn btn-success"
%hr
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index 16961784e00..98e2829ba43 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -12,7 +12,7 @@
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _("Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report.")
+ = _("Customize your pipeline configuration, view your pipeline status and coverage report.")
.settings-content
= render 'form'
diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml
index 98c609d7bd4..a0bcaaf3c54 100644
--- a/app/views/projects/settings/repository/show.html.haml
+++ b/app/views/projects/settings/repository/show.html.haml
@@ -2,6 +2,7 @@
- page_title _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
+= render "projects/default_branch/show"
= render "projects/mirrors/show"
-# Protected branches & tags use a lot of nested partials.
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 8a5abb64515..f29ce4f5c06 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -8,8 +8,8 @@
= render partial: 'flash_messages', locals: { project: @project }
-- if @project.repository_exists? && !@project.empty_repo?
- - signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.full_path, project_id: @project.path, id: @project.default_branch)
+- if !@project.empty_repo? && can?(current_user, :download_code, @project)
+ - signatures_path = project_signatures_path(@project, @project.default_branch)
.js-signature-container{ data: { 'signatures-path': signatures_path } }
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
@@ -19,10 +19,14 @@
- if can?(current_user, :download_code, @project)
%nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
- = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
- = render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
- - if Feature.enabled?(:repository_languages, @project.namespace.becomes(Namespace))
- = repository_languages_bar(@project.repository_languages)
+ .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
+ .fade-left= icon('angle-left')
+ .fade-right= icon('angle-right')
+ .nav-links.scrolling-tabs.quick-links
+ = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
+ = render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
+
+ = repository_languages_bar(@project.repository_languages)
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
- if @project.archived?
diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml
index 4a3aa3dc626..ea963510a68 100644
--- a/app/views/projects/snippets/_actions.html.haml
+++ b/app/views/projects/snippets/_actions.html.haml
@@ -8,7 +8,7 @@
= link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: _("Are you sure?") }, class: "btn btn-grouped btn-inverted btn-remove", title: _('Delete Snippet') do
= _('Delete')
- if can?(current_user, :create_project_snippet, @project)
- = link_to new_project_snippet_path(@project), class: 'btn btn-grouped btn-inverted btn-create', title: _("New snippet") do
+ = link_to new_project_snippet_path(@project), class: 'btn btn-grouped btn-inverted btn-success', title: _("New snippet") do
= _('New snippet')
- if @snippet.submittable_as_spam_by?(current_user)
= link_to _('Submit as spam'), mark_as_spam_project_snippet_path(@project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam')
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 1c4c73dc776..a4974d89c1a 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -7,6 +7,6 @@
.nav-controls
- if can?(current_user, :create_project_snippet, @project)
- = link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-new", title: _("New snippet")
+ = link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-success", title: _("New snippet")
= render 'snippets/snippets'
diff --git a/app/views/projects/tags/_tag.atom.builder b/app/views/projects/tags/_tag.atom.builder
new file mode 100644
index 00000000000..60d4b21b9d1
--- /dev/null
+++ b/app/views/projects/tags/_tag.atom.builder
@@ -0,0 +1,19 @@
+commit = @repository.commit(tag.dereferenced_target)
+release = @releases.find { |r| r.tag == tag.name }
+tag_url = project_tag_url(@project, tag.name)
+
+if commit
+ xml.entry do
+ xml.id tag_url
+ xml.link href: tag_url
+ xml.title truncate(tag.name, length: 80)
+ xml.summary strip_gpg_signature(tag.message)
+ xml.content markdown_field(release, :description), type: 'html'
+ xml.updated release.updated_at.xmlschema if release
+ xml.media :thumbnail, width: '40', height: '40', url: image_url(avatar_icon_for_email(commit.author_email))
+ xml.author do |author|
+ xml.name commit.author_name
+ xml.email commit.author_email
+ end
+ end
+end
diff --git a/app/views/projects/tags/index.atom.builder b/app/views/projects/tags/index.atom.builder
new file mode 100644
index 00000000000..b9b58b7beaa
--- /dev/null
+++ b/app/views/projects/tags/index.atom.builder
@@ -0,0 +1,7 @@
+xml.title "#{@project.name} tags"
+xml.link href: project_tags_url(@project, @ref, rss_url_options), rel: 'self', type: 'application/atom+xml'
+xml.link href: project_tags_url(@project, @ref), rel: 'alternate', type: 'text/html'
+xml.id project_tags_url(@project, @ref)
+xml.updated @releases.first.updated_at.xmlschema if @releases.any?
+
+xml << render(partial: 'tag', collection: @tags) if @tags.any?
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index dab95ba09f2..37535370940 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -1,6 +1,8 @@
- @no_container = true
- @sort ||= sort_value_recently_updated
- page_title s_('TagsPage|Tags')
+= content_for :meta_tags do
+ = auto_discovery_link_tag(:atom, project_tags_url(@project, rss_url_options), title: "#{@project.name} tags")
.flex-list{ class: container_class }
.top-area.adjust
@@ -23,8 +25,10 @@
%li
= link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value)
- if can?(current_user, :push_code, @project)
- = link_to new_project_tag_path(@project), class: 'btn btn-create new-tag-btn' do
+ = link_to new_project_tag_path(@project), class: 'btn btn-success new-tag-btn' do
= s_('TagsPage|New tag')
+ = link_to project_tags_path(@project, rss_url_options), title: _("Tags feed"), class: 'btn rss-btn has-tooltip' do
+ = icon("rss")
= render_if_exists 'projects/commits/mirror_status'
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index da822ac5675..5e6d06d980e 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -20,8 +20,9 @@
.col-sm-10.create-from
.dropdown
= hidden_field_tag :ref, default_ref
- = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do
+ = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select monospace', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do
.text-left.dropdown-toggle-text= default_ref
+ = icon('chevron-down')
= render 'shared/ref_dropdown', dropdown_class: 'wide'
.form-text.text-muted
= s_('TagsPage|Existing branch name, tag, or commit SHA')
@@ -41,7 +42,7 @@
.form-text.text-muted
= s_('TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.')
.form-actions
- = button_tag s_('TagsPage|Create tag'), class: 'btn btn-create'
+ = button_tag s_('TagsPage|Create tag'), class: 'btn btn-success'
= link_to s_('TagsPage|Cancel'), project_tags_path(@project), class: 'btn btn-cancel'
-# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml
deleted file mode 100644
index f79f3af36d4..00000000000
--- a/app/views/projects/tree/_blob_item.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-- is_lfs_blob = @lfs_blob_ids.include?(blob_item.id)
-%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
- %td.tree-item-file-name
- = tree_icon(type, blob_item.mode, blob_item.name)
- - file_name = blob_item.name
- = link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
- %span= file_name
- - if is_lfs_blob
- %span.badge.label-lfs.prepend-left-5 LFS
- %td.d-none.d-sm-table-cell.tree-commit
- %td.tree-time-ago.cgray.text-right
- = render 'projects/tree/spinner'
diff --git a/app/views/projects/tree/_spinner.html.haml b/app/views/projects/tree/_spinner.html.haml
deleted file mode 100644
index b47ad0f41e4..00000000000
--- a/app/views/projects/tree/_spinner.html.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-%span.log_loading.hide
- %i.fa.fa-spinner.fa-spin
- Loading commit data...
diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml
deleted file mode 100644
index e563c8c4036..00000000000
--- a/app/views/projects/tree/_submodule_item.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-%tr.tree-item
- %td.tree-item-file-name
- %i.fa.fa-archive.fa-fw
- = submodule_link(submodule_item, @ref)
- %td
- %td.d-none.d-sm-table-cell
diff --git a/app/views/projects/tree/_tree_commit_column.html.haml b/app/views/projects/tree/_tree_commit_column.html.haml
index abb3e918e87..e37fd7624be 100644
--- a/app/views/projects/tree/_tree_commit_column.html.haml
+++ b/app/views/projects/tree/_tree_commit_column.html.haml
@@ -1,2 +1,2 @@
%span.str-truncated
- = link_to_markdown commit.full_title, project_commit_path(@project, commit.id), class: "tree-commit-link"
+ = link_to_html commit.redacted_full_title_html, project_commit_path(@project, commit.id), title: commit.redacted_full_title_html, class: 'tree-commit-link'
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index 587aeafa82f..5e0523f0b96 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -1,6 +1,6 @@
.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path }
.table-holder
- %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" }
+ %table.table#tree-slider{ class: "table_#{@hex_path} tree-table qa-file-tree" }
%thead
%tr
%th= s_('ProjectFileTree|Name')
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 9d196075bf1..601e3f25852 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -82,7 +82,7 @@
- if can_collaborate
= succeed " " do
- = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default' do
+ = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
= _('Web IDE')
= render 'projects/buttons/download', project: @project, ref: @ref
diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml
deleted file mode 100644
index ce0cd95b468..00000000000
--- a/app/views/projects/tree/_tree_item.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-%tr{ class: "tree-item #{tree_hex_class(tree_item)}" }
- %td.tree-item-file-name
- = tree_icon(type, tree_item.mode, tree_item.name)
- - path = flatten_tree(@path, tree_item)
- = link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), class: 'str-truncated', title: path do
- %span= path
- %td.d-none.d-sm-table-cell.tree-commit
- %td.tree-time-ago.text-right
- = render 'projects/tree/spinner'
diff --git a/app/views/projects/tree/_tree_row.html.haml b/app/views/projects/tree/_tree_row.html.haml
index 0a5c6f048f7..8a27ea66523 100644
--- a/app/views/projects/tree/_tree_row.html.haml
+++ b/app/views/projects/tree/_tree_row.html.haml
@@ -1,6 +1,27 @@
-- if tree_row.type == :tree
- = render partial: 'projects/tree/tree_item', object: tree_row, as: 'tree_item', locals: { type: 'folder' }
-- elsif tree_row.type == :blob
- = render partial: 'projects/tree/blob_item', object: tree_row, as: 'blob_item', locals: { type: 'file' }
-- elsif tree_row.type == :commit
- = render partial: 'projects/tree/submodule_item', object: tree_row, as: 'submodule_item'
+- tree_row_name = tree_row.name
+- tree_row_type = tree_row.type
+
+%tr{ class: "tree-item file_#{hexdigest(tree_row_name)}" }
+ %td.tree-item-file-name
+ - if tree_row_type == :tree
+ = tree_icon('folder', tree_row.mode, tree_row.name)
+ - path = flatten_tree(@path, tree_row)
+ %a.str-truncated{ href: fast_project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path }
+ %span= path
+
+ - elsif tree_row_type == :blob
+ = tree_icon('file', tree_row.mode, tree_row_name)
+ %a.str-truncated{ href: fast_project_blob_path(@project, tree_join(@id || @commit.id, tree_row_name)), title: tree_row_name }
+ %span= tree_row_name
+ - if @lfs_blob_ids.include?(tree_row.id)
+ %span.badge.label-lfs.prepend-left-5 LFS
+
+ - elsif tree_row_type == :commit
+ = tree_icon('archive', tree_row.mode, tree_row.name)
+ = submodule_link(tree_row, @ref)
+
+ %td.d-none.d-sm-table-cell.tree-commit
+ %td.tree-time-ago.text-right
+ %span.log_loading.hide
+ %i.fa.fa-spinner.fa-spin
+ Loading commit data...
diff --git a/app/views/projects/triggers/_form.html.haml b/app/views/projects/triggers/_form.html.haml
index 1a5fc56f429..a9abfac239c 100644
--- a/app/views/projects/triggers/_form.html.haml
+++ b/app/views/projects/triggers/_form.html.haml
@@ -8,4 +8,4 @@
.form-group
= f.label :key, "Description", class: "label-bold"
= f.text_field :description, class: "form-control", required: true, title: 'Trigger description is required.', placeholder: "Trigger description"
- = f.submit btn_text, class: "btn btn-save"
+ = f.submit btn_text, class: "btn btn-success"
diff --git a/app/views/projects/triggers/_index.html.haml b/app/views/projects/triggers/_index.html.haml
index a15bb4c4f3f..a559ce41e57 100644
--- a/app/views/projects/triggers/_index.html.haml
+++ b/app/views/projects/triggers/_index.html.haml
@@ -3,8 +3,7 @@
= render "projects/triggers/content"
.card
.card-header
- %h4.card-title
- Manage your project's triggers
+ Manage your project's triggers
.card-body
= render "projects/triggers/form", btn_text: "Add trigger"
%hr
diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml
index e8681da6528..70f1bf8ef46 100644
--- a/app/views/projects/update.js.haml
+++ b/app/views/projects/update.js.haml
@@ -7,4 +7,4 @@
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
$('.save-project-loader').hide();
$('.project-edit-container').show();
- $('.edit-project .js-btn-save-general-project-settings').enable();
+ $('.edit-project .js-btn-success-general-project-settings').enable();
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index de692466fe5..7d8826e540c 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,9 +1,13 @@
- commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}")
- commit_message = commit_message % { page_title: @page.title }
+- if params[:legacy_render] || !commonmark_for_repositories_enabled?
+ - markdown_version = CacheMarkdownField::CACHE_REDCARPET_VERSION
+- else
+ - markdown_version = 0
= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post,
html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' },
- data: { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION } do |f|
+ data: { markdown_version: markdown_version, uploads_path: uploads_path } do |f|
= form_errors(@page)
- if @page.persisted?
@@ -47,10 +51,10 @@
.form-actions
- if @page && @page.persisted?
- = f.submit _("Save changes"), class: 'btn-save btn'
+ = f.submit _("Save changes"), class: 'btn-success btn'
.float-right
= link_to _("Cancel"), project_wiki_path(@project, @page), class: 'btn btn-cancel btn-grouped'
- else
- = f.submit s_("Wiki|Create page"), class: 'btn-create btn'
+ = f.submit s_("Wiki|Create page"), class: 'btn-success btn'
.float-right
= link_to _("Cancel"), project_wiki_path(@project, :home), class: 'btn btn-cancel'
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 8d91f411f89..643b51e01d1 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,6 +1,6 @@
- if (@page && @page.persisted?)
- if can?(current_user, :create_wiki, @project)
- = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-success", "data-toggle" => "modal" do
= s_("Wiki|New page")
= link_to project_wiki_history_path(@project, @page), class: "btn" do
= s_("Wiki|Page history")
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index 38382aae67c..dc12e368b35 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -15,4 +15,4 @@
= icon('lightbulb-o')
= s_("WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories.")
.form-actions
- = button_tag s_("Wiki|Create page"), class: "build-new-wiki btn btn-create"
+ = button_tag s_("Wiki|Create page"), class: "build-new-wiki btn btn-success"
diff --git a/app/views/projects/wikis/_pages_wiki_page.html.haml b/app/views/projects/wikis/_pages_wiki_page.html.haml
index cbb441d7509..c156f8cbf50 100644
--- a/app/views/projects/wikis/_pages_wiki_page.html.haml
+++ b/app/views/projects/wikis/_pages_wiki_page.html.haml
@@ -2,4 +2,5 @@
= link_to wiki_page.title, project_wiki_path(@project, wiki_page)
%small (#{wiki_page.format})
.float-right
- %small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
+ - if wiki_page.last_version
+ %small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml
index 28353927135..02c5a6ea55c 100644
--- a/app/views/projects/wikis/_sidebar.html.haml
+++ b/app/views/projects/wikis/_sidebar.html.haml
@@ -12,7 +12,7 @@
.blocks-container
.block.block-first
- if @sidebar_page
- = render_wiki_content(@sidebar_page)
+ = render_wiki_content(@sidebar_page, legacy_render_context(params))
- else
%ul.wiki-pages
= render @sidebar_wiki_entries, context: 'sidebar'
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index d80d2957466..80aa1500d53 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -22,22 +22,14 @@
.nav-controls
- if can?(current_user, :create_wiki, @project)
- = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-success", "data-toggle" => "modal" do
= s_("Wiki|New page")
- if @page.persisted?
= link_to project_wiki_history_path(@project, @page), class: "btn" do
= s_("Wiki|Page history")
- if can?(current_user, :admin_wiki, @project)
- %button.btn.btn-danger{ data: { toggle: 'modal',
- target: '#delete-wiki-modal',
- delete_wiki_url: project_wiki_path(@project, @page),
- page_title: @page.title.capitalize },
- id: 'delete-wiki-button',
- type: 'button' }
- = _('Delete')
+ #delete-wiki-modal-wrapper{ data: { delete_wiki_url: project_wiki_path(@project, @page), page_title: @page.title.capitalize } }
-= render 'form'
+= render 'form', uploads_path: wiki_attachment_upload_url
= render 'sidebar'
-
-#delete-wiki-modal.modal.fade
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index a08973c7f32..fbf248c2058 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -11,8 +11,9 @@
.nav-text
%h2.wiki-page-title= @page.title.capitalize
%span.wiki-last-edit-by
- = (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
- #{time_ago_with_tooltip(@page.last_version.authored_date)}
+ - if @page.last_version
+ = (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
+ #{time_ago_with_tooltip(@page.last_version.authored_date)}
.nav-controls
= render 'main_links'
@@ -26,6 +27,6 @@
.prepend-top-default.append-bottom-default
.wiki
- = render_wiki_content(@page)
+ = render_wiki_content(@page, legacy_render_context(params))
= render 'sidebar'
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index fdcd126e7a3..a8d4d4af93a 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -1,4 +1,6 @@
- project = find_project_for_result_blob(blob)
+- return unless project
+
- file_name, blob = parse_search_result(blob)
- blob_link = project_blob_path(project, tree_join(blob.ref, file_name))
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index 57a0b64bfd5..b4ecd7bbae9 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -21,7 +21,7 @@
.file-content.wiki
- snippet_chunks.each do |chunk|
- unless chunk[:data].empty?
- = markup(snippet.file_name, chunk[:data])
+ = markup(snippet.file_name, chunk[:data], legacy_render_context(params))
- else
.file-content.code
.nothing-here-block Empty file
@@ -39,7 +39,7 @@
.blob-content
- snippet_chunks.each do |chunk|
- unless chunk[:data].empty?
- = highlight(snippet.file_name, chunk[:data], repository: nil, plain: snippet.blob.no_highlighting?)
+ = highlight(snippet.file_name, chunk[:data])
- else
.file-content.code
.nothing-here-block Empty file
diff --git a/app/views/shared/_allow_request_access.html.haml b/app/views/shared/_allow_request_access.html.haml
index 92268e74b1e..a50f1877d08 100644
--- a/app/views/shared/_allow_request_access.html.haml
+++ b/app/views/shared/_allow_request_access.html.haml
@@ -1,6 +1,8 @@
+- label_class = local_assigns.fetch(:bold_label, false) ? 'font-weight-bold' : ''
+
.form-check
= form.check_box :request_access_enabled, class: 'form-check-input'
= form.label :request_access_enabled, class: 'form-check-label' do
- %strong Allow users to request access
+ %span{ class: label_class }= _('Allow users to request access')
%br
- %span.descr Allow users to request access if visibility is public or internal.
+ %span.text-muted= _('Allow users to request access if visibility is public or internal.')
diff --git a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
new file mode 100644
index 00000000000..6c4607b2f16
--- /dev/null
+++ b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
@@ -0,0 +1,9 @@
+- if show_auto_devops_implicitly_enabled_banner?(project)
+ .auto-devops-implicitly-enabled-banner.alert.alert-warning
+ - more_information_link = link_to _('More information'), 'https://docs.gitlab.com/ee/topics/autodevops/', class: 'alert-link'
+ - auto_devops_message = s_("AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}") % { more_information_link: more_information_link }
+ = auto_devops_message.html_safe
+ .alert-link-group
+ = link_to _('Settings'), project_settings_ci_cd_path(project), class: 'alert-link'
+ |
+ = link_to _('Dismiss'), '#', class: 'hide-auto-devops-implicitly-enabled-banner alert-link', data: { project_id: project.id }
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 3655c2a1d42..a2df0347fd6 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,14 +1,14 @@
- project = project || @project
-.git-clone-holder.input-group
+.git-clone-holder.js-git-clone-holder.input-group
.input-group-prepend
- if allowed_protocols_present?
.input-group-text.clone-dropdown-btn.btn
- %span
+ %span.js-clone-dropdown-label
= enabled_project_button(project, enabled_protocol)
- else
%a#clone-dropdown.input-group-text.btn.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
- %span
+ %span.js-clone-dropdown-label
= default_clone_protocol.upcase
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index 7afb7b3a93b..6612497e7e2 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -2,13 +2,13 @@
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.nav-links.event-filter.scrolling-tabs.nav.nav-tabs
- = event_filter_link EventFilter.all, _('All'), s_('EventFilterBy|Filter by all')
+ = event_filter_link EventFilter::ALL, _('All'), s_('EventFilterBy|Filter by all')
- if event_filter_visible(:repository)
- = event_filter_link EventFilter.push, _('Push events'), s_('EventFilterBy|Filter by push events')
+ = event_filter_link EventFilter::PUSH, _('Push events'), s_('EventFilterBy|Filter by push events')
- if event_filter_visible(:merge_requests)
- = event_filter_link EventFilter.merged, _('Merge events'), s_('EventFilterBy|Filter by merge events')
+ = event_filter_link EventFilter::MERGED, _('Merge events'), s_('EventFilterBy|Filter by merge events')
- if event_filter_visible(:issues)
- = event_filter_link EventFilter.issue, _('Issue events'), s_('EventFilterBy|Filter by issue events')
+ = event_filter_link EventFilter::ISSUE, _('Issue events'), s_('EventFilterBy|Filter by issue events')
- if comments_visible?
- = event_filter_link EventFilter.comments, _('Comments'), s_('EventFilterBy|Filter by comments')
- = event_filter_link EventFilter.team, _('Team'), s_('EventFilterBy|Filter by team')
+ = event_filter_link EventFilter::COMMENTS, _('Comments'), s_('EventFilterBy|Filter by comments')
+ = event_filter_link EventFilter::TEAM, _('Team'), s_('EventFilterBy|Filter by team')
diff --git a/app/views/shared/_field.html.haml b/app/views/shared/_field.html.haml
index b89045e726a..606d0f241aa 100644
--- a/app/views/shared/_field.html.haml
+++ b/app/views/shared/_field.html.haml
@@ -24,6 +24,6 @@
- elsif type == 'select'
= form.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control", disabled: disabled}
- elsif type == 'password'
- = form.password_field name, autocomplete: "new-password", class: "form-control", required: value.blank? && required, disabled: disabled
+ = form.password_field name, autocomplete: "new-password", placeholder: placeholder, class: "form-control", required: value.blank? && required, disabled: disabled
- if help
%span.form-text.text-muted= help
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index 8d64cb5d698..5073e6ad48f 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,5 +1,3 @@
-- repository = nil unless local_assigns.key?(:repository)
-
.file-content.code.js-syntax-highlight
.line-numbers
- if blob.data.present?
@@ -13,4 +11,6 @@
= link_icon
= i
.blob-content{ data: { blob_id: blob.id } }
- = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?)
+ %pre.code.highlight
+ %code
+ = blob.present.highlight
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index dbed4b94d61..973c756f496 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -2,10 +2,19 @@
- group_path = root_url
- group_path << parent.full_path + '/' if parent
-.form-group.row
- = f.label :path, class: 'col-form-label col-sm-2' do
- Group path
- .col-sm-10
+.row
+ .form-group.group-name-holder.col-sm-12
+ = f.label :name, class: 'label-bold' do
+ = _("Group name")
+ = f.text_field :name, placeholder: 'My Awesome Group', class: 'form-control input-lg',
+ required: true,
+ title: _('Please fill in a descriptive name for your group.'),
+ autofocus: true
+
+.row
+ .form-group.col-xs-12.col-sm-8
+ = f.label :path, class: 'label-bold' do
+ = _("Group URL")
.input-group.gl-field-error-anchor
.group-root-path.input-group-prepend.has-tooltip{ title: group_path, :'data-placement' => 'bottom' }
.input-group-text
@@ -13,10 +22,10 @@
- if parent
%strong= parent.full_path + '/'
= f.hidden_field :parent_id
- = f.text_field :path, placeholder: 'open-source', class: 'form-control',
+ = f.text_field :path, placeholder: 'my-awesome-group', class: 'form-control',
autofocus: local_assigns[:autofocus] || false, required: true,
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
- title: 'Please choose a group path with no special characters.',
+ title: _('Please choose a group URL with no special characters.'),
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
- if @group.persisted?
@@ -25,23 +34,17 @@
= succeed '.' do
= link_to 'Learn more', help_page_path('user/group/index', anchor: 'changing-a-groups-path'), target: '_blank'
-.form-group.row.group-name-holder
- = f.label :name, class: 'col-form-label col-sm-2' do
- Group name
- .col-sm-10
- = f.text_field :name, class: 'form-control',
- required: true,
- title: 'You can choose a descriptive name different from the path.'
-
- if @group.persisted?
- .form-group.row.group-name-holder
- = f.label :id, class: 'col-form-label col-sm-2' do
- = _("Group ID")
- .col-sm-10
+ .row
+ .form-group.group-name-holder.col-sm-8
+ = f.label :id, class: 'label-bold' do
+ = _("Group ID")
= f.text_field :id, class: 'form-control', readonly: true
-.form-group.row.group-description-holder
- = f.label :description, class: 'col-form-label col-sm-2'
- .col-sm-10
+.row
+ .form-group.group-description-holder.col-sm-8
+ = f.label :description, class: 'label-bold' do
+ = _("Group description")
+ %span (optional)
= f.text_area :description, maxlength: 250,
class: 'form-control js-gfm-input', rows: 4
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index 2c3cbd0b986..21ea188d7b3 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -4,8 +4,6 @@
- use_label_priority = local_assigns.fetch(:use_label_priority, false)
- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority.present? : false)
- toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user
-- show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project)
-- show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project)
- tooltip_title = label_status_tooltip(label, status) if status
%li.label-list-item{ id: label_css_id, data: { id: label.id } }
@@ -25,28 +23,29 @@
%li.inline
= link_to edit_label_path(label), class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
= sprite_icon('pencil')
- %li.inline
- .dropdown
- %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' }, aria_label: _('Label actions dropdown') }
- = sprite_icon('ellipsis_v')
- .dropdown-menu.dropdown-open-left
- %ul
- - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group)
- %li
- %button.js-promote-project-label-button.btn.btn-transparent.btn-action{ disabled: true, type: 'button',
- data: { url: promote_project_label_path(label.project, label),
- label_title: label.title,
- label_color: label.color,
- label_text_color: label.text_color,
- group_name: label.project.group.name,
- target: '#promote-label-modal',
- container: 'body',
- toggle: 'modal' } }
- = _('Promote to group label')
- - if can?(current_user, :admin_label, label)
- %li
- %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } }
- %button.text-danger.remove-row{ type: 'button' }= _('Delete')
+ - if can?(current_user, :admin_label, label)
+ %li.inline
+ .dropdown
+ %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' }, aria_label: _('Label actions dropdown') }
+ = sprite_icon('ellipsis_v')
+ .dropdown-menu.dropdown-open-left
+ %ul
+ - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group)
+ %li
+ %button.js-promote-project-label-button.btn.btn-transparent.btn-action{ disabled: true, type: 'button',
+ data: { url: promote_project_label_path(label.project, label),
+ label_title: label.title,
+ label_color: label.color,
+ label_text_color: label.text_color,
+ group_name: label.project.group.name,
+ target: '#promote-label-modal',
+ container: 'body',
+ toggle: 'modal' } }
+ = _('Promote to group label')
+ - if can?(current_user, :admin_label, label)
+ %li
+ %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } }
+ %button.text-danger.remove-row{ type: 'button' }= _('Delete')
- if current_user
%li.inline.label-subscription
- if can_subscribe_to_label_in_different_levels?(label)
diff --git a/app/views/shared/_labels_row.html.haml b/app/views/shared/_labels_row.html.haml
deleted file mode 100644
index 21b37a7c9ae..00000000000
--- a/app/views/shared/_labels_row.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- labels.each do |label|
- %span.label-row.btn-group{ role: "group", aria: { label: label.name }, style: "color: #{text_color_for_bg(label.color)}" }
- = link_to_label(label, subject: @project, css_class: 'btn btn-transparent')
- %button.btn.btn-transparent.label-remove.js-label-filter-remove{ type: "button", style: "background-color: #{label.color};", data: { label: label.title } }
- = icon("times")
diff --git a/app/views/shared/_mini_pipeline_graph.html.haml b/app/views/shared/_mini_pipeline_graph.html.haml
index ac2164a4a71..28b34e38b15 100644
--- a/app/views/shared/_mini_pipeline_graph.html.haml
+++ b/app/views/shared/_mini_pipeline_graph.html.haml
@@ -3,7 +3,6 @@
- if stage.status
- detailed_status = stage.detailed_status(current_user)
- icon_status = "#{detailed_status.icon}_borderless"
- - status_klass = "ci-status-icon ci-status-icon-#{detailed_status.group}"
.stage-container.dropdown{ class: klass }
%button.mini-pipeline-graph-dropdown-toggle.has-tooltip.js-builds-dropdown-button{ class: "ci-status-icon-#{detailed_status.group}", type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_ajax_project_pipeline_path(pipeline.project, pipeline, stage: stage.name) } }
diff --git a/app/views/shared/_mobile_clone_panel.html.haml b/app/views/shared/_mobile_clone_panel.html.haml
new file mode 100644
index 00000000000..998985cabe1
--- /dev/null
+++ b/app/views/shared/_mobile_clone_panel.html.haml
@@ -0,0 +1,13 @@
+- project = project || @project
+- ssh_copy_label = _("Copy SSH clone URL")
+- http_copy_label = _("Copy HTTPS clone URL")
+
+.btn-group.mobile-git-clone.js-mobile-git-clone
+ = clipboard_button(button_text: default_clone_label, target: '#project_clone', hide_button_icon: true, class: "input-group-text clone-dropdown-btn js-clone-dropdown-label btn btn-default")
+ %button.btn.btn-default.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { toggle: "dropdown" } }
+ = icon("caret-down", class: "dropdown-btn-icon")
+ %ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } }
+ %li
+ = dropdown_item_with_description(ssh_copy_label, project.ssh_url_to_repo, href: project.ssh_url_to_repo, data: { clone_type: 'ssh' })
+ %li
+ = dropdown_item_with_description(http_copy_label, project.http_url_to_repo, href: project.http_url_to_repo, data: { clone_type: 'http' })
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index d38d161047b..9bc67a7c715 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,7 +1,7 @@
- if any_projects?(@projects)
.project-item-select-holder.btn-group
- %a.btn.btn-new.new-project-item-link.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
+ %a.btn.btn-success.new-project-item-link.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
= icon('spinner spin')
= project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path] }, with_feature_enabled: local_assigns[:with_feature_enabled]
- %button.btn.btn-new.new-project-item-select-button.qa-new-project-item-select-button
+ %button.btn.btn-success.new-project-item-select-button.qa-new-project-item-select-button
= icon('caret-down')
diff --git a/app/views/shared/_old_visibility_level.html.haml b/app/views/shared/_old_visibility_level.html.haml
new file mode 100644
index 00000000000..fd576e4fbea
--- /dev/null
+++ b/app/views/shared/_old_visibility_level.html.haml
@@ -0,0 +1,6 @@
+.form-group.row
+ .col-sm-2.col-form-label
+ = _('Visibility level')
+ = link_to icon('question-circle'), help_page_path("public_access/public_access")
+ .col-sm-10
+ = render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: can_change_visibility_level, form_model: form_model, with_label: with_label
diff --git a/app/views/shared/_personal_access_tokens_created_container.html.haml b/app/views/shared/_personal_access_tokens_created_container.html.haml
new file mode 100644
index 00000000000..3150d39b84a
--- /dev/null
+++ b/app/views/shared/_personal_access_tokens_created_container.html.haml
@@ -0,0 +1,14 @@
+- container_title = local_assigns.fetch(:container_title, 'Your New Personal Access Token')
+- clipboard_button_title = local_assigns.fetch(:clipboard_button_title, 'Copy personal access token to clipboard')
+
+.created-personal-access-token-container
+ %h5.prepend-top-0
+ = container_title
+ .form-group
+ .input-group
+ = text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-token-help-block"
+ %span.input-group-append
+ = clipboard_button(text: new_token_value, title: clipboard_button_title, placement: "left", class: "input-group-text btn-default btn-clipboard")
+ %span#created-token-help-block.form-text.text-muted.text-danger Make sure you save it - you won't be able to access it again.
+
+%hr
diff --git a/app/views/shared/_personal_access_tokens_form.html.haml b/app/views/shared/_personal_access_tokens_form.html.haml
index 58d310fac16..f4df7bdcd83 100644
--- a/app/views/shared/_personal_access_tokens_form.html.haml
+++ b/app/views/shared/_personal_access_tokens_form.html.haml
@@ -26,4 +26,4 @@
= render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: token, scopes: scopes
.prepend-top-default
- = f.submit "Create #{type} token", class: "btn btn-create"
+ = f.submit "Create #{type} token", class: "btn btn-success"
diff --git a/app/views/shared/_personal_access_tokens_table.html.haml b/app/views/shared/_personal_access_tokens_table.html.haml
index cadac1cc99d..2efd03d4867 100644
--- a/app/views/shared/_personal_access_tokens_table.html.haml
+++ b/app/views/shared/_personal_access_tokens_table.html.haml
@@ -15,8 +15,6 @@
%th Created
%th Expires
%th Scopes
- - if impersonation
- %th Token
%th
%tbody
- active_tokens.each do |token|
@@ -30,10 +28,6 @@
- else
%span.token-never-expires-label Never
%td= token.scopes.present? ? token.scopes.join(", ") : "<no scopes selected>"
- - if impersonation
- %td.token-token-container
- = text_field_tag 'impersonation-token-token', token.token, readonly: true, class: "form-control"
- = clipboard_button(text: token.token)
- path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token)
%td= link_to "Revoke", path, method: :put, class: "btn btn-danger float-right", data: { confirm: "Are you sure you want to revoke this #{type} Token? This action cannot be undone." }
- else
diff --git a/app/views/shared/_ping_consent.html.haml b/app/views/shared/_ping_consent.html.haml
new file mode 100644
index 00000000000..f8eb2b2833b
--- /dev/null
+++ b/app/views/shared/_ping_consent.html.haml
@@ -0,0 +1,12 @@
+- if session[:ask_for_usage_stats_consent]
+ .ping-consent-message.alert.alert-warning.flex-alert
+ - settings_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="alert-link">'.html_safe % { url: admin_application_settings_path(anchor: 'js-usage-settings') }
+ - info_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="alert-link">'.html_safe % { url: help_page_path('user/admin_area/settings/usage_statistics.md') }
+ .alert-message
+ = s_('To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}').html_safe % { settings_link_start: settings_link_start, info_link_start: info_link_start, link_end: '</a>'.html_safe }
+ .alert-link-group
+ - send_usage_data_path = admin_application_settings_path(application_setting: { version_check_enabled: 1, usage_ping_enabled: 1 })
+ - not_now_path = admin_application_settings_path(application_setting: { version_check_enabled: 0, usage_ping_enabled: 0 })
+ = link_to _("Send usage data"), send_usage_data_path, 'data-url' => admin_application_settings_path, method: :put, 'data-check-enabled': true, 'data-ping-enabled': true, class: 'alert-link js-usage-consent-action'
+ |
+ = link_to _('Not now'), not_now_path, 'data-url' => admin_application_settings_path, method: :put, 'data-check-enabled': false, 'data-ping-enabled': false, class: 'hide-ping-consent-message alert-link js-usage-consent-action'
diff --git a/app/views/shared/_recaptcha_form.html.haml b/app/views/shared/_recaptcha_form.html.haml
index a0ba1afc284..10f358402c1 100644
--- a/app/views/shared/_recaptcha_form.html.haml
+++ b/app/views/shared/_recaptcha_form.html.haml
@@ -17,4 +17,4 @@
- if has_submit
.row-content-block.footer-block
- = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-create'
+ = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-success'
diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml
index 4e7061eef1c..7cbc5810c10 100644
--- a/app/views/shared/_ref_switcher.html.haml
+++ b/app/views/shared/_ref_switcher.html.haml
@@ -1,5 +1,3 @@
-- show_create = local_assigns.fetch(:show_create, false)
-
- dropdown_toggle_text = @ref || @project.default_branch
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
= hidden_field_tag :destination, destination
diff --git a/app/views/shared/_user_dropdown_contributing_link.html.haml b/app/views/shared/_user_dropdown_contributing_link.html.haml
index 333d6fa3489..564d21a39be 100644
--- a/app/views/shared/_user_dropdown_contributing_link.html.haml
+++ b/app/views/shared/_user_dropdown_contributing_link.html.haml
@@ -1,5 +1,3 @@
%li
= link_to "https://about.gitlab.com/contributing", target: '_blank', class: 'text-nowrap' do
= _("Contribute to GitLab")
- = sprite_icon('external-link', size: 16)
-%li.divider
diff --git a/app/views/shared/_visibility_level.html.haml b/app/views/shared/_visibility_level.html.haml
index 01ce1225b8d..2f42a877beb 100644
--- a/app/views/shared/_visibility_level.html.haml
+++ b/app/views/shared/_visibility_level.html.haml
@@ -1,17 +1,19 @@
- with_label = local_assigns.fetch(:with_label, true)
-.form-group.row.visibility-level-setting
+.form-group.visibility-level-setting
- if with_label
- = f.label :visibility_level, class: 'col-form-label col-sm-2' do
- Visibility Level
- = link_to icon('question-circle'), help_page_path("public_access/public_access")
- %div{ :class => (with_label ? "col-sm-10" : "col-sm-12") }
- - if can_change_visibility_level
- = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: visibility_level, form_model: form_model)
- - else
- %div
- %span.info
- = visibility_level_icon(visibility_level)
- %strong
- = visibility_level_label(visibility_level)
- .light= visibility_level_description(visibility_level, form_model)
+ = f.label :visibility_level, _('Visibility level'), class: 'label-bold append-bottom-0'
+ %p
+ = _('Who can see this group?')
+ - visibility_docs_path = help_page_path('public_access/public_access')
+ - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: visibility_docs_path }
+ = s_('Check the %{docs_link_start}documentation%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: '</a>'.html_safe }
+ - if can_change_visibility_level
+ = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: visibility_level, form_model: form_model)
+ - else
+ %div
+ %span.info
+ = visibility_level_icon(visibility_level)
+ %strong
+ = visibility_level_label(visibility_level)
+ .light= visibility_level_description(visibility_level, form_model)
diff --git a/app/views/shared/_visibility_radios.html.haml b/app/views/shared/_visibility_radios.html.haml
index dd6b9cce58e..9fc46afe177 100644
--- a/app/views/shared/_visibility_radios.html.haml
+++ b/app/views/shared/_visibility_radios.html.haml
@@ -3,7 +3,7 @@
- restricted = restricted_visibility_levels.include?(level)
- disabled = disallowed || restricted
.form-check{ class: [('disabled' if disabled), ('restricted' if restricted)] }
- = form.radio_button model_method, level, checked: (selected_level == level), disabled: disabled, class: 'form-check-input'
+ = form.radio_button model_method, level, checked: (selected_level == level), disabled: disabled, class: 'form-check-input', data: { track_label: "blank_project", track_event: "activate_form_input", track_property: "#{model_method}", track_value: "#{level}" }
= form.label "#{model_method}_#{level}", class: 'form-check-label' do
= visibility_level_icon(level)
.option-title
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index 28e6fe1b16d..f0d1dd162df 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -10,7 +10,7 @@
-# haml-lint:disable InlineJavaScript
%script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board"
- %script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal
+ %script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal, show_sorting_dropdown: false
%script#js-board-promotion{ type: "text/x-template" }= render_if_exists "shared/promotions/promote_issue_board"
#board-app.boards-app{ "v-cloak" => true, data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
@@ -33,7 +33,7 @@
- if @project
%board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project),
"milestone-path" => milestones_filter_dropdown_path,
- "label-path" => labels_filter_path,
+ "label-path" => labels_filter_path_with_defaults,
"empty-state-svg" => image_path('illustrations/issues.svg'),
":issue-link-base" => "issueLinkBase",
":root-path" => "rootPath",
diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml
index e26f5260e5b..c6c5cadc3f5 100644
--- a/app/views/shared/boards/components/_board.html.haml
+++ b/app/views/shared/boards/components/_board.html.haml
@@ -39,14 +39,13 @@
{{ list.issuesSize }}
= render_if_exists "shared/boards/components/list_weight"
- - if can?(current_user, :admin_list, current_board_parent)
- %button.issue-count-badge-add-button.btn.btn-sm.btn-default.ml-1.has-tooltip.js-no-trigger-collapse{ type: "button",
- "@click" => "showNewIssueForm",
- "v-if" => 'list.type !== "closed"',
- "aria-label" => _("New issue"),
- "title" => _("New issue"),
- data: { placement: "top", container: "body" } }
- = icon("plus", class: "js-no-trigger-collapse")
+ %button.issue-count-badge-add-button.btn.btn-sm.btn-default.ml-1.has-tooltip.js-no-trigger-collapse{ type: "button",
+ "@click" => "showNewIssueForm",
+ "v-if" => "isNewIssueShown",
+ "aria-label" => _("New issue"),
+ "title" => _("New issue"),
+ data: { placement: "top", container: "body" } }
+ = icon("plus", class: "js-no-trigger-collapse")
%board-list{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"',
":list" => "list",
diff --git a/app/views/shared/boards/components/sidebar/_labels.html.haml b/app/views/shared/boards/components/sidebar/_labels.html.haml
index 532045f3697..19159684420 100644
--- a/app/views/shared/boards/components/sidebar/_labels.html.haml
+++ b/app/views/shared/boards/components/sidebar/_labels.html.haml
@@ -19,13 +19,13 @@
":value" => "label.id" }
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-board-sidebar{ type: "button",
- "v-bind:data-selected" => "selectedLabels",
+ ":data-selected" => "selectedLabels",
+ ":data-labels" => "issue.assignableLabelsEndpoint",
data: { toggle: "dropdown",
field_name: "issue[label_names][]",
show_no: "true",
show_any: "true",
project_id: @project&.try(:id),
- labels: labels_filter_path(false),
namespace_path: @namespace_path,
project_path: @project.try(:path) } }
%span.dropdown-toggle-text
diff --git a/app/views/shared/empty_states/_labels.html.haml b/app/views/shared/empty_states/_labels.html.haml
index e8749ee3956..bee26cd8312 100644
--- a/app/views/shared/empty_states/_labels.html.haml
+++ b/app/views/shared/empty_states/_labels.html.haml
@@ -6,6 +6,9 @@
.text-content
%h4= _("Labels can be applied to issues and merge requests to categorize them.")
%p= _("You can also star a label to make it a priority label.")
- - if can?(current_user, :admin_label, @project)
- = link_to _('New label'), new_project_label_path(@project), class: 'btn btn-new', title: _('New label'), id: 'new_label_link'
- = link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link'
+ .text-center
+ - if can?(current_user, :admin_label, @project)
+ = link_to _('New label'), new_project_label_path(@project), class: 'btn btn-success qa-label-create-new', title: _('New label'), id: 'new_label_link'
+ = link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link'
+ - if can?(current_user, :admin_label, @group)
+ = link_to _('New label'), new_group_label_path(@group), class: 'btn btn-success', title: _('New label'), id: 'new_label_link'
diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml
index 186139f3526..421a1b2415b 100644
--- a/app/views/shared/empty_states/_merge_requests.html.haml
+++ b/app/views/shared/empty_states/_merge_requests.html.haml
@@ -17,7 +17,7 @@
- if project_select_button
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('New merge request'), type: :merge_requests, with_feature_enabled: 'merge_requests'
- else
- = link_to _('New merge request'), button_path, class: 'btn btn-new', title: _('New merge request'), id: 'new_merge_request_link'
+ = link_to _('New merge request'), button_path, class: 'btn btn-success', title: _('New merge request'), id: 'new_merge_request_link'
- else
%h4.text-center
= _("There are no merge requests to show")
diff --git a/app/views/shared/empty_states/_wikis.html.haml b/app/views/shared/empty_states/_wikis.html.haml
index f1a41074c28..df3308abe0d 100644
--- a/app/views/shared/empty_states/_wikis.html.haml
+++ b/app/views/shared/empty_states/_wikis.html.haml
@@ -2,10 +2,10 @@
- if can?(current_user, :create_wiki, @project)
- create_path = project_wiki_path(@project, params[:id], { view: 'create' })
- - create_link = link_to s_('WikiEmpty|Create your first page'), create_path, class: 'btn btn-new', title: s_('WikiEmpty|Create your first page')
+ - create_link = link_to s_('WikiEmpty|Create your first page'), create_path, class: 'btn btn-success', title: s_('WikiEmpty|Create your first page')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_login_empty.svg' } do
- %h4
+ %h4.text-left
= s_('WikiEmpty|The wiki lets you write documentation for your project')
%p.text-left
= s_("WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on.")
@@ -13,7 +13,7 @@
- elsif can?(current_user, :read_issue, @project)
- issues_link = link_to s_('WikiEmptyIssueMessage|issue tracker'), project_issues_path(@project)
- - new_issue_link = link_to s_('WikiEmpty|Suggest wiki improvement'), new_project_issue_path(@project), class: 'btn btn-new', title: s_('WikiEmptyIssueMessage|Suggest wiki improvement')
+ - new_issue_link = link_to s_('WikiEmpty|Suggest wiki improvement'), new_project_issue_path(@project), class: 'btn btn-success', title: s_('WikiEmptyIssueMessage|Suggest wiki improvement')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4
diff --git a/app/views/shared/groups/_empty_state.html.haml b/app/views/shared/groups/_empty_state.html.haml
index 13bb4baee3f..f6b3a49eacb 100644
--- a/app/views/shared/groups/_empty_state.html.haml
+++ b/app/views/shared/groups/_empty_state.html.haml
@@ -1,7 +1,8 @@
-.groups-empty-state
- = custom_icon("icon_empty_groups")
+.group-empty-state.row.align-items-center.justify-content-center
+ .icon.text-center.order-md-2
+ = custom_icon("icon_empty_groups")
- .text-content
+ .text-content.m-0.order-md-1
%h4= s_("GroupsEmptyState|A group is a collection of several projects.")
%p= s_("GroupsEmptyState|If you organize your projects under a group, it works like a folder.")
%p= s_("GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group.")
diff --git a/app/views/shared/groups/_search_form.html.haml b/app/views/shared/groups/_search_form.html.haml
index 3f91263089a..49b812baefc 100644
--- a/app/views/shared/groups/_search_form.html.haml
+++ b/app/views/shared/groups/_search_form.html.haml
@@ -1,2 +1,2 @@
-= form_tag request.path, method: :get, class: 'group-filter-form append-right-10', id: 'group-filter-form' do |f|
- = search_field_tag :filter, params[:filter], placeholder: s_('GroupsTree|Filter by name...'), class: 'group-filter-form-field form-control input-short js-groups-list-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2"
+= form_tag request.path, method: :get, class: "group-filter-form js-group-filter-form", id: 'group-filter-form' do |f|
+ = search_field_tag :filter, params[:filter], placeholder: s_('GroupsTree|Search by name'), class: 'group-filter-form-field form-control js-groups-list-filter qa-groups-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2"
diff --git a/app/views/shared/icons/_icon_status_scheduled.svg b/app/views/shared/icons/_icon_status_scheduled.svg
new file mode 100644
index 00000000000..ca6e4efce50
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_scheduled.svg
@@ -0,0 +1 @@
+<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><circle cx="7" cy="7" r="7"/><circle fill="#FFF" cx="7" cy="7" r="6"/><g transform="translate(2.75 2.75)" fill-rule="nonzero"><path d="M4.165 7.81a3.644 3.644 0 1 1 0-7.29 3.644 3.644 0 0 1 0 7.29zm0-1.042a2.603 2.603 0 1 0 0-5.206 2.603 2.603 0 0 0 0 5.206z"/><rect x="3.644" y="2.083" width="1.041" height="2.603" rx=".488"/><rect x="3.644" y="3.644" width="2.083" height="1.041" rx=".488"/></g></svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_icon_status_scheduled_borderless.svg b/app/views/shared/icons/_icon_status_scheduled_borderless.svg
new file mode 100644
index 00000000000..dc38c01d898
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_scheduled_borderless.svg
@@ -0,0 +1 @@
+<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M6.16 11.55a5.39 5.39 0 1 1 0-10.78 5.39 5.39 0 0 1 0 10.78zm0-1.54a3.85 3.85 0 1 0 0-7.7 3.85 3.85 0 0 0 0 7.7z"/><rect x="5.39" y="3.08" width="1.54" height="3.85" rx=".767"/><rect x="5.39" y="5.39" width="3.08" height="1.54" rx=".767"/></svg> \ No newline at end of file
diff --git a/app/views/shared/issuable/_assignees.html.haml b/app/views/shared/issuable/_assignees.html.haml
index fc86f855865..ef3d44a9241 100644
--- a/app/views/shared/issuable/_assignees.html.haml
+++ b/app/views/shared/issuable/_assignees.html.haml
@@ -3,7 +3,7 @@
- render_count = assignees_rendering_overflow ? max_render - 1 : max_render
- more_assignees_count = issue.assignees.size - render_count
-- issue.assignees.take(render_count).each do |assignee|
+- issue.assignees.take(render_count).each do |assignee| # rubocop: disable CodeReuse/ActiveRecord
= link_to_member(@project, assignee, name: false, title: "Assigned to :name")
- if more_assignees_count.positive?
diff --git a/app/views/shared/issuable/_board_create_list_dropdown.html.haml b/app/views/shared/issuable/_board_create_list_dropdown.html.haml
index 23b2e1b91e5..4597d9439fa 100644
--- a/app/views/shared/issuable/_board_create_list_dropdown.html.haml
+++ b/app/views/shared/issuable/_board_create_list_dropdown.html.haml
@@ -1,5 +1,5 @@
.dropdown.prepend-left-10#js-add-list
- %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data }
+ %button.btn.btn-success.btn-inverted.js-new-board-list{ type: "button", data: board_list_data }
Add list
.dropdown-menu.dropdown-menu-paging.dropdown-menu-right.dropdown-menu-issues-board-new.dropdown-menu-selectable.js-tab-container-labels
= render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
diff --git a/app/views/shared/issuable/_close_reopen_button.html.haml b/app/views/shared/issuable/_close_reopen_button.html.haml
index 933d4b2ea65..4f6a71b6071 100644
--- a/app/views/shared/issuable/_close_reopen_button.html.haml
+++ b/app/views/shared/issuable/_close_reopen_button.html.haml
@@ -1,14 +1,18 @@
- is_current_user = issuable_author_is_current_user(issuable)
- display_issuable_type = issuable_display_type(issuable)
- button_method = issuable_close_reopen_button_method(issuable)
+- are_close_and_open_buttons_hidden = issuable_button_hidden?(issuable, true) && issuable_button_hidden?(issuable, false)
-- if can_update && is_current_user
- = link_to "Close #{display_issuable_type}", close_issuable_path(issuable), method: button_method,
- class: "d-none d-sm-none d-md-block btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)}", title: "Close #{display_issuable_type}"
- = link_to "Reopen #{display_issuable_type}", reopen_issuable_path(issuable), method: button_method,
- class: "d-none d-sm-none d-md-block btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}"
-- elsif can_update && !is_current_user
- = render 'shared/issuable/close_reopen_report_toggle', issuable: issuable
+- if is_current_user
+ - if can_update
+ = link_to "Close #{display_issuable_type}", close_issuable_path(issuable), method: button_method,
+ class: "d-none d-sm-none d-md-block btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)}", title: "Close #{display_issuable_type}"
+ - if can_reopen
+ = link_to "Reopen #{display_issuable_type}", reopen_issuable_path(issuable), method: button_method,
+ class: "d-none d-sm-none d-md-block btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}"
- else
- = link_to 'Report abuse', new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
- class: 'd-none d-sm-none d-md-block btn btn-grouped btn-close-color', title: 'Report abuse'
+ - if can_update && !are_close_and_open_buttons_hidden
+ = render 'shared/issuable/close_reopen_report_toggle', issuable: issuable
+ - else
+ = link_to 'Report abuse', new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
+ class: 'd-none d-sm-none d-md-block btn btn-grouped btn-close-color', title: 'Report abuse'
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
deleted file mode 100644
index 1cd8ce0826c..00000000000
--- a/app/views/shared/issuable/_filter.html.haml
+++ /dev/null
@@ -1,34 +0,0 @@
-- boards_page = controller.controller_name == 'boards'
-
-.issues-filters
- .issues-details-filters.row-content-block.second-block
- = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search]), method: :get, class: 'filter-form js-filter-form' do
- - if params[:search].present?
- = hidden_field_tag :search, params[:search]
- .issues-other-filters
- .filter-item.inline
- - if params[:author_id].present?
- = hidden_field_tag(:author_id, params[:author_id])
- = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit",
- placeholder: "Search authors", data: { any_user: "Any Author", first_user: current_user&.username, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
-
- .filter-item.inline
- - if params[:assignee_id].present?
- = hidden_field_tag(:assignee_id, params[:assignee_id])
- = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
- placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user&.username, null_user: true, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
-
- .filter-item.inline.milestone-filter
- = render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, show_started: true
-
- .filter-item.inline.labels-filter
- = render "shared/issuable/label_dropdown", selected: selected_labels, use_id: false, selected_toggle: params[:label_name], data_options: { field_name: "label_name[]" }
-
- - unless @no_filters_set
- .float-right
- = render 'shared/sort_dropdown'
-
- - has_labels = @labels && @labels.any?
- .row-content-block.second-block.filtered-labels{ class: ("hidden" unless has_labels) }
- - if has_labels
- = render 'shared/labels_row', labels: @labels
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index b49e47a7266..b33c758b464 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -1,6 +1,7 @@
- form = local_assigns.fetch(:f)
- commits = local_assigns[:commits]
- project = @target_project || @project
+- presenter = local_assigns.fetch(:presenter, nil)
= form_errors(issuable)
@@ -29,7 +30,7 @@
= render 'shared/issuable/form/metadata', issuable: issuable, form: form
-= render_if_exists 'shared/issuable/approvals', issuable: issuable, form: form
+= render_if_exists 'shared/issuable/approvals', issuable: issuable, presenter: presenter, form: form
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
@@ -69,9 +70,9 @@
%span.append-right-10
- if issuable.new_record?
- = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create qa-issuable-create-button'
+ = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-success qa-issuable-create-button'
- else
- = form.submit 'Save changes', class: 'btn btn-save'
+ = form.submit 'Save changes', class: 'btn btn-success'
- if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = issuable.project.present.contribution_guide_path)
.inline.prepend-top-10
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 34911fd2712..d5fb85ba0f3 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -7,9 +7,8 @@
- data_options = local_assigns.fetch(:data_options, {})
- classes = local_assigns.fetch(:classes, [])
- selected = local_assigns.fetch(:selected, nil)
-- selected_toggle = local_assigns.fetch(:selected_toggle, nil)
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by label")
-- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), labels: labels_filter_path, default_label: "Labels"}
+- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), labels: labels_filter_path_with_defaults, default_label: "Labels"}
- dropdown_data.merge!(data_options)
- label_name = local_assigns.fetch(:label_name, "Labels")
- no_default_styles = local_assigns.fetch(:no_default_styles, false)
@@ -21,7 +20,7 @@
= hidden_field_tag data_options[:field_name], use_id ? label.try(:id) : label.try(:title), id: nil
.dropdown
- %button.dropdown-menu-toggle.js-label-select.js-multiselect{ class: classes.join(' '), type: "button", data: dropdown_data }
+ %button.dropdown-menu-toggle.js-label-select.js-multiselect.qa-issuable-label{ class: classes.join(' '), type: "button", data: dropdown_data }
- apply_is_default_styles = (selected.nil? || selected.empty?) && !no_default_styles
%span.dropdown-toggle-text{ class: ("is-default" if apply_is_default_styles) }
= multi_label_name(selected, label_name)
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 9ce7f6fe269..824bbe3524b 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -1,15 +1,15 @@
- type = local_assigns.fetch(:type)
- board = local_assigns.fetch(:board, nil)
- block_css_class = type != :boards_modal ? 'row-content-block second-block' : ''
-- full_path = @project.present? ? @project.full_path : @group.full_path
- user_can_admin_list = board && can?(current_user, :admin_list, board.parent)
+- show_sorting_dropdown = local_assigns.fetch(:show_sorting_dropdown, true)
.issues-filters
.issues-details-filters.filtered-search-block{ class: block_css_class, "v-pre" => type == :boards_modal }
- if type == :boards
#js-multiple-boards-switcher.inline.boards-switcher{ "v-cloak" => true }
= render_if_exists "shared/boards/switcher", board: board
- = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search]), method: :get, class: 'filter-form js-filter-form' do
+ = form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form' do
- if params[:search].present?
= hidden_field_tag :search, params[:search]
- if @can_bulk_update
@@ -24,7 +24,7 @@
dropdown_class: "filtered-search-history-dropdown",
content_class: "filtered-search-history-dropdown-content",
title: "Recent searches" }) do
- .js-filtered-search-history-dropdown{ data: { full_path: full_path } }
+ .js-filtered-search-history-dropdown{ data: { full_path: search_history_storage_prefix } }
.filtered-search-box-input-container.droplab-dropdown
.scroll-container
%ul.tokens-container.list-unstyled
@@ -33,16 +33,17 @@
#js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { action: 'submit' } }
- %button.btn.btn-link
- = icon('search')
+ %button.btn.btn-link{ type: 'button' }
+ = sprite_icon('search')
%span
- Press Enter or click to search
+ = _('Press Enter or click to search')
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
- %button.btn.btn-link
+ %button.btn.btn-link{ type: 'button' }
-# Encapsulate static class name `{{icon}}` inside #{} to bypass
-# haml lint's ClassAttributeWithStaticValue
- %i.fa{ class: "#{'{{icon}}'}" }
+ %svg
+ %use{ 'xlink:href': "#{'{{icon}}'}" }
%span.js-filter-hint
{{hint}}
%span.js-filter-tag.dropdown-light-content
@@ -59,8 +60,11 @@
#js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
- %button.btn.btn-link
- No Assignee
+ %button.btn.btn-link{ type: 'button' }
+ = _('None')
+ %li.filter-dropdown-item{ data: { value: 'any' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Any')
%li.divider.droplab-item-ignore
- if current_user
= render 'shared/issuable/user_dropdown_item',
@@ -72,38 +76,57 @@
#js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
- %button.btn.btn-link
- No Milestone
+ %button.btn.btn-link{ type: 'button' }
+ = _('None')
+ %li.filter-dropdown-item{ data: { value: 'any' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Any')
%li.filter-dropdown-item{ data: { value: 'upcoming' } }
- %button.btn.btn-link
- Upcoming
- %li.filter-dropdown-item{ 'data-value' => 'started' }
- %button.btn.btn-link
- Started
+ %button.btn.btn-link{ type: 'button' }
+ = _('Upcoming')
+ %li.filter-dropdown-item{ data: { value: 'started' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Started')
%li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
- %button.btn.btn-link.js-data-value
+ %button.btn.btn-link.js-data-value{ type: 'button' }
{{title}}
#js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
- %button.btn.btn-link
- No Label
+ %button.btn.btn-link{ type: 'button' }
+ = _('No Label')
%li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
- %button.btn.btn-link
+ %button.btn.btn-link{ type: 'button' }
%span.dropdown-label-box{ style: 'background: {{color}}' }
%span.label-title.js-data-value
{{title}}
#js-dropdown-my-reaction.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'none' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('None')
+ %li.filter-dropdown-item{ data: { value: 'any' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Any')
+ %li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
- %button.btn.btn-link
+ %button.btn.btn-link{ type: 'button' }
%gl-emoji
%span.js-data-value.prepend-left-10
{{name}}
+ #js-dropdown-wip.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul.filter-dropdown{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'yes', capitalize: true } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Yes')
+ %li.filter-dropdown-item{ data: { value: 'no', capitalize: true } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('No')
= render_if_exists 'shared/issuable/filter_weight', type: type
@@ -117,5 +140,5 @@
- if @project
#js-add-issues-btn.prepend-left-10{ data: { can_admin_list: can?(current_user, :admin_list, @project) } }
#js-toggle-focus-btn
- - elsif type != :boards_modal
+ - elsif show_sorting_dropdown
= render 'shared/sort_dropdown'
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 0ca35ea1298..5295e656ab0 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -24,7 +24,7 @@
.block.milestone
.sidebar-collapsed-icon.has-tooltip{ title: milestone_tooltip_title(issuable.milestone), data: { container: 'body', html: 'true', placement: 'left', boundary: 'viewport' } }
= icon('clock-o', 'aria-hidden': 'true')
- %span.milestone-title
+ %span.milestone-title.collapse-truncated-title
- if issuable.milestone
= issuable.milestone.title
- else
@@ -98,7 +98,7 @@
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right'
- .value.issuable-show-labels.dont-hide.hide-collapsed{ class: ("has-labels" if selected_labels.any?) }
+ .value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) }
- if selected_labels.any?
- selected_labels.each do |label|
= link_to_label(label, subject: issuable.project, type: issuable.to_ability_name)
@@ -109,7 +109,7 @@
- selected_labels.each do |label|
= hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil
.dropdown
- %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path(false) if @project), display: 'static' } }
+ %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path_with_defaults if @project), display: 'static' } }
%span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
= multi_label_name(selected_labels, "Labels")
= icon('chevron-down', 'aria-hidden': 'true')
@@ -159,7 +159,7 @@
= dropdown_content
= dropdown_loading
= dropdown_footer add_content_class: true do
- %button.btn.btn-new.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ disabled: true }
+ %button.btn.btn-success.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ disabled: true }
= _('Move')
= icon('spinner spin', class: 'sidebar-move-issue-confirmation-loading-icon')
diff --git a/app/views/shared/issuable/_sidebar_todo.html.haml b/app/views/shared/issuable/_sidebar_todo.html.haml
index 583b33a8a1b..660ee6d5777 100644
--- a/app/views/shared/issuable/_sidebar_todo.html.haml
+++ b/app/views/shared/issuable/_sidebar_todo.html.haml
@@ -1,6 +1,6 @@
- is_collapsed = local_assigns.fetch(:is_collapsed, false)
-- mark_content = is_collapsed ? icon('check-square', class: 'todo-undone') : _('Mark todo as done')
-- todo_content = is_collapsed ? icon('plus-square') : _('Add todo')
+- mark_content = is_collapsed ? sprite_icon('todo-done', css_class: 'todo-undone') : _('Mark todo as done')
+- todo_content = is_collapsed ? sprite_icon('todo-add') : _('Add todo')
%button.issuable-todo-btn.js-issuable-todo{ type: 'button',
class: (is_collapsed ? 'btn-blank sidebar-collapsed-icon dont-change-state has-tooltip' : 'btn btn-default issuable-header-btn float-right'),
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
index 3b017c62a80..ac8d58c0bfe 100644
--- a/app/views/shared/issuable/form/_metadata.html.haml
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -3,7 +3,6 @@
- return unless can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
- has_due_date = issuable.has_attribute?(:due_date)
-- has_labels = @labels && @labels.any?
- form = local_assigns.fetch(:form)
%hr
@@ -35,4 +34,4 @@
= form.label :due_date, "Due date", class: "col-form-label col-md-2 col-lg-4"
.col-8
.issuable-form-select-holder
- = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
+ = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date", autocomplete: 'off'
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index e49bdec386a..56c4b021eab 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -9,7 +9,7 @@
autocomplete: 'off', class: 'form-control pad qa-issuable-form-title', placeholder: _('Title')
- if issuable.respond_to?(:work_in_progress?)
- %p.form-text.text-muted
+ .form-text.text-muted
.js-wip-explanation
%a.js-toggle-wip{ href: '', tabindex: -1 }
Remove the
diff --git a/app/views/shared/labels/_form.html.haml b/app/views/shared/labels/_form.html.haml
index 2bf5efae1e6..7619d0a2e9c 100644
--- a/app/views/shared/labels/_form.html.haml
+++ b/app/views/shared/labels/_form.html.haml
@@ -4,18 +4,18 @@
.form-group.row
= f.label :title, class: 'col-form-label col-sm-2'
.col-sm-10
- = f.text_field :title, class: "form-control", required: true, autofocus: true
+ = f.text_field :title, class: "form-control qa-label-title", required: true, autofocus: true
.form-group.row
= f.label :description, class: 'col-form-label col-sm-2'
.col-sm-10
- = f.text_field :description, class: "form-control js-quick-submit"
+ = f.text_field :description, class: "form-control js-quick-submit qa-label-description"
.form-group.row
= f.label :color, "Background color", class: 'col-form-label col-sm-2'
.col-sm-10
.input-group
.input-group-prepend
.input-group-text.label-color-preview &nbsp;
- = f.text_field :color, class: "form-control"
+ = f.text_field :color, class: "form-control qa-label-color"
.form-text.text-muted
Choose any color.
%br
@@ -28,7 +28,7 @@
.form-actions
- if @label.persisted?
- = f.submit 'Save changes', class: 'btn btn-save js-save-button'
+ = f.submit 'Save changes', class: 'btn btn-success js-save-button'
- else
- = f.submit 'Create label', class: 'btn btn-create js-save-button'
+ = f.submit 'Create label', class: 'btn btn-success js-save-button qa-label-create-button'
= link_to 'Cancel', back_path, class: 'btn btn-cancel'
diff --git a/app/views/shared/labels/_nav.html.haml b/app/views/shared/labels/_nav.html.haml
new file mode 100644
index 00000000000..98572db738b
--- /dev/null
+++ b/app/views/shared/labels/_nav.html.haml
@@ -0,0 +1,20 @@
+- subscribed = params[:subscribed]
+
+.top-area.adjust
+ %ul.nav-links.nav.nav-tabs
+ %li{ class: active_when(subscribed != 'true') }>
+ = link_to labels_filter_path do
+ = _('All')
+ - if current_user
+ %li{ class: active_when(subscribed == 'true') }>
+ = link_to labels_filter_path(subscribed: 'true') do
+ = _('Subscribed')
+ .nav-controls
+ = form_tag labels_filter_path, method: :get do
+ = hidden_field_tag :subscribed, params[:subscribed]
+ .input-group
+ = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false }
+ %span.input-group-append
+ %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') }
+ = icon("search")
+ = render 'shared/labels/sort_dropdown'
diff --git a/app/views/shared/labels/_sort_dropdown.html.haml b/app/views/shared/labels/_sort_dropdown.html.haml
new file mode 100644
index 00000000000..8a7d037e15b
--- /dev/null
+++ b/app/views/shared/labels/_sort_dropdown.html.haml
@@ -0,0 +1,9 @@
+- sort_title = label_sort_options_hash[@sort] || sort_title_name_desc
+.dropdown.inline
+ %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown' } }
+ = sort_title
+ = icon('chevron-down')
+ %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-sort
+ %li
+ - label_sort_options_hash.each do |value, title|
+ = sortable_item(title, page_filter_path(sort: value, label: true, subscribed: params[:subscribed]), sort_title)
diff --git a/app/views/shared/members/_access_request_buttons.html.haml b/app/views/shared/members/_access_request_buttons.html.haml
index 40224cec9e8..ebae58f28ba 100644
--- a/app/views/shared/members/_access_request_buttons.html.haml
+++ b/app/views/shared/members/_access_request_buttons.html.haml
@@ -1,13 +1,13 @@
- model_name = source.model_name.to_s.downcase
-- if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id))
+- if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id)) # rubocop: disable CodeReuse/ActiveRecord
.project-action-button.inline
- link_text = source.is_a?(Group) ? _('Leave group') : _('Leave project')
= link_to link_text, polymorphic_path([:leave, source, :members]),
method: :delete,
data: { confirm: leave_confirmation_message(source) },
class: 'btn'
-- elsif requester = source.requesters.find_by(user_id: current_user.id)
+- elsif requester = source.requesters.find_by(user_id: current_user.id) # rubocop: disable CodeReuse/ActiveRecord
.project-action-button.inline
= link_to _('Withdraw Access Request'), polymorphic_path([:leave, source, :members]),
method: :delete,
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index af29c0fe59e..b4b3f4a6b7e 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -14,6 +14,8 @@
= user_status(user)
%span.cgray= user.to_reference
+ = render_if_exists 'shared/members/ee/sso_badge', member: member
+
- if user == current_user
%span.badge.badge-success.prepend-left-5 It's you
@@ -22,7 +24,7 @@
%strong Blocked
- if user.two_factor_enabled?
- %label.label.label-info
+ %label.badge.badge-info
2FA
- if source.instance_of?(Group) && source != @group
diff --git a/app/views/shared/milestones/_delete_button.html.haml b/app/views/shared/milestones/_delete_button.html.haml
new file mode 100644
index 00000000000..e236c24b088
--- /dev/null
+++ b/app/views/shared/milestones/_delete_button.html.haml
@@ -0,0 +1,14 @@
+- milestone_url = @milestone.project_milestone? ? project_milestone_path(@project, @milestone) : group_milestone_path(@group, @milestone)
+
+%button.js-delete-milestone-button.btn.btn-grouped.btn-danger{ data: { toggle: 'modal',
+ target: '#delete-milestone-modal',
+ milestone_id: @milestone.id,
+ milestone_title: markdown_field(@milestone, :title),
+ milestone_url: milestone_url,
+ milestone_issue_count: @milestone.issues.count,
+ milestone_merge_request_count: @milestone.merge_requests.count },
+ disabled: true }
+ = _('Delete')
+ = icon('spin spinner', class: 'js-loading-icon hidden' )
+
+#delete-milestone-modal
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index c559945a9c9..3dd2842be4f 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -16,6 +16,9 @@
= milestone_date_range(milestone)
%div
= render('shared/milestone_expired', milestone: milestone)
+ - if milestone.group_milestone?
+ .label-badge.label-badge-blue.d-inline-block
+ = milestone.group.full_name
- if milestone.legacy_group_milestone?
.projects
- milestone.milestones.each do |milestone|
@@ -49,7 +52,7 @@
- unless milestone.active?
= link_to 'Reopen Milestone', project_milestone_path(@project, milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
- if @group
- - if can?(current_user, :admin_milestones, @group)
+ - if can?(current_user, :admin_milestone, @group)
- if milestone.closed?
= link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
- else
diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml
index 320e3788a0f..0499b04a482 100644
--- a/app/views/shared/milestones/_top.html.haml
+++ b/app/views/shared/milestones/_top.html.haml
@@ -23,7 +23,7 @@
= milestone_date_range(milestone)
- if group
.float-right
- - if can?(current_user, :admin_milestones, group)
+ - if can?(current_user, :admin_milestone, group)
- if milestone.group_milestone?
= link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do
Edit
@@ -32,6 +32,9 @@
- else
= link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
+ - unless is_dynamic_milestone
+ = render 'shared/milestones/delete_button'
+
= render 'shared/milestones/deprecation_message' if is_dynamic_milestone
.detail-page-description.milestone-detail
diff --git a/app/views/shared/notes/_comment_button.html.haml b/app/views/shared/notes/_comment_button.html.haml
index ed336df4e9d..0674c822d63 100644
--- a/app/views/shared/notes/_comment_button.html.haml
+++ b/app/views/shared/notes/_comment_button.html.haml
@@ -1,7 +1,7 @@
- noteable_name = @note.noteable.human_class_name
.float-left.btn-group.append-right-10.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown
- %input.btn.btn-nr.btn-create.comment-btn.js-comment-button.js-comment-submit-button{ type: 'submit', value: 'Comment' }
+ %input.btn.btn-nr.btn-success.comment-btn.js-comment-button.js-comment-submit-button{ type: 'submit', value: 'Comment' }
- if @note.can_be_discussion_note?
= button_tag type: 'button', class: 'btn btn-nr dropdown-toggle comment-btn js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => 'Open comment type dropdown' do
diff --git a/app/views/shared/notes/_edit_form.html.haml b/app/views/shared/notes/_edit_form.html.haml
index 71a5b94e958..fec966069b9 100644
--- a/app/views/shared/notes/_edit_form.html.haml
+++ b/app/views/shared/notes/_edit_form.html.haml
@@ -9,6 +9,6 @@
.note-form-actions.clearfix
.settings-message.note-edit-warning.js-finish-edit-warning
Finish editing this message first!
- = submit_tag 'Save comment', class: 'btn btn-nr btn-save js-comment-save-button'
+ = submit_tag 'Save comment', class: 'btn btn-nr btn-success js-comment-save-button'
%button.btn.btn-nr.btn-cancel.note-edit-cancel{ type: 'button' }
Cancel
diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml
index 84adbd444c5..bc918430823 100644
--- a/app/views/shared/notes/_note.html.haml
+++ b/app/views/shared/notes/_note.html.haml
@@ -5,7 +5,7 @@
- note_editable = can?(current_user, :admin_note, note)
- note_counter = local_assigns.fetch(:note_counter, 0)
-%li.timeline-entry{ id: dom_id(note),
+%li.timeline-entry.note-wrapper.outlined{ id: dom_id(note),
class: ["note", "note-row-#{note.id}", ('system-note' if note.system)],
data: { author_id: note.author.id,
editable: note_editable,
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index 9dd1c24fdfa..4c4050c6054 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -7,8 +7,8 @@
= render 'shared/notes/edit_form', project: @project
- if can_create_note?
- %ul.notes.notes-form.timeline
- %li.timeline-entry
+ .notes.notes-form.timeline
+ .timeline-entry.note-form
.timeline-entry-inner
.flash-container.timeline-content
diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml
index 09ddf732ada..f6c7ca70ebd 100644
--- a/app/views/shared/notifications/_button.html.haml
+++ b/app/views/shared/notifications/_button.html.haml
@@ -9,11 +9,11 @@
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
- %button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting) } }
+ %button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= icon('caret-down')
.sr-only Toggle dropdown
- else
- %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), display: 'static' } }
+ %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
= icon("caret-down")
diff --git a/app/views/shared/notifications/_custom_notifications.html.haml b/app/views/shared/notifications/_custom_notifications.html.haml
index 1f6e8f98bbb..43a87fd8397 100644
--- a/app/views/shared/notifications/_custom_notifications.html.haml
+++ b/app/views/shared/notifications/_custom_notifications.html.haml
@@ -19,7 +19,7 @@
- paragraph = _('Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.') % { notification_link: notification_link.html_safe }
#{ paragraph.html_safe }
.col-lg-8
- - NotificationSetting::EMAIL_EVENTS.each_with_index do |event, index|
+ - notification_setting.email_events.each_with_index do |event, index|
- field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]"
.form-group
.form-check{ class: ("prepend-top-0" if index == 0) }
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index e1da05d8f08..06eb3d03e31 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -8,6 +8,7 @@
- user = local_assigns[:user]
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true
- remote = false unless local_assigns[:remote] == true
+- skip_pagination = false unless local_assigns[:skip_pagination] == true
.js-projects-list-holder
- if any_projects?(projects)
@@ -25,6 +26,6 @@
= icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
%strong= pluralize(@private_forks_count, 'private fork')
%span &nbsp;you have no access to.
- = paginate_collection(projects, remote: remote)
+ = paginate_collection(projects, remote: remote) unless skip_pagination
- else
.nothing-here-block No projects found
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index be053d481e4..aba790e1217 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -1,9 +1,7 @@
- avatar = true unless local_assigns[:avatar] == false
- stars = true unless local_assigns[:stars] == false
- forks = false unless local_assigns[:forks] == true
-- ci = false unless local_assigns[:ci] == true
- skip_namespace = false unless local_assigns[:skip_namespace] == true
-- user = local_assigns[:user]
- access = max_project_member_access(project)
- css_class = '' unless local_assigns[:css_class]
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && can_show_last_commit_in_list?(project)
diff --git a/app/views/shared/projects/_search_form.html.haml b/app/views/shared/projects/_search_form.html.haml
index b89194bcc67..3b5c13ed93a 100644
--- a/app/views/shared/projects/_search_form.html.haml
+++ b/app/views/shared/projects/_search_form.html.haml
@@ -21,3 +21,5 @@
- if params[:visibility_level].present?
= hidden_field_tag :visibility_level, params[:visibility_level]
+
+ = render_if_exists 'shared/projects/search_fields'
diff --git a/app/views/shared/runners/_form.html.haml b/app/views/shared/runners/_form.html.haml
index 0337680d79b..daf08d9bb2c 100644
--- a/app/views/shared/runners/_form.html.haml
+++ b/app/views/shared/runners/_form.html.haml
@@ -1,56 +1,56 @@
= form_for runner, url: runner_form_url do |f|
= form_errors(runner)
.form-group.row
- = label :active, "Active", class: 'col-form-label col-sm-2'
+ = label :active, _("Active"), class: 'col-form-label col-sm-2'
.col-sm-10
.form-check
= f.check_box :active, { class: 'form-check-input' }
- %span.light Paused Runners don't accept new jobs
+ %label.light{ for: :runner_active }= _("Paused Runners don't accept new jobs")
.form-group.row
- = label :protected, "Protected", class: 'col-form-label col-sm-2'
+ = label :protected, _("Protected"), class: 'col-form-label col-sm-2'
.col-sm-10
.form-check
= f.check_box :access_level, { class: 'form-check-input' }, 'ref_protected', 'not_protected'
- %span.light This runner will only run on pipelines triggered on protected branches
+ %label.light{ for: :runner_access_level }= _('This runner will only run on pipelines triggered on protected branches')
.form-group.row
- = label :run_untagged, 'Run untagged jobs', class: 'col-form-label col-sm-2'
+ = label :run_untagged, _('Run untagged jobs'), class: 'col-form-label col-sm-2'
.col-sm-10
.form-check
= f.check_box :run_untagged, { class: 'form-check-input' }
- %span.light Indicates whether this runner can pick jobs without tags
+ %label.light{ for: :runner_run_untagged }= _('Indicates whether this runner can pick jobs without tags')
- unless runner.group_type?
.form-group.row
= label :locked, _('Lock to current projects'), class: 'col-form-label col-sm-2'
.col-sm-10
.form-check
= f.check_box :locked, { class: 'form-check-input' }
- %span.light= _('When a runner is locked, it cannot be assigned to other projects')
+ %label.light{ for: :runner_locked }= _('When a runner is locked, it cannot be assigned to other projects')
.form-group.row
= label_tag :token, class: 'col-form-label col-sm-2' do
- Token
+ = _('Token')
.col-sm-10
= f.text_field :token, class: 'form-control', readonly: true
.form-group.row
= label_tag :ip_address, class: 'col-form-label col-sm-2' do
- IP Address
+ = _('IP Address')
.col-sm-10
= f.text_field :ip_address, class: 'form-control', readonly: true
.form-group.row
= label_tag :description, class: 'col-form-label col-sm-2' do
- Description
+ = _('Description')
.col-sm-10
= f.text_field :description, class: 'form-control'
.form-group.row
= label_tag :maximum_timeout_human_readable, class: 'col-form-label col-sm-2' do
- Maximum job timeout
+ = _('Maximum job timeout')
.col-sm-10
= f.text_field :maximum_timeout_human_readable, class: 'form-control'
- .form-text.text-muted This timeout will take precedence when lower than Project-defined timeout
+ .form-text.text-muted= _('This timeout will take precedence when lower than Project-defined timeout')
.form-group.row
= label_tag :tag_list, class: 'col-form-label col-sm-2' do
- Tags
+ = _('Tags')
.col-sm-10
= f.text_field :tag_list, value: runner.tag_list.sort.join(', '), class: 'form-control'
- .form-text.text-muted You can setup jobs to only use Runners with specific tags. Separate tags with commas.
+ .form-text.text-muted= _('You can set up jobs to only use Runners with specific tags. Separate tags with commas.')
.form-actions
- = f.submit 'Save changes', class: 'btn btn-save'
+ = f.submit _('Save changes'), class: 'btn btn-success'
diff --git a/app/views/shared/runners/_runner_description.html.haml b/app/views/shared/runners/_runner_description.html.haml
index da5c032add5..5935750ca06 100644
--- a/app/views/shared/runners/_runner_description.html.haml
+++ b/app/views/shared/runners/_runner_description.html.haml
@@ -1,6 +1,6 @@
.light.prepend-top-default
%p
- = _("A 'Runner' is a process which runs a job. You can setup as many Runners as you need.")
+ = _("A 'Runner' is a process which runs a job. You can set up as many Runners as you need.")
%br
= _('Runners can be placed on separate users, servers, and even on your local machine.')
diff --git a/app/views/shared/runners/show.html.haml b/app/views/shared/runners/show.html.haml
index 362569bfbaf..f62eed694d2 100644
--- a/app/views/shared/runners/show.html.haml
+++ b/app/views/shared/runners/show.html.haml
@@ -24,7 +24,7 @@
%td= @runner.active? ? 'Yes' : 'No'
%tr
%td Protected
- %td= @runner.active? ? _('Yes') : _('No')
+ %td= @runner.ref_protected? ? 'Yes' : 'No'
%tr
%td Can run untagged jobs
%td= @runner.run_untagged? ? 'Yes' : 'No'
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 5e5c050d5c3..cf9c3055499 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -14,7 +14,7 @@
= render 'shared/form_elements/description', model: @snippet, project: @project, form: f
- = render 'shared/visibility_level', f: f, visibility_level: @snippet.visibility_level, can_change_visibility_level: true, form_model: @snippet
+ = render 'shared/old_visibility_level', f: f, visibility_level: @snippet.visibility_level, can_change_visibility_level: true, form_model: @snippet, with_label: false
.file-editor
.form-group.row
@@ -32,9 +32,9 @@
.form-actions
- if @snippet.new_record?
- = f.submit 'Create snippet', class: "btn-create btn"
+ = f.submit 'Create snippet', class: "btn-success btn"
- else
- = f.submit 'Save changes', class: "btn-save btn"
+ = f.submit 'Save changes', class: "btn-success btn"
- if @snippet.project_id
= link_to "Cancel", project_snippets_path(@project), class: "btn btn-cancel"
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index 07ebb8680d2..9c5b9593bba 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -17,6 +17,7 @@
%strong Push events
%p.light.ml-1
This URL will be triggered by a push to the repository
+ = form.text_field :push_events_branch_filter, class: 'form-control', placeholder: 'Branch name or wildcard pattern to trigger on (leave blank for all)'
%li
= form.check_box :tag_push_events, class: 'form-check-input'
= form.label :tag_push_events, class: 'list-label form-check-label ml-1' do
diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml
index ddc089b0bd7..52c7bc47ca7 100644
--- a/app/views/sherlock/queries/_general.html.haml
+++ b/app/views/sherlock/queries/_general.html.haml
@@ -36,7 +36,7 @@
%li
.code.js-syntax-highlight.sherlock-code
:preserve
- #{highlight("#{@query.id}.sql", @query.formatted_query)}
+ #{highlight("#{@query.id}.sql", @query.formatted_query, language: 'sql')}
.card
.card-header
diff --git a/app/views/sherlock/transactions/_queries.html.haml b/app/views/sherlock/transactions/_queries.html.haml
index c1ec4b91bb6..5e224f3aa0e 100644
--- a/app/views/sherlock/transactions/_queries.html.haml
+++ b/app/views/sherlock/transactions/_queries.html.haml
@@ -17,7 +17,7 @@
= t('sherlock.milliseconds')
%td
.code.js-syntax-highlight.sherlock-code
- = highlight("#{query.id}.sql", query.formatted_query)
+ = highlight("#{query.id}.sql", query.formatted_query, language: 'sql')
%td
= link_to(t('sherlock.view'),
sherlock_transaction_query_path(@transaction, query),
diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml
index ae69d0d07c7..0ce13ee7a53 100644
--- a/app/views/snippets/_actions.html.haml
+++ b/app/views/snippets/_actions.html.haml
@@ -7,7 +7,7 @@
- if can?(current_user, :admin_personal_snippet, @snippet)
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do
Delete
- = link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: "New snippet" do
+ = link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-success", title: "New snippet" do
New snippet
- if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml
index f01915107e3..6bc748d346e 100644
--- a/app/views/snippets/new.html.haml
+++ b/app/views/snippets/new.html.haml
@@ -1,7 +1,9 @@
- @hide_top_links = true
-- breadcrumb_title "Snippets"
+- @hide_breadcrumbs = true
- page_title "New Snippet"
-%h3.page-title
- New Snippet
-%hr
-= render "shared/snippets/form", url: snippets_path(@snippet)
+
+.page-title-holder
+ %h1.page-title= _('New Snippet')
+
+.prepend-top-default
+ = render "shared/snippets/form", url: snippets_path(@snippet)
diff --git a/app/views/snippets/notes/_actions.html.haml b/app/views/snippets/notes/_actions.html.haml
index 3a50324770d..220ba2b49e6 100644
--- a/app/views/snippets/notes/_actions.html.haml
+++ b/app/views/snippets/notes/_actions.html.haml
@@ -1,8 +1,7 @@
- if current_user
- if note.emoji_awardable?
- - user_authored = note.user_authored?(current_user)
.note-actions-item
- = link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored} has-tooltip", data: { position: 'right' } do
+ = link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip", data: { position: 'right' } do
= icon('spinner spin')
%span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face')
%span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley')
diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml
index 1c788b9a737..979f6862de3 100644
--- a/app/views/u2f/_authenticate.html.haml
+++ b/app/views/u2f/_authenticate.html.haml
@@ -1,18 +1,18 @@
#js-authenticate-u2f
-%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' } Sign in via 2FA code
+%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' }= _("Sign in via 2FA code")
-# haml-lint:disable InlineJavaScript
%script#js-authenticate-u2f-in-progress{ type: "text/template" }
- %p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
+ %p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.")
%script#js-authenticate-u2f-error{ type: "text/template" }
%div
- %p <%= error_message %> (error code: <%= error_code %>)
- %a.btn.btn-block.btn-warning#js-u2f-try-again Try again?
+ %p <%= error_message %> (#{_("error code:")} <%= error_code %>)
+ %a.btn.btn-block.btn-warning#js-u2f-try-again= _("Try again?")
%script#js-authenticate-u2f-authenticated{ type: "text/template" }
%div
- %p We heard back from your U2F device. You have been authenticated.
+ %p= _("We heard back from your U2F device. You have been authenticated.")
= form_tag(new_user_session_path, method: :post, id: 'js-login-u2f-form') do |f|
- resource_params = params[resource_name].presence || params
= hidden_field_tag 'user[remember_me]', resource_params.fetch(:remember_me, 0)
diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml
index cc0e93c0755..f6724f72307 100644
--- a/app/views/u2f/_register.html.haml
+++ b/app/views/u2f/_register.html.haml
@@ -2,39 +2,39 @@
-# haml-lint:disable InlineJavaScript
%script#js-register-u2f-not-supported{ type: "text/template" }
- %p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).
+ %p= _("Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).")
%script#js-register-u2f-setup{ type: "text/template" }
- if current_user.two_factor_otp_enabled?
.row.append-bottom-10
.col-md-4
- %button#js-setup-u2f-device.btn.btn-info.btn-block Setup new U2F device
+ %button#js-setup-u2f-device.btn.btn-info.btn-block= _("Set up new U2F device")
.col-md-8
- %p Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left.
+ %p= _("Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left.")
- else
.row.append-bottom-10
.col-md-4
- %button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true } Setup new U2F device
+ %button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true }= _("Set up new U2F device")
.col-md-8
- %p.text-warning You need to register a two-factor authentication app before you can set up a U2F device.
+ %p.text-warning= _("You need to register a two-factor authentication app before you can set up a U2F device.")
%script#js-register-u2f-in-progress{ type: "text/template" }
- %p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
+ %p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.")
%script#js-register-u2f-error{ type: "text/template" }
%div
%p
- %span <%= error_message %> (error code: <%= error_code %>)
- %a.btn.btn-warning#js-u2f-try-again Try again?
+ %span <%= error_message %> (#{_("error code:")} <%= error_code %>)
+ %a.btn.btn-warning#js-u2f-try-again= _("Try again?")
%script#js-register-u2f-registered{ type: "text/template" }
.row.append-bottom-10
.col-md-12
- %p Your device was successfully set up! Give it a name and register it with the GitLab server.
+ %p= _("Your device was successfully set up! Give it a name and register it with the GitLab server.")
= form_tag(create_u2f_profile_two_factor_auth_path, method: :post) do
.row.append-bottom-10
.col-md-3
- = text_field_tag 'u2f_registration[name]', nil, class: 'form-control', placeholder: "Pick a name"
+ = text_field_tag 'u2f_registration[name]', nil, class: 'form-control', placeholder: _("Pick a name")
.col-md-3
= hidden_field_tag 'u2f_registration[device_response]', nil, class: 'form-control', required: true, id: "js-device-response"
- = submit_tag "Register U2F device", class: "btn btn-success"
+ = submit_tag _("Register U2F device"), class: "btn btn-success"
diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml
new file mode 100644
index 00000000000..b5bc1180290
--- /dev/null
+++ b/app/views/users/_overview.html.haml
@@ -0,0 +1,30 @@
+.row
+ .col-12
+ .calendar-block.prepend-top-default.append-bottom-default
+ .user-calendar.d-none.d-sm-block{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
+ %h4.center.light
+ = spinner nil, true
+ .user-calendar-activities.d-none.d-sm-block
+.row
+ .col-md-12.col-lg-6
+ - if can?(current_user, :read_cross_project)
+ .activities-block
+ .prepend-top-16
+ .d-flex.align-items-center.border-bottom
+ %h4.flex-grow
+ = s_('UserProfile|Activity')
+ = link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all"
+ .overview-content-list{ data: { href: user_path } }
+ .center.light.loading
+ = spinner nil, true
+
+ .col-md-12.col-lg-6
+ .projects-block
+ .prepend-top-16
+ .d-flex.align-items-center.border-bottom
+ %h4.flex-grow
+ = s_('UserProfile|Personal projects')
+ = link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all"
+ .overview-content-list{ data: { href: user_projects_path } }
+ .center.light.loading
+ = spinner nil, true
diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml
index 6b1d75c6e72..938cb579e9f 100644
--- a/app/views/users/calendar_activities.html.haml
+++ b/app/views/users/calendar_activities.html.haml
@@ -1,6 +1,5 @@
%h4.prepend-top-20
- Contributions for
- %strong= @calendar_date.to_s(:medium)
+ = _("Contributions for <strong>%{calendar_date}</strong>").html_safe % { calendar_date: @calendar_date.to_s(:medium) }
- if @events.any?
%ul.bordered-list
@@ -9,25 +8,28 @@
%span.light
%i.fa.fa-clock-o
= event.created_at.strftime('%-I:%M%P')
- - if event.push?
- #{event.action_name} #{event.ref_type}
+ - if event.visible_to_user?(current_user)
+ - if event.push?
+ #{event.action_name} #{event.ref_type}
+ %strong
+ - commits_path = project_commits_path(event.project, event.ref_name)
+ = link_to_if event.project.repository.branch_exists?(event.ref_name), event.ref_name, commits_path
+ - else
+ = event_action_name(event)
+ %strong
+ - if event.note?
+ = link_to event.note_target.to_reference, event_note_target_url(event), class: 'has-tooltip', title: event.target_title
+ - elsif event.target
+ = link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
+
+ at
%strong
- - commits_path = project_commits_path(event.project, event.ref_name)
- = link_to_if event.project.repository.branch_exists?(event.ref_name), event.ref_name, commits_path
+ - if event.project
+ = link_to_project(event.project)
+ - else
+ = event.project_name
- else
- = event_action_name(event)
- %strong
- - if event.note?
- = link_to event.note_target.to_reference, event_note_target_path(event), class: 'has-tooltip', title: event.target_title
- - elsif event.target
- = link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
-
- at
- %strong
- - if event.project
- = link_to_project event.project
- - else
- = event.project_name
+ made a private contribution
- else
%p
- No contributions found for #{@calendar_date.to_s(:medium)}
+ = _('No contributions were found')
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 7a38d290915..d11476738e4 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -12,22 +12,22 @@
.cover-block.user-cover-block.top-area
.cover-controls
- if @user == current_user
- = link_to profile_path, class: 'btn btn-default has-tooltip', title: 'Edit profile', 'aria-label': 'Edit profile' do
+ = link_to profile_path, class: 'btn btn-default has-tooltip', title: s_('UserProfile|Edit profile'), 'aria-label': 'Edit profile' do
= icon('pencil')
- elsif current_user
- if @user.abuse_report
- %button.btn.btn-danger{ title: 'Already reported for abuse',
+ %button.btn.btn-danger{ title: s_('UserProfile|Already reported for abuse'),
data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } }
= icon('exclamation-circle')
- else
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn',
- title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
+ title: s_('UserProfile|Report abuse'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle')
- if can?(current_user, :read_user_profile, @user)
- = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
+ = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: s_('UserProfile|Subscribe'), 'aria-label': 'Subscribe' do
= icon('rss')
- if current_user && current_user.admin?
- = link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area',
+ = link_to [:admin, @user], class: 'btn btn-default', title: s_('UserProfile|View user in admin area'),
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('users')
@@ -51,7 +51,7 @@
@#{@user.username}
- if can?(current_user, :read_user_profile, @user)
%span.middle-dot-divider
- Member since #{@user.created_at.to_date.to_s(:long)}
+ = s_('Member since %{date}') % { date: @user.created_at.to_date.to_s(:long) }
.cover-desc
- unless @user.public_email.blank?
@@ -91,40 +91,42 @@
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
+ - if profile_tab?(:overview)
+ %li.js-overview-tab
+ = link_to user_path, data: { target: 'div#js-overview', action: 'overview', toggle: 'tab' } do
+ = s_('UserProfile|Overview')
- if profile_tab?(:activity)
%li.js-activity-tab
- = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
- Activity
+ = link_to user_activity_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
+ = s_('UserProfile|Activity')
- if profile_tab?(:groups)
%li.js-groups-tab
= link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
- Groups
+ = s_('UserProfile|Groups')
- if profile_tab?(:contributed)
%li.js-contributed-tab
= link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
- Contributed projects
+ = s_('UserProfile|Contributed projects')
- if profile_tab?(:projects)
%li.js-projects-tab
= link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
- Personal projects
+ = s_('UserProfile|Personal projects')
- if profile_tab?(:snippets)
%li.js-snippets-tab
= link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
- Snippets
+ = s_('UserProfile|Snippets')
%div{ class: container_class }
.tab-content
+ - if profile_tab?(:overview)
+ #js-overview.tab-pane
+ = render "users/overview"
+
- if profile_tab?(:activity)
#activity.tab-pane
- .row-content-block.calender-block.white.second-block.d-none.d-sm-block
- .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
- %h4.center.light
- %i.fa.fa-spinner.fa-spin
- .user-calendar-activities
-
- if can?(current_user, :read_cross_project)
%h4.prepend-top-20
- Most Recent Activity
+ = s_('UserProfile|Most Recent Activity')
.content_list{ data: { href: user_path } }
= spinner
@@ -155,4 +157,4 @@
.col-12.text-center
.text-content
%h4
- This user has a private profile
+ = s_('UserProfile|This user has a private profile')
diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb
index 06324575ffc..f69e74b2674 100644
--- a/app/workers/admin_email_worker.rb
+++ b/app/workers/admin_email_worker.rb
@@ -10,10 +10,12 @@ class AdminEmailWorker
private
+ # rubocop: disable CodeReuse/ActiveRecord
def send_repository_check_mail
repository_check_failed_count = Project.where(last_repository_check_failed: true).count
return if repository_check_failed_count.zero?
RepositoryCheckMailer.notify(repository_check_failed_count).deliver_now
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index f95df7ecf03..953ab95735b 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -1,4 +1,6 @@
---
+- auto_devops:auto_devops_disable
+
- cronjob:admin_email
- cronjob:expire_build_artifacts
- cronjob:gitlab_usage_ping
@@ -26,6 +28,7 @@
- gcp_cluster:cluster_wait_for_app_installation
- gcp_cluster:wait_for_cluster_creation
- gcp_cluster:cluster_wait_for_ingress_ip_address
+- gcp_cluster:cluster_platform_configure
- github_import_advance_stage
- github_importer:github_import_import_diff_note
@@ -68,6 +71,9 @@
- pipeline_processing:pipeline_update
- pipeline_processing:stage_update
- pipeline_processing:update_head_pipeline_for_merge_request
+- pipeline_processing:ci_build_schedule
+
+- deployment:deployments_success
- repository_check:repository_check_clear
- repository_check:repository_check_batch
@@ -85,6 +91,7 @@
- authorized_projects
- background_migration
- create_gpg_signature
+- delete_container_repository
- delete_merged_branches
- delete_user
- email_receiver
diff --git a/app/workers/archive_trace_worker.rb b/app/workers/archive_trace_worker.rb
index c6f89a17729..c1283e9b2fc 100644
--- a/app/workers/archive_trace_worker.rb
+++ b/app/workers/archive_trace_worker.rb
@@ -4,9 +4,11 @@ class ArchiveTraceWorker
include ApplicationWorker
include PipelineBackgroundQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(job_id)
Ci::Build.without_archived_trace.find_by(id: job_id).try do |job|
job.trace.archive!
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb
index dd62bb0f33d..c9ddeb08613 100644
--- a/app/workers/authorized_projects_worker.rb
+++ b/app/workers/authorized_projects_worker.rb
@@ -12,9 +12,11 @@ class AuthorizedProjectsWorker
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(user_id)
user = User.find_by(id: user_id)
user&.refresh_authorized_projects
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/auto_devops/disable_worker.rb b/app/workers/auto_devops/disable_worker.rb
new file mode 100644
index 00000000000..73ddc591505
--- /dev/null
+++ b/app/workers/auto_devops/disable_worker.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module AutoDevops
+ class DisableWorker
+ include ApplicationWorker
+ include AutoDevopsQueue
+
+ def perform(pipeline_id)
+ pipeline = Ci::Pipeline.find(pipeline_id)
+ project = pipeline.project
+
+ send_notification_email(pipeline, project) if disable_service(project).execute
+ end
+
+ private
+
+ def disable_service(project)
+ Projects::AutoDevops::DisableService.new(project)
+ end
+
+ def send_notification_email(pipeline, project)
+ recipients = email_receivers_for(pipeline, project)
+
+ return unless recipients.any?
+
+ NotificationService.new.autodevops_disabled(pipeline, recipients)
+ end
+
+ def email_receivers_for(pipeline, project)
+ recipients = [pipeline.user&.email]
+ recipients << project.owner.email unless project.group
+ recipients.uniq.compact
+ end
+ end
+end
diff --git a/app/workers/background_migration_worker.rb b/app/workers/background_migration_worker.rb
index 7d006cc348e..688b600649a 100644
--- a/app/workers/background_migration_worker.rb
+++ b/app/workers/background_migration_worker.rb
@@ -10,17 +10,7 @@ class BackgroundMigrationWorker
# maintenance related tasks have plenty of time to clean up after a migration
# has been performed.
def self.minimum_interval
- if enable_health_check?
- 2.minutes.to_i
- else
- 5.minutes.to_i
- end
- end
-
- def self.enable_health_check?
- Rails.env.development? ||
- Rails.env.test? ||
- Feature.enabled?('background_migration_health_check')
+ 2.minutes.to_i
end
# Performs the background migration.
@@ -86,8 +76,6 @@ class BackgroundMigrationWorker
# class_name - The name of the background migration that we might want to
# run.
def healthy_database?
- return true unless self.class.enable_health_check?
-
return true unless Gitlab::Database.postgresql?
!Postgresql::ReplicationSlot.lag_too_great?
diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb
index 53d77dc4524..912c53e11f8 100644
--- a/app/workers/build_coverage_worker.rb
+++ b/app/workers/build_coverage_worker.rb
@@ -4,7 +4,9 @@ class BuildCoverageWorker
include ApplicationWorker
include PipelineQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
Ci::Build.find_by(id: build_id)&.update_coverage
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index 9dc2c7f3601..61d866b1f02 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -6,15 +6,17 @@ class BuildFinishedWorker
queue_namespace :pipeline_processing
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
# We execute that in sync as this access the files in order to access local file, and reduce IO
BuildTraceSectionsWorker.new.perform(build.id)
BuildCoverageWorker.new.perform(build.id)
- # We execute that async as this are two indepentent operations that can be executed after TraceSections and Coverage
+ # We execute that async as this are two independent operations that can be executed after TraceSections and Coverage
BuildHooksWorker.perform_async(build.id)
ArchiveTraceWorker.perform_async(build.id)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb
index f1f71dc589c..b0c3676714c 100644
--- a/app/workers/build_hooks_worker.rb
+++ b/app/workers/build_hooks_worker.rb
@@ -6,8 +6,10 @@ class BuildHooksWorker
queue_namespace :pipeline_hooks
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
Ci::Build.find_by(id: build_id)
.try(:execute_hooks)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/build_queue_worker.rb b/app/workers/build_queue_worker.rb
index 1b3f1fd3c2a..67d5b0f5f5b 100644
--- a/app/workers/build_queue_worker.rb
+++ b/app/workers/build_queue_worker.rb
@@ -6,9 +6,11 @@ class BuildQueueWorker
queue_namespace :pipeline_processing
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
Ci::UpdateBuildQueueService.new.execute(build)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb
index e1c1cc24a94..9a865fea621 100644
--- a/app/workers/build_success_worker.rb
+++ b/app/workers/build_success_worker.rb
@@ -6,15 +6,31 @@ class BuildSuccessWorker
queue_namespace :pipeline_processing
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
create_deployment(build) if build.has_environment?
+ stop_environment(build) if build.stops_environment?
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ ##
+ # Deprecated:
+ # As of 11.5, we started creating a deployment record when ci_builds record is created.
+ # Therefore we no longer need to create a deployment, after a build succeeded.
+ # We're leaving this code for the transition period, but we can remove this code in 11.6.
def create_deployment(build)
- CreateDeploymentService.new(build).execute
+ build.create_deployment.try do |deployment|
+ deployment.succeed
+ end
+ end
+
+ ##
+ # TODO: This should be processed in DeploymentSuccessWorker once we started storing `action` value in `deployments` records
+ def stop_environment(build)
+ build.persisted_environment.fire_state_event(:stop)
end
end
diff --git a/app/workers/build_trace_sections_worker.rb b/app/workers/build_trace_sections_worker.rb
index f4114b3353c..0641130fd64 100644
--- a/app/workers/build_trace_sections_worker.rb
+++ b/app/workers/build_trace_sections_worker.rb
@@ -4,7 +4,9 @@ class BuildTraceSectionsWorker
include ApplicationWorker
include PipelineQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
Ci::Build.find_by(id: build_id)&.parse_trace_sections!
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/ci/archive_traces_cron_worker.rb b/app/workers/ci/archive_traces_cron_worker.rb
index 7d4e9660a4e..7443aad1380 100644
--- a/app/workers/ci/archive_traces_cron_worker.rb
+++ b/app/workers/ci/archive_traces_cron_worker.rb
@@ -5,6 +5,7 @@ module Ci
include ApplicationWorker
include CronjobQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform
# Archive stale live traces which still resides in redis or database
# This could happen when ArchiveTraceWorker sidekiq jobs were lost by receiving SIGKILL
@@ -19,6 +20,7 @@ module Ci
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/workers/ci/build_schedule_worker.rb b/app/workers/ci/build_schedule_worker.rb
new file mode 100644
index 00000000000..da219adffc6
--- /dev/null
+++ b/app/workers/ci/build_schedule_worker.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Ci
+ class BuildScheduleWorker
+ include ApplicationWorker
+ include PipelineQueue
+
+ queue_namespace :pipeline_processing
+
+ def perform(build_id)
+ ::Ci::Build.find_by_id(build_id).try do |build|
+ break unless build.scheduled?
+
+ Ci::RunScheduledBuildService
+ .new(build.project, build.user).execute(build)
+ end
+ end
+ end
+end
diff --git a/app/workers/ci/build_trace_chunk_flush_worker.rb b/app/workers/ci/build_trace_chunk_flush_worker.rb
index 9dbf2e5e1ac..23a11c28f9b 100644
--- a/app/workers/ci/build_trace_chunk_flush_worker.rb
+++ b/app/workers/ci/build_trace_chunk_flush_worker.rb
@@ -5,10 +5,12 @@ module Ci
include ApplicationWorker
include PipelineBackgroundQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(build_trace_chunk_id)
::Ci::BuildTraceChunk.find_by(id: build_trace_chunk_id).try do |build_trace_chunk|
build_trace_chunk.persist_data!
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/workers/cluster_platform_configure_worker.rb b/app/workers/cluster_platform_configure_worker.rb
new file mode 100644
index 00000000000..8f3689f0166
--- /dev/null
+++ b/app/workers/cluster_platform_configure_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class ClusterPlatformConfigureWorker
+ include ApplicationWorker
+ include ClusterQueue
+
+ def perform(cluster_id)
+ Clusters::Cluster.find_by_id(cluster_id).try do |cluster|
+ next unless cluster.cluster_project
+
+ kubernetes_namespace = cluster.find_or_initialize_kubernetes_namespace(cluster.cluster_project)
+
+ Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
+ cluster: cluster,
+ kubernetes_namespace: kubernetes_namespace
+ ).execute
+ end
+
+ rescue ::Kubeclient::HttpError => err
+ Rails.logger.error "Failed to create/update Kubernetes namespace for cluster_id: #{cluster_id} with error: #{err.message}"
+ end
+end
diff --git a/app/workers/cluster_provision_worker.rb b/app/workers/cluster_provision_worker.rb
index 59de7903c1c..3d5894b73ec 100644
--- a/app/workers/cluster_provision_worker.rb
+++ b/app/workers/cluster_provision_worker.rb
@@ -9,6 +9,8 @@ class ClusterProvisionWorker
cluster.provider.try do |provider|
Clusters::Gcp::ProvisionService.new.execute(provider) if cluster.gcp?
end
+
+ ClusterPlatformConfigureWorker.perform_async(cluster.id) if cluster.user?
end
end
end
diff --git a/app/workers/concerns/application_worker.rb b/app/workers/concerns/application_worker.rb
index bb06e31641d..d64c2f82a09 100644
--- a/app/workers/concerns/application_worker.rb
+++ b/app/workers/concerns/application_worker.rb
@@ -11,7 +11,7 @@ module ApplicationWorker
set_queue
end
- module ClassMethods
+ class_methods do
def inherited(subclass)
subclass.set_queue
end
diff --git a/app/workers/concerns/auto_devops_queue.rb b/app/workers/concerns/auto_devops_queue.rb
new file mode 100644
index 00000000000..aba928ccaab
--- /dev/null
+++ b/app/workers/concerns/auto_devops_queue.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+#
+module AutoDevopsQueue
+ extend ActiveSupport::Concern
+
+ included do
+ queue_namespace :auto_devops
+ end
+end
diff --git a/app/workers/concerns/gitlab/github_import/rescheduling_methods.rb b/app/workers/concerns/gitlab/github_import/rescheduling_methods.rb
index 692ca6b7f42..1c6413674a0 100644
--- a/app/workers/concerns/gitlab/github_import/rescheduling_methods.rb
+++ b/app/workers/concerns/gitlab/github_import/rescheduling_methods.rb
@@ -8,6 +8,7 @@ module Gitlab
# project_id - The ID of the GitLab project to import the note into.
# hash - A Hash containing the details of the GitHub object to imoprt.
# notify_key - The Redis key to notify upon completion, if any.
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(project_id, hash, notify_key = nil)
project = Project.find_by(id: project_id)
@@ -24,6 +25,7 @@ module Gitlab
.perform_in(client.rate_limit_resets_in, project.id, hash, notify_key)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def try_import(*args)
import(*args)
diff --git a/app/workers/concerns/gitlab/github_import/stage_methods.rb b/app/workers/concerns/gitlab/github_import/stage_methods.rb
index 147c8c8d683..59e6bc2c97d 100644
--- a/app/workers/concerns/gitlab/github_import/stage_methods.rb
+++ b/app/workers/concerns/gitlab/github_import/stage_methods.rb
@@ -20,11 +20,13 @@ module Gitlab
self.class.perform_in(client.rate_limit_resets_in, project.id)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_project(id)
# If the project has been marked as failed we want to bail out
# automatically.
Project.import_started.find_by(id: id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/workers/concerns/new_issuable.rb b/app/workers/concerns/new_issuable.rb
index 7735dec5e6b..a89451a4475 100644
--- a/app/workers/concerns/new_issuable.rb
+++ b/app/workers/concerns/new_issuable.rb
@@ -10,17 +10,21 @@ module NewIssuable
user && issuable
end
+ # rubocop: disable CodeReuse/ActiveRecord
def set_user(user_id)
@user = User.find_by(id: user_id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
log_error(User, user_id) unless @user # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def set_issuable(issuable_id)
@issuable = issuable_class.find_by(id: issuable_id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
log_error(issuable_class, issuable_id) unless @issuable # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
+ # rubocop: enable CodeReuse/ActiveRecord
def log_error(record_class, record_id)
Rails.logger.error("#{self.class}: couldn't find #{record_class} with ID=#{record_id}, skipping job")
diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb
index d85bc7d1660..27b94a82444 100644
--- a/app/workers/concerns/waitable_worker.rb
+++ b/app/workers/concerns/waitable_worker.rb
@@ -3,7 +3,7 @@
module WaitableWorker
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
# Schedules multiple jobs and waits for them to be completed.
def bulk_perform_and_wait(args_list, timeout: 10)
# Short-circuit: it's more efficient to do small numbers of jobs inline
diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_gpg_signature_worker.rb
index a1aeeb7c4fc..49c7a403838 100644
--- a/app/workers/create_gpg_signature_worker.rb
+++ b/app/workers/create_gpg_signature_worker.rb
@@ -3,6 +3,7 @@
class CreateGpgSignatureWorker
include ApplicationWorker
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(commit_shas, project_id)
# Older versions of GitPushService may push a single commit ID on the stack.
# We need this to be backwards compatible.
@@ -26,4 +27,5 @@ class CreateGpgSignatureWorker
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/delete_container_repository_worker.rb b/app/workers/delete_container_repository_worker.rb
new file mode 100644
index 00000000000..e8fe9d82797
--- /dev/null
+++ b/app/workers/delete_container_repository_worker.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+class DeleteContainerRepositoryWorker
+ include ApplicationWorker
+ include ExclusiveLeaseGuard
+
+ LEASE_TIMEOUT = 1.hour
+
+ attr_reader :container_repository
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(current_user_id, container_repository_id)
+ current_user = User.find_by(id: current_user_id)
+ @container_repository = ContainerRepository.find_by(id: container_repository_id)
+ project = container_repository&.project
+
+ return unless current_user && container_repository && project
+
+ # If a user accidentally attempts to delete the same container registry in quick succession,
+ # this can lead to orphaned tags.
+ try_obtain_lease do
+ Projects::ContainerRepository::DestroyService.new(project, current_user).execute(container_repository)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # For ExclusiveLeaseGuard concern
+ def lease_key
+ @lease_key ||= "container_repository:delete:#{container_repository.id}"
+ end
+
+ # For ExclusiveLeaseGuard concern
+ def lease_timeout
+ LEASE_TIMEOUT
+ end
+end
diff --git a/app/workers/delete_diff_files_worker.rb b/app/workers/delete_diff_files_worker.rb
index 0874a0b75e8..f518dfe871c 100644
--- a/app/workers/delete_diff_files_worker.rb
+++ b/app/workers/delete_diff_files_worker.rb
@@ -3,6 +3,7 @@
class DeleteDiffFilesWorker
include ApplicationWorker
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(merge_request_diff_id)
merge_request_diff = MergeRequestDiff.find(merge_request_diff_id)
@@ -16,4 +17,5 @@ class DeleteDiffFilesWorker
.delete_all
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/deployments/success_worker.rb b/app/workers/deployments/success_worker.rb
new file mode 100644
index 00000000000..da517f3fb26
--- /dev/null
+++ b/app/workers/deployments/success_worker.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Deployments
+ class SuccessWorker
+ include ApplicationWorker
+
+ queue_namespace :deployment
+
+ def perform(deployment_id)
+ Deployment.find_by_id(deployment_id).try do |deployment|
+ break unless deployment.success?
+
+ UpdateDeploymentService.new(deployment).execute
+ end
+ end
+ end
+end
diff --git a/app/workers/detect_repository_languages_worker.rb b/app/workers/detect_repository_languages_worker.rb
index cfbbbffc8a6..64bc9776d48 100644
--- a/app/workers/detect_repository_languages_worker.rb
+++ b/app/workers/detect_repository_languages_worker.rb
@@ -11,17 +11,17 @@ class DetectRepositoryLanguagesWorker
attr_reader :project
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(project_id, user_id)
@project = Project.find_by(id: project_id)
user = User.find_by(id: user_id)
return unless project && user
- return if Feature.disabled?(:repository_languages, project.namespace)
-
try_obtain_lease do
::Projects::DetectRepositoryLanguagesService.new(project, user).execute
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb
index 12706613ac2..bf637f82df2 100644
--- a/app/workers/email_receiver_worker.rb
+++ b/app/workers/email_receiver_worker.rb
@@ -40,6 +40,8 @@ class EmailReceiverWorker
"You are not allowed to perform this action. If you believe this is in error, contact a staff member."
when Gitlab::Email::NoteableNotFoundError
"The thread you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member."
+ when Gitlab::Email::InvalidAttachment
+ error.message
when Gitlab::Email::InvalidRecordError
can_retry = true
error.message
diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb
index 5d3a9a39b93..dce812d1ae2 100644
--- a/app/workers/expire_build_artifacts_worker.rb
+++ b/app/workers/expire_build_artifacts_worker.rb
@@ -4,6 +4,7 @@ class ExpireBuildArtifactsWorker
include ApplicationWorker
include CronjobQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform
Rails.logger.info 'Scheduling removal of build artifacts'
@@ -12,4 +13,5 @@ class ExpireBuildArtifactsWorker
ExpireBuildInstanceArtifactsWorker.bulk_perform_async(build_ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/expire_build_instance_artifacts_worker.rb b/app/workers/expire_build_instance_artifacts_worker.rb
index 3b57ecb36e3..94426dcf921 100644
--- a/app/workers/expire_build_instance_artifacts_worker.rb
+++ b/app/workers/expire_build_instance_artifacts_worker.rb
@@ -3,6 +3,7 @@
class ExpireBuildInstanceArtifactsWorker
include ApplicationWorker
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
build = Ci::Build
.with_expired_artifacts
@@ -12,6 +13,7 @@ class ExpireBuildInstanceArtifactsWorker
return unless build&.project && !build.project.pending_delete
Rails.logger.info "Removing artifacts for build #{build.id}..."
- build.erase_artifacts!
+ build.erase_erasable_artifacts!
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/expire_job_cache_worker.rb b/app/workers/expire_job_cache_worker.rb
index 14a57b90114..b09d0a5d121 100644
--- a/app/workers/expire_job_cache_worker.rb
+++ b/app/workers/expire_job_cache_worker.rb
@@ -6,6 +6,7 @@ class ExpireJobCacheWorker
queue_namespace :pipeline_cache
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(job_id)
job = CommitStatus.joins(:pipeline, :project).find_by(id: job_id)
return unless job
@@ -18,6 +19,7 @@ class ExpireJobCacheWorker
store.touch(project_job_path(project, job))
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb
index 992fc63c451..c96e8a0379b 100644
--- a/app/workers/expire_pipeline_cache_worker.rb
+++ b/app/workers/expire_pipeline_cache_worker.rb
@@ -6,6 +6,7 @@ class ExpirePipelineCacheWorker
queue_namespace :pipeline_cache
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
pipeline = Ci::Pipeline.find_by(id: pipeline_id)
return unless pipeline
@@ -23,6 +24,7 @@ class ExpirePipelineCacheWorker
Gitlab::Cache::Ci::ProjectPipelineStatus.update_for_pipeline(pipeline)
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/workers/gitlab/github_import/advance_stage_worker.rb b/app/workers/gitlab/github_import/advance_stage_worker.rb
index be0b6c180b0..2b49860025a 100644
--- a/app/workers/gitlab/github_import/advance_stage_worker.rb
+++ b/app/workers/gitlab/github_import/advance_stage_worker.rb
@@ -14,7 +14,7 @@ module Gitlab
INTERVAL = 30.seconds.to_i
# The number of seconds to wait (while blocking the thread) before
- # continueing to the next waiter.
+ # continuing to the next waiter.
BLOCKING_WAIT_TIME = 5
# The known importer stages and their corresponding Sidekiq workers.
@@ -63,12 +63,14 @@ module Gitlab
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_project(id)
# TODO: Only select the JID
# This is due to the fact that the JID could be present in either the project record or
# its associated import_state record
Project.import_started.find_by(id: id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/workers/gitlab/github_import/refresh_import_jid_worker.rb b/app/workers/gitlab/github_import/refresh_import_jid_worker.rb
index 68d2c5c4331..65473026b4c 100644
--- a/app/workers/gitlab/github_import/refresh_import_jid_worker.rb
+++ b/app/workers/gitlab/github_import/refresh_import_jid_worker.rb
@@ -30,12 +30,14 @@ module Gitlab
# stage, if it died there's nothing we can do anyway.
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_project(id)
# TODO: Only select the JID
# This is due to the fact that the JID could be present in either the project record or
# its associated import_state record
Project.import_started.find_by(id: id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/workers/invalid_gpg_signature_update_worker.rb b/app/workers/invalid_gpg_signature_update_worker.rb
index 4724ab7ad98..fc8a731b427 100644
--- a/app/workers/invalid_gpg_signature_update_worker.rb
+++ b/app/workers/invalid_gpg_signature_update_worker.rb
@@ -3,6 +3,7 @@
class InvalidGpgSignatureUpdateWorker
include ApplicationWorker
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(gpg_key_id)
gpg_key = GpgKey.find_by(id: gpg_key_id)
@@ -10,4 +11,5 @@ class InvalidGpgSignatureUpdateWorker
Gitlab::Gpg::InvalidGpgSignatureUpdater.new(gpg_key).run
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/issue_due_scheduler_worker.rb b/app/workers/issue_due_scheduler_worker.rb
index c04a2d75e0b..476cba47ad7 100644
--- a/app/workers/issue_due_scheduler_worker.rb
+++ b/app/workers/issue_due_scheduler_worker.rb
@@ -4,9 +4,11 @@ class IssueDueSchedulerWorker
include ApplicationWorker
include CronjobQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform
project_ids = Issue.opened.due_tomorrow.group(:project_id).pluck(:project_id).map { |id| [id] }
MailScheduler::IssueDueWorker.bulk_perform_async(project_ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/mail_scheduler/issue_due_worker.rb b/app/workers/mail_scheduler/issue_due_worker.rb
index 8794ad7a82c..1e1dde1e829 100644
--- a/app/workers/mail_scheduler/issue_due_worker.rb
+++ b/app/workers/mail_scheduler/issue_due_worker.rb
@@ -5,10 +5,12 @@ module MailScheduler
include ApplicationWorker
include MailSchedulerQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(project_id)
Issue.opened.due_tomorrow.in_projects(project_id).preload(:project).find_each do |issue|
notification_service.issue_due(issue)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/workers/namespaceless_project_destroy_worker.rb b/app/workers/namespaceless_project_destroy_worker.rb
index d9df42c9e17..2965f3b1150 100644
--- a/app/workers/namespaceless_project_destroy_worker.rb
+++ b/app/workers/namespaceless_project_destroy_worker.rb
@@ -32,7 +32,5 @@ class NamespacelessProjectDestroyWorker
merge_requests = project.forked_from_project.merge_requests.opened.from_project(project)
merge_requests.update_all(state: 'closed')
-
- project.forked_project_link.destroy
end
end
diff --git a/app/workers/new_merge_request_worker.rb b/app/workers/new_merge_request_worker.rb
index 5d8b8904502..fa48c1b29a8 100644
--- a/app/workers/new_merge_request_worker.rb
+++ b/app/workers/new_merge_request_worker.rb
@@ -9,6 +9,8 @@ class NewMergeRequestWorker
EventCreateService.new.open_mr(issuable, user)
NotificationService.new.new_merge_request(issuable, user)
+
+ issuable.diffs(include_stats: false).write_cache
issuable.create_cross_references!(user)
end
diff --git a/app/workers/new_note_worker.rb b/app/workers/new_note_worker.rb
index 74f34dcf9aa..42f5b945a75 100644
--- a/app/workers/new_note_worker.rb
+++ b/app/workers/new_note_worker.rb
@@ -5,6 +5,7 @@ class NewNoteWorker
# Keep extra parameter to preserve backwards compatibility with
# old `NewNoteWorker` jobs (can remove later)
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(note_id, _params = {})
if note = Note.find_by(id: note_id)
NotificationService.new.new_note(note)
@@ -13,4 +14,5 @@ class NewNoteWorker
Rails.logger.error("NewNoteWorker: couldn't find note with ID=#{note_id}, skipping job")
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/object_storage/migrate_uploads_worker.rb b/app/workers/object_storage/migrate_uploads_worker.rb
index 01d03ec7888..fe5d27b087d 100644
--- a/app/workers/object_storage/migrate_uploads_worker.rb
+++ b/app/workers/object_storage/migrate_uploads_worker.rb
@@ -57,11 +57,13 @@ module ObjectStorage
include Report
+ # rubocop: disable CodeReuse/ActiveRecord
def self.enqueue!(uploads, model_class, mounted_as, to_store)
sanity_check!(uploads, model_class, mounted_as)
perform_async(uploads.ids, model_class.to_s, mounted_as, to_store)
end
+ # rubocop: enable CodeReuse/ActiveRecord
# We need to be sure all the uploads are for the same uploader and model type
# and that the mount point exists if provided.
@@ -78,6 +80,7 @@ module ObjectStorage
raise(SanityCheckError, "Mount point #{mounted_as} not found in #{model_class}.") unless model_has_mount
end
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(*args)
args_check!(args)
@@ -97,6 +100,7 @@ module ObjectStorage
# do not retry: the job is insane
Rails.logger.warn "#{self.class}: Sanity check error (#{e.message})"
end
+ # rubocop: enable CodeReuse/ActiveRecord
def sanity_check!(uploads)
self.class.sanity_check!(uploads, @model_class, @mounted_as)
diff --git a/app/workers/pages_domain_verification_worker.rb b/app/workers/pages_domain_verification_worker.rb
index 4610b688189..b3319ff5a13 100644
--- a/app/workers/pages_domain_verification_worker.rb
+++ b/app/workers/pages_domain_verification_worker.rb
@@ -3,6 +3,7 @@
class PagesDomainVerificationWorker
include ApplicationWorker
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(domain_id)
domain = PagesDomain.find_by(id: domain_id)
@@ -10,4 +11,5 @@ class PagesDomainVerificationWorker
VerifyPagesDomainService.new(domain).execute
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/pages_worker.rb b/app/workers/pages_worker.rb
index 13a6576a301..fa0dfa2ff4b 100644
--- a/app/workers/pages_worker.rb
+++ b/app/workers/pages_worker.rb
@@ -9,6 +9,7 @@ class PagesWorker
send(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
end
+ # rubocop: disable CodeReuse/ActiveRecord
def deploy(build_id)
build = Ci::Build.find_by(id: build_id)
result = Projects::UpdatePagesService.new(build.project, build).execute
@@ -18,6 +19,7 @@ class PagesWorker
result
end
+ # rubocop: enable CodeReuse/ActiveRecord
def remove(namespace_path, project_path)
full_path = File.join(Settings.pages.path, namespace_path, project_path)
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
index 58023e0af1b..eae1115e60c 100644
--- a/app/workers/pipeline_hooks_worker.rb
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -6,8 +6,10 @@ class PipelineHooksWorker
queue_namespace :pipeline_hooks
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
.try(:execute_hooks)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb
index a97019b100a..c2fbfd2b3a5 100644
--- a/app/workers/pipeline_metrics_worker.rb
+++ b/app/workers/pipeline_metrics_worker.rb
@@ -4,12 +4,14 @@ class PipelineMetricsWorker
include ApplicationWorker
include PipelineQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
update_metrics_for_active_pipeline(pipeline) if pipeline.active?
update_metrics_for_succeeded_pipeline(pipeline) if pipeline.success?
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -21,9 +23,11 @@ class PipelineMetricsWorker
metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: pipeline.finished_at, pipeline_id: pipeline.id)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def metrics(pipeline)
MergeRequest::Metrics.where(merge_request_id: merge_requests(pipeline))
end
+ # rubocop: enable CodeReuse/ActiveRecord
def merge_requests(pipeline)
pipeline.merge_requests.map(&:id)
diff --git a/app/workers/pipeline_notification_worker.rb b/app/workers/pipeline_notification_worker.rb
index 3a8846b3747..e4a18573d20 100644
--- a/app/workers/pipeline_notification_worker.rb
+++ b/app/workers/pipeline_notification_worker.rb
@@ -4,6 +4,7 @@ class PipelineNotificationWorker
include ApplicationWorker
include PipelineQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id, recipients = nil)
pipeline = Ci::Pipeline.find_by(id: pipeline_id)
@@ -11,4 +12,5 @@ class PipelineNotificationWorker
NotificationService.new.pipeline_finished(pipeline, recipients)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb
index 83744c5338a..f2aa17acb51 100644
--- a/app/workers/pipeline_process_worker.rb
+++ b/app/workers/pipeline_process_worker.rb
@@ -6,8 +6,10 @@ class PipelineProcessWorker
queue_namespace :pipeline_processing
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
.try(:process!)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/pipeline_schedule_worker.rb b/app/workers/pipeline_schedule_worker.rb
index a1815757735..85d1ffe0fa9 100644
--- a/app/workers/pipeline_schedule_worker.rb
+++ b/app/workers/pipeline_schedule_worker.rb
@@ -4,6 +4,7 @@ class PipelineScheduleWorker
include ApplicationWorker
include CronjobQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform
Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now)
.preload(:owner, :project).find_each do |schedule|
@@ -21,4 +22,5 @@ class PipelineScheduleWorker
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb
index 68e9af6a619..4f349ed922c 100644
--- a/app/workers/pipeline_success_worker.rb
+++ b/app/workers/pipeline_success_worker.rb
@@ -6,6 +6,7 @@ class PipelineSuccessWorker
queue_namespace :pipeline_processing
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
MergeRequests::MergeWhenPipelineSucceedsService
@@ -13,4 +14,5 @@ class PipelineSuccessWorker
.trigger(pipeline)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/pipeline_update_worker.rb b/app/workers/pipeline_update_worker.rb
index c33468c1f14..13a748e1551 100644
--- a/app/workers/pipeline_update_worker.rb
+++ b/app/workers/pipeline_update_worker.rb
@@ -6,8 +6,10 @@ class PipelineUpdateWorker
queue_namespace :pipeline_processing
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
.try(:update_status)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 09a594cdb4e..72a1733a2a8 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -29,15 +29,14 @@ class PostReceive
def process_project_changes(post_received)
changes = []
refs = Set.new
+ @user = post_received.identify
- post_received.changes_refs do |oldrev, newrev, ref|
- @user ||= post_received.identify(newrev)
-
- unless @user
- log("Triggered hook for non-existing user \"#{post_received.identifier}\"")
- return false # rubocop:disable Cop/AvoidReturnFromBlocks
- end
+ unless @user
+ log("Triggered hook for non-existing user \"#{post_received.identifier}\"")
+ return false
+ end
+ post_received.changes_refs do |oldrev, newrev, ref|
if Gitlab::Git.tag_ref?(ref)
GitTagPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute
elsif Gitlab::Git.branch_ref?(ref)
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index c9f6df9b56d..29a7f8e691a 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-# Worker for processing individiual commit messages pushed to a repository.
+# Worker for processing individual commit messages pushed to a repository.
#
# Jobs for this worker are scheduled for every commit that is being pushed. As a
# result of this the workload of this worker should be kept to a bare minimum.
@@ -14,6 +14,7 @@ class ProcessCommitWorker
# commit_hash - Hash containing commit details to use for constructing a
# Commit object without having to use the Git repository.
# default - The data was pushed to the default branch.
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(project_id, user_id, commit_hash, default = false)
project = Project.find_by(id: project_id)
@@ -30,6 +31,7 @@ class ProcessCommitWorker
process_commit_message(project, commit, user, author, default)
update_issue_metrics(commit, author)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def process_commit_message(project, commit, user, author, default = false)
# Ignore closing references from GitLab-generated commit messages.
@@ -50,6 +52,7 @@ class ProcessCommitWorker
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def update_issue_metrics(commit, author)
mentioned_issues = commit.all_references(author).issues
@@ -58,6 +61,7 @@ class ProcessCommitWorker
Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil)
.update_all(first_mentioned_in_commit_at: commit.committed_date)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def build_commit(project, hash)
date_suffix = '_date'
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index b0e1d8837d9..d27b5e62574 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -12,6 +12,7 @@ class ProjectCacheWorker
# CHANGELOG.
# statistics - An Array containing columns from ProjectStatistics to
# refresh, if empty all columns will be refreshed
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(project_id, files = [], statistics = [])
project = Project.find_by(id: project_id)
@@ -23,6 +24,7 @@ class ProjectCacheWorker
project.cleanup
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update_statistics(project, statistics = [])
return unless try_obtain_lease_for(project.id, :update_statistics)
diff --git a/app/workers/project_migrate_hashed_storage_worker.rb b/app/workers/project_migrate_hashed_storage_worker.rb
index ad0003e7bff..4c6339f7701 100644
--- a/app/workers/project_migrate_hashed_storage_worker.rb
+++ b/app/workers/project_migrate_hashed_storage_worker.rb
@@ -5,6 +5,7 @@ class ProjectMigrateHashedStorageWorker
LEASE_TIMEOUT = 30.seconds.to_i
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(project_id, old_disk_path = nil)
project = Project.find_by(id: project_id)
return if project.nil? || project.pending_delete?
@@ -19,6 +20,7 @@ class ProjectMigrateHashedStorageWorker
cancel_lease_for(project_id, uuid) if uuid
raise ex
end
+ # rubocop: enable CodeReuse/ActiveRecord
def lease_for(project_id)
Gitlab::ExclusiveLease.new(lease_key(project_id), timeout: LEASE_TIMEOUT)
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
index a0bc9288cf0..25567cec08b 100644
--- a/app/workers/project_service_worker.rb
+++ b/app/workers/project_service_worker.rb
@@ -7,6 +7,10 @@ class ProjectServiceWorker
def perform(hook_id, data)
data = data.with_indifferent_access
- Service.find(hook_id).execute(data)
+ service = Service.find(hook_id)
+ service.execute(data)
+ rescue => error
+ service_class = service&.class&.name || "Not Found"
+ logger.error class: self.class.name, service_class: service_class, message: error.message
end
end
diff --git a/app/workers/propagate_service_template_worker.rb b/app/workers/propagate_service_template_worker.rb
index c9da1cae255..3ccd7615697 100644
--- a/app/workers/propagate_service_template_worker.rb
+++ b/app/workers/propagate_service_template_worker.rb
@@ -6,11 +6,13 @@ class PropagateServiceTemplateWorker
LEASE_TIMEOUT = 4.hours.to_i
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(template_id)
return unless try_obtain_lease_for(template_id)
Projects::PropagateServiceTemplate.propagate(Service.find_by(id: template_id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb
index c1d05ebbcfd..dc4b7670131 100644
--- a/app/workers/prune_old_events_worker.rb
+++ b/app/workers/prune_old_events_worker.rb
@@ -4,17 +4,18 @@ class PruneOldEventsWorker
include ApplicationWorker
include CronjobQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform
- # Contribution calendar shows maximum 12 months of events.
- # Double nested query is used because MySQL doesn't allow DELETE subqueries
- # on the same table.
+ # Contribution calendar shows maximum 12 months of events, we retain 2 years for data integrity.
+ # Double nested query is used because MySQL doesn't allow DELETE subqueries on the same table.
Event.unscoped.where(
'(id IN (SELECT id FROM (?) ids_to_remove))',
Event.unscoped.where(
'created_at < ?',
- (12.months + 1.day).ago)
+ (2.years + 1.day).ago)
.select(:id)
.limit(10_000))
.delete_all
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/prune_web_hook_logs_worker.rb b/app/workers/prune_web_hook_logs_worker.rb
index 45c7d32f7eb..38054069f4e 100644
--- a/app/workers/prune_web_hook_logs_worker.rb
+++ b/app/workers/prune_web_hook_logs_worker.rb
@@ -9,6 +9,7 @@ class PruneWebHookLogsWorker
# The maximum number of rows to remove in a single job.
DELETE_LIMIT = 50_000
+ # rubocop: disable CodeReuse/ActiveRecord
def perform
# MySQL doesn't allow "DELETE FROM ... WHERE id IN ( ... )" if the inner
# query refers to the same table. To work around this we wrap the IN body in
@@ -23,4 +24,5 @@ class PruneWebHookLogsWorker
)
.delete_all
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/reactive_caching_worker.rb b/app/workers/reactive_caching_worker.rb
index 9b331f15dc5..96ff8cd6222 100644
--- a/app/workers/reactive_caching_worker.rb
+++ b/app/workers/reactive_caching_worker.rb
@@ -3,6 +3,7 @@
class ReactiveCachingWorker
include ApplicationWorker
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(class_name, id, *args)
klass = begin
Kernel.const_get(class_name)
@@ -13,4 +14,5 @@ class ReactiveCachingWorker
klass.find_by(id: id).try(:exclusively_update_reactive_cache!, *args)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/remove_expired_group_links_worker.rb b/app/workers/remove_expired_group_links_worker.rb
index 6b8b972a440..25128caf72f 100644
--- a/app/workers/remove_expired_group_links_worker.rb
+++ b/app/workers/remove_expired_group_links_worker.rb
@@ -5,6 +5,6 @@ class RemoveExpiredGroupLinksWorker
include CronjobQueue
def perform
- ProjectGroupLink.expired.destroy_all
+ ProjectGroupLink.expired.destroy_all # rubocop: disable DestroyAll
end
end
diff --git a/app/workers/remove_old_web_hook_logs_worker.rb b/app/workers/remove_old_web_hook_logs_worker.rb
index 17140ac4450..0f486f8991d 100644
--- a/app/workers/remove_old_web_hook_logs_worker.rb
+++ b/app/workers/remove_old_web_hook_logs_worker.rb
@@ -6,7 +6,9 @@ class RemoveOldWebHookLogsWorker
WEB_HOOK_LOG_LIFETIME = 2.days
+ # rubocop: disable DestroyAll
def perform
WebHookLog.destroy_all(['created_at < ?', Time.now - WEB_HOOK_LOG_LIFETIME])
end
+ # rubocop: enable DestroyAll
end
diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb
index 07559ea479b..c1bb1adc9cc 100644
--- a/app/workers/repository_check/batch_worker.rb
+++ b/app/workers/repository_check/batch_worker.rb
@@ -59,22 +59,28 @@ module RepositoryCheck
never_checked_project_ids(BATCH_SIZE) + old_checked_project_ids(BATCH_SIZE)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def never_checked_project_ids(batch_size)
projects_on_shard.where(last_repository_check_at: nil)
.where('created_at < ?', 24.hours.ago)
.limit(batch_size).pluck(:id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def old_checked_project_ids(batch_size)
projects_on_shard.where.not(last_repository_check_at: nil)
.where('last_repository_check_at < ?', 1.month.ago)
.reorder(last_repository_check_at: :asc)
.limit(batch_size).pluck(:id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def projects_on_shard
Project.where(repository_storage: shard_name)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def try_obtain_lease_for_project(id)
# Use a 24-hour timeout because on servers/projects where 'git fsck' is
diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb
index 81e1a4b63bb..01964c69fb2 100644
--- a/app/workers/repository_check/clear_worker.rb
+++ b/app/workers/repository_check/clear_worker.rb
@@ -5,6 +5,7 @@ module RepositoryCheck
include ApplicationWorker
include RepositoryCheckQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform
# Do small batched updates because these updates will be slow and locking
Project.select(:id).find_in_batches(batch_size: 100) do |batch|
@@ -14,5 +15,6 @@ module RepositoryCheck
)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb
index f44e5693b25..a8097af321f 100644
--- a/app/workers/repository_check/single_repository_worker.rb
+++ b/app/workers/repository_check/single_repository_worker.rb
@@ -48,9 +48,11 @@ module RepositoryCheck
false
end
+ # rubocop: disable CodeReuse/ActiveRecord
def has_changes?(project)
Project.with_push.exists?(project.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def has_wiki_changes?(project)
return false unless project.wiki_enabled?
diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb
index 1f6cb18c812..f72331c003a 100644
--- a/app/workers/run_pipeline_schedule_worker.rb
+++ b/app/workers/run_pipeline_schedule_worker.rb
@@ -6,6 +6,7 @@ class RunPipelineScheduleWorker
queue_namespace :pipeline_creation
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(schedule_id, user_id)
schedule = Ci::PipelineSchedule.find_by(id: schedule_id)
user = User.find_by(id: user_id)
@@ -14,6 +15,7 @@ class RunPipelineScheduleWorker
run_pipeline_schedule(schedule, user)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def run_pipeline_schedule(schedule, user)
Ci::CreatePipelineService.new(schedule.project,
diff --git a/app/workers/stage_update_worker.rb b/app/workers/stage_update_worker.rb
index ec8c8e3689f..ea587789d03 100644
--- a/app/workers/stage_update_worker.rb
+++ b/app/workers/stage_update_worker.rb
@@ -6,9 +6,11 @@ class StageUpdateWorker
queue_namespace :pipeline_processing
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(stage_id)
Ci::Stage.find_by(id: stage_id).try do |stage|
stage.update_status
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/stuck_ci_jobs_worker.rb b/app/workers/stuck_ci_jobs_worker.rb
index c78b7fac589..25809f68080 100644
--- a/app/workers/stuck_ci_jobs_worker.rb
+++ b/app/workers/stuck_ci_jobs_worker.rb
@@ -8,6 +8,7 @@ class StuckCiJobsWorker
BUILD_RUNNING_OUTDATED_TIMEOUT = 1.hour
BUILD_PENDING_OUTDATED_TIMEOUT = 1.day
+ BUILD_SCHEDULED_OUTDATED_TIMEOUT = 1.hour
BUILD_PENDING_STUCK_TIMEOUT = 1.hour
def perform
@@ -15,9 +16,10 @@ class StuckCiJobsWorker
Rails.logger.info "#{self.class}: Cleaning stuck builds"
- drop :running, BUILD_RUNNING_OUTDATED_TIMEOUT
- drop :pending, BUILD_PENDING_OUTDATED_TIMEOUT
- drop_stuck :pending, BUILD_PENDING_STUCK_TIMEOUT
+ drop :running, BUILD_RUNNING_OUTDATED_TIMEOUT, 'ci_builds.updated_at < ?', :stuck_or_timeout_failure
+ drop :pending, BUILD_PENDING_OUTDATED_TIMEOUT, 'ci_builds.updated_at < ?', :stuck_or_timeout_failure
+ drop :scheduled, BUILD_SCHEDULED_OUTDATED_TIMEOUT, 'scheduled_at IS NOT NULL AND scheduled_at < ?', :stale_schedule
+ drop_stuck :pending, BUILD_PENDING_STUCK_TIMEOUT, 'ci_builds.updated_at < ?', :stuck_or_timeout_failure
remove_lease
end
@@ -32,24 +34,25 @@ class StuckCiJobsWorker
Gitlab::ExclusiveLease.cancel(EXCLUSIVE_LEASE_KEY, @uuid)
end
- def drop(status, timeout)
- search(status, timeout) do |build|
- drop_build :outdated, build, status, timeout
+ def drop(status, timeout, condition, reason)
+ search(status, timeout, condition) do |build|
+ drop_build :outdated, build, status, timeout, reason
end
end
- def drop_stuck(status, timeout)
- search(status, timeout) do |build|
+ def drop_stuck(status, timeout, condition, reason)
+ search(status, timeout, condition) do |build|
break unless build.stuck?
- drop_build :stuck, build, status, timeout
+ drop_build :stuck, build, status, timeout, reason
end
end
- def search(status, timeout)
+ # rubocop: disable CodeReuse/ActiveRecord
+ def search(status, timeout, condition)
loop do
jobs = Ci::Build.where(status: status)
- .where('ci_builds.updated_at < ?', timeout.ago)
+ .where(condition, timeout.ago)
.includes(:tags, :runner, project: :namespace)
.limit(100)
.to_a
@@ -60,11 +63,12 @@ class StuckCiJobsWorker
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
- def drop_build(type, build, status, timeout)
- Rails.logger.info "#{self.class}: Dropping #{type} build #{build.id} for runner #{build.runner_id} (status: #{status}, timeout: #{timeout})"
+ def drop_build(type, build, status, timeout, reason)
+ Rails.logger.info "#{self.class}: Dropping #{type} build #{build.id} for runner #{build.runner_id} (status: #{status}, timeout: #{timeout}, reason: #{reason})"
Gitlab::OptimisticLocking.retry_lock(build, 3) do |b|
- b.drop(:stuck_or_timeout_failure)
+ b.drop(reason)
end
end
end
diff --git a/app/workers/stuck_import_jobs_worker.rb b/app/workers/stuck_import_jobs_worker.rb
index 79ce06dd66e..667a4121131 100644
--- a/app/workers/stuck_import_jobs_worker.rb
+++ b/app/workers/stuck_import_jobs_worker.rb
@@ -7,54 +7,60 @@ class StuckImportJobsWorker
IMPORT_JOBS_EXPIRATION = 15.hours.to_i
def perform
- projects_without_jid_count = mark_projects_without_jid_as_failed!
- projects_with_jid_count = mark_projects_with_jid_as_failed!
+ import_state_without_jid_count = mark_import_states_without_jid_as_failed!
+ import_state_with_jid_count = mark_import_states_with_jid_as_failed!
Gitlab::Metrics.add_event(:stuck_import_jobs,
- projects_without_jid_count: projects_without_jid_count,
- projects_with_jid_count: projects_with_jid_count)
+ projects_without_jid_count: import_state_without_jid_count,
+ projects_with_jid_count: import_state_with_jid_count)
end
private
- def mark_projects_without_jid_as_failed!
- enqueued_projects_without_jid.each do |project|
- project.mark_import_as_failed(error_message)
+ def mark_import_states_without_jid_as_failed!
+ enqueued_import_states_without_jid.each do |import_state|
+ import_state.mark_as_failed(error_message)
end.count
end
- def mark_projects_with_jid_as_failed!
- # TODO: Rollback this change to use SQL through #pluck
- jids_and_ids = enqueued_projects_with_jid.map { |project| [project.import_jid, project.id] }.to_h
+ # rubocop: disable CodeReuse/ActiveRecord
+ def mark_import_states_with_jid_as_failed!
+ jids_and_ids = enqueued_import_states_with_jid.pluck(:jid, :id).to_h
# Find the jobs that aren't currently running or that exceeded the threshold.
completed_jids = Gitlab::SidekiqStatus.completed_jids(jids_and_ids.keys)
return unless completed_jids.any?
- completed_project_ids = jids_and_ids.values_at(*completed_jids)
+ completed_import_state_ids = jids_and_ids.values_at(*completed_jids)
- # We select the projects again, because they may have transitioned from
+ # We select the import states again, because they may have transitioned from
# scheduled/started to finished/failed while we were looking up their Sidekiq status.
- completed_projects = enqueued_projects_with_jid.where(id: completed_project_ids)
+ completed_import_states = enqueued_import_states_with_jid.where(id: completed_import_state_ids)
- Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_projects.map(&:import_jid).join(', ')}")
+ completed_import_state_jids = completed_import_states.map { |import_state| import_state.jid }.join(', ')
+ Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_import_state_jids}")
- completed_projects.each do |project|
- project.mark_import_as_failed(error_message)
+ completed_import_states.each do |import_state|
+ import_state.mark_as_failed(error_message)
end.count
end
+ # rubocop: enable CodeReuse/ActiveRecord
- def enqueued_projects
- Project.joins_import_state.where("(import_state.status = 'scheduled' OR import_state.status = 'started') OR (projects.import_status = 'scheduled' OR projects.import_status = 'started')")
+ def enqueued_import_states
+ ProjectImportState.with_status([:scheduled, :started])
end
- def enqueued_projects_with_jid
- enqueued_projects.where.not("import_state.jid IS NULL AND projects.import_jid IS NULL")
+ # rubocop: disable CodeReuse/ActiveRecord
+ def enqueued_import_states_with_jid
+ enqueued_import_states.where.not(jid: nil)
end
+ # rubocop: enable CodeReuse/ActiveRecord
- def enqueued_projects_without_jid
- enqueued_projects.where("import_state.jid IS NULL AND projects.import_jid IS NULL")
+ # rubocop: disable CodeReuse/ActiveRecord
+ def enqueued_import_states_without_jid
+ enqueued_import_states.where(jid: nil)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def error_message
"Import timed out. Import took longer than #{IMPORT_JOBS_EXPIRATION} seconds"
diff --git a/app/workers/stuck_merge_jobs_worker.rb b/app/workers/stuck_merge_jobs_worker.rb
index b0a62f76e94..98c81956cba 100644
--- a/app/workers/stuck_merge_jobs_worker.rb
+++ b/app/workers/stuck_merge_jobs_worker.rb
@@ -4,6 +4,7 @@ class StuckMergeJobsWorker
include ApplicationWorker
include CronjobQueue
+ # rubocop: disable CodeReuse/ActiveRecord
def perform
stuck_merge_requests.find_in_batches(batch_size: 100) do |group|
jids = group.map(&:merge_jid)
@@ -18,9 +19,11 @@ class StuckMergeJobsWorker
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ # rubocop: disable CodeReuse/ActiveRecord
def apply_current_state!(completed_jids, completed_ids)
merge_requests = MergeRequest.where(id: completed_ids)
@@ -34,8 +37,11 @@ class StuckMergeJobsWorker
Rails.logger.info("Updated state of locked merge jobs. JIDs: #{completed_jids.join(', ')}")
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def stuck_merge_requests
MergeRequest.select('id, merge_jid').with_state(:locked).where.not(merge_jid: nil).reorder(nil)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/update_head_pipeline_for_merge_request_worker.rb b/app/workers/update_head_pipeline_for_merge_request_worker.rb
index 0487a393566..9ce51662969 100644
--- a/app/workers/update_head_pipeline_for_merge_request_worker.rb
+++ b/app/workers/update_head_pipeline_for_merge_request_worker.rb
@@ -6,6 +6,7 @@ class UpdateHeadPipelineForMergeRequestWorker
queue_namespace :pipeline_processing
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(merge_request_id)
merge_request = MergeRequest.find(merge_request_id)
pipeline = Ci::Pipeline.where(project: merge_request.source_project, ref: merge_request.source_branch).last
@@ -20,6 +21,7 @@ class UpdateHeadPipelineForMergeRequestWorker
merge_request.update_attribute(:head_pipeline_id, pipeline.id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def log_error_message_for(merge_request)
Rails.logger.error(
diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb
index 742841219b3..c7213df652a 100644
--- a/app/workers/update_merge_requests_worker.rb
+++ b/app/workers/update_merge_requests_worker.rb
@@ -5,6 +5,7 @@ class UpdateMergeRequestsWorker
LOG_TIME_THRESHOLD = 90 # seconds
+ # rubocop: disable CodeReuse/ActiveRecord
def perform(project_id, user_id, oldrev, newrev, ref)
project = Project.find_by(id: project_id)
return unless project
@@ -28,4 +29,5 @@ class UpdateMergeRequestsWorker
Rails.logger.info("UpdateMergeRequestsWorker#perform #{args_log}") if time.real > LOG_TIME_THRESHOLD
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/bin/pkgr_before_precompile.sh b/bin/pkgr_before_precompile.sh
index 5a2007f4ab0..54ff32c711b 100755
--- a/bin/pkgr_before_precompile.sh
+++ b/bin/pkgr_before_precompile.sh
@@ -6,7 +6,7 @@ for file in config/*.yml.example; do
cp ${file} config/$(basename ${file} .example)
done
-# Allow to override the Gitlab URL from an environment variable, as this will avoid having to change the configuration file for simple deployments.
+# Allow to override the GitLab URL from an environment variable, as this will avoid having to change the configuration file for simple deployments.
config=$(echo '<% gitlab_url = URI(ENV["GITLAB_URL"] || "http://localhost:80") %>' | cat - config/gitlab.yml)
echo "$config" > config/gitlab.yml
sed -i "s/host: localhost/host: <%= gitlab_url.host %>/" config/gitlab.yml
diff --git a/bin/profile-url b/bin/profile-url
index d8d09641624..9e8585aabba 100755
--- a/bin/profile-url
+++ b/bin/profile-url
@@ -48,7 +48,7 @@ require File.expand_path('../config/environment', File.dirname(__FILE__))
result = Gitlab::Profiler.profile(options[:url],
logger: Logger.new(options[:sql_output]),
post_data: options[:post_data],
- user: User.find_by_username(options[:username]),
+ user: UserFinder.new(options[:username]).find_by_username,
private_token: ENV['PRIVATE_TOKEN'])
printer = RubyProf::CallStackPrinter.new(result)
diff --git a/bin/rails b/bin/rails
index 228f812ccaf..d21b64b3007 100755
--- a/bin/rails
+++ b/bin/rails
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby
# Remove this block when upgraded to rails 5.0.
-unless %w[1 true].include?(ENV["RAILS5"])
+if %w[0 false].include?(ENV["RAILS5"])
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
diff --git a/bin/rake b/bin/rake
index b52a8321f1a..1362d51e3d6 100755
--- a/bin/rake
+++ b/bin/rake
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby
# Remove this block when upgraded to rails 5.0.
-unless %w[1 true].include?(ENV["RAILS5"])
+if %w[0 false].include?(ENV["RAILS5"])
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
diff --git a/bin/rspec b/bin/rspec
index 26583242051..b0770e30a70 100755
--- a/bin/rspec
+++ b/bin/rspec
@@ -2,7 +2,7 @@
# Remove these two lines below when upgraded to rails 5.0.
# Allow run `rspec` command as `RAILS5=1 rspec ...` instead of `BUNDLE_GEMFILE=Gemfile.rails5 rspec ...`
-gemfile = %w[1 true].include?(ENV["RAILS5"]) ? "Gemfile.rails5" : "Gemfile"
+gemfile = %w[0 false].include?(ENV["RAILS5"]) ? "Gemfile.rails4" : "Gemfile"
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../#{gemfile}", __dir__)
begin
diff --git a/bin/secpick b/bin/secpick
index 5029fe57cfe..2b263d452c9 100755
--- a/bin/secpick
+++ b/bin/secpick
@@ -15,7 +15,7 @@ parser = OptionParser.new do |opts|
options[:version] = version&.tr('.', '-')
end
- opts.on('-b', '--branch security-fix-branch', 'Original branch name') do |branch|
+ opts.on('-b', '--branch security-fix-branch', 'Original branch name (optional, defaults to current)') do |branch|
options[:branch] = branch
end
@@ -32,13 +32,21 @@ end
parser.parse!
+options[:branch] ||= `git rev-parse --abbrev-ref HEAD`
+
abort("Missing options. Use #{$0} --help to see the list of options available".red) if options.values.include?(nil)
abort("Wrong version format #{options[:version].bold}".red) unless options[:version] =~ /\A\d*\-\d*\Z/
-branch = [BRANCH_PREFIX, options[:branch], options[:version]].join('-').freeze
-stable_branch = "#{BRANCH_PREFIX}-#{options[:version]}".freeze
+ee = File.exist?('./CHANGELOG-EE.md')
+original_branch = options[:branch].strip
+branch = "#{original_branch}-#{options[:version]}"
+branch.prepend("#{BRANCH_PREFIX}-") unless branch.start_with?("#{BRANCH_PREFIX}-")
+branch = branch.freeze
+stable_branch = "#{BRANCH_PREFIX}-#{options[:version]}".tap do |name|
+ name << "-ee" if ee
+end.freeze
-command = "git fetch #{REMOTE} #{stable_branch} && git checkout #{stable_branch} && git pull #{REMOTE} #{stable_branch} && git checkout -B #{branch} && git cherry-pick #{options[:sha]} && git push #{REMOTE} #{branch}"
+command = "git fetch #{REMOTE} #{stable_branch} && git checkout #{stable_branch} && git pull #{REMOTE} #{stable_branch} && git checkout -B #{branch} && git cherry-pick #{options[:sha]} && git push #{REMOTE} #{branch} && git checkout #{original_branch}"
_stdin, stdout, stderr = Open3.popen3(command)
diff --git a/bin/setup b/bin/setup
index c60c1267e06..34bb667087a 100755
--- a/bin/setup
+++ b/bin/setup
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby
def rails5?
- %w[1 true].include?(ENV["RAILS5"])
+ !%w[0 false].include?(ENV["RAILS5"])
end
require "pathname"
@@ -16,7 +16,7 @@ if rails5?
end
Dir.chdir APP_ROOT do
- # This script is a starting point to setup your application.
+ # This script is a starting point to set up your application.
# Add necessary setup steps to this file:
puts "== Installing dependencies =="
diff --git a/bin/storage_check b/bin/storage_check
deleted file mode 100755
index 5a818732bd1..00000000000
--- a/bin/storage_check
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'optparse'
-require 'net/http'
-require 'json'
-require 'socket'
-require 'logger'
-
-require_relative '../lib/gitlab/storage_check'
-
-Gitlab::StorageCheck::CLI.start!(ARGV)
diff --git a/bin/web b/bin/web
index ecd0bbd10b0..06ff7c39296 100755
--- a/bin/web
+++ b/bin/web
@@ -3,6 +3,11 @@
cd $(dirname $0)/..
app_root=$(pwd)
+# Switch to experimental PUMA configuration
+if [ -n "${EXPERIMENTAL_PUMA}" ]; then
+ exec bin/web_puma "$@"
+fi
+
unicorn_pidfile="$app_root/tmp/pids/unicorn.pid"
unicorn_config="$app_root/config/unicorn.rb"
unicorn_cmd="bundle exec unicorn_rails -c $unicorn_config -E $RAILS_ENV"
diff --git a/bin/web_puma b/bin/web_puma
new file mode 100755
index 00000000000..178fe84800d
--- /dev/null
+++ b/bin/web_puma
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+set -e
+
+cd $(dirname $0)/..
+app_root=$(pwd)
+
+puma_pidfile="$app_root/tmp/pids/puma.pid"
+puma_config="$app_root/config/puma.rb"
+
+spawn_puma()
+{
+ exec bundle exec puma --config "${puma_config}" "$@"
+}
+
+get_puma_pid()
+{
+ pid=$(cat "${puma_pidfile}")
+ if [ -z "$pid" ] ; then
+ echo "Could not find a PID in $puma_pidfile"
+ exit 1
+ fi
+ echo "${pid}"
+}
+
+start()
+{
+ spawn_puma -d
+}
+
+start_foreground()
+{
+ spawn_puma
+}
+
+stop()
+{
+ get_puma_pid
+ kill -QUIT "$(get_puma_pid)"
+}
+
+reload()
+{
+ kill -USR2 "$(get_puma_pid)"
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ start_foreground)
+ start_foreground
+ ;;
+ stop)
+ stop
+ ;;
+ reload)
+ reload
+ ;;
+ *)
+ echo "Usage: RAILS_ENV=your_env $0 {start|stop|reload}"
+ ;;
+esac
diff --git a/changelogs/archive.md b/changelogs/archive.md
index fe461a6ac5e..b57440f7dc6 100644
--- a/changelogs/archive.md
+++ b/changelogs/archive.md
@@ -739,7 +739,7 @@
- Update duration at the end of pipeline
- ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup
- Add group level labels. (!6425)
-- Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
+- Add an example for testing a phoenix application with GitLab CI in the docs (Manthan Mallikarjun)
- Cancelled pipelines could be retried. !6927
- Updating verbiage on git basics to be more intuitive
- Fix project_feature record not generated on project creation
@@ -768,7 +768,7 @@
- Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller)
- Replace unique keyframes mixin with keyframe mixin with specific names (ClemMakesApps)
- Add more tests for calendar contribution (ClemMakesApps)
-- Update Gitlab Shell to fix some problems with moving projects between storages
+- Update GitLab Shell to fix some problems with moving projects between storages
- Cache rendered markdown in the database, rather than Redis
- Add todo toggle event (ClemMakesApps)
- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
@@ -815,7 +815,7 @@
- Replace static issue fixtures by script !6059 (winniehell)
- Append issue template to existing description !6149 (Joseph Frazier)
- Trending projects now only show public projects and the list of projects is cached for a day
-- Memoize Gitlab Shell's secret token (!6599, Justin DiPierro)
+- Memoize GitLab Shell's secret token (!6599, Justin DiPierro)
- Revoke button in Applications Settings underlines on hover.
- Use higher size on Gitlab::Redis connection pool on Sidekiq servers
- Add missing values to linter !6276 (Katarzyna Kobierska Ula Budziszewska)
@@ -930,7 +930,7 @@
## 8.12.3
- - Update Gitlab Shell to support low IO priority for storage moves
+ - Update GitLab Shell to support low IO priority for storage moves
## 8.12.2
@@ -1001,7 +1001,7 @@
- Added ability to specify URL in environment configuration in gitlab-ci.yml
- Escape search term before passing it to Regexp.new !6241 (winniehell)
- Fix pinned sidebar behavior in smaller viewports !6169
- - Fix file permissions change when updating a file on the Gitlab UI !5979
+ - Fix file permissions change when updating a file on the GitLab UI !5979
- Added horizontal padding on build page sidebar on code coverage block. !6196 (Vitaly Baev)
- Change merge_error column from string to text type
- Fix issue with search filter labels not displaying
@@ -1688,7 +1688,7 @@
- Fix commit avatar alignment in compare view. !5128
- Fix broken migration in MySQL. !5005
- Overwrite Host and X-Forwarded-Host headers in NGINX !5213
- - Keeps issue number when importing from Gitlab.com
+ - Keeps issue number when importing from GitLab.com
- Add Pending tab for Builds (Katarzyna Kobierska, Urszula Budziszewska)
## 8.9.5
@@ -4786,7 +4786,7 @@
## 3.1.0
- Updated gems
-- Services: Gitlab CI integration
+- Services: GitLab CI integration
- Events filter on dashboard
- Own namespace for redis/resque
- Optimized commit diff views
@@ -4869,7 +4869,7 @@
## 2.8.0
-- Gitlab Flavored Markdown
+- GitLab Flavored Markdown
- Bulk issues update
- Issues API
- Cucumber coverage increased
diff --git a/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml b/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml
deleted file mode 100644
index f7521ff9225..00000000000
--- a/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-title: First Improvements made to the contributor on-boarding experience.
-merge_request: 20682
-author: Eddie Stubbington
-type: added
diff --git a/changelogs/unreleased/1756-set-iid-via-api.yml b/changelogs/unreleased/1756-set-iid-via-api.yml
deleted file mode 100644
index 680a9464ab4..00000000000
--- a/changelogs/unreleased/1756-set-iid-via-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow issues API to receive an internal ID (iid) on create
-merge_request: 20626
-author: Jamie Schembri
-type: fixed
diff --git a/changelogs/unreleased/18933-render-index-as-readme.yml b/changelogs/unreleased/18933-render-index-as-readme.yml
new file mode 100644
index 00000000000..44acc2c719a
--- /dev/null
+++ b/changelogs/unreleased/18933-render-index-as-readme.yml
@@ -0,0 +1,5 @@
+---
+title: Make index.* render like README.* when it's present in a repository
+merge_request: 22639
+author: Jakub Jirutka
+type: added
diff --git a/changelogs/unreleased/21480-parallel-job-keyword-mvc.yml b/changelogs/unreleased/21480-parallel-job-keyword-mvc.yml
new file mode 100644
index 00000000000..7ac2410b18c
--- /dev/null
+++ b/changelogs/unreleased/21480-parallel-job-keyword-mvc.yml
@@ -0,0 +1,5 @@
+---
+title: Implement parallel job keyword.
+merge_request: 22631
+author:
+type: added
diff --git a/changelogs/unreleased/21970-fix-bamboo-results.yml b/changelogs/unreleased/21970-fix-bamboo-results.yml
new file mode 100644
index 00000000000..2fbb354c477
--- /dev/null
+++ b/changelogs/unreleased/21970-fix-bamboo-results.yml
@@ -0,0 +1,5 @@
+---
+title: "Correctly process Bamboo API result array"
+merge_request: 21970
+author: Alex Lossent
+type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/22311-fix-duplicated-key-in-license-management-job.yml b/changelogs/unreleased/22311-fix-duplicated-key-in-license-management-job.yml
new file mode 100644
index 00000000000..ab64a1387d9
--- /dev/null
+++ b/changelogs/unreleased/22311-fix-duplicated-key-in-license-management-job.yml
@@ -0,0 +1,5 @@
+---
+title: "fix duplicated key in license management job auto devops gitlab ci template"
+merge_request: 22311
+author: Adam Lemanski
+type: fixed
diff --git a/changelogs/unreleased/22717-single-letter-identifier-external-issue-tracker.yml b/changelogs/unreleased/22717-single-letter-identifier-external-issue-tracker.yml
new file mode 100644
index 00000000000..3f7a0d9204e
--- /dev/null
+++ b/changelogs/unreleased/22717-single-letter-identifier-external-issue-tracker.yml
@@ -0,0 +1,5 @@
+---
+title: "Allowing issues with single letter identifiers to be linked to external issue tracker (f.ex T-123)"
+merge_request: 22717
+author: Dídac Rodríguez Arbonès
+type: changed \ No newline at end of file
diff --git a/changelogs/unreleased/23705-add-single-file-download-in-repo.yml b/changelogs/unreleased/23705-add-single-file-download-in-repo.yml
deleted file mode 100644
index f156bfb1101..00000000000
--- a/changelogs/unreleased/23705-add-single-file-download-in-repo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add download button for single file (including raw files) in repository
-merge_request: 20480
-author: Kia Mei Somabes
-type: added
diff --git a/changelogs/unreleased/25140-disable-stop-button.yml b/changelogs/unreleased/25140-disable-stop-button.yml
new file mode 100644
index 00000000000..a6ef52c3155
--- /dev/null
+++ b/changelogs/unreleased/25140-disable-stop-button.yml
@@ -0,0 +1,5 @@
+---
+title: Disables stop environment button while the deploy is in progress
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/25990-improve-web-terminal.yml b/changelogs/unreleased/25990-improve-web-terminal.yml
deleted file mode 100644
index 3f8a8c6211c..00000000000
--- a/changelogs/unreleased/25990-improve-web-terminal.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move xterm to a node dependency and remove it from vendor's folder
-merge_request: 20588
-author:
-type: other
diff --git a/changelogs/unreleased/25990-interactive-web-terminals-authorization.yml b/changelogs/unreleased/25990-interactive-web-terminals-authorization.yml
deleted file mode 100644
index 0a2853c20c6..00000000000
--- a/changelogs/unreleased/25990-interactive-web-terminals-authorization.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix authorization for interactive web terminals
-merge_request: 20811
-author:
-type: fixed
diff --git a/changelogs/unreleased/25990-web-terminal-improvements.yml b/changelogs/unreleased/25990-web-terminal-improvements.yml
deleted file mode 100644
index 99a4a82ea66..00000000000
--- a/changelogs/unreleased/25990-web-terminal-improvements.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make terminal button more visible
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/26723-discussion-filters.yml b/changelogs/unreleased/26723-discussion-filters.yml
new file mode 100644
index 00000000000..3abe95bf30d
--- /dev/null
+++ b/changelogs/unreleased/26723-discussion-filters.yml
@@ -0,0 +1,5 @@
+---
+title: Filter notes by comments or activity for issues and merge requests
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/27231-add-license-data-to-projects-endpoint.yml b/changelogs/unreleased/27231-add-license-data-to-projects-endpoint.yml
new file mode 100644
index 00000000000..f5ed6ccf6df
--- /dev/null
+++ b/changelogs/unreleased/27231-add-license-data-to-projects-endpoint.yml
@@ -0,0 +1,5 @@
+---
+title: Add license data to projects endpoint
+merge_request: 21606
+author: J.D. Bean (@jdbean)
+type: added
diff --git a/changelogs/unreleased/27456-improve-feedback-when-dev-cannot-push-to-empty-repo.yml b/changelogs/unreleased/27456-improve-feedback-when-dev-cannot-push-to-empty-repo.yml
deleted file mode 100644
index 55d82c4ee5d..00000000000
--- a/changelogs/unreleased/27456-improve-feedback-when-dev-cannot-push-to-empty-repo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve feedback when a developer is unable to push to an empty repository
-merge_request: 20519
-author:
-type: changed
diff --git a/changelogs/unreleased/2747-protected-environments-backend-ce.yml b/changelogs/unreleased/2747-protected-environments-backend-ce.yml
deleted file mode 100644
index dcec74a33a7..00000000000
--- a/changelogs/unreleased/2747-protected-environments-backend-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: CE Port of Protected Environments backend
-merge_request: 20859
-author:
-type: other
diff --git a/changelogs/unreleased/28249-add-pagination.yml b/changelogs/unreleased/28249-add-pagination.yml
new file mode 100644
index 00000000000..df15094405a
--- /dev/null
+++ b/changelogs/unreleased/28249-add-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: Adds pagination to pipelines table in merge request page
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/28682-can-merge-branch-before-build-is-started.yml b/changelogs/unreleased/28682-can-merge-branch-before-build-is-started.yml
new file mode 100644
index 00000000000..5ffd93e098f
--- /dev/null
+++ b/changelogs/unreleased/28682-can-merge-branch-before-build-is-started.yml
@@ -0,0 +1,5 @@
+---
+title: Strictly require a pipeline to merge.
+merge_request: 22911
+author:
+type: changed
diff --git a/changelogs/unreleased/28930-add-project-reference-filter.yml b/changelogs/unreleased/28930-add-project-reference-filter.yml
deleted file mode 100644
index c7679c5fe76..00000000000
--- a/changelogs/unreleased/28930-add-project-reference-filter.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add the ability to reference projects in comments and other markdown text.
-merge_request: 20285
-author: Reuben Pereira
-type: added
diff --git a/changelogs/unreleased/29278-commits-page-tooltips.yml b/changelogs/unreleased/29278-commits-page-tooltips.yml
deleted file mode 100644
index d54301a1cf0..00000000000
--- a/changelogs/unreleased/29278-commits-page-tooltips.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove tooltips from commit author avatar and name in commit lists
-merge_request: 20674
-author:
-type: other
diff --git a/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml b/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml
deleted file mode 100644
index 21d9d25d342..00000000000
--- a/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redirect commits to root if no ref is provided (31576)
-merge_request: 20738
-author: Kia Mei Somabes
-type: added
diff --git a/changelogs/unreleased/32783-api-all-members-with-ancestors.yml b/changelogs/unreleased/32783-api-all-members-with-ancestors.yml
deleted file mode 100644
index ca53d02845d..00000000000
--- a/changelogs/unreleased/32783-api-all-members-with-ancestors.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Adds API endpoint /api/v4/(project/group)/:id/members/all to list also inherited
- members
-merge_request: 19748
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/32821-better-error-message-add-invalid-user-to-project.yml b/changelogs/unreleased/32821-better-error-message-add-invalid-user-to-project.yml
deleted file mode 100644
index 587d7209c2f..00000000000
--- a/changelogs/unreleased/32821-better-error-message-add-invalid-user-to-project.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve error message when adding invalid user to a project
-merge_request: 20885
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/32959-update-todo-icon.yml b/changelogs/unreleased/32959-update-todo-icon.yml
new file mode 100644
index 00000000000..f08fd6aa89f
--- /dev/null
+++ b/changelogs/unreleased/32959-update-todo-icon.yml
@@ -0,0 +1,5 @@
+---
+title: Update Todo icons in collapsed sidebar for Issues and MRs
+merge_request: 22534
+author:
+type: changed
diff --git a/changelogs/unreleased/34572-ssh-certificates.yml b/changelogs/unreleased/34572-ssh-certificates.yml
deleted file mode 100644
index 76a08a188de..00000000000
--- a/changelogs/unreleased/34572-ssh-certificates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for SSH certificate authentication
-merge_request: 19911
-author: Ævar Arnfjörð Bjarmason
-type: added
diff --git a/changelogs/unreleased/34758-create-group-clusters.yml b/changelogs/unreleased/34758-create-group-clusters.yml
new file mode 100644
index 00000000000..50efde3cac3
--- /dev/null
+++ b/changelogs/unreleased/34758-create-group-clusters.yml
@@ -0,0 +1,5 @@
+---
+title: Adds model and migrations to enable group level clusters
+merge_request: 22307
+author:
+type: other
diff --git a/changelogs/unreleased/34758-group-cluster-controller.yml b/changelogs/unreleased/34758-group-cluster-controller.yml
new file mode 100644
index 00000000000..88c4c872714
--- /dev/null
+++ b/changelogs/unreleased/34758-group-cluster-controller.yml
@@ -0,0 +1,5 @@
+---
+title: Add ability to create group level clusters and install gitlab managed applications
+merge_request: 22450
+author:
+type: added
diff --git a/changelogs/unreleased/35952-keep-admin-settings-open-after-submit.yml b/changelogs/unreleased/35952-keep-admin-settings-open-after-submit.yml
deleted file mode 100644
index e1e5c8db046..00000000000
--- a/changelogs/unreleased/35952-keep-admin-settings-open-after-submit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Keep admin settings sections open after submitting forms
-merge_request: 21040
-author:
-type: other
diff --git a/changelogs/unreleased/36409-frontend-for-clarifying-the-usefulness-of-the-search-bar.yml b/changelogs/unreleased/36409-frontend-for-clarifying-the-usefulness-of-the-search-bar.yml
deleted file mode 100644
index efa13c9ab3c..00000000000
--- a/changelogs/unreleased/36409-frontend-for-clarifying-the-usefulness-of-the-search-bar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: UX improvements to top nav search bar
-merge_request: 20537
-author:
-type: changed
diff --git a/changelogs/unreleased/37727-fix-file-delete-redirect.yml b/changelogs/unreleased/37727-fix-file-delete-redirect.yml
new file mode 100644
index 00000000000..3fc3965f1f0
--- /dev/null
+++ b/changelogs/unreleased/37727-fix-file-delete-redirect.yml
@@ -0,0 +1,6 @@
+---
+title: On deletion of a file in sub directory in web IDE redirect to the sub directory
+ instead of project root
+merge_request: 21465
+author: George Thomas @thegeorgeous
+type: changed
diff --git a/changelogs/unreleased/38304-username-API-call-case-sensitive.yml b/changelogs/unreleased/38304-username-API-call-case-sensitive.yml
new file mode 100644
index 00000000000..b89778b6c23
--- /dev/null
+++ b/changelogs/unreleased/38304-username-API-call-case-sensitive.yml
@@ -0,0 +1,5 @@
+---
+title: "Use case insensitve username lookups"
+merge_request: 21728
+author: William George
+type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/38604-add-private-profile.yml b/changelogs/unreleased/38604-add-private-profile.yml
deleted file mode 100644
index e40e7d9321e..00000000000
--- a/changelogs/unreleased/38604-add-private-profile.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add an option to have a private profile on GitLab.
-merge_request: 20387
-author: jxterry
-type: added
diff --git a/changelogs/unreleased/40085-add-a-create_merge_request-quick-action.yml b/changelogs/unreleased/40085-add-a-create_merge_request-quick-action.yml
new file mode 100644
index 00000000000..e1614ac7669
--- /dev/null
+++ b/changelogs/unreleased/40085-add-a-create_merge_request-quick-action.yml
@@ -0,0 +1,5 @@
+---
+title: Creates /create_merge_request quickaction
+merge_request: 22485
+author: Jacopo Beschi @jacopo-beschi
+type: added
diff --git a/changelogs/unreleased/40372-prometheus-dashboard-broken-on-firefox.yml b/changelogs/unreleased/40372-prometheus-dashboard-broken-on-firefox.yml
new file mode 100644
index 00000000000..8376fac7abf
--- /dev/null
+++ b/changelogs/unreleased/40372-prometheus-dashboard-broken-on-firefox.yml
@@ -0,0 +1,5 @@
+---
+title: Fix prometheus graphs in firefox
+merge_request: 22400
+author:
+type: fixed
diff --git a/changelogs/unreleased/40973-disable-rack-attack-by-default.yml b/changelogs/unreleased/40973-disable-rack-attack-by-default.yml
deleted file mode 100644
index 681aa761e2a..00000000000
--- a/changelogs/unreleased/40973-disable-rack-attack-by-default.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rack attack is now disabled by default
-merge_request: 16669
-author:
-type: changed
diff --git a/changelogs/unreleased/41416-making-instance-wide-data-tools-more-accessible.yml b/changelogs/unreleased/41416-making-instance-wide-data-tools-more-accessible.yml
deleted file mode 100644
index b980b719d68..00000000000
--- a/changelogs/unreleased/41416-making-instance-wide-data-tools-more-accessible.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow non-admins to view instance statistics (if permitted by the instance admins)
-merge_request: 20874
-author:
-type: changed
diff --git a/changelogs/unreleased/41545-gitlab-merge-request-status-could-not-connect-to-the-ci-server-please-check-your-settings-and-try-again.yml b/changelogs/unreleased/41545-gitlab-merge-request-status-could-not-connect-to-the-ci-server-please-check-your-settings-and-try-again.yml
new file mode 100644
index 00000000000..103419c1185
--- /dev/null
+++ b/changelogs/unreleased/41545-gitlab-merge-request-status-could-not-connect-to-the-ci-server-please-check-your-settings-and-try-again.yml
@@ -0,0 +1,5 @@
+---
+title: Reword error message for internal CI unknown pipeline status
+merge_request: 22474
+author:
+type: changed
diff --git a/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml b/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml
deleted file mode 100644
index c6a0dc4f129..00000000000
--- a/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Fixing milestone date change when editing"
-merge_request: 20279
-author: Orlando Del Aguila
-type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/41784-monitoring-graph-popovers.yml b/changelogs/unreleased/41784-monitoring-graph-popovers.yml
deleted file mode 100644
index 757445d7e0c..00000000000
--- a/changelogs/unreleased/41784-monitoring-graph-popovers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update design for system metrics popovers
-merge_request: 20655
-author:
-type: fixed
diff --git a/changelogs/unreleased/41875-allow-pipelines-to-be-deleted-by-project-owners.yml b/changelogs/unreleased/41875-allow-pipelines-to-be-deleted-by-project-owners.yml
new file mode 100644
index 00000000000..0662ff6f523
--- /dev/null
+++ b/changelogs/unreleased/41875-allow-pipelines-to-be-deleted-by-project-owners.yml
@@ -0,0 +1,5 @@
+---
+title: Allow deleting a Pipeline via the API.
+merge_request: 22988
+author:
+type: added
diff --git a/changelogs/unreleased/41996-copy-to-clipboard-tooltip-appears-under-modal.yml b/changelogs/unreleased/41996-copy-to-clipboard-tooltip-appears-under-modal.yml
deleted file mode 100644
index e452a91d8b7..00000000000
--- a/changelogs/unreleased/41996-copy-to-clipboard-tooltip-appears-under-modal.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Solve tooltip appears under modal
-merge_request: 21017
-author:
-type: fixed
diff --git a/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml b/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml
deleted file mode 100644
index cabe5216045..00000000000
--- a/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds with_projects optional parameter to GET /groups/:id API endpoint
-merge_request: 20494
-author:
-type: changed
diff --git a/changelogs/unreleased/42611-removed-branch-link.yml b/changelogs/unreleased/42611-removed-branch-link.yml
new file mode 100644
index 00000000000..03a206871b4
--- /dev/null
+++ b/changelogs/unreleased/42611-removed-branch-link.yml
@@ -0,0 +1,5 @@
+---
+title: Only render link to branch when branch still exists in pipeline page
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42790-improve-feedback-for-internal-git-access-checks-timeouts.yml b/changelogs/unreleased/42790-improve-feedback-for-internal-git-access-checks-timeouts.yml
new file mode 100644
index 00000000000..d58d8da3a0e
--- /dev/null
+++ b/changelogs/unreleased/42790-improve-feedback-for-internal-git-access-checks-timeouts.yml
@@ -0,0 +1,5 @@
+---
+title: Adds trace of each access check when git push times out
+merge_request: 22265
+author:
+type: added
diff --git a/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml
deleted file mode 100644
index b60aeba860a..00000000000
--- a/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Vue datatype errors for markdownVersion parsing
-merge_request: 20800
-author:
-type: fixed
diff --git a/changelogs/unreleased/43312-remove_user_activity_workers.yml b/changelogs/unreleased/43312-remove_user_activity_workers.yml
deleted file mode 100644
index 6dfd018e350..00000000000
--- a/changelogs/unreleased/43312-remove_user_activity_workers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Delete UserActivities and related workers
-merge_request: 20597
-author:
-type: performance
diff --git a/changelogs/unreleased/43422-Update-images-in-group-docs.yml b/changelogs/unreleased/43422-Update-images-in-group-docs.yml
new file mode 100644
index 00000000000..4c4146589ad
--- /dev/null
+++ b/changelogs/unreleased/43422-Update-images-in-group-docs.yml
@@ -0,0 +1,5 @@
+---
+title: Update images in group docs
+merge_request: 22031
+author: Marc Schwede
+type: other
diff --git a/changelogs/unreleased/43521-keep-personal-emails-private.yml b/changelogs/unreleased/43521-keep-personal-emails-private.yml
new file mode 100644
index 00000000000..0f0bede6482
--- /dev/null
+++ b/changelogs/unreleased/43521-keep-personal-emails-private.yml
@@ -0,0 +1,5 @@
+---
+title: Adds option to override commit email with a noreply private email
+merge_request: 22560
+author:
+type: added
diff --git a/changelogs/unreleased/44012-filter-reactions-none-any.yml b/changelogs/unreleased/44012-filter-reactions-none-any.yml
new file mode 100644
index 00000000000..5d685010f8a
--- /dev/null
+++ b/changelogs/unreleased/44012-filter-reactions-none-any.yml
@@ -0,0 +1,5 @@
+---
+title: Add None / Any options to reactions filter
+merge_request: 22638
+author: Heinrich Lee Yu
+type: added
diff --git a/changelogs/unreleased/44127-board-label-edit-drop-down-is-showing-incorrect-selected-labels-summary.yml b/changelogs/unreleased/44127-board-label-edit-drop-down-is-showing-incorrect-selected-labels-summary.yml
deleted file mode 100644
index de991ef475a..00000000000
--- a/changelogs/unreleased/44127-board-label-edit-drop-down-is-showing-incorrect-selected-labels-summary.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Board label edit dropdown shows incorrect selected labels summary
-merge_request: 20673
-author:
-type: fixed
diff --git a/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml b/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml
deleted file mode 100644
index ddc878ee710..00000000000
--- a/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds foreign key to notification_settings.user_id
-merge_request: 20567
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/45068-no-longer-require-a-deploy-to-start-prometheus-monitoring.yml b/changelogs/unreleased/45068-no-longer-require-a-deploy-to-start-prometheus-monitoring.yml
new file mode 100644
index 00000000000..6a305099dde
--- /dev/null
+++ b/changelogs/unreleased/45068-no-longer-require-a-deploy-to-start-prometheus-monitoring.yml
@@ -0,0 +1,5 @@
+---
+title: No longer require a deploy to start Prometheus monitoring
+merge_request: 22401
+author:
+type: changed
diff --git a/changelogs/unreleased/45318-junit-FE.yml b/changelogs/unreleased/45318-junit-FE.yml
deleted file mode 100644
index bbc08f54484..00000000000
--- a/changelogs/unreleased/45318-junit-FE.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds frontend support to render test reports on the MR widget
-merge_request: 20936
-author:
-type: added
diff --git a/changelogs/unreleased/45318-vuex-store.yml b/changelogs/unreleased/45318-vuex-store.yml
deleted file mode 100644
index 5ea89034bce..00000000000
--- a/changelogs/unreleased/45318-vuex-store.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds Vuex store for reports section in MR widget
-merge_request: 20709
-author:
-type: added
diff --git a/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml b/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml
deleted file mode 100644
index b18f7aec546..00000000000
--- a/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve "Unable to save user profile update with Safari"
-merge_request: 20676
-author:
-type: fixed
diff --git a/changelogs/unreleased/45663-tag-quick-action-on-commit-comments.yml b/changelogs/unreleased/45663-tag-quick-action-on-commit-comments.yml
deleted file mode 100644
index 6d664511e6e..00000000000
--- a/changelogs/unreleased/45663-tag-quick-action-on-commit-comments.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "`/tag` quick action on Commit comments"
-merge_request: 20694
-author: Peter Leitzen
-type: added
diff --git a/changelogs/unreleased/45669-table-in-jobs-on-pipeline.yml b/changelogs/unreleased/45669-table-in-jobs-on-pipeline.yml
new file mode 100644
index 00000000000..97052d01b24
--- /dev/null
+++ b/changelogs/unreleased/45669-table-in-jobs-on-pipeline.yml
@@ -0,0 +1,5 @@
+---
+title: Hide all tables on Pipeline when no Jobs for the Pipeline
+merge_request: 18540
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/46165-web-ide-branch-picker.yml b/changelogs/unreleased/46165-web-ide-branch-picker.yml
deleted file mode 100644
index ff879cb3d37..00000000000
--- a/changelogs/unreleased/46165-web-ide-branch-picker.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Create branch and MR picker for Web IDE
-merge_request: 20978
-author:
-type: changed
diff --git a/changelogs/unreleased/46535-orphaned-uploads.yml b/changelogs/unreleased/46535-orphaned-uploads.yml
deleted file mode 100644
index 1cd087a6aad..00000000000
--- a/changelogs/unreleased/46535-orphaned-uploads.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Clean orphaned files in object storage
-merge_request: 20918
-author:
-type: added
diff --git a/changelogs/unreleased/46703-group-dashboard-line-height-is-too-tall-for-group-names.yml b/changelogs/unreleased/46703-group-dashboard-line-height-is-too-tall-for-group-names.yml
deleted file mode 100644
index 5b91c6d5a9f..00000000000
--- a/changelogs/unreleased/46703-group-dashboard-line-height-is-too-tall-for-group-names.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Solves group dashboard line height is too tall for group names.
-merge_request: 21033
-author:
-type: fixed
diff --git a/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml
deleted file mode 100644
index d490df58144..00000000000
--- a/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow cloning LFS repositories through DeployTokens
-merge_request: 20729
-author:
-type: other
diff --git a/changelogs/unreleased/46884-remove-card-title.yml b/changelogs/unreleased/46884-remove-card-title.yml
new file mode 100644
index 00000000000..95f08a67638
--- /dev/null
+++ b/changelogs/unreleased/46884-remove-card-title.yml
@@ -0,0 +1,5 @@
+---
+title: Remove .card-title from .card-header for BS4 migration
+merge_request: 19335
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml b/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml
deleted file mode 100644
index d95714a5267..00000000000
--- a/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix updated_at if created_at is set for Note API
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects.yml b/changelogs/unreleased/46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects.yml
deleted file mode 100644
index 71e523e6de8..00000000000
--- a/changelogs/unreleased/46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable hashed storage for all newly created or renamed projects
-merge_request: 19747
-author:
-type: changed
diff --git a/changelogs/unreleased/47008-issue-board-card-design.yml b/changelogs/unreleased/47008-issue-board-card-design.yml
new file mode 100644
index 00000000000..39238687943
--- /dev/null
+++ b/changelogs/unreleased/47008-issue-board-card-design.yml
@@ -0,0 +1,5 @@
+---
+title: Issue board card design
+merge_request: 21229
+author:
+type: changed
diff --git a/changelogs/unreleased/47156-improve-auto-devops-settings.yml b/changelogs/unreleased/47156-improve-auto-devops-settings.yml
deleted file mode 100644
index d8993565047..00000000000
--- a/changelogs/unreleased/47156-improve-auto-devops-settings.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve and simplify Auto DevOps settings flow
-merge_request: 20946
-author:
-type: other
diff --git a/changelogs/unreleased/47419-Fix-breadcrumbs.yml b/changelogs/unreleased/47419-Fix-breadcrumbs.yml
deleted file mode 100644
index 1a7f8196683..00000000000
--- a/changelogs/unreleased/47419-Fix-breadcrumbs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix breadcrumbs in Admin/User interface.
-merge_request: 19608
-author: Robin Naundorf
-type: fixed
diff --git a/changelogs/unreleased/47548-monospace-commit-messages.yml b/changelogs/unreleased/47548-monospace-commit-messages.yml
deleted file mode 100644
index 7344f72207b..00000000000
--- a/changelogs/unreleased/47548-monospace-commit-messages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update commit message styles with monospace font and overflow-x
-merge_request: 20988
-author:
-type: changed
diff --git a/changelogs/unreleased/47728-mr-api-documentation-changes.yml b/changelogs/unreleased/47728-mr-api-documentation-changes.yml
deleted file mode 100644
index 12720f280a1..00000000000
--- a/changelogs/unreleased/47728-mr-api-documentation-changes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove changes_count from MR API documentation where necessary
-merge_request: 19745
-author: Jan Beckmann
-type: fixed
diff --git a/changelogs/unreleased/47752-buttons-on-new-file-page-wrap-outside-of-container-for-long-branch-names.yml b/changelogs/unreleased/47752-buttons-on-new-file-page-wrap-outside-of-container-for-long-branch-names.yml
deleted file mode 100644
index 81ca632947d..00000000000
--- a/changelogs/unreleased/47752-buttons-on-new-file-page-wrap-outside-of-container-for-long-branch-names.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix buttons on the new file page wrapping outside of the container
-merge_request: 21015
-author:
-type: fixed
diff --git a/changelogs/unreleased/47768-web-ide-redesign-header.yml b/changelogs/unreleased/47768-web-ide-redesign-header.yml
deleted file mode 100644
index 49133158164..00000000000
--- a/changelogs/unreleased/47768-web-ide-redesign-header.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redesign Web IDE back button and context header
-merge_request: 20850
-author:
-type: changed
diff --git a/changelogs/unreleased/48036-fix-web-ide-blob-crash.yml b/changelogs/unreleased/48036-fix-web-ide-blob-crash.yml
deleted file mode 100644
index 6ff209b5181..00000000000
--- a/changelogs/unreleased/48036-fix-web-ide-blob-crash.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Web IDE crashing on directories named 'blob'
-merge_request: 20712
-author:
-type: fixed
diff --git a/changelogs/unreleased/48055-web-ide-resize-handles.yml b/changelogs/unreleased/48055-web-ide-resize-handles.yml
deleted file mode 100644
index 0f650cdda6f..00000000000
--- a/changelogs/unreleased/48055-web-ide-resize-handles.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Increase width of Web IDE sidebar resize handles
-merge_request: 20818
-author:
-type: fixed
diff --git a/changelogs/unreleased/48098-mutual-auth-cluster-applications.yml b/changelogs/unreleased/48098-mutual-auth-cluster-applications.yml
deleted file mode 100644
index 43125ef25c4..00000000000
--- a/changelogs/unreleased/48098-mutual-auth-cluster-applications.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Ensure installed Helm Tiller For GitLab Managed Apps Is protected by mutual
- auth
-merge_request: 20928
-author:
-type: changed
diff --git a/changelogs/unreleased/48246-osw-load-diffs-improvement.yml b/changelogs/unreleased/48246-osw-load-diffs-improvement.yml
deleted file mode 100644
index c4292ab0d29..00000000000
--- a/changelogs/unreleased/48246-osw-load-diffs-improvement.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve performance when fetching collapsed diffs and commenting in merge requests
-merge_request: 20940
-author:
-type: performance
diff --git a/changelogs/unreleased/48320-cancel-a-created-job.yml b/changelogs/unreleased/48320-cancel-a-created-job.yml
deleted file mode 100644
index 3e7a9e9ae52..00000000000
--- a/changelogs/unreleased/48320-cancel-a-created-job.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allows to cancel a Created job
-merge_request: 20635
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/48419-charts-with-long-label-appear-oversized.yml b/changelogs/unreleased/48419-charts-with-long-label-appear-oversized.yml
deleted file mode 100644
index b3ccbb121f0..00000000000
--- a/changelogs/unreleased/48419-charts-with-long-label-appear-oversized.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: fix height of full-width Metrics charts on large screens
-merge_request: 20866
-author:
-type: fixed
diff --git a/changelogs/unreleased/48456-fix-system-level-labels-admin-ui.yml b/changelogs/unreleased/48456-fix-system-level-labels-admin-ui.yml
deleted file mode 100644
index c34750a3b88..00000000000
--- a/changelogs/unreleased/48456-fix-system-level-labels-admin-ui.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix the UI for listing system-level labels
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/48475-gitlab-pages-settings-regressions.yml b/changelogs/unreleased/48475-gitlab-pages-settings-regressions.yml
new file mode 100644
index 00000000000..f543730a57d
--- /dev/null
+++ b/changelogs/unreleased/48475-gitlab-pages-settings-regressions.yml
@@ -0,0 +1,5 @@
+---
+title: Fixing regression issues on pages settings and details
+merge_request: 22821
+author:
+type: fixed
diff --git a/changelogs/unreleased/48494-fix-merge-request-buttons-spacing.yml b/changelogs/unreleased/48494-fix-merge-request-buttons-spacing.yml
new file mode 100644
index 00000000000..41cc024b8ac
--- /dev/null
+++ b/changelogs/unreleased/48494-fix-merge-request-buttons-spacing.yml
@@ -0,0 +1,5 @@
+---
+title: Fix incorrect spacing between buttons when commenting on a MR.
+merge_request: 22135
+author:
+type: fixed
diff --git a/changelogs/unreleased/48537-update-avatar-only-via-api.yml b/changelogs/unreleased/48537-update-avatar-only-via-api.yml
deleted file mode 100644
index 9b3ab946cc1..00000000000
--- a/changelogs/unreleased/48537-update-avatar-only-via-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow updating a project's avatar without other params
-merge_request:
-author: Jamie Schembri
-type: fixed
diff --git a/changelogs/unreleased/48542-code-link.yml b/changelogs/unreleased/48542-code-link.yml
deleted file mode 100644
index 8d8d9bf8d74..00000000000
--- a/changelogs/unreleased/48542-code-link.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix link color in markdown code brackets
-merge_request: 20841
-author:
-type: fixed
diff --git a/changelogs/unreleased/48617-promoting-milestone.yml b/changelogs/unreleased/48617-promoting-milestone.yml
deleted file mode 100644
index 7fbc15051cf..00000000000
--- a/changelogs/unreleased/48617-promoting-milestone.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Escapes milestone and label's names on flash notice when promoting them
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/48636-new-mr-card-styles.yml b/changelogs/unreleased/48636-new-mr-card-styles.yml
deleted file mode 100644
index 94f62e677fb..00000000000
--- a/changelogs/unreleased/48636-new-mr-card-styles.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix new MR card styles
-merge_request: 20822
-author:
-type: fixed
diff --git a/changelogs/unreleased/48657-persist-auto-devops-banner-dismissal-per-user-cookie.yml b/changelogs/unreleased/48657-persist-auto-devops-banner-dismissal-per-user-cookie.yml
deleted file mode 100644
index 7ee018ef679..00000000000
--- a/changelogs/unreleased/48657-persist-auto-devops-banner-dismissal-per-user-cookie.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Persist 'Auto DevOps' banner dismissal globally
-merge_request: 20540
-author:
-type: other
diff --git a/changelogs/unreleased/48684-sort-projects-by-stars-in-groups.yml b/changelogs/unreleased/48684-sort-projects-by-stars-in-groups.yml
new file mode 100644
index 00000000000..01681adab24
--- /dev/null
+++ b/changelogs/unreleased/48684-sort-projects-by-stars-in-groups.yml
@@ -0,0 +1,4 @@
+---
+title: Add new sort option "most_stars" to "Group > Children" pages
+merge_request: 22121
+author: Rene Hennig
diff --git a/changelogs/unreleased/48731-show-empty-state-on-wiki-only-projects.yml b/changelogs/unreleased/48731-show-empty-state-on-wiki-only-projects.yml
new file mode 100644
index 00000000000..8fd1b48b00e
--- /dev/null
+++ b/changelogs/unreleased/48731-show-empty-state-on-wiki-only-projects.yml
@@ -0,0 +1,6 @@
+---
+title: Update the empty state on wiki-only projects to display an empty state that
+ is more consistent with the rest of the system.
+merge_request: 22262
+author:
+type: changed
diff --git a/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml b/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml
deleted file mode 100644
index f298380b920..00000000000
--- a/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add object storage logic to project import
-merge_request: 20773
-author:
-type: added
diff --git a/changelogs/unreleased/48804-redesign-gcp-banner.yml b/changelogs/unreleased/48804-redesign-gcp-banner.yml
deleted file mode 100644
index 729f959badc..00000000000
--- a/changelogs/unreleased/48804-redesign-gcp-banner.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redesign GCP offer banner
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/48823-copy-gfm.yml b/changelogs/unreleased/48823-copy-gfm.yml
deleted file mode 100644
index b6137e2e3f9..00000000000
--- a/changelogs/unreleased/48823-copy-gfm.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Copy diff file path as GFM is broken
-merge_request: 20725
-author:
-type: fixed
diff --git a/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml b/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml
deleted file mode 100644
index d79b95411aa..00000000000
--- a/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Chart versions for applications installed by one click install buttons should
- be version locked
-merge_request: 20765
-author:
-type: fixed
diff --git a/changelogs/unreleased/48889-message-for-were-merged-into.yml b/changelogs/unreleased/48889-message-for-were-merged-into.yml
new file mode 100644
index 00000000000..552b8826829
--- /dev/null
+++ b/changelogs/unreleased/48889-message-for-were-merged-into.yml
@@ -0,0 +1,5 @@
+---
+title: Fix 'merged with' UI being displayed when merge request has no merge commit
+merge_request: 22022
+author:
+type: fixed
diff --git a/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml b/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml
deleted file mode 100644
index 0466debea65..00000000000
--- a/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable SAML and Bitbucket if OmniAuth is disabled
-merge_request: 20608
-author:
-type: fixed
diff --git a/changelogs/unreleased/48934.yml b/changelogs/unreleased/48934.yml
deleted file mode 100644
index 8e2e53ed198..00000000000
--- a/changelogs/unreleased/48934.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve danger confirmation modals by focusing input field
-merge_request:
-author: Jamie Schembri
-type: added
diff --git a/changelogs/unreleased/48976-fix-sti-background-migration.yml b/changelogs/unreleased/48976-fix-sti-background-migration.yml
deleted file mode 100644
index e95536b213c..00000000000
--- a/changelogs/unreleased/48976-fix-sti-background-migration.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: "[Rails5] Fix 'Invalid single-table inheritance type: Group is not a subclass
- of Gitlab::BackgroundMigration::FixCrossProjectLabelLinks::Namespace'"
-merge_request: 20462
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/49025-docs-kubernetes-tiller.yml b/changelogs/unreleased/49025-docs-kubernetes-tiller.yml
deleted file mode 100644
index c4f01490cfa..00000000000
--- a/changelogs/unreleased/49025-docs-kubernetes-tiller.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update docs of Helm Tiller
-merge_request: 20515
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml b/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml
deleted file mode 100644
index 541b562adac..00000000000
--- a/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: DNS prefetching if asset_host for CDN hosting is set
-merge_request: 20781
-author:
-type: performance
diff --git a/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml b/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml
deleted file mode 100644
index ab320450a1a..00000000000
--- a/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Gitaly Servers link into Admin > Overview navigation menu
-merge_request: 20550
-author:
-type: added
diff --git a/changelogs/unreleased/49161-disable-toggle-comments.yml b/changelogs/unreleased/49161-disable-toggle-comments.yml
deleted file mode 100644
index 5ec16191f07..00000000000
--- a/changelogs/unreleased/49161-disable-toggle-comments.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disables toggle comments button if diff has no discussions
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml
deleted file mode 100644
index c757e55f1cd..00000000000
--- a/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Sanitize git URL in import errors
-merge_request:
-author: Jamie Schembri
-type: fixed
diff --git a/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml b/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml
deleted file mode 100644
index f21bd454e84..00000000000
--- a/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix typo in CSS transform property for Memory Graph component
-merge_request: 20650
-author:
-type: fixed
diff --git a/changelogs/unreleased/49324-add-support-for-tar-gz-autodevops-charts.yml b/changelogs/unreleased/49324-add-support-for-tar-gz-autodevops-charts.yml
deleted file mode 100644
index a87eef3f7f3..00000000000
--- a/changelogs/unreleased/49324-add-support-for-tar-gz-autodevops-charts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for tar.gz AUTO_DEVOPS_CHART charts (#49324)
-merge_request: 20691
-author: '@kondi1'
-type: added
diff --git a/changelogs/unreleased/49364-fix-broadcast-margin.yml b/changelogs/unreleased/49364-fix-broadcast-margin.yml
deleted file mode 100644
index 821fb9df1af..00000000000
--- a/changelogs/unreleased/49364-fix-broadcast-margin.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix misalignment of broadcast message on login page
-merge_request: 20794
-author: Robin Naundorf
-type: fixed
diff --git a/changelogs/unreleased/49375-move-help-popover.yml b/changelogs/unreleased/49375-move-help-popover.yml
deleted file mode 100644
index 2547d5768bf..00000000000
--- a/changelogs/unreleased/49375-move-help-popover.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Moves help_popover component to a common location
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/49403-redesign-activity-feed.yml b/changelogs/unreleased/49403-redesign-activity-feed.yml
new file mode 100644
index 00000000000..cec53a3ef5a
--- /dev/null
+++ b/changelogs/unreleased/49403-redesign-activity-feed.yml
@@ -0,0 +1,4 @@
+title: Redesign activity feed
+merge_request: 22217
+author:
+type: other
diff --git a/changelogs/unreleased/49417-improve-settings-pages-design-by-prioritizing-content-group-settings.yml b/changelogs/unreleased/49417-improve-settings-pages-design-by-prioritizing-content-group-settings.yml
new file mode 100644
index 00000000000..8ded24a1cd0
--- /dev/null
+++ b/changelogs/unreleased/49417-improve-settings-pages-design-by-prioritizing-content-group-settings.yml
@@ -0,0 +1,5 @@
+---
+title: Update group settings/edit page to new design
+merge_request: 21115
+author:
+type: other
diff --git a/changelogs/unreleased/49479-hide-unmerged-env-perf-stats.yml b/changelogs/unreleased/49479-hide-unmerged-env-perf-stats.yml
new file mode 100644
index 00000000000..5118949f8a3
--- /dev/null
+++ b/changelogs/unreleased/49479-hide-unmerged-env-perf-stats.yml
@@ -0,0 +1,5 @@
+---
+title: Don't show Memory Usage for unmerged MRs
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/49499-list-of-projects-not-loading-when-trying-to-create-an-issue-from-a-board-typeerror.yml b/changelogs/unreleased/49499-list-of-projects-not-loading-when-trying-to-create-an-issue-from-a-board-typeerror.yml
deleted file mode 100644
index 043698269e2..00000000000
--- a/changelogs/unreleased/49499-list-of-projects-not-loading-when-trying-to-create-an-issue-from-a-board-typeerror.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed list of projects not loading in group boards
-merge_request: 20955
-author:
-type: fixed
diff --git a/changelogs/unreleased/49591-use-cached-readme-blobs.yml b/changelogs/unreleased/49591-use-cached-readme-blobs.yml
new file mode 100644
index 00000000000..59098d2733a
--- /dev/null
+++ b/changelogs/unreleased/49591-use-cached-readme-blobs.yml
@@ -0,0 +1,5 @@
+---
+title: Use cached readme contents when available
+merge_request: 22325
+author:
+type: performance
diff --git a/changelogs/unreleased/49701-sorting-by-name-on-milestones-page-error.yml b/changelogs/unreleased/49701-sorting-by-name-on-milestones-page-error.yml
deleted file mode 100644
index 7eb73110d60..00000000000
--- a/changelogs/unreleased/49701-sorting-by-name-on-milestones-page-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix sorting by name on milestones page
-merge_request: 20881
-author:
-type: fixed
diff --git a/changelogs/unreleased/49726-upgrade-helm-to-2-11.yml b/changelogs/unreleased/49726-upgrade-helm-to-2-11.yml
new file mode 100644
index 00000000000..dd26af875f5
--- /dev/null
+++ b/changelogs/unreleased/49726-upgrade-helm-to-2-11.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade helm to 2.11.0 and upgrade on every install
+merge_request: 22693
+author:
+type: added
diff --git a/changelogs/unreleased/49747-update-poll-2xx.yml b/changelogs/unreleased/49747-update-poll-2xx.yml
deleted file mode 100644
index 359d1b80447..00000000000
--- a/changelogs/unreleased/49747-update-poll-2xx.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Changes poll.js to keep polling on any 2xx http status code
-merge_request: 20904
-author:
-type: other
diff --git a/changelogs/unreleased/49776-pipeline-job-log-page-uses-too-much-cpu-for-loading-animation.yml b/changelogs/unreleased/49776-pipeline-job-log-page-uses-too-much-cpu-for-loading-animation.yml
deleted file mode 100644
index 96da2436a9f..00000000000
--- a/changelogs/unreleased/49776-pipeline-job-log-page-uses-too-much-cpu-for-loading-animation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: refactor pipeline job log animation to reduce CPU usage
-merge_request: 20915
-author:
-type: performance
diff --git a/changelogs/unreleased/49830-use-helm-272.yml b/changelogs/unreleased/49830-use-helm-272.yml
deleted file mode 100644
index f6ecc12dbfa..00000000000
--- a/changelogs/unreleased/49830-use-helm-272.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use Helm 2.7.2 for GitLab Managed Apps
-merge_request: 20956
-author:
-type: changed
diff --git a/changelogs/unreleased/49835-increase-width.yml b/changelogs/unreleased/49835-increase-width.yml
deleted file mode 100644
index f963c0c5e47..00000000000
--- a/changelogs/unreleased/49835-increase-width.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Increases title column on modal for reports
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/49851-link-to-runners.yml b/changelogs/unreleased/49851-link-to-runners.yml
deleted file mode 100644
index 89fd6853bc8..00000000000
--- a/changelogs/unreleased/49851-link-to-runners.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Automatically expand runner's settings block when linking to the runner's settings
- page
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-1.yml b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-1.yml
deleted file mode 100644
index ffa4a3bc710..00000000000
--- a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-1.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix rendering of the context lines in MR diffs page.
-merge_request: 20968
-author:
-type: fixed
diff --git a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-2.yml b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-2.yml
deleted file mode 100644
index 42b0e4194f1..00000000000
--- a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix autosave and ESC confirmation issues for MR discussions.
-merge_request: 20968
-author:
-type: fixed
diff --git a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-3.yml b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-3.yml
deleted file mode 100644
index 29419091d02..00000000000
--- a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-3.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix navigation to First and Next discussion on MR Changes tab.
-merge_request: 20968
-author:
-type: fixed
diff --git a/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml b/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml
deleted file mode 100644
index 30f5002c5b5..00000000000
--- a/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: fix error caused when using the search bar while unauthenticated
-merge_request: 20970
-author:
-type: fixed
diff --git a/changelogs/unreleased/49899-merge-request-e-mail-link-has-full-url.yml b/changelogs/unreleased/49899-merge-request-e-mail-link-has-full-url.yml
deleted file mode 100644
index 856a7c579f3..00000000000
--- a/changelogs/unreleased/49899-merge-request-e-mail-link-has-full-url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensure links in notifications footer are not escaped
-merge_request: 21000
-author:
-type: fixed
diff --git a/changelogs/unreleased/49966-improve-junit-fe.yml b/changelogs/unreleased/49966-improve-junit-fe.yml
deleted file mode 100644
index 48971d3bfd6..00000000000
--- a/changelogs/unreleased/49966-improve-junit-fe.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Renders test reports for resolved failures and resets error state
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/50063-add-missing-i18n-strings-to-issue-boards.yml b/changelogs/unreleased/50063-add-missing-i18n-strings-to-issue-boards.yml
deleted file mode 100644
index ca17a41d611..00000000000
--- a/changelogs/unreleased/50063-add-missing-i18n-strings-to-issue-boards.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added missing i18n strings to issue boards lables dropdown
-merge_request: 21081
-author:
-type: other
diff --git a/changelogs/unreleased/50101-stuck-component.yml b/changelogs/unreleased/50101-stuck-component.yml
deleted file mode 100644
index bfe4009a2b3..00000000000
--- a/changelogs/unreleased/50101-stuck-component.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Creates Vvue component for warning block about stuck runners
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/50126-blocked-user-card.yml b/changelogs/unreleased/50126-blocked-user-card.yml
deleted file mode 100644
index a42d62e5530..00000000000
--- a/changelogs/unreleased/50126-blocked-user-card.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix blocked user card style
-merge_request: 21095
-author:
-type: fixed
diff --git a/changelogs/unreleased/50185-fix-broken-file-name-navigation.yml b/changelogs/unreleased/50185-fix-broken-file-name-navigation.yml
new file mode 100644
index 00000000000..d1b341af457
--- /dev/null
+++ b/changelogs/unreleased/50185-fix-broken-file-name-navigation.yml
@@ -0,0 +1,5 @@
+---
+title: Fix broken file name navigation on MRs
+merge_request: 22109
+author:
+type: fixed
diff --git a/changelogs/unreleased/50243-auto-devops-behind-a-proxy.yml b/changelogs/unreleased/50243-auto-devops-behind-a-proxy.yml
deleted file mode 100644
index 0f6208d0c7a..00000000000
--- a/changelogs/unreleased/50243-auto-devops-behind-a-proxy.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Vendor Auto-DevOps.gitlab-ci.yml with new proxy env vars passed through to
- docker
-merge_request: 21159
-author: kinolaev
-type: added
diff --git a/changelogs/unreleased/50728-re-arrange-help-related-user-menu-items-into-new-help-menu.yml b/changelogs/unreleased/50728-re-arrange-help-related-user-menu-items-into-new-help-menu.yml
new file mode 100644
index 00000000000..dfda65ef91b
--- /dev/null
+++ b/changelogs/unreleased/50728-re-arrange-help-related-user-menu-items-into-new-help-menu.yml
@@ -0,0 +1,5 @@
+---
+title: Re-arrange help-related user menu items into new Help menu
+merge_request: 22195
+author:
+type: added
diff --git a/changelogs/unreleased/50904-job-log.yml b/changelogs/unreleased/50904-job-log.yml
new file mode 100644
index 00000000000..31eb2d56b77
--- /dev/null
+++ b/changelogs/unreleased/50904-job-log.yml
@@ -0,0 +1,5 @@
+---
+title: Transform job page into a single Vue+Vuex application
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/50962-create-new-group-rename-form-fields-and-update-ui.yml b/changelogs/unreleased/50962-create-new-group-rename-form-fields-and-update-ui.yml
new file mode 100644
index 00000000000..db374e10c36
--- /dev/null
+++ b/changelogs/unreleased/50962-create-new-group-rename-form-fields-and-update-ui.yml
@@ -0,0 +1,5 @@
+---
+title: 'Create new group: Rename form fields and update UI'
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/51259-ci-cd-gitlab-ui-1.yml b/changelogs/unreleased/51259-ci-cd-gitlab-ui-1.yml
new file mode 100644
index 00000000000..1d761d6299c
--- /dev/null
+++ b/changelogs/unreleased/51259-ci-cd-gitlab-ui-1.yml
@@ -0,0 +1,5 @@
+---
+title: Uses new gitlab-ui components in Jobs and Pipelines components
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/51259-ci-cd-gitlab-ui.yml b/changelogs/unreleased/51259-ci-cd-gitlab-ui.yml
new file mode 100644
index 00000000000..a15f1c033b3
--- /dev/null
+++ b/changelogs/unreleased/51259-ci-cd-gitlab-ui.yml
@@ -0,0 +1,5 @@
+---
+title: Uses gitlab-ui components in jobs components
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/51259-ci-cd-tooltips.yml b/changelogs/unreleased/51259-ci-cd-tooltips.yml
new file mode 100644
index 00000000000..fc0010dbeba
--- /dev/null
+++ b/changelogs/unreleased/51259-ci-cd-tooltips.yml
@@ -0,0 +1,6 @@
+---
+title: Replaces tooltip directive with the new gl-tooltip directive for consistency
+ in some ci/cd code
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/51306-fix-inaccessible-dropdown-for-codeless-projects.yml b/changelogs/unreleased/51306-fix-inaccessible-dropdown-for-codeless-projects.yml
new file mode 100644
index 00000000000..13e3bb66430
--- /dev/null
+++ b/changelogs/unreleased/51306-fix-inaccessible-dropdown-for-codeless-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Fix inaccessible dropdown for code-less projects
+merge_request: 22137
+author:
+type: other
diff --git a/changelogs/unreleased/51335-fail-early-when-user-cannot-be-identified.yml b/changelogs/unreleased/51335-fail-early-when-user-cannot-be-identified.yml
new file mode 100644
index 00000000000..a042debc28f
--- /dev/null
+++ b/changelogs/unreleased/51335-fail-early-when-user-cannot-be-identified.yml
@@ -0,0 +1,5 @@
+---
+title: If user was not found, service hooks won't run on post receive background job
+merge_request: 22519
+author:
+type: fixed
diff --git a/changelogs/unreleased/51386-broken-border-reports.yml b/changelogs/unreleased/51386-broken-border-reports.yml
new file mode 100644
index 00000000000..720b4edc467
--- /dev/null
+++ b/changelogs/unreleased/51386-broken-border-reports.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes broken borders for reports section in MR widget
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/51527-xss-in-mr-source-branch.yml b/changelogs/unreleased/51527-xss-in-mr-source-branch.yml
new file mode 100644
index 00000000000..dae277b6413
--- /dev/null
+++ b/changelogs/unreleased/51527-xss-in-mr-source-branch.yml
@@ -0,0 +1,5 @@
+---
+title: Fix XSS in merge request source branch name
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/51620-cannot-add-label-to-issue-from-board.yml b/changelogs/unreleased/51620-cannot-add-label-to-issue-from-board.yml
new file mode 100644
index 00000000000..9e99779d352
--- /dev/null
+++ b/changelogs/unreleased/51620-cannot-add-label-to-issue-from-board.yml
@@ -0,0 +1,4 @@
+title: Make Issue Board sidebar show project-specific labels based on selected Issue
+merge_request: 22475
+author:
+type: fixed
diff --git a/changelogs/unreleased/51716-add-kubernetes-namespace-background-migration.yml b/changelogs/unreleased/51716-add-kubernetes-namespace-background-migration.yml
new file mode 100644
index 00000000000..89a91e8deaf
--- /dev/null
+++ b/changelogs/unreleased/51716-add-kubernetes-namespace-background-migration.yml
@@ -0,0 +1,5 @@
+---
+title: Add background migration to populate Kubernetes namespaces
+merge_request: 22433
+author:
+type: added
diff --git a/changelogs/unreleased/51716-add-kubernetes-namespace-model.yml b/changelogs/unreleased/51716-add-kubernetes-namespace-model.yml
new file mode 100644
index 00000000000..ad43c512ba3
--- /dev/null
+++ b/changelogs/unreleased/51716-add-kubernetes-namespace-model.yml
@@ -0,0 +1,5 @@
+---
+title: Introduce new model to persist specific cluster information
+merge_request: 22404
+author:
+type: added
diff --git a/changelogs/unreleased/51716-create-kube-namespace.yml b/changelogs/unreleased/51716-create-kube-namespace.yml
new file mode 100644
index 00000000000..851e19c0a38
--- /dev/null
+++ b/changelogs/unreleased/51716-create-kube-namespace.yml
@@ -0,0 +1,5 @@
+---
+title: Extend RBAC by having a service account restricted to project's namespace
+merge_request: 22011
+author:
+type: other
diff --git a/changelogs/unreleased/51955-change-single-item-breadcrumbs-to-page-titles.yml b/changelogs/unreleased/51955-change-single-item-breadcrumbs-to-page-titles.yml
new file mode 100644
index 00000000000..63fa84d2d51
--- /dev/null
+++ b/changelogs/unreleased/51955-change-single-item-breadcrumbs-to-page-titles.yml
@@ -0,0 +1,5 @@
+---
+title: Change single-item breadcrumbs to page titles
+merge_request: 22155
+author:
+type: changed
diff --git a/changelogs/unreleased/51959-branch-and-tag-name-links.yml b/changelogs/unreleased/51959-branch-and-tag-name-links.yml
new file mode 100644
index 00000000000..64f1522c70d
--- /dev/null
+++ b/changelogs/unreleased/51959-branch-and-tag-name-links.yml
@@ -0,0 +1,5 @@
+---
+title: Chat message push notifications now include links back to GitLab branches
+merge_request: 22651
+author: Tony Castrogiovanni
+type: added
diff --git a/changelogs/unreleased/51972-prometheus-not-showing-as-installed-even-though-it-is.yml b/changelogs/unreleased/51972-prometheus-not-showing-as-installed-even-though-it-is.yml
new file mode 100644
index 00000000000..73b035fca00
--- /dev/null
+++ b/changelogs/unreleased/51972-prometheus-not-showing-as-installed-even-though-it-is.yml
@@ -0,0 +1,5 @@
+---
+title: Show available clusters when installed or updated
+merge_request: 22356
+author:
+type: fixed
diff --git a/changelogs/unreleased/52059-filter-milestone-by-none-any.yml b/changelogs/unreleased/52059-filter-milestone-by-none-any.yml
new file mode 100644
index 00000000000..5511440c0b9
--- /dev/null
+++ b/changelogs/unreleased/52059-filter-milestone-by-none-any.yml
@@ -0,0 +1,5 @@
+---
+title: Added `Any` option to milestones filter
+merge_request: 22351
+author: Heinrich Lee Yu
+type: added
diff --git a/changelogs/unreleased/52115-Link-button-in-markdown-editor-should-recognize-URLs.yml b/changelogs/unreleased/52115-Link-button-in-markdown-editor-should-recognize-URLs.yml
new file mode 100644
index 00000000000..8521335c2ea
--- /dev/null
+++ b/changelogs/unreleased/52115-Link-button-in-markdown-editor-should-recognize-URLs.yml
@@ -0,0 +1,5 @@
+---
+title: Link button in markdown editor recognize URLs
+merge_request: 1983
+author: Johann Hubert Sonntagbauer
+type: changed
diff --git a/changelogs/unreleased/52122-fix-broken-whitespace-button.yml b/changelogs/unreleased/52122-fix-broken-whitespace-button.yml
new file mode 100644
index 00000000000..3f261eb2ac5
--- /dev/null
+++ b/changelogs/unreleased/52122-fix-broken-whitespace-button.yml
@@ -0,0 +1,5 @@
+---
+title: Fix broken "Show whitespace changes" button on MRs.
+merge_request: 22539
+author:
+type: fixed
diff --git a/changelogs/unreleased/52147-loading-state.yml b/changelogs/unreleased/52147-loading-state.yml
new file mode 100644
index 00000000000..5ae844073f7
--- /dev/null
+++ b/changelogs/unreleased/52147-loading-state.yml
@@ -0,0 +1,5 @@
+---
+title: Removes extra border from test reports in the merge request widget
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/52193-Pipeline-graph-is-not-vertically-aligned-in-commit-page.yml b/changelogs/unreleased/52193-Pipeline-graph-is-not-vertically-aligned-in-commit-page.yml
new file mode 100644
index 00000000000..2d3ac49807a
--- /dev/null
+++ b/changelogs/unreleased/52193-Pipeline-graph-is-not-vertically-aligned-in-commit-page.yml
@@ -0,0 +1,5 @@
+---
+title: Vertical align Pipeline Graph in Commit Page
+merge_request: 22173
+author: Johann Hubert Sonntagbauer
+type: fixed
diff --git a/changelogs/unreleased/52202-consider-moving-isjobstuck-verification-to-backend.yml b/changelogs/unreleased/52202-consider-moving-isjobstuck-verification-to-backend.yml
new file mode 100644
index 00000000000..0efd97d91b8
--- /dev/null
+++ b/changelogs/unreleased/52202-consider-moving-isjobstuck-verification-to-backend.yml
@@ -0,0 +1,5 @@
+---
+title: Renders stuck block when runners are stuck
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/52299-follow-up-from-resolve-add-status-message-from-within-user-menu.yml b/changelogs/unreleased/52299-follow-up-from-resolve-add-status-message-from-within-user-menu.yml
new file mode 100644
index 00000000000..8ea0693037f
--- /dev/null
+++ b/changelogs/unreleased/52299-follow-up-from-resolve-add-status-message-from-within-user-menu.yml
@@ -0,0 +1,5 @@
+---
+title: Fix size of emojis of user status in user menu
+merge_request: 22194
+author:
+type: fixed
diff --git a/changelogs/unreleased/52300-pool-repositories.yml b/changelogs/unreleased/52300-pool-repositories.yml
new file mode 100644
index 00000000000..5435f3aa21f
--- /dev/null
+++ b/changelogs/unreleased/52300-pool-repositories.yml
@@ -0,0 +1,5 @@
+---
+title: Start tracking shards and pool repositories in the database
+merge_request: 22482
+author:
+type: other
diff --git a/changelogs/unreleased/52353-keyboard-navigation-project-slug-is-not-focused-on-new-project-page.yml b/changelogs/unreleased/52353-keyboard-navigation-project-slug-is-not-focused-on-new-project-page.yml
new file mode 100644
index 00000000000..ffcc0cc08a0
--- /dev/null
+++ b/changelogs/unreleased/52353-keyboard-navigation-project-slug-is-not-focused-on-new-project-page.yml
@@ -0,0 +1,5 @@
+---
+title: Focus project slug on tab navigation
+merge_request: 22198
+author:
+type: other
diff --git a/changelogs/unreleased/52367-cleanup-web-hooks-columns.yml b/changelogs/unreleased/52367-cleanup-web-hooks-columns.yml
new file mode 100644
index 00000000000..d1f3ca83613
--- /dev/null
+++ b/changelogs/unreleased/52367-cleanup-web-hooks-columns.yml
@@ -0,0 +1,5 @@
+---
+title: Remove legacy unencrypted webhook columns from the database
+merge_request: 22199
+author:
+type: changed
diff --git a/changelogs/unreleased/52382-filter-milestone-api-none-any.yml b/changelogs/unreleased/52382-filter-milestone-api-none-any.yml
new file mode 100644
index 00000000000..a7559a25645
--- /dev/null
+++ b/changelogs/unreleased/52382-filter-milestone-api-none-any.yml
@@ -0,0 +1,5 @@
+---
+title: Standardize milestones filter in APIs to None / Any
+merge_request: 22637
+author: Heinrich Lee Yu
+type: changed
diff --git a/changelogs/unreleased/52383-ui-filter-assignee-none-any.yml b/changelogs/unreleased/52383-ui-filter-assignee-none-any.yml
new file mode 100644
index 00000000000..adf153f33ce
--- /dev/null
+++ b/changelogs/unreleased/52383-ui-filter-assignee-none-any.yml
@@ -0,0 +1,5 @@
+---
+title: Add None/Any option for assignee_id in search bar
+merge_request: 22599
+author: Heinrich Lee Yu
+type: added
diff --git a/changelogs/unreleased/52384-api-filter-assignee-none-any.yml b/changelogs/unreleased/52384-api-filter-assignee-none-any.yml
new file mode 100644
index 00000000000..9acec04d946
--- /dev/null
+++ b/changelogs/unreleased/52384-api-filter-assignee-none-any.yml
@@ -0,0 +1,5 @@
+---
+title: Add None/Any option for assignee_id in Issues and Merge Requests API
+merge_request: 22598
+author: Heinrich Lee Yu
+type: added
diff --git a/changelogs/unreleased/52385-search-bar-for-dashboard-list.yml b/changelogs/unreleased/52385-search-bar-for-dashboard-list.yml
new file mode 100644
index 00000000000..a437ae560cb
--- /dev/null
+++ b/changelogs/unreleased/52385-search-bar-for-dashboard-list.yml
@@ -0,0 +1,5 @@
+---
+title: Use search bar for filtering in dashboard issues / MRs
+merge_request: 22641
+author: Heinrich Lee Yu
+type: changed
diff --git a/changelogs/unreleased/52408-pip-cache-dir-to-cache-python-dependencies.yml b/changelogs/unreleased/52408-pip-cache-dir-to-cache-python-dependencies.yml
new file mode 100644
index 00000000000..19d3e35c15c
--- /dev/null
+++ b/changelogs/unreleased/52408-pip-cache-dir-to-cache-python-dependencies.yml
@@ -0,0 +1,5 @@
+---
+title: Use the standard PIP_CACHE_DIR for Python dependency caching template
+merge_request: 22211
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/52421-show-canary-no-canary-in-the-performance-bar.yml b/changelogs/unreleased/52421-show-canary-no-canary-in-the-performance-bar.yml
new file mode 100644
index 00000000000..20e32a2e987
--- /dev/null
+++ b/changelogs/unreleased/52421-show-canary-no-canary-in-the-performance-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Show canary status in the performance bar
+merge_request: 22222
+author:
+type: changed
diff --git a/changelogs/unreleased/52477-add-iid-headers-to-emails.yml b/changelogs/unreleased/52477-add-iid-headers-to-emails.yml
new file mode 100644
index 00000000000..c17b66c5f54
--- /dev/null
+++ b/changelogs/unreleased/52477-add-iid-headers-to-emails.yml
@@ -0,0 +1,5 @@
+---
+title: Add IID headers to E-Mail notifications
+merge_request: 22263
+author:
+type: changed
diff --git a/changelogs/unreleased/52527-harden-wiki-against-missing-last-version.yml b/changelogs/unreleased/52527-harden-wiki-against-missing-last-version.yml
new file mode 100644
index 00000000000..c7c33e47d01
--- /dev/null
+++ b/changelogs/unreleased/52527-harden-wiki-against-missing-last-version.yml
@@ -0,0 +1,5 @@
+---
+title: Fix a bug displaying certain wiki pages
+merge_request: 22377
+author:
+type: fixed
diff --git a/changelogs/unreleased/52545-guest-create-issue-in-group-board.yml b/changelogs/unreleased/52545-guest-create-issue-in-group-board.yml
new file mode 100644
index 00000000000..5701e44eb32
--- /dev/null
+++ b/changelogs/unreleased/52545-guest-create-issue-in-group-board.yml
@@ -0,0 +1,5 @@
+---
+title: Always show new issue button in boards' Open list
+merge_request: 22557
+author: Heinrich Lee Yu
+type: fixed
diff --git a/changelogs/unreleased/52548-links-in-tabs-of-the-labels-index-pages-ends-with-html.yml b/changelogs/unreleased/52548-links-in-tabs-of-the-labels-index-pages-ends-with-html.yml
new file mode 100644
index 00000000000..052ef70c41a
--- /dev/null
+++ b/changelogs/unreleased/52548-links-in-tabs-of-the-labels-index-pages-ends-with-html.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug when links in tabs of the labels index pages ends with .html
+merge_request: 22716
+author:
+type: fixed
diff --git a/changelogs/unreleased/52559-applications-api-get-delete.yml b/changelogs/unreleased/52559-applications-api-get-delete.yml
new file mode 100644
index 00000000000..19f98a2bb56
--- /dev/null
+++ b/changelogs/unreleased/52559-applications-api-get-delete.yml
@@ -0,0 +1,5 @@
+---
+title: Add Applications API endpoints for listing and deleting entries.
+merge_request: 22296
+author: Jean-Baptiste Vasseur
+type: added
diff --git a/changelogs/unreleased/52686-project-slug-does-not-auto-populate-in-ie11.yml b/changelogs/unreleased/52686-project-slug-does-not-auto-populate-in-ie11.yml
new file mode 100644
index 00000000000..5a30317babf
--- /dev/null
+++ b/changelogs/unreleased/52686-project-slug-does-not-auto-populate-in-ie11.yml
@@ -0,0 +1,5 @@
+---
+title: Use literal instead of constructor for creating regex
+merge_request: 22367
+author:
+type: other
diff --git a/changelogs/unreleased/52692-catch-redirect-loops.yml b/changelogs/unreleased/52692-catch-redirect-loops.yml
new file mode 100644
index 00000000000..655124b8fb4
--- /dev/null
+++ b/changelogs/unreleased/52692-catch-redirect-loops.yml
@@ -0,0 +1,5 @@
+---
+title: Fix 500 error when testing webhooks with redirect loops
+merge_request: 22447
+author: Heinrich Lee Yu
+type: fixed
diff --git a/changelogs/unreleased/52712-further-ui-improvements-to-profile-overview-tab.yml b/changelogs/unreleased/52712-further-ui-improvements-to-profile-overview-tab.yml
new file mode 100644
index 00000000000..65aa9323d2e
--- /dev/null
+++ b/changelogs/unreleased/52712-further-ui-improvements-to-profile-overview-tab.yml
@@ -0,0 +1,5 @@
+---
+title: UI improvements to user's profile
+merge_request: 22977
+author:
+type: other
diff --git a/changelogs/unreleased/52767-more-chaos-for-gitlab.yml b/changelogs/unreleased/52767-more-chaos-for-gitlab.yml
new file mode 100644
index 00000000000..067777cb7fa
--- /dev/null
+++ b/changelogs/unreleased/52767-more-chaos-for-gitlab.yml
@@ -0,0 +1,5 @@
+---
+title: Add endpoints for simulating certain failure modes in the application
+merge_request: 22746
+author:
+type: other
diff --git a/changelogs/unreleased/52771-ldap-users-can-t-choose-private-or-internal-when-creating-a-new-group.yml b/changelogs/unreleased/52771-ldap-users-can-t-choose-private-or-internal-when-creating-a-new-group.yml
new file mode 100644
index 00000000000..a05ef75b6a6
--- /dev/null
+++ b/changelogs/unreleased/52771-ldap-users-can-t-choose-private-or-internal-when-creating-a-new-group.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug stopping non-admin users from changing visibility level on group creation
+merge_request: 22468
+author:
+type: fixed
diff --git a/changelogs/unreleased/52772-assign-me-quick-action-doesn-t-work-if-there-is-extra-white-space.yml b/changelogs/unreleased/52772-assign-me-quick-action-doesn-t-work-if-there-is-extra-white-space.yml
new file mode 100644
index 00000000000..4451cdd78b5
--- /dev/null
+++ b/changelogs/unreleased/52772-assign-me-quick-action-doesn-t-work-if-there-is-extra-white-space.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve assign-me quick action doesn't work if there is extra white space
+merge_request: 22402
+author:
+type: fixed
diff --git a/changelogs/unreleased/52780-stale-pipeline-status-cache-for-_project-after-disabling-pipelines.yml b/changelogs/unreleased/52780-stale-pipeline-status-cache-for-_project-after-disabling-pipelines.yml
new file mode 100644
index 00000000000..7586d7995b7
--- /dev/null
+++ b/changelogs/unreleased/52780-stale-pipeline-status-cache-for-_project-after-disabling-pipelines.yml
@@ -0,0 +1,5 @@
+---
+title: Cache pipeline status per SHA.
+merge_request: 22589
+author:
+type: fixed
diff --git a/changelogs/unreleased/52840-fix-runners-details-page.yml b/changelogs/unreleased/52840-fix-runners-details-page.yml
new file mode 100644
index 00000000000..b061390fcf0
--- /dev/null
+++ b/changelogs/unreleased/52840-fix-runners-details-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix rendering of 'Protected' value on Runner details page
+merge_request: 22459
+author:
+type: fixed
diff --git a/changelogs/unreleased/52886-fix-broken-master.yml b/changelogs/unreleased/52886-fix-broken-master.yml
new file mode 100644
index 00000000000..c6488c83e3b
--- /dev/null
+++ b/changelogs/unreleased/52886-fix-broken-master.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes broken test in master
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/52925-scheduled-pipelines-ui-problems.yml b/changelogs/unreleased/52925-scheduled-pipelines-ui-problems.yml
new file mode 100644
index 00000000000..792b24d75ac
--- /dev/null
+++ b/changelogs/unreleased/52925-scheduled-pipelines-ui-problems.yml
@@ -0,0 +1,5 @@
+---
+title: Fixing styling issues on the scheduled pipelines page
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/52940-fix-internal-email-pattern-not-respected.yml b/changelogs/unreleased/52940-fix-internal-email-pattern-not-respected.yml
new file mode 100644
index 00000000000..98e15a5cc0a
--- /dev/null
+++ b/changelogs/unreleased/52940-fix-internal-email-pattern-not-respected.yml
@@ -0,0 +1,5 @@
+---
+title: Fix a bug where internal email pattern wasn't respected
+merge_request: 22516
+author:
+type: fixed
diff --git a/changelogs/unreleased/52993-ldap-rename_provider-rake-task-broken.yml b/changelogs/unreleased/52993-ldap-rename_provider-rake-task-broken.yml
new file mode 100644
index 00000000000..ca78f9a392e
--- /dev/null
+++ b/changelogs/unreleased/52993-ldap-rename_provider-rake-task-broken.yml
@@ -0,0 +1,5 @@
+---
+title: Use gitlab_environment for ldap rake task
+merge_request: 22582
+author:
+type: fixed
diff --git a/changelogs/unreleased/53013-duplicate-escape.yml b/changelogs/unreleased/53013-duplicate-escape.yml
new file mode 100644
index 00000000000..c5ec2322fb5
--- /dev/null
+++ b/changelogs/unreleased/53013-duplicate-escape.yml
@@ -0,0 +1,5 @@
+---
+title: Remove duplicate escape in job sidebar
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/53023-endless-scroll-loader-is-visible-on-user-profile-overview-page.yml b/changelogs/unreleased/53023-endless-scroll-loader-is-visible-on-user-profile-overview-page.yml
new file mode 100644
index 00000000000..0377e10fe9e
--- /dev/null
+++ b/changelogs/unreleased/53023-endless-scroll-loader-is-visible-on-user-profile-overview-page.yml
@@ -0,0 +1,4 @@
+title: Adds container to pager to enable scoping
+merge_request: 22529
+? author
+type: other
diff --git a/changelogs/unreleased/53052-mg-fix-broken-ie11.yml b/changelogs/unreleased/53052-mg-fix-broken-ie11.yml
new file mode 100644
index 00000000000..c616efffa6b
--- /dev/null
+++ b/changelogs/unreleased/53052-mg-fix-broken-ie11.yml
@@ -0,0 +1,5 @@
+---
+title: Fix incompatibility with IE11 due to non-transpiled gitlab-ui components
+merge_request: 22695
+author:
+type: fixed
diff --git a/changelogs/unreleased/53055-combine-date-util-functions.yml b/changelogs/unreleased/53055-combine-date-util-functions.yml
new file mode 100644
index 00000000000..56d4406f1bf
--- /dev/null
+++ b/changelogs/unreleased/53055-combine-date-util-functions.yml
@@ -0,0 +1,5 @@
+---
+title: Combine all datetime library functions into 'datetime_utility.js'
+merge_request: 22570
+author:
+type: other
diff --git a/changelogs/unreleased/53133-jobs-list.yml b/changelogs/unreleased/53133-jobs-list.yml
new file mode 100644
index 00000000000..2e13edc0e76
--- /dev/null
+++ b/changelogs/unreleased/53133-jobs-list.yml
@@ -0,0 +1,5 @@
+---
+title: Fix stage dropdown not rendering in different languages
+merge_request: 22604
+author:
+type: other
diff --git a/changelogs/unreleased/53155-structured-logs-params-array.yml b/changelogs/unreleased/53155-structured-logs-params-array.yml
new file mode 100644
index 00000000000..4d4f68a5c84
--- /dev/null
+++ b/changelogs/unreleased/53155-structured-logs-params-array.yml
@@ -0,0 +1,5 @@
+---
+title: Use key-value pair arrays for API query parameter logging instead of hashes
+merge_request: 22623
+author:
+type: other
diff --git a/changelogs/unreleased/53227-empty-list.yml b/changelogs/unreleased/53227-empty-list.yml
new file mode 100644
index 00000000000..8b222145299
--- /dev/null
+++ b/changelogs/unreleased/53227-empty-list.yml
@@ -0,0 +1,6 @@
+---
+title: Only renders dropdown for review app changes when we have a list of files to
+ show. Otherwise will render the regular review app button
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/53230-remove_personal_access_tokens_finder_find_by_method.yml b/changelogs/unreleased/53230-remove_personal_access_tokens_finder_find_by_method.yml
new file mode 100644
index 00000000000..d4d78a2fd06
--- /dev/null
+++ b/changelogs/unreleased/53230-remove_personal_access_tokens_finder_find_by_method.yml
@@ -0,0 +1,5 @@
+---
+title: Remove PersonalAccessTokensFinder#find_by method
+merge_request: 22617
+author:
+type: fixed
diff --git a/changelogs/unreleased/53270-remove-mousetrap-rails.yml b/changelogs/unreleased/53270-remove-mousetrap-rails.yml
new file mode 100644
index 00000000000..7214c81d73d
--- /dev/null
+++ b/changelogs/unreleased/53270-remove-mousetrap-rails.yml
@@ -0,0 +1,5 @@
+---
+title: Remove mousetrap-rails gem
+merge_request: 22647
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/53273-update-moment-to-2-22-2.yml b/changelogs/unreleased/53273-update-moment-to-2-22-2.yml
new file mode 100644
index 00000000000..a6b40466927
--- /dev/null
+++ b/changelogs/unreleased/53273-update-moment-to-2-22-2.yml
@@ -0,0 +1,5 @@
+---
+title: Update moment to 2.22.2
+merge_request: 22648
+author: Takuya Noguchi
+type: security
diff --git a/changelogs/unreleased/53289-update-haml_lint-to-0-28-0.yml b/changelogs/unreleased/53289-update-haml_lint-to-0-28-0.yml
new file mode 100644
index 00000000000..9a16666c416
--- /dev/null
+++ b/changelogs/unreleased/53289-update-haml_lint-to-0-28-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update haml_lint to 0.28.0
+merge_request: 22660
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/53290-incorrect-project-list-order-select-default-label.yml b/changelogs/unreleased/53290-incorrect-project-list-order-select-default-label.yml
new file mode 100644
index 00000000000..d076352a27b
--- /dev/null
+++ b/changelogs/unreleased/53290-incorrect-project-list-order-select-default-label.yml
@@ -0,0 +1,5 @@
+---
+title: Fix default sorting for subgroups and projects list
+merge_request: 23058
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/53291-update-ffaker-to-2-10-0.yml b/changelogs/unreleased/53291-update-ffaker-to-2-10-0.yml
new file mode 100644
index 00000000000..a1b95df5e32
--- /dev/null
+++ b/changelogs/unreleased/53291-update-ffaker-to-2-10-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update ffaker to 2.10.0
+merge_request: 22661
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/53362-allow-concurrency-in-puma.yml b/changelogs/unreleased/53362-allow-concurrency-in-puma.yml
new file mode 100644
index 00000000000..5fbda0161c1
--- /dev/null
+++ b/changelogs/unreleased/53362-allow-concurrency-in-puma.yml
@@ -0,0 +1,5 @@
+---
+title: Allow Rails concurrency when running in Puma
+merge_request: 22751
+author:
+type: performance
diff --git a/changelogs/unreleased/53450-wrong-value-for-kubernetes_version-variable.yml b/changelogs/unreleased/53450-wrong-value-for-kubernetes_version-variable.yml
new file mode 100644
index 00000000000..cd9300ca2d1
--- /dev/null
+++ b/changelogs/unreleased/53450-wrong-value-for-kubernetes_version-variable.yml
@@ -0,0 +1,5 @@
+---
+title: Bump KUBERNETES_VERSION for Auto DevOps to latest 1.10 series
+merge_request: 22757
+author:
+type: other
diff --git a/changelogs/unreleased/53533-fix-broken-link.yml b/changelogs/unreleased/53533-fix-broken-link.yml
new file mode 100644
index 00000000000..6d55c75d82e
--- /dev/null
+++ b/changelogs/unreleased/53533-fix-broken-link.yml
@@ -0,0 +1,5 @@
+---
+title: Render unescaped link for failed pipeline status
+merge_request: 22807
+author:
+type: fixed
diff --git a/changelogs/unreleased/53535-sticky-archived.yml b/changelogs/unreleased/53535-sticky-archived.yml
new file mode 100644
index 00000000000..8d452d84871
--- /dev/null
+++ b/changelogs/unreleased/53535-sticky-archived.yml
@@ -0,0 +1,5 @@
+---
+title: Renders warning info when job is archieved
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/53626-update-config-map-on-install-retry.yml b/changelogs/unreleased/53626-update-config-map-on-install-retry.yml
new file mode 100644
index 00000000000..38e79c06c89
--- /dev/null
+++ b/changelogs/unreleased/53626-update-config-map-on-install-retry.yml
@@ -0,0 +1,5 @@
+---
+title: Update config map for gitlab managed application if already present on install
+merge_request: 22969
+author:
+type: other
diff --git a/changelogs/unreleased/53636-fix-rendering-of-any-user-filter.yml b/changelogs/unreleased/53636-fix-rendering-of-any-user-filter.yml
new file mode 100644
index 00000000000..a59a276a334
--- /dev/null
+++ b/changelogs/unreleased/53636-fix-rendering-of-any-user-filter.yml
@@ -0,0 +1,5 @@
+---
+title: Fix rendering of filter bar tokens for special values
+merge_request: 22865
+author: Heinrich Lee Yu
+type: fixed
diff --git a/changelogs/unreleased/53640-follow-up-from-resolve-redesign-activity-feed.yml b/changelogs/unreleased/53640-follow-up-from-resolve-redesign-activity-feed.yml
new file mode 100644
index 00000000000..66301329c52
--- /dev/null
+++ b/changelogs/unreleased/53640-follow-up-from-resolve-redesign-activity-feed.yml
@@ -0,0 +1,4 @@
+title: Adds new icon size to Vue icon component
+merge_request: 22899
+author:
+type: other
diff --git a/changelogs/unreleased/53700-hashed-storagemigration.yml b/changelogs/unreleased/53700-hashed-storagemigration.yml
new file mode 100644
index 00000000000..899012ffd22
--- /dev/null
+++ b/changelogs/unreleased/53700-hashed-storagemigration.yml
@@ -0,0 +1,5 @@
+---
+title: 'Hashed Storage: allow migration to be retried in partially migrated projects'
+merge_request: 23087
+author:
+type: fixed
diff --git a/changelogs/unreleased/53816-empty-label-menu-if-not-logged-in.yml b/changelogs/unreleased/53816-empty-label-menu-if-not-logged-in.yml
new file mode 100644
index 00000000000..a9ca56303eb
--- /dev/null
+++ b/changelogs/unreleased/53816-empty-label-menu-if-not-logged-in.yml
@@ -0,0 +1,5 @@
+---
+title: Removes promote to group label for anonymous user
+merge_request: 23042
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/53879-kube-token-nil.yml b/changelogs/unreleased/53879-kube-token-nil.yml
new file mode 100644
index 00000000000..61a0db15d84
--- /dev/null
+++ b/changelogs/unreleased/53879-kube-token-nil.yml
@@ -0,0 +1,5 @@
+---
+title: Fix deployment jobs using nil KUBE_TOKEN due to migration issue
+merge_request: 23009
+author:
+type: fixed
diff --git a/changelogs/unreleased/53888-missing-favicon.yml b/changelogs/unreleased/53888-missing-favicon.yml
new file mode 100644
index 00000000000..ba6f26c6b9f
--- /dev/null
+++ b/changelogs/unreleased/53888-missing-favicon.yml
@@ -0,0 +1,5 @@
+---
+title: Adds CI favicon back to jobs page
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/53972-fix-fill-shards.yml b/changelogs/unreleased/53972-fix-fill-shards.yml
new file mode 100644
index 00000000000..ca94d6cc589
--- /dev/null
+++ b/changelogs/unreleased/53972-fix-fill-shards.yml
@@ -0,0 +1,5 @@
+---
+title: Fix a race condition intermittently breaking GitLab startup
+merge_request: 23028
+author:
+type: fixed
diff --git a/changelogs/unreleased/54002-activity-feed-missing-padding-in-event-note-when-a-branch-is-deleted.yml b/changelogs/unreleased/54002-activity-feed-missing-padding-in-event-note-when-a-branch-is-deleted.yml
new file mode 100644
index 00000000000..9f4f104a12c
--- /dev/null
+++ b/changelogs/unreleased/54002-activity-feed-missing-padding-in-event-note-when-a-branch-is-deleted.yml
@@ -0,0 +1,5 @@
+---
+title: Adds margin after a deleted branch name in the activity feed.
+merge_request: 23038
+author:
+type: fixed
diff --git a/changelogs/unreleased/54004-update-asana-to-0-8-1.yml b/changelogs/unreleased/54004-update-asana-to-0-8-1.yml
new file mode 100644
index 00000000000..a47b4f3c4d9
--- /dev/null
+++ b/changelogs/unreleased/54004-update-asana-to-0-8-1.yml
@@ -0,0 +1,5 @@
+---
+title: Update asana to 0.8.1
+merge_request: 23039
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/54010-update-asciidoctor-to-1-5-8.yml b/changelogs/unreleased/54010-update-asciidoctor-to-1-5-8.yml
new file mode 100644
index 00000000000..f0b0aa0ee1c
--- /dev/null
+++ b/changelogs/unreleased/54010-update-asciidoctor-to-1-5-8.yml
@@ -0,0 +1,5 @@
+---
+title: Update asciidoctor to 1.5.8
+merge_request: 23047
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/54015-Markdown-Editor-improve-Cursor-placement.yml b/changelogs/unreleased/54015-Markdown-Editor-improve-Cursor-placement.yml
new file mode 100644
index 00000000000..28e3fae01a9
--- /dev/null
+++ b/changelogs/unreleased/54015-Markdown-Editor-improve-Cursor-placement.yml
@@ -0,0 +1,5 @@
+---
+title: Refine cursor positioning in Markdown Editor for wrap tags
+merge_request: 23085
+author: Johann Hubert Sonntagbauer
+type: changed
diff --git a/changelogs/unreleased/54021-empty-button.yml b/changelogs/unreleased/54021-empty-button.yml
new file mode 100644
index 00000000000..3b03665cf95
--- /dev/null
+++ b/changelogs/unreleased/54021-empty-button.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent empty button being rendered in empty state
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/54093-the-default_value_for-gem-doesn-t-handle-actioncontroller-parameters-correctly.yml b/changelogs/unreleased/54093-the-default_value_for-gem-doesn-t-handle-actioncontroller-parameters-correctly.yml
new file mode 100644
index 00000000000..3d6fd2d065a
--- /dev/null
+++ b/changelogs/unreleased/54093-the-default_value_for-gem-doesn-t-handle-actioncontroller-parameters-correctly.yml
@@ -0,0 +1,7 @@
+---
+title: Fixes an issue where default values from models would override values set in
+ the interface (e.g. users would be set to external even though their emails matches
+ the internal email address pattern)
+merge_request: 23114
+author:
+type: fixed
diff --git a/changelogs/unreleased/6500-fix-misaligned-approvers-dropdown.yml b/changelogs/unreleased/6500-fix-misaligned-approvers-dropdown.yml
new file mode 100644
index 00000000000..3e87c5875c6
--- /dev/null
+++ b/changelogs/unreleased/6500-fix-misaligned-approvers-dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Fix misaligned approvers dropdown
+merge_request: 22832
+author:
+type: fixed
diff --git a/changelogs/unreleased/6860-FE-instance-level-project-templates.yml b/changelogs/unreleased/6860-FE-instance-level-project-templates.yml
deleted file mode 100644
index 74e0daee71b..00000000000
--- a/changelogs/unreleased/6860-FE-instance-level-project-templates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update design of project templates
-merge_request: 21012
-author:
-type: changed
diff --git a/changelogs/unreleased/7737-ci-pipeline-view-slowed-down-massivly-if-security-tabs-has-many-entries-ee.yml b/changelogs/unreleased/7737-ci-pipeline-view-slowed-down-massivly-if-security-tabs-has-many-entries-ee.yml
new file mode 100644
index 00000000000..aaae8feb220
--- /dev/null
+++ b/changelogs/unreleased/7737-ci-pipeline-view-slowed-down-massivly-if-security-tabs-has-many-entries-ee.yml
@@ -0,0 +1,5 @@
+---
+title: Improve performance of rendering large reports
+merge_request: 22835
+author:
+type: performance
diff --git a/changelogs/unreleased/Fix-pipeline-redirect.yml b/changelogs/unreleased/Fix-pipeline-redirect.yml
new file mode 100644
index 00000000000..459273c7740
--- /dev/null
+++ b/changelogs/unreleased/Fix-pipeline-redirect.yml
@@ -0,0 +1,5 @@
+---
+title: Redirect to the pipeline builds page when a build is canceled
+merge_request:
+author: Eva Kadlecova
+type: fixed
diff --git a/changelogs/unreleased/ab-45608-stuck-mr-query.yml b/changelogs/unreleased/ab-45608-stuck-mr-query.yml
new file mode 100644
index 00000000000..3b64534e480
--- /dev/null
+++ b/changelogs/unreleased/ab-45608-stuck-mr-query.yml
@@ -0,0 +1,5 @@
+---
+title: Add index to find stuck merge requests.
+merge_request: 22749
+author:
+type: performance
diff --git a/changelogs/unreleased/ac-post-merge-pipeline.yml b/changelogs/unreleased/ac-post-merge-pipeline.yml
new file mode 100644
index 00000000000..08322c9cb8a
--- /dev/null
+++ b/changelogs/unreleased/ac-post-merge-pipeline.yml
@@ -0,0 +1,5 @@
+---
+title: Show post-merge pipeline in merge request page
+merge_request: 22292
+author:
+type: added
diff --git a/changelogs/unreleased/accept-rf3-2822-compliant-addresses.yml b/changelogs/unreleased/accept-rf3-2822-compliant-addresses.yml
deleted file mode 100644
index 97d017613ba..00000000000
--- a/changelogs/unreleased/accept-rf3-2822-compliant-addresses.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Emails on push recipients now accepts formats like John Doe <johndoe@example.com>
-merge_request:
-author: George Thomas
-type: added
diff --git a/changelogs/unreleased/add-action-to-deployment.yml b/changelogs/unreleased/add-action-to-deployment.yml
new file mode 100644
index 00000000000..4629f762ae8
--- /dev/null
+++ b/changelogs/unreleased/add-action-to-deployment.yml
@@ -0,0 +1,5 @@
+---
+title: Fix environment status in merge request widget
+merge_request: 22799
+author:
+type: changed
diff --git a/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml b/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml
deleted file mode 100644
index 08376014ad7..00000000000
--- a/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for daylight savings time to pipleline schedules
-merge_request: 20145
-author:
-type: fixed
diff --git a/changelogs/unreleased/add-failure-reason-for-execution-timeout.yml b/changelogs/unreleased/add-failure-reason-for-execution-timeout.yml
new file mode 100644
index 00000000000..c8488cbf200
--- /dev/null
+++ b/changelogs/unreleased/add-failure-reason-for-execution-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Add failure reason for execution timeout
+merge_request: 22224
+author:
+type: changed
diff --git a/changelogs/unreleased/add-gl-link-to-markdown-header.yml b/changelogs/unreleased/add-gl-link-to-markdown-header.yml
new file mode 100644
index 00000000000..b8fe66ab52a
--- /dev/null
+++ b/changelogs/unreleased/add-gl-link-to-markdown-header.yml
@@ -0,0 +1,5 @@
+---
+title: Change markdown header tab anchor links to buttons
+merge_request: 21988
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/add-homepage-link-to-status-pages.yml b/changelogs/unreleased/add-homepage-link-to-status-pages.yml
deleted file mode 100644
index 0e7375f2061..00000000000
--- a/changelogs/unreleased/add-homepage-link-to-status-pages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add link to homepage on static http status pages (404, 500, etc)
-merge_request: 20898
-author: Jason Funk
-type: added
diff --git a/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml b/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml
deleted file mode 100644
index 4f9a551d13e..00000000000
--- a/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add merge request header branch actions left margin
-merge_request: 20643
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/add-new-kubernetes-spec-helpers.yml b/changelogs/unreleased/add-new-kubernetes-spec-helpers.yml
new file mode 100644
index 00000000000..87023ede020
--- /dev/null
+++ b/changelogs/unreleased/add-new-kubernetes-spec-helpers.yml
@@ -0,0 +1,5 @@
+---
+title: Introduce new kubernetes helpers
+merge_request: 22525
+author:
+type: other
diff --git a/changelogs/unreleased/add-role-binding-to-kubeclient.yml b/changelogs/unreleased/add-role-binding-to-kubeclient.yml
new file mode 100644
index 00000000000..bc343116eb4
--- /dev/null
+++ b/changelogs/unreleased/add-role-binding-to-kubeclient.yml
@@ -0,0 +1,5 @@
+---
+title: Allow kubeclient to call RoleBinding methods
+merge_request: 22524
+author:
+type: other
diff --git a/changelogs/unreleased/add-scheduled-flag-to-job-entity.yml b/changelogs/unreleased/add-scheduled-flag-to-job-entity.yml
new file mode 100644
index 00000000000..a80b5a931b9
--- /dev/null
+++ b/changelogs/unreleased/add-scheduled-flag-to-job-entity.yml
@@ -0,0 +1,5 @@
+---
+title: Add scheduled flag to job entity
+merge_request: 22710
+author:
+type: other
diff --git a/changelogs/unreleased/add-total-time-flat-printer-for-profiling.yml b/changelogs/unreleased/add-total-time-flat-printer-for-profiling.yml
deleted file mode 100644
index 37a4e31896e..00000000000
--- a/changelogs/unreleased/add-total-time-flat-printer-for-profiling.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Add a Gitlab::Profiler.print_by_total_time convenience method for profiling
- from a Rails console
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/add_google_noto_color_emoji_font.yml b/changelogs/unreleased/add_google_noto_color_emoji_font.yml
deleted file mode 100644
index 9ba46262767..00000000000
--- a/changelogs/unreleased/add_google_noto_color_emoji_font.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Noto Color Emoji font support
-merge_request: 19036
-author: Alexander Popov
-type: changed
diff --git a/changelogs/unreleased/added-glob-for-ci-changes-detection.yml b/changelogs/unreleased/added-glob-for-ci-changes-detection.yml
new file mode 100644
index 00000000000..887c6ef0346
--- /dev/null
+++ b/changelogs/unreleased/added-glob-for-ci-changes-detection.yml
@@ -0,0 +1,5 @@
+---
+title: Added glob for CI changes detection
+merge_request: 23128
+author: Kirill Zaitsev
+type: added
diff --git a/changelogs/unreleased/an-gitaly-version-0-133-0.yml b/changelogs/unreleased/an-gitaly-version-0-133-0.yml
new file mode 100644
index 00000000000..4f3943ceacb
--- /dev/null
+++ b/changelogs/unreleased/an-gitaly-version-0-133-0.yml
@@ -0,0 +1,5 @@
+---
+title: Updated Gitaly to v0.133.0
+merge_request: 23148
+author:
+type: other
diff --git a/changelogs/unreleased/an-multithreading.yml b/changelogs/unreleased/an-multithreading.yml
new file mode 100644
index 00000000000..fca847e6ea4
--- /dev/null
+++ b/changelogs/unreleased/an-multithreading.yml
@@ -0,0 +1,5 @@
+---
+title: Experimental support for running Puma multithreaded web-server
+merge_request: 22372
+author:
+type: performance
diff --git a/changelogs/unreleased/api-minimal-access-level.yml b/changelogs/unreleased/api-minimal-access-level.yml
deleted file mode 100644
index 43cab246d69..00000000000
--- a/changelogs/unreleased/api-minimal-access-level.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add filter for minimal access level in groups and projects API
-merge_request: 20478
-author: Marko, Peter
-type: added
diff --git a/changelogs/unreleased/artifact-format-v2-with-parser.yml b/changelogs/unreleased/artifact-format-v2-with-parser.yml
deleted file mode 100644
index e1a779cf6dd..00000000000
--- a/changelogs/unreleased/artifact-format-v2-with-parser.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: JUnit XML Test Summary In MR widget
-merge_request: 20576
-author:
-type: added
diff --git a/changelogs/unreleased/artifact-format-v2.yml b/changelogs/unreleased/artifact-format-v2.yml
deleted file mode 100644
index e264e0a9fa1..00000000000
--- a/changelogs/unreleased/artifact-format-v2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extend gitlab-ci.yml to request junit.xml test reports
-merge_request: 20390
-author:
-type: added
diff --git a/changelogs/unreleased/ashmckenzie-hmac-token-decode-and-tests.yml b/changelogs/unreleased/ashmckenzie-hmac-token-decode-and-tests.yml
new file mode 100644
index 00000000000..d15c5654d99
--- /dev/null
+++ b/changelogs/unreleased/ashmckenzie-hmac-token-decode-and-tests.yml
@@ -0,0 +1,5 @@
+---
+title: Relocate JSONWebToken::HMACToken from EE
+merge_request: 22906
+author:
+type: changed
diff --git a/changelogs/unreleased/auto-devops-support-for-group-security-dashboard.yml b/changelogs/unreleased/auto-devops-support-for-group-security-dashboard.yml
new file mode 100644
index 00000000000..7fb11f24902
--- /dev/null
+++ b/changelogs/unreleased/auto-devops-support-for-group-security-dashboard.yml
@@ -0,0 +1,5 @@
+---
+title: Auto DevOps support for Group Security Dashboard
+merge_request: 23165
+author:
+type: fixed
diff --git a/changelogs/unreleased/auto_devops_kubernetes_active.yml b/changelogs/unreleased/auto_devops_kubernetes_active.yml
new file mode 100644
index 00000000000..310d37128c9
--- /dev/null
+++ b/changelogs/unreleased/auto_devops_kubernetes_active.yml
@@ -0,0 +1,5 @@
+---
+title: Switch kubernetes:active with checking in Auto-DevOps.gitlab-ci.yml
+merge_request: 22929
+author:
+type: fixed
diff --git a/changelogs/unreleased/avoid-lock-when-introduce-new-failure-reason.yml b/changelogs/unreleased/avoid-lock-when-introduce-new-failure-reason.yml
new file mode 100644
index 00000000000..30b9ae032d4
--- /dev/null
+++ b/changelogs/unreleased/avoid-lock-when-introduce-new-failure-reason.yml
@@ -0,0 +1,5 @@
+---
+title: Support backward compatibility when introduce new failure reason
+merge_request: 22566
+author:
+type: changed
diff --git a/changelogs/unreleased/blackst0ne-add-discord-service.yml b/changelogs/unreleased/blackst0ne-add-discord-service.yml
new file mode 100644
index 00000000000..85dedf6d81f
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-add-discord-service.yml
@@ -0,0 +1,5 @@
+---
+title: Add Discord integration
+merge_request: 22684
+author: "@blackst0ne"
+type: added
diff --git a/changelogs/unreleased/blackst0ne-bump-mermaid.yml b/changelogs/unreleased/blackst0ne-bump-mermaid.yml
new file mode 100644
index 00000000000..cb924ac8448
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-bump-mermaid.yml
@@ -0,0 +1,5 @@
+---
+title: Bump mermaid to 8.0.0-rc.8
+merge_request: 22509
+author: "@blackst0ne"
+type: changed
diff --git a/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
deleted file mode 100644
index 69e6b7d815a..00000000000
--- a/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace 'Sidekiq::Testing.inline!' with 'perform_enqueued_jobs'
-merge_request: 20768
-author: "@blackst0ne"
-type: other
diff --git a/changelogs/unreleased/bvl-graphql-wip-mutation.yml b/changelogs/unreleased/bvl-graphql-wip-mutation.yml
deleted file mode 100644
index 00aa1c48677..00000000000
--- a/changelogs/unreleased/bvl-graphql-wip-mutation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add the first mutations for merge requests to GraphQL
-merge_request: 20443
-author:
-type: added
diff --git a/changelogs/unreleased/bvl-merge-base-api.yml b/changelogs/unreleased/bvl-merge-base-api.yml
deleted file mode 100644
index 78fb3ce0897..00000000000
--- a/changelogs/unreleased/bvl-merge-base-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Get the merge base of two refs through the API
-merge_request: 20929
-author:
-type: added
diff --git a/changelogs/unreleased/bvl-merge-base-multiple-revisions.yml b/changelogs/unreleased/bvl-merge-base-multiple-revisions.yml
new file mode 100644
index 00000000000..4075e35fce9
--- /dev/null
+++ b/changelogs/unreleased/bvl-merge-base-multiple-revisions.yml
@@ -0,0 +1,5 @@
+---
+title: Allow finding the common ancestor for multiple revisions through the API
+merge_request: 22295
+author:
+type: changed
diff --git a/changelogs/unreleased/bvl-patches-via-mail.yml b/changelogs/unreleased/bvl-patches-via-mail.yml
new file mode 100644
index 00000000000..6fd9e6a956c
--- /dev/null
+++ b/changelogs/unreleased/bvl-patches-via-mail.yml
@@ -0,0 +1,5 @@
+---
+title: Allow adding patches when creating a merge request via email
+merge_request: 22723
+author: Serdar Dogruyol
+type: added
diff --git a/changelogs/unreleased/bvl-preload-user-status-for-events.yml b/changelogs/unreleased/bvl-preload-user-status-for-events.yml
new file mode 100644
index 00000000000..e13b19b19c1
--- /dev/null
+++ b/changelogs/unreleased/bvl-preload-user-status-for-events.yml
@@ -0,0 +1,5 @@
+---
+title: Show user status for label events in system notes
+merge_request: 22609
+author:
+type: fixed
diff --git a/changelogs/unreleased/bvl-user-status-message-35463.yml b/changelogs/unreleased/bvl-user-status-message-35463.yml
deleted file mode 100644
index c844e7ea0e4..00000000000
--- a/changelogs/unreleased/bvl-user-status-message-35463.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Users can set a status message and emoji
-merge_request: 20614
-author: niedermyer & davamr
-type: added
diff --git a/changelogs/unreleased/bw-automatically-navigate-to-last-board-visited.yml b/changelogs/unreleased/bw-automatically-navigate-to-last-board-visited.yml
new file mode 100644
index 00000000000..836b9aa21c5
--- /dev/null
+++ b/changelogs/unreleased/bw-automatically-navigate-to-last-board-visited.yml
@@ -0,0 +1,5 @@
+---
+title: Automatically navigate to last board visited
+merge_request: 22430
+author:
+type: changed
diff --git a/changelogs/unreleased/ccr-43034_issues_controller_100_queries.yml b/changelogs/unreleased/ccr-43034_issues_controller_100_queries.yml
new file mode 100644
index 00000000000..d92c0e74c07
--- /dev/null
+++ b/changelogs/unreleased/ccr-43034_issues_controller_100_queries.yml
@@ -0,0 +1,5 @@
+---
+title: Add preload for routes and namespaces for issues controller.
+merge_request: 21651
+author:
+type: performance
diff --git a/changelogs/unreleased/ccr-51052_keep_labels_on_issue.yml b/changelogs/unreleased/ccr-51052_keep_labels_on_issue.yml
new file mode 100644
index 00000000000..7ef857d38ed
--- /dev/null
+++ b/changelogs/unreleased/ccr-51052_keep_labels_on_issue.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed label removal from issue
+merge_request: 22762
+author:
+type: fixed
diff --git a/changelogs/unreleased/ccr-51520_change_milestone_email.yml b/changelogs/unreleased/ccr-51520_change_milestone_email.yml
new file mode 100644
index 00000000000..ce4beba2c5f
--- /dev/null
+++ b/changelogs/unreleased/ccr-51520_change_milestone_email.yml
@@ -0,0 +1,5 @@
+---
+title: Add email for milestone change
+merge_request: 22279
+author:
+type: added
diff --git a/changelogs/unreleased/ce-53347_fix_impersonation_tokens.yml b/changelogs/unreleased/ce-53347_fix_impersonation_tokens.yml
new file mode 100644
index 00000000000..6cc743d6f3a
--- /dev/null
+++ b/changelogs/unreleased/ce-53347_fix_impersonation_tokens.yml
@@ -0,0 +1,5 @@
+---
+title: Display impersonation token value only after creation
+merge_request: 22916
+author:
+type: fixed
diff --git a/changelogs/unreleased/ce-5666-backport.yml b/changelogs/unreleased/ce-5666-backport.yml
deleted file mode 100644
index 344f1a1983f..00000000000
--- a/changelogs/unreleased/ce-5666-backport.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: CE port of "List groups with developer maintainer access on project creation"
-merge_request: 21051
-author:
-type: other
diff --git a/changelogs/unreleased/ce-5666-optimize_querying_manageable_groups.yml b/changelogs/unreleased/ce-5666-optimize_querying_manageable_groups.yml
deleted file mode 100644
index 0c6a1071cdd..00000000000
--- a/changelogs/unreleased/ce-5666-optimize_querying_manageable_groups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Optimize querying User#manageable_groups
-merge_request: 21050
-author:
-type: performance
diff --git a/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml b/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml
deleted file mode 100644
index b76437a8773..00000000000
--- a/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Tracking the number of repositories and wikis with a cached counter for site-wide statistics
-merge_request: 20413
-author:
-type: performance
diff --git a/changelogs/unreleased/change-branch-font-type-in-tag-creation.yml b/changelogs/unreleased/change-branch-font-type-in-tag-creation.yml
new file mode 100644
index 00000000000..0f46efb693f
--- /dev/null
+++ b/changelogs/unreleased/change-branch-font-type-in-tag-creation.yml
@@ -0,0 +1,5 @@
+---
+title: Change branch font type in tag creation
+merge_request: 22454
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/ci-builds-status-index.yml b/changelogs/unreleased/ci-builds-status-index.yml
deleted file mode 100644
index 8b7252f09e5..00000000000
--- a/changelogs/unreleased/ci-builds-status-index.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove redundant ci_builds (status) index
-merge_request: 21070
-author:
-type: performance
diff --git a/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml b/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml
deleted file mode 100644
index 49648cdfcfc..00000000000
--- a/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Close revert and cherry pick modal on escape keypress
-merge_request: 20341
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/cr-add-group-milestone-to-dashboard.yml b/changelogs/unreleased/cr-add-group-milestone-to-dashboard.yml
deleted file mode 100644
index b87a1e5faf7..00000000000
--- a/changelogs/unreleased/cr-add-group-milestone-to-dashboard.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds the ability to view group milestones on the dashboard milestone page.
-merge_request: 20618
-author:
-type: fixed
diff --git a/changelogs/unreleased/cr-add-path-of-group-milestone.yml b/changelogs/unreleased/cr-add-path-of-group-milestone.yml
deleted file mode 100644
index 5ce240110ef..00000000000
--- a/changelogs/unreleased/cr-add-path-of-group-milestone.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds the project and group name to the return type for project and group milestones.
-merge_request: 20890
-author:
-type: changed
diff --git a/changelogs/unreleased/custom_wiki_sidebar.yml b/changelogs/unreleased/custom_wiki_sidebar.yml
deleted file mode 100644
index 988fccc929c..00000000000
--- a/changelogs/unreleased/custom_wiki_sidebar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Custom Wiki Sidebar Support Issue 14995"
-merge_request:
-author: Josh Sooter
-type: added
diff --git a/changelogs/unreleased/da-fix-does-not-import-projects-over-ssh.yml b/changelogs/unreleased/da-fix-does-not-import-projects-over-ssh.yml
new file mode 100644
index 00000000000..5867b1f0981
--- /dev/null
+++ b/changelogs/unreleased/da-fix-does-not-import-projects-over-ssh.yml
@@ -0,0 +1,5 @@
+---
+title: Does not allow a SSH URI when importing new projects
+merge_request: 22309
+author:
+type: fixed
diff --git a/changelogs/unreleased/diff-expand-all-button.yml b/changelogs/unreleased/diff-expand-all-button.yml
new file mode 100644
index 00000000000..77600e726d5
--- /dev/null
+++ b/changelogs/unreleased/diff-expand-all-button.yml
@@ -0,0 +1,5 @@
+---
+title: Show expand all diffs button when a single diff file is collapsed
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/diff-stats-perf-bar.yml b/changelogs/unreleased/diff-stats-perf-bar.yml
new file mode 100644
index 00000000000..52d70d4537f
--- /dev/null
+++ b/changelogs/unreleased/diff-stats-perf-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed diff stats not showing when performance bar is enabled
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/disallow-retry-of-old-builds.yml b/changelogs/unreleased/disallow-retry-of-old-builds.yml
new file mode 100644
index 00000000000..03992fc0213
--- /dev/null
+++ b/changelogs/unreleased/disallow-retry-of-old-builds.yml
@@ -0,0 +1,5 @@
+---
+title: Soft-archive old jobs
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/discussion-perf-improvement.yml b/changelogs/unreleased/discussion-perf-improvement.yml
new file mode 100644
index 00000000000..defff8a55f5
--- /dev/null
+++ b/changelogs/unreleased/discussion-perf-improvement.yml
@@ -0,0 +1,5 @@
+---
+title: Improve initial discussion rendering performance
+merge_request: 22607
+author:
+type: changed
diff --git a/changelogs/unreleased/dm-api-merge-requests-index-merged-at.yml b/changelogs/unreleased/dm-api-merge-requests-index-merged-at.yml
new file mode 100644
index 00000000000..8e02a9019df
--- /dev/null
+++ b/changelogs/unreleased/dm-api-merge-requests-index-merged-at.yml
@@ -0,0 +1,5 @@
+---
+title: Expose {closed,merged}_{at,by} in merge requests API index
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/dm-commit-email-select-options.yml b/changelogs/unreleased/dm-commit-email-select-options.yml
new file mode 100644
index 00000000000..90d5c8cf0c6
--- /dev/null
+++ b/changelogs/unreleased/dm-commit-email-select-options.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug causing not all emails to show up in commit email selectbox
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/docs-minor-aws-fixes.yml b/changelogs/unreleased/docs-minor-aws-fixes.yml
new file mode 100644
index 00000000000..64fa6b12afe
--- /dev/null
+++ b/changelogs/unreleased/docs-minor-aws-fixes.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes to AWS documentation spelling and grammar
+merge_request: 23198
+author: Brendan O'Leary
+type: other
diff --git a/changelogs/unreleased/drop-allow_overflow-option-duration_in_numbers.yml b/changelogs/unreleased/drop-allow_overflow-option-duration_in_numbers.yml
new file mode 100644
index 00000000000..4bece6459a0
--- /dev/null
+++ b/changelogs/unreleased/drop-allow_overflow-option-duration_in_numbers.yml
@@ -0,0 +1,5 @@
+---
+title: Drop `allow_overflow` option in `TimeHelper.duration_in_numbers`
+merge_request: 52284
+author:
+type: changed
diff --git a/changelogs/unreleased/drop-default-value-status-deployments.yml b/changelogs/unreleased/drop-default-value-status-deployments.yml
new file mode 100644
index 00000000000..fdb826a0507
--- /dev/null
+++ b/changelogs/unreleased/drop-default-value-status-deployments.yml
@@ -0,0 +1,5 @@
+---
+title: Drop default value on status column in deployments table
+merge_request: 22971
+author:
+type: other
diff --git a/changelogs/unreleased/drop-gcp-cluster-table.yml b/changelogs/unreleased/drop-gcp-cluster-table.yml
new file mode 100644
index 00000000000..15964ec2eaf
--- /dev/null
+++ b/changelogs/unreleased/drop-gcp-cluster-table.yml
@@ -0,0 +1,5 @@
+---
+title: Drop gcp_clusters table
+merge_request: 22713
+author:
+type: other
diff --git a/changelogs/unreleased/dz-labels-search.yml b/changelogs/unreleased/dz-labels-search.yml
deleted file mode 100644
index 49c1b6c1a86..00000000000
--- a/changelogs/unreleased/dz-labels-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Search for labels by title or description on project labels page
-merge_request: 20749
-author:
-type: added
diff --git a/changelogs/unreleased/dz-manifest-import.yml b/changelogs/unreleased/dz-manifest-import.yml
deleted file mode 100644
index b0d29b0869f..00000000000
--- a/changelogs/unreleased/dz-manifest-import.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add ability to import multiple repositories by uploading a manifest file
-merge_request: 20304
-author:
-type: added
diff --git a/changelogs/unreleased/enable-frozen-string-lib-gitlab.yml b/changelogs/unreleased/enable-frozen-string-lib-gitlab.yml
new file mode 100644
index 00000000000..4a216c46d38
--- /dev/null
+++ b/changelogs/unreleased/enable-frozen-string-lib-gitlab.yml
@@ -0,0 +1,5 @@
+---
+title: Enable some frozen string in lib/gitlab
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/even-more-frozen-string-lib.yml b/changelogs/unreleased/even-more-frozen-string-lib.yml
new file mode 100644
index 00000000000..3f5fd7710aa
--- /dev/null
+++ b/changelogs/unreleased/even-more-frozen-string-lib.yml
@@ -0,0 +1,5 @@
+---
+title: Enable even more frozen string in lib/**/*.rb
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/fast_project_blob_path.yml b/changelogs/unreleased/fast_project_blob_path.yml
new file mode 100644
index 00000000000..b56c9d9cf59
--- /dev/null
+++ b/changelogs/unreleased/fast_project_blob_path.yml
@@ -0,0 +1,5 @@
+---
+title: Improve performance of tree rendering in repositories with lots of items
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/fe-ac-review-app-changes-33418.yml b/changelogs/unreleased/fe-ac-review-app-changes-33418.yml
new file mode 100644
index 00000000000..e4803683062
--- /dev/null
+++ b/changelogs/unreleased/fe-ac-review-app-changes-33418.yml
@@ -0,0 +1,5 @@
+---
+title: Adds filtered dropdown with changed files in review
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/feat-add-default-avatar-to-group.yml b/changelogs/unreleased/feat-add-default-avatar-to-group.yml
deleted file mode 100644
index 56d8f2ccd6d..00000000000
--- a/changelogs/unreleased/feat-add-default-avatar-to-group.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add default avatar to group
-merge_request: 17271
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/feature-gb-email-delivery-metrics.yml b/changelogs/unreleased/feature-gb-email-delivery-metrics.yml
deleted file mode 100644
index 9d0d08a471d..00000000000
--- a/changelogs/unreleased/feature-gb-email-delivery-metrics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add emails delivery Prometheus metrics
-merge_request: 20638
-author:
-type: added
diff --git a/changelogs/unreleased/feature-gb-improve-include-config-errors-reporting.yml b/changelogs/unreleased/feature-gb-improve-include-config-errors-reporting.yml
new file mode 100644
index 00000000000..67eb6b78096
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-improve-include-config-errors-reporting.yml
@@ -0,0 +1,5 @@
+---
+title: Improve validation errors for external CI/CD configuration
+merge_request: 22394
+author:
+type: added
diff --git a/changelogs/unreleased/feature-gb-login-activity-metrics.yml b/changelogs/unreleased/feature-gb-login-activity-metrics.yml
deleted file mode 100644
index 5d687b984eb..00000000000
--- a/changelogs/unreleased/feature-gb-login-activity-metrics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add more comprehensive metrics tracking authentication activity
-merge_request: 20668
-author:
-type: added
diff --git a/changelogs/unreleased/feature-improved-branch-filter-sorting.yml b/changelogs/unreleased/feature-improved-branch-filter-sorting.yml
new file mode 100644
index 00000000000..539c297e0dd
--- /dev/null
+++ b/changelogs/unreleased/feature-improved-branch-filter-sorting.yml
@@ -0,0 +1,6 @@
+---
+title: Improving branch filter sorting by listing exact matches first and added support
+ for begins_with (^) and ends_with ($) matching.
+merge_request: 22166
+author: Jason Rutherford
+type: changed
diff --git a/changelogs/unreleased/features-show-project-id-on-home-panel.yml b/changelogs/unreleased/features-show-project-id-on-home-panel.yml
deleted file mode 100644
index f592be07a52..00000000000
--- a/changelogs/unreleased/features-show-project-id-on-home-panel.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show Project ID on project home panel
-merge_request: 20305
-author: Tuğçe Nur Taş
-type: added
diff --git a/changelogs/unreleased/fix-53298.yml b/changelogs/unreleased/fix-53298.yml
new file mode 100644
index 00000000000..f0bf5470dc8
--- /dev/null
+++ b/changelogs/unreleased/fix-53298.yml
@@ -0,0 +1,5 @@
+---
+title: 'Fix #53298: JupyterHub restarts should work without errors'
+merge_request: 22671
+author: Amit Rathi
+type: fixed
diff --git a/changelogs/unreleased/fix-base64-encoded-file-uploads.yml b/changelogs/unreleased/fix-base64-encoded-file-uploads.yml
new file mode 100644
index 00000000000..3dde2419fa1
--- /dev/null
+++ b/changelogs/unreleased/fix-base64-encoded-file-uploads.yml
@@ -0,0 +1,5 @@
+---
+title: Remove base64 encoding from files that contain plain text
+merge_request: 22425
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-deployment-metrics-in-mr-widget.yml b/changelogs/unreleased/fix-deployment-metrics-in-mr-widget.yml
new file mode 100644
index 00000000000..5427ead3d1b
--- /dev/null
+++ b/changelogs/unreleased/fix-deployment-metrics-in-mr-widget.yml
@@ -0,0 +1,6 @@
+---
+title: Avoid returning deployment metrics url to MR widget when the deployment is
+ not successful
+merge_request: 23010
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-diff-note.yml b/changelogs/unreleased/fix-diff-note.yml
deleted file mode 100644
index 6f10f86b9bc..00000000000
--- a/changelogs/unreleased/fix-diff-note.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix serialization of LegacyDiffNote
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-email-confirmation-addtional-email.yml b/changelogs/unreleased/fix-email-confirmation-addtional-email.yml
deleted file mode 100644
index 56a2efa4d60..00000000000
--- a/changelogs/unreleased/fix-email-confirmation-addtional-email.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix email confirmation bug when user adds additional email to account
-merge_request: 20084
-author: muhammadn
-type: fixed
diff --git a/changelogs/unreleased/fix-error-handling-bugs-in-kubernetes-integration.yml b/changelogs/unreleased/fix-error-handling-bugs-in-kubernetes-integration.yml
new file mode 100644
index 00000000000..f2a117fe63f
--- /dev/null
+++ b/changelogs/unreleased/fix-error-handling-bugs-in-kubernetes-integration.yml
@@ -0,0 +1,5 @@
+---
+title: Fix error handling bugs in kubernetes integration
+merge_request: 22922
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml b/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml
deleted file mode 100644
index 7e9e8c33a71..00000000000
--- a/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add missing predefined variable and fix docs
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml b/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml
deleted file mode 100644
index 80b069c9251..00000000000
--- a/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix accessing imported pipeline builds
-merge_request: 20713
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml b/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml
deleted file mode 100644
index adf582e34a2..00000000000
--- a/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Limit maximum project build timeout setting to 1 month
-merge_request: 20591
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml b/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml
deleted file mode 100644
index d215d034917..00000000000
--- a/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix label list item container height when there is no label description
-merge_request: 21106
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-multiple-scopes.yml b/changelogs/unreleased/fix-multiple-scopes.yml
deleted file mode 100644
index 24e5172d9a1..00000000000
--- a/changelogs/unreleased/fix-multiple-scopes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support multiple scopes when authing container registry scopes
-merge_request: 20617
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-not-render-emoji.yml b/changelogs/unreleased/fix-not-render-emoji.yml
new file mode 100644
index 00000000000..857b97004f0
--- /dev/null
+++ b/changelogs/unreleased/fix-not-render-emoji.yml
@@ -0,0 +1,5 @@
+---
+title: Fix not render emoji in filter dropdown
+merge_request: 23112
+author: Hiroyuki Sato
+type: fixed
diff --git a/changelogs/unreleased/fix-pipeline-fixture-seeder.yml b/changelogs/unreleased/fix-pipeline-fixture-seeder.yml
deleted file mode 100644
index 02b83062e07..00000000000
--- a/changelogs/unreleased/fix-pipeline-fixture-seeder.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix pipeline fixture seeder
-merge_request: 21088
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-project-api-archived.yml b/changelogs/unreleased/fix-project-api-archived.yml
deleted file mode 100644
index 9d119fd3429..00000000000
--- a/changelogs/unreleased/fix-project-api-archived.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix archived parameter for projects API
-merge_request: 20566
-author: Peter Marko
-type: fixed
diff --git a/changelogs/unreleased/fix-prometheus-updated-status.yml b/changelogs/unreleased/fix-prometheus-updated-status.yml
deleted file mode 100644
index 7261c3429c8..00000000000
--- a/changelogs/unreleased/fix-prometheus-updated-status.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix UI error whereby prometheus application status is updated
-merge_request: 21029
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-search-bar.yml b/changelogs/unreleased/fix-search-bar.yml
deleted file mode 100644
index d4c0c1efddf..00000000000
--- a/changelogs/unreleased/fix-search-bar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix search bar text input alignment
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-storage-size-for-artifacts-change.yml b/changelogs/unreleased/fix-storage-size-for-artifacts-change.yml
deleted file mode 100644
index 6a3e1420726..00000000000
--- a/changelogs/unreleased/fix-storage-size-for-artifacts-change.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update total storage size when changing size of artifacts
-merge_request: 20697
-author: Peter Marko
-type: fixed
diff --git a/changelogs/unreleased/fix-stuck-import-jobs-query-performance-issue.yml b/changelogs/unreleased/fix-stuck-import-jobs-query-performance-issue.yml
new file mode 100644
index 00000000000..d8455a8509f
--- /dev/null
+++ b/changelogs/unreleased/fix-stuck-import-jobs-query-performance-issue.yml
@@ -0,0 +1,5 @@
+---
+title: Improves performance of stuck import jobs detection
+merge_request: 22879
+author:
+type: performance
diff --git a/changelogs/unreleased/fix-tags-for-envs.yml b/changelogs/unreleased/fix-tags-for-envs.yml
new file mode 100644
index 00000000000..633788ff6d8
--- /dev/null
+++ b/changelogs/unreleased/fix-tags-for-envs.yml
@@ -0,0 +1,5 @@
+---
+title: Do not reload self on hooks when creating deployment
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-37736-improve-performance-post-receive-create-gpg-siganture-worker.yml b/changelogs/unreleased/fj-37736-improve-performance-post-receive-create-gpg-siganture-worker.yml
deleted file mode 100644
index 0b35c5c6786..00000000000
--- a/changelogs/unreleased/fj-37736-improve-performance-post-receive-create-gpg-siganture-worker.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Performing Commit GPG signature calculation in bulk
-merge_request: 20870
-author:
-type: performance
diff --git a/changelogs/unreleased/fj-41213-api-update-submodule-commit.yml b/changelogs/unreleased/fj-41213-api-update-submodule-commit.yml
new file mode 100644
index 00000000000..c06b02b05e8
--- /dev/null
+++ b/changelogs/unreleased/fj-41213-api-update-submodule-commit.yml
@@ -0,0 +1,5 @@
+---
+title: Add endpoint to update a git submodule reference
+merge_request: 20949
+author:
+type: added
diff --git a/changelogs/unreleased/fj-48123-fix-gitlab-import.yml b/changelogs/unreleased/fj-48123-fix-gitlab-import.yml
deleted file mode 100644
index 896db2cdcb8..00000000000
--- a/changelogs/unreleased/fj-48123-fix-gitlab-import.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix GitLab project imports not loading due to API timeouts
-merge_request: 20599
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-49014-wiki-search-error.yml b/changelogs/unreleased/fj-49014-wiki-search-error.yml
deleted file mode 100644
index a76805cb7f9..00000000000
--- a/changelogs/unreleased/fj-49014-wiki-search-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed bug with invalid repository reference using the wiki search
-merge_request: 20722
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-49512-fix-gitlab-git-pages-encoding.yml b/changelogs/unreleased/fj-49512-fix-gitlab-git-pages-encoding.yml
deleted file mode 100644
index 3af90fff3f6..00000000000
--- a/changelogs/unreleased/fj-49512-fix-gitlab-git-pages-encoding.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent editing and updating wiki pages with non UTF-8 encoding via web interface
-merge_request: 20906
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-49802-bug-api-set-http-headers.yml b/changelogs/unreleased/fj-49802-bug-api-set-http-headers.yml
deleted file mode 100644
index ba61d378cda..00000000000
--- a/changelogs/unreleased/fj-49802-bug-api-set-http-headers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix bug setting http headers in Files API
-merge_request: 20938
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-50890-fix-commit-message-wiki-new-page.yml b/changelogs/unreleased/fj-50890-fix-commit-message-wiki-new-page.yml
new file mode 100644
index 00000000000..5add6d727ac
--- /dev/null
+++ b/changelogs/unreleased/fj-50890-fix-commit-message-wiki-new-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug with wiki page create message
+merge_request: 22849
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-52406-wiki-file-content-disposition.yml b/changelogs/unreleased/fj-52406-wiki-file-content-disposition.yml
new file mode 100644
index 00000000000..b765e8caf8b
--- /dev/null
+++ b/changelogs/unreleased/fj-52406-wiki-file-content-disposition.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug with wiki attachments content disposition
+merge_request: 22220
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-bump-gitaly-0-129-0.yml b/changelogs/unreleased/fj-bump-gitaly-0-129-0.yml
new file mode 100644
index 00000000000..9d44e46c0ed
--- /dev/null
+++ b/changelogs/unreleased/fj-bump-gitaly-0-129-0.yml
@@ -0,0 +1,5 @@
+---
+title: Bump Gitaly to 0.129.0
+merge_request: 22868
+author:
+type: added
diff --git a/changelogs/unreleased/fl-missing-i18n.yml b/changelogs/unreleased/fl-missing-i18n.yml
new file mode 100644
index 00000000000..d41a691e636
--- /dev/null
+++ b/changelogs/unreleased/fl-missing-i18n.yml
@@ -0,0 +1,5 @@
+---
+title: Adds missing i18n to pipelines table
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/fl-reduce-ee-conflicts-reports-code.yml b/changelogs/unreleased/fl-reduce-ee-conflicts-reports-code.yml
deleted file mode 100644
index 068681dfe19..00000000000
--- a/changelogs/unreleased/fl-reduce-ee-conflicts-reports-code.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Reduce differences between CE and EE code base in reports components
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/fl-update-svgs.yml b/changelogs/unreleased/fl-update-svgs.yml
new file mode 100644
index 00000000000..e6e76617df1
--- /dev/null
+++ b/changelogs/unreleased/fl-update-svgs.yml
@@ -0,0 +1,5 @@
+---
+title: Updates svg dependency
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/floating-avarage-commit-numbers.yml b/changelogs/unreleased/floating-avarage-commit-numbers.yml
deleted file mode 100644
index 7f91ab16af4..00000000000
--- a/changelogs/unreleased/floating-avarage-commit-numbers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show one digit after dot in commit_per_day value in charts page.
-merge_request:
-author: msdundar
-type: changed
diff --git a/changelogs/unreleased/frozen-string-danger.yml b/changelogs/unreleased/frozen-string-danger.yml
deleted file mode 100644
index 9910139b8a9..00000000000
--- a/changelogs/unreleased/frozen-string-danger.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Dangerfile for frozen_string_literal
-merge_request: 20767
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml b/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml
deleted file mode 100644
index a77f3baeed3..00000000000
--- a/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in rest of app/models/**/*.rb
-merge_request: gfyoung
-author:
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-models-more.yml b/changelogs/unreleased/frozen-string-enable-app-models-more.yml
deleted file mode 100644
index c0466984134..00000000000
--- a/changelogs/unreleased/frozen-string-enable-app-models-more.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string for app/models/**/*.rb
-merge_request: 21001
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-models.yml b/changelogs/unreleased/frozen-string-enable-app-models.yml
deleted file mode 100644
index 4c149ea55ef..00000000000
--- a/changelogs/unreleased/frozen-string-enable-app-models.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in app/models/*.rb
-merge_request: 20851
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml b/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml
deleted file mode 100644
index 5c6b1b1a904..00000000000
--- a/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in app/presenters and app/policies
-merge_request: 20819
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-serializers.yml b/changelogs/unreleased/frozen-string-enable-app-serializers.yml
deleted file mode 100644
index 40c7b695d39..00000000000
--- a/changelogs/unreleased/frozen-string-enable-app-serializers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in app/serializers/**/*.rb
-merge_request: 20726
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-services.yml b/changelogs/unreleased/frozen-string-enable-app-services.yml
deleted file mode 100644
index cfc1f356e3a..00000000000
--- a/changelogs/unreleased/frozen-string-enable-app-services.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in apps/uploaders/*.rb
-merge_request: 20401
-author: gfyoung
-type: other
diff --git a/changelogs/unreleased/frozen-string-enable-app-vestigial.yml b/changelogs/unreleased/frozen-string-enable-app-vestigial.yml
deleted file mode 100644
index 8cb7bd43784..00000000000
--- a/changelogs/unreleased/frozen-string-enable-app-vestigial.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in vestigial app files
-merge_request:
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner-even-more.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner-even-more.yml
deleted file mode 100644
index cee790a07ff..00000000000
--- a/changelogs/unreleased/frozen-string-enable-apps-services-inner-even-more.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable even more frozen string in app/services/**/*.rb
-merge_request: 20702
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml
deleted file mode 100644
index ea962cf8edc..00000000000
--- a/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable more frozen string in app/services/**/*.rb
-merge_request: 20677
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml
deleted file mode 100644
index 16b8ec3908f..00000000000
--- a/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in app/services/**/*.rb
-merge_request: 20656
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-lib-gitlab-ci-remain.yml b/changelogs/unreleased/frozen-string-enable-lib-gitlab-ci-remain.yml
new file mode 100644
index 00000000000..ecbfc323080
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-lib-gitlab-ci-remain.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string for remaining lib/gitlab/ci/**/*.rb
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-lib-gitlab-ci.yml b/changelogs/unreleased/frozen-string-enable-lib-gitlab-ci.yml
new file mode 100644
index 00000000000..a881c304f34
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-lib-gitlab-ci.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string for lib/gitlab/ci
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-lib-gitlab.yml b/changelogs/unreleased/frozen-string-enable-lib-gitlab.yml
new file mode 100644
index 00000000000..d64278eb626
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-lib-gitlab.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string for lib/gitlab/*.rb
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-lib-gitlab-even-even-more.yml b/changelogs/unreleased/frozen-string-lib-gitlab-even-even-more.yml
new file mode 100644
index 00000000000..e718d716647
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-lib-gitlab-even-even-more.yml
@@ -0,0 +1,5 @@
+---
+title: Enable even more frozen string for lib/gitlab
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-lib-gitlab-even-more.yml b/changelogs/unreleased/frozen-string-lib-gitlab-even-more.yml
new file mode 100644
index 00000000000..cfbc4ced635
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-lib-gitlab-even-more.yml
@@ -0,0 +1,5 @@
+---
+title: Enable even more frozen string in lib/gitlab/**/*.rb
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-lib-gitlab-more.yml b/changelogs/unreleased/frozen-string-lib-gitlab-more.yml
new file mode 100644
index 00000000000..cfbc4ced635
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-lib-gitlab-more.yml
@@ -0,0 +1,5 @@
+---
+title: Enable even more frozen string in lib/gitlab/**/*.rb
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-vestigial.yml b/changelogs/unreleased/frozen-string-vestigial.yml
deleted file mode 100644
index 79e92a19a7a..00000000000
--- a/changelogs/unreleased/frozen-string-vestigial.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in newly added files to previously processed directories
-merge_request: 20763
-author: gfyoung
-type: performance
diff --git a/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml b/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml
deleted file mode 100644
index b26eb82b6c9..00000000000
--- a/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removes "show all" on reports and adds an actionButtons slot
-merge_request: 20855
-author:
-type: changed
diff --git a/changelogs/unreleased/git-rerere-link-doc-update.yml b/changelogs/unreleased/git-rerere-link-doc-update.yml
deleted file mode 100644
index 06093e8ec13..00000000000
--- a/changelogs/unreleased/git-rerere-link-doc-update.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update git rerere link in docs
-merge_request: 21060
-author: gfyoung
-type: other
diff --git a/changelogs/unreleased/gl-ui-loading-icon.yml b/changelogs/unreleased/gl-ui-loading-icon.yml
new file mode 100644
index 00000000000..5540fc7d7ea
--- /dev/null
+++ b/changelogs/unreleased/gl-ui-loading-icon.yml
@@ -0,0 +1,5 @@
+---
+title: Remove gitlab-ui's loading icon from global
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/gl-ui-modal.yml b/changelogs/unreleased/gl-ui-modal.yml
new file mode 100644
index 00000000000..fbdb8260d24
--- /dev/null
+++ b/changelogs/unreleased/gl-ui-modal.yml
@@ -0,0 +1,5 @@
+---
+title: Remove gitlab-ui's modal from global
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/gl-ui-pagination.yml b/changelogs/unreleased/gl-ui-pagination.yml
new file mode 100644
index 00000000000..cf73d6a1f8f
--- /dev/null
+++ b/changelogs/unreleased/gl-ui-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: Remove gitlab-ui's pagination from global
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/gl-ui-progress-bar.yml b/changelogs/unreleased/gl-ui-progress-bar.yml
new file mode 100644
index 00000000000..1e584dacd6f
--- /dev/null
+++ b/changelogs/unreleased/gl-ui-progress-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Remove gitlab-ui's progress bar from global
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/gl-ui-tooltip.yml b/changelogs/unreleased/gl-ui-tooltip.yml
new file mode 100644
index 00000000000..99ded9f812e
--- /dev/null
+++ b/changelogs/unreleased/gl-ui-tooltip.yml
@@ -0,0 +1,5 @@
+---
+title: Remove gitlab-ui's tooltip from global
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/gt-add-transparent-background-to-markdown-header-tabs.yml b/changelogs/unreleased/gt-add-transparent-background-to-markdown-header-tabs.yml
new file mode 100644
index 00000000000..2ba52e07324
--- /dev/null
+++ b/changelogs/unreleased/gt-add-transparent-background-to-markdown-header-tabs.yml
@@ -0,0 +1,5 @@
+---
+title: Add transparent background to markdown header tabs
+merge_request: 22565
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/gt-align-sign-in-button.yml b/changelogs/unreleased/gt-align-sign-in-button.yml
new file mode 100644
index 00000000000..a51fa319231
--- /dev/null
+++ b/changelogs/unreleased/gt-align-sign-in-button.yml
@@ -0,0 +1,5 @@
+---
+title: Align sign in button
+merge_request: 22888
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/gt-change-breadcrumb-title-for-contribution-charts.yml b/changelogs/unreleased/gt-change-breadcrumb-title-for-contribution-charts.yml
new file mode 100644
index 00000000000..233cc43117d
--- /dev/null
+++ b/changelogs/unreleased/gt-change-breadcrumb-title-for-contribution-charts.yml
@@ -0,0 +1,5 @@
+---
+title: Change breadcrumb title for contribution charts
+merge_request: 23071
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/gt-fix-ide-typos-in-props.yml b/changelogs/unreleased/gt-fix-ide-typos-in-props.yml
new file mode 100644
index 00000000000..a81b227c82f
--- /dev/null
+++ b/changelogs/unreleased/gt-fix-ide-typos-in-props.yml
@@ -0,0 +1,5 @@
+---
+title: Fix IDE typos in props
+merge_request: 22685
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-fix-quick-links-button-styles.yml b/changelogs/unreleased/gt-fix-quick-links-button-styles.yml
new file mode 100644
index 00000000000..4c1150631f8
--- /dev/null
+++ b/changelogs/unreleased/gt-fix-quick-links-button-styles.yml
@@ -0,0 +1,5 @@
+---
+title: Fix quick links button styles
+merge_request: 22657
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/gt-fix-typo-in-notebook-props.yml b/changelogs/unreleased/gt-fix-typo-in-notebook-props.yml
new file mode 100644
index 00000000000..60603905a2d
--- /dev/null
+++ b/changelogs/unreleased/gt-fix-typo-in-notebook-props.yml
@@ -0,0 +1,5 @@
+---
+title: Fix typo in notebook props
+merge_request: 23103
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-fix-typos-in-lib.yml b/changelogs/unreleased/gt-fix-typos-in-lib.yml
new file mode 100644
index 00000000000..32ccd03b063
--- /dev/null
+++ b/changelogs/unreleased/gt-fix-typos-in-lib.yml
@@ -0,0 +1,5 @@
+---
+title: Fix typos in lib
+merge_request: 23106
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-remove-empty-spec-describe-blocks.yml b/changelogs/unreleased/gt-remove-empty-spec-describe-blocks.yml
new file mode 100644
index 00000000000..d2a65d48d8d
--- /dev/null
+++ b/changelogs/unreleased/gt-remove-empty-spec-describe-blocks.yml
@@ -0,0 +1,5 @@
+---
+title: Remove empty spec describe blocks
+merge_request: 22451
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-remove-prometheus-configuration-help-text.yml b/changelogs/unreleased/gt-remove-prometheus-configuration-help-text.yml
new file mode 100644
index 00000000000..018686c0d47
--- /dev/null
+++ b/changelogs/unreleased/gt-remove-prometheus-configuration-help-text.yml
@@ -0,0 +1,5 @@
+---
+title: Remove prometheus configuration help text
+merge_request: 22413
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-remove-unused-project-method.yml b/changelogs/unreleased/gt-remove-unused-project-method.yml
new file mode 100644
index 00000000000..2d60c2fe423
--- /dev/null
+++ b/changelogs/unreleased/gt-remove-unused-project-method.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unused project method
+merge_request: 54103
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-rename-diffs-store-variable.yml b/changelogs/unreleased/gt-rename-diffs-store-variable.yml
new file mode 100644
index 00000000000..0aed49f3d60
--- /dev/null
+++ b/changelogs/unreleased/gt-rename-diffs-store-variable.yml
@@ -0,0 +1,5 @@
+---
+title: Rename diffs store variable
+merge_request: 23123
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-truncate-milestone-title-on-collapsed-sidebar.yml b/changelogs/unreleased/gt-truncate-milestone-title-on-collapsed-sidebar.yml
new file mode 100644
index 00000000000..ca3b99e73ab
--- /dev/null
+++ b/changelogs/unreleased/gt-truncate-milestone-title-on-collapsed-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Truncate milestone title on collapsed sidebar
+merge_request: 22624
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/gt-update-environments-empty-state.yml b/changelogs/unreleased/gt-update-environments-empty-state.yml
new file mode 100644
index 00000000000..dcb477cdbe4
--- /dev/null
+++ b/changelogs/unreleased/gt-update-environments-empty-state.yml
@@ -0,0 +1,5 @@
+---
+title: Update environments empty state
+merge_request: 22297
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-update-project-and-group-labels-empty-state.yml b/changelogs/unreleased/gt-update-project-and-group-labels-empty-state.yml
new file mode 100644
index 00000000000..d644ca86b79
--- /dev/null
+++ b/changelogs/unreleased/gt-update-project-and-group-labels-empty-state.yml
@@ -0,0 +1,5 @@
+---
+title: Update project and group labels empty state
+merge_request: 22745
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/gt-update-wiki-empty-state.yml b/changelogs/unreleased/gt-update-wiki-empty-state.yml
new file mode 100644
index 00000000000..76f923ae814
--- /dev/null
+++ b/changelogs/unreleased/gt-update-wiki-empty-state.yml
@@ -0,0 +1,5 @@
+---
+title: Update wiki empty state
+merge_requrst: 22218
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-use-merge-request-prefix-in-event-feed-title.yml b/changelogs/unreleased/gt-use-merge-request-prefix-in-event-feed-title.yml
new file mode 100644
index 00000000000..51af2807a03
--- /dev/null
+++ b/changelogs/unreleased/gt-use-merge-request-prefix-in-event-feed-title.yml
@@ -0,0 +1,5 @@
+---
+title: Use merge request prefix symbol in event feed title
+merge_request: 22449
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/hangouts_chat_integration.yml b/changelogs/unreleased/hangouts_chat_integration.yml
deleted file mode 100644
index bf3484a6d02..00000000000
--- a/changelogs/unreleased/hangouts_chat_integration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Hangouts Chat integration
-merge_request: 20290
-author: Kukovskii Vladimir
-type: added
diff --git a/changelogs/unreleased/ide-codesandbox-poc.yml b/changelogs/unreleased/ide-codesandbox-poc.yml
deleted file mode 100644
index 7da1f4e6472..00000000000
--- a/changelogs/unreleased/ide-codesandbox-poc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added live preview for JavaScript projects in the Web IDE
-merge_request: 19764
-author:
-type: added
diff --git a/changelogs/unreleased/ide-delete-entries.yml b/changelogs/unreleased/ide-delete-entries.yml
deleted file mode 100644
index 8cbc0739406..00000000000
--- a/changelogs/unreleased/ide-delete-entries.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enabled deletion of files in the Web IDE
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/ide-file-templates-clear.yml b/changelogs/unreleased/ide-file-templates-clear.yml
new file mode 100644
index 00000000000..7878f2231a7
--- /dev/null
+++ b/changelogs/unreleased/ide-file-templates-clear.yml
@@ -0,0 +1,5 @@
+---
+title: Clear fetched file templates when changing template type in Web IDE
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/ide-header-buttons-tooltip.yml b/changelogs/unreleased/ide-header-buttons-tooltip.yml
deleted file mode 100644
index 4c8f6fd554f..00000000000
--- a/changelogs/unreleased/ide-header-buttons-tooltip.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added tooltips to tree list header
-merge_request: 21138
-author:
-type: added
diff --git a/changelogs/unreleased/ide-open-empty-merge-request.yml b/changelogs/unreleased/ide-open-empty-merge-request.yml
deleted file mode 100644
index 05f2de5d31c..00000000000
--- a/changelogs/unreleased/ide-open-empty-merge-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix empty merge requests not opening in the Web IDE
-merge_request: 21102
-author:
-type: fixed
diff --git a/changelogs/unreleased/ide-pipeline-icon-open.yml b/changelogs/unreleased/ide-pipeline-icon-open.yml
deleted file mode 100644
index 3a73ff2170f..00000000000
--- a/changelogs/unreleased/ide-pipeline-icon-open.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Clicking CI icon in Web IDE now opens up pipelines panel
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/ide-rename-files.yml b/changelogs/unreleased/ide-rename-files.yml
deleted file mode 100644
index c2db284e07c..00000000000
--- a/changelogs/unreleased/ide-rename-files.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable renaming files and folders in Web IDE
-merge_request: 20835
-author:
-type: added
diff --git a/changelogs/unreleased/ide-row-dropdown-design-update.yml b/changelogs/unreleased/ide-row-dropdown-design-update.yml
deleted file mode 100644
index e0fe64c944e..00000000000
--- a/changelogs/unreleased/ide-row-dropdown-design-update.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Updated design of new entry dropdown in Web IDE
-merge_request: 20526
-author:
-type: changed
diff --git a/changelogs/unreleased/ide-warn-staged-files.yml b/changelogs/unreleased/ide-warn-staged-files.yml
deleted file mode 100644
index ae3c4f392c0..00000000000
--- a/changelogs/unreleased/ide-warn-staged-files.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Warn user when reload IDE with staged changes
-merge_request: 20857
-author:
-type: added
diff --git a/changelogs/unreleased/ignore-environment-validation-failure.yml b/changelogs/unreleased/ignore-environment-validation-failure.yml
new file mode 100644
index 00000000000..1b61cf86dc4
--- /dev/null
+++ b/changelogs/unreleased/ignore-environment-validation-failure.yml
@@ -0,0 +1,5 @@
+---
+title: Ignore environment validation failure
+merge_request: 23100
+author:
+type: fixed
diff --git a/changelogs/unreleased/improve-junit-support-be.yml b/changelogs/unreleased/improve-junit-support-be.yml
deleted file mode 100644
index db4d47caa7c..00000000000
--- a/changelogs/unreleased/improve-junit-support-be.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve JUnit test reports in merge request widgets
-merge_request: 49966
-author:
-type: fixed
diff --git a/changelogs/unreleased/improve-metadata-access-performance.yml b/changelogs/unreleased/improve-metadata-access-performance.yml
deleted file mode 100644
index b16fa99dd3b..00000000000
--- a/changelogs/unreleased/improve-metadata-access-performance.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Access metadata directly from Object Storage
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/introduce-knative-support.yml b/changelogs/unreleased/introduce-knative-support.yml
new file mode 100644
index 00000000000..53290d71977
--- /dev/null
+++ b/changelogs/unreleased/introduce-knative-support.yml
@@ -0,0 +1,5 @@
+---
+title: Introduce Knative support
+author: Chris Baumbauer
+merge_request: 43959
+type: added
diff --git a/changelogs/unreleased/issue_43602.yml b/changelogs/unreleased/issue_43602.yml
deleted file mode 100644
index 0482606db0a..00000000000
--- a/changelogs/unreleased/issue_43602.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow multiple JIRA transition ids
-merge_request: 20939
-author:
-type: changed
diff --git a/changelogs/unreleased/issue_44821.yml b/changelogs/unreleased/issue_44821.yml
deleted file mode 100644
index b1807e069af..00000000000
--- a/changelogs/unreleased/issue_44821.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Retrieve merge request closing issues from database cache
-merge_request: 20911
-author:
-type: fixed
diff --git a/changelogs/unreleased/issue_47709.yml b/changelogs/unreleased/issue_47709.yml
deleted file mode 100644
index c3ef55fd692..00000000000
--- a/changelogs/unreleased/issue_47709.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Allow to toggle notifications for issues due soon'
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/issue_49936.yml b/changelogs/unreleased/issue_49936.yml
new file mode 100644
index 00000000000..2283eb15fd9
--- /dev/null
+++ b/changelogs/unreleased/issue_49936.yml
@@ -0,0 +1,5 @@
+---
+title: Update JIRA service UI to accept email and API token
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/issue_51323.yml b/changelogs/unreleased/issue_51323.yml
new file mode 100644
index 00000000000..b0e83e303d1
--- /dev/null
+++ b/changelogs/unreleased/issue_51323.yml
@@ -0,0 +1,5 @@
+---
+title: Add 'only history' option to notes filter
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/jivl-fix-bar-char-transient-spec-failure.yml b/changelogs/unreleased/jivl-fix-bar-char-transient-spec-failure.yml
new file mode 100644
index 00000000000..344997add74
--- /dev/null
+++ b/changelogs/unreleased/jivl-fix-bar-char-transient-spec-failure.yml
@@ -0,0 +1,5 @@
+---
+title: Fix transient spec error in the bar_chart component
+merge_request: 22495
+author:
+type: fixed
diff --git a/changelogs/unreleased/jprovazn-resource-events.yml b/changelogs/unreleased/jprovazn-resource-events.yml
deleted file mode 100644
index 05643150f16..00000000000
--- a/changelogs/unreleased/jprovazn-resource-events.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add new model for tracking label events.
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/jr-archive-hook.yml b/changelogs/unreleased/jr-archive-hook.yml
deleted file mode 100644
index 56c13f1370e..00000000000
--- a/changelogs/unreleased/jr-archive-hook.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Trigger system hooks when project is archived/unarchived
-merge_request: 20995
-author:
-type: added
diff --git a/changelogs/unreleased/jramsay-42673-commit-tooltip.yml b/changelogs/unreleased/jramsay-42673-commit-tooltip.yml
new file mode 100644
index 00000000000..083cd1a54a0
--- /dev/null
+++ b/changelogs/unreleased/jramsay-42673-commit-tooltip.yml
@@ -0,0 +1,5 @@
+---
+title: Add commit message to commit tree anchor title
+merge_request: 22585
+author:
+type: fixed
diff --git a/changelogs/unreleased/jupyter-image.yml b/changelogs/unreleased/jupyter-image.yml
deleted file mode 100644
index 8aeefd603d8..00000000000
--- a/changelogs/unreleased/jupyter-image.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rubix, scikit-learn, tensorflow & other useful libraries pre-installed with JupyterHub
-merge_request: 20714
-author: Amit Rathi
-type: changed
diff --git a/changelogs/unreleased/kinolaev-master-patch-91872.yml b/changelogs/unreleased/kinolaev-master-patch-91872.yml
new file mode 100644
index 00000000000..053e9101e39
--- /dev/null
+++ b/changelogs/unreleased/kinolaev-master-patch-91872.yml
@@ -0,0 +1,5 @@
+---
+title: Change HELM_HOST in Auto-DevOps template to work behind proxy
+merge_request: 22596
+author: Sergej Nikolaev <kinolaev@gmail.com>
+type: fixed
diff --git a/changelogs/unreleased/kp-6927-epic-dates-from-milestone.yml b/changelogs/unreleased/kp-6927-epic-dates-from-milestone.yml
deleted file mode 100644
index c15d73a0c12..00000000000
--- a/changelogs/unreleased/kp-6927-epic-dates-from-milestone.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add 'tabindex' attribute support on Icon component to show BS4 popover on trigger type 'focus'
-merge_request: 21066
-author:
-type: other
diff --git a/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml
deleted file mode 100644
index a2fca4c5b91..00000000000
--- a/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show decimal place up to single digit in Stacked Progress Bar
-merge_request: 20776
-author:
-type: changed
diff --git a/changelogs/unreleased/kubernetes-http-response-code.yml b/changelogs/unreleased/kubernetes-http-response-code.yml
new file mode 100644
index 00000000000..551fe2edc3c
--- /dev/null
+++ b/changelogs/unreleased/kubernetes-http-response-code.yml
@@ -0,0 +1,5 @@
+---
+title: Show HTTP response code for Kubernetes errors
+merge_request: 22964
+author:
+type: other
diff --git a/changelogs/unreleased/leipert-fix-pipelines-view.yml b/changelogs/unreleased/leipert-fix-pipelines-view.yml
deleted file mode 100644
index 7bcc2e84cd2..00000000000
--- a/changelogs/unreleased/leipert-fix-pipelines-view.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix rendering of pipeline failure view when directly navigationg to it
-merge_request: 21043
-author:
-type: fixed
diff --git a/changelogs/unreleased/lfs-project-attribute-alias.yml b/changelogs/unreleased/lfs-project-attribute-alias.yml
new file mode 100644
index 00000000000..883869f651a
--- /dev/null
+++ b/changelogs/unreleased/lfs-project-attribute-alias.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve LFS not correctly showing enabled
+merge_request: 22501
+author:
+type: fixed
diff --git a/changelogs/unreleased/max_retries_when.yml b/changelogs/unreleased/max_retries_when.yml
new file mode 100644
index 00000000000..dad3cd8a123
--- /dev/null
+++ b/changelogs/unreleased/max_retries_when.yml
@@ -0,0 +1,5 @@
+---
+title: Allow to configure when to retry failed CI jobs
+merge_request: 21758
+author: Markus Doits
+type: added
diff --git a/changelogs/unreleased/mk-add-local-project-uploads-cleanup-task.yml b/changelogs/unreleased/mk-add-local-project-uploads-cleanup-task.yml
deleted file mode 100644
index 9d38b353a41..00000000000
--- a/changelogs/unreleased/mk-add-local-project-uploads-cleanup-task.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add local project uploads cleanup task
-merge_request: 20863
-author:
-type: added
diff --git a/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml b/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml
deleted file mode 100644
index 8e71377d93f..00000000000
--- a/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix namespace move callback behavior, especially to fix Geo replication of namespace moves during certain exceptions.
-merge_request: 19297
-author:
-type: fixed
diff --git a/changelogs/unreleased/more-frozen-string-enable-lib.yml b/changelogs/unreleased/more-frozen-string-enable-lib.yml
new file mode 100644
index 00000000000..9598c53b7fd
--- /dev/null
+++ b/changelogs/unreleased/more-frozen-string-enable-lib.yml
@@ -0,0 +1,5 @@
+---
+title: Enable more frozen string in lib/**/*.rb
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/mr-creation-source-project-filtering.yml b/changelogs/unreleased/mr-creation-source-project-filtering.yml
new file mode 100644
index 00000000000..818101a6f1b
--- /dev/null
+++ b/changelogs/unreleased/mr-creation-source-project-filtering.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed source project not filtering in merge request creation compare form
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/mr-file-list.yml b/changelogs/unreleased/mr-file-list.yml
new file mode 100644
index 00000000000..0a2a5e0c1cc
--- /dev/null
+++ b/changelogs/unreleased/mr-file-list.yml
@@ -0,0 +1,5 @@
+---
+title: Switch between tree list & file list in diffs file browser
+merge_request: 22191
+author:
+type: added
diff --git a/changelogs/unreleased/mr-image-commenting.yml b/changelogs/unreleased/mr-image-commenting.yml
new file mode 100644
index 00000000000..3cc3becc795
--- /dev/null
+++ b/changelogs/unreleased/mr-image-commenting.yml
@@ -0,0 +1,5 @@
+---
+title: Reimplemented image commenting in merge request diffs
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/mr-tree-filter-path-name.yml b/changelogs/unreleased/mr-tree-filter-path-name.yml
new file mode 100644
index 00000000000..152f8a67337
--- /dev/null
+++ b/changelogs/unreleased/mr-tree-filter-path-name.yml
@@ -0,0 +1,5 @@
+---
+title: Changed merge request filtering to be by path instead of name
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/osw-comment-on-any-line-on-diffs.yml b/changelogs/unreleased/osw-comment-on-any-line-on-diffs.yml
new file mode 100644
index 00000000000..e25d64a89d7
--- /dev/null
+++ b/changelogs/unreleased/osw-comment-on-any-line-on-diffs.yml
@@ -0,0 +1,5 @@
+---
+title: Allow commenting on any diff line in Merge Requests
+merge_request: 22914
+author:
+type: added
diff --git a/changelogs/unreleased/osw-fallback-on-blank-refs.yml b/changelogs/unreleased/osw-fallback-on-blank-refs.yml
new file mode 100644
index 00000000000..039179f5829
--- /dev/null
+++ b/changelogs/unreleased/osw-fallback-on-blank-refs.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid Gitaly RPC errors when fetching diff stats
+merge_request: 22995
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-fix-missing-and-duplicated-milestones-on-list.yml b/changelogs/unreleased/osw-fix-missing-and-duplicated-milestones-on-list.yml
deleted file mode 100644
index 62416b7f87e..00000000000
--- a/changelogs/unreleased/osw-fix-missing-and-duplicated-milestones-on-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix missing and duplicates on project milestone listing page
-merge_request: 21058
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-fix-n-plus-1-for-mrs-without-merge-info.yml b/changelogs/unreleased/osw-fix-n-plus-1-for-mrs-without-merge-info.yml
deleted file mode 100644
index dc8148fa1a5..00000000000
--- a/changelogs/unreleased/osw-fix-n-plus-1-for-mrs-without-merge-info.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid N+1 on MRs page when metrics merging date cannot be found
-merge_request: 21053
-author:
-type: performance
diff --git a/changelogs/unreleased/pl-json-gon.yml b/changelogs/unreleased/pl-json-gon.yml
deleted file mode 100644
index c0f93006c07..00000000000
--- a/changelogs/unreleased/pl-json-gon.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Don't set gon variables in JSON requests
-merge_request: 21016
-author: Peter Leitzen
-type: performance
diff --git a/changelogs/unreleased/pl-uprade-prometheus-alertmanager.yml b/changelogs/unreleased/pl-uprade-prometheus-alertmanager.yml
new file mode 100644
index 00000000000..d0c8ed8001d
--- /dev/null
+++ b/changelogs/unreleased/pl-uprade-prometheus-alertmanager.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade Prometheus to 2.4.3 and Alertmanager to 0.15.2
+merge_request: 22600
+author:
+type: other
diff --git a/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml b/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml
deleted file mode 100644
index 6994a238074..00000000000
--- a/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Process commits as normal in forks when the upstream project is deleted
-merge_request: 20534
-author:
-type: fixed
diff --git a/changelogs/unreleased/project-visibility-tooltip.yml b/changelogs/unreleased/project-visibility-tooltip.yml
deleted file mode 100644
index 806c93e493a..00000000000
--- a/changelogs/unreleased/project-visibility-tooltip.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix project visibility tooltip
-merge_request: 20535
-author: Jamie Schembri
-type: fixed
diff --git a/changelogs/unreleased/project_identicon_fix.yml b/changelogs/unreleased/project_identicon_fix.yml
new file mode 100644
index 00000000000..de4876fc4a5
--- /dev/null
+++ b/changelogs/unreleased/project_identicon_fix.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project identicon aligning Harry Kiselev
+merge_request: 23166
+author: Harry Kiselev
+type: other
diff --git a/changelogs/unreleased/rails5-active-record-class-value.yml b/changelogs/unreleased/rails5-active-record-class-value.yml
new file mode 100644
index 00000000000..9f9fdf10cd1
--- /dev/null
+++ b/changelogs/unreleased/rails5-active-record-class-value.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: Passing a class as a value in an Active Record query is deprecated'
+merge_request: 23164
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rails5-deprecated-uniq.yml b/changelogs/unreleased/rails5-deprecated-uniq.yml
new file mode 100644
index 00000000000..69a169100f0
--- /dev/null
+++ b/changelogs/unreleased/rails5-deprecated-uniq.yml
@@ -0,0 +1,5 @@
+---
+title: Replace deprecated uniq on a Relation with distinct
+merge_request: 22625
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rails5-fix-48977.yml b/changelogs/unreleased/rails5-fix-48977.yml
deleted file mode 100644
index bfd86f20e24..00000000000
--- a/changelogs/unreleased/rails5-fix-48977.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix mysql milliseconds problem in specs
-merge_request: 20464
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-delete-blob.yml b/changelogs/unreleased/rails5-fix-delete-blob.yml
new file mode 100644
index 00000000000..ee8304fbdf4
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-delete-blob.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: fix delete blob'
+merge_request: 22456
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rails5-fix-deployment-spec.yml b/changelogs/unreleased/rails5-fix-deployment-spec.yml
new file mode 100644
index 00000000000..9e53c617a54
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-deployment-spec.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: fix deployment model spec'
+merge_request: 22428
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml b/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml
deleted file mode 100644
index e31768773b1..00000000000
--- a/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix specs duplicate key value violates unique constraint 'index_gpg_signatures_on_commit_sha'
-merge_request: 21119
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-flaky-spec-user-uses-shortcuts.yml b/changelogs/unreleased/rails5-fix-flaky-spec-user-uses-shortcuts.yml
deleted file mode 100644
index 5f2504c604d..00000000000
--- a/changelogs/unreleased/rails5-fix-flaky-spec-user-uses-shortcuts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Rails5: fix flaky spec'
-merge_request: 20953
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-revert-modal-spec.yml b/changelogs/unreleased/rails5-fix-revert-modal-spec.yml
deleted file mode 100644
index 0637e503ca9..00000000000
--- a/changelogs/unreleased/rails5-fix-revert-modal-spec.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix user sees revert modal spec
-merge_request: 20706
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-gpg-permit-concurrent.yml b/changelogs/unreleased/rails5-gpg-permit-concurrent.yml
deleted file mode 100644
index cf1b0023f86..00000000000
--- a/changelogs/unreleased/rails5-gpg-permit-concurrent.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Permit concurrent loads in gpg keychain mutex
-merge_request: 20894
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml b/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml
deleted file mode 100644
index afd9865ee45..00000000000
--- a/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 mysql fix milliseconds problem in pull request importer spec
-merge_request: 20475
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-mysql-milliseconds-deployment-spec.yml b/changelogs/unreleased/rails5-mysql-milliseconds-deployment-spec.yml
new file mode 100644
index 00000000000..8c71ecebfdb
--- /dev/null
+++ b/changelogs/unreleased/rails5-mysql-milliseconds-deployment-spec.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: fix mysql milliseconds issue in deployment model specs'
+merge_request: 22850
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rails5-mysql-rename-column.yml b/changelogs/unreleased/rails5-mysql-rename-column.yml
deleted file mode 100644
index cbae9250744..00000000000
--- a/changelogs/unreleased/rails5-mysql-rename-column.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 MySQL fix rename_column as part of cleanup_concurrent_column_type_change
-merge_request: 20514
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-mysql-schedule-build.yml b/changelogs/unreleased/rails5-mysql-schedule-build.yml
new file mode 100644
index 00000000000..cbc481fbf89
--- /dev/null
+++ b/changelogs/unreleased/rails5-mysql-schedule-build.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails 5: fix mysql milliseconds problems in scheduled build specs'
+merge_request: 22170
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rails5-update-gemfile-lock-2.yml b/changelogs/unreleased/rails5-update-gemfile-lock-2.yml
deleted file mode 100644
index 1f3e9bd2238..00000000000
--- a/changelogs/unreleased/rails5-update-gemfile-lock-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 update Gemfile.rails5.lock
-merge_request: 20858
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-update-gemfile-lock.yml b/changelogs/unreleased/rails5-update-gemfile-lock.yml
deleted file mode 100644
index 58931587fff..00000000000
--- a/changelogs/unreleased/rails5-update-gemfile-lock.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Gemfile.rails5.lock with latest Gemfile.lock changes
-merge_request: 20466
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-update-rouge.yml b/changelogs/unreleased/rails5-update-rouge.yml
deleted file mode 100644
index 1173b3b7e9a..00000000000
--- a/changelogs/unreleased/rails5-update-rouge.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Rails5: update Rails5 lock for forgotten gem rouge'
-merge_request: 21010
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-user-status-spec.yml b/changelogs/unreleased/rails5-user-status-spec.yml
new file mode 100644
index 00000000000..818d480e9fc
--- /dev/null
+++ b/changelogs/unreleased/rails5-user-status-spec.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: fix user edit profile clear status spec'
+merge_request: 22169
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rake-gitaly-check.yml b/changelogs/unreleased/rake-gitaly-check.yml
new file mode 100644
index 00000000000..90fbd62d203
--- /dev/null
+++ b/changelogs/unreleased/rake-gitaly-check.yml
@@ -0,0 +1,5 @@
+---
+title: Add gitlab:gitaly:check task for Gitaly health check
+merge_request: 22063
+author:
+type: other
diff --git a/changelogs/unreleased/ravlen-deploy-tokens-display-update.yml b/changelogs/unreleased/ravlen-deploy-tokens-display-update.yml
deleted file mode 100644
index fd5a6521882..00000000000
--- a/changelogs/unreleased/ravlen-deploy-tokens-display-update.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Cleans up display of Deploy Tokens to match Personal Access Tokens"
-merge_request: 20578
-author: Marcel Amirault
-type: added \ No newline at end of file
diff --git a/changelogs/unreleased/ravlen-rename-secret-variables-in-codebase.yml b/changelogs/unreleased/ravlen-rename-secret-variables-in-codebase.yml
new file mode 100644
index 00000000000..211d51a3d43
--- /dev/null
+++ b/changelogs/unreleased/ravlen-rename-secret-variables-in-codebase.yml
@@ -0,0 +1,5 @@
+---
+title: "Secret Variables renamed to CI Variables in the codebase, to match UX"
+merge_request: 22414
+author: Marcel Amirault @ravlen
+type: changed \ No newline at end of file
diff --git a/changelogs/unreleased/redact-links-dev.yml b/changelogs/unreleased/redact-links-dev.yml
new file mode 100644
index 00000000000..338e7965465
--- /dev/null
+++ b/changelogs/unreleased/redact-links-dev.yml
@@ -0,0 +1,5 @@
+---
+title: Redact personal tokens in unsubscribe links.
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/refactor-snippets-finder.yml b/changelogs/unreleased/refactor-snippets-finder.yml
new file mode 100644
index 00000000000..37cacf71c14
--- /dev/null
+++ b/changelogs/unreleased/refactor-snippets-finder.yml
@@ -0,0 +1,5 @@
+---
+title: Rewrite SnippetsFinder to improve performance by a factor of 1500
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/regen-2fa-codes.yml b/changelogs/unreleased/regen-2fa-codes.yml
deleted file mode 100644
index 596f759df0f..00000000000
--- a/changelogs/unreleased/regen-2fa-codes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added button to regenerate 2FA codes
-merge_request:
-author: Luke Picciau
-type: added
diff --git a/changelogs/unreleased/related_mrs.yml b/changelogs/unreleased/related_mrs.yml
new file mode 100644
index 00000000000..cc89e9d0cdb
--- /dev/null
+++ b/changelogs/unreleased/related_mrs.yml
@@ -0,0 +1,5 @@
+---
+title: Add API endpoint to list issue related merge requests
+merge_request: 21806
+author: Helmut Januschka
+type: added
diff --git a/changelogs/unreleased/remove-asset-sync.yml b/changelogs/unreleased/remove-asset-sync.yml
new file mode 100644
index 00000000000..ddb82212975
--- /dev/null
+++ b/changelogs/unreleased/remove-asset-sync.yml
@@ -0,0 +1,5 @@
+---
+title: Remove asset_sync gem from Gemfile and related code from codebase
+merge_request: 22610
+author:
+type: other
diff --git a/changelogs/unreleased/remove-ci_enable_scheduled_build-feature-flag.yml b/changelogs/unreleased/remove-ci_enable_scheduled_build-feature-flag.yml
new file mode 100644
index 00000000000..ce52a487551
--- /dev/null
+++ b/changelogs/unreleased/remove-ci_enable_scheduled_build-feature-flag.yml
@@ -0,0 +1,5 @@
+---
+title: Remove `ci_enable_scheduled_build` feature flag
+merge_request: 22742
+author:
+type: other
diff --git a/changelogs/unreleased/remove-duplicate-primary-button-in-dashboard-snippets.yml b/changelogs/unreleased/remove-duplicate-primary-button-in-dashboard-snippets.yml
new file mode 100644
index 00000000000..3a8b3a0df5d
--- /dev/null
+++ b/changelogs/unreleased/remove-duplicate-primary-button-in-dashboard-snippets.yml
@@ -0,0 +1,5 @@
+---
+title: Remove duplicate primary button in dashboard snippets on small viewports
+merge_request: 22902
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/remove-experimental-label-from-cluster-views.yml b/changelogs/unreleased/remove-experimental-label-from-cluster-views.yml
new file mode 100644
index 00000000000..af9512b27e9
--- /dev/null
+++ b/changelogs/unreleased/remove-experimental-label-from-cluster-views.yml
@@ -0,0 +1,5 @@
+---
+title: Removes experimental labels from cluster views
+merge_request: 22550
+author:
+type: other
diff --git a/changelogs/unreleased/rename-scheduled-label-badges.yml b/changelogs/unreleased/rename-scheduled-label-badges.yml
new file mode 100644
index 00000000000..f9ee17a98a4
--- /dev/null
+++ b/changelogs/unreleased/rename-scheduled-label-badges.yml
@@ -0,0 +1,5 @@
+---
+title: Rename "scheduled" label/badge of delayed jobs to "delayed"
+merge_request: 22245
+author:
+type: changed
diff --git a/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml b/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml
deleted file mode 100644
index 8d5ecdfa57e..00000000000
--- a/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace snake case in SCSS variables
-merge_request: 20799
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/replace-i-to-icons-in-vue-components.yml b/changelogs/unreleased/replace-i-to-icons-in-vue-components.yml
new file mode 100644
index 00000000000..6de57b04338
--- /dev/null
+++ b/changelogs/unreleased/replace-i-to-icons-in-vue-components.yml
@@ -0,0 +1,5 @@
+---
+title: Replace i to icons in vue components
+merge_request: 20748
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/replace-snake-case-css-classes.yml b/changelogs/unreleased/replace-snake-case-css-classes.yml
deleted file mode 100644
index 28ec5ee097f..00000000000
--- a/changelogs/unreleased/replace-snake-case-css-classes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace author_link snake case in stylesheets, specs, and helpers
-merge_request: 20797
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/replace-tooltip-in-markdown-component.yml b/changelogs/unreleased/replace-tooltip-in-markdown-component.yml
new file mode 100644
index 00000000000..5047e75c06a
--- /dev/null
+++ b/changelogs/unreleased/replace-tooltip-in-markdown-component.yml
@@ -0,0 +1,5 @@
+---
+title: Replace tooltip in markdown component with gl-tooltip
+merge_request: 21989
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/rouge-3-2-0.yml b/changelogs/unreleased/rouge-3-2-0.yml
deleted file mode 100644
index 15ac4cc1e76..00000000000
--- a/changelogs/unreleased/rouge-3-2-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update to Rouge 3.2.0, including Terraform and Crystal lexer and bug fixes
-merge_request: 20991
-author:
-type: changed
diff --git a/changelogs/unreleased/rs-cherry-pick-api.yml b/changelogs/unreleased/rs-cherry-pick-api.yml
new file mode 100644
index 00000000000..ce844dfc939
--- /dev/null
+++ b/changelogs/unreleased/rs-cherry-pick-api.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve possible cherry pick API race condition
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/rs-revert-api.yml b/changelogs/unreleased/rs-revert-api.yml
new file mode 100644
index 00000000000..c07b2fe624c
--- /dev/null
+++ b/changelogs/unreleased/rs-revert-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add revert to commits API
+merge_request: 22919
+author:
+type: added
diff --git a/changelogs/unreleased/runner-features.yml b/changelogs/unreleased/runner-features.yml
deleted file mode 100644
index c5e0fff5a18..00000000000
--- a/changelogs/unreleased/runner-features.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Verify runner feature set
-merge_request: 20664
-author:
-type: added
diff --git a/changelogs/unreleased/runners-max-timeout-param.yml b/changelogs/unreleased/runners-max-timeout-param.yml
deleted file mode 100644
index 875f805d849..00000000000
--- a/changelogs/unreleased/runners-max-timeout-param.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add missing maximum_timeout parameter
-merge_request: 20355
-author: gfyoung
-type: fixed
diff --git a/changelogs/unreleased/rz_fix_milestone_count.yml b/changelogs/unreleased/rz_fix_milestone_count.yml
new file mode 100644
index 00000000000..1013b88e0bc
--- /dev/null
+++ b/changelogs/unreleased/rz_fix_milestone_count.yml
@@ -0,0 +1,5 @@
+---
+title: Fixing count on Milestones
+merge_request: 21446
+author:
+type: fixed
diff --git a/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml b/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml
deleted file mode 100644
index 7bfe1b5778f..00000000000
--- a/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Include full image URL in webhooks for uploaded images
-merge_request: 18109
-author: Satish Perala
-type: changed
diff --git a/changelogs/unreleased/scheduled-manual-jobs-environment-play-buttons.yml b/changelogs/unreleased/scheduled-manual-jobs-environment-play-buttons.yml
new file mode 100644
index 00000000000..c89af78d989
--- /dev/null
+++ b/changelogs/unreleased/scheduled-manual-jobs-environment-play-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Add the Play button for delayed jobs in environment page
+merge_request: 22106
+author:
+type: added
diff --git a/changelogs/unreleased/security-2717-fix-issue-title-xss.yml b/changelogs/unreleased/security-2717-fix-issue-title-xss.yml
new file mode 100644
index 00000000000..f2e638e5ab5
--- /dev/null
+++ b/changelogs/unreleased/security-2717-fix-issue-title-xss.yml
@@ -0,0 +1,5 @@
+---
+title: Escape entity title while autocomplete template rendering to prevent XSS
+merge_request: 2556
+author:
+type: security
diff --git a/changelogs/unreleased/security-2717-xss-username-autocomplete.yml b/changelogs/unreleased/security-2717-xss-username-autocomplete.yml
new file mode 100644
index 00000000000..d9b1015eeb4
--- /dev/null
+++ b/changelogs/unreleased/security-2717-xss-username-autocomplete.yml
@@ -0,0 +1,5 @@
+---
+title: Escape user fullname while rendering autocomplete template to prevent XSS
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-51113-hash_personal_access_tokens.yml b/changelogs/unreleased/security-51113-hash_personal_access_tokens.yml
new file mode 100644
index 00000000000..4cebe814148
--- /dev/null
+++ b/changelogs/unreleased/security-51113-hash_personal_access_tokens.yml
@@ -0,0 +1,5 @@
+---
+title: Persist only SHA digest of PersonalAccessToken#token
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml
deleted file mode 100644
index fabf48acbbc..00000000000
--- a/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adding CSRF protection to Hooks test action
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-ide-branch-name-xss.yml b/changelogs/unreleased/security-ide-branch-name-xss.yml
deleted file mode 100644
index 51742ffa4e9..00000000000
--- a/changelogs/unreleased/security-ide-branch-name-xss.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed XSS in branch name in Web IDE
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-kubeclient-ssrf.yml b/changelogs/unreleased/security-kubeclient-ssrf.yml
new file mode 100644
index 00000000000..45fc41029fc
--- /dev/null
+++ b/changelogs/unreleased/security-kubeclient-ssrf.yml
@@ -0,0 +1,5 @@
+---
+title: Monkey kubeclient to not follow any redirects.
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/sh-53180-append-path.yml b/changelogs/unreleased/sh-53180-append-path.yml
new file mode 100644
index 00000000000..64fae5522d8
--- /dev/null
+++ b/changelogs/unreleased/sh-53180-append-path.yml
@@ -0,0 +1,5 @@
+---
+title: Make sure there's only one slash as path separator
+merge_request: 22954
+author:
+type: other
diff --git a/changelogs/unreleased/sh-add-audit-logging-json-ce.yml b/changelogs/unreleased/sh-add-audit-logging-json-ce.yml
new file mode 100644
index 00000000000..3c0a27da269
--- /dev/null
+++ b/changelogs/unreleased/sh-add-audit-logging-json-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for JSON logging for audit events
+merge_request: 22471
+author:
+type: added
diff --git a/changelogs/unreleased/sh-associate-rakefile-ruby.yml b/changelogs/unreleased/sh-associate-rakefile-ruby.yml
new file mode 100644
index 00000000000..3e3fcb8d860
--- /dev/null
+++ b/changelogs/unreleased/sh-associate-rakefile-ruby.yml
@@ -0,0 +1,5 @@
+---
+title: Associate Rakefile with Ruby icon in diffs
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/sh-bump-gems-security.yml b/changelogs/unreleased/sh-bump-gems-security.yml
new file mode 100644
index 00000000000..06489f6f979
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-gems-security.yml
@@ -0,0 +1,5 @@
+---
+title: Bump nokogiri, loofah, and rack gems for security updates
+merge_request: 23204
+author:
+type: security
diff --git a/changelogs/unreleased/sh-bump-gitaly-0-117.yml b/changelogs/unreleased/sh-bump-gitaly-0-117.yml
deleted file mode 100644
index 90ca86d076b..00000000000
--- a/changelogs/unreleased/sh-bump-gitaly-0-117.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump Gitaly to 0.117.0
-merge_request: 21055
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-bump-haml-5-0-4.yml b/changelogs/unreleased/sh-bump-haml-5-0-4.yml
deleted file mode 100644
index 269b1e55417..00000000000
--- a/changelogs/unreleased/sh-bump-haml-5-0-4.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump haml gem to 5.0.4
-merge_request: 20847
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-bump-ruby-2-5-3.yml b/changelogs/unreleased/sh-bump-ruby-2-5-3.yml
new file mode 100644
index 00000000000..13cadc73e9c
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-ruby-2-5-3.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade to Ruby 2.5.3
+merge_request: 2806
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-bump-rugged-0-27-4.yml b/changelogs/unreleased/sh-bump-rugged-0-27-4.yml
deleted file mode 100644
index 50373cb81ad..00000000000
--- a/changelogs/unreleased/sh-bump-rugged-0-27-4.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump rugged to 0.27.4 for security fixes
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml
deleted file mode 100644
index b9444440cb9..00000000000
--- a/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump nokogiri to 1.8.4 and sanitize to 4.6.6 for performance
-merge_request: 20795
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
deleted file mode 100644
index 897d673e97d..00000000000
--- a/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen strings in remaining lib/banzai/filter/*.rb files
-merge_request: 20777
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-fix-admin-jobs-controller-timing-out.yml b/changelogs/unreleased/sh-fix-admin-jobs-controller-timing-out.yml
deleted file mode 100644
index e1adebbf076..00000000000
--- a/changelogs/unreleased/sh-fix-admin-jobs-controller-timing-out.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix /admin/jobs failing to load due to statement timeout
-merge_request: 20909
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml b/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml
deleted file mode 100644
index 3f7044833f1..00000000000
--- a/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Bitbucket Cloud importer omitting replies
-merge_request: 21076
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-commit-signatures-error.yml b/changelogs/unreleased/sh-fix-commit-signatures-error.yml
new file mode 100644
index 00000000000..e2ea0e5857e
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-commit-signatures-error.yml
@@ -0,0 +1,5 @@
+---
+title: Fix commit signature error when project is disabled
+merge_request: 22344
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-hipchat-ssrf.yml b/changelogs/unreleased/sh-fix-hipchat-ssrf.yml
new file mode 100644
index 00000000000..cdc95a34fcf
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-hipchat-ssrf.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent SSRF attacks in HipChat integration
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/sh-fix-issue-47797-ce.yml b/changelogs/unreleased/sh-fix-issue-47797-ce.yml
deleted file mode 100644
index 456d96acacb..00000000000
--- a/changelogs/unreleased/sh-fix-issue-47797-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix handling of annotated tags when Gitaly is not in use
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-49133.yml b/changelogs/unreleased/sh-fix-issue-49133.yml
deleted file mode 100644
index 847220d88b2..00000000000
--- a/changelogs/unreleased/sh-fix-issue-49133.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix symlink vulnerability in project import
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/sh-fix-issue-52176.yml b/changelogs/unreleased/sh-fix-issue-52176.yml
new file mode 100644
index 00000000000..7269e14d910
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-52176.yml
@@ -0,0 +1,5 @@
+---
+title: Disable replication lag check for Aurora PostgreSQL databases
+merge_request: 22786
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-52649.yml b/changelogs/unreleased/sh-fix-issue-52649.yml
new file mode 100644
index 00000000000..34b7f74a345
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-52649.yml
@@ -0,0 +1,5 @@
+---
+title: Fix statement timeouts in RemoveRestrictedTodos migration
+merge_request: 22795
+author:
+type: other
diff --git a/changelogs/unreleased/sh-fix-issue-53153.yml b/changelogs/unreleased/sh-fix-issue-53153.yml
new file mode 100644
index 00000000000..ee51631f959
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-53153.yml
@@ -0,0 +1,5 @@
+---
+title: Fix extra merge request versions created from forked merge requests
+merge_request: 22611
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-53783-ce.yml b/changelogs/unreleased/sh-fix-issue-53783-ce.yml
new file mode 100644
index 00000000000..10be1d81768
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-53783-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Fix enabling project deploy key for admins
+merge_request: 23043
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-54189.yml b/changelogs/unreleased/sh-fix-issue-54189.yml
new file mode 100644
index 00000000000..eee743aa5d9
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-54189.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent templated services from being imported
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/sh-fix-search-relative-urls.yml b/changelogs/unreleased/sh-fix-search-relative-urls.yml
new file mode 100644
index 00000000000..2545e9ca553
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-search-relative-urls.yml
@@ -0,0 +1,5 @@
+---
+title: Fix search "all in GitLab" not working with relative URLs
+merge_request: 22644
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml b/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml
deleted file mode 100644
index b7366cf2569..00000000000
--- a/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid process deadlock in popen by consuming input pipes
-merge_request: 20600
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-wiki-security-issue-53072.yml b/changelogs/unreleased/sh-fix-wiki-security-issue-53072.yml
new file mode 100644
index 00000000000..ac6ab7cc3f4
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-wiki-security-issue-53072.yml
@@ -0,0 +1,5 @@
+---
+title: Validate Wiki attachments are valid temporary files
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
deleted file mode 100644
index 37b397ea49f..00000000000
--- a/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen strings in lib/banzai/filter/*.rb
-merge_request: 20775
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml b/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml
deleted file mode 100644
index 7717d0aab37..00000000000
--- a/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Properly handle colons in URL passwords
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-handle-invalid-comparison.yml b/changelogs/unreleased/sh-handle-invalid-comparison.yml
new file mode 100644
index 00000000000..30b5b3d8198
--- /dev/null
+++ b/changelogs/unreleased/sh-handle-invalid-comparison.yml
@@ -0,0 +1,5 @@
+---
+title: Reject invalid branch names in repository compare controller
+merge_request: 22186
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-include-rbtrace.yml b/changelogs/unreleased/sh-include-rbtrace.yml
deleted file mode 100644
index 41f0655e3f8..00000000000
--- a/changelogs/unreleased/sh-include-rbtrace.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add rbtrace to Gemfile
-merge_request: 20831
-author:
-type: other
diff --git a/changelogs/unreleased/sh-lfs-fix-content-type.yml b/changelogs/unreleased/sh-lfs-fix-content-type.yml
deleted file mode 100644
index a839be9b3ae..00000000000
--- a/changelogs/unreleased/sh-lfs-fix-content-type.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix LFS uploads not working with git-lfs 2.5.0
-merge_request: 20923
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-limit-unauthenticated-session-times.yml b/changelogs/unreleased/sh-limit-unauthenticated-session-times.yml
deleted file mode 100644
index 44a46b4115e..00000000000
--- a/changelogs/unreleased/sh-limit-unauthenticated-session-times.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Limit the TTL for anonymous sessions to 1 hour
-merge_request: 20700
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-normalize-urls.yml b/changelogs/unreleased/sh-normalize-urls.yml
deleted file mode 100644
index b0d1120e10b..00000000000
--- a/changelogs/unreleased/sh-normalize-urls.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Escape username and password in UrlSanitizer#full_url
-merge_request: 20684
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-optimize-merge-request-project-lookup.yml b/changelogs/unreleased/sh-optimize-merge-request-project-lookup.yml
new file mode 100644
index 00000000000..241b89c4633
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-merge-request-project-lookup.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce SQL queries needed to load open merge requests
+merge_request: 22709
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-optimize-mr-commit-sha-lookup.yml b/changelogs/unreleased/sh-optimize-mr-commit-sha-lookup.yml
new file mode 100644
index 00000000000..bea73f8d329
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-mr-commit-sha-lookup.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize merge request refresh by using the database to check commit SHAs
+merge_request: 22731
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-optimize-reload-diffs-service.yml b/changelogs/unreleased/sh-optimize-reload-diffs-service.yml
new file mode 100644
index 00000000000..422102560ed
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-reload-diffs-service.yml
@@ -0,0 +1,5 @@
+---
+title: Significantly cut memory usage and SQL queries when reloading diffs
+merge_request: 22725
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-optimize-wiki-empty-check.yml b/changelogs/unreleased/sh-optimize-wiki-empty-check.yml
deleted file mode 100644
index 31ca7497b5a..00000000000
--- a/changelogs/unreleased/sh-optimize-wiki-empty-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Optimize ProjectWiki#empty? check
-merge_request: 20573
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-pages-eof-error.yml b/changelogs/unreleased/sh-pages-eof-error.yml
new file mode 100644
index 00000000000..497a74c1458
--- /dev/null
+++ b/changelogs/unreleased/sh-pages-eof-error.yml
@@ -0,0 +1,5 @@
+---
+title: Fix EOF detection with CI artifacts metadata
+merge_request: 22479
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-paginate-bitbucket-server-imports.yml b/changelogs/unreleased/sh-paginate-bitbucket-server-imports.yml
new file mode 100644
index 00000000000..b5743e71cf9
--- /dev/null
+++ b/changelogs/unreleased/sh-paginate-bitbucket-server-imports.yml
@@ -0,0 +1,5 @@
+---
+title: Paginate Bitbucket Server importer projects
+merge_request: 22825
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-remove-banzai-instrumentation.yml b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml
deleted file mode 100644
index 8bb3cd5942b..00000000000
--- a/changelogs/unreleased/sh-remove-banzai-instrumentation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove method instrumentation for Banzai filters and reference parsers
-merge_request: 20770
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-remove-koding.yml b/changelogs/unreleased/sh-remove-koding.yml
new file mode 100644
index 00000000000..2c4e8c76a61
--- /dev/null
+++ b/changelogs/unreleased/sh-remove-koding.yml
@@ -0,0 +1,5 @@
+---
+title: Remove Koding integration and documentation
+merge_request: 22334
+author:
+type: removed
diff --git a/changelogs/unreleased/sh-remove-local-sidekiq-admin-check.yml b/changelogs/unreleased/sh-remove-local-sidekiq-admin-check.yml
new file mode 100644
index 00000000000..3ec15908fc7
--- /dev/null
+++ b/changelogs/unreleased/sh-remove-local-sidekiq-admin-check.yml
@@ -0,0 +1,5 @@
+---
+title: Remove display of local Sidekiq process in /admin/sidekiq
+merge_request: 23118
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-simplify-liveness-check.yml b/changelogs/unreleased/sh-simplify-liveness-check.yml
deleted file mode 100644
index 225e3dc1378..00000000000
--- a/changelogs/unreleased/sh-simplify-liveness-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add /-/health basic health check endpoint
-merge_request: 20456
-author:
-type: added
diff --git a/changelogs/unreleased/sh-strip-github-pat-whitespace.yml b/changelogs/unreleased/sh-strip-github-pat-whitespace.yml
new file mode 100644
index 00000000000..ea26f57e8f0
--- /dev/null
+++ b/changelogs/unreleased/sh-strip-github-pat-whitespace.yml
@@ -0,0 +1,5 @@
+---
+title: Strip whitespace around GitHub personal access tokens
+merge_request: 22432
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-support-users-find-by-confirmed-emails.yml b/changelogs/unreleased/sh-support-users-find-by-confirmed-emails.yml
deleted file mode 100644
index 4b0c8117b3e..00000000000
--- a/changelogs/unreleased/sh-support-users-find-by-confirmed-emails.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for searching users by confirmed e-mails
-merge_request: 20893
-author:
-type: other
diff --git a/changelogs/unreleased/sh-use-nakayoshi-fork.yml b/changelogs/unreleased/sh-use-nakayoshi-fork.yml
new file mode 100644
index 00000000000..5977d9b0974
--- /dev/null
+++ b/changelogs/unreleased/sh-use-nakayoshi-fork.yml
@@ -0,0 +1,5 @@
+---
+title: Improve memory performance by reducing dirty pages after fork()
+merge_request: 23169
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-use-nokogiri-xml-backend.yml b/changelogs/unreleased/sh-use-nokogiri-xml-backend.yml
new file mode 100644
index 00000000000..6a82e32c416
--- /dev/null
+++ b/changelogs/unreleased/sh-use-nokogiri-xml-backend.yml
@@ -0,0 +1,5 @@
+---
+title: Use Nokogiri as the ActiveSupport XML backend
+merge_request: 23136
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml b/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml
deleted file mode 100644
index e8c2e11ad31..00000000000
--- a/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use limit parameter to retrieve Wikis from Gitaly
-merge_request: 20764
-author:
-type: performance
diff --git a/changelogs/unreleased/stateful_deployments.yml b/changelogs/unreleased/stateful_deployments.yml
new file mode 100644
index 00000000000..4caa5ad77b8
--- /dev/null
+++ b/changelogs/unreleased/stateful_deployments.yml
@@ -0,0 +1,5 @@
+---
+title: Add status to Deployment
+merge_request: 22380
+author:
+type: changed
diff --git a/changelogs/unreleased/stop-dynamic-routable-creation.yml b/changelogs/unreleased/stop-dynamic-routable-creation.yml
deleted file mode 100644
index 8bfcb5b2d11..00000000000
--- a/changelogs/unreleased/stop-dynamic-routable-creation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Stop dynamically creating project and namespace routes
-merge_request: 20313
-author:
-type: performance
diff --git a/changelogs/unreleased/support-license-management-and-performance.yml b/changelogs/unreleased/support-license-management-and-performance.yml
new file mode 100644
index 00000000000..2e65dba5e76
--- /dev/null
+++ b/changelogs/unreleased/support-license-management-and-performance.yml
@@ -0,0 +1,5 @@
+---
+title: Support licenses and performance
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/switch-rails.yml b/changelogs/unreleased/switch-rails.yml
new file mode 100644
index 00000000000..4edf709dbd4
--- /dev/null
+++ b/changelogs/unreleased/switch-rails.yml
@@ -0,0 +1,5 @@
+---
+title: Switch to Rails 5
+merge_request: 21492
+author:
+type: other
diff --git a/changelogs/unreleased/tc-index-lfs-objects-file-store.yml b/changelogs/unreleased/tc-index-lfs-objects-file-store.yml
new file mode 100644
index 00000000000..90e80cb1ef1
--- /dev/null
+++ b/changelogs/unreleased/tc-index-lfs-objects-file-store.yml
@@ -0,0 +1,5 @@
+---
+title: Enhance performance of counting local LFS objects
+merge_request: 22143
+author:
+type: performance
diff --git a/changelogs/unreleased/tc-index-uploads-file-store.yml b/changelogs/unreleased/tc-index-uploads-file-store.yml
new file mode 100644
index 00000000000..fa3b3164e38
--- /dev/null
+++ b/changelogs/unreleased/tc-index-uploads-file-store.yml
@@ -0,0 +1,5 @@
+---
+title: Enhance performance of counting local Uploads
+merge_request: 22522
+author:
+type: performance
diff --git a/changelogs/unreleased/tc-reorder-mail-notify-references.yml b/changelogs/unreleased/tc-reorder-mail-notify-references.yml
deleted file mode 100644
index 689afda0259..00000000000
--- a/changelogs/unreleased/tc-reorder-mail-notify-references.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Put fallback reply-key address first in the References header
-merge_request: 20871
-author:
-type: changed
diff --git a/changelogs/unreleased/test-usage-ping-in-timeout-case.yml b/changelogs/unreleased/test-usage-ping-in-timeout-case.yml
new file mode 100644
index 00000000000..daee98765ac
--- /dev/null
+++ b/changelogs/unreleased/test-usage-ping-in-timeout-case.yml
@@ -0,0 +1,5 @@
+---
+title: Fix auto-corrected upload URLs in webhooks
+merge_request: 22361
+author:
+type: fixed
diff --git a/changelogs/unreleased/todos-visibility-change.yml b/changelogs/unreleased/todos-visibility-change.yml
deleted file mode 100644
index b7632b94771..00000000000
--- a/changelogs/unreleased/todos-visibility-change.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Delete todos when user loses access to read the target
-merge_request: 20665
-author:
-type: other
diff --git a/changelogs/unreleased/todos-visibility-migration.yml b/changelogs/unreleased/todos-visibility-migration.yml
deleted file mode 100644
index 651facc4ec8..00000000000
--- a/changelogs/unreleased/todos-visibility-migration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove todos of users without access to targets migration
-merge_request: 20927
-author:
-type: other
diff --git a/changelogs/unreleased/toggle-password-cluster.yml b/changelogs/unreleased/toggle-password-cluster.yml
deleted file mode 100644
index 1a43c4baa25..00000000000
--- a/changelogs/unreleased/toggle-password-cluster.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Toggle Show / Hide Button for Kubernetes Password
-merge_request: 20659
-author: gfyoung
-type: fixed
diff --git a/changelogs/unreleased/toggle-sidebar-alignment.yml b/changelogs/unreleased/toggle-sidebar-alignment.yml
new file mode 100644
index 00000000000..428fe61da9b
--- /dev/null
+++ b/changelogs/unreleased/toggle-sidebar-alignment.yml
@@ -0,0 +1,5 @@
+---
+title: Align toggle sidebar button across all browsers and OSs
+merge_request: 22771
+author:
+type: fixed
diff --git a/changelogs/unreleased/top_level_clusters_controller.yml b/changelogs/unreleased/top_level_clusters_controller.yml
new file mode 100644
index 00000000000..1fe1d048de4
--- /dev/null
+++ b/changelogs/unreleased/top_level_clusters_controller.yml
@@ -0,0 +1,6 @@
+---
+title: Change to top level controller for clusters so that we can use it for project
+ clusters (now) and group clusters (later)
+merge_request: 22438
+author:
+type: other
diff --git a/changelogs/unreleased/triggermesh-phase2-serverless.yml b/changelogs/unreleased/triggermesh-phase2-serverless.yml
new file mode 100644
index 00000000000..bee2b5e1e2c
--- /dev/null
+++ b/changelogs/unreleased/triggermesh-phase2-serverless.yml
@@ -0,0 +1,5 @@
+---
+title: Add knative client to kubeclient library
+merge_request: 22968
+author: cab105
+type: added
diff --git a/changelogs/unreleased/tweak-sql-buckets.yml b/changelogs/unreleased/tweak-sql-buckets.yml
deleted file mode 100644
index 00a0f733ee1..00000000000
--- a/changelogs/unreleased/tweak-sql-buckets.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add a 10 ms bucket for SQL timings
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/tz-mr-port-memory-fixes.yml b/changelogs/unreleased/tz-mr-port-memory-fixes.yml
deleted file mode 100644
index 61d3c9abf71..00000000000
--- a/changelogs/unreleased/tz-mr-port-memory-fixes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve performance and memory footprint of Changes tab of Merge Requests
-merge_request: 21028
-author:
-type: performance
diff --git a/changelogs/unreleased/update-card-body-style.yml b/changelogs/unreleased/update-card-body-style.yml
deleted file mode 100644
index d9197c18502..00000000000
--- a/changelogs/unreleased/update-card-body-style.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove background color from card-body style
-merge_request: 20689
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/update-issue-closing-pattern.yml b/changelogs/unreleased/update-issue-closing-pattern.yml
deleted file mode 100644
index 95488adf449..00000000000
--- a/changelogs/unreleased/update-issue-closing-pattern.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update issue closing pattern
-merge_request: 20554
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/update-readme-ruby-version.yml b/changelogs/unreleased/update-readme-ruby-version.yml
new file mode 100644
index 00000000000..524b8112d4f
--- /dev/null
+++ b/changelogs/unreleased/update-readme-ruby-version.yml
@@ -0,0 +1,5 @@
+---
+title: Update Ruby version in README
+merge_request: 22466
+author: J.D. Bean
+type: changed
diff --git a/changelogs/unreleased/update-runner-chart-to-0-1-34.yml b/changelogs/unreleased/update-runner-chart-to-0-1-34.yml
new file mode 100644
index 00000000000..ebd34bb86b8
--- /dev/null
+++ b/changelogs/unreleased/update-runner-chart-to-0-1-34.yml
@@ -0,0 +1,5 @@
+---
+title: Update used version of Runner Helm Chart to 0.1.34
+merge_request: 22274
+author:
+type: other
diff --git a/changelogs/unreleased/update-runner-chart-to-0-1-35.yml b/changelogs/unreleased/update-runner-chart-to-0-1-35.yml
new file mode 100644
index 00000000000..3b8029c8d96
--- /dev/null
+++ b/changelogs/unreleased/update-runner-chart-to-0-1-35.yml
@@ -0,0 +1,5 @@
+---
+title: Update used version of Runner Helm Chart to 0.1.35
+merge_request: 22541
+author:
+type: other
diff --git a/changelogs/unreleased/update-specific-runners-help-url.yml b/changelogs/unreleased/update-specific-runners-help-url.yml
deleted file mode 100644
index 0ccbc3b2d65..00000000000
--- a/changelogs/unreleased/update-specific-runners-help-url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update specific runners help URL
-merge_request: 20213
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/update_license_management_job.yml b/changelogs/unreleased/update_license_management_job.yml
new file mode 100644
index 00000000000..d6e56080e77
--- /dev/null
+++ b/changelogs/unreleased/update_license_management_job.yml
@@ -0,0 +1,5 @@
+---
+title: "Remove dind from license_management auto-devops job definition"
+merge_request: 22732
+author:
+type: performance
diff --git a/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml b/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml
deleted file mode 100644
index 39e10121507..00000000000
--- a/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Update hamlit to fix ruby 2.5 incompatibilities, fixes #42045'
-merge_request:
-author: Matthew Dawson
-type: fixed
diff --git a/changelogs/unreleased/upgrade-workhorse-7-1-0.yml b/changelogs/unreleased/upgrade-workhorse-7-1-0.yml
new file mode 100644
index 00000000000..b6df35e6d10
--- /dev/null
+++ b/changelogs/unreleased/upgrade-workhorse-7-1-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab-Workhorse to v7.1.0
+merge_request: 22883
+author:
+type: other
diff --git a/changelogs/unreleased/use-raw-file-format.yml b/changelogs/unreleased/use-raw-file-format.yml
new file mode 100644
index 00000000000..d86db51fea4
--- /dev/null
+++ b/changelogs/unreleased/use-raw-file-format.yml
@@ -0,0 +1,5 @@
+---
+title: Make all legacy security reports to use raw format
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/validate-foreign-keys-being-indexed.yml b/changelogs/unreleased/validate-foreign-keys-being-indexed.yml
new file mode 100644
index 00000000000..6608a93c08f
--- /dev/null
+++ b/changelogs/unreleased/validate-foreign-keys-being-indexed.yml
@@ -0,0 +1,5 @@
+---
+title: Validate foreign keys being created and indexed for column with _id
+merge_request: 22808
+author:
+type: performance
diff --git a/changelogs/unreleased/visual-improvements-language-bar.yml b/changelogs/unreleased/visual-improvements-language-bar.yml
deleted file mode 100644
index 23cae22b962..00000000000
--- a/changelogs/unreleased/visual-improvements-language-bar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve visuals of language bar on projects
-merge_request: 21006
-author:
-type: changed
diff --git a/changelogs/unreleased/winh-delayed-jobs-dynamic-timer.yml b/changelogs/unreleased/winh-delayed-jobs-dynamic-timer.yml
new file mode 100644
index 00000000000..fbedd2796b2
--- /dev/null
+++ b/changelogs/unreleased/winh-delayed-jobs-dynamic-timer.yml
@@ -0,0 +1,5 @@
+---
+title: Add dynamic timer to delayed jobs
+merge_request: 22382
+author:
+type: changed
diff --git a/changelogs/unreleased/winh-fix-gpg-regressions.yml b/changelogs/unreleased/winh-fix-gpg-regressions.yml
deleted file mode 100644
index 75d28321259..00000000000
--- a/changelogs/unreleased/winh-fix-gpg-regressions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix GPG status badge loading regressions
-merge_request: 20987
-author:
-type: fixed
diff --git a/changelogs/unreleased/winh-job-list-dynamic-timer.yml b/changelogs/unreleased/winh-job-list-dynamic-timer.yml
new file mode 100644
index 00000000000..333a974d6aa
--- /dev/null
+++ b/changelogs/unreleased/winh-job-list-dynamic-timer.yml
@@ -0,0 +1,5 @@
+---
+title: Add dynamic timer for delayed jobs in job list
+merge_request: 22656
+author:
+type: changed
diff --git a/changelogs/unreleased/winh-pipeline-actions-dynamic-timer.yml b/changelogs/unreleased/winh-pipeline-actions-dynamic-timer.yml
new file mode 100644
index 00000000000..4ea1d3f8256
--- /dev/null
+++ b/changelogs/unreleased/winh-pipeline-actions-dynamic-timer.yml
@@ -0,0 +1,5 @@
+---
+title: Add dynamic timer for delayed jobs in pipelines list
+merge_request: 22621
+author:
+type: changed
diff --git a/changelogs/unreleased/winh-restyle-user-status.yml b/changelogs/unreleased/winh-restyle-user-status.yml
deleted file mode 100644
index 90370e87825..00000000000
--- a/changelogs/unreleased/winh-restyle-user-status.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Restyle status message input on profile settings
-merge_request: 20903
-author:
-type: changed
diff --git a/changelogs/unreleased/winh-stop-all-environments.yml b/changelogs/unreleased/winh-stop-all-environments.yml
deleted file mode 100644
index 6e5f2f506d9..00000000000
--- a/changelogs/unreleased/winh-stop-all-environments.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support manually stopping any environment from the UI
-merge_request: 20077
-author:
-type: changed
diff --git a/changelogs/unreleased/winh-tree-view-gpg.yml b/changelogs/unreleased/winh-tree-view-gpg.yml
deleted file mode 100644
index 84d63814a47..00000000000
--- a/changelogs/unreleased/winh-tree-view-gpg.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display GPG status on repository and blob pages
-merge_request: 20524
-author:
-type: changed
diff --git a/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml b/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml
deleted file mode 100644
index 62addff1d0f..00000000000
--- a/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade grape-path-helpers to 1.0.6
-merge_request: 20601
-author:
-type: other
diff --git a/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml b/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml
deleted file mode 100644
index 97fa1592753..00000000000
--- a/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Wrap job name on pipeline job sidebar
-merge_request: 20804
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/zj-bump-gitaly-0-128.yml b/changelogs/unreleased/zj-bump-gitaly-0-128.yml
new file mode 100644
index 00000000000..451df4b800e
--- /dev/null
+++ b/changelogs/unreleased/zj-bump-gitaly-0-128.yml
@@ -0,0 +1,5 @@
+---
+title: Bump Gitaly to 0.128.0
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/zj-circuit-breaker-removal.yml b/changelogs/unreleased/zj-circuit-breaker-removal.yml
new file mode 100644
index 00000000000..f753cec993f
--- /dev/null
+++ b/changelogs/unreleased/zj-circuit-breaker-removal.yml
@@ -0,0 +1,5 @@
+---
+title: Remove Git circuit breaker
+merge_request: 22212
+author:
+type: removed
diff --git a/changelogs/unreleased/zj-remove-broken-storage.yml b/changelogs/unreleased/zj-remove-broken-storage.yml
new file mode 100644
index 00000000000..9df87b40e09
--- /dev/null
+++ b/changelogs/unreleased/zj-remove-broken-storage.yml
@@ -0,0 +1,5 @@
+---
+title: Remove obsolete gitlab_shell rake tasks
+merge_request: 22417
+author:
+type: removed
diff --git a/changelogs/unreleased/zj-remove-git-rake-tasks.yml b/changelogs/unreleased/zj-remove-git-rake-tasks.yml
deleted file mode 100644
index 8c90fc7d0fe..00000000000
--- a/changelogs/unreleased/zj-remove-git-rake-tasks.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove gitlab:user:check_repos, gitlab:check_repo, gitlab:git:prune, gitlab:git:gc, and gitlab:git:repack
-merge_request: 20806
-author:
-type: removed
diff --git a/changelogs/unreleased/zj-remove-linguist.yml b/changelogs/unreleased/zj-remove-linguist.yml
new file mode 100644
index 00000000000..5719512c4cc
--- /dev/null
+++ b/changelogs/unreleased/zj-remove-linguist.yml
@@ -0,0 +1,5 @@
+---
+title: Remove Linguist gem, reducing Rails memory usage by 128MB per process
+merge_request: 21008
+author:
+type: changed
diff --git a/changelogs/unreleased/zj-repository-languages.yml b/changelogs/unreleased/zj-repository-languages.yml
deleted file mode 100644
index c42ba60be29..00000000000
--- a/changelogs/unreleased/zj-repository-languages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show repository languages for projects
-merge_request: 19480
-author:
-type: added
diff --git a/config/application.rb b/config/application.rb
index 76a2c47a750..921baa5d617 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -8,7 +8,7 @@ module Gitlab
# This method is used for smooth upgrading from the current Rails 4.x to Rails 5.0.
# https://gitlab.com/gitlab-org/gitlab-ce/issues/14286
def self.rails5?
- ENV["RAILS5"].in?(%w[1 true])
+ !%w[0 false].include?(ENV["RAILS5"])
end
class Application < Rails::Application
@@ -19,6 +19,7 @@ module Gitlab
require_dependency Rails.root.join('lib/gitlab/request_context')
require_dependency Rails.root.join('lib/gitlab/current_settings')
require_dependency Rails.root.join('lib/gitlab/middleware/read_only')
+ require_dependency Rails.root.join('lib/gitlab/middleware/basic_health_check')
# This needs to be loaded before DB connection is made
# to make sure that all connections have NO_ZERO_DATE
@@ -84,6 +85,7 @@ module Gitlab
# - Any parameter ending with `token`
# - Any parameter containing `password`
# - Any parameter containing `secret`
+ # - Any parameter ending with `key`
# - Two-factor tokens (:otp_attempt)
# - Repo/Project Import URLs (:import_url)
# - Build traces (:trace)
@@ -91,15 +93,13 @@ module Gitlab
# - GitLab Pages SSL cert/key info (:certificate, :encrypted_key)
# - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn)
- # - Deploy keys (:key)
# - File content from Web Editor (:content)
- config.filter_parameters += [/token$/, /password/, /secret/]
+ config.filter_parameters += [/token$/, /password/, /secret/, /key$/]
config.filter_parameters += %i(
certificate
encrypted_key
hook
import_url
- key
otp_attempt
sentry_dsn
trace
@@ -134,6 +134,7 @@ module Gitlab
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
config.assets.precompile << "page_bundles/ide.css"
+ config.assets.precompile << "page_bundles/xterm.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
config.assets.precompile << "test.css"
@@ -143,7 +144,7 @@ module Gitlab
config.assets.precompile << "errors.css"
# Import gitlab-svgs directly from vendored directory
- config.assets.paths << "#{config.root}/node_modules/@gitlab-org/gitlab-svgs/dist"
+ config.assets.paths << "#{config.root}/node_modules/@gitlab/svgs/dist"
config.assets.precompile << "icons.svg"
config.assets.precompile << "icons.json"
config.assets.precompile << "illustrations/*.svg"
@@ -157,9 +158,12 @@ module Gitlab
config.action_view.sanitized_allowed_protocols = %w(smb)
+ # Nokogiri is significantly faster and uses less memory than REXML
+ ActiveSupport::XmlMini.backend = 'Nokogiri'
+
# This middleware needs to precede ActiveRecord::QueryCache and other middlewares that
# connect to the database.
- config.middleware.insert_after "Rails::Rack::Logger", "Gitlab::Middleware::BasicHealthCheck"
+ config.middleware.insert_after Rails::Rack::Logger, ::Gitlab::Middleware::BasicHealthCheck
config.middleware.insert_after Warden::Manager, Rack::Attack
@@ -196,7 +200,7 @@ module Gitlab
config.cache_store = :redis_store, caching_config_hash
- config.active_record.raise_in_transactional_callbacks = true
+ config.active_record.raise_in_transactional_callbacks = true unless rails5?
config.active_job.queue_adapter = :sidekiq
@@ -204,7 +208,7 @@ module Gitlab
ENV['GITLAB_PATH_OUTSIDE_HOOK'] = ENV['PATH']
ENV['GIT_TERMINAL_PROMPT'] = '0'
- # Gitlab Read-only middleware support
+ # GitLab Read-only middleware support
config.middleware.insert_after ActionDispatch::Flash, ::Gitlab::Middleware::ReadOnly
config.generators do |g|
diff --git a/config/boot.rb b/config/boot.rb
index 655c54ddb84..1aeacdabbad 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -1,10 +1,10 @@
def rails5?
- %w[1 true].include?(ENV["RAILS5"])
+ !%w[0 false].include?(ENV["RAILS5"])
end
require 'rubygems' unless rails5?
-gemfile = rails5? ? "Gemfile.rails5" : "Gemfile"
+gemfile = rails5? ? "Gemfile" : "Gemfile.rails4"
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../#{gemfile}", __dir__)
# Set up gems listed in the Gemfile.
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index dce1fc1bc45..84d47bd52ad 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -235,8 +235,9 @@
:why: https://github.com/component/inherit/blob/master/LICENSE
:versions: []
:when: 2017-01-14 20:10:41.804804000 Z
-- - :approve
+- - :license
- fsevents
+ - MIT
- :who: Matt Lee
:why: https://github.com/strongloop/fsevents/blob/master/LICENSE
:versions: []
@@ -380,8 +381,9 @@
:why: https://github.com/Tjatse/ansi-html/blob/master/LICENSE
:versions: []
:when: 2017-04-10 05:42:12.898178000 Z
-- - :approve
+- - :license
- map-stream
+ - MIT
- :who: Mike Greiling
:why: https://github.com/dominictarr/map-stream/blob/master/LICENCE
:versions: []
@@ -458,8 +460,9 @@
:why: CC0 1.0 - https://github.com/jonathantneal/svg4everybody/blob/master/LICENSE.md
:versions: []
:when: 2017-09-13 17:31:16.425819400 Z
-- - :approve
- - "@gitlab-org/gitlab-svgs"
+- - :license
+ - "@gitlab/svgs"
+ - MIT
- :who: Tim Zallmann
:why: Our own library - GitLab License https://gitlab.com/gitlab-org/gitlab-svgs
:versions: []
@@ -528,8 +531,9 @@
:why: https://github.com/mafintosh/cyclist/blob/master/LICENSE
:versions: []
:when: 2018-02-20 21:37:43.774978000 Z
-- - :approve
+- - :license
- bitsyntax
+ - MIT
- :who: Mike Greiling
:why: https://github.com/squaremo/bitsyntax-js/blob/master/LICENSE-MIT
:versions: []
@@ -541,32 +545,50 @@
:versions: []
:when: 2018-06-08 05:30:56.764116000 Z
- - :approve
- - "@gitlab-org/gitlab-ui"
- - :who: Clement Ho
- :why: Our own library
- :versions: []
- :when: 2018-07-17 21:02:54.529227000 Z
-- - :approve
- lz-string
- :who: Phil Hughes
:why: https://github.com/pieroxy/lz-string/blob/master/LICENSE.txt
:versions: []
:when: 2018-08-03 08:22:44.973457000 Z
-- - :approve
+- - :license
- smooshpack
+ - LGPL
- :who: Phil Hughes
:why: https://github.com/CompuIves/codesandbox-client/blob/master/packages/sandpack/LICENSE.md
:versions: []
:when: 2018-08-03 08:24:29.578991000 Z
-- - :approve
+- - :license
- codesandbox-import-util-types
+ - LGPL
- :who: Phil Hughes
:why: https://github.com/codesandbox-app/codesandbox-importers/blob/master/packages/types/LICENSE
:versions: []
:when: 2018-08-03 12:22:47.574421000 Z
-- - :approve
+- - :license
- codesandbox-import-utils
+ - LGPL
- :who: Phil Hughes
:why: https://github.com/codesandbox-app/codesandbox-importers/blob/master/packages/import-utils/LICENSE
:versions: []
:when: 2018-08-03 12:23:24.083046000 Z
+- - :ignore_group
+ - devDependencies
+ - :who: Winnie Hellmann
+ :why: NPM packages used for development are not distributed with the final product
+ and are therefore exempt.
+ :versions: []
+ :when: 2018-08-30 12:06:35.668181000 Z
+- - :approve
+ - caniuse-lite
+ - :who: Mike Greiling
+ :why: CC-BY-4.0 license. Tool only used during build process, code is not present
+ in compiled/distributed product so attribution not needed.
+ :versions: []
+ :when: 2018-10-02 19:23:11.221660000 Z
+- - :approve
+ - node-releases
+ - :who: Mike Greiling
+ :why: CC-BY-4.0 license. Tool only used during build process, code is not present
+ in compiled/distributed product so attribution not needed.
+ :versions: []
+ :when: 2018-10-02 19:23:54.840151000 Z
diff --git a/config/environment.rb b/config/environment.rb
index 5d35937f7c6..3a52656a2c1 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -1,10 +1,10 @@
# Load the rails application
# Remove this condition when upgraded to rails 5.0.
-if %w[1 true].include?(ENV["RAILS5"])
- require_relative 'application'
-else
+if %w[0 false].include?(ENV["RAILS5"])
require File.expand_path('application', __dir__)
+else
+ require_relative 'application'
end
# Initialize the rails application
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 23790b84e3c..494ddd72556 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -45,4 +45,6 @@ Rails.application.configure do
# Do not log asset requests
config.assets.quiet = true
+
+ config.allow_concurrency = defined?(::Puma)
end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 9941987929c..71195164e7a 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -83,5 +83,5 @@ Rails.application.configure do
config.eager_load = true
- config.allow_concurrency = false
+ config.allow_concurrency = defined?(::Puma)
end
diff --git a/config/environments/test.rb b/config/environments/test.rb
index af1011a1ab1..072f93150a3 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -21,12 +21,12 @@ Rails.application.configure do
if Gitlab.rails5?
config.public_file_server.enabled = true
+ config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
else
config.serve_static_files = true
+ config.static_cache_control = "public, max-age=3600"
end
- config.static_cache_control = "public, max-age=3600"
-
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 561ff57c9fb..09e21b2c6f2 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -94,7 +94,7 @@ production: &base
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# Tip: you can test your closing pattern at http://rubular.com.
- # issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)'
+ # issue_closing_pattern: '\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)'
## Default project features settings
default_projects_features:
@@ -207,9 +207,14 @@ production: &base
# endpoint: 'http://127.0.0.1:9000' # default: nil
# path_style: true # Use 'host/bucket_name/object' instead of 'bucket_name.host/object'
+ ## Packages (maven repository so far)
+ packages:
+ enabled: false
+
## GitLab Pages
pages:
enabled: false
+ access_control: false
# The location where pages are stored (default: shared/pages).
# path: shared/pages
@@ -261,6 +266,9 @@ production: &base
# once per hour you will have concurrent 'git fsck' jobs.
repository_check_worker:
cron: "20 * * * *"
+ # Archive live traces which have not been archived yet
+ ci_archive_traces_cron_worker:
+ cron: "17 * * * *"
# Send admin emails once a week
admin_email_worker:
cron: "0 0 * * 0"
@@ -444,7 +452,7 @@ production: &base
## OmniAuth settings
omniauth:
# Allow login via Twitter, Google, etc. using OmniAuth providers
- enabled: false
+ # enabled: true
# Uncomment this to automatically sign in with a specific omniauth provider's without
# showing GitLab's sign-in page (default: show the GitLab sign-in page)
@@ -583,7 +591,7 @@ production: &base
gitaly:
# Path to the directory containing Gitaly client executables.
client_path: /home/git/gitaly/bin
- # Default Gitaly authentication token. Can be overriden per storage. Can
+ # Default Gitaly authentication token. Can be overridden per storage. Can
# be left blank when Gitaly is running locally on a Unix socket, which
# is the normal way to deploy Gitaly.
token:
@@ -757,8 +765,8 @@ test:
host: localhost
port: 80
- # When you run tests we clone and setup gitlab-shell
- # In order to setup it correctly you need to specify
+ # When you run tests we clone and set up gitlab-shell
+ # In order to set it up correctly you need to specify
# your system username you use to run GitLab
# user: YOUR_USERNAME
pages:
@@ -768,9 +776,6 @@ test:
default:
path: tmp/tests/repositories/
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
- broken:
- path: tmp/tests/non-existent-repositories
- gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
gitaly:
client_path: tmp/tests/gitaly
@@ -792,7 +797,7 @@ test:
project_key: PROJECT
omniauth:
- enabled: true
+ # enabled: true
allow_single_sign_on: true
external_providers: []
diff --git a/config/initializers/0_as_concern.rb b/config/initializers/0_as_concern.rb
index 40232bd6252..ff132547225 100644
--- a/config/initializers/0_as_concern.rb
+++ b/config/initializers/0_as_concern.rb
@@ -1,25 +1,7 @@
-# This module is based on: https://gist.github.com/bcardarella/5735987
-
-module Prependable
- def prepend_features(base)
- if base.instance_variable_defined?(:@_dependencies)
- base.instance_variable_get(:@_dependencies) << self
- false
- else
- return false if base < self
-
- super
- base.singleton_class.send(:prepend, const_get('ClassMethods')) if const_defined?(:ClassMethods)
- @_dependencies.each { |dep| base.send(:prepend, dep) } # rubocop:disable Gitlab/ModuleWithInstanceVariables
- base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) # rubocop:disable Gitlab/ModuleWithInstanceVariables
- end
- end
-end
+# frozen_string_literal: true
module ActiveSupport
module Concern
- prepend Prependable
-
- alias_method :prepended, :included
+ prepend Gitlab::Patch::Prependable
end
end
diff --git a/config/initializers/0_post_deployment_migrations.rb b/config/initializers/0_post_deployment_migrations.rb
index 3d81b869b52..2d647f72840 100644
--- a/config/initializers/0_post_deployment_migrations.rb
+++ b/config/initializers/0_post_deployment_migrations.rb
@@ -1,14 +1,4 @@
# Post deployment migrations are included by default. This file must be loaded
# before other initializers as Rails may otherwise memoize a list of migrations
# excluding the post deployment migrations.
-unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS']
- Rails.application.config.paths['db'].each do |db_path|
- path = Rails.root.join(db_path, 'post_migrate').to_s
-
- Rails.application.config.paths['db/migrate'] << path
-
- # Rails memoizes migrations at certain points where it won't read the above
- # path just yet. As such we must also update the following list of paths.
- ActiveRecord::Migrator.migrations_paths << path
- end
-end
+Gitlab::Database.add_post_migrate_path_to_rails
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 9ad55e21d11..bd02b85c7ce 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -45,7 +45,7 @@ if Settings.ldap['enabled'] || Rails.env.test?
end
Settings['omniauth'] ||= Settingslogic.new({})
-Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
+Settings.omniauth['enabled'] = true if Settings.omniauth['enabled'].nil?
Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil?
Settings.omniauth['allow_single_sign_on'] = false if Settings.omniauth['allow_single_sign_on'].nil?
Settings.omniauth['external_providers'] = [] if Settings.omniauth['external_providers'].nil?
@@ -136,12 +136,12 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
Settings.gitlab['session_expire_delay'] ||= 10080
-Settings.gitlab['unauthenticated_session_expire_delay'] ||= 1.hour.to_i
+Settings.gitlab['unauthenticated_session_expire_delay'] ||= 2.hours.to_i
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
@@ -200,6 +200,7 @@ Settings.registry['path'] = Settings.absolute(Settings.registry['path
#
Settings['pages'] ||= Settingslogic.new({})
Settings.pages['enabled'] = false if Settings.pages['enabled'].nil?
+Settings.pages['access_control'] = false if Settings.pages['access_control'].nil?
Settings.pages['path'] = Settings.absolute(Settings.pages['path'] || File.join(Settings.shared['path'], "pages"))
Settings.pages['https'] = false if Settings.pages['https'].nil?
Settings.pages['host'] ||= "example.com"
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index 146c4b1e024..8052880cc3d 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -26,9 +26,25 @@ Sidekiq.configure_server do |config|
end
if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
- unless Sidekiq.server?
- Gitlab::Metrics::Samplers::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_sampler_interval).start
+ Gitlab::Cluster::LifecycleEvents.on_worker_start do
+ defined?(::Prometheus::Client.reinitialize_on_pid_change) && Prometheus::Client.reinitialize_on_pid_change
+
+ unless Sidekiq.server?
+ Gitlab::Metrics::Samplers::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_sampler_interval).start
+ end
+
+ Gitlab::Metrics::Samplers::RubySampler.initialize_instance(Settings.monitoring.ruby_sampler_interval).start
end
+end
- Gitlab::Metrics::Samplers::RubySampler.initialize_instance(Settings.monitoring.ruby_sampler_interval).start
+Gitlab::Cluster::LifecycleEvents.on_master_restart do
+ # The following is necessary to ensure stale Prometheus metrics don't
+ # accumulate over time. It needs to be done in this hook as opposed to
+ # inside an init script to ensure metrics files aren't deleted after new
+ # unicorn workers start after a SIGUSR2 is received.
+ prometheus_multiproc_dir = ENV['prometheus_multiproc_dir']
+ if prometheus_multiproc_dir
+ old_metrics = Dir[File.join(prometheus_multiproc_dir, '*.db')]
+ FileUtils.rm_rf(old_metrics)
+ end
end
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index 4d8d35bf6cf..468f80939d7 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -3,7 +3,6 @@
# that we can stub it for testing, as it is only called when metrics are
# enabled.
#
-# rubocop:disable Metrics/AbcSize
def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(Gitlab::Shell)
@@ -48,16 +47,6 @@ def instrument_classes(instrumentation)
instrumentation.instrument_methods(Premailer::Adapter::Nokogiri)
instrumentation.instrument_instance_methods(Premailer::Adapter::Nokogiri)
- [
- :Blame, :Branch, :BranchCollection, :Blob, :Commit, :Diff, :Repository,
- :Tag, :TagCollection, :Tree
- ].each do |name|
- const = Rugged.const_get(name)
-
- instrumentation.instrument_methods(const)
- instrumentation.instrument_instance_methods(const)
- end
-
instrumentation.instrument_methods(Banzai::Renderer)
instrumentation.instrument_methods(Banzai::Querying)
@@ -101,7 +90,6 @@ def instrument_classes(instrumentation)
# Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/30224#note_32306159
instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits)
end
-# rubocop:enable Metrics/AbcSize
# With prometheus enabled by default this breaks all specs
# that stubs methods using `any_instance_of` for the models reloaded here.
@@ -110,7 +98,11 @@ end
# check: https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class
#
# Related issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/33587
-if Gitlab::Metrics.enabled? && !Rails.env.test?
+#
+# In development mode, we turn off eager loading when we're running
+# `rails generate migration` because eager loading short-circuits the
+# loading of our custom migration templates.
+if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && defined?(Rails::Generators))
require 'pathname'
require 'influxdb'
require 'connection_pool'
@@ -170,7 +162,9 @@ if Gitlab::Metrics.enabled? && !Rails.env.test?
GC::Profiler.enable
- Gitlab::Metrics::Samplers::InfluxSampler.initialize_instance.start
+ Gitlab::Cluster::LifecycleEvents.on_worker_start do
+ Gitlab::Metrics::Samplers::InfluxSampler.initialize_instance.start
+ end
module TrackNewRedisConnections
def connect(*args)
diff --git a/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb b/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb
index d9418caf68b..ef4abb77bd7 100644
--- a/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb
+++ b/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb
@@ -21,8 +21,6 @@
# This bug was fixed in Rails 5.1 by https://github.com/rails/rails/pull/24745/commits/aa062318c451512035c10898a1af95943b1a3803
if Gitlab.rails5?
- ActiveSupport::Deprecation.warn("#{__FILE__} is a monkey patch which must be removed when upgrading to Rails 5.1")
-
if Rails.version.start_with?("5.1")
raise "Remove this monkey patch: #{__FILE__}"
end
diff --git a/config/initializers/active_record_lifecycle.rb b/config/initializers/active_record_lifecycle.rb
new file mode 100644
index 00000000000..7fa37121efc
--- /dev/null
+++ b/config/initializers/active_record_lifecycle.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+# Don't handle sidekiq configuration as it
+# has its own special active record configuration here
+if defined?(ActiveRecord::Base) && !Sidekiq.server?
+ Gitlab::Cluster::LifecycleEvents.on_worker_start do
+ ActiveSupport.on_load(:active_record) do
+ ActiveRecord::Base.establish_connection
+
+ Rails.logger.debug("ActiveRecord connection established")
+ end
+ end
+end
+
+if defined?(ActiveRecord::Base)
+ Gitlab::Cluster::LifecycleEvents.on_before_fork do
+ # the following is highly recommended for Rails + "preload_app true"
+ # as there's no need for the master process to hold a connection
+ ActiveRecord::Base.connection.disconnect!
+
+ Rails.logger.debug("ActiveRecord connection disconnected")
+ end
+end
diff --git a/config/initializers/active_record_verbose_query_logs.rb b/config/initializers/active_record_verbose_query_logs.rb
index 44f86fec7e0..1c5fbc8e830 100644
--- a/config/initializers/active_record_verbose_query_logs.rb
+++ b/config/initializers/active_record_verbose_query_logs.rb
@@ -47,7 +47,9 @@ module ActiveRecord
end
end
- unless Gitlab.rails5?
+ if Rails.version.start_with?("5.2")
+ raise "Remove this monkey patch: #{__FILE__}"
+ else
prepend(VerboseQueryLogs) unless Rails.env.production?
end
end
diff --git a/config/initializers/asset_sync.rb b/config/initializers/asset_sync.rb
deleted file mode 100644
index 7f3934853fa..00000000000
--- a/config/initializers/asset_sync.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-AssetSync.configure do |config|
- # Disable the asset_sync gem by default. If it is enabled, but not configured,
- # asset_sync will cause the build to fail.
- config.enabled = if ENV.has_key?('ASSET_SYNC_ENABLED')
- ENV['ASSET_SYNC_ENABLED'] == 'true'
- else
- false
- end
-
- # Pulled from https://github.com/AssetSync/asset_sync/blob/v2.2.0/lib/asset_sync/engine.rb#L15-L40
- # This allows us to disable asset_sync by default and configure through environment variables
- # Updates to asset_sync gem should be checked
- config.fog_provider = ENV['FOG_PROVIDER'] if ENV.has_key?('FOG_PROVIDER')
- config.fog_directory = ENV['FOG_DIRECTORY'] if ENV.has_key?('FOG_DIRECTORY')
- config.fog_region = ENV['FOG_REGION'] if ENV.has_key?('FOG_REGION')
-
- config.aws_access_key_id = ENV['ASSETS_AWS_ACCESS_KEY_ID'] if ENV.has_key?('ASSETS_AWS_ACCESS_KEY_ID')
- config.aws_secret_access_key = ENV['ASSETS_AWS_SECRET_ACCESS_KEY'] if ENV.has_key?('ASSETS_AWS_SECRET_ACCESS_KEY')
- config.aws_reduced_redundancy = ENV['AWS_REDUCED_REDUNDANCY'] == true if ENV.has_key?('AWS_REDUCED_REDUNDANCY')
-
- config.rackspace_username = ENV['RACKSPACE_USERNAME'] if ENV.has_key?('RACKSPACE_USERNAME')
- config.rackspace_api_key = ENV['RACKSPACE_API_KEY'] if ENV.has_key?('RACKSPACE_API_KEY')
-
- config.google_storage_access_key_id = ENV['GOOGLE_STORAGE_ACCESS_KEY_ID'] if ENV.has_key?('GOOGLE_STORAGE_ACCESS_KEY_ID')
- config.google_storage_secret_access_key = ENV['GOOGLE_STORAGE_SECRET_ACCESS_KEY'] if ENV.has_key?('GOOGLE_STORAGE_SECRET_ACCESS_KEY')
-
- config.existing_remote_files = ENV['ASSET_SYNC_EXISTING_REMOTE_FILES'] || "keep"
-
- config.gzip_compression = (ENV['ASSET_SYNC_GZIP_COMPRESSION'] == 'true') if ENV.has_key?('ASSET_SYNC_GZIP_COMPRESSION')
- config.manifest = (ENV['ASSET_SYNC_MANIFEST'] == 'true') if ENV.has_key?('ASSET_SYNC_MANIFEST')
-end
diff --git a/config/initializers/carrierwave_patch.rb b/config/initializers/carrierwave_patch.rb
new file mode 100644
index 00000000000..35ffff03abe
--- /dev/null
+++ b/config/initializers/carrierwave_patch.rb
@@ -0,0 +1,29 @@
+# This monkey patches CarrierWave 1.2.3 to make Google Cloud Storage work with
+# extra query parameters:
+# https://github.com/carrierwaveuploader/carrierwave/pull/2332/files
+module CarrierWave
+ module Storage
+ class Fog < Abstract
+ class File
+ def authenticated_url(options = {})
+ if %w(AWS Google Rackspace OpenStack).include?(@uploader.fog_credentials[:provider])
+ # avoid a get by using local references
+ local_directory = connection.directories.new(key: @uploader.fog_directory)
+ local_file = local_directory.files.new(key: path)
+ expire_at = ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
+ case @uploader.fog_credentials[:provider]
+ when 'AWS', 'Google'
+ local_file.url(expire_at, options)
+ when 'Rackspace'
+ connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
+ when 'OpenStack'
+ connection.get_object_https_url(@uploader.fog_directory, path, expire_at)
+ else
+ local_file.url(expire_at)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index c41b2db722c..179e00cdbd0 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -71,7 +71,7 @@ Devise.setup do |config|
# a value less than 10 in other environments.
config.stretches = Rails.env.test? ? 1 : 10
- # Setup a pepper to generate the encrypted password.
+ # Set up a pepper to generate the encrypted password.
# config.pepper = "2ef62d549c4ff98a5d3e0ba211e72cff592060247e3bbbb9f499af1222f876f53d39b39b823132affb32858168c79c1d7741d26499901b63c6030a42129924ef"
# ==> Configuration for :confirmable
diff --git a/config/initializers/fill_shards.rb b/config/initializers/fill_shards.rb
new file mode 100644
index 00000000000..18e067c8854
--- /dev/null
+++ b/config/initializers/fill_shards.rb
@@ -0,0 +1,3 @@
+if Shard.connected? && !Gitlab::Database.read_only?
+ Shard.populate!
+end
diff --git a/config/initializers/fog_google_https_private_urls.rb b/config/initializers/fog_google_https_private_urls.rb
index f92e623a5d2..682b1050c68 100644
--- a/config/initializers/fog_google_https_private_urls.rb
+++ b/config/initializers/fog_google_https_private_urls.rb
@@ -7,9 +7,9 @@ module Fog
class GoogleXML
class File < Fog::Model
module MonkeyPatch
- def url(expires)
+ def url(expires, options = {})
requires :key
- collection.get_https_url(key, expires)
+ collection.get_https_url(key, expires, options)
end
end
diff --git a/config/initializers/gettext_rails_i18n_patch.rb b/config/initializers/gettext_rails_i18n_patch.rb
index 49551319435..c1342f48ebd 100644
--- a/config/initializers/gettext_rails_i18n_patch.rb
+++ b/config/initializers/gettext_rails_i18n_patch.rb
@@ -1,5 +1,6 @@
require 'gettext_i18n_rails/haml_parser'
require 'gettext_i18n_rails_js/parser/javascript'
+require 'json'
VUE_TRANSLATE_REGEX = /((%[\w.-]+)(?:\s))?{{ (N|n|s)?__\((.*)\) }}/
@@ -36,6 +37,20 @@ module GettextI18nRailsJs
".vue"
].include? ::File.extname(file)
end
+
+ def collect_for(file)
+ gettext_messages_by_file[file] || []
+ end
+
+ private
+
+ def gettext_messages_by_file
+ @gettext_messages_by_file ||= JSON.parse(load_messages)
+ end
+
+ def load_messages
+ `node scripts/frontend/extract_gettext_all.js --all`
+ end
end
end
end
diff --git a/config/initializers/gollum.rb b/config/initializers/gollum.rb
deleted file mode 100644
index ea9cc151a57..00000000000
--- a/config/initializers/gollum.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# WARNING changes in this file must be manually propagated to gitaly-ruby.
-#
-# https://gitlab.com/gitlab-org/gitaly/blob/master/ruby/lib/gitlab/gollum.rb
-
-module Gollum
- GIT_ADAPTER = "rugged".freeze
-end
-require "gollum-lib"
-
-module Gollum
- class Page
- def text_data(encoding = nil)
- data = if raw_data.respond_to?(:encoding)
- raw_data.force_encoding(encoding || Encoding::UTF_8)
- else
- raw_data
- end
-
- Gitlab::EncodingHelper.encode!(data)
- end
- end
-end
-
-Rails.application.configure do
- config.after_initialize do
- Gollum::Page.per_page = Kaminari.config.default_per_page
- end
-end
diff --git a/config/initializers/hipchat_client_patch.rb b/config/initializers/hipchat_client_patch.rb
new file mode 100644
index 00000000000..aec265312bb
--- /dev/null
+++ b/config/initializers/hipchat_client_patch.rb
@@ -0,0 +1,14 @@
+# This monkey patches the HTTParty used in https://github.com/hipchat/hipchat-rb.
+module HipChat
+ class Client
+ connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
+ end
+
+ class Room
+ connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
+ end
+
+ class User
+ connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
+ end
+end
diff --git a/config/initializers/kubeclient.rb b/config/initializers/kubeclient.rb
index 7f115268b37..2d9f439fdc0 100644
--- a/config/initializers/kubeclient.rb
+++ b/config/initializers/kubeclient.rb
@@ -13,4 +13,25 @@ class Kubeclient::Client
ns_prefix = build_namespace_prefix(namespace)
rest_client["#{ns_prefix}#{entity_name_plural}/#{name}:#{port}/proxy"].url
end
+
+ # Monkey patch to set `max_redirects: 0`, so that kubeclient
+ # does not follow redirects and expose internal services.
+ # See https://gitlab.com/gitlab-org/gitlab-ce/issues/53158
+ def create_rest_client(path = nil)
+ path ||= @api_endpoint.path
+ options = {
+ ssl_ca_file: @ssl_options[:ca_file],
+ ssl_cert_store: @ssl_options[:cert_store],
+ verify_ssl: @ssl_options[:verify_ssl],
+ ssl_client_cert: @ssl_options[:client_cert],
+ ssl_client_key: @ssl_options[:client_key],
+ proxy: @http_proxy_uri,
+ user: @auth_options[:username],
+ password: @auth_options[:password],
+ open_timeout: @timeouts[:open],
+ read_timeout: @timeouts[:read],
+ max_redirects: 0
+ }
+ RestClient::Resource.new(@api_endpoint.merge(path).to_s, options)
+ end
end
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
index 1cf8a24e98c..840404e0ec0 100644
--- a/config/initializers/lograge.rb
+++ b/config/initializers/lograge.rb
@@ -22,7 +22,8 @@ unless Sidekiq.server?
params: params,
remote_ip: event.payload[:remote_ip],
user_id: event.payload[:user_id],
- username: event.payload[:username]
+ username: event.payload[:username],
+ ua: event.payload[:ua]
}
gitaly_calls = Gitlab::GitalyClient.get_request_count
diff --git a/config/initializers/macos.rb b/config/initializers/macos.rb
new file mode 100644
index 00000000000..f410af6ed47
--- /dev/null
+++ b/config/initializers/macos.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+if /darwin/ =~ RUBY_PLATFORM
+ Gitlab::Cluster::LifecycleEvents.on_before_fork do
+ require 'fiddle'
+
+ # Dynamically load Foundation.framework, ~implicitly~ initialising
+ # the Objective-C runtime before any forking happens in Unicorn
+ #
+ # From https://bugs.ruby-lang.org/issues/14009
+ Fiddle.dlopen '/System/Library/Frameworks/Foundation.framework/Foundation'
+ end
+end
diff --git a/config/initializers/mysql_set_length_for_binary_indexes.rb b/config/initializers/mysql_set_length_for_binary_indexes.rb
index de0bc5322aa..0445d8fcae2 100644
--- a/config/initializers/mysql_set_length_for_binary_indexes.rb
+++ b/config/initializers/mysql_set_length_for_binary_indexes.rb
@@ -2,13 +2,17 @@
# MySQL adapter apply a length of 20. Otherwise MySQL can't create an index on
# binary columns.
+# This module can be removed once a Rails 5 schema is used.
+# It can't be wrapped in a check that checks Gitlab.rails5? because
+# the old Rails 4 schema layout is still used
module MysqlSetLengthForBinaryIndex
def add_index(table_name, column_names, options = {})
+ options[:length] ||= {}
Array(column_names).each do |column_name|
column = ActiveRecord::Base.connection.columns(table_name).find { |c| c.name == column_name }
if column&.type == :binary
- options[:length] = 20
+ options[:length][column_name] = 20
end
end
@@ -19,3 +23,47 @@ end
if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
ActiveRecord::ConnectionAdapters::Mysql2Adapter.send(:prepend, MysqlSetLengthForBinaryIndex)
end
+
+module MysqlSetLengthForBinaryIndexAndIgnorePostgresOptionsForSchema
+ # This method is used in Rails 5 schema loading as t.index
+ def index(column_names, options = {})
+ # Ignore indexes that use opclasses,
+ # also see config/initializers/mysql_ignore_postgresql_options.rb
+ if options[:opclasses]
+ warn "WARNING: index on columns #{column_names} uses unsupported option, skipping."
+ return
+ end
+
+ # when running rails 4 with rails 5 schema, rails 4 doesn't support multiple
+ # indexes on the same set of columns. Mysql doesn't support partial indexes, so if
+ # an index already exists and we add another index, skip it if it's partial:
+ # see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21492#note_102821326
+ if !Gitlab.rails5? && indexes[column_names] && options[:where]
+ warn "WARNING: index on columns #{column_names} already exists and partial index is not supported, skipping."
+ return
+ end
+
+ options[:length] ||= {}
+ Array(column_names).each do |column_name|
+ column = columns.find { |c| c.name == column_name }
+
+ if column&.type == :binary
+ options[:length][column_name] = 20
+ end
+ end
+
+ super(column_names, options)
+ end
+end
+
+def mysql_adapter?
+ defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) && ActiveRecord::Base.connection.is_a?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
+end
+
+if Gitlab.rails5?
+ if defined?(ActiveRecord::ConnectionAdapters::MySQL::TableDefinition)
+ ActiveRecord::ConnectionAdapters::MySQL::TableDefinition.send(:prepend, MysqlSetLengthForBinaryIndexAndIgnorePostgresOptionsForSchema)
+ end
+elsif mysql_adapter? && defined?(ActiveRecord::ConnectionAdapters::TableDefinition)
+ ActiveRecord::ConnectionAdapters::TableDefinition.send(:prepend, MysqlSetLengthForBinaryIndexAndIgnorePostgresOptionsForSchema)
+end
diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb
index bc9b52ceef7..a6f43415ec5 100644
--- a/config/initializers/peek.rb
+++ b/config/initializers/peek.rb
@@ -18,7 +18,6 @@ Peek.into PEEK_DB_VIEW
Peek.into Peek::Views::Gitaly
Peek.into Peek::Views::Rblineprof
Peek.into Peek::Views::Redis
-Peek.into Peek::Views::Sidekiq
Peek.into Peek::Views::GC
# rubocop:disable Naming/ClassAndModuleCamelCase
diff --git a/config/initializers/postgresql_opclasses_support.rb b/config/initializers/postgresql_opclasses_support.rb
index 7b8afc78817..07b06629dea 100644
--- a/config/initializers/postgresql_opclasses_support.rb
+++ b/config/initializers/postgresql_opclasses_support.rb
@@ -144,7 +144,10 @@ module ActiveRecord
[column, opclass] if opclass
end.compact]
- IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using, opclasses)
+ index_attrs = [table_name, index_name, unique, column_names, [], orders, where, nil, using, opclasses]
+ index_attrs.insert(-2, nil) if Gitlab.rails5? # include index comment for Rails 5
+
+ IndexDefinition.new(*index_attrs)
end
end.compact
end
@@ -172,29 +175,42 @@ module ActiveRecord
def indexes(table, stream)
if (indexes = @connection.indexes(table)).any?
add_index_statements = indexes.map do |index|
- statement_parts = [
- "add_index #{remove_prefix_and_suffix(index.table).inspect}",
- index.columns.inspect,
- "name: #{index.name.inspect}",
- ]
- statement_parts << 'unique: true' if index.unique
-
- index_lengths = (index.lengths || []).compact
- statement_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
-
- index_orders = index.orders || {}
- statement_parts << "order: #{index.orders.inspect}" if index_orders.any?
- statement_parts << "where: #{index.where.inspect}" if index.where
- statement_parts << "using: #{index.using.inspect}" if index.using
- statement_parts << "type: #{index.type.inspect}" if index.type
- statement_parts << "opclasses: #{index.opclasses}" if index.opclasses.present?
-
- " #{statement_parts.join(', ')}"
+ table_name = remove_prefix_and_suffix(index.table).inspect
+ " add_index #{([table_name]+index_parts(index)).join(', ')}"
end
stream.puts add_index_statements.sort.join("\n")
stream.puts
end
end
+
+ def indexes_in_create(table, stream)
+ if (indexes = @connection.indexes(table)).any?
+ index_statements = indexes.map do |index|
+ " t.index #{index_parts(index).join(', ')}"
+ end
+ stream.puts index_statements.sort.join("\n")
+ end
+ end
+
+ def index_parts(index)
+ index_parts = [
+ index.columns.inspect,
+ "name: #{index.name.inspect}",
+ ]
+ index_parts << "unique: true" if index.unique
+ index_parts << "length: { #{format_options(index.lengths)} }" if index.lengths.present?
+ index_parts << "order: { #{format_options(index.orders)} }" if index.orders.present?
+ index_parts << "where: #{index.where.inspect}" if index.where
+ index_parts << "using: #{index.using.inspect}" if index.using
+ index_parts << "type: #{index.type.inspect}" if index.type
+ index_parts << "opclasses: #{index.opclasses.inspect}" if index.opclasses.present?
+ index_parts << "comment: #{index.comment.inspect}" if Gitlab.rails5? && index.comment
+ index_parts
+ end
+
+ def format_options(options)
+ options.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
+ end
end
end
diff --git a/config/initializers/rbtrace.rb b/config/initializers/rbtrace.rb
new file mode 100644
index 00000000000..6a1b71bf4bd
--- /dev/null
+++ b/config/initializers/rbtrace.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+if ENV['ENABLE_RBTRACE']
+ Gitlab::Cluster::LifecycleEvents.on_worker_start do
+ # Unicorn clears out signals before it forks, so rbtrace won't work
+ # unless it is enabled after the fork.
+ require 'rbtrace'
+ end
+end
diff --git a/config/initializers/routing_draw.rb b/config/initializers/routing_draw.rb
index 25003cf0239..f0f74954eef 100644
--- a/config/initializers/routing_draw.rb
+++ b/config/initializers/routing_draw.rb
@@ -1,7 +1,3 @@
# Adds draw method into Rails routing
-# It allows us to keep routing splitted into files
-class ActionDispatch::Routing::Mapper
- def draw(routes_name)
- instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb")))
- end
-end
+# It allows us to keep routing split into files
+ActionDispatch::Routing::Mapper.prepend Gitlab::Patch::DrawRoute
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 6f54bee4713..565efc858d1 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -1,3 +1,9 @@
+require 'sidekiq/web'
+
+# Disable the Sidekiq Rack session since GitLab already has its own session store.
+# CSRF protection still works (https://github.com/mperham/sidekiq/commit/315504e766c4fd88a29b7772169060afc4c40329).
+Sidekiq::Web.set :sessions, false
+
# Custom Queues configuration
queues_config_hash = Gitlab::Redis::Queues.params
queues_config_hash[:namespace] = Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE
@@ -8,8 +14,6 @@ Sidekiq.default_worker_options = { retry: 3 }
enable_json_logs = Gitlab.config.sidekiq.log_format == 'json'
Sidekiq.configure_server do |config|
- require 'rbtrace' if ENV['ENABLE_RBTRACE']
-
config.redis = queues_config_hash
config.server_middleware do |chain|
@@ -34,6 +38,10 @@ Sidekiq.configure_server do |config|
ActiveRecord::Base.clear_all_connections!
end
+ if Feature.enabled?(:gitlab_sidekiq_reliable_fetcher)
+ Sidekiq::ReliableFetcher.setup_reliable_fetch!(config)
+ end
+
# Sidekiq-cron: load recurring jobs from gitlab.yml
# UGLY Hack to get nested hash from settingslogic
cron_jobs = JSON.parse(Gitlab.config.cron_jobs.to_json)
@@ -49,14 +57,12 @@ Sidekiq.configure_server do |config|
end
Sidekiq::Cron::Job.load_from_hash! cron_jobs
- Gitlab::SidekiqThrottler.execute!
-
Gitlab::SidekiqVersioning.install!
- config = Gitlab::Database.config ||
+ db_config = Gitlab::Database.config ||
Rails.application.config.database_configuration[Rails.env]
- config['pool'] = Sidekiq.options[:concurrency]
- ActiveRecord::Base.establish_connection(config)
+ db_config['pool'] = Sidekiq.options[:concurrency]
+ ActiveRecord::Base.establish_connection(db_config)
Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{ActiveRecord::Base.connection.pool.instance_variable_get('@size')}")
# Avoid autoload issue such as 'Mail::Parsers::AddressStruct'
diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb
index 6c28686e69a..a0b8b68f3ef 100644
--- a/config/initializers/static_files.rb
+++ b/config/initializers/static_files.rb
@@ -1,17 +1,26 @@
app = Rails.application
-if app.config.serve_static_files
+if (Gitlab.rails5? && app.config.public_file_server.enabled) || app.config.serve_static_files
# The `ActionDispatch::Static` middleware intercepts requests for static files
# by checking if they exist in the `/public` directory.
# We're replacing it with our `Gitlab::Middleware::Static` that does the same,
# except ignoring `/uploads`, letting those go through to the GitLab Rails app.
- app.config.middleware.swap(
- ActionDispatch::Static,
- Gitlab::Middleware::Static,
- app.paths["public"].first,
- app.config.static_cache_control
- )
+ if Gitlab.rails5?
+ app.config.middleware.swap(
+ ActionDispatch::Static,
+ Gitlab::Middleware::Static,
+ app.paths["public"].first,
+ headers: app.config.public_file_server.headers
+ )
+ else
+ app.config.middleware.swap(
+ ActionDispatch::Static,
+ Gitlab::Middleware::Static,
+ app.paths["public"].first,
+ app.config.static_cache_control
+ )
+ end
# If webpack-dev-server is configured, proxy webpack's public directory
# instead of looking for static assets
diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb
index 33f55069c3e..1d2bb2bce0a 100644
--- a/config/initializers/warden.rb
+++ b/config/initializers/warden.rb
@@ -31,6 +31,11 @@ Rails.application.configure do |config|
Warden::Manager.before_logout(scope: :user) do |user, auth, opts|
user ||= auth.user
+
+ # Rails CSRF protection may attempt to log out a user before that
+ # user even logs in
+ next unless user
+
activity = Gitlab::Auth::Activity.new(opts)
tracker = Gitlab::Auth::BlockedUserTracker.new(user, auth)
diff --git a/config/karma.config.js b/config/karma.config.js
index 84810332dc2..e1d7c30b1c2 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -22,6 +22,13 @@ webpackConfig.optimization.splitChunks = false;
// use quicker sourcemap option
webpackConfig.devtool = 'cheap-inline-source-map';
+// set BABEL_ENV to indicate when we're running code coverage
+webpackConfig.plugins.push(
+ new webpack.DefinePlugin({
+ 'process.env.BABEL_ENV': JSON.stringify(process.env.BABEL_ENV || process.env.NODE_ENV || null),
+ })
+);
+
const specFilters = argumentsParser
.option(
'-f, --filter-spec [filter]',
@@ -80,11 +87,12 @@ if (specFilters.length) {
module.exports = function(config) {
process.env.TZ = 'Etc/UTC';
- const progressReporter = process.env.CI ? 'mocha' : 'progress';
-
const karmaConfig = {
basePath: ROOT_PATH,
browsers: ['ChromeHeadlessCustom'],
+ client: {
+ color: !process.env.CI,
+ },
customLaunchers: {
ChromeHeadlessCustom: {
base: 'ChromeHeadless',
@@ -104,11 +112,19 @@ module.exports = function(config) {
preprocessors: {
'spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
},
- reporters: [progressReporter],
+ reporters: ['progress'],
webpack: webpackConfig,
webpackMiddleware: { stats: 'errors-only' },
};
+ if (process.env.CI) {
+ karmaConfig.reporters = ['mocha', 'junit'];
+ karmaConfig.junitReporter = {
+ outputFile: 'junit_karma.xml',
+ useBrowserName: false,
+ };
+ }
+
if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') {
karmaConfig.reporters.push('coverage-istanbul');
karmaConfig.coverageIstanbulReporter = {
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 795e5d4e6bc..0a43a1d9a6b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -8,6 +8,8 @@ en:
issue_link:
source: Source issue
target: Target issue
+ group:
+ path: Group URL
errors:
messages:
label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml
deleted file mode 100644
index c994bad7865..00000000000
--- a/config/prometheus/additional_metrics.yml
+++ /dev/null
@@ -1,177 +0,0 @@
-- group: Response metrics (NGINX Ingress)
- priority: 10
- metrics:
- - title: "Throughput"
- y_label: "Requests / Sec"
- required_metrics:
- - nginx_upstream_responses_total
- weight: 1
- queries:
- - query_range: 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) by (status_code)'
- unit: req / sec
- label: Status Code
- series:
- - label: status_code
- when:
- - value: 2xx
- color: green
- - value: 4xx
- color: orange
- - value: 5xx
- color: red
- - title: "Latency"
- y_label: "Latency (ms)"
- required_metrics:
- - nginx_upstream_response_msecs_avg
- weight: 1
- queries:
- - query_range: 'avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"})'
- label: Pod average
- unit: ms
- - title: "HTTP Error Rate"
- y_label: "HTTP Errors"
- required_metrics:
- - nginx_upstream_responses_total
- weight: 1
- queries:
- - query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100'
- label: 5xx Errors
- unit: "%"
-- group: Response metrics (HA Proxy)
- priority: 10
- metrics:
- - title: "Throughput"
- y_label: "Requests / Sec"
- required_metrics:
- - haproxy_frontend_http_requests_total
- weight: 1
- queries:
- - query_range: 'sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) by (code)'
- unit: req / sec
- series:
- - label: code
- when:
- - value: 2xx
- color: green
- - value: 4xx
- color: yellow
- - value: 5xx
- color: red
- - title: "HTTP Error Rate"
- y_label: "Error Rate (%)"
- required_metrics:
- - haproxy_frontend_http_responses_total
- weight: 1
- queries:
- - query_range: 'sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m]))'
- label: HTTP Errors
- unit: "%"
-- group: Response metrics (AWS ELB)
- priority: 10
- metrics:
- - title: "Throughput"
- y_label: "Requests / Sec"
- required_metrics:
- - aws_elb_request_count_sum
- weight: 1
- queries:
- - query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) / 60'
- label: Total
- unit: req / sec
- - title: "Latency"
- y_label: "Latency (ms)"
- required_metrics:
- - aws_elb_latency_average
- weight: 1
- queries:
- - query_range: 'avg(aws_elb_latency_average{%{environment_filter}}) * 1000'
- label: Average
- unit: ms
- - title: "HTTP Error Rate"
- y_label: "Error Rate (%)"
- required_metrics:
- - aws_elb_request_count_sum
- - aws_elb_httpcode_backend_5_xx_sum
- weight: 1
- queries:
- - query_range: 'sum(aws_elb_httpcode_backend_5_xx_sum{%{environment_filter}}) / sum(aws_elb_request_count_sum{%{environment_filter}})'
- label: HTTP Errors
- unit: "%"
-- group: Response metrics (NGINX)
- priority: 10
- metrics:
- - title: "Throughput"
- y_label: "Requests / Sec"
- required_metrics:
- - nginx_server_requests
- weight: 1
- queries:
- - query_range: 'sum(rate(nginx_server_requests{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) by (code)'
- unit: req / sec
- label: Status Code
- series:
- - label: status_code
- when:
- - value: 2xx
- color: green
- - value: 4xx
- color: orange
- - value: 5xx
- color: red
- - title: "Latency"
- y_label: "Latency (ms)"
- required_metrics:
- - nginx_server_requestMsec
- weight: 1
- queries:
- - query_range: 'avg(nginx_server_requestMsec{%{environment_filter}})'
- label: Upstream
- unit: ms
- - title: "HTTP Error Rate"
- y_label: "HTTP 500 Errors / Sec"
- required_metrics:
- - nginx_server_requests
- weight: 1
- queries:
- - query_range: 'sum(rate(nginx_server_requests{code="5xx", %{environment_filter}}[2m]))'
- label: HTTP Errors
- unit: "errors / sec"
-- group: System metrics (Kubernetes)
- priority: 5
- metrics:
- - title: "Memory Usage (Total)"
- y_label: "Total Memory Used"
- required_metrics:
- - container_memory_usage_bytes
- weight: 4
- queries:
- - query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024'
- label: Total
- unit: GB
- - title: "Core Usage (Total)"
- y_label: "Total Cores"
- required_metrics:
- - container_cpu_usage_seconds_total
- weight: 3
- queries:
- - query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job)'
- label: Total
- unit: "cores"
- - title: "Memory Usage (Pod average)"
- y_label: "Memory Used per Pod"
- required_metrics:
- - container_memory_usage_bytes
- weight: 2
- queries:
- - query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
- label: Pod average
- unit: MB
- - title: "Core Usage (Pod average)"
- y_label: "Cores per Pod"
- required_metrics:
- - container_cpu_usage_seconds_total
- weight: 1
- queries:
- - query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job) / count(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (pod_name))'
- label: Pod average
- unit: "cores" \ No newline at end of file
diff --git a/config/prometheus/common_metrics.yml b/config/prometheus/common_metrics.yml
new file mode 100644
index 00000000000..52023a2e3cb
--- /dev/null
+++ b/config/prometheus/common_metrics.yml
@@ -0,0 +1,215 @@
+- group: Response metrics (NGINX Ingress)
+ priority: 10
+ metrics:
+ - title: "Throughput"
+ y_label: "Requests / Sec"
+ required_metrics:
+ - nginx_upstream_responses_total
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_ingress_throughput_status_code
+ query_range: 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) by (status_code)'
+ unit: req / sec
+ label: Status Code
+ series:
+ - label: status_code
+ when:
+ - value: 2xx
+ color: green
+ - value: 4xx
+ color: orange
+ - value: 5xx
+ color: red
+ - title: "Latency"
+ y_label: "Latency (ms)"
+ required_metrics:
+ - nginx_upstream_response_msecs_avg
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_ingress_latency_pod_average
+ query_range: 'avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"})'
+ label: Pod average
+ unit: ms
+ - title: "HTTP Error Rate"
+ y_label: "HTTP Errors"
+ required_metrics:
+ - nginx_upstream_responses_total
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_ingress_http_error_rate
+ query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100'
+ label: 5xx Errors
+ unit: "%"
+- group: Response metrics (HA Proxy)
+ priority: 10
+ metrics:
+ - title: "Throughput"
+ y_label: "Requests / Sec"
+ required_metrics:
+ - haproxy_frontend_http_requests_total
+ weight: 1
+ queries:
+ - id: response_metrics_ha_proxy_throughput_status_code
+ query_range: 'sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) by (code)'
+ unit: req / sec
+ label: Status Code
+ series:
+ - label: status_code
+ when:
+ - value: 2xx
+ color: green
+ - value: 4xx
+ color: yellow
+ - value: 5xx
+ color: red
+ - title: "HTTP Error Rate"
+ y_label: "Error Rate (%)"
+ required_metrics:
+ - haproxy_frontend_http_responses_total
+ weight: 1
+ queries:
+ - id: response_metrics_ha_proxy_http_error_rate
+ query_range: 'sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m]))'
+ label: HTTP Errors
+ unit: "%"
+- group: Response metrics (AWS ELB)
+ priority: 10
+ metrics:
+ - title: "Throughput"
+ y_label: "Requests / Sec"
+ required_metrics:
+ - aws_elb_request_count_sum
+ weight: 1
+ queries:
+ - id: response_metrics_aws_elb_throughput_requests
+ query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) / 60'
+ label: Total
+ unit: req / sec
+ - title: "Latency"
+ y_label: "Latency (ms)"
+ required_metrics:
+ - aws_elb_latency_average
+ weight: 1
+ queries:
+ - id: response_metrics_aws_elb_latency_average
+ query_range: 'avg(aws_elb_latency_average{%{environment_filter}}) * 1000'
+ label: Average
+ unit: ms
+ - title: "HTTP Error Rate"
+ y_label: "Error Rate (%)"
+ required_metrics:
+ - aws_elb_request_count_sum
+ - aws_elb_httpcode_backend_5_xx_sum
+ weight: 1
+ queries:
+ - id: response_metrics_aws_elb_http_error_rate
+ query_range: 'sum(aws_elb_httpcode_backend_5_xx_sum{%{environment_filter}}) / sum(aws_elb_request_count_sum{%{environment_filter}})'
+ label: HTTP Errors
+ unit: "%"
+- group: Response metrics (NGINX)
+ priority: 10
+ metrics:
+ - title: "Throughput"
+ y_label: "Requests / Sec"
+ required_metrics:
+ - nginx_server_requests
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_throughput_status_code
+ query_range: 'sum(rate(nginx_server_requests{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) by (code)'
+ unit: req / sec
+ label: Status Code
+ series:
+ - label: status_code
+ when:
+ - value: 2xx
+ color: green
+ - value: 4xx
+ color: orange
+ - value: 5xx
+ color: red
+ - title: "Latency"
+ y_label: "Latency (ms)"
+ required_metrics:
+ - nginx_server_requestMsec
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_latency
+ query_range: 'avg(nginx_server_requestMsec{%{environment_filter}})'
+ label: Upstream
+ unit: ms
+ - title: "HTTP Error Rate"
+ y_label: "HTTP 500 Errors / Sec"
+ required_metrics:
+ - nginx_server_requests
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_http_error_rate
+ query_range: 'sum(rate(nginx_server_requests{code="5xx", %{environment_filter}}[2m]))'
+ label: HTTP Errors
+ unit: "errors / sec"
+- group: System metrics (Kubernetes)
+ priority: 5
+ metrics:
+ - title: "Memory Usage (Total)"
+ y_label: "Total Memory Used"
+ required_metrics:
+ - container_memory_usage_bytes
+ weight: 4
+ queries:
+ - id: system_metrics_kubernetes_container_memory_total
+ query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024'
+ label: Total
+ unit: GB
+ - title: "Core Usage (Total)"
+ y_label: "Total Cores"
+ required_metrics:
+ - container_cpu_usage_seconds_total
+ weight: 3
+ queries:
+ - id: system_metrics_kubernetes_container_cores_total
+ query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job)'
+ label: Total
+ unit: "cores"
+ - title: "Memory Usage (Pod average)"
+ y_label: "Memory Used per Pod"
+ required_metrics:
+ - container_memory_usage_bytes
+ weight: 2
+ queries:
+ - id: system_metrics_kubernetes_container_memory_average
+ query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
+ label: Pod average
+ unit: MB
+ - title: "Canary: Memory Usage (Pod Average)"
+ y_label: "Memory Used per Pod"
+ required_metrics:
+ - container_memory_usage_bytes
+ weight: 2
+ queries:
+ - id: system_metrics_kubernetes_container_memory_average_canary
+ query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
+ label: Pod average
+ unit: MB
+ track: canary
+ - title: "Core Usage (Pod Average)"
+ y_label: "Cores per Pod"
+ required_metrics:
+ - container_cpu_usage_seconds_total
+ weight: 1
+ queries:
+ - id: system_metrics_kubernetes_container_core_usage
+ query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job) / count(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (pod_name))'
+ label: Pod average
+ unit: "cores"
+ - title: "Canary: Core Usage (Pod Average)"
+ y_label: "Cores per Pod"
+ required_metrics:
+ - container_cpu_usage_seconds_total
+ weight: 1
+ queries:
+ - id: system_metrics_kubernetes_container_core_usage_canary
+ query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job) / count(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}[15m])) by (pod_name))'
+ label: Pod average
+ unit: "cores"
+ track: canary
diff --git a/config/puma.example.development.rb b/config/puma.example.development.rb
new file mode 100644
index 00000000000..490c940077a
--- /dev/null
+++ b/config/puma.example.development.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+# -----------------------------------------------------------------------
+# This file is used by the GDK to generate a default config/puma.rb file
+# Note that `/home/git` will be substituted for the actual GDK root
+# directory when this file is generated
+# -----------------------------------------------------------------------
+
+# Load "path" as a rackup file.
+#
+# The default is "config.ru".
+#
+rackup 'config.ru'
+pidfile '/home/git/gitlab/tmp/pids/puma.pid'
+state_path '/home/git/gitlab/tmp/pids/puma.state'
+
+stdout_redirect '/home/git/gitlab/log/puma.stdout.log',
+ '/home/git/gitlab/log/puma.stderr.log',
+ true
+
+# Configure "min" to be the minimum number of threads to use to answer
+# requests and "max" the maximum.
+#
+# The default is "0, 16".
+#
+threads 1, 4
+
+# By default, workers accept all requests and queue them to pass to handlers.
+# When false, workers accept the number of simultaneous requests configured.
+#
+# Queueing requests generally improves performance, but can cause deadlocks if
+# the app is waiting on a request to itself. See https://github.com/puma/puma/issues/612
+#
+# When set to false this may require a reverse proxy to handle slow clients and
+# queue requests before they reach puma. This is due to disabling HTTP keepalive
+queue_requests false
+
+# Bind the server to "url". "tcp://", "unix://" and "ssl://" are the only
+# accepted protocols.
+bind 'unix:///home/git/gitlab.socket'
+
+workers 2
+
+require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
+require_relative "/home/git/gitlab/lib/gitlab/cluster/puma_worker_killer_initializer"
+
+on_restart do
+ # Signal application hooks that we're about to restart
+ Gitlab::Cluster::LifecycleEvents.do_master_restart
+end
+
+before_fork do
+ # Signal to the puma killer
+ Gitlab::Cluster::PumaWorkerKillerInitializer.start @config.options unless ENV['DISABLE_PUMA_WORKER_KILLER']
+
+ # Signal application hooks that we're about to fork
+ Gitlab::Cluster::LifecycleEvents.do_before_fork
+end
+
+Gitlab::Cluster::LifecycleEvents.set_puma_options @config.options
+on_worker_boot do
+ # Signal application hooks of worker start
+ Gitlab::Cluster::LifecycleEvents.do_worker_start
+end
+
+# Preload the application before starting the workers; this conflicts with
+# phased restart feature. (off by default)
+
+preload_app!
+
+tag 'gitlab-puma-worker'
+
+# Verifies that all workers have checked in to the master process within
+# the given timeout. If not the worker process will be restarted. Default
+# value is 60 seconds.
+#
+worker_timeout 60
diff --git a/config/routes.rb b/config/routes.rb
index d16a587c5ee..484e05114be 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -27,6 +27,15 @@ Rails.application.routes.draw do
authorizations: 'oauth/authorizations'
end
+ # This is here so we can "reserve" the path for the Jira integration in GitLab EE
+ # Having a non-existent controller here does not affect the scope in any way since all possible routes
+ # get a 404 proc returned. It is written in this way to minimize merge conflicts with EE
+ scope path: '/login/oauth', controller: 'oauth/jira/authorizations', as: :oauth_jira do
+ match '*all', via: [:get, :post], to: proc { [404, {}, ['']] }
+ end
+
+ draw :oauth
+
use_doorkeeper_openid_connect
# Autocomplete
@@ -49,7 +58,6 @@ Rails.application.routes.draw do
# '/-/health' implemented by BasicHealthMiddleware
get 'liveness' => 'health#liveness'
get 'readiness' => 'health#readiness'
- post 'storage_check' => 'health#storage_check'
resources :metrics, only: [:index]
mount Peek::Railtie => '/peek', as: 'peek_routes'
@@ -72,11 +80,33 @@ Rails.application.routes.draw do
get 'ide' => 'ide#index'
get 'ide/*vueroute' => 'ide#index', format: false
+ draw :operations
draw :instance_statistics
+
+ if ENV['GITLAB_ENABLE_CHAOS_ENDPOINTS']
+ get '/chaos/leakmem' => 'chaos#leakmem'
+ get '/chaos/cpuspin' => 'chaos#cpuspin'
+ get '/chaos/sleep' => 'chaos#sleep'
+ get '/chaos/kill' => 'chaos#kill'
+ end
end
- # Koding route
- get 'koding' => 'koding#index'
+ concern :clusterable do
+ resources :clusters, only: [:index, :new, :show, :update, :destroy] do
+ collection do
+ post :create_user
+ post :create_gcp
+ end
+
+ member do
+ scope :applications do
+ post '/:application', to: 'clusters/applications#create', as: :install_applications
+ end
+
+ get :cluster_status, format: :json
+ end
+ end
+ end
draw :api
draw :sidekiq
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 7ee960970f8..af333bdc748 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -59,7 +59,7 @@ namespace :admin do
resources :hook_logs, only: [:show] do
member do
- get :retry
+ post :retry
end
end
end
@@ -69,10 +69,9 @@ namespace :admin do
end
resource :logs, only: [:show]
- resource :health_check, controller: 'health_check', only: [:show] do
- post :reset_storage_health
- end
+ resource :health_check, controller: 'health_check', only: [:show]
resource :background_jobs, controller: 'background_jobs', only: [:show]
+
resource :system_info, controller: 'system_info', only: [:show]
resources :requests_profiles, only: [:index, :show], param: :name, constraints: { name: /.+\.html/ }
@@ -106,10 +105,12 @@ namespace :admin do
resource :application_settings, only: [:show, :update] do
resources :services, only: [:index, :edit, :update]
+
get :usage_data
- put :reset_runners_token
+ put :reset_registration_token
put :reset_health_check_token
put :clear_repository_check_states
+ get :integrations, :repository, :templates, :ci_cd, :reporting, :metrics_and_profiling, :network, :geo, :preferences
end
resources :labels
diff --git a/config/routes/group.rb b/config/routes/group.rb
index 25fbb38ba87..a0aeebe4b91 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
resources :groups, only: [:index, :new, :create] do
post :preview_markdown
end
@@ -14,6 +16,9 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :projects, as: :projects_group
get :activity, as: :activity_group
put :transfer, as: :transfer_group
+ # TODO: Remove as part of refactor in https://gitlab.com/gitlab-org/gitlab-ce/issues/49693
+ get 'shared', action: :show, as: :group_shared
+ get 'archived', action: :show, as: :group_archived
end
get '/', action: :show, as: :group_canonical
@@ -24,8 +29,9 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
as: :group,
constraints: { group_id: Gitlab::PathRegex.full_namespace_route_regex }) do
namespace :settings do
- resource :ci_cd, only: [:show], controller: 'ci_cd'
- resources :badges, only: [:index]
+ resource :ci_cd, only: [:show], controller: 'ci_cd' do
+ put :reset_registration_token
+ end
end
resource :variables, only: [:show, :update]
@@ -37,7 +43,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
post :toggle_subscription, on: :member
end
- resources :milestones, constraints: { id: %r{[^/]+} }, only: [:index, :show, :edit, :update, :new, :create] do
+ resources :milestones, constraints: { id: %r{[^/]+} } do
member do
get :merge_requests
get :participants
@@ -47,6 +53,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :avatar, only: [:destroy]
+ concerns :clusterable
+
resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do
post :resend_invite, on: :member
delete :leave, on: :collection
@@ -59,7 +67,6 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end
end
- # On CE only index and show actions are needed
resources :boards, only: [:index, :show]
resources :runners, only: [:index, :edit, :update, :destroy, :show] do
diff --git a/config/routes/instance_statistics.rb b/config/routes/instance_statistics.rb
index 824ef47cda3..1102ef6b017 100644
--- a/config/routes/instance_statistics.rb
+++ b/config/routes/instance_statistics.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
namespace :instance_statistics do
- root to: redirect('/-/instance_statistics/conversational_development_index')
+ root to: redirect('-/instance_statistics/conversational_development_index')
resources :cohorts, only: :index
resources :conversational_development_index, only: :index
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 0220e88c819..3f1ad90dfca 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -32,6 +32,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get 'labels'
get 'milestones'
get 'commands'
+ get 'snippets'
end
end
@@ -145,7 +146,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
- controller 'merge_requests/creations', path: 'merge_requests' do
+ scope path: 'merge_requests', controller: 'merge_requests/creations' do
post '', action: :create, as: nil
scope path: 'new', as: :new_merge_request do
@@ -177,6 +178,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :mirror, only: [:show, :update] do
member do
+ get :ssh_host_keys, constraints: { format: :json }
post :update_now
end
end
@@ -205,20 +207,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
- resources :clusters, except: [:edit, :create] do
- collection do
- post :create_gcp
- post :create_user
- end
-
- member do
- get :status, format: :json
-
- scope :applications do
- post '/:application', to: 'clusters/applications#create', as: :install_applications
- end
- end
- end
+ concerns :clusterable
resources :environments, except: [:destroy] do
member do
@@ -275,6 +264,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
member do
get :status
post :cancel
+ post :unschedule
post :retry
post :play
post :erase
@@ -307,7 +297,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :hook_logs, only: [:show] do
member do
- get :retry
+ post :retry
end
end
end
@@ -365,7 +355,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :discussions, format: :json
end
collection do
- post :bulk_update
+ post :bulk_update
end
end
@@ -437,12 +427,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :members, to: redirect("%{namespace_id}/%{project_id}/project_members")
resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do
post :reset_cache
+ put :reset_registration_token
end
resource :integrations, only: [:show]
resource :repository, only: [:show], controller: :repository do
post :create_deploy_token, path: 'deploy_token/create'
end
- resources :badges, only: [:index]
end
# Since both wiki and repository routing contains wildcard characters
diff --git a/config/routes/user.rb b/config/routes/user.rb
index bc7df5e7584..e0ae264e2c0 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -45,6 +45,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d
get :contributed, as: :contributed_projects
get :snippets
get :exists
+ get :activity
get '/', to: redirect('%{username}'), as: nil
end
diff --git a/config/routes/wiki.rb b/config/routes/wiki.rb
index cd3828b743c..1a07b1c206b 100644
--- a/config/routes/wiki.rb
+++ b/config/routes/wiki.rb
@@ -2,7 +2,7 @@ scope(controller: :wikis) do
scope(path: 'wikis', as: :wikis) do
get :git_access
get :pages
- get '/', to: redirect('/%{namespace_id}/%{project_id}/wikis/home')
+ get '/', to: redirect('%{namespace_id}/%{project_id}/wikis/home')
post '/', to: 'wikis#create'
end
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index fb7738a5536..53e1c8778b6 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -29,6 +29,7 @@
- [pipeline_creation, 4]
- [pipeline_default, 3]
- [pipeline_cache, 3]
+ - [deployment, 3]
- [pipeline_hooks, 2]
- [gitlab_shell, 2]
- [email_receiver, 2]
@@ -46,6 +47,7 @@
- [project_service, 1]
- [delete_user, 1]
- [todos_destroyer, 1]
+ - [delete_container_repository, 1]
- [delete_merged_branches, 1]
- [authorized_projects, 1]
- [expire_build_instance_artifacts, 1]
@@ -78,3 +80,4 @@
- [create_note_diff_file, 1]
- [delete_diff_files, 1]
- [detect_repository_languages, 1]
+ - [auto_devops, 2]
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index 020e9a00d87..4637eb8bc6e 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -81,22 +81,16 @@ preload_app true
# fast LAN.
check_client_connection false
+require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
+
before_exec do |server|
- # The following is necessary to ensure stale Prometheus metrics don't
- # accumulate over time. It needs to be done in this hook as opposed to
- # inside an init script to ensure metrics files aren't deleted after new
- # unicorn workers start after a SIGUSR2 is received.
- if ENV['prometheus_multiproc_dir']
- old_metrics = Dir[File.join(ENV['prometheus_multiproc_dir'], '*.db')]
- FileUtils.rm_rf(old_metrics)
- end
+ # Signal application hooks that we're about to restart
+ Gitlab::Cluster::LifecycleEvents.do_master_restart
end
before_fork do |server, worker|
- # the following is highly recommended for Rails + "preload_app true"
- # as there's no need for the master process to hold a connection
- defined?(ActiveRecord::Base) and
- ActiveRecord::Base.connection.disconnect!
+ # Signal application hooks that we're about to fork
+ Gitlab::Cluster::LifecycleEvents.do_before_fork
# The following is only recommended for memory/DB-constrained
# installations. It is not needed if your system can house
@@ -124,25 +118,10 @@ before_fork do |server, worker|
end
after_fork do |server, worker|
- # Unicorn clears out signals before it forks, so rbtrace won't work
- # unless it is enabled after the fork.
- require 'rbtrace' if ENV['ENABLE_RBTRACE']
+ # Signal application hooks of worker start
+ Gitlab::Cluster::LifecycleEvents.do_worker_start
# per-process listener ports for debugging/admin/migrations
# addr = "127.0.0.1:#{9293 + worker.nr}"
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
-
- # the following is *required* for Rails + "preload_app true",
- defined?(ActiveRecord::Base) and
- ActiveRecord::Base.establish_connection
-
- # reset prometheus client, this will cause any opened metrics files to be closed
- defined?(::Prometheus::Client.reinitialize_on_pid_change) &&
- Prometheus::Client.reinitialize_on_pid_change
-
- # if preload_app is true, then you may also want to check and
- # restart any other shared sockets/descriptors such as Memcached,
- # and Redis. TokyoCabinet file handles are safe to reuse
- # between any number of forked children (assuming your kernel
- # correctly implements pread()/pwrite() system calls)
end
diff --git a/config/unicorn.rb.example.development b/config/unicorn.rb.example.development
index 5712549a66d..f7541bb9d55 100644
--- a/config/unicorn.rb.example.development
+++ b/config/unicorn.rb.example.development
@@ -1,32 +1,61 @@
+# frozen_string_literal: true
+
+# -------------------------------------------------------------------------
+# This file is used by the GDK to generate a default config/unicorn.rb file
+# Note that `/home/git` will be substituted for the actual GDK root
+# directory when this file is generated
+# -------------------------------------------------------------------------
+
worker_processes 2
timeout 60
+listen '/home/git/gitlab.socket'
+
preload_app true
check_client_connection false
+require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
+
+before_exec do |server|
+ # Signal application hooks that we're about to restart
+ Gitlab::Cluster::LifecycleEvents.do_master_restart
+end
+
before_fork do |server, worker|
- # the following is highly recommended for Rails + "preload_app true"
- # as there's no need for the master process to hold a connection
- defined?(ActiveRecord::Base) and
- ActiveRecord::Base.connection.disconnect!
-
- if /darwin/ =~ RUBY_PLATFORM
- require 'fiddle'
-
- # Dynamically load Foundation.framework, ~implicitly~ initialising
- # the Objective-C runtime before any forking happens in Unicorn
- #
- # From https://bugs.ruby-lang.org/issues/14009
- Fiddle.dlopen '/System/Library/Frameworks/Foundation.framework/Foundation'
+ # Signal application hooks that we're about to fork
+ Gitlab::Cluster::LifecycleEvents.do_before_fork
+
+ # The following is only recommended for memory/DB-constrained
+ # installations. It is not needed if your system can house
+ # twice as many worker_processes as you have configured.
+ #
+ # This allows a new master process to incrementally
+ # phase out the old master process with SIGTTOU to avoid a
+ # thundering herd (especially in the "preload_app false" case)
+ # when doing a transparent upgrade. The last worker spawned
+ # will then kill off the old master process with a SIGQUIT.
+ old_pid = "#{server.config[:pid]}.oldbin"
+ if old_pid != server.pid
+ begin
+ sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
+ Process.kill(sig, File.read(old_pid).to_i)
+ rescue Errno::ENOENT, Errno::ESRCH
+ end
end
+ #
+ # Throttle the master from forking too quickly by sleeping. Due
+ # to the implementation of standard Unix signal handlers, this
+ # helps (but does not completely) prevent identical, repeated signals
+ # from being lost when the receiving process is busy.
+ # sleep 1
end
after_fork do |server, worker|
- # Unicorn clears out signals before it forks, so rbtrace won't work
- # unless it is enabled after the fork.
- require 'rbtrace' if ENV['ENABLE_RBTRACE']
+ # Signal application hooks of worker start
+ Gitlab::Cluster::LifecycleEvents.do_worker_start
- # the following is *required* for Rails + "preload_app true",
- defined?(ActiveRecord::Base) and
- ActiveRecord::Base.establish_connection
+ # per-process listener ports for debugging/admin/migrations
+ # addr = "127.0.0.1:#{9293 + worker.nr}"
+ # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
end
+
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 583f05f2fb7..9ecae9790fd 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -8,7 +8,7 @@ const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ROOT_PATH = path.resolve(__dirname, '..');
-const CACHE_PATH = path.join(ROOT_PATH, 'tmp/cache');
+const CACHE_PATH = process.env.WEBPACK_CACHE_PATH || path.join(ROOT_PATH, 'tmp/cache');
const IS_PRODUCTION = process.env.NODE_ENV === 'production';
const IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1;
const DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
diff --git a/danger/changelog/Dangerfile b/danger/changelog/Dangerfile
index a1f94dc6004..713ed95a04c 100644
--- a/danger/changelog/Dangerfile
+++ b/danger/changelog/Dangerfile
@@ -53,9 +53,11 @@ end
changelog_needed = (gitlab.mr_labels & NO_CHANGELOG_LABELS).empty?
changelog_found = git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
+mr_title = gitlab.mr_json["title"].gsub(/^WIP: */, '')
+
if git.modified_files.include?("CHANGELOG.md")
fail "**CHANGELOG.md was edited.** Please remove the additions and create a CHANGELOG entry.\n\n" +
- format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels)
+ format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: mr_title, labels: presented_no_changelog_labels)
end
if changelog_needed
@@ -63,6 +65,6 @@ if changelog_needed
check_changelog(changelog_found)
else
warn "**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html).**\n\n" +
- format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels)
+ format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: mr_title, labels: presented_no_changelog_labels)
end
end
diff --git a/danger/commit_messages/Dangerfile b/danger/commit_messages/Dangerfile
new file mode 100644
index 00000000000..c5ebb9b457e
--- /dev/null
+++ b/danger/commit_messages/Dangerfile
@@ -0,0 +1,236 @@
+# frozen_string_literal: true
+
+require 'json'
+
+# rubocop: disable Style/SignalException
+# rubocop: disable Metrics/CyclomaticComplexity
+# rubocop: disable Metrics/PerceivedComplexity
+
+# Perform various checks against commits. We're not using
+# https://github.com/jonallured/danger-commit_lint because its output is not
+# very helpful, and it doesn't offer the means of ignoring merge commits.
+
+class EmojiChecker
+ DIGESTS = File.expand_path('../../fixtures/emojis/digests.json', __dir__)
+ ALIASES = File.expand_path('../../fixtures/emojis/aliases.json', __dir__)
+
+ # A regex that indicates a piece of text _might_ include an Emoji. The regex
+ # alone is not enough, as we'd match `:foo:bar:baz`. Instead, we use this
+ # regex to save us from having to check for all possible emoji names when we
+ # know one definitely is not included.
+ LIKELY_EMOJI = /:[\+a-z0-9_\-]+:/
+
+ def initialize
+ names = JSON.parse(File.read(DIGESTS)).keys +
+ JSON.parse(File.read(ALIASES)).keys
+
+ @emoji = names.map { |name| ":#{name}:" }
+ end
+
+ def includes_emoji?(text)
+ return false unless text.match?(LIKELY_EMOJI)
+
+ @emoji.any? { |emoji| text.include?(emoji) }
+ end
+end
+
+def fail_commit(commit, message)
+ fail("#{commit.sha}: #{message}")
+end
+
+def warn_commit(commit, message)
+ warn("#{commit.sha}: #{message}")
+end
+
+def lines_changed_in_commit(commit)
+ commit.diff_parent.stats[:total][:lines]
+end
+
+def subject_starts_with_capital?(subject)
+ first_char = subject.chars.first
+
+ first_char.upcase == first_char
+end
+
+def ce_upstream?
+ gitlab.mr_labels.any? { |label| label == 'CE upstream' }
+end
+
+def too_many_changed_lines?(commit)
+ commit.diff_parent.stats[:total][:files] > 3 &&
+ lines_changed_in_commit(commit) >= 30
+end
+
+def lint_commits(commits)
+ failures = false
+ emoji_checker = EmojiChecker.new
+
+ unicode_emoji_regex = %r((
+ [\u{1F300}-\u{1F5FF}] |
+ [\u{1F1E6}-\u{1F1FF}] |
+ [\u{2700}-\u{27BF}] |
+ [\u{1F900}-\u{1F9FF}] |
+ [\u{1F600}-\u{1F64F}] |
+ [\u{1F680}-\u{1F6FF}] |
+ [\u{2600}-\u{26FF}]
+ ))x
+
+ commits.each do |commit|
+ # For now we'll ignore merge commits, as getting rid of those is a problem
+ # separate from enforcing good commit messages.
+ next if commit.message.start_with?('Merge branch')
+
+ subject, separator, details = commit.message.split("\n", 3)
+
+ if subject.split.length < 3
+ fail_commit(
+ commit,
+ 'The commit subject must contain at least three words'
+ )
+
+ failures = true
+ end
+
+ if subject.length > 72
+ fail_commit(
+ commit,
+ 'The commit subject may not be longer than 72 characters'
+ )
+
+ failures = true
+ elsif subject.length > 50
+ warn_commit(
+ commit,
+ "This commit's subject line could be improved. " \
+ 'Commit subjects are ideally no longer than roughly 50 characters, ' \
+ 'though we allow up to 72 characters in the subject. ' \
+ 'If possible, try to reduce the length of the subject to roughly 50 characters.'
+ )
+ end
+
+ unless subject_starts_with_capital?(subject)
+ fail_commit(commit, 'The commit subject must start with a capital letter')
+ failures = true
+ end
+
+ if subject.end_with?('.')
+ fail_commit(commit, 'The commit subject must not end with a period')
+ failures = true
+ end
+
+ if separator && !separator.empty?
+ fail_commit(
+ commit,
+ 'The commit subject and body must be separated by a blank line'
+ )
+
+ failures = true
+ end
+
+ details&.each_line do |line|
+ line = line.strip
+
+ next if line.length <= 72
+
+ url_size = line.scan(%r((https?://\S+))).sum { |(url)| url.length }
+
+ # If the line includes a URL, we'll allow it to exceed 72 characters, but
+ # only if the line _without_ the URL does not exceed this limit.
+ next if line.length - url_size <= 72
+
+ fail_commit(
+ commit,
+ 'The commit body should not contain more than 72 characters per line'
+ )
+
+ failures = true
+ end
+
+ if !details && too_many_changed_lines?(commit)
+ fail_commit(
+ commit,
+ 'Commits that change 30 or more lines in more than three files ' \
+ 'must describe these changes in the commit body'
+ )
+
+ failures = true
+ end
+
+ if emoji_checker.includes_emoji?(commit.message)
+ fail_commit(
+ commit,
+ 'Avoid the use of Markdown Emoji such as `:+1:`. ' \
+ 'These add no value to the commit message, ' \
+ 'and are displayed as plain text outside of GitLab'
+ )
+
+ failures = true
+ end
+
+ if commit.message.match?(unicode_emoji_regex)
+ fail_commit(
+ commit,
+ 'Avoid the use of Unicode Emoji. ' \
+ 'These add no value to the commit message, ' \
+ 'and may not be displayed properly everywhere'
+ )
+
+ failures = true
+ end
+
+ if commit.message.match?(%r(([\w\-\/]+)?(#|!|&|%)\d+))
+ fail_commit(
+ commit,
+ 'Use full URLs instead of short references ' \
+ '(`gitlab-org/gitlab-ce#123` or `!123`), as short references are ' \
+ 'displayed as plain text outside of GitLab'
+ )
+
+ failures = true
+ end
+ end
+
+ if failures
+ markdown(<<~MARKDOWN)
+ ## Commit message standards
+
+ One or more commit messages do not meet our Git commit message standards.
+ For more information on how to write a good commit message, take a look at
+ [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/).
+
+ Here is an example of a good commit message:
+
+ Reject ruby interpolation in externalized strings
+
+ When using ruby interpolation in externalized strings, they can't be
+ detected. Which means they will never be presented to be translated.
+
+ To mix variables into translations we need to use `sprintf`
+ instead.
+
+ Instead of:
+
+ _("Hello \#{subject}")
+
+ Use:
+
+ _("Hello %{subject}") % { subject: 'world' }
+
+ This is an example of a bad commit message:
+
+ updated README.md
+
+ This commit message is bad because although it tells us that README.md is
+ updated, it doesn't tell us why or how it was updated.
+ MARKDOWN
+ end
+end
+
+if git.commits.length > 10 && !ce_upstream?
+ warn(
+ 'This merge request includes more than 10 commits. ' \
+ 'Please rebase these commits into a smaller number of commits.'
+ )
+else
+ lint_commits(git.commits)
+end
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index ad5f1c1e0f3..38ccbd94edb 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -39,8 +39,6 @@ def database_paths_requiring_review(files)
to_review
end
-all_files = git.added_files + git.modified_files
-
non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/schema\.rb}).empty?
geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb}).empty?
@@ -55,7 +53,7 @@ if geo_migration_created && !geo_db_schema_updated
warn format(SCHEMA_NOT_UPDATED_MESSAGE, migrations: 'Geo migrations', schema: gitlab.html_link("ee/db/geo/schema.rb"))
end
-db_paths_to_review = database_paths_requiring_review(all_files)
+db_paths_to_review = database_paths_requiring_review(helper.all_changed_files)
unless db_paths_to_review.empty?
message 'This merge request adds or changes files that require a ' \
diff --git a/danger/documentation/Dangerfile b/danger/documentation/Dangerfile
new file mode 100644
index 00000000000..87c61d6e90d
--- /dev/null
+++ b/danger/documentation/Dangerfile
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+# All the files/directories that should be reviewed by the Docs team.
+DOCS_FILES = [
+ 'doc/'
+].freeze
+
+def docs_paths_requiring_review(files)
+ files.select do |file|
+ DOCS_FILES.any? { |pattern| file.start_with?(pattern) }
+ end
+end
+
+docs_paths_to_review = docs_paths_requiring_review(helper.all_changed_files)
+
+unless docs_paths_to_review.empty?
+ message 'This merge request adds or changes files that require a ' \
+ 'review from the Docs team.'
+
+ markdown(<<~MARKDOWN)
+## Docs review
+
+The following files require a review from the Documentation team:
+
+* #{docs_paths_to_review.map { |path| "`#{path}`" }.join("\n* ")}
+
+When your content is ready for review, mention a technical writer in a separate
+comment and explain what needs to be reviewed.
+
+You are welcome to mention them sooner if you have questions about writing or updating
+the documentation. GitLabbers are also welcome to use the [#docs](https://gitlab.slack.com/archives/C16HYA2P5) channel on Slack.
+
+Who to ping [based on DevOps stages](https://about.gitlab.com/handbook/product/categories/#devops-stages):
+
+| Tech writer | Stage(s) |
+| ------------ | ------------------------------------------------------------ |
+| `@marcia` | ~Create ~Release |
+| `@axil` | ~Distribution ~Gitaly ~Gitter ~Monitoring ~Packaging ~Secure |
+| `@eread` | ~Manage ~Configure ~Geo ~Verify |
+| `@mikelewis` | ~Plan |
+
+If you are not sure which category the change falls within, or the change is not
+part of one of these categories, you can mention one of the usernames above.
+ MARKDOWN
+
+ unless gitlab.mr_labels.include?('Documentation')
+ warn 'This merge request is missing the ~Documentation label.'
+ end
+end
diff --git a/danger/eslint/Dangerfile b/danger/eslint/Dangerfile
new file mode 100644
index 00000000000..4916cacfd7e
--- /dev/null
+++ b/danger/eslint/Dangerfile
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+def get_eslint_files(files)
+ files.select do |file|
+ file.end_with?('.js', '.vue') &&
+ File.read(file).include?('/* eslint-disable')
+ end
+end
+
+eslint_candidates = get_eslint_files(helper.all_changed_files)
+
+return if eslint_candidates.empty?
+
+warn 'This merge request changed files with disabled eslint rules. Please consider fixing them.'
+
+markdown(<<~MARKDOWN)
+ ## Disabled eslint rules
+
+ The following files have disabled `eslint` rules. Please consider fixing them:
+
+ * #{eslint_candidates.map { |path| "`#{path}`" }.join("\n* ")}
+
+ Run the following command for more details
+
+ ```
+ node_modules/.bin/eslint --report-unused-disable-directives --no-inline-config \\
+ #{eslint_candidates.map { |path| " '#{path}'" }.join(" \\\n")}
+ ```
+MARKDOWN
diff --git a/danger/metadata/Dangerfile b/danger/metadata/Dangerfile
index 3cfaa04e01b..1adca152736 100644
--- a/danger/metadata/Dangerfile
+++ b/danger/metadata/Dangerfile
@@ -21,5 +21,12 @@ end
has_pick_into_stable_label = gitlab.mr_labels.find { |label| label.start_with?('Pick into') }
if gitlab.branch_for_base != "master" && !has_pick_into_stable_label
- warn "Most of the time, all merge requests should target `master`. Otherwise, please set the relevant `Pick into X.Y` label."
+ warn "Most of the time, merge requests should target `master`. Otherwise, please set the relevant `Pick into X.Y` label."
+end
+
+if gitlab.mr_json['title'].length > 72
+ warn 'The title of this merge request is longer than 72 characters and ' \
+ 'would violate our commit message rules when using the Squash on Merge ' \
+ 'feature. Please consider adjusting the title, or rebase the ' \
+ "commits manually and don't use Squash on Merge."
end
diff --git a/danger/plugins/helper.rb b/danger/plugins/helper.rb
new file mode 100644
index 00000000000..f4eb9119266
--- /dev/null
+++ b/danger/plugins/helper.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Danger
+ # Common helper functions for our danger scripts
+ # If we find ourselves repeating code in our danger files, we might as well put them in here.
+ class Helper < Plugin
+ # Returns a list of all files that have been added, modified or renamed.
+ # `git.modified_files` might contain paths that already have been renamed,
+ # so we need to remove them from the list.
+ #
+ # Considering these changes:
+ #
+ # - A new_file.rb
+ # - D deleted_file.rb
+ # - M modified_file.rb
+ # - R renamed_file_before.rb -> renamed_file_after.rb
+ #
+ # it will return
+ # ```
+ # [ 'new_file.rb', 'modified_file.rb', 'renamed_file_after.rb' ]
+ # ```
+ #
+ # @return [Array<String>]
+ def all_changed_files
+ Set.new
+ .merge(git.added_files.to_a)
+ .merge(git.modified_files.to_a)
+ .merge(git.renamed_files.map { |x| x[:after] })
+ .subtract(git.renamed_files.map { |x| x[:before] })
+ .to_a
+ .sort
+ end
+ end
+end
diff --git a/danger/prettier/Dangerfile b/danger/prettier/Dangerfile
new file mode 100644
index 00000000000..37c4b78a213
--- /dev/null
+++ b/danger/prettier/Dangerfile
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+def get_prettier_files(files)
+ files.select do |file|
+ file.end_with?('.js', '.scss', '.vue')
+ end
+end
+
+prettier_candidates = get_prettier_files(helper.all_changed_files)
+
+return if prettier_candidates.empty?
+
+unpretty = `node_modules/prettier/bin-prettier.js --list-different #{prettier_candidates.join(" ")}`
+ .split(/$/)
+ .map(&:strip)
+ .reject(&:empty?)
+
+return if unpretty.empty?
+
+warn 'This merge request changed frontend files without pretty printing them.'
+
+markdown(<<~MARKDOWN)
+ ## Pretty print Frontend files
+
+ The following files should have been pretty printed with `prettier`:
+
+ * #{unpretty.map { |path| "`#{path}`" }.join("\n* ")}
+
+ Please run
+
+ ```
+ node_modules/.bin/prettier --write \\
+ #{unpretty.map { |path| " '#{path}'" }.join(" \\\n")}
+ ```
+
+ Also consider auto-formatting [on-save].
+
+ [on-save]: https://docs.gitlab.com/ee/development/new_fe_guide/style/prettier.html
+MARKDOWN
diff --git a/danger/specs/Dangerfile b/danger/specs/Dangerfile
index 97188df8785..a526bb8adaa 100644
--- a/danger/specs/Dangerfile
+++ b/danger/specs/Dangerfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
NO_SPECS_LABELS = %w[backstage Documentation QA].freeze
NO_NEW_SPEC_MESSAGE = <<~MSG.freeze
You've made some app changes, but didn't add any tests.
@@ -9,8 +11,8 @@ def presented_no_changelog_labels
NO_SPECS_LABELS.map { |label| "~#{label}" }.join(', ')
end
-has_app_changes = !git.modified_files.grep(%r{\A(ee/)?(app|lib|db/(geo/)?(post_)?migrate)/}).empty?
-has_spec_changes = !git.modified_files.grep(%r{\A(ee/)?spec/}).empty?
+has_app_changes = !helper.all_changed_files.grep(%r{\A(ee/)?(app|lib|db/(geo/)?(post_)?migrate)/}).empty?
+has_spec_changes = !helper.all_changed_files.grep(%r{\A(ee/)?spec/}).empty?
new_specs_needed = (gitlab.mr_labels & NO_SPECS_LABELS).empty?
if has_app_changes && !has_spec_changes && new_specs_needed
diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb
index 51e69879c79..089de211380 100644
--- a/db/fixtures/development/04_project.rb
+++ b/db/fixtures/development/04_project.rb
@@ -7,8 +7,8 @@ Sidekiq::Testing.inline! do
'https://gitlab.com/gitlab-org/gitlab-shell.git',
'https://gitlab.com/gnuwget/wget2.git',
'https://gitlab.com/Commit451/LabCoat.git',
- 'https://github.com/documentcloud/underscore.git',
- 'https://github.com/twitter/flight.git',
+ 'https://github.com/jashkenas/underscore.git',
+ 'https://github.com/flightjs/flight.git',
'https://github.com/twitter/typeahead.js.git',
'https://github.com/h5bp/html5-boilerplate.git',
'https://github.com/google/material-design-lite.git',
@@ -20,18 +20,18 @@ Sidekiq::Testing.inline! do
'https://github.com/airbnb/javascript.git',
'https://github.com/tessalt/echo-chamber-js.git',
'https://github.com/atom/atom.git',
- 'https://github.com/mattermost/platform.git',
+ 'https://github.com/mattermost/mattermost-server.git',
'https://github.com/purifycss/purifycss.git',
'https://github.com/facebook/nuclide.git',
'https://github.com/wbkd/awesome-d3.git',
'https://github.com/kilimchoi/engineering-blogs.git',
'https://github.com/gilbarbara/logos.git',
- 'https://github.com/gaearon/redux.git',
+ 'https://github.com/reduxjs/redux.git',
'https://github.com/awslabs/s2n.git',
'https://github.com/arkency/reactjs_koans.git',
'https://github.com/twbs/bootstrap.git',
'https://github.com/chjj/ttystudio.git',
- 'https://github.com/DrBoolean/mostly-adequate-guide.git',
+ 'https://github.com/MostlyAdequate/mostly-adequate-guide.git',
'https://github.com/octocat/Spoon-Knife.git',
'https://github.com/opencontainers/runc.git',
'https://github.com/googlesamples/android-topeka.git'
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index 0b32a461d56..16243b72f9a 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -8,7 +8,8 @@ Gitlab::Seeder.quiet do
description: FFaker::Lorem.sentence,
state: ['opened', 'closed'].sample,
milestone: project.milestones.sample,
- assignees: [project.team.users.sample]
+ assignees: [project.team.users.sample],
+ created_at: rand(12).months.ago
}
Issues::CreateService.new(project, project.team.users.sample, issue_params).execute
diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb
index 5535c4a14e5..5af77c49913 100644
--- a/db/fixtures/development/14_pipelines.rb
+++ b/db/fixtures/development/14_pipelines.rb
@@ -1,7 +1,7 @@
require './spec/support/sidekiq'
class Gitlab::Seeder::Pipelines
- STAGES = %w[build test deploy notify]
+ STAGES = %w[build test security deploy notify]
BUILDS = [
# build stage
{ name: 'build:linux', stage: 'build', status: :success,
@@ -31,6 +31,16 @@ class Gitlab::Seeder::Pipelines
{ name: 'spinach:osx', stage: 'test', status: :failed, allow_failure: true,
queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ # security stage
+ { name: 'dast', stage: 'security', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'sast', stage: 'security', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'dependency_scanning', stage: 'security', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'container_scanning', stage: 'security', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+
# deploy stage
{ name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success,
options: { environment: { action: 'start', on_stop: 'stop staging' } },
@@ -108,6 +118,11 @@ class Gitlab::Seeder::Pipelines
setup_artifacts(build)
setup_test_reports(build)
+ if build.ref == build.project.default_branch
+ setup_security_reports_file(build)
+ else
+ setup_security_reports_legacy_archive(build)
+ end
setup_build_log(build)
build.project.environments.
@@ -143,6 +158,55 @@ class Gitlab::Seeder::Pipelines
end
end
+ def setup_security_reports_file(build)
+ return unless build.stage == "security"
+
+ # we have two sources: master and feature-branch
+ branch_name = build.ref == build.project.default_branch ?
+ 'master' : 'feature-branch'
+
+ artifacts_cache_file(security_reports_path(branch_name, build.name)) do |file|
+ build.job_artifacts.build(
+ project: build.project,
+ file_type: build.name,
+ file_format: :raw,
+ file: file)
+ end
+ end
+
+ def setup_security_reports_legacy_archive(build)
+ return unless build.stage == "security"
+
+ # we have two sources: master and feature-branch
+ branch_name = build.ref == build.project.default_branch ?
+ 'master' : 'feature-branch'
+
+ artifacts_cache_file(security_reports_archive_path(branch_name)) do |file|
+ build.job_artifacts.build(
+ project: build.project,
+ file_type: :archive,
+ file_format: :zip,
+ file: file)
+ end
+
+ # assign dummy metadata
+ artifacts_cache_file(artifacts_metadata_path) do |file|
+ build.job_artifacts.build(
+ project: build.project,
+ file_type: :metadata,
+ file_format: :gzip,
+ file: file)
+ end
+
+ build.options = {
+ artifacts: {
+ paths: [
+ Ci::JobArtifact::DEFAULT_FILE_NAMES.fetch(build.name.to_sym)
+ ]
+ }
+ }
+ end
+
def setup_build_log(build)
if %w(running success failed).include?(build.status)
build.trace.set(FFaker::Lorem.paragraphs(6).join("\n\n"))
@@ -190,6 +254,15 @@ class Gitlab::Seeder::Pipelines
Rails.root + 'spec/fixtures/junit/junit.xml.gz'
end
+ def security_reports_archive_path(branch)
+ Rails.root.join('spec', 'fixtures', 'security-reports', branch + '.zip')
+ end
+
+ def security_reports_path(branch, name)
+ file_name = Ci::JobArtifact::DEFAULT_FILE_NAMES.fetch(name.to_sym)
+ Rails.root.join('spec', 'fixtures', 'security-reports', branch, file_name)
+ end
+
def artifacts_cache_file(file_path)
file = Tempfile.new("artifacts")
file.close
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index 7b9a4bad449..7a86fe2eb7c 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -6,20 +6,6 @@ class Gitlab::Seeder::CycleAnalytics
@project = project
@user = User.admins.first
@issue_count = perf ? 1000 : 5
- stub_git_pre_receive!
- end
-
- # The GitLab API needn't be running for the fixtures to be
- # created. Since we're performing a number of git actions
- # here (like creating a branch or committing a file), we need
- # to disable the `pre_receive` hook in order to remove this
- # dependency on the GitLab API.
- def stub_git_pre_receive!
- Gitlab::Git::HooksService.class_eval do
- def run_hook(name)
- [true, '']
- end
- end
end
def seed_metrics!
@@ -194,11 +180,8 @@ class Gitlab::Seeder::CycleAnalytics
ref: "refs/heads/#{merge_request.source_branch}")
pipeline = service.execute(:push, ignore_skip_ci: true, save_on_errors: false)
- pipeline.run!
- Timecop.travel rand(1..6).hours.from_now
- pipeline.succeed!
-
- PipelineMetricsWorker.new.perform(pipeline.id)
+ pipeline.builds.map(&:run!)
+ pipeline.update_status
end
end
@@ -218,7 +201,8 @@ class Gitlab::Seeder::CycleAnalytics
job = merge_request.head_pipeline.builds.where.not(environment: nil).last
- CreateDeploymentService.new(job).execute
+ job.success!
+ pipeline.update_status
end
end
end
diff --git a/db/fixtures/development/99_common_metrics.rb b/db/fixtures/development/99_common_metrics.rb
new file mode 100644
index 00000000000..1f39c0ce5a0
--- /dev/null
+++ b/db/fixtures/development/99_common_metrics.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+require Rails.root.join('db/importers/common_metrics_importer.rb')
+
+::Importers::CommonMetricsImporter.new.execute
diff --git a/db/fixtures/production/999_common_metrics.rb b/db/fixtures/production/999_common_metrics.rb
new file mode 100644
index 00000000000..1f39c0ce5a0
--- /dev/null
+++ b/db/fixtures/production/999_common_metrics.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+require Rails.root.join('db/importers/common_metrics_importer.rb')
+
+::Importers::CommonMetricsImporter.new.execute
diff --git a/db/importers/common_metrics_importer.rb b/db/importers/common_metrics_importer.rb
new file mode 100644
index 00000000000..6302394d7a6
--- /dev/null
+++ b/db/importers/common_metrics_importer.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+module Importers
+ class PrometheusMetric < ActiveRecord::Base
+ enum group: {
+ # built-in groups
+ nginx_ingress: -1,
+ ha_proxy: -2,
+ aws_elb: -3,
+ nginx: -4,
+ kubernetes: -5,
+
+ # custom groups
+ business: 0,
+ response: 1,
+ system: 2
+ }
+
+ scope :common, -> { where(common: true) }
+
+ GROUP_TITLES = {
+ business: _('Business metrics (Custom)'),
+ response: _('Response metrics (Custom)'),
+ system: _('System metrics (Custom)'),
+ nginx_ingress: _('Response metrics (NGINX Ingress)'),
+ ha_proxy: _('Response metrics (HA Proxy)'),
+ aws_elb: _('Response metrics (AWS ELB)'),
+ nginx: _('Response metrics (NGINX)'),
+ kubernetes: _('System metrics (Kubernetes)')
+ }.freeze
+ end
+
+ class CommonMetricsImporter
+ MissingQueryId = Class.new(StandardError)
+
+ attr_reader :content
+
+ def initialize(filename = 'common_metrics.yml')
+ @content = YAML.load_file(Rails.root.join('config', 'prometheus', filename))
+ end
+
+ def execute
+ PrometheusMetric.reset_column_information
+
+ process_content do |id, attributes|
+ find_or_build_metric!(id)
+ .update!(**attributes)
+ end
+ end
+
+ private
+
+ def process_content(&blk)
+ content.map do |group|
+ process_group(group, &blk)
+ end
+ end
+
+ def process_group(group, &blk)
+ attributes = {
+ group: find_group_title_key(group['group'])
+ }
+
+ group['metrics'].map do |metric|
+ process_metric(metric, attributes, &blk)
+ end
+ end
+
+ def process_metric(metric, attributes, &blk)
+ attributes = attributes.merge(
+ title: metric['title'],
+ y_label: metric['y_label'])
+
+ metric['queries'].map do |query|
+ process_metric_query(query, attributes, &blk)
+ end
+ end
+
+ def process_metric_query(query, attributes, &blk)
+ attributes = attributes.merge(
+ legend: query['label'],
+ query: query['query_range'],
+ unit: query['unit'])
+
+ yield(query['id'], attributes)
+ end
+
+ def find_or_build_metric!(id)
+ raise MissingQueryId unless id
+
+ PrometheusMetric.common.find_by(identifier: id) ||
+ PrometheusMetric.new(common: true, identifier: id)
+ end
+
+ def find_group_title_key(title)
+ PrometheusMetric.groups[find_group_title(title)]
+ end
+
+ def find_group_title(title)
+ PrometheusMetric::GROUP_TITLES.invert[title]
+ end
+ end
+end
diff --git a/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb b/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb
index 668c22bb51c..8ebf1a5234d 100644
--- a/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb
+++ b/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb
@@ -16,6 +16,6 @@ class RemoveAwardEmojisWithNoUser < ActiveRecord::Migration
# disable_ddl_transaction!
def up
- AwardEmoji.joins('LEFT JOIN users ON users.id = user_id').where('users.id IS NULL').destroy_all
+ AwardEmoji.joins('LEFT JOIN users ON users.id = user_id').where('users.id IS NULL').destroy_all # rubocop: disable DestroyAll
end
end
diff --git a/db/migrate/20170222111732_create_gpg_keys.rb b/db/migrate/20170222111732_create_gpg_keys.rb
index 541228e8735..0d6d454bbf3 100644
--- a/db/migrate/20170222111732_create_gpg_keys.rb
+++ b/db/migrate/20170222111732_create_gpg_keys.rb
@@ -1,4 +1,6 @@
class CreateGpgKeys < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
DOWNTIME = false
def change
@@ -12,8 +14,8 @@ class CreateGpgKeys < ActiveRecord::Migration
t.text :key
- t.index :primary_keyid, unique: true, length: Gitlab::Database.mysql? ? 20 : nil
- t.index :fingerprint, unique: true, length: Gitlab::Database.mysql? ? 20 : nil
+ t.index :primary_keyid, unique: true, length: mysql_compatible_index_length
+ t.index :fingerprint, unique: true, length: mysql_compatible_index_length
end
end
end
diff --git a/db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb b/db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb
index 55bf40ba24d..cc5cb355579 100644
--- a/db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb
+++ b/db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb
@@ -13,7 +13,7 @@ class AddForeignKeyPipelineSchedulesAndPipelines < ActiveRecord::Migration
'SET NULL'
end
- add_concurrent_foreign_key :ci_pipelines, :ci_pipeline_schedules,
+ add_concurrent_foreign_key :ci_pipelines, :ci_pipeline_schedules,
column: :pipeline_schedule_id, on_delete: on_delete
end
diff --git a/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb b/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
index 1199073ed3a..12352d98a62 100644
--- a/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
+++ b/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
@@ -106,11 +106,11 @@ class ProjectForeignKeysWithCascadingDeletes < ActiveRecord::Migration
# Disables statement timeouts for the current connection. This is
# necessary as removing of orphaned data might otherwise exceed the
# statement timeout.
- disable_statement_timeout
+ disable_statement_timeout do
+ remove_orphans(*queue.pop) until queue.empty?
- remove_orphans(*queue.pop) until queue.empty?
-
- steal_from_queues(queues - [queue])
+ steal_from_queues(queues - [queue])
+ end
end
end
end
diff --git a/db/migrate/20170613154149_create_gpg_signatures.rb b/db/migrate/20170613154149_create_gpg_signatures.rb
index f6b5e7ebb7b..abef13a7a0b 100644
--- a/db/migrate/20170613154149_create_gpg_signatures.rb
+++ b/db/migrate/20170613154149_create_gpg_signatures.rb
@@ -1,4 +1,6 @@
class CreateGpgSignatures < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
DOWNTIME = false
def change
@@ -16,8 +18,8 @@ class CreateGpgSignatures < ActiveRecord::Migration
t.text :gpg_key_user_name
t.text :gpg_key_user_email
- t.index :commit_sha, unique: true, length: Gitlab::Database.mysql? ? 20 : nil
- t.index :gpg_key_primary_keyid, length: Gitlab::Database.mysql? ? 20 : nil
+ t.index :commit_sha, unique: true, length: mysql_compatible_index_length
+ t.index :gpg_key_primary_keyid, length: mysql_compatible_index_length
end
end
end
diff --git a/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb b/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb
index db60c2087b9..a770ff63b4e 100644
--- a/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb
+++ b/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb
@@ -25,8 +25,9 @@ class AddLowerPathIndexToRedirectRoutes < ActiveRecord::Migration
# trivial to write a query that checks for an index. BUT there is a
# convenient `IF EXISTS` parameter for `DROP INDEX`.
if supports_drop_index_concurrently?
- disable_statement_timeout
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};"
+ disable_statement_timeout do
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};"
+ end
else
execute "DROP INDEX IF EXISTS #{INDEX_NAME};"
end
diff --git a/db/migrate/20170827123848_add_index_on_merge_request_diff_commit_sha.rb b/db/migrate/20170827123848_add_index_on_merge_request_diff_commit_sha.rb
index 1b360b231a8..2140ff7b05e 100644
--- a/db/migrate/20170827123848_add_index_on_merge_request_diff_commit_sha.rb
+++ b/db/migrate/20170827123848_add_index_on_merge_request_diff_commit_sha.rb
@@ -8,7 +8,7 @@ class AddIndexOnMergeRequestDiffCommitSha < ActiveRecord::Migration
disable_ddl_transaction!
def up
- add_concurrent_index :merge_request_diff_commits, :sha, length: Gitlab::Database.mysql? ? 20 : nil
+ add_concurrent_index :merge_request_diff_commits, :sha, length: mysql_compatible_index_length
end
def down
diff --git a/db/migrate/20170927161718_create_gpg_key_subkeys.rb b/db/migrate/20170927161718_create_gpg_key_subkeys.rb
index c03c40416a8..d9dc2404cac 100644
--- a/db/migrate/20170927161718_create_gpg_key_subkeys.rb
+++ b/db/migrate/20170927161718_create_gpg_key_subkeys.rb
@@ -1,4 +1,6 @@
class CreateGpgKeySubkeys < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
DOWNTIME = false
def up
@@ -8,8 +10,8 @@ class CreateGpgKeySubkeys < ActiveRecord::Migration
t.binary :keyid
t.binary :fingerprint
- t.index :keyid, unique: true, length: Gitlab::Database.mysql? ? 20 : nil
- t.index :fingerprint, unique: true, length: Gitlab::Database.mysql? ? 20 : nil
+ t.index :keyid, unique: true, length: mysql_compatible_index_length
+ t.index :fingerprint, unique: true, length: mysql_compatible_index_length
end
add_reference :gpg_signatures, :gpg_key_subkey, index: true, foreign_key: { on_delete: :nullify }
diff --git a/db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb b/db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb
index d1a039ed551..130b24fe6f0 100644
--- a/db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb
+++ b/db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb
@@ -8,25 +8,25 @@ class AddIndexOnNamespacesLowerName < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- if Gitlab::Database.version.to_f >= 9.5
- # Allow us to hot-patch the index manually ahead of the migration
- execute "CREATE INDEX CONCURRENTLY IF NOT EXISTS #{INDEX_NAME} ON namespaces (lower(name));"
- else
- execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON namespaces (lower(name));"
+ disable_statement_timeout do
+ if Gitlab::Database.version.to_f >= 9.5
+ # Allow us to hot-patch the index manually ahead of the migration
+ execute "CREATE INDEX CONCURRENTLY IF NOT EXISTS #{INDEX_NAME} ON namespaces (lower(name));"
+ else
+ execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON namespaces (lower(name));"
+ end
end
end
def down
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- if Gitlab::Database.version.to_f >= 9.2
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};"
- else
- execute "DROP INDEX IF EXISTS #{INDEX_NAME};"
+ disable_statement_timeout do
+ if Gitlab::Database.version.to_f >= 9.2
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};"
+ else
+ execute "DROP INDEX IF EXISTS #{INDEX_NAME};"
+ end
end
end
end
diff --git a/db/migrate/20180101160629_create_prometheus_metrics.rb b/db/migrate/20180101160629_create_prometheus_metrics.rb
new file mode 100644
index 00000000000..c3be0939b17
--- /dev/null
+++ b/db/migrate/20180101160629_create_prometheus_metrics.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class CreatePrometheusMetrics < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ create_table :prometheus_metrics do |t|
+ t.references :project, index: true, foreign_key: { on_delete: :cascade }, null: false
+ t.string :title, null: false
+ t.string :query, null: false
+ t.string :y_label
+ t.string :unit
+ t.string :legend
+ t.integer :group, null: false, index: true
+ t.timestamps_with_timezone null: false
+ end
+ end
+end
diff --git a/db/migrate/20180101160630_change_project_id_for_prometheus_metrics.rb b/db/migrate/20180101160630_change_project_id_for_prometheus_metrics.rb
new file mode 100644
index 00000000000..66820f13f54
--- /dev/null
+++ b/db/migrate/20180101160630_change_project_id_for_prometheus_metrics.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class ChangeProjectIdForPrometheusMetrics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ change_column_null :prometheus_metrics, :project_id, true
+ end
+end
diff --git a/db/migrate/20180113220114_rework_redirect_routes_indexes.rb b/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
index ab9971be074..53f82a31203 100644
--- a/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
+++ b/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
@@ -18,51 +18,51 @@ class ReworkRedirectRoutesIndexes < ActiveRecord::Migration
OLD_INDEX_NAME_PATH_LOWER = "index_on_redirect_routes_lower_path"
def up
- disable_statement_timeout
-
- # this is a plain btree on a single boolean column. It'll never be
- # selective enough to be valuable. This class is called by
- # setup_postgresql.rake so it needs to be able to handle this
- # index not existing.
- if index_exists?(:redirect_routes, :permanent)
- remove_concurrent_index(:redirect_routes, :permanent)
- end
+ disable_statement_timeout do
+ # this is a plain btree on a single boolean column. It'll never be
+ # selective enough to be valuable. This class is called by
+ # setup_postgresql.rake so it needs to be able to handle this
+ # index not existing.
+ if index_exists?(:redirect_routes, :permanent)
+ remove_concurrent_index(:redirect_routes, :permanent)
+ end
- # If we're on MySQL then the existing index on path is ok. But on
- # Postgres we need to clean things up:
- return unless Gitlab::Database.postgresql?
+ # If we're on MySQL then the existing index on path is ok. But on
+ # Postgres we need to clean things up:
+ break unless Gitlab::Database.postgresql?
- if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : ""
+ if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : ""
- # Unique index on lower(path) across both types of redirect_routes:
- execute("CREATE UNIQUE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_UNIQUE} ON redirect_routes (lower(path) varchar_pattern_ops);")
+ # Unique index on lower(path) across both types of redirect_routes:
+ execute("CREATE UNIQUE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_UNIQUE} ON redirect_routes (lower(path) varchar_pattern_ops);")
- # Make two indexes on path -- one for permanent and one for temporary routes:
- execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
- execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
+ # Make two indexes on path -- one for permanent and one for temporary routes:
+ execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
+ execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
- # Remove the old indexes:
+ # Remove the old indexes:
- # This one needed to be on lower(path) but wasn't so it's replaced with the two above
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_TPOPS};"
+ # This one needed to be on lower(path) but wasn't so it's replaced with the two above
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_TPOPS};"
- # This one isn't needed because we only ever do = and LIKE on this
- # column so the varchar_pattern_ops index is sufficient
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_LOWER};"
+ # This one isn't needed because we only ever do = and LIKE on this
+ # column so the varchar_pattern_ops index is sufficient
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_LOWER};"
+ end
end
def down
- disable_statement_timeout
+ disable_statement_timeout do
+ add_concurrent_index(:redirect_routes, :permanent)
- add_concurrent_index(:redirect_routes, :permanent)
+ break unless Gitlab::Database.postgresql?
- return unless Gitlab::Database.postgresql?
+ execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);")
+ execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));")
- execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);")
- execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));")
-
- execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_UNIQUE};")
- execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};")
- execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};")
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_UNIQUE};")
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};")
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};")
+ end
end
end
diff --git a/db/migrate/20180228172924_add_include_private_contributions_to_users.rb b/db/migrate/20180228172924_add_include_private_contributions_to_users.rb
new file mode 100644
index 00000000000..ea3ebdd83d1
--- /dev/null
+++ b/db/migrate/20180228172924_add_include_private_contributions_to_users.rb
@@ -0,0 +1,7 @@
+class AddIncludePrivateContributionsToUsers < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :users, :include_private_contributions, :boolean
+ end
+end
diff --git a/db/migrate/20180308125206_add_user_internal_regex_to_application_setting.rb b/db/migrate/20180308125206_add_user_internal_regex_to_application_setting.rb
new file mode 100644
index 00000000000..fe50e909563
--- /dev/null
+++ b/db/migrate/20180308125206_add_user_internal_regex_to_application_setting.rb
@@ -0,0 +1,13 @@
+class AddUserInternalRegexToApplicationSetting < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ add_column :application_settings, :user_default_internal_regex, :string, null: true
+ end
+
+ def down
+ remove_column :application_settings, :user_default_internal_regex
+ end
+end
diff --git a/db/migrate/20180403035759_create_project_ci_cd_settings.rb b/db/migrate/20180403035759_create_project_ci_cd_settings.rb
index 06856af6204..173e662cffc 100644
--- a/db/migrate/20180403035759_create_project_ci_cd_settings.rb
+++ b/db/migrate/20180403035759_create_project_ci_cd_settings.rb
@@ -13,16 +13,16 @@ class CreateProjectCiCdSettings < ActiveRecord::Migration
end
end
- disable_statement_timeout
+ disable_statement_timeout do
+ # This particular INSERT will take between 10 and 20 seconds.
+ execute 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects'
- # This particular INSERT will take between 10 and 20 seconds.
- execute 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects'
+ # We add the index and foreign key separately so the above INSERT statement
+ # takes as little time as possible.
+ add_concurrent_index(:project_ci_cd_settings, :project_id, unique: true)
- # We add the index and foreign key separately so the above INSERT statement
- # takes as little time as possible.
- add_concurrent_index(:project_ci_cd_settings, :project_id, unique: true)
-
- add_foreign_key_with_retry
+ add_foreign_key_with_retry
+ end
end
def down
diff --git a/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb b/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb
index 8fc558be733..b7b346cb10e 100644
--- a/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb
+++ b/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb
@@ -45,7 +45,7 @@ class CreateMissingNamespaceForInternalUsers < ActiveRecord::Migration
connection.exec_query(query).present?
end
- insert_query = "INSERT INTO namespaces(owner_id, path, name) VALUES(#{user_id}, '#{path}', '#{path}')"
+ insert_query = "INSERT INTO namespaces(owner_id, path, name, created_at, updated_at) VALUES(#{user_id}, '#{path}', '#{path}', NOW(), NOW())"
namespace_id = connection.insert_sql(insert_query)
create_route(namespace_id)
@@ -57,7 +57,7 @@ class CreateMissingNamespaceForInternalUsers < ActiveRecord::Migration
row = connection.exec_query("SELECT id, path FROM namespaces WHERE id=#{namespace_id}").first
id, path = row.values_at('id', 'path')
- execute("INSERT INTO routes(source_id, source_type, path, name) VALUES(#{id}, 'Namespace', '#{path}', '#{path}')")
+ execute("INSERT INTO routes(source_id, source_type, path, name, created_at, updated_at) VALUES(#{id}, 'Namespace', '#{path}', '#{path}', NOW(), NOW())")
end
def set_notification_email(user_id)
diff --git a/db/migrate/20180420010616_cleanup_build_stage_migration.rb b/db/migrate/20180420010616_cleanup_build_stage_migration.rb
index 24777294101..5e9fe756efd 100644
--- a/db/migrate/20180420010616_cleanup_build_stage_migration.rb
+++ b/db/migrate/20180420010616_cleanup_build_stage_migration.rb
@@ -14,48 +14,50 @@ class CleanupBuildStageMigration < ActiveRecord::Migration
end
def up
- disable_statement_timeout
-
- ##
- # We steal from the background migrations queue to catch up with the
- # scheduled migrations set.
- #
- Gitlab::BackgroundMigration.steal('MigrateBuildStage')
-
- ##
- # We add temporary index, to make iteration over batches more performant.
- # Conditional here is to avoid the need of doing that in a separate
- # migration file to make this operation idempotent.
- #
- unless index_exists_by_name?(:ci_builds, TMP_INDEX)
- add_concurrent_index(:ci_builds, :id, where: 'stage_id IS NULL', name: TMP_INDEX)
- end
-
- ##
- # We check if there are remaining rows that should be migrated (for example
- # if Sidekiq / Redis fails / is restarted, what could result in not all
- # background migrations being executed correctly.
- #
- # We migrate remaining rows synchronously in a blocking way, to make sure
- # that when this migration is done we are confident that all rows are
- # already migrated.
- #
- Build.where('stage_id IS NULL').each_batch(of: 50) do |batch|
- range = batch.pluck('MIN(id)', 'MAX(id)').first
-
- Gitlab::BackgroundMigration::MigrateBuildStage.new.perform(*range)
+ disable_statement_timeout do
+ ##
+ # We steal from the background migrations queue to catch up with the
+ # scheduled migrations set.
+ #
+ Gitlab::BackgroundMigration.steal('MigrateBuildStage')
+
+ ##
+ # We add temporary index, to make iteration over batches more performant.
+ # Conditional here is to avoid the need of doing that in a separate
+ # migration file to make this operation idempotent.
+ #
+ unless index_exists_by_name?(:ci_builds, TMP_INDEX)
+ add_concurrent_index(:ci_builds, :id, where: 'stage_id IS NULL', name: TMP_INDEX)
+ end
+
+ ##
+ # We check if there are remaining rows that should be migrated (for example
+ # if Sidekiq / Redis fails / is restarted, what could result in not all
+ # background migrations being executed correctly.
+ #
+ # We migrate remaining rows synchronously in a blocking way, to make sure
+ # that when this migration is done we are confident that all rows are
+ # already migrated.
+ #
+ Build.where('stage_id IS NULL').each_batch(of: 50) do |batch|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ Gitlab::BackgroundMigration::MigrateBuildStage.new.perform(*range)
+ end
+
+ ##
+ # We remove temporary index, because it is not required during standard
+ # operations and runtime.
+ #
+ remove_concurrent_index_by_name(:ci_builds, TMP_INDEX)
end
-
- ##
- # We remove temporary index, because it is not required during standard
- # operations and runtime.
- #
- remove_concurrent_index_by_name(:ci_builds, TMP_INDEX)
end
def down
if index_exists_by_name?(:ci_builds, TMP_INDEX)
- remove_concurrent_index_by_name(:ci_builds, TMP_INDEX)
+ disable_statement_timeout do
+ remove_concurrent_index_by_name(:ci_builds, TMP_INDEX)
+ end
end
end
end
diff --git a/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb b/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb
new file mode 100644
index 00000000000..1d2f8cf9c76
--- /dev/null
+++ b/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb
@@ -0,0 +1,16 @@
+class AddPagesAccessLevelToProjectFeature < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column_with_default(:project_features, :pages_access_level, :integer, default: ProjectFeature::PUBLIC, allow_null: false)
+
+ change_column_default(:project_features, :pages_access_level, ProjectFeature::ENABLED)
+ end
+
+ def down
+ remove_column :project_features, :pages_access_level
+ end
+end
diff --git a/db/migrate/20180504195842_project_name_lower_index.rb b/db/migrate/20180504195842_project_name_lower_index.rb
index d6f25d3d4ab..74f3673bb03 100644
--- a/db/migrate/20180504195842_project_name_lower_index.rb
+++ b/db/migrate/20180504195842_project_name_lower_index.rb
@@ -13,20 +13,20 @@ class ProjectNameLowerIndex < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON projects (LOWER(name))"
+ disable_statement_timeout do
+ execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON projects (LOWER(name))"
+ end
end
def down
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- if supports_drop_index_concurrently?
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME}"
- else
- execute "DROP INDEX IF EXISTS #{INDEX_NAME}"
+ disable_statement_timeout do
+ if supports_drop_index_concurrently?
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME}"
+ else
+ execute "DROP INDEX IF EXISTS #{INDEX_NAME}"
+ end
end
end
end
diff --git a/db/migrate/20180607071808_add_push_events_branch_filter_to_web_hooks.rb b/db/migrate/20180607071808_add_push_events_branch_filter_to_web_hooks.rb
new file mode 100644
index 00000000000..6a69460e611
--- /dev/null
+++ b/db/migrate/20180607071808_add_push_events_branch_filter_to_web_hooks.rb
@@ -0,0 +1,12 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddPushEventsBranchFilterToWebHooks < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :web_hooks, :push_events_branch_filter, :text
+ end
+end
diff --git a/db/migrate/20180702124358_remove_orphaned_routes.rb b/db/migrate/20180702124358_remove_orphaned_routes.rb
index 6f6e289ba87..4068e479b6c 100644
--- a/db/migrate/20180702124358_remove_orphaned_routes.rb
+++ b/db/migrate/20180702124358_remove_orphaned_routes.rb
@@ -28,16 +28,16 @@ class RemoveOrphanedRoutes < ActiveRecord::Migration
# which is pretty close to our 15 second statement timeout. To ensure a
# smooth deployment procedure we disable the statement timeouts for this
# migration, just in case.
- disable_statement_timeout
-
- # On GitLab.com there are around 4000 orphaned project routes, and around
- # 150 orphaned namespace routes.
- [
- Route.orphaned_project_routes,
- Route.orphaned_namespace_routes
- ].each do |relation|
- relation.each_batch(of: 1_000) do |batch|
- batch.delete_all
+ disable_statement_timeout do
+ # On GitLab.com there are around 4000 orphaned project routes, and around
+ # 150 orphaned namespace routes.
+ [
+ Route.orphaned_project_routes,
+ Route.orphaned_namespace_routes
+ ].each do |relation|
+ relation.each_batch(of: 1_000) do |batch|
+ batch.delete_all
+ end
end
end
end
diff --git a/db/migrate/20180711103851_drop_duplicate_protected_tags.rb b/db/migrate/20180711103851_drop_duplicate_protected_tags.rb
new file mode 100644
index 00000000000..8fa2137551e
--- /dev/null
+++ b/db/migrate/20180711103851_drop_duplicate_protected_tags.rb
@@ -0,0 +1,45 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class DropDuplicateProtectedTags < ActiveRecord::Migration
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ BATCH_SIZE = 1000
+
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+
+ include ::EachBatch
+ end
+
+ class ProtectedTag < ActiveRecord::Base
+ self.table_name = 'protected_tags'
+ end
+
+ def up
+ Project.each_batch(of: BATCH_SIZE) do |projects|
+ ids = ProtectedTag
+ .where(project_id: projects)
+ .group(:name, :project_id)
+ .select('max(id)')
+
+ tags = ProtectedTag
+ .where(project_id: projects)
+ .where.not(id: ids)
+
+ if Gitlab::Database.postgresql?
+ tags.delete_all
+ else
+ # Workaround needed for MySQL
+ sql = "SELECT id FROM (#{tags.to_sql}) protected_tags"
+
+ ProtectedTag.where("id IN (#{sql})").delete_all # rubocop:disable GitlabSecurity/SqlInjection
+ end
+ end
+ end
+
+ def down
+ end
+end
diff --git a/db/migrate/20180711103922_add_protected_tags_index.rb b/db/migrate/20180711103922_add_protected_tags_index.rb
new file mode 100644
index 00000000000..7ed2258ebaf
--- /dev/null
+++ b/db/migrate/20180711103922_add_protected_tags_index.rb
@@ -0,0 +1,18 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddProtectedTagsIndex < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :protected_tags, [:project_id, :name], unique: true
+ end
+
+ def down
+ remove_concurrent_index :protected_tags, [:project_id, :name]
+ end
+end
diff --git a/db/migrate/20180720023512_add_receive_max_input_size_to_application_settings.rb b/db/migrate/20180720023512_add_receive_max_input_size_to_application_settings.rb
new file mode 100644
index 00000000000..4ed851a0780
--- /dev/null
+++ b/db/migrate/20180720023512_add_receive_max_input_size_to_application_settings.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddReceiveMaxInputSizeToApplicationSettings < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :application_settings, :receive_max_input_size, :integer
+ end
+end
diff --git a/db/migrate/20180808162000_add_user_show_add_ssh_key_message_to_application_settings.rb b/db/migrate/20180808162000_add_user_show_add_ssh_key_message_to_application_settings.rb
new file mode 100644
index 00000000000..e3019af2cc9
--- /dev/null
+++ b/db/migrate/20180808162000_add_user_show_add_ssh_key_message_to_application_settings.rb
@@ -0,0 +1,19 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddUserShowAddSshKeyMessageToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :application_settings, :user_show_add_ssh_key_message, :boolean, default: true, allow_null: false
+ end
+
+ def down
+ remove_column :application_settings, :user_show_add_ssh_key_message
+ end
+end
diff --git a/db/migrate/20180813101999_change_default_of_auto_devops_instance_wide.rb b/db/migrate/20180813101999_change_default_of_auto_devops_instance_wide.rb
new file mode 100644
index 00000000000..05d1124f5c4
--- /dev/null
+++ b/db/migrate/20180813101999_change_default_of_auto_devops_instance_wide.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class ChangeDefaultOfAutoDevopsInstanceWide < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ change_column_default :application_settings, :auto_devops_enabled, true
+ end
+
+ def down
+ change_column_default :application_settings, :auto_devops_enabled, false
+ end
+end
diff --git a/db/migrate/20180813102000_enable_auto_devops_instance_wide_for_everyone.rb b/db/migrate/20180813102000_enable_auto_devops_instance_wide_for_everyone.rb
new file mode 100644
index 00000000000..21fb62806b3
--- /dev/null
+++ b/db/migrate/20180813102000_enable_auto_devops_instance_wide_for_everyone.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class EnableAutoDevopsInstanceWideForEveryone < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ execute "UPDATE application_settings SET auto_devops_enabled = true"
+ end
+
+ def down
+ # No way to know here what their previous setting was...
+ end
+end
diff --git a/db/migrate/20180814153625_add_commit_email_to_users.rb b/db/migrate/20180814153625_add_commit_email_to_users.rb
new file mode 100644
index 00000000000..5c87d73688e
--- /dev/null
+++ b/db/migrate/20180814153625_add_commit_email_to_users.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddCommitEmailToUsers < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ # When a migration requires downtime you **must** uncomment the following
+ # constant and define a short and easy to understand explanation as to why the
+ # migration requires downtime.
+ # DOWNTIME_REASON = ''
+
+ # When using the methods "add_concurrent_index", "remove_concurrent_index" or
+ # "add_column_with_default" you must disable the use of transactions
+ # as these methods can not run in an existing transaction.
+ # When using "add_concurrent_index" or "remove_concurrent_index" methods make sure
+ # that either of them is the _only_ method called in the migration,
+ # any other changes should go in a separate migration.
+ # This ensures that upon failure _only_ the index creation or removing fails
+ # and can be retried or reverted easily.
+ #
+ # To disable transactions uncomment the following line and remove these
+ # comments:
+ # disable_ddl_transaction!
+
+ def change
+ add_column :users, :commit_email, :string
+ end
+end
diff --git a/db/migrate/20180815040323_add_authorization_type_to_cluster_platforms_kubernetes.rb b/db/migrate/20180815040323_add_authorization_type_to_cluster_platforms_kubernetes.rb
new file mode 100644
index 00000000000..6397d6dd99f
--- /dev/null
+++ b/db/migrate/20180815040323_add_authorization_type_to_cluster_platforms_kubernetes.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddAuthorizationTypeToClusterPlatformsKubernetes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :cluster_platforms_kubernetes, :authorization_type, :integer, limit: 2
+ end
+end
diff --git a/db/migrate/20180815160409_add_file_location_to_ci_job_artifacts.rb b/db/migrate/20180815160409_add_file_location_to_ci_job_artifacts.rb
new file mode 100644
index 00000000000..620342005fe
--- /dev/null
+++ b/db/migrate/20180815160409_add_file_location_to_ci_job_artifacts.rb
@@ -0,0 +1,9 @@
+class AddFileLocationToCiJobArtifacts < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :ci_job_artifacts, :file_location, :integer, limit: 2
+ end
+end
diff --git a/db/migrate/20180815170510_add_partial_index_to_ci_builds_artifacts_file.rb b/db/migrate/20180815170510_add_partial_index_to_ci_builds_artifacts_file.rb
new file mode 100644
index 00000000000..5e041ea6559
--- /dev/null
+++ b/db/migrate/20180815170510_add_partial_index_to_ci_builds_artifacts_file.rb
@@ -0,0 +1,16 @@
+class AddPartialIndexToCiBuildsArtifactsFile < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'partial_index_ci_builds_on_id_with_legacy_artifacts'.freeze
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:ci_builds, :id, where: "artifacts_file <> ''", name: INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name(:ci_builds, INDEX_NAME)
+ end
+end
diff --git a/db/migrate/20180815175440_add_index_on_list_type.rb b/db/migrate/20180815175440_add_index_on_list_type.rb
new file mode 100644
index 00000000000..aad805e436e
--- /dev/null
+++ b/db/migrate/20180815175440_add_index_on_list_type.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+class AddIndexOnListType < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :lists, :list_type
+ end
+
+ def down
+ remove_concurrent_index :lists, :list_type
+ end
+end
diff --git a/db/migrate/20180831164904_fix_prometheus_metric_query_limits.rb b/db/migrate/20180831164904_fix_prometheus_metric_query_limits.rb
new file mode 100644
index 00000000000..28c92e7c7ac
--- /dev/null
+++ b/db/migrate/20180831164904_fix_prometheus_metric_query_limits.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+require Rails.root.join('db/migrate/prometheus_metrics_limits_to_mysql')
+
+class FixPrometheusMetricQueryLimits < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ PrometheusMetricsLimitsToMysql.new.up
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/migrate/20180831164905_add_common_to_prometheus_metrics.rb b/db/migrate/20180831164905_add_common_to_prometheus_metrics.rb
new file mode 100644
index 00000000000..e21c156fff6
--- /dev/null
+++ b/db/migrate/20180831164905_add_common_to_prometheus_metrics.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddCommonToPrometheusMetrics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:prometheus_metrics, :common, :boolean, default: false)
+ end
+
+ def down
+ remove_column(:prometheus_metrics, :common)
+ end
+end
diff --git a/db/migrate/20180831164907_add_index_on_common_for_prometheus_metrics.rb b/db/migrate/20180831164907_add_index_on_common_for_prometheus_metrics.rb
new file mode 100644
index 00000000000..fdbaaf67b87
--- /dev/null
+++ b/db/migrate/20180831164907_add_index_on_common_for_prometheus_metrics.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexOnCommonForPrometheusMetrics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :prometheus_metrics, :common
+ end
+
+ def down
+ remove_concurrent_index :prometheus_metrics, :common
+ end
+end
diff --git a/db/migrate/20180831164908_add_identifier_to_prometheus_metric.rb b/db/migrate/20180831164908_add_identifier_to_prometheus_metric.rb
new file mode 100644
index 00000000000..67de990757e
--- /dev/null
+++ b/db/migrate/20180831164908_add_identifier_to_prometheus_metric.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddIdentifierToPrometheusMetric < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :prometheus_metrics, :identifier, :string
+ end
+end
diff --git a/db/migrate/20180831164909_add_index_for_identifier_to_prometheus_metric.rb b/db/migrate/20180831164909_add_index_for_identifier_to_prometheus_metric.rb
new file mode 100644
index 00000000000..b30c24ccafe
--- /dev/null
+++ b/db/migrate/20180831164909_add_index_for_identifier_to_prometheus_metric.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexForIdentifierToPrometheusMetric < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :prometheus_metrics, :identifier, unique: true
+ end
+
+ def down
+ remove_concurrent_index :prometheus_metrics, :identifier, unique: true
+ end
+end
diff --git a/db/migrate/20180831164910_import_common_metrics.rb b/db/migrate/20180831164910_import_common_metrics.rb
new file mode 100644
index 00000000000..72658c09b8e
--- /dev/null
+++ b/db/migrate/20180831164910_import_common_metrics.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class ImportCommonMetrics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ require Rails.root.join('db/importers/common_metrics_importer.rb')
+
+ DOWNTIME = false
+
+ def up
+ Importers::CommonMetricsImporter.new.execute
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/migrate/20180901171833_add_project_config_source_status_index_to_pipeline.rb b/db/migrate/20180901171833_add_project_config_source_status_index_to_pipeline.rb
new file mode 100644
index 00000000000..99dfcc94b12
--- /dev/null
+++ b/db/migrate/20180901171833_add_project_config_source_status_index_to_pipeline.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddProjectConfigSourceStatusIndexToPipeline < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :ci_pipelines, [:project_id, :status, :config_source]
+ end
+
+ def down
+ remove_concurrent_index :ci_pipelines, [:project_id, :status, :config_source]
+ end
+end
diff --git a/db/migrate/20180901200537_add_resource_label_event_reference_fields.rb b/db/migrate/20180901200537_add_resource_label_event_reference_fields.rb
new file mode 100644
index 00000000000..264970ceed8
--- /dev/null
+++ b/db/migrate/20180901200537_add_resource_label_event_reference_fields.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddResourceLabelEventReferenceFields < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :resource_label_events, :cached_markdown_version, :integer
+ add_column :resource_label_events, :reference, :text
+ add_column :resource_label_events, :reference_html, :text
+ end
+end
diff --git a/db/migrate/20180906101639_add_user_ping_consent_to_application_settings.rb b/db/migrate/20180906101639_add_user_ping_consent_to_application_settings.rb
new file mode 100644
index 00000000000..5d0e67d2648
--- /dev/null
+++ b/db/migrate/20180906101639_add_user_ping_consent_to_application_settings.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddUserPingConsentToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column :application_settings, :usage_stats_set_by_user_id, :integer
+ add_concurrent_foreign_key :application_settings, :users, column: :usage_stats_set_by_user_id, on_delete: :nullify
+ end
+
+ def down
+ remove_foreign_key :application_settings, column: :usage_stats_set_by_user_id
+ remove_column :application_settings, :usage_stats_set_by_user_id
+ end
+end
diff --git a/db/migrate/20180907015926_add_legacy_abac_to_cluster_providers_gcp.rb b/db/migrate/20180907015926_add_legacy_abac_to_cluster_providers_gcp.rb
new file mode 100644
index 00000000000..933047e32de
--- /dev/null
+++ b/db/migrate/20180907015926_add_legacy_abac_to_cluster_providers_gcp.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddLegacyAbacToClusterProvidersGcp < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:cluster_providers_gcp, :legacy_abac, :boolean, default: true)
+ end
+
+ def down
+ remove_column(:cluster_providers_gcp, :legacy_abac)
+ end
+end
diff --git a/db/migrate/20180910115836_add_attr_encrypted_columns_to_web_hook.rb b/db/migrate/20180910115836_add_attr_encrypted_columns_to_web_hook.rb
new file mode 100644
index 00000000000..72f5c8d653b
--- /dev/null
+++ b/db/migrate/20180910115836_add_attr_encrypted_columns_to_web_hook.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddAttrEncryptedColumnsToWebHook < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :web_hooks, :encrypted_token, :string
+ add_column :web_hooks, :encrypted_token_iv, :string
+
+ add_column :web_hooks, :encrypted_url, :string
+ add_column :web_hooks, :encrypted_url_iv, :string
+ end
+end
diff --git a/db/migrate/20180910153412_add_token_digest_to_personal_access_tokens.rb b/db/migrate/20180910153412_add_token_digest_to_personal_access_tokens.rb
new file mode 100644
index 00000000000..203fcfe8eae
--- /dev/null
+++ b/db/migrate/20180910153412_add_token_digest_to_personal_access_tokens.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddTokenDigestToPersonalAccessTokens < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ change_column :personal_access_tokens, :token, :string, null: true
+
+ add_column :personal_access_tokens, :token_digest, :string
+ end
+
+ def down
+ remove_column :personal_access_tokens, :token_digest
+
+ change_column :personal_access_tokens, :token, :string, null: false
+ end
+end
diff --git a/db/migrate/20180910153413_add_index_to_token_digest_on_personal_access_tokens.rb b/db/migrate/20180910153413_add_index_to_token_digest_on_personal_access_tokens.rb
new file mode 100644
index 00000000000..4300cd13a45
--- /dev/null
+++ b/db/migrate/20180910153413_add_index_to_token_digest_on_personal_access_tokens.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToTokenDigestOnPersonalAccessTokens < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :personal_access_tokens, :token_digest, unique: true
+ end
+
+ def down
+ remove_concurrent_index :personal_access_tokens, :token_digest if index_exists?(:personal_access_tokens, :token_digest)
+ end
+end
diff --git a/db/migrate/20180912111628_add_knative_application.rb b/db/migrate/20180912111628_add_knative_application.rb
new file mode 100644
index 00000000000..bfda6a945a7
--- /dev/null
+++ b/db/migrate/20180912111628_add_knative_application.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddKnativeApplication < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table "clusters_applications_knative" do |t|
+ t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade }
+
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.integer "status", null: false
+ t.string "version", null: false
+ t.string "hostname"
+ t.text "status_reason"
+ end
+ end
+end
diff --git a/db/migrate/20180916011959_add_index_pipelines_project_id_source.rb b/db/migrate/20180916011959_add_index_pipelines_project_id_source.rb
new file mode 100644
index 00000000000..b9bebf30cf0
--- /dev/null
+++ b/db/migrate/20180916011959_add_index_pipelines_project_id_source.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddIndexPipelinesProjectIdSource < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :ci_pipelines, [:project_id, :source]
+ end
+
+ def down
+ remove_concurrent_index :ci_pipelines, [:project_id, :source]
+ end
+end
diff --git a/db/migrate/20180924141949_add_diff_max_patch_bytes_to_application_settings.rb b/db/migrate/20180924141949_add_diff_max_patch_bytes_to_application_settings.rb
new file mode 100644
index 00000000000..084dfc65ce5
--- /dev/null
+++ b/db/migrate/20180924141949_add_diff_max_patch_bytes_to_application_settings.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddDiffMaxPatchBytesToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:application_settings,
+ :diff_max_patch_bytes,
+ :integer,
+ default: 100.kilobytes,
+ allow_null: false)
+ end
+
+ def down
+ remove_column(:application_settings, :diff_max_patch_bytes)
+ end
+end
diff --git a/db/migrate/20180924190739_add_scheduled_at_to_ci_builds.rb b/db/migrate/20180924190739_add_scheduled_at_to_ci_builds.rb
new file mode 100644
index 00000000000..c163fbb1fd6
--- /dev/null
+++ b/db/migrate/20180924190739_add_scheduled_at_to_ci_builds.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddScheduledAtToCiBuilds < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :ci_builds, :scheduled_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb b/db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb
new file mode 100644
index 00000000000..81bf0d94e11
--- /dev/null
+++ b/db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddPartialIndexToScheduledAt < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'partial_index_ci_builds_on_scheduled_at_with_scheduled_jobs'.freeze
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:ci_builds, :scheduled_at, where: "scheduled_at IS NOT NULL AND type = 'Ci::Build' AND status = 'scheduled'", name: INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name(:ci_builds, INDEX_NAME)
+ end
+end
diff --git a/db/migrate/20180925200829_create_user_preferences.rb b/db/migrate/20180925200829_create_user_preferences.rb
new file mode 100644
index 00000000000..755cabdabde
--- /dev/null
+++ b/db/migrate/20180925200829_create_user_preferences.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class CreateUserPreferences < ActiveRecord::Migration
+ DOWNTIME = false
+
+ class UserPreference < ActiveRecord::Base
+ self.table_name = 'user_preferences'
+
+ NOTES_FILTERS = { all_notes: 0, comments: 1 }.freeze
+ end
+
+ def change
+ create_table :user_preferences do |t|
+ t.references :user,
+ null: false,
+ index: { unique: true },
+ foreign_key: { on_delete: :cascade }
+
+ t.integer :issue_notes_filter,
+ default: UserPreference::NOTES_FILTERS[:all_notes],
+ null: false, limit: 2
+
+ t.integer :merge_request_notes_filter,
+ default: UserPreference::NOTES_FILTERS[:all_notes],
+ null: false,
+ limit: 2
+
+ t.timestamps_with_timezone null: false
+ end
+ end
+end
diff --git a/db/migrate/20180927073410_add_index_to_project_deploy_tokens_deploy_token_id.rb b/db/migrate/20180927073410_add_index_to_project_deploy_tokens_deploy_token_id.rb
new file mode 100644
index 00000000000..61d32fe16eb
--- /dev/null
+++ b/db/migrate/20180927073410_add_index_to_project_deploy_tokens_deploy_token_id.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexToProjectDeployTokensDeployTokenId < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ # MySQL already has index inserted
+ add_concurrent_index :project_deploy_tokens, :deploy_token_id if Gitlab::Database.postgresql?
+ end
+
+ def down
+ remove_concurrent_index(:project_deploy_tokens, :deploy_token_id) if Gitlab::Database.postgresql?
+ end
+end
diff --git a/db/migrate/20181002172433_remove_restricted_todos_with_cte.rb b/db/migrate/20181002172433_remove_restricted_todos_with_cte.rb
new file mode 100644
index 00000000000..0a8f4a12266
--- /dev/null
+++ b/db/migrate/20181002172433_remove_restricted_todos_with_cte.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+# rescheduling of the revised RemoveRestrictedTodos background migration
+class RemoveRestrictedTodosWithCte < ActiveRecord::Migration
+ DOWNTIME = false
+ disable_ddl_transaction!
+
+ MIGRATION = 'RemoveRestrictedTodos'.freeze
+ BATCH_SIZE = 1000
+ DELAY_INTERVAL = 5.minutes.to_i
+
+ class Project < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'projects'
+ end
+
+ def up
+ Project.where('EXISTS (SELECT 1 FROM todos WHERE todos.project_id = projects.id)')
+ .each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, range)
+ end
+ end
+
+ def down
+ # nothing to do
+ end
+end
diff --git a/db/migrate/20181005110927_add_index_to_lfs_objects_file_store.rb b/db/migrate/20181005110927_add_index_to_lfs_objects_file_store.rb
new file mode 100644
index 00000000000..d09543aa4cc
--- /dev/null
+++ b/db/migrate/20181005110927_add_index_to_lfs_objects_file_store.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToLfsObjectsFileStore < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :lfs_objects, :file_store
+ end
+
+ def down
+ remove_concurrent_index :lfs_objects, :file_store
+ end
+end
diff --git a/db/migrate/20181005125926_add_index_to_uploads_store.rb b/db/migrate/20181005125926_add_index_to_uploads_store.rb
new file mode 100644
index 00000000000..d32ca05e980
--- /dev/null
+++ b/db/migrate/20181005125926_add_index_to_uploads_store.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToUploadsStore < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :uploads, :store
+ end
+
+ def down
+ remove_concurrent_index :uploads, :store
+ end
+end
diff --git a/db/migrate/20181009190428_create_clusters_kubernetes_namespaces.rb b/db/migrate/20181009190428_create_clusters_kubernetes_namespaces.rb
new file mode 100644
index 00000000000..a58c190e1d6
--- /dev/null
+++ b/db/migrate/20181009190428_create_clusters_kubernetes_namespaces.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class CreateClustersKubernetesNamespaces < ActiveRecord::Migration
+ DOWNTIME = false
+ INDEX_NAME = 'kubernetes_namespaces_cluster_and_namespace'
+
+ def change
+ create_table :clusters_kubernetes_namespaces, id: :bigserial do |t|
+ t.references :cluster, null: false, index: true, foreign_key: { on_delete: :cascade }
+ t.references :project, index: true, foreign_key: { on_delete: :nullify }
+ t.references :cluster_project, index: true, foreign_key: { on_delete: :nullify }
+
+ t.timestamps_with_timezone null: false
+
+ t.string :encrypted_service_account_token_iv
+ t.string :namespace, null: false
+ t.string :service_account_name
+
+ t.text :encrypted_service_account_token
+
+ t.index [:cluster_id, :namespace], name: INDEX_NAME, unique: true
+ end
+ end
+end
diff --git a/db/migrate/20181010235606_create_board_project_recent_visits.rb b/db/migrate/20181010235606_create_board_project_recent_visits.rb
new file mode 100644
index 00000000000..426f41e202a
--- /dev/null
+++ b/db/migrate/20181010235606_create_board_project_recent_visits.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class CreateBoardProjectRecentVisits < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :board_project_recent_visits, id: :bigserial do |t|
+ t.timestamps_with_timezone null: false
+
+ t.references :user, index: true, foreign_key: { on_delete: :cascade }
+ t.references :project, index: true, foreign_key: { on_delete: :cascade }
+ t.references :board, index: true, foreign_key: { on_delete: :cascade }
+ end
+
+ add_index :board_project_recent_visits, [:user_id, :project_id, :board_id], unique: true, name: 'index_board_project_recent_visits_on_user_project_and_board'
+ end
+end
diff --git a/db/migrate/20181014203236_create_cluster_groups.rb b/db/migrate/20181014203236_create_cluster_groups.rb
new file mode 100644
index 00000000000..69382d5c851
--- /dev/null
+++ b/db/migrate/20181014203236_create_cluster_groups.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class CreateClusterGroups < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :cluster_groups do |t|
+ t.references :cluster, null: false, foreign_key: { on_delete: :cascade }
+ t.references :group, null: false, index: true
+
+ t.index [:cluster_id, :group_id], unique: true
+ t.foreign_key :namespaces, column: :group_id, on_delete: :cascade
+ end
+ end
+end
diff --git a/db/migrate/20181015155839_add_finished_at_to_deployments.rb b/db/migrate/20181015155839_add_finished_at_to_deployments.rb
new file mode 100644
index 00000000000..1a061bb0f5f
--- /dev/null
+++ b/db/migrate/20181015155839_add_finished_at_to_deployments.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddFinishedAtToDeployments < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ add_column :deployments, :finished_at, :datetime_with_timezone
+ end
+
+ def down
+ remove_column :deployments, :finished_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20181016141739_add_status_to_deployments.rb b/db/migrate/20181016141739_add_status_to_deployments.rb
new file mode 100644
index 00000000000..321172696b4
--- /dev/null
+++ b/db/migrate/20181016141739_add_status_to_deployments.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class AddStatusToDeployments < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DEPLOYMENT_STATUS_SUCCESS = 2 # Equivalent to Deployment.state_machine.states['success'].value
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ ##
+ # NOTE:
+ # Ideally, `status` column should not have default value because it should be leveraged by state machine (i.e. application level).
+ # However, we have to use the default value for avoiding `NOT NULL` violation during the transition period.
+ # The default value should be removed in the future release.
+ def up
+ add_column_with_default(:deployments,
+ :status,
+ :integer,
+ limit: 2,
+ default: DEPLOYMENT_STATUS_SUCCESS,
+ allow_null: false)
+ end
+
+ def down
+ remove_column(:deployments, :status)
+ end
+end
diff --git a/db/migrate/20181016152238_create_board_group_recent_visits.rb b/db/migrate/20181016152238_create_board_group_recent_visits.rb
new file mode 100644
index 00000000000..1e55dc8658e
--- /dev/null
+++ b/db/migrate/20181016152238_create_board_group_recent_visits.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class CreateBoardGroupRecentVisits < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :board_group_recent_visits, id: :bigserial do |t|
+ t.timestamps_with_timezone null: false
+
+ t.references :user, index: true, foreign_key: { on_delete: :cascade }
+ t.references :board, index: true, foreign_key: { on_delete: :cascade }
+ t.references :group, references: :namespace, column: :group_id, index: true
+ t.foreign_key :namespaces, column: :group_id, on_delete: :cascade
+ end
+
+ add_index :board_group_recent_visits, [:user_id, :group_id, :board_id], unique: true, name: 'index_board_group_recent_visits_on_user_group_and_board'
+ end
+end
diff --git a/db/migrate/20181017001059_add_cluster_type_to_clusters.rb b/db/migrate/20181017001059_add_cluster_type_to_clusters.rb
new file mode 100644
index 00000000000..191e7eb4fb3
--- /dev/null
+++ b/db/migrate/20181017001059_add_cluster_type_to_clusters.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddClusterTypeToClusters < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ PROJECT_CLUSTER_TYPE = 3
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:clusters, :cluster_type, :smallint, default: PROJECT_CLUSTER_TYPE)
+ end
+
+ def down
+ remove_column(:clusters, :cluster_type)
+ end
+end
diff --git a/db/migrate/20181019032400_add_shards_table.rb b/db/migrate/20181019032400_add_shards_table.rb
new file mode 100644
index 00000000000..5e0a6960548
--- /dev/null
+++ b/db/migrate/20181019032400_add_shards_table.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddShardsTable < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ create_table :shards do |t|
+ t.string :name, null: false, index: { unique: true }
+ end
+ end
+end
diff --git a/db/migrate/20181019032408_add_repositories_table.rb b/db/migrate/20181019032408_add_repositories_table.rb
new file mode 100644
index 00000000000..077f264d3ce
--- /dev/null
+++ b/db/migrate/20181019032408_add_repositories_table.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddRepositoriesTable < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ create_table :repositories, id: :bigserial do |t|
+ t.references :shard, null: false, index: true, foreign_key: { on_delete: :restrict }
+ t.string :disk_path, null: false, index: { unique: true }
+ end
+
+ add_column :projects, :pool_repository_id, :bigint
+ add_index :projects, :pool_repository_id, where: 'pool_repository_id IS NOT NULL'
+ end
+end
diff --git a/db/migrate/20181019105553_add_projects_pool_repository_id_foreign_key.rb b/db/migrate/20181019105553_add_projects_pool_repository_id_foreign_key.rb
new file mode 100644
index 00000000000..059988de38a
--- /dev/null
+++ b/db/migrate/20181019105553_add_projects_pool_repository_id_foreign_key.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddProjectsPoolRepositoryIdForeignKey < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key(
+ :projects,
+ :repositories,
+ column: :pool_repository_id,
+ on_delete: :nullify
+ )
+ end
+
+ def down
+ remove_foreign_key(:projects, column: :pool_repository_id)
+ end
+end
diff --git a/db/migrate/20181022135539_add_index_on_status_to_deployments.rb b/db/migrate/20181022135539_add_index_on_status_to_deployments.rb
new file mode 100644
index 00000000000..2eed20aa855
--- /dev/null
+++ b/db/migrate/20181022135539_add_index_on_status_to_deployments.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddIndexOnStatusToDeployments < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :deployments, [:project_id, :status]
+ add_concurrent_index :deployments, [:environment_id, :status]
+ end
+
+ def down
+ remove_concurrent_index :deployments, [:project_id, :status]
+ remove_concurrent_index :deployments, [:environment_id, :status]
+ end
+end
diff --git a/db/migrate/20181023104858_add_archive_builds_duration_to_application_settings.rb b/db/migrate/20181023104858_add_archive_builds_duration_to_application_settings.rb
new file mode 100644
index 00000000000..744748b3fad
--- /dev/null
+++ b/db/migrate/20181023104858_add_archive_builds_duration_to_application_settings.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddArchiveBuildsDurationToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column(:application_settings, :archive_builds_in_seconds, :integer, allow_null: true)
+ end
+end
diff --git a/db/migrate/20181023144439_add_partial_index_for_legacy_successful_deployments.rb b/db/migrate/20181023144439_add_partial_index_for_legacy_successful_deployments.rb
new file mode 100644
index 00000000000..5896102af1c
--- /dev/null
+++ b/db/migrate/20181023144439_add_partial_index_for_legacy_successful_deployments.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddPartialIndexForLegacySuccessfulDeployments < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'partial_index_deployments_for_legacy_successful_deployments'.freeze
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:deployments, :id, where: "finished_at IS NULL AND status = 2", name: INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name(:deployments, INDEX_NAME)
+ end
+end
diff --git a/db/migrate/20181025115728_add_private_commit_email_hostname_to_application_settings.rb b/db/migrate/20181025115728_add_private_commit_email_hostname_to_application_settings.rb
new file mode 100644
index 00000000000..89ddaf2ae2b
--- /dev/null
+++ b/db/migrate/20181025115728_add_private_commit_email_hostname_to_application_settings.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddPrivateCommitEmailHostnameToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column(:application_settings, :commit_email_hostname, :string, null: true)
+ end
+end
diff --git a/db/migrate/20181026143227_migrate_snippets_access_level_default_value.rb b/db/migrate/20181026143227_migrate_snippets_access_level_default_value.rb
new file mode 100644
index 00000000000..ede0ee27b8a
--- /dev/null
+++ b/db/migrate/20181026143227_migrate_snippets_access_level_default_value.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class MigrateSnippetsAccessLevelDefaultValue < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ ENABLED = 20
+
+ disable_ddl_transaction!
+
+ class ProjectFeature < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'project_features'
+ end
+
+ def up
+ change_column_default :project_features, :snippets_access_level, ENABLED
+
+ # On GitLab.com this will update about 28 000 rows. Since our updates are
+ # very small and this column is not indexed, these updates should be very
+ # lightweight.
+ ProjectFeature.where(snippets_access_level: nil).each_batch do |batch|
+ batch.update_all(snippets_access_level: ENABLED)
+ end
+
+ # We do not need to perform this in a post-deployment migration as the
+ # ProjectFeature model already enforces a default value for all new rows.
+ change_column_null :project_features, :snippets_access_level, false
+ end
+
+ def down
+ change_column_null :project_features, :snippets_access_level, true
+ change_column_default :project_features, :snippets_access_level, nil
+
+ # We can't migrate from 20 -> NULL, as some projects may have explicitly set
+ # the access level to 20.
+ end
+end
diff --git a/db/migrate/20181030154446_add_missing_indexes_for_foreign_keys.rb b/db/migrate/20181030154446_add_missing_indexes_for_foreign_keys.rb
new file mode 100644
index 00000000000..176d55565d8
--- /dev/null
+++ b/db/migrate/20181030154446_add_missing_indexes_for_foreign_keys.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+class AddMissingIndexesForForeignKeys < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:application_settings, :usage_stats_set_by_user_id)
+ add_concurrent_index(:ci_pipeline_schedules, :owner_id)
+ add_concurrent_index(:ci_trigger_requests, :trigger_id)
+ add_concurrent_index(:ci_triggers, :owner_id)
+ add_concurrent_index(:clusters_applications_helm, :cluster_id, unique: true)
+ add_concurrent_index(:clusters_applications_ingress, :cluster_id, unique: true)
+ add_concurrent_index(:clusters_applications_jupyter, :cluster_id, unique: true)
+ add_concurrent_index(:clusters_applications_jupyter, :oauth_application_id)
+ add_concurrent_index(:clusters_applications_knative, :cluster_id, unique: true)
+ add_concurrent_index(:clusters_applications_prometheus, :cluster_id, unique: true)
+ add_concurrent_index(:fork_network_members, :forked_from_project_id)
+ add_concurrent_index(:internal_ids, :namespace_id)
+ add_concurrent_index(:internal_ids, :project_id)
+ add_concurrent_index(:issues, :closed_by_id)
+ add_concurrent_index(:label_priorities, :label_id)
+ add_concurrent_index(:merge_request_metrics, :merged_by_id)
+ add_concurrent_index(:merge_request_metrics, :latest_closed_by_id)
+ add_concurrent_index(:oauth_openid_requests, :access_grant_id)
+ add_concurrent_index(:project_deploy_tokens, :deploy_token_id)
+ add_concurrent_index(:protected_tag_create_access_levels, :group_id)
+ add_concurrent_index(:subscriptions, :project_id)
+ add_concurrent_index(:user_statuses, :user_id)
+ add_concurrent_index(:users, :accepted_term_id)
+ end
+
+ def down
+ # MySQL requires index for FK,
+ # thus removal of indexes does fail
+ return if Gitlab::Database.mysql?
+
+ remove_concurrent_index(:application_settings, :usage_stats_set_by_user_id)
+ remove_concurrent_index(:ci_pipeline_schedules, :owner_id)
+ remove_concurrent_index(:ci_trigger_requests, :trigger_id)
+ remove_concurrent_index(:ci_triggers, :owner_id)
+ remove_concurrent_index(:clusters_applications_helm, :cluster_id, unique: true)
+ remove_concurrent_index(:clusters_applications_ingress, :cluster_id, unique: true)
+ remove_concurrent_index(:clusters_applications_jupyter, :cluster_id, unique: true)
+ remove_concurrent_index(:clusters_applications_jupyter, :oauth_application_id)
+ remove_concurrent_index(:clusters_applications_knative, :cluster_id, unique: true)
+ remove_concurrent_index(:clusters_applications_prometheus, :cluster_id, unique: true)
+ remove_concurrent_index(:fork_network_members, :forked_from_project_id)
+ remove_concurrent_index(:internal_ids, :namespace_id)
+ remove_concurrent_index(:internal_ids, :project_id)
+ remove_concurrent_index(:issues, :closed_by_id)
+ remove_concurrent_index(:label_priorities, :label_id)
+ remove_concurrent_index(:merge_request_metrics, :merged_by_id)
+ remove_concurrent_index(:merge_request_metrics, :latest_closed_by_id)
+ remove_concurrent_index(:oauth_openid_requests, :access_grant_id)
+ remove_concurrent_index(:project_deploy_tokens, :deploy_token_id)
+ remove_concurrent_index(:protected_tag_create_access_levels, :group_id)
+ remove_concurrent_index(:subscriptions, :project_id)
+ remove_concurrent_index(:user_statuses, :user_id)
+ remove_concurrent_index(:users, :accepted_term_id)
+ end
+end
diff --git a/db/migrate/20181031190558_drop_fk_gcp_clusters_table.rb b/db/migrate/20181031190558_drop_fk_gcp_clusters_table.rb
new file mode 100644
index 00000000000..a7106111f46
--- /dev/null
+++ b/db/migrate/20181031190558_drop_fk_gcp_clusters_table.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+class DropFkGcpClustersTable < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_foreign_key_if_exists :gcp_clusters, column: :project_id
+ remove_foreign_key_if_exists :gcp_clusters, column: :user_id
+ remove_foreign_key_if_exists :gcp_clusters, column: :service_id
+ end
+
+ def down
+ add_foreign_key_if_not_exists :gcp_clusters, :projects, column: :project_id, on_delete: :cascade
+ add_foreign_key_if_not_exists :gcp_clusters, :users, column: :user_id, on_delete: :nullify
+ add_foreign_key_if_not_exists :gcp_clusters, :services, column: :service_id, on_delete: :nullify
+ end
+
+ private
+
+ def add_foreign_key_if_not_exists(source, target, column:, on_delete:)
+ return unless table_exists?(source)
+ return if foreign_key_exists?(source, target, column: column)
+
+ add_concurrent_foreign_key(source, target, column: column, on_delete: on_delete)
+ end
+
+ def remove_foreign_key_if_exists(table, column:)
+ return unless table_exists?(table)
+ return unless foreign_key_exists?(table, column: column)
+
+ remove_foreign_key(table, column: column)
+ end
+end
diff --git a/db/migrate/20181031190559_drop_gcp_clusters_table.rb b/db/migrate/20181031190559_drop_gcp_clusters_table.rb
new file mode 100644
index 00000000000..808d474b4fc
--- /dev/null
+++ b/db/migrate/20181031190559_drop_gcp_clusters_table.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+class DropGcpClustersTable < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ drop_table :gcp_clusters
+ end
+
+ def down
+ create_table :gcp_clusters do |t|
+ # Order columns by best align scheme
+ t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
+ t.references :user, foreign_key: { on_delete: :nullify }
+ t.references :service, foreign_key: { on_delete: :nullify }
+ t.integer :status
+ t.integer :gcp_cluster_size, null: false
+
+ # Timestamps
+ t.datetime_with_timezone :created_at, null: false
+ t.datetime_with_timezone :updated_at, null: false
+
+ # Enable/disable
+ t.boolean :enabled, default: true
+
+ # General
+ t.text :status_reason
+
+ # k8s integration specific
+ t.string :project_namespace
+
+ # Cluster details
+ t.string :endpoint
+ t.text :ca_cert
+ t.text :encrypted_kubernetes_token
+ t.string :encrypted_kubernetes_token_iv
+ t.string :username
+ t.text :encrypted_password
+ t.string :encrypted_password_iv
+
+ # GKE
+ t.string :gcp_project_id, null: false
+ t.string :gcp_cluster_zone, null: false
+ t.string :gcp_cluster_name, null: false
+ t.string :gcp_machine_type
+ t.string :gcp_operation_id
+ t.text :encrypted_gcp_token
+ t.string :encrypted_gcp_token_iv
+ end
+ end
+end
diff --git a/db/migrate/20181101144347_add_index_for_stuck_mr_query.rb b/db/migrate/20181101144347_add_index_for_stuck_mr_query.rb
new file mode 100644
index 00000000000..5d3ace54e5c
--- /dev/null
+++ b/db/migrate/20181101144347_add_index_for_stuck_mr_query.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+class AddIndexForStuckMrQuery < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :merge_requests, [:id, :merge_jid], where: "merge_jid IS NOT NULL and state = 'locked'"
+ end
+
+ def down
+ remove_concurrent_index :merge_requests, [:id, :merge_jid], where: "merge_jid IS NOT NULL and state = 'locked'"
+ end
+end
diff --git a/db/migrate/20181106135939_add_index_to_deployments.rb b/db/migrate/20181106135939_add_index_to_deployments.rb
new file mode 100644
index 00000000000..5f988a4723c
--- /dev/null
+++ b/db/migrate/20181106135939_add_index_to_deployments.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToDeployments < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :deployments, [:project_id, :status, :created_at]
+ end
+
+ def down
+ remove_concurrent_index :deployments, [:project_id, :status, :created_at]
+ end
+end
diff --git a/db/migrate/20181112103239_drop_default_value_on_status_deployments.rb b/db/migrate/20181112103239_drop_default_value_on_status_deployments.rb
new file mode 100644
index 00000000000..a480c15e66b
--- /dev/null
+++ b/db/migrate/20181112103239_drop_default_value_on_status_deployments.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class DropDefaultValueOnStatusDeployments < ActiveRecord::Migration
+ DOWNTIME = false
+ DEPLOYMENT_STATUS_SUCCESS = 2 # Equivalent to Deployment.state_machine.states['success'].value
+
+ def up
+ change_column_default :deployments, :status, nil
+ end
+
+ def down
+ change_column_default :deployments, :status, DEPLOYMENT_STATUS_SUCCESS
+ end
+end
diff --git a/db/migrate/prometheus_metrics_limits_to_mysql.rb b/db/migrate/prometheus_metrics_limits_to_mysql.rb
new file mode 100644
index 00000000000..79f4ab9b64b
--- /dev/null
+++ b/db/migrate/prometheus_metrics_limits_to_mysql.rb
@@ -0,0 +1,12 @@
+class PrometheusMetricsLimitsToMysql < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def up
+ return unless Gitlab::Database.mysql?
+
+ change_column :prometheus_metrics, :query, :text, limit: 4096, default: nil
+ end
+
+ def down
+ end
+end
diff --git a/db/optional_migrations/composite_primary_keys.rb b/db/optional_migrations/composite_primary_keys.rb
index d45705021b0..b330da13d43 100644
--- a/db/optional_migrations/composite_primary_keys.rb
+++ b/db/optional_migrations/composite_primary_keys.rb
@@ -29,18 +29,20 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
- TABLES.each do |index|
- add_primary_key(index)
+ disable_statement_timeout do
+ TABLES.each do |index|
+ add_primary_key(index)
+ end
end
end
def down
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
- TABLES.each do |index|
- remove_primary_key(index)
+ disable_statement_timeout do
+ TABLES.each do |index|
+ remove_primary_key(index)
+ end
end
end
diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
index 017c58477ac..678876e886c 100644
--- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb
+++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
@@ -1,5 +1,3 @@
-require 'thread'
-
class RenameReservedProjectNames < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::ShellAdapter
@@ -115,7 +113,9 @@ class RenameReservedProjectNames < ActiveRecord::Migration
begin
# Because project path update is quite complex operation we can't safely
# copy-paste all code from GitLab. As exception we use Rails code here
- project.rename_repo if rename_project_row(project, path)
+ if rename_project_row(project, path)
+ Projects::AfterRenameService.new(project).execute
+ end
rescue Exception => e # rubocop: disable Lint/RescueException
Rails.logger.error "Exception when renaming project #{id}: #{e.message}"
end
@@ -125,6 +125,6 @@ class RenameReservedProjectNames < ActiveRecord::Migration
def rename_project_row(project, path)
project.respond_to?(:update_attributes) &&
project.update(path: path) &&
- project.respond_to?(:rename_repo)
+ defined?(Projects::AfterRenameService)
end
end
diff --git a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
index 3e8ccfdb899..26a67b0f814 100644
--- a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
+++ b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
@@ -1,5 +1,3 @@
-require 'thread'
-
class RenameMoreReservedProjectNames < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::ShellAdapter
@@ -57,7 +55,9 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration
begin
# Because project path update is quite complex operation we can't safely
# copy-paste all code from GitLab. As exception we use Rails code here
- project.rename_repo if rename_project_row(project, path)
+ if rename_project_row(project, path)
+ Projects::AfterRenameService.new(project).execute
+ end
rescue Exception => e # rubocop: disable Lint/RescueException
Rails.logger.error "Exception when renaming project #{id}: #{e.message}"
end
@@ -67,6 +67,6 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration
def rename_project_row(project, path)
project.respond_to?(:update_attributes) &&
project.update(path: path) &&
- project.respond_to?(:rename_repo)
+ defined?(Projects::AfterRenameService)
end
end
diff --git a/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb b/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
index bba37e32c01..845c6f0557f 100644
--- a/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
+++ b/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
@@ -8,9 +8,9 @@ class EnableAutoCancelPendingPipelinesForAll < ActiveRecord::Migration
DOWNTIME = false
def up
- disable_statement_timeout
-
- update_column_in_batches(:projects, :auto_cancel_pending_pipelines, 1)
+ disable_statement_timeout do
+ update_column_in_batches(:projects, :auto_cancel_pending_pipelines, 1)
+ end
end
def down
diff --git a/db/post_migrate/20170503004427_update_retried_for_ci_build.rb b/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
index b0b58ab3011..079f0e7511f 100644
--- a/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
+++ b/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
@@ -7,12 +7,12 @@ class UpdateRetriedForCiBuild < ActiveRecord::Migration
disable_ddl_transaction!
def up
- disable_statement_timeout
-
if Gitlab::Database.mysql?
up_mysql
else
- up_postgres
+ disable_statement_timeout do
+ up_postgres
+ end
end
end
diff --git a/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb b/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
index 81e9d050668..5df3ab71648 100644
--- a/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
+++ b/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
@@ -7,20 +7,20 @@ class AddHeadPipelineForEachMergeRequest < ActiveRecord::Migration
disable_ddl_transaction!
def up
- disable_statement_timeout
-
pipelines = Arel::Table.new(:ci_pipelines)
merge_requests = Arel::Table.new(:merge_requests)
- head_id = pipelines
- .project(Arel::Nodes::NamedFunction.new('max', [pipelines[:id]]))
- .from(pipelines)
- .where(pipelines[:ref].eq(merge_requests[:source_branch]))
- .where(pipelines[:project_id].eq(merge_requests[:source_project_id]))
+ disable_statement_timeout do
+ head_id = pipelines
+ .project(Arel::Nodes::NamedFunction.new('max', [pipelines[:id]]))
+ .from(pipelines)
+ .where(pipelines[:ref].eq(merge_requests[:source_branch]))
+ .where(pipelines[:project_id].eq(merge_requests[:source_project_id]))
- sub_query = Arel::Nodes::SqlLiteral.new(Arel::Nodes::Grouping.new(head_id).to_sql)
+ sub_query = Arel::Nodes::SqlLiteral.new(Arel::Nodes::Grouping.new(head_id).to_sql)
- update_column_in_batches(:merge_requests, :head_pipeline_id, sub_query)
+ update_column_in_batches(:merge_requests, :head_pipeline_id, sub_query)
+ end
end
def down
diff --git a/db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb b/db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb
index 2125cc046e5..c996ddbec84 100644
--- a/db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb
+++ b/db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb
@@ -87,16 +87,16 @@ class RenameAllReservedPathsAgain < ActiveRecord::Migration
].freeze
def up
- disable_statement_timeout
-
- TOP_LEVEL_ROUTES.each { |route| rename_root_paths(route) }
- PROJECT_WILDCARD_ROUTES.each { |route| rename_wildcard_paths(route) }
- GROUP_ROUTES.each { |route| rename_child_paths(route) }
+ disable_statement_timeout do
+ TOP_LEVEL_ROUTES.each { |route| rename_root_paths(route) }
+ PROJECT_WILDCARD_ROUTES.each { |route| rename_wildcard_paths(route) }
+ GROUP_ROUTES.each { |route| rename_child_paths(route) }
+ end
end
def down
- disable_statement_timeout
-
- revert_renames
+ disable_statement_timeout do
+ revert_renames
+ end
end
end
diff --git a/db/post_migrate/20170526185842_migrate_pipeline_stages.rb b/db/post_migrate/20170526185842_migrate_pipeline_stages.rb
index afd4db183c2..736aff77f02 100644
--- a/db/post_migrate/20170526185842_migrate_pipeline_stages.rb
+++ b/db/post_migrate/20170526185842_migrate_pipeline_stages.rb
@@ -6,17 +6,17 @@ class MigratePipelineStages < ActiveRecord::Migration
disable_ddl_transaction!
def up
- disable_statement_timeout
-
- execute <<-SQL.strip_heredoc
- INSERT INTO ci_stages (project_id, pipeline_id, name)
- SELECT project_id, commit_id, stage FROM ci_builds
- WHERE stage IS NOT NULL
- AND stage_id IS NULL
- AND EXISTS (SELECT 1 FROM projects WHERE projects.id = ci_builds.project_id)
- AND EXISTS (SELECT 1 FROM ci_pipelines WHERE ci_pipelines.id = ci_builds.commit_id)
- GROUP BY project_id, commit_id, stage
- ORDER BY MAX(stage_idx)
- SQL
+ disable_statement_timeout do
+ execute <<-SQL.strip_heredoc
+ INSERT INTO ci_stages (project_id, pipeline_id, name)
+ SELECT project_id, commit_id, stage FROM ci_builds
+ WHERE stage IS NOT NULL
+ AND stage_id IS NULL
+ AND EXISTS (SELECT 1 FROM projects WHERE projects.id = ci_builds.project_id)
+ AND EXISTS (SELECT 1 FROM ci_pipelines WHERE ci_pipelines.id = ci_builds.commit_id)
+ GROUP BY project_id, commit_id, stage
+ ORDER BY MAX(stage_idx)
+ SQL
+ end
end
end
diff --git a/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb b/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
index 31a73bb3b27..a7bfba0ab2b 100644
--- a/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
+++ b/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
@@ -7,22 +7,22 @@ class MigrateBuildStageReferenceAgain < ActiveRecord::Migration
disable_ddl_transaction!
def up
- disable_statement_timeout
-
stage_id = Arel.sql <<-SQL.strip_heredoc
(SELECT id FROM ci_stages
WHERE ci_stages.pipeline_id = ci_builds.commit_id
AND ci_stages.name = ci_builds.stage)
SQL
- update_column_in_batches(:ci_builds, :stage_id, stage_id) do |table, query|
- query.where(table[:stage_id].eq(nil))
+ disable_statement_timeout do
+ update_column_in_batches(:ci_builds, :stage_id, stage_id) do |table, query|
+ query.where(table[:stage_id].eq(nil))
+ end
end
end
def down
- disable_statement_timeout
-
- update_column_in_batches(:ci_builds, :stage_id, nil)
+ disable_statement_timeout do
+ update_column_in_batches(:ci_builds, :stage_id, nil)
+ end
end
end
diff --git a/db/post_migrate/20170711145558_migrate_stages_statuses.rb b/db/post_migrate/20170711145558_migrate_stages_statuses.rb
index 65755c0e824..265f7317b9b 100644
--- a/db/post_migrate/20170711145558_migrate_stages_statuses.rb
+++ b/db/post_migrate/20170711145558_migrate_stages_statuses.rb
@@ -26,9 +26,9 @@ class MigrateStagesStatuses < ActiveRecord::Migration
end
def down
- disable_statement_timeout
-
- # rubocop:disable Migration/UpdateLargeTable
- update_column_in_batches(:ci_stages, :status, nil)
+ disable_statement_timeout do
+ # rubocop:disable Migration/UpdateLargeTable
+ update_column_in_batches(:ci_stages, :status, nil)
+ end
end
end
diff --git a/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb b/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
index c48f1c938d0..3ae4406ff96 100644
--- a/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
+++ b/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
@@ -13,7 +13,7 @@ class CleanupMoveSystemUploadFolderSymlink < ActiveRecord::Migration
say "Removing #{old_directory} -> #{new_directory} symlink"
FileUtils.rm(old_directory)
else
- say "Symlink #{old_directory} non existant, nothing to do."
+ say "Symlink #{old_directory} non existent, nothing to do."
end
end
diff --git a/db/post_migrate/20171207150343_remove_soft_removed_objects.rb b/db/post_migrate/20171207150343_remove_soft_removed_objects.rb
index 3e2dedfdd6a..3109b6dbf8e 100644
--- a/db/post_migrate/20171207150343_remove_soft_removed_objects.rb
+++ b/db/post_migrate/20171207150343_remove_soft_removed_objects.rb
@@ -78,12 +78,12 @@ class RemoveSoftRemovedObjects < ActiveRecord::Migration
MODELS = [Issue, MergeRequest, CiPipelineSchedule, CiTrigger].freeze
def up
- disable_statement_timeout
-
- remove_personal_routes
- remove_personal_namespaces
- remove_group_namespaces
- remove_simple_soft_removed_rows
+ disable_statement_timeout do
+ remove_personal_routes
+ remove_personal_namespaces
+ remove_group_namespaces
+ remove_simple_soft_removed_rows
+ end
end
def down
diff --git a/db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb b/db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb
index 61ea85eb2a7..269f1287f91 100644
--- a/db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb
+++ b/db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb
@@ -38,29 +38,29 @@ class RemoveRedundantPipelineStages < ActiveRecord::Migration
end
def remove_redundant_pipeline_stages!
- disable_statement_timeout
-
- redundant_stages_ids = <<~SQL
- SELECT id FROM ci_stages WHERE (pipeline_id, name) IN (
- SELECT pipeline_id, name FROM ci_stages
- GROUP BY pipeline_id, name HAVING COUNT(*) > 1
- )
- SQL
-
- execute <<~SQL
- UPDATE ci_builds SET stage_id = NULL WHERE stage_id IN (#{redundant_stages_ids})
- SQL
-
- if Gitlab::Database.postgresql?
- execute <<~SQL
- DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids})
+ disable_statement_timeout do
+ redundant_stages_ids = <<~SQL
+ SELECT id FROM ci_stages WHERE (pipeline_id, name) IN (
+ SELECT pipeline_id, name FROM ci_stages
+ GROUP BY pipeline_id, name HAVING COUNT(*) > 1
+ )
SQL
- else # We can't modify a table we are selecting from on MySQL
+
execute <<~SQL
- DELETE a FROM ci_stages AS a, ci_stages AS b
- WHERE a.pipeline_id = b.pipeline_id AND a.name = b.name
- AND a.id <> b.id
+ UPDATE ci_builds SET stage_id = NULL WHERE stage_id IN (#{redundant_stages_ids})
SQL
+
+ if Gitlab::Database.postgresql?
+ execute <<~SQL
+ DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids})
+ SQL
+ else # We can't modify a table we are selecting from on MySQL
+ execute <<~SQL
+ DELETE a FROM ci_stages AS a, ci_stages AS b
+ WHERE a.pipeline_id = b.pipeline_id AND a.name = b.name
+ AND a.id <> b.id
+ SQL
+ end
end
end
end
diff --git a/db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb b/db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb
index db5165dbe70..aa19732ca1c 100644
--- a/db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb
+++ b/db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb
@@ -15,10 +15,10 @@ class RemovePermanentFromRedirectRoutes < ActiveRecord::Migration
# ReworkRedirectRoutesIndexes:
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/16211
if Gitlab::Database.postgresql?
- disable_statement_timeout
-
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};"
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};"
+ disable_statement_timeout do
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};"
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};"
+ end
end
remove_column(:redirect_routes, :permanent)
@@ -28,10 +28,10 @@ class RemovePermanentFromRedirectRoutes < ActiveRecord::Migration
add_column(:redirect_routes, :permanent, :boolean)
if Gitlab::Database.postgresql?
- disable_statement_timeout
-
- execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
- execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
+ disable_statement_timeout do
+ execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
+ execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
+ end
end
end
end
diff --git a/db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb b/db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb
index d6fb4f06695..ca9212fae27 100644
--- a/db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb
+++ b/db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb
@@ -20,10 +20,10 @@ class AddPathIndexToRedirectRoutes < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- unless index_exists_by_name?(:redirect_routes, INDEX_NAME)
- execute("CREATE UNIQUE INDEX CONCURRENTLY #{INDEX_NAME} ON redirect_routes (lower(path) varchar_pattern_ops);")
+ disable_statement_timeout do
+ unless index_exists_by_name?(:redirect_routes, INDEX_NAME)
+ execute("CREATE UNIQUE INDEX CONCURRENTLY #{INDEX_NAME} ON redirect_routes (lower(path) varchar_pattern_ops);")
+ end
end
end
diff --git a/db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb b/db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb
index e19387bce1e..c32123454f9 100644
--- a/db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb
+++ b/db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb
@@ -17,13 +17,13 @@ class RescheduleBuildsStagesMigration < ActiveRecord::Migration
end
def up
- disable_statement_timeout
-
- Build.where('stage_id IS NULL').tap do |relation|
- queue_background_migration_jobs_by_range_at_intervals(relation,
- MIGRATION,
- 5.minutes,
- batch_size: BATCH_SIZE)
+ disable_statement_timeout do
+ Build.where('stage_id IS NULL').tap do |relation|
+ queue_background_migration_jobs_by_range_at_intervals(relation,
+ MIGRATION,
+ 5.minutes,
+ batch_size: BATCH_SIZE)
+ end
end
end
diff --git a/db/post_migrate/20180420080616_schedule_stages_index_migration.rb b/db/post_migrate/20180420080616_schedule_stages_index_migration.rb
index 1d0daad002f..eb82f098639 100644
--- a/db/post_migrate/20180420080616_schedule_stages_index_migration.rb
+++ b/db/post_migrate/20180420080616_schedule_stages_index_migration.rb
@@ -13,13 +13,13 @@ class ScheduleStagesIndexMigration < ActiveRecord::Migration
end
def up
- disable_statement_timeout
-
- Stage.all.tap do |relation|
- queue_background_migration_jobs_by_range_at_intervals(relation,
- MIGRATION,
- 5.minutes,
- batch_size: BATCH_SIZE)
+ disable_statement_timeout do
+ Stage.all.tap do |relation|
+ queue_background_migration_jobs_by_range_at_intervals(relation,
+ MIGRATION,
+ 5.minutes,
+ batch_size: BATCH_SIZE)
+ end
end
end
diff --git a/db/post_migrate/20180604123514_cleanup_stages_position_migration.rb b/db/post_migrate/20180604123514_cleanup_stages_position_migration.rb
index 73c23dffca0..5418f442e79 100644
--- a/db/post_migrate/20180604123514_cleanup_stages_position_migration.rb
+++ b/db/post_migrate/20180604123514_cleanup_stages_position_migration.rb
@@ -12,32 +12,34 @@ class CleanupStagesPositionMigration < ActiveRecord::Migration
end
def up
- disable_statement_timeout
+ disable_statement_timeout do
+ Gitlab::BackgroundMigration.steal('MigrateStageIndex')
- Gitlab::BackgroundMigration.steal('MigrateStageIndex')
-
- unless index_exists_by_name?(:ci_stages, TMP_INDEX_NAME)
- add_concurrent_index(:ci_stages, :id, where: 'position IS NULL', name: TMP_INDEX_NAME)
- end
+ unless index_exists_by_name?(:ci_stages, TMP_INDEX_NAME)
+ add_concurrent_index(:ci_stages, :id, where: 'position IS NULL', name: TMP_INDEX_NAME)
+ end
- migratable = <<~SQL
- position IS NULL AND EXISTS (
- SELECT 1 FROM ci_builds WHERE stage_id = ci_stages.id AND stage_idx IS NOT NULL
- )
- SQL
+ migratable = <<~SQL
+ position IS NULL AND EXISTS (
+ SELECT 1 FROM ci_builds WHERE stage_id = ci_stages.id AND stage_idx IS NOT NULL
+ )
+ SQL
- Stages.where(migratable).each_batch(of: 1000) do |batch|
- batch.pluck(:id).each do |stage|
- Gitlab::BackgroundMigration::MigrateStageIndex.new.perform(stage, stage)
+ Stages.where(migratable).each_batch(of: 1000) do |batch|
+ batch.pluck(:id).each do |stage|
+ Gitlab::BackgroundMigration::MigrateStageIndex.new.perform(stage, stage)
+ end
end
- end
- remove_concurrent_index_by_name(:ci_stages, TMP_INDEX_NAME)
+ remove_concurrent_index_by_name(:ci_stages, TMP_INDEX_NAME)
+ end
end
def down
if index_exists_by_name?(:ci_stages, TMP_INDEX_NAME)
- remove_concurrent_index_by_name(:ci_stages, TMP_INDEX_NAME)
+ disable_statement_timeout do
+ remove_concurrent_index_by_name(:ci_stages, TMP_INDEX_NAME)
+ end
end
end
end
diff --git a/db/post_migrate/20180723130817_delete_inconsistent_internal_id_records.rb b/db/post_migrate/20180723130817_delete_inconsistent_internal_id_records.rb
new file mode 100644
index 00000000000..3b9b95ec9ca
--- /dev/null
+++ b/db/post_migrate/20180723130817_delete_inconsistent_internal_id_records.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+class DeleteInconsistentInternalIdRecords < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ # This migration cleans up any inconsistent records in internal_ids.
+ #
+ # That is, it deletes records that track a `last_value` that is
+ # smaller than the maximum internal id (usually `iid`) found in
+ # the corresponding model records.
+
+ def up
+ disable_statement_timeout do
+ delete_internal_id_records('issues', 'project_id')
+ delete_internal_id_records('merge_requests', 'project_id', 'target_project_id')
+ delete_internal_id_records('deployments', 'project_id')
+ delete_internal_id_records('milestones', 'project_id')
+ delete_internal_id_records('milestones', 'namespace_id', 'group_id')
+ delete_internal_id_records('ci_pipelines', 'project_id')
+ end
+ end
+
+ class InternalId < ActiveRecord::Base
+ self.table_name = 'internal_ids'
+ enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5 }
+ end
+
+ private
+
+ def delete_internal_id_records(base_table, scope_column_name, base_scope_column_name = scope_column_name)
+ sql = <<~SQL
+ SELECT id FROM ( -- workaround for MySQL
+ SELECT internal_ids.id FROM (
+ SELECT #{base_scope_column_name} AS #{scope_column_name}, max(iid) as maximum_iid from #{base_table} GROUP BY #{scope_column_name}
+ ) maxima JOIN internal_ids USING (#{scope_column_name})
+ WHERE internal_ids.usage=#{InternalId.usages.fetch(base_table)} AND maxima.maximum_iid > internal_ids.last_value
+ ) internal_ids
+ SQL
+
+ InternalId.where("id IN (#{sql})").tap do |ids| # rubocop:disable GitlabSecurity/SqlInjection
+ say "Deleting internal_id records for #{base_table}: #{ids.pluck(:project_id, :last_value)}" unless ids.empty?
+ end.delete_all
+ end
+end
diff --git a/db/post_migrate/20180809195358_migrate_null_wiki_access_levels.rb b/db/post_migrate/20180809195358_migrate_null_wiki_access_levels.rb
new file mode 100644
index 00000000000..0a0a33299e4
--- /dev/null
+++ b/db/post_migrate/20180809195358_migrate_null_wiki_access_levels.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class MigrateNullWikiAccessLevels < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class ProjectFeature < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'project_features'
+ end
+
+ def up
+ ProjectFeature.where(wiki_access_level: nil).each_batch do |relation|
+ relation.update_all(wiki_access_level: 20)
+ end
+
+ # We need to re-count wikis as previous attempt was not considering the NULLs.
+ transaction do
+ execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
+
+ execute("UPDATE site_statistics SET wikis_count = (SELECT COUNT(*) FROM project_features WHERE wiki_access_level != 0)")
+ end
+ end
+
+ def down
+ # there is no way to rollback this change, there are no downsides in keeping migrated data.
+ end
+end
diff --git a/db/post_migrate/20180816161409_migrate_legacy_artifacts_to_job_artifacts.rb b/db/post_migrate/20180816161409_migrate_legacy_artifacts_to_job_artifacts.rb
new file mode 100644
index 00000000000..2dd711e9c10
--- /dev/null
+++ b/db/post_migrate/20180816161409_migrate_legacy_artifacts_to_job_artifacts.rb
@@ -0,0 +1,32 @@
+class MigrateLegacyArtifactsToJobArtifacts < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ MIGRATION = 'MigrateLegacyArtifacts'.freeze
+ BATCH_SIZE = 100
+
+ disable_ddl_transaction!
+
+ class Build < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'ci_builds'
+ self.inheritance_column = :_type_disabled
+
+ scope :with_legacy_artifacts, -> { where("artifacts_file <> ''") }
+ end
+
+ def up
+ MigrateLegacyArtifactsToJobArtifacts::Build
+ .with_legacy_artifacts.tap do |relation|
+ queue_background_migration_jobs_by_range_at_intervals(relation,
+ MIGRATION,
+ 5.minutes,
+ batch_size: BATCH_SIZE)
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20180816193530_rename_login_root_namespaces.rb b/db/post_migrate/20180816193530_rename_login_root_namespaces.rb
new file mode 100644
index 00000000000..4ab1250473f
--- /dev/null
+++ b/db/post_migrate/20180816193530_rename_login_root_namespaces.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+class RenameLoginRootNamespaces < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ include Gitlab::Database::RenameReservedPathsMigration::V1
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ # We're taking over the /login namespace as part of a fix for the Jira integration
+ def up
+ disable_statement_timeout do
+ rename_root_paths 'login'
+ end
+ end
+
+ def down
+ disable_statement_timeout do
+ revert_renames
+ end
+ end
+end
diff --git a/db/post_migrate/20180826111825_recalculate_site_statistics.rb b/db/post_migrate/20180826111825_recalculate_site_statistics.rb
new file mode 100644
index 00000000000..741035a444f
--- /dev/null
+++ b/db/post_migrate/20180826111825_recalculate_site_statistics.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class RecalculateSiteStatistics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ transaction do
+ execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
+
+ execute("UPDATE site_statistics SET repositories_count = (SELECT COUNT(*) FROM projects)")
+ end
+
+ transaction do
+ execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
+
+ execute("UPDATE site_statistics SET wikis_count = (SELECT COUNT(*) FROM project_features WHERE wiki_access_level != 0)")
+ end
+ end
+
+ def down
+ # No downside in keeping the counter up-to-date
+ end
+end
diff --git a/db/post_migrate/20180906051323_remove_orphaned_label_links.rb b/db/post_migrate/20180906051323_remove_orphaned_label_links.rb
new file mode 100644
index 00000000000..b56b74f483e
--- /dev/null
+++ b/db/post_migrate/20180906051323_remove_orphaned_label_links.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+class RemoveOrphanedLabelLinks < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class LabelLinks < ActiveRecord::Base
+ self.table_name = 'label_links'
+ include EachBatch
+
+ def self.orphaned
+ where('NOT EXISTS ( SELECT 1 FROM labels WHERE labels.id = label_links.label_id )')
+ end
+ end
+
+ def up
+ # Some of these queries can take up to 10 seconds to run on GitLab.com,
+ # which is pretty close to our 15 second statement timeout. To ensure a
+ # smooth deployment procedure we disable the statement timeouts for this
+ # migration, just in case.
+ disable_statement_timeout do
+ # On GitLab.com there are over 2,000,000 orphaned label links. On
+ # staging, removing 100,000 rows generated a max replication lag of 6.7
+ # MB. In total, removing all these rows will only generate about 136 MB
+ # of data, so it should be safe to do this.
+ LabelLinks.orphaned.each_batch(of: 100_000) do |batch|
+ batch.delete_all
+ end
+ end
+
+ add_concurrent_foreign_key(:label_links, :labels, column: :label_id, on_delete: :cascade)
+ end
+
+ def down
+ # There is no way to restore orphaned label links.
+ if foreign_key_exists?(:label_links, column: :label_id)
+ remove_foreign_key(:label_links, column: :label_id)
+ end
+ end
+end
diff --git a/db/post_migrate/20180913051323_consume_remaining_diff_files_deletion_jobs.rb b/db/post_migrate/20180913051323_consume_remaining_diff_files_deletion_jobs.rb
new file mode 100644
index 00000000000..ed9422a3894
--- /dev/null
+++ b/db/post_migrate/20180913051323_consume_remaining_diff_files_deletion_jobs.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class ConsumeRemainingDiffFilesDeletionJobs < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ MIGRATION = 'ScheduleDiffFilesDeletion'.freeze
+ TMP_INDEX = 'tmp_partial_diff_id_with_files_index'.freeze
+
+ def up
+ # Perform any ongoing background migration that might still be scheduled.
+ Gitlab::BackgroundMigration.steal(MIGRATION)
+
+ remove_concurrent_index_by_name(:merge_request_diffs, TMP_INDEX)
+ end
+
+ def down
+ add_concurrent_index(:merge_request_diffs, :id, where: "(state NOT IN ('without_files', 'empty'))", name: TMP_INDEX)
+ end
+end
diff --git a/db/post_migrate/20180913142237_schedule_digest_personal_access_tokens.rb b/db/post_migrate/20180913142237_schedule_digest_personal_access_tokens.rb
new file mode 100644
index 00000000000..36be819b245
--- /dev/null
+++ b/db/post_migrate/20180913142237_schedule_digest_personal_access_tokens.rb
@@ -0,0 +1,28 @@
+class ScheduleDigestPersonalAccessTokens < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ BATCH_SIZE = 10_000
+ MIGRATION = 'DigestColumn'
+ DELAY_INTERVAL = 5.minutes.to_i
+
+ disable_ddl_transaction!
+
+ class PersonalAccessToken < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'personal_access_tokens'
+ end
+
+ def up
+ PersonalAccessToken.where('token is NOT NULL').each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+ BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, ['PersonalAccessToken', :token, :token_digest, *range])
+ end
+ end
+
+ def down
+ # raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/post_migrate/20180914162043_encrypt_web_hooks_columns.rb b/db/post_migrate/20180914162043_encrypt_web_hooks_columns.rb
new file mode 100644
index 00000000000..05ec4864a9e
--- /dev/null
+++ b/db/post_migrate/20180914162043_encrypt_web_hooks_columns.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class EncryptWebHooksColumns < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ BATCH_SIZE = 10000
+ RANGE_SIZE = 100
+ MIGRATION = 'EncryptColumns'
+ COLUMNS = [:token, :url]
+
+ WebHook = ::Gitlab::BackgroundMigration::Models::EncryptColumns::WebHook
+
+ disable_ddl_transaction!
+
+ def up
+ WebHook.each_batch(of: BATCH_SIZE) do |relation, index|
+ delay = index * 2.minutes
+
+ relation.each_batch(of: RANGE_SIZE) do |relation|
+ range = relation.pluck('MIN(id)', 'MAX(id)').first
+ args = [WebHook, COLUMNS, *range]
+
+ BackgroundMigrationWorker.perform_in(delay, MIGRATION, args)
+ end
+ end
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/post_migrate/20180914201132_remove_sidekiq_throttling_from_application_settings.rb b/db/post_migrate/20180914201132_remove_sidekiq_throttling_from_application_settings.rb
new file mode 100644
index 00000000000..b3ed0d3f1e9
--- /dev/null
+++ b/db/post_migrate/20180914201132_remove_sidekiq_throttling_from_application_settings.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveSidekiqThrottlingFromApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ remove_column :application_settings, :sidekiq_throttling_enabled, :boolean, default: false
+ remove_column :application_settings, :sidekiq_throttling_queues, :string
+ remove_column :application_settings, :sidekiq_throttling_factor, :decimal
+
+ Rails.cache.delete("ApplicationSetting:#{Gitlab::VERSION}:#{Rails.version}")
+ end
+end
diff --git a/db/post_migrate/20180916014356_populate_external_pipeline_source.rb b/db/post_migrate/20180916014356_populate_external_pipeline_source.rb
new file mode 100644
index 00000000000..5577d05cf40
--- /dev/null
+++ b/db/post_migrate/20180916014356_populate_external_pipeline_source.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class PopulateExternalPipelineSource < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+ MIGRATION = 'PopulateExternalPipelineSource'.freeze
+ BATCH_SIZE = 500
+
+ disable_ddl_transaction!
+
+ class Pipeline < ActiveRecord::Base
+ include EachBatch
+ self.table_name = 'ci_pipelines'
+ end
+
+ def up
+ Pipeline.where(source: nil).tap do |relation|
+ queue_background_migration_jobs_by_range_at_intervals(relation,
+ MIGRATION,
+ 5.minutes,
+ batch_size: BATCH_SIZE)
+ end
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/post_migrate/20180917172041_remove_wikis_count_from_site_statistics.rb b/db/post_migrate/20180917172041_remove_wikis_count_from_site_statistics.rb
new file mode 100644
index 00000000000..0a39abe3bdf
--- /dev/null
+++ b/db/post_migrate/20180917172041_remove_wikis_count_from_site_statistics.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+class RemoveWikisCountFromSiteStatistics < ActiveRecord::Migration
+ def change
+ remove_column :site_statistics, :wikis_count, :integer
+ end
+end
diff --git a/db/post_migrate/20181008145341_steal_encrypt_columns.rb b/db/post_migrate/20181008145341_steal_encrypt_columns.rb
new file mode 100644
index 00000000000..c107ac72913
--- /dev/null
+++ b/db/post_migrate/20181008145341_steal_encrypt_columns.rb
@@ -0,0 +1,15 @@
+class StealEncryptColumns < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ Gitlab::BackgroundMigration.steal('EncryptColumns')
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20181008145359_remove_web_hooks_token_and_url.rb b/db/post_migrate/20181008145359_remove_web_hooks_token_and_url.rb
new file mode 100644
index 00000000000..0c44bca5f1a
--- /dev/null
+++ b/db/post_migrate/20181008145359_remove_web_hooks_token_and_url.rb
@@ -0,0 +1,10 @@
+class RemoveWebHooksTokenAndUrl < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ remove_column :web_hooks, :token, :string
+ remove_column :web_hooks, :url, :string, limit: 2000
+ end
+end
diff --git a/db/post_migrate/20181008200441_remove_circuit_breaker.rb b/db/post_migrate/20181008200441_remove_circuit_breaker.rb
new file mode 100644
index 00000000000..838addb7286
--- /dev/null
+++ b/db/post_migrate/20181008200441_remove_circuit_breaker.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class RemoveCircuitBreaker < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ CIRCUIT_BREAKER_COLUMS_WITH_DEFAULT = {
+ circuitbreaker_failure_count_threshold: 3,
+ circuitbreaker_failure_reset_time: 1800,
+ circuitbreaker_storage_timeout: 15,
+ circuitbreaker_access_retries: 3,
+ circuitbreaker_check_interval: 1
+ }.freeze
+
+ def up
+ CIRCUIT_BREAKER_COLUMS_WITH_DEFAULT.keys.each do |column|
+ remove_column(:application_settings, column) if column_exists?(:application_settings, column)
+ end
+ end
+
+ def down
+ CIRCUIT_BREAKER_COLUMS_WITH_DEFAULT.each do |column, default|
+ add_column_with_default(:application_settings, column, :integer, default: default) unless column_exists?(:application_settings, column)
+ end
+ end
+end
diff --git a/db/post_migrate/20181013005024_remove_koding_from_application_settings.rb b/db/post_migrate/20181013005024_remove_koding_from_application_settings.rb
new file mode 100644
index 00000000000..938a32e4e98
--- /dev/null
+++ b/db/post_migrate/20181013005024_remove_koding_from_application_settings.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RemoveKodingFromApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ remove_column :application_settings, :koding_enabled
+ remove_column :application_settings, :koding_url
+ end
+
+ def down
+ add_column :application_settings, :koding_enabled, :boolean # rubocop:disable Migration/SaferBooleanColumn
+ add_column :application_settings, :koding_url, :string
+ end
+end
diff --git a/db/post_migrate/20181014121030_enqueue_redact_links.rb b/db/post_migrate/20181014121030_enqueue_redact_links.rb
new file mode 100644
index 00000000000..1ee4703c88a
--- /dev/null
+++ b/db/post_migrate/20181014121030_enqueue_redact_links.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+class EnqueueRedactLinks < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ BATCH_SIZE = 1000
+ DELAY_INTERVAL = 5.minutes.to_i
+ MIGRATION = 'RedactLinks'
+
+ disable_ddl_transaction!
+
+ class Note < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'notes'
+ self.inheritance_column = :_type_disabled
+ end
+
+ class Issue < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'issues'
+ self.inheritance_column = :_type_disabled
+ end
+
+ class MergeRequest < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'merge_requests'
+ self.inheritance_column = :_type_disabled
+ end
+
+ class Snippet < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'snippets'
+ self.inheritance_column = :_type_disabled
+ end
+
+ def up
+ disable_statement_timeout do
+ schedule_migration(Note, 'note')
+ schedule_migration(Issue, 'description')
+ schedule_migration(MergeRequest, 'description')
+ schedule_migration(Snippet, 'description')
+ end
+ end
+
+ def down
+ # nothing to do
+ end
+
+ private
+
+ def schedule_migration(model, field)
+ link_pattern = "%/sent_notifications/" + ("_" * 32) + "/unsubscribe%"
+
+ model.where("#{field} like ?", link_pattern).each_batch(of: BATCH_SIZE) do |batch, index|
+ start_id, stop_id = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, [model.name.demodulize, field, start_id, stop_id])
+ end
+ end
+end
diff --git a/db/post_migrate/20181022173835_enqueue_populate_cluster_kubernetes_namespace.rb b/db/post_migrate/20181022173835_enqueue_populate_cluster_kubernetes_namespace.rb
new file mode 100644
index 00000000000..f80a2aa6eac
--- /dev/null
+++ b/db/post_migrate/20181022173835_enqueue_populate_cluster_kubernetes_namespace.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class EnqueuePopulateClusterKubernetesNamespace < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ MIGRATION = 'PopulateClusterKubernetesNamespaceTable'.freeze
+
+ disable_ddl_transaction!
+
+ def up
+ BackgroundMigrationWorker.perform_async(MIGRATION)
+ end
+
+ def down
+ Clusters::KubernetesNamespace.delete_all
+ end
+end
diff --git a/db/post_migrate/20181030135124_fill_empty_finished_at_in_deployments.rb b/db/post_migrate/20181030135124_fill_empty_finished_at_in_deployments.rb
new file mode 100644
index 00000000000..32b271c472a
--- /dev/null
+++ b/db/post_migrate/20181030135124_fill_empty_finished_at_in_deployments.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class FillEmptyFinishedAtInDeployments < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ DEPLOYMENT_STATUS_SUCCESS = 2 # Equivalent to Deployment.statuses[:success]
+
+ class Deployments < ActiveRecord::Base
+ self.table_name = 'deployments'
+
+ include EachBatch
+ end
+
+ def up
+ FillEmptyFinishedAtInDeployments::Deployments
+ .where('finished_at IS NULL')
+ .where('status = ?', DEPLOYMENT_STATUS_SUCCESS)
+ .each_batch(of: 10_000) do |relation|
+ relation.update_all('finished_at=created_at')
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20181105201455_steal_fill_store_upload.rb b/db/post_migrate/20181105201455_steal_fill_store_upload.rb
new file mode 100644
index 00000000000..982001fedbe
--- /dev/null
+++ b/db/post_migrate/20181105201455_steal_fill_store_upload.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class StealFillStoreUpload < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ BATCH_SIZE = 10_000
+
+ disable_ddl_transaction!
+
+ class Upload < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'uploads'
+ self.inheritance_column = :_type_disabled # Disable STI
+ end
+
+ def up
+ Gitlab::BackgroundMigration.steal('FillStoreUpload')
+
+ Upload.where(store: nil).each_batch(of: BATCH_SIZE) do |batch|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ Gitlab::BackgroundMigration::FillStoreUpload.new.perform(*range)
+ end
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/post_migrate/20181107054254_remove_restricted_todos_again.rb b/db/post_migrate/20181107054254_remove_restricted_todos_again.rb
new file mode 100644
index 00000000000..644e0074c46
--- /dev/null
+++ b/db/post_migrate/20181107054254_remove_restricted_todos_again.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+# rescheduling of the revised RemoveRestrictedTodosWithCte background migration
+class RemoveRestrictedTodosAgain < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ disable_ddl_transaction!
+
+ MIGRATION = 'RemoveRestrictedTodos'.freeze
+ BATCH_SIZE = 1000
+ DELAY_INTERVAL = 5.minutes.to_i
+
+ class Project < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'projects'
+ end
+
+ def up
+ Project.where('EXISTS (SELECT 1 FROM todos WHERE todos.project_id = projects.id)')
+ .each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, range)
+ end
+ end
+
+ def down
+ # nothing to do
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f1d8f4df3b7..cc47368c530 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1,4 +1,3 @@
-# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
@@ -11,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180807153545) do
+ActiveRecord::Schema.define(version: 20181112103239) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -104,8 +103,6 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "domain_blacklist_enabled", default: false
t.text "domain_blacklist"
t.boolean "usage_ping_enabled", default: true, null: false
- t.boolean "koding_enabled"
- t.string "koding_url"
t.text "sign_in_text_html"
t.text "help_page_text_html"
t.text "shared_runners_text_html"
@@ -119,9 +116,6 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "housekeeping_incremental_repack_period", default: 10, null: false
t.integer "housekeeping_full_repack_period", default: 50, null: false
t.integer "housekeeping_gc_period", default: 200, null: false
- t.boolean "sidekiq_throttling_enabled", default: false
- t.string "sidekiq_throttling_queues"
- t.decimal "sidekiq_throttling_factor"
t.boolean "html_emails_enabled", default: true
t.string "plantuml_url"
t.boolean "plantuml_enabled"
@@ -131,7 +125,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "unique_ips_limit_enabled", default: false, null: false
t.string "default_artifacts_expire_in", default: "0", null: false
t.string "uuid"
- t.decimal "polling_interval_multiplier", default: 1.0, null: false
+ t.decimal "polling_interval_multiplier", default: "1.0", null: false
t.integer "cached_markdown_version"
t.boolean "clientside_sentry_enabled", default: false, null: false
t.string "clientside_sentry_dsn"
@@ -141,11 +135,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "performance_bar_allowed_group_id"
t.boolean "hashed_storage_enabled", default: false, null: false
t.boolean "project_export_enabled", default: true, null: false
- t.boolean "auto_devops_enabled", default: false, null: false
- t.integer "circuitbreaker_failure_count_threshold", default: 3
- t.integer "circuitbreaker_failure_reset_time", default: 1800
- t.integer "circuitbreaker_storage_timeout", default: 15
- t.integer "circuitbreaker_access_retries", default: 3
+ t.boolean "auto_devops_enabled", default: true, null: false
t.boolean "throttle_unauthenticated_enabled", default: false, null: false
t.integer "throttle_unauthenticated_requests_per_period", default: 3600, null: false
t.integer "throttle_unauthenticated_period_in_seconds", default: 3600, null: false
@@ -155,7 +145,6 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "throttle_authenticated_web_enabled", default: false, null: false
t.integer "throttle_authenticated_web_requests_per_period", default: 7200, null: false
t.integer "throttle_authenticated_web_period_in_seconds", default: 3600, null: false
- t.integer "circuitbreaker_check_interval", default: 1, null: false
t.boolean "password_authentication_enabled_for_web"
t.boolean "password_authentication_enabled_for_git", default: true
t.integer "gitaly_timeout_default", default: 55, null: false
@@ -164,12 +153,20 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "authorized_keys_enabled", default: true, null: false
t.string "auto_devops_domain"
t.boolean "pages_domain_verification_enabled", default: true, null: false
+ t.string "user_default_internal_regex"
t.boolean "allow_local_requests_from_hooks_and_services", default: false, null: false
t.boolean "enforce_terms", default: false
t.boolean "mirror_available", default: true, null: false
t.boolean "hide_third_party_offers", default: false, null: false
t.boolean "instance_statistics_visibility_private", default: false, null: false
t.boolean "web_ide_clientside_preview_enabled", default: false, null: false
+ t.boolean "user_show_add_ssh_key_message", default: true, null: false
+ t.integer "usage_stats_set_by_user_id"
+ t.integer "receive_max_input_size"
+ t.integer "diff_max_patch_bytes", default: 102400, null: false
+ t.integer "archive_builds_in_seconds"
+ t.string "commit_email_hostname"
+ t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
end
create_table "audit_events", force: :cascade do |t|
@@ -180,10 +177,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.text "details"
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree
end
- add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree
-
create_table "award_emoji", force: :cascade do |t|
t.string "name"
t.integer "user_id"
@@ -191,11 +187,10 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "awardable_type"
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["awardable_type", "awardable_id"], name: "index_award_emoji_on_awardable_type_and_awardable_id", using: :btree
+ t.index ["user_id", "name"], name: "index_award_emoji_on_user_id_and_name", using: :btree
end
- add_index "award_emoji", ["awardable_type", "awardable_id"], name: "index_award_emoji_on_awardable_type_and_awardable_id", using: :btree
- add_index "award_emoji", ["user_id", "name"], name: "index_award_emoji_on_user_id_and_name", using: :btree
-
create_table "badges", force: :cascade do |t|
t.string "link_url", null: false
t.string "image_url", null: false
@@ -204,21 +199,43 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "type", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
+ t.index ["group_id"], name: "index_badges_on_group_id", using: :btree
+ t.index ["project_id"], name: "index_badges_on_project_id", using: :btree
+ end
+
+ create_table "board_group_recent_visits", id: :bigserial, force: :cascade do |t|
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.integer "user_id"
+ t.integer "board_id"
+ t.integer "group_id"
+ t.index ["board_id"], name: "index_board_group_recent_visits_on_board_id", using: :btree
+ t.index ["group_id"], name: "index_board_group_recent_visits_on_group_id", using: :btree
+ t.index ["user_id", "group_id", "board_id"], name: "index_board_group_recent_visits_on_user_group_and_board", unique: true, using: :btree
+ t.index ["user_id"], name: "index_board_group_recent_visits_on_user_id", using: :btree
end
- add_index "badges", ["group_id"], name: "index_badges_on_group_id", using: :btree
- add_index "badges", ["project_id"], name: "index_badges_on_project_id", using: :btree
+ create_table "board_project_recent_visits", id: :bigserial, force: :cascade do |t|
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.integer "user_id"
+ t.integer "project_id"
+ t.integer "board_id"
+ t.index ["board_id"], name: "index_board_project_recent_visits_on_board_id", using: :btree
+ t.index ["project_id"], name: "index_board_project_recent_visits_on_project_id", using: :btree
+ t.index ["user_id", "project_id", "board_id"], name: "index_board_project_recent_visits_on_user_project_and_board", unique: true, using: :btree
+ t.index ["user_id"], name: "index_board_project_recent_visits_on_user_id", using: :btree
+ end
create_table "boards", force: :cascade do |t|
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "group_id"
+ t.index ["group_id"], name: "index_boards_on_group_id", using: :btree
+ t.index ["project_id"], name: "index_boards_on_project_id", using: :btree
end
- add_index "boards", ["group_id"], name: "index_boards_on_group_id", using: :btree
- add_index "boards", ["project_id"], name: "index_boards_on_project_id", using: :btree
-
create_table "broadcast_messages", force: :cascade do |t|
t.text "message", null: false
t.datetime "starts_at", null: false
@@ -229,10 +246,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "font"
t.text "message_html", null: false
t.integer "cached_markdown_version"
+ t.index ["starts_at", "ends_at", "id"], name: "index_broadcast_messages_on_starts_at_and_ends_at_and_id", using: :btree
end
- add_index "broadcast_messages", ["starts_at", "ends_at", "id"], name: "index_broadcast_messages_on_starts_at_and_ends_at_and_id", using: :btree
-
create_table "chat_names", force: :cascade do |t|
t.integer "user_id", null: false
t.integer "service_id", null: false
@@ -243,51 +259,46 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "last_used_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["service_id", "team_id", "chat_id"], name: "index_chat_names_on_service_id_and_team_id_and_chat_id", unique: true, using: :btree
+ t.index ["user_id", "service_id"], name: "index_chat_names_on_user_id_and_service_id", unique: true, using: :btree
end
- add_index "chat_names", ["service_id", "team_id", "chat_id"], name: "index_chat_names_on_service_id_and_team_id_and_chat_id", unique: true, using: :btree
- add_index "chat_names", ["user_id", "service_id"], name: "index_chat_names_on_user_id_and_service_id", unique: true, using: :btree
-
create_table "chat_teams", force: :cascade do |t|
t.integer "namespace_id", null: false
t.string "team_id"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree
end
- add_index "chat_teams", ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree
-
create_table "ci_build_trace_chunks", id: :bigserial, force: :cascade do |t|
t.integer "build_id", null: false
t.integer "chunk_index", null: false
t.integer "data_store", null: false
t.binary "raw_data"
+ t.index ["build_id", "chunk_index"], name: "index_ci_build_trace_chunks_on_build_id_and_chunk_index", unique: true, using: :btree
end
- add_index "ci_build_trace_chunks", ["build_id", "chunk_index"], name: "index_ci_build_trace_chunks_on_build_id_and_chunk_index", unique: true, using: :btree
-
create_table "ci_build_trace_section_names", force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
+ t.index ["project_id", "name"], name: "index_ci_build_trace_section_names_on_project_id_and_name", unique: true, using: :btree
end
- add_index "ci_build_trace_section_names", ["project_id", "name"], name: "index_ci_build_trace_section_names_on_project_id_and_name", unique: true, using: :btree
-
create_table "ci_build_trace_sections", force: :cascade do |t|
t.integer "project_id", null: false
t.datetime_with_timezone "date_start", null: false
t.datetime_with_timezone "date_end", null: false
- t.integer "byte_start", limit: 8, null: false
- t.integer "byte_end", limit: 8, null: false
+ t.bigint "byte_start", null: false
+ t.bigint "byte_end", null: false
t.integer "build_id", null: false
t.integer "section_name_id", null: false
+ t.index ["build_id", "section_name_id"], name: "index_ci_build_trace_sections_on_build_id_and_section_name_id", unique: true, using: :btree
+ t.index ["project_id"], name: "index_ci_build_trace_sections_on_project_id", using: :btree
+ t.index ["section_name_id"], name: "index_ci_build_trace_sections_on_section_name_id", using: :btree
end
- add_index "ci_build_trace_sections", ["build_id", "section_name_id"], name: "index_ci_build_trace_sections_on_build_id_and_section_name_id", unique: true, using: :btree
- add_index "ci_build_trace_sections", ["project_id"], name: "index_ci_build_trace_sections_on_project_id", using: :btree
- add_index "ci_build_trace_sections", ["section_name_id"], name: "index_ci_build_trace_sections_on_section_name_id", using: :btree
-
create_table "ci_builds", force: :cascade do |t|
t.string "status"
t.datetime "finished_at"
@@ -318,7 +329,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "erased_at"
t.datetime "artifacts_expire_at"
t.string "environment"
- t.integer "artifacts_size", limit: 8
+ t.bigint "artifacts_size"
t.string "when"
t.text "yaml_variables"
t.datetime "queued_at"
@@ -332,43 +343,43 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "artifacts_metadata_store"
t.boolean "protected"
t.integer "failure_reason"
+ t.datetime_with_timezone "scheduled_at"
+ t.index ["artifacts_expire_at"], name: "index_ci_builds_on_artifacts_expire_at", where: "(artifacts_file <> ''::text)", using: :btree
+ t.index ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id", using: :btree
+ t.index ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
+ t.index ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
+ t.index ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
+ t.index ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
+ t.index ["id"], name: "partial_index_ci_builds_on_id_with_legacy_artifacts", where: "(artifacts_file <> ''::text)", using: :btree
+ t.index ["project_id", "id"], name: "index_ci_builds_on_project_id_and_id", using: :btree
+ t.index ["protected"], name: "index_ci_builds_on_protected", using: :btree
+ t.index ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
+ t.index ["scheduled_at"], name: "partial_index_ci_builds_on_scheduled_at_with_scheduled_jobs", where: "((scheduled_at IS NOT NULL) AND ((type)::text = 'Ci::Build'::text) AND ((status)::text = 'scheduled'::text))", using: :btree
+ t.index ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)", using: :btree
+ t.index ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
+ t.index ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
+ t.index ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree
+ t.index ["updated_at"], name: "index_ci_builds_on_updated_at", using: :btree
+ t.index ["user_id"], name: "index_ci_builds_on_user_id", using: :btree
end
- add_index "ci_builds", ["artifacts_expire_at"], name: "index_ci_builds_on_artifacts_expire_at", where: "(artifacts_file <> ''::text)", using: :btree
- add_index "ci_builds", ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id", using: :btree
- add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
- add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
- add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
- add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
- add_index "ci_builds", ["project_id", "id"], name: "index_ci_builds_on_project_id_and_id", using: :btree
- add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree
- add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
- add_index "ci_builds", ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)", using: :btree
- add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
- add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
- add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree
- add_index "ci_builds", ["updated_at"], name: "index_ci_builds_on_updated_at", using: :btree
- add_index "ci_builds", ["user_id"], name: "index_ci_builds_on_user_id", using: :btree
-
create_table "ci_builds_metadata", force: :cascade do |t|
t.integer "build_id", null: false
t.integer "project_id", null: false
t.integer "timeout"
t.integer "timeout_source", default: 1, null: false
+ t.index ["build_id"], name: "index_ci_builds_metadata_on_build_id", unique: true, using: :btree
+ t.index ["project_id"], name: "index_ci_builds_metadata_on_project_id", using: :btree
end
- add_index "ci_builds_metadata", ["build_id"], name: "index_ci_builds_metadata_on_build_id", unique: true, using: :btree
- add_index "ci_builds_metadata", ["project_id"], name: "index_ci_builds_metadata_on_project_id", using: :btree
-
create_table "ci_builds_runner_session", id: :bigserial, force: :cascade do |t|
t.integer "build_id", null: false
t.string "url", null: false
t.string "certificate"
t.string "authorization"
+ t.index ["build_id"], name: "index_ci_builds_runner_session_on_build_id", unique: true, using: :btree
end
- add_index "ci_builds_runner_session", ["build_id"], name: "index_ci_builds_runner_session_on_build_id", unique: true, using: :btree
-
create_table "ci_group_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
@@ -379,29 +390,28 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "protected", default: false, null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
+ t.index ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
end
- add_index "ci_group_variables", ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
-
create_table "ci_job_artifacts", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "job_id", null: false
t.integer "file_type", null: false
t.integer "file_store"
- t.integer "size", limit: 8
+ t.bigint "size"
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.datetime_with_timezone "expire_at"
t.string "file"
t.binary "file_sha256"
t.integer "file_format", limit: 2
+ t.integer "file_location", limit: 2
+ t.index ["expire_at", "job_id"], name: "index_ci_job_artifacts_on_expire_at_and_job_id", using: :btree
+ t.index ["file_store"], name: "index_ci_job_artifacts_on_file_store", using: :btree
+ t.index ["job_id", "file_type"], name: "index_ci_job_artifacts_on_job_id_and_file_type", unique: true, using: :btree
+ t.index ["project_id"], name: "index_ci_job_artifacts_on_project_id", using: :btree
end
- add_index "ci_job_artifacts", ["expire_at", "job_id"], name: "index_ci_job_artifacts_on_expire_at_and_job_id", using: :btree
- add_index "ci_job_artifacts", ["file_store"], name: "index_ci_job_artifacts_on_file_store", using: :btree
- add_index "ci_job_artifacts", ["job_id", "file_type"], name: "index_ci_job_artifacts_on_job_id_and_file_type", unique: true, using: :btree
- add_index "ci_job_artifacts", ["project_id"], name: "index_ci_job_artifacts_on_project_id", using: :btree
-
create_table "ci_pipeline_schedule_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
@@ -411,10 +421,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "pipeline_schedule_id", null: false
t.datetime_with_timezone "created_at"
t.datetime_with_timezone "updated_at"
+ t.index ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree
end
- add_index "ci_pipeline_schedule_variables", ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree
-
create_table "ci_pipeline_schedules", force: :cascade do |t|
t.string "description"
t.string "ref"
@@ -426,11 +435,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "active", default: true
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["next_run_at", "active"], name: "index_ci_pipeline_schedules_on_next_run_at_and_active", using: :btree
+ t.index ["owner_id"], name: "index_ci_pipeline_schedules_on_owner_id", using: :btree
+ t.index ["project_id"], name: "index_ci_pipeline_schedules_on_project_id", using: :btree
end
- add_index "ci_pipeline_schedules", ["next_run_at", "active"], name: "index_ci_pipeline_schedules_on_next_run_at_and_active", using: :btree
- add_index "ci_pipeline_schedules", ["project_id"], name: "index_ci_pipeline_schedules_on_project_id", using: :btree
-
create_table "ci_pipeline_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
@@ -438,10 +447,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "encrypted_value_salt"
t.string "encrypted_value_iv"
t.integer "pipeline_id", null: false
+ t.index ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true, using: :btree
end
- add_index "ci_pipeline_variables", ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true, using: :btree
-
create_table "ci_pipelines", force: :cascade do |t|
t.string "ref"
t.string "sha"
@@ -465,35 +473,34 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "protected"
t.integer "failure_reason"
t.integer "iid"
+ t.index ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
+ t.index ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree
+ t.index ["project_id", "iid"], name: "index_ci_pipelines_on_project_id_and_iid", unique: true, where: "(iid IS NOT NULL)", using: :btree
+ t.index ["project_id", "ref", "status", "id"], name: "index_ci_pipelines_on_project_id_and_ref_and_status_and_id", using: :btree
+ t.index ["project_id", "sha"], name: "index_ci_pipelines_on_project_id_and_sha", using: :btree
+ t.index ["project_id", "source"], name: "index_ci_pipelines_on_project_id_and_source", using: :btree
+ t.index ["project_id", "status", "config_source"], name: "index_ci_pipelines_on_project_id_and_status_and_config_source", using: :btree
+ t.index ["project_id"], name: "index_ci_pipelines_on_project_id", using: :btree
+ t.index ["status"], name: "index_ci_pipelines_on_status", using: :btree
+ t.index ["user_id"], name: "index_ci_pipelines_on_user_id", using: :btree
end
- add_index "ci_pipelines", ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
- add_index "ci_pipelines", ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree
- add_index "ci_pipelines", ["project_id", "iid"], name: "index_ci_pipelines_on_project_id_and_iid", unique: true, where: "(iid IS NOT NULL)", using: :btree
- add_index "ci_pipelines", ["project_id", "ref", "status", "id"], name: "index_ci_pipelines_on_project_id_and_ref_and_status_and_id", using: :btree
- add_index "ci_pipelines", ["project_id", "sha"], name: "index_ci_pipelines_on_project_id_and_sha", using: :btree
- add_index "ci_pipelines", ["project_id"], name: "index_ci_pipelines_on_project_id", using: :btree
- add_index "ci_pipelines", ["status"], name: "index_ci_pipelines_on_status", using: :btree
- add_index "ci_pipelines", ["user_id"], name: "index_ci_pipelines_on_user_id", using: :btree
-
create_table "ci_runner_namespaces", force: :cascade do |t|
t.integer "runner_id"
t.integer "namespace_id"
+ t.index ["namespace_id"], name: "index_ci_runner_namespaces_on_namespace_id", using: :btree
+ t.index ["runner_id", "namespace_id"], name: "index_ci_runner_namespaces_on_runner_id_and_namespace_id", unique: true, using: :btree
end
- add_index "ci_runner_namespaces", ["namespace_id"], name: "index_ci_runner_namespaces_on_namespace_id", using: :btree
- add_index "ci_runner_namespaces", ["runner_id", "namespace_id"], name: "index_ci_runner_namespaces_on_runner_id_and_namespace_id", unique: true, using: :btree
-
create_table "ci_runner_projects", force: :cascade do |t|
t.integer "runner_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "project_id"
+ t.index ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree
+ t.index ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree
end
- add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree
- add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree
-
create_table "ci_runners", force: :cascade do |t|
t.string "token"
t.datetime "created_at"
@@ -513,14 +520,13 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "ip_address"
t.integer "maximum_timeout"
t.integer "runner_type", limit: 2, null: false
+ t.index ["contacted_at"], name: "index_ci_runners_on_contacted_at", using: :btree
+ t.index ["is_shared"], name: "index_ci_runners_on_is_shared", using: :btree
+ t.index ["locked"], name: "index_ci_runners_on_locked", using: :btree
+ t.index ["runner_type"], name: "index_ci_runners_on_runner_type", using: :btree
+ t.index ["token"], name: "index_ci_runners_on_token", using: :btree
end
- add_index "ci_runners", ["contacted_at"], name: "index_ci_runners_on_contacted_at", using: :btree
- add_index "ci_runners", ["is_shared"], name: "index_ci_runners_on_is_shared", using: :btree
- add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree
- add_index "ci_runners", ["runner_type"], name: "index_ci_runners_on_runner_type", using: :btree
- add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree
-
create_table "ci_stages", force: :cascade do |t|
t.integer "project_id"
t.integer "pipeline_id"
@@ -530,23 +536,22 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "status"
t.integer "lock_version"
t.integer "position"
+ t.index ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true, using: :btree
+ t.index ["pipeline_id", "position"], name: "index_ci_stages_on_pipeline_id_and_position", using: :btree
+ t.index ["pipeline_id"], name: "index_ci_stages_on_pipeline_id", using: :btree
+ t.index ["project_id"], name: "index_ci_stages_on_project_id", using: :btree
end
- add_index "ci_stages", ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true, using: :btree
- add_index "ci_stages", ["pipeline_id", "position"], name: "index_ci_stages_on_pipeline_id_and_position", using: :btree
- add_index "ci_stages", ["pipeline_id"], name: "index_ci_stages_on_pipeline_id", using: :btree
- add_index "ci_stages", ["project_id"], name: "index_ci_stages_on_project_id", using: :btree
-
create_table "ci_trigger_requests", force: :cascade do |t|
t.integer "trigger_id", null: false
t.text "variables"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "commit_id"
+ t.index ["commit_id"], name: "index_ci_trigger_requests_on_commit_id", using: :btree
+ t.index ["trigger_id"], name: "index_ci_trigger_requests_on_trigger_id", using: :btree
end
- add_index "ci_trigger_requests", ["commit_id"], name: "index_ci_trigger_requests_on_commit_id", using: :btree
-
create_table "ci_triggers", force: :cascade do |t|
t.string "token"
t.datetime "created_at"
@@ -555,10 +560,10 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "owner_id"
t.string "description"
t.string "ref"
+ t.index ["owner_id"], name: "index_ci_triggers_on_owner_id", using: :btree
+ t.index ["project_id"], name: "index_ci_triggers_on_project_id", using: :btree
end
- add_index "ci_triggers", ["project_id"], name: "index_ci_triggers_on_project_id", using: :btree
-
create_table "ci_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
@@ -568,9 +573,15 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "project_id", null: false
t.boolean "protected", default: false, null: false
t.string "environment_scope", default: "*", null: false
+ t.index ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree
end
- add_index "ci_variables", ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree
+ create_table "cluster_groups", force: :cascade do |t|
+ t.integer "cluster_id", null: false
+ t.integer "group_id", null: false
+ t.index ["cluster_id", "group_id"], name: "index_cluster_groups_on_cluster_id_and_group_id", unique: true, using: :btree
+ t.index ["group_id"], name: "index_cluster_groups_on_group_id", using: :btree
+ end
create_table "cluster_platforms_kubernetes", force: :cascade do |t|
t.integer "cluster_id", null: false
@@ -584,20 +595,19 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "encrypted_password_iv"
t.text "encrypted_token"
t.string "encrypted_token_iv"
+ t.integer "authorization_type", limit: 2
+ t.index ["cluster_id"], name: "index_cluster_platforms_kubernetes_on_cluster_id", unique: true, using: :btree
end
- add_index "cluster_platforms_kubernetes", ["cluster_id"], name: "index_cluster_platforms_kubernetes_on_cluster_id", unique: true, using: :btree
-
create_table "cluster_projects", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
+ t.index ["cluster_id"], name: "index_cluster_projects_on_cluster_id", using: :btree
+ t.index ["project_id"], name: "index_cluster_projects_on_project_id", using: :btree
end
- add_index "cluster_projects", ["cluster_id"], name: "index_cluster_projects_on_cluster_id", using: :btree
- add_index "cluster_projects", ["project_id"], name: "index_cluster_projects_on_project_id", using: :btree
-
create_table "cluster_providers_gcp", force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "status"
@@ -612,10 +622,10 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "endpoint"
t.text "encrypted_access_token"
t.string "encrypted_access_token_iv"
+ t.boolean "legacy_abac", default: true, null: false
+ t.index ["cluster_id"], name: "index_cluster_providers_gcp_on_cluster_id", unique: true, using: :btree
end
- add_index "cluster_providers_gcp", ["cluster_id"], name: "index_cluster_providers_gcp_on_cluster_id", unique: true, using: :btree
-
create_table "clusters", force: :cascade do |t|
t.integer "user_id"
t.integer "provider_type"
@@ -625,11 +635,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "enabled", default: true
t.string "name", null: false
t.string "environment_scope", default: "*", null: false
+ t.integer "cluster_type", limit: 2, default: 3, null: false
+ t.index ["enabled"], name: "index_clusters_on_enabled", using: :btree
+ t.index ["user_id"], name: "index_clusters_on_user_id", using: :btree
end
- add_index "clusters", ["enabled"], name: "index_clusters_on_enabled", using: :btree
- add_index "clusters", ["user_id"], name: "index_clusters_on_user_id", using: :btree
-
create_table "clusters_applications_helm", force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
@@ -640,6 +650,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.text "encrypted_ca_key"
t.text "encrypted_ca_key_iv"
t.text "ca_cert"
+ t.index ["cluster_id"], name: "index_clusters_applications_helm_on_cluster_id", unique: true, using: :btree
end
create_table "clusters_applications_ingress", force: :cascade do |t|
@@ -652,6 +663,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "cluster_ip"
t.text "status_reason"
t.string "external_ip"
+ t.index ["cluster_id"], name: "index_clusters_applications_ingress_on_cluster_id", unique: true, using: :btree
end
create_table "clusters_applications_jupyter", force: :cascade do |t|
@@ -663,6 +675,19 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.text "status_reason"
+ t.index ["cluster_id"], name: "index_clusters_applications_jupyter_on_cluster_id", unique: true, using: :btree
+ t.index ["oauth_application_id"], name: "index_clusters_applications_jupyter_on_oauth_application_id", using: :btree
+ end
+
+ create_table "clusters_applications_knative", force: :cascade do |t|
+ t.integer "cluster_id", null: false
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.integer "status", null: false
+ t.string "version", null: false
+ t.string "hostname"
+ t.text "status_reason"
+ t.index ["cluster_id"], name: "index_clusters_applications_knative_on_cluster_id", unique: true, using: :btree
end
create_table "clusters_applications_prometheus", force: :cascade do |t|
@@ -672,6 +697,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.text "status_reason"
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
+ t.index ["cluster_id"], name: "index_clusters_applications_prometheus_on_cluster_id", unique: true, using: :btree
end
create_table "clusters_applications_runners", force: :cascade do |t|
@@ -683,21 +709,35 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "version", null: false
t.text "status_reason"
t.boolean "privileged", default: true, null: false
+ t.index ["cluster_id"], name: "index_clusters_applications_runners_on_cluster_id", unique: true, using: :btree
+ t.index ["runner_id"], name: "index_clusters_applications_runners_on_runner_id", using: :btree
end
- add_index "clusters_applications_runners", ["cluster_id"], name: "index_clusters_applications_runners_on_cluster_id", unique: true, using: :btree
- add_index "clusters_applications_runners", ["runner_id"], name: "index_clusters_applications_runners_on_runner_id", using: :btree
+ create_table "clusters_kubernetes_namespaces", id: :bigserial, force: :cascade do |t|
+ t.integer "cluster_id", null: false
+ t.integer "project_id"
+ t.integer "cluster_project_id"
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.text "encrypted_service_account_token"
+ t.string "encrypted_service_account_token_iv"
+ t.string "namespace", null: false
+ t.string "service_account_name"
+ t.index ["cluster_id", "namespace"], name: "kubernetes_namespaces_cluster_and_namespace", unique: true, using: :btree
+ t.index ["cluster_id"], name: "index_clusters_kubernetes_namespaces_on_cluster_id", using: :btree
+ t.index ["cluster_project_id"], name: "index_clusters_kubernetes_namespaces_on_cluster_project_id", using: :btree
+ t.index ["project_id"], name: "index_clusters_kubernetes_namespaces_on_project_id", using: :btree
+ end
create_table "container_repositories", force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["project_id", "name"], name: "index_container_repositories_on_project_id_and_name", unique: true, using: :btree
+ t.index ["project_id"], name: "index_container_repositories_on_project_id", using: :btree
end
- add_index "container_repositories", ["project_id", "name"], name: "index_container_repositories_on_project_id_and_name", unique: true, using: :btree
- add_index "container_repositories", ["project_id"], name: "index_container_repositories_on_project_id", using: :btree
-
create_table "conversational_development_index_metrics", force: :cascade do |t|
t.float "leader_issues", null: false
t.float "instance_issues", null: false
@@ -739,10 +779,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "can_push", default: false, null: false
+ t.index ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
end
- add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
-
create_table "deploy_tokens", force: :cascade do |t|
t.boolean "revoked", default: false
t.boolean "read_repository", default: false, null: false
@@ -751,11 +790,10 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime_with_timezone "created_at", null: false
t.string "name", null: false
t.string "token", null: false
+ t.index ["token", "expires_at", "id"], name: "index_deploy_tokens_on_token_and_expires_at_and_id", where: "(revoked IS FALSE)", using: :btree
+ t.index ["token"], name: "index_deploy_tokens_on_token", unique: true, using: :btree
end
- add_index "deploy_tokens", ["token", "expires_at", "id"], name: "index_deploy_tokens_on_token_and_expires_at_and_id", where: "(revoked IS FALSE)", using: :btree
- add_index "deploy_tokens", ["token"], name: "index_deploy_tokens_on_token", unique: true, using: :btree
-
create_table "deployments", force: :cascade do |t|
t.integer "iid", null: false
t.integer "project_id", null: false
@@ -769,14 +807,19 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "created_at"
t.datetime "updated_at"
t.string "on_stop"
+ t.integer "status", limit: 2, null: false
+ t.datetime_with_timezone "finished_at"
+ t.index ["created_at"], name: "index_deployments_on_created_at", using: :btree
+ t.index ["deployable_type", "deployable_id"], name: "index_deployments_on_deployable_type_and_deployable_id", using: :btree
+ t.index ["environment_id", "id"], name: "index_deployments_on_environment_id_and_id", using: :btree
+ t.index ["environment_id", "iid", "project_id"], name: "index_deployments_on_environment_id_and_iid_and_project_id", using: :btree
+ t.index ["environment_id", "status"], name: "index_deployments_on_environment_id_and_status", using: :btree
+ t.index ["id"], name: "partial_index_deployments_for_legacy_successful_deployments", where: "((finished_at IS NULL) AND (status = 2))", using: :btree
+ t.index ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree
+ t.index ["project_id", "status", "created_at"], name: "index_deployments_on_project_id_and_status_and_created_at", using: :btree
+ t.index ["project_id", "status"], name: "index_deployments_on_project_id_and_status", using: :btree
end
- add_index "deployments", ["created_at"], name: "index_deployments_on_created_at", using: :btree
- add_index "deployments", ["deployable_type", "deployable_id"], name: "index_deployments_on_deployable_type_and_deployable_id", using: :btree
- add_index "deployments", ["environment_id", "id"], name: "index_deployments_on_environment_id_and_id", using: :btree
- add_index "deployments", ["environment_id", "iid", "project_id"], name: "index_deployments_on_environment_id_and_iid_and_project_id", using: :btree
- add_index "deployments", ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree
-
create_table "emails", force: :cascade do |t|
t.integer "user_id", null: false
t.string "email", null: false
@@ -785,12 +828,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "confirmation_token"
t.datetime_with_timezone "confirmed_at"
t.datetime_with_timezone "confirmation_sent_at"
+ t.index ["confirmation_token"], name: "index_emails_on_confirmation_token", unique: true, using: :btree
+ t.index ["email"], name: "index_emails_on_email", unique: true, using: :btree
+ t.index ["user_id"], name: "index_emails_on_user_id", using: :btree
end
- add_index "emails", ["confirmation_token"], name: "index_emails_on_confirmation_token", unique: true, using: :btree
- add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree
- add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree
-
create_table "environments", force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
@@ -800,11 +842,10 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "environment_type"
t.string "state", default: "available", null: false
t.string "slug", null: false
+ t.index ["project_id", "name"], name: "index_environments_on_project_id_and_name", unique: true, using: :btree
+ t.index ["project_id", "slug"], name: "index_environments_on_project_id_and_slug", unique: true, using: :btree
end
- add_index "environments", ["project_id", "name"], name: "index_environments_on_project_id_and_name", unique: true, using: :btree
- add_index "environments", ["project_id", "slug"], name: "index_environments_on_project_id_and_slug", unique: true, using: :btree
-
create_table "events", force: :cascade do |t|
t.integer "project_id"
t.integer "author_id", null: false
@@ -813,95 +854,60 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime_with_timezone "updated_at", null: false
t.integer "action", limit: 2, null: false
t.string "target_type"
+ t.index ["action"], name: "index_events_on_action", using: :btree
+ t.index ["author_id", "project_id"], name: "index_events_on_author_id_and_project_id", using: :btree
+ t.index ["project_id", "id"], name: "index_events_on_project_id_and_id", using: :btree
+ t.index ["target_type", "target_id"], name: "index_events_on_target_type_and_target_id", using: :btree
end
- add_index "events", ["action"], name: "index_events_on_action", using: :btree
- add_index "events", ["author_id", "project_id"], name: "index_events_on_author_id_and_project_id", using: :btree
- add_index "events", ["project_id", "id"], name: "index_events_on_project_id_and_id", using: :btree
- add_index "events", ["target_type", "target_id"], name: "index_events_on_target_type_and_target_id", using: :btree
-
create_table "feature_gates", force: :cascade do |t|
t.string "feature_key", null: false
t.string "key", null: false
t.string "value"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["feature_key", "key", "value"], name: "index_feature_gates_on_feature_key_and_key_and_value", unique: true, using: :btree
end
- add_index "feature_gates", ["feature_key", "key", "value"], name: "index_feature_gates_on_feature_key_and_key_and_value", unique: true, using: :btree
-
create_table "features", force: :cascade do |t|
t.string "key", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["key"], name: "index_features_on_key", unique: true, using: :btree
end
- add_index "features", ["key"], name: "index_features_on_key", unique: true, using: :btree
-
create_table "fork_network_members", force: :cascade do |t|
t.integer "fork_network_id", null: false
t.integer "project_id", null: false
t.integer "forked_from_project_id"
+ t.index ["fork_network_id"], name: "index_fork_network_members_on_fork_network_id", using: :btree
+ t.index ["forked_from_project_id"], name: "index_fork_network_members_on_forked_from_project_id", using: :btree
+ t.index ["project_id"], name: "index_fork_network_members_on_project_id", unique: true, using: :btree
end
- add_index "fork_network_members", ["fork_network_id"], name: "index_fork_network_members_on_fork_network_id", using: :btree
- add_index "fork_network_members", ["project_id"], name: "index_fork_network_members_on_project_id", unique: true, using: :btree
-
create_table "fork_networks", force: :cascade do |t|
t.integer "root_project_id"
t.string "deleted_root_project_name"
+ t.index ["root_project_id"], name: "index_fork_networks_on_root_project_id", unique: true, using: :btree
end
- add_index "fork_networks", ["root_project_id"], name: "index_fork_networks_on_root_project_id", unique: true, using: :btree
-
create_table "forked_project_links", force: :cascade do |t|
t.integer "forked_to_project_id", null: false
t.integer "forked_from_project_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
end
- add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
-
- create_table "gcp_clusters", force: :cascade do |t|
- t.integer "project_id", null: false
- t.integer "user_id"
- t.integer "service_id"
- t.integer "status"
- t.integer "gcp_cluster_size", null: false
- t.datetime_with_timezone "created_at", null: false
- t.datetime_with_timezone "updated_at", null: false
- t.boolean "enabled", default: true
- t.text "status_reason"
- t.string "project_namespace"
- t.string "endpoint"
- t.text "ca_cert"
- t.text "encrypted_kubernetes_token"
- t.string "encrypted_kubernetes_token_iv"
- t.string "username"
- t.text "encrypted_password"
- t.string "encrypted_password_iv"
- t.string "gcp_project_id", null: false
- t.string "gcp_cluster_zone", null: false
- t.string "gcp_cluster_name", null: false
- t.string "gcp_machine_type"
- t.string "gcp_operation_id"
- t.text "encrypted_gcp_token"
- t.string "encrypted_gcp_token_iv"
- end
-
- add_index "gcp_clusters", ["project_id"], name: "index_gcp_clusters_on_project_id", unique: true, using: :btree
-
create_table "gpg_key_subkeys", force: :cascade do |t|
t.integer "gpg_key_id", null: false
t.binary "keyid"
t.binary "fingerprint"
+ t.index ["fingerprint"], name: "index_gpg_key_subkeys_on_fingerprint", unique: true, using: :btree
+ t.index ["gpg_key_id"], name: "index_gpg_key_subkeys_on_gpg_key_id", using: :btree
+ t.index ["keyid"], name: "index_gpg_key_subkeys_on_keyid", unique: true, using: :btree
end
- add_index "gpg_key_subkeys", ["fingerprint"], name: "index_gpg_key_subkeys_on_fingerprint", unique: true, using: :btree
- add_index "gpg_key_subkeys", ["gpg_key_id"], name: "index_gpg_key_subkeys_on_gpg_key_id", using: :btree
- add_index "gpg_key_subkeys", ["keyid"], name: "index_gpg_key_subkeys_on_keyid", unique: true, using: :btree
-
create_table "gpg_keys", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
@@ -909,12 +915,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.binary "primary_keyid"
t.binary "fingerprint"
t.text "key"
+ t.index ["fingerprint"], name: "index_gpg_keys_on_fingerprint", unique: true, using: :btree
+ t.index ["primary_keyid"], name: "index_gpg_keys_on_primary_keyid", unique: true, using: :btree
+ t.index ["user_id"], name: "index_gpg_keys_on_user_id", using: :btree
end
- add_index "gpg_keys", ["fingerprint"], name: "index_gpg_keys_on_fingerprint", unique: true, using: :btree
- add_index "gpg_keys", ["primary_keyid"], name: "index_gpg_keys_on_primary_keyid", unique: true, using: :btree
- add_index "gpg_keys", ["user_id"], name: "index_gpg_keys_on_user_id", using: :btree
-
create_table "gpg_signatures", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
@@ -926,63 +931,59 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.text "gpg_key_user_email"
t.integer "verification_status", limit: 2, default: 0, null: false
t.integer "gpg_key_subkey_id"
+ t.index ["commit_sha"], name: "index_gpg_signatures_on_commit_sha", unique: true, using: :btree
+ t.index ["gpg_key_id"], name: "index_gpg_signatures_on_gpg_key_id", using: :btree
+ t.index ["gpg_key_primary_keyid"], name: "index_gpg_signatures_on_gpg_key_primary_keyid", using: :btree
+ t.index ["gpg_key_subkey_id"], name: "index_gpg_signatures_on_gpg_key_subkey_id", using: :btree
+ t.index ["project_id"], name: "index_gpg_signatures_on_project_id", using: :btree
end
- add_index "gpg_signatures", ["commit_sha"], name: "index_gpg_signatures_on_commit_sha", unique: true, using: :btree
- add_index "gpg_signatures", ["gpg_key_id"], name: "index_gpg_signatures_on_gpg_key_id", using: :btree
- add_index "gpg_signatures", ["gpg_key_primary_keyid"], name: "index_gpg_signatures_on_gpg_key_primary_keyid", using: :btree
- add_index "gpg_signatures", ["gpg_key_subkey_id"], name: "index_gpg_signatures_on_gpg_key_subkey_id", using: :btree
- add_index "gpg_signatures", ["project_id"], name: "index_gpg_signatures_on_project_id", using: :btree
-
create_table "group_custom_attributes", force: :cascade do |t|
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
t.integer "group_id", null: false
t.string "key", null: false
t.string "value", null: false
+ t.index ["group_id", "key"], name: "index_group_custom_attributes_on_group_id_and_key", unique: true, using: :btree
+ t.index ["key", "value"], name: "index_group_custom_attributes_on_key_and_value", using: :btree
end
- add_index "group_custom_attributes", ["group_id", "key"], name: "index_group_custom_attributes_on_group_id_and_key", unique: true, using: :btree
- add_index "group_custom_attributes", ["key", "value"], name: "index_group_custom_attributes_on_key_and_value", using: :btree
-
create_table "identities", force: :cascade do |t|
t.string "extern_uid"
t.string "provider"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["user_id"], name: "index_identities_on_user_id", using: :btree
end
- add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
-
create_table "import_export_uploads", force: :cascade do |t|
t.datetime_with_timezone "updated_at", null: false
t.integer "project_id"
t.text "import_file"
t.text "export_file"
+ t.index ["project_id"], name: "index_import_export_uploads_on_project_id", using: :btree
+ t.index ["updated_at"], name: "index_import_export_uploads_on_updated_at", using: :btree
end
- add_index "import_export_uploads", ["project_id"], name: "index_import_export_uploads_on_project_id", using: :btree
- add_index "import_export_uploads", ["updated_at"], name: "index_import_export_uploads_on_updated_at", using: :btree
-
create_table "internal_ids", id: :bigserial, force: :cascade do |t|
t.integer "project_id"
t.integer "usage", null: false
t.integer "last_value", null: false
t.integer "namespace_id"
+ t.index ["namespace_id"], name: "index_internal_ids_on_namespace_id", using: :btree
+ t.index ["project_id"], name: "index_internal_ids_on_project_id", using: :btree
+ t.index ["usage", "namespace_id"], name: "index_internal_ids_on_usage_and_namespace_id", unique: true, where: "(namespace_id IS NOT NULL)", using: :btree
+ t.index ["usage", "project_id"], name: "index_internal_ids_on_usage_and_project_id", unique: true, where: "(project_id IS NOT NULL)", using: :btree
end
- add_index "internal_ids", ["usage", "namespace_id"], name: "index_internal_ids_on_usage_and_namespace_id", unique: true, where: "(namespace_id IS NOT NULL)", using: :btree
- add_index "internal_ids", ["usage", "project_id"], name: "index_internal_ids_on_usage_and_project_id", unique: true, where: "(project_id IS NOT NULL)", using: :btree
-
create_table "issue_assignees", id: false, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "issue_id", null: false
+ t.index ["issue_id", "user_id"], name: "index_issue_assignees_on_issue_id_and_user_id", unique: true, using: :btree
+ t.index ["user_id"], name: "index_issue_assignees_on_user_id", using: :btree
end
- add_index "issue_assignees", ["issue_id", "user_id"], name: "index_issue_assignees_on_issue_id_and_user_id", unique: true, using: :btree
- add_index "issue_assignees", ["user_id"], name: "index_issue_assignees_on_user_id", using: :btree
-
create_table "issue_metrics", force: :cascade do |t|
t.integer "issue_id", null: false
t.datetime "first_mentioned_in_commit_at"
@@ -990,10 +991,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "first_added_to_board_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["issue_id"], name: "index_issue_metrics", using: :btree
end
- add_index "issue_metrics", ["issue_id"], name: "index_issue_metrics", using: :btree
-
create_table "issues", force: :cascade do |t|
t.string "title"
t.integer "author_id"
@@ -1019,23 +1019,23 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "discussion_locked"
t.datetime_with_timezone "closed_at"
t.integer "closed_by_id"
+ t.index ["author_id"], name: "index_issues_on_author_id", using: :btree
+ t.index ["closed_by_id"], name: "index_issues_on_closed_by_id", using: :btree
+ t.index ["confidential"], name: "index_issues_on_confidential", using: :btree
+ t.index ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
+ t.index ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
+ t.index ["moved_to_id"], name: "index_issues_on_moved_to_id", where: "(moved_to_id IS NOT NULL)", using: :btree
+ t.index ["project_id", "created_at", "id", "state"], name: "index_issues_on_project_id_and_created_at_and_id_and_state", using: :btree
+ t.index ["project_id", "due_date", "id", "state"], name: "idx_issues_on_project_id_and_due_date_and_id_and_state_partial", where: "(due_date IS NOT NULL)", using: :btree
+ t.index ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree
+ t.index ["project_id", "updated_at", "id", "state"], name: "index_issues_on_project_id_and_updated_at_and_id_and_state", using: :btree
+ t.index ["relative_position"], name: "index_issues_on_relative_position", using: :btree
+ t.index ["state"], name: "index_issues_on_state", using: :btree
+ t.index ["title"], name: "index_issues_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
+ t.index ["updated_at"], name: "index_issues_on_updated_at", using: :btree
+ t.index ["updated_by_id"], name: "index_issues_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
end
- add_index "issues", ["author_id"], name: "index_issues_on_author_id", using: :btree
- add_index "issues", ["confidential"], name: "index_issues_on_confidential", using: :btree
- add_index "issues", ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
- add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
- add_index "issues", ["moved_to_id"], name: "index_issues_on_moved_to_id", where: "(moved_to_id IS NOT NULL)", using: :btree
- add_index "issues", ["project_id", "created_at", "id", "state"], name: "index_issues_on_project_id_and_created_at_and_id_and_state", using: :btree
- add_index "issues", ["project_id", "due_date", "id", "state"], name: "idx_issues_on_project_id_and_due_date_and_id_and_state_partial", where: "(due_date IS NOT NULL)", using: :btree
- add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree
- add_index "issues", ["project_id", "updated_at", "id", "state"], name: "index_issues_on_project_id_and_updated_at_and_id_and_state", using: :btree
- add_index "issues", ["relative_position"], name: "index_issues_on_relative_position", using: :btree
- add_index "issues", ["state"], name: "index_issues_on_state", using: :btree
- add_index "issues", ["title"], name: "index_issues_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
- add_index "issues", ["updated_at"], name: "index_issues_on_updated_at", using: :btree
- add_index "issues", ["updated_by_id"], name: "index_issues_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
-
create_table "keys", force: :cascade do |t|
t.integer "user_id"
t.datetime "created_at"
@@ -1046,33 +1046,31 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "fingerprint"
t.boolean "public", default: false, null: false
t.datetime "last_used_at"
+ t.index ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree
+ t.index ["user_id"], name: "index_keys_on_user_id", using: :btree
end
- add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree
- add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree
-
create_table "label_links", force: :cascade do |t|
t.integer "label_id"
t.integer "target_id"
t.string "target_type"
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["label_id"], name: "index_label_links_on_label_id", using: :btree
+ t.index ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree
end
- add_index "label_links", ["label_id"], name: "index_label_links_on_label_id", using: :btree
- add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree
-
create_table "label_priorities", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "label_id", null: false
t.integer "priority", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["label_id"], name: "index_label_priorities_on_label_id", using: :btree
+ t.index ["priority"], name: "index_label_priorities_on_priority", using: :btree
+ t.index ["project_id", "label_id"], name: "index_label_priorities_on_project_id_and_label_id", unique: true, using: :btree
end
- add_index "label_priorities", ["priority"], name: "index_label_priorities_on_priority", using: :btree
- add_index "label_priorities", ["project_id", "label_id"], name: "index_label_priorities_on_project_id_and_label_id", unique: true, using: :btree
-
create_table "labels", force: :cascade do |t|
t.string "title"
t.string "color"
@@ -1085,44 +1083,41 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "type"
t.integer "group_id"
t.integer "cached_markdown_version"
+ t.index ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree
+ t.index ["project_id"], name: "index_labels_on_project_id", using: :btree
+ t.index ["template"], name: "index_labels_on_template", where: "template", using: :btree
+ t.index ["title"], name: "index_labels_on_title", using: :btree
+ t.index ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree
end
- add_index "labels", ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree
- add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
- add_index "labels", ["template"], name: "index_labels_on_template", where: "template", using: :btree
- add_index "labels", ["title"], name: "index_labels_on_title", using: :btree
- add_index "labels", ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree
-
create_table "lfs_file_locks", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "user_id", null: false
t.datetime "created_at", null: false
t.string "path", limit: 511
+ t.index ["project_id", "path"], name: "index_lfs_file_locks_on_project_id_and_path", unique: true, using: :btree
+ t.index ["user_id"], name: "index_lfs_file_locks_on_user_id", using: :btree
end
- add_index "lfs_file_locks", ["project_id", "path"], name: "index_lfs_file_locks_on_project_id_and_path", unique: true, using: :btree
- add_index "lfs_file_locks", ["user_id"], name: "index_lfs_file_locks_on_user_id", using: :btree
-
create_table "lfs_objects", force: :cascade do |t|
t.string "oid", null: false
- t.integer "size", limit: 8, null: false
+ t.bigint "size", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "file"
t.integer "file_store"
+ t.index ["file_store"], name: "index_lfs_objects_on_file_store", using: :btree
+ t.index ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree
end
- add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree
-
create_table "lfs_objects_projects", force: :cascade do |t|
t.integer "lfs_object_id", null: false
t.integer "project_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree
end
- add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree
-
create_table "lists", force: :cascade do |t|
t.integer "board_id", null: false
t.integer "label_id"
@@ -1130,11 +1125,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "position"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["board_id", "label_id"], name: "index_lists_on_board_id_and_label_id", unique: true, using: :btree
+ t.index ["label_id"], name: "index_lists_on_label_id", using: :btree
+ t.index ["list_type"], name: "index_lists_on_list_type", using: :btree
end
- add_index "lists", ["board_id", "label_id"], name: "index_lists_on_board_id_and_label_id", unique: true, using: :btree
- add_index "lists", ["label_id"], name: "index_lists_on_label_id", using: :btree
-
create_table "members", force: :cascade do |t|
t.integer "access_level", null: false
t.integer "source_id", null: false
@@ -1150,14 +1145,13 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "invite_accepted_at"
t.datetime "requested_at"
t.date "expires_at"
+ t.index ["access_level"], name: "index_members_on_access_level", using: :btree
+ t.index ["invite_token"], name: "index_members_on_invite_token", unique: true, using: :btree
+ t.index ["requested_at"], name: "index_members_on_requested_at", using: :btree
+ t.index ["source_id", "source_type"], name: "index_members_on_source_id_and_source_type", using: :btree
+ t.index ["user_id"], name: "index_members_on_user_id", using: :btree
end
- add_index "members", ["access_level"], name: "index_members_on_access_level", using: :btree
- add_index "members", ["invite_token"], name: "index_members_on_invite_token", unique: true, using: :btree
- add_index "members", ["requested_at"], name: "index_members_on_requested_at", using: :btree
- add_index "members", ["source_id", "source_type"], name: "index_members_on_source_id_and_source_type", using: :btree
- add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree
-
create_table "merge_request_diff_commits", id: false, force: :cascade do |t|
t.datetime_with_timezone "authored_date"
t.datetime_with_timezone "committed_date"
@@ -1169,11 +1163,10 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.text "committer_name"
t.text "committer_email"
t.text "message"
+ t.index ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_commits_on_mr_diff_id_and_order", unique: true, using: :btree
+ t.index ["sha"], name: "index_merge_request_diff_commits_on_sha", using: :btree
end
- add_index "merge_request_diff_commits", ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_commits_on_mr_diff_id_and_order", unique: true, using: :btree
- add_index "merge_request_diff_commits", ["sha"], name: "index_merge_request_diff_commits_on_sha", using: :btree
-
create_table "merge_request_diff_files", id: false, force: :cascade do |t|
t.integer "merge_request_diff_id", null: false
t.integer "relative_order", null: false
@@ -1187,10 +1180,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.text "old_path", null: false
t.text "diff", null: false
t.boolean "binary"
+ t.index ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_files_on_mr_diff_id_and_order", unique: true, using: :btree
end
- add_index "merge_request_diff_files", ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_files_on_mr_diff_id_and_order", unique: true, using: :btree
-
create_table "merge_request_diffs", force: :cascade do |t|
t.string "state"
t.integer "merge_request_id", null: false
@@ -1201,10 +1193,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "head_commit_sha"
t.string "start_commit_sha"
t.integer "commits_count"
+ t.index ["merge_request_id", "id"], name: "index_merge_request_diffs_on_merge_request_id_and_id", using: :btree
end
- add_index "merge_request_diffs", ["merge_request_id", "id"], name: "index_merge_request_diffs_on_merge_request_id_and_id", using: :btree
-
create_table "merge_request_metrics", force: :cascade do |t|
t.integer "merge_request_id", null: false
t.datetime "latest_build_started_at"
@@ -1217,12 +1208,13 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "merged_by_id"
t.integer "latest_closed_by_id"
t.datetime_with_timezone "latest_closed_at"
+ t.index ["first_deployed_to_production_at"], name: "index_merge_request_metrics_on_first_deployed_to_production_at", using: :btree
+ t.index ["latest_closed_by_id"], name: "index_merge_request_metrics_on_latest_closed_by_id", using: :btree
+ t.index ["merge_request_id"], name: "index_merge_request_metrics", using: :btree
+ t.index ["merged_by_id"], name: "index_merge_request_metrics_on_merged_by_id", using: :btree
+ t.index ["pipeline_id"], name: "index_merge_request_metrics_on_pipeline_id", using: :btree
end
- add_index "merge_request_metrics", ["first_deployed_to_production_at"], name: "index_merge_request_metrics_on_first_deployed_to_production_at", using: :btree
- add_index "merge_request_metrics", ["merge_request_id"], name: "index_merge_request_metrics", using: :btree
- add_index "merge_request_metrics", ["pipeline_id"], name: "index_merge_request_metrics_on_pipeline_id", using: :btree
-
create_table "merge_requests", force: :cascade do |t|
t.string "target_branch", null: false
t.string "source_branch", null: false
@@ -1259,37 +1251,36 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "rebase_commit_sha"
t.boolean "squash", default: false, null: false
t.boolean "allow_maintainer_to_push"
+ t.index ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
+ t.index ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
+ t.index ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
+ t.index ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
+ t.index ["head_pipeline_id"], name: "index_merge_requests_on_head_pipeline_id", using: :btree
+ t.index ["id", "merge_jid"], name: "index_merge_requests_on_id_and_merge_jid", where: "((merge_jid IS NOT NULL) AND ((state)::text = 'locked'::text))", using: :btree
+ t.index ["latest_merge_request_diff_id"], name: "index_merge_requests_on_latest_merge_request_diff_id", using: :btree
+ t.index ["merge_user_id"], name: "index_merge_requests_on_merge_user_id", where: "(merge_user_id IS NOT NULL)", using: :btree
+ t.index ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
+ t.index ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
+ t.index ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_and_branch_state_opened", where: "((state)::text = 'opened'::text)", using: :btree
+ t.index ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_id_and_source_branch", using: :btree
+ t.index ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
+ t.index ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree
+ t.index ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid_opened", where: "((state)::text = 'opened'::text)", using: :btree
+ t.index ["target_project_id", "merge_commit_sha", "id"], name: "index_merge_requests_on_tp_id_and_merge_commit_sha_and_id", using: :btree
+ t.index ["title"], name: "index_merge_requests_on_title", using: :btree
+ t.index ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
+ t.index ["updated_by_id"], name: "index_merge_requests_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
end
- add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
- add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
- add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
- add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
- add_index "merge_requests", ["head_pipeline_id"], name: "index_merge_requests_on_head_pipeline_id", using: :btree
- add_index "merge_requests", ["latest_merge_request_diff_id"], name: "index_merge_requests_on_latest_merge_request_diff_id", using: :btree
- add_index "merge_requests", ["merge_user_id"], name: "index_merge_requests_on_merge_user_id", where: "(merge_user_id IS NOT NULL)", using: :btree
- add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
- add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
- add_index "merge_requests", ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_and_branch_state_opened", where: "((state)::text = 'opened'::text)", using: :btree
- add_index "merge_requests", ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_id_and_source_branch", using: :btree
- add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
- add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree
- add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid_opened", where: "((state)::text = 'opened'::text)", using: :btree
- add_index "merge_requests", ["target_project_id", "merge_commit_sha", "id"], name: "index_merge_requests_on_tp_id_and_merge_commit_sha_and_id", using: :btree
- add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
- add_index "merge_requests", ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
- add_index "merge_requests", ["updated_by_id"], name: "index_merge_requests_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
-
create_table "merge_requests_closing_issues", force: :cascade do |t|
t.integer "merge_request_id", null: false
t.integer "issue_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["issue_id"], name: "index_merge_requests_closing_issues_on_issue_id", using: :btree
+ t.index ["merge_request_id"], name: "index_merge_requests_closing_issues_on_merge_request_id", using: :btree
end
- add_index "merge_requests_closing_issues", ["issue_id"], name: "index_merge_requests_closing_issues_on_issue_id", using: :btree
- add_index "merge_requests_closing_issues", ["merge_request_id"], name: "index_merge_requests_closing_issues_on_merge_request_id", using: :btree
-
create_table "milestones", force: :cascade do |t|
t.string "title", null: false
t.integer "project_id"
@@ -1304,15 +1295,14 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.date "start_date"
t.integer "cached_markdown_version"
t.integer "group_id"
+ t.index ["description"], name: "index_milestones_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
+ t.index ["due_date"], name: "index_milestones_on_due_date", using: :btree
+ t.index ["group_id"], name: "index_milestones_on_group_id", using: :btree
+ t.index ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree
+ t.index ["title"], name: "index_milestones_on_title", using: :btree
+ t.index ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
end
- add_index "milestones", ["description"], name: "index_milestones_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
- add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
- add_index "milestones", ["group_id"], name: "index_milestones_on_group_id", using: :btree
- add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree
- add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree
- add_index "milestones", ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
-
create_table "namespaces", force: :cascade do |t|
t.string "name", null: false
t.string "path", null: false
@@ -1332,19 +1322,18 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "two_factor_grace_period", default: 48, null: false
t.integer "cached_markdown_version"
t.string "runners_token"
+ t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
+ t.index ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree
+ t.index ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
+ t.index ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
+ t.index ["parent_id", "id"], name: "index_namespaces_on_parent_id_and_id", unique: true, using: :btree
+ t.index ["path"], name: "index_namespaces_on_path", using: :btree
+ t.index ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
+ t.index ["require_two_factor_authentication"], name: "index_namespaces_on_require_two_factor_authentication", using: :btree
+ t.index ["runners_token"], name: "index_namespaces_on_runners_token", unique: true, using: :btree
+ t.index ["type"], name: "index_namespaces_on_type", using: :btree
end
- add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree
- add_index "namespaces", ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree
- add_index "namespaces", ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
- add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
- add_index "namespaces", ["parent_id", "id"], name: "index_namespaces_on_parent_id_and_id", unique: true, using: :btree
- add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree
- add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
- add_index "namespaces", ["require_two_factor_authentication"], name: "index_namespaces_on_require_two_factor_authentication", using: :btree
- add_index "namespaces", ["runners_token"], name: "index_namespaces_on_runners_token", unique: true, using: :btree
- add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
-
create_table "note_diff_files", force: :cascade do |t|
t.integer "diff_note_id", null: false
t.text "diff", null: false
@@ -1355,10 +1344,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "b_mode", null: false
t.text "new_path", null: false
t.text "old_path", null: false
+ t.index ["diff_note_id"], name: "index_note_diff_files_on_diff_note_id", unique: true, using: :btree
end
- add_index "note_diff_files", ["diff_note_id"], name: "index_note_diff_files_on_diff_note_id", unique: true, using: :btree
-
create_table "notes", force: :cascade do |t|
t.text "note"
t.string "noteable_type"
@@ -1383,19 +1371,18 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "cached_markdown_version"
t.text "change_position"
t.boolean "resolved_by_push"
+ t.index ["author_id"], name: "index_notes_on_author_id", using: :btree
+ t.index ["commit_id"], name: "index_notes_on_commit_id", using: :btree
+ t.index ["created_at"], name: "index_notes_on_created_at", using: :btree
+ t.index ["discussion_id"], name: "index_notes_on_discussion_id", using: :btree
+ t.index ["line_code"], name: "index_notes_on_line_code", using: :btree
+ t.index ["note"], name: "index_notes_on_note_trigram", using: :gin, opclasses: {"note"=>"gin_trgm_ops"}
+ t.index ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
+ t.index ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
+ t.index ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
+ t.index ["updated_at"], name: "index_notes_on_updated_at", using: :btree
end
- add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
- add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
- add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree
- add_index "notes", ["discussion_id"], name: "index_notes_on_discussion_id", using: :btree
- add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree
- add_index "notes", ["note"], name: "index_notes_on_note_trigram", using: :gin, opclasses: {"note"=>"gin_trgm_ops"}
- add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
- add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
- add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
- add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
-
create_table "notification_settings", force: :cascade do |t|
t.integer "user_id", null: false
t.integer "source_id"
@@ -1417,12 +1404,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "success_pipeline"
t.boolean "push_to_merge_request"
t.boolean "issue_due"
+ t.index ["source_id", "source_type"], name: "index_notification_settings_on_source_id_and_source_type", using: :btree
+ t.index ["user_id", "source_id", "source_type"], name: "index_notifications_on_user_id_and_source_id_and_source_type", unique: true, using: :btree
+ t.index ["user_id"], name: "index_notification_settings_on_user_id", using: :btree
end
- add_index "notification_settings", ["source_id", "source_type"], name: "index_notification_settings_on_source_id_and_source_type", using: :btree
- add_index "notification_settings", ["user_id", "source_id", "source_type"], name: "index_notifications_on_user_id_and_source_id_and_source_type", unique: true, using: :btree
- add_index "notification_settings", ["user_id"], name: "index_notification_settings_on_user_id", using: :btree
-
create_table "oauth_access_grants", force: :cascade do |t|
t.integer "resource_owner_id", null: false
t.integer "application_id", null: false
@@ -1432,10 +1418,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "created_at", null: false
t.datetime "revoked_at"
t.string "scopes"
+ t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
end
- add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
-
create_table "oauth_access_tokens", force: :cascade do |t|
t.integer "resource_owner_id"
t.integer "application_id"
@@ -1445,12 +1430,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "revoked_at"
t.datetime "created_at", null: false
t.string "scopes"
+ t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
+ t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
+ t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
end
- add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
- add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
- add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
-
create_table "oauth_applications", force: :cascade do |t|
t.string "name", null: false
t.string "uid", null: false
@@ -1462,14 +1446,14 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "owner_id"
t.string "owner_type"
t.boolean "trusted", default: false, null: false
+ t.index ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
+ t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
end
- add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
- add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
-
create_table "oauth_openid_requests", force: :cascade do |t|
t.integer "access_grant_id", null: false
t.string "nonce", null: false
+ t.index ["access_grant_id"], name: "index_oauth_openid_requests_on_access_grant_id", using: :btree
end
create_table "pages_domains", force: :cascade do |t|
@@ -1482,17 +1466,16 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime_with_timezone "verified_at"
t.string "verification_code", null: false
t.datetime_with_timezone "enabled_until"
+ t.index ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
+ t.index ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until", using: :btree
+ t.index ["project_id"], name: "index_pages_domains_on_project_id", using: :btree
+ t.index ["verified_at", "enabled_until"], name: "index_pages_domains_on_verified_at_and_enabled_until", using: :btree
+ t.index ["verified_at"], name: "index_pages_domains_on_verified_at", using: :btree
end
- add_index "pages_domains", ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
- add_index "pages_domains", ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until", using: :btree
- add_index "pages_domains", ["project_id"], name: "index_pages_domains_on_project_id", using: :btree
- add_index "pages_domains", ["verified_at", "enabled_until"], name: "index_pages_domains_on_verified_at_and_enabled_until", using: :btree
- add_index "pages_domains", ["verified_at"], name: "index_pages_domains_on_verified_at", using: :btree
-
create_table "personal_access_tokens", force: :cascade do |t|
t.integer "user_id", null: false
- t.string "token", null: false
+ t.string "token"
t.string "name", null: false
t.boolean "revoked", default: false
t.date "expires_at"
@@ -1500,28 +1483,27 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "updated_at", null: false
t.string "scopes", default: "--- []\n", null: false
t.boolean "impersonation", default: false, null: false
+ t.string "token_digest"
+ t.index ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
+ t.index ["token_digest"], name: "index_personal_access_tokens_on_token_digest", unique: true, using: :btree
+ t.index ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree
end
- add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
- add_index "personal_access_tokens", ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree
-
create_table "programming_languages", force: :cascade do |t|
t.string "name", null: false
t.string "color", null: false
t.datetime_with_timezone "created_at", null: false
+ t.index ["name"], name: "index_programming_languages_on_name", unique: true, using: :btree
end
- add_index "programming_languages", ["name"], name: "index_programming_languages_on_name", unique: true, using: :btree
-
create_table "project_authorizations", id: false, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "project_id", null: false
t.integer "access_level", null: false
+ t.index ["project_id"], name: "index_project_authorizations_on_project_id", using: :btree
+ t.index ["user_id", "project_id", "access_level"], name: "index_project_authorizations_on_user_id_project_id_access_level", unique: true, using: :btree
end
- add_index "project_authorizations", ["project_id"], name: "index_project_authorizations_on_project_id", using: :btree
- add_index "project_authorizations", ["user_id", "project_id", "access_level"], name: "index_project_authorizations_on_user_id_project_id_access_level", unique: true, using: :btree
-
create_table "project_auto_devops", force: :cascade do |t|
t.integer "project_id", null: false
t.datetime_with_timezone "created_at", null: false
@@ -1529,50 +1511,47 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "enabled"
t.string "domain"
t.integer "deploy_strategy", default: 0, null: false
+ t.index ["project_id"], name: "index_project_auto_devops_on_project_id", unique: true, using: :btree
end
- add_index "project_auto_devops", ["project_id"], name: "index_project_auto_devops_on_project_id", unique: true, using: :btree
-
create_table "project_ci_cd_settings", force: :cascade do |t|
t.integer "project_id", null: false
t.boolean "group_runners_enabled", default: true, null: false
+ t.index ["project_id"], name: "index_project_ci_cd_settings_on_project_id", unique: true, using: :btree
end
- add_index "project_ci_cd_settings", ["project_id"], name: "index_project_ci_cd_settings_on_project_id", unique: true, using: :btree
-
create_table "project_custom_attributes", force: :cascade do |t|
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
t.integer "project_id", null: false
t.string "key", null: false
t.string "value", null: false
+ t.index ["key", "value"], name: "index_project_custom_attributes_on_key_and_value", using: :btree
+ t.index ["project_id", "key"], name: "index_project_custom_attributes_on_project_id_and_key", unique: true, using: :btree
end
- add_index "project_custom_attributes", ["key", "value"], name: "index_project_custom_attributes_on_key_and_value", using: :btree
- add_index "project_custom_attributes", ["project_id", "key"], name: "index_project_custom_attributes_on_project_id_and_key", unique: true, using: :btree
-
create_table "project_deploy_tokens", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "deploy_token_id", null: false
t.datetime_with_timezone "created_at", null: false
+ t.index ["deploy_token_id"], name: "index_project_deploy_tokens_on_deploy_token_id", using: :btree
+ t.index ["project_id", "deploy_token_id"], name: "index_project_deploy_tokens_on_project_id_and_deploy_token_id", unique: true, using: :btree
end
- add_index "project_deploy_tokens", ["project_id", "deploy_token_id"], name: "index_project_deploy_tokens_on_project_id_and_deploy_token_id", unique: true, using: :btree
-
create_table "project_features", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "merge_requests_access_level"
t.integer "issues_access_level"
t.integer "wiki_access_level"
- t.integer "snippets_access_level"
+ t.integer "snippets_access_level", default: 20, null: false
t.integer "builds_access_level"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "repository_access_level", default: 20, null: false
+ t.integer "pages_access_level", default: 20, null: false
+ t.index ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
end
- add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
-
create_table "project_group_links", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "group_id", null: false
@@ -1580,45 +1559,41 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "updated_at"
t.integer "group_access", default: 30, null: false
t.date "expires_at"
+ t.index ["group_id"], name: "index_project_group_links_on_group_id", using: :btree
+ t.index ["project_id"], name: "index_project_group_links_on_project_id", using: :btree
end
- add_index "project_group_links", ["group_id"], name: "index_project_group_links_on_group_id", using: :btree
- add_index "project_group_links", ["project_id"], name: "index_project_group_links_on_project_id", using: :btree
-
create_table "project_import_data", force: :cascade do |t|
t.integer "project_id"
t.text "data"
t.text "encrypted_credentials"
t.string "encrypted_credentials_iv"
t.string "encrypted_credentials_salt"
+ t.index ["project_id"], name: "index_project_import_data_on_project_id", using: :btree
end
- add_index "project_import_data", ["project_id"], name: "index_project_import_data_on_project_id", using: :btree
-
create_table "project_mirror_data", force: :cascade do |t|
t.integer "project_id", null: false
t.string "status"
t.string "jid"
t.text "last_error"
+ t.index ["jid"], name: "index_project_mirror_data_on_jid", using: :btree
+ t.index ["project_id"], name: "index_project_mirror_data_on_project_id", unique: true, using: :btree
+ t.index ["status"], name: "index_project_mirror_data_on_status", using: :btree
end
- add_index "project_mirror_data", ["jid"], name: "index_project_mirror_data_on_jid", using: :btree
- add_index "project_mirror_data", ["project_id"], name: "index_project_mirror_data_on_project_id", unique: true, using: :btree
- add_index "project_mirror_data", ["status"], name: "index_project_mirror_data_on_status", using: :btree
-
create_table "project_statistics", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "namespace_id", null: false
- t.integer "commit_count", limit: 8, default: 0, null: false
- t.integer "storage_size", limit: 8, default: 0, null: false
- t.integer "repository_size", limit: 8, default: 0, null: false
- t.integer "lfs_objects_size", limit: 8, default: 0, null: false
- t.integer "build_artifacts_size", limit: 8, default: 0, null: false
+ t.bigint "commit_count", default: 0, null: false
+ t.bigint "storage_size", default: 0, null: false
+ t.bigint "repository_size", default: 0, null: false
+ t.bigint "lfs_objects_size", default: 0, null: false
+ t.bigint "build_artifacts_size", default: 0, null: false
+ t.index ["namespace_id"], name: "index_project_statistics_on_namespace_id", using: :btree
+ t.index ["project_id"], name: "index_project_statistics_on_project_id", unique: true, using: :btree
end
- add_index "project_statistics", ["namespace_id"], name: "index_project_statistics_on_namespace_id", using: :btree
- add_index "project_statistics", ["project_id"], name: "index_project_statistics_on_project_id", unique: true, using: :btree
-
create_table "projects", force: :cascade do |t|
t.string "name"
t.string "path"
@@ -1671,55 +1646,71 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "jobs_cache_index"
t.boolean "pages_https_only", default: true
t.boolean "remote_mirror_available_overridden"
+ t.bigint "pool_repository_id"
+ t.index ["ci_id"], name: "index_projects_on_ci_id", using: :btree
+ t.index ["created_at"], name: "index_projects_on_created_at", using: :btree
+ t.index ["creator_id"], name: "index_projects_on_creator_id", using: :btree
+ t.index ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
+ t.index ["id"], name: "index_projects_on_id_partial_for_visibility", unique: true, where: "(visibility_level = ANY (ARRAY[10, 20]))", using: :btree
+ t.index ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
+ t.index ["last_repository_check_at"], name: "index_projects_on_last_repository_check_at", where: "(last_repository_check_at IS NOT NULL)", using: :btree
+ t.index ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree
+ t.index ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at", using: :btree
+ t.index ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
+ t.index ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
+ t.index ["path"], name: "index_projects_on_path", using: :btree
+ t.index ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
+ t.index ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree
+ t.index ["pool_repository_id"], name: "index_projects_on_pool_repository_id", where: "(pool_repository_id IS NOT NULL)", using: :btree
+ t.index ["repository_storage", "created_at"], name: "idx_project_repository_check_partial", where: "(last_repository_check_at IS NULL)", using: :btree
+ t.index ["repository_storage"], name: "index_projects_on_repository_storage", using: :btree
+ t.index ["runners_token"], name: "index_projects_on_runners_token", using: :btree
+ t.index ["star_count"], name: "index_projects_on_star_count", using: :btree
+ t.index ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
+ end
+
+ create_table "prometheus_metrics", force: :cascade do |t|
+ t.integer "project_id"
+ t.string "title", null: false
+ t.string "query", null: false
+ t.string "y_label"
+ t.string "unit"
+ t.string "legend"
+ t.integer "group", null: false
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.boolean "common", default: false, null: false
+ t.string "identifier"
+ t.index ["common"], name: "index_prometheus_metrics_on_common", using: :btree
+ t.index ["group"], name: "index_prometheus_metrics_on_group", using: :btree
+ t.index ["identifier"], name: "index_prometheus_metrics_on_identifier", unique: true, using: :btree
+ t.index ["project_id"], name: "index_prometheus_metrics_on_project_id", using: :btree
end
- add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
- add_index "projects", ["created_at"], name: "index_projects_on_created_at", using: :btree
- add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
- add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
- add_index "projects", ["id"], name: "index_projects_on_id_partial_for_visibility", unique: true, where: "(visibility_level = ANY (ARRAY[10, 20]))", using: :btree
- add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
- add_index "projects", ["last_repository_check_at"], name: "index_projects_on_last_repository_check_at", where: "(last_repository_check_at IS NOT NULL)", using: :btree
- add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree
- add_index "projects", ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at", using: :btree
- add_index "projects", ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
- add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
- add_index "projects", ["path"], name: "index_projects_on_path", using: :btree
- add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
- add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree
- add_index "projects", ["repository_storage", "created_at"], name: "idx_project_repository_check_partial", where: "(last_repository_check_at IS NULL)", using: :btree
- add_index "projects", ["repository_storage"], name: "index_projects_on_repository_storage", using: :btree
- add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree
- add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
- add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
-
create_table "protected_branch_merge_access_levels", force: :cascade do |t|
t.integer "protected_branch_id", null: false
t.integer "access_level", default: 40, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["protected_branch_id"], name: "index_protected_branch_merge_access", using: :btree
end
- add_index "protected_branch_merge_access_levels", ["protected_branch_id"], name: "index_protected_branch_merge_access", using: :btree
-
create_table "protected_branch_push_access_levels", force: :cascade do |t|
t.integer "protected_branch_id", null: false
t.integer "access_level", default: 40, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["protected_branch_id"], name: "index_protected_branch_push_access", using: :btree
end
- add_index "protected_branch_push_access_levels", ["protected_branch_id"], name: "index_protected_branch_push_access", using: :btree
-
create_table "protected_branches", force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
end
- add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
-
create_table "protected_tag_create_access_levels", force: :cascade do |t|
t.integer "protected_tag_id", null: false
t.integer "access_level", default: 40
@@ -1727,22 +1718,22 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "group_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["group_id"], name: "index_protected_tag_create_access_levels_on_group_id", using: :btree
+ t.index ["protected_tag_id"], name: "index_protected_tag_create_access", using: :btree
+ t.index ["user_id"], name: "index_protected_tag_create_access_levels_on_user_id", using: :btree
end
- add_index "protected_tag_create_access_levels", ["protected_tag_id"], name: "index_protected_tag_create_access", using: :btree
- add_index "protected_tag_create_access_levels", ["user_id"], name: "index_protected_tag_create_access_levels_on_user_id", using: :btree
-
create_table "protected_tags", force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["project_id", "name"], name: "index_protected_tags_on_project_id_and_name", unique: true, using: :btree
+ t.index ["project_id"], name: "index_protected_tags_on_project_id", using: :btree
end
- add_index "protected_tags", ["project_id"], name: "index_protected_tags_on_project_id", using: :btree
-
create_table "push_event_payloads", id: false, force: :cascade do |t|
- t.integer "commit_count", limit: 8, null: false
+ t.bigint "commit_count", null: false
t.integer "event_id", null: false
t.integer "action", limit: 2, null: false
t.integer "ref_type", limit: 2, null: false
@@ -1750,21 +1741,19 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.binary "commit_to"
t.text "ref"
t.string "commit_title", limit: 70
+ t.index ["event_id"], name: "index_push_event_payloads_on_event_id", unique: true, using: :btree
end
- add_index "push_event_payloads", ["event_id"], name: "index_push_event_payloads_on_event_id", unique: true, using: :btree
-
create_table "redirect_routes", force: :cascade do |t|
t.integer "source_id", null: false
t.string "source_type", null: false
t.string "path", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["path"], name: "index_redirect_routes_on_path", unique: true, using: :btree
+ t.index ["source_type", "source_id"], name: "index_redirect_routes_on_source_type_and_source_id", using: :btree
end
- add_index "redirect_routes", ["path"], name: "index_redirect_routes_on_path", unique: true, using: :btree
- add_index "redirect_routes", ["source_type", "source_id"], name: "index_redirect_routes_on_source_type_and_source_id", using: :btree
-
create_table "releases", force: :cascade do |t|
t.string "tag"
t.text "description"
@@ -1773,11 +1762,10 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "updated_at"
t.text "description_html"
t.integer "cached_markdown_version"
+ t.index ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree
+ t.index ["project_id"], name: "index_releases_on_project_id", using: :btree
end
- add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree
- add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree
-
create_table "remote_mirrors", force: :cascade do |t|
t.integer "project_id"
t.string "url"
@@ -1794,19 +1782,24 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "encrypted_credentials_salt"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["last_successful_update_at"], name: "index_remote_mirrors_on_last_successful_update_at", using: :btree
+ t.index ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree
end
- add_index "remote_mirrors", ["last_successful_update_at"], name: "index_remote_mirrors_on_last_successful_update_at", using: :btree
- add_index "remote_mirrors", ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree
+ create_table "repositories", id: :bigserial, force: :cascade do |t|
+ t.integer "shard_id", null: false
+ t.string "disk_path", null: false
+ t.index ["disk_path"], name: "index_repositories_on_disk_path", unique: true, using: :btree
+ t.index ["shard_id"], name: "index_repositories_on_shard_id", using: :btree
+ end
create_table "repository_languages", id: false, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "programming_language_id", null: false
t.float "share", null: false
+ t.index ["project_id", "programming_language_id"], name: "index_repository_languages_on_project_and_languages_id", unique: true, using: :btree
end
- add_index "repository_languages", ["project_id", "programming_language_id"], name: "index_repository_languages_on_project_and_languages_id", unique: true, using: :btree
-
create_table "resource_label_events", id: :bigserial, force: :cascade do |t|
t.integer "action", null: false
t.integer "issue_id"
@@ -1814,13 +1807,15 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "label_id"
t.integer "user_id"
t.datetime_with_timezone "created_at", null: false
+ t.integer "cached_markdown_version"
+ t.text "reference"
+ t.text "reference_html"
+ t.index ["issue_id"], name: "index_resource_label_events_on_issue_id", using: :btree
+ t.index ["label_id"], name: "index_resource_label_events_on_label_id", using: :btree
+ t.index ["merge_request_id"], name: "index_resource_label_events_on_merge_request_id", using: :btree
+ t.index ["user_id"], name: "index_resource_label_events_on_user_id", using: :btree
end
- add_index "resource_label_events", ["issue_id"], name: "index_resource_label_events_on_issue_id", using: :btree
- add_index "resource_label_events", ["label_id"], name: "index_resource_label_events_on_label_id", using: :btree
- add_index "resource_label_events", ["merge_request_id"], name: "index_resource_label_events_on_merge_request_id", using: :btree
- add_index "resource_label_events", ["user_id"], name: "index_resource_label_events_on_user_id", using: :btree
-
create_table "routes", force: :cascade do |t|
t.integer "source_id", null: false
t.string "source_type", null: false
@@ -1828,12 +1823,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
+ t.index ["path"], name: "index_routes_on_path", unique: true, using: :btree
+ t.index ["path"], name: "index_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
+ t.index ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree
end
- add_index "routes", ["path"], name: "index_routes_on_path", unique: true, using: :btree
- add_index "routes", ["path"], name: "index_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
- add_index "routes", ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree
-
create_table "sent_notifications", force: :cascade do |t|
t.integer "project_id"
t.integer "noteable_id"
@@ -1845,10 +1839,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "note_type"
t.text "position"
t.string "in_reply_to_discussion_id"
+ t.index ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree
end
- add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree
-
create_table "services", force: :cascade do |t|
t.string "type"
t.string "title"
@@ -1871,14 +1864,17 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "commit_events", default: true, null: false
t.boolean "job_events", default: false, null: false
t.boolean "confidential_note_events", default: true
+ t.index ["project_id"], name: "index_services_on_project_id", using: :btree
+ t.index ["template"], name: "index_services_on_template", using: :btree
end
- add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
- add_index "services", ["template"], name: "index_services_on_template", using: :btree
+ create_table "shards", force: :cascade do |t|
+ t.string "name", null: false
+ t.index ["name"], name: "index_shards_on_name", unique: true, using: :btree
+ end
create_table "site_statistics", force: :cascade do |t|
t.integer "repositories_count", default: 0, null: false
- t.integer "wikis_count", default: 0, null: false
end
create_table "snippets", force: :cascade do |t|
@@ -1896,15 +1892,14 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "cached_markdown_version"
t.text "description"
t.text "description_html"
+ t.index ["author_id"], name: "index_snippets_on_author_id", using: :btree
+ t.index ["file_name"], name: "index_snippets_on_file_name_trigram", using: :gin, opclasses: {"file_name"=>"gin_trgm_ops"}
+ t.index ["project_id"], name: "index_snippets_on_project_id", using: :btree
+ t.index ["title"], name: "index_snippets_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
+ t.index ["updated_at"], name: "index_snippets_on_updated_at", using: :btree
+ t.index ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
end
- add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree
- add_index "snippets", ["file_name"], name: "index_snippets_on_file_name_trigram", using: :gin, opclasses: {"file_name"=>"gin_trgm_ops"}
- add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
- add_index "snippets", ["title"], name: "index_snippets_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
- add_index "snippets", ["updated_at"], name: "index_snippets_on_updated_at", using: :btree
- add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
-
create_table "spam_logs", force: :cascade do |t|
t.integer "user_id"
t.string "source_ip"
@@ -1927,20 +1922,19 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "created_at"
t.datetime "updated_at"
t.integer "project_id"
+ t.index ["project_id"], name: "index_subscriptions_on_project_id", using: :btree
+ t.index ["subscribable_id", "subscribable_type", "user_id", "project_id"], name: "index_subscriptions_on_subscribable_and_user_id_and_project_id", unique: true, using: :btree
end
- add_index "subscriptions", ["subscribable_id", "subscribable_type", "user_id", "project_id"], name: "index_subscriptions_on_subscribable_and_user_id_and_project_id", unique: true, using: :btree
-
create_table "system_note_metadata", force: :cascade do |t|
t.integer "note_id", null: false
t.integer "commit_count"
t.string "action"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["note_id"], name: "index_system_note_metadata_on_note_id", unique: true, using: :btree
end
- add_index "system_note_metadata", ["note_id"], name: "index_system_note_metadata_on_note_id", unique: true, using: :btree
-
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
@@ -1949,32 +1943,29 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "tagger_type"
t.string "context"
t.datetime "created_at"
+ t.index ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true, using: :btree
+ t.index ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
+ t.index ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
+ t.index ["taggable_id", "taggable_type"], name: "index_taggings_on_taggable_id_and_taggable_type", using: :btree
end
- add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true, using: :btree
- add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
- add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
- add_index "taggings", ["taggable_id", "taggable_type"], name: "index_taggings_on_taggable_id_and_taggable_type", using: :btree
-
create_table "tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
+ t.index ["name"], name: "index_tags_on_name", unique: true, using: :btree
end
- add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
-
create_table "term_agreements", force: :cascade do |t|
t.integer "term_id", null: false
t.integer "user_id", null: false
t.boolean "accepted", default: false, null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
+ t.index ["term_id"], name: "index_term_agreements_on_term_id", using: :btree
+ t.index ["user_id", "term_id"], name: "term_agreements_unique_index", unique: true, using: :btree
+ t.index ["user_id"], name: "index_term_agreements_on_user_id", using: :btree
end
- add_index "term_agreements", ["term_id"], name: "index_term_agreements_on_term_id", using: :btree
- add_index "term_agreements", ["user_id", "term_id"], name: "term_agreements_unique_index", unique: true, using: :btree
- add_index "term_agreements", ["user_id"], name: "index_term_agreements_on_user_id", using: :btree
-
create_table "timelogs", force: :cascade do |t|
t.integer "time_spent", null: false
t.integer "user_id"
@@ -1983,12 +1974,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "issue_id"
t.integer "merge_request_id"
t.datetime_with_timezone "spent_at"
+ t.index ["issue_id"], name: "index_timelogs_on_issue_id", using: :btree
+ t.index ["merge_request_id"], name: "index_timelogs_on_merge_request_id", using: :btree
+ t.index ["user_id"], name: "index_timelogs_on_user_id", using: :btree
end
- add_index "timelogs", ["issue_id"], name: "index_timelogs_on_issue_id", using: :btree
- add_index "timelogs", ["merge_request_id"], name: "index_timelogs_on_merge_request_id", using: :btree
- add_index "timelogs", ["user_id"], name: "index_timelogs_on_user_id", using: :btree
-
create_table "todos", force: :cascade do |t|
t.integer "user_id", null: false
t.integer "project_id"
@@ -2002,24 +1992,22 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "note_id"
t.string "commit_id"
t.integer "group_id"
+ t.index ["author_id"], name: "index_todos_on_author_id", using: :btree
+ t.index ["commit_id"], name: "index_todos_on_commit_id", using: :btree
+ t.index ["group_id"], name: "index_todos_on_group_id", using: :btree
+ t.index ["note_id"], name: "index_todos_on_note_id", using: :btree
+ t.index ["project_id"], name: "index_todos_on_project_id", using: :btree
+ t.index ["target_type", "target_id"], name: "index_todos_on_target_type_and_target_id", using: :btree
+ t.index ["user_id", "id"], name: "index_todos_on_user_id_and_id_done", where: "((state)::text = 'done'::text)", using: :btree
+ t.index ["user_id", "id"], name: "index_todos_on_user_id_and_id_pending", where: "((state)::text = 'pending'::text)", using: :btree
+ t.index ["user_id"], name: "index_todos_on_user_id", using: :btree
end
- add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree
- add_index "todos", ["commit_id"], name: "index_todos_on_commit_id", using: :btree
- add_index "todos", ["group_id"], name: "index_todos_on_group_id", using: :btree
- add_index "todos", ["note_id"], name: "index_todos_on_note_id", using: :btree
- add_index "todos", ["project_id"], name: "index_todos_on_project_id", using: :btree
- add_index "todos", ["target_type", "target_id"], name: "index_todos_on_target_type_and_target_id", using: :btree
- add_index "todos", ["user_id", "id"], name: "index_todos_on_user_id_and_id_done", where: "((state)::text = 'done'::text)", using: :btree
- add_index "todos", ["user_id", "id"], name: "index_todos_on_user_id_and_id_pending", where: "((state)::text = 'pending'::text)", using: :btree
- add_index "todos", ["user_id"], name: "index_todos_on_user_id", using: :btree
-
create_table "trending_projects", force: :cascade do |t|
t.integer "project_id", null: false
+ t.index ["project_id"], name: "index_trending_projects_on_project_id", unique: true, using: :btree
end
- add_index "trending_projects", ["project_id"], name: "index_trending_projects_on_project_id", unique: true, using: :btree
-
create_table "u2f_registrations", force: :cascade do |t|
t.text "certificate"
t.string "key_handle"
@@ -2029,13 +2017,12 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
+ t.index ["key_handle"], name: "index_u2f_registrations_on_key_handle", using: :btree
+ t.index ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree
end
- add_index "u2f_registrations", ["key_handle"], name: "index_u2f_registrations_on_key_handle", using: :btree
- add_index "u2f_registrations", ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree
-
create_table "uploads", force: :cascade do |t|
- t.integer "size", limit: 8, null: false
+ t.bigint "size", null: false
t.string "path", limit: 511, null: false
t.string "checksum", limit: 64
t.integer "model_id"
@@ -2045,12 +2032,12 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "mount_point"
t.string "secret"
t.integer "store"
+ t.index ["checksum"], name: "index_uploads_on_checksum", using: :btree
+ t.index ["model_id", "model_type"], name: "index_uploads_on_model_id_and_model_type", using: :btree
+ t.index ["store"], name: "index_uploads_on_store", using: :btree
+ t.index ["uploader", "path"], name: "index_uploads_on_uploader_and_path", using: :btree
end
- add_index "uploads", ["checksum"], name: "index_uploads_on_checksum", using: :btree
- add_index "uploads", ["model_id", "model_type"], name: "index_uploads_on_model_id_and_model_type", using: :btree
- add_index "uploads", ["uploader", "path"], name: "index_uploads_on_uploader_and_path", using: :btree
-
create_table "user_agent_details", force: :cascade do |t|
t.string "user_agent", null: false
t.string "ip_address", null: false
@@ -2059,42 +2046,48 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "submitted", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["subject_id", "subject_type"], name: "index_user_agent_details_on_subject_id_and_subject_type", using: :btree
end
- add_index "user_agent_details", ["subject_id", "subject_type"], name: "index_user_agent_details_on_subject_id_and_subject_type", using: :btree
-
create_table "user_callouts", force: :cascade do |t|
t.integer "feature_name", null: false
t.integer "user_id", null: false
+ t.index ["user_id", "feature_name"], name: "index_user_callouts_on_user_id_and_feature_name", unique: true, using: :btree
+ t.index ["user_id"], name: "index_user_callouts_on_user_id", using: :btree
end
- add_index "user_callouts", ["user_id", "feature_name"], name: "index_user_callouts_on_user_id_and_feature_name", unique: true, using: :btree
- add_index "user_callouts", ["user_id"], name: "index_user_callouts_on_user_id", using: :btree
-
create_table "user_custom_attributes", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "user_id", null: false
t.string "key", null: false
t.string "value", null: false
+ t.index ["key", "value"], name: "index_user_custom_attributes_on_key_and_value", using: :btree
+ t.index ["user_id", "key"], name: "index_user_custom_attributes_on_user_id_and_key", unique: true, using: :btree
end
- add_index "user_custom_attributes", ["key", "value"], name: "index_user_custom_attributes_on_key_and_value", using: :btree
- add_index "user_custom_attributes", ["user_id", "key"], name: "index_user_custom_attributes_on_user_id_and_key", unique: true, using: :btree
-
create_table "user_interacted_projects", id: false, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "project_id", null: false
+ t.index ["project_id", "user_id"], name: "index_user_interacted_projects_on_project_id_and_user_id", unique: true, using: :btree
+ t.index ["user_id"], name: "index_user_interacted_projects_on_user_id", using: :btree
end
- add_index "user_interacted_projects", ["project_id", "user_id"], name: "index_user_interacted_projects_on_project_id_and_user_id", unique: true, using: :btree
- add_index "user_interacted_projects", ["user_id"], name: "index_user_interacted_projects_on_user_id", using: :btree
+ create_table "user_preferences", force: :cascade do |t|
+ t.integer "user_id", null: false
+ t.integer "issue_notes_filter", limit: 2, default: 0, null: false
+ t.integer "merge_request_notes_filter", limit: 2, default: 0, null: false
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true, using: :btree
+ end
create_table "user_statuses", primary_key: "user_id", force: :cascade do |t|
t.integer "cached_markdown_version"
t.string "emoji", default: "speech_balloon", null: false
t.string "message", limit: 100
t.string "message_html"
+ t.index ["user_id"], name: "index_user_statuses_on_user_id", using: :btree
end
create_table "user_synced_attributes_metadata", force: :cascade do |t|
@@ -2103,10 +2096,9 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "location_synced", default: false
t.integer "user_id", null: false
t.string "provider"
+ t.index ["user_id"], name: "index_user_synced_attributes_metadata_on_user_id", unique: true, using: :btree
end
- add_index "user_synced_attributes_metadata", ["user_id"], name: "index_user_synced_attributes_metadata_on_user_id", unique: true, using: :btree
-
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
@@ -2174,33 +2166,34 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.integer "accepted_term_id"
t.string "feed_token"
t.boolean "private_profile"
+ t.boolean "include_private_contributions"
+ t.string "commit_email"
+ t.index ["accepted_term_id"], name: "index_users_on_accepted_term_id", using: :btree
+ t.index ["admin"], name: "index_users_on_admin", using: :btree
+ t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
+ t.index ["created_at"], name: "index_users_on_created_at", using: :btree
+ t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
+ t.index ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"}
+ t.index ["feed_token"], name: "index_users_on_feed_token", using: :btree
+ t.index ["ghost"], name: "index_users_on_ghost", using: :btree
+ t.index ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree
+ t.index ["name"], name: "index_users_on_name", using: :btree
+ t.index ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
+ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
+ t.index ["state"], name: "index_users_on_state", using: :btree
+ t.index ["username"], name: "index_users_on_username", using: :btree
+ t.index ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"}
end
- add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
- add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
- add_index "users", ["created_at"], name: "index_users_on_created_at", using: :btree
- add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
- add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"}
- add_index "users", ["feed_token"], name: "index_users_on_feed_token", using: :btree
- add_index "users", ["ghost"], name: "index_users_on_ghost", using: :btree
- add_index "users", ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree
- add_index "users", ["name"], name: "index_users_on_name", using: :btree
- add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
- add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
- add_index "users", ["state"], name: "index_users_on_state", using: :btree
- add_index "users", ["username"], name: "index_users_on_username", using: :btree
- add_index "users", ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"}
-
create_table "users_star_projects", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "user_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
+ t.index ["project_id"], name: "index_users_star_projects_on_project_id", using: :btree
+ t.index ["user_id", "project_id"], name: "index_users_star_projects_on_user_id_and_project_id", unique: true, using: :btree
end
- add_index "users_star_projects", ["project_id"], name: "index_users_star_projects_on_project_id", using: :btree
- add_index "users_star_projects", ["user_id", "project_id"], name: "index_users_star_projects_on_user_id_and_project_id", unique: true, using: :btree
-
create_table "web_hook_logs", force: :cascade do |t|
t.integer "web_hook_id", null: false
t.string "trigger"
@@ -2214,13 +2207,11 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.string "internal_error_message"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["created_at", "web_hook_id"], name: "index_web_hook_logs_on_created_at_and_web_hook_id", using: :btree
+ t.index ["web_hook_id"], name: "index_web_hook_logs_on_web_hook_id", using: :btree
end
- add_index "web_hook_logs", ["created_at", "web_hook_id"], name: "index_web_hook_logs_on_created_at_and_web_hook_id", using: :btree
- add_index "web_hook_logs", ["web_hook_id"], name: "index_web_hook_logs_on_web_hook_id", using: :btree
-
create_table "web_hooks", force: :cascade do |t|
- t.string "url", limit: 2000
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
@@ -2233,19 +2224,29 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "note_events", default: false, null: false
t.boolean "enable_ssl_verification", default: true
t.boolean "wiki_page_events", default: false, null: false
- t.string "token"
t.boolean "pipeline_events", default: false, null: false
t.boolean "confidential_issues_events", default: false, null: false
t.boolean "repository_update_events", default: false, null: false
t.boolean "job_events", default: false, null: false
t.boolean "confidential_note_events"
+ t.text "push_events_branch_filter"
+ t.string "encrypted_token"
+ t.string "encrypted_token_iv"
+ t.string "encrypted_url"
+ t.string "encrypted_url_iv"
+ t.index ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
+ t.index ["type"], name: "index_web_hooks_on_type", using: :btree
end
- add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
- add_index "web_hooks", ["type"], name: "index_web_hooks_on_type", using: :btree
-
+ add_foreign_key "application_settings", "users", column: "usage_stats_set_by_user_id", name: "fk_964370041d", on_delete: :nullify
add_foreign_key "badges", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "badges", "projects", on_delete: :cascade
+ add_foreign_key "board_group_recent_visits", "boards", on_delete: :cascade
+ add_foreign_key "board_group_recent_visits", "namespaces", column: "group_id", on_delete: :cascade
+ add_foreign_key "board_group_recent_visits", "users", on_delete: :cascade
+ add_foreign_key "board_project_recent_visits", "boards", on_delete: :cascade
+ add_foreign_key "board_project_recent_visits", "projects", on_delete: :cascade
+ add_foreign_key "board_project_recent_visits", "users", on_delete: :cascade
add_foreign_key "boards", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "boards", "projects", name: "fk_f15266b5f9", on_delete: :cascade
add_foreign_key "chat_teams", "namespaces", on_delete: :cascade
@@ -2280,6 +2281,8 @@ ActiveRecord::Schema.define(version: 20180807153545) do
add_foreign_key "ci_triggers", "projects", name: "fk_e3e63f966e", on_delete: :cascade
add_foreign_key "ci_triggers", "users", column: "owner_id", name: "fk_e8e10d1964", on_delete: :cascade
add_foreign_key "ci_variables", "projects", name: "fk_ada5eb64b3", on_delete: :cascade
+ add_foreign_key "cluster_groups", "clusters", on_delete: :cascade
+ add_foreign_key "cluster_groups", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "cluster_platforms_kubernetes", "clusters", on_delete: :cascade
add_foreign_key "cluster_projects", "clusters", on_delete: :cascade
add_foreign_key "cluster_projects", "projects", on_delete: :cascade
@@ -2289,9 +2292,13 @@ ActiveRecord::Schema.define(version: 20180807153545) do
add_foreign_key "clusters_applications_ingress", "clusters", name: "fk_753a7b41c1", on_delete: :cascade
add_foreign_key "clusters_applications_jupyter", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_jupyter", "oauth_applications", on_delete: :nullify
+ add_foreign_key "clusters_applications_knative", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_prometheus", "clusters", name: "fk_557e773639", on_delete: :cascade
add_foreign_key "clusters_applications_runners", "ci_runners", column: "runner_id", name: "fk_02de2ded36", on_delete: :nullify
add_foreign_key "clusters_applications_runners", "clusters", on_delete: :cascade
+ add_foreign_key "clusters_kubernetes_namespaces", "cluster_projects", on_delete: :nullify
+ add_foreign_key "clusters_kubernetes_namespaces", "clusters", on_delete: :cascade
+ add_foreign_key "clusters_kubernetes_namespaces", "projects", on_delete: :nullify
add_foreign_key "container_repositories", "projects"
add_foreign_key "deploy_keys_projects", "projects", name: "fk_58a901ca7e", on_delete: :cascade
add_foreign_key "deployments", "projects", name: "fk_b9a3851b82", on_delete: :cascade
@@ -2303,9 +2310,6 @@ ActiveRecord::Schema.define(version: 20180807153545) do
add_foreign_key "fork_network_members", "projects", on_delete: :cascade
add_foreign_key "fork_networks", "projects", column: "root_project_id", name: "fk_e7b436b2b5", on_delete: :nullify
add_foreign_key "forked_project_links", "projects", column: "forked_to_project_id", name: "fk_434510edb0", on_delete: :cascade
- add_foreign_key "gcp_clusters", "projects", on_delete: :cascade
- add_foreign_key "gcp_clusters", "services", on_delete: :nullify
- add_foreign_key "gcp_clusters", "users", on_delete: :nullify
add_foreign_key "gpg_key_subkeys", "gpg_keys", on_delete: :cascade
add_foreign_key "gpg_keys", "users", on_delete: :cascade
add_foreign_key "gpg_signatures", "gpg_key_subkeys", on_delete: :nullify
@@ -2324,6 +2328,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
add_foreign_key "issues", "users", column: "author_id", name: "fk_05f1e72feb", on_delete: :nullify
add_foreign_key "issues", "users", column: "closed_by_id", name: "fk_c63cbf6c25", on_delete: :nullify
add_foreign_key "issues", "users", column: "updated_by_id", name: "fk_ffed080f01", on_delete: :nullify
+ add_foreign_key "label_links", "labels", name: "fk_d97dd08678", on_delete: :cascade
add_foreign_key "label_priorities", "labels", on_delete: :cascade
add_foreign_key "label_priorities", "projects", on_delete: :cascade
add_foreign_key "labels", "namespaces", column: "group_id", on_delete: :cascade
@@ -2371,6 +2376,8 @@ ActiveRecord::Schema.define(version: 20180807153545) do
add_foreign_key "project_import_data", "projects", name: "fk_ffb9ee3a10", on_delete: :cascade
add_foreign_key "project_mirror_data", "projects", on_delete: :cascade
add_foreign_key "project_statistics", "projects", on_delete: :cascade
+ add_foreign_key "projects", "repositories", column: "pool_repository_id", name: "fk_6e5c14658a", on_delete: :nullify
+ add_foreign_key "prometheus_metrics", "projects", on_delete: :cascade
add_foreign_key "protected_branch_merge_access_levels", "protected_branches", name: "fk_8a3072ccb3", on_delete: :cascade
add_foreign_key "protected_branch_push_access_levels", "protected_branches", name: "fk_9ffc86a3d9", on_delete: :cascade
add_foreign_key "protected_branches", "projects", name: "fk_7a9c6d93e7", on_delete: :cascade
@@ -2381,6 +2388,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
add_foreign_key "push_event_payloads", "events", name: "fk_36c74129da", on_delete: :cascade
add_foreign_key "releases", "projects", name: "fk_47fe2a0596", on_delete: :cascade
add_foreign_key "remote_mirrors", "projects", on_delete: :cascade
+ add_foreign_key "repositories", "shards", on_delete: :restrict
add_foreign_key "repository_languages", "projects", on_delete: :cascade
add_foreign_key "resource_label_events", "issues", on_delete: :cascade
add_foreign_key "resource_label_events", "labels", on_delete: :nullify
@@ -2405,6 +2413,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
add_foreign_key "user_custom_attributes", "users", on_delete: :cascade
add_foreign_key "user_interacted_projects", "projects", name: "fk_722ceba4f7", on_delete: :cascade
add_foreign_key "user_interacted_projects", "users", name: "fk_0894651f08", on_delete: :cascade
+ add_foreign_key "user_preferences", "users", on_delete: :cascade
add_foreign_key "user_statuses", "users", on_delete: :cascade
add_foreign_key "user_synced_attributes_metadata", "users", on_delete: :cascade
add_foreign_key "users", "application_setting_terms", column: "accepted_term_id", name: "fk_789cd90b35", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index a814c787f94..20fcd2e1724 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -33,7 +33,7 @@ provides solutions for all the stages of the DevOps lifecycle:
[plan](#plan), [create](#create), [verify](#verify), [package](#package),
[release](#release), [configure](#configure), [monitor](#monitor).
-![DevOps Lifecycle](img/devops_lifecycle.png)
+<img class="image-noshadow" src="img/devops_lifecycle.png" alt="DevOps Lifecycle">
### Plan
@@ -133,6 +133,7 @@ scales to run your tests faster.
- [GitLab CI/CD](ci/README.md): Explore the features and capabilities of Continuous Integration, Continuous Delivery, and Continuous Deployment with GitLab.
- [Review Apps](ci/review_apps/index.md): Preview changes to your app right from a merge request.
- [Pipeline Graphs](ci/pipelines.md#pipeline-graphs)
+- [JUnit test reports](ci/junit_test_reports.md)
### Package
@@ -164,6 +165,7 @@ configuration. Then customize everything from buildpacks to CI/CD.
- [Deployment of Helm, Ingress, and Prometheus on Kubernetes](user/project/clusters/index.md#installing-applications)
- [Protected variables](ci/variables/README.md#protected-variables)
- [Easy creation of Kubernetes clusters on GKE](user/project/clusters/index.md#adding-and-creating-a-new-gke-cluster-via-gitlab)
+- [Executable Runbooks](user/project/clusters/runbooks/index.md)
### Monitor
@@ -190,7 +192,7 @@ instant how code changes impact your production environment.
### User account
- [User account](user/profile/index.md): Manage your account
- - [Authentication](topics/authentication/index.md): Account security with two-factor authentication, setup your ssh keys and deploy keys for secure access to your projects.
+ - [Authentication](topics/authentication/index.md): Account security with two-factor authentication, set up your ssh keys and deploy keys for secure access to your projects.
- [Profile settings](user/profile/index.md#profile-settings): Manage your profile settings, two factor authentication and more.
- [User permissions](user/permissions.md): Learn what each role in a project (external/guest/reporter/developer/maintainer/owner) can do.
diff --git a/doc/administration/auth/authentiq.md b/doc/administration/auth/authentiq.md
index 252ff1f4b15..772e55cef07 100644
--- a/doc/administration/auth/authentiq.md
+++ b/doc/administration/auth/authentiq.md
@@ -6,7 +6,7 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
1. Get your Client credentials (Client ID and Client Secret) at [Authentiq](https://www.authentiq.com/developers).
-2. On your GitLab server, open the configuration file:
+1. On your GitLab server, open the configuration file:
For omnibus installation
```sh
@@ -18,11 +18,11 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
```sh
sudo -u git -H editor /home/git/gitlab/config/gitlab.yml
```
-
-3. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings to enable single sign-on and add Authentiq as an OAuth provider.
-4. Add the provider configuration for Authentiq:
-
+1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings to enable single sign-on and add Authentiq as an OAuth provider.
+
+1. Add the provider configuration for Authentiq:
+
For Omnibus packages:
```ruby
@@ -31,15 +31,15 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
"name" => "authentiq",
"app_id" => "YOUR_CLIENT_ID",
"app_secret" => "YOUR_CLIENT_SECRET",
- "args" => {
+ "args" => {
"scope": 'aq:name email~rs address aq:push'
}
}
]
```
-
+
For installations from source:
-
+
```yaml
- { name: 'authentiq',
app_id: 'YOUR_CLIENT_ID',
@@ -49,20 +49,20 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
}
}
```
-
-
-5. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits.
+
+
+1. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits.
See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq/wiki/Scopes,-callback-url-configuration-and-responses) for more information on scopes and modifiers.
-6. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in step 1.
+1. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in step 1.
-7. Save the configuration file.
+1. Save the configuration file.
-8. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
+1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
-On the sign in page there should now be an Authentiq icon below the regular sign in form. Click the icon to begin the authentication process.
+On the sign in page there should now be an Authentiq icon below the regular sign in form. Click the icon to begin the authentication process.
-- If the user has the Authentiq ID app installed in their iOS or Android device, they can scan the QR code, decide what personal details to share and sign in to your GitLab installation.
-- If not they will be prompted to download the app and then follow the procedure above.
+- If the user has the Authentiq ID app installed in their iOS or Android device, they can scan the QR code, decide what personal details to share and sign in to your GitLab installation.
+- If not they will be prompted to download the app and then follow the procedure above.
-If everything goes right, the user will be returned to GitLab and will be signed in. \ No newline at end of file
+If everything goes right, the user will be returned to GitLab and will be signed in.
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.png b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.png
index 11ce324f938..223fd0ac401 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.png
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.png
Binary files differ
diff --git a/doc/administration/auth/img/crowd_application.png b/doc/administration/auth/img/crowd_application.png
index 7deea9dac8e..5029a005667 100644
--- a/doc/administration/auth/img/crowd_application.png
+++ b/doc/administration/auth/img/crowd_application.png
Binary files differ
diff --git a/doc/administration/auth/img/crowd_application_authorisation.png b/doc/administration/auth/img/crowd_application_authorisation.png
index 70339891b34..0e0bde1344b 100644
--- a/doc/administration/auth/img/crowd_application_authorisation.png
+++ b/doc/administration/auth/img/crowd_application_authorisation.png
Binary files differ
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index 3c98d683924..54ded25291a 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -111,7 +111,7 @@ main:
uid: 'sAMAccountName' # This should be the attribute, not the value that maps to uid.
##
- ## Examples: 'america\\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com'
+ ## Examples: 'america\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com'
##
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
@@ -132,7 +132,7 @@ main:
## Enables SSL certificate verification if encryption method is
## "start_tls" or "simple_tls". Defaults to true since GitLab 10.0 for
## security. This may break installations upon upgrade to 10.0, that did
- ## not know their LDAP SSL certificates were not setup properly.
+ ## not know their LDAP SSL certificates were not set up properly.
##
verify_certificates: true
@@ -382,29 +382,30 @@ the configuration option `lowercase_usernames`. By default, this configuration o
1. Edit `/etc/gitlab/gitlab.rb`:
- ```ruby
- gitlab_rails['ldap_servers'] = YAML.load <<-EOS
- main:
- # snip...
- lowercase_usernames: true
- EOS
- ```
+ ```ruby
+ gitlab_rails['ldap_servers'] = YAML.load <<-EOS
+ main:
+ # snip...
+ lowercase_usernames: true
+ EOS
+ ```
-2. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**Source configuration**
1. Edit `config/gitlab.yaml`:
- ```yaml
- production:
- ldap:
- servers:
- main:
- # snip...
- lowercase_usernames: true
- ```
-2. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
+ ```yaml
+ production:
+ ldap:
+ servers:
+ main:
+ # snip...
+ lowercase_usernames: true
+ ```
+
+1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
## Encryption
diff --git a/doc/administration/auth/okta.md b/doc/administration/auth/okta.md
index 664657650d4..ae38094391b 100644
--- a/doc/administration/auth/okta.md
+++ b/doc/administration/auth/okta.md
@@ -11,7 +11,7 @@ The following documentation enables Okta as a SAML provider.
1. When the app screen comes up you see another button to **Create an App** and
choose SAML 2.0 on the next screen.
1. Now, very important, add a logo
- (you can choose it from https://about.gitlab.com/press/). You'll have to
+ (you can choose it from <https://about.gitlab.com/press/>). You'll have to
crop and resize it.
1. Next, you'll need the to fill in the SAML general config. Here's an example
image.
diff --git a/doc/administration/compliance.md b/doc/administration/compliance.md
new file mode 100644
index 00000000000..0414b3ec12e
--- /dev/null
+++ b/doc/administration/compliance.md
@@ -0,0 +1,18 @@
+# Compliance features
+
+You can configure the following GitLab features to help ensure that your GitLab instance meets common compliance standards. Click a feature name for further documentation.
+
+GitLab’s [security features](../security/README.md) may also help you meet relevant compliance standards.
+
+|Feature |GitLab tier |GitLab.com |
+| ---------| :--------: | :-------: |
+|**[Restrict SSH Keys](../README.html#administrator-documentation)**<br>Control the technology and key length of SSH keys used to access GitLab|Core+||
+|**[Granular user roles and flexible permissions](../user/permissions.html)**<br>Manage access and permissions with five different user roles and settings for external users. Set permissions according to people's role, rather than either read or write access to a repository. Don't share the source code with people that only need access to the issue tracker.|Core+|✓|
+|**[Enforce TOS acceptance](../user/admin_area/settings/terms.html)**<br>Enforce your users accepting new terms of service by blocking GitLab traffic.|Core+||
+|**[Email all users of a project, group, or entire server](../user/admin_area/settings/terms.html)**<br>An admin can email groups of users based on project or group membership, or email everyone using the GitLab instance. This is great for scheduled maintenance or upgrades.|Starter+||
+|**[Omnibus package supports log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-forwarding)**<br>Forward your logs to a central system.|Starter+||
+|**[Lock project membership to group](../workflow/groups.html#lock-project-membership-to-members-of-this-group)**<br>Group owners can prevent new members from being added to projects within a group.|Starter+|✓|
+|**[LDAP group sync](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#group-sync)**<br>GitLab Enterprise Edition gives admins the ability to automatically sync groups and manage SSH keys, permissions, and authentication, so you can focus on building your product, not configuring your tools.|Starter+||
+|**[LDAP group sync filters](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#group-sync)**<br>GitLab Enterprise Edition Premium gives more flexibility to synchronize with LDAP based on filters, meaning you can leverage LDAP attributes to map GitLab permissions.|Premium+||
+|**[Audit logs](https://docs.gitlab.com/ee/administration/audit_events.html)**<br>To maintain the integrity of your code, GitLab Enterprise Edition Premium gives admins the ability to view any modifications made within the GitLab server in an advanced audit log system, so you can control, analyze and track every change.|Premium+||
+|**[Auditor users](https://docs.gitlab.com/ee/administration/auditor_users.html)**<br>Auditor users are users who are given read-only access to all projects, groups, and other resources on the GitLab instance.|Premium+|| \ No newline at end of file
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index 2441ff85783..cfe7b0e05e3 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -1,11 +1,11 @@
# GitLab Container Registry administration
> **Notes:**
-- [Introduced][ce-4040] in GitLab 8.8.
-- Container Registry manifest `v1` support was added in GitLab 8.9 to support
- Docker versions earlier than 1.10.
-- This document is about the admin guide. To learn how to use GitLab Container
- Registry [user documentation](../user/project/container_registry.md).
+> - [Introduced][ce-4040] in GitLab 8.8.
+> - Container Registry manifest `v1` support was added in GitLab 8.9 to support
+> Docker versions earlier than 1.10.
+> - This document is about the admin guide. To learn how to use GitLab Container
+> Registry [user documentation](../user/project/container_registry.md).
With the Container Registry integrated into GitLab, every project can have its
own space to store its Docker images.
@@ -71,7 +71,7 @@ A Registry init file is not shipped with GitLab if you install it from source.
Hence, [restarting GitLab][restart gitlab] will not restart the Registry should
you modify its settings. Read the upstream documentation on how to achieve that.
-At the absolute minimum, make sure your [Registry configuration][registry-auth]
+At the **absolute** minimum, make sure your [Registry configuration][registry-auth]
has `container_registry` as the service and `https://gitlab.example.com/jwt/auth`
as the realm:
@@ -84,6 +84,9 @@ auth:
rootcertbundle: /root/certs/certbundle
```
+CAUTION: **Caution:**
+If `auth` is not set up, users will be able to pull docker images without authentication.
+
## Container Registry domain configuration
There are two ways you can configure the Registry's external domain.
@@ -203,10 +206,10 @@ If you have a [wildcard certificate][], you need to specify the path to the
certificate in addition to the URL, in this case `/etc/gitlab/gitlab.rb` will
look like:
>
-```ruby
-registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/certificate.pem"
-registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/certificate.key"
-```
+> ```ruby
+> registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/certificate.pem"
+> registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/certificate.key"
+> ```
---
@@ -375,7 +378,7 @@ Read more about the individual driver's config options in the
> **Warning** GitLab will not backup Docker images that are not stored on the
filesystem. Remember to enable backups with your object storage provider if
desired.
-
+>
> **Important** Enabling storage driver other than `filesystem` would mean
that your Docker client needs to be able to access the storage backend directly.
So you must use an address that resolves and is accessible outside GitLab server.
@@ -598,11 +601,11 @@ thus the error above.
While GitLab doesn't support using self-signed certificates with Container
Registry out of the box, it is possible to make it work if you follow
-[Docker's documentation][docker-insecure]. You may find some additional
+[Docker's documentation][docker-insecure-self-signed]. You may find some additional
information in [issue 18239][ce-18239].
[ce-18239]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18239
-[docker-insecure]: https://docs.docker.com/registry/insecure/#using-self-signed-certificates
+[docker-insecure-self-signed]: https://docs.docker.com/registry/insecure/#use-self-signed-certificates
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
[restart gitlab]: restart_gitlab.md#installations-from-source
[wildcard certificate]: https://en.wikipedia.org/wiki/Wildcard_certificate
diff --git a/doc/administration/custom_hooks.md b/doc/administration/custom_hooks.md
index 1c508c77ffa..60ad4bf4e8f 100644
--- a/doc/administration/custom_hooks.md
+++ b/doc/administration/custom_hooks.md
@@ -1,7 +1,6 @@
# Custom Git Hooks
->
-**Note:** Custom Git hooks must be configured on the filesystem of the GitLab
+> **Note:** Custom Git hooks must be configured on the filesystem of the GitLab
server. Only GitLab server administrators will be able to complete these tasks.
Please explore [webhooks] and [CI] as an option if you do not
have filesystem access. For a user configurable Git hook interface, see
@@ -51,6 +50,9 @@ Hooks can be also placed in `hooks/<hook_name>.d` (global) or
`custom_hooks/<hook_name>.d` (per project) directories supporting chained
execution of the hooks.
+NOTE: **Note:** `<hook_name>.d` would need to be either `pre-receive.d`,
+`post-receive.d`, or `update.d` to work properly. Any other names will be ignored.
+
To look in a different directory for the global custom hooks (those in
`hooks/<hook_name.d>`), set `custom_hooks_dir` in gitlab-shell config. For
Omnibus installations, this can be set in `gitlab.rb`; and in source
@@ -58,7 +60,7 @@ installations, this can be set in `gitlab-shell/config.yml`.
The hooks are searched and executed in this order:
-1. `<project>.git/hooks/` - symlink to `gitlab-shell/hooks` global dir
+1. `gitlab-shell/hooks` directory as known to Gitaly
1. `<project>.git/hooks/<hook_name>` - executed by `git` itself, this is `gitlab-shell/hooks/<hook_name>`
1. `<project>.git/custom_hooks/<hook_name>` - per project hook (this is already existing behavior)
1. `<project>.git/custom_hooks/<hook_name>.d/*` - per project hooks
diff --git a/doc/administration/external_database.md b/doc/administration/external_database.md
index 31199f2cdc7..ec2d30c82d1 100644
--- a/doc/administration/external_database.md
+++ b/doc/administration/external_database.md
@@ -9,7 +9,7 @@ separate from the GitLab Omnibus package.
If you use a cloud-managed service, or provide your own PostgreSQL instance:
-1. Setup PostgreSQL according to the
+1. Set up PostgreSQL according to the
[database requirements document](../install/requirements.md#database).
1. Set up a `gitlab` username with a password of your choice. The `gitlab` user
needs privileges to create the `gitlabhq_production` database.
diff --git a/doc/administration/git_protocol.md b/doc/administration/git_protocol.md
new file mode 100644
index 00000000000..341a00009e5
--- /dev/null
+++ b/doc/administration/git_protocol.md
@@ -0,0 +1,103 @@
+---
+description: "Set and configure Git protocol v2"
+---
+
+# Configuring Git Protocol v2
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/46555) in GitLab 11.4.
+
+Git protocol v2 improves the v1 wire protocol in several ways and is
+enabled by default in GitLab for HTTP requests. In order to enable SSH,
+further configuration is needed by the administrator.
+
+More details about the new features and improvements are available in
+the [Google Open Source Blog](https://opensource.googleblog.com/2018/05/introducing-git-protocol-version-2.html)
+and the [protocol documentation](https://github.com/git/git/blob/master/Documentation/technical/protocol-v2.txt).
+
+## Requirements
+
+From the client side, `git` `v2.18.0` or newer must be installed.
+
+From the server side, if we want to configure SSH we need to set the `sshd`
+server to accept the `GIT_PROTOCOL` environment.
+
+In installations using [GitLab Helm Charts](../install/kubernetes/gitlab_chart.md)
+and [All-in-one docker image](https://docs.gitlab.com/omnibus/docker/), the SSH
+service is already configured to accept the `GIT_PROTOCOL` environment and users
+need not do anything more.
+
+For Omnibus GitLab and installations from source, you have to manually update
+the SSH configuration of your server:
+
+```
+# /etc/ssh/sshd_config
+AcceptEnv GIT_PROTOCOL
+```
+
+Once configured, restart the SSH daemon. In Ubuntu, run:
+
+```sh
+sudo service ssh restart
+```
+
+## Instructions
+
+In order to use the new protocol, clients need to either pass the configuration
+`-c protocol.version=2` to the git command, or set it globally:
+
+```sh
+git config --global protocol.version 2
+```
+
+### HTTP connections
+
+Verify Git v2 is used by the client:
+
+```sh
+GIT_TRACE_CURL=1 git -c protocol.version=2 ls-remote https://your-gitlab-instance.com/group/repo.git 2>&1 | grep Git-Protocol
+```
+
+You should see that the `Git-Protocol` header is sent:
+
+```
+16:29:44.577888 http.c:657 => Send header: Git-Protocol: version=2
+```
+
+Verify Git v2 is used by the server:
+
+```sh
+GIT_TRACE_PACKET=1 git -c protocol.version=2 ls-remote https://your-gitlab-instance.com/group/repo.git 2>&1 | head
+```
+
+Example response using Git protocol v2:
+
+```sh
+$ GIT_TRACE_PACKET=1 git -c protocol.version=2 ls-remote https://your-gitlab-instance.com/group/repo.git 2>&1 | head
+10:42:50.574485 pkt-line.c:80 packet: git< # service=git-upload-pack
+10:42:50.574653 pkt-line.c:80 packet: git< 0000
+10:42:50.574673 pkt-line.c:80 packet: git< version 2
+10:42:50.574679 pkt-line.c:80 packet: git< agent=git/2.18.1
+10:42:50.574684 pkt-line.c:80 packet: git< ls-refs
+10:42:50.574688 pkt-line.c:80 packet: git< fetch=shallow
+10:42:50.574693 pkt-line.c:80 packet: git< server-option
+10:42:50.574697 pkt-line.c:80 packet: git< 0000
+10:42:50.574817 pkt-line.c:80 packet: git< version 2
+10:42:50.575308 pkt-line.c:80 packet: git< agent=git/2.18.1
+```
+
+### SSH Connections
+
+Verify Git v2 is used by the client:
+
+```sh
+GIT_SSH_COMMAND="ssh -v" git -c protocol.version=2 ls-remote ssh://your-gitlab-instance.com:group/repo.git 2>&1 |grep GIT_PROTOCOL
+```
+
+You should see that the `GIT_PROTOCOL` environment variable is sent:
+
+```
+debug1: Sending env GIT_PROTOCOL = version=2
+```
+
+For the server side, you can use the [same examples from HTTP](#http-connections), changing the
+URL to use SSH.
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 6d7e408d41b..e1b2a0a24eb 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -25,15 +25,13 @@ gitaly['prometheus_listen_addr'] = 'localhost:9236'
```
To change a Gitaly setting in installations from source you can edit
-`/home/git/gitaly/config.toml`.
+`/home/git/gitaly/config.toml`. Changes will be applied when you run
+`service gitlab restart`.
```toml
prometheus_listen_addr = "localhost:9236"
```
-Changes to `/home/git/gitaly/config.toml` are applied when you run `service
-gitlab restart`.
-
## Client-side GRPC logs
Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC
@@ -101,8 +99,13 @@ documentation on configuring Gitaly
authentication](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configuration/README.md#authentication)
.
->
-**NOTE:** In most or all cases the storage paths below end in `/repositories` which is
+Gitaly must trigger some callbacks to GitLab via GitLab Shell. As a result,
+the GitLab Shell secret must be the same between the other GitLab servers and
+the Gitaly server. The easiest way to accomplish this is to copy `/etc/gitlab/gitlab-secrets.json`
+from an existing GitLab server to the Gitaly server. Without this shared secret,
+Git operations in GitLab will result in an API error.
+
+> **NOTE:** In most or all cases the storage paths below end in `/repositories` which is
different than `path` in `git_data_dirs` of Omnibus installations. Check the
directory layout on your Gitaly server to be sure.
@@ -149,7 +152,7 @@ listen_addr = '0.0.0.0:8075'
[auth]
token = 'abc123secret'
-[[storage]
+[[storage]]
name = 'default'
path = '/mnt/gitlab/default/repositories'
@@ -214,7 +217,7 @@ repository from your GitLab server over HTTP.
If you are running Gitaly [as a remote
service](#running-gitaly-on-its-own-server) you may want to disable
-the local Gitaly service that runs on your Gitlab server by default.
+the local Gitaly service that runs on your GitLab server by default.
> 'Disabling Gitaly' only makes sense when you run GitLab in a custom
cluster configuration, where different services run on different
diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md
index ea8077f0623..49fe80fb2a6 100644
--- a/doc/administration/high_availability/README.md
+++ b/doc/administration/high_availability/README.md
@@ -37,7 +37,7 @@ Follow the steps below to configure an active/active setup:
1. [Configure the database](database.md)
1. [Configure Redis](redis.md)
- 1. [Configure Redis for GitLab source installations](redis_source.md)
+ 1. [Configure Redis for GitLab source installations](redis_source.md)
1. [Configure NFS](nfs.md)
1. [Configure the GitLab application servers](gitlab.md)
1. [Configure the load balancers](load_balancer.md)
diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md
index b5124b1d540..c1eeb40b98f 100644
--- a/doc/administration/high_availability/database.md
+++ b/doc/administration/high_availability/database.md
@@ -13,7 +13,7 @@ Database Service (RDS) that runs PostgreSQL.
If you use a cloud-managed service, or provide your own PostgreSQL:
-1. Setup PostgreSQL according to the
+1. Set up PostgreSQL according to the
[database requirements document](../../install/requirements.md#database).
1. Set up a `gitlab` username with a password of your choice. The `gitlab` user
needs privileges to create the `gitlabhq_production` database.
diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md
index 637d44d2823..f16ae835ced 100644
--- a/doc/administration/high_availability/gitlab.md
+++ b/doc/administration/high_availability/gitlab.md
@@ -25,11 +25,11 @@ for each GitLab application server in your environment.
options. Here is an example snippet to add to `/etc/fstab`:
```
- 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
- 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
- 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
- 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
- 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
```
1. Create the shared directories. These may be different depending on your NFS
@@ -84,7 +84,7 @@ for each GitLab application server in your environment.
servers should point to the external url that users will use to access GitLab.
In a typical HA setup, this will be the url of the load balancer which will
route traffic to all GitLab application servers in the HA cluster.
-
+ >
> **Note:** When you specify `https` in the `external_url`, as in the example
above, GitLab assumes you have SSL certificates in `/etc/gitlab/ssl/`. If
certificates are not present, Nginx will fail to start. See
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index 387c3fb6a5b..74b0e2c8184 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -3,6 +3,11 @@
You can view information and options set for each of the mounted NFS file
systems by running `nfsstat -m` and `cat /etc/fstab`.
+NOTE: **Note:** Filesystem performance has a big impact on overall GitLab
+performance, especially for actions that read or write to Git repositories. See
+[Filesystem Performance Benchmarking](../operations/filesystem_benchmarking.md)
+for steps to test filesystem performance.
+
## NFS Server features
### Required features
@@ -32,7 +37,31 @@ options:
circumstances it could lead to data loss if a failure occurs before data has
synced.
-## AWS Elastic File System
+### Known issues
+
+On some customer systems, we have seen NFS clients slow precipitously due to
+[excessive network traffic from numerous `TEST_STATEID` NFS
+messages](https://gitlab.com/gitlab-org/gitlab-ce/issues/52017). This is
+likely due to a [Linux kernel
+bug](https://bugzilla.redhat.com/show_bug.cgi?id=1552203) that may be fixed in
+[more recent kernels with this
+commit](https://github.com/torvalds/linux/commit/95da1b3a5aded124dd1bda1e3cdb876184813140).
+
+Users encountering a similar issue may be advised to disable the NFS server
+delegation feature, which is an optimization to reduce the number of network
+round-trips needed to read or write files. To disable NFS server delegations
+on an Linux NFS server, do the following:
+
+1. On the NFS server, run:
+
+ ```sh
+ echo 0 > /proc/sys/fs/leases-enable
+ sysctl -w fs.leases-enable=0
+ ```
+
+1. Restart the NFS server process. For example, on CentOS run `service nfs restart`.
+
+## Avoid using AWS's Elastic File System (EFS)
GitLab strongly recommends against using AWS Elastic File System (EFS).
Our support team will not be able to assist on performance issues related to
@@ -47,7 +76,24 @@ there because this will also affect performance. We recommend that the log files
stored on a local volume.
For more details on another person's experience with EFS, see
-[Amazon's Elastic File System: Burst Credits](https://www.rawkode.io/2017/04/amazons-elastic-file-system-burst-credits/)
+[Amazon's Elastic File System: Burst Credits](https://rawkode.com/2017/04/16/amazons-elastic-file-system-burst-credits/)
+
+## Avoid using PostgreSQL with NFS
+
+GitLab strongly recommends against running your PostgreSQL database
+across NFS. The GitLab support team will not be able to assist on performance issues related to
+this configuration.
+
+Additionally, this configuration is specifically warned against in the
+[Postgres Documentation](https://www.postgresql.org/docs/current/static/creating-cluster.html#CREATING-CLUSTER-NFS):
+
+>PostgreSQL does nothing special for NFS file systems, meaning it assumes NFS behaves exactly like
+>locally-connected drives. If the client or server NFS implementation does not provide standard file
+>system semantics, this can cause reliability problems. Specifically, delayed (asynchronous) writes
+>to the NFS server can cause data corruption problems.
+
+For supported database architecture, please see our documentation on
+[Configuring a Database for GitLab HA](https://docs.gitlab.com/ee/administration/high_availability/database.html).
## NFS Client mount options
@@ -55,14 +101,15 @@ Below is an example of an NFS mount point defined in `/etc/fstab` we use on
GitLab.com:
```
-10.1.1.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
+10.1.1.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
```
-Notice several options that you should consider using:
+Note there are several options that you should consider using:
| Setting | Description |
| ------- | ----------- |
-| `nobootwait` | Don't halt boot process waiting for this mount to become available
+| `vers=4.1` |NFS v4.1 should be used instead of v4.0 because there is a Linux [NFS client bug in v4.0](https://gitlab.com/gitlab-org/gitaly/issues/1339) that can cause significant problems due to stale data.
+| `nofail` | Don't halt boot process waiting for this mount to become available
| `lookupcache=positive` | Tells the NFS client to honor `positive` cache results but invalidates any `negative` cache results. Negative cache results cause problems with Git. Specifically, a `git push` can fail to register uniformly across all NFS clients. The negative cache causes the clients to 'remember' that the files did not exist previously.
## A single NFS mount
@@ -87,7 +134,7 @@ Mount `/gitlab-nfs` then use the following Omnibus
configuration to move each data location to a subdirectory:
```ruby
-git_data_dirs({"default" => "/gitlab-nfs/gitlab-data/git-data"})
+git_data_dirs({"default" => { "path" => "/gitlab-nfs/gitlab-data/git-data"} })
user['home'] = '/gitlab-nfs/gitlab-data/home'
gitlab_rails['uploads_directory'] = '/gitlab-nfs/gitlab-data/uploads'
gitlab_rails['shared_path'] = '/gitlab-nfs/gitlab-data/shared'
@@ -133,7 +180,7 @@ following are the 5 locations need to be shared:
| Location | Description | Default configuration |
| -------- | ----------- | --------------------- |
-| `/var/opt/gitlab/git-data` | Git repository data. This will account for a large portion of your data | `git_data_dirs({"default" => "/var/opt/gitlab/git-data"})`
+| `/var/opt/gitlab/git-data` | Git repository data. This will account for a large portion of your data | `git_data_dirs({"default" => { "path" => "/var/opt/gitlab/git-data"} })`
| `/var/opt/gitlab/.ssh` | SSH `authorized_keys` file and keys used to import repositories from some other Git services | `user['home'] = '/var/opt/gitlab/'`
| `/var/opt/gitlab/gitlab-rails/uploads` | User uploaded attachments | `gitlab_rails['uploads_directory'] = '/var/opt/gitlab/gitlab-rails/uploads'`
| `/var/opt/gitlab/gitlab-rails/shared` | Build artifacts, GitLab Pages, LFS objects, temp files, etc. If you're using LFS this may also account for a large portion of your data | `gitlab_rails['shared_path'] = '/var/opt/gitlab/gitlab-rails/shared'`
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index 031fb31ca4f..a9ba40c870c 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -1,7 +1,6 @@
# Configuring Redis for GitLab HA
->
-Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11.
+> Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11.
Starting with 8.14, Redis Sentinel is no longer experimental.
If you've used it with versions `< 8.14` before, please check the updated
documentation here.
@@ -15,20 +14,20 @@ a hosted cloud solution or you can use the one that comes bundled with
Omnibus GitLab packages.
> **Notes:**
-- Redis requires authentication for High Availability. See
- [Redis Security](http://redis.io/topics/security) documentation for more
- information. We recommend using a combination of a Redis password and tight
- firewall rules to secure your Redis service.
-- You are highly encouraged to read the [Redis Sentinel][sentinel] documentation
- before configuring Redis HA with GitLab to fully understand the topology and
- architecture.
-- This is the documentation for the Omnibus GitLab packages. For installations
- from source, follow the [Redis HA source installation](redis_source.md) guide.
-- Redis Sentinel daemon is bundled with Omnibus GitLab Enterprise Edition only.
- For configuring Sentinel with the Omnibus GitLab Community Edition and
- installations from source, read the
- [Available configuration setups](#available-configuration-setups) section
- below.
+> - Redis requires authentication for High Availability. See
+> [Redis Security](https://redis.io/topics/security) documentation for more
+> information. We recommend using a combination of a Redis password and tight
+> firewall rules to secure your Redis service.
+> - You are highly encouraged to read the [Redis Sentinel][sentinel] documentation
+> before configuring Redis HA with GitLab to fully understand the topology and
+> architecture.
+> - This is the documentation for the Omnibus GitLab packages. For installations
+> from source, follow the [Redis HA source installation](redis_source.md) guide.
+> - Redis Sentinel daemon is bundled with Omnibus GitLab Enterprise Edition only.
+> For configuring Sentinel with the Omnibus GitLab Community Edition and
+> installations from source, read the
+> [Available configuration setups](#available-configuration-setups) section
+> below.
## Overview
@@ -55,11 +54,11 @@ components below.
### High Availability with Sentinel
->**Notes:**
-- Starting with GitLab `8.11`, you can configure a list of Redis Sentinel
- servers that will monitor a group of Redis servers to provide failover support.
-- Starting with GitLab `8.14`, the Omnibus GitLab Enterprise Edition package
- comes with Redis Sentinel daemon built-in.
+> **Notes:**
+> - Starting with GitLab `8.11`, you can configure a list of Redis Sentinel
+> servers that will monitor a group of Redis servers to provide failover support.
+> - Starting with GitLab `8.14`, the Omnibus GitLab Enterprise Edition package
+> comes with Redis Sentinel daemon built-in.
High Availability with Redis requires a few things:
@@ -82,8 +81,8 @@ When a **Master** fails to respond, it's the application's responsibility
(in our case GitLab) to handle timeout and reconnect (querying a **Sentinel**
for a new **Master**).
-To get a better understanding on how to correctly setup Sentinel, please read
-the [Redis Sentinel documentation](http://redis.io/topics/sentinel) first, as
+To get a better understanding on how to correctly set up Sentinel, please read
+the [Redis Sentinel documentation](https://redis.io/topics/sentinel) first, as
failing to configure it correctly can lead to data loss or can bring your
whole cluster down, invalidating the failover effort.
@@ -218,7 +217,7 @@ Pick the one that suits your needs.
and configure Sentinel, jump directly to the Sentinel section in the
[Redis HA installation from source](redis_source.md#step-3-configuring-the-redis-sentinel-instances) documentation.
- [Omnibus GitLab **Enterprise Edition** (EE) package][ee]: Both Redis and Sentinel
- are bundled in the package, so you can use the EE package to setup the whole
+ are bundled in the package, so you can use the EE package to set up the whole
Redis HA infrastructure (master, slave and Sentinel) which is described in
this document.
- If you have installed GitLab using the Omnibus GitLab packages (CE or EE),
@@ -229,15 +228,15 @@ Pick the one that suits your needs.
## Configuring Redis HA
-This is the section where we install and setup the new Redis instances.
+This is the section where we install and set up the new Redis instances.
->**Notes:**
-- We assume that you have installed GitLab and all HA components from scratch. If you
- already have it installed and running, read how to
- [switch from a single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha).
-- Redis nodes (both master and slaves) will need the same password defined in
- `redis['password']`. At any time during a failover the Sentinels can
- reconfigure a node and change its status from master to slave and vice versa.
+> **Notes:**
+> - We assume that you have installed GitLab and all HA components from scratch. If you
+> already have it installed and running, read how to
+> [switch from a single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha).
+> - Redis nodes (both master and slaves) will need the same password defined in
+> `redis['password']`. At any time during a failover the Sentinels can
+> reconfigure a node and change its status from master to slave and vice versa.
### Prerequisites
@@ -371,7 +370,7 @@ You must have at least `3` Redis Sentinel servers, and they need to
be each in an independent machine. You can configure them in the same
machines where you've configured the other Redis servers.
-With GitLab Enterprise Edition, you can use the Omnibus package to setup
+With GitLab Enterprise Edition, you can use the Omnibus package to set up
multiple machines with the Sentinel daemon.
---
@@ -383,9 +382,9 @@ multiple machines with the Sentinel daemon.
[Download/install](https://about.gitlab.com/downloads-ee) the
Omnibus GitLab Enterprise Edition package using **steps 1 and 2** from the
GitLab downloads page.
- - Make sure you select the correct Omnibus package, with the same version
- the GitLab application is running.
- - Do not complete any other steps on the download page.
+ - Make sure you select the correct Omnibus package, with the same version
+ the GitLab application is running.
+ - Do not complete any other steps on the download page.
1. Edit `/etc/gitlab/gitlab.rb` and add the contents (if you are installing the
Sentinels in the same node as the other Redis instances, some values might
@@ -536,7 +535,7 @@ In this example we consider that all servers have an internal network
interface with IPs in the `10.0.0.x` range, and that they can connect
to each other using these IPs.
-In a real world usage, you would also setup firewall rules to prevent
+In a real world usage, you would also set up firewall rules to prevent
unauthorized access from other machines and block traffic from the
outside (Internet).
@@ -666,7 +665,7 @@ cache, queues, and shared_state. To make this work with Sentinel:
**Note**: Redis URLs should be in the format: `redis://:PASSWORD@SENTINEL_MASTER_NAME`
1. PASSWORD is the plaintext password for the Redis instance
- 2. SENTINEL_MASTER_NAME is the Sentinel master name (e.g. `gitlab-redis-cache`)
+ 1. SENTINEL_MASTER_NAME is the Sentinel master name (e.g. `gitlab-redis-cache`)
1. Include an array of hashes with host/port combinations, such as the following:
```ruby
@@ -685,7 +684,7 @@ cache, queues, and shared_state. To make this work with Sentinel:
```
1. Note that for each persistence class, GitLab will default to using the
configuration specified in `gitlab_rails['redis_sentinels']` unless
- overriden by the settings above.
+ overridden by the settings above.
1. Be sure to include BOTH configuration options for each persistent classes. For example,
if you choose to configure a cache instance, you must specify both `gitlab_rails['redis_cache_instance']`
and `gitlab_rails['redis_cache_sentinels']` for GitLab to generate the proper configuration files.
@@ -886,8 +885,8 @@ Read more on High Availability:
[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
[gh-531]: https://github.com/redis/redis-rb/issues/531
[gh-534]: https://github.com/redis/redis-rb/issues/534
-[redis]: http://redis.io/
-[sentinel]: http://redis.io/topics/sentinel
+[redis]: https://redis.io/
+[sentinel]: https://redis.io/topics/sentinel
[omnifile]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb
[source]: ../../install/installation.md
[ce]: https://about.gitlab.com/downloads
diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md
index 8b7a515a076..2101d36d2b6 100644
--- a/doc/administration/high_availability/redis_source.md
+++ b/doc/administration/high_availability/redis_source.md
@@ -24,7 +24,7 @@ the Omnibus Redis HA documentation.
## Configuring your own Redis server
-This is the section where we install and setup the new Redis instances.
+This is the section where we install and set up the new Redis instances.
### Prerequisites
@@ -107,7 +107,7 @@ starting with `sentinel` prefix.
Assuming that the Redis Sentinel is installed on the same instance as Redis
master with IP `10.0.0.1` (some settings might overlap with the master):
-1. [Install Redis Sentinel](http://redis.io/topics/sentinel)
+1. [Install Redis Sentinel](https://redis.io/topics/sentinel)
1. Edit `/etc/redis/sentinel.conf`:
```conf
@@ -204,7 +204,7 @@ In this example we consider that all servers have an internal network
interface with IPs in the `10.0.0.x` range, and that they can connect
to each other using these IPs.
-In a real world usage, you would also setup firewall rules to prevent
+In a real world usage, you would also set up firewall rules to prevent
unauthorized access from other machines, and block traffic from the
outside ([Internet][it]).
@@ -363,7 +363,7 @@ production:
port: 26379 # point to sentinel, not to redis port
```
-When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics/sentinel).
+When in doubt, please read [Redis Sentinel documentation](https://redis.io/topics/sentinel).
[gh-531]: https://github.com/redis/redis-rb/issues/531
[downloads]: https://about.gitlab.com/downloads
diff --git a/doc/administration/img/circuitbreaker_config.png b/doc/administration/img/circuitbreaker_config.png
deleted file mode 100644
index 693b2ee9c69..00000000000
--- a/doc/administration/img/circuitbreaker_config.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/img/custom_hooks_error_msg.png b/doc/administration/img/custom_hooks_error_msg.png
index 1b3277bef16..845f0de19ce 100644
--- a/doc/administration/img/custom_hooks_error_msg.png
+++ b/doc/administration/img/custom_hooks_error_msg.png
Binary files differ
diff --git a/doc/administration/img/failing_storage.png b/doc/administration/img/failing_storage.png
deleted file mode 100644
index 82b393a58b2..00000000000
--- a/doc/administration/img/failing_storage.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/img/integration/plantuml-example.png b/doc/administration/img/integration/plantuml-example.png
index cb64eca1a8a..3e0d6389cbd 100644
--- a/doc/administration/img/integration/plantuml-example.png
+++ b/doc/administration/img/integration/plantuml-example.png
Binary files differ
diff --git a/doc/administration/img/repository_storages_admin_ui.png b/doc/administration/img/repository_storages_admin_ui.png
index 036e708cdac..5f1b4936704 100644
--- a/doc/administration/img/repository_storages_admin_ui.png
+++ b/doc/administration/img/repository_storages_admin_ui.png
Binary files differ
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 112d14652af..6083806af6c 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -46,6 +46,8 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Plugins](plugins.md): With custom plugins, GitLab administrators can introduce custom integrations without modifying GitLab's source code.
- [Enforcing Terms of Service](../user/admin_area/settings/terms.md)
- [Third party offers](../user/admin_area/settings/third_party_offers.md)
+- [Compliance](compliance.md): A collection of features from across the application that you may configure to help ensure that your GitLab instance and DevOps workflow meet compliance standards.
+- [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering size limits of branch comparison pages.
#### Customizing GitLab's appearance
@@ -59,7 +61,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Raketasks](../raketasks/README.md): Perform various tasks for maintenance, backups, automatic webhooks setup, etc.
- [Backup and restore](../raketasks/backup_restore.md): Backup and restore your GitLab instance.
-- [Operations](operations/index.md): Keeping GitLab up and running (clean up Redis sessions, moving repositories, Sidekiq Job throttling, Sidekiq MemoryKiller, Unicorn).
+- [Operations](operations/index.md): Keeping GitLab up and running (clean up Redis sessions, moving repositories, Sidekiq MemoryKiller, Unicorn).
- [Restart GitLab](restart_gitlab.md): Learn how to restart GitLab and its components.
#### Updating GitLab
@@ -93,7 +95,6 @@ created in snippets, wikis, and repos.
- [Postfix for incoming email](reply_by_email_postfix_setup.md): Set up a
basic Postfix mail server with IMAP authentication on Ubuntu for incoming
emails.
-- [User Cohorts](../user/admin_area/user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
[reply by email]: reply_by_email.md
[issues by email]: ../user/project/issues/create_new_issue.md#new-issue-via-email
@@ -117,10 +118,9 @@ created in snippets, wikis, and repos.
## Continuous Integration settings
- [Enable/disable GitLab CI/CD](../ci/enable_or_disable_ci.md#site-wide-admin-setting): Enable or disable GitLab CI/CD for your instance.
-- [GitLab CI/CD admin settings](../user/admin_area/settings/continuous_integration.md): Define max artifacts size and expiration time.
+- [GitLab CI/CD admin settings](../user/admin_area/settings/continuous_integration.md): Enable or disable Auto DevOps site-wide and define the artifacts' max size and expiration time.
- [Job artifacts](job_artifacts.md): Enable, disable, and configure job artifacts (a set of files and directories which are outputted by a job when it completes successfully).
- [Job traces](job_traces.md): Information about the job traces (logs).
-- [Artifacts size and expiration](../user/admin_area/settings/continuous_integration.md#maximum-artifacts-size): Define maximum artifacts limits and expiration date.
- [Register Shared and specific Runners](../ci/runners/README.md#registering-a-shared-runner): Learn how to register and configure Shared and specific Runners to your own instance.
- [Shared Runners pipelines quota](../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota): Limit the usage of pipeline minutes for Shared Runners.
- [Enable/disable Auto DevOps](../topics/autodevops/index.md#enabling-auto-devops): Enable or disable Auto DevOps for your instance.
@@ -130,21 +130,21 @@ created in snippets, wikis, and repos.
- [Custom Git hooks](custom_hooks.md): Custom Git hooks (on the filesystem) for when webhooks aren't enough.
- [Git LFS configuration](../workflow/lfs/lfs_administration.md): Learn how to configure LFS for GitLab.
- [Housekeeping](housekeeping.md): Keep your Git repositories tidy and fast.
+- [Configuring Git Protocol v2](git_protocol.md): Git protocol version 2 support.
## Monitoring GitLab
- [Monitoring GitLab](monitoring/index.md):
- [Monitoring uptime](../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
- - [IP whitelist](monitoring/ip_whitelist.md): Monitor endpoints that provide health check information when probed.
- - [Monitoring GitHub imports](monitoring/github_imports.md): GitLab's GitHub Importer displays Prometheus metrics to monitor the health and progress of the importer.
-- [Conversational Development (ConvDev) Index](../user/admin_area/monitoring/convdev.md): Provides an overview of your entire instance's feature usage.
+ - [IP whitelist](monitoring/ip_whitelist.md): Monitor endpoints that provide health check information when probed.
+ - [Monitoring GitHub imports](monitoring/github_imports.md): GitLab's GitHub Importer displays Prometheus metrics to monitor the health and progress of the importer.
### Performance Monitoring
- [GitLab Performance Monitoring](monitoring/performance/index.md):
- [Enable Performance Monitoring](monitoring/performance/gitlab_configuration.md): Enable GitLab Performance Monitoring.
- [GitLab performance monitoring with InfluxDB](monitoring/performance/influxdb_configuration.md): Configure GitLab and InfluxDB for measuring performance metrics.
- - [InfluxDB Schema](monitoring/performance/influxdb_schema.md): Measurements stored in InfluxDB.
+ - [InfluxDB Schema](monitoring/performance/influxdb_schema.md): Measurements stored in InfluxDB.
- [GitLab performance monitoring with Prometheus](monitoring/prometheus/index.md): Configure GitLab and Prometheus for measuring performance metrics.
- [GitLab performance monitoring with Grafana](monitoring/performance/grafana_configuration.md): Configure GitLab to visualize time series metrics through graphs and dashboards.
- [Request Profiling](monitoring/performance/request_profiling.md): Get a detailed profile on slow requests.
diff --git a/doc/administration/integration/koding.md b/doc/administration/integration/koding.md
deleted file mode 100644
index 6c1ec3028cc..00000000000
--- a/doc/administration/integration/koding.md
+++ /dev/null
@@ -1,247 +0,0 @@
-# Koding & GitLab
-
->**Notes:**
-- **As of GitLab 10.0, the Koding integration is deprecated and will be removed
- in a future version. The option to configure it is removed from GitLab's admin
- area.**
-- [Introduced][ce-5909] in GitLab 8.11.
-
-This document will guide you through installing and configuring Koding with
-GitLab.
-
-First of all, to be able to use Koding and GitLab together you will need public
-access to your server. This allows you to use single sign-on from GitLab to
-Koding and using vms from cloud providers like AWS. Koding has a registry for
-VMs, called Kontrol and it runs on the same server as Koding itself, VMs from
-cloud providers register themselves to Kontrol via the agent that we put into
-provisioned VMs. This agent is called Klient and it provides Koding to access
-and manage the target machine.
-
-Kontrol and Klient are based on another technology called
-[Kite](https://github.com/koding/kite), that we have written at Koding. Which is a
-microservice framework that allows you to develop microservices easily.
-
-## Requirements
-
-### Hardware
-
-Minimum requirements are;
-
- - 2 cores CPU
- - 3G RAM
- - 10G Storage
-
-If you plan to use AWS to install Koding it is recommended that you use at
-least a `c3.xlarge` instance.
-
-### Software
-
- - [Git](https://git-scm.com)
- - [Docker](https://www.docker.com)
- - [docker-compose](https://www.docker.com/products/docker-compose)
-
-Koding can run on most of the UNIX based operating systems, since it's shipped
-as containerized with Docker support, it can work on any operating system that
-supports Docker.
-
-Required services are:
-
-- **PostgreSQL** - Kontrol and Service DB provider
-- **MongoDB** - Main DB provider the application
-- **Redis** - In memory DB used by both application and services
-- **RabbitMQ** - Message Queue for both application and services
-
-which are also provided as a Docker container by Koding.
-
-
-## Getting Started with Development Versions
-
-
-### Koding
-
-You can run `docker-compose` environment for developing koding by
-executing commands in the following snippet.
-
-```bash
-git clone https://github.com/koding/koding.git
-cd koding
-docker-compose -f docker-compose-init.yml run init
-docker-compose up
-```
-
-This should start koding on `localhost:8090`.
-
-By default there is no team exists in Koding DB. You'll need to create a team
-called `gitlab` which is the default team name for GitLab integration in the
-configuration. To make things in order it's recommended to create the `gitlab`
-team first thing after setting up Koding.
-
-
-### GitLab
-
-To install GitLab to your environment for development purposes it's recommended
-to use GitLab Development Kit which you can get it from
-[here](https://gitlab.com/gitlab-org/gitlab-development-kit).
-
-After all those steps, gitlab should be running on `localhost:3000`
-
-
-## Integration
-
-Integration includes following components;
-
- - Single Sign On with OAuth from GitLab to Koding
- - System Hook integration for handling GitLab events on Koding
- (`project_created`, `user_joined` etc.)
- - Service endpoints for importing/executing stacks from GitLab to Koding
- (`Run/Try on IDE (Koding)` buttons on GitLab Projects, Issues, MRs)
-
-As it's pointed out before, you will need public access to this machine that
-you've installed Koding and GitLab on. Better to use a domain but a static IP
-is also fine.
-
-For IP based installation you can use [nip.io](https://nip.io) service which is
-free and provides DNS resolution to IP based requests like following;
-
- - 127.0.0.1.nip.io -> resolves to 127.0.0.1
- - foo.bar.baz.127.0.0.1.nip.io -> resolves to 127.0.0.1
- - and so on...
-
-As Koding needs subdomains for team names; `foo.127.0.0.1.nip.io` requests for
-a running koding instance on `127.0.0.1` server will be handled as `foo` team
-requests.
-
-
-### GitLab Side
-
-You need to enable Koding integration from Settings under Admin Area. To do
-that login with an Admin account and do followings;
-
- - open [http://127.0.0.1:3000/admin/application_settings](http://127.0.0.1:3000/admin/application_settings)
- - scroll to bottom of the page until Koding section
- - check `Enable Koding` checkbox
- - provide GitLab team page for running Koding instance as `Koding URL`*
-
-* For `Koding URL` you need to provide the gitlab integration enabled team on
-your Koding installation. Team called `gitlab` has integration on Koding out
-of the box, so if you didn't change anything your team on Koding should be
-`gitlab`.
-
-So, if your Koding is running on `http://1.2.3.4.nip.io:8090` your URL needs
-to be `http://gitlab.1.2.3.4.nip.io:8090`. You need to provide the same host
-with your Koding installation here.
-
-
-#### Registering Koding for OAuth integration
-
-We need `Application ID` and `Secret` to enable login to Koding via GitLab
-feature and to do that you need to register running Koding as a new application
-to your running GitLab application. Follow
-[these](http://docs.gitlab.com/ce/integration/oauth_provider.html) steps to
-enable this integration.
-
-Redirect URI should be `http://gitlab.127.0.0.1:8090/-/oauth/gitlab/callback`
-which again you need to _replace `127.0.0.1` with your instance public IP._
-
-Take a copy of `Application ID` and `Secret` that is generated by the GitLab
-application, we will need those on _Koding Part_ of this guide.
-
-
-#### Registering system hooks to Koding (optional)
-
-Koding can take actions based on the events generated by GitLab application.
-This feature is still in progress and only following events are processed by
-Koding at the moment;
-
- - user_create
- - user_destroy
-
-All system events are handled but not implemented on Koding side.
-
-To enable this feature you need to provide a `URL` and a `Secret Token` to your
-GitLab application. Open your admin area on your GitLab app from
-[http://127.0.0.1:3000/admin/hooks](http://127.0.0.1:3000/admin/hooks)
-and provide `URL` as `http://gitlab.127.0.0.1:8090/-/api/gitlab` which is the
-endpoint to handle GitLab events on Koding side. Provide a `Secret Token` and
-keep a copy of it, we will need it on _Koding Part_ of this guide.
-
-_(replace `127.0.0.1` with your instance public IP)_
-
-
-### Koding Part
-
-If you followed the steps in GitLab part we should have followings to enable
-Koding part integrations;
-
- - `Application ID` and `Secret` for OAuth integration
- - `Secret Token` for system hook integration
- - Public address of running GitLab instance
-
-
-#### Start Koding with GitLab URL
-
-Now we need to configure Koding with all this information to get things ready.
-If it's already running please stop koding first.
-
-##### From command-line
-
-Replace followings with the ones you got from GitLab part of this guide;
-
-```bash
-cd koding
-docker-compose run \
- --service-ports backend \
- /opt/koding/scripts/bootstrap-container build \
- --host=**YOUR_IP**.nip.io \
- --gitlabHost=**GITLAB_IP** \
- --gitlabPort=**GITLAB_PORT** \
- --gitlabToken=**SECRET_TOKEN** \
- --gitlabAppId=**APPLICATION_ID** \
- --gitlabAppSecret=**SECRET**
-```
-
-##### By updating configuration
-
-Alternatively you can update `gitlab` section on
-`config/credentials.default.coffee` like following;
-
-```
-gitlab =
- host: '**GITLAB_IP**'
- port: '**GITLAB_PORT**'
- applicationId: '**APPLICATION_ID**'
- applicationSecret: '**SECRET**'
- team: 'gitlab'
- redirectUri: ''
- systemHookToken: '**SECRET_TOKEN**'
- hooksEnabled: yes
-```
-
-and start by only providing the `host`;
-
-```bash
-cd koding
-docker-compose run \
- --service-ports backend \
- /opt/koding/scripts/bootstrap-container build \
- --host=**YOUR_IP**.nip.io \
-```
-
-#### Enable Single Sign On
-
-Once you restarted your Koding and logged in with your username and password
-you need to activate oauth authentication for your user. To do that
-
- - Navigate to Dashboard on Koding from;
- `http://gitlab.**YOUR_IP**.nip.io:8090/Home/my-account`
- - Scroll down to Integrations section
- - Click on toggle to turn On integration in GitLab integration section
-
-This will redirect you to your GitLab instance and will ask your permission (
-if you are not logged in to GitLab at this point you will be redirected after
-login) once you accept you will be redirected to your Koding instance.
-
-From now on you can login by using `SIGN IN WITH GITLAB` button on your Login
-screen in your Koding instance.
-
-[ce-5909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5909
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index d978d1dca53..b61c5409a56 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -74,28 +74,27 @@ our AsciiDoc snippets, wikis and repos using delimited blocks:
```plantuml
Bob -> Alice : hello
Alice -> Bob : Go Away
- ```
- </pre>
+ ```</pre>
- **AsciiDoc**
- <pre>
+ ```
[plantuml, format="png", id="myDiagram", width="200px"]
- --
+ ----
Bob->Alice : hello
Alice -> Bob : Go Away
- --
- </pre>
+ ----
+ ```
- **reStructuredText**
- <pre>
+ ```
.. plantuml::
:caption: Caption with **bold** and *italic*
Bob -> Alice: hello
Alice -> Bob: Go Away
- </pre>
+ ```
You can also use the `uml::` directive for compatibility with [sphinxcontrib-plantuml](https://pypi.python.org/pypi/sphinxcontrib-plantuml), but please note that we currently only support the `caption` option.
diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md
index e11ed58eb91..fa58d0ef15f 100644
--- a/doc/administration/integration/terminal.md
+++ b/doc/administration/integration/terminal.md
@@ -1,7 +1,6 @@
# Web terminals
->
-[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7690)
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7690)
in GitLab 8.15. Only project maintainers and owners can access web terminals.
With the introduction of the [Kubernetes integration](../../user/project/clusters/index.md),
diff --git a/doc/administration/issue_closing_pattern.md b/doc/administration/issue_closing_pattern.md
index 466bb1f851e..35f25e55414 100644
--- a/doc/administration/issue_closing_pattern.md
+++ b/doc/administration/issue_closing_pattern.md
@@ -28,7 +28,7 @@ Because Rubular doesn't understand `%{issue_ref}`, you can replace this by
expression of your liking:
```ruby
- gitlab_rails['gitlab_issue_closing_pattern'] = "((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)"
+ gitlab_rails['gitlab_issue_closing_pattern'] = "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)"
```
1. [Reconfigure] GitLab for the changes to take effect.
@@ -38,7 +38,7 @@ Because Rubular doesn't understand `%{issue_ref}`, you can replace this by
1. Change the value of `issue_closing_pattern`:
```yaml
- issue_closing_pattern: "((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)"
+ issue_closing_pattern: "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)"
```
1. [Restart] GitLab for the changes to take effect.
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 8c55c8c4298..a1ea78b64bd 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -1,12 +1,10 @@
# Jobs artifacts administration
->**Notes:**
->- Introduced in GitLab 8.2 and GitLab Runner 0.7.0.
->- Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format
- changed to `ZIP`.
->- Starting with GitLab 8.17, builds are renamed to jobs.
->- This is the administration documentation. For the user guide see
- [pipelines/job_artifacts](../user/project/pipelines/job_artifacts.md).
+> **Notes:**
+> - Introduced in GitLab 8.2 and GitLab Runner 0.7.0.
+> - Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format changed to `ZIP`.
+> - Starting with GitLab 8.17, builds are renamed to jobs.
+> - This is the administration documentation. For the user guide see [pipelines/job_artifacts](../user/project/pipelines/job_artifacts.md).
Artifacts is a list of files and directories which are attached to a job
after it completes successfully. This feature is enabled by default in all
@@ -87,18 +85,18 @@ _The artifacts are stored by default in
### Using object storage
->**Notes:**
-- [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1762) in
- [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
-- Since version 9.5, artifacts are [browsable](../user/project/pipelines/job_artifacts.md#browsing-artifacts),
- when object storage is enabled. 9.4 lacks this feature.
-- Since version 10.6, available in [GitLab Core](https://about.gitlab.com/pricing/)
-- Since version 11.0, we support `direct_upload` to S3.
+> **Notes:**
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1762) in
+> [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
+> - Since version 9.5, artifacts are [browsable](../user/project/pipelines/job_artifacts.md#browsing-artifacts),
+> when object storage is enabled. 9.4 lacks this feature.
+> - Since version 10.6, available in [GitLab Core](https://about.gitlab.com/pricing/)
+> - Since version 11.0, we support `direct_upload` to S3.
If you don't want to use the local disk where GitLab is installed to store the
artifacts, you can use an object storage like AWS S3 instead.
This configuration relies on valid AWS credentials to be configured already.
-Use an [Object storage option][os] like AWS S3 to store job artifacts.
+Use an object storage option like AWS S3 to store job artifacts.
### Object Storage Settings
@@ -127,6 +125,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
| `path_style` | Set to true to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as false for AWS S3 | false |
+| `use_iam_profile` | Set to true to use IAM profile instead of access keys | false
**In Omnibus installations:**
@@ -162,15 +161,15 @@ _The artifacts are stored by default in
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
1. Migrate any existing local artifacts to the object storage:
- ```bash
- gitlab-rake gitlab:artifacts:migrate
- ```
+ ```bash
+ gitlab-rake gitlab:artifacts:migrate
+ ```
- Currently this has to be executed manually and it will allow you to
- migrate the existing artifacts to the object storage, but all new
- artifacts will still be stored on the local disk. In the future
- you will be given an option to define a default storage artifacts for all
- new files.
+ Currently this has to be executed manually and it will allow you to
+ migrate the existing artifacts to the object storage, but all new
+ artifacts will still be stored on the local disk. In the future
+ you will be given an option to define a default storage artifacts for all
+ new files.
---
@@ -190,7 +189,7 @@ _The artifacts are stored by default in
remote_directory: "artifacts" # The bucket name
connection:
provider: AWS # Only AWS supported at the moment
- aws_access_key_id: AWS_ACESS_KEY_ID
+ aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
region: eu-central-1
```
@@ -198,15 +197,15 @@ _The artifacts are stored by default in
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Migrate any existing local artifacts to the object storage:
- ```bash
- sudo -u git -H bundle exec rake gitlab:artifacts:migrate RAILS_ENV=production
- ```
+ ```bash
+ sudo -u git -H bundle exec rake gitlab:artifacts:migrate RAILS_ENV=production
+ ```
- Currently this has to be executed manually and it will allow you to
- migrate the existing artifacts to the object storage, but all new
- artifacts will still be stored on the local disk. In the future
- you will be given an option to define a default storage artifacts for all
- new files.
+ Currently this has to be executed manually and it will allow you to
+ migrate the existing artifacts to the object storage, but all new
+ artifacts will still be stored on the local disk. In the future
+ you will be given an option to define a default storage artifacts for all
+ new files.
## Expiring artifacts
@@ -266,6 +265,7 @@ you can flip the feature flag from a Rails console.
```ruby
Feature.enable('ci_disable_validates_dependencies')
```
+
---
**In installations from source:**
@@ -313,4 +313,3 @@ memory and disk I/O.
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab"
[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
-[os]: https://docs.gitlab.com/administration/job_artifacts.html#using-object-storage
diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md
index 24d1a3fd151..63945506f3c 100644
--- a/doc/administration/job_traces.md
+++ b/doc/administration/job_traces.md
@@ -3,10 +3,6 @@
Job traces are sent by GitLab Runner while it's processing a job. You can see
traces in job pages, pipelines, email notifications, etc.
-There isn't a way to automatically expire old job logs, but it's safe to remove
-them if they're taking up too much space. If you remove the logs manually, the
-job output in the UI will be empty.
-
## Data flow
In general, there are two states in job traces: "live trace" and "archived trace".
@@ -16,8 +12,8 @@ In the following table you can see the phases a trace goes through.
| ----- | ----- | --------- | --------- | ----------- |
| 1: patching | Live trace | When a job is running | GitLab Runner => Unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`|
| 2: overwriting | Live trace | When a job is finished | GitLab Runner => Unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`|
-| 3: archiving | Archived trace | After a job is finished | Sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`|
-| 4: uploading | Archived trace | After a trace is archived | Sidekiq moves archived trace to [object storage](#uploading-traces-to-object-storage) (if configured) |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`|
+| 3: archiving | Archived trace | After a job is finished | Sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/job.log`|
+| 4: uploading | Archived trace | After a trace is archived | Sidekiq moves archived trace to [object storage](#uploading-traces-to-object-storage) (if configured) |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/job.log`|
The `ROOT_PATH` varies per your environment. For Omnibus GitLab it
would be `/var/opt/gitlab/gitlab-ci`, whereas for installations from source
@@ -57,11 +53,57 @@ To change the location where the job logs will be stored, follow the steps below
## Uploading traces to object storage
-An archived trace is considered as a [job artifact](job_artifacts.md).
-Therefore, when you [set up an object storage](job_artifacts.md#object-storage-settings),
+Archived traces are considered as [job artifacts](job_artifacts.md).
+Therefore, when you [set up the object storage integration](job_artifacts.md#object-storage-settings),
job traces are automatically migrated to it along with the other job artifacts.
-See [Data flow](#data-flow) to learn about the process.
+See "Phase 4: uploading" in [Data flow](#data-flow) to learn about the process.
+
+## How to archive legacy job trace files
+
+Legacy job traces, which were created before GitLab 10.5, were not archived regularly.
+It's the same state with the "2: overwriting" in the above [Data flow](#data-flow).
+To archive those legacy job traces, please follow the instruction below.
+
+1. Execute the following command
+
+ ```bash
+ gitlab-rake gitlab:traces:archive
+ ```
+
+ After you executed this task, GitLab instance queues up Sidekiq jobs (asynchronous processes)
+ for migrating job trace files from local storage to object storage.
+ It could take time to complete the all migration jobs. You can check the progress by the following command
+
+ ```bash
+ sudo gitlab-rails console
+ ```
+
+ ```bash
+ [1] pry(main)> Sidekiq::Stats.new.queues['pipeline_background:archive_trace']
+ => 100
+ ```
+
+ If the count becomes zero, the archiving processes are done
+
+## How to migrate archived job traces to object storage
+
+> [Introduced][ce-21193] in GitLab 11.3.
+
+If job traces have already been archived into local storage, and you want to migrate those traces to object storage, please follow the instruction below.
+
+1. Ensure [Object storage integration for Job Artifacts](job_artifacts.md#object-storage-settings) is enabled
+1. Execute the following command
+
+ ```bash
+ gitlab-rake gitlab:traces:migrate
+ ```
+
+## How to remove job traces
+
+There isn't a way to automatically expire old job logs, but it's safe to remove
+them if they're taking up too much space. If you remove the logs manually, the
+job output in the UI will be empty.
## New live trace architecture
@@ -143,15 +185,15 @@ with the legacy architecture.
In some cases, having data stored on Redis could incur data loss:
1. **Case 1: When all data in Redis are accidentally flushed**
- - On going live traces could be recovered by re-sending traces (this is
- supported by all versions of the GitLab Runner).
- - Finished jobs which have not archived live traces will lose the last part
- (~128KB) of trace data.
+ - On going live traces could be recovered by re-sending traces (this is
+ supported by all versions of the GitLab Runner).
+ - Finished jobs which have not archived live traces will lose the last part
+ (~128KB) of trace data.
1. **Case 2: When Sidekiq workers fail to archive (e.g., there was a bug that
prevents archiving process, Sidekiq inconsistency, etc.)**
- - Currently all trace data in Redis will be deleted after one week. If the
- Sidekiq workers can't finish by the expiry date, the part of trace data will be lost.
+ - Currently all trace data in Redis will be deleted after one week. If the
+ Sidekiq workers can't finish by the expiry date, the part of trace data will be lost.
Another issue that might arise is that it could consume all memory on the Redis
instance. If the number of jobs is 1000, 128MB (128KB * 1000) is consumed.
@@ -161,4 +203,5 @@ indicate that we have trace chunk. `UPDATE`s with 128KB of data is issued once w
receive multiple chunks.
[ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169
+[ce-21193]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21193
[ce-46097]: https://gitlab.com/gitlab-org/gitlab-ce/issues/46097
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index c8a3ef80e8f..7e5a3eb9ccd 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -13,7 +13,7 @@ This guide talks about how to read and use these system log files.
This file lives in `/var/log/gitlab/gitlab-rails/production_json.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/production_json.log` for
-installations from source. (When Gitlab is running in an environment
+installations from source. (When GitLab is running in an environment
other than production, the corresponding logfile is shown here.)
It contains a structured log for Rails controller requests received from
@@ -29,9 +29,9 @@ Each line contains a JSON line that can be ingested by Elasticsearch, Splunk, et
In this example, you can see this was a GET request for a specific issue. Notice each line also contains performance data:
1. `duration`: the total time taken to retrieve the request
-2. `view`: total time taken inside the Rails views
-3. `db`: total time to retrieve data from the database
-4. `gitaly_calls`: total number of calls made to Gitaly
+1. `view`: total time taken inside the Rails views
+1. `db`: total time to retrieve data from the database
+1. `gitaly_calls`: total number of calls made to Gitaly
User clone/fetch activity using http transport appears in this log as `action: git_upload_pack`.
@@ -42,7 +42,7 @@ In addition, the log contains the IP address from which the request originated
This file lives in `/var/log/gitlab/gitlab-rails/production.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/production.log` for
-installations from source. (When Gitlab is running in an environment
+installations from source. (When GitLab is running in an environment
other than production, the corresponding logfile is shown here.)
It contains information about all performed requests. You can see the
@@ -84,7 +84,7 @@ Introduced in GitLab 10.0, this file lives in
It helps you see requests made directly to the API. For example:
```json
-{"time":"2017-10-10T12:30:11.579Z","severity":"INFO","duration":16.84,"db":1.57,"view":15.27,"status":200,"method":"POST","path":"/api/v4/internal/allowed","params":{"action":"git-upload-pack","changes":"_any","gl_repository":null,"project":"root/foobar.git","protocol":"ssh","env":"{}","key_id":"[FILTERED]","secret_token":"[FILTERED]"},"host":"127.0.0.1","ip":"127.0.0.1","ua":"Ruby"}
+{"time":"2018-10-29T12:49:42.123Z","severity":"INFO","duration":709.08,"db":14.59,"view":694.49,"status":200,"method":"GET","path":"/api/v4/projects","params":[{"key":"action","value":"git-upload-pack"},{"key":"changes","value":"_any"},{"key":"key_id","value":"secret"},{"key":"secret_token","value":"[FILTERED]"}],"host":"localhost","ip":"::1","ua":"Ruby","route":"/api/:version/projects","user_id":1,"username":"root","queue_duration":100.31,"gitaly_calls":30}
```
This entry above shows an access to an internal endpoint to check whether an
@@ -113,6 +113,19 @@ October 07, 2014 11:25: User "Claudie Hodkiewicz" (nasir_stehr@olson.co.uk) was
October 07, 2014 11:25: Project "project133" was removed
```
+## `integrations_json.log`
+
+This file lives in `/var/log/gitlab/gitlab-rails/integrations_json.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/integrations_json.log` for
+installations from source.
+
+It contains information about [integrations](../user/project/integrations/project_services.md) activities such as JIRA, Asana and Irker services. It uses JSON format like the example below:
+
+``` json
+{"severity":"ERROR","time":"2018-09-06T14:56:20.439Z","service_class":"JiraService","project_id":8,"project_path":"h5bp/html5-boilerplate","message":"Error sending message","client_url":"http://jira.gitlap.com:8080","error":"execution expired"}
+{"severity":"INFO","time":"2018-09-06T17:15:16.365Z","service_class":"JiraService","project_id":3,"project_path":"namespace2/project2","message":"Successfully posted","client_url":"http://jira.example.net"}
+```
+
## `githost.log`
This file lives in `/var/log/gitlab/gitlab-rails/githost.log` for
@@ -131,6 +144,20 @@ December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/
error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
```
+## `audit_json.log`
+
+This file lives in `/var/log/gitlab/gitlab-rails/audit_json.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/audit_json.log` for
+installations from source.
+
+Changes to group or project settings are logged to this file. For example:
+
+```json
+{"severity":"INFO","time":"2018-10-17T17:38:22.523Z","author_id":3,"entity_id":2,"entity_type":"Project","change":"visibility","from":"Private","to":"Public","author_name":"John Doe4","target_id":2,"target_type":"Project","target_details":"namespace2/project2"}
+{"severity":"INFO","time":"2018-10-17T17:38:22.830Z","author_id":5,"entity_id":3,"entity_type":"Project","change":"name","from":"John Doe7 / project3","to":"John Doe7 / new name","author_name":"John Doe6","target_id":3,"target_type":"Project","target_details":"namespace3/project3"}
+{"severity":"INFO","time":"2018-10-17T17:38:23.175Z","author_id":7,"entity_id":4,"entity_type":"Project","change":"path","from":"","to":"namespace4/newpath","author_name":"John Doe8","target_id":4,"target_type":"Project","target_details":"namespace4/newpath"}
+```
+
## `sidekiq.log`
This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for
@@ -174,7 +201,7 @@ This file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log` for
Omnibus GitLab packages or in `/home/git/gitlab-shell/gitlab-shell.log` for
installations from source.
-GitLab shell is used by Gitlab for executing Git commands and provide
+GitLab shell is used by GitLab for executing Git commands and provide
SSH access to Git repositories. For example:
```
@@ -219,10 +246,19 @@ installations from source.
It logs information whenever a [repository check is run][repocheck] on a project.
+## `importer.log`
+
+Introduced in GitLab 11.3. This file lives in `/var/log/gitlab/gitlab-rails/importer.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/importer.log` for
+installations from source.
+
+Currently it logs the progress of project imports from the Bitbucket Server
+importer. Future importers may use this file.
+
## Reconfigure Logs
-Reconfigure log files live in `/var/log/gitlab/reconfigure` for Omnibus GitLab
-packages. Installations from source don't have reconfigure logs. A reconfigure log
+Reconfigure log files live in `/var/log/gitlab/reconfigure` for Omnibus GitLab
+packages. Installations from source don't have reconfigure logs. A reconfigure log
is populated whenever `gitlab-ctl reconfigure` is run manually or as part of an upgrade.
Reconfigure logs files are named according to the UNIX timestamp of when the reconfigure
diff --git a/doc/administration/monitoring/performance/img/grafana_dashboard_import.png b/doc/administration/monitoring/performance/img/grafana_dashboard_import.png
index 7761ea00522..fd639ee0eb8 100644
--- a/doc/administration/monitoring/performance/img/grafana_dashboard_import.png
+++ b/doc/administration/monitoring/performance/img/grafana_dashboard_import.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png b/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png
index 3e749eb8f9d..a98e0ed1e7d 100644
--- a/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png
+++ b/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/grafana_data_source_empty.png b/doc/administration/monitoring/performance/img/grafana_data_source_empty.png
index 33fcaaaef64..549ada8343e 100644
--- a/doc/administration/monitoring/performance/img/grafana_data_source_empty.png
+++ b/doc/administration/monitoring/performance/img/grafana_data_source_empty.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/grafana_save_icon.png b/doc/administration/monitoring/performance/img/grafana_save_icon.png
index c18f2147e9d..68a071f5ae2 100644
--- a/doc/administration/monitoring/performance/img/grafana_save_icon.png
+++ b/doc/administration/monitoring/performance/img/grafana_save_icon.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/performance_bar.png b/doc/administration/monitoring/performance/img/performance_bar.png
index 48212f6276a..2bf686f9017 100644
--- a/doc/administration/monitoring/performance/img/performance_bar.png
+++ b/doc/administration/monitoring/performance/img/performance_bar.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/performance_bar_configuration_settings.png b/doc/administration/monitoring/performance/img/performance_bar_configuration_settings.png
index 2d64ef8c5fc..fafc50cd000 100644
--- a/doc/administration/monitoring/performance/img/performance_bar_configuration_settings.png
+++ b/doc/administration/monitoring/performance/img/performance_bar_configuration_settings.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png b/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png
index 52176df9ecd..7af6d401d1d 100644
--- a/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png
+++ b/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/performance_bar_line_profiling.png b/doc/administration/monitoring/performance/img/performance_bar_line_profiling.png
index 7868e2c46d1..a55ce753101 100644
--- a/doc/administration/monitoring/performance/img/performance_bar_line_profiling.png
+++ b/doc/administration/monitoring/performance/img/performance_bar_line_profiling.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png b/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png
index 372ae021f6b..b3219b4fa94 100644
--- a/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png
+++ b/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/request_profile_result.png b/doc/administration/monitoring/performance/img/request_profile_result.png
index 8ebd74c2d3c..1b06e240fa0 100644
--- a/doc/administration/monitoring/performance/img/request_profile_result.png
+++ b/doc/administration/monitoring/performance/img/request_profile_result.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/request_profiling_token.png b/doc/administration/monitoring/performance/img/request_profiling_token.png
index 9160407e028..8c4109c17f0 100644
--- a/doc/administration/monitoring/performance/img/request_profiling_token.png
+++ b/doc/administration/monitoring/performance/img/request_profiling_token.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/influxdb_configuration.md b/doc/administration/monitoring/performance/influxdb_configuration.md
index c30cd2950d8..fa281f47ed8 100644
--- a/doc/administration/monitoring/performance/influxdb_configuration.md
+++ b/doc/administration/monitoring/performance/influxdb_configuration.md
@@ -95,10 +95,10 @@ UDP can be done using the following settings:
This does the following:
1. Enable UDP and bind it to port 8089 for all addresses.
-2. Store any data received in the "gitlab" database.
-3. Define a batch of points to be 1000 points in size and allow a maximum of
+1. Store any data received in the "gitlab" database.
+1. Define a batch of points to be 1000 points in size and allow a maximum of
5 batches _or_ flush them automatically after 1 second.
-4. Define a UDP read buffer size of 200 MB.
+1. Define a UDP read buffer size of 200 MB.
One of the most important settings here is the UDP read buffer size as if this
value is set too low, packets will be dropped. You must also make sure the OS
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 7bc92ae77ee..c9a2778b3a4 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -45,9 +45,7 @@ The following metrics are available:
| redis_ping_success | Gauge | 9.4 | Whether or not the last redis ping succeeded |
| redis_ping_latency_seconds | Gauge | 9.4 | Round trip time of the redis ping |
| user_session_logins_total | Counter | 9.4 | Counter of how many users have logged in |
-| filesystem_circuitbreaker_latency_seconds | Gauge | 9.5 | Time spent validating if a storage is accessible |
-| filesystem_circuitbreaker | Gauge | 9.5 | Whether or not the circuit for a certain shard is broken or not |
-| circuitbreaker_storage_check_duration_seconds | Histogram | 10.3 | Time a single storage probe took |
+| upload_file_does_not_exist | Counter | 10.7 in EE, 11.5 in CE | Number of times an upload record could not find its file |
| failed_login_captcha_total | Gauge | 11.0 | Counter of failed CAPTCHA attempts during login |
| successful_login_captcha_total | Gauge | 11.0 | Counter of successful CAPTCHA attempts during login |
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index 1c79e86dcb4..2d9fdedcbeb 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -1,13 +1,13 @@
-# GitLab Prometheus
+# Monitoring GitLab with Prometheus
->**Notes:**
-- Prometheus and the various exporters listed in this page are bundled in the
- Omnibus GitLab package. Check each exporter's documentation for the timeline
- they got added. For installations from source you will have to install them
- yourself. Over subsequent releases additional GitLab metrics will be captured.
-- Prometheus services are on by default with GitLab 9.0.
-- Prometheus and its exporters do not authenticate users, and will be available
- to anyone who can access them.
+> **Notes:**
+> - Prometheus and the various exporters listed in this page are bundled in the
+> Omnibus GitLab package. Check each exporter's documentation for the timeline
+> they got added. For installations from source you will have to install them
+> yourself. Over subsequent releases additional GitLab metrics will be captured.
+> - Prometheus services are on by default with GitLab 9.0.
+> - Prometheus and its exporters do not authenticate users, and will be available
+> to anyone who can access them.
[Prometheus] is a powerful time-series monitoring service, providing a flexible
platform for monitoring GitLab and other software products.
@@ -24,10 +24,10 @@ dashboard tool like [Grafana].
## Configuring Prometheus
->**Note:**
+NOTE: **Note:**
For installations from source you'll have to install and configure it yourself.
-Prometheus and it's exporters are on by default, starting with GitLab 9.0.
+Prometheus and its exporters are on by default, starting with GitLab 9.0.
Prometheus will run as the `gitlab-prometheus` user and listen on
`http://localhost:9090`. By default Prometheus is only accessible from the GitLab server itself.
Each exporter will be automatically set up as a
@@ -43,17 +43,17 @@ To disable Prometheus and all of its exporters, as well as any added in the futu
```
1. Save the file and [reconfigure GitLab][reconfigure] for the changes to
- take effect
+ take effect.
-## Changing the port and address Prometheus listens on
+### Changing the port and address Prometheus listens on
->**Note:**
+NOTE: **Note:**
The following change was added in [GitLab Omnibus 8.17][1261]. Although possible,
it's not recommended to change the port Prometheus listens
on as this might affect or conflict with other services running on the GitLab
server. Proceed at your own risk.
-In order to access Prometheus from outside the GitLab server you will need to
+In order to access Prometheus from outside the GitLab server you will need to
set a FQDN or IP in `prometheus['listen_address']`.
To change the address/port that Prometheus listens on:
@@ -77,6 +77,60 @@ To change the address/port that Prometheus listens on:
1. Save the file and [reconfigure GitLab][reconfigure] for the changes to
take effect
+### Using an external Prometheus server
+
+NOTE: **Note:**
+Prometheus and most exporters do not support authentication. We do not recommend exposing them outside the local network.
+
+A few configuration changes are required to allow GitLab to be monitored by an external Prometheus server. External servers are recommended for highly available deployments of GitLab with multiple nodes.
+
+To use an external Prometheus server:
+
+1. Edit `/etc/gitlab/gitlab.rb`.
+1. Disable the bundled Prometheus:
+
+ ```ruby
+ prometheus['enable'] = false
+ ```
+
+1. Set each bundled service's [exporter](#bundled-software-metrics) to listen on a network address, for example:
+
+ ```ruby
+ gitlab_monitor['listen_address'] = '0.0.0.0'
+ gitlab_monitor['listen_port'] = '9168'
+ gitaly['prometheus_listen_addr'] = "0.0.0.0:9236"
+ node_exporter['listen_address'] = '0.0.0.0:9100'
+ redis_exporter['listen_address'] = '0.0.0.0:9121'
+ postgres_exporter['listen_address'] = '0.0.0.0:9187'
+ ```
+
+1. Install and set up a dedicated Prometheus instance, if necessary, using the [official installation instructions](https://prometheus.io/docs/prometheus/latest/installation/).
+1. Add the Prometheus server IP address to the [monitoring IP whitelist](../ip_whitelist.html). For example:
+
+ ```ruby
+ gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '192.168.0.1']
+ ```
+
+1. [Reconfigure GitLab][reconfigure] to apply the changes
+1. Edit the Prometheus server's configuration file.
+1. Add each node's exporters to the Prometheus server's
+ [scrape target configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Cscrape_config%3E).
+ For example, a sample snippet using `static_configs`:
+
+ ```yaml
+ scrape_configs:
+ - job_name: 'gitlab_exporters'
+ static_configs:
+ - targets: ['1.1.1.1:9168', '1.1.1.1:9236', '1.1.1.1:9236', '1.1.1.1:9100', '1.1.1.1:9121', '1.1.1.1:9187']
+
+ - job_name: 'gitlab_metrics'
+ metrics_path: /-/metrics
+ static_configs:
+ - targets: ['1.1.1.1:443']
+ ```
+
+1. Restart the Prometheus server.
+
## Viewing performance metrics
You can visit `http://localhost:9090` for the dashboard that Prometheus offers by default.
@@ -86,7 +140,7 @@ If SSL has been enabled on your GitLab instance, you may not be able to access
Prometheus on the same browser as GitLab if using the same FQDN due to [HSTS][hsts]. We plan to
[provide access via GitLab][multi-user-prometheus], but in the interim there are
some workarounds: using a separate FQDN, using server IP, using a separate browser for Prometheus, resetting HSTS, or
-having [Nginx proxy it][nginx-custom-config].
+having [NGINX proxy it][nginx-custom-config].
The performance data collected by Prometheus can be viewed directly in the
Prometheus console or through a compatible dashboard tool.
@@ -97,31 +151,12 @@ For a more fully featured dashboard, Grafana can be used and has
Sample Prometheus queries:
-- **% Memory used:** `(1 - ((node_memory_MemFree + node_memory_Cached) / node_memory_MemTotal)) * 100`
-- **% CPU load:** `1 - rate(node_cpu{mode="idle"}[5m])`
-- **Data transmitted:** `irate(node_network_transmit_bytes[5m])`
-- **Data received:** `irate(node_network_receive_bytes[5m])`
-
-## Configuring Prometheus to monitor Kubernetes
-
-> Introduced in GitLab 9.0.
-> Pod monitoring introduced in GitLab 9.4.
+- **% Memory available:** `((node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) or ((node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) / node_memory_MemTotal_bytes)) * 100`
+- **% CPU utilization:** `1 - avg without (mode,cpu) (rate(node_cpu_seconds_total{mode="idle"}[5m]))`
+- **Data transmitted:** `rate(node_network_transmit_bytes_total{device!="lo"}[5m])`
+- **Data received:** `rate(node_network_receive_bytes_total{device!="lo"}[5m])`
-If your GitLab server is running within Kubernetes, Prometheus will collect metrics from the Nodes and [annotated Pods](https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config>) in the cluster, including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration][] to monitor them.
-
-To disable the monitoring of Kubernetes:
-
-1. Edit `/etc/gitlab/gitlab.rb`
-1. Add or find and uncomment the following line and set it to `false`:
-
- ```ruby
- prometheus['monitor_kubernetes'] = false
- ```
-
-1. Save the file and [reconfigure GitLab][reconfigure] for the changes to
- take effect
-
-## GitLab Prometheus metrics
+## GitLab metrics
> Introduced in GitLab 9.3.
@@ -129,17 +164,10 @@ GitLab monitors its own internal service metrics, and makes them available at th
[âž” Read more about the GitLab Metrics.](gitlab_metrics.md)
-## Prometheus exporters
-
-There are a number of libraries and servers which help in exporting existing
-metrics from third-party systems as Prometheus metrics. This is useful for cases
-where it is not feasible to instrument a given system with Prometheus metrics
-directly (for example, HAProxy or Linux system stats). You can read more in the
-[Prometheus exporters and integrations upstream documentation][prom-exporters].
+## Bundled software metrics
-While you can use any exporter you like with your GitLab installation, the
-following ones documented here are bundled in the Omnibus GitLab packages
-making it easy to configure and use.
+Many of the GitLab dependencies bundled in Omnibus GitLab are preconfigured to
+export Prometheus metrics.
### Node exporter
@@ -166,6 +194,25 @@ The GitLab monitor exporter allows you to measure various GitLab metrics, pulled
[âž” Read more about the GitLab monitor exporter.](gitlab_monitor_exporter.md)
+## Configuring Prometheus to monitor Kubernetes
+
+> Introduced in GitLab 9.0.
+> Pod monitoring introduced in GitLab 9.4.
+
+If your GitLab server is running within Kubernetes, Prometheus will collect metrics from the Nodes and [annotated Pods](https://prometheus.io/docs/operating/configuration/#kubernetes_sd_config) in the cluster, including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration][prometheus integration] to monitor them.
+
+To disable the monitoring of Kubernetes:
+
+1. Edit `/etc/gitlab/gitlab.rb`.
+1. Add or find and uncomment the following line and set it to `false`:
+
+ ```ruby
+ prometheus['monitor_kubernetes'] = false
+ ```
+
+1. Save the file and [reconfigure GitLab][reconfigure] for the changes to
+ take effect.
+
[grafana]: https://grafana.net
[hsts]: https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
[multi-user-prometheus]: https://gitlab.com/gitlab-org/multi-user-prometheus
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index eada7b19dcd..c293df3fc57 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -5,7 +5,7 @@ NOTE: **Note:** This document describes a drop-in replacement for the
using [ssh certificates](ssh_certificates.md), they are even faster,
but are not a drop-in replacement.
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1631) in
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1631) in
> [GitLab Starter](https://about.gitlab.com/gitlab-ee) 9.3.
>
> [Available in](https://gitlab.com/gitlab-org/gitlab-ee/issues/3953) GitLab
@@ -109,7 +109,7 @@ the database. The following instructions can be used to build OpenSSH 7.5:
yum install rpm-build gcc make wget openssl-devel krb5-devel pam-devel libX11-devel xmkmf libXt-devel
```
-3. Prepare the build by copying files to the right place:
+1. Prepare the build by copying files to the right place:
```
mkdir -p /root/rpmbuild/{SOURCES,SPECS}
@@ -118,7 +118,7 @@ the database. The following instructions can be used to build OpenSSH 7.5:
cd /root/rpmbuild/SPECS
```
-3. Next, set the spec settings properly:
+1. Next, set the spec settings properly:
```
sed -i -e "s/%define no_gnome_askpass 0/%define no_gnome_askpass 1/g" openssh.spec
@@ -126,13 +126,13 @@ the database. The following instructions can be used to build OpenSSH 7.5:
sed -i -e "s/BuildPreReq/BuildRequires/g" openssh.spec
```
-3. Build the RPMs:
+1. Build the RPMs:
```
rpmbuild -bb openssh.spec
```
-4. Ensure the RPMs were built:
+1. Ensure the RPMs were built:
```
ls -al /root/rpmbuild/RPMS/x86_64/
@@ -150,7 +150,7 @@ the database. The following instructions can be used to build OpenSSH 7.5:
-rw-r--r--. 1 root root 367516 Jun 20 19:37 openssh-server-7.5p1-1.x86_64.rpm
```
-5. Install the packages. OpenSSH packages will replace `/etc/pam.d/sshd`
+1. Install the packages. OpenSSH packages will replace `/etc/pam.d/sshd`
with its own version, which may prevent users from logging in, so be sure
that the file is backed up and restored after installation:
@@ -161,7 +161,7 @@ the database. The following instructions can be used to build OpenSSH 7.5:
yes | cp pam-ssh-conf-$timestamp /etc/pam.d/sshd
```
-6. Verify the installed version. In another window, attempt to login to the server:
+1. Verify the installed version. In another window, attempt to login to the server:
```
ssh -v <your-centos-machine>
@@ -171,7 +171,7 @@ the database. The following instructions can be used to build OpenSSH 7.5:
If not, you may need to restart sshd (e.g. `systemctl restart sshd.service`).
-7. *IMPORTANT!* Open a new SSH session to your server before exiting to make
+1. *IMPORTANT!* Open a new SSH session to your server before exiting to make
sure everything is working! If you need to downgrade, simple install the
older package:
diff --git a/doc/administration/operations/filesystem_benchmarking.md b/doc/administration/operations/filesystem_benchmarking.md
new file mode 100644
index 00000000000..44018e966e0
--- /dev/null
+++ b/doc/administration/operations/filesystem_benchmarking.md
@@ -0,0 +1,55 @@
+# Filesystem Performance Benchmarking
+
+Filesystem performance has a big impact on overall GitLab performance,
+especially for actions that read or write to Git repositories. This information
+will help benchmark filesystem performance against known good and bad real-world
+systems.
+
+Normally when talking about filesystem performance the biggest concern is
+with Network Filesystems (NFS). However, even some local disks can have slow
+IO. The information on this page can be used for either scenario.
+
+## Write Performance
+
+The following one-line command is a quick benchmark for filesystem write
+performance. This will write 1,000 small files to the directory in which it is
+executed.
+
+1. Change into the root of the appropriate
+ [repository storage path](../repository_storage_paths.md).
+1. Create a temporary directory for the test so it's easy to remove the files later:
+
+ ```sh
+ mkdir test; cd test
+ ```
+1. Run the command:
+
+ ```sh
+ time for i in {0..1000}; do echo 'test' > "test${i}.txt"; done
+ ```
+1. Remove the test files:
+
+ ```sh
+ cd ../; rm -rf test
+ ```
+
+The output of the `time for ...` command will look similar to the following. The
+important metric is the `real` time.
+
+```sh
+$ time for i in {0..1000}; do echo 'test' > "test${i}.txt"; done
+
+real 0m0.116s
+user 0m0.025s
+sys 0m0.091s
+```
+
+From experience with multiple customers, the following are ranges that indicate
+whether your filesystem performance is satisfactory or less than ideal:
+
+| Rating | Benchmark result |
+|:----------|:------------------------|
+| Best | Less than 10 seconds |
+| OK | 10-18 seconds |
+| Poor | 18-25 seconds |
+| Very poor | Greater than 25 seconds |
diff --git a/doc/administration/operations/img/sidekiq_job_throttling.png b/doc/administration/operations/img/sidekiq_job_throttling.png
deleted file mode 100644
index dcf40b4bf17..00000000000
--- a/doc/administration/operations/img/sidekiq_job_throttling.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/operations/img/write_to_authorized_keys_setting.png b/doc/administration/operations/img/write_to_authorized_keys_setting.png
index 232765f1917..f6227a6057b 100644
--- a/doc/administration/operations/img/write_to_authorized_keys_setting.png
+++ b/doc/administration/operations/img/write_to_authorized_keys_setting.png
Binary files differ
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index e9cad99c4b0..a16fc7ae74f 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -9,8 +9,6 @@ GitLab 7.3 we recommend cleaning up stale sessions to compact the Redis
database after you upgrade to GitLab 7.3.
- [Moving repositories](moving_repositories.md): Moving all repositories managed
by GitLab to another file system or another server.
-- [Sidekiq job throttling](sidekiq_job_throttling.md): Throttle Sidekiq queues
-that to prioritize important jobs.
- [Sidekiq MemoryKiller](sidekiq_memory_killer.md): Configure Sidekiq MemoryKiller
to restart Sidekiq.
- [Unicorn](unicorn.md): Understand Unicorn and unicorn-worker-killer.
@@ -18,3 +16,7 @@ to restart Sidekiq.
indexed lookup to the GitLab database](fast_ssh_key_lookup.md), and/or
by [doing away with user SSH keys stored on GitLab entirely in favor
of SSH certificates](ssh_certificates.md).
+- [Filesystem Performance Benchmarking](filesystem_benchmarking.md): Filesystem
+performance can have a big impact on GitLab performance, especially for actions
+that read or write Git repositories. This information will help benchmark
+filesystem performance against known good and bad real-world systems.
diff --git a/doc/administration/operations/moving_repositories.md b/doc/administration/operations/moving_repositories.md
index 54adb99386a..ec11a92db1b 100644
--- a/doc/administration/operations/moving_repositories.md
+++ b/doc/administration/operations/moving_repositories.md
@@ -22,9 +22,8 @@ However, it is not possible to resume an interrupted tar pipe: if
that happens then all data must be copied again.
```
-# As the git user
-tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
- tar -C /mnt/gitlab/repositories -xf -
+sudo -u git sh -c 'tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
+ tar -C /mnt/gitlab/repositories -xf -'
```
If you want to see progress, replace `-xf` with `-xvf`.
@@ -36,9 +35,8 @@ You can also use a tar pipe to copy data to another server. If your
can pipe the data through SSH.
```
-# As the git user
-tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
- ssh git@newserver tar -C /mnt/gitlab/repositories -xf -
+sudo -u git sh -c 'tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
+ ssh git@newserver tar -C /mnt/gitlab/repositories -xf -'
```
If you want to compress the data before it goes over the network
@@ -53,9 +51,8 @@ is either already installed on your system or easily installable
via apt, yum etc.
```
-# As the 'git' user
-rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
- /mnt/gitlab/repositories
+sudo -u git sh -c 'rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
+ /mnt/gitlab/repositories'
```
The `/.` in the command above is very important, without it you can
@@ -68,9 +65,8 @@ If the 'git' user on your source system has SSH access to the target
server you can send the repositories over the network with rsync.
```
-# As the 'git' user
-rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
- git@newserver:/mnt/gitlab/repositories
+sudo -u git sh -c 'rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
+ git@newserver:/mnt/gitlab/repositories'
```
## Thousands of Git repositories: use one rsync per repository
@@ -125,7 +121,7 @@ sudo -u git -H sh -c 'bundle exec rake gitlab:list_repos > /home/git/transfer-lo
Now we can start the transfer. The command below is idempotent, and
the number of jobs done by GNU Parallel should converge to zero. If it
-does not some repositories listed in all-repos-1234.txt may have been
+does not, some repositories listed in `all-repos-1234.txt` may have been
deleted/renamed before they could be copied.
```
@@ -155,8 +151,8 @@ cat /home/git/transfer-logs/* | sort | uniq -u |\
Suppose you have already done one sync that started after 2015-10-1 12:00 UTC.
Then you might only want to sync repositories that were changed via GitLab
-_after_ that time. You can use the 'SINCE' variable to tell 'rake
-gitlab:list_repos' to only print repositories with recent activity.
+_after_ that time. You can use the `SINCE` variable to tell `rake
+gitlab:list_repos` to only print repositories with recent activity.
```
# Omnibus
diff --git a/doc/administration/operations/sidekiq_job_throttling.md b/doc/administration/operations/sidekiq_job_throttling.md
deleted file mode 100644
index ddeaa22e288..00000000000
--- a/doc/administration/operations/sidekiq_job_throttling.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Sidekiq Job throttling
-
-> Note: Introduced with GitLab 8.14
-
-When your GitLab installation needs to handle tens of thousands of background
-jobs, it can be convenient to throttle queues that do not need to be executed
-immediately, e.g. long running jobs like Pipelines, thus allowing jobs that do
-need to be executed immediately to have access to more resources.
-
-In order to accomplish this, you can limit the amount of workers that certain
-slow running queues can have available. This is what we call Sidekiq Job
-Throttling. Depending on your infrastructure, you might have different slow
-running queues, which is why you can choose which queues you want to throttle
-and by how much you want to throttle them.
-
-These settings are available in the Application Settings of your GitLab
-installation.
-
-![Sidekiq Job Throttling](img/sidekiq_job_throttling.png)
-
-The throttle factor determines the maximum number of workers a queue can run on.
-This value gets multiplied by `:concurrency` value set in the Sidekiq settings
-and rounded up to the closest full integer.
-
-So, for example, you set the `:concurrency` to 25 and the `Throttling factor` to
-0.1, the maximum workers assigned to the selected queues would be 3.
-
-```ruby
-queue_limit = (factor * Sidekiq.options[:concurrency]).ceil
-```
-
-After enabling the job throttling, you will need to restart your GitLab
-instance, in order for the changes to take effect. \ No newline at end of file
diff --git a/doc/administration/operations/ssh_certificates.md b/doc/administration/operations/ssh_certificates.md
index 8968afba01b..b00301fec1c 100644
--- a/doc/administration/operations/ssh_certificates.md
+++ b/doc/administration/operations/ssh_certificates.md
@@ -31,7 +31,7 @@ uploading user SSH keys to GitLab entirely.
## Setting up SSH certificate lookup via GitLab Shell
-How to fully setup SSH certificates is outside the scope of this
+How to fully set up SSH certificates is outside the scope of this
document. See [OpenSSH's
PROTOCOL.certkeys](https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD)
for how it works, and e.g. [RedHat's documentation about
@@ -132,7 +132,7 @@ message about this being an invalid user.
## Interaction with the `authorized_keys` file
SSH certificates can be used in conjunction with the `authorized_keys`
-file, and if setup as configured above the `authorized_keys` file will
+file, and if set up as configured above the `authorized_keys` file will
still serve as a fallback.
This is because if the `AuthorizedPrincipalsCommand` can't
@@ -163,3 +163,20 @@ Such a restriction can currently be hacked in by e.g. providing a
custom `AuthorizedKeysCommand` which checks if the discovered key-ID
returned from `gitlab-shell-authorized-keys-check` is a deploy key or
not (all non-deploy keys should be refused).
+
+## Disabling the global warning about users lacking SSH keys
+
+By default GitLab will show a "You won't be able to pull or push
+project code via SSH" warning to users who have not uploaded an SSH
+key to their profile.
+
+This is counterproductive when using SSH certificates, since users
+aren't expected to upload their own keys.
+
+To disable this warning globally, go to "Application settings ->
+Account and limit settings" and disable the "Show user add SSH key
+message" setting.
+
+This setting was added specifically for use with SSH certificates, but
+can be turned off without using them if you'd like to hide the warning
+for some other reason.
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index eefa86f8e42..d8345f2d6bd 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -5,12 +5,13 @@ description: 'Learn how to administer GitLab Pages.'
# GitLab Pages administration
> **Notes:**
-- [Introduced][ee-80] in GitLab EE 8.3.
-- Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
-- GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17.
-- This guide is for Omnibus GitLab installations. If you have installed
- GitLab from source, follow the [Pages source installation document](source.md).
-- To learn how to use GitLab Pages, read the [user documentation][pages-userguide].
+> - [Introduced][ee-80] in GitLab EE 8.3.
+> - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
+> - GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17.
+> - This guide is for Omnibus GitLab installations. If you have installed
+> GitLab from source, follow the [Pages source installation document](source.md).
+> - To learn how to use GitLab Pages, read the [user documentation][pages-userguide].
+> - Does NOT support subgroups. See [this issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/30548) for more information and status.
This document describes how to set up the _latest_ GitLab Pages feature. Make
sure to read the [changelog](#changelog) if you are upgrading to a new GitLab
@@ -73,8 +74,8 @@ among other things.
Follow [these instructions](https://publicsuffix.org/submit/) to submit your
GitLab Pages subdomain. For instance, if your domain is `example.io`, you should
-request that `*.example.io` is added to the Public Suffix List. GitLab.com
-added `*.gitlab.io` [in 2016](https://gitlab.com/gitlab-com/infrastructure/issues/230).
+request that `example.io` is added to the Public Suffix List. GitLab.com
+added `gitlab.io` [in 2016](https://gitlab.com/gitlab-com/infrastructure/issues/230).
### DNS configuration
@@ -91,9 +92,8 @@ where `example.io` is the domain under which GitLab Pages will be served
and `192.0.2.1` is the IPv4 address of your GitLab instance and `2001::1` is the
IPv6 address. If you don't have IPv6, you can omit the AAAA record.
-> **Note:**
-You should not use the GitLab domain to serve user pages. For more information
-see the [security section](#security).
+NOTE: **Note:**
+You should not use the GitLab domain to serve user pages. For more information see the [security section](#security).
[wiki-wildcard-dns]: https://en.wikipedia.org/wiki/Wildcard_DNS_record
@@ -106,11 +106,12 @@ since that is needed in all configurations.
### Wildcard domains
->**Requirements:**
+**Requirements:**
+
- [Wildcard DNS setup](#dns-configuration)
->
->---
->
+
+---
+
URL scheme: `http://page.example.io`
This is the minimum setup that you can use Pages with. It is the base for all
@@ -125,17 +126,17 @@ The Pages daemon doesn't listen to the outside world.
1. [Reconfigure GitLab][reconfigure]
-
Watch the [video tutorial][video-admin] for this configuration.
### Wildcard domains with TLS support
->**Requirements:**
+**Requirements:**
+
- [Wildcard DNS setup](#dns-configuration)
- Wildcard TLS certificate
->
->---
->
+
+---
+
URL scheme: `https://page.example.io`
Nginx will proxy all requests to the daemon. Pages daemon doesn't listen to the
@@ -167,12 +168,13 @@ you have IPv6 as well as IPv4 addresses, you can use them both.
### Custom domains
->**Requirements:**
+**Requirements:**
+
- [Wildcard DNS setup](#dns-configuration)
- Secondary IP
->
+
---
->
+
URL scheme: `http://page.example.io` and `http://domain.com`
In that case, the Pages daemon is running, Nginx still proxies requests to
@@ -196,13 +198,14 @@ world. Custom domains are supported, but no TLS.
### Custom domains with TLS support
->**Requirements:**
+**Requirements:**
+
- [Wildcard DNS setup](#dns-configuration)
- Wildcard TLS certificate
- Secondary IP
->
+
---
->
+
URL scheme: `https://page.example.io` and `https://domain.com`
In that case, the Pages daemon is running, Nginx still proxies requests to
@@ -239,6 +242,33 @@ verification requirement. Navigate to `Admin area âž” Settings` and uncheck
**Require users to prove ownership of custom domains** in the Pages section.
This setting is enabled by default.
+### Access control
+
+Access control was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33422)
+in GitLab 11.5. It can be configured per-project, and allows access to a Pages
+site to be controlled based on a user's membership to that project.
+
+Access control works by registering the Pages daemon as an OAuth application
+with GitLab. Whenever a request to access a private Pages site is made by an
+unauthenticated user, the Pages daemon redirects the user to GitLab. If
+authentication is successful, the user is redirected back to Pages with a token,
+which is persisted in a cookie. The cookies are signed with a secret key, so
+tampering can be detected.
+
+Each request to view a resource in a private site is authenticated by Pages
+using that token. For each request it receives, it makes a request to the GitLab
+API to check that the user is authorized to read that site.
+
+Pages access control is currently disabled by default. To enable it, you must:
+
+1. Enable it in `/etc/gitlab/gitlab.rb`
+
+ ```ruby
+ gitlab_pages['access_control'] = true
+ ```
+
+1. [Reconfigure GitLab][reconfigure]
+
## Activate verbose logging for daemon
Verbose logging was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in
@@ -250,9 +280,9 @@ Follow the steps below to configure verbose logging of GitLab Pages daemon.
If you wish to make it log events with level `DEBUG` you must configure this in
`/etc/gitlab/gitlab.rb`:
- ```shell
- gitlab_pages['log_verbose'] = true
- ```
+ ```shell
+ gitlab_pages['log_verbose'] = true
+ ```
1. [Reconfigure GitLab][reconfigure]
@@ -265,9 +295,9 @@ are stored.
If you wish to store them in another location you must set it up in
`/etc/gitlab/gitlab.rb`:
- ```shell
- gitlab_rails['pages_path'] = "/mnt/storage/pages"
- ```
+ ```shell
+ gitlab_rails['pages_path'] = "/mnt/storage/pages"
+ ```
1. [Reconfigure GitLab][reconfigure]
@@ -278,19 +308,19 @@ Omnibus GitLab 11.1.
1. By default the listener is configured to listen for requests on `localhost:8090`.
- If you wish to disable it you must configure this in
- `/etc/gitlab/gitlab.rb`:
+ If you wish to disable it you must configure this in
+ `/etc/gitlab/gitlab.rb`:
- ```shell
- gitlab_pages['listen_proxy'] = nil
- ```
+ ```shell
+ gitlab_pages['listen_proxy'] = nil
+ ```
- If you wish to make it listen on a different port you must configure this also in
- `/etc/gitlab/gitlab.rb`:
+ If you wish to make it listen on a different port you must configure this also in
+ `/etc/gitlab/gitlab.rb`:
- ```shell
- gitlab_pages['listen_proxy'] = "localhost:10080"
- ```
+ ```shell
+ gitlab_pages['listen_proxy'] = "localhost:10080"
+ ```
1. [Reconfigure GitLab][reconfigure]
@@ -319,12 +349,12 @@ latest previous version.
---
-**GitLab 8.17 ([documentation][8-17-docs])**
+**GitLab 8.17 ([documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable/doc/administration/pages/index.md))**
- GitLab Pages were ported to Community Edition in GitLab 8.17.
- Documentation was refactored to be more modular and easy to follow.
-**GitLab 8.5 ([documentation][8-5-docs])**
+**GitLab 8.5 ([documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/8-5-stable-ee/doc/pages/administration.md))**
- In GitLab 8.5 we introduced the [gitlab-pages][] daemon which is now the
recommended way to set up GitLab Pages.
@@ -333,13 +363,10 @@ latest previous version.
- Custom CNAME and TLS certificates support.
- Documentation was moved to one place.
-**GitLab 8.3 ([documentation][8-3-docs])**
+**GitLab 8.3 ([documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/8-3-stable-ee/doc/pages/administration.md))**
- GitLab Pages feature was introduced.
-[8-3-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-3-stable-ee/doc/pages/administration.md
-[8-5-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-5-stable-ee/doc/pages/administration.md
-[8-17-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable-ce/doc/administration/pages/index.md
[backup]: ../../raketasks/backup_restore.md
[ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605
[ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80
diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md
index 4e40a7cb18d..ddff54be575 100644
--- a/doc/administration/pages/source.md
+++ b/doc/administration/pages/source.md
@@ -89,11 +89,11 @@ since that is needed in all configurations.
### Wildcard domains
>**Requirements:**
-- [Wildcard DNS setup](#dns-configuration)
+> - [Wildcard DNS setup](#dns-configuration)
>
->---
+> ---
>
-URL scheme: `http://page.example.io`
+> URL scheme: `http://page.example.io`
This is the minimum setup that you can use Pages with. It is the base for all
other setups as described below. Nginx will proxy all requests to the daemon.
@@ -111,24 +111,24 @@ The Pages daemon doesn't listen to the outside world.
1. Go to the GitLab installation directory:
- ```bash
- cd /home/git/gitlab
- ```
+ ```bash
+ cd /home/git/gitlab
+ ```
1. Edit `gitlab.yml` and under the `pages` setting, set `enabled` to `true` and
the `host` to the FQDN under which GitLab Pages will be served:
- ```yaml
- ## GitLab Pages
- pages:
- enabled: true
- # The location where pages are stored (default: shared/pages).
- # path: shared/pages
+ ```yaml
+ ## GitLab Pages
+ pages:
+ enabled: true
+ # The location where pages are stored (default: shared/pages).
+ # path: shared/pages
- host: example.io
- port: 80
- https: false
- ```
+ host: example.io
+ port: 80
+ https: false
+ ```
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
order to enable the pages daemon. In `gitlab_pages_options` the
@@ -151,13 +151,13 @@ The Pages daemon doesn't listen to the outside world.
### Wildcard domains with TLS support
->**Requirements:**
-- [Wildcard DNS setup](#dns-configuration)
-- Wildcard TLS certificate
+> **Requirements:**
+> - [Wildcard DNS setup](#dns-configuration)
+> - Wildcard TLS certificate
>
->---
+> ---
>
-URL scheme: `https://page.example.io`
+> URL scheme: `https://page.example.io`
Nginx will proxy all requests to the daemon. Pages daemon doesn't listen to the
outside world.
@@ -174,17 +174,17 @@ outside world.
1. In `gitlab.yml`, set the port to `443` and https to `true`:
- ```bash
- ## GitLab Pages
- pages:
- enabled: true
- # The location where pages are stored (default: shared/pages).
- # path: shared/pages
-
- host: example.io
- port: 443
- https: true
- ```
+ ```bash
+ ## GitLab Pages
+ pages:
+ enabled: true
+ # The location where pages are stored (default: shared/pages).
+ # path: shared/pages
+
+ host: example.io
+ port: 443
+ https: true
+ ```
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
order to enable the pages daemon. In `gitlab_pages_options` the
@@ -216,13 +216,13 @@ that without TLS certificates.
### Custom domains
->**Requirements:**
-- [Wildcard DNS setup](#dns-configuration)
-- Secondary IP
+> **Requirements:**
+> - [Wildcard DNS setup](#dns-configuration)
+> - Secondary IP
>
----
+> ---
>
-URL scheme: `http://page.example.io` and `http://domain.com`
+> URL scheme: `http://page.example.io` and `http://domain.com`
In that case, the pages daemon is running, Nginx still proxies requests to
the daemon but the daemon is also able to receive requests from the outside
@@ -243,18 +243,18 @@ world. Custom domains are supported, but no TLS.
`external_http` to the secondary IP on which the pages daemon will listen
for connections:
- ```yaml
- pages:
- enabled: true
- # The location where pages are stored (default: shared/pages).
- # path: shared/pages
+ ```yaml
+ pages:
+ enabled: true
+ # The location where pages are stored (default: shared/pages).
+ # path: shared/pages
- host: example.io
- port: 80
- https: false
+ host: example.io
+ port: 80
+ https: false
- external_http: 192.0.2.2:80
- ```
+ external_http: 192.0.2.2:80
+ ```
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
order to enable the pages daemon. In `gitlab_pages_options` the
@@ -281,14 +281,14 @@ world. Custom domains are supported, but no TLS.
### Custom domains with TLS support
->**Requirements:**
-- [Wildcard DNS setup](#dns-configuration)
-- Wildcard TLS certificate
-- Secondary IP
+> **Requirements:**
+> - [Wildcard DNS setup](#dns-configuration)
+> - Wildcard TLS certificate
+> - Secondary IP
>
----
+> ---
>
-URL scheme: `https://page.example.io` and `https://domain.com`
+> URL scheme: `https://page.example.io` and `https://domain.com`
In that case, the pages daemon is running, Nginx still proxies requests to
the daemon but the daemon is also able to receive requests from the outside
@@ -309,20 +309,20 @@ world. Custom domains and TLS are supported.
`external_http` and `external_https` to the secondary IP on which the pages
daemon will listen for connections:
- ```yaml
- ## GitLab Pages
- pages:
- enabled: true
- # The location where pages are stored (default: shared/pages).
- # path: shared/pages
+ ```yaml
+ ## GitLab Pages
+ pages:
+ enabled: true
+ # The location where pages are stored (default: shared/pages).
+ # path: shared/pages
- host: example.io
- port: 443
- https: true
+ host: example.io
+ port: 443
+ https: true
- external_http: 192.0.2.2:80
- external_https: 192.0.2.2:443
- ```
+ external_http: 192.0.2.2:80
+ external_https: 192.0.2.2:443
+ ```
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
order to enable the pages daemon. In `gitlab_pages_options` the
@@ -358,9 +358,9 @@ are stored.
If you wish to store them in another location you must set it up in
`/etc/gitlab/gitlab.rb`:
- ```ruby
- gitlab_rails['pages_path'] = "/mnt/storage/pages"
- ```
+ ```ruby
+ gitlab_rails['pages_path'] = "/mnt/storage/pages"
+ ```
1. [Reconfigure GitLab][reconfigure]
@@ -391,6 +391,44 @@ the first one with a backslash (\). For example `pages.example.io` would be:
server_name ~^.*\.pages\.example\.io$;
```
+## Access control
+
+Access control was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33422)
+in GitLab 11.5. It can be configured per-project, and allows access to a Pages
+site to be controlled based on a user's membership to that project.
+
+Access control works by registering the Pages daemon as an OAuth application
+with GitLab. Whenever a request to access a private Pages site is made by an
+unauthenticated user, the Pages daemon redirects the user to GitLab. If
+authentication is successful, the user is redirected back to Pages with a token,
+which is persisted in a cookie. The cookies are signed with a secret key, so
+tampering can be detected.
+
+Each request to view a resource in a private site is authenticated by Pages
+using that token. For each request it receives, it makes a request to the GitLab
+API to check that the user is authorized to read that site.
+
+Pages access control is currently disabled by default. To enable it, you must:
+
+1. Modify your `config/gitlab.yml` file:
+ ```yaml
+ pages:
+ access_control: true
+ ```
+1. [Restart GitLab][restart]
+1. Create a new [system OAuth application](../../integration/oauth_provider.md#adding-an-application-through-the-profile)
+ This should be called `GitLab Pages` and have a `Redirect URL` of
+ `https://projects.example.io/auth`. It does not need to be a "trusted"
+ application, but it does need the "api" scope.
+1. Start the Pages daemon with the following additional arguments:
+
+ ```shell
+ -auth-client-secret <OAuth code generated by GitLab> \
+ -auth-redirect-uri http://projects.example.io/auth \
+ -auth-secret <40 random hex characters> \
+ -auth-server <URL of the GitLab instance>
+ ```
+
## Change storage path
Follow the steps below to change the default path where GitLab Pages' contents
@@ -400,12 +438,12 @@ are stored.
If you wish to store them in another location you must set it up in
`gitlab.yml` under the `pages` section:
- ```yaml
- pages:
- enabled: true
- # The location where pages are stored (default: shared/pages).
- path: /mnt/storage/pages
- ```
+ ```yaml
+ pages:
+ enabled: true
+ # The location where pages are stored (default: shared/pages).
+ path: /mnt/storage/pages
+ ```
1. [Restart GitLab][restart]
diff --git a/doc/administration/raketasks/github_import.md b/doc/administration/raketasks/github_import.md
index 6b8ad1b039b..ccd9c0afb1d 100644
--- a/doc/administration/raketasks/github_import.md
+++ b/doc/administration/raketasks/github_import.md
@@ -1,37 +1,41 @@
# GitHub import
->**Note:**
->
-> - [Introduced][ce-10308] in GitLab 9.1.
-> - You need a personal access token in order to retrieve and import GitHub
-> projects. You can get it from: https://github.com/settings/tokens
-> - You also need to pass an username as the second argument to the rake task
-> which will become the owner of the project.
-> - You can also resume an import with the same command.
+> [Introduced]( https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10308) in GitLab 9.1.
+
+In order to retrieve and import GitHub repositories, you will need a
+[GitHub personal access token](https://github.com/settings/tokens).
+A username should be passed as the second argument to the rake task
+which will become the owner of the project. You can resume an import
+with the same command.
+
+Bear in mind that the syntax is very specific. Remove any spaces within the argument block and
+before/after the brackets. Also, Some shells (e.g., zsh) can interpret the open/close brackets
+(`[]`) separately. You may need to either escape the brackets or use double quotes.
+
+## Importing multiple projects
To import a project from the list of your GitHub projects available:
```bash
# Omnibus installations
-sudo gitlab-rake import:github[access_token,root,foo/bar]
+sudo gitlab-rake "import:github[access_token,root,foo/bar]"
# Installations from source
-bundle exec rake import:github[access_token,root,foo/bar] RAILS_ENV=production
+bundle exec rake "import:github[access_token,root,foo/bar]" RAILS_ENV=production
```
In this case, `access_token` is your GitHub personal access token, `root`
is your GitLab username, and `foo/bar` is the new GitLab namespace/project that
will get created from your GitHub project. Subgroups are also possible: `foo/foo/bar`.
+## Importing a single project
To import a specific GitHub project (named `foo/github_repo` here):
```bash
# Omnibus installations
-sudo gitlab-rake import:github[access_token,root,foo/bar,foo/github_repo]
+sudo gitlab-rake "import:github[access_token,root,foo/bar,foo/github_repo]"
# Installations from source
-bundle exec rake import:github[access_token,root,foo/bar,foo/github_repo] RAILS_ENV=production
+bundle exec rake "import:github[access_token,root,foo/bar,foo/github_repo]" RAILS_ENV=production
```
-
-[ce-10308]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10308
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index 2b4252a7b1d..0d863594fc7 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -53,14 +53,15 @@ Git: /usr/bin/git
Runs the following rake tasks:
- `gitlab:gitlab_shell:check`
+- `gitlab:gitaly:check`
- `gitlab:sidekiq:check`
- `gitlab:app:check`
-It will check that each component was setup according to the installation guide and suggest fixes for issues found.
+It will check that each component was set up according to the installation guide and suggest fixes for issues found.
-You may also have a look at our Trouble Shooting Guides:
-- [Trouble Shooting Guide (GitLab)](http://docs.gitlab.com/ee/README.html#troubleshooting)
-- [Trouble Shooting Guide (Omnibus Gitlab)](http://docs.gitlab.com/omnibus/README.html#troubleshooting)
+You may also have a look at our Troubleshooting Guides:
+- [Troubleshooting Guide (GitLab)](http://docs.gitlab.com/ee/README.html#troubleshooting)
+- [Troubleshooting Guide (Omnibus Gitlab)](http://docs.gitlab.com/omnibus/README.html#troubleshooting)
**Omnibus Installation**
@@ -252,7 +253,7 @@ clear it.
To clear all exclusive leases:
-DANGER: **DANGER**:
+DANGER: **DANGER**:
Don't run it while GitLab or Sidekiq is running
```bash
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index 7bd765a35e0..f43bba0a7a7 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -9,6 +9,7 @@
> application settings (`/admin/application_settings`) under 'Import sources'.
> - The exports are stored in a temporary [shared directory][tmp] and are deleted
> every 24 hours by a specific worker.
+> - ImportExport can use object storage automatically starting from GitLab 11.3
The GitLab Import/Export version can be checked by using:
@@ -30,12 +31,6 @@ sudo gitlab-rake gitlab:import_export:data
bundle exec rake gitlab:import_export:data RAILS_ENV=production
```
-In order to enable Object Storage on the Export, you can use the [feature flag][feature-flags]:
-
-```
-import_export_object_storage
-```
-
[ce-3050]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3050
[feature-flags]: https://docs.gitlab.com/ee/api/features.html
[tmp]: ../../development/shared_files.md
diff --git a/doc/administration/raketasks/uploads/migrate.md b/doc/administration/raketasks/uploads/migrate.md
index 0cd33ffc122..b5c40478ea5 100644
--- a/doc/administration/raketasks/uploads/migrate.md
+++ b/doc/administration/raketasks/uploads/migrate.md
@@ -7,10 +7,32 @@ After [configuring the object storage](../../uploads.md#using-object-storage) fo
>**Note:**
All of the processing will be done in a background worker and requires **no downtime**.
-This tasks uses 3 parameters to find uploads to migrate.
+### All-in-one rake task
+
+GitLab provides a wrapper rake task that migrates all uploaded files - avatars,
+logos, attachments, favicon, etc. - to object storage in one go. Under the hood,
+it invokes individual rake tasks to migrate files falling under each of this
+category one by one. The specifications of these individual rake tasks are
+described in the next section.
+
+**Omnibus Installation**
+
+```bash
+gitlab-rake "gitlab:uploads:migrate:all"
+```
+
+**Source Installation**
+
+```bash
+sudo RAILS_ENV=production -u git -H bundle exec rake gitlab:uploads:migrate:all
+```
+
+### Individual rake tasks
>**Note:**
-These parameters are mainly internal to GitLab's structure, you may want to refer to the task list instead below.
+If you already ran the rake task mentioned above, no need to run these individual rake tasks as that has been done automatically.
+
+The rake task uses 3 parameters to find uploads to migrate.
Parameter | Type | Description
--------- | ---- | -----------
@@ -18,6 +40,9 @@ Parameter | Type | Description
`model_class` | string | Type of the model to migrate from
`mount_point` | string/symbol | Name of the model's column on which the uploader is mounted on.
+>**Note:**
+These parameters are mainly internal to GitLab's structure, you may want to refer to the task list instead below.
+
This task also accepts some environment variables which you can use to override
certain values:
@@ -25,7 +50,7 @@ Variable | Type | Description
-------- | ---- | -----------
`BATCH` | integer | Specifies the size of the batch. Defaults to 200.
-** Omnibus Installation**
+**Omnibus Installation**
```bash
# gitlab-rake gitlab:uploads:migrate[uploader_class, model_class, mount_point]
@@ -40,6 +65,9 @@ gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Note, :attachment]"
gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :logo]"
gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :header_logo]"
+# Favicon
+gitlab-rake "gitlab:uploads:migrate[FaviconUploader, Appearance, :favicon]"
+
# Markdown
gitlab-rake "gitlab:uploads:migrate[FileUploader, Project]"
gitlab-rake "gitlab:uploads:migrate[PersonalFileUploader, Snippet]"
@@ -65,6 +93,9 @@ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Note
sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :logo]"
sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :header_logo]"
+# Favicon
+sudo -u git -H bundle exec rake "gitlab:uploads:migrate[FaviconUploader, Appearance, :favicon]"
+
# Markdown
sudo -u git -H bundle exec rake "gitlab:uploads:migrate[FileUploader, Project]"
sudo -u git -H bundle exec rake "gitlab:uploads:migrate[PersonalFileUploader, Snippet]"
diff --git a/doc/administration/reply_by_email.md b/doc/administration/reply_by_email.md
index 426245c7aca..6d7069dd461 100644
--- a/doc/administration/reply_by_email.md
+++ b/doc/administration/reply_by_email.md
@@ -5,7 +5,7 @@ replying to notification emails.
## Requirement
-Make sure [incoming email](incoming_email.md) is setup.
+Make sure [incoming email](incoming_email.md) is set up.
## How it works?
diff --git a/doc/administration/reply_by_email_postfix_setup.md b/doc/administration/reply_by_email_postfix_setup.md
index 3e8b78e56d5..4c42cb7756a 100644
--- a/doc/administration/reply_by_email_postfix_setup.md
+++ b/doc/administration/reply_by_email_postfix_setup.md
@@ -8,7 +8,7 @@ The instructions make the assumption that you will be using the email address `i
## Configure your server firewall
1. Open up port 25 on your server so that people can send email into the server over SMTP.
-2. If the mail server is different from the server running GitLab, open up port 143 on your server so that GitLab can read email from the server over IMAP.
+1. If the mail server is different from the server running GitLab, open up port 143 on your server so that GitLab can read email from the server over IMAP.
## Install packages
@@ -245,7 +245,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
220 gitlab.example.com ESMTP Postfix (Ubuntu)
```
- If you get a `Connection refused` error instead, make sure your firewall is setup to allow inbound traffic on port 25.
+ If you get a `Connection refused` error instead, make sure your firewall is set up to allow inbound traffic on port 25.
1. Send the `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt:
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index efeec9db517..8b725e50f58 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -18,7 +18,8 @@ repositories and wiki repositories in order to detect data corruption.
A project will be checked no more than once per month. If any projects
fail their repository checks all GitLab administrators will receive an email
notification of the situation. This notification is sent out once a week,
-by default, midnight at the start of Sunday.
+by default, midnight at the start of Sunday. Repositories with known check
+failures can be found at `/admin/projects?last_repository_check_failed=1`.
## Disabling periodic checks
@@ -30,7 +31,7 @@ panel.
If the repository check fails for some repository you should look up the error
in `repocheck.log`:
-- in the [admin panel](logs.md#repocheck.log)
+- in the [admin panel](logs.md#repocheck-log)
- or on disk, see:
- `/var/log/gitlab/gitlab-rails` for Omnibus installations
- `/home/git/gitlab/log` for installations from source
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index 96f436fa7c3..c03f23a8931 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -5,36 +5,36 @@
GitLab allows you to define multiple repository storage paths to distribute the
storage load between several mount points.
->**Notes:**
+> **Notes:**
>
-- You must have at least one storage path called `default`.
-- The paths are defined in key-value pairs. The key is an arbitrary name you
- can pick to name the file path.
-- The target directories and any of its subpaths must not be a symlink.
+> - You must have at least one storage path called `default`.
+> - The paths are defined in key-value pairs. The key is an arbitrary name you
+> can pick to name the file path.
+> - The target directories and any of its subpaths must not be a symlink.
## Configure GitLab
->**Warning:**
-In order for [backups] to work correctly, the storage path must **not** be a
-mount point and the GitLab user should have correct permissions for the parent
-directory of the path. In Omnibus GitLab this is taken care of automatically,
-but for source installations you should be extra careful.
+> **Warning:**
+> In order for [backups] to work correctly, the storage path must **not** be a
+> mount point and the GitLab user should have correct permissions for the parent
+> directory of the path. In Omnibus GitLab this is taken care of automatically,
+> but for source installations you should be extra careful.
>
-The thing is that for compatibility reasons `gitlab.yml` has a different
-structure than Omnibus. In `gitlab.yml` you indicate the path for the
-repositories, for example `/home/git/repositories`, while in Omnibus you
-indicate `git_data_dirs`, which for the example above would be `/home/git`.
-Then, Omnibus will create a `repositories` directory under that path to use with
-`gitlab.yml`.
+> The thing is that for compatibility reasons `gitlab.yml` has a different
+> structure than Omnibus. In `gitlab.yml` you indicate the path for the
+> repositories, for example `/home/git/repositories`, while in Omnibus you
+> indicate `git_data_dirs`, which for the example above would be `/home/git`.
+> Then, Omnibus will create a `repositories` directory under that path to use with
+> `gitlab.yml`.
>
-This little detail matters because while restoring a backup, the current
-contents of `/home/git/repositories` [are moved to][raketask] `/home/git/repositories.old`,
-so if `/home/git/repositories` is the mount point, then `mv` would be moving
-things between mount points, and bad things could happen. Ideally,
-`/home/git` would be the mount point, so then things would be moving within the
-same mount point. This is guaranteed with Omnibus installations (because they
-don't specify the full repository path but the parent path), but not for source
-installations.
+> This little detail matters because while restoring a backup, the current
+> contents of `/home/git/repositories` [are moved to][raketask] `/home/git/repositories.old`,
+> so if `/home/git/repositories` is the mount point, then `mv` would be moving
+> things between mount points, and bad things could happen. Ideally,
+> `/home/git` would be the mount point, so then things would be moving within the
+> same mount point. This is guaranteed with Omnibus installations (because they
+> don't specify the full repository path but the parent path), but not for source
+> installations.
---
@@ -97,55 +97,6 @@ be stored via the **Application Settings** in the Admin area.
Beginning with GitLab 8.13.4, multiple paths can be chosen. New projects will be
randomly placed on one of the selected paths.
-## Handling failing repository storage
-
-> [Introduced][ce-11449] in GitLab 9.5.
-
-When GitLab detects access to the repositories storage fails repeatedly, it can
-gracefully prevent attempts to access the storage. This might be useful when
-the repositories are stored somewhere on the network.
-
-This can be configured from the admin interface:
-
-![circuitbreaker configuration](img/circuitbreaker_config.png)
-
-**Number of access attempts**: The number of attempts GitLab will make to access a
-storage when probing a shard.
-
-**Number of failures before backing off**: The number of failures after which
-GitLab will start temporarily disabling access to a storage shard on a host.
-
-**Maximum git storage failures:** The number of failures of after which GitLab will
-completely prevent access to the storage. The number of failures can be reset in
-the admin interface: `https://gitlab.example.com/admin/health_check` or using the
-[api](../api/repository_storage_health.md) to allow access to the storage again.
-
-**Seconds to wait after a storage failure:** When access to a storage fails. GitLab
-will prevent access to the storage for the time specified here. This allows the
-filesystem to recover.
-
-**Seconds before reseting failure information:** The time in seconds GitLab will
-keep failure information. When no failures occur during this time, information about the
-mount is reset.
-
-**Seconds to wait for a storage access attempt:** The time in seconds GitLab will
-try to access storage. After this time a timeout error will be raised.
-
-To enable the circuitbreaker for repository storage you can flip the feature flag from a rails console:
-
-```
-Feature.enable('git_storage_circuit_breaker')
-```
-
-Alternatively it can be enabled by setting `true` in the `GIT_STORAGE_CIRCUIT_BREAKER` environment variable.
-This approach would be used when enabling the circuit breaker on a single host.
-
-When storage failures occur, this will be visible in the admin interface like this:
-
-![failing storage](img/failing_storage.png)
-
-To allow access to all storages, click the `Reset git storage health information` button.
-
[ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578
[restart-gitlab]: restart_gitlab.md#installations-from-source
[reconfigure-gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 88221db78f1..9379944b250 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -41,11 +41,8 @@ Registry, etc.
## Hashed Storage
-> **Warning:** Hashed storage is in **Beta**. For the latest updates, check the
-> associated [issue](https://gitlab.com/gitlab-com/infrastructure/issues/2821)
-> and please report any problems you encounter.
-Hashed Storage is the new storage behavior we are rolling out with 10.0. Instead
+Hashed Storage is the new storage behavior we rolled out with 10.0. Instead
of coupling project URL and the folder structure where the repository will be
stored on disk, we are coupling a hash, based on the project's ID. This makes
the folder structure immutable, and therefore eliminates any requirement to
diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md
index 2902af8c782..643c5b9fe80 100644
--- a/doc/administration/troubleshooting/debug.md
+++ b/doc/administration/troubleshooting/debug.md
@@ -20,7 +20,7 @@ an SMTP server, but you're not seeing mail delivered. Here's how to check the se
bundle exec rails console production
```
-2. Look at the ActionMailer `delivery_method` to make sure it matches what you
+1. Look at the ActionMailer `delivery_method` to make sure it matches what you
intended. If you configured SMTP, it should say `:smtp`. If you're using
Sendmail, it should say `:sendmail`:
@@ -29,7 +29,7 @@ an SMTP server, but you're not seeing mail delivered. Here's how to check the se
=> :smtp
```
-3. If you're using SMTP, check the mail settings:
+1. If you're using SMTP, check the mail settings:
```ruby
irb(main):002:0> ActionMailer::Base.smtp_settings
@@ -39,7 +39,7 @@ an SMTP server, but you're not seeing mail delivered. Here's how to check the se
In the example above, the SMTP server is configured for the local machine. If this is intended, you may need to check your local mail
logs (e.g. `/var/log/mail.log`) for more details.
-4. Send a test message via the console.
+1. Send a test message via the console.
```ruby
irb(main):003:0> Notify.test_email('youremail@email.com', 'Hello World', 'This is a test message').deliver_now
diff --git a/doc/administration/troubleshooting/sidekiq.md b/doc/administration/troubleshooting/sidekiq.md
index 9d157720ad2..7067958ecb4 100644
--- a/doc/administration/troubleshooting/sidekiq.md
+++ b/doc/administration/troubleshooting/sidekiq.md
@@ -8,15 +8,15 @@ may not show up and merge requests may not be updated. The following are some
troubleshooting steps that will help you diagnose the bottleneck.
> **Note:** GitLab administrators/users should consider working through these
-debug steps with GitLab Support so the backtraces can be analyzed by our team.
-It may reveal a bug or necessary improvement in GitLab.
-
+> debug steps with GitLab Support so the backtraces can be analyzed by our team.
+> It may reveal a bug or necessary improvement in GitLab.
+>
> **Note:** In any of the backtraces, be wary of suspecting cases where every
- thread appears to be waiting in the database, Redis, or waiting to acquire
- a mutex. This **may** mean there's contention in the database, for example,
- but look for one thread that is different than the rest. This other thread
- may be using all available CPU, or have a Ruby Global Interpreter Lock,
- preventing other threads from continuing.
+> thread appears to be waiting in the database, Redis, or waiting to acquire
+> a mutex. This **may** mean there's contention in the database, for example,
+> but look for one thread that is different than the rest. This other thread
+> may be using all available CPU, or have a Ruby Global Interpreter Lock,
+> preventing other threads from continuing.
## Thread dump
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index 77e73b23021..f85a1f791f9 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -18,7 +18,7 @@ below.
>**Notes:**
For historical reasons, uploads are stored into a base directory, which by default is `uploads/-/system`. It is strongly discouraged to change this configuration option on an existing GitLab installation.
-_The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/public/uploads/-/system`._
+_The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads/-/system`._
1. To change the storage path for example to `/mnt/storage/uploads`, edit
`/etc/gitlab/gitlab.rb` and add the following line:
@@ -48,11 +48,13 @@ _The uploads are stored by default in
1. Save the file and [restart GitLab][] for the changes to take effect.
-### Using object storage
+### Using object storage **[CORE ONLY]**
->**Notes:**
-- [Introduced][ee-3867] in [GitLab Enterprise Edition Premium][eep] 10.5.
-- Since version 11.1, we support direct_upload to S3.
+> **Notes:**
+>
+> - [Introduced][ee-3867] in [GitLab Premium][eep] 10.5.
+> - [Introduced][ce17358] in [GitLab Core][ce] 10.7.
+> - Since version 11.1, we support direct_upload to S3.
If you don't want to use the local disk where GitLab is installed to store the
uploads, you can use an object storage provider like AWS S3 instead.
@@ -85,6 +87,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
| `path_style` | Set to true to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as false for AWS S3 | false |
+| `use_iam_profile` | Set to true to use IAM profile instead of access keys | false
**In Omnibus installations:**
@@ -105,8 +108,8 @@ _The uploads are stored by default in
}
```
->**Note:**
-If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs.
+ >**Note:**
+ If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs.
```ruby
gitlab_rails['uploads_object_store_connection'] = {
@@ -119,28 +122,28 @@ If you are using AWS IAM profiles, be sure to omit the AWS access key and secret
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
1. Migrate any existing local uploads to the object storage:
->**Notes:**
-These task complies with the `BATCH` environment variable to process uploads in batch (200 by default). All of the processing will be done in a background worker and requires **no downtime**.
-
- ```bash
- # gitlab-rake gitlab:uploads:migrate[uploader_class, model_class, mount_point]
-
- # Avatars
- gitlab-rake "gitlab:uploads:migrate[AvatarUploader, Project, :avatar]"
- gitlab-rake "gitlab:uploads:migrate[AvatarUploader, Group, :avatar]"
- gitlab-rake "gitlab:uploads:migrate[AvatarUploader, User, :avatar]"
-
- # Attachments
- gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Note, :attachment]"
- gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :logo]"
- gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :header_logo]"
-
- # Markdown
- gitlab-rake "gitlab:uploads:migrate[FileUploader, Project]"
- gitlab-rake "gitlab:uploads:migrate[PersonalFileUploader, Snippet]"
- gitlab-rake "gitlab:uploads:migrate[NamespaceFileUploader, Snippet]"
- gitlab-rake "gitlab:uploads:migrate[FileUploader, MergeRequest]"
- ```
+ > **Notes:**
+ > These task complies with the `BATCH` environment variable to process uploads in batch (200 by default). All of the processing will be done in a background worker and requires **no downtime**.
+
+ ```bash
+ # gitlab-rake gitlab:uploads:migrate[uploader_class, model_class, mount_point]
+
+ # Avatars
+ gitlab-rake "gitlab:uploads:migrate[AvatarUploader, Project, :avatar]"
+ gitlab-rake "gitlab:uploads:migrate[AvatarUploader, Group, :avatar]"
+ gitlab-rake "gitlab:uploads:migrate[AvatarUploader, User, :avatar]"
+
+ # Attachments
+ gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Note, :attachment]"
+ gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :logo]"
+ gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :header_logo]"
+
+ # Markdown
+ gitlab-rake "gitlab:uploads:migrate[FileUploader, Project]"
+ gitlab-rake "gitlab:uploads:migrate[PersonalFileUploader, Snippet]"
+ gitlab-rake "gitlab:uploads:migrate[NamespaceFileUploader, Snippet]"
+ gitlab-rake "gitlab:uploads:migrate[FileUploader, MergeRequest]"
+ ```
---
@@ -167,34 +170,34 @@ _The uploads are stored by default in
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Migrate any existing local uploads to the object storage:
->**Notes:**
-
-- These task comply with the `BATCH` environment variable to process uploads in batch (200 by default). All of the processing will be done in a background worker and requires **no downtime**.
-
-- To migrate in production use `RAILS_ENV=production` environment variable.
-
- ```bash
- # sudo -u git -H bundle exec rake gitlab:uploads:migrate
-
- # Avatars
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, Project, :avatar]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, Group, :avatar]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, User, :avatar]"
-
- # Attachments
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Note, :attachment]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :logo]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :header_logo]"
-
- # Markdown
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[FileUploader, Project]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[PersonalFileUploader, Snippet]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[NamespaceFileUploader, Snippet]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[FileUploader, MergeRequest]"
-
- ```
+ > **Notes:**
+ > - These task comply with the `BATCH` environment variable to process uploads in batch (200 by default). All of the processing will be done in a background worker and requires **no downtime**.
+ > - To migrate in production use `RAILS_ENV=production` environment variable.
+
+ ```bash
+ # sudo -u git -H bundle exec rake gitlab:uploads:migrate
+
+ # Avatars
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, Project, :avatar]"
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, Group, :avatar]"
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, User, :avatar]"
+
+ # Attachments
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Note, :attachment]"
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :logo]"
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :header_logo]"
+
+ # Markdown
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[FileUploader, Project]"
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[PersonalFileUploader, Snippet]"
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[NamespaceFileUploader, Snippet]"
+ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[FileUploader, MergeRequest]"
+
+ ```
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab"
[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition Premium"
+[ce]: https://about.gitlab.com/gitlab-ce/ "GitLab Community Edition"
[ee-3867]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3867
+[ce-17358]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17358
diff --git a/doc/api/README.md b/doc/api/README.md
index 45e926d3b6b..19abbdc7a1e 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -3,6 +3,8 @@
Automate GitLab via a simple and powerful API. All definitions can be found
under [`/lib/api`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/api).
+The main GitLab API is a [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) API. Therefore, documentation in this section assumes knowledge of REST concepts.
+
## Resources
Documentation for various API resources can be found separately in the
@@ -18,10 +20,11 @@ following locations:
- [Custom Attributes](custom_attributes.md)
- [Deployments](deployments.md)
- [Deploy Keys](deploy_keys.md)
+- [Dockerfile templates](templates/dockerfiles.md)
- [Environments](environments.md)
- [Events](events.md)
- [Feature flags](features.md)
-- [Gitignores templates](templates/gitignores.md)
+- [Gitignore templates](templates/gitignores.md)
- [GitLab CI Config templates](templates/gitlab_ci_ymls.md)
- [Groups](groups.md)
- [Group Access Requests](access_requests.md)
@@ -40,6 +43,7 @@ following locations:
- [Namespaces](namespaces.md)
- [Notes](notes.md) (comments)
- [Discussions](discussions.md) (threaded comments)
+- [Resource Label Events](resource_label_events.md)
- [Notification settings](notification_settings.md)
- [Open source license templates](templates/licenses.md)
- [Pages Domains](pages_domains.md)
@@ -52,9 +56,12 @@ following locations:
- [Project import/export](project_import_export.md)
- [Project Members](members.md)
- [Project Snippets](project_snippets.md)
+- [Project Templates](project_templates.md)
- [Protected Branches](protected_branches.md)
+- [Protected Tags](protected_tags.md)
- [Repositories](repositories.md)
- [Repository Files](repository_files.md)
+- [Repository Submodules](repository_submodules.md)
- [Runners](runners.md)
- [Search](search.md)
- [Services](services.md)
@@ -63,6 +70,7 @@ following locations:
- [System Hooks](system_hooks.md)
- [Tags](tags.md)
- [Todos](todos.md)
+- [Triggering Pipelines](../ci/triggers/README.md)
- [Users](users.md)
- [Validate CI configuration](lint.md)
- [V3 to V4](v3_to_v4.md)
@@ -76,8 +84,8 @@ Going forward, we will start on moving to
controller-specific endpoints. GraphQL has a number of benefits:
1. We avoid having to maintain two different APIs.
-2. Callers of the API can request only what they need.
-3. It is versioned by default.
+1. Callers of the API can request only what they need.
+1. It is versioned by default.
It will co-exist with the current v4 REST API. If we have a v5 API, this should
be a compatibility layer on top of GraphQL.
@@ -138,8 +146,9 @@ There are three ways to authenticate with the GitLab API:
1. [Session cookie](#session-cookie)
For admins who want to authenticate with the API as a specific user, or who want to build applications or scripts that do so, two options are available:
+
1. [Impersonation tokens](#impersonation-tokens)
-2. [Sudo](#sudo)
+1. [Sudo](#sudo)
If authentication information is invalid or omitted, an error message will be
returned with status code `401`:
@@ -218,7 +227,8 @@ Impersonation tokens are used exactly like regular personal access tokens, and c
### Sudo
-> Needs admin permissions.
+NOTE: **Note:**
+Only available to [administrators](../user/permissions.md).
All API requests support performing an API call as if you were another user,
provided you are authenticated as an administrator with an OAuth or Personal Access Token that has the `sudo` scope.
@@ -227,6 +237,9 @@ You need to pass the `sudo` parameter either via query string or a header with a
the user you want to perform the operation as. If passed as a header, the
header name must be `Sudo`.
+NOTE: **Note:**
+Usernames are case insensitive.
+
If a non administrative access token is provided, an error message will
be returned with status code `403`:
@@ -444,28 +457,23 @@ curl --request POST --header "PRIVATE-TOKEN: ********************" \
## `id` vs `iid`
-When you work with the API, you may notice two similar fields in API entities:
-`id` and `iid`. The main difference between them is scope.
+ Some resources have two similarly-named fields. For example, [issues](issues.md), [merge requests](merge_requests.md), and [project milestones](merge_requests.md). The fields are:
-For example, an issue might have `id: 46` and `iid: 5`.
+- `id`: ID that is unique across all projects.
+- `iid`: additional, internal ID that is unique in the scope of a single project.
-| Parameter | Description |
-| --------- | ----------- |
-| `id` | Is unique across all issues and is used for any API call |
-| `iid` | Is unique only in scope of a single project. When you browse issues or merge requests with the Web UI, you see the `iid` |
+NOTE: **Note:**
+The `iid` is displayed in the web UI.
-That means that if you want to get an issue via the API you should use the `id`:
+If a resource has the `iid` field and the `id` field, the `iid` field is usually used instead of `id` to fetch the resource.
-```
-GET /projects/42/issues/:id
-```
+For example, suppose a project with `id: 42` has an issue with `id: 46` and `iid: 5`. In this case:
-On the other hand, if you want to create a link to a web page you should use
-the `iid`:
+- A valid API call to retrieve the issue is `GET /projects/42/issues/5`
+- An invalid API call to retrieve the issue is `GET /projects/42/issues/46`.
-```
-GET /projects/42/issues/:iid
-```
+NOTE: **Note:**
+Not all resources with the `iid` field are fetched by `iid`. For guidance on which field to use, see the documentation for the specific resource.
## Data validation and error reporting
diff --git a/doc/api/applications.md b/doc/api/applications.md
index 6d244594b71..d74a3cdf5c1 100644
--- a/doc/api/applications.md
+++ b/doc/api/applications.md
@@ -4,12 +4,12 @@
[ce-8160]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8160
+Only admin user can use the Applications API.
+
## Create a application
Create a application by posting a JSON payload.
-User must be admin to do that.
-
Returns `200` if the request succeeds.
```
@@ -30,8 +30,55 @@ Example response:
```json
{
+ "id":1,
"application_id": "5832fc6e14300a0d962240a8144466eef4ee93ef0d218477e55f11cf12fc3737",
+ "application_name": "MyApplication",
"secret": "ee1dd64b6adc89cf7e2c23099301ccc2c61b441064e9324d963c46902a85ec34",
"callback_url": "http://redirect.uri"
}
```
+
+## List all applications
+
+List all registered applications.
+
+```
+GET /applications
+```
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/applications
+```
+
+Example response:
+
+```json
+[
+ {
+ "id":1,
+ "application_id": "5832fc6e14300a0d962240a8144466eef4ee93ef0d218477e55f11cf12fc3737",
+ "application_name": "MyApplication",
+ "callback_url": "http://redirect.uri"
+ }
+]
+```
+
+> Note: the `secret` value will not be exposed by this API.
+
+## Delete an application
+
+Delete a specific application.
+
+Returns `204` if the request succeeds.
+
+```
+DELETE /applications/:id
+```
+
+Parameters:
+
+- `id` (required) - The id of the application (not the application_id)
+
+```bash
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/applications/:id
+```
diff --git a/doc/api/avatar.md b/doc/api/avatar.md
index 7faed893066..aa6f7c185ae 100644
--- a/doc/api/avatar.md
+++ b/doc/api/avatar.md
@@ -4,7 +4,7 @@
## Get a single avatar URL
-Get a single avatar URL for a given email addres. If user with matching public
+Get a single avatar URL for a given email address. If user with matching public
email address is not found, results from external avatar services are returned.
This endpoint can be accessed without authentication. In case public visibility
is restricted, response will be `403 Forbidden` when unauthenticated.
diff --git a/doc/api/boards.md b/doc/api/boards.md
index 246de50323e..5f006f4f012 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -144,7 +144,7 @@ Example response:
## List board lists
Get a list of the board's lists.
-Does not include `backlog` and `closed` lists
+Does not include `open` and `closed` lists
```
GET /projects/:id/boards/:board_id/lists
diff --git a/doc/api/branches.md b/doc/api/branches.md
index bfb21608d28..4abf0639eb0 100644
--- a/doc/api/branches.md
+++ b/doc/api/branches.md
@@ -27,6 +27,7 @@ Example response:
"name": "master",
"merged": false,
"protected": true,
+ "default": true,
"developers_can_push": false,
"developers_can_merge": false,
"can_push": true,
@@ -75,6 +76,7 @@ Example response:
"name": "master",
"merged": false,
"protected": true,
+ "default": true,
"developers_can_push": false,
"developers_can_merge": false,
"can_push": true,
@@ -141,6 +143,7 @@ Example response:
"name": "master",
"merged": false,
"protected": true,
+ "default": true,
"developers_can_push": true,
"developers_can_merge": true,
"can_push": true
@@ -190,6 +193,7 @@ Example response:
"name": "master",
"merged": false,
"protected": false,
+ "default": true,
"developers_can_push": false,
"developers_can_merge": false,
"can_push": true
@@ -234,6 +238,7 @@ Example response:
"name": "newbranch",
"merged": false,
"protected": false,
+ "default": false,
"developers_can_push": false,
"developers_can_merge": false,
"can_push": true
diff --git a/doc/api/commits.md b/doc/api/commits.md
index d07b9d5614a..7d9b52ec24f 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -79,16 +79,18 @@ POST /projects/:id/repository/commits
| `actions[]` | array | yes | An array of action hashes to commit as a batch. See the next table for what attributes it can take. |
| `author_email` | string | no | Specify the commit author's email address |
| `author_name` | string | no | Specify the commit author's name |
+| `stats` | boolean | no | Include commit stats. Default is true |
| `actions[]` Attribute | Type | Required | Description |
| --------------------- | ---- | -------- | ----------- |
-| `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update` |
+| `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update`, `chmod`|
| `file_path` | string | yes | Full path to the file. Ex. `lib/class.rb` |
-| `previous_path` | string | no | Original full path to the file being moved. Ex. `lib/class1.rb` |
-| `content` | string | no | File content, required for all except `delete`. Optional for `move` |
+| `previous_path` | string | no | Original full path to the file being moved. Ex. `lib/class1.rb`. Only considered for `move` action. |
+| `content` | string | no | File content, required for all except `delete` and `chmod`. Optional for `move` |
| `encoding` | string | no | `text` or `base64`. `text` is default. |
| `last_commit_id` | string | no | Last known file commit id. Will be only considered in update, move and delete actions. |
+| `execute_filemode` | boolean | no | When `true/false` enables/disables the execute flag on the file. Only considered for `chmod` action. |
```bash
PAYLOAD=$(cat << 'JSON'
@@ -115,6 +117,11 @@ PAYLOAD=$(cat << 'JSON'
"action": "update",
"file_path": "foo/bar5",
"content": "new content"
+ },
+ {
+ "action": "chmod",
+ "file_path": "foo/bar5",
+ "execute_filemode": true
}
]
}
@@ -281,6 +288,47 @@ Example response:
}
```
+## Revert a commit
+
+> [Introduced][ce-22919] in GitLab 11.5.
+
+Reverts a commit in a given branch.
+
+```
+POST /projects/:id/repository/commits/:sha/revert
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `sha` | string | yes | Commit SHA to revert |
+| `branch` | string | yes | Target branch name |
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "branch=master" "https://gitlab.example.com/api/v4/projects/5/repository/commits/a738f717824ff53aebad8b090c1b79a14f2bd9e8/revert"
+```
+
+Example response:
+
+```json
+{
+ "id":"8b090c1b79a14f2bd9e8a738f717824ff53aebad",
+ "short_id": "8b090c1b",
+ "title":"Revert \"Feature added\"",
+ "created_at":"2018-11-08T15:55:26.000Z",
+ "parent_ids":["a738f717824ff53aebad8b090c1b79a14f2bd9e8"],
+ "message":"Revert \"Feature added\"\n\nThis reverts commit a738f717824ff53aebad8b090c1b79a14f2bd9e8",
+ "author_name":"Administrator",
+ "author_email":"admin@example.com",
+ "authored_date":"2018-11-08T15:55:26.000Z",
+ "committer_name":"Administrator",
+ "committer_email":"admin@example.com",
+ "committed_date":"2018-11-08T15:55:26.000Z"
+}
+```
+
## Get the diff of a commit
Get the diff of a commit in a project.
@@ -464,7 +512,7 @@ Example response:
},
{
"started_at" : null,
- "name" : "flay",
+ "name" : "test",
"allow_failure" : false,
"status" : "pending",
"created_at" : "2016-01-19T08:40:25.832Z",
@@ -612,3 +660,4 @@ Example response:
[ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047
[ce-15026]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15026
[ce-18004]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18004
+[ce-22919]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22919
diff --git a/doc/api/deployments.md b/doc/api/deployments.md
index fd11894ea8f..1963b0a21de 100644
--- a/doc/api/deployments.md
+++ b/doc/api/deployments.md
@@ -46,19 +46,21 @@ Example of response
"status": "success",
"tag": false,
"user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.dev/root",
+ "created_at": "2015-12-21T13:14:24.077Z",
"bio": null,
- "created_at": "2016-08-11T07:09:20.351Z",
- "id": 1,
- "linkedin": "",
"location": null,
- "name": "Administrator",
+ "public_email": "",
"skype": "",
- "state": "active",
+ "linkedin": "",
"twitter": "",
- "username": "root",
- "web_url": "http://localhost:3000/root",
- "website_url": ""
+ "website_url": "",
+ "organization": ""
}
},
"environment": {
@@ -103,19 +105,21 @@ Example of response
"status": "success",
"tag": false,
"user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.dev/root",
+ "created_at": "2015-12-21T13:14:24.077Z",
"bio": null,
- "created_at": "2016-08-11T07:09:20.351Z",
- "id": 1,
- "linkedin": "",
"location": null,
- "name": "Administrator",
+ "public_email": "",
"skype": "",
- "state": "active",
+ "linkedin": "",
"twitter": "",
- "username": "root",
- "web_url": "http://localhost:3000/root",
- "website_url": ""
+ "website_url": "",
+ "organization": ""
}
},
"environment": {
@@ -188,19 +192,20 @@ Example of response
"started_at": null,
"finished_at": "2016-08-11T11:32:35.145Z",
"user": {
+ "id": 1,
"name": "Administrator",
"username": "root",
- "id": 1,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "web_url": "http://localhost:3000/root",
- "created_at": "2016-08-11T07:09:20.351Z",
+ "web_url": "http://gitlab.dev/root",
+ "created_at": "2015-12-21T13:14:24.077Z",
"bio": null,
"location": null,
"skype": "",
"linkedin": "",
"twitter": "",
- "website_url": ""
+ "website_url": "",
+ "organization": ""
},
"commit": {
"id": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
diff --git a/doc/api/discussions.md b/doc/api/discussions.md
index 65e2f9d6cd9..a1e1ff1419d 100644
--- a/doc/api/discussions.md
+++ b/doc/api/discussions.md
@@ -136,7 +136,7 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `issue_iid` | integer | yes | The IID of an issue |
| `body` | string | yes | The content of a discussion |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/discussions?body=comment
@@ -159,7 +159,7 @@ Parameters:
| `discussion_id` | integer | yes | The ID of a discussion |
| `note_id` | integer | yes | The ID of a discussion note |
| `body` | string | yes | The content of a discussion |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment
@@ -342,7 +342,7 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `snippet_id` | integer | yes | The ID of an snippet |
| `body` | string | yes | The content of a discussion |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippets/11/discussions?body=comment
@@ -365,7 +365,7 @@ Parameters:
| `discussion_id` | integer | yes | The ID of a discussion |
| `note_id` | integer | yes | The ID of a discussion note |
| `body` | string | yes | The content of a discussion |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippets/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment
@@ -601,7 +601,7 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `merge_request_iid` | integer | yes | The IID of a merge request |
| `body` | string | yes | The content of a discussion |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
| `position` | hash | no | Position when creating a diff note |
| `position[base_sha]` | string | yes | Base commit SHA in the source branch |
| `position[start_sha]` | string | yes | SHA referencing commit in target branch |
@@ -659,7 +659,7 @@ Parameters:
| `discussion_id` | integer | yes | The ID of a discussion |
| `note_id` | integer | yes | The ID of a discussion note |
| `body` | string | yes | The content of a discussion |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment
@@ -894,7 +894,7 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `commit_id` | integer | yes | The ID of a commit |
| `body` | string | yes | The content of a discussion |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
| `position` | hash | no | Position when creating a diff note |
| `position[base_sha]` | string | yes | Base commit SHA in the source branch |
| `position[start_sha]` | string | yes | SHA referencing commit in target branch |
@@ -930,7 +930,7 @@ Parameters:
| `discussion_id` | integer | yes | The ID of a discussion |
| `note_id` | integer | yes | The ID of a discussion note |
| `body` | string | yes | The content of a discussion |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment
diff --git a/doc/api/events.md b/doc/api/events.md
index f4d26c4de1c..e1c6b801a77 100644
--- a/doc/api/events.md
+++ b/doc/api/events.md
@@ -44,13 +44,15 @@ YYYY-MM-DD
### Event Time Period Limit
-GitLab removes events older than 1 year from the events table for performance reasons. The range of 1 year was chosen because user contribution calendars only show contributions of the past year.
+GitLab removes events older than 2 years from the events table for performance reasons.
## List currently authenticated user's events
->**Note:** This endpoint was introduced in GitLab 9.3.
+>**Notes:**
+> This endpoint was introduced in GitLab 9.3.
+> `read_user` access was introduced in GitLab 11.3.
-Get a list of events for the authenticated user.
+Get a list of events for the authenticated user. Scope `read_user` or `api` is required.
```
GET /events
@@ -69,7 +71,7 @@ Parameters:
Example request:
```
-curl --header "PRIVATE-TOKEN 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/events&target_type=issue&action=created&after=2017-01-31&before=2017-03-01
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/events?target_type=issue&action=created&after=2017-01-31&before=2017-03-01
```
Example response:
@@ -119,9 +121,11 @@ Example response:
### Get user contribution events
->**Note:** Documentation was formerly located in the [Users API pages][users-api].
+>**Notes:**
+> Documentation was formerly located in the [Users API pages][users-api].
+> `read_user` access was introduced in GitLab 11.3.
-Get the contribution events for the specified user, sorted from newest to oldest.
+Get the contribution events for the specified user, sorted from newest to oldest. Scope `read_user` or `api` is required.
```
GET /users/:id/events
@@ -255,7 +259,7 @@ Example response:
Get a list of visible events for a particular project.
```
-GET /:project_id/events
+GET /projects/:project_id/events
```
Parameters:
@@ -272,7 +276,7 @@ Parameters:
Example request:
```
-curl --header "PRIVATE-TOKEN 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:project_id/events&target_type=issue&action=created&after=2017-01-31&before=2017-03-01
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:project_id/events?target_type=issue&action=created&after=2017-01-31&before=2017-03-01
```
Example response:
diff --git a/doc/api/group_boards.md b/doc/api/group_boards.md
index 45a8544d6b1..373904e50c4 100644
--- a/doc/api/group_boards.md
+++ b/doc/api/group_boards.md
@@ -119,7 +119,7 @@ Example response:
## List board lists
Get a list of the board's lists.
-Does not include `backlog` and `closed` lists
+Does not include `open` and `closed` lists
```
GET /groups/:id/boards/:board_id/lists
diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md
index 152929b7614..e396f4411e6 100644
--- a/doc/api/group_milestones.md
+++ b/doc/api/group_milestones.md
@@ -96,6 +96,19 @@ Parameters:
- `start_date` (optional) - The start date of the milestone
- `state_event` (optional) - The state event of the milestone (close|activate)
+## Delete group milestone
+
+Only for user with developer access to the group.
+
+```
+DELETE /groups/:id/milestones/:milestone_id
+```
+
+Parameters:
+
+- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user
+- `milestone_id` (required) - The ID of the group's milestone
+
## Get all issues assigned to a single milestone
Gets all issues assigned to a single group milestone.
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 87be36cc815..a9462fc413f 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -37,6 +37,7 @@ GET /groups
"request_access_enabled": false,
"full_name": "Foobar Group",
"full_path": "foo-bar",
+ "file_template_project_id": 1,
"parent_id": null
}
]
@@ -62,6 +63,7 @@ GET /groups?statistics=true
"request_access_enabled": false,
"full_name": "Foobar Group",
"full_path": "foo-bar",
+ "file_template_project_id": 1,
"parent_id": null,
"statistics": {
"storage_size" : 212,
@@ -82,7 +84,7 @@ You can filter by [custom attributes](custom_attributes.md) with:
GET /groups?custom_attributes[key]=value&custom_attributes[other_key]=other_value
```
-## List a groups's subgroups
+## List a group's subgroups
> [Introduced][ce-15142] in GitLab 10.3.
@@ -122,6 +124,7 @@ GET /groups/:id/subgroups
"request_access_enabled": false,
"full_name": "Foobar Group",
"full_path": "foo-bar",
+ "file_template_project_id": 1,
"parent_id": 123
}
]
@@ -232,6 +235,7 @@ Example response:
"request_access_enabled": false,
"full_name": "Twitter",
"full_path": "twitter",
+ "file_template_project_id": 1,
"parent_id": null,
"projects": [
{
@@ -351,12 +355,14 @@ Example response:
{
"group_id": 4,
"group_name": "Twitter",
- "group_access_level": 30
+ "group_access_level": 30,
+ "expires_at": null
},
{
"group_id": 3,
"group_name": "Gitlab Org",
- "group_access_level": 10
+ "group_access_level": 10,
+ "expires_at": "2018-08-14"
}
]
}
@@ -384,6 +390,7 @@ Example response:
"request_access_enabled": false,
"full_name": "Twitter",
"full_path": "twitter",
+ "file_template_project_id": 1,
"parent_id": null
}
```
@@ -440,6 +447,7 @@ PUT /groups/:id
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
+| `file_template_project_id` | integer | no | **(Premium)** The ID of a project to load custom file templates from |
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/5?name=Experimental"
@@ -460,6 +468,7 @@ Example response:
"request_access_enabled": false,
"full_name": "Foobar Group",
"full_path": "foo-bar",
+ "file_template_project_id": 1,
"parent_id": null,
"projects": [
{
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 103eaa5655f..0dc9d706120 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -37,11 +37,11 @@ GET /issues?my_reaction_emoji=star
| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
+| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
+| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
@@ -151,11 +151,11 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
-| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
+| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
+| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search group issues against their `title` and `description` |
@@ -265,11 +265,11 @@ GET /projects/:id/issues?my_reaction_emoji=star
| `iids[]` | Array[integer] | no | Return only the milestone having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
+| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
+| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search project issues against their `title` and `description` |
@@ -470,7 +470,7 @@ POST /projects/:id/issues
| `assignee_ids` | Array[integer] | no | The ID of the users to assign issue |
| `milestone_id` | integer | no | The global ID of a milestone to assign issue |
| `labels` | string | no | Comma-separated label names for an issue |
-| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project/group owner rights) |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
| `merge_request_to_resolve_discussions_of` | integer | no | The IID of a merge request in which to resolve all issues. This will fill the issue with a default description and mark all discussions as resolved. When passing a description or title, these values will take precedence over the default values.|
| `discussion_to_resolve` | string | no | The ID of a discussion to resolve. This will fill in the issue with a default description and mark the discussion as resolved. Use in combination with `merge_request_to_resolve_discussions_of`. |
@@ -1113,6 +1113,93 @@ Example response:
}
```
+## List merge requests related to issue
+
+Get all the merge requests that are related to the issue.
+
+```
+GET /projects/:id/issues/:issue_id/related_merge_requests
+```
+
+| Attribute | Type | Required | Description |
+|-------------|---------|----------|--------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `issue_iid` | integer | yes | The internal ID of a project's issue |
+
+```sh
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/11/related_merge_requests
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 29,
+ "iid": 11,
+ "project_id": 1,
+ "title": "Provident eius eos blanditiis consequatur neque odit.",
+ "description": "Ut consequatur ipsa aspernatur quisquam voluptatum fugit. Qui harum corporis quo fuga ut incidunt veritatis. Autem necessitatibus et harum occaecati nihil ea.\r\n\r\ntwitter/flight#8",
+ "state": "opened",
+ "created_at": "2018-09-18T14:36:15.510Z",
+ "updated_at": "2018-09-19T07:45:13.089Z",
+ "target_branch": "v2.x",
+ "source_branch": "so_long_jquery",
+ "upvotes": 0,
+ "downvotes": 0,
+ "author": {
+ "id": 14,
+ "name": "Verna Hills",
+ "username": "lawanda_reinger",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/de68a91aeab1cff563795fb98a0c2cc0?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/lawanda_reinger"
+ },
+ "assignee": {
+ "id": 19,
+ "name": "Jody Baumbach",
+ "username": "felipa.kuvalis",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/6541fc75fc4e87e203529bd275fafd07?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/felipa.kuvalis"
+ },
+ "source_project_id": 1,
+ "target_project_id": 1,
+ "labels": [],
+ "work_in_progress": false,
+ "milestone": {
+ "id": 27,
+ "iid": 2,
+ "project_id": 1,
+ "title": "v1.0",
+ "description": "Et tenetur voluptatem minima doloribus vero dignissimos vitae.",
+ "state": "active",
+ "created_at": "2018-09-18T14:35:44.353Z",
+ "updated_at": "2018-09-18T14:35:44.353Z",
+ "due_date": null,
+ "start_date": null,
+ "web_url": "https://gitlab.example.com/twitter/flight/milestones/2"
+ },
+ "merge_when_pipeline_succeeds": false,
+ "merge_status": "cannot_be_merged",
+ "sha": "3b7b528e9353295c1c125dad281ac5b5deae5f12",
+ "merge_commit_sha": null,
+ "user_notes_count": 9,
+ "discussion_locked": null,
+ "should_remove_source_branch": null,
+ "force_remove_source_branch": false,
+ "web_url": "https://gitlab.example.com/twitter/flight/merge_requests/4",
+ "time_stats": {
+ "time_estimate": 0,
+ "total_time_spent": 0,
+ "human_time_estimate": null,
+ "human_total_time_spent": null
+ },
+ "squash": false
+ }
+]
+```
+
## List merge requests that will close issue on merge
Get all the merge requests that will close issue when merged.
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 9a950097675..aa290ff4cf8 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -33,8 +33,9 @@ Example of response
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.727Z",
- "artifacts_file": null,
+ "started_at": "2015-12-24T17:54:24.729Z",
"finished_at": "2015-12-24T17:54:24.921Z",
+ "duration": 0.192,
"artifacts_expire_at": "2016-01-23T17:54:24.921Z",
"id": 6,
"name": "rspec:other",
@@ -45,25 +46,28 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:24.729Z",
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/6",
"user": {
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "bio": null,
- "created_at": "2015-12-21T13:14:24.077Z",
"id": 1,
- "linkedin": "",
"name": "Administrator",
- "skype": "",
- "state": "active",
- "twitter": "",
"username": "root",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://gitlab.dev/root",
- "website_url": ""
+ "created_at": "2015-12-21T13:14:24.077Z",
+ "bio": null,
+ "location": null,
+ "public_email": "",
+ "skype": "",
+ "linkedin": "",
+ "twitter": "",
+ "website_url": "",
+ "organization": ""
}
},
{
@@ -78,11 +82,19 @@ Example of response
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.802Z",
+ "started_at": "2015-12-24T17:54:27.722Z",
+ "finished_at": "2015-12-24T17:54:27.895Z",
+ "duration": 0.173,
"artifacts_file": {
"filename": "artifacts.zip",
"size": 1000
},
- "finished_at": "2015-12-24T17:54:27.895Z",
+ "artifacts": [
+ {"file_type": "archive", "size": 1000, "filename": "artifacts.zip", "file_format": "zip"},
+ {"file_type": "metadata", "size": 186, "filename": "metadata.gz", "file_format": "gzip"},
+ {"file_type": "trace", "size": 1500, "filename": "job.log", "file_format": "raw"},
+ {"file_type": "junit", "size": 750, "filename": "junit.xml.gz", "file_format": "gzip"}
+ ],
"artifacts_expire_at": "2016-01-23T17:54:27.895Z",
"id": 7,
"name": "teaspoon",
@@ -93,25 +105,28 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:27.722Z",
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/7",
"user": {
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "bio": null,
- "created_at": "2015-12-21T13:14:24.077Z",
"id": 1,
- "linkedin": "",
"name": "Administrator",
- "skype": "",
- "state": "active",
- "twitter": "",
"username": "root",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://gitlab.dev/root",
- "website_url": ""
+ "created_at": "2015-12-21T13:14:24.077Z",
+ "bio": null,
+ "location": null,
+ "public_email": "",
+ "skype": "",
+ "linkedin": "",
+ "twitter": "",
+ "website_url": "",
+ "organization": ""
}
}
]
@@ -151,8 +166,9 @@ Example of response
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.727Z",
- "artifacts_file": null,
+ "started_at": "2015-12-24T17:54:24.729Z",
"finished_at": "2015-12-24T17:54:24.921Z",
+ "duration": 0.192,
"artifacts_expire_at": "2016-01-23T17:54:24.921Z",
"id": 6,
"name": "rspec:other",
@@ -163,25 +179,28 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:24.729Z",
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/6",
"user": {
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "bio": null,
- "created_at": "2015-12-21T13:14:24.077Z",
"id": 1,
- "linkedin": "",
"name": "Administrator",
- "skype": "",
- "state": "active",
- "twitter": "",
"username": "root",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://gitlab.dev/root",
- "website_url": ""
+ "created_at": "2015-12-21T13:14:24.077Z",
+ "bio": null,
+ "location": null,
+ "public_email": "",
+ "skype": "",
+ "linkedin": "",
+ "twitter": "",
+ "website_url": "",
+ "organization": ""
}
},
{
@@ -196,11 +215,19 @@ Example of response
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.802Z",
+ "started_at": "2015-12-24T17:54:27.722Z",
+ "finished_at": "2015-12-24T17:54:27.895Z",
+ "duration": 0.173,
"artifacts_file": {
"filename": "artifacts.zip",
"size": 1000
},
- "finished_at": "2015-12-24T17:54:27.895Z",
+ "artifacts": [
+ {"file_type": "archive", "size": 1000, "filename": "artifacts.zip", "file_format": "zip"},
+ {"file_type": "metadata", "size": 186, "filename": "metadata.gz", "file_format": "gzip"},
+ {"file_type": "trace", "size": 1500, "filename": "job.log", "file_format": "raw"},
+ {"file_type": "junit", "size": 750, "filename": "junit.xml.gz", "file_format": "gzip"}
+ ],
"artifacts_expire_at": "2016-01-23T17:54:27.895Z",
"id": 7,
"name": "teaspoon",
@@ -211,25 +238,28 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:27.722Z",
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/7",
"user": {
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "bio": null,
- "created_at": "2015-12-21T13:14:24.077Z",
"id": 1,
- "linkedin": "",
"name": "Administrator",
- "skype": "",
- "state": "active",
- "twitter": "",
"username": "root",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://gitlab.dev/root",
- "website_url": ""
+ "created_at": "2015-12-21T13:14:24.077Z",
+ "bio": null,
+ "location": null,
+ "public_email": "",
+ "skype": "",
+ "linkedin": "",
+ "twitter": "",
+ "website_url": "",
+ "organization": ""
}
}
]
@@ -267,8 +297,9 @@ Example of response
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.880Z",
- "artifacts_file": null,
+ "started_at": "2015-12-24T17:54:30.733Z",
"finished_at": "2015-12-24T17:54:31.198Z",
+ "duration": 0.465,
"artifacts_expire_at": "2016-01-23T17:54:31.198Z",
"id": 8,
"name": "rubocop",
@@ -279,25 +310,28 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:30.733Z",
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/8",
"user": {
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "bio": null,
- "created_at": "2015-12-21T13:14:24.077Z",
"id": 1,
- "linkedin": "",
"name": "Administrator",
- "skype": "",
- "state": "active",
- "twitter": "",
"username": "root",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://gitlab.dev/root",
- "website_url": ""
+ "created_at": "2015-12-21T13:14:24.077Z",
+ "bio": null,
+ "location": null,
+ "public_email": "",
+ "skype": "",
+ "linkedin": "",
+ "twitter": "",
+ "website_url": "",
+ "organization": ""
}
}
```
@@ -305,7 +339,8 @@ Example of response
## Get job artifacts
> **Notes**:
-- [Introduced][ce-2893] in GitLab 8.5.
+>
+> - [Introduced][ce-2893] in GitLab 8.5.
Get job artifacts of a project.
@@ -336,7 +371,8 @@ Response:
## Download the artifacts archive
> **Notes**:
-- [Introduced][ce-5347] in GitLab 8.10.
+>
+> - [Introduced][ce-5347] in GitLab 8.10.
Download the artifacts archive from the given reference name and job provided the
job finished successfully.
@@ -458,14 +494,15 @@ Example of response
},
"coverage": null,
"created_at": "2016-01-11T10:13:33.506Z",
- "artifacts_file": null,
- "finished_at": "2016-01-11T10:14:09.526Z",
+ "started_at": "2016-01-11T10:14:09.526Z",
+ "finished_at": null,
+ "duration": 8,
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
- "started_at": null,
"status": "canceled",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/42",
@@ -505,14 +542,15 @@ Example of response
},
"coverage": null,
"created_at": "2016-01-11T10:13:33.506Z",
- "artifacts_file": null,
+ "started_at": null,
"finished_at": null,
+ "duration": null,
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
- "started_at": null,
"status": "pending",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/42",
@@ -559,11 +597,13 @@ Example of response
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"created_at": "2016-01-11T10:13:33.506Z",
"started_at": "2016-01-11T10:13:33.506Z",
"finished_at": "2016-01-11T10:15:10.506Z",
+ "duration": 97.0,
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/42",
@@ -610,11 +650,13 @@ Example response:
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"created_at": "2016-01-11T10:13:33.506Z",
"started_at": "2016-01-11T10:13:33.506Z",
"finished_at": "2016-01-11T10:15:10.506Z",
+ "duration": 97.0,
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/42",
@@ -654,14 +696,15 @@ Example of response
},
"coverage": null,
"created_at": "2016-01-11T10:13:33.506Z",
- "artifacts_file": null,
+ "started_at": null,
"finished_at": null,
+ "duration": null,
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
- "started_at": null,
"status": "started",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/42",
diff --git a/doc/api/keys.md b/doc/api/keys.md
index ddcf7830621..06b31a67d6a 100644
--- a/doc/api/keys.md
+++ b/doc/api/keys.md
@@ -27,10 +27,16 @@ Parameters:
"web_url": "http://localhost:3000/john_smith",
"created_at": "2015-09-03T07:24:01.670Z",
"bio": null,
+ "location": null,
+ "public_email": "john@example.com",
"skype": "",
"linkedin": "",
"twitter": "",
"website_url": "",
+ "organization": null,
+ "last_sign_in_at": "2015-09-03T07:24:01.670Z",
+ "confirmed_at": "2015-09-03T07:24:01.670Z",
+ "last_activity_on": "2015-09-03",
"email": "john@example.com",
"theme_id": 2,
"color_scheme_id": 1,
@@ -40,6 +46,8 @@ Parameters:
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": false
+ "external": false,
+ "private_profile": null
}
}
```
diff --git a/doc/api/members.md b/doc/api/members.md
index 7b228b92594..bb4fae35f52 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -15,6 +15,7 @@ The access levels are defined in the `Gitlab::Access` module. Currently, these l
## List all members of a group or project
Gets a list of group or project members viewable by the authenticated user.
+Returns only direct members and not inherited members through ancestors groups.
```
GET /groups/:id/members
@@ -61,6 +62,7 @@ Example response:
## List all members of a group or project including inherited members
Gets a list of group or project members viewable by the authenticated user, including inherited members through ancestor groups.
+Returns multiple times the same user (with different member attributes) when the user is a member of the project/group and of one or more ancestor group.
```
GET /groups/:id/members/all
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 58d05a70d05..9cb3f0d9c0c 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -30,10 +30,10 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
-| `milestone` | string | no | Return merge requests for a specific milestone |
+| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created on or after the given time |
@@ -42,46 +42,61 @@ Parameters:
| `updated_before` | datetime | no | Return merge requests updated on or before the given time |
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead. |
| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me` |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` |
-| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
+| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
| `search` | string | no | Search merge requests against their `title` and `description` |
+| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests |
```json
[
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
- "state": "opened",
+ "description": "fixed login page css paddings",
+ "state": "merged",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
@@ -92,23 +107,28 @@ Parameters:
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false
}
]
```
@@ -145,10 +165,10 @@ Parameters:
| ------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `id` | integer | yes | The ID of a project |
| `iids[]` | Array[integer] | no | Return the request having the given `iid` |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
-| `milestone` | string | no | Return merge requests for a specific milestone |
+| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created on or after the given time |
@@ -157,10 +177,10 @@ Parameters:
| `updated_before` | datetime | no | Return merge requests updated on or before the given time |
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13060] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
-| `source_branch` | string | no | Return merge requests with the given source branch |
-| `target_branch` | string | no | Return merge requests with the given target branch |
+| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ |
+| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `source_branch` | string | no | Return merge requests with the given source branch |
+| `target_branch` | string | no | Return merge requests with the given target branch |
| `search` | string | no | Search merge requests against their `title` and `description` |
```json
@@ -168,35 +188,49 @@ Parameters:
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
- "state": "opened",
+ "description": "fixed login page css paddings",
+ "state": "merged",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
@@ -207,24 +241,28 @@ Parameters:
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false
}
]
```
@@ -250,11 +288,11 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
-| `id` | integer | yes | The ID of a group |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
-| `order_by` | string | no | Return merge requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
-| `sort` | string | no | Return merge requests sorted in `asc` or `desc` order. Default is `desc` |
-| `milestone` | string | no | Return merge requests for a specific milestone |
+| `id` | integer | yes | The ID of a group |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
+| `order_by` | string | no | Return merge requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
+| `sort` | string | no | Return merge requests sorted in `asc` or `desc` order. Default is `desc` |
+| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created on or after the given time |
@@ -263,10 +301,10 @@ Parameters:
| `updated_before` | datetime | no | Return merge requests updated on or before the given time |
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
-| `source_branch` | string | no | Return merge requests with the given source branch |
-| `target_branch` | string | no | Return merge requests with the given target branch |
+| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ |
+| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `source_branch` | string | no | Return merge requests with the given source branch |
+| `target_branch` | string | no | Return merge requests with the given target branch |
| `search` | string | no | Search merge requests against their `title` and `description` |
```json
@@ -274,35 +312,49 @@ Parameters:
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
- "state": "opened",
+ "description": "fixed login page css paddings",
+ "state": "merged",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
@@ -313,23 +365,26 @@ Parameters:
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-10-22",
+ "start_date": "2018-09-08",
+ "web_url": "gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false
}
]
```
@@ -352,40 +407,44 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request
- `render_html` (optional) - If `true` response includes rendered HTML for title and description
+- `include_diverged_commits_count` (optional) - If `true` response includes the commits behind the target branch
```json
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
+ "description": "fixed login page css paddings",
"state": "merged",
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
- "state" : "active",
- "web_url" : "https://gitlab.example.com/root",
- "avatar_url" : null,
- "username" : "root",
- "id" : 1,
- "name" : "Administrator"
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
+ "state": "active",
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
- "state" : "active",
- "web_url" : "https://gitlab.example.com/root",
- "avatar_url" : null,
- "username" : "root",
- "id" : 1,
- "name" : "Administrator"
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
+ "state": "active",
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
@@ -396,46 +455,57 @@ Parameters:
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": "9999999999999999999999999999999999999999",
+ "merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
},
- "closed_at": "2018-01-19T14:36:11.086Z",
- "latest_build_started_at": null,
- "latest_build_finished_at": null,
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
"first_deployed_to_production_at": null,
"pipeline": {
- "id": 8,
- "ref": "master",
- "sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
- "status": "created"
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
},
- "merged_by": null,
- "merged_at": null,
- "closed_by": {
- "state" : "active",
- "web_url" : "https://gitlab.example.com/root",
- "avatar_url" : null,
- "username" : "root",
- "id" : 1,
- "name" : "Administrator"
- }
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -655,65 +725,99 @@ POST /projects/:id/merge_requests
{
"id": 1,
"iid": 1,
+ "project_id": 3,
+ "title": "test1",
+ "description": "fixed login page css paddings",
+ "state": "merged",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master",
"source_branch": "test1",
- "project_id": 4,
- "title": "test1",
- "state": "opened",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 3,
- "target_project_id": 4,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
- "project_id": 4,
+ "project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
- "user_notes_count": 0,
- "changes_count": "1",
+ "user_notes_count": 1,
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
"allow_collaboration": false,
"allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -748,64 +852,99 @@ Must include at least one non-required attribute from above.
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "project_id": 4,
+ "project_id": 3,
"title": "test1",
- "state": "opened",
+ "description": "fixed login page css paddings",
+ "state": "merged",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 3,
- "target_project_id": 4,
- "labels": [ ],
- "description": "description1",
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
- "project_id": 4,
+ "project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
"allow_collaboration": false,
"allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -849,70 +988,106 @@ Parameters:
- `merge_request_iid` (required) - Internal ID of MR
- `merge_commit_message` (optional) - Custom merge commit message
- `should_remove_source_branch` (optional) - if `true` removes the source branch
-- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds
+- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds
- `sha` (optional) - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail
```json
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
+ "description": "fixed login page css paddings",
"state": "merged",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 4,
- "target_project_id": 4,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
- "project_id": 4,
+ "project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": "9999999999999999999999999999999999999999",
+ "merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -929,69 +1104,105 @@ PUT /projects/:id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_s
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
-- `merge_request_iid` (required) - Internal ID of MR
+- `merge_request_iid` (required) - Internal ID of MR
```json
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
- "state": "opened",
+ "description": "fixed login page css paddings",
+ "state": "merged",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 4,
- "target_project_id": 4,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
- "project_id": 4,
+ "project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
- "merge_when_pipeline_succeeds": true,
+ "merge_when_pipeline_succeeds": false,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -1059,7 +1270,7 @@ Example response when the GitLab issue tracker is used:
"labels" : [],
"user_notes_count": 1,
"changes_count": "1"
- },
+ }
]
```
@@ -1096,54 +1307,101 @@ Example response:
```json
{
- "id": 17,
+ "id": 1,
"iid": 1,
- "project_id": 5,
- "title": "Et et sequi est impedit nulla ut rem et voluptatem.",
- "description": "Consequatur velit eos rerum optio autem. Quia id officia quaerat dolorum optio. Illo laudantium aut ipsum dolorem.",
- "state": "opened",
- "created_at": "2016-04-05T21:42:23.233Z",
- "updated_at": "2016-04-05T22:11:52.900Z",
- "target_branch": "ui-dev-kit",
- "source_branch": "version-1-9",
+ "project_id": 3,
+ "title": "test1",
+ "description": "fixed login page css paddings",
+ "state": "merged",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
- "name": "Eileen Skiles",
- "username": "leila",
- "id": 19,
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/39ce4a2822cc896933ffbd68c1470e55?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/leila"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
- "name": "Celine Wehner",
- "username": "carli",
- "id": 16,
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/f4cd5605b769dd2ce405a27c6e6f2684?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/carli"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 5,
- "target_project_id": 5,
- "labels": [],
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
- "id": 7,
+ "id": 5,
"iid": 1,
- "project_id": 5,
+ "project_id": 3,
"title": "v2.0",
- "description": "Corrupti eveniet et velit occaecati dolorem est rerum aut.",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
- "created_at": "2016-04-05T21:41:40.905Z",
- "updated_at": "2016-04-05T21:41:40.905Z",
- "due_date": null
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
- "merge_when_pipeline_succeeds": false,
- "merge_status": "cannot_be_merged",
- "subscribed": true,
+ "merge_when_pipeline_succeeds": true,
+ "merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": null
+ "merge_commit_sha": null,
+ "user_notes_count": 1,
+ "discussion_locked": null,
+ "should_remove_source_branch": true,
+ "force_remove_source_branch": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
+ "time_stats": {
+ "time_estimate": 0,
+ "total_time_spent": 0,
+ "human_time_estimate": null,
+ "human_total_time_spent": null
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -1170,54 +1428,101 @@ Example response:
```json
{
- "id": 17,
+ "id": 1,
"iid": 1,
- "project_id": 5,
- "title": "Et et sequi est impedit nulla ut rem et voluptatem.",
- "description": "Consequatur velit eos rerum optio autem. Quia id officia quaerat dolorum optio. Illo laudantium aut ipsum dolorem.",
- "state": "opened",
- "created_at": "2016-04-05T21:42:23.233Z",
- "updated_at": "2016-04-05T22:11:52.900Z",
- "target_branch": "ui-dev-kit",
- "source_branch": "version-1-9",
+ "project_id": 3,
+ "title": "test1",
+ "description": "fixed login page css paddings",
+ "state": "merged",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
- "name": "Eileen Skiles",
- "username": "leila",
- "id": 19,
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/39ce4a2822cc896933ffbd68c1470e55?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/leila"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
- "name": "Celine Wehner",
- "username": "carli",
- "id": 16,
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/f4cd5605b769dd2ce405a27c6e6f2684?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/carli"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 5,
- "target_project_id": 5,
- "labels": [],
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
- "id": 7,
+ "id": 5,
"iid": 1,
- "project_id": 5,
+ "project_id": 3,
"title": "v2.0",
- "description": "Corrupti eveniet et velit occaecati dolorem est rerum aut.",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
- "created_at": "2016-04-05T21:41:40.905Z",
- "updated_at": "2016-04-05T21:41:40.905Z",
- "due_date": null
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
- "merge_when_pipeline_succeeds": false,
- "merge_status": "cannot_be_merged",
- "subscribed": false,
+ "merge_when_pipeline_succeeds": true,
+ "merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": null
+ "merge_commit_sha": null,
+ "user_notes_count": 1,
+ "discussion_locked": null,
+ "should_remove_source_branch": true,
+ "force_remove_source_branch": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
+ "time_stats": {
+ "time_estimate": 0,
+ "total_time_spent": 0,
+ "human_time_estimate": null,
+ "human_total_time_spent": null
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -1268,7 +1573,7 @@ Example response:
"project_id": 3,
"title": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
"description": "Veniam sunt nihil modi earum cumque illum delectus. Nihil ad quis distinctio quia. Autem eligendi at quibusdam repellendus.",
- "state": "opened",
+ "state": "merged",
"created_at": "2016-06-17T07:48:04.330Z",
"updated_at": "2016-07-01T11:14:15.537Z",
"target_branch": "allow_regex_for_project_skip_ref",
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index 07e66f89443..8f1a5c8e19b 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -19,7 +19,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `iids[]` | Array[integer] | optional | Return only the milestones having the given `iid` |
-| `state` | string | optional | Return only `active` or `closed` milestones` |
+| `state` | string | optional | Return only `active` or `closed` milestones |
| `search` | string | optional | Return only milestones with a title or description matching the provided string |
```bash
diff --git a/doc/api/notes.md b/doc/api/notes.md
index c271d46688f..9f6740ad86a 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -98,9 +98,9 @@ POST /projects/:id/issues/:issue_iid/notes
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
-- `issue_id` (required) - The IID of an issue
+- `issue_iid` (required) - The IID of an issue
- `body` (required) - The content of a note
-- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
+- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights)
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/notes?body=note
diff --git a/doc/api/notification_settings.md b/doc/api/notification_settings.md
index 682b90361bd..165b9a11c7a 100644
--- a/doc/api/notification_settings.md
+++ b/doc/api/notification_settings.md
@@ -15,7 +15,7 @@ mention
custom
```
-If the `custom` level is used, specific email events can be controlled. Notification email events are defined in the `NotificationSetting::EMAIL_EVENTS` model variable. Currently, these events are recognized:
+If the `custom` level is used, specific email events can be controlled. Available events are returned by `NotificationSetting.email_events`. Currently, these events are recognized:
```
new_note
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index 77bb7a55d8c..8a1e6b52092 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -1,6 +1,6 @@
# GitLab as an OAuth2 provider
-This document covers using the [OAuth2](https://oauth.net/2/) protocol to allow other services access Gitlab resources on user's behalf.
+This document covers using the [OAuth2](https://oauth.net/2/) protocol to allow other services access GitLab resources on user's behalf.
If you want GitLab to be an OAuth authentication service provider to sign into other services please see the [OAuth2 provider](../integration/oauth_provider.md)
documentation.
@@ -9,7 +9,7 @@ This functionality is based on [doorkeeper gem](https://github.com/doorkeeper-ge
## Supported OAuth2 Flows
-Gitlab currently supports following authorization flows:
+GitLab currently supports following authorization flows:
* *Web Application Flow* - Most secure and common type of flow, designed for the applications with secure server-side.
* *Implicit Flow* - This flow is designed for user-agent only apps (e.g. single page web application running on GitLab Pages).
@@ -76,7 +76,7 @@ Check [RFC spec](http://tools.ietf.org/html/rfc6749#section-4.2) for a detailed
Unlike the web flow, the client receives an `access token` immediately as a result of the authorization request. The flow does not use client secret
or authorization code because all of the application code and storage is easily accessible, therefore __secrets__ can leak easily.
->**Important:** Avoid using this flow for applications that store data outside of the Gitlab instance. If you do, make sure to verify `application id`
+>**Important:** Avoid using this flow for applications that store data outside of the GitLab instance. If you do, make sure to verify `application id`
associated with access token before granting access to the data
(see [/oauth/token/info](https://github.com/doorkeeper-gem/doorkeeper/wiki/API-endpoint-descriptions-and-examples#get----oauthtokeninfo)).
@@ -146,7 +146,7 @@ access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token
```
-## Access Gitlab API with `access token`
+## Access GitLab API with `access token`
The `access token` allows you to make requests to the API on a behalf of a user. You can pass the token either as GET parameter
```
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 574be52801c..7b4c9a8fbb3 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -235,5 +235,22 @@ Response:
}
```
+## Delete a pipeline
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22988) in GitLab 11.6.
+
+```
+DELETE /projects/:id/pipelines/:pipeline_id
+```
+
+| Attribute | Type | Required | Description |
+|------------|---------|----------|---------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `pipeline_id` | integer | yes | The ID of a pipeline |
+
+```
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --request "DELETE" "https://gitlab.example.com/api/v4/projects/1/pipelines/46"
+```
+
[ce-5837]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5837
[ce-7209]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7209
diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md
new file mode 100644
index 00000000000..ef98205cd68
--- /dev/null
+++ b/doc/api/project_templates.md
@@ -0,0 +1,139 @@
+# Project templates API
+
+This API is a project-specific version of these endpoints:
+
+- [Dockerfile templates](templates/dockerfiles.md)
+- [Gitignore templates](templates/gitignores.md)
+- [GitLab CI Config templates](templates/gitlab_ci_ymls.md)
+- [Open source license templates](templates/licenses.md)
+
+It deprecates these endpoints, which will be removed for API version 5.
+
+In addition to templates common to the entire instance, project-specific
+templates are also available from this API endpoint.
+
+Support will be added for [Issue and Merge Request templates](../user/project/description_templates.md)
+in a future release.
+
+Support for [Group-level file templates](../user/group/index.md#group-level-file-templates-premium)
+**[PREMIUM]** was [added](https://gitlab.com/gitlab-org/gitlab-ee/issues/5987)
+in GitLab 11.5
+
+## Get all templates of a particular type
+
+```
+GET /projects/:id/templates/:type
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses)` of the template |
+
+Example response (licenses):
+
+```json
+[
+ {
+ "key": "epl-1.0",
+ "name": "Eclipse Public License 1.0"
+ },
+ {
+ "key": "lgpl-3.0",
+ "name": "GNU Lesser General Public License v3.0"
+ },
+ {
+ "key": "unlicense",
+ "name": "The Unlicense"
+ },
+ {
+ "key": "agpl-3.0",
+ "name": "GNU Affero General Public License v3.0"
+ },
+ {
+ "key": "gpl-3.0",
+ "name": "GNU General Public License v3.0"
+ },
+ {
+ "key": "bsd-3-clause",
+ "name": "BSD 3-clause \"New\" or \"Revised\" License"
+ },
+ {
+ "key": "lgpl-2.1",
+ "name": "GNU Lesser General Public License v2.1"
+ },
+ {
+ "key": "mit",
+ "name": "MIT License"
+ },
+ {
+ "key": "apache-2.0",
+ "name": "Apache License 2.0"
+ },
+ {
+ "key": "bsd-2-clause",
+ "name": "BSD 2-clause \"Simplified\" License"
+ },
+ {
+ "key": "mpl-2.0",
+ "name": "Mozilla Public License 2.0"
+ },
+ {
+ "key": "gpl-2.0",
+ "name": "GNU General Public License v2.0"
+ }
+]
+```
+
+## Get one template of a particular type
+
+```
+GET /projects/:id/templates/:type/:key
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses)` of the template |
+| `key` | string | yes | The key of the template, as obtained from the collection endpoint |
+| `project` | string | no | The project name to use when expanding placeholders in the template. Only affects licenses |
+| `fullname` | string | no | The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses |
+
+Example response (Dockerfile):
+
+
+```json
+{
+ "name": "Binary",
+ "content": "# This file is a template, and might need editing before it works on your project.\n# This Dockerfile installs a compiled binary into a bare system.\n# You must either commit your compiled binary into source control (not recommended)\n# or build the binary first as part of a CI/CD pipeline.\n\nFROM buildpack-deps:jessie\n\nWORKDIR /usr/local/bin\n\n# Change `app` to whatever your binary is called\nAdd app .\nCMD [\"./app\"]\n"
+}
+
+```
+
+Example response (license):
+
+```json
+{
+ "key": "mit",
+ "name": "MIT License",
+ "nickname": null,
+ "popular": true,
+ "html_url": "http://choosealicense.com/licenses/mit/",
+ "source_url": "https://opensource.org/licenses/MIT",
+ "description": "A short and simple permissive license with conditions only requiring preservation of copyright and license notices. Licensed works, modifications, and larger works may be distributed under different terms and without source code.",
+ "conditions": [
+ "include-copyright"
+ ],
+ "permissions": [
+ "commercial-use",
+ "modifications",
+ "distribution",
+ "private-use"
+ ],
+ "limitations": [
+ "liability",
+ "warranty"
+ ],
+ "content": "MIT License\n\nCopyright (c) 2018 [fullname]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
+}
+```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index bda4164ee92..961241f31e1 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -451,6 +451,7 @@ GET /projects/:id
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `statistics` | boolean | no | Include project statistics |
+| `license` | boolean | no | Include project license data |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
```json
@@ -508,6 +509,14 @@ GET /projects/:id
},
"archived": false,
"avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+ "license_url": "http://example.com/diaspora/diaspora-client/blob/master/LICENSE",
+ "license": {
+ "key": "lgpl-3.0",
+ "name": "GNU Lesser General Public License v3.0",
+ "nickname": "GNU LGPLv3",
+ "html_url": "http://choosealicense.com/licenses/lgpl-3.0/",
+ "source_url": "http://www.gnu.org/licenses/lgpl-3.0.txt"
+ },
"shared_runners_enabled": true,
"forks_count": 0,
"star_count": 0,
@@ -572,6 +581,14 @@ If the project is a fork, and you provide a valid token to authenticate, the
"http_url_to_repo":"https://gitlab.com/gitlab-org/gitlab-ce.git",
"web_url":"https://gitlab.com/gitlab-org/gitlab-ce",
"avatar_url":"https://assets.gitlab-static.net/uploads/-/system/project/avatar/13083/logo-extra-whitespace.png",
+ "license_url": "https://gitlab.com/gitlab-org/gitlab-ce/blob/master/LICENSE",
+ "license": {
+ "key": "mit",
+ "name": "MIT License",
+ "nickname": null,
+ "html_url": "http://choosealicense.com/licenses/mit/",
+ "source_url": "https://opensource.org/licenses/MIT",
+ },
"star_count":3812,
"forks_count":3561,
"last_activity_at":"2018-01-02T11:40:26.570Z",
@@ -661,6 +678,7 @@ POST /projects
| `avatar` | mixed | no | Image file for avatar of the project |
| `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line |
| `ci_config_path` | string | no | The path to CI config file |
+| `initialize_with_readme` | boolean | no | `false` by default |
## Create project for user
@@ -710,7 +728,7 @@ PUT /projects/:id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
-| `name` | string | yes | The name of the project |
+| `name` | string | no | The name of the project |
| `path` | string | no | Custom repository name for the project. By default generated based on name |
| `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description |
@@ -904,6 +922,14 @@ Example response:
"import_status": "none",
"archived": true,
"avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+ "license_url": "http://example.com/diaspora/diaspora-client/blob/master/LICENSE",
+ "license": {
+ "key": "lgpl-3.0",
+ "name": "GNU Lesser General Public License v3.0",
+ "nickname": "GNU LGPLv3",
+ "html_url": "http://choosealicense.com/licenses/lgpl-3.0/",
+ "source_url": "http://www.gnu.org/licenses/lgpl-3.0.txt"
+ },
"shared_runners_enabled": true,
"forks_count": 0,
"star_count": 1,
@@ -982,6 +1008,14 @@ Example response:
"import_status": "none",
"archived": true,
"avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+ "license_url": "http://example.com/diaspora/diaspora-client/blob/master/LICENSE",
+ "license": {
+ "key": "lgpl-3.0",
+ "name": "GNU Lesser General Public License v3.0",
+ "nickname": "GNU LGPLv3",
+ "html_url": "http://choosealicense.com/licenses/lgpl-3.0/",
+ "source_url": "http://www.gnu.org/licenses/lgpl-3.0.txt"
+ },
"shared_runners_enabled": true,
"forks_count": 0,
"star_count": 0,
@@ -1100,6 +1134,14 @@ Example response:
},
"archived": true,
"avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+ "license_url": "http://example.com/diaspora/diaspora-client/blob/master/LICENSE",
+ "license": {
+ "key": "lgpl-3.0",
+ "name": "GNU Lesser General Public License v3.0",
+ "nickname": "GNU LGPLv3",
+ "html_url": "http://choosealicense.com/licenses/lgpl-3.0/",
+ "source_url": "http://www.gnu.org/licenses/lgpl-3.0.txt"
+ },
"shared_runners_enabled": true,
"forks_count": 0,
"star_count": 0,
@@ -1196,6 +1238,14 @@ Example response:
},
"archived": false,
"avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+ "license_url": "http://example.com/diaspora/diaspora-client/blob/master/LICENSE",
+ "license": {
+ "key": "lgpl-3.0",
+ "name": "GNU Lesser General Public License v3.0",
+ "nickname": "GNU LGPLv3",
+ "html_url": "http://choosealicense.com/licenses/lgpl-3.0/",
+ "source_url": "http://www.gnu.org/licenses/lgpl-3.0.txt"
+ },
"shared_runners_enabled": true,
"forks_count": 0,
"star_count": 0,
@@ -1334,6 +1384,7 @@ GET /projects/:id/hooks/:hook_id
"url": "http://example.com/hook",
"project_id": 3,
"push_events": true,
+ "push_events_branch_filter": "",
"issues_events": true,
"confidential_issues_events": true,
"merge_requests_events": true,
@@ -1360,6 +1411,7 @@ POST /projects/:id/hooks
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `url` | string | yes | The hook URL |
| `push_events` | boolean | no | Trigger hook on push events |
+| `push_events_branch_filter` | string | no | Trigger hook on push events for matching branches only |
| `issues_events` | boolean | no | Trigger hook on issues events |
| `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events |
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
@@ -1385,6 +1437,7 @@ PUT /projects/:id/hooks/:hook_id
| `hook_id` | integer | yes | The ID of the project hook |
| `url` | string | yes | The hook URL |
| `push_events` | boolean | no | Trigger hook on push events |
+| `push_events_branch_filter` | string | no | Trigger hook on push events for matching branches only |
| `issues_events` | boolean | no | Trigger hook on issues events |
| `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events |
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
@@ -1413,12 +1466,17 @@ DELETE /projects/:id/hooks/:hook_id
Note the JSON response differs if the hook is available or not. If the project hook
is available before it is returned in the JSON response or an empty response is returned.
-## Admin fork relation
+## Fork relationship
-Allows modification of the forked relationship between existing projects. Available only for admins.
+Allows modification of the forked relationship between existing projects. Available only for project owners and admins.
### Create a forked from/to relation between existing projects
+CAUTION: **Warning:**
+This will destroy the LFS objects stored in the fork.
+So to retain the LFS objects, make sure you've pulled them **before** creating the fork relation,
+and push them again **after** creating the fork relation.
+
```
POST /projects/:id/fork/:forked_from_id
```
diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md
index f6813f27dc0..ed8837574a0 100644
--- a/doc/api/protected_branches.md
+++ b/doc/api/protected_branches.md
@@ -4,7 +4,7 @@
**Valid access levels**
-The access levels are defined in the `ProtectedRefAccess::ALLOWED_ACCESS_LEVELS` constant. Currently, these levels are recognized:
+The access levels are defined in the `ProtectedRefAccess.allowed_access_levels` method. Currently, these levels are recognized:
```
0 => No access
30 => Developer access
diff --git a/doc/api/protected_tags.md b/doc/api/protected_tags.md
new file mode 100644
index 00000000000..aa750e467f8
--- /dev/null
+++ b/doc/api/protected_tags.md
@@ -0,0 +1,128 @@
+# Protected tags API
+
+>**Note:** This feature was introduced in GitLab 11.3
+
+**Valid access levels**
+
+Currently, these levels are recognized:
+```
+0 => No access
+30 => Developer access
+40 => Maintainer access
+```
+
+## List protected tags
+
+Gets a list of protected tags from a project.
+This function takes pagination parameters `page` and `per_page` to restrict the list of protected tags.
+
+```
+GET /projects/:id/protected_tags
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags'
+```
+
+Example response:
+
+```json
+[
+ {
+ "name": "release-1-0",
+ "create_access_levels": [
+ {
+ "access_level": 40,
+ "access_level_description": "Maintainers"
+ }
+ ]
+ },
+ ...
+]
+```
+
+## Get a single protected tag or wildcard protected tag
+
+Gets a single protected tag or wildcard protected tag.
+The pagination parameters `page` and `per_page` can be used to restrict the list of protected tags.
+
+```
+GET /projects/:id/protected_tags/:name
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `name` | string | yes | The name of the tag or wildcard |
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags/release-1-0'
+```
+
+Example response:
+
+```json
+{
+ "name": "release-1-0",
+ "create_access_levels": [
+ {
+ "access_level": 40,
+ "access_level_description": "Maintainers"
+ }
+ ]
+}
+```
+
+## Protect repository tags
+
+Protects a single repository tag or several project repository
+tags using a wildcard protected tag.
+
+```
+POST /projects/:id/protected_tags
+```
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags?name=*-stable&create_access_level=30'
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `name` | string | yes | The name of the tag or wildcard |
+| `create_access_level` | string | no | Access levels allowed to create (defaults: `40`, maintainer access level) |
+
+Example response:
+
+```json
+{
+ "name": "*-stable",
+ "create_access_levels": [
+ {
+ "access_level": 30,
+ "access_level_description": "Developers + Maintainers"
+ }
+ ]
+}
+```
+
+## Unprotect repository tags
+
+Unprotects the given protected tag or wildcard protected tag.
+
+```
+DELETE /projects/:id/protected_tags/:name
+```
+
+```bash
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags/*-stable'
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `name` | string | yes | The name of the tag |
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index a4fdeca162e..c8de7f2191d 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -14,9 +14,10 @@ GET /projects/:id/repository/tree
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
-- `path` (optional) - The path inside repository. Used to get contend of subdirectories
+- `path` (optional) - The path inside repository. Used to get content of subdirectories
- `ref` (optional) - The name of a repository branch or tag or if not given the default branch
- `recursive` (optional) - Boolean value used to get a recursive tree (false by default)
+- `per_page` (optional) - Number of results to show per page. If not specified, defaults to `20`
```json
[
@@ -216,7 +217,7 @@ GET /projects/:id/repository/merge_base
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
-| `refs` | array | yes | The refs to find the common ancestor of, for now only 2 refs are supported |
+| `refs` | array | yes | The refs to find the common ancestor of, multiple refs can be passed |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/merge_base?refs[]=304d257dcb821665ab5110318fc58a007bd104ed&refs[]=0031876facac3f2b2702a0e53a26e89939a42209"
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
index 49fb9bc141d..c3624f1a535 100644
--- a/doc/api/repository_files.md
+++ b/doc/api/repository_files.md
@@ -90,12 +90,17 @@ Like [Get file from repository](repository_files.md#get-file-from-repository) yo
## Create new file in repository
+This allows you to create a single file. For creating multiple files with a single request see the [commits API](commits.html#create-a-commit-with-multiple-files-and-actions).
+
```
POST /projects/:id/repository/files/:file_path
```
```bash
-curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fprojectrb%2E?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20content&commit_message=create%20a%20new%20file'
+curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' --header "Content-Type: application/json" \
+ --data '{"branch": "master", "author_email": "author@example.com", "author_name": "Firstname Lastname", \
+ "content": "some content", "commit_message": "create a new file"}' \
+ 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb'
```
Example response:
@@ -120,12 +125,17 @@ Parameters:
## Update existing file in repository
+This allows you to update a single file. For updating multiple files with a single request see the [commits API](commits.html#create-a-commit-with-multiple-files-and-actions).
+
```
PUT /projects/:id/repository/files/:file_path
```
```bash
-curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20other%20content&commit_message=update%20file'
+curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' --header "Content-Type: application/json" \
+ --data '{"branch": "master", "author_email": "author@example.com", "author_name": "Firstname Lastname", \
+ "content": "some content", "commit_message": "update file"}' \
+ 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb'
```
Example response:
@@ -160,12 +170,17 @@ Currently gitlab-shell has a boolean return code, preventing GitLab from specify
## Delete existing file in repository
+This allows you to delete a single file. For deleting multiple files with a singleh request see the [commits API](commits.html#create-a-commit-with-multiple-files-and-actions).
+
```
DELETE /projects/:id/repository/files/:file_path
```
```bash
-curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&commit_message=delete%20file'
+curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' --header "Content-Type: application/json" \
+ --data '{"branch": "master", "author_email": "author@example.com", "author_name": "Firstname Lastname", \
+ "commit_message": "delete file"}' \
+ 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb'
```
Parameters:
diff --git a/doc/api/repository_storage_health.md b/doc/api/repository_storage_health.md
index e0c0315c2d7..edf4b04acea 100644
--- a/doc/api/repository_storage_health.md
+++ b/doc/api/repository_storage_health.md
@@ -1,74 +1,5 @@
# Circuitbreaker API
-> [Introduced][ce-11449] in GitLab 9.5.
-
-The Circuitbreaker API is only accessible to administrators. All requests by
-guests will respond with `401 Unauthorized`, and all requests by normal users
-will respond with `403 Forbidden`.
-
-## Repository Storages
-
-### Get all storage information
-
-Returns of all currently configured storages and their health information.
-
-```
-GET /circuit_breakers/repository_storage
-```
-
-```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/circuit_breakers/repository_storage
-```
-
-```json
-[
- {
- "storage_name": "default",
- "failing_on_hosts": [],
- "total_failures": 0
- },
- {
- "storage_name": "broken",
- "failing_on_hosts": [
- "web01", "worker01"
- ],
- "total_failures": 1
- }
-]
-```
-
-### Get failing storages
-
-This returns a list of all currently failing storages.
-
-```
-GET /circuit_breakers/repository_storage/failing
-```
-
-```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/circuit_breakers/repository_storage/failing
-```
-
-```json
-[
- {
- "storage_name":"broken",
- "failing_on_hosts":["web01", "worker01"],
- "total_failures":2
- }
-]
-```
-
-## Reset failing storage information
-
-Use this remove all failing storage information and allow access to the storage again.
-
-```
-DELETE /circuit_breakers/repository_storage
-```
-
-```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/circuit_breakers/repository_storage
-```
-
-[ce-11449]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11449
+NOTE: **Deprecated:**
+Support of the circuit breaker is removed, as Gitaly can be configured to
+to work without NFS and [communicate solely over HTTP](../administration/gitaly/index.md).
diff --git a/doc/api/repository_submodules.md b/doc/api/repository_submodules.md
new file mode 100644
index 00000000000..2e6797f18f4
--- /dev/null
+++ b/doc/api/repository_submodules.md
@@ -0,0 +1,49 @@
+# Repository submodules API
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/41213) in GitLab 11.5
+
+## Update existing submodule reference in repository
+
+In some workflows, especially automated ones, it can be useful to update a
+submodule's reference to keep up to date other projects that use it.
+This endpoint allows you to update a [Git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) reference in a
+specific branch.
+
+```
+PUT /projects/:id/repository/submodules/:submodule
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `submodule` | string | yes | URL encoded full path to the submodule. For example, `lib%2Fclass%2Erb` |
+| `branch` | string | yes | Name of the branch to commit into |
+| `commit_sha` | string | yes | Full commit SHA to update the submodule to |
+| `commit_message` | string | no | Commit message. If no message is provided, a default one will be set |
+
+```sh
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repositories/submodules/lib%2Fmodules%2Fexample"
+--data "branch=master&commit_sha=3ddec28ea23acc5caa5d8331a6ecb2a65fc03e88&commit_message=Update submodule reference"
+```
+
+Example response:
+
+```json
+{
+ "id": "ed899a2f4b50b4370feeea94676502b42383c746",
+ "short_id": "ed899a2f4b5",
+ "title": "Updated submodule example_submodule with oid 3ddec28ea23acc5caa5d8331a6ecb2a65fc03e88",
+ "author_name": "Dmitriy Zaporozhets",
+ "author_email": "dzaporozhets@sphereconsultinginc.com",
+ "committer_name": "Dmitriy Zaporozhets",
+ "committer_email": "dzaporozhets@sphereconsultinginc.com",
+ "created_at": "2018-09-20T09:26:24.000-07:00",
+ "message": "Updated submodule example_submodule with oid 3ddec28ea23acc5caa5d8331a6ecb2a65fc03e88",
+ "parent_ids": [
+ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
+ ],
+ "committed_date": "2018-09-20T09:26:24.000-07:00",
+ "authored_date": "2018-09-20T09:26:24.000-07:00",
+ "status": null
+}
+```
diff --git a/doc/api/resource_label_events.md b/doc/api/resource_label_events.md
new file mode 100644
index 00000000000..33e4821ccf4
--- /dev/null
+++ b/doc/api/resource_label_events.md
@@ -0,0 +1,175 @@
+# Resource label events API
+
+Resource label events keep track about who, when, and which label was added or removed to an issuable.
+
+## Issues
+
+### List project issue label events
+
+Gets a list of all label events for a single issue.
+
+```
+GET /projects/:id/issues/:issue_iid/resource_label_events
+```
+
+| Attribute | Type | Required | Description |
+| ------------------- | ---------------- | ---------- | ------------ |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `issue_iid` | integer | yes | The IID of an issue |
+
+```json
+[
+ {
+ "id": 142,
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.example.com/root"
+ },
+ "created_at": "2018-08-20T13:38:20.077Z",
+ "resource_type": "Issue",
+ "resource_id": 253,
+ "label": {
+ "id": 73,
+ "name": "a1",
+ "color": "#34495E",
+ "description": ""
+ },
+ "action": "add"
+ },
+ {
+ "id": 143,
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.example.com/root"
+ },
+ "created_at": "2018-08-20T13:38:20.077Z",
+ "resource_type": "Issue",
+ "resource_id": 253,
+ "label": {
+ "id": 74,
+ "name": "p1",
+ "color": "#0033CC",
+ "description": ""
+ },
+ "action": "remove"
+ }
+]
+```
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/resource_label_events
+```
+
+### Get single issue label event
+
+Returns a single label event for a specific project issue
+
+```
+GET /projects/:id/issues/:issue_iid/resource_label_events/:resource_label_event_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| --------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `issue_iid` | integer | yes | The IID of an issue |
+| `resource_label_event_id` | integer | yes | The ID of a label event |
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/resource_label_events/1
+```
+
+## Merge requests
+
+### List project merge request label events
+
+Gets a list of all label events for a single merge request.
+
+```
+GET /projects/:id/merge_requests/:merge_request_iid/resource_label_events
+```
+
+| Attribute | Type | Required | Description |
+| ------------------- | ---------------- | ---------- | ------------ |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+
+```json
+[
+ {
+ "id": 119,
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.example.com/root"
+ },
+ "created_at": "2018-08-20T06:17:28.394Z",
+ "resource_type": "MergeRequest",
+ "resource_id": 28,
+ "label": {
+ "id": 74,
+ "name": "p1",
+ "color": "#0033CC",
+ "description": ""
+ },
+ "action": "add"
+ },
+ {
+ "id": 120,
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.example.com/root"
+ },
+ "created_at": "2018-08-20T06:17:28.394Z",
+ "resource_type": "MergeRequest",
+ "resource_id": 28,
+ "label": {
+ "id": 41,
+ "name": "project",
+ "color": "#D1D100",
+ "description": ""
+ },
+ "action": "add"
+ }
+]
+```
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/resource_label_events
+```
+
+### Get single merge request label event
+
+Returns a single label event for a specific project merge request
+
+```
+GET /projects/:id/merge_requests/:merge_request_iid/resource_label_events/:resource_label_event_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+| `resource_label_event_id` | integer | yes | The ID of a label event |
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/resource_label_events/120
+```
diff --git a/doc/api/runners.md b/doc/api/runners.md
index ac814bbf19a..071c13f41cb 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -11,11 +11,15 @@ Get a list of specific runners available to the user.
```
GET /runners
GET /runners?scope=active
+GET /runners?type=project_type
+GET /runners?status=active
```
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| `scope` | string | no | The scope of specific runners to show, one of: `active`, `paused`, `online`; showing all runners if none provided |
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners"
@@ -56,11 +60,15 @@ is restricted to users with `admin` privileges.
```
GET /runners/all
GET /runners/all?scope=online
+GET /runners/all?type=project_type
+GET /runners/all?status=active
```
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| `scope` | string | no | The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online`; showing all runners if none provided |
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online`, `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/all"
@@ -286,6 +294,7 @@ Example response:
"created_at": "2017-11-16T18:38:46.000Z",
"bio": null,
"location": null,
+ "public_email": "",
"skype": "",
"linkedin": "",
"twitter": "",
@@ -331,16 +340,21 @@ Example response:
## List project's runners
List all runners (specific and shared) available in the project. Shared runners
-are listed if at least one shared runner is defined **and** shared runners
-usage is enabled in the project's settings.
+are listed if at least one shared runner is defined.
```
GET /projects/:id/runners
+GET /projects/:id/runners?scope=active
+GET /projects/:id/runners?type=project_type
+GET /projects/:id/runners?status=active
```
-| Attribute | Type | Required | Description |
-|-----------|---------|----------|---------------------|
+| Attribute | Type | Required | Description |
+|-----------|----------------|----------|---------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners"
diff --git a/doc/api/services.md b/doc/api/services.md
index efa173180bb..f122bac6f1f 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -10,7 +10,7 @@ Asana - Teamwork without email
Set Asana service for a project.
-> This service adds commit messages as comments to Asana tasks. Once enabled, commit messages are checked for Asana task URLs (for example, `https://app.asana.com/0/123456/987654`) or task IDs starting with # (for example, `#987654`). Every task ID found will get the commit comment added to it. You can also close a task with a message containing: `fix #123456`. You can find your Api Keys here: https://asana.com/developers/documentation/getting-started/auth#api-key
+> This service adds commit messages as comments to Asana tasks. Once enabled, commit messages are checked for Asana task URLs (for example, `https://app.asana.com/0/123456/987654`) or task IDs starting with # (for example, `#987654`). Every task ID found will get the commit comment added to it. You can also close a task with a message containing: `fix #123456`. You can find your Api Keys here: <https://asana.com/developers/documentation/getting-started/auth#api-key>.
```
PUT /projects/:id/services/asana
@@ -92,7 +92,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `bamboo_url` | string | true | Bamboo root URL like https://bamboo.example.com |
+| `bamboo_url` | string | true | Bamboo root URL. For example, `https://bamboo.example.com`. |
| `build_key` | string | true | Bamboo build plan key like KEY |
| `username` | string | true | A user with API access, if applicable |
| `password` | string | true | Password of the user |
@@ -117,7 +117,7 @@ GET /projects/:id/services/bamboo
Bugzilla Issue Tracker
-### Create/Edit Buildkite service
+### Create/Edit Bugzilla service
Set Bugzilla service for a project.
@@ -168,7 +168,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `token` | string | true | Buildkite project GitLab token |
-| `project_url` | string | true | https://buildkite.com/example/project |
+| `project_url` | string | true | `https://buildkite.com/example/project` |
| `enable_ssl_verification` | boolean | false | Enable SSL verification |
### Delete Buildkite service
@@ -278,7 +278,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `token` | string | true | Drone CI project specific token |
-| `drone_url` | string | true | http://drone.example.com |
+| `drone_url` | string | true | `http://drone.example.com` |
| `enable_ssl_verification` | boolean | false | Enable SSL verification |
### Delete Drone CI service
@@ -401,48 +401,6 @@ Get Flowdock service settings for a project.
GET /projects/:id/services/flowdock
```
-## Gemnasium
-
-Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities.
-
-CAUTION: **Warning:**
-Gemnasium service integration has been deprecated in GitLab 11.0. Gemnasium has been
-[acquired by GitLab](https://about.gitlab.com/press/releases/2018-01-30-gemnasium-acquisition.html)
-in January 2018 and since May 15, 2018, the service provided by Gemnasium is no longer available.
-You can [migrate from Gemnasium to GitLab](https://docs.gitlab.com/ee/user/project/import/gemnasium.html)
-to keep monitoring your dependencies.
-
-### Create/Edit Gemnasium service
-
-Set Gemnasium service for a project.
-
-```
-PUT /projects/:id/services/gemnasium
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `api_key` | string | true | Your personal API KEY on gemnasium.com |
-| `token` | string | true | The project's slug on gemnasium.com |
-
-### Delete Gemnasium service
-
-Delete Gemnasium service for a project.
-
-```
-DELETE /projects/:id/services/gemnasium
-```
-
-### Get Gemnasium service settings
-
-Get Gemnasium service settings for a project.
-
-```
-GET /projects/:id/services/gemnasium
-```
-
## Hangouts Chat
Google GSuite team collaboration tool.
@@ -463,7 +421,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces... |
+| `webhook` | string | true | The Hangouts Chat webhook. For example, `https://chat.googleapis.com/v1/spaces...`. |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
| `notify_only_default_branch` | boolean | false | Send notifications only for the default branch |
| `push_events` | boolean | false | Enable notifications for push events |
@@ -512,7 +470,7 @@ Parameters:
| `notify` | boolean | false | Enable notifications |
| `room` | string | false |Room name or ID |
| `api_version` | string | false | Leave blank for default (v2) |
-| `server` | string | false | Leave blank for default. https://hipchat.example.com |
+| `server` | string | false | Leave blank for default. For example, `https://hipchat.example.com`. |
### Delete HipChat service
@@ -538,7 +496,7 @@ Send IRC messages, on update, to a list of recipients through an Irker gateway.
Set Irker (IRC gateway) service for a project.
-> NOTE: Irker does NOT have built-in authentication, which makes it vulnerable to spamming IRC channels if it is hosted outside of a firewall. Please make sure you run the daemon within a secured network to prevent abuse. For more details, read: http://www.catb.org/~esr/irker/security.html.
+> NOTE: Irker does NOT have built-in authentication, which makes it vulnerable to spamming IRC channels if it is hosted outside of a firewall. Please make sure you run the daemon within a secured network to prevent abuse. For more details, read: <http://www.catb.org/~esr/irker/security.html>.
```
PUT /projects/:id/services/irker
@@ -586,10 +544,10 @@ GET /projects/:id/services/jira
Set JIRA service for a project.
->**Notes:**
-- Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
- `project_url` are replaced by `project_key`, `url`. If you are using an
- older version, [follow this documentation][old-jira-api].
+> **Notes:**
+> - Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
+> `project_url` are replaced by `project_key`, `url`. If you are using an
+> older version, [follow this documentation][old-jira-api].
```
PUT /projects/:id/services/jira
@@ -599,7 +557,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `url` | string | yes | The URL to the JIRA project which is being linked to this GitLab project, e.g., `https://jira.example.com`. |
+| `url` | string | yes | The URL to the JIRA project which is being linked to this GitLab project. For example, `https://jira.example.com`. |
| `project_key` | string | yes | The short identifier for your JIRA project, all uppercase, e.g., `PROJ`. |
| `username` | string | yes | The username of the user created to be used with GitLab/JIRA. |
| `password` | string | yes | The password of the user created to be used with GitLab/JIRA. |
@@ -631,7 +589,7 @@ PUT /projects/:id/services/kubernetes
Parameters:
- `namespace` (**required**) - The Kubernetes namespace to use
-- `api_url` (**required**) - The URL to the Kubernetes cluster API, e.g., https://kubernetes.example.com
+- `api_url` (**required**) - The URL to the Kubernetes cluster API. For example, `https://kubernetes.example.com`
- `token` (**required**) - The service token to authenticate against the Kubernetes cluster with
- `ca_pem` (optional) - A custom certificate authority bundle to verify the Kubernetes cluster with (PEM format)
@@ -700,7 +658,6 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `token` | string | yes | The Slack token |
-
### Delete Slack slash command service
Delete Slack slash command service for a project.
@@ -865,7 +822,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `api_url` | string | true | Prometheus API Base URL, like http://prometheus.example.com/ |
+| `api_url` | string | true | Prometheus API Base URL. For example, `http://prometheus.example.com/`. |
### Delete Prometheus service
@@ -976,7 +933,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | https://hooks.slack.com/services/... |
+| `webhook` | string | true | `https://hooks.slack.com/services/...` |
| `username` | string | false | username |
| `channel` | string | false | Default channel to use if others are not configured |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
@@ -1030,7 +987,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/... |
+| `webhook` | string | true | The Microsoft Teams webhook. For example, `https://outlook.office.com/webhook/...` |
### Delete Microsoft Teams service
@@ -1066,7 +1023,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Mattermost webhook. e.g. http://mattermost_host/hooks/... |
+| `webhook` | string | true | The Mattermost webhook. For example, `http://mattermost_host/hooks/...` |
| `username` | string | false | username |
| `channel` | string | false | Default channel to use if others are not configured |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
@@ -1122,7 +1079,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `teamcity_url` | string | true | TeamCity root URL like https://teamcity.example.com |
+| `teamcity_url` | string | true | TeamCity root URL. For example, `https://teamcity.example.com` |
| `build_type` | string | true | Build configuration ID |
| `username` | string | true | A user with permissions to trigger a manual build |
| `password` | string | true | The password of the user |
@@ -1146,7 +1103,6 @@ GET /projects/:id/services/teamcity
[jira-doc]: ../user/project/integrations/jira.md
[old-jira-api]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-13-stable/doc/api/services.md#jira
-
## MockCI
Mock an external CI. See [`gitlab-org/gitlab-mock-ci-service`](https://gitlab.com/gitlab-org/gitlab-mock-ci-service) for an example of a companion mock service.
@@ -1165,7 +1121,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `mock_service_url` | string | true | http://localhost:4004 |
+| `mock_service_url` | string | true | `http://localhost:4004` |
### Delete MockCI service
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 68fc56b1fa3..9b38e3a4eb7 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -1,12 +1,18 @@
+---
+table_display_block: true
+---
+
# Application settings API
-These API calls allow you to read and modify GitLab instance application
-settings as appear in `/admin/application_settings`. You have to be an
+These API calls allow you to read and modify GitLab instance
+[application settings](#list-of-settings-that-can-be-accessed-via-api-calls)
+as appear in `/admin/application_settings`. You have to be an
administrator in order to perform this action.
## Get current application settings
-List the current application settings of the GitLab instance.
+List the current [application settings](#list-of-settings-that-can-be-accessed-via-api-calls)
+of the GitLab instance.
```
GET /application/settings
@@ -43,8 +49,6 @@ Example response:
"sign_in_text" : null,
"container_registry_token_expire_delay": 5,
"repository_storages": ["default"],
- "koding_enabled": false,
- "koding_url": null,
"plantuml_enabled": false,
"plantuml_url": null,
"terminal_max_session_time": 0,
@@ -56,112 +60,20 @@ Example response:
"enforce_terms": true,
"terms": "Hello world!",
"performance_bar_allowed_group_id": 42,
- "instance_statistics_visibility_private": false
+ "instance_statistics_visibility_private": false,
+ "user_show_add_ssh_key_message": true
}
```
## Change application settings
+Use an API call to modify GitLab instance
+[application settings](#list-of-settings-that-can-be-accessed-via-api-calls).
+
```
PUT /application/settings
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | :------: | ----------- |
-| `admin_notification_email` | string | no | Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area. |
-| `after_sign_out_path` | string | no | Where to redirect users after logout |
-| `after_sign_up_text` | string | no | Text shown to the user after signing up |
-| `akismet_api_key` | string | no | API key for akismet spam protection |
-| `akismet_enabled` | boolean | no | Enable or disable akismet spam protection |
-| `circuitbreaker_access_retries` | integer | no | The number of attempts GitLab will make to access a storage. |
-| `circuitbreaker_check_interval` | integer | no | Number of seconds in between storage checks. |
-| `circuitbreaker_failure_count_threshold` | integer | no | The number of failures of after which GitLab will completely prevent access to the storage. |
-| `circuitbreaker_failure_reset_time` | integer | no | Time in seconds GitLab will keep storage failure information. When no failures occur during this time, the failure information is reset. |
-| `circuitbreaker_storage_timeout` | integer | no | Seconds to wait for a storage access attempt |
-| `clientside_sentry_dsn` | string | no | Required if `clientside_sentry_dsn` is enabled |
-| `clientside_sentry_enabled` | boolean | no | Enable Sentry error reporting for the client side |
-| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
-| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts |
-| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and maintainers can push new commits, force push, or delete the branch)_, `1` _(partially protected, developers and maintainers can push new commits, but cannot force push or delete the branch)_ or `2` _(fully protected, developers cannot push new commits, but maintainers can; no-one can force push or delete the branch)_ as a parameter. Default is `2`. |
-| `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
-| `default_project_visibility` | string | no | What visibility level new projects receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
-| `default_projects_limit` | integer | no | Project limit per user. Default is `100000` |
-| `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
-| `disabled_oauth_sign_in_sources` | Array of strings | no | Disabled OAuth sign-in sources |
-| `domain_blacklist_enabled` | boolean | no | Enable/disable the `domain_blacklist` |
-| `domain_blacklist` | array of strings | yes (if `domain_blacklist_enabled` is `true`) | People trying to sign-up with emails from this domain will not be allowed to do so. |
-| `domain_whitelist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is null, meaning there is no restriction. |
-| `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. |
-| `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. |
-| `ed25519_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ED25519 key. Default is `0` (no restriction). `-1` disables ED25519 keys. |
-| `email_author_in_body` | boolean | no | Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead. |
-| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
-| `gravatar_enabled` | boolean | no | Enable Gravatar |
-| `help_page_hide_commercial_content` | boolean | no | Hide marketing-related entries from help |
-| `help_page_support_url` | string | no | Alternate support URL for help page |
-| `home_page_url` | string | no | Redirect to this URL when not logged in |
-| `housekeeping_bitmaps_enabled` | boolean | no | Enable Git pack file bitmap creation |
-| `housekeeping_enabled` | boolean | no | Enable or disable git housekeeping |
-| `housekeeping_full_repack_period` | integer | no | Number of Git pushes after which an incremental 'git repack' is run. |
-| `housekeeping_gc_period` | integer | no | Number of Git pushes after which 'git gc' is run. |
-| `housekeeping_incremental_repack_period` | integer | no | Number of Git pushes after which an incremental 'git repack' is run. |
-| `html_emails_enabled` | boolean | no | Enable HTML emails |
-| `import_sources` | Array of strings | no | Sources to allow project import from, possible values: "github bitbucket gitlab google_code fogbugz git gitlab_project manifest |
-| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
-| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
-| `max_artifacts_size` | integer | no | Maximum artifacts size in MB |
-| `max_attachment_size` | integer | no | Limit attachment size in MB |
-| `max_pages_size` | integer | no | Maximum size of pages repositories in MB |
-| `metrics_enabled` | boolean | no | Enable influxDB metrics |
-| `metrics_host` | string | yes (if `metrics_enabled` is `true`) | InfluxDB host |
-| `metrics_method_call_threshold` | integer | yes (if `metrics_enabled` is `true`) | A method call is only tracked when it takes longer than the given amount of milliseconds |
-| `metrics_packet_size` | integer | yes (if `metrics_enabled` is `true`) | The amount of datapoints to send in a single UDP packet. |
-| `metrics_pool_size` | integer | yes (if `metrics_enabled` is `true`) | The amount of InfluxDB connections to keep open |
-| `metrics_port` | integer | no | The UDP port to use for connecting to InfluxDB |
-| `metrics_sample_interval` | integer | yes (if `metrics_enabled` is `true`) | The sampling interval in seconds. |
-| `metrics_timeout` | integer | yes (if `metrics_enabled` is `true`) | The amount of seconds after which InfluxDB will time out. |
-| `password_authentication_enabled_for_web` | boolean | no | Enable authentication for the web interface via a GitLab account password. Default is `true`. |
-| `password_authentication_enabled_for_git` | boolean | no | Enable authentication for Git over HTTP(S) via a GitLab account password. Default is `true`. |
-| `performance_bar_allowed_group_path` | string | no | Path of the group that is allowed to toggle the performance bar |
-| `performance_bar_allowed_group_id` | string | no | Deprecated: Use `performance_bar_allowed_group_path` instead. Path of the group that is allowed to toggle the performance bar |
-| `performance_bar_enabled` | boolean | no | Deprecated: Pass `performance_bar_allowed_group_path: nil` instead. Allow enabling the performance bar |
-| `plantuml_enabled` | boolean | no | Enable PlantUML integration. Default is `false`. |
-| `plantuml_url` | string | yes (if `plantuml_enabled` is `true`) | The PlantUML instance URL for integration. |
-| `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling. |
-| `project_export_enabled` | boolean | no | Enable project export |
-| `prometheus_metrics_enabled` | boolean | no | Enable prometheus metrics |
-| `recaptcha_enabled` | boolean | no | Enable recaptcha |
-| `recaptcha_private_key` | string | yes (if `recaptcha_enabled` is true) | Private key for recaptcha |
-| `recaptcha_site_key` | string | yes (if `recaptcha_enabled` is true) | Site key for recaptcha |
-| `repository_checks_enabled` | boolean | no | GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues. |
-| `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. |
-| `require_two_factor_authentication` | boolean | no | Require all users to setup Two-factor authentication |
-| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is null which means there is no restriction. |
-| `rsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded RSA key. Default is `0` (no restriction). `-1` disables RSA keys. |
-| `send_user_confirmation_email` | boolean | no | Send confirmation email on sign-up |
-| `sentry_dsn` | string | yes (if `sentry_enabled` is true) | Sentry Data Source Name |
-| `sentry_enabled` | boolean | no | Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here: https://getsentry.com |
-| `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes |
-| `shared_runners_enabled` | true | no | Enable shared runners for new projects |
-| `shared_runners_text` | string | no | Shared runners text |
-| `sidekiq_throttling_enabled` | boolean | no | Enable Sidekiq Job Throttling |
-| `sidekiq_throttling_factor` | decimal | yes (if `sidekiq_throttling_enabled` is true) | The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive. |
-| `sidekiq_throttling_queues` | array of strings | yes (if `sidekiq_throttling_enabled` is true) | Choose which queues you wish to throttle |
-| `sign_in_text` | string | no | Text on login page |
-| `signup_enabled` | boolean | no | Enable registration. Default is `true`. |
-| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time. |
-| `two_factor_grace_period` | integer | no | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication |
-| `unique_ips_limit_enabled` | boolean | no | Limit sign in from multiple ips |
-| `unique_ips_limit_per_user` | integer | yes (if `unique_ips_limit_enabled` is true) | Maximum number of ips per user |
-| `unique_ips_limit_time_window` | integer | yes (if `unique_ips_limit_enabled` is true) | How many seconds an IP will be counted towards the limit |
-| `usage_ping_enabled` | boolean | no | Every week GitLab will report license usage back to GitLab, Inc. |
-| `user_default_external` | boolean | no | Newly registered users will by default be external |
-| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
-| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
-| `enforce_terms` | boolean | no | Enforce application ToS to all users |
-| `terms` | text | yes (if `enforce_terms` is true) | Markdown content for the ToS |
-| `instance_statistics_visibility_private` | boolean | no | When set to `true` Instance statistics will only be available to admins |
-
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings?signup_enabled=false&default_project_visibility=internal
```
@@ -172,7 +84,7 @@ Example response:
{
"id": 1,
"default_projects_limit": 100000,
- "signup_enabled": true,
+ "signup_enabled": false,
"password_authentication_enabled_for_web": true,
"gravatar_enabled": true,
"sign_in_text": "",
@@ -193,8 +105,6 @@ Example response:
"after_sign_out_path": "",
"container_registry_token_expire_delay": 5,
"repository_storages": ["default"],
- "koding_enabled": false,
- "koding_url": null,
"plantuml_enabled": false,
"plantuml_url": null,
"terminal_max_session_time": 0,
@@ -206,6 +116,122 @@ Example response:
"enforce_terms": true,
"terms": "Hello world!",
"performance_bar_allowed_group_id": 42,
- "instance_statistics_visibility_private": false
+ "instance_statistics_visibility_private": false,
+ "user_show_add_ssh_key_message": true
}
```
+
+## List of settings that can be accessed via API calls
+
+In general, all settings are optional. Certain settings though, if enabled, will
+require other settings to be set in order to function properly. These requirements
+are listed in the descriptions of the relevant settings.
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | :------: | ----------- |
+| `admin_notification_email` | string | no | Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area. |
+| `after_sign_out_path` | string | no | Where to redirect users after logout. |
+| `after_sign_up_text` | string | no | Text shown to the user after signing up |
+| `akismet_api_key` | string | required by: `akismet_enabled` | API key for akismet spam protection. |
+| `akismet_enabled` | boolean | no | (**If enabled, requires:** `akismet_api_key`) Enable or disable akismet spam protection. |
+| `allow_local_requests_from_hooks_and_services` | boolean | no | Allow requests to the local network from hooks and services. |
+| `authorized_keys_enabled` | boolean | no | By default, we write to the `authorized_keys` file to support Git over SSH without additional configuration. GitLab can be optimized to authenticate SSH keys via the database file. Only disable this if you have configured your OpenSSH server to use the AuthorizedKeysCommand. |
+| `auto_devops_domain` | string | no | Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages. |
+| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for projects by default. It will automatically build, test, and deploy applications based on a predefined CI/CD configuration. |
+| `clientside_sentry_dsn` | string | required by: `clientside_sentry_enabled` | Clientside Sentry Data Source Name. |
+| `clientside_sentry_enabled` | boolean | no | (**If enabled, requires:** `clientside_sentry_dsn`) Enable Sentry error reporting for the client side. |
+| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. |
+| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
+| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take: `0` _(not protected, both developers and maintainers can push new commits, force push, or delete the branch)_, `1` _(partially protected, developers and maintainers can push new commits, but cannot force push or delete the branch)_ or `2` _(fully protected, developers cannot push new commits, but maintainers can; no-one can force push or delete the branch)_ as a parameter. Default is `2`. |
+| `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
+| `default_project_visibility` | string | no | What visibility level new projects receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
+| `default_projects_limit` | integer | no | Project limit per user. Default is `100000`. |
+| `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
+| `disabled_oauth_sign_in_sources` | array of strings | no | Disabled OAuth sign-in sources. |
+| `domain_blacklist` | array of strings | required by: `domain_blacklist_enabled` | Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: `domain.com`, `*.domain.com`. |
+| `domain_blacklist_enabled` | boolean | no | (**If enabled, requires:** `domain_blacklist`) Allows blocking sign-ups from emails from specific domains. |
+| `domain_whitelist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is `null`, meaning there is no restriction. |
+| `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. |
+| `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. |
+| `ed25519_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ED25519 key. Default is `0` (no restriction). `-1` disables ED25519 keys. |
+| `email_author_in_body` | boolean | no | Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead. |
+| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
+| `enforce_terms` | boolean | no | (**If enabled, requires:** `terms`) Enforce application ToS to all users. |
+| `gitaly_timeout_default` | integer | no | Default Gitaly timeout, in seconds. This timeout is not enforced for git fetch/push operations or Sidekiq jobs. Set to `0` to disable timeouts. |
+| `gitaly_timeout_fast` | integer | no | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. |
+| `gitaly_timeout_medium` | integer | no | Medium Gitaly timeout, in seconds. This should be a value between the Fast and the Default timeout. Set to `0` to disable timeouts. |
+| `gravatar_enabled` | boolean | no | Enable Gravatar. |
+| `hashed_storage_enabled` | boolean | no | Create new projects using hashed storage paths: Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance. (EXPERIMENTAL) |
+| `help_page_hide_commercial_content` | boolean | no | Hide marketing-related entries from help. |
+| `help_page_support_url` | string | no | Alternate support URL for help page. |
+| `help_page_text` | string | no | Custom text displayed on the help page. |
+| `hide_third_party_offers` | boolean | no | Do not display offers from third parties within GitLab. |
+| `home_page_url` | string | no | Redirect to this URL when not logged in. |
+| `housekeeping_bitmaps_enabled` | boolean | required by: `housekeeping_enabled` | Enable Git pack file bitmap creation. |
+| `housekeeping_enabled` | boolean | no | (**If enabled, requires:** `housekeeping_bitmaps_enabled`, `housekeeping_full_repack_period`, `housekeeping_gc_period`, and `housekeeping_incremental_repack_period`) Enable or disable git housekeeping. |
+| `housekeeping_full_repack_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which an incremental `git repack` is run. |
+| `housekeeping_gc_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which `git gc` is run. |
+| `housekeeping_incremental_repack_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which an incremental `git repack` is run. |
+| `html_emails_enabled` | boolean | no | Enable HTML emails. |
+| `instance_statistics_visibility_private` | boolean | no | When set to `true` Instance statistics will only be available to admins. |
+| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `gitlab`, `google_code`, `fogbugz`, `git`, and `gitlab_project`. |
+| `max_artifacts_size` | integer | no | Maximum artifacts size in MB |
+| `max_attachment_size` | integer | no | Limit attachment size in MB |
+| `max_pages_size` | integer | no | Maximum size of pages repositories in MB |
+| `metrics_enabled` | boolean | no | (**If enabled, requires:** `metrics_host`, `metrics_method_call_threshold`, `metrics_packet_size`, `metrics_pool_size`, `metrics_port`, `metrics_sample_interval` and `metrics_timeout`) Enable influxDB metrics. |
+| `metrics_host` | string | required by: `metrics_enabled` | InfluxDB host. |
+| `metrics_method_call_threshold` | integer | required by: `metrics_enabled` | A method call is only tracked when it takes longer than the given amount of milliseconds. |
+| `metrics_packet_size` | integer | required by: `metrics_enabled` | The amount of datapoints to send in a single UDP packet. |
+| `metrics_pool_size` | integer | required by: `metrics_enabled` | The amount of InfluxDB connections to keep open. |
+| `metrics_port` | integer | required by: `metrics_enabled` | The UDP port to use for connecting to InfluxDB. |
+| `metrics_sample_interval` | integer | required by: `metrics_enabled` | The sampling interval in seconds. |
+| `metrics_timeout` | integer | required by: `metrics_enabled` | The amount of seconds after which InfluxDB will time out. |
+| `mirror_available` | boolean | no | Allow mirrors to be set up for projects. If disabled, only admins will be able to set up mirrors in projects. |
+| `pages_domain_verification_enabled` | boolean | no | Require users to prove ownership of custom domains. Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled. |
+| `password_authentication_enabled_for_git` | boolean | no | Enable authentication for Git over HTTP(S) via a GitLab account password. Default is `true`. |
+| `password_authentication_enabled_for_web` | boolean | no | Enable authentication for the web interface via a GitLab account password. Default is `true`. |
+| `performance_bar_allowed_group_id` | string | no | (Deprecated: Use `performance_bar_allowed_group_path` instead) Path of the group that is allowed to toggle the performance bar. |
+| `performance_bar_allowed_group_path` | string | no | Path of the group that is allowed to toggle the performance bar. |
+| `performance_bar_enabled` | boolean | no | (Deprecated: Pass `performance_bar_allowed_group_path: nil` instead) Allow enabling the performance bar. |
+| `plantuml_enabled` | boolean | no | (**If enabled, requires:** `plantuml_url`) Enable PlantUML integration. Default is `false`. |
+| `plantuml_url` | string | required by: `plantuml_enabled` | The PlantUML instance URL for integration. |
+| `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to `0` to disable polling. |
+| `project_export_enabled` | boolean | no | Enable project export. |
+| `prometheus_metrics_enabled` | boolean | no | Enable prometheus metrics. |
+| `recaptcha_enabled` | boolean | no | (**If enabled, requires:** `recaptcha_private_key` and `recaptcha_site_key`) Enable recaptcha. |
+| `recaptcha_private_key` | string | required by: `recaptcha_enabled` | Private key for recaptcha. |
+| `recaptcha_site_key` | string | required by: `recaptcha_enabled` | Site key for recaptcha. |
+| `repository_checks_enabled` | boolean | no | GitLab will periodically run `git fsck` in all project and wiki repositories to look for silent disk corruption issues. |
+| `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. |
+| `require_two_factor_authentication` | boolean | no | (**If enabled, requires:** `two_factor_grace_period`) Require all users to set up Two-factor authentication. |
+| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is `null` which means there is no restriction. |
+| `rsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded RSA key. Default is `0` (no restriction). `-1` disables RSA keys. |
+| `send_user_confirmation_email` | boolean | no | Send confirmation email on sign-up. |
+| `sentry_dsn` | string | required by: `sentry_enabled` | Sentry Data Source Name. |
+| `sentry_enabled` | boolean | no | (**If enabled, requires:** `sentry_dsn`) Sentry is an error reporting and logging tool which is currently not shipped with GitLab, available at https://getsentry.com. |
+| `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes |
+| `shared_runners_enabled` | boolean | no | (**If enabled, requires:** `shared_runners_text`) Enable shared runners for new projects. |
+| `shared_runners_text` | string | required by: `shared_runners_enabled` | Shared runners text. |
+| `sign_in_text` | string | no | Text on the login page. |
+| `signin_enabled` | string | no | (Deprecated: Use `password_authentication_enabled_for_web` instead) Flag indicating if password authentication is enabled for the web interface. |
+| `signup_enabled` | boolean | no | Enable registration. Default is `true`. |
+| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. |
+| `terms` | text | required by: `enforce_terms` | (**Required by:** `enforce_terms`) Markdown content for the ToS. |
+| `throttle_authenticated_api_enabled` | boolean | no | (**If enabled, requires:** `throttle_authenticated_api_period_in_seconds` and `throttle_authenticated_api_requests_per_period`) Enable authenticated API request rate limit. Helps reduce request volume (e.g. from crawlers or abusive bots). |
+| `throttle_authenticated_api_period_in_seconds` | integer | required by: `throttle_authenticated_api_enabled` | Rate limit period in seconds. |
+| `throttle_authenticated_api_requests_per_period` | integer | required by: `throttle_authenticated_api_enabled` | Max requests per period per user. |
+| `throttle_authenticated_web_enabled` | boolean | no | (**If enabled, requires:** `throttle_authenticated_web_period_in_seconds` and `throttle_authenticated_web_requests_per_period`) Enable authenticated web request rate limit. Helps reduce request volume (e.g. from crawlers or abusive bots). |
+| `throttle_authenticated_web_period_in_seconds` | integer | required by: `throttle_authenticated_web_enabled` | Rate limit period in seconds. |
+| `throttle_authenticated_web_requests_per_period` | integer | required by: `throttle_authenticated_web_enabled` | Max requests per period per user. |
+| `throttle_unauthenticated_enabled` | boolean | no | (**If enabled, requires:** `throttle_unauthenticated_period_in_seconds` and `throttle_unauthenticated_requests_per_period`) Enable unauthenticated request rate limit. Helps reduce request volume (e.g. from crawlers or abusive bots). |
+| `throttle_unauthenticated_period_in_seconds` | integer | required by: `throttle_unauthenticated_enabled` | Rate limit period in seconds. |
+| `throttle_unauthenticated_requests_per_period` | integer | required by: `throttle_unauthenticated_enabled` | Max requests per period per IP. |
+| `two_factor_grace_period` | integer | required by: `require_two_factor_authentication` | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication. |
+| `unique_ips_limit_enabled` | boolean | no | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple ips. |
+| `unique_ips_limit_per_user` | integer | required by: `unique_ips_limit_enabled` | Maximum number of ips per user. |
+| `unique_ips_limit_time_window` | integer | required by: `unique_ips_limit_enabled` | How many seconds an IP will be counted towards the limit. |
+| `usage_ping_enabled` | boolean | no | Every week GitLab will report license usage back to GitLab, Inc. |
+| `user_default_external` | boolean | no | Newly registered users will be external by default. |
+| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider. |
+| `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the "You won't be able to pull or push project code via SSH" warning shown to users with no uploaded SSH key. |
+| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index dd424470b67..7b8db6cfa8f 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -34,6 +34,7 @@ Example response:
"push_events":true,
"tag_push_events":false,
"merge_requests_events": true,
+ "repository_update_events": true,
"enable_ssl_verification":true
}
]
@@ -56,6 +57,7 @@ POST /hooks
| `push_events` | boolean | no | When true, the hook will fire on push events |
| `tag_push_events` | boolean | no | When true, the hook will fire on new tags being pushed |
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
+| `repository_update_events` | boolean | no | Trigger hook on repository update events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
Example request:
@@ -75,6 +77,7 @@ Example response:
"push_events":true,
"tag_push_events":false,
"merge_requests_events": true,
+ "repository_update_events": true,
"enable_ssl_verification":true
}
]
@@ -127,4 +130,4 @@ Example request:
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/hooks/2
-```
+``` \ No newline at end of file
diff --git a/doc/api/tags.md b/doc/api/tags.md
index 4af096c3c0c..826900ca518 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -134,7 +134,7 @@ Parameters:
"description": "Amazing release. Wow"
},
"name": "v1.0.0",
- "target: "2695effb5807a22ff3d138d593fd856244e155e7",
+ "target": "2695effb5807a22ff3d138d593fd856244e155e7",
"message": null
}
```
@@ -174,10 +174,21 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `tag_name` (required) - The name of a tag
+
+Request body:
+
- `description` (required) - Release notes with markdown support
```json
{
+ "description": "Amazing release. Wow"
+}
+```
+
+Response:
+
+```json
+{
"tag_name": "1.0.0",
"description": "Amazing release. Wow"
}
@@ -195,10 +206,21 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `tag_name` (required) - The name of a tag
+
+Request body:
+
- `description` (required) - Release notes with markdown support
```json
{
+ "description": "Amazing release. Wow"
+}
+```
+
+Response:
+
+```json
+{
"tag_name": "1.0.0",
"description": "Amazing release. Wow"
}
diff --git a/doc/api/templates/dockerfiles.md b/doc/api/templates/dockerfiles.md
new file mode 100644
index 00000000000..a08b8d33693
--- /dev/null
+++ b/doc/api/templates/dockerfiles.md
@@ -0,0 +1,113 @@
+# Dockerfiles API
+
+## List Dockerfile templates
+
+Get all Dockerfile templates.
+
+```
+GET /templates/dockerfiles
+```
+
+```bash
+curl https://gitlab.example.com/api/v4/templates/dockerfiles
+```
+
+Example response:
+
+```json
+[
+ {
+ "key": "Binary",
+ "name": "Binary"
+ },
+ {
+ "key": "Binary-alpine",
+ "name": "Binary-alpine"
+ },
+ {
+ "key": "Binary-scratch",
+ "name": "Binary-scratch"
+ },
+ {
+ "key": "Golang",
+ "name": "Golang"
+ },
+ {
+ "key": "Golang-alpine",
+ "name": "Golang-alpine"
+ },
+ {
+ "key": "Golang-scratch",
+ "name": "Golang-scratch"
+ },
+ {
+ "key": "HTTPd",
+ "name": "HTTPd"
+ },
+ {
+ "key": "Node",
+ "name": "Node"
+ },
+ {
+ "key": "Node-alpine",
+ "name": "Node-alpine"
+ },
+ {
+ "key": "OpenJDK",
+ "name": "OpenJDK"
+ },
+ {
+ "key": "OpenJDK-alpine",
+ "name": "OpenJDK-alpine"
+ },
+ {
+ "key": "PHP",
+ "name": "PHP"
+ },
+ {
+ "key": "Python",
+ "name": "Python"
+ },
+ {
+ "key": "Python-alpine",
+ "name": "Python-alpine"
+ },
+ {
+ "key": "Python2",
+ "name": "Python2"
+ },
+ {
+ "key": "Ruby",
+ "name": "Ruby"
+ },
+ {
+ "key": "Ruby-alpine",
+ "name": "Ruby-alpine"
+ }
+]
+```
+
+## Single Dockerfile template
+
+Get a single Dockerfile template.
+
+```
+GET /templates/dockerfiles/:key
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `key` | string | yes | The key of the Dockerfile template |
+
+```bash
+curl https://gitlab.example.com/api/v4/templates/dockerfiles/Binary
+```
+
+Example response:
+
+```json
+{
+ "name": "Binary",
+ "content": "# This file is a template, and might need editing before it works on your project.\n# This Dockerfile installs a compiled binary into a bare system.\n# You must either commit your compiled binary into source control (not recommended)\n# or build the binary first as part of a CI/CD pipeline.\n\nFROM buildpack-deps:jessie\n\nWORKDIR /usr/local/bin\n\n# Change `app` to whatever your binary is called\nAdd app .\nCMD [\"./app\"]\n"
+}
+```
diff --git a/doc/api/templates/gitignores.md b/doc/api/templates/gitignores.md
index d3f5c88ca90..3804855129c 100644
--- a/doc/api/templates/gitignores.md
+++ b/doc/api/templates/gitignores.md
@@ -17,538 +17,84 @@ Example response:
```json
[
{
- "name": "AppEngine"
- },
- {
- "name": "Laravel"
- },
- {
- "name": "Elisp"
- },
- {
- "name": "SketchUp"
+ "key": "Actionscript",
+ "name": "Actionscript"
},
{
+ "key": "Ada",
"name": "Ada"
},
{
- "name": "Ruby"
- },
- {
- "name": "Kohana"
- },
- {
- "name": "Nanoc"
- },
- {
- "name": "Erlang"
- },
- {
- "name": "OCaml"
- },
- {
- "name": "Lithium"
- },
- {
- "name": "Fortran"
- },
- {
- "name": "Scala"
- },
- {
- "name": "Node"
- },
- {
- "name": "Fancy"
- },
- {
- "name": "Perl"
- },
- {
- "name": "Zephir"
- },
- {
- "name": "WordPress"
- },
- {
- "name": "Symfony"
- },
- {
- "name": "FuelPHP"
- },
- {
- "name": "DM"
- },
- {
- "name": "Sdcc"
- },
- {
- "name": "Rust"
- },
- {
- "name": "C"
- },
- {
- "name": "Umbraco"
- },
- {
- "name": "Actionscript"
+ "key": "Agda",
+ "name": "Agda"
},
{
+ "key": "Android",
"name": "Android"
},
{
- "name": "Grails"
- },
- {
- "name": "Composer"
- },
- {
- "name": "ExpressionEngine"
- },
- {
- "name": "Gcov"
- },
- {
- "name": "Qt"
+ "key": "AppEngine",
+ "name": "AppEngine"
},
{
- "name": "Phalcon"
+ "key": "AppceleratorTitanium",
+ "name": "AppceleratorTitanium"
},
{
+ "key": "ArchLinuxPackages",
"name": "ArchLinuxPackages"
},
{
- "name": "TeX"
- },
- {
- "name": "SCons"
- },
- {
- "name": "Lilypond"
- },
- {
- "name": "CommonLisp"
- },
- {
- "name": "Rails"
- },
- {
- "name": "Mercury"
- },
- {
- "name": "Magento"
- },
- {
- "name": "ChefCookbook"
- },
- {
- "name": "GitBook"
- },
- {
- "name": "C++"
- },
- {
- "name": "Eagle"
- },
- {
- "name": "Go"
- },
- {
- "name": "OpenCart"
- },
- {
- "name": "Scheme"
- },
- {
- "name": "Typo3"
- },
- {
- "name": "SeamGen"
- },
- {
- "name": "Swift"
- },
- {
- "name": "Elm"
- },
- {
- "name": "Unity"
- },
- {
- "name": "Agda"
- },
- {
- "name": "CUDA"
- },
- {
- "name": "VVVV"
- },
- {
- "name": "Finale"
- },
- {
- "name": "LemonStand"
- },
- {
- "name": "Textpattern"
- },
- {
- "name": "Julia"
- },
- {
- "name": "Packer"
- },
- {
- "name": "Scrivener"
- },
- {
- "name": "Dart"
- },
- {
- "name": "Plone"
- },
- {
- "name": "Jekyll"
- },
- {
- "name": "Xojo"
- },
- {
- "name": "LabVIEW"
- },
- {
+ "key": "Autotools",
"name": "Autotools"
},
{
- "name": "KiCad"
- },
- {
- "name": "Prestashop"
- },
- {
- "name": "ROS"
- },
- {
- "name": "Smalltalk"
- },
- {
- "name": "GWT"
- },
- {
- "name": "OracleForms"
- },
- {
- "name": "SugarCRM"
- },
- {
- "name": "Nim"
- },
- {
- "name": "SymphonyCMS"
+ "key": "C",
+ "name": "C"
},
{
- "name": "Maven"
+ "key": "C++",
+ "name": "C++"
},
{
+ "key": "CFWheels",
"name": "CFWheels"
},
{
- "name": "Python"
- },
- {
- "name": "ZendFramework"
- },
- {
- "name": "CakePHP"
- },
- {
- "name": "Concrete5"
- },
- {
- "name": "PlayFramework"
- },
- {
- "name": "Terraform"
- },
- {
- "name": "Elixir"
- },
- {
+ "key": "CMake",
"name": "CMake"
},
{
- "name": "Joomla"
- },
- {
- "name": "Coq"
- },
- {
- "name": "Delphi"
- },
- {
- "name": "Haskell"
- },
- {
- "name": "Yii"
- },
- {
- "name": "Java"
- },
- {
- "name": "UnrealEngine"
- },
- {
- "name": "AppceleratorTitanium"
- },
- {
- "name": "CraftCMS"
- },
- {
- "name": "ForceDotCom"
- },
- {
- "name": "ExtJs"
- },
- {
- "name": "MetaProgrammingSystem"
- },
- {
- "name": "D"
- },
- {
- "name": "Objective-C"
- },
- {
- "name": "RhodesRhomobile"
- },
- {
- "name": "R"
- },
- {
- "name": "EPiServer"
- },
- {
- "name": "Yeoman"
- },
- {
- "name": "VisualStudio"
- },
- {
- "name": "Processing"
- },
- {
- "name": "Leiningen"
- },
- {
- "name": "Stella"
- },
- {
- "name": "Opa"
- },
- {
- "name": "Drupal"
- },
- {
- "name": "TurboGears2"
- },
- {
- "name": "Idris"
- },
- {
- "name": "Jboss"
- },
- {
- "name": "CodeIgniter"
- },
- {
- "name": "Qooxdoo"
- },
- {
- "name": "Waf"
+ "key": "CUDA",
+ "name": "CUDA"
},
{
- "name": "Sass"
+ "key": "CakePHP",
+ "name": "CakePHP"
},
{
- "name": "Lua"
+ "key": "ChefCookbook",
+ "name": "ChefCookbook"
},
{
+ "key": "Clojure",
"name": "Clojure"
},
{
- "name": "IGORPro"
- },
- {
- "name": "Gradle"
- },
- {
- "name": "Archives"
- },
- {
- "name": "SynopsysVCS"
- },
- {
- "name": "Ninja"
- },
- {
- "name": "Tags"
- },
- {
- "name": "OSX"
- },
- {
- "name": "Dreamweaver"
- },
- {
- "name": "CodeKit"
- },
- {
- "name": "NotepadPP"
- },
- {
- "name": "VisualStudioCode"
- },
- {
- "name": "Mercurial"
- },
- {
- "name": "BricxCC"
- },
- {
- "name": "DartEditor"
- },
- {
- "name": "Eclipse"
- },
- {
- "name": "Cloud9"
- },
- {
- "name": "TortoiseGit"
- },
- {
- "name": "NetBeans"
- },
- {
- "name": "GPG"
- },
- {
- "name": "Espresso"
- },
- {
- "name": "Redcar"
- },
- {
- "name": "Xcode"
- },
- {
- "name": "Matlab"
- },
- {
- "name": "LyX"
- },
- {
- "name": "SlickEdit"
- },
- {
- "name": "Dropbox"
- },
- {
- "name": "CVS"
- },
- {
- "name": "Calabash"
- },
- {
- "name": "JDeveloper"
- },
- {
- "name": "Vagrant"
- },
- {
- "name": "IPythonNotebook"
- },
- {
- "name": "TextMate"
- },
- {
- "name": "Ensime"
- },
- {
- "name": "WebMethods"
- },
- {
- "name": "VirtualEnv"
- },
- {
- "name": "Emacs"
- },
- {
- "name": "Momentics"
- },
- {
- "name": "JetBrains"
- },
- {
- "name": "SublimeText"
- },
- {
- "name": "Kate"
- },
- {
- "name": "ModelSim"
- },
- {
- "name": "Redis"
- },
- {
- "name": "KDevelop4"
- },
- {
- "name": "Bazaar"
- },
- {
- "name": "Linux"
- },
- {
- "name": "Windows"
- },
- {
- "name": "XilinxISE"
- },
- {
- "name": "Lazarus"
- },
- {
- "name": "EiffelStudio"
- },
- {
- "name": "Anjuta"
- },
- {
- "name": "Vim"
- },
- {
- "name": "Otto"
- },
- {
- "name": "MicrosoftOffice"
- },
- {
- "name": "LibreOffice"
- },
- {
- "name": "SBT"
+ "key": "CodeIgniter",
+ "name": "CodeIgniter"
},
{
- "name": "MonoDevelop"
+ "key": "CommonLisp",
+ "name": "CommonLisp"
},
{
- "name": "SVN"
+ "key": "Composer",
+ "name": "Composer"
},
{
- "name": "FlexBuilder"
+ "key": "Concrete5",
+ "name": "Concrete5"
}
]
```
diff --git a/doc/api/templates/gitlab_ci_ymls.md b/doc/api/templates/gitlab_ci_ymls.md
index bdb128fc336..cecfc8cd9b9 100644
--- a/doc/api/templates/gitlab_ci_ymls.md
+++ b/doc/api/templates/gitlab_ci_ymls.md
@@ -17,79 +17,84 @@ Example response:
```json
[
{
- "name": "C++"
- },
- {
- "name": "Docker"
- },
- {
- "name": "Elixir"
- },
- {
- "name": "LaTeX"
- },
- {
- "name": "Grails"
- },
- {
- "name": "Rust"
+ "key": "Android",
+ "name": "Android"
},
{
- "name": "Nodejs"
+ "key": "Auto-DevOps",
+ "name": "Auto-DevOps"
},
{
- "name": "Ruby"
+ "key": "Bash",
+ "name": "Bash"
},
{
- "name": "Scala"
+ "key": "C++",
+ "name": "C++"
},
{
- "name": "Maven"
+ "key": "Chef",
+ "name": "Chef"
},
{
- "name": "Harp"
+ "key": "Clojure",
+ "name": "Clojure"
},
{
- "name": "Pelican"
+ "key": "Crystal",
+ "name": "Crystal"
},
{
- "name": "Hyde"
+ "key": "Django",
+ "name": "Django"
},
{
- "name": "Nanoc"
+ "key": "Docker",
+ "name": "Docker"
},
{
- "name": "Octopress"
+ "key": "Elixir",
+ "name": "Elixir"
},
{
- "name": "JBake"
+ "key": "Go",
+ "name": "Go"
},
{
- "name": "HTML"
+ "key": "Gradle",
+ "name": "Gradle"
},
{
- "name": "Hugo"
+ "key": "Grails",
+ "name": "Grails"
},
{
- "name": "Metalsmith"
+ "key": "Julia",
+ "name": "Julia"
},
{
- "name": "Hexo"
+ "key": "LaTeX",
+ "name": "LaTeX"
},
{
- "name": "Lektor"
+ "key": "Laravel",
+ "name": "Laravel"
},
{
- "name": "Doxygen"
+ "key": "Maven",
+ "name": "Maven"
},
{
- "name": "Brunch"
+ "key": "Mono",
+ "name": "Mono"
},
{
- "name": "Jekyll"
+ "key": "Nodejs",
+ "name": "Nodejs"
},
{
- "name": "Middleman"
+ "key": "OpenShift",
+ "name": "OpenShift"
}
]
```
diff --git a/doc/api/users.md b/doc/api/users.md
index a8858468cab..e3633c46041 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -59,6 +59,9 @@ GET /users?active=true
GET /users?blocked=true
```
+NOTE: **Note:**
+Username search is case insensitive.
+
### For admins
```
@@ -199,6 +202,7 @@ Parameters:
"created_at": "2012-05-23T08:00:58Z",
"bio": null,
"location": null,
+ "public_email": "john@example.com",
"skype": "",
"linkedin": "",
"twitter": "",
@@ -230,6 +234,7 @@ Parameters:
"is_admin": false,
"bio": null,
"location": null,
+ "public_email": "john@example.com",
"skype": "",
"linkedin": "",
"twitter": "",
@@ -263,7 +268,7 @@ GET /users/:id?with_custom_attributes=true
## User creation
-Creates a new user. Note only administrators can create new users. Either `password` or `reset_password` should be specified (`reset_password` takes priority).
+Creates a new user. Note only administrators can create new users. Either `password` or `reset_password` should be specified (`reset_password` takes priority). If `reset_password` is `false`, then `password` is required.
```
POST /users
@@ -286,6 +291,7 @@ Parameters:
- `provider` (optional) - External provider name
- `bio` (optional) - User's biography
- `location` (optional) - User's location
+- `public_email` (optional) - The public email of the user
- `admin` (optional) - User is admin - true or false (default)
- `can_create_group` (optional) - User can create groups - true or false
- `skip_confirmation` (optional) - Skip confirmation - true or false (default)
@@ -303,26 +309,27 @@ PUT /users/:id
Parameters:
-- `email` - Email
-- `username` - Username
-- `name` - Name
-- `password` - Password
-- `skype` - Skype ID
-- `linkedin` - LinkedIn
-- `twitter` - Twitter account
-- `website_url` - Website URL
-- `organization` - Organization name
-- `projects_limit` - Limit projects each user can create
-- `extern_uid` - External UID
-- `provider` - External provider name
-- `bio` - User's biography
-- `location` (optional) - User's location
-- `admin` (optional) - User is admin - true or false (default)
-- `can_create_group` (optional) - User can create groups - true or false
+- `email` - Email
+- `username` - Username
+- `name` - Name
+- `password` - Password
+- `skype` - Skype ID
+- `linkedin` - LinkedIn
+- `twitter` - Twitter account
+- `website_url` - Website URL
+- `organization` - Organization name
+- `projects_limit` - Limit projects each user can create
+- `extern_uid` - External UID
+- `provider` - External provider name
+- `bio` - User's biography
+- `location` (optional) - User's location
+- `public_email` (optional) - The public email of the user
+- `admin` (optional) - User is admin - true or false (default)
+- `can_create_group` (optional) - User can create groups - true or false
- `skip_reconfirmation` (optional) - Skip reconfirmation - true or false (default)
-- `external` (optional) - Flags the user as external - true or false(default)
-- `avatar` (optional) - Image file for user's avatar
-- `private_profile` (optional) - User's profile is private - true or false
+- `external` (optional) - Flags the user as external - true or false(default)
+- `avatar` (optional) - Image file for user's avatar
+- `private_profile` (optional) - User's profile is private - true or false
On password update, user will be forced to change it upon next login.
Note, at the moment this method does only return a `404` error,
@@ -367,6 +374,7 @@ GET /user
"created_at": "2012-05-23T08:00:58Z",
"bio": null,
"location": null,
+ "public_email": "john@example.com",
"skype": "",
"linkedin": "",
"twitter": "",
@@ -415,6 +423,7 @@ GET /user
"is_admin": false,
"bio": null,
"location": null,
+ "public_email": "john@example.com",
"skype": "",
"linkedin": "",
"twitter": "",
@@ -504,7 +513,7 @@ PUT /user/status
When both parameters `emoji` and `message` are empty, the status will be cleared.
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "emoji=coffee" --data "emoji=I crave coffee" https://gitlab.example.com/api/v4/user/status
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "emoji=coffee" --data "message=I crave coffee" https://gitlab.example.com/api/v4/user/status
```
Example responses
@@ -552,7 +561,7 @@ Parameters:
## List SSH keys for user
-Get a list of a specified user's SSH keys. Available only for admin
+Get a list of a specified user's SSH keys.
```
GET /users/:id/keys
@@ -972,6 +981,7 @@ Parameters:
- `id` (required) - id of specified user
- `email` (required) - email address
+- `skip_confirmation` (optional) - Skip confirmation and assume e-mail is verified - true or false (default)
## Delete email for current user
@@ -1062,7 +1072,6 @@ Example response:
[
{
"active" : true,
- "token" : "EsMo-vhKfXGwX9RKrwiy",
"scopes" : [
"api"
],
@@ -1079,7 +1088,6 @@ Example response:
"read_user"
],
"revoked" : true,
- "token" : "ZcZRpLeEuQRprkRjYydY",
"name" : "mytoken2",
"created_at" : "2017-03-17T17:19:28.697Z",
"id" : 3,
@@ -1115,7 +1123,6 @@ Example response:
```json
{
"active" : true,
- "token" : "EsMo-vhKfXGwX9RKrwiy",
"scopes" : [
"api"
],
@@ -1132,6 +1139,8 @@ Example response:
> Requires admin permissions.
+> Token values are returned once. Make sure you save it - you won't be able to access it again.
+
It creates a new impersonation token. Note that only administrators can do this.
You are only able to create impersonation tokens to impersonate the user and perform
both API calls and Git reads and writes. The user will not see these tokens in their profile
diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md
index 98eae66469f..5752fb7c078 100644
--- a/doc/api/v3_to_v4.md
+++ b/doc/api/v3_to_v4.md
@@ -76,8 +76,8 @@ Below are the changes made between V3 and V4.
- Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675)
- API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
- API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
-- Change initial page from `0` to `1` on `GET /projects/:id/repository/commits` (like on the rest of the API) [!9679] (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679)
-- Return correct `Link` header data for `GET /projects/:id/repository/commits` [!9679] (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679)
+- Change initial page from `0` to `1` on `GET /projects/:id/repository/commits` (like on the rest of the API) [!9679](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679)
+- Return correct `Link` header data for `GET /projects/:id/repository/commits` [!9679](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679)
- Update endpoints for repository files [!9637](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9637)
- Moved `GET /projects/:id/repository/files?file_path=:file_path` to `GET /projects/:id/repository/files/:file_path` (`:file_path` should be URL-encoded)
- `GET /projects/:id/repository/blobs/:sha` now returns JSON attributes for the blob identified by `:sha`, instead of finding the commit identified by `:sha` and returning the raw content of the blob in that commit identified by the required `?filepath=:filepath`
diff --git a/doc/api/wikis.md b/doc/api/wikis.md
index 15ce5f96b60..fb0ec773da5 100644
--- a/doc/api/wikis.md
+++ b/doc/api/wikis.md
@@ -97,12 +97,12 @@ curl --data "format=rdoc&title=Hello&content=Hello world" --header "PRIVATE-TOKE
Example response:
```json
-{
+{
"content" : "Hello world",
"format" : "markdown",
"slug" : "Hello",
"title" : "Hello"
-}
+}
```
## Edit an existing wiki page
@@ -154,6 +154,44 @@ DELETE /projects/:id/wikis/:slug
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/wikis/foo"
```
-On success the HTTP status code is `204` and no JSON response is expected.
+On success the HTTP status code is `204` and no JSON response is expected.
[ce-13372]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13372
+
+## Upload an attachment to the wiki repository
+
+Uploads a file to the attachment folder inside the wiki's repository. The
+ attachment folder is the `uploads` folder.
+
+```
+POST /projects/:id/wikis/attachments
+```
+
+| Attribute | Type | Required | Description |
+| ------------- | ------- | -------- | ---------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `file` | string | yes | The attachment to be uploaded |
+| `branch` | string | no | The name of the branch. Defaults to the wiki repository default branch |
+
+To upload a file from your filesystem, use the `--form` argument. This causes
+cURL to post data using the header `Content-Type: multipart/form-data`.
+The `file=` parameter must point to a file on your filesystem and be preceded
+by `@`. For example:
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "file=@dk.png" https://gitlab.example.com/api/v4/projects/1/wikis/attachments
+```
+
+Example response:
+
+```json
+{
+ "file_name" : "dk.png",
+ "file_path" : "uploads/6a061c4cf9f1c28cb22c384b4b8d4e3c/dk.png",
+ "branch" : "master",
+ "link" : {
+ "url" : "uploads/6a061c4cf9f1c28cb22c384b4b8d4e3c/dk.png",
+ "markdown" : "![dk](uploads/6a061c4cf9f1c28cb22c384b4b8d4e3c/dk.png)"
+ }
+}
+```
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 7666219acb0..dba1f38abe2 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -76,6 +76,8 @@ learn how to leverage its potential even more.
- [Trigger pipelines on a schedule](../user/project/pipelines/schedules.md)
- [Kubernetes clusters](../user/project/clusters/index.md) - Integrate one or
more Kubernetes clusters to your project
+- [Interactive web terminal](interactive_web_terminal/index.md) - Open an interactive
+ web terminal to debug the running jobs
## GitLab CI/CD for Docker
@@ -130,5 +132,3 @@ your whole GitLab instance as well as in each project.
- [New CI job permissions model](../user/project/new_ci_build_permissions_model.md)
Read about what changed in GitLab 8.12 and how that affects your jobs.
There's a new way to access your Git submodules and LFS objects in jobs.
-
-[gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml
diff --git a/doc/ci/autodeploy/img/auto_deploy_btn.png b/doc/ci/autodeploy/img/auto_deploy_btn.png
index 25915ed1c9d..ee88e5ce8c0 100644
--- a/doc/ci/autodeploy/img/auto_deploy_btn.png
+++ b/doc/ci/autodeploy/img/auto_deploy_btn.png
Binary files differ
diff --git a/doc/ci/autodeploy/img/auto_deploy_button.png b/doc/ci/autodeploy/img/auto_deploy_button.png
index 423e76a6cda..0e84d9c57a1 100644
--- a/doc/ci/autodeploy/img/auto_deploy_button.png
+++ b/doc/ci/autodeploy/img/auto_deploy_button.png
Binary files differ
diff --git a/doc/ci/autodeploy/img/auto_deploy_dropdown.png b/doc/ci/autodeploy/img/auto_deploy_dropdown.png
index 5815937a4af..4094f8ebb4e 100644
--- a/doc/ci/autodeploy/img/auto_deploy_dropdown.png
+++ b/doc/ci/autodeploy/img/auto_deploy_dropdown.png
Binary files differ
diff --git a/doc/ci/autodeploy/img/auto_monitoring.png b/doc/ci/autodeploy/img/auto_monitoring.png
index 5661b50841b..5a11923d199 100644
--- a/doc/ci/autodeploy/img/auto_monitoring.png
+++ b/doc/ci/autodeploy/img/auto_monitoring.png
Binary files differ
diff --git a/doc/ci/autodeploy/img/guide_connect_cluster.png b/doc/ci/autodeploy/img/guide_connect_cluster.png
index b856b81a1d0..703d536f37a 100644
--- a/doc/ci/autodeploy/img/guide_connect_cluster.png
+++ b/doc/ci/autodeploy/img/guide_connect_cluster.png
Binary files differ
diff --git a/doc/ci/autodeploy/img/guide_integration.png b/doc/ci/autodeploy/img/guide_integration.png
index 723b2619ea2..ab72de2bba3 100644
--- a/doc/ci/autodeploy/img/guide_integration.png
+++ b/doc/ci/autodeploy/img/guide_integration.png
Binary files differ
diff --git a/doc/ci/autodeploy/img/guide_secret.png b/doc/ci/autodeploy/img/guide_secret.png
index 01f5aa49908..8469bee48b7 100644
--- a/doc/ci/autodeploy/img/guide_secret.png
+++ b/doc/ci/autodeploy/img/guide_secret.png
Binary files differ
diff --git a/doc/ci/autodeploy/quick_start_guide.md b/doc/ci/autodeploy/quick_start_guide.md
index cc6c9ec0e0a..1473703542d 100644
--- a/doc/ci/autodeploy/quick_start_guide.md
+++ b/doc/ci/autodeploy/quick_start_guide.md
@@ -11,7 +11,7 @@ We made a minimal [Ruby application](https://gitlab.com/gitlab-examples/minimal-
Let’s start by forking our sample application. Go to [the project page](https://gitlab.com/gitlab-examples/minimal-ruby-app) and press the `Fork` button. Soon you should have a project under your namespace with the necessary files.
-## Setup your own cluster on Google Kubernetes Engine
+## Set up your own cluster on Google Kubernetes Engine
If you do not already have a Google Cloud account, create one at https://console.cloud.google.com.
@@ -23,9 +23,9 @@ You need to have the Google Cloud SDK installed. e.g.
On OSX, install [homebrew](https://brew.sh):
1. Install Brew Caskroom: `brew install caskroom/cask/brew-cask`
-2. Install Google Cloud SDK: `brew cask install google-cloud-sdk`
-3. Add `kubectl`: `gcloud components install kubectl`
-4. Log in: `gcloud auth login`
+1. Install Google Cloud SDK: `brew cask install google-cloud-sdk`
+1. Add `kubectl`: `gcloud components install kubectl`
+1. Log in: `gcloud auth login`
Now go back to the Google interface, find your cluster, and follow the instructions under `Connect to the cluster` and open the Kubernetes Dashboard. It will look something like `gcloud container clusters get-credentials ruby-autodeploy \ --zone europe-west2-c --project api-project-XXXXXXX` and then `kubectl proxy`.
@@ -71,7 +71,7 @@ Use this IP address to configure your DNS. This part heavily depends on your pre
Use `nslookup minimal-ruby-app-staging.<yourdomain>` to confirm that domain is assigned to the cluster IP.
-## Setup Auto Deploy
+## Set up Auto Deploy
Visit the home page of your GitLab.com project and press "Set up Auto Deploy" button.
diff --git a/doc/ci/caching/img/clear_runners_cache.png b/doc/ci/caching/img/clear_runners_cache.png
index e5db4a47b3e..4f1171513ad 100644
--- a/doc/ci/caching/img/clear_runners_cache.png
+++ b/doc/ci/caching/img/clear_runners_cache.png
Binary files differ
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index c159198d16b..758ab37861b 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -14,6 +14,65 @@ starting from GitLab 9.0.
Make sure you read the [`cache` reference](../yaml/README.md#cache) to learn
how it is defined in `.gitlab-ci.yml`.
+## Cache vs artifacts
+
+NOTE: **Note:**
+Be careful if you use cache and artifacts to store the same path in your jobs
+as **caches are restored before artifacts** and the content would be overwritten.
+
+Don't mix the caching with passing artifacts between stages. Caching is not
+designed to pass artifacts between stages. Cache is for runtime dependencies
+needed to compile the project:
+
+- `cache`: **Use for temporary storage for project dependencies.** Not useful
+ for keeping intermediate build results, like `jar` or `apk` files.
+ Cache was designed to be used to speed up invocations of subsequent runs of a
+ given job, by keeping things like dependencies (e.g., npm packages, Go vendor
+ packages, etc.) so they don't have to be re-fetched from the public internet.
+ While the cache can be abused to pass intermediate build results between stages,
+ there may be cases where artifacts are a better fit.
+- `artifacts`: **Use for stage results that will be passed between stages.**
+ Artifacts were designed to upload some compiled/generated bits of the build,
+ and they can be fetched by any number of concurrent Runners. They are
+ guaranteed to be available and are there to pass data between jobs. They are
+ also exposed to be downloaded from the UI. **Artifacts can only exist in
+ directories relative to the build directory** and specifying paths which don't
+ comply to this rule trigger an unintuitive and illogical error message (an
+ enhancement is discussed at
+ https://gitlab.com/gitlab-org/gitlab-ce/issues/15530). Artifacts need to be
+ uploaded to the GitLab instance (not only the GitLab runner) before the next
+ stage job(s) can start, so you need to evaluate carefully whether your
+ bandwidth allows you to profit from parallelization with stages and shared
+ artifacts before investing time in changes to the setup.
+
+It's sometimes confusing because the name artifact sounds like something that
+is only useful outside of the job, like for downloading a final image. But
+artifacts are also available in between stages within a pipeline. So if you
+build your application by downloading all the required modules, you might want
+to declare them as artifacts so that each subsequent stage can depend on them
+being there. There are some optimizations like declaring an
+[expiry time](../yaml/README.md#artifacts-expire_in) so you don't keep artifacts
+around too long, and using [dependencies](../yaml/README.md#dependencies) to
+control exactly where artifacts are passed around.
+
+In summary:
+
+- Caches are disabled if not defined globally or per job (using `cache:`).
+- Caches are available for all jobs in your `.gitlab-ci.yml` if enabled globally.
+- Caches can be used by subsequent pipelines of that very same job (a script in
+ a stage) in which the cache was created (if not defined globally).
+- Caches are stored where the Runner is installed **and** uploaded to S3 if
+ [distributed cache is enabled](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching).
+- Caches defined per job are only used, either:
+ - For the next pipeline of that job.
+ - If that same cache is also defined in a subsequent job of the same pipeline.
+- Artifacts are disabled if not defined per job (using `artifacts:`).
+- Artifacts can only be enabled per job, not globally.
+- Artifacts are created during a pipeline and can be used by the subsequent
+ jobs of that currently active pipeline.
+- Artifacts are always uploaded to GitLab (known as coordinator).
+- Artifacts can have an expiration value for controlling disk usage (30 days by default).
+
## Good caching practices
We have the cache from the perspective of the developers (who consume a cache
@@ -39,13 +98,13 @@ or pipelines in a guaranteed manner.
From the perspective of the Runner, in order for cache to work effectively, one
of the following must be true:
-- Use a single Runner for all your jobs
-- Use multiple Runners (in autoscale mode or not) that use
+- Use a single Runner for all your jobs.
+- Use multiple Runners (in autoscale mode or not) that use.
[distributed caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching),
- where the cache is stored in S3 buckets (like shared Runners on GitLab.com)
+ where the cache is stored in S3 buckets (like shared Runners on GitLab.com).
- Use multiple Runners (not in autoscale mode) of the same architecture that
share a common network-mounted directory (using NFS or something similar)
- where the cache will be stored
+ where the cache will be stored.
TIP: **Tip:**
Read about the [availability of the cache](#availability-of-the-cache)
@@ -87,7 +146,7 @@ you can use the same key for all of them:
```yaml
cache:
- key: one-key-to-rull-them-all
+ key: one-key-to-rule-them-all
```
To share the same cache between branches, but separate them by job:
@@ -119,8 +178,8 @@ runs of jobs for things like dependencies and commonly used libraries
so they don't have to be re-fetched from the public internet.
NOTE: **Note:**
-For more examples, check the [GitLab CI Yml](https://gitlab.com/gitlab-org/gitlab-ci-yml)
-project.
+For more examples, check out our [GitLab CI/CD
+templates](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates).
### Caching Nodejs dependencies
@@ -131,7 +190,7 @@ Nodejs modules are installed in `node_modules/` and are cached per-branch:
```yaml
#
-# https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Nodejs.gitlab-ci.yml
+# https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml
#
image: node:latest
@@ -158,7 +217,7 @@ are cached per-branch:
```yaml
#
-# https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/PHP.gitlab-ci.yml
+# https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
#
image: php:7.2
@@ -187,14 +246,14 @@ pip's cache is defined under `.cache/pip/` and both are cached per-branch:
```yaml
#
-# https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Python.gitlab-ci.yml
+# https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/Python.gitlab-ci.yml
#
image: python:latest
# Change pip's cache directory to be inside the project directory since we can
# only cache local items.
variables:
- PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache"
+ PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
# Pip's cache doesn't store the python packages
# https://pip.pypa.io/en/stable/reference/pip_install/#caching
@@ -203,7 +262,7 @@ variables:
# them in a virtualenv and cache it as well.
cache:
paths:
- - .cache/
+ - .cache/pip
- venv/
before_script:
@@ -227,7 +286,7 @@ jobs inherit it. Gems are installed in `vendor/ruby/` and are cached per-branch:
```yaml
#
-# https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Ruby.gitlab-ci.yml
+# https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
#
image: ruby:2.5
@@ -309,19 +368,19 @@ job B:
Here's what happens behind the scenes:
-1. Pipeline starts
-1. `job A` runs
-1. `before_script` is executed
-1. `script` is executed
-1. `after_script` is executed
+1. Pipeline starts.
+1. `job A` runs.
+1. `before_script` is executed.
+1. `script` is executed.
+1. `after_script` is executed.
1. `cache` runs and the `vendor/` directory is zipped into `cache.zip`.
This file is then saved in the directory based on the
[Runner's setting](#where-the-caches-are-stored) and the `cache: key`.
-1. `job B` runs
-1. The cache is extracted (if found)
-1. `before_script` is executed
-1. `script` is executed
-1. Pipeline finishes
+1. `job B` runs.
+1. The cache is extracted (if found).
+1. `before_script` is executed.
+1. `script` is executed.
+1. Pipeline finishes.
By using a single Runner on a single machine, you'll not have the issue where
`job B` might execute on a Runner different from `job A`, thus guaranteeing the
@@ -393,13 +452,13 @@ job B:
- vendor/
```
-1. `job A` runs
-1. `public/` is cached as cache.zip
-1. `job B` runs
-1. The previous cache, if any, is unzipped
-1. `vendor/` is cached as cache.zip and overwrites the previous one
+1. `job A` runs.
+1. `public/` is cached as cache.zip.
+1. `job B` runs.
+1. The previous cache, if any, is unzipped.
+1. `vendor/` is cached as cache.zip and overwrites the previous one.
1. The next time `job A` runs it will use the cache of `job B` which is different
- and thus will be ineffective
+ and thus will be ineffective.
To fix that, use different `keys` for each job.
@@ -456,63 +515,14 @@ next run of the pipeline, the cache will be stored in a different location.
If you want to avoid editing `.gitlab-ci.yml`, you can easily clear the cache
via GitLab's UI:
-1. Navigate to your project's **CI/CD > Pipelines** page
-1. Click on the **Clear Runner caches** button to clean up the cache
+1. Navigate to your project's **CI/CD > Pipelines** page.
+1. Click on the **Clear Runner caches** button to clean up the cache.
![Clear Runners cache](img/clear_runners_cache.png)
-1. On the next push, your CI/CD job will use a new cache
+1. On the next push, your CI/CD job will use a new cache.
Behind the scenes, this works by increasing a counter in the database, and the
value of that counter is used to create the key for the cache by appending an
integer to it: `-1`, `-2`, etc. After a push, a new key is generated and the
old cache is not valid anymore.
-
-## Cache vs artifacts
-
-NOTE: **Note:**
-Be careful if you use cache and artifacts to store the same path in your jobs
-as **caches are restored before artifacts** and the content would be overwritten.
-
-Don't mix the caching with passing artifacts between stages. Caching is not
-designed to pass artifacts between stages. Cache is for runtime dependencies
-needed to compile the project:
-
-- `cache` - **Use for temporary storage for project dependencies.** Not useful
- for keeping intermediate build results, like `jar` or `apk` files.
- Cache was designed to be used to speed up invocations of subsequent runs of a
- given job, by keeping things like dependencies (e.g., npm packages, Go vendor
- packages, etc.) so they don't have to be re-fetched from the public internet.
- While the cache can be abused to pass intermediate build results between stages,
- there may be cases where artifacts are a better fit.
-- `artifacts` - **Use for stage results that will be passed between stages.**
- Artifacts were designed to upload some compiled/generated bits of the build,
- and they can be fetched by any number of concurrent Runners. They are
- guaranteed to be available and are there to pass data between jobs. They are
- also exposed to be downloaded from the UI.
-
-It's sometimes confusing because the name artifact sounds like something that
-is only useful outside of the job, like for downloading a final image. But
-artifacts are also available in between stages within a pipeline. So if you
-build your application by downloading all the required modules, you might want
-to declare them as artifacts so that each subsequent stage can depend on them
-being there. There are some optimizations like declaring an
-[expiry time](../yaml/README.md#artifacts-expire_in) so you don't keep artifacts
-around too long, and using [dependencies](../yaml/README.md#dependencies) to
-control exactly where artifacts are passed around.
-
-So, to sum up:
-- Caches are disabled if not defined globally or per job (using `cache:`)
-- Caches are available for all jobs in your `.gitlab-ci.yml` if enabled globally
-- Caches can be used by subsequent pipelines of that very same job (a script in
- a stage) in which the cache was created (if not defined globally).
-- Caches are stored where the Runner is installed **and** uploaded to S3 if
- [distributed cache is enabled](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching)
-- Caches defined per job are only used either a) for the next pipeline of that job,
- or b) if that same cache is also defined in a subsequent job of the same pipeline
-- Artifacts are disabled if not defined per job (using `artifacts:`)
-- Artifacts can only be enabled per job, not globally
-- Artifacts are created during a pipeline and can be used by the subsequent
- jobs of that currently active pipeline
-- Artifacts are always uploaded to GitLab (known as coordinator)
-- Artifacts can have an expiration value for controlling disk usage (30 days by default)
diff --git a/doc/ci/docker/README.md b/doc/ci/docker/README.md
index b0e01d74f7e..8ae80b2bc02 100644
--- a/doc/ci/docker/README.md
+++ b/doc/ci/docker/README.md
@@ -6,3 +6,4 @@ comments: false
- [Using Docker Images](using_docker_images.md)
- [Using Docker Build](using_docker_build.md)
+- [Using kaniko](using_kaniko.md)
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index 63338ff632c..fef367051bf 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -46,18 +46,18 @@ GitLab Runner then executes job scripts as the `gitlab-runner` user.
--description "My Runner"
```
-2. Install Docker Engine on server.
+1. Install Docker Engine on server.
For more information how to install Docker Engine on different systems
checkout the [Supported installations](https://docs.docker.com/engine/installation/).
-3. Add `gitlab-runner` user to `docker` group:
+1. Add `gitlab-runner` user to `docker` group:
```bash
sudo usermod -aG docker gitlab-runner
```
-4. Verify that `gitlab-runner` has access to Docker:
+1. Verify that `gitlab-runner` has access to Docker:
```bash
sudo -u gitlab-runner -H docker info
@@ -75,7 +75,7 @@ GitLab Runner then executes job scripts as the `gitlab-runner` user.
- docker run my-docker-image /script/to/run/tests
```
-5. You can now use `docker` command and install `docker-compose` if needed.
+1. You can now use `docker` command and install `docker-compose` if needed.
NOTE: **Note:**
By adding `gitlab-runner` to the `docker` group you are effectively granting `gitlab-runner` full root permissions.
@@ -151,16 +151,16 @@ In order to do that, follow the steps:
DOCKER_DRIVER: overlay2
services:
- - docker:dind
+ - docker:dind
before_script:
- - docker info
+ - docker info
build:
stage: build
script:
- - docker build -t my-docker-image .
- - docker run my-docker-image /script/to/run/tests
+ - docker build -t my-docker-image .
+ - docker run my-docker-image /script/to/run/tests
```
Docker-in-Docker works well, and is the recommended configuration, but it is
@@ -246,13 +246,13 @@ In order to do that, follow the steps:
image: docker:stable
before_script:
- - docker info
+ - docker info
build:
stage: build
script:
- - docker build -t my-docker-image .
- - docker run my-docker-image /script/to/run/tests
+ - docker build -t my-docker-image .
+ - docker run my-docker-image /script/to/run/tests
```
While the above method avoids using Docker in privileged mode, you should be
@@ -314,8 +314,8 @@ build:
stage: build
script:
- docker pull $CONTAINER_IMAGE:latest || true
- - docker build --cache-from $CONTAINER_IMAGE:latest --tag $CONTAINER_IMAGE:$CI_BUILD_REF --tag $CONTAINER_IMAGE:latest .
- - docker push $CONTAINER_IMAGE:$CI_BUILD_REF
+ - docker build --cache-from $CONTAINER_IMAGE:latest --tag $CONTAINER_IMAGE:$CI_COMMIT_SHA --tag $CONTAINER_IMAGE:latest .
+ - docker push $CONTAINER_IMAGE:$CI_COMMIT_SHA
- docker push $CONTAINER_IMAGE:latest
```
@@ -381,28 +381,88 @@ environment = ["DOCKER_DRIVER=overlay2"]
If you're running multiple Runners you will have to modify all configuration files.
> **Notes:**
-- More information about the Runner configuration is available in the [Runner documentation](https://docs.gitlab.com/runner/configuration/).
-- For more information about using OverlayFS with Docker, you can read
- [Use the OverlayFS storage driver](https://docs.docker.com/engine/userguide/storagedriver/overlayfs-driver/).
+>
+> - More information about the Runner configuration is available in the [Runner documentation](https://docs.gitlab.com/runner/configuration/).
+> - For more information about using OverlayFS with Docker, you can read
+> [Use the OverlayFS storage driver](https://docs.docker.com/engine/userguide/storagedriver/overlayfs-driver/).
## Using the GitLab Container Registry
> **Notes:**
-- This feature requires GitLab 8.8 and GitLab Runner 1.2.
-- Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
- to pass a [personal access token][pat] instead of your password in order to
- login to GitLab's Container Registry.
+> - This feature requires GitLab 8.8 and GitLab Runner 1.2.
+> - Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
+> to pass a [personal access token][pat] instead of your password in order to
+> login to GitLab's Container Registry.
Once you've built a Docker image, you can push it up to the built-in
-[GitLab Container Registry](../../user/project/container_registry.md). For example,
-if you're using docker-in-docker on your runners, this is how your `.gitlab-ci.yml`
+[GitLab Container Registry](../../user/project/container_registry.md).
+Some things you should be aware of:
+
+- You must [log in to the container registry](#authenticating-to-the-container-registry)
+ before running commands. You can do this in the `before_script` if multiple
+ jobs depend on it.
+- Using `docker build --pull` fetches any changes to base
+ images before building just in case your cache is stale. It takes slightly
+ longer, but means you don’t get stuck without security patches to base images.
+- Doing an explicit `docker pull` before each `docker run` fetches
+ the latest image that was just built. This is especially important if you are
+ using multiple runners that cache images locally. Using the git SHA in your
+ image tag makes this less necessary since each job will be unique and you
+ shouldn't ever have a stale image. However, it's still possible to have a
+ stale image if you re-build a given commit after a dependency has changed.
+- You don't want to build directly to `latest` tag in case there are multiple jobs
+ happening simultaneously.
+
+### Authenticating to the Container Registry
+
+There are three ways to authenticate to the Container Registry via GitLab CI/CD
+and depend on the visibility of your project.
+
+For all projects, mostly suitable for public ones:
+
+- **Using the special `gitlab-ci-token` user**: This user is created for you in order to
+ push to the Registry connected to your project. Its password is automatically
+ set with the `$CI_JOB_TOKEN` variable. This allows you to automate building and deploying
+ your Docker images and has read/write access to the Registry. This is ephemeral,
+ so it's only valid for one job. You can use the following example as-is:
+
+ ```sh
+ docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
+ ```
+
+For private and internal projects:
+
+- **Using a personal access token**: You can create and use a
+ [personal access token](../../user/profile/personal_access_tokens.md)
+ in case your project is private:
+ - For read (pull) access, the scope should be `read_registry`.
+ - For read/write (pull/push) access, use `api`.
+ Replace the `<username>` and `<access_token>` in the following example:
+
+ ```sh
+ docker login -u <username> -p <access_token> $CI_REGISTRY
+ ```
+
+- **Using the GitLab Deploy Token**: You can create and use a
+ [special deploy token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token)
+ with your private projects. It provides read-only (pull) access to the Registry.
+ Once created, you can use the special environment variables, and GitLab CI/CD
+ will fill them in for you. You can use the following example as-is:
+
+ ```sh
+ docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
+ ```
+
+### Container Registry examples
+
+If you're using docker-in-docker on your Runners, this is how your `.gitlab-ci.yml`
could look like:
```yaml
build:
image: docker:stable
services:
- - docker:dind
+ - docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
@@ -413,11 +473,6 @@ could look like:
- docker push registry.example.com/group/project/image:latest
```
-You have to use the special `gitlab-ci-token` user created for you in order to
-push to the Registry connected to your project. Its password is provided in the
-`$CI_JOB_TOKEN` variable. This allows you to automate building and deployment
-of your Docker images.
-
You can also make use of [other variables](../variables/README.md) to avoid hardcoding:
```yaml
@@ -455,13 +510,13 @@ an application-specific deploy script:
```yaml
image: docker:stable
services:
-- docker:dind
+ - docker:dind
stages:
-- build
-- test
-- release
-- deploy
+ - build
+ - test
+ - release
+ - deploy
variables:
DOCKER_HOST: tcp://docker:2375
@@ -507,22 +562,6 @@ deploy:
- master
```
-Some things you should be aware of when using the Container Registry:
-
-- You must log in to the container registry before running commands. Putting
- this in `before_script` will run it before each job.
-- Using `docker build --pull` makes sure that Docker fetches any changes to base
- images before building just in case your cache is stale. It takes slightly
- longer, but means you don’t get stuck without security patches to base images.
-- Doing an explicit `docker pull` before each `docker run` makes sure to fetch
- the latest image that was just built. This is especially important if you are
- using multiple runners that cache images locally. Using the git SHA in your
- image tag makes this less necessary since each job will be unique and you
- shouldn't ever have a stale image, but it's still possible if you re-build a
- given commit after a dependency has changed.
-- You don't want to build directly to `latest` in case there are multiple jobs
- happening simultaneously.
-
[docker-in-docker]: https://blog.docker.com/2013/09/docker-can-now-run-within-docker/
[docker-cap]: https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
[2fa]: ../../user/profile/account/two_factor_authentication.md
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 71f1d69cdf4..31649ee2792 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -452,84 +452,91 @@ that runner.
## Define an image from a private Container Registry
> **Notes:**
-- This feature requires GitLab Runner **1.8** or higher
-- For GitLab Runner versions **>= 0.6, <1.8** there was a partial
- support for using private registries, which required manual configuration
- of credentials on runner's host. We recommend to upgrade your Runner to
- at least version **1.8** if you want to use private registries.
-- If the repository is private you need to authenticate your GitLab Runner in the
- registry. Learn more about how [GitLab Runner works in this case][runner-priv-reg].
-
-As an example, let's assume that you want to use the `registry.example.com/private/image:latest`
+>
+> - This feature requires GitLab Runner **1.8** or higher
+> - For GitLab Runner versions **>= 0.6, <1.8** there was a partial
+> support for using private registries, which required manual configuration
+> of credentials on runner's host. We recommend to upgrade your Runner to
+> at least version **1.8** if you want to use private registries.
+> - If the repository is private you need to authenticate your GitLab Runner in the
+> registry. Learn more about how [GitLab Runner works in this case][runner-priv-reg].
+
+As an example, let's assume that you want to use the `registry.example.com:5000/private/image:latest`
image which is private and requires you to login into a private container registry.
Let's also assume that these are the login credentials:
-| Key | Value |
-|----------|----------------------|
-| registry | registry.example.com |
-| username | my_username |
-| password | my_password |
+| Key | Value |
+|----------|---------------------------|
+| registry | registry.example.com:5000 |
+| username | my_username |
+| password | my_password |
-To configure access for `registry.example.com`, follow these steps:
+To configure access for `registry.example.com:5000`, follow these steps:
1. Find what the value of `DOCKER_AUTH_CONFIG` should be. There are two ways to
accomplish this:
- - **First way -** Do a `docker login` on your local machine:
+ - **First way -** Do a `docker login` on your local machine:
- ```bash
- docker login registry.example.com --username my_username --password my_password
- ```
+ ```bash
+ docker login registry.example.com:5000 --username my_username --password my_password
+ ```
- Then copy the content of `~/.docker/config.json`.
- - **Second way -** In some setups, it's possible that Docker client will use
+ Then copy the content of `~/.docker/config.json`.
+ - **Second way -** In some setups, it's possible that Docker client will use
the available system keystore to store the result of `docker login`. In
that case, it's impossible to read `~/.docker/config.json`, so you will
need to prepare the required base64-encoded version of
`${username}:${password}` manually. Open a terminal and execute the
following command:
- ```bash
- echo -n "my_username:my_password" | base64
+ ```bash
+ echo -n "my_username:my_password" | base64
- # Example output to copy
- bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=
- ```
+ # Example output to copy
+ bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=
+ ```
1. Create a [variable] `DOCKER_AUTH_CONFIG` with the content of the
Docker configuration file as the value:
- ```json
- {
- "auths": {
- "registry.example.com": {
- "auth": "bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ="
- }
- }
- }
- ```
+ ```json
+ {
+ "auths": {
+ "registry.example.com:5000": {
+ "auth": "bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ="
+ }
+ }
+ }
+ ```
1. Optionally,if you followed the first way of finding the `DOCKER_AUTH_CONFIG`
value, do a `docker logout` on your computer if you don't need access to the
registry from it:
- ```bash
- docker logout registry.example.com
- ```
+ ```bash
+ docker logout registry.example.com:5000
+ ```
-1. You can now use any private image from `registry.example.com` defined in
+1. You can now use any private image from `registry.example.com:5000` defined in
`image` and/or `services` in your `.gitlab-ci.yml` file:
- ```yaml
- image: my.registry.tld:5000/namespace/image:tag
- ```
+ ```yaml
+ image: registry.example.com:5000/namespace/image:tag
+ ```
- In the example above, GitLab Runner will look at `my.registry.tld:5000` for the
- image `namespace/image:tag`.
+ In the example above, GitLab Runner will look at `registry.example.com:5000` for the
+ image `namespace/image:tag`.
You can add configuration for as many registries as you want, adding more
registries to the `"auths"` hash as described above.
+NOTE: **Note:** The full `hostname:port` combination is required everywhere
+for the Runner to match the `DOCKER_AUTH_CONFIG`. For example, if
+`registry.example.com:5000/namespace/image:tag` is specified in `.gitlab-ci.yml`,
+then the `DOCKER_AUTH_CONFIG` must also specify `registry.example.com:5000`.
+Specifying only `registry.example.com` will not work.
+
## Configuring services
Many services accept environment variables which allow you to easily change
diff --git a/doc/ci/docker/using_kaniko.md b/doc/ci/docker/using_kaniko.md
new file mode 100644
index 00000000000..66f0d429165
--- /dev/null
+++ b/doc/ci/docker/using_kaniko.md
@@ -0,0 +1,59 @@
+# Building images with kaniko and GitLab CI/CD
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/45512) in GitLab 11.2.
+Requires GitLab Runner 11.2 and above.
+
+[kaniko](https://github.com/GoogleContainerTools/kaniko) is a tool to build
+container images from a Dockerfile, inside a container or Kubernetes cluster.
+
+kaniko solves two problems with using the
+[docker-in-docker build](using_docker_build.md#use-docker-in-docker-executor) method:
+
+1. Docker-in-docker requires [privileged mode](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities)
+ in order to function, which is a significant security concern.
+1. Docker-in-docker generally incurs a performance penalty and can be quite slow.
+
+## Requirements
+
+In order to utilize kaniko with GitLab, a [GitLab Runner](https://docs.gitlab.com/runner/)
+using either the [Kubernetes](https://docs.gitlab.com/runner/executors/kubernetes.html),
+[Docker](https://docs.gitlab.com/runner/executors/docker.html), or
+[Docker Machine](https://docs.gitlab.com/runner/executors/docker_machine.html)
+executors is required.
+
+## Building a Docker image with kaniko
+
+When building an image with kaniko and GitLab CI/CD, you should be aware of a
+few important details:
+
+- The kaniko debug image is recommended (`gcr.io/kaniko-project/executor:debug`)
+ because it has a shell, and a shell is required for an image to be used with
+ GitLab CI/CD.
+- The entrypoint will need to be [overridden](using_docker_images.md#overriding-the-entrypoint-of-an-image),
+ otherwise the build script will not run.
+- A Docker `config.json` file needs to be created with the authentication
+ information for the desired container registry.
+
+---
+
+In the following example, kaniko is used to build a Docker image and then push
+it to [GitLab Container Registry](../../user/project/container_registry.md).
+The job will run only when a tag is pushed. A `config.json` file is created under
+`/kaniko/.docker` with the needed GitLab Container Registry credentials taken from the
+[environment variables](../variables/README.md#predefined-variables-environment-variables)
+GitLab CI/CD provides. In the last step, kaniko uses the `Dockerfile` under the
+root directory of the project, builds the Docker image and pushes it to the
+project's Container Registry while tagging it with the Git tag:
+
+```yaml
+build:
+ stage: build
+ image:
+ name: gcr.io/kaniko-project/executor:debug
+ entrypoint: [""]
+ script:
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
+ - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
+ only:
+ - tags
+```
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index 8ea2e0a81dc..6874583256a 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -87,18 +87,18 @@ will later see, is exposed in various places within GitLab. Each time a job that
has an environment specified and succeeds, a deployment is recorded, remembering
the Git SHA and environment name.
->**Note:**
-Starting with GitLab 8.15, the environment name is exposed to the Runner in
-two forms: `$CI_ENVIRONMENT_NAME`, and `$CI_ENVIRONMENT_SLUG`. The first is
-the name given in `.gitlab-ci.yml` (with any variables expanded), while the
-second is a "cleaned-up" version of the name, suitable for use in URLs, DNS,
-etc.
-
->**Note:**
-Starting with GitLab 9.3, the environment URL is exposed to the Runner via
-`$CI_ENVIRONMENT_URL`. The URL would be expanded from `.gitlab-ci.yml`, or if
-the URL was not defined there, the external URL from the environment would be
-used.
+> **Note:**
+> Starting with GitLab 8.15, the environment name is exposed to the Runner in
+> two forms: `$CI_ENVIRONMENT_NAME`, and `$CI_ENVIRONMENT_SLUG`. The first is
+> the name given in `.gitlab-ci.yml` (with any variables expanded), while the
+> second is a "cleaned-up" version of the name, suitable for use in URLs, DNS,
+> etc.
+>
+> **Note:**
+> Starting with GitLab 9.3, the environment URL is exposed to the Runner via
+> `$CI_ENVIRONMENT_URL`. The URL would be expanded from `.gitlab-ci.yml`, or if
+> the URL was not defined there, the external URL from the environment would be
+> used.
To sum up, with the above `.gitlab-ci.yml` we have achieved that:
@@ -134,14 +134,15 @@ There's a bunch of information there, specifically you can see:
- A button that re-deploys the latest deployment, meaning it runs the job
defined by the environment name for that specific commit
->**Notes:**
-- While you can create environments manually in the web interface, we recommend
- that you define your environments in `.gitlab-ci.yml` first. They will
- be automatically created for you after the first deploy.
-- The environments page can only be viewed by Reporters and above. For more
- information on the permissions, see the [permissions documentation][permissions].
-- Only deploys that happen after your `.gitlab-ci.yml` is properly configured
- will show up in the "Environment" and "Last deployment" lists.
+> **Notes:**
+>
+> - While you can create environments manually in the web interface, we recommend
+> that you define your environments in `.gitlab-ci.yml` first. They will
+> be automatically created for you after the first deploy.
+> - The environments page can only be viewed by Reporters and above. For more
+> information on the permissions, see the [permissions documentation][permissions].
+> - Only deploys that happen after your `.gitlab-ci.yml` is properly configured
+> will show up in the "Environment" and "Last deployment" lists.
The information shown in the Environments page is limited to the latest
deployments, but as you may have guessed an environment can have multiple
@@ -369,7 +370,7 @@ review_app:
url: https://$CI_COMMIT_REF_SLUG.example.com
```
-It is assumed that the user has already setup NGINX and GitLab Runner in the
+It is assumed that the user has already set up NGINX and GitLab Runner in the
server this job will run on.
>**Note:**
@@ -415,19 +416,18 @@ and/or `production`) you can see this information in the merge request itself.
### Go directly from source files to public pages on the environment
-> Introduced in GitLab 8.17.
-
-To go one step further, we can specify a Route Map to get GitLab to show us "View on [environment URL]" buttons to go directly from a file to that file's representation on the deployed website. It will be exposed in a few places:
+> Introduced in GitLab 8.17. In GitLab 11.5 the file links
+are surfaced to the merge request widget.
-| In the diff for a merge request, comparison or commit | In the file view |
-| ------ | ------ |
-| !["View on env" button in merge request diff](img/view_on_env_mr.png) | !["View on env" button in file view](img/view_on_env_blob.png) |
+You can specify a Route Map to get GitLab to show "View on <environment URL>"
+buttons to go directly from a file to that file's representation on the
+[deployed website via Review Apps](review_apps/index.md).
To get this to work, you need to tell GitLab how the paths of files in your repository map to paths of pages on your website, using a Route Map.
A Route Map is a file inside the repository at `.gitlab/route-map.yml`, which contains a YAML array that maps `source` paths (in the repository) to `public` paths (on the website).
-
-This is an example of a route map for [Middleman](https://middlemanapp.com) static websites like [http://about.gitlab.com](https://gitlab.com/gitlab-com/www-gitlab-com):
+Below is an example of a route map for [Middleman](https://middlemanapp.com) static websites
+like <https://gitlab.com/gitlab-com/www-gitlab-com>:
```yaml
# Team data
@@ -466,6 +466,25 @@ In the example above, the fact that mappings are evaluated in order of their def
---
+Once you have the route mapping set up, it will be exposed in a few places:
+
+- In the merge request widget. The **View app** button will take you to the
+ environment URL you have set up in `.gitlab-ci.yml`. The dropdown will render
+ the first 5 matched items from the route map, but you can filter them if more
+ than 5 are available.
+
+ ![View app file list in merge request widget](img/view_on_mr_widget.png)
+
+- In the diff for a merge request, comparison, or commit.
+
+ !["View on env" button in merge request diff](img/view_on_env_mr.png)
+
+- In the blob file view.
+
+ !["View on env" button in file view](img/view_on_env_blob.png) |
+
+---
+
We now have a full development cycle, where our app is tested, built, deployed
as a Review app, deployed to a staging server once the merge request is merged,
and finally manually deployed to the production server. What we just described
@@ -563,13 +582,13 @@ exist, you should see something like:
## Monitoring environments
->**Notes:**
+> **Notes:**
>
-- For the monitoring dashboard to appear, you need to:
- - Have enabled the [Prometheus integration][prom]
- - Configured Prometheus to collect at least one [supported metric](../user/project/integrations/prometheus_library/metrics.md)
-- With GitLab 9.2, all deployments to an environment are shown directly on the
- monitoring dashboard
+> - For the monitoring dashboard to appear, you need to:
+> - Have enabled the [Prometheus integration][prom]
+> - Configured Prometheus to collect at least one [supported metric](../user/project/integrations/prometheus_library/metrics.md)
+> - With GitLab 9.2, all deployments to an environment are shown directly on the
+> monitoring dashboard
If you have enabled [Prometheus for monitoring system and response metrics](https://docs.gitlab.com/ee/user/project/integrations/prometheus.html), you can monitor the performance behavior of your app running in each environment.
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 811f4d1f07a..fdf09d332a5 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -4,8 +4,8 @@ comments: false
# GitLab CI/CD Examples
-A collection of `.gitlab-ci.yml` template files is maintained at the [GitLab CI/CD YAML project][gitlab-ci-templates]. When you create a new file via the UI,
-GitLab will give you the option to choose one of the templates existent on this project.
+A collection of [`.gitlab-ci.yml` template files][gitlab-ci-templates] is maintained in GitLab. When you create a new file via the UI,
+GitLab will give you the option to choose one of these templates.
If your favorite programming language or framework are missing we would love your
help by sending a merge request with a new `.gitlab-ci.yml` to this project.
@@ -43,6 +43,10 @@ There's also a collection of repositories with [example projects](https://gitlab
- [Using `dpl` as deployment tool](deployment/README.md)
- [The `.gitlab-ci.yml` file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
+## Test Reports
+
+[Collect test reports in Verify stage](../junit_test_reports.md).
+
## Code Quality analysis
**(Starter)** [Analyze your project's Code Quality](code_quality.md).
@@ -83,4 +87,4 @@ language users and GitLab by sending a merge request with a guide for that langu
You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/)
to get paid for writing complete articles for GitLab.
-[gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml
+[gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index 9657f52159e..6aa0edd87b4 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -16,8 +16,8 @@ to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory
You'll create two different projects:
-- `simple-maven-dep`: the app built and deployed to Artifactory (available at https://gitlab.com/gitlab-examples/maven/simple-maven-dep)
-- `simple-maven-app`: the app using the previous one as a dependency (available at https://gitlab.com/gitlab-examples/maven/simple-maven-app)
+- `simple-maven-dep`: the app built and deployed to Artifactory (available at https://gitlab.com/gitlab-examples/maven/simple-maven-dep )
+- `simple-maven-app`: the app using the previous one as a dependency (available at https://gitlab.com/gitlab-examples/maven/simple-maven-app )
We assume that you already have a GitLab account on [GitLab.com](https://gitlab.com/), and that you know the basic usage of Git and [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/).
We also assume that an Artifactory instance is available and reachable from the internet, and that you have valid credentials to deploy on it.
diff --git a/doc/ci/examples/browser_performance.md b/doc/ci/examples/browser_performance.md
index 0dab07a7f80..7c3b3a65675 100644
--- a/doc/ci/examples/browser_performance.md
+++ b/doc/ci/examples/browser_performance.md
@@ -1,14 +1,20 @@
# Browser Performance Testing with the Sitespeed.io container
+CAUTION: **Caution:**
+The job definition shown below is supported on GitLab 11.5 and later versions.
+It also requires the GitLab Runner 11.5 or later.
+For earlier versions, use the [previous job definitions](#previous-job-definitions).
+
This example shows how to run the
[Sitespeed.io container](https://hub.docker.com/r/sitespeedio/sitespeed.io/) on
your code by using GitLab CI/CD and [Sitespeed.io](https://www.sitespeed.io)
using Docker-in-Docker.
-First, you need a GitLab Runner with the
+First, you need GitLab Runner with
[docker-in-docker executor](../docker/using_docker_build.md#use-docker-in-docker-executor).
-Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called
-`performance`:
+
+Once you set up the Runner, add a new job to `.gitlab-ci.yml` that
+generates the expected report:
```yaml
performance:
@@ -26,19 +32,22 @@ performance:
- mv sitespeed-results/data/performance.json performance.json
artifacts:
paths:
- - performance.json
- - sitespeed-results/
+ - sitespeed-results/
+ reports:
+ performance: performance.json
```
-The above example will:
+The above example will create a `performance` job in your CI/CD pipeline and will run
+Sitespeed.io against the webpage you defined in `URL` to gather key metrics.
+The [GitLab plugin](https://gitlab.com/gitlab-org/gl-performance) for
+Sitespeed.io is downloaded in order to save the report as a
+[Performance report artifact](https://docs.gitlab.com/ee//ci/yaml/README.html#artifactsreportsperformance)
+that you can later download and analyze.
+Due to implementation limitations we always take the latest Performance artifact available.
-1. Create a `performance` job in your CI/CD pipeline and will run
- Sitespeed.io against the webpage you defined in `URL`.
-1. The [GitLab plugin](https://gitlab.com/gitlab-org/gl-performance) for
- Sitespeed.io is downloaded in order to export key metrics to JSON. The full
- HTML Sitespeed.io report will also be saved as an artifact, and if you have
- [GitLab Pages](../../user/project/pages/index.md) enabled, it can be viewed
- directly in your browser.
+The full HTML Sitespeed.io report will also be saved as an artifact, and if you have
+[GitLab Pages](../../user/project/pages/index.md) enabled, it can be viewed
+directly in your browser.
For further customization options of Sitespeed.io, including the ability to
provide a list of URLs to test, please consult
@@ -46,8 +55,8 @@ provide a list of URLs to test, please consult
TIP: **Tip:**
For [GitLab Premium](https://about.gitlab.com/pricing/) users, key metrics are automatically
-extracted and shown right in the merge request widget. Learn more about
-[Browser Performance Testing](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html).
+extracted and shown right in the merge request widget.
+[Learn more on Browser Performance Testing in merge requests](https://docs.gitlab.com/ee//user/project/merge_requests/browser_performance_testing.html).
## Performance testing on Review Apps
@@ -106,8 +115,40 @@ performance:
- mv sitespeed-results/data/performance.json performance.json
artifacts:
paths:
- - performance.json
- sitespeed-results/
+ reports:
+ performance: performance.json
```
-A complete example can be found in our [Auto DevOps CI YML](https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml).
+A complete example can be found in our [Auto DevOps CI YML](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml).
+
+## Previous job definitions
+
+CAUTION: **Caution:**
+Before GitLab 11.5, Performance job and artifact had to be named specifically
+to automatically extract report data and show it in the merge request widget.
+While these old job definitions are still maintained they have been deprecated
+and may be removed in next major release, GitLab 12.0.
+You are advised to update your current `.gitlab-ci.yml` configuration to reflect that change.
+
+For GitLab 11.4 and earlier, the job should look like:
+
+```yaml
+performance:
+ stage: performance
+ image: docker:git
+ variables:
+ URL: https://example.com
+ services:
+ - docker:stable-dind
+ script:
+ - mkdir gitlab-exporter
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
+ - mkdir sitespeed-results
+ - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results $URL
+ - mv sitespeed-results/data/performance.json performance.json
+ artifacts:
+ paths:
+ - performance.json
+ - sitespeed-results/
+``` \ No newline at end of file
diff --git a/doc/ci/examples/code_quality.md b/doc/ci/examples/code_quality.md
index 2a7040ecdeb..ae000b9d30d 100644
--- a/doc/ci/examples/code_quality.md
+++ b/doc/ci/examples/code_quality.md
@@ -1,11 +1,18 @@
# Analyze your project's Code Quality
+CAUTION: **Caution:**
+The job definition shown below is supported on GitLab 11.5 and later versions.
+It also requires the GitLab Runner 11.5 or later.
+For earlier versions, use the [previous job definitions](#previous-job-definitions).
+
This example shows how to run Code Quality on your code by using GitLab CI/CD
and Docker.
-First, you need GitLab Runner with [docker-in-docker executor][dind].
+First, you need GitLab Runner with
+[docker-in-docker executor](../docker/using_docker_build.md#use-docker-in-docker-executor).
-Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `code_quality`:
+Once you set up the Runner, add a new job to `.gitlab-ci.yml` that
+generates the expected report:
```yaml
code_quality:
@@ -23,27 +30,72 @@ code_quality:
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
- paths: [gl-code-quality-report.json]
+ reports:
+ codequality: gl-code-quality-report.json
```
The above example will create a `code_quality` job in your CI/CD pipeline which
-will scan your source code for code quality issues. The report will be saved
-as an artifact that you can later download and analyze.
+will scan your source code for code quality issues. The report will be saved as a
+[Code Quality report artifact](../../ci/yaml/README.md#artifactsreportscodequality)
+that you can later download and analyze.
+Due to implementation limitations we always take the latest Code Quality artifact available.
TIP: **Tip:**
-Starting with [GitLab Starter][ee] 9.3, this information will
-be automatically extracted and shown right in the merge request widget. To do
-so, the CI/CD job must be named `code_quality` and the artifact path must be
-`gl-code-quality-report.json`.
+For [GitLab Starter][ee] users, this information will be automatically
+extracted and shown right in the merge request widget.
[Learn more on Code Quality in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html).
+## Previous job definitions
+
CAUTION: **Caution:**
-Code Quality was previously using `codeclimate` and `codequality` for job name and
-`codeclimate.json` for the artifact name. While these old names
-are still maintained they have been deprecated with GitLab 11.0 and may be removed
-in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
-configuration to reflect that change.
+Before GitLab 11.5, Code Quality job and artifact had to be named specifically
+to automatically extract report data and show it in the merge request widget.
+While these old job definitions are still maintained they have been deprecated
+and may be removed in next major release, GitLab 12.0.
+You are advised to update your current `.gitlab-ci.yml` configuration to reflect that change.
+
+For GitLab 11.4 and earlier, the job should look like:
+
+```yaml
+code_quality:
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+ - docker run
+ --env SOURCE_CODE="$PWD"
+ --volume "$PWD":/code
+ --volume /var/run/docker.sock:/var/run/docker.sock
+ "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
+ artifacts:
+ paths: [gl-code-quality-report.json]
+```
+
+Alternatively the job name could be `codeclimate` or `codequality`
+and the artifact name could be `codeclimate.json`.
+These names have been deprecated with GitLab 11.0
+and may be removed in next major release, GitLab 12.0.
+
+For GitLab 10.3 and earlier, the job should look like:
+
+```yaml
+codequality:
+ image: docker:latest
+ variables:
+ DOCKER_DRIVER: overlay
+ services:
+ - docker:dind
+ script:
+ - docker pull codeclimate/codeclimate:0.69.0
+ - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate:0.69.0 init
+ - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate:0.69.0 analyze -f json > codeclimate.json || true
+ artifacts:
+ paths: [codeclimate.json]
+```
[cli]: https://github.com/codeclimate/codeclimate
-[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/ci/examples/container_scanning.md b/doc/ci/examples/container_scanning.md
index 0f79f7d1b17..68330261910 100644
--- a/doc/ci/examples/container_scanning.md
+++ b/doc/ci/examples/container_scanning.md
@@ -1,13 +1,20 @@
# Container Scanning with GitLab CI/CD
+CAUTION: **Caution:**
+The job definition shown below is supported on GitLab 11.5 and later versions.
+It also requires the GitLab Runner 11.5 or later.
+For earlier versions, use the [previous job definitions](#previous-job-definitions).
+
You can check your Docker images (or more precisely the containers) for known
vulnerabilities by using [Clair](https://github.com/coreos/clair) and
[clair-scanner](https://github.com/arminc/clair-scanner), two open source tools
for Vulnerability Static Analysis for containers.
-All you need is a GitLab Runner with the Docker executor (the shared Runners on
-GitLab.com will work fine). You can then add a new job to `.gitlab-ci.yml`,
-called `container_scanning`:
+First, you need GitLab Runner with
+[docker-in-docker executor](../docker/using_docker_build.md#use-docker-in-docker-executor).
+
+Once you set up the Runner, add a new job to `.gitlab-ci.yml` that
+generates the expected report:
```yaml
container_scanning:
@@ -36,31 +43,77 @@ container_scanning:
- while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done
- ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
artifacts:
- paths: [gl-container-scanning-report.json]
+ reports:
+ container_scanning: gl-container-scanning-report.json
```
The above example will create a `container_scanning` job in your CI/CD pipeline, pull
the image from the [Container Registry](../../user/project/container_registry.md)
(whose name is defined from the two `CI_APPLICATION_` variables) and scan it
-for possible vulnerabilities. The report will be saved as an artifact that you
-can later download and analyze.
+for possible vulnerabilities. The report will be saved as a
+[Container Scanning report artifact](https://docs.gitlab.com/ee//ci/yaml/README.html#artifactsreportscontainer_scanning)
+that you can later download and analyze.
+Due to implementation limitations we always take the latest Container Scanning artifact available.
If you want to whitelist some specific vulnerabilities, you can do so by defining
them in a [YAML file](https://github.com/arminc/clair-scanner/blob/master/README.md#example-whitelist-yaml-file),
in our case its named `clair-whitelist.yml`.
TIP: **Tip:**
-Starting with [GitLab Ultimate][ee] 10.4, this information will
-be automatically extracted and shown right in the merge request widget. To do
-so, the CI/CD job must be named `container_scanning` and the artifact path must be
-`gl-container-scanning-report.json`.
-[Learn more on container scanning results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html).
+For [GitLab Ultimate][ee] users, this information will
+be automatically extracted and shown right in the merge request widget.
+[Learn more on Container Scanning in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html).
+
+CAUTION: **Caution:**
+Starting with GitLab 11.5, Container Scanning feature is licensed under the name `container_scanning`.
+While the old name `sast_container` is still maintained, it has been deprecated with GitLab 11.5 and
+may be removed in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
+configuration to reflect that change if you are using the `$GITLAB_FEATURES` environment variable.
+
+## Previous job definitions
CAUTION: **Caution:**
-Before GitLab 11.0, Container Scanning was previously using `sast:container` for job name and
-`gl-sast-container-report.json` for the artifact name. While these old names
-are still maintained, they have been deprecated with GitLab 11.0 and may be removed
-in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
-configuration to reflect that change.
+Before GitLab 11.5, Container Scanning job and artifact had to be named specifically
+to automatically extract report data and show it in the merge request widget.
+While these old job definitions are still maintained they have been deprecated
+and may be removed in next major release, GitLab 12.0.
+You are advised to update your current `.gitlab-ci.yml` configuration to reflect that change.
+
+For GitLab 11.4 and earlier, the job should look like:
+
+```yaml
+container_scanning:
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ ## Define two new variables based on GitLab's CI/CD predefined variables
+ ## https://docs.gitlab.com/ee/ci/variables/#predefined-variables-environment-variables
+ CI_APPLICATION_REPOSITORY: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG
+ CI_APPLICATION_TAG: $CI_COMMIT_SHA
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - docker run -d --name db arminc/clair-db:latest
+ - docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.1
+ - apk add -U wget ca-certificates
+ - docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
+ - wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
+ - mv clair-scanner_linux_amd64 clair-scanner
+ - chmod +x clair-scanner
+ - touch clair-whitelist.yml
+ - while( ! wget -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; done
+ - retries=0
+ - echo "Waiting for clair daemon to start"
+ - while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done
+ - ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
+ artifacts:
+ paths: [gl-container-scanning-report.json]
+```
+
+Alternatively the job name could be `sast:container`
+and the artifact name could be `gl-sast-container-report.json`.
+These names have been deprecated with GitLab 11.0
+and may be removed in next major release, GitLab 12.0.
[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/ci/examples/dast.md b/doc/ci/examples/dast.md
index ff20f0b3b5e..0ca89eb6700 100644
--- a/doc/ci/examples/dast.md
+++ b/doc/ci/examples/dast.md
@@ -1,16 +1,26 @@
# Dynamic Application Security Testing with GitLab CI/CD
+CAUTION: **Caution:**
+The job definition shown below is supported on GitLab 11.5 and later versions.
+It also requires the GitLab Runner 11.5 or later.
+For earlier versions, use the [previous job definitions](#previous-job-definitions).
+
[Dynamic Application Security Testing (DAST)](https://en.wikipedia.org/wiki/Dynamic_program_analysis)
is using the popular open source tool [OWASP ZAProxy](https://github.com/zaproxy/zaproxy)
to perform an analysis on your running web application.
+Since it is based on [ZAP Baseline](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan)
+DAST will perform passive scanning only;
+it will not actively attack your application.
It can be very useful combined with [Review Apps](../review_apps/index.md).
## Example
-All you need is a GitLab Runner with the Docker executor (the shared Runners on
-GitLab.com will work fine). You can then add a new job to `.gitlab-ci.yml`,
-called `dast`:
+First, you need GitLab Runner with
+[docker-in-docker executor](../docker/using_docker_build.md#use-docker-in-docker-executor).
+
+Once you set up the Runner, add a new job to `.gitlab-ci.yml` that
+generates the expected report:
```yaml
dast:
@@ -23,13 +33,16 @@ dast:
- /zap/zap-baseline.py -J gl-dast-report.json -t $website || true
- cp /zap/wrk/gl-dast-report.json .
artifacts:
- paths: [gl-dast-report.json]
+ reports:
+ dast: gl-dast-report.json
```
The above example will create a `dast` job in your CI/CD pipeline which will run
the tests on the URL defined in the `website` variable (change it to use your
-own) and finally write the results in the `gl-dast-report.json` file. You can
-then download and analyze the report artifact in JSON format.
+own) and scan it for possible vulnerabilities. The report will be saved as a
+[DAST report artifact](https://docs.gitlab.com/ee//ci/yaml/README.html#artifactsreportsdast)
+that you can later download and analyze.
+Due to implementation limitations we always take the latest DAST artifact available.
It's also possible to authenticate the user before performing DAST checks:
@@ -39,25 +52,51 @@ dast:
variables:
website: "https://example.com"
login_url: "https://example.com/sign-in"
+ username: "john.doe@example.com"
+ password: "john-doe-password"
allow_failure: true
script:
- mkdir /zap/wrk/
- /zap/zap-baseline.py -J gl-dast-report.json -t $website
--auth-url $login_url
- --auth-username "john.doe@example.com"
- --auth-password "john-doe-password" || true
+ --auth-username $username
+ --auth-password $password || true
- cp /zap/wrk/gl-dast-report.json .
artifacts:
- paths: [gl-dast-report.json]
+ reports:
+ dast: gl-dast-report.json
```
See [zaproxy documentation](https://gitlab.com/gitlab-org/security-products/zaproxy)
to learn more about authentication settings.
TIP: **Tip:**
-Starting with [GitLab Ultimate][ee] 10.4, this information will
-be automatically extracted and shown right in the merge request widget. To do
-so, the CI job must be named `dast` and the artifact path must be
-`gl-dast-report.json`.
-[Learn more about DAST results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
+For [GitLab Ultimate][ee] users, this information will
+be automatically extracted and shown right in the merge request widget.
+[Learn more on DAST in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
+
+## Previous job definitions
+
+CAUTION: **Caution:**
+Before GitLab 11.5, DAST job and artifact had to be named specifically
+to automatically extract report data and show it in the merge request widget.
+While these old job definitions are still maintained they have been deprecated
+and may be removed in next major release, GitLab 12.0.
+You are advised to update your current `.gitlab-ci.yml` configuration to reflect that change.
+
+For GitLab 11.4 and earlier, the job should look like:
+
+```yaml
+dast:
+ image: registry.gitlab.com/gitlab-org/security-products/zaproxy
+ variables:
+ website: "https://example.com"
+ allow_failure: true
+ script:
+ - mkdir /zap/wrk/
+ - /zap/zap-baseline.py -J gl-dast-report.json -t $website || true
+ - cp /zap/wrk/gl-dast-report.json .
+ artifacts:
+ paths: [gl-dast-report.json]
+```
[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png
deleted file mode 100644
index 5b5d91ec07a..00000000000
--- a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_variables.png b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_variables.png
new file mode 100644
index 00000000000..28323e2d8de
--- /dev/null
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_variables.png
Binary files differ
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
index b88761be56b..3ea81be1569 100644
--- a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
@@ -106,10 +106,10 @@ Now, since the steps defined in `.gitlab-ci.yml` require credentials to login
to CF, you'll need to add your CF credentials as [environment
variables](../../variables/README.md#predefined-variables-environment-variables)
on GitLab CI/CD. To set the environment variables, navigate to your project's
-**Settings > CI/CD** and expand **Secret Variables**. Name the variables
+**Settings > CI/CD** and expand **Variables**. Name the variables
`CF_USERNAME` and `CF_PASSWORD` and set them to the correct values.
-![Secret Variable Settings in GitLab](img/cloud_foundry_secret_variables.png)
+![Variable Settings in GitLab](img/cloud_foundry_variables.png)
Once set up, GitLab CI/CD will deploy your app to CF at every push to your
repository's deafult branch. To see the build logs or watch your builds running
diff --git a/doc/ci/examples/deployment/README.md b/doc/ci/examples/deployment/README.md
index bd60d641493..46effb76d71 100644
--- a/doc/ci/examples/deployment/README.md
+++ b/doc/ci/examples/deployment/README.md
@@ -5,7 +5,7 @@ continuous deployment that's developed and used by Travis CI, but can also be
used with GitLab CI.
>**Note:**
-We recommend to use Dpl if you're deploying to any of these of these services:
+We recommend to use Dpl if you're deploying to any of these services:
https://github.com/travis-ci/dpl#supported-providers.
## Requirements
@@ -101,12 +101,12 @@ production:
We created two deploy jobs that are executed on different events:
1. `staging` is executed for all commits that were pushed to `master` branch,
-2. `production` is executed for all pushed tags.
+1. `production` is executed for all pushed tags.
We also use two secure variables:
1. `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app,
-2. `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app.
+1. `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app.
## Storing API keys
@@ -120,7 +120,7 @@ is hidden in the job log.
You access added variable by prefixing it's name with `$` (on non-Windows runners)
or `%` (for Windows Batch runners):
-1. `$SECRET_VARIABLE` - use it for non-Windows runners
-2. `%SECRET_VARIABLE%` - use it for Windows Batch runners
+1. `$VARIABLE` - use it for non-Windows runners
+1. `%VARIABLE%` - use it for Windows Batch runners
Read more about the [CI variables](../../variables/README.md).
diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md
index bed379b0254..36358515b84 100644
--- a/doc/ci/examples/deployment/composer-npm-deploy.md
+++ b/doc/ci/examples/deployment/composer-npm-deploy.md
@@ -33,9 +33,9 @@ before_script:
In this particular case, the `npm deploy` script is a Gulp script that does the following:
1. Compile CSS & JS
-2. Create sprites
-3. Copy various assets (images, fonts) around
-4. Replace some strings
+1. Create sprites
+1. Copy various assets (images, fonts) around
+1. Replace some strings
All these operations will put all files into a `build` folder, which is ready to be deployed to a live server.
@@ -43,7 +43,7 @@ All these operations will put all files into a `build` folder, which is ready to
You have multiple options: rsync, scp, sftp and so on. For now, we will use scp.
-To make this work, you need to add a GitLab Secret Variable (accessible on _gitlab.example/your-project-name/variables_). That variable will be called `STAGING_PRIVATE_KEY` and it's the **private** ssh key of your server.
+To make this work, you need to add a GitLab CI/CD Variable (accessible on _gitlab.example/your-project-name/variables_). That variable will be called `STAGING_PRIVATE_KEY` and it's the **private** ssh key of your server.
### Security tip
@@ -62,10 +62,10 @@ before_script:
In order, this means that:
-1. We check if the `ssh-agent` is available and we install it if it's not;
-2. We create the `~/.ssh` folder;
-3. We make sure we're running bash;
-4. We disable host checking (we don't ask for user accept when we first connect to a server; and since every job will equal a first connect, we kind of need this)
+1. We check if the `ssh-agent` is available and we install it if it's not.
+1. We create the `~/.ssh` folder.
+1. We make sure we're running bash.
+1. We disable host checking (we don't ask for user accept when we first connect to a server and since every job will equal a first connect, we kind of need this).
And this is basically all you need in the `before_script` section.
@@ -91,11 +91,11 @@ stage_deploy:
Here's the breakdown:
1. `only:dev` means that this build will run only when something is pushed to the `dev` branch. You can remove this block completely and have everything be ran on every push (but probably this is something you don't want)
-2. `ssh-add ...` we will add that private key you added on the web UI to the docker container
-3. We will connect via `ssh` and create a new `_tmp` folder
-4. We will connect via `scp` and upload the `build` folder (which was generated by a `npm` script) to our previously created `_tmp` folder
-5. We will connect again to `ssh` and move the `live` folder to an `_old` folder, then move `_tmp` to `live`.
-6. We connect to ssh and remove the `_old` folder
+1. `ssh-add ...` we will add that private key you added on the web UI to the docker container
+1. We will connect via `ssh` and create a new `_tmp` folder
+1. We will connect via `scp` and upload the `build` folder (which was generated by a `npm` script) to our previously created `_tmp` folder
+1. We will connect again to `ssh` and move the `live` folder to an `_old` folder, then move `_tmp` to `live`.
+1. We connect to ssh and remove the `_old` folder
What's the deal with the artifacts? We just tell GitLab CI to keep the `build` directory (later on, you can download that as needed).
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.png b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.png
index 76e0295722b..09eef98202f 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.png
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.png
Binary files differ
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.png b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.png
index 050a97d2726..71ffcdea289 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.png
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.png
Binary files differ
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.png b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.png
index 4ab5d5f401a..a9452577a42 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.png
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.png
Binary files differ
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index c226b5bfb71..cae051daa56 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -418,7 +418,7 @@ fully understand [IAM Best Practices in AWS](http://docs.aws.amazon.com/IAM/late
1. Click the **Access Keys** section and **Create New Access Key**. Create the key and keep the id and secret around, you'll need them later
![AWS Access Key Config](img/aws_config_window.png)
1. Go to your GitLab project, click **Settings > CI/CD** on the left sidebar
-1. Expand the **Secret Variables** section
+1. Expand the **Variables** section
![GitLab Secret Config](img/gitlab_config.png)
1. Add a key named `AWS_KEY_ID` and copy the key id from Step 2 into the **Value** textbox
1. Add a key named `AWS_KEY_SECRET` and copy the key secret from Step 2 into the **Value** textbox
@@ -520,7 +520,7 @@ a lot of breathing room in quickly getting changes to players.
Here are some ideas to further investigate that can speed up or improve your pipeline:
- [Yarn](https://yarnpkg.com) instead of npm
-- Setup a custom [Docker](../../../ci/docker/using_docker_images.md#define-image-and-services-from-gitlab-ci-yml) image that can preload dependencies and tools (like AWS CLI)
+- Set up a custom [Docker](../../../ci/docker/using_docker_images.md#define-image-and-services-from-gitlab-ci-yml) image that can preload dependencies and tools (like AWS CLI)
- Forward a [custom domain](http://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html) to your game's S3 static website
- Combine jobs if you find it unnecessary for a small project
- Avoid the queues and set up your own [custom GitLab CI/CD runner](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_checkbox.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_checkbox.png
deleted file mode 100644
index a56c07a0da7..00000000000
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_checkbox.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.png
index b1406fed6b8..704d43ea52e 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.png
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.png
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.png
index 9aae11b8679..763ce48fa5a 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.png
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.png
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.png
index a06b6d417cd..f299d6355cb 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.png
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.png
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.png
index d357ecda7d2..9c301e1fc8c 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.png
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.png
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.png
index baf8dec499c..2f451615a3a 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.png
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.png
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.png
index d96c43bcf16..a5fd6b020d1 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.png
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.png
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.png
index 997db10189f..1f605504171 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.png
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.png
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/secret_variables_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/secret_variables_page.png
deleted file mode 100644
index 658c0b5bcac..00000000000
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/secret_variables_page.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/variables_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/variables_page.png
new file mode 100644
index 00000000000..80d8eb0f4fc
--- /dev/null
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/variables_page.png
Binary files differ
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index 39c65399332..b090ea014dc 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -13,7 +13,7 @@ date: 2017-08-31
GitLab features our applications with Continuous Integration, and it is possible to easily deploy the new code changes to the production server whenever we want.
-In this tutorial, we'll show you how to initialize a [Laravel](http://laravel.com/) application and setup our [Envoy](https://laravel.com/docs/envoy) tasks, then we'll jump into see how to test and deploy it with [GitLab CI/CD](../README.md) via [Continuous Delivery](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/).
+In this tutorial, we'll show you how to initialize a [Laravel](http://laravel.com/) application and set up our [Envoy](https://laravel.com/docs/envoy) tasks, then we'll jump into see how to test and deploy it with [GitLab CI/CD](../README.md) via [Continuous Delivery](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/).
We assume you have a basic experience with Laravel, Linux servers,
and you know how to use GitLab.
@@ -23,7 +23,7 @@ It has a great community with a [fantastic documentation](https://laravel.com/do
Aside from the usual routing, controllers, requests, responses, views, and (blade) templates, out of the box Laravel provides plenty of additional services such as cache, events, localization, authentication and many others.
We will use [Envoy](https://laravel.com/docs/master/envoy) as an SSH task runner based on PHP.
-It uses a clean, minimal [Blade syntax](https://laravel.com/docs/blade) to setup tasks that can run on remote servers, such as, cloning your project from the repository, installing the Composer dependencies, and running [Artisan commands](https://laravel.com/docs/artisan).
+It uses a clean, minimal [Blade syntax](https://laravel.com/docs/blade) to set up tasks that can run on remote servers, such as, cloning your project from the repository, installing the Composer dependencies, and running [Artisan commands](https://laravel.com/docs/artisan).
## Initialize our Laravel app on GitLab
@@ -120,12 +120,12 @@ Now, let's add it to your GitLab project as a [variable](../../variables/README.
Variables are user-defined variables and are stored out of `.gitlab-ci.yml`, for security purposes.
They can be added per project by navigating to the project's **Settings** > **CI/CD**.
-![variables page](img/secret_variables_page.png)
-
To the field **KEY**, add the name `SSH_PRIVATE_KEY`, and to the **VALUE** field, paste the private key you've copied earlier.
We'll use this variable in the `.gitlab-ci.yml` later, to easily connect to our remote server as the deployer user without entering its password.
-We also need to add the public key to **Project** > **Settings** > **Repository** as [Deploy Keys](../../../ssh/README.md/#deploy-keys), which gives us the ability to access our repository from the server through [SSH protocol](../../../gitlab-basics/command-line-commands.md/#start-working-on-your-project).
+![variables page](img/variables_page.png)
+
+We also need to add the public key to **Project** > **Settings** > **Repository** as a [Deploy Key](../../../ssh/README.md#deploy-keys), which gives us the ability to access our repository from the server through [SSH protocol](../../../gitlab-basics/command-line-commands.md#start-working-on-your-project).
```bash
@@ -135,10 +135,10 @@ We also need to add the public key to **Project** > **Settings** > **Repository*
cat ~/.ssh/id_rsa.pub
```
-![deploy keys page](img/deploy_keys_page.png)
-
To the field **Title**, add any name you want, and paste the public key into the **Key** field.
+![deploy keys page](img/deploy_keys_page.png)
+
Now, let's clone our repository on the server just to make sure the `deployer` user has access to the repository.
```bash
@@ -372,13 +372,13 @@ At the end, our `Envoy.blade.php` file will look like this:
One more thing we should do before any deployment is to manually copy our application `storage` folder to the `/var/www/app` directory on the server for the first time.
You might want to create another Envoy task to do that for you.
-We also create the `.env` file in the same path to setup our production environment variables for Laravel.
+We also create the `.env` file in the same path to set up our production environment variables for Laravel.
These are persistent data and will be shared to every new release.
Now, we would need to deploy our app by running `envoy run deploy`, but it won't be necessary since GitLab can handle that for us with CI's [environments](../../environments.md), which will be described [later](#setting-up-gitlab-ci-cd) in this tutorial.
Now it's time to commit [Envoy.blade.php](https://gitlab.com/mehranrasulian/laravel-sample/blob/master/Envoy.blade.php) and push it to the `master` branch.
-To keep things simple, we commit directly to `master`, without using [feature-branches](../../../workflow/gitlab_flow.md/#github-flow-as-a-simpler-alternative) since collaboration is beyond the scope of this tutorial.
+To keep things simple, we commit directly to `master`, without using [feature-branches](../../../workflow/gitlab_flow.md#github-flow-as-a-simpler-alternative) since collaboration is beyond the scope of this tutorial.
In a real world project, teams may use [Issue Tracker](../../../user/project/issues/index.md) and [Merge Requests](../../../user/project/merge_requests/index.md) to move their code across branches:
```bash
@@ -398,7 +398,7 @@ In the case you're not familiar with Docker, refer to [How to Automate Docker De
To be able to build, test, and deploy our app with GitLab CI/CD, we need to prepare our work environment.
To do that, we'll use a Docker image which has the minimum requirements that a Laravel app needs to run.
-[There are other ways](../php.md/#test-php-projects-using-the-docker-executor) to do that as well, but they may lead our builds run slowly, which is not what we want when there are faster options to use.
+[There are other ways](../php.md#test-php-projects-using-the-docker-executor) to do that as well, but they may lead our builds run slowly, which is not what we want when there are faster options to use.
With Docker images our builds run incredibly faster!
@@ -444,9 +444,7 @@ On your GitLab project repository navigate to the **Registry** tab.
![container registry page empty image](img/container_registry_page_empty_image.png)
-You may need to [enable Container Registry](../../../user/project/container_registry.md#enable-the-container-registry-for-your-project) to your project to see this tab. You'll find it under your project's **Settings > General > Sharing and permissions**.
-
-![container registry checkbox](img/container_registry_checkbox.png)
+You may need to [enable Container Registry](../../../user/project/container_registry.md#enable-the-container-registry-for-your-project) to your project to see this tab. You'll find it under your project's **Settings > General > Permissions**.
To start using Container Registry on our machine, we first need to login to the GitLab registry using our GitLab username and password:
@@ -536,7 +534,7 @@ That's a lot to take in, isn't it? Let's run through it step by step.
[GitLab Runners](../../runners/README.md) run the script defined by `.gitlab-ci.yml`.
The `image` keyword tells the Runners which image to use.
-The `services` keyword defines additional images [that are linked to the main image](../../docker/using_docker_images.md/#what-is-a-service).
+The `services` keyword defines additional images [that are linked to the main image](../../docker/using_docker_images.md#what-is-a-service).
Here we use the container image we created before as our main image and also use MySQL 5.7 as a service.
```yaml
@@ -560,7 +558,7 @@ So we should adjust the configuration of MySQL instance by defining `MYSQL_DATAB
Find out more about MySQL variables at the [official MySQL Docker Image](https://hub.docker.com/r/_/mysql/).
Also set the variables `DB_HOST` to `mysql` and `DB_USERNAME` to `root`, which are Laravel specific variables.
-We define `DB_HOST` as `mysql` instead of `127.0.0.1`, as we use MySQL Docker image as a service which [is linked to the main Docker image](../../docker/using_docker_images.md/#how-services-are-linked-to-the-build).
+We define `DB_HOST` as `mysql` instead of `127.0.0.1`, as we use MySQL Docker image as a service which [is linked to the main Docker image](../../docker/using_docker_images.md#how-services-are-linked-to-the-build).
```yaml
...
@@ -587,7 +585,7 @@ unit_test:
script:
# Install app dependencies
- composer install
- # Setup .env
+ # Set up .env
- cp .env.example .env
# Generate an environment key
- php artisan key:generate
@@ -602,7 +600,7 @@ unit_test:
#### Deploy to production
The job `deploy_production` will deploy the app to the production server.
-To deploy our app with Envoy, we had to set up the `$SSH_PRIVATE_KEY` variable as an [SSH private key](../../ssh_keys/README.md/#ssh-keys-when-using-the-docker-executor).
+To deploy our app with Envoy, we had to set up the `$SSH_PRIVATE_KEY` variable as an [SSH private key](../../ssh_keys/README.md#ssh-keys-when-using-the-docker-executor).
If the SSH keys have added successfully, we can run Envoy.
As mentioned before, GitLab supports [Continuous Delivery](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#continuous-delivery) methods as well.
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index a2ba29a4ee2..c1048f3d2e3 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -20,7 +20,7 @@ build environment.
Let's first specify the PHP image that will be used for the job process
(you can read more about what an image means in the Runner's lingo reading
-about [Using Docker images](../docker/using_docker_images.md#what-is-image)).
+about [Using Docker images](../docker/using_docker_images.md#what-is-an-image)).
Start by adding the image to your `.gitlab-ci.yml`:
@@ -199,7 +199,7 @@ pecl install <extension>
```
It's not advised to add this to `.gitlab-ci.yml`. You should execute this
-command once, only to setup the build environment.
+command once, only to set up the build environment.
## Extend your tests
diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
index 087b317ab73..ec0b5aaed09 100644
--- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
@@ -40,15 +40,17 @@ production:
```
This project has three jobs:
-1. `test` - used to test Django application,
-2. `staging` - used to automatically deploy staging environment every push to `master` branch
-3. `production` - used to automatically deploy production environment for every created tag
+
+- `test` - used to test Django application,
+- `staging` - used to automatically deploy staging environment every push to `master` branch
+- `production` - used to automatically deploy production environment for every created tag
## Store API keys
You'll need to create two variables in `Settings > CI/CD > Variables` on your GitLab project settings:
-1. `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app,
-2. `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app.
+
+- `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app.
+- `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app.
Find your Heroku API key in [Manage Account](https://dashboard.heroku.com/account).
diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
index 7f9ab1f3a5e..33a353f17f5 100644
--- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
@@ -36,16 +36,17 @@ production:
```
This project has three jobs:
-1. `test` - used to test Rails application,
-2. `staging` - used to automatically deploy staging environment every push to `master` branch
-3. `production` - used to automatically deploy production environment for every created tag
+
+- `test` - used to test Rails application.
+- `staging` - used to automatically deploy staging environment every push to `master` branch.
+- `production` - used to automatically deploy production environment for every created tag.
## Store API keys
You'll need to create two variables in your project's **Settings > CI/CD > Variables**:
-1. `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app,
-2. `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app.
+- `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app.
+- `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app.
Find your Heroku API key in [Manage Account](https://dashboard.heroku.com/account).
diff --git a/doc/ci/examples/test-scala-application.md b/doc/ci/examples/test-scala-application.md
index 09d83c33f95..66bfa41cad9 100644
--- a/doc/ci/examples/test-scala-application.md
+++ b/doc/ci/examples/test-scala-application.md
@@ -1,6 +1,6 @@
# Test and deploy to Heroku a Scala application
-This example demonstrates the integration of Gitlab CI with Scala
+This example demonstrates the integration of GitLab CI with Scala
applications using SBT. Checkout the example
[project](https://gitlab.com/gitlab-examples/scala-sbt) and
[build status](https://gitlab.com/gitlab-examples/scala-sbt/builds).
@@ -25,7 +25,7 @@ before_script:
- apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 642AC823
- apt-get update -y
- apt-get install sbt -y
- - sbt sbt-version
+ - sbt sbtVersion
test:
stage: test
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png
index 0f94ac60fee..77b05f55f88 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png
index 94828a20f51..04d3dc40fa5 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png
index 68503b392ed..63812b41c2c 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png
index d73140ccdd9..c0daa1a6a91 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png
index 38bfde0a3dd..727995f463c 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png
index bfe85c6a10b..50c6ca593c1 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
index a2de0408797..c0346d78141 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
@@ -85,7 +85,7 @@ When asked, answer `Y` to fetch and install dependencies.
If everything went fine, you'll get an output like this:
-![`mix phoenix.new`](img/mix-phoenix-new.png)
+![mix phoenix.new](img/mix-phoenix-new.png)
Now, our project is located inside the directory with the same name we pass to `mix` command, for
example, `~/GitLab/hello_gitlab_ci`.
@@ -145,7 +145,7 @@ Now, we have our app running locally. We can preview it directly on our browser.
not work, open [`127.0.0.1:4000`](http://127.0.0.1:4000) instead and later, configure your OS to
point `localhost` to `127.0.0.1`.
-![`mix phoenix.server`](img/mix-phoenix-server.png)
+![mix phoenix.server](img/mix-phoenix-server.png)
Great, now we have a local Phoenix Server running our app.
@@ -398,10 +398,10 @@ other reasons][ci-reasons] to keep using GitLab CI/CD. The benefits to our teams
- [Using Docker images documentation][using-docker]
- [Example project: Hello GitLab CI/CD on GitLab][hello-gitlab]
-[phoenix-site]: http://phoenixframework.org/ "Phoenix Framework"
+[phoenix-site]: https://phoenixframework.org/ "Phoenix Framework"
[phoenix-learning-guide]: https://hexdocs.pm/phoenix/learning.html "Phoenix Learning Guide"
-[phoenix-install]: http://www.phoenixframework.org/docs/installation "Phoenix Installation"
-[phoenix-mysql]: http://www.phoenixframework.org/docs/using-mysql "Phoenix with MySQL"
+[phoenix-install]: https://hexdocs.pm/phoenix/installation.html "Phoenix Installation"
+[phoenix-mysql]: https://hexdocs.pm/phoenix/ecto.html#using-mysql "Phoenix with MySQL"
[elixir-site]: http://elixir-lang.org/ "Elixir"
[elixir-mix]: http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html "Introduction to mix"
[elixir-docs]: http://elixir-lang.org/getting-started/introduction.html "Elixir Documentation"
diff --git a/doc/ci/git_submodules.md b/doc/ci/git_submodules.md
index 286f3dee665..37078230b34 100644
--- a/doc/ci/git_submodules.md
+++ b/doc/ci/git_submodules.md
@@ -1,15 +1,16 @@
# Using Git submodules with GitLab CI
> **Notes:**
-- GitLab 8.12 introduced a new [CI job permissions model][newperms] and you
- are encouraged to upgrade your GitLab instance if you haven't done already.
- If you are **not** using GitLab 8.12 or higher, you would need to work your way
- around submodules in order to access the sources of e.g., `gitlab.com/group/project`
- with the use of [SSH keys](ssh_keys/README.md).
-- With GitLab 8.12 onward, your permissions are used to evaluate what a CI job
- can access. More information about how this system works can be found in the
- [Jobs permissions model](../user/permissions.md#job-permissions).
-- The HTTP(S) Git protocol [must be enabled][gitpro] in your GitLab instance.
+>
+> - GitLab 8.12 introduced a new [CI job permissions model][newperms] and you
+> are encouraged to upgrade your GitLab instance if you haven't done already.
+> If you are **not** using GitLab 8.12 or higher, you would need to work your way
+> around submodules in order to access the sources of e.g., `gitlab.com/group/project`
+> with the use of [SSH keys](ssh_keys/README.md).
+> - With GitLab 8.12 onward, your permissions are used to evaluate what a CI job
+> can access. More information about how this system works can be found in the
+> [Jobs permissions model](../user/permissions.md#job-permissions).
+> - The HTTP(S) Git protocol [must be enabled][gitpro] in your GitLab instance.
## Configuring the `.gitmodules` file
diff --git a/doc/ci/img/deployments_view.png b/doc/ci/img/deployments_view.png
index 436fed5f465..45d882b536c 100644
--- a/doc/ci/img/deployments_view.png
+++ b/doc/ci/img/deployments_view.png
Binary files differ
diff --git a/doc/ci/img/environments_available.png b/doc/ci/img/environments_available.png
index 2991a309655..7ab92838ece 100644
--- a/doc/ci/img/environments_available.png
+++ b/doc/ci/img/environments_available.png
Binary files differ
diff --git a/doc/ci/img/environments_dynamic_groups.png b/doc/ci/img/environments_dynamic_groups.png
index 45124b3d8d8..37828ccd0c1 100644
--- a/doc/ci/img/environments_dynamic_groups.png
+++ b/doc/ci/img/environments_dynamic_groups.png
Binary files differ
diff --git a/doc/ci/img/environments_link_url_mr.png b/doc/ci/img/environments_link_url_mr.png
index 7ce46063062..75d7311b862 100644
--- a/doc/ci/img/environments_link_url_mr.png
+++ b/doc/ci/img/environments_link_url_mr.png
Binary files differ
diff --git a/doc/ci/img/environments_manual_action_deployments.png b/doc/ci/img/environments_manual_action_deployments.png
index 93beaa0de54..c5959c0003e 100644
--- a/doc/ci/img/environments_manual_action_deployments.png
+++ b/doc/ci/img/environments_manual_action_deployments.png
Binary files differ
diff --git a/doc/ci/img/environments_manual_action_environments.png b/doc/ci/img/environments_manual_action_environments.png
index 9490be63f14..b2ec27cc721 100644
--- a/doc/ci/img/environments_manual_action_environments.png
+++ b/doc/ci/img/environments_manual_action_environments.png
Binary files differ
diff --git a/doc/ci/img/environments_manual_action_jobs.png b/doc/ci/img/environments_manual_action_jobs.png
index 9ae223cf77f..d948ee5da9e 100644
--- a/doc/ci/img/environments_manual_action_jobs.png
+++ b/doc/ci/img/environments_manual_action_jobs.png
Binary files differ
diff --git a/doc/ci/img/environments_manual_action_pipelines.png b/doc/ci/img/environments_manual_action_pipelines.png
index 129e44f6fb0..332850afb7f 100644
--- a/doc/ci/img/environments_manual_action_pipelines.png
+++ b/doc/ci/img/environments_manual_action_pipelines.png
Binary files differ
diff --git a/doc/ci/img/environments_manual_action_single_pipeline.png b/doc/ci/img/environments_manual_action_single_pipeline.png
index 1eeb4379eb7..8c1c0c1d993 100644
--- a/doc/ci/img/environments_manual_action_single_pipeline.png
+++ b/doc/ci/img/environments_manual_action_single_pipeline.png
Binary files differ
diff --git a/doc/ci/img/environments_monitoring.png b/doc/ci/img/environments_monitoring.png
index dcffdd1fdb8..63d272ae42a 100644
--- a/doc/ci/img/environments_monitoring.png
+++ b/doc/ci/img/environments_monitoring.png
Binary files differ
diff --git a/doc/ci/img/environments_mr_review_app.png b/doc/ci/img/environments_mr_review_app.png
index 4bb643d708f..61b7e9fe77c 100644
--- a/doc/ci/img/environments_mr_review_app.png
+++ b/doc/ci/img/environments_mr_review_app.png
Binary files differ
diff --git a/doc/ci/img/environments_terminal_button_on_index.png b/doc/ci/img/environments_terminal_button_on_index.png
index 061bb7c3c87..40110ff325f 100644
--- a/doc/ci/img/environments_terminal_button_on_index.png
+++ b/doc/ci/img/environments_terminal_button_on_index.png
Binary files differ
diff --git a/doc/ci/img/environments_terminal_button_on_show.png b/doc/ci/img/environments_terminal_button_on_show.png
index 4d24304bc93..e96ca9c9c7e 100644
--- a/doc/ci/img/environments_terminal_button_on_show.png
+++ b/doc/ci/img/environments_terminal_button_on_show.png
Binary files differ
diff --git a/doc/ci/img/environments_terminal_page.png b/doc/ci/img/environments_terminal_page.png
index fde1bf325a6..736b2d01a99 100644
--- a/doc/ci/img/environments_terminal_page.png
+++ b/doc/ci/img/environments_terminal_page.png
Binary files differ
diff --git a/doc/ci/img/job_failure_reason.png b/doc/ci/img/job_failure_reason.png
index a60ce1fb21c..d44b8e6d1be 100644
--- a/doc/ci/img/job_failure_reason.png
+++ b/doc/ci/img/job_failure_reason.png
Binary files differ
diff --git a/doc/ci/img/junit_test_report.png b/doc/ci/img/junit_test_report.png
new file mode 100644
index 00000000000..ad098eb457f
--- /dev/null
+++ b/doc/ci/img/junit_test_report.png
Binary files differ
diff --git a/doc/ci/img/pipeline_incremental_rollout.png b/doc/ci/img/pipeline_incremental_rollout.png
new file mode 100644
index 00000000000..b3498e9a5a5
--- /dev/null
+++ b/doc/ci/img/pipeline_incremental_rollout.png
Binary files differ
diff --git a/doc/ci/img/pipelines_grouped.png b/doc/ci/img/pipelines_grouped.png
index 06f52e03320..82814754747 100644
--- a/doc/ci/img/pipelines_grouped.png
+++ b/doc/ci/img/pipelines_grouped.png
Binary files differ
diff --git a/doc/ci/img/pipelines_index.png b/doc/ci/img/pipelines_index.png
index 3b522a9c5e4..e168e7e23df 100644
--- a/doc/ci/img/pipelines_index.png
+++ b/doc/ci/img/pipelines_index.png
Binary files differ
diff --git a/doc/ci/img/pipelines_mini_graph.png b/doc/ci/img/pipelines_mini_graph.png
index 042c8ffeef5..8656b02f60d 100644
--- a/doc/ci/img/pipelines_mini_graph.png
+++ b/doc/ci/img/pipelines_mini_graph.png
Binary files differ
diff --git a/doc/ci/img/pipelines_mini_graph_simple.png b/doc/ci/img/pipelines_mini_graph_simple.png
index eb36c09b2d4..d00a8313088 100644
--- a/doc/ci/img/pipelines_mini_graph_simple.png
+++ b/doc/ci/img/pipelines_mini_graph_simple.png
Binary files differ
diff --git a/doc/ci/img/view_on_env_blob.png b/doc/ci/img/view_on_env_blob.png
index f4fe99046f0..acc457fbb38 100644
--- a/doc/ci/img/view_on_env_blob.png
+++ b/doc/ci/img/view_on_env_blob.png
Binary files differ
diff --git a/doc/ci/img/view_on_env_mr.png b/doc/ci/img/view_on_env_mr.png
index 47ddb40bdc1..2c0bd25a4f2 100644
--- a/doc/ci/img/view_on_env_mr.png
+++ b/doc/ci/img/view_on_env_mr.png
Binary files differ
diff --git a/doc/ci/img/view_on_mr_widget.png b/doc/ci/img/view_on_mr_widget.png
new file mode 100644
index 00000000000..04f4b58df62
--- /dev/null
+++ b/doc/ci/img/view_on_mr_widget.png
Binary files differ
diff --git a/doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.png b/doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.png
new file mode 100644
index 00000000000..199268a1486
--- /dev/null
+++ b/doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.png
Binary files differ
diff --git a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png
new file mode 100644
index 00000000000..0523e62db70
--- /dev/null
+++ b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png
Binary files differ
diff --git a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png
new file mode 100644
index 00000000000..3ee5e39afc0
--- /dev/null
+++ b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png
Binary files differ
diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md
new file mode 100644
index 00000000000..1ddc1bf4d7e
--- /dev/null
+++ b/doc/ci/interactive_web_terminal/index.md
@@ -0,0 +1,54 @@
+# Getting started with interactive web terminals
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/50144) in GitLab 11.3.
+
+Interactive web terminals give the user access to a terminal in GitLab for
+running one-off commands for their CI pipeline.
+
+NOTE: **Note:**
+This is not available for the shared Runners on GitLab.com.
+To make use of this feature, you need to provide your
+[own Runner](https://docs.gitlab.com/runner/install/) and properly
+[configure it](#configuration).
+
+## Configuration
+
+Two things need to be configured for the interactive web terminal to work:
+
+- The Runner needs to have [`[session_server]` configured
+ properly][session-server]
+- If you are using a reverse proxy with your GitLab instance, web terminals need to be
+ [enabled](../../administration/integration/terminal.md#enabling-and-disabling-terminal-support)
+
+## Debugging a running job
+
+NOTE: **Note:** Not all executors are
+[supported](https://docs.gitlab.com/runner/executors/#compatibility-chart).
+
+NOTE: **Note:** The `docker` executor does not keep running
+after the build script is finished. At that point, the terminal will automatically
+disconnect and will not wait for the user to finish. Please follow [this
+issue](https://gitlab.com/gitlab-org/gitlab-runner/issues/3605) for updates on
+improving this behavior.
+
+Sometimes, when a job is running, things don't go as you would expect, and it
+would be helpful if one can have a shell to aid debugging. When a job is
+running, on the right panel you can see a button `debug` that will open the terminal
+for the current job.
+
+![Example of job running with terminal
+available](img/interactive_web_terminal_running_job.png)
+
+When clicked, a new tab will open to the terminal page where you can access
+the terminal and type commands like a normal shell.
+
+![terminal of the job](img/interactive_web_terminal_page.png)
+
+If you have the terminal open and the job has finished with its tasks, the
+terminal will block the job from finishing for the duration configured in
+[`[session_server].terminal_max_retention_time`][session-server] until you
+close the terminal window.
+
+![finished job with terminal open](img/finished_job_with_terminal_open.png)
+
+[session-server]: https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section
diff --git a/doc/ci/junit_test_reports.md b/doc/ci/junit_test_reports.md
new file mode 100644
index 00000000000..3fd54647abb
--- /dev/null
+++ b/doc/ci/junit_test_reports.md
@@ -0,0 +1,170 @@
+# JUnit test reports
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/45318) in GitLab 11.2.
+Requires GitLab Runner 11.2 and above.
+
+## Overview
+
+It is very common that a [CI/CD pipeline](pipelines.md) contains a
+test job that will verify your code.
+If the tests fail, the pipeline fails and users get notified. The person that
+works on the merge request will have to check the job logs and see where the
+tests failed so that they can fix them.
+
+You can configure your job to use JUnit test reports, and GitLab will display a
+report on the merge request so that it's easier and faster to identify the
+failure without having to check the entire log.
+
+## Use cases
+
+Consider the following workflow:
+
+1. Your `master` branch is rock solid, your project is using GitLab CI/CD and
+ your pipelines indicate that there isn't anything broken.
+1. Someone from you team submits a merge request, a test fails and the pipeline
+ gets the known red icon. To investigate more, you have to go through the job
+ logs to figure out the cause of the failed test, which usually contain
+ thousands of lines.
+1. You configure the JUnit test reports and immediately GitLab collects and
+ exposes them in the merge request. No more searching in the job logs.
+1. Your development and debugging workflow becomes easier, faster and efficient.
+
+## How it works
+
+First, GitLab Runner uploads all JUnit XML files as artifacts to GitLab. Then,
+when you visit a merge request, GitLab starts comparing the head and base branch's
+JUnit test reports, where:
+
+- The base branch is the target branch (usually `master`).
+- The head branch is the source branch (the latest pipeline in each merge request).
+
+The reports panel has a summary showing how many tests failed and how many were fixed.
+If no comparison can be done because data for the base branch is not available,
+the panel will just show the list of failed tests for head.
+
+There are three types of results:
+
+1. **Newly failed tests:** Test cases which passed on base branch and failed on head branch
+1. **Existing failures:** Test cases which failed on base branch and failed on head branch
+1. **Resolved failures:** Test cases which failed on base branch and passed on head branch
+
+Each entry in the panel will show the test name and its type from the list
+above. Clicking on the test name will open a modal window with details of its
+execution time and the error output.
+
+![Test Reports Widget](img/junit_test_report.png)
+
+## How to set it up
+
+NOTE: **Note:**
+For a list of supported languages on JUnit tests, check the
+[Wikipedia article](https://en.wikipedia.org/wiki/JUnit#Ports).
+
+To enable the JUnit reports in merge requests, you need to add
+[`artifacts:reports:junit`](yaml/README.md#artifacts-reports-junit)
+in `.gitlab-ci.yml`, and specify the path(s) of the generated test reports.
+
+In the following examples, the job in the `test` stage runs and GitLab
+collects the JUnit test report from each job. After each job is executed, the
+XML reports are stored in GitLab as artifacts and their results are shown in the
+merge request widget.
+
+### Ruby example
+
+Use the following job in `.gitlab-ci.yml`:
+
+```yaml
+## Use https://github.com/sj26/rspec_junit_formatter to generate a JUnit report with rspec
+ruby:
+ stage: test
+ script:
+ - bundle install
+ - rspec spec/lib/ --format RspecJunitFormatter --out rspec.xml
+ artifacts:
+ reports:
+ junit: rspec.xml
+```
+
+### Go example
+
+Use the following job in `.gitlab-ci.yml`:
+
+```yaml
+## Use https://github.com/jstemmer/go-junit-report to generate a JUnit report with go
+golang:
+ stage: test
+ script:
+ - go get -u github.com/jstemmer/go-junit-report
+ - go test -v 2>&1 | go-junit-report > report.xml
+ artifacts:
+ reports:
+ junit: report.xml
+```
+
+### Java examples
+
+There are a few tools that can produce JUnit reports in Java.
+
+#### Gradle
+
+In the following example, `gradle` is used to generate the test reports.
+If there are multiple test tasks defined, `gradle` will generate multiple
+directories under `build/test-results/`. In that case, you can leverage regex
+matching by defining the following path: `build/test-results/test/TEST-*.xml`:
+
+```yaml
+java:
+ stage: test
+ script:
+ - gradle test
+ artifacts:
+ reports:
+ junit: build/test-results/test/TEST-*.xml
+```
+
+#### Maven
+
+For parsing [Surefire](https://maven.apache.org/surefire/maven-surefire-plugin/)
+and [Failsafe](https://maven.apache.org/surefire/maven-failsafe-plugin/) test
+reports, use the following job in `.gitlab-ci.yml`:
+
+```yaml
+java:
+ stage: test
+ script:
+ - mvn verify
+ artifacts:
+ reports:
+ junit:
+ - target/surefire-reports/TEST-*.xml
+ - target/failsafe-reports/TEST-*.xml
+```
+
+### C/C++ example
+
+There are a few tools that can produce JUnit reports in C/C++.
+
+#### GoogleTest
+
+In the following example, `gtest` is used to generate the test reports.
+If there are multiple gtest executables created for different architectures (`x86`, `x64` or `arm`),
+you will be required to run each test providing a unique filename. The results
+will then be aggregated together.
+
+```yaml
+cpp:
+ stage: test
+ script:
+ - gtest.exe --gtest_output="xml:report.xml"
+ artifacts:
+ reports:
+ junit: report.xml
+```
+
+## Limitations
+
+Currently, the following tools might not work because their XML formats are unsupported in GitLab.
+
+|Case|Tool|Issue|
+|---|---|---|
+|`<testcase>` does not have `classname` attribute|ESlint, sass-lint|https://gitlab.com/gitlab-org/gitlab-ce/issues/50964|
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index 4e964af97f5..c628895ee1a 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -9,7 +9,7 @@ you may need to enable pipeline triggering in your project's
## Pipelines
-A pipeline is a group of [jobs][] that get executed in [stages][](batches).
+A pipeline is a group of [jobs] that get executed in [stages].
All of the jobs in a stage are executed in parallel (if there are enough
concurrent [Runners]), and if they all succeed, the pipeline moves on to the
next stage. If one of the jobs fails, the next stage is not (usually)
@@ -29,17 +29,17 @@ There are three types of pipelines that often use the single shorthand of "pipel
![Types of Pipelines](img/types-of-pipelines.svg)
-1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml`
-2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production
-3. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus.
+1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml`.
+1. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production.
+1. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus.
## Development workflows
Pipelines accommodate several development workflows:
-1. **Branch Flow** (e.g. different branch for dev, qa, staging, production)
-2. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases)
-3. **Fork-based Flow** (e.g. merge requests come from forks)
+1. **Branch Flow** (e.g. different branch for dev, qa, staging, production).
+1. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases).
+1. **Fork-based Flow** (e.g. merge requests come from forks).
Example continuous delivery flow:
@@ -57,6 +57,16 @@ Pipelines are defined in `.gitlab-ci.yml` by specifying [jobs] that run in
See the reference [documentation for jobs](yaml/README.md#jobs).
+## Manually executing pipelines
+
+Pipelines can be manually executed, with predefined or manually-specified [variables](variables/README.md).
+
+To execute a pipeline manually:
+
+1. Navigate to your project's **CI/CD > Pipelines**.
+1. Click on the **Run Pipeline** button.
+1. Select the branch to run the pipeline for and enter any environment variables required for the pipeline run.
+
## Seeing pipeline status
You can find the current and historical pipeline runs under your project's
@@ -112,9 +122,9 @@ Then, there is the pipeline mini graph which takes less space and can give you a
quick glance if all jobs pass or something failed. The pipeline mini graph can
be found when you visit:
-- the pipelines index page
-- a single commit page
-- a merge request page
+- The pipelines index page.
+- A single commit page.
+- A merge request page.
That way, you can see all related jobs for a single commit and the net result
of each stage of your pipeline. This allows you to quickly see what failed and
@@ -142,9 +152,9 @@ jobs. Click to expand them.
The basic requirements is that there are two numbers separated with one of
the following (you can even use them interchangeably):
-- a space
-- a slash (`/`)
-- a colon (`:`)
+- A space (` `)
+- A slash (`/`)
+- A colon (`:`)
>**Note:**
More specifically, [it uses][regexp] this regular expression: `\d+[\s:\/\\]+\d+\s*`.
@@ -183,6 +193,18 @@ stage has a job with a manual action.
![Pipelines example](img/pipelines.png)
+### Delay a particular job in the pipeline graph
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21767) in GitLab 11.4.
+
+When you do not want to run a job immediately, you can [delay the job to run after a certain period](yaml/README.md#when-delayed).
+This is especially useful for timed incremental rollout that new code is rolled out gradually.
+For example, if you start rolling out new code and users do not experience trouble, GitLab automatically completes the deployment from 0% to 100%.
+Alternatively, if you start rolling out and you noticed that a few users experience trouble with the version,
+you can stop the timed incremental rollout by canceling the pipeline, and [rolling](environments.md#rolling-back-changes) it back to the stable version.
+
+![Pipelines example](img/pipeline_incremental_rollout.png)
+
### Ordering of jobs in pipeline graphs
**Regular pipeline graph**
@@ -201,6 +223,7 @@ by name. The order of severity is:
- pending
- running
- manual
+- scheduled
- canceled
- success
- skipped
@@ -252,11 +275,12 @@ A strict security model is enforced when pipelines are executed on
The following actions are allowed on protected branches only if the user is
[allowed to merge or push](../user/project/protected_branches.md#using-the-allowed-to-merge-and-allowed-to-push-settings)
on that specific branch:
-- run **manual pipelines** (using Web UI or Pipelines API)
-- run **scheduled pipelines**
-- run pipelines using **triggers**
-- trigger **manual actions** on existing pipelines
-- **retry/cancel** existing jobs (using Web UI or Pipelines API)
+
+- Run **manual pipelines** (using [Web UI](#manually-executing-pipelines) or Pipelines API).
+- Run **scheduled pipelines**.
+- Run pipelines using **triggers**.
+- Trigger **manual actions** on existing pipelines.
+- **Retry/cancel** existing jobs (using Web UI or Pipelines API).
**Variables** marked as **protected** are accessible only to jobs that
run on protected branches, avoiding untrusted users to get unintended access to
@@ -270,7 +294,7 @@ runners will not use regular runners, they must be tagged accordingly.
[jobs]: #jobs
[jobs-yaml]: yaml/README.md#jobs
-[manual]: yaml/README.md#manual
+[manual]: yaml/README.md#whenmanual
[env-manual]: environments.md#manually-deploying-to-environments
[stages]: yaml/README.md#stages
[runners]: runners/README.html
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index 47e658f610e..bdc593493ea 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -7,7 +7,7 @@ projects.
GitLab offers a [continuous integration][ci] service. If you
[add a `.gitlab-ci.yml` file][yaml] to the root directory of your repository,
and configure your GitLab project to use a [Runner], then each commit or
-push, triggers your CI [pipeline].
+push triggers your CI [pipeline].
The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it runs
a pipeline with three [stages]: `build`, `test`, and `deploy`. You don't need to
@@ -168,7 +168,7 @@ can be found at <https://docs.gitlab.com/runner/>.
In order to have a functional Runner you need to follow two steps:
1. [Install it][runner-install]
-2. [Configure it](../runners/README.md#registering-a-specific-runner)
+1. [Configure it](../runners/README.md#registering-a-specific-runner)
Follow the links above to set up your own Runner or use a Shared Runner as
described in the next section.
diff --git a/doc/ci/quick_start/img/build_log.png b/doc/ci/quick_start/img/build_log.png
index 3a7248ca772..2bf0992c50e 100644
--- a/doc/ci/quick_start/img/build_log.png
+++ b/doc/ci/quick_start/img/build_log.png
Binary files differ
diff --git a/doc/ci/quick_start/img/builds_status.png b/doc/ci/quick_start/img/builds_status.png
index f829240f3b3..58978e23978 100644
--- a/doc/ci/quick_start/img/builds_status.png
+++ b/doc/ci/quick_start/img/builds_status.png
Binary files differ
diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png
index b3dd848b294..507eb93ac0c 100644
--- a/doc/ci/quick_start/img/new_commit.png
+++ b/doc/ci/quick_start/img/new_commit.png
Binary files differ
diff --git a/doc/ci/review_apps/img/continuous-delivery-review-apps.svg b/doc/ci/review_apps/img/continuous-delivery-review-apps.svg
new file mode 100644
index 00000000000..90ac763a01e
--- /dev/null
+++ b/doc/ci/review_apps/img/continuous-delivery-review-apps.svg
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="1170px" height="638px" viewBox="0 0 1170 638" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
+ <title>review-apps-CD-outlined</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="review-apps-CD-outlined">
+ <g id="Group-4-Copy" transform="translate(22.000000, 43.000000)">
+ <polygon id="Line-Copy-25" fill="#E0513E" points="163.545966 259 858.690432 259 858.690432 255 163.545966 255"></polygon>
+ <path d="M870.470919,266.702602 C875.287562,266.702602 879.19137,262.80085 879.19137,257.988848 C879.19137,253.176845 875.287562,249.275093 870.470919,249.275093 C865.654276,249.275093 861.750469,253.176845 861.750469,257.988848 C861.750469,262.80085 865.654276,266.702602 870.470919,266.702602 Z M870.470919,270.702602 C863.445609,270.702602 857.750469,265.01046 857.750469,257.988848 C857.750469,250.967235 863.445609,245.275093 870.470919,245.275093 C877.49623,245.275093 883.19137,250.967235 883.19137,257.988848 C883.19137,265.01046 877.49623,270.702602 870.470919,270.702602 Z" id="Oval-Copy-20" fill="#E04733"></path>
+ <path d="M991.315197,145.921933 C996.13184,145.921933 1000.03565,142.020181 1000.03565,137.208178 C1000.03565,132.396176 996.13184,128.494424 991.315197,128.494424 C986.498554,128.494424 982.594747,132.396176 982.594747,137.208178 C982.594747,142.020181 986.498554,145.921933 991.315197,145.921933 Z M991.315197,149.921933 C984.289886,149.921933 978.594747,144.229791 978.594747,137.208178 C978.594747,130.186566 984.289886,124.494424 991.315197,124.494424 C998.340508,124.494424 1004.03565,130.186566 1004.03565,137.208178 C1004.03565,144.229791 998.340508,149.921933 991.315197,149.921933 Z" id="Oval-Copy-22" fill="#E04733"></path>
+ <g id="Group-7" transform="translate(0.000000, 236.799257)" fill="#E04733">
+ <path d="M151.765478,29.9033457 C156.582121,29.9033457 160.485929,26.0015933 160.485929,21.1895911 C160.485929,16.3775889 156.582121,12.4758364 151.765478,12.4758364 C146.948835,12.4758364 143.045028,16.3775889 143.045028,21.1895911 C143.045028,26.0015933 146.948835,29.9033457 151.765478,29.9033457 Z M151.765478,33.9033457 C144.740168,33.9033457 139.045028,28.2112039 139.045028,21.1895911 C139.045028,14.1679783 144.740168,8.47583643 151.765478,8.47583643 C158.790789,8.47583643 164.485929,14.1679783 164.485929,21.1895911 C164.485929,28.2112039 158.790789,33.9033457 151.765478,33.9033457 Z" id="Oval-Copy-10"></path>
+ <path d="M0.187705647,10.1392011 L3.30410548,10.1392011 L7.34270526,21.3328005 L8.86910518,25.5940003 L8.99630517,25.5940003 L10.4591051,21.3328005 L14.4659049,10.1392011 L17.5823047,10.1392011 L17.5823047,31 L15.0701048,31 L15.0701048,19.5202006 C15.0701048,18.587396 15.1072045,17.5592063 15.1814048,16.4356008 C15.2556052,15.3119952 15.3351044,14.2838056 15.4199048,13.351001 L15.2927048,13.351001 L13.6073049,17.9938007 L9.60050514,28.9012001 L8.10590522,28.9012001 L4.06730544,17.9938007 L2.41370553,13.351001 L2.28650553,13.351001 C2.35010585,14.2838056 2.4243051,15.3119952 2.50910552,16.4356008 C2.59390594,17.5592063 2.63630552,18.587396 2.63630552,19.5202006 L2.63630552,31 L0.187705647,31 L0.187705647,10.1392011 Z M32.1785039,22.5094005 L31.192704,19.3294006 C30.8111021,18.1633949 30.4507057,17.0027065 30.111504,15.8473008 C29.7723024,14.6918951 29.4331058,13.510007 29.0939041,12.301601 L28.9667041,12.301601 C28.6487025,13.510007 28.3201058,14.6918951 27.9809041,15.8473008 C27.6417025,17.0027065 27.2813061,18.1633949 26.8997042,19.3294006 L25.9139043,22.5094005 L32.1785039,22.5094005 Z M32.8463039,24.6400003 L25.2461043,24.6400003 L23.2427044,31 L20.5397045,31 L27.5993042,10.1392011 L30.588504,10.1392011 L37.6481036,31 L34.8179038,31 L32.8463039,24.6400003 Z M40.6691035,26.4526002 C41.4111071,27.2370041 42.2855983,27.8676978 43.2926033,28.3447001 C44.2996083,28.8217025 45.3436978,29.0602001 46.4249032,29.0602001 C47.80291,29.0602001 48.8734992,28.7475032 49.636703,28.1221002 C50.3999068,27.4966971 50.7815029,26.6752053 50.7815029,25.6576003 C50.7815029,25.1275977 50.6914038,24.6771022 50.5112029,24.3061004 C50.331002,23.9350985 50.0872045,23.6171017 49.779803,23.3521004 C49.4724014,23.0870991 49.1067051,22.8486015 48.682703,22.6366005 C48.2587009,22.4245994 47.8029055,22.2020016 47.3153031,21.9688005 L44.3261033,20.6650006 C43.8385009,20.4529995 43.3403059,20.1986021 42.8315033,19.9018006 C42.3227008,19.6049991 41.8669054,19.2446028 41.4641034,18.8206007 C41.0613014,18.3965986 40.7327047,17.8931036 40.4783035,17.3101007 C40.2239022,16.7270979 40.0967035,16.0540046 40.0967035,15.2908008 C40.0967035,14.506397 40.2610018,13.7750043 40.5896035,13.096601 C40.9182051,12.4181976 41.3740005,11.8299035 41.9570034,11.3317011 C42.5400063,10.8334986 43.2289993,10.4466025 44.0240033,10.1710011 C44.8190072,9.89539976 45.6934984,9.75760115 46.6475031,9.75760115 C47.8983093,9.75760115 49.0536977,9.99609875 50.113703,10.4731011 C51.1737082,10.9501035 52.0746991,11.5701972 52.8167028,12.333401 L51.3857029,14.0506009 C50.7496997,13.4357979 50.0448068,12.9535027 49.271003,12.603701 C48.4971992,12.2538993 47.622708,12.079001 46.6475031,12.079001 C45.4814974,12.079001 44.5434068,12.3492983 43.8332033,12.889901 C43.1229998,13.4305037 42.7679033,14.1777961 42.7679033,15.1318009 C42.7679033,15.6406034 42.8686023,16.0698991 43.0700033,16.4197008 C43.2714043,16.7695025 43.5417016,17.0768994 43.8809033,17.3419007 C44.220105,17.606902 44.5911012,17.8400997 44.9939032,18.0415007 C45.3967052,18.2429017 45.8101011,18.4283998 46.2341032,18.5980007 L49.191503,19.8700006 C49.7851059,20.1244019 50.3469003,20.4158989 50.8769029,20.7445006 C51.4069055,21.0731022 51.8627009,21.4546983 52.2443028,21.8893005 C52.6259047,22.3239026 52.9280017,22.8326975 53.1506028,23.4157004 C53.3732039,23.9987033 53.4845028,24.6717965 53.4845028,25.4350003 C53.4845028,26.2618044 53.3202044,27.0355966 52.9916028,27.7564002 C52.6630012,28.4772037 52.1913059,29.1078974 51.5765029,29.6485001 C50.9616998,30.1891027 50.2197073,30.6130985 49.350503,30.9205 C48.4812987,31.2279015 47.4955086,31.3816 46.3931032,31.3816 C44.9302959,31.3816 43.5735096,31.1060028 42.3227034,30.5548 C41.0718972,30.0035973 39.9907081,29.2510049 39.0791035,28.2970001 L40.6691035,26.4526002 Z M61.9115023,12.365201 L55.6151027,12.365201 L55.6151027,10.1392011 L70.8791018,10.1392011 L70.8791018,12.365201 L64.5827022,12.365201 L64.5827022,31 L61.9115023,31 L61.9115023,12.365201 Z M74.6315016,10.1392011 L86.651901,10.1392011 L86.651901,12.365201 L77.2709015,12.365201 L77.2709015,18.9160007 L85.1891011,18.9160007 L85.1891011,21.1738005 L77.2709015,21.1738005 L77.2709015,28.7422001 L86.969901,28.7422001 L86.969901,31 L74.6315016,31 L74.6315016,10.1392011 Z M94.0295006,20.0290006 L97.5275004,20.0290006 C99.1599085,20.0290006 100.410696,19.6951039 101.2799,19.0273006 C102.149104,18.3594973 102.5837,17.3472075 102.5837,15.9904008 C102.5837,14.612394 102.149104,13.6478037 101.2799,13.096601 C100.410696,12.5453982 99.1599085,12.269801 97.5275004,12.269801 L94.0295006,12.269801 L94.0295006,20.0290006 Z M102.8381,31 L97.8137004,22.1914005 L94.0295006,22.1914005 L94.0295006,31 L91.3901007,31 L91.3901007,10.1392011 L97.9091004,10.1392011 C98.9691056,10.1392011 99.9495958,10.2399001 100.8506,10.4413011 C101.751605,10.6427021 102.525397,10.9765987 103.172,11.4430011 C103.818603,11.9094034 104.322098,12.5135973 104.6825,13.255601 C105.042902,13.9976046 105.2231,14.9091955 105.2231,15.9904008 C105.2231,17.6228089 104.799104,18.9265958 103.9511,19.9018006 C103.103096,20.8770054 101.968907,21.5447987 100.5485,21.9052005 L105.8273,31 L102.8381,31 Z" id="MASTER"></path>
+ </g>
+ <path d="M829.454852,142.471188 C830.196855,143.255591 831.071347,143.886285 832.078351,144.363288 C833.085356,144.84029 834.129446,145.078787 835.210651,145.078787 C836.588658,145.078787 837.659247,144.766091 838.422451,144.140688 C839.185655,143.515284 839.567251,142.693793 839.567251,141.676188 C839.567251,141.146185 839.477152,140.69569 839.296951,140.324688 C839.11675,139.953686 838.872953,139.635689 838.565551,139.370688 C838.25815,139.105686 837.892453,138.867189 837.468451,138.655188 C837.044449,138.443187 836.588654,138.220589 836.101051,137.987388 L833.111851,136.683588 C832.624249,136.471587 832.126054,136.217189 831.617252,135.920388 C831.108449,135.623586 830.652654,135.26319 830.249852,134.839188 C829.84705,134.415186 829.518453,133.911691 829.264052,133.328688 C829.00965,132.745685 828.882452,132.072592 828.882452,131.309388 C828.882452,130.524984 829.04675,129.793592 829.375352,129.115188 C829.703953,128.436785 830.159749,127.848491 830.742752,127.350288 C831.325754,126.852086 832.014748,126.46519 832.809751,126.189588 C833.604755,125.913987 834.479247,125.776189 835.433251,125.776189 C836.684057,125.776189 837.839446,126.014686 838.899451,126.491688 C839.959456,126.968691 840.860447,127.588785 841.602451,128.351988 L840.171451,130.069188 C839.535448,129.454385 838.830555,128.97209 838.056751,128.622288 C837.282947,128.272487 836.408456,128.097588 835.433251,128.097588 C834.267246,128.097588 833.329155,128.367886 832.618951,128.908488 C831.908748,129.449091 831.553652,130.196383 831.553652,131.150388 C831.553652,131.659191 831.654351,132.088486 831.855752,132.438288 C832.057152,132.78809 832.32745,133.095487 832.666651,133.360488 C833.005853,133.625489 833.376849,133.858687 833.779651,134.060088 C834.182453,134.261489 834.595849,134.446987 835.019851,134.616588 L837.977251,135.888588 C838.570854,136.142989 839.132648,136.434486 839.662651,136.763088 C840.192654,137.09169 840.648449,137.473286 841.030051,137.907888 C841.411653,138.34249 841.71375,138.851285 841.936351,139.434288 C842.158952,140.017291 842.270251,140.690384 842.270251,141.453588 C842.270251,142.280392 842.105953,143.054184 841.777351,143.774988 C841.448749,144.495791 840.977054,145.126485 840.362251,145.667087 C839.747448,146.20769 839.005455,146.631686 838.136251,146.939087 C837.267047,147.246489 836.281257,147.400187 835.178851,147.400187 C833.716044,147.400187 832.359258,147.12459 831.108452,146.573387 C829.857645,146.022185 828.776456,145.269592 827.864852,144.315588 L829.454852,142.471188 Z M850.69725,128.383788 L844.400851,128.383788 L844.400851,126.157788 L859.66485,126.157788 L859.66485,128.383788 L853.36845,128.383788 L853.36845,147.018587 L850.69725,147.018587 L850.69725,128.383788 Z M872.289449,138.527988 L871.303649,135.347988 C870.922047,134.181982 870.561651,133.021294 870.222449,131.865888 C869.883248,130.710482 869.544051,129.528594 869.204849,128.320188 L869.077649,128.320188 C868.759648,129.528594 868.431051,130.710482 868.09185,131.865888 C867.752648,133.021294 867.392251,134.181982 867.01065,135.347988 L866.02485,138.527988 L872.289449,138.527988 Z M872.957249,140.658588 L865.35705,140.658588 L863.35365,147.018587 L860.65065,147.018587 L867.71025,126.157788 L870.699449,126.157788 L877.759049,147.018587 L874.928849,147.018587 L872.957249,140.658588 Z M879.508049,136.588188 C879.508049,134.91338 879.746547,133.408195 880.223549,132.072588 C880.700551,130.736982 881.368345,129.602793 882.226949,128.669988 C883.085553,127.737184 884.097843,127.021691 885.263849,126.523488 C886.429854,126.025286 887.712441,125.776189 889.111648,125.776189 C890.553256,125.776189 891.756343,126.046486 892.720948,126.587088 C893.685553,127.127691 894.475245,127.715985 895.090048,128.351988 L893.595448,130.037388 C893.065446,129.486186 892.455952,129.02509 891.766948,128.654088 C891.077945,128.283087 890.203454,128.097588 889.143448,128.097588 C888.083443,128.097588 887.129453,128.293686 886.281449,128.685888 C885.433444,129.07809 884.712652,129.639885 884.119049,130.371288 C883.525446,131.102692 883.06435,131.987783 882.735749,133.026588 C882.407147,134.065393 882.242849,135.231382 882.242849,136.524588 C882.242849,137.838994 882.396547,139.020883 882.703949,140.070288 C883.01135,141.119693 883.456546,142.015384 884.039549,142.757388 C884.622552,143.499391 885.343344,144.071786 886.201949,144.474587 C887.060553,144.877389 888.051643,145.078787 889.175248,145.078787 C889.917252,145.078787 890.622145,144.967489 891.289948,144.744887 C891.957752,144.522286 892.503646,144.220189 892.927648,143.838588 L892.927648,138.400788 L888.507448,138.400788 L888.507448,136.206588 L895.344448,136.206588 L895.344448,144.983387 C894.666045,145.682991 893.770354,146.260685 892.657348,146.716487 C891.544343,147.17229 890.288255,147.400187 888.889048,147.400187 C887.511042,147.400187 886.249654,147.15639 885.104849,146.668787 C883.960043,146.181185 882.974253,145.476292 882.147449,144.554087 C881.320645,143.631883 880.674051,142.497694 880.207649,141.151488 C879.741247,139.805281 879.508049,138.284196 879.508049,136.588188 Z M900.337048,126.157788 L902.976448,126.157788 L902.976448,147.018587 L900.337048,147.018587 L900.337048,126.157788 Z M908.700447,126.157788 L911.435247,126.157788 L918.971847,139.259388 L921.229647,143.584188 L921.356847,143.584188 C921.293246,142.524182 921.224347,141.427093 921.150147,140.292888 C921.075946,139.158682 921.038847,138.040393 921.038847,136.937988 L921.038847,126.157788 L923.551047,126.157788 L923.551047,147.018587 L920.816247,147.018587 L913.247847,133.885188 L910.990047,129.592188 L910.862847,129.592188 C910.947648,130.652194 911.027147,131.728083 911.101347,132.819888 C911.175548,133.911694 911.212647,135.008782 911.212647,136.111188 L911.212647,147.018587 L908.700447,147.018587 L908.700447,126.157788 Z M928.066646,136.588188 C928.066646,134.91338 928.305144,133.408195 928.782146,132.072588 C929.259149,130.736982 929.926942,129.602793 930.785546,128.669988 C931.64415,127.737184 932.65644,127.021691 933.822446,126.523488 C934.988452,126.025286 936.271039,125.776189 937.670246,125.776189 C939.111853,125.776189 940.314941,126.046486 941.279546,126.587088 C942.24415,127.127691 943.033842,127.715985 943.648645,128.351988 L942.154046,130.037388 C941.624043,129.486186 941.014549,129.02509 940.325546,128.654088 C939.636542,128.283087 938.762051,128.097588 937.702046,128.097588 C936.642041,128.097588 935.68805,128.293686 934.840046,128.685888 C933.992042,129.07809 933.271249,129.639885 932.677646,130.371288 C932.084043,131.102692 931.622948,131.987783 931.294346,133.026588 C930.965745,134.065393 930.801446,135.231382 930.801446,136.524588 C930.801446,137.838994 930.955145,139.020883 931.262546,140.070288 C931.569948,141.119693 932.015143,142.015384 932.598146,142.757388 C933.181149,143.499391 933.901942,144.071786 934.760546,144.474587 C935.61915,144.877389 936.61024,145.078787 937.733846,145.078787 C938.475849,145.078787 939.180742,144.967489 939.848546,144.744887 C940.516349,144.522286 941.062243,144.220189 941.486246,143.838588 L941.486246,138.400788 L937.066046,138.400788 L937.066046,136.206588 L943.903045,136.206588 L943.903045,144.983387 C943.224642,145.682991 942.328951,146.260685 941.215946,146.716487 C940.10294,147.17229 938.846853,147.400187 937.447646,147.400187 C936.069639,147.400187 934.808252,147.15639 933.663446,146.668787 C932.51864,146.181185 931.53285,145.476292 930.706046,144.554087 C929.879242,143.631883 929.232649,142.497694 928.766246,141.151488 C928.299844,139.805281 928.066646,138.284196 928.066646,136.588188 Z" id="STAGING" fill="#E04733"></path>
+ <path d="M888.668416,1.13920113 L895.060216,1.13920113 C896.205021,1.13920113 897.254411,1.24520006 898.208415,1.45720111 C899.16242,1.66920216 899.973312,2.01369869 900.641115,2.49070105 C901.308919,2.96770341 901.828313,3.59839707 902.199315,4.38280095 C902.570317,5.16720483 902.755815,6.13179513 902.755815,7.27660079 C902.755815,8.37900625 902.570317,9.33299666 902.199315,10.1386006 C901.828313,10.9442046 901.303619,11.6119979 900.625215,12.1420005 C899.946812,12.6720032 899.13592,13.0694992 898.192515,13.3345005 C897.249111,13.5995018 896.205021,13.7320004 895.060216,13.7320004 L891.307816,13.7320004 L891.307816,22 L888.668416,22 L888.668416,1.13920113 Z M894.742216,11.5696006 C896.565425,11.5696006 897.916911,11.225104 898.796715,10.5361006 C899.67652,9.84709721 900.116415,8.76060813 900.116415,7.27660079 C900.116415,5.77139335 899.67122,4.72730385 898.780815,4.14430096 C897.890411,3.56129808 896.544224,3.26980101 894.742216,3.26980101 L891.307816,3.26980101 L891.307816,11.5696006 L894.742216,11.5696006 Z M909.624615,11.0290006 L913.122615,11.0290006 C914.755023,11.0290006 916.00581,10.6951039 916.875014,10.0273006 C917.744219,9.35949734 918.178814,8.34720752 918.178814,6.99040081 C918.178814,5.61239399 917.744219,4.64780369 916.875014,4.09660097 C916.00581,3.54539824 914.755023,3.26980101 913.122615,3.26980101 L909.624615,3.26980101 L909.624615,11.0290006 Z M918.433214,22 L913.408815,13.1914005 L909.624615,13.1914005 L909.624615,22 L906.985215,22 L906.985215,1.13920113 L913.504215,1.13920113 C914.56422,1.13920113 915.54471,1.23990011 916.445714,1.44130111 C917.346719,1.64270211 918.120511,1.97659875 918.767114,2.44300106 C919.413717,2.90940336 919.917212,3.51359729 920.277614,4.25560096 C920.638016,4.99760463 920.818214,5.90919546 920.818214,6.99040081 C920.818214,8.62280888 920.394218,9.92659578 919.546214,10.9018006 C918.69821,11.8770054 917.564021,12.5447987 916.143614,12.9052005 L921.422414,22 L918.433214,22 Z M933.156613,22.3816 C931.842207,22.3816 930.639119,22.1272025 929.547314,21.6184 C928.455508,21.1095975 927.517418,20.3835048 926.733014,19.4401001 C925.94861,18.4966955 925.339116,17.351907 924.904514,16.0057003 C924.469912,14.6594937 924.252614,13.1490089 924.252614,11.4742006 C924.252614,9.79939228 924.469912,8.29950736 924.904514,6.97450081 C925.339116,5.64949426 925.94861,4.52590555 926.733014,3.60370099 C927.517418,2.68149643 928.455508,1.97660352 929.547314,1.48900111 C930.639119,1.0013987 931.842207,0.757601147 933.156613,0.757601147 C934.47102,0.757601147 935.674108,1.00669864 936.765913,1.50490111 C937.857719,2.00310357 938.801109,2.71329643 939.596113,3.63550099 C940.391117,4.55770555 941.005911,5.68129426 941.440513,7.00630081 C941.875115,8.33130736 942.092413,9.82059239 942.092413,11.4742006 C942.092413,13.1490089 941.875115,14.6594937 941.440513,16.0057003 C941.005911,17.351907 940.391117,18.4966955 939.596113,19.4401001 C938.801109,20.3835048 937.857719,21.1095975 936.765913,21.6184 C935.674108,22.1272025 934.47102,22.3816 933.156613,22.3816 Z M933.156613,20.0602001 C934.089418,20.0602001 934.93211,19.8588021 935.684713,19.4560001 C936.437317,19.0531981 937.083911,18.475504 937.624513,17.7229002 C938.165116,16.9702965 938.583812,16.0693056 938.880613,15.0199004 C939.177415,13.9704952 939.325813,12.7886071 939.325813,11.4742006 C939.325813,10.1809942 939.177415,9.01500589 938.880613,7.97620076 C938.583812,6.93739562 938.165116,6.05760446 937.624513,5.3368009 C937.083911,4.61599733 936.437317,4.05950293 935.684713,3.66730099 C934.93211,3.27509905 934.089418,3.07900102 933.156613,3.07900102 C932.223809,3.07900102 931.381117,3.27509905 930.628514,3.66730099 C929.87591,4.05950293 929.229316,4.61599733 928.688714,5.3368009 C928.148111,6.05760446 927.729415,6.93739562 927.432614,7.97620076 C927.135812,9.01500589 926.987414,10.1809942 926.987414,11.4742006 C926.987414,12.7886071 927.135812,13.9704952 927.432614,15.0199004 C927.729415,16.0693056 928.148111,16.9702965 928.688714,17.7229002 C929.229316,18.475504 929.87591,19.0531981 930.628514,19.4560001 C931.381117,19.8588021 932.223809,20.0602001 933.156613,20.0602001 Z M946.576213,1.13920113 L951.791412,1.13920113 C955.013828,1.13920113 957.462404,2.02959217 959.137212,3.81040098 C960.81202,5.59120979 961.649412,8.14578411 961.649412,11.4742006 C961.649412,13.1278087 961.437414,14.6064939 961.013412,15.9103003 C960.58941,17.2141068 959.964016,18.3164957 959.137212,19.2175002 C958.310408,20.1185046 957.292818,20.8074977 956.084412,21.2845 C954.876006,21.7615024 953.48742,22 951.918612,22 L946.576213,22 L946.576213,1.13920113 Z M951.600612,19.8376001 C954.038625,19.8376001 955.867106,19.1062075 957.086112,17.6434002 C958.305118,16.180593 958.914612,14.1242137 958.914612,11.4742006 C958.914612,8.82418746 958.305118,6.79960782 957.086112,5.4004009 C955.867106,4.00119398 954.038625,3.30160101 951.600612,3.30160101 L949.215613,3.30160101 L949.215613,19.8376001 L951.600612,19.8376001 Z M966.037812,1.13920113 L968.677212,1.13920113 L968.677212,13.4458005 C968.677212,14.6966066 968.80441,15.7459961 969.058812,16.5940003 C969.313213,17.4420045 969.657709,18.1203977 970.092311,18.6292002 C970.526914,19.1380027 971.041008,19.503699 971.634611,19.7263001 C972.228214,19.9489012 972.864208,20.0602001 973.542611,20.0602001 C974.242215,20.0602001 974.888808,19.9489012 975.482411,19.7263001 C976.076014,19.503699 976.595409,19.1380027 977.040611,18.6292002 C977.485813,18.1203977 977.83561,17.4420045 978.090011,16.5940003 C978.344412,15.7459961 978.471611,14.6966066 978.471611,13.4458005 L978.471611,1.13920113 L981.015611,1.13920113 L981.015611,13.3822005 C981.015611,15.0570087 980.824813,16.4667946 980.443211,17.6116002 C980.061609,18.7564059 979.536914,19.6838966 978.869111,20.3941001 C978.201308,21.1043036 977.411616,21.6130985 976.500011,21.9205 C975.588407,22.2279015 974.602617,22.3816 973.542611,22.3816 C972.482606,22.3816 971.491516,22.2279015 970.569311,21.9205 C969.647107,21.6130985 968.852115,21.1043036 968.184312,20.3941001 C967.516508,19.6838966 966.991814,18.7564059 966.610212,17.6116002 C966.22861,16.4667946 966.037812,15.0570087 966.037812,13.3822005 L966.037812,1.13920113 Z M985.435811,11.5696006 C985.435811,9.89479228 985.669008,8.38960741 986.135411,7.05400081 C986.601813,5.7183942 987.248406,4.5842056 988.07521,3.65140099 C988.902015,2.71859638 989.882505,2.00310357 991.01671,1.50490111 C992.150916,1.00669864 993.385804,0.757601147 994.72141,0.757601147 C995.993416,0.757601147 997.106405,1.01729854 998.06041,1.5367011 C999.014415,2.05610367 999.798807,2.65499765 1000.41361,3.33340101 L998.91901,5.01880092 C998.367807,4.42519798 997.758313,3.95350272 997.09051,3.60370099 C996.422707,3.25389926 995.643615,3.07900102 994.75321,3.07900102 C993.756805,3.07900102 992.855814,3.27509905 992.05021,3.66730099 C991.244606,4.05950293 990.555613,4.62129728 989.98321,5.3527009 C989.410808,6.08410452 988.965612,6.96919562 988.64761,8.00800076 C988.329609,9.04680589 988.17061,10.2127942 988.17061,11.5060006 C988.17061,12.8204071 988.324309,14.0022952 988.63171,15.0517004 C988.939112,16.1011056 989.368408,16.9967966 989.91961,17.7388002 C990.470813,18.4808039 991.143906,19.0531981 991.93891,19.4560001 C992.733914,19.8588021 993.629605,20.0602001 994.62601,20.0602001 C995.643615,20.0602001 996.528706,19.8588021 997.28131,19.4560001 C998.033914,19.0531981 998.749407,18.4808039 999.42781,17.7388002 L1000.92241,19.3606001 C1000.09561,20.3146049 999.162815,21.0565974 998.12401,21.5866 C997.085205,22.1166026 995.898017,22.3816 994.56241,22.3816 C993.248004,22.3816 992.034316,22.1378024 990.92131,21.6502 C989.808305,21.1625976 988.843715,20.4577047 988.02751,19.5355001 C987.211306,18.6132956 986.575313,17.479107 986.119511,16.1329003 C985.663708,14.7866937 985.435811,13.265609 985.435811,11.5696006 Z M1009.12681,3.36520101 L1002.83041,3.36520101 L1002.83041,1.13920113 L1018.09441,1.13920113 L1018.09441,3.36520101 L1011.79801,3.36520101 L1011.79801,22 L1009.12681,22 L1009.12681,3.36520101 Z M1021.84681,1.13920113 L1024.48621,1.13920113 L1024.48621,22 L1021.84681,22 L1021.84681,1.13920113 Z M1037.90581,22.3816 C1036.5914,22.3816 1035.38831,22.1272025 1034.29651,21.6184 C1033.2047,21.1095975 1032.26661,20.3835048 1031.48221,19.4401001 C1030.6978,18.4966955 1030.08831,17.351907 1029.65371,16.0057003 C1029.21911,14.6594937 1029.00181,13.1490089 1029.00181,11.4742006 C1029.00181,9.79939228 1029.21911,8.29950736 1029.65371,6.97450081 C1030.08831,5.64949426 1030.6978,4.52590555 1031.48221,3.60370099 C1032.26661,2.68149643 1033.2047,1.97660352 1034.29651,1.48900111 C1035.38831,1.0013987 1036.5914,0.757601147 1037.90581,0.757601147 C1039.22021,0.757601147 1040.4233,1.00669864 1041.51511,1.50490111 C1042.60691,2.00310357 1043.5503,2.71329643 1044.34531,3.63550099 C1045.14031,4.55770555 1045.75511,5.68129426 1046.18971,7.00630081 C1046.62431,8.33130736 1046.84161,9.82059239 1046.84161,11.4742006 C1046.84161,13.1490089 1046.62431,14.6594937 1046.18971,16.0057003 C1045.75511,17.351907 1045.14031,18.4966955 1044.34531,19.4401001 C1043.5503,20.3835048 1042.60691,21.1095975 1041.51511,21.6184 C1040.4233,22.1272025 1039.22021,22.3816 1037.90581,22.3816 Z M1037.90581,20.0602001 C1038.83861,20.0602001 1039.6813,19.8588021 1040.43391,19.4560001 C1041.18651,19.0531981 1041.8331,18.475504 1042.37371,17.7229002 C1042.91431,16.9702965 1043.33301,16.0693056 1043.62981,15.0199004 C1043.92661,13.9704952 1044.07501,12.7886071 1044.07501,11.4742006 C1044.07501,10.1809942 1043.92661,9.01500589 1043.62981,7.97620076 C1043.33301,6.93739562 1042.91431,6.05760446 1042.37371,5.3368009 C1041.8331,4.61599733 1041.18651,4.05950293 1040.43391,3.66730099 C1039.6813,3.27509905 1038.83861,3.07900102 1037.90581,3.07900102 C1036.973,3.07900102 1036.13031,3.27509905 1035.37771,3.66730099 C1034.6251,4.05950293 1033.97851,4.61599733 1033.43791,5.3368009 C1032.89731,6.05760446 1032.47861,6.93739562 1032.18181,7.97620076 C1031.88501,9.01500589 1031.73661,10.1809942 1031.73661,11.4742006 C1031.73661,12.7886071 1031.88501,13.9704952 1032.18181,15.0199004 C1032.47861,16.0693056 1032.89731,16.9702965 1033.43791,17.7229002 C1033.97851,18.475504 1034.6251,19.0531981 1035.37771,19.4560001 C1036.13031,19.8588021 1036.973,20.0602001 1037.90581,20.0602001 Z M1051.32541,1.13920113 L1054.06021,1.13920113 L1061.59681,14.2408004 L1063.85461,18.5656002 L1063.98181,18.5656002 C1063.91821,17.5055949 1063.84931,16.408506 1063.77511,15.2743004 C1063.70091,14.1400948 1063.66381,13.021806 1063.66381,11.9194005 L1063.66381,1.13920113 L1066.17601,1.13920113 L1066.17601,22 L1063.44121,22 L1055.87281,8.86660071 L1053.61501,4.57360094 L1053.48781,4.57360094 C1053.57261,5.63360618 1053.65211,6.70949537 1053.72631,7.80130077 C1053.80051,8.89310617 1053.83761,9.99019514 1053.83761,11.0926006 L1053.83761,22 L1051.32541,22 L1051.32541,1.13920113 Z" id="PRODUCTION-Copy-2" fill="#E04733"></path>
+ <polygon id="Line-Copy-17" fill="#E0513E" points="282.270169 382 498.277674 382 498.277674 378 282.270169 378"></polygon>
+ <path d="M277.201592,391.484016 L283.029685,397.309042 L285.857368,394.47987 L280.029274,388.654844 L277.201592,391.484016 Z M286.198357,400.476046 L292.02645,406.301071 L294.854133,403.4719 L289.026039,397.646874 L286.198357,400.476046 Z M295.195122,409.468075 L301.023216,415.293101 L303.850898,412.463929 L298.022805,406.638904 L295.195122,409.468075 Z M304.191888,418.460105 L310.019981,424.285131 L312.847663,421.455959 L307.01957,415.630933 L304.191888,418.460105 Z M313.188653,427.452135 L319.016746,433.27716 L321.844429,430.447989 L316.016335,424.622963 L313.188653,427.452135 Z M322.185418,436.444164 L328.013511,442.26919 L330.841194,439.440018 L325.0131,433.614993 L322.185418,436.444164 Z M331.182183,445.436194 L337.010277,451.26122 L339.837959,448.432048 L334.009866,442.607022 L331.182183,445.436194 Z M340.178949,454.428224 L346.007042,460.253249 L348.834724,457.424078 L343.006631,451.599052 L340.178949,454.428224 Z M349.175714,463.420253 L355.003807,469.245279 L357.83149,466.416107 L352.003396,460.591082 L349.175714,463.420253 Z M358.172479,472.412283 L364.000572,478.237309 L366.828255,475.408137 L361.000162,469.583111 L358.172479,472.412283 Z M367.169244,481.404313 L372.997338,487.229338 L375.82502,484.400167 L369.996927,478.575141 L367.169244,481.404313 Z M376.16601,490.396342 L381.994103,496.221368 L384.821785,493.392196 L378.993692,487.567171 L376.16601,490.396342 Z" id="Line-Copy-18" fill="#E0513E"></path>
+ <path d="M516.770072,391.484016 L522.598165,397.309042 L525.425848,394.47987 L519.597754,388.654844 L516.770072,391.484016 Z M525.766837,400.476046 L531.594931,406.301071 L534.422613,403.4719 L528.59452,397.646874 L525.766837,400.476046 Z M534.763603,409.468075 L540.591696,415.293101 L543.419378,412.463929 L537.591285,406.638904 L534.763603,409.468075 Z M543.760368,418.460105 L549.588461,424.285131 L552.416144,421.455959 L546.58805,415.630933 L543.760368,418.460105 Z M552.757133,427.452135 L558.585226,433.27716 L561.412909,430.447989 L555.584816,424.622963 L552.757133,427.452135 Z M561.753898,436.444164 L567.581992,442.26919 L570.409674,439.440018 L564.581581,433.614993 L561.753898,436.444164 Z M570.750664,445.436194 L576.578757,451.26122 L579.406439,448.432048 L573.578346,442.607022 L570.750664,445.436194 Z M579.747429,454.428224 L585.575522,460.253249 L588.403205,457.424078 L582.575111,451.599052 L579.747429,454.428224 Z M588.744194,463.420253 L594.572288,469.245279 L597.39997,466.416107 L591.571877,460.591082 L588.744194,463.420253 Z M597.740959,472.412283 L603.569053,478.237309 L606.396735,475.408137 L600.568642,469.583111 L597.740959,472.412283 Z M606.737725,481.404313 L612.565818,487.229338 L615.3935,484.400167 L609.565407,478.575141 L606.737725,481.404313 Z M615.73449,490.396342 L621.562583,496.221368 L624.390266,493.392196 L618.562172,487.567171 L615.73449,490.396342 Z" id="Line-Copy-22" fill="#E0513E"></path>
+ <path d="M756.338552,391.484016 L762.166646,397.309042 L764.994328,394.47987 L759.166235,388.654844 L756.338552,391.484016 Z M765.335318,400.476046 L771.163411,406.301071 L773.991093,403.4719 L768.163,397.646874 L765.335318,400.476046 Z M774.332083,409.468075 L780.160176,415.293101 L782.987859,412.463929 L777.159765,406.638904 L774.332083,409.468075 Z M783.328848,418.460105 L789.156941,424.285131 L791.984624,421.455959 L786.156531,415.630933 L783.328848,418.460105 Z M792.325613,427.452135 L798.153707,433.27716 L800.981389,430.447989 L795.153296,424.622963 L792.325613,427.452135 Z M801.322379,436.444164 L807.150472,442.26919 L809.978154,439.440018 L804.150061,433.614993 L801.322379,436.444164 Z M810.319144,445.436194 L816.147237,451.26122 L818.97492,448.432048 L813.146826,442.607022 L810.319144,445.436194 Z M819.315909,454.428224 L825.144003,460.253249 L827.971685,457.424078 L822.143592,451.599052 L819.315909,454.428224 Z M828.312674,463.420253 L834.140768,469.245279 L836.96845,466.416107 L831.140357,460.591082 L828.312674,463.420253 Z M837.30944,472.412283 L843.137533,478.237309 L845.965216,475.408137 L840.137122,469.583111 L837.30944,472.412283 Z M846.306205,481.404313 L852.134298,487.229338 L854.961981,484.400167 L849.133887,478.575141 L846.306205,481.404313 Z M855.30297,490.396342 L861.131064,496.221368 L863.958746,493.392196 L858.130653,487.567171 L855.30297,490.396342 Z" id="Line-Copy-23" fill="#E0513E"></path>
+ <path d="M880.010512,252.341438 L885.838606,246.516413 L883.010923,243.687241 L877.18283,249.512267 L880.010512,252.341438 Z M889.007278,243.349409 L894.835371,237.524383 L892.007689,234.695211 L886.179595,240.520237 L889.007278,243.349409 Z M898.004043,234.357379 L903.832136,228.532353 L901.004454,225.703182 L895.176361,231.528207 L898.004043,234.357379 Z M907.000808,225.365349 L912.828902,219.540324 L910.001219,216.711152 L904.173126,222.536178 L907.000808,225.365349 Z M915.997573,216.37332 L921.825667,210.548294 L918.997984,207.719122 L913.169891,213.544148 L915.997573,216.37332 Z M924.994339,207.38129 L930.822432,201.556264 L927.99475,198.727093 L922.166656,204.552118 L924.994339,207.38129 Z M933.991104,198.38926 L939.819197,192.564235 L936.991515,189.735063 L931.163422,195.560089 L933.991104,198.38926 Z M942.987869,189.397231 L948.815963,183.572205 L945.98828,180.743033 L940.160187,186.568059 L942.987869,189.397231 Z M951.984635,180.405201 L957.812728,174.580175 L954.985045,171.751004 L949.156952,177.576029 L951.984635,180.405201 Z M960.9814,171.413171 L966.809493,165.588146 L963.981811,162.758974 L958.153717,168.584 L960.9814,171.413171 Z M969.978165,162.421142 L975.806258,156.596116 L972.978576,153.766944 L967.150483,159.59197 L969.978165,162.421142 Z M978.97493,153.429112 L984.803024,147.604086 L981.975341,144.774915 L976.147248,150.59994 L978.97493,153.429112 Z" id="Line-Copy-27" fill="#E0513E"></path>
+ <path d="M1114.27955,23.0223048 C1119.09619,23.0223048 1123,19.1205524 1123,14.3085502 C1123,9.49654797 1119.09619,5.59479554 1114.27955,5.59479554 C1109.46291,5.59479554 1105.5591,9.49654797 1105.5591,14.3085502 C1105.5591,19.1205524 1109.46291,23.0223048 1114.27955,23.0223048 Z M1114.27955,27.0223048 C1107.25424,27.0223048 1101.5591,21.330163 1101.5591,14.3085502 C1101.5591,7.28693738 1107.25424,1.59479554 1114.27955,1.59479554 C1121.30486,1.59479554 1127,7.28693738 1127,14.3085502 C1127,21.330163 1121.30486,27.0223048 1114.27955,27.0223048 Z" id="Oval-Copy-24" fill="#E04733"></path>
+ <polygon id="Line" fill="#E04733" points="758.424239 374.304508 864.800215 266.404117 861.951738 263.595883 755.575761 371.496274"></polygon>
+ <polygon id="Line-Copy" fill="#E04733" points="1000.57718 131.539201 1109.53226 24.6788494 1106.73141 21.8231038 997.776337 128.683455"></polygon>
+ <path d="M510.058161,389.60223 C514.874804,389.60223 518.778612,385.700478 518.778612,380.888476 C518.778612,376.076474 514.874804,372.174721 510.058161,372.174721 C505.241518,372.174721 501.337711,376.076474 501.337711,380.888476 C501.337711,385.700478 505.241518,389.60223 510.058161,389.60223 Z M510.058161,393.60223 C503.032851,393.60223 497.337711,387.910089 497.337711,380.888476 C497.337711,373.866863 503.032851,368.174721 510.058161,368.174721 C517.083472,368.174721 522.778612,373.866863 522.778612,380.888476 C522.778612,387.910089 517.083472,393.60223 510.058161,393.60223 Z" id="Oval-Copy-15" fill="#E04733"></path>
+ <polygon id="Line-Copy-19" fill="#E0513E" points="521.838649 382 737.846154 382 737.846154 378 521.838649 378"></polygon>
+ <path d="M749.626642,389.60223 C754.443285,389.60223 758.347092,385.700478 758.347092,380.888476 C758.347092,376.076474 754.443285,372.174721 749.626642,372.174721 C744.809999,372.174721 740.906191,376.076474 740.906191,380.888476 C740.906191,385.700478 744.809999,389.60223 749.626642,389.60223 Z M749.626642,393.60223 C742.601331,393.60223 736.906191,387.910089 736.906191,380.888476 C736.906191,373.866863 742.601331,368.174721 749.626642,368.174721 C756.651952,368.174721 762.347092,373.866863 762.347092,380.888476 C762.347092,387.910089 756.651952,393.60223 749.626642,393.60223 Z" id="Oval-Copy-16" fill="#E04733"></path>
+ <path d="M270.489681,389.60223 C275.306324,389.60223 279.210131,385.700478 279.210131,380.888476 C279.210131,376.076474 275.306324,372.174721 270.489681,372.174721 C265.673038,372.174721 261.769231,376.076474 261.769231,380.888476 C261.769231,385.700478 265.673038,389.60223 270.489681,389.60223 Z M270.489681,393.60223 C263.46437,393.60223 257.769231,387.910089 257.769231,380.888476 C257.769231,373.866863 263.46437,368.174721 270.489681,368.174721 C277.514992,368.174721 283.210131,373.866863 283.210131,380.888476 C283.210131,387.910089 277.514992,393.60223 270.489681,393.60223 Z" id="Oval-Copy-14" fill="#E04733"></path>
+ <path d="M630.902439,510.3829 C635.719082,510.3829 639.622889,506.481147 639.622889,501.669145 C639.622889,496.857143 635.719082,492.95539 630.902439,492.95539 C626.085796,492.95539 622.181989,496.857143 622.181989,501.669145 C622.181989,506.481147 626.085796,510.3829 630.902439,510.3829 Z M630.902439,514.3829 C623.877128,514.3829 618.181989,508.690758 618.181989,501.669145 C618.181989,494.647532 623.877128,488.95539 630.902439,488.95539 C637.92775,488.95539 643.622889,494.647532 643.622889,501.669145 C643.622889,508.690758 637.92775,514.3829 630.902439,514.3829 Z" id="Oval-Copy-19" fill="#E04733"></path>
+ <path d="M870.470919,510.3829 C875.287562,510.3829 879.19137,506.481147 879.19137,501.669145 C879.19137,496.857143 875.287562,492.95539 870.470919,492.95539 C865.654276,492.95539 861.750469,496.857143 861.750469,501.669145 C861.750469,506.481147 865.654276,510.3829 870.470919,510.3829 Z M870.470919,514.3829 C863.445609,514.3829 857.750469,508.690758 857.750469,501.669145 C857.750469,494.647532 863.445609,488.95539 870.470919,488.95539 C877.49623,488.95539 883.19137,494.647532 883.19137,501.669145 C883.19137,508.690758 877.49623,514.3829 870.470919,514.3829 Z" id="Oval-Copy-18" fill="#E04733"></path>
+ <path d="M391.333959,510.3829 C396.150602,510.3829 400.054409,506.481147 400.054409,501.669145 C400.054409,496.857143 396.150602,492.95539 391.333959,492.95539 C386.517316,492.95539 382.613508,496.857143 382.613508,501.669145 C382.613508,506.481147 386.517316,510.3829 391.333959,510.3829 Z M391.333959,514.3829 C384.308648,514.3829 378.613508,508.690758 378.613508,501.669145 C378.613508,494.647532 384.308648,488.95539 391.333959,488.95539 C398.359269,488.95539 404.054409,494.647532 404.054409,501.669145 C404.054409,508.690758 398.359269,514.3829 391.333959,514.3829 Z" id="Oval-Copy-17" fill="#E04733"></path>
+ <path d="M28.833894,372.064086 L22.5374943,372.064086 L22.5374943,369.838086 L37.8014935,369.838086 L37.8014935,372.064086 L31.5050938,372.064086 L31.5050938,390.698885 L28.833894,390.698885 L28.833894,372.064086 Z M49.2494929,391.080485 C47.9350864,391.080485 46.7319985,390.826087 45.6401931,390.317285 C44.5483877,389.808482 43.6102971,389.08239 42.8258932,388.138985 C42.0414893,387.19558 41.4319955,386.050792 40.9973933,384.704585 C40.5627912,383.358378 40.3454934,381.847894 40.3454934,380.173085 C40.3454934,378.498277 40.5627912,376.998392 40.9973933,375.673386 C41.4319955,374.348379 42.0414893,373.22479 42.8258932,372.302586 C43.6102971,371.380381 44.5483877,370.675488 45.6401931,370.187886 C46.7319985,369.700283 47.9350864,369.456486 49.2494929,369.456486 C50.5638994,369.456486 51.7669873,369.705583 52.8587927,370.203786 C53.9505981,370.701988 54.8939886,371.412181 55.6889925,372.334386 C56.4839965,373.25659 57.0987903,374.380179 57.5333924,375.705186 C57.9679946,377.030192 58.1852924,378.519477 58.1852924,380.173085 C58.1852924,381.847894 57.9679946,383.358378 57.5333924,384.704585 C57.0987903,386.050792 56.4839965,387.19558 55.6889925,388.138985 C54.8939886,389.08239 53.9505981,389.808482 52.8587927,390.317285 C51.7669873,390.826087 50.5638994,391.080485 49.2494929,391.080485 Z M49.2494929,388.759085 C50.1822975,388.759085 51.024989,388.557687 51.7775927,388.154885 C52.5301965,387.752083 53.17679,387.174389 53.7173926,386.421785 C54.2579953,385.669181 54.6766911,384.76819 54.9734926,383.718785 C55.270294,382.66938 55.4186925,381.487492 55.4186925,380.173085 C55.4186925,378.879879 55.270294,377.713891 54.9734926,376.675086 C54.6766911,375.63628 54.2579953,374.756489 53.7173926,374.035686 C53.17679,373.314882 52.5301965,372.758388 51.7775927,372.366186 C51.024989,371.973984 50.1822975,371.777886 49.2494929,371.777886 C48.3166883,371.777886 47.4739967,371.973984 46.721393,372.366186 C45.9687893,372.758388 45.3221958,373.314882 44.7815931,374.035686 C44.2409904,374.756489 43.8222947,375.63628 43.5254932,376.675086 C43.2286917,377.713891 43.0802932,378.879879 43.0802932,380.173085 C43.0802932,381.487492 43.2286917,382.66938 43.5254932,383.718785 C43.8222947,384.76819 44.2409904,385.669181 44.7815931,386.421785 C45.3221958,387.174389 45.9687893,387.752083 46.721393,388.154885 C47.4739967,388.557687 48.3166883,388.759085 49.2494929,388.759085 Z M62.6690922,369.838086 L69.0608918,369.838086 C70.2056975,369.838086 71.2550869,369.944085 72.2090916,370.156086 C73.1630964,370.368087 73.9739882,370.712583 74.6417915,371.189586 C75.3095948,371.666588 75.8289896,372.297282 76.1999914,373.081686 C76.5709933,373.86609 76.7564914,374.83068 76.7564914,375.975486 C76.7564914,377.077891 76.5709933,378.031881 76.1999914,378.837485 C75.8289896,379.643089 75.3042949,380.310883 74.6258915,380.840885 C73.9474882,381.370888 73.1365963,381.768384 72.1931916,382.033385 C71.249787,382.298387 70.2056975,382.430885 69.0608918,382.430885 L65.308492,382.430885 L65.308492,390.698885 L62.6690922,390.698885 L62.6690922,369.838086 Z M68.7428918,380.268485 C70.5661008,380.268485 71.9175873,379.923989 72.7973916,379.234985 C73.677196,378.545982 74.1170915,377.459493 74.1170915,375.975486 C74.1170915,374.470278 73.671896,373.426189 72.7814916,372.843186 C71.8910872,372.260183 70.5449007,371.968686 68.7428918,371.968686 L65.308492,371.968686 L65.308492,380.268485 L68.7428918,380.268485 Z M80.9858912,369.838086 L83.625291,369.838086 L83.625291,390.698885 L80.9858912,390.698885 L80.9858912,369.838086 Z M88.1408908,380.268485 C88.1408908,378.593677 88.3740884,377.088492 88.8404907,375.752886 C89.306893,374.417279 89.9534865,373.28309 90.7802906,372.350286 C91.6070947,371.417481 92.5875849,370.701988 93.7217905,370.203786 C94.8559961,369.705583 96.0908837,369.456486 97.4264903,369.456486 C98.6984966,369.456486 99.8114854,369.716183 100.76549,370.235586 C101.719495,370.754988 102.503887,371.353882 103.11869,372.032286 L101.62409,373.717686 C101.072887,373.124083 100.463393,372.652387 99.7955901,372.302586 C99.1277868,371.952784 98.3486947,371.777886 97.4582903,371.777886 C96.4618853,371.777886 95.5608944,371.973984 94.7552904,372.366186 C93.9496864,372.758388 93.2606934,373.320182 92.6882905,374.051586 C92.1158877,374.782989 91.6706922,375.66808 91.3526906,376.706886 C91.034689,377.745691 90.8756906,378.911679 90.8756906,380.204885 C90.8756906,381.519292 91.0293891,382.70118 91.3367906,383.750585 C91.6441921,384.79999 92.0734878,385.695681 92.6246905,386.437685 C93.1758933,387.179689 93.8489865,387.752083 94.6439904,388.154885 C95.4389944,388.557687 96.3346854,388.759085 97.3310903,388.759085 C98.3486953,388.759085 99.2337864,388.557687 99.9863901,388.154885 C100.738994,387.752083 101.454487,387.179689 102.13289,386.437685 L103.62749,388.059485 C102.800686,389.01349 101.867895,389.755482 100.82909,390.285485 C99.790285,390.815487 98.6030969,391.080485 97.2674903,391.080485 C95.9530838,391.080485 94.739396,390.836687 93.6263905,390.349085 C92.513385,389.861482 91.5487947,389.156589 90.7325906,388.234385 C89.9163866,387.31218 89.280393,386.177992 88.8245907,384.831785 C88.3687885,383.485578 88.1408908,381.964494 88.1408908,380.268485 Z M113.930689,369.838086 L120.131689,369.838086 C121.191694,369.838086 122.161585,369.933485 123.041389,370.124286 C123.921193,370.315087 124.673786,370.611884 125.299189,371.014686 C125.924592,371.417488 126.412187,371.942183 126.761989,372.588786 C127.11179,373.235389 127.286689,374.014481 127.286689,374.926086 C127.286689,375.964891 126.989892,376.902981 126.396289,377.740385 C125.802686,378.57779 124.891095,379.155484 123.661489,379.473485 L123.661489,379.600685 C125.187896,379.833887 126.375084,380.363881 127.223089,381.190685 C128.071093,382.017489 128.495089,383.151678 128.495089,384.593285 C128.495089,385.61089 128.304291,386.501281 127.922689,387.264485 C127.541087,388.027689 127.000492,388.663682 126.300889,389.172485 C125.601285,389.681287 124.763894,390.062884 123.788689,390.317285 C122.813484,390.571686 121.742895,390.698885 120.576889,390.698885 L113.930689,390.698885 L113.930689,369.838086 Z M119.654689,378.742085 C121.456698,378.742085 122.744585,378.434688 123.518389,377.819885 C124.292193,377.205082 124.679089,376.314691 124.679089,375.148686 C124.679089,374.00388 124.270993,373.182388 123.454789,372.684186 C122.638585,372.185983 121.414297,371.936886 119.781889,371.936886 L116.570089,371.936886 L116.570089,378.742085 L119.654689,378.742085 Z M120.195289,388.600085 C121.997298,388.600085 123.396484,388.266188 124.392889,387.598385 C125.389294,386.930582 125.887489,385.886492 125.887489,384.466085 C125.887489,383.172879 125.399894,382.224188 124.424689,381.619985 C123.449484,381.015782 122.039698,380.713685 120.195289,380.713685 L116.570089,380.713685 L116.570089,388.600085 L120.195289,388.600085 Z M135.268488,379.727885 L138.766488,379.727885 C140.398896,379.727885 141.649684,379.393989 142.518888,378.726185 C143.388092,378.058382 143.822688,377.046092 143.822688,375.689286 C143.822688,374.311279 143.388092,373.346688 142.518888,372.795486 C141.649684,372.244283 140.398896,371.968686 138.766488,371.968686 L135.268488,371.968686 L135.268488,379.727885 Z M144.077088,390.698885 L139.052688,381.890285 L135.268488,381.890285 L135.268488,390.698885 L132.629088,390.698885 L132.629088,369.838086 L139.148088,369.838086 C140.208093,369.838086 141.188583,369.938785 142.089588,370.140186 C142.990592,370.341587 143.764385,370.675484 144.410988,371.141886 C145.057591,371.608288 145.561086,372.212482 145.921488,372.954486 C146.281889,373.696489 146.462088,374.60808 146.462088,375.689286 C146.462088,377.321694 146.038092,378.625481 145.190088,379.600685 C144.342084,380.57589 143.207895,381.243683 141.787488,381.604085 L147.066288,390.698885 L144.077088,390.698885 Z M159.977087,382.208285 L158.991287,379.028285 C158.609685,377.86228 158.249289,376.701591 157.910087,375.546186 C157.570885,374.39078 157.231689,373.208892 156.892487,372.000486 L156.765287,372.000486 C156.447286,373.208892 156.118689,374.39078 155.779487,375.546186 C155.440285,376.701591 155.079889,377.86228 154.698287,379.028285 L153.712487,382.208285 L159.977087,382.208285 Z M160.644887,384.338885 L153.044687,384.338885 L151.041287,390.698885 L148.338288,390.698885 L155.397887,369.838086 L158.387087,369.838086 L165.446687,390.698885 L162.616487,390.698885 L160.644887,384.338885 Z M168.404086,369.838086 L171.138886,369.838086 L178.675486,382.939685 L180.933286,387.264485 L181.060486,387.264485 C180.996885,386.20448 180.927986,385.107391 180.853786,383.973185 C180.779585,382.83898 180.742486,381.720691 180.742486,380.618285 L180.742486,369.838086 L183.254686,369.838086 L183.254686,390.698885 L180.519886,390.698885 L172.951486,377.565485 L170.693686,373.272486 L170.566486,373.272486 C170.651287,374.332491 170.730786,375.40838 170.804986,376.500186 C170.879187,377.591991 170.916286,378.68908 170.916286,379.791485 L170.916286,390.698885 L168.404086,390.698885 L168.404086,369.838086 Z M187.770285,380.268485 C187.770285,378.593677 188.003483,377.088492 188.469885,375.752886 C188.936288,374.417279 189.582881,373.28309 190.409685,372.350286 C191.236489,371.417481 192.216979,370.701988 193.351185,370.203786 C194.485391,369.705583 195.720278,369.456486 197.055885,369.456486 C198.327891,369.456486 199.44088,369.716183 200.394885,370.235586 C201.348889,370.754988 202.133282,371.353882 202.748085,372.032286 L201.253485,373.717686 C200.702282,373.124083 200.092788,372.652387 199.424985,372.302586 C198.757181,371.952784 197.978089,371.777886 197.087685,371.777886 C196.09128,371.777886 195.190289,371.973984 194.384685,372.366186 C193.579081,372.758388 192.890088,373.320182 192.317685,374.051586 C191.745282,374.782989 191.300087,375.66808 190.982085,376.706886 C190.664084,377.745691 190.505085,378.911679 190.505085,380.204885 C190.505085,381.519292 190.658784,382.70118 190.966185,383.750585 C191.273587,384.79999 191.702882,385.695681 192.254085,386.437685 C192.805288,387.179689 193.478381,387.752083 194.273385,388.154885 C195.068389,388.557687 195.96408,388.759085 196.960485,388.759085 C197.97809,388.759085 198.863181,388.557687 199.615785,388.154885 C200.368388,387.752083 201.083881,387.179689 201.762285,386.437685 L203.256885,388.059485 C202.43008,389.01349 201.49729,389.755482 200.458485,390.285485 C199.41968,390.815487 198.232492,391.080485 196.896885,391.080485 C195.582478,391.080485 194.368791,390.836687 193.255785,390.349085 C192.14278,389.861482 191.178189,389.156589 190.361985,388.234385 C189.545781,387.31218 188.909788,386.177992 188.453985,384.831785 C187.998183,383.485578 187.770285,381.964494 187.770285,380.268485 Z M207.136484,369.838086 L209.775884,369.838086 L209.775884,378.583085 L219.474884,378.583085 L219.474884,369.838086 L222.146084,369.838086 L222.146084,390.698885 L219.474884,390.698885 L219.474884,380.872685 L209.775884,380.872685 L209.775884,390.698885 L207.136484,390.698885 L207.136484,369.838086 Z" id="TOPIC-BRANCH" fill="#E04733"></path>
+ <path d="M687.15753,304.150354 L690.273929,304.150354 L694.312529,315.343953 L695.838929,319.605153 L695.966129,319.605153 L697.428929,315.343953 L701.435729,304.150354 L704.552129,304.150354 L704.552129,325.011152 L702.039929,325.011152 L702.039929,313.531353 C702.039929,312.598548 702.077028,311.570359 702.151229,310.446753 C702.225429,309.323148 702.304928,308.294958 702.389729,307.362153 L702.262529,307.362153 L700.577129,312.004953 L696.570329,322.912353 L695.075729,322.912353 L691.037129,312.004953 L689.383529,307.362153 L689.256329,307.362153 C689.31993,308.294958 689.394129,309.323148 689.478929,310.446753 C689.56373,311.570359 689.606129,312.598548 689.606129,313.531353 L689.606129,325.011152 L687.15753,325.011152 L687.15753,304.150354 Z M710.276128,304.150354 L722.296528,304.150354 L722.296528,306.376353 L712.915528,306.376353 L712.915528,312.927153 L720.833728,312.927153 L720.833728,315.184953 L712.915528,315.184953 L712.915528,322.753353 L722.614528,322.753353 L722.614528,325.011152 L710.276128,325.011152 L710.276128,304.150354 Z M729.674127,314.040153 L733.172127,314.040153 C734.804535,314.040153 736.055323,313.706256 736.924527,313.038453 C737.793731,312.37065 738.228327,311.35836 738.228327,310.001553 C738.228327,308.623546 737.793731,307.658956 736.924527,307.107753 C736.055323,306.556551 734.804535,306.280953 733.172127,306.280953 L729.674127,306.280953 L729.674127,314.040153 Z M738.482727,325.011152 L733.458327,316.202553 L729.674127,316.202553 L729.674127,325.011152 L727.034727,325.011152 L727.034727,304.150354 L733.553727,304.150354 C734.613732,304.150354 735.594222,304.251053 736.495227,304.452454 C737.396231,304.653855 738.170024,304.987751 738.816627,305.454153 C739.46323,305.920556 739.966725,306.52475 740.327127,307.266753 C740.687528,308.008757 740.867727,308.920348 740.867727,310.001553 C740.867727,311.633961 740.443731,312.937748 739.595727,313.912953 C738.747723,314.888158 737.613534,315.555951 736.193127,315.916353 L741.471927,325.011152 L738.482727,325.011152 Z M744.302126,314.580753 C744.302126,312.905945 744.540624,311.40076 745.017626,310.065153 C745.494629,308.729547 746.162422,307.595358 747.021026,306.662553 C747.879631,305.729749 748.89192,305.014256 750.057926,304.516054 C751.223932,304.017851 752.506519,303.768754 753.905726,303.768754 C755.347333,303.768754 756.550421,304.039051 757.515026,304.579654 C758.479631,305.120256 759.269323,305.70855 759.884126,306.344553 L758.389526,308.029953 C757.859523,307.478751 757.250029,307.017655 756.561026,306.646653 C755.872022,306.275652 754.997531,306.090153 753.937526,306.090153 C752.877521,306.090153 751.92353,306.286251 751.075526,306.678453 C750.227522,307.070655 749.506729,307.63245 748.913126,308.363853 C748.319523,309.095257 747.858428,309.980348 747.529826,311.019153 C747.201225,312.057958 747.036926,313.223947 747.036926,314.517153 C747.036926,315.831559 747.190625,317.013448 747.498026,318.062853 C747.805428,319.112258 748.250623,320.007949 748.833626,320.749953 C749.416629,321.491956 750.137422,322.064351 750.996026,322.467153 C751.85463,322.869955 752.84572,323.071353 753.969326,323.071353 C754.71133,323.071353 755.416223,322.960054 756.084026,322.737453 C756.751829,322.514851 757.297724,322.212754 757.721726,321.831153 L757.721726,316.393353 L753.301526,316.393353 L753.301526,314.199153 L760.138526,314.199153 L760.138526,322.975953 C759.460122,323.675556 758.564431,324.25325 757.451426,324.709052 C756.33842,325.164855 755.082333,325.392752 753.683126,325.392752 C752.305119,325.392752 751.043732,325.148955 749.898926,324.661352 C748.754121,324.17375 747.76833,323.468857 746.941526,322.546653 C746.114722,321.624448 745.468129,320.490259 745.001726,319.144053 C744.535324,317.797846 744.302126,316.276761 744.302126,314.580753 Z M765.131125,304.150354 L777.151525,304.150354 L777.151525,306.376353 L767.770525,306.376353 L767.770525,312.927153 L775.688725,312.927153 L775.688725,315.184953 L767.770525,315.184953 L767.770525,322.753353 L777.469525,322.753353 L777.469525,325.011152 L765.131125,325.011152 L765.131125,304.150354 Z" id="MERGE" fill="#E04733"></path>
+ <path d="M189.429182,500.508554 L192.927182,500.508554 C194.55959,500.508554 195.810377,500.174658 196.679581,499.506855 C197.548786,498.839051 197.983381,497.826761 197.983381,496.469955 C197.983381,495.091948 197.548786,494.127358 196.679581,493.576155 C195.810377,493.024952 194.55959,492.749355 192.927182,492.749355 L189.429182,492.749355 L189.429182,500.508554 Z M198.237781,511.479554 L193.213382,502.670954 L189.429182,502.670954 L189.429182,511.479554 L186.789782,511.479554 L186.789782,490.618755 L193.308782,490.618755 C194.368787,490.618755 195.349277,490.719454 196.250281,490.920855 C197.151286,491.122256 197.925078,491.456153 198.571681,491.922555 C199.218284,492.388957 199.721779,492.993151 200.082181,493.735155 C200.442583,494.477159 200.622781,495.388749 200.622781,496.469955 C200.622781,498.102363 200.198785,499.40615 199.350781,500.381355 C198.502777,501.356559 197.368588,502.024353 195.948181,502.384754 L201.226981,511.479554 L198.237781,511.479554 Z M205.265581,490.618755 L217.28598,490.618755 L217.28598,492.844755 L207.904981,492.844755 L207.904981,499.395555 L215.82318,499.395555 L215.82318,501.653354 L207.904981,501.653354 L207.904981,509.221754 L217.60398,509.221754 L217.60398,511.479554 L205.265581,511.479554 L205.265581,490.618755 Z M219.16218,490.618755 L221.99238,490.618755 L225.33138,501.875954 C225.712982,503.126761 226.046878,504.282149 226.33308,505.342154 C226.619281,506.402159 226.963778,507.546948 227.36658,508.776554 L227.49378,508.776554 C227.875382,507.546948 228.214578,506.402159 228.51138,505.342154 C228.808181,504.282149 229.136778,503.126761 229.49718,501.875954 L232.836179,490.618755 L235.539179,490.618755 L228.92478,511.479554 L225.84018,511.479554 L219.16218,490.618755 Z M238.401179,490.618755 L241.040579,490.618755 L241.040579,511.479554 L238.401179,511.479554 L238.401179,490.618755 Z M246.764579,490.618755 L258.784978,490.618755 L258.784978,492.844755 L249.403978,492.844755 L249.403978,499.395555 L257.322178,499.395555 L257.322178,501.653354 L249.403978,501.653354 L249.403978,509.221754 L259.102978,509.221754 L259.102978,511.479554 L246.764579,511.479554 L246.764579,490.618755 Z M261.392578,490.618755 L264.127378,490.618755 L266.321578,501.971354 C266.512379,503.11616 266.713776,504.239749 266.925778,505.342154 C267.137779,506.44456 267.339177,507.568148 267.529977,508.712954 L267.657177,508.712954 C267.890379,507.568148 268.134176,506.43926 268.388577,505.326254 C268.642979,504.213249 268.886776,503.09496 269.119977,501.971354 L272.013777,490.618755 L274.430577,490.618755 L277.324377,501.971354 C277.578778,503.07376 277.833176,504.186749 278.087577,505.310354 C278.341978,506.43396 278.596376,507.568148 278.850777,508.712954 L278.977977,508.712954 C279.168778,507.568148 279.359576,506.43926 279.550377,505.326254 C279.741178,504.213249 279.942576,503.09496 280.154577,501.971354 L282.348777,490.618755 L284.892777,490.618755 L280.567977,511.479554 L277.387977,511.479554 L274.239777,498.918555 C274.048976,498.112951 273.874078,497.323259 273.715077,496.549455 C273.556076,495.775651 273.391778,494.985959 273.222177,494.180355 L273.094977,494.180355 C272.925376,494.985959 272.750478,495.775651 272.570277,496.549455 C272.390076,497.323259 272.215178,498.112951 272.045577,498.918555 L268.960977,511.479554 L265.812778,511.479554 L261.392578,490.618755 Z M303.813776,502.988954 L302.827976,499.808955 C302.446374,498.642949 302.085977,497.48226 301.746776,496.326855 C301.407574,495.171449 301.068377,493.989561 300.729176,492.781155 L300.601976,492.781155 C300.283974,493.989561 299.955377,495.171449 299.616176,496.326855 C299.276974,497.48226 298.916578,498.642949 298.534976,499.808955 L297.549176,502.988954 L303.813776,502.988954 Z M304.481576,505.119554 L296.881376,505.119554 L294.877976,511.479554 L292.174976,511.479554 L299.234576,490.618755 L302.223776,490.618755 L309.283375,511.479554 L306.453175,511.479554 L304.481576,505.119554 Z M312.240775,490.618755 L318.632575,490.618755 C319.77738,490.618755 320.82677,490.724754 321.780775,490.936755 C322.734779,491.148756 323.545671,491.493253 324.213474,491.970255 C324.881278,492.447257 325.400673,493.077951 325.771674,493.862355 C326.142676,494.646759 326.328174,495.611349 326.328174,496.756155 C326.328174,497.85856 326.142676,498.812551 325.771674,499.618155 C325.400673,500.423759 324.875978,501.091552 324.197574,501.621554 C323.519171,502.151557 322.708279,502.549053 321.764875,502.814054 C320.82147,503.079056 319.77738,503.211554 318.632575,503.211554 L314.880175,503.211554 L314.880175,511.479554 L312.240775,511.479554 L312.240775,490.618755 Z M318.314575,501.049154 C320.137784,501.049154 321.48927,500.704658 322.369075,500.015655 C323.248879,499.326651 323.688774,498.240162 323.688774,496.756155 C323.688774,495.250947 323.243579,494.206858 322.353175,493.623855 C321.46277,493.040852 320.116584,492.749355 318.314575,492.749355 L314.880175,492.749355 L314.880175,501.049154 L318.314575,501.049154 Z M330.557574,490.618755 L336.949374,490.618755 C338.094179,490.618755 339.143569,490.724754 340.097574,490.936755 C341.051578,491.148756 341.86247,491.493253 342.530273,491.970255 C343.198077,492.447257 343.717472,493.077951 344.088473,493.862355 C344.459475,494.646759 344.644973,495.611349 344.644973,496.756155 C344.644973,497.85856 344.459475,498.812551 344.088473,499.618155 C343.717472,500.423759 343.192777,501.091552 342.514373,501.621554 C341.83597,502.151557 341.025078,502.549053 340.081674,502.814054 C339.138269,503.079056 338.094179,503.211554 336.949374,503.211554 L333.196974,503.211554 L333.196974,511.479554 L330.557574,511.479554 L330.557574,490.618755 Z M336.631374,501.049154 C338.454583,501.049154 339.806069,500.704658 340.685874,500.015655 C341.565678,499.326651 342.005573,498.240162 342.005573,496.756155 C342.005573,495.250947 341.560378,494.206858 340.669974,493.623855 C339.779569,493.040852 338.433383,492.749355 336.631374,492.749355 L333.196974,492.749355 L333.196974,501.049154 L336.631374,501.049154 Z" id="REVIEW-APP" fill="#E04733"></path>
+ <path d="M281.839315,443.148434 C281.839315,441.89441 281.395681,440.823773 280.5084,439.936492 C279.621119,439.049211 278.550482,438.605577 277.296458,438.605577 C276.042434,438.605577 274.971797,439.049211 274.084516,439.936492 C273.197235,440.823773 272.753601,441.89441 272.753601,443.148434 C272.753601,444.402458 273.197235,445.473095 274.084516,446.360376 C274.971797,447.247657 276.042434,447.691291 277.296458,447.691291 C278.550482,447.691291 279.621119,447.247657 280.5084,446.360376 C281.395681,445.473095 281.839315,444.402458 281.839315,443.148434 Z M295.467886,452.234148 C295.467886,451.618966 295.243111,451.086605 294.793555,450.63705 C294.343999,450.187494 293.811639,449.962719 293.196457,449.962719 C292.581275,449.962719 292.048915,450.187494 291.599359,450.63705 C291.149803,451.086605 290.925029,451.618966 290.925029,452.234148 C290.925029,452.86116 291.146846,453.396478 291.590486,453.840119 C292.034127,454.283759 292.569445,454.505576 293.196457,454.505576 C293.823469,454.505576 294.358787,454.283759 294.802428,453.840119 C295.246069,453.396478 295.467886,452.86116 295.467886,452.234148 Z M295.467886,434.06272 C295.467886,433.447539 295.243111,432.915178 294.793555,432.465622 C294.343999,432.016066 293.811639,431.791292 293.196457,431.791292 C292.581275,431.791292 292.048915,432.016066 291.599359,432.465622 C291.149803,432.915178 290.925029,433.447539 290.925029,434.06272 C290.925029,434.689732 291.146846,435.22505 291.590486,435.668691 C292.034127,436.112332 292.569445,436.334149 293.196457,436.334149 C293.823469,436.334149 294.358787,436.112332 294.802428,435.668691 C295.246069,435.22505 295.467886,434.689732 295.467886,434.06272 Z M288.6536,441.53359 L288.6536,444.816514 C288.6536,444.934818 288.612194,445.050163 288.529381,445.162552 C288.446569,445.274941 288.351927,445.33705 288.245453,445.34888 L285.494895,445.774773 C285.36476,446.188838 285.175477,446.638387 284.927038,447.123434 C285.329272,447.691294 285.861633,448.371532 286.524136,449.16417 C286.606949,449.282474 286.648355,449.400777 286.648355,449.519081 C286.648355,449.661046 286.606949,449.773433 286.524136,449.856246 C286.252036,450.211159 285.764039,450.740562 285.060129,451.444471 C284.35622,452.148381 283.891883,452.500331 283.667105,452.500331 C283.53697,452.500331 283.412753,452.458925 283.294449,452.376112 L281.253712,450.779014 C280.815987,451.003792 280.360523,451.18716 279.887306,451.329125 C279.757171,452.60681 279.621124,453.523654 279.479159,454.079683 C279.396346,454.363613 279.218892,454.505576 278.946793,454.505576 L275.646123,454.505576 C275.515989,454.505576 275.397686,454.461213 275.291212,454.372485 C275.184739,454.283757 275.125588,454.180242 275.113757,454.061938 L274.70561,451.346871 C274.303376,451.228567 273.859742,451.045198 273.374695,450.796759 L271.280722,452.376112 C271.197909,452.458925 271.079606,452.500331 270.925811,452.500331 C270.795676,452.500331 270.671459,452.45301 270.553155,452.358367 C268.849575,450.784921 267.997798,449.838502 267.997798,449.519081 C267.997798,449.412607 268.039204,449.30022 268.122016,449.181916 C268.240321,449.01629 268.48284,448.702789 268.849583,448.241402 C269.216326,447.780016 269.494337,447.419194 269.683624,447.158925 C269.411524,446.638387 269.204495,446.153347 269.06253,445.703791 L266.365209,445.277898 C266.246904,445.266068 266.146347,445.209874 266.063534,445.109316 C265.980722,445.008757 265.939316,444.893412 265.939316,444.763278 L265.939316,441.480354 C265.939316,441.36205 265.980722,441.246705 266.063534,441.134316 C266.146347,441.021927 266.240989,440.959818 266.347463,440.947988 L269.098021,440.522095 C269.228155,440.10803 269.417439,439.658481 269.665878,439.173434 C269.263644,438.605574 268.731283,437.925336 268.06878,437.132698 C267.985967,437.002563 267.944561,436.884261 267.944561,436.777787 C267.944561,436.635822 267.985967,436.51752 268.06878,436.422876 C268.329049,436.067964 268.814089,435.541518 269.523914,434.843524 C270.233739,434.145529 270.701033,433.796537 270.925811,433.796537 C271.055946,433.796537 271.180163,433.837943 271.298467,433.920756 L273.339204,435.517854 C273.741438,435.304907 274.196902,435.115623 274.70561,434.949997 C274.835744,433.672312 274.971792,432.761384 275.113757,432.217185 C275.19657,431.933255 275.374024,431.791292 275.646123,431.791292 L278.946793,431.791292 C279.076927,431.791292 279.19523,431.835655 279.301703,431.924383 C279.408177,432.013111 279.467328,432.116626 279.479159,432.23493 L279.887306,434.949997 C280.28954,435.068301 280.733174,435.25167 281.218221,435.500108 L283.312194,433.920756 C283.406838,433.837943 283.52514,433.796537 283.667105,433.796537 C283.797239,433.796537 283.921457,433.843858 284.039761,433.938501 C285.743341,435.511947 286.595118,436.458366 286.595118,436.777787 C286.595118,436.884261 286.553712,436.996648 286.470899,437.114952 C286.328934,437.304239 286.080499,437.623655 285.725587,438.073211 C285.370674,438.522767 285.104494,438.877674 284.927038,439.137943 C285.199137,439.705803 285.400251,440.190843 285.530386,440.593077 L288.227707,441.001224 C288.346011,441.024885 288.446569,441.086994 288.529381,441.187552 C288.612194,441.288111 288.6536,441.403456 288.6536,441.53359 Z M300.010742,450.99196 L300.010742,453.476335 C300.010742,453.665622 299.12939,453.848991 297.366658,454.026447 C297.224693,454.345868 297.047239,454.653454 296.834292,454.949215 C297.437643,456.286052 297.739314,457.102338 297.739314,457.398098 C297.739314,457.44542 297.715653,457.486826 297.668332,457.522317 C296.225021,458.362277 295.491546,458.78225 295.467886,458.78225 C295.373242,458.78225 295.101147,458.504239 294.651591,457.94821 C294.202035,457.39218 293.894449,456.989952 293.728823,456.741514 C293.492215,456.765174 293.314761,456.777005 293.196457,456.777005 C293.078153,456.777005 292.900699,456.765174 292.664091,456.741514 C292.498465,456.989952 292.190879,457.39218 291.741323,457.94821 C291.291767,458.504239 291.019672,458.78225 290.925029,458.78225 C290.901368,458.78225 290.167893,458.362277 288.724582,457.522317 C288.677261,457.486826 288.6536,457.44542 288.6536,457.398098 C288.6536,457.102338 288.955271,456.286052 289.558622,454.949215 C289.345675,454.653454 289.168221,454.345868 289.026256,454.026447 C287.263524,453.848991 286.382172,453.665622 286.382172,453.476335 L286.382172,450.99196 C286.382172,450.802674 287.263524,450.619305 289.026256,450.441849 C289.180052,450.098767 289.357505,449.791181 289.558622,449.519081 C288.955271,448.182244 288.6536,447.365958 288.6536,447.070197 C288.6536,447.022875 288.677261,446.98147 288.724582,446.945978 C288.771904,446.922318 288.978933,446.804015 289.345676,446.591068 C289.712419,446.37812 290.061411,446.177006 290.392663,445.98772 C290.723914,445.798433 290.901368,445.703791 290.925029,445.703791 C291.019672,445.703791 291.291767,445.978844 291.741323,446.528958 C292.190879,447.079073 292.498465,447.478343 292.664091,447.726782 C292.900699,447.703121 293.078153,447.691291 293.196457,447.691291 C293.314761,447.691291 293.492215,447.703121 293.728823,447.726782 C294.332174,446.886822 294.876365,446.224329 295.361412,445.739282 L295.467886,445.703791 C295.515207,445.703791 296.248682,446.117849 297.668332,446.945978 C297.715653,446.98147 297.739314,447.022875 297.739314,447.070197 C297.739314,447.365958 297.437643,448.182244 296.834292,449.519081 C297.035409,449.791181 297.212862,450.098767 297.366658,450.441849 C299.12939,450.619305 300.010742,450.802674 300.010742,450.99196 Z M300.010742,432.820533 L300.010742,435.304908 C300.010742,435.494194 299.12939,435.677563 297.366658,435.855019 C297.224693,436.17444 297.047239,436.482027 296.834292,436.777787 C297.437643,438.114624 297.739314,438.93091 297.739314,439.226671 C297.739314,439.273992 297.715653,439.315398 297.668332,439.35089 C296.225021,440.190849 295.491546,440.610822 295.467886,440.610822 C295.373242,440.610822 295.101147,440.332812 294.651591,439.776782 C294.202035,439.220753 293.894449,438.818525 293.728823,438.570086 C293.492215,438.593747 293.314761,438.605577 293.196457,438.605577 C293.078153,438.605577 292.900699,438.593747 292.664091,438.570086 C292.498465,438.818525 292.190879,439.220753 291.741323,439.776782 C291.291767,440.332812 291.019672,440.610822 290.925029,440.610822 C290.901368,440.610822 290.167893,440.190849 288.724582,439.35089 C288.677261,439.315398 288.6536,439.273992 288.6536,439.226671 C288.6536,438.93091 288.955271,438.114624 289.558622,436.777787 C289.345675,436.482027 289.168221,436.17444 289.026256,435.855019 C287.263524,435.677563 286.382172,435.494194 286.382172,435.304908 L286.382172,432.820533 C286.382172,432.631246 287.263524,432.447877 289.026256,432.270421 C289.180052,431.927339 289.357505,431.619753 289.558622,431.347653 C288.955271,430.010816 288.6536,429.19453 288.6536,428.89877 C288.6536,428.851448 288.677261,428.810042 288.724582,428.774551 C288.771904,428.75089 288.978933,428.632588 289.345676,428.41964 C289.712419,428.206693 290.061411,428.005579 290.392663,427.816292 C290.723914,427.627005 290.901368,427.532363 290.925029,427.532363 C291.019672,427.532363 291.291767,427.807416 291.741323,428.357531 C292.190879,428.907645 292.498465,429.306916 292.664091,429.555354 C292.900699,429.531694 293.078153,429.519863 293.196457,429.519863 C293.314761,429.519863 293.492215,429.531694 293.728823,429.555354 C294.332174,428.715395 294.876365,428.052901 295.361412,427.567854 L295.467886,427.532363 C295.515207,427.532363 296.248682,427.946422 297.668332,428.774551 C297.715653,428.810042 297.739314,428.851448 297.739314,428.89877 C297.739314,429.19453 297.437643,430.010816 296.834292,431.347653 C297.035409,431.619753 297.212862,431.927339 297.366658,432.270421 C299.12939,432.447877 300.010742,432.631246 300.010742,432.820533 Z" id="ï‚…" fill="#E04733"></path>
+ <path d="M518.847717,443.148434 C518.847717,441.89441 518.404083,440.823773 517.516802,439.936492 C516.629521,439.049211 515.558884,438.605577 514.30486,438.605577 C513.050836,438.605577 511.9802,439.049211 511.092919,439.936492 C510.205637,440.823773 509.762003,441.89441 509.762003,443.148434 C509.762003,444.402458 510.205637,445.473095 511.092919,446.360376 C511.9802,447.247657 513.050836,447.691291 514.30486,447.691291 C515.558884,447.691291 516.629521,447.247657 517.516802,446.360376 C518.404083,445.473095 518.847717,444.402458 518.847717,443.148434 Z M532.476288,452.234148 C532.476288,451.618966 532.251513,451.086605 531.801958,450.63705 C531.352402,450.187494 530.820041,449.962719 530.20486,449.962719 C529.589678,449.962719 529.057317,450.187494 528.607761,450.63705 C528.158206,451.086605 527.933431,451.618966 527.933431,452.234148 C527.933431,452.86116 528.155248,453.396478 528.598889,453.840119 C529.042529,454.283759 529.577847,454.505576 530.20486,454.505576 C530.831872,454.505576 531.36719,454.283759 531.81083,453.840119 C532.254471,453.396478 532.476288,452.86116 532.476288,452.234148 Z M532.476288,434.06272 C532.476288,433.447539 532.251513,432.915178 531.801958,432.465622 C531.352402,432.016066 530.820041,431.791292 530.20486,431.791292 C529.589678,431.791292 529.057317,432.016066 528.607761,432.465622 C528.158206,432.915178 527.933431,433.447539 527.933431,434.06272 C527.933431,434.689732 528.155248,435.22505 528.598889,435.668691 C529.042529,436.112332 529.577847,436.334149 530.20486,436.334149 C530.831872,436.334149 531.36719,436.112332 531.81083,435.668691 C532.254471,435.22505 532.476288,434.689732 532.476288,434.06272 Z M525.662003,441.53359 L525.662003,444.816514 C525.662003,444.934818 525.620597,445.050163 525.537784,445.162552 C525.454971,445.274941 525.360329,445.33705 525.253855,445.34888 L522.503297,445.774773 C522.373163,446.188838 522.183879,446.638387 521.93544,447.123434 C522.337674,447.691294 522.870035,448.371532 523.532538,449.16417 C523.615351,449.282474 523.656757,449.400777 523.656757,449.519081 C523.656757,449.661046 523.615351,449.773433 523.532538,449.856246 C523.260439,450.211159 522.772442,450.740562 522.068532,451.444471 C521.364622,452.148381 520.900285,452.500331 520.675507,452.500331 C520.545373,452.500331 520.421155,452.458925 520.302851,452.376112 L518.262115,450.779014 C517.824389,451.003792 517.368925,451.18716 516.895708,451.329125 C516.765574,452.60681 516.629526,453.523654 516.487561,454.079683 C516.404748,454.363613 516.227295,454.505576 515.955195,454.505576 L512.654526,454.505576 C512.524391,454.505576 512.406089,454.461213 512.299615,454.372485 C512.193141,454.283757 512.13399,454.180242 512.12216,454.061938 L511.714012,451.346871 C511.311778,451.228567 510.868144,451.045198 510.383097,450.796759 L508.289124,452.376112 C508.206311,452.458925 508.088009,452.500331 507.934213,452.500331 C507.804079,452.500331 507.679861,452.45301 507.561557,452.358367 C505.857977,450.784921 505.0062,449.838502 505.0062,449.519081 C505.0062,449.412607 505.047606,449.30022 505.130419,449.181916 C505.248723,449.01629 505.491243,448.702789 505.857986,448.241402 C506.224729,447.780016 506.502739,447.419194 506.692026,447.158925 C506.419926,446.638387 506.212897,446.153347 506.070932,445.703791 L503.373611,445.277898 C503.255307,445.266068 503.15475,445.209874 503.071937,445.109316 C502.989124,445.008757 502.947718,444.893412 502.947718,444.763278 L502.947718,441.480354 C502.947718,441.36205 502.989124,441.246705 503.071937,441.134316 C503.15475,441.021927 503.249392,440.959818 503.355865,440.947988 L506.106423,440.522095 C506.236558,440.10803 506.425842,439.658481 506.67428,439.173434 C506.272046,438.605574 505.739686,437.925336 505.077182,437.132698 C504.994369,437.002563 504.952964,436.884261 504.952964,436.777787 C504.952964,436.635822 504.994369,436.51752 505.077182,436.422876 C505.337451,436.067964 505.822491,435.541518 506.532316,434.843524 C507.242141,434.145529 507.709436,433.796537 507.934213,433.796537 C508.064348,433.796537 508.188565,433.837943 508.30687,433.920756 L510.347606,435.517854 C510.74984,435.304907 511.205304,435.115623 511.714012,434.949997 C511.844147,433.672312 511.980195,432.761384 512.12216,432.217185 C512.204973,431.933255 512.382426,431.791292 512.654526,431.791292 L515.955195,431.791292 C516.08533,431.791292 516.203632,431.835655 516.310106,431.924383 C516.41658,432.013111 516.475731,432.116626 516.487561,432.23493 L516.895708,434.949997 C517.297943,435.068301 517.741577,435.25167 518.226624,435.500108 L520.320597,433.920756 C520.41524,433.837943 520.533542,433.796537 520.675507,433.796537 C520.805642,433.796537 520.929859,433.843858 521.048164,433.938501 C522.751743,435.511947 523.603521,436.458366 523.603521,436.777787 C523.603521,436.884261 523.562115,436.996648 523.479302,437.114952 C523.337337,437.304239 523.088902,437.623655 522.733989,438.073211 C522.379077,438.522767 522.112897,438.877674 521.93544,439.137943 C522.20754,439.705803 522.408654,440.190843 522.538789,440.593077 L525.23611,441.001224 C525.354414,441.024885 525.454971,441.086994 525.537784,441.187552 C525.620597,441.288111 525.662003,441.403456 525.662003,441.53359 Z M537.019145,450.99196 L537.019145,453.476335 C537.019145,453.665622 536.137792,453.848991 534.37506,454.026447 C534.233095,454.345868 534.055642,454.653454 533.842694,454.949215 C534.446045,456.286052 534.747716,457.102338 534.747716,457.398098 C534.747716,457.44542 534.724056,457.486826 534.676734,457.522317 C533.233424,458.362277 532.499949,458.78225 532.476288,458.78225 C532.381645,458.78225 532.109549,458.504239 531.659993,457.94821 C531.210438,457.39218 530.902851,456.989952 530.737226,456.741514 C530.500617,456.765174 530.323164,456.777005 530.20486,456.777005 C530.086555,456.777005 529.909102,456.765174 529.672493,456.741514 C529.506868,456.989952 529.199281,457.39218 528.749726,457.94821 C528.30017,458.504239 528.028074,458.78225 527.933431,458.78225 C527.90977,458.78225 527.176295,458.362277 525.732985,457.522317 C525.685663,457.486826 525.662003,457.44542 525.662003,457.398098 C525.662003,457.102338 525.963674,456.286052 526.567025,454.949215 C526.354077,454.653454 526.176624,454.345868 526.034659,454.026447 C524.271927,453.848991 523.390574,453.665622 523.390574,453.476335 L523.390574,450.99196 C523.390574,450.802674 524.271927,450.619305 526.034659,450.441849 C526.188454,450.098767 526.365908,449.791181 526.567025,449.519081 C525.963674,448.182244 525.662003,447.365958 525.662003,447.070197 C525.662003,447.022875 525.685663,446.98147 525.732985,446.945978 C525.780306,446.922318 525.987336,446.804015 526.354078,446.591068 C526.720821,446.37812 527.069813,446.177006 527.401065,445.98772 C527.732317,445.798433 527.90977,445.703791 527.933431,445.703791 C528.028074,445.703791 528.30017,445.978844 528.749726,446.528958 C529.199281,447.079073 529.506868,447.478343 529.672493,447.726782 C529.909102,447.703121 530.086555,447.691291 530.20486,447.691291 C530.323164,447.691291 530.500617,447.703121 530.737226,447.726782 C531.340577,446.886822 531.884768,446.224329 532.369815,445.739282 L532.476288,445.703791 C532.52361,445.703791 533.257084,446.117849 534.676734,446.945978 C534.724056,446.98147 534.747716,447.022875 534.747716,447.070197 C534.747716,447.365958 534.446045,448.182244 533.842694,449.519081 C534.043811,449.791181 534.221265,450.098767 534.37506,450.441849 C536.137792,450.619305 537.019145,450.802674 537.019145,450.99196 Z M537.019145,432.820533 L537.019145,435.304908 C537.019145,435.494194 536.137792,435.677563 534.37506,435.855019 C534.233095,436.17444 534.055642,436.482027 533.842694,436.777787 C534.446045,438.114624 534.747716,438.93091 534.747716,439.226671 C534.747716,439.273992 534.724056,439.315398 534.676734,439.35089 C533.233424,440.190849 532.499949,440.610822 532.476288,440.610822 C532.381645,440.610822 532.109549,440.332812 531.659993,439.776782 C531.210438,439.220753 530.902851,438.818525 530.737226,438.570086 C530.500617,438.593747 530.323164,438.605577 530.20486,438.605577 C530.086555,438.605577 529.909102,438.593747 529.672493,438.570086 C529.506868,438.818525 529.199281,439.220753 528.749726,439.776782 C528.30017,440.332812 528.028074,440.610822 527.933431,440.610822 C527.90977,440.610822 527.176295,440.190849 525.732985,439.35089 C525.685663,439.315398 525.662003,439.273992 525.662003,439.226671 C525.662003,438.93091 525.963674,438.114624 526.567025,436.777787 C526.354077,436.482027 526.176624,436.17444 526.034659,435.855019 C524.271927,435.677563 523.390574,435.494194 523.390574,435.304908 L523.390574,432.820533 C523.390574,432.631246 524.271927,432.447877 526.034659,432.270421 C526.188454,431.927339 526.365908,431.619753 526.567025,431.347653 C525.963674,430.010816 525.662003,429.19453 525.662003,428.89877 C525.662003,428.851448 525.685663,428.810042 525.732985,428.774551 C525.780306,428.75089 525.987336,428.632588 526.354078,428.41964 C526.720821,428.206693 527.069813,428.005579 527.401065,427.816292 C527.732317,427.627005 527.90977,427.532363 527.933431,427.532363 C528.028074,427.532363 528.30017,427.807416 528.749726,428.357531 C529.199281,428.907645 529.506868,429.306916 529.672493,429.555354 C529.909102,429.531694 530.086555,429.519863 530.20486,429.519863 C530.323164,429.519863 530.500617,429.531694 530.737226,429.555354 C531.340577,428.715395 531.884768,428.052901 532.369815,427.567854 L532.476288,427.532363 C532.52361,427.532363 533.257084,427.946422 534.676734,428.774551 C534.724056,428.810042 534.747716,428.851448 534.747716,428.89877 C534.747716,429.19453 534.446045,430.010816 533.842694,431.347653 C534.043811,431.619753 534.221265,431.927339 534.37506,432.270421 C536.137792,432.447877 537.019145,432.631246 537.019145,432.820533 Z" id="ï‚…-copy" fill="#E04733"></path>
+ <path d="M859.734507,544.858471 C857.81798,544.917623 856.250474,545.674758 855.031941,547.1299 L852.654039,547.1299 C851.683945,547.1299 850.867659,546.890337 850.205155,546.411205 C849.542652,545.932074 849.211405,545.231132 849.211405,544.30836 C849.211405,540.132223 849.94488,538.044186 851.411852,538.044186 C851.482834,538.044186 851.740142,538.168403 852.183782,538.416842 C852.627423,538.665281 853.204147,538.916673 853.913972,539.171027 C854.623797,539.425381 855.327696,539.552556 856.025691,539.552556 C856.818329,539.552556 857.605039,539.416509 858.385847,539.144409 C858.326695,539.582134 858.297119,539.972532 858.297119,540.315614 C858.297119,541.960042 858.776244,543.474313 859.734507,544.858471 Z M878.739975,556.162377 C878.739975,557.582027 878.308171,558.702942 877.444551,559.525156 C876.580931,560.34737 875.433398,560.75847 874.001917,560.75847 L858.49232,560.75847 C857.06084,560.75847 855.913307,560.34737 855.049686,559.525156 C854.186066,558.702942 853.754262,557.582027 853.754262,556.162377 C853.754262,555.535365 853.774965,554.92315 853.816372,554.325714 C853.857778,553.728278 853.94059,553.08353 854.064809,552.391451 C854.189028,551.699371 854.345779,551.057581 854.535066,550.46606 C854.724352,549.874539 854.978703,549.297815 855.298124,548.73587 C855.617545,548.173926 855.984282,547.694801 856.398347,547.298482 C856.812411,546.902163 857.318154,546.585704 857.91559,546.349096 C858.513026,546.112488 859.172562,545.994185 859.894217,545.994185 C860.012521,545.994185 860.266872,546.12136 860.657275,546.375714 C861.047679,546.630068 861.479483,546.913994 861.952699,547.2275 C862.425916,547.541006 863.058834,547.824932 863.851471,548.079286 C864.644109,548.33364 865.44265,548.460815 866.247119,548.460815 C867.051587,548.460815 867.850128,548.33364 868.642766,548.079286 C869.435404,547.824932 870.068321,547.541006 870.541538,547.2275 C871.014755,546.913994 871.446558,546.630068 871.836962,546.375714 C872.227366,546.12136 872.481716,545.994185 872.60002,545.994185 C873.321675,545.994185 873.981211,546.112488 874.578647,546.349096 C875.176083,546.585704 875.681826,546.902163 876.09589,547.298482 C876.509955,547.694801 876.876692,548.173926 877.196114,548.73587 C877.515535,549.297815 877.769885,549.874539 877.959172,550.46606 C878.148458,551.057581 878.305209,551.699371 878.429428,552.391451 C878.553648,553.08353 878.636459,553.728278 878.677866,554.325714 C878.719272,554.92315 878.739975,555.535365 878.739975,556.162377 Z M860.568548,533.501329 C860.568548,534.755353 860.124914,535.82599 859.237632,536.713271 C858.350351,537.600552 857.279715,538.044186 856.025691,538.044186 C854.771667,538.044186 853.70103,537.600552 852.813749,536.713271 C851.926468,535.82599 851.482834,534.755353 851.482834,533.501329 C851.482834,532.247305 851.926468,531.176668 852.813749,530.289387 C853.70103,529.402106 854.771667,528.958472 856.025691,528.958472 C857.279715,528.958472 858.350351,529.402106 859.237632,530.289387 C860.124914,531.176668 860.568548,532.247305 860.568548,533.501329 Z M873.061404,540.315614 C873.061404,542.19665 872.395953,543.802605 871.065031,545.133527 C869.73411,546.464449 868.128155,547.1299 866.247119,547.1299 C864.366083,547.1299 862.760128,546.464449 861.429206,545.133527 C860.098284,543.802605 859.432833,542.19665 859.432833,540.315614 C859.432833,538.434578 860.098284,536.828623 861.429206,535.497702 C862.760128,534.16678 864.366083,533.501329 866.247119,533.501329 C868.128155,533.501329 869.73411,534.16678 871.065031,535.497702 C872.395953,536.828623 873.061404,538.434578 873.061404,540.315614 Z M883.282832,544.30836 C883.282832,545.231132 882.951585,545.932074 882.289082,546.411205 C881.626579,546.890337 880.810292,547.1299 879.840198,547.1299 L877.462297,547.1299 C876.243764,545.674758 874.676257,544.917623 872.75973,544.858471 C873.717994,543.474313 874.197118,541.960042 874.197118,540.315614 C874.197118,539.972532 874.167543,539.582134 874.108391,539.144409 C874.889198,539.416509 875.675909,539.552556 876.468547,539.552556 C877.166541,539.552556 877.87044,539.425381 878.580265,539.171027 C879.29009,538.916673 879.866814,538.665281 880.310455,538.416842 C880.754096,538.168403 881.011403,538.044186 881.082386,538.044186 C882.549357,538.044186 883.282832,540.132223 883.282832,544.30836 Z M881.011404,533.501329 C881.011404,534.755353 880.56777,535.82599 879.680489,536.713271 C878.793207,537.600552 877.722571,538.044186 876.468547,538.044186 C875.214523,538.044186 874.143886,537.600552 873.256605,536.713271 C872.369324,535.82599 871.92569,534.755353 871.92569,533.501329 C871.92569,532.247305 872.369324,531.176668 873.256605,530.289387 C874.143886,529.402106 875.214523,528.958472 876.468547,528.958472 C877.722571,528.958472 878.793207,529.402106 879.680489,530.289387 C880.56777,531.176668 881.011404,532.247305 881.011404,533.501329 Z M920.88562,539.002445 C920.88562,539.475661 920.719997,539.877889 920.388745,540.209141 L905.127585,555.470301 C904.796334,555.801553 904.394106,555.967176 903.920889,555.967176 C903.447672,555.967176 903.045444,555.801553 902.714193,555.470301 L893.876916,546.633025 C893.545665,546.301773 893.380041,545.899545 893.380041,545.426328 C893.380041,544.953112 893.545665,544.550884 893.876916,544.219632 L896.290309,541.806239 C896.621561,541.474988 897.023789,541.309364 897.497005,541.309364 C897.970222,541.309364 898.37245,541.474988 898.703702,541.806239 L903.920889,547.041172 L915.56196,535.382356 C915.893211,535.051104 916.29544,534.885481 916.768656,534.885481 C917.241873,534.885481 917.644101,535.051104 917.975353,535.382356 L920.388745,537.795748 C920.719997,538.127 920.88562,538.529228 920.88562,539.002445 Z" id="--copy" fill="#E04733"></path>
+ <path d="M1045.7411,135.899363 C1043.82457,135.958515 1042.25707,136.715651 1041.03854,138.170792 L1038.66063,138.170792 C1037.69054,138.170792 1036.87425,137.931229 1036.21175,137.452098 C1035.54925,136.972966 1035.218,136.272024 1035.218,135.349252 C1035.218,131.173115 1035.95147,129.085078 1037.41845,129.085078 C1037.48943,129.085078 1037.74674,129.209295 1038.19038,129.457734 C1038.63402,129.706173 1039.21074,129.957566 1039.92057,130.211919 C1040.63039,130.466273 1041.33429,130.593448 1042.03229,130.593448 C1042.82492,130.593448 1043.61163,130.457401 1044.39244,130.185301 C1044.33329,130.623027 1044.30371,131.013424 1044.30371,131.356506 C1044.30371,133.000934 1044.78284,134.515205 1045.7411,135.899363 Z M1064.74657,147.203269 C1064.74657,148.622919 1064.31477,149.743834 1063.45115,150.566048 C1062.58753,151.388262 1061.43999,151.799362 1060.00851,151.799362 L1044.49891,151.799362 C1043.06743,151.799362 1041.9199,151.388262 1041.05628,150.566048 C1040.19266,149.743834 1039.76086,148.622919 1039.76086,147.203269 C1039.76086,146.576257 1039.78156,145.964042 1039.82297,145.366606 C1039.86437,144.76917 1039.94718,144.124422 1040.0714,143.432343 C1040.19562,142.740264 1040.35237,142.098473 1040.54166,141.506952 C1040.73095,140.915432 1040.9853,140.338707 1041.30472,139.776763 C1041.62414,139.214818 1041.99088,138.735693 1042.40494,138.339374 C1042.81901,137.943055 1043.32475,137.626597 1043.92218,137.389988 C1044.51962,137.15338 1045.17916,137.035078 1045.90081,137.035078 C1046.01912,137.035078 1046.27347,137.162253 1046.66387,137.416607 C1047.05427,137.67096 1047.48608,137.954886 1047.95929,138.268392 C1048.43251,138.581898 1049.06543,138.865824 1049.85807,139.120178 C1050.6507,139.374532 1051.44924,139.501707 1052.25371,139.501707 C1053.05818,139.501707 1053.85672,139.374532 1054.64936,139.120178 C1055.442,138.865824 1056.07492,138.581898 1056.54813,138.268392 C1057.02135,137.954886 1057.45315,137.67096 1057.84356,137.416607 C1058.23396,137.162253 1058.48831,137.035078 1058.60661,137.035078 C1059.32827,137.035078 1059.98781,137.15338 1060.58524,137.389988 C1061.18268,137.626597 1061.68842,137.943055 1062.10248,138.339374 C1062.51655,138.735693 1062.88329,139.214818 1063.20271,139.776763 C1063.52213,140.338707 1063.77648,140.915432 1063.96577,141.506952 C1064.15505,142.098473 1064.3118,142.740264 1064.43602,143.432343 C1064.56024,144.124422 1064.64305,144.76917 1064.68446,145.366606 C1064.72587,145.964042 1064.74657,146.576257 1064.74657,147.203269 Z M1046.57514,124.542221 C1046.57514,125.796245 1046.13151,126.866882 1045.24423,127.754163 C1044.35695,128.641444 1043.28631,129.085078 1042.03229,129.085078 C1040.77826,129.085078 1039.70762,128.641444 1038.82034,127.754163 C1037.93306,126.866882 1037.48943,125.796245 1037.48943,124.542221 C1037.48943,123.288197 1037.93306,122.21756 1038.82034,121.330279 C1039.70762,120.442998 1040.77826,119.999364 1042.03229,119.999364 C1043.28631,119.999364 1044.35695,120.442998 1045.24423,121.330279 C1046.13151,122.21756 1046.57514,123.288197 1046.57514,124.542221 Z M1059.068,131.356506 C1059.068,133.237543 1058.40255,134.843497 1057.07163,136.174419 C1055.7407,137.505341 1054.13475,138.170792 1052.25371,138.170792 C1050.37268,138.170792 1048.76672,137.505341 1047.4358,136.174419 C1046.10488,134.843497 1045.43943,133.237543 1045.43943,131.356506 C1045.43943,129.47547 1046.10488,127.869516 1047.4358,126.538594 C1048.76672,125.207672 1050.37268,124.542221 1052.25371,124.542221 C1054.13475,124.542221 1055.7407,125.207672 1057.07163,126.538594 C1058.40255,127.869516 1059.068,129.47547 1059.068,131.356506 Z M1069.28943,135.349252 C1069.28943,136.272024 1068.95818,136.972966 1068.29568,137.452098 C1067.63317,137.931229 1066.81689,138.170792 1065.84679,138.170792 L1063.46889,138.170792 C1062.25036,136.715651 1060.68285,135.958515 1058.76632,135.899363 C1059.72459,134.515205 1060.20371,133.000934 1060.20371,131.356506 C1060.20371,131.013424 1060.17414,130.623027 1060.11498,130.185301 C1060.89579,130.457401 1061.6825,130.593448 1062.47514,130.593448 C1063.17314,130.593448 1063.87703,130.466273 1064.58686,130.211919 C1065.29668,129.957566 1065.87341,129.706173 1066.31705,129.457734 C1066.76069,129.209295 1067.018,129.085078 1067.08898,129.085078 C1068.55595,129.085078 1069.28943,131.173115 1069.28943,135.349252 Z M1067.018,124.542221 C1067.018,125.796245 1066.57436,126.866882 1065.68708,127.754163 C1064.7998,128.641444 1063.72917,129.085078 1062.47514,129.085078 C1061.22112,129.085078 1060.15048,128.641444 1059.2632,127.754163 C1058.37592,126.866882 1057.93228,125.796245 1057.93228,124.542221 C1057.93228,123.288197 1058.37592,122.21756 1059.2632,121.330279 C1060.15048,120.442998 1061.22112,119.999364 1062.47514,119.999364 C1063.72917,119.999364 1064.7998,120.442998 1065.68708,121.330279 C1066.57436,122.21756 1067.018,123.288197 1067.018,124.542221 Z M1106.89221,130.043337 C1106.89221,130.516553 1106.72659,130.918782 1106.39534,131.250033 L1091.13418,146.511193 C1090.80293,146.842445 1090.4007,147.008068 1089.92748,147.008068 C1089.45427,147.008068 1089.05204,146.842445 1088.72079,146.511193 L1079.88351,137.673917 C1079.55226,137.342665 1079.38664,136.940437 1079.38664,136.46722 C1079.38664,135.994004 1079.55226,135.591776 1079.88351,135.260524 L1082.2969,132.847131 C1082.62816,132.51588 1083.03038,132.350256 1083.5036,132.350256 C1083.97682,132.350256 1084.37904,132.51588 1084.7103,132.847131 L1089.92748,138.082064 L1101.56855,126.423248 C1101.89981,126.091996 1102.30203,125.926373 1102.77525,125.926373 C1103.24847,125.926373 1103.6507,126.091996 1103.98195,126.423248 L1106.39534,128.83664 C1106.72659,129.167892 1106.89221,129.57012 1106.89221,130.043337 Z" id="--copy-2" fill="#E04733"></path>
+ <path d="M385.53174,544.858471 C383.615212,544.917623 382.047706,545.674758 380.829173,547.1299 L378.451271,547.1299 C377.481177,547.1299 376.664891,546.890337 376.002387,546.411205 C375.339884,545.932074 375.008638,545.231132 375.008638,544.30836 C375.008638,540.132223 375.742112,538.044186 377.209084,538.044186 C377.280066,538.044186 377.537374,538.168403 377.981015,538.416842 C378.424655,538.665281 379.001379,538.916673 379.711204,539.171027 C380.421029,539.425381 381.124928,539.552556 381.822923,539.552556 C382.615561,539.552556 383.402272,539.416509 384.183079,539.144409 C384.123927,539.582134 384.094351,539.972532 384.094351,540.315614 C384.094351,541.960042 384.573476,543.474313 385.53174,544.858471 Z M404.537207,556.162377 C404.537207,557.582027 404.105404,558.702942 403.241783,559.525156 C402.378163,560.34737 401.23063,560.75847 399.79915,560.75847 L384.289552,560.75847 C382.858072,560.75847 381.710539,560.34737 380.846918,559.525156 C379.983298,558.702942 379.551494,557.582027 379.551494,556.162377 C379.551494,555.535365 379.572197,554.92315 379.613604,554.325714 C379.65501,553.728278 379.737822,553.08353 379.862041,552.391451 C379.986261,551.699371 380.143011,551.057581 380.332298,550.46606 C380.521585,549.874539 380.775935,549.297815 381.095356,548.73587 C381.414777,548.173926 381.781515,547.694801 382.195579,547.298482 C382.609644,546.902163 383.115386,546.585704 383.712822,546.349096 C384.310258,546.112488 384.969794,545.994185 385.691449,545.994185 C385.809754,545.994185 386.064104,546.12136 386.454507,546.375714 C386.844911,546.630068 387.276715,546.913994 387.749932,547.2275 C388.223148,547.541006 388.856066,547.824932 389.648704,548.079286 C390.441342,548.33364 391.239883,548.460815 392.044351,548.460815 C392.848819,548.460815 393.64736,548.33364 394.439998,548.079286 C395.232636,547.824932 395.865554,547.541006 396.33877,547.2275 C396.811987,546.913994 397.243791,546.630068 397.634194,546.375714 C398.024598,546.12136 398.278948,545.994185 398.397252,545.994185 C399.118908,545.994185 399.778443,546.112488 400.375879,546.349096 C400.973315,546.585704 401.479058,546.902163 401.893123,547.298482 C402.307187,547.694801 402.673925,548.173926 402.993346,548.73587 C403.312767,549.297815 403.567117,549.874539 403.756404,550.46606 C403.945691,551.057581 404.102441,551.699371 404.226661,552.391451 C404.35088,553.08353 404.433692,553.728278 404.475098,554.325714 C404.516504,554.92315 404.537207,555.535365 404.537207,556.162377 Z M386.36578,533.501329 C386.36578,534.755353 385.922146,535.82599 385.034865,536.713271 C384.147584,537.600552 383.076947,538.044186 381.822923,538.044186 C380.568899,538.044186 379.498262,537.600552 378.610981,536.713271 C377.7237,535.82599 377.280066,534.755353 377.280066,533.501329 C377.280066,532.247305 377.7237,531.176668 378.610981,530.289387 C379.498262,529.402106 380.568899,528.958472 381.822923,528.958472 C383.076947,528.958472 384.147584,529.402106 385.034865,530.289387 C385.922146,531.176668 386.36578,532.247305 386.36578,533.501329 Z M398.858636,540.315614 C398.858636,542.19665 398.193185,543.802605 396.862264,545.133527 C395.531342,546.464449 393.925387,547.1299 392.044351,547.1299 C390.163315,547.1299 388.55736,546.464449 387.226438,545.133527 C385.895516,543.802605 385.230066,542.19665 385.230066,540.315614 C385.230066,538.434578 385.895516,536.828623 387.226438,535.497702 C388.55736,534.16678 390.163315,533.501329 392.044351,533.501329 C393.925387,533.501329 395.531342,534.16678 396.862264,535.497702 C398.193185,536.828623 398.858636,538.434578 398.858636,540.315614 Z M409.080064,544.30836 C409.080064,545.231132 408.748818,545.932074 408.086314,546.411205 C407.423811,546.890337 406.607525,547.1299 405.637431,547.1299 L403.259529,547.1299 C402.040996,545.674758 400.47349,544.917623 398.556962,544.858471 C399.515226,543.474313 399.99435,541.960042 399.99435,540.315614 C399.99435,539.972532 399.964775,539.582134 399.905623,539.144409 C400.68643,539.416509 401.473141,539.552556 402.265779,539.552556 C402.963773,539.552556 403.667673,539.425381 404.377498,539.171027 C405.087323,538.916673 405.664047,538.665281 406.107687,538.416842 C406.551328,538.168403 406.808635,538.044186 406.879618,538.044186 C408.34659,538.044186 409.080064,540.132223 409.080064,544.30836 Z M406.808636,533.501329 C406.808636,534.755353 406.365002,535.82599 405.477721,536.713271 C404.59044,537.600552 403.519803,538.044186 402.265779,538.044186 C401.011755,538.044186 399.941118,537.600552 399.053837,536.713271 C398.166556,535.82599 397.722922,534.755353 397.722922,533.501329 C397.722922,532.247305 398.166556,531.176668 399.053837,530.289387 C399.941118,529.402106 401.011755,528.958472 402.265779,528.958472 C403.519803,528.958472 404.59044,529.402106 405.477721,530.289387 C406.365002,531.176668 406.808636,532.247305 406.808636,533.501329 Z M440.063768,552.418069 C440.063768,552.891286 439.898145,553.293514 439.566893,553.624765 L437.1535,556.038158 C436.822249,556.36941 436.420021,556.535033 435.946804,556.535033 C435.473587,556.535033 435.071359,556.36941 434.740108,556.038158 L429.52292,550.820971 L424.305733,556.038158 C423.974481,556.36941 423.572253,556.535033 423.099037,556.535033 C422.62582,556.535033 422.223592,556.36941 421.89234,556.038158 L419.478948,553.624765 C419.147696,553.293514 418.982073,552.891286 418.982073,552.418069 C418.982073,551.944852 419.147696,551.542624 419.478948,551.211373 L424.696135,545.994185 L419.478948,540.776998 C419.147696,540.445747 418.982073,540.043518 418.982073,539.570302 C418.982073,539.097085 419.147696,538.694857 419.478948,538.363605 L421.89234,535.950213 C422.223592,535.618961 422.62582,535.453338 423.099037,535.453338 C423.572253,535.453338 423.974481,535.618961 424.305733,535.950213 L429.52292,541.1674 L434.740108,535.950213 C435.071359,535.618961 435.473587,535.453338 435.946804,535.453338 C436.420021,535.453338 436.822249,535.618961 437.1535,535.950213 L439.566893,538.363605 C439.898145,538.694857 440.063768,539.097085 440.063768,539.570302 C440.063768,540.043518 439.898145,540.445747 439.566893,540.776998 L434.349706,545.994185 L439.566893,551.211373 C439.898145,551.542624 440.063768,551.944852 440.063768,552.418069 Z" id="-ï€" fill="#E04733"></path>
+ <path d="M622.540142,544.858471 C620.623615,544.917623 619.056108,545.674758 617.837575,547.1299 L615.459674,547.1299 C614.48958,547.1299 613.673293,546.890337 613.01079,546.411205 C612.348287,545.932074 612.01704,545.231132 612.01704,544.30836 C612.01704,540.132223 612.750515,538.044186 614.217486,538.044186 C614.288469,538.044186 614.545777,538.168403 614.989417,538.416842 C615.433058,538.665281 616.009782,538.916673 616.719607,539.171027 C617.429432,539.425381 618.133331,539.552556 618.831325,539.552556 C619.623963,539.552556 620.410674,539.416509 621.191481,539.144409 C621.132329,539.582134 621.102754,539.972532 621.102754,540.315614 C621.102754,541.960042 621.581878,543.474313 622.540142,544.858471 Z M641.54561,556.162377 C641.54561,557.582027 641.113806,558.702942 640.250186,559.525156 C639.386565,560.34737 638.239032,560.75847 636.807552,560.75847 L621.297955,560.75847 C619.866474,560.75847 618.718941,560.34737 617.855321,559.525156 C616.991701,558.702942 616.559897,557.582027 616.559897,556.162377 C616.559897,555.535365 616.5806,554.92315 616.622006,554.325714 C616.663413,553.728278 616.746224,553.08353 616.870444,552.391451 C616.994663,551.699371 617.151414,551.057581 617.3407,550.46606 C617.529987,549.874539 617.784337,549.297815 618.103758,548.73587 C618.42318,548.173926 618.789917,547.694801 619.203982,547.298482 C619.618046,546.902163 620.123789,546.585704 620.721225,546.349096 C621.318661,546.112488 621.978197,545.994185 622.699852,545.994185 C622.818156,545.994185 623.072506,546.12136 623.46291,546.375714 C623.853314,546.630068 624.285117,546.913994 624.758334,547.2275 C625.231551,547.541006 625.864468,547.824932 626.657106,548.079286 C627.449744,548.33364 628.248285,548.460815 629.052753,548.460815 C629.857222,548.460815 630.655763,548.33364 631.448401,548.079286 C632.241038,547.824932 632.873956,547.541006 633.347173,547.2275 C633.820389,546.913994 634.252193,546.630068 634.642597,546.375714 C635.033001,546.12136 635.287351,545.994185 635.405655,545.994185 C636.12731,545.994185 636.786846,546.112488 637.384282,546.349096 C637.981718,546.585704 638.487461,546.902163 638.901525,547.298482 C639.31559,547.694801 639.682327,548.173926 640.001748,548.73587 C640.32117,549.297815 640.57552,549.874539 640.764806,550.46606 C640.954093,551.057581 641.110844,551.699371 641.235063,552.391451 C641.359282,553.08353 641.442094,553.728278 641.4835,554.325714 C641.524907,554.92315 641.54561,555.535365 641.54561,556.162377 Z M623.374182,533.501329 C623.374182,534.755353 622.930548,535.82599 622.043267,536.713271 C621.155986,537.600552 620.085349,538.044186 618.831325,538.044186 C617.577301,538.044186 616.506665,537.600552 615.619384,536.713271 C614.732102,535.82599 614.288468,534.755353 614.288468,533.501329 C614.288468,532.247305 614.732102,531.176668 615.619384,530.289387 C616.506665,529.402106 617.577301,528.958472 618.831325,528.958472 C620.085349,528.958472 621.155986,529.402106 622.043267,530.289387 C622.930548,531.176668 623.374182,532.247305 623.374182,533.501329 Z M635.867039,540.315614 C635.867039,542.19665 635.201588,543.802605 633.870666,545.133527 C632.539744,546.464449 630.933789,547.1299 629.052753,547.1299 C627.171717,547.1299 625.565762,546.464449 624.234841,545.133527 C622.903919,543.802605 622.238468,542.19665 622.238468,540.315614 C622.238468,538.434578 622.903919,536.828623 624.234841,535.497702 C625.565762,534.16678 627.171717,533.501329 629.052753,533.501329 C630.933789,533.501329 632.539744,534.16678 633.870666,535.497702 C635.201588,536.828623 635.867039,538.434578 635.867039,540.315614 Z M646.088467,544.30836 C646.088467,545.231132 645.75722,545.932074 645.094717,546.411205 C644.432214,546.890337 643.615927,547.1299 642.645833,547.1299 L640.267931,547.1299 C639.049399,545.674758 637.481892,544.917623 635.565365,544.858471 C636.523628,543.474313 637.002753,541.960042 637.002753,540.315614 C637.002753,539.972532 636.973177,539.582134 636.914025,539.144409 C637.694833,539.416509 638.481544,539.552556 639.274181,539.552556 C639.972176,539.552556 640.676075,539.425381 641.3859,539.171027 C642.095725,538.916673 642.672449,538.665281 643.11609,538.416842 C643.55973,538.168403 643.817038,538.044186 643.88802,538.044186 C645.354992,538.044186 646.088467,540.132223 646.088467,544.30836 Z M643.817038,533.501329 C643.817038,534.755353 643.373404,535.82599 642.486123,536.713271 C641.598842,537.600552 640.528205,538.044186 639.274181,538.044186 C638.020157,538.044186 636.949521,537.600552 636.06224,536.713271 C635.174958,535.82599 634.731325,534.755353 634.731325,533.501329 C634.731325,532.247305 635.174958,531.176668 636.06224,530.289387 C636.949521,529.402106 638.020157,528.958472 639.274181,528.958472 C640.528205,528.958472 641.598842,529.402106 642.486123,530.289387 C643.373404,531.176668 643.817038,532.247305 643.817038,533.501329 Z M677.07217,552.418069 C677.07217,552.891286 676.906547,553.293514 676.575295,553.624765 L674.161903,556.038158 C673.830651,556.36941 673.428423,556.535033 672.955206,556.535033 C672.48199,556.535033 672.079762,556.36941 671.74851,556.038158 L666.531323,550.820971 L661.314136,556.038158 C660.982884,556.36941 660.580656,556.535033 660.107439,556.535033 C659.634223,556.535033 659.231994,556.36941 658.900743,556.038158 L656.48735,553.624765 C656.156098,553.293514 655.990475,552.891286 655.990475,552.418069 C655.990475,551.944852 656.156098,551.542624 656.48735,551.211373 L661.704537,545.994185 L656.48735,540.776998 C656.156098,540.445747 655.990475,540.043518 655.990475,539.570302 C655.990475,539.097085 656.156098,538.694857 656.48735,538.363605 L658.900743,535.950213 C659.231994,535.618961 659.634223,535.453338 660.107439,535.453338 C660.580656,535.453338 660.982884,535.618961 661.314136,535.950213 L666.531323,541.1674 L671.74851,535.950213 C672.079762,535.618961 672.48199,535.453338 672.955206,535.453338 C673.428423,535.453338 673.830651,535.618961 674.161903,535.950213 L676.575295,538.363605 C676.906547,538.694857 677.07217,539.097085 677.07217,539.570302 C677.07217,540.043518 676.906547,540.445747 676.575295,540.776998 L671.358108,545.994185 L676.575295,551.211373 C676.906547,551.542624 677.07217,551.944852 677.07217,552.418069 Z" id="-ï€-copy" fill="#E04733"></path>
+ <path d="M756.856155,443.148434 C756.856155,441.89441 756.412521,440.823773 755.52524,439.936492 C754.637959,439.049211 753.567322,438.605577 752.313298,438.605577 C751.059274,438.605577 749.988638,439.049211 749.101357,439.936492 C748.214075,440.823773 747.770441,441.89441 747.770441,443.148434 C747.770441,444.402458 748.214075,445.473095 749.101357,446.360376 C749.988638,447.247657 751.059274,447.691291 752.313298,447.691291 C753.567322,447.691291 754.637959,447.247657 755.52524,446.360376 C756.412521,445.473095 756.856155,444.402458 756.856155,443.148434 Z M770.484726,452.234148 C770.484726,451.618966 770.259951,451.086605 769.810396,450.63705 C769.36084,450.187494 768.828479,449.962719 768.213297,449.962719 C767.598116,449.962719 767.065755,450.187494 766.616199,450.63705 C766.166644,451.086605 765.941869,451.618966 765.941869,452.234148 C765.941869,452.86116 766.163686,453.396478 766.607327,453.840119 C767.050967,454.283759 767.586285,454.505576 768.213297,454.505576 C768.840309,454.505576 769.375628,454.283759 769.819268,453.840119 C770.262909,453.396478 770.484726,452.86116 770.484726,452.234148 Z M770.484726,434.06272 C770.484726,433.447539 770.259951,432.915178 769.810396,432.465622 C769.36084,432.016066 768.828479,431.791292 768.213297,431.791292 C767.598116,431.791292 767.065755,432.016066 766.616199,432.465622 C766.166644,432.915178 765.941869,433.447539 765.941869,434.06272 C765.941869,434.689732 766.163686,435.22505 766.607327,435.668691 C767.050967,436.112332 767.586285,436.334149 768.213297,436.334149 C768.840309,436.334149 769.375628,436.112332 769.819268,435.668691 C770.262909,435.22505 770.484726,434.689732 770.484726,434.06272 Z M763.670441,441.53359 L763.670441,444.816514 C763.670441,444.934818 763.629035,445.050163 763.546222,445.162552 C763.463409,445.274941 763.368767,445.33705 763.262293,445.34888 L760.511735,445.774773 C760.381601,446.188838 760.192317,446.638387 759.943878,447.123434 C760.346112,447.691294 760.878473,448.371532 761.540976,449.16417 C761.623789,449.282474 761.665195,449.400777 761.665195,449.519081 C761.665195,449.661046 761.623789,449.773433 761.540976,449.856246 C761.268877,450.211159 760.780879,450.740562 760.07697,451.444471 C759.37306,452.148381 758.908723,452.500331 758.683945,452.500331 C758.553811,452.500331 758.429593,452.458925 758.311289,452.376112 L756.270553,450.779014 C755.832827,451.003792 755.377363,451.18716 754.904146,451.329125 C754.774012,452.60681 754.637964,453.523654 754.495999,454.079683 C754.413186,454.363613 754.235733,454.505576 753.963633,454.505576 L750.662964,454.505576 C750.532829,454.505576 750.414527,454.461213 750.308053,454.372485 C750.201579,454.283757 750.142428,454.180242 750.130598,454.061938 L749.72245,451.346871 C749.320216,451.228567 748.876582,451.045198 748.391535,450.796759 L746.297562,452.376112 C746.214749,452.458925 746.096447,452.500331 745.942651,452.500331 C745.812517,452.500331 745.688299,452.45301 745.569995,452.358367 C743.866415,450.784921 743.014638,449.838502 743.014638,449.519081 C743.014638,449.412607 743.056044,449.30022 743.138857,449.181916 C743.257161,449.01629 743.499681,448.702789 743.866424,448.241402 C744.233167,447.780016 744.511177,447.419194 744.700464,447.158925 C744.428364,446.638387 744.221335,446.153347 744.07937,445.703791 L741.382049,445.277898 C741.263745,445.266068 741.163188,445.209874 741.080375,445.109316 C740.997562,445.008757 740.956156,444.893412 740.956156,444.763278 L740.956156,441.480354 C740.956156,441.36205 740.997562,441.246705 741.080375,441.134316 C741.163188,441.021927 741.25783,440.959818 741.364303,440.947988 L744.114861,440.522095 C744.244996,440.10803 744.43428,439.658481 744.682718,439.173434 C744.280484,438.605574 743.748124,437.925336 743.08562,437.132698 C743.002807,437.002563 742.961401,436.884261 742.961401,436.777787 C742.961401,436.635822 743.002807,436.51752 743.08562,436.422876 C743.345889,436.067964 743.830929,435.541518 744.540754,434.843524 C745.250579,434.145529 745.717873,433.796537 745.942651,433.796537 C746.072786,433.796537 746.197003,433.837943 746.315308,433.920756 L748.356044,435.517854 C748.758278,435.304907 749.213742,435.115623 749.72245,434.949997 C749.852585,433.672312 749.988633,432.761384 750.130598,432.217185 C750.21341,431.933255 750.390864,431.791292 750.662964,431.791292 L753.963633,431.791292 C754.093768,431.791292 754.21207,431.835655 754.318544,431.924383 C754.425017,432.013111 754.484169,432.116626 754.495999,432.23493 L754.904146,434.949997 C755.306381,435.068301 755.750014,435.25167 756.235061,435.500108 L758.329035,433.920756 C758.423678,433.837943 758.54198,433.796537 758.683945,433.796537 C758.81408,433.796537 758.938297,433.843858 759.056602,433.938501 C760.760181,435.511947 761.611959,436.458366 761.611959,436.777787 C761.611959,436.884261 761.570553,436.996648 761.48774,437.114952 C761.345775,437.304239 761.09734,437.623655 760.742427,438.073211 C760.387515,438.522767 760.121334,438.877674 759.943878,439.137943 C760.215978,439.705803 760.417092,440.190843 760.547226,440.593077 L763.244548,441.001224 C763.362852,441.024885 763.463409,441.086994 763.546222,441.187552 C763.629035,441.288111 763.670441,441.403456 763.670441,441.53359 Z M775.027583,450.99196 L775.027583,453.476335 C775.027583,453.665622 774.14623,453.848991 772.383498,454.026447 C772.241533,454.345868 772.06408,454.653454 771.851132,454.949215 C772.454483,456.286052 772.756154,457.102338 772.756154,457.398098 C772.756154,457.44542 772.732494,457.486826 772.685172,457.522317 C771.241861,458.362277 770.508387,458.78225 770.484726,458.78225 C770.390083,458.78225 770.117987,458.504239 769.668431,457.94821 C769.218876,457.39218 768.911289,456.989952 768.745663,456.741514 C768.509055,456.765174 768.331602,456.777005 768.213297,456.777005 C768.094993,456.777005 767.91754,456.765174 767.680931,456.741514 C767.515306,456.989952 767.207719,457.39218 766.758164,457.94821 C766.308608,458.504239 766.036512,458.78225 765.941869,458.78225 C765.918208,458.78225 765.184733,458.362277 763.741423,457.522317 C763.694101,457.486826 763.670441,457.44542 763.670441,457.398098 C763.670441,457.102338 763.972112,456.286052 764.575463,454.949215 C764.362515,454.653454 764.185062,454.345868 764.043097,454.026447 C762.280365,453.848991 761.399012,453.665622 761.399012,453.476335 L761.399012,450.99196 C761.399012,450.802674 762.280365,450.619305 764.043097,450.441849 C764.196892,450.098767 764.374346,449.791181 764.575463,449.519081 C763.972112,448.182244 763.670441,447.365958 763.670441,447.070197 C763.670441,447.022875 763.694101,446.98147 763.741423,446.945978 C763.788744,446.922318 763.995774,446.804015 764.362516,446.591068 C764.729259,446.37812 765.078251,446.177006 765.409503,445.98772 C765.740755,445.798433 765.918208,445.703791 765.941869,445.703791 C766.036512,445.703791 766.308608,445.978844 766.758164,446.528958 C767.207719,447.079073 767.515306,447.478343 767.680931,447.726782 C767.91754,447.703121 768.094993,447.691291 768.213297,447.691291 C768.331602,447.691291 768.509055,447.703121 768.745663,447.726782 C769.349015,446.886822 769.893206,446.224329 770.378253,445.739282 L770.484726,445.703791 C770.532048,445.703791 771.265522,446.117849 772.685172,446.945978 C772.732494,446.98147 772.756154,447.022875 772.756154,447.070197 C772.756154,447.365958 772.454483,448.182244 771.851132,449.519081 C772.052249,449.791181 772.229703,450.098767 772.383498,450.441849 C774.14623,450.619305 775.027583,450.802674 775.027583,450.99196 Z M775.027583,432.820533 L775.027583,435.304908 C775.027583,435.494194 774.14623,435.677563 772.383498,435.855019 C772.241533,436.17444 772.06408,436.482027 771.851132,436.777787 C772.454483,438.114624 772.756154,438.93091 772.756154,439.226671 C772.756154,439.273992 772.732494,439.315398 772.685172,439.35089 C771.241861,440.190849 770.508387,440.610822 770.484726,440.610822 C770.390083,440.610822 770.117987,440.332812 769.668431,439.776782 C769.218876,439.220753 768.911289,438.818525 768.745663,438.570086 C768.509055,438.593747 768.331602,438.605577 768.213297,438.605577 C768.094993,438.605577 767.91754,438.593747 767.680931,438.570086 C767.515306,438.818525 767.207719,439.220753 766.758164,439.776782 C766.308608,440.332812 766.036512,440.610822 765.941869,440.610822 C765.918208,440.610822 765.184733,440.190849 763.741423,439.35089 C763.694101,439.315398 763.670441,439.273992 763.670441,439.226671 C763.670441,438.93091 763.972112,438.114624 764.575463,436.777787 C764.362515,436.482027 764.185062,436.17444 764.043097,435.855019 C762.280365,435.677563 761.399012,435.494194 761.399012,435.304908 L761.399012,432.820533 C761.399012,432.631246 762.280365,432.447877 764.043097,432.270421 C764.196892,431.927339 764.374346,431.619753 764.575463,431.347653 C763.972112,430.010816 763.670441,429.19453 763.670441,428.89877 C763.670441,428.851448 763.694101,428.810042 763.741423,428.774551 C763.788744,428.75089 763.995774,428.632588 764.362516,428.41964 C764.729259,428.206693 765.078251,428.005579 765.409503,427.816292 C765.740755,427.627005 765.918208,427.532363 765.941869,427.532363 C766.036512,427.532363 766.308608,427.807416 766.758164,428.357531 C767.207719,428.907645 767.515306,429.306916 767.680931,429.555354 C767.91754,429.531694 768.094993,429.519863 768.213297,429.519863 C768.331602,429.519863 768.509055,429.531694 768.745663,429.555354 C769.349015,428.715395 769.893206,428.052901 770.378253,427.567854 L770.484726,427.532363 C770.532048,427.532363 771.265522,427.946422 772.685172,428.774551 C772.732494,428.810042 772.756154,428.851448 772.756154,428.89877 C772.756154,429.19453 772.454483,430.010816 771.851132,431.347653 C772.052249,431.619753 772.229703,431.927339 772.383498,432.270421 C774.14623,432.447877 775.027583,432.631246 775.027583,432.820533 Z" id="ï‚…-copy-2" fill="#E04733"></path>
+ <path d="M885.860729,188.873341 C885.860729,187.619317 885.417095,186.54868 884.529814,185.661399 C883.642532,184.774118 882.571896,184.330484 881.317872,184.330484 C880.063848,184.330484 878.993211,184.774118 878.10593,185.661399 C877.218649,186.54868 876.775015,187.619317 876.775015,188.873341 C876.775015,190.127365 877.218649,191.198002 878.10593,192.085283 C878.993211,192.972564 880.063848,193.416198 881.317872,193.416198 C882.571896,193.416198 883.642532,192.972564 884.529814,192.085283 C885.417095,191.198002 885.860729,190.127365 885.860729,188.873341 Z M899.489299,197.959055 C899.489299,197.343873 899.264525,196.811512 898.814969,196.361957 C898.365413,195.912401 897.833053,195.687626 897.217871,195.687626 C896.602689,195.687626 896.070329,195.912401 895.620773,196.361957 C895.171217,196.811512 894.946442,197.343873 894.946442,197.959055 C894.946442,198.586067 895.168259,199.121385 895.6119,199.565026 C896.055541,200.008666 896.590859,200.230483 897.217871,200.230483 C897.844883,200.230483 898.380201,200.008666 898.823842,199.565026 C899.267482,199.121385 899.489299,198.586067 899.489299,197.959055 Z M899.489299,179.787627 C899.489299,179.172446 899.264525,178.640085 898.814969,178.190529 C898.365413,177.740973 897.833053,177.516199 897.217871,177.516199 C896.602689,177.516199 896.070329,177.740973 895.620773,178.190529 C895.171217,178.640085 894.946442,179.172446 894.946442,179.787627 C894.946442,180.414639 895.168259,180.949958 895.6119,181.393598 C896.055541,181.837239 896.590859,182.059056 897.217871,182.059056 C897.844883,182.059056 898.380201,181.837239 898.823842,181.393598 C899.267482,180.949958 899.489299,180.414639 899.489299,179.787627 Z M892.675014,187.258497 L892.675014,190.541421 C892.675014,190.659725 892.633608,190.77507 892.550795,190.887459 C892.467982,190.999848 892.37334,191.061957 892.266867,191.073787 L889.516309,191.49968 C889.386174,191.913745 889.19689,192.363294 888.948452,192.848341 C889.350686,193.416201 889.883047,194.096439 890.54555,194.889077 C890.628363,195.007381 890.669769,195.125684 890.669769,195.243988 C890.669769,195.385953 890.628363,195.49834 890.54555,195.581153 C890.27345,195.936066 889.785453,196.465469 889.081543,197.169379 C888.377634,197.873288 887.913297,198.225238 887.688519,198.225238 C887.558384,198.225238 887.434167,198.183832 887.315863,198.101019 L885.275126,196.503921 C884.837401,196.728699 884.381937,196.912068 883.90872,197.054033 C883.778585,198.331717 883.642538,199.248561 883.500573,199.80459 C883.41776,200.08852 883.240306,200.230483 882.968207,200.230483 L879.667537,200.230483 C879.537403,200.230483 879.4191,200.18612 879.312626,200.097392 C879.206153,200.008664 879.147001,199.905149 879.135171,199.786845 L878.727024,197.071778 C878.32479,196.953474 877.881156,196.770105 877.396109,196.521667 L875.302136,198.101019 C875.219323,198.183832 875.10102,198.225238 874.947225,198.225238 C874.81709,198.225238 874.692873,198.177917 874.574569,198.083274 C872.870989,196.509828 872.019212,195.563409 872.019212,195.243988 C872.019212,195.137514 872.060617,195.025127 872.14343,194.906823 C872.261734,194.741197 872.504254,194.427696 872.870997,193.96631 C873.23774,193.504923 873.515751,193.144101 873.705037,192.883832 C873.432938,192.363294 873.225909,191.878254 873.083944,191.428698 L870.386622,191.002805 C870.268318,190.990975 870.167761,190.934781 870.084948,190.834223 C870.002135,190.733664 869.96073,190.618319 869.96073,190.488185 L869.96073,187.205261 C869.96073,187.086957 870.002135,186.971612 870.084948,186.859223 C870.167761,186.746834 870.262403,186.684725 870.368877,186.672895 L873.119435,186.247002 C873.249569,185.832937 873.438853,185.383388 873.687292,184.898341 C873.285058,184.330481 872.752697,183.650243 872.090194,182.857605 C872.007381,182.72747 871.965975,182.609168 871.965975,182.502694 C871.965975,182.360729 872.007381,182.242427 872.090194,182.147783 C872.350463,181.792871 872.835503,181.266425 873.545328,180.568431 C874.255153,179.870436 874.722447,179.521444 874.947225,179.521444 C875.077359,179.521444 875.201577,179.56285 875.319881,179.645663 L877.360618,181.242761 C877.762852,181.029814 878.218316,180.84053 878.727024,180.674904 C878.857158,179.397219 878.993206,178.486291 879.135171,177.942092 C879.217984,177.658162 879.395438,177.516199 879.667537,177.516199 L882.968207,177.516199 C883.098341,177.516199 883.216643,177.560562 883.323117,177.64929 C883.429591,177.738018 883.488742,177.841533 883.500573,177.959837 L883.90872,180.674904 C884.310954,180.793208 884.754588,180.976577 885.239635,181.225016 L887.333608,179.645663 C887.428251,179.56285 887.546554,179.521444 887.688519,179.521444 C887.818653,179.521444 887.942871,179.568765 888.061175,179.663408 C889.764755,181.236854 890.616532,182.183273 890.616532,182.502694 C890.616532,182.609168 890.575126,182.721555 890.492313,182.839859 C890.350348,183.029146 890.101913,183.348562 889.747001,183.798118 C889.392088,184.247674 889.125908,184.602581 888.948452,184.86285 C889.220551,185.43071 889.421665,185.91575 889.5518,186.317984 L892.249121,186.726131 C892.367425,186.749792 892.467982,186.811901 892.550795,186.912459 C892.633608,187.013018 892.675014,187.128363 892.675014,187.258497 Z M904.032156,196.716867 L904.032156,199.201242 C904.032156,199.390529 903.150804,199.573898 901.388072,199.751354 C901.246107,200.070775 901.068653,200.378361 900.855706,200.674122 C901.459057,202.010959 901.760728,202.827245 901.760728,203.123005 C901.760728,203.170327 901.737067,203.211733 901.689746,203.247224 C900.246435,204.087184 899.51296,204.507157 899.489299,204.507157 C899.394656,204.507157 899.122561,204.229147 898.673005,203.673117 C898.223449,203.117087 897.915863,202.714859 897.750237,202.466421 C897.513629,202.490081 897.336175,202.501912 897.217871,202.501912 C897.099567,202.501912 896.922113,202.490081 896.685505,202.466421 C896.519879,202.714859 896.212293,203.117087 895.762737,203.673117 C895.313181,204.229147 895.041086,204.507157 894.946442,204.507157 C894.922782,204.507157 894.189307,204.087184 892.745996,203.247224 C892.698675,203.211733 892.675014,203.170327 892.675014,203.123005 C892.675014,202.827245 892.976685,202.010959 893.580036,200.674122 C893.367089,200.378361 893.189635,200.070775 893.04767,199.751354 C891.284938,199.573898 890.403586,199.390529 890.403586,199.201242 L890.403586,196.716867 C890.403586,196.527581 891.284938,196.344212 893.04767,196.166756 C893.201466,195.823674 893.378919,195.516088 893.580036,195.243988 C892.976685,193.907151 892.675014,193.090865 892.675014,192.795104 C892.675014,192.747783 892.698675,192.706377 892.745996,192.670885 C892.793318,192.647225 893.000347,192.528922 893.36709,192.315975 C893.733833,192.103027 894.082825,191.901913 894.414076,191.712627 C894.745328,191.52334 894.922782,191.428698 894.946442,191.428698 C895.041086,191.428698 895.313181,191.703751 895.762737,192.253865 C896.212293,192.80398 896.519879,193.20325 896.685505,193.451689 C896.922113,193.428028 897.099567,193.416198 897.217871,193.416198 C897.336175,193.416198 897.513629,193.428028 897.750237,193.451689 C898.353588,192.611729 898.897779,191.949236 899.382826,191.464189 L899.489299,191.428698 C899.536621,191.428698 900.270096,191.842756 901.689746,192.670885 C901.737067,192.706377 901.760728,192.747783 901.760728,192.795104 C901.760728,193.090865 901.459057,193.907151 900.855706,195.243988 C901.056823,195.516088 901.234276,195.823674 901.388072,196.166756 C903.150804,196.344212 904.032156,196.527581 904.032156,196.716867 Z M904.032156,178.54544 L904.032156,181.029815 C904.032156,181.219101 903.150804,181.40247 901.388072,181.579926 C901.246107,181.899347 901.068653,182.206934 900.855706,182.502694 C901.459057,183.839531 901.760728,184.655817 901.760728,184.951578 C901.760728,184.9989 901.737067,185.040305 901.689746,185.075797 C900.246435,185.915756 899.51296,186.33573 899.489299,186.33573 C899.394656,186.33573 899.122561,186.057719 898.673005,185.501689 C898.223449,184.94566 897.915863,184.543432 897.750237,184.294993 C897.513629,184.318654 897.336175,184.330484 897.217871,184.330484 C897.099567,184.330484 896.922113,184.318654 896.685505,184.294993 C896.519879,184.543432 896.212293,184.94566 895.762737,185.501689 C895.313181,186.057719 895.041086,186.33573 894.946442,186.33573 C894.922782,186.33573 894.189307,185.915756 892.745996,185.075797 C892.698675,185.040305 892.675014,184.9989 892.675014,184.951578 C892.675014,184.655817 892.976685,183.839531 893.580036,182.502694 C893.367089,182.206934 893.189635,181.899347 893.04767,181.579926 C891.284938,181.40247 890.403586,181.219101 890.403586,181.029815 L890.403586,178.54544 C890.403586,178.356153 891.284938,178.172784 893.04767,177.995328 C893.201466,177.652246 893.378919,177.34466 893.580036,177.07256 C892.976685,175.735723 892.675014,174.919437 892.675014,174.623677 C892.675014,174.576355 892.698675,174.534949 892.745996,174.499458 C892.793318,174.475797 893.000347,174.357495 893.36709,174.144547 C893.733833,173.9316 894.082825,173.730486 894.414076,173.541199 C894.745328,173.351912 894.922782,173.25727 894.946442,173.25727 C895.041086,173.25727 895.313181,173.532323 895.762737,174.082438 C896.212293,174.632552 896.519879,175.031823 896.685505,175.280261 C896.922113,175.256601 897.099567,175.24477 897.217871,175.24477 C897.336175,175.24477 897.513629,175.256601 897.750237,175.280261 C898.353588,174.440302 898.897779,173.777809 899.382826,173.292762 L899.489299,173.25727 C899.536621,173.25727 900.270096,173.671329 901.689746,174.499458 C901.737067,174.534949 901.760728,174.576355 901.760728,174.623677 C901.760728,174.919437 901.459057,175.735723 900.855706,177.07256 C901.056823,177.34466 901.234276,177.652246 901.388072,177.995328 C903.150804,178.172784 904.032156,178.356153 904.032156,178.54544 Z" id="ï‚…-copy-3" fill="#E04733"></path>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/doc/ci/review_apps/img/review_apps_preview_in_mr.png b/doc/ci/review_apps/img/review_apps_preview_in_mr.png
index 0300392f24b..3e6506a6a3a 100644
--- a/doc/ci/review_apps/img/review_apps_preview_in_mr.png
+++ b/doc/ci/review_apps/img/review_apps_preview_in_mr.png
Binary files differ
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index 28c484ddbe6..64be011008e 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -1,96 +1,94 @@
-# Getting started with Review Apps
+# Review Apps
->
-- [Introduced][ce-21971] in GitLab 8.12. Further additions were made in GitLab
- 8.13 and 8.14.
-- Inspired by [Heroku's Review Apps][heroku-apps] which itself was inspired by
- [Fourchette].
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/21971) in GitLab 8.12. Further additions were made in GitLab 8.13 and 8.14.
+> - Inspired by [Heroku's Review Apps](https://devcenter.heroku.com/articles/github-integration-review-apps), which itself was inspired by [Fourchette](https://github.com/rainforestapp/fourchette).
-The basis of Review Apps is the [dynamic environments] which allow you to create
-a new environment (dynamically) for each one of your branches.
+For a video introduction to Review Apps, see [8.14 Webcast: Review Apps & Time Tracking Beta (EE) - GitLab Release](https://www.youtube.com/watch?v=CteZol_7pxo).
-A Review App can then be visible as a link when you visit the [merge request]
-relevant to the branch. That way, you are able to see live all changes introduced
-by the merge request changes. Reviewing anything, from performance to interface
-changes, becomes much easier with a live environment and as such, Review Apps
-can make a huge impact on your development flow.
+## Overview
-They mostly make sense to be used with web applications, but you can use them
-any way you'd like.
+Review Apps are a collaboration tool that takes the hard work out of providing an environment to showcase product changes.
-## Overview
+Review Apps:
+
+- Provide an automatic live preview of changes made in a feature branch by spinning up a dynamic environment for your merge requests.
+- Allow designers and product manages to see your changes without needing to check out your branch and run your changes in a sandbox environment.
+- Are fully integrated with the [GitLab DevOps LifeCycle](../../README.md#complete-devops-with-gitlab).
+- Allow you to deploy your changes wherever you want.
+
+![Review Apps Workflow](img/continuous-delivery-review-apps.svg)
+
+Reviewing anything, from performance to interface changes, becomes much easier with a live environment and so Review Apps can make a large impact on your development flow.
-Simply put, a Review App is a mapping of a branch with an environment as there
-is a 1:1 relation between them.
+## What are Review Apps?
-Here's an example of what it looks like when viewing a merge request with a
-dynamically set environment.
+A Review App is a mapping of a branch with an [environment](../environments.md). The following is an example of a merge request with an environment set dynamically.
![Review App in merge request](img/review_apps_preview_in_mr.png)
-In the image above you can see that the `add-new-line` branch was successfully
-built and deployed under a dynamic environment and can be previewed with an
-also dynamically URL.
+In this example, you can see a branch was:
+
+- Successfully built.
+- Deployed under a dynamic environment that can be reached by clicking on the **View app** button.
+
+## How do Review Apps work?
+
+The basis of Review Apps in GitLab is [dynamic environments](../environments.md#dynamic-environments), which allow you to dynamically create a new environment for each branch.
+
+Access to the Review App is made available as a link on the [merge request](../../user/project/merge_requests.md) relevant to the branch. Review Apps enable you to review all changes proposed by the merge request in live environment.
-The details of the Review Apps implementation depend widely on your real
-technology stack and on your deployment process. The simplest case is to
-deploy a simple static HTML website, but it will not be that straightforward
-when your app is using a database for example. To make a branch be deployed
-on a temporary instance and booting up this instance with all required software
-and services automatically on the fly is not a trivial task. However, it is
-doable, especially if you use Docker, or at least a configuration management
-tool like Chef, Puppet, Ansible or Salt.
+## Use cases
-## Prerequisites
+Some supported use cases include the:
-To get a better understanding of Review Apps, you must first learn how
-environments and deployments work. The following docs will help you grasp that
-knowledge:
+- Simple case of deploying a simple static HTML website.
+- More complicated case of an application that uses a database. Deploying a branch on a temporary instance and booting up this instance with all required software and services automatically on the fly is not a trivial task. However, it is possible, especially if you use Docker or a configuration management tool like Chef, Puppet, Ansible, or Salt.
-1. First, learn about [environments][] and their role in the development workflow.
-1. Then make a small stop to learn about [CI variables][variables] and how they
- can be used in your CI jobs.
-1. Next, explore the [`environment` syntax][yaml-env] as defined in `.gitlab-ci.yml`.
- This will be your primary reference when you are finally comfortable with
- how environments work.
-1. Additionally, find out about [manual actions][] and how you can use them to
- deploy to critical environments like production with the push of a button.
-1. And as a last step, follow the [example tutorials](#examples) which will
- guide you step by step to set up the infrastructure and make use of
- Review Apps.
+Review Apps usually make sense with web applications, but you can use them any way you'd like.
-## Configuration
+## Implementing Review Apps
-The configuration of Review apps depends on your technology stack and your
-infrastructure. Read the [dynamic environments] documentation to understand
-how to define and create them.
+Implementing Review Apps depends on your:
-## Creating and destroying Review Apps
+- Technology stack.
+- Deployment process.
-The creation and destruction of a Review App is defined in `.gitlab-ci.yml`
-at a job level under the `environment` keyword.
+### Prerequisite Knowledge
-Check the [environments] documentation how to do so.
+To get a better understanding of Review Apps, review documentation on how environments and deployments work. Before you implement your own Review Apps:
-## A simple workflow
+1. Learn about [environments](../environments.md) and their role in the development workflow.
+1. Learn about [CI variables](../variables/README.md) and how they can be used in your CI jobs.
+1. Explore the [`environment` syntax](../yaml/README.md#environment) as defined in `.gitlab-ci.yml`. This will become a primary reference.
+1. Additionally, find out about [manual actions](../environments.md#manual-actions) and how you can use them to deploy to critical environments like production with the push of a button.
+1. Follow the [example tutorials](#examples). These will guide you through setting up infrastructure and using Review Apps.
-The process of adding Review Apps in your workflow would look like:
+### Configuring dynamic environments
+
+Configuring Review Apps dynamic environments depends on your technology stack and infrastructure.
+
+For more information, see [dynamic environments](../environments.md#dynamic-environments) documentation to understand how to define and create them.
+
+### Creating and destroying Review Apps
+
+Creating and destroying Review Apps is defined in `.gitlab-ci.yml` at a job level under the `environment` keyword.
+
+For more information, see [Introduction to environments and deployments](../environments.md).
+
+### Adding Review Apps to your workflow
+
+The process of adding Review Apps in your workflow is as follows:
1. Set up the infrastructure to host and deploy the Review Apps.
-1. [Install][install-runner] and [configure][conf-runner] a Runner that does
- the deployment.
-1. Set up a job in `.gitlab-ci.yml` that uses the predefined
- [predefined CI environment variable][variables] `${CI_COMMIT_REF_NAME}` to
- create dynamic environments and restrict it to run only on branches.
-1. Optionally set a job that [manually stops][manual-env] the Review Apps.
+1. [Install](https://docs.gitlab.com/runner/install/) and [configure](https://docs.gitlab.com/runner/commands/) a Runner to do deployment.
+1. Set up a job in `.gitlab-ci.yml` that uses the predefined [predefined CI environment variable](../variables/README.md) `${CI_COMMIT_REF_NAME}` to create dynamic environments and restrict it to run only on branches.
+1. Optionally, set a job that [manually stops](../environments.md#stopping-an-environment) the Review Apps.
-From there on, you would follow the branched Git flow:
+After adding Review Apps to your workflow, you follow the branched Git flow. That is:
-1. Push a branch and let the Runner deploy the Review App based on the `script`
- definition of the dynamic environment job.
-1. Wait for the Runner to build and/or deploy your web app.
-1. Click on the link that's present in the MR related to the branch and see the
- changes live.
+1. Push a branch and let the Runner deploy the Review App based on the `script` definition of the dynamic environment job.
+1. Wait for the Runner to build and deploy your web application.
+1. Click on the link that provided in the merge request related to the branch to see the changes live.
## Limitations
@@ -98,27 +96,9 @@ Check the [environments limitations](../environments.md#limitations).
## Examples
-A list of examples used with Review Apps can be found below:
-
-- [Use with NGINX][app-nginx] - Use NGINX and the shell executor of GitLab Runner
- to deploy a simple HTML website.
-
-And below is a soon to be added examples list:
-
-- Use with Amazon S3
-- Use on Heroku with dpl
-- Use with OpenShift/kubernetes
-
-[app-nginx]: https://gitlab.com/gitlab-examples/review-apps-nginx
-[ce-21971]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21971
-[dynamic environments]: ../environments.md#dynamic-environments
-[environments]: ../environments.md
-[fourchette]: https://github.com/rainforestapp/fourchette
-[heroku-apps]: https://devcenter.heroku.com/articles/github-integration-review-apps
-[manual actions]: ../environments.md#manual-actions
-[merge request]: ../../user/project/merge_requests.md
-[variables]: ../variables/README.md
-[yaml-env]: ../yaml/README.md#environment
-[install-runner]: https://docs.gitlab.com/runner/install/
-[conf-runner]: https://docs.gitlab.com/runner/commands/
-[manual-env]: ../environments.md#stopping-an-environment
+The following are example projects that use Review Apps with:
+
+- [NGINX](https://gitlab.com/gitlab-examples/review-apps-nginx).
+- [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift).
+
+See also the video [Demo: Cloud Native Development with GitLab](https://www.youtube.com/watch?v=jfIyQEwrocw), which includes a Review Apps example.
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
index 8f1ff190804..9c9ea651678 100644
--- a/doc/ci/runners/README.md
+++ b/doc/ci/runners/README.md
@@ -29,7 +29,7 @@ are:
- **Specific Runners** are useful for jobs that have special requirements or for
projects with a specific demand. If a job has certain requirements, you can set
up the specific Runner with this in mind, while not having to do this for all
- Runners. For example, if you want to deploy a certain project, you can setup
+ Runners. For example, if you want to deploy a certain project, you can set up
a specific Runner to have the right credentials for this. The [usage of tags](#using-tags)
may be useful in this case. Specific Runners process jobs using a [FIFO] queue.
- **Group Runners** are useful when you have multiple projects under one group
@@ -138,15 +138,14 @@ project without requiring your authorization, so use it with caution.
An admin can enable/disable a specific Runner for projects:
1. Navigate to **Admin > Runners**
-2. Find the Runner you wish to enable/disable
-3. Click edit on the Runner
-4. Click **Enable** or **Disable** on the project
+1. Find the Runner you wish to enable/disable
+1. Click edit on the Runner
+1. Click **Enable** or **Disable** on the project
## Protected Runners
->
-[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13194)
-in GitLab 10.0.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13194)
+> in GitLab 10.0.
You can protect Runners from revealing sensitive information.
Whenever a Runner is protected, the Runner picks only jobs created on
@@ -223,7 +222,7 @@ should keep in mind.
### Using tags
-You must setup a Runner to be able to run all the different types of jobs
+You must set up a Runner to be able to run all the different types of jobs
that it may encounter on the projects it's shared over. This would be
problematic for large amounts of projects, if it wasn't for tags.
@@ -299,7 +298,7 @@ and using more secure [Runner Executors](https://docs.gitlab.com/runner/executor
### Forks
Whenever a project is forked, it copies the settings of the jobs that relate
-to it. This means that if you have shared Runners setup for a project and
+to it. This means that if you have shared Runners set up for a project and
someone forks that project, the shared Runners will also serve jobs of this
project.
@@ -313,7 +312,7 @@ We're always looking for contributions that can mitigate these
If you think that registration token for a Project was revealed, you should
reset them. It's recommended because such token can be used to register another
-Runner to thi Project. It may be next used to obtain the values of secret
+Runner to the Project. It may be next used to obtain the values of secret
variables or clone the project code, that normally may be unavailable for the
attacker.
diff --git a/doc/ci/runners/img/protected_runners_check_box.png b/doc/ci/runners/img/protected_runners_check_box.png
index fb58498c7ce..3c47ebdec29 100644
--- a/doc/ci/runners/img/protected_runners_check_box.png
+++ b/doc/ci/runners/img/protected_runners_check_box.png
Binary files differ
diff --git a/doc/ci/runners/img/shared_runner_ip_address.png b/doc/ci/runners/img/shared_runner_ip_address.png
index 3b1542d59d3..527b4f4043d 100644
--- a/doc/ci/runners/img/shared_runner_ip_address.png
+++ b/doc/ci/runners/img/shared_runner_ip_address.png
Binary files differ
diff --git a/doc/ci/runners/img/specific_runner_ip_address.png b/doc/ci/runners/img/specific_runner_ip_address.png
index 3b4c3e9f2eb..e08663109ba 100644
--- a/doc/ci/runners/img/specific_runner_ip_address.png
+++ b/doc/ci/runners/img/specific_runner_ip_address.png
Binary files differ
diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md
index 338368dbbc9..b76f9618fc9 100644
--- a/doc/ci/services/mysql.md
+++ b/doc/ci/services/mysql.md
@@ -31,7 +31,7 @@ Database: el_duderino
```
If you are wondering why we used `mysql` for the `Host`, read more at
-[How is service linked to the job](../docker/using_docker_images.md#how-is-service-linked-to-the-job).
+[How services are linked to the job](../docker/using_docker_images.md#how-services-are-linked-to-the-job).
You can also use any other docker image available on [Docker Hub][hub-mysql].
For example, to use MySQL 5.5 the service becomes `mysql:5.5`.
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
index 4cb05509e7b..0c3b0bf6990 100644
--- a/doc/ci/ssh_keys/README.md
+++ b/doc/ci/ssh_keys/README.md
@@ -46,7 +46,7 @@ to access it. This is where an SSH key pair comes in handy.
1. You will first need to create an SSH key pair. For more information, follow
the instructions to [generate an SSH key](../../ssh/README.md#generating-a-new-ssh-key-pair).
- **Do not** add a passphrase to the SSH key, or the `before_script` will\
+ **Do not** add a passphrase to the SSH key, or the `before_script` will
prompt for it.
1. Create a new [variable](../variables/README.md#variables).
@@ -175,7 +175,7 @@ Now that the `SSH_KNOWN_HOSTS` variable is created, in addition to the
[content of `.gitlab-ci.yml`](#ssh-keys-when-using-the-docker-executor)
above, here's what more you need to add:
- ```yaml
+```yaml
before_script:
##
## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index c213b096a14..bffb0121603 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -1,9 +1,10 @@
# Triggering pipelines through the API
> **Notes**:
-- [Introduced][ci-229] in GitLab CE 7.14.
-- GitLab 8.12 has a completely redesigned job permissions system. Read all
- about the [new model and its implications](../../user/project/new_ci_build_permissions_model.md#job-triggers).
+>
+> - [Introduced](https://about.gitlab.com/2015/08/22/gitlab-7-14-released/) in GitLab 7.14.
+> - GitLab 8.12 has a completely redesigned job permissions system. Read all
+> about the [new model and its implications](../../user/project/new_ci_build_permissions_model.md#job-triggers).
Triggers can be used to force a pipeline rerun of a specific `ref` (branch or
tag) with an API call.
@@ -49,11 +50,12 @@ The action is irreversible.
## Triggering a pipeline
> **Notes**:
-- Valid refs are only the branches and tags. If you pass a commit SHA as a ref,
- it will not trigger a job.
-- If your project is public, passing the token in plain text is probably not the
- wisest idea, so you might want to use a
- [variable](../variables/README.md#variables) for that purpose.
+>
+> - Valid refs are only the branches and tags. If you pass a commit SHA as a ref,
+> it will not trigger a job.
+> - If your project is public, passing the token in plain text is probably not the
+> wisest idea, so you might want to use a
+> [variable](../variables/README.md#variables) for that purpose.
To trigger a job you need to send a `POST` request to GitLab's API endpoint:
@@ -122,11 +124,12 @@ Now, whenever a new tag is pushed on project A, the job will run and the
## Triggering a pipeline from a webhook
> **Notes**:
-- Introduced in GitLab 8.14.
-- `ref` should be passed as part of the URL in order to take precedence over
- `ref` from the webhook body that designates the branch ref that fired the
- trigger in the source repository.
-- `ref` should be URL-encoded if it contains slashes.
+>
+> - Introduced in GitLab 8.14.
+> - `ref` should be passed as part of the URL in order to take precedence over
+> `ref` from the webhook body that designates the branch ref that fired the
+> trigger in the source repository.
+> - `ref` should be URL-encoded if it contains slashes.
To trigger a job from a webhook of another project you need to add the following
webhook URL for Push and Tag events (change the project ID, ref and token):
@@ -151,10 +154,10 @@ This information is also exposed in the UI.
Using trigger variables can be proven useful for a variety of reasons:
-* Identifiable jobs. Since the variable is exposed in the UI you can know
+- Identifiable jobs. Since the variable is exposed in the UI you can know
why the rebuild was triggered if you pass a variable that explains the
purpose.
-* Conditional job processing. You can have conditional jobs that run whenever
+- Conditional job processing. You can have conditional jobs that run whenever
a certain variable is present.
Consider the following `.gitlab-ci.yml` where we set three
@@ -218,7 +221,6 @@ removed with one of the future versions of GitLab. You are advised to
[take ownership](#taking-ownership) of any legacy triggers.
[ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017
-[ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229
[ee]: https://about.gitlab.com/pricing/
[variables]: ../variables/README.md
[predef]: ../variables/README.md#predefined-variables-environment-variables
diff --git a/doc/ci/triggers/img/builds_page.png b/doc/ci/triggers/img/builds_page.png
index c9cc8f308f4..14d73b140f4 100644
--- a/doc/ci/triggers/img/builds_page.png
+++ b/doc/ci/triggers/img/builds_page.png
Binary files differ
diff --git a/doc/ci/triggers/img/trigger_single_build.png b/doc/ci/triggers/img/trigger_single_build.png
index 837bbeffe9f..b760782afdc 100644
--- a/doc/ci/triggers/img/trigger_single_build.png
+++ b/doc/ci/triggers/img/trigger_single_build.png
Binary files differ
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 115e6e390c9..bdbcf8c9435 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -34,7 +34,7 @@ Some of the predefined environment variables are available only if a minimum
version of [GitLab Runner][runner] is used. Consult the table below to find the
version of Runner required.
->**Note:**
+NOTE: **Note:**
Starting with GitLab 9.0, we have deprecated some variables. Read the
[9.0 Renaming](#9-0-renaming) section to find out their replacements. **You are
strongly advised to use the new variables as we will remove the old ones in
@@ -65,6 +65,8 @@ future GitLab releases.**
| **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` |
| **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry][registry] and downloading [dependent repositories][dependent-repositories] |
+| **CI_NODE_INDEX** | 11.5 | all | Index of the job in the job set. If the job is not parallelized, this variable is not set. |
+| **CI_NODE_TOTAL** | 11.5 | all | Total number of instances of this job running in parallel. If the job is not parallelized, this variable is set to `1`. |
| **CI_JOB_URL** | 11.1 | 0.5 | Job details URL |
| **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository |
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
@@ -94,6 +96,9 @@ future GitLab releases.**
| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs |
| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs |
| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs |
+| **CI_SERVER_VERSION_MAJOR** | 11.4 | all | GitLab version major component |
+| **CI_SERVER_VERSION_MINOR** | 11.4 | all | GitLab version minor component |
+| **CI_SERVER_VERSION_PATCH** | 11.4 | all | GitLab version patch component |
| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. |
| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job |
| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment |
@@ -109,7 +114,7 @@ To follow conventions of naming across GitLab, and to further move away from the
`build` term and toward `job` CI variables have been renamed for the 9.0
release.
->**Note:**
+NOTE: **Note:**
Starting with GitLab 9.0, we have deprecated the `$CI_BUILD_*` variables. **You are
strongly advised to use the new variables as we will remove the old ones in
future GitLab releases.**
@@ -131,7 +136,7 @@ future GitLab releases.**
## `.gitlab-ci.yml` defined variables
->**Note:**
+NOTE **Note:**
This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher.
GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in the
@@ -194,7 +199,7 @@ Likewise, group-level variables can be added by going to your group's
**Settings > CI/CD**, then finding the section called **Variables**.
Any variables of [subgroups] will be inherited recursively.
-![Variables](img/secret_variables.png)
+![Variables](img/variables.png)
Once you set them, they will be available for all subsequent pipelines. You can also
[protect your variables](#protected-variables).
@@ -215,9 +220,15 @@ Protected variables can be added by going to your project's
Once you set them, they will be available for all subsequent pipelines.
+### Manually-specified variables
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/44059) in GitLab 10.8.
+
+Variables can be specified for a single pipeline run when a [manual pipeline](../pipelines.md#manually-executing-pipelines) is created.
+
## Deployment variables
->**Note:**
+NOTE: **Note:**
This feature requires GitLab CI 8.15 or higher.
[Project services](../../user/project/integrations/project_services.md) that are
@@ -317,6 +328,12 @@ Running on runner-8a2f473d-project-1796893-concurrent-0 via runner-8a2f473d-mach
++ CI_SERVER_NAME='GitLab CI'
++ export CI_SERVER_VERSION=
++ CI_SERVER_VERSION=
+++ export CI_SERVER_VERSION_MAJOR=
+++ CI_SERVER_VERSION_MAJOR=
+++ export CI_SERVER_VERSION_MINOR=
+++ CI_SERVER_VERSION_MINOR=
+++ export CI_SERVER_VERSION_PATCH=
+++ CI_SERVER_VERSION_PATCH=
++ export CI_SERVER_REVISION=
++ CI_SERVER_REVISION=
++ export GITLAB_CI=true
@@ -462,6 +479,9 @@ export CI_SERVER="yes"
export CI_SERVER_NAME="GitLab"
export CI_SERVER_REVISION="70606bf"
export CI_SERVER_VERSION="8.9.0"
+export CI_SERVER_VERSION_MAJOR="8"
+export CI_SERVER_VERSION_MINOR="9"
+export CI_SERVER_VERSION_PATCH="0"
export GITLAB_USER_ID="42"
export GITLAB_USER_EMAIL="user@example.com"
export CI_REGISTRY_USER="gitlab-ci-token"
@@ -567,4 +587,4 @@ Below you can find supported syntax reference:
[builds-policies]: ../yaml/README.md#only-and-except-complex
[gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token
[registry]: ../../user/project/container_registry.md
-[dependent-repositories]: ../../user/project/new_ci_build_permissions_model.md#dependent-repositories \ No newline at end of file
+[dependent-repositories]: ../../user/project/new_ci_build_permissions_model.md#dependent-repositories
diff --git a/doc/ci/variables/img/secret_variables.png b/doc/ci/variables/img/secret_variables.png
deleted file mode 100644
index 3c1aa361dc2..00000000000
--- a/doc/ci/variables/img/secret_variables.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/variables/img/variables.png b/doc/ci/variables/img/variables.png
new file mode 100644
index 00000000000..d2dc99bbac0
--- /dev/null
+++ b/doc/ci/variables/img/variables.png
Binary files differ
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index b2b4a26bdda..1d98e8426fe 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -8,17 +8,17 @@ This document describes where and how the different types of variables can be us
## Variables usage
-There are basically two places where you can use any defined variables:
+There are two places defined variables can be used. On the:
-1. On GitLab's side there's `.gitlab-ci.yml`
-1. On the Runner's side there's `config.toml`
+1. GitLab side, in `.gitlab-ci.yml`.
+1. The runner side, in `config.toml`.
### `.gitlab-ci.yml` file
| Definition | Can be expanded? | Expansion place | Description |
|--------------------------------------|-------------------|-----------------|--------------|
-| `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<ul><li>**Supported:** all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules)</li><li>**Not suported:** variables defined in Runner's `config.toml` and variables created in job's `script`</li></ul> |
-| `environment:name` | yes | GitLab | Similar to `environment:url`, but the variables expansion **doesn't support**: <ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
+| `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<ul><li>Supported: all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules)</li><li>Not supported: variables defined in Runner's `config.toml` and variables created in job's `script`</li></ul> |
+| `environment:name` | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support: <ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
| `variables` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `image` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `services:[]` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
@@ -26,7 +26,7 @@ There are basically two places where you can use any defined variables:
| `cache:key` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `artifacts:name` | yes | Runner | The variable expansion is made by GitLab Runner's shell environment |
| `script`, `before_script`, `after_script` | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment) |
-| `only:variables:[]`, `except:variables:[]` | no | n/a | The variable must be in the form of `$variable`.<br/>**Not supported:**<ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
+| `only:variables:[]`, `except:variables:[]` | no | n/a | The variable must be in the form of `$variable`.<br/>Not supported:<ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
### `config.toml` file
@@ -55,9 +55,9 @@ since the expansion is done in GitLab before any Runner will get the job.
### GitLab Runner internal variable expansion mechanism
-- **Supported:** project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and
- variables from triggers and pipeline schedules
-- **Not supported:** variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`)
+- Supported: project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and
+ variables from triggers, pipeline schedules, and manual pipelines.
+- Not supported: variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`).
The Runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle
only variables defined as `$variable` and `${variable}`. What's also important, is that
@@ -73,18 +73,17 @@ by bash/sh (leaving empty strings or some values depending whether the variables
defined or not), but will not work with Windows' cmd/PowerShell, since these shells
are using a different variables syntax.
-**Supported:**
+Supported:
- The `script` may use all available variables that are default for the shell (e.g., `$PATH` which
should be present in all bash/sh shells) and all variables defined by GitLab CI/CD (project/group variables,
`.gitlab-ci.yml` variables, `config.toml` variables, and variables from triggers and pipeline schedules).
- The `script` may also use all variables defined in the lines before. So, for example, if you define
a variable `export MY_VARIABLE="test"`:
-
- - in `before_script`, it will work in the following lines of `before_script` and
- all lines of the related `script`
- - in `script`, it will work in the following lines of `script`
- - in `after_script`, it will work in following lines of `after_script`
+ - In `before_script`, it will work in the following lines of `before_script` and
+ all lines of the related `script`.
+ - In `script`, it will work in the following lines of `script`.
+ - In `after_script`, it will work in following lines of `after_script`.
## Persisted variables
@@ -107,7 +106,9 @@ The following variables are known as "persisted":
They are:
-- **supported** for all definitions as [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "Runner"
-- **not supported:**
- - by the definitions [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "GitLab"
- - in the `only` and `except` [variables expressions](README.md#variables-expressions)
+- Supported for definitions where the ["Expansion place"](#gitlab-ci-yml-file) is:
+ - Runner.
+ - Script execution shell.
+- Not supported:
+ - For definitions where the ["Expansion place"](#gitlab-ci-yml-file) is GitLab.
+ - In the `only` and `except` [variables expressions](README.md#variables-expressions).
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 95d705d3a3d..44eec43ef54 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -56,6 +56,7 @@ A job is defined by a list of parameters that define the job behavior.
| Keyword | Required | Description |
|---------------|----------|-------------|
| script | yes | Defines a shell script which is executed by Runner |
+| extends | no | Defines a configuration entry that this job is going to inherit from |
| image | no | Use docker image, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) |
| services | no | Use docker services, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) |
| stage | no | Defines a job stage (default: `test`) |
@@ -73,7 +74,91 @@ A job is defined by a list of parameters that define the job behavior.
| after_script | no | Override a set of commands that are executed after job |
| environment | no | Defines a name of environment to which deployment is done by this job |
| coverage | no | Define code coverage settings for a given job |
-| retry | no | Define how many times a job can be auto-retried in case of a failure |
+| retry | no | Define when and how many times a job can be auto-retried in case of a failure |
+| parallel | no | Defines how many instances of a job should be run in parallel |
+
+### `extends`
+
+> Introduced in GitLab 11.3.
+
+`extends` defines an entry name that a job that uses `extends` is going to
+inherit from.
+
+It is an alternative to using [YAML anchors](#anchors) and is a little
+more flexible and readable:
+
+```yaml
+.tests:
+ script: rake test
+ stage: test
+ only:
+ refs:
+ - branches
+
+rspec:
+ extends: .tests
+ script: rake rspec
+ only:
+ variables:
+ - $RSPEC
+```
+
+In the example above, the `rspec` job inherits from the `.tests` template job.
+GitLab will perform a reverse deep merge based on the keys. GitLab will:
+
+- Merge the `rspec` contents into `.tests` recursively.
+- Not merge the values of the keys.
+
+This results in the following `rspec` job:
+
+```yaml
+rspec:
+ script: rake rspec
+ stage: test
+ only:
+ refs:
+ - branches
+ variables:
+ - $RSPEC
+```
+
+NOTE: **Note:**
+Note that `script: rake test` has been overwritten by `script: rake rspec`.
+
+If you do want to include the `rake test`, have a look at [before_script-and-after_script](#before_script-and-after_script).
+
+`.tests` in this example is a [hidden key](#hidden-keys-jobs), but it's
+possible to inherit from regular jobs as well.
+
+`extends` supports multi-level inheritance, however it is not recommended to
+use more than three levels. The maximum nesting level that is supported is 10.
+The following example has two levels of inheritance:
+
+```yaml
+.tests:
+ only:
+ - pushes
+
+.rspec:
+ extends: .tests
+ script: rake rspec
+
+rspec 1:
+ variables:
+ RSPEC_SUITE: '1'
+ extends: .rspec
+
+rspec 2:
+ variables:
+ RSPEC_SUITE: '2'
+ extends: .rspec
+
+spinach:
+ extends: .tests
+ script: rake spinach
+```
+
+`extends` works across configuration files combined with [`include`](#include).
### `pages`
@@ -112,7 +197,7 @@ used for time of the job. The configuration of this feature is covered in
## `before_script` and `after_script`
-> Introduced in GitLab 8.7 and requires Gitlab Runner v1.2
+> Introduced in GitLab 8.7 and requires GitLab Runner v1.2
`before_script` is used to define the command that should be run before all
jobs, including deploy jobs, but after the restoration of [artifacts](#artifacts).
@@ -309,8 +394,10 @@ except master.
## `only` and `except` (complex)
> `refs` and `kubernetes` policies introduced in GitLab 10.0
-
+>
> `variables` policy introduced in 10.7
+>
+> `changes` policy [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/19232) in 11.4
CAUTION: **Warning:**
This an _alpha_ feature, and it it subject to change at any time without
@@ -322,10 +409,15 @@ policy configuration.
GitLab now supports both, simple and complex strategies, so it is possible to
use an array and a hash configuration scheme.
-Three keys are now available: `refs`, `kubernetes` and `variables`.
+Four keys are now available: `refs`, `kubernetes` and `variables` and `changes`.
+
+### `refs` and `kubernetes`
+
Refs strategy equals to simplified only/except configuration, whereas
kubernetes strategy accepts only `active` keyword.
+### `only:variables`
+
`variables` keyword is used to define variables expressions. In other words
you can use predefined variables / project / group or
environment-scoped variables to define an expression GitLab is going to
@@ -369,6 +461,50 @@ end-to-end:
Learn more about variables expressions on [a separate page][variables-expressions].
+### `only:changes`
+
+Using `changes` keyword with `only` or `except` makes it possible to define if
+a job should be created based on files modified by a git push event.
+
+For example:
+
+```yaml
+docker build:
+ script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
+ only:
+ changes:
+ - Dockerfile
+ - docker/scripts/*
+ - dockerfiles/**/*
+ - more_scripts/*.{rb,py,sh}
+```
+
+In the scenario above, if you are pushing multiple commits to GitLab to an
+existing branch, GitLab creates and triggers `docker build` job, provided that
+one of the commits contains changes to either:
+
+- The `Dockerfile` file.
+- Any of the files inside `docker/scripts/` directory.
+- Any of the files and subfolders inside `dockerfiles` directory.
+- Any of the files with `rb`, `py`, `sh` extensions inside `more_scripts` directory.
+
+CAUTION: **Warning:**
+There are some caveats when using this feature with new branches and tags. See
+the section below.
+
+#### Using `changes` with new branches and tags
+
+If you are pushing a **new** branch or a **new** tag to GitLab, the policy
+always evaluates to true and GitLab will create a job. This feature is not
+connected with merge requests yet, and because GitLab is creating pipelines
+before an user can create a merge request we don't know a target branch at
+this point.
+
+Without a target branch, it is not possible to know what the common ancestor is,
+thus we always create a job in that case. This feature works best for stable
+branches like `master` because in that case GitLab uses the previous commit
+that is present in a branch to compare against the latest SHA that was pushed.
+
## `tags`
`tags` is used to select specific Runners from the list of all Runners that are
@@ -390,10 +526,33 @@ job:
The specification above, will make sure that `job` is built by a Runner that
has both `ruby` AND `postgres` tags defined.
+Tags are also a great way to run different jobs on different platforms, for
+example, given an OS X Runner with tag `osx` and Windows Runner with tag
+`windows`, the following jobs run on respective platforms:
+
+```yaml
+windows job:
+ stage:
+ - build
+ tags:
+ - windows
+ script:
+ - echo Hello, %USERNAME%!
+
+osx job:
+ stage:
+ - build
+ tags:
+ - osx
+ script:
+ - echo "Hello, $USER!"
+```
+
## `allow_failure`
`allow_failure` is used when you want to allow a job to fail without impacting
the rest of the CI suite. Failed jobs don't contribute to the commit status.
+The default value is `false`.
When enabled and the job fails, the pipeline will be successful/green for all
intents and purposes, but a "CI build passed with warnings" message will be
@@ -487,9 +646,10 @@ The above script will:
### `when:manual`
> **Notes:**
-- Introduced in GitLab 8.10.
-- Blocking manual actions were introduced in GitLab 9.0.
-- Protected actions were introduced in GitLab 9.2.
+>
+> - Introduced in GitLab 8.10.
+> - Blocking manual actions were introduced in GitLab 9.0.
+> - Protected actions were introduced in GitLab 9.2.
Manual actions are a special type of job that are not executed automatically,
they need to be explicitly started by a user. An example usage of manual actions
@@ -518,13 +678,49 @@ user wants to trigger an action. In other words, in order to trigger a manual
action assigned to a branch that the pipeline is running for, user needs to
have ability to merge to this branch.
+### `when:delayed`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21767) in GitLab 11.4.
+
+Delayed job are for executing scripts after a certain period.
+This is useful if you want to avoid jobs entering `pending` state immediately.
+
+You can set the period with `start_in` key. The value of `start_in` key is an elapsed time in seconds, unless a unit is
+provided. `start_in` key must be less than or equal to one hour. Examples of valid values include:
+
+- `10 seconds`
+- `30 minutes`
+- `1 hour`
+
+When there is a delayed job in a stage, the pipeline will not progress until the delayed job has finished.
+This means this keyword can also be used for inserting delays between different stages.
+
+The timer of a delayed job starts immediately after the previous stage has completed.
+Similar to other types of jobs, a delayed job's timer will not start unless the previous stage passed.
+
+The following example creates a job named `timed rollout 10%` that is executed 30 minutes after the previous stage has completed:
+
+```yaml
+timed rollout 10%:
+ stage: deploy
+ script: echo 'Rolling out 10% ...'
+ when: delayed
+ start_in: 30 minutes
+```
+
+You can stop the active timer of a delayed job by clicking the **Unschedule** button.
+This job will never be executed in the future unless you execute the job manually.
+
+You can start a delayed job immediately by clicking the **Play** button.
+GitLab runner will pick your job soon and start the job.
+
## `environment`
+> **Notes:**
>
-**Notes:**
-- Introduced in GitLab 8.9.
-- You can read more about environments and find more examples in the
- [documentation about environments][environment].
+> - Introduced in GitLab 8.9.
+> - You can read more about environments and find more examples in the
+> [documentation about environments][environment].
`environment` is used to define that a job deploys to a specific environment.
If `environment` is specified and no environment under that name exists, a new
@@ -545,15 +741,15 @@ deployment to the `production` environment.
### `environment:name`
+> **Notes:**
>
-**Notes:**
-- Introduced in GitLab 8.11.
-- Before GitLab 8.11, the name of an environment could be defined as a string like
- `environment: production`. The recommended way now is to define it under the
- `name` keyword.
-- The `name` parameter can use any of the defined CI variables,
- including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
- You however cannot use variables defined under `script`.
+> - Introduced in GitLab 8.11.
+> - Before GitLab 8.11, the name of an environment could be defined as a string like
+> `environment: production`. The recommended way now is to define it under the
+> `name` keyword.
+> - The `name` parameter can use any of the defined CI variables,
+> including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
+> You however cannot use variables defined under `script`.
The `environment` name can contain:
@@ -584,14 +780,14 @@ deploy to production:
### `environment:url`
+> **Notes:**
>
-**Notes:**
-- Introduced in GitLab 8.11.
-- Before GitLab 8.11, the URL could be added only in GitLab's UI. The
- recommended way now is to define it in `.gitlab-ci.yml`.
-- The `url` parameter can use any of the defined CI variables,
- including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
- You however cannot use variables defined under `script`.
+> - Introduced in GitLab 8.11.
+> - Before GitLab 8.11, the URL could be added only in GitLab's UI. The
+> recommended way now is to define it in `.gitlab-ci.yml`.
+> - The `url` parameter can use any of the defined CI variables,
+> including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
+> You however cannot use variables defined under `script`.
This is an optional value that when set, it exposes buttons in various places
in GitLab which when clicked take you to the defined URL.
@@ -611,14 +807,14 @@ deploy to production:
### `environment:on_stop`
+> **Notes:**
>
-**Notes:**
-- [Introduced][ce-6669] in GitLab 8.13.
-- Starting with GitLab 8.14, when you have an environment that has a stop action
- defined, GitLab will automatically trigger a stop action when the associated
- branch is deleted.
+> - [Introduced][ce-6669] in GitLab 8.13.
+> - Starting with GitLab 8.14, when you have an environment that has a stop action
+> defined, GitLab will automatically trigger a stop action when the associated
+> branch is deleted.
-Closing (stoping) environments can be achieved with the `on_stop` keyword defined under
+Closing (stopping) environments can be achieved with the `on_stop` keyword defined under
`environment`. It declares a different job that runs in order to close
the environment.
@@ -667,13 +863,13 @@ The `stop_review_app` job is **required** to have the following keywords defined
### Dynamic environments
+> **Notes:**
>
-**Notes:**
-- [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6.
-- The `$CI_ENVIRONMENT_SLUG` was [introduced][ce-7983] in GitLab 8.15.
-- The `name` and `url` parameters can use any of the defined CI variables,
- including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
- You however cannot use variables defined under `script`.
+> - [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6.
+> - The `$CI_ENVIRONMENT_SLUG` was [introduced][ce-7983] in GitLab 8.15.
+> - The `name` and `url` parameters can use any of the defined CI variables,
+> including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
+> You however cannot use variables defined under `script`.
For example:
@@ -703,13 +899,13 @@ as Review Apps. You can see a simple example using Review Apps at
## `cache`
+> **Notes:**
>
-**Notes:**
-- Introduced in GitLab Runner v0.7.0.
-- `cache` can be set globally and per-job.
-- From GitLab 9.0, caching is enabled and shared between pipelines and jobs
- by default.
-- From GitLab 9.2, caches are restored before [artifacts](#artifacts).
+> - Introduced in GitLab Runner v0.7.0.
+> - `cache` can be set globally and per-job.
+> - From GitLab 9.0, caching is enabled and shared between pipelines and jobs
+> by default.
+> - From GitLab 9.2, caches are restored before [artifacts](#artifacts).
TIP: **Learn more:**
Read how caching works and find out some good practices in the
@@ -871,13 +1067,13 @@ skip the download step.
## `artifacts`
+> **Notes:**
>
-**Notes:**
-- Introduced in GitLab Runner v0.7.0 for non-Windows platforms.
-- Windows support was added in GitLab Runner v.1.0.0.
-- From GitLab 9.2, caches are restored before artifacts.
-- Not all executors are [supported](https://docs.gitlab.com/runner/executors/#compatibility-chart).
-- Job artifacts are only collected for successful jobs by default.
+> - Introduced in GitLab Runner v0.7.0 for non-Windows platforms.
+> - Windows support was added in GitLab Runner v.1.0.0.
+> - From GitLab 9.2, caches are restored before artifacts.
+> - Not all executors are [supported](https://docs.gitlab.com/runner/executors/#compatibility-chart).
+> - Job artifacts are only collected for successful jobs by default.
`artifacts` is used to specify a list of files and directories which should be
attached to the job after success.
@@ -1075,8 +1271,10 @@ keep artifacts forever.
After their expiry, artifacts are deleted hourly by default (via a cron job),
and are not accessible anymore.
-The value of `expire_in` is an elapsed time. Examples of parsable values:
+The value of `expire_in` is an elapsed time in seconds, unless a unit is
+provided. Examples of parsable values:
+- '42'
- '3 mins 4 sec'
- '2 hrs 20 min'
- '2h20min'
@@ -1092,6 +1290,130 @@ job:
expire_in: 1 week
```
+### `artifacts:reports`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20390) in
+GitLab 11.2. Requires GitLab Runner 11.2 and above.
+
+The `reports` keyword is used for collecting test reports from jobs and
+exposing them in GitLab's UI (merge requests, pipeline views). Read how to use
+this with [JUnit reports](#artifacts-reports-junit).
+
+NOTE: **Note:**
+The test reports are collected regardless of the job results (success or failure).
+You can use [`artifacts:expire_in`](#artifacts-expire_in) to set up an expiration
+date for their artifacts.
+
+#### `artifacts:reports:junit`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20390) in
+GitLab 11.2. Requires GitLab Runner 11.2 and above.
+
+The `junit` report collects [JUnit XML files](https://www.ibm.com/support/knowledgecenter/en/SSQ2R2_14.1.0/com.ibm.rsar.analysis.codereview.cobol.doc/topics/cac_useresults_junit.html)
+as artifacts. Although JUnit was originally developed in Java, there are many
+[third party ports](https://en.wikipedia.org/wiki/JUnit#Ports) for other
+languages like Javascript, Python, Ruby, etc.
+
+Below is an example of collecting a JUnit XML file from Ruby's RSpec test tool:
+
+```yaml
+rspec:
+ stage: test
+ script:
+ - bundle install
+ - rspec --format RspecJunitFormatter --out rspec.xml
+ artifacts:
+ reports:
+ junit: rspec.xml
+```
+
+The collected JUnit reports will be uploaded to GitLab as an artifact and will
+be automatically shown in merge requests.
+
+For more examples, see [JUnit test reports](../junit_test_reports.md).
+
+NOTE: **Note:**
+In case the JUnit tool you use exports to multiple XML files, you can specify
+multiple test report paths within a single job and they will be automatically
+concatenated into a single file. Use a filename pattern (`junit: rspec-*.xml`),
+an array of filenames (`junit: [rspec-1.xml, rspec-2.xml, rspec-3.xml]`), or a
+combination thereof (`junit: [rspec.xml, test-results/TEST-*.xml]`).
+
+#### `artifacts:reports:codequality` **[STARTER]**
+
+> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
+
+The `codequality` report collects [CodeQuality issues](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html)
+as artifacts.
+
+The collected Code Quality report will be uploaded to GitLab as an artifact and will
+be automatically shown in merge requests.
+
+#### `artifacts:reports:sast` **[ULTIMATE]**
+
+> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
+
+The `sast` report collects [SAST vulnerabilities](https://docs.gitlab.com/ee/user/project/merge_requests/sast.html)
+as artifacts.
+
+The collected SAST report will be uploaded to GitLab as an artifact and will
+be automatically shown in merge requests, pipeline view and provide data for security
+dashboards.
+
+#### `artifacts:reports:dependency_scanning` **[ULTIMATE]**
+
+> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
+
+The `dependency_scanning` report collects [Dependency Scanning vulnerabilities](https://docs.gitlab.com/ee/user/project/merge_requests/dependency_scanning.html)
+as artifacts.
+
+The collected Dependency Scanning report will be uploaded to GitLab as an artifact and will
+be automatically shown in merge requests, pipeline view and provide data for security
+dashboards.
+
+#### `artifacts:reports:container_scanning` **[ULTIMATE]**
+
+> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
+
+The `container_scanning` report collects [Container Scanning vulnerabilities](https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html)
+as artifacts.
+
+The collected Container Scanning report will be uploaded to GitLab as an artifact and will
+be automatically shown in merge requests, pipeline view and provide data for security
+dashboards.
+
+#### `artifacts:reports:dast` **[ULTIMATE]**
+
+> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
+
+The `dast` report collects [DAST vulnerabilities](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html)
+as artifacts.
+
+The collected DAST report will be uploaded to GitLab as an artifact and will
+be automatically shown in merge requests, pipeline view and provide data for security
+dashboards.
+
+#### `artifacts:reports:license_management` **[ULTIMATE]**
+
+> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
+
+The `license_management` report collects [Licenses](https://docs.gitlab.com/ee/user/project/merge_requests/license_management.html)
+as artifacts.
+
+The collected License Management report will be uploaded to GitLab as an artifact and will
+be automatically shown in merge requests, pipeline view and provide data for security
+dashboards.
+
+#### `artifacts:reports:performance` **[PREMIUM]**
+
+> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
+
+The `performance` report collects [Performance metrics](https://docs.gitlab.com/ee//user/project/merge_requests/browser_performance_testing.html)
+as artifacts.
+
+The collected Performance report will be uploaded to GitLab as an artifact and will
+be automatically shown in merge requests.
+
## `dependencies`
> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
@@ -1188,18 +1510,20 @@ job1:
## `retry`
> [Introduced][ce-12909] in GitLab 9.5.
+> [Behaviour expanded](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21758)
+> in GitLab 11.5 to control on which failures to retry.
`retry` allows you to configure how many times a job is going to be retried in
case of a failure.
-When a job fails, and has `retry` configured it is going to be processed again
+When a job fails and has `retry` configured, it is going to be processed again
up to the amount of times specified by the `retry` keyword.
If `retry` is set to 2, and a job succeeds in a second run (first retry), it won't be retried
again. `retry` value has to be a positive integer, equal or larger than 0, but
lower or equal to 2 (two retries maximum, three runs in total).
-A simple example:
+A simple example to retry in all failure cases:
```yaml
test:
@@ -1207,6 +1531,264 @@ test:
retry: 2
```
+By default, a job will be retried on all failure cases. To have a better control
+on which failures to retry, `retry` can be a hash with with the following keys:
+
+- `max`: The maximum number of retries.
+- `when`: The failure cases to retry.
+
+To retry only runner system failures at maximum two times:
+
+```yaml
+test:
+ script: rspec
+ retry:
+ max: 2
+ when: runner_system_failure
+```
+
+If there is another failure, other than a runner system failure, the job will
+not be retried.
+
+To retry on multiple failure cases, `when` can also be an array of failures:
+
+```yaml
+test:
+ script: rspec
+ retry:
+ max: 2
+ when:
+ - runner_system_failure
+ - stuck_or_timeout_failure
+```
+
+Possible values for `when` are:
+
+<!--
+ Please make sure to update `RETRY_WHEN_IN_DOCUMENTATION` array in
+ `spec/lib/gitlab/ci/config/entry/retry_spec.rb` if you change any of
+ the documented values below. The test there makes sure that all documented
+ values are really valid as a config option and therefore should always
+ stay in sync with this documentation.
+ -->
+
+- `always`: Retry on any failure (default).
+- `unknown_failure`: Retry when the failure reason is unknown.
+- `script_failure`: Retry when the script failed.
+- `api_failure`: Retry on API failure.
+- `stuck_or_timeout_failure`: Retry when the job got stuck or timed out.
+- `runner_system_failure`: Retry if there was a runner system failure (e.g. setting up the job failed).
+- `missing_dependency_failure`: Retry if a dependency was missing.
+- `runner_unsupported`: Retry if the runner was unsupported.
+
+
+## `parallel`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22631) in GitLab 11.5.
+
+`parallel` allows you to configure how many instances of a job to run in
+parallel. This value has to be greater than or equal to two (2) and less or equal than 50.
+
+This creates N instances of the same job that run in parallel. They're named
+sequentially from `job_name 1/N` to `job_name N/N`.
+
+For every job, `CI_NODE_INDEX` and `CI_NODE_TOTAL` [environment variables](../variables/README.html#predefined-variables-environment-variables) are set.
+
+A simple example:
+
+```yaml
+test:
+ script: rspec
+ parallel: 5
+```
+
+## `include`
+
+> Introduced in [GitLab Premium](https://about.gitlab.com/pricing/) 10.5.
+> Available for Starter, Premium and Ultimate since 10.6.
+> Behaviour expanded in GitLab 10.8 to allow more flexible overriding.
+> [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21603)
+to GitLab Core in 11.4
+
+Using the `include` keyword, you can allow the inclusion of external YAML files.
+
+In the following example, the content of `.before-script-template.yml` will be
+automatically fetched and evaluated along with the content of `.gitlab-ci.yml`:
+
+```yaml
+# Content of https://gitlab.com/awesome-project/raw/master/.before-script-template.yml
+
+before_script:
+ - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
+ - gem install bundler --no-ri --no-rdoc
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
+```
+
+```yaml
+# Content of .gitlab-ci.yml
+
+include: 'https://gitlab.com/awesome-project/raw/master/.before-script-template.yml'
+
+rspec:
+ script:
+ - bundle exec rspec
+```
+
+You can define it either as a single string, or, in case you want to include
+more than one files, an array of different values . The following examples
+are both valid cases:
+
+```yaml
+# Single string
+
+include: '/templates/.after-script-template.yml'
+```
+
+```yaml
+# Array
+
+include:
+ - 'https://gitlab.com/awesome-project/raw/master/.before-script-template.yml'
+ - '/templates/.after-script-template.yml'
+```
+
+---
+
+`include` supports two types of files:
+
+- **local** to the same repository, referenced by using full paths in the same
+ repository, with `/` being the root directory. For example:
+
+ ```yaml
+ # Within the repository
+ include: '/templates/.gitlab-ci-template.yml'
+ ```
+
+ NOTE: **Note:**
+ You can only use files that are currently tracked by Git on the same branch
+ your configuration file is. In other words, when using a **local file**, make
+ sure that both `.gitlab-ci.yml` and the local file are on the same branch.
+
+ NOTE: **Note:**
+ We don't support the inclusion of local files through Git submodules paths.
+
+- **remote** in a different location, accessed using HTTP/HTTPS, referenced
+ using the full URL. For example:
+
+ ```yaml
+ include: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
+ ```
+
+ NOTE: **Note:**
+ The remote file must be publicly accessible through a simple GET request, as we don't support authentication schemas in the remote URL.
+
+ NOTE: **Note:**
+ In order to include files from another repository inside your local network,
+ you may need to enable the **Allow requests to the local network from hooks and services** checkbox
+ located in the **Settings > Network > Outbound requests** section within the **Admin area**.
+
+---
+
+
+Since GitLab 10.8 we are now recursively merging the files defined in `include`
+with those in `.gitlab-ci.yml`. Files defined by `include` are always
+evaluated first and recursively merged with the content of `.gitlab-ci.yml`, no
+matter the position of the `include` keyword. You can take advantage of
+recursive merging to customize and override details in included CI
+configurations with local definitions.
+
+The following example shows specific YAML-defined variables and details of the
+`production` job from an include file being customized in `.gitlab-ci.yml`.
+
+```yaml
+# Content of https://company.com/autodevops-template.yml
+
+variables:
+ POSTGRES_USER: user
+ POSTGRES_PASSWORD: testing_password
+ POSTGRES_DB: $CI_ENVIRONMENT_SLUG
+
+production:
+ stage: production
+ script:
+ - install_dependencies
+ - deploy
+ environment:
+ name: production
+ url: https://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
+ only:
+ - master
+```
+
+```yaml
+# Content of .gitlab-ci.yml
+
+include: 'https://company.com/autodevops-template.yml'
+
+image: alpine:latest
+
+variables:
+ POSTGRES_USER: root
+ POSTGRES_PASSWORD: secure_password
+
+stages:
+ - build
+ - test
+ - production
+
+production:
+ environment:
+ url: https://domain.com
+```
+
+In this case, the variables `POSTGRES_USER` and `POSTGRES_PASSWORD` along
+with the environment url of the `production` job defined in
+`autodevops-template.yml` have been overridden by new values defined in
+`.gitlab-ci.yml`.
+
+NOTE: **Note:**
+Recursive includes are not supported meaning your external files
+should not use the `include` keyword, as it will be ignored.
+
+Recursive merging lets you extend and override dictionary mappings, but
+you cannot add or modify items to an included array. For example, to add
+an additional item to the production job script, you must repeat the
+existing script items.
+
+```yaml
+# Content of https://company.com/autodevops-template.yml
+
+production:
+ stage: production
+ script:
+ - install_dependencies
+ - deploy
+```
+
+```yaml
+# Content of .gitlab-ci.yml
+
+include: 'https://company.com/autodevops-template.yml'
+
+stages:
+ - production
+
+production:
+ script:
+ - install_dependencies
+ - deploy
+ - notify_owner
+```
+
+In this case, if `install_dependencies` and `deploy` were not repeated in
+`.gitlab-ci.yml`, they would not be part of the script for the `production`
+job in the combined CI configuration.
+
+NOTE: **Note:**
+We currently do not support using YAML aliases across different YAML files
+sourced by `include`. You must only refer to aliases in the same file. Instead
+of using YAML anchors you can use [`extends` keyword](#extends).
+
## `variables`
> Introduced in GitLab Runner v0.5.0.
@@ -1311,7 +1893,9 @@ There are three possible values: `none`, `normal`, and `recursive`:
```
- `recursive` means that all submodules (including submodules of submodules)
- will be included. It is equivalent to:
+ will be included. This feature needs Git v1.8.1 and later. When using a
+ GitLab Runner with an executor not based on Docker, make sure the Git version
+ meets that requirement. It is equivalent to:
```
git submodule sync --recursive
@@ -1606,3 +2190,5 @@ CI with various languages.
[ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909
[schedules]: ../../user/project/pipelines/schedules.md
[variables-expressions]: ../variables/README.md#variables-expressions
+[ee]: https://about.gitlab.com/gitlab-ee/
+[gitlab-versions]: https://about.gitlab.com/products/
diff --git a/doc/customization/branded_login_page/custom_sign_in.png b/doc/customization/branded_login_page/custom_sign_in.png
index c0888fe1f18..03ea5281ebe 100644
--- a/doc/customization/branded_login_page/custom_sign_in.png
+++ b/doc/customization/branded_login_page/custom_sign_in.png
Binary files differ
diff --git a/doc/customization/branded_page_and_email_header/appearance.png b/doc/customization/branded_page_and_email_header/appearance.png
index abbba6f9ac9..6b79bc47005 100644
--- a/doc/customization/branded_page_and_email_header/appearance.png
+++ b/doc/customization/branded_page_and_email_header/appearance.png
Binary files differ
diff --git a/doc/customization/branded_page_and_email_header/custom_brand_header.png b/doc/customization/branded_page_and_email_header/custom_brand_header.png
index 7390f8a5e4e..d779236bbe7 100644
--- a/doc/customization/branded_page_and_email_header/custom_brand_header.png
+++ b/doc/customization/branded_page_and_email_header/custom_brand_header.png
Binary files differ
diff --git a/doc/customization/branded_page_and_email_header/custom_email_header.png b/doc/customization/branded_page_and_email_header/custom_email_header.png
index 705698ef4a8..729b166364b 100644
--- a/doc/customization/branded_page_and_email_header/custom_email_header.png
+++ b/doc/customization/branded_page_and_email_header/custom_email_header.png
Binary files differ
diff --git a/doc/customization/favicon/appearance.png b/doc/customization/favicon/appearance.png
index 6c41a05fc1f..da1002826dd 100644
--- a/doc/customization/favicon/appearance.png
+++ b/doc/customization/favicon/appearance.png
Binary files differ
diff --git a/doc/customization/favicon/custom_favicon.png b/doc/customization/favicon/custom_favicon.png
index fa1b8827a36..20dddfbea33 100644
--- a/doc/customization/favicon/custom_favicon.png
+++ b/doc/customization/favicon/custom_favicon.png
Binary files differ
diff --git a/doc/customization/new_project_page/appearance_settings.png b/doc/customization/new_project_page/appearance_settings.png
index 08eea684e14..4fcdd1caa21 100644
--- a/doc/customization/new_project_page/appearance_settings.png
+++ b/doc/customization/new_project_page/appearance_settings.png
Binary files differ
diff --git a/doc/customization/new_project_page/custom_new_project_page.png b/doc/customization/new_project_page/custom_new_project_page.png
index 662c715f193..c6f7839e9c3 100644
--- a/doc/customization/new_project_page/custom_new_project_page.png
+++ b/doc/customization/new_project_page/custom_new_project_page.png
Binary files differ
diff --git a/doc/customization/new_project_page/default_new_project_page.png b/doc/customization/new_project_page/default_new_project_page.png
index 4a0bcf09903..f5b209ac5ea 100644
--- a/doc/customization/new_project_page/default_new_project_page.png
+++ b/doc/customization/new_project_page/default_new_project_page.png
Binary files differ
diff --git a/doc/development/README.md b/doc/development/README.md
index fed3903c771..d2dd62ecac5 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -7,8 +7,8 @@ description: 'Learn how to contribute to GitLab.'
## Get started!
-- Setup GitLab's development environment with [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/README.md)
-- [GitLab contributing guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md)
+- Set up GitLab's development environment with [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/README.md)
+- [GitLab contributing guide](contributing/index.md)
- [Architecture](architecture.md) of GitLab
- [Rake tasks](rake_tasks.md) for development
@@ -30,6 +30,7 @@ description: 'Learn how to contribute to GitLab.'
## Backend guides
- [GitLab utilities](utilities.md)
+- [Logging](logging.md)
- [API styleguide](api_styleguide.md) Use this styleguide if you are
contributing to the API.
- [GraphQL API styleguide](api_graphql_styleguide.md) Use this
@@ -47,15 +48,30 @@ description: 'Learn how to contribute to GitLab.'
- [How to dump production data to staging](db_dump.md)
- [Working with the GitHub importer](github_importer.md)
- [Working with Merge Request diffs](diffs.md)
+- [Permissions](permissions.md)
+- [Prometheus metrics](prometheus_metrics.md)
+- [Guidelines for reusing abstractions](reusing_abstractions.md)
+- [DeclarativePolicy framework](policies.md)
+- [Switching to Rails 5](switching_to_rails5.md)
## Performance guides
-- [Instrumentation](instrumentation.md)
-- [Performance guidelines](performance.md)
+- [Instrumentation](instrumentation.md) for Ruby code running in production
+ environments
+- [Performance guidelines](performance.md) for writing code, benchmarks, and
+ certain patterns to avoid
- [Merge request performance guidelines](merge_request_performance_guidelines.md)
for ensuring merge requests do not negatively impact GitLab performance
+- [Profiling](profiling.md) a URL, measuring performance using Sherlock, or
+ tracking down N+1 queries using Bullet
-## Databases guides
+## Database guides
+
+### Tooling
+
+- [Understanding EXPLAIN plans](understanding_explain_plans.md)
+- [explain.depesz.com](https://explain.depesz.com/) for visualising the output
+ of `EXPLAIN`
### Migrations
@@ -81,6 +97,7 @@ description: 'Learn how to contribute to GitLab.'
- [Verifying database capabilities](verifying_database_capabilities.md)
- [Database Debugging and Troubleshooting](database_debugging.md)
- [Query Count Limits](query_count_limits.md)
+- [Database helper modules](database_helpers.md)
## Testing guides
diff --git a/doc/development/adding_database_indexes.md b/doc/development/adding_database_indexes.md
index ea6f14da3b9..d1d2b8c4907 100644
--- a/doc/development/adding_database_indexes.md
+++ b/doc/development/adding_database_indexes.md
@@ -28,9 +28,9 @@ to filter data by. Instead one should ask themselves the following questions:
1. Can I write my query in such a way that it re-uses as many existing indexes
as possible?
-2. Is the data going to be large enough that using an index will actually be
+1. Is the data going to be large enough that using an index will actually be
faster than just iterating over the rows in the table?
-3. Is the overhead of maintaining the index worth the reduction in query
+1. Is the overhead of maintaining the index worth the reduction in query
timings?
We'll explore every question in detail below.
@@ -62,7 +62,7 @@ In short:
1. Try to write your query in such a way that it re-uses as many existing
indexes as possible.
-2. Run the query using `EXPLAIN ANALYZE` and study the output to find the most
+1. Run the query using `EXPLAIN ANALYZE` and study the output to find the most
ideal query.
## Data Size
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 6c6e198a7c3..95722c027ba 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -379,7 +379,7 @@ let(:mutation) do
)
end
-it 'returns a successfull response' do
+it 'returns a successful response' do
post_graphql_mutation(mutation, current_user: user)
expect(response).to have_gitlab_http_status(:success)
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 3e417a44ec1..01d99c46f89 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -10,39 +10,182 @@ For information, see the [GitLab Release Process](https://gitlab.com/gitlab-org/
Both EE and CE require some add-on components called gitlab-shell and Gitaly. These components are available from the [gitlab-shell](https://gitlab.com/gitlab-org/gitlab-shell/tree/master) and [gitaly](https://gitlab.com/gitlab-org/gitaly/tree/master) repositories respectively. New versions are usually tags but staying on the master branch will give you the latest stable version. New releases are generally around the same time as GitLab CE releases with exception for informal security updates deemed critical.
-## Physical office analogy
+## GitLab Omnibus Component by Component
-You can imagine GitLab as a physical office.
+This document is designed to be consumed by systems adminstrators and GitLab Support Engineers who want to understand more about the internals of GitLab and how they work together.
-**The repositories** are the goods GitLab handles.
-They can be stored in a warehouse.
-This can be either a hard disk, or something more complex, such as a NFS filesystem;
+When deployed, GitLab should be considered the amalgamation of the below processes. When troubleshooting or debugging, be as specific as possible as to which component you are referencing. That should increase clarity and reduce confusion.
-**Nginx** acts like the front-desk.
-Users come to Nginx and request actions to be done by workers in the office;
+### GitLab Process Descriptions
-**The database** is a series of metal file cabinets with information on:
- - The goods in the warehouse (metadata, issues, merge requests etc);
- - The users coming to the front desk (permissions)
+As of this writing, a fresh GitLab 11.3.0 install will show the following processes with `gitlab-ctl status`:
-**Redis** is a communication board with “cubby holes†that can contain tasks for office workers;
+```
+run: alertmanager: (pid 30829) 14207s; run: log: (pid 13906) 2432044s
+run: gitaly: (pid 30771) 14210s; run: log: (pid 13843) 2432046s
+run: gitlab-monitor: (pid 30788) 14209s; run: log: (pid 13868) 2432045s
+run: gitlab-workhorse: (pid 30758) 14210s; run: log: (pid 13855) 2432046s
+run: logrotate: (pid 30246) 3407s; run: log: (pid 13825) 2432047s
+run: nginx: (pid 30849) 14207s; run: log: (pid 13856) 2432046s
+run: node-exporter: (pid 30929) 14206s; run: log: (pid 13877) 2432045s
+run: postgres-exporter: (pid 30935) 14206s; run: log: (pid 13931) 2432044s
+run: postgresql: (pid 13133) 2432214s; run: log: (pid 13848) 2432046s
+run: prometheus: (pid 30807) 14209s; run: log: (pid 13884) 2432045s
+run: redis: (pid 30560) 14274s; run: log: (pid 13807) 2432047s
+run: redis-exporter: (pid 30946) 14205s; run: log: (pid 13869) 2432045s
+run: sidekiq: (pid 30953) 14205s; run: log: (pid 13810) 2432047s
+run: unicorn: (pid 30960) 14204s; run: log: (pid 13809) 2432047s
+```
+
+### Layers
+
+GitLab can be considered to have two layers from a process perspective:
+
+- **Monitoring**: Anything from this layer is not required to deliver GitLab the application, but will allow administrators more insight into their infrastructure and what the service as a whole is doing.
+- **Core**: Any process that is vital for the delivery of GitLab as as platform. If any of these processes halt there will be a GitLab outage. For the Core layer, you can further divide into:
+ - **Processors**: These processes are responsible for actually performing operations and presenting the service.
+ - **Data**: These services store/expose structured data for the GitLab service.
+
+### alertmanager
+
+- Omnibus configuration options
+- Layer: Monitoring
+
+[Alert manager](https://prometheus.io/docs/alerting/alertmanager/) is a tool provided by prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue gitlab-ce#45740](https://gitlab.com/gitlab-org/gitlab-ce/issues/45740) about what we will be alerting on.
+
+### gitaly
+
+- [Omnibus confiugration options](https://gitlab.com/gitlab-org/gitaly/tree/master/doc/configuration)
+- Layer: Core Service (Data)
+
+Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab. (Think GitLab.com or High Availablity Deployments) As of 11.3.0, This service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly).
+
+### gitlab-monitor
+
+- Omnibus configuration options
+- Layer: Monitoring
+
+GitLab Monitor is a process disigned in house that allows us to export metrics about GitLab application internals to prometheus. You can read more [in the project's readme](https://gitlab.com/gitlab-org/gitlab-monitor)
+
+### gitlab-workhorse
+
+- Omnibus configuration options
+- Layer: Core Service (Processor)
+
+[GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) is a program designed at GitLab to help alieviate pressure from unicorn. You can read more about the [historical reasons for developing](https://about.gitlab.com/2016/04/12/a-brief-history-of-gitlab-workhorse/). It's designed to act as a smart reverse proxy to help speed up GitLab as a whole.
+
+### logrotate
+
+- [Omnibus configuration options](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate)
+- Layer: Core Service
+
+GitLab is comprised of a large number of services that all log. We started bundling our own logrotate as of 7.4 to make sure we were logging responsibly. This is just a packaged version of the common opensource offering.
+
+### nginx
+
+- [Omnibus configuration options](https://docs.gitlab.com/omnibus/settings/nginx.html)
+- Layer: Core Service (Processor)
+
+Nginx as as an ingress port for all HTTP requests and routes them to the approriate sub-systems within GitLab. We are bundling an unmodified version of the popular open source webserver.
+
+### node-exporter
+
+- [Omnibus configuration options](https://docs.gitlab.com/ee/administration/monitoring/prometheus/node_exporter.html)
+- Layer: Monitoring
+
+[Node Exporter](https://github.com/prometheus/node_exporter) is a Prometheus tool that gives us metrics on the underlying machine. (Think CPU/Disk/Load) It's just a packaged version of the common open source offering from the Prometheus project.
+
+### postgres-exporter
+
+- [Omnibus configuration options](https://docs.gitlab.com/ee/administration/monitoring/prometheus/postgres_exporter.html)
+- Layer: Monitoring
+
+[Postgres-exporter](https://github.com/wrouesnel/postgres_exporter) is the community provided Prometheus exporter that will deliver data about Postgres to prometheus for use in Grafana Dashboards.
+
+### postgresql
+
+- [Omnibus configuration options](https://docs.gitlab.com/omnibus/settings/database.html)
+- Layer: Core Service (Data)
+
+GitLab packages the popular Database to provide storage for Application meta data and user information.
+
+### prometheus
+
+- [Omnibus configuration options](https://docs.gitlab.com/ee/administration/monitoring/prometheus/)
+- Layer: Monitoring
+
+Prometheus is a time-series tool that helps GitLab administrators expose metrics about the individual processes used to provide GitLab the service.
+
+### redis
+
+- [Omnibus configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
+- Layer: Core Service (Data)
+
+Redis is packaged to provide a place to store:
+
+- session data
+- temporary cache information
+- background job queues.
+
+### redis-exporter
+
+- [Omnibus configuration options](https://docs.gitlab.com/ee/administration/monitoring/prometheus/redis_exporter.html)
+- Layer: Monitoring
+
+[Redis Exporter](https://github.com/oliver006/redis_exporter) is designed to give specific metrics about the Redis process to Prometheus so that we can graph these metrics in Graphana.
+
+### sidekiq
+
+- Omnibus configuration options
+- Layer: Core Service (Processor)
+
+Sidekiq is a Ruby background job processor that pulls jobs from the redis queue and processes them. Background jobs allow GitLab to provide a faster request/response cycle by moving work into the background.
+
+### unicorn
+
+- [Omnibus configuration options](https://docs.gitlab.com/omnibus/settings/unicorn.html)
+- Layer: Core Service (Processor)
+
+[Unicorn](https://bogomips.org/unicorn/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often process output you will see this as `bundle` or `config.ru` depending on the GitLab version.
+
+### Additional Processes
+
+### GitLab Pages
+
+TODO
+
+### Mattermost
+
+TODO
+
+## GitLab by Request Type
+
+GitLab provides two "interfaces" for end users to access the service:
+
+- Web HTTP Requests (Viewing the UI/API)
+- Git HTTP/SSH Requests (Pushing/Pulling Git Data)
+
+It's important to understand the distinction as some processes are used in both and others are exclusive to a specific request type.
+
+### GitLab Web HTTP Request Cycle
+
+When making a request to an HTTP Endpoint (Think `/users/sign_in`) the request will take the following path through the GitLab Service:
+
+- nginx - Acts as our first line reverse proxy
+- gitlab-workhorse - This determines if it needs to go to the Rails application or somewhere else to reduce load on unicorn.
+- unicorn - Since this is a web request, and it needs to access the application it will go to Unicorn.
+- Postgres/Gitaly/Redis - Depending on the type of request, it may hit these services to store or retreive data.
-**Sidekiq** is a worker that primarily handles sending out emails.
-It takes tasks from the Redis communication board;
-**A Unicorn worker** is a worker that handles quick/mundane tasks.
-They work with the communication board (Redis).
-Their job description:
- - check permissions by checking the user session stored in a Redis “cubby holeâ€;
- - make tasks for Sidekiq;
- - fetch stuff from the warehouse or move things around in there;
+### GitLab Git Request Cycle
-**GitLab-shell** is a third kind of worker that takes orders from a fax machine (SSH) instead of the front desk (HTTP).
-GitLab-shell communicates with Sidekiq via the “communication board†(Redis), and asks quick questions of the Unicorn workers either directly or via the front desk.
+Below we describe the different pathing that HTTP vs. SSH Git requests will take. There is some overlap with the Web Request Cycle but also some differences.
-**Gitaly** is a back desk that is specialized on reaching the disks to perform git operations efficiently and keep a copy of the result of costly operations. All git operations go through Gitaly.
+### Web Request (80/443)
+TODO
-**GitLab Enterprise Edition (the application)** is the collection of processes and business practices that the office is run by.
+### SSH Request (22)
+TODO
## System Layout
@@ -70,7 +213,7 @@ The add-on component gitlab-shell serves repositories over SSH. It manages the S
Gitaly executes git operations from gitlab-shell and the GitLab web app, and provides an API to the GitLab web app to get attributes from git (e.g. title, branches, tags, other meta data), and to get blobs (e.g. diffs, commits, files).
-You may also be interested in the [production architecture of GitLab.com](https://about.gitlab.com/handbook/infrastructure/production-architecture/).
+You may also be interested in the [production architecture of GitLab.com](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/).
### Installation Folder Summary
diff --git a/doc/development/automatic_ce_ee_merge.md b/doc/development/automatic_ce_ee_merge.md
index 8d41503f874..9dd78806a12 100644
--- a/doc/development/automatic_ce_ee_merge.md
+++ b/doc/development/automatic_ce_ee_merge.md
@@ -9,16 +9,16 @@ This merge is done automatically in a
## What to do if you are pinged in a `CE Upstream` merge request to resolve a conflict?
1. Please resolve the conflict as soon as possible or ask someone else to do it
- - It's ok to resolve more conflicts than the one that you are asked to resolve.
- In that case, it's a good habit to ask for a double-check on your resolution
- by someone who is familiar with the code you touched.
+ - It's ok to resolve more conflicts than the one that you are asked to resolve.
+ In that case, it's a good habit to ask for a double-check on your resolution
+ by someone who is familiar with the code you touched.
1. Once you have resolved your conflicts, push to the branch (no force-push)
1. Assign the merge request to the next person that has to resolve a conflict
1. If all conflicts are resolved after your resolution is pushed, keep the merge
- request assigned to you: **you are now responsible for the merge request to be
- green**
+ request assigned to you: **you are now responsible for the merge request to be
+ green**
1. If you need any help, you can ping the current [release managers], or ask in
- the `#ce-to-ee` Slack channel
+ the `#ce-to-ee` Slack channel
A few notes about the automatic CE->EE merge job:
@@ -63,7 +63,7 @@ EE version of your CE merge request.
For each commit (except on `master`), the `ee_compat_check` CI job tries to
detect if the current branch's changes will conflict during the CE->EE merge.
-The job reports what files are conflicting and how to setup a merge request
+The job reports what files are conflicting and how to set up a merge request
against EE.
#### How the job works
@@ -127,19 +127,19 @@ Now, every time you create an MR for CE and EE:
1. Open two terminal windows, one in CE, and another one in EE
1. In the CE terminal:
- 1. Create the CE branch, e.g., `branch-example`
- 1. Make your changes and push a commit (commit A)
- 1. Create the CE merge request in GitLab
+ 1. Create the CE branch, e.g., `branch-example`
+ 1. Make your changes and push a commit (commit A)
+ 1. Create the CE merge request in GitLab
1. In the EE terminal:
- 1. Create the EE-equivalent branch ending with `-ee`, e.g.,
- `git checkout -b branch-example-ee`
- 1. Fetch the CE branch: `git fetch ce branch-example`
- 1. Cherry-pick the commit A: `git cherry-pick commit-A-SHA`
- 1. If Git prompts you to fix the conflicts, do a `git status`
- to check which files contain conflicts, fix them, save the files
- 1. Add the changes with `git add .` but **DO NOT commit** them
- 1. Continue cherry-picking: `git cherry-pick --continue`
- 1. Push to EE: `git push origin branch-example-ee`
+ 1. Create the EE-equivalent branch ending with `-ee`, e.g.,
+ `git checkout -b branch-example-ee`
+ 1. Fetch the CE branch: `git fetch ce branch-example`
+ 1. Cherry-pick the commit A: `git cherry-pick commit-A-SHA`
+ 1. If Git prompts you to fix the conflicts, do a `git status`
+ to check which files contain conflicts, fix them, save the files
+ 1. Add the changes with `git add .` but **DO NOT commit** them
+ 1. Continue cherry-picking: `git cherry-pick --continue`
+ 1. Push to EE: `git push origin branch-example-ee`
1. Create the EE-equivalent MR and link to the CE MR from the
description "Ports [CE-MR-LINK] to EE"
1. Once all the jobs are passing in both CE and EE, you've addressed the
diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md
index f0d5af9fcb5..bb9a296ef12 100644
--- a/doc/development/background_migrations.md
+++ b/doc/development/background_migrations.md
@@ -10,10 +10,10 @@ migrations automatically reschedule themselves for a later point in time.
## When To Use Background Migrations
->**Note:**
-When adding background migrations _you must_ make sure they are announced in the
-monthly release post along with an estimate of how long it will take to complete
-the migrations.
+> **Note:**
+> When adding background migrations _you must_ make sure they are announced in the
+> monthly release post along with an estimate of how long it will take to complete
+> the migrations.
In the vast majority of cases you will want to use a regular Rails migration
instead. Background migrations should _only_ be used when migrating _data_ in
@@ -127,23 +127,23 @@ big JSON blob) to column `bar` (containing a string). The process for this would
roughly be as follows:
1. Release A:
- 1. Create a migration class that perform the migration for a row with a given ID.
- 1. Deploy the code for this release, this should include some code that will
- schedule jobs for newly created data (e.g. using an `after_create` hook).
- 1. Schedule jobs for all existing rows in a post-deployment migration. It's
- possible some newly created rows may be scheduled twice so your migration
- should take care of this.
+ 1. Create a migration class that perform the migration for a row with a given ID.
+ 1. Deploy the code for this release, this should include some code that will
+ schedule jobs for newly created data (e.g. using an `after_create` hook).
+ 1. Schedule jobs for all existing rows in a post-deployment migration. It's
+ possible some newly created rows may be scheduled twice so your migration
+ should take care of this.
1. Release B:
- 1. Deploy code so that the application starts using the new column and stops
- scheduling jobs for newly created data.
- 1. In a post-deployment migration you'll need to ensure no jobs remain.
- 1. Use `Gitlab::BackgroundMigration.steal` to process any remaining
- jobs in Sidekiq.
- 1. Reschedule the migration to be run directly (i.e. not through Sidekiq)
- on any rows that weren't migrated by Sidekiq. This can happen if, for
- instance, Sidekiq received a SIGKILL, or if a particular batch failed
- enough times to be marked as dead.
- 1. Remove the old column.
+ 1. Deploy code so that the application starts using the new column and stops
+ scheduling jobs for newly created data.
+ 1. In a post-deployment migration you'll need to ensure no jobs remain.
+ 1. Use `Gitlab::BackgroundMigration.steal` to process any remaining
+ jobs in Sidekiq.
+ 1. Reschedule the migration to be run directly (i.e. not through Sidekiq)
+ on any rows that weren't migrated by Sidekiq. This can happen if, for
+ instance, Sidekiq received a SIGKILL, or if a particular batch failed
+ enough times to be marked as dead.
+ 1. Remove the old column.
This may also require a bump to the [import/export version][import-export], if
importing a project from a prior version of GitLab requires the data to be in
diff --git a/doc/development/build_test_package.md b/doc/development/build_test_package.md
index 439d228baef..c5f6adfeaeb 100644
--- a/doc/development/build_test_package.md
+++ b/doc/development/build_test_package.md
@@ -4,12 +4,13 @@ While developing a new feature or modifying an existing one, it is helpful if an
installable package (or a docker image) containing those changes is available
for testing. For this very purpose, a manual job is provided in the GitLab CI/CD
pipeline that can be used to trigger a pipeline in the omnibus-gitlab repository
-that will create
-1. A deb package for Ubuntu 16.04, available as a build artifact, and
-2. A docker image, which is pushed to [Omnibus GitLab's container
-registry](https://gitlab.com/gitlab-org/omnibus-gitlab/container_registry)
-(images titled `gitlab-ce` and `gitlab-ee` respectively and image tag is the
-commit which triggered the pipeline).
+that will create:
+
+- A deb package for Ubuntu 16.04, available as a build artifact, and
+- A docker image, which is pushed to [Omnibus GitLab's container
+ registry](https://gitlab.com/gitlab-org/omnibus-gitlab/container_registry)
+ (images titled `gitlab-ce` and `gitlab-ee` respectively and image tag is the
+ commit which triggered the pipeline).
When you push a commit to either the gitlab-ce or gitlab-ee project, the
pipeline for that commit will have a `build-package` manual action you can
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index 9e0c81b3d60..cd0a1f46d27 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -110,7 +110,8 @@ At this point the script would ask you to select the category of the change (map
4. New deprecation
5. Feature removal
6. Security fix
-7. Other
+7. Performance improvement
+8. Other
```
The entry filename is based on the name of the current Git branch. If you run
@@ -132,15 +133,15 @@ If you're working on the GitLab EE repository, the entry will be added to
### Arguments
-| Argument | Shorthand | Purpose |
-| ----------------- | --------- | ---------------------------------------------------------------------------------------------------------- |
-| [`--amend`] | | Amend the previous commit |
-| [`--force`] | `-f` | Overwrite an existing entry |
-| [`--merge-request`] | `-m` | Set merge request ID |
-| [`--dry-run`] | `-n` | Don't actually write anything, just print |
-| [`--git-username`] | `-u` | Use Git user.name configuration as the author |
-| [`--type`] | `-t` | The category of the change, valid options are: added, fixed, changed, deprecated, removed, security, other |
-| [`--help`] | `-h` | Print help message |
+| Argument | Shorthand | Purpose |
+| ----------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------- |
+| [`--amend`] | | Amend the previous commit |
+| [`--force`] | `-f` | Overwrite an existing entry |
+| [`--merge-request`] | `-m` | Set merge request ID |
+| [`--dry-run`] | `-n` | Don't actually write anything, just print |
+| [`--git-username`] | `-u` | Use Git user.name configuration as the author |
+| [`--type`] | `-t` | The category of the change, valid options are: `added`, `fixed`, `changed`, `deprecated`, `removed`, `security`, `performance`, `other` |
+| [`--help`] | `-h` | Print help message |
[`--amend`]: #-amend
[`--force`]: #-force-or-f
diff --git a/doc/development/chaos_endpoints.md b/doc/development/chaos_endpoints.md
new file mode 100644
index 00000000000..403a5b21827
--- /dev/null
+++ b/doc/development/chaos_endpoints.md
@@ -0,0 +1,117 @@
+# Generating chaos in a test GitLab instance
+
+As [Werner Vogels](https://twitter.com/Werner), the CTO at Amazon Web Services, famously put it, **Everything fails, all the time**.
+
+As a developer, it's as important to consider the failure modes in which your software will operate as much as normal operation. Doing so can mean the difference between a minor hiccup leading to a scattering of `500` errors experienced by a tiny fraction of users and a full site outage that affects all users for an extended period.
+
+To paraphrase [Tolstoy](https://en.wikipedia.org/wiki/Anna_Karenina_principle), _all happy servers are alike, but all failing servers are failing in their own way_. Luckily, there are ways we can attempt to simulate these failure modes, and the chaos endpoints are tools for assisting in this process.
+
+Currently, there are four endpoints for simulating the following conditions:
+
+- Slow requests.
+- CPU-bound requests.
+- Memory leaks.
+- Unexpected process crashes.
+
+## Enabling chaos endpoints
+
+For obvious reasons, these endpoints are not enabled by default. They can be enabled by setting the `GITLAB_ENABLE_CHAOS_ENDPOINTS` environment variable to `1`.
+
+For example, if you're using the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit) this can be done with the following command:
+
+```bash
+GITLAB_ENABLE_CHAOS_ENDPOINTS=1 gdk run
+```
+
+## Securing the chaos endpoints
+
+DANGER: **Danger:**
+It is highly recommended that you secure access to the chaos endpoints using a secret token. This is recommended when enabling these endpoints locally and essential when running in a staging or other shared environment. You should not enable them in production unless you absolutely know what you're doing.
+
+A secret token can be set through the `GITLAB_CHAOS_SECRET` environment variable. For example, when using the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit) this can be done with the following command:
+
+```bash
+GITLAB_ENABLE_CHAOS_ENDPOINTS=1 GITLAB_CHAOS_SECRET=secret gdk run
+```
+
+Replace `secret` with your own secret token.
+
+## Invoking chaos
+
+Once you have enabled the chaos endpoints and restarted the application, you can start testing using the endpoints.
+
+## Memory leaks
+
+To simulate a memory leak in your application, use the `/-/chaos/leakmem` endpoint.
+
+NOTE: **Note:**
+The memory is not retained after the request finishes. Once the request has completed, the Ruby garbage collector will attempt to recover the memory.
+
+```
+GET /-/chaos/leakmem
+GET /-/chaos/leakmem?memory_mb=1024
+GET /-/chaos/leakmem?memory_mb=1024&duration_s=50
+```
+
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | ---------------------------------------------------------------------------------- |
+| `memory_mb` | integer | no | How much memory, in MB, should be leaked. Defaults to 100MB. |
+| `duration_s` | integer | no | Minimum duration, in seconds, that the memory should be retained. Defaults to 30s. |
+
+```bash
+curl http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10 --header 'X-Chaos-Secret: secret'
+```
+
+## CPU spin
+
+This endpoint attempts to fully utilise a single core, at 100%, for the given period.
+
+Depending on your rack server setup, your request may timeout after a predermined period (normally 60 seconds).
+If you're using Unicorn, this is done by killing the worker process.
+
+```
+GET /-/chaos/cpuspin
+GET /-/chaos/cpuspin?duration_s=50
+```
+
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | --------------------------------------------------------------------- |
+| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s |
+
+```bash
+curl http://localhost:3000/-/chaos/cpuspin?duration_s=60 --header 'X-Chaos-Secret: secret'
+```
+
+## Sleep
+
+This endpoint is similar to the CPU Spin endpoint but simulates off-processor activity, such as network calls to backend services. It will sleep for a given duration.
+
+As with the CPU Spin endpoint, this may lead to your request timing out if duration exceeds the configured limit.
+
+```
+GET /-/chaos/sleep
+GET /-/chaos/sleep?duration_s=50
+```
+
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | ---------------------------------------------------------------------- |
+| `duration_s` | integer | no | Duration, in seconds, that the request will sleep for. Defaults to 30s |
+
+```bash
+curl http://localhost:3000/-/chaos/sleep?duration_s=60 --header 'X-Chaos-Secret: secret'
+```
+
+## Kill
+
+This endpoint will simulate the unexpected death of a worker process using a `kill` signal.
+
+NOTE: **Note:**
+Since this endpoint uses the `KILL` signal, the worker is not given a chance to cleanup or shutdown.
+
+```
+GET /-/chaos/kill
+```
+
+```bash
+curl http://localhost:3000/-/chaos/kill --header 'X-Chaos-Secret: secret'
+```
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 23c80799235..52710e54e86 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -1,43 +1,5 @@
# Code Review Guidelines
-## Getting your merge request reviewed, approved, and merged
-
-There are a few rules to get your merge request accepted:
-
-1. Your merge request should only be **merged by a [maintainer][team]**.
- 1. If your merge request includes only backend changes [^1], it must be
- **approved by a [backend maintainer][projects]**.
- 1. If your merge request includes only frontend changes [^1], it must be
- **approved by a [frontend maintainer][projects]**.
- 1. If your merge request includes UX changes [^1], it must
- be **approved by a [UX team member][team]**.
- 1. If your merge request includes adding a new JavaScript library [^1], it must be
- **approved by a [frontend lead][team]**.
- 1. If your merge request includes adding a new UI/UX paradigm [^1], it must be
- **approved by a [UX lead][team]**.
- 1. If your merge request includes frontend and backend changes [^1], it must
- be **approved by a [frontend and a backend maintainer][projects]**.
- 1. If your merge request includes UX and frontend changes [^1], it must
- be **approved by a [UX team member and a frontend maintainer][team]**.
- 1. If your merge request includes UX, frontend and backend changes [^1], it must
- be **approved by a [UX team member, a frontend and a backend maintainer][team]**.
- 1. If your merge request includes a new dependency or a filesystem change, it must
- be *approved by a [Distribution team member][team]*. See how to work with the [Distribution team for more details.](https://about.gitlab.com/handbook/engineering/dev-backend/distribution/)
-1. To lower the amount of merge requests maintainers need to review, you can
- ask or assign any [reviewers][projects] for a first review.
- 1. If you need some guidance (e.g. it's your first merge request), feel free
- to ask one of the [Merge request coaches][team].
- 1. The reviewer will assign the merge request to a maintainer once the
- reviewer is satisfied with the state of the merge request.
-1. Keep in mind that maintainers are also going to perform a final code review.
- The ideal scenario is that the reviewer has already addressed any concerns
- the maintainer would have found, and the maintainer only has to perform the
- merge, but be prepared for further review comments.
-
-For more guidance, see [CONTRIBUTING.md](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md).
-
-## Best practices
-
This guide contains advice and best practices for performing code review, and
having your code reviewed.
@@ -45,10 +7,103 @@ All merge requests for GitLab CE and EE, whether written by a GitLab team member
or a volunteer contributor, must go through a code review process to ensure the
code is effective, understandable, and maintainable.
-Any developer can, and is encouraged to, perform code review on merge requests
-of colleagues and contributors. However, the final decision to accept a merge
-request is up to one the project's maintainers, denoted on the
-[engineering projects][projects].
+## Getting your merge request reviewed, approved, and merged
+
+You are strongly encouraged to get your code **reviewed** by a
+[reviewer](https://about.gitlab.com/handbook/engineering/#reviewer) as soon as
+there is any code to review, to get a second opinion on the chosen solution and
+implementation, and an extra pair of eyes looking for bugs, logic problems, or
+uncovered edge cases. The reviewer can be from a different team, but it is
+recommended to pick someone who knows the domain well. You can read more about the
+importance of involving reviewer(s) in the section on the responsibility of the author below.
+
+If you need some guidance (e.g. it's your first merge request), feel free to ask
+one of the [Merge request coaches][team].
+
+Depending on the areas your merge request touches, it must be **approved** by one
+or more [maintainers](https://about.gitlab.com/handbook/engineering/#maintainer):
+
+For approvals, we use the approval functionality found in the merge request
+widget. Reviewers can add their approval by [approving additionally](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html#adding-or-removing-an-approval).
+
+ 1. If your merge request includes backend changes [^1], it must be
+ **approved by a [backend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce_maintainers_backend)**.
+ 1. If your merge request includes frontend changes [^1], it must be
+ **approved by a [frontend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce_maintainers_frontend)**.
+ 1. If your merge request includes UX changes [^1], it must be
+ **approved by a [UX team member][team]**.
+ 1. If your merge request includes adding a new JavaScript library [^1], it must be
+ **approved by a [frontend lead][team]**.
+ 1. If your merge request includes adding a new UI/UX paradigm [^1], it must be
+ **approved by a [UX lead][team]**.
+ 1. If your merge request includes a new dependency or a filesystem change, it must be
+ **approved by a [Distribution team member][team]**. See how to work with the [Distribution team](https://about.gitlab.com/handbook/engineering/dev-backend/distribution/) for more details.
+
+Getting your merge request **merged** also requires a maintainer. If it requires
+more than one approval, the last maintainer to review and approve it will also merge it.
+
+As described in the section on the responsibility of the maintainer below, you
+are recommended to get your merge request approved and merged by maintainer(s)
+from other teams than your own.
+
+### The responsibility of the merge request author
+
+The responsibility to find the best solution and implement it lies with the
+merge request author.
+
+Before assigning a merge request to a maintainer for approval and merge, they
+should be confident that it actually solves the problem it was meant to solve,
+that it does so in the most appropriate way, that it satisfies all requirements,
+and that there are no remaining bugs, logical problems, or uncovered edge cases.
+The merge request should also have a completed task list in its description and
+a passing CI pipeline to avoid unnecessary back and forth.
+
+To reach the required level of confidence in their solution, an author is expected
+to involve other people in the investigation and implementation processes as
+appropriate.
+
+They are encouraged to reach out to domain experts to discuss different solutions
+or get an implementation reviewed, to product managers and UX designers to clear
+up confusion or verify that the end result matches what they had in mind, to
+database specialists to get input on the data model or specific queries, or to
+any other developer to get an in-depth review of the solution.
+
+If an author is unsure if a merge request needs a domain expert's opinion, that's
+usually a pretty good sign that it does, since without it the required level of
+confidence in their solution will not have been reached.
+
+### The responsibility of the maintainer
+
+Maintainers are responsible for the overall health, quality, and consistency of
+the GitLab codebase, across domains and product areas.
+
+Consequently, their reviews will focus primarily on things like overall
+architecture, code organization, separation of concerns, tests, DRYness,
+consistency, and readability.
+
+Since a maintainer's job only depends on their knowledge of the overall GitLab
+codebase, and not that of any specific domain, they can review, approve and merge
+merge requests from any team and in any product area.
+
+In fact, authors are recommended to get their merge requests merged by maintainers
+from other teams than their own, to ensure that all code across GitLab is consistent
+and can be easily understood by all contributors, from both inside and outside the
+company, without requiring team-specific expertise.
+
+Maintainers will do their best to also review the specifics of the chosen solution
+before merging, but as they are not necessarily domain experts, they may be poorly
+placed to do so without an unreasonable investment of time. In those cases, they
+will defer to the judgment of the author and earlier reviewers and involved domain
+experts, in favor of focusing on their primary responsibilities.
+
+If a developer who happens to also be a maintainer was involved in a merge request
+as a domain expert and/or reviewer, it is recommended that they are not also picked
+as the maintainer to ultimately approve and merge it.
+
+Maintainers should check before merging if the merge request is approved by the
+required approvers.
+
+## Best practices
### Everyone
@@ -96,6 +151,20 @@ first time.
branch. Do not squash until the branch is ready to merge. Reviewers should be
able to read individual updates based on their earlier feedback.
+### Assigning a merge request for a review
+
+If you want to have your merge request reviewed you can assign it to any reviewer. The list of reviewers can be found on [Engineering projects](https://about.gitlab.com/handbook/engineering/projects/) page.
+
+You can also use `ready for review` label. That means that your merge request is ready to be reviewed and any reviewer can pick it. It is recommended to use that label only if there isn't time pressure and make sure the merge request is assigned to a reviewer.
+
+When your merge request was reviewed and can be passed to a maintainer you can either pick a specific maintainer or use a label `ready for merge`.
+
+It is responsibility of the author of a merge request that the merge request is reviewed. If it stays in `ready for review` state too long it is recommended to assign it to a specific reviewer.
+
+### List of merge requests ready for review
+
+Developers who have capacity can regularly check the list of [merge requests to review](https://gitlab.com/groups/gitlab-org/-/merge_requests?scope=all&utf8=%E2%9C%93&state=opened&label_name%5B%5D=ready%20for%20review) and assign any merge request they want to review.
+
### Reviewing code
Understand why the change is necessary (fixes a bug, improves the user
@@ -160,42 +229,42 @@ Enterprise Edition instance. This has some implications:
1. **Query changes** should be tested to ensure that they don't result in worse
performance at the scale of GitLab.com:
- 1. Generating large quantities of data locally can help.
- 2. Asking for query plans from GitLab.com is the most reliable way to validate
- these.
-2. **Database migrations** must be:
- 1. Reversible.
- 2. Performant at the scale of GitLab.com - ask a maintainer to test the
- migration on the staging environment if you aren't sure.
- 3. Categorised correctly:
- - Regular migrations run before the new code is running on the instance.
- - [Post-deployment migrations](post_deployment_migrations.md) run _after_
- the new code is deployed, when the instance is configured to do that.
- - [Background migrations](background_migrations.md) run in Sidekiq, and
- should only be done for migrations that would take an extreme amount of
- time at GitLab.com scale.
-3. **Sidekiq workers**
+ 1. Generating large quantities of data locally can help.
+ 1. Asking for query plans from GitLab.com is the most reliable way to validate
+ these.
+1. **Database migrations** must be:
+ 1. Reversible.
+ 1. Performant at the scale of GitLab.com - ask a maintainer to test the
+ migration on the staging environment if you aren't sure.
+ 1. Categorised correctly:
+ - Regular migrations run before the new code is running on the instance.
+ - [Post-deployment migrations](post_deployment_migrations.md) run _after_
+ the new code is deployed, when the instance is configured to do that.
+ - [Background migrations](background_migrations.md) run in Sidekiq, and
+ should only be done for migrations that would take an extreme amount of
+ time at GitLab.com scale.
+1. **Sidekiq workers**
[cannot change in a backwards-incompatible way](sidekiq_style_guide.md#removing-or-renaming-queues):
- 1. Sidekiq queues are not drained before a deploy happens, so there will be
- workers in the queue from the previous version of GitLab.
- 2. If you need to change a method signature, try to do so across two releases,
- and accept both the old and new arguments in the first of those.
- 3. Similarly, if you need to remove a worker, stop it from being scheduled in
- one release, then remove it in the next. This will allow existing jobs to
- execute.
- 4. Don't forget, not every instance will upgrade to every intermediate version
- (some people may go from X.1.0 to X.10.0, or even try bigger upgrades!), so
- try to be liberal in accepting the old format if it is cheap to do so.
-4. **Cached values** may persist across releases. If you are changing the type a
+ 1. Sidekiq queues are not drained before a deploy happens, so there will be
+ workers in the queue from the previous version of GitLab.
+ 1. If you need to change a method signature, try to do so across two releases,
+ and accept both the old and new arguments in the first of those.
+ 1. Similarly, if you need to remove a worker, stop it from being scheduled in
+ one release, then remove it in the next. This will allow existing jobs to
+ execute.
+ 1. Don't forget, not every instance will upgrade to every intermediate version
+ (some people may go from X.1.0 to X.10.0, or even try bigger upgrades!), so
+ try to be liberal in accepting the old format if it is cheap to do so.
+1. **Cached values** may persist across releases. If you are changing the type a
cached value returns (say, from a string or nil to an array), change the
cache key at the same time.
-5. **Settings** should be added as a
+1. **Settings** should be added as a
[last resort](https://about.gitlab.com/handbook/product/#convention-over-configuration).
If you're adding a new setting in `gitlab.yml`:
- 1. Try to avoid that, and add to `ApplicationSetting` instead.
- 2. Ensure that it is also
- [added to Omnibus](https://docs.gitlab.com/omnibus/settings/gitlab.yml.html#adding-a-new-setting-to-gitlab-yml).
-6. **Filesystem access** can be slow, so try to avoid
+ 1. Try to avoid that, and add to `ApplicationSetting` instead.
+ 1. Ensure that it is also
+ [added to Omnibus](https://docs.gitlab.com/omnibus/settings/gitlab.yml.html#adding-a-new-setting-to-gitlab-yml).
+1. **Filesystem access** can be slow, so try to avoid
[shared files](shared_files.md) when an alternative solution is available.
### Credits
diff --git a/doc/development/contributing/community_roles.md b/doc/development/contributing/community_roles.md
new file mode 100644
index 00000000000..b9c369286d2
--- /dev/null
+++ b/doc/development/contributing/community_roles.md
@@ -0,0 +1,16 @@
+### Community members & roles
+
+GitLab community members and their privileges/responsibilities.
+
+| Roles | Responsibilities | Requirements |
+|-------|------------------|--------------|
+| Maintainer | Accepts merge requests on several GitLab projects | Added to the [team page](https://about.gitlab.com/team/). An expert on code reviews and knows the product/code base |
+| Reviewer | Performs code reviews on MRs | Added to the [team page](https://about.gitlab.com/team/) |
+| Developer |Has access to GitLab internal infrastructure & issues (e.g. HR-related) | GitLab employee or a Core Team member (with an NDA) |
+| Contributor | Can make contributions to all GitLab public projects | Have a GitLab.com account |
+
+[List of current reviewers/maintainers](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce).
+
+---
+
+[Return to Contributing documentation](index.md)
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
index 57ae318e821..79750878aac 100644
--- a/doc/development/contributing/design.md
+++ b/doc/development/contributing/design.md
@@ -1,13 +1,4 @@
-<!-- START doctoc generated TOC please keep comment here to allow auto update -->
-<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [Implement design & UI elements](#implement-design--ui-elements)
-- [Style guides](#style-guides)
-
-<!-- END doctoc generated TOC please keep comment here to allow auto update -->
-
-## Implement design & UI elements
+# Implement design & UI elements
For guidance on UX implementation at GitLab, please refer to our [Design System](https://design.gitlab.com/).
@@ -22,41 +13,18 @@ There is a special type label called ~"product discovery". It represents a disco
~"product discovery" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
-The initial issue should be about the problem we are solving. If a separate [product discovery issue](#product-discovery-issues) is needed for additional research and design work, it will be created by a PM or UX person. Assign the ~UX, ~"product discovery" and ~"Deliverable" labels, add a milestone and use a title that makes it clear that the scheduled issue is product discovery
+The initial issue should be about the problem we are solving. If a separate [product discovery issue](https://about.gitlab.com/handbook/engineering/ux/ux-department-workflow/#how-we-use-labels)
+is needed for additional research and design work, it will be created by a PM or UX person.
+Assign the ~UX, ~"product discovery" and ~"Deliverable" labels, add a milestone and
+use a title that makes it clear that the scheduled issue is product discovery
(e.g. `Product discovery for XYZ`).
-When the ~"product discovery" issue has been completed, the UXer removes the ~UX
-label, adds the ~"UX ready" label and closes the issue. This indicates the
-UX work for the issue is complete. The UXer will also copy any designs to related
-issues for implementation in an upcoming milestone.
-
-## Style guides
-
-1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
- Important sections include [Source Code Layout][rss-source] and
- [Naming][rss-naming]. Use:
- - multi-line method chaining style **Option A**: dot `.` on the second line
- - string literal quoting style **Option A**: single quoted by default
-1. [Rails](https://github.com/bbatsov/rails-style-guide)
-1. [Newlines styleguide][newlines-styleguide]
-1. [Testing][testing]
-1. [JavaScript styleguide][js-styleguide]
-1. [SCSS styleguide][scss-styleguide]
-1. [Shell commands](../shell_commands.md) created by GitLab
- contributors to enhance security
-1. [Database Migrations](../migration_style_guide.md)
-1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
-1. [Documentation styleguide](https://docs.gitlab.com/ee/development/documentation/styleguide.html)
-1. Interface text should be written subjectively instead of objectively. It
- should be the GitLab core team addressing a person. It should be written in
- present time and never use past tense (has been/was). For example instead
- of _prohibited this user from being saved due to the following errors:_ the
- text should be _sorry, we could not create your account because:_
-1. Code should be written in [US English][us-english]
-
-This is also the style used by linting tools such as
-[RuboCop](https://github.com/bbatsov/rubocop),
-[PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
+In order to complete a product discovery issue in a release, you must complete the following:
+
+1. UXer removes the ~UX label, adds the ~"UX ready" label.
+1. Modify the issue description in the product discovery issue to contain the final design. If it makes sense, the original information indicating the need for the design can be moved to a lower "Original Information" section.
+1. Copy the design to the description of the delivery issue for which the product discovery issue was created. Do not simply refer to the product discovery issue as a separate source of truth.
+1. In some cases, a product discovery issue also identifies future enhancements that will not go into the issue that originated the product discovery issue. For these items, create new issues containing the designs to ensure they are not lost. Put the issues in the backlog if they are agreed upon as good ideas. Otherwise leave them for triage.
---
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 64f5a2c8022..9da4c66933c 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -1,59 +1,26 @@
-<!-- START doctoc generated TOC please keep comment here to allow auto update -->
-<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [Contribute to GitLab](#contribute-to-gitlab)
-- [Security vulnerability disclosure](#security-vulnerability-disclosure)
-- [Code of conduct](#code-of-conduct)
-- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
-- [Helping others](#helping-others)
-- [I want to contribute!](#i-want-to-contribute)
-- [Contribution Flow](#contribution-flow)
-- [Workflow labels](#workflow-labels)
- - [Type labels](#type-labels)
- - [Subject labels](#subject-labels)
- - [Team labels](#team-labels)
- - [Milestone labels](#milestone-labels)
- - [Bug Priority labels](#bug-priority-labels)
- - [Bug Severity labels](#bug-severity-labels)
- - [Severity impact guidance](#severity-impact-guidance)
- - [Label for community contributors](#label-for-community-contributors)
-- [Implement design & UI elements](#implement-design--ui-elements)
-- [Issue tracker](#issue-tracker)
- - [Issue triaging](#issue-triaging)
- - [Feature proposals](#feature-proposals)
- - [Issue tracker guidelines](#issue-tracker-guidelines)
- - [Issue weight](#issue-weight)
- - [Regression issues](#regression-issues)
- - [Technical and UX debt](#technical-and-ux-debt)
- - [Stewardship](#stewardship)
-- [Merge requests](#merge-requests)
- - [Merge request guidelines](#merge-request-guidelines)
- - [Contribution acceptance criteria](#contribution-acceptance-criteria)
-- [Definition of done](#definition-of-done)
-- [Style guides](#style-guides)
-
-<!-- END doctoc generated TOC please keep comment here to allow auto update -->
-
-## Contribute to GitLab
-
-For a first-time step-by-step guide to the contribution process, see
-["Contributing to GitLab"](https://about.gitlab.com/contributing/).
+# Contribute to GitLab
Thank you for your interest in contributing to GitLab. This guide details how
-to contribute to GitLab in a way that is efficient for everyone.
+to contribute to GitLab in a way that is easy for everyone.
+
+We want to create a welcoming environment for everyone who is interested in contributing. Please visit our [Code of Conduct page](https://about.gitlab.com/contributing/code-of-conduct) to learn more about our committment to an open and welcoming environment.
+
+For a first-time step-by-step guide to the contribution process, please see
+["Contributing to GitLab"](https://about.gitlab.com/contributing/).
-Looking for something to work on? Look for issues with the label [Accepting Merge Requests](#i-want-to-contribute).
+Looking for something to work on? Look for issues with the label [`Accepting merge requests`](#i-want-to-contribute).
-GitLab comes into two flavors, GitLab Community Edition (CE) our free and open
+GitLab comes in two flavors, GitLab Community Edition (CE) our free and open
source edition, and GitLab Enterprise Edition (EE) which is our commercial
edition. Throughout this guide you will see references to CE and EE for
abbreviation.
-If you have read this guide and want to know how the GitLab [core team]
+To get an overview of GitLab community membership including those that would be reviewing or merging your contributions, please visit [the community roles page](community_roles.md).
+
+If you want to know how the GitLab [core team]
operates please see [the GitLab contributing process](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/PROCESS.md).
-- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
+[GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
## Security vulnerability disclosure
@@ -65,33 +32,8 @@ vulnerabilities.
## Code of conduct
-As contributors and maintainers of this project, we pledge to respect all
-people who contribute through reporting issues, posting feature requests,
-updating documentation, submitting pull requests or patches, and other
-activities.
-
-We are committed to making participation in this project a harassment-free
-experience for everyone, regardless of level of experience, gender, gender
-identity and expression, sexual orientation, disability, personal appearance,
-body size, race, ethnicity, age, or religion.
-
-Examples of unacceptable behavior by participants include the use of sexual
-language or imagery, derogatory comments or personal attacks, trolling, public
-or private harassment, insults, or other unprofessional conduct.
-
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct. Project maintainers who do not
-follow the Code of Conduct may be removed from the project team.
-
-This code of conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community.
-
-Instances of abusive, harassing, or otherwise unacceptable behavior can be
-reported by emailing `contact@gitlab.com`.
-
-This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
-available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
+Our code of conduct can be found on the
+["Contributing to GitLab"](https://about.gitlab.com/contributing/) page.
## Closing policy for issues and merge requests
@@ -124,10 +66,10 @@ the remaining issues on the GitHub issue tracker.
## I want to contribute!
-If you want to contribute to GitLab [issues with the label `Accepting Merge Requests` and small weight][accepting-mrs-weight]
-is a great place to start. Issues with a lower weight (1 or 2) are deemed
-suitable for beginners. These issues will be of reasonable size and challenge,
-for anyone to start contributing to GitLab. If you have any questions or need help visit [Getting Help](https://about.gitlab.com/getting-help/#discussion) to
+If you want to contribute to GitLab,
+[issues with the `Accepting merge requests` label](issue_workflow.md#label-for-community-contributors)
+are a great place to start.
+If you have any questions or need help visit [Getting Help](https://about.gitlab.com/getting-help/#discussion) to
learn how to communicate with GitLab. If you're looking for a Gitter or Slack channel
please consider we favor
[asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real time communication. Thanks for your contribution!
@@ -154,93 +96,38 @@ When your code contains more than 500 changes, any major breaking changes, or an
This [documentation](issue_workflow.md) outlines the current workflow labels.
-### Type labels
-
-This [documentation](issue_workflow.md) outlines the current type labels.
-
-### Subject labels
-
-This [documentation](issue_workflow.md) outlines the current subject labels.
-
-### Team labels
-
-This [documentation](issue_workflow.md) outlines the current team labels.
-
-### Milestone labels
-
-This [documentation](issue_workflow.md) outlines the current milestone labels.
-
-### Bug Priority labels
-
-This [documentation](issue_workflow.md) outlines the current bug priority labels.
-
-### Bug Severity labels
-
-This [documentation](issue_workflow.md) outlines the current severity labels.
-
-#### Severity impact guidance
-
-This [documentation](issue_workflow.md) outlines the current severity impact guidance.
-
-### Label for community contributors
-
-This [documentation](issue_workflow.md) outlines the current policy regarding community contributor issues.
-
-## Implement design & UI elements
-
-This [documentation](design.md) outlines the current design and UI guidelines.
-
-## Issue tracker
-
-This [documentation](issue_workflow.md) outlines the issue tracker process.
-
-### Issue triaging
-
-This [documentation](issue_workflow.md) outlines the current issue triaging process.
-
-### Feature proposals
-
-This [documentation](issue_workflow.md) outlines the feature proposal process.
-
-### Issue tracker guidelines
-
-This [documentation](issue_workflow.md) outlines the issue tracker guidelines.
-
-### Issue weight
-
-This [documentation](issue_workflow.md) outlines the issue weight guidelines.
-
-### Regression issues
-
-This [documentation](issue_workflow.md) outlines the regression issue process.
-
-### Technical and UX debt
-
-This [documentation](issue_workflow.md) about technical and UX debt has been moved.
-
-### Stewardship
-
-This [documentation](issue_workflow.md) outlines the stewardship process.
+* [Type labels](issue_workflow.md#type-labels)
+* [Subject labels](issue_workflow.md#subject-labels)
+* [Team labels](issue_workflow.md#team-labels)
+* [Release Scoping labels](issue_workflow.md#release-scoping-labels)
+* [Priority labels](issue_workflow.md#priority-labels)
+* [Severity labels](issue_workflow.md#severity-labels)
+* [Label for community contributors](issue_workflow.md#label-for-community-contributors)
+* [Issue triaging](issue_workflow.md#issue-triaging)
+* [Feature proposals](issue_workflow.md#feature-proposals)
+* [Issue tracker guidelines](issue_workflow.md#issue-tracker-guidelines)
+* [Issue weight](issue_workflow.md#issue-weight)
+* [Regression issues](issue_workflow.md#regression-issues)
+* [Technical and UX debt](issue_workflow.md#technical-and-ux-debt)
+* [Stewardship](issue_workflow.md#stewardship)
## Merge requests
This [documentation](merge_request_workflow.md) outlines the current merge request process.
-### Merge request guidelines
-
-This [documentation](merge_request_workflow.md) outlines the current merge request guidelines.
-
-### Contribution acceptance criteria
-
-This [documentation](merge_request_workflow.md) outlines the current acceptance criteria for contributions.
-
-## Definition of done
-
-This [documentation](merge_request_workflow.md) outlines the definition of done.
+* [Merge request guidelines](merge_request_workflow.md#merge-request-guidelines)
+* [Contribution acceptance criteria](merge_request_workflow.md#contribution-acceptance-criteria)
+* [Definition of done](merge_request_workflow.md#definition-of-done)
## Style guides
-This [documentation](design.md) outlines the current style guidelines.
+
+This [documentation](style_guides.md) outlines the current style guidelines.
---
[Return to Development documentation](../README.md)
+
+[core team]: https://about.gitlab.com/core-team/
+[team]: https://about.gitlab.com/team/
+[getting-help]: https://about.gitlab.com/getting-help/
+[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index 6a334e9b17d..233dc83f95b 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -1,27 +1,4 @@
-<!-- START doctoc generated TOC please keep comment here to allow auto update -->
-<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [Workflow labels](#workflow-labels)
- - [Type labels](#type-labels)
- - [Subject labels](#subject-labels)
- - [Team labels](#team-labels)
- - [Release Scoping labels](#release-scoping-labels)
- - [Priority labels](#priority-labels)
- - [Severity labels](#severity-labels)
- - [Severity impact guidance](#severity-impact-guidance)
- - [Label for community contributors](#label-for-community-contributors)
- - [Issue triaging](#issue-triaging)
- - [Feature proposals](#feature-proposals)
- - [Issue tracker guidelines](#issue-tracker-guidelines)
- - [Issue weight](#issue-weight)
- - [Regression issues](#regression-issues)
- - [Technical and UX debt](#technical-and-ux-debt)
- - [Stewardship](#stewardship)
-
-<!-- END doctoc generated TOC please keep comment here to allow auto update -->
-
-## Workflow labels
+# Workflow labels
To allow for asynchronous issue handling, we use [milestones][milestones-page]
and [labels][labels-page]. Leads and product managers handle most of the
@@ -31,7 +8,8 @@ Most issues will have labels for at least one of the following:
- Type: ~"feature proposal", ~bug, ~customer, etc.
- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
-- Team: ~"CI/CD", ~Plan, ~Manage, ~Quality, etc.
+- Team: ~Plan, ~Manage, ~Quality, etc.
+- Stage: ~"devops:plan", ~"devops:create", etc.
- Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release"
- Priority: ~P1, ~P2, ~P3, ~P4
- Severity: ~S1, ~S2, ~S3, ~S4
@@ -43,9 +21,8 @@ If you come across an issue that has none of these, and you're allowed to set
labels, you can _always_ add the team and type, and often also the subject.
[milestones-page]: https://gitlab.com/gitlab-org/gitlab-ce/milestones
-[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
-### Type labels
+## Type labels
Type labels are very important. They define what kind of issue this is. Every
issue should have one or more.
@@ -61,7 +38,7 @@ already reserved for subject labels).
The descriptions on the [labels page][labels-page] explain what falls under each type label.
-### Subject labels
+## Subject labels
Subject labels are labels that define what area or feature of GitLab this issue
hits. They are not always necessary, but very convenient.
@@ -75,7 +52,7 @@ issue is labeled with a subject label corresponding to your expertise.
Subject labels are always all-lowercase.
-### Team labels
+## Team labels
Team labels specify what team is responsible for this issue.
Assigning a team label makes sure issues get the attention of the appropriate
@@ -83,8 +60,7 @@ people.
The current team labels are:
-- ~Configuration
-- ~"CI/CD"
+- ~Configure
- ~Create
- ~Distribution
- ~Documentation
@@ -97,6 +73,7 @@ The current team labels are:
- ~Release
- ~Secure
- ~UX
+- ~Verify
The descriptions on the [labels page][labels-page] explain what falls under the
responsibility of each team.
@@ -107,7 +84,40 @@ indicate if an issue needs backend work, frontend work, or both.
Team labels are always capitalized so that they show up as the first label for
any issue.
-### Release Scoping labels
+## Stage labels
+
+Stage labels specify which [DevOps stage][devops-stages] the issue belongs to.
+
+The current stage labels are:
+
+- ~"devops:manage"
+- ~"devops:plan"
+- ~"devops:create"
+- ~"devops:verify"
+- ~"devops:package"
+- ~"devops:release"
+- ~"devops:configure"
+- ~"devops:monitor"
+- ~"devops:secure"
+
+These labels should be mutually exclusive. If an issue belongs to multiple
+stages, the most relevant should be used.
+
+They differ from the [Team labels](#team-labels) because teams may work on
+issues outside their stage.
+
+Normally there is a 1:1 relationship between Stage labels and Team labels, but
+any issue can be picked up by any team, depending on current priorities.
+So, an issue labeled ~"devops:create" may be scheduled by the ~Plan team, for
+example. In such cases, it's usual to include both team labels so each team can
+be aware of the progress.
+
+The Stage labels are used to generate the [direction pages][direction-pages] automatically.
+
+[devops-stages]: https://about.gitlab.com/direction/#devops-stages
+[direction-pages]: https://about.gitlab.com/direction/
+
+## Release Scoping labels
Release Scoping labels help us clearly communicate expectations of the work for the
release. There are three levels of Release Scoping labels:
@@ -131,14 +141,17 @@ Priority labels help us define the time a ~bug fix should be completed. Priority
If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes.
-| Label | Meaning | Estimate time to fix |
-|-------|-----------------|------------------------------------------------------------------|
-| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com |
-| ~P2 | High Priority | The next release |
-| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) |
-| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) |
+| Label | Meaning | Defect SLA (applies only to ~bug and ~security defects) |
+|-------|-----------------|----------------------------------------------------------------------------|
+| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com (30 days) |
+| ~P2 | High Priority | The next release (60 days) |
+| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter or 90 days) |
+| ~P4 | Low Priority | Anything outside the next 3 releases (more than one quarter or 120 days) |
-### Severity labels
+If an issue seems to fall between two priority labels, assign it to the higher-
+priority label.
+
+## Severity labels
Severity labels help us clearly communicate the impact of a ~bug on users.
@@ -149,7 +162,11 @@ Severity labels help us clearly communicate the impact of a ~bug on users.
| ~S3 | Major Severity | Broken Feature, workaround acceptable | Can create merge requests only from the Merge Requests page, not through the Issue. |
| ~S4 | Low Severity | Functionality inconvenience or cosmetic issue | Label colors are incorrect / not being displayed. |
-#### Severity impact guidance
+If an issue seems to fall between two severity labels, even taking the
+[severity impact guidance](#severity-impact-guidance) into account, assign
+it to the higher-severity label.
+
+### Severity impact guidance
Severity levels can be applied further depending on the facet of the impact; e.g. Affected customers, GitLab.com availability, performance and etc. The below is a guideline.
@@ -160,14 +177,14 @@ Severity levels can be applied further depending on the facet of the impact; e.g
| ~S3 | A few users or a single paid customer affected | Limited impact on important portions of GitLab.com | Degradation is likely to occur in the near future |
| ~S4 | No paid users/customer affected, or expected to in the near future | Minor impact on on GitLab.com | Degradation _may_ occur but it's not likely |
-### Label for community contributors
+## Label for community contributors
Issues that are beneficial to our users, 'nice to haves', that we currently do
not have the capacity for or want to give the priority to, are labeled as
-~"Accepting Merge Requests", so the community can make a contribution.
+~"Accepting merge requests", so the community can make a contribution.
Community contributors can submit merge requests for any issue they want, but
-the ~"Accepting Merge Requests" label has a special meaning. It points to
+the ~"Accepting merge requests" label has a special meaning. It points to
changes that:
1. We already agreed on,
@@ -175,43 +192,42 @@ changes that:
1. Are likely to get accepted by a maintainer.
We want to avoid a situation when a contributor picks an
-~"Accepting Merge Requests" issue and then their merge request gets closed,
+~"Accepting merge requests" issue and then their merge request gets closed,
because we realize that it does not fit our vision, or we want to solve it in a
different way.
-We add the ~"Accepting Merge Requests" label to:
+We add the ~"Accepting merge requests" label to:
- Low priority ~bug issues (i.e. we do not add it to the bugs that we want to
solve in the ~"Next Patch Release")
- Small ~"feature proposal"
- Small ~"technical debt" issues
-After adding the ~"Accepting Merge Requests" label, we try to estimate the
+After adding the ~"Accepting merge requests" label, we try to estimate the
[weight](#issue-weight) of the issue. We use issue weight to let contributors
know how difficult the issue is. Additionally:
-- We advertise ["Accepting Merge Requests" issues with weight < 5][up-for-grabs]
+- We advertise [`Accepting merge requests` issues with weight < 5][up-for-grabs]
as suitable for people that have never contributed to GitLab before on the
[Up For Grabs campaign](http://up-for-grabs.net)
- We encourage people that have never contributed to any open source project to
- look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers]
+ look for [`Accepting merge requests` issues with a weight of 1][firt-timers]
If you've decided that you would like to work on an issue, please @-mention
the [appropriate product manager](https://about.gitlab.com/handbook/product/#who-to-talk-to-for-what)
as soon as possible. The product manager will then pull in appropriate GitLab team
members to further discuss scope, design, and technical considerations. This will
-ensure that that your contribution is aligned with the GitLab product and minimize
+ensure that your contribution is aligned with the GitLab product and minimize
any rework and delay in getting it merged into master.
-GitLab team members who apply the ~"Accepting Merge Requests" label to an issue
+GitLab team members who apply the ~"Accepting merge requests" label to an issue
should update the issue description with a responsible product manager, inviting
any potential community contributor to @-mention per above.
-[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened
-[firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1
+[up-for-grabs]: https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=0&sort=weight
+[firt-timers]: https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=0&sort=weight&weight=1
-
-### Issue triaging
+## Issue triaging
Our issue triage policies are [described in our handbook]. You are very welcome
to help the GitLab team triage issues. We also organize [issue bash events] once
@@ -224,7 +240,7 @@ on those issues. Please select someone with relevant experience from the
the commit history for the affected files to find someone.
We also use [GitLab Triage] to automate some triaging policies. This is
-currently setup as a [scheduled pipeline] running on [quality/triage-ops]
+currently set up as a [scheduled pipeline] running on [quality/triage-ops]
project.
[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
@@ -232,8 +248,9 @@ project.
[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage
[scheduled pipeline]: https://gitlab.com/gitlab-org/quality/triage-ops/pipeline_schedules/10512/edit
[quality/triage-ops]: https://gitlab.com/gitlab-org/quality/triage-ops
+[team]: https://about.gitlab.com/team/
-### Feature proposals
+## Feature proposals
To create a feature proposal for CE, open an issue on the
[issue tracker of CE][ce-tracker].
@@ -250,7 +267,7 @@ code snippet right after your description in a new line: `~"feature proposal"`.
Please keep feature proposals as small and simple as possible, complex ones
might be edited to make them small and simple.
-Please submit Feature Proposals using the ['Feature Proposal' issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker.
+Please submit Feature Proposals using the ['Feature Proposal' issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Feature%20proposal.md) provided on the issue tracker.
For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
@@ -259,7 +276,9 @@ need to ask one of the [core team] members to add the label, if you do not have
If you want to create something yourself, consider opening an issue first to
discuss whether it is interesting to include this in GitLab.
-### Issue tracker guidelines
+[fpl]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=feature+proposal
+
+## Issue tracker guidelines
**[Search the issue tracker][ce-tracker]** for similar entries before
submitting your own, there's a good chance somebody else had the same issue or
@@ -271,7 +290,7 @@ The text in the parenthesis is there to help you with what to include. Omit it
when submitting the actual issue. You can copy-paste it and then edit as you
see fit.
-### Issue weight
+## Issue weight
Issue weight allows us to get an idea of the amount of work required to solve
one or multiple issues. This makes it possible to schedule work more accurately.
@@ -293,7 +312,7 @@ is probably 1, adding a new Git Hook maybe 4 or 5, big features 7-9.
issues or chunks. You can simply not set the weight of a parent issue and set
weights to children issues.
-### Regression issues
+## Regression issues
Every monthly release has a corresponding issue on the CE issue tracker to keep
track of functionality broken by that release and any fixes that need to be
@@ -313,7 +332,7 @@ addressed.
[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127
[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
-### Technical and UX debt
+## Technical and UX debt
In order to track things that can be improved in GitLab's codebase,
we use the ~"technical debt" label in [GitLab's issue tracker][ce-tracker].
@@ -337,7 +356,46 @@ for a release by the appropriate person.
Make sure to mention the merge request that the ~"technical debt" issue or
~"UX debt" issue is associated with in the description of the issue.
-### Stewardship
+## Technical debt in follow-up issues
+
+It's common to discover technical debt during development of a new feature. In
+the spirit of "minimum viable change", resolution is often deferred to a
+follow-up issue. However, this cannot be used as an excuse to merge poor-quality
+code that would otherwise not pass review, or to overlook trivial matters that
+don't deserve the be scheduled independently, and would be best resolved in the
+original merge request - or not tracked at all!
+
+The overheads of scheduling, and rate of change in the GitLab codebase, mean
+that the cost of a trivial technical debt issue can quickly exceed the value of
+tracking it. This generally means we should resolve these in the original merge
+request - or simply not create a follow-up issue at all.
+
+For example, a typo in a comment that is being copied between files is worth
+fixing in the same MR, but not worth creating a follow-up issue for. Renaming a
+method that is used in many places to make its intent slightly clearer may be
+worth fixing, but it should not happen in the same MR, and is generally not
+worth the overhead of having an issue of its own. These issues would invariably
+be labelled `~P4 ~S4` if we were to create them.
+
+More severe technical debt can have implications for development velocity. If
+it isn't addressed in a timely manner, the codebase becomes needlessly difficult
+to change, new features become difficult to add, and regressions abound.
+
+Discoveries of this kind of technical debt should be treated seriously, and
+while resolution in a follow-up issue may be appropriate, maintainers should
+generally obtain a scheduling commitment from the author of the original MR, or
+the engineering or product manager for the relevant area. This may take the form
+of appropriate Priority / Severity labels on the issue, or an explicit milestone
+and assignee.
+
+The maintainer must always agree before an outstanding discussion is resolved in
+this manner, and will be the one to create the issue. The title and description
+should be of the same quality as those created
+[in the usual manner](#technical-and-ux-debt) - in particular, the issue title
+**must not** begin with `Follow-up`! The creating maintainer should also expect
+to be involved in some capacity when work begins on the follow-up issue.
+
+## Stewardship
For issues related to the open source stewardship of GitLab,
there is the ~"stewardship" label.
@@ -355,3 +413,7 @@ A recent example of this was the issue for
---
[Return to Contributing documentation](index.md)
+
+[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
+[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
+[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index 9b1da4e7bc1..5b32b5cd46f 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -1,22 +1,10 @@
-<!-- START doctoc generated TOC please keep comment here to allow auto update -->
-<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [Merge requests](#merge-requests)
- - [Merge request guidelines](#merge-request-guidelines)
- - [Contribution acceptance criteria](#contribution-acceptance-criteria)
-- [Definition of done](#definition-of-done)
-
-<!-- END doctoc generated TOC please keep comment here to allow auto update -->
-
-## Merge requests
+# Merge requests
We welcome merge requests with fixes and improvements to GitLab code, tests,
and/or documentation. The issues that are specifically suitable for
-community contributions are listed with the label
-[`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
-and [EE][accepting-mrs-ee], but you are free to contribute to any other issue
-you want.
+community contributions are listed with
+[the `Accepting merge requests` label](issue_workflow.md#label-for-community-contributors),
+but you are free to contribute to any other issue you want.
Please note that if an issue is marked for the current milestone either before
or while you are working on it, a team member may take over the merge request
@@ -30,13 +18,16 @@ wireframes if the feature will also change the UI.
Merge requests should be opened at [GitLab.com][gitlab-mr-tracker].
If you are new to GitLab development (or web development in general), see the
-[I want to contribute!](#i-want-to-contribute) section to get you started with
+[I want to contribute!](index.md#i-want-to-contribute) section to get you started with
some potentially easy issues.
To start with GitLab development download the [GitLab Development Kit][gdk] and
-see the [Development section](../README.md) for some guidelines.
+see the [Development section](../../README.md) for some guidelines.
+
+[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests
+[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
-### Merge request guidelines
+## Merge request guidelines
If you can, please submit a merge request with the fix or improvements
including tests. If you don't know how to fix the issue but can write a test
@@ -55,30 +46,30 @@ request is as follows:
organized commits by [squashing them][git-squash]
1. Push the commit(s) to your fork
1. Submit a merge request (MR) to the `master` branch
- 1. Your merge request needs at least 1 approval but feel free to require more.
- For instance if you're touching backend and frontend code, it's a good idea
- to require 2 approvals: 1 from a backend maintainer and 1 from a frontend
- maintainer
- 1. You don't have to select any approvers, but you can if you really want
- specific people to approve your merge request
+ 1. Your merge request needs at least 1 approval but feel free to require more.
+ For instance if you're touching backend and frontend code, it's a good idea
+ to require 2 approvals: 1 from a backend maintainer and 1 from a frontend
+ maintainer
+ 1. You don't have to select any approvers, but you can if you really want
+ specific people to approve your merge request
1. The MR title should describe the change you want to make
1. The MR description should give a motive for your change and the method you
used to achieve it.
- 1. If you are contributing code, fill in the template already provided in the
- "Description" field.
- 1. If you are contributing documentation, choose `Documentation` from the
- "Choose a template" menu and fill in the template.
- 1. Mention the issue(s) your merge request solves, using the `Solves #XXX` or
- `Closes #XXX` syntax to auto-close the issue(s) once the merge request will
- be merged.
+ 1. If you are contributing code, fill in the template already provided in the
+ "Description" field.
+ 1. If you are contributing documentation, choose `Documentation` from the
+ "Choose a template" menu and fill in the template.
+ 1. Mention the issue(s) your merge request solves, using the `Solves #XXX` or
+ `Closes #XXX` syntax to auto-close the issue(s) once the merge request will
+ be merged.
1. If you're allowed to, set a relevant milestone and labels
1. If the MR changes the UI it should include *Before* and *After* screenshots
1. If the MR changes CSS classes please include the list of affected pages,
`grep css-class ./app -R`
1. Be prepared to answer questions and incorporate feedback even if requests
for this arrive weeks or months after your MR submission
- 1. If a discussion has been addressed, select the "Resolve discussion" button
- beneath it to mark it resolved.
+ 1. If a discussion has been addressed, select the "Resolve discussion" button
+ beneath it to mark it resolved.
1. If your MR touches code that executes shell commands, reads or opens files or
handles paths to files on disk, make sure it adheres to the
[shell command guidelines](../shell_commands.md)
@@ -114,7 +105,11 @@ Please ensure that your merge request meets the contribution acceptance criteria
When having your code reviewed and when reviewing merge requests please take the
[code review guidelines](../code_review.md) into account.
-### Contribution acceptance criteria
+[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
+[closed-merge-requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed
+[team]: https://about.gitlab.com/team/
+
+## Contribution acceptance criteria
1. The change is as small as possible
1. Include proper tests and make all tests pass (unless it contains a test
@@ -138,13 +133,13 @@ When having your code reviewed and when reviewing merge requests please take the
making and testing future changes
1. Changes do not adversely degrade performance.
- Avoid repeated polling of endpoints that require a significant amount of overhead
- - Check for N+1 queries via the SQL log or [`QueryRecorder`](https://docs.gitlab.com/ce/development/mer ge_request_performance_guidelines.html)
+ - Check for N+1 queries via the SQL log or [`QueryRecorder`](https://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- Avoid repeated access of filesystem
1. If you need polling to support real-time features, please use
[polling with ETag caching][polling-etag].
1. Changes after submitting the merge request should be in separate commits
(no squashing).
-1. It conforms to the [style guides](#style-guides) and the following:
+1. It conforms to the [style guides](style_guides.md) and the following:
- If your change touches a line that does not follow the style, modify the
entire line to follow it. This prevents linting tools from generating warnings.
- Don't touch neighbouring lines. As an exception, automatic mass
@@ -155,6 +150,9 @@ When having your code reviewed and when reviewing merge requests please take the
"license-finder" test with a "Dependencies that need approval" error.
1. The merge request meets the [definition of done](#definition-of-done).
+[license-finder-doc]: ../licensing.md
+[polling-etag]: ../polling.md
+
## Definition of done
If you contribute to GitLab please know that changes involve more than just
@@ -167,12 +165,13 @@ the feature you contribute through all of these steps.
1. Performance/scalability implications have been considered, addressed, and tested
1. [Documented][doc-guidelines] in the `/doc` directory
1. [Changelog entry added][changelog], if necessary
-1. Reviewed and any concerns are addressed
+1. Reviewed by UX/FE/BE and any concerns are addressed
1. Merged by a project maintainer
1. Added to the release blog article, if relevant
1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/), if relevant
1. Community questions answered
1. Answers to questions radiated (in docs/wiki/support etc.)
+1. [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-or-end-to-end-tests) added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams) with any questions
If you add a dependency in GitLab (such as an operating system package) please
consider updating the following and note the applicability of each in your
@@ -186,6 +185,12 @@ merge request:
1. Test suite https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/prepare_build.sh
1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
+[definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
+[testing]: ../testing_guide/index.md
+
---
[Return to Contributing documentation](index.md)
+
+[changelog]: ../changelog.md "Generate a changelog entry"
+[doc-guidelines]: ../documentation/index.md "Documentation guidelines"
diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md
new file mode 100644
index 00000000000..fb0454db7d2
--- /dev/null
+++ b/doc/development/contributing/style_guides.md
@@ -0,0 +1,40 @@
+# Style guides
+
+1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
+ Important sections include [Source Code Layout][rss-source] and
+ [Naming][rss-naming]. Use:
+ - multi-line method chaining style **Option A**: dot `.` on the second line
+ - string literal quoting style **Option A**: single quoted by default
+1. [Rails](https://github.com/bbatsov/rails-style-guide)
+1. [Newlines styleguide][newlines-styleguide]
+1. [Testing][testing]
+1. [JavaScript styleguide][js-styleguide]
+1. [SCSS styleguide][scss-styleguide]
+1. [Shell commands](../shell_commands.md) created by GitLab
+ contributors to enhance security
+1. [Database Migrations](../migration_style_guide.md)
+1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
+1. [Documentation styleguide](../documentation/styleguide.md)
+1. Interface text should be written subjectively instead of objectively. It
+ should be the GitLab core team addressing a person. It should be written in
+ present time and never use past tense (has been/was). For example instead
+ of _prohibited this user from being saved due to the following errors:_ the
+ text should be _sorry, we could not create your account because:_
+1. Code should be written in [US English][us-english]
+
+This is also the style used by linting tools such as
+[RuboCop](https://github.com/bbatsov/rubocop),
+[PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
+
+---
+
+[Return to Contributing documentation](index.md)
+
+[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
+[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
+[doc-guidelines]: ../documentation/index.md "Documentation guidelines"
+[js-styleguide]: ../fe_guide/style_guide_js.md "JavaScript styleguide"
+[scss-styleguide]: ../fe_guide/style_guide_scss.md "SCSS styleguide"
+[newlines-styleguide]: ../newlines_styleguide.md "Newlines styleguide"
+[testing]: ../testing_guide/index.md
+[us-english]: https://en.wikipedia.org/wiki/American_English
diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md
index 9c31265e417..b2c804b2ff0 100644
--- a/doc/development/database_debugging.md
+++ b/doc/development/database_debugging.md
@@ -33,7 +33,7 @@ If your test DB is giving you problems, it is safe to nuke it because it doesn't
- `bundle exec rake db:migrate RAILS_ENV=development`: Execute any pending migrations that you may have picked up from a MR
- `bundle exec rake db:migrate:status RAILS_ENV=development`: Check if all migrations are `up` or `down`
- `bundle exec rake db:migrate:down VERSION=20170926203418 RAILS_ENV=development`: Tear down a migration
- - `bundle exec rake db:migrate:up VERSION=20170926203418 RAILS_ENV=development`: Setup a migration
+ - `bundle exec rake db:migrate:up VERSION=20170926203418 RAILS_ENV=development`: Set up a migration
- `bundle exec rake db:migrate:redo VERSION=20170926203418 RAILS_ENV=development`: Re-run a specific migration
diff --git a/doc/development/database_helpers.md b/doc/development/database_helpers.md
new file mode 100644
index 00000000000..21e4e725de6
--- /dev/null
+++ b/doc/development/database_helpers.md
@@ -0,0 +1,63 @@
+# Database helpers
+
+There are a number of useful helper modules defined in `/lib/gitlab/database/`.
+
+## Subquery
+
+In some cases it is not possible to perform an operation on a query.
+For example:
+
+```ruby
+Geo::EventLog.where('id < 100').limit(10).delete_all
+```
+
+Will give this error:
+
+> ActiveRecord::ActiveRecordError: delete_all doesn't support limit
+
+One solution would be to wrap it in another `where`:
+
+```ruby
+Geo::EventLog.where(id: Geo::EventLog.where('id < 100').limit(10)).delete_all
+```
+
+This works with PostgreSQL, but with MySQL it gives this error:
+
+> ActiveRecord::StatementInvalid: Mysql2::Error: This version of MySQL
+> doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
+
+Also, that query doesn't have very good performance. Using a
+`INNER JOIN` with itself is better.
+
+So instead of this query:
+
+```sql
+SELECT geo_event_log.*
+FROM geo_event_log
+WHERE geo_event_log.id IN
+ (SELECT geo_event_log.id
+ FROM geo_event_log
+ WHERE (id < 100)
+ LIMIT 10)
+```
+
+It's better to write:
+
+```sql
+SELECT geo_event_log.*
+FROM geo_event_log
+INNER JOIN
+ (SELECT geo_event_log.*
+ FROM geo_event_log
+ WHERE (id < 100)
+ LIMIT 10) t2 ON geo_event_log.id = t2.id
+```
+
+And this is where `Gitlab::Database::Subquery.self_join` can help
+you. So you can rewrite the above statement as:
+
+```ruby
+Gitlab::Database::Subquery.self_join(Geo::EventLog.where('id < 100').limit(10)).delete_all
+```
+
+And this also works with MySQL, so you don't need to worry about that.
diff --git a/doc/development/database_merge_request_checklist.md b/doc/development/database_merge_request_checklist.md
index 75c395b61ef..48864c81592 100644
--- a/doc/development/database_merge_request_checklist.md
+++ b/doc/development/database_merge_request_checklist.md
@@ -1,15 +1,15 @@
# Merge Request Checklist
When creating a merge request that performs database related changes (schema
-changes, adjusting queries to optimise performance, etc) you should use the
-merge request template called "Database Changes". This template contains a
+changes, adjusting queries to optimize performance, etc) you should use the
+merge request template called "Database changes". This template contains a
checklist of steps to follow to make sure the changes are up to snuff.
To use the checklist, create a new merge request and click on the "Choose a
-template" dropdown, then click "Database Changes".
+template" dropdown, then click "Database changes".
An example of this checklist can be found at
-https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12463.
+<https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12463>.
The source code of the checklist can be found in at
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20Changes.md
+<https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20changes.md>
diff --git a/doc/development/diffs.md b/doc/development/diffs.md
index d06339480b1..43fc125c21d 100644
--- a/doc/development/diffs.md
+++ b/doc/development/diffs.md
@@ -2,37 +2,35 @@
Currently we rely on different sources to present diffs, these include:
-- Rugged gem
- Gitaly service
- Database (through `merge_request_diff_files`)
- Redis (cached highlighted diffs)
-We're constantly moving Rugged calls to Gitaly and the progress can be followed through [Gitaly repo](https://gitlab.com/gitlab-org/gitaly).
-
## Architecture overview
### Merge request diffs
When refreshing a Merge Request (pushing to a source branch, force-pushing to target branch, or if the target branch now contains any commits from the MR)
we fetch the comparison information using `Gitlab::Git::Compare`, which fetches `base` and `head` data using Gitaly and diff between them through
-`Gitlab::Git::Diff.between` (which uses _Gitaly_ if it's enabled, otherwise _Rugged_).
+`Gitlab::Git::Diff.between`.
The diffs fetching process _limits_ single file diff sizes and the overall size of the whole diff through a series of constant values. Raw diff files are
-then persisted on `merge_request_diff_files` table.
+then persisted on `merge_request_diff_files` table.
-Even though diffs higher than 10kb are collapsed (`Gitlab::Git::Diff::COLLAPSE_LIMIT`), we still keep them on Postgres. However, diff files over _safety limits_
-(see the [Diff limits section](#diff-limits)) are _not_ persisted.
+Even though diffs larger than 10% of the value of `ApplicationSettings#diff_max_patch_bytes` are collapsed,
+we still keep them on Postgres. However, diff files larger than defined _safety limits_
+(see the [Diff limits section](#diff-limits)) are _not_ persisted in the database.
In order to present diffs information on the Merge Request diffs page, we:
1. Fetch all diff files from database `merge_request_diff_files`
-2. Fetch the _old_ and _new_ file blobs in batch to:
- 1. Highlight old and new file content
- 2. Know which viewer it should use for each file (text, image, deleted, etc)
- 3. Know if the file content changed
- 4. Know if it was stored externally
- 5. Know if it had storage errors
-3. If the diff file is cacheable (text-based), it's cached on Redis
-using `Gitlab::Diff::FileCollection::MergeRequestDiff`
+1. Fetch the _old_ and _new_ file blobs in batch to:
+ - Highlight old and new file content
+ - Know which viewer it should use for each file (text, image, deleted, etc)
+ - Know if the file content changed
+ - Know if it was stored externally
+ - Know if it had storage errors
+1. If the diff file is cacheable (text-based), it's cached on Redis
+ using `Gitlab::Diff::FileCollection::MergeRequestDiff`
### Note diffs
@@ -41,9 +39,9 @@ on `NoteDiffFile` (which is associated with the actual `DiffNote`). So instead
of hitting the repository every time we need the diff of the file, we:
1. Check whether we have the `NoteDiffFile#diff` persisted and use it
-2. Otherwise, if it's a current MR revision, use the persisted
-`MergeRequestDiffFile#diff`
-3. In the last scenario, go the the repository and fetch the diff
+1. Otherwise, if it's a current MR revision, use the persisted
+ `MergeRequestDiffFile#diff`
+1. In the last scenario, go the repository and fetch the diff
## Diff limits
@@ -63,55 +61,59 @@ File diffs will be collapsed (but be expandable) if 100 files have already been
```ruby
-Gitlab::Git::DiffCollection.collection_limits[:safe_max_lines] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
+Gitlab::Git::DiffCollection.collection_limits[:safe_max_lines] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
```
File diffs will be collapsed (but be expandable) if 5000 lines have already been rendered.
```ruby
-Gitlab::Git::DiffCollection.collection_limits[:safe_max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] * 5.kilobytes = 500.kilobytes
+Gitlab::Git::DiffCollection.collection_limits[:safe_max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] * 5.kilobytes = 500.kilobytes
```
File diffs will be collapsed (but be expandable) if 500 kilobytes have already been rendered.
```ruby
-Gitlab::Git::DiffCollection.collection_limits[:max_files] = Commit::DIFF_HARD_LIMIT_FILES = 1000
+Gitlab::Git::DiffCollection.collection_limits[:max_files] = Commit::DIFF_HARD_LIMIT_FILES = 1000
```
No more files will be rendered at all if 1000 files have already been rendered.
```ruby
-Gitlab::Git::DiffCollection.collection_limits[:max_lines] = Commit::DIFF_HARD_LIMIT_LINES = 50000
+Gitlab::Git::DiffCollection.collection_limits[:max_lines] = Commit::DIFF_HARD_LIMIT_LINES = 50000
```
No more files will be rendered at all if 50,000 lines have already been rendered.
```ruby
-Gitlab::Git::DiffCollection.collection_limits[:max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:max_files] * 5.kilobytes = 5000.kilobytes
+Gitlab::Git::DiffCollection.collection_limits[:max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:max_files] * 5.kilobytes = 5000.kilobytes
```
No more files will be rendered at all if 5 megabytes have already been rendered.
+*Note:* All collection limit parameters are currently sent and applied on Gitaly. That is, once the limit is surpassed,
+Gitaly will only return the safe amount of data to be persisted on `merge_request_diff_files`.
### Individual diff file limits
Limits that act onto each diff file of a collection. Files number, lines number and files size are considered.
-```ruby
-Gitlab::Git::Diff::COLLAPSE_LIMIT = 10.kilobytes
-```
+#### Expandable patches (collapsed)
-File diff will be collapsed (but be expandable) if it is larger than 10 kilobytes.
+Diff patches are collapsed when surpassing 10% of the value set in `ApplicationSettings#diff_max_patch_bytes`.
+That is, it's equivalent to 10kb if the maximum allowed value is 100kb.
+The diff will still be persisted and expandable if the patch size doesn't
+surpass `ApplicationSettings#diff_max_patch_bytes`.
-```ruby
-Gitlab::Git::Diff::SIZE_LIMIT = 100.kilobytes
-```
+*Note:* Although this nomenclature (Collapsing) is also used on Gitaly, this limit is only used on GitLab (hardcoded - not sent to Gitaly).
+Gitaly will only return `Diff.Collapsed` (RPC) when surpassing collection limits.
-File diff will not be rendered if it's larger than 100 kilobytes.
+#### Not expandable patches (too large)
+The patch not be rendered if it's larger than `ApplicationSettings#diff_max_patch_bytes`.
+Users will see a `This source diff could not be displayed because it is too large` message.
```ruby
Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
@@ -119,10 +121,12 @@ Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines
File diff will be suppressed (technically different from collapsed, but behaves the same, and is expandable) if it has more than 5000 lines.
+*Note:* This limit is currently hardcoded and only applied on GitLab.
+
## Viewers
Diff Viewers, which can be found on `models/diff_viewer/*` are classes used to map metadata about each type of Diff File. It has information
-whether it's a binary, which partial should be used to render it or which File extensions this class accounts for.
+whether it's a binary, which partial should be used to render it or which File extensions this class accounts for.
`DiffViewer::Base` validates _blobs_ (old and new versions) content, extension and file type in order to check if it can be rendered.
diff --git a/doc/development/documentation/img/manual_build_docs.png b/doc/development/documentation/img/manual_build_docs.png
index 615facabb5f..e366a2f7ec4 100644
--- a/doc/development/documentation/img/manual_build_docs.png
+++ b/doc/development/documentation/img/manual_build_docs.png
Binary files differ
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index f5cdd310f6f..b8b86ac1bf5 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -4,9 +4,9 @@ description: Learn how to contribute to GitLab Documentation.
# GitLab Documentation guidelines
- - **General Documentation**: written by the [developers responsible by creating features](#contributing-to-docs). Should be submitted in the same merge request containing code. Feature proposals (by GitLab contributors) should also be accompanied by its respective documentation. They can be later improved by PMs and Technical Writers.
- - **[Technical Articles](#technical-articles)**: written by any [GitLab Team](https://about.gitlab.com/team/) member, GitLab contributors, or [Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/).
- - **Indexes per topic**: initially prepared by the Technical Writing Team, and kept up-to-date by developers and PMs in the same merge request containing code. They gather all resources for that topic in a single page (user and admin documentation, articles, and third-party docs).
+- **General Documentation**: written by the [developers responsible by creating features](#contributing-to-docs). Should be submitted in the same merge request containing code. Feature proposals (by GitLab contributors) should also be accompanied by its respective documentation. They can be later improved by PMs and Technical Writers.
+- **[Technical Articles](#technical-articles)**: written by any [GitLab Team](https://about.gitlab.com/team/) member, GitLab contributors, or [Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/).
+- **Indexes per topic**: initially prepared by the Technical Writing Team, and kept up-to-date by developers and PMs in the same merge request containing code. They gather all resources for that topic in a single page (user and admin documentation, articles, and third-party docs).
## Contributing to docs
@@ -25,68 +25,50 @@ them to review it for you.
We use the [monthly release blog post](https://about.gitlab.com/handbook/marketing/blog/release-posts/#monthly-releases) as a changelog checklist to ensure everything
is documented.
-Whenever you submit a merge request for the documentation, use the documentation MR description template.
+Whenever you submit a merge request for the documentation, use the
+"Documentation" MR description template. If you're changing documentation
+location, use the MR description template called "Change documentation
+location" instead.
-Please check the [documentation workflow](https://about.gitlab.com/handbook/product/technical-writing/workflow/) before getting started.
+## Documentation workflow
-## Documentation structure
-
-- Overview and use cases: what it is, why it is necessary, why one would use it
-- Requirements: what do we need to get started
-- Tutorial: how to set it up, how to use it
-
-Always link a new document from its topic-related index, otherwise, it will
-not be included it in the documentation site search.
-
-_Note: to be extended._
-
-### Feature overview and use cases
-
-Every major feature (regardless if present in GitLab Community or Enterprise editions)
-should present, at the beginning of the document, two main sections: **overview** and
-**use cases**. Every GitLab EE-only feature should also contain these sections.
-
-**Overview**: as the name suggests, the goal here is to provide an overview of the feature.
-Describe what is it, what it does, why it is important/cool/nice-to-have,
-what problem it solves, and what you can do with this feature that you couldn't
-do before.
-
-**Use cases**: provide at least two, ideally three, use cases for every major feature.
-You should answer this question: what can you do with this feature/change? Use cases
-are examples of how this feature or change can be used in real life.
-
-Examples:
-- CE and EE: [Issues](../user/project/issues/index.md#use-cases)
-- CE and EE: [Merge Requests](../user/project/merge_requests/index.md#overview)
-- EE-only: [Geo](https://docs.gitlab.com/ee/gitlab-geo/README.html#overview)
-- EE-only: [Jenkins integration](https://docs.gitlab.com/ee/integration/jenkins.md#overview)
+Please read through the [documentation workflow](workflow.md) before getting started.
-Note that if you don't have anything to add between the doc title (`<h1>`) and
-the header `## Overview`, you can omit the header, but keep the content of the
-overview there.
+## Documentation structure
-> **Overview** and **use cases** are required to **every** Enterprise Edition feature,
-and for every **major** feature present in Community Edition.
+Follow through the [documentation structure guide](structure.md) for learning
+how to structure GitLab docs.
## Markdown and styles
-Currently GitLab docs use Redcarpet as [markdown](../user/markdown.md) engine, but there's an [open discussion](https://gitlab.com/gitlab-com/gitlab-docs/issues/50) for implementing Kramdown in the near future.
+[GitLab docs](https://gitlab.com/gitlab-com/gitlab-docs) uses [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown)
+as markdown engine. Check the [GitLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/)
+for a complete Kramdown reference.
-All the docs follow the [documentation style guidelines](styleguide.md).
+Follow the [documentation style guidelines](styleguide.md) strictly.
## Documentation directory structure
The documentation is structured based on the GitLab UI structure itself,
separated by [`user`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/user),
-[`administrator`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/administration), and [`contributor`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/development).
+[`administrator`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/administration), and [`contributor`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/development).
In order to have a [solid site structure](https://searchengineland.com/seo-benefits-developing-solid-site-structure-277456) for our documentation,
all docs should be linked. Every new document should be cross-linked to its related documentation, and linked from its topic-related index, when existent.
The directories `/workflow/`, `/gitlab-basics/`, `/university/`, and `/articles/` have
-been deprecated and the majority their docs have been moved to their correct location
+been **deprecated** and the majority their docs have been moved to their correct location
in small iterations. Please don't create new docs in these folders.
+### Documentation files
+
+- When you create a new directory, always start with an `index.md` file.
+ Do not use another file name and **do not** create `README.md` files.
+- **Do not** use special chars and spaces, or capital letters in file names,
+ directory names, branch names, and anything that generates a path.
+- Max screenshot size: 100KB.
+- We do not support videos (yet).
+
### Location and naming documents
The documentation hierarchy can be vastly improved by providing a better layout
@@ -103,20 +85,20 @@ and cross-link between any related content.
The table below shows what kind of documentation goes where.
-| Directory | What belongs here |
-| --------- | -------------- |
-| `doc/user/` | User related documentation. Anything that can be done within the GitLab UI goes here including `/admin`. |
-| `doc/administration/` | Documentation that requires the user to have access to the server where GitLab is installed. The admin settings that can be accessed via GitLab's interface go under `doc/user/admin_area/`. |
-| `doc/api/` | API related documentation. |
-| `doc/development/` | Documentation related to the development of GitLab. Any styleguides should go here. |
-| `doc/legal/` | Legal documents about contributing to GitLab. |
-| `doc/install/`| Probably the most visited directory, since `installation.md` is there. Ideally this should go under `doc/administration/`, but it's best to leave it as-is in order to avoid confusion (still debated though). |
-| `doc/update/` | Same with `doc/install/`. Should be under `administration/`, but this is a well known location, better leave as-is, at least for now. |
-| `doc/topics/` | Indexes per Topic (`doc/topics/topic-name/index.md`): all resources for that topic (user and admin documentation, articles, and third-party docs) |
+| Directory | What belongs here |
+|:----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `doc/user/` | User related documentation. Anything that can be done within the GitLab UI goes here including `/admin`. |
+| `doc/administration/` | Documentation that requires the user to have access to the server where GitLab is installed. The admin settings that can be accessed via GitLab's interface go under `doc/user/admin_area/`. |
+| `doc/api/` | API related documentation. |
+| `doc/development/` | Documentation related to the development of GitLab. Any styleguides should go here. |
+| `doc/legal/` | Legal documents about contributing to GitLab. |
+| `doc/install/` | Probably the most visited directory, since `installation.md` is there. Ideally this should go under `doc/administration/`, but it's best to leave it as-is in order to avoid confusion (still debated though). |
+| `doc/update/` | Same with `doc/install/`. Should be under `administration/`, but this is a well known location, better leave as-is, at least for now. |
+| `doc/topics/` | Indexes per Topic (`doc/topics/topic-name/index.md`): all resources for that topic (user and admin documentation, articles, and third-party docs) |
---
-**General rules:**
+**General rules & best practices:**
1. The correct naming and location of a new document, is a combination
of the relative URL of the document in question and the GitLab Map design
@@ -154,13 +136,13 @@ merge request.
Changing a document's location is not to be taken lightly. Remember that the
documentation is available to all installations under `help/` and not only to
-GitLab.com or http://docs.gitlab.com. Make sure this is discussed with the
+GitLab.com or <http://docs.gitlab.com>. Make sure this is discussed with the
Documentation team beforehand.
If you indeed need to change a document's location, do NOT remove the old
document, but rather replace all of its contents with a new line:
-```
+```md
This document was moved to [another location](path/to/new_doc.md).
```
@@ -174,7 +156,7 @@ For example, if you were to move `doc/workflow/lfs/lfs_administration.md` to
1. Copy `doc/workflow/lfs/lfs_administration.md` to `doc/administration/lfs.md`
1. Replace the contents of `doc/workflow/lfs/lfs_administration.md` with:
- ```
+ ```md
This document was moved to [another location](../../administration/lfs.md).
```
@@ -182,7 +164,7 @@ For example, if you were to move `doc/workflow/lfs/lfs_administration.md` to
A quick way to find them is to use `git grep`. First go to the root directory
where you cloned the `gitlab-ce` repository and then do:
- ```
+ ```sh
git grep -n "workflow/lfs/lfs_administration"
git grep -n "lfs/lfs_administration"
```
@@ -203,7 +185,7 @@ Things to note:
documentation, sometimes it might be useful to search a path deeper.
- The `*.md` extension is not used when a document is linked to GitLab's
built-in help page, that's why we omit it in `git grep`.
-- Use the checklist on the documentation MR description template.
+- Use the checklist on the "Change documentation location" MR description template.
#### Alternative redirection method
@@ -218,6 +200,11 @@ redirect_to: '../path/to/file/README.md'
It supports both full and relative URLs, e.g. `https://docs.gitlab.com/ee/path/to/file.html`, `../path/to/file.html`, `path/to/file.md`. Note that any `*.md` paths will be compiled to `*.html`.
+NOTE: **Note:**
+This redirection method will not provide a redirect fallback on GitLab `/help`. When using
+it, make sure to add a link to the new page on the doc, otherwise it's a dead end for users that
+land on the doc via `/help`.
+
### Redirections for pages with Disqus comments
If the documentation page being relocated already has any Disqus comments,
@@ -243,33 +230,14 @@ redirect_from: 'https://docs.gitlab.com/my-old-location/README.html'
Note: it is necessary to include the file name in the `redirect_from` URL,
even if it's `index.html` or `README.html`.
-## Testing
-
-We treat documentation as code, thus have implemented some testing.
-Currently, the following tests are in place:
-
-1. `docs lint`: Check that all internal (relative) links work correctly and
- that all cURL examples in API docs use the full switches. It's recommended
- to [check locally](#previewing-locally) before pushing to GitLab by executing the command
- `bundle exec nanoc check internal_links` on your local
- [`gitlab-docs`](https://gitlab.com/gitlab-com/gitlab-docs) directory.
-1. [`ee_compat_check`](../automatic_ce_ee_merge.md#avoiding-ce-gt-ee-merge-conflicts-beforehand) (runs on CE only):
- When you submit a merge request to GitLab Community Edition (CE),
- there is this additional job that runs against Enterprise Edition (EE)
- and checks if your changes can apply cleanly to the EE codebase.
- If that job fails, read the instructions in the job log for what to do next.
- As CE is merged into EE once a day, it's important to avoid merge conflicts.
- Submitting an EE-equivalent merge request cherry-picking all commits from CE to EE is
- essential to avoid them.
-
## Branch naming
If your contribution contains **only** documentation changes, you can speed up
the CI process by following some branch naming conventions. You have three
choices:
-| Branch name | Valid example |
-| ----------- | ------------- |
+| Branch name | Valid example |
+|:----------------------|:-----------------------------|
| Starting with `docs/` | `docs/update-api-issues` |
| Starting with `docs-` | `docs-update-api-issues` |
| Ending in `-docs` | `123-update-api-issues-docs` |
@@ -289,7 +257,6 @@ for GitLab Team members.
- Label the MR `Documentation`
- Assign the correct milestone (see note below)
-
NOTE: **Note:**
If the release version you want to add the documentation to has already been
frozen or released, use the label `Pick into X.Y` to get it merged into
@@ -310,118 +277,37 @@ Follow this [method for cherry-picking from CE to EE](../automatic_ce_ee_merge.m
- Create the EE-equivalent branch ending with `-ee`, e.g.,
`git checkout -b docs-example-ee`
- Once all the jobs are passing in CE and EE, and you've addressed the
-feedback from your own team, assign the CE MR to a technical writer for review
+ feedback from your own team, assign the CE MR to a technical writer for review
- When both MRs are ready, the EE merge request will be merged first, and the
-CE-equivalent will be merged next.
+ CE-equivalent will be merged next.
- Note that the review will occur only in the CE MR, as the EE MR
-contains the same commits as the CE MR.
+ contains the same commits as the CE MR.
- If you have a few more changes that apply to the EE-version only, you can submit
-a couple more commits to the EE branch, but ask the reviewer to review the EE merge request
-additionally to the CE MR. If there are many EE-only changes though, start a new MR
-to EE only.
-
-## Previewing the changes live
-
-NOTE: **Note:**
-To preview your changes to documentation locally, follow this
-[development guide](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md#development).
-
-The live preview is currently enabled for the following projects:
-
-- https://gitlab.com/gitlab-org/gitlab-ce
-- https://gitlab.com/gitlab-org/gitlab-ee
-- https://gitlab.com/gitlab-org/gitlab-runner
-
-If your branch contains only documentation changes, you can use
-[special branch names](#branch-naming) to avoid long running pipelines.
-
-For [docs-only changes](#branch-naming), the review app is run automatically.
-For all other branches, you can use the manual `review-docs-deploy-manual` job
-in your merge request. You will need at least Maintainer permissions to be able
-to run it. In the mini pipeline graph, you should see an `>>` icon. Clicking on it will
-reveal the `review-docs-deploy-manual` job. Hit the play button for the job to start.
-
-![Manual trigger a docs build](img/manual_build_docs.png)
-
-NOTE: **Note:**
-You will need to push a branch to those repositories, it doesn't work for forks.
-
-The `review-docs-deploy*` job will:
-
-1. Create a new branch in the [gitlab-docs](https://gitlab.com/gitlab-com/gitlab-docs)
- project named after the scheme: `$DOCS_GITLAB_REPO_SUFFIX-$CI_ENVIRONMENT_SLUG`,
- where `DOCS_GITLAB_REPO_SUFFIX` is the suffix for each product, e.g, `ce` for
- CE, etc.
-1. Trigger a cross project pipeline and build the docs site with your changes
-
-After a few minutes, the Review App will be deployed and you will be able to
-preview the changes. The docs URL can be found in two places:
-
-- In the merge request widget
-- In the output of the `review-docs-deploy*` job, which also includes the
- triggered pipeline so that you can investigate whether something went wrong
-
-In case the Review App URL returns 404, follow these steps to debug:
-
-1. **Did you follow the URL from the merge request widget?** If yes, then check if
- the link is the same as the one in the job output.
-1. **Did you follow the URL from the job output?** If yes, then it means that
- either the site is not yet deployed or something went wrong with the remote
- pipeline. Give it a few minutes and it should appear online, otherwise you
- can check the status of the remote pipeline from the link in the job output.
- If the pipeline failed or got stuck, drop a line in the `#docs` chat channel.
-
-TIP: **Tip:**
-Someone that has no merge rights to the CE/EE projects (think of forks from
-contributors) will not be able to run the manual job. In that case, you can
-ask someone from the GitLab team who has the permissions to do that for you.
-
-NOTE: **Note:**
-Make sure that you always delete the branch of the merge request you were
-working on. If you don't, the remote docs branch won't be removed either,
-and the server where the Review Apps are hosted will eventually be out of
-disk space.
-
-### Technical aspects
-
-If you want to know the hot details, here's what's really happening:
-
-1. You manually run the `review-docs-deploy` job in a CE/EE merge request.
-1. The job runs the [`scripts/trigger-build-docs`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/trigger-build-docs)
- script with the `deploy` flag, which in turn:
- 1. Takes your branch name and applies the following:
- - The slug of the branch name is used to avoid special characters since
- ultimately this will be used by NGINX.
- - The `preview-` prefix is added to avoid conflicts if there's a remote branch
- with the same name that you created in the merge request.
- - The final branch name is truncated to 42 characters to avoid filesystem
- limitations with long branch names (> 63 chars).
- 1. The remote branch is then created if it doesn't exist (meaning you can
- re-run the manual job as many times as you want and this step will be skipped).
- 1. A new cross-project pipeline is triggered in the docs project.
- 1. The preview URL is shown both at the job output and in the merge request
- widget. You also get the link to the remote pipeline.
-1. In the docs project, the pipeline is created and it
- [skips the test jobs](https://gitlab.com/gitlab-com/gitlab-docs/blob/8d5d5c750c602a835614b02f9db42ead1c4b2f5e/.gitlab-ci.yml#L50-55)
- to lower the build time.
-1. Once the docs site is built, the HTML files are uploaded as artifacts.
-1. A specific Runner tied only to the docs project, runs the Review App job
- that downloads the artifacts and uses `rsync` to transfer the files over
- to a location where NGINX serves them.
-
-The following GitLab features are used among others:
-
-- [Manual actions](../../ci/yaml/README.md#manual-actions)
-- [Multi project pipelines](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html)
-- [Review Apps](../../ci/review_apps/index.md)
-- [Artifacts](../../ci/yaml/README.md#artifacts)
-- [Specific Runner](../../ci/runners/README.md#locking-a-specific-runner-from-being-enabled-for-other-projects)
+ a couple more commits to the EE branch, but ask the reviewer to review the EE merge request
+ additionally to the CE MR. If there are many EE-only changes though, start a new MR
+ to EE only.
## GitLab `/help`
Every GitLab instance includes the documentation, which is available from `/help`
(`http://my-instance.com/help`), e.g., <https://gitlab.com/help>.
+The documentation available online on docs.gitlab.com is continuously
+deployed every hour from the `master` branch of CE, EE, Omnibus, and Runner. Therefore,
+once a merge request gets merged, it will be available online on the same day.
+However, they will be shipped (and available on `/help`) within the milestone assigned
+to the MR.
+
+For instance, let's say your merge request has a milestone set to 11.3, which
+will be released on 2018-09-22. If it gets merged on 2018-09-15, it will be
+available online on 2018-09-15, but, as the feature freeze date has passed, if
+the MR does not have a "pick into 11.3" label, the milestone has to be changed
+to 11.4 and it will be shipped with all GitLab packages only on 2018-10-22,
+with GitLab 11.4. Meaning, it will only be available under `/help` from GitLab
+11.4 onwards, but available on docs.gitlab.com on the same day it was merged.
+
+### Linking to `/help`
+
When you're building a new feature, you may need to link the documentation
from GitLab, the application. This is normally done in files inside the
`app/views/` directory with the help of the `help_page_path` helper method.
@@ -506,7 +392,7 @@ They should be placed in a new directory named `/article-title/index.md` under a
- **User guides**: technical content to guide regular users from point A to point B
- **Admin guides**: technical content to guide administrators of GitLab instances from point A to point B
- **Technical Overviews**: technical content describing features, solutions, and third-party integrations
-- **Tutorials**: technical content provided step-by-step on how to do things, or how to reach very specific objectives
+- **Tutorials**: technical content provided step-by-step on how to do things, or how to reach specific objectives
#### Understanding guides, tutorials, and technical overviews
@@ -514,7 +400,7 @@ Suppose there's a process to go from point A to point B in 5 steps: `(A) 1 > 2 >
A **guide** can be understood as a description of certain processes to achieve a particular objective. A guide brings you from A to B describing the characteristics of that process, but not necessarily going over each step. It can mention, for example, steps 2 and 3, but does not necessarily explain how to accomplish them.
-- Live example: "[Static sites and GitLab Pages domains (Part 1)](../user/project/pages/getting_started_part_one.md) to [Creating and Tweaking GitLab CI/CD for GitLab Pages (Part 4)](../../user/project/pages/getting_started_part_four.md)"
+- Live example: "[Static sites and GitLab Pages domains (Part 1)](../../user/project/pages/getting_started_part_one.md) to [Creating and Tweaking GitLab CI/CD for GitLab Pages (Part 4)](../../user/project/pages/getting_started_part_four.md)"
A **tutorial** requires a clear **step-by-step** guidance to achieve a singular objective. It brings you from A to B, describing precisely all the necessary steps involved in that process, showing each of the 5 steps to go from A to B.
It does not only describes steps 2 and 3, but also shows you how to accomplish them.
@@ -538,7 +424,6 @@ with the following information:
For example:
-
```yaml
---
author: John Doe
@@ -553,5 +438,250 @@ date: 2017-02-01
Use the [writing method](https://about.gitlab.com/handbook/product/technical-writing/#writing-method) defined by the Technical Writing team.
+## Previewing the changes live
+
+NOTE: **Note:**
+To preview your changes to documentation locally, follow this
+[development guide](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md#development-when-contributing-to-gitlab-documentation) or [these instructions for GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/gitlab_docs.md).
+
+The live preview is currently enabled for the following projects:
+
+- <https://gitlab.com/gitlab-org/gitlab-ce>
+- <https://gitlab.com/gitlab-org/gitlab-ee>
+- <https://gitlab.com/gitlab-org/gitlab-runner>
+
+If your branch contains only documentation changes, you can use
+[special branch names](#branch-naming) to avoid long running pipelines.
+
+For [docs-only changes](#branch-naming), the review app is run automatically.
+For all other branches, you can use the manual `review-docs-deploy-manual` job
+in your merge request. You will need at least Maintainer permissions to be able
+to run it. In the mini pipeline graph, you should see an `>>` icon. Clicking on it will
+reveal the `review-docs-deploy-manual` job. Hit the play button for the job to start.
+
+![Manual trigger a docs build](img/manual_build_docs.png)
+
+NOTE: **Note:**
+You will need to push a branch to those repositories, it doesn't work for forks.
+
+The `review-docs-deploy*` job will:
+
+1. Create a new branch in the [gitlab-docs](https://gitlab.com/gitlab-com/gitlab-docs)
+ project named after the scheme: `$DOCS_GITLAB_REPO_SUFFIX-$CI_ENVIRONMENT_SLUG`,
+ where `DOCS_GITLAB_REPO_SUFFIX` is the suffix for each product, e.g, `ce` for
+ CE, etc.
+1. Trigger a cross project pipeline and build the docs site with your changes
+
+After a few minutes, the Review App will be deployed and you will be able to
+preview the changes. The docs URL can be found in two places:
+
+- In the merge request widget
+- In the output of the `review-docs-deploy*` job, which also includes the
+ triggered pipeline so that you can investigate whether something went wrong
+
+TIP: **Tip:**
+Someone that has no merge rights to the CE/EE projects (think of forks from
+contributors) will not be able to run the manual job. In that case, you can
+ask someone from the GitLab team who has the permissions to do that for you.
+
+NOTE: **Note:**
+Make sure that you always delete the branch of the merge request you were
+working on. If you don't, the remote docs branch won't be removed either,
+and the server where the Review Apps are hosted will eventually be out of
+disk space.
+
+### Troubleshooting review apps
+
+In case the review app URL returns 404, follow these steps to debug:
+
+1. **Did you follow the URL from the merge request widget?** If yes, then check if
+ the link is the same as the one in the job output.
+1. **Did you follow the URL from the job output?** If yes, then it means that
+ either the site is not yet deployed or something went wrong with the remote
+ pipeline. Give it a few minutes and it should appear online, otherwise you
+ can check the status of the remote pipeline from the link in the job output.
+ If the pipeline failed or got stuck, drop a line in the `#docs` chat channel.
+
+### Technical aspects
+
+If you want to know the in-depth details, here's what's really happening:
+
+1. You manually run the `review-docs-deploy` job in a CE/EE merge request.
+1. The job runs the [`scripts/trigger-build-docs`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/trigger-build-docs)
+ script with the `deploy` flag, which in turn:
+ 1. Takes your branch name and applies the following:
+ - The slug of the branch name is used to avoid special characters since
+ ultimately this will be used by NGINX.
+ - The `preview-` prefix is added to avoid conflicts if there's a remote branch
+ with the same name that you created in the merge request.
+ - The final branch name is truncated to 42 characters to avoid filesystem
+ limitations with long branch names (> 63 chars).
+ 1. The remote branch is then created if it doesn't exist (meaning you can
+ re-run the manual job as many times as you want and this step will be skipped).
+ 1. A new cross-project pipeline is triggered in the docs project.
+ 1. The preview URL is shown both at the job output and in the merge request
+ widget. You also get the link to the remote pipeline.
+1. In the docs project, the pipeline is created and it
+ [skips the test jobs](https://gitlab.com/gitlab-com/gitlab-docs/blob/8d5d5c750c602a835614b02f9db42ead1c4b2f5e/.gitlab-ci.yml#L50-55)
+ to lower the build time.
+1. Once the docs site is built, the HTML files are uploaded as artifacts.
+1. A specific Runner tied only to the docs project, runs the Review App job
+ that downloads the artifacts and uses `rsync` to transfer the files over
+ to a location where NGINX serves them.
+
+The following GitLab features are used among others:
+
+- [Manual actions](../../ci/yaml/README.md#manual-actions)
+- [Multi project pipelines](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html)
+- [Review Apps](../../ci/review_apps/index.md)
+- [Artifacts](../../ci/yaml/README.md#artifacts)
+- [Specific Runner](../../ci/runners/README.md#locking-a-specific-runner-from-being-enabled-for-other-projects)
+
+## Testing
+
+We treat documentation as code, thus have implemented some testing.
+Currently, the following tests are in place:
+
+1. `docs lint`: Check that all internal (relative) links work correctly and
+ that all cURL examples in API docs use the full switches. It's recommended
+ to [check locally](#previewing-locally) before pushing to GitLab by executing the command
+ `bundle exec nanoc check internal_links` on your local
+ [`gitlab-docs`](https://gitlab.com/gitlab-com/gitlab-docs) directory.
+1. [`ee_compat_check`](../automatic_ce_ee_merge.md#avoiding-ce-gt-ee-merge-conflicts-beforehand) (runs on CE only):
+ When you submit a merge request to GitLab Community Edition (CE),
+ there is this additional job that runs against Enterprise Edition (EE)
+ and checks if your changes can apply cleanly to the EE codebase.
+ If that job fails, read the instructions in the job log for what to do next.
+ As CE is merged into EE once a day, it's important to avoid merge conflicts.
+ Submitting an EE-equivalent merge request cherry-picking all commits from CE to EE is
+ essential to avoid them.
+
+### Linting
+
+To help adhere to the [documentation style guidelines](styleguide.md), and to improve the content
+added to documentation, consider locally installing and running documentation linters. This will
+help you catch common issues before raising merge requests for review of documentation.
+
+The following are some suggested linters you can install locally and sample configuration:
+
+- [`proselint`](#proselint)
+- [`markdownlint`](#markdownlint)
+
+NOTE: **Note:**
+This list does not limit what other linters you can add to your local documentation writing toolchain.
+
+#### `proselint`
+
+`proselint` checks for common problems with English prose. It provides a
+ [plethora of checks](http://proselint.com/checks/) that are helpful for technical writing.
+
+`proselint` can be used [on the command line](http://proselint.com/utility/), either on a single
+ Markdown file or on all Markdown files in a project. For example, to run `proselint` on all
+ documentation in the [`gitlab-ce` project](https://gitlab.com/gitlab-org/gitlab-ce), run the
+ following commands from within the `gitlab-ce` project:
+
+```sh
+cd doc
+proselint **/*.md
+```
+
+`proselint` can also be run from within editors using plugins. For example, the following plugins
+ are available:
+
+- [Sublime Text](https://packagecontrol.io/packages/SublimeLinter-contrib-proselint)
+- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=PatrykPeszko.vscode-proselint)
+- [Others](https://github.com/amperser/proselint#plugins-for-other-software)
+
+##### Sample `proselint` configuration
+
+All of the checks are good to use. However, excluding the `typography.symbols` and `misc.phrasal_adjectives` checks will reduce
+noise. The following sample `proselint` configuration disables these checks:
+
+```json
+{
+ "checks": {
+ "typography.symbols": false,
+ "misc.phrasal_adjectives": false
+ }
+}
+```
+
+A file with `proselint` configuration must be placed in a
+[valid location](https://github.com/amperser/proselint#checks). For example, `~/.config/proselint/config`.
+
+#### `markdownlint`
+
+`markdownlint` checks that certain rules ([example](https://github.com/DavidAnson/markdownlint/blob/master/README.md#rules--aliases))
+ are followed for Markdown syntax. Our [style guidelines](styleguide.md) elaborate on which choices
+ must be made when selecting Markdown syntax for GitLab documentation and this tool helps
+ catch deviations from those guidelines.
+
+`markdownlint` can be used [on the command line](https://github.com/igorshubovych/markdownlint-cli#markdownlint-cli--),
+ either on a single Markdown file or on all Markdown files in a project. For example, to run
+ `markdownlint` on all documentation in the [`gitlab-ce` project](https://gitlab.com/gitlab-org/gitlab-ce),
+ run the following commands from within the `gitlab-ce` project:
+
+```sh
+cd doc
+markdownlint **/*.md
+```
+
+`markdownlint` can also be run from within editors using plugins. For example, the following plugins
+ are available:
+
+- [Sublime Text](https://packagecontrol.io/packages/SublimeLinter-contrib-markdownlint)
+- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint)
+- [Others](https://github.com/DavidAnson/markdownlint#related)
+
+##### Sample `markdownlint` configuration
+
+The following sample `markdownlint` configuration modifies the available default rules to:
+
+- Adhere to the [style guidelines](styleguide.md).
+- Apply conventions found in the GitLab documentation.
+- Allow the flexibility of using some inline HTML.
+
+```json
+{
+ "default": true,
+ "header-style": { "style": "atx" },
+ "ul-style": { "style": "dash" },
+ "line-length": false,
+ "no-trailing-punctuation": false,
+ "ol-prefix": { "style": "one" },
+ "blanks-around-fences": false,
+ "no-inline-html": {
+ "allowed_elements": [
+ "table",
+ "tbody",
+ "tr",
+ "td",
+ "ul",
+ "ol",
+ "li",
+ "br",
+ "img",
+ "a",
+ "strong",
+ "i",
+ "div"
+ ]
+ },
+ "hr-style": { "style": "---" },
+ "fenced-code-language": false
+}
+```
+
+For [`markdownlint`](https://github.com/DavidAnson/markdownlint/), this configuration must be
+placed in a [valid location](https://github.com/igorshubovych/markdownlint-cli#configuration). For
+example, `~/.markdownlintrc`.
+
+## Danger bot
+
+GitLab uses [danger bot](https://github.com/danger/danger) for some elements in
+code review. For docs changes in merge requests, whenever a change under `/doc`
+is made, the bot leaves a comment for the author to mention `@gl-docsteam`, so
+that the docs can be properly reviewed.
+
[gitlab-map]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png
[graffle]: https://gitlab.com/gitlab-org/gitlab-design/blob/d8d39f4a87b90fb9ae89ca12dc565347b4900d5e/production/resources/gitlab-map.graffle
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
new file mode 100644
index 00000000000..607ad21d459
--- /dev/null
+++ b/doc/development/documentation/structure.md
@@ -0,0 +1,190 @@
+---
+description: Learn the how to correctly structure GitLab documentation.
+---
+
+# Documentation structure
+
+For consistency throughout the documentation, it's important to maintain the same
+structure among the docs.
+
+Before getting started, read through the following docs:
+
+- [Contributing to GitLab documentation](index.md#contributing-to-docs)
+- [Merge requests for GitLab documentation](index.md#merge-requests-for-gitlab-documentation)
+- [Branch naming for docs-only changes](index.md#branch-naming)
+- [Documentation directory structure](index.md#documentation-directory-structure)
+- [Documentation style guidelines](styleguide.md)
+- [Documentation workflow](workflow.md)
+
+## Documentation blurb
+
+Every document should include the following content in the following sequence:
+
+- **Feature name**: defines an intuitive name for the feature that clearly
+ states what it is and is consistent with any relevant UI text.
+- **Feature overview** and description: describe what it is, what it does, and in what context it should be used.
+- **Use cases**: describes real use case scenarios for that feature.
+- **Requirements**: describes what software and/or configuration is required to be able to
+ use the feature and, if applicable, prerequisite knowledge for being able to follow/implement the tutorial.
+ For example, familiarity with GitLab CI/CD, an account on a third-party service, dependencies installed, etc.
+ Link each one to its most relevant resource; i.e., where the reader can go to begin to fullfil that requirement.
+ (Another doc page, a third party application's site, etc.)
+- **Instructions**: clearly describes the steps to use the feature, leaving no gaps.
+- **Troubleshooting** guide (recommended but not required): if you know beforehand what issues
+ one might have when setting it up, or when something is changed, or on upgrading, it's
+ important to describe those too. Think of things that may go wrong and include them in the
+ docs. This is important to minimize requests for support, and to avoid doc comments with
+ questions that you know someone might ask. Answering them beforehand only makes your
+ document better and more approachable.
+
+For additional details, see the subsections below, as well as the [Documentation template for new docs](#Documentation-template-for-new-docs).
+
+### Feature overview and use cases
+
+Every major feature (regardless if present in GitLab Community or Enterprise editions)
+should present, at the beginning of the document, two main sections: **overview** and
+**use cases**. Every GitLab EE-only feature should also contain these sections.
+
+**Overview**: as the name suggests, the goal here is to provide an overview of the feature.
+Describe what is it, what it does, why it is important/cool/nice-to-have,
+what problem it solves, and what you can do with this feature that you couldn't
+do before.
+
+**Use cases**: provide at least two, ideally three, use cases for every major feature.
+You should answer this question: what can you do with this feature/change? Use cases
+are examples of how this feature or change can be used in real life.
+
+Examples:
+
+- CE and EE: [Issues](../../user/project/issues/index.md#use-cases)
+- CE and EE: [Merge Requests](../../user/project/merge_requests/index.md)
+- EE-only: [Geo](https://docs.gitlab.com/ee/administration/geo/replication/index.html)
+- EE-only: [Jenkins integration](https://docs.gitlab.com/ee/integration/jenkins.html)
+
+Note that if you don't have anything to add between the doc title (`<h1>`) and
+the header `## Overview`, you can omit the header, but keep the content of the
+overview there.
+
+> **Overview** and **use cases** are required to **every** Enterprise Edition feature,
+and for every **major** feature present in Community Edition.
+
+### Discoverability
+
+Your new document will be discoverable by the user only if:
+
+- Crosslinked from the higher-level index (e.g., Issue Boards docs
+ should be linked from Issues; Prometheus docs should be linked from
+ Monitoring; CI/CD tutorials should be linked from CI/CD examples).
+ - When referencing other GitLab products and features, link to their
+ respective docs; when referencing third-party products or technologies,
+ link out to their external sites, documentation, and resources.
+- The headings are clear. E.g., "App testing" is a bad heading, "Testing
+ an application with GitLab CI/CD" is much better. Think of something
+ someone will search for and use these keywords in the headings.
+
+## Documentation template for new docs
+
+To start a new document, respect the file tree and file name guidelines,
+as well as the style guidelines. Use the following template:
+
+```md
+---
+description: "short document description." # Up to ~200 chars long. They will be displayed in Google Search Snippets.
+---
+
+# Feature Name **[TIER]** (1)
+
+> [Introduced](link_to_issue_or_mr) in GitLab Tier X.Y (2).
+
+A short description for the feature (can be the same used in the frontmatter's
+`description`).
+
+## Overview
+
+To write the feature overview, you should consider answering the following questions:
+
+- What is it?
+- Who is it for?
+- What is the context in which it is used and are there any prerequisites/requirements?
+- What can the user do with it? (Be sure to consider multiple audiences, like GitLab admin and developer-user.)
+- What are the benefits to using it over any alternatives?
+
+## Use cases
+
+Describe one to three use cases for that feature. Give real-life examples.
+
+## Requirements
+
+State any requirements, if any, for using the feature and/or following along with the tutorial.
+
+The only assumption that is redundant and doesn't need to be mentioned is having an account
+on GitLab.
+
+## Instructions
+
+("Instructions" is not necessarily the name of the heading)
+
+- Write a step-by-step guide, with no gaps between the steps.
+- Start with an h2 (`##`), break complex steps into small steps using
+subheadings h3 > h4 > h5 > h6. _Never skip the hierarchy level, such
+as h2 > h4_, as it will break the TOC and may affect the breadcrumbs.
+- Use short and descriptive headings (up to ~50 chars). You can use one
+single heading `## How it works` for the instructions when the feature
+is simple and the document is short.
+- Be clear, concise, and stick to the goal of the doc: explain how to
+use that feature.
+- Use inclusive language and avoid jargons, as well as uncommon and
+fancy words. The docs should be clear and easy to understand.
+- Write in the 3rd person (use "we", "you", "us", "one", instead of "I" or "me").
+- Always provide internal and external reference links.
+- Always link the doc from its higher-level index.
+
+<!-- ## Troubleshooting
+
+Add a troubleshooting guide when possible/applicable. -->
+
+---
+
+Notes:
+
+- (1): Apply the [tier badges](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) accordingly
+- (2): Apply the correct format for the [GitLab version introducing the feature](https://docs.gitlab.com/ee/development/documentation/styleguide.html#gitlab-versions-and-tiers)
+```
+
+## Help and feedback section
+
+The "help and feedback" section (introduced by [!319](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/319)) displayed at the end of each document
+can be omitted from the doc by adding a key into the its frontmatter:
+
+```yaml
+---
+feedback: false
+---
+```
+
+The default is to leave it there. If you want to omit it from a document,
+you must check with a technical writer before doing so.
+
+### Disqus
+
+We also have integrated the docs site with Disqus (introduced by
+[!151](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/151)),
+allowing our users to post comments.
+
+To omit only the comments from the feedback section, use the following
+key on the frontmatter:
+
+```yaml
+---
+comments: false
+---
+```
+
+We are only hiding comments in main index pages, such as [the main documentation index](../../README.md), since its content is too broad to comment on. Before omitting Disqus,
+you must check with a technical writer.
+
+Note that once `feedback: false` is added to the frontmatter, it will automatically omit
+Disqus, therefore, don't add both keys to the same document.
+
+The click events in the feedback section are tracked with Google Tag Manager. The
+conversions can be viewed on Google Analytics by navigating to **Behavior > Events > Top events > docs**.
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index ad49c77aac8..8309ba9a72c 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -10,82 +10,182 @@ GitLab documentation. Check the
Check the GitLab handbook for the [writing styles guidelines](https://about.gitlab.com/handbook/communication/#writing-style-guidelines).
-## Text
+For help adhering to the guidelines, see [linting](index.md#linting).
+
+## Files
+
+- [Directory structure](index.md#location-and-naming-documents): place the docs
+in the correct location.
+- [Documentation files](index.md#documentation-files): name the files accordingly.
+
+DANGER: **Attention:**
+**Do not** use capital letters, spaces, or special chars in file names,
+branch names, directory names, headings, or in anything that generates a path.
+
+NOTE: **Note:**
+**Do not** create new `README.md` files, name them `index.md` instead. There's
+a test that will fail if it spots a new `README.md` file.
+
+### Markdown
+
+The [documentation website](https://docs.gitlab.com) had its markdown engine migrated from [Redcarpet to GitLab Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108)
+in October, 2018.
+
+The [`gitlab-kramdown`](https://gitlab.com/gitlab-org/gitlab_kramdown)
+gem will support all [GFM markup](../../user/markdown.md) in the future. For now,
+use regular markdown markup, following the rules on this style guide. For a complete
+Kramdown reference, check the [GiLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
+Use Kramdown markup wisely: do not overuse its specific markup (e.g., `{:.class}`) as it will not render properly in
+[`/help`](#gitlab-help).
+
+## Content
-- Split up long lines (wrap text), this makes it much easier to review and edit. Only
- double line breaks are shown as a full line break in [GitLab markdown][gfm].
- 80-100 characters is a good line length
- Make sure that the documentation is added in the correct
- [directory](index.md#documentation-directory-structure) and that
- there's a link to it somewhere useful
-- Do not duplicate information
-- Be brief and clear
-- Unless there's a logical reason not to, add documents in alphabetical order
-- Write in US English
-- Use [single spaces][] instead of double spaces
-- Jump a line between different markups (e.g., after every paragraph, header, list, etc)
-- Capitalize "G" and "L" in GitLab
-- Use sentence case for titles, headings, labels, menu items, and buttons.
+ [directory](index.md#documentation-directory-structure), linked from its
+ higher-level index, and linked from other related pages.
+- Do not duplicate information.
+- Be brief and clear.
+- Unless there's a logical reason not to, structure the document in alphabetical order
+(headings, tables, and lists).
+- Write in US English.
+- Capitalize "G" and "L" in GitLab.
- Use title case when referring to [features](https://about.gitlab.com/features/) or
[products](https://about.gitlab.com/pricing/) (e.g., GitLab Runner, Geo,
Issue Boards, GitLab Core, Git, Prometheus, Kubernetes, etc), and methods or methodologies
(e.g., Continuous Integration, Continuous Deployment, Scrum, Agile, etc). Note that
-some features are also objects (e.g. "Merge Requests" and "merge requests").
+some features are also objects (e.g. "GitLab's Merge Requests support X." and "Create a new merge request for Z.").
-## Formatting
+## Text
-- Use double asterisks (`**`) to mark a word or text in bold (`**bold**`)
-- Use undescore (`_`) for text in italics (`_italic_`)
-- Jump a line between different markups, for example:
+- Split up long lines (wrap text), this makes it much easier to review and edit. Only
+ double line breaks are shown as a full line break by creating new paragraphs.
+ 80-100 characters is the recommended line length.
+- Use sentence case for titles, headings, labels, menu items, and buttons.
+- Jump a line between different markups (e.g., after every paragraph, header, list, etc). Example:
```md
## Header
Paragraph.
- - List item
- - List item
+ - List item 1
+ - List item 2
+ ```
+
+## Emphasis
+
+- Use double asterisks (`**`) to mark a word or text in bold (`**bold**`).
+- Use undescore (`_`) for text in italics (`_italic_`).
+- Use greater than (`>`) for blockquotes.
+
+## Punctuation
+
+Check the general punctuation rules for the GitLab documentation on the table below.
+Check specific punctuation rules for [list items](#list-items) below.
+
+| Rule | Example |
+| ---- | ------- |
+| Always end full sentences with a period. | _For a complete overview, read through this document._|
+| Always add a space after a period when beginning a new sentence | _For a complete overview, check this doc. For other references, check out this guide._ |
+| Do not use double spaces. | --- |
+| Do not use tabs for indentation. Use spaces instead. You can configure your code editor to output spaces instead of tabs when pressing the tab key. | --- |
+| Use serial commas ("Oxford commas") before the final 'and/or' in a list. | _You can create new issues, merge requests, and milestones._ |
+| Always add a space before and after dashes when using it in a sentence (for replacing a comma, for example). | _You should try this - or not._ |
+| Always use lowercase after a colon. | _Related Issues: a way to create a relationship between issues._ |
+
+## List items
+
+- Always start list items with a capital letter.
+- Always leave a blank line before and after a list.
+- Begin a line with spaces (not tabs) to denote a subitem.
+- To nest subitems, indent them with two spaces.
+- To nest code blocks, indent them with four spaces.
+- Only use ordered lists when their items describe a sequence of steps to follow.
+
+**Markup:**
+
+- Use dashes (`- `) for unordered lists instead of asterisks (`* `).
+- Use the number one (`1`) for each item in an ordered list.
+When rendered, the list items will appear with sequential numbering.
+
+**Punctuation:**
+
+- Do not add commas (`,`) or semicolons (`;`) to the end of a list item.
+- Only add periods to the end of a list item if the item consists of a complete sentence. The [definition of full sentence](https://www2.le.ac.uk/offices/ld/resources/writing/grammar/grammar-guides/sentence) is: _"a complete sentence always contains a verb, expresses a complete idea, and makes sense standing alone"_.
+- Be consistent throughout the list: if the majority of the items do not end in a period, do not end any of the items in a period, even if they consist of a complete sentence. The opposite is also valid: if the majority of the items end with a period, end all with a period.
+- Separate list items from explanatory text with a colon (`:`). For example:
+
+ ```md
+ The list is as follows:
+
+ - First item: this explains the first item.
+ - Second item: this explains the second item.
```
-### Punctuation
+**Examples:**
+
+Do:
+
+- First list item
+- Second list item
+- Third list item
-For punctuation rules, please refer to the [GitLab UX guide](https://design.gitlab.com/content/punctuation/).
+Don't:
-### Ordered and unordered lists
+- First list item
+- Second list item
+- Third list item.
-- Use dashes (`-`) for unordered lists instead of asterisks (`*`)
-- Use the number one (`1`) for ordered lists
-- For punctuation in bullet lists, please refer to the [GitLab UX guide](https://design.gitlab.com/content/punctuation/)
+Do:
+
+- Let's say this is a complete sentence.
+- Let's say this is also a complete sentence.
+- Not a complete sentence.
+
+Don't:
+
+- Let's say this is a complete sentence.
+- Let's say this is also a complete sentence.
+- Not a complete sentence
+
+## Quotes
+
+Valid for markdown content only, not for frontmatter entries:
+
+- Standard quotes: double quotes (`"`). Example: "This is wrapped in double quotes".
+- Quote within a quote: double quotes (`"`) wrap single quotes (`'`). Example: "I am 'quoting' something within a quote".
+
+For other punctuation rules, please refer to the
+[GitLab UX guide](https://design.gitlab.com/content/punctuation/).
## Headings
- Add **only one H1** in each document, by adding `#` at the beginning of
it (when using markdown). The `h1` will be the document `<title>`.
-- For subheadings, use `##`, `###` and so on
+- Start with an `h2` (`##`), and respect the order `h2` > `h3` > `h4` > `h5` > `h6`.
+ Never skip the hierarchy level, such as `h2` > `h4`
- Avoid putting numbers in headings. Numbers shift, hence documentation anchor
links shift too, which eventually leads to dead links. If you think it is
compelling to add numbers in headings, make sure to at least discuss it with
- someone in the Merge Request
+ someone in the Merge Request.
- [Avoid using symbols and special chars](https://gitlab.com/gitlab-com/gitlab-docs/issues/84)
in headers. Whenever possible, they should be plain and short text.
- Avoid adding things that show ephemeral statuses. For example, if a feature is
considered beta or experimental, put this info in a note, not in the heading.
- When introducing a new document, be careful for the headings to be
- grammatically and syntactically correct. Mention one or all
- of the following GitLab members for a review: `@axil` or `@marcia`.
+ grammatically and syntactically correct. Mention an [assigned technical writer (TW)](https://about.gitlab.com/handbook/product/categories/)
+ for review.
This is to ensure that no document with wrong heading is going
live without an audit, thus preventing dead links and redirection issues when
- corrected
-- Leave exactly one new line after a heading
+ corrected.
+- Leave exactly one new line after a heading.
+- Do not use links in headings.
+- Add the corresponding [product badge](#product-badges) according to the tier the feature belongs.
## Links
-- Use the regular inline link markdown markup `[Text](https://example.com)`.
- It's easier to read, review, and maintain.
-- If there's a link that repeats several times through the same document,
- you can use `[Text][identifier]` and at the bottom of the section or the
- document add: `[identifier]: https://example.com`, in which case, we do
- encourage you to also add an alternative text: `[identifier]: https://example.com "Alternative text"` that appears when hovering your mouse on a link.
+- Use inline link markdown markup `[Text](https://example.com)`.
+ It's easier to read, review, and maintain. **Do not** use `[Text][identifier]`.
- To link to internal documentation, use relative links, not full URLs. Use `../` to
navigate tp high-level directories, and always add the file name `file.md` at the
end of the link with the `.md` extension, not `.html`.
@@ -103,11 +203,12 @@ For punctuation rules, please refer to the [GitLab UX guide](https://design.gitl
To indicate the steps of navigation through the UI:
-- Use the exact word as shown in the UI, including any capital letters as-is
-- Use bold text for navigation items and the char `>` as separator
-(e.g., `Navigate to your project's **Settings > CI/CD**` )
+
+- Use the exact word as shown in the UI, including any capital letters as-is.
+- Use bold text for navigation items and the char "greater than" (`>`) as separator
+(e.g., `Navigate to your project's **Settings > CI/CD**` ).
- If there are any expandable menus, make sure to mention that the user
-needs to expand the tab to find the settings you're referring to
+needs to expand the tab to find the settings you're referring to (e.g., `Navigate to your project's **Settings > CI/CD** and expand **General pipelines**`).
## Images
@@ -115,17 +216,16 @@ needs to expand the tab to find the settings you're referring to
the `.md` document that you're working on is located. Always prepend their
names with the name of the document that they will be included in. For
example, if there is a document called `twitter.md`, then a valid image name
- could be `twitter_login_screen.png`. [**Exception**: images for
- [articles](index.md#technical-articles) should be
- put in a directory called `img` underneath `/articles/article_title/img/`, therefore,
- there's no need to prepend the document name to their filenames.]
-- Images should have a specific, non-generic name that will differentiate them.
+ could be `twitter_login_screen.png`.
+- Images should have a specific, non-generic name that will differentiate and describe them properly.
- Keep all file names in lower case.
- Consider using PNG images instead of JPEG.
- Compress all images with <https://tinypng.com/> or similar tool.
- Compress gifs with <https://ezgif.com/optimize> or similar tool.
- Images should be used (only when necessary) to _illustrate_ the description
of a process, not to _replace_ it.
+- Max image size: 100KB (gifs included).
+- The GitLab docs do not support videos yet.
Inside the document:
@@ -133,12 +233,38 @@ Inside the document:
`![Proper description what the image is about](img/document_image_title.png)`
- Always use a proper description for what the image is about. That way, when a
browser fails to show the image, this text will be used as an alternative
- description
+ description.
- If there are consecutive images with little text between them, always add
three dashes (`---`) between the image and the text to create a horizontal
- line for better clarity
+ line for better clarity.
- If a heading is placed right after an image, always add three dashes (`---`)
- between the image and the heading
+ between the image and the heading.
+
+## Code blocks
+
+- Always wrap code added to a sentence in inline code blocks (``` ` ```).
+E.g., `.gitlab-ci.yml`, `git add .`, `CODEOWNERS`, `only: master`.
+File names, commands, entries, and anything that refers to code should be added to code blocks.
+To make things easier for the user, always add a full code block for things that can be
+useful to copy and paste, as they can easily do it with the button on code blocks.
+- For regular code blocks, always use a highlighting class corresponding to the
+language for better readability. Examples:
+
+ ```md
+ ```ruby
+ Ruby code
+ ```
+
+ ```js
+ JavaScript code
+ ```
+
+ ```md
+ Markdown code
+ ```
+ ```
+
+- For a complete reference on code blocks, check the [Kramdown guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/#code-blocks).
## Alert boxes
@@ -146,7 +272,7 @@ Whenever you want to call the attention to a particular sentence,
use the following markup for highlighting.
_Note that the alert boxes only work for one paragraph only. Multiple paragraphs,
-lists, headers, etc will not render correctly._
+lists, headers, etc will not render correctly. For multiple lines, use blockquotes instead._
### Note
@@ -210,6 +336,31 @@ which renders in docs.gitlab.com to:
If the text spans across multiple lines it's OK to split the line.
+For multiple paragraphs, use the symbol `>` before every line:
+
+```md
+> This is the first paragraph.
+>
+> This is the second paragraph.
+>
+> - This is a list item
+> - Second item in the list
+>
+> ### This is an `h3`
+```
+
+Which renders to:
+
+> This is the first paragraph.
+>
+> This is the second paragraph.
+>
+> - This is a list item
+> - Second item in the list
+>
+> ### This is an `h3`
+>{:.no_toc}
+
## Specific sections and terms
To mention and/or reference specific terms in GitLab, please follow the styles
@@ -229,7 +380,7 @@ below.
(in that order) that introduced it. The above quote would be then transformed to:
```md
- > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/1242) in GitLab 8.3.
+ > [Introduced](<link-to-issue>) in GitLab 8.3.
```
- If the feature is only available in GitLab Enterprise Edition, don't forget to mention
@@ -237,35 +388,47 @@ below.
the feature is available in:
```md
- > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/1242)
- in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
+ > [Introduced](<link-to-issue>) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
```
+#### Early versions of EE
+
+If the feature was created before GitLab 9.2 (before [different EE tiers were introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1851)):
+
+- Declare it as "Introduced in GitLab Enterprise Edition X.Y".
+- Note which tier the feature is available in.
+
+For example:
+
+```md
+> [Introduced](<link-to-issue>) in GitLab Enterprise Edition 9.0. Available in [GitLab Premium](https://about.gitlab.com/pricing/).
+```
+
### Product badges
When a feature is available in EE-only tiers, add the corresponding tier according to the
feature availability:
-- For GitLab Starter and GitLab.com Bronze: `**[STARTER]**`
-- For GitLab Premium and GitLab.com Silver: `**[PREMIUM]**`
-- For GitLab Ultimate and GitLab.com Gold: `**[ULTIMATE]**`
-- For GitLab Core and GitLab.com Free: `**[CORE]**`
+- For GitLab Starter and GitLab.com Bronze: `**[STARTER]**`.
+- For GitLab Premium and GitLab.com Silver: `**[PREMIUM]**`.
+- For GitLab Ultimate and GitLab.com Gold: `**[ULTIMATE]**`.
+- For GitLab Core and GitLab.com Free: `**[CORE]**`.
To exclude GitLab.com tiers (when the feature is not available in GitLab.com), add the
keyword "only":
-- For GitLab Starter: `**[STARTER ONLY]**`
-- For GitLab Premium: `**[PREMIUM ONLY]**`
-- For GitLab Ultimate: `**[ULTIMATE ONLY]**`
-- For GitLab Core: `**[CORE ONLY]**`
+- For GitLab Core: `**[CORE ONLY]**`.
+- For GitLab Starter: `**[STARTER ONLY]**`.
+- For GitLab Premium: `**[PREMIUM ONLY]**`.
+- For GitLab Ultimate: `**[ULTIMATE ONLY]**`.
The tier should be ideally added to headers, so that the full badge will be displayed.
-But it can be also mentioned from paragraphs, list items, and table cells. For these cases,
-the tier mention will be represented by an orange question mark.
+However, it can be also mentioned from paragraphs, list items, and table cells. For these cases,
+the tier mention will be represented by an orange question mark that will show the tiers on hover.
E.g., `**[STARTER]**` renders **[STARTER]**, `**[STARTER ONLY]**` renders **[STARTER ONLY]**.
The absence of tiers' mentions mean that the feature is available in GitLab Core,
-GitLab.com Free, and higher tiers.
+GitLab.com Free, and all higher tiers.
#### How it works
@@ -281,10 +444,10 @@ avoid duplication, link to the special document that can be found in
[`doc/administration/restart_gitlab.md`][doc-restart]. Usually the text will
read like:
- ```
- Save the file and [reconfigure GitLab](../../administration/restart_gitlab.md)
- for the changes to take effect.
- ```
+```md
+Save the file and [reconfigure GitLab](../../administration/restart_gitlab.md)
+for the changes to take effect.
+```
If the document you are editing resides in a place other than the GitLab CE/EE
`doc/` directory, instead of the relative link, use the full path:
@@ -312,8 +475,8 @@ prefer to document it in the CE docs to avoid duplication.
Configuration settings include:
-- settings that touch configuration files in `config/`
-- NGINX settings and settings in `lib/support/` in general
+1. Settings that touch configuration files in `config/`.
+1. NGINX settings and settings in `lib/support/` in general.
When there is a list of steps to perform, usually that entails editing the
configuration file and reconfiguring/restarting GitLab. In such case, follow
@@ -350,13 +513,13 @@ the style below as a guide:
In this case:
-- before each step list the installation method is declared in bold
-- three dashes (`---`) are used to create a horizontal line and separate the
+- Before each step list the installation method is declared in bold
+- Three dashes (`---`) are used to create a horizontal line and separate the
two methods
-- the code blocks are indented one or more spaces under the list item to render
+- The code blocks are indented one or more spaces under the list item to render
correctly
-- different highlighting languages are used for each config in the code block
-- the [references](#references) guide is used for reconfigure/restart
+- Different highlighting languages are used for each config in the code block
+- The [references](#references) guide is used for reconfigure/restart
### Fake tokens
@@ -367,19 +530,19 @@ low.
You can use the following fake tokens as examples.
-| **Token type** | **Token value** |
-| --------------------- | --------------------------------- |
-| Private user token | `9koXpg98eAheJpvBs5tK` |
-| Personal access token | `n671WNGecHugsdEDPsyo` |
+| **Token type** | **Token value** |
+|:----------------------|:-------------------------------------------------------------------|
+| Private user token | `9koXpg98eAheJpvBs5tK` |
+| Personal access token | `n671WNGecHugsdEDPsyo` |
| Application ID | `2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6` |
| Application secret | `04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df` |
-| Secret CI variable | `Li8j-mLUVA3eZYjPfd_H` |
-| Specific Runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
-| Shared Runner token | `6Vk7ZsosqQyfreAxXTZr` |
-| Trigger token | `be20d8dcc028677c931e04f3871a9b` |
-| Webhook secret token | `6XhDroRcYPM5by_h-HLY` |
-| Health check token | `Tu7BgjR9qeZTEyRzGG2P` |
-| Request profile token | `7VgpS4Ax5utVD2esNstz` |
+| CI/CD variable | `Li8j-mLUVA3eZYjPfd_H` |
+| Specific Runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
+| Shared Runner token | `6Vk7ZsosqQyfreAxXTZr` |
+| Trigger token | `be20d8dcc028677c931e04f3871a9b` |
+| Webhook secret token | `6XhDroRcYPM5by_h-HLY` |
+| Health check token | `Tu7BgjR9qeZTEyRzGG2P` |
+| Request profile token | `7VgpS4Ax5utVD2esNstz` |
### API
@@ -402,16 +565,16 @@ on this document. Further explanation is given below.
Use the following table headers to describe the methods. Attributes should
always be in code blocks using backticks (``` ` ```).
-```
+```md
| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
+|:----------|:-----|:---------|:------------|
```
Rendered example:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `user` | string | yes | The GitLab username |
+| Attribute | Type | Required | Description |
+|:----------|:-------|:---------|:--------------------|
+| `user` | string | yes | The GitLab username |
#### cURL commands
@@ -423,12 +586,12 @@ Rendered example:
- Prefer to use examples using the personal access token and don't pass data of
username and password.
-| Methods | Description |
-| ------- | ----------- |
+| Methods | Description |
+|:-------------------------------------------|:------------------------------------------------------|
| `-H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK"` | Use this method as is, whenever authentication needed |
-| `-X POST` | Use this method when creating new objects |
-| `-X PUT` | Use this method when updating existing objects |
-| `-X DELETE` | Use this method when removing existing objects |
+| `-X POST` | Use this method when creating new objects |
+| `-X PUT` | Use this method when updating existing objects |
+| `-X DELETE` | Use this method when removing existing objects |
#### cURL Examples
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
new file mode 100644
index 00000000000..75ce8640e87
--- /dev/null
+++ b/doc/development/documentation/workflow.md
@@ -0,0 +1,185 @@
+---
+description: Learn the process of shipping documentation for GitLab.
+---
+
+# Documentation process at GitLab
+
+At GitLab, developers contribute new or updated documentation along with their code, but product managers and technical writers also have essential roles in the process.
+
+- Product Managers (PMs): in the issue for all new and updated features,
+ PMs include specific documentation requirements that the developer who is
+ writing or updating the docs must meet, along with feature descriptions
+ and use cases. They call out any specific areas where collaborating with
+ a technical writer is recommended, and usually act as the first reviewer
+ of the docs.
+- Developers: author documentation and merge it on time (up to a week after
+ the feature freeze).
+- Technical Writers: review each issue to ensure PM's requirements are complete,
+ help developers with any questions throughout the process, and act as the final
+ reviewer of all new and updated docs content before it's merged.
+
+## Requirements
+
+Documentation must be delivered whenever:
+
+- A new feature is shipped
+- There are changes to the UI
+- A process, workflow, or previously documented feature is changed
+
+Documentation is not required when a feature is changed on the backend
+only and does not directly affect the way that any regular user or
+administrator would interact with GitLab.
+
+NOTE: **Note:**
+When refactoring documentation, it should be submitted in its own MR.
+**Do not** join new features' MRs with refactoring existing docs, as they might have
+different priorities.
+
+NOTE: **Note:**
+[Smaller MRs are better](https://gitlab.com/gitlab-com/blog-posts/issues/185#note_4401010)! Do not mix subjects, and ship the smallest MR possible.
+
+### Documentation review process
+
+The docs shipped by the developer should be reviewed by the PM (for accuracy) and a Technical Writer (for clarity and structure).
+
+#### Documentation updates that require Technical Writer review
+
+Every documentation change that meets the criteria below must be reviewed by a Technical Writer
+to ensure clarity and discoverability, and avoid redundancy, bad file locations, typos, broken links, etc.
+Within the GitLab issue or MR, ping the relevant technical writer for the subject area. If you're not sure who that is,
+ping any of them or all of them (`@gl\-docsteam`).
+
+A Technical Writer must review documentation updates that involve:
+
+- Docs introducing new features
+- Changing documentation location
+- Refactoring existing documentation
+- Creating new documentation files
+
+If you need any help to choose the correct place for a doc, discuss a documentation
+idea or outline, or request any other help, ping a Technical Writer on your issue, MR,
+or on Slack in `#docs`.
+
+#### Skip the PM's review
+
+When there's a non-significant change to the docs, you can skip the review
+of the PM. Add the same labels as you would for a regular doc change and
+assign the correct milestone. In these cases, assign a Technical Writer
+for approval/merge, or mention `@gl\-docsteam` in case you don't know
+which Tech Writer to assign for.
+
+#### Skip the entire review
+
+When the MR only contains corrections to the content (typos, grammar,
+broken links, etc), it can be merged without the PM's and Tech Writer's review.
+
+## Documentation structure
+
+Read through the [documentation structure](structure.md) docs for an overview.
+
+## Documentation workflow
+
+To follow a consistent workflow every month, documentation changes
+involve the Product Managers, the developer who shipped the feature,
+and the Technical Writing team. Each role is described below.
+
+### 1. Product Manager's role in the documentation process
+
+The Product Manager (PM) should add to the feature issue:
+
+- Feature name, overview/description, and use cases, for the [documentation blurb](structure.md#documentation-blurb)
+- The documentation requirements for the developer working on the docs
+ - What new page, new subsection of an existing page, or other update to an existing page/subsection is needed.
+ - Just one page/section/update or multiple (perhaps there's an end user and admin change needing docs, or we need to update a previously recommended workflow, or we want to link the new feature from various places; consider and mention all ways documentation should be affected
+ - Suggested title of any page or subsection, if applicable
+- Label the issue with `Documentation`, `Deliverable`, `docs:P1`, and assign
+ the correct milestone
+
+### 2. Developer's role in the documentation process
+
+As a developer, or as a community contributor, you should ship the documentation
+with the feature, as in GitLab the documentation is part of the product.
+
+The docs can either be shipped along with the MR introducing the code, or,
+alternatively, created from a follow-up issue and MR.
+
+The docs should be shipped **by the feature freeze date**. Justified
+exceptions are accepted, as long as the [following process](#documentation-shipped-late)
+and the missed-deliverable due date (the 14th of each month) are both respected.
+
+#### Documentation shipped in the feature MR
+
+The developer should add to the feature MR the documentation containing:
+
+- The [documentation blurb](structure.md#documentation-blurb): copy the
+ feature name, overview/description, and use cases from the feature issue
+- Instructions: write how to use the feature, step by step, with no gaps.
+- [Crosslink for discoverability](structure.md#discoverability): link with
+ internal docs and external resources (if applicable)
+- Index: link the new doc or the new heading from the higher-level index
+ for [discoverability](#discoverability)
+- [Screenshots](styleguide.md#images): when necessary, add screenshots for:
+ - Illustrating a step of the process
+ - Indicating the location of a navigation menu
+- Label the MR with `Documentation`, `Deliverable`, `docs-P1`, and assign
+ the correct milestone
+- Assign the PM for review
+- When done, mention the `@gl\-docsteam` in the MR asking for review
+- **Due date**: feature freeze date and time
+
+#### Documentation shipped in a follow-up MR
+
+If the docs aren't being shipped within the feature MR:
+
+- Create a new issue mentioning "docs" or "documentation" in the title (use the Documentation issue description template)
+- Label the issue with: `Documentation`, `Deliverable`, `docs-P1`, `<product-label>`
+ (product label == CI/CD, Pages, Prometheus, etc)
+- Add the correct milestone
+- Create a new MR for shipping the docs changes and follow the same
+ process [described above](#documentation-shipped-in-the-feature-mr)
+- Use the MR description template called "Documentation"
+- Add the same labels and milestone as you did for the issue
+- Assign the PM for review
+- When done, mention the `@gl\-docsteam` in the MR asking for review
+- **Due date**: feature freeze date and time
+
+#### Documentation shipped late
+
+Shipping late means that you are affecting the whole feature workflow
+as well as other teams' priorities (PMs, tech writers, release managers,
+release post reviewers), so every effort should be made to avoid this.
+
+If you did not ship the docs within the feature freeze, proceed as
+[described above](#documentation-shipped-in-a-follow-up-mr) and,
+besides the regular labels, include the labels `Pick into X.Y` and
+`missed-deliverable` in the issue and the MR, and assign them the correct
+milestone.
+
+The **due date** for **merging** `missed-deliverable` MRs is on the
+**14th** of each month.
+
+### 3. Technical Writer's role in the documentation process
+
+- **Planning**
+ - Once an issue contains a Documentation label and the current milestone, a
+ technical writer reviews the Product Manager's documentation requirements.
+ - Once the documentation requirements are approved, the technical writer can
+ work with the developer to discuss any documentation questions and plans/outlines, as needed.
+
+- **Review** - A technical writer must review the documentation for:
+ - Clarity
+ - Relevance (make sure the content is appropriate given the impact of the feature)
+ - Location (make sure the doc is in the correct dir and has the correct name)
+ - Syntax, typos, and broken links
+ - Improvements to the content
+ - Accordance to the [docs style guide](styleguide.md)
+
+<!-- TBA: issue and MR description templates as part of the process -->
+
+<!--
+## New features vs feature updates
+
+- TBA:
+ - Describe the difference between new features and feature updates
+ - Creating a new doc vs updating an existing doc
+-->
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 32de741c9fe..9aea03139ee 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -119,10 +119,20 @@ This also applies to views.
### EE features based on CE features
-For features that build on existing CE features, write a module in the
-`EE` namespace and `prepend` it in the CE class. This makes conflicts
-less likely to happen during CE to EE merges because only one line is
-added to the CE class - the `prepend` line.
+For features that build on existing CE features, write a module in the `EE`
+namespace and `prepend` it in the CE class, on the last line of the file that
+the class resides in. This makes conflicts less likely to happen during CE to EE
+merges because only one line is added to the CE class - the `prepend` line. For
+example, to prepend a module into the `User` class you would use the following
+approach:
+
+```ruby
+class User < ActiveRecord::Base
+ # ... lots of code here ...
+end
+
+User.prepend(EE::User)
+```
Since the module would require an `EE` namespace, the file should also be
put in an `ee/` sub-directory. For example, we want to extend the user model
@@ -166,47 +176,53 @@ There are a few gotchas with it:
to make it call the other method we want to extend, like a [template method
pattern](https://en.wikipedia.org/wiki/Template_method_pattern).
For example, given this base:
- ``` ruby
- class Base
- def execute
- return unless enabled?
- # ...
- # ...
- end
- end
- ```
- Instead of just overriding `Base#execute`, we should update it and extract
- the behaviour into another method:
- ``` ruby
- class Base
- def execute
- return unless enabled?
-
- do_something
+ ```ruby
+ class Base
+ def execute
+ return unless enabled?
+
+ # ...
+ # ...
+ end
end
+ ```
- private
+ Instead of just overriding `Base#execute`, we should update it and extract
+ the behaviour into another method:
+
+ ```ruby
+ class Base
+ def execute
+ return unless enabled?
+
+ do_something
+ end
- def do_something
- # ...
- # ...
+ private
+
+ def do_something
+ # ...
+ # ...
+ end
end
- end
- ```
- Then we're free to override that `do_something` without worrying about the
- guards:
- ``` ruby
- module EE::Base
- extend ::Gitlab::Utils::Override
+ ```
+
+ Then we're free to override that `do_something` without worrying about the
+ guards:
- override :do_something
- def do_something
- # Follow the above pattern to call super and extend it
+ ```ruby
+ module EE::Base
+ extend ::Gitlab::Utils::Override
+
+ override :do_something
+ def do_something
+ # Follow the above pattern to call super and extend it
+ end
end
- end
- ```
- This would require updating CE first, or make sure this is back ported to CE.
+ ```
+
+ This would require updating CE first, or make sure this is back ported to CE.
When prepending, place them in the `ee/` specific sub-directory, and
wrap class or module in `module EE` to avoid naming conflicts.
@@ -225,7 +241,6 @@ the existing file:
```ruby
class ApplicationController < ActionController::Base
- prepend EE::ApplicationController
# ...
def after_sign_out_path_for(resource)
@@ -234,6 +249,8 @@ class ApplicationController < ActionController::Base
# ...
end
+
+ApplicationController.prepend(EE::ApplicationController)
```
And create a new file in the `ee/` sub-directory with the altered
@@ -258,6 +275,31 @@ end
[`extend ::Gitlab::Utils::Override`]: utilities.md#override
+##### Overriding CE class methods
+
+The same applies to class methods, except we want to use
+`ActiveSupport::Concern` and put `extend ::Gitlab::Utils::Override`
+within the block of `class_methods`. Here's an example:
+
+```ruby
+module EE
+ module Groups
+ module GroupMembersController
+ extend ActiveSupport::Concern
+
+ class_methods do
+ extend ::Gitlab::Utils::Override
+
+ override :admin_not_required_endpoints
+ def admin_not_required_endpoints
+ super.concat(%i[update override])
+ end
+ end
+ end
+ end
+end
+```
+
#### Use self-descriptive wrapper methods
When it's not possible/logical to modify the implementation of a
@@ -301,6 +343,21 @@ full implementation details.
[ce-mr-full-private]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12373
[ee-mr-full-private]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2199
+### Code in `config/routes`
+
+When we add `draw :admin` in `config/routes.rb`, the application will try to
+load the file located in `config/routes/admin.rb`, and also try to load the
+file located in `ee/config/routes/admin.rb`.
+
+In EE, it should at least load one file, at most two files. If it cannot find
+any files, an error will be raised. In CE, since we don't know if there will
+be an EE route, it will not raise any errors even if it cannot find anything.
+
+This means if we want to extend a particular CE route file, just add the same
+file located in `ee/config/routes`. If we want to add an EE only route, we
+could still put `draw :ee_only` in both CE and EE, and add
+`ee/config/routes/ee_only.rb` in EE, similar to `render_if_exists`.
+
### Code in `app/controllers/`
In controllers, the most common type of conflict is with `before_action` that
@@ -444,7 +501,7 @@ Put the EE module files following
For EE API routes, we put them in a `prepended` block:
-``` ruby
+```ruby
module EE
module API
module MergeRequests
@@ -478,7 +535,7 @@ interface first here.
For example, suppose we have a few more optional params for EE, given this CE
API code:
-``` ruby
+```ruby
module API
class MergeRequests < Grape::API
# EE::API::MergeRequests would override the following helpers
@@ -487,8 +544,6 @@ module API
end
end
- prepend EE::API::MergeRequests
-
params :optional_params do
# CE specific params go here...
@@ -496,11 +551,13 @@ module API
end
end
end
+
+API::MergeRequests.prepend(EE::API::MergeRequests)
```
And then we could override it in EE module:
-``` ruby
+```ruby
module EE
module API
module MergeRequests
@@ -527,7 +584,7 @@ To make it easy for an EE module to override the CE helpers, we need to define
those helpers we want to extend first. Try to do that immediately after the
class definition to make it easy and clear:
-``` ruby
+```ruby
module API
class JobArtifacts < Grape::API
# EE::API::JobArtifacts would override the following helpers
@@ -536,15 +593,15 @@ module API
authorize_read_builds!
end
end
-
- prepend EE::API::JobArtifacts
end
end
+
+API::JobArtifacts.prepend(EE::API::JobArtifacts)
```
And then we can follow regular object-oriented practices to override it:
-``` ruby
+```ruby
module EE
module API
module JobArtifacts
@@ -571,7 +628,7 @@ therefore can't be simply overridden. We need to extract them into a standalone
method, or introduce some "hooks" where we could inject behavior in the CE
route. Something like this:
-``` ruby
+```ruby
module API
class MergeRequests < Grape::API
helpers do
@@ -580,8 +637,6 @@ module API
end
end
- prepend EE::API::MergeRequests
-
put ':id/merge_requests/:merge_request_iid/merge' do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -593,12 +648,14 @@ module API
end
end
end
+
+API::MergeRequests.prepend(EE::API::MergeRequests)
```
Note that `update_merge_request_ee` doesn't do anything in CE, but
then we could override it in EE:
-``` ruby
+```ruby
module EE
module API
module MergeRequests
@@ -630,27 +687,37 @@ or not we really need to extend it from EE. For now we're not using it much.
Sometimes we need to use different arguments for a particular API route, and we
can't easily extend it with an EE module because Grape has different context in
-different blocks. In order to overcome this, we could use class methods from the
-API class.
+different blocks. In order to overcome this, we need to move the data to a class
+method that resides in a separate module or class. This allows us to extend that
+module or class before its data is used, without having to place a `prepend` in
+the middle of CE code.
For example, in one place we need to pass an extra argument to
`at_least_one_of` so that the API could consider an EE-only argument as the
-least argument. This is not quite beautiful but it's working:
+least argument. We would approach this as follows:
-``` ruby
+```ruby
+# api/merge_requests/parameters.rb
module API
class MergeRequests < Grape::API
- def self.update_params_at_least_one_of
- %i[
- assignee_id
- description
- ]
+ module Parameters
+ def self.update_params_at_least_one_of
+ %i[
+ assignee_id
+ description
+ ]
+ end
end
+ end
+end
- prepend EE::API::MergeRequests
+API::MergeRequests::Parameters.prepend(EE::API::MergeRequests::Parameters)
+# api/merge_requests.rb
+module API
+ class MergeRequests < Grape::API
params do
- at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of)
+ at_least_one_of(*Parameters.update_params_at_least_one_of)
end
end
end
@@ -658,17 +725,22 @@ end
And then we could easily extend that argument in the EE class method:
-``` ruby
+```ruby
module EE
module API
module MergeRequests
- extend ActiveSupport::Concern
+ module Parameters
+ extend ActiveSupport::Concern
- class_methods do
- def update_params_at_least_one_of
- super.push(*%i[
- squash
- ])
+ class_methods do
+ extend ::Gitlab::Utils::Override
+
+ override :update_params_at_least_one_of
+ def update_params_at_least_one_of
+ super.push(*%i[
+ squash
+ ])
+ end
end
end
end
@@ -679,6 +751,78 @@ end
It could be annoying if we need this for a lot of routes, but it might be the
simplest solution right now.
+This approach can also be used when models define validations that depend on
+class methods. For example:
+
+```ruby
+# app/models/identity.rb
+class Identity < ActiveRecord::Base
+ def self.uniqueness_scope
+ [:provider]
+ end
+
+ prepend EE::Identity
+
+ validates :extern_uid,
+ allow_blank: true,
+ uniqueness: { scope: uniqueness_scope, case_sensitive: false }
+end
+
+# ee/app/models/ee/identity.rb
+module EE
+ module Identity
+ extend ActiveSupport::Concern
+
+ class_methods do
+ extend ::Gitlab::Utils::Override
+
+ def uniqueness_scope
+ [*super, :saml_provider_id]
+ end
+ end
+ end
+end
+```
+
+Instead of taking this approach, we would refactor our code into the following:
+
+```ruby
+# ee/app/models/ee/identity/uniqueness_scopes.rb
+module EE
+ module Identity
+ module UniquenessScopes
+ extend ActiveSupport::Concern
+
+ class_methods do
+ extend ::Gitlab::Utils::Override
+
+ def uniqueness_scope
+ [*super, :saml_provider_id]
+ end
+ end
+ end
+ end
+end
+
+# app/models/identity/uniqueness_scopes.rb
+class Identity < ActiveRecord::Base
+ module UniquenessScopes
+ def self.uniqueness_scope
+ [:provider]
+ end
+ end
+end
+
+Identity::UniquenessScopes.prepend(EE::Identity::UniquenessScopes)
+
+# app/models/identity.rb
+class Identity < ActiveRecord::Base
+ validates :extern_uid,
+ allow_blank: true,
+ uniqueness: { scope: Identity::UniquenessScopes.scopes, case_sensitive: false }
+end
+```
+
### Code in `spec/`
When you're testing EE-only features, avoid adding examples to the
diff --git a/doc/development/fe_guide/components.md b/doc/development/fe_guide/components.md
index 66a8abe42f7..ee0c2d534ff 100644
--- a/doc/development/fe_guide/components.md
+++ b/doc/development/fe_guide/components.md
@@ -42,7 +42,7 @@ See also the [corresponding UX guide](../ux_guide/components.md#dropdowns).
See also the [corresponding UX guide](../ux_guide/components.md#modals).
-We have a reusable Vue component for modals: [vue_shared/components/gl-modal.vue](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/vue_shared/components/gl-modal.vue)
+We have a reusable Vue component for modals: [vue_shared/components/gl_modal.vue](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/vue_shared/components/gl_modal.vue)
Here is an example of how to use it:
diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md
index 3d8da6accc1..533e2001300 100644
--- a/doc/development/fe_guide/icons.md
+++ b/doc/development/fe_guide/icons.md
@@ -3,7 +3,7 @@
We manage our own Icon and Illustration library in the [gitlab-svgs][gitlab-svgs] repository.
This repository is published on [npm][npm] and managed as a dependency via yarn.
You can browse all available Icons and Illustrations [here][svg-preview].
-To upgrade to a new version run `yarn upgrade @gitlab-org/gitlab-svgs`.
+To upgrade to a new version run `yarn upgrade @gitlab/svgs`.
## Icons
@@ -111,6 +111,6 @@ export default {
</template>
```
-[npm]: https://www.npmjs.com/package/@gitlab-org/gitlab-svgs
+[npm]: https://www.npmjs.com/package/@gitlab/svgs
[gitlab-svgs]: https://gitlab.com/gitlab-org/gitlab-svgs
[svg-preview]: https://gitlab-org.gitlab.io/gitlab-svgs
diff --git a/doc/development/fe_guide/img/boards_diagram.png b/doc/development/fe_guide/img/boards_diagram.png
index 7a2cf972fd0..856c9b05bbf 100644
--- a/doc/development/fe_guide/img/boards_diagram.png
+++ b/doc/development/fe_guide/img/boards_diagram.png
Binary files differ
diff --git a/doc/development/fe_guide/img/gl-modal.png b/doc/development/fe_guide/img/gl-modal.png
index 47302e857bc..b2d2d637e57 100644
--- a/doc/development/fe_guide/img/gl-modal.png
+++ b/doc/development/fe_guide/img/gl-modal.png
Binary files differ
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index da836a0e82e..ef0eed786d2 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -74,7 +74,7 @@ bundle and included on the page.
> `content_for :page_specific_javascripts` within haml files, along with
> manually generated webpack bundles. However under this new system you should
> not ever need to manually add an entry point to the `webpack.config.js` file.
-
+>
> **Tip:**
> If you are unsure what controller and action corresponds to a given page, you
> can find this out by inspecting `document.body.dataset.page` within your
@@ -109,7 +109,6 @@ bundle and included on the page.
`my_widget.js` is only imported within `pages/widget/show/index.js`, you
should place the module at `pages/widget/show/my_widget.js` and import it
with a relative path (e.g. `import initMyWidget from './my_widget';`).
-
- If a class or module is _used by multiple routes_, place it within a
shared directory at the closest common parent directory for the entry
points that import it. For example, if `my_widget.js` is imported within
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md
index 284b4b53334..1e0529262ad 100644
--- a/doc/development/fe_guide/style_guide_js.md
+++ b/doc/development/fe_guide/style_guide_js.md
@@ -1,11 +1,13 @@
# Style guides and linting
+
See the relevant style guides for our guidelines and for information on linting:
## JavaScript
+
We defer to [Airbnb][airbnb-js-style-guide] on most style-related
conventions and enforce them with eslint.
-See [our current .eslintrc][eslintrc] for specific rules and patterns.
+See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.eslintrc.yml) for specific rules and patterns.
### Common
@@ -17,74 +19,81 @@ at the top, but legacy files are a special case. Any time you develop a new fea
refactor an existing one, you should abide by the eslint rules.
1. **Never Ever EVER** disable eslint globally for a file
- ```javascript
- // bad
- /* eslint-disable */
- // better
- /* eslint-disable some-rule, some-other-rule */
+ ```javascript
+ // bad
+ /* eslint-disable */
+
+ // better
+ /* eslint-disable some-rule, some-other-rule */
- // best
- // nothing :)
- ```
+ // best
+ // nothing :)
+ ```
1. If you do need to disable a rule for a single violation, try to do it as locally as possible
- ```javascript
- // bad
- /* eslint-disable no-new */
- import Foo from 'foo';
+ ```javascript
+ // bad
+ /* eslint-disable no-new */
- new Foo();
+ import Foo from 'foo';
- // better
- import Foo from 'foo';
+ new Foo();
+
+ // better
+ import Foo from 'foo';
+
+ // eslint-disable-next-line no-new
+ new Foo();
+ ```
- // eslint-disable-next-line no-new
- new Foo();
- ```
1. There are few rules that we need to disable due to technical debt. Which are:
- 1. [no-new][eslint-new]
- 1. [class-methods-use-this][eslint-this]
+ 1. [no-new][eslint-new]
+ 1. [class-methods-use-this][eslint-this]
1. When they are needed _always_ place ESlint directive comment blocks on the first line of a script,
followed by any global declarations, then a blank newline prior to any imports or code.
- ```javascript
- // bad
- /* global Foo */
- /* eslint-disable no-new */
- import Bar from './bar';
- // good
- /* eslint-disable no-new */
- /* global Foo */
+ ```javascript
+ // bad
+ /* global Foo */
+ /* eslint-disable no-new */
+ import Bar from './bar';
- import Bar from './bar';
- ```
+ // good
+ /* eslint-disable no-new */
+ /* global Foo */
+
+ import Bar from './bar';
+ ```
1. **Never** disable the `no-undef` rule. Declare globals with `/* global Foo */` instead.
1. When declaring multiple globals, always use one `/* global [name] */` line per variable.
- ```javascript
- // bad
- /* globals Flash, Cookies, jQuery */
- // good
- /* global Flash */
- /* global Cookies */
- /* global jQuery */
- ```
+ ```javascript
+ // bad
+ /* globals Flash, Cookies, jQuery */
+
+ // good
+ /* global Flash */
+ /* global Cookies */
+ /* global jQuery */
+ ```
1. Use up to 3 parameters for a function or class. If you need more accept an Object instead.
- ```javascript
- // bad
- fn(p1, p2, p3, p4) {}
- // good
- fn(options) {}
- ```
+ ```javascript
+ // bad
+ fn(p1, p2, p3, p4) {}
+
+ // good
+ fn(options) {}
+ ```
#### Modules, Imports, and Exports
+
1. Use ES module syntax to import modules
```javascript
// bad
@@ -178,8 +187,10 @@ Do not use them anymore and feel free to remove them when refactoring legacy cod
```
#### Data Mutation and Pure functions
+
1. Strive to write many small pure functions, and minimize where mutations occur.
- ```javascript
+
+ ```javascript
// bad
const values = {foo: 1};
@@ -205,284 +216,306 @@ Do not use them anymore and feel free to remove them when refactoring legacy cod
}
var c = pureFunction(values.foo);
- ```
+ ```
1. Avoid constructors with side-effects.
-Although we aim for code without side-effects we need some side-effects for our code to run.
-
-If the class won't do anything if we only instantiate it, it's ok to add side effects into the constructor (_Note:_ The following is just an example. If the only purpose of the class is to add an event listener and handle the callback a function will be more suitable.)
-
-```javascript
-// Bad
-export class Foo {
- constructor() {
- this.init();
- }
- init() {
- document.addEventListener('click', this.handleCallback)
- },
- handleCallback() {
-
- }
-}
-
-// Good
-export class Foo {
- constructor() {
- document.addEventListener()
- }
- handleCallback() {
- }
-}
-```
-
-On the other hand, if a class only needs to extend a third party/add event listeners in some specific cases, they should be initialized outside of the constructor.
+ Although we aim for code without side-effects we need some side-effects for our code to run.
+
+ If the class won't do anything if we only instantiate it, it's ok to add side effects into the constructor (_Note:_ The following is just an example. If the only purpose of the class is to add an event listener and handle the callback a function will be more suitable.)
+
+ ```javascript
+ // Bad
+ export class Foo {
+ constructor() {
+ this.init();
+ }
+ init() {
+ document.addEventListener('click', this.handleCallback)
+ },
+ handleCallback() {
+
+ }
+ }
+
+ // Good
+ export class Foo {
+ constructor() {
+ document.addEventListener()
+ }
+ handleCallback() {
+ }
+ }
+ ```
+
+ On the other hand, if a class only needs to extend a third party/add event listeners in some specific cases, they should be initialized outside of the constructor.
1. Prefer `.map`, `.reduce` or `.filter` over `.forEach`
-A forEach will most likely cause side effects, it will be mutating the array being iterated. Prefer using `.map`,
-`.reduce` or `.filter`
- ```javascript
- const users = [ { name: 'Foo' }, { name: 'Bar' } ];
+ A forEach will most likely cause side effects, it will be mutating the array being iterated. Prefer using `.map`,
+ `.reduce` or `.filter`
- // bad
- users.forEach((user, index) => {
- user.id = index;
- });
+ ```javascript
+ const users = [ { name: 'Foo' }, { name: 'Bar' } ];
- // good
- const usersWithId = users.map((user, index) => {
- return Object.assign({}, user, { id: index });
- });
- ```
+ // bad
+ users.forEach((user, index) => {
+ user.id = index;
+ });
+
+ // good
+ const usersWithId = users.map((user, index) => {
+ return Object.assign({}, user, { id: index });
+ });
+ ```
#### Parse Strings into Numbers
+
1. `parseInt()` is preferable over `Number()` or `+`
- ```javascript
- // bad
- +'10' // 10
- // good
- Number('10') // 10
+ ```javascript
+ // bad
+ +'10' // 10
- // better
- parseInt('10', 10);
- ```
+ // good
+ Number('10') // 10
+
+ // better
+ parseInt('10', 10);
+ ```
#### CSS classes used for JavaScript
+
1. If the class is being used in Javascript it needs to be prepend with `js-`
- ```html
- // bad
- <button class="add-user">
- Add User
- </button>
- // good
- <button class="js-add-user">
- Add User
- </button>
- ```
+ ```html
+ // bad
+ <button class="add-user">
+ Add User
+ </button>
+
+ // good
+ <button class="js-add-user">
+ Add User
+ </button>
+ ```
### Vue.js
#### `eslint-vue-plugin`
+
We default to [eslint-vue-plugin][eslint-plugin-vue], with the `plugin:vue/recommended`.
Please check this [rules][eslint-plugin-vue-rules] for more documentation.
#### Basic Rules
+
1. The service has it's own file
1. The store has it's own file
1. Use a function in the bundle file to instantiate the Vue component:
- ```javascript
- // bad
- class {
- init() {
- new Component({})
+
+ ```javascript
+ // bad
+ class {
+ init() {
+ new Component({})
+ }
}
- }
- // good
- document.addEventListener('DOMContentLoaded', () => new Vue({
- el: '#element',
- components: {
- componentName
- },
- render: createElement => createElement('component-name'),
- }));
- ```
+ // good
+ document.addEventListener('DOMContentLoaded', () => new Vue({
+ el: '#element',
+ components: {
+ componentName
+ },
+ render: createElement => createElement('component-name'),
+ }));
+ ```
1. Do not use a singleton for the service or the store
- ```javascript
- // bad
- class Store {
- constructor() {
- if (!this.prototype.singleton) {
- // do something
+
+ ```javascript
+ // bad
+ class Store {
+ constructor() {
+ if (!this.prototype.singleton) {
+ // do something
+ }
}
}
- }
- // good
- class Store {
- constructor() {
- // do something
+ // good
+ class Store {
+ constructor() {
+ // do something
+ }
}
- }
- ```
+ ```
1. Use `.vue` for Vue templates. Do not use `%template` in HAML.
#### Naming
1. **Extensions**: Use `.vue` extension for Vue components. Do not use `.js` as file extension ([#34371]).
1. **Reference Naming**: Use PascalCase for their instances:
- ```javascript
- // bad
- import cardBoard from 'cardBoard.vue'
- components: {
- cardBoard,
- };
+ ```javascript
+ // bad
+ import cardBoard from 'cardBoard.vue'
- // good
- import CardBoard from 'cardBoard.vue'
+ components: {
+ cardBoard,
+ };
- components: {
- CardBoard,
- };
- ```
+ // good
+ import CardBoard from 'cardBoard.vue'
+
+ components: {
+ CardBoard,
+ };
+ ```
1. **Props Naming:** Avoid using DOM component prop names.
1. **Props Naming:** Use kebab-case instead of camelCase to provide props in templates.
- ```javascript
- // bad
- <component class="btn">
- // good
- <component css-class="btn">
+ ```javascript
+ // bad
+ <component class="btn">
- // bad
- <component myProp="prop" />
+ // good
+ <component css-class="btn">
- // good
- <component my-prop="prop" />
- ```
+ // bad
+ <component myProp="prop" />
+
+ // good
+ <component my-prop="prop" />
+ ```
[#34371]: https://gitlab.com/gitlab-org/gitlab-ce/issues/34371
#### Alignment
+
1. Follow these alignment styles for the template method:
- 1. With more than one attribute, all attributes should be on a new line:
- ```javascript
- // bad
- <component v-if="bar"
- param="baz" />
-
- <button class="btn">Click me</button>
-
- // good
- <component
- v-if="bar"
- param="baz"
- />
-
- <button class="btn">
- Click me
- </button>
- ```
- 1. The tag can be inline if there is only one attribute:
- ```javascript
- // good
- <component bar="bar" />
-
- // good
+
+ 1. With more than one attribute, all attributes should be on a new line:
+
+ ```javascript
+ // bad
+ <component v-if="bar"
+ param="baz" />
+
+ <button class="btn">Click me</button>
+
+ // good
<component
- bar="bar"
- />
+ v-if="bar"
+ param="baz"
+ />
+
+ <button class="btn">
+ Click me
+ </button>
+ ```
+
+ 1. The tag can be inline if there is only one attribute:
- // bad
- <component
- bar="bar" />
- ```
+ ```javascript
+ // good
+ <component bar="bar" />
+
+ // good
+ <component
+ bar="bar"
+ />
+
+ // bad
+ <component
+ bar="bar" />
+ ```
#### Quotes
+
1. Always use double quotes `"` inside templates and single quotes `'` for all other JS.
- ```javascript
- // bad
- template: `
- <button :class='style'>Button</button>
- `
- // good
- template: `
- <button :class="style">Button</button>
- `
- ```
+ ```javascript
+ // bad
+ template: `
+ <button :class='style'>Button</button>
+ `
+
+ // good
+ template: `
+ <button :class="style">Button</button>
+ `
+ ```
#### Props
+
1. Props should be declared as an object
- ```javascript
- // bad
- props: ['foo']
+ ```javascript
+ // bad
+ props: ['foo']
- // good
- props: {
- foo: {
- type: String,
- required: false,
- default: 'bar'
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
+ }
}
- }
- ```
+ ```
1. Required key should always be provided when declaring a prop
- ```javascript
- // bad
- props: {
- foo: {
- type: String,
+
+ ```javascript
+ // bad
+ props: {
+ foo: {
+ type: String,
+ }
}
- }
- // good
- props: {
- foo: {
- type: String,
- required: false,
- default: 'bar'
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
+ }
}
- }
- ```
+ ```
1. Default key should be provided if the prop is not required.
_Note:_ There are some scenarios where we need to check for the existence of the property.
On those a default key should not be provided.
- ```javascript
- // good
- props: {
- foo: {
- type: String,
- required: false,
+
+ ```javascript
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ }
}
- }
- // good
- props: {
- foo: {
- type: String,
- required: false,
- default: 'bar'
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
+ }
}
- }
- // good
- props: {
- foo: {
- type: String,
- required: true
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: true
+ }
}
- }
- ```
+ ```
#### Data
+
1. `data` method should always be a function
```javascript
@@ -502,38 +535,41 @@ On those a default key should not be provided.
#### Directives
1. Shorthand `@` is preferable over `v-on`
- ```javascript
- // bad
- <component v-on:click="eventHandler"/>
+ ```javascript
+ // bad
+ <component v-on:click="eventHandler"/>
- // good
- <component @click="eventHandler"/>
- ```
+ // good
+ <component @click="eventHandler"/>
+ ```
1. Shorthand `:` is preferable over `v-bind`
- ```javascript
- // bad
- <component v-bind:class="btn"/>
+ ```javascript
+ // bad
+ <component v-bind:class="btn"/>
- // good
- <component :class="btn"/>
- ```
+ // good
+ <component :class="btsn"/>
+ ```
#### Closing tags
+
1. Prefer self closing component tags
- ```javascript
- // bad
- <component></component>
- // good
- <component />
- ```
+ ```javascript
+ // bad
+ <component></component>
+
+ // good
+ <component />
+ ```
#### Ordering
1. Tag order in `.vue` file
+
```
<script>
// ...
@@ -550,12 +586,14 @@ On those a default key should not be provided.
```
1. Properties in a Vue Component:
- Check [order of properties in components rule][vue-order].
+ Check [order of properties in components rule][vue-order].
#### `:key`
+
When using `v-for` you need to provide a *unique* `:key` attribute for each item.
1. If the elements of the array being iterated have an unique `id` it is advised to use it:
+
```html
<div
v-for="item in items"
@@ -566,6 +604,7 @@ When using `v-for` you need to provide a *unique* `:key` attribute for each item
```
1. When the elements being iterated don't have a unique id, you can use the array index as the `:key` attribute
+
```html
<div
v-for="(item, index) in items"
@@ -575,8 +614,8 @@ When using `v-for` you need to provide a *unique* `:key` attribute for each item
</div>
```
-
1. When using `v-for` with `template` and there is more than one child element, the `:key` values must be unique. It's advised to use `kebab-case` namespaces.
+
```html
<template v-for="(item, index) in items">
<span :key="`span-${index}`"></span>
@@ -585,64 +624,69 @@ When using `v-for` you need to provide a *unique* `:key` attribute for each item
```
1. When dealing with nested `v-for` use the same guidelines as above.
- ```html
- <div
- v-for="item in items"
- :key="item.id"
- >
- <span
- v-for="element in array"
- :key="element.id"
- >
- <!-- content -->
- </span>
- </div>
- ```
+ ```html
+ <div
+ v-for="item in items"
+ :key="item.id"
+ >
+ <span
+ v-for="element in array"
+ :key="element.id"
+ >
+ <!-- content -->
+ </span>
+ </div>
+ ```
Useful links:
+
1. [`key`](https://vuejs.org/v2/guide/list.html#key)
1. [Vue Style Guide: Keyed v-for](https://vuejs.org/v2/style-guide/#Keyed-v-for-essential )
+
#### Vue and Bootstrap
1. Tooltips: Do not rely on `has-tooltip` class name for Vue components
- ```javascript
- // bad
- <span
- class="has-tooltip"
- title="Some tooltip text">
- Text
- </span>
- // good
- <span
- v-tooltip
- title="Some tooltip text">
- Text
- </span>
- ```
+ ```javascript
+ // bad
+ <span
+ class="has-tooltip"
+ title="Some tooltip text">
+ Text
+ </span>
+
+ // good
+ <span
+ v-tooltip
+ title="Some tooltip text">
+ Text
+ </span>
+ ```
1. Tooltips: When using a tooltip, include the tooltip directive, `./app/assets/javascripts/vue_shared/directives/tooltip.js`
1. Don't change `data-original-title`.
- ```javascript
- // bad
- <span data-original-title="tooltip text">Foo</span>
- // good
- <span title="tooltip text">Foo</span>
+ ```javascript
+ // bad
+ <span data-original-title="tooltip text">Foo</span>
- $('span').tooltip('_fixTitle');
- ```
+ // good
+ <span title="tooltip text">Foo</span>
+
+ $('span').tooltip('_fixTitle');
+ ```
### The Javascript/Vue Accord
+
The goal of this accord is to make sure we are all on the same page.
1. When writing Vue, you may not use jQuery in your application.
- 1. If you need to grab data from the DOM, you may query the DOM 1 time while bootstrapping your application to grab data attributes using `dataset`. You can do this without jQuery.
- 1. You may use a jQuery dependency in Vue.js following [this example from the docs](https://vuejs.org/v2/examples/select2.html).
- 1. If an outside jQuery Event needs to be listen to inside the Vue application, you may use jQuery event listeners.
- 1. We will avoid adding new jQuery events when they are not required. Instead of adding new jQuery events take a look at [different methods to do the same task](https://vuejs.org/v2/api/#vm-emit).
+ 1. If you need to grab data from the DOM, you may query the DOM 1 time while bootstrapping your application to grab data attributes using `dataset`. You can do this without jQuery.
+ 1. You may use a jQuery dependency in Vue.js following [this example from the docs](https://vuejs.org/v2/examples/select2.html).
+ 1. If an outside jQuery Event needs to be listen to inside the Vue application, you may use jQuery event listeners.
+ 1. We will avoid adding new jQuery events when they are not required. Instead of adding new jQuery events take a look at [different methods to do the same task](https://vuejs.org/v2/api/#vm-emit).
1. You may query the `window` object 1 time, while bootstrapping your application for application specific data (e.g. `scrollTo` is ok to access anytime). Do this access during the bootstrapping of your application.
1. You may have a temporary but immediate need to create technical debt by writing code that does not follow our standards, to be refactored later. Maintainers need to be ok with the tech debt in the first place. An issue should be created for that tech debt to evaluate it further and discuss. In the coming months you should fix that tech debt, with it's priority to be determined by maintainers.
1. When creating tech debt you must write the tests for that code before hand and those tests may not be rewritten. e.g. jQuery tests rewritten to Vue tests.
@@ -650,6 +694,7 @@ The goal of this accord is to make sure we are all on the same page.
1. Once you have chosen a centralized state management solution you must use it for your entire application. i.e. Don't mix and match your state management solutions.
## SCSS
+
- [SCSS](style_guide_scss.md)
[airbnb-js-style-guide]: https://github.com/airbnb/javascript
diff --git a/doc/development/fe_guide/style_guide_scss.md b/doc/development/fe_guide/style_guide_scss.md
index 48eb6d0a7d6..b09243598d5 100644
--- a/doc/development/fe_guide/style_guide_scss.md
+++ b/doc/development/fe_guide/style_guide_scss.md
@@ -183,9 +183,11 @@ Don't use ID selectors in CSS.
```
### Variables
+
Before adding a new variable for a color or a size, guarantee:
-1. There isn't already one
-2. There isn't a similar one we can use instead.
+
+- There isn't already one
+- There isn't a similar one we can use instead.
## Linting
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index f6cbd11042c..ccfd465531a 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -221,6 +221,14 @@ const vm = mountComponent(Component, data);
The main return value of a Vue component is the rendered output. In order to test the component we
need to test the rendered output. [Vue][vue-test] guide's to unit test show us exactly that:
+## Vue.js Expert Role
+One should apply to be a Vue.js expert by opening an MR when the Merge Request's they create and review show:
+- Deep understanding of Vue and Vuex reactivy
+- Vue and Vuex code are structured according to both official and our guidelines
+- Full understanding of testing a Vue and Vuex application
+- Vuex code follows the [documented pattern](./vuex.md#actions-pattern-request-and-receive-namespaces)
+- Knowledge about the existing Vue and Vuex applications and existing reusable components
+
[vue-docs]: http://vuejs.org/guide/index.html
[issue-boards]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/boards
diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md
index 4089cd37d73..0f57835fb87 100644
--- a/doc/development/fe_guide/vuex.md
+++ b/doc/development/fe_guide/vuex.md
@@ -114,19 +114,21 @@ When a request is made we often want to show a loading state to the user.
Instead of creating an action to toggle the loading state and dispatch it in the component,
create:
+
1. An action `requestSomething`, to toggle the loading state
1. An action `receiveSomethingSuccess`, to handle the success callback
1. An action `receiveSomethingError`, to handle the error callback
1. An action `fetchSomething` to make the request.
1. In case your application does more than a `GET` request you can use these as examples:
- 1. `PUT`: `createSomething`
- 2. `POST`: `updateSomething`
- 3. `DELETE`: `deleteSomething`
+ - `PUT`: `createSomething`
+ - `POST`: `updateSomething`
+ - `DELETE`: `deleteSomething`
The component MUST only dispatch the `fetchNamespace` action. Actions namespaced with `request` or `receive` should not be called from the component
The `fetch` action will be responsible to dispatch `requestNamespace`, `receiveNamespaceSuccess` and `receiveNamespaceError`
By following this pattern we guarantee:
+
1. All applications follow the same pattern, making it easier for anyone to maintain the code
1. All data in the application follows the same lifecycle pattern
1. Actions are contained and human friendly
@@ -290,23 +292,24 @@ export default {
```
### Vuex Gotchas
+
1. Do not call a mutation directly. Always use an action to commit a mutation. Doing so will keep consistency throughout the application. From Vuex docs:
- > why don't we just call store.commit('action') directly? Well, remember that mutations must be synchronous? Actions aren't. We can perform asynchronous operations inside an action.
+ > why don't we just call store.commit('action') directly? Well, remember that mutations must be synchronous? Actions aren't. We can perform asynchronous operations inside an action.
- ```javascript
- // component.vue
+ ```javascript
+ // component.vue
- // bad
- created() {
- this.$store.commit('mutation');
- }
+ // bad
+ created() {
+ this.$store.commit('mutation');
+ }
- // good
- created() {
- this.$store.dispatch('action');
- }
- ```
+ // good
+ created() {
+ this.$store.dispatch('action');
+ }
+ ```
1. Use mutation types instead of hardcoding strings. It will be less error prone.
1. The State will be accessible in all components descending from the use where the store is instantiated.
@@ -342,7 +345,7 @@ describe('component', () => {
name: 'Foo',
age: '30',
};
-
+
store = createStore();
// populate the store
diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md
index 5d1f657015c..1019a1fd0e2 100644
--- a/doc/development/feature_flags.md
+++ b/doc/development/feature_flags.md
@@ -20,7 +20,100 @@ dynamic (querying the DB etc.).
Once defined in `lib/feature.rb`, you will be able to activate a
feature for a given feature group via the [`feature_group` param of the features API](../api/features.md#set-or-create-a-feature)
+For GitLab.com, team members have access to feature flags through chatops. Only
+percentage gates are supported at this time. Setting a feature to be used 50% of
+the time, you should execute `/chatops run feature set my_feature_flag 50`.
+
## Feature flags for user applications
GitLab does not yet support the use of feature flags in deployed user applications.
-You can follow the progress on that [in the issue on our issue tracker](https://gitlab.com/gitlab-org/gitlab-ee/issues/779). \ No newline at end of file
+You can follow the progress on that [in the issue on our issue tracker](https://gitlab.com/gitlab-org/gitlab-ee/issues/779).
+
+## Developing with feature flags
+
+In general, it's better to have a group- or user-based gate, and you should prefer
+it over the use of percentage gates. This would make debugging easier, as you
+filter for example logs and errors based on actors too. Furthermore, this allows
+for enabling for the `gitlab-org` group first, while the rest of the users
+aren't impacted.
+
+```ruby
+# Good
+Feature.enabled?(:feature_flag, project)
+
+# Avoid, if possible
+Feature.enabled?(:feature_flag)
+```
+
+To use feature gates based on actors, the model needs to respond to
+`flipper_id`. For example, to enable for the Foo model:
+
+```ruby
+class Foo < ActiveRecord::Base
+ include FeatureGate
+end
+```
+
+Features that are developed and are intended to be merged behind a feature flag
+should not include a changelog entry. The entry should be added in the merge
+request removing the feature flags.
+
+In the rare case that you need the feature flag to be on automatically, use
+`default_enabled: true` when checking:
+
+```ruby
+Feature.enabled?(:feature_flag, project, default_enabled: true)
+```
+
+For more information about rolling out changes using feature flags, refer to the
+[Rolling out changes using feature flags](rolling_out_changes_using_feature_flags.md)
+guide.
+
+### Frontend
+
+For frontend code you can use the method `push_frontend_feature_flag`, which is
+available to all controllers that inherit from `ApplicationController`. Using
+this method you can expose the state of a feature flag as follows:
+
+```ruby
+before_action do
+ push_frontend_feature_flag(:vim_bindings)
+end
+
+def index
+ # ...
+end
+
+def edit
+ # ...
+end
+```
+
+You can then check for the state of the feature flag in JavaScript as follows:
+
+```javascript
+if ( gon.features.vimBindings ) {
+ // ...
+}
+```
+
+The name of the feature flag in JavaScript will always be camelCased, meaning
+that checking for `gon.features.vim_bindings` would not work.
+
+### Specs
+
+In the test environment `Feature.enabled?` is stubbed to always respond to `true`,
+so we make sure behavior under feature flag doesn't go untested in some non-specific
+contexts.
+
+Whenever a feature flag is present, make sure to test _both_ states of the
+feature flag. You can stub a feature flag as follows:
+
+```ruby
+stub_feature_flags(my_feature_flag: false)
+```
+
+## Enabling a feature flag
+
+Check how to [roll out changes using feature flags](rolling_out_changes_using_feature_flags.md).
+
diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md
index fdbd7f1fa37..6e014e8c751 100644
--- a/doc/development/file_storage.md
+++ b/doc/development/file_storage.md
@@ -45,6 +45,11 @@ In the case of Issues/MR/Notes Markdown attachments, there is a different approa
instead of basing the path into a mutable variable `:project_path_with_namespace`, it's possible to use the
hash of the project ID instead, if project migrates to the new approach (introduced in 10.2).
+> Note: We provide an [all-in-one rake task] to migrate all uploads to object
+> storage in one go. If a new Uploader class or model type is introduced, make
+> sure you add a rake task invocation corresponding to it to the [category
+> list].
+
### Path segments
Files are stored at multiple locations and use different path schemes.
@@ -137,3 +142,5 @@ end
[CarrierWave]: https://github.com/carrierwaveuploader/carrierwave
[Hashed Storage]: ../administration/repository_storage_types.md
+[all-in-one rake task]: ../administration/raketasks/uploads/migrate.md
+[category list]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/uploads/migrate.rake
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index 4f9ca1920a5..32beafad307 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -1,15 +1,11 @@
# GitLab Developers Guide to Working with Gitaly
[Gitaly](https://gitlab.com/gitlab-org/gitaly) is a high-level Git RPC service used by GitLab CE/EE,
-Workhorse and GitLab-Shell. All Rugged operations in GitLab CE/EE are currently being phased out to
-be replaced by Gitaly API calls.
-
-Visit the [Gitaly Migration Board](https://gitlab.com/gitlab-org/gitaly/boards/331341) for current
-status of the migration.
+Workhorse and GitLab-Shell.
## Developing new Git features
-Starting with Gitlab 10.8, all new Git features should be developed in
+Starting with GitLab 10.8, all new Git features should be developed in
Gitaly.
> This is a new process that is not clearly defined yet. If you want
@@ -52,57 +48,6 @@ comfortable writing Go code.
There is documentation for this approach in [the Gitaly
repo](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/ruby_endpoint.md).
-## Modifying existing Git features
-
-If you modify existing Git features in `lib/gitlab/git` you need to make
-sure the changes also work in Gitaly. Because we are still in the
-migration process there are a number of subtle pitfalls. Features that
-have been migrated have dual implementations (Gitaly and local). The
-Gitaly implementation may or may not use a vendored (and therefore
-possibly outdated) copy of the local implementation in `lib/gitlab/git`.
-
-To avoid unexpected problems and conflicts, all changes to
-`lib/gitlab/git` need to be approved by a member of the Gitaly team.
-
-For the time being, while the Gitaly migration is still in progress,
-there should be no Enterprise Edition-only Git code in
-`lib/gitlab/git`. Also no mixins.
-
-## Feature Flags
-
-Gitaly makes heavy use of [feature flags](feature_flags.md).
-
-Each Rugged-to-Gitaly migration goes through a [series of phases](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/MIGRATION_PROCESS.md):
-
-* **Opt-In**: by default the Rugged implementation is used.
- * Production instances can choose to enable the Gitaly endpoint by enabling the feature flag.
- * For testing purposes, you may wish to enable all feature flags by default. This can be done by exporting the following
- environment variable: `GITALY_FEATURE_DEFAULT_ON=1`.
- * On developer instances (ie, when `Rails.env.development?` is true), the Gitaly endpoint
- is enabled by default, but can be _disabled_ using feature flags.
-* **Opt-Out**: by default, the Gitaly endpoint is used, but the feature can be explicitly disabled using the feature flag.
-* **Mandatory**: The migration is complete and cannot be disabled. The old codepath is removed.
-
-### Enabling and Disabling Feature
-
-In the Rails console, type:
-
-```ruby
-Feature.enable(:gitaly_feature_name)
-Feature.disable(:gitaly_feature_name)
-```
-
-Where `gitaly_feature_name` is the name of the Gitaly feature. This can be determined by finding the appropriate
-`gitaly_migrate` code block, for example:
-
-```ruby
-gitaly_migrate(:tag_names) do
-...
-end
-```
-
-Since Gitaly features are always prefixed with `gitaly_`, the name of the feature flag in this case would be `gitaly_tag_names`.
-
## Gitaly-Related Test Failures
If your test-suite is failing with Gitaly issues, as a first step, try running:
diff --git a/doc/development/github_importer.md b/doc/development/github_importer.md
index 0d558583bb8..e860bde48dc 100644
--- a/doc/development/github_importer.md
+++ b/doc/development/github_importer.md
@@ -99,8 +99,8 @@ This worker will wrap up the import process by performing some housekeeping
Advancing stages is done in one of two ways:
-1. Scheduling the worker for the next stage directly.
-2. Scheduling a job for `Gitlab::GithubImport::AdvanceStageWorker` which will
+- Scheduling the worker for the next stage directly.
+- Scheduling a job for `Gitlab::GithubImport::AdvanceStageWorker` which will
advance the stage when all work of the current stage has been completed.
The first approach should only be used by workers that perform all their work in
@@ -147,7 +147,7 @@ We handle this by doing the following:
1. Once we hit the rate limit all jobs will automatically reschedule themselves
in such a way that they are not executed until the rate limit has been reset.
-2. We cache the mapping of GitHub users to GitLab users in Redis.
+1. We cache the mapping of GitHub users to GitLab users in Redis.
More information on user caching can be found below.
@@ -157,21 +157,21 @@ When mapping GitHub users to GitLab users we need to (in the worst case)
perform:
1. One API call to get the user's Email address.
-2. Two database queries to see if a corresponding GitLab user exists. One query
+1. Two database queries to see if a corresponding GitLab user exists. One query
will try to find the user based on the GitHub user ID, while the second query
is used to find the user using their GitHub Email address.
Because this process is quite expensive we cache the result of these lookups in
Redis. For every user looked up we store three keys:
-1. A Redis key mapping GitHub usernames to their Email addresses.
-2. A Redis key mapping a GitHub Email addresses to a GitLab user ID.
-3. A Redis key mapping a GitHub user ID to GitLab user ID.
+- A Redis key mapping GitHub usernames to their Email addresses.
+- A Redis key mapping a GitHub Email addresses to a GitLab user ID.
+- A Redis key mapping a GitHub user ID to GitLab user ID.
There are two types of lookups we cache:
-1. A positive lookup, meaning we found a GitLab user ID.
-2. A negative lookup, meaning we didn't find a GitLab user ID. Caching this
+- A positive lookup, meaning we found a GitLab user ID.
+- A negative lookup, meaning we didn't find a GitLab user ID. Caching this
prevents us from performing the same work for users that we know don't exist
in our GitLab database.
diff --git a/doc/development/gitlab_architecture_diagram.png b/doc/development/gitlab_architecture_diagram.png
index 378f7384574..90e27d5462a 100644
--- a/doc/development/gitlab_architecture_diagram.png
+++ b/doc/development/gitlab_architecture_diagram.png
Binary files differ
diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md
index d25d856c3a3..84dea7ce9aa 100644
--- a/doc/development/gotchas.md
+++ b/doc/development/gotchas.md
@@ -101,8 +101,10 @@ end
in a prepended module, which is very likely the case in EE. We could see
error like this:
- 1.1) Failure/Error: allow_any_instance_of(ApplicationSetting).to receive_messages(messages)
- Using `any_instance` to stub a method (elasticsearch_indexing) that has been defined on a prepended module (EE::ApplicationSetting) is not supported.
+ ```
+ 1.1) Failure/Error: allow_any_instance_of(ApplicationSetting).to receive_messages(messages)
+ Using `any_instance` to stub a method (elasticsearch_indexing) that has been defined on a prepended module (EE::ApplicationSetting) is not supported.
+ ```
### Alternative: `expect_next_instance_of`
diff --git a/doc/development/i18n/index.md b/doc/development/i18n/index.md
index 7290a175501..c44690a4c5d 100644
--- a/doc/development/i18n/index.md
+++ b/doc/development/i18n/index.md
@@ -50,3 +50,5 @@ able to proofread and instructions on becoming a proofreader yourself.
## Release
Translations are typically included in the next major or minor release.
+
+See [Merging translations from Crowdin](merging_translations.md)
diff --git a/doc/development/i18n/merging_translations.md b/doc/development/i18n/merging_translations.md
new file mode 100644
index 00000000000..d172aa6da21
--- /dev/null
+++ b/doc/development/i18n/merging_translations.md
@@ -0,0 +1,60 @@
+# Merging translations from Crowdin
+
+Crowdin automatically syncs the `gitlab.pot` file presenting newly
+added translations to the community of translators.
+
+At the same time, it creates a merge request to merge all newly added
+& approved translations. Find the [merge reqeust created by
+`gitlab-crowdin-bot`](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests?scope=all&utf8=%E2%9C%93&state=opened&author_username=gitlab-crowdin-bot)
+to see new and merged merge requests. They are created in EE and need
+to be ported to CE manually.
+
+## Validation
+
+By default Crowdin commits translations with `[skip ci]` in the commit
+message. This is done to avoid a bunch of pipelines being run. Before
+merging translations, make sure to trigger a pipeline to validate
+translations, we have static analysis validating things Crowdin
+doesn't do. Create a [new pipeline](https://gitlab.com/gitlab-org/gitlab-ee/pipelines/new) for the
+`master-i18n` branch.
+
+If there are validation errors, the easiest solution is to disapprove
+the offending string in Crowdin, leaving a comment with what is
+required to fix the offense. There is an
+[issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/49208)
+suggesting to automate this process. Disapproving will exclude the
+invalid translation, the merge request will be updated within a few
+minutes.
+
+It might be handy to pause the integration on the Crowdin side for a
+little while so translations don't keep coming. This can be done by
+clicking `Pause sync` on the [Crowdin integration settings
+page](https://translate.gitlab.com/project/gitlab-ee/settings#integration).
+
+When all failures are resolved, the translations need to be double
+checked once more [as discussed in this
+issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/37850).
+
+## Merging translations
+
+When all translations are found good and pipelines pass the
+translations can be merged into the master branch. After that is done,
+create a new merge request cherry-picking the translations from EE to
+CE. When merging the translations, make sure to check the `Remove
+source branch` checkbox, so Crowdin recreates the `master-i18n` from
+master after the new translation was merged.
+
+We are discussing automating this entire process
+[here](https://gitlab.com/gitlab-org/gitlab-ce/issues/39309).
+
+## Recreate the merge request
+
+Crowdin creates a new merge request as soon as the old one is closed
+or merged. But it won't recreate the `master-i18n` branch every
+time. To force Crowdin to recreate the branch, close any [open merge
+request](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests?scope=all&utf8=%E2%9C%93&state=opened&author_username=gitlab-crowdin-bot)
+and delete the
+[`master-18n`](https://gitlab.com/gitlab-org/gitlab-ee/branches/all?utf8=%E2%9C%93&search=master-i18n).
+
+This might be needed when the merge request contains failures that
+have been fixed on master.
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index ad5f6b2ecf6..c4ac53f45ac 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -20,6 +20,7 @@ are very appreciative of the work done by translators and proofreaders!
- French
- Davy Defaud - [GitLab](https://gitlab.com/DevDef), [Crowdin](https://crowdin.com/profile/DevDef)
- German
+ - Michael Hahnle - [GitLab](https://gitlab.com/mhah), [Crowdin](https://crowdin.com/profile/mhah)
- Indonesian
- Ahmad Naufal Mukhtar - [GitLab](https://gitlab.com/anaufalm), [Crowdin](https://crowdin.com/profile/anaufalm)
- Italian
@@ -30,6 +31,7 @@ are very appreciative of the work done by translators and proofreaders!
- Korean
- Chang-Ho Cha - [GitLab](https://gitlab.com/changho-cha), [Crowdin](https://crowdin.com/profile/zzazang)
- Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
+ - Ji Hun Oh - [GitLab](https://gitlab.com/Baw-Appie), [Crowdin](https://crowdin.com/profile/BawAppie)
- Polish
- Filip Mech - [GitLab](https://gitlab.com/mehenz), [Crowdin](https://crowdin.com/profile/mehenz)
- Portuguese, Brazilian
@@ -39,6 +41,7 @@ are very appreciative of the work done by translators and proofreaders!
- Nikita Grylov - [GitLab](https://gitlab.com/nixel2007), [Crowdin](https://crowdin.com/profile/nixel2007)
- Alexy Lustin - [GitLab](https://gitlab.com/allustin), [Crowdin](https://crowdin.com/profile/lustin)
- Spanish
+ - Pedro Garcia - [GitLab](https://gitlab.com/pedgarrod), [Crowdin](https://crowdin.com/profile/breaking_pitt)
- Ukrainian
- Volodymyr Sobotovych - [GitLab](https://gitlab.com/wheleph), [Crowdin](https://crowdin.com/profile/wheleph)
- Andrew Vityuk - [GitLab](https://gitlab.com/3_1_3_u), [Crowdin](https://crowdin.com/profile/andruwa13)
@@ -65,7 +68,6 @@ are very appreciative of the work done by translators and proofreaders!
Add your language in alphabetical order, and add yourself to the list
including:
-
- name
- link to your GitLab profile
- link to your CrowdIn profile
@@ -74,6 +76,8 @@ are very appreciative of the work done by translators and proofreaders!
have previously translated.
1. Your request to become a proofreader will be considered on the merits of
- your previous translations.
+ your previous translations by [GitLab team members](https://about.gitlab.com/team/)
+ or [Core team members](https://about.gitlab.com/core-team/) who are fluent in
+ the language or current proofreaders.
[proofreader-src]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/i18n/proofreader.md
diff --git a/doc/development/img/trigger_ss1.png b/doc/development/img/trigger_ss1.png
index ccff1009a25..addbc551f73 100644
--- a/doc/development/img/trigger_ss1.png
+++ b/doc/development/img/trigger_ss1.png
Binary files differ
diff --git a/doc/development/img/trigger_ss2.png b/doc/development/img/trigger_ss2.png
index 94dfd048793..02ef3810a59 100644
--- a/doc/development/img/trigger_ss2.png
+++ b/doc/development/img/trigger_ss2.png
Binary files differ
diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md
index a14c0752366..bef166f2aec 100644
--- a/doc/development/instrumentation.md
+++ b/doc/development/instrumentation.md
@@ -69,7 +69,7 @@ The easiest way to check if a method has been instrumented is to check its
source location. For example:
```ruby
-method = Rugged::TagCollection.instance_method(:[])
+method = Banzai::Renderer.method(:render)
method.source_location
```
@@ -82,7 +82,7 @@ method (along with its source location), this is easier than running the above
Ruby code. In case of the above snippet you'd run the following:
```
-$ Rugged::TagCollection#[]
+$ Banzai::Renderer.render
```
This will print out something along the lines of:
@@ -117,11 +117,11 @@ The block is executed and the execution time is stored as a set of fields in the
currently running transaction. If no transaction is present the block is yielded
without measuring anything.
-3 values are measured for a block:
+Three values are measured for a block:
-1. The real time elapsed, stored in NAME_real_time.
-2. The CPU time elapsed, stored in NAME_cpu_time.
-3. The call count, stored in NAME_call_count.
+- The real time elapsed, stored in NAME_real_time.
+- The CPU time elapsed, stored in NAME_cpu_time.
+- The call count, stored in NAME_call_count.
Both the real and CPU timings are measured in milliseconds.
diff --git a/doc/development/licensing.md b/doc/development/licensing.md
index ddaf636a742..0e71cd47481 100644
--- a/doc/development/licensing.md
+++ b/doc/development/licensing.md
@@ -100,7 +100,7 @@ If a gem uses a license which is not listed above, open an issue and ask. If a l
Keep in mind that each license has its own restrictions (typically defined in their body text). Please make sure to comply with those restrictions at all times whenever an external library is used.
-Gems which are included only in the "development" or "test" groups by Bundler are exempt from license requirements, as they're not distributed for use in production.
+Dependencies which are only used in development or test environment are exempt from license requirements, as they're not distributed for use in production.
**NOTE:** This document is **not** legal advice, nor is it comprehensive. It should not be taken as such.
diff --git a/doc/development/logging.md b/doc/development/logging.md
new file mode 100644
index 00000000000..abd08c420da
--- /dev/null
+++ b/doc/development/logging.md
@@ -0,0 +1,144 @@
+# GitLab Developers Guide to Logging
+
+[GitLab Logs](../administration/logs.md) play a critical role for both
+administrators and GitLab team members to diagnose problems in the field.
+
+## Don't use `Rails.logger`
+
+Currently `Rails.logger` calls all get saved into `production.log`, which contains
+a mix of Rails' logs and other calls developers have inserted in the code base.
+For example:
+
+```
+Started GET "/gitlabhq/yaml_db/tree/master" for 168.111.56.1 at 2015-02-12 19:34:53 +0200
+Processing by Projects::TreeController#show as HTML
+ Parameters: {"project_id"=>"gitlabhq/yaml_db", "id"=>"master"}
+
+ ...
+
+ Namespaces"."created_at" DESC, "namespaces"."id" DESC LIMIT 1 [["id", 26]]
+ CACHE (0.0ms) SELECT "members".* FROM "members" WHERE "members"."source_type" = 'Project' AND "members"."type" IN ('ProjectMember') AND "members"."source_id" = $1 AND "members"."source_type" = $2 AND "members"."user_id" = 1 ORDER BY "members"."created_at" DESC, "members"."id" DESC LIMIT 1 [["source_id", 18], ["source_type", "Project"]]
+ CACHE (0.0ms) SELECT "members".* FROM "members" WHERE "members"."source_type" = 'Project' AND "members".
+ (1.4ms) SELECT COUNT(*) FROM "merge_requests" WHERE "merge_requests"."target_project_id" = $1 AND ("merge_requests"."state" IN ('opened','reopened')) [["target_project_id", 18]]
+ Rendered layouts/nav/_project.html.haml (28.0ms)
+ Rendered layouts/_collapse_button.html.haml (0.2ms)
+ Rendered layouts/_flash.html.haml (0.1ms)
+ Rendered layouts/_page.html.haml (32.9ms)
+Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms)
+```
+
+These logs suffer from a number of problems:
+
+1. They often lack timestamps or other contextual information (e.g. project ID, user)
+2. They may span multiple lines, which make them hard to find via Elasticsearch.
+3. They lack a common structure, which make them hard to parse by log
+forwarders, such as Logstash or Fluentd. This also makes them hard to
+search.
+
+Note that currently on GitLab.com, any messages in `production.log` will
+NOT get indexed by Elasticsearch due to the sheer volume and noise. They
+do end up in Google Stackdriver, but it is still harder to search for
+logs there. See the [GitLab.com logging
+documentation](https://gitlab.com/gitlab-com/runbooks/blob/master/howto/logging.md)
+for more details.
+
+## Use structured (JSON) logging
+
+Structured logging solves these problems. Consider the example from an API request:
+
+```json
+{"time":"2018-10-29T12:49:42.123Z","severity":"INFO","duration":709.08,"db":14.59,"view":694.49,"status":200,"method":"GET","path":"/api/v4/projects","params":[{"key":"action","value":"git-upload-pack"},{"key":"changes","value":"_any"},{"key":"key_id","value":"secret"},{"key":"secret_token","value":"[FILTERED]"}],"host":"localhost","ip":"::1","ua":"Ruby","route":"/api/:version/projects","user_id":1,"username":"root","queue_duration":100.31,"gitaly_calls":30}
+```
+
+In a single line, we've included all the information that a user needs
+to understand what happened: the timestamp, HTTP method and path, user
+ID, etc.
+
+### How to use JSON logging
+
+Suppose you want to log the events that happen in a project
+importer. You want to log issues created, merge requests, etc. as the
+importer progresses. Here's what to do:
+
+1. Look at [the list of GitLab Logs](../administration/logs.md) to see
+if your log message might belong with one of the existing log files.
+1. If there isn't a good place, consider creating a new filename, but
+check with a maintainer if it makes sense to do so. A log file should
+make it easy for people to search pertinent logs in one place. For
+example, `geo.log` contains all logs pertaining to GitLab Geo.
+To create a new file:
+ 1. Choose a filename (e.g. `importer_json.log`).
+ 1. Create a new subclass of `Gitlab::JsonLogger`:
+
+ ```ruby
+ module Gitlab
+ module Import
+ class Logger < ::Gitlab::JsonLogger
+ def self.file_name_noext
+ 'importer_json'
+ end
+ end
+ end
+ end
+ ```
+
+ 1. In your class where you want to log, you might initialize the logger as an instance variable:
+
+ ```ruby
+ attr_accessor :logger
+
+ def initialize
+ @logger = Gitlab::Import::Logger.build
+ end
+ ```
+
+ Note that it's useful to memoize this because creating a new logger
+ each time you log will open a file, adding unnecessary overhead.
+
+1. Now insert log messages into your code. When adding logs,
+ make sure to include all the context as key-value pairs:
+
+ ```ruby
+ # BAD
+ logger.info("Unable to create project #{project.id}")
+ ```
+
+ ```ruby
+ # GOOD
+ logger.info("Unable to create project", project_id: project.id)
+ ```
+
+1. Be sure to create a common base structure of your log messages. For example,
+ all messages might have `current_user_id` and `project_id` to make it easier
+ to search for activities by user for a given time.
+
+1. Do NOT mix and match types. Elasticsearch won't be able to index your
+ logs properly if you [mix integer and string
+ types](https://www.elastic.co/guide/en/elasticsearch/guide/current/mapping.html#_avoiding_type_gotchas):
+
+ ```ruby
+ # BAD
+ logger.info("Import error", error: 1)
+ logger.info("Import error", error: "I/O failure")
+ ```
+
+ ```ruby
+ # GOOD
+ logger.info("Import error", error_code: 1, error: "I/O failure")
+ ```
+
+## Additional steps with new log files
+
+1. Consider log retention settings. By default, Omnibus will rotate any
+logs in `/var/log/gitlab/gitlab-rails/*.log` every hour and [keep at
+most 30 compressed files](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate).
+On GitLab.com, that setting is only 6 compressed files. These settings should suffice
+for most users, but you may need to tweak them in [omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab).
+
+1. If you add a new file, submit an issue to the [production
+tracker](https://gitlab.com/gitlab-com/gl-infra/production/issues) or
+a merge request to the [gitlab_fluentd](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
+project. See [this example](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd/merge_requests/51/diffs).
+
+1. Be sure to update the [GitLab CE/EE documentation](../administration/logs.md) and the [GitLab.com
+runbooks](https://gitlab.com/gitlab-com/runbooks/blob/master/howto/logging.md).
diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md
index 12badbe39b2..ee01c89e0ed 100644
--- a/doc/development/merge_request_performance_guidelines.md
+++ b/doc/development/merge_request_performance_guidelines.md
@@ -168,6 +168,7 @@ user objects for every username we can remove the need for running the same
query for every mention of `@alice`.
Caching data per transaction can be done using
-[RequestStore](https://github.com/steveklabnik/request_store). Caching data in
-Redis can be done using [Rails' caching
+[RequestStore](https://github.com/steveklabnik/request_store) (use
+`Gitlab::SafeRequestStore` to avoid having to remember to check
+`RequestStore.active?`). Caching data in Redis can be done using [Rails' caching
system](http://guides.rubyonrails.org/caching_with_rails.html).
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 6a5ed54cdb7..a99267bfbba 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -187,12 +187,7 @@ end
When adding a foreign-key constraint to either an existing or new
column remember to also add a index on the column.
-This is _required_ if the foreign-key constraint specifies
-`ON DELETE CASCADE` or `ON DELETE SET NULL` behavior. On a cascading
-delete, the [corresponding record needs to be retrieved using an
-index](https://www.cybertec-postgresql.com/en/postgresql-indexes-and-foreign-keys/)
-(otherwise, we'd need to scan the whole table) for subsequent update or
-deletion.
+This is _required_ for all foreign-keys.
Here's an example where we add a new column with a foreign key
constraint. Note it includes `index: true` to create an index for it.
diff --git a/doc/development/module_with_instance_variables.md b/doc/development/module_with_instance_variables.md
index 48a1b7f847e..7bdfa04fc57 100644
--- a/doc/development/module_with_instance_variables.md
+++ b/doc/development/module_with_instance_variables.md
@@ -60,7 +60,7 @@ as long as it's contained in the same module; that is, no other modules or
objects are touching them, then it would be an acceptable use.
We especially allow the case where a single instance variable is used with
-`||=` to setup the value. This would look like:
+`||=` to set up the value. This would look like:
``` ruby
module M
diff --git a/doc/development/new_fe_guide/development/performance.md b/doc/development/new_fe_guide/development/performance.md
index 244dfb3756f..5ccd5357314 100644
--- a/doc/development/new_fe_guide/development/performance.md
+++ b/doc/development/new_fe_guide/development/performance.md
@@ -2,7 +2,7 @@
## Monitoring
-We have a performance dashboard available in one of our [grafana instances](https://performance.gprd.gitlab.com/dashboard/db/sitespeed-page-summary?orgId=1). This dashboard automatically aggregates metric data from [sitespeed.io](https://sitespeed.io) every 6 hours. These changes are displayed after a set number of pages are aggregated.
+We have a performance dashboard available in one of our [grafana instances](https://dashboards.gitlab.net/d/1EBTz3Dmz/sitespeed-page-summary?orgId=1). This dashboard automatically aggregates metric data from [sitespeed.io](https://sitespeed.io) every 6 hours. These changes are displayed after a set number of pages are aggregated.
These pages can be found inside a text file in the gitlab-build-images [repository](https://gitlab.com/gitlab-org/gitlab-build-images) called [gitlab.txt](https://gitlab.com/gitlab-org/gitlab-build-images/blob/master/scripts/gitlab.txt)
Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing urls of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/team) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes will go live on the next scheduled run after the changes are merged into `master`.
diff --git a/doc/development/new_fe_guide/development/testing.md b/doc/development/new_fe_guide/development/testing.md
index 53dfe6774e9..082acbedcd2 100644
--- a/doc/development/new_fe_guide/development/testing.md
+++ b/doc/development/new_fe_guide/development/testing.md
@@ -1,30 +1,247 @@
# Overview of Frontend Testing
-## Types of tests in our codebase
+Tests relevant for frontend development can be found at two places:
+
+- `spec/javascripts/` which are run by Karma and contain
+ - [frontend unit tests](#frontend-unit-tests)
+ - [frontend component tests](#frontend-component-tests)
+ - [frontend integration tests](#frontend-integration-tests)
+- `spec/features/` which are run by RSpec and contain
+ - [feature tests](#feature-tests)
+
+In addition there were feature tests in `features/` run by Spinach in the past.
+These have been removed from our codebase in May 2018 ([#23036](https://gitlab.com/gitlab-org/gitlab-ce/issues/23036)).
+
+See also:
-* **RSpec**
- * **[Ruby unit tests](#ruby-unit-tests-spec-rb)** for models, controllers, helpers, etc. (`/spec/**/*.rb`)
- * **[Full feature tests](#full-feature-tests-spec-features-rb)** (`/spec/features/**/*.rb`)
-* **[Karma](#karma-tests-spec-javascripts-js)** (`/spec/javascripts/**/*.js`)
-* ~~Spinach~~ — These have been removed from our codebase in May 2018. (`/features/`)
+- [old testing guide](../../testing_guide/frontend_testing.html)
+- [notes on testing Vue components](../../fe_guide/vue.html#testing-vue-components)
-## RSpec: Ruby unit tests `/spec/**/*.rb`
+## Frontend unit tests
-These tests are meant to unit test the ruby models, controllers and helpers.
+Unit tests are on the lowest abstraction level and typically test functionality that is not directly perceivable by a user.
-### When do we write/update these tests?
+### When to use unit tests
-Whenever we create or modify any Ruby models, controllers or helpers we add/update corresponding tests.
+<details>
+ <summary>exported functions and classes</summary>
+ Anything that is exported can be reused at various places in a way you have no control over.
+ Therefore it is necessary to document the expected behavior of the public interface with tests.
+</details>
----
+<details>
+ <summary>Vuex actions</summary>
+ Any Vuex action needs to work in a consistent way independent of the component it is triggered from.
+</details>
-## RSpec: Full feature tests `/spec/features/**/*.rb`
+<details>
+ <summary>Vuex mutations</summary>
+ For complex Vuex mutations it helps to identify the source of a problem by separating the tests from other parts of the Vuex store.
+</details>
-Full feature tests will load a full app environment and allow us to test things like rendering DOM, interacting with links and buttons, testing the outcome of those interactions through multiple pages if necessary. These are also called end-to-end tests but should not be confused with QA end-to-end tests (`package-and-qa` manual pipeline job).
+### When *not* to use unit tests
-### When do we write/update these tests?
+<details>
+ <summary>non-exported functions or classes</summary>
+ Anything that is not exported from a module can be considered private or an implementation detail and doesn't need to be tested.
+</details>
-When we add a new feature, we write at least two tests covering the success and the failure scenarios.
+<details>
+ <summary>constants</summary>
+ Testing the value of a constant would mean to copy it.
+ This results in extra effort without additional confidence that the value is correct.
+</details>
+
+<details>
+ <summary>Vue components</summary>
+ Computed properties, methods, and lifecycle hooks can be considered an implementation detail of components and don't need to be tested.
+ They are implicitly covered by component tests.
+ The <a href="https://vue-test-utils.vuejs.org/guides/#getting-started">official Vue guidelines</a> suggest the same.
+</details>
+
+### What to mock in unit tests
+
+<details>
+ <summary>state of the class under test</summary>
+ Modifying the state of the class under test directly rather than using methods of the class avoids side-effects in test setup.
+</details>
+
+<details>
+ <summary>other exported classes</summary>
+ Every class needs to be tested in isolation to prevent test scenarios from growing exponentially.
+</details>
+
+<details>
+ <summary>single DOM elements if passed as parameters</summary>
+ For tests that only operate on single DOM elements rather than a whole page, creating these elements is cheaper than loading a whole HTML fixture.
+</details>
+
+<details>
+ <summary>all server requests</summary>
+ When running frontend unit tests, the backend may not be reachable.
+ Therefore all outgoing requests need to be mocked.
+</details>
+
+<details>
+ <summary>asynchronous background operations</summary>
+ Background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
+</details>
+
+### What *not* to mock in unit tests
+
+<details>
+ <summary>non-exported functions or classes</summary>
+ Everything that is not exported can be considered private to the module and will be implicitly tested via the exported classes / functions.
+</details>
+
+<details>
+ <summary>methods of the class under test</summary>
+ By mocking methods of the class under test, the mocks will be tested and not the real methods.
+</details>
+
+<details>
+ <summary>utility functions (pure functions, or those that only modify parameters)</summary>
+ If a function has no side effects because it has no state, it is safe to not mock it in tests.
+</details>
+
+<details>
+ <summary>full HTML pages</summary>
+ Loading the HTML of a full page slows down tests, so it should be avoided in unit tests.
+</details>
+
+## Frontend component tests
+
+Component tests cover the state of a single component that is perceivable by a user depending on external signals such as user input, events fired from other components, or application state.
+
+### When to use component tests
+
+- Vue components
+
+### When *not* to use component tests
+
+<details>
+ <summary>Vue applications</summary>
+ Vue applications may contain many components.
+ Testing them on a component level requires too much effort.
+ Therefore they are tested on frontend integration level.
+</details>
+
+<details>
+ <summary>HAML templates</summary>
+ HAML templates contain only Markup and no frontend-side logic.
+ Therefore they are not complete components.
+</details>
+
+### What to mock in component tests
+
+<details>
+ <summary>DOM</summary>
+ Operating on the real DOM is significantly slower than on the virtual DOM.
+</details>
+
+<details>
+ <summary>properties and state of the component under test</summary>
+ Similarly to testing classes, modifying the properties directly (rather than relying on methods of the component) avoids side-effects.
+</details>
+
+<details>
+ <summary>Vuex store</summary>
+ To avoid side effects and keep component tests simple, Vuex stores are replaced with mocks.
+</details>
+
+<details>
+ <summary>all server requests</summary>
+ Similar to unit tests, when running component tests, the backend may not be reachable.
+ Therefore all outgoing requests need to be mocked.
+</details>
+
+<details>
+ <summary>asynchronous background operations</summary>
+ Similar to unit tests, background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
+</details>
+
+<details>
+ <summary>child components</summary>
+ Every component is tested individually, so child components are mocked.
+ See also <a href="https://vue-test-utils.vuejs.org/api/#shallowmount">shallowMount()</a>
+</details>
+
+### What *not* to mock in component tests
+
+<details>
+ <summary>methods or computed properties of the component under test</summary>
+ By mocking part of the component under test, the mocks will be tested and not the real component.
+</details>
+
+<details>
+ <summary>functions and classes independent from Vue</summary>
+ All plain JavaScript code is already covered by unit tests and needs not to be mocked in component tests.
+</details>
+
+## Frontend integration tests
+
+Integration tests cover the interaction between all components on a single page.
+Their abstraction level is comparable to how a user would interact with the UI.
+
+### When to use integration tests
+
+<details>
+ <summary>page bundles (<code>index.js</code> files in <code>app/assets/javascripts/pages/</code>)</summary>
+ Testing the page bundles ensures the corresponding frontend components integrate well.
+</details>
+
+<details>
+ <summary>Vue applications outside of page bundles</summary>
+ Testing Vue applications as a whole ensures the corresponding frontend components integrate well.
+</details>
+
+### What to mock in integration tests
+
+<details>
+ <summary>HAML views (use fixtures instead)</summary>
+ Rendering HAML views requires a Rails environment including a running database which we cannot rely on in frontend tests.
+</details>
+
+<details>
+ <summary>all server requests</summary>
+ Similar to unit and component tests, when running component tests, the backend may not be reachable.
+ Therefore all outgoing requests need to be mocked.
+</details>
+
+<details>
+ <summary>asynchronous background operations that are not perceivable on the page</summary>
+ Background operations that affect the page need to be tested on this level.
+ All other background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
+</details>
+
+### What *not* to mock in integration tests
+
+<details>
+ <summary>DOM</summary>
+ Testing on the real DOM ensures our components work in the environment they are meant for.
+ Part of this will be delegated to <a href="https://gitlab.com/gitlab-org/quality/team-tasks/issues/45">cross-browser testing</a>.
+</details>
+
+<details>
+ <summary>properties or state of components</summary>
+ On this level, all tests can only perform actions a user would do.
+ For example to change the state of a component, a click event would be fired.
+</details>
+
+<details>
+ <summary>Vuex stores</summary>
+ When testing the frontend code of a page as a whole, the interaction between Vue components and Vuex stores is covered as well.
+</details>
+
+## Feature tests
+
+In contrast to [frontend integration tests](#frontend-integration-tests), feature tests make requests against the real backend instead of using fixtures.
+This also implies that database queries are executed which makes this category significantly slower.
+
+### When to use feature tests
+
+- use cases that require a backend and cannot be tested using fixtures
+- behavior that is not part of a page bundle but defined globally
### Relevant notes
@@ -48,33 +265,9 @@ wait_for_requests
expect(page).not_to have_selector('.card')
```
----
-
-## Karma tests `/spec/javascripts/**/*.js`
-
-These are the more frontend-focused, at the moment. They're **faster** than `rspec` and make for very quick testing of frontend components.
-
-### When do we write/update these tests?
-
-When we add/update a method/action/mutation to Vue or Vuex, we write karma tests to ensure the logic we wrote doesn't break. We should, however, refrain from writing tests that double-test Vue's internal features.
-
-### Relevant notes
-
-Karma tests are run against a virtual DOM.
+## Test helpers
-To populate the DOM, we can use fixtures to fake the generation of HTML instead of having Rails do that.
-
-Be sure to check the [best practices for karma tests](../../testing_guide/frontend_testing.html#best-practices).
-
-### Vue and Vuex
-
-Test as much as possible without double-testing Vue's internal features, as mentioned above.
-
-Make sure to test computedProperties, mutations, actions. Run the action and test that the proper mutations are committed.
-
-Also check these [notes on testing Vue components](../../fe_guide/vue.html#testing-vue-components).
-
-#### Vuex Helper: `testAction`
+### Vuex Helper: `testAction`
We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/en/testing.html):
@@ -97,7 +290,7 @@ testAction(
Check an example in [spec/javascripts/ide/stores/actions_spec.jsspec/javascripts/ide/stores/actions_spec.js](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/javascripts/ide/stores/actions_spec.js).
-#### Vue Helper: `mountComponent`
+### Vue Helper: `mountComponent`
To make mounting a Vue component easier and more readable, we have a few helpers available in `spec/helpers/vue_mount_component_helper`.
@@ -133,6 +326,7 @@ afterEach(() => {
vm.$destroy();
});
```
+
## Testing with older browsers
Some regressions only affect a specific browser version. We can install and test in particular browsers with either Firefox or Browserstack using the following steps:
@@ -140,20 +334,21 @@ Some regressions only affect a specific browser version. We can install and test
### Browserstack
-[Browserstack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers.
+[Browserstack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers.
You can use it directly through the [live app](https://www.browserstack.com/live) or you can install the [chrome extension](https://chrome.google.com/webstore/detail/browserstack/nkihdmlheodkdfojglpcjjmioefjahjb) for easy access.
You can find the credentials on 1Password, under `frontendteam@gitlab.com`.
### Firefox
#### macOS
+
You can download any older version of Firefox from the releases FTP server, https://ftp.mozilla.org/pub/firefox/releases/
1. From the website, select a version, in this case `50.0.1`.
-2. Go to the mac folder.
-3. Select your preferred language, you will find the dmg package inside, download it.
-4. Drag and drop the application to any other folder but the `Applications` folder.
-5. Rename the application to something like `Firefox_Old`.
-6. Move the application to the `Applications` folder.
-7. Open up a terminal and run `/Applications/Firefox_Old.app/Contents/MacOS/firefox-bin -profilemanager` to create a new profile specific to that Firefox version.
-8. Once the profile has been created, quit the app, and run it again like normal. You now have a working older Firefox version.
+1. Go to the mac folder.
+1. Select your preferred language, you will find the dmg package inside, download it.
+1. Drag and drop the application to any other folder but the `Applications` folder.
+1. Rename the application to something like `Firefox_Old`.
+1. Move the application to the `Applications` folder.
+1. Open up a terminal and run `/Applications/Firefox_Old.app/Contents/MacOS/firefox-bin -profilemanager` to create a new profile specific to that Firefox version.
+1. Once the profile has been created, quit the app, and run it again like normal. You now have a working older Firefox version.
diff --git a/doc/development/new_fe_guide/index.md b/doc/development/new_fe_guide/index.md
index 78931defa24..bfcca9cec7b 100644
--- a/doc/development/new_fe_guide/index.md
+++ b/doc/development/new_fe_guide/index.md
@@ -19,6 +19,10 @@ Guidance on topics related to development.
Learn about all the dependencies that make up our frontend, including some of our own custom built libraries.
+## [Modules](modules/index.md)
+
+Learn about all the internal JavaScript modules that make up our frontend.
+
## [Style guides](style/index.md)
Style guides to keep our code consistent.
diff --git a/doc/development/new_fe_guide/modules/dirty_submit.md b/doc/development/new_fe_guide/modules/dirty_submit.md
new file mode 100644
index 00000000000..6c03958b463
--- /dev/null
+++ b/doc/development/new_fe_guide/modules/dirty_submit.md
@@ -0,0 +1,23 @@
+# Dirty Submit
+
+> [Introduced][ce-21115] in GitLab 11.3.
+> [dirty_submit][dirty-submit]
+
+## Summary
+
+Prevent submitting forms with no changes.
+
+Currently handles `input`, `textarea` and `select` elements.
+
+## Usage
+
+```js
+import dirtySubmitFactory from './dirty_submit/dirty_submit_form';
+
+new DirtySubmitForm(document.querySelector('form'));
+// or
+new DirtySubmitForm(document.querySelectorAll('form'));
+```
+
+[ce-21115]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21115
+[dirty-submit]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/dirty_submit/ \ No newline at end of file
diff --git a/doc/development/new_fe_guide/modules/index.md b/doc/development/new_fe_guide/modules/index.md
new file mode 100644
index 00000000000..0a7f2dbd819
--- /dev/null
+++ b/doc/development/new_fe_guide/modules/index.md
@@ -0,0 +1,5 @@
+# Modules
+
+* [DirtySubmit](dirty_submit.md)
+
+ Disable form submits until there are unsaved changes. \ No newline at end of file
diff --git a/doc/development/new_fe_guide/style/javascript.md b/doc/development/new_fe_guide/style/javascript.md
index 57efd9353bc..922fd1e4ea4 100644
--- a/doc/development/new_fe_guide/style/javascript.md
+++ b/doc/development/new_fe_guide/style/javascript.md
@@ -12,158 +12,157 @@ You can run eslint locally by running `yarn eslint`
<a name="avoid-foreach"></a><a name="1.1"></a>
- [1.1](#avoid-foreach) **Avoid ForEach when mutating data** Use `map`, `reduce` or `filter` instead of `forEach` when mutating data. This will minimize mutations in functions ([which is aligned with Airbnb's style guide][airbnb-minimize-mutations])
-```
-// bad
-users.forEach((user, index) => {
- user.id = index;
-});
-
-// good
-const usersWithId = users.map((user, index) => {
- return Object.assign({}, user, { id: index });
-});
-```
+ ```
+ // bad
+ users.forEach((user, index) => {
+ user.id = index;
+ });
+
+ // good
+ const usersWithId = users.map((user, index) => {
+ return Object.assign({}, user, { id: index });
+ });
+ ```
## Functions
<a name="limit-params"></a><a name="2.1"></a>
- [2.1](#limit-params) **Limit number of parameters** If your function or method has more than 3 parameters, use an object as a parameter instead.
-```
-// bad
-function a(p1, p2, p3) {
- // ...
-};
-
-// good
-function a(p) {
- // ...
-};
-```
+ ```
+ // bad
+ function a(p1, p2, p3) {
+ // ...
+ };
+
+ // good
+ function a(p) {
+ // ...
+ };
+ ```
## Classes & constructors
<a name="avoid-constructor-side-effects"></a><a name="3.1"></a>
- [3.1](#avoid-constructor-side-effects) **Avoid side effects in constructors** Avoid making some operations in the `constructor`, such as asynchronous calls, API requests and DOM manipulations. Prefer moving them into separate functions. This will make tests easier to write and code easier to maintain.
- ```javascript
- // bad
- class myClass {
- constructor(config) {
- this.config = config;
- axios.get(this.config.endpoint)
+ ```javascript
+ // bad
+ class myClass {
+ constructor(config) {
+ this.config = config;
+ axios.get(this.config.endpoint)
+ }
}
- }
-
- // good
- class myClass {
- constructor(config) {
- this.config = config;
+
+ // good
+ class myClass {
+ constructor(config) {
+ this.config = config;
+ }
+
+ makeRequest() {
+ axios.get(this.config.endpoint)
+ }
}
-
- makeRequest() {
- axios.get(this.config.endpoint)
- }
- }
- const instance = new myClass();
- instance.makeRequest();
-
- ```
+ const instance = new myClass();
+ instance.makeRequest();
+
+ ```
<a name="avoid-classes-to-handle-dom-events"></a><a name="3.2"></a>
- [3.2](#avoid-classes-to-handle-dom-events) **Avoid classes to handle DOM events** If the only purpose of the class is to bind a DOM event and handle the callback, prefer using a function.
-```
-// bad
-class myClass {
- constructor(config) {
- this.config = config;
- }
-
- init() {
- document.addEventListener('click', () => {});
- }
-}
-
-// good
-
-const myFunction = () => {
- document.addEventListener('click', () => {
- // handle callback here
- });
-}
-```
+ ```
+ // bad
+ class myClass {
+ constructor(config) {
+ this.config = config;
+ }
+
+ init() {
+ document.addEventListener('click', () => {});
+ }
+ }
+
+ // good
+
+ const myFunction = () => {
+ document.addEventListener('click', () => {
+ // handle callback here
+ });
+ }
+ ```
<a name="element-container"></a><a name="3.3"></a>
- [3.3](#element-container) **Pass element container to constructor** When your class manipulates the DOM, receive the element container as a parameter.
This is more maintainable and performant.
-```
-// bad
-class a {
- constructor() {
- document.querySelector('.b');
- }
-}
-
-// good
-class a {
- constructor(options) {
- options.container.querySelector('.b');
- }
-}
-```
+ ```
+ // bad
+ class a {
+ constructor() {
+ document.querySelector('.b');
+ }
+ }
+
+ // good
+ class a {
+ constructor(options) {
+ options.container.querySelector('.b');
+ }
+ }
+ ```
## Type Casting & Coercion
<a name="use-parseint"></a><a name="4.1"></a>
- [4.1](#use-parseint) **Use ParseInt** Use `ParseInt` when converting a numeric string into a number.
-```
-// bad
-Number('10')
-
-
-// good
-parseInt('10', 10);
-```
+ ```
+ // bad
+ Number('10')
+
+ // good
+ parseInt('10', 10);
+ ```
## CSS Selectors
<a name="use-js-prefix"></a><a name="5.1"></a>
- [5.1](#use-js-prefix) **Use js prefix** If a CSS class is only being used in JavaScript as a reference to the element, prefix the class name with `js-`
-```
-// bad
-<button class="add-user"></button>
-
-// good
-<button class="js-add-user"></button>
-```
+ ```
+ // bad
+ <button class="add-user"></button>
+
+ // good
+ <button class="js-add-user"></button>
+ ```
## Modules
<a name="use-absolute-paths"></a><a name="6.1"></a>
- [6.1](#use-absolute-paths) **Use absolute paths for nearby modules** Use absolute paths if the module you are importing is less than two levels up.
-```
-// bad
-import GitLabStyleGuide from '~/guides/GitLabStyleGuide';
-
-// good
-import GitLabStyleGuide from '../GitLabStyleGuide';
-```
+ ```
+ // bad
+ import GitLabStyleGuide from '~/guides/GitLabStyleGuide';
+
+ // good
+ import GitLabStyleGuide from '../GitLabStyleGuide';
+ ```
<a name="use-relative-paths"></a><a name="6.2"></a>
- [6.2](#use-relative-paths) **Use relative paths for distant modules** If the module you are importing is two or more levels up, use a relative path instead of an absolute path.
-```
-// bad
-import GitLabStyleGuide from '../../../guides/GitLabStyleGuide';
-
-// good
-import GitLabStyleGuide from '~/GitLabStyleGuide';
-```
+ ```
+ // bad
+ import GitLabStyleGuide from '../../../guides/GitLabStyleGuide';
+
+ // good
+ import GitLabStyleGuide from '~/GitLabStyleGuide';
+ ```
<a name="global-namespace"></a><a name="6.3"></a>
- [6.3](#global-namespace) **Do not add to global namespace**
diff --git a/doc/development/newlines_styleguide.md b/doc/development/newlines_styleguide.md
index 32aac2529a4..5f7210020b6 100644
--- a/doc/development/newlines_styleguide.md
+++ b/doc/development/newlines_styleguide.md
@@ -10,7 +10,7 @@ def method
issue = Issue.new
issue.save
-
+
render json: issue
end
```
@@ -20,7 +20,7 @@ end
def method
issue = Issue.new
issue.save
-
+
render json: issue
end
```
diff --git a/doc/development/ordering_table_columns.md b/doc/development/ordering_table_columns.md
index 5d00e1f7a0c..e9c6481635b 100644
--- a/doc/development/ordering_table_columns.md
+++ b/doc/development/ordering_table_columns.md
@@ -1,32 +1,49 @@
-# Ordering Table Columns
+# Ordering Table Columns in PostgreSQL
Similar to C structures the space of a table is influenced by the order of
columns. This is because the size of columns is aligned depending on the type of
-the column. Take the following column order for example:
+the following column. Let's consider an example:
-* id (integer, 4 bytes)
-* name (text, variable)
-* user_id (integer, 4 bytes)
+- `id` (integer, 4 bytes)
+- `name` (text, variable)
+- `user_id` (integer, 4 bytes)
-Integers are aligned to the word size. This means that on a 64 bit platform the
-actual size of each column would be: 8 bytes, variable, 8 bytes. This means that
-each row will require at least 16 bytes for the two integers, and a variable
-amount for the text field. If a table has a few rows this is not an issue, but
-once you start storing millions of rows you can save space by using a different
-order. For the above example a more ideal column order would be the following:
+The first column is a 4-byte integer. The next is text of variable length. The
+`text` data type requires 1-word alignment, and on 64-bit platform, 1 word is 8
+bytes. To meet the alignment requirements, four zeros are to be added right
+after the first column, so `id` occupies 4 bytes, then 4 bytes of alignment
+padding, and only next `name` is being stored. Therefore, in this case, 8 bytes
+will be spent for storing a 4-byte integer.
-* id (integer, 4 bytes)
-* user_id (integer, 4 bytes)
-* name (text, variable)
+The space between rows is also subject to alignment padding. The `user_id`
+column takes only 4 bytes, and on 64-bit platform, 4 zeroes will be added for
+alignment padding, to allow storing the next row beginning with the "clear" word.
-In this setup the `id` and `user_id` columns can be packed together, which means
-we only need 8 bytes to store _both_ of them. This in turn each row will require
-8 bytes less of space.
+As a result, the actual size of each column would be (ommiting variable length
+data and 24-byte tuple header): 8 bytes, variable, 8 bytes. This means that
+each row will require at least 16 bytes for the two 4-byte integers. If a table
+has a few rows this is not an issue. However, once you start storing millions of
+rows you can save space by using a different order. For the above example, the
+ideal column order would be the following:
+
+- `id` (integer, 4 bytes)
+- `user_id` (integer, 4 bytes)
+- `name` (text, variable)
+
+or
+
+- `name` (text, variable)
+- `id` (integer, 4 bytes)
+- `user_id` (integer, 4 bytes)
+
+In these examples, the `id` and `user_id` columns are packed together, which
+means we only need 8 bytes to store _both_ of them. This in turn means each row
+will require 8 bytes less space.
For GitLab we require that columns of new tables are ordered based to use the
least amount of space. An easy way of doing this is to order them based on the
-type size in descending order with variable sizes (string and text columns for
-example) at the end.
+type size in descending order with variable sizes (`text`, `varchar`, arrays,
+`json`, `jsonb`, and so on) at the end.
## Type Sizes
@@ -36,7 +53,7 @@ of information we will list the sizes of common types here so it's easier to
look them up. Here "word" refers to the word size, which is 4 bytes for a 32
bits platform and 8 bytes for a 64 bits platform.
-| Type | Size | Aligned To |
+| Type | Size | Alignment needed |
|:-----------------|:-------------------------------------|:-----------|
| smallint | 2 bytes | 1 word |
| integer | 4 bytes | 1 word |
@@ -58,7 +75,7 @@ always be at the end of a table.
## Real Example
-Let's use the "events" table as an example, which currently has the following
+Let's use the `events` table as an example, which currently has the following
layout:
| Column | Type | Size |
@@ -89,8 +106,8 @@ divided into fixed size chunks as follows:
| 8 bytes | updated_at |
| 8 bytes | action, author_id |
-This means that excluding the variable sized data we need at least 48 bytes per
-row.
+This means that excluding the variable sized data and tuple header, we need at
+least 8 * 6 = 48 bytes per row.
We can optimise this by using the following column order instead:
@@ -120,8 +137,8 @@ This would produce the following chunks:
| variable | title |
| variable | data |
-Here we only need 40 bytes per row excluding the variable sized data. 8 bytes
-being saved may not sound like much, but for tables as large as the "events"
-table it does begin to matter. For example, when storing 80 000 000 rows this
-translates to a space saving of at least 610 MB: all by just changing the order
-of a few columns.
+Here we only need 40 bytes per row excluding the variable sized data and 24-byte
+tuple header. 8 bytes being saved may not sound like much, but for tables as
+large as the `events` table it does begin to matter. For example, when storing
+80 000 000 rows this translates to a space saving of at least 610 MB, all by
+just changing the order of a few columns.
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 6b4cb6d72d1..4cc2fdc9a58 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -9,17 +9,17 @@ The process of solving performance problems is roughly as follows:
1. Make sure there's an issue open somewhere (e.g., on the GitLab CE issue
tracker), create one if there isn't. See [#15607][#15607] for an example.
-2. Measure the performance of the code in a production environment such as
+1. Measure the performance of the code in a production environment such as
GitLab.com (see the [Tooling](#tooling) section below). Performance should be
measured over a period of _at least_ 24 hours.
-3. Add your findings based on the measurement period (screenshots of graphs,
+1. Add your findings based on the measurement period (screenshots of graphs,
timings, etc) to the issue mentioned in step 1.
-4. Solve the problem.
-5. Create a merge request, assign the "Performance" label and assign it to
+1. Solve the problem.
+1. Create a merge request, assign the "Performance" label and assign it to
[@yorickpeterse][yorickpeterse] for reviewing.
-6. Once a change has been deployed make sure to _again_ measure for at least 24
+1. Once a change has been deployed make sure to _again_ measure for at least 24
hours to see if your changes have any impact on the production environment.
-7. Repeat until you're done.
+1. Repeat until you're done.
When providing timings make sure to provide:
@@ -34,16 +34,17 @@ graphs/dashboards.
## Tooling
-GitLab provides built-in tools to aid the process of improving performance:
+GitLab provides built-in tools to help improve performance and availability:
* [Profiling](profiling.md)
* [Sherlock](profiling.md#sherlock)
* [GitLab Performance Monitoring](../administration/monitoring/performance/index.md)
* [Request Profiling](../administration/monitoring/performance/request_profiling.md)
* [QueryRecoder](query_recorder.md) for preventing `N+1` regressions
+* [Chaos endpoints](chaos_endpoints.md) for testing failure scenarios. Intended mainly for testing availability.
GitLab employees can use GitLab.com's performance monitoring systems located at
-<http://performance.gitlab.net>, this requires you to log in using your
+<https://dashboards.gitlab.net>, this requires you to log in using your
`@gitlab.com` Email address. Non-GitLab employees are advised to set up their
own InfluxDB + Grafana stack.
@@ -93,14 +94,14 @@ result of this should be used instead of the `Benchmark` module.
In short:
-1. Don't trust benchmarks you find on the internet.
-2. Never make claims based on just benchmarks, always measure in production to
+- Don't trust benchmarks you find on the internet.
+- Never make claims based on just benchmarks, always measure in production to
confirm your findings.
-3. X being N times faster than Y is meaningless if you don't know what impact it
+- X being N times faster than Y is meaningless if you don't know what impact it
will actually have on your production environment.
-4. A production environment is the _only_ benchmark that always tells the truth
+- A production environment is the _only_ benchmark that always tells the truth
(unless your performance monitoring systems are not set up correctly).
-5. If you must write a benchmark use the benchmark-ips Gem instead of Ruby's
+- If you must write a benchmark use the benchmark-ips Gem instead of Ruby's
`Benchmark` module.
## Profiling
@@ -364,8 +365,7 @@ Depending on the size of the String and how frequently it would be allocated
there's no guarantee it will.
Strings will be frozen by default in Ruby 3.0. To prepare our code base for
-this eventuality, it's a good practice to add the following header to all
-Ruby files:
+this eventuality, we will be adding the following header to all Ruby files:
```ruby
# frozen_string_literal: true
@@ -379,6 +379,9 @@ test = +"hello"
test += " world"
```
+When adding new Ruby files, please check that you can add the above header,
+as omitting it may lead to style check failures.
+
## Anti-Patterns
This is a collection of [anti-patterns][anti-pattern] that should be avoided
diff --git a/doc/development/permissions.md b/doc/development/permissions.md
new file mode 100644
index 00000000000..5d409c9461e
--- /dev/null
+++ b/doc/development/permissions.md
@@ -0,0 +1,63 @@
+# GitLab permissions guide
+
+There are multiple types of permissions across GitLab, and when implementing
+anything that deals with permissions, all of them should be considered.
+
+## Groups and Projects
+
+### General permissions
+
+Groups and projects can have the following visibility levels:
+
+- public (20) - an entity is visible to everyone
+- internal (10) - an entity is visible to logged in users
+- private (0) - an entity is visible only to the approved members of the entity
+
+The visibility level of a group can be changed only if all subgroups and
+subprojects have the same or lower visibility level. (e.g., a group can be set
+to internal only if all subgroups and projects are internal or private).
+
+Visibility levels can be found in the `Gitlab::VisibilityLevel` module.
+
+### Feature specific permissions
+
+Additionally, the following project features can have different visibility levels:
+
+- Issues
+- Repository
+ - Merge Request
+ - Pipelines
+ - Container Registry
+ - Git Large File Storage
+- Wiki
+- Snippets
+
+These features can be set to "Everyone with Access" or "Only Project Members".
+They make sense only for public or internal projects because private projects
+can be accessed only by project members by default.
+
+### Members
+
+Users can be members of multiple groups and projects. The following access
+levels are available (defined in the `Gitlab::Access` module):
+
+- Guest
+- Reporter
+- Developer
+- Maintainer
+- Owner
+
+If a user is the member of both a project and the project parent group, the
+higher permission is taken into account for the project.
+
+If a user is the member of a project, but not the parent group (or groups), they
+can still view the groups and their entities (like epics).
+
+Project membership (where the group membership is already taken into account)
+is stored in the `project_authorizations` table.
+
+### Confidential issues
+
+Confidential issues can be accessed only by project members who are at least
+reporters (they can't be accessed by guests). Additionally they can be accessed
+by their authors and assignees.
diff --git a/doc/development/post_deployment_migrations.md b/doc/development/post_deployment_migrations.md
index cfc91539bee..5986efa9974 100644
--- a/doc/development/post_deployment_migrations.md
+++ b/doc/development/post_deployment_migrations.md
@@ -57,13 +57,13 @@ depends on this column being present while it's running. Normally you'd follow
these steps in such a case:
1. Stop the GitLab instance
-2. Run the migration removing the column
-3. Start the GitLab instance again
+1. Run the migration removing the column
+1. Start the GitLab instance again
Using post deployment migrations we can instead follow these steps:
1. Deploy a new version of GitLab while ignoring post deployment migrations
-2. Re-run `rake db:migrate` but without the environment variable set
+1. Re-run `rake db:migrate` but without the environment variable set
Here we don't need any downtime as the migration takes place _after_ a new
version (which doesn't depend on the column anymore) has been deployed.
diff --git a/doc/development/prometheus_metrics.md b/doc/development/prometheus_metrics.md
new file mode 100644
index 00000000000..b6b6d9665ea
--- /dev/null
+++ b/doc/development/prometheus_metrics.md
@@ -0,0 +1,48 @@
+# Working with Prometheus Metrics
+
+## Adding to the library
+
+We strive to support the 2-4 most important metrics for each common system service that supports Prometheus. If you are looking for support for a particular exporter which has not yet been added to the library, additions can be made [to the `common_metrics.yml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/prometheus/common_metrics.yml) file.
+
+### Query identifier
+
+The requirement for adding a new metric is to make each query to have an unique identifier which is used to update the metric later when changed:
+
+```yaml
+- group: Response metrics (NGINX Ingress)
+ metrics:
+ - title: "Throughput"
+ y_label: "Requests / Sec"
+ queries:
+ - id: response_metrics_nginx_ingress_throughput_status_code
+ query_range: 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) by (status_code)'
+ unit: req / sec
+ label: Status Code
+```
+
+### Update existing metrics
+
+After you add or change existing _common_ metric you have to create a new database migration that will query and update all existing metrics.
+
+NOTE: **Note:**
+If a query metric (which is identified by `id:`) is removed it will not be removed from database by default.
+You might want to add additional database migration that makes a decision what to do with removed one.
+For example: you might be interested in migrating all dependent data to a different metric.
+
+```ruby
+class ImportCommonMetrics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ require Rails.root.join('db/importers/common_metrics_importer.rb')
+
+ DOWNTIME = false
+
+ def up
+ Importers::CommonMetricsImporter.new.execute
+ end
+
+ def down
+ # no-op
+ end
+end
+```
diff --git a/doc/development/query_count_limits.md b/doc/development/query_count_limits.md
index 310e3faf61b..b3ecaf30d8a 100644
--- a/doc/development/query_count_limits.md
+++ b/doc/development/query_count_limits.md
@@ -8,8 +8,8 @@ in test environments we'll raise an error when this threshold is exceeded.
When a test fails because it executes more than 100 SQL queries there are two
solutions to this problem:
-1. Reduce the number of SQL queries that are executed.
-2. Whitelist the controller or API endpoint.
+- Reduce the number of SQL queries that are executed.
+- Whitelist the controller or API endpoint.
You should only resort to whitelisting when an existing controller or endpoint
is to blame as in this case reducing the number of SQL queries can take a lot of
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index fc51b74da1d..2ad748d4802 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -1,6 +1,6 @@
# Rake tasks for developers
-## Setup db with developer seeds
+## Set up db with developer seeds
Note that if your db user does not have advanced privileges you must create the db manually before running this command.
diff --git a/doc/development/reusing_abstractions.md b/doc/development/reusing_abstractions.md
new file mode 100644
index 00000000000..83d7d42bd1f
--- /dev/null
+++ b/doc/development/reusing_abstractions.md
@@ -0,0 +1,182 @@
+# Guidelines for reusing abstractions
+
+As GitLab has grown, different patterns emerged across the codebase. Service
+classes, serializers, and presenters are just a few. These patterns made it easy
+to reuse code, but at the same time make it easy to accidentally reuse the wrong
+abstraction in a particular place.
+
+## Why these guidelines are necessary
+
+Code reuse is good, but sometimes this can lead to shoehorning the wrong
+abstraction into a particular use case. This in turn can have a negative impact
+on maintainability, the ability to easily debug problems, or even performance.
+
+An example would be to use `ProjectsFinder` in `IssuesFinder` to limit issues to
+those belonging to a set of projects. While initially this may seem like a good
+idea, both classes provide a very high level interface with very little control.
+This means that `IssuesFinder` may not be able to produce a better optimised
+database query, as a large portion of the query is controlled by the internals
+of `ProjectsFinder`.
+
+To work around this problem, you would use the same code used by
+`ProjectsFinder`, instead of using `ProjectsFinder` itself directly. This allows
+you to compose your behaviour better, giving you more control over the behaviour
+of the code.
+
+To illustrate, consider the following code from `IssuableFinder#projects`:
+
+```ruby
+return @projects = project if project?
+
+projects =
+ if current_user && params[:authorized_only].presence && !current_user_related?
+ current_user.authorized_projects
+ elsif group
+ finder_options = { include_subgroups: params[:include_subgroups], only_owned: true }
+ GroupProjectsFinder.new(group: group, current_user: current_user, options: finder_options).execute
+ else
+ ProjectsFinder.new(current_user: current_user).execute
+ end
+
+@projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil)
+```
+
+Here we determine what projects to scope our data to, using three different
+approaches. When a group is specified, we use `GroupProjectsFinder` to retrieve
+all the projects of that group. On the surface this seems harmless: it is easy
+to use, and we only need two lines of code.
+
+In reality, things can get hairy very quickly. For example, the query produced
+by `GroupProjectsFinder` may start out simple. Over time more and more
+functionality is added to this (high level) interface. Instead of _only_
+affecting the cases where this is necessary, it may also start affecting
+`IssuableFinder` in a negative way. For example, the query produced by
+`GroupProjectsFinder` may include unnecessary conditions. Since we're using a
+finder here, we can't easily opt-out of that behaviour. We could add options to
+do so, but then we'd need as many options as we have features. Every option adds
+two code paths, which means that for four features we have to cover 8 different
+code paths.
+
+A much more reliable (and pleasant) way of dealing with this, is to simply use
+the underlying bits that make up `GroupProjectsFinder` directly. This means we
+may need a little bit more code in `IssuableFinder`, but it also gives us much
+more control and certainty. This means we might end up with something like this:
+
+```ruby
+return @projects = project if project?
+
+projects =
+ if current_user && params[:authorized_only].presence && !current_user_related?
+ current_user.authorized_projects
+ elsif group
+ current_user
+ .owned_groups(subgroups: params[:include_subgroups])
+ .projects
+ .any_additional_method_calls
+ .that_might_be_necessary
+ else
+ current_user
+ .projects_visible_to_user
+ .any_additional_method_calls
+ .that_might_be_necessary
+ end
+
+@projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil)
+```
+
+This is just a sketch, but it shows the general idea: we would use whatever the
+`GroupProjectsFinder` and `ProjectsFinder` finders use under the hoods.
+
+## End goal
+
+The guidelines in this document are meant to foster _better_ code reuse, by
+clearly defining what can be reused where, and what to do when you can not reuse
+something. Clearly separating abstractions makes it harder to use the wrong one,
+makes it easier to debug the code, and (hopefully) results in fewer performance
+problems.
+
+## Abstractions
+
+Now let's take a look at the various abstraction levels available, and what they
+can (or cannot) reuse. For this we can use the following table, which defines
+the various abstractions and what they can (not) reuse:
+
+| Abstraction | Service classes | Finders | Presenters | Serializers | Model instance method | Model class methods | Active Record | Worker
+|:-----------------------|:-----------------|:---------|:------------|:--------------|:------------------------|:----------------------|:----------------|:--------
+| Controller | Yes | Yes | Yes | Yes | Yes | No | No | No
+| Service class | Yes | Yes | No | No | Yes | No | No | Yes
+| Finder | No | No | No | No | Yes | Yes | No | No
+| Presenter | No | Yes | No | No | Yes | Yes | No | No
+| Serializer | No | Yes | No | No | Yes | Yes | No | No
+| Model class method | No | No | No | No | Yes | Yes | Yes | No
+| Model instance method | No | Yes | No | No | Yes | Yes | Yes | Yes
+| Worker | Yes | Yes | No | No | Yes | No | No | Yes
+
+### Controllers
+
+Everything in `app/controllers`.
+
+Controllers should not do much work on their own, instead they simply pass input
+to other classes and present the results.
+
+### Grape endpoint
+
+Everything in `lib/api`.
+
+### Service classes
+
+Everything that resides in `app/services`.
+
+### Finders
+
+Everything in `app/finders`, typically used for retrieving data from a database.
+
+Finders can not reuse other finders in an attempt to better control the SQL
+queries they produce.
+
+### Presenters
+
+Everything in `app/presenters`, used for exposing complex data to a Rails view,
+without having to create many instance variables.
+
+### Serializers
+
+Everything in `app/serializers`, used for presenting the response to a request,
+typically in JSON.
+
+### Model class methods
+
+These are class methods defined by _GitLab itself_, including the following
+methods provided by Active Record:
+
+* `find`
+* `find_by_id`
+* `delete_all`
+* `destroy`
+* `destroy_all`
+
+Any other methods such as `find_by(some_column: X)` are not included, and
+instead fall under the "Active Record" abstraction.
+
+### Model instance methods
+
+Instance methods defined on Active Record models by _GitLab itself_. Methods
+provided by Active Record are not included, except for the following methods:
+
+* `save`
+* `update`
+* `destroy`
+* `delete`
+
+### Active Record
+
+The API provided by Active Record itself, such as the `where` method, `save`,
+`delete_all`, etc.
+
+### Worker
+
+Everything in `app/workers`.
+
+The scheduling of Sidekiq jobs using `SomeWorker.perform_async`, `perform_in`,
+etc. Directly invoking a worker using `SomeWorker.new.perform` should be avoided
+at all times in application code, though this is fine to use in tests.
diff --git a/doc/development/rolling_out_changes_using_feature_flags.md b/doc/development/rolling_out_changes_using_feature_flags.md
new file mode 100644
index 00000000000..b65fbc9d958
--- /dev/null
+++ b/doc/development/rolling_out_changes_using_feature_flags.md
@@ -0,0 +1,190 @@
+# Rolling out changes using feature flags
+
+[Feature flags](feature_flags.md) can be used to gradually roll out changes, be
+it a new feature, or a performance improvement. By using feature flags, we can
+comfortably measure the impact of our changes, while still being able to easily
+disable those changes, without having to revert an entire release.
+
+## When to use feature flags
+
+Starting with GitLab 11.4, developers are required to use feature flags for
+non-trivial changes. Such changes include:
+
+* New features (e.g. a new merge request widget, epics, etc).
+* Complex performance improvements that may require additional testing in
+ production, such as rewriting complex queries.
+* Invasive changes to the user interface, such as a new navigation bar or the
+ removal of a sidebar.
+* Adding support for importing projects from a third-party service.
+
+In all cases, those working on the changes can best decide if a feature flag is
+necessary. For example, changing the color of a button doesn't need a feature
+flag, while changing the navigation bar definitely needs one. In case you are
+uncertain if a feature flag is necessary, simply ask about this in the merge
+request, and those reviewing the changes will likely provide you with an answer.
+
+When using a feature flag for UI elements, make sure to _also_ use a feature
+flag for the underlying backend code, if there is any. This ensures there is
+absolutely no way to use the feature until it is enabled.
+
+## The cost of feature flags
+
+When reading the above, one might be tempted to think this procedure is going to
+add a lot of work. Fortunately, this is not the case, and we'll show why. For
+this example we'll specify the cost of the work to do as a number, ranging from
+0 to infinity. The greater the number, the more expensive the work is. The cost
+does _not_ translate to time, it's just a way of measuring complexity of one
+change relative to another.
+
+Let's say we are building a new feature, and we have determined that the cost of
+this is 10. We have also determined that the cost of adding a feature flag check
+in a variety of places is 1. If we do not use feature flags, and our feature
+works as intended, our total cost is 10. This however is the best case scenario.
+Optimising for the best case scenario is guaranteed to lead to trouble, whereas
+optimising for the worst case scenario is almost always better.
+
+To illustrate this, let's say our feature causes an outage, and there's no
+immediate way to resolve it. This means we'd have to take the following steps to
+resolve the outage:
+
+1. Revert the release.
+1. Perform any cleanups that might be necessary, depending on the changes that
+ were made.
+1. Revert the commit, ensuring the "master" branch remains stable. This is
+ especially necessary if solving the problem can take days or even weeks.
+1. Pick the revert commit into the appropriate stable branches, ensuring we
+ don't block any future releases until the problem is resolved.
+
+As history has shown, these steps are time consuming, complex, often involve
+many developers, and worst of all: our users will have a bad experience using
+GitLab.com until the problem is resolved.
+
+Now let's say that all of this has an associated cost of 10. This means that in
+the worst case scenario, which we should optimise for, our total cost is now 20.
+
+If we had used a feature flag, things would have been very different. We don't
+need to revert a release, and because feature flags are disabled by default we
+don't need to revert and pick any Git commits. In fact, all we have to do is
+disable the feature, and _maybe_ perform some cleanup. Let's say that the cost
+of this is 1. In this case, our best case cost is 11: 10 to build the feature,
+and 1 to add the feature flag. The worst case cost is now 12: 10 to build the
+feature, 1 to add the feature flag, and 1 to disable it.
+
+Here we can see that in the best case scenario the work necessary is only a tiny
+bit more compared to not using a feature flag. Meanwhile, the process of
+reverting our changes has been made significantly cheaper, to the point of being
+trivial.
+
+In other words, feature flags do not slow down the development process. Instead,
+they speed up the process as managing incidents now becomes _much_ easier. Once
+continuous deployments are easier to perform, the time to iterate on a feature
+is reduced even further, as you no longer need to wait weeks before your changes
+are available on GitLab.com.
+
+## Rolling out changes
+
+The procedure of using feature flags is straightforward, and similar to not
+using them. You add the necessary tests (make sure to test both the on and off
+states of your feature flag(s)), make sure they all pass, have the code
+reviewed, etc. You then submit your merge request, and add the ~"feature flag"
+label. This label is used to signal to release managers that your changes are
+hidden behind a feature flag and that it is safe to pick the MR into a stable
+branch, without the need for an exception request.
+
+When the changes are deployed it is time to start rolling out the feature to our
+users. The exact procedure of rolling out a change is unspecified, as this can
+vary from change to change. However, in general we recommend rolling out changes
+incrementally, instead of enabling them for everybody right away. We also
+recommend you to _not_ enable a feature _before_ the code is being deployed.
+This allows you to separate rolling out a feature from a deploy, making it
+easier to measure the impact of both separately.
+
+GitLab's feature library (using
+[Flipper](https://github.com/jnunemaker/flipper), and covered in the [Feature
+Flags](feature_flags.md) guide) supports rolling out changes to a percentage of
+users. This in turn can be controlled using [GitLab
+chatops](https://docs.gitlab.com/ee/ci/chatops/).
+
+For example, to enable a feature for 25% of all users, run the following in
+Slack:
+
+```
+/chatops run feature set new_navigation_bar 25
+```
+
+This will enable the feature for GitLab.com, with `new_navigation_bar` being the
+name of the feature. We can also enable the feature for <https://dev.gitlab.org>
+or <https://staging.gitlab.com>:
+
+```
+/chatops run feature set new_navigation_bar 25 --dev
+/chatops run feature set new_navigation_bar 25 --staging
+```
+
+If you are not certain what percentages to use, simply use the following steps:
+
+1. 25%
+1. 50%
+1. 75%
+1. 100%
+
+Between every step you'll want to wait a little while and monitor the
+appropriate graphs on <https://dashboards.gitlab.net>. The exact time to wait
+may differ. For some features a few minutes is enough, while for others you may
+want to wait several hours or even days. This is entirely up to you, just make
+sure it is clearly communicated to your team, and the Production team if you
+anticipate any potential problems.
+
+Once a change is deemed stable, submit a new merge request to remove the
+feature flag. This ensures the change is available to all users and self-hosted
+instances. Make sure to add the ~"feature flag" label to this merge request so
+release managers are aware the changes are hidden behind a feature flag. If the
+merge request has to be picked into a stable branch (e.g. after the 7th), make
+sure to also add the appropriate "Pick into X" label (e.g. "Pick into 11.4").
+
+One might be tempted to think this will delay the release of a feature by at
+least one month (= one release). This is not the case. A feature flag does not
+have to stick around for a specific amount of time (e.g. at least one release),
+instead they should stick around until the feature is deemed stable. Stable
+means it works on GitLab.com without causing any problems, such as outages. In
+most cases this will translate to a feature (with a feature flag) being shipped
+in RC1, followed by the feature flag being removed in RC2. This in turn means
+the feature will be stable by the time we publish a stable package around the
+22nd of the month.
+
+## Implicit feature flags
+
+The [`Project#feature_available?`][project-fa],
+[`Namespace#feature_available?`][namespace-fa] (EE), and
+[`License.feature_available?`][license-fa] (EE) methods all implicitly check for
+a feature flag by the same name as the provided argument.
+
+For example if a feature is license-gated, there's no need to add an additional
+explicit feature flag check since the flag will be checked as part of the
+`License.feature_available?` call. Similarly, there's no need to "clean up" a
+feature flag once the feature has reached general availability.
+
+You'd still want to use an explicit `Feature.enabled?` check if your new feature
+isn't gated by a License or Plan.
+
+[project-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/app/models/project_feature.rb#L63-68
+[namespace-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/ee/namespace.rb#L71-85
+[license-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/license.rb#L293-300
+
+### Undefined feature flags default to "on"
+
+An important side-effect of the [implicit feature
+flags][#implicit-feature-flags] mentioned above is that unless the feature is
+explicitly disabled or limited to a percentage of users, the feature flag check
+will default to `true`.
+
+As an example, if you were to ship the backend half of a feature behind a flag,
+you'd want to explicitly disable that flag until the frontend half is also ready
+to be shipped. You can do this via ChatOps:
+
+```
+/chatops run feature set some_feature 0
+```
+
+Note that you can do this at any time, even before the merge request using the
+flag has been merged!
diff --git a/doc/development/sidekiq_debugging.md b/doc/development/sidekiq_debugging.md
index d6d770e27c1..84b61bd7e61 100644
--- a/doc/development/sidekiq_debugging.md
+++ b/doc/development/sidekiq_debugging.md
@@ -3,8 +3,7 @@
## Log arguments to Sidekiq jobs
If you want to see what arguments are being passed to Sidekiq jobs you can set
-the `SIDEKIQ_LOG_ARGUMENTS` [environment variable]
-(https://docs.gitlab.com/omnibus/settings/environment-variables.html) to `1` (true).
+the `SIDEKIQ_LOG_ARGUMENTS` [environment variable](https://docs.gitlab.com/omnibus/settings/environment-variables.html) to `1` (true).
Example:
diff --git a/doc/development/swapping_tables.md b/doc/development/swapping_tables.md
index 6b990ece72c..29cd6a43aff 100644
--- a/doc/development/swapping_tables.md
+++ b/doc/development/swapping_tables.md
@@ -8,8 +8,8 @@ Let's say you want to swap the table "events" with "events_for_migration". In
this case you need to follow 3 steps:
1. Rename "events" to "events_temporary"
-2. Rename "events_for_migration" to "events"
-3. Rename "events_temporary" to "events_for_migration"
+1. Rename "events_for_migration" to "events"
+1. Rename "events_temporary" to "events_for_migration"
Rails allows you to do this using the `rename_table` method:
diff --git a/doc/development/switching_to_rails5.md b/doc/development/switching_to_rails5.md
new file mode 100644
index 00000000000..c9a4ce1a1d1
--- /dev/null
+++ b/doc/development/switching_to_rails5.md
@@ -0,0 +1,27 @@
+# Switching to Rails 5
+
+GitLab switched recently to Rails 5. This is a big change (especially for backend development) and it introduces couple of temporary inconveniences.
+
+## After the switch, I found a broken feature. What do I do?
+
+Many fixes and tweaks were done to make our codebase compatible with Rails 5, but it's possible that not all issues were found. If you find an bug, please create an issue and assign it the ~rails5 label.
+
+## It takes much longer to run CI pipelines that build GitLab. Why?
+
+We are temporarily running CI pipelines with Rails 4 and 5 so that we ensure we remain compatible with Rails 4 in case we must revert back to Rails 4 from Rails 5 (this can double the duration of CI pipelines).
+
+We might revert back to Rails 4 if we found a major issue we were unable to quickly fix.
+
+Once we are sure we can stay with Rails 5, we will stop running CI pipelines with Rails 4.
+
+## Can I skip running Rails 4 tests?
+
+If you are sure that your merge request doesn't introduce any incompatibility, you can just include `norails4` anywhere in your branch name and Rails 4 tests will be skipped.
+
+## CI is failing on my test with Rails 4. How can I debug it?
+
+You can run specs locally with Rails 4 using the following command:
+
+```sh
+BUNDLE_GEMFILE=Gemfile.rails4 RAILS5=0 bundle exec rspec ...
+```
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index acbfa1850b4..7727bd74c3c 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -209,6 +209,130 @@ it 'is overdue' do
end
```
+### Pristine test environments
+
+The code exercised by a single GitLab test may access and modify many items of
+data. Without careful preparation before a test runs, and cleanup afterward,
+data can be changed by a test in such a way that it affects the behaviour of
+following tests. This should be avoided at all costs! Fortunately, the existing
+test framework handles most cases already.
+
+When the test environment does get polluted, a common outcome is
+[flaky tests](flaky_tests.md). Pollution will often manifest as an order
+dependency: running spec A followed by spec B will reliably fail, but running
+spec B followed by spec A will reliably succeed. In these cases, you can use
+`rspec --bisect` (or a manual pairwise bisect of spec files) to determine which
+spec is at fault. Fixing the problem requires some understanding of how the test
+suite ensures the environment is pristine. Read on to discover more about each
+data store!
+
+#### SQL database
+
+This is managed for us by the `database_cleaner` gem. Each spec is surrounded in
+a transaction, which is rolled back once the test completes. Certain specs will
+instead issue `DELETE FROM` queries against every table after completion; this
+allows the created rows to be viewed from multiple database connections, which
+is important for specs that run in a browser, or migration specs, among others.
+
+One consequence of using these strategies, instead of the well-known
+`TRUNCATE TABLES` approach, is that primary keys and other sequences are **not**
+reset across specs. So if you create a project in spec A, then create a project
+in spec B, the first will have `id=1`, while the second will have `id=2`.
+
+This means that specs should **never** rely on the value of an ID, or any other
+sequence-generated column. To avoid accidental conflicts, specs should also
+avoid manually specifying any values in these kinds of columns. Instead, leave
+them unspecified, and look up the value after the row is created.
+
+#### Redis
+
+GitLab stores two main categories of data in Redis: cached items, and sidekiq
+jobs.
+
+In most specs, the Rails cache is actually an in-memory store. This is replaced
+between specs, so calls to `Rails.cache.read` and `Rails.cache.write` are safe.
+However, if a spec makes direct Redis calls, it should mark itself with the
+`:clean_gitlab_redis_cache`, `:clean_gitlab_redis_shared_state` or
+`:clean_gitlab_redis_queues` traits as appropriate.
+
+Sidekiq jobs are typically not run in specs, but this behaviour can be altered
+in each spec through the use of `Sidekiq::Testing.inline!` blocks. Any spec that
+causes Sidekiq jobs to be pushed to Redis should use the `:sidekiq` trait, to
+ensure that they are removed once the spec completes.
+
+#### Filesystem
+
+Filesystem data can be roughly split into "repositories", and "everything else".
+Repositories are stored in `tmp/tests/repositories`. This directory is emptied
+before a test run starts, and after the test run ends. It is not emptied between
+specs, so created repositories accumulate within this directory over the
+lifetime of the process. Deleting them is expensive, but this could lead to
+pollution unless carefully managed.
+
+To avoid this, [hashed storage](../../administration/repository_storage_types.md)
+is enabled in the test suite. This means that repositories are given a unique
+path that depends on their project's ID. Since the project IDs are not reset
+between specs, this guarantees that each spec gets its own repository on disk,
+and prevents changes from being visible between specs.
+
+If a spec manually specifies a project ID, or inspects the state of the
+`tmp/tests/repositories/` directory directly, then it should clean up the
+directory both before and after it runs. In general, these patterns should be
+completely avoided.
+
+Other classes of file linked to database objects, such as uploads, are generally
+managed in the same way. With hashed storage enabled in the specs, they are
+written to disk in locations determined by ID, so conflicts should not occur.
+
+Some specs disable hashed storage by passing the `:legacy_storage` trait to the
+`projects` factory. Specs that do this must **never** override the `path` of the
+project, or any of its groups. The default path includes the project ID, so will
+not conflict; but if two specs create a `:legacy_storage` project with the same
+path, they will use the same repository on disk and lead to test environment
+pollution.
+
+Other files must be managed manually by the spec. If you run code that creates a
+`tmp/test-file.csv` file, for instance, the spec must ensure that the file is
+removed as part of cleanup.
+
+#### Persistent in-memory application state
+
+All the specs in a given `rspec` run share the same Ruby process, which means
+they can affect each other by modifying Ruby objects that are accessible between
+specs. In practice, this means global variables, and constants (which includes
+Ruby classes, modules, etc).
+
+Global variables should generally not be modified. If absolutely necessary, a
+block like this can be used to ensure the change is rolled back afterwards:
+
+```ruby
+around(:each) do |example|
+ old_value = $0
+
+ begin
+ $0 = "new-value"
+ example.run
+ ensure
+ $0 = old_value
+ end
+end
+```
+
+If a spec needs to modify a constant, it should use the `stub_const` helper to
+ensure the change is rolled back.
+
+If you need to modify the contents of the `ENV` constant, you can use the
+`stub_env` helper method instead.
+
+While most Ruby **instances** are not shared between specs, **classes**
+and **modules** generally are. Class and module instance variables, accessors,
+class variables, and other stateful idioms, should be treated in the same way as
+global variables - don't modify them unless you have to! In particular, prefer
+using expectations, or dependency injection along with stubs, to avoid the need
+for modifications. If you have no other choice, an `around` block similar to the
+example for global variables, above, can be used, but this should be avoided if
+at all possible.
+
### Table-based / Parameterized tests
This style of testing is used to exercise one piece of code with a comprehensive
@@ -348,6 +472,37 @@ GitLab uses [factory_bot] as a test fixture replacement.
All fixtures should be be placed under `spec/fixtures/`.
+### Repositories
+
+Testing some functionality, e.g., merging a merge request, requires a git
+repository with a certain state to be present in the test environment. GitLab
+maintains the [gitlab-test](https://gitlab.com/gitlab-org/gitlab-test)
+repository for certain common cases - you can ensure a copy of the repository is
+used with the `:repository` trait for project factories:
+
+```ruby
+let(:project) { create(:project, :repository) }
+```
+
+Where you can, consider using the `:custom_repo` trait instead of `:repository`.
+This allows you to specify exactly what files will appear in the `master` branch
+of the project's repository. For example:
+
+```ruby
+let(:project) do
+ create(
+ :project, :custom_repo,
+ files: {
+ 'README.md' => 'Content here',
+ 'foo/bar/baz.txt' => 'More content here'
+ }
+ )
+end
+```
+
+This will create a repository containing two files, with default permissions and
+the specified content.
+
### Config
RSpec config files are files that change the RSpec config (i.e.
diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md
index 0d8e150e090..8d9706a9501 100644
--- a/doc/development/testing_guide/ci.md
+++ b/doc/development/testing_guide/ci.md
@@ -5,23 +5,23 @@
Our current CI parallelization setup is as follows:
1. The `knapsack` job in the prepare stage that is supposed to ensure we have a
- `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file:
- - The `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file is fetched
- from S3, if it's not here we initialize the file with `{}`.
+ `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file:
+ - The `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file is fetched
+ from S3, if it's not here we initialize the file with `{}`.
1. Each `rspec x y` job are run with `knapsack rspec` and should have an evenly
- distributed share of tests:
- - It works because the jobs have access to the
- `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` since the "artifacts
- from all previous stages are passed by default". [^1]
- - the jobs set their own report path to
- `KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`.
- - if knapsack is doing its job, test files that are run should be listed under
- `Report specs`, not under `Leftover specs`.
+ distributed share of tests:
+ - It works because the jobs have access to the
+ `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` since the "artifacts
+ from all previous stages are passed by default".
+ - the jobs set their own report path to
+ `KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`.
+ - if knapsack is doing its job, test files that are run should be listed under
+ `Report specs`, not under `Leftover specs`.
1. The `update-knapsack` job takes all the
- `knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`
- files from the `rspec x y` jobs and merge them all together into a single
- `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file that is then
- uploaded to S3.
+ `knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`
+ files from the `rspec x y` jobs and merge them all together into a single
+ `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file that is then
+ uploaded to S3.
After that, the next pipeline will use the up-to-date
`knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file.
diff --git a/doc/development/testing_guide/end_to_end_tests.md b/doc/development/testing_guide/end_to_end_tests.md
index 21ec926414d..e9f236c6b3a 100644
--- a/doc/development/testing_guide/end_to_end_tests.md
+++ b/doc/development/testing_guide/end_to_end_tests.md
@@ -1,32 +1,37 @@
-# End-to-End Testing
+# End-to-end Testing
-## What is End-to-End testing?
+## What is end-to-end testing?
-End-to-End testing is a strategy used to check whether your application works
-as expected across entire software stack and architecture, including
-integration of all microservices and components that are supposed to work
+End-to-end testing is a strategy used to check whether your application works
+as expected across the entire software stack and architecture, including
+integration of all micro-services and components that are supposed to work
together.
## How do we test GitLab?
We use [Omnibus GitLab][omnibus-gitlab] to build GitLab packages and then we
-test these packages using [GitLab QA][gitlab-qa] project, which is entirely
-black-box, click-driven testing framework.
+test these packages using the [GitLab QA orchestrator][gitlab-qa] tool, which is
+a black-box testing framework for the API and the UI.
### Testing nightly builds
We run scheduled pipeline each night to test nightly builds created by Omnibus.
-You can find these nightly pipelines at [GitLab QA pipelines page][gitlab-qa-pipelines].
+You can find these nightly pipelines at [gitlab-org/quality/nightly/pipelines][quality-nightly-pipelines].
+
+### Testing staging
+
+We run scheduled pipeline each night to test staging.
+You can find these nightly pipelines at [gitlab-org/quality/staging/pipelines][quality-staging-pipelines].
### Testing code in merge requests
It is possible to run end-to-end tests (eventually being run within a
[GitLab QA pipeline][gitlab-qa-pipelines]) for a merge request by triggering
-the `package-and-qa` manual action, that should be present in a merge request
-widget.
+the `package-and-qa` manual action in the `test` stage, that should be present
+in a merge request widget (unless the merge request is from a fork).
Manual action that starts end-to-end tests is also available in merge requests
-in Omnibus GitLab project.
+in [Omnibus GitLab][omnibus-gitlab].
Below you can read more about how to use it and how does it work.
@@ -35,46 +40,56 @@ Below you can read more about how to use it and how does it work.
Currently, we are using _multi-project pipeline_-like approach to run QA
pipelines.
-1. Developer triggers a manual action, that can be found in CE and EE merge
+1. Developer triggers a manual action, that can be found in CE / EE merge
requests. This starts a chain of pipelines in multiple projects.
-1. The script being executed triggers a pipeline in GitLab Omnibus and waits
-for the resulting status. We call this a _status attribution_.
+1. The script being executed triggers a pipeline in [Omnibus GitLab][omnibus-gitlab]
+and waits for the resulting status. We call this a _status attribution_.
-1. GitLab packages are being built in Omnibus pipeline. Packages are going to be
-pushed to Container Registry.
+1. GitLab packages are being built in the [Omnibus GitLab][omnibus-gitlab]
+pipeline. Packages are then pushed to its Container Registry.
1. When packages are ready, and available in the registry, a final step in the
-pipeline, that is now running in Omnibus, triggers a new pipeline in the GitLab
-QA project. It also waits for a resulting status.
+[Omnibus GitLab][omnibus-gitlab] pipeline, triggers a new
+[GitLab QA pipeline][gitlab-qa-pipelines]. It also waits for a resulting status.
1. GitLab QA pulls images from the registry, spins-up containers and runs tests
against a test environment that has been just orchestrated by the `gitlab-qa`
tool.
-1. The result of the GitLab QA pipeline is being propagated upstream, through
-Omnibus, back to CE / EE merge request.
+1. The result of the [GitLab QA pipeline][gitlab-qa-pipelines] is being
+propagated upstream, through Omnibus, back to the CE / EE merge request.
#### How do I write tests?
In order to write new tests, you first need to learn more about GitLab QA
-architecture. See the [documentation about it][gitlab-qa-architecture] in
-GitLab QA project.
+architecture. See the [documentation about it][gitlab-qa-architecture].
-Once you decided where to put test environment orchestration scenarios and
-instance specs, take a look at the [relevant documentation][instance-qa-readme]
-and examples in [the `qa/` directory][instance-qa-examples].
+Once you decided where to put [test environment orchestration scenarios] and
+[instance-level scenarios], take a look at the [GitLab QA README][instance-qa-readme],
+the [GitLab QA orchestrator README][gitlab-qa-readme], and [the already existing
+instance-level scenarios][instance-level scenarios].
## Where can I ask for help?
You can ask question in the `#quality` channel on Slack (GitLab internal) or
you can find an issue you would like to work on in
-[the issue tracker][gitlab-qa-issues] and start a new discussion there.
+[the `gitlab-ce` issue tracker][gitlab-ce-issues],
+[the `gitlab-ee` issue tracker][gitlab-ce-issues], or
+[the `gitlab-qa` issue tracker][gitlab-qa-issues].
[omnibus-gitlab]: https://gitlab.com/gitlab-org/omnibus-gitlab
[gitlab-qa]: https://gitlab.com/gitlab-org/gitlab-qa
+[gitlab-qa-readme]: https://gitlab.com/gitlab-org/gitlab-qa/tree/master/README.md
[gitlab-qa-pipelines]: https://gitlab.com/gitlab-org/gitlab-qa/pipelines
+[quality-nightly-pipelines]: https://gitlab.com/gitlab-org/quality/nightly/pipelines
+[quality-staging-pipelines]: https://gitlab.com/gitlab-org/quality/staging/pipelines
[gitlab-qa-architecture]: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/architecture.md
-[gitlab-qa-issues]: https://gitlab.com/gitlab-org/gitlab-qa/issues
+[gitlab-qa-issues]: https://gitlab.com/gitlab-org/gitlab-qa/issues?label_name%5B%5D=new+scenario
+[gitlab-ce-issues]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name[]=QA&label_name[]=test
+[gitlab-ee-issues]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name[]=QA&label_name[]=test
+[test environment orchestration scenarios]: https://gitlab.com/gitlab-org/gitlab-qa/tree/master/lib/gitlab/qa/scenario
+[instance-level scenarios]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa/qa/specs/features
+[Page objects documentation]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa/qa/page/README.md
[instance-qa-readme]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa/README.md
[instance-qa-examples]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa/qa
diff --git a/doc/development/testing_guide/index.md b/doc/development/testing_guide/index.md
index 0cd63a54b55..67e4cfeda0e 100644
--- a/doc/development/testing_guide/index.md
+++ b/doc/development/testing_guide/index.md
@@ -59,6 +59,12 @@ parallelization, monitoring.
---
+## [Review apps](review_apps.md)
+
+How review apps are set up for GitLab CE/EE and how to use them.
+
+---
+
## [Testing Rake tasks](testing_rake_tasks.md)
Everything you should know about how to test Rake tasks.
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
new file mode 100644
index 00000000000..1830641431e
--- /dev/null
+++ b/doc/development/testing_guide/review_apps.md
@@ -0,0 +1,91 @@
+# Review Apps
+
+Review Apps are automatically deployed by each pipeline, both in
+[CE](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22010) and
+[EE](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6665).
+
+## How does it work?
+
+1. On every [pipeline][gitlab-pipeline] during the `test` stage, the
+ [`review` job][review-job] is automatically started.
+1. The `review` job [triggers a pipeline][cng-pipeline] in the
+ [`CNG-mirror`][cng-mirror] project.
+ - We use the `CNG-mirror` project so that the `CNG`, (**C**loud **N**ative
+ **G**itLab), project's registry is not overloaded with a lot of transient
+ Docker images.
+1. The `CNG-mirror` pipeline creates the Docker images of each component (e.g.
+ `gitlab-rails-ee`, `gitlab-shell`, `gitaly` etc.) based on the commit from the
+ [GitLab pipeline][gitlab-pipeline] and store them in its
+ [registry][cng-mirror-registry].
+1. Once all images are built, the Review App is deployed using
+ [the official GitLab Helm chart][helm-chart] to the
+ [`review-apps-ee` Kubernetes cluster on GCP][review-apps-ee]
+ - The actual scripts used to deploy the Review App can be found at
+ [`scripts/review_apps/review-apps.sh`][review-apps.sh]
+ - These scripts are basically
+ [our official Auto DevOps scripts][Auto-DevOps.gitlab-ci.yml] where the
+ default CNG images are overridden with the images built and stored in the
+ [`CNG-mirror` project's registry][cng-mirror-registry].
+ - Since we're using [the official GitLab Helm chart][helm-chart], this means
+ you get a dedicated environment for your branch that's very close to what it
+ would look in production.
+1. Once the `review` job succeeds, you should be able to use your Review App
+ thanks to the direct link to it from the MR widget. The default username is
+ `root` and its password can be found in the 1Password secure note named
+ **gitlab-{ce,ee} Review App's root password** (note that there's currently
+ [a bug where the default password seems to be overridden][password-bug]).
+
+**Additional notes:**
+
+- The Kubernetes cluster is connected to the `gitlab-{ce,ee}` projects using
+ [GitLab's Kubernetes integration][gitlab-k8s-integration]. This basically
+ allows to have a link to the Review App directly from the merge request widget.
+- The manual `stop_review` in the `test` stage can be used to stop a Review App
+ manually, and is also started by GitLab once a branch is deleted.
+- Review Apps are cleaned up regularly using a pipeline schedule that runs
+ the [`scripts/review_apps/automated_cleanup.rb`][automated_cleanup.rb] script.
+- If the Review App deployment fails, you can simply retry it (there's no need
+ to run the `stop_review` job first).
+- If you're unable to log in using the `root` username and password, you may
+ encounter [this bug][password-bug]. Stop the Review App via the `stop_review`
+ manual job and then retry the `review` job to redeploy the Review App.
+
+## Frequently Asked Questions
+
+**Isn't it too much to trigger CNG image builds on every test run? This creates
+thousands of unused Docker images.**
+
+ > We have to start somewhere and improve later. Also, we're using the
+ CNG-mirror project to store these Docker images so that we can just wipe out
+ the registry at some point, and use a new fresh, empty one.
+
+**How big are the Kubernetes clusters (`review-apps-ce` and `review-apps-ee`)?**
+
+ > The clusters are currently set up with a single pool of preemptible nodes,
+ with a minimum of 1 node and a maximum of 100 nodes.
+
+**What are the machine running on the cluster?**
+
+ > We're currently using `n1-standard-4` (4 vCPUs, 15 GB memory) machines.
+
+**How do we secure this from abuse? Apps are open to the world so we need to
+find a way to limit it to only us.**
+
+ > This isn't enabled for forks.
+
+[gitlab-pipeline]: https://gitlab.com/gitlab-org/gitlab-ce/pipelines/35850709
+[review-job]: https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/118076368
+[cng-mirror]: https://gitlab.com/gitlab-org/build/CNG-mirror
+[cng-pipeline]: https://gitlab.com/gitlab-org/build/CNG-mirror/pipelines/35883435
+[cng-mirror-registry]: https://gitlab.com/gitlab-org/build/CNG-mirror/container_registry
+[helm-chart]: https://gitlab.com/charts/gitlab/
+[review-apps-ee]: https://console.cloud.google.com/kubernetes/clusters/details/us-central1-b/review-apps-ee?project=gitlab-review-apps
+[review-apps.sh]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/review_apps/review-apps.sh
+[automated_cleanup.rb]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/review_apps/automated_cleanup.rb
+[Auto-DevOps.gitlab-ci.yml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+[gitlab-k8s-integration]: https://docs.gitlab.com/ee/user/project/clusters/index.html
+[password-bug]: https://gitlab.com/gitlab-org/gitlab-ce/issues/53621
+
+---
+
+[Return to Testing documentation](index.md)
diff --git a/doc/development/testing_guide/smoke.md b/doc/development/testing_guide/smoke.md
new file mode 100644
index 00000000000..3360031c220
--- /dev/null
+++ b/doc/development/testing_guide/smoke.md
@@ -0,0 +1,19 @@
+# Smoke Tests
+
+It is imperative in any testing suite that we have Smoke Tests. In short, smoke
+tests will run quick sanity end-to-end functional tests from GitLab QA and are
+designed to run against the specified environment to ensure that basic
+functionality is working.
+
+Currently, our suite consists of this basic functionality coverage:
+
+- User Login (Standard Auth)
+- Project Creation
+- Issue Creation
+- Merge Request Creation
+
+Smoke tests have the `:smoke` RSpec metadata.
+
+---
+
+[Return to Testing documentation](index.md)
diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md
index 07ced36f0c1..a8671fc3aa3 100644
--- a/doc/development/testing_guide/testing_levels.md
+++ b/doc/development/testing_guide/testing_levels.md
@@ -34,7 +34,11 @@ records should use stubs/doubles as much as possible.
Formal definition: https://en.wikipedia.org/wiki/Integration_testing
-These kind of tests ensure that individual parts of the application work well together, without the overhead of the actual app environment (i.e. the browser). These tests should assert at the request/response level: status code, headers, body. They're useful to test permissions, redirections, what view is rendered etc.
+These kind of tests ensure that individual parts of the application work well
+together, without the overhead of the actual app environment (i.e. the browser).
+These tests should assert at the request/response level: status code, headers,
+body.
+They're useful to test permissions, redirections, what view is rendered etc.
| Code path | Tests path | Testing engine | Notes |
| --------- | ---------- | -------------- | ----- |
@@ -67,20 +71,40 @@ run JavaScript tests, so you can either run unit tests (e.g. test a single
JavaScript method), or integration tests (e.g. test a component that is composed
of multiple components).
-## System tests or feature tests
+## White-box tests at the system level (formerly known as System / Feature tests)
-Formal definition: https://en.wikipedia.org/wiki/System_testing.
+Formal definitions:
-These kind of tests ensure the application works as expected from a user point
-of view (aka black-box testing). These tests should test a happy path for a
-given page or set of pages, and a test case should be added for any regression
+- https://en.wikipedia.org/wiki/System_testing
+- https://en.wikipedia.org/wiki/White-box_testing
+
+These kind of tests ensure the GitLab *Rails* application (i.e.
+`gitlab-ce`/`gitlab-ee`) works as expected from a *browser* point of view.
+
+Note that:
+
+- knowledge of the internals of the application are still required
+- data needed for the tests are usually created directly using RSpec factories
+- expectations are often set on the database or objects state
+
+These tests should only be used when:
+
+- the functionality/component being tested is small
+- the internal state of the objects/database *needs* to be tested
+- it cannot be tested at a lower level
+
+For instance, to test the breadcrumbs on a given page, writing a system test
+makes sense since it's a small component, which cannot be tested at the unit or
+controller level.
+
+Only test the happy path, but make sure to add a test case for any regression
that couldn't have been caught at lower levels with better tests (i.e. if a
regression is found, regression tests should be added at the lowest-level
possible).
| Tests path | Testing engine | Notes |
| ---------- | -------------- | ----- |
-| `spec/features/` | [Capybara] + [RSpec] | If your spec has the `:js` metadata, the browser driver will be [Poltergeist], otherwise it's using [RackTest]. |
+| `spec/features/` | [Capybara] + [RSpec] | If your test has the `:js` metadata, the browser driver will be [Poltergeist], otherwise it's using [RackTest]. |
### Consider **not** writing a system test!
@@ -89,7 +113,7 @@ we have enough Unit & Integration tests), we shouldn't need to duplicate their
thorough testing at the System test level.
It's very easy to add tests, but a lot harder to remove or improve tests, so one
-should take care of not introducing too many (slow and duplicated) specs.
+should take care of not introducing too many (slow and duplicated) tests.
The reasons why we should follow these best practices are as follows:
@@ -107,21 +131,33 @@ The reasons why we should follow these best practices are as follows:
[Poltergeist]: https://github.com/teamcapybara/capybara#poltergeist
[RackTest]: https://github.com/teamcapybara/capybara#racktest
-## Black-box tests or end-to-end tests
+## Black-box tests at the system level, aka end-to-end tests
+
+Formal definitions:
+
+- https://en.wikipedia.org/wiki/System_testing
+- https://en.wikipedia.org/wiki/Black-box_testing
GitLab consists of [multiple pieces] such as [GitLab Shell], [GitLab Workhorse],
[Gitaly], [GitLab Pages], [GitLab Runner], and GitLab Rails. All theses pieces
are configured and packaged by [GitLab Omnibus].
-[GitLab QA] is a tool that allows to test that all these pieces integrate well
-together by building a Docker image for a given version of GitLab Rails and
-running feature tests (i.e. using Capybara) against it.
+The QA framework and instance-level scenarios are [part of GitLab Rails] so that
+they're always in-sync with the codebase (especially the views).
+
+Note that:
+
+- knowledge of the internals of the application are not required
+- data needed for the tests can only be created using the GUI or the API
+- expectations can only be made against the browser page and API responses
-The actual test scenarios and steps are [part of GitLab Rails] so that they're
-always in-sync with the codebase.
+Every new feature should come with a [test plan].
-Read a separate document about [end-to-end tests](end_to_end_tests.md) to
-learn more.
+| Tests path | Testing engine | Notes |
+| ---------- | -------------- | ----- |
+| `qa/qa/specs/features/` | [Capybara] + [RSpec] + Custom QA framework | Tests should be placed under their corresponding [Product category] |
+
+> See [end-to-end tests](end_to_end_tests.md) for more information.
[multiple pieces]: ../architecture.md#components
[GitLab Shell]: https://gitlab.com/gitlab-org/gitlab-shell
@@ -130,8 +166,29 @@ learn more.
[GitLab Pages]: https://gitlab.com/gitlab-org/gitlab-pages
[GitLab Runner]: https://gitlab.com/gitlab-org/gitlab-runner
[GitLab Omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab
-[GitLab QA]: https://gitlab.com/gitlab-org/gitlab-qa
[part of GitLab Rails]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa
+[test plan]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/.gitlab/issue_templates/Test%20plan.md
+[Product category]: https://about.gitlab.com/handbook/product/categories/
+
+### Smoke tests
+
+Smoke tests are quick tests that may be run at any time (especially after the
+pre-deployment migrations).
+
+These tests run against the UI and ensure that basic functionality is working.
+
+> See [Smoke Tests](smoke.md) for more information.
+
+### GitLab QA orchestrator
+
+[GitLab QA orchestrator] is a tool that allows to test that all these pieces
+integrate well together by building a Docker image for a given version of GitLab
+Rails and running end-to-end tests (i.e. using Capybara) against it.
+
+Learn more in the [GitLab QA orchestrator README][gitlab-qa-readme].
+
+[GitLab QA orchestrator]: https://gitlab.com/gitlab-org/gitlab-qa
+[gitlab-qa-readme]: https://gitlab.com/gitlab-org/gitlab-qa/tree/master/README.md
## EE-specific tests
@@ -145,7 +202,7 @@ trade-off:
- Unit tests are usually cheap, and you should consider them like the basement
of your house: you need them to be confident that your code is behaving
correctly. However if you run only unit tests without integration / system
- tests, you might [miss] the [big] [picture]!
+ tests, you might [miss] the [big] / [picture] !
- Integration tests are a bit more expensive, but don't abuse them. A system test
is often better than an integration test that is stubbing a lot of internals.
- System tests are expensive (compared to unit tests), even more if they require
diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md
index df6ac452300..dd206bb2ae9 100644
--- a/doc/development/ui_guide.md
+++ b/doc/development/ui_guide.md
@@ -54,12 +54,12 @@ information from database or file system
When exporting SVGs, be sure to follow the following guidelines:
-1. Convert all strokes to outlines.
-2. Use pathfinder tools to combine overlapping paths and create compound paths.
-3. SVGs that are limited to one color should be exported without a fill color so the color can be set using CSS.
-4. Ensure that exported SVGs have been run through an [SVG cleaner](https://github.com/RazrFalcon/SVGCleaner) to remove unused elements and attributes.
+- Convert all strokes to outlines.
+- Use pathfinder tools to combine overlapping paths and create compound paths.
+- SVGs that are limited to one color should be exported without a fill color so the color can be set using CSS.
+- Ensure that exported SVGs have been run through an [SVG cleaner](https://github.com/RazrFalcon/SVGCleaner) to remove unused elements and attributes.
-You can open your svg in a text editor to ensure that it is clean.
+You can open your svg in a text editor to ensure that it is clean.
Incorrect files will look like this:
```xml
diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md
new file mode 100644
index 00000000000..adf8795a5e3
--- /dev/null
+++ b/doc/development/understanding_explain_plans.md
@@ -0,0 +1,676 @@
+# Understanding EXPLAIN plans
+
+PostgreSQL allows you to obtain query plans using the `EXPLAIN` command. This
+command can be invaluable when trying to determine how a query will perform.
+You can use this command directly in your SQL query, as long as the query starts
+with it:
+
+```sql
+EXPLAIN
+SELECT COUNT(*)
+FROM projects
+WHERE visibility_level IN (0, 20);
+```
+
+When running this on GitLab.com, we are presented with the following output:
+
+```
+Aggregate (cost=922411.76..922411.77 rows=1 width=8)
+ -> Seq Scan on projects (cost=0.00..908044.47 rows=5746914 width=0)
+ Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+```
+
+When using _just_ `EXPLAIN`, PostgreSQL won't actually execute our query,
+instead it produces an _estimated_ execution plan based on the available
+statistics. This means the actual plan can differ quite a bit. Fortunately,
+PostgreSQL provides us with the option to execute the query as well. To do so,
+we need to use `EXPLAIN ANALYZE` instead of just `EXPLAIN`:
+
+```sql
+EXPLAIN ANALYZE
+SELECT COUNT(*)
+FROM projects
+WHERE visibility_level IN (0, 20);
+```
+
+This will produce:
+
+```
+Aggregate (cost=922420.60..922420.61 rows=1 width=8) (actual time=3428.535..3428.535 rows=1 loops=1)
+ -> Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1)
+ Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+ Rows Removed by Filter: 65677
+Planning time: 2.861 ms
+Execution time: 3428.596 ms
+```
+
+As we can see this plan is quite different, and includes a lot more data. Let's
+discuss this step by step.
+
+Because `EXPLAIN ANALYZE` executes the query, care should be taken when using a
+query that will write data or might time out. If the query modifies data,
+consider wrapping it in a transaction that rolls back automatically like so:
+
+```sql
+BEGIN;
+EXPLAIN ANALYZE
+DELETE FROM users WHERE id = 1;
+ROLLBACK;
+```
+
+The `EXPLAIN` command also takes additional options, such as `BUFFERS`:
+
+```sql
+EXPLAIN (ANALYZE, BUFFERS)
+SELECT COUNT(*)
+FROM projects
+WHERE visibility_level IN (0, 20);
+```
+
+This will then produce:
+
+```
+Aggregate (cost=922420.60..922420.61 rows=1 width=8) (actual time=3428.535..3428.535 rows=1 loops=1)
+ Buffers: shared hit=208846
+ -> Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1)
+ Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+ Rows Removed by Filter: 65677
+ Buffers: shared hit=208846
+Planning time: 2.861 ms
+Execution time: 3428.596 ms
+```
+
+For more information, refer to the official [EXPLAIN
+documentation](https://www.postgresql.org/docs/current/static/sql-explain.html).
+
+## Nodes
+
+Every query plan consists of nodes. Nodes can be nested, and are executed from
+the inside out. This means that the innermost node is executed before an outer
+node. This can be best thought of as nested function calls, returning their
+results as they unwind. For example, a plan starting with an `Aggregate`
+followed by a `Nested Loop`, followed by an `Index Only scan` can be thought of
+as the following Ruby code:
+
+```ruby
+aggregate(
+ nested_loop(
+ index_only_scan()
+ index_only_scan()
+ )
+)
+```
+
+Nodes are indicated using a `->` followed by the type of node taken. For
+example:
+
+```
+Aggregate (cost=922411.76..922411.77 rows=1 width=8)
+ -> Seq Scan on projects (cost=0.00..908044.47 rows=5746914 width=0)
+ Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+```
+
+Here the first node executed is `Seq scan on projects`. The `Filter:` is an
+additional filter applied to the results of the node. A filter is very similar
+to Ruby's `Array#select`: it takes the input rows, applies the filter, and
+produces a new list of rows. Once the node is done, we perform the `Aggregate`
+above it.
+
+Nested nodes will look like this:
+
+```
+Aggregate (cost=176.97..176.98 rows=1 width=8) (actual time=0.252..0.252 rows=1 loops=1)
+ Buffers: shared hit=155
+ -> Nested Loop (cost=0.86..176.75 rows=87 width=0) (actual time=0.035..0.249 rows=36 loops=1)
+ Buffers: shared hit=155
+ -> Index Only Scan using users_pkey on users users_1 (cost=0.43..4.95 rows=87 width=4) (actual time=0.029..0.123 rows=36 loops=1)
+ Index Cond: (id < 100)
+ Heap Fetches: 0
+ -> Index Only Scan using users_pkey on users (cost=0.43..1.96 rows=1 width=4) (actual time=0.003..0.003 rows=1 loops=36)
+ Index Cond: (id = users_1.id)
+ Heap Fetches: 0
+Planning time: 2.585 ms
+Execution time: 0.310 ms
+```
+
+Here we first perform two separate "Index Only" scans, followed by performing a
+"Nested Loop" on the result of these two scans.
+
+## Node statistics
+
+Each node in a plan has a set of associated statistics, such as the cost, the
+number of rows produced, the number of loops performed, and more. For example:
+
+```
+Seq Scan on projects (cost=0.00..908044.47 rows=5746914 width=0)
+```
+
+Here we can see that our cost ranges from `0.00..908044.47` (we'll cover this in
+a moment), and we estimate (since we're using `EXPLAIN` and not `EXPLAIN
+ANALYZE`) a total of 5,746,914 rows to be produced by this node. The `width`
+statistics describes the estimated width of each row, in bytes.
+
+The `costs` field specifies how expensive a node was. The cost is measured in
+arbitrary units determined by the query planner's cost parameters. What
+influences the costs depends on a variety of settings, such as `seq_page_cost`,
+`cpu_tuple_cost`, and various others.
+The format of the costs field is as follows:
+
+```
+STARTUP COST..TOTAL COST
+```
+
+The startup cost states how expensive it was to start the node, with the total
+cost describing how expensive the entire node was. In general: the greater the
+values, the more expensive the node.
+
+When using `EXPLAIN ANALYZE`, these statistics will also include the actual time
+(in milliseconds) spent, and other runtime statistics (e.g. the actual number of
+produced rows):
+
+```
+Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1)
+```
+
+Here we can see we estimated 5,746,969 rows to be returned, but in reality we
+returned 5,746,940 rows. We can also see that _just_ this sequential scan took
+2.98 seconds to run.
+
+Using `EXPLAIN (ANALYZE, BUFFERS)` will also give us information about the
+number of rows removed by a filter, the number of buffers used, and more. For
+example:
+
+```
+Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1)
+ Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+ Rows Removed by Filter: 65677
+ Buffers: shared hit=208846
+```
+
+Here we can see that our filter has to remove 65,677 rows, and that we use
+208,846 buffers. Each buffer in PostgreSQL is 8 KB (8192 bytes), meaning our
+above node uses *1.6 GB of buffers*. That's a lot!
+
+## Node types
+
+There are quite a few different types of nodes, so we only cover some of the
+more common ones here.
+
+A full list of all the available nodes and their descriptions can be found in
+the [PostgreSQL source file
+"plannodes.h"](https://github.com/postgres/postgres/blob/master/src/include/nodes/plannodes.h)
+
+### Seq Scan
+
+A sequential scan over (a chunk of) a database table. This is like using
+`Array#each`, but on a database table. Sequential scans can be quite slow when
+retrieving lots of rows, so it's best to avoid these for large tables.
+
+### Index Only Scan
+
+A scan on an index that did not require fetching anything from the table. In
+certain cases an index only scan may still fetch data from the table, in this
+case the node will include a `Heap Fetches:` statistic.
+
+### Index Scan
+
+A scan on an index that required retrieving some data from the table.
+
+### Bitmap Index Scan and Bitmap Heap scan
+
+Bitmap scans fall between sequential scans and index scans. These are typically
+used when we would read too much data from an index scan, but too little to
+perform a sequential scan. A bitmap scan uses what is known as a [bitmap
+index](https://en.wikipedia.org/wiki/Bitmap_index) to perform its work.
+
+The [source code of PostgreSQL](https://github.com/postgres/postgres/blob/1c2cb2744bf3d8ad751cd5cf3b347f10f48492b3/src/include/nodes/plannodes.h#L446-L457)
+states the following on bitmap scans:
+
+> Bitmap Index Scan delivers a bitmap of potential tuple locations; it does not
+> access the heap itself. The bitmap is used by an ancestor Bitmap Heap Scan
+> node, possibly after passing through intermediate Bitmap And and/or Bitmap Or
+> nodes to combine it with the results of other Bitmap Index Scans.
+
+### Limit
+
+Applies a `LIMIT` on the input rows.
+
+### Sort
+
+Sorts the input rows as specified using an `ORDER BY` statement.
+
+### Nested Loop
+
+A nested loop will execute its child nodes for every row produced by a node that
+precedes it. For example:
+
+```
+-> Nested Loop (cost=0.86..176.75 rows=87 width=0) (actual time=0.035..0.249 rows=36 loops=1)
+ Buffers: shared hit=155
+ -> Index Only Scan using users_pkey on users users_1 (cost=0.43..4.95 rows=87 width=4) (actual time=0.029..0.123 rows=36 loops=1)
+ Index Cond: (id < 100)
+ Heap Fetches: 0
+ -> Index Only Scan using users_pkey on users (cost=0.43..1.96 rows=1 width=4) (actual time=0.003..0.003 rows=1 loops=36)
+ Index Cond: (id = users_1.id)
+ Heap Fetches: 0
+```
+
+Here the first child node (`Index Only Scan using users_pkey on users users_1`)
+produces 36 rows, and is executed once (`rows=36 loops=1`). The next node
+produces 1 row (`rows=1`), but is repeated 36 times (`loops=36`). This is
+because the previous node produced 36 rows.
+
+This means that nested loops can quickly slow the query down if the various
+child nodes keep producing many rows.
+
+## Optimising queries
+
+With that out of the way, let's see how we can optimise a query. Let's use the
+following query as an example:
+
+```sql
+SELECT COUNT(*)
+FROM users
+WHERE twitter != '';
+```
+
+This query simply counts the number of users that have a Twitter profile set.
+Let's run this using `EXPLAIN (ANALYZE, BUFFERS)`:
+
+```sql
+EXPLAIN (ANALYZE, BUFFERS)
+SELECT COUNT(*)
+FROM users
+WHERE twitter != '';
+```
+
+This will produce the following plan:
+
+```
+Aggregate (cost=845110.21..845110.22 rows=1 width=8) (actual time=1271.157..1271.158 rows=1 loops=1)
+ Buffers: shared hit=202662
+ -> Seq Scan on users (cost=0.00..844969.99 rows=56087 width=0) (actual time=0.019..1265.883 rows=51833 loops=1)
+ Filter: ((twitter)::text <> ''::text)
+ Rows Removed by Filter: 2487813
+ Buffers: shared hit=202662
+Planning time: 0.390 ms
+Execution time: 1271.180 ms
+```
+
+From this query plan we can see the following:
+
+1. We need to perform a sequential scan on the `users` table.
+1. This sequential scan filters out 2,487,813 rows using a `Filter`.
+1. We use 202,622 buffers, which equals 1.58 GB of memory.
+1. It takes us 1.2 seconds to do all of this.
+
+Considering we are just counting users, that's quite expensive!
+
+Before we start making any changes, let's see if there are any existing indexes
+on the `users` table that we might be able to use. We can obtain this
+information by running `\d users` in a `psql` console, then scrolling down to
+the `Indexes:` section:
+
+```
+Indexes:
+ "users_pkey" PRIMARY KEY, btree (id)
+ "users_confirmation_token_key" UNIQUE CONSTRAINT, btree (confirmation_token)
+ "users_email_key" UNIQUE CONSTRAINT, btree (email)
+ "users_reset_password_token_key" UNIQUE CONSTRAINT, btree (reset_password_token)
+ "index_on_users_lower_email" btree (lower(email::text))
+ "index_on_users_lower_username" btree (lower(username::text))
+ "index_on_users_name_lower" btree (lower(name::text))
+ "index_users_on_admin" btree (admin)
+ "index_users_on_created_at" btree (created_at)
+ "index_users_on_email_trigram" gin (email gin_trgm_ops)
+ "index_users_on_feed_token" btree (feed_token)
+ "index_users_on_ghost" btree (ghost)
+ "index_users_on_incoming_email_token" btree (incoming_email_token)
+ "index_users_on_name" btree (name)
+ "index_users_on_name_trigram" gin (name gin_trgm_ops)
+ "index_users_on_state" btree (state)
+ "index_users_on_state_and_internal_attrs" btree (state) WHERE ghost <> true AND support_bot <> true
+ "index_users_on_support_bot" btree (support_bot)
+ "index_users_on_username" btree (username)
+ "index_users_on_username_trigram" gin (username gin_trgm_ops)
+```
+
+Here we can see there is no index on the `twitter` column, which means
+PostgreSQL has to perform a sequential scan in this case. Let's try to fix this
+by adding the following index:
+
+```sql
+CREATE INDEX CONCURRENTLY twitter_test ON users (twitter);
+```
+
+If we now re-run our query using `EXPLAIN (ANALYZE, BUFFERS)` we get the
+following plan:
+
+```
+Aggregate (cost=61002.82..61002.83 rows=1 width=8) (actual time=297.311..297.312 rows=1 loops=1)
+ Buffers: shared hit=51854 dirtied=19
+ -> Index Only Scan using twitter_test on users (cost=0.43..60873.13 rows=51877 width=0) (actual time=279.184..293.532 rows=51833 loops=1)
+ Filter: ((twitter)::text <> ''::text)
+ Rows Removed by Filter: 2487830
+ Heap Fetches: 26037
+ Buffers: shared hit=51854 dirtied=19
+Planning time: 0.191 ms
+Execution time: 297.334 ms
+```
+
+Now it takes just under 300 milliseconds to get our data, instead of 1.2
+seconds. However, we still use 51,854 buffers, which is about 400 MB of memory.
+300 milliseconds is also quite slow for such a simple query. To understand why
+this query is still expensive, let's take a look at the following:
+
+```
+Index Only Scan using twitter_test on users (cost=0.43..60873.13 rows=51877 width=0) (actual time=279.184..293.532 rows=51833 loops=1)
+ Filter: ((twitter)::text <> ''::text)
+ Rows Removed by Filter: 2487830
+```
+
+We start with an index only scan on our index, but we somehow still apply a
+`Filter` that filters out 2,487,830 rows. Why is that? Well, let's look at how
+we created the index:
+
+```sql
+CREATE INDEX CONCURRENTLY twitter_test ON users (twitter);
+```
+
+We simply told PostgreSQL to index all possible values of the `twitter` column,
+even empty strings. Our query in turn uses `WHERE twitter != ''`. This means
+that the index does improve things, as we don't need to do a sequential scan,
+but we may still encounter empty strings. This means PostgreSQL _has_ to apply a
+Filter on the index results to get rid of those values.
+
+Fortunately, we can improve this even further using "partial indexes". Partial
+indexes are indexes with a `WHERE` condition that is applied when indexing data.
+For example:
+
+```sql
+CREATE INDEX CONCURRENTLY some_index ON users (email) WHERE id < 100
+```
+
+This index would only index the `email` value of rows that match `WHERE id <
+100`. We can use partial indexes to change our Twitter index to the following:
+
+```sql
+CREATE INDEX CONCURRENTLY twitter_test ON users (twitter) WHERE twitter != '';
+```
+
+Once created, if we run our query again we will be given the following plan:
+
+```
+Aggregate (cost=1608.26..1608.27 rows=1 width=8) (actual time=19.821..19.821 rows=1 loops=1)
+ Buffers: shared hit=44036
+ -> Index Only Scan using twitter_test on users (cost=0.41..1479.71 rows=51420 width=0) (actual time=0.023..15.514 rows=51833 loops=1)
+ Heap Fetches: 1208
+ Buffers: shared hit=44036
+Planning time: 0.123 ms
+Execution time: 19.848 ms
+```
+
+That's _a lot_ better! Now it only takes 20 milliseconds to get the data, and we
+only use about 344 MB of buffers (instead of the original 1.58 GB). The reason
+this works is that now PostgreSQL no longer needs to apply a `Filter`, as the
+index only contains `twitter` values that are not empty.
+
+Keep in mind that you shouldn't just add partial indexes every time you want to
+optimise a query. Every index has to be updated for every write, and they may
+require quite a bit of space, depending on the amount of indexed data. As a
+result, first check if there are any existing indexes you may be able to reuse.
+If there aren't any, check if you can perhaps slightly change an existing one to
+fit both the existing and new queries. Only add a new index if none of the
+existing indexes can be used in any way.
+
+## Queries that can't be optimised
+
+Now that we have seen how to optimise a query, let's look at another query that
+we might not be able to optimise:
+
+```sql
+EXPLAIN (ANALYZE, BUFFERS)
+SELECT COUNT(*)
+FROM projects
+WHERE visibility_level IN (0, 20);
+```
+
+The output of `EXPLAIN (ANALYZE, BUFFERS)` is as follows:
+
+```
+Aggregate (cost=922420.60..922420.61 rows=1 width=8) (actual time=3428.535..3428.535 rows=1 loops=1)
+ Buffers: shared hit=208846
+ -> Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1)
+ Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+ Rows Removed by Filter: 65677
+ Buffers: shared hit=208846
+Planning time: 2.861 ms
+Execution time: 3428.596 ms
+```
+
+Looking at the output we see the following Filter:
+
+```
+Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+Rows Removed by Filter: 65677
+```
+
+Looking at the number of rows removed by the filter, we may be tempted to add an
+index on `projects.visibility_level` to somehow turn this Sequential scan +
+filter into an index-only scan.
+
+Unfortunately, doing so is unlikely to improve anything. Contrary to what some
+might believe, an index being present _does not guarantee_ that PostgreSQL will
+actually use it. For example, when doing a `SELECT * FROM projects` it is much
+cheaper to just scan the entire table, instead of using an index and then
+fetching data from the table. In such cases PostgreSQL may decide to not use an
+index.
+
+Second, let's think for a moment what our query does: it gets all projects with
+visibility level 0 or 20. In the above plan we can see this produces quite a lot
+of rows (5,745,940), but how much is that relative to the total? Let's find out
+by running the following query:
+
+```sql
+SELECT visibility_level, count(*) AS amount
+FROM projects
+GROUP BY visibility_level
+ORDER BY visibility_level ASC;
+```
+
+For GitLab.com this produces:
+
+```
+ visibility_level | amount
+------------------+---------
+ 0 | 5071325
+ 10 | 65678
+ 20 | 674801
+```
+
+Here the total number of projects is 5,811,804, and 5,746,126 of those are of
+level 0 or 20. That's 98% of the entire table!
+
+So no matter what we do, this query will retrieve 98% of the entire table. Since
+most time is spent doing exactly that, there isn't really much we can do to
+improve this query, other than _not_ running it at all.
+
+What is important here is that while some may recommend to straight up add an
+index the moment you see a sequential scan, it is _much more important_ to first
+understand what your query does, how much data it retrieves, and so on. After
+all, you can not optimise something you do not understand.
+
+### Cardinality and selectivity
+
+Earlier we saw that our query had to retrieve 98% of the rows in the table.
+There are two terms commonly used for databases: cardinality, and selectivity.
+Cardinality refers to the number of unique values in a particular column in a
+table.
+
+Selectivity is the number of unique values produced by an operation (e.g. an
+index scan or filter), relative to the total number of rows. The higher the
+selectivity, the more likely PostgreSQL is able to use an index.
+
+In the above example, there are only 3 unique values: 0, 10, and 20. This means
+the cardinality is 3. The selectivity in turn is also very low: 0.0000003% (2 /
+5,811,804), because our `Filter` only filters using two values (`0` and `20`).
+With such a low selectivity value it's not surprising that PostgreSQL decides
+using an index is not worth it, because it would produce almost no unique rows.
+
+## Rewriting queries
+
+So the above query can't really be optimised as-is, or at least not much. But
+what if we slightly change the purpose of it? What if instead of retrieving all
+projects with `visibility_level` 0 or 20, we retrieve those that a user
+interacted with somehow?
+
+Fortunately, GitLab has an answer for this, and it's a table called
+`user_interacted_projects`. This table has the following schema:
+
+```
+Table "public.user_interacted_projects"
+ Column | Type | Modifiers
+------------+---------+-----------
+ user_id | integer | not null
+ project_id | integer | not null
+Indexes:
+ "index_user_interacted_projects_on_project_id_and_user_id" UNIQUE, btree (project_id, user_id)
+ "index_user_interacted_projects_on_user_id" btree (user_id)
+Foreign-key constraints:
+ "fk_rails_0894651f08" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
+ "fk_rails_722ceba4f7" FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
+```
+
+Let's rewrite our query to JOIN this table onto our projects, and get the
+projects for a specific user:
+
+```sql
+EXPLAIN ANALYZE
+SELECT COUNT(*)
+FROM projects
+INNER JOIN user_interacted_projects ON user_interacted_projects.project_id = projects.id
+WHERE projects.visibility_level IN (0, 20)
+AND user_interacted_projects.user_id = 1;
+```
+
+What we do here is the following:
+
+1. Get our projects.
+1. INNER JOIN `user_interacted_projects`, meaning we're only left with rows in
+ `projects` that have a corresponding row in `user_interacted_projects`.
+1. Limit this to the projects with `visibility_level` of 0 or 20, and to
+ projects that the user with ID 1 interacted with.
+
+If we run this query we get the following plan:
+
+```
+ Aggregate (cost=871.03..871.04 rows=1 width=8) (actual time=9.763..9.763 rows=1 loops=1)
+ -> Nested Loop (cost=0.86..870.52 rows=203 width=0) (actual time=1.072..9.748 rows=143 loops=1)
+ -> Index Scan using index_user_interacted_projects_on_user_id on user_interacted_projects (cost=0.43..160.71 rows=205 width=4) (actual time=0.939..2.508 rows=145 loops=1)
+ Index Cond: (user_id = 1)
+ -> Index Scan using projects_pkey on projects (cost=0.43..3.45 rows=1 width=4) (actual time=0.049..0.050 rows=1 loops=145)
+ Index Cond: (id = user_interacted_projects.project_id)
+ Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+ Rows Removed by Filter: 0
+ Planning time: 2.614 ms
+ Execution time: 9.809 ms
+```
+
+Here it only took us just under 10 milliseconds to get the data. We can also see
+we're retrieving far fewer projects:
+
+```
+Index Scan using projects_pkey on projects (cost=0.43..3.45 rows=1 width=4) (actual time=0.049..0.050 rows=1 loops=145)
+ Index Cond: (id = user_interacted_projects.project_id)
+ Filter: (visibility_level = ANY ('{0,20}'::integer[]))
+ Rows Removed by Filter: 0
+```
+
+Here we see we perform 145 loops (`loops=145`), with every loop producing 1 row
+(`rows=1`). This is much less than before, and our query performs much better!
+
+If we look at the plan we also see our costs are very low:
+
+```
+Index Scan using projects_pkey on projects (cost=0.43..3.45 rows=1 width=4) (actual time=0.049..0.050 rows=1 loops=145)
+```
+
+Here our cost is only 3.45, and it only takes us 0.050 milliseconds to do so.
+The next index scan is a bit more expensive:
+
+```
+Index Scan using index_user_interacted_projects_on_user_id on user_interacted_projects (cost=0.43..160.71 rows=205 width=4) (actual time=0.939..2.508 rows=145 loops=1)
+```
+
+Here the cost is 160.71 (`cost=0.43..160.71`), taking about 2.5 milliseconds
+(based on the output of `actual time=....`).
+
+The most expensive part here is the "Nested Loop" that acts upon the result of
+these two index scans:
+
+```
+Nested Loop (cost=0.86..870.52 rows=203 width=0) (actual time=1.072..9.748 rows=143 loops=1)
+```
+
+Here we had to perform 870.52 disk page fetches for 203 rows, 9.748
+milliseconds, producing 143 rows in a single loop.
+
+The key takeaway here is that sometimes you have to rewrite (parts of) a query
+to make it better. Sometimes that means having to slightly change your feature
+to accommodate for better performance.
+
+## What makes a bad plan
+
+This is a bit of a difficult question to answer, because the definition of "bad"
+is relative to the problem you are trying to solve. However, some patterns are
+best avoided in most cases, such as:
+
+* Sequential scans on large tables
+* Filters that remove a lot of rows
+* Performing a certain step (e.g. an index scan) that requires _a lot_ of
+ buffers (e.g. more than 512 MB for GitLab.com).
+
+As a general guideline, aim for a query that:
+
+1. Takes no more than 10 milliseconds. Our target time spent in SQL per request
+ is around 100 milliseconds, so every query should be as fast as possible.
+1. Does not use an excessive number of buffers, relative to the workload. For
+ example, retrieving ten rows shouldn't require 1 GB of buffers.
+1. Does not spend a long amount of time performing disk IO operations. The
+ setting `track_io_timing` must be enabled for this data to be included in the
+ output of `EXPLAIN ANALYZE`.
+1. Applies a `LIMIT` when retrieving rows without aggregating them, such as
+ `SELECT * FROM users`.
+1. Doesn't use a `Filter` to filter out too many rows, especially if the query
+ does not use a `LIMIT` to limit the number of returned rows. Filters can
+ usually be removed by adding a (partial) index.
+
+These are _guidelines_ and not hard requirements, as different needs may require
+different queries. The only _rule_ is that you _must always measure_ your query
+(preferably using a production-like database) using `EXPLAIN (ANALYZE, BUFFERS)`
+and related tools such as:
+
+* <https://explain.depesz.com/>
+* <http://tatiyants.com/postgres-query-plan-visualization/>
+
+GitLab employees can also use our chatops solution, available in Slack using the
+`/chatops` slash command. You can use chatops to get a query plan by running the
+following:
+
+```
+/chatops run explain SELECT COUNT(*) FROM projects WHERE visibility_level IN (0, 20)
+```
+
+Visualising the plan using <https://explain.depesz.com/> is also supported:
+
+```
+/chatops run explain --visual SELECT COUNT(*) FROM projects WHERE visibility_level IN (0, 20)
+```
+
+Quoting the query is not necessary.
+
+For more information about the available options, run:
+
+```
+/chatops run explain --help
+```
diff --git a/doc/development/utilities.md b/doc/development/utilities.md
index 0d074a3ef05..e5466ae8914 100644
--- a/doc/development/utilities.md
+++ b/doc/development/utilities.md
@@ -171,8 +171,8 @@ class Commit
extend Gitlab::Cache::RequestCache
def author
- User.find_by_any_email(author_email.downcase)
+ User.find_by_any_email(author_email)
end
- request_cache(:author) { author_email.downcase }
+ request_cache(:author) { author_email }
end
```
diff --git a/doc/development/ux_guide/img/button-close--active.png b/doc/development/ux_guide/img/button-close--active.png
index 824bfc8f31b..97a5301fb91 100644
--- a/doc/development/ux_guide/img/button-close--active.png
+++ b/doc/development/ux_guide/img/button-close--active.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-close--hover.png b/doc/development/ux_guide/img/button-close--hover.png
index 0291e121894..6b8fdf5695b 100644
--- a/doc/development/ux_guide/img/button-close--hover.png
+++ b/doc/development/ux_guide/img/button-close--hover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-close--resting.png b/doc/development/ux_guide/img/button-close--resting.png
index 986d7174ce7..5679b51687c 100644
--- a/doc/development/ux_guide/img/button-close--resting.png
+++ b/doc/development/ux_guide/img/button-close--resting.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-danger--active.png b/doc/development/ux_guide/img/button-danger--active.png
index d3c64424b26..6a9aab0fcc2 100644
--- a/doc/development/ux_guide/img/button-danger--active.png
+++ b/doc/development/ux_guide/img/button-danger--active.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-danger--hover.png b/doc/development/ux_guide/img/button-danger--hover.png
index 8506e093306..13e21c28779 100644
--- a/doc/development/ux_guide/img/button-danger--hover.png
+++ b/doc/development/ux_guide/img/button-danger--hover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-danger--resting.png b/doc/development/ux_guide/img/button-danger--resting.png
index 69ad6bb796b..0ff192bc463 100644
--- a/doc/development/ux_guide/img/button-danger--resting.png
+++ b/doc/development/ux_guide/img/button-danger--resting.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-info--active.png b/doc/development/ux_guide/img/button-info--active.png
index 23be20b225c..12ecdc72a31 100644
--- a/doc/development/ux_guide/img/button-info--active.png
+++ b/doc/development/ux_guide/img/button-info--active.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-info--hover.png b/doc/development/ux_guide/img/button-info--hover.png
index 4cb4e38558c..3bf93bf2b32 100644
--- a/doc/development/ux_guide/img/button-info--hover.png
+++ b/doc/development/ux_guide/img/button-info--hover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-info--resting.png b/doc/development/ux_guide/img/button-info--resting.png
index 5883340aa83..a37a37033bf 100644
--- a/doc/development/ux_guide/img/button-info--resting.png
+++ b/doc/development/ux_guide/img/button-info--resting.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-spam--active.png b/doc/development/ux_guide/img/button-spam--active.png
index 55b44898684..a9e115f49c1 100644
--- a/doc/development/ux_guide/img/button-spam--active.png
+++ b/doc/development/ux_guide/img/button-spam--active.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-spam--hover.png b/doc/development/ux_guide/img/button-spam--hover.png
index 3dc8ed34c54..3b2c16430a6 100644
--- a/doc/development/ux_guide/img/button-spam--hover.png
+++ b/doc/development/ux_guide/img/button-spam--hover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-spam--resting.png b/doc/development/ux_guide/img/button-spam--resting.png
index b6bf10a5b64..4f9f18ca68a 100644
--- a/doc/development/ux_guide/img/button-spam--resting.png
+++ b/doc/development/ux_guide/img/button-spam--resting.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-success--active.png b/doc/development/ux_guide/img/button-success--active.png
index 895a52831cb..b99f6f5e70e 100644
--- a/doc/development/ux_guide/img/button-success--active.png
+++ b/doc/development/ux_guide/img/button-success--active.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-success--hover.png b/doc/development/ux_guide/img/button-success--hover.png
index e4c74bd9778..0d0a61c679a 100644
--- a/doc/development/ux_guide/img/button-success--hover.png
+++ b/doc/development/ux_guide/img/button-success--hover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-success--resting.png b/doc/development/ux_guide/img/button-success--resting.png
index 2fa971b5347..53b955c650a 100644
--- a/doc/development/ux_guide/img/button-success--resting.png
+++ b/doc/development/ux_guide/img/button-success--resting.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-success-secondary--active.png b/doc/development/ux_guide/img/button-success-secondary--active.png
index e7383b36946..333a91f2217 100644
--- a/doc/development/ux_guide/img/button-success-secondary--active.png
+++ b/doc/development/ux_guide/img/button-success-secondary--active.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-success-secondary--hover.png b/doc/development/ux_guide/img/button-success-secondary--hover.png
index 4af2a68cf1b..0cce59212e3 100644
--- a/doc/development/ux_guide/img/button-success-secondary--hover.png
+++ b/doc/development/ux_guide/img/button-success-secondary--hover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-success-secondary--resting.png b/doc/development/ux_guide/img/button-success-secondary--resting.png
index a5a4ec512c8..2779a4949f8 100644
--- a/doc/development/ux_guide/img/button-success-secondary--resting.png
+++ b/doc/development/ux_guide/img/button-success-secondary--resting.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-warning--active.png b/doc/development/ux_guide/img/button-warning--active.png
index 5877d46c94d..f5760cd7c12 100644
--- a/doc/development/ux_guide/img/button-warning--active.png
+++ b/doc/development/ux_guide/img/button-warning--active.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-warning--hover.png b/doc/development/ux_guide/img/button-warning--hover.png
index 308e1adc8a3..a1f4c5cbcc6 100644
--- a/doc/development/ux_guide/img/button-warning--hover.png
+++ b/doc/development/ux_guide/img/button-warning--hover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/button-warning--resting.png b/doc/development/ux_guide/img/button-warning--resting.png
index 28e5e601520..3d62fed5930 100644
--- a/doc/development/ux_guide/img/button-warning--resting.png
+++ b/doc/development/ux_guide/img/button-warning--resting.png
Binary files differ
diff --git a/doc/development/ux_guide/img/color-blue.png b/doc/development/ux_guide/img/color-blue.png
index 844e926f1f5..77c1a2cab31 100644
--- a/doc/development/ux_guide/img/color-blue.png
+++ b/doc/development/ux_guide/img/color-blue.png
Binary files differ
diff --git a/doc/development/ux_guide/img/color-green.png b/doc/development/ux_guide/img/color-green.png
index 5c4c23c7067..51600584c96 100644
--- a/doc/development/ux_guide/img/color-green.png
+++ b/doc/development/ux_guide/img/color-green.png
Binary files differ
diff --git a/doc/development/ux_guide/img/color-grey.png b/doc/development/ux_guide/img/color-grey.png
index 5247649a0ce..f0f0b9d80bb 100644
--- a/doc/development/ux_guide/img/color-grey.png
+++ b/doc/development/ux_guide/img/color-grey.png
Binary files differ
diff --git a/doc/development/ux_guide/img/color-orange.png b/doc/development/ux_guide/img/color-orange.png
index 1103c715225..f16435c0a64 100644
--- a/doc/development/ux_guide/img/color-orange.png
+++ b/doc/development/ux_guide/img/color-orange.png
Binary files differ
diff --git a/doc/development/ux_guide/img/color-red.png b/doc/development/ux_guide/img/color-red.png
index 77ecbbc0a20..5008e75da78 100644
--- a/doc/development/ux_guide/img/color-red.png
+++ b/doc/development/ux_guide/img/color-red.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-anchorlinks.png b/doc/development/ux_guide/img/components-anchorlinks.png
index 4a9c730566c..bd8d30f5905 100644
--- a/doc/development/ux_guide/img/components-anchorlinks.png
+++ b/doc/development/ux_guide/img/components-anchorlinks.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-coverblock.png b/doc/development/ux_guide/img/components-coverblock.png
index fb135f9648a..61160de5613 100644
--- a/doc/development/ux_guide/img/components-coverblock.png
+++ b/doc/development/ux_guide/img/components-coverblock.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-dateexact.png b/doc/development/ux_guide/img/components-dateexact.png
index 686ca727293..cc1fb8216bf 100644
--- a/doc/development/ux_guide/img/components-dateexact.png
+++ b/doc/development/ux_guide/img/components-dateexact.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-fileholder.png b/doc/development/ux_guide/img/components-fileholder.png
index ec2911a1232..5bf8565346a 100644
--- a/doc/development/ux_guide/img/components-fileholder.png
+++ b/doc/development/ux_guide/img/components-fileholder.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-horizontalform.png b/doc/development/ux_guide/img/components-horizontalform.png
index c57dceda43a..e6cbc69d20a 100644
--- a/doc/development/ux_guide/img/components-horizontalform.png
+++ b/doc/development/ux_guide/img/components-horizontalform.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-listinsidepanel.png b/doc/development/ux_guide/img/components-listinsidepanel.png
index 3a72d39bb5d..6b773a19954 100644
--- a/doc/development/ux_guide/img/components-listinsidepanel.png
+++ b/doc/development/ux_guide/img/components-listinsidepanel.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-listwithhover.png b/doc/development/ux_guide/img/components-listwithhover.png
index 8521a8ad53e..0826848ff34 100644
--- a/doc/development/ux_guide/img/components-listwithhover.png
+++ b/doc/development/ux_guide/img/components-listwithhover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-referencehover.png b/doc/development/ux_guide/img/components-referencehover.png
index f80564dbb16..af5405d3e0b 100644
--- a/doc/development/ux_guide/img/components-referencehover.png
+++ b/doc/development/ux_guide/img/components-referencehover.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-referenceissues.png b/doc/development/ux_guide/img/components-referenceissues.png
index 51fb2cf3e43..4e175dc169d 100644
--- a/doc/development/ux_guide/img/components-referenceissues.png
+++ b/doc/development/ux_guide/img/components-referenceissues.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-referencelabels.png b/doc/development/ux_guide/img/components-referencelabels.png
index aba450cc3ba..29a985bbaa0 100644
--- a/doc/development/ux_guide/img/components-referencelabels.png
+++ b/doc/development/ux_guide/img/components-referencelabels.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-referencemilestone.png b/doc/development/ux_guide/img/components-referencemilestone.png
index adf2555ccf8..47c76a9d60f 100644
--- a/doc/development/ux_guide/img/components-referencemilestone.png
+++ b/doc/development/ux_guide/img/components-referencemilestone.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-referencemrs.png b/doc/development/ux_guide/img/components-referencemrs.png
index 6c3375f1ea1..9a5032a1516 100644
--- a/doc/development/ux_guide/img/components-referencemrs.png
+++ b/doc/development/ux_guide/img/components-referencemrs.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-referencepeople.png b/doc/development/ux_guide/img/components-referencepeople.png
index b8dd431e2e6..f9ef11be853 100644
--- a/doc/development/ux_guide/img/components-referencepeople.png
+++ b/doc/development/ux_guide/img/components-referencepeople.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-searchbox.png b/doc/development/ux_guide/img/components-searchbox.png
index a25189296ba..5c19024bfb0 100644
--- a/doc/development/ux_guide/img/components-searchbox.png
+++ b/doc/development/ux_guide/img/components-searchbox.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-searchboxscoped.png b/doc/development/ux_guide/img/components-searchboxscoped.png
index b116d714848..d4a35977658 100644
--- a/doc/development/ux_guide/img/components-searchboxscoped.png
+++ b/doc/development/ux_guide/img/components-searchboxscoped.png
Binary files differ
diff --git a/doc/development/ux_guide/img/components-simplelist.png b/doc/development/ux_guide/img/components-simplelist.png
index 858e5064c25..8d11c674e84 100644
--- a/doc/development/ux_guide/img/components-simplelist.png
+++ b/doc/development/ux_guide/img/components-simplelist.png
Binary files differ
diff --git a/doc/development/ux_guide/img/features-contextualnav.png b/doc/development/ux_guide/img/features-contextualnav.png
index f8466f28627..aa816776fad 100644
--- a/doc/development/ux_guide/img/features-contextualnav.png
+++ b/doc/development/ux_guide/img/features-contextualnav.png
Binary files differ
diff --git a/doc/development/ux_guide/img/features-emptystates.png b/doc/development/ux_guide/img/features-emptystates.png
index 51835a7080b..50f31f5e523 100644
--- a/doc/development/ux_guide/img/features-emptystates.png
+++ b/doc/development/ux_guide/img/features-emptystates.png
Binary files differ
diff --git a/doc/development/ux_guide/img/icon-add.png b/doc/development/ux_guide/img/icon-add.png
index bcad5e84591..f66525cc1b4 100644
--- a/doc/development/ux_guide/img/icon-add.png
+++ b/doc/development/ux_guide/img/icon-add.png
Binary files differ
diff --git a/doc/development/ux_guide/img/icon-close.png b/doc/development/ux_guide/img/icon-close.png
index dfe1495f5fa..af6c30ebe6a 100644
--- a/doc/development/ux_guide/img/icon-close.png
+++ b/doc/development/ux_guide/img/icon-close.png
Binary files differ
diff --git a/doc/development/ux_guide/img/icon-edit.png b/doc/development/ux_guide/img/icon-edit.png
index 50f6f841868..b9649f4aeec 100644
--- a/doc/development/ux_guide/img/icon-edit.png
+++ b/doc/development/ux_guide/img/icon-edit.png
Binary files differ
diff --git a/doc/development/ux_guide/img/icon-notification.png b/doc/development/ux_guide/img/icon-notification.png
index 6ddfaa44f66..5cf8f8ab59a 100644
--- a/doc/development/ux_guide/img/icon-notification.png
+++ b/doc/development/ux_guide/img/icon-notification.png
Binary files differ
diff --git a/doc/development/ux_guide/img/icon-rss.png b/doc/development/ux_guide/img/icon-rss.png
index b766488b32d..7e2987a2656 100644
--- a/doc/development/ux_guide/img/icon-rss.png
+++ b/doc/development/ux_guide/img/icon-rss.png
Binary files differ
diff --git a/doc/development/ux_guide/img/icon-spec.png b/doc/development/ux_guide/img/icon-spec.png
index 56b19610dc1..5bb85c5be98 100644
--- a/doc/development/ux_guide/img/icon-spec.png
+++ b/doc/development/ux_guide/img/icon-spec.png
Binary files differ
diff --git a/doc/development/ux_guide/img/icon-subscribe.png b/doc/development/ux_guide/img/icon-subscribe.png
index 650168296c6..7e2f5e6a1c6 100644
--- a/doc/development/ux_guide/img/icon-subscribe.png
+++ b/doc/development/ux_guide/img/icon-subscribe.png
Binary files differ
diff --git a/doc/development/ux_guide/img/icon-trash.png b/doc/development/ux_guide/img/icon-trash.png
index b02178ca992..bc46638fb2e 100644
--- a/doc/development/ux_guide/img/icon-trash.png
+++ b/doc/development/ux_guide/img/icon-trash.png
Binary files differ
diff --git a/doc/development/ux_guide/img/illustrations-caps-do.png b/doc/development/ux_guide/img/illustrations-caps-do.png
index 7a2c74382f6..f1030769b94 100644
--- a/doc/development/ux_guide/img/illustrations-caps-do.png
+++ b/doc/development/ux_guide/img/illustrations-caps-do.png
Binary files differ
diff --git a/doc/development/ux_guide/img/illustrations-caps-don't.png b/doc/development/ux_guide/img/illustrations-caps-don't.png
index 848f72dbe30..ab7abcaaf6f 100644
--- a/doc/development/ux_guide/img/illustrations-caps-don't.png
+++ b/doc/development/ux_guide/img/illustrations-caps-don't.png
Binary files differ
diff --git a/doc/development/ux_guide/img/james-mackey.png b/doc/development/ux_guide/img/james-mackey.png
index c8f9097f69f..f51a45c437b 100644
--- a/doc/development/ux_guide/img/james-mackey.png
+++ b/doc/development/ux_guide/img/james-mackey.png
Binary files differ
diff --git a/doc/development/ux_guide/img/karolina-plaskaty.png b/doc/development/ux_guide/img/karolina-plaskaty.png
index ae2e98b7bad..d1c9528dd5a 100644
--- a/doc/development/ux_guide/img/karolina-plaskaty.png
+++ b/doc/development/ux_guide/img/karolina-plaskaty.png
Binary files differ
diff --git a/doc/development/ux_guide/img/matthieu-poirier.png b/doc/development/ux_guide/img/matthieu-poirier.png
index dd21948ebe2..0ecc2d670d6 100644
--- a/doc/development/ux_guide/img/matthieu-poirier.png
+++ b/doc/development/ux_guide/img/matthieu-poirier.png
Binary files differ
diff --git a/doc/development/ux_guide/img/modals-general-confimation-dialog.png b/doc/development/ux_guide/img/modals-general-confimation-dialog.png
index 00a17374a0b..4ea0ea10ca7 100644
--- a/doc/development/ux_guide/img/modals-general-confimation-dialog.png
+++ b/doc/development/ux_guide/img/modals-general-confimation-dialog.png
Binary files differ
diff --git a/doc/development/ux_guide/img/modals-layout-for-modals.png b/doc/development/ux_guide/img/modals-layout-for-modals.png
index 6c7bc09e750..c481edd8250 100644
--- a/doc/development/ux_guide/img/modals-layout-for-modals.png
+++ b/doc/development/ux_guide/img/modals-layout-for-modals.png
Binary files differ
diff --git a/doc/development/ux_guide/img/modals-special-confimation-dialog.png b/doc/development/ux_guide/img/modals-special-confimation-dialog.png
index bf1e56326c5..d966010158b 100644
--- a/doc/development/ux_guide/img/modals-special-confimation-dialog.png
+++ b/doc/development/ux_guide/img/modals-special-confimation-dialog.png
Binary files differ
diff --git a/doc/development/ux_guide/img/modals-three-buttons.png b/doc/development/ux_guide/img/modals-three-buttons.png
index 519439e64e4..157d1b650bf 100644
--- a/doc/development/ux_guide/img/modals-three-buttons.png
+++ b/doc/development/ux_guide/img/modals-three-buttons.png
Binary files differ
diff --git a/doc/development/ux_guide/img/nazim-ramesh.png b/doc/development/ux_guide/img/nazim-ramesh.png
index cc3e197679d..dad2b37010b 100644
--- a/doc/development/ux_guide/img/nazim-ramesh.png
+++ b/doc/development/ux_guide/img/nazim-ramesh.png
Binary files differ
diff --git a/doc/development/ux_guide/img/popover-placement-above.png b/doc/development/ux_guide/img/popover-placement-above.png
index 1aa044bfc9c..84c9c878ec2 100644
--- a/doc/development/ux_guide/img/popover-placement-above.png
+++ b/doc/development/ux_guide/img/popover-placement-above.png
Binary files differ
diff --git a/doc/development/ux_guide/img/popover-placement-below.png b/doc/development/ux_guide/img/popover-placement-below.png
index 2d6ab8a1618..f6f18199ab6 100644
--- a/doc/development/ux_guide/img/popover-placement-below.png
+++ b/doc/development/ux_guide/img/popover-placement-below.png
Binary files differ
diff --git a/doc/development/ux_guide/img/surfaces-contentitemtitle.png b/doc/development/ux_guide/img/surfaces-contentitemtitle.png
index 3af0b56c8fb..f6cd212ecfd 100644
--- a/doc/development/ux_guide/img/surfaces-contentitemtitle.png
+++ b/doc/development/ux_guide/img/surfaces-contentitemtitle.png
Binary files differ
diff --git a/doc/development/ux_guide/img/surfaces-systeminformationblock.png b/doc/development/ux_guide/img/surfaces-systeminformationblock.png
index 9f42f1d4dd0..f3313add2b8 100644
--- a/doc/development/ux_guide/img/surfaces-systeminformationblock.png
+++ b/doc/development/ux_guide/img/surfaces-systeminformationblock.png
Binary files differ
diff --git a/doc/development/ux_guide/img/surfaces-ux.png b/doc/development/ux_guide/img/surfaces-ux.png
index 53208727c64..eaa7f70c0c7 100644
--- a/doc/development/ux_guide/img/surfaces-ux.png
+++ b/doc/development/ux_guide/img/surfaces-ux.png
Binary files differ
diff --git a/doc/development/ux_guide/img/tooltip-placement.png b/doc/development/ux_guide/img/tooltip-placement.png
index 061f82e4df0..da49c192878 100644
--- a/doc/development/ux_guide/img/tooltip-placement.png
+++ b/doc/development/ux_guide/img/tooltip-placement.png
Binary files differ
diff --git a/doc/development/ux_guide/img/tooltip-usage.png b/doc/development/ux_guide/img/tooltip-usage.png
index 40c4f051cd0..4f5884c4b48 100644
--- a/doc/development/ux_guide/img/tooltip-usage.png
+++ b/doc/development/ux_guide/img/tooltip-usage.png
Binary files differ
diff --git a/doc/development/ux_guide/tips.md b/doc/development/ux_guide/tips.md
index 8348de4f8a2..ceb9ed56ac4 100644
--- a/doc/development/ux_guide/tips.md
+++ b/doc/development/ux_guide/tips.md
@@ -1,20 +1,16 @@
# Tips
-## Contents
-* [SVGs](#svgs)
-
----
-
## SVGs
When exporting SVGs, be sure to follow the following guidelines:
1. Convert all strokes to outlines.
-2. Use pathfinder tools to combine overlapping paths and create compound paths.
-3. SVGs that are limited to one color should be exported without a fill color so the color can be set using CSS.
-4. Ensure that exported SVGs have been run through an [SVG cleaner](https://github.com/RazrFalcon/SVGCleaner) to remove unused elements and attributes.
+1. Use pathfinder tools to combine overlapping paths and create compound paths.
+1. SVGs that are limited to one color should be exported without a fill color so the color can be set using CSS.
+1. Ensure that exported SVGs have been run through an [SVG cleaner](https://github.com/RazrFalcon/SVGCleaner) to remove unused elements and attributes.
+
+You can open your SVG in a text editor to ensure that it is clean.
-You can open your svg in a text editor to ensure that it is clean.
Incorrect files will look like this:
```xml
@@ -35,10 +31,10 @@ Incorrect files will look like this:
</svg>
```
-Correct file will look like this:
+Correct files will look like this:
```xml
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" enable-background="new 0 0 16 17"><path d="m15.1 1h-2.1v-1h-2v1h-6v-1h-2v1h-2.1c-.5 0-.9.5-.9 1v14c0 .6.4 1 .9 1h14.2c.5 0 .9-.4.9-1v-14c0-.5-.4-1-.9-1m-1.1 14h-12v-9h12v9m0-11h-12v-1h12v1"/><path d="m5.4 11.6l1.5 1.2c.4.3 1.1.3 1.4-.1l2.5-3c.3-.4.3-1.1-.1-1.4-.5-.4-1.1-.3-1.5.1l-1.8 2.2-.8-.6c-.4-.3-1.1-.3-1.4.2-.3.4-.3 1 .2 1.4"/></svg>
```
-> TODO: Checkout [https://github.com/svg/svgo](https://github.com/svg/svgo)
+> TODO: Checkout <https://github.com/svg/svgo>.
diff --git a/doc/development/ux_guide/users.md b/doc/development/ux_guide/users.md
index 6afb33cfc36..30386e728c4 100644
--- a/doc/development/ux_guide/users.md
+++ b/doc/development/ux_guide/users.md
@@ -101,7 +101,7 @@ GitLab's interface initially attracted Nazim when he was comparing version contr
### Demographics
**Age**
-
+
42 years old
**Location**
@@ -148,13 +148,13 @@ Matthieu describes GitLab as:
>"the only tool that offers the real feeling of having everything you need in one place."
-He credits himself as being entirely responsible for moving his company to GitLab.
+He credits himself as being entirely responsible for moving his company to GitLab.
### Frustrations
#### Updating to the latest release
-Matthieu introduced his company to GitLab. He is responsible for maintaining and managing the company's installation in addition to his day job. He feels updates are too frequent and he doesn't always have sufficient time to update GitLab. As a result, he's not up to date with releases.
+Matthieu introduced his company to GitLab. He is responsible for maintaining and managing the company's installation in addition to his day job. He feels updates are too frequent and he doesn't always have sufficient time to update GitLab. As a result, he's not up to date with releases.
-Matthieu tried to set up automatic updates, however, as he isn't a Systems Administrator, he wasn't confident in his set-up. He feels he should be able to "upgrade without users even noticing" but hasn't figured out how to do this yet. Matthieu would like the "update process to be triggered from the Admin Panel, perhaps accompanied with a changelog and the option to skip updates."
+Matthieu tried to set up automatic updates, however, as he isn't a Systems Administrator, he wasn't confident in his setup. He feels he should be able to "upgrade without users even noticing" but hasn't figured out how to do this yet. Matthieu would like the "update process to be triggered from the Admin Panel, perhaps accompanied with a changelog and the option to skip updates."
Matthieu is looking for confirmation that his update procedure is "secure and efficient" so more tutorials related to this topic would be useful to him.
@@ -173,11 +173,11 @@ It's Matthieu's responsibility to get teams across his organization up and runni
He states that there has been: "a sluggishness of others to adapt" and it's "a low-effort adaptation at that."
### Goals
-* To save time. One of the reasons Matthieu moved his company to GitLab was to reduce the effort it took him to manage and configure multiple tools, thus saving him time. He has to balance his day job in addition to managing the company's GitLab installation and onboarding new teams to GitLab.
-* To use a platform which is easy to manage. Matthieu isn't a Systems Administrator, and when updating GitLab, creating backups, etc. He would prefer to work within GitLab's UI. Explanations / guided instructions when configuring settings in GitLab's interface would really help Matthieu. He needs reassurance that what he is about to change is
+* To save time. One of the reasons Matthieu moved his company to GitLab was to reduce the effort it took him to manage and configure multiple tools, thus saving him time. He has to balance his day job in addition to managing the company's GitLab installation and onboarding new teams to GitLab.
+* To use a platform which is easy to manage. Matthieu isn't a Systems Administrator, and when updating GitLab, creating backups, etc. He would prefer to work within GitLab's UI. Explanations / guided instructions when configuring settings in GitLab's interface would really help Matthieu. He needs reassurance that what he is about to change is
-1. the right setting
-2. will provide him with the desired result he wants.
+- The right setting.
+- Will provide him with the desired result he wants.
* Matthieu needs to educate his colleagues about GitLab. Matthieu's colleagues won't adopt GitLab as they're unaware of its capabilities and the positive impact it could have on their work. Matthieu needs support in getting this message across to them.
@@ -242,7 +242,7 @@ Some of GitLab EE's features are too basic, in particular, issues boards which d
James and his team use CI quite heavily for several projects. Whilst they've welcomed improvements to the builds and pipelines interface, they still have some difficulty following build process on the different tabs under Pipelines. Some confusion has arisen from not knowing where to find different pieces of information or how to get to the next stages logs from the current stage's log output screen. They feel more intuitive linking and flow may alleviate the problem. Generally, they feel GitLab's navigation needs to reviewed and optimized.
#### Permissions
->"There is no granular control over user or group permissions. The permissions for a project are too tightly coupled to the permissions for Gitlab CI/build pipelines."
+>"There is no granular control over user or group permissions. The permissions for a project are too tightly coupled to the permissions for GitLab CI/build pipelines."
### Goals
@@ -290,7 +290,7 @@ JavaScript and SQL
Web development, mobile development, UX, open source, gaming, and travel.
### Motivations
-Karolina has been using GitLab.com for around a year. She roughly spends 8 hours every week programming, of that, 2 hours is spent contributing to open source projects. Karolina contributes to open source projects to gain programming experience and to give back to the community. She likes GitLab.com for its free private repositories and range of features which provide her with everything she needs for her personal projects. Karolina is also a massive fan of GitLab's values and the fact that it isn't a "behemoth of a company". She explains that "displaying every single thing (doc, culture, assumptions, development...) in the open gives me greater confidence to choose Gitlab personally and to recommend it at work." She's also an avid reader of GitLab's blog.
+Karolina has been using GitLab.com for around a year. She roughly spends 8 hours every week programming, of that, 2 hours is spent contributing to open source projects. Karolina contributes to open source projects to gain programming experience and to give back to the community. She likes GitLab.com for its free private repositories and range of features which provide her with everything she needs for her personal projects. Karolina is also a massive fan of GitLab's values and the fact that it isn't a "behemoth of a company". She explains that "displaying every single thing (doc, culture, assumptions, development...) in the open gives me greater confidence to choose GitLab personally and to recommend it at work." She's also an avid reader of GitLab's blog.
Karolina works for a software development company which currently hires around 500 people. Karolina would love to use GitLab at work but the company has used GitHub Enterprise for a number of years. She describes management at her company as "old fashioned" and explains that it's "less of a technical issue and more of a cultural issue" to convince upper management to move to GitLab. Karolina is also relatively new to the company so she's apprehensive about pushing too hard to change version control platforms.
@@ -307,4 +307,4 @@ Karolina has an interest in UX and therefore has strong opinions about how GitLa
### Goals
* To develop her programming experience and to learn from other developers.
* To contribute to both her own and other open source projects.
-* To use a fast and intuitive version control platform. \ No newline at end of file
+* To use a fast and intuitive version control platform.
diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md
index b668c9de6a0..3630a28fae9 100644
--- a/doc/development/what_requires_downtime.md
+++ b/doc/development/what_requires_downtime.md
@@ -300,7 +300,7 @@ The same applies to `rename_column_using_background_migration`:
1. Create a migration using the helper, which will schedule background
migrations to spread the writes over a longer period of time.
-2. In the next monthly release, create a clean-up migration to steal from the
+1. In the next monthly release, create a clean-up migration to steal from the
Sidekiq queues, migrate any missing rows, and cleanup the rename. This
migration should skip the steps after stealing from the Sidekiq queues if the
column has already been renamed.
diff --git a/doc/gitlab-basics/add-image.md b/doc/gitlab-basics/add-image.md
index 1a44123aa81..17c210c43e0 100644
--- a/doc/gitlab-basics/add-image.md
+++ b/doc/gitlab-basics/add-image.md
@@ -5,7 +5,7 @@ in Windows, or...), put the image file into the GitLab project. You can find the
project as a regular folder in your files.
Go to your [shell](command-line-commands.md), and move into the folder of your
-Gitlab project. This usually means running the following command until you get
+GitLab project. This usually means running the following command until you get
to the desired destination:
```
diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md
index 4666511d747..a0111be0767 100644
--- a/doc/gitlab-basics/command-line-commands.md
+++ b/doc/gitlab-basics/command-line-commands.md
@@ -125,3 +125,6 @@ pwd
```
clear
```
+### Sample Git taskflow
+
+If you are completely new to Git, looking through some [sample taskflows](https://rogerdudler.github.io/git-guide/) will help you understand best practices for using these commands as you work.
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 2517908e5b1..33f46e8d4f3 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -1,8 +1,8 @@
# How to create a project in GitLab
->**Notes:**
-- For a list of words that are not allowed to be used as project names see the
- [reserved names][reserved].
+> **Notes:**
+> - For a list of words that are not allowed to be used as project names see the
+> [reserved names][reserved].
1. In your dashboard, click the green **New project** button or use the plus
icon in the upper right corner of the navigation bar.
diff --git a/doc/gitlab-basics/create-your-ssh-keys.md b/doc/gitlab-basics/create-your-ssh-keys.md
index b6ebe374de3..881629c3bfd 100644
--- a/doc/gitlab-basics/create-your-ssh-keys.md
+++ b/doc/gitlab-basics/create-your-ssh-keys.md
@@ -12,7 +12,7 @@
![SSH Keys](img/profile_settings_ssh_keys.png)
-3. Paste your **public** key that you generated in the first step in the 'Key'
+1. Paste your **public** key that you generated in the first step in the 'Key'
box.
![Paste SSH public key](img/profile_settings_ssh_keys_paste_pub.png)
diff --git a/doc/gitlab-basics/img/create_new_project_info.png b/doc/gitlab-basics/img/create_new_project_info.png
index b4119dc046a..2693a7f9a6d 100644
--- a/doc/gitlab-basics/img/create_new_project_info.png
+++ b/doc/gitlab-basics/img/create_new_project_info.png
Binary files differ
diff --git a/doc/gitlab-basics/img/fork_new.png b/doc/gitlab-basics/img/fork_new.png
index fa185fdaca1..7bbc3d8fbae 100644
--- a/doc/gitlab-basics/img/fork_new.png
+++ b/doc/gitlab-basics/img/fork_new.png
Binary files differ
diff --git a/doc/gitlab-basics/img/merge_request_select_branch.png b/doc/gitlab-basics/img/merge_request_select_branch.png
index 57ea0e65f34..b1dec975f9b 100644
--- a/doc/gitlab-basics/img/merge_request_select_branch.png
+++ b/doc/gitlab-basics/img/merge_request_select_branch.png
Binary files differ
diff --git a/doc/gitlab-basics/img/profile_settings.png b/doc/gitlab-basics/img/profile_settings.png
index aaa1a39313d..b91b698fb18 100644
--- a/doc/gitlab-basics/img/profile_settings.png
+++ b/doc/gitlab-basics/img/profile_settings.png
Binary files differ
diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png
index 5e501ec86ef..0b1c64a72f3 100644
--- a/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png
+++ b/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png
Binary files differ
diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png
index 7ebb8973ef0..8014f1d5301 100644
--- a/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png
+++ b/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png
Binary files differ
diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png
index 89a04c17fed..02ca0bf7478 100644
--- a/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png
+++ b/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png
Binary files differ
diff --git a/doc/img/devops_lifecycle.png b/doc/img/devops_lifecycle.png
index 0616be46df8..0b15e9619a5 100644
--- a/doc/img/devops_lifecycle.png
+++ b/doc/img/devops_lifecycle.png
Binary files differ
diff --git a/doc/install/README.md b/doc/install/README.md
index 27df03c6ac6..92116305775 100644
--- a/doc/install/README.md
+++ b/doc/install/README.md
@@ -34,11 +34,11 @@ the hardware requirements.
- [Install GitLab on Google Cloud Platform](google_cloud_platform/index.md)
- [Install GitLab on Google Kubernetes Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/): video tutorial on
the full process of installing GitLab on Google Kubernetes Engine (GKE), pushing an application to GitLab, building the app with GitLab CI/CD, and deploying to production.
-- [Install on AWS](https://about.gitlab.com/aws/)
-- _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) -
- Quickly test any version of GitLab on DigitalOcean using Docker Machine.
+- [Install on AWS](aws/index.md): Install GitLab on AWS using the community AMIs that GitLab provides.
- [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/): requirements, installation process, updates.
- [Demo: Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/): video demonstration on how to install GitLab on Kubernetes, build a project, create Review Apps, store Docker images in Container Registry, deploy to production on Kubernetes, and monitor with Prometheus.
+- _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) -
+ Quickly test any version of GitLab on DigitalOcean using Docker Machine.
## Database
diff --git a/doc/install/aws/img/add_tags.png b/doc/install/aws/img/add_tags.png
new file mode 100644
index 00000000000..3572cd5daa1
--- /dev/null
+++ b/doc/install/aws/img/add_tags.png
Binary files differ
diff --git a/doc/install/aws/img/associate_subnet_gateway.png b/doc/install/aws/img/associate_subnet_gateway.png
new file mode 100644
index 00000000000..1edca974fca
--- /dev/null
+++ b/doc/install/aws/img/associate_subnet_gateway.png
Binary files differ
diff --git a/doc/install/aws/img/associate_subnet_gateway_2.png b/doc/install/aws/img/associate_subnet_gateway_2.png
new file mode 100644
index 00000000000..76e101d32a3
--- /dev/null
+++ b/doc/install/aws/img/associate_subnet_gateway_2.png
Binary files differ
diff --git a/doc/install/aws/img/aws_diagram.png b/doc/install/aws/img/aws_diagram.png
new file mode 100644
index 00000000000..bcd5c69bbeb
--- /dev/null
+++ b/doc/install/aws/img/aws_diagram.png
Binary files differ
diff --git a/doc/install/aws/img/choose_ami.png b/doc/install/aws/img/choose_ami.png
new file mode 100644
index 00000000000..034ac92691d
--- /dev/null
+++ b/doc/install/aws/img/choose_ami.png
Binary files differ
diff --git a/doc/install/aws/img/create_gateway.png b/doc/install/aws/img/create_gateway.png
new file mode 100644
index 00000000000..9408520e050
--- /dev/null
+++ b/doc/install/aws/img/create_gateway.png
Binary files differ
diff --git a/doc/install/aws/img/create_route_table.png b/doc/install/aws/img/create_route_table.png
new file mode 100644
index 00000000000..ea72c57257e
--- /dev/null
+++ b/doc/install/aws/img/create_route_table.png
Binary files differ
diff --git a/doc/install/aws/img/create_security_group.png b/doc/install/aws/img/create_security_group.png
new file mode 100644
index 00000000000..9a0dfccfe37
--- /dev/null
+++ b/doc/install/aws/img/create_security_group.png
Binary files differ
diff --git a/doc/install/aws/img/create_subnet.png b/doc/install/aws/img/create_subnet.png
new file mode 100644
index 00000000000..26f5ab1b625
--- /dev/null
+++ b/doc/install/aws/img/create_subnet.png
Binary files differ
diff --git a/doc/install/aws/img/create_vpc.png b/doc/install/aws/img/create_vpc.png
new file mode 100644
index 00000000000..a678f7013fd
--- /dev/null
+++ b/doc/install/aws/img/create_vpc.png
Binary files differ
diff --git a/doc/install/aws/img/ec_az.png b/doc/install/aws/img/ec_az.png
new file mode 100644
index 00000000000..22a8291c593
--- /dev/null
+++ b/doc/install/aws/img/ec_az.png
Binary files differ
diff --git a/doc/install/aws/img/ec_subnet.png b/doc/install/aws/img/ec_subnet.png
new file mode 100644
index 00000000000..c44fb4485e3
--- /dev/null
+++ b/doc/install/aws/img/ec_subnet.png
Binary files differ
diff --git a/doc/install/aws/img/policies.png b/doc/install/aws/img/policies.png
new file mode 100644
index 00000000000..e99497a52a2
--- /dev/null
+++ b/doc/install/aws/img/policies.png
Binary files differ
diff --git a/doc/install/aws/img/rds_subnet_group.png b/doc/install/aws/img/rds_subnet_group.png
new file mode 100644
index 00000000000..7c6157e38e0
--- /dev/null
+++ b/doc/install/aws/img/rds_subnet_group.png
Binary files differ
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
new file mode 100644
index 00000000000..53fe1a6b25b
--- /dev/null
+++ b/doc/install/aws/index.md
@@ -0,0 +1,655 @@
+# Installing GitLab on Amazon Web Services (AWS)
+
+To install GitLab on AWS, you can use the Amazon Machine Images (AMIs) that GitLab
+provides with [each release](https://about.gitlab.com/releases/).
+
+This page offers a walkthrough of a common HA (Highly Available) configuration
+for GitLab on AWS. You should customize it to accommodate your needs.
+
+## Introduction
+
+GitLab on AWS can leverage many of the services that are already
+configurable with GitLab High Availability (HA). These services offer a great deal of
+flexibility and can be adapted to the needs of most companies, while enabling the
+automation of both vertical and horizontal scaling.
+
+In this guide, we'll go through a basic HA setup where we'll start by
+configuring our Virtual Private Cloud and subnets to later integrate
+services such as RDS for our database server and ElastiCache as a Redis
+cluster to finally manage them within an auto scaling group with custom
+scaling policies.
+
+## Requirements
+
+In addition to having a basic familiarity with [AWS](https://docs.aws.amazon.com/) and [Amazon EC2](https://docs.aws.amazon.com/ec2/), you will need:
+
+- [An AWS account](https://console.aws.amazon.com/console/home)
+- [To create or upload an SSH key](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html)
+ to connect to the instance via SSH
+- A domain name for the GitLab instance
+
+## Architecture
+
+Below is a diagram of the recommended architecture.
+
+![AWS architecture diagram](img/aws_diagram.png)
+
+## AWS costs
+
+Here's a list of the AWS services we will use, with links to pricing information:
+
+- **EC2**: GitLab will deployed on shared hardware which means
+ [on-demand pricing](https://aws.amazon.com/ec2/pricing/on-demand)
+ will apply. If you want to run it on a dedicated or reserved instance,
+ consult the [EC2 pricing page](https://aws.amazon.com/ec2/pricing/) for more
+ information on the cost.
+- **EBS**: We will also use an EBS volume to store the Git data. See the
+ [Amazon EBS pricing](https://aws.amazon.com/ebs/pricing/).
+- **S3**: We will use S3 to store backups, artifacts, LFS objects, etc. See the
+ [Amazon S3 pricing](https://aws.amazon.com/s3/pricing/).
+- **ALB**: An Application Load Balancer will be used to route requests to the
+ GitLab instance. See the [Amazon ELB pricing](https://aws.amazon.com/elasticloadbalancing/pricing/).
+- **RDS**: An Amazon Relational Database Service using PostgreSQL will be used
+ to provide a High Availability database configuration. See the
+ [Amazon RDS pricing](https://aws.amazon.com/rds/postgresql/pricing/).
+- **ElastiCache**: An in-memory cache environment will be used to provide a
+ High Availability Redis configuration. See the
+ [Amazon ElastiCache pricing](https://aws.amazon.com/elasticache/pricing/).
+
+## Creating an IAM EC2 instance role and profile
+To minimize the permissions of the user, we'll create a new [IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html)
+role with limited access:
+
+1. Navigate to the IAM dashboard https://console.aws.amazon.com/iam/home and
+ click **Create role**.
+1. Create a new role by selecting **AWS service > EC2**, then click
+ **Next: Permissions**.
+1. Choose **AmazonEC2FullAccess** and **AmazonS3FullAccess**, then click **Next: Review**.
+1. Give the role the name `GitLabAdmin` and click **Create role**.
+
+## Configuring the network
+
+We'll start by creating a VPC for our GitLab cloud infrastructure, then
+we can create subnets to have public and private instances in at least
+two [Availability Zones (AZs)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html). Public subnets will require a Route Table keep and an associated
+Internet Gateway.
+
+### Creating the Virtual Private Cloud (VPC)
+
+We'll now create a VPC, a virtual networking environment that you'll control:
+
+1. Navigate to https://console.aws.amazon.com/vpc/home.
+1. Select **Your VPCs** from the left menu and then click **Create VPC**.
+ At the "Name tag" enter `gitlab-vpc` and at the "IPv4 CIDR block" enter
+ `10.0.0.0/16`. If you don't require dedicated hardware, you can leave
+ "Tenancy" as default. Click **Yes, Create** when ready.
+
+ ![Create VPC](img/create_vpc.png)
+
+### Subnets
+
+Now, let's create some subnets in different Availability Zones. Make sure
+that each subnet is associated the the VPC we just created and
+that CIDR blocks don't overlap. This will also
+allow us to enable multi AZ for redundancy.
+
+We will create private and public subnets to match load balancers and
+RDS instances as well:
+
+1. Select **Subnets** from the left menu.
+1. Click **Create subnet**. Give it a descriptive name tag based on the IP,
+ for example `gitlab-public-10.0.0.0`, select the VPC we created previously,
+ and at the IPv4 CIDR block let's give it a 24 subnet `10.0.0.0/24`:
+
+ ![Create subnet](img/create_subnet.png)
+
+1. Follow the same steps to create all subnets:
+
+ | Name tag | Type |Availability Zone | CIDR block |
+ | -------- | ---- | ---------------- | ---------- |
+ | gitlab-public-10.0.0.0 | public | us-west-2a | 10.0.0.0 |
+ | gitlab-private-10.0.1.0 | private | us-west-2a | 10.0.1.0 |
+ | gitlab-public-10.0.2.0 | public | us-west-2b | 10.0.2.0 |
+ | gitlab-private-10.0.3.0 | private | us-west-2b | 10.0.3.0 |
+
+### Route Table
+
+Up to now all our subnets are private. We need to create a Route Table
+to associate an Internet Gateway. On the same VPC dashboard:
+
+1. Select **Route Tables** from the left menu.
+1. Click **Create Route Table**.
+1. At the "Name tag" enter `gitlab-public` and choose `gitlab-vpc` under "VPC".
+1. Hit **Yes, Create**.
+
+### Internet Gateway
+
+Now, still on the same dashboard, go to Internet Gateways and
+create a new one:
+
+1. Select **Internet Gateways** from the left menu.
+1. Click **Create internet gateway**, give it the name `gitlab-gateway` and
+ click **Create**.
+1. Select it from the table, and then under the **Actions** dropdown choose
+ "Attach to VPC".
+
+ ![Create gateway](img/create_gateway.png)
+
+1. Choose `gitlab-vpc` from the list and hit **Attach**.
+
+### Configuring subnets
+
+We now need to add a new target which will be our Internet Gateway and have
+it receive traffic from any destination.
+
+1. Select **Route Tables** from the left menu and select the `gitlab-public`
+ route to show the options at the bottom.
+1. Select the **Routes** tab, hit **Edit > Add another route** and set `0.0.0.0/0`
+ as destination. In the target, select the `gitlab-gateway` we created previously.
+ Hit **Save** once done.
+
+ ![Associate subnet with gateway](img/associate_subnet_gateway.png)
+
+Next, we must associate the **public** subnets to the route table:
+
+1. Select the **Subnet Associations** tab and hit **Edit**.
+1. Check only the public subnet and hit **Save**.
+
+ ![Associate subnet with gateway](img/associate_subnet_gateway_2.png)
+
+---
+
+Now that we're done with the network, let's create a security group.
+
+## Creating a security group
+
+The security group is basically the firewall:
+
+1. Select **Security Groups** from the left menu.
+1. Click **Create Security Group** and fill in the details. Give it a name,
+ add a description, and choose the VPC we created previously
+1. Select the security group from the list and at the the bottom select the
+ Inbound Rules tab. You will need to open the SSH, HTTP, and HTTPS ports. Set
+ the source to `0.0.0.0/0`.
+
+ ![Create security group](img/create_security_group.png)
+
+ TIP: **Tip:**
+ Based on best practices, you should allow SSH traffic from only a known
+ host or CIDR block. In that case, change the SSH source to be custom and give
+ it the IP you want to SSH from.
+
+1. When done, click **Save**.
+
+## PostgreSQL with RDS
+
+For our database server we will use Amazon RDS which offers Multi AZ
+for redundancy. Let's start by creating a subnet group and then we'll
+create the actual RDS instance.
+
+### RDS Subnet Group
+
+1. Navigate to the RDS dashboard and select **Subnet Groups** from the left menu.
+1. Give it a name (`gitlab-rds-group`), a description, and choose the VPC from
+ the VPC dropdown.
+1. Click "Add all the subnets related to this VPC" and
+ remove the public ones, we only want the **private subnets**.
+ In the end, you should see `10.0.1.0/24` and `10.0.3.0/24` (as
+ we defined them in the [subnets section](#subnets)).
+ Click **Create** when ready.
+
+ ![RDS Subnet Group](img/rds_subnet_group.png)
+
+### Creating the database
+
+Now, it's time to create the database:
+
+1. Select **Instances** from the left menu and click **Create database**.
+1. Select PostgreSQL and click **Next**.
+1. Since this is a production server, let's choose "Production". Click **Next**.
+1. Let's see the instance specifications:
+ 1. Leave the license model as is (`postgresql-license`).
+ 1. For the version, select the latest of the 9.6 series (check the
+ [database requirements](../../install/requirements.md#postgresql-requirements))
+ if there are any updates on this).
+ 1. For the size, let's select a `t2.medium` instance.
+ 1. Multi-AZ-deployment is recommended as redundancy, so choose "Create
+ replica in different zone". Read more at
+ [High Availability (Multi-AZ)](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html).
+ 1. A Provisioned IOPS (SSD) storage type is best suited for HA (though you can
+ choose a General Purpose (SSD) to reduce the costs). Read more about it at
+ [Storage for Amazon RDS](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html).
+
+1. The rest of the settings on this page request a DB isntance identifier, username
+ and a master password. We've chosen to use `gitlab-db-ha`, `gitlab` and a
+ very secure password respectively. Keep these in hand for later.
+1. Click **Next** to proceed to the advanced settings.
+1. Make sure to choose our gitlab VPC, our subnet group, set public accessibility to
+ **No**, and to leave it to create a new security group. The only additional
+ change which will be helpful is the database name for which we can use
+ `gitlabhq_production`. At the very bottom, there's an option to enable
+ auto updates to minor versions. You may want to turn it off.
+1. When done, click **Create database**.
+
+### Installing the `pg_trgm` extension for PostgreSQL
+
+Once the database is created, connect to your new RDS instance to verify access
+and to install a required extension.
+
+You can find the host or endpoint by selecting the instance you just created and
+after the details drop down you'll find it labeled as 'Endpoint'. Do not to
+include the colon and port number:
+
+```sh
+sudo /opt/gitlab/embedded/bin/psql -U gitlab -h <rds-endpoint> -d gitlabhq_production
+```
+
+At the psql prompt create the extension and then quit the session:
+
+```sh
+psql (9.4.7)
+Type "help" for help.
+
+gitlab=# CREATE EXTENSION pg_trgm;
+gitlab=# \q
+```
+
+---
+
+Now that the database is created, let's move on setting up Redis with ElasticCache.
+
+## Redis with ElastiCache
+
+ElastiCache is an in-memory hosted caching solution. Redis maintains its own
+persistence and is used for certain types of the GitLab application.
+
+To set up Redis:
+
+1. Navigate to the ElastiCache dashboard from your AWS console.
+1. Go to **Subnet Groups** in the left menu, and create a new subnet group.
+ Make sure to select our VPC and its [private subnets](#subnets). Click
+ **Create** when ready.
+
+ ![ElastiCache subnet](img/ec_subnet.png)
+
+1. Select **Redis** on the left menu and click **Create** to create a new
+ Redis cluster. Depending on your load, you can choose whether to enable
+ cluster mode or not. Even without cluster mode on, you still get the
+ chance to deploy Redis in multi availability zones. In this guide, we chose
+ not to enable it.
+1. In the settings section:
+ 1. Give the cluster a name (`gitlab-redis`) and a description.
+ 1. For the version, select the latest of `3.2` series (e.g., `3.2.10`).
+ 1. Select the node type and the number of replicas.
+1. In the advanced settings section:
+ 1. Select the multi-AZ auto-failover option.
+ 1. Select the subnet group we created previously.
+ 1. Manually select the preferred availability zones, and under "Replica 2"
+ choose a different zone than the other two.
+
+ ![Redis availability zones](img/ec_az.png)
+
+1. In the security settings, edit the security groups and choose the
+ `gitlab-security-group` we had previously created.
+1. Leave the rest of the settings to their default values or edit to your liking.
+1. When done, click **Create**.
+
+## RDS and Redis Security Group
+
+Let's navigate to our EC2 security groups and add a small change for our EC2
+instances to be able to connect to RDS. First, copy the security group name we
+defined, namely `gitlab-security-group`, select the RDS security group and edit the
+inbound rules. Choose the rule type to be PostgreSQL and paste the name under
+source.
+
+Similar to the above, jump to the `gitlab-security-group` group
+and add a custom TCP rule for port `6379` accessible within itself.
+
+## Load Balancer
+
+On the EC2 dashboard, look for Load Balancer on the left column:
+
+1. Click the **Create Load Balancer** button.
+ 1. Choose the Application Load Balancer.
+ 1. Give it a name (`gitlab-loadbalancer`) and set the scheme to "internet-facing".
+ 1. In the "Listeners" section, make sure it has HTTP and HTTPS.
+ 1. In the "Availability Zones" section, select the `gitlab-vpc` we have created
+ and associate the **public subnets**.
+1. Click **Configure Security Settings** to go to the next section to
+ select the TLS certificate. When done, go to the next step.
+1. In the "Security Groups" section, create a new one by giving it a name
+ (`gitlab-loadbalancer-sec-group`) and allow both HTTP ad HTTPS traffic
+ from anywhere (`0.0.0.0/0, ::/0`).
+1. In the next step, configure the routing and select an existing target group
+ (`gitlab-public`). The Load Balancer Health will allow us to indicate where to
+ ping and what makes up a healthy or unhealthy instance.
+1. Leave the "Register Targets" section as is, and finally review the settings
+ and create the ELB.
+
+After the Load Balancer is up and running, you can revisit your Security
+Groups to refine the access only through the ELB and any other requirement
+you might have.
+
+## Deploying GitLab inside an auto scaling group
+
+We'll use AWS's wizard to deploy GitLab and then SSH into the instance to
+configure the PostgreSQL and Redis connections.
+
+The Auto Scaling Group option is available through the EC2 dashboard on the left
+sidebar.
+
+1. Click **Create Auto Scaling group**.
+1. Create a new launch configuration.
+
+### Choose the AMI
+
+Choose the AMI:
+
+1. Go to the Community AMIs and search for `GitLab EE <version>`
+ where `<version>` the latest version as seen on the
+ [releases page](https://about.gitlab.com/releases/).
+
+ ![Choose AMI](img/choose_ami.png)
+
+### Choose an instance type
+
+You should choose an instance type based on your workload. Consult
+[the hardware requirements](../requirements.md#hardware-requirements) to choose
+one that fits your needs (at least `c4.xlarge`, which is enough to accommodate 100 users):
+
+1. Choose the your instance type.
+1. Click **Next: Configure Instance Details**.
+
+### Configure details
+
+In this step we'll configure some details:
+
+1. Enter a name (`gitlab-autoscaling`).
+1. Select the IAM role we created.
+1. Optionally, enable CloudWatch and the EBS-optimized instance settings.
+1. In the "Advanced Details" section, set the IP address type to
+ "Do not assign a public IP address to any instances."
+1. Click **Next: Add Storage**.
+
+### Add storage
+
+The root volume is 8GB by default and should be enough given that we won't store
+any data there. Let's create a new EBS volume that will host the Git data. Its
+size depends on your needs and you can always migrate to a bigger volume later.
+You will be able to [set up that volume](#setting-up-the-ebs-volume)
+after the instance is created.
+
+### Configure security group
+
+As a last step, configure the security group:
+
+1. Select the existing load balancer security group we have [created](#load-balancer).
+1. Select **Review**.
+
+### Review and launch
+
+Now is a good time to review all the previous settings. When ready, click
+**Create launch configuration** and select the SSH key pair with which you will
+connect to the instance.
+
+### Create Auto Scaling Group
+
+We are now able to start creating our Auto Scaling Group:
+
+1. Give it a group name.
+1. Set the group size to 2 as we want to always start with two instances.
+1. Assign it our network VPC and add the **private subnets**.
+1. In the "Advanced Details" section, choose to receive traffic from ELBs
+ and select our ELB.
+1. Choose the ELB health check.
+1. Click **Next: Configure scaling policies**.
+
+This is the really great part of Auto Scaling; we get to choose when AWS
+launches new instances and when it removes them. For this group we'll
+scale between 2 and 4 instances where one instance will be added if CPU
+utilization is greater than 60% and one instance is removed if it falls
+to less than 45%.
+
+![Auto scaling group policies](img/policies.png)
+
+Finally, configure notifications and tags as you see fit, and create the
+auto scaling group.
+
+You'll notice that after we save the configuration, AWS starts launching our two
+instances in different AZs and without a public IP which is exactly what
+we intended.
+
+## After deployment
+
+After a few minutes, the instances should be up and accessible via the internet.
+Let's connect to the primary and configure some things before logging in.
+
+### Configuring GitLab to connect with postgres and Redis
+
+While connected to your server, let's connect to the RDS instance to verify
+access and to install a required extension:
+
+```sh
+sudo /opt/gitlab/embedded/bin/psql -U gitlab -h <rds-endpoint> -d gitlabhq_production
+```
+
+Edit the `gitlab.rb` file at `/etc/gitlab/gitlab.rb`
+find the `external_url 'http://gitlab.example.com'` option and change it
+to the domain you will be using or the public IP address of the current
+instance to test the configuration.
+
+For a more detailed description about configuring GitLab, see [Configuring GitLab for HA](../../administration/high_availability/gitlab.md)
+
+Now look for the GitLab database settings and uncomment as necessary. In
+our current case we'll specify the database adapter, encoding, host, name,
+username, and password:
+
+```ruby
+# Disable the built-in Postgres
+postgresql['enable'] = false
+
+# Fill in the connection details
+gitlab_rails['db_adapter'] = "postgresql"
+gitlab_rails['db_encoding'] = "unicode"
+gitlab_rails['db_database'] = "gitlabhq_production"
+gitlab_rails['db_username'] = "gitlab"
+gitlab_rails['db_password'] = "mypassword"
+gitlab_rails['db_host'] = "<rds-endpoint>"
+```
+
+Next, we need to configure the Redis section by adding the host and
+uncommenting the port:
+
+```ruby
+# Disable the built-in Redis
+redis['enable'] = false
+
+# Fill in the connection details
+gitlab_rails['redis_host'] = "<redis-endpoint>"
+gitlab_rails['redis_port'] = 6379
+```
+
+Finally, reconfigure GitLab for the change to take effect:
+
+
+```sh
+sudo gitlab-ctl reconfigure
+```
+
+You might also find it useful to run a check and a service status to make sure
+everything has been setup correctly:
+
+```sh
+sudo gitlab-rake gitlab:check
+sudo gitlab-ctl status
+```
+
+If everything looks good, you should be able to reach GitLab in your browser.
+
+### Setting up the EBS volume
+
+The EBS volume will host the Git repositories data:
+
+1. First, format the `/dev/xvdb` volume and then mount it under the directory
+ where the data will be stored. For example, `/mnt/gitlab-data/`.
+1. Tell GitLab to store its data in the new directory by editing
+ `/etc/gitlab/gitlab.rb` with your editor:
+
+ ```ruby
+ git_data_dirs({
+ "default" => { "path" => "/mnt/gitlab-data" }
+ })
+ ```
+
+ where `/mnt/gitlab-data` the location where you will store the Git data.
+
+1. Save the file and reconfigure GitLab:
+
+ ```sh
+ sudo gitlab-ctl reconfigure
+ ```
+
+TIP: **Tip:**
+If you wish to add more than one data volumes to store the Git repositories,
+read the [repository storage paths docs](../../administration/repository_storage_paths.md).
+
+### Setting up Gitaly
+
+Gitaly is a service that provides high-level RPC access to Git repositories.
+It should be enabled and configured in a separate EC2 instance on the
+[private VPC](#subnets) we configured previously.
+
+Follow the [documentation to set up Gitaly](../../administration/gitaly/index.md).
+
+### Using Amazon S3 object storage
+
+GitLab stores many objects outside the Git repository, many of which can be
+uploaded to S3. That way, you can offload the root disk volume of these objects
+which would otherwise take much space.
+
+In particular, you can store in S3:
+
+- [The Git LFS objects](../../workflow/lfs/lfs_administration.md#s3-for-omnibus-installations) ((Omnibus GitLab installations))
+- [The Container Registry images](../../administration/container_registry.md#container-registry-storage-driver) (Omnibus GitLab installations)
+- [The GitLab CI/CD job artifacts](../../administration/job_artifacts.md#using-object-storage) (Omnibus GitLab installations)
+
+### Setting up a domain name
+
+After you SSH into the instance, configure the domain name:
+
+1. Open `/etc/gitlab/gitlab.rb` with your preferred editor.
+1. Edit the `external_url` value:
+
+ ```ruby
+ external_url 'http://example.com'
+ ```
+
+1. Reconfigure GitLab:
+
+ ```sh
+ sudo gitlab-ctl reconfigure
+ ```
+
+You should now be able to reach GitLab at the URL you defined. To use HTTPS
+(recommended), see the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+
+### Logging in for the first time
+
+If you followed the previous section, you should be now able to visit GitLab
+in your browser. The very first time, you will be asked to set up a password
+for the `root` user which has admin privileges on the GitLab instance.
+
+After you set it up, login with username `root` and the newly created password.
+
+## Health check and monitoring with Prometheus
+
+Apart from Amazon's Cloudwatch which you can enable on various services,
+GitLab provides its own integrated monitoring solution based on Prometheus.
+For more information on how to set it up, visit the
+[GitLab Prometheus documentation](../../administration/monitoring/prometheus/index.md)
+
+GitLab also has various [health check endpoints](../..//user/admin_area/monitoring/health_check.md)
+that you can ping and get reports.
+
+## GitLab Runners
+
+If you want to take advantage of [GitLab CI/CD](../../ci/README.md), you have to
+set up at least one [GitLab Runner](https://docs.gitlab.com/runner/).
+
+Read more on configuring an
+[autoscaling GitLab Runner on AWS](https://docs.gitlab.com/runner/configuration/runner_autoscale_aws/).
+
+## Backup and restore
+
+GitLab provides [a tool to backup](../../raketasks/backup_restore.md#creating-a-backup-of-the-gitlab-system)
+and restore its Git data, database, attachments, LFS objects, etc.
+
+Some important things to know:
+
+- The backup/restore tool **does not** store some configuration files, like secrets; you'll
+ need to [configure this yourself](../../raketasks/backup_restore.md#storing-configuration-files).
+- By default, the backup files are stored locally, but you can
+ [backup GitLab using S3](../../raketasks/backup_restore.md#using-amazon-s3).
+- You can [exclude specific directories form the backup](../../raketasks/backup_restore.md#excluding-specific-directories-from-the-backup).
+
+### Backing up GitLab
+
+To back up GitLab:
+
+1. SSH into your instance.
+1. Take a backup:
+
+ ```sh
+ sudo gitlab-rake gitlab:backup:create
+ ```
+
+### Restoring GitLab from a backup
+
+To restore GitLab, first review the [restore documentation](../../raketasks/backup_restore.md#restore),
+and primarily the restore prerequisites. Then, follow the steps under the
+[Omnibus installations section](../../raketasks/backup_restore.md#restore-for-omnibus-installations).
+
+## Updating GitLab
+
+GitLab releases a new version every month on the 22nd. Whenever a new version is
+released, you can update your GitLab instance:
+
+1. SSH into your instance
+1. Take a backup:
+
+ ```sh
+ sudo gitlab-rake gitlab:backup:create
+ ```
+
+1. Update the repositories and install GitLab:
+
+ ```sh
+ sudo apt update
+ sudo apt install gitlab-ee
+ ```
+
+After a few minutes, the new version should be up and running.
+
+## Conclusion
+
+In this guide, we went mostly through scaling and some redundancy options,
+your mileage may vary.
+
+Keep in mind that all Highly Available solutions come with a trade-off between
+cost/complexity and uptime. The more uptime you want, the more complex the solution.
+And the more complex the solution, the more work is involved in setting up and
+maintaining it.
+
+Have a read through these other resources and feel free to
+[open an issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/new)
+to request additional material:
+
+- [GitLab High Availability](https://docs.gitlab.com/ee/administration/high_availability/):
+ GitLab supports several different types of clustering and high-availability.
+- [Geo replication](https://docs.gitlab.com/ee/administration/geo/replication/):
+ Geo is the solution for widely distributed development teams.
+- [Omnibus GitLab](https://docs.gitlab.com/omnibus/) - Everything you need to know
+ about administering your GitLab instance.
+- [Upload a license](https://docs.gitlab.com/ee/user/admin_area/license.html):
+ Activate all GitLab Enterprise Edition functionality with a license.
+- [Pricing](https://about.gitlab.com/pricing): Pricing for the different tiers.
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index 21694b02d18..7835401cc0b 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -71,17 +71,19 @@ The first items we need to configure are the basic settings of the underlying vi
1. Enter a `User name` - e.g. **"gitlab-admin"**
1. Select an `Authentication type`, either **SSH public key** or **Password**:
- >**Note:** if you're unsure which authentication type to use, select **Password**
+ > **Note:** if you're unsure which authentication type to use, select **Password**
1. If you chose **SSH public key** - enter your `SSH public key` into the field provided
- _(read the [SSH documentation][GitLab-Docs-SSH] to learn more about how to setup SSH
+ _(read the [SSH documentation][GitLab-Docs-SSH] to learn more about how to set up SSH
public keys)_
1. If you chose **Password** - enter the password you wish to use _(this is the password that you
will use later in this tutorial to [SSH] into the VM, so make sure it's a strong password/passphrase)_
1. Choose the appropriate `Subscription` tier for your Azure account
1. Choose an existing `Resource Group` or create a new one - e.g. **"GitLab-CE-Azure"**
->**Note:** a "Resource group" is a way to group related resources together for easier administration.
-We chose "GitLab-CE-Azure", but your resource group can have the same name as your VM.
+
+ > **Note:** a "Resource group" is a way to group related resources together for easier administration.
+ > We chose "GitLab-CE-Azure", but your resource group can have the same name as your VM.
+
1. Choose a `Location` - if you're unsure, select the default location
Here are the settings we've used:
@@ -95,7 +97,7 @@ Check the settings you have entered, and then click **"OK"** when you're ready t
Next, you need to choose the size of your VM - selecting features such as the number of CPU cores,
the amount of RAM, the size of storage (and its speed), etc.
->**Note:** in common with other cloud vendors, Azure operates a resource/usage pricing model, i.e.
+> **Note:** in common with other cloud vendors, Azure operates a resource/usage pricing model, i.e.
the more resources your VM consumes the more it will cost you to run, so make your selection
carefully. You'll see that Azure provides an _estimated_ monthly cost beneath each VM Size to help
guide your selection.
@@ -106,7 +108,7 @@ ahead and select this one, but please choose the size which best meets your own
![Azure - Create Virtual Machine - Size](img/azure-create-virtual-machine-size.png)
->**Note:** be aware that whilst your VM is active (known as "allocated"), it will incur
+> **Note:** be aware that whilst your VM is active (known as "allocated"), it will incur
"compute charges" which, ultimately, you will be billed for. So, even if you're using the
free trial credits, you'll likely want to learn
[how to properly shutdown an Azure VM to save money][Azure-Properly-Shutdown-VM].
@@ -132,7 +134,7 @@ new VM. You'll be billed only for the VM itself (e.g. "Standard DS1 v2") because
![Azure - Create Virtual Machine - Purchase](img/azure-create-virtual-machine-purchase.png)
->**Note:** at this stage, you can review and modify the any of the settings you have made during all
+> **Note:** at this stage, you can review and modify the any of the settings you have made during all
previous steps, just click on any of the four steps to re-open them.
When you have read and agreed to the terms of use and are ready to proceed, click **"Purchase"**.
@@ -152,7 +154,7 @@ on the Azure Dashboard (you may need to refresh the page):
The new VM can also be accessed by clicking the `All resources` or `Virtual machines` icons in the
Azure Portal sidebar navigation menu.
-## Setup a domain name
+## Set up a domain name
The VM will have a public IP address (static by default), but Azure allows us to assign a friendly
DNS name to the VM, so let's go ahead and do that.
@@ -174,7 +176,7 @@ _(the full domain name of your own VM will be different, of course)_.
Click **"Save"** for the changes to take effect.
->**Note:** if you want to use your own domain name, you will need to add a DNS `A` record at your
+> **Note:** if you want to use your own domain name, you will need to add a DNS `A` record at your
domain registrar which points to the public IP address of your Azure VM. If you do this, you'll need
to make sure your VM is configured to use a _static_ public IP address (i.e. not a _dynamic_ one)
or you will have to reconfigure the DNS `A` record each time Azure reassigns your VM a new public IP
@@ -190,7 +192,7 @@ Ports are opened by adding _security rules_ to the **"Network security group"**
has been assigned to. If you followed the process above, then Azure will have automatically created
an NSG named `GitLab-CE-nsg` and assigned the `GitLab-CE` VM to it.
->**Note:** if you gave your VM a different name then the NSG automatically created by Azure will
+> **Note:** if you gave your VM a different name then the NSG automatically created by Azure will
also have a different name - the name you have your VM, with `-nsg` appended to it.
You can navigate to the NSG settings via many different routes in the Azure Portal, but one of the
@@ -294,7 +296,7 @@ homepage for the project:
![GitLab - Empty Project](img/gitlab-project-home-empty.png)
If you scroll further down the project's home page, you'll see some basic instructions on how to
-setup a local clone of your new repository and push and pull from it:
+set up a local clone of your new repository and push and pull from it:
![GitLab - Empty Project - Basic Instructions](img/gitlab-project-home-instructions.png)
@@ -321,7 +323,7 @@ Under the **"Components"** section, we can see that our VM is currently running
GitLab. This is the version of GitLab which was contained in the Azure Marketplace
**"GitLab Community Edition"** offering we used to build the VM when we wrote this tutorial.
->**Note:** The version of GitLab in your own VM instance may well be different, but the update
+> **Note:** The version of GitLab in your own VM instance may well be different, but the update
process will still be the same.
### Connect via SSH
@@ -333,11 +335,11 @@ connect to it using SSH ([Secure Shell][SSH]).
If you're running Windows, you'll need to connect using [PuTTY] or an equivalent Windows SSH client.
If you're running Linux or macOS, then you already have an SSH client installed.
->**Note:**
-- Remember that you will need to login with the username and password you specified
-[when you created](#basics) your Azure VM
-- If you need to reset your VM password, read
-[how to reset SSH credentials for a user on an Azure VM][Azure-Troubleshoot-SSH-Connection].
+> **Note:**
+> - Remember that you will need to login with the username and password you specified
+> [when you created](#basics) your Azure VM
+> - If you need to reset your VM password, read
+> [how to reset SSH credentials for a user on an Azure VM][Azure-Troubleshoot-SSH-Connection].
#### SSH from the command-line
@@ -345,7 +347,7 @@ If you're running [SSH] from the command-line (terminal), then type in the follo
connect to your VM, substituting `username` and `your-azure-domain-name.com` for the correct values.
Again, remember that your Azure VM domain name will be the one you
-[setup previously in the tutorial](#set-up-a-domain-name). If you didn't setup a domain name for
+[set up previously in the tutorial](#set-up-a-domain-name). If you didn't set up a domain name for
your VM, you can use the IP address in its place in the following command:
```bash
@@ -399,7 +401,7 @@ is now showing **"up-to-date"**:
Naturally, we believe that GitLab is a great git repository tool. However, GitLab is a whole lot
more than that too. GitLab unifies issues, code review, CI and CD into a single UI, helping you to
move faster from idea to production, and in this tutorial we showed you how quick and easy it is to
-setup and run your own instance of GitLab on Azure, Microsoft's cloud service.
+set up and run your own instance of GitLab on Azure, Microsoft's cloud service.
Azure is a great way to experiment with GitLab, and if you decide (as we hope) that GitLab is for
you, you can continue to use Azure as your secure, scalable cloud provider or of course run GitLab
@@ -422,7 +424,7 @@ Check out our other [Technical Articles][GitLab-Technical-Articles] or browse th
- [Azure - Properly Shutdown an Azure VM][Azure-Properly-Shutdown-VM]
- [SSH], [PuTTY] and [Using SSH in PuTTY][Using-SSH-In-Putty]
-[Original-Blog-Post]: https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/ "How to Setup a GitLab Instance on Microsoft Azure"
+[Original-Blog-Post]: https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/ "How to Set up a GitLab Instance on Microsoft Azure"
[GitLab-Docs]: https://docs.gitlab.com/ce/README.html "GitLab Documentation"
[GitLab-Technical-Articles]: https://docs.gitlab.com/ce/articles/index.html "GitLab Technical Articles"
[GitLab-Docs-SSH]: https://docs.gitlab.com/ce/ssh/README.html "GitLab Documentation: SSH"
diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md
index e1af086f418..4cb8ca4f3e7 100644
--- a/doc/install/database_mysql.md
+++ b/doc/install/database_mysql.md
@@ -1,15 +1,20 @@
# Database MySQL
->**Note:**
-- We do not recommend using MySQL due to various issues. For example, case
-[(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html)
-and [problems](https://bugs.mysql.com/bug.php?id=65830) that
-[suggested](https://bugs.mysql.com/bug.php?id=50909)
-[fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164).
+NOTE: **Note:**
+We do not recommend using MySQL due to various issues.
+For example, there have been bugs with case
+[(in)sensitivity](https://dev.mysql.com/doc/refman/5.7/en/case-sensitivity.html).
+
+Bugs relating to case sensitivity:
+
+- <https://bugs.mysql.com/bug.php?id=65830>
+- <https://bugs.mysql.com/bug.php?id=50909>
+- <https://bugs.mysql.com/bug.php?id=65830>
+- <https://bugs.mysql.com/bug.php?id=63164>
## Initial database setup
-```
+```sh
# Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
@@ -79,13 +84,14 @@ After installation or upgrade, remember to [convert any new tables](#tables-and-
---
-GitLab 8.14 has introduced [a feature](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7420) requiring `utf8mb4` encoding to be supported in your GitLab MySQL Database, which is not the case if you have setup your database before GitLab 8.16.
+GitLab 8.14 has introduced [a feature](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7420) requiring `utf8mb4` encoding to be supported in your GitLab MySQL Database, which is not the case if you have set up your database before GitLab 8.16.
Follow the below instructions to ensure you use the most up to date requirements for your GitLab MySQL Database.
**We are about to do the following:**
+
- Ensure you can enable `utf8mb4` encoding and `utf8mb4_general_ci` collation for your GitLab DB, tables and data.
-- Convert your GitLab tables and data from `utf8`/`utf8_general_ci` to `utf8mb4`/`utf8mb4_general_ci`
+- Convert your GitLab tables and data from `utf8`/`utf8_general_ci` to `utf8mb4`/`utf8mb4_general_ci`.
### Check for utf8mb4 support
@@ -130,7 +136,8 @@ We need to check, enable and maybe convert your existing GitLab DB tables to the
Whatever the results of your checks above, we now need to check if your GitLab database has been created using [InnoDB File-Per-Table Tablespaces](http://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) (i.e. `innodb_file_per_table` was set to **1** at initial setup time).
-> Note: This setting is [enabled by default](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_file_per_table) since MySQL 5.6.6.
+NOTE: **Note:**
+This setting is [enabled by default](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_file_per_table) since MySQL 5.6.6.
# Run this command with root privileges, replace the data dir if different:
sudo ls -lh /var/lib/mysql/gitlabhq_production/*.ibd | wc -l
@@ -138,18 +145,19 @@ Whatever the results of your checks above, we now need to check if your GitLab d
# Run this command with root privileges, replace the data dir if different:
sudo ls -lh /var/lib/mysql/gitlabhq_production/*.frm | wc -l
-
- **Case 1: a result > 0 for both commands**
-Congrats, your GitLab database uses the right InnoDB tablespace format.
+Congratulations, your GitLab database uses the right InnoDB tablespace format.
However, you must still ensure that any **future tables** created by GitLab will still use the right format:
- If `SELECT @@innodb_file_per_table` returned **1** previously, your server is running correctly.
-> It's however a requirement to check *now* that this setting is indeed persisted in your [my.cnf](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) file!
+
+ > It's however a requirement to check *now* that this setting is indeed persisted in your [`my.cnf`](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) file!
- If `SELECT @@innodb_file_per_table` returned **0** previously, your server is not running correctly.
-> [Enable innodb_file_per_table](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) by running in a MySQL session as root the command `SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda;` and persist the two settings in your [my.cnf](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) file
+
+ > [Enable innodb_file_per_table](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) by running in a MySQL session as root the command `SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda;` and persist the two settings in your [`my.cnf`](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) file.
Now, if you have a **different result** returned by the 2 commands above, it means you have a **mix of tables format** uses in your GitLab database. This can happen if your MySQL server had different values for `innodb_file_per_table` in its life and you updated GitLab at different moments with those inconsistent values. So keep reading.
@@ -170,7 +178,7 @@ Let's enable what we need on the running server:
# You can now quit the database session
mysql> \q
-> Now, **persist** [innodb_file_per_table](https://dev.mysql.com/doc/refman/5.6/en/tablespace-enabling.html) and [innodb_file_format](https://dev.mysql.com/doc/refman/5.6/en/innodb-file-format-enabling.html) in your `my.cnf` file.
+> Now, **persist** [innodb_file_per_table](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) and [innodb_file_format](https://dev.mysql.com/doc/refman/5.7/en/innodb-file-format-enabling.html) in your `my.cnf` file.
Ensure at this stage that your GitLab instance is indeed **stopped**.
@@ -182,7 +190,7 @@ Now, let's convert all the GitLab database tables to the new tablespace format:
# Type the MySQL root password
mysql > use gitlabhq_production;
- # Safety check: you should still have those values set as follow:
+ # Safety check: you should still have those values set as follows:
mysql> SELECT @@innodb_file_per_table, @@innodb_file_format;
+-------------------------+----------------------+
| @@innodb_file_per_table | @@innodb_file_format |
@@ -201,7 +209,7 @@ Now, let's convert all the GitLab database tables to the new tablespace format:
#### Check for proper InnoDB File Format, Row Format, Large Prefix and tables conversion
-We need to check, enable and probably convert your existing GitLab DB tables to use the [Barracuda InnoDB file format](https://dev.mysql.com/doc/refman/5.6/en/innodb-file-format.html), the [DYNAMIC row format](https://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_dynamic_row_format) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) as a second prerequisite for supporting **utfb8mb4 with long indexes** used by recent GitLab databases.
+We need to check, enable and probably convert your existing GitLab DB tables to use the [Barracuda InnoDB file format](https://dev.mysql.com/doc/refman/5.7/en/innodb-file-format.html), the [DYNAMIC row format](https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_dynamic_row_format) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) as a second prerequisite for supporting **utfb8mb4 with long indexes** used by recent GitLab databases.
# Login to MySQL
mysql -u root -p
@@ -227,7 +235,7 @@ We need to check, enable and probably convert your existing GitLab DB tables to
| utf8 | utf8_general_ci |
+--------------------------+----------------------+
-> Now, ensure that [innodb_file_format](https://dev.mysql.com/doc/refman/5.6/en/tablespace-enabling.html) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) are **persisted** in your `my.cnf` file.
+> Now, ensure that [innodb_file_format](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) are **persisted** in your `my.cnf` file.
#### Tables and data conversion to utf8mb4
@@ -255,7 +263,7 @@ Now that you have a persistent MySQL setup, you can safely upgrade tables after
Ensure your GitLab database configuration file uses a proper connection encoding and collation:
-```sudo -u git -H editor config/database.yml```
+`sudo -u git -H editor config/database.yml`
production:
adapter: mysql2
@@ -264,19 +272,19 @@ Ensure your GitLab database configuration file uses a proper connection encoding
[Restart your GitLab instance](../administration/restart_gitlab.md).
-
## MySQL strings limits
After installation or upgrade, remember to run the `add_limits_mysql` Rake task:
**Omnibus GitLab installations**
-```
+
+```sh
sudo gitlab-rake add_limits_mysql
```
**Installations from source**
-```
+```sh
bundle exec rake add_limits_mysql RAILS_ENV=production
```
diff --git a/doc/install/digitaloceandocker.md b/doc/install/digitaloceandocker.md
index 8efc0530b8a..d67695d75b4 100644
--- a/doc/install/digitaloceandocker.md
+++ b/doc/install/digitaloceandocker.md
@@ -1,7 +1,8 @@
# Digital Ocean and Docker Machine test environment
-## Warning. This guide is for quickly testing different versions of GitLab and
-## not recommended for ease of future upgrades or keeping the data you create.
+CAUTION: **Caution:**
+This guide is for quickly testing different versions of GitLab and not recommended for ease of
+future upgrades or keeping the data you create.
## Initial setup
@@ -12,92 +13,88 @@ locally on either macOS or Linux.
#### Install Docker Toolbox
-1. [https://www.docker.com/products/docker-toolbox](https://www.docker.com/products/docker-toolbox)
+- <https://www.docker.com/products/docker-toolbox>
### On Linux
#### Install Docker Engine
-1. [https://docs.docker.com/engine/installation/linux](https://docs.docker.com/engine/installation/linux/)
+- <https://docs.docker.com/engine/installation/linux/>
#### Install Docker Machine
-1. [https://docs.docker.com/machine/install-machine](https://docs.docker.com/machine/install-machine/)
+- <https://docs.docker.com/machine/install-machine/>
-_The rest of the steps are identical for macOS and Linux_
+NOTE: **Note:**
+The rest of the steps are identical for macOS and Linux.
### Create new docker host
-1. Login to Digital Ocean
-1. Generate a new API token at https://cloud.digitalocean.com/settings/api/tokens
+1. Login to Digital Ocean.
+1. Generate a new API token at <https://cloud.digitalocean.com/settings/api/tokens>.
+ This command will create a new DO droplet called `gitlab-test-env-do` that will act as a docker host.
-This command will create a new DO droplet called `gitlab-test-env-do` that will act as a docker host.
+ NOTE: **Note:**
+ 4GB is the minimum requirement for a Docker host that will run more than one GitLab instance.
-**Note: 4GB is the minimum requirement for a Docker host that will run more then one GitLab instance**
+ - RAM: 4GB
+ - Name: `gitlab-test-env-do`
+ - Driver: `digitalocean`
-+ RAM: 4GB
-+ Name: `gitlab-test-env-do`
-+ Driver: `digitalocean`
+1. Set the DO token:
+ ```sh
+ export DOTOKEN=<your generated token>
+ ```
-**Set the DO token** - Replace the string below with your generated token
+1. Create the machine:
-```
-export DOTOKEN=cf3dfd0662933203005c4a73396214b7879d70aabc6352573fe178d340a80248
-```
-
-**Create the machine**
-
-```
-docker-machine create \
- --driver digitalocean \
- --digitalocean-access-token=$DOTOKEN \
- --digitalocean-size "4gb" \
- gitlab-test-env-do
-```
-
-+ Resource: https://docs.docker.com/machine/drivers/digital-ocean/
+ ```sh
+ docker-machine create \
+ --driver digitalocean \
+ --digitalocean-access-token=$DOTOKEN \
+ --digitalocean-size "4gb" \
+ gitlab-test-env-do
+ ```
+Resource: <https://docs.docker.com/machine/drivers/digital-ocean/>.
### Creating GitLab test instance
-
#### Connect your shell to the new machine
-
In this example we'll create a GitLab EE 8.10.8 instance.
-
First connect the docker client to the docker host you created previously.
-```
+```sh
eval "$(docker-machine env gitlab-test-env-do)"
```
You can add this to your `~/.bash_profile` file to ensure the `docker` client uses the `gitlab-test-env-do` docker host
-
#### Create new GitLab container
-+ HTTP port: `8888`
-+ SSH port: `2222`
- + Set `gitlab_shell_ssh_port` using `--env GITLAB_OMNIBUS_CONFIG `
-+ Hostname: IP of docker host
-+ Container name: `gitlab-test-8.10`
-+ GitLab version: **EE** `8.10.8-ee.0`
+- HTTP port: `8888`
+- SSH port: `2222`
+ - Set `gitlab_shell_ssh_port` using `--env GITLAB_OMNIBUS_CONFIG`
+- Hostname: IP of docker host
+- Container name: `gitlab-test-8.10`
+- GitLab version: **EE** `8.10.8-ee.0`
-##### Setup container settings
+##### Set up container settings
-```
+```sh
export SSH_PORT=2222
export HTTP_PORT=8888
export VERSION=8.10.8-ee.0
export NAME=gitlab-test-8.10
```
-##### Create container
-```
+##### Create container
+
+```sh
docker run --detach \
--env GITLAB_OMNIBUS_CONFIG="external_url 'http://$(docker-machine ip gitlab-test-env-do):$HTTP_PORT'; gitlab_rails['gitlab_shell_ssh_port'] = $SSH_PORT;" \
--hostname $(docker-machine ip gitlab-test-env-do) \
@@ -110,23 +107,20 @@ gitlab/gitlab-ee:$VERSION
##### Retrieve the docker host IP
-```
+```sh
docker-machine ip gitlab-test-env-do
# example output: 192.168.151.134
```
-
-+ Browse to: http://192.168.151.134:8888/
-
+Browse to: <http://192.168.151.134:8888/>.
##### Execute interactive shell/edit configuration
-
-```
+```sh
docker exec -it $NAME /bin/bash
```
-```
+```sh
# example commands
root@192:/# vi /etc/gitlab/gitlab.rb
root@192:/# gitlab-ctl reconfigure
@@ -134,6 +128,6 @@ root@192:/# gitlab-ctl reconfigure
#### Resources
-+ [https://docs.gitlab.com/omnibus/docker/](https://docs.gitlab.com/omnibus/docker/)
-+ [https://docs.docker.com/machine/get-started/](https://docs.docker.com/machine/get-started/)
-+ [https://docs.docker.com/machine/reference/ip/](https://docs.docker.com/machine/reference/ip/)+
+- <https://docs.gitlab.com/omnibus/docker/>.
+- <https://docs.docker.com/machine/get-started/>.
+- <https://docs.docker.com/machine/reference/ip/>.
diff --git a/doc/install/google_cloud_platform/img/boot_disk.png b/doc/install/google_cloud_platform/img/boot_disk.png
index 37b2d9eaae7..b9f7eed6601 100644
--- a/doc/install/google_cloud_platform/img/boot_disk.png
+++ b/doc/install/google_cloud_platform/img/boot_disk.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/first_signin.png b/doc/install/google_cloud_platform/img/first_signin.png
index 6eb3392d674..1e218abf63d 100644
--- a/doc/install/google_cloud_platform/img/first_signin.png
+++ b/doc/install/google_cloud_platform/img/first_signin.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_landing.png b/doc/install/google_cloud_platform/img/gcp_landing.png
index d6390c4dd4f..92a9873728c 100644
--- a/doc/install/google_cloud_platform/img/gcp_landing.png
+++ b/doc/install/google_cloud_platform/img/gcp_landing.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/launch_vm.png b/doc/install/google_cloud_platform/img/launch_vm.png
index 3fd13f232bb..53cb23277fd 100644
--- a/doc/install/google_cloud_platform/img/launch_vm.png
+++ b/doc/install/google_cloud_platform/img/launch_vm.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/ssh_terminal.png b/doc/install/google_cloud_platform/img/ssh_terminal.png
index 6a1a418d8e9..171cb572074 100644
--- a/doc/install/google_cloud_platform/img/ssh_terminal.png
+++ b/doc/install/google_cloud_platform/img/ssh_terminal.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/vm_created.png b/doc/install/google_cloud_platform/img/vm_created.png
index fb467f40838..0ba422af60c 100644
--- a/doc/install/google_cloud_platform/img/vm_created.png
+++ b/doc/install/google_cloud_platform/img/vm_created.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/vm_details.png b/doc/install/google_cloud_platform/img/vm_details.png
index 2d230416a4b..85b9ca066c8 100644
--- a/doc/install/google_cloud_platform/img/vm_details.png
+++ b/doc/install/google_cloud_platform/img/vm_details.png
Binary files differ
diff --git a/doc/install/installation.md b/doc/install/installation.md
index ea01d88d85f..cac97b63d92 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -12,7 +12,7 @@ Since installations from source don't have Runit, Sidekiq can't be terminated an
## Select Version to Install
-Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-2-stable`).
+Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-5-stable`).
You can select the branch in the version dropdown in the top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the [GitLab Blog](https://about.gitlab.com/blog/) for installation guide links by version.
@@ -103,7 +103,7 @@ Is the system packaged Git too old? Remove it and compile from source.
# When editing config/gitlab.yml (Step 5), change the git -> bin_path to /usr/local/bin/git
-**Note:** In order to receive mail notifications, make sure to install a mail server. By default, Debian is shipped with exim4 but this [has problems](https://github.com/gitlabhq/gitlabhq/issues/4866#issuecomment-32726573) while Ubuntu does not ship with one. The recommended mail server is postfix and you can install it with:
+**Note:** In order to receive mail notifications, make sure to install a mail server. By default, Debian is shipped with exim4 but this [has problems](https://gitlab.com/gitlab-org/gitlab-ce/issues/12754) while Ubuntu does not ship with one. The recommended mail server is postfix and you can install it with:
sudo apt-get install -y postfix
@@ -132,9 +132,9 @@ Remove the old Ruby 1.8 if present:
Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby
- curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.4.tar.gz
- echo 'ec82b0d53bd0adad9b19e6b45e44d54e9ec3f10c ruby-2.4.4.tar.gz' | shasum -c - && tar xzf ruby-2.4.4.tar.gz
- cd ruby-2.4.4
+ curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.3.tar.gz
+ echo 'f919a9fbcdb7abecd887157b49833663c5c15fda ruby-2.5.3.tar.gz' | shasum -c - && tar xzf ruby-2.5.3.tar.gz
+ cd ruby-2.5.3
./configure --disable-install-rdoc
make
@@ -153,7 +153,7 @@ page](https://golang.org/dl).
# Remove former Go installation folder
sudo rm -rf /usr/local/go
-
+
curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
@@ -300,9 +300,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-2-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-5-stable gitlab
-**Note:** You can change `11-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `11-5-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
@@ -457,12 +457,36 @@ GitLab-Pages uses [GNU Make](https://www.gnu.org/software/make/). This step is o
sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
sudo -u git -H make
+### Install Gitaly
+
+ # Fetch Gitaly source with Git and compile with Go
+ sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,/home/git/repositories]" RAILS_ENV=production
+
+You can specify a different Git repository by providing it as an extra parameter:
+
+ sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,/home/git/repositories,https://example.com/gitaly.git]" RAILS_ENV=production
+
+Next, make sure gitaly configured:
+
+ # Restrict Gitaly socket access
+ sudo chmod 0700 /home/git/gitlab/tmp/sockets/private
+ sudo chown git /home/git/gitlab/tmp/sockets/private
+
+ # If you are using non-default settings you need to update config.toml
+ cd /home/git/gitaly
+ sudo -u git -H editor config.toml
+
+For more information about configuring Gitaly see
+[doc/administration/gitaly](../administration/gitaly).
+
### Initialize Database and Activate Advanced Features
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
-
# Type 'yes' to create the database tables.
+ # or you can skip the question by adding force=yes
+ sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production force=yes
+
# When done you see 'Administrator account created:'
**Note:** You can set the Administrator/root password and e-mail by supplying them in environmental variables, `GITLAB_ROOT_PASSWORD` and `GITLAB_ROOT_EMAIL` respectively, as seen below. If you don't set the password (and it is set to the default one) please wait with exposing GitLab to the public internet until the installation is done and you've logged into the server the first time. During the first login you'll be forced to change the default password.
@@ -491,29 +515,7 @@ Make GitLab start on boot:
sudo update-rc.d gitlab defaults 21
-### Install Gitaly
-
- # Fetch Gitaly source with Git and compile with Go
- sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_ENV=production
-
-You can specify a different Git repository by providing it as an extra parameter:
-
- sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,https://example.com/gitaly.git]" RAILS_ENV=production
-
-Next, make sure gitaly configured:
-
- # Restrict Gitaly socket access
- sudo chmod 0700 /home/git/gitlab/tmp/sockets/private
- sudo chown git /home/git/gitlab/tmp/sockets/private
-
- # If you are using non-default settings you need to update config.toml
- cd /home/git/gitaly
- sudo -u git -H editor config.toml
-
-For more information about configuring Gitaly see
-[doc/administration/gitaly](../administration/gitaly).
-
-### Setup Logrotate
+### Set up Logrotate
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md
index 692f81dd7cd..3f5b36f7254 100644
--- a/doc/install/kubernetes/gitlab_chart.md
+++ b/doc/install/kubernetes/gitlab_chart.md
@@ -1,11 +1,12 @@
# GitLab Helm Chart
-> **Note:** The chart is currently **beta**, if you encounter any problems please [open an issue](https://gitlab.com/charts/gitlab/issues/new).
-For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview).
+This is the official and recommended way to install GitLab on a cloud native environment.
+For more information on other available GitLab Helm Charts, see the [charts overview](index.md#chart-overview).
## Introduction
-The `gitlab` chart is the best way to operate GitLab on Kubernetes. This chart contains all the required components to get started, and can scale to large deployments.
+The `gitlab` chart is the best way to operate GitLab on Kubernetes. This chart
+contains all the required components to get started, and can scale to large deployments.
The default deployment includes:
@@ -14,78 +15,94 @@ The default deployment includes:
- An auto-scaling, unprivileged [GitLab Runner](https://docs.gitlab.com/runner/) using the Kubernetes executor
- Automatically provisioned SSL via [Let's Encrypt](https://letsencrypt.org/).
-### Limitations
+## Limitations
-Some features and functions are not currently available in the beta release.
-For details, see [known issues and limitations](https://gitlab.com/charts/gitlab/blob/master/doc/architecture/beta.md#known-issues-and-limitations) in the charts repository.
+Some features of GitLab are not currently available:
-## Prerequisites
+- [GitLab Pages](https://gitlab.com/charts/gitlab/issues/37)
+- [GitLab Geo](https://gitlab.com/charts/gitlab/issues/8)
+- [No in-cluster HA database](https://gitlab.com/charts/gitlab/issues/48)
+- MySQL will not be supported, as support is [deprecated within GitLab](https://docs.gitlab.com/omnibus/settings/database.html#using-a-mysql-database-management-server-enterprise-edition-only)
-In order to deploy GitLab on Kubernetes, a few prerequisites are required.
+## Installing GitLab using the Helm Chart
+
+The `gitlab` chart includes all required dependencies, and takes a few minutes
+to deploy.
+
+TIP: **Tip:**
+For production deployments, we strongly recommend using the
+[detailed installation instructions](https://gitlab.com/charts/gitlab/blob/master/doc/installation/index.md)
+utilizing [external Postgres, Redis, and object storage](https://gitlab.com/charts/gitlab/tree/master/doc/advanced) services.
+
+### Requirements
+
+In order to deploy GitLab on Kubernetes, the following are required:
1. `helm` and `kubectl` [installed on your computer](preparation/tools_installation.md).
1. A Kubernetes cluster, version 1.8 or higher. 6vCPU and 16GB of RAM is recommended.
- * [Google GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-container-cluster)
- * [Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html)
- * [Microsoft AKS](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal)
+ - [Google GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-container-cluster)
+ - [Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html)
+ - [Microsoft AKS](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal)
1. A [wildcard DNS entry and external IP address](preparation/networking.md)
1. [Authenticate and connect](preparation/connect.md) to the cluster
1. Configure and initialize [Helm Tiller](preparation/tiller.md).
-## Configuring and Installing GitLab
+### Deployment of GitLab to Kubernetes
-> **Note**: For deployments to Amazon EKS, there are [additional configuration requirements](preparation/eks.md).
+To deploy GitLab, the following three parameters are required:
-For simple deployments, running all services within Kubernetes, only three parameters are required:
-- `global.hosts.domain`: the [base domain](preparation/networking.md) of the wildcard host entry. For example, `mycompany.io` if the wild card entry is `*.mycompany.io`.
-- `global.hosts.externalIP`: the [external IP](preparation/networking.md) which the wildcard DNS resolves to.
-- `certmanager-issuer.email`: Email address to use when requesting new SSL certificates from Let's Encrypt.
+- `global.hosts.domain`: the [base domain](preparation/networking.md) of the
+ wildcard host entry. For example, `example.com` if the wild card entry is
+ `*.example.com`.
+- `global.hosts.externalIP`: the [external IP](preparation/networking.md) which
+ the wildcard DNS resolves to.
+- `certmanager-issuer.email`: the email address to use when requesting new SSL
+ certificates from Let's Encrypt.
-For enterprise deployments, or to utilize advanced settings, please use the instructions in the [`gitlab` chart project](https://gitlab.com/charts/gitlab) for the most up to date directions.
-- [External Postgres, Redis, and other dependencies](https://gitlab.com/charts/gitlab/tree/master/doc/advanced)
-- [Persistence settings](https://gitlab.com/charts/gitlab/blob/master/doc/installation/storage.md)
-- [Manual TLS certificates](https://gitlab.com/charts/gitlab/blob/master/doc/installation/tls.md)
-- [Manual secret creation](https://gitlab.com/charts/gitlab/blob/master/doc/installation/secrets.md)
+NOTE: **Note:**
+For deployments to Amazon EKS, there are
+[additional configuration requirements](preparation/eks.md). A full list of
+configuration options is [also available](https://gitlab.com/charts/gitlab/blob/master/doc/installation/command-line-options.md).
-For additional configuration options, consult the [full list of settings](https://gitlab.com/charts/gitlab/blob/master/doc/installation/command-line-options.md).
+Once you have all of your configuration options collected, you can get any
+dependencies and run helm. In this example, the helm release is named "gitlab":
-## Installing GitLab using the Helm Chart
-
-Once you have all of your configuration options collected, we can get any dependencies and
-run helm. In this example, we've named our helm release "gitlab".
-
-```
+```sh
helm repo add gitlab https://charts.gitlab.io/
-helm update
+helm repo update
helm upgrade --install gitlab gitlab/gitlab \
--timeout 600 \
- --set global.hosts.domain=example.local \
+ --set global.hosts.domain=example.com \
--set global.hosts.externalIP=10.10.10.10 \
- --set certmanager-issuer.email=me@example.local
+ --set certmanager-issuer.email=email@example.com
```
### Monitoring the Deployment
-This will output the list of resources installed once the deployment finishes which may take 5-10 minutes.
+This will output the list of resources installed once the deployment finishes,
+which may take 5-10 minutes.
-The status of the deployment can be checked by running `helm status gitlab` which can also be done while
-the deployment is taking place if you run the command in another terminal.
+The status of the deployment can be checked by running `helm status gitlab`
+which can also be done while the deployment is taking place if you run the
+command in another terminal.
### Initial login
-You can access the GitLab instance by visiting the domain name beginning with `gitlab.` followed by the domain specified during installation. From the example above, the URL would be `https://gitlab.example.local`.
+You can access the GitLab instance by visiting the domain name beginning with
+`gitlab.` followed by the domain specified during installation. From the example
+above, the URL would be `https://gitlab.example.com`.
If you manually created the secret for initial root password, you
-can use that to sign in as `root` user. If not, Gitlab automatically
+can use that to sign in as `root` user. If not, GitLab automatically
created a random password for `root` user. This can be extracted by the
following command (replace `<name>` by name of the release - which is `gitlab`
-if you used the command above).
+if you used the command above):
-```
-kubectl get secret <name>-gitlab-initial-root-password -ojsonpath={.data.password} | base64 --decode
+```sh
+kubectl get secret <name>-gitlab-initial-root-password -ojsonpath={.data.password} | base64 --decode ; echo
```
-## Outgoing email
+### Outgoing email
By default outgoing email is disabled. To enable it, provide details for your SMTP server
using the `global.smtp` and `global.email` settings. You can find details for these settings in the
@@ -95,17 +112,19 @@ If your SMTP server requires authentication make sure to read the section on pro
your password in the [secrets documentation](https://gitlab.com/charts/gitlab/blob/master/doc/installation/secrets.md#smtp-password).
You can disable authentication settings with `--set global.smtp.authentication=""`.
-If your Kubernetes cluster is on GKE, be aware that smtp [ports 25, 465, and 587
+If your Kubernetes cluster is on GKE, be aware that SMTP ports [25, 465, and 587
are blocked](https://cloud.google.com/compute/docs/tutorials/sending-mail/#using_standard_email_ports).
-## Deploying the Community Edition
+### Deploying the Community Edition
To deploy the Community Edition, include these options in your `helm install` command:
-```shell
+```sh
--set gitlab.migrations.image.repository=registry.gitlab.com/gitlab-org/build/cng/gitlab-rails-ce
--set gitlab.sidekiq.image.repository=registry.gitlab.com/gitlab-org/build/cng/gitlab-sidekiq-ce
--set gitlab.unicorn.image.repository=registry.gitlab.com/gitlab-org/build/cng/gitlab-unicorn-ce
+--set gitlab.unicorn.workhorse.image=registry.gitlab.com/gitlab-org/build/cng/gitlab-workhorse-ce
+--set gitlab.task-runner.image.repository=registry.gitlab.com/gitlab-org/build/cng/gitlab-task-runner-ce
```
## Updating GitLab using the Helm Chart
@@ -113,15 +132,16 @@ To deploy the Community Edition, include these options in your `helm install` co
Once your GitLab Chart is installed, configuration changes and chart updates
should be done using `helm upgrade`:
-```bash
-helm upgrade -f values.yaml gitlab gitlab/gitlab
+```sh
+helm repo update
+helm upgrade --reuse-values gitlab gitlab/gitlab
```
## Uninstalling GitLab using the Helm Chart
To uninstall the GitLab Chart, run the following:
-```bash
+```sh
helm delete gitlab
```
diff --git a/doc/install/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md
index 9aee6b9dc74..498b702cab1 100644
--- a/doc/install/kubernetes/gitlab_omnibus.md
+++ b/doc/install/kubernetes/gitlab_omnibus.md
@@ -1,18 +1,24 @@
# GitLab-Omnibus Helm Chart
-> **Note:**.
-* This chart has been tested on Google Kubernetes Engine and Azure Container Service.
-**[This chart is beta](#limitations), and is the best way to install GitLab on Kubernetes today.** A new [cloud native GitLab chart](index.md#cloud-native-gitlab-chart) is in development with increased scalability and resilience, among other benefits. Once available, the cloud native chart will be the recommended installation method for Kubernetes, and this chart will be deprecated.
+CAUTION: **Caution:**
+This chart is **deprecated**. We recommend using the [`gitlab` chart](gitlab_chart.md)
+instead. A comparison of the two charts is available in [this video](https://youtu.be/Z6jWR8Z8dv8).
-For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview).
+For more information on available GitLab Helm Charts, see the [charts overview](index.md#chart-overview).
-This work is based partially on: https://github.com/lwolf/kubernetes-gitlab/. GitLab would like to thank Sergey Nuzhdin for his work.
+- This GitLab-Omnibus chart has been tested on Google Kubernetes Engine and Azure Container Service.
+- This work is based partially on: https://github.com/lwolf/kubernetes-gitlab/. GitLab would like to thank Sergey Nuzhdin for his work.
## Introduction
-This chart provides an easy way to get started with GitLab, provisioning an installation with nearly all functionality enabled. SSL is automatically provisioned via [Let's Encrypt](https://letsencrypt.org/).
+This chart provides an easy way to get started with GitLab, provisioning an
+installation with nearly all functionality enabled. SSL is automatically
+provisioned via [Let's Encrypt](https://letsencrypt.org/).
-This Helm chart is in beta, and is suited for small to medium deployments. It will be deprecated by the [cloud native GitLab chart](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md) once available. Due to the significant architectural changes, migrating will require backing up data out of this instance and importing it into the new deployment.
+This Helm chart is suited for small to medium deployments and is **deprecated**
+and replaced by the [cloud native GitLab chart](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md).
+Due to the significant architectural changes, migrating will require backing up
+data out of this instance and importing it into the new deployment.
The deployment includes:
@@ -23,14 +29,12 @@ The deployment includes:
- [NGINX Ingress](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress)
- Persistent Volume Claims for Data, Registry, Postgres, and Redis
-### Limitations
+## Limitations
-* This chart is in beta, and suited for small to medium size deployments. [High Availability](https://docs.gitlab.com/ee/administration/high_availability/) and [Geo](https://docs.gitlab.com/ee/gitlab-geo/README.html) are not supported.
-* A new generation [cloud native GitLab chart](index.md#cloud-native-gitlab-chart) is in development, and will deprecate this chart. Due to the difficulty in supporting upgrades to the new architecture, migrating will require exporting data out of this instance and importing it into the new deployment. We plan to release the new chart in beta by the end of 2017.
+[High Availability](../../administration/high_availability/README.md) and
+[Geo](https://docs.gitlab.com/ee/gitlab-geo/README.html) are not supported.
-For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview).
-
-## Prerequisites
+## Requirements
- _At least_ 4 GB of RAM available on your cluster. 41GB of storage and 2 CPU are also required.
- Kubernetes 1.4+ with Beta APIs enabled
@@ -39,43 +43,65 @@ For more information on available GitLab Helm Charts, please see our [overview](
- The `kubectl` CLI installed locally and authenticated for the cluster
- The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
-### Networking Prerequisites
+### Networking requirements
-This chart configures a GitLab server and Kubernetes cluster which can support dynamic [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/index.html), as well as services like the integrated [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html) and [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/).
+This chart configures a GitLab server and Kubernetes cluster which can support
+dynamic [Review Apps](../../ci/review_apps/index.md), as well as services like
+the integrated [Container Registry](../../user/project/container_registry.md)
+and [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/).
-To support the GitLab services and dynamic environments, a wildcard DNS entry is required which resolves to the [Load Balancer](#load-balancer-ip) or [External IP](#external-ip). Configuration of the DNS entry will depend upon the DNS service being used.
+To support the GitLab services and dynamic environments, a wildcard DNS entry
+is required which resolves to the [load balancer](#load-balancer-ip) or
+[external IP](#external-ip). Configuration of the DNS entry will depend upon
+the DNS service being used.
-#### External IP (Recommended)
+#### External IP (recommended)
-To provision an external IP on GCP and Azure, simply request a new address from the Networking section. Ensure that the region matches the region your container cluster is created in. Note, it is important that the IP is not assigned at this point in time. It will be automatically assigned once the Helm chart is installed, and assigned to the Load Balancer.
+To provision an external IP on GCP and Azure, simply request a new address from
+the Networking section. Ensure that the region matches the region your container
+cluster is created in. It is important that the IP is not assigned at this point
+in time. It will be automatically assigned once the Helm chart is installed,
+and assigned to the Load Balancer.
-Now that an external IP address has been allocated, ensure that the wildcard DNS entry you would like to use resolves to this IP. Please consult the documentation for your DNS service for more information on creating DNS records.
+Now that an external IP address has been allocated, ensure that the wildcard
+DNS entry you would like to use resolves to this IP. Please consult the
+documentation for your DNS service for more information on creating DNS records.
-Finally, set the `baseIP` setting to this IP address when [deploying GitLab](#configuring-and-installing-gitlab).
+Finally, set the `baseIP` setting to this IP address when
+[deploying GitLab](#configuring-and-installing-gitlab).
#### Load Balancer IP
-If you do not specify a `baseIP`, an IP will be assigned to the Load Balancer or Ingress. You can retrieve this IP by running the following command *after* deploying GitLab:
+If you do not specify a `baseIP`, an IP will be assigned to the Load Balancer or
+Ingress. You can retrieve this IP by running the following command *after* deploying GitLab:
-`kubectl get svc -w --namespace nginx-ingress nginx`
+```sh
+kubectl get svc -w --namespace nginx-ingress nginx
+```
-The IP address will be displayed in the `EXTERNAL-IP` field, and should be used to configure the Wildcard DNS entry. For more information on creating a wildcard DNS entry, consult the documentation for the DNS server you are using.
+The IP address will be displayed in the `EXTERNAL-IP` field, and should be used
+to configure the Wildcard DNS entry. For more information on creating a wildcard
+DNS entry, consult the documentation for the DNS server you are using.
-For production deployments of GitLab, we strongly recommend using an [External IP](#external-ip).
+For production deployments of GitLab, we strongly recommend using a
+[external IP](#external-ip).
## Configuring and Installing GitLab
-For most installations, only two parameters are required:
+For most installations, two parameters are required:
+
- `baseDomain`: the [base domain](#networking-prerequisites) of the wildcard host entry. For example, `mycompany.io` if the wild card entry is `*.mycompany.io`.
- `legoEmail`: Email address to use when requesting new SSL certificates from Let's Encrypt.
Other common configuration options:
+
- `baseIP`: the desired [external IP address](#external-ip-recommended)
- `gitlab`: Choose the [desired edition](https://about.gitlab.com/pricing), either `ee` or `ce`. `ce` is the default.
- `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart
- `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/), with `acs` also supported for the [Azure Container Service](https://azure.microsoft.com/en-us/services/container-service/).
-For additional configuration options, consult the [values.yaml](https://gitlab.com/charts/charts.gitlab.io/blob/master/charts/gitlab-omnibus/values.yaml).
+For additional configuration options, consult the
+[`values.yaml`](https://gitlab.com/charts/gitlab-omnibus/blob/master/values.yaml).
### Choosing a different GitLab release version
@@ -92,13 +118,14 @@ The different images can be found in the [gitlab-ce](https://hub.docker.com/r/gi
repositories on Docker Hub.
### Persistent storage
-> **Note:**
-If you are using a machine type with support for less than 4 attached disks, like an Azure trial, you should disable dedicated storage for Postgres and Redis.
-By default, persistent storage is enabled for GitLab and the charts it depends
-on (Redis and PostgreSQL).
+NOTE: **Note:**
+If you are using a machine type with support for less than 4 attached disks,
+like an Azure trial, you should disable dedicated storage for Postgres and Redis.
-Components can have their claim size set from your `values.yaml`, along with whether to provision separate storage for Postgres and Redis.
+By default, persistent storage is enabled for GitLab and the charts it depends
+on (Redis and PostgreSQL). Components can have their claim size set from your
+`values.yaml`, along with whether to provision separate storage for Postgres and Redis.
Basic configuration:
@@ -117,14 +144,23 @@ gitlabConfigStorageSize: 1Gi
### Routing and SSL
-Ingress routing and SSL are automatically configured within this Chart. An NGINX ingress is provisioned and configured, and will route traffic to any service. SSL certificates are automatically created and configured by [kube-lego](https://github.com/kubernetes/charts/tree/master/stable/kube-lego).
+Ingress routing and SSL are automatically configured within this Chart. An NGINX
+ingress is provisioned and configured, and will route traffic to any service.
+SSL certificates are automatically created and configured by
+[kube-lego](https://github.com/kubernetes/charts/tree/master/stable/kube-lego).
-> **Note:**
-Let's Encrypt limits a single TLD to five certificate requests within a single week. This means that common DNS wildcard services like [nip.io](http://nip.io) and [nip.io](http://nip.io) are unlikely to work.
+NOTE: **Note:**
+Let's Encrypt limits a single TLD to five certificate requests within a single
+week. This means that common DNS wildcard services like [nip.io](http://nip.io)
+and [xip.io](http://xip.io) are unlikely to work.
## Installing GitLab using the Helm Chart
-> **Note:**
-You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically start. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage.
+
+NOTE: **Note:**
+You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound`
+while storage provisions. Once the storage provisions, the pods will automatically start.
+This may take a couple minutes depending on your cloud provider. If the error persists,
+please review the [requirements sections](#requirements) to ensure you have enough RAM, CPU, and storage.
Add the GitLab Helm repository and initialize Helm:
@@ -133,15 +169,15 @@ helm init
helm repo add gitlab https://charts.gitlab.io
```
-Once you have reviewed the [configuration settings](#configuring-and-installing-gitlab) you can install the chart. We recommending saving your configuration options in a `values.yaml` file for easier upgrades in the future.
-
-For example:
+Once you have reviewed the [configuration settings](#configuring-and-installing-gitlab),
+you can install the chart. We recommending saving your configuration options in a
+`values.yaml` file for easier upgrades in the future:
```bash
helm install --name gitlab -f values.yaml gitlab/gitlab-omnibus
```
-or passing them on the command line:
+Or you can pass them on the command line:
```bash
helm install --name gitlab --set baseDomain=gitlab.io,baseIP=192.0.2.1,gitlab=ee,gitlabEELicense=$LICENSE,legoEmail=email@gitlab.com gitlab/gitlab-omnibus
@@ -149,8 +185,11 @@ helm install --name gitlab --set baseDomain=gitlab.io,baseIP=192.0.2.1,gitlab=ee
## Updating GitLab using the Helm Chart
->**Note**: If you are upgrading from a previous version to 0.1.35 or above, you will need to change the access mode values for GitLab's storage. To do this, set the following in `values.yaml` or on the CLI:
-```
+If you are upgrading from a previous version to 0.1.35 or above, you will need to
+change the access mode values for GitLab's storage. To do this, set the following
+in `values.yaml` or on the CLI:
+
+```sh
gitlabDataAccessMode=ReadWriteMany
gitlabRegistryAccessMode=ReadWriteMany
gitlabConfigAccessMode=ReadWriteMany
@@ -159,15 +198,20 @@ gitlabConfigAccessMode=ReadWriteMany
Once your GitLab Chart is installed, configuration changes and chart updates
should be done using `helm upgrade`:
-```bash
+```sh
helm upgrade -f values.yaml gitlab gitlab/gitlab-omnibus
```
## Upgrading from CE to EE using the Helm Chart
-If you have installed the Community Edition using this chart, upgrading to Enterprise Edition is easy.
+If you have installed the Community Edition using this chart, upgrading to
+Enterprise Edition is easy.
-If you are using a `values.yaml` file to specify the configuration options, edit the file and set `gitlab=ee`. If you would like to run a specific version of GitLab EE, set `gitlabEEImage` to be the desired GitLab [docker image](https://hub.docker.com/r/gitlab/gitlab-ee/tags/). Then you can use `helm upgrade` to update your GitLab instance to EE:
+If you are using a `values.yaml` file to specify the configuration options, edit
+the file and set `gitlab=ee`. If you would like to run a specific version of
+GitLab EE, set `gitlabEEImage` to be the desired GitLab
+[docker image](https://hub.docker.com/r/gitlab/gitlab-ee/tags/). Then you can
+use `helm upgrade` to update your GitLab instance to EE:
```bash
helm upgrade -f values.yaml gitlab gitlab/gitlab-omnibus
@@ -184,16 +228,19 @@ helm upgrade gitlab --set gitlab=ee,gitlabEEImage=gitlab/gitlab-ee:9.5.5-ee.0 gi
To uninstall the GitLab Chart, run the following:
```bash
-helm delete gitlab
+helm delete --purge gitlab
```
## Troubleshooting
### Storage errors when updating `gitlab-omnibus` versions prior to 0.1.35
-Users upgrading `gitlab-omnibus` from a version prior to 0.1.35, may see an error like: `Error: UPGRADE FAILED: PersistentVolumeClaim "gitlab-gitlab-config-storage" is invalid: spec: Forbidden: field is immutable after creation`.
+Users upgrading `gitlab-omnibus` from a version prior to 0.1.35, may see an error
+like: `Error: UPGRADE FAILED: PersistentVolumeClaim "gitlab-gitlab-config-storage" is invalid: spec: Forbidden: field is immutable after creation`.
-This is due to a change in the access mode for GitLab storage in version 0.1.35. To successfully upgrade, the access mode flags must be set to `ReadWriteMany` as detailed in the [update section](#updating-gitlab-using-the-helm-chart).
+This is due to a change in the access mode for GitLab storage in version 0.1.35.
+To successfully upgrade, the access mode flags must be set to `ReadWriteMany`
+as detailed in the [update section](#updating-gitlab-using-the-helm-chart).
[kube-srv]: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types
[storageclass]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#storageclasses
diff --git a/doc/install/kubernetes/gitlab_runner_chart.md b/doc/install/kubernetes/gitlab_runner_chart.md
index 2aab225fcdb..f34d398a7f5 100644
--- a/doc/install/kubernetes/gitlab_runner_chart.md
+++ b/doc/install/kubernetes/gitlab_runner_chart.md
@@ -1,6 +1,6 @@
# GitLab Runner Helm Chart
> **Note:**
-These charts have been tested on Google Kubernetes Engine and Azure Container Service. Other Kubernetes installations may work as well, if not please [open an issue](https://gitlab.com/charts/gitlab-runner/issues).
+These charts have been tested on Google Kubernetes Engine and Azure Container Service. Other Kubernetes installations may work as well, if not please [open an issue](https://gitlab.com/gitlab-org/gitlab-runner/issues).
The `gitlab-runner` Helm chart deploys a GitLab Runner instance into your
Kubernetes cluster.
@@ -132,7 +132,7 @@ runners:
If your cluster has RBAC enabled, you can choose to either have the chart create its own service account or provide one.
-To have the chart create the service account for you, set `rbac.create` to true.
+To have the chart create the service account for you, set `rbac.create` to true.
### Controlling maximum Runner concurrency
diff --git a/doc/install/kubernetes/index.md b/doc/install/kubernetes/index.md
index 6419a9dcb69..69171fbb341 100644
--- a/doc/install/kubernetes/index.md
+++ b/doc/install/kubernetes/index.md
@@ -4,58 +4,57 @@ description: 'Read through the different methods to deploy GitLab on Kubernetes.
# Installing GitLab on Kubernetes
-> **Note**: These charts have been tested on Google Kubernetes Engine. Other Kubernetes installations may work as well, if not please [open an issue](https://gitlab.com/charts/issues).
-
The easiest method to deploy GitLab on [Kubernetes](https://kubernetes.io/) is
to take advantage of GitLab's Helm charts. [Helm] is a package
management tool for Kubernetes, allowing apps to be easily managed via their
Charts. A [Chart] is a detailed description of the application including how it
should be deployed, upgraded, and configured.
-## Chart Overview
-
-* **[GitLab Chart](gitlab_chart.html)**: The recommended GitLab chart, currently in beta. Supports large deployments with horizontal scaling of individual GitLab components, and does not require NFS.
-* **[GitLab Runner Chart](gitlab_runner_chart.md)**: For deploying just the GitLab Runner.
-* Other Charts
- * [GitLab-Omnibus](gitlab_omnibus.md): Chart based on the Omnibus GitLab linux package, only suitable for small deployments. The chart will be deprecated by the [GitLab chart](#gitlab-chart) when it is GA.
- * [Community Contributed Charts](#community-contributed-charts): Community contributed charts, deprecated by the official GitLab chart.
-
## GitLab Chart
-> **Note**: This chart is **beta**, while we work on the [remaining items for GA](https://gitlab.com/groups/charts/-/epics/15).
+This chart contains all the required components to get started, and can scale to
+large deployments. It offers a number of benefits:
-The best way to operate GitLab on Kubernetes. This chart contains all the required components to get started, and can scale to large deployments.
+- Horizontal scaling of individual components
+- No requirement for shared storage to scale
+- Containers do not need `root` permissions
+- Automatic SSL with Let's Encrypt
+- An unprivileged GitLab Runner
+- and plenty more.
-This chart offers a number of benefits:
-* Horizontal scaling of individual components
-* No requirement for shared storage to scale
-* Containers do not need `root` permissions
-* Automatic SSL with Let's Encrypt
-* and plenty more.
-
-Learn more about the [GitLab chart here](gitlab_chart.md) and [here [Video]](https://youtu.be/Z6jWR8Z8dv8).
+Learn more about the [GitLab chart](gitlab_chart.md).
## GitLab Runner Chart
-If you already have a GitLab instance running, inside or outside of Kubernetes, and you'd like to leverage the Runner's [Kubernetes capabilities](https://docs.gitlab.com/runner/executors/kubernetes.html), it can be deployed with the GitLab Runner chart.
+If you already have a GitLab instance running, inside or outside of Kubernetes,
+and you'd like to leverage the Runner's
+[Kubernetes capabilities](https://docs.gitlab.com/runner/executors/kubernetes.html),
+it can be deployed with the GitLab Runner chart.
Learn more about [gitlab-runner chart](gitlab_runner_chart.md).
-## Other Charts
+## Deprecated Charts
-### GitLab-Omnibus Chart
+CAUTION: **Deprecated:**
+These charts are **deprecated**. We recommend using the [GitLab Chart](gitlab_chart.md)
+instead.
-> **Note**: This chart is beta, and **will be deprecated** when the [`gitlab`](#gitlab-chart) chart is GA.
+### GitLab-Omnibus Chart
-It deploys and configures nearly all features of GitLab, including: a [Runner](https://docs.gitlab.com/runner/), [Container Registry](../../user/project/container_registry.html#gitlab-container-registry), [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/), [automatic SSL](https://github.com/kubernetes/charts/tree/master/stable/kube-lego), and a [load balancer](https://github.com/kubernetes/ingress/tree/master/controllers/nginx). It is based on our [GitLab Omnibus Docker Images](https://docs.gitlab.com/omnibus/docker/README.html).
+This chart is based on the [GitLab Omnibus Docker images](https://docs.gitlab.com/omnibus/docker/).
+It deploys and configures nearly all features of GitLab, including:
-Once the [GitLab chart](#gitlab-chart) is GA, this chart will be deprecated. Migrating to the `gitlab` chart will require exporting data out of this instance and importing it into a new deployment.
+- a [GitLab Runner](https://docs.gitlab.com/runner/)
+- [Container Registry](../../user/project/container_registry.html#gitlab-container-registry)
+- [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/)
+- [automatic SSL](https://github.com/kubernetes/charts/tree/master/stable/kube-lego)
+- and an [NGINX load balancer](https://github.com/kubernetes/ingress/tree/master/controllers/nginx).
Learn more about the [gitlab-omnibus chart](gitlab_omnibus.md).
### Community Contributed Charts
-The community has also contributed GitLab [CE](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ce) and [EE](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ee) charts to the [Helm Stable Repository](https://github.com/kubernetes/charts#repository-structure). These charts should be considered [deprecated](https://github.com/kubernetes/charts/issues/1138) in favor of the [official Charts](gitlab_omnibus.md).
+The community has also contributed GitLab [CE](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ce) and [EE](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ee) charts to the [Helm Stable Repository](https://github.com/kubernetes/charts#repository-structure). These charts are [deprecated](https://github.com/kubernetes/charts/issues/1138) in favor of the [official Chart](gitlab_chart.md).
[chart]: https://github.com/kubernetes/charts
[helm]: https://github.com/kubernetes/helm/blob/master/README.md
diff --git a/doc/install/kubernetes/preparation/connect.md b/doc/install/kubernetes/preparation/connect.md
index fb633c456f5..a3a0cba4bf2 100644
--- a/doc/install/kubernetes/preparation/connect.md
+++ b/doc/install/kubernetes/preparation/connect.md
@@ -2,19 +2,14 @@
In order to deploy software and settings to a cluster, you must connect and authenticate to it.
-* [GKE cluster](#connect-to-gke-cluster)
-* [EKS cluster](#connect-to-eks-cluster)
-* [Local minikube cluster](#connect-to-local-minikube-cluster)
-
## Connect to GKE cluster
-The command for connection to the cluster can be obtained from the [Google Cloud Platform Console](https://console.cloud.google.com/kubernetes/list) by the individual cluster.
-
-Look for the **Connect** button in the clusters list page.
-
-**Or**
+The command for connection to the cluster can be obtained from the
+[Google Cloud Platform Console](https://console.cloud.google.com/kubernetes/list)
+by the individual cluster.
-Use the command below, filling in your cluster's informtion:
+Look for the **Connect** button in the clusters list page or use the command below,
+filling in your cluster's information:
```
gcloud container clusters get-credentials <cluster-name> --zone <zone> --project <project-id>
@@ -22,7 +17,8 @@ gcloud container clusters get-credentials <cluster-name> --zone <zone> --project
## Connect to EKS cluster
-For the most up to date instructions, follow the Amazon EKS documentation on [connecting to a cluster](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html#eks-configure-kubectl).
+For the most up to date instructions, follow the Amazon EKS documentation on
+[connecting to a cluster](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html#eks-configure-kubectl).
## Connect to local minikube cluster
diff --git a/doc/install/kubernetes/preparation/networking.md b/doc/install/kubernetes/preparation/networking.md
index b157cf31aa9..34a6130de27 100644
--- a/doc/install/kubernetes/preparation/networking.md
+++ b/doc/install/kubernetes/preparation/networking.md
@@ -1,6 +1,8 @@
# Networking Prerequisites
-> **Note**: Amazon EKS utilizes Elastic Load Balancers, which are addressed by DNS name and cannot be known ahead of time. Skip this section.
+NOTE: **Note:**
+Amazon EKS utilizes Elastic Load Balancers, which are addressed by DNS name and
+cannot be known ahead of time. If you're using EKS, you can skip this section.
The `gitlab` chart configures a GitLab server and Kubernetes cluster which can support dynamic [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/index.html), as well as services like the integrated [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html).
@@ -30,7 +32,7 @@ Now that an external IP address has been allocated, ensure that the wildcard DNS
Please consult the documentation for your DNS service for more information on creating DNS records:
-* [Google Domains](https://support.google.com/domains/answer/3290350?hl=en)
-* [GoDaddy](https://www.godaddy.com/help/add-an-a-record-19238)
+- [Google Domains](https://support.google.com/domains/answer/3290350?hl=en)
+- [GoDaddy](https://www.godaddy.com/help/add-an-a-record-19238)
Set `global.hosts.domain` to this DNS name when [deploying GitLab](../gitlab_chart.md#configuring-and-installing-gitlab).
diff --git a/doc/install/kubernetes/preparation/rbac.md b/doc/install/kubernetes/preparation/rbac.md
index 240893526d3..c5f8d7a7e9e 100644
--- a/doc/install/kubernetes/preparation/rbac.md
+++ b/doc/install/kubernetes/preparation/rbac.md
@@ -1,16 +1,20 @@
# Role Based Access Control
-Until Kubernetes 1.7, there were no permissions within a cluster. With the launch of 1.7, there is now a role based access control system ([RBAC](https://kubernetes.io/docs/admin/authorization/rbac/)) which determines what services can perform actions within a cluster.
+Until Kubernetes 1.7, there were no permissions within a cluster. With the launch
+of 1.7, there is now a [role based access control system (RBAC)](https://kubernetes.io/docs/admin/authorization/rbac/)
+which determines what services can perform actions within a cluster.
RBAC affects a few different aspects of GitLab:
-* [Installation of GitLab using Helm](tiller.md#preparing-for-helm-with-rbac)
-* Prometheus monitoring
-* GitLab Runner
-## Checking that RBAC is enabled
+- [Installation of GitLab using Helm](tiller.md#preparing-for-helm-with-rbac)
+- Prometheus monitoring
+- GitLab Runner
-Try listing the current cluster roles, if it fails then `RBAC` is disabled
+## Checking that RBAC is enabled
-This command will output `false` if `RBAC` is disabled and `true` otherwise
+Try listing the current cluster roles, if it fails then `RBAC` is disabled.
+The following command will output `false` if `RBAC` is disabled and `true` otherwise:
-`kubectl get clusterroles > /dev/null 2>&1 && echo true || echo false`
+```sh
+kubectl get clusterroles > /dev/null 2>&1 && echo true || echo false
+```
diff --git a/doc/install/kubernetes/preparation/tiller.md b/doc/install/kubernetes/preparation/tiller.md
index 016aac2abeb..107df074b3b 100644
--- a/doc/install/kubernetes/preparation/tiller.md
+++ b/doc/install/kubernetes/preparation/tiller.md
@@ -1,10 +1,15 @@
# Configuring and initializing Helm Tiller
-To make use of Helm, you must have a [Kubernetes][k8s-io] cluster. Ensure you can access your cluster using `kubectl`.
+To make use of Helm, you must have a [Kubernetes][k8s-io] cluster. Ensure you can
+access your cluster using `kubectl`.
Helm consists of two parts, the `helm` client and a `tiller` server inside Kubernetes.
-> **Note**: If you are not able to run Tiller in your cluster, for example on OpenShift, it is possible to use [Tiller locally](https://gitlab.com/charts/gitlab/tree/master/doc/helm#local-tiller) and avoid deploying it into the cluster. This should only be used when Tiller cannot be normally deployed.
+NOTE: **Note:**
+If you are not able to run Tiller in your cluster, for example on OpenShift, it
+is possible to use [Tiller locally](https://gitlab.com/charts/gitlab/tree/master/doc/helm#local-tiller)
+and avoid deploying it into the cluster. This should only be used when Tiller
+cannot be normally deployed.
## Initialize Helm and Tiller
diff --git a/doc/install/openshift_and_gitlab/img/add-gitlab-to-project.png b/doc/install/openshift_and_gitlab/img/add-gitlab-to-project.png
index fcad4e59ae3..5b6059dd022 100644
--- a/doc/install/openshift_and_gitlab/img/add-gitlab-to-project.png
+++ b/doc/install/openshift_and_gitlab/img/add-gitlab-to-project.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/add-to-project.png b/doc/install/openshift_and_gitlab/img/add-to-project.png
index bd915a229f6..f9b00431d00 100644
--- a/doc/install/openshift_and_gitlab/img/add-to-project.png
+++ b/doc/install/openshift_and_gitlab/img/add-to-project.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/create-project-ui.png b/doc/install/openshift_and_gitlab/img/create-project-ui.png
index e72866f252a..43b151264c5 100644
--- a/doc/install/openshift_and_gitlab/img/create-project-ui.png
+++ b/doc/install/openshift_and_gitlab/img/create-project-ui.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/gitlab-logs.png b/doc/install/openshift_and_gitlab/img/gitlab-logs.png
index 1e24080c7df..8b90b2f74ac 100644
--- a/doc/install/openshift_and_gitlab/img/gitlab-logs.png
+++ b/doc/install/openshift_and_gitlab/img/gitlab-logs.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/gitlab-overview.png b/doc/install/openshift_and_gitlab/img/gitlab-overview.png
index 3c5df0ea101..3a7bec7c2bc 100644
--- a/doc/install/openshift_and_gitlab/img/gitlab-overview.png
+++ b/doc/install/openshift_and_gitlab/img/gitlab-overview.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/gitlab-running.png b/doc/install/openshift_and_gitlab/img/gitlab-running.png
index c7db691cb30..0fcd9f00d08 100644
--- a/doc/install/openshift_and_gitlab/img/gitlab-running.png
+++ b/doc/install/openshift_and_gitlab/img/gitlab-running.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/gitlab-scale.png b/doc/install/openshift_and_gitlab/img/gitlab-scale.png
index 4903c7d7498..ebae8b588b1 100644
--- a/doc/install/openshift_and_gitlab/img/gitlab-scale.png
+++ b/doc/install/openshift_and_gitlab/img/gitlab-scale.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/gitlab-settings.png b/doc/install/openshift_and_gitlab/img/gitlab-settings.png
index db4360ffef0..0dd1e1f5b8e 100644
--- a/doc/install/openshift_and_gitlab/img/gitlab-settings.png
+++ b/doc/install/openshift_and_gitlab/img/gitlab-settings.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/no-resources.png b/doc/install/openshift_and_gitlab/img/no-resources.png
index 480fb766468..1ef0a0b31e5 100644
--- a/doc/install/openshift_and_gitlab/img/no-resources.png
+++ b/doc/install/openshift_and_gitlab/img/no-resources.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/openshift-infra-project.png b/doc/install/openshift_and_gitlab/img/openshift-infra-project.png
index 8b9f85aa341..e31dda1461c 100644
--- a/doc/install/openshift_and_gitlab/img/openshift-infra-project.png
+++ b/doc/install/openshift_and_gitlab/img/openshift-infra-project.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/pods-overview.png b/doc/install/openshift_and_gitlab/img/pods-overview.png
index e1cf08bd217..65927f65f4f 100644
--- a/doc/install/openshift_and_gitlab/img/pods-overview.png
+++ b/doc/install/openshift_and_gitlab/img/pods-overview.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/rc-name.png b/doc/install/openshift_and_gitlab/img/rc-name.png
index 889e34adbec..16d967b8460 100644
--- a/doc/install/openshift_and_gitlab/img/rc-name.png
+++ b/doc/install/openshift_and_gitlab/img/rc-name.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/running-pods.png b/doc/install/openshift_and_gitlab/img/running-pods.png
index 3fd4e56662f..e08487c881c 100644
--- a/doc/install/openshift_and_gitlab/img/running-pods.png
+++ b/doc/install/openshift_and_gitlab/img/running-pods.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/storage-volumes.png b/doc/install/openshift_and_gitlab/img/storage-volumes.png
index ae1e5381faa..3fd092919bb 100644
--- a/doc/install/openshift_and_gitlab/img/storage-volumes.png
+++ b/doc/install/openshift_and_gitlab/img/storage-volumes.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/img/web-console.png b/doc/install/openshift_and_gitlab/img/web-console.png
index aa1425d4f94..012d7703c73 100644
--- a/doc/install/openshift_and_gitlab/img/web-console.png
+++ b/doc/install/openshift_and_gitlab/img/web-console.png
Binary files differ
diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md
index 60e1e2b5f8a..4c88b6f97fc 100644
--- a/doc/install/openshift_and_gitlab/index.md
+++ b/doc/install/openshift_and_gitlab/index.md
@@ -63,22 +63,24 @@ what we will use in this tutorial.
In short:
1. Open a terminal and in a new directory run:
- ```sh
- vagrant init openshift/origin-all-in-one
- ```
+
+ ```sh
+ vagrant init openshift/origin-all-in-one
+ ```
+
1. This will generate a Vagrantfile based on the all-in-one VM image
1. In the same directory where you generated the Vagrantfile
enter:
- ```sh
- vagrant up
- ```
+ ```sh
+ vagrant up
+ ```
This will download the VirtualBox image and fire up the VM with some preconfigured
values as you can see in the Vagrantfile. As you may have noticed, you need
plenty of RAM (5GB in our example), so make sure you have enough.
-Now that OpenShift is setup, let's see how the web console looks like.
+Now that OpenShift is set up, let's see how the web console looks like.
### Explore the OpenShift web console
@@ -187,22 +189,22 @@ In that case, the OpenShift service might not be running, so in order to fix it:
1. SSH into the VM by going to the directory where the Vagrantfile is and then
run:
- ```sh
- vagrant ssh
- ```
+ ```sh
+ vagrant ssh
+ ```
1. Run `systemctl` and verify by the output that the `openshift` service is not
running (it will be in red color). If that's the case start the service with:
- ```sh
- sudo systemctl start openshift
- ```
+ ```sh
+ sudo systemctl start openshift
+ ```
1. Verify the service is up with:
- ```sh
- systemctl status openshift -l
- ```
+ ```sh
+ systemctl status openshift -l
+ ```
Now you will be able to login using `oc` (like we did before) and visit the web
console.
@@ -385,55 +387,55 @@ Let's see how to do that using the following steps.
1. Make sure you are in the `gitlab` project:
- ```sh
- oc project gitlab
- ```
+ ```sh
+ oc project gitlab
+ ```
1. See what services are used for this project:
- ```sh
- oc get svc
- ```
+ ```sh
+ oc get svc
+ ```
- The output will be similar to:
+ The output will be similar to:
- ```
- NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- gitlab-ce 172.30.243.177 <none> 22/TCP,80/TCP 5d
- gitlab-ce-postgresql 172.30.116.75 <none> 5432/TCP 5d
- gitlab-ce-redis 172.30.105.88 <none> 6379/TCP 5d
- ```
+ ```
+ NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+ gitlab-ce 172.30.243.177 <none> 22/TCP,80/TCP 5d
+ gitlab-ce-postgresql 172.30.116.75 <none> 5432/TCP 5d
+ gitlab-ce-redis 172.30.105.88 <none> 6379/TCP 5d
+ ```
1. We need to see the replication controllers of the `gitlab-ce` service.
Get a detailed view of the current ones:
- ```sh
- oc describe rc gitlab-ce
- ```
+ ```sh
+ oc describe rc gitlab-ce
+ ```
- This will return a large detailed list of the current replication controllers.
- Search for the name of the GitLab controller, usually `gitlab-ce-1` or if
- that failed at some point and you spawned another one, it will be named
- `gitlab-ce-2`.
+ This will return a large detailed list of the current replication controllers.
+ Search for the name of the GitLab controller, usually `gitlab-ce-1` or if
+ that failed at some point and you spawned another one, it will be named
+ `gitlab-ce-2`.
1. Scale GitLab using the previous information:
- ```sh
- oc scale --replicas=2 replicationcontrollers gitlab-ce-2
- ```
+ ```sh
+ oc scale --replicas=2 replicationcontrollers gitlab-ce-2
+ ```
1. Get the new replicas number to make sure scaling worked:
- ```sh
- oc get rc gitlab-ce-2
- ```
+ ```sh
+ oc get rc gitlab-ce-2
+ ```
- which will return something like:
+ which will return something like:
- ```
- NAME DESIRED CURRENT AGE
- gitlab-ce-2 2 2 5d
- ```
+ ```
+ NAME DESIRED CURRENT AGE
+ gitlab-ce-2 2 2 5d
+ ```
And that's it! We successfully scaled the replicas to 2 using the CLI.
@@ -469,9 +471,10 @@ GitLab service account to the `anyuid` [Security Context Constraints][scc].
For OpenShift v3.0, you will need to do this manually:
1. Edit the Security Context:
- ```sh
- oc edit scc anyuid
- ```
+
+ ```sh
+ oc edit scc anyuid
+ ```
1. Add `system:serviceaccount:<project>:gitlab-ce-user` to the `users` section.
If you changed the Application Name from the default the user will
@@ -502,7 +505,7 @@ PaaS and managing your applications with the ease of containers.
[RedHat]: https://www.redhat.com/en "RedHat website"
[openshift]: https://www.openshift.org "OpenShift Origin website"
[vm]: https://www.openshift.org/vm/ "OpenShift All-in-one VM"
-[vm-new]: https://atlas.hashicorp.com/openshift/boxes/origin-all-in-one "Official OpenShift Vagrant box on Atlas"
+[vm-new]: https://app.vagrantup.com/openshift/boxes/origin-all-in-one "Official OpenShift Vagrant box on Vagrant Cloud"
[template]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/docker/openshift-template.json "OpenShift template for GitLab"
[openshift.com]: https://openshift.com "OpenShift Online"
[kubernetes]: http://kubernetes.io/ "Kubernetes website"
diff --git a/doc/install/relative_url.md b/doc/install/relative_url.md
index 2f5d4142d04..5f129fd3bd1 100644
--- a/doc/install/relative_url.md
+++ b/doc/install/relative_url.md
@@ -124,5 +124,5 @@ To disable the relative URL:
1. Follow the same as above starting from 2. and set up the
GitLab URL to one that doesn't contain a relative path.
-[omnibus-rel]: http://docs.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to setup relative URL in Omnibus GitLab"
+[omnibus-rel]: http://docs.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to set up relative URL in Omnibus GitLab"
[restart gitlab]: ../administration/restart_gitlab.md#installations-from-source "How to restart GitLab"
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 5531dcde4e9..1b7e0d1d0ab 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -27,7 +27,7 @@ Please see the [installation from source guide](installation.md) and the [instal
### Non-Unix operating systems such as Windows
-GitLab is developed for Unix operating systems.
+GitLab is developed for Unix operating systems.
It does **not** run on Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46567).
Please consider using a virtual machine to run GitLab.
@@ -36,7 +36,7 @@ Please consider using a virtual machine to run GitLab.
GitLab requires Ruby (MRI) 2.3. Support for Ruby versions below 2.3 (2.1, 2.2) will stop with GitLab 8.13.
You will have to use the standard MRI implementation of Ruby.
-We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
+We love [JRuby](https://www.jruby.org/) and [Rubinius](https://rubinius.com) but GitLab
needs several Gems that have native extensions.
## Hardware requirements
@@ -103,19 +103,21 @@ features of GitLab work with MySQL/MariaDB:
1. MySQL support for subgroups was [dropped with GitLab 9.3][post].
See [issue #30472][30472] for more information.
-1. GitLab Geo does [not support MySQL](https://docs.gitlab.com/ee/gitlab-geo/database.html#mysql-replication).
-1. [Zero downtime migrations][zero] do not work with MySQL
+1. Geo does [not support MySQL](https://docs.gitlab.com/ee/administration/geo/replication/database.html#mysql-replication). This means no supported Disaster Recovery solution if using MySQL. **[PREMIUM ONLY]**
+1. [Zero downtime migrations](../update/README.md#upgrading-without-downtime) do not work with MySQL.
1. GitLab [optimizes the loading of dashboard events](https://gitlab.com/gitlab-org/gitlab-ce/issues/31806) using [PostgreSQL LATERAL JOINs](https://blog.heapanalytics.com/postgresqls-powerful-new-join-type-lateral/).
1. In general, SQL optimized for PostgreSQL may run much slower in MySQL due to
differences in query planners. For example, subqueries that work well in PostgreSQL
- may not be [performant in MySQL](https://dev.mysql.com/doc/refman/5.7/en/optimizing-subqueries.html)
+ may not be [performant in MySQL](https://dev.mysql.com/doc/refman/5.7/en/optimizing-subqueries.html).
+1. Binary column index length is limited to 20 bytes. This is accomplished with [a hack](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/mysql_set_length_for_binary_indexes.rb).
+1. MySQL requires a variety of hacks to increase limits on various columns, [for example](https://gitlab.com/gitlab-org/gitlab-ce/issues/49583).
+1. [The milestone filter runs slower queries on MySQL](https://gitlab.com/gitlab-org/gitlab-ce/issues/51173#note_99391731).
1. We expect this list to grow over time.
Existing users using GitLab with MySQL/MariaDB are advised to
[migrate to PostgreSQL](../update/mysql_to_postgresql.md) instead.
[30472]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472
-[zero]: ../update/README.md#upgrading-without-downtime
[post]: https://about.gitlab.com/2017/06/22/gitlab-9-3-released/#dropping-support-for-subgroups-in-mysql
### PostgreSQL Requirements
@@ -195,7 +197,13 @@ use the CI features.
## Supported web browsers
-We support the current and the previous major release of Firefox, Chrome/Chromium, Safari and Microsoft browsers (Microsoft Edge and Internet Explorer 11).
+We support the current and the previous major release of:
+
+- Firefox
+- Chrome/Chromium
+- Safari
+- Microsoft Edge
+- Internet Explorer 11
Each time a new browser version is released, we begin supporting that version and stop supporting the third most recent version.
diff --git a/doc/integration/README.md b/doc/integration/README.md
index 54e78bdef54..8a93d4cb84b 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -30,7 +30,7 @@ Bitbucket.org account
## Project services
-Integration with services such as Campfire, Flowdock, Gemnasium, HipChat,
+Integration with services such as Campfire, Flowdock, HipChat,
Pivotal Tracker, and Slack are available in the form of a [Project Service][].
[Project Service]: ../user/project/integrations/project_services.md
diff --git a/doc/integration/akismet.md b/doc/integration/akismet.md
index a6436b5f926..200fe6f5206 100644
--- a/doc/integration/akismet.md
+++ b/doc/integration/akismet.md
@@ -20,17 +20,17 @@ To use Akismet:
1. Go to the URL: https://akismet.com/account/
-2. Sign-in or create a new account.
+1. Sign-in or create a new account.
-3. Click on **Show** to reveal the API key.
+1. Click on **Show** to reveal the API key.
-4. Go to Applications Settings on Admin Area (`admin/application_settings`)
+1. Go to Applications Settings on Admin Area (`admin/application_settings`)
-5. Check the **Enable Akismet** checkbox
+1. Check the **Enable Akismet** checkbox
-6. Fill in the API key from step 3.
+1. Fill in the API key from step 3.
-7. Save the configuration.
+1. Save the configuration.
![Screenshot of Akismet settings](img/akismet_settings.png)
@@ -42,9 +42,9 @@ To use Akismet:
As a way to better recognize between spam and ham, you can train the Akismet
filter whenever there is a false positive or false negative.
-When an entry is recognized as spam, it is rejected and added to the Spam Logs.
+When an entry is recognized as spam, it is rejected and added to the Spam Logs.
From here you can review if they are really spam. If one of them is not really
-spam, you can use the **Submit as ham** button to tell Akismet that it falsely
+spam, you can use the **Submit as ham** button to tell Akismet that it falsely
recognized an entry as spam.
![Screenshot of Spam Logs](img/spam_log.png)
diff --git a/doc/integration/auth0.md b/doc/integration/auth0.md
index a75836a915a..bccaeec3706 100644
--- a/doc/integration/auth0.md
+++ b/doc/integration/auth0.md
@@ -3,7 +3,7 @@
To enable the Auth0 OmniAuth provider, you must create an Auth0 account, and an
application.
-1. Sign in to the [Auth0 Console](https://manage.auth0.com). If you need to
+1. Sign in to the [Auth0 Console](https://auth0.com/auth/login). If you need to
create an account, you can do so at the same link.
1. Select "New App/API".
diff --git a/doc/integration/azure.md b/doc/integration/azure.md
index f3c9c498634..634dd952448 100644
--- a/doc/integration/azure.md
+++ b/doc/integration/azure.md
@@ -2,7 +2,7 @@
To enable the Microsoft Azure OAuth2 OmniAuth provider you must register your application with Azure. Azure will generate a client ID and secret key for you to use.
-1. Sign in to the [Azure Management Portal](https://manage.windowsazure.com>).
+1. Sign in to the [Azure Management Portal](https://manage.windowsazure.com).
1. Select "Active Directory" on the left and choose the directory you want to use to register GitLab.
diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md
index bf587b5b296..a69db1d1a6e 100644
--- a/doc/integration/bitbucket.md
+++ b/doc/integration/bitbucket.md
@@ -54,6 +54,7 @@ you to use.
```
Account: Email, Read
+ Projects: Read
Repositories: Read
Pull Requests: Read
Issues: Read
diff --git a/doc/integration/github.md b/doc/integration/github.md
index 680712f9e01..7a83b8e4b35 100644
--- a/doc/integration/github.md
+++ b/doc/integration/github.md
@@ -19,7 +19,7 @@ GitHub will generate an application ID and secret key for you to use.
- Application name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive.
- Homepage URL: The URL to your GitLab installation. 'https://gitlab.company.com'
- Application description: Fill this in if you wish.
- - Authorization callback URL is 'http(s)://${YOUR_DOMAIN}'. Please make sure the port is included if your Gitlab instance is not configured on default port.
+ - Authorization callback URL is 'http(s)://${YOUR_DOMAIN}'. Please make sure the port is included if your GitLab instance is not configured on default port.
1. Select "Register application".
1. You should now see a Client ID and Client Secret near the top right of the page (see screenshot).
@@ -154,7 +154,7 @@ You will also need to disable Git SSL verification on the server hosting GitLab.
$ git config --global http.sslVerify false
```
-For the changes to take effect, [reconfigure Gitlab] if you installed
+For the changes to take effect, [reconfigure GitLab] if you installed
via Omnibus, or [restart GitLab] if you installed from source.
[reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
diff --git a/doc/integration/gmail_action_buttons_for_gitlab.md b/doc/integration/gmail_action_buttons_for_gitlab.md
index 05a91d9bef9..c19320471e3 100644
--- a/doc/integration/gmail_action_buttons_for_gitlab.md
+++ b/doc/integration/gmail_action_buttons_for_gitlab.md
@@ -2,7 +2,7 @@
GitLab supports [Google actions in email](https://developers.google.com/gmail/markup/actions/actions-overview).
-If correctly setup, emails that require an action will be marked in Gmail.
+If correctly set up, emails that require an action will be marked in Gmail.
![gmail_actions_button.png](img/gmail_action_buttons_for_gitlab.png)
diff --git a/doc/integration/google.md b/doc/integration/google.md
index 73e2f5826ff..b91d40d4bd4 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -35,7 +35,7 @@ In Google's side:
1. You should now be able to see a Client ID and Client secret. Note them down
or keep this page open as you will need them later.
-1. From the **Dashboard** select **ENABLE APIS AND SERVICES > Compute > Google+ API > Enable**
+1. From the **Dashboard** select **ENABLE APIS AND SERVICES > Social > Google+ API > Enable**
1. To enable projects to access [Google Kubernetes Engine](../user/project/clusters/index.md), you must also
enable these APIs:
- Google Kubernetes Engine API
diff --git a/doc/integration/img/bitbucket_oauth_keys.png b/doc/integration/img/bitbucket_oauth_keys.png
index 6dd2c7d744e..2f0c0eff784 100644
--- a/doc/integration/img/bitbucket_oauth_keys.png
+++ b/doc/integration/img/bitbucket_oauth_keys.png
Binary files differ
diff --git a/doc/integration/img/enable_trello_powerup.png b/doc/integration/img/enable_trello_powerup.png
index 65d01f1c38c..f80d0eadc0b 100644
--- a/doc/integration/img/enable_trello_powerup.png
+++ b/doc/integration/img/enable_trello_powerup.png
Binary files differ
diff --git a/doc/integration/img/enabled-oauth-sign-in-sources.png b/doc/integration/img/enabled-oauth-sign-in-sources.png
index f145aeae75c..e83f9d5cfdf 100644
--- a/doc/integration/img/enabled-oauth-sign-in-sources.png
+++ b/doc/integration/img/enabled-oauth-sign-in-sources.png
Binary files differ
diff --git a/doc/integration/img/facebook_api_keys.png b/doc/integration/img/facebook_api_keys.png
index 9463ec1e7a3..7480b144091 100644
--- a/doc/integration/img/facebook_api_keys.png
+++ b/doc/integration/img/facebook_api_keys.png
Binary files differ
diff --git a/doc/integration/img/facebook_website_url.png b/doc/integration/img/facebook_website_url.png
index 67d78d13951..7873c9905f1 100644
--- a/doc/integration/img/facebook_website_url.png
+++ b/doc/integration/img/facebook_website_url.png
Binary files differ
diff --git a/doc/integration/img/gitlab_app.png b/doc/integration/img/gitlab_app.png
index 8d6a4456fc4..228e8a01305 100644
--- a/doc/integration/img/gitlab_app.png
+++ b/doc/integration/img/gitlab_app.png
Binary files differ
diff --git a/doc/integration/img/google_app.png b/doc/integration/img/google_app.png
index 9fda06dabb1..08f230452b4 100644
--- a/doc/integration/img/google_app.png
+++ b/doc/integration/img/google_app.png
Binary files differ
diff --git a/doc/integration/img/oauth_provider_admin_application.png b/doc/integration/img/oauth_provider_admin_application.png
index c8ecce129c8..353114fea30 100644
--- a/doc/integration/img/oauth_provider_admin_application.png
+++ b/doc/integration/img/oauth_provider_admin_application.png
Binary files differ
diff --git a/doc/integration/img/oauth_provider_application_form.png b/doc/integration/img/oauth_provider_application_form.png
index 954681e054e..c4546d8b3f5 100644
--- a/doc/integration/img/oauth_provider_application_form.png
+++ b/doc/integration/img/oauth_provider_application_form.png
Binary files differ
diff --git a/doc/integration/img/oauth_provider_application_id_secret.png b/doc/integration/img/oauth_provider_application_id_secret.png
index 65cca5f1e1b..21e442b5d04 100644
--- a/doc/integration/img/oauth_provider_application_id_secret.png
+++ b/doc/integration/img/oauth_provider_application_id_secret.png
Binary files differ
diff --git a/doc/integration/img/oauth_provider_authorized_application.png b/doc/integration/img/oauth_provider_authorized_application.png
index ed99db3476d..ebff8529b4e 100644
--- a/doc/integration/img/oauth_provider_authorized_application.png
+++ b/doc/integration/img/oauth_provider_authorized_application.png
Binary files differ
diff --git a/doc/integration/img/submit_issue.png b/doc/integration/img/submit_issue.png
index 8accb78faf3..e794eac189e 100644
--- a/doc/integration/img/submit_issue.png
+++ b/doc/integration/img/submit_issue.png
Binary files differ
diff --git a/doc/integration/img/twitter_app_api_keys.png b/doc/integration/img/twitter_app_api_keys.png
index 34e3c3ba001..c6a3245b1b1 100644
--- a/doc/integration/img/twitter_app_api_keys.png
+++ b/doc/integration/img/twitter_app_api_keys.png
Binary files differ
diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md
index e71706fef7d..3e72589ce12 100644
--- a/doc/integration/oauth2_generic.md
+++ b/doc/integration/oauth2_generic.md
@@ -24,11 +24,11 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc
1. Register your application in the OAuth2 provider you wish to authenticate with.
- The redirect URI you provide when registering the application should be:
+ The redirect URI you provide when registering the application should be:
- ```
- http://your-gitlab.host.com/users/auth/oauth2_generic/callback
- ```
+ ```
+ http://your-gitlab.host.com/users/auth/oauth2_generic/callback
+ ```
1. You should now be able to get a Client ID and Client Secret.
Where this shows up will differ for each provider.
@@ -36,18 +36,18 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc
1. On your GitLab server, open the configuration file.
- For Omnibus package:
+ For Omnibus package:
- ```sh
- sudo editor /etc/gitlab/gitlab.rb
- ```
+ ```sh
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
- For installations from source:
+ For installations from source:
- ```sh
- cd /home/git/gitlab
- sudo -u git -H editor config/gitlab.yml
- ```
+ ```sh
+ cd /home/git/gitlab
+ sudo -u git -H editor config/gitlab.yml
+ ```
1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index 82e8fbdb93e..4e1d5ba9b35 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -39,7 +39,10 @@ contains some settings that are common for all providers.
Before configuring individual OmniAuth providers there are a few global settings
that are in common for all providers that we need to consider.
-- Omniauth needs to be enabled, see details below for example.
+> **NOTE:**
+> Starting from GitLab 11.4, Omniauth is enabled by default. If you're using an
+> earlier version, you'll need to explicitly enable it.
+
- `allow_single_sign_on` allows you to specify the providers you want to allow to
automatically create an account. It defaults to `false`. If `false` users must
be created manually or they will not be able to sign in via OmniAuth.
@@ -50,16 +53,16 @@ that are in common for all providers that we need to consider.
be blocked by default and will have to be unblocked by an administrator before
they are able to sign in.
->**Note:**
-If you set `block_auto_created_users` to `false`, make sure to only
-define providers under `allow_single_sign_on` that you are able to control, like
-SAML, Shibboleth, Crowd or Google, or set it to `false` otherwise any user on
-the Internet will be able to successfully sign in to your GitLab without
-administrative approval.
-
->**Note:**
-`auto_link_ldap_user` requires the `uid` of the user to be the same in both LDAP
-and the OmniAuth provider.
+> **Note:**
+> If you set `block_auto_created_users` to `false`, make sure to only
+> define providers under `allow_single_sign_on` that you are able to control, like
+> SAML, Shibboleth, Crowd or Google, or set it to `false` otherwise any user on
+> the Internet will be able to successfully sign in to your GitLab without
+> administrative approval.
+>
+> **Note:**
+> `auto_link_ldap_user` requires the `uid` of the user to be the same in both LDAP
+> and the OmniAuth provider.
To change these settings:
@@ -74,7 +77,8 @@ To change these settings:
and change:
```ruby
- gitlab_rails['omniauth_enabled'] = true
+ # Versions prior to 11.4 require this to be set to true
+ # gitlab_rails['omniauth_enabled'] = nil
# CAUTION!
# This allows users to login without having a user account first. Define the allowed providers
@@ -101,7 +105,8 @@ To change these settings:
## OmniAuth settings
omniauth:
# Allow login via Twitter, Google, etc. using OmniAuth providers
- enabled: true
+ # Versions prior to 11.4 require this to be set to true
+ # enabled: true
# CAUTION!
# This allows users to login without having a user account first. Define the allowed providers
@@ -227,21 +232,42 @@ In order to enable/disable an OmniAuth provider, go to Admin Area -> Settings ->
![Enabled OAuth Sign-In sources](img/enabled-oauth-sign-in-sources.png)
+## Disabling Omniauth
+
+Starting from version 11.4 of GitLab, Omniauth is enabled by default. This only
+has an effect if providers are configured and [enabled](#enable-or-disable-sign-in-with-an-omniauth-provider-without-disabling-import-sources).
+
+If omniauth providers are causing problems even when individually disabled, you
+can disable the entire omniauth subsystem by modifying the configuration file:
+
+**For Omnibus installations**
+
+```ruby
+gitlab_rails['omniauth_enabled'] = false
+```
+
+**For installations from source**
+
+```yaml
+ omniauth:
+ enabled: false
+```
+
## Keep OmniAuth user profiles up to date
You can enable profile syncing from selected OmniAuth providers and for all or for specific user information.
When authenticating using LDAP, the user's email is always synced.
- ```ruby
- gitlab_rails['sync_profile_from_provider'] = ['twitter', 'google_oauth2']
- gitlab_rails['sync_profile_attributes'] = ['name', 'email', 'location']
+```ruby
+ gitlab_rails['sync_profile_from_provider'] = ['twitter', 'google_oauth2']
+ gitlab_rails['sync_profile_attributes'] = ['name', 'email', 'location']
```
- **For installations from source**
+**For installations from source**
- ```yaml
- omniauth:
- sync_profile_from_provider: ['twitter', 'google_oauth2']
- sync_profile_attributes: ['email', 'location']
- ```
+```yaml
+ omniauth:
+ sync_profile_from_provider: ['twitter', 'google_oauth2']
+ sync_profile_attributes: ['email', 'location']
+```
diff --git a/doc/integration/recaptcha.md b/doc/integration/recaptcha.md
index 932cd479d56..8fdadb008ec 100644
--- a/doc/integration/recaptcha.md
+++ b/doc/integration/recaptcha.md
@@ -8,19 +8,13 @@ to confirm that a real user, not a bot, is attempting to create an account.
To use reCAPTCHA, first you must create a site and private key.
-1. Go to the URL: https://www.google.com/recaptcha/admin
-
-2. Fill out the form necessary to obtain reCAPTCHA keys.
-
-3. Login to your GitLab server, with administrator credentials.
-
-4. Go to Applications Settings on Admin Area (`admin/application_settings`)
-
-5. Fill all recaptcha fields with keys from previous steps
-
-6. Check the `Enable reCAPTCHA` checkbox
-
-7. Save the configuration.
+1. Go to the URL: <https://www.google.com/recaptcha/admin>.
+1. Fill out the form necessary to obtain reCAPTCHA keys.
+1. Login to your GitLab server, with administrator credentials.
+1. Go to Applications Settings on Admin Area (`admin/application_settings`).
+1. Fill all recaptcha fields with keys from previous steps.
+1. Check the `Enable reCAPTCHA` checkbox.
+1. Save the configuration.
## Enabling reCAPTCHA for user logins via passwords
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 25f396bc9c4..a7470d27b4b 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -123,9 +123,10 @@ in your SAML IdP:
To ease configuration, most IdP accept a metadata URL for the application to provide
configuration information to the IdP. To build the metadata URL for GitLab, append
`users/auth/saml/metadata` to the HTTPS URL of your GitLab installation, for instance:
- ```
- https://gitlab.example.com/users/auth/saml/metadata
- ```
+
+```
+https://gitlab.example.com/users/auth/saml/metadata
+```
At a minimum the IdP *must* provide a claim containing the user's email address, using
claim name `email` or `mail`. The email will be used to automatically generate the GitLab
@@ -338,6 +339,23 @@ args: {
}
```
+### `uid_attribute`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/43806) in GitLab 10.7.
+
+By default, the `uid` is set as the `name_id` in the SAML response. If you'd like to designate a unique attribute for the `uid`, you can set the `uid_attribute`. In the example below, the value of `uid` attribute in the SAML response is set as the `uid_attribute`.
+
+```yaml
+args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+ uid_attribute: 'uid'
+}
+```
+
## Troubleshooting
### 500 error after login
diff --git a/doc/monitoring/performance/img/grafana_dashboard_import.png b/doc/monitoring/performance/img/grafana_dashboard_import.png
index 7761ea00522..fd639ee0eb8 100644
--- a/doc/monitoring/performance/img/grafana_dashboard_import.png
+++ b/doc/monitoring/performance/img/grafana_dashboard_import.png
Binary files differ
diff --git a/doc/monitoring/performance/img/grafana_data_source_configuration.png b/doc/monitoring/performance/img/grafana_data_source_configuration.png
index 3e749eb8f9d..a98e0ed1e7d 100644
--- a/doc/monitoring/performance/img/grafana_data_source_configuration.png
+++ b/doc/monitoring/performance/img/grafana_data_source_configuration.png
Binary files differ
diff --git a/doc/monitoring/performance/img/grafana_data_source_empty.png b/doc/monitoring/performance/img/grafana_data_source_empty.png
index 33fcaaaef64..549ada8343e 100644
--- a/doc/monitoring/performance/img/grafana_data_source_empty.png
+++ b/doc/monitoring/performance/img/grafana_data_source_empty.png
Binary files differ
diff --git a/doc/monitoring/performance/img/grafana_save_icon.png b/doc/monitoring/performance/img/grafana_save_icon.png
index c18f2147e9d..68a071f5ae2 100644
--- a/doc/monitoring/performance/img/grafana_save_icon.png
+++ b/doc/monitoring/performance/img/grafana_save_icon.png
Binary files differ
diff --git a/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png b/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png
index d96a18ebc04..b9563a00e97 100644
--- a/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png
+++ b/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png
Binary files differ
diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md
index 7f028565412..03ba2ae8817 100644
--- a/doc/policy/maintenance.md
+++ b/doc/policy/maintenance.md
@@ -44,7 +44,7 @@ This decision is made on a case-by-case basis.
## Upgrade recommendations
-We encourage everyone to run the [latest stable release](https://about.gitlab.com/blog/categories/release/) to ensure that you can
+We encourage everyone to run the [latest stable release](https://about.gitlab.com/blog/categories/releases/) to ensure that you can
easily upgrade to the most secure and feature-rich GitLab experience. In order
to make sure you can easily run the most recent stable release, we are working
hard to keep the update process simple and reliable.
@@ -75,7 +75,8 @@ Please see the table below for some examples:
| Latest stable version | Your version | Recommended upgrade path | Note |
| -------------- | ------------ | ------------------------ | ---------------- |
| 9.4.5 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.4.5` | `8.17.7` is the last version in version `8` |
-| 10.1.4 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.5.8` -> `10.1.4` | `8.17.7` is the last version in version `8`, `9.5.8` is the last version in version `9` |
+| 10.1.4 | 8.13.4 | `8.13.4 -> 8.17.7 -> 9.5.10 -> 10.1.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9` |
+| 11.3.4 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9`, `10.8.7` is the last version in version `10` |
More information about the release procedures can be found in our
[release-tools documentation][rel]. You may also want to read our
diff --git a/doc/public_access/img/restrict_visibility_levels.png b/doc/public_access/img/restrict_visibility_levels.png
index c7d4d87981f..e9315cfb701 100644
--- a/doc/public_access/img/restrict_visibility_levels.png
+++ b/doc/public_access/img/restrict_visibility_levels.png
Binary files differ
diff --git a/doc/raketasks/backup_hrz.png b/doc/raketasks/backup_hrz.png
index c9595b236ee..32690b2904c 100644
--- a/doc/raketasks/backup_hrz.png
+++ b/doc/raketasks/backup_hrz.png
Binary files differ
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index f1881e0f767..76f5495ff78 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -9,27 +9,39 @@ You can only restore a backup to **exactly the same version and type (CE/EE)**
of GitLab on which it was created. The best way to migrate your repositories
from one server to another is through backup restore.
-## Backup
+## Requirements
-GitLab provides a simple command line interface to backup your whole installation,
-and is flexible enough to fit your needs.
+In order to be able to backup and restore, you need two essential tools
+installed on your system.
-### Requirements
+### Rsync
-If you're using GitLab with the Omnibus package, you're all set. If you
-installed GitLab from source, make sure the following packages are installed:
+If you installed GitLab:
-* rsync
+- Using the Omnibus package, you're all set.
+- From source, make sure `rsync` is installed:
-If you're using Ubuntu, you could run:
+ ```sh
+ # Debian/Ubuntu
+ sudo apt-get install rsync
-```
-sudo apt-get install -y rsync
+ # RHEL/CentOS
+ sudo yum install rsync
+ ```
+
+### Tar
+
+Backup and restore tasks use `tar` under the hood to create and extract
+archives. Ensure you have version 1.30 or above of `tar` available in your
+system. To check the version, run:
+
+```sh
+tar --version
```
-### Backup timestamp
+## Backup timestamp
->**Note:**
+NOTE: **Note:**
In GitLab 9.2 the timestamp format was changed from `EPOCH_YYYY_MM_DD` to
`EPOCH_YYYY_MM_DD_GitLab_version`, for example `1493107454_2018_04_25`
would become `1493107454_2018_04_25_10.6.4-ce`.
@@ -44,30 +56,46 @@ available.
For example, if the backup name is `1493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar`,
then the timestamp is `1493107454_2018_04_25_10.6.4-ce`.
-### Creating a backup of the GitLab system
+## Creating a backup of the GitLab system
+
+GitLab provides a simple command line interface to backup your whole instance.
+It backs up your:
+
+- Database
+- Attachments
+- Git repositories data
+- CI/CD job output logs
+- CI/CD job artifacts
+- LFS objects
+- Container Registry images
+- GitLab Pages content
+
+CAUTION: **Warning:**
+GitLab does not back up any configuration files, SSL certificates, or system files.
+You are highly advised to [read about storing configuration files](#storing-configuration-files).
Use this command if you've installed GitLab with the Omnibus package:
-```
+```sh
sudo gitlab-rake gitlab:backup:create
```
Use this if you've installed GitLab from source:
-```
+```sh
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
If you are running GitLab within a Docker container, you can run the backup from the host:
-```
+```sh
docker exec -t <container name> gitlab-rake gitlab:backup:create
```
If you are using the gitlab-omnibus helm chart on a Kubernetes cluster, you can
-run the backup task on the gitlab application pod using kubectl
+run the backup task on the gitlab application pod using kubectl:
-```
+```sh
kubectl exec -it <gitlab-gitlab pod> gitlab-rake gitlab:backup:create
```
@@ -100,9 +128,50 @@ Deleting tmp directories...[DONE]
Deleting old backups... [SKIPPING]
```
+## Storing configuration files
+
+A backup performed by the [raketask GitLab provides](#creating-a-backup-of-the-gitlab-system)
+does **not** store your configuration files. The primary reason for this is that your
+database contains encrypted information for two-factor authentication, the CI/CD
+'secure variables', etc. Storing encrypted information along with its key in the
+same place defeats the purpose of using encryption in the first place.
+
+CAUTION: **Warning:**
+The secrets file is essential to preserve your database encryption key.
+
+At the very **minimum**, you must backup:
+
+For Omnibus:
+
+- `/etc/gitlab/gitlab-secrets.json`
+- `/etc/gitlab/gitlab.rb`
+
+For installation from source:
+
+- `/home/git/gitlab/config/secrets.yml`
+- `/home/git/gitlab/config/gitlab.yml`
+
+For [Docker installations](https://docs.gitlab.com/omnibus/docker/), you must
+back up the volume where the configuration files are stored. If you have created
+the GitLab container according to the documentation, it should be under
+`/srv/gitlab/config`.
+
+You may also want to back up any TLS keys and certificates, and your
+[SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079).
+
+If you use Omnibus GitLab, see some additional information
+[to backup your configuration](https://docs.gitlab.com/omnibus/settings/backups.html).
+
+In the unlikely event that the secrets file is lost, see the
+[troubleshooting section](#when-the-secrets-file-is-lost).
+
+## Backup options
+
+The command line tool GitLab provides to backup your instance can take more options.
+
### Backup strategy option
-> **Note:** Introduced as an option in GitLab 8.17.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8728) in GitLab 8.17.
The default backup strategy is to essentially stream data from the respective
data locations to the backup using the Linux command `tar` and `gzip`. This works
@@ -119,12 +188,15 @@ so the problem doesn't compound, but it could be a considerable change for large
installations. This is why the `copy` strategy is not the default in 8.17.
To use the `copy` strategy instead of the default streaming strategy, specify
-`STRATEGY=copy` in the Rake task command. For example,
-`sudo gitlab-rake gitlab:backup:create STRATEGY=copy`.
+`STRATEGY=copy` in the Rake task command. For example:
+
+```sh
+sudo gitlab-rake gitlab:backup:create STRATEGY=copy
+```
### Excluding specific directories from the backup
-You can choose what should be backed up by adding the environment variable `SKIP`.
+You can choose what should be exempt from the backup up by adding the environment variable `SKIP`.
The available options are:
- `db` (database)
@@ -138,11 +210,18 @@ The available options are:
Use a comma to specify several options at the same time:
-```
-# use this command if you've installed GitLab with the Omnibus package
+All wikis will be backed up as part of the `repositories` group. Non-existent wikis
+will be skipped during a backup.
+
+For Omnibus GitLab packages:
+
+```sh
sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
+```
-# if you've installed GitLab from source
+For installations from source:
+
+```sh
sudo -u git -H bundle exec rake gitlab:backup:create SKIP=db,uploads RAILS_ENV=production
```
@@ -195,7 +274,7 @@ This example can be used for a bucket in Amsterdam (AMS3).
1. [Reconfigure GitLab] for the changes to take effect
-CAUTION: **Warning:**
+NOTE: **Note:**
If you see `400 Bad Request` by using Digital Ocean Spaces, the cause may be the
usage of backup encryption. Remove or comment the line that
contains `gitlab_rails['backup_encryption']` since Digital Ocean Spaces
@@ -357,33 +436,43 @@ backups will be copied to, and will be created if it does not exist. If the
directory that you want to copy the tarballs to is the root of your mounted
directory, just use `.` instead.
-For omnibus packages:
-```ruby
-gitlab_rails['backup_upload_connection'] = {
- :provider => 'Local',
- :local_root => '/mnt/backups'
-}
+For Omnibus GitLab packages:
-# The directory inside the mounted folder to copy backups to
-# Use '.' to store them in the root directory
-gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups'
-```
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_rails['backup_upload_connection'] = {
+ :provider => 'Local',
+ :local_root => '/mnt/backups'
+ }
+
+ # The directory inside the mounted folder to copy backups to
+ # Use '.' to store them in the root directory
+ gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups'
+ ```
+
+1. [Reconfigure GitLab] for the changes to take effect.
+
+---
For installations from source:
-```yaml
- backup:
- # snip
- upload:
- # Fog storage connection settings, see http://fog.io/storage/ .
- connection:
- provider: Local
- local_root: '/mnt/backups'
- # The directory inside the mounted folder to copy backups to
- # Use '.' to store them in the root directory
- remote_directory: 'gitlab_backups'
-```
+1. Edit `home/git/gitlab/config/gitlab.yml`:
+
+ ```yaml
+ backup:
+ upload:
+ # Fog storage connection settings, see http://fog.io/storage/ .
+ connection:
+ provider: Local
+ local_root: '/mnt/backups'
+ # The directory inside the mounted folder to copy backups to
+ # Use '.' to store them in the root directory
+ remote_directory: 'gitlab_backups'
+ ```
+
+1. [Restart GitLab] for the changes to take effect.
### Backup archive permissions
@@ -392,45 +481,56 @@ will have owner/group git:git and 0600 permissions by default.
This is meant to avoid other system users reading GitLab's data.
If you need the backup archives to have different permissions you can use the 'archive_permissions' setting.
-```
-# In /etc/gitlab/gitlab.rb, for omnibus packages
-gitlab_rails['backup_archive_permissions'] = 0644 # Makes the backup archives world-readable
-```
+For Omnibus GitLab packages:
-```
-# In gitlab.yml, for installations from source:
- backup:
- archive_permissions: 0644 # Makes the backup archives world-readable
-```
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_rails['backup_archive_permissions'] = 0644 # Makes the backup archives world-readable
+ ```
-### Storing configuration files
+1. [Reconfigure GitLab] for the changes to take effect.
+
+---
+
+For installations from source:
-Please be informed that a backup does not store your configuration
-files. One reason for this is that your database contains encrypted
-information for two-factor authentication. Storing encrypted
-information along with its key in the same place defeats the purpose
-of using encryption in the first place!
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
-If you use an Omnibus package please see the [instructions in the readme to backup your configuration](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#backup-and-restore-omnibus-gitlab-configuration).
-If you have a cookbook installation there should be a copy of your configuration in Chef.
-If you installed from source, please consider backing up your `config/secrets.yml` file, `gitlab.yml` file, any SSL keys and certificates, and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079).
+ ```yaml
+ backup:
+ archive_permissions: 0644 # Makes the backup archives world-readable
+ ```
-At the very **minimum** you should backup `/etc/gitlab/gitlab.rb` and
-`/etc/gitlab/gitlab-secrets.json` (Omnibus), or
-`/home/git/gitlab/config/secrets.yml` (source) to preserve your database
-encryption key.
+1. [Restart GitLab] for the changes to take effect.
### Configuring cron to make daily backups
->**Note:**
+NOTE: **Note:**
The following cron jobs do not [backup your GitLab configuration files](#storing-configuration-files)
or [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079).
-**For Omnibus installations**
+For Omnibus GitLab packages:
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ ## Limit backup lifetime to 7 days - 604800 seconds
+ gitlab_rails['backup_keep_time'] = 604800
+ ```
+
+1. [Reconfigure GitLab] for the changes to take effect.
+
+Note that the `backup_keep_time` configuration option only manages local
+files. GitLab does not automatically prune old files stored in a third-party
+object storage (e.g., AWS S3) because the user may not have permission to list
+and delete files. We recommend that you configure the appropriate retention
+policy for your object storage. For example, you can configure [the S3 backup
+policy as described here](http://stackoverflow.com/questions/37553070/gitlab-omnibus-delete-backup-from-amazon-s3).
To schedule a cron job that backs up your repositories and GitLab metadata, use the root user:
-```
+```sh
sudo su -
crontab -e
```
@@ -442,26 +542,24 @@ There, add the following line to schedule the backup for everyday at 2 AM:
```
You may also want to set a limited lifetime for backups to prevent regular
-backups using all your disk space. To do this add the following lines to
-`/etc/gitlab/gitlab.rb` and reconfigure:
+backups using all your disk space.
-```
-# limit backup lifetime to 7 days - 604800 seconds
-gitlab_rails['backup_keep_time'] = 604800
-```
+---
-Note that the `backup_keep_time` configuration option only manages local
-files. GitLab does not automatically prune old files stored in a third-party
-object storage (e.g., AWS S3) because the user may not have permission to list
-and delete files. We recommend that you configure the appropriate retention
-policy for your object storage. For example, you can configure [the S3 backup
-policy as described here](http://stackoverflow.com/questions/37553070/gitlab-omnibus-delete-backup-from-amazon-s3).
+For installations from source:
+
+1. Edit `home/git/gitlab/config/gitlab.yml`:
-**For installation from source**
+ ```yaml
+ backup:
+ ## Limit backup lifetime to 7 days - 604800 seconds
+ keep_time: 604800
+ ```
-```
-cd /home/git/gitlab
-sudo -u git -H editor config/gitlab.yml # Enable keep_time in the backup section to automatically delete old backups
+1. [Restart GitLab] for the changes to take effect.
+
+
+```sh
sudo -u git crontab -e # Edit the crontab for the git user
```
@@ -510,7 +608,7 @@ more of the following options:
- `BACKUP=timestamp_of_backup` - Required if more than one backup exists.
Read what the [backup timestamp is about](#backup-timestamp).
-- `force=yes` - Does not ask if the authorized_keys file should get regenerated and assumes 'yes' for warning that database tables will be removed.
+- `force=yes` - Does not ask if the authorized_keys file should get regenerated and assumes 'yes' for warning that database tables will be removed, enabling the "Write to authorized_keys file" setting, and updating LDAP providers.
If you are restoring into directories that are mountpoints you will need to make
sure these directories are empty before attempting a restore. Otherwise GitLab
@@ -578,10 +676,11 @@ This procedure assumes that:
First make sure your backup tar file is in the backup directory described in the
`gitlab.rb` configuration `gitlab_rails['backup_path']`. The default is
-`/var/opt/gitlab/backups`.
+`/var/opt/gitlab/backups`. It needs to be owned by the `git` user.
```shell
sudo cp 11493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar /var/opt/gitlab/backups/
+sudo chown git.git /var/opt/gitlab/backups/11493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar
```
Stop the processes that are connected to the database. Leave the rest of GitLab
@@ -697,5 +796,53 @@ Those objects have no influence on the database backup/restore but they give thi
For more information see similar questions on postgresql issue tracker[here](http://www.postgresql.org/message-id/201110220712.30886.adrian.klaver@gmail.com) and [here](http://www.postgresql.org/message-id/2039.1177339749@sss.pgh.pa.us) as well as [stack overflow](http://stackoverflow.com/questions/4368789/error-must-be-owner-of-language-plpgsql).
+### When the secrets file is lost
+
+If you have failed to [back up the secrets file](#storing-configuration-files),
+then users with 2FA enabled will not be able to log into GitLab. In that case,
+you need to [disable 2FA for everyone](../security/two_factor_authentication.md#disabling-2fa-for-everyone).
+
+In the case of CI/CD, if your project has secure variables set, you might experience
+some weird behavior, like stuck jobs or 500 errors. In that case, you can try
+deleting the `ci_variables` table from the database.
+
+CAUTION: **Warning:**
+Use the following commands at your own risk, and make sure you've taken a
+backup beforehand.
+
+1. Enter the Rails console:
+
+ For Omnibus GitLab packages:
+
+ ```sh
+ sudo gitlab-rails dbconsole
+ ```
+
+ For installations from source:
+
+ ```sh
+ sudo -u git -H bundle exec rails dbconsole RAILS_ENV=production
+ ```
+
+1. Check the `ci_variables` table:
+
+ ```sql
+ SELECT * FROM public."ci_variables";
+ ```
+
+ Those are the variables that you need to delete.
+
+1. Drop the table:
+
+ ```sql
+ DELETE FROM ci_variables;
+ ```
+
+1. You may need to reconfigure or restart GitLab for the changes to take
+ effect.
+
+You should now be able to visit your project, and the jobs will start
+running again.
+
[reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
[restart GitLab]: ../administration/restart_gitlab.md#installations-from-source
diff --git a/doc/raketasks/import.md b/doc/raketasks/import.md
index 97e9b36d1a6..bb316df5b9a 100644
--- a/doc/raketasks/import.md
+++ b/doc/raketasks/import.md
@@ -6,6 +6,7 @@
- The groups will be created as needed, including subgroups
- The owner of the group will be the first admin
- Existing projects will be skipped
+- Projects in hashed storage may be skipped (see [Importing bare repositories from hashed storage](#importing-bare-repositories-from-hashed-storage))
- The existing Git repos will be moved from disk (removed from the original path)
## How to use
@@ -26,7 +27,6 @@ sudo -u git mkdir /var/opt/gitlab/git-data/repository-import-<date>/new_group
If we copy the repos to `/var/opt/gitlab/git-data/repository-import-<date>`, and repo A needs to be under the groups G1 and G2, it will
have to be created under those folders: `/var/opt/gitlab/git-data/repository-import-<date>/G1/G2/A.git`.
-
```
sudo cp -r /old/git/foo.git /var/opt/gitlab/git-data/repository-import-<date>/new_group/
@@ -70,3 +70,73 @@ Processing /var/opt/gitlab/git-data/repository-import-1/group/xyz.git
* Skipping repo /var/opt/gitlab/git-data/repository-import-1/@shared/a/b/abcd.git
[...]
```
+
+## Importing bare repositories from hashed storage
+
+### Background
+
+Projects in legacy storage have a directory structure that mirrors their full
+project path in GitLab, including their namespace structure. This information is
+leveraged by the bare repository importer to import projects into their proper
+locations. Each project and its parent namespaces are meaningfully named.
+
+However, the directory structure of projects in hashed storage do not contain
+this information. This is beneficial for a variety of reasons, especially
+improved performance and data integrity. See
+[Repository Storage Types](../administration/repository_storage_types.md) for
+more details.
+
+### Which repositories are importable?
+
+#### GitLab 10.3 or earlier
+
+Importing bare repositories from hashed storage is unsupported.
+
+#### GitLab 10.4 and later
+
+To support importing bare repositories from hashed storage, GitLab 10.4 and
+later stores the full project path with each repository, in a special section of
+the git repository's config file. This section is formatted as follows:
+
+```
+[gitlab]
+ fullpath = gitlab-org/gitlab-ce
+```
+
+However, existing repositories were not migrated to include this path.
+
+Bare repositories are importable if the following events occurred to the
+repository in GitLab 10.4 and later:
+
+- Created
+- Migrated to hashed storage
+- Renamed
+- Transferred to another namespace
+- Ancestor renamed
+- Ancestor transferred to another namespace
+
+Bare repositories are **not** importable by GitLab 10.4 and later when all the following are true about the repository:
+
+- It was created in GitLab 10.3 or earlier.
+- It was not renamed, transferred, or migrated to hashed storage in GitLab 10.4 and later.
+- Its ancestor namespaces were not renamed or transferred in GitLab 10.4 and later.
+
+There is an [open issue to add a migration to make all bare repositories
+importable](https://gitlab.com/gitlab-org/gitlab-ce/issues/41776).
+
+Until then, you may wish to manually migrate repositories yourself. You can use
+[Rails console](https://docs.gitlab.com/omnibus/maintenance/#starting-a-rails-console-session)
+to do so. In a Rails console session, run the following to migrate a project:
+
+```
+project = Project.find_by_full_path('gitlab-org/gitlab-ce')
+project.write_repository_config
+```
+
+In a Rails console session, run the following to migrate all of a namespace's
+projects (this may take a while if there are 1000s of projects in a namespace):
+
+```
+namespace = Namespace.find_by_full_path('gitlab-org')
+namespace.send(:write_projects_repository_config)
+```
diff --git a/doc/security/README.md b/doc/security/README.md
index d397ff104ab..e22dc00759d 100644
--- a/doc/security/README.md
+++ b/doc/security/README.md
@@ -10,6 +10,7 @@ comments: false
- [Webhooks and insecure internal web services](webhooks.md)
- [Information exclusivity](information_exclusivity.md)
- [Reset your root password](reset_root_password.md)
+- [Unlock a locked user](unlock_user.md)
- [User File Uploads](user_file_uploads.md)
- [How we manage the CRIME vulnerability](crime_vulnerability.md)
- [Enforce Two-factor authentication](two_factor_authentication.md)
diff --git a/doc/security/img/outbound_requests_section.png b/doc/security/img/outbound_requests_section.png
index 95c9c6ee771..f7783f34cdd 100644
--- a/doc/security/img/outbound_requests_section.png
+++ b/doc/security/img/outbound_requests_section.png
Binary files differ
diff --git a/doc/security/img/ssh_keys_restrictions_settings.png b/doc/security/img/ssh_keys_restrictions_settings.png
index 2e918fd4b3f..94258af3bf9 100644
--- a/doc/security/img/ssh_keys_restrictions_settings.png
+++ b/doc/security/img/ssh_keys_restrictions_settings.png
Binary files differ
diff --git a/doc/security/img/two_factor_authentication_group_settings.png b/doc/security/img/two_factor_authentication_group_settings.png
index a1b3c58bfdc..05d95554fd9 100644
--- a/doc/security/img/two_factor_authentication_group_settings.png
+++ b/doc/security/img/two_factor_authentication_group_settings.png
Binary files differ
diff --git a/doc/security/img/two_factor_authentication_settings.png b/doc/security/img/two_factor_authentication_settings.png
index 6d89be1eb04..2a2208f98bd 100644
--- a/doc/security/img/two_factor_authentication_settings.png
+++ b/doc/security/img/two_factor_authentication_settings.png
Binary files differ
diff --git a/doc/security/rack_attack.md b/doc/security/rack_attack.md
index 3efb19c1526..07e7b3da13b 100644
--- a/doc/security/rack_attack.md
+++ b/doc/security/rack_attack.md
@@ -10,7 +10,7 @@ Rack Attack offers IP whitelisting, blacklisting, Fail2ban style filtering and
tracking.
**Note:** Starting with 11.2, Rack Attack is disabled by default. To continue
-using this feature, please enable it in your `gitlab.rb` by setting
+using this feature, please enable it in your `gitlab.rb` by setting
`gitlab_rails['rack_attack_git_basic_auth'] = true`.
By default, user sign-in, user sign-up (if enabled), and user password reset is
@@ -41,7 +41,7 @@ For more information on how to use these options check out
}
```
-3. Reconfigure GitLab:
+1. Reconfigure GitLab:
```
sudo gitlab-ctl reconfigure
@@ -98,26 +98,26 @@ In case you want to remove a blocked IP, follow these steps:
grep "Rack_Attack" /var/log/gitlab/gitlab-rails/production.log
```
-2. Since the blacklist is stored in Redis, you need to open up `redis-cli`:
+1. Since the blacklist is stored in Redis, you need to open up `redis-cli`:
```sh
/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket
```
-3. You can remove the block using the following syntax, replacing `<ip>` with
+1. You can remove the block using the following syntax, replacing `<ip>` with
the actual IP that is blacklisted:
```
del cache:gitlab:rack::attack:allow2ban:ban:<ip>
```
-4. Confirm that the key with the IP no longer shows up:
+1. Confirm that the key with the IP no longer shows up:
```
keys *rack::attack*
```
-5. Optionally, add the IP to the whitelist to prevent it from being blacklisted
+1. Optionally, add the IP to the whitelist to prevent it from being blacklisted
again (see [settings](#settings)).
## Troubleshooting
@@ -129,11 +129,11 @@ the load balancer. In that case, you will need to:
1. [Configure `nginx[real_ip_trusted_addresses]`](https://docs.gitlab.com/omnibus/settings/nginx.html#configuring-gitlab-trusted_proxies-and-the-nginx-real_ip-module).
This will keep users' IPs from being listed as the load balancer IPs.
-2. Whitelist the load balancer's IP address(es) in the Rack Attack [settings](#settings).
-3. Reconfigure GitLab:
+1. Whitelist the load balancer's IP address(es) in the Rack Attack [settings](#settings).
+1. Reconfigure GitLab:
```
sudo gitlab-ctl reconfigure
```
-4. [Remove the block via Redis.](#remove-blocked-ips-from-rack-attack-via-redis)
+1. [Remove the block via Redis.](#remove-blocked-ips-from-rack-attack-via-redis)
diff --git a/doc/security/reset_root_password.md b/doc/security/reset_root_password.md
index 3c13f262677..6a882ed6fe5 100644
--- a/doc/security/reset_root_password.md
+++ b/doc/security/reset_root_password.md
@@ -37,4 +37,4 @@ Don't forget to save the changes.
user.save!
```
-Exit the console and try to login with your new password. \ No newline at end of file
+Exit the console and try to login with your new password.
diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md
index f02f7b807cf..b770f2544d2 100644
--- a/doc/security/two_factor_authentication.md
+++ b/doc/security/two_factor_authentication.md
@@ -11,10 +11,10 @@ You can read more about it here:
## Enforcing 2FA for all users
Users on GitLab, can enable it without any admin's intervention. If you want to
-enforce everyone to setup 2FA, you can choose from two different ways:
+enforce everyone to set up 2FA, you can choose from two different ways:
- 1. Enforce on next login
- 2. Suggest on next login, but allow a grace period before enforcing.
+- Enforce on next login.
+- Suggest on next login, but allow a grace period before enforcing.
In the Admin area under **Settings** (`/admin/application_settings`), look for
the "Sign-in Restrictions" area, where you can configure both.
diff --git a/doc/security/unlock_user.md b/doc/security/unlock_user.md
new file mode 100644
index 00000000000..d5ecef7f605
--- /dev/null
+++ b/doc/security/unlock_user.md
@@ -0,0 +1,31 @@
+# How to unlock a locked user
+
+Log into your server with root privileges. Then start a Ruby on Rails console.
+
+Start the console with this command:
+
+```bash
+gitlab-rails console production
+```
+
+Wait until the console has loaded.
+
+There are multiple ways to find your user. You can search for email or username.
+
+```bash
+user = User.where(id: 1).first
+```
+
+or
+
+```bash
+user = User.find_by(email: 'admin@local.host')
+```
+
+Unlock the user:
+
+```bash
+user.unlock_access!
+```
+
+Exit the console, the user should now be able to log in again.
diff --git a/doc/security/user_email_confirmation.md b/doc/security/user_email_confirmation.md
index 4293944ae8b..8c07e11dcb1 100644
--- a/doc/security/user_email_confirmation.md
+++ b/doc/security/user_email_confirmation.md
@@ -1,7 +1,7 @@
# User email confirmation at sign-up
-Gitlab admin can enable email confirmation on sign-up, if you want to confirm all
+GitLab admin can enable email confirmation on sign-up, if you want to confirm all
user emails before they are able to sign-in.
In the Admin area under **Settings** (`/admin/application_settings`), go to section
-**Sign-in Restrictions** and look for **Send confirmation email on sign-up** option.
+**Sign-up Restrictions** and look for **Send confirmation email on sign-up** option.
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 63f0a654fcf..c5b7813b285 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -8,159 +8,224 @@ you need a secure communication channel for sharing information.
The SSH protocol provides this security and allows you to authenticate to the
GitLab remote server without supplying your username or password each time.
-For a more detailed explanation of how the SSH protocol works, we advise you to
-read [this nice tutorial by DigitalOcean](https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process).
+For a more detailed explanation of how the SSH protocol works, read
+[this nice tutorial by DigitalOcean](https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process).
-## Locating an existing SSH key pair
+## Requirements
-Before generating a new SSH key pair check if your system already has one
-at the default location by opening a shell, or Command Prompt on Windows,
-and running the following command:
+The only requirement is to have the OpenSSH client installed on your system. This
+comes pre-installed on GNU/Linux and macOS, but not on Windows.
-**Windows Command Prompt:**
+Depending on your Windows version, there are different methods to work with
+SSH keys.
-```bash
-type %userprofile%\.ssh\id_rsa.pub
-```
+### Installing the SSH client for Windows 10
-**Git Bash on Windows / GNU/Linux / macOS / PowerShell:**
+Starting with Windows 10, you can
+[install the Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install-win10)
+where you can run Linux distributions directly on Windows, without the overhead
+of a virtual machine. Once installed and set up, you'll have the Git and SSH
+clients at your disposal.
-```bash
-cat ~/.ssh/id_rsa.pub
-```
+### Installing the SSH client for Windows 8.1 and Windows 7
+
+The easiest way to install Git and the SSH client on Windows 8.1 and Windows 7
+is [Git for Windows](https://gitforwindows.com). It provides a BASH
+emulation (Git Bash) used for running Git from the command line and the
+`ssh-keygen` command that is useful to create SSH keys as you'll learn below.
+
+NOTE: **Alternative tools:**
+Although not explored in this page, you can use some alternative tools.
+[Cygwin](https://www.cygwin.com) is a large collection of GNU and open source
+tools which provide functionality similar to a Unix distribution.
+[PuttyGen](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html)
+provides a graphical user interface to [create SSH keys](https://tartarus.org/~simon/putty-snapshots/htmldoc/Chapter8.html#pubkey-puttygen).
+
+## Types of SSH keys and which to choose
+
+GitLab supports RSA, DSA, ECDSA, and ED25519 keys. Their difference lies on
+the signing algorithm, and some of them have advantages over the others. For
+more information, you can read this
+[nice article on ArchWiki](https://wiki.archlinux.org/index.php/SSH_keys#Choosing_the_authentication_key_type).
+We'll focus on ED25519 and RSA and here.
+
+NOTE: **Note:**
+As an admin, you can restrict
+[which keys should be permitted and their minimum length](../security/ssh_keys_restrictions.md).
+By default, all keys are permitted, which is also the case for
+[GitLab.com](../user/gitlab_com/index.md#ssh-host-keys-fingerprints).
-If you see a string starting with `ssh-rsa` you already have an SSH key pair
-and you can skip the generate portion of the next section and skip to the copy
-to clipboard step.
-If you don't see the string or would like to generate a SSH key pair with a
-custom name continue onto the next step.
+## ED25519 SSH keys
-Note that Public SSH key may also be named as follows:
+Following [best practices](https://linux-audit.com/using-ed25519-openssh-keys-instead-of-dsa-rsa-ecdsa/),
+you should always favor [ED25519](https://ed25519.cr.yp.to/) SSH keys, since they
+are more secure and have better performance over the other types.
-- `id_dsa.pub`
-- `id_ecdsa.pub`
-- `id_ed25519.pub`
+They were introduced in OpenSSH 6.5, so any modern OS should include the
+option to create them. If for any reason your OS or the GitLab instance you
+interact with doesn't support this, you can fallback to RSA.
+
+## RSA SSH keys
+
+RSA keys are the most common ones and therefore the most compatible with
+servers that may have an old OpenSSH version. Use them if the GitLab server
+doesn't work with ED25519 keys.
+
+The minimum key size is 1024 bits, defaulting to 2048. If you wish to generate a
+stronger RSA key pair, specify the `-b` flag with a higher bit value than the
+default.
+
+The old, default password encoding for SSH private keys keys is
+[insecure](https://latacora.singles/2018/08/03/the-default-openssh.html);
+it's only a single round of an MD5 hash. Since OpenSSH version 6.5, you should
+use the `-o` option to `ssh-keygen` to encode your private key in a new, more
+secure format.
+
+If you already have an RSA SSH key pair to use with GitLab, consider upgrading it
+to use the more secure password encryption format by using the following command
+on the private key:
+
+```bash
+ssh-keygen -o -f ~/.ssh/id_rsa
+```
## Generating a new SSH key pair
-1. To generate a new SSH key pair, use the following command:
+Before creating an SSH key pair, make sure to read about the
+[different types of keys](#types-of-ssh-keys-and-which-to-choose) to understand
+their differences.
+
+To create a new SSH key pair:
- **Git Bash on Windows / GNU/Linux / macOS:**
+1. Open a terminal on Linux or macOS, or Git Bash / WSL on Windows.
+1. Generate a new ED25519 SSH key pair:
```bash
- ssh-keygen -t rsa -C "your.email@example.com" -b 4096
+ ssh-keygen -t ed25519 -C "email@example.com"
```
- **Windows:**
+ Or, if you want to use RSA:
- Alternatively on Windows you can download
- [PuttyGen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)
- and follow [this documentation article][winputty] to generate a SSH key pair.
+ ```bash
+ ssh-keygen -o -t rsa -b 4096 -C "email@example.com"
+ ```
+
+ The `-C` flag adds a comment in the key in case you have multiple of them
+ and want to tell which is which. It is optional.
1. Next, you will be prompted to input a file path to save your SSH key pair to.
+ If you don't already have an SSH key pair, use the suggested path by pressing
+ <kbd>Enter</kbd>. Using the suggested path will normally allow your SSH client
+ to automatically use the SSH key pair with no additional configuration.
- If you don't already have an SSH key pair use the suggested path by pressing
- enter. Using the suggested path will normally allow your SSH client
- to automatically use the SSH key pair with no additional configuration.
+ If you already have an SSH key pair with the suggested file path, you will need
+ to input a new file path and [declare what host](#working-with-non-default-ssh-key-pair-paths)
+ this SSH key pair will be used for in your `~/.ssh/config` file.
- If you already have a SSH key pair with the suggested file path, you will need
- to input a new file path and declare what host this SSH key pair will be used
- for in your `.ssh/config` file, see [**Working with non-default SSH key pair paths**](#working-with-non-default-ssh-key-pair-paths)
- for more information.
+1. Once the path is decided, you will be prompted to input a password to
+ secure your new SSH key pair. It's a best practice to use a password,
+ but it's not required and you can skip creating it by pressing
+ <kbd>Enter</kbd> twice.
-1. Once you have input a file path you will be prompted to input a password to
- secure your SSH key pair. It is a best practice to use a password for an SSH
- key pair, but it is not required and you can skip creating a password by
- pressing enter.
+ If, in any case, you want to add or change the password of your SSH key pair,
+ you can use the `-p`flag:
- NOTE: **Note:**
- If you want to change the password of your SSH key pair, you can use
- `ssh-keygen -p <keyname>`.
+ ```
+ ssh-keygen -p -o -f <keyname>
+ ```
-## Adding a SSH key to your GitLab account
+Now, it's time to add the newly created public key to your GitLab account.
-1. The next step is to copy the public SSH key as we will need it afterwards.
+## Adding an SSH key to your GitLab account
- To copy your public SSH key to the clipboard, use the appropriate code below:
+1. Copy your **public** SSH key to the clipboard by using one of the commands below
+ depending on your Operating System:
**macOS:**
```bash
- pbcopy < ~/.ssh/id_rsa.pub
+ pbcopy < ~/.ssh/id_ed25519.pub
```
- **GNU/Linux (requires the xclip package):**
+ **WSL / GNU/Linux (requires the xclip package):**
```bash
- xclip -sel clip < ~/.ssh/id_rsa.pub
+ xclip -sel clip < ~/.ssh/id_ed25519.pub
```
- **Windows Command Line:**
+ **Git Bash on Windows:**
```bash
- type %userprofile%\.ssh\id_rsa.pub | clip
+ cat ~/.ssh/id_ed25519.pub | clip
```
- **Git Bash on Windows / Windows PowerShell:**
+ You can also open the key in a graphical editor and copy it from there,
+ but be careful not to accidentally change anything.
- ```bash
- cat ~/.ssh/id_rsa.pub | clip
- ```
-
-1. The final step is to add your public SSH key to GitLab.
+ NOTE: **Note:**
+ If you opted to create an RSA key, the name might differ.
- Navigate to the 'SSH Keys' tab in your 'Profile Settings'.
- Paste your key in the 'Key' section and give it a relevant 'Title'.
- Use an identifiable title like 'Work Laptop - Windows 7' or
- 'Home MacBook Pro 15'.
+1. Add your public SSH key to your GitLab account by clicking your avatar
+ in the upper right corner and selecting **Settings**. From there on,
+ navigate to **SSH Keys** and paste your public key in the "Key" section.
+ If you created the key with a comment, this will appear under "Title".
+ If not, give your key an identifiable title like _Work Laptop_ or
+ _Home Workstation_, and click **Add key**.
+ NOTE: **Note:**
If you manually copied your public SSH key make sure you copied the entire
- key starting with `ssh-rsa` and ending with your email.
+ key starting with `ssh-ed25519` (or `ssh-rsa`) and ending with your email.
-1. Optionally you can test your setup by running `ssh -T git@example.com`
- (replacing `example.com` with your GitLab domain) and verifying that you
- receive a `Welcome to GitLab` message.
+## Testing that everything is set up correctly
+
+To test whether your SSH key was added correctly, run the following command in
+your terminal (replacing `gitlab.com` with your GitLab's instance domain):
+
+```bash
+ssh -T git@gitlab.com
+```
+
+You should receive a _Welcome to GitLab, `@username`!_ message.
+
+If the welcome message doesn't appear, run SSH's verbose mode by replacing `-T`
+with `-vvvT` to understand where the error is.
## Working with non-default SSH key pair paths
If you used a non-default file path for your GitLab SSH key pair,
you must configure your SSH client to find your GitLab private SSH key
-for connections to your GitLab server (perhaps `gitlab.com`).
+for connections to GitLab.
-For your current terminal session you can do so using the following commands
+Open a terminal and use the following commands
(replacing `other_id_rsa` with your private SSH key):
-**Git Bash on Windows / GNU/Linux / macOS:**
-
```bash
eval $(ssh-agent -s)
ssh-add ~/.ssh/other_id_rsa
```
-To retain these settings you'll need to save them to a configuration file.
-For OpenSSH clients this is configured in the `~/.ssh/config` file for some
-operating systems.
+To retain these settings, you'll need to save them to a configuration file.
+For OpenSSH clients this is configured in the `~/.ssh/config` file. In this
+file you can set up configurations for multiple hosts, like GitLab.com, your
+own GitLab instance, GitHub, Bitbucket, etc.
+
Below are two example host configurations using their own SSH key:
-```
-# GitLab.com server
+```conf
+# GitLab.com
Host gitlab.com
-RSAAuthentication yes
-IdentityFile ~/.ssh/config/private-key-filename-01
+ Preferredauthentications publickey
+ IdentityFile ~/.ssh/gitlab_com_rsa
-# Private GitLab server
+# Private GitLab instance
Host gitlab.company.com
-RSAAuthentication yes
-IdentityFile ~/.ssh/config/private-key-filename
+ Preferredauthentications publickey
+ IdentityFile ~/.ssh/example_com_rsa
```
-Due to the wide variety of SSH clients and their very large number of
-configuration options, further explanation of these topics is beyond the scope
-of this document.
-
-Public SSH keys need to be unique, as they will bind to your account.
-Your SSH key is the only identifier you'll have when pushing code via SSH.
-That's why it needs to uniquely map to a single user.
+Public SSH keys need to be unique to GitLab, as they will bind to your account.
+Your SSH key is the only identifier you'll have when pushing code via SSH,
+that's why it needs to uniquely map to a single user.
## Deploy keys
@@ -191,15 +256,15 @@ project.
### Global shared deploy keys
-Global Shared Deploy keys allow read-only or read-write (if enabled) access to
+Global Shared Deploy keys allow read-only or read-write (if enabled) access to
be configured on any repository in the entire GitLab installation.
This is really useful for integrating repositories to secured, shared Continuous
-Integration (CI) services or other shared services.
-GitLab administrators can set up the Global Shared Deploy key in GitLab and
+Integration (CI) services or other shared services.
+GitLab administrators can set up the Global Shared Deploy key in GitLab and
add the private key to any shared systems. Individual repositories opt into
exposing their repository using these keys when a project maintainers (or higher)
-authorizes a Global Shared Deploy key to be used with their project.
+authorizes a Global Shared Deploy key to be used with their project.
Global Shared Keys can provide greater security compared to Per-Project Deploy
Keys since an administrator of the target integrated system is the only one
@@ -211,13 +276,13 @@ the primary way for project maintainers and owners to identify the correct Globa
Deploy key to add. For instance, if the key gives access to a SaaS CI instance,
use the name of that service in the key name if that is all it is used for.
When creating Global Shared Deploy keys, give some thought to the granularity
-of keys - they could be of very narrow usage such as just a specific service or
-of broader usage for something like "Anywhere you need to give read access to
+of keys - they could be of very narrow usage such as just a specific service or
+of broader usage for something like "Anywhere you need to give read access to
your repository".
-Once a GitLab administrator adds the Global Deployment key, project maintainers
-and owners can add it in project's **Settings > Repository** section by expanding the
-**Deploy Key** section and clicking **Enable** next to the appropriate key listed
+Once a GitLab administrator adds the Global Deployment key, project maintainers
+and owners can add it in project's **Settings > Repository** section by expanding the
+**Deploy Key** section and clicking **Enable** next to the appropriate key listed
under **Public deploy keys available to any project**.
NOTE: **Note:**
@@ -236,8 +301,6 @@ not implicitly give any access just by setting them up.
How to add your SSH key to Eclipse: https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
-[winputty]: https://the.earth.li/~sgtatham/putty/0.67/htmldoc/Chapter8.html#pubkey-puttygen
-
## SSH on the GitLab server
GitLab integrates with the system-installed SSH daemon, designating a user
diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md
index a8aa11265d0..394f3ea60b7 100644
--- a/doc/topics/authentication/index.md
+++ b/doc/topics/authentication/index.md
@@ -23,7 +23,7 @@ This page gathers all the resources for the topic **Authentication** within GitL
- [How to Configure LDAP with GitLab CE](../../administration/auth/how_to_configure_ldap_gitlab_ce/index.md)
- [How to Configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/)
- [Feature Highlight: LDAP Integration](https://about.gitlab.com/2014/07/10/feature-highlight-ldap-sync/)
- - [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/ldap/debugging_ldap.html)
+ - [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/support-engineering/ldap/debugging_ldap.html)
- **Integrations:**
- [OmniAuth](../../integration/omniauth.md)
- [Authentiq OmniAuth Provider](../../administration/auth/authentiq.md#authentiq-omniauth-provider)
@@ -36,14 +36,14 @@ This page gathers all the resources for the topic **Authentication** within GitL
## API
- [OAuth 2 Tokens](../../api/README.md#oauth-2-tokens)
-- [Private Tokens](../../api/README.md#private-tokens)
+- [Personal access tokens](../../api/README.md#personal-access-tokens)
- [Impersonation tokens](../../api/README.md#impersonation-tokens)
- [GitLab as an OAuth2 provider](../../api/oauth2.md#gitlab-as-an-oauth2-provider)
## Third-party resources
-- [Kanboard Plugin GitLab Authentication](https://kanboard.net/plugin/gitlab-auth)
-- [Jenkins GitLab OAuth Plugin](https://wiki.jenkins-ci.org/display/JENKINS/GitLab+OAuth+Plugin)
-- [Setup Gitlab CE with Active Directory authentication](https://www.caseylabs.com/setup-gitlab-ce-with-active-directory-authentication/)
+- [Kanboard Plugin GitLab Authentication](https://github.com/kanboard/plugin-gitlab-auth)
+- [Jenkins GitLab OAuth Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+OAuth+Plugin)
+- [Set up Gitlab CE with Active Directory authentication](https://www.caseylabs.com/setup-gitlab-ce-with-active-directory-authentication/)
- [How to customize GitLab to support OpenID authentication](http://eric.van-der-vlist.com/blog/2013/11/23/how-to-customize-gitlab-to-support-openid-authentication/)
- [Openshift - Configuring Authentication and User Agent](https://docs.openshift.org/latest/install_config/configuring_authentication.html#GitLab)
diff --git a/doc/topics/autodevops/img/guide_environments.png b/doc/topics/autodevops/img/guide_environments.png
index 1d8d5614e64..404db17c57a 100644
--- a/doc/topics/autodevops/img/guide_environments.png
+++ b/doc/topics/autodevops/img/guide_environments.png
Binary files differ
diff --git a/doc/topics/autodevops/img/guide_ide_commit.png b/doc/topics/autodevops/img/guide_ide_commit.png
index 188f60f2a4b..d7be66f4049 100644
--- a/doc/topics/autodevops/img/guide_ide_commit.png
+++ b/doc/topics/autodevops/img/guide_ide_commit.png
Binary files differ
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index f5574506595..6bb2e236dc1 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -2,40 +2,54 @@
> [Introduced][ce-37115] in GitLab 10.0. Generally available on GitLab 11.0.
-Auto DevOps automatically detects, builds, tests, deploys, and monitors your
-applications.
+Auto DevOps provides pre-defined CI/CD configuration which allows you to automatically detect, build, test,
+deploy, and monitor your applications. Leveraging CI/CD best practices and tools, Auto DevOps aims
+to simplify the setup and execution of a mature & modern software development lifecycle.
## Overview
+NOTE: **Enabled by default:**
+Starting with GitLab 11.3, the Auto DevOps pipeline is enabled by default for all
+projects. If it has not been explicitly enabled for the project, Auto DevOps will be automatically
+disabled on the first pipeline failure. Your project will continue to use an alternative
+[CI/CD configuration file](../../ci/yaml/README.md) if one is found. A GitLab
+administrator can [change this setting](../../user/admin_area/settings/continuous_integration.html#auto-devops)
+in the admin area.
+
With Auto DevOps, the software development process becomes easier to set up
as every project can have a complete workflow from verification to monitoring
-without needing to configure anything. Just push your code and GitLab takes
+with minimal configuration. Just push your code and GitLab takes
care of everything else. This makes it easier to start new projects and brings
consistency to how applications are set up throughout a company.
## Quick start
If you are using GitLab.com, see the [quick start guide](quick_start_guide.md)
-for using Auto DevOps with GitLab.com and a Kubernetes cluster on Google Kubernetes
-Engine.
+for how to use Auto DevOps with GitLab.com and a Kubernetes cluster on Google Kubernetes
+Engine (GKE).
+
+If you are using a self-hosted instance of GitLab, you will need to configure the
+[Google OAuth2 OmniAuth Provider](../../integration/google.md) before
+you can configure a cluster on GKE. Once this is set up, you can follow the steps on the
+[quick start guide](quick_start_guide.md) to get started.
## Comparison to application platforms and PaaS
-Auto DevOps provides functionality described by others as an application
-platform or as a Platform as a Service (PaaS). It takes inspiration from the
+Auto DevOps provides functionality that is often included in an application
+platform or a Platform as a Service (PaaS). It takes inspiration from the
innovative work done by [Heroku](https://www.heroku.com/) and goes beyond it
-in a couple of ways:
+in multiple ways:
-1. Auto DevOps works with any Kubernetes cluster, you're not limited to running
- on GitLab's infrastructure (note that many features also work without Kubernetes).
+1. Auto DevOps works with any Kubernetes cluster; you're not limited to running
+ on GitLab's infrastructure. (Note that many features also work without Kubernetes.)
1. There is no additional cost (no markup on the infrastructure costs), and you
can use a self-hosted Kubernetes cluster or Containers as a Service on any
- public cloud (for example [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/)).
+ public cloud (for example, [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/)).
1. Auto DevOps has more features including security testing, performance testing,
and code quality testing.
-1. It offers an incremental graduation path. If you need advanced customizations
+1. Auto DevOps offers an incremental graduation path. If you need advanced customizations,
you can start modifying the templates without having to start over on a
- completely different platform.
+ completely different platform. Review the [customizing](#customizing) section for more information.
## Features
@@ -71,11 +85,6 @@ For an overview on the creation of Auto DevOps, read the blog post [From 2/3 of
## Requirements
-TIP: **Tip:**
-For self-hosted installations, the easiest way to make use of Auto DevOps is to
-install GitLab inside a Kubernetes cluster using the [GitLab Omnibus Helm Chart]
-which automatically installs and configures everything you need!
-
To make full use of Auto DevOps, you will need:
1. **GitLab Runner** (needed for all stages) - Your Runner needs to be
@@ -101,10 +110,6 @@ To make full use of Auto DevOps, you will need:
Kubernetes cluster using the
[`nginx-ingress`](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress)
Helm chart.
- 1. **Wildcard TLS termination** - You can deploy the
- [`kube-lego`](https://github.com/kubernetes/charts/tree/master/stable/kube-lego)
- Helm chart to your Kubernetes cluster to automatically issue certificates
- for your domains using Let's Encrypt.
1. **Prometheus** (needed for Auto Monitoring) - To enable Auto Monitoring, you
will need Prometheus installed somewhere (inside or outside your cluster) and
configured to scrape your Kubernetes cluster. To get response metrics
@@ -148,18 +153,13 @@ Auto DevOps base domain to `1.2.3.4.nip.io`.
Once set up, all requests will hit the load balancer, which in turn will route
them to the Kubernetes pods that run your application(s).
-NOTE: **Note:**
-If GitLab is installed using the [GitLab Omnibus Helm Chart], there are two
-options: provide a static IP, or have one assigned. For more information see the
-relevant docs on the [network prerequisites](../../install/kubernetes/gitlab_omnibus.md#networking-prerequisites).
-
## Using multiple Kubernetes clusters **[PREMIUM]**
When using Auto DevOps, you may want to deploy different environments to
different Kubernetes clusters. This is possible due to the 1:1 connection that
[exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters).
-In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml)
+In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml)
(used behind the scenes by Auto DevOps), there are currently 3 defined environment names that you need to know:
- `review/` (every environment starting with `review/`)
@@ -203,23 +203,38 @@ and verifying that your app is deployed as a review app in the Kubernetes
cluster with the `review/*` environment scope. Similarly, you can check the
other environments.
-## Enabling Auto DevOps
+## Enabling/Disabling Auto DevOps
-If you haven't done already, read the [requirements](#requirements) to make
-full use of Auto DevOps. If this is your fist time, we recommend you follow the
+When first using Auto Devops, review the [requirements](#requirements) to ensure all necessary components to make
+full use of Auto DevOps are available. If this is your fist time, we recommend you follow the
[quick start guide](quick_start_guide.md).
-To enable Auto DevOps to your project:
+GitLab.com users can enable/disable Auto DevOps at the project-level only. Self-managed users
+can enable/disable Auto DevOps at either the project-level or instance-level.
+
+### Enabling/disabling Auto DevOps at the instance-level (Administrators only)
+
+1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
+1. Toggle the checkbox labeled **Default to Auto DevOps pipeline for all projects**.
+1. If enabling, optionally set up the Auto DevOps [base domain](#auto-devops-base-domain) which will be used for Auto Deploy and Auto Review Apps.
+1. Click **Save changes** for the changes to take effect.
+
+NOTE: **Note:**
+Even when disabled at the instance level, project maintainers are still able to enable
+Auto DevOps at the project level.
+
+### Enabling/disabling Auto DevOps at the project-level
+
+If enabling, check that your project doesn't have a `.gitlab-ci.yml`, or if one exists, remove it.
-1. Check that your project doesn't have a `.gitlab-ci.yml`, or remove it otherwise
-1. Go to your project's **Settings > CI/CD > Auto DevOps**
-1. Select "Enable Auto DevOps"
-1. Optionally, but recommended, add in the [base domain](#auto-devops-base-domain)
- that will be used by Kubernetes to [deploy your application](#auto-deploy)
- and choose the [deployment strategy](#deployment-strategy)
-1. Hit **Save changes** for the changes to take effect
+1. Go to your project's **Settings > CI/CD > Auto DevOps**.
+1. Toggle the **Default to Auto DevOps pipeline** checkbox (checked to enable, unchecked to disable)
+1. When enabling, it's optional but recommended to add in the [base domain](#auto-devops-base-domain)
+ that will be used by Auto DevOps to [deploy your application](#auto-deploy)
+ and choose the [deployment strategy](#deployment-strategy).
+1. Click **Save changes** for the changes to take effect.
-Once saved, an Auto DevOps pipeline will be triggered on the default branch.
+When the feature has been enabled, an Auto DevOps pipeline is triggered on the default branch.
NOTE: **Note:**
For GitLab versions 10.0 - 10.2, when enabling Auto DevOps, a pipeline needs to be
@@ -228,10 +243,9 @@ manually triggered either by pushing a new commit to the repository or by visiti
a new pipeline for your default branch, generally `master`.
NOTE: **Note:**
-If you are a GitLab Administrator, you can enable Auto DevOps instance wide
-in **Admin Area > Settings > Continuous Integration and Deployment**. Doing that,
-all the projects that haven't explicitly set an option will have Auto DevOps
-enabled by default.
+There is also a feature flag to enable Auto DevOps to a percentage of projects
+which can be enabled from the console with
+`Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(10)`.
### Deployment strategy
@@ -242,14 +256,19 @@ project's **Settings > CI/CD > Auto DevOps**.
The available options are:
-- **Continuous deployment to production** - enables [Auto Deploy](#auto-deploy)
- by setting the [`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
- [`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables
- to false.
-- **Automatic deployment to staging, manual deployment to production** - sets the
+- **Continuous deployment to production**: Enables [Auto Deploy](#auto-deploy)
+ with `master` branch directly deployed to production.
+- **Continuous deployment to production using timed incremental rollout**: Sets the
+ [`INCREMENTAL_ROLLOUT_MODE`](#timed-incremental-rollout-to-production) variable
+ to `timed`, and production deployment will be executed with a 5 minute delay between
+ each increment in rollout.
+- **Automatic deployment to staging, manual deployment to production**: Sets the
[`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
- [`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables
- to true, and the user is responsible for manually deploying to staging and production.
+ [`INCREMENTAL_ROLLOUT_MODE`](#incremental-rollout-to-production) variables
+ to `1` and `manual`. This means:
+
+ - `master` branch is directly deployed to staging.
+ - Manual actions are provided for incremental rollout to production.
## Stages of Auto DevOps
@@ -295,8 +314,7 @@ static analysis and other code checks on the current code. The report is
created, and is uploaded as an artifact which you can later download and check
out.
-In GitLab Starter, differences between the source and
-target branches are also
+Any differences between the source and target branches are also
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html).
### Auto SAST **[ULTIMATE]**
@@ -309,9 +327,12 @@ analysis on the current code and checks for potential security issues. Once the
report is created, it's uploaded as an artifact which you can later download and
check out.
-In GitLab Ultimate, any security warnings are also
+Any security warnings are also
[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/sast.html).
+NOTE: **Note:**
+The Auto SAST stage will be skipped on licenses other than Ultimate.
+
### Auto Dependency Scanning **[ULTIMATE]**
> Introduced in [GitLab Ultimate][ee] 10.7.
@@ -325,6 +346,9 @@ check out.
Any security warnings are also
[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/dependency_scanning.html).
+NOTE: **Note:**
+The Auto Dependency Scanning stage will be skipped on licenses other than Ultimate.
+
### Auto License Management **[ULTIMATE]**
> Introduced in [GitLab Ultimate][ee] 11.0.
@@ -338,6 +362,9 @@ check out.
Any licenses are also
[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/license_management.html).
+NOTE: **Note:**
+The Auto License Management stage will be skipped on licenses other than Ultimate.
+
### Auto Container Scanning
> Introduced in GitLab 10.4.
@@ -348,9 +375,12 @@ Docker image and checks for potential security issues. Once the report is
created, it's uploaded as an artifact which you can later download and
check out.
-In GitLab Ultimate, any security warnings are also
+Any security warnings are also
[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/container_scanning.html).
+NOTE: **Note:**
+The Auto Container Scanning stage will be skipped on licenses other than Ultimate.
+
### Auto Review Apps
NOTE: **Note:**
@@ -370,6 +400,9 @@ branch's code so developers, designers, QA, product managers, and other
reviewers can actually see and interact with code changes as part of the review
process. Auto Review Apps create a Review App for each branch.
+Auto Review Apps will deploy your app to your Kubernetes cluster only. When no cluster
+is available, no deployment will occur.
+
The Review App will have a unique URL based on the project name, the branch
name, and a unique number, combined with the Auto DevOps base domain. For
example, `user-project-branch-1234.example.com`. A link to the Review App shows
@@ -387,9 +420,12 @@ to perform an analysis on the current code and checks for potential security
issues. Once the report is created, it's uploaded as an artifact which you can
later download and check out.
-In GitLab Ultimate, any security warnings are also
+Any security warnings are also
[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/dast.html).
+NOTE: **Note:**
+The Auto DAST stage will be skipped on licenses other than Ultimate.
+
### Auto Browser Performance Testing **[PREMIUM]**
> Introduced in [GitLab Premium][ee] 10.4.
@@ -402,8 +438,8 @@ Auto Browser Performance Testing utilizes the [Sitespeed.io container](https://h
/direction
```
-In GitLab Premium, performance differences between the source
-and target branches are [shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/browser_performance_testing.html).
+Any performance differences between the source and target branches are also
+[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/browser_performance_testing.html).
### Auto Deploy
@@ -443,9 +479,33 @@ no longer be valid as soon as the deployment job finishes. This means that
Kubernetes can run the application, but in case it should be restarted or
executed somewhere else, it cannot be accessed again.
+> [Introduced][ce-21955] in GitLab 11.4
+
+Database initialization and migrations for PostgreSQL can be configured to run
+within the application pod by setting the project variables `DB_INITIALIZE` and
+`DB_MIGRATE` respectively.
+
+If present, `DB_INITIALIZE` will be run as a shell command within an application pod as a helm
+post-install hook. Note that this means that if any deploy succeeds,
+`DB_INITIALIZE` will not be processed thereafter.
+
+If present, `DB_MIGRATE` will be run as a shell command within an application pod as
+a helm pre-upgrade hook.
+
+For example, in a Rails application:
+
+* `DB_INITIALIZE` can be set to `cd /app && RAILS_ENV=production
+ bin/setup`
+* `DB_MIGRATE` can be set to `cd /app && RAILS_ENV=production bin/update`
+
+NOTE: **Note:**
+The `/app` path is the directory of your project inside the docker image
+as [configured by
+Herokuish](https://github.com/gliderlabs/herokuish#paths)
+
> [Introduced][ce-19507] in GitLab 11.0.
-For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md###gitlab-deploy-token)
+For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md###gitlab-deploy-token)
will be automatically created, when Auto DevOps is enabled and the Auto DevOps settings are saved. This Deploy Token
can be used for permanent access to the registry.
@@ -471,10 +531,7 @@ The metrics include:
- **Response Metrics:** latency, throughput, error rate
- **System Metrics:** CPU utilization, memory utilization
-If GitLab has been deployed using the [GitLab Omnibus Helm Chart], no
-configuration is required.
-
-If you have installed GitLab using a different method, you need to:
+In order to make use of monitoring you need to:
1. [Deploy Prometheus](../../user/project/integrations/prometheus.md#configuring-your-own-prometheus-server-within-kubernetes) into your Kubernetes cluster
1. If you would like response metrics, ensure you are running at least version
@@ -529,7 +586,7 @@ repo or by specifying a project variable:
file in it, Auto DevOps will detect the chart and use it instead of the [default
one](https://gitlab.com/charts/auto-deploy-app).
This can be a great way to control exactly how your application is deployed.
-- **Project variable** - Create a [project variable](../../ci/variables/README.md#secret-variables)
+- **Project variable** - Create a [project variable](../../ci/variables/README.md#variables)
`AUTO_DEVOPS_CHART` with the URL of a custom chart to use.
### Customizing `.gitlab-ci.yml`
@@ -569,13 +626,13 @@ postgres://user:password@postgres-host:postgres-port/postgres-database
### Environment variables
The following variables can be used for setting up the Auto DevOps domain,
-providing a custom Helm chart, or scaling your application. PostgreSQL can be
+providing a custom Helm chart, or scaling your application. PostgreSQL can
also be customized, and you can easily use a [custom buildpack](#custom-buildpacks).
| **Variable** | **Description** |
| ------------ | --------------- |
| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-domain); by default set automatically by the [Auto DevOps setting](#enabling-auto-devops). |
-| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/charts/charts.gitlab.io/tree/master/charts/auto-deploy-app). |
+| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/charts/auto-deploy-app). |
| `REPLICAS` | The number of replicas to deploy; defaults to 1. |
| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. This takes precedence over `REPLICAS`; defaults to 1. |
| `CANARY_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html); defaults to 1 |
@@ -587,9 +644,11 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` |
| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`.|
| `DEP_SCAN_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks).|
+| `DB_INITIALIZE` | From GitLab 11.4, this variable can be used to specify the command to run to initialize the application's PostgreSQL database. It runs inside the application pod. |
+| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. |
| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
-| `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. |
+| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> |
| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
@@ -598,10 +657,12 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. |
| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. |
| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. |
+| `OLD_REPORTS_DISABLED` | From GitLab 11.5, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
+| `NEW_REPORTS_DISABLED` | From GitLab 11.5, this variable can be used to disable the `sast_dashboard` job. If the variable is present, the job will not be created. |
TIP: **Tip:**
Set up the replica variables using a
-[project variable](../../ci/variables/README.md#secret-variables)
+[project variable](../../ci/variables/README.md#variables)
and scale your application by just redeploying it!
CAUTION: **Caution:**
@@ -679,7 +740,7 @@ staging environment and deploy to production manually. For this scenario, the
`STAGING_ENABLED` environment variable was introduced.
If `STAGING_ENABLED` is defined in your project (e.g., set `STAGING_ENABLED` to
-`1` as a secret variable), then the application will be automatically deployed
+`1` as a CI/CD variable), then the application will be automatically deployed
to a `staging` environment, and a `production_manual` job will be created for
you when you're ready to manually deploy to production.
@@ -692,7 +753,7 @@ A [canary environment](https://docs.gitlab.com/ee/user/project/canary_deployment
before any changes are deployed to production.
If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to
-`1` as a secret variable) then two manual jobs will be created:
+`1` as a CI/CD variable) then two manual jobs will be created:
- `canary` which will deploy the application to the canary environment
- `production_manual` which is to be used by you when you're ready to manually
@@ -710,9 +771,8 @@ to use an incremental rollout to replace just a few pods with the latest code.
This will allow you to first check how the app is behaving, and later manually
increasing the rollout up to 100%.
-If `INCREMENTAL_ROLLOUT_ENABLED` is defined in your project (e.g., set
-`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a secret variable), then instead of the
-standard `production` job, 4 different
+If `INCREMENTAL_ROLLOUT_MODE` is set to `manual` in your project, then instead
+of the standard `production` job, 4 different
[manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph)
will be created:
@@ -736,21 +796,45 @@ environment page.
Below, you can see how the pipeline will look if the rollout or staging
variables are defined.
-- **Without `INCREMENTAL_ROLLOUT_ENABLED` and without `STAGING_ENABLED`**
+Without `INCREMENTAL_ROLLOUT_MODE` and without `STAGING_ENABLED`:
+
+![Staging and rollout disabled](img/rollout_staging_disabled.png)
+
+Without `INCREMENTAL_ROLLOUT_MODE` and with `STAGING_ENABLED`:
- ![Staging and rollout disabled](img/rollout_staging_disabled.png)
+![Staging enabled](img/staging_enabled.png)
-- **Without `INCREMENTAL_ROLLOUT_ENABLED` and with `STAGING_ENABLED`**
+With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and without `STAGING_ENABLED`:
- ![Staging enabled](img/staging_enabled.png)
+![Rollout enabled](img/rollout_enabled.png)
-- **With `INCREMENTAL_ROLLOUT_ENABLED` and without `STAGING_ENABLED`**
+With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and with `STAGING_ENABLED`
+
+![Rollout and staging enabled](img/rollout_staging_enabled.png)
+
+CAUTION: **Caution:**
+Before GitLab 11.4 this feature was enabled by the presence of the
+`INCREMENTAL_ROLLOUT_ENABLED` environment variable.
+This configuration is deprecated and will be removed in the future.
+
+#### Timed incremental rollout to production **[PREMIUM]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7545) in GitLab 11.4.
+
+TIP: **Tip:**
+You can also set this inside your [project's settings](#deployment-strategy).
- ![Rollout enabled](img/rollout_enabled.png)
+This configuration based on
+[incremental rollout to production](#incremental-rollout-to-production).
-- **With `INCREMENTAL_ROLLOUT_ENABLED` and with `STAGING_ENABLED`**
+Everything behaves the same way, except:
- ![Rollout and staging enabled](img/rollout_staging_enabled.png)
+- It's enabled by setting the `INCREMENTAL_ROLLOUT_MODE` variable to `timed`.
+- Instead of the standard `production` job, the following jobs with a 5 minute delay between each are created:
+ 1. `timed rollout 10%`
+ 1. `timed rollout 25%`
+ 1. `timed rollout 50%`
+ 1. `timed rollout 100%`
## Currently supported languages
@@ -838,7 +922,7 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
[review-app]: ../../ci/review_apps/index.md
[container-registry]: ../../user/project/container_registry.md
[postgresql]: https://www.postgresql.org/
-[Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml
-[GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md
+[Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
[ee]: https://about.gitlab.com/pricing/
+[ce-21955]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21955
[ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index 44b0cf758dc..6326aadcdf2 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -15,7 +15,7 @@ need to ensure your own [Runners are configured](../../ci/runners/README.md) and
Before creating and connecting your Kubernetes cluster to your GitLab project,
you need a Google Cloud Platform account. If you don't already have one,
-sign up at https://console.cloud.google.com. You'll need to either sign in with an existing
+sign up at <https://console.cloud.google.com>. You'll need to either sign in with an existing
Google account (for example, one that you use to access Gmail, Drive, etc.) or create a new one.
1. Follow the steps as outlined in the ["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin)
@@ -83,6 +83,9 @@ under which this application will be deployed.
![GitLab GKE cluster details](img/guide_gitlab_gke_details.png)
1. Once ready, click **Create Kubernetes cluster**.
+
+NOTE: **Note:**
+Do not select `f1-micro` from the **Machine type** dropdown. `f1-micro` machines cannot support a full GitLab installation.
After a couple of minutes, the cluster will be created. You can also see its
status on your [GCP dashboard](https://console.cloud.google.com/kubernetes).
@@ -143,7 +146,7 @@ In the next section we'll break down the pipeline and explain what each job does
By now you should see the pipeline running, but what is it running exactly?
-To navigate inside the pipeline, click its status badge. (It's status should be "running").
+To navigate inside the pipeline, click its status badge. (Its status should be "running").
The pipeline is split into 4 stages, each running a couple of jobs.
![Pipeline stages](img/guide_pipeline_stages.png)
@@ -194,7 +197,7 @@ applications. In the rightmost column for the production environment, you can ma
- The first icon will open the URL of the application that is deployed in
production. It's a very simple page, but the important part is that it works!
-- The next icon with the small graph will take you to the metrics page where
+- The next icon, with the small graph, will take you to the metrics page where
Prometheus collects data about the Kubernetes cluster and how the application
affects it (in terms of memory/CPU usage, latency, etc.).
@@ -205,7 +208,7 @@ applications. In the rightmost column for the production environment, you can ma
application is running.
Right below, there is the
-[Deploy Board](https://docs.gitlab.com/ee/user/project/deploy_boards.md).
+[Deploy Board](https://docs.gitlab.com/ee/user/project/deploy_boards.html).
The squares represent pods in your Kubernetes cluster that are associated with
the given environment. Hovering above each square you can see the state of a
deployment and clicking a square will take you to the pod's logs page.
@@ -217,7 +220,7 @@ under **Settings > CI/CD > Variables**.
### Working with branches
-Following the [GitLab flow](../../workflow/gitlab_flow.md#working-with-feature-branches)
+Following the [GitLab flow](../../workflow/gitlab_flow.md#working-with-feature-branches),
let's create a feature branch that will add some content to the application.
Under your repository, navigate to the following file: `app/views/welcome/index.html.erb`.
@@ -235,7 +238,7 @@ by clicking **Commit**.
![Web IDE commit](img/guide_ide_commit.png)
Once you submit the merge request, you'll see the pipeline running. This will
-run all the jobs as [described previously](#deploying-the-application), as well
+run all the jobs as [described previously](#deploying-the-application), as well as
a few more that run only on branches other than `master`.
![Merge request](img/guide_merge_request.png)
@@ -264,8 +267,8 @@ Let's fix that:
to stage the changes.
1. Write a commit message and click **Commit**.
-Now, if you go back to the merge request you should not only see the test passing,
-but also the application deployed as a [review app](index.md#auto-review-apps). You
+Now, if you go back to the merge request you should not only see the test passing, but
+also the application deployed as a [review app](index.md#auto-review-apps). You
can visit it by following the URL in the merge request. The changes that we
previously made should be there.
@@ -278,7 +281,7 @@ and the application will be eventually deployed straight to production.
After implementing this project, you should now have a solid understanding of the basics of Auto DevOps.
We started from building and testing to deploying and monitoring an application
-all within GitLab. Despite its automatic nature, Audo DevOps can also be configured
+all within GitLab. Despite its automatic nature, Auto DevOps can also be configured
and customized to fit your workflow. Here are some helpful resources for further reading:
1. [Auto DevOps](index.md)
diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/img/branching.png b/doc/topics/git/numerous_undo_possibilities_in_git/img/branching.png
index 9a80c211c99..d8dc9fc8097 100644
--- a/doc/topics/git/numerous_undo_possibilities_in_git/img/branching.png
+++ b/doc/topics/git/numerous_undo_possibilities_in_git/img/branching.png
Binary files differ
diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.png b/doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.png
index ac7ea9ecddc..6506de209f4 100644
--- a/doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.png
+++ b/doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.png
Binary files differ
diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/img/revert.png b/doc/topics/git/numerous_undo_possibilities_in_git/img/revert.png
index 13b3a35ca45..040f8118d72 100644
--- a/doc/topics/git/numerous_undo_possibilities_in_git/img/revert.png
+++ b/doc/topics/git/numerous_undo_possibilities_in_git/img/revert.png
Binary files differ
diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/index.md b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
index 4cb8f083fb5..7195b0f0f04 100644
--- a/doc/topics/git/numerous_undo_possibilities_in_git/index.md
+++ b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
@@ -41,10 +41,9 @@ Here's what we'll cover in this tutorial:
- [Without history modification](#undo-remote-changes-without-changing-history) (preferred way)
- [With history modification](#undo-remote-changes-with-modifying-history) (requires
coordination with team and force pushes).
-
- - [Usecases when modifying history is generally acceptable](#where-modifying-history-is-generally-acceptable)
- - [How to modify history](#how-modifying-history-is-done)
- - [How to remove sensitive information from repository](#deleting-sensitive-information-from-commits)
+ - [Usecases when modifying history is generally acceptable](#where-modifying-history-is-generally-acceptable)
+ - [How to modify history](#how-modifying-history-is-done)
+ - [How to remove sensitive information from repository](#deleting-sensitive-information-from-commits)
### Branching strategy
@@ -101,24 +100,23 @@ no changes added to commit (use "git add" and/or "git commit -a")
At this point there are 3 options to undo the local changes you have:
- - Discard all local changes, but save them for possible re-use [later](#quickly-save-local-changes)
-
- ```shell
- git stash
- ```
+- Discard all local changes, but save them for possible re-use [later](#quickly-save-local-changes)
- - Discarding local changes (permanently) to a file
+ ```shell
+ git stash
+ ```
- ```shell
- git checkout -- <file>
- ```
+- Discarding local changes (permanently) to a file
- - Discard all local changes to all files permanently
+ ```shell
+ git checkout -- <file>
+ ```
- ```shell
- git reset --hard
- ```
+- Discard all local changes to all files permanently
+ ```shell
+ git reset --hard
+ ```
Before executing `git reset --hard`, keep in mind that there is also a way to
just temporary store the changes without committing them using `git stash`.
@@ -140,10 +138,10 @@ them. This is achieved by Git stashing command `git stash`, which in fact saves
current work and runs `git reset --hard`, but it also has various
additional options like:
- - `git stash save`, which enables including temporary commit message, which will help you identify changes, among with other options
- - `git stash list`, which lists all previously stashed commits (yes, there can be more) that were not `pop`ed
- - `git stash pop`, which redoes previously stashed changes and removes them from stashed list
- - `git stash apply`, which redoes previously stashed changes, but keeps them on stashed list
+- `git stash save`, which enables including temporary commit message, which will help you identify changes, among with other options
+- `git stash list`, which lists all previously stashed commits (yes, there can be more) that were not `pop`ed
+- `git stash pop`, which redoes previously stashed changes and removes them from stashed list
+- `git stash apply`, which redoes previously stashed changes, but keeps them on stashed list
### Staged local changes (before you commit)
@@ -174,29 +172,29 @@ Changes to be committed:
Now you have 4 options to undo your changes:
- - Unstage the file to current commit (HEAD)
+- Unstage the file to current commit (HEAD)
- ```shell
- git reset HEAD <file>
- ```
+ ```shell
+ git reset HEAD <file>
+ ```
- - Unstage everything - retain changes
+- Unstage everything - retain changes
- ```shell
- git reset
- ```
+ ```shell
+ git reset
+ ```
- - Discard all local changes, but save them for [later](#quickly-save-local-changes)
+- Discard all local changes, but save them for [later](#quickly-save-local-changes)
- ```shell
- git stash
- ```
+ ```shell
+ git stash
+ ```
- - Discard everything permanently
+- Discard everything permanently
- ```shell
- git reset --hard
- ```
+ ```shell
+ git reset --hard
+ ```
## Committed local changes
@@ -232,42 +230,42 @@ In our example we will end up with commit `B`, that introduced bug/error. We hav
- Undo (swap additions and deletions) changes introduced by commit `B`.
- ```shell
- git revert commit-B-id
- ```
+ ```shell
+ git revert commit-B-id
+ ```
- Undo changes on a single file or directory from commit `B`, but retain them in the staged state
- ```shell
- git checkout commit-B-id <file>
- ```
+ ```shell
+ git checkout commit-B-id <file>
+ ```
- Undo changes on a single file or directory from commit `B`, but retain them in the unstaged state
- ```shell
- git reset commit-B-id <file>
- ```
-
- - There is one command we also must not forget: **creating a new branch**
- from the point where changes are not applicable or where the development has hit a
- dead end. For example you have done commits `A-B-C-D` on your feature-branch
- and then you figure `C` and `D` are wrong. At this point you either reset to `B`
- and do commit `F` (which will cause problems with pushing and if forced pushed also with other developers)
- since branch now looks `A-B-F`, which clashes with what other developers have locally (you will
- [change history](#with-history-modification)), or you simply checkout commit `B` create
- a new branch and do commit `F`. In the last case, everyone else can still do their work while you
- have your new way to get it right and merge it back in later. Alternatively, with GitLab,
- you can [cherry-pick](../../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
- that commit into a new merge request.
-
- ![Create a new branch to avoid clashing](img/branching.png)
-
- ```shell
- git checkout commit-B-id
- git checkout -b new-path-of-feature
- # Create <commit F>
- git commit -a
- ```
+ ```shell
+ git reset commit-B-id <file>
+ ```
+
+- There is one command we also must not forget: **creating a new branch**
+ from the point where changes are not applicable or where the development has hit a
+ dead end. For example you have done commits `A-B-C-D` on your feature-branch
+ and then you figure `C` and `D` are wrong. At this point you either reset to `B`
+ and do commit `F` (which will cause problems with pushing and if forced pushed also with other developers)
+ since branch now looks `A-B-F`, which clashes with what other developers have locally (you will
+ [change history](#with-history-modification)), or you simply checkout commit `B` create
+ a new branch and do commit `F`. In the last case, everyone else can still do their work while you
+ have your new way to get it right and merge it back in later. Alternatively, with GitLab,
+ you can [cherry-pick](../../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
+ that commit into a new merge request.
+
+ ![Create a new branch to avoid clashing](img/branching.png)
+
+ ```shell
+ git checkout commit-B-id
+ git checkout -b new-path-of-feature
+ # Create <commit F>
+ git commit -a
+ ```
### With history modification
@@ -287,9 +285,9 @@ delete commit `B`.
- Rebase the range from current commit D to A:
- ```shell
- git rebase -i A
- ```
+ ```shell
+ git rebase -i A
+ ```
- Command opens your favorite editor where you write `drop` in front of commit
`B`, but you leave default `pick` with all other commits. Save and exit the
diff --git a/doc/topics/git/troubleshooting_git.md b/doc/topics/git/troubleshooting_git.md
index 8555c5e91ea..d1729d70158 100644
--- a/doc/topics/git/troubleshooting_git.md
+++ b/doc/topics/git/troubleshooting_git.md
@@ -78,5 +78,20 @@ git push
In case you're running an older version of Git (< 2.9), consider upgrading
to >= 2.9 (see [Broken pipe when pushing to Git repository][Broken-Pipe]).
+## Timeout during git push/pull
+
+If pulling/pushing from/to your repository ends up taking more than 50 seconds,
+a timeout will be issued with a log of the number of operations performed
+and their respective timings, like the example below:
+
+```
+remote: Running checks for branch: master
+remote: Scanning for LFS objects... (153ms)
+remote: Calculating new repository size... (cancelled after 729ms)
+```
+
+This could be used to further investigate what operation is performing poorly
+and provide GitLab with more information on how to improve the service.
+
[SSH troubleshooting]: ../../ssh/README.md#troubleshooting "SSH Troubleshooting"
[Broken-Pipe]: https://stackoverflow.com/questions/19120120/broken-pipe-when-pushing-to-git-repository/36971469#36971469 "StackOverflow: 'Broken pipe when pushing to Git repository'"
diff --git a/doc/university/README.md b/doc/university/README.md
index 595fc480887..3e7d02770e4 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -11,7 +11,7 @@ and [Blog Articles](https://about.gitlab.com/blog/).
Would you like to contribute to GitLab University? Then please take a look at our contribution [process](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/PROCESS.md) for more information.
-## Gitlab University Curriculum
+## GitLab University Curriculum
The curriculum is composed of GitLab videos, screencasts, presentations, projects and external GitLab content hosted on other services and has been organized into the following sections.
@@ -73,10 +73,10 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
#### 1.7 Community and Support
1. [Getting Help](https://about.gitlab.com/getting-help/)
- - Proposing Features and Reporting and Tracking bugs for GitLab
- - The GitLab IRC channel, Gitter Chat Room, Community Forum and Mailing List
- - Getting Technical Support
- - Being part of our Great Community and Contributing to GitLab
+ - Proposing Features and Reporting and Tracking bugs for GitLab
+ - The GitLab IRC channel, Gitter Chat Room, Community Forum and Mailing List
+ - Getting Technical Support
+ - Being part of our Great Community and Contributing to GitLab
1. [Getting Started with the GitLab Development Kit (GDK)](https://about.gitlab.com/2016/06/08/getting-started-with-gitlab-development-kit/)
1. [Contributing Technical Articles to the GitLab Blog](https://about.gitlab.com/2016/01/26/call-for-writers/)
1. [GitLab Training Workshops](https://docs.gitlab.com/ce/university/training/end-user/)
@@ -104,7 +104,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Due Dates and Milestones for GitLab Issues](https://about.gitlab.com/2016/08/05/feature-highlight-set-dates-for-issues/)
1. [How to Use GitLab Labels](https://about.gitlab.com/2016/08/17/using-gitlab-labels/)
1. [Applying GitLab Labels Automatically](https://about.gitlab.com/2016/08/19/applying-gitlab-labels-automatically/)
-1. [GitLab Issue Board - Product Page](https://about.gitlab.com/solutions/issueboard/)
+1. [GitLab Issue Board - Product Page](https://about.gitlab.com/product/issueboard/)
1. [An Overview of GitLab Issue Board](https://about.gitlab.com/2016/08/22/announcing-the-gitlab-issue-board/)
1. [Designing GitLab Issue Board](https://about.gitlab.com/2016/08/31/designing-issue-boards/)
1. [From Idea to Production with GitLab - Video](https://www.youtube.com/watch?v=25pHyknRgEo&index=14&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
@@ -125,7 +125,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
1. [IBM: Continuous Delivery vs Continuous Deployment - Video](https://www.youtube.com/watch?v=igwFj8PPSnw)
1. [Amazon: Transition to Continuous Delivery - Video](https://www.youtube.com/watch?v=esEFaY0FDKc)
-2. [TechBeacon: Doing continuous delivery? Focus first on reducing release cycle times](https://techbeacon.com/doing-continuous-delivery-focus-first-reducing-release-cycle-times)
+1. [TechBeacon: Doing continuous delivery? Focus first on reducing release cycle times](https://techbeacon.com/doing-continuous-delivery-focus-first-reducing-release-cycle-times)
1. See **[Integrations](#39-integrations)** for integrations with other CI services.
#### 2.4. Workflow
@@ -140,7 +140,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [GitLab Compared to Other Tools](https://about.gitlab.com/comparison/)
1. [Comparing GitLab Terminology](https://about.gitlab.com/2016/01/27/comparing-terms-gitlab-github-bitbucket/)
-1. [GitLab Compared to Atlassian (Recording 2016-03-03) ](https://youtu.be/Nbzp1t45ERo)
+1. [GitLab Compared to Atlassian (Recording 2016-03-03)](https://youtu.be/Nbzp1t45ERo)
1. [GitLab Position FAQ](https://about.gitlab.com/handbook/positioning-faq)
1. [Customer review of GitLab with points on why they prefer GitLab](https://www.enovate.co.uk/web-design-blog/2015/11/25/gitlab-review/)
@@ -189,7 +189,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
#### 3.8 Cycle Analytics
1. [GitLab Cycle Analytics Overview](https://about.gitlab.com/2016/09/21/cycle-analytics-feature-highlight/)
-1. [GitLab Cycle Analytics - Product Page](https://about.gitlab.com/solutions/cycle-analytics/)
+1. [GitLab Cycle Analytics - Product Page](https://about.gitlab.com/product/cycle-analytics/)
#### 3.9. Integrations
@@ -205,7 +205,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
### 4. External Articles
-1. [2011 WSJ article by Marc Andreessen - Software is Eating the World](http://www.wsj.com/articles/SB10001424053111903480904576512250915629460)
+1. [2011 WSJ article by Marc Andreessen - Software is Eating the World](https://www.wsj.com/articles/SB10001424053111903480904576512250915629460)
1. [2014 Blog post by Chris Dixon - Software eats software development](http://cdixon.org/2014/04/13/software-eats-software-development/)
1. [2015 Venture Beat article - Actually, Open Source is Eating the World](http://venturebeat.com/2015/12/06/its-actually-open-source-software-thats-eating-the-world/)
@@ -213,7 +213,8 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
### 5. Resources for GitLab Team Members
-*Some content can only be accessed by GitLab team members*
+NOTE: **Note:**
+Some content can only be accessed by GitLab team members
1. [Support Path](support/README.md)
1. [Sales Path (redirect to sales handbook)](https://about.gitlab.com/handbook/sales-onboarding/)
diff --git a/doc/university/bookclub/booklist.md b/doc/university/bookclub/booklist.md
index 26c3851276b..84b1f643b91 100644
--- a/doc/university/bookclub/booklist.md
+++ b/doc/university/bookclub/booklist.md
@@ -10,7 +10,7 @@ List of books and resources, that may be worth reading.
1. **The Humble Programmer**
- Edsger W. Dijkstra, 1972 ([paper](http://dl.acm.org/citation.cfm?id=361591))
+ Edsger W. Dijkstra, 1972 ([paper](https://dl.acm.org/citation.cfm?id=361591))
## Programming
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index 89516dba60b..6e0f71017c6 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -303,7 +303,7 @@ A [tool](https://docs.gitlab.com/ee/integration/external-issue-tracker.html) use
### Jenkins
-An Open Source CI tool written using the Java programming language. [Jenkins](https://jenkins-ci.org/) does the same job as GitLab CI, Bamboo, and Travis CI. It is extremely popular. Related [documentation](https://docs.gitlab.com/ee/integration/jenkins.html).
+An Open Source CI tool written using the Java programming language. [Jenkins](https://jenkins.io/) does the same job as GitLab CI, Bamboo, and Travis CI. It is extremely popular. Related [documentation](https://docs.gitlab.com/ee/integration/jenkins.html).
### Jira
@@ -395,7 +395,7 @@ Allow you to [organize issues](../../user/project/milestones/index.md) and merge
### Mirror Repositories
-A project that is setup to automatically have its branches, tags, and commits [updated from an upstream repository](https://docs.gitlab.com/ee/workflow/repository_mirroring.html). This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and activity using the familiar GitLab interface.
+A project that is set up to automatically have its branches, tags, and commits [updated from an upstream repository](https://docs.gitlab.com/ee/workflow/repository_mirroring.html). This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and activity using the familiar GitLab interface.
### MIT License
@@ -673,7 +673,7 @@ Version control is a system that records changes to a file or set of files over
### Virtual Private Cloud (VPC)
-A [VPC](https://docs.gitlab.com/ce/university/glossary/README.html#virtual-private-cloud-vpc) is an on demand configurable pool of shared computing resources allocated within a public cloud environment, providing some isolation between the different users using the resources. GitLab users need to create a new Amazon VPC in order to [setup High Availability](https://docs.gitlab.com/ce/university/high-availability/aws/).
+A [VPC](https://docs.gitlab.com/ce/university/glossary/README.html#virtual-private-cloud-vpc) is an on demand configurable pool of shared computing resources allocated within a public cloud environment, providing some isolation between the different users using the resources. GitLab users need to create a new Amazon VPC in order to [set up High Availability](https://docs.gitlab.com/ce/university/high-availability/aws/).
### Virtual private server (VPS)
diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index 8f7bb8636c5..b21cf27c1d3 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -9,7 +9,7 @@ in [significantly degraded performance](https://gitlab.com/gitlab-org/gitlab-ee/
GitLab on AWS can leverage many of the services that are already
configurable with High Availability. These services have a lot of
-flexibility and are able to adopt to most companies, best of all is the
+flexibility and are able to adapt to most companies, best of all is the
ability to automate both vertical and horizontal scaling.
In this article we'll go through a basic HA setup where we'll start by
@@ -30,7 +30,7 @@ we'll be using to configure our cloud infrastructure.
### Reference Architecture
-![Reference Architecture](img/reference-arch.png)
+![Reference Architecture](img/reference-arch2.png)
***
@@ -55,9 +55,9 @@ and from the Actions dropdown choose Edit DNS Hostnames and select Yes.
### Subnet
Now let's create some subnets in different Availability Zones. Make sure
-that each subnet is associated the the VPC we just created, that it has
+that each subnet is associated to the VPC we just created, that it has
a distinct VPC and lastly that CIDR blocks don't overlap. This will also
-allow us to enable multi AZ for redundancy.
+allow us to enable multi-AZ for redundancy.
We will create private and public subnets to match load balancers and
RDS instances as well.
@@ -98,7 +98,7 @@ traffic from any destination.
![Subnet Config](img/ig-rt.png)
-Before leaving this screen select the next tab to the rgiht which is
+Before leaving this screen select the next tab to the right which is
Subnet Associations and add our public subnets. If you followed our
naming convention they should be easy to find.
@@ -106,8 +106,8 @@ naming convention they should be easy to find.
## Database with RDS
-For our database server we will use Amazon RDS which offers Multi AZ
-for redundancy. Lets start by creating a subnet group and then we'll
+For our database server we will use Amazon RDS which offers Multi-AZ
+for redundancy. Let's start by creating a subnet group and then we'll
create the actual RDS instance.
### Subnet Group
@@ -122,7 +122,7 @@ the VPC ID dropdown and at the bottom we can add our private subnets.
Select the RDS service from the Database section and create a new
PostgreSQL instance. After choosing between a Production or
Development instance we'll start with the actual configuration. On the
-image bellow we have the settings for this article but note the
+image below we have the settings for this article but note the
following two options which are of particular interest for HA:
1. Multi-AZ-Deployment is recommended as redundancy. Read more at
@@ -133,7 +133,7 @@ IOPS (SSD) is best suited for HA. Read more about it at
![RDS Instance Specs](img/instance_specs.png)
-The rest of the setting on this page request a DB identifier, username
+The rest of the setting on this page request a DB identifier, username,
and a master password. We've chosen to use `gitlab-ha`, `gitlab` and a
very secure password respectively. Keep these in hand for later.
@@ -152,7 +152,7 @@ EC is an in-memory hosted caching solution. Redis maintains its own
persistence and is used for certain types of application.
Let's choose the ElastiCache service in the Database section from our
-AWS console. Now lets create a cache subnet group which will be very
+AWS console. Now let's create a cache subnet group which will be very
similar to the RDS subnet group. Make sure to select our VPC and its
private subnets.
@@ -160,7 +160,7 @@ private subnets.
Now press the Launch a Cache Cluster and choose Redis for our
DB engine. You'll be able to configure details such as replication,
-Multi AZ and node types. The second section will allow us to choose our
+Multi-AZ and node types. The second section will allow us to choose our
subnet and security group and
![Redis Cluster details](img/redis-cluster-det.png)
@@ -274,7 +274,7 @@ username, and password.
gitlab_rails['db_password'] = "mypassword"
gitlab_rails['db_host'] = "<rds-endpoint>"
-Next we only need to configure the Redis section by adding the host and
+Next, we only need to configure the Redis section by adding the host and
uncommenting the port.
@@ -285,8 +285,8 @@ to make the EFS integration easier to manage.
gitlab_rails['redis_host'] = "<redis-endpoint>"
gitlab_rails['redis_port'] = 6379
-Finally run reconfigure, you might find it useful to run a check and
-a service status to make sure everything has been setup correctly.
+Finally, run reconfigure. You might find it useful to run a check and
+a service status to make sure everything has been set up correctly.
sudo gitlab-ctl reconfigure
sudo gitlab-rake gitlab:check
@@ -321,10 +321,10 @@ The Load Balancer Health will allow us to indicate where to ping and what
makes up a healthy or unhealthy instance.
We won't add the instance on the next session because we'll destroy it
-momentarily as we'll be using the image we where creating. We will keep
+momentarily as we'll be using the image we were creating. We will keep
the Enable Cross-Zone and Enable Connection Draining active.
-After we finish creating the Load Balancer we can re visit our Security
+After we finish creating the Load Balancer we can revisit our Security
Groups to improve access only through the ELB and any other requirement
you might have.
@@ -363,7 +363,7 @@ After this is launched we are able to start creating our Auto Scaling
Group. Start by giving it a name and assigning it our VPC and private
subnets. We also want to always start with two instances and if you
scroll down to Advanced Details we can choose to receive traffic from ELBs.
-Lets enable that option and select our ELB. We also want to use the ELB's
+Let's enable that option and select our ELB. We also want to use the ELB's
health check.
![Auto scaling](img/auto-scaling-det.png)
@@ -388,12 +388,12 @@ we where aiming for.
After you're done with the policies section have some fun trying to break
instances. You should be able to see how the Auto Scaling Group and the
-EC2 screen start bringing them up again.
+EC2 screen starts bringing them up again.
-High Availability is a very big area, we went mostly through scaling and
+High Availability is a vast area, we went mostly through scaling and
some redundancy options but it might also imply Geographic replication.
There is a lot of ground yet to cover so have a read through these other
resources and feel free to open an issue to request additional material.
- * [GitLab High Availability](http://docs.gitlab.com/ce/administration/high_availability/README.html#sts=High Availability)
- * [GitLab Geo](http://docs.gitlab.com/ee/gitlab-geo/README.html)
+* [GitLab High Availability](http://docs.gitlab.com/ce/administration/high_availability/README.html#sts=High%20Availability)
+* [GitLab Geo](http://docs.gitlab.com/ee/gitlab-geo/README.html)
diff --git a/doc/university/high-availability/aws/img/auto-scaling-det.png b/doc/university/high-availability/aws/img/auto-scaling-det.png
index 1e125f301bc..cf32c024bf8 100644
--- a/doc/university/high-availability/aws/img/auto-scaling-det.png
+++ b/doc/university/high-availability/aws/img/auto-scaling-det.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/db-subnet-group.png b/doc/university/high-availability/aws/img/db-subnet-group.png
index 590a02b8dbe..875184af310 100644
--- a/doc/university/high-availability/aws/img/db-subnet-group.png
+++ b/doc/university/high-availability/aws/img/db-subnet-group.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/ig.png b/doc/university/high-availability/aws/img/ig.png
index d4fc2d12de8..2798d4beac3 100644
--- a/doc/university/high-availability/aws/img/ig.png
+++ b/doc/university/high-availability/aws/img/ig.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/instance_specs.png b/doc/university/high-availability/aws/img/instance_specs.png
index 650f375ab3c..2a2b80103fb 100644
--- a/doc/university/high-availability/aws/img/instance_specs.png
+++ b/doc/university/high-availability/aws/img/instance_specs.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/new_vpc.png b/doc/university/high-availability/aws/img/new_vpc.png
index e51c066cee2..d872554fab7 100644
--- a/doc/university/high-availability/aws/img/new_vpc.png
+++ b/doc/university/high-availability/aws/img/new_vpc.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/policies.png b/doc/university/high-availability/aws/img/policies.png
index afcd9e4af9b..e99497a52a2 100644
--- a/doc/university/high-availability/aws/img/policies.png
+++ b/doc/university/high-availability/aws/img/policies.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/rds-net-opt.png b/doc/university/high-availability/aws/img/rds-net-opt.png
index 651cc23b1ab..13130ac96b8 100644
--- a/doc/university/high-availability/aws/img/rds-net-opt.png
+++ b/doc/university/high-availability/aws/img/rds-net-opt.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/rds-sec-group.png b/doc/university/high-availability/aws/img/rds-sec-group.png
index c6d1bc350e4..a88caba62c2 100644
--- a/doc/university/high-availability/aws/img/rds-sec-group.png
+++ b/doc/university/high-availability/aws/img/rds-sec-group.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/reference-arch.png b/doc/university/high-availability/aws/img/reference-arch.png
deleted file mode 100644
index 271ee5bc614..00000000000
--- a/doc/university/high-availability/aws/img/reference-arch.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/reference-arch2.png b/doc/university/high-availability/aws/img/reference-arch2.png
new file mode 100644
index 00000000000..9f50b2f5171
--- /dev/null
+++ b/doc/university/high-availability/aws/img/reference-arch2.png
Binary files differ
diff --git a/doc/university/high-availability/aws/img/subnet.png b/doc/university/high-availability/aws/img/subnet.png
index de910edc948..681c29bf07a 100644
--- a/doc/university/high-availability/aws/img/subnet.png
+++ b/doc/university/high-availability/aws/img/subnet.png
Binary files differ
diff --git a/doc/university/support/README.md b/doc/university/support/README.md
index d1d5db6bbcd..805af253367 100644
--- a/doc/university/support/README.md
+++ b/doc/university/support/README.md
@@ -37,7 +37,7 @@ Continue to look over remaining portions of the [University Overview](../README.
Get your development machine ready to familiarize yourself with the codebase, the components, and to be prepared to reproduce issues that our users encounter
- Install the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit)
- - [Setup OpenLDAP as part of this](https://gitlab.com/gitlab-org/gitlab-development-kit#openldap)
+ - [Set up OpenLDAP as part of this](https://gitlab.com/gitlab-org/gitlab-development-kit#openldap)
#### Become comfortable with the Installation processes that we support
@@ -55,13 +55,13 @@ Sometimes we need to upgrade customers from old versions of GitLab to latest, so
- Keep this up-to-date as patch and version releases become available, just like our customers would
- Try out the following installation path
- [Install GitLab 4.2 from source](https://gitlab.com/gitlab-org/gitlab-ce/blob/d67117b5a185cfb15a1d7e749588ff981ffbf779/doc/install/installation.md)
- - External MySQL database
- - External NGINX
+ - External MySQL database
+ - External NGINX
- Create some test data
- - Populated Repos
- - Users
- - Groups
- - Projects
+ - Populated Repos
+ - Users
+ - Groups
+ - Projects
- [Backup using our Backup rake task](https://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
- [Upgrade to 5.0 source using our Upgrade documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/4.2-to-5.0.md)
- [Upgrade to 5.1 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.0-to-5.1.md)
@@ -72,7 +72,7 @@ Sometimes we need to upgrade customers from old versions of GitLab to latest, so
- [Upgrade to Omnibus 7.14](https://docs.gitlab.com/omnibus/update/README.html#upgrading-from-a-non-omnibus-installation-to-an-omnibus-installation)
- [Restore backup using our Restore rake task](https://docs.gitlab.com/ce/raketasks/backup_restore.html#restore-a-previously-created-backup)
- [Upgrade to latest EE](https://about.gitlab.com/downloads-ee)
- - (GitLab inc. only) Acquire and apply a license for the Enterprise Edition product, ask in #support
+ - (GitLab inc. only) Acquire and apply a license for the Enterprise Edition product, ask in #support
- Perform a downgrade from [EE to CE](https://docs.gitlab.com/ee/downgrade_ee_to_ce/README.html)
#### Start to learn about some of the integrations that we support
@@ -98,9 +98,9 @@ Our integrations add great value to GitLab. User questions often relate to integ
- [Environment Information and maintenance checks](https://docs.gitlab.com/ce/raketasks/maintenance.html)
- [GitLab check](https://docs.gitlab.com/ce/raketasks/check.html)
- Omnibus commands
- - [Status](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#get-service-status)
- - [Starting and stopping services](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#starting-and-stopping)
- - [Starting a rails console](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#invoking-rake-tasks)
+ - [Status](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#get-service-status)
+ - [Starting and stopping services](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#starting-and-stopping)
+ - [Starting a rails console](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#invoking-rake-tasks)
#### Learn about the Support process
@@ -118,16 +118,16 @@ Zendesk is our Support Centre and our main communication line with our Customers
- Here you will find a large variety of queries mainly from our Users who are self hosting GitLab CE
- Understand the questions that are asked and dig in to try to find a solution
- [Proceed on to the GitLab.com Support Forum](https://about.gitlab.com/handbook/support/#gitlabcom-support-trackera-namesupp-foruma)
- - Here you will find queries regarding our own GitLab.com
- - Helping Users here will give you an understanding of our Admin interface and other tools
+ - Here you will find queries regarding our own GitLab.com
+ - Helping Users here will give you an understanding of our Admin interface and other tools
- [Proceed on to the Twitter tickets in Zendesk](https://about.gitlab.com/handbook/support/#twitter)
- - Here you will gain a great insight into our userbase
- - Learn from any complaints and problems and feed them back to the team
- - Tweets can range from help needed with GitLab installations, the API and just general queries
+ - Here you will gain a great insight into our userbase
+ - Learn from any complaints and problems and feed them back to the team
+ - Tweets can range from help needed with GitLab installations, the API and just general queries
- [Proceed on to Regular email Support tickets](https://about.gitlab.com/handbook/support/#regular-zendesk-tickets-a-nameregulara)
- - Here you will find tickets from our GitLab EE Customers and GitLab CE Users
- - Tickets here are extremely varied and often very technical
- - You should be prepared for these tickets, given the knowledge gained from previous tiers and your training
+ - Here you will find tickets from our GitLab EE Customers and GitLab CE Users
+ - Tickets here are extremely varied and often very technical
+ - You should be prepared for these tickets, given the knowledge gained from previous tiers and your training
- Check out your colleagues' responses
- Hop on to the #support-live-feed channel in Slack and see the tickets as they come in and are updated
- Read through old tickets that your colleagues have worked on
@@ -135,10 +135,10 @@ Zendesk is our Support Centre and our main communication line with our Customers
- [Learn about Cisco WebEx](https://about.gitlab.com/handbook/support/onboarding/#webexa-namewebexa)
- Training calls
- Information gathering calls
- - It's good to find out how new and prospective customers are going to be using the product and how they will set up their infrastructure
+ - It's good to find out how new and prospective customers are going to be using the product and how they will set up their infrastructure
- Diagnosis calls
- - When email isn't enough we may need to hop on a call and do some debugging along side the customer
- - These paired calls are a great learning experience
+ - When email isn't enough we may need to hop on a call and do some debugging along side the customer
+ - These paired calls are a great learning experience
- Upgrade calls
- Emergency calls
diff --git a/doc/university/training/end-user/README.md b/doc/university/training/end-user/README.md
index 9b8a8db58e2..701533358c8 100644
--- a/doc/university/training/end-user/README.md
+++ b/doc/university/training/end-user/README.md
@@ -78,9 +78,9 @@ Workshop Time!
```bash
git config --global user.name "Your Name"
git config --global user.email you@example.com
-```
+```
-- If you don't use the global flag you can setup a different author for
+- If you don't use the global flag you can set up a different author for
each project
- Check settings with:
@@ -107,14 +107,14 @@ cd ~/development
-or-
mkdir ~/workspace
-cd ~/workspace
+cd ~/workspace
```
---
## Git Basics
----
+---
### Git Workflow
@@ -136,7 +136,7 @@ cd ~/workspace
issue tracking, Merge Requests, and other features.
- The hosted version of GitLab is gitlab.com
----
+---
### New Project
@@ -150,12 +150,12 @@ cd ~/workspace
### Git and GitLab basics
1. Edit `edit_this_file.rb` in `training-examples`
-2. See it listed as a changed file (working area)
-3. View the differences
-4. Stage the file
-5. Commit
-6. Push the commit to the remote
-7. View the git log
+1. See it listed as a changed file (working area)
+1. View the differences
+1. Stage the file
+1. Commit
+1. Push the commit to the remote
+1. View the git log
---
@@ -169,14 +169,14 @@ git push origin master
git log
```
----
+---
### Feature Branching
1. Create a new feature branch called `squash_some_bugs`
-2. Edit `bugs.rb` and remove all the bugs.
-3. Commit
-4. Push
+1. Edit `bugs.rb` and remove all the bugs.
+1. Commit
+1. Push
---
@@ -250,16 +250,17 @@ git push origin squash_some_bugs
---
### Example Plan
+
1. Checkout a new branch and edit conflicts.rb. Add 'Line4' and 'Line5'.
-2. Commit and push
-3. Checkout master and edit conflicts.rb. Add 'Line6' and 'Line7' below 'Line3'.
-4. Commit and push to master
-5. Create a merge request and watch it fail
-6. Rebase our new branch with master
-7. Fix conflicts on the conflicts.rb file.
-8. Stage the file and continue rebasing
-9. Force push the changes
-10. Finally continue with the Merge Request
+1. Commit and push
+1. Checkout master and edit conflicts.rb. Add 'Line6' and 'Line7' below 'Line3'.
+1. Commit and push to master
+1. Create a merge request and watch it fail
+1. Rebase our new branch with master
+1. Fix conflicts on the conflicts.rb file.
+1. Stage the file and continue rebasing
+1. Force push the changes
+1. Finally continue with the Merge Request
---
@@ -362,15 +363,15 @@ Don't reset after pushing
### Reset Workflow
1. Edit file again 'edit_this_file.rb'
-2. Check status
-3. Add and commit with wrong message
-4. Check log
-5. Amend commit
-6. Check log
-7. Soft reset
-8. Check log
-9. Pull for updates
-10. Push changes
+1. Check status
+1. Add and commit with wrong message
+1. Check log
+1. Amend commit
+1. Check log
+1. Soft reset
+1. Check log
+1. Pull for updates
+1. Push changes
----
@@ -389,9 +390,9 @@ Don't reset after pushing
### Note
-git revert vs git reset
-Reset removes the commit while revert removes the changes but leaves the commit
-Revert is safer considering we can revert a revert
+git revert vs git reset
+Reset removes the commit while revert removes the changes but leaves the commit
+Revert is safer considering we can revert a revert
# Changed file
diff --git a/doc/university/training/gitlab_flow/production_branch.png b/doc/university/training/gitlab_flow/production_branch.png
index 66456cc51af..956761d7eb8 100644
--- a/doc/university/training/gitlab_flow/production_branch.png
+++ b/doc/university/training/gitlab_flow/production_branch.png
Binary files differ
diff --git a/doc/university/training/gitlab_flow/release_branches.png b/doc/university/training/gitlab_flow/release_branches.png
index 5661e36c4e2..dcb5f97dff0 100644
--- a/doc/university/training/gitlab_flow/release_branches.png
+++ b/doc/university/training/gitlab_flow/release_branches.png
Binary files differ
diff --git a/doc/university/training/topics/additional_resources.md b/doc/university/training/topics/additional_resources.md
index d01634df744..4871372d105 100644
--- a/doc/university/training/topics/additional_resources.md
+++ b/doc/university/training/topics/additional_resources.md
@@ -4,9 +4,9 @@ comments: false
# Additional Resources
-1. GitLab Documentation [http://docs.gitlab.com](http://docs.gitlab.com/)
-2. GUI Clients [http://git-scm.com/downloads/guis](http://git-scm.com/downloads/guis)
-3. Pro git book [http://git-scm.com/book](http://git-scm.com/book)
-4. Platzi Course [https://courses.platzi.com/courses/git-gitlab/](https://courses.platzi.com/courses/git-gitlab/)
-5. Code School tutorial [http://try.github.io/](http://try.github.io/)
-6. Contact Us at `subscribers@gitlab.com`
+1. GitLab Documentation: <http://docs.gitlab.com>.
+1. GUI Clients: <http://git-scm.com/downloads/guis>.
+1. Pro Git book: <http://git-scm.com/book>.
+1. Platzi Course: <https://courses.platzi.com/courses/git-gitlab/>.
+1. Code School tutorial: <http://try.github.io/>.
+1. Contact us at `subscribers@gitlab.com`.
diff --git a/doc/university/training/topics/bisect.md b/doc/university/training/topics/bisect.md
index 2d5ab107fe6..4848d0412c1 100644
--- a/doc/university/training/topics/bisect.md
+++ b/doc/university/training/topics/bisect.md
@@ -2,7 +2,7 @@
comments: false
---
-# Bisect
+# Bisect
----------
@@ -10,18 +10,18 @@ comments: false
- Find a commit that introduced a bug
- Works through a process of elimination
-- Specify a known good and bad revision to begin
+- Specify a known good and bad revision to begin
----------
## Bisect
1. Start the bisect process
-2. Enter the bad revision (usually latest commit)
-3. Enter a known good revision (commit/branch)
-4. Run code to see if bug still exists
-5. Tell bisect the result
-6. Repeat the previous 2 items until you find the offending commit
+1. Enter the bad revision (usually latest commit)
+1. Enter a known good revision (commit/branch)
+1. Run code to see if bug still exists
+1. Tell bisect the result
+1. Repeat the previous 2 items until you find the offending commit
----------
diff --git a/doc/university/training/topics/env_setup.md b/doc/university/training/topics/env_setup.md
index b7bec83ed8a..bdf805711e0 100644
--- a/doc/university/training/topics/env_setup.md
+++ b/doc/university/training/topics/env_setup.md
@@ -16,10 +16,10 @@ comments: false
- **Linux**
```bash
- sudo yum install git-all
+ sudo yum install git-all
```
```bash
- sudo apt-get install git-all
+ sudo apt-get install git-all
```
----------
diff --git a/doc/university/training/topics/getting_started.md b/doc/university/training/topics/getting_started.md
index 153b45fb4da..66cb08feacb 100644
--- a/doc/university/training/topics/getting_started.md
+++ b/doc/university/training/topics/getting_started.md
@@ -9,13 +9,15 @@ comments: false
## Instantiating Repositories
* Create a new repository by instantiating it through
-```bash
-git init
-```
+
+ ```bash
+ git init
+ ```
* Copy an existing project by cloning the repository through
-```bash
-git clone <url>
-```
+
+ ```bash
+ git clone <url>
+ ```
----------
@@ -24,19 +26,19 @@ git clone <url>
* To instantiate a central repository a `--bare` flag is required.
* Bare repositories don't allow file editing or committing changes.
* Create a bare repo with
-```bash
-git init --bare project-name.git
-```
+
+ ```bash
+ git init --bare project-name.git
+ ```
----------
## Instantiate workflow with clone
-1. Create a project in your user namespace
- - Choose to import from 'Any Repo by URL' and use
- https://gitlab.com/gitlab-org/training-examples.git
-2. Create a '`Workspace`' directory in your home directory.
-3. Clone the '`training-examples`' project
+1. Create a project in your user namespace.
+ - Choose to import from 'Any Repo by URL' and use <https://gitlab.com/gitlab-org/training-examples.git>.
+1. Create a '`Workspace`' directory in your home directory.
+1. Clone the '`training-examples`' project.
----------
diff --git a/doc/university/training/topics/git_add.md b/doc/university/training/topics/git_add.md
index 651366e0d49..b1483e725fe 100644
--- a/doc/university/training/topics/git_add.md
+++ b/doc/university/training/topics/git_add.md
@@ -11,27 +11,35 @@ comments: false
Adds content to the index or staging area.
* Adds a list of file
-```bash
-git add <files>
-```
+
+ ```bash
+ git add <files>
+ ```
+
* Adds all files including deleted ones
-```bash
-git add -A
-```
+
+ ```bash
+ git add -A
+ ```
----------
## Git add continued
* Add all text files in current dir
-```bash
-git add *.txt
-```
+
+ ```bash
+ git add *.txt
+ ```
+
* Add all text file in the project
-```bash
-git add "*.txt*"
-```
+
+ ```bash
+ git add "*.txt*"
+ ```
+
* Adds all files in directory
-```bash
-git add views/layouts/
-```
+
+ ```bash
+ git add views/layouts/
+ ```
diff --git a/doc/university/training/topics/git_log.md b/doc/university/training/topics/git_log.md
index f2709ae3890..6ba6f9eb69d 100644
--- a/doc/university/training/topics/git_log.md
+++ b/doc/university/training/topics/git_log.md
@@ -9,31 +9,36 @@ comments: false
Git log lists commit history. It allows searching and filtering.
* Initiate log
-```
-git log
-```
+
+ ```
+ git log
+ ```
* Retrieve set number of records:
-```
-git log -n 2
-```
+
+ ```
+ git log -n 2
+ ```
* Search commits by author. Allows user name or a regular expression.
-```
-git log --author="user_name"
-```
+
+ ```
+ git log --author="user_name"
+ ```
----------
* Search by comment message.
-```
-git log --grep="<pattern>"
-```
+
+ ```
+ git log --grep="<pattern>"
+ ```
* Search by date
-```
-git log --since=1.month.ago --until=3.weeks.ago
-```
+
+ ```
+ git log --since=1.month.ago --until=3.weeks.ago
+ ```
----------
@@ -41,11 +46,11 @@ git log --since=1.month.ago --until=3.weeks.ago
## Git Log Workflow
1. Change to workspace directory
-2. Clone the multi runner projects
-3. Change to project dir
-4. Search by author
-5. Search by date
-6. Combine
+1. Clone the multi runner projects
+1. Change to project dir
+1. Search by author
+1. Search by date
+1. Combine
----------
diff --git a/doc/university/training/topics/merge_conflicts.md b/doc/university/training/topics/merge_conflicts.md
index 9a1ce550868..071baddf508 100644
--- a/doc/university/training/topics/merge_conflicts.md
+++ b/doc/university/training/topics/merge_conflicts.md
@@ -16,15 +16,15 @@ comments: false
## Merge conflicts
1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
-2. Commit and push
-3. Checkout master and edit `conflicts.rb`. Add 'Line6' and 'Line7' below 'Line3'.
-4. Commit and push to master
-5. Create a merge request and watch it fail
-6. Rebase our new branch with master
-7. Fix conflicts on the `conflicts.rb` file.
-8. Stage the file and continue rebasing
-9. Force push the changes
-10. Finally continue with the Merge Request
+1. Commit and push.
+1. Checkout master and edit `conflicts.rb`. Add 'Line6' and 'Line7' below 'Line3'.
+1. Commit and push to master.
+1. Create a merge request and watch it fail.
+1. Rebase our new branch with master.
+1. Fix conflicts on the `conflicts.rb` file.
+1. Stage the file and continue rebasing.
+1. Force push the changes.
+1. Finally continue with the Merge Request.
----------
diff --git a/doc/university/training/topics/rollback_commits.md b/doc/university/training/topics/rollback_commits.md
index 0db1d93d1dc..44304634f36 100644
--- a/doc/university/training/topics/rollback_commits.md
+++ b/doc/university/training/topics/rollback_commits.md
@@ -9,26 +9,30 @@ comments: false
## Undo Commits
* Undo last commit putting everything back into the staging area.
-```
-git reset --soft HEAD^
-```
+
+ ```
+ git reset --soft HEAD^
+ ```
* Add files and change message with:
-```
-git commit --amend -m "New Message"
-```
+
+ ```
+ git commit --amend -m "New Message"
+ ```
----------
* Undo last and remove changes
-```
-git reset --hard HEAD^
-```
+
+ ```
+ git reset --hard HEAD^
+ ```
* Same as last one but for two commits back
-```
-git reset --hard HEAD^^
-```
+
+ ```
+ git reset --hard HEAD^^
+ ```
** Don't reset after pushing **
@@ -37,15 +41,15 @@ git reset --hard HEAD^^
## Reset Workflow
1. Edit file again 'edit_this_file.rb'
-2. Check status
-3. Add and commit with wrong message
-4. Check log
-5. Amend commit
-6. Check log
-7. Soft reset
-8. Check log
-9. Pull for updates
-10. Push changes
+1. Check status
+1. Add and commit with wrong message
+1. Check log
+1. Amend commit
+1. Check log
+1. Soft reset
+1. Check log
+1. Pull for updates
+1. Push changes
----------
diff --git a/doc/university/training/topics/stash.md b/doc/university/training/topics/stash.md
index 5b27ac12f77..42eedea14e5 100644
--- a/doc/university/training/topics/stash.md
+++ b/doc/university/training/topics/stash.md
@@ -10,50 +10,52 @@ We use git stash to store our changes when they are not ready to be committed
and we need to change to a different branch.
* Stash
-```
-git stash save
-# or
-git stash
-# or with a message
-git stash save "this is a message to display on the list"
-```
+
+ ```
+ git stash save
+ # or
+ git stash
+ # or with a message
+ git stash save "this is a message to display on the list"
+ ```
* Apply stash to keep working on it
-```
-git stash apply
-# or apply a specific one from out stack
-git stash apply stash@{3}
-```
+
+ ```
+ git stash apply
+ # or apply a specific one from out stack
+ git stash apply stash@{3}
+ ```
----------
* Every time we save a stash it gets stacked so by using list we can see all our
stashes.
-```
-git stash list
-# or for more information (log methods)
-git stash list --stat
-```
+ ```
+ git stash list
+ # or for more information (log methods)
+ git stash list --stat
+ ```
* To clean our stack we need to manually remove them.
-```
-# drop top stash
-git stash drop
-# or
-git stash drop <name>
-# to clear all history we can use
-git stash clear
-```
+ ```
+ # drop top stash
+ git stash drop
+ # or
+ git stash drop <name>
+ # to clear all history we can use
+ git stash clear
+ ```
----------
* Apply and drop on one command
-```
- git stash pop
-```
+ ```
+ git stash pop
+ ```
* If we meet conflicts we need to either reset or commit our changes.
@@ -64,12 +66,12 @@ git stash clear
## Git Stash
1. Modify a file
-2. Stage file
-3. Stash it
-4. View our stash list
-5. Confirm no pending changes through status
-5. Apply with pop
-6. View list to confirm changes
+1. Stage file
+1. Stash it
+1. View our stash list
+1. Confirm no pending changes through status
+1. Apply with pop
+1. View list to confirm changes
----------
diff --git a/doc/university/training/topics/tags.md b/doc/university/training/topics/tags.md
index 6333ceedbd7..14c39457838 100644
--- a/doc/university/training/topics/tags.md
+++ b/doc/university/training/topics/tags.md
@@ -22,7 +22,7 @@ comments: false
**Additional resources**
-[http://git-scm.com/book/en/Git-Basics-Tagging](http://git-scm.com/book/en/Git-Basics-Tagging)
+<https://git-scm.com/book/en/Git-Basics-Tagging>
----------
diff --git a/doc/university/training/topics/unstage.md b/doc/university/training/topics/unstage.md
index fc72949ade9..ee7913637b9 100644
--- a/doc/university/training/topics/unstage.md
+++ b/doc/university/training/topics/unstage.md
@@ -10,26 +10,27 @@ comments: false
* To remove files from stage use reset HEAD. Where HEAD is the last commit of the current branch.
-```bash
-git reset HEAD <file>
-```
+ ```bash
+ git reset HEAD <file>
+ ```
* This will unstage the file but maintain the modifications. To revert the file back to the state it was in before the changes we can use:
-```bash
-git checkout -- <file>
-```
+ ```bash
+ git checkout -- <file>
+ ```
----------
* To remove a file from disk and repo use 'git rm' and to rm a dir use the '-r' flag.
-```
-git rm '*.txt'
-git rm -r <dirname>
-```
+ ```
+ git rm '*.txt'
+ git rm -r <dirname>
+ ```
* If we want to remove a file from the repository but keep it on disk, say we forgot to add it to our `.gitignore` file then use `--cache`.
-```
-git rm <filename> --cache
-```
+
+ ```
+ git rm <filename> --cache
+ ```
diff --git a/doc/university/training/user_training.md b/doc/university/training/user_training.md
index dccb6cbf071..ca3f777f403 100644
--- a/doc/university/training/user_training.md
+++ b/doc/university/training/user_training.md
@@ -6,91 +6,90 @@ comments: false
---
-# Agenda
+## Agenda
-1. Brief history of Git
-1. GitLab walkthrough
-1. Configure your environment
-1. Workshop
+1. Brief history of Git.
+1. GitLab walkthrough.
+1. Configure your environment.
+1. Workshop.
---
-# Git introduction
+## Git introduction
-https://git-scm.com/about
+<https://git-scm.com/about>
-- Distributed version control
- - Does not rely on connection to a central server
- - Many copies of the complete history
-- Powerful branching and merging
-- Adapts to nearly any workflow
-- Fast, reliable and stable file format
+- Distributed version control.
+ - Does not rely on connection to a central server.
+ - Many copies of the complete history.
+- Powerful branching and merging.
+- Adapts to nearly any workflow.
+- Fast, reliable and stable file format.
---
-# Help!
+## Help!
Use the tools at your disposal when you get stuck.
-- Use '`git help <command>`' command
-- Use Google
-- Read documentation at https://git-scm.com
+- Use '`git help <command>`' command.
+- Use Google.
+- Read documentation at <https://git-scm.com>.
---
-# GitLab Walkthrough
+## GitLab Walkthrough
![fit](logo.png)
---
-# Configure your environment
+## Configure your environment
- Windows: Install 'Git for Windows'
-> https://git-for-windows.github.io
+> <https://git-for-windows.github.io>
- Mac: Type '`git`' in the Terminal application.
> If it's not installed, it will prompt you to install it.
-- Debian: '`sudo apt-get install git-all`'
-or Red Hat '`sudo yum install git-all`'
+- Debian: '`sudo apt-get install git-all`' or Red Hat '`sudo yum install git-all`'
---
-# Git Workshop
+## Git Workshop
-## Overview
+### Overview
-1. Configure Git
-1. Configure SSH Key
-1. Create a project
-1. Committing
-1. Feature branching
-1. Merge requests
-1. Feedback and Collaboration
+1. Configure Git.
+1. Configure SSH Key.
+1. Create a project.
+1. Committing.
+1. Feature branching.
+1. Merge requests.
+1. Feedback and Collaboration.
---
-# Configure Git
+## Configure Git
-One-time configuration of the Git client
+One-time configuration of the Git client:
-```bash
+```sh
git config --global user.name "Your Name"
git config --global user.email you@example.com
```
---
-# Configure SSH Key
+## Configure SSH Key
-```bash
+```sh
ssh-keygen -t rsa -b 4096 -C "you@computer-name"
```
-```bash
+```sh
# You will be prompted for the following information. Press enter to accept the defaults. Defaults appear in parentheses.
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/you/.ssh/id_rsa):
@@ -102,31 +101,30 @@ The key fingerprint is:
39:fc:ce:94:f4:09:13:95:64:9a:65:c1:de:05:4d:01 you@computer-name
```
-Copy your public key and add it to your GitLab profile
+Copy your public key and add it to your GitLab profile:
-```bash
+```sh
cat ~/.ssh/id_rsa.pub
```
-```bash
+```sh
ssh-rsa AAAAB3NzaC1yc2EAAAADAQEL17Ufacg8cDhlQMS5NhV8z3GHZdhCrZbl4gz you@example.com
```
---
-# Create a project
+## Create a project
-- Create a project in your user namespace
- - Choose to import from 'Any Repo by URL' and use
- https://gitlab.com/gitlab-org/training-examples.git
+- Create a project in your user namespace.
+ - Choose to import from 'Any Repo by URL' and use <https://gitlab.com/gitlab-org/training-examples.git>.
- Create a '`development`' or '`workspace`' directory in your home directory.
-- Clone the '`training-examples`' project
+- Clone the '`training-examples`' project.
---
-# Commands
+## Commands (project)
-```
+```sh
mkdir ~/development
cd ~/development
@@ -141,37 +139,37 @@ cd training-examples
---
-# Git concepts
+## Git concepts
-**Untracked files**
+### Untracked files
New files that Git has not been told to track previously.
-**Working area**
+### Working area
Files that have been modified but are not committed.
-**Staging area**
+### Staging area
Modified files that have been marked to go in the next commit.
---
-# Committing
+## Committing
-1. Edit '`edit_this_file.rb`' in '`training-examples`'
-1. See it listed as a changed file (working area)
-1. View the differences
-1. Stage the file
-1. Commit
-1. Push the commit to the remote
-1. View the git log
+1. Edit '`edit_this_file.rb`' in '`training-examples`'.
+1. See it listed as a changed file (working area).
+1. View the differences.
+1. Stage the file.
+1. Commit.
+1. Push the commit to the remote.
+1. View the git log.
---
-# Commands
+## Commands (committing)
-```
+```sh
# Edit `edit_this_file.rb`
git status
git diff
@@ -183,29 +181,29 @@ git log
---
-# Feature branching
+## Feature branching
-- Efficient parallel workflow for teams
-- Develop each feature in a branch
-- Keeps changes isolated
-- Consider a 1-to-1 link to issues
-- Push branches to the server frequently
- - Hint: This is a cheap backup for your work-in-progress code
+- Efficient parallel workflow for teams.
+- Develop each feature in a branch.
+- Keeps changes isolated.
+- Consider a 1-to-1 link to issues.
+- Push branches to the server frequently.
+ - Hint: This is a cheap backup for your work-in-progress code.
---
-# Feature branching
+## Feature branching steps
-1. Create a new feature branch called 'squash_some_bugs'
+1. Create a new feature branch called 'squash_some_bugs'.
1. Edit '`bugs.rb`' and remove all the bugs.
-1. Commit
-1. Push
+1. Commit.
+1. Push.
---
-# Commands
+## Commands (feature branching)
-```
+```sh
git checkout -b squash_some_bugs
# Edit `bugs.rb`
git status
@@ -216,51 +214,50 @@ git push origin squash_some_bugs
---
-# Merge requests
+## Merge requests
-- When you want feedback create a merge request
-- Target is the ‘default’ branch (usually master)
-- Assign or mention the person you would like to review
-- Add 'WIP' to the title if it's a work in progress
-- When accepting, always delete the branch
-- Anyone can comment, not just the assignee
-- Push corrections to the same branch
+- When you want feedback create a merge request.
+- Target is the ‘default’ branch (usually master).
+- Assign or mention the person you would like to review.
+- Add 'WIP' to the title if it's a work in progress.
+- When accepting, always delete the branch.
+- Anyone can comment, not just the assignee.
+- Push corrections to the same branch.
---
-# Merge requests
+## Merge requests steps
-**Create your first merge request**
+Create your first merge request:
-1. Use the blue button in the activity feed
-1. View the diff (changes) and leave a comment
-1. Push a new commit to the same branch
-1. Review the changes again and notice the update
+1. Use the blue button in the activity feed.
+1. View the diff (changes) and leave a comment.
+1. Push a new commit to the same branch.
+1. Review the changes again and notice the update.
---
-# Feedback and Collaboration
+## Feedback and Collaboration
-- Merge requests are a time for feedback and collaboration
-- Giving feedback is hard
-- Be as kind as possible
-- Receiving feedback is hard
-- Be as receptive as possible
-- Feedback is about the best code, not the person. You are not your code
+- Merge requests are a time for feedback and collaboration.
+- Giving feedback is hard.
+- Be as kind as possible.
+- Receiving feedback is hard.
+- Be as receptive as possible.
+- Feedback is about the best code, not the person. You are not your code.
---
-# Feedback and Collaboration
+## Feedback and Collaboration resources
Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:
-[https://github.com/thoughtbot/guides/tree/master/code-review](https://github.com/thoughtbot/guides/tree/master/code-review)
+<https://github.com/thoughtbot/guides/tree/master/code-review>.
-See GitLab merge requests for examples:
-[https://gitlab.com/gitlab-org/gitlab-ce/merge_requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests)
+See GitLab merge requests for examples: <https://gitlab.com/gitlab-org/gitlab-ce/merge_requests>.
---
-# Explore GitLab projects
+## Explore GitLab projects
![fit](logo.png)
@@ -274,31 +271,29 @@ See GitLab merge requests for examples:
---
-# Tags
+## Tags
-- Useful for marking deployments and releases
-- Annotated tags are an unchangeable part of Git history
-- Soft/lightweight tags can be set and removed at will
-- Many projects combine an annotated release tag with a stable branch
-- Consider setting deployment/release tags automatically
+- Useful for marking deployments and releases.
+- Annotated tags are an unchangeable part of Git history.
+- Soft/lightweight tags can be set and removed at will.
+- Many projects combine an annotated release tag with a stable branch.
+- Consider setting deployment/release tags automatically.
---
-# Tags
-
-- Create a lightweight tag
-- Create an annotated tag
-- Push the tags to the remote repository
+## Tags steps
-**Additional resources**
+1. Create a lightweight tag.
+1. Create an annotated tag.
+1. Push the tags to the remote repository.
-[http://git-scm.com/book/en/Git-Basics-Tagging](http://git-scm.com/book/en/Git-Basics-Tagging)
+Additional resources: <http://git-scm.com/book/en/Git-Basics-Tagging>.
---
-# Commands
+## Commands (tags)
-```
+```sh
git checkout master
# Lightweight tag
@@ -313,31 +308,31 @@ git push origin --tags
---
-# Merge conflicts
+## Merge conflicts
-- Happen often
-- Learning to fix conflicts is hard
-- Practice makes perfect
+- Happen often.
+- Learning to fix conflicts is hard.
+- Practice makes perfect.
- Force push after fixing conflicts. Be careful!
---
-# Merge conflicts
+## Merge conflicts steps
1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
-1. Commit and push
+1. Commit and push.
1. Checkout master and edit `conflicts.rb`. Add 'Line6' and 'Line7' below 'Line3'.
-1. Commit and push to master
-1. Create a merge request
+1. Commit and push to master.
+1. Create a merge request.
---
-# Merge conflicts
+## Merge conflicts commands
After creating a merge request you should notice that conflicts exist. Resolve
the conflicts locally by rebasing.
-```
+```sh
git rebase master
# Fix conflicts by editing the files.
@@ -350,7 +345,7 @@ git push origin <branch> -f
---
-# Rebase with squash
+## Rebase with squash
You may end up with a commit log that looks like this:
@@ -368,11 +363,11 @@ Squash these in to meaningful commits using an interactive rebase.
---
-# Rebase with squash
+## Rebase with squash commands
Squash the commits on the same branch we used for the merge conflicts step.
-```
+```sh
git rebase -i master
```
@@ -380,17 +375,17 @@ In the editor, leave the first commit as 'pick' and set others to 'fixup'.
---
-# Questions?
+## Questions?
![fit](logo.png)
Thank you for your hard work!
-**Additional Resources**
+## Additional Resources
-GitLab Documentation [http://docs.gitlab.com](http://docs.gitlab.com/)
-GUI Clients [http://git-scm.com/downloads/guis](http://git-scm.com/downloads/guis)
-Pro git book [http://git-scm.com/book](http://git-scm.com/book)
-Platzi Course [https://courses.platzi.com/courses/git-gitlab/](https://courses.platzi.com/courses/git-gitlab/)
-Code School tutorial [http://try.github.io/](http://try.github.io/)
-Contact Us at `subscribers@gitlab.com`
+- GitLab Documentation: <http://docs.gitlab.com/>.
+- GUI Clients: <http://git-scm.com/downloads/guis>.
+- Pro Git book: <http://git-scm.com/book>.
+- Platzi Course: <https://courses.platzi.com/courses/git-gitlab/>.
+- Code School tutorial: <http://try.github.io/>.
+- Contact us at `subscribers@gitlab.com`.
diff --git a/doc/update/10.7-to-10.8.md b/doc/update/10.7-to-10.8.md
index 13101a987f4..7bb628f9740 100644
--- a/doc/update/10.7-to-10.8.md
+++ b/doc/update/10.7-to-10.8.md
@@ -38,16 +38,16 @@ You can check which version you are running with `ruby -v`.
Download Ruby and compile it:
- ```bash
- mkdir /tmp/ruby && cd /tmp/ruby
- curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.gz
- echo '540996fec64984ab6099e34d2f5820b14904f15a ruby-2.3.7.tar.gz' | shasum -c - && tar xzf ruby-2.3.7.tar.gz
- cd ruby-2.3.7
-
- ./configure --disable-install-rdoc
- make
- sudo make install
- ```
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.gz
+echo '540996fec64984ab6099e34d2f5820b14904f15a ruby-2.3.7.tar.gz' | shasum -c - && tar xzf ruby-2.3.7.tar.gz
+cd ruby-2.3.7
+
+./configure --disable-install-rdoc
+make
+sudo make install
+```
Install Bundler:
diff --git a/doc/update/11.2-to-11.3.md b/doc/update/11.2-to-11.3.md
new file mode 100644
index 00000000000..d77f879ee57
--- /dev/null
+++ b/doc/update/11.2-to-11.3.md
@@ -0,0 +1,378 @@
+---
+comments: false
+---
+
+# From 11.2 to 11.3
+
+Make sure you view this update guide from the branch (version) of GitLab you would
+like to install (e.g., `11-3-stable`. You can select the branch in the version
+dropdown at the top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+NOTE: If you installed GitLab from source, make sure `rsync` is installed.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 11.0 and higher only support Ruby 2.4.x and dropped support for Ruby 2.3.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download Ruby and compile it:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.4.tar.gz
+echo 'ec82b0d53bd0adad9b19e6b45e44d54e9ec3f10c ruby-2.4.4.tar.gz' | shasum -c - && tar xzf ruby-2.4.4.tar.gz
+cd ruby-2.4.4
+
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
+This requires a minimum version of node v6.0.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v6.0.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript
+dependencies.
+
+```bash
+curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install yarn
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 11.0 and higher only supports Go 1.9.x and newer, and dropped support for Go
+1.5.x through 1.8.x. Be sure to upgrade your installation if necessary.
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
+echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.10.3.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all --prune
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-3-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-3-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. GitLab-Workhorse uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
+```
+
+### 9. Update Gitaly
+
+#### New Gitaly configuration options required
+
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
+
+```shell
+echo '
+[gitaly-ruby]
+dir = "/home/git/gitaly/ruby"
+
+[gitlab-shell]
+dir = "/home/git/gitlab-shell"
+' | sudo -u git tee -a /home/git/gitaly/config.toml
+```
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update gitlab-pages
+
+#### Only needed if you use GitLab Pages.
+
+Install and compile gitlab-pages. GitLab-Pages uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-pages
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+sudo -u git -H make
+```
+
+### 11. Update MySQL permissions
+
+If you are using MySQL you need to grant the GitLab user the necessary
+permissions on the database:
+
+```bash
+mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
+```
+
+If you use MySQL with replication, or just have MySQL configured with binary logging,
+you will need to also run the following on all of your MySQL servers:
+
+```bash
+mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+```
+
+You can make this setting permanent by adding it to your `my.cnf`:
+
+```
+log_bin_trust_function_creators=1
+```
+
+### 12. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-1-stable:config/gitlab.yml.example origin/11-3-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/11-1-stable:lib/support/nginx/gitlab-ssl origin/11-3-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/11-1-stable:lib/support/nginx/gitlab origin/11-3-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-3-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-3-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-1-stable:lib/support/init.d/gitlab.default.example origin/11-3-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 13. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Compile GetText PO files
+
+sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 14. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 15. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (11.2)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 11.1 to 11.2](11.1-to-11.2.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-3-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-3-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/update/11.3-to-11.4.md b/doc/update/11.3-to-11.4.md
new file mode 100644
index 00000000000..00dfb19b4b4
--- /dev/null
+++ b/doc/update/11.3-to-11.4.md
@@ -0,0 +1,378 @@
+---
+comments: false
+---
+
+# From 11.3 to 11.4
+
+Make sure you view this update guide from the branch (version) of GitLab you would
+like to install (e.g., `11-4-stable`. You can select the branch in the version
+dropdown at the top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+NOTE: If you installed GitLab from source, make sure `rsync` is installed.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 11.0 and higher only support Ruby 2.4.x and dropped support for Ruby 2.3.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download Ruby and compile it:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.5.tar.gz
+echo '4d650f302f1ec00256450b112bb023644b6ab6dd ruby-2.4.5.tar.gz' | shasum -c - && tar xzf ruby-2.4.5.tar.gz
+cd ruby-2.4.5
+
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
+This requires a minimum version of node v6.0.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v6.0.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript
+dependencies.
+
+```bash
+curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install yarn
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 11.4 and higher only supports Go 1.10.x and newer, and dropped support for Go
+1.9.x. Be sure to upgrade your installation if necessary.
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
+echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.10.3.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all --prune
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-4-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-4-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. GitLab-Workhorse uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
+```
+
+### 9. Update Gitaly
+
+#### New Gitaly configuration options required
+
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
+
+```shell
+echo '
+[gitaly-ruby]
+dir = "/home/git/gitaly/ruby"
+
+[gitlab-shell]
+dir = "/home/git/gitlab-shell"
+' | sudo -u git tee -a /home/git/gitaly/config.toml
+```
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update gitlab-pages
+
+#### Only needed if you use GitLab Pages.
+
+Install and compile gitlab-pages. GitLab-Pages uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-pages
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+sudo -u git -H make
+```
+
+### 11. Update MySQL permissions
+
+If you are using MySQL you need to grant the GitLab user the necessary
+permissions on the database:
+
+```bash
+mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
+```
+
+If you use MySQL with replication, or just have MySQL configured with binary logging,
+you will need to also run the following on all of your MySQL servers:
+
+```bash
+mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+```
+
+You can make this setting permanent by adding it to your `my.cnf`:
+
+```
+log_bin_trust_function_creators=1
+```
+
+### 12. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-1-stable:config/gitlab.yml.example origin/11-4-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/11-1-stable:lib/support/nginx/gitlab-ssl origin/11-4-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/11-1-stable:lib/support/nginx/gitlab origin/11-4-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-4-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-4-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-1-stable:lib/support/init.d/gitlab.default.example origin/11-4-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 13. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Compile GetText PO files
+
+sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 14. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 15. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (11.3)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 11.2 to 11.3](11.2-to-11.3.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-4-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-4-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/update/11.4-to-11.5.md b/doc/update/11.4-to-11.5.md
new file mode 100644
index 00000000000..44105348d14
--- /dev/null
+++ b/doc/update/11.4-to-11.5.md
@@ -0,0 +1,390 @@
+---
+comments: false
+---
+
+# From 11.4 to 11.5
+
+Make sure you view this update guide from the branch (version) of GitLab you would
+like to install (e.g., `11-5-stable`. You can select the branch in the version
+dropdown at the top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+NOTE: If you installed GitLab from source, make sure `rsync` is installed.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 11.0 and higher only support Ruby 2.4.x and dropped support for Ruby 2.3.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download Ruby and compile it:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.5.tar.gz
+echo '4d650f302f1ec00256450b112bb023644b6ab6dd ruby-2.4.5.tar.gz' | shasum -c - && tar xzf ruby-2.4.5.tar.gz
+cd ruby-2.4.5
+
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
+This requires a minimum version of node v6.0.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v6.0.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript
+dependencies.
+
+```bash
+curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install yarn
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 11.4 and higher only supports Go 1.10.x and newer, and dropped support for Go
+1.9.x. Be sure to upgrade your installation if necessary.
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://dl.google.com/go/go1.10.5.linux-amd64.tar.gz
+echo 'a035d9beda8341b645d3f45a1b620cf2d8fb0c5eb409be36b389c0fd384ecc3a go1.10.5.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.10.5.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.10.5.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all --prune
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-5-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-5-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. GitLab-Workhorse uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
+```
+
+### 9. Update Gitaly
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update gitlab-pages
+
+#### Only needed if you use GitLab Pages.
+
+Install and compile gitlab-pages. GitLab-Pages uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-pages
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+sudo -u git -H make
+```
+
+### 11. Update MySQL permissions
+
+If you are using MySQL you need to grant the GitLab user the necessary
+permissions on the database:
+
+```bash
+mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
+```
+
+If you use MySQL with replication, or just have MySQL configured with binary logging,
+you will need to also run the following on all of your MySQL servers:
+
+```bash
+mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+```
+
+You can make this setting permanent by adding it to your `my.cnf`:
+
+```
+log_bin_trust_function_creators=1
+```
+
+### 12. Update configuration files
+
+#### New `unicorn.rb` configuration
+
+Note: we have made [changes](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22372) to `unicorn.rb` to allow GitLab run with both Unicorn and Puma in future.
+
+- Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/11-5-stable/config/unicorn.rb.example but with your settings.
+ - In particular, make sure that `require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"` line exists and the `before_exec`, `before_fork`, and `after_fork` handlers are configured as shown below:
+
+```ruby
+require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
+
+before_exec do |server|
+ # Signal application hooks that we're about to restart
+ Gitlab::Cluster::LifecycleEvents.do_master_restart
+end
+
+before_fork do |server, worker|
+ # Signal application hooks that we're about to fork
+ Gitlab::Cluster::LifecycleEvents.do_before_fork
+end
+
+after_fork do |server, worker|
+ # Signal application hooks of worker start
+ Gitlab::Cluster::LifecycleEvents.do_worker_start
+end
+```
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-4-stable:config/gitlab.yml.example origin/11-5-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/11-4-stable:lib/support/nginx/gitlab-ssl origin/11-5-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/11-4-stable:lib/support/nginx/gitlab origin/11-5-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-5-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-5-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-4-stable:lib/support/init.d/gitlab.default.example origin/11-5-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 13. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Compile GetText PO files
+
+sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 14. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 15. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (11.4)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 11.3 to 11.4](11.3-to-11.4.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-5-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-5-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md
index 311664b2bc1..d292327efbd 100644
--- a/doc/update/4.2-to-5.0.md
+++ b/doc/update/4.2-to-5.0.md
@@ -32,7 +32,7 @@ cd /home/git/
sudo -u git -H git clone https://github.com/gitlabhq/gitlab-shell.git /home/git/gitlab-shell
```
-## 3. setup gitlab-shell
+## 3. set up gitlab-shell
```bash
# chmod all repos and files under git
diff --git a/doc/update/7.5-to-7.6.md b/doc/update/7.5-to-7.6.md
index f0dfb177b79..0d45a9528b9 100644
--- a/doc/update/7.5-to-7.6.md
+++ b/doc/update/7.5-to-7.6.md
@@ -82,7 +82,7 @@ git diff origin/7-5-stable:config/gitlab.yml.example origin/7-6-stable:config/gi
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`][nginx] but with your settings
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your setting
-#### Setup time zone (optional)
+#### Set up time zone (optional)
Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it.
diff --git a/doc/update/7.6-to-7.7.md b/doc/update/7.6-to-7.7.md
index 85de6b0c546..5e0b2ca7bcd 100644
--- a/doc/update/7.6-to-7.7.md
+++ b/doc/update/7.6-to-7.7.md
@@ -82,7 +82,7 @@ git diff origin/7-6-stable:config/gitlab.yml.example origin/7-7-stable:config/gi
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`][nginx] but with your settings
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your setting
-#### Setup time zone (optional)
+#### Set up time zone (optional)
Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it.
diff --git a/doc/update/7.7-to-7.8.md b/doc/update/7.7-to-7.8.md
index 7cee5f79a13..f5b1ebf0a9c 100644
--- a/doc/update/7.7-to-7.8.md
+++ b/doc/update/7.7-to-7.8.md
@@ -83,7 +83,7 @@ git diff origin/7-7-stable:config/gitlab.yml.example origin/7-8-stable:config/gi
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your settings.
* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section.
-#### Setup time zone (optional)
+#### Set up time zone (optional)
Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it.
diff --git a/doc/update/7.8-to-7.9.md b/doc/update/7.8-to-7.9.md
index 5a8b689dbc1..0db7698936b 100644
--- a/doc/update/7.8-to-7.9.md
+++ b/doc/update/7.8-to-7.9.md
@@ -85,7 +85,7 @@ git diff origin/7-8-stable:config/gitlab.yml.example origin/7-9-stable:config/gi
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your settings.
* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section.
-#### Setup time zone (optional)
+#### Set up time zone (optional)
Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it.
diff --git a/doc/update/7.9-to-7.10.md b/doc/update/7.9-to-7.10.md
index 99df51dbb99..782fb0736e6 100644
--- a/doc/update/7.9-to-7.10.md
+++ b/doc/update/7.9-to-7.10.md
@@ -81,7 +81,7 @@ git diff origin/7-9-stable:config/gitlab.yml.example origin/7-10-stable:config/g
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your settings.
* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section.
-#### Setup time zone (optional)
+#### Set up time zone (optional)
Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it.
diff --git a/doc/update/8.17-to-9.0.md b/doc/update/8.17-to-9.0.md
index 2e0c26a9092..74ce52859fa 100644
--- a/doc/update/8.17-to-9.0.md
+++ b/doc/update/8.17-to-9.0.md
@@ -150,48 +150,48 @@ Update your current configuration as follows, replacing with your storages names
1. Update your `gitlab.yml`, from
- ```yaml
- repositories:
- storages: # You must have at least a 'default' storage path.
- default: /home/git/repositories
- nfs: /mnt/nfs/repositories
- cephfs: /mnt/cephfs/repositories
- ```
-
- to
-
- ```yaml
- repositories:
- storages: # You must have at least a 'default' storage path.
- default:
- path: /home/git/repositories
- nfs:
- path: /mnt/nfs/repositories
- cephfs:
- path: /mnt/cephfs/repositories
- ```
+ ```yaml
+ repositories:
+ storages: # You must have at least a 'default' storage path.
+ default: /home/git/repositories
+ nfs: /mnt/nfs/repositories
+ cephfs: /mnt/cephfs/repositories
+ ```
+
+ to
+
+ ```yaml
+ repositories:
+ storages: # You must have at least a 'default' storage path.
+ default:
+ path: /home/git/repositories
+ nfs:
+ path: /mnt/nfs/repositories
+ cephfs:
+ path: /mnt/cephfs/repositories
+ ```
**For Omnibus installations**
1. Update your `/etc/gitlab/gitlab.rb`, from
- ```ruby
- git_data_dirs({
- "default" => "/var/opt/gitlab/git-data",
- "nfs" => "/mnt/nfs/git-data",
- "cephfs" => "/mnt/cephfs/git-data"
- })
- ```
-
- to
-
- ```ruby
- git_data_dirs({
- "default" => { "path" => "/var/opt/gitlab/git-data" },
- "nfs" => { "path" => "/mnt/nfs/git-data" },
- "cephfs" => { "path" => "/mnt/cephfs/git-data" }
- })
- ```
+ ```ruby
+ git_data_dirs({
+ "default" => "/var/opt/gitlab/git-data",
+ "nfs" => "/mnt/nfs/git-data",
+ "cephfs" => "/mnt/cephfs/git-data"
+ })
+ ```
+
+ to
+
+ ```ruby
+ git_data_dirs({
+ "default" => { "path" => "/var/opt/gitlab/git-data" },
+ "nfs" => { "path" => "/mnt/nfs/git-data" },
+ "cephfs" => { "path" => "/mnt/cephfs/git-data" }
+ })
+ ```
#### Git configuration
diff --git a/doc/update/9.0-to-9.1.md b/doc/update/9.0-to-9.1.md
index f60bd92e236..3a806d2f8c8 100644
--- a/doc/update/9.0-to-9.1.md
+++ b/doc/update/9.0-to-9.1.md
@@ -150,48 +150,48 @@ Update your current configuration as follows, replacing with your storages names
1. Update your `gitlab.yml`, from
- ```yaml
- repositories:
- storages: # You must have at least a 'default' storage path.
- default: /home/git/repositories
- nfs: /mnt/nfs/repositories
- cephfs: /mnt/cephfs/repositories
- ```
-
- to
-
- ```yaml
- repositories:
- storages: # You must have at least a 'default' storage path.
- default:
- path: /home/git/repositories
- nfs:
- path: /mnt/nfs/repositories
- cephfs:
- path: /mnt/cephfs/repositories
- ```
+ ```yaml
+ repositories:
+ storages: # You must have at least a 'default' storage path.
+ default: /home/git/repositories
+ nfs: /mnt/nfs/repositories
+ cephfs: /mnt/cephfs/repositories
+ ```
+
+ to
+
+ ```yaml
+ repositories:
+ storages: # You must have at least a 'default' storage path.
+ default:
+ path: /home/git/repositories
+ nfs:
+ path: /mnt/nfs/repositories
+ cephfs:
+ path: /mnt/cephfs/repositories
+ ```
**For Omnibus installations**
1. Update your `/etc/gitlab/gitlab.rb`, from
-
- ```ruby
- git_data_dirs({
- "default" => "/var/opt/gitlab/git-data",
- "nfs" => "/mnt/nfs/git-data",
- "cephfs" => "/mnt/cephfs/git-data"
- })
- ```
-
- to
-
- ```ruby
- git_data_dirs({
- "default" => { "path" => "/var/opt/gitlab/git-data" },
- "nfs" => { "path" => "/mnt/nfs/git-data" },
- "cephfs" => { "path" => "/mnt/cephfs/git-data" }
- })
- ```
+
+ ```ruby
+ git_data_dirs({
+ "default" => "/var/opt/gitlab/git-data",
+ "nfs" => "/mnt/nfs/git-data",
+ "cephfs" => "/mnt/cephfs/git-data"
+ })
+ ```
+
+ to
+
+ ```ruby
+ git_data_dirs({
+ "default" => { "path" => "/var/opt/gitlab/git-data" },
+ "nfs" => { "path" => "/mnt/nfs/git-data" },
+ "cephfs" => { "path" => "/mnt/cephfs/git-data" }
+ })
+ ```
#### Git configuration
diff --git a/doc/update/9.4-to-9.5.md b/doc/update/9.4-to-9.5.md
index 1bfc1167c36..6a655f77a55 100644
--- a/doc/update/9.4-to-9.5.md
+++ b/doc/update/9.4-to-9.5.md
@@ -154,7 +154,7 @@ sudo -u git -H make
#### New Gitaly configuration options required
-In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell'.
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
```shell
echo '
diff --git a/doc/update/9.5-to-10.0.md b/doc/update/9.5-to-10.0.md
index 8d1cf0f737b..7790d192a82 100644
--- a/doc/update/9.5-to-10.0.md
+++ b/doc/update/9.5-to-10.0.md
@@ -154,7 +154,7 @@ sudo -u git -H make
#### New Gitaly configuration options required
-In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell'.
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
```shell
echo '
diff --git a/doc/update/README.md b/doc/update/README.md
index c98e20686e0..d4fc0cc91bf 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -38,11 +38,12 @@ Starting with GitLab 9.1.0 it's possible to upgrade to a newer major, minor, or
patch version of GitLab without having to take your GitLab instance offline.
However, for this to work there are the following requirements:
-1. You can only upgrade 1 minor release at a time. So from 9.1 to 9.2, not to
+- You can only upgrade 1 minor release at a time. So from 9.1 to 9.2, not to
9.3.
-2. You have to use [post-deployment
- migrations](../development/post_deployment_migrations.md).
-3. You are using PostgreSQL. If you are using MySQL please look at the release
+- You have to use [post-deployment
+ migrations](../development/post_deployment_migrations.md) (included in
+ zero downtime update steps below).
+- You are using PostgreSQL. If you are using MySQL please look at the release
post to see if downtime is required.
Most of the time you can safely upgrade from a patch release to the next minor
@@ -142,4 +143,4 @@ possible.
[ee-ce]: ../downgrade_ee_to_ce/README.md
[ce]: https://about.gitlab.com/features/#community
[ee]: https://about.gitlab.com/features/#enterprise
-[omni-ce-ee]: http://docs.gitlab.com/omnibus/update/README.html#from-community-edition-to-enterprise-edition
+[omni-ce-ee]: https://docs.gitlab.com/omnibus/update/README.html#updating-community-edition-to-enterprise-edition
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index a4f17746b69..2e8380aa5d8 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -83,7 +83,7 @@ sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workh
```bash
cd /home/git/gitlab
-sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_ENV=production
+sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,/home/git/repositories]" RAILS_ENV=production
```
### 6. Update gitlab-shell to the corresponding version
diff --git a/doc/user/admin_area/diff_limits.md b/doc/user/admin_area/diff_limits.md
new file mode 100644
index 00000000000..9205860ef1f
--- /dev/null
+++ b/doc/user/admin_area/diff_limits.md
@@ -0,0 +1,21 @@
+# Diff limits administration
+
+NOTE: **Note:**
+Merge requests and branch comparison views will be affected.
+
+CAUTION: **Caution:**
+These settings are currently under experimental state. They'll
+increase the resource consumption of your instance and should
+be edited mindfully.
+
+1. Access **Admin area > Settings > General**
+1. Expand **Diff limits**
+
+### Maximum diff patch size
+
+This is the content size each diff file (patch) is allowed to reach before
+it's collapsed, without the possibility of being expanded. A link redirecting
+to the blob view will be presented for the patches that surpass this limit.
+
+Patches surpassing 10% of this content size will be automatically collapsed,
+but expandable (a link to expand the diff will be presented).
diff --git a/doc/user/admin_area/img/admin_area_settings_button.png b/doc/user/admin_area/img/admin_area_settings_button.png
new file mode 100644
index 00000000000..315ef40a375
--- /dev/null
+++ b/doc/user/admin_area/img/admin_area_settings_button.png
Binary files differ
diff --git a/doc/user/admin_area/img/cohorts.png b/doc/user/admin_area/img/cohorts.png
deleted file mode 100644
index 8bae7faff07..00000000000
--- a/doc/user/admin_area/img/cohorts.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/admin_area/monitoring/convdev.md b/doc/user/admin_area/monitoring/convdev.md
index a98602c4d70..6ad8a5a7ff0 100644
--- a/doc/user/admin_area/monitoring/convdev.md
+++ b/doc/user/admin_area/monitoring/convdev.md
@@ -1,29 +1,5 @@
-# Conversational Development Index
+---
+redirect_to: '../../instance_statistics/convdev.md'
+---
-> [Introduced][ce-30469] in GitLab 9.3.
-
-Conversational Development Index (ConvDev) gives you an overview of your entire
-instance's feature usage, from idea to production. It looks at your usage in the
-past 30 days, averaged over the number of active users in that time period. It also
-provides a lead score per feature, which is calculated based on GitLab's analysis
-of top performing instances, based on [usage ping data][ping] that GitLab has
-collected. Your score is compared to the lead score, expressed as a percentage.
-The overall index score is an average over all your feature scores.
-
-![ConvDev index](img/convdev_index.png)
-
-The page also provides helpful links to articles and GitLab docs, to help you
-improve your scores.
-
-Your GitLab instance's usage ping must be activated in order to use this feature.
-Usage ping data is aggregated on GitLab's servers for analysis. Your usage
-information is **not sent** to any other GitLab instances.
-
-If you have just started using GitLab, it may take a few weeks for data to be
-collected before this feature is available.
-
-This feature is accessible only to a system admin, at
-**Admin area > Overview > ConvDev Index**.
-
-[ce-30469]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30469
-[ping]: ../settings/usage_statistics.md#usage-ping
+This document was moved to [another location](../../instance_statistics/convdev.md).
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
index 1b676bfb383..43b1190fb48 100644
--- a/doc/user/admin_area/monitoring/health_check.md
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -1,12 +1,12 @@
# Health Check
->**Notes:**
- - Liveness and readiness probes were [introduced][ce-10416] in GitLab 9.1.
- - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and will
- be deprecated in GitLab 9.1. Read more in the [old behavior](#old-behavior)
- section.
- - [Access token](#access-token) has been deprecated in GitLab 9.4
- in favor of [IP whitelist](#ip-whitelist)
+> **Notes:**
+> - Liveness and readiness probes were [introduced][ce-10416] in GitLab 9.1.
+> - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and will
+> be deprecated in GitLab 9.1. Read more in the [old behavior](#old-behavior)
+> section.
+> - [Access token](#access-token) has been deprecated in GitLab 9.4
+> in favor of [IP whitelist](#ip-whitelist)
GitLab provides liveness and readiness probes to indicate service health and
reachability to required services. These probes report on the status of the
diff --git a/doc/user/admin_area/monitoring/img/convdev_index.png b/doc/user/admin_area/monitoring/img/convdev_index.png
deleted file mode 100644
index 1bf1d6a83c9..00000000000
--- a/doc/user/admin_area/monitoring/img/convdev_index.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/admin_area/monitoring/img/health_check_token.png b/doc/user/admin_area/monitoring/img/health_check_token.png
index 182549fc484..8d4cf710176 100644
--- a/doc/user/admin_area/monitoring/img/health_check_token.png
+++ b/doc/user/admin_area/monitoring/img/health_check_token.png
Binary files differ
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index eb6f915f3f4..d4853a5842e 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -1,38 +1,67 @@
-# Continuous integration Admin settings
+# Continuous Integration and Deployment Admin settings **[CORE ONLY]**
-## Maximum artifacts size
+In this area, you will find settings for Auto DevOps, Runners and job artifacts.
+You can find it in the admin area, under **Settings > Continuous Integration and Deployment**.
-The maximum size of the [job artifacts][art-yml] can be set in the Admin area
-of your GitLab instance. The value is in *MB* and the default is 100MB. Note
-that this setting is set for each job.
+![Admin area settings button](../img/admin_area_settings_button.png)
+
+## Auto DevOps **[CORE ONLY]**
+
+To enable (or disable) [Auto DevOps](../../../topics/autodevops/index.md)
+for all projects:
+
+1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
+1. Check (or uncheck to disable) the box that says "Default to Auto DevOps pipeline for all projects".
+1. Optionally, set up the [Auto DevOps base domain](../../../topics/autodevops/index.md#auto-devops-base-domain)
+ which is going to be used for Auto Deploy and Auto Review Apps.
+1. Hit **Save changes** for the changes to take effect.
-1. Go to **Admin area > Settings** (`/admin/application_settings`).
+From now on, every existing project and newly created ones that don't have a
+`.gitlab-ci.yml`, will use the Auto DevOps pipelines.
- ![Admin area settings button](img/admin_area_settings_button.png)
+If you want to disable it for a specific project, you can do so in
+[its settings](../../../topics/autodevops/index.md#enabling-auto-devops).
+
+## Maximum artifacts size **[CORE ONLY]**
+
+The maximum size of the [job artifacts][art-yml] can be set in the Admin area
+of your GitLab instance. The value is in *MB* and the default is 100MB per job;
+on GitLab.com it's [set to 1G](../../gitlab_com/index.md#gitlab-ci-cd).
-1. Change the value of maximum artifacts size (in MB):
+To change it:
- ![Admin area maximum artifacts size](img/admin_area_maximum_artifacts_size.png)
+1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
+1. Change the value of maximum artifacts size (in MB).
+1. Hit **Save changes** for the changes to take effect.
-1. Hit **Save** for the changes to take effect.
+## Default artifacts expiration **[CORE ONLY]**
-## Default artifacts expiration
+The default expiration time of the [job artifacts](../../../administration/job_artifacts.md)
+can be set in the Admin area of your GitLab instance. The syntax of duration is
+described in [`artifacts:expire_in`](../../../ci/yaml/README.md#artifacts-expire_in)
+and the default value is `30 days`. On GitLab.com they
+[never expire](../../gitlab_com/index.md#gitlab-ci-cd).
-The default expiration time of the [job artifacts][art-yml] can be set in
-the Admin area of your GitLab instance. The syntax of duration is described
-in [artifacts:expire_in][duration-syntax]. The default is `30 days`. Note that
-this setting is set for each job. Set it to 0 if you don't want default
-expiration.
+1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
+1. Change the value of default expiration time.
+1. Hit **Save changes** for the changes to take effect.
-1. Go to **Admin area > Settings** (`/admin/application_settings`).
+This setting is set per job and can be overridden in
+[`.gitlab-ci.yml`](../../../ci/yaml/README.md#artifacts-expire_in).
+To disable the expiration, set it to `0`. The default unit is in seconds.
- ![Admin area settings button](img/admin_area_settings_button.png)
+## Archive jobs **[CORE ONLY]**
-1. Change the value of default expiration time ([syntax][duration-syntax]):
+Archiving jobs is useful for reducing the CI/CD footprint on the system by
+removing some of the capabilities of the jobs (metadata needed to run the job),
+but persisting the traces and artifacts for auditing purposes.
- ![Admin area default artifacts expiration](img/admin_area_default_artifacts_expiration.png)
+To set the duration for which the jobs will be considered as old and expired:
-1. Hit **Save** for the changes to take effect.
+1. Go to **Admin area > Settings > CI/CD > Continuous Integration and Deployment**.
+1. Change the value of "Archive jobs".
+1. Hit **Save changes** for the changes to take effect.
-[art-yml]: ../../../administration/job_artifacts.md
-[duration-syntax]: ../../../ci/yaml/README.md#artifactsexpire_in
+Once that time passes, the jobs will be archived and no longer able to be
+retried. Make it empty to never expire jobs. It has to be no less than 1 day,
+for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>.
diff --git a/doc/user/admin_area/settings/email.md b/doc/user/admin_area/settings/email.md
index 7c9e5bf882e..50c318a4969 100644
--- a/doc/user/admin_area/settings/email.md
+++ b/doc/user/admin_area/settings/email.md
@@ -3,3 +3,20 @@
## Custom logo
The logo in the header of some emails can be customized, see the [logo customization section](../../../customization/branded_page_and_email_header.md).
+
+## Custom hostname for private commit emails
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22560) in GitLab 11.5.
+
+This configuration option sets the email hostname for [private commit emails](../../profile/index.md#private-commit-email),
+and it's, by default, set to `users.noreply.YOUR_CONFIGURED_HOSTNAME`.
+
+In order to change this option:
+
+1. Go to **Admin area > Settings** (`/admin/application_settings`).
+1. Under the **Email** section, change the **Custom hostname (for private commit emails)** field.
+1. Hit **Save** for the changes to take effect.
+
+NOTE: **Note**: Once the hostname gets configured, every private commit email using the previous hostname, will not get
+recognized by GitLab. This can directly conflict with certain [Push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html) such as
+`Check whether author is a GitLab user` and `Check whether committer is the current authenticated user`.
diff --git a/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png b/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png
index 50a86ede56b..723be23e77b 100644
--- a/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png
+++ b/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png
Binary files differ
diff --git a/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png b/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png
index 33fd29e2039..3f827f1f7a3 100644
--- a/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png
+++ b/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png
Binary files differ
diff --git a/doc/user/admin_area/settings/img/admin_area_settings_button.png b/doc/user/admin_area/settings/img/admin_area_settings_button.png
deleted file mode 100644
index 1d2c0ac04bc..00000000000
--- a/doc/user/admin_area/settings/img/admin_area_settings_button.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/admin_area/settings/img/domain_blacklist.png b/doc/user/admin_area/settings/img/domain_blacklist.png
index dedd3be1e8f..a7e972b7c0a 100644
--- a/doc/user/admin_area/settings/img/domain_blacklist.png
+++ b/doc/user/admin_area/settings/img/domain_blacklist.png
Binary files differ
diff --git a/doc/user/admin_area/settings/img/restricted_url.png b/doc/user/admin_area/settings/img/restricted_url.png
index 67abd13f741..c71abf0a226 100644
--- a/doc/user/admin_area/settings/img/restricted_url.png
+++ b/doc/user/admin_area/settings/img/restricted_url.png
Binary files differ
diff --git a/doc/user/admin_area/settings/img/update-available.png b/doc/user/admin_area/settings/img/update-available.png
index 0dafdad618e..9887e06c7dc 100644
--- a/doc/user/admin_area/settings/img/update-available.png
+++ b/doc/user/admin_area/settings/img/update-available.png
Binary files differ
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
new file mode 100644
index 00000000000..93767aefb51
--- /dev/null
+++ b/doc/user/admin_area/settings/index.md
@@ -0,0 +1,22 @@
+# Admin area settings **[CORE ONLY]**
+
+In the admin area settings, you can find various options for your GitLab
+instance like sign-up restrictions, account limits and quota, metrics, etc.
+
+Navigate to it by going to **Admin area > Settings**. Some of the settings
+include:
+
+- [Continuous Integration and Deployment](continuous_integration.md)
+- [Email](email.md)
+- [Sign up restrictions](sign_up_restrictions.md)
+- [Terms](terms.md)
+- [Third party offers](third_party_offers.md)
+- [Usage statistics](usage_statistics.md)
+- [Visibility and access controls](visibility_and_access_controls.md)
+
+## GitLab.com admin area settings
+
+Most of the settings under the admin area change the behavior of the whole
+GitLab instance. For GitLab.com, the admin settings are available only for the
+GitLab.com administrators, and the parameters can be found on the
+[GitLab.com settings](../../gitlab_com/index.md) documentation.
diff --git a/doc/user/admin_area/settings/terms.md b/doc/user/admin_area/settings/terms.md
index aa817c9a209..e2290bf0598 100644
--- a/doc/user/admin_area/settings/terms.md
+++ b/doc/user/admin_area/settings/terms.md
@@ -35,17 +35,17 @@ continue their registration afterwards.
## Accepting terms
-When this feature was enabled, the users that have not accepted the
+When this feature is enabled, the users that have not accepted the
terms of service will be presented with a screen where they can either
accept or decline the terms.
![Respond to terms](img/respond_to_terms.png)
-When the user accepts the terms, they will be directed to where they
+If the user accepts the terms, they will be directed to where they
were going. After a sign-in or sign-up this will most likely be the
dashboard.
-When the user was already logged in when the feature was turned on,
+If the user was already logged in when the feature was turned on,
they will be asked to accept the terms on their next interaction.
-When a user declines the terms, they will be signed out.
+If a user declines the terms, they will be signed out.
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index 381efdf5d67..bd0155dc712 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -6,7 +6,7 @@ to perform various actions.
All statistics are opt-out, you can enable/disable them from the admin panel
under **Admin area > Settings > Usage statistics**.
-## Version check
+## Version check **[CORE ONLY]**
If enabled, version check will inform you if a new version is available and the
importance of it through a status. This is shown on the help page (i.e. `/help`)
@@ -23,17 +23,18 @@ GitLab Inc. collects your instance's version and hostname (through the HTTP
referer) as part of the version check. No other information is collected.
This information is used, among other things, to identify to which versions
-patches will need to be back ported, making sure active GitLab instances remain
+patches will need to be backported, making sure active GitLab instances remain
secure.
If you disable version check, this information will not be collected. Enable or
disable the version check at **Admin area > Settings > Usage statistics**.
-## Usage ping
+## Usage ping **[CORE ONLY]**
> [Introduced][ee-557] in GitLab Enterprise Edition 8.10. More statistics
[were added][ee-735] in GitLab Enterprise Edition
-8.12. [Moved to GitLab Community Edition][ce-23361] in 9.1.
+8.12. [Moved to GitLab Core][ce-23361] in 9.1. More statistics
+[were added][ee-6602] in GitLab Ultimate 11.2.
GitLab sends a weekly payload containing usage data to GitLab Inc. The usage
ping uses high-level data to help our product, support, and sales teams. It does
@@ -41,7 +42,11 @@ not send any project names, usernames, or any other specific data. The
information from the usage ping is not anonymous, it is linked to the hostname
of the instance.
-You can view the exact JSON payload in the administration panel.
+You can view the exact JSON payload in the administration panel. To view the payload:
+
+1. Go to the **Admin area** (spanner symbol on the top bar).
+1. Expand **Settings** in the left sidebar and click on **Metrics and profiling**.
+1. Expand **Usage statistics** and click on the **Preview payload** button.
### Deactivate the usage ping
@@ -67,6 +72,16 @@ production: &base
usage_ping_enabled: false
```
+## Instance statistics visibility **[CORE ONLY]**
+
+Once usage ping is enabled, GitLab will gather data from other instances and
+will be able to show [usage statistics](../../instance_statistics/index.md)
+of your instance to your users.
+
+This can be restricted to admins by selecting "Only admins" in the Instance
+Statistics visibility section under **Admin area > Settings > Usage statistics**.
+
[ee-557]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/557
[ee-735]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/735
[ce-23361]: https://gitlab.com/gitlab-org/gitlab-ce/issues/23361
+[ee-6602]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6602
diff --git a/doc/user/admin_area/user_cohorts.md b/doc/user/admin_area/user_cohorts.md
index e25e7a8bbc3..21e61e2ec44 100644
--- a/doc/user/admin_area/user_cohorts.md
+++ b/doc/user/admin_area/user_cohorts.md
@@ -1,37 +1,5 @@
-# Cohorts
+---
+redirect_to: '../instance_statistics/user_cohorts.md'
+---
-> **Notes:**
-> [Introduced][ce-23361] in GitLab 9.1.
-
-As a benefit of having the [usage ping active](settings/usage_statistics.md),
-GitLab lets you analyze the users' activities of your GitLab installation.
-Under `/admin/cohorts`, when the usage ping is active, GitLab will show the
-monthly cohorts of new users and their activities over time.
-
-## Overview
-
-How do we read the user cohorts table? Let's take an example with the following
-user cohorts.
-
-![User cohort example](img/cohorts.png)
-
-For the cohort of June 2016, 163 users have been added on this server and have
-been active since this month. One month later, in July 2016, out of
-these 163 users, 155 users (or 95% of the June cohort) are still active. Two
-months later, 139 users (or 85%) are still active. 9 months later, we can see
-that only 6% of this cohort are still active.
-
-The Inactive users column shows the number of users who have been added during
-the month, but who have never actually had any activity in the instance.
-
-How do we measure the activity of users? GitLab considers a user active if:
-
-* the user signs in
-* the user has Git activity (whether push or pull).
-
-## Setup
-
-1. [Activate the usage ping](settings/usage_statistics.md)
-2. Go to `/admin/cohorts` to see the user cohorts of the server
-
-[ce-23361]: https://gitlab.com/gitlab-org/gitlab-ce/issues/23361
+This document was moved to [another location](../instance_statistics/user_cohorts.md).
diff --git a/doc/user/award_emojis.md b/doc/user/award_emojis.md
index acbd2a66d37..93be3da44d4 100644
--- a/doc/user/award_emojis.md
+++ b/doc/user/award_emojis.md
@@ -1,10 +1,10 @@
# Award emoji
->**Notes:**
-- First [introduced][1825] in GitLab 8.2.
-- GitLab 9.0 [introduced][ce-9570] the usage of native emojis if the platform
- supports them and falls back to images or CSS sprites. This change greatly
- improved the award emoji performance overall.
+> **Notes:**
+> - First [introduced][1825] in GitLab 8.2.
+> - GitLab 9.0 [introduced][ce-9570] the usage of native emojis if the platform
+> supports them and falls back to images or CSS sprites. This change greatly
+> improved the award emoji performance overall.
When you're collaborating online, you get fewer opportunities for high-fives
and thumbs-ups. Emoji can be awarded to issues, merge requests, snippets, and
diff --git a/doc/user/discussions/img/automatically_resolve_outdated_discussions.png b/doc/user/discussions/img/automatically_resolve_outdated_discussions.png
index 9a798ddd178..ba129e7a618 100644
--- a/doc/user/discussions/img/automatically_resolve_outdated_discussions.png
+++ b/doc/user/discussions/img/automatically_resolve_outdated_discussions.png
Binary files differ
diff --git a/doc/user/discussions/img/btn_new_issue_for_all_discussions.png b/doc/user/discussions/img/btn_new_issue_for_all_discussions.png
index b15447ec290..3306bf2e60e 100644
--- a/doc/user/discussions/img/btn_new_issue_for_all_discussions.png
+++ b/doc/user/discussions/img/btn_new_issue_for_all_discussions.png
Binary files differ
diff --git a/doc/user/discussions/img/discussion_comment.png b/doc/user/discussions/img/discussion_comment.png
index 8f66d138922..206ddebf54b 100644
--- a/doc/user/discussions/img/discussion_comment.png
+++ b/doc/user/discussions/img/discussion_comment.png
Binary files differ
diff --git a/doc/user/discussions/img/discussion_lock_system_notes.png b/doc/user/discussions/img/discussion_lock_system_notes.png
index 8e8e8e0bc3d..44a47e3f097 100644
--- a/doc/user/discussions/img/discussion_lock_system_notes.png
+++ b/doc/user/discussions/img/discussion_lock_system_notes.png
Binary files differ
diff --git a/doc/user/discussions/img/discussion_view.png b/doc/user/discussions/img/discussion_view.png
index 2ee1db2eab3..3a2b766ed7e 100644
--- a/doc/user/discussions/img/discussion_view.png
+++ b/doc/user/discussions/img/discussion_view.png
Binary files differ
diff --git a/doc/user/discussions/img/lock_form_member.png b/doc/user/discussions/img/lock_form_member.png
index 01c6308d24c..7bfcb4faae6 100644
--- a/doc/user/discussions/img/lock_form_member.png
+++ b/doc/user/discussions/img/lock_form_member.png
Binary files differ
diff --git a/doc/user/discussions/img/lock_form_non_member.png b/doc/user/discussions/img/lock_form_non_member.png
index 3bb70b69580..59e5fd89499 100644
--- a/doc/user/discussions/img/lock_form_non_member.png
+++ b/doc/user/discussions/img/lock_form_non_member.png
Binary files differ
diff --git a/doc/user/discussions/img/new_issue_for_discussion.png b/doc/user/discussions/img/new_issue_for_discussion.png
index 93c9dad8921..819d872a9a2 100644
--- a/doc/user/discussions/img/new_issue_for_discussion.png
+++ b/doc/user/discussions/img/new_issue_for_discussion.png
Binary files differ
diff --git a/doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.png b/doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.png
index bcdc0250d7c..9044926b0eb 100644
--- a/doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.png
+++ b/doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.png
Binary files differ
diff --git a/doc/user/discussions/img/preview_issue_for_discussion.png b/doc/user/discussions/img/preview_issue_for_discussion.png
index 2ee0653b2ba..30c273ca4c5 100644
--- a/doc/user/discussions/img/preview_issue_for_discussion.png
+++ b/doc/user/discussions/img/preview_issue_for_discussion.png
Binary files differ
diff --git a/doc/user/discussions/img/preview_issue_for_discussions.png b/doc/user/discussions/img/preview_issue_for_discussions.png
index 3fe0a666678..3d906e1b0b0 100644
--- a/doc/user/discussions/img/preview_issue_for_discussions.png
+++ b/doc/user/discussions/img/preview_issue_for_discussions.png
Binary files differ
diff --git a/doc/user/discussions/img/resolve_comment_button.png b/doc/user/discussions/img/resolve_comment_button.png
index 70340108874..7c19fac31a2 100644
--- a/doc/user/discussions/img/resolve_comment_button.png
+++ b/doc/user/discussions/img/resolve_comment_button.png
Binary files differ
diff --git a/doc/user/discussions/img/resolve_discussion_issue_notice.png b/doc/user/discussions/img/resolve_discussion_issue_notice.png
index e0ee6a39ffd..ed50dc1de91 100644
--- a/doc/user/discussions/img/resolve_discussion_issue_notice.png
+++ b/doc/user/discussions/img/resolve_discussion_issue_notice.png
Binary files differ
diff --git a/doc/user/discussions/img/resolve_discussion_open_issue.png b/doc/user/discussions/img/resolve_discussion_open_issue.png
index 98d63278326..9d0a14671d6 100644
--- a/doc/user/discussions/img/resolve_discussion_open_issue.png
+++ b/doc/user/discussions/img/resolve_discussion_open_issue.png
Binary files differ
diff --git a/doc/user/discussions/img/turn_off_lock.png b/doc/user/discussions/img/turn_off_lock.png
index dd05b398a8b..aae1def6f72 100644
--- a/doc/user/discussions/img/turn_off_lock.png
+++ b/doc/user/discussions/img/turn_off_lock.png
Binary files differ
diff --git a/doc/user/discussions/img/turn_on_lock.png b/doc/user/discussions/img/turn_on_lock.png
index 9597da4e14d..f36ffc8831b 100644
--- a/doc/user/discussions/img/turn_on_lock.png
+++ b/doc/user/discussions/img/turn_on_lock.png
Binary files differ
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 9b0ff02f227..097b18ad496 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -23,9 +23,9 @@ in the form of a resolvable or threaded discussion.
## Resolvable discussions
->**Notes:**
-- The main feature was [introduced][ce-5022] in GitLab 8.11.
-- Resolvable discussions can be added only to merge request diffs.
+> **Notes:**
+> - The main feature was [introduced][ce-5022] in GitLab 8.11.
+> - Resolvable discussions can be added only to merge request diffs.
Discussion resolution helps keep track of progress during planning or code review.
Resolving comments prevents you from forgetting to address feedback and lets you
@@ -271,6 +271,8 @@ edit existing comments. Non-team members are restricted from adding or editing c
| :-----------: | :----------: |
| ![Comment form member](img/lock_form_member.png) | ![Comment form non-member](img/lock_form_non_member.png) |
+Additionally locked issues can not be reopened.
+
[ce-5022]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5022
[ce-7125]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7125
[ce-7527]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7527
@@ -279,7 +281,7 @@ edit existing comments. Non-team members are restricted from adding or editing c
[ce-14053]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14053
[ce-14061]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14061
[ce-14531]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14531
-[ce-31847]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/31847
+[ce-31847]: https://gitlab.com/gitlab-org/gitlab-ce/issues/31847
[resolve-discussion-button]: img/resolve_discussion_button.png
[resolve-comment-button]: img/resolve_comment_button.png
[discussion-view]: img/discussion_view.png
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 20886faf418..e14e716a5eb 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -74,22 +74,17 @@ or over the size limit, you can [reduce your repository size with Git](../projec
## Shared Runners
Shared Runners on GitLab.com run in [autoscale mode] and powered by
-Google Cloud Platform and DigitalOcean. Autoscaling means reduced
+Google Cloud Platform. Autoscaling means reduced
waiting times to spin up CI/CD jobs, and isolated VMs for each project,
thus maximizing security.
They're free to use for public open source projects and limited to 2000 CI
minutes per month per group for private projects. Read about all
[GitLab.com plans](https://about.gitlab.com/pricing/).
-In case of DigitalOcean based Runners, all your CI/CD jobs run on ephemeral
-instances with 2GB of RAM, CoreOS and the latest Docker Engine installed.
-Instances provide 2 vCPUs and 60GB of SSD disk space. The default region of the
-VMs is NYC1.
-
-In case of Google Cloud Platform based Runners, all your CI/CD jobs run on
-ephemeral instances with 3.75GB of RAM, CoreOS and the latest Docker Engine
+All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, CoreOS and the latest Docker Engine
installed. Instances provide 1 vCPU and 25GB of HDD disk space. The default
region of the VMs is US East1.
+Each instance is used only for one job, this ensures any sensitive data left on the system can't be accessed by other people their CI jobs.
Jobs handled by the shared Runners on GitLab.com (`shared-runners-manager-X.gitlab.com`),
**will be timed out after 3 hours**, regardless of the timeout configured in a
@@ -104,7 +99,7 @@ Below are the shared Runners settings.
| Default Docker image | `ruby:2.5` | - |
| `privileged` (run [Docker in Docker]) | `true` | `false` |
-[ci_version_dashboard]: https://monitor.gitlab.net/dashboard/db/ci?from=now-1h&to=now&refresh=5m&orgId=1&panelId=12&fullscreen&theme=light
+[ci_version_dashboard]: https://dashboards.gitlab.com/dashboard/db/ci?from=now-1h&to=now&refresh=5m&orgId=1&panelId=12&fullscreen&theme=light
### `config.toml`
@@ -226,7 +221,7 @@ and the following environment variables:
## Cron jobs
-Periodically executed jobs by Sidekiq, to self-heal Gitlab, do external
+Periodically executed jobs by Sidekiq, to self-heal GitLab, do external
synchronizations, run scheduled pipelines, etc.:
| Setting | GitLab.com | Default |
diff --git a/doc/user/group/img/access_requests_management.png b/doc/user/group/img/access_requests_management.png
index 36deaa89a70..7de6a1c0a5e 100644
--- a/doc/user/group/img/access_requests_management.png
+++ b/doc/user/group/img/access_requests_management.png
Binary files differ
diff --git a/doc/user/group/img/add_new_members.png b/doc/user/group/img/add_new_members.png
index 53f5596de23..4431c9fbe0b 100644
--- a/doc/user/group/img/add_new_members.png
+++ b/doc/user/group/img/add_new_members.png
Binary files differ
diff --git a/doc/user/group/img/create_new_group_info.png b/doc/user/group/img/create_new_group_info.png
index 8d2501d9f7a..c2e6ed43c5b 100644
--- a/doc/user/group/img/create_new_group_info.png
+++ b/doc/user/group/img/create_new_group_info.png
Binary files differ
diff --git a/doc/user/group/img/create_new_project_from_group.png b/doc/user/group/img/create_new_project_from_group.png
index c35234660db..b6286ac7800 100644
--- a/doc/user/group/img/create_new_project_from_group.png
+++ b/doc/user/group/img/create_new_project_from_group.png
Binary files differ
diff --git a/doc/user/group/img/group_settings.png b/doc/user/group/img/group_settings.png
index 629cd0729aa..f3a75f1bde8 100644
--- a/doc/user/group/img/group_settings.png
+++ b/doc/user/group/img/group_settings.png
Binary files differ
diff --git a/doc/user/group/img/groups.png b/doc/user/group/img/groups.png
index 3173ddce7ff..2e27d46b370 100644
--- a/doc/user/group/img/groups.png
+++ b/doc/user/group/img/groups.png
Binary files differ
diff --git a/doc/user/group/img/membership_lock.png b/doc/user/group/img/membership_lock.png
index d31fbb43375..c9ad82c90f2 100644
--- a/doc/user/group/img/membership_lock.png
+++ b/doc/user/group/img/membership_lock.png
Binary files differ
diff --git a/doc/user/group/img/new_group_form.png b/doc/user/group/img/new_group_form.png
index 91727ab5336..1c4d3ec6ceb 100644
--- a/doc/user/group/img/new_group_form.png
+++ b/doc/user/group/img/new_group_form.png
Binary files differ
diff --git a/doc/user/group/img/new_group_from_groups.png b/doc/user/group/img/new_group_from_groups.png
index 9c5dd7ebd8b..ffafac1b1cd 100644
--- a/doc/user/group/img/new_group_from_groups.png
+++ b/doc/user/group/img/new_group_from_groups.png
Binary files differ
diff --git a/doc/user/group/img/new_group_from_other_pages.png b/doc/user/group/img/new_group_from_other_pages.png
index 77427224447..f84501d1ff2 100644
--- a/doc/user/group/img/new_group_from_other_pages.png
+++ b/doc/user/group/img/new_group_from_other_pages.png
Binary files differ
diff --git a/doc/user/group/img/request_access_button.png b/doc/user/group/img/request_access_button.png
index f1aae6afed7..4d73990ec7e 100644
--- a/doc/user/group/img/request_access_button.png
+++ b/doc/user/group/img/request_access_button.png
Binary files differ
diff --git a/doc/user/group/img/select_group_dropdown.png b/doc/user/group/img/select_group_dropdown.png
index 68fc950304c..8c03ffffbde 100644
--- a/doc/user/group/img/select_group_dropdown.png
+++ b/doc/user/group/img/select_group_dropdown.png
Binary files differ
diff --git a/doc/user/group/img/share_with_group_lock.png b/doc/user/group/img/share_with_group_lock.png
index c0f25389eaf..77b00d8a248 100644
--- a/doc/user/group/img/share_with_group_lock.png
+++ b/doc/user/group/img/share_with_group_lock.png
Binary files differ
diff --git a/doc/user/group/img/withdraw_access_request_button.png b/doc/user/group/img/withdraw_access_request_button.png
index c5d8ef6c04f..a5fe78eb090 100644
--- a/doc/user/group/img/withdraw_access_request_button.png
+++ b/doc/user/group/img/withdraw_access_request_button.png
Binary files differ
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index e6bf32a2dc5..d673fa4d21a 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -22,14 +22,14 @@ group and grant access to all their projects at once
- Create a group, include members of your team, and make it easier to
`@mention` all the team at once in issues and merge requests
- Create a group for your company members, and create [subgroups](subgroups/index.md)
- for each individual team. Let's say you create a group called `company-team`, and among others,
- you created subgroups in this group for each individual team `backend-team`,
- `frontend-team`, and `production-team`:
- 1. When you start a new implementation from an issue, you add a comment:
+ for each individual team. Let's say you create a group called `company-team`, and among others,
+ you created subgroups in this group for each individual team `backend-team`,
+ `frontend-team`, and `production-team`:
+ 1. When you start a new implementation from an issue, you add a comment:
_"`@company-team`, let's do it! `@company-team/backend-team` you're good to go!"_
- 1. When your backend team needs help from frontend, they add a comment:
+ 1. When your backend team needs help from frontend, they add a comment:
_"`@company-team/frontend-team` could you help us here please?"_
- 1. When the frontend team completes their implementation, they comment:
+ 1. When the frontend team completes their implementation, they comment:
_"`@company-team/backend-team`, it's done! Let's ship it `@company-team/production-team`!"_
## Namespaces
@@ -64,8 +64,8 @@ together in a single list view.
## Create a new group
> **Notes:**
-- For a list of words that are not allowed to be used as group names see the
- [reserved names](../reserved_names.md).
+> - For a list of words that are not allowed to be used as group names see the
+> [reserved names](../reserved_names.md).
You can create a group in GitLab from:
@@ -252,12 +252,18 @@ level of members in group.
Learn more about [Member Lock](https://docs.gitlab.com/ee/user/group/index.html#member-lock).
+#### Group-level file templates **[PREMIUM]**
+
+Group-level file templates allow you to share a set of templates for common file
+types with every project in a group.
+
+Learn more about [Group-level file templates](https://docs.gitlab.com/ee/user/group/index.html#group-level-file-templates-premium).
+
### Advanced settings
- **Projects**: view all projects within that group, add members to each project,
access each project's settings, and remove any project from the same screen.
- **Webhooks**: configure [webhooks](../project/integrations/webhooks.md) to your group.
-- **Push rules**: configure [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html#push-rules) to your group. **[STARTER]**
- **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events)
for the group. **[STARTER ONLY]**
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group
diff --git a/doc/user/group/subgroups/img/group_members.png b/doc/user/group/subgroups/img/group_members.png
index b95fe6263bf..830ccafa794 100644
--- a/doc/user/group/subgroups/img/group_members.png
+++ b/doc/user/group/subgroups/img/group_members.png
Binary files differ
diff --git a/doc/user/group/subgroups/img/mention_subgroups.png b/doc/user/group/subgroups/img/mention_subgroups.png
index 8e6bed0111b..ec370add4f9 100644
--- a/doc/user/group/subgroups/img/mention_subgroups.png
+++ b/doc/user/group/subgroups/img/mention_subgroups.png
Binary files differ
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 08849ac1df4..8db36c4a0e8 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -1,9 +1,8 @@
# Subgroups
->**Notes:**
-- [Introduced][ce-2772] in GitLab 9.0.
-- Not available when using MySQL as external database (support removed in
- GitLab 9.3 [due to performance reasons][issue]).
+NOTE: **Note:**
+[Introduced][ce-2772] in GitLab 9.0. Not available when using MySQL as external
+database (support removed in GitLab 9.3 [due to performance reasons][issue]).
With subgroups (aka nested groups or hierarchical groups) you can have
up to 20 levels of nested groups, which among other things can help you to:
@@ -79,14 +78,14 @@ structure.
## Creating a subgroup
->**Notes:**
-- You need to be an Owner of a group in order to be able to create
- a subgroup. For more information check the [permissions table][permissions].
-- For a list of words that are not allowed to be used as group names see the
- [reserved names][reserved].
-- Users can always create subgroups if they are explicitly added as an Owner to
- a parent group even if group creation is disabled by an administrator in their
- settings.
+NOTE: **Note:**
+You need to be an Owner of a group in order to be able to create a subgroup. For
+more information check the [permissions table][permissions].
+For a list of words that are not allowed to be used as group names see the
+[reserved names][reserved].
+Users can always create subgroups if they are explicitly added as an Owner to
+a parent group even if group creation is disabled by an administrator in their
+settings.
To create a subgroup:
@@ -136,12 +135,15 @@ From the image above, we can deduct the following things:
### Overriding the ancestor group membership
->**Note:**
+NOTE: **Note:**
You need to be an Owner of a group in order to be able to add members to it.
+NOTE: **Note:**
+A user's permissions in a subgroup cannot be lower than in any of its ancestor groups.
+Therefore, you cannot reduce a user's permissions in a subgroup with respect to its ancestor groups.
+
To override a user's membership of an ancestor group (the first group they were
-added to), simply add the user in the new subgroup again, but with different
-permissions.
+added to), add the user to the new subgroup again with a higher set of permissions.
For example, if User0 was first added to group `group-1/group-1-1` with Developer
permissions, then they will inherit those permissions in every other subgroup
diff --git a/doc/user/img/award_emoji_comment_picker.png b/doc/user/img/award_emoji_comment_picker.png
index 3ad1bab3119..07f90c898ed 100644
--- a/doc/user/img/award_emoji_comment_picker.png
+++ b/doc/user/img/award_emoji_comment_picker.png
Binary files differ
diff --git a/doc/user/img/award_emoji_select.png b/doc/user/img/award_emoji_select.png
index 496acb29eec..269282b94b0 100644
--- a/doc/user/img/award_emoji_select.png
+++ b/doc/user/img/award_emoji_select.png
Binary files differ
diff --git a/doc/user/img/award_emoji_votes_sort_options.png b/doc/user/img/award_emoji_votes_sort_options.png
index dd84b7f4f64..dc02d5169e0 100644
--- a/doc/user/img/award_emoji_votes_sort_options.png
+++ b/doc/user/img/award_emoji_votes_sort_options.png
Binary files differ
diff --git a/doc/user/img/color_inline_colorchip_render_gfm.png b/doc/user/img/color_inline_colorchip_render_gfm.png
new file mode 100644
index 00000000000..6a8a674d6e0
--- /dev/null
+++ b/doc/user/img/color_inline_colorchip_render_gfm.png
Binary files differ
diff --git a/doc/user/img/markdown_logo.png b/doc/user/img/markdown_logo.png
index bb3faaaec76..5184851b6cf 100644
--- a/doc/user/img/markdown_logo.png
+++ b/doc/user/img/markdown_logo.png
Binary files differ
diff --git a/doc/user/img/math_inline_sup_render_gfm.png b/doc/user/img/math_inline_sup_render_gfm.png
new file mode 100644
index 00000000000..bf1464457bc
--- /dev/null
+++ b/doc/user/img/math_inline_sup_render_gfm.png
Binary files differ
diff --git a/doc/user/img/mermaid_diagram_render_gfm.png b/doc/user/img/mermaid_diagram_render_gfm.png
new file mode 100644
index 00000000000..3b3eb3a738a
--- /dev/null
+++ b/doc/user/img/mermaid_diagram_render_gfm.png
Binary files differ
diff --git a/doc/user/img/task_list_ordered_render_gfm.png b/doc/user/img/task_list_ordered_render_gfm.png
new file mode 100644
index 00000000000..fdff8a9886c
--- /dev/null
+++ b/doc/user/img/task_list_ordered_render_gfm.png
Binary files differ
diff --git a/doc/user/img/unordered_check_list_render_gfm.png b/doc/user/img/unordered_check_list_render_gfm.png
new file mode 100644
index 00000000000..2e3fb7cbb79
--- /dev/null
+++ b/doc/user/img/unordered_check_list_render_gfm.png
Binary files differ
diff --git a/doc/user/index.md b/doc/user/index.md
index 90f0e2285c3..08995032cb1 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -96,7 +96,7 @@ directly from GitLab. No third-party integrations needed.
- [GitLab Auto Deploy](../ci/autodeploy/index.md): Deploy your application out-of-the-box with GitLab Auto Deploy.
- [Review Apps](../ci/review_apps/index.md): Live-preview the changes introduced by a merge request with Review Apps.
- [GitLab Pages](project/pages/index.md): Publish your static site directly from
-GitLab with Gitlab Pages. You can build, test, and deploy any Static Site Generator with Pages.
+GitLab with GitLab Pages. You can build, test, and deploy any Static Site Generator with Pages.
- [GitLab Container Registry](project/container_registry.md): Build and deploy Docker
images with Container Registry.
@@ -172,3 +172,7 @@ Automate GitLab via [API](../api/README.md).
## Git and GitLab
Learn what is [Git](../topics/git/index.md) and its best practices.
+
+## Instance statistics
+
+See [various statistics](instance_statistics/index.md) of your GitLab instance.
diff --git a/doc/user/instance_statistics/convdev.md b/doc/user/instance_statistics/convdev.md
new file mode 100644
index 00000000000..52b99b69a02
--- /dev/null
+++ b/doc/user/instance_statistics/convdev.md
@@ -0,0 +1,27 @@
+# Conversational Development Index
+
+> [Introduced][ce-30469] in GitLab 9.3.
+
+The Conversational Development Index (ConvDev Index) gives you an overview of your entire
+instance's adoption of [Concurrent DevOps](https://about.gitlab.com/concurrent-devops/)
+from planning to monitoring. It displays the usage of these GitLab features over
+the last 30 days, averaged over the number of active users in that time period. It also
+provides a Lead score per feature, which is calculated based on GitLab's analysis
+of top-performing instances based on [usage ping data][ping] that GitLab has
+collected. Your score is compared to the lead score, expressed as a percentage.
+Your overall index score is an average of all your feature score percentages.
+
+![ConvDev index](img/convdev_index.png)
+
+The page also provides helpful links to articles and GitLab docs, to help you
+improve your scores.
+
+Your GitLab instance's [usage ping][ping] must be activated in order to use this feature.
+Usage ping data is aggregated on GitLab's servers for analysis. Your usage
+information is **not sent** to any other GitLab instances.
+
+If you have just started using GitLab, it may take a few weeks for data to be
+collected before this feature is available.
+
+[ce-30469]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30469
+[ping]: ../admin_area/settings/usage_statistics.md#usage-ping
diff --git a/doc/user/instance_statistics/img/cohorts.png b/doc/user/instance_statistics/img/cohorts.png
new file mode 100644
index 00000000000..12e839e7cd2
--- /dev/null
+++ b/doc/user/instance_statistics/img/cohorts.png
Binary files differ
diff --git a/doc/user/instance_statistics/img/convdev_index.png b/doc/user/instance_statistics/img/convdev_index.png
new file mode 100644
index 00000000000..bee1317438d
--- /dev/null
+++ b/doc/user/instance_statistics/img/convdev_index.png
Binary files differ
diff --git a/doc/user/instance_statistics/img/instance_statistics_button.png b/doc/user/instance_statistics/img/instance_statistics_button.png
new file mode 100644
index 00000000000..6104321b1a6
--- /dev/null
+++ b/doc/user/instance_statistics/img/instance_statistics_button.png
Binary files differ
diff --git a/doc/user/instance_statistics/index.md b/doc/user/instance_statistics/index.md
new file mode 100644
index 00000000000..22f76f728e3
--- /dev/null
+++ b/doc/user/instance_statistics/index.md
@@ -0,0 +1,16 @@
+# Instance statistics
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/41416)
+in GitLab 11.2.
+
+Instance statistics gives users or admins access to instance-wide analytics.
+They are accessible to all users by default (GitLab admins can restrict its
+visibility in the [admin area](../admin_area/settings/usage_statistics.md)),
+and can be accessed via the top bar.
+
+![Instance Statistics button](img/instance_statistics_button.png)
+
+There are two kinds of statistics:
+
+- [Conversational Development (ConvDev) Index](convdev.md): Provides an overview of your entire instance's feature usage.
+- [User Cohorts](user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
diff --git a/doc/user/instance_statistics/user_cohorts.md b/doc/user/instance_statistics/user_cohorts.md
new file mode 100644
index 00000000000..70d5912dc4e
--- /dev/null
+++ b/doc/user/instance_statistics/user_cohorts.md
@@ -0,0 +1,27 @@
+# Cohorts
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/23361)
+in GitLab 9.1.
+
+As a benefit of having the [usage ping active](../admin_area/settings/usage_statistics.md),
+GitLab lets you analyze the users' activities over time of your GitLab installation.
+
+## Overview
+
+How do we read the user cohorts table? Let's take an example with the following
+user cohorts.
+
+![User cohort example](img/cohorts.png)
+
+For the cohort of Jan 2018, 15 users have been added on this server and have
+been active since this month. One month later, in Feb 2018, all 15 users are
+still active. 6 months later (Month 6, July), we can see 10 users from this cohort
+are active, or 66% of the original cohort of 15 that joined in January.
+
+The Inactive users column shows the number of users who have been added during
+the month, but who have never actually had any activity in the instance.
+
+How do we measure the activity of users? GitLab considers a user active if:
+
+* the user signs in
+* the user has Git activity (whether push or pull).
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 6856544ae1b..6c6119a2691 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -1,52 +1,104 @@
-# Markdown
+# GitLab Markdown
+
+This markdown guide is **valid for GitLab's system markdown entries and files**.
+It is not valid for the [GitLab documentation website](https://docs.gitlab.com)
+nor [GitLab's main website](https://about.gitlab.com), as they both use
+[Kramdown](https://kramdown.gettalong.org) as their markdown engine.
+The documentation website uses an extended Kramdown gem, [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown).
+Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) for a complete Kramdown reference._
## GitLab Flavored Markdown (GFM)
-> **Note:**
-> Not all of the GitLab-specific extensions to Markdown that are described in
-> this document currently work on our documentation website.
+GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification][commonmark-spec] (which is based on standard Markdown) in a few significant ways to add some useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
+
+You can use GFM in the following areas:
+
+- Comments
+- Issues
+- Merge requests
+- Milestones
+- Snippets (the snippet must be named with a `.md` extension)
+- Wiki pages
+- Markdown documents inside repositories
+
+You can also use other rich text files in GitLab. You might have to install a
+dependency to do so. Please see the [`github-markup` gem readme](https://github.com/gitlabhq/markup#markups) for more information.
+
+> **Notes:**
+>
+> For the best result, we encourage you to check this document out as [rendered
+> by GitLab itself](markdown.md).
+>
+> As of 11.1, GitLab uses the [CommonMark Ruby Library][commonmarker] for Markdown
+processing of all new issues, merge requests, comments, and other Markdown content
+in the GitLab system. As of 11.3, wiki pages and Markdown files (`.md`) in the
+repositories are also processed with CommonMark. Older content in issues/comments
+are still processed using the [Redcarpet Ruby library][redcarpet].
>
-> For the best result, we encourage you to check this document out as rendered
-by GitLab: [markdown.md]
+> The documentation website had its [markdown engine migrated from Redcarpet to Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108)
+in October 2018.
+>
+> _Where there are significant differences, we will try to call them out in this document._
-_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content, wiki pages and Markdown files (`.md`) in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._
+### Transitioning to CommonMark
-_Where there are significant differences, we will try to call them out in this document._
+You may have Markdown documents in your repository that were written using some
+of the nuances of RedCarpet's version of Markdown. Since CommonMark uses a
+slightly stricter syntax, these documents may now display a little strangely
+since we've transitioned to CommonMark. Numbered lists with nested lists in
+particular can be displayed incorrectly.
-GitLab uses "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
+It is usually quite easy to fix. In the case of a nested list such as this:
-You can use GFM in the following areas:
+```markdown
+1. Chocolate
+ - dark
+ - milk
+```
-- comments
-- issues
-- merge requests
-- milestones
-- snippets (the snippet must be named with a `.md` extension)
-- wiki pages (currently only rendered by Redcarpet)
-- markdown documents inside the repository (currently only rendered by Redcarpet)
+simply add a space to each nested item:
-You can also use other rich text files in GitLab. You might have to install a
-dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information.
+```markdown
+1. Chocolate
+ - dark
+ - milk
+```
+
+In the documentation below, we try to highlight some of the differences.
+
+If you have a need to view a document using RedCarpet, you can add the token
+`legacy_render=1` to the end of the url, like this:
+
+https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md?legacy_render=1
+
+If you have a large volume of Markdown files, it can be tedious to determine
+if they will be displayed correctly or not. You can use the
+[diff_redcarpet_cmark](https://gitlab.com/digitalmoksha/diff_redcarpet_cmark)
+tool (not an officially supported product) to generate a list of files and
+differences between how RedCarpet and CommonMark render the files. It can give
+you a great idea if anything needs to be changed - many times nothing will need
+to changed.
### Newlines
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#newlines
-GFM honors the markdown specification in how [paragraphs and line breaks are handled](https://daringfireball.net/projects/markdown/syntax#p).
+GFM honors the markdown specification in how [paragraphs and line breaks are handled][commonmark-spec].
-A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
+A paragraph is simply one or more consecutive lines of text, separated by one or
+more blank lines.
Line-breaks, or soft returns, are rendered if you end a line with two or more spaces:
-[//]: # (Do *NOT* remove the two ending whitespaces in the following line.)
-[//]: # (They are needed for the Markdown text to render correctly.)
+<!-- (Do *NOT* remove the two ending whitespaces in the following line.) -->
+<!-- (They are needed for the Markdown text to render correctly.) -->
Roses are red [followed by two or more spaces]
Violets are blue
Sugar is sweet
-[//]: # (Do *NOT* remove the two ending whitespaces in the following line.)
-[//]: # (They are needed for the Markdown text to render correctly.)
+<!-- (Do *NOT* remove the two ending whitespaces in the following line.) -->
+<!-- (They are needed for the Markdown text to render correctly.) -->
Roses are red
Violets are blue
@@ -57,7 +109,9 @@ Sugar is sweet
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiple-underscores-in-words
-It is not reasonable to italicize just _part_ of a word, especially when you're dealing with code and names that often appear with multiple underscores. Therefore, GFM ignores multiple underscores in words:
+It is not reasonable to italicize just _part_ of a word, especially when you're
+dealing with code and names that often appear with multiple underscores.
+Therefore, GFM ignores multiple underscores in words:
perform_complicated_task
@@ -84,8 +138,8 @@ GFM will autolink almost any URL you copy and paste into your text:
* https://www.google.com
* https://google.com/
* ftp://ftp.us.debian.org/debian/
-* smb://foo/bar/baz
-* irc://irc.freenode.net/gitlab
+* <a href="smb://foo/bar/baz">smb://foo/bar/baz</a>
+* <a href="irc://irc.freenode.net/gitlab">irc://irc.freenode.net/gitlab</a>
* http://localhost:3000
### Multiline Blockquote
@@ -96,7 +150,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multili
On top of standard Markdown [blockquotes](#blockquotes), which require prepending `>` to quoted lines,
GFM supports multiline blockquotes fenced by <code>>>></code>:
-```no-highlight
+```
>>>
If you paste a message from somewhere else
@@ -110,17 +164,13 @@ you can quote that without having to manually prepend `>` to every line!
>>>
```
->>>
-If you paste a message from somewhere else
-
-that
-
-spans
-
-multiple lines,
-
-you can quote that without having to manually prepend `>` to every line!
->>>
+<blockquote dir="auto">
+<p>If you paste a message from somewhere else</p>
+<p>that</p>
+<p>spans</p>
+<p>multiple lines,</p>
+<p>you can quote that without having to manually prepend <code>&gt;</code> to every line!</p>
+</blockquote>
### Code and Syntax Highlighting
@@ -134,7 +184,7 @@ Blocks of code are either fenced by lines with three back-ticks <code>```</code>
or are indented with four spaces. Only the fenced code blocks support syntax
highlighting:
-```no-highlight
+```
Inline `code` has `back-ticks around` it.
```
@@ -224,29 +274,40 @@ However the wrapping tags cannot be mixed as such:
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
- Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
+```
+Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
- :zap: You can use emoji anywhere GFM is supported. :v:
+:zap: You can use emoji anywhere GFM is supported. :v:
- You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
+You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
- If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
+If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
- Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
- Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
-Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
+On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
-:zap: You can use emoji anywhere GFM is supported. :v:
+Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
+```
-You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
-If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
+Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px">. Well we have a gift for you:
-Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/zap.png" width="20px" height="20px">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/v.png" width="20px" height="20px">
+
+You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px"> you for that.
-Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px">. All you need to do is to look up one of the supported codes.
+
+Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px">
+
+Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
+
+On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+
+Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
### Special GitLab References
@@ -309,7 +370,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#task-li
You can add task lists to issues, merge requests and comments. To create a task list, add a specially-formatted Markdown list, like so:
-```no-highlight
+```
- [x] Completed task
- [ ] Incomplete task
- [ ] Sub-task 1
@@ -317,25 +378,18 @@ You can add task lists to issues, merge requests and comments. To create a task
- [ ] Sub-task 3
```
-- [x] Completed task
-- [ ] Incomplete task
- - [ ] Sub-task 1
- - [x] Sub-task 2
- - [ ] Sub-task 3
+![alt unordered-check-list-render-gfm](img/unordered_check_list_render_gfm.png)
Tasks formatted as ordered lists are supported as well:
-```no-highlight
+```
1. [x] Completed task
1. [ ] Incomplete task
1. [ ] Sub-task 1
1. [x] Sub-task 2
```
-1. [x] Completed task
-1. [ ] Incomplete task
- 1. [ ] Sub-task 1
- 1. [x] Sub-task 2
+![alt task-list-ordered-render-gfm](img/task_list_ordered_render_gfm.png)
Task lists can only be created in descriptions, not in titles. Task item state can be managed by editing the description's Markdown or by toggling the rendered check boxes.
@@ -354,7 +408,10 @@ The valid video extensions are `.mp4`, `.m4v`, `.mov`, `.webm`, and `.ogv`.
Here's a sample video:
-![Sample Video](img/markdown_video.mp4)
+<div class="video-container">
+ <video src="img/markdown_video.mp4" width="400" controls="true" data-setup="{}" data-title="Sample Video"></video>
+ <p><a href="img/markdown_video.mp4" target="_blank" rel="noopener noreferrer" title="Download 'Sample Video'">Sample Video</a></p>
+</div>
### Math
@@ -378,12 +435,11 @@ Example:
Becomes:
-This math is inline $`a^2+b^2=c^2`$.
+This math is inline ![alt text](img/math_inline_sup_render_gfm.png).
This is on a separate line
-```math
-a^2+b^2=c^2
-```
+
+<img src="./img/math_inline_sup_render_gfm.png" >
_Be advised that KaTeX only supports a [subset][katex-subset] of LaTeX._
@@ -411,17 +467,9 @@ Examples:
`HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.7)`
-Become:
+Becomes:
-`#F00`
-`#F00A`
-`#FF0000`
-`#FF0000AA`
-`RGB(0,255,0)`
-`RGB(0%,100%,0%)`
-`RGBA(0,255,0,0.7)`
-`HSL(540,70%,50%)`
-`HSLA(540,70%,50%,0.7)`
+![alt color-inline-colorchip-render-gfm](img/color_inline_colorchip_render_gfm.png)
#### Supported formats:
@@ -433,7 +481,7 @@ Become:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107) in
GitLab 10.3.
-
+>
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid
@@ -453,13 +501,7 @@ Example:
Becomes:
-```mermaid
-graph TD;
- A-->B;
- A-->C;
- B-->D;
- C-->D;
-```
+<img src="./img/mermaid_diagram_render_gfm.png" width="200px" height="400px">
For details see the [Mermaid official page][mermaid].
@@ -467,7 +509,7 @@ For details see the [Mermaid official page][mermaid].
### Headers
-```no-highlight
+```
# H1
## H2
### H3
@@ -525,7 +567,7 @@ Note that the Emoji processing happens before the header IDs are generated, so t
Examples:
-```no-highlight
+```
Emphasis, aka italics, with *asterisks* or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__.
@@ -535,7 +577,7 @@ Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~
```
-Become:
+Becomes:
Emphasis, aka italics, with *asterisks* or _underscores_.
@@ -549,7 +591,7 @@ Strikethrough uses two tildes. ~~Scratch this.~~
Examples:
-```no-highlight
+```
1. First ordered list item
2. Another item
* Unordered sub-list.
@@ -562,7 +604,7 @@ Examples:
+ Or pluses
```
-Become:
+Becomes:
1. First ordered list item
2. Another item
@@ -580,7 +622,7 @@ each subsequent paragraph should be indented to the same level as the start of t
Example:
-```no-highlight
+```
1. First ordered list item
Second paragraph of first item.
@@ -601,7 +643,7 @@ the paragraph will appear outside the list, instead of properly indented under t
Example:
-```no-highlight
+```
1. First ordered list item
Paragraph of first item.
@@ -661,7 +703,7 @@ Examples:
[logo]: img/markdown_logo.png
-Become:
+Becomes:
Here's our logo:
@@ -679,7 +721,7 @@ Reference-style:
Examples:
-```no-highlight
+```
> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
@@ -688,7 +730,7 @@ Quote break.
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
```
-Become:
+Becomes:
> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
@@ -705,7 +747,7 @@ See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubyd
Examples:
-```no-highlight
+```
<dl>
<dt>Definition list</dt>
<dd>Is something people use sometimes.</dd>
@@ -715,7 +757,7 @@ Examples:
</dl>
```
-Become:
+Becomes:
<dl>
<dt>Definition list</dt>
@@ -740,7 +782,7 @@ These details <em>will</em> remain <strong>hidden</strong> until expanded.
</details>
</p>
-**Note:** Markdown inside these tags is supported, as long as you have a blank link after the `</summary>` tag and before the `</details>` tag, as shown in the example. _Redcarpet does not support Markdown inside these tags. You can work around this by using HTML, for example you can use `<pre><code>` tags instead of [code fences](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting)._
+**Note:** Markdown inside these tags is supported, as long as you have a blank line after the `</summary>` tag and before the `</details>` tag, as shown in the example. _Redcarpet does not support Markdown inside these tags. You can work around this by using HTML, for example you can use `<pre><code>` tags instead of [code fences](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting)._
```html
<details>
@@ -773,7 +815,7 @@ ___
Underscores
```
-Become:
+Becomes:
Three or more...
@@ -811,7 +853,7 @@ This line is *on its own line*, because the previous line ends with two spaces.
spaces.
```
-Become:
+Becomes:
Here's a line for us to start with.
@@ -968,8 +1010,9 @@ A link starting with a `/` is relative to the wiki root.
## References
- This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
-- The [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
-- [Dillinger.io](http://dillinger.io) is a handy tool for testing standard markdown.
+- The original [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
+- The detailed specification for CommonMark can be found in the [CommonMark Spec][commonmark-spec]
+- The [CommonMark Dingus](http://try.commonmark.org) is a handy tool for testing CommonMark syntax.
[^1]: This link will be broken if you see this document from the Help page or docs.gitlab.com
[^2]: This is my awesome footnote.
@@ -982,3 +1025,4 @@ A link starting with a `/` is relative to the wiki root.
[katex-subset]: https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX "Macros supported by KaTeX"
[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual"
[commonmarker]: https://github.com/gjtorikian/commonmarker
+[commonmark-spec]: https://spec.commonmark.org/current/
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index b6438397db8..1fd230a41aa 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -42,11 +42,14 @@ The following table depicts the various user permission levels in a project.
| See a job log | ✓ [^3] | ✓ | ✓ | ✓ | ✓ |
| Download and browse job artifacts | ✓ [^3] | ✓ | ✓ | ✓ | ✓ |
| View wiki pages | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
+| View license management reports **[ULTIMATE]** | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
+| View Security reports **[ULTIMATE]** | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Pull project code | [^1] | ✓ | ✓ | ✓ | ✓ |
| Download project | [^1] | ✓ | ✓ | ✓ | ✓ |
| Assign issues | | ✓ | ✓ | ✓ | ✓ |
| Assign merge requests | | | ✓ | ✓ | ✓ |
-| Label issues and merge requests | | ✓ | ✓ | ✓ | ✓ |
+| Label issues | | ✓ | ✓ | ✓ | ✓ |
+| Label merge requests | | | ✓ | ✓ | ✓ |
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
@@ -56,6 +59,7 @@ The following table depicts the various user permission levels in a project.
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
| Manage related issues **[STARTER]** | | ✓ | ✓ | ✓ | ✓ |
| Lock issue discussions | | ✓ | ✓ | ✓ | ✓ |
+| Create issue from vulnerability **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
| Lock merge request discussions | | | ✓ | ✓ | ✓ |
| Create new environments | | | ✓ | ✓ | ✓ |
| Stop environments | | | ✓ | ✓ | ✓ |
@@ -72,6 +76,9 @@ The following table depicts the various user permission levels in a project.
| Update a container registry | | | ✓ | ✓ | ✓ |
| Remove a container registry image | | | ✓ | ✓ | ✓ |
| Create/edit/delete project milestones | | | ✓ | ✓ | ✓ |
+| View approved/blacklisted licenses **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
+| Use security dashboard **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
+| Dismiss vulnerability **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
| Use environment terminals | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ |
@@ -88,7 +95,9 @@ The following table depicts the various user permission levels in a project.
| Manage GitLab Pages | | | | ✓ | ✓ |
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
| Remove GitLab Pages | | | | | ✓ |
+| View GitLab Pages protected by [access control](../administration/pages/index.md#access-control) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Manage clusters | | | | ✓ | ✓ |
+| Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ |
| Transfer project to another namespace | | | | | ✓ |
@@ -97,7 +106,7 @@ The following table depicts the various user permission levels in a project.
| Remove pages | | | | | ✓ |
| Force push to protected branches [^4] | | | | | |
| Remove protected branches [^4] | | | | | |
-| View project Audit Events | | | | ✓ | ✓ |
+| View project Audit Events | | | | ✓ | ✓ |
## Project features permissions
@@ -109,6 +118,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level
+- Everyone: enabled for everyone (only available for GitLab Pages)
### Protected branches
@@ -206,6 +216,21 @@ will find the option to flag the user as external.
By default new users are not set as external users. This behavior can be changed
by an administrator under **Admin > Application Settings**.
+### Default internal users
+
+The "Internal users" field allows specifying an e-mail address regex pattern to identify default internal users.
+
+New users whose email address matches the regex pattern will be set to internal by default rather than an external collaborator.
+
+The regex pattern format is Ruby, but it needs to be convertible to JavaScript, and the ignore case flag will be set, e.g. "/regex pattern/i".
+
+Here are some examples:
+
+- Use `\.internal@domain\.com` to mark email addresses containing ".internal@domain.com" internal.
+- Use `^(?:(?!\.ext@domain\.com).)*$\r?` to mark users with email addresses NOT including .ext@domain.com internal.
+
+Please be aware that this regex could lead to a DOS attack, [see](https://en.wikipedia.org/wiki/ReDoS?) ReDos on Wikipedia.
+
## Auditor users **[PREMIUM ONLY]**
>[Introduced][ee-998] in [GitLab Premium][eep] 8.17.
@@ -226,6 +251,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level
+- Everyone: enabled for everyone (only available for GitLab Pages)
## GitLab CI/CD permissions
diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md
index 910bd20f882..49f0ce2cd79 100644
--- a/doc/user/profile/account/delete_account.md
+++ b/doc/user/profile/account/delete_account.md
@@ -1,5 +1,8 @@
# Deleting a User Account
+NOTE: **Note:**
+Deleting a user will delete all projects in that user namespace.
+
- As a user, you can delete your own account by navigating to **Settings** > **Account** and selecting **Delete account**
- As an admin, you can delete a user account by navigating to the **Admin Area**, selecting the **Users** tab, selecting a user, and clicking on **Delete user**
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index e25e1e19b13..76f7e869ff7 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -2,18 +2,18 @@
Two-factor Authentication (2FA) provides an additional level of security to your
GitLab account. Once enabled, in addition to supplying your username and
-password to login, you'll be prompted for a code generated by an application on
-your phone.
+password to login, you'll be prompted for a code generated by your one time password
+authenticator. For example, a password manager on one of your devices.
By enabling 2FA, the only way someone other than you can log into your account
-is to know your username and password *and* have access to your phone.
+is to know your username and password *and* have access to your one time password secret.
## Overview
> **Note:**
When you enable 2FA, don't forget to back up your recovery codes.
-In addition to a phone application, GitLab supports U2F (universal 2nd factor) devices as
+In addition to one time authenticators (TOTP), GitLab supports U2F (universal 2nd factor) devices as
the second factor of authentication. Once enabled, in addition to supplying your username and
password to login, you'll be prompted to activate your U2F device (usually by pressing
a button on it), and it will perform secure authentication on your behalf.
@@ -24,10 +24,10 @@ from other browsers.
## Enabling 2FA
-There are two ways to enable two-factor authentication: via a mobile application
+There are two ways to enable two-factor authentication: via a one time password authenticator
or a U2F device.
-### Enable 2FA via mobile application
+### Enable 2FA via one time password authenticator
**In GitLab:**
@@ -59,8 +59,8 @@ of recovery codes.
### Enable 2FA via U2F device
> **Notes:**
-- GitLab officially only supports [Yubikey] U2F devices.
-- Support for U2F devices was added in GitLab 8.8.
+> - GitLab officially only supports [Yubikey] U2F devices.
+> - Support for U2F devices was added in GitLab 8.8.
**In GitLab:**
@@ -69,7 +69,7 @@ of recovery codes.
1. Go to **Account**.
1. Click **Enable Two-Factor Authentication**.
1. Plug in your U2F device.
-1. Click on **Setup New U2F Device**.
+1. Click on **Set up New U2F Device**.
1. A light will start blinking on your device. Activate it by pressing its button.
You will see a message indicating that your device was successfully set up.
@@ -82,10 +82,12 @@ Click on **Register U2F Device** to complete the process.
> **Note:**
Recovery codes are not generated for U2F devices.
-Should you ever lose access to your phone, you can use one of the ten provided
-backup codes to login to your account. We suggest copying or printing them for
-storage in a safe place. **Each code can be used only once** to log in to your
-account.
+Should you ever lose access to your one time password authenticator, you can use one of the ten provided
+backup codes to login to your account. We suggest copying them, printing them, or downloading them using
+the **Download codes** button for storage in a safe place.
+
+CAUTION: **Caution:**
+Each code can be used only once to log in to your account.
If you lose the recovery codes or just want to generate new ones, you can do so
[using SSH](#generate-new-recovery-codes-using-ssh).
@@ -98,7 +100,7 @@ be presented with a second prompt, depending on which type of 2FA you've enabled
### Log in via mobile application
-Enter the pin from your phone's application or a recovery code to log in.
+Enter the pin from your one time password authenticator's application or a recovery code to log in.
![Two-Factor Authentication on sign in via OTP](img/2fa_auth.png)
@@ -145,7 +147,7 @@ codes. If you saved these codes, you can use one of them to sign in.
To use a recovery code, enter your username/email and password on the GitLab
sign-in page. When prompted for a two-factor code, enter the recovery code.
->**Note:**
+> **Note:**
Once you use a recovery code, you cannot re-use it. You can still use the other
recovery codes you saved.
@@ -156,7 +158,7 @@ authentication. If an SSH key is added to your GitLab account, you can generate
a new set of recovery codes with SSH.
1. Run `ssh git@gitlab.example.com 2fa_recovery_codes`.
-2. You are prompted to confirm that you want to generate new codes. Continuing this process invalidates previously saved codes.
+1. You are prompted to confirm that you want to generate new codes. Continuing this process invalidates previously saved codes.
```
bash
$ ssh git@gitlab.example.com 2fa_recovery_codes
@@ -183,11 +185,11 @@ a new set of recovery codes with SSH.
so you do not lose access to your account again.
```
-3. Go to the GitLab sign-in page and enter your username/email and password.
+1. Go to the GitLab sign-in page and enter your username/email and password.
When prompted for a two-factor code, enter one of the recovery codes obtained
from the command-line output.
->**Note:**
+> **Note:**
After signing in, visit your **Profile settings > Account** immediately to set
up two-factor authentication with a new device.
diff --git a/doc/user/profile/img/active_sessions_list.png b/doc/user/profile/img/active_sessions_list.png
index 76a52220bcd..5d94dca69cc 100644
--- a/doc/user/profile/img/active_sessions_list.png
+++ b/doc/user/profile/img/active_sessions_list.png
Binary files differ
diff --git a/doc/user/profile/img/personal_access_tokens.png b/doc/user/profile/img/personal_access_tokens.png
index 6aa63dbe342..d29f4cb0a20 100644
--- a/doc/user/profile/img/personal_access_tokens.png
+++ b/doc/user/profile/img/personal_access_tokens.png
Binary files differ
diff --git a/doc/user/profile/img/profil-preferences-navigation-theme.png b/doc/user/profile/img/profil-preferences-navigation-theme.png
index 7adaec33b60..335a19ac290 100644
--- a/doc/user/profile/img/profil-preferences-navigation-theme.png
+++ b/doc/user/profile/img/profil-preferences-navigation-theme.png
Binary files differ
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index b1b822f25bd..2f989a26725 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -31,12 +31,14 @@ From there, you can:
- Update your personal information
- Set a [custom status](#current-status) for your profile
+- Manage your [commit email](#commit-email) for your profile
- Manage [2FA](account/two_factor_authentication.md)
- Change your username and [delete your account](account/delete_account.md)
- Manage applications that can
[use GitLab as an OAuth provider](../../integration/oauth_provider.md#introduction-to-oauth)
- Manage [personal access tokens](personal_access_tokens.md) to access your account via API and authorized applications
- Add and delete emails linked to your account
+- Choose which email to use for notifications, web-based commits, and display on your public profile
- Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH
- Manage your [preferences](preferences.md#syntax-highlighting-theme)
to customize your own GitLab experience
@@ -91,6 +93,18 @@ To enable private profile:
NOTE: **Note:**
You and GitLab admins can see your the abovementioned information on your profile even if it is private.
+## Private contributions
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/14078) in GitLab 11.3.
+
+Enabling private contributions will include contributions to private projects, in the user contribution calendar graph and user recent activity.
+
+To enable private contributions:
+
+1. Navigate to your personal [profile settings](#profile-settings).
+1. Check the "Private contributions" option.
+1. Hit **Update profile settings**.
+
## Current status
> Introduced in GitLab 11.2.
@@ -102,6 +116,13 @@ Please be aware that your status is publicly visible even if your [profile is pr
To set your current status:
+1. Open the user menu in the top-right corner of the navigation bar.
+1. Hit **Set status**, or **Edit status** if you have already set a status.
+1. Set the emoji and/or status message to your liking.
+1. Hit **Set status**. Alternatively, you can also hit **Remove status** to remove your user status entirely.
+
+or
+
1. Navigate to your personal [profile settings](#profile-settings).
1. In the text field below `Your status`, enter your status message.
1. Select an emoji from the dropdown if you like.
@@ -112,6 +133,45 @@ They may however contain emoji codes such as `I'm on vacation :palm_tree:`.
You can also set your current status [using the API](../../api/users.md#user-status).
+## Commit email
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21598) in GitLab 11.4.
+
+A commit email, is the email that will be displayed in every Git-related action done through the
+GitLab interface.
+
+You are able to select from the list of your own verified emails which email you want to use as the commit email.
+
+To change it:
+
+1. Open the user menu in the top-right corner of the navigation bar.
+1. Hit **Commit email** selection box.
+1. Select any of the verified emails.
+1. Hit **Update profile settings**.
+
+### Private commit email
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22560) in GitLab 11.5.
+
+GitLab provides the user with an automatically generated private commit email option,
+which allows the user to not make their email information public.
+
+To enable this option:
+
+1. Open the user menu in the top-right corner of the navigation bar.
+1. Hit **Commit email** selection box.
+1. Select **Use a private email** option.
+1. Hit **Update profile settings**.
+
+Once this option is enabled, every Git-related action will be performed using the private commit email.
+
+In order to stay fully annonymous, you can also copy this private commit email
+and configure it on your local machine using the following command:
+
+```
+git config --global user.email "YOUR_PRIVATE_COMMIT_EMAIL"
+```
+
## Troubleshooting
### Why do I keep getting signed out?
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index 25d6c34409c..7d55048c994 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -45,16 +45,14 @@ the following table.
| Scope | Description |
| ----- | ----------- |
|`read_user` | Allows access to the read-only endpoints under `/users`. Essentially, any of the `GET` requests in the [Users API][users] are allowed ([introduced][ce-5951] in GitLab 8.15). |
-| `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. |
-| `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). |
+| `api` | Grants complete access to the API and Container Registry (read/write) ([introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951) in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. |
+| `read_registry` | Allows to read (pull) [container registry] images if a project is private and authorization is required ([introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845) in GitLab 9.3). |
| `sudo` | Allows performing API actions as any user in the system (if the authenticated user is an admin) ([introduced][ce-14838] in GitLab 10.2). |
-| `read_repository` | Allows read-access to the repository through git clone. |
+| `read_repository` | Allows read-access (pull) to the repository through git clone. |
[2fa]: ../account/two_factor_authentication.md
[api]: ../../api/README.md
[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749
-[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
-[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845
[ce-14838]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14838
[container registry]: ../project/container_registry.md
[users]: ../../api/users.md
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index c4e59444ef7..19eb95099ce 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -17,7 +17,7 @@ If you find that you have to add the same badges to several projects, you may wa
To add a new badge to a project:
-1. Navigate to your project's **Settings > Badges**.
+1. Navigate to your project's **Settings > General > Badges**.
1. Under "Link", enter the URL that the badges should point to and under
"Badge image URL" the URL of the image that should be displayed.
1. Submit the badge by clicking the **Add badge** button.
@@ -39,7 +39,7 @@ project, consider adding them on the [project level](#project-badges) or use
To add a new badge to a group:
-1. Navigate to your group's **Settings > Project Badges**.
+1. Navigate to your group's **Settings > General > Badges**.
1. Under "Link", enter the URL that the badges should point to and under
"Badge image URL" the URL of the image that should be displayed.
1. Submit the badge by clicking the **Add badge** button.
diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md
index 4261293b06f..fead99c5e88 100644
--- a/doc/user/project/bulk_editing.md
+++ b/doc/user/project/bulk_editing.md
@@ -1,11 +1,10 @@
# Bulk editing issues and merge requests
->
-**Notes:**
-- A permission level of `Reporter` or higher is required in order to manage
-issues.
-- A permission level of `Developer` or higher is required in order to manage
-merge requests.
+> **Notes:**
+> - A permission level of `Reporter` or higher is required in order to manage
+> issues.
+> - A permission level of `Developer` or higher is required in order to manage
+> merge requests.
Attributes can be updated simultaneously across multiple issues or merge requests
by using the bulk editing feature.
diff --git a/doc/user/project/clusters/eks_and_gitlab/img/add_cluster.png b/doc/user/project/clusters/eks_and_gitlab/img/add_cluster.png
index 9a0559a19d4..94ec83f1514 100644
--- a/doc/user/project/clusters/eks_and_gitlab/img/add_cluster.png
+++ b/doc/user/project/clusters/eks_and_gitlab/img/add_cluster.png
Binary files differ
diff --git a/doc/user/project/clusters/eks_and_gitlab/img/create_dns.png b/doc/user/project/clusters/eks_and_gitlab/img/create_dns.png
index 657ab0d9fa9..61ed85e5cd9 100644
--- a/doc/user/project/clusters/eks_and_gitlab/img/create_dns.png
+++ b/doc/user/project/clusters/eks_and_gitlab/img/create_dns.png
Binary files differ
diff --git a/doc/user/project/clusters/eks_and_gitlab/img/create_project.png b/doc/user/project/clusters/eks_and_gitlab/img/create_project.png
index f3446131419..b02ab4b9064 100644
--- a/doc/user/project/clusters/eks_and_gitlab/img/create_project.png
+++ b/doc/user/project/clusters/eks_and_gitlab/img/create_project.png
Binary files differ
diff --git a/doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.png b/doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.png
index d6c3b1b3a94..0d9fcc838d9 100644
--- a/doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.png
+++ b/doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.png
Binary files differ
diff --git a/doc/user/project/clusters/eks_and_gitlab/img/environment.png b/doc/user/project/clusters/eks_and_gitlab/img/environment.png
index 77d711ba8f6..4714c447026 100644
--- a/doc/user/project/clusters/eks_and_gitlab/img/environment.png
+++ b/doc/user/project/clusters/eks_and_gitlab/img/environment.png
Binary files differ
diff --git a/doc/user/project/clusters/eks_and_gitlab/img/new_project.png b/doc/user/project/clusters/eks_and_gitlab/img/new_project.png
deleted file mode 100644
index d401c4ac2bf..00000000000
--- a/doc/user/project/clusters/eks_and_gitlab/img/new_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/clusters/eks_and_gitlab/img/pipeline.png b/doc/user/project/clusters/eks_and_gitlab/img/pipeline.png
index 5f9c9815c24..0eb00d0faa7 100644
--- a/doc/user/project/clusters/eks_and_gitlab/img/pipeline.png
+++ b/doc/user/project/clusters/eks_and_gitlab/img/pipeline.png
Binary files differ
diff --git a/doc/user/project/clusters/eks_and_gitlab/img/rbac.png b/doc/user/project/clusters/eks_and_gitlab/img/rbac.png
new file mode 100644
index 00000000000..517e4f7ca44
--- /dev/null
+++ b/doc/user/project/clusters/eks_and_gitlab/img/rbac.png
Binary files differ
diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md
index ec8467da14f..fa2ed21f980 100644
--- a/doc/user/project/clusters/eks_and_gitlab/index.md
+++ b/doc/user/project/clusters/eks_and_gitlab/index.md
@@ -1,72 +1,171 @@
----
-author: Joshua Lambert
-author_gitlab: joshlambert
-level: intermediate
-article_type: tutorial
-date: 2018-06-05
----
-
# Connecting and deploying to an Amazon EKS cluster
-## Introduction
+In this tutorial, we will show how to integrate an
+[Amazon EKS](https://aws.amazon.com/eks/) cluster with GitLab and begin
+deploying applications.
-In this tutorial, we will show how easy it is to integrate an [Amazon EKS](https://aws.amazon.com/eks/) cluster with GitLab, and begin deploying applications.
+## Introduction
For an end-to-end walkthrough we will:
-1. Start with a new project based on the sample Ruby on Rails template
-1. Integrate an EKS cluster
-1. Utilize [Auto DevOps](../../../../topics/autodevops/) to build, test, and deploy our application
+
+1. Start with a new project based on the sample Ruby on Rails template.
+1. Integrate an EKS cluster.
+1. Utilize [Auto DevOps](../../../../topics/autodevops/) to build, test, and deploy our application.
You will need:
-1. An account on GitLab, like [GitLab.com](https://gitlab.com)
-1. An Amazon EKS cluster
-1. `kubectl` [installed and configured for access to the EKS cluster](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html#get-started-kubectl)
-If you don't have an Amazon EKS cluster, one can be created by following [the EKS getting started guide](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html).
+1. An account on GitLab, like [GitLab.com](https://gitlab.com).
+1. An Amazon EKS cluster (with worker nodes properly configured).
+1. `kubectl` [installed and configured for access to the EKS cluster](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html#get-started-kubectl).
-## Creating a new project
+If you don't have an Amazon EKS cluster, one can be created by following the
+[EKS getting started guide](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html).
-On GitLab, create a new project by clicking on the `+` icon in the top navigation bar, and selecting `New project`.
+## Creating a new project
-![New Project](img/new_project.png)
+On GitLab, create a new project by clicking on the `+` icon in the top navigation
+bar and selecting **New project**.
-On the new project screen, click on the `Create from template` tab, and select `Use template` for the Ruby on Rails sample project.
+On the new project screen, click on the **Create from template** tab, and select
+"Use template" for the Ruby on Rails sample project.
-Give the project a name, and then select `Create project`.
+Give the project a name, and then select **Create project**.
![Create Project](img/create_project.png)
-## Connecting the EKS cluster
+## Configuring and connecting the EKS cluster
+
+From the left side bar, hover over **Operations > Kubernetes > Add Kubernetes cluster**,
+then click **Add an existing Kubernetes cluster**.
+
+A few details from the EKS cluster will be required to connect it to GitLab:
+
+1. **Retrieve the certificate**: A valid Kubernetes certificate is needed to
+ authenticate to the EKS cluster. We will use the certificate created by default.
+ Open a shell and use `kubectl` to retrieve it:
+
+ - List the secrets with `kubectl get secrets`, and one should named similar to
+ `default-token-xxxxx`. Copy that token name for use below.
+ - Get the certificate with:
+
+ ```sh
+ kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 -D
+ ```
+
+1. **Create admin token**: A `cluster-admin` token is required to install and
+ manage Helm Tiller. GitLab establishes mutual SSL auth with Helm Tiller
+ and creates limited service accounts for each application. To create the
+ token we will create an admin service account as follows:
+
+ 2.1. Create a file called `eks-admin-service-account.yaml` with contents:
+
+ ```yaml
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: eks-admin
+ namespace: kube-system
+ ```
+
+ 2.2. Apply the service account to your cluster:
+
+ ```bash
+ kubectl apply -f eks-admin-service-account.yaml
+ ```
+
+ Output:
-From the left side bar, hover over `Operations` and select `Kubernetes`, then click on `Add Kubernetes cluster`, and finally `Add an existing Kubernetes cluster`.
+ ```bash
+ serviceaccount "eks-admin" created
+ ```
-A few details from the EKS cluster will be required to connect it to GitLab.
+ 2.3. Create a file called `eks-admin-cluster-role-binding.yaml` with contents:
-1. A valid Kubernetes certificate and token are needed to authenticate to the EKS cluster. A pair is created by default, which can be used. Open a shell and use `kubectl` to retrieve them:
- * List the secrets with `kubectl get secrets`, and one should named similar to `default-token-xxxxx`. Copy that token name for use below.
- * Get the certificate with `kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 -D`
- * Retrieve the token with `kubectl get secret <secret name> -o jsonpath="{['data']['token']}" | base64 -D`.
-1. The API server endpoint is also required, so GitLab can connect to the cluster. This is displayed on the AWS EKS console, when viewing the EKS cluster details.
+ ```yaml
+ apiVersion: rbac.authorization.k8s.io/v1beta1
+ kind: ClusterRoleBinding
+ metadata:
+ name: eks-admin
+ roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+ subjects:
+ - kind: ServiceAccount
+ name: eks-admin
+ namespace: kube-system
+ ```
+
+ 2.4. Apply the cluster role binding to your cluster:
+
+ ```bash
+ kubectl apply -f eks-admin-cluster-role-binding.yaml
+ ```
+
+ Output:
+
+ ```bash
+ clusterrolebinding "eks-admin" created
+ ```
+
+ 2.5. Retrieve the token for the `eks-admin` service account:
+
+ ```bash
+ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep eks-admin | awk '{print $1}')
+ ```
+
+ Copy the `<authentication_token>` value from the output:
+
+ ```yaml
+ Name: eks-admin-token-b5zv4
+ Namespace: kube-system
+ Labels: <none>
+ Annotations: kubernetes.io/service-account.name=eks-admin
+ kubernetes.io/service-account.uid=bcfe66ac-39be-11e8-97e8-026dce96b6e8
+
+ Type: kubernetes.io/service-account-token
+
+ Data
+ ====
+ ca.crt: 1025 bytes
+ namespace: 11 bytes
+ token: <authentication_token>
+ ```
+
+1. The API server endpoint is also required, so GitLab can connect to the cluster.
+ This is displayed on the AWS EKS console, when viewing the EKS cluster details.
You now have all the information needed to connect the EKS cluster:
-* Kubernetes cluster name: Provide a name for the cluster to identify it within GitLab.
-* Environment scope: Leave this as `*` for now, since we are only connecting a single cluster.
-* API URL: Paste in the API server endpoint retrieved above.
-* CA Certificate: Paste the certificate data from the earlier step, as-is.
-* Paste the token value. Note on some versions of Kubernetes a trailing `%` is output, do not include it.
-* Project namespace: This can be left blank to accept the default namespace, based on the project name.
+
+- Kubernetes cluster name: Provide a name for the cluster to identify it within GitLab.
+- Environment scope: Leave this as `*` for now, since we are only connecting a single cluster.
+- API URL: Paste in the API server endpoint retrieved above.
+- CA Certificate: Paste the certificate data from the earlier step, as-is.
+- Paste the admin token value.
+- Project namespace: This can be left blank to accept the default namespace, based on the project name.
![Add Cluster](img/add_cluster.png)
-Click on `Add Kubernetes cluster`, the cluster is now connected to GitLab. At this point, [Kubernetes deployment variables](../#deployment-variables) will automatically be available during CI jobs, making it easy to interact with the cluster.
+Click on **Add Kubernetes cluster**, the cluster is now connected to GitLab.
+At this point, [Kubernetes deployment variables](../#deployment-variables) will
+automatically be available during CI/CD jobs, making it easy to interact with the cluster.
If you would like to utilize your own CI/CD scripts to deploy to the cluster, you can stop here.
-## Disable Role-Based Access Control (RBAC)
+## Disable Role-Based Access Control (RBAC) (optional)
+
+When connecting a cluster via GitLab integration, you may specify whether the
+cluster is RBAC-enabled or not. This will affect how GitLab interacts with the
+cluster for certain operations. If you **did not** check the "RBAC-enabled cluster"
+checkbox at creation time, GitLab will assume RBAC is disabled for your cluster
+when interacting with it. If so, you must disable RBAC on your cluster for the
+integration to work properly.
-Presently, Auto DevOps and one-click app installs do not support [Kubernetes role-based access control](https://kubernetes.io/docs/reference/access-authn-authz/rbac/). Support is [being worked on](https://gitlab.com/groups/gitlab-org/-/epics/136), but in the interim RBAC must be disabled to utilize for these features.
+![rbac](img/rbac.png)
-> **Note**: Disabling RBAC means that any application running in the cluster, or user who can authenticate to the cluster, has full API access. This is a [security concern](https://docs.gitlab.com/ee/user/project/clusters/#security-implications), and may not be desirable.
+NOTE: **Note**: Disabling RBAC means that any application running in the cluster,
+or user who can authenticate to the cluster, has full API access. This is a
+[security concern](../index.md#security-implications), and may not be desirable.
To effectively disable RBAC, global permissions can be applied granting full access:
@@ -80,56 +179,100 @@ kubectl create clusterrolebinding permissive-binding \
## Deploy services to the cluster
-GitLab supports one-click deployment of helpful services to the cluster, many of which support Auto DevOps. Back on the Kubernetes cluster screen in GitLab, a list of applications is now available to deploy.
+GitLab supports one-click deployment of helpful services to the cluster, many of
+which support Auto DevOps. Back on the Kubernetes cluster screen in GitLab, a
+list of applications is now available to deploy.
-First install Helm Tiller, a package manager for Kubernetes. This enables deployment of the other applications.
+First, install Helm Tiller, a package manager for Kubernetes. This enables
+deployment of the other applications.
![Deploy Apps](img/deploy_apps.png)
### Deploying NGINX Ingress (optional)
-Next, if you would like the deployed app to be reachable on the internet, deploy the Ingress. Note that this will also cause an [Elastic Load Balancer](https://aws.amazon.com/documentation/elastic-load-balancing/) to be created, which will incur additional AWS costs.
+Next, if you would like the deployed app to be reachable on the internet, deploy
+the Ingress. Note that this will also cause an
+[Elastic Load Balancer](https://aws.amazon.com/documentation/elastic-load-balancing/)
+to be created, which will incur additional AWS costs.
-Once installed, you may see a `?` for `Ingress IP Address`. This is because the created ELB is available at a DNS name, not an IP address. To get the DNS name, run: `kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}"`. Note, you may see a trailing `%` on some Kubernetes versions, do not include it.
+Once installed, you may see a `?` for "Ingress IP Address". This is because the
+created ELB is available at a DNS name, not an IP address. To get the DNS name,
+run:
-The Ingress is now available at this address, and will route incoming requests to the proper service based on the DNS name in the request. To support this, a wildcard DNS CNAME record should be created for the desired domain name. For example `*.myekscluster.com` would point to the Ingress hostname obtained earlier.
+```sh
+kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}"
+```
+
+Note that you may see a trailing `%` on some Kubernetes versions, **do not include it**.
+
+The Ingress is now available at this address and will route incoming requests to
+the proper service based on the DNS name in the request. To support this, a
+wildcard DNS CNAME record should be created for the desired domain name. For example,
+`*.myekscluster.com` would point to the Ingress hostname obtained earlier.
![Create DNS](img/create_dns.png)
### Deploying the GitLab Runner (optional)
-If the project is on GitLab.com, free shared runners are available and you do not have to deploy one. If a project specific runner is desired, or there are no shared runners, it is easy to deploy one.
+If the project is on GitLab.com, free shared Runners are available and you do
+not have to deploy one. If a project specific Runner is desired, or there are no
+shared Runners, it is easy to deploy one.
-Simply click on the `Install` button for the GitLab Runner. It is important to note that the runner deployed is set as **privileged**, which means it essentially has root access to the underlying machine. This is required to build docker images, and so is on by default.
+Simply click on the **Install** button for the GitLab Runner. It is important to
+note that the Runner deployed is set as **privileged**, which means it essentially
+has root access to the underlying machine. This is required to build docker images,
+and so is on by default.
### Deploying Prometheus (optional)
-GitLab is able to monitor applications automatically, utilizing [Prometheus](../../integrations/prometheus.html). Kubernetes container CPU and memory metrics are automatically collected, and response metrics are retrieved from NGINX Ingress as well.
+GitLab is able to monitor applications automatically, utilizing
+[Prometheus](../../integrations/prometheus.html). Kubernetes container CPU and
+memory metrics are automatically collected, and response metrics are retrieved
+from NGINX Ingress as well.
-To enable monitoring, simply install Prometheus into the cluster with the `Install` button.
+To enable monitoring, simply install Prometheus into the cluster with the
+**Install** button.
## Create a default Storage Class
-Amazon EKS does not have a default Storage Class out of the box, which means requests for persistent volumes will not be automatically fulfilled. As part of Auto DevOps, the deployed Postgres instance requests persistent storage, and without a default storage class it will fail to start.
+Amazon EKS doesn't have a default Storage Class out of the box, which means
+requests for persistent volumes will not be automatically fulfilled. As part
+of Auto DevOps, the deployed Postgres instance requests persistent storage,
+and without a default storage class it will fail to start.
-If a default Storage Class does not already exist and is desired, follow Amazon's [short guide](https://docs.aws.amazon.com/eks/latest/userguide/storage-classes.html) to create one.
+If a default Storage Class doesn't already exist and is desired, follow Amazon's
+[guide on storage classes](https://docs.aws.amazon.com/eks/latest/userguide/storage-classes.html)
+to create one.
-Alternatively, disable Postgres by setting the project variable [`POSTGRES_ENABLED`](../../../../topics/autodevops/#environment-variables) to `false`.
+Alternatively, disable Postgres by setting the project variable
+[`POSTGRES_ENABLED`](../../../../topics/autodevops/#environment-variables) to `false`.
## Deploy the app to EKS
-With RBAC disabled and services deployed, [Auto DevOps](https://docs.gitlab.com/ee/topics/autodevops/) can now be leveraged to build, test, and deploy the app. To enable, click on `Settings` in the left sidebar, then `CI/CD`. You will see a section for `Auto DevOps`, expand it. Click on the radio button to `Enable Auto DevOps`.
+With RBAC disabled and services deployed,
+[Auto DevOps](../../../../topics/autodevops/index.md) can now be leveraged
+to build, test, and deploy the app.
-If a wildcard DNS entry was created resolving to the Load Balancer, enter it in the `domain` field. Otherwise, the deployed app will not be externally available outside of the cluster. To save, click `Save changes`.
+[Enable Auto DevOps](../../../../topics/autodevops/index.md##enablingdisabling-auto-devops-at-the-project-level)
+if not already enabled. If a wildcard DNS entry was created resolving to the
+Load Balancer, enter it in the `domain` field under the Auto DevOps settings.
+Otherwise, the deployed app will not be externally available outside of the cluster.
![Deploy Pipeline](img/pipeline.png)
-A new pipeline will automatically be created, which will begin to build, test, and deploy the app.
+A new pipeline will automatically be created, which will begin to build, test,
+and deploy the app.
-After the pipeline has finished, your app will be running in EKS and available to users. Click on `CI/CD` tab in the left navigation bar, and choose `Environments`.
+After the pipeline has finished, your app will be running in EKS and available
+to users. Click on **CI/CD > Environments**.
![Deployed Environment](img/environment.png)
-You will see a list of the environments and their deploy status, as well as options to browse to the app, view monitoring metrics, and even access a shell on the running pod.
+You will see a list of the environments and their deploy status, as well as
+options to browse to the app, view monitoring metrics, and even access a shell
+on the running pod.
+
+## Learn more
-To learn more about Auto DevOps, review our [documentation](../../../../topics/autodevops/).
+To learn more on automatically deploying your applications,
+read about [Auto DevOps](../../../../topics/autodevops/index.md).
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 7c552103412..ca262e4b76e 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -49,21 +49,21 @@ new Kubernetes cluster to your project:
NOTE: **Note:**
You need Maintainer [permissions] and above to access the Kubernetes page.
-1. Click on **Add Kubernetes cluster**.
-1. Click on **Create with Google Kubernetes Engine**.
+1. Click **Add Kubernetes cluster**.
+1. Click **Create with Google Kubernetes Engine**.
1. Connect your Google account if you haven't done already by clicking the
**Sign in with Google** button.
1. From there on, choose your cluster's settings:
- - **Kubernetes cluster name** - The name you wish to give the cluster.
- - **Environment scope** - The [associated environment](#setting-the-environment-scope) to this cluster.
- - **Google Cloud Platform project** - Choose the project you created in your GCP
- console that will host the Kubernetes cluster. Learn more about
- [Google Cloud Platform projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects).
- - **Zone** - Choose the [region zone](https://cloud.google.com/compute/docs/regions-zones/)
- under which the cluster will be created.
- - **Number of nodes** - Enter the number of nodes you wish the cluster to have.
- - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
- of the Virtual Machine instance that the cluster will be based on.
+ - **Kubernetes cluster name** - The name you wish to give the cluster.
+ - **Environment scope** - The [associated environment](#setting-the-environment-scope) to this cluster.
+ - **Google Cloud Platform project** - Choose the project you created in your GCP
+ console that will host the Kubernetes cluster. Learn more about
+ [Google Cloud Platform projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects).
+ - **Zone** - Choose the [region zone](https://cloud.google.com/compute/docs/regions-zones/)
+ under which the cluster will be created.
+ - **Number of nodes** - Enter the number of nodes you wish the cluster to have.
+ - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
+ of the Virtual Machine instance that the cluster will be based on.
1. Finally, click the **Create Kubernetes cluster** button.
After a couple of minutes, your cluster will be ready to go. You can now proceed
@@ -78,8 +78,8 @@ To add an existing Kubernetes cluster to your project:
NOTE: **Note:**
You need Maintainer [permissions] and above to access the Kubernetes page.
-1. Click on **Add Kubernetes cluster**.
-1. Click on **Add an existing Kubernetes cluster** and fill in the details:
+1. Click **Add Kubernetes cluster**.
+1. Click **Add an existing Kubernetes cluster** and fill in the details:
- **Kubernetes cluster name** (required) - The name you wish to give the cluster.
- **Environment scope** (required)- The
[associated environment](#setting-the-environment-scope) to this cluster.
@@ -113,6 +113,14 @@ To add an existing Kubernetes cluster to your project:
After a couple of minutes, your cluster will be ready to go. You can now proceed
to install some [pre-defined applications](#installing-applications).
+To determine the:
+
+- API URL, run `kubectl cluster-info | grep 'Kubernetes master' | awk '/http/ {print $NF}'`.
+- Token:
+ 1. List the secrets by running: `kubectl get secrets`. Note the name of the secret you need the token for.
+ 1. Get the token for the appropriate secret by running: `kubectl get secret <SECRET_NAME> -o jsonpath="{['data']['token']}" | base64 -D`.
+- CA certificate, run `kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 -D`.
+
## Security implications
CAUTION: **Important:**
@@ -124,11 +132,63 @@ functionalities needed to successfully build and deploy a containerized
application. Bare in mind that the same credentials are used for all the
applications running on the cluster.
-When GitLab creates the cluster, it enables and uses the legacy
-[Attribute-based access control (ABAC)](https://kubernetes.io/docs/admin/authorization/abac/).
-The newer [RBAC](https://kubernetes.io/docs/admin/authorization/rbac/)
-authorization will be supported in a
-[future release](https://gitlab.com/gitlab-org/gitlab-ce/issues/29398).
+## Access controls
+
+When creating a cluster in GitLab, you will be asked if you would like to create an
+[Attribute-based access control (ABAC)](https://kubernetes.io/docs/admin/authorization/abac/) cluster, or
+a [Role-based access control (RBAC)](https://kubernetes.io/docs/admin/authorization/rbac/) one.
+
+Whether ABAC or RBAC is enabled, GitLab will create the necessary
+service accounts and privileges in order to install and run
+[GitLab managed applications](#installing-applications):
+
+- A `gitlab` service account with `cluster-admin` privileges will be created in the
+ `default` namespace, which will be used by GitLab to manage the newly created cluster.
+
+- A project service account with [`edit`
+ privileges](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)
+ will be created in the project namespace (also created by GitLab), which will
+ be used in [deployment jobs](#deployment-variables).
+
+ NOTE: **Note:**
+ Restricted service account for deployment was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/51716) in GitLab 11.5.
+
+- When you install Helm Tiller into your cluster, the `tiller` service account
+ will be created with `cluster-admin` privileges in the `gitlab-managed-apps`
+ namespace. This service account will be added to the installed Helm Tiller and will
+ be used by Helm to install and run [GitLab managed applications](#installing-applications).
+ Helm Tiller will also create additional service accounts and other resources for each
+ installed application. Consult the documentation of the Helm charts for each application
+ for details.
+
+If you are [adding an existing Kubernetes cluster](#adding-an-existing-kubernetes-cluster),
+ensure the token of the account has administrator privileges for the cluster.
+
+The following sections summarize which resources will be created on ABAC/RBAC clusters.
+
+### Attribute-based access control (ABAC)
+
+| Name | Kind | Details | Created when |
+| --- | --- | --- | --- |
+| `gitlab` | `ServiceAccount` | `default` namespace | Creating a new GKE Cluster |
+| `gitlab-token` | `Secret` | Token for `gitlab` ServiceAccount | Creating a new GKE Cluster |
+| `tiller` | `ServiceAccount` | `gitlab-managed-apps` namespace | Installing Helm Tiller |
+| `tiller-admin` | `ClusterRoleBinding` | `cluster-admin` roleRef | Installing Helm Tiller |
+| Project namespace | `ServiceAccount` | Uses namespace of Project | Creating/Adding a new GKE Cluster |
+| Project namespace | `Secret` | Token for project ServiceAccount | Creating/Adding a new GKE Cluster |
+
+### Role-based access control (RBAC)
+
+| Name | Kind | Details | Created when |
+| --- | --- | --- | --- |
+| `gitlab` | `ServiceAccount` | `default` namespace | Creating a new GKE Cluster |
+| `gitlab-admin` | `ClusterRoleBinding` | [`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) roleRef | Creating a new GKE Cluster |
+| `gitlab-token` | `Secret` | Token for `gitlab` ServiceAccount | Creating a new GKE Cluster |
+| `tiller` | `ServiceAccount` | `gitlab-managed-apps` namespace | Installing Helm Tiller |
+| `tiller-admin` | `ClusterRoleBinding` | `cluster-admin` roleRef | Installing Helm Tiller |
+| Project namespace | `ServiceAccount` | Uses namespace of Project | Creating/Adding a new GKE Cluster |
+| Project namespace | `Secret` | Token for project ServiceAccount | Creating/Adding a new GKE Cluster |
+| Project namespace | `RoleBinding` | [`edit`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) roleRef | Creating/Adding a new GKE Cluster |
### Security of GitLab Runners
@@ -155,19 +215,24 @@ added directly to your configured cluster. Those applications are needed for
[Review Apps](../../../ci/review_apps/index.md) and [deployments](../../../ci/environments.md).
NOTE: **Note:**
-The applications will be installed in a dedicated namespace called
+With the exception of Knative, the applications will be installed in a dedicated namespace called
`gitlab-managed-apps`. In case you have added an existing Kubernetes cluster
with Tiller already installed, you should be careful as GitLab cannot
detect it. By installing it via the applications will result into having it
twice, which can lead to confusion during deployments.
-| Application | GitLab version | Description |
-| ----------- | :------------: | ----------- |
-| [Helm Tiller](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. |
-| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. |
-| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. |
-| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. |
-| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. |
+| Application | GitLab version | Description | Helm Chart |
+| ----------- | :------------: | ----------- | --------------- |
+| [Helm Tiller](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. | n/a |
+| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) |
+| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) |
+| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
+| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. You will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix). More information on creating executable runbooks can be found at [Nurtch Documentation](http://docs.nurtch.com/en/latest). **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
+| [Knative](https://cloud.google.com/knative) | 11.5+ | Knative provides a platform to create, deploy, and manage serverless workloads from a Kubernetes cluster. It is used in conjunction with, and includes [Istio](https://istio.io) to provide an external IP address for all programs hosted by Knative. You will be prompted to enter a wildcard domain where your applications will be exposed. Configure your DNS server to use the external IP address for that domain. For any application created and installed, they will be accessible as `<program_name>.<kubernetes_namespace>.<domain_name>`. This will require your kubernetes cluster to have [RBAC enabled](#role-based-access-control-rbac). | [knative/knative](https://storage.googleapis.com/triggermesh-charts)
+
+NOTE: **Note:**
+As of GitLab 11.6 Helm Tiller will be upgraded to the latest version supported
+by GitLab before installing any of the above applications.
## Getting the external IP address
@@ -176,6 +241,10 @@ You need a load balancer installed in your cluster in order to obtain the
external IP address with the following procedure. It can be deployed using the
[**Ingress** application](#installing-applications).
+NOTE: **Note:**
+Knative will include its own load balancer in the form of [Istio](https://istio.io).
+At this time, to determine the external IP address, you will need to follow the manual approach.
+
In order to publish your web application, you first need to find the external IP
address associated to your load balancer.
@@ -190,14 +259,17 @@ your ingress application in which case you should manually determine it.
### Manually determining the IP address
-If the cluster is on GKE, click on the **Google Kubernetes Engine** link in the
+If the cluster is on GKE, click the **Google Kubernetes Engine** link in the
**Advanced settings**, or go directly to the
[Google Kubernetes Engine dashboard](https://console.cloud.google.com/kubernetes/)
-and select the proper project and cluster. Then click on **Connect** and execute
+and select the proper project and cluster. Then click **Connect** and execute
the `gcloud` command in a local terminal or using the **Cloud Shell**.
If the cluster is not on GKE, follow the specific instructions for your
Kubernetes provider to configure `kubectl` with the right credentials.
+The output of the following examples will show the external IP address of your
+cluster. This information can then be used to set up DNS entries and forwarding
+rules that allow external access to your deployed applications.
If you installed the Ingress [via the **Applications**](#installing-applications),
run the following command:
@@ -206,20 +278,23 @@ run the following command:
kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
```
-Otherwise, you can list the IP addresses of all load balancers:
+For Istio/Knative, the command will be different:
```bash
-kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalancer.ingress)]}{.status.loadBalancer.ingress[*].ip} '
+kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
+```
+
+Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
+
+```bash
+kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}".
```
-> **Note**: Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
-> ```bash
-> kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}".
-> ```
+Otherwise, you can list the IP addresses of all load balancers:
-The output is the external IP address of your cluster. This information can then
-be used to set up DNS entries and forwarding rules that allow external access to
-your deployed applications.
+```bash
+kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalancer.ingress)]}{.status.loadBalancer.ingress[*].ip} '
+```
### Using a static IP
@@ -229,7 +304,7 @@ your apps will not be able to be reached, and you'd have to change the DNS
record again. In order to avoid that, you should change it into a static
reserved IP.
-[Read how to promote an ephemeral external IP address in GKE.](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip)
+Read how to [promote an ephemeral external IP address in GKE](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip).
### Pointing your DNS at the cluster IP
@@ -320,18 +395,22 @@ GitLab CI/CD build environment.
| Variable | Description |
| -------- | ----------- |
| `KUBE_URL` | Equal to the API URL. |
-| `KUBE_TOKEN` | The Kubernetes token. |
+| `KUBE_TOKEN` | The Kubernetes token of the [project service account](#access-controls). |
| `KUBE_NAMESPACE` | The Kubernetes namespace is auto-generated if not specified. The default value is `<project_name>-<project_id>`. You can overwrite it to use different one if needed, otherwise the `KUBE_NAMESPACE` variable will receive the default value. |
-| `KUBE_CA_PEM_FILE` | Only present if a custom CA bundle was specified. Path to a file containing PEM data. |
-| `KUBE_CA_PEM` | (**deprecated**) Only if a custom CA bundle was specified. Raw PEM data. |
+| `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. |
+| `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. |
| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. |
+NOTE: **NOTE:**
+Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main
+service account of the cluster integration.
+
## Enabling or disabling the Kubernetes cluster integration
After you have successfully added your cluster information, you can enable the
Kubernetes cluster integration:
-1. Click the "Enabled/Disabled" switch
+1. Click the **Enabled/Disabled** switch
1. Hit **Save** for the changes to take effect
You can now start using your Kubernetes cluster for your deployments.
@@ -348,7 +427,7 @@ When you remove a cluster, you only remove its relation to GitLab, not the
cluster itself. To remove the cluster, you can do so by visiting the GKE
dashboard or using `kubectl`.
-To remove the Kubernetes cluster integration from your project, simply click on the
+To remove the Kubernetes cluster integration from your project, simply click the
**Remove integration** button. You will then be able to follow the procedure
and add a Kubernetes cluster again.
@@ -411,7 +490,13 @@ the deployment variables above, ensuring any pods you create are labelled with
## Read more
-- [Connecting and deploying to an Amazon EKS cluster](eks_and_gitlab/index.md)
+### Integrating Amazon EKS cluster with GitLab
+
+- Learn how to [connect and deploy to an Amazon EKS cluster](eks_and_gitlab/index.md).
+
+### Serverless
+
+- [Run serverless workloads on Kubernetes with Knative.](serverless/index.md)
[permissions]: ../../permissions.md
[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/user/project/clusters/runbooks/img/authorize-jupyter.png b/doc/user/project/clusters/runbooks/img/authorize-jupyter.png
new file mode 100644
index 00000000000..64f95ed45f0
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/img/authorize-jupyter.png
Binary files differ
diff --git a/doc/user/project/clusters/runbooks/img/demo-runbook.png b/doc/user/project/clusters/runbooks/img/demo-runbook.png
new file mode 100644
index 00000000000..25c9df4126d
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/img/demo-runbook.png
Binary files differ
diff --git a/doc/user/project/clusters/runbooks/img/gitlab-variables.png b/doc/user/project/clusters/runbooks/img/gitlab-variables.png
new file mode 100644
index 00000000000..f76ed21145f
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/img/gitlab-variables.png
Binary files differ
diff --git a/doc/user/project/clusters/runbooks/img/helm-install.png b/doc/user/project/clusters/runbooks/img/helm-install.png
new file mode 100644
index 00000000000..e39094bcbf7
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/img/helm-install.png
Binary files differ
diff --git a/doc/user/project/clusters/runbooks/img/ingress-install.png b/doc/user/project/clusters/runbooks/img/ingress-install.png
new file mode 100644
index 00000000000..093c61f2d0e
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/img/ingress-install.png
Binary files differ
diff --git a/doc/user/project/clusters/runbooks/img/jupyterhub-install.png b/doc/user/project/clusters/runbooks/img/jupyterhub-install.png
new file mode 100644
index 00000000000..2115ec9745b
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/img/jupyterhub-install.png
Binary files differ
diff --git a/doc/user/project/clusters/runbooks/img/postgres-query.png b/doc/user/project/clusters/runbooks/img/postgres-query.png
new file mode 100644
index 00000000000..3880438c97a
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/img/postgres-query.png
Binary files differ
diff --git a/doc/user/project/clusters/runbooks/img/sample-runbook.png b/doc/user/project/clusters/runbooks/img/sample-runbook.png
new file mode 100644
index 00000000000..c12ce8990a4
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/img/sample-runbook.png
Binary files differ
diff --git a/doc/user/project/clusters/runbooks/index.md b/doc/user/project/clusters/runbooks/index.md
new file mode 100644
index 00000000000..e1b8dc07b50
--- /dev/null
+++ b/doc/user/project/clusters/runbooks/index.md
@@ -0,0 +1,137 @@
+# Runbooks
+
+Runbooks are a collection of documented procedures that explain how to
+carry out a particular process, be it starting, stopping, debugging,
+or troubleshooting a particular system.
+
+## Overview
+
+Historically, runbooks took the form of a decision tree or a detailed
+step-by-step guide depending on the condition or system.
+
+Modern implementations have introduced the concept of an "executable
+runbooks", where, along with a well-defined process, operators can execute
+pre-written code blocks or database queries against a given environment.
+
+## Nurtch Executable Runbooks
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/45912) in GitLab 11.4.
+
+The JupyterHub app offered via GitLab’s Kubernetes integration now ships
+with Nurtch’s Rubix library, providing a simple way to create DevOps
+runbooks. A sample runbook is provided, showcasing common operations.
+
+**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+Watch this [video](https://www.youtube.com/watch?v=Q_OqHIIUPjE)
+for an overview of how this is acomplished in GitLab!**
+
+## Requirements
+
+To create an executable runbook, you will need:
+
+1. **Kubernetes** - A Kubernetes cluster is required to deploy the rest of the applications.
+ The simplest way to get started is to add a cluster using [GitLab's GKE integration](https://docs.gitlab.com/ee/user/project/clusters/#adding-and-creating-a-new-gke-cluster-via-gitlab).
+1. **Helm Tiller** - Helm is a package manager for Kubernetes and is required to install
+ all the other applications. It is installed in its own pod inside the cluster which
+ can run the helm CLI in a safe environment.
+1. **Ingress** - Ingress can provide load balancing, SSL termination, and name-based
+ virtual hosting. It acts as a web proxy for your applications.
+1. **JupyterHub** - JupyterHub is a multi-user service for managing notebooks across
+ a team. Jupyter Notebooks provide a web-based interactive programming environment
+ used for data analysis, visualization, and machine learning.
+
+## Nurtch
+
+Nurtch is the company behind the [Rubix library](https://github.com/Nurtch/rubix). Rubix is
+an open-source python library that makes it easy to perform common DevOps tasks inside Jupyter Notebooks.
+Tasks such as plotting Cloudwatch metrics and rolling your ECS/Kubernetes app are simplified
+down to a couple of lines of code. See the [Nurtch Documentation](http://docs.nurtch.com/en/latest)
+for more information.
+
+## Configure an executable runbook with GitLab
+
+Follow this step-by-step guide to configure an executable runbook in GitLab using
+the components outlined above and the preloaded demo runbook.
+
+### 1. Add a Kubernetes cluster
+
+Follow the steps outlined in [Adding and creating a new GKE cluster via GitLab](https://docs.gitlab.com/ee/user/project/clusters/#adding-and-creating-a-new-gke-cluster-via-gitlab)
+to add a Kubernetes cluster to your project.
+
+### 2. Install Helm Tiller, Ingress, and JupyterHub
+
+Once the cluster has been provisioned in GKE, click the **Install** button next to the **Helm Tiller** app.
+
+![install helm](img/helm-install.png)
+
+Once Tiller has been installed successfully, click the **Install** button next to the **Ingress** app.
+
+![install ingress](img/ingress-install.png)
+
+Once Ingress has been installed successfully, click the **Install** button next to the **JupyterHub** app.
+
+![install jupyterhub](img/jupyterhub-install.png)
+
+### 3. Login to JupyterHub and start the server
+
+Once JupyterHub has been installed successfully, navigate to the displayed **Jupyter Hostname** URL and click
+**Sign in with GitLab**. Authentication is automatically enabled for any user of the GitLab instance via OAuth2. This
+will redirect to GitLab in order to authorize JupyterHub to use your GitLab account. Click **Authorize**.
+
+![authorize jupyter](img/authorize-jupyter.png)
+
+Once the application has been authorized you will taken back to the JupyterHub application. Click **Start My Server**.
+The server will take a couple of seconds to start.
+
+### 4. Configure access
+
+In order for the runbook to access your GitLab project, you will need to enter a
+[GitLab Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)
+as well as your Project ID in the **Setup** section of the demo runbook.
+
+Double-click the **DevOps-Runbook-Demo** folder located on the left panel.
+
+![demo runbook](img/demo-runbook.png)
+
+Double-click the "Nurtch-DevOps-Demo.ipynb" runbook.
+
+![sample runbook](img/sample-runbook.png)
+
+The contents on the runbook will be displayed on the right side of the screen. Under the "Setup" section, you will find
+entries for both your `PRIVATE_TOKEN` and your `PROJECT_ID`. Enter both these values, conserving the single quotes as follows:
+
+```sql
+PRIVATE_TOKEN = 'n671WNGecHugsdEDPsyo'
+PROJECT_ID = '1234567'
+```
+
+Update the `VARIABLE_NAME` on the last line of this section to match the name of the variable you are using for your
+access token. In this example our variable name is `PRIVATE_TOKEN`.
+
+```sql
+VARIABLE_VALUE = project.variables.get('PRIVATE_TOKEN').value
+```
+
+### 5. Configure an operation
+
+For this example we'll use the "**Run SQL queries in Notebook**" section in the sample runbook to query
+a postgres database. The first 4 lines of the section define the variables that are required for this query to function.
+
+```sql
+%env DB_USER={project.variables.get('DB_USER').value}
+%env DB_PASSWORD={project.variables.get('DB_PASSWORD').value}
+%env DB_ENDPOINT={project.variables.get('DB_ENDPOINT').value}
+%env DB_NAME={project.variables.get('DB_NAME').value}
+```
+
+Create the matching variables in your project's **Settings >> CI/CD >> Variables**
+
+![gitlab variables](img/gitlab-variables.png)
+
+Back in Jupyter, click the "Run SQL queries in Notebook" heading and the click *Run*. The results will be
+displayed in-line as follows:
+
+![postgres query](img/postgres-query.png)
+
+You can try other operations such as running shell scripts or interacting with a Kubernetes cluster. Visit the
+[Nurtch Documentation](http://docs.nurtch.com/) for more information. \ No newline at end of file
diff --git a/doc/user/project/clusters/serverless/img/deploy-stage.png b/doc/user/project/clusters/serverless/img/deploy-stage.png
new file mode 100644
index 00000000000..dc2f8af9c63
--- /dev/null
+++ b/doc/user/project/clusters/serverless/img/deploy-stage.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/dns-entry.png b/doc/user/project/clusters/serverless/img/dns-entry.png
new file mode 100644
index 00000000000..2e7655c6041
--- /dev/null
+++ b/doc/user/project/clusters/serverless/img/dns-entry.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/install-knative.png b/doc/user/project/clusters/serverless/img/install-knative.png
new file mode 100644
index 00000000000..dd576a9df35
--- /dev/null
+++ b/doc/user/project/clusters/serverless/img/install-knative.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/knative-app.png b/doc/user/project/clusters/serverless/img/knative-app.png
new file mode 100644
index 00000000000..54301e1786f
--- /dev/null
+++ b/doc/user/project/clusters/serverless/img/knative-app.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
new file mode 100644
index 00000000000..bdbc4f7f09d
--- /dev/null
+++ b/doc/user/project/clusters/serverless/index.md
@@ -0,0 +1,137 @@
+# Serverless
+
+> Introduced in GitLab 11.5.
+
+Run serverless workloads on Kubernetes using [Knative](https://cloud.google.com/knative/).
+
+## Overview
+
+Knative extends Kubernetes to provide a set of middleware components that are useful to build modern, source-centric, container-based applications. Knative brings some significant benefits out of the box through its main components:
+
+- [Build:](https://github.com/knative/build) Source-to-container build orchestration
+- [Eventing:](https://github.com/knative/eventing) Management and delivery of events
+- [Serving:](https://github.com/knative/serving) Request-driven compute that can scale to zero
+
+For more information on Knative, visit the [Knative docs repo](https://github.com/knative/docs).
+
+## Requirements
+
+To run Knative on Gitlab, you will need:
+
+1. **Kubernetes:** An RBAC-enabled Kubernetes cluster is required to deploy Knative.
+ The simplest way to get started is to add a cluster using [GitLab's GKE integration](https://docs.gitlab.com/ee/user/project/clusters/#adding-and-creating-a-new-gke-cluster-via-gitlab).
+ GitLab recommends
+1. **Helm Tiller:** Helm is a package manager for Kubernetes and is required to install
+ all the other applications.
+1. **Domain Name:** Knative will provide its own load balancer using Istio. It will provide an
+ external IP address for all the applications served by Knative. You will be prompted to enter a
+ wildcard domain where your applications will be served. Configure your DNS server to use the
+ external IP address for that domain.
+1. **Serverless `gitlab-ci.yml` Template:** GitLab uses [Kaniko](https://github.com/GoogleContainerTools/kaniko)
+ to build the application and the [TriggerMesh CLI](https://github.com/triggermesh/tm), to simplify the
+ deployment of knative services and functions.
+
+ Add the following `.gitlab-ci.yml` to the root of your repository (you may skip this step if using the sample
+ [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) mentioned below).
+
+ ```yaml
+ stages:
+ - build
+ - deploy
+
+ build:
+ stage: build
+ image:
+ name: gcr.io/kaniko-project/executor:debug
+ entrypoint: [""]
+ only:
+ - master
+ script:
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
+ - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE
+
+ deploy:
+ stage: deploy
+ image: gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5
+ only:
+ - master
+ environment: production
+ script:
+ - echo "$CI_REGISTRY_IMAGE"
+ - tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait
+ ```
+
+1. **Dockerfile:** Knative requires a Dockerfile in order to build your application. It should be included
+ at the root of your project's repo and expose port 8080.
+
+## Installing Knative via GitLab's Kubernetes integration
+
+NOTE: **Note:**
+Minimum recommended cluster size to run Knative is 3-nodes, 6 vCPUs, and 22.50 GB memory. RBAC must be enabled.
+
+You may download the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
+
+1. [Add a Kubernetes cluster](https://docs.gitlab.com/ce/user/project/clusters/) and install Helm.
+
+1. Once Helm has been successfully installed, on the Knative app section, enter the domain to be used with
+ your application and click "Install".
+
+ ![install-knative](img/install-knative.png)
+
+1. After the Knative installation has finished, retrieve the Istio Ingress IP address by running the following command:
+
+ ```bash
+ kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
+ ```
+
+ Output:
+
+ ```bash
+ 35.161.143.124 my-machine-name:~ my-user$
+ ```
+
+1. The ingress is now available at this address and will route incoming requests to the proper service based on the DNS
+ name in the request. To support this, a wildcard DNS A record should be created for the desired domain name. For example,
+ if your Knative base domain is `knative.example.com` then you need to create an A record with domain `*.knative.example.com`
+ pointing the ip address of the ingress.
+
+ ![dns entry](img/dns-entry.png)
+
+## Deploy the application with Knative
+
+With all the pieces in place, you can simply create a new CI pipeline to deploy the Knative application. Navigate to
+**CI/CD >> Pipelines** and click the **Run Pipeline** button at the upper-right part of the screen. Then, on the
+Pipelines page, click **Create pipeline**.
+
+## Obtain the URL for the Knative deployment
+
+Once all the stages of the pipeline finish, click the **deploy** stage.
+
+![deploy stage](img/deploy-stage.png)
+
+The output will look like this:
+
+```bash
+Running with gitlab-runner 11.5.0~beta.844.g96d88322 (96d88322)
+ on docker-auto-scale 72989761
+Using Docker executor with image gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ...
+Pulling docker image gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ...
+Using docker image sha256:6b3f6590a9b30bd7aafb9573f047d930c70066e43955b4beb18a1eee175f6de1 for gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ...
+Running on runner-72989761-project-4342902-concurrent-0 via runner-72989761-stg-srm-1541795796-27929c96...
+Cloning repository...
+Cloning into '/builds/danielgruesso/knative'...
+Checking out 8671ad20 as master...
+Skipping Git submodules setup
+$ echo "$CI_REGISTRY_IMAGE"
+registry.staging.gitlab.com/danielgruesso/knative
+$ tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait
+Deployment started. Run "tm -n knative-4342902 describe service knative" to see the details
+Waiting for ready state.......
+Service domain: knative.knative-4342902.knative.info
+Job succeeded
+```
+
+The second to last line, labeled **Service domain** contains the URL for the deployment. Copy and paste the domain into your
+browser to see the app live.
+
+![knative app](img/knative-app.png) \ No newline at end of file
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index 03302b3815d..cac64fc0cb6 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -1,16 +1,16 @@
# GitLab Container Registry
->**Notes:**
+> **Notes:**
> [Introduced][ce-4040] in GitLab 8.8.
-- Docker Registry manifest `v1` support was added in GitLab 8.9 to support Docker
- versions earlier than 1.10.
-- This document is about the user guide. To learn how to enable GitLab Container
- Registry across your GitLab instance, visit the
- [administrator documentation](../../administration/container_registry.md).
-- Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
- to pass a [personal access token][pat] instead of your password in order to
- login to GitLab's Container Registry.
-- Multiple level image names support was added in GitLab 9.1
+> - Docker Registry manifest `v1` support was added in GitLab 8.9 to support Docker
+> versions earlier than 1.10.
+> - This document is about the user guide. To learn how to enable GitLab Container
+> Registry across your GitLab instance, visit the
+> [administrator documentation](../../administration/container_registry.md).
+> - Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
+> to pass a [personal access token][pat] instead of your password in order to
+> login to GitLab's Container Registry.
+> - Multiple level image names support was added in GitLab 9.1
With the Docker Container Registry integrated into GitLab, every project can
have its own space to store its Docker images.
@@ -27,7 +27,7 @@ to enable it.
1. First, ask your system administrator to enable GitLab Container Registry
following the [administration documentation](../../administration/container_registry.md).
If you are using GitLab.com, this is enabled by default so you can start using
- the Registry immediately. Currently there is a soft (10GB) size restriction for
+ the Registry immediately. Currently there is a soft (10GB) size restriction for
registry on GitLab.com, as part of the [repository size limit](repository/index.html#repository-size).
1. Go to your [project's General settings](settings/index.md#sharing-and-permissions)
and enable the **Container Registry** feature on your project. For new
@@ -40,12 +40,12 @@ to enable it.
## Build and push images
->**Notes:**
-- Moving or renaming existing container registry repositories is not supported
-once you have pushed images because the images are signed, and the
-signature includes the repository name.
-- To move or rename a repository with a container registry you will have to
-delete all existing images.
+> **Notes:**
+> - Moving or renaming existing container registry repositories is not supported
+> once you have pushed images because the images are signed, and the
+> signature includes the repository name.
+> - To move or rename a repository with a container registry you will have to
+> delete all existing images.
If you visit the **Registry** link under your project's menu, you can see the
@@ -119,12 +119,17 @@ and [Using the GitLab Container Registry documentation](../../ci/docker/using_do
> Project Deploy Tokens were [introduced][ce-17894] in GitLab 10.7
If a project is private, credentials will need to be provided for authorization.
-The preferred way to do this, is either by using a [personal access tokens][pat] or a [project deploy token][pdt].
+There are two ways to do this:
+
+- By using a [personal access token](../profile/personal_access_tokens.md).
+- By using a [deploy token](../project/deploy_tokens/index.md).
+
The minimal scope needed for both of them is `read_registry`.
-Example of using a personal access token:
-```
-docker login registry.example.com -u <your_username> -p <your_access_token>
+Example of using a token:
+
+```sh
+docker login registry.example.com -u <username> -p <token>
```
## Troubleshooting the GitLab Container Registry
@@ -134,12 +139,12 @@ docker login registry.example.com -u <your_username> -p <your_access_token>
1. Check to make sure that the system clock on your Docker client and GitLab server have
been synchronized (e.g. via NTP).
-2. If you are using an S3-backed Registry, double check that the IAM
+1. If you are using an S3-backed Registry, double check that the IAM
permissions and the S3 credentials (including region) are correct. See [the
sample IAM policy](https://docs.docker.com/registry/storage-drivers/s3/)
for more details.
-3. Check the Registry logs (e.g. `/var/log/gitlab/registry/current`) and the GitLab production logs
+1. Check the Registry logs (e.g. `/var/log/gitlab/registry/current`) and the GitLab production logs
for errors (e.g. `/var/log/gitlab/gitlab-rails/production.log`). You may be able to find clues
there.
@@ -216,7 +221,7 @@ needs to trust the mitmproxy SSL certificates for this to work.
The following installation instructions assume you are running Ubuntu:
-1. Install mitmproxy (see http://docs.mitmproxy.org/en/stable/install.html)
+1. [Install mitmproxy](https://docs.mitmproxy.org/stable/overview-installation/).
1. Run `mitmproxy --port 9000` to generate its certificates.
Enter <kbd>CTRL</kbd>-<kbd>C</kbd> to quit.
1. Install the certificate from `~/.mitmproxy` to your system:
@@ -245,7 +250,7 @@ This will run mitmproxy on port `9000`. In another window, run:
curl --proxy http://localhost:9000 https://httpbin.org/status/200
```
-If everything is setup correctly, you will see information on the mitmproxy window and
+If everything is set up correctly, you will see information on the mitmproxy window and
no errors from the curl commands.
#### Running the Docker daemon with a proxy
@@ -293,4 +298,4 @@ Once the right permissions were set, the error will go away.
[docker-docs]: https://docs.docker.com/engine/userguide/intro/
[pat]: ../profile/personal_access_tokens.md
[pdt]: ../project/deploy_tokens/index.md
-[reconfigure]: ../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure \ No newline at end of file
+[reconfigure]: ../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index 8f6b530c033..ea843054f8e 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -72,7 +72,7 @@ Here's a little explanation of how this works behind the scenes:
`<issue, merge request>` pair, the merge request has the [issue closing pattern]
for the corresponding issue. All other issues and merge requests are **not**
considered.
-1. Then the <issue, merge request> pairs are filtered out by last XX days (specified
+1. Then the `<issue, merge request>` pairs are filtered out by last XX days (specified
by the UI - default is 90 days). So it prohibits these pairs from being considered.
1. For the remaining `<issue, merge request>` pairs, we check the information that
we need for the stages, like issue creation date, merge request merge time,
@@ -154,7 +154,7 @@ You can [read more about permissions][permissions] in general.
Learn more about Cycle Analytics in the following resources:
-- [Cycle Analytics feature page](https://about.gitlab.com/solutions/cycle-analytics/)
+- [Cycle Analytics feature page](https://about.gitlab.com/features/cycle-analytics/)
- [Cycle Analytics feature preview](https://about.gitlab.com/2016/09/16/feature-preview-introducing-cycle-analytics/)
- [Cycle Analytics feature highlight](https://about.gitlab.com/2016/09/21/cycle-analytics-feature-highlight/)
diff --git a/doc/user/project/deploy_tokens/img/deploy_tokens.png b/doc/user/project/deploy_tokens/img/deploy_tokens.png
index 7e2d67a3120..55c537fd1d3 100644
--- a/doc/user/project/deploy_tokens/img/deploy_tokens.png
+++ b/doc/user/project/deploy_tokens/img/deploy_tokens.png
Binary files differ
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 0b9b49f326f..7688508c6ac 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -9,12 +9,12 @@ at midnight UTC and that they can be only managed by [maintainers](https://docs.
## Creating a Deploy Token
-You can create as many deploy tokens as you like from the settings of your project:
+You can create as many deploy tokens as you like from the settings of your project:
1. Log in to your GitLab account.
1. Go to the project you want to create Deploy Tokens for.
-1. Go to **Settings** > **Repository**
-1. Click on "Expand" on **Deploy Tokens** section
+1. Go to **Settings** > **Repository**.
+1. Click on "Expand" on **Deploy Tokens** section.
1. Choose a name and optionally an expiry date for the token.
1. Choose the [desired scopes](#limiting-scopes-of-a-deploy-token).
1. Click on **Create deploy token**.
@@ -46,39 +46,46 @@ the following table.
To download a repository using a Deploy Token, you just need to:
1. Create a Deploy Token with `read_repository` as a scope.
-2. Take note of your `username` and `token`
-3. `git clone` the project using the Deploy Token:
+1. Take note of your `username` and `token`.
+1. `git clone` the project using the Deploy Token:
+ ```sh
+ git clone http://<username>:<deploy_token>@gitlab.example.com/tanuki/awesome_project.git
+ ```
-```bash
-git clone http://<username>:<deploy_token>@gitlab.example.com/tanuki/awesome_project.git
-```
-
-Just replace `<username>` and `<deploy_token>` with the proper values
+Replace `<username>` and `<deploy_token>` with the proper values.
-### Read container registry images
+### Read Container Registry images
To read the container registry images, you'll need to:
1. Create a Deploy Token with `read_registry` as a scope.
-2. Take note of your `username` and `token`
-3. Log in to GitLab’s Container Registry using the deploy token:
+1. Take note of your `username` and `token`.
+1. Log in to GitLab’s Container Registry using the deploy token:
-```
+```sh
docker login registry.example.com -u <username> -p <deploy_token>
```
-Just replace `<username>` and `<deploy_token>` with the proper values. Then you can simply
+Just replace `<username>` and `<deploy_token>` with the proper values. Then you can simply
pull images from your Container Registry.
### GitLab Deploy Token
> [Introduced][ce-18414] in GitLab 10.8.
-There's a special case when it comes to Deploy Tokens, if a user creates one
-named `gitlab-deploy-token`, the username and token of the Deploy Token will be
-automatically exposed to the CI/CD jobs as environment variables: `CI_DEPLOY_USER` and
-`CI_DEPLOY_PASSWORD`, respectively.
+There's a special case when it comes to Deploy Tokens. If a user creates one
+named `gitlab-deploy-token`, the username and token of the Deploy Token will be
+automatically exposed to the CI/CD jobs as environment variables: `CI_DEPLOY_USER` and
+`CI_DEPLOY_PASSWORD`, respectively. With the GitLab Deploy Token, the
+`read_registry` scope is implied.
+
+After you create the token, you can login to the Container Registry using
+those variables:
+
+```sh
+docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
+```
[ce-17894]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17894
[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845
diff --git a/doc/user/project/img/bulk-editing.png b/doc/user/project/img/bulk-editing.png
index f6b163f55d9..8ae649e5020 100644
--- a/doc/user/project/img/bulk-editing.png
+++ b/doc/user/project/img/bulk-editing.png
Binary files differ
diff --git a/doc/user/project/img/cycle_analytics_landing_page.png b/doc/user/project/img/cycle_analytics_landing_page.png
index 316612c0da0..8b17fae5e05 100644
--- a/doc/user/project/img/cycle_analytics_landing_page.png
+++ b/doc/user/project/img/cycle_analytics_landing_page.png
Binary files differ
diff --git a/doc/user/project/img/issue_board.png b/doc/user/project/img/issue_board.png
index 50e051e25a0..925b969eebe 100644
--- a/doc/user/project/img/issue_board.png
+++ b/doc/user/project/img/issue_board.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_assignee_lists.png b/doc/user/project/img/issue_board_assignee_lists.png
index 1ec94d22e33..f2660cd8f80 100644
--- a/doc/user/project/img/issue_board_assignee_lists.png
+++ b/doc/user/project/img/issue_board_assignee_lists.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_creation.png b/doc/user/project/img/issue_board_creation.png
index 9dc4925b0a5..099fe6eee21 100644
--- a/doc/user/project/img/issue_board_creation.png
+++ b/doc/user/project/img/issue_board_creation.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_edit_button.png b/doc/user/project/img/issue_board_edit_button.png
index 23883175344..a0dc6f41592 100644
--- a/doc/user/project/img/issue_board_edit_button.png
+++ b/doc/user/project/img/issue_board_edit_button.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_milestone_lists.png b/doc/user/project/img/issue_board_milestone_lists.png
new file mode 100644
index 00000000000..91926f58f87
--- /dev/null
+++ b/doc/user/project/img/issue_board_milestone_lists.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_move_issue_card_list.png b/doc/user/project/img/issue_board_move_issue_card_list.png
index cce252234c1..13750a63766 100644
--- a/doc/user/project/img/issue_board_move_issue_card_list.png
+++ b/doc/user/project/img/issue_board_move_issue_card_list.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_summed_weights.png b/doc/user/project/img/issue_board_summed_weights.png
new file mode 100644
index 00000000000..2288d767d8c
--- /dev/null
+++ b/doc/user/project/img/issue_board_summed_weights.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_view_scope.png b/doc/user/project/img/issue_board_view_scope.png
index 4e03cecbc2d..d173679a0e7 100644
--- a/doc/user/project/img/issue_board_view_scope.png
+++ b/doc/user/project/img/issue_board_view_scope.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_add_issues_modal.png b/doc/user/project/img/issue_boards_add_issues_modal.png
index 625a4304eaf..ecddf6709d0 100644
--- a/doc/user/project/img/issue_boards_add_issues_modal.png
+++ b/doc/user/project/img/issue_boards_add_issues_modal.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_core.png b/doc/user/project/img/issue_boards_core.png
new file mode 100644
index 00000000000..9e819160861
--- /dev/null
+++ b/doc/user/project/img/issue_boards_core.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_multiple.png b/doc/user/project/img/issue_boards_multiple.png
index 4b2b8d457f1..7bb088aad0b 100644
--- a/doc/user/project/img/issue_boards_multiple.png
+++ b/doc/user/project/img/issue_boards_multiple.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_premium.png b/doc/user/project/img/issue_boards_premium.png
new file mode 100644
index 00000000000..bd9164b2961
--- /dev/null
+++ b/doc/user/project/img/issue_boards_premium.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_remove_issue.png b/doc/user/project/img/issue_boards_remove_issue.png
index 9a2fad2cc7f..7050e6c3ede 100644
--- a/doc/user/project/img/issue_boards_remove_issue.png
+++ b/doc/user/project/img/issue_boards_remove_issue.png
Binary files differ
diff --git a/doc/user/project/img/koding_build-in-progress.png b/doc/user/project/img/koding_build-in-progress.png
deleted file mode 100644
index 79b7b2f10a2..00000000000
--- a/doc/user/project/img/koding_build-in-progress.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_build-logs.png b/doc/user/project/img/koding_build-logs.png
deleted file mode 100644
index b30c8375b20..00000000000
--- a/doc/user/project/img/koding_build-logs.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_build-success.png b/doc/user/project/img/koding_build-success.png
deleted file mode 100644
index a2342cfd324..00000000000
--- a/doc/user/project/img/koding_build-success.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_commit-koding.yml.png b/doc/user/project/img/koding_commit-koding.yml.png
deleted file mode 100644
index 16842410ae2..00000000000
--- a/doc/user/project/img/koding_commit-koding.yml.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_different-stack-on-mr-try.png b/doc/user/project/img/koding_different-stack-on-mr-try.png
deleted file mode 100644
index 10c7c51d2e6..00000000000
--- a/doc/user/project/img/koding_different-stack-on-mr-try.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_edit-on-ide.png b/doc/user/project/img/koding_edit-on-ide.png
deleted file mode 100644
index ab861281d3e..00000000000
--- a/doc/user/project/img/koding_edit-on-ide.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_enable-koding.png b/doc/user/project/img/koding_enable-koding.png
deleted file mode 100644
index 0b6fcfadcc5..00000000000
--- a/doc/user/project/img/koding_enable-koding.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_landing.png b/doc/user/project/img/koding_landing.png
deleted file mode 100644
index 1eeddcd3813..00000000000
--- a/doc/user/project/img/koding_landing.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_open-gitlab-from-koding.png b/doc/user/project/img/koding_open-gitlab-from-koding.png
deleted file mode 100644
index 4235a72b36f..00000000000
--- a/doc/user/project/img/koding_open-gitlab-from-koding.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_run-in-ide.png b/doc/user/project/img/koding_run-in-ide.png
deleted file mode 100644
index d22e5023c59..00000000000
--- a/doc/user/project/img/koding_run-in-ide.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_run-mr-in-ide.png b/doc/user/project/img/koding_run-mr-in-ide.png
deleted file mode 100644
index cb1112c4034..00000000000
--- a/doc/user/project/img/koding_run-mr-in-ide.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_set-up-ide.png b/doc/user/project/img/koding_set-up-ide.png
deleted file mode 100644
index 033d41729a2..00000000000
--- a/doc/user/project/img/koding_set-up-ide.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_stack-import.png b/doc/user/project/img/koding_stack-import.png
deleted file mode 100644
index 245ccb07ba3..00000000000
--- a/doc/user/project/img/koding_stack-import.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/koding_start-build.png b/doc/user/project/img/koding_start-build.png
deleted file mode 100644
index 3f5c16d5d2f..00000000000
--- a/doc/user/project/img/koding_start-build.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_generate_default.png b/doc/user/project/img/labels_generate_default.png
index fca2a06e04f..982a4df999c 100644
--- a/doc/user/project/img/labels_generate_default.png
+++ b/doc/user/project/img/labels_generate_default.png
Binary files differ
diff --git a/doc/user/project/img/labels_group_issues.png b/doc/user/project/img/labels_group_issues.png
index 29dcf7ff45e..cea1d304d31 100644
--- a/doc/user/project/img/labels_group_issues.png
+++ b/doc/user/project/img/labels_group_issues.png
Binary files differ
diff --git a/doc/user/project/img/labels_list.png b/doc/user/project/img/labels_list.png
index 12c47ea9766..6878349fc0c 100644
--- a/doc/user/project/img/labels_list.png
+++ b/doc/user/project/img/labels_list.png
Binary files differ
diff --git a/doc/user/project/img/labels_prioritized.png b/doc/user/project/img/labels_prioritized.png
index 57dcfe89b3d..7ce2d08b38c 100644
--- a/doc/user/project/img/labels_prioritized.png
+++ b/doc/user/project/img/labels_prioritized.png
Binary files differ
diff --git a/doc/user/project/img/labels_project_list_search.png b/doc/user/project/img/labels_project_list_search.png
index ff9bf92e1c3..512d7767e6e 100644
--- a/doc/user/project/img/labels_project_list_search.png
+++ b/doc/user/project/img/labels_project_list_search.png
Binary files differ
diff --git a/doc/user/project/img/labels_promotion.png b/doc/user/project/img/labels_promotion.png
index 8a5efd210a2..762a3773692 100644
--- a/doc/user/project/img/labels_promotion.png
+++ b/doc/user/project/img/labels_promotion.png
Binary files differ
diff --git a/doc/user/project/img/labels_sidebar.png b/doc/user/project/img/labels_sidebar.png
index 7349c6d4f0c..454a0ca3f07 100644
--- a/doc/user/project/img/labels_sidebar.png
+++ b/doc/user/project/img/labels_sidebar.png
Binary files differ
diff --git a/doc/user/project/img/labels_sidebar_assign.png b/doc/user/project/img/labels_sidebar_assign.png
index 61e8d04fc85..5b7fb78b032 100644
--- a/doc/user/project/img/labels_sidebar_assign.png
+++ b/doc/user/project/img/labels_sidebar_assign.png
Binary files differ
diff --git a/doc/user/project/img/labels_sidebar_inline.png b/doc/user/project/img/labels_sidebar_inline.png
index 31fa397761d..2186f14ea92 100644
--- a/doc/user/project/img/labels_sidebar_inline.png
+++ b/doc/user/project/img/labels_sidebar_inline.png
Binary files differ
diff --git a/doc/user/project/img/labels_sort_label_priority.png b/doc/user/project/img/labels_sort_label_priority.png
index c8b97639121..faf629ac61d 100644
--- a/doc/user/project/img/labels_sort_label_priority.png
+++ b/doc/user/project/img/labels_sort_label_priority.png
Binary files differ
diff --git a/doc/user/project/img/labels_sort_priority.png b/doc/user/project/img/labels_sort_priority.png
index a95198e7f72..a6b5fca26f4 100644
--- a/doc/user/project/img/labels_sort_priority.png
+++ b/doc/user/project/img/labels_sort_priority.png
Binary files differ
diff --git a/doc/user/project/img/labels_subscriptions.png b/doc/user/project/img/labels_subscriptions.png
index 8bcb3b57f6c..f3c4235d051 100644
--- a/doc/user/project/img/labels_subscriptions.png
+++ b/doc/user/project/img/labels_subscriptions.png
Binary files differ
diff --git a/doc/user/project/img/priority_sort_order.png b/doc/user/project/img/priority_sort_order.png
index c558ec23b0e..cd1dd8237c0 100644
--- a/doc/user/project/img/priority_sort_order.png
+++ b/doc/user/project/img/priority_sort_order.png
Binary files differ
diff --git a/doc/user/project/img/project_overview_badges.png b/doc/user/project/img/project_overview_badges.png
index 3067a7dfa13..83b9766828a 100644
--- a/doc/user/project/img/project_overview_badges.png
+++ b/doc/user/project/img/project_overview_badges.png
Binary files differ
diff --git a/doc/user/project/img/project_repository_settings.png b/doc/user/project/img/project_repository_settings.png
index aa4d4452c87..69d36753a58 100644
--- a/doc/user/project/img/project_repository_settings.png
+++ b/doc/user/project/img/project_repository_settings.png
Binary files differ
diff --git a/doc/user/project/img/protected_branches_delete.png b/doc/user/project/img/protected_branches_delete.png
index cfdfe6c6c29..8910ae9e39d 100644
--- a/doc/user/project/img/protected_branches_delete.png
+++ b/doc/user/project/img/protected_branches_delete.png
Binary files differ
diff --git a/doc/user/project/img/protected_branches_devs_can_push.png b/doc/user/project/img/protected_branches_devs_can_push.png
index 320e6eb7fee..b537839c00b 100644
--- a/doc/user/project/img/protected_branches_devs_can_push.png
+++ b/doc/user/project/img/protected_branches_devs_can_push.png
Binary files differ
diff --git a/doc/user/project/img/protected_branches_error_ui.png b/doc/user/project/img/protected_branches_error_ui.png
index 3f8e462d3ad..62839e49d89 100644
--- a/doc/user/project/img/protected_branches_error_ui.png
+++ b/doc/user/project/img/protected_branches_error_ui.png
Binary files differ
diff --git a/doc/user/project/img/protected_branches_list.png b/doc/user/project/img/protected_branches_list.png
index 1b2936cb711..495ce4d7b6f 100644
--- a/doc/user/project/img/protected_branches_list.png
+++ b/doc/user/project/img/protected_branches_list.png
Binary files differ
diff --git a/doc/user/project/img/protected_branches_page.png b/doc/user/project/img/protected_branches_page.png
index 4e5afff3bae..9b10991f62e 100644
--- a/doc/user/project/img/protected_branches_page.png
+++ b/doc/user/project/img/protected_branches_page.png
Binary files differ
diff --git a/doc/user/project/img/protected_tag_matches.png b/doc/user/project/img/protected_tag_matches.png
index a36a11a1271..e89d0a47073 100644
--- a/doc/user/project/img/protected_tag_matches.png
+++ b/doc/user/project/img/protected_tag_matches.png
Binary files differ
diff --git a/doc/user/project/img/protected_tags_list.png b/doc/user/project/img/protected_tags_list.png
index c5e42dc0705..6c5295e0f4b 100644
--- a/doc/user/project/img/protected_tags_list.png
+++ b/doc/user/project/img/protected_tags_list.png
Binary files differ
diff --git a/doc/user/project/img/protected_tags_page.png b/doc/user/project/img/protected_tags_page.png
index 3848d91ebd6..5f8a2106cd1 100644
--- a/doc/user/project/img/protected_tags_page.png
+++ b/doc/user/project/img/protected_tags_page.png
Binary files differ
diff --git a/doc/user/project/img/protected_tags_permissions_dropdown.png b/doc/user/project/img/protected_tags_permissions_dropdown.png
index 9e0fc4e2a43..77098eeb591 100644
--- a/doc/user/project/img/protected_tags_permissions_dropdown.png
+++ b/doc/user/project/img/protected_tags_permissions_dropdown.png
Binary files differ
diff --git a/doc/user/project/import/clearcase.md b/doc/user/project/import/clearcase.md
index f23623ed485..89a9f7da852 100644
--- a/doc/user/project/import/clearcase.md
+++ b/doc/user/project/import/clearcase.md
@@ -1,6 +1,6 @@
# Migrating from ClearCase
-[ClearCase](https://www-03.ibm.com/software/products/en/clearcase/) is a set of
+[ClearCase](https://www.ibm.com/us-en/marketplace/rational-clearcase) is a set of
tools developed by IBM which also include a centralized version control system
similar to Git.
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index fcd6192e82f..42da2210fab 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -17,6 +17,7 @@ the [GitHub rake task](../../../administration/raketasks/github_import.md) to im
GitHub without the constraints of a Sidekiq worker.
The following aspects of a project are imported:
+
* Repository description (GitLab.com & 7.7+)
* Git repository data (GitLab.com & 7.7+)
* Issues (GitLab.com & 7.7+)
@@ -65,9 +66,9 @@ developer documentation.
Before you begin, ensure that any GitHub users who you want to map to GitLab users have either:
-1. A GitLab account that has logged in using the GitHub icon
+- A GitLab account that has logged in using the GitHub icon
\- or -
-2. A GitLab account with an email address that matches the [public email address](https://help.github.com/articles/setting-your-commit-email-address-on-github/) of the GitHub user
+- A GitLab account with an email address that matches the [public email address](https://help.github.com/articles/setting-your-commit-email-address-on-github/) of the GitHub user
User-matching attempts occur in that order, and if a user is not identified either way, the activity is associated with
the user account that is performing the import.
@@ -77,10 +78,10 @@ If you are using a self-hosted GitLab instance, this process requires that you h
[GitHub integration][gh-import].
1. From the top navigation bar, click **+** and select **New project**.
-2. Select the **Import project** tab and then select **GitHub**.
-3. Select the first button to **List your GitHub repositories**. You are redirected to a page on github.com to authorize the GitLab application.
-4. Click **Authorize gitlabhq**. You are redirected back to GitLab's Import page and all of your GitHub repositories are listed.
-5. Continue on to [selecting which repositories to import](#selecting-which-repositories-to-import).
+1. Select the **Import project** tab and then select **GitHub**.
+1. Select the first button to **List your GitHub repositories**. You are redirected to a page on github.com to authorize the GitLab application.
+1. Click **Authorize gitlabhq**. You are redirected back to GitLab's Import page and all of your GitHub repositories are listed.
+1. Continue on to [selecting which repositories to import](#selecting-which-repositories-to-import).
### Using a GitHub token
@@ -92,12 +93,12 @@ integration enabled, that should be the preferred method to import your reposito
If you are not using the GitHub integration, you can still perform an authorization with GitHub to grant GitLab access your repositories:
1. Go to https://github.com/settings/tokens/new
-2. Enter a token description.
-3. Select the repo scope.
-4. Click **Generate token**.
-5. Copy the token hash.
-6. Go back to GitLab and provide the token to the GitHub importer.
-7. Hit the **List Your GitHub Repositories** button and wait while GitLab reads your repositories' information.
+1. Enter a token description.
+1. Select the repo scope.
+1. Click **Generate token**.
+1. Copy the token hash.
+1. Go back to GitLab and provide the token to the GitHub importer.
+1. Hit the **List Your GitHub Repositories** button and wait while GitLab reads your repositories' information.
Once done, you'll be taken to the importer page to select the repositories to import.
### Selecting which repositories to import
@@ -107,10 +108,10 @@ your GitHub repositories are listed.
1. By default, the proposed repository namespaces match the names as they exist in GitHub, but based on your permissions,
you can choose to edit these names before you proceed to import any of them.
-2. Select the **Import** button next to any number of repositories, or select **Import all repositories**.
-3. The **Status** column shows the import status of each repository. You can choose to leave the page open and it will
+1. Select the **Import** button next to any number of repositories, or select **Import all repositories**.
+1. The **Status** column shows the import status of each repository. You can choose to leave the page open and it will
update in realtime or you can return to it later.
-4. Once a repository has been imported, click its GitLab path to open its GitLab URL.
+1. Once a repository has been imported, click its GitLab path to open its GitLab URL.
## Mirroring and pipeline status sharing
diff --git a/doc/user/project/import/img/bitbucket_server_import_credentials.png b/doc/user/project/import/img/bitbucket_server_import_credentials.png
index 70b26e89d49..25bcc3ab6e6 100644
--- a/doc/user/project/import/img/bitbucket_server_import_credentials.png
+++ b/doc/user/project/import/img/bitbucket_server_import_credentials.png
Binary files differ
diff --git a/doc/user/project/import/img/bitbucket_server_import_select_project.png b/doc/user/project/import/img/bitbucket_server_import_select_project.png
index e5b1b89e6a3..e7fddef9955 100644
--- a/doc/user/project/import/img/bitbucket_server_import_select_project.png
+++ b/doc/user/project/import/img/bitbucket_server_import_select_project.png
Binary files differ
diff --git a/doc/user/project/import/img/fogbugz_import_login.png b/doc/user/project/import/img/fogbugz_import_login.png
index 96bce70b74d..6ba4d443f1a 100644
--- a/doc/user/project/import/img/fogbugz_import_login.png
+++ b/doc/user/project/import/img/fogbugz_import_login.png
Binary files differ
diff --git a/doc/user/project/import/img/fogbugz_import_select_fogbogz.png b/doc/user/project/import/img/fogbugz_import_select_fogbogz.png
index b26c652e382..d207646a6f2 100644
--- a/doc/user/project/import/img/fogbugz_import_select_fogbogz.png
+++ b/doc/user/project/import/img/fogbugz_import_select_fogbogz.png
Binary files differ
diff --git a/doc/user/project/import/img/fogbugz_import_select_project.png b/doc/user/project/import/img/fogbugz_import_select_project.png
index ccc82f9d4cd..b5e6f497f9b 100644
--- a/doc/user/project/import/img/fogbugz_import_select_project.png
+++ b/doc/user/project/import/img/fogbugz_import_select_project.png
Binary files differ
diff --git a/doc/user/project/import/img/import_projects_from_gitea_new_import.png b/doc/user/project/import/img/import_projects_from_gitea_new_import.png
index a3f603cbd0a..41eb4b2bd00 100644
--- a/doc/user/project/import/img/import_projects_from_gitea_new_import.png
+++ b/doc/user/project/import/img/import_projects_from_gitea_new_import.png
Binary files differ
diff --git a/doc/user/project/import/img/import_projects_from_github_select_auth_method.png b/doc/user/project/import/img/import_projects_from_github_select_auth_method.png
index 1ccb38a815e..90e6243aec0 100644
--- a/doc/user/project/import/img/import_projects_from_github_select_auth_method.png
+++ b/doc/user/project/import/img/import_projects_from_github_select_auth_method.png
Binary files differ
diff --git a/doc/user/project/import/img/import_projects_from_new_project_page.png b/doc/user/project/import/img/import_projects_from_new_project_page.png
index 40402eae226..7c32d3555d1 100644
--- a/doc/user/project/import/img/import_projects_from_new_project_page.png
+++ b/doc/user/project/import/img/import_projects_from_new_project_page.png
Binary files differ
diff --git a/doc/user/project/import/img/manifest_upload.png b/doc/user/project/import/img/manifest_upload.png
deleted file mode 100644
index d6bf4b157dd..00000000000
--- a/doc/user/project/import/img/manifest_upload.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index b55435e5b4f..2f5efbe84d9 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -1,6 +1,7 @@
# Migrating projects to a GitLab instance
-1. [From Bitbucket.org](bitbucket.md)
+1. [From Bitbucket Cloud (aka bitbucket.org)](bitbucket.md)
+1. [From Bitbucket Server (aka Stash)](bitbucket_server.md)
1. [From ClearCase](clearcase.md)
1. [From CVS](cvs.md)
1. [From FogBugz](fogbugz.md)
@@ -11,7 +12,7 @@
1. [From SVN](svn.md)
1. [From TFS](tfs.md)
1. [From repo by URL](repo_by_url.md)
-1. [By uploading a manifest file](manifest.md)
+1. [By uploading a manifest file (AOSP)](manifest.md)
In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
index 06171f11e12..baf410d9c9e 100644
--- a/doc/user/project/import/manifest.md
+++ b/doc/user/project/import/manifest.md
@@ -1,49 +1,59 @@
# Import multiple repositories by uploading a manifest file
-GitLab allows you to import all the required git repositories
-based a manifest file like the one used by the [Android repository](https://android.googlesource.com/platform/manifest/+/2d6f081a3b05d8ef7a2b1b52b0d536b2b74feab4/default.xml).
-This feature can be very handy when you need to import a project with many repositories like Android Open Source Project (AOSP).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28811) in
+GitLab 11.2.
+GitLab allows you to import all the required Git repositories
+based on a manifest file like the one used by the
+[Android repository](https://android.googlesource.com/platform/manifest/+/2d6f081a3b05d8ef7a2b1b52b0d536b2b74feab4/default.xml).
+This feature can be very handy when you need to import a project with many
+repositories like the Android Open Source Project (AOSP).
->**Note:**
-This feature requires [subgroups](../../group/subgroups/index.md) to be supported by your database.
+## Requirements
-You can do it by following next steps:
+GitLab must be using PostgreSQL for its database, since
+[subgroups](../../group/subgroups/index.md) are needed for the manifest import
+to work.
-1. From your GitLab dashboard click **New project**
-1. Switch to the **Import project** tab
-1. Click on the **Manifest file** button
-1. Provide GitLab with a manifest xml file
-1. Select a group you want to import to (you need to create a group first if you don't have one)
-1. Click **List available repositories**
-1. You will be redirected to the import status page with projects list based on manifest file
-1. Check the list and click 'Import all repositories' to start import.
-
-![Manifest upload](img/manifest_upload.png)
+Read more about the [database requirements](../../../install/requirements.md#database).
-![Manifest status](img/manifest_status.png)
+## Manifest format
-### Manifest format
+A manifest must be an XML file. There must be one `remote` tag with a `review`
+attribute that contains a URL to a Git server, and each `project` tag must have
+a `name` and `path` attribute. GitLab will then build the URL to the repository
+by combining the URL from the `remote` tag with a project name.
+A path attribute will be used to represent the project path in GitLab.
-A manifest must be an XML file. There must be one `remote` tag with `review` attribute
-that contains a URL to a git server. Each `project` tag must have `name` and `path` attribute.
-GitLab will build URL to the repository by combining URL from `remote` tag with a project name.
-A path attribute will be used to represent project path in GitLab system.
-
-Below is a valid example of manifest file.
+Below is a valid example of a manifest file:
```xml
<manifest>
- <remote review="https://android-review.googlesource.com/" />
+ <remote review="https://android.googlesource.com/" />
<project path="build/make" name="platform/build" />
<project path="build/blueprint" name="platform/build/blueprint" />
</manifest>
```
-As result next projects will be created:
+As a result, the following projects will be created:
+
+| GitLab | Import URL |
+|:------------------------------------------------|:------------------------------------------------------------|
+| `https://gitlab.com/YOUR_GROUP/build/make` | <https://android.googlesource.com/platform/build> |
+| `https://gitlab.com/YOUR_GROUP/build/blueprint` | <https://android.googlesource.com/platform/build/blueprint> |
+
+## Importing the repositories
+
+You can start the import with:
+
+1. From your GitLab dashboard click **New project**
+1. Switch to the **Import project** tab
+1. Click on the **Manifest file** button
+1. Provide GitLab with a manifest xml file
+1. Select a group you want to import to (you need to create a group first if you don't have one)
+1. Click **List available repositories**. At this point, you will be redirected
+ to the import status page with projects list based on the manifest file.
+1. Check the list and click **Import all repositories** to start the import.
-| GitLab | Import URL |
-|---|---|
-| https://gitlab/YOUR_GROUP/build/make | https://android-review.googlesource.com/platform/build |
-| https://gitlab/YOUR_GROUP/build/blueprint | https://android-review.googlesource.com/platform/build/blueprint |
+ ![Manifest status](img/manifest_status.png)
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
index 7a3628a39d7..a5923986292 100644
--- a/doc/user/project/import/svn.md
+++ b/doc/user/project/import/svn.md
@@ -8,7 +8,7 @@ between the two, for more information consult your favorite search engine.
There are two approaches to SVN to Git migration:
-1. [Git/SVN Mirror](#smooth-migration-with-a-gitsvn-mirror-using-subgit) which:
+1. [Git/SVN Mirror](#smooth-migration-with-a-git-svn-mirror-using-subgit) which:
- Makes the GitLab repository to mirror the SVN project.
- Git and SVN repositories are kept in sync; you can use either one.
- Smoothens the migration process and allows to manage migration risks.
@@ -29,7 +29,7 @@ directly in a filesystem level.
1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can
follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html).
-1. Download SubGit from https://subgit.com/download/.
+1. Download SubGit from <https://subgit.com/download/>.
1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit`
command will be available at `/opt/subgit-VERSION/bin/subgit`.
@@ -71,7 +71,7 @@ edit $GIT_REPO_PATH/subgit/config
```
For more information regarding the SubGit configuration options, refer to
-[SubGit's documentation](https://subgit.com/documentation.html) website.
+[SubGit's documentation](https://subgit.com/documentation/) website.
### Initial translation
@@ -97,7 +97,7 @@ subgit import $GIT_REPO_PATH
### SubGit licensing
Running SubGit in a mirror mode requires a
-[registration](https://subgit.com/pricing.html). Registration is free for open
+[registration](https://subgit.com/pricing/). Registration is free for open
source, academic and startup projects.
We're currently working on deeper GitLab/SubGit integration. You may track our
@@ -179,5 +179,6 @@ git push --tags origin
```
## Contribute to this guide
+
We welcome all contributions that would expand this guide with instructions on
how to migrate from SVN and other version control systems.
diff --git a/doc/user/project/integrations/bamboo.md b/doc/user/project/integrations/bamboo.md
index 9b18eb15599..70c0d434f1f 100644
--- a/doc/user/project/integrations/bamboo.md
+++ b/doc/user/project/integrations/bamboo.md
@@ -57,6 +57,6 @@ service in GitLab.
If builds are not triggered, ensure you entered the right GitLab IP address in
Bamboo under 'Trigger IP addresses'.
->**Note:**
-- Starting with GitLab 8.14.0, builds are triggered on push events.
+> **Note:**
+> - Starting with GitLab 8.14.0, builds are triggered on push events.
diff --git a/doc/user/project/integrations/bugzilla.md b/doc/user/project/integrations/bugzilla.md
index 671804035cc..040e80d529d 100644
--- a/doc/user/project/integrations/bugzilla.md
+++ b/doc/user/project/integrations/bugzilla.md
@@ -16,8 +16,9 @@ Once you have configured and enabled Bugzilla you'll see the Bugzilla link on th
## Referencing issues in Bugzilla
Issues in Bugzilla can be referenced in two alternative ways:
-1. `#<ID>` where `<ID>` is a number (example `#143`).
-2. `<PROJECT>-<ID>` where `<PROJECT>` starts with a capital letter which is
+
+- `#<ID>` where `<ID>` is a number (example `#143`).
+- `<PROJECT>-<ID>` where `<PROJECT>` starts with a capital letter which is
then followed by capital letters, numbers or underscores, and `<ID>` is
a number (example `API_32-143`).
diff --git a/doc/user/project/integrations/discord_notifications.md b/doc/user/project/integrations/discord_notifications.md
new file mode 100644
index 00000000000..e157f5cc106
--- /dev/null
+++ b/doc/user/project/integrations/discord_notifications.md
@@ -0,0 +1,29 @@
+# Discord Notifications service
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22684) in GitLab 11.5.
+
+The Discord Notifications service sends event notifications from GitLab to the channel for which the webhook was created.
+
+To send GitLab event notifications to a Discord channel, create a webhook in Discord and configure it in GitLab.
+
+## Create webhook
+
+1. Open the Discord channel you want to receive GitLab event notifications.
+1. From the channel menu, select **Edit channel**.
+1. Click on **Webhooks** menu item.
+1. Click the **Create Webhook** button and fill in the name of the bot that will post the messages. Optionally, edit the avatar.
+1. Note the URL from the **WEBHOOK URL** field.
+1. Click the **Save** button.
+
+## Configure created webhook in GitLab
+
+With the webhook URL created in the Discord channel, you can set up the Discord Notifications service in GitLab.
+
+1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services) in your project's settings. That is, **Project > Settings > Integrations**.
+1. Select the **Discord Notifications** project service to configure it.
+1. Check the **Active** checkbox to turn on the service.
+1. Check the checkboxes corresponding to the GitLab events for which you want to send notifications to Discord.
+1. Paste the webhook URL that you copied from the create Discord webhook step.
+1. Configure the remaining options and click the **Save changes** button.
+
+The Discord channel you created the webhook for will now receive notification of the GitLab events that were configured.
diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md
index 6ab44420a10..20a71da927c 100644
--- a/doc/user/project/integrations/hangouts_chat.md
+++ b/doc/user/project/integrations/hangouts_chat.md
@@ -1,5 +1,7 @@
# Hangouts Chat service
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/43756) in GitLab 11.2.
+
The Hangouts Chat service sends notifications from GitLab to the room for which the webhook was created.
## On Hangouts Chat
@@ -13,7 +15,7 @@ See also [the Hangouts Chat documentation for configuring incoming webhooks](htt
## On GitLab
-When you have the **Webhook URL** for your Hangouts Chat room webhook, you can setup the GitLab service.
+When you have the **Webhook URL** for your Hangouts Chat room webhook, you can set up the GitLab service.
1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services) in your project's settings, i.e. **Project > Settings > Integrations**.
1. Select the **Hangouts Chat** project service to configure it.
diff --git a/doc/user/project/integrations/img/hangouts_chat_configuration.png b/doc/user/project/integrations/img/hangouts_chat_configuration.png
index 33fadbe6547..54aaef6632d 100644
--- a/doc/user/project/integrations/img/hangouts_chat_configuration.png
+++ b/doc/user/project/integrations/img/hangouts_chat_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/img/issue_configuration.png b/doc/user/project/integrations/img/issue_configuration.png
index 2049d60fdd2..5dfd85974d8 100644
--- a/doc/user/project/integrations/img/issue_configuration.png
+++ b/doc/user/project/integrations/img/issue_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_api_token.png b/doc/user/project/integrations/img/jira_api_token.png
new file mode 100644
index 00000000000..2c64f7bc44f
--- /dev/null
+++ b/doc/user/project/integrations/img/jira_api_token.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_api_token_menu.png b/doc/user/project/integrations/img/jira_api_token_menu.png
new file mode 100644
index 00000000000..20655ba3c0e
--- /dev/null
+++ b/doc/user/project/integrations/img/jira_api_token_menu.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_group_access.png b/doc/user/project/integrations/img/jira_group_access.png
index 9d64cc57269..448cc55504d 100644
--- a/doc/user/project/integrations/img/jira_group_access.png
+++ b/doc/user/project/integrations/img/jira_group_access.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_project_name.png b/doc/user/project/integrations/img/jira_project_name.png
index 8540a427461..981c7f7ca18 100644
--- a/doc/user/project/integrations/img/jira_project_name.png
+++ b/doc/user/project/integrations/img/jira_project_name.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_project_settings.png b/doc/user/project/integrations/img/jira_project_settings.png
index cb6a6ba14ce..d96002b7db8 100644
--- a/doc/user/project/integrations/img/jira_project_settings.png
+++ b/doc/user/project/integrations/img/jira_project_settings.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_service.png b/doc/user/project/integrations/img/jira_service.png
index 8e073b84ff9..0ae2fa28756 100644
--- a/doc/user/project/integrations/img/jira_service.png
+++ b/doc/user/project/integrations/img/jira_service.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_service_close_comment.png b/doc/user/project/integrations/img/jira_service_close_comment.png
index bb9cd7e3d13..9af0d38f098 100644
--- a/doc/user/project/integrations/img/jira_service_close_comment.png
+++ b/doc/user/project/integrations/img/jira_service_close_comment.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_service_page.png b/doc/user/project/integrations/img/jira_service_page.png
index 63aa0e99a50..869d562ed5b 100644
--- a/doc/user/project/integrations/img/jira_service_page.png
+++ b/doc/user/project/integrations/img/jira_service_page.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_user_management_link.png b/doc/user/project/integrations/img/jira_user_management_link.png
index f81c5b5fc87..5eb9d031c3e 100644
--- a/doc/user/project/integrations/img/jira_user_management_link.png
+++ b/doc/user/project/integrations/img/jira_user_management_link.png
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_bot_auth.png b/doc/user/project/integrations/img/mattermost_bot_auth.png
index 830b7849f3d..a05d8da1237 100644
--- a/doc/user/project/integrations/img/mattermost_bot_auth.png
+++ b/doc/user/project/integrations/img/mattermost_bot_auth.png
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_bot_available_commands.png b/doc/user/project/integrations/img/mattermost_bot_available_commands.png
index b51798cf10d..3232ccc3451 100644
--- a/doc/user/project/integrations/img/mattermost_bot_available_commands.png
+++ b/doc/user/project/integrations/img/mattermost_bot_available_commands.png
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_configuration.png b/doc/user/project/integrations/img/mattermost_configuration.png
index f52acf4ef3b..e0b55b23520 100644
--- a/doc/user/project/integrations/img/mattermost_configuration.png
+++ b/doc/user/project/integrations/img/mattermost_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_console_integrations.png b/doc/user/project/integrations/img/mattermost_console_integrations.png
index 92a30da5be0..625b57d4dc9 100644
--- a/doc/user/project/integrations/img/mattermost_console_integrations.png
+++ b/doc/user/project/integrations/img/mattermost_console_integrations.png
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_gitlab_token.png b/doc/user/project/integrations/img/mattermost_gitlab_token.png
index 257018914d2..63140503824 100644
--- a/doc/user/project/integrations/img/mattermost_gitlab_token.png
+++ b/doc/user/project/integrations/img/mattermost_gitlab_token.png
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_goto_console.png b/doc/user/project/integrations/img/mattermost_goto_console.png
index 3354c2a24b4..8bacbe485f4 100644
--- a/doc/user/project/integrations/img/mattermost_goto_console.png
+++ b/doc/user/project/integrations/img/mattermost_goto_console.png
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_slash_command_configuration.png b/doc/user/project/integrations/img/mattermost_slash_command_configuration.png
index 12766ab2b34..f9e9de439ca 100644
--- a/doc/user/project/integrations/img/mattermost_slash_command_configuration.png
+++ b/doc/user/project/integrations/img/mattermost_slash_command_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_team_integrations.png b/doc/user/project/integrations/img/mattermost_team_integrations.png
index 69d4a231e5a..c2b68256e11 100644
--- a/doc/user/project/integrations/img/mattermost_team_integrations.png
+++ b/doc/user/project/integrations/img/mattermost_team_integrations.png
Binary files differ
diff --git a/doc/user/project/integrations/img/merge_request_performance.png b/doc/user/project/integrations/img/merge_request_performance.png
index eba6515a6ae..a9cd761cdcb 100644
--- a/doc/user/project/integrations/img/merge_request_performance.png
+++ b/doc/user/project/integrations/img/merge_request_performance.png
Binary files differ
diff --git a/doc/user/project/integrations/img/microsoft_teams_configuration.png b/doc/user/project/integrations/img/microsoft_teams_configuration.png
index b5c9efc3dd9..627715d5c18 100644
--- a/doc/user/project/integrations/img/microsoft_teams_configuration.png
+++ b/doc/user/project/integrations/img/microsoft_teams_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/img/project_services.png b/doc/user/project/integrations/img/project_services.png
index 25b6cd5690b..5fed38a349c 100644
--- a/doc/user/project/integrations/img/project_services.png
+++ b/doc/user/project/integrations/img/project_services.png
Binary files differ
diff --git a/doc/user/project/integrations/img/prometheus_dashboard.png b/doc/user/project/integrations/img/prometheus_dashboard.png
index bd19f1b44cc..1fa36ca2675 100644
--- a/doc/user/project/integrations/img/prometheus_dashboard.png
+++ b/doc/user/project/integrations/img/prometheus_dashboard.png
Binary files differ
diff --git a/doc/user/project/integrations/img/prometheus_deploy.png b/doc/user/project/integrations/img/prometheus_deploy.png
index d39081bcc7b..3f19f23b0cc 100644
--- a/doc/user/project/integrations/img/prometheus_deploy.png
+++ b/doc/user/project/integrations/img/prometheus_deploy.png
Binary files differ
diff --git a/doc/user/project/integrations/img/prometheus_yaml_deploy.png b/doc/user/project/integrations/img/prometheus_yaml_deploy.png
index 978cd7eaa50..78dd178a077 100644
--- a/doc/user/project/integrations/img/prometheus_yaml_deploy.png
+++ b/doc/user/project/integrations/img/prometheus_yaml_deploy.png
Binary files differ
diff --git a/doc/user/project/integrations/img/redmine_configuration.png b/doc/user/project/integrations/img/redmine_configuration.png
index 7b6dd271401..eb392b848b5 100644
--- a/doc/user/project/integrations/img/redmine_configuration.png
+++ b/doc/user/project/integrations/img/redmine_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/img/services_templates_redmine_example.png b/doc/user/project/integrations/img/services_templates_redmine_example.png
index 379cef9888d..34594dfdd55 100644
--- a/doc/user/project/integrations/img/services_templates_redmine_example.png
+++ b/doc/user/project/integrations/img/services_templates_redmine_example.png
Binary files differ
diff --git a/doc/user/project/integrations/img/slack_configuration.png b/doc/user/project/integrations/img/slack_configuration.png
index 527824fc3eb..53b30e0e8cd 100644
--- a/doc/user/project/integrations/img/slack_configuration.png
+++ b/doc/user/project/integrations/img/slack_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/img/webhook_logs.png b/doc/user/project/integrations/img/webhook_logs.png
index 803678db6b6..24bb593c7d0 100644
--- a/doc/user/project/integrations/img/webhook_logs.png
+++ b/doc/user/project/integrations/img/webhook_logs.png
Binary files differ
diff --git a/doc/user/project/integrations/img/webhook_testing.png b/doc/user/project/integrations/img/webhook_testing.png
index 176dcec9d8a..acfebf473b9 100644
--- a/doc/user/project/integrations/img/webhook_testing.png
+++ b/doc/user/project/integrations/img/webhook_testing.png
Binary files differ
diff --git a/doc/user/project/integrations/img/webhooks_ssl.png b/doc/user/project/integrations/img/webhooks_ssl.png
index 21ddec4ebdf..e5777a2e99b 100644
--- a/doc/user/project/integrations/img/webhooks_ssl.png
+++ b/doc/user/project/integrations/img/webhooks_ssl.png
Binary files differ
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index 67c543e00fb..bc4bba40e59 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -1,123 +1,84 @@
-# GitLab JIRA integration
+# GitLab Jira integration
-GitLab can be configured to interact with [JIRA], a project management platform.
+GitLab Issues are a powerful tool for discussing ideas and planning and tracking work.
+However, many organizations have been using Jira for these purposes and have
+extensive data and business processes built into it.
-Once your GitLab project is connected to JIRA, you can reference and close the
-issues in JIRA directly from GitLab.
+While you can always migrate content and process from Jira to GitLab Issues,
+you can also opt to continue using Jira and use it together with GitLab through
+our integration.
-For a use case, check out this article of [How and why to integrate GitLab with
-JIRA](https://www.programmableweb.com/news/how-and-why-to-integrate-gitlab-jira/how-to/2017/04/25).
+Once you integrate your GitLab project with your Jira instance, you can automatically
+detect and cross-reference activity between the GitLab project and any of your projects
+in Jira. This includes the ability to close or transition Jira issues when the work
+is completed in GitLab.
-## Configuration
-
-Each GitLab project can be configured to connect to a different JIRA instance. That
-means one GitLab project maps to _all_ JIRA projects in that JIRA instance once
-the configuration is set up. Therefore, you don't have to explicitly associate
-one GitLab project to any JIRA project. Once the configuration is set up, any JIRA
-projects in the JIRA instance are already mapped to the GitLab project.
-
-If you have one JIRA instance you can pre-fill the settings page with a default
-template, see the [Services Templates][services-templates] docs.
-
-Configuration happens via user name and password. Connecting to a JIRA server
-via CAS is not possible.
-
-In order to enable the JIRA service in GitLab, you need to first configure the
-project in JIRA and then enter the correct values in GitLab.
-
-### Configuring JIRA
-
-We need to create a user in JIRA which will have access to all projects that
-need to integrate with GitLab. Login to your JIRA instance as admin and under
-Administration go to User Management and create a new user.
-
-As an example, we'll create a user named `gitlab` and add it to `JIRA-developers`
-group.
-
-**It is important that the user `GitLab` has write-access to projects in JIRA**
-
-We have split this stage in steps so it is easier to follow.
-
----
-
-1. Login to your JIRA instance as an administrator and under **Administration**
- go to **User Management** to create a new user.
-
- ![JIRA user management link](img/jira_user_management_link.png)
-
- ---
+Here's how the integration responds when you take the following actions in GitLab:
-1. The next step is to create a new user (e.g., `gitlab`) who has write access
- to projects in JIRA. Enter the user's name and a _valid_ e-mail address
- since JIRA sends a verification e-mail to set-up the password.
- _**Note:** JIRA creates the username automatically by using the e-mail
- prefix. You can change it later if you want._
+- **Mention a Jira issue ID** in a commit message or MR (merge request).
+ - GitLab hyperlinks to the Jira issue.
+ - The Jira issue adds an issue link to the commit/MR in GitLab.
+ - The Jira issue adds a comment reflecting the comment made in GitLab, the comment author, and a link to the commit/MR in GitLab.
+- **Mention that a commit or MR 'closes', 'resolves', or 'fixes' a Jira issue ID**. When the commit is made on master or the change is merged to master:
+ - GitLab's merge request page displays a note that it "Closed" the Jira issue, with a link to the issue. (Note: Before the merge, an MR will display that it "Closes" the Jira issue.)
+ - The Jira issue shows the activity and the Jira issue is closed, or otherwise transitioned.
- ![JIRA create new user](img/jira_create_new_user.png)
+You can also use [Jira's Smart Commits](https://confluence.atlassian.com/fisheye/using-smart-commits-298976812.html)
+directly from GitLab, as covered in the article
+[How and why to integrate GitLab with Jira](https://www.programmableweb.com/news/how-and-why-to-integrate-gitlab-Jira/how-to/2017/04/25).
- ---
-
-1. Now, let's create a `gitlab-developers` group which will have write access
- to projects in JIRA. Go to the **Groups** tab and select **Create group**.
-
- ![JIRA create new user](img/jira_create_new_group.png)
-
- ---
-
- Give it an optional description and hit **Create group**.
-
- ![jira create new group](img/jira_create_new_group_name.png)
-
- ---
+## Configuration
-1. Give the newly-created group write access by going to
- **Application access âž” View configuration** and adding the `gitlab-developers`
- group to JIRA Core.
+Each GitLab project can be configured to connect to an entire Jira instance. That
+means one GitLab project can interact with _all_ Jira projects in that instance, once
+configured. Therefore, you will not have to explicitly associate
+a GitLab project with any single Jira project.
- ![JIRA group access](img/jira_group_access.png)
+If you have one Jira instance, you can pre-fill the settings page with a default
+template. See the [Services Templates][services-templates] docs.
- ---
+Configuration happens via user name and password. Connecting to a Jira server
+via CAS is not possible.
-1. Add the `gitlab` user to the `gitlab-developers` group by going to
- **Users âž” GitLab user âž” Add group** and selecting the `gitlab-developers`
- group from the dropdown menu. Notice that the group says _Access_ which is
- what we aim for.
+In order to enable the Jira service in GitLab, you need to first configure the
+project in Jira and then enter the correct values in GitLab.
- ![JIRA add user to group](img/jira_add_user_to_group.png)
+### Configuring Jira
- ---
+When connecting to **JIRA Server**, which supports basic authentication, a **username and password** are required. Check the link below and proceed to the next step:
+* [Setting up an user in JIRA server](jira_server_configuration.md)
-The JIRA configuration is over. Write down the new JIRA username and its
-password as they will be needed when configuring GitLab in the next section.
+When connecting to **JIRA Cloud**, which supports authentication via API token, an **email and API token**, are required. Check the link below and proceed to the next step:
+* [Setting up an user in JIRA cloud](jira_cloud_configuration.md)
### Configuring GitLab
->**Notes:**
-- The currently supported JIRA versions are `v6.x` and `v7.x.`. GitLab 7.8 or
- higher is required.
-- GitLab 8.14 introduced a new way to integrate with JIRA which greatly simplified
- the configuration options you have to enter. If you are using an older version,
- [follow this documentation][jira-repo-old-docs].
-- In order to support Oracle's Access Manager, GitLab will send additional cookies
- to enable Basic Auth. The cookie being added to each request is `OBBasicAuth` with
- a value of `fromDialog`.
-
-To enable JIRA integration in a project, navigate to the
+> **Notes:**
+> - The currently supported Jira versions are `v6.x` and `v7.x.`. GitLab 7.8 or
+> higher is required.
+> - GitLab 8.14 introduced a new way to integrate with Jira which greatly simplified
+> the configuration options you have to enter. If you are using an older version,
+> [follow this documentation][jira-repo-old-docs].
+> - In order to support Oracle's Access Manager, GitLab will send additional cookies
+> to enable Basic Auth. The cookie being added to each request is `OBBasicAuth` with
+> a value of `fromDialog`.
+
+To enable Jira integration in a project, navigate to the
[Integrations page](project_services.md#accessing-the-project-services), click
-the **JIRA** service, and fill in the required details on the page as described
+the **Jira** service, and fill in the required details on the page as described
in the table below.
| Field | Description |
| ----- | ----------- |
-| `Web URL` | The base URL to the JIRA instance web interface which is being linked to this GitLab project. E.g., `https://jira.example.com`. |
-| `JIRA API URL` | The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. |
-| `Username` | The user name created in [configuring JIRA step](#configuring-jira). Using the email address will cause `401 unauthorized`. |
-| `Password` |The password of the user created in [configuring JIRA step](#configuring-jira). |
-| `Transition ID` | This is the ID of a transition that moves issues to the desired state. It is possible to insert transition ids separated by `,` or `;` which means the issue will be moved to each state after another using the given order. **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
+| `Web URL` | The base URL to the Jira instance web interface which is being linked to this GitLab project. E.g., `https://Jira.example.com`. |
+| `Jira API URL` | The base URL to the Jira instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. |
+| `Username/Email` | Created when [configuring Jira step](#configuring-jira). Use `username` for **JIRA server** or `email` for **JIRA cloud**. |
+| `Password/API token` |Created in [configuring Jira step](#configuring-jira). Use `password` for **JIRA server** or `API token` for **JIRA cloud**. |
+| `Transition ID` | This is the ID of a transition that moves issues to the desired state. It is possible to insert transition ids separated by `,` or `;` which means the issue will be moved to each state after another using the given order. **Closing Jira issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
-### Getting a transition ID
+### Obtaining a transition ID
-In the most recent JIRA UI, you can no longer see transition IDs in the workflow
+In the most recent Jira user interface, you can no longer see transition IDs in the workflow
administration UI. You can get the ID you need in either of the following ways:
1. By using the API, with a request like `https://yourcompany.atlassian.net/rest/api/2/issue/ISSUE-123/transitions`
@@ -129,25 +90,23 @@ Note that the transition ID may vary between workflows (e.g., bug vs. story),
even if the status you are changing to is the same.
After saving the configuration, your GitLab project will be able to interact
-with all JIRA projects in your JIRA instance and you'll see the JIRA link on the GitLab project pages that takes you to the appropriate JIRA project.
+with all Jira projects in your Jira instance and you'll see the Jira link on the GitLab project pages that takes you to the appropriate Jira project.
-![JIRA service page](img/jira_service_page.png)
+![Jira service page](img/jira_service_page.png)
----
+## Jira issues
-## JIRA issues
-
-By now you should have [configured JIRA](#configuring-jira) and enabled the
-[JIRA service in GitLab](#configuring-gitlab). If everything is set up correctly
-you should be able to reference and close JIRA issues by just mentioning their
+By now you should have [configured Jira](#configuring-jira) and enabled the
+[Jira service in GitLab](#configuring-gitlab). If everything is set up correctly
+you should be able to reference and close Jira issues by just mentioning their
ID in GitLab commits and merge requests.
-### Referencing JIRA Issues
+### Referencing Jira Issues
-When GitLab project has JIRA issue tracker configured and enabled, mentioning
-JIRA issue in GitLab will automatically add a comment in JIRA issue with the
+When GitLab project has Jira issue tracker configured and enabled, mentioning
+Jira issue in GitLab will automatically add a comment in Jira issue with the
link back to GitLab. This means that in comments in merge requests and commits
-referencing an issue, e.g., `PROJECT-7`, will add a comment in JIRA issue in the
+referencing an issue, e.g., `PROJECT-7`, will add a comment in Jira issue in the
format:
```
@@ -156,21 +115,19 @@ ENTITY_TITLE
```
* `USER` A user that mentioned the issue. This is the link to the user profile in GitLab.
-* `LINK_TO_THE_COMMENT` Link to the origin of mention with a name of the entity where JIRA issue was mentioned.
+* `LINK_TO_THE_COMMENT` Link to the origin of mention with a name of the entity where Jira issue was mentioned.
* `RESOURCE_NAME` Kind of resource which referenced the issue. Can be a commit or merge request.
* `PROJECT_NAME` GitLab project name.
* `ENTITY_TITLE` Merge request title or commit message first line.
-![example of mentioning or closing the JIRA issue](img/jira_issue_reference.png)
-
----
+![example of mentioning or closing the Jira issue](img/jira_issue_reference.png)
-### Closing JIRA Issues
+### Closing Jira Issues
-JIRA issues can be closed directly from GitLab by using trigger words in
+Jira issues can be closed directly from GitLab by using trigger words in
commits and merge requests. When a commit which contains the trigger word
-followed by the JIRA issue ID in the commit message is pushed, GitLab will
-add a comment in the mentioned JIRA issue and immediately close it (provided
+followed by the Jira issue ID in the commit message is pushed, GitLab will
+add a comment in the mentioned Jira issue and immediately close it (provided
the transition ID was set up correctly).
There are currently three trigger words, and you can use either one to achieve
@@ -180,66 +137,66 @@ the same goal:
- `Closes PROJECT-1`
- `Fixes PROJECT-1`
-where `PROJECT-1` is the issue ID of the JIRA project.
+where `PROJECT-1` is the issue ID of the Jira project.
->**Note:**
-- Only commits and merges into the project's default branch (usually **master**) will
- close an issue in Jira. You can change your projects default branch under
- [project settings](img/jira_project_settings.png).
-- The JIRA issue will not be transitioned if it has a resolution.
+> **Notes:**
+> - Only commits and merges into the project's default branch (usually **master**) will
+> close an issue in Jira. You can change your projects default branch under
+> [project settings](img/jira_project_settings.png).
+> - The Jira issue will not be transitioned if it has a resolution.
-### JIRA issue closing example
+### Jira issue closing example
Let's consider the following example:
-1. For the project named `PROJECT` in JIRA, we implemented a new feature
+1. For the project named `PROJECT` in Jira, we implemented a new feature
and created a merge request in GitLab.
-1. This feature was requested in JIRA issue `PROJECT-7` and the merge request
+1. This feature was requested in Jira issue `PROJECT-7` and the merge request
in GitLab contains the improvement
1. In the merge request description we use the issue closing trigger
`Closes PROJECT-7`.
-1. Once the merge request is merged, the JIRA issue will be automatically closed
+1. Once the merge request is merged, the Jira issue will be automatically closed
with a comment and an associated link to the commit that resolved the issue.
----
-
-In the following screenshot you can see what the link references to the JIRA
+In the following screenshot you can see what the link references to the Jira
issue look like.
-![A Git commit that causes the JIRA issue to be closed](img/jira_merge_request_close.png)
-
----
+![A Git commit that causes the Jira issue to be closed](img/jira_merge_request_close.png)
-Once this merge request is merged, the JIRA issue will be automatically closed
+Once this merge request is merged, the Jira issue will be automatically closed
with a link to the commit that resolved the issue.
-![The GitLab integration closes JIRA issue](img/jira_service_close_issue.png)
+![The GitLab integration closes Jira issue](img/jira_service_close_issue.png)
----
-
-![The GitLab integration creates a comment and a link on JIRA issue.](img/jira_service_close_comment.png)
+![The GitLab integration creates a comment and a link on Jira issue.](img/jira_service_close_comment.png)
## Troubleshooting
-If things don't work as expected that's usually because you have configured
-incorrectly the JIRA-GitLab integration.
+If these features do not work as expected, it is likely due to a problem with the way the integration settings were configured.
+
+### GitLab is unable to comment on a Jira issue
+
+Make sure that the Jira user you set up for the integration has the
+correct access permission to post comments on a Jira issue and also to transition
+the issue, if you'd like GitLab to also be able to do so.
+Jira issue references and update comments will not work if the GitLab issue tracker is disabled.
-### GitLab is unable to comment on a ticket
+### GitLab is unable to close a Jira issue
-Make sure that the user you set up for GitLab to communicate with JIRA has the
-correct access permission to post comments on a ticket and to also transition
-the ticket, if you'd like GitLab to also take care of closing them.
-JIRA issue references and update comments will not work if the GitLab issue tracker is disabled.
+Make sure the `Transition ID` you set within the Jira settings matches the one
+your project needs to close an issue.
-### GitLab is unable to close a ticket
+Make sure that the Jira issue is not already marked as resolved; that is,
+the Jira issue resolution field is not set. (It should not be struck through in
+Jira lists.)
-Make sure the `Transition ID` you set within the JIRA settings matches the one
-your project needs to close a ticket.
+### CAPTCHA
-Make sure that the JIRA issue is not already marked as resolved, in other words that
-the JIRA issue resolution field is not set. (It should not be struck through in
-JIRA lists.)
+CAPTCHA may be triggered after several consecutive failed login attempts
+which may lead to a `401 unauthorized` error when testing your Jira integration.
+If CAPTCHA has been triggered, you will not be able to use Jira's REST API to
+authenticate with the Jira site. You will need to log in to your Jira instance
+and complete the CAPTCHA.
[services-templates]: services_templates.md
[jira-repo-old-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-13-stable/doc/project_services/jira.md
-[jira]: https://www.atlassian.com/software/jira
diff --git a/doc/user/project/integrations/jira_cloud_configuration.md b/doc/user/project/integrations/jira_cloud_configuration.md
new file mode 100644
index 00000000000..cae66526175
--- /dev/null
+++ b/doc/user/project/integrations/jira_cloud_configuration.md
@@ -0,0 +1,18 @@
+# Creating an API token in JIRA cloud
+
+An API token is needed when integrating with JIRA Cloud, follow the steps
+below to create one:
+
+1. Log in to https://id.atlassian.com with your email.
+1. **Click API tokens**, then **Create API token**.
+
+![JIRA API token](img/jira_api_token_menu.png)
+
+![JIRA API token](img/jira_api_token.png)
+
+1. Make sure to write down your new API token as you will need it in the next [steps](jira.md#configuring-gitlab).
+
+NOTE: **Note**
+It is important that the user associated with this email has 'write' access to projects in JIRA.
+
+The JIRA configuration is complete. You are going to need this new created token and the email you used to log in when [configuring GitLab in the next section](jira.md#configuring-gitlab).
diff --git a/doc/user/project/integrations/jira_server_configuration.md b/doc/user/project/integrations/jira_server_configuration.md
new file mode 100644
index 00000000000..20036183187
--- /dev/null
+++ b/doc/user/project/integrations/jira_server_configuration.md
@@ -0,0 +1,53 @@
+# Creating a username and password for JIRA server
+
+We need to create a user in Jira which will have access to all projects that
+need to integrate with GitLab. Login to your Jira instance as admin and under
+*Administration*, go to *User Management* and create a new user.
+
+As an example, we'll create a user named `gitlab` and add it to the `Jira-developers`
+group.
+
+NOTE: **Note**
+It is important that the user `gitlab` has 'write' access to projects in Jira.
+
+We have split this stage in steps so it is easier to follow.
+
+1. Log in to your Jira instance as an administrator and under **Administration**
+ go to **User Management** to create a new user.
+
+ ![Jira user management link](img/jira_user_management_link.png)
+
+1. The next step is to create a new user (e.g., `gitlab`) who has write access
+ to projects in Jira. Enter the user's name and a _valid_ e-mail address
+ since Jira sends a verification e-mail to set up the password.
+ _**Note:** Jira creates the username automatically by using the e-mail
+ prefix. You can change it later, if needed. Our integration does not support SSO (such as SAML). You will need to create
+ an HTTP basic authentication password. You can do this by visiting the user
+ profile, looking up the username, and setting a password._
+
+ ![Jira create new user](img/jira_create_new_user.png)
+
+1. Create a `gitlab-developers` group which will have write access
+ to projects in Jira. Go to the **Groups** tab and select **Create group**.
+
+ ![Jira create new user](img/jira_create_new_group.png)
+
+ Give it an optional description and click **Create group**.
+
+ ![Jira create new group](img/jira_create_new_group_name.png)
+
+1. To give the newly-created group 'write' access, go to
+ **Application access > View configuration** and add the `gitlab-developers`
+ group to Jira Core.
+
+ ![Jira group access](img/jira_group_access.png)
+
+1. Add the `gitlab` user to the `gitlab-developers` group by going to
+ **Users > GitLab user > Add group** and selecting the `gitlab-developers`
+ group from the dropdown menu. Notice that the group says _Access_, which is
+ intended as part of this process.
+
+ ![Jira add user to group](img/jira_add_user_to_group.png)
+
+The Jira configuration is complete. Write down the new Jira username and its
+password as they will be needed when [configuring GitLab in the next section](jira.md#configuring-gitlab).
diff --git a/doc/user/project/integrations/mattermost.md b/doc/user/project/integrations/mattermost.md
index 3e77823a6aa..ed4367c1135 100644
--- a/doc/user/project/integrations/mattermost.md
+++ b/doc/user/project/integrations/mattermost.md
@@ -4,9 +4,9 @@
To enable Mattermost integration you must create an incoming webhook integration:
-1. Sign in to your Mattermost instance
-1. Visit incoming webhooks, that will be something like: https://mattermost.example/your_team_name/integrations/incoming_webhooks/add
-1. Choose a display name, description and channel, those can be overridden on GitLab
+1. Sign in to your Mattermost instance.
+1. Visit incoming webhooks, that will be something like: `https://mattermost.example.com/your_team_name/integrations/incoming_webhooks/add`.
+1. Choose a display name, description and channel, those can be overridden on GitLab.
1. Save it, copy the **Webhook URL**, we'll need this later for GitLab.
There might be some cases that Incoming Webhooks are blocked by admin, ask your mattermost admin to enable
@@ -38,7 +38,7 @@ At the end, fill in your Mattermost details:
| Field | Description |
| ----- | ----------- |
-| **Webhook** | The incoming webhook URL which you have to setup on Mattermost, it will be something like: http://mattermost.example/hooks/5xo… |
+| **Webhook** | The incoming webhook URL which you have to set up on Mattermost, it will be something like: http://mattermost.example/hooks/5xo… |
| **Username** | Optional username which can be on messages sent to Mattermost. Fill this in if you want to change the username of the bot. |
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
diff --git a/doc/user/project/integrations/mattermost_slash_commands.md b/doc/user/project/integrations/mattermost_slash_commands.md
index 488f61c77a3..e031dcad2c3 100644
--- a/doc/user/project/integrations/mattermost_slash_commands.md
+++ b/doc/user/project/integrations/mattermost_slash_commands.md
@@ -102,7 +102,7 @@ in a new slash command.
![Mattermost add command configuration](img/mattermost_slash_command_configuration.png)
-1. After you setup all the values, copy the token (we will use it below) and
+1. After you set up all the values, copy the token (we will use it below) and
click **Done**.
![Mattermost slash command token](img/mattermost_slash_command_token.png)
diff --git a/doc/user/project/integrations/microsoft_teams.md b/doc/user/project/integrations/microsoft_teams.md
index 5cf80a298ad..ca32689910c 100644
--- a/doc/user/project/integrations/microsoft_teams.md
+++ b/doc/user/project/integrations/microsoft_teams.md
@@ -2,7 +2,7 @@
## On Microsoft Teams
-To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors#setting-up-a-custom-incoming-webhook).
+To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors/connectors-using#setting-up-a-custom-incoming-webhook).
## On GitLab
@@ -25,7 +25,7 @@ At the end fill in your Microsoft Teams details:
| Field | Description |
| ----- | ----------- |
-| **Webhook** | The incoming webhook URL which you have to setup on Microsoft Teams. |
+| **Webhook** | The incoming webhook URL which you have to set up on Microsoft Teams. |
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
After you are all done, click **Save changes** for the changes to take effect.
diff --git a/doc/user/project/integrations/mock_ci.md b/doc/user/project/integrations/mock_ci.md
index 6aefe5dbded..8b1908c46fe 100644
--- a/doc/user/project/integrations/mock_ci.md
+++ b/doc/user/project/integrations/mock_ci.md
@@ -2,7 +2,7 @@
**NB: This service is only listed if you are in a development environment!**
-To setup the mock CI service server, respond to the following endpoints
+To set up the mock CI service server, respond to the following endpoints
- `commit_status`: `#{project.namespace.path}/#{project.path}/status/#{sha}.json`
- Have your service return `200 { status: ['failed'|'canceled'|'running'|'pending'|'success'|'success_with_warnings'|'skipped'|'not_found'] }`
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index 05ee1b4e6d7..be45ce46dfd 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -30,11 +30,11 @@ Click on the service links to see further configuration instructions and details
| [Bugzilla](bugzilla.md) | Bugzilla issue tracker |
| Campfire | Simple web-based real-time group chat |
| Custom Issue Tracker | Custom issue tracker |
+| [Discord Notifications](discord_notifications.md) | Receive event notifications in Discord |
| Drone CI | Continuous Integration platform built on Docker, written in Go |
| [Emails on push](emails_on_push.md) | Email the commits and diff of each push to a list of recipients |
| External Wiki | Replaces the link to the internal wiki with a link to an external wiki |
| Flowdock | Flowdock is a collaboration web app for technical teams |
-| Gemnasium _(Has been deprecated in GitLab 11.0)_ | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities |
| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat |
| [HipChat](hipchat.md) | Private group chat and IM |
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index f687027e8c8..0b61a41aab0 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -8,7 +8,7 @@ within the GitLab interface.
![Environment Dashboard](img/prometheus_dashboard.png)
-There are two ways to setup Prometheus integration, depending on where your apps are running:
+There are two ways to set up Prometheus integration, depending on where your apps are running:
* For deployments on Kubernetes, GitLab can automatically [deploy and manage Prometheus](#managed-prometheus-on-kubernetes)
* For other deployment targets, simply [specify the Prometheus server](#manual-configuration-of-prometheus).
diff --git a/doc/user/project/integrations/prometheus_library/metrics.md b/doc/user/project/integrations/prometheus_library/metrics.md
index 96a22316265..ec16902fcc8 100644
--- a/doc/user/project/integrations/prometheus_library/metrics.md
+++ b/doc/user/project/integrations/prometheus_library/metrics.md
@@ -17,9 +17,3 @@ GitLab retrieves performance data from the configured Prometheus server, and att
In order to isolate and only display relevant metrics for a given environment, GitLab needs a method to detect which labels are associated. To do that,
GitLab uses the defined queries and fills in the environment specific variables. Typically this involves looking for the [$CI_ENVIRONMENT_SLUG](../../../../ci/variables/README.md#predefined-variables-environment-variables), but may also include other information such as the project's Kubernetes namespace. Each search query is defined in the [exporter specific documentation](#prometheus-metrics-library).
-
-## Adding to the library
-
-We strive to support the 2-4 most important metrics for each common system service that supports Prometheus. If you are looking for support for a particular exporter which has not yet been added to the library, additions can be made [to the `additional_metrics.yml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/prometheus/additional_metrics.yml) file.
-
-> Note: The library is only for monitoring public, common, system services which all customers can benefit from. Support for monitoring [customer proprietary metrics](https://gitlab.com/gitlab-org/gitlab-ee/issues/2273) will be added in a subsequent release.
diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md
index de2cf6d4647..76a2617125e 100644
--- a/doc/user/project/integrations/redmine.md
+++ b/doc/user/project/integrations/redmine.md
@@ -18,15 +18,16 @@ in the table below.
![Redmine configuration](img/redmine_configuration.png)
-2. To disable the internal issue tracking system in a project, navigate to the General page, expand [Permissions](../settings/index.md#sharing-and-permissions), and slide the Issues switch invalid.
+1. To disable the internal issue tracking system in a project, navigate to the General page, expand [Permissions](../settings/index.md#sharing-and-permissions), and slide the Issues switch invalid.
![Issue configuration](img/issue_configuration.png)
## Referencing issues in Redmine
Issues in Redmine can be referenced in two alternative ways:
-1. `#<ID>` where `<ID>` is a number (example `#143`)
-2. `<PROJECT>-<ID>` where `<PROJECT>` starts with a capital letter which is
+
+- `#<ID>` where `<ID>` is a number (example `#143`).
+- `<PROJECT>-<ID>` where `<PROJECT>` starts with a capital letter which is
then followed by capital letters, numbers or underscores, and `<ID>` is
a number (example `API_32-143`).
diff --git a/doc/user/project/integrations/services_templates.md b/doc/user/project/integrations/services_templates.md
index 5b04d7d88b8..a0bf31c526f 100644
--- a/doc/user/project/integrations/services_templates.md
+++ b/doc/user/project/integrations/services_templates.md
@@ -1,8 +1,10 @@
# Services templates
A GitLab administrator can add a service template that sets a default for each
-project. After a service template is enabled, it will be applied to new
-projects only and its details will be pre-filled on the project's Service page.
+project. After a service template is enabled, it will be applied to **all**
+projects that don't have it already enabled and its details will be pre-filled
+on the project's Service page. By disabling the template, it will be disabled
+for new projects only.
## Enable a service template
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 77fa517b5b1..4d1d95da6f0 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -1,21 +1,21 @@
# Webhooks
->**Note:**
-Starting from GitLab 8.5:
-- the `repository` key is deprecated in favor of the `project` key
-- the `project.ssh_url` key is deprecated in favor of the `project.git_ssh_url` key
-- the `project.http_url` key is deprecated in favor of the `project.git_http_url` key
-
->**Note:**
-Starting from GitLab 11.1, the logs of web hooks are automatically removed after
-one month.
-
->**Note**
-Starting from GitLab 11.2:
-- The `description` field for issues, merge requests, comments, and wiki pages
- is rewritten so that simple Markdown image references (like
- `![](/uploads/...)`) have their target URL changed to an absolute URL. See
- [image URL rewriting](#image-url-rewriting) for more details.
+> **Note:**
+> Starting from GitLab 8.5:
+> - the `repository` key is deprecated in favor of the `project` key
+> - the `project.ssh_url` key is deprecated in favor of the `project.git_ssh_url` key
+> - the `project.http_url` key is deprecated in favor of the `project.git_http_url` key
+>
+> **Note:**
+> Starting from GitLab 11.1, the logs of webhooks are automatically removed after
+> one month.
+>
+> **Note:**
+> Starting from GitLab 11.2:
+> - The `description` field for issues, merge requests, comments, and wiki pages
+> is rewritten so that simple Markdown image references (like
+> `![](/uploads/...)`) have their target URL changed to an absolute URL. See
+> [image URL rewriting](#image-url-rewriting) for more details.
Project webhooks allow you to trigger a URL if for example new code is pushed or
a new issue is created. You can configure webhooks to listen for specific events
@@ -57,6 +57,14 @@ You can turn this off in the webhook settings in your GitLab projects.
![SSL Verification](img/webhooks_ssl.png)
+## Branch filtering
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/20338) in GitLab 11.3.
+
+Push events can be filtered by branch using a branch name or wildcard pattern
+to limit which push events are sent to your webhook endpoint. By default the
+field is blank causing all push events to be sent to your webhook endpoint.
+
## Events
Below are described the supported events.
@@ -65,7 +73,7 @@ Below are described the supported events.
Triggered when you push to the repository except when pushing tags.
-> **Note:** When more than 20 commits are pushed at once, the `commits` web hook
+> **Note:** When more than 20 commits are pushed at once, the `commits` webhook
attribute will only contain the first 20 for performance reasons. Loading
detailed commit data is expensive. Note that despite only 20 commits being
present in the `commits` attribute, the `total_commits_count` attribute will
@@ -320,7 +328,7 @@ X-Gitlab-Event: Issue Hook
}
```
-**Note**: `assignee` and `assignee_id` keys are deprecated and now show the first assignee only.
+> **Note**: `assignee` and `assignee_id` keys are deprecated and now show the first assignee only.
### Comment events
@@ -330,10 +338,10 @@ payload will also include information about the target of the comment. For examp
a comment on an issue will include the specific issue information under the `issue` key.
Valid target types:
-1. `commit`
-2. `merge_request`
-3. `issue`
-4. `snippet`
+- `commit`
+- `merge_request`
+- `issue`
+- `snippet`
#### Comment on commit
@@ -619,7 +627,7 @@ X-Gitlab-Event: Note Hook
}
```
-**Note**: `assignee_id` field is deprecated and now shows the first assignee only.
+> **Note**: `assignee_id` field is deprecated and now shows the first assignee only.
#### Comment on code snippet
@@ -935,7 +943,13 @@ X-Gitlab-Event: Pipeline Hook
],
"created_at": "2016-08-12 15:23:28 UTC",
"finished_at": "2016-08-12 15:26:29 UTC",
- "duration": 63
+ "duration": 63,
+ "variables": [
+ {
+ "key": "NESTOR_PROD_ENVIRONMENT",
+ "value": "us-west-1"
+ }
+ ]
},
"user":{
"name": "Administrator",
@@ -1102,6 +1116,7 @@ X-Gitlab-Event: Build Hook
"build_finished_at": null,
"build_duration": null,
"build_allow_failure": false,
+ "build_failure_reason": "script_failure",
"project_id": 380,
"project_name": "gitlab-org/gitlab-test",
"user": {
@@ -1142,10 +1157,11 @@ its description:
```
It will appear in the webhook body as the below (assuming that GitLab is
-installed at gitlab.example.com):
+installed at gitlab.example.com, and the project is at
+example-group/example-project):
```markdown
-![image](https://gitlab.example.com/uploads/$sha/image.png)
+![image](https://gitlab.example.com/example-group/example-project/uploads/$sha/image.png)
```
This will not rewrite URLs that already are pointing to HTTP, HTTPS, or
@@ -1161,7 +1177,7 @@ You can trigger the webhook manually. Sample data from the project will be used.
## Troubleshoot webhooks
-Gitlab stores each perform of the webhook.
+GitLab stores each perform of the webhook.
You can find records for last 2 days in "Recent Deliveries" section on the edit page of each webhook.
![Recent deliveries](img/webhook_logs.png)
@@ -1173,9 +1189,9 @@ On this page, you can see data that GitLab sends (request headers and body) and
From this page, you can repeat delivery with the same data by clicking `Resend Request` button.
->**Note:** If URL or secret token of the webhook were updated, data will be delivered to the new address.
+> **Note:** If URL or secret token of the webhook were updated, data will be delivered to the new address.
-### Receiving duplicate or multiple web hook requests triggered by one event
+### Receiving duplicate or multiple webhook requests triggered by one event
When GitLab sends a webhook it expects a response in 10 seconds (set default value). If it does not receive one, it'll retry the webhook.
If the endpoint doesn't send its HTTP response within those 10 seconds, GitLab may decide the hook failed and retry it.
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 49b49271cff..9e2434c02ec 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -2,13 +2,44 @@
> [Introduced][ce-5554] in [GitLab 8.11](https://about.gitlab.com/2016/08/22/gitlab-8-11-released/#issue-board).
+## Overview
+
The GitLab Issue Board is a software project management tool used to plan,
organize, and visualize a workflow for a feature or product release.
It can be used as a [Kanban] or a [Scrum] board.
-![GitLab Issue Board](img/issue_board.png)
+It provides perfect pairing between issue tracking and project management,
+keeping everything in the same place, so that you don't need to jump
+between different platforms to organize your workflow.
-## Overview
+With GitLab Issue Boards, you organize your issues in lists that correspond to
+their assigned labels, visualizing issues designed as cards throughout that lists.
+
+You define your process and GitLab organizes it for you. You add your labels
+then create the corresponding list to pull in your existing issues. When
+you're ready, you can drag and drop your issue cards from one step to the next.
+
+![GitLab Issue Board - Core](img/issue_boards_core.png)
+
+**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+Watch a [video presentation](https://youtu.be/UWsJ8tkHAa8) of
+Issue Boards** (version introduced in GitLab 8.11 - August 2016).
+
+### Advanced features of Issue Boards
+
+With [GitLab Starter](https://about.gitlab.com/pricing/), you can create
+[multiple issue boards](#multiple-issue-boards) for a given project. **[STARTER]**
+
+With [GitLab Premium](https://about.gitlab.com/pricing/), you can also create multiple
+issue boards for your groups, and add lists for [assignees](#assignee-lists) and
+[milestones](#milestone-lists). **[PREMIUM]**
+
+Check all the [advanced features of Issue Boards](#gitlab-enterprise-features-for-issue-boards)
+below.
+
+![GitLab Issue Boards - Premium](img/issue_boards_premium.png)
+
+## How it works
The Issue Board builds on GitLab's existing
[issue tracking functionality](issues/index.md#issue-tracker) and
@@ -28,15 +59,12 @@ and deploy from one single platform. Issue Boards help you to visualize
and manage the entire process _in_ GitLab.
With [Multiple Issue Boards](#use-cases-for-multiple-issue-boards), available
-only in [GitLab Enterprise Edition](#features-per-tier),
+only in [different tiers of GitLab Enterprise Edition](#gitlab-enterprise-features-for-issue-boards),
you go even further, as you can not only keep yourself and your project
organized from a broader perspective with one Issue Board per project,
but also allow your team members to organize their own workflow by creating
multiple Issue Boards within the same project.
-For a visual overview, see our [Issue Board feature page](https://about.gitlab.com/features/issueboard/)
-on about.gitlab.com or our [video introduction to Issue Boards](https://www.youtube.com/watch?v=UWsJ8tkHAa8).
-
## Use cases
There are many ways to use GitLab Issue Boards tailored to your own preferred workflow.
@@ -111,21 +139,152 @@ to improve their workflow with multiple boards.
Create lists for each of your team members and quickly drag-and-drop issues onto each team member.
-## Permissions
-
-[Developers and up](../permissions.md) can use all the functionality of the
-Issue Board, that is, create or delete lists and drag issues from one list to another.
-
## Issue Board terminology
- **Issue Board** - Each board represents a unique view for your issues. It can have multiple lists with each list consisting of issues represented by cards.
-- **List** - A column on the issue board that displays issues matching certain attributes. In addition to the default lists of 'Backlog' and 'Closed' issue, each additional list will show issues matching your chosen label or assignee. On the top of that list you can see the number of issues that belong to it.
+- **List** - A column on the issue board that displays issues matching certain attributes. In addition to the default lists of 'Open' and 'Closed' issue, each additional list will show issues matching your chosen label or assignee. On the top of that list you can see the number of issues that belong to it.
- **Label list**: a list based on a label. It shows all opened issues with that label.
- **Assignee list**: a list which includes all issues assigned to a user.
- - **Backlog** (default): shows all open issues that do not belong to one of the other lists. Always appears as the leftmost list.
+ - **Open** (default): shows all open issues that do not belong to one of the other lists. Always appears as the leftmost list.
- **Closed** (default): shows all closed issues. Always appears as the rightmost list.
- **Card** - A box in the list that represents an individual issue. The information you can see on a card consists of the issue number, the issue title, the assignee, and the labels associated with the issue. You can drag cards from one list to another to change their label or assignee from that of the source list to that of the destination list.
+## Permissions
+
+[Developers and up](../permissions.md) can use all the functionality of the
+Issue Board, that is, create or delete lists and drag issues from one list to another.
+
+## GitLab Enterprise features for Issue Boards
+
+GitLab Issue Boards are available on GitLab Core and GitLab.com Free, but some
+advanced functionalities are only present in higher tiers: GitLab.com Bronze,
+Silver, or Gold, or GitLab self-managed Starter, Premium, and Ultimate, as described
+on the following sections.
+
+For a collection of [features per tier](#summary-of-features-per-tier), check the summary below.
+
+### Multiple Issue Boards **[STARTER]**
+
+> Introduced in [GitLab Enterprise Edition 8.13](https://about.gitlab.com/2016/10/22/gitlab-8-13-released/#multiple-issue-boards-ee).
+
+Multiple Issue Boards, as the name suggests, allow for more than one Issue Board
+for a given project or group. This is great for large projects with more than one team
+or in situations where a repository is used to host the code of multiple
+products.
+
+Clicking on the current board name in the upper left corner will reveal a
+menu from where you can create another Issue Board and rename or delete the
+existing one.
+
+When you're revisiting an issue board in a project or group with multiple boards,
+GitLab will automatically load the last board you visited.
+
+NOTE: **Note:**
+The Multiple Issue Boards feature is available for
+**projects in GitLab Starter Edition** and for **groups in GitLab Premium Edition**.
+
+![Multiple Issue Boards](img/issue_boards_multiple.png)
+
+### Configurable Issue Boards **[STARTER]**
+
+> Introduced in [GitLab Starter Edition 10.2](https://about.gitlab.com/2017/11/22/gitlab-10-2-released/#issue-boards-configuration).
+
+An Issue Board can be associated with a GitLab [Milestone](milestones/index.md#milestones),
+[Labels](labels.md), Assignee and Weight
+which will automatically filter the Board issues according to these fields.
+This allows you to create unique boards according to your team's need.
+
+![Create scoped board](img/issue_board_creation.png)
+
+You can define the scope of your board when creating it or by clicking on the "Edit board" button. Once a milestone, assignee or weight is assigned to an Issue Board, you will no longer be able to filter
+through these in the search bar. In order to do that, you need to remove the desired scope (e.g. milestone, assignee or weight) from the Issue Board.
+
+![Edit board configuration](img/issue_board_edit_button.png)
+
+If you don't have editing permission in a board, you're still able to see the configuration by clicking on "View scope".
+
+![Viewing board configuration](img/issue_board_view_scope.png)
+
+### Focus mode **[STARTER]**
+
+> Introduced in [GitLab Starter 9.1](https://about.gitlab.com/2017/04/22/gitlab-9-1-released/#issue-boards-focus-mode-ees-eep).
+
+Click the button at the top right to toggle focus mode on and off. In focus mode, the navigation UI is hidden, allowing you to focus on issues in the board.
+
+![Board focus mode](img/issue_board_focus_mode.gif)
+
+### Sum of Issue Weights **[STARTER]**
+
+The top of each list indicates the sum of issue weights for the issues that
+belong to that list. This is useful when using boards for capacity allocation,
+especially in combination with [assignee lists](#assignee-lists).
+
+![Issue Board summed weights](img/issue_board_summed_weights.png)
+
+### Group Issue Boards **[PREMIUM]**
+
+> Introduced in [GitLab Premium 10.0](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards).
+
+Accessible at the group navigation level, a group issue board offers the same features as a project-level board,
+but it can display issues from all projects in that
+group and its descendant subgroups. Similarly, you can only filter by group labels for these
+boards. When updating milestones and labels for an issue through the sidebar update mechanism, again only
+group-level objects are available.
+
+NOTE: **Note:**
+Multiple group issue boards were originally introduced in [GitLab 10.0 Premium](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards) and
+one group issue board per group was made available in GitLab 10.6 Core.
+
+![Group issue board](img/group_issue_board.png)
+
+### Assignee lists **[PREMIUM]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5784) in GitLab 11.0 Premium.
+
+Like a regular list that shows all issues that have the list label, you can add
+an assignee list that shows all issues assigned to the given user.
+You can have a board with both label lists and assignee lists. To add an
+assignee list:
+
+1. Click **Add list**.
+1. Select the **Assignee list** tab.
+1. Search and click on the user you want to add as an assignee.
+
+Now that the assignee list is added, you can assign or unassign issues to that user
+by [dragging issues](#dragging-issues-between-lists) to and/or from an assignee list.
+To remove an assignee list, just as with a label list, click the trash icon.
+
+![Assignee lists](img/issue_board_assignee_lists.png)
+
+### Milestone lists **[PREMIUM]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6469) in GitLab 11.2 Premium.
+
+As of 11.2, you're also able to create lists of a milestone. As the name states,
+these are lists that filter issues by the assigned milestone, giving you more
+freedom and visibility on the Issue Board. To do so:
+
+1. Click **Add list**.
+1. Select the **Milestone** tab.
+1. Search and click on the milestone.
+
+Similar to the assignee lists, you're now able to [drag issues](#dragging-issues-between-lists)
+to and/or from a milestone list to manipulate the milestone of the dragged issues.
+As on another list types, click on the trash icon to remove it.
+
+![Milestone lists](img/issue_board_milestone_lists.png)
+
+### Summary of features per tier
+
+Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/), as shown in the following table:
+
+| Tier | Number of Project Issue Boards | Number of Group Issue Boards | Configurable Issue Boards | Assignee Lists |
+|----------|--------------------------------|------------------------------|---------------------------|----------------|
+| Core / Free | 1 | 1 | No | No |
+| Starter / Bronze | Multiple | 1 | Yes | No |
+| Premium / Silver | Multiple | Multiple | Yes | Yes |
+| Ultimate / Gold | Multiple | Multiple | Yes | Yes |
+
## Actions you can take on an Issue Board
- [Create a new list](#creating-a-new-list).
@@ -142,7 +301,7 @@ Issue Board, that is, create or delete lists and drag issues from one list to an
If you are not able to perform one or more of the things above, make sure you
have the right [permissions](#permissions).
-## First time using the Issue Board
+### First time using the Issue Board
The first time you navigate to your Issue Board, you will be presented with
a default list (**Done**) and a welcoming message that gives
@@ -157,7 +316,7 @@ which means the system has no way of populating them automatically. That's of
course if the predefined labels don't already exist. If any of them does exist,
the list will be created and filled with the issues that have that label.
-## Creating a new list
+### Creating a new list
Create a new list by clicking on the **Add list** button at the upper
right corner of the Issue Board.
@@ -172,7 +331,7 @@ To create a list for a label that doesn't yet exist, simply create the label by
choosing **Create new label**. The label will be created on-the-fly and it will
be immediately added to the dropdown. You can now choose it to create a list.
-## Deleting a list
+### Deleting a list
To delete a list from the Issue Board use the small trash icon that is present
in the list's heading. A confirmation dialog will appear for you to confirm.
@@ -180,7 +339,7 @@ in the list's heading. A confirmation dialog will appear for you to confirm.
Deleting a list doesn't have any effect in issues and labels, it's just the
list view that is removed. You can always add it back later if you need.
-## Adding issues to a list
+### Adding issues to a list
You can add issues to a list by clicking the **Add issues** button that is
present in the upper right corner of the Issue Board. This will open up a modal
@@ -192,7 +351,7 @@ the list by filtering by author, assignee, milestone and label.
![Bulk adding issues to lists](img/issue_boards_add_issues_modal.png)
-## Removing an issue from a list
+### Removing an issue from a list
Removing an issue from a list can be done by clicking on the issue card and then
clicking the **Remove from board** button in the sidebar. Under the hood, the
@@ -201,7 +360,7 @@ board itself.
![Remove issue from list](img/issue_boards_remove_issue.png)
-## Issue ordering in a list
+### Issue ordering in a list
When visiting a board, issues appear ordered in any list. You are able to change
that order simply by dragging and dropping the issues. The changed order will be saved
@@ -224,7 +383,7 @@ a given board inside your GitLab instance, any time those two issues are subsequ
loaded in any board in the same instance (could be a different project board or a different group board, for example),
that ordering will be maintained.
-## Filtering issues
+### Filtering issues
You should be able to use the filters on top of your Issue Board to show only
the results you want. This is similar to the filtering used in the issue tracker
@@ -232,7 +391,7 @@ since the metadata from the issues and labels are re-used in the Issue Board.
You can filter by author, assignee, milestone and label.
-## Creating workflows
+### Creating workflows
By reordering your lists, you can create workflows. As lists in Issue Boards are
based on labels, it works out of the box with your existing issues. So if you've
@@ -267,109 +426,16 @@ to another list the label changes and a system not is recorded.
![Issue Board system notes](img/issue_board_system_notes.png)
-## Multiple Issue Boards **[STARTER]**
-
-> Introduced in [GitLab Enterprise Edition 8.13](https://about.gitlab.com/2016/10/22/gitlab-8-13-released/#multiple-issue-boards-ee).
-
-Multiple Issue Boards, as the name suggests, allow for more than one Issue Board
-for a given project or group. This is great for large projects with more than one team
-or in situations where a repository is used to host the code of multiple
-products.
-
-Clicking on the current board name in the upper left corner will reveal a
-menu from where you can create another Issue Board and rename or delete the
-existing one.
-
-NOTE: **Note:**
-The Multiple Issue Boards feature is available for
-**projects in GitLab Starter Edition** and for **groups in GitLab Premium Edition**.
-
-![Multiple Issue Boards](img/issue_boards_multiple.png)
-
-## Configurable Issue Boards **[STARTER]**
-
-> Introduced in [GitLab Starter Edition 10.2](https://about.gitlab.com/2017/11/22/gitlab-10-2-released/#issue-boards-configuration).
-
-An Issue Board can be associated with GitLab [Milestone](milestones/index.md#milestones),
-[Labels](labels.md), Assignee and Weight
-which will automatically filter the Board issues according to these fields.
-This allows you to create unique boards according to your team's need.
-
-![Create scoped board](img/issue_board_creation.png)
-
-You can define the scope of your board when creating it or by clicking on the "Edit board" button. Once a milestone, assignee or weight is assigned to an Issue Board, you will no longer be able to filter
-through these in the search bar. In order to do that, you need to remove the desired scope (e.g. milestone, assignee or weight) from the Issue Board.
-
-![Edit board configuration](img/issue_board_edit_button.png)
-
-If you don't have editing permission in a board, you're still able to see the configuration by clicking on "View scope".
-
-![Viewing board configuration](img/issue_board_view_scope.png)
-
-## Focus mode **[STARTER]**
-
-> Introduced in [GitLab Starter 9.1](https://about.gitlab.com/2017/04/22/gitlab-9-1-released/#issue-boards-focus-mode-ees-eep).
-
-Click the button at the top right to toggle focus mode on and off. In focus mode, the navigation UI is hidden, allowing you to focus on issues in the board.
-
-![Board focus mode](img/issue_board_focus_mode.gif)
-
-## Group Issue Boards **[PREMIUM]**
-
-> Introduced in [GitLab Premium 10.0](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards).
-
-Accessible at the group navigation level, a group issue board offers the same features as a project-level board,
-but it can display issues from all projects in that
-group and its descendant subgroups. Similarly, you can only filter by group labels for these
-boards. When updating milestones and labels for an issue through the sidebar update mechanism, again only
-group-level objects are available.
-
-NOTE: **Note:**
-Multiple group issue boards were originally introduced in [GitLab 10.0 Premium](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards) and
-one group issue board per group was made available in GitLab 10.6 Core.
-
-![Group issue board](img/group_issue_board.png)
-
-## Assignee lists **[PREMIUM]**
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5784) in GitLab 11.0 Premium.
-
-Like a regular list that shows all issues that have the list label, you can add
-an assignee list that shows all issues assigned to the given user.
-You can have a board with both label lists and assignee lists. To add an
-assignee list:
-
-1. Click **Add list**.
-1. Select the **Assignee list** tab.
-1. Search and click on the user you want to add as an assignee.
-
-Now that the assignee list is added, you can assign or unassign issues to that user
-by [dragging issues](#dragging-issues-between-lists) to and/or from an assignee list.
-To remove an assignee list, just as with a label list, click the trash icon.
-
-![Assignee lists](img/issue_board_assignee_lists.png)
-
-## Dragging issues between lists
+### Dragging issues between lists
When dragging issues between lists, different behavior occurs depending on the source list and the target list.
-| | To Backlog | To Closed | To label `B` list | To assignee `Bob` list |
-| --- | --- | --- | --- | --- |
-| From Backlog | - | Issue closed | `B` added | `Bob` assigned |
-| From Closed | Issue reopened | - | Issue reopened<br/>`B` added | Issue reopened<br/>`Bob` assigned |
-| From label `A` list | `A` removed | Issue closed | `A` removed<br/>`B` added | `Bob` assigned |
-| From assignee `Alice` list | `Alice` unassigned | Issue closed | `B` added | `Alice` unassigned<br/>`Bob` assigned |
-
-## Features per tier
-
-Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/), as shown in the following table:
-
-| Tier | Number of Project Issue Boards | Number of Group Issue Boards | Configurable Issue Boards | Assignee Lists
-| --- | --- | --- | --- | --- | --- |
-| Core | 1 | 1 | No | No |
-| Starter | Multiple | 1 | Yes | No |
-| Premium | Multiple | Multiple | Yes | Yes |
-| Ultimate | Multiple | Multiple | Yes | Yes |
+| | To Open | To Closed | To label `B` list | To assignee `Bob` list |
+|----------------------------|--------------------|--------------|------------------------------|---------------------------------------|
+| From Open | - | Issue closed | `B` added | `Bob` assigned |
+| From Closed | Issue reopened | - | Issue reopened<br/>`B` added | Issue reopened<br/>`Bob` assigned |
+| From label `A` list | `A` removed | Issue closed | `A` removed<br/>`B` added | `Bob` assigned |
+| From assignee `Alice` list | `Alice` unassigned | Issue closed | `B` added | `Alice` unassigned<br/>`Bob` assigned |
## Tips
diff --git a/doc/user/project/issues/automatic_issue_closing.md b/doc/user/project/issues/automatic_issue_closing.md
index b9607243c8a..afb7d9ada5f 100644
--- a/doc/user/project/issues/automatic_issue_closing.md
+++ b/doc/user/project/issues/automatic_issue_closing.md
@@ -26,9 +26,12 @@ used:
```
Note that `%{issue_ref}` is a complex regular expression defined inside GitLab's
-source code that can match a reference to 1) a local issue (`#123`),
-2) a cross-project issue (`group/project#123`) or 3) a link to an issue
-(`https://gitlab.example.com/group/project/issues/123`).
+source code that can match references to:
+
+- A local issue (`#123`).
+- A cross-project issue (`group/project#123`).
+- A link to an issue
+ (`https://gitlab.example.com/group/project/issues/123`).
---
diff --git a/doc/user/project/issues/create_new_issue.md b/doc/user/project/issues/create_new_issue.md
index 1688edc1ee2..c33d1365001 100644
--- a/doc/user/project/issues/create_new_issue.md
+++ b/doc/user/project/issues/create_new_issue.md
@@ -58,3 +58,21 @@ body becomes the issue description. [Markdown] and [quick actions] are
supported.
![Bottom of a project issues page](img/new_issue_from_email.png)
+
+## New issue via URL with prefilled fields
+
+You can link directly to the new issue page for a given project, with prefilled
+field values using query string parameters in a URL. This is useful for embedding
+a URL in an external HTML page, and also certain scenarios where you want the user to
+create an issue with certain fields prefilled.
+
+The title, description, and description template fields can be prefilled using
+this method. The description and description template fields cannot be pre-entered
+in the same URL (since a description template just populates the description field).
+
+Follow these examples to form your new issue URL with prefilled fields.
+
+- For a new issue in the GitLab Community Edition project with a pre-entered title
+and a pre-entered description, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
+- For a new issue in the GitLab Community Edition project with a pre-entered title
+and a pre-entered description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
diff --git a/doc/user/project/issues/img/confidential_issues_index_page.png b/doc/user/project/issues/img/confidential_issues_index_page.png
index f3efe0ce04e..16979bf9ac2 100644
--- a/doc/user/project/issues/img/confidential_issues_index_page.png
+++ b/doc/user/project/issues/img/confidential_issues_index_page.png
Binary files differ
diff --git a/doc/user/project/issues/img/delete_issue.png b/doc/user/project/issues/img/delete_issue.png
index a356f52044e..87ea65956fc 100644
--- a/doc/user/project/issues/img/delete_issue.png
+++ b/doc/user/project/issues/img/delete_issue.png
Binary files differ
diff --git a/doc/user/project/issues/img/due_dates_create.png b/doc/user/project/issues/img/due_dates_create.png
index ece35d44213..392fb3553cb 100644
--- a/doc/user/project/issues/img/due_dates_create.png
+++ b/doc/user/project/issues/img/due_dates_create.png
Binary files differ
diff --git a/doc/user/project/issues/img/group_issues_list_view.png b/doc/user/project/issues/img/group_issues_list_view.png
index bba964076d0..c951a9e2dcd 100644
--- a/doc/user/project/issues/img/group_issues_list_view.png
+++ b/doc/user/project/issues/img/group_issues_list_view.png
Binary files differ
diff --git a/doc/user/project/issues/img/issue_board.png b/doc/user/project/issues/img/issue_board.png
index 87b1016cc76..df9d6f64985 100644
--- a/doc/user/project/issues/img/issue_board.png
+++ b/doc/user/project/issues/img/issue_board.png
Binary files differ
diff --git a/doc/user/project/issues/img/issue_template.png b/doc/user/project/issues/img/issue_template.png
index 0e4c8df897b..6cb2c07d27e 100644
--- a/doc/user/project/issues/img/issue_template.png
+++ b/doc/user/project/issues/img/issue_template.png
Binary files differ
diff --git a/doc/user/project/issues/img/new_issue_from_email.png b/doc/user/project/issues/img/new_issue_from_email.png
index 775ea0cdffb..6da899ea37c 100644
--- a/doc/user/project/issues/img/new_issue_from_email.png
+++ b/doc/user/project/issues/img/new_issue_from_email.png
Binary files differ
diff --git a/doc/user/project/issues/img/new_issue_from_issue_board.png b/doc/user/project/issues/img/new_issue_from_issue_board.png
index da892eff0a6..30a1ffb9011 100644
--- a/doc/user/project/issues/img/new_issue_from_issue_board.png
+++ b/doc/user/project/issues/img/new_issue_from_issue_board.png
Binary files differ
diff --git a/doc/user/project/issues/img/new_issue_from_projects_dashboard.png b/doc/user/project/issues/img/new_issue_from_projects_dashboard.png
index 4b9535f6b15..474ca2b45c0 100644
--- a/doc/user/project/issues/img/new_issue_from_projects_dashboard.png
+++ b/doc/user/project/issues/img/new_issue_from_projects_dashboard.png
Binary files differ
diff --git a/doc/user/project/issues/img/project_issues_list_view.png b/doc/user/project/issues/img/project_issues_list_view.png
index 584a81aab8a..c80bd58f5c9 100644
--- a/doc/user/project/issues/img/project_issues_list_view.png
+++ b/doc/user/project/issues/img/project_issues_list_view.png
Binary files differ
diff --git a/doc/user/project/issues/img/sidebar_confidential_issue.png b/doc/user/project/issues/img/sidebar_confidential_issue.png
index d99a1ca756e..a320f4dcfe5 100644
--- a/doc/user/project/issues/img/sidebar_confidential_issue.png
+++ b/doc/user/project/issues/img/sidebar_confidential_issue.png
Binary files differ
diff --git a/doc/user/project/issues/img/sidebar_move_issue.png b/doc/user/project/issues/img/sidebar_move_issue.png
index 1e688cec894..031284a24b2 100644
--- a/doc/user/project/issues/img/sidebar_move_issue.png
+++ b/doc/user/project/issues/img/sidebar_move_issue.png
Binary files differ
diff --git a/doc/user/project/issues/img/sidebar_not_confidential_issue.png b/doc/user/project/issues/img/sidebar_not_confidential_issue.png
index 2e6cbbc5b3a..c09f8204b37 100644
--- a/doc/user/project/issues/img/sidebar_not_confidential_issue.png
+++ b/doc/user/project/issues/img/sidebar_not_confidential_issue.png
Binary files differ
diff --git a/doc/user/project/issues/img/turn_off_confidentiality.png b/doc/user/project/issues/img/turn_off_confidentiality.png
index 248ae6522d6..04a85933071 100644
--- a/doc/user/project/issues/img/turn_off_confidentiality.png
+++ b/doc/user/project/issues/img/turn_off_confidentiality.png
Binary files differ
diff --git a/doc/user/project/issues/img/turn_on_confidentiality.png b/doc/user/project/issues/img/turn_on_confidentiality.png
index fac4c833699..fac360ca6dc 100644
--- a/doc/user/project/issues/img/turn_on_confidentiality.png
+++ b/doc/user/project/issues/img/turn_on_confidentiality.png
Binary files differ
diff --git a/doc/user/project/issues/issues_functionalities.md b/doc/user/project/issues/issues_functionalities.md
index 46f25417fde..d78721f8658 100644
--- a/doc/user/project/issues/issues_functionalities.md
+++ b/doc/user/project/issues/issues_functionalities.md
@@ -69,7 +69,7 @@ Learn more on the [Time Tracking documentation](../../../workflow/time_tracking.
#### 6. Due date
When you work on a tight schedule, and it's important to
-have a way to setup a deadline for implementations and for solving
+have a way to set up a deadline for implementations and for solving
problems. This can be facilitated by the [due date](due_dates.md)). Due dates
can be changed as many times as needed.
@@ -118,9 +118,12 @@ is the `project-name`, and `xxx` is the issue number.
#### 13. @mentions
-- Mentions: you can either `@mention` a user or a group present in your
-GitLab instance and they will be notified via todos and email, unless that
-person has disabled all notifications in their profile settings.
+- You can either `@mention` a user or a group present in your
+ GitLab instance and they will be notified via todos and email, unless that
+ person has disabled all notifications in their profile settings.
+- Mentions for yourself (the current logged in user),will be distinctly highlighted
+ in a different color, allowing you to easily see which comments involve you,
+ helping you focus on them quickly.
To change your [notification settings](../../../workflow/notifications.md) navigate to
**Profile Settings** > **Notifications** > **Global notification level**
diff --git a/doc/user/project/koding.md b/doc/user/project/koding.md
deleted file mode 100644
index 86e06a39e59..00000000000
--- a/doc/user/project/koding.md
+++ /dev/null
@@ -1,131 +0,0 @@
-# Koding integration
-
->**Notes:**
-- **As of GitLab 10.0, the Koding integration is deprecated and will be removed
- in a future version.**
-- [Introduced][ce-5909] in GitLab 8.11.
-
-This document will guide you through using Koding integration on GitLab in
-detail. For configuring and installing please follow the
-[administrator guide](../../administration/integration/koding.md).
-
-You can use Koding integration to run and develop your projects on GitLab. This
-will allow you and the users to test your project without leaving the browser.
-Koding handles projects as stacks which are basic recipes to define your
-environment for your project. With this integration you can automatically
-create a proper stack template for your projects. Currently auto-generated
-stack templates are designed to work with AWS which requires a valid AWS
-credential to be able to use these stacks. You can find more information about
-stacks and the other providers that you can use on Koding following the
-[Koding documentation][koding-docs].
-
-## Enable Integration
-
-You can enable Koding integration by providing the running Koding instance URL
-in Application Settings under **Admin area > Settings** (`/admin/application_settings`).
-
-![Enable Koding](img/koding_enable-koding.png)
-
-Once enabled you will see `Koding` link on your sidebar which leads you to
-Koding Landing page.
-
-![Koding Landing](img/koding_landing.png)
-
-You can navigate to running Koding instance from here. For more information and
-details about configuring the integration, please follow the
-[administrator guide](../../administration/integration/koding.md).
-
-## Set up Koding on Projects
-
-Once it's enabled, you will see some integration buttons on Project pages,
-Merge Requests etc. To get started working on a specific project you first need
-to create a `.koding.yml` file under your project root. You can easily do that
-by using `Set Up Koding` button which will be visible on every project's
-landing page;
-
-![Set Up Koding](img/koding_set-up-ide.png)
-
-Once you click this will open a New File page on GitLab with auto-generated
-`.koding.yml` content based on your server and repository configuration.
-
-![Commit .koding.yml](img/koding_commit-koding.yml.png)
-
-
-## Run a project on Koding
-
-If there is `.koding.yml` exists in your project root, you will see
-`Run in IDE (Koding)` button in your project landing page. You can initiate the
-process from here.
-
-![Run on Koding](img/koding_run-in-ide.png)
-
-This will open Koding defined in the settings in a new window and will start
-importing the project's stack file.
-
-![Import Stack](img/koding_stack-import.png)
-
-You should see the details of your repository imported into your Koding
-instance. Once it's completed it will lead you to the Stack Editor and from
-there you can start using your new stack integrated with your project on your
-GitLab instance. For details about what's next you can follow
-[this guide](https://www.koding.com/docs/creating-an-aws-stack) from step 8.
-
-Once stack initialized you will see the `README.md` content from your project
-in `Stack Build` wizard, this wizard will let you build the stack and import
-your project into it. **Once it's completed it will automatically open the
-related vm instead of importing from scratch**.
-
-![Stack Building](img/koding_start-build.png)
-
-This will take time depending on the required environment.
-
-![Stack Building in Progress](img/koding_build-in-progress.png)
-
-It usually takes ~4 min. to make it ready with a `t2.nano` instance on given
-AWS region. (`t2.nano` is default vm type on auto-generated stack template
-which can be manually changed).
-
-![Stack Building Success](img/koding_build-success.png)
-
-You can check out the `Build Logs` from this success modal as well.
-
-![Stack Build Logs](img/koding_build-logs.png)
-
-You can now `Start Coding`!
-
-![Edit On IDE](img/koding_edit-on-ide.png)
-
-## Try a Merge Request on IDE
-
-It's also possible to try a change on IDE before merging it. This flow only
-enabled if the target project has `.koding.yml` in it's target branch. You
-should see the alternative version of `Run in IDE (Koding)` button in merge
-request pages as well;
-
-![Run in IDE on MR](img/koding_run-mr-in-ide.png)
-
-This will again take you to Koding with proper arguments passed, which will
-allow Koding to modify the stack template provided by target branch. You can
-see the difference;
-
-![Different Branch for MR](img/koding_different-stack-on-mr-try.png)
-
-The flow for the branch stack is also same with the regular project flow.
-
-## Open GitLab from Koding
-
-Since stacks generated with import flow defined in previous steps, they have
-information about the repository they are belonging to. By using this
-information you can access to related GitLab page from stacks on your sidebar
-on Koding.
-
-![Open GitLab from Koding](img/koding_open-gitlab-from-koding.png)
-
-## Other links
-
-- [YouTube video on GitLab + Koding workflow][youtube]
-- [Koding documentation][koding-docs]
-
-[ce-5909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5909
-[youtube]: https://youtu.be/3wei5yv_Ye8
-[koding-docs]: https://www.koding.com/docs
diff --git a/doc/user/project/members/img/access_requests_management.png b/doc/user/project/members/img/access_requests_management.png
index 3693bed869b..8996d9564d7 100644
--- a/doc/user/project/members/img/access_requests_management.png
+++ b/doc/user/project/members/img/access_requests_management.png
Binary files differ
diff --git a/doc/user/project/members/img/add_new_user_to_project_settings.png b/doc/user/project/members/img/add_new_user_to_project_settings.png
index 40db600455f..e49ea1a3e3d 100644
--- a/doc/user/project/members/img/add_new_user_to_project_settings.png
+++ b/doc/user/project/members/img/add_new_user_to_project_settings.png
Binary files differ
diff --git a/doc/user/project/members/img/add_user_email_accept.png b/doc/user/project/members/img/add_user_email_accept.png
index 763b3ff463d..cbee9e08c70 100644
--- a/doc/user/project/members/img/add_user_email_accept.png
+++ b/doc/user/project/members/img/add_user_email_accept.png
Binary files differ
diff --git a/doc/user/project/members/img/add_user_import_members_from_another_project.png b/doc/user/project/members/img/add_user_import_members_from_another_project.png
index 0c32001098e..cb3b70bd4b5 100644
--- a/doc/user/project/members/img/add_user_import_members_from_another_project.png
+++ b/doc/user/project/members/img/add_user_import_members_from_another_project.png
Binary files differ
diff --git a/doc/user/project/members/img/add_user_members_menu.png b/doc/user/project/members/img/add_user_members_menu.png
index 8e61d15fe65..6f08088b52f 100644
--- a/doc/user/project/members/img/add_user_members_menu.png
+++ b/doc/user/project/members/img/add_user_members_menu.png
Binary files differ
diff --git a/doc/user/project/members/img/max_access_level.png b/doc/user/project/members/img/max_access_level.png
index 63f33f9d91d..42a0416ffbb 100644
--- a/doc/user/project/members/img/max_access_level.png
+++ b/doc/user/project/members/img/max_access_level.png
Binary files differ
diff --git a/doc/user/project/members/img/request_access_button.png b/doc/user/project/members/img/request_access_button.png
index 608baccb0ca..e8b490b91b8 100644
--- a/doc/user/project/members/img/request_access_button.png
+++ b/doc/user/project/members/img/request_access_button.png
Binary files differ
diff --git a/doc/user/project/members/img/withdraw_access_request_button.png b/doc/user/project/members/img/withdraw_access_request_button.png
index 6edd786b151..6a3172dfcdb 100644
--- a/doc/user/project/members/img/withdraw_access_request_button.png
+++ b/doc/user/project/members/img/withdraw_access_request_button.png
Binary files differ
diff --git a/doc/user/project/merge_requests/cherry_pick_changes.md b/doc/user/project/merge_requests/cherry_pick_changes.md
index 22ef11e4049..06b3779668b 100644
--- a/doc/user/project/merge_requests/cherry_pick_changes.md
+++ b/doc/user/project/merge_requests/cherry_pick_changes.md
@@ -12,9 +12,11 @@ to cherry-pick the changes introduced by that merge request.
![Cherry-pick Merge Request](img/cherry_pick_changes_mr.png)
-After you click that button, a modal will appear where you can choose to
-cherry-pick the changes directly into the selected branch or you can opt to
-create a new merge request with the cherry-pick changes
+After you click that button, a modal will appear showing a [branch filter search box](../repository/branches/index.md#branch-filter-search-box)
+where you can choose to either:
+
+- Cherry-pick the changes directly into the selected branch.
+- Create a new merge request with the cherry-picked changes.
## Cherry-picking a Commit
diff --git a/doc/user/project/merge_requests/img/allow_collaboration.png b/doc/user/project/merge_requests/img/allow_collaboration.png
index 75596e7d9ad..3c81e4c27b8 100644
--- a/doc/user/project/merge_requests/img/allow_collaboration.png
+++ b/doc/user/project/merge_requests/img/allow_collaboration.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png b/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png
index 7dc344f8cf6..c98821548f8 100644
--- a/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png
+++ b/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png b/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png
index 811b0998f85..8b51503419b 100644
--- a/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png
+++ b/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/comment-on-any-diff-line.png b/doc/user/project/merge_requests/img/comment-on-any-diff-line.png
new file mode 100644
index 00000000000..856ede41527
--- /dev/null
+++ b/doc/user/project/merge_requests/img/comment-on-any-diff-line.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/create_from_email.png b/doc/user/project/merge_requests/img/create_from_email.png
index 71eb4bf267d..610f0b3d0c1 100644
--- a/doc/user/project/merge_requests/img/create_from_email.png
+++ b/doc/user/project/merge_requests/img/create_from_email.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/filter_wip_merge_requests.png b/doc/user/project/merge_requests/img/filter_wip_merge_requests.png
new file mode 100644
index 00000000000..40913718385
--- /dev/null
+++ b/doc/user/project/merge_requests/img/filter_wip_merge_requests.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_conflict_editor.png b/doc/user/project/merge_requests/img/merge_conflict_editor.png
index 6660920c191..f10efbce5f5 100644
--- a/doc/user/project/merge_requests/img/merge_conflict_editor.png
+++ b/doc/user/project/merge_requests/img/merge_conflict_editor.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_request.png b/doc/user/project/merge_requests/img/merge_request.png
index 61b61122b11..c0a62bbaba0 100644
--- a/doc/user/project/merge_requests/img/merge_request.png
+++ b/doc/user/project/merge_requests/img/merge_request.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png b/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
index 4eee734ff8d..1cdac5ef573 100644
--- a/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
+++ b/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_request_pipeline.png b/doc/user/project/merge_requests/img/merge_request_pipeline.png
new file mode 100644
index 00000000000..183d9cb910b
--- /dev/null
+++ b/doc/user/project/merge_requests/img/merge_request_pipeline.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_request_widget.png b/doc/user/project/merge_requests/img/merge_request_widget.png
index 43a945c74d9..6c2317b29b5 100644
--- a/doc/user/project/merge_requests/img/merge_request_widget.png
+++ b/doc/user/project/merge_requests/img/merge_request_widget.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png
index d7f0535d3c5..9487264b41a 100644
--- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png
+++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png
index c43f76b058c..761690d1e0c 100644
--- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png
+++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png
index 9629ed99838..2a2101719ba 100644
--- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png
+++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png
index d0691437c65..70fa2efc855 100644
--- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png
+++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/project_merge_requests_list_view.png b/doc/user/project/merge_requests/img/project_merge_requests_list_view.png
index 702ec1a2949..457716d811c 100644
--- a/doc/user/project/merge_requests/img/project_merge_requests_list_view.png
+++ b/doc/user/project/merge_requests/img/project_merge_requests_list_view.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/remove_source_branch_status.png b/doc/user/project/merge_requests/img/remove_source_branch_status.png
index 1377fab54ec..afd93207e02 100644
--- a/doc/user/project/merge_requests/img/remove_source_branch_status.png
+++ b/doc/user/project/merge_requests/img/remove_source_branch_status.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/revert_changes_commit.png b/doc/user/project/merge_requests/img/revert_changes_commit.png
index a0663e130e9..c9dd0019024 100644
--- a/doc/user/project/merge_requests/img/revert_changes_commit.png
+++ b/doc/user/project/merge_requests/img/revert_changes_commit.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/revert_changes_mr.png b/doc/user/project/merge_requests/img/revert_changes_mr.png
index 8792018ee53..06b841b3002 100644
--- a/doc/user/project/merge_requests/img/revert_changes_mr.png
+++ b/doc/user/project/merge_requests/img/revert_changes_mr.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/squash_edit_form.png b/doc/user/project/merge_requests/img/squash_edit_form.png
index 496c6f44ea7..326d74b68cb 100644
--- a/doc/user/project/merge_requests/img/squash_edit_form.png
+++ b/doc/user/project/merge_requests/img/squash_edit_form.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/squash_mr_commits.png b/doc/user/project/merge_requests/img/squash_mr_commits.png
index 5fc6a8c48bb..dfc1ee38435 100644
--- a/doc/user/project/merge_requests/img/squash_mr_commits.png
+++ b/doc/user/project/merge_requests/img/squash_mr_commits.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/squash_mr_widget.png b/doc/user/project/merge_requests/img/squash_mr_widget.png
index 9cb458b2a35..81334ca9758 100644
--- a/doc/user/project/merge_requests/img/squash_mr_widget.png
+++ b/doc/user/project/merge_requests/img/squash_mr_widget.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/squash_squashed_commit.png b/doc/user/project/merge_requests/img/squash_squashed_commit.png
index 0cf5875f82c..458361c5490 100644
--- a/doc/user/project/merge_requests/img/squash_squashed_commit.png
+++ b/doc/user/project/merge_requests/img/squash_squashed_commit.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/versions.png b/doc/user/project/merge_requests/img/versions.png
index 3883fb4bc1c..8355fd62dcb 100644
--- a/doc/user/project/merge_requests/img/versions.png
+++ b/doc/user/project/merge_requests/img/versions.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/versions_compare.png b/doc/user/project/merge_requests/img/versions_compare.png
index f5bd85dc7c1..0957a0310ac 100644
--- a/doc/user/project/merge_requests/img/versions_compare.png
+++ b/doc/user/project/merge_requests/img/versions_compare.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/versions_dropdown.png b/doc/user/project/merge_requests/img/versions_dropdown.png
index cc70a5bf14b..831c92db2c0 100644
--- a/doc/user/project/merge_requests/img/versions_dropdown.png
+++ b/doc/user/project/merge_requests/img/versions_dropdown.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/versions_system_note.png b/doc/user/project/merge_requests/img/versions_system_note.png
index 90be6298d15..97d552692c9 100644
--- a/doc/user/project/merge_requests/img/versions_system_note.png
+++ b/doc/user/project/merge_requests/img/versions_system_note.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/wip_blocked_accept_button.png b/doc/user/project/merge_requests/img/wip_blocked_accept_button.png
index 0c492aca363..31f23be4d3d 100644
--- a/doc/user/project/merge_requests/img/wip_blocked_accept_button.png
+++ b/doc/user/project/merge_requests/img/wip_blocked_accept_button.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/wip_mark_as_wip.png b/doc/user/project/merge_requests/img/wip_mark_as_wip.png
index e405879b28a..2c2a263b316 100644
--- a/doc/user/project/merge_requests/img/wip_mark_as_wip.png
+++ b/doc/user/project/merge_requests/img/wip_mark_as_wip.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/wip_unmark_as_wip.png b/doc/user/project/merge_requests/img/wip_unmark_as_wip.png
index d7f8c419945..327ad9a8448 100644
--- a/doc/user/project/merge_requests/img/wip_unmark_as_wip.png
+++ b/doc/user/project/merge_requests/img/wip_unmark_as_wip.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 86ecf33ed31..a0e7c1c99d5 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -43,8 +43,7 @@ A. Consider you are a software developer working in a team:
1. You checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team
-1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) **[STARTER]**
-1. You build and test your changes with GitLab CI/CD
+1. You verify your changes with [JUnit test reports](../../../ci/junit_test_reports.md) in GitLab CI/CD
1. You request the approval from your manager
1. Your manager pushes a commit with his final review, [approves the merge request](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Starter)
1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#manual-actions) for GitLab CI/CD
@@ -142,6 +141,15 @@ you hide discussions that are no longer relevant.
[Read more about resolving discussion comments in merge requests reviews.](../../discussions/index.md)
+## Commenting on any file line in merge requests
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/13950) in GitLab 11.5.
+
+GitLab provides a way of leaving comments in any part of the file being changed
+in a Merge Request. To do so, click the **...** button in the gutter of the Merge Request diff UI to expand the diff lines and leave a comment, just as you would for a changed line.
+
+![Comment on any diff file line](img/comment-on-any-diff-line.png)
+
## Resolve conflicts
When a merge request has conflicts, GitLab may provide the option to resolve
@@ -167,9 +175,26 @@ administrator to do so.
![Create new merge requests by email](img/create_from_email.png)
+### Adding patches when creating a merge request via e-mail
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22723) in GitLab 11.5.
+
+You can add commits to the merge request being created by adding
+patches as attachments to the email. All attachments with a filename
+ending in `.patch` will be considered patches and they will be processed
+ordered by name.
+
+The combined size of the patches can be 2MB.
+
+If the source branch from the subject does not exist, it will be
+created from the repository's HEAD or the specified target branch to
+apply the patches. The target branch can be specified using the
+[`/target_branch` quick action](../quick_actions.md). If the source
+branch already exists, the patches will be applied on top of it.
+
## Find the merge request that introduced a change
-> **Note**: this feature was [implemented in GitLab 10.5](https://gitlab.com/gitlab-org/gitlab-ce/issues/2383).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2383) in GitLab 10.5.
When viewing the commit details page, GitLab will link to the merge request (or
merge requests, if it's in more than one) containing that commit.
@@ -206,9 +231,10 @@ have been marked as a **Work In Progress**.
## Merge request diff file navigation
-The diff view has a persistent dropdown for file navigation. As you scroll through
-diffs with a large number of files and/or many changes in those files, you can
-easily jump to any changed file through the dropdown navigation.
+When reviewing changes in the **Changes** tab the diff can be navigated using
+the file tree or file list. As you scroll through large diffs with many
+changes, you can quickly jump to any changed file using the file tree or file
+list.
![Merge request diff file navigation](img/merge_request_diff_file_navigation.png)
@@ -233,10 +259,67 @@ all your changes will be available to preview by anyone with the Review Apps lin
[Read more about Review Apps.](../../../ci/review_apps/index.md)
+## Pipeline status in merge requests
+
+If you've set up [GitLab CI/CD](../../../ci/README.md) in your project,
+you will be able to see:
+
+- Both pre and post-merge pipelines and the environment information if any.
+- Which deployments are in progress.
+
+If there's an [environment](../../../ci/environments.md) and the application is
+successfully deployed to it, the deployed environment and the link to the
+Review App will be shown as well.
+
+### Post-merge pipeline status
+
+When a merge request is merged, you can see the post-merge pipeline status of
+the branch the merge request was merged into. For example, when a merge request
+is merged into the master branch and then triggers a deployment to the staging
+environment.
+
+Deployments that are ongoing will be shown, as well as the deploying/deployed state
+for environments. If it's the first time the branch is deployed, the link
+will return a `404` error until done. During the deployment, the stop button will
+be disabled. If the pipeline fails to deploy, the deployment info will be hidden.
+
+![Merge request pipeline](img/merge_request_pipeline.png)
+
+For more information, [read about pipelines](../../../ci/pipelines.md).
+
## Bulk editing merge requests
Find out about [bulk editing merge requests](../../project/bulk_editing.md).
+## Troubleshooting
+
+Sometimes things don't go as expected in a merge request, here are some
+troubleshooting steps.
+
+### Merge request cannot retrieve the pipeline status
+
+This can occur for one of two reasons:
+
+* Sidekiq doesn't pick up the changes fast enough
+* Because of the bug described in [#41545](https://gitlab.com/gitlab-org/gitlab-ce/issues/41545)
+
+#### Sidekiq
+
+Sidekiq didn't process the CI state change fast enough. Please wait a few
+seconds and the status will update automatically.
+
+#### Bug
+
+Merge Request pipeline statuses can't be retrieved when the following occurs:
+
+1. A Merge Requst is created
+1. The Merge Request is closed
+1. Changes are made in the project
+1. The Merge Request is reopened
+
+To enable the pipeline status to be properly retrieved, close and reopen the
+Merge Request again.
+
## Tips
Here are some tips that will help you be more efficient with merge requests in
diff --git a/doc/user/project/merge_requests/versions.md b/doc/user/project/merge_requests/versions.md
index 610250ccf12..90500fd9c21 100644
--- a/doc/user/project/merge_requests/versions.md
+++ b/doc/user/project/merge_requests/versions.md
@@ -1,12 +1,12 @@
# Merge requests versions
->**Notes:**
-- [Introduced][ce-5467] in GitLab 8.12.
-- Comments are disabled while viewing outdated merge versions or comparing to
- versions other than base.
-- Merge request versions are based on push not on commit. So, if you pushed 5
- commits in a single push, it will be a single option in the dropdown. If you
- pushed 5 times, that will count for 5 options.
+> **Notes:**
+> - [Introduced][ce-5467] in GitLab 8.12.
+> - Comments are disabled while viewing outdated merge versions or comparing to
+> versions other than base.
+> - Merge request versions are based on push not on commit. So, if you pushed 5
+> commits in a single push, it will be a single option in the dropdown. If you
+> pushed 5 times, that will count for 5 options.
Every time you push to a branch that is tied to a merge request, a new version
of merge request diff is created. When you visit a merge request that contains
diff --git a/doc/user/project/merge_requests/work_in_progress_merge_requests.md b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
index f01da06fa6e..66ac7740157 100644
--- a/doc/user/project/merge_requests/work_in_progress_merge_requests.md
+++ b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
@@ -7,7 +7,7 @@ have been marked a **Work In Progress**.
![Blocked Accept Button](img/wip_blocked_accept_button.png)
To mark a merge request a Work In Progress, simply start its title with `[WIP]`
-or `WIP:`. As an alternative, you're also able to do it by sending a commit
+or `WIP:`. As an alternative, you're also able to do it by sending a commit
with its title starting with `wip` or `WIP` to the merge request's source branch.
![Mark as WIP](img/wip_mark_as_wip.png)
@@ -15,4 +15,11 @@ with its title starting with `wip` or `WIP` to the merge request's source branch
To allow a Work In Progress merge request to be accepted again when it's ready,
simply remove the `WIP` prefix.
-![Unark as WIP](img/wip_unmark_as_wip.png)
+![Unmark as WIP](img/wip_unmark_as_wip.png)
+
+## Filtering merge requests with WIP Status
+
+To filter merge requests with the `WIP` status, you can type `wip`
+and select the value for your filter from the merge request search input.
+
+![Filter WIP MRs](img/filter_wip_merge_requests.png)
diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone.png b/doc/user/project/milestones/img/milestones_new_group_milestone.png
index b6defab101d..a517f0f7537 100644
--- a/doc/user/project/milestones/img/milestones_new_group_milestone.png
+++ b/doc/user/project/milestones/img/milestones_new_group_milestone.png
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_new_project_milestone.png b/doc/user/project/milestones/img/milestones_new_project_milestone.png
index 9aaff7dfef1..482c73f6568 100644
--- a/doc/user/project/milestones/img/milestones_new_project_milestone.png
+++ b/doc/user/project/milestones/img/milestones_new_project_milestone.png
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_project_milestone_page.png b/doc/user/project/milestones/img/milestones_project_milestone_page.png
index 9717075b8d0..c17bf350aeb 100644
--- a/doc/user/project/milestones/img/milestones_project_milestone_page.png
+++ b/doc/user/project/milestones/img/milestones_project_milestone_page.png
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_promote_milestone.png b/doc/user/project/milestones/img/milestones_promote_milestone.png
index 5e7f94c316f..2ef85c5951d 100644
--- a/doc/user/project/milestones/img/milestones_promote_milestone.png
+++ b/doc/user/project/milestones/img/milestones_promote_milestone.png
Binary files differ
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index 632253db94c..7168fe63887 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -6,6 +6,26 @@ Milestones in GitLab are a way to track issues and merge requests created to ach
Milestones allow you to organize issues and merge requests into a cohesive group, with an optional start date and an optional due date.
+## Milestones as Agile sprints
+
+Milestones can be used as Agile sprints.
+Set the milestone start date and due date to represent
+the start and end of your Agile sprint.
+Set the milestone title to the name of your Agile sprint,
+such as `November 2018 sprint`.
+Add an issue to your Agile sprint by associating
+the milestone to the issue.
+
+## Milestones as releases
+
+Milestones can be used as releases.
+Set the milestone due date to represent the release date of your release.
+(And leave the milestone start date blank.)
+Set the the milestone title to the version of your release,
+such as `Version 9.4`.
+Add an issue to your release by associating
+the milestone to the issue.
+
## Project milestones and group milestones
- **Project milestones** can be assigned to issues or merge requests in that project only.
@@ -68,7 +88,8 @@ From [project issue boards](../issue_board.md), you can filter by both group mil
When filtering by milestone, in addition to choosing a specific project milestone or group milestone, you can choose a special milestone filter.
-- **No Milestone**: Show issues or merge requests with no assigned milestone.
+- **None**: Show issues or merge requests with no assigned milestone.
+- **Any**: Show issues or merge requests that have an assigned milestone.
- **Upcoming**: Show issues or merge requests that have been assigned the open milestone that has the next upcoming due date (i.e. nearest due date in the future).
- **Started**: Show issues or merge requests that have an assigned milestone with a start date that is before today.
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index 15455a54627..9a53036b4d1 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -60,7 +60,7 @@ Let's consider the following scenario:
hosted in private repositories and you have multiple CI jobs that make use
of these repositories.
-2. You invite a new [external user][ext]. CI jobs created by that user do not
+1. You invite a new [external user][ext]. CI jobs created by that user do not
have access to internal repositories, because the user also doesn't have the
access from within GitLab. You as an employee have to grant explicit access
for this user. This allows us to prevent from accidental data leakage.
@@ -205,16 +205,16 @@ With the update permission model we also extended the support for accessing
Container Registries for private projects.
> **Notes:**
-- GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
- for permissions. This makes the `image:` directive to not work with private
- projects automatically and it needs to be configured manually on Runner's host
- with a predefined account (for example administrator's personal account with
- access token created explicitly for this purpose). This issue is resolved with
- latest changes in GitLab Runner 1.8 which receives GitLab credentials with
- build data.
-- Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
- to pass a [personal access token][pat] instead of your password in order to
- login to GitLab's Container Registry.
+> - GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
+> for permissions. This makes the `image:` directive to not work with private
+> projects automatically and it needs to be configured manually on Runner's host
+> with a predefined account (for example administrator's personal account with
+> access token created explicitly for this purpose). This issue is resolved with
+> latest changes in GitLab Runner 1.8 which receives GitLab credentials with
+> build data.
+> - Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
+> to pass a [personal access token][pat] instead of your password in order to
+> login to GitLab's Container Registry.
Your jobs can access all container images that you would normally have access
to. The only implication is that you can push to the Container Registry of the
diff --git a/doc/user/project/pages/getting_started_part_three.md b/doc/user/project/pages/getting_started_part_three.md
index 61af1d2ab27..89b9621b8b9 100644
--- a/doc/user/project/pages/getting_started_part_three.md
+++ b/doc/user/project/pages/getting_started_part_three.md
@@ -1,5 +1,5 @@
---
-last_updated: 2018-02-16
+last_updated: 2018-08-16
author: Marcia Ramos
author_gitlab: marcia
level: beginner
@@ -28,7 +28,7 @@ Let's start from the beginning with [DNS records](#dns-records).
If you already know how they work and want to skip the introduction to DNS,
you may be interested in skipping it until the [TL;DR](#tl-dr) section below.
-## DNS Records
+### DNS Records
A Domain Name System (DNS) web service routes visitors to websites
by translating domain names (such as `www.example.com`) into the
@@ -64,22 +64,28 @@ for the most popular hosting services:
If your hosting service is not listed above, you can just try to
search the web for `how to add dns record on <my hosting service>`.
-### DNS A record
+#### DNS A record
In case you want to point a root domain (`example.com`) to your
GitLab Pages site, deployed to `namespace.gitlab.io`, you need to
log into your domain's admin control panel and add a DNS `A` record
pointing your domain to Pages' server IP address. For projects on
-GitLab.com, this IP is `52.167.214.135`. For projects living in
+GitLab.com, this IP is `35.185.44.232`. For projects living in
other GitLab instances (CE or EE), please contact your sysadmin
asking for this information (which IP address is Pages server
running on your instance).
**Practical Example:**
-![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated.png)
+![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated_2018.png)
-### DNS CNAME record
+NOTE: **Note:**
+Note that if you use your root domain for your GitLab Pages website **only**, and if
+your domain registrar supports this feature, you can add a DNS apex `CNAME`
+record instead of an `A` record. The main advantage of doing so is that when GitLab Pages
+IP on GitLab.com changes for whatever reason, you don't need to update your `A` record.
+
+#### DNS CNAME record
In case you want to point a subdomain (`hello-world.example.com`)
to your GitLab Pages site initially deployed to `namespace.gitlab.io`,
@@ -112,14 +118,14 @@ If the domain has multiple uses (e.g., you host email on it as well):
| From | DNS Record | To |
| ---- | ---------- | -- |
-| domain.com | A | 52.167.214.135 |
+| domain.com | A | 35.185.44.232 |
| domain.com | TXT | gitlab-pages-verification-code=00112233445566778899aabbccddeeff |
If the domain is dedicated to GitLab Pages use and no other services run on it:
| From | DNS Record | To |
| ---- | ---------- | -- |
-| subdomain.domain.com | CNAME | gitlab.io |
+| subdomain.domain.com | CNAME | namespace.gitlab.io |
| _gitlab-pages-verification-code.subdomain.domain.com | TXT | gitlab-pages-verification-code=00112233445566778899aabbccddeeff |
> **Notes**:
@@ -129,9 +135,11 @@ If the domain is dedicated to GitLab Pages use and no other services run on it:
> - **Do not** add any special chars after the default Pages
domain. E.g., **do not** point your `subdomain.domain.com` to
`namespace.gitlab.io.` or `namespace.gitlab.io/`.
-> - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) from `104.208.235.32` to `52.167.214.135`.
+> - GitLab Pages IP on GitLab.com [was changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) in 2017
+> - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2018/07/19/gcp-move-update/#gitlab-pages-and-custom-domains)
+from `52.167.214.135` to `35.185.44.232` in 2018
-## Add your custom domain to GitLab Pages settings
+### Add your custom domain to GitLab Pages settings
Once you've set the DNS record, you'll need navigate to your project's
**Setting > Pages** and click **+ New domain** to add your custom domain to
@@ -165,6 +173,18 @@ will fail and attempts to visit your domain will respond with a 404.
Read through the [general documentation on GitLab Pages](introduction.md#add-a-custom-domain-to-your-pages-website) to learn more about adding
custom domains to GitLab Pages sites.
+### Redirecting `www.domain.com` to `domain.com` with Cloudflare
+
+If you use Cloudflare, you can redirect `www` to `domain.com` without the need of adding both
+`www.domain.com` and `domain.com` to GitLab. This happens due to a [Cloudflare feature that creates
+a 301 redirect as a "page rule"](https://gitlab.com/gitlab-org/gitlab-ce/issues/48848#note_87314849) for redirecting `www.domain.com` to `domain.com`. In this case,
+you can use the following setup:
+
+- In Cloudflare, create a DNS `A` record pointing `domain.com` to `35.185.44.232`
+- In GitLab, add the domain to GitLab Pages
+- In Cloudflare, create a DNS `TXT` record to verify your domain
+- In Cloudflare, create a DNS `CNAME` record poiting `www` to `domain.com`
+
## SSL/TLS Certificates
Every GitLab Pages project on GitLab.com will be available under
@@ -192,7 +212,7 @@ security measure, necessary just for big companies, like banks and shoppings sit
with financial transactions.
Now we have a different picture. [According to Josh Aas](https://letsencrypt.org/2015/10/29/phishing-and-malware.html), Executive Director at [ISRG](https://en.wikipedia.org/wiki/Internet_Security_Research_Group):
-> _We’ve since come to realize that HTTPS is important for almost all websites. It’s important for any website that allows people to log in with a password, any website that [tracks its users](https://www.washingtonpost.com/news/the-switch/wp/2013/12/10/nsa-uses-google-cookies-to-pinpoint-targets-for-hacking/) in any way, any website that [doesn’t want its content altered](http://arstechnica.com/tech-policy/2014/09/why-comcasts-javascript-ad-injections-threaten-security-net-neutrality/), and for any site that offers content people might not want others to know they are consuming. We’ve also learned that any site not secured by HTTPS [can be used to attack other sites](http://krebsonsecurity.com/2015/04/dont-be-fodder-for-chinas-great-cannon/)._
+> _We’ve since come to realize that HTTPS is important for almost all websites. It’s important for any website that allows people to log in with a password, any website that [tracks its users](https://www.washingtonpost.com/news/the-switch/wp/2013/12/10/nsa-uses-google-cookies-to-pinpoint-targets-for-hacking/) in any way, any website that [doesn’t want its content altered](http://arstechnica.com/tech-policy/2014/09/why-comcasts-javascript-ad-injections-threaten-security-net-neutrality/), and for any site that offers content people might not want others to know they are consuming. We’ve also learned that any site not secured by HTTPS [can be used to attack other sites](https://krebsonsecurity.com/2015/04/dont-be-fodder-for-chinas-great-cannon/)._
Therefore, the reason why certificates are so important is that they encrypt
the connection between the **client** (you, me, your visitors)
diff --git a/doc/user/project/pages/getting_started_part_two.md b/doc/user/project/pages/getting_started_part_two.md
index 556bf1db116..b0560c2f44c 100644
--- a/doc/user/project/pages/getting_started_part_two.md
+++ b/doc/user/project/pages/getting_started_part_two.md
@@ -94,7 +94,7 @@ where you'll find its default URL.
>
> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but,
if you don't find yours among the templates, you'll need
-to configure your own `.gitlab-ci.yml`. Do do that, please
+to configure your own `.gitlab-ci.yml`. To do that, please
read through the article [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among
the [example projects](https://gitlab.com/pages). If you set
up a new one, please
diff --git a/doc/user/project/pages/img/dns_add_new_a_record_example_updated.png b/doc/user/project/pages/img/dns_add_new_a_record_example_updated.png
deleted file mode 100644
index 2661a497b91..00000000000
--- a/doc/user/project/pages/img/dns_add_new_a_record_example_updated.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png b/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png
new file mode 100644
index 00000000000..0150329d4b2
--- /dev/null
+++ b/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png
Binary files differ
diff --git a/doc/user/project/pages/img/icons/click.png b/doc/user/project/pages/img/icons/click.png
new file mode 100644
index 00000000000..daaf760ec08
--- /dev/null
+++ b/doc/user/project/pages/img/icons/click.png
Binary files differ
diff --git a/doc/user/project/pages/img/icons/cogs.png b/doc/user/project/pages/img/icons/cogs.png
new file mode 100644
index 00000000000..a12da1b5e8c
--- /dev/null
+++ b/doc/user/project/pages/img/icons/cogs.png
Binary files differ
diff --git a/doc/user/project/pages/img/icons/fork.png b/doc/user/project/pages/img/icons/fork.png
new file mode 100644
index 00000000000..e2c9577e7ce
--- /dev/null
+++ b/doc/user/project/pages/img/icons/fork.png
Binary files differ
diff --git a/doc/user/project/pages/img/icons/free.png b/doc/user/project/pages/img/icons/free.png
new file mode 100644
index 00000000000..3b8f8f6863e
--- /dev/null
+++ b/doc/user/project/pages/img/icons/free.png
Binary files differ
diff --git a/doc/user/project/pages/img/icons/lock.png b/doc/user/project/pages/img/icons/lock.png
new file mode 100644
index 00000000000..1c1f0b4457b
--- /dev/null
+++ b/doc/user/project/pages/img/icons/lock.png
Binary files differ
diff --git a/doc/user/project/pages/img/icons/monitor.png b/doc/user/project/pages/img/icons/monitor.png
new file mode 100644
index 00000000000..7b99d430eef
--- /dev/null
+++ b/doc/user/project/pages/img/icons/monitor.png
Binary files differ
diff --git a/doc/user/project/pages/img/icons/terminal.png b/doc/user/project/pages/img/icons/terminal.png
new file mode 100644
index 00000000000..ab5ae11310c
--- /dev/null
+++ b/doc/user/project/pages/img/icons/terminal.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_create_project.png b/doc/user/project/pages/img/pages_create_project.png
index be47f9d2a44..69e84b84984 100644
--- a/doc/user/project/pages/img/pages_create_project.png
+++ b/doc/user/project/pages/img/pages_create_project.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_dns_details.png b/doc/user/project/pages/img/pages_dns_details.png
index 274e98fde4d..3e57f43f7ba 100644
--- a/doc/user/project/pages/img/pages_dns_details.png
+++ b/doc/user/project/pages/img/pages_dns_details.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_multiple_domains.png b/doc/user/project/pages/img/pages_multiple_domains.png
index 6bc92db6b41..76c39101439 100644
--- a/doc/user/project/pages/img/pages_multiple_domains.png
+++ b/doc/user/project/pages/img/pages_multiple_domains.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_remove.png b/doc/user/project/pages/img/pages_remove.png
index b064310380e..10299880247 100644
--- a/doc/user/project/pages/img/pages_remove.png
+++ b/doc/user/project/pages/img/pages_remove.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_upload_cert.png b/doc/user/project/pages/img/pages_upload_cert.png
index dc431ea3fef..64e5f8eced1 100644
--- a/doc/user/project/pages/img/pages_upload_cert.png
+++ b/doc/user/project/pages/img/pages_upload_cert.png
Binary files differ
diff --git a/doc/user/project/pages/img/ssgs_pages.png b/doc/user/project/pages/img/ssgs_pages.png
new file mode 100644
index 00000000000..608881c8e31
--- /dev/null
+++ b/doc/user/project/pages/img/ssgs_pages.png
Binary files differ
diff --git a/doc/user/project/pages/img/verify_your_domain.png b/doc/user/project/pages/img/verify_your_domain.png
index 89c69cac9a5..d870f9e6505 100644
--- a/doc/user/project/pages/img/verify_your_domain.png
+++ b/doc/user/project/pages/img/verify_your_domain.png
Binary files differ
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index 205f0283107..60144fa1971 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -4,71 +4,180 @@ description: 'Learn how to use GitLab Pages to deploy a static website at no add
# GitLab Pages
-With GitLab Pages it's easy to publish your project website. GitLab Pages is a hosting service for static websites, at no additional cost.
-
-## Getting Started
-
-[Create a project from scratch](getting_started_part_two.md#create-a-project-from-scratch)
-to get you started quickly, or,
-alternatively, start from an existing project as follows:
-
-- 1. [Fork](../../../gitlab-basics/fork-project.md#how-to-fork-a-project) an [example project](https://gitlab.com/pages):
-by forking a project, you create a copy of the codebase you're forking from to start from a template instead of starting from scratch.
-- 2. Change a file to trigger a GitLab CI/CD pipeline: GitLab CI/CD will build and deploy your site to GitLab Pages.
-- 3. Visit your project's **Settings > Pages** to see your **website link**, and click on it. Bam! Your website is live! :)
-
-_Further steps (optional):_
-
-- 4. Remove the [fork relationship](getting_started_part_two.md#fork-a-project-to-get-started-from)
-(_You don't need the relationship unless you intent to contribute back to the example project you forked from_).
-- 5. Make it a [user/group website](getting_started_part_one.md#user-and-group-websites)
-
-**Watch a video with the steps above: https://www.youtube.com/watch?v=TWqh9MtT4Bg**
+**GitLab Pages is a feature that allows you to publish static websites
+directly from a repository in GitLab.**
+
+You can use it either for personal or business websites, such as
+portfolios, documentation, manifestos, and business presentations,
+and attribute any license to your content.
+
+<table class="borderless-table center fixed-table">
+ <tr>
+ <td style="width: 22%"><img src="img/icons/cogs.png" alt="SSGs" class="image-noshadow half-width"></td>
+ <td style="width: 4%">
+ <strong>
+ <i class="fa fa-angle-double-right" aria-hidden="true"></i>
+ </strong>
+ </td>
+ <td style="width: 22%"><img src="img/icons/monitor.png" alt="Websites" class="image-noshadow half-width"></td>
+ <td style="width: 4%">
+ <strong>
+ <i class="fa fa-angle-double-right" aria-hidden="true"></i>
+ </strong>
+ </td>
+ <td style="width: 22%"><img src="img/icons/free.png" alt="Pages is free" class="image-noshadow half-width"></td>
+ <td style="width: 4%">
+ <strong>
+ <i class="fa fa-angle-double-right" aria-hidden="true"></i>
+ </strong>
+ </td>
+ <td style="width: 22%"><img src="img/icons/lock.png" alt="Secure your website" class="image-noshadow half-width"></td>
+ </tr>
+ <tr>
+ <td><em>Use any static website generator or plain HTML</em></td>
+ <td></td>
+ <td><em>Create websites for your projects, groups, or user account</em></td>
+ <td></td>
+ <td><em>Host on GitLab.com for free, or on your own GitLab instance</em></td>
+ <td></td>
+ <td><em>Connect your custom domain(s) and TLS certificates</em></td>
+ </tr>
+</table>
+
+Pages is available for free for all GitLab.com users as well as for self-managed
+instances (GitLab Core, Starter, Premium, and Ultimate).
+
+## Overview
+
+<div class="row">
+<div class="col-md-9">
+<p style="margin-top: 18px;">
+To publish a website with Pages, you can use any Static Site Generator (SSG),
+such as Jekyll, Hugo, Middleman, Harp, Hexo, and Brunch, just to name a few. You can also
+publish any website written directly in plain HTML, CSS, and JavaScript.</p>
+<p>Pages does <strong>not</strong> support dynamic server-side processing, for instance, as <code>.php</code> and <code>.asp</code> requires. See this article to learn more about
+<a href="https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/">static websites vs dynamic websites</a>.</p>
+</div>
+<div class="col-md-3"><img src="img/ssgs_pages.png" alt="Examples of SSGs supported by Pages" class="image-noshadow middle display-block"></div>
+</div>
+
+### Availability
+
+If you're using GitLab.com, your website will be publicly available to the internet.
+If you're using self-managed instances (Core, Starter, Premium, or Ultimate),
+your websites will be published on your own server, according to the
+[Pages admin settings](../../../administration/pages/index.md) chosen by your sysdamin,
+who can opt for making them public or internal to your server.
+
+### How it works
+
+To use GitLab Pages, first you need to create a project in GitLab to upload your website's
+files to. These projects can be either public, internal, or private, at your own choice.
+GitLab will always deploy your website from a very specific folder called `public` in your
+repository. Note that when you create a new project in GitLab, a [repository](../repository/index.md)
+becomes available automatically.
+
+To deploy your site, GitLab will use its built-in tool called [GitLab CI/CD](../../../ci/README.md),
+that will build your site and publish it to the GitLab Pages server. The sequence of
+scripts that GitLab CI/CD runs to accomplish this task is created from a file named
+`.gitlab-ci.yml`, which you can [create and modify](getting_started_part_four.md) at will.
+
+You can either use GitLab's [default domain for GitLab Pages websites](getting_started_part_one.md#gitlab-pages-domain),
+`*.gitlab.io`, or your own domain (`example.com`). In that case, you'll
+need admin access to your domain's registrar (or control panel) to set it up with Pages.
+
+Optionally, when adding your own domain, you can add an SSL/TLS certificate to secure your
+site under the HTTPS protocol.
+
+## Getting started
+
+To get started with GitLab Pages, you can either [create a project from scratch](getting_started_part_two.md#create-a-project-from-scratch)
+or quickly start from copying an existing example project, as follows:
+
+1. Choose an [example project](https://gitlab.com/pages) to [fork](../../../gitlab-basics/fork-project.md#how-to-fork-a-project):
+ by forking a project, you create a copy of the codebase you're forking from to start from a template instead of starting from scratch.
+1. From the left sidebar, navigate to your project's **CI/CD > Pipelines** and click
+**Run pipeline** so that GitLab CI/CD will build and deploy your site to the server.
+1. Once the pipeline has finished successfully, find the link to visit your website from your
+ project's **Settings > Pages**.
+
+<table class="borderless-table center fixed-table middle width-80">
+ <tr>
+ <td style="width: 30%"><img src="img/icons/fork.png" alt="Fork" class="image-noshadow half-width"></td>
+ <td style="width: 10%">
+ <strong>
+ <i class="fa fa-angle-double-right" aria-hidden="true"></i>
+ </strong>
+ </td>
+ <td style="width: 30%"><img src="img/icons/terminal.png" alt="Deploy" class="image-noshadow half-width"></td>
+ <td style="width: 10%">
+ <strong>
+ <i class="fa fa-angle-double-right" aria-hidden="true"></i>
+ </strong>
+ </td>
+ <td style="width: 30%"><img src="img/icons/click.png" alt="Visit" class="image-noshadow half-width"></td>
+ </tr>
+ <tr>
+ <td><em>Fork an example project</em></td>
+ <td></td>
+ <td><em>Deploy your website</em></td>
+ <td></td>
+ <td><em>Visit your website's URL</em></td>
+ </tr>
+</table>
+
+Your website is then visible on your domain, and you can modify your files
+as you wish. For every modification pushed to your repository, GitLab CI/CD will run
+a new pipeline to publish your changes to the server.
+
+You can also take some optional further steps:
+
+- Remove the [fork relationship](getting_started_part_two.md#fork-a-project-to-get-started-from)
+ (_You don't need the relationship unless you intent to contribute back to the example project you forked from_).
+- Make it a [user/group website](getting_started_part_one.md#user-and-group-websites)
+
+**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> Watch a [video tutorial](https://www.youtube.com/watch?v=TWqh9MtT4Bg) with all the steps above!**
_Advanced options:_
- [Use a custom domain](getting_started_part_three.md#adding-your-custom-domain-to-gitlab-pages)
- Apply [SSL/TLS certification](getting_started_part_three.md#ssl-tls-certificates) to your custom domain
-## How Does It Work?
-
-With GitLab Pages you can create [static websites](getting_started_part_one.md#what-you-need-to-know-before-getting-started)
-for your GitLab projects, groups, or user accounts.
-
-It supports plain static content, such as HTML, and **all** [static site generators (SSGs)](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/), such as Jekyll, Middleman, Hexo, Hugo, and Pelican.
-
-Connect as many custom domains as you like and bring your own TLS certificate
-to secure them.
-
-Your files live in a project [repository](../repository/index.md) on GitLab.
-[GitLab CI](../../../ci/README.md) picks up those files and makes them available at, typically,
-`https://<username>.gitlab.io/<projectname>`. Please read through the docs on
-[GitLab Pages domains](getting_started_part_one.md#gitlab-pages-domain) for more info.
-
## Explore GitLab Pages
-Read the following tutorials to know more about:
+To learn more about GitLab Pages, read the following tutorials:
- [Static websites and GitLab Pages domains](getting_started_part_one.md): Understand what is a static website, and how GitLab Pages default domains work
- [Projects for GitLab Pages and URL structure](getting_started_part_two.md): Forking projects and creating new ones from scratch, understanding URLs structure and baseurls
-- [GitLab Pages custom domains and SSL/TLS Certificates](getting_started_part_three.md): How to add custom domains and subdomains to your website, configure DNS records, and SSL/TLS certificates
+- [GitLab Pages custom domains and SSL/TLS Certificates](getting_started_part_three.md): How to add custom domains and subdomains to your website, configure DNS records and SSL/TLS certificates
- [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md): Understand how to create your own `.gitlab-ci.yml` for your site
- [Technical aspects, custom 404 pages, limitations](introduction.md)
-- [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) (outdated)
-_Blog posts series about Static Site Generators (SSGs):_
+### GitLab Pages with Static Site Generators (SSGs)
+
+To understand more about SSGs, their advantages, and how to get the most from them
+with Pages, read through this series:
- [SSGs part 1: Static vs dynamic websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/)
- [SSGs part 2: Modern static site generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/)
- [SSGs part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
-_Blog posts for securing GitLab Pages custom domains with SSL/TLS certificates:_
+### GitLab Pages with SSL/TLS certificates
+
+If you're using GitLab Pages default domain (`.gitlab.io`), your website will be
+automatically secure and available under HTTPS. If you're using your own domain, you can
+optionally secure it with with SSL/TLS certificates. You can read the following
+tutorials to learn how to use these third-party certificates with GitLab Pages:
- [CloudFlare](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/)
-- [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) (outdated)
+- [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) (mind that although this article is out-of-date, it can still be useful to guide you through the basic steps)
## Advanced use
+There are quite some great examples of GitLab Pages websites built for some
+specific reasons. These examples can teach you some advanced techniques
+to use and adapt to your own needs:
+
- [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/)
- [GitLab CI: Run jobs sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
- [GitLab CI: Deployment & environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
@@ -80,10 +189,9 @@ _Blog posts for securing GitLab Pages custom domains with SSL/TLS certificates:_
Enable and configure GitLab Pages on your own instance (GitLab Community Edition and Enterprise Editions) with
the [admin guide](../../../administration/pages/index.md).
-**Watch the video: https://www.youtube.com/watch?v=dD8c7WNcc6s**
+**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> Watch a [video tutorial](https://www.youtube.com/watch?v=dD8c7WNcc6s) for getting started with GitLab Pages admin!**
## More information about GitLab Pages
-- For an overview, visit the [feature webpage](https://about.gitlab.com/features/pages/)
- Announcement (2016-12-24): ["We're bringing GitLab Pages to CE"](https://about.gitlab.com/2016/12/24/were-bringing-gitlab-pages-to-community-edition/)
- Announcement (2017-03-06): ["We are changing the IP of GitLab Pages on GitLab.com"](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/)
diff --git a/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png b/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png
index 3ccce4f9bb4..983f903ca72 100644
--- a/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png
+++ b/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png
Binary files differ
diff --git a/doc/user/project/pipelines/img/pipeline_schedule_play.png b/doc/user/project/pipelines/img/pipeline_schedule_play.png
index f594ceee19d..ec6eb0d156b 100644
--- a/doc/user/project/pipelines/img/pipeline_schedule_play.png
+++ b/doc/user/project/pipelines/img/pipeline_schedule_play.png
Binary files differ
diff --git a/doc/user/project/pipelines/img/pipeline_schedule_variables.png b/doc/user/project/pipelines/img/pipeline_schedule_variables.png
index 47a0c6f3697..74692add93a 100644
--- a/doc/user/project/pipelines/img/pipeline_schedule_variables.png
+++ b/doc/user/project/pipelines/img/pipeline_schedule_variables.png
Binary files differ
diff --git a/doc/user/project/pipelines/img/pipeline_schedules_list.png b/doc/user/project/pipelines/img/pipeline_schedules_list.png
index 2ab2061db94..541fe4f9b1d 100644
--- a/doc/user/project/pipelines/img/pipeline_schedules_list.png
+++ b/doc/user/project/pipelines/img/pipeline_schedules_list.png
Binary files differ
diff --git a/doc/user/project/pipelines/img/pipeline_schedules_new_form.png b/doc/user/project/pipelines/img/pipeline_schedules_new_form.png
index 5a0e5965992..95203ec861b 100644
--- a/doc/user/project/pipelines/img/pipeline_schedules_new_form.png
+++ b/doc/user/project/pipelines/img/pipeline_schedules_new_form.png
Binary files differ
diff --git a/doc/user/project/pipelines/img/pipeline_schedules_ownership.png b/doc/user/project/pipelines/img/pipeline_schedules_ownership.png
index 31ed83abb4d..8fc5c5fbc82 100644
--- a/doc/user/project/pipelines/img/pipeline_schedules_ownership.png
+++ b/doc/user/project/pipelines/img/pipeline_schedules_ownership.png
Binary files differ
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index 402989f4508..a8b47558c99 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -1,18 +1,18 @@
# Introduction to job artifacts
->**Notes:**
->- Since GitLab 8.2 and GitLab Runner 0.7.0, job artifacts that are created by
- GitLab Runner are uploaded to GitLab and are downloadable as a single archive
- (`tar.gz`) using the GitLab UI.
->- Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format
- changed to `ZIP`, and it is now possible to browse its contents, with the added
- ability of downloading the files separately.
->- Starting with GitLab 8.17, builds are renamed to jobs.
->- The artifacts browser will be available only for new artifacts that are sent
- to GitLab using GitLab Runner version 1.0 and up. It will not be possible to
- browse old artifacts already uploaded to GitLab.
+> **Notes:**
+> - Since GitLab 8.2 and GitLab Runner 0.7.0, job artifacts that are created by
+> GitLab Runner are uploaded to GitLab and are downloadable as a single archive
+> (`tar.gz`) using the GitLab UI.
+> - Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format
+> changed to `ZIP`, and it is now possible to browse its contents, with the added
+> ability of downloading the files separately.
+> - Starting with GitLab 8.17, builds are renamed to jobs.
+> - The artifacts browser will be available only for new artifacts that are sent
+> to GitLab using GitLab Runner version 1.0 and up. It will not be possible to
+> browse old artifacts already uploaded to GitLab.
>- This is the user documentation. For the administration guide see
- [administration/job_artifacts](../../../administration/job_artifacts.md).
+> [administration/job_artifacts](../../../administration/job_artifacts.md).
Artifacts is a list of files and directories which are attached to a job
after it completes successfully. This feature is enabled by default in all
@@ -46,14 +46,14 @@ For more examples on artifacts, follow the [artifacts reference in
## Browsing artifacts
->**Note:**
-With GitLab 9.2, PDFs, images, videos and other formats can be previewed
-directly in the job artifacts browser without the need to download them.
-
->**Note:**
-With [GitLab 10.1][ce-14399], HTML files in a public project can be previewed
-directly in a new tab without the need to download them when
-[GitLab Pages](../../../administration/pages/index.md) is enabled
+> **Note:**
+> With GitLab 9.2, PDFs, images, videos and other formats can be previewed
+> directly in the job artifacts browser without the need to download them.
+>
+> **Note:**
+> With [GitLab 10.1][ce-14399], HTML files in a public project can be previewed
+> directly in a new tab without the need to download them when
+> [GitLab Pages](../../../administration/pages/index.md) is enabled
After a job finishes, if you visit the job's specific page, there are three
buttons. You can download the artifacts archive or browse its contents, whereas
@@ -151,6 +151,20 @@ For example:
https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/master/browse?job=coverage
```
+There is also a URL to specific files, including html files that
+are shown in [GitLab Pages](../../../administration/pages/index.md):
+
+```
+https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/file/<path>?job=<job_name>
+```
+
+For example, when a job `coverage` creates the artifact `htmlcov/index.html`,
+you can access it at:
+
+```
+https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/master/file/htmlcov/index.html?job=coverage
+```
+
The latest builds are also exposed in the UI in various places. Specifically,
look for the download button in:
diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md
index a13b1b4561c..9daacc37994 100644
--- a/doc/user/project/pipelines/schedules.md
+++ b/doc/user/project/pipelines/schedules.md
@@ -1,9 +1,9 @@
# Pipeline Schedules
> **Notes**:
-- This feature was introduced in 9.1 as [Trigger Schedule][ce-10533].
-- In 9.2, the feature was [renamed to Pipeline Schedule][ce-10853].
-- Cron notation is parsed by [Rufus-Scheduler](https://github.com/jmettraux/rufus-scheduler).
+> - This feature was introduced in 9.1 as [Trigger Schedule][ce-10533].
+> - In 9.2, the feature was [renamed to Pipeline Schedule][ce-10853].
+> - Cron notation is parsed by [Rufus-Scheduler](https://github.com/jmettraux/rufus-scheduler).
Pipeline schedules can be used to run a pipeline at specific intervals, for example every
month on the 22nd for a certain branch.
@@ -19,7 +19,7 @@ In order to schedule a pipeline:
![New Schedule Form](img/pipeline_schedules_new_form.png)
->**Attention:**
+> **Attention:**
The pipelines won't be executed precisely, because schedules are handled by
Sidekiq, which runs according to its interval.
See [advanced admin configuration](#advanced-admin-configuration) for more
@@ -83,7 +83,7 @@ The next time a pipeline is scheduled, your credentials will be used.
![Schedules list](img/pipeline_schedules_ownership.png)
->**Note:**
+> **Note:**
When the owner of the schedule doesn't have the ability to create pipelines
anymore, due to e.g., being blocked or removed from the project, or lacking
the permission to run on protected branches or tags. When this happened, the
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index 14f2e522f01..15eacc48dfe 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -157,6 +157,10 @@ into your `README.md`:
![coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)
```
+### Environment Variables
+
+[Environment variables](../../../ci/variables/README.html#variables) can be set in an environment to be available to a runner.
+
[var]: ../../../ci/yaml/README.md#git-strategy
[coverage report]: #test-coverage-parsing
[timeout overriding]: ../../../ci/runners/README.html#setting-maximum-job-timeout-for-a-runner
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index 3bf63a22963..db706e5020e 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -76,7 +76,7 @@ You can specify a wildcard protected branch, which will protect all branches
matching the wildcard. For example:
| Wildcard Protected Branch | Matching Branches |
-|---------------------------+--------------------------------------------------------|
+|---------------------------|--------------------------------------------------------|
| `*-stable` | `production-stable`, `staging-stable` |
| `production/*` | `production/app-server`, `production/load-balancer` |
| `*gitlab*` | `gitlab`, `gitlab/staging`, `master/gitlab/production` |
diff --git a/doc/user/project/protected_tags.md b/doc/user/project/protected_tags.md
index a5eaf2e9835..3d8fff9f733 100644
--- a/doc/user/project/protected_tags.md
+++ b/doc/user/project/protected_tags.md
@@ -37,7 +37,7 @@ You can specify a wildcard protected tag, which will protect all tags
matching the wildcard. For example:
| Wildcard Protected Tag | Matching Tags |
-|------------------------+-------------------------------|
+|------------------------|-------------------------------|
| `v*` | `v1.0.0`, `version-9.1` |
| `*-deploy` | `march-deploy`, `1.0-deploy` |
| `*gitlab*` | `gitlab`, `gitlab/v1` |
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 8fdfd2a6f4d..85a03d125dd 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -1,46 +1,85 @@
# GitLab quick actions
-Quick actions are textual shortcuts for common actions on issues, merge requests
-or commits that are usually done by clicking buttons or dropdowns in GitLab's UI.
-You can enter these commands while creating a new issue or merge request, and
-in comments. Each command should be on a separate line in order to be properly
-detected and executed. The commands are removed from the issue, merge request or
-comment body before it is saved and will not be visible to anyone else.
-
-Below is a list of all of the available commands and descriptions about what they
-do.
-
-| Command | Action |
-|:---------------------------|:-------------|
-| `/close` | Close the issue or merge request |
-| `/reopen` | Reopen the issue or merge request |
-| `/merge` | Merge (when pipeline succeeds) |
-| `/title <New title>` | Change title |
-| `/assign @username` | Assign |
-| `/unassign` | Remove assignee |
-| `/milestone %milestone` | Set milestone |
-| `/remove_milestone` | Remove milestone |
-| `/label ~foo ~"bar baz"` | Add label(s) |
-| `/unlabel ~foo ~"bar baz"` | Remove all or specific label(s) |
-| `/relabel ~foo ~"bar baz"` | Replace all label(s) |
-| `/todo` | Add a todo |
-| `/done` | Mark todo as done |
-| `/subscribe` | Subscribe |
-| `/unsubscribe` | Unsubscribe |
-| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code> | Set due date |
-| `/remove_due_date` | Remove due date |
-| `/wip` | Toggle the Work In Progress status |
-| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate |
-| `/remove_estimate` | Remove estimated time |
-| <code>/spend &lt;time(1h 30m &#124; -1h 5m)&gt; &lt;date(YYYY-MM-DD)&gt;</code> | Add or subtract spent time; optionally, specify the date that time was spent on |
-| `/remove_time_spent` | Remove time spent |
-| `/target_branch <Branch Name>` | Set target branch for current merge request |
-| `/award :emoji:` | Toggle award for :emoji: |
-| `/board_move ~column` | Move issue to column on the board |
-| `/duplicate #issue` | Closes this issue and marks it as a duplicate of another issue |
-| `/move path/to/project` | Moves issue to another project |
-| `/tag v1.2.3 <message>` | Tags a commit with a given tag name and optional message |
-| `/tableflip` | Append the comment with `(╯°□°)╯︵ â”»â”â”»` |
-| `/shrug` | Append the comment with `¯\_(ツ)_/¯` |
-| <code>/copy_metadata #issue &#124; !merge_request</code> | Copy labels and milestone from other issue or merge request |
-| `/confidential` | Makes the issue confidential |
+Quick actions are textual shortcuts for common actions on issues, epics, merge requests,
+and commits that are usually done by clicking buttons or dropdowns in GitLab's UI.
+You can enter these commands while creating a new issue or merge request, or
+in comments of issues, epics, merge requests, and commits. Each command should be
+on a separate line in order to be properly detected and executed. Once executed,
+the commands are removed from the text body and not visible to anyone else.
+
+## Quick actions for issues and merge requests
+
+The following quick actions are applicable to both issues and merge requests threads,
+discussions, and descriptions:
+
+| Command | Action | Issue | Merge request |
+|:---------------------------|:------------------------------ |:------|:--------------|
+| `/tableflip <Comment>` | Append the comment with `(╯°□°)╯︵ â”»â”â”»` | ✓ | ✓ |
+| `/shrug <Comment>` | Append the comment with `¯\_(ツ)_/¯` | ✓ | ✓ |
+| `/todo` | Add a todo | ✓ | ✓ |
+| `/done` | Mark todo as done | ✓ | ✓ |
+| `/subscribe` | Subscribe | ✓ | ✓ |
+| `/unsubscribe` | Unsubscribe | ✓ | ✓ |
+| `/close` | Close | ✓ | ✓ |
+| `/reopen` | Reopen | ✓ | ✓ |
+| `/title <New title>` | Change title | ✓ | ✓ |
+| `/award :emoji:` | Toggle emoji award | ✓ | ✓ |
+| `/assign me` | Assign yourself | ✓ | ✓ |
+| `/assign @user` | Assign one user | ✓ | ✓ |
+| `/assign @user1 @user2` | Assign multiple users **[STARTER]** | ✓ | |
+| `/unassign` | Remove assignee(s) | ✓ | ✓ |
+| `/reassign @user1 @user2` | Change assignee | ✓ | ✓ |
+| `/milestone %milestone` | Set milestone | ✓ | ✓ |
+| `/remove_milestone` | Remove milestone | ✓ | ✓ |
+| `/label ~label1 ~label2` | Add label(s) | ✓ | ✓ |
+| `/unlabel ~label1 ~label2` | Remove all or specific label(s)| ✓ | ✓ |
+| `/relabel ~label1 ~label2` | Replace label | ✓ | ✓ |
+| <code>/copy_metadata #issue &#124; !merge_request</code> | Copy labels and milestone from other issue or merge request | ✓ | ✓ |
+| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate | ✓ | ✓ |
+| `/remove_estimate` | Remove time estimate | ✓ | ✓ |
+| <code>/spend &lt;time(1h 30m &#124; -1h 5m)&gt; &lt;date(YYYY-MM-DD)&gt;</code> | Add or subtract spent time; optionally, specify the date that time was spent on | ✓ | ✓ |
+| `/remove_time_spent` | Remove time spent | ✓ | ✓ |
+| `/lock` | Lock the discussion | ✓ | ✓ |
+| `/unlock` | Unlock the discussion | ✓ | ✓ |
+| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code>| Set due date | ✓ | |
+| `/remove_due_date` | Remove due date | ✓ | |
+| `/weight 0,1,2, ...` | Set weight **[STARTER]** | ✓ | |
+| `/clear_weight` | Clears weight **[STARTER]** | ✓ | |
+| `/epic <group&epic &#124; Epic URL>` | Add to epic **[ULTIMATE]** | ✓ | |
+| `/remove_epic` | Removes from epic **[ULTIMATE]** | ✓ | |
+| `/confidential` | Make confidential | ✓ | |
+| `/duplicate #issue` | Mark this issue as a duplicate of another issue | ✓ |
+| `/move path/to/project` | Move this issue to another project | ✓ | |
+| `/target_branch <Local branch Name>` | Set target branch | | ✓ |
+| `/wip` | Toggle the Work In Progress status | | ✓ |
+| `/merge` | Merge (when pipeline succeeds) | | ✓ |
+| `/create_merge_request <branch name>` | Create a new merge request starting from the current issue | ✓ | |
+
+
+## Quick actions for commit messages
+
+The following quick actions are applicable for commit messages:
+
+| Command | Action |
+|:------------------------|:------------------------------------------|
+| `/tag v1.2.3 <message>` | Tags this commit with an optional message |
+
+## Quick actions for Epics **[ULTIMATE]**
+
+The following quick actions are applicable for epics threads and description:
+
+| Command | Action |
+|:---------------------------|:----------------------------------------|
+| `/tableflip <Comment>` | Append the comment with `(╯°□°)╯︵ â”»â”â”»` |
+| `/shrug <Comment>` | Append the comment with `¯\_(ツ)_/¯` |
+| `/todo` | Add a todo |
+| `/done` | Mark todo as done |
+| `/subscribe` | Subscribe |
+| `/unsubscribe` | Unsubscribe |
+| `/close` | Close |
+| `/reopen` | Reopen |
+| `/title <New title>` | Change title |
+| `/award :emoji:` | Toggle emoji award |
+| `/label ~label1 ~label2` | Add label(s) |
+| `/unlabel ~label1 ~label2` | Remove all or specific label(s) |
+| `/relabel ~label1 ~label2` | Replace label |
diff --git a/doc/user/project/repository/branches/img/branch_filter_search_box.png b/doc/user/project/repository/branches/img/branch_filter_search_box.png
new file mode 100644
index 00000000000..c4364ef39f4
--- /dev/null
+++ b/doc/user/project/repository/branches/img/branch_filter_search_box.png
Binary files differ
diff --git a/doc/user/project/repository/branches/img/delete_merged_branches.png b/doc/user/project/repository/branches/img/delete_merged_branches.png
index 1856a624f74..649a758b95f 100644
--- a/doc/user/project/repository/branches/img/delete_merged_branches.png
+++ b/doc/user/project/repository/branches/img/delete_merged_branches.png
Binary files differ
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index 9d16a4c74f2..783081cec26 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -6,6 +6,7 @@ Read through GiLab's branching documentation:
- [Default branch](#default-branch)
- [Protected branches](../../protected_branches.md#protected-branches)
- [Delete merged branches](#delete-merged-branches)
+- [Branch filter search box](#branch-filter-search-box)
See also:
@@ -16,7 +17,7 @@ See also:
When you create a new [project](../../index.md), GitLab sets `master` as the default
branch for your project. You can choose another branch to be your project's
-default under your project's **Settings > General**.
+default under your project's **Settings > Repository**.
The default branch is the branch affected by the
[issue closing pattern](../../issues/automatic_issue_closing.md),
@@ -29,16 +30,30 @@ to learn more.
## Delete merged branches
-> [Introduced][ce-6449] in GitLab 8.14.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6449) in GitLab 8.14.
![Delete merged branches](img/delete_merged_branches.png)
This feature allows merged branches to be deleted in bulk. Only branches that
-have been merged and [are not protected][protected] will be deleted as part of
+have been merged and [are not protected](../../protected_branches.md) will be deleted as part of
this operation.
It's particularly useful to clean up old branches that were not deleted
automatically when a merge request was merged.
-[ce-6449]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6449 "Add button to delete all merged branches"
-[protected]: ../../protected_branches.md
+
+## Branch filter search box
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22166) in GitLab 11.5.
+
+![Branch filter search box](img/branch_filter_search_box.png)
+
+This feature allows you to search and select branches quickly. Search results appear in the following order:
+
+- Branches with names that matched search terms exactly.
+- Other branches with names that include search terms, sorted alphabetically.
+
+Sometimes when you have hundreds of branches you may want a more flexible matching pattern. In such cases you can use the following:
+
+- `^feature` will only match branch names that begin with 'feature'.
+- `feature$` will only match branch names that end with 'feature'.
diff --git a/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png b/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png
index 8e26d98f1b0..6e2ff33eebb 100644
--- a/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png
+++ b/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png
Binary files differ
diff --git a/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png b/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png
index 5c14df36d73..ae0a8696c6c 100644
--- a/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png
+++ b/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png
Binary files differ
diff --git a/doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.png b/doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.png
index 088ecfa6d89..e1d44f15f3f 100644
--- a/doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.png
+++ b/doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.png
Binary files differ
diff --git a/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.png b/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.png
index 4e3392406b1..763a677f94a 100644
--- a/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.png
+++ b/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.png
Binary files differ
diff --git a/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.png b/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.png
index 766970dee81..1b6fa3fc2e2 100644
--- a/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.png
+++ b/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.png
Binary files differ
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index d41be0989d2..c6239c8e41c 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -36,15 +36,16 @@ to be met:
## Generating a GPG key
->**Notes:**
-- If your Operating System has `gpg2` installed, replace `gpg` with `gpg2` in
- the following commands.
-- If Git is using `gpg` and you get errors like `secret key not available` or
- `gpg: signing failed: secret key not available`, run the following command to
- change to `gpg2`:
- ```
- git config --global gpg.program gpg2
- ```
+> **Notes:**
+> - If your Operating System has `gpg2` installed, replace `gpg` with `gpg2` in
+> the following commands.
+> - If Git is using `gpg` and you get errors like `secret key not available` or
+> `gpg: signing failed: secret key not available`, run the following command to
+> change to `gpg2`:
+>
+> ```
+> git config --global gpg.program gpg2
+> ```
If you don't already have a GPG key, the following steps will help you get
started:
@@ -55,6 +56,8 @@ started:
```sh
gpg --full-gen-key
```
+
+ _NOTE: In some cases like Gpg4win on Windows and other Mac OS versions the command here may be ` gpg --gen-key`_
This will spawn a series of questions.
diff --git a/doc/user/project/repository/img/compare_branches.png b/doc/user/project/repository/img/compare_branches.png
index d7ab587f030..52d5c518c45 100644
--- a/doc/user/project/repository/img/compare_branches.png
+++ b/doc/user/project/repository/img/compare_branches.png
Binary files differ
diff --git a/doc/user/project/repository/img/repository_languages.png b/doc/user/project/repository/img/repository_languages.png
new file mode 100644
index 00000000000..5977ad7faae
--- /dev/null
+++ b/doc/user/project/repository/img/repository_languages.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_new_branch_dropdown.png b/doc/user/project/repository/img/web_editor_new_branch_dropdown.png
index 31edb6bde3a..a6edea1fcce 100644
--- a/doc/user/project/repository/img/web_editor_new_branch_dropdown.png
+++ b/doc/user/project/repository/img/web_editor_new_branch_dropdown.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_new_branch_from_issue.png b/doc/user/project/repository/img/web_editor_new_branch_from_issue.png
index 4729f5383c0..4e156b8adc8 100644
--- a/doc/user/project/repository/img/web_editor_new_branch_from_issue.png
+++ b/doc/user/project/repository/img/web_editor_new_branch_from_issue.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_new_branch_page.png b/doc/user/project/repository/img/web_editor_new_branch_page.png
index 8d82f981527..7bb8b9e29e3 100644
--- a/doc/user/project/repository/img/web_editor_new_branch_page.png
+++ b/doc/user/project/repository/img/web_editor_new_branch_page.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_new_directory_dialog.png b/doc/user/project/repository/img/web_editor_new_directory_dialog.png
index 1c9beff8849..590989c360e 100644
--- a/doc/user/project/repository/img/web_editor_new_directory_dialog.png
+++ b/doc/user/project/repository/img/web_editor_new_directory_dialog.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_new_directory_dropdown.png b/doc/user/project/repository/img/web_editor_new_directory_dropdown.png
index ede691f6f74..efa3087fc0c 100644
--- a/doc/user/project/repository/img/web_editor_new_directory_dropdown.png
+++ b/doc/user/project/repository/img/web_editor_new_directory_dropdown.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_new_file_dropdown.png b/doc/user/project/repository/img/web_editor_new_file_dropdown.png
index 13a4d721039..b40fb1ce58d 100644
--- a/doc/user/project/repository/img/web_editor_new_file_dropdown.png
+++ b/doc/user/project/repository/img/web_editor_new_file_dropdown.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_new_push_widget.png b/doc/user/project/repository/img/web_editor_new_push_widget.png
index 77756876d4f..8957b5d6a6b 100644
--- a/doc/user/project/repository/img/web_editor_new_push_widget.png
+++ b/doc/user/project/repository/img/web_editor_new_push_widget.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_new_tag_dropdown.png b/doc/user/project/repository/img/web_editor_new_tag_dropdown.png
index b52d5cabdf2..33e8ed891b5 100644
--- a/doc/user/project/repository/img/web_editor_new_tag_dropdown.png
+++ b/doc/user/project/repository/img/web_editor_new_tag_dropdown.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_start_new_merge_request.png b/doc/user/project/repository/img/web_editor_start_new_merge_request.png
index 384e8320f15..85f4769661a 100644
--- a/doc/user/project/repository/img/web_editor_start_new_merge_request.png
+++ b/doc/user/project/repository/img/web_editor_start_new_merge_request.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_template_dropdown_buttons.png b/doc/user/project/repository/img/web_editor_template_dropdown_buttons.png
index f21183125f6..4608843b1f4 100644
--- a/doc/user/project/repository/img/web_editor_template_dropdown_buttons.png
+++ b/doc/user/project/repository/img/web_editor_template_dropdown_buttons.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_template_dropdown_first_file.png b/doc/user/project/repository/img/web_editor_template_dropdown_first_file.png
index 7f31c2a8887..a4440ec3cc9 100644
--- a/doc/user/project/repository/img/web_editor_template_dropdown_first_file.png
+++ b/doc/user/project/repository/img/web_editor_template_dropdown_first_file.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_upload_file_dialog.png b/doc/user/project/repository/img/web_editor_upload_file_dialog.png
index 04e951406ad..c0e9a99aa61 100644
--- a/doc/user/project/repository/img/web_editor_upload_file_dialog.png
+++ b/doc/user/project/repository/img/web_editor_upload_file_dialog.png
Binary files differ
diff --git a/doc/user/project/repository/img/web_editor_upload_file_dropdown.png b/doc/user/project/repository/img/web_editor_upload_file_dropdown.png
index b8c766d4b99..c80a9ae4b3d 100644
--- a/doc/user/project/repository/img/web_editor_upload_file_dropdown.png
+++ b/doc/user/project/repository/img/web_editor_upload_file_dropdown.png
Binary files differ
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 704c1777e62..1710bba2fd0 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -53,6 +53,46 @@ To get started with the command line, please read through the
Use GitLab's [file finder](../../../workflow/file_finder.md) to search for files in a repository.
+### Supported markup languages and extensions
+
+GitLab supports a number of markup languages (sometimes called [lightweight
+markup languages](https://en.wikipedia.org/wiki/Lightweight_markup_language))
+that you can use for the content of your files in a repository. They are mostly
+used for documentation purposes.
+
+Just pick the right extension for your files and GitLab will render them
+according to the markup language.
+
+| Markup language | Extensions |
+| --------------- | ---------- |
+| Plain text | `txt` |
+| [Markdown](../../markdown.md) | `mdown`, `mkd`, `mkdn`, `md`, `markdown` |
+| [reStructuredText](http://docutils.sourceforge.net/rst.html) | `rst` |
+| [Asciidoc](https://asciidoctor.org/docs/what-is-asciidoc/) | `adoc`, `ad`, `asciidoc` |
+| [Textile](https://txstyle.org/) | `textile` |
+| [rdoc](http://rdoc.sourceforge.net/doc/index.html) | `rdoc` |
+| [Orgmode](https://orgmode.org/) | `org` |
+| [creole](http://www.wikicreole.org/) | `creole` |
+| [Mediawiki](https://www.mediawiki.org/wiki/MediaWiki) | `wiki`, `mediawiki` |
+
+### Repository README and index files
+
+When a `README` or `index` file is present in a repository, its contents will be
+automatically pre-rendered by GitLab without opening it.
+
+They can either be plain text or have an extension of a
+[supported markup language](#supported-markup-languages-and-extensions):
+
+Some things to note about precedence:
+
+1. When both a `README` and an `index` file are present, the `README` will always
+ take precedence.
+1. When more than one file is present with different extensions, they are
+ ordered alphabetically, with the exception of a file without an extension
+ which will always be last in precedence. For example, `README.adoc` will take
+ precedence over `README.md`, and `README.rst` will take precedence over
+ `README`.
+
### Jupyter Notebook files
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2508) in GitLab 9.1
@@ -85,12 +125,13 @@ You can live preview changes submitted to a new branch with
With [GitLab Starter](https://about.gitlab.com/pricing/), you can also request
[approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers.
-To create, delete, and [branches](branches/index.md) via GitLab's UI:
+To create, delete, and view [branches](branches/index.md) via GitLab's UI:
- [Default branches](branches/index.md#default-branch)
- [Create a branch](web_editor.md#create-a-new-branch)
- [Protected branches](../protected_branches.md#protected-branches)
- [Delete merged branches](branches/index.md#delete-merged-branches)
+- [Branch filter search box](branches/index.md#branch-filter-search-box)
Alternatively, you can use the
[command line](../../../gitlab-basics/start-using-git.md#create-a-branch).
@@ -155,9 +196,25 @@ The repository graph displays visually the Git flow strategy used in that reposi
Find it under your project's **Repository > Graph**.
+## Repository Languages
+
+For the default branch of each repository, GitLab will determine what programming languages
+were used and display this on the projects pages. If this information is missing, it will
+be added after updating the default branch on the project. This process can take up to 5
+minutes.
+
+![Repository Languages bar](img/repository_languages.png)
+
+Not all files are detected, among others; documentation,
+vendored code, and most markup languages are excluded. This behaviour can be
+adjusted by overriding the default. For example, to enable `.proto` files to be
+detected, add the following to `.gitattributes` in the root of your repository.
+
+> *.proto linguist-detectable=true
+
## Compare
-Select branches to compare and view the changes inline:
+Select branches to compare using the [branch filter search box](branches/index.md#branch-filter-search-box), then click the **Compare** button to view the changes inline:
![compare branches](img/compare_branches.png)
diff --git a/doc/user/project/repository/reducing_the_repo_size_using_git.md b/doc/user/project/repository/reducing_the_repo_size_using_git.md
index a06ecc3220f..d534c8cbe4b 100644
--- a/doc/user/project/repository/reducing_the_repo_size_using_git.md
+++ b/doc/user/project/repository/reducing_the_repo_size_using_git.md
@@ -33,11 +33,10 @@ following method.
## Using `git filter-branch` to purge files
->
-**Warning:**
-Make sure to first make a copy of your repository since rewriting history will
-purge the files and information you are about to delete. Also make sure to
-inform any collaborators to not use `pull` after your changes, but use `rebase`.
+> **Warning:**
+> Make sure to first make a copy of your repository since rewriting history will
+> purge the files and information you are about to delete. Also make sure to
+> inform any collaborators to not use `pull` after your changes, but use `rebase`.
1. Navigate to your repository:
@@ -71,10 +70,10 @@ inform any collaborators to not use `pull` after your changes, but use `rebase`.
Your repository should now be below the size limit.
->**Note:**
-As an alternative to `filter-branch`, you can use the `bfg` tool with a
-command like: `bfg --delete-files path/to/big_file.mpg`. Read the
-[BFG Repo-Cleaner][bfg] documentation for more information.
+> **Note:**
+> As an alternative to `filter-branch`, you can use the `bfg` tool with a
+> command like: `bfg --delete-files path/to/big_file.mpg`. Read the
+> [BFG Repo-Cleaner][bfg] documentation for more information.
[admin-repo-size]: https://docs.gitlab.com/ee/user/admin_area/settings/account_and_limit_settings.html#repository-size-limit
[bfg]: https://rtyley.github.io/bfg-repo-cleaner/
diff --git a/doc/user/project/repository/web_editor.md b/doc/user/project/repository/web_editor.md
index 33c9a1a4d6b..035028c9266 100644
--- a/doc/user/project/repository/web_editor.md
+++ b/doc/user/project/repository/web_editor.md
@@ -177,5 +177,9 @@ you commit the changes you will be taken to a new merge request form.
![Start a new merge request with these changes](img/web_editor_start_new_merge_request.png)
+If you'd prefer _not_ to use your primary email address for commits created
+through the web editor, you can choose to use another of your linked email
+addresses from the **User Settings > Edit Profile** page.
+
[ce-2808]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2808
[issue closing pattern]: ../issues/automatic_issue_closing.md
diff --git a/doc/user/project/settings/img/import_export_download_export.png b/doc/user/project/settings/img/import_export_download_export.png
index 4945590e3e8..668254073e8 100644
--- a/doc/user/project/settings/img/import_export_download_export.png
+++ b/doc/user/project/settings/img/import_export_download_export.png
Binary files differ
diff --git a/doc/user/project/settings/img/import_export_export_button.png b/doc/user/project/settings/img/import_export_export_button.png
index eef79821f8b..7f21bb2335b 100644
--- a/doc/user/project/settings/img/import_export_export_button.png
+++ b/doc/user/project/settings/img/import_export_export_button.png
Binary files differ
diff --git a/doc/user/project/settings/img/import_export_new_project.png b/doc/user/project/settings/img/import_export_new_project.png
index 9dd509dc4a0..b335700c5be 100644
--- a/doc/user/project/settings/img/import_export_new_project.png
+++ b/doc/user/project/settings/img/import_export_new_project.png
Binary files differ
diff --git a/doc/user/project/settings/img/import_export_select_file.png b/doc/user/project/settings/img/import_export_select_file.png
index fb831dca32b..e1e5e031d81 100644
--- a/doc/user/project/settings/img/import_export_select_file.png
+++ b/doc/user/project/settings/img/import_export_select_file.png
Binary files differ
diff --git a/doc/user/project/settings/img/settings_edit_button.png b/doc/user/project/settings/img/settings_edit_button.png
index 9f3a8330e3a..32bcda03c7e 100644
--- a/doc/user/project/settings/img/settings_edit_button.png
+++ b/doc/user/project/settings/img/settings_edit_button.png
Binary files differ
diff --git a/doc/user/project/settings/img/sharing_and_permissions_settings.png b/doc/user/project/settings/img/sharing_and_permissions_settings.png
index 0f9cf9512af..f5e3e32f95c 100644
--- a/doc/user/project/settings/img/sharing_and_permissions_settings.png
+++ b/doc/user/project/settings/img/sharing_and_permissions_settings.png
Binary files differ
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 084d1161633..d6754372816 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -18,6 +18,8 @@ Adjust your project's name, description, avatar, [default branch](../repository/
![general project settings](img/general_settings.png)
+The project description also partially supports [standard markdown](../../markdown.md#standard-markdown). You can use [emphasis](../../markdown.md#emphasis), [links](../../markdown.md#links), and [line-breaks](../../markdown.md#line-breaks) to add more context to the project description.
+
### Sharing and permissions
Set up your project's access, [visibility](../../../public_access/public_access.md), and enable [Container Registry](../container_registry.md) for your projects:
diff --git a/doc/user/project/web_ide/img/open_web_ide.png b/doc/user/project/web_ide/img/open_web_ide.png
index d1192daf506..02a5a564472 100644
--- a/doc/user/project/web_ide/img/open_web_ide.png
+++ b/doc/user/project/web_ide/img/open_web_ide.png
Binary files differ
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 16969b2c527..9429b1268f0 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -78,13 +78,14 @@ switching to a different branch.
The Web IDE can be used to preview JavaScript projects right in the browser.
This feature uses CodeSandbox to compile and bundle the JavaScript used to
-preview the web application. On public projects, an `Open in CodeSandbox`
-button is visible which will transfer the contents of the project into a
-CodeSandbox project to share with others.
-**Note** this button is not visible on private or internal projects.
+preview the web application.
![Web IDE Client Side Evaluation](img/clientside_evaluation.png)
+Additionally, for public projects an `Open in CodeSandbox` button is available
+to transfer the contents of the project into a public CodeSandbox project to
+quickly share your project with others.
+
### Enabling Client Side Evaluation
The Client Side Evaluation feature needs to be enabled in the GitLab instances
diff --git a/doc/user/project/wiki/img/wiki_create_home_page.png b/doc/user/project/wiki/img/wiki_create_home_page.png
index f50f564034c..658af33d76e 100644
--- a/doc/user/project/wiki/img/wiki_create_home_page.png
+++ b/doc/user/project/wiki/img/wiki_create_home_page.png
Binary files differ
diff --git a/doc/user/project/wiki/img/wiki_create_new_page.png b/doc/user/project/wiki/img/wiki_create_new_page.png
index c19124a8923..8954ec0d3a8 100644
--- a/doc/user/project/wiki/img/wiki_create_new_page.png
+++ b/doc/user/project/wiki/img/wiki_create_new_page.png
Binary files differ
diff --git a/doc/user/project/wiki/img/wiki_create_new_page_modal.png b/doc/user/project/wiki/img/wiki_create_new_page_modal.png
index ece437967dc..b800508901b 100644
--- a/doc/user/project/wiki/img/wiki_create_new_page_modal.png
+++ b/doc/user/project/wiki/img/wiki_create_new_page_modal.png
Binary files differ
diff --git a/doc/user/project/wiki/img/wiki_move_page_1.png b/doc/user/project/wiki/img/wiki_move_page_1.png
index 0331c9d3a5c..189fcc9a845 100644
--- a/doc/user/project/wiki/img/wiki_move_page_1.png
+++ b/doc/user/project/wiki/img/wiki_move_page_1.png
Binary files differ
diff --git a/doc/user/project/wiki/img/wiki_move_page_2.png b/doc/user/project/wiki/img/wiki_move_page_2.png
index a8e0c055051..63e6ddb29c1 100644
--- a/doc/user/project/wiki/img/wiki_move_page_2.png
+++ b/doc/user/project/wiki/img/wiki_move_page_2.png
Binary files differ
diff --git a/doc/user/project/wiki/img/wiki_page_history.png b/doc/user/project/wiki/img/wiki_page_history.png
index 0e6af1b468d..5a1ae295ed2 100644
--- a/doc/user/project/wiki/img/wiki_page_history.png
+++ b/doc/user/project/wiki/img/wiki_page_history.png
Binary files differ
diff --git a/doc/user/project/wiki/img/wiki_sidebar.png b/doc/user/project/wiki/img/wiki_sidebar.png
index 59814e2a06e..ff39c861a73 100644
--- a/doc/user/project/wiki/img/wiki_sidebar.png
+++ b/doc/user/project/wiki/img/wiki_sidebar.png
Binary files differ
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index ad0ef60373c..127a30d6669 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -40,11 +40,6 @@ support Markdown, RDoc and AsciiDoc. For Markdown based pages, all the
[Markdown features](../../markdown.md) are supported and for links there is
some [wiki specific](../../markdown.md#wiki-specific-markdown) behavior.
->**Note:**
-The wiki is based on a Git repository and contains only text files. Uploading
-files via the web interface will upload them in GitLab itself, and they will
-not be available if you clone the wiki repo locally.
-
In the web interface the commit message is optional, but the GitLab Wiki is
based on Git and needs a commit message, so one will be created for you if you
do not enter one.
@@ -53,6 +48,14 @@ When you're ready, click the **Create page** and the new page will be created.
![New page](img/wiki_create_new_page.png)
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33475) in GitLab 11.3.
+
+Starting with GitLab 11.3, any file that is uploaded to the wiki via GitLab's
+interface will be stored in the wiki Git repository, and it will be available
+if you clone the wiki repository locally. All uploaded files prior to GitLab
+11.3 are stored in GitLab itself. If you want them to be part of the wiki's Git
+repository, you will have to upload them again.
+
## Editing a wiki page
To edit a page, simply click on the **Edit** button. From there on, you can
diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md
index 918daee5d9f..45c306f5988 100644
--- a/doc/user/reserved_names.md
+++ b/doc/user/reserved_names.md
@@ -13,7 +13,7 @@ For a list of words that are not allowed to be used as group or project names, s
It is currently not possible to create a project with the following names:
-- -
+- \-
- badges
- blame
- blob
@@ -40,7 +40,7 @@ It is currently not possible to create a project with the following names:
Currently the following names are reserved as top level groups:
- 503.html
-- -
+- \-
- .well-known
- 404.html
- 422.html
@@ -68,7 +68,6 @@ Currently the following names are reserved as top level groups:
- import
- invites
- jwt
-- koding
- notification_settings
- oauth
- profile
@@ -88,7 +87,7 @@ Currently the following names are reserved as top level groups:
These group names are unavailable as subgroup names:
-- -
+- \-
- activity
- analytics
- audit_events
diff --git a/doc/user/search/img/dashboard_links.png b/doc/user/search/img/dashboard_links.png
new file mode 100644
index 00000000000..2c472c7e464
--- /dev/null
+++ b/doc/user/search/img/dashboard_links.png
Binary files differ
diff --git a/doc/user/search/img/issue_search_by_term.png b/doc/user/search/img/issue_search_by_term.png
index 3cefa3adb8b..64450c6a891 100644
--- a/doc/user/search/img/issue_search_by_term.png
+++ b/doc/user/search/img/issue_search_by_term.png
Binary files differ
diff --git a/doc/user/search/img/issue_search_filter.png b/doc/user/search/img/issue_search_filter.png
index f357abd6bac..d4de3ff7656 100644
--- a/doc/user/search/img/issue_search_filter.png
+++ b/doc/user/search/img/issue_search_filter.png
Binary files differ
diff --git a/doc/user/search/img/issues_assigned_to_you.png b/doc/user/search/img/issues_assigned_to_you.png
index 36c670eedd5..d2fff5e9a67 100644
--- a/doc/user/search/img/issues_assigned_to_you.png
+++ b/doc/user/search/img/issues_assigned_to_you.png
Binary files differ
diff --git a/doc/user/search/img/issues_filter_none_any.png b/doc/user/search/img/issues_filter_none_any.png
new file mode 100644
index 00000000000..9682fc55315
--- /dev/null
+++ b/doc/user/search/img/issues_filter_none_any.png
Binary files differ
diff --git a/doc/user/search/img/issues_mrs_shortcut.png b/doc/user/search/img/issues_mrs_shortcut.png
index cf43df98aa0..2fe1350c806 100644
--- a/doc/user/search/img/issues_mrs_shortcut.png
+++ b/doc/user/search/img/issues_mrs_shortcut.png
Binary files differ
diff --git a/doc/user/search/img/left_menu_bar.png b/doc/user/search/img/left_menu_bar.png
deleted file mode 100644
index d68a71cba8e..00000000000
--- a/doc/user/search/img/left_menu_bar.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/search/img/project_search.png b/doc/user/search/img/project_search.png
index 0b76d7d6038..b2525b2c771 100644
--- a/doc/user/search/img/project_search.png
+++ b/doc/user/search/img/project_search.png
Binary files differ
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 4f1b96b775c..78c1294346b 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -2,27 +2,27 @@
## Issues and merge requests
-To search through issues and merge requests in multiple projects, you can use the left-sidebar.
+To search through issues and merge requests in multiple projects, you can use the **Issues** or **Merge Requests** links
+in the top-right part of your screen.
-Click the menu bar, then **Issues** or **Merge Requests**, which work in the same way,
-therefore, the following notes are valid for both.
+Both of them work in the same way, therefore, the following notes are valid for both.
The number displayed on their right represents the number of issues and merge requests assigned to you.
-![menu bar - issues and MRs assigned to you](img/left_menu_bar.png)
+![issues and MRs dashboard links](img/dashboard_links.png)
When you click **Issues**, you'll see the opened issues assigned to you straight away:
![Issues assigned to you](img/issues_assigned_to_you.png)
-You can filter them by **Author**, **Assignee**, **Milestone**, and **Labels**,
-searching through **Open**, **Closed**, and **All** issues.
+You can search through **Open**, **Closed**, or **All** issues.
-Of course, you can combine all filters together.
+You can also filter the results using the search and filter field. This works in the same way as the ones found in the
+per project pages described below.
### Issues and MRs assigned to you or created by you
-You'll find a shortcut to issues and merge requests create by you or assigned to you
+You'll also find shortcuts to issues and merge requests created by you or assigned to you
on the search field on the top-right of your screen:
![shortcut to your issues and mrs](img/issues_mrs_shortcut.png)
@@ -40,6 +40,16 @@ The same process is valid for merge requests. Navigate to your project's **Merge
and click **Search or filter results...**. Merge requests can be filtered by author, assignee,
milestone, and label.
+### Filtering by **None** / **Any**
+
+Some filter fields like milestone and assignee, allow you to filter by **None** or **Any**.
+
+![filter by none any](img/issues_filter_none_any.png)
+
+Selecting **None** returns results that have an empty value for that field. E.g.: no milestone, no assignee.
+
+Selecting **Any** does the opposite. It returns results that have a non-empty value for that field.
+
### Searching for specific terms
You can filter issues and merge requests by specific terms included in titles or descriptions.
diff --git a/doc/workflow/ci_mr.png b/doc/workflow/ci_mr.png
index 77423c68190..85a609cb814 100644
--- a/doc/workflow/ci_mr.png
+++ b/doc/workflow/ci_mr.png
Binary files differ
diff --git a/doc/workflow/environment_branches.png b/doc/workflow/environment_branches.png
index 0941a4cad9c..0aff33c6bb8 100644
--- a/doc/workflow/environment_branches.png
+++ b/doc/workflow/environment_branches.png
Binary files differ
diff --git a/doc/workflow/forking/branch_select.png b/doc/workflow/forking/branch_select.png
index 3e82afca75b..77236137190 100644
--- a/doc/workflow/forking/branch_select.png
+++ b/doc/workflow/forking/branch_select.png
Binary files differ
diff --git a/doc/workflow/forking/merge_request.png b/doc/workflow/forking/merge_request.png
index 294775e1fdd..407ddfb4799 100644
--- a/doc/workflow/forking/merge_request.png
+++ b/doc/workflow/forking/merge_request.png
Binary files differ
diff --git a/doc/workflow/git_pull.png b/doc/workflow/git_pull.png
index 2dd06b56c56..0e56e59471c 100644
--- a/doc/workflow/git_pull.png
+++ b/doc/workflow/git_pull.png
Binary files differ
diff --git a/doc/workflow/gitlab_flow.png b/doc/workflow/gitlab_flow.png
index c3562cc69a8..a6f3c947843 100644
--- a/doc/workflow/gitlab_flow.png
+++ b/doc/workflow/gitlab_flow.png
Binary files differ
diff --git a/doc/workflow/good_commit.png b/doc/workflow/good_commit.png
index c3664aa97f2..ceb0d4b1691 100644
--- a/doc/workflow/good_commit.png
+++ b/doc/workflow/good_commit.png
Binary files differ
diff --git a/doc/workflow/img/file_finder_find_button.png b/doc/workflow/img/file_finder_find_button.png
index 23139cc00c5..0c2d7d7bc73 100644
--- a/doc/workflow/img/file_finder_find_button.png
+++ b/doc/workflow/img/file_finder_find_button.png
Binary files differ
diff --git a/doc/workflow/img/forking_workflow_fork_button.png b/doc/workflow/img/forking_workflow_fork_button.png
index 29854e6c516..941d5363c35 100644
--- a/doc/workflow/img/forking_workflow_fork_button.png
+++ b/doc/workflow/img/forking_workflow_fork_button.png
Binary files differ
diff --git a/doc/workflow/img/forking_workflow_path_taken_error.png b/doc/workflow/img/forking_workflow_path_taken_error.png
index 9365fd13200..df938da5677 100644
--- a/doc/workflow/img/forking_workflow_path_taken_error.png
+++ b/doc/workflow/img/forking_workflow_path_taken_error.png
Binary files differ
diff --git a/doc/workflow/img/notification_group_settings.png b/doc/workflow/img/notification_group_settings.png
index fc096f46901..ed5e9459216 100644
--- a/doc/workflow/img/notification_group_settings.png
+++ b/doc/workflow/img/notification_group_settings.png
Binary files differ
diff --git a/doc/workflow/img/notification_project_settings.png b/doc/workflow/img/notification_project_settings.png
index 006432f65c9..e2db2037d94 100644
--- a/doc/workflow/img/notification_project_settings.png
+++ b/doc/workflow/img/notification_project_settings.png
Binary files differ
diff --git a/doc/workflow/img/repository_mirroring_force_update.png b/doc/workflow/img/repository_mirroring_force_update.png
new file mode 100644
index 00000000000..8ba715d1ba3
--- /dev/null
+++ b/doc/workflow/img/repository_mirroring_force_update.png
Binary files differ
diff --git a/doc/workflow/img/repository_mirroring_pull_settings_lower.png b/doc/workflow/img/repository_mirroring_pull_settings_lower.png
new file mode 100644
index 00000000000..a3e0b74ddf8
--- /dev/null
+++ b/doc/workflow/img/repository_mirroring_pull_settings_lower.png
Binary files differ
diff --git a/doc/workflow/img/repository_mirroring_pull_settings_upper.png b/doc/workflow/img/repository_mirroring_pull_settings_upper.png
new file mode 100644
index 00000000000..c60354fdca7
--- /dev/null
+++ b/doc/workflow/img/repository_mirroring_pull_settings_upper.png
Binary files differ
diff --git a/doc/workflow/img/repository_mirroring_push_settings.png b/doc/workflow/img/repository_mirroring_push_settings.png
new file mode 100644
index 00000000000..21a6aca4526
--- /dev/null
+++ b/doc/workflow/img/repository_mirroring_push_settings.png
Binary files differ
diff --git a/doc/workflow/img/todo_list_item.png b/doc/workflow/img/todo_list_item.png
index 076069b651e..91bbf9e5373 100644
--- a/doc/workflow/img/todo_list_item.png
+++ b/doc/workflow/img/todo_list_item.png
Binary files differ
diff --git a/doc/workflow/img/todos_add_todo_sidebar.png b/doc/workflow/img/todos_add_todo_sidebar.png
index 3fa37067d1e..aefec7a2d9c 100644
--- a/doc/workflow/img/todos_add_todo_sidebar.png
+++ b/doc/workflow/img/todos_add_todo_sidebar.png
Binary files differ
diff --git a/doc/workflow/img/todos_mark_done_sidebar.png b/doc/workflow/img/todos_mark_done_sidebar.png
index a8e756a71db..2badd880b40 100644
--- a/doc/workflow/img/todos_mark_done_sidebar.png
+++ b/doc/workflow/img/todos_mark_done_sidebar.png
Binary files differ
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index 6ac3bb8c0b4..ec5943fd51b 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -24,7 +24,7 @@ There are various configuration options to help GitLab server administrators:
In `/etc/gitlab/gitlab.rb`:
```ruby
-# Change to true to enable lfs
+# Change to true to enable lfs - enabled by default if not defined
gitlab_rails['lfs_enabled'] = false
# Optionally, change the storage path location. Defaults to
@@ -54,7 +54,7 @@ to offload local hard disk R/W operations, and free up disk space significantly.
GitLab is tightly integrated with `Fog`, so you can refer to its [documentation](http://fog.io/about/provider_documentation.html)
to check which storage services can be integrated with GitLab.
You can also use external object storage in a private local network. For example,
-[Minio](https://www.minio.io/) is a standalone object storage service, is easy to setup, and works well with GitLab instances.
+[Minio](https://www.minio.io/) is a standalone object storage service, is easy to set up, and works well with GitLab instances.
GitLab provides two different options for the uploading mechanism: "Direct upload" and "Background upload".
@@ -95,6 +95,7 @@ Here is a configuration example with S3.
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
| `path_style` | Set to true to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as false for AWS S3 | false |
+| `use_iam_profile` | Set to true to use IAM profile instead of access keys | false
Here is a configuration example with GCS.
diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
index ae161e43233..d02e921fa84 100644
--- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
+++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
@@ -61,11 +61,12 @@ git commit -am "Added Debian iso" # commit the file meta data
git push origin master # sync the git repo and large file to the GitLab server
```
->**Note**: Make sure that `.gitattributes` is tracked by git. Otherwise Git
- LFS will not be working properly for people cloning the project.
- ```bash
- git add .gitattributes
- ```
+> **Note**: Make sure that `.gitattributes` is tracked by git. Otherwise Git
+> LFS will not be working properly for people cloning the project.
+>
+> ```bash
+> git add .gitattributes
+> ```
Cloning the repository works the same as before. Git automatically detects the
LFS-tracked files and clones them via HTTP. If you performed the git clone
@@ -77,10 +78,10 @@ git clone git@gitlab.example.com:group/project.git
```
If you already cloned the repository and you want to get the latest LFS object
-that are on the remote repository, eg. from branch `master`:
+that are on the remote repository, eg. for a branch from origin:
```bash
-git lfs fetch master
+git lfs fetch origin master
```
## File Locking
diff --git a/doc/workflow/merge_request.png b/doc/workflow/merge_request.png
index 08dfc7f2468..010e95983fc 100644
--- a/doc/workflow/merge_request.png
+++ b/doc/workflow/merge_request.png
Binary files differ
diff --git a/doc/workflow/messy_flow.png b/doc/workflow/messy_flow.png
index 7e72e2a3be6..4fa22d2bb5d 100644
--- a/doc/workflow/messy_flow.png
+++ b/doc/workflow/messy_flow.png
Binary files differ
diff --git a/doc/workflow/mr_inline_comments.png b/doc/workflow/mr_inline_comments.png
index 6a2e66a01ba..a18801f56e4 100644
--- a/doc/workflow/mr_inline_comments.png
+++ b/doc/workflow/mr_inline_comments.png
Binary files differ
diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md
index 5dc62a30128..c590ac4b0ba 100644
--- a/doc/workflow/notifications.md
+++ b/doc/workflow/notifications.md
@@ -10,31 +10,32 @@ You can find notification settings under the user profile.
Notification settings are divided into three groups:
-* Global Settings
-* Group Settings
-* Project Settings
+- Global settings
+- Group settings
+- Project settings
Each of these settings have levels of notification:
-* Disabled - turns off notifications
-* Participating - receive notifications from related resources
-* Watch - receive notifications from projects or groups user is a member of
-* Global - notifications as set at the global settings
-* Custom - user will receive notifications when mentioned, is participant and custom selected events.
+- Watch: Receive notifications for any activity.
+- On Mention: Receive notifications when `@mentioned` in comments.
+- Participate: Receive notifications for threads you have participated in.
+- Disabled: Turns off notifications.
+- Custom: Receive notifications for custom selected events.
+- Global: For groups and projects, notifications as per global settings.
-#### Global Settings
+### Global Settings
-Global Settings are at the bottom of the hierarchy.
+Global settings are at the bottom of the hierarchy.
Any setting set here will be overridden by a setting at the group or a project level.
Group or Project settings can use `global` notification setting which will then use
anything that is set at Global Settings.
-#### Group Settings
+### Group Settings
![notification settings](img/notification_group_settings.png)
-Group Settings are taking precedence over Global Settings but are on a level below Project or Subgroup Settings:
+Group settings are taking precedence over Global Settings but are on a level below Project or Subgroup settings:
```
Group < Subgroup < Project
@@ -46,11 +47,11 @@ Organization like this is suitable for users that belong to different groups but
same need for being notified for every group they are member of.
These settings can be configured on group page under the name of the group. It will be the dropdown with the bell icon. They can also be configured on the user profile notifications dropdown.
-#### Project Settings
+### Project Settings
![notification settings](img/notification_project_settings.png)
-Project Settings are at the top level and any setting placed at this level will take precedence of any
+Project settings are at the top level and any setting placed at this level will take precedence of any
other setting.
This is suitable for users that have different needs for notifications per project basis.
These settings can be configured on project page under the name of the project. It will be the dropdown with the bell icon. They can also be configured on the user profile notifications dropdown.
@@ -73,14 +74,13 @@ Below is the table of events users can be notified of:
### Issue / Merge request events
In most of the below cases, the notification will be sent to:
+
- Participants:
- the author and assignee of the issue/merge request
- authors of comments on the issue/merge request
- anyone mentioned by `@username` in the issue/merge request title or description
- anyone mentioned by `@username` in any of the comments on the issue/merge request
-
- ...with notification level "Participating" or higher
-
+ ...with notification level "Participating" or higher
- Watchers: users with notification level "Watch"
- Subscribers: anyone who manually subscribed to the issue/merge request
- Custom: Users with notification level "custom" who turned on notifications for any of the events present in the table below
@@ -92,12 +92,16 @@ In most of the below cases, the notification will be sent to:
| Reassign issue | The above, plus the old assignee |
| Reopen issue | |
| Due issue | Participants and Custom notification level with this event selected |
+| Change milestone issue | Subscribers, participants mentioned, and Custom notification level with this event selected |
+| Remove milestone issue | Subscribers, participants mentioned, and Custom notification level with this event selected |
| New merge request | |
| Push to merge request | Participants and Custom notification level with this event selected |
| Reassign merge request | The above, plus the old assignee |
| Close merge request | |
| Reopen merge request | |
| Merge merge request | |
+| Change milestone merge request | Subscribers, participants mentioned, and Custom notification level with this event selected |
+| Remove milestone merge request | Subscribers, participants mentioned, and Custom notification level with this event selected |
| New comment | The above, plus anyone mentioned by `@username` in the comment, with notification level "Mention" or higher |
| Failed pipeline | The author of the pipeline |
| Successful pipeline | The author of the pipeline, if they have the custom notification setting for successful pipelines set |
@@ -131,16 +135,19 @@ Notification emails include headers that provide extra content about the notific
| X-GitLab-NotificationReason | The reason for being notified. "mentioned", "assigned", etc |
#### X-GitLab-NotificationReason
+
This header holds the reason for the notification to have been sent out,
where reason can be `mentioned`, `assigned`, `own_activity`, etc.
Only one reason is sent out according to its priority:
+
- `own_activity`
- `assigned`
- `mentioned`
-The reason in this header will also be shown in the footer of the notification email. For example an email with the
+The reason in this header will also be shown in the footer of the notification email. For example an email with the
reason `assigned` will have this sentence in the footer:
`"You are receiving this email because you have been assigned an item on {configured GitLab hostname}"`
-**Note: Only reasons listed above have been implemented so far**
-Further implementation is [being discussed here](https://gitlab.com/gitlab-org/gitlab-ce/issues/42062)
+NOTE: **Note:**
+Only reasons listed above have been implemented so far.
+Further implementation is [being discussed](https://gitlab.com/gitlab-org/gitlab-ce/issues/42062).
diff --git a/doc/workflow/production_branch.png b/doc/workflow/production_branch.png
index 648d5d5c92e..c132d51bfb6 100644
--- a/doc/workflow/production_branch.png
+++ b/doc/workflow/production_branch.png
Binary files differ
diff --git a/doc/workflow/rebase.png b/doc/workflow/rebase.png
index 8b9bb61a5cc..fe865177ba8 100644
--- a/doc/workflow/rebase.png
+++ b/doc/workflow/rebase.png
Binary files differ
diff --git a/doc/workflow/release_branches.png b/doc/workflow/release_branches.png
index 5194d75a667..0a7f61d0248 100644
--- a/doc/workflow/release_branches.png
+++ b/doc/workflow/release_branches.png
Binary files differ
diff --git a/doc/workflow/releases/new_tag.png b/doc/workflow/releases/new_tag.png
index 97519e5808f..6137ad2ee56 100644
--- a/doc/workflow/releases/new_tag.png
+++ b/doc/workflow/releases/new_tag.png
Binary files differ
diff --git a/doc/workflow/repository_mirroring.md b/doc/workflow/repository_mirroring.md
index 8c4e6ea8eab..4225d1aa31d 100644
--- a/doc/workflow/repository_mirroring.md
+++ b/doc/workflow/repository_mirroring.md
@@ -1,351 +1,394 @@
# Repository mirroring
-Repository mirroring is a way to mirror repositories from external sources.
-It can be used to mirror all branches, tags, and commits that you have
-in your repository.
+Repository mirroring allows for mirroring of repositories to and from external sources. It can be
+used to mirror branches, tags, and commits between repositories.
-Your mirror at GitLab will be updated automatically. You can
-also manually trigger an update at most once every 5 minutes.
+A repository mirror at GitLab will be updated automatically. You can also manually trigger an update
+at most once every 5 minutes.
## Overview
-Repository mirroring is very useful when, for some reason, you must use a
-project from another source.
+Repository mirroring is useful when you want to use a repository outside of GitLab.
-There are two kinds of repository mirroring features supported by GitLab:
-**push** and **pull**, the latter being only available in GitLab Enterprise Edition.
-The **push** method mirrors the repository in GitLab to another location.
+There are two kinds of repository mirroring supported by GitLab:
-Once the mirror repository is updated, all new branches,
-tags, and commits will be visible in the project's activity feed.
-Users with at least [developer access][perms] to the project can also force an
-immediate update with the click of a button. This button will not be available if
-the mirror is already being updated or 5 minutes still haven't passed since its last update.
+- Push: for mirroring a GitLab repository to another location.
+- Pull: for mirroring a repository from another location to GitLab. **[STARTER]**
-A few things/limitations to consider:
+When the mirror repository is updated, all new branches, tags, and commits will be visible in the
+project's activity feed.
-- The repository must be accessible over `http://`, `https://`, `ssh://` or `git://`.
-- If your HTTP repository is not publicly accessible, add authentication
- information to the URL, like: `https://username@gitlab.company.com/group/project.git`.
- In some cases, you might need to use a personal access token instead of a
- password, e.g., you want to mirror to GitHub and have 2FA enabled.
-- The import will time out after 15 minutes. For repositories that take longer
- use a clone/push combination.
-- The Git LFS objects will not be synced. You'll need to push/pull them
- manually.
+Users with at least [developer access](../user/permissions.md) to the project can also force an
+immediate update, unless:
+
+- The mirror is already being updated.
+- 5 minutes haven't elapsed since its last update.
## Use cases
-- You migrated to GitLab but still need to keep your project in another source.
- In that case, you can simply set it up to mirror to GitLab (pull) and all the
- essential history of commits, tags and branches will be available in your
- GitLab instance.
-- You have old projects in another source that you don't use actively anymore,
- but don't want to remove for archiving purposes. In that case, you can create
- a push mirror so that your active GitLab repository can push its changes to the
- old location.
+The following are some possible use cases for repository mirroring:
-## Pulling from a remote repository **[STARTER]**
+- You migrated to GitLab but still need to keep your project in another source. In that case, you
+ can simply set it up to mirror to GitLab (pull) and all the essential history of commits, tags,
+ and branches will be available in your GitLab instance. **[STARTER]**
+- You have old projects in another source that you don't use actively anymore, but don't want to
+ remove for archiving purposes. In that case, you can create a push mirror so that your active
+ GitLab repository can push its changes to the old location.
->[Introduced][ee-51] in GitLab Enterprise Edition 8.2.
+## Pushing to a remote repository **[CORE]**
-You can set up a repository to automatically have its branches, tags, and commits
-updated from an upstream repository. This is useful when a repository you're
-interested in is located on a different server, and you want to be able to
-browse its content and its activity using the familiar GitLab interface.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in GitLab Enterprise
+> Edition 8.7. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8.
-When creating a new project, you can enable repository mirroring when you choose
-to import the repository from "Any repo by URL". Enter the full URL of the Git
-repository to pull from and click on the **Mirror repository** checkbox.
+For an existing project, you can set up push mirroring as follows:
-![New project](repository_mirroring/repository_mirroring_new_project.png)
+1. Navigate to your project's **Settings > Repository** and expand the **Mirroring repositories** section.
+1. Enter a repository URL.
+1. Select **Push** from the **Mirror direction** dropdown.
+1. Select an authentication method from the **Authentication method** dropdown, if necessary.
+1. Check the **Only mirror protected branches** box, if necessary.
+1. Click the **Mirror repository** button to save the configuration.
-For an existing project, you can set up mirror pulling by visiting your project's
-**Settings âž” Repository** and searching for the "Pull from a remote repository"
-section. Check the "Mirror repository" box and hit **Save changes** at the bottom.
-You have a few options to choose from one being the user who will be the author
-of all events in the activity feed that are the result of an update. This user
-needs to have at least [master access][perms] to the project. Another option is
-whether you want to trigger builds for mirror updates.
+![Repository mirroring push settings screen](img/repository_mirroring_push_settings.png)
-![Pull settings](repository_mirroring/repository_mirroring_pull_settings.png)
+When push mirroring is enabled, only push commits directly to the mirrored repository to prevent the
+mirror diverging. All changes will end up in the mirrored repository whenever:
-Since the repository on GitLab functions as a mirror of the upstream repository,
-you are advised not to push commits directly to the repository on GitLab.
-Instead, any commits should be pushed to the upstream repository, and will end
-up in the GitLab repository automatically within a certain period of time
-or when a [forced update](#forcing-an-update) is initiated.
+- Commits are pushed to GitLab.
+- A [forced update](#forcing-an-update) is initiated.
-If you do manually update a branch in the GitLab repository, the branch will
-become diverged from upstream, and GitLab will no longer automatically update
-this branch to prevent any changes from being lost.
+Changes pushed to files in the repository are automatically pushed to the remote mirror at least:
-![Diverged branch](repository_mirroring/repository_mirroring_diverged_branch.png)
+- Within five minutes of being received.
+- Within one minute if **Only mirror protected branches** is enabled.
-### Trigger update using API **[STARTER]**
+In the case of a diverged branch, you will see an error indicated at the **Mirroring repositories**
+section.
+
+### Push only protected branches **[CORE]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3350) in
+> [GitLab Starter](https://about.gitlab.com/pricing/) 10.3. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8.
->[Introduced][ee-3453] in GitLab Enterprise Edition 10.3.
+You can choose to only push your protected branches from GitLab to your remote repository.
-Pull mirroring uses polling to detect new branches and commits added upstream,
-often many minutes afterwards. If you notify GitLab by [API][pull-api], updates
-will be pulled immediately.
+To use this option, check the **Only mirror protected branches** box when creating a repository
+mirror.
-Read the [Pull Mirror Trigger API docs][pull-api].
+## Setting up a push mirror from GitLab to GitHub **[CORE]**
-### Pull only protected branches **[STARTER]**
+To set up a mirror from GitLab to GitHub, you need to follow these steps:
->[Introduced][ee-3326] in GitLab Enterprise Edition 10.3.
+1. Create a [GitHub personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/) with the `public_repo` box checked.
+1. Fill in the **Git repository URL** field, with the personal access token instead of a password.
+ For example: `https://<GitHubUsername>:<GitHubPersonalAccessToken>@github.com/group/project.git`.
+1. Click the **Mirror repository** button.
+1. Wait, or click the update button.
-You can choose to only pull the protected branches from your remote repository to GitLab.
+## Pulling from a remote repository **[STARTER]**
-To use this option go to your project's repository settings page under pull mirror.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2.
-### Overwrite diverged branches **[STARTER]**
+You can set up a repository to automatically have its branches, tags, and commits updated from an
+upstream repository.
->[Introduced][ee-4559] in GitLab Enterprise Edition 10.6.
+This is useful when a repository you're interested in is located on a different server, and you want
+to be able to browse its content and its activity using the familiar GitLab interface.
-You can choose to always update your local branch with the remote version even
-if your local version has diverged from the remote.
+To configure mirror pulling for an existing project:
-To use this option go to your project's repository settings page under pull mirror.
+1. Navigate to your project's **Settings > Repository** and expand the **Mirroring repositories**
+ section.
+1. Enter a repository URL.
+1. Select **Pull** from the **Mirror direction** dropdown.
+1. Select an authentication method from the **Authentication method** dropdown, if necessary.
+1. If necessary, check the following boxes:
+ - **Overwrite diverged branches**.
+ - **Trigger pipelines for mirror updates**.
+ - **Only mirror protected branches**.
+1. Click the **Mirror repository** button to save the configuration.
-### Hard failure **[STARTER]**
+![Repository mirroring pull settings screen - upper part](img/repository_mirroring_pull_settings_upper.png)
->[Introduced][ee-3117] in GitLab Enterprise Edition 10.2.
+---
-Once a mirror gets retried 14 times in a row, it will get marked as hard failed,
-this will become visible in either the project main dashboard or in the
-pull mirror settings page.
+![Repository mirroring pull settings screen - lower part](img/repository_mirroring_pull_settings_lower.png)
-![Hard failed mirror main notice](repository_mirroring/repository_mirroring_hard_failed_main.png)
+Because GitLab is now set to pull changes from the upstream repository, you should not push commits
+directly to the repository on GitLab. Instead, any commits should be pushed to the upstream repository.
+Changes pushed to the upstream repository will be pulled into the GitLab repository, either:
-![Hard failed mirror settings notice](repository_mirroring/repository_mirroring_hard_failed_settings.png)
+- Automatically within a certain period of time.
+- When a [forced update](#forcing-an-update) is initiated.
-When a project is hard failed, it will no longer get picked up for mirroring.
-A user can resume the project mirroring again by either [forcing an update](#forcing-an-update)
-or by changing the import URL in repository settings.
+CAUTION: **Caution:**
+If you do manually update a branch in the GitLab repository, the branch will become diverged from
+upstream and GitLab will no longer automatically update this branch to prevent any changes from being lost.
+
+### How it works
+
+Once you activate the pull mirroring feature, the mirror will be inserted into a queue. A scheduler
+will start every minute and schedule a fixed number of mirrors for update, based on the configured maximum capacity.
+
+If the mirror updates successfully, it will be enqueued once again with a small backoff period.
+
+If the mirror fails (for example, a branch diverged from upstream), the project's backoff period is
+increased each time it fails, up to a maximum amount of time.
### SSH authentication **[STARTER]**
-> [Introduced][ee-2551] in GitLab Starter 9.5
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2551) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.5.
-If you're mirroring over SSH (i.e., an `ssh://` URL), you can authenticate using
-password-based authentication, just as over HTTPS, but you can also use public
-key authentication. This is often more secure than password authentication,
-especially when the source repository supports [Deploy Keys][deploy-key].
+SSH authentication is mutual:
-To get started, navigate to **Settings âž” Repository âž” Pull from a remote repository**,
-enable mirroring (if not already enabled) and enter an `ssh://` URL.
+- You have to prove to the server that you're allowed to access the repository.
+- The server also has to prove to *you* that it's who it claims to be.
-> **NOTE**: SCP-style URLs, e.g., `git@example.com:group/project.git`, are not
-supported at this time.
+You provide your credentials as a password or public key. The server that the source repository
+resides on provides its credentials as a "host key", the fingerprint of which needs to be verified manually.
-Entering the URL adds two features to the page - `Fingerprints` and
-`SSH public key authentication`:
+If you're mirroring over SSH (that is, using an `ssh://` URL), you can authenticate using:
-![Pull settings for SSH](repository_mirroring/repository_mirroring_pull_settings_for_ssh.png)
+- Password-based authentication, just as over HTTPS.
+- Public key authentication. This is often more secure than password authentication, especially when
+ the source repository supports [Deploy Keys](../ssh/README.md#deploy-keys).
-SSH authentication is mutual. You have to prove to the server that you're
-allowed to access the repository, but the server also has to prove to *you* that
-it's who it claims to be. You provide your credentials as a password or public
-key. The server that the source repository resides on provides its credentials
-as a "host key", the fingerprint of which needs to be verified manually.
+To get started:
-Press the `Detect host keys` button. GitLab will fetch the host keys from the
-server, and display the fingerprints to you:
+1. Navigate to your project's **Settings > Repository** and expand the **Mirroring repositories** section.
+1. Enter an `ssh://` URL for mirroring.
-![Detect SSH host keys](repository_mirroring/repository_mirroring_detect_host_keys.png)
+NOTE: **Note:**
+SCP-style URLs (that is, `git@example.com:group/project.git`) are not supported at this time.
+
+Entering the URL adds two buttons to the page:
+
+- **Detect host keys**.
+- **Input host keys manually**.
+
+If you click the:
+
+- **Detect host keys** button, GitLab will fetch the host keys from the server and display the fingerprints.
+- **Input host keys manually** button, a field is displayed where you can paste in host keys.
You now need to verify that the fingerprints are those you expect. GitLab.com
and other code hosting sites publish their fingerprints in the open for you
to check:
-* [AWS CodeCommit](http://docs.aws.amazon.com/codecommit/latest/userguide/regions.html#regions-fingerprints)
-* [Bitbucket](https://confluence.atlassian.com/bitbucket/use-the-ssh-protocol-with-bitbucket-cloud-221449711.html#UsetheSSHprotocolwithBitbucketCloud-KnownhostorBitbucket%27spublickeyfingerprints)
-* [GitHub](https://help.github.com/articles/github-s-ssh-key-fingerprints/)
-* [GitLab.com](https://about.gitlab.com/gitlab-com/settings/#ssh-host-keys-fingerprints)
-* [Launchpad](https://help.launchpad.net/SSHFingerprints)
-* [Savannah](http://savannah.gnu.org/maintenance/SshAccess/)
-* [SourceForge](https://sourceforge.net/p/forge/documentation/SSH%20Key%20Fingerprints/)
+- [AWS CodeCommit](http://docs.aws.amazon.com/codecommit/latest/userguide/regions.html#regions-fingerprints)
+- [Bitbucket](https://confluence.atlassian.com/bitbucket/use-the-ssh-protocol-with-bitbucket-cloud-221449711.html#UsetheSSHprotocolwithBitbucketCloud-KnownhostorBitbucket%27spublickeyfingerprints)
+- [GitHub](https://help.github.com/articles/github-s-ssh-key-fingerprints/)
+- [GitLab.com](https://about.gitlab.com/gitlab-com/settings/#ssh-host-keys-fingerprints)
+- [Launchpad](https://help.launchpad.net/SSHFingerprints)
+- [Savannah](http://savannah.gnu.org/maintenance/SshAccess/)
+- [SourceForge](https://sourceforge.net/p/forge/documentation/SSH%20Key%20Fingerprints/)
-Other providers will vary. If you're running on-premises GitLab, or otherwise
+Other providers will vary. If you're running self-managed GitLab, or otherwise
have access to the source server, you can securely gather the key fingerprints:
-```
+```sh
$ cat /etc/ssh/ssh_host*pub | ssh-keygen -E md5 -l -f -
256 MD5:f4:28:9f:23:99:15:21:1b:bf:ed:1f:8e:a0:76:b2:9d root@example.com (ECDSA)
256 MD5:e6:eb:45:8a:3c:59:35:5f:e9:5b:80:12:be:7e:22:73 root@example.com (ED25519)
2048 MD5:3f:72:be:3d:62:03:5c:62:83:e8:6e:14:34:3a:85:1d root@example.com (RSA)
```
-(You may need to exclude `-E md5` for some older versions of SSH).
+NOTE: **Note:**
+You may need to exclude `-E md5` for some older versions of SSH.
-If you're an SSH expert and already have a `known_hosts` file you'd like to use
-unaltered, then you can skip these steps. Just press the "Show advanced" button
-and paste in the file contents:
+When pulling changes from the source repository, GitLab will now check that at least one of the stored
+host keys matches before connecting. This can prevent malicious code from being injected into your
+mirror, or your password being stolen.
-![Advanced SSH host key management](repository_mirroring/repository_mirroring_pull_advanced_host_keys.png)
+### SSH public key authentication
-Once you've **carefully verified** that all the fingerprints match your trusted
-source, you can press `Save changes`. This will record the host keys, along with
-the person who verified them (you!) and the date:
+To use SSH public key authentication, you'll also need to choose that option from the **Authentication method**
+dropdown. GitLab will generate a 4096-bit RSA key and display the public component of that key to you.
-![SSH host keys submitted](repository_mirroring/repository_mirroring_ssh_host_keys_verified.png)
+You then need to add the public SSH key to the source repository configuration. If:
-When pulling changes from the source repository, GitLab will now check that at
-least one of the stored host keys matches before connecting. This can prevent
-malicious code from being injected into your mirror, or your password being
-stolen!
+- The source is hosted on GitLab, you should add the public SSH key as a [Deploy Key](../ssh/README.md#deploy-keys).
+- The source is hosted elsewhere, you may need to add the key to your user's `authorized_keys` file.
+ Paste the entire public SSH key into the file on its own line and save it.
-To use SSH public key authentication, you'll also need to choose that option
-from the authentication methods dropdown. GitLab will generate a 4096-bit RSA
-key and display the public component of that key to you:
+Once the public key is set up on the source repository, click the **Mirror repository** button and
+your mirror will begin working.
-![SSH public key authentication](repository_mirroring/repository_mirroring_ssh_public_key_authentication.png)
+If you need to change the key at any time, you can click the **Regenerate key** button to do so. You'll have to update the source repository with the new key to keep the mirror running.
-You then need to add the public SSH key to the source repository configuration.
-If the source is hosted on GitLab, you should add it as a [Deploy Key][deploy-key].
-Other sources may require you to add the key to your user's `authorized_keys`
-file - just paste the entire `ssh-rsa AAA.... user@host` block into the file on
-its own line and save it.
-
-Once the public key is set up on the source repository, press `Save changes` and your
-mirror will begin working.
-
-If you need to change the key at any time, you can press the `Regenerate key`
-button to do so. You'll have to update the source repository with the new key
-to keep the mirror running.
-
-### How it works
-
-Once you activate the pull mirroring feature, the mirror will be inserted into
-a queue. A scheduler will start every minute and schedule a fixed amount of
-mirrors for update, based on the configured maximum capacity.
-
-If the mirror successfully updates it will be enqueued once again with a small
-backoff period.
-
-If the mirror fails (eg: branch diverged from upstream), the project's backoff
-period will be penalized each time it fails up to a maximum amount of time.
-
-## Pushing to a remote repository
-
->[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in
-GitLab Enterprise Edition 8.7. [Moved to GitLab Community Edition][ce-18715] in 10.8.
-
-For an existing project, you can set up push mirror from your project's
-**Settings âž” Repository** and searching for the "Push to a remote repository"
-section. Check the "Remote mirror repository" box and fill in the Git URL of
-the repository to push to. Click **Save changes** for the changes to take
-effect.
+### Overwrite diverged branches **[STARTER]**
-![Push settings](repository_mirroring/repository_mirroring_push_settings.png)
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4559) in
+> [GitLab Starter](https://about.gitlab.com/pricing/) 10.6.
-When push mirroring is enabled, you are advised not to push commits directly
-to the mirrored repository to prevent the mirror diverging.
-All changes will end up in the mirrored repository whenever commits
-are pushed to GitLab, or when a [forced update](#forcing-an-update) is
-initiated.
+You can choose to always update your local branches with remote versions, even if they have
+diverged from the remote.
-Pushes into GitLab are automatically pushed to the remote mirror at least once
-every 5 minutes after they are received or once every minute if **push only
-protected branches** is enabled.
+CAUTION: **Caution:**
+For mirrored branches, enabling this option results in the loss of local changes.
-In case of a diverged branch, you will see an error indicated at the **Mirror
-repository** settings.
+To use this option, check the **Overwrite diverged branches** box when creating a repository mirror.
-![Diverged branch](
-repository_mirroring/repository_mirroring_diverged_branch_push.png)
+### Only mirror protected branches **[STARTER]**
-### Push only protected branches
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3326) in
+> [GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
->[Introduced][ee-3350] in GitLab Enterprise Edition 10.3. [Moved to GitLab Community Edition][ce-18715] in 10.8.
+You can choose to pull mirror only the protected branches from your remote repository to GitLab.
+Non-protected branches are not mirrored and can diverge.
-You can choose to only push your protected branches from GitLab to your remote repository.
+To use this option, check the **Only mirror protected branches** box when creating a repository mirror.
-To use this option go to your project's repository settings page under push mirror.
+### Hard failure **[STARTER]**
-## Setting up a push mirror from GitLab to GitHub
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3117) in
+> [GitLab Starter](https://about.gitlab.com/pricing/) 10.2.
-To set up a mirror from GitLab to GitHub, you need to follow these steps:
+Once the mirroring process is unsuccessfully retried 14 times in a row, it will get marked as hard
+failed. This will become visible in either the:
-1. Create a [GitHub personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/) with the "public_repo" box checked:
+- Project's main dashboard.
+- Pull mirror settings page.
- ![edit personal access token GitHub](repository_mirroring/repository_mirroring_github_edit_personal_access_token.png)
+When a project is hard failed, it will no longer get picked up for mirroring. A user can resume the
+project mirroring again by [Forcing an update](#forcing-an-update).
-1. Fill in the "Git repository URL" with the personal access token replacing the password `https://GitHubUsername:GitHubPersonalAccessToken@github.com/group/project.git`:
+### Trigger update using API **[STARTER]**
- ![push to remote repo](repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository.png)
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3453) in
+[GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
-1. Save
-1. And either wait or trigger the "Update Now" button:
+Pull mirroring uses polling to detect new branches and commits added upstream, often minutes
+afterwards. If you notify GitLab by [API](https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project),
+updates will be pulled immediately.
+
+For more information, see [Start the pull mirroring process for a Project](https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project).
+
+## Forcing an update **[CORE]**
+
+While mirrors are scheduled to update automatically, you can always force an update by using the
+update button which is available on the **Mirroring repositories** section of the **Repository Settings** page.
+
+![Repository mirroring force update user interface](img/repository_mirroring_force_update.png)
+
+## Bidirectional mirroring **[STARTER]**
+
+CAUTION: **Caution:**
+Bidirectional mirroring may cause conflicts.
+
+If you configure a GitLab repository to both pull from, and push to, the same remote source, there
+is no guarantee that either repository will update correctly. If you set up a repository for
+bidirectional mirroring, you should prepare for the likely conflicts by deciding who will resolve
+them and how they will be resolved.
+
+Rewriting any mirrored commit on either remote will cause conflicts and mirroring to fail. This can
+be prevented by:
+
+- [Pulling only protected branches](#pull-only-protected-branches).
+- [Pushing only protected branches](#push-only-protected-branches).
+
+You should [protect the branches](../user/project/protected_branches.md) you wish to mirror on both
+remotes to prevent conflicts caused by rewriting history.
+
+Bidirectional mirroring also creates a race condition where commits made close together to the same
+branch causes conflicts. The race condition can be mitigated by reducing the mirroring delay by using
+a [Push event webhook](../user/project/integrations/webhooks.md#push-events) to trigger an immediate
+pull to GitLab. Push mirroring from GitLab is rate limited to once per minute when only push mirroring
+protected branches.
+
+### Preventing conflicts using a `pre-receive` hook
+
+> **Warning:** The solution proposed will negatively impact the performance of
+> Git push operations because they will be proxied to the upstream Git
+> repository.
+
+A server-side `pre-receive` hook can be used to prevent the race condition
+described above by only accepting the push after first pushing the commit to
+the upstream Git repository. In this configuration one Git repository acts as
+the authoritative upstream, and the other as downstream. The `pre-receive` hook
+will be installed on the downstream repository.
+
+Read about [configuring custom Git hooks](../administration/custom_hooks.md) on the GitLab server.
+
+A sample `pre-receive` hook is provided below.
+
+```bash
+#!/usr/bin/env bash
+
+# --- Assume only one push mirror target
+# Push mirroring remotes are named `remote_mirror_<id>`, this finds the first remote and uses that.
+TARGET_REPO=$(git remote | grep -m 1 remote_mirror)
+
+proxy_push()
+{
+ # --- Arguments
+ OLDREV=$(git rev-parse $1)
+ NEWREV=$(git rev-parse $2)
+ REFNAME="$3"
+
+ # --- Pattern of branches to proxy pushes
+ whitelisted=$(expr "$branch" : "\(master\)")
+
+ case "$refname" in
+ refs/heads/*)
+ branch=$(expr "$refname" : "refs/heads/\(.*\)")
+
+ if [ "$whitelisted" = "$branch" ]; then
+ error="$(git push --quiet $TARGET_REPO $NEWREV:$REFNAME 2>&1)"
+ fail=$?
+
+ if [ "$fail" != "0" ]; then
+ echo >&2 ""
+ echo >&2 " Error: updates were rejected by upstream server"
+ echo >&2 " This is usually caused by another repository pushing changes"
+ echo >&2 " to the same ref. You may want to first integrate remote changes"
+ echo >&2 ""
+ return
+ fi
+ fi
+ ;;
+ esac
+}
+
+# Allow dual mode: run from the command line just like the update hook, or
+# if no arguments are given then run as a hook script
+if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
+ # Output to the terminal in command line mode - if someone wanted to
+ # resend an email; they could redirect the output to sendmail
+ # themselves
+ PAGER= proxy_push $2 $3 $1
+else
+ # Push is proxied upstream one ref at a time. Because of this it is possible
+ # for some refs to succeed, and others to fail. This will result in a failed
+ # push.
+ while read oldrev newrev refname
+ do
+ proxy_push $oldrev $newrev $refname
+ done
+fi
+```
- ![update now](repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository_update_now.png)
+### Mirroring with Perforce Helix via Git Fusion **[STARTER]**
-## Forcing an update
+CAUTION: **Warning:**
+Bidirectional mirroring should not be used as a permanent configuration. Refer to
+[Migrating from Perforce Helix](../user/project/import/perforce.md) for alternative migration approaches.
-While mirrors are scheduled to update automatically, you can always force an update
-by using the **Update now** button which is exposed in various places:
+[Git Fusion](https://www.perforce.com/video-tutorials/git-fusion-overview) provides a Git interface
+to [Perforce Helix](https://www.perforce.com/products) which can be used by GitLab to bidirectionally
+mirror projects with GitLab. This may be useful in some situations when migrating from Perforce Helix
+to GitLab where overlapping Perforce Helix workspaces cannot be migrated simultaneously to GitLab.
-- in the commits page
-- in the branches page
-- in the tags page
-- in the **Mirror repository** settings page
+If using mirroring with Perforce Helix, you should only mirror protected branches. Perforce Helix
+will reject any pushes that rewrite history. Only the fewest number of branches should be mirrored
+due to the performance limitations of Git Fusion.
-## Bidirectional mirroring
+When configuring mirroring with Perforce Helix via Git Fusion, the following Git Fusion
+settings are recommended:
-CAUTION: **Warning:**
-There is no bidirectional support without conflicts. If you
-configure a repository to pull and push to a second remote, there is no
-guarantee that it will update correctly on both remotes. If you configure
-a repository for bidirectional mirroring, you should consider when conflicts
-occur who and how they will be resolved.
-
-Rewriting any mirrored commit on either remote will cause conflicts and
-mirroring to fail. This can be prevented by [only pulling protected branches](
-#pull-only-protected-branches) and [only pushing protected branches](
-#push-only-protected-branches). You should protect the branches you wish to
-mirror on both remotes to prevent conflicts caused by rewriting history.
-
-Bidirectional mirroring also creates a race condition where commits to the same
-branch in close proximity will cause conflicts. The race condition can be
-mitigated by reducing the mirroring delay by using a Push event webhook to
-trigger an immediate pull to GitLab. Push mirroring from GitLab is rate limited
-to once per minute when only push mirroring protected branches.
-
-It may be possible to implement a locking mechanism using the server-side
-`pre-receive` hook to prevent the race condition. Read about [configuring
-custom Git hooks][hooks] on the GitLab server.
-
-### Mirroring with Perforce via GitFusion
+- `change-pusher` should be disabled. Otherwise, every commit will be rewritten as being committed
+ by the mirroring account, rather than being mapped to existing Perforce Helix users or the `unknown_git` user.
+- `unknown_git` user will be used as the commit author if the GitLab user does not exist in
+ Perforce Helix.
-CAUTION: **Warning:**
-Bidirectional mirroring should not be used as a permanent
-configuration. There is no bidirectional mirroring without conflicts.
-Refer to [Migrating from Perforce Helix][perforce] for alternative migration
-approaches.
-
-GitFusion provides a Git interface to Perforce which can be used by GitLab to
-bidirectionally mirror projects with GitLab. This may be useful in some
-situations when migrating from Perforce to GitLab where overlapping Perforce
-workspaces cannot be migrated simultaneously to GitLab.
-
-If using mirroring with Perforce you should only mirror protected branches.
-Perforce will reject any pushes that rewrite history. It is recommended that
-only the fewest number of branches are mirrored due to the performance
-limitations of GitFusion.
-
-[ee-51]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51
-[ee-2551]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2551
-[ee-3117]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3117
-[ee-3326]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3326
-[ee-3350]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3350
-[ee-3453]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3453
-[ee-4559]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4559
-[ce-18715]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715
-[perms]: ../user/permissions.md
-[hooks]: ../administration/custom_hooks.md
-[deploy-key]: ../ssh/README.md#deploy-keys
-[webhook]: ../user/project/integrations/webhooks.md#push-events
-[pull-api]: ../api/projects.md#start-the-pull-mirroring-process-for-a-project
-[perforce]: ../user/project/import/perforce.md
+Read about [Git Fusion settings on Perforce.com](https://www.perforce.com/perforce/doc.current/manuals/git-fusion/Content/Git-Fusion/section_zdp_zz1_3l.html).
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_detect_host_keys.png b/doc/workflow/repository_mirroring/repository_mirroring_detect_host_keys.png
deleted file mode 100644
index 333648942f8..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_detect_host_keys.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch.png b/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch.png
deleted file mode 100644
index 45c9bce0889..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch_push.png b/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch_push.png
deleted file mode 100644
index 038b05cb31d..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch_push.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_github_edit_personal_access_token.png b/doc/workflow/repository_mirroring/repository_mirroring_github_edit_personal_access_token.png
deleted file mode 100644
index 139de42d8db..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_github_edit_personal_access_token.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository.png b/doc/workflow/repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository.png
deleted file mode 100644
index ccbc1d92329..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository_update_now.png b/doc/workflow/repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository_update_now.png
deleted file mode 100644
index b16b3d2828e..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_gitlab_push_to_a_remote_repository_update_now.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_main.png b/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_main.png
deleted file mode 100644
index 99d429a1802..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_main.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_settings.png b/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_settings.png
deleted file mode 100644
index 0ab07afa3cc..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_new_project.png b/doc/workflow/repository_mirroring/repository_mirroring_new_project.png
deleted file mode 100644
index 43bf304838f..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_new_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_pull_advanced_host_keys.png b/doc/workflow/repository_mirroring/repository_mirroring_pull_advanced_host_keys.png
deleted file mode 100644
index 5da5a7436bb..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_pull_advanced_host_keys.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_pull_settings.png b/doc/workflow/repository_mirroring/repository_mirroring_pull_settings.png
deleted file mode 100644
index 4b9085302a1..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_pull_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_pull_settings_for_ssh.png b/doc/workflow/repository_mirroring/repository_mirroring_pull_settings_for_ssh.png
deleted file mode 100644
index 8c2efdafa43..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_pull_settings_for_ssh.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_push_settings.png b/doc/workflow/repository_mirroring/repository_mirroring_push_settings.png
deleted file mode 100644
index f8199aa7c0f..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_push_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_ssh_host_keys_verified.png b/doc/workflow/repository_mirroring/repository_mirroring_ssh_host_keys_verified.png
deleted file mode 100644
index 93f3a532a0e..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_ssh_host_keys_verified.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/repository_mirroring/repository_mirroring_ssh_public_key_authentication.png b/doc/workflow/repository_mirroring/repository_mirroring_ssh_public_key_authentication.png
deleted file mode 100644
index 6997ad511d9..00000000000
--- a/doc/workflow/repository_mirroring/repository_mirroring_ssh_public_key_authentication.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md
index b2f1cbec204..7863dd8c242 100644
--- a/doc/workflow/shortcuts.md
+++ b/doc/workflow/shortcuts.md
@@ -93,4 +93,4 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?'
| Keyboard Shortcut | Description |
| ----------------- | ----------- |
-| <kbd>⌘</kbd> + <kbd>p</kbd> | Go to file |
+| <kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>p</kbd> | Go to file |
diff --git a/doc/workflow/time-tracking/time-tracking-example.png b/doc/workflow/time-tracking/time-tracking-example.png
index bbcabb602d6..a96e4da7f74 100644
--- a/doc/workflow/time-tracking/time-tracking-example.png
+++ b/doc/workflow/time-tracking/time-tracking-example.png
Binary files differ
diff --git a/doc/workflow/time-tracking/time-tracking-sidebar.png b/doc/workflow/time-tracking/time-tracking-sidebar.png
index d1ff5571f95..22124afed6f 100644
--- a/doc/workflow/time-tracking/time-tracking-sidebar.png
+++ b/doc/workflow/time-tracking/time-tracking-sidebar.png
Binary files differ
diff --git a/doc/workflow/timezone.md b/doc/workflow/timezone.md
index 7e08c0e51ac..338b3a32265 100644
--- a/doc/workflow/timezone.md
+++ b/doc/workflow/timezone.md
@@ -9,6 +9,7 @@ Uncomment and customize if you want to change the default time zone of GitLab ap
To see all available time zones, run `bundle exec rake time:zones:all`.
+With Omnibus installations, run `gitlab-rake time:zones:all`.
## Changing time zone in omnibus installations
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index dda82352c67..f94d592d0db 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -14,7 +14,7 @@ in a simple dashboard.
---
-You can quickly access the Todos dashboard using the bell icon next to the
+You can quickly access the Todos dashboard using the checkmark icon next to the
search bar in the upper right corner. The number in blue is the number of Todos
you still have open if the count is < 100, else it's 99+. The exact number
will still be shown in the body of the _To do_ tab.
diff --git a/lib/after_commit_queue.rb b/lib/after_commit_queue.rb
index a4d8507960e..6fb7985f955 100644
--- a/lib/after_commit_queue.rb
+++ b/lib/after_commit_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AfterCommitQueue
extend ActiveSupport::Concern
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb
index ae13c248171..cecff6d3b81 100644
--- a/lib/api/access_requests.rb
+++ b/lib/api/access_requests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class AccessRequests < Grape::API
include PaginationParams
@@ -18,6 +20,7 @@ module API
params do
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/access_requests" do
source = find_source(source_type, params[:id])
@@ -26,6 +29,7 @@ module API
present access_requesters, with: Entities::AccessRequester
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc "Requests access for the authenticated user to a #{source_type}." do
detail 'This feature was introduced in GitLab 8.11.'
@@ -50,6 +54,7 @@ module API
requires :user_id, type: Integer, desc: 'The user ID of the access requester'
optional :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)'
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id/access_requests/:user_id/approve' do
source = find_source(source_type, params[:id])
@@ -61,6 +66,7 @@ module API
status :created
present member, with: Entities::Member
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Denies an access request for the given user.' do
detail 'This feature was introduced in GitLab 8.11.'
@@ -68,6 +74,7 @@ module API
params do
requires :user_id, type: Integer, desc: 'The user ID of the access requester'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ":id/access_requests/:user_id" do
source = find_source(source_type, params[:id])
member = source.requesters.find_by!(user_id: params[:user_id])
@@ -76,6 +83,7 @@ module API
::Members::DestroyService.new(current_user).execute(member)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/api.rb b/lib/api/api.rb
index e2ad3c5f4e3..8e259961828 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class API < Grape::API
include APIGuard
@@ -15,8 +17,10 @@ module API
include: [
GrapeLogging::Loggers::FilterParameters.new,
GrapeLogging::Loggers::ClientEnv.new,
+ Gitlab::GrapeLogging::Loggers::RouteLogger.new,
Gitlab::GrapeLogging::Loggers::UserLogger.new,
- Gitlab::GrapeLogging::Loggers::QueueDurationLogger.new
+ Gitlab::GrapeLogging::Loggers::QueueDurationLogger.new,
+ Gitlab::GrapeLogging::Loggers::PerfLogger.new
]
allow_access_with_scope :api
@@ -99,12 +103,13 @@ module API
mount ::API::Features
mount ::API::Files
mount ::API::GroupBoards
- mount ::API::Groups
mount ::API::GroupMilestones
+ mount ::API::Groups
+ mount ::API::GroupVariables
mount ::API::Internal
mount ::API::Issues
- mount ::API::Jobs
mount ::API::JobArtifacts
+ mount ::API::Jobs
mount ::API::Keys
mount ::API::Labels
mount ::API::Lint
@@ -115,6 +120,7 @@ module API
mount ::API::Namespaces
mount ::API::Notes
mount ::API::Discussions
+ mount ::API::ResourceLabelEvents
mount ::API::NotificationSettings
mount ::API::PagesDomains
mount ::API::Pipelines
@@ -122,11 +128,13 @@ module API
mount ::API::ProjectExport
mount ::API::ProjectImport
mount ::API::ProjectHooks
- mount ::API::Projects
mount ::API::ProjectMilestones
+ mount ::API::Projects
mount ::API::ProjectSnapshots
mount ::API::ProjectSnippets
+ mount ::API::ProjectTemplates
mount ::API::ProtectedBranches
+ mount ::API::ProtectedTags
mount ::API::Repositories
mount ::API::Runner
mount ::API::Runners
@@ -135,6 +143,7 @@ module API
mount ::API::Settings
mount ::API::SidekiqMetrics
mount ::API::Snippets
+ mount ::API::Submodules
mount ::API::Subscriptions
mount ::API::SystemHooks
mount ::API::Tags
@@ -143,7 +152,6 @@ module API
mount ::API::Triggers
mount ::API::Users
mount ::API::Variables
- mount ::API::GroupVariables
mount ::API::Version
mount ::API::Wikis
diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb
index c17089759de..61357b3f1d6 100644
--- a/lib/api/api_guard.rb
+++ b/lib/api/api_guard.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Guard API with OAuth 2.0 Access Token
require 'rack/oauth2'
@@ -84,7 +86,7 @@ module API
end
end
- module ClassMethods
+ class_methods do
private
def install_error_responders(base)
diff --git a/lib/api/applications.rb b/lib/api/applications.rb
index b122cdefe4e..92717e04543 100644
--- a/lib/api/applications.rb
+++ b/lib/api/applications.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# External applications API
class Applications < Grape::API
@@ -22,6 +24,22 @@ module API
render_validation_error! application
end
end
+
+ desc 'Get applications' do
+ success Entities::Application
+ end
+ get do
+ applications = ApplicationsFinder.new.execute
+ present applications, with: Entities::Application
+ end
+
+ desc 'Delete an application'
+ delete ':id' do
+ application = ApplicationsFinder.new(params).execute
+ application.destroy
+
+ status 204
+ end
end
end
end
diff --git a/lib/api/avatar.rb b/lib/api/avatar.rb
index 70219bc8ea0..0f14d003065 100644
--- a/lib/api/avatar.rb
+++ b/lib/api/avatar.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Avatar < Grape::API
resource :avatar do
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index c3d93996816..c2abf9155f3 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class AwardEmoji < Grape::API
include PaginationParams
@@ -100,9 +102,10 @@ module API
end
def can_award_awardable?
- awardable.user_can_award?(current_user, params[:name])
+ awardable.user_can_award?(current_user)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def awardable
@awardable ||=
begin
@@ -119,6 +122,7 @@ module API
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def read_ability(awardable)
case awardable
diff --git a/lib/api/badges.rb b/lib/api/badges.rb
index 8ceffe9c5ef..ab670988f47 100644
--- a/lib/api/badges.rb
+++ b/lib/api/badges.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Badges < Grape::API
include PaginationParams
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index 0f89414148b..c80e1c57864 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Boards < Grape::API
include BoardsResponses
diff --git a/lib/api/boards_responses.rb b/lib/api/boards_responses.rb
index 7e873012efe..86d9b24802f 100644
--- a/lib/api/boards_responses.rb
+++ b/lib/api/boards_responses.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module BoardsResponses
extend ActiveSupport::Concern
@@ -49,11 +51,13 @@ module API
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def authorize_list_type_resource!
unless available_labels_for(board_parent).exists?(params[:label_id])
render_api_error!({ error: 'Label not found!' }, 400)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
params :list_creation_params do
requires :label_id, type: Integer, desc: 'The ID of an existing label'
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 3e445e6b1fa..2735d410c8e 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'mime/types'
module API
@@ -9,14 +11,6 @@ module API
before { authorize! :download_code, user_project }
helpers do
- def find_branch!(branch_name)
- begin
- user_project.repository.find_branch(branch_name) || not_found!('Branch')
- rescue Gitlab::Git::CommandError
- render_api_error!('The branch refname is invalid', 400)
- end
- end
-
params :filter_params do
optional :search, type: String, desc: 'Return list of branches matching the search criteria'
optional :sort, type: String, desc: 'Return list of branches sorted by the given field'
@@ -77,10 +71,11 @@ module API
success Entities::Branch
end
params do
- requires :branch, type: String, desc: 'The name of the branch'
+ requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
optional :developers_can_push, type: Boolean, desc: 'Flag if developers can push to that branch'
optional :developers_can_merge, type: Boolean, desc: 'Flag if developers can merge to that branch'
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id/repository/branches/:branch/protect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
authorize_admin_project
@@ -108,14 +103,16 @@ module API
render_api_error!(protected_branch.errors.full_messages, 422)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Note: This API will be deprecated in favor of the protected branches API.
desc 'Unprotect a single branch' do
success Entities::Branch
end
params do
- requires :branch, type: String, desc: 'The name of the branch'
+ requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id/repository/branches/:branch/unprotect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
authorize_admin_project
@@ -125,13 +122,14 @@ module API
present branch, with: Entities::Branch, current_user: current_user, project: user_project
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Create branch' do
success Entities::Branch
end
params do
- requires :branch, type: String, desc: 'The name of the branch'
- requires :ref, type: String, desc: 'Create branch from commit sha or existing branch'
+ requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
+ requires :ref, type: String, desc: 'Create branch from commit sha or existing branch', allow_blank: false
end
post ':id/repository/branches' do
authorize_push_project
@@ -151,7 +149,7 @@ module API
desc 'Delete a branch'
params do
- requires :branch, type: String, desc: 'The name of the branch'
+ requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
end
delete ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
authorize_push_project
diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb
index d7138b2f2fe..19148758fc5 100644
--- a/lib/api/broadcast_messages.rb
+++ b/lib/api/broadcast_messages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class BroadcastMessages < Grape::API
include PaginationParams
diff --git a/lib/api/circuit_breakers.rb b/lib/api/circuit_breakers.rb
index c13154dc0ec..da756daadcc 100644
--- a/lib/api/circuit_breakers.rb
+++ b/lib/api/circuit_breakers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class CircuitBreakers < Grape::API
before { authenticated_as_admin! }
@@ -11,37 +13,24 @@ module API
end
resource ':type' do
namespace '', requirements: { type: 'repository_storage' } do
- helpers do
- def failing_storage_health
- @failing_storage_health ||= Gitlab::Git::Storage::Health.for_failing_storages
- end
-
- def storage_health
- @storage_health ||= Gitlab::Git::Storage::Health.for_all_storages
- end
- end
-
desc 'Get all git storages' do
detail 'This feature was introduced in GitLab 9.5'
- success Entities::RepositoryStorageHealth
end
get do
- present storage_health, with: Entities::RepositoryStorageHealth
+ present []
end
desc 'Get all failing git storages' do
detail 'This feature was introduced in GitLab 9.5'
- success Entities::RepositoryStorageHealth
end
get 'failing' do
- present failing_storage_health, with: Entities::RepositoryStorageHealth
+ present []
end
desc 'Reset all storage failures and open circuitbreaker' do
detail 'This feature was introduced in GitLab 9.5'
end
delete do
- Gitlab::Git::Storage::FailureInfo.reset_all!
end
end
end
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 829eef18795..99553d993ca 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'mime/types'
module API
@@ -21,6 +23,7 @@ module API
optional :all, type: String, desc: 'Show all statuses, default: false'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/repository/commits/:sha/statuses' do
authorize!(:read_commit_status, user_project)
@@ -34,6 +37,7 @@ module API
statuses = statuses.where(name: params[:name]) if params[:name].present?
present paginate(statuses), with: Entities::CommitStatus
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Post status to a commit' do
success Entities::CommitStatus
@@ -49,6 +53,7 @@ module API
optional :context, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"'
optional :coverage, type: Float, desc: 'The total code coverage'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/statuses/:sha' do
authorize! :create_commit_status, user_project
@@ -118,6 +123,7 @@ module API
render_api_error!(e.message, 400)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 92329465b2c..337b92a6183 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'mime/types'
module API
@@ -71,12 +73,32 @@ module API
detail 'This feature was introduced in GitLab 8.13'
end
params do
- requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.'
+ requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.', allow_blank: false
requires :commit_message, type: String, desc: 'Commit message'
- requires :actions, type: Array[Hash], desc: 'Actions to perform in commit'
+ requires :actions, type: Array, desc: 'Actions to perform in commit' do
+ requires :action, type: String, desc: 'The action to perform, `create`, `delete`, `move`, `update`, `chmod`', values: %w[create update move delete chmod].freeze
+ requires :file_path, type: String, desc: 'Full path to the file. Ex. `lib/class.rb`'
+ given action: ->(action) { action == 'move' } do
+ requires :previous_path, type: String, desc: 'Original full path to the file being moved. Ex. `lib/class1.rb`'
+ end
+ given action: ->(action) { %w[create move].include? action } do
+ optional :content, type: String, desc: 'File content'
+ end
+ given action: ->(action) { action == 'update' } do
+ requires :content, type: String, desc: 'File content'
+ end
+ optional :encoding, type: String, desc: '`text` or `base64`', default: 'text', values: %w[text base64]
+ given action: ->(action) { %w[update move delete].include? action } do
+ optional :last_commit_id, type: String, desc: 'Last known file commit id'
+ end
+ given action: ->(action) { action == 'chmod' } do
+ requires :execute_filemode, type: Boolean, desc: 'When `true/false` enables/disables the execute flag on the file.'
+ end
+ end
optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from'
optional :author_email, type: String, desc: 'Author email for commit'
optional :author_name, type: String, desc: 'Author name for commit'
+ optional :stats, type: Boolean, default: true, desc: 'Include commit stats'
end
post ':id/repository/commits' do
authorize_push_to_branch!(params[:branch])
@@ -89,7 +111,10 @@ module API
if result[:status] == :success
commit_detail = user_project.repository.commit(result[:result])
- present commit_detail, with: Entities::CommitDetail
+
+ Gitlab::WebIdeCommitsCounter.increment if find_user_from_warden
+
+ present commit_detail, with: Entities::CommitDetail, stats: params[:stats]
else
render_api_error!(result[:message], 400)
end
@@ -136,6 +161,7 @@ module API
use :pagination
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
@@ -144,6 +170,7 @@ module API
present paginate(notes), with: Entities::CommitNote
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Cherry pick commit into a branch' do
detail 'This feature was introduced in GitLab 8.15'
@@ -151,7 +178,7 @@ module API
end
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag to be cherry picked'
- requires :branch, type: String, desc: 'The name of the branch'
+ requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
end
post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
authorize_push_to_branch!(params[:branch])
@@ -159,8 +186,41 @@ module API
commit = user_project.commit(params[:sha])
not_found!('Commit') unless commit
- branch = user_project.repository.find_branch(params[:branch])
- not_found!('Branch') unless branch
+ find_branch!(params[:branch])
+
+ commit_params = {
+ commit: commit,
+ start_branch: params[:branch],
+ branch_name: params[:branch]
+ }
+
+ result = ::Commits::CherryPickService
+ .new(user_project, current_user, commit_params)
+ .execute
+
+ if result[:status] == :success
+ present user_project.repository.commit(result[:result]),
+ with: Entities::Commit
+ else
+ render_api_error!(result[:message], 400)
+ end
+ end
+
+ desc 'Revert a commit in a branch' do
+ detail 'This feature was introduced in GitLab 11.5'
+ success Entities::Commit
+ end
+ params do
+ requires :sha, type: String, desc: 'Commit SHA to revert'
+ requires :branch, type: String, desc: 'Target branch name', allow_blank: false
+ end
+ post ':id/repository/commits/:sha/revert', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
+ authorize_push_to_branch!(params[:branch])
+
+ commit = user_project.commit(params[:sha])
+ not_found!('Commit') unless commit
+
+ find_branch!(params[:branch])
commit_params = {
commit: commit,
@@ -168,11 +228,13 @@ module API
branch_name: params[:branch]
}
- result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute
+ result = ::Commits::RevertService
+ .new(user_project, current_user, commit_params)
+ .execute
if result[:status] == :success
- branch = user_project.repository.find_branch(params[:branch])
- present user_project.repository.commit(branch.dereferenced_target), with: Entities::Commit
+ present user_project.repository.commit(result[:result]),
+ with: Entities::Commit
else
render_api_error!(result[:message], 400)
end
diff --git a/lib/api/custom_attributes_endpoints.rb b/lib/api/custom_attributes_endpoints.rb
index 5000aa0d9ac..2149e04451e 100644
--- a/lib/api/custom_attributes_endpoints.rb
+++ b/lib/api/custom_attributes_endpoints.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module CustomAttributesEndpoints
extend ActiveSupport::Concern
@@ -30,6 +32,7 @@ module API
params do
use :custom_attributes_key
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/custom_attributes/:key' do
resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend
authorize! :read_custom_attribute
@@ -38,12 +41,14 @@ module API
present custom_attribute, with: Entities::CustomAttribute
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc "Set a custom attribute on a #{attributable_name}"
params do
use :custom_attributes_key
requires :value, type: String, desc: 'The value of the custom attribute'
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id/custom_attributes/:key' do
resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend
authorize! :update_custom_attribute
@@ -59,11 +64,13 @@ module API
render_validation_error!(custom_attribute)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc "Delete a custom attribute on a #{attributable_name}"
params do
use :custom_attributes_key
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/custom_attributes/:key' do
resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend
authorize! :update_custom_attribute
@@ -72,6 +79,7 @@ module API
status 204
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 6769855b899..ce35720d408 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class DeployKeys < Grape::API
include PaginationParams
@@ -9,9 +11,11 @@ module API
project.deploy_keys_projects.create(attrs)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_by_deploy_key(project, key_id)
project.deploy_keys_projects.find_by!(deploy_key: key_id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
desc 'Return all deploy keys'
@@ -36,11 +40,13 @@ module API
params do
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/deploy_keys" do
keys = user_project.deploy_keys_projects.preload(:deploy_key)
present paginate(keys), with: Entities::DeployKeysProject
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get single deploy key' do
success Entities::DeployKeysProject
@@ -62,6 +68,7 @@ module API
requires :title, type: String, desc: 'The name of the deploy key'
optional :can_push, type: Boolean, desc: "Can deploy key push to the project's repository"
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ":id/deploy_keys" do
params[:key].strip!
@@ -94,6 +101,7 @@ module API
render_validation_error!(deploy_key_project)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Update an existing deploy key for a project' do
success Entities::SSHKey
@@ -147,12 +155,14 @@ module API
params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ":id/deploy_keys/:key_id" do
deploy_key_project = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
not_found!('Deploy Key') unless deploy_key_project
destroy_conditionally!(deploy_key_project)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index 184fae0eb76..6747e2e5005 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# Deployments RESTful API endpoints
class Deployments < Grape::API
@@ -18,11 +20,13 @@ module API
optional :order_by, type: String, values: %w[id iid created_at ref], default: 'id', desc: 'Return deployments ordered by `id` or `iid` or `created_at` or `ref`'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/deployments' do
authorize! :read_deployment, user_project
present paginate(user_project.deployments.order(params[:order_by] => params[:sort])), with: Entities::Deployment
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Gets a specific deployment' do
detail 'This feature was introduced in GitLab 8.11.'
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index 13c34e3473a..39c6d28391d 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Discussions < Grape::API
include PaginationParams
@@ -23,6 +25,7 @@ module API
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/#{noteables_path}/:noteable_id/discussions" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
@@ -36,6 +39,7 @@ module API
present paginate(discussions), with: Entities::Discussion
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc "Get a single #{noteable_type.to_s.downcase} discussion" do
success Entities::Discussion
@@ -219,6 +223,7 @@ module API
end
helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
def readable_discussion_notes(noteable, discussion_id)
notes = noteable.notes
.where(discussion_id: discussion_id)
@@ -228,6 +233,7 @@ module API
notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 27f28e1df93..5572e86985c 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Entities
class WikiPageBasic < Grape::Entity
@@ -10,6 +12,28 @@ module API
expose :content
end
+ class WikiAttachment < Grape::Entity
+ include Gitlab::FileMarkdownLinkBuilder
+
+ expose :file_name
+ expose :file_path
+ expose :branch
+ expose :link do
+ expose :file_path, as: :url
+ expose :markdown do |_entity|
+ self.markdown_link
+ end
+ end
+
+ def filename
+ object.file_name
+ end
+
+ def secure_url
+ object.file_path
+ end
+ end
+
class UserSafe < Grape::Entity
expose :id, :name, :username
end
@@ -31,7 +55,7 @@ module API
class User < UserBasic
expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
- expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
+ expose :bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization
end
class UserActivity < Grape::Entity
@@ -83,6 +107,7 @@ module API
expose :project_id, :issues_events, :confidential_issues_events
expose :note_events, :confidential_note_events, :pipeline_events, :wiki_page_events
expose :job_events
+ expose :push_events_branch_filter
end
class SharedGroup < Grape::Entity
@@ -91,6 +116,7 @@ module API
group_link.group.name
end
expose :group_access, as: :group_access_level
+ expose :expires_at
end
class ProjectIdentity < Grape::Entity
@@ -134,16 +160,31 @@ module API
# (fixed in https://github.com/rails/rails/pull/25976).
project.tags.map(&:name).sort
end
+
expose :ssh_url_to_repo, :http_url_to_repo, :web_url, :readme_url
+
+ expose :license_url, if: :license do |project|
+ license = project.repository.license_blob
+
+ if license
+ Gitlab::Routing.url_helpers.project_blob_url(project, File.join(project.default_branch, license.path))
+ end
+ end
+
+ expose :license, with: 'API::Entities::LicenseBasic', if: :license do |project|
+ project.repository.license
+ end
+
expose :avatar_url do |project, options|
project.avatar_url(only_path: false)
end
+
expose :star_count, :forks_count
expose :last_activity_at
-
expose :namespace, using: 'API::Entities::NamespaceBasic'
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
+ # rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {})
# Preloading tags, should be done with using only `:tags`,
# as `:tags` are defined as: `has_many :tags, through: :taggings`
@@ -153,6 +194,7 @@ module API
.preload(:import_state, :tags)
.preload(namespace: [:route, :owner])
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
class Project < BasicProjectDetails
@@ -223,6 +265,7 @@ module API
expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
+ # rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {})
# Preloading tags, should be done with using only `:tags`,
# as `:tags` are defined as: `has_many :tags, through: :taggings`
@@ -231,9 +274,10 @@ module API
super(projects_relation).preload(:group)
.preload(project_group_links: :group,
fork_network: :root_project,
- forked_project_link: :forked_from_project,
+ fork_network_member: :forked_from_project,
forked_from_project: [:route, :forks, :tags, namespace: :route])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def self.forks_counting_projects(projects_relation)
projects_relation + projects_relation.map(&:forked_from_project).compact
@@ -369,6 +413,10 @@ module API
expose :can_push do |repo_branch, options|
Gitlab::UserAccess.new(options[:current_user], project: options[:project]).can_push_to_branch?(repo_branch.name)
end
+
+ expose :default do |repo_branch, options|
+ options[:project].default_branch == repo_branch.name
+ end
end
class TreeObject < Grape::Entity
@@ -428,6 +476,11 @@ module API
expose :merge_access_levels, using: Entities::ProtectedRefAccess
end
+ class ProtectedTag < Grape::Entity
+ expose :name
+ expose :create_access_levels, using: Entities::ProtectedRefAccess
+ end
+
class Milestone < Grape::Entity
expose :id, :iid
expose :project_id, if: -> (entity, options) { entity&.project_id }
@@ -525,10 +578,12 @@ module API
expose :total_time_spent, as: :human_total_time_spent
end
+ # rubocop: disable CodeReuse/ActiveRecord
def total_time_spent
# Avoids an N+1 query since timelogs are preloaded
object.timelogs.map(&:time_spent).sum
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
class ExternalIssue < Grape::Entity
@@ -552,6 +607,22 @@ module API
end
class MergeRequestBasic < ProjectEntity
+ expose :merged_by, using: Entities::UserBasic do |merge_request, _options|
+ merge_request.metrics&.merged_by
+ end
+
+ expose :merged_at do |merge_request, _options|
+ merge_request.metrics&.merged_at
+ end
+
+ expose :closed_by, using: Entities::UserBasic do |merge_request, _options|
+ merge_request.metrics&.latest_closed_by
+ end
+
+ expose :closed_at do |merge_request, _options|
+ merge_request.metrics&.latest_closed_at
+ end
+
expose :title_html, if: -> (_, options) { options[:render_html] } do |entity|
MarkupHelper.markdown_field(entity, :title)
end
@@ -621,22 +692,6 @@ module API
merge_request.merge_request_diff.real_size
end
- expose :merged_by, using: Entities::UserBasic do |merge_request, _options|
- merge_request.metrics&.merged_by
- end
-
- expose :merged_at do |merge_request, _options|
- merge_request.metrics&.merged_at
- end
-
- expose :closed_by, using: Entities::UserBasic do |merge_request, _options|
- merge_request.metrics&.latest_closed_by
- end
-
- expose :closed_at do |merge_request, _options|
- merge_request.metrics&.latest_closed_at
- end
-
expose :latest_build_started_at, if: -> (_, options) { build_available?(options) } do |merge_request, _options|
merge_request.metrics&.latest_build_started_at
end
@@ -655,6 +710,8 @@ module API
expose :diff_refs, using: Entities::DiffRefs
+ expose :diverged_commits_count, as: :diverged_commits_count, if: -> (_, options) { options[:include_diverged_commits_count] }
+
def build_available?(options)
options[:project]&.feature_available?(:builds, options[:current_user])
end
@@ -856,7 +913,7 @@ module API
class NotificationSetting < Grape::Entity
expose :level
expose :events, if: ->(notification_setting, _) { notification_setting.custom? } do
- ::NotificationSetting::EMAIL_EVENTS.each do |event|
+ ::NotificationSetting.email_events.each do |event|
expose event
end
end
@@ -901,6 +958,7 @@ module API
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {})
relation = super(projects_relation, options)
@@ -925,6 +983,7 @@ module API
relation
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
class LabelBasic < Grape::Entity
@@ -1031,9 +1090,11 @@ module API
options[:project].repository.commit(repo_tag.dereferenced_target)
end
+ # rubocop: disable CodeReuse/ActiveRecord
expose :release, using: Entities::Release do |repo_tag, options|
options[:project].releases.find_by(tag: repo_tag.name)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
class Runner < Grape::Entity
@@ -1056,6 +1117,7 @@ module API
expose :version, :revision, :platform, :architecture
expose :contacted_at
expose :token, if: lambda { |runner, options| options[:current_user].admin? || !runner.instance_type? }
+ # rubocop: disable CodeReuse/ActiveRecord
expose :projects, with: Entities::BasicProjectDetails do |runner, options|
if options[:current_user].admin?
runner.projects
@@ -1063,6 +1125,8 @@ module API
options[:current_user].authorized_projects.where(id: runner.projects)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
expose :groups, with: Entities::BasicGroupDetails do |runner, options|
if options[:current_user].admin?
runner.groups
@@ -1070,6 +1134,7 @@ module API
options[:current_user].authorized_groups.where(id: runner.groups)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
class RunnerRegistrationDetails < Grape::Entity
@@ -1080,6 +1145,10 @@ module API
expose :filename, :size
end
+ class JobArtifact < Grape::Entity
+ expose :file_type, :size, :filename, :file_format
+ end
+
class JobBasic < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at
@@ -1094,7 +1163,9 @@ module API
end
class Job < JobBasic
+ # artifacts_file is included in job_artifacts, but kept for backward compatibility (remove in api/v5)
expose :artifacts_file, using: JobArtifactFile, if: -> (job, opts) { job.artifacts? }
+ expose :job_artifacts, as: :artifacts, using: JobArtifact
expose :runner, with: Runner
expose :artifacts_expire_at
end
@@ -1151,11 +1222,14 @@ module API
expose :deployable, using: Entities::Job
end
- class License < Grape::Entity
+ class LicenseBasic < Grape::Entity
expose :key, :name, :nickname
- expose :featured, as: :popular
expose :url, as: :html_url
expose(:source_url) { |license| license.meta['source'] }
+ end
+
+ class License < LicenseBasic
+ expose :popular?, as: :popular
expose(:description) { |license| license.meta['description'] }
expose(:conditions) { |license| license.meta['conditions'] }
expose(:permissions) { |license| license.meta['permissions'] }
@@ -1164,6 +1238,7 @@ module API
end
class TemplatesList < Grape::Entity
+ expose :key
expose :name
end
@@ -1188,7 +1263,11 @@ module API
expose :token
end
- class ImpersonationToken < PersonalAccessTokenWithToken
+ class ImpersonationToken < PersonalAccessToken
+ expose :impersonation
+ end
+
+ class ImpersonationTokenWithToken < PersonalAccessTokenWithToken
expose :impersonation
end
@@ -1306,12 +1385,6 @@ module API
expose :submitted, as: :akismet_submitted
end
- class RepositoryStorageHealth < Grape::Entity
- expose :storage_name
- expose :failing_on_hosts
- expose :total_failures
- end
-
class CustomAttribute < Grape::Entity
expose :key
expose :value
@@ -1360,7 +1433,9 @@ module API
end
class Application < Grape::Entity
+ expose :id
expose :uid, as: :application_id
+ expose :name, as: :application_name
expose :redirect_uri, as: :callback_url
end
@@ -1396,5 +1471,19 @@ module API
badge.type == 'ProjectBadge' ? 'project' : 'group'
end
end
+
+ class ResourceLabelEvent < Grape::Entity
+ expose :id
+ expose :user, using: Entities::UserBasic
+ expose :created_at
+ expose :resource_type do |event, options|
+ event.issuable.class.name
+ end
+ expose :resource_id do |event, options|
+ event.issuable.id
+ end
+ expose :label, using: Entities::LabelBasic
+ expose :action
+ end
end
end
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index fa828f43001..c64217a6977 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# Environments RESTfull API endpoints
class Environments < Grape::API
diff --git a/lib/api/events.rb b/lib/api/events.rb
index fc4ba5a3188..6e0b508be19 100644
--- a/lib/api/events.rb
+++ b/lib/api/events.rb
@@ -1,6 +1,9 @@
+# frozen_string_literal: true
+
module API
class Events < Grape::API
include PaginationParams
+ include APIGuard
helpers do
params :event_filter_params do
@@ -15,15 +18,34 @@ module API
desc: 'Return events sorted in ascending and descending order'
end
- def present_events(events)
+ RedactedEvent = OpenStruct.new(target_title: 'Confidential event').freeze
+
+ def redact_events(events)
+ events.map do |event|
+ if event.visible_to_user?(current_user)
+ event
+ else
+ RedactedEvent
+ end
+ end
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def present_events(events, redact: true)
events = events.reorder(created_at: params[:sort])
.with_associations
- present paginate(events), with: Entities::Event
+ events = paginate(events)
+ events = redact_events(events) if redact
+
+ present events, with: Entities::Event
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
resource :events do
+ allow_access_with_scope :read_user, if: -> (request) { request.get? }
+
desc "List currently authenticated user's events" do
detail 'This feature was introduced in GitLab 9.3.'
success Entities::Event
@@ -33,19 +55,24 @@ module API
use :event_filter_params
use :sort_params
end
+ # rubocop: disable CodeReuse/ActiveRecord
get do
authenticate!
events = EventsFinder.new(params.merge(source: current_user, current_user: current_user)).execute.preload(:author, :target)
- present_events(events)
+ # Since we're viewing our own events, redaction is unnecessary
+ present_events(events, redact: false)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
params do
requires :id, type: String, desc: 'The ID or Username of the user'
end
resource :users do
+ allow_access_with_scope :read_user, if: -> (request) { request.get? }
+
desc 'Get the contribution events of a specified user' do
detail 'This feature was introduced in GitLab 8.13.'
success Entities::Event
@@ -55,6 +82,7 @@ module API
use :event_filter_params
use :sort_params
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/events' do
user = find_user(params[:id])
not_found!('User') unless user
@@ -63,6 +91,7 @@ module API
present_events(events)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
params do
@@ -77,11 +106,13 @@ module API
use :event_filter_params
use :sort_params
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/events" do
events = EventsFinder.new(params.merge(source: user_project, current_user: current_user)).execute.preload(:author, :target)
present_events(events)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 11d848584d9..1331248699f 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Features < Grape::API
before { authenticated_as_admin! }
@@ -14,13 +16,15 @@ module API
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def gate_targets(params)
targets = []
targets << Feature.group(params[:feature_group]) if params[:feature_group]
- targets << User.find_by_username(params[:user]) if params[:user]
+ targets << UserFinder.new(params[:user]).find_by_username if params[:user]
targets
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
resource :features do
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 29d7489bd7c..bcd2cd48a45 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Files < Grape::API
FILE_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(file_path: API::NO_SLASH_URL_PART_REGEX)
@@ -58,8 +60,8 @@ module API
params :simple_file_params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
- requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.'
- requires :commit_message, type: String, desc: 'Commit message'
+ requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.', allow_blank: false
+ requires :commit_message, type: String, allow_blank: false, desc: 'Commit message'
optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from'
optional :author_email, type: String, desc: 'The email of the author'
optional :author_name, type: String, desc: 'The name of the author'
@@ -80,7 +82,7 @@ module API
desc 'Get raw file metadata from repository'
params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
- requires :ref, type: String, desc: 'The name of branch, tag or commit'
+ requires :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false
end
head ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
@@ -91,7 +93,7 @@ module API
desc 'Get raw file contents from the repository'
params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
- requires :ref, type: String, desc: 'The name of branch, tag commit'
+ requires :ref, type: String, desc: 'The name of branch, tag commit', allow_blank: false
end
get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
@@ -104,7 +106,7 @@ module API
desc 'Get file metadata from repository'
params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
- requires :ref, type: String, desc: 'The name of branch, tag or commit'
+ requires :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false
end
head ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
@@ -115,7 +117,7 @@ module API
desc 'Get a file from the repository'
params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
- requires :ref, type: String, desc: 'The name of branch, tag or commit'
+ requires :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false
end
get ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb
index 3832cdc10a8..dc30e868e2e 100644
--- a/lib/api/group_boards.rb
+++ b/lib/api/group_boards.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class GroupBoards < Grape::API
include BoardsResponses
diff --git a/lib/api/group_milestones.rb b/lib/api/group_milestones.rb
index 93fa0b95857..b36436dbf43 100644
--- a/lib/api/group_milestones.rb
+++ b/lib/api/group_milestones.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class GroupMilestones < Grape::API
include MilestoneResponses
@@ -41,7 +43,7 @@ module API
use :optional_params
end
post ":id/milestones" do
- authorize! :admin_milestones, user_group
+ authorize! :admin_milestone, user_group
create_milestone_for(user_group)
end
@@ -53,11 +55,21 @@ module API
use :update_params
end
put ":id/milestones/:milestone_id" do
- authorize! :admin_milestones, user_group
+ authorize! :admin_milestone, user_group
update_milestone_for(user_group)
end
+ desc 'Remove a project milestone'
+ delete ":id/milestones/:milestone_id" do
+ authorize! :admin_milestone, user_group
+
+ milestone = user_group.milestones.find(params[:milestone_id])
+ Milestones::DestroyService.new(user_group, current_user).execute(milestone)
+
+ status(204)
+ end
+
desc 'Get all issues for a single group milestone' do
success Entities::IssueBasic
end
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
index 55d5c7f1606..ae7241e9a30 100644
--- a/lib/api/group_variables.rb
+++ b/lib/api/group_variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class GroupVariables < Grape::API
include PaginationParams
@@ -27,6 +29,7 @@ module API
params do
requires :key, type: String, desc: 'The key of the variable'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/variables/:key' do
key = params[:key]
variable = user_group.variables.find_by(key: key)
@@ -35,6 +38,7 @@ module API
present variable, with: Entities::Variable
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Create a new variable in a group' do
success Entities::Variable
@@ -64,6 +68,7 @@ module API
optional :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id/variables/:key' do
variable = user_group.variables.find_by(key: params[:key])
@@ -77,6 +82,7 @@ module API
render_validation_error!(variable)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete an existing variable from a group' do
success Entities::Variable
@@ -84,12 +90,14 @@ module API
params do
requires :key, type: String, desc: 'The key of the variable'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/variables/:key' do
variable = user_group.variables.find_by(key: params[:key])
not_found!('GroupVariable') unless variable
destroy_conditionally!(variable)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index b4f441f6a4f..64b998ab455 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Groups < Grape::API
include PaginationParams
@@ -38,6 +40,7 @@ module API
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_groups(params, parent_id = nil)
find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
find_params[:parent] = find_group!(parent_id) if parent_id
@@ -53,6 +56,7 @@ module API
groups
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_group_projects(params)
group = find_group!(params[:id])
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index be17653dbb2..60bf977f0e4 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
include Gitlab::Utils
@@ -95,20 +97,20 @@ module API
end
def find_user(id)
- if id =~ /^\d+$/
- User.find_by(id: id)
- else
- User.find_by(username: id)
- end
+ UserFinder.new(id).find_by_id_or_username
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_project(id)
+ projects = Project.without_deleted
+
if id.is_a?(Integer) || id =~ /^\d+$/
- Project.find_by(id: id)
+ projects.find_by(id: id)
elsif id.include?("/")
- Project.find_by_full_path(id)
+ projects.find_by_full_path(id)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_project!(id)
project = find_project(id)
@@ -120,6 +122,7 @@ module API
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_group(id)
if id.to_s =~ /^\d+$/
Group.find_by(id: id)
@@ -127,6 +130,7 @@ module API
Group.find_by_full_path(id)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_group!(id)
group = find_group(id)
@@ -138,6 +142,7 @@ module API
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_namespace(id)
if id.to_s =~ /^\d+$/
Namespace.find_by(id: id)
@@ -145,6 +150,7 @@ module API
Namespace.find_by_full_path(id)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_namespace!(id)
namespace = find_namespace(id)
@@ -156,6 +162,12 @@ module API
end
end
+ def find_branch!(branch_name)
+ user_project.repository.find_branch(branch_name) || not_found!('Branch')
+ rescue Gitlab::Git::CommandError
+ render_api_error!('The branch refname is invalid', 400)
+ end
+
def find_project_label(id)
labels = available_labels_for(user_project)
label = labels.find_by_id(id) || labels.find_by_title(id)
@@ -163,13 +175,17 @@ module API
label || not_found!('Label')
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_project_issue(iid)
IssuesFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def find_project_merge_request(iid)
MergeRequestsFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_project_commit(id)
user_project.commit_by(oid: id)
@@ -180,11 +196,13 @@ module API
SnippetsFinder.new(current_user, finder_params).find(id)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_merge_request_with_access(iid, access_level = :read_merge_request)
merge_request = user_project.merge_requests.find_by!(iid: iid)
authorize! access_level, merge_request
merge_request
end
+ # rubocop: enable CodeReuse/ActiveRecord
def find_build!(id)
user_project.builds.find(id.to_i)
@@ -276,9 +294,11 @@ module API
Gitlab.rails5? ? permitted_attrs.to_h : permitted_attrs
end
+ # rubocop: disable CodeReuse/ActiveRecord
def filter_by_iid(items, iid)
items.where(iid: iid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def filter_by_search(items, text)
items.search(text)
@@ -357,9 +377,10 @@ module API
# lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60
trace = exception.backtrace
- message = "\n#{exception.class} (#{exception.message}):\n"
+ message = ["\n#{exception.class} (#{exception.message}):\n"]
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << trace.join("\n ")
+ message = message.join
API.logger.add Logger::FATAL, message
@@ -375,12 +396,14 @@ module API
# project helpers
+ # rubocop: disable CodeReuse/ActiveRecord
def reorder_projects(projects)
projects.reorder(params[:order_by] => params[:sort])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def project_finder_params
- finder_params = {}
+ finder_params = { without_deleted: true }
finder_params[:owned] = true if params[:owned].present?
finder_params[:non_public] = true if params[:membership].present?
finder_params[:starred] = true if params[:starred].present?
diff --git a/lib/api/helpers/badges_helpers.rb b/lib/api/helpers/badges_helpers.rb
index 1f8afbf3c90..46ce5b4e7b5 100644
--- a/lib/api/helpers/badges_helpers.rb
+++ b/lib/api/helpers/badges_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module BadgesHelpers
diff --git a/lib/api/helpers/common_helpers.rb b/lib/api/helpers/common_helpers.rb
index 9993caa5249..7551ca50a7f 100644
--- a/lib/api/helpers/common_helpers.rb
+++ b/lib/api/helpers/common_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module CommonHelpers
diff --git a/lib/api/helpers/custom_attributes.rb b/lib/api/helpers/custom_attributes.rb
index 10d652e33f5..88208226c40 100644
--- a/lib/api/helpers/custom_attributes.rb
+++ b/lib/api/helpers/custom_attributes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module CustomAttributes
@@ -12,6 +14,7 @@ module API
desc: 'Filter with custom attributes'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def with_custom_attributes(collection_or_resource, options = {})
options = options.merge(
with_custom_attributes: params[:with_custom_attributes] &&
@@ -24,6 +27,7 @@ module API
[collection_or_resource, options]
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/helpers/custom_validators.rb b/lib/api/helpers/custom_validators.rb
index dd4f6c41131..1058f4e8a5e 100644
--- a/lib/api/helpers/custom_validators.rb
+++ b/lib/api/helpers/custom_validators.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module CustomValidators
@@ -8,8 +10,21 @@ module API
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:absence)
end
end
+
+ class IntegerNoneAny < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ value = params[attr_name]
+
+ return if value.is_a?(Integer) ||
+ [IssuableFinder::FILTER_NONE, IssuableFinder::FILTER_ANY].include?(value.to_s.downcase)
+
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
+ message: "should be an integer, 'None' or 'Any'"
+ end
+ end
end
end
end
Grape::Validations.register_validator(:absence, ::API::Helpers::CustomValidators::Absence)
+Grape::Validations.register_validator(:integer_none_any, ::API::Helpers::CustomValidators::IntegerNoneAny)
diff --git a/lib/api/helpers/headers_helpers.rb b/lib/api/helpers/headers_helpers.rb
index c9c44e3c218..7553af9d156 100644
--- a/lib/api/helpers/headers_helpers.rb
+++ b/lib/api/helpers/headers_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module HeadersHelpers
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index 83151be82ad..4eaaca96b49 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module InternalHelpers
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index fed8846e505..73d58ee7f37 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop:disable GitlabSecurity/PublicSend
module API
@@ -17,6 +19,7 @@ module API
.non_request
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_all_members_for_project(project)
shared_group_ids = project.project_group_links.pluck(:group_id)
project_group_ids = project.group&.self_and_ancestors&.pluck(:id)
@@ -28,13 +31,16 @@ module API
.where(project_authorizations: { project_id: project.id })
.where(source_id: source_ids)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def find_all_members_for_group(group)
source_ids = group.self_and_ancestors.pluck(:id)
Member.includes(:user)
.where(source_id: source_ids)
.where(source_type: 'Namespace')
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index e2984b08eca..216b2c45741 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module NotesHelpers
@@ -92,10 +94,7 @@ module API
parent = noteable_parent(noteable)
- if opts[:created_at]
- opts.delete(:created_at) unless
- current_user.admin? || parent.owned_by?(current_user)
- end
+ opts.delete(:created_at) unless current_user.can?(:set_note_created_at, policy_object)
opts[:updated_at] = opts[:created_at] if opts[:created_at]
diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb
index 3308212216e..d311cbb5f7e 100644
--- a/lib/api/helpers/pagination.rb
+++ b/lib/api/helpers/pagination.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module Pagination
@@ -91,6 +93,7 @@ module API
@request_context = request_context
end
+ # rubocop: disable CodeReuse/ActiveRecord
def paginate(relation)
pagination = KeysetPaginationInfo.new(relation, request_context)
@@ -112,6 +115,7 @@ module API
paged_relation
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
@@ -183,6 +187,7 @@ module API
private
+ # rubocop: disable CodeReuse/ActiveRecord
def add_default_order(relation)
if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty?
relation = relation.order(:id)
@@ -190,6 +195,7 @@ module API
relation
end
+ # rubocop: enable CodeReuse/ActiveRecord
def add_pagination_headers(paginated_data)
header 'X-Per-Page', paginated_data.limit_value.to_s
diff --git a/lib/api/helpers/project_snapshots_helpers.rb b/lib/api/helpers/project_snapshots_helpers.rb
index 94798a8cb51..1b5dc281e38 100644
--- a/lib/api/helpers/project_snapshots_helpers.rb
+++ b/lib/api/helpers/project_snapshots_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module ProjectSnapshotsHelpers
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 381d5e8968c..e6a72b949f9 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module ProjectsHelpers
@@ -26,6 +28,7 @@ module API
optional :avatar, type: File, desc: 'Avatar image for project'
optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
+ optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
end
params :optional_project_params do
diff --git a/lib/api/helpers/related_resources_helpers.rb b/lib/api/helpers/related_resources_helpers.rb
index bc7333ca4b3..793ae11b41d 100644
--- a/lib/api/helpers/related_resources_helpers.rb
+++ b/lib/api/helpers/related_resources_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module RelatedResourcesHelpers
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 61eb88d3331..45d0343bc89 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module Helpers
module Runner
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 516f25db15b..ae40b5f7557 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# Internal access API
class Internal < Grape::API
@@ -6,8 +8,17 @@ module API
helpers ::API::Helpers::InternalHelpers
helpers ::Gitlab::Identifier
+ UNKNOWN_CHECK_RESULT_ERROR = 'Unknown check result'.freeze
+
+ helpers do
+ def response_with_status(code: 200, success: true, message: nil, **extra_options)
+ status code
+ { status: success, message: message }.merge(extra_options).compact
+ end
+ end
+
namespace 'internal' do
- # Check if git command is allowed to project
+ # Check if git command is allowed for project
#
# Params:
# key_id - ssh key id for Git over SSH
@@ -17,9 +28,8 @@ module API
# project - project full_path (not path on disk)
# action - git action (git-upload-pack or git-receive-pack)
# changes - changes as "oldrev newrev ref", see Gitlab::ChangesList
+ # rubocop: disable CodeReuse/ActiveRecord
post "/allowed" do
- status 200
-
# Stores some Git-specific env thread-safely
env = parse_env
Gitlab::Git::HookEnv.set(gl_repository, env) if project
@@ -30,7 +40,7 @@ module API
elsif params[:user_id]
User.find_by(id: params[:user_id])
elsif params[:username]
- User.find_by_username(params[:username])
+ UserFinder.new(params[:username]).find_by_username
end
protocol = params[:protocol]
@@ -49,29 +59,51 @@ module API
namespace_path: namespace_path, project_path: project_path,
redirected_path: redirected_path)
- begin
- access_checker.check(params[:action], params[:changes])
- @project ||= access_checker.project
- rescue Gitlab::GitAccess::UnauthorizedError, Gitlab::GitAccess::NotFoundError => e
- break { status: false, message: e.message }
- end
+ check_result = begin
+ result = access_checker.check(params[:action], params[:changes])
+ @project ||= access_checker.project
+ result
+ rescue Gitlab::GitAccess::UnauthorizedError => e
+ break response_with_status(code: 401, success: false, message: e.message)
+ rescue Gitlab::GitAccess::TimeoutError => e
+ break response_with_status(code: 503, success: false, message: e.message)
+ rescue Gitlab::GitAccess::NotFoundError => e
+ break response_with_status(code: 404, success: false, message: e.message)
+ end
log_user_activity(actor)
- {
- status: true,
- gl_repository: gl_repository,
- gl_id: Gitlab::GlId.gl_id(user),
- gl_username: user&.username,
-
- # This repository_path is a bogus value but gitlab-shell still requires
- # its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135
- repository_path: '/',
+ case check_result
+ when ::Gitlab::GitAccessResult::Success
+ payload = {
+ gl_repository: gl_repository,
+ gl_id: Gitlab::GlId.gl_id(user),
+ gl_username: user&.username,
+ git_config_options: [],
+
+ # This repository_path is a bogus value but gitlab-shell still requires
+ # its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135
+ repository_path: '/',
+
+ gitaly: gitaly_payload(params[:action])
+ }
+
+ # Custom option for git-receive-pack command
+ receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
+ if receive_max_input_size > 0
+ payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
+ end
- gitaly: gitaly_payload(params[:action])
- }
+ response_with_status(**payload)
+ when ::Gitlab::GitAccessResult::CustomAction
+ response_with_status(code: 300, message: check_result.message, payload: check_result.payload)
+ else
+ response_with_status(code: 500, success: false, message: UNKNOWN_CHECK_RESULT_ERROR)
+ end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
post "/lfs_authenticate" do
status 200
@@ -93,6 +125,7 @@ module API
repository_http_path: project.http_url_to_repo
}
end
+ # rubocop: enable CodeReuse/ActiveRecord
get "/merge_request_urls" do
merge_request_urls
@@ -101,6 +134,7 @@ module API
#
# Get a ssh key using the fingerprint
#
+ # rubocop: disable CodeReuse/ActiveRecord
get "/authorized_keys" do
fingerprint = params.fetch(:fingerprint) do
Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint
@@ -109,10 +143,12 @@ module API
not_found!("Key") if key.nil?
present key, with: Entities::SSHKey
end
+ # rubocop: enable CodeReuse/ActiveRecord
#
# Discover user by ssh key, user id or username
#
+ # rubocop: disable CodeReuse/ActiveRecord
get "/discover" do
if params[:key_id]
key = Key.find(params[:key_id])
@@ -120,11 +156,12 @@ module API
elsif params[:user_id]
user = User.find_by(id: params[:user_id])
elsif params[:username]
- user = User.find_by(username: params[:username])
+ user = UserFinder.new(params[:username]).find_by_username
end
present user, with: Entities::UserSafe
end
+ # rubocop: enable CodeReuse/ActiveRecord
get "/check" do
{
@@ -151,6 +188,7 @@ module API
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
post '/two_factor_recovery_codes' do
status 200
@@ -192,6 +230,7 @@ module API
{ success: true, recovery_codes: codes }
end
+ # rubocop: enable CodeReuse/ActiveRecord
post '/pre_receive' do
status 200
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index bda05d1795b..491b5085bb8 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Issues < Grape::API
include PaginationParams
@@ -6,7 +8,17 @@ module API
helpers ::Gitlab::IssuableMetadata
+ # EE::API::Issues would override the following helpers
+ helpers do
+ params :issues_params_ee do
+ end
+
+ params :issue_params_ee do
+ end
+ end
+
helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
def find_issues(args = {})
args = declared_params.merge(args)
@@ -16,10 +28,11 @@ module API
args[:scope] = args[:scope].underscore if args[:scope]
issues = IssuesFinder.new(current_user, args).execute
- .preload(:assignees, :labels, :notes, :timelogs, :project, :author)
+ .preload(:assignees, :labels, :notes, :timelogs, :project, :author, :closed_by)
issues.reorder(args[:order_by] => args[:sort])
end
+ # rubocop: enable CodeReuse/ActiveRecord
params :issues_params do
optional :labels, type: String, desc: 'Comma-separated list of label names'
@@ -36,14 +49,17 @@ module API
optional :updated_after, type: DateTime, desc: 'Return issues updated after the specified time'
optional :updated_before, type: DateTime, desc: 'Return issues updated before the specified time'
optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID'
- optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID'
+ optional :assignee_id, types: [Integer, String], integer_none_any: true,
+ desc: 'Return issues which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
use :pagination
+
+ use :issues_params_ee
end
- params :issue_params_ce do
+ params :issue_params do
optional :description, type: String, desc: 'The description of an issue'
optional :assignee_ids, type: Array[Integer], desc: 'The array of user IDs to assign issue'
optional :assignee_id, type: Integer, desc: '[Deprecated] The ID of a user to assign issue'
@@ -52,10 +68,8 @@ module API
optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
optional :discussion_locked, type: Boolean, desc: " Boolean parameter indicating if the issue's discussion is locked"
- end
- params :issue_params do
- use :issue_params_ce
+ use :issue_params_ee
end
end
@@ -172,11 +186,8 @@ module API
authorize! :create_issue, user_project
- # Setting created_at time or iid only allowed for admins and project owners
- unless current_user.admin? || user_project.owner == current_user
- params.delete(:created_at)
- params.delete(:iid)
- end
+ params.delete(:created_at) unless current_user.can?(:set_issue_created_at, user_project)
+ params.delete(:iid) unless current_user.can?(:set_issue_iid, user_project)
issue_params = declared_params(include_missing: false)
@@ -210,14 +221,15 @@ module API
at_least_one_of :title, :description, :assignee_ids, :assignee_id, :milestone_id, :discussion_locked,
:labels, :created_at, :due_date, :confidential, :state_event
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id/issues/:issue_iid' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42322')
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue
- # Setting created_at time only allowed for admins and project owners
- unless current_user.admin? || user_project.owner == current_user
+ # Setting created_at time only allowed for admins and project/group owners
+ unless current_user.admin? || user_project.owner == current_user || current_user.owned_groups.include?(user_project.owner)
params.delete(:updated_at)
end
@@ -237,6 +249,7 @@ module API
render_validation_error!(issue)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Move an existing issue' do
success Entities::Issue
@@ -245,6 +258,7 @@ module API
requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
requires :to_project_id, type: Integer, desc: 'The ID of the new project'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/issues/:issue_iid/move' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42323')
@@ -261,11 +275,13 @@ module API
render_api_error!(error.message, 400)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete a project issue'
params do
requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ":id/issues/:issue_iid" do
issue = user_project.issues.find_by(iid: params[:issue_iid])
not_found!('Issue') unless issue
@@ -276,6 +292,31 @@ module API
Issuable::DestroyService.new(user_project, current_user).execute(issue)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ desc 'List merge requests that are related to the issue' do
+ success Entities::MergeRequestBasic
+ end
+ params do
+ requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
+ end
+ get ':id/issues/:issue_iid/related_merge_requests' do
+ issue = find_project_issue(params[:issue_iid])
+
+ merge_request_iids = ::Issues::ReferencedMergeRequestsService.new(user_project, current_user)
+ .execute(issue)
+ .flatten
+ .map(&:iid)
+
+ merge_requests =
+ if merge_request_iids.present?
+ MergeRequestsFinder.new(current_user, project_id: user_project.id, iids: merge_request_iids).execute
+ else
+ MergeRequest.none
+ end
+
+ present paginate(merge_requests), with: Entities::MergeRequestBasic, current_user: current_user, project: user_project
+ end
desc 'List merge requests closing issue' do
success Entities::MergeRequestBasic
@@ -283,6 +324,7 @@ module API
params do
requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/issues/:issue_iid/closed_by' do
issue = find_project_issue(params[:issue_iid])
@@ -291,6 +333,7 @@ module API
present paginate(merge_requests), with: Entities::MergeRequestBasic, current_user: current_user, project: user_project
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'List participants for an issue' do
success Entities::UserBasic
diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb
index 32379d7c8ab..2229cbcd9d4 100644
--- a/lib/api/job_artifacts.rb
+++ b/lib/api/job_artifacts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class JobArtifacts < Grape::API
before { authenticate_non_get! }
@@ -21,6 +23,7 @@ module API
requires :job, type: String, desc: 'The name for the job'
end
route_setting :authentication, job_token_allowed: true
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/jobs/artifacts/:ref_name/download',
requirements: { ref_name: /.+/ } do
authorize_download_artifacts!
@@ -30,6 +33,7 @@ module API
present_carrierwave_file!(latest_build.artifacts_file)
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Download the artifacts archive from a job' do
detail 'This feature was introduced in GitLab 8.5'
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index 10c6e565f09..697555c9605 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Jobs < Grape::API
include PaginationParams
@@ -34,13 +36,15 @@ module API
use :optional_scope
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/jobs' do
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
- builds = builds.preload(:user, :job_artifacts_archive, :runner, pipeline: :project)
+ builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, pipeline: :project)
present paginate(builds), with: Entities::Job
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get pipeline jobs' do
success Entities::Job
@@ -50,14 +54,16 @@ module API
use :optional_scope
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/pipelines/:pipeline_id/jobs' do
pipeline = user_project.pipelines.find(params[:pipeline_id])
builds = pipeline.builds
builds = filter_builds(builds, params[:scope])
- builds = builds.preload(:job_artifacts_archive, project: [:namespace])
+ builds = builds.preload(:job_artifacts_archive, :job_artifacts, project: [:namespace])
present paginate(builds), with: Entities::Job
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a specific job of a project' do
success Entities::Job
@@ -145,7 +151,7 @@ module API
present build, with: Entities::Job
end
- desc 'Trigger a manual job' do
+ desc 'Trigger a actionable job (manual, delayed, etc)' do
success Entities::Job
detail 'This feature was added in GitLab 8.11'
end
@@ -168,6 +174,7 @@ module API
end
helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
def filter_builds(builds, scope)
return builds if scope.nil? || scope.empty?
@@ -178,6 +185,7 @@ module API
builds.where(status: available_statuses && scope)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/keys.rb b/lib/api/keys.rb
index fd93f797f72..d5280a0035d 100644
--- a/lib/api/keys.rb
+++ b/lib/api/keys.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# Keys API
class Keys < Grape::API
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 81eaf56e48e..28555454307 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Labels < Grape::API
include PaginationParams
@@ -27,6 +29,7 @@ module API
optional :description, type: String, desc: 'The description of label to be created'
optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/labels' do
authorize! :admin_label, user_project
@@ -43,6 +46,7 @@ module API
render_validation_error!(label)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete an existing label' do
success Entities::Label
@@ -50,6 +54,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the label to be deleted'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/labels' do
authorize! :admin_label, user_project
@@ -58,6 +63,7 @@ module API
destroy_conditionally!(label)
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Update an existing label. At least one optional parameter is required.' do
success Entities::Label
@@ -70,6 +76,7 @@ module API
optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
at_least_one_of :new_name, :color, :description, :priority
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id/labels' do
authorize! :admin_label, user_project
@@ -95,6 +102,7 @@ module API
present label, with: Entities::Label, current_user: current_user, project: user_project
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
index d202eaa4c49..0342a4b6654 100644
--- a/lib/api/lint.rb
+++ b/lib/api/lint.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Lint < Grape::API
namespace :ci do
diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb
index 5d55224c1a7..de77bef43ce 100644
--- a/lib/api/markdown.rb
+++ b/lib/api/markdown.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Markdown < Grape::API
params do
@@ -10,7 +12,8 @@ module API
detail "This feature was introduced in GitLab 11.0."
end
post do
- context = { only_path: false }
+ context = { only_path: false, current_user: current_user }
+ context[:pipeline] = params[:gfm] ? :full : :plain_markdown
if params[:project]
project = Project.find_by_full_path(params[:project])
@@ -22,9 +25,7 @@ module API
context[:skip_project_check] = true
end
- context[:pipeline] = params[:gfm] ? :full : :plain_markdown
-
- { html: Banzai.render(params[:text], context) }
+ { html: Banzai.render_and_post_process(params[:text], context) }
end
end
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index d23dd834c69..a8f67be3463 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Members < Grape::API
include PaginationParams
@@ -18,6 +20,7 @@ module API
optional :query, type: String, desc: 'A query string to search for members'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/members" do
source = find_source(source_type, params[:id])
@@ -27,6 +30,7 @@ module API
present members, with: Entities::Member
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Gets a list of group or project members viewable by the authenticated user, including those who gained membership through ancestor group.' do
success Entities::Member
@@ -35,6 +39,7 @@ module API
optional :query, type: String, desc: 'A query string to search for members'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/members/all" do
source = find_source(source_type, params[:id])
@@ -44,6 +49,7 @@ module API
present members, with: Entities::Member
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Gets a member of a group or project.' do
success Entities::Member
@@ -51,6 +57,7 @@ module API
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/members/:user_id" do
source = find_source(source_type, params[:id])
@@ -59,6 +66,7 @@ module API
present member, with: Entities::Member
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Adds a member to a group or project.' do
success Entities::Member
@@ -68,6 +76,7 @@ module API
requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ":id/members" do
source = find_source(source_type, params[:id])
authorize_admin_source!(source_type, source)
@@ -88,6 +97,7 @@ module API
render_validation_error!(member)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Updates a member of a group or project.' do
success Entities::Member
@@ -97,6 +107,7 @@ module API
requires :access_level, type: Integer, desc: 'A valid access level'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ":id/members/:user_id" do
source = find_source(source_type, params.delete(:id))
authorize_admin_source!(source_type, source)
@@ -113,11 +124,13 @@ module API
render_validation_error!(updated_member)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Removes a user from a group or project.'
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ":id/members/:user_id" do
source = find_source(source_type, params[:id])
member = source.members.find_by!(user_id: params[:user_id])
@@ -126,6 +139,7 @@ module API
::Members::DestroyService.new(current_user).execute(member)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb
index 95ef8f42954..e4fb890960a 100644
--- a/lib/api/merge_request_diffs.rb
+++ b/lib/api/merge_request_diffs.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# MergeRequestDiff API
class MergeRequestDiffs < Grape::API
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index abad418771c..16f07f16387 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class MergeRequests < Grape::API
include PaginationParams
@@ -28,9 +30,9 @@ module API
end
helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
def find_merge_requests(args = {})
args = declared_params.merge(args)
-
args[:milestone_title] = args.delete(:milestone)
args[:label_name] = args.delete(:labels)
args[:scope] = args[:scope].underscore if args[:scope]
@@ -43,8 +45,9 @@ module API
return merge_requests if args[:view] == 'simple'
merge_requests
- .preload(:notes, :author, :assignee, :milestone, :latest_merge_request_diff, :labels, :timelogs)
+ .preload(:notes, :author, :assignee, :milestone, :latest_merge_request_diff, :labels, :timelogs, metrics: [:latest_closed_by, :merged_by])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def merge_request_pipelines_with_access
authorize! :read_pipeline, user_project
@@ -86,13 +89,15 @@ module API
optional :updated_before, type: DateTime, desc: 'Return merge requests updated before the specified time'
optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID'
- optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID'
+ optional :assignee_id, types: [Integer, String], integer_none_any: true,
+ desc: 'Return merge requests which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
optional :source_branch, type: String, desc: 'Return merge requests with the given source branch'
optional :target_branch, type: String, desc: 'Return merge requests with the given target branch'
optional :search, type: String, desc: 'Search merge requests for text present in the title or description'
+ optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
use :pagination
end
end
@@ -233,6 +238,7 @@ module API
params do
requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML'
+ optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch'
end
desc 'Get a single merge request' do
success Entities::MergeRequest
@@ -240,7 +246,7 @@ module API
get ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html]
+ present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html], include_diverged_commits_count: params[:include_diverged_commits_count]
end
desc 'Get the participants of a merge request' do
diff --git a/lib/api/milestone_responses.rb b/lib/api/milestone_responses.rb
index a8eb137e46a..a0ca39b69d4 100644
--- a/lib/api/milestone_responses.rb
+++ b/lib/api/milestone_responses.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module MilestoneResponses
extend ActiveSupport::Concern
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index 32b77aedba8..76639fbb031 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Namespaces < Grape::API
include PaginationParams
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 39923e6d5b5..9f323b87baf 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Notes < Grape::API
include PaginationParams
@@ -28,6 +30,7 @@ module API
desc: 'Return notes sorted in `asc` or `desc` order.'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/#{noteables_str}/:noteable_id/notes" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
@@ -45,6 +48,7 @@ module API
.reject { |n| n.cross_reference_not_visible_for?(current_user) }
present notes, with: Entities::Note
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc "Get a single #{noteable_type.to_s.downcase} note" do
success Entities::Note
diff --git a/lib/api/notification_settings.rb b/lib/api/notification_settings.rb
index 0266bf2f717..4d9a4629268 100644
--- a/lib/api/notification_settings.rb
+++ b/lib/api/notification_settings.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# notification_settings API
class NotificationSettings < Grape::API
@@ -23,7 +25,7 @@ module API
params do
optional :level, type: String, desc: 'The global notification level'
optional :notification_email, type: String, desc: 'The email address to send notifications'
- NotificationSetting::EMAIL_EVENTS.each do |event|
+ NotificationSetting.email_events.each do |event|
optional event, type: Boolean, desc: 'Enable/disable this notification'
end
end
@@ -50,7 +52,9 @@ module API
end
end
- %w[group project].each do |source_type|
+ [Group, Project].each do |source_class|
+ source_type = source_class.name.underscore
+
params do
requires :id, type: String, desc: "The #{source_type} ID"
end
@@ -73,7 +77,7 @@ module API
end
params do
optional :level, type: String, desc: "The #{source_type} notification level"
- NotificationSetting::EMAIL_EVENTS.each do |event|
+ NotificationSetting.email_events(source_class).each do |event|
optional event, type: Boolean, desc: 'Enable/disable this notification'
end
end
diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb
index ba33993d852..c9ad47e0f0d 100644
--- a/lib/api/pages_domains.rb
+++ b/lib/api/pages_domains.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class PagesDomains < Grape::API
include PaginationParams
@@ -13,9 +15,11 @@ module API
end
helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
def find_pages_domain!
user_project.pages_domains.find_by(domain: params[:domain]) || not_found!('PagesDomain')
end
+ # rubocop: enable CodeReuse/ActiveRecord
def pages_domain
@pages_domain ||= find_pages_domain!
@@ -61,11 +65,13 @@ module API
params do
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/pages/domains" do
authorize! :read_pages, user_project
present paginate(user_project.pages_domains.order(:domain)), with: Entities::PagesDomain
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a single pages domain' do
success Entities::PagesDomain
diff --git a/lib/api/pagination_params.rb b/lib/api/pagination_params.rb
index f566eb3ed2b..ae03595eb25 100644
--- a/lib/api/pagination_params.rb
+++ b/lib/api/pagination_params.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# Concern for declare pagination params.
#
diff --git a/lib/api/pipeline_schedules.rb b/lib/api/pipeline_schedules.rb
index 37f32411296..ed0a38b9d70 100644
--- a/lib/api/pipeline_schedules.rb
+++ b/lib/api/pipeline_schedules.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class PipelineSchedules < Grape::API
include PaginationParams
@@ -16,6 +18,7 @@ module API
optional :scope, type: String, values: %w[active inactive],
desc: 'The scope of pipeline schedules'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/pipeline_schedules' do
authorize! :read_pipeline_schedule, user_project
@@ -23,6 +26,7 @@ module API
.preload([:owner, :last_pipeline])
present paginate(schedules), with: Entities::PipelineSchedule
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a single pipeline schedule' do
success Entities::PipelineScheduleDetails
@@ -39,7 +43,7 @@ module API
end
params do
requires :description, type: String, desc: 'The description of pipeline schedule'
- requires :ref, type: String, desc: 'The branch/tag name will be triggered'
+ requires :ref, type: String, desc: 'The branch/tag name will be triggered', allow_blank: false
requires :cron, type: String, desc: 'The cron'
optional :cron_timezone, type: String, default: 'UTC', desc: 'The timezone'
optional :active, type: Boolean, default: true, desc: 'The activation of pipeline schedule'
@@ -161,6 +165,7 @@ module API
end
helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
def pipeline_schedule
@pipeline_schedule ||=
user_project
@@ -172,7 +177,9 @@ module API
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def pipeline_schedule_variable
@pipeline_schedule_variable ||=
pipeline_schedule.variables.find_by(key: params[:key]).tap do |pipeline_schedule_variable|
@@ -181,6 +188,7 @@ module API
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb
index 5d33a13d035..cba1e3a6684 100644
--- a/lib/api/pipelines.rb
+++ b/lib/api/pipelines.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Pipelines < Grape::API
include PaginationParams
@@ -43,6 +45,7 @@ module API
requires :ref, type: String, desc: 'Reference'
optional :variables, Array, desc: 'Array of variables available in the pipeline'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/pipeline' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42124')
@@ -63,6 +66,7 @@ module API
render_validation_error!(new_pipeline)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Gets a specific pipeline for the project' do
detail 'This feature was introduced in GitLab 8.11'
@@ -77,6 +81,21 @@ module API
present pipeline, with: Entities::Pipeline
end
+ desc 'Deletes a pipeline' do
+ detail 'This feature was introduced in GitLab 11.6'
+ http_codes [[204, 'Pipeline was deleted'], [403, 'Forbidden']]
+ end
+ params do
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ end
+ delete ':id/pipelines/:pipeline_id' do
+ authorize! :destroy_pipeline, pipeline
+
+ destroy_conditionally!(pipeline) do
+ ::Ci::DestroyPipelineService.new(user_project, current_user).execute(pipeline)
+ end
+ end
+
desc 'Retry builds in the pipeline' do
detail 'This feature was introduced in GitLab 8.11.'
success Entities::Pipeline
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index 15c57a2fc02..e34ed0bdb44 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class ProjectExport < Grape::API
before do
@@ -21,12 +23,8 @@ module API
detail 'This feature was introduced in GitLab 10.6.'
end
get ':id/export/download' do
- path = user_project.export_project_path
-
- if path
- present_disk_file!(path, File.basename(path), 'application/gzip')
- elsif user_project.export_project_object_exists?
- present_carrierwave_file!(user_project.import_export_upload.export_file)
+ if user_project.export_file_exists?
+ present_carrierwave_file!(user_project.export_file)
else
render_api_error!('404 Not found or has expired', 404)
end
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 4760a1c08d7..4af4c6ac593 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class ProjectHooks < Grape::API
include PaginationParams
@@ -20,6 +22,7 @@ module API
optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events"
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response"
+ optional :push_events_branch_filter, type: String, desc: "Trigger hook on specified branch only"
end
end
@@ -63,6 +66,7 @@ module API
present hook, with: Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
+ error!("Invalid branch filter given", 422) if hook.errors[:push_events_branch_filter].present?
not_found!("Project hook #{hook.errors.messages}")
end
@@ -84,6 +88,7 @@ module API
present hook, with: Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
+ error!("Invalid branch filter given", 422) if hook.errors[:push_events_branch_filter].present?
not_found!("Project hook #{hook.errors.messages}")
end
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
index bc5152e539f..cbfa0c5bc1c 100644
--- a/lib/api/project_import.rb
+++ b/lib/api/project_import.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class ProjectImport < Grape::API
include PaginationParams
diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb
index 306dc0e63d7..c7137ba5217 100644
--- a/lib/api/project_milestones.rb
+++ b/lib/api/project_milestones.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class ProjectMilestones < Grape::API
include PaginationParams
@@ -64,7 +66,8 @@ module API
delete ":id/milestones/:milestone_id" do
authorize! :admin_milestone, user_project
- user_project.milestones.find(params[:milestone_id]).destroy
+ milestone = user_project.milestones.find(params[:milestone_id])
+ Milestones::DestroyService.new(user_project, current_user).execute(milestone)
status(204)
end
diff --git a/lib/api/project_snapshots.rb b/lib/api/project_snapshots.rb
index 71005acc587..175fbb2ce92 100644
--- a/lib/api/project_snapshots.rb
+++ b/lib/api/project_snapshots.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class ProjectSnapshots < Grape::API
helpers ::API::Helpers::ProjectSnapshotsHelpers
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index 1de5551fee9..f3a1b73b153 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class ProjectSnippets < Grape::API
include PaginationParams
@@ -49,7 +51,7 @@ module API
params do
requires :title, type: String, desc: 'The title of the snippet'
requires :file_name, type: String, desc: 'The file name of the snippet'
- requires :code, type: String, desc: 'The content of the snippet'
+ requires :code, type: String, allow_blank: false, desc: 'The content of the snippet'
optional :description, type: String, desc: 'The description of a snippet'
requires :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
@@ -78,13 +80,14 @@ module API
requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
optional :title, type: String, desc: 'The title of the snippet'
optional :file_name, type: String, desc: 'The file name of the snippet'
- optional :code, type: String, desc: 'The content of the snippet'
+ optional :code, type: String, allow_blank: false, desc: 'The content of the snippet'
optional :description, type: String, desc: 'The description of a snippet'
optional :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
desc: 'The visibility of the snippet'
at_least_one_of :title, :file_name, :code, :visibility_level
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ":id/snippets/:snippet_id" do
snippet = snippets_for_current_user.find_by(id: params.delete(:snippet_id))
not_found!('Snippet') unless snippet
@@ -107,11 +110,13 @@ module API
render_validation_error!(snippet)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete a project snippet'
params do
requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ":id/snippets/:snippet_id" do
snippet = snippets_for_current_user.find_by(id: params[:snippet_id])
not_found!('Snippet') unless snippet
@@ -120,11 +125,13 @@ module API
destroy_conditionally!(snippet)
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a raw project snippet'
params do
requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/snippets/:snippet_id/raw" do
snippet = snippets_for_current_user.find_by(id: params[:snippet_id])
not_found!('Snippet') unless snippet
@@ -133,6 +140,7 @@ module API
content_type 'text/plain'
present snippet.content
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get the user agent details for a project snippet' do
success Entities::UserAgentDetail
@@ -140,6 +148,7 @@ module API
params do
requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/snippets/:snippet_id/user_agent_detail" do
authenticated_as_admin!
@@ -149,6 +158,7 @@ module API
present snippet.user_agent_detail, with: Entities::UserAgentDetail
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb
new file mode 100644
index 00000000000..d05ddad7466
--- /dev/null
+++ b/lib/api/project_templates.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module API
+ class ProjectTemplates < Grape::API
+ include PaginationParams
+
+ TEMPLATE_TYPES = %w[dockerfiles gitignores gitlab_ci_ymls licenses].freeze
+
+ before { authenticate_non_get! }
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ requires :type, type: String, values: TEMPLATE_TYPES, desc: 'The type (dockerfiles|gitignores|gitlab_ci_ymls|licenses) of the template'
+ end
+ resource :projects do
+ desc 'Get a list of templates available to this project' do
+ detail 'This endpoint was introduced in GitLab 11.4'
+ end
+ params do
+ use :pagination
+ end
+ get ':id/templates/:type' do
+ templates = TemplateFinder
+ .build(params[:type], user_project)
+ .execute
+
+ present paginate(::Kaminari.paginate_array(templates)), with: Entities::TemplatesList
+ end
+
+ desc 'Download a template available to this project' do
+ detail 'This endpoint was introduced in GitLab 11.4'
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the template'
+
+ optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses'
+ optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses'
+ end
+ get ':id/templates/:type/:name', requirements: { name: /[\w\.-]+/ } do
+ template = TemplateFinder
+ .build(params[:type], user_project, name: params[:name])
+ .execute
+
+ not_found!('Template') unless template.present?
+
+ template.resolve!(
+ project_name: params[:project].presence,
+ fullname: params[:fullname].presence || current_user&.name
+ )
+
+ if template.is_a?(::LicenseTemplate)
+ present template, with: Entities::License
+ else
+ present template, with: Entities::Template
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 5738bf220c6..0a914f9012e 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'declarative_policy'
module API
@@ -112,7 +114,8 @@ module API
options = options.reverse_merge(
with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails,
statistics: params[:statistics],
- current_user: current_user
+ current_user: current_user,
+ license: false
)
options[:with] = Entities::BasicProjectDetails if params[:simple]
@@ -198,6 +201,7 @@ module API
use :optional_project_params
use :create_params
end
+ # rubocop: disable CodeReuse/ActiveRecord
post "user/:user_id" do
authenticated_as_admin!
user = User.find_by(id: params.delete(:user_id))
@@ -214,6 +218,7 @@ module API
render_validation_error!(project)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
params do
@@ -226,13 +231,17 @@ module API
params do
use :statistics_params
use :with_custom_attributes
+
+ optional :license, type: Boolean, default: false,
+ desc: 'Include project license data'
end
get ":id" do
options = {
with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails,
current_user: current_user,
user_can_admin_project: can?(current_user, :admin_project, user_project),
- statistics: params[:statistics]
+ statistics: params[:statistics],
+ license: params[:license]
}
project, options = with_custom_attributes(user_project, options)
@@ -283,6 +292,12 @@ module API
present_projects forks
end
+ desc 'Check pages access of this project'
+ get ':id/pages_access' do
+ authorize! :read_pages_content, user_project unless user_project.public_pages?
+ status 200
+ end
+
desc 'Update an existing project' do
success Entities::Project
end
@@ -386,7 +401,7 @@ module API
requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from'
end
post ":id/fork/:forked_from_id" do
- authenticated_as_admin!
+ authorize! :admin_project, user_project
fork_from_project = find_project!(params[:forked_from_id])
@@ -444,6 +459,7 @@ module API
params do
requires :group_id, type: Integer, desc: 'The ID of the group'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ":id/share/:group_id" do
authorize! :admin_project, user_project
@@ -452,6 +468,7 @@ module API
destroy_conditionally!(link)
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Upload a file'
params do
diff --git a/lib/api/projects_relation_builder.rb b/lib/api/projects_relation_builder.rb
index 6482fd94ab8..8edcfea7c93 100644
--- a/lib/api/projects_relation_builder.rb
+++ b/lib/api/projects_relation_builder.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
module API
module ProjectsRelationBuilder
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def prepare_relation(projects_relation, options = {})
projects_relation = preload_relation(projects_relation, options)
execute_batch_counting(projects_relation)
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index a30eb46c220..47752f40e58 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class ProtectedBranches < Grape::API
include PaginationParams
@@ -16,11 +18,13 @@ module API
params do
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/protected_branches' do
protected_branches = user_project.protected_branches.preload(:push_access_levels, :merge_access_levels)
present paginate(protected_branches), with: Entities::ProtectedBranch, project: user_project
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a single protected branch' do
success Entities::ProtectedBranch
@@ -28,11 +32,13 @@ module API
params do
requires :name, type: String, desc: 'The name of the branch or wildcard'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
protected_branch = user_project.protected_branches.find_by!(name: params[:name])
present protected_branch, with: Entities::ProtectedBranch, project: user_project
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Protect a single branch or wildcard' do
success Entities::ProtectedBranch
@@ -40,12 +46,13 @@ module API
params do
requires :name, type: String, desc: 'The name of the protected branch'
optional :push_access_level, type: Integer,
- values: ProtectedRefAccess::ALLOWED_ACCESS_LEVELS,
+ values: ProtectedBranch::PushAccessLevel.allowed_access_levels,
desc: 'Access levels allowed to push (defaults: `40`, maintainer access level)'
optional :merge_access_level, type: Integer,
- values: ProtectedRefAccess::ALLOWED_ACCESS_LEVELS,
+ values: ProtectedBranch::MergeAccessLevel.allowed_access_levels,
desc: 'Access levels allowed to merge (defaults: `40`, maintainer access level)'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/protected_branches' do
protected_branch = user_project.protected_branches.find_by(name: params[:name])
if protected_branch
@@ -62,11 +69,13 @@ module API
render_api_error!(protected_branch.errors.full_messages, 422)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Unprotect a single branch'
params do
requires :name, type: String, desc: 'The name of the protected branch'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
protected_branch = user_project.protected_branches.find_by!(name: params[:name])
@@ -75,6 +84,7 @@ module API
destroy_service.execute(protected_branch)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/protected_tags.rb b/lib/api/protected_tags.rb
new file mode 100644
index 00000000000..ed1c5f0cc05
--- /dev/null
+++ b/lib/api/protected_tags.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+module API
+ class ProtectedTags < Grape::API
+ include PaginationParams
+
+ TAG_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX)
+
+ before { authorize_admin_project }
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
+ desc "Get a project's protected tags" do
+ detail 'This feature was introduced in GitLab 11.3.'
+ success Entities::ProtectedTag
+ end
+ params do
+ use :pagination
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ':id/protected_tags' do
+ protected_tags = user_project.protected_tags.preload(:create_access_levels)
+
+ present paginate(protected_tags), with: Entities::ProtectedTag, project: user_project
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ desc 'Get a single protected tag' do
+ detail 'This feature was introduced in GitLab 11.3.'
+ success Entities::ProtectedTag
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the tag or wildcard'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do
+ protected_tag = user_project.protected_tags.find_by!(name: params[:name])
+
+ present protected_tag, with: Entities::ProtectedTag, project: user_project
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ desc 'Protect a single tag or wildcard' do
+ detail 'This feature was introduced in GitLab 11.3.'
+ success Entities::ProtectedTag
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the protected tag'
+ optional :create_access_level, type: Integer, default: Gitlab::Access::MAINTAINER,
+ values: ProtectedTag::CreateAccessLevel.allowed_access_levels,
+ desc: 'Access levels allowed to create (defaults: `40`, maintainer access level)'
+ end
+ post ':id/protected_tags' do
+ protected_tags_params = {
+ name: params[:name],
+ create_access_levels_attributes: [{ access_level: params[:create_access_level] }]
+ }
+
+ protected_tag = ::ProtectedTags::CreateService.new(user_project,
+ current_user,
+ protected_tags_params).execute
+
+ if protected_tag.persisted?
+ present protected_tag, with: Entities::ProtectedTag, project: user_project
+ else
+ render_api_error!(protected_tag.errors.full_messages, 422)
+ end
+ end
+
+ desc 'Unprotect a single tag' do
+ detail 'This feature was introduced in GitLab 11.3.'
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the protected tag'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ delete ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do
+ protected_tag = user_project.protected_tags.find_by!(name: params[:name])
+
+ destroy_conditionally!(protected_tag)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 79736107bbb..dc844c0bd27 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'mime/types'
module API
@@ -128,18 +130,13 @@ module API
success Entities::Commit
end
params do
- # For now we just support 2 refs passed, but `merge-base` supports
- # multiple defining this as an Array instead of 2 separate params will
- # make sure we don't need to deprecate this API in favor of one
- # supporting multiple commits when this functionality gets added to
- # Gitaly
requires :refs, type: Array[String]
end
get ':id/repository/merge_base' do
refs = params[:refs]
- unless refs.size == 2
- render_api_error!('Provide exactly 2 refs', 400)
+ if refs.size < 2
+ render_api_error!('Provide at least 2 refs', 400)
end
merge_base = Gitlab::Git::MergeBase.new(user_project.repository, refs)
diff --git a/lib/api/resource_label_events.rb b/lib/api/resource_label_events.rb
new file mode 100644
index 00000000000..b6fbe8c0235
--- /dev/null
+++ b/lib/api/resource_label_events.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module API
+ class ResourceLabelEvents < Grape::API
+ include PaginationParams
+ helpers ::API::Helpers::NotesHelpers
+
+ before { authenticate! }
+
+ EVENTABLE_TYPES = [Issue, MergeRequest].freeze
+
+ EVENTABLE_TYPES.each do |eventable_type|
+ parent_type = eventable_type.parent_class.to_s.underscore
+ eventables_str = eventable_type.to_s.underscore.pluralize
+
+ params do
+ requires :id, type: String, desc: "The ID of a #{parent_type}"
+ end
+ resource parent_type.pluralize.to_sym, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
+ desc "Get a list of #{eventable_type.to_s.downcase} resource label events" do
+ success Entities::ResourceLabelEvent
+ detail 'This feature was introduced in 11.3'
+ end
+ params do
+ requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable'
+ use :pagination
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ":id/#{eventables_str}/:eventable_id/resource_label_events" do
+ eventable = find_noteable(parent_type, eventables_str, params[:eventable_id])
+ events = eventable.resource_label_events.includes(:label, :user)
+
+ present paginate(events), with: Entities::ResourceLabelEvent
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ desc "Get a single #{eventable_type.to_s.downcase} resource label event" do
+ success Entities::ResourceLabelEvent
+ detail 'This feature was introduced in 11.3'
+ end
+ params do
+ requires :event_id, type: String, desc: 'The ID of a resource label event'
+ requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable'
+ end
+ get ":id/#{eventables_str}/:eventable_id/resource_label_events/:event_id" do
+ eventable = find_noteable(parent_type, eventables_str, params[:eventable_id])
+ event = eventable.resource_label_events.find(params[:event_id])
+
+ present event, with: Entities::ResourceLabelEvent
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index c9931c2d603..2f15f3a7d76 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Runner < Grape::API
helpers ::API::Helpers::Runner
@@ -17,6 +19,7 @@ module API
optional :tag_list, type: Array[String], desc: %q(List of Runner's tags)
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post '/' do
attributes = attributes_for_keys([:description, :active, :locked, :run_untagged, :tag_list, :maximum_timeout])
.merge(get_runner_details_from_request)
@@ -43,6 +46,7 @@ module API
render_validation_error!(runner)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Deletes a registered Runner' do
http_codes [[204, 'Runner was deleted'], [403, 'Forbidden']]
@@ -138,8 +142,7 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
optional :trace, type: String, desc: %q(Job's full trace)
optional :state, type: String, desc: %q(Job's status: success, failed)
- optional :failure_reason, type: String, values: CommitStatus.failure_reasons.keys,
- desc: %q(Job's failure_reason)
+ optional :failure_reason, type: String, desc: %q(Job's failure_reason)
end
put '/:id' do
job = authenticate_job!
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index 51242341dba..ce70460af11 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Runners < Grape::API
include PaginationParams
@@ -9,12 +11,20 @@ module API
success Entities::Runner
end
params do
- optional :scope, type: String, values: %w[active paused online],
+ optional :scope, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The scope of specific runners to show'
+ optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
+ desc: 'The type of the runners to show'
+ optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
+ desc: 'The status of the runners to show'
use :pagination
end
get do
- runners = filter_runners(current_user.ci_owned_runners, params[:scope], without: %w(specific shared))
+ runners = current_user.ci_owned_runners
+ runners = filter_runners(runners, params[:scope], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
+ runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
+ runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
+
present paginate(runners), with: Entities::Runner
end
@@ -22,13 +32,22 @@ module API
success Entities::Runner
end
params do
- optional :scope, type: String, values: %w[active paused online specific shared],
+ optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES,
desc: 'The scope of specific runners to show'
+ optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
+ desc: 'The type of the runners to show'
+ optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
+ desc: 'The status of the runners to show'
use :pagination
end
get 'all' do
authenticated_as_admin!
- runners = filter_runners(Ci::Runner.all, params[:scope])
+
+ runners = Ci::Runner.all
+ runners = filter_runners(runners, params[:scope])
+ runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
+ runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
+
present paginate(runners), with: Entities::Runner
end
@@ -94,7 +113,7 @@ module API
optional :status, type: String, desc: 'Status of the job', values: Ci::Build::AVAILABLE_STATUSES
use :pagination
end
- get ':id/jobs' do
+ get ':id/jobs' do
runner = get_runner(params[:id])
authenticate_list_runners_jobs!(runner)
@@ -114,12 +133,20 @@ module API
success Entities::Runner
end
params do
- optional :scope, type: String, values: %w[active paused online specific shared],
+ optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES,
desc: 'The scope of specific runners to show'
+ optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
+ desc: 'The type of the runners to show'
+ optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
+ desc: 'The status of the runners to show'
use :pagination
end
get ':id/runners' do
- runners = filter_runners(Ci::Runner.owned_or_instance_wide(user_project.id), params[:scope])
+ runners = Ci::Runner.owned_or_instance_wide(user_project.id)
+ runners = filter_runners(runners, params[:scope])
+ runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
+ runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
+
present paginate(runners), with: Entities::Runner
end
@@ -146,6 +173,7 @@ module API
params do
requires :runner_id, type: Integer, desc: 'The ID of the runner'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/runners/:runner_id' do
runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
not_found!('Runner') unless runner_project
@@ -155,18 +183,14 @@ module API
destroy_conditionally!(runner_project)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
helpers do
- def filter_runners(runners, scope, options = {})
+ def filter_runners(runners, scope, allowed_scopes: ::Ci::Runner::AVAILABLE_SCOPES)
return runners unless scope.present?
- available_scopes = ::Ci::Runner::AVAILABLE_SCOPES
- if options[:without]
- available_scopes = available_scopes - options[:without]
- end
-
- if (available_scopes & [scope]).empty?
+ unless allowed_scopes.include?(scope)
render_api_error!('Scope contains invalid value', 400)
end
diff --git a/lib/api/scope.rb b/lib/api/scope.rb
index d5165b2e482..707775e5d15 100644
--- a/lib/api/scope.rb
+++ b/lib/api/scope.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Encapsulate a scope used for authorization, such as `api`, or `read_user`
module API
class Scope
diff --git a/lib/api/search.rb b/lib/api/search.rb
index 37fbabe419c..12d97dcfe7f 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Search < Grape::API
include PaginationParams
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 1f2bf546cd7..1cb3b8a7277 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -298,6 +298,14 @@ module API
desc: 'Title'
}
],
+ 'discord' => [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'Discord webhook. e.g. https://discordapp.com/api/webhooks/…'
+ }
+ ],
'drone-ci' => [
{
required: true,
@@ -354,20 +362,6 @@ module API
desc: 'Flowdock token'
}
],
- 'gemnasium' => [
- {
- required: true,
- name: :api_key,
- type: String,
- desc: 'Your personal API key on gemnasium.com'
- },
- {
- required: true,
- name: :token,
- type: String,
- desc: "The project's slug on gemnasium.com"
- }
- ],
'hangouts-chat' => [
{
required: true,
@@ -691,11 +685,11 @@ module API
BuildkiteService,
CampfireService,
CustomIssueTrackerService,
+ DiscordService,
DroneCiService,
EmailsOnPushService,
ExternalWikiService,
FlowdockService,
- GemnasiumService,
HangoutsChatService,
HipchatService,
IrkerService,
@@ -836,11 +830,13 @@ module API
TRIGGER_SERVICES.each do |service_slug, settings|
helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
def slash_command_service(project, service_slug, params)
project.services.active.where(template: false).find do |service|
service.try(:token) == params[:token] && service.to_param == service_slug.underscore
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
params do
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 897010217dc..f53ba0ab761 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Settings < Grape::API
before { authenticated_as_admin! }
@@ -64,10 +66,6 @@ module API
optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project manifest],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
- optional :koding_enabled, type: Boolean, desc: 'Enable Koding'
- given koding_enabled: ->(val) { val } do
- requires :koding_url, type: String, desc: 'The Koding team URL'
- end
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts"
optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
@@ -102,7 +100,7 @@ module API
end
optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues."
optional :repository_storages, type: Array[String], desc: 'Storage paths for new projects'
- optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to setup Two-factor authentication'
+ optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to set up Two-factor authentication'
given require_two_factor_authentication: ->(val) { val } do
requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
end
@@ -117,11 +115,6 @@ module API
given shared_runners_enabled: ->(val) { val } do
requires :shared_runners_text, type: String, desc: 'Shared runners text '
end
- optional :sidekiq_throttling_enabled, type: Boolean, desc: 'Enable Sidekiq Job Throttling'
- given sidekiq_throttling_enabled: ->(val) { val } do
- requires :sidekiq_throttling_factor, type: Float, desc: 'The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.'
- requires :sidekiq_throttling_queues, type: Array[String], desc: 'Choose which queues you wish to throttle'
- end
optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application'
optional :signin_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled'
diff --git a/lib/api/sidekiq_metrics.rb b/lib/api/sidekiq_metrics.rb
index 11f2b40269a..daa9598a204 100644
--- a/lib/api/sidekiq_metrics.rb
+++ b/lib/api/sidekiq_metrics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sidekiq/api'
module API
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index b30305b4bc9..1ae144ca9c1 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
# Snippets API
class Snippets < Grape::API
@@ -12,7 +14,7 @@ module API
end
def public_snippets
- SnippetsFinder.new(current_user, visibility: Snippet::PUBLIC).execute
+ SnippetsFinder.new(current_user, scope: :are_public).execute
end
end
@@ -92,6 +94,7 @@ module API
desc: 'The visibility of the snippet'
at_least_one_of :title, :file_name, :content, :visibility
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id' do
snippet = snippets_for_current_user.find_by(id: params.delete(:id))
break not_found!('Snippet') unless snippet
@@ -110,6 +113,7 @@ module API
render_validation_error!(snippet)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Remove snippet' do
detail 'This feature was introduced in GitLab 8.15.'
@@ -118,6 +122,7 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id' do
snippet = snippets_for_current_user.find_by(id: params.delete(:id))
break not_found!('Snippet') unless snippet
@@ -126,6 +131,7 @@ module API
destroy_conditionally!(snippet)
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a raw snippet' do
detail 'This feature was introduced in GitLab 8.15.'
@@ -133,6 +139,7 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/raw" do
snippet = snippets_for_current_user.find_by(id: params.delete(:id))
break not_found!('Snippet') unless snippet
@@ -141,6 +148,7 @@ module API
content_type 'text/plain'
present snippet.content
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get the user agent details for a snippet' do
success Entities::UserAgentDetail
@@ -148,6 +156,7 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id/user_agent_detail" do
authenticated_as_admin!
@@ -157,6 +166,7 @@ module API
present snippet.user_agent_detail, with: Entities::UserAgentDetail
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/submodules.rb b/lib/api/submodules.rb
new file mode 100644
index 00000000000..72d7d994102
--- /dev/null
+++ b/lib/api/submodules.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module API
+ class Submodules < Grape::API
+ before { authenticate! }
+
+ helpers do
+ def commit_params(attrs)
+ {
+ submodule: attrs[:submodule],
+ commit_sha: attrs[:commit_sha],
+ branch_name: attrs[:branch],
+ commit_message: attrs[:commit_message]
+ }
+ end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The project ID'
+ end
+ resource :projects, requirements: Files::FILE_ENDPOINT_REQUIREMENTS do
+ desc 'Update existing submodule reference in repository' do
+ success Entities::Commit
+ end
+ params do
+ requires :submodule, type: String, desc: 'Url encoded full path to submodule.'
+ requires :commit_sha, type: String, desc: 'Commit sha to update the submodule to.'
+ requires :branch, type: String, desc: 'Name of the branch to commit into.'
+ optional :commit_message, type: String, desc: 'Commit message. If no message is provided a default one will be set.'
+ end
+ put ":id/repository/submodules/:submodule", requirements: Files::FILE_ENDPOINT_REQUIREMENTS do
+ authorize! :push_code, user_project
+
+ submodule_params = declared_params(include_missing: false)
+
+ result = ::Submodules::UpdateService.new(user_project, current_user, commit_params(submodule_params)).execute
+
+ if result[:status] == :success
+ commit_detail = user_project.repository.commit(result[:result])
+ present commit_detail, with: Entities::CommitDetail
+ else
+ render_api_error!(result[:message], result[:http_status] || 400)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
index b3e1e23031a..077e9373ac4 100644
--- a/lib/api/subscriptions.rb
+++ b/lib/api/subscriptions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Subscriptions < Grape::API
before { authenticate! }
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index c7a460df46a..51fae0e54aa 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class SystemHooks < Grape::API
include PaginationParams
@@ -63,12 +65,14 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of the system hook'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ":id" do
hook = SystemHook.find_by(id: params[:id])
not_found!('System hook') unless hook
destroy_conditionally!(hook)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index 5e0afc6a7e4..f739eacf9ba 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Tags < Grape::API
include PaginationParams
diff --git a/lib/api/templates.rb b/lib/api/templates.rb
index 41862768a3f..8dab19d50c2 100644
--- a/lib/api/templates.rb
+++ b/lib/api/templates.rb
@@ -1,46 +1,22 @@
+# frozen_string_literal: true
+
module API
class Templates < Grape::API
include PaginationParams
GLOBAL_TEMPLATE_TYPES = {
gitignores: {
- klass: Gitlab::Template::GitignoreTemplate,
gitlab_version: 8.8
},
gitlab_ci_ymls: {
- klass: Gitlab::Template::GitlabCiYmlTemplate,
gitlab_version: 8.9
},
dockerfiles: {
- klass: Gitlab::Template::DockerfileTemplate,
gitlab_version: 8.15
}
}.freeze
- PROJECT_TEMPLATE_REGEX =
- %r{[\<\{\[]
- (project|description|
- one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
- [\>\}\]]}xi.freeze
- YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
- FULLNAME_TEMPLATE_REGEX =
- %r{[\<\{\[]
- (fullname|name\sof\s(author|copyright\sowner))
- [\>\}\]]}xi.freeze
helpers do
- def parsed_license_template
- # We create a fresh Licensee::License object since we'll modify its
- # content in place below.
- template = Licensee::License.new(params[:name])
-
- template.content.gsub!(YEAR_TEMPLATE_REGEX, Time.now.year.to_s)
- template.content.gsub!(PROJECT_TEMPLATE_REGEX, params[:project]) if params[:project].present?
-
- fullname = params[:fullname].presence || current_user.try(:name)
- template.content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname
- template
- end
-
def render_response(template_type, template)
not_found!(template_type.to_s.singularize) unless template
present template, with: Entities::Template
@@ -56,11 +32,12 @@ module API
use :pagination
end
get "templates/licenses" do
- options = {
- featured: declared(params)[:popular].present? ? true : nil
- }
- licences = ::Kaminari.paginate_array(Licensee::License.all(options))
- present paginate(licences), with: Entities::License
+ popular = declared(params)[:popular]
+ popular = to_boolean(popular) if popular.present?
+
+ templates = TemplateFinder.build(:licenses, nil, popular: popular).execute
+
+ present paginate(::Kaminari.paginate_array(templates)), with: ::API::Entities::License
end
desc 'Get the text for a specific license' do
@@ -71,15 +48,19 @@ module API
requires :name, type: String, desc: 'The name of the template'
end
get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do
- not_found!('License') unless Licensee::License.find(declared(params)[:name])
+ template = TemplateFinder.build(:licenses, nil, name: params[:name]).execute
+
+ not_found!('License') unless template.present?
- template = parsed_license_template
+ template.resolve!(
+ project_name: params[:project].presence,
+ fullname: params[:fullname].presence || current_user&.name
+ )
present template, with: ::API::Entities::License
end
GLOBAL_TEMPLATE_TYPES.each do |template_type, properties|
- klass = properties[:klass]
gitlab_version = properties[:gitlab_version]
desc 'Get the list of the available template' do
@@ -90,7 +71,7 @@ module API
use :pagination
end
get "templates/#{template_type}" do
- templates = ::Kaminari.paginate_array(klass.all)
+ templates = ::Kaminari.paginate_array(TemplateFinder.build(template_type, nil).execute)
present paginate(templates), with: Entities::TemplatesList
end
@@ -102,7 +83,8 @@ module API
requires :name, type: String, desc: 'The name of the template'
end
get "templates/#{template_type}/:name" do
- new_template = klass.find(declared(params)[:name])
+ finder = TemplateFinder.build(template_type, nil, name: declared(params)[:name])
+ new_template = finder.execute
render_response(template_type, new_template)
end
diff --git a/lib/api/time_tracking_endpoints.rb b/lib/api/time_tracking_endpoints.rb
index 2bb451dea89..93fe06bec27 100644
--- a/lib/api/time_tracking_endpoints.rb
+++ b/lib/api/time_tracking_endpoints.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
module TimeTrackingEndpoints
extend ActiveSupport::Concern
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index c6dbcf84e3a..ed2cf2cc31b 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Todos < Grape::API
include PaginationParams
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index b29e660c6e0..f784c857883 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Triggers < Grape::API
include PaginationParams
@@ -10,7 +12,7 @@ module API
success Entities::Pipeline
end
params do
- requires :ref, type: String, desc: 'The commit sha or name of a branch or tag'
+ requires :ref, type: String, desc: 'The commit sha or name of a branch or tag', allow_blank: false
requires :token, type: String, desc: 'The unique token of trigger'
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
@@ -42,6 +44,7 @@ module API
params do
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/triggers' do
authenticate!
authorize! :admin_build, user_project
@@ -50,6 +53,7 @@ module API
present paginate(triggers), with: Entities::Trigger
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get specific trigger of a project' do
success Entities::Trigger
diff --git a/lib/api/users.rb b/lib/api/users.rb
index b0811bb4aad..b41fce76df0 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Users < Grape::API
include PaginationParams
@@ -14,11 +16,14 @@ module API
end
helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
def find_user_by_id(params)
id = params[:user_id] || params[:id]
User.find_by(id: id) || not_found!('User')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def reorder_users(users)
if params[:order_by] && params[:sort]
users.reorder(params[:order_by] => params[:sort])
@@ -26,6 +31,7 @@ module API
users
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
params :optional_attributes do
optional :skype, type: String, desc: 'The Skype username'
@@ -38,12 +44,12 @@ module API
optional :provider, type: String, desc: 'The external provider'
optional :bio, type: String, desc: 'The biography of the user'
optional :location, type: String, desc: 'The location of the user'
+ optional :public_email, type: String, desc: 'The public email of the user'
optional :admin, type: Boolean, desc: 'Flag indicating the user is an administrator'
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
optional :avatar, type: File, desc: 'Avatar image for user'
optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
- optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
all_or_none_of :extern_uid, :provider
end
@@ -75,6 +81,7 @@ module API
use :pagination
use :with_custom_attributes
end
+ # rubocop: disable CodeReuse/ActiveRecord
get do
authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)
@@ -102,6 +109,7 @@ module API
present paginate(users), options
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a single user' do
success Entities::User
@@ -111,6 +119,7 @@ module API
use :with_custom_attributes
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ":id" do
user = User.find_by(id: params[:id])
not_found!('User') unless user && can?(current_user, :read_user, user)
@@ -120,6 +129,7 @@ module API
present user, opts
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc "Get the status of a user"
params do
@@ -155,12 +165,12 @@ module API
present user, with: Entities::UserPublic, current_user: current_user
else
conflict!('Email has already been taken') if User
- .where(email: user.email)
- .count > 0
+ .by_any_email(user.email.downcase)
+ .any?
conflict!('Username has already been taken') if User
- .where(username: user.username)
- .count > 0
+ .by_username(user.username)
+ .any?
render_validation_error!(user)
end
@@ -178,6 +188,7 @@ module API
optional :username, type: String, desc: 'The username of the user'
use :optional_attributes
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ":id" do
authenticated_as_admin!
@@ -185,11 +196,11 @@ module API
not_found!('User') unless user
conflict!('Email has already been taken') if params[:email] &&
- User.where(email: params[:email])
+ User.by_any_email(params[:email].downcase)
.where.not(id: user.id).count > 0
conflict!('Username has already been taken') if params[:username] &&
- User.where(username: params[:username])
+ User.by_username(params[:username])
.where.not(id: user.id).count > 0
user_params = declared_params(include_missing: false)
@@ -216,6 +227,7 @@ module API
render_validation_error!(user)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Add an SSH key to a specified user. Available only for admins.' do
success Entities::SSHKey
@@ -225,6 +237,7 @@ module API
requires :key, type: String, desc: 'The new SSH key'
requires :title, type: String, desc: 'The title of the new SSH key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ":id/keys" do
authenticated_as_admin!
@@ -239,22 +252,23 @@ module API
render_validation_error!(key)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
- desc 'Get the SSH keys of a specified user. Available only for admins.' do
+ desc 'Get the SSH keys of a specified user.' do
success Entities::SSHKey
end
params do
requires :id, type: Integer, desc: 'The ID of the user'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/keys' do
- authenticated_as_admin!
-
user = User.find_by(id: params[:id])
- not_found!('User') unless user
+ not_found!('User') unless user && can?(current_user, :read_user, user)
present paginate(user.keys), with: Entities::SSHKey
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete an existing SSH key from a specified user. Available only for admins.' do
success Entities::SSHKey
@@ -263,6 +277,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
requires :key_id, type: Integer, desc: 'The ID of the SSH key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/keys/:key_id' do
authenticated_as_admin!
@@ -274,6 +289,7 @@ module API
destroy_conditionally!(key)
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Add a GPG key to a specified user. Available only for admins.' do
detail 'This feature was added in GitLab 10.0'
@@ -283,6 +299,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
requires :key, type: String, desc: 'The new GPG key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/gpg_keys' do
authenticated_as_admin!
@@ -297,6 +314,7 @@ module API
render_validation_error!(key)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get the GPG keys of a specified user. Available only for admins.' do
detail 'This feature was added in GitLab 10.0'
@@ -306,6 +324,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/gpg_keys' do
authenticated_as_admin!
@@ -314,6 +333,7 @@ module API
present paginate(user.gpg_keys), with: Entities::GPGKey
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete an existing GPG key from a specified user. Available only for admins.' do
detail 'This feature was added in GitLab 10.0'
@@ -322,6 +342,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
requires :key_id, type: Integer, desc: 'The ID of the GPG key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/gpg_keys/:key_id' do
authenticated_as_admin!
@@ -334,6 +355,7 @@ module API
status 204
key.destroy
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Revokes an existing GPG key from a specified user. Available only for admins.' do
detail 'This feature was added in GitLab 10.0'
@@ -342,6 +364,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
requires :key_id, type: Integer, desc: 'The ID of the GPG key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/gpg_keys/:key_id/revoke' do
authenticated_as_admin!
@@ -354,6 +377,7 @@ module API
key.revoke
status :accepted
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Add an email address to a specified user. Available only for admins.' do
success Entities::Email
@@ -361,7 +385,9 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of the user'
requires :email, type: String, desc: 'The email of the user'
+ optional :skip_confirmation, type: Boolean, desc: 'Skip confirmation of email and assume it is verified'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ":id/emails" do
authenticated_as_admin!
@@ -376,6 +402,7 @@ module API
render_validation_error!(email)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get the emails addresses of a specified user. Available only for admins.' do
success Entities::Email
@@ -384,6 +411,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/emails' do
authenticated_as_admin!
user = User.find_by(id: params[:id])
@@ -391,6 +419,7 @@ module API
present paginate(user.emails), with: Entities::Email
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete an email address of a specified user. Available only for admins.' do
success Entities::Email
@@ -399,6 +428,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
requires :email_id, type: Integer, desc: 'The ID of the email'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/emails/:email_id' do
authenticated_as_admin!
user = User.find_by(id: params[:id])
@@ -411,6 +441,7 @@ module API
Emails::DestroyService.new(current_user, user: user).execute(email)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete a user. Available only for admins.' do
success Entities::Email
@@ -419,6 +450,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
optional :hard_delete, type: Boolean, desc: "Whether to remove a user's contributions"
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ":id" do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42279')
@@ -431,11 +463,13 @@ module API
user.delete_async(deleted_by: current_user, params: params)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Block a user. Available only for admins.'
params do
requires :id, type: Integer, desc: 'The ID of the user'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/block' do
authenticated_as_admin!
user = User.find_by(id: params[:id])
@@ -447,11 +481,13 @@ module API
forbidden!('LDAP blocked users cannot be modified by the API')
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Unblock a user. Available only for admins.'
params do
requires :id, type: Integer, desc: 'The ID of the user'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post ':id/unblock' do
authenticated_as_admin!
user = User.find_by(id: params[:id])
@@ -463,6 +499,7 @@ module API
user.activate
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
params do
requires :user_id, type: Integer, desc: 'The ID of the user'
@@ -476,7 +513,7 @@ module API
end
def find_impersonation_token
- finder.find_by(id: declared_params[:impersonation_token_id]) || not_found!('Impersonation Token')
+ finder.find_by_id(declared_params[:impersonation_token_id]) || not_found!('Impersonation Token')
end
end
@@ -494,7 +531,7 @@ module API
desc 'Create a impersonation token. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0'
- success Entities::ImpersonationToken
+ success Entities::ImpersonationTokenWithToken
end
params do
requires :name, type: String, desc: 'The name of the impersonation token'
@@ -505,7 +542,7 @@ module API
impersonation_token = finder.build(declared_params(include_missing: false))
if impersonation_token.save
- present impersonation_token, with: Entities::ImpersonationToken
+ present impersonation_token, with: Entities::ImpersonationTokenWithToken
else
render_validation_error!(impersonation_token)
end
@@ -578,12 +615,14 @@ module API
params do
requires :key_id, type: Integer, desc: 'The ID of the SSH key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get "keys/:key_id" do
key = current_user.keys.find_by(id: params[:key_id])
not_found!('Key') unless key
present key, with: Entities::SSHKey
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Add a new SSH key to the currently authenticated user' do
success Entities::SSHKey
@@ -608,12 +647,14 @@ module API
params do
requires :key_id, type: Integer, desc: 'The ID of the SSH key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete "keys/:key_id" do
key = current_user.keys.find_by(id: params[:key_id])
not_found!('Key') unless key
destroy_conditionally!(key)
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc "Get the currently authenticated user's GPG keys" do
detail 'This feature was added in GitLab 10.0'
@@ -633,12 +674,14 @@ module API
params do
requires :key_id, type: Integer, desc: 'The ID of the GPG key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get 'gpg_keys/:key_id' do
key = current_user.gpg_keys.find_by(id: params[:key_id])
not_found!('GPG Key') unless key
present key, with: Entities::GPGKey
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Add a new GPG key to the currently authenticated user' do
detail 'This feature was added in GitLab 10.0'
@@ -663,6 +706,7 @@ module API
params do
requires :key_id, type: Integer, desc: 'The ID of the GPG key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
post 'gpg_keys/:key_id/revoke' do
key = current_user.gpg_keys.find_by(id: params[:key_id])
not_found!('GPG Key') unless key
@@ -670,6 +714,7 @@ module API
key.revoke
status :accepted
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete a GPG key from the currently authenticated user' do
detail 'This feature was added in GitLab 10.0'
@@ -677,6 +722,7 @@ module API
params do
requires :key_id, type: Integer, desc: 'The ID of the SSH key'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete 'gpg_keys/:key_id' do
key = current_user.gpg_keys.find_by(id: params[:key_id])
not_found!('GPG Key') unless key
@@ -684,6 +730,7 @@ module API
status 204
key.destroy
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc "Get the currently authenticated user's email addresses" do
success Entities::Email
@@ -701,12 +748,14 @@ module API
params do
requires :email_id, type: Integer, desc: 'The ID of the email'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get "emails/:email_id" do
email = current_user.emails.find_by(id: params[:email_id])
not_found!('Email') unless email
present email, with: Entities::Email
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Add new email address to the currently authenticated user' do
success Entities::Email
@@ -728,6 +777,7 @@ module API
params do
requires :email_id, type: Integer, desc: 'The ID of the email'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete "emails/:email_id" do
email = current_user.emails.find_by(id: params[:email_id])
not_found!('Email') unless email
@@ -736,12 +786,14 @@ module API
Emails::DestroyService.new(current_user, user: current_user).execute(email)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a list of user activities'
params do
optional :from, type: DateTime, default: 6.months.ago, desc: 'Date string in the format YEAR-MONTH-DAY'
use :pagination
end
+ # rubocop: disable CodeReuse/ActiveRecord
get "activities" do
authenticated_as_admin!
@@ -751,6 +803,7 @@ module API
present paginate(activities), with: Entities::UserActivity
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Set the status of the current user' do
success Entities::UserStatus
diff --git a/lib/api/validations/types/safe_file.rb b/lib/api/validations/types/safe_file.rb
new file mode 100644
index 00000000000..53b5790bfa2
--- /dev/null
+++ b/lib/api/validations/types/safe_file.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+# This module overrides the Grape type validator defined in
+# https://github.com/ruby-grape/grape/blob/master/lib/grape/validations/types/file.rb
+module API
+ module Validations
+ module Types
+ class SafeFile < ::Grape::Validations::Types::File
+ def value_coerced?(value)
+ super && value[:tempfile].is_a?(Tempfile)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index a34de9410e8..c844ba321ed 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Variables < Grape::API
include PaginationParams
@@ -27,6 +29,7 @@ module API
params do
requires :key, type: String, desc: 'The key of the variable'
end
+ # rubocop: disable CodeReuse/ActiveRecord
get ':id/variables/:key' do
key = params[:key]
variable = user_project.variables.find_by(key: key)
@@ -35,6 +38,7 @@ module API
present variable, with: Entities::Variable
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Create a new variable in a project' do
success Entities::Variable
@@ -64,6 +68,7 @@ module API
optional :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
end
+ # rubocop: disable CodeReuse/ActiveRecord
put ':id/variables/:key' do
variable = user_project.variables.find_by(key: params[:key])
@@ -77,6 +82,7 @@ module API
render_validation_error!(variable)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
desc 'Delete an existing variable from a project' do
success Entities::Variable
@@ -84,6 +90,7 @@ module API
params do
requires :key, type: String, desc: 'The key of the variable'
end
+ # rubocop: disable CodeReuse/ActiveRecord
delete ':id/variables/:key' do
variable = user_project.variables.find_by(key: params[:key])
not_found!('Variable') unless variable
@@ -92,6 +99,7 @@ module API
status 204
variable.destroy
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/version.rb b/lib/api/version.rb
index 3b10bfa6a7d..74cd857f447 100644
--- a/lib/api/version.rb
+++ b/lib/api/version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module API
class Version < Grape::API
before { authenticate! }
diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb
index b3fc4e876ad..24746f4efc6 100644
--- a/lib/api/wikis.rb
+++ b/lib/api/wikis.rb
@@ -1,6 +1,16 @@
+# frozen_string_literal: true
+
module API
class Wikis < Grape::API
helpers do
+ def commit_params(attrs)
+ {
+ file_name: attrs[:file][:filename],
+ file_content: attrs[:file][:tempfile].read,
+ branch_name: attrs[:branch]
+ }
+ end
+
params :wiki_page_params do
requires :content, type: String, desc: 'Content of a wiki page'
requires :title, type: String, desc: 'Title of a wiki page'
@@ -84,6 +94,29 @@ module API
status 204
WikiPages::DestroyService.new(user_project, current_user).execute(wiki_page)
end
+
+ desc 'Upload an attachment to the wiki repository' do
+ detail 'This feature was introduced in GitLab 11.3.'
+ success Entities::WikiAttachment
+ end
+ params do
+ requires :file, type: ::API::Validations::Types::SafeFile, desc: 'The attachment file to be uploaded'
+ optional :branch, type: String, desc: 'The name of the branch'
+ end
+ post ":id/wikis/attachments", requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
+ authorize! :create_wiki, user_project
+
+ result = ::Wikis::CreateAttachmentService.new(user_project,
+ current_user,
+ commit_params(declared_params(include_missing: false))).execute
+
+ if result[:status] == :success
+ status(201)
+ present OpenStruct.new(result[:result]), with: Entities::WikiAttachment
+ else
+ render_api_error!(result[:message], 400)
+ end
+ end
end
end
end
diff --git a/lib/backup.rb b/lib/backup.rb
index e2c62af23ae..2712b33b4b4 100644
--- a/lib/backup.rb
+++ b/lib/backup.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Backup
Error = Class.new(StandardError)
end
diff --git a/lib/backup/artifacts.rb b/lib/backup/artifacts.rb
index 45a935ab352..33658ae225f 100644
--- a/lib/backup/artifacts.rb
+++ b/lib/backup/artifacts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'backup/files'
module Backup
diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb
index adf85ca4719..5e795a449de 100644
--- a/lib/backup/builds.rb
+++ b/lib/backup/builds.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'backup/files'
module Backup
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index 086ca5986bd..e6bf3d1856f 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'yaml'
module Backup
diff --git a/lib/backup/files.rb b/lib/backup/files.rb
index e287aa1e392..0032ae8f84b 100644
--- a/lib/backup/files.rb
+++ b/lib/backup/files.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'open3'
require_relative 'helper'
diff --git a/lib/backup/helper.rb b/lib/backup/helper.rb
index 54b9ce10b4d..22f00aef569 100644
--- a/lib/backup/helper.rb
+++ b/lib/backup/helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Backup
module Helper
def access_denied_error(path)
diff --git a/lib/backup/lfs.rb b/lib/backup/lfs.rb
index 185ff8ae6bd..0dfe56e214f 100644
--- a/lib/backup/lfs.rb
+++ b/lib/backup/lfs.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'backup/files'
module Backup
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index a3641505196..a0434a66ef1 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Backup
class Manager
ARCHIVES_TO_BACKUP = %w[uploads builds artifacts pages lfs registry].freeze
@@ -162,7 +164,7 @@ module Backup
def tar_version
tar_version, _ = Gitlab::Popen.popen(%w(tar --version))
- tar_version.force_encoding('locale').split("\n").first
+ tar_version.dup.force_encoding('locale').split("\n").first
end
def skipped?(item)
@@ -241,6 +243,7 @@ module Backup
backup_created_at: Time.now,
gitlab_version: Gitlab::VERSION,
tar_version: tar_version,
+ installation_type: Gitlab::INSTALLATION_TYPE,
skipped: ENV["SKIP"]
}
end
diff --git a/lib/backup/pages.rb b/lib/backup/pages.rb
index 542e35a7c7c..a4be728df08 100644
--- a/lib/backup/pages.rb
+++ b/lib/backup/pages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'backup/files'
module Backup
diff --git a/lib/backup/registry.rb b/lib/backup/registry.rb
index 35821805797..d16ed2facf1 100644
--- a/lib/backup/registry.rb
+++ b/lib/backup/registry.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'backup/files'
module Backup
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 906ed498026..c8a5377bfa0 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'yaml'
module Backup
diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb
index 49b117a7ee3..9577df2634a 100644
--- a/lib/backup/uploads.rb
+++ b/lib/backup/uploads.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'backup/files'
module Backup
diff --git a/lib/banzai.rb b/lib/banzai.rb
index 5df98f66f3b..1eb41ff7133 100644
--- a/lib/banzai.rb
+++ b/lib/banzai.rb
@@ -1,4 +1,13 @@
+# frozen_string_literal: true
+
module Banzai
+ # if you need to render markdown, then you probably need to post_process as well,
+ # such as removing references that the current user doesn't have
+ # permission to make
+ def self.render_and_post_process(text, context = {})
+ post_process(render(text, context), context)
+ end
+
def self.render(text, context = {})
Renderer.render(text, context)
end
diff --git a/lib/banzai/color_parser.rb b/lib/banzai/color_parser.rb
index 355c364b07b..6d01d51955c 100644
--- a/lib/banzai/color_parser.rb
+++ b/lib/banzai/color_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ColorParser
ALPHA = /0(?:\.\d+)?|\.\d+|1(?:\.0+)?/ # 0.0..1.0
diff --git a/lib/banzai/commit_renderer.rb b/lib/banzai/commit_renderer.rb
index c351a155ae5..f346151a3c1 100644
--- a/lib/banzai/commit_renderer.rb
+++ b/lib/banzai/commit_renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module CommitRenderer
ATTRIBUTES = [:description, :title].freeze
diff --git a/lib/banzai/cross_project_reference.rb b/lib/banzai/cross_project_reference.rb
index 3f1e95d4cc0..b7344808989 100644
--- a/lib/banzai/cross_project_reference.rb
+++ b/lib/banzai/cross_project_reference.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Common methods for ReferenceFilters that support an optional cross-project
# reference.
@@ -13,6 +15,7 @@ module Banzai
# Returns a Project, or nil if the reference can't be found
def parent_from_ref(ref)
return context[:project] || context[:group] unless ref
+ return context[:project] if context[:project]&.full_path == ref
Project.find_by_full_path(ref)
end
diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb
index 3eb544dfef9..7d9766c906c 100644
--- a/lib/banzai/filter.rb
+++ b/lib/banzai/filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
def self.[](name)
diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb
index 04ec568eee3..a9bdb004c4b 100644
--- a/lib/banzai/filter/absolute_link_filter.rb
+++ b/lib/banzai/filter/absolute_link_filter.rb
@@ -29,6 +29,7 @@ module Banzai
end
def absolute_link_attr(uri)
+ # Here we really want to expand relative path to absolute path
URI.join(Gitlab.config.gitlab.url, uri).to_s
end
end
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index ad0806df8e6..4764f8e1e19 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -296,7 +296,7 @@ module Banzai
# Returns projects for the given paths.
def find_for_paths(paths)
- if RequestStore.active?
+ if Gitlab::SafeRequestStore.active?
cache = refs_cache
to_query = paths - cache.keys
@@ -340,7 +340,7 @@ module Banzai
end
def refs_cache
- RequestStore["banzai_#{parent_type}_refs".to_sym] ||= {}
+ Gitlab::SafeRequestStore["banzai_#{parent_type}_refs".to_sym] ||= {}
end
def parent_type
diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb
index e06e2fb3870..26bcf5c04b4 100644
--- a/lib/banzai/filter/epic_reference_filter.rb
+++ b/lib/banzai/filter/epic_reference_filter.rb
@@ -9,6 +9,12 @@ module Banzai
def self.object_class
Epic
end
+
+ private
+
+ def group
+ context[:group] || context[:project]&.group
+ end
end
end
end
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index b4a7a44e109..8159dcfed72 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -97,9 +97,7 @@ module Banzai
private
def external_issues_cached(attribute)
- return project.public_send(attribute) unless RequestStore.active? # rubocop:disable GitlabSecurity/PublicSend
-
- cached_attributes = RequestStore[:banzai_external_issues_tracker_attributes] ||= Hash.new { |h, k| h[k] = {} }
+ cached_attributes = Gitlab::SafeRequestStore[:banzai_external_issues_tracker_attributes] ||= Hash.new { |h, k| h[k] = {} }
cached_attributes[project.id][attribute] = project.public_send(attribute) if cached_attributes[project.id][attribute].nil? # rubocop:disable GitlabSecurity/PublicSend
cached_attributes[project.id][attribute]
end
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
index d7fe012883d..8e2358694d4 100644
--- a/lib/banzai/filter/issuable_state_filter.rb
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -18,7 +18,7 @@ module Banzai
issuables = extractor.extract([doc])
issuables.each do |node, issuable|
- next if !can_read_cross_project? && issuable.project != project
+ next if !can_read_cross_project? && cross_reference?(issuable)
if VISIBLE_STATES.include?(issuable.state) && issuable_reference?(node.inner_html, issuable)
node.content += " (#{issuable.state})"
@@ -31,7 +31,14 @@ module Banzai
private
def issuable_reference?(text, issuable)
- text == issuable.reference_link_text(project || group)
+ CGI.unescapeHTML(text) == issuable.reference_link_text(project || group)
+ end
+
+ def cross_reference?(issuable)
+ return true if issuable.project != project
+ return true if issuable.respond_to?(:group) && issuable.group != group
+
+ false
end
def can_read_cross_project?
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index b92e9e55bb9..04ec38209c7 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -48,7 +48,7 @@ module Banzai
include_ancestor_groups: true,
only_group_labels: true }
else
- { project_id: parent.id,
+ { project: parent,
include_ancestor_groups: true }
end
diff --git a/lib/banzai/filter/markdown_engines/common_mark.rb b/lib/banzai/filter/markdown_engines/common_mark.rb
index dbb25280849..e52c0d15b31 100644
--- a/lib/banzai/filter/markdown_engines/common_mark.rb
+++ b/lib/banzai/filter/markdown_engines/common_mark.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# `CommonMark` markdown engine for GitLab's Banzai markdown filter.
# This module is used in Banzai::Filter::MarkdownFilter.
# Used gem is `commonmarker` which is a ruby wrapper for libcmark (CommonMark parser)
diff --git a/lib/banzai/filter/markdown_engines/redcarpet.rb b/lib/banzai/filter/markdown_engines/redcarpet.rb
index ac99941fefa..ec150d041ff 100644
--- a/lib/banzai/filter/markdown_engines/redcarpet.rb
+++ b/lib/banzai/filter/markdown_engines/redcarpet.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# `Redcarpet` markdown engine for GitLab's Banzai markdown filter.
# This module is used in Banzai::Filter::MarkdownFilter.
# Used gem is `redcarpet` which is a ruby library for markdown processing.
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 8e838d04bad..7acbc933adc 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -60,7 +60,11 @@ module Banzai
path_parts.unshift(relative_url_root, project.full_path)
end
- path = Addressable::URI.escape(File.join(*path_parts))
+ begin
+ path = Addressable::URI.escape(File.join(*path_parts))
+ rescue Addressable::URI::InvalidURIError
+ return
+ end
html_attr.value =
if context[:only_path]
diff --git a/lib/banzai/filter/spaced_link_filter.rb b/lib/banzai/filter/spaced_link_filter.rb
new file mode 100644
index 00000000000..a27f1d46863
--- /dev/null
+++ b/lib/banzai/filter/spaced_link_filter.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'uri'
+
+module Banzai
+ module Filter
+ # HTML Filter for markdown links with spaces in the URLs
+ #
+ # Based on Banzai::Filter::AutolinkFilter
+ #
+ # CommonMark does not allow spaces in the url portion of a link/url.
+ # For example, `[example](page slug)` is not valid.
+ # Neither is `![example](test image.jpg)`. However, particularly
+ # in our wikis, we support (via RedCarpet) this type of link, allowing
+ # wiki pages to be easily linked by their title. This filter adds that functionality.
+ #
+ # This is a small extension to the CommonMark spec. If they start allowing
+ # spaces in urls, we could then remove this filter.
+ #
+ class SpacedLinkFilter < HTML::Pipeline::Filter
+ include ActionView::Helpers::TagHelper
+
+ # Pattern to match a standard markdown link
+ #
+ # Rubular: http://rubular.com/r/2EXEQ49rg5
+ LINK_OR_IMAGE_PATTERN = %r{
+ (?<preview_operator>!)?
+ \[(?<text>.+?)\]
+ \(
+ (?<new_link>.+?)
+ (?<title>\ ".+?")?
+ \)
+ }x
+
+ # Text matching LINK_OR_IMAGE_PATTERN inside these elements will not be linked
+ IGNORE_PARENTS = %w(a code kbd pre script style).to_set
+
+ # The XPath query to use for finding text nodes to parse.
+ TEXT_QUERY = %Q(descendant-or-self::text()[
+ not(#{IGNORE_PARENTS.map { |p| "ancestor::#{p}" }.join(' or ')})
+ and contains(., ']\(')
+ ]).freeze
+
+ def call
+ return doc if context[:markdown_engine] == :redcarpet
+
+ doc.xpath(TEXT_QUERY).each do |node|
+ content = node.to_html
+
+ next unless content.match(LINK_OR_IMAGE_PATTERN)
+
+ html = spaced_link_filter(content)
+
+ next if html == content
+
+ node.replace(html)
+ end
+
+ doc
+ end
+
+ private
+
+ def spaced_link_match(link)
+ match = LINK_OR_IMAGE_PATTERN.match(link)
+ return link unless match
+
+ # escape the spaces in the url so that it's a valid markdown link,
+ # then run it through the markdown processor again, let it do its magic
+ html = Banzai::Filter::MarkdownFilter.call(transform_markdown(match), context)
+
+ # link is wrapped in a <p>, so strip that off
+ html.sub('<p>', '').chomp('</p>')
+ end
+
+ def spaced_link_filter(text)
+ Gitlab::StringRegexMarker.new(CGI.unescapeHTML(text), text.html_safe).mark(LINK_OR_IMAGE_PATTERN) do |link, left:, right:|
+ spaced_link_match(link)
+ end
+ end
+
+ def transform_markdown(match)
+ preview_operator, text, new_link, title = process_match(match)
+
+ "#{preview_operator}[#{text}](#{new_link}#{title})"
+ end
+
+ def process_match(match)
+ [
+ match[:preview_operator],
+ match[:text],
+ match[:new_link].gsub(' ', '%20'),
+ match[:title]
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb
index 870721f895d..1728a442533 100644
--- a/lib/banzai/filter/wiki_link_filter.rb
+++ b/lib/banzai/filter/wiki_link_filter.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require 'uri'
-
module Banzai
module Filter
# HTML filter that "fixes" links to pages/files in a wiki.
@@ -13,8 +11,12 @@ module Banzai
def call
return doc unless project_wiki?
- doc.search('a:not(.gfm)').each do |el|
- process_link_attr el.attribute('href')
+ doc.search('a:not(.gfm)').each { |el| process_link_attr(el.attribute('href')) }
+ doc.search('video').each { |el| process_link_attr(el.attribute('src')) }
+ doc.search('img').each do |el|
+ attr = el.attribute('data-src') || el.attribute('src')
+
+ process_link_attr(attr)
end
doc
diff --git a/lib/banzai/filter/wiki_link_filter/rewriter.rb b/lib/banzai/filter/wiki_link_filter/rewriter.rb
index 072d24e5a11..f4cc8beeb52 100644
--- a/lib/banzai/filter/wiki_link_filter/rewriter.rb
+++ b/lib/banzai/filter/wiki_link_filter/rewriter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class WikiLinkFilter < HTML::Pipeline::Filter
@@ -10,11 +12,16 @@ module Banzai
def apply_rules
# Special case: relative URLs beginning with `/uploads/` refer to
- # user-uploaded files and will be handled elsewhere.
- return @uri.to_s if @uri.relative? && @uri.path.starts_with?('/uploads/')
+ # user-uploaded files will be handled elsewhere.
+ return @uri.to_s if public_upload?
+
+ # Special case: relative URLs beginning with Wikis::CreateAttachmentService::ATTACHMENT_PATH
+ # refer to user-uploaded files to the wiki repository.
+ unless repository_upload?
+ apply_file_link_rules!
+ apply_hierarchical_link_rules!
+ end
- apply_file_link_rules!
- apply_hierarchical_link_rules!
apply_relative_link_rules!
@uri.to_s
end
@@ -39,6 +46,14 @@ module Banzai
@uri = Addressable::URI.parse(link)
end
end
+
+ def public_upload?
+ @uri.relative? && @uri.path.starts_with?('/uploads/')
+ end
+
+ def repository_upload?
+ @uri.relative? && @uri.path.starts_with?(Wikis::CreateAttachmentService::ATTACHMENT_PATH)
+ end
end
end
end
diff --git a/lib/banzai/filter_array.rb b/lib/banzai/filter_array.rb
index 77835a14027..818af4643a7 100644
--- a/lib/banzai/filter_array.rb
+++ b/lib/banzai/filter_array.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
class FilterArray < Array
# Insert a value immediately after another value
diff --git a/lib/banzai/issuable_extractor.rb b/lib/banzai/issuable_extractor.rb
index ae7dc71e7eb..341dbb74fe0 100644
--- a/lib/banzai/issuable_extractor.rb
+++ b/lib/banzai/issuable_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Extract references to issuables from multiple documents
@@ -7,13 +9,11 @@ module Banzai
# so we can avoid N+1 queries problem
class IssuableExtractor
- QUERY = %q(
- descendant-or-self::a[contains(concat(" ", @class, " "), " gfm ")]
- [@data-reference-type="issue" or @data-reference-type="merge_request"]
- ).freeze
-
attr_reader :context
+ ISSUE_REFERENCE_TYPE = '@data-reference-type="issue"'.freeze
+ MERGE_REQUEST_REFERENCE_TYPE = '@data-reference-type="merge_request"'.freeze
+
# context - An instance of Banzai::RenderContext.
def initialize(context)
@context = context
@@ -22,21 +22,38 @@ module Banzai
# Returns Hash in the form { node => issuable_instance }
def extract(documents)
nodes = documents.flat_map do |document|
- document.xpath(QUERY)
+ document.xpath(query)
end
- issue_parser = Banzai::ReferenceParser::IssueParser.new(context)
+ # The project or group for the issuable might be pending for deletion!
+ # Filter them out because we don't care about them.
+ issuables_for_nodes(nodes).select { |node, issuable| issuable.project || issuable.group }
+ end
+
+ private
- merge_request_parser =
+ def issuables_for_nodes(nodes)
+ parsers.each_with_object({}) do |parser, result|
+ result.merge!(parser.records_for_nodes(nodes))
+ end
+ end
+
+ def parsers
+ [
+ Banzai::ReferenceParser::IssueParser.new(context),
Banzai::ReferenceParser::MergeRequestParser.new(context)
+ ]
+ end
- issuables_for_nodes = issue_parser.records_for_nodes(nodes).merge(
- merge_request_parser.records_for_nodes(nodes)
+ def query
+ %Q(
+ descendant-or-self::a[contains(concat(" ", @class, " "), " gfm ")]
+ [#{reference_types.join(' or ')}]
)
+ end
- # The project for the issue/MR might be pending for deletion!
- # Filter them out because we don't care about them.
- issuables_for_nodes.select { |node, issuable| issuable.project }
+ def reference_types
+ [ISSUE_REFERENCE_TYPE, MERGE_REQUEST_REFERENCE_TYPE]
end
end
end
diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb
index a176f1e261b..75661ffa233 100644
--- a/lib/banzai/object_renderer.rb
+++ b/lib/banzai/object_renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Class for rendering multiple objects (e.g. Note instances) in a single pass,
# using +render_field+ to benefit from caching in the database. Rendering and
@@ -38,6 +40,7 @@ module Banzai
redacted_data = redacted[index]
object.__send__("redacted_#{attribute}_html=", redacted_data[:document].to_html(save_options).html_safe) # rubocop:disable GitlabSecurity/PublicSend
object.user_visible_reference_count = redacted_data[:visible_reference_count] if object.respond_to?(:user_visible_reference_count)
+ object.total_reference_count = redacted_data[:total_reference_count] if object.respond_to?(:total_reference_count)
end
end
diff --git a/lib/banzai/pipeline.rb b/lib/banzai/pipeline.rb
index 142a9962eb1..e8a81bebaa9 100644
--- a/lib/banzai/pipeline.rb
+++ b/lib/banzai/pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
def self.[](name)
diff --git a/lib/banzai/pipeline/ascii_doc_pipeline.rb b/lib/banzai/pipeline/ascii_doc_pipeline.rb
index 1048b927cd3..cc4af280872 100644
--- a/lib/banzai/pipeline/ascii_doc_pipeline.rb
+++ b/lib/banzai/pipeline/ascii_doc_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class AsciiDocPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/atom_pipeline.rb b/lib/banzai/pipeline/atom_pipeline.rb
index 9694e4bc23f..13a342351b6 100644
--- a/lib/banzai/pipeline/atom_pipeline.rb
+++ b/lib/banzai/pipeline/atom_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class AtomPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/base_pipeline.rb b/lib/banzai/pipeline/base_pipeline.rb
index 3ae3bed570d..87d1cf9912f 100644
--- a/lib/banzai/pipeline/base_pipeline.rb
+++ b/lib/banzai/pipeline/base_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class BasePipeline
diff --git a/lib/banzai/pipeline/broadcast_message_pipeline.rb b/lib/banzai/pipeline/broadcast_message_pipeline.rb
index 5dd572de3a1..a3d63e0aaf5 100644
--- a/lib/banzai/pipeline/broadcast_message_pipeline.rb
+++ b/lib/banzai/pipeline/broadcast_message_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class BroadcastMessagePipeline < DescriptionPipeline
diff --git a/lib/banzai/pipeline/combined_pipeline.rb b/lib/banzai/pipeline/combined_pipeline.rb
index 60190f8d9dd..56b424dc8e0 100644
--- a/lib/banzai/pipeline/combined_pipeline.rb
+++ b/lib/banzai/pipeline/combined_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
module CombinedPipeline
diff --git a/lib/banzai/pipeline/commit_description_pipeline.rb b/lib/banzai/pipeline/commit_description_pipeline.rb
index 607c2731ed3..e8ec7453f0f 100644
--- a/lib/banzai/pipeline/commit_description_pipeline.rb
+++ b/lib/banzai/pipeline/commit_description_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class CommitDescriptionPipeline < SingleLinePipeline
diff --git a/lib/banzai/pipeline/description_pipeline.rb b/lib/banzai/pipeline/description_pipeline.rb
index 042fb2e6e14..d5ff9b025cc 100644
--- a/lib/banzai/pipeline/description_pipeline.rb
+++ b/lib/banzai/pipeline/description_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class DescriptionPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/email_pipeline.rb b/lib/banzai/pipeline/email_pipeline.rb
index 8f5f144d582..2c08581ce0d 100644
--- a/lib/banzai/pipeline/email_pipeline.rb
+++ b/lib/banzai/pipeline/email_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class EmailPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/full_pipeline.rb b/lib/banzai/pipeline/full_pipeline.rb
index 3c974f73176..a5b1cbdd030 100644
--- a/lib/banzai/pipeline/full_pipeline.rb
+++ b/lib/banzai/pipeline/full_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline)
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index e9be05e174e..be75e34a673 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class GfmPipeline < BasePipeline
@@ -16,6 +18,7 @@ module Banzai
Filter::MathFilter,
Filter::ColorFilter,
Filter::MermaidFilter,
+ Filter::SpacedLinkFilter,
Filter::VideoLinkFilter,
Filter::ImageLazyLoadFilter,
Filter::ImageLinkFilter,
diff --git a/lib/banzai/pipeline/label_pipeline.rb b/lib/banzai/pipeline/label_pipeline.rb
new file mode 100644
index 00000000000..725cccc4b2b
--- /dev/null
+++ b/lib/banzai/pipeline/label_pipeline.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Pipeline
+ class LabelPipeline < BasePipeline
+ def self.filters
+ @filters ||= FilterArray[
+ Filter::SanitizationFilter,
+ Filter::LabelReferenceFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/markup_pipeline.rb b/lib/banzai/pipeline/markup_pipeline.rb
index c56d908009f..db79a22549c 100644
--- a/lib/banzai/pipeline/markup_pipeline.rb
+++ b/lib/banzai/pipeline/markup_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class MarkupPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/note_pipeline.rb b/lib/banzai/pipeline/note_pipeline.rb
index 7890f20f716..4480d7ede05 100644
--- a/lib/banzai/pipeline/note_pipeline.rb
+++ b/lib/banzai/pipeline/note_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class NotePipeline < FullPipeline
diff --git a/lib/banzai/pipeline/plain_markdown_pipeline.rb b/lib/banzai/pipeline/plain_markdown_pipeline.rb
index 3f45db21869..b64f13cde47 100644
--- a/lib/banzai/pipeline/plain_markdown_pipeline.rb
+++ b/lib/banzai/pipeline/plain_markdown_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class PlainMarkdownPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
index 0b2e584ef16..63a998a2c1f 100644
--- a/lib/banzai/pipeline/post_process_pipeline.rb
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class PostProcessPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/pre_process_pipeline.rb b/lib/banzai/pipeline/pre_process_pipeline.rb
index 6cf219661d3..c937f783180 100644
--- a/lib/banzai/pipeline/pre_process_pipeline.rb
+++ b/lib/banzai/pipeline/pre_process_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class PreProcessPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/relative_link_pipeline.rb b/lib/banzai/pipeline/relative_link_pipeline.rb
index 270990e7ab4..88651892acc 100644
--- a/lib/banzai/pipeline/relative_link_pipeline.rb
+++ b/lib/banzai/pipeline/relative_link_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class RelativeLinkPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb
index cd5a6c8875c..61ff7b0bcce 100644
--- a/lib/banzai/pipeline/single_line_pipeline.rb
+++ b/lib/banzai/pipeline/single_line_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class SingleLinePipeline < GfmPipeline
diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb
index c37b8e71cb0..97a03895ff3 100644
--- a/lib/banzai/pipeline/wiki_pipeline.rb
+++ b/lib/banzai/pipeline/wiki_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class WikiPipeline < FullPipeline
diff --git a/lib/banzai/querying.rb b/lib/banzai/querying.rb
index a19a05e8c0d..55aa5fa66c3 100644
--- a/lib/banzai/querying.rb
+++ b/lib/banzai/querying.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Querying
module_function
diff --git a/lib/banzai/redactor.rb b/lib/banzai/redactor.rb
index 28928d6f376..7db5f5e1f7d 100644
--- a/lib/banzai/redactor.rb
+++ b/lib/banzai/redactor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Class for removing Markdown references a certain user is not allowed to
# view.
@@ -37,7 +39,13 @@ module Banzai
all_document_nodes.each do |entry|
nodes_for_document = entry[:nodes]
- doc_data = { document: entry[:document], visible_reference_count: nodes_for_document.count }
+
+ doc_data = {
+ document: entry[:document],
+ total_reference_count: nodes_for_document.count,
+ visible_reference_count: nodes_for_document.count
+ }
+
metadata << doc_data
nodes_for_document.each do |node|
diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb
index 78588299c18..3fc3ae02088 100644
--- a/lib/banzai/reference_extractor.rb
+++ b/lib/banzai/reference_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor
diff --git a/lib/banzai/reference_parser.rb b/lib/banzai/reference_parser.rb
index 557bec4316e..efe15096f08 100644
--- a/lib/banzai/reference_parser.rb
+++ b/lib/banzai/reference_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
# Returns the reference parser class for the given type
diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb
index 68752f5bb5a..8419769085a 100644
--- a/lib/banzai/reference_parser/base_parser.rb
+++ b/lib/banzai/reference_parser/base_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
# Base class for reference parsing classes.
@@ -166,7 +168,7 @@ module Banzai
# objects that have not yet been queried. For objects that have already
# been queried the object is returned from the cache.
def collection_objects_for_ids(collection, ids)
- if RequestStore.active?
+ if Gitlab::SafeRequestStore.active?
ids = ids.map(&:to_i)
cache = collection_cache[collection_cache_key(collection)]
to_query = ids - cache.keys
@@ -215,7 +217,7 @@ module Banzai
#
def projects_for_nodes(nodes)
@projects_for_nodes ||=
- grouped_objects_for_nodes(nodes, Project, 'data-project')
+ grouped_objects_for_nodes(nodes, Project.includes(:project_feature), 'data-project')
end
def can?(user, permission, subject = :global)
@@ -248,7 +250,7 @@ module Banzai
end
def collection_cache
- RequestStore[:banzai_collection_cache] ||= Hash.new do |hash, key|
+ Gitlab::SafeRequestStore[:banzai_collection_cache] ||= Hash.new do |hash, key|
hash[key] = {}
end
end
diff --git a/lib/banzai/reference_parser/commit_parser.rb b/lib/banzai/reference_parser/commit_parser.rb
index 30dc87248b4..0bfb6a92020 100644
--- a/lib/banzai/reference_parser/commit_parser.rb
+++ b/lib/banzai/reference_parser/commit_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class CommitParser < BaseParser
diff --git a/lib/banzai/reference_parser/commit_range_parser.rb b/lib/banzai/reference_parser/commit_range_parser.rb
index 2920e886938..480eefd5c4d 100644
--- a/lib/banzai/reference_parser/commit_range_parser.rb
+++ b/lib/banzai/reference_parser/commit_range_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class CommitRangeParser < BaseParser
diff --git a/lib/banzai/reference_parser/directly_addressed_user_parser.rb b/lib/banzai/reference_parser/directly_addressed_user_parser.rb
index 77df9bbd024..1f18f82b916 100644
--- a/lib/banzai/reference_parser/directly_addressed_user_parser.rb
+++ b/lib/banzai/reference_parser/directly_addressed_user_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class DirectlyAddressedUserParser < UserParser
diff --git a/lib/banzai/reference_parser/epic_parser.rb b/lib/banzai/reference_parser/epic_parser.rb
index 08b8a4c9a0f..7f366f0f8ab 100644
--- a/lib/banzai/reference_parser/epic_parser.rb
+++ b/lib/banzai/reference_parser/epic_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
# The actual parser is implemented in the EE mixin
diff --git a/lib/banzai/reference_parser/external_issue_parser.rb b/lib/banzai/reference_parser/external_issue_parser.rb
index 1802cd04854..029b09dcd25 100644
--- a/lib/banzai/reference_parser/external_issue_parser.rb
+++ b/lib/banzai/reference_parser/external_issue_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class ExternalIssueParser < BaseParser
diff --git a/lib/banzai/reference_parser/issuable_parser.rb b/lib/banzai/reference_parser/issuable_parser.rb
index fad127d7e5b..f8c26288017 100644
--- a/lib/banzai/reference_parser/issuable_parser.rb
+++ b/lib/banzai/reference_parser/issuable_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class IssuableParser < BaseParser
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
index 7b5915899cf..97c7173ac0f 100644
--- a/lib/banzai/reference_parser/issue_parser.rb
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class IssueParser < IssuableParser
diff --git a/lib/banzai/reference_parser/label_parser.rb b/lib/banzai/reference_parser/label_parser.rb
index 30e2a012f09..398cc45fea0 100644
--- a/lib/banzai/reference_parser/label_parser.rb
+++ b/lib/banzai/reference_parser/label_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class LabelParser < BaseParser
diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb
index a370ff5b5b3..e8147ac591a 100644
--- a/lib/banzai/reference_parser/merge_request_parser.rb
+++ b/lib/banzai/reference_parser/merge_request_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class MergeRequestParser < IssuableParser
@@ -14,11 +16,12 @@ module Banzai
# Eager loading these ensures we don't end up running dozens of
# queries in this process.
target_project: [
- { namespace: :owner },
+ { namespace: [:owner, :route] },
{ group: [:owners, :group_members] },
:invited_groups,
:project_members,
- :project_feature
+ :project_feature,
+ :route
]
}),
self.class.data_attribute
diff --git a/lib/banzai/reference_parser/milestone_parser.rb b/lib/banzai/reference_parser/milestone_parser.rb
index 68675abe22a..925d736fb9a 100644
--- a/lib/banzai/reference_parser/milestone_parser.rb
+++ b/lib/banzai/reference_parser/milestone_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class MilestoneParser < BaseParser
diff --git a/lib/banzai/reference_parser/snippet_parser.rb b/lib/banzai/reference_parser/snippet_parser.rb
index 3ade168b566..6f6ac08de04 100644
--- a/lib/banzai/reference_parser/snippet_parser.rb
+++ b/lib/banzai/reference_parser/snippet_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class SnippetParser < BaseParser
diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb
index ceb7f1d165c..067b06b7590 100644
--- a/lib/banzai/reference_parser/user_parser.rb
+++ b/lib/banzai/reference_parser/user_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class UserParser < BaseParser
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index 0050295eeda..c7239a5eaa6 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Renderer
# Convert a Markdown String into an HTML-safe String of HTML
diff --git a/lib/banzai/renderer/common_mark/html.rb b/lib/banzai/renderer/common_mark/html.rb
index 46b609c36b0..837665451a1 100644
--- a/lib/banzai/renderer/common_mark/html.rb
+++ b/lib/banzai/renderer/common_mark/html.rb
@@ -1,18 +1,16 @@
+# frozen_string_literal: true
+
module Banzai
module Renderer
module CommonMark
class HTML < CommonMarker::HtmlRenderer
def code_block(node)
block do
- code = node.string_content
- lang = node.fence_info
- lang_attr = lang.present? ? %Q{ lang="#{lang}"} : ''
- result =
- "<pre>" \
- "<code#{lang_attr}>#{ERB::Util.html_escape(code)}</code>" \
- "</pre>"
-
- out(result)
+ out("<pre#{sourcepos(node)}><code")
+ out(' lang="', node.fence_info, '"') if node.fence_info.present?
+ out('>')
+ out(escape_html(node.string_content))
+ out('</code></pre>')
end
end
end
diff --git a/lib/banzai/renderer/redcarpet/html.rb b/lib/banzai/renderer/redcarpet/html.rb
index 30e815f1224..84931fdc784 100644
--- a/lib/banzai/renderer/redcarpet/html.rb
+++ b/lib/banzai/renderer/redcarpet/html.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Renderer
module Redcarpet
diff --git a/lib/banzai/request_store_reference_cache.rb b/lib/banzai/request_store_reference_cache.rb
index 426131442a2..91fb489b72d 100644
--- a/lib/banzai/request_store_reference_cache.rb
+++ b/lib/banzai/request_store_reference_cache.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
module Banzai
module RequestStoreReferenceCache
def cached_call(request_store_key, cache_key, path: [])
- if RequestStore.active?
- cache = RequestStore[request_store_key] ||= Hash.new do |hash, key|
+ if Gitlab::SafeRequestStore.active?
+ cache = Gitlab::SafeRequestStore[request_store_key] ||= Hash.new do |hash, key|
hash[key] = Hash.new { |h, k| h[k] = {} }
end
diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb
index f8ee7e0f9ae..1343f424c51 100644
--- a/lib/bitbucket/client.rb
+++ b/lib/bitbucket/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Client
attr_reader :connection
diff --git a/lib/bitbucket/collection.rb b/lib/bitbucket/collection.rb
index a78495dbf5e..9c496daccaa 100644
--- a/lib/bitbucket/collection.rb
+++ b/lib/bitbucket/collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Collection < Enumerator
def initialize(paginator)
diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb
index ba5a9e2f04c..0041634f9e3 100644
--- a/lib/bitbucket/connection.rb
+++ b/lib/bitbucket/connection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Connection
DEFAULT_API_VERSION = '2.0'.freeze
diff --git a/lib/bitbucket/error/unauthorized.rb b/lib/bitbucket/error/unauthorized.rb
index efe10542f19..3cde11babee 100644
--- a/lib/bitbucket/error/unauthorized.rb
+++ b/lib/bitbucket/error/unauthorized.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Error
Unauthorized = Class.new(StandardError)
diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb
index 2b0a3fe7b1a..7cc1342ad65 100644
--- a/lib/bitbucket/page.rb
+++ b/lib/bitbucket/page.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Page
attr_reader :attrs, :items
diff --git a/lib/bitbucket/paginator.rb b/lib/bitbucket/paginator.rb
index 135d0d55674..0d004592e67 100644
--- a/lib/bitbucket/paginator.rb
+++ b/lib/bitbucket/paginator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Paginator
PAGE_LENGTH = 50 # The minimum length is 10 and the maximum is 100.
diff --git a/lib/bitbucket/representation/base.rb b/lib/bitbucket/representation/base.rb
index 800d5a075c6..bb8dcd91ad5 100644
--- a/lib/bitbucket/representation/base.rb
+++ b/lib/bitbucket/representation/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class Base
diff --git a/lib/bitbucket/representation/comment.rb b/lib/bitbucket/representation/comment.rb
index 4937aa9728f..1b8dc27793a 100644
--- a/lib/bitbucket/representation/comment.rb
+++ b/lib/bitbucket/representation/comment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class Comment < Representation::Base
diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb
index 44bcbc250b3..a88797cdab9 100644
--- a/lib/bitbucket/representation/issue.rb
+++ b/lib/bitbucket/representation/issue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class Issue < Representation::Base
diff --git a/lib/bitbucket/representation/pull_request.rb b/lib/bitbucket/representation/pull_request.rb
index eebf8093380..6a0e8b354bf 100644
--- a/lib/bitbucket/representation/pull_request.rb
+++ b/lib/bitbucket/representation/pull_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class PullRequest < Representation::Base
diff --git a/lib/bitbucket/representation/pull_request_comment.rb b/lib/bitbucket/representation/pull_request_comment.rb
index c52acbc3ddc..34dbf9ad22d 100644
--- a/lib/bitbucket/representation/pull_request_comment.rb
+++ b/lib/bitbucket/representation/pull_request_comment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class PullRequestComment < Comment
diff --git a/lib/bitbucket/representation/repo.rb b/lib/bitbucket/representation/repo.rb
index 59b0fda8e14..c5bfc91e43d 100644
--- a/lib/bitbucket/representation/repo.rb
+++ b/lib/bitbucket/representation/repo.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class Repo < Representation::Base
diff --git a/lib/bitbucket/representation/user.rb b/lib/bitbucket/representation/user.rb
index ba6b7667b49..2b45d751e70 100644
--- a/lib/bitbucket/representation/user.rb
+++ b/lib/bitbucket/representation/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class User < Representation::Base
diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb
index 15e59f93141..83e8808db07 100644
--- a/lib/bitbucket_server/client.rb
+++ b/lib/bitbucket_server/client.rb
@@ -35,9 +35,9 @@ module BitbucketServer
BitbucketServer::Representation::Repo.new(parsed_response)
end
- def repos
+ def repos(page_offset: 0, limit: nil)
path = "/repos"
- get_collection(path, :repo)
+ get_collection(path, :repo, page_offset: page_offset, limit: limit)
end
def create_branch(project_key, repo, branch_name, sha)
@@ -61,8 +61,8 @@ module BitbucketServer
private
- def get_collection(path, type)
- paginator = BitbucketServer::Paginator.new(connection, Addressable::URI.escape(path), type)
+ def get_collection(path, type, page_offset: 0, limit: nil)
+ paginator = BitbucketServer::Paginator.new(connection, Addressable::URI.escape(path), type, page_offset: page_offset, limit: limit)
BitbucketServer::Collection.new(paginator)
rescue *SERVER_ERRORS => e
raise ServerError, e
diff --git a/lib/bitbucket_server/collection.rb b/lib/bitbucket_server/collection.rb
index b50c5dde352..7e4b2277bbe 100644
--- a/lib/bitbucket_server/collection.rb
+++ b/lib/bitbucket_server/collection.rb
@@ -2,7 +2,13 @@
module BitbucketServer
class Collection < Enumerator
+ attr_reader :paginator
+
+ delegate :page_offset, :has_next_page?, to: :paginator
+
def initialize(paginator)
+ @paginator = paginator
+
super() do |yielder|
loop do
paginator.items.each { |item| yielder << item }
@@ -12,6 +18,24 @@ module BitbucketServer
lazy
end
+ def current_page
+ return 1 if page_offset <= 1
+
+ [1, page_offset].max
+ end
+
+ def prev_page
+ return nil unless current_page > 1
+
+ current_page - 1
+ end
+
+ def next_page
+ return nil unless has_next_page?
+
+ current_page + 1
+ end
+
def method_missing(method, *args)
return super unless self.respond_to?(method)
diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb
index 45a437844bd..7efcdcf8619 100644
--- a/lib/bitbucket_server/connection.rb
+++ b/lib/bitbucket_server/connection.rb
@@ -88,35 +88,19 @@ module BitbucketServer
def build_url(path)
return path if path.starts_with?(root_url)
- url_join_paths(root_url, path)
+ Gitlab::Utils.append_path(root_url, path)
end
def root_url
- url_join_paths(base_uri, "/rest/api/#{api_version}")
+ Gitlab::Utils.append_path(base_uri, "rest/api/#{api_version}")
end
def delete_url(resource, path)
if resource == :branches
- url_join_paths(base_uri, "/rest/branch-utils/#{api_version}#{path}")
+ Gitlab::Utils.append_path(base_uri, "rest/branch-utils/#{api_version}#{path}")
else
build_url(path)
end
end
-
- # URI.join is stupid in that slashes are important:
- #
- # # URI.join('http://example.com/subpath', 'hello')
- # => http://example.com/hello
- #
- # We really want http://example.com/subpath/hello
- #
- def url_join_paths(*paths)
- paths.map { |path| strip_slashes(path) }.join(SEPARATOR)
- end
-
- def strip_slashes(path)
- path = path[1..-1] if path.starts_with?(SEPARATOR)
- path.chomp(SEPARATOR)
- end
end
end
diff --git a/lib/bitbucket_server/paginator.rb b/lib/bitbucket_server/paginator.rb
index c351fb2f11f..aa5f84f44b3 100644
--- a/lib/bitbucket_server/paginator.rb
+++ b/lib/bitbucket_server/paginator.rb
@@ -4,34 +4,49 @@ module BitbucketServer
class Paginator
PAGE_LENGTH = 25
- def initialize(connection, url, type)
+ attr_reader :page_offset
+
+ def initialize(connection, url, type, page_offset: 0, limit: nil)
@connection = connection
@type = type
@url = url
@page = nil
+ @page_offset = page_offset
+ @limit = limit || PAGE_LENGTH
+ @total = 0
end
def items
raise StopIteration unless has_next_page?
+ raise StopIteration if over_limit?
@page = fetch_next_page
+ @total += @page.items.count
@page.items
end
+ def has_next_page?
+ page.nil? || page.next?
+ end
+
private
- attr_reader :connection, :page, :url, :type
+ attr_reader :connection, :page, :url, :type, :limit
- def has_next_page?
- page.nil? || page.next?
+ def over_limit?
+ @limit.positive? && @total >= @limit
end
def next_offset
- page.nil? ? 0 : page.next
+ page.nil? ? starting_offset : page.next
+ end
+
+ def starting_offset
+ [0, page_offset - 1].max * limit
end
def fetch_next_page
- parsed_response = connection.get(@url, start: next_offset, limit: PAGE_LENGTH)
+ parsed_response = connection.get(@url, start: next_offset, limit: @limit)
Page.new(parsed_response, type)
end
end
diff --git a/lib/carrier_wave_string_file.rb b/lib/carrier_wave_string_file.rb
index 6c848902e4a..c9a64d9e631 100644
--- a/lib/carrier_wave_string_file.rb
+++ b/lib/carrier_wave_string_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CarrierWaveStringFile < StringIO
def original_filename
""
diff --git a/lib/constraints/feature_constrainer.rb b/lib/constraints/feature_constrainer.rb
index 05d48b0f25a..ca4376a9d38 100644
--- a/lib/constraints/feature_constrainer.rb
+++ b/lib/constraints/feature_constrainer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Constraints
class FeatureConstrainer
attr_reader :feature
diff --git a/lib/constraints/group_url_constrainer.rb b/lib/constraints/group_url_constrainer.rb
index 87649c50424..8a3f8d2faaf 100644
--- a/lib/constraints/group_url_constrainer.rb
+++ b/lib/constraints/group_url_constrainer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Constraints
class GroupUrlConstrainer
def matches?(request)
diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb
index 32aea98f0f7..eadfbf7bc01 100644
--- a/lib/constraints/project_url_constrainer.rb
+++ b/lib/constraints/project_url_constrainer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Constraints
class ProjectUrlConstrainer
def matches?(request)
diff --git a/lib/constraints/user_url_constrainer.rb b/lib/constraints/user_url_constrainer.rb
index 8afa04d29a4..e763569cb2e 100644
--- a/lib/constraints/user_url_constrainer.rb
+++ b/lib/constraints/user_url_constrainer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Constraints
class UserUrlConstrainer
def matches?(request)
diff --git a/lib/container_registry/blob.rb b/lib/container_registry/blob.rb
index d5f85f9fcad..837b22c3082 100644
--- a/lib/container_registry/blob.rb
+++ b/lib/container_registry/blob.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
class Blob
attr_reader :repository, :config
diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb
index 010ca1ec27b..c80f49f5ae0 100644
--- a/lib/container_registry/client.rb
+++ b/lib/container_registry/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'faraday'
require 'faraday_middleware'
diff --git a/lib/container_registry/config.rb b/lib/container_registry/config.rb
index 589f9f4380a..740c0e13da0 100644
--- a/lib/container_registry/config.rb
+++ b/lib/container_registry/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
class Config
attr_reader :tag, :blob, :data
diff --git a/lib/container_registry/path.rb b/lib/container_registry/path.rb
index 61849a40383..9b2a61cdedc 100644
--- a/lib/container_registry/path.rb
+++ b/lib/container_registry/path.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
##
# Class responsible for extracting project and repository name from
@@ -28,6 +30,7 @@ module ContainerRegistry
@components ||= @path.split('/')
end
+ # rubocop: disable CodeReuse/ActiveRecord
def nodes
raise InvalidRegistryPathError unless valid?
@@ -35,17 +38,20 @@ module ContainerRegistry
components.take(length).join('/')
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def has_project?
repository_project.present?
end
+ # rubocop: disable CodeReuse/ActiveRecord
def has_repository?
return false unless has_project?
repository_project.container_repositories
.where(name: repository_name).any?
end
+ # rubocop: enable CodeReuse/ActiveRecord
def root_repository?
@path == project_path
diff --git a/lib/container_registry/registry.rb b/lib/container_registry/registry.rb
index f90d711474a..523364ac7c7 100644
--- a/lib/container_registry/registry.rb
+++ b/lib/container_registry/registry.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
class Registry
attr_reader :uri, :client, :path
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
index 728deea224f..8633e764f90 100644
--- a/lib/container_registry/tag.rb
+++ b/lib/container_registry/tag.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
class Tag
attr_reader :repository, :name
@@ -73,11 +75,13 @@ module ContainerRegistry
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def total_size
return unless layers
layers.map(&:size).sum if v2?
end
+ # rubocop: enable CodeReuse/ActiveRecord
def delete
return unless digest
diff --git a/lib/declarative_policy.rb b/lib/declarative_policy.rb
index dda6cd38dcd..5e22523e45a 100644
--- a/lib/declarative_policy.rb
+++ b/lib/declarative_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'declarative_policy/cache'
require_dependency 'declarative_policy/condition'
require_dependency 'declarative_policy/delegate_dsl'
@@ -10,8 +12,6 @@ require_dependency 'declarative_policy/step'
require_dependency 'declarative_policy/base'
-require 'thread'
-
module DeclarativePolicy
CLASS_CACHE_MUTEX = Mutex.new
CLASS_CACHE_IVAR = :@__DeclarativePolicy_CLASS_CACHE
diff --git a/lib/declarative_policy/base.rb b/lib/declarative_policy/base.rb
index da3fabba39b..cd6e1606f22 100644
--- a/lib/declarative_policy/base.rb
+++ b/lib/declarative_policy/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
class Base
# A map of ability => list of rules together with :enable
diff --git a/lib/declarative_policy/cache.rb b/lib/declarative_policy/cache.rb
index 780d8f707bd..13006e56454 100644
--- a/lib/declarative_policy/cache.rb
+++ b/lib/declarative_policy/cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
module Cache
class << self
diff --git a/lib/declarative_policy/condition.rb b/lib/declarative_policy/condition.rb
index 51c4a8b2bbe..b77f40b1093 100644
--- a/lib/declarative_policy/condition.rb
+++ b/lib/declarative_policy/condition.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# A Condition is the data structure that is created by the
# `condition` declaration on DeclarativePolicy::Base. It is
diff --git a/lib/declarative_policy/delegate_dsl.rb b/lib/declarative_policy/delegate_dsl.rb
index ca2eb98e3e8..67e3429b696 100644
--- a/lib/declarative_policy/delegate_dsl.rb
+++ b/lib/declarative_policy/delegate_dsl.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# Used when the name of a delegate is mentioned in
# the rule DSL.
diff --git a/lib/declarative_policy/policy_dsl.rb b/lib/declarative_policy/policy_dsl.rb
index c96049768a1..96741c0478e 100644
--- a/lib/declarative_policy/policy_dsl.rb
+++ b/lib/declarative_policy/policy_dsl.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# The return value of a rule { ... } declaration.
# Can call back to register rules with the containing
diff --git a/lib/declarative_policy/preferred_scope.rb b/lib/declarative_policy/preferred_scope.rb
index c77784cb49d..239780d8626 100644
--- a/lib/declarative_policy/preferred_scope.rb
+++ b/lib/declarative_policy/preferred_scope.rb
@@ -1,4 +1,7 @@
-module DeclarativePolicy # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module DeclarativePolicy
PREFERRED_SCOPE_KEY = :"DeclarativePolicy.preferred_scope"
class << self
diff --git a/lib/declarative_policy/rule.rb b/lib/declarative_policy/rule.rb
index 407398cc770..f38f4f0a50f 100644
--- a/lib/declarative_policy/rule.rb
+++ b/lib/declarative_policy/rule.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
module Rule
# A Rule is the object that results from the `rule` declaration,
diff --git a/lib/declarative_policy/rule_dsl.rb b/lib/declarative_policy/rule_dsl.rb
index 7254b08eda5..85da7f261fa 100644
--- a/lib/declarative_policy/rule_dsl.rb
+++ b/lib/declarative_policy/rule_dsl.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# The DSL evaluation context inside rule { ... } blocks.
# Responsible for creating and combining Rule objects.
diff --git a/lib/declarative_policy/runner.rb b/lib/declarative_policy/runner.rb
index fec672f4b8c..f739fe5e16e 100644
--- a/lib/declarative_policy/runner.rb
+++ b/lib/declarative_policy/runner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
class Runner
class State
diff --git a/lib/declarative_policy/step.rb b/lib/declarative_policy/step.rb
index 3469fe9f991..c289c17cc19 100644
--- a/lib/declarative_policy/step.rb
+++ b/lib/declarative_policy/step.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# This object represents one step in the runtime decision of whether
# an ability is allowed. It contains a Rule and a context (instance
diff --git a/lib/event_filter.rb b/lib/event_filter.rb
index 515095af1c2..24fdcd6fbb1 100644
--- a/lib/event_filter.rb
+++ b/lib/event_filter.rb
@@ -1,74 +1,42 @@
-class EventFilter
- attr_accessor :params
-
- class << self
- def all
- 'all'
- end
-
- def push
- 'push'
- end
-
- def merged
- 'merged'
- end
+# frozen_string_literal: true
- def issue
- 'issue'
- end
-
- def comments
- 'comments'
- end
-
- def team
- 'team'
- end
+class EventFilter
+ attr_accessor :filter
+
+ ALL = 'all'
+ PUSH = 'push'
+ MERGED = 'merged'
+ ISSUE = 'issue'
+ COMMENTS = 'comments'
+ TEAM = 'team'
+ FILTERS = [ALL, PUSH, MERGED, ISSUE, COMMENTS, TEAM].freeze
+
+ def initialize(filter)
+ # Split using comma to maintain backward compatibility Ex/ "filter1,filter2"
+ filter = filter.to_s.split(',')[0].to_s
+ @filter = FILTERS.include?(filter) ? filter : ALL
end
- def initialize(params)
- @params = if params
- params.dup
- else
- [] # EventFilter.default_filter
- end
+ def active?(key)
+ filter == key.to_s
end
+ # rubocop: disable CodeReuse/ActiveRecord
def apply_filter(events)
- return events if params.blank? || params == EventFilter.all
-
- case params
- when EventFilter.push
+ case filter
+ when PUSH
events.where(action: Event::PUSHED)
- when EventFilter.merged
+ when MERGED
events.where(action: Event::MERGED)
- when EventFilter.comments
+ when COMMENTS
events.where(action: Event::COMMENTED)
- when EventFilter.team
+ when TEAM
events.where(action: [Event::JOINED, Event::LEFT, Event::EXPIRED])
- when EventFilter.issue
+ when ISSUE
events.where(action: [Event::CREATED, Event::UPDATED, Event::CLOSED, Event::REOPENED])
- end
- end
-
- def options(key)
- filter = params.dup
-
- if filter.include? key
- filter.delete key
- else
- filter << key
- end
-
- filter
- end
-
- def active?(key)
- if params.present?
- params.include? key
else
- key == EventFilter.all
+ events
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/lib/expand_variables.rb b/lib/expand_variables.rb
index 7b1533d0d32..c83cec9dc4a 100644
--- a/lib/expand_variables.rb
+++ b/lib/expand_variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ExpandVariables
class << self
def expand(value, variables)
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index e8dbde176ef..655278da711 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Module providing methods for dealing with separating a tree-ish string and a
# file path string when combined in a request parameter
module ExtractsPath
@@ -50,11 +52,13 @@ module ExtractsPath
# branches and tags
# Append a trailing slash if we only get a ref and no file path
- id += '/' unless id.ends_with?('/')
+ unless id.ends_with?('/')
+ id = [id, '/'].join
+ end
valid_refs = ref_names.select { |v| id.start_with?("#{v}/") }
- if valid_refs.length == 0
+ if valid_refs.empty?
# No exact ref match, so just try our best
pair = id.match(%r{([^/]+)(.*)}).captures
else
@@ -149,11 +153,11 @@ module ExtractsPath
private
- # overriden in subclasses, do not remove
+ # overridden in subclasses, do not remove
def get_id
- id = params[:id] || params[:ref]
- id += "/" + params[:path] unless params[:path].blank?
- id
+ id = [params[:id] || params[:ref]]
+ id << "/" + params[:path] unless params[:path].blank?
+ id.join
end
def ref_names
diff --git a/lib/feature.rb b/lib/feature.rb
index 09c5ef3ad94..e048a443abc 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'flipper/adapters/active_record'
require 'flipper/adapters/active_support_cache_store'
@@ -28,11 +30,7 @@ class Feature
end
def persisted_names
- if RequestStore.active?
- RequestStore[:flipper_persisted_names] ||= FlipperFeature.feature_names
- else
- FlipperFeature.feature_names
- end
+ Gitlab::SafeRequestStore[:flipper_persisted_names] ||= FlipperFeature.feature_names
end
def persisted?(feature)
@@ -42,12 +40,21 @@ class Feature
persisted_names.include?(feature.name.to_s)
end
- def enabled?(key, thing = nil)
- get(key).enabled?(thing)
+ # use `default_enabled: true` to default the flag to being `enabled`
+ # unless set explicitly. The default is `disabled`
+ def enabled?(key, thing = nil, default_enabled: false)
+ feature = Feature.get(key)
+
+ # If we're not default enabling the flag or the feature has been set, always evaluate.
+ # `persisted?` can potentially generate DB queries and also checks for inclusion
+ # in an array of feature names (177 at last count), possibly reducing performance by half.
+ # So we only perform the `persisted` check if `default_enabled: true`
+ !default_enabled || Feature.persisted?(feature) ? feature.enabled?(thing) : true
end
- def disabled?(key, thing = nil)
- !enabled?(key, thing)
+ def disabled?(key, thing = nil, default_enabled: false)
+ # we need to make different method calls to make it easy to mock / define expectations in test mode
+ thing.nil? ? !enabled?(key, default_enabled: default_enabled) : !enabled?(key, thing, default_enabled: default_enabled)
end
def enable(key, thing = true)
@@ -67,8 +74,8 @@ class Feature
end
def flipper
- if RequestStore.active?
- RequestStore[:flipper] ||= build_flipper_instance
+ if Gitlab::SafeRequestStore.active?
+ Gitlab::SafeRequestStore[:flipper] ||= build_flipper_instance
else
@flipper ||= build_flipper_instance
end
diff --git a/lib/file_size_validator.rb b/lib/file_size_validator.rb
index 69d981e8be9..70a145cd5bd 100644
--- a/lib/file_size_validator.rb
+++ b/lib/file_size_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class FileSizeValidator < ActiveModel::EachValidator
MESSAGES = { is: :wrong_size, minimum: :size_too_small, maximum: :size_too_big }.freeze
CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
@@ -32,6 +34,7 @@ class FileSizeValidator < ActiveModel::EachValidator
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def validate_each(record, attribute, value)
raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.is_a? CarrierWave::Uploader::Base
@@ -62,6 +65,7 @@ class FileSizeValidator < ActiveModel::EachValidator
record.errors.add(attribute, MESSAGES[key], errors_options)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def help
Helper.instance
diff --git a/lib/flowdock/git.rb b/lib/flowdock/git.rb
new file mode 100644
index 00000000000..f165ecfc1fa
--- /dev/null
+++ b/lib/flowdock/git.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+require 'flowdock'
+require 'flowdock/git/builder'
+
+module Flowdock
+ class Git
+ TokenError = Class.new(StandardError)
+
+ DEFAULT_PERMANENT_REFS = [
+ Regexp.new('refs/heads/master')
+ ].freeze
+
+ class << self
+ def post(ref, from, to, options = {})
+ Git.new(ref, from, to, options).post
+ end
+ end
+
+ def initialize(ref, from, to, options = {})
+ raise TokenError.new("Flowdock API token not found") unless options[:token]
+
+ @ref = ref
+ @from = from
+ @to = to
+ @options = options
+ @token = options[:token]
+ @commit_url = options[:commit_url]
+ @diff_url = options[:diff_url]
+ @repo_url = options[:repo_url]
+ @repo_name = options[:repo_name]
+ @permanent_refs = options.fetch(:permanent_refs, DEFAULT_PERMANENT_REFS)
+ end
+
+ # Send git push notification to Flowdock
+ def post
+ messages.each do |message|
+ Flowdock::Client.new(flow_token: @token).post_to_thread(message)
+ end
+ end
+
+ def repo
+ @options[:repo]
+ end
+
+ private
+
+ def messages
+ Git::Builder.new(repo: repo,
+ ref: @ref,
+ before: @from,
+ after: @to,
+ commit_url: @commit_url,
+ branch_url: @branch_url,
+ diff_url: @diff_url,
+ repo_url: @repo_url,
+ repo_name: @repo_name,
+ permanent_refs: @permanent_refs,
+ tags: tags
+ ).to_hashes
+ end
+
+ # Flowdock tags attached to the push notification
+ def tags
+ Array(@options[:tags]).map { |tag| CGI.escape(tag) }
+ end
+ end
+end
diff --git a/lib/flowdock/git/builder.rb b/lib/flowdock/git/builder.rb
new file mode 100644
index 00000000000..6f4428d1f42
--- /dev/null
+++ b/lib/flowdock/git/builder.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+module Flowdock
+ class Git
+ class Commit
+ def initialize(external_thread_id, thread, tags, commit)
+ @commit = commit
+ @external_thread_id = external_thread_id
+ @thread = thread
+ @tags = tags
+ end
+
+ def to_hash
+ hash = {
+ external_thread_id: @external_thread_id,
+ event: "activity",
+ author: {
+ name: @commit[:author][:name],
+ email: @commit[:author][:email]
+ },
+ title: title,
+ thread: @thread,
+ body: body
+ }
+ hash[:tags] = @tags if @tags
+ encode(hash)
+ end
+
+ private
+
+ def encode(hash)
+ return hash unless "".respond_to?(:encode)
+
+ encode_as_utf8(hash)
+ end
+
+ # This only works on Ruby 1.9
+ def encode_as_utf8(obj)
+ if obj.is_a? Hash
+ obj.each_pair do |key, val|
+ encode_as_utf8(val)
+ end
+ elsif obj.is_a?(Array)
+ obj.each do |val|
+ encode_as_utf8(val)
+ end
+ elsif obj.is_a?(String) && obj.encoding != Encoding::UTF_8
+ unless obj.force_encoding("UTF-8").valid_encoding?
+ obj.force_encoding("ISO-8859-1").encode!(Encoding::UTF_8, invalid: :replace, undef: :replace)
+ end
+ end
+ end
+
+ def body
+ content = @commit[:message][first_line.size..-1]
+ content.strip! if content
+ "<pre>#{content}</pre>" unless content.empty?
+ end
+
+ def first_line
+ @first_line ||= (@commit[:message].split("\n")[0] || @commit[:message])
+ end
+
+ def title
+ commit_id = @commit[:id][0, 7]
+ if @commit[:url]
+ "<a href=\"#{@commit[:url]}\">#{commit_id}</a> #{message_title}"
+ else
+ "#{commit_id} #{message_title}"
+ end
+ end
+
+ def message_title
+ CGI.escape_html(first_line.strip)
+ end
+ end
+
+ # Class used to build Git payload
+ class Builder
+ include ::Gitlab::Utils::StrongMemoize
+
+ def initialize(opts)
+ @repo = opts[:repo]
+ @ref = opts[:ref]
+ @before = opts[:before]
+ @after = opts[:after]
+ @opts = opts
+ end
+
+ def commits
+ @repo.commits_between(@before, @after).map do |commit|
+ {
+ url: @opts[:commit_url] ? @opts[:commit_url] % [commit.sha] : nil,
+ id: commit.sha,
+ message: commit.message,
+ author: {
+ name: commit.author_name,
+ email: commit.author_email
+ }
+ }
+ end
+ end
+
+ def ref_name
+ @ref.to_s.sub(%r{\Arefs/(heads|tags)/}, '')
+ end
+
+ def to_hashes
+ commits.map do |commit|
+ Commit.new(external_thread_id, thread, @opts[:tags], commit).to_hash
+ end
+ end
+
+ private
+
+ def thread
+ @thread ||= {
+ title: thread_title,
+ external_url: @opts[:repo_url]
+ }
+ end
+
+ def permanent?
+ strong_memoize(:permanent) do
+ @opts[:permanent_refs].any? { |regex| regex.match(@ref) }
+ end
+ end
+
+ def thread_title
+ action = "updated" if permanent?
+ type = @ref =~ %r(^refs/heads/) ? "branch" : "tag"
+
+ [@opts[:repo_name], type, ref_name, action].compact.join(" ")
+ end
+
+ def external_thread_id
+ @external_thread_id ||=
+ if permanent?
+ SecureRandom.hex
+ else
+ @ref
+ end
+ end
+ end
+ end
+end
diff --git a/lib/forever.rb b/lib/forever.rb
index 7df17912544..0a37118fe68 100644
--- a/lib/forever.rb
+++ b/lib/forever.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Forever
POSTGRESQL_DATE = DateTime.new(3000, 1, 1)
MYSQL_DATE = DateTime.new(2038, 01, 19)
diff --git a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
index 91175b49c79..15cdd25e711 100644
--- a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
+++ b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rails/generators'
module Rails
diff --git a/lib/gitaly/server.rb b/lib/gitaly/server.rb
index f95e423ef22..7b238623418 100644
--- a/lib/gitaly/server.rb
+++ b/lib/gitaly/server.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitaly
class Server
def self.all
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index ab6b609d099..2ef54658a11 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'gitlab/popen'
module Gitlab
@@ -47,4 +49,12 @@ module Gitlab
def self.dev_env_or_com?
Rails.env.development? || org? || com?
end
+
+ def self.pre_release?
+ VERSION.include?('pre')
+ end
+
+ def self.version_info
+ Gitlab::VersionInfo.parse(Gitlab::VERSION)
+ end
end
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index b170145f013..ec090aea784 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitlab::Access module
#
# Define allowed roles that can be used
diff --git a/lib/gitlab/action_rate_limiter.rb b/lib/gitlab/action_rate_limiter.rb
index 4cd3bdefda3..c442211e073 100644
--- a/lib/gitlab/action_rate_limiter.rb
+++ b/lib/gitlab/action_rate_limiter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# This class implements a simple rate limiter that can be used to throttle
# certain actions. Unlike Rack Attack and Rack::Throttle, which operate at
diff --git a/lib/gitlab/allowable.rb b/lib/gitlab/allowable.rb
index 45c2b01dd8f..4518c8a862c 100644
--- a/lib/gitlab/allowable.rb
+++ b/lib/gitlab/allowable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Allowable
def can?(*args)
diff --git a/lib/gitlab/app_logger.rb b/lib/gitlab/app_logger.rb
index dddcb2538f9..5edec8b3efe 100644
--- a/lib/gitlab/app_logger.rb
+++ b/lib/gitlab/app_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class AppLogger < Gitlab::Logger
def self.file_name_noext
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index 62c41801d75..df8f0470063 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'asciidoctor'
require 'asciidoctor/converter/html5'
require "asciidoctor-plantuml"
diff --git a/lib/gitlab/audit_json_logger.rb b/lib/gitlab/audit_json_logger.rb
new file mode 100644
index 00000000000..12e0645f3e4
--- /dev/null
+++ b/lib/gitlab/audit_json_logger.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class AuditJsonLogger < Gitlab::JsonLogger
+ def self.file_name_noext
+ 'audit_json'
+ end
+ end
+end
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 111e18b2076..6eb5f9e2300 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
MissingPersonalAccessTokenError = Class.new(StandardError)
@@ -136,6 +138,7 @@ module Gitlab
Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def oauth_access_token_check(login, password)
if login == "oauth2" && password.present?
token = Doorkeeper::AccessToken.by_token(password)
@@ -146,11 +149,12 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def personal_access_token_check(password)
return unless password.present?
- token = PersonalAccessTokensFinder.new(state: 'active').find_by(token: password)
+ token = PersonalAccessTokensFinder.new(state: 'active').find_by_token(password)
if token && valid_scoped_token?(token, available_scopes)
Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes))
@@ -177,6 +181,7 @@ module Gitlab
end.uniq
end
+ # rubocop: disable CodeReuse/ActiveRecord
def deploy_token_check(login, password)
return unless password.present?
@@ -192,6 +197,7 @@ module Gitlab
Gitlab::Auth::Result.new(token, token.project, :deploy_token, scopes)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def lfs_token_check(login, password, project)
deploy_key_matches = login.match(/\Alfs\+deploy-key-(\d+)\z/)
diff --git a/lib/gitlab/auth/activity.rb b/lib/gitlab/auth/activity.rb
index 761f0819c60..558628b5422 100644
--- a/lib/gitlab/auth/activity.rb
+++ b/lib/gitlab/auth/activity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
##
diff --git a/lib/gitlab/auth/database/authentication.rb b/lib/gitlab/auth/database/authentication.rb
index 1234ace0334..c0dc2b0875f 100644
--- a/lib/gitlab/auth/database/authentication.rb
+++ b/lib/gitlab/auth/database/authentication.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These calls help to authenticate to OAuth provider by providing username and password
#
diff --git a/lib/gitlab/auth/ip_rate_limiter.rb b/lib/gitlab/auth/ip_rate_limiter.rb
index e6173d45af3..81e616fa20a 100644
--- a/lib/gitlab/auth/ip_rate_limiter.rb
+++ b/lib/gitlab/auth/ip_rate_limiter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class IpRateLimiter
diff --git a/lib/gitlab/auth/ldap/access.rb b/lib/gitlab/auth/ldap/access.rb
index 865185eb5db..c875bba4bcb 100644
--- a/lib/gitlab/auth/ldap/access.rb
+++ b/lib/gitlab/auth/ldap/access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# LDAP authorization model
#
# * Check if we are allowed access (not blocked)
@@ -19,8 +21,10 @@ module Gitlab
# Whether user is allowed, or not, we should update
# permissions to keep things clean
if access.allowed?
- access.update_user
- Users::UpdateService.new(user, user: user, last_credential_check_at: Time.now).execute
+ unless Gitlab::Database.read_only?
+ access.update_user
+ Users::UpdateService.new(user, user: user, last_credential_check_at: Time.now).execute
+ end
true
else
@@ -60,6 +64,12 @@ module Gitlab
false
end
+ def update_user
+ # no-op in CE
+ end
+
+ private
+
def adapter
@adapter ||= Gitlab::Auth::LDAP::Adapter.new(provider)
end
@@ -68,28 +78,28 @@ module Gitlab
Gitlab::Auth::LDAP::Config.new(provider)
end
- def find_ldap_user
- Gitlab::Auth::LDAP::Person.find_by_dn(ldap_identity.extern_uid, adapter)
- end
-
def ldap_user
return unless provider
@ldap_user ||= find_ldap_user
end
+ def find_ldap_user
+ Gitlab::Auth::LDAP::Person.find_by_dn(ldap_identity.extern_uid, adapter)
+ end
+
def block_user(user, reason)
user.ldap_block
if provider
Gitlab::AppLogger.info(
"LDAP account \"#{ldap_identity.extern_uid}\" #{reason}, " \
- "blocking Gitlab user \"#{user.name}\" (#{user.email})"
+ "blocking GitLab user \"#{user.name}\" (#{user.email})"
)
else
Gitlab::AppLogger.info(
"Account is not provided by LDAP, " \
- "blocking Gitlab user \"#{user.name}\" (#{user.email})"
+ "blocking GitLab user \"#{user.name}\" (#{user.email})"
)
end
end
@@ -99,13 +109,9 @@ module Gitlab
Gitlab::AppLogger.info(
"LDAP account \"#{ldap_identity.extern_uid}\" #{reason}, " \
- "unblocking Gitlab user \"#{user.name}\" (#{user.email})"
+ "unblocking GitLab user \"#{user.name}\" (#{user.email})"
)
end
-
- def update_user
- # no-op in CE
- end
end
end
end
diff --git a/lib/gitlab/auth/ldap/adapter.rb b/lib/gitlab/auth/ldap/adapter.rb
index 82ff1e77e5c..42c657afe6a 100644
--- a/lib/gitlab/auth/ldap/adapter.rb
+++ b/lib/gitlab/auth/ldap/adapter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module LDAP
diff --git a/lib/gitlab/auth/ldap/auth_hash.rb b/lib/gitlab/auth/ldap/auth_hash.rb
index ac5c14d374d..83fdc8a8c76 100644
--- a/lib/gitlab/auth/ldap/auth_hash.rb
+++ b/lib/gitlab/auth/ldap/auth_hash.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Class to parse and transform the info provided by omniauth
#
module Gitlab
diff --git a/lib/gitlab/auth/ldap/authentication.rb b/lib/gitlab/auth/ldap/authentication.rb
index 7c134fb6438..174e81dd603 100644
--- a/lib/gitlab/auth/ldap/authentication.rb
+++ b/lib/gitlab/auth/ldap/authentication.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These calls help to authenticate to LDAP by providing username and password
#
# Since multiple LDAP servers are supported, it will loop through all of them
diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb
index d4415eaa6dc..7ceb96f502b 100644
--- a/lib/gitlab/auth/ldap/config.rb
+++ b/lib/gitlab/auth/ldap/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Load a specific server configuration
module Gitlab
module Auth
diff --git a/lib/gitlab/auth/ldap/dn.rb b/lib/gitlab/auth/ldap/dn.rb
index 1fa5338f5a6..5df914aa367 100644
--- a/lib/gitlab/auth/ldap/dn.rb
+++ b/lib/gitlab/auth/ldap/dn.rb
@@ -1,4 +1,5 @@
# -*- ruby encoding: utf-8 -*-
+# frozen_string_literal: true
# Based on the `ruby-net-ldap` gem's `Net::LDAP::DN`
#
diff --git a/lib/gitlab/auth/ldap/ldap_connection_error.rb b/lib/gitlab/auth/ldap/ldap_connection_error.rb
index ef0a695742b..d0e5f24d203 100644
--- a/lib/gitlab/auth/ldap/ldap_connection_error.rb
+++ b/lib/gitlab/auth/ldap/ldap_connection_error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module LDAP
diff --git a/lib/gitlab/auth/ldap/person.rb b/lib/gitlab/auth/ldap/person.rb
index 8dfae3ee541..a0244a3cea1 100644
--- a/lib/gitlab/auth/ldap/person.rb
+++ b/lib/gitlab/auth/ldap/person.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module LDAP
diff --git a/lib/gitlab/auth/ldap/user.rb b/lib/gitlab/auth/ldap/user.rb
index 922d0567d99..9c71671f409 100644
--- a/lib/gitlab/auth/ldap/user.rb
+++ b/lib/gitlab/auth/ldap/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# LDAP extension for User model
#
# * Find or create user from omniauth.auth data
@@ -11,11 +13,13 @@ module Gitlab
extend ::Gitlab::Utils::Override
class << self
+ # rubocop: disable CodeReuse/ActiveRecord
def find_by_uid_and_provider(uid, provider)
identity = ::Identity.with_extern_uid(provider, uid).take
identity && identity.user
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
def save
diff --git a/lib/gitlab/auth/o_auth/auth_hash.rb b/lib/gitlab/auth/o_auth/auth_hash.rb
index ed8fba94305..36fc8061d92 100644
--- a/lib/gitlab/auth/o_auth/auth_hash.rb
+++ b/lib/gitlab/auth/o_auth/auth_hash.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Class to parse and transform the info provided by omniauth
#
module Gitlab
@@ -78,7 +80,7 @@ module Gitlab
end
# Get the first part of the email address (before @)
- # In addtion in removes illegal characters
+ # In addition in removes illegal characters
def generate_username(email)
email.match(/^[^@]*/)[0].mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/, '').to_s
end
diff --git a/lib/gitlab/auth/o_auth/authentication.rb b/lib/gitlab/auth/o_auth/authentication.rb
index d4e7f35c857..5f008678bd1 100644
--- a/lib/gitlab/auth/o_auth/authentication.rb
+++ b/lib/gitlab/auth/o_auth/authentication.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These calls help to authenticate to OAuth provider by providing username and password
#
diff --git a/lib/gitlab/auth/o_auth/identity_linker.rb b/lib/gitlab/auth/o_auth/identity_linker.rb
index de92d7a214d..e69c2bb54dc 100644
--- a/lib/gitlab/auth/o_auth/identity_linker.rb
+++ b/lib/gitlab/auth/o_auth/identity_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module OAuth
diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb
index e73743944a9..9fdf3324db3 100644
--- a/lib/gitlab/auth/o_auth/provider.rb
+++ b/lib/gitlab/auth/o_auth/provider.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module OAuth
@@ -29,6 +31,7 @@ module Gitlab
def self.enabled?(name)
return true if name == 'database'
+ return true if self.ldap_provider?(name) && providers.include?(name.to_sym)
Gitlab::Auth.omniauth_enabled? && providers.include?(name.to_sym)
end
diff --git a/lib/gitlab/auth/o_auth/session.rb b/lib/gitlab/auth/o_auth/session.rb
index 8f2b4d58552..4925b107042 100644
--- a/lib/gitlab/auth/o_auth/session.rb
+++ b/lib/gitlab/auth/o_auth/session.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# :nocov:
module Gitlab
module Auth
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index 589e8062226..a4e8a41b246 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# OAuth extension for User model
#
# * Find GitLab user based on omniauth uid and provider
@@ -112,11 +114,13 @@ module Gitlab
build_new_user
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_by_email
return unless auth_hash.has_attribute?(:email)
::User.find_by(email: auth_hash.email.downcase)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def auto_link_ldap_user?
Gitlab.config.omniauth.auto_link_ldap_user
@@ -180,10 +184,12 @@ module Gitlab
@auth_hash = AuthHash.new(auth_hash)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_by_uid_and_provider
identity = Identity.with_extern_uid(auth_hash.provider, auth_hash.uid).take
identity&.user
end
+ # rubocop: enable CodeReuse/ActiveRecord
def build_new_user
user_params = user_attributes.merge(skip_confirmation: true)
diff --git a/lib/gitlab/auth/omniauth_identity_linker_base.rb b/lib/gitlab/auth/omniauth_identity_linker_base.rb
index f79ce6bb809..253445570f2 100644
--- a/lib/gitlab/auth/omniauth_identity_linker_base.rb
+++ b/lib/gitlab/auth/omniauth_identity_linker_base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class OmniauthIdentityLinkerBase
@@ -33,11 +35,13 @@ module Gitlab
@changed = identity.save
end
+ # rubocop: disable CodeReuse/ActiveRecord
def identity
@identity ||= current_user.identities
.with_extern_uid(provider, uid)
.first_or_initialize(extern_uid: uid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def provider
oauth['provider']
diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb
index 66de52506ce..cb9f2582936 100644
--- a/lib/gitlab/auth/request_authenticator.rb
+++ b/lib/gitlab/auth/request_authenticator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Use for authentication only, in particular for Rack::Attack.
# Does not perform authorization of scopes, etc.
module Gitlab
diff --git a/lib/gitlab/auth/result.rb b/lib/gitlab/auth/result.rb
index 00cdc94a9ef..78fa25c5516 100644
--- a/lib/gitlab/auth/result.rb
+++ b/lib/gitlab/auth/result.rb
@@ -1,4 +1,7 @@
-module Gitlab # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module Gitlab
module Auth
Result = Struct.new(:actor, :project, :type, :authentication_abilities) do
def ci?(for_project)
diff --git a/lib/gitlab/auth/saml/auth_hash.rb b/lib/gitlab/auth/saml/auth_hash.rb
index 3bc5e2864df..1af9fa40c3a 100644
--- a/lib/gitlab/auth/saml/auth_hash.rb
+++ b/lib/gitlab/auth/saml/auth_hash.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module Saml
@@ -26,7 +28,7 @@ module Gitlab
end
def extract_authn_context(document)
- REXML::XPath.first(document, "//saml:AuthnStatement/saml:AuthnContext/saml:AuthnContextClassRef/text()").to_s
+ REXML::XPath.first(document, "//*[name()='saml:AuthnStatement' or name()='saml2:AuthnStatement']/*[name()='saml:AuthnContext' or name()='saml2:AuthnContext']/*[name()='saml:AuthnContextClassRef' or name()='saml2:AuthnContextClassRef']/text()").to_s
end
end
end
diff --git a/lib/gitlab/auth/saml/config.rb b/lib/gitlab/auth/saml/config.rb
index 625dab7c6f4..8cb999f50d4 100644
--- a/lib/gitlab/auth/saml/config.rb
+++ b/lib/gitlab/auth/saml/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module Saml
diff --git a/lib/gitlab/auth/saml/identity_linker.rb b/lib/gitlab/auth/saml/identity_linker.rb
index 7e4b191d512..ae0d6dded4e 100644
--- a/lib/gitlab/auth/saml/identity_linker.rb
+++ b/lib/gitlab/auth/saml/identity_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module Saml
diff --git a/lib/gitlab/auth/saml/user.rb b/lib/gitlab/auth/saml/user.rb
index 6c3b75f3eb0..ec95bc46791 100644
--- a/lib/gitlab/auth/saml/user.rb
+++ b/lib/gitlab/auth/saml/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# SAML extension for User model
#
# * Find GitLab user based on SAML uid and provider
diff --git a/lib/gitlab/auth/too_many_ips.rb b/lib/gitlab/auth/too_many_ips.rb
index ed862791551..ee4d80e6b89 100644
--- a/lib/gitlab/auth/too_many_ips.rb
+++ b/lib/gitlab/auth/too_many_ips.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class TooManyIps < StandardError
diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb
index baa1f802d8a..31dd61ae6cf 100644
--- a/lib/gitlab/auth/unique_ips_limiter.rb
+++ b/lib/gitlab/auth/unique_ips_limiter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class UniqueIpsLimiter
diff --git a/lib/gitlab/auth/user_access_denied_reason.rb b/lib/gitlab/auth/user_access_denied_reason.rb
index 1893cb001b2..fd09fe76c02 100644
--- a/lib/gitlab/auth/user_access_denied_reason.rb
+++ b/lib/gitlab/auth/user_access_denied_reason.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class UserAccessDeniedReason
diff --git a/lib/gitlab/auth/user_auth_finders.rb b/lib/gitlab/auth/user_auth_finders.rb
index c7993665421..c304adc64db 100644
--- a/lib/gitlab/auth/user_auth_finders.rb
+++ b/lib/gitlab/auth/user_auth_finders.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
AuthenticationError = Class.new(StandardError)
@@ -79,7 +81,7 @@ module Gitlab
return unless token
# Expiration, revocation and scopes are verified in `validate_access_token!`
- PersonalAccessToken.find_by(token: token) || raise(UnauthorizedError)
+ PersonalAccessToken.find_by_token(token) || raise(UnauthorizedError)
end
def find_oauth_access_token
diff --git a/lib/gitlab/background_migration.rb b/lib/gitlab/background_migration.rb
index 36c85dec544..d72befce571 100644
--- a/lib/gitlab/background_migration.rb
+++ b/lib/gitlab/background_migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BackgroundMigration
def self.queue
diff --git a/lib/gitlab/background_migration/digest_column.rb b/lib/gitlab/background_migration/digest_column.rb
new file mode 100644
index 00000000000..22a3bb8f8f3
--- /dev/null
+++ b/lib/gitlab/background_migration/digest_column.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# rubocop:disable Style/Documentation
+module Gitlab
+ module BackgroundMigration
+ class DigestColumn
+ class PersonalAccessToken < ActiveRecord::Base
+ self.table_name = 'personal_access_tokens'
+ end
+
+ def perform(model, attribute_from, attribute_to, start_id, stop_id)
+ model = model.constantize if model.is_a?(String)
+
+ model.transaction do
+ relation = model.where(id: start_id..stop_id).where.not(attribute_from => nil).lock
+
+ relation.each do |instance|
+ instance.update_columns(attribute_to => Gitlab::CryptoHelper.sha256(instance.read_attribute(attribute_from)),
+ attribute_from => nil)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/encrypt_columns.rb b/lib/gitlab/background_migration/encrypt_columns.rb
new file mode 100644
index 00000000000..0d333e47e7b
--- /dev/null
+++ b/lib/gitlab/background_migration/encrypt_columns.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # EncryptColumn migrates data from an unencrypted column - `foo`, say - to
+ # an encrypted column - `encrypted_foo`, say.
+ #
+ # For this background migration to work, the table that is migrated _has_ to
+ # have an `id` column as the primary key. Additionally, the encrypted column
+ # should be managed by attr_encrypted, and map to an attribute with the same
+ # name as the unencrypted column (i.e., the unencrypted column should be
+ # shadowed).
+ #
+ # To avoid depending on a particular version of the model in app/, add a
+ # model to `lib/gitlab/background_migration/models/encrypt_columns` and use
+ # it in the migration that enqueues the jobs, so code can be shared.
+ class EncryptColumns
+ def perform(model, attributes, from, to)
+ model = model.constantize if model.is_a?(String)
+ attributes = expand_attributes(model, Array(attributes).map(&:to_sym))
+
+ model.transaction do
+ # Use SELECT ... FOR UPDATE to prevent the value being changed while
+ # we are encrypting it
+ relation = model.where(id: from..to).lock
+
+ relation.each do |instance|
+ encrypt!(instance, attributes)
+ end
+ end
+ end
+
+ private
+
+ # Build a hash of { attribute => encrypted column name }
+ def expand_attributes(klass, attributes)
+ expanded = attributes.flat_map do |attribute|
+ attr_config = klass.encrypted_attributes[attribute]
+ crypt_column_name = attr_config&.fetch(:attribute)
+
+ raise "Couldn't determine encrypted column for #{klass}##{attribute}" if
+ crypt_column_name.nil?
+
+ [attribute, crypt_column_name]
+ end
+
+ Hash[*expanded]
+ end
+
+ # Generate ciphertext for each column and update the database
+ def encrypt!(instance, attributes)
+ to_clear = attributes
+ .map { |plain, crypt| apply_attribute!(instance, plain, crypt) }
+ .compact
+ .flat_map { |plain| [plain, nil] }
+
+ to_clear = Hash[*to_clear]
+
+ if instance.changed?
+ instance.save!
+ instance.update_columns(to_clear)
+ end
+ end
+
+ def apply_attribute!(instance, plain_column, crypt_column)
+ plaintext = instance[plain_column]
+ ciphertext = instance[crypt_column]
+
+ # No need to do anything if the plaintext is nil, or an encrypted
+ # value already exists
+ return nil unless plaintext.present? && !ciphertext.present?
+
+ # attr_encrypted will calculate and set the expected value for us
+ instance.public_send("#{plain_column}=", plaintext) # rubocop:disable GitlabSecurity/PublicSend
+
+ plain_column
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/migrate_legacy_artifacts.rb b/lib/gitlab/background_migration/migrate_legacy_artifacts.rb
new file mode 100644
index 00000000000..5cd638083b0
--- /dev/null
+++ b/lib/gitlab/background_migration/migrate_legacy_artifacts.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+# rubocop:disable Metrics/ClassLength
+
+module Gitlab
+ module BackgroundMigration
+ ##
+ # The class to migrate job artifacts from `ci_builds` to `ci_job_artifacts`
+ class MigrateLegacyArtifacts
+ FILE_LOCAL_STORE = 1 # equal to ObjectStorage::Store::LOCAL
+ ARCHIVE_FILE_TYPE = 1 # equal to Ci::JobArtifact.file_types['archive']
+ METADATA_FILE_TYPE = 2 # equal to Ci::JobArtifact.file_types['metadata']
+ LEGACY_PATH_FILE_LOCATION = 1 # equal to Ci::JobArtifact.file_location['legacy_path']
+
+ def perform(start_id, stop_id)
+ ActiveRecord::Base.transaction do
+ insert_archives(start_id, stop_id)
+ insert_metadatas(start_id, stop_id)
+ delete_legacy_artifacts(start_id, stop_id)
+ end
+ end
+
+ private
+
+ def insert_archives(start_id, stop_id)
+ ActiveRecord::Base.connection.execute <<~SQL
+ INSERT INTO
+ ci_job_artifacts (
+ project_id,
+ job_id,
+ expire_at,
+ file_location,
+ created_at,
+ updated_at,
+ file,
+ size,
+ file_store,
+ file_type
+ )
+ SELECT
+ project_id,
+ id,
+ artifacts_expire_at,
+ #{LEGACY_PATH_FILE_LOCATION},
+ created_at,
+ created_at,
+ artifacts_file,
+ artifacts_size,
+ COALESCE(artifacts_file_store, #{FILE_LOCAL_STORE}),
+ #{ARCHIVE_FILE_TYPE}
+ FROM
+ ci_builds
+ WHERE
+ id BETWEEN #{start_id.to_i} AND #{stop_id.to_i}
+ AND artifacts_file <> ''
+ AND NOT EXISTS (
+ SELECT
+ 1
+ FROM
+ ci_job_artifacts
+ WHERE
+ ci_builds.id = ci_job_artifacts.job_id
+ AND ci_job_artifacts.file_type = #{ARCHIVE_FILE_TYPE})
+ SQL
+ end
+
+ def insert_metadatas(start_id, stop_id)
+ ActiveRecord::Base.connection.execute <<~SQL
+ INSERT INTO
+ ci_job_artifacts (
+ project_id,
+ job_id,
+ expire_at,
+ file_location,
+ created_at,
+ updated_at,
+ file,
+ size,
+ file_store,
+ file_type
+ )
+ SELECT
+ project_id,
+ id,
+ artifacts_expire_at,
+ #{LEGACY_PATH_FILE_LOCATION},
+ created_at,
+ created_at,
+ artifacts_metadata,
+ NULL,
+ COALESCE(artifacts_metadata_store, #{FILE_LOCAL_STORE}),
+ #{METADATA_FILE_TYPE}
+ FROM
+ ci_builds
+ WHERE
+ id BETWEEN #{start_id.to_i} AND #{stop_id.to_i}
+ AND artifacts_file <> ''
+ AND artifacts_metadata <> ''
+ AND NOT EXISTS (
+ SELECT
+ 1
+ FROM
+ ci_job_artifacts
+ WHERE
+ ci_builds.id = ci_job_artifacts.job_id
+ AND ci_job_artifacts.file_type = #{METADATA_FILE_TYPE})
+ SQL
+ end
+
+ def delete_legacy_artifacts(start_id, stop_id)
+ ActiveRecord::Base.connection.execute <<~SQL
+ UPDATE
+ ci_builds
+ SET
+ artifacts_file = NULL,
+ artifacts_file_store = NULL,
+ artifacts_size = NULL,
+ artifacts_metadata = NULL,
+ artifacts_metadata_store = NULL
+ WHERE
+ id BETWEEN #{start_id.to_i} AND #{stop_id.to_i}
+ AND artifacts_file <> ''
+ SQL
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/models/encrypt_columns/web_hook.rb b/lib/gitlab/background_migration/models/encrypt_columns/web_hook.rb
new file mode 100644
index 00000000000..bb76eb8ed48
--- /dev/null
+++ b/lib/gitlab/background_migration/models/encrypt_columns/web_hook.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ module Models
+ module EncryptColumns
+ # This model is shared between synchronous and background migrations to
+ # encrypt the `token` and `url` columns
+ class WebHook < ActiveRecord::Base
+ include ::EachBatch
+
+ self.table_name = 'web_hooks'
+ self.inheritance_column = :_type_disabled
+
+ attr_encrypted :token,
+ mode: :per_attribute_iv,
+ algorithm: 'aes-256-gcm',
+ key: Settings.attr_encrypted_db_key_base_truncated
+
+ attr_encrypted :url,
+ mode: :per_attribute_iv,
+ algorithm: 'aes-256-gcm',
+ key: Settings.attr_encrypted_db_key_base_truncated
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/populate_cluster_kubernetes_namespace_table.rb b/lib/gitlab/background_migration/populate_cluster_kubernetes_namespace_table.rb
new file mode 100644
index 00000000000..35bfc381180
--- /dev/null
+++ b/lib/gitlab/background_migration/populate_cluster_kubernetes_namespace_table.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+#
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class PopulateClusterKubernetesNamespaceTable
+ include Gitlab::Database::MigrationHelpers
+
+ BATCH_SIZE = 1_000
+
+ module Migratable
+ class KubernetesNamespace < ActiveRecord::Base
+ self.table_name = 'clusters_kubernetes_namespaces'
+ end
+
+ class ClusterProject < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'cluster_projects'
+
+ belongs_to :project
+
+ def self.with_no_kubernetes_namespace
+ where.not(id: Migratable::KubernetesNamespace.select(:cluster_project_id))
+ end
+
+ def namespace
+ slug = "#{project.path}-#{project.id}".downcase
+ slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
+ end
+
+ def service_account
+ "#{namespace}-service-account"
+ end
+ end
+
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+ end
+ end
+
+ def perform
+ cluster_projects_with_no_kubernetes_namespace.each_batch(of: BATCH_SIZE) do |cluster_projects_batch, index|
+ sql_values = sql_values_for(cluster_projects_batch)
+
+ insert_into_cluster_kubernetes_namespace(sql_values)
+ end
+ end
+
+ private
+
+ def cluster_projects_with_no_kubernetes_namespace
+ Migratable::ClusterProject.with_no_kubernetes_namespace
+ end
+
+ def sql_values_for(cluster_projects)
+ cluster_projects.map do |cluster_project|
+ values_for_cluster_project(cluster_project)
+ end
+ end
+
+ def values_for_cluster_project(cluster_project)
+ {
+ cluster_project_id: cluster_project.id,
+ cluster_id: cluster_project.cluster_id,
+ project_id: cluster_project.project_id,
+ namespace: cluster_project.namespace,
+ service_account_name: cluster_project.service_account,
+ created_at: 'NOW()',
+ updated_at: 'NOW()'
+ }
+ end
+
+ def insert_into_cluster_kubernetes_namespace(rows)
+ Gitlab::Database.bulk_insert(Migratable::KubernetesNamespace.table_name,
+ rows,
+ disable_quote: [:created_at, :updated_at])
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/populate_external_pipeline_source.rb b/lib/gitlab/background_migration/populate_external_pipeline_source.rb
new file mode 100644
index 00000000000..036fe641757
--- /dev/null
+++ b/lib/gitlab/background_migration/populate_external_pipeline_source.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class PopulateExternalPipelineSource
+ module Migratable
+ class Pipeline < ActiveRecord::Base
+ self.table_name = 'ci_pipelines'
+
+ def self.sources
+ {
+ unknown: nil,
+ push: 1,
+ web: 2,
+ trigger: 3,
+ schedule: 4,
+ api: 5,
+ external: 6
+ }
+ end
+ end
+
+ class CommitStatus < ActiveRecord::Base
+ self.table_name = 'ci_builds'
+ self.inheritance_column = :_type_disabled
+
+ scope :has_pipeline, -> { where('ci_builds.commit_id=ci_pipelines.id') }
+ scope :of_type, -> (type) { where('type=?', type) }
+ end
+ end
+
+ def perform(start_id, stop_id)
+ external_pipelines(start_id, stop_id)
+ .update_all(source: Migratable::Pipeline.sources[:external])
+ end
+
+ private
+
+ def external_pipelines(start_id, stop_id)
+ Migratable::Pipeline.where(id: (start_id..stop_id))
+ .where(
+ 'EXISTS (?) AND NOT EXISTS (?)',
+ Migratable::CommitStatus.of_type('GenericCommitStatus').has_pipeline.select(1),
+ Migratable::CommitStatus.of_type('Ci::Build').has_pipeline.select(1)
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/redact_links.rb b/lib/gitlab/background_migration/redact_links.rb
new file mode 100644
index 00000000000..92256e59a6c
--- /dev/null
+++ b/lib/gitlab/background_migration/redact_links.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+require_relative 'redact_links/redactable'
+
+module Gitlab
+ module BackgroundMigration
+ class RedactLinks
+ class Note < ActiveRecord::Base
+ include EachBatch
+ include ::Gitlab::BackgroundMigration::RedactLinks::Redactable
+
+ self.table_name = 'notes'
+ self.inheritance_column = :_type_disabled
+ end
+
+ class Issue < ActiveRecord::Base
+ include EachBatch
+ include ::Gitlab::BackgroundMigration::RedactLinks::Redactable
+
+ self.table_name = 'issues'
+ self.inheritance_column = :_type_disabled
+ end
+
+ class MergeRequest < ActiveRecord::Base
+ include EachBatch
+ include ::Gitlab::BackgroundMigration::RedactLinks::Redactable
+
+ self.table_name = 'merge_requests'
+ self.inheritance_column = :_type_disabled
+ end
+
+ class Snippet < ActiveRecord::Base
+ include EachBatch
+ include ::Gitlab::BackgroundMigration::RedactLinks::Redactable
+
+ self.table_name = 'snippets'
+ self.inheritance_column = :_type_disabled
+ end
+
+ def perform(model_name, field, start_id, stop_id)
+ link_pattern = "%/sent_notifications/" + ("_" * 32) + "/unsubscribe%"
+ model = "Gitlab::BackgroundMigration::RedactLinks::#{model_name}".constantize
+
+ model.where("#{field} like ?", link_pattern).where(id: start_id..stop_id).each do |resource|
+ resource.redact_field!(field)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/redact_links/redactable.rb b/lib/gitlab/background_migration/redact_links/redactable.rb
new file mode 100644
index 00000000000..baab34221f1
--- /dev/null
+++ b/lib/gitlab/background_migration/redact_links/redactable.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class RedactLinks
+ module Redactable
+ extend ActiveSupport::Concern
+
+ def redact_field!(field)
+ self[field].gsub!(%r{/sent_notifications/\h{32}/unsubscribe}, '/sent_notifications/REDACTED/unsubscribe')
+
+ if self.changed?
+ self.update_columns(field => self[field],
+ "#{field}_html" => nil)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/remove_restricted_todos.rb b/lib/gitlab/background_migration/remove_restricted_todos.rb
index 68f3fa62170..47579d46c1b 100644
--- a/lib/gitlab/background_migration/remove_restricted_todos.rb
+++ b/lib/gitlab/background_migration/remove_restricted_todos.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
# rubocop:disable Style/Documentation
+# rubocop:disable Metrics/ClassLength
module Gitlab
module BackgroundMigration
@@ -49,11 +50,14 @@ module Gitlab
private
def remove_non_members_todos(project_id)
- Todo.where(project_id: project_id)
- .where('user_id NOT IN (?)', authorized_users(project_id))
- .each_batch(of: 5000) do |batch|
- batch.delete_all
- end
+ if Gitlab::Database.postgresql?
+ batch_remove_todos_cte(project_id)
+ else
+ unauthorized_project_todos(project_id)
+ .each_batch(of: 5000) do |batch|
+ batch.delete_all
+ end
+ end
end
def remove_confidential_issue_todos(project_id)
@@ -63,7 +67,7 @@ module Gitlab
.where('access_level >= ?', 20)
confidential_issues = Issue.select(:id, :author_id).where(confidential: true, project_id: project_id)
- confidential_issues.each_batch(of: 100) do |batch|
+ confidential_issues.each_batch(of: 100, order_hint: :confidential) do |batch|
batch.each do |issue|
assigned_users = IssueAssignee.select(:user_id).where(issue_id: issue.id)
@@ -86,10 +90,13 @@ module Gitlab
next if target_types.empty?
- Todo.where(project_id: project_id)
- .where('user_id NOT IN (?)', authorized_users(project_id))
- .where(target_type: target_types)
- .delete_all
+ if Gitlab::Database.postgresql?
+ batch_remove_todos_cte(project_id, target_types)
+ else
+ unauthorized_project_todos(project_id)
+ .where(target_type: target_types)
+ .delete_all
+ end
end
end
@@ -100,6 +107,65 @@ module Gitlab
def authorized_users(project_id)
ProjectAuthorization.select(:user_id).where(project_id: project_id)
end
+
+ def unauthorized_project_todos(project_id)
+ Todo.where(project_id: project_id)
+ .where('user_id NOT IN (?)', authorized_users(project_id))
+ end
+
+ def batch_remove_todos_cte(project_id, target_types = nil)
+ loop do
+ count = remove_todos_cte(project_id, target_types)
+
+ break if count == 0
+ end
+ end
+
+ def remove_todos_cte(project_id, target_types = nil)
+ sql = []
+ sql << with_all_todos_sql(project_id, target_types)
+ sql << as_deleted_sql
+ sql << "SELECT count(*) FROM deleted"
+
+ result = Todo.connection.exec_query(sql.join(' '))
+ result.rows[0][0].to_i
+ end
+
+ def with_all_todos_sql(project_id, target_types = nil)
+ if target_types
+ table = Arel::Table.new(:todos)
+ in_target = table[:target_type].in(target_types)
+ target_types_sql = " AND #{in_target.to_sql}"
+ end
+
+ <<-SQL
+ WITH all_todos AS (
+ SELECT id
+ FROM "todos"
+ WHERE "todos"."project_id" = #{project_id}
+ AND (user_id NOT IN (
+ SELECT "project_authorizations"."user_id"
+ FROM "project_authorizations"
+ WHERE "project_authorizations"."project_id" = #{project_id})
+ #{target_types_sql}
+ )
+ ),
+ SQL
+ end
+
+ def as_deleted_sql
+ <<-SQL
+ deleted AS (
+ DELETE FROM todos
+ WHERE id IN (
+ SELECT id
+ FROM all_todos
+ LIMIT 5000
+ )
+ RETURNING id
+ )
+ SQL
+ end
end
end
end
diff --git a/lib/gitlab/background_migration/set_confidential_note_events_on_services.rb b/lib/gitlab/background_migration/set_confidential_note_events_on_services.rb
index e5e8837221e..bc434b0cb64 100644
--- a/lib/gitlab/background_migration/set_confidential_note_events_on_services.rb
+++ b/lib/gitlab/background_migration/set_confidential_note_events_on_services.rb
@@ -3,8 +3,8 @@
module Gitlab
module BackgroundMigration
- # Ensures services which previously recieved all notes events continue
- # to recieve confidential ones.
+ # Ensures services which previously received all notes events continue
+ # to receive confidential ones.
class SetConfidentialNoteEventsOnServices
class Service < ActiveRecord::Base
self.table_name = 'services'
diff --git a/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb b/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb
index 171c8ef21b7..28d8d2c640b 100644
--- a/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb
+++ b/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb
@@ -3,8 +3,8 @@
module Gitlab
module BackgroundMigration
- # Ensures hooks which previously recieved all notes events continue
- # to recieve confidential ones.
+ # Ensures hooks which previously received all notes events continue
+ # to receive confidential ones.
class SetConfidentialNoteEventsOnWebhooks
class WebHook < ActiveRecord::Base
self.table_name = 'web_hooks'
diff --git a/lib/gitlab/badge/base.rb b/lib/gitlab/badge/base.rb
index 909fa24fa90..fb55b9e2f1f 100644
--- a/lib/gitlab/badge/base.rb
+++ b/lib/gitlab/badge/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
class Base
diff --git a/lib/gitlab/badge/coverage/metadata.rb b/lib/gitlab/badge/coverage/metadata.rb
index e898f5d790e..9181ba2d4b0 100644
--- a/lib/gitlab/badge/coverage/metadata.rb
+++ b/lib/gitlab/badge/coverage/metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Coverage
diff --git a/lib/gitlab/badge/coverage/report.rb b/lib/gitlab/badge/coverage/report.rb
index 778d78185ff..a7fcb6b0fca 100644
--- a/lib/gitlab/badge/coverage/report.rb
+++ b/lib/gitlab/badge/coverage/report.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Coverage
@@ -36,6 +38,7 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def raw_coverage
return unless @pipeline
@@ -47,6 +50,7 @@ module Gitlab
.try(:coverage)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/badge/coverage/template.rb b/lib/gitlab/badge/coverage/template.rb
index afbf9dd17e3..817dc28f84a 100644
--- a/lib/gitlab/badge/coverage/template.rb
+++ b/lib/gitlab/badge/coverage/template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Coverage
diff --git a/lib/gitlab/badge/metadata.rb b/lib/gitlab/badge/metadata.rb
index 8ad6f3cb986..b9ae68134b0 100644
--- a/lib/gitlab/badge/metadata.rb
+++ b/lib/gitlab/badge/metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
##
diff --git a/lib/gitlab/badge/pipeline/metadata.rb b/lib/gitlab/badge/pipeline/metadata.rb
index db1e9f8cfb8..d4d789558c9 100644
--- a/lib/gitlab/badge/pipeline/metadata.rb
+++ b/lib/gitlab/badge/pipeline/metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Pipeline
diff --git a/lib/gitlab/badge/pipeline/status.rb b/lib/gitlab/badge/pipeline/status.rb
index 5fee7a93475..37e61f07e5b 100644
--- a/lib/gitlab/badge/pipeline/status.rb
+++ b/lib/gitlab/badge/pipeline/status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Pipeline
@@ -18,11 +20,13 @@ module Gitlab
'pipeline'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def status
@project.pipelines
.where(sha: @sha)
.latest_status(@ref) || 'unknown'
end
+ # rubocop: enable CodeReuse/ActiveRecord
def metadata
@metadata ||= Pipeline::Metadata.new(self)
diff --git a/lib/gitlab/badge/pipeline/template.rb b/lib/gitlab/badge/pipeline/template.rb
index e09db32262d..64c3dfcd10b 100644
--- a/lib/gitlab/badge/pipeline/template.rb
+++ b/lib/gitlab/badge/pipeline/template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Pipeline
diff --git a/lib/gitlab/badge/template.rb b/lib/gitlab/badge/template.rb
index bfeb0052642..ed2ec50b197 100644
--- a/lib/gitlab/badge/template.rb
+++ b/lib/gitlab/badge/template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
##
diff --git a/lib/gitlab/bare_repository_import/importer.rb b/lib/gitlab/bare_repository_import/importer.rb
index 04aa6aab771..3cd327f5109 100644
--- a/lib/gitlab/bare_repository_import/importer.rb
+++ b/lib/gitlab/bare_repository_import/importer.rb
@@ -1,10 +1,15 @@
+# frozen_string_literal: true
+
module Gitlab
module BareRepositoryImport
class Importer
NoAdminError = Class.new(StandardError)
def self.execute(import_path)
- import_path << '/' unless import_path.ends_with?('/')
+ unless import_path.ends_with?('/')
+ import_path = "#{import_path}/"
+ end
+
repos_to_import = Dir.glob(import_path + '**/*.git')
unless user = User.admins.order_id_asc.first
diff --git a/lib/gitlab/bare_repository_import/repository.rb b/lib/gitlab/bare_repository_import/repository.rb
index c0c666dfb7b..b903c581aac 100644
--- a/lib/gitlab/bare_repository_import/repository.rb
+++ b/lib/gitlab/bare_repository_import/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BareRepositoryImport
class Repository
@@ -6,9 +8,12 @@ module Gitlab
attr_reader :group_path, :project_name, :repo_path
def initialize(root_path, repo_path)
+ unless root_path.ends_with?('/')
+ root_path = "#{root_path}/"
+ end
+
@root_path = root_path
@repo_path = repo_path
- @root_path << '/' unless root_path.ends_with?('/')
full_path =
if hashed? && !wiki?
diff --git a/lib/gitlab/base_doorkeeper_controller.rb b/lib/gitlab/base_doorkeeper_controller.rb
index e4227af25d2..b78993aba30 100644
--- a/lib/gitlab/base_doorkeeper_controller.rb
+++ b/lib/gitlab/base_doorkeeper_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This is a base controller for doorkeeper.
# It adds the `can?` helper used in the views.
module Gitlab
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index fa0186c854c..45e550b3450 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BitbucketImport
class Importer
@@ -43,6 +45,7 @@ module Gitlab
find_user_id(username) || project.creator_id
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_user_id(username)
return nil unless username
@@ -53,6 +56,7 @@ module Gitlab
.find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", username)
.try(:id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def repo
@repo ||= client.repo(project.import_source)
@@ -68,6 +72,7 @@ module Gitlab
errors << { type: :wiki, errors: e.message }
end
+ # rubocop: disable CodeReuse/ActiveRecord
def import_issues
return unless repo.issues_enabled?
@@ -101,6 +106,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def import_issue_comments(issue, gitlab_issue)
client.issue_comments(repo, issue.iid).each do |comment|
diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb
index d94f70fd1fb..11070a68e02 100644
--- a/lib/gitlab/bitbucket_import/project_creator.rb
+++ b/lib/gitlab/bitbucket_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BitbucketImport
class ProjectCreator
diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb
index 268d21a77d1..15aa4739ee9 100644
--- a/lib/gitlab/bitbucket_server_import/importer.rb
+++ b/lib/gitlab/bitbucket_server_import/importer.rb
@@ -1,9 +1,13 @@
+# frozen_string_literal: true
+
module Gitlab
module BitbucketServerImport
class Importer
include Gitlab::ShellAdapter
+
attr_reader :recover_missing_commits
attr_reader :project, :project_key, :repository_slug, :client, :errors, :users
+ attr_accessor :logger
REMOTE_NAME = 'bitbucket_server'.freeze
BATCH_SIZE = 100
@@ -33,6 +37,7 @@ module Gitlab
@errors = []
@users = {}
@temp_branches = []
+ @logger = Gitlab::Import::Logger.build
end
def execute
@@ -41,6 +46,8 @@ module Gitlab
delete_temp_branches
handle_errors
+ log_info(stage: "complete")
+
true
end
@@ -115,15 +122,21 @@ module Gitlab
client.create_branch(project_key, repository_slug, branch_name, sha)
branches_created << temp_branch
rescue BitbucketServer::Connection::ConnectionError => e
- Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{e}")
+ log_warn(message: "Unable to recreate branch", sha: sha, error: e.message)
end
end
end
def import_repository
+ log_info(stage: 'import_repository', message: 'starting import')
+
project.ensure_repository
project.repository.fetch_as_mirror(project.import_url, refmap: self.class.refmap, remote_name: REMOTE_NAME)
+
+ log_info(stage: 'import_repository', message: 'finished import')
rescue Gitlab::Shell::Error, Gitlab::Git::RepositoryMirroring::RemoteError => e
+ log_error(stage: 'import_repository', message: 'failed import', error: e.message)
+
# Expire cache to prevent scenarios such as:
# 1. First import failed, but the repo was imported successfully, so +exists?+ returns true
# 2. Retried import, repo is broken or not imported but +exists?+ still returns true
@@ -154,7 +167,10 @@ module Gitlab
begin
import_bitbucket_pull_request(pull_request)
rescue StandardError => e
- errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw }
+ backtrace = Gitlab::Profiler.clean_backtrace(e.backtrace)
+ log_error(stage: 'import_pull_requests', iid: pull_request.iid, error: e.message, backtrace: backtrace)
+
+ errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, backtrace: backtrace.join("\n"), raw_response: pull_request.raw }
end
end
end
@@ -166,30 +182,30 @@ module Gitlab
client.delete_branch(project_key, repository_slug, branch.name, branch.sha)
project.repository.delete_branch(branch.name)
rescue BitbucketServer::Connection::ConnectionError => e
+ log_error(stage: 'delete_temp_branches', branch: branch.name, error: e.message)
@errors << { type: :delete_temp_branches, branch_name: branch.name, errors: e.message }
end
end
end
def import_bitbucket_pull_request(pull_request)
+ log_info(stage: 'import_bitbucket_pull_requests', message: 'starting', iid: pull_request.iid)
+
description = ''
description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email)
description += pull_request.description if pull_request.description
-
- source_branch_sha = pull_request.source_branch_sha
- target_branch_sha = pull_request.target_branch_sha
author_id = gitlab_user_id(pull_request.author_email)
attributes = {
iid: pull_request.iid,
title: pull_request.title,
description: description,
- source_project: project,
+ source_project_id: project.id,
source_branch: Gitlab::Git.ref_name(pull_request.source_branch_name),
- source_branch_sha: source_branch_sha,
- target_project: project,
+ source_branch_sha: pull_request.source_branch_sha,
+ target_project_id: project.id,
target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name),
- target_branch_sha: target_branch_sha,
+ target_branch_sha: pull_request.target_branch_sha,
state: pull_request.state,
author_id: author_id,
assignee_id: nil,
@@ -197,11 +213,17 @@ module Gitlab
updated_at: pull_request.updated_at
}
- merge_request = project.merge_requests.create!(attributes)
+ creator = Gitlab::Import::MergeRequestCreator.new(project)
+ merge_request = creator.execute(attributes)
+
import_pull_request_comments(pull_request, merge_request) if merge_request.persisted?
+
+ log_info(stage: 'import_bitbucket_pull_requests', message: 'finished', iid: pull_request.iid)
end
def import_pull_request_comments(pull_request, merge_request)
+ log_info(stage: 'import_pull_request_comments', message: 'starting', iid: merge_request.iid)
+
comments, other_activities = client.activities(project_key, repository_slug, pull_request.iid).partition(&:comment?)
merge_event = other_activities.find(&:merge_event?)
@@ -211,9 +233,17 @@ module Gitlab
import_inline_comments(inline_comments.map(&:comment), merge_request)
import_standalone_pr_comments(pr_comments.map(&:comment), merge_request)
+
+ log_info(stage: 'import_pull_request_comments', message: 'finished', iid: merge_request.iid,
+ merge_event_found: merge_event.present?,
+ inline_comments_count: inline_comments.count,
+ standalone_pr_comments: pr_comments.count)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def import_merge_event(merge_request, merge_event)
+ log_info(stage: 'import_merge_event', message: 'starting', iid: merge_request.iid)
+
committer = merge_event.committer_email
user_id = gitlab_user_id(committer)
@@ -221,9 +251,14 @@ module Gitlab
merge_request.update({ merge_commit_sha: merge_event.merge_commit })
metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request)
metric.update(merged_by_id: user_id, merged_at: timestamp)
+
+ log_info(stage: 'import_merge_event', message: 'finished', iid: merge_request.iid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def import_inline_comments(inline_comments, merge_request)
+ log_info(stage: 'import_inline_comments', message: 'starting', iid: merge_request.iid)
+
inline_comments.each do |comment|
position = build_position(merge_request, comment)
parent = create_diff_note(merge_request, comment, position)
@@ -236,6 +271,8 @@ module Gitlab
create_diff_note(merge_request, reply, position, discussion_id)
end
end
+
+ log_info(stage: 'import_inline_comments', message: 'finished', iid: merge_request.iid)
end
def create_diff_note(merge_request, comment, position, discussion_id = nil)
@@ -250,11 +287,14 @@ module Gitlab
return note
end
+ log_info(stage: 'create_diff_note', message: 'creating fallback DiffNote', iid: merge_request.iid)
+
# Bitbucket Server supports the ability to comment on any line, not just the
# line in the diff. If we can't add the note as a DiffNote, fallback to creating
# a regular note.
create_fallback_diff_note(merge_request, comment, position)
rescue StandardError => e
+ log_error(stage: 'create_diff_note', comment_id: comment.id, error: e.message)
errors << { type: :pull_request, id: comment.id, errors: e.message }
nil
end
@@ -292,7 +332,8 @@ module Gitlab
merge_request.notes.create!(pull_request_comment_attributes(replies))
end
rescue StandardError => e
- errors << { type: :pull_request, iid: comment.id, errors: e.message }
+ log_error(stage: 'import_standalone_pr_comments', merge_request_id: merge_request.id, comment_id: comment.id, error: e.message)
+ errors << { type: :pull_request, comment_id: comment.id, errors: e.message }
end
end
end
@@ -322,6 +363,26 @@ module Gitlab
updated_at: comment.updated_at
}
end
+
+ def log_info(details)
+ logger.info(log_base_data.merge(details))
+ end
+
+ def log_error(details)
+ logger.error(log_base_data.merge(details))
+ end
+
+ def log_warn(details)
+ logger.warn(log_base_data.merge(details))
+ end
+
+ def log_base_data
+ {
+ class: self.class.name,
+ project_id: project.id,
+ project_path: project.full_path
+ }
+ end
end
end
end
diff --git a/lib/gitlab/bitbucket_server_import/project_creator.rb b/lib/gitlab/bitbucket_server_import/project_creator.rb
index 35e8cd7e0ab..48ca4951957 100644
--- a/lib/gitlab/bitbucket_server_import/project_creator.rb
+++ b/lib/gitlab/bitbucket_server_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BitbucketServerImport
class ProjectCreator
diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb
index 169aac79854..f1a653a9d95 100644
--- a/lib/gitlab/blame.rb
+++ b/lib/gitlab/blame.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class Blame
attr_accessor :blob, :commit
@@ -41,8 +43,7 @@ module Gitlab
def highlighted_lines
@blob.load_all_data!
- @highlighted_lines ||=
- Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: repository).lines
+ @highlighted_lines ||= @blob.present.highlight.lines
end
def project
diff --git a/lib/gitlab/blob_helper.rb b/lib/gitlab/blob_helper.rb
new file mode 100644
index 00000000000..488c1d85387
--- /dev/null
+++ b/lib/gitlab/blob_helper.rb
@@ -0,0 +1,147 @@
+# frozen_string_literal: true
+
+# This has been extracted from https://github.com/github/linguist/blob/master/lib/linguist/blob_helper.rb
+module Gitlab
+ module BlobHelper
+ def extname
+ File.extname(name.to_s)
+ end
+
+ def known_extension?
+ LanguageData.extensions.include?(extname)
+ end
+
+ def viewable?
+ !large? && text?
+ end
+
+ MEGABYTE = 1024 * 1024
+
+ def large?
+ size.to_i > MEGABYTE
+ end
+
+ def binary?
+ # Large blobs aren't even loaded into memory
+ if data.nil?
+ true
+
+ # Treat blank files as text
+ elsif data == ""
+ false
+
+ # Charlock doesn't know what to think
+ elsif encoding.nil?
+ true
+
+ # If Charlock says its binary
+ else
+ detect_encoding[:type] == :binary
+ end
+ end
+
+ def text?
+ !binary?
+ end
+
+ def image?
+ ['.png', '.jpg', '.jpeg', '.gif'].include?(extname.downcase)
+ end
+
+ # Internal: Lookup mime type for extension.
+ #
+ # Returns a MIME::Type
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def _mime_type
+ if defined? @_mime_type
+ @_mime_type
+ else
+ guesses = ::MIME::Types.type_for(extname.to_s)
+
+ # Prefer text mime types over binary
+ @_mime_type = guesses.detect { |type| type.ascii? } || guesses.first
+ end
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ # Public: Get the actual blob mime type
+ #
+ # Examples
+ #
+ # # => 'text/plain'
+ # # => 'text/html'
+ #
+ # Returns a mime type String.
+ def mime_type
+ _mime_type ? _mime_type.to_s : 'text/plain'
+ end
+
+ def binary_mime_type?
+ _mime_type ? _mime_type.binary? : false
+ end
+
+ def lines
+ @lines ||=
+ if viewable? && data
+ # `data` is usually encoded as ASCII-8BIT even when the content has
+ # been detected as a different encoding. However, we are not allowed
+ # to change the encoding of `data` because we've made the implicit
+ # guarantee that each entry in `lines` is encoded the same way as
+ # `data`.
+ #
+ # Instead, we re-encode each possible newline sequence as the
+ # detected encoding, then force them back to the encoding of `data`
+ # (usually a binary encoding like ASCII-8BIT). This means that the
+ # byte sequence will match how newlines are likely encoded in the
+ # file, but we don't have to change the encoding of `data` as far as
+ # Ruby is concerned. This allows us to correctly parse out each line
+ # without changing the encoding of `data`, and
+ # also--importantly--without having to duplicate many (potentially
+ # large) strings.
+ begin
+ data.split(encoded_newlines_re, -1)
+ rescue Encoding::ConverterNotFoundError
+ # The data is not splittable in the detected encoding. Assume it's
+ # one big line.
+ [data]
+ end
+ else
+ []
+ end
+ end
+
+ def content_type
+ # rubocop:disable Style/MultilineTernaryOperator
+ # rubocop:disable Style/NestedTernaryOperator
+ @content_type ||= binary_mime_type? || binary? ? mime_type :
+ (encoding ? "text/plain; charset=#{encoding.downcase}" : "text/plain")
+ # rubocop:enable Style/NestedTernaryOperator
+ # rubocop:enable Style/MultilineTernaryOperator
+ end
+
+ def encoded_newlines_re
+ @encoded_newlines_re ||=
+ Regexp.union(["\r\n", "\r", "\n"].map { |nl| nl.encode(ruby_encoding, "ASCII-8BIT").force_encoding(data.encoding) })
+ end
+
+ def ruby_encoding
+ if hash = detect_encoding
+ hash[:ruby_encoding]
+ end
+ end
+
+ def encoding
+ if hash = detect_encoding
+ hash[:encoding]
+ end
+ end
+
+ def detect_encoding
+ @detect_encoding ||= CharlockHolmes::EncodingDetector.new.detect(data) if data # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+
+ def empty?
+ data.nil? || data == ""
+ end
+ end
+end
diff --git a/lib/gitlab/build_access.rb b/lib/gitlab/build_access.rb
index 08a8f846ca5..37e79413541 100644
--- a/lib/gitlab/build_access.rb
+++ b/lib/gitlab/build_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class BuildAccess < UserAccess
attr_accessor :user, :project
diff --git a/lib/gitlab/cache/ci/project_pipeline_status.rb b/lib/gitlab/cache/ci/project_pipeline_status.rb
index add048d671e..78b0eaac8cd 100644
--- a/lib/gitlab/cache/ci/project_pipeline_status.rb
+++ b/lib/gitlab/cache/ci/project_pipeline_status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class is not backed by a table in the main database.
# It loads the latest Pipeline for the HEAD of a repository, and caches that
# in Redis.
@@ -40,7 +42,7 @@ module Gitlab
end
def self.cache_key_for_project(project)
- "#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:projects/#{project.id}/pipeline_status"
+ "#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:project:#{project.id}:pipeline_status:#{project.commit&.sha}"
end
def self.update_for_pipeline(pipeline)
@@ -82,9 +84,7 @@ module Gitlab
def load_from_project
return unless commit
- self.sha = commit.sha
- self.status = commit.status
- self.ref = project.default_branch
+ self.sha, self.status, self.ref = commit.sha, commit.status, project.default_branch
end
# We only cache the status for the HEAD commit of a project
@@ -102,6 +102,8 @@ module Gitlab
def load_from_cache
Gitlab::Redis::Cache.with do |redis|
self.sha, self.status, self.ref = redis.hmget(cache_key, :sha, :status, :ref)
+
+ self.status = nil if self.status.empty?
end
end
diff --git a/lib/gitlab/cache/request_cache.rb b/lib/gitlab/cache/request_cache.rb
index 671b8e7e1b1..4c658dc0b8d 100644
--- a/lib/gitlab/cache/request_cache.rb
+++ b/lib/gitlab/cache/request_cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Cache
# See https://docs.gitlab.com/ee/development/utilities.html#requestcache
@@ -26,8 +28,8 @@ module Gitlab
define_method(method_name) do |*args|
store =
- if RequestStore.active?
- RequestStore.store
+ if Gitlab::SafeRequestStore.active?
+ Gitlab::SafeRequestStore.store
else
ivar_name = # ! and ? cannot be used as ivar name
"@cache_#{method_name.to_s.tr('!?', "\u2605\u2606")}"
diff --git a/lib/gitlab/changes_list.rb b/lib/gitlab/changes_list.rb
index 9c9e6668e6f..fb75a78a978 100644
--- a/lib/gitlab/changes_list.rb
+++ b/lib/gitlab/changes_list.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class ChangesList
include Enumerable
diff --git a/lib/gitlab/chat_name_token.rb b/lib/gitlab/chat_name_token.rb
index e63e5437331..8b3c5dc9e8b 100644
--- a/lib/gitlab/chat_name_token.rb
+++ b/lib/gitlab/chat_name_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'json'
module Gitlab
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index 7a4224e5bbe..074afe9c412 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class ChangeAccess
@@ -16,11 +18,24 @@ module Gitlab
lfs_objects_missing: 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'
}.freeze
- attr_reader :user_access, :project, :skip_authorization, :skip_lfs_integrity_check, :protocol, :oldrev, :newrev, :ref, :branch_name, :tag_name
+ LOG_MESSAGES = {
+ push_checks: "Checking if you are allowed to push...",
+ delete_default_branch_check: "Checking if default branch is being deleted...",
+ protected_branch_checks: "Checking if you are force pushing to a protected branch...",
+ protected_branch_push_checks: "Checking if you are allowed to push to the protected branch...",
+ protected_branch_deletion_checks: "Checking if you are allowed to delete the protected branch...",
+ tag_checks: "Checking if you are allowed to change existing tags...",
+ protected_tag_checks: "Checking if you are creating, updating or deleting a protected tag...",
+ lfs_objects_exist_check: "Scanning repository for blobs stored in LFS and verifying their files have been uploaded to GitLab...",
+ commits_check_file_paths_validation: "Validating commits' file paths...",
+ commits_check: "Validating commit contents..."
+ }.freeze
+
+ attr_reader :user_access, :project, :skip_authorization, :skip_lfs_integrity_check, :protocol, :oldrev, :newrev, :ref, :branch_name, :tag_name, :logger
def initialize(
change, user_access:, project:, skip_authorization: false,
- skip_lfs_integrity_check: false, protocol:
+ skip_lfs_integrity_check: false, protocol:, logger:
)
@oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
@branch_name = Gitlab::Git.branch_name(@ref)
@@ -30,6 +45,9 @@ module Gitlab
@skip_authorization = skip_authorization
@skip_lfs_integrity_check = skip_lfs_integrity_check
@protocol = protocol
+
+ @logger = logger
+ @logger.append_message("Running checks for ref: #{@branch_name || @tag_name}")
end
def exec(skip_commits_check: false)
@@ -47,26 +65,32 @@ module Gitlab
protected
def push_checks
- unless can_push?
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:push_code]
+ logger.log_timed(LOG_MESSAGES[__method__]) do
+ unless can_push?
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:push_code]
+ end
end
end
def branch_checks
return unless branch_name
- if deletion? && branch_name == project.default_branch
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_default_branch]
+ logger.log_timed(LOG_MESSAGES[:delete_default_branch_check]) do
+ if deletion? && branch_name == project.default_branch
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_default_branch]
+ end
end
protected_branch_checks
end
def protected_branch_checks
- return unless ProtectedBranch.protected?(project, branch_name)
+ logger.log_timed(LOG_MESSAGES[__method__]) do
+ return unless ProtectedBranch.protected?(project, branch_name) # rubocop:disable Cop/AvoidReturnFromBlocks
- if forced_push?
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:force_push_protected_branch]
+ if forced_push?
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:force_push_protected_branch]
+ end
end
if deletion?
@@ -77,23 +101,27 @@ module Gitlab
end
def protected_branch_deletion_checks
- unless user_access.can_delete_branch?(branch_name)
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_master_delete_protected_branch]
- end
+ logger.log_timed(LOG_MESSAGES[__method__]) do
+ unless user_access.can_delete_branch?(branch_name)
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_master_delete_protected_branch]
+ end
- unless updated_from_web?
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_web_delete_protected_branch]
+ unless updated_from_web?
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_web_delete_protected_branch]
+ end
end
end
def protected_branch_push_checks
- if matching_merge_request?
- unless user_access.can_merge_to_branch?(branch_name) || user_access.can_push_to_branch?(branch_name)
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:merge_protected_branch]
- end
- else
- unless user_access.can_push_to_branch?(branch_name)
- raise GitAccess::UnauthorizedError, push_to_protected_branch_rejected_message
+ logger.log_timed(LOG_MESSAGES[__method__]) do
+ if matching_merge_request?
+ unless user_access.can_merge_to_branch?(branch_name) || user_access.can_push_to_branch?(branch_name)
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:merge_protected_branch]
+ end
+ else
+ unless user_access.can_push_to_branch?(branch_name)
+ raise GitAccess::UnauthorizedError, push_to_protected_branch_rejected_message
+ end
end
end
end
@@ -101,21 +129,25 @@ module Gitlab
def tag_checks
return unless tag_name
- if tag_exists? && user_access.cannot_do_action?(:admin_project)
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:change_existing_tags]
+ logger.log_timed(LOG_MESSAGES[__method__]) do
+ if tag_exists? && user_access.cannot_do_action?(:admin_project)
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:change_existing_tags]
+ end
end
protected_tag_checks
end
def protected_tag_checks
- return unless ProtectedTag.protected?(project, tag_name)
+ logger.log_timed(LOG_MESSAGES[__method__]) do
+ return unless ProtectedTag.protected?(project, tag_name) # rubocop:disable Cop/AvoidReturnFromBlocks
- raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:update_protected_tag]) if update?
- raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_protected_tag]) if deletion?
+ raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:update_protected_tag]) if update?
+ raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_protected_tag]) if deletion?
- unless user_access.can_create_tag?(tag_name)
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:create_protected_tag]
+ unless user_access.can_create_tag?(tag_name)
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:create_protected_tag]
+ end
end
end
@@ -123,14 +155,20 @@ module Gitlab
return if deletion? || newrev.nil?
return unless should_run_commit_validations?
- # n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593
- ::Gitlab::GitalyClient.allow_n_plus_1_calls do
- commits.each do |commit|
- commit_check.validate(commit, validations_for_commit(commit))
+ logger.log_timed(LOG_MESSAGES[__method__]) do
+ # n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593
+ ::Gitlab::GitalyClient.allow_n_plus_1_calls do
+ commits.each do |commit|
+ logger.check_timeout_reached
+
+ commit_check.validate(commit, validations_for_commit(commit))
+ end
end
end
- commit_check.validate_file_paths
+ logger.log_timed(LOG_MESSAGES[:commits_check_file_paths_validation]) do
+ commit_check.validate_file_paths
+ end
end
# Method overwritten in EE to inject custom validations
@@ -192,10 +230,12 @@ module Gitlab
end
def lfs_objects_exist_check
- lfs_check = Checks::LfsIntegrity.new(project, newrev)
+ logger.log_timed(LOG_MESSAGES[__method__]) do
+ lfs_check = Checks::LfsIntegrity.new(project, newrev, logger.time_left)
- if lfs_check.objects_missing?
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:lfs_objects_missing]
+ if lfs_check.objects_missing?
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:lfs_objects_missing]
+ end
end
end
diff --git a/lib/gitlab/checks/commit_check.rb b/lib/gitlab/checks/commit_check.rb
index 22310e313ac..58267b6752f 100644
--- a/lib/gitlab/checks/commit_check.rb
+++ b/lib/gitlab/checks/commit_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class CommitCheck
@@ -8,8 +10,8 @@ module Gitlab
def initialize(project, user, newrev, oldrev)
@project = project
@user = user
- @newrev = user
- @oldrev = user
+ @newrev = newrev
+ @oldrev = oldrev
@file_paths = []
end
@@ -43,6 +45,7 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def lfs_file_locks_validation
lambda do |paths|
lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user.id).first
@@ -52,6 +55,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def path_validations
validate_lfs_file_locks? ? [lfs_file_locks_validation] : []
diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb
index 87af4a90572..263972923ed 100644
--- a/lib/gitlab/checks/force_push.rb
+++ b/lib/gitlab/checks/force_push.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class ForcePush
diff --git a/lib/gitlab/checks/lfs_integrity.rb b/lib/gitlab/checks/lfs_integrity.rb
index b816a8f00cd..1652d5a30a4 100644
--- a/lib/gitlab/checks/lfs_integrity.rb
+++ b/lib/gitlab/checks/lfs_integrity.rb
@@ -1,16 +1,20 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class LfsIntegrity
- def initialize(project, newrev)
+ def initialize(project, newrev, time_left)
@project = project
@newrev = newrev
+ @time_left = time_left
end
+ # rubocop: disable CodeReuse/ActiveRecord
def objects_missing?
return false unless @newrev && @project.lfs_enabled?
new_lfs_pointers = Gitlab::Git::LfsChanges.new(@project.repository, @newrev)
- .new_pointers(object_limit: ::Gitlab::Git::Repository::REV_LIST_COMMIT_LIMIT)
+ .new_pointers(object_limit: ::Gitlab::Git::Repository::REV_LIST_COMMIT_LIMIT, dynamic_timeout: @time_left)
return false unless new_lfs_pointers.present?
@@ -20,6 +24,7 @@ module Gitlab
existing_count != new_lfs_pointers.count
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/checks/matching_merge_request.rb b/lib/gitlab/checks/matching_merge_request.rb
index 849848515da..71361b12d07 100644
--- a/lib/gitlab/checks/matching_merge_request.rb
+++ b/lib/gitlab/checks/matching_merge_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class MatchingMergeRequest
@@ -7,12 +9,14 @@ module Gitlab
@project = project
end
+ # rubocop: disable CodeReuse/ActiveRecord
def match?
@project.merge_requests
.with_state(:locked)
.where(in_progress_merge_commit_sha: @newrev, target_branch: @branch_name)
.exists?
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/checks/post_push_message.rb b/lib/gitlab/checks/post_push_message.rb
index 473c0385b34..492dbb5a596 100644
--- a/lib/gitlab/checks/post_push_message.rb
+++ b/lib/gitlab/checks/post_push_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class PostPushMessage
diff --git a/lib/gitlab/checks/project_created.rb b/lib/gitlab/checks/project_created.rb
index cec270d6a58..0058a402a62 100644
--- a/lib/gitlab/checks/project_created.rb
+++ b/lib/gitlab/checks/project_created.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class ProjectCreated < PostPushMessage
diff --git a/lib/gitlab/checks/project_moved.rb b/lib/gitlab/checks/project_moved.rb
index 3a197078d08..cb3b7acaaad 100644
--- a/lib/gitlab/checks/project_moved.rb
+++ b/lib/gitlab/checks/project_moved.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class ProjectMoved < PostPushMessage
diff --git a/lib/gitlab/checks/timed_logger.rb b/lib/gitlab/checks/timed_logger.rb
new file mode 100644
index 00000000000..f365e0a43f6
--- /dev/null
+++ b/lib/gitlab/checks/timed_logger.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Checks
+ class TimedLogger
+ TimeoutError = Class.new(StandardError)
+
+ attr_reader :start_time, :header, :log, :timeout
+
+ def initialize(start_time: Time.now, log: [], header: "", timeout:)
+ @start_time = start_time
+ @timeout = timeout
+ @header = header
+ @log = log
+ end
+
+ # Adds trace of method being tracked with
+ # the correspondent time it took to run it.
+ # We make use of the start default argument
+ # on unit tests related to this method
+ #
+ def log_timed(log_message, start = Time.now)
+ check_timeout_reached
+
+ timed = true
+
+ yield
+
+ append_message(log_message + time_suffix_message(start: start))
+ rescue GRPC::DeadlineExceeded, TimeoutError
+ args = { cancelled: true }
+ args[:start] = start if timed
+
+ append_message(log_message + time_suffix_message(args))
+
+ raise TimeoutError
+ end
+
+ def check_timeout_reached
+ return unless time_expired?
+
+ raise TimeoutError
+ end
+
+ def time_left
+ (start_time + timeout.seconds) - Time.now
+ end
+
+ def full_message
+ header + log.join("\n")
+ end
+
+ # We always want to append in-place on the log
+ def append_message(message)
+ log << message
+ end
+
+ private
+
+ def time_expired?
+ time_left <= 0
+ end
+
+ def time_suffix_message(cancelled: false, start: nil)
+ return " (#{elapsed_time(start)}ms)" unless cancelled
+
+ if start
+ " (cancelled after #{elapsed_time(start)}ms)"
+ else
+ " (cancelled)"
+ end
+ end
+
+ def elapsed_time(start)
+ to_ms(Time.now - start)
+ end
+
+ def to_ms(elapsed)
+ (elapsed.to_f * 1000).round(2)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index e780f8c646b..974b5ad6877 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# ANSI color library
#
# Implementation per http://en.wikipedia.org/wiki/ANSI_escape_code
@@ -265,7 +267,7 @@ module Gitlab
def reset_state
@offset = 0
@n_open_tags = 0
- @out = ''
+ @out = +''
reset
end
diff --git a/lib/gitlab/ci/build/artifacts/adapters/gzip_stream.rb b/lib/gitlab/ci/build/artifacts/adapters/gzip_stream.rb
new file mode 100644
index 00000000000..25a82086676
--- /dev/null
+++ b/lib/gitlab/ci/build/artifacts/adapters/gzip_stream.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Artifacts
+ module Adapters
+ class GzipStream
+ attr_reader :stream
+
+ InvalidStreamError = Class.new(StandardError)
+
+ def initialize(stream)
+ raise InvalidStreamError, "Stream is required" unless stream
+
+ @stream = stream
+ end
+
+ def each_blob
+ stream.seek(0)
+
+ until stream.eof?
+ gzip(stream) do |gz|
+ yield gz.read, gz.orig_name
+ unused = gz.unused&.length.to_i
+ # pos has already reached to EOF at the moment
+ # We rewind the pos to the top of unused files
+ # to read next gzip stream, to support multistream archives
+ # https://golang.org/src/compress/gzip/gunzip.go#L117
+ stream.seek(-unused, IO::SEEK_CUR)
+ end
+ end
+ end
+
+ private
+
+ def gzip(stream, &block)
+ gz = Zlib::GzipReader.new(stream)
+ yield(gz)
+ rescue Zlib::Error => e
+ raise InvalidStreamError, e.message
+ ensure
+ gz&.finish
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/artifacts/adapters/raw_stream.rb b/lib/gitlab/ci/build/artifacts/adapters/raw_stream.rb
new file mode 100644
index 00000000000..cf37d700991
--- /dev/null
+++ b/lib/gitlab/ci/build/artifacts/adapters/raw_stream.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Artifacts
+ module Adapters
+ class RawStream
+ attr_reader :stream
+
+ InvalidStreamError = Class.new(StandardError)
+
+ def initialize(stream)
+ raise InvalidStreamError, "Stream is required" unless stream
+
+ @stream = stream
+ end
+
+ def each_blob
+ stream.seek(0)
+
+ yield(stream.read, 'raw') unless stream.eof?
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/artifacts/gzip_file_adapter.rb b/lib/gitlab/ci/build/artifacts/gzip_file_adapter.rb
deleted file mode 100644
index 65f65cdce08..00000000000
--- a/lib/gitlab/ci/build/artifacts/gzip_file_adapter.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-module Gitlab
- module Ci
- module Build
- module Artifacts
- class GzipFileAdapter
- attr_reader :stream
-
- InvalidStreamError = Class.new(StandardError)
-
- def initialize(stream)
- raise InvalidStreamError, "Stream is required" unless stream
-
- @stream = stream
- end
-
- def each_blob
- stream.seek(0)
-
- until stream.eof?
- gzip(stream) do |gz|
- yield gz.read, gz.orig_name
- unused = gz.unused&.length.to_i
- # pos has already reached to EOF at the moment
- # We rewind the pos to the top of unused files
- # to read next gzip stream, to support multistream archives
- # https://golang.org/src/compress/gzip/gunzip.go#L117
- stream.seek(-unused, IO::SEEK_CUR)
- end
- end
- end
-
- private
-
- def gzip(stream, &block)
- gz = Zlib::GzipReader.new(stream)
- yield(gz)
- rescue Zlib::Error => e
- raise InvalidStreamError, e.message
- ensure
- gz&.finish
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
index 375d8bc1ff5..08dac756cc1 100644
--- a/lib/gitlab/ci/build/artifacts/metadata.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'zlib'
require 'json'
@@ -59,9 +61,12 @@ module Gitlab
until gz.eof?
begin
- path = read_string(gz).force_encoding('UTF-8')
- meta = read_string(gz).force_encoding('UTF-8')
+ path = read_string(gz)&.force_encoding('UTF-8')
+ meta = read_string(gz)&.force_encoding('UTF-8')
+ # We might hit an EOF while reading either value, so we should
+ # abort if we don't get any data.
+ next unless path && meta
next unless path.valid_encoding? && meta.valid_encoding?
next unless path =~ match_pattern
next if path =~ INVALID_PATH_PATTERN
diff --git a/lib/gitlab/ci/build/artifacts/metadata/entry.rb b/lib/gitlab/ci/build/artifacts/metadata/entry.rb
index 428c0505808..d0a80518ae8 100644
--- a/lib/gitlab/ci/build/artifacts/metadata/entry.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata/entry.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
@@ -96,12 +98,14 @@ module Gitlab
blank_node? || @entries.include?(@path.to_s)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def total_size
descendant_pattern = /^#{Regexp.escape(@path.to_s)}/
entries.sum do |path, entry|
(entry[:size] if path =~ descendant_pattern).to_i
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def path
@path.to_s
diff --git a/lib/gitlab/ci/build/artifacts/path.rb b/lib/gitlab/ci/build/artifacts/path.rb
index 9cd9b36c5f8..65cd935afaa 100644
--- a/lib/gitlab/ci/build/artifacts/path.rb
+++ b/lib/gitlab/ci/build/artifacts/path.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/credentials/base.rb b/lib/gitlab/ci/build/credentials/base.rb
index 29a7a27c963..58adf6e506d 100644
--- a/lib/gitlab/ci/build/credentials/base.rb
+++ b/lib/gitlab/ci/build/credentials/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/credentials/factory.rb b/lib/gitlab/ci/build/credentials/factory.rb
index 2423aa8857d..fa805abb8bb 100644
--- a/lib/gitlab/ci/build/credentials/factory.rb
+++ b/lib/gitlab/ci/build/credentials/factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/credentials/registry.rb b/lib/gitlab/ci/build/credentials/registry.rb
index 55eafcaed10..1c8588d9913 100644
--- a/lib/gitlab/ci/build/credentials/registry.rb
+++ b/lib/gitlab/ci/build/credentials/registry.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/image.rb b/lib/gitlab/ci/build/image.rb
index c811f88f483..4dd932f61d4 100644
--- a/lib/gitlab/ci/build/image.rb
+++ b/lib/gitlab/ci/build/image.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/policy.rb b/lib/gitlab/ci/build/policy.rb
index d10cc7802d4..43c46ad74af 100644
--- a/lib/gitlab/ci/build/policy.rb
+++ b/lib/gitlab/ci/build/policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/policy/changes.rb b/lib/gitlab/ci/build/policy/changes.rb
new file mode 100644
index 00000000000..1663c875426
--- /dev/null
+++ b/lib/gitlab/ci/build/policy/changes.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Policy
+ class Changes < Policy::Specification
+ def initialize(globs)
+ @globs = Array(globs)
+ end
+
+ def satisfied_by?(pipeline, seed)
+ return true unless pipeline.branch_updated?
+
+ pipeline.modified_paths.any? do |path|
+ @globs.any? do |glob|
+ File.fnmatch?(glob, path, File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/policy/kubernetes.rb b/lib/gitlab/ci/build/policy/kubernetes.rb
index 782f6c4c0af..4c7dc947cd0 100644
--- a/lib/gitlab/ci/build/policy/kubernetes.rb
+++ b/lib/gitlab/ci/build/policy/kubernetes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/policy/refs.rb b/lib/gitlab/ci/build/policy/refs.rb
index 4aa5dc89f47..10934536536 100644
--- a/lib/gitlab/ci/build/policy/refs.rb
+++ b/lib/gitlab/ci/build/policy/refs.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/policy/specification.rb b/lib/gitlab/ci/build/policy/specification.rb
index f09ba42c074..ceb5210cfb5 100644
--- a/lib/gitlab/ci/build/policy/specification.rb
+++ b/lib/gitlab/ci/build/policy/specification.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/policy/variables.rb b/lib/gitlab/ci/build/policy/variables.rb
index 9d2a362b7d4..0698136166a 100644
--- a/lib/gitlab/ci/build/policy/variables.rb
+++ b/lib/gitlab/ci/build/policy/variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/build/step.rb b/lib/gitlab/ci/build/step.rb
index 0b1ebe4e048..d587c896712 100644
--- a/lib/gitlab/ci/build/step.rb
+++ b/lib/gitlab/ci/build/step.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Build
diff --git a/lib/gitlab/ci/charts.rb b/lib/gitlab/ci/charts.rb
index 46ed330dbbf..a4f01468e8e 100644
--- a/lib/gitlab/ci/charts.rb
+++ b/lib/gitlab/ci/charts.rb
@@ -1,13 +1,17 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Charts
module DailyInterval
+ # rubocop: disable CodeReuse/ActiveRecord
def grouped_count(query)
query
.group("DATE(#{::Ci::Pipeline.table_name}.created_at)")
.count(:created_at)
.transform_keys { |date| date.strftime(@format) } # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
+ # rubocop: enable CodeReuse/ActiveRecord
def interval_step
@interval_step ||= 1.day
@@ -15,6 +19,7 @@ module Gitlab
end
module MonthlyInterval
+ # rubocop: disable CodeReuse/ActiveRecord
def grouped_count(query)
if Gitlab::Database.postgresql?
query
@@ -27,6 +32,7 @@ module Gitlab
.count(:created_at)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def interval_step
@interval_step ||= 1.month
@@ -46,6 +52,7 @@ module Gitlab
collect
end
+ # rubocop: disable CodeReuse/ActiveRecord
def collect
query = project.pipelines
.where("? > #{::Ci::Pipeline.table_name}.created_at AND #{::Ci::Pipeline.table_name}.created_at > ?", @to, @from) # rubocop:disable GitlabSecurity/SqlInjection
@@ -64,6 +71,7 @@ module Gitlab
current += interval_step
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
class YearChart < Chart
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
index 66ac4a40616..2fb3c4582e7 100644
--- a/lib/gitlab/ci/config.rb
+++ b/lib/gitlab/ci/config.rb
@@ -1,15 +1,24 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
- ##
+ #
# Base GitLab CI Configuration facade
#
class Config
- # EE would override this and utilize opts argument
+ ConfigError = Class.new(StandardError)
+
def initialize(config, opts = {})
- @config = Loader.new(config).load!
+ @config = Config::Extendable
+ .new(build_config(config, opts))
+ .to_hash
@global = Entry::Global.new(@config)
@global.compose!
+ rescue Loader::FormatError,
+ Extendable::ExtensionError,
+ External::Processor::IncludeError => e
+ raise Config::ConfigError, e.message
end
def valid?
@@ -58,6 +67,24 @@ module Gitlab
def jobs
@global.jobs_value
end
+
+ private
+
+ def build_config(config, opts = {})
+ initial_config = Loader.new(config).load!
+ project = opts.fetch(:project, nil)
+
+ if project
+ process_external_files(initial_config, project, opts)
+ else
+ initial_config
+ end
+ end
+
+ def process_external_files(config, project, opts)
+ sha = opts.fetch(:sha) { project.repository.root_ref_sha }
+ Config::External::Processor.new(config, project, sha).perform
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb
index e80f9d2e452..ef5f25b42c0 100644
--- a/lib/gitlab/ci/config/entry/artifacts.rb
+++ b/lib/gitlab/ci/config/entry/artifacts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/attributable.rb b/lib/gitlab/ci/config/entry/attributable.rb
index 3e87a09704e..3c2e1df9b83 100644
--- a/lib/gitlab/ci/config/entry/attributable.rb
+++ b/lib/gitlab/ci/config/entry/attributable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/boolean.rb b/lib/gitlab/ci/config/entry/boolean.rb
index f3357f85b99..b9639c83075 100644
--- a/lib/gitlab/ci/config/entry/boolean.rb
+++ b/lib/gitlab/ci/config/entry/boolean.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/cache.rb b/lib/gitlab/ci/config/entry/cache.rb
index d7e09acbbf3..0a25057f482 100644
--- a/lib/gitlab/ci/config/entry/cache.rb
+++ b/lib/gitlab/ci/config/entry/cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/commands.rb b/lib/gitlab/ci/config/entry/commands.rb
index 9f66f11be9b..d9658291ebe 100644
--- a/lib/gitlab/ci/config/entry/commands.rb
+++ b/lib/gitlab/ci/config/entry/commands.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/configurable.rb b/lib/gitlab/ci/config/entry/configurable.rb
index 7cddd2c7b7e..4aabf0cfa31 100644
--- a/lib/gitlab/ci/config/entry/configurable.rb
+++ b/lib/gitlab/ci/config/entry/configurable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
@@ -24,6 +26,7 @@ module Gitlab
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def compose!(deps = nil)
return unless valid?
@@ -41,6 +44,7 @@ module Gitlab
entry.compose!(deps)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
class_methods do
def nodes
@@ -49,12 +53,14 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def entry(key, entry, metadata)
factory = Entry::Factory.new(entry)
.with(description: metadata[:description])
(@nodes ||= {}).merge!(key.to_sym => factory)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def helpers(*nodes)
nodes.each do |symbol|
diff --git a/lib/gitlab/ci/config/entry/coverage.rb b/lib/gitlab/ci/config/entry/coverage.rb
index 12a063059cb..690409ccf77 100644
--- a/lib/gitlab/ci/config/entry/coverage.rb
+++ b/lib/gitlab/ci/config/entry/coverage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/environment.rb b/lib/gitlab/ci/config/entry/environment.rb
index 0c1f9eb7cbf..07e9e1d3f67 100644
--- a/lib/gitlab/ci/config/entry/environment.rb
+++ b/lib/gitlab/ci/config/entry/environment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/factory.rb b/lib/gitlab/ci/config/entry/factory.rb
index 6be8288748f..85c9c3511a4 100644
--- a/lib/gitlab/ci/config/entry/factory.rb
+++ b/lib/gitlab/ci/config/entry/factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/global.rb b/lib/gitlab/ci/config/entry/global.rb
index a4ec8f0ff2f..eba203d9d06 100644
--- a/lib/gitlab/ci/config/entry/global.rb
+++ b/lib/gitlab/ci/config/entry/global.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
@@ -45,6 +47,7 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def compose_jobs!
factory = Entry::Factory.new(Entry::Jobs)
.value(@config.except(*self.class.nodes.keys))
@@ -53,6 +56,7 @@ module Gitlab
@entries[:jobs] = factory.create!
end
+ # rubocop: enable CodeReuse/ActiveRecord
def compose_deprecated_entries!
##
diff --git a/lib/gitlab/ci/config/entry/hidden.rb b/lib/gitlab/ci/config/entry/hidden.rb
index 6fc3aa385bc..dc0ede2a25f 100644
--- a/lib/gitlab/ci/config/entry/hidden.rb
+++ b/lib/gitlab/ci/config/entry/hidden.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/image.rb b/lib/gitlab/ci/config/entry/image.rb
index 2844be80a84..fc453b72fa5 100644
--- a/lib/gitlab/ci/config/entry/image.rb
+++ b/lib/gitlab/ci/config/entry/image.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 91aac6df4b1..c8cb3248fa7 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
@@ -9,9 +11,10 @@ module Gitlab
include Configurable
include Attributable
- ALLOWED_KEYS = %i[tags script only except type image services allow_failure
- type stage when artifacts cache dependencies before_script
- after_script variables environment coverage retry].freeze
+ ALLOWED_KEYS = %i[tags script only except type image services
+ allow_failure type stage when start_in artifacts cache
+ dependencies before_script after_script variables
+ environment coverage retry parallel extends].freeze
validations do
validates :config, allowed_keys: ALLOWED_KEYS
@@ -23,16 +26,19 @@ module Gitlab
with_options allow_nil: true do
validates :tags, array_of_strings: true
validates :allow_failure, boolean: true
- validates :retry, numericality: { only_integer: true,
- greater_than_or_equal_to: 0,
- less_than_or_equal_to: 2 }
+ validates :parallel, numericality: { only_integer: true,
+ greater_than_or_equal_to: 2,
+ less_than_or_equal_to: 50 }
validates :when,
- inclusion: { in: %w[on_success on_failure always manual],
+ inclusion: { in: %w[on_success on_failure always manual delayed],
message: 'should be on_success, on_failure, ' \
- 'always or manual' }
-
+ 'always, manual or delayed' }
validates :dependencies, array_of_strings: true
+ validates :extends, type: String
end
+
+ validates :start_in, duration: { limit: '1 day' }, if: :delayed?
+ validates :start_in, absence: true, unless: :delayed?
end
entry :before_script, Entry::Script,
@@ -72,16 +78,21 @@ module Gitlab
description: 'Artifacts configuration for this job.'
entry :environment, Entry::Environment,
- description: 'Environment configuration for this job.'
+ description: 'Environment configuration for this job.'
entry :coverage, Entry::Coverage,
- description: 'Coverage configuration for this job.'
+ description: 'Coverage configuration for this job.'
+
+ entry :retry, Entry::Retry,
+ description: 'Retry configuration for this job.'
helpers :before_script, :script, :stage, :type, :after_script,
:cache, :image, :services, :only, :except, :variables,
- :artifacts, :commands, :environment, :coverage, :retry
+ :artifacts, :commands, :environment, :coverage, :retry,
+ :parallel
- attributes :script, :tags, :allow_failure, :when, :dependencies, :retry
+ attributes :script, :tags, :allow_failure, :when, :dependencies,
+ :retry, :parallel, :extends, :start_in
def compose!(deps = nil)
super do
@@ -111,6 +122,10 @@ module Gitlab
self.when == 'manual'
end
+ def delayed?
+ self.when == 'delayed'
+ end
+
def ignored?
allow_failure.nil? ? manual_action? : allow_failure
end
@@ -145,7 +160,8 @@ module Gitlab
environment: environment_defined? ? environment_value : nil,
environment_name: environment_defined? ? environment_value[:name] : nil,
coverage: coverage_defined? ? coverage_value : nil,
- retry: retry_defined? ? retry_value.to_i : nil,
+ retry: retry_defined? ? retry_value : nil,
+ parallel: parallel_defined? ? parallel_value.to_i : nil,
artifacts: artifacts_value,
after_script: after_script_value,
ignore: ignored? }
diff --git a/lib/gitlab/ci/config/entry/jobs.rb b/lib/gitlab/ci/config/entry/jobs.rb
index 5671a09480b..1535b108000 100644
--- a/lib/gitlab/ci/config/entry/jobs.rb
+++ b/lib/gitlab/ci/config/entry/jobs.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
@@ -26,6 +28,7 @@ module Gitlab
name.to_s.start_with?('.')
end
+ # rubocop: disable CodeReuse/ActiveRecord
def compose!(deps = nil)
super do
@config.each do |name, config|
@@ -45,6 +48,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/ci/config/entry/key.rb b/lib/gitlab/ci/config/entry/key.rb
index f27ad0a7759..963b200c7bb 100644
--- a/lib/gitlab/ci/config/entry/key.rb
+++ b/lib/gitlab/ci/config/entry/key.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb b/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb
index a78a85397bd..4043629dea9 100644
--- a/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb
+++ b/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
@@ -11,6 +13,15 @@ module Gitlab
false
end
+ def validate_duration_limit(value, limit)
+ return false unless value.is_a?(String)
+
+ ChronicDuration.parse(value).second.from_now <
+ ChronicDuration.parse(limit).second.from_now
+ rescue ChronicDuration::DurationParseError
+ false
+ end
+
def validate_array_of_strings(values)
values.is_a?(Array) && values.all? { |value| validate_string(value) }
end
diff --git a/lib/gitlab/ci/config/entry/node.rb b/lib/gitlab/ci/config/entry/node.rb
index 26505c91be3..347089722e4 100644
--- a/lib/gitlab/ci/config/entry/node.rb
+++ b/lib/gitlab/ci/config/entry/node.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/paths.rb b/lib/gitlab/ci/config/entry/paths.rb
index 68dad161149..9580b5e2e7f 100644
--- a/lib/gitlab/ci/config/entry/paths.rb
+++ b/lib/gitlab/ci/config/entry/paths.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/policy.rb b/lib/gitlab/ci/config/entry/policy.rb
index 09e8e52b60f..0535d7c1a1a 100644
--- a/lib/gitlab/ci/config/entry/policy.rb
+++ b/lib/gitlab/ci/config/entry/policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
@@ -25,17 +27,19 @@ module Gitlab
include Entry::Validatable
include Entry::Attributable
- attributes :refs, :kubernetes, :variables
+ ALLOWED_KEYS = %i[refs kubernetes variables changes].freeze
+ attributes :refs, :kubernetes, :variables, :changes
validations do
validates :config, presence: true
- validates :config, allowed_keys: %i[refs kubernetes variables]
+ validates :config, allowed_keys: ALLOWED_KEYS
validate :variables_expressions_syntax
with_options allow_nil: true do
validates :refs, array_of_strings_or_regexps: true
validates :kubernetes, allowed_values: %w[active]
validates :variables, array_of_strings: true
+ validates :changes, array_of_strings: true
end
def variables_expressions_syntax
diff --git a/lib/gitlab/ci/config/entry/reports.rb b/lib/gitlab/ci/config/entry/reports.rb
index 5963f3eb90c..3ac2a6fa777 100644
--- a/lib/gitlab/ci/config/entry/reports.rb
+++ b/lib/gitlab/ci/config/entry/reports.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
@@ -9,7 +11,7 @@ module Gitlab
include Validatable
include Attributable
- ALLOWED_KEYS = %i[junit].freeze
+ ALLOWED_KEYS = %i[junit codequality sast dependency_scanning container_scanning dast performance license_management].freeze
attributes ALLOWED_KEYS
@@ -19,6 +21,13 @@ module Gitlab
with_options allow_nil: true do
validates :junit, array_of_strings_or_string: true
+ validates :codequality, array_of_strings_or_string: true
+ validates :sast, array_of_strings_or_string: true
+ validates :dependency_scanning, array_of_strings_or_string: true
+ validates :container_scanning, array_of_strings_or_string: true
+ validates :dast, array_of_strings_or_string: true
+ validates :performance, array_of_strings_or_string: true
+ validates :license_management, array_of_strings_or_string: true
end
end
diff --git a/lib/gitlab/ci/config/entry/retry.rb b/lib/gitlab/ci/config/entry/retry.rb
new file mode 100644
index 00000000000..e39cc5de229
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/retry.rb
@@ -0,0 +1,90 @@
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a retry config for a job.
+ #
+ class Retry < Simplifiable
+ strategy :SimpleRetry, if: -> (config) { config.is_a?(Integer) }
+ strategy :FullRetry, if: -> (config) { config.is_a?(Hash) }
+
+ class SimpleRetry < Entry::Node
+ include Entry::Validatable
+
+ validations do
+ validates :config, numericality: { only_integer: true,
+ greater_than_or_equal_to: 0,
+ less_than_or_equal_to: 2 }
+ end
+
+ def value
+ {
+ max: config
+ }
+ end
+
+ def location
+ 'retry'
+ end
+ end
+
+ class FullRetry < Entry::Node
+ include Entry::Validatable
+ include Entry::Attributable
+
+ ALLOWED_KEYS = %i[max when].freeze
+ attributes :max, :when
+
+ validations do
+ validates :config, allowed_keys: ALLOWED_KEYS
+
+ with_options allow_nil: true do
+ validates :max, numericality: { only_integer: true,
+ greater_than_or_equal_to: 0,
+ less_than_or_equal_to: 2 }
+
+ validates :when, array_of_strings_or_string: true
+ validates :when,
+ allowed_array_values: { in: FullRetry.possible_retry_when_values },
+ if: -> (config) { config.when.is_a?(Array) }
+ validates :when,
+ inclusion: { in: FullRetry.possible_retry_when_values },
+ if: -> (config) { config.when.is_a?(String) }
+ end
+ end
+
+ def self.possible_retry_when_values
+ @possible_retry_when_values ||= ::Ci::Build.failure_reasons.keys.map(&:to_s) + ['always']
+ end
+
+ def value
+ super.tap do |config|
+ # make sure that `when` is an array, because we allow it to
+ # be passed as a String in config for simplicity
+ config[:when] = Array.wrap(config[:when]) if config[:when]
+ end
+ end
+
+ def location
+ 'retry'
+ end
+ end
+
+ class UnknownStrategy < Entry::Node
+ def errors
+ ["#{location} has to be either an integer or a hash"]
+ end
+
+ def location
+ 'retry config'
+ end
+ end
+
+ def self.default
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/script.rb b/lib/gitlab/ci/config/entry/script.rb
index 29ecd9995ca..f7d39e5cf55 100644
--- a/lib/gitlab/ci/config/entry/script.rb
+++ b/lib/gitlab/ci/config/entry/script.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/service.rb b/lib/gitlab/ci/config/entry/service.rb
index 3e2ebcff31a..47bf9205147 100644
--- a/lib/gitlab/ci/config/entry/service.rb
+++ b/lib/gitlab/ci/config/entry/service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/services.rb b/lib/gitlab/ci/config/entry/services.rb
index 0066894e069..bdf7f80f382 100644
--- a/lib/gitlab/ci/config/entry/services.rb
+++ b/lib/gitlab/ci/config/entry/services.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/simplifiable.rb b/lib/gitlab/ci/config/entry/simplifiable.rb
index 12764629686..9961bbfaa40 100644
--- a/lib/gitlab/ci/config/entry/simplifiable.rb
+++ b/lib/gitlab/ci/config/entry/simplifiable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/stage.rb b/lib/gitlab/ci/config/entry/stage.rb
index b7afaba1de8..65ab5953131 100644
--- a/lib/gitlab/ci/config/entry/stage.rb
+++ b/lib/gitlab/ci/config/entry/stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/stages.rb b/lib/gitlab/ci/config/entry/stages.rb
index ec187bd3732..ab184246d29 100644
--- a/lib/gitlab/ci/config/entry/stages.rb
+++ b/lib/gitlab/ci/config/entry/stages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/undefined.rb b/lib/gitlab/ci/config/entry/undefined.rb
index 1171ac10f22..77dcfa88170 100644
--- a/lib/gitlab/ci/config/entry/undefined.rb
+++ b/lib/gitlab/ci/config/entry/undefined.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/unspecified.rb b/lib/gitlab/ci/config/entry/unspecified.rb
index fbb2551e870..bab32489d2f 100644
--- a/lib/gitlab/ci/config/entry/unspecified.rb
+++ b/lib/gitlab/ci/config/entry/unspecified.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/validatable.rb b/lib/gitlab/ci/config/entry/validatable.rb
index e45787773a8..08a6593c980 100644
--- a/lib/gitlab/ci/config/entry/validatable.rb
+++ b/lib/gitlab/ci/config/entry/validatable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/validator.rb b/lib/gitlab/ci/config/entry/validator.rb
index 2df23a3edcd..33ffdd3a95d 100644
--- a/lib/gitlab/ci/config/entry/validator.rb
+++ b/lib/gitlab/ci/config/entry/validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/entry/validators.rb b/lib/gitlab/ci/config/entry/validators.rb
index b3c889ee92f..a1d552fb2e5 100644
--- a/lib/gitlab/ci/config/entry/validators.rb
+++ b/lib/gitlab/ci/config/entry/validators.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
@@ -5,11 +7,11 @@ module Gitlab
module Validators
class AllowedKeysValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- unknown_keys = record.config.try(:keys).to_a - options[:in]
+ unknown_keys = value.try(:keys).to_a - options[:in]
if unknown_keys.any?
- record.errors.add(:config, 'contains unknown keys: ' +
- unknown_keys.join(', '))
+ record.errors.add(attribute, "contains unknown keys: " +
+ unknown_keys.join(', '))
end
end
end
@@ -22,6 +24,16 @@ module Gitlab
end
end
+ class AllowedArrayValuesValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ unkown_values = value - options[:in]
+ unless unkown_values.empty?
+ record.errors.add(attribute, "contains unknown values: " +
+ unkown_values.join(', '))
+ end
+ end
+ end
+
class ArrayOfStringsValidator < ActiveModel::EachValidator
include LegacyValidationHelpers
@@ -49,6 +61,12 @@ module Gitlab
unless validate_duration(value)
record.errors.add(attribute, 'should be a duration')
end
+
+ if options[:limit]
+ unless validate_duration_limit(value, options[:limit])
+ record.errors.add(attribute, 'should not exceed the limit')
+ end
+ end
end
end
@@ -60,6 +78,14 @@ module Gitlab
end
end
+ class HashOrIntegerValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ unless value.is_a?(Hash) || value.is_a?(Integer)
+ record.errors.add(attribute, 'should be a hash or an integer')
+ end
+ end
+ end
+
class KeyValidator < ActiveModel::EachValidator
include LegacyValidationHelpers
diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb
index 8acab605c91..6fd3cec2f5f 100644
--- a/lib/gitlab/ci/config/entry/variables.rb
+++ b/lib/gitlab/ci/config/entry/variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/extendable.rb b/lib/gitlab/ci/config/extendable.rb
new file mode 100644
index 00000000000..a43901c69fe
--- /dev/null
+++ b/lib/gitlab/ci/config/extendable.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ class Extendable
+ include Enumerable
+
+ ExtensionError = Class.new(StandardError)
+
+ def initialize(hash)
+ @hash = hash.to_h.deep_dup
+
+ each { |entry| entry.extend! if entry.extensible? }
+ end
+
+ def each
+ @hash.each_key do |key|
+ yield Extendable::Entry.new(key, @hash)
+ end
+ end
+
+ def to_hash
+ @hash.to_h
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/extendable/entry.rb b/lib/gitlab/ci/config/extendable/entry.rb
new file mode 100644
index 00000000000..7793db09d33
--- /dev/null
+++ b/lib/gitlab/ci/config/extendable/entry.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ class Extendable
+ class Entry
+ InvalidExtensionError = Class.new(Extendable::ExtensionError)
+ CircularDependencyError = Class.new(Extendable::ExtensionError)
+ NestingTooDeepError = Class.new(Extendable::ExtensionError)
+
+ MAX_NESTING_LEVELS = 10
+
+ attr_reader :key
+
+ def initialize(key, context, parent = nil)
+ @key = key
+ @context = context
+ @parent = parent
+
+ unless @context.key?(@key)
+ raise StandardError, 'Invalid entry key!'
+ end
+ end
+
+ def extensible?
+ value.is_a?(Hash) && value.key?(:extends)
+ end
+
+ def value
+ @value ||= @context.fetch(@key)
+ end
+
+ def base_hash!
+ @base ||= Extendable::Entry
+ .new(extends_key, @context, self)
+ .extend!
+ end
+
+ def extends_key
+ value.fetch(:extends).to_s.to_sym if extensible?
+ end
+
+ def ancestors
+ @ancestors ||= Array(@parent&.ancestors) + Array(@parent&.key)
+ end
+
+ def extend!
+ return value unless extensible?
+
+ if unknown_extension?
+ raise Entry::InvalidExtensionError,
+ "#{key}: unknown key in `extends`"
+ end
+
+ if invalid_base?
+ raise Entry::InvalidExtensionError,
+ "#{key}: invalid base hash in `extends`"
+ end
+
+ if nesting_too_deep?
+ raise Entry::NestingTooDeepError,
+ "#{key}: nesting too deep in `extends`"
+ end
+
+ if circular_dependency?
+ raise Entry::CircularDependencyError,
+ "#{key}: circular dependency detected in `extends`"
+ end
+
+ @context[key] = base_hash!.deep_merge(value)
+ end
+
+ private
+
+ def nesting_too_deep?
+ ancestors.count > MAX_NESTING_LEVELS
+ end
+
+ def circular_dependency?
+ ancestors.include?(key)
+ end
+
+ def unknown_extension?
+ !@context.key?(extends_key)
+ end
+
+ def invalid_base?
+ !@context[extends_key].is_a?(Hash)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/file/base.rb b/lib/gitlab/ci/config/external/file/base.rb
new file mode 100644
index 00000000000..15ca47ef60e
--- /dev/null
+++ b/lib/gitlab/ci/config/external/file/base.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ module File
+ class Base
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :location, :opts, :errors
+
+ YAML_WHITELIST_EXTENSION = /.+\.(yml|yaml)$/i.freeze
+
+ def initialize(location, opts = {})
+ @location = location
+ @opts = opts
+ @errors = []
+
+ validate!
+ end
+
+ def invalid_extension?
+ !::File.basename(location).match(YAML_WHITELIST_EXTENSION)
+ end
+
+ def valid?
+ errors.none?
+ end
+
+ def error_message
+ errors.first
+ end
+
+ def content
+ raise NotImplementedError, 'subclass must implement fetching raw content'
+ end
+
+ def to_hash
+ @hash ||= Ci::Config::Loader.new(content).load!
+ rescue Ci::Config::Loader::FormatError
+ nil
+ end
+
+ protected
+
+ def validate!
+ validate_location!
+ validate_content! if errors.none?
+ validate_hash! if errors.none?
+ end
+
+ def validate_location!
+ if invalid_extension?
+ errors.push("Included file `#{location}` does not have YAML extension!")
+ end
+ end
+
+ def validate_content!
+ if content.blank?
+ errors.push("Included file `#{location}` is empty or does not exist!")
+ end
+ end
+
+ def validate_hash!
+ if to_hash.blank?
+ errors.push("Included file `#{location}` does not have valid YAML syntax!")
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/file/local.rb b/lib/gitlab/ci/config/external/file/local.rb
new file mode 100644
index 00000000000..2a256aff65c
--- /dev/null
+++ b/lib/gitlab/ci/config/external/file/local.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ module File
+ class Local < Base
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :project, :sha
+
+ def initialize(location, opts = {})
+ @project = opts.fetch(:project)
+ @sha = opts.fetch(:sha)
+
+ super
+ end
+
+ def content
+ strong_memoize(:content) { fetch_local_content }
+ end
+
+ private
+
+ def validate_content!
+ if content.nil?
+ errors.push("Local file `#{location}` does not exist!")
+ elsif content.blank?
+ errors.push("Local file `#{location}` is empty!")
+ end
+ end
+
+ def fetch_local_content
+ project.repository.blob_data_at(sha, location)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/file/remote.rb b/lib/gitlab/ci/config/external/file/remote.rb
new file mode 100644
index 00000000000..86fa5ad8800
--- /dev/null
+++ b/lib/gitlab/ci/config/external/file/remote.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ module File
+ class Remote < Base
+ include Gitlab::Utils::StrongMemoize
+
+ def content
+ strong_memoize(:content) { fetch_remote_content }
+ end
+
+ private
+
+ def validate_location!
+ super
+
+ unless ::Gitlab::UrlSanitizer.valid?(location)
+ errors.push("Remote file `#{location}` does not have a valid address!")
+ end
+ end
+
+ def fetch_remote_content
+ begin
+ response = Gitlab::HTTP.get(location)
+ rescue SocketError
+ errors.push("Remote file `#{location}` could not be fetched because of a socket error!")
+ rescue Timeout::Error
+ errors.push("Remote file `#{location}` could not be fetched because of a timeout error!")
+ rescue Gitlab::HTTP::Error
+ errors.push("Remote file `#{location}` could not be fetched because of HTTP error!")
+ rescue Gitlab::HTTP::BlockedUrlError => e
+ errors.push("Remote file could not be fetched because #{e}!")
+ end
+
+ if response&.code.to_i >= 400
+ errors.push("Remote file `#{location}` could not be fetched because of HTTP code `#{response.code}` error!")
+ end
+
+ response.to_s if errors.none?
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/mapper.rb b/lib/gitlab/ci/config/external/mapper.rb
new file mode 100644
index 00000000000..def3563e505
--- /dev/null
+++ b/lib/gitlab/ci/config/external/mapper.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Mapper
+ def initialize(values, project, sha)
+ @locations = Array(values.fetch(:include, []))
+ @project = project
+ @sha = sha
+ end
+
+ def process
+ locations.map { |location| build_external_file(location) }
+ end
+
+ private
+
+ attr_reader :locations, :project, :sha
+
+ def build_external_file(location)
+ if ::Gitlab::UrlSanitizer.valid?(location)
+ External::File::Remote.new(location)
+ else
+ External::File::Local.new(location, project: project, sha: sha)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/processor.rb b/lib/gitlab/ci/config/external/processor.rb
new file mode 100644
index 00000000000..eae0bdeb644
--- /dev/null
+++ b/lib/gitlab/ci/config/external/processor.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Processor
+ IncludeError = Class.new(StandardError)
+
+ def initialize(values, project, sha)
+ @values = values
+ @external_files = External::Mapper.new(values, project, sha).process
+ @content = {}
+ end
+
+ def perform
+ return @values if @external_files.empty?
+
+ validate_external_files!
+ merge_external_files!
+ append_inline_content!
+ remove_include_keyword!
+ end
+
+ private
+
+ def validate_external_files!
+ @external_files.each do |file|
+ raise IncludeError, file.error_message unless file.valid?
+ end
+ end
+
+ def merge_external_files!
+ @external_files.each do |file|
+ @content.deep_merge!(file.to_hash)
+ end
+ end
+
+ def append_inline_content!
+ @content.deep_merge!(@values)
+ end
+
+ def remove_include_keyword!
+ @content.tap { @content.delete(:include) }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/loader.rb b/lib/gitlab/ci/config/loader.rb
index 141d2714cb6..b4c491e84a6 100644
--- a/lib/gitlab/ci/config/loader.rb
+++ b/lib/gitlab/ci/config/loader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Config
diff --git a/lib/gitlab/ci/config/normalizer.rb b/lib/gitlab/ci/config/normalizer.rb
new file mode 100644
index 00000000000..b7743bd2090
--- /dev/null
+++ b/lib/gitlab/ci/config/normalizer.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ class Normalizer
+ def initialize(jobs_config)
+ @jobs_config = jobs_config
+ end
+
+ def normalize_jobs
+ extract_parallelized_jobs!
+ return @jobs_config if @parallelized_jobs.empty?
+
+ parallelized_config = parallelize_jobs
+ parallelize_dependencies(parallelized_config)
+ end
+
+ private
+
+ def extract_parallelized_jobs!
+ @parallelized_jobs = {}
+
+ @jobs_config.each do |job_name, config|
+ if config[:parallel]
+ @parallelized_jobs[job_name] = self.class.parallelize_job_names(job_name, config[:parallel])
+ end
+ end
+
+ @parallelized_jobs
+ end
+
+ def parallelize_jobs
+ @jobs_config.each_with_object({}) do |(job_name, config), hash|
+ if @parallelized_jobs.key?(job_name)
+ @parallelized_jobs[job_name].each { |name, index| hash[name.to_sym] = config.merge(name: name, instance: index) }
+ else
+ hash[job_name] = config
+ end
+
+ hash
+ end
+ end
+
+ def parallelize_dependencies(parallelized_config)
+ parallelized_job_names = @parallelized_jobs.keys.map(&:to_s)
+ parallelized_config.each_with_object({}) do |(job_name, config), hash|
+ if config[:dependencies] && (intersection = config[:dependencies] & parallelized_job_names).any?
+ deps = intersection.map { |dep| @parallelized_jobs[dep.to_sym].map(&:first) }.flatten
+ hash[job_name] = config.merge(dependencies: deps)
+ else
+ hash[job_name] = config
+ end
+
+ hash
+ end
+ end
+
+ def self.parallelize_job_names(name, total)
+ Array.new(total) { |index| ["#{name} #{index + 1}/#{total}", index + 1] }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/cron_parser.rb b/lib/gitlab/ci/cron_parser.rb
index 73f36735e35..b1db9084662 100644
--- a/lib/gitlab/ci/cron_parser.rb
+++ b/lib/gitlab/ci/cron_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class CronParser
diff --git a/lib/gitlab/ci/mask_secret.rb b/lib/gitlab/ci/mask_secret.rb
index 0daddaa638c..58d55b1bd6f 100644
--- a/lib/gitlab/ci/mask_secret.rb
+++ b/lib/gitlab/ci/mask_secret.rb
@@ -1,9 +1,13 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci::MaskSecret
class << self
def mask!(value, token)
return value unless value.present? && token.present?
+ # We assume 'value' must be mutable, given
+ # that frozen string is enabled.
value.gsub!(token, 'x' * token.length)
value
end
diff --git a/lib/gitlab/ci/model.rb b/lib/gitlab/ci/model.rb
index 3994a50772b..fbdb84c0522 100644
--- a/lib/gitlab/ci/model.rb
+++ b/lib/gitlab/ci/model.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Model
diff --git a/lib/gitlab/ci/parsers.rb b/lib/gitlab/ci/parsers.rb
deleted file mode 100644
index a4eccc08dfc..00000000000
--- a/lib/gitlab/ci/parsers.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Gitlab
- module Ci
- module Parsers
- def self.fabricate!(file_type)
- "Gitlab::Ci::Parsers::#{file_type.classify}".constantize.new
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/parsers/junit.rb b/lib/gitlab/ci/parsers/junit.rb
deleted file mode 100644
index 3c4668ec13b..00000000000
--- a/lib/gitlab/ci/parsers/junit.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-module Gitlab
- module Ci
- module Parsers
- class Junit
- attr_reader :data
-
- JunitParserError = Class.new(StandardError)
-
- def parse!(xml_data, test_suite)
- @data = Hash.from_xml(xml_data)
-
- each_suite do |testcases|
- testcases.each do |testcase|
- test_case = create_test_case(testcase)
- test_suite.add_test_case(test_case)
- end
- end
- rescue REXML::ParseException => e
- raise JunitParserError, "XML parsing failed: #{e.message}"
- rescue => e
- raise JunitParserError, "JUnit parsing failed: #{e.message}"
- end
-
- private
-
- def each_suite
- testsuites.each do |testsuite|
- yield testcases(testsuite)
- end
- end
-
- def testsuites
- if data['testsuites']
- data['testsuites']['testsuite']
- else
- [data['testsuite']]
- end
- end
-
- def testcases(testsuite)
- if testsuite['testcase'].is_a?(Array)
- testsuite['testcase']
- else
- [testsuite['testcase']]
- end
- end
-
- def create_test_case(data)
- if data['failure']
- status = ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED
- system_output = data['failure']
- else
- status = ::Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS
- system_output = nil
- end
-
- ::Gitlab::Ci::Reports::TestCase.new(
- classname: data['classname'],
- name: data['name'],
- file: data['file'],
- execution_time: data['time'],
- status: status,
- system_output: system_output
- )
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/parsers/test.rb b/lib/gitlab/ci/parsers/test.rb
new file mode 100644
index 00000000000..c6bc9662b07
--- /dev/null
+++ b/lib/gitlab/ci/parsers/test.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Parsers
+ module Test
+ ParserNotFoundError = Class.new(StandardError)
+
+ PARSERS = {
+ junit: ::Gitlab::Ci::Parsers::Test::Junit
+ }.freeze
+
+ def self.fabricate!(file_type)
+ PARSERS.fetch(file_type.to_sym).new
+ rescue KeyError
+ raise ParserNotFoundError, "Cannot find any parser matching file type '#{file_type}'"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/parsers/test/junit.rb b/lib/gitlab/ci/parsers/test/junit.rb
new file mode 100644
index 00000000000..2791730fd26
--- /dev/null
+++ b/lib/gitlab/ci/parsers/test/junit.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Parsers
+ module Test
+ class Junit
+ JunitParserError = Class.new(StandardError)
+
+ def parse!(xml_data, test_suite)
+ root = Hash.from_xml(xml_data)
+
+ all_cases(root) do |test_case|
+ test_case = create_test_case(test_case)
+ test_suite.add_test_case(test_case)
+ end
+ rescue Nokogiri::XML::SyntaxError
+ raise JunitParserError, "XML parsing failed"
+ rescue
+ raise JunitParserError, "JUnit parsing failed"
+ end
+
+ private
+
+ def all_cases(root, parent = nil, &blk)
+ return unless root.present?
+
+ [root].flatten.compact.map do |node|
+ next unless node.is_a?(Hash)
+
+ # we allow only one top-level 'testsuites'
+ all_cases(node['testsuites'], root, &blk) unless parent
+
+ # we require at least one level of testsuites or testsuite
+ each_case(node['testcase'], &blk) if parent
+
+ # we allow multiple nested 'testsuite' (eg. PHPUnit)
+ all_cases(node['testsuite'], root, &blk)
+ end
+ end
+
+ def each_case(testcase, &blk)
+ return unless testcase.present?
+
+ [testcase].flatten.compact.map(&blk)
+ end
+
+ def create_test_case(data)
+ if data['failure']
+ status = ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED
+ system_output = data['failure']
+ else
+ status = ::Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS
+ system_output = nil
+ end
+
+ ::Gitlab::Ci::Reports::TestCase.new(
+ classname: data['classname'],
+ name: data['name'],
+ file: data['file'],
+ execution_time: data['time'],
+ status: status,
+ system_output: system_output
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/base.rb b/lib/gitlab/ci/pipeline/chain/base.rb
index efed19da21c..bab1c73e2f1 100644
--- a/lib/gitlab/ci/pipeline/chain/base.rb
+++ b/lib/gitlab/ci/pipeline/chain/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/build.rb b/lib/gitlab/ci/pipeline/chain/build.rb
index b5eb0cfa2f0..b445a872b3d 100644
--- a/lib/gitlab/ci/pipeline/chain/build.rb
+++ b/lib/gitlab/ci/pipeline/chain/build.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index a53c80d34f7..05978804d92 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -1,4 +1,7 @@
-module Gitlab # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module Gitlab
module Ci
module Pipeline
module Chain
diff --git a/lib/gitlab/ci/pipeline/chain/create.rb b/lib/gitlab/ci/pipeline/chain/create.rb
index f4c8d5342c1..aa627bdb009 100644
--- a/lib/gitlab/ci/pipeline/chain/create.rb
+++ b/lib/gitlab/ci/pipeline/chain/create.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
@@ -6,20 +8,7 @@ module Gitlab
include Chain::Helpers
def perform!
- ::Ci::Pipeline.transaction do
- pipeline.save!
-
- ##
- # Create environments before the pipeline starts.
- #
- pipeline.builds.each do |build|
- if build.has_environment?
- project.environments.find_or_create_by(
- name: build.expanded_environment_name
- )
- end
- end
- end
+ pipeline.save!
rescue ActiveRecord::RecordInvalid => e
error("Failed to persist the pipeline: #{e}")
end
diff --git a/lib/gitlab/ci/pipeline/chain/helpers.rb b/lib/gitlab/ci/pipeline/chain/helpers.rb
index bf1380a1da9..6bb3a75291b 100644
--- a/lib/gitlab/ci/pipeline/chain/helpers.rb
+++ b/lib/gitlab/ci/pipeline/chain/helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/populate.rb b/lib/gitlab/ci/pipeline/chain/populate.rb
index f34c11ca3c2..633d3cd4f6b 100644
--- a/lib/gitlab/ci/pipeline/chain/populate.rb
+++ b/lib/gitlab/ci/pipeline/chain/populate.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/sequence.rb b/lib/gitlab/ci/pipeline/chain/sequence.rb
index e24630656d3..99780409085 100644
--- a/lib/gitlab/ci/pipeline/chain/sequence.rb
+++ b/lib/gitlab/ci/pipeline/chain/sequence.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/skip.rb b/lib/gitlab/ci/pipeline/chain/skip.rb
index 32cbb7ca6af..b9707d2f8f5 100644
--- a/lib/gitlab/ci/pipeline/chain/skip.rb
+++ b/lib/gitlab/ci/pipeline/chain/skip.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
index 13c6fedd831..ebd7e6e8289 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/validate/config.rb b/lib/gitlab/ci/pipeline/chain/validate/config.rb
index a3bd2a5a23a..28c38cc3d18 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/config.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/validate/repository.rb b/lib/gitlab/ci/pipeline/chain/validate/repository.rb
index 9699c24e5b6..d88851d8245 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/repository.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/duration.rb b/lib/gitlab/ci/pipeline/duration.rb
index 469fc094cc8..de24bbf688b 100644
--- a/lib/gitlab/ci/pipeline/duration.rb
+++ b/lib/gitlab/ci/pipeline/duration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
@@ -86,6 +88,7 @@ module Gitlab
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def from_pipeline(pipeline)
status = %w[success failed running canceled]
builds = pipeline.builds.latest
@@ -93,6 +96,7 @@ module Gitlab
from_builds(builds)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def from_builds(builds)
now = Time.now
@@ -134,9 +138,11 @@ module Gitlab
Period.new(previous.first, [previous.last, current.last].max)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def process_duration(periods)
periods.sum(&:duration)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/ci/pipeline/expression.rb b/lib/gitlab/ci/pipeline/expression.rb
index f57df7c5637..61d392121d8 100644
--- a/lib/gitlab/ci/pipeline/expression.rb
+++ b/lib/gitlab/ci/pipeline/expression.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/base.rb b/lib/gitlab/ci/pipeline/expression/lexeme/base.rb
index 047ab66e9b3..70c774416f6 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/base.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/equals.rb b/lib/gitlab/ci/pipeline/expression/lexeme/equals.rb
index 3a2f0c6924e..668e85f5b9e 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/equals.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/equals.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
index 10957598f76..cd17bc4d78b 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/null.rb b/lib/gitlab/ci/pipeline/expression/lexeme/null.rb
index a2778716924..be7258c201a 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/null.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/null.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/operator.rb b/lib/gitlab/ci/pipeline/expression/lexeme/operator.rb
index f640d0b5855..3ebceb92eb7 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/operator.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/operator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
index 9b239c29ea4..d7e6dacf068 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/string.rb b/lib/gitlab/ci/pipeline/expression/lexeme/string.rb
index 346c92dc51e..2db2bf011f1 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/string.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/string.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/value.rb b/lib/gitlab/ci/pipeline/expression/lexeme/value.rb
index f2611d65faf..ef9ddb6cae9 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/value.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/value.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/variable.rb b/lib/gitlab/ci/pipeline/expression/lexeme/variable.rb
index 37643c8ef53..85c0899e4f6 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/variable.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/variable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/lexer.rb b/lib/gitlab/ci/pipeline/expression/lexer.rb
index 4cacb1e62c9..f26542361a2 100644
--- a/lib/gitlab/ci/pipeline/expression/lexer.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/parser.rb b/lib/gitlab/ci/pipeline/expression/parser.rb
index 90f94d0b763..ed184309ab4 100644
--- a/lib/gitlab/ci/pipeline/expression/parser.rb
+++ b/lib/gitlab/ci/pipeline/expression/parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/statement.rb b/lib/gitlab/ci/pipeline/expression/statement.rb
index b36f1e0f865..b03611f756e 100644
--- a/lib/gitlab/ci/pipeline/expression/statement.rb
+++ b/lib/gitlab/ci/pipeline/expression/statement.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/expression/token.rb b/lib/gitlab/ci/pipeline/expression/token.rb
index 58211800b88..513d43f6fca 100644
--- a/lib/gitlab/ci/pipeline/expression/token.rb
+++ b/lib/gitlab/ci/pipeline/expression/token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/seed/base.rb b/lib/gitlab/ci/pipeline/seed/base.rb
index db9706924bb..1fd3a61017f 100644
--- a/lib/gitlab/ci/pipeline/seed/base.rb
+++ b/lib/gitlab/ci/pipeline/seed/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb
index 6980b0b7aff..ef738a93bfe 100644
--- a/lib/gitlab/ci/pipeline/seed/build.rb
+++ b/lib/gitlab/ci/pipeline/seed/build.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/pipeline/seed/stage.rb b/lib/gitlab/ci/pipeline/seed/stage.rb
index 2b58d9863a0..4775ff15581 100644
--- a/lib/gitlab/ci/pipeline/seed/stage.rb
+++ b/lib/gitlab/ci/pipeline/seed/stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Pipeline
diff --git a/lib/gitlab/ci/reports/test_case.rb b/lib/gitlab/ci/reports/test_case.rb
index b4d08ed257f..292e273a03a 100644
--- a/lib/gitlab/ci/reports/test_case.rb
+++ b/lib/gitlab/ci/reports/test_case.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Reports
diff --git a/lib/gitlab/ci/reports/test_reports.rb b/lib/gitlab/ci/reports/test_reports.rb
index c6e732e68eb..7397ff35d46 100644
--- a/lib/gitlab/ci/reports/test_reports.rb
+++ b/lib/gitlab/ci/reports/test_reports.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Reports
@@ -12,13 +14,17 @@ module Gitlab
test_suites[suite_name] ||= TestSuite.new(suite_name)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def total_time
test_suites.values.sum(&:total_time)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def total_count
test_suites.values.sum(&:total_count)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def total_status
if failed_count > 0 || error_count > 0
@@ -30,7 +36,9 @@ module Gitlab
TestCase::STATUS_TYPES.each do |status_type|
define_method("#{status_type}_count") do
+ # rubocop: disable CodeReuse/ActiveRecord
test_suites.values.sum { |suite| suite.public_send("#{status_type}_count") } # rubocop:disable GitlabSecurity/PublicSend
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/ci/reports/test_reports_comparer.rb b/lib/gitlab/ci/reports/test_reports_comparer.rb
index c0943f5a51a..11810bdc0a8 100644
--- a/lib/gitlab/ci/reports/test_reports_comparer.rb
+++ b/lib/gitlab/ci/reports/test_reports_comparer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Reports
@@ -29,7 +31,9 @@ module Gitlab
%w(total_count resolved_count failed_count).each do |method|
define_method(method) do
+ # rubocop: disable CodeReuse/ActiveRecord
suite_comparers.sum { |suite| suite.public_send(method) } # rubocop:disable GitlabSecurity/PublicSend
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/ci/reports/test_suite.rb b/lib/gitlab/ci/reports/test_suite.rb
index b722d0ba735..b0391160c15 100644
--- a/lib/gitlab/ci/reports/test_suite.rb
+++ b/lib/gitlab/ci/reports/test_suite.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Reports
@@ -21,9 +23,11 @@ module Gitlab
@total_time += test_case.execution_time
end
+ # rubocop: disable CodeReuse/ActiveRecord
def total_count
test_cases.values.sum(&:count)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def total_status
if failed_count > 0 || error_count > 0
diff --git a/lib/gitlab/ci/reports/test_suite_comparer.rb b/lib/gitlab/ci/reports/test_suite_comparer.rb
index 642aa593092..9cb7db5934c 100644
--- a/lib/gitlab/ci/reports/test_suite_comparer.rb
+++ b/lib/gitlab/ci/reports/test_suite_comparer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Reports
diff --git a/lib/gitlab/ci/status/build/action.rb b/lib/gitlab/ci/status/build/action.rb
index 6c9125647ad..45d9ba41e92 100644
--- a/lib/gitlab/ci/status/build/action.rb
+++ b/lib/gitlab/ci/status/build/action.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/cancelable.rb b/lib/gitlab/ci/status/build/cancelable.rb
index 024047d4983..43fb5cdbbe6 100644
--- a/lib/gitlab/ci/status/build/cancelable.rb
+++ b/lib/gitlab/ci/status/build/cancelable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/canceled.rb b/lib/gitlab/ci/status/build/canceled.rb
index c83e2734a73..0518b9e673d 100644
--- a/lib/gitlab/ci/status/build/canceled.rb
+++ b/lib/gitlab/ci/status/build/canceled.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/common.rb b/lib/gitlab/ci/status/build/common.rb
index c1fc70ac266..6a75ec5c37f 100644
--- a/lib/gitlab/ci/status/build/common.rb
+++ b/lib/gitlab/ci/status/build/common.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/created.rb b/lib/gitlab/ci/status/build/created.rb
index 5be8e9de425..780fea23123 100644
--- a/lib/gitlab/ci/status/build/created.rb
+++ b/lib/gitlab/ci/status/build/created.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/erased.rb b/lib/gitlab/ci/status/build/erased.rb
index 495227c2ffb..d74cfc1ee77 100644
--- a/lib/gitlab/ci/status/build/erased.rb
+++ b/lib/gitlab/ci/status/build/erased.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb
index 2b26ebb45a1..6e4bfe23f2b 100644
--- a/lib/gitlab/ci/status/build/factory.rb
+++ b/lib/gitlab/ci/status/build/factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
@@ -5,6 +7,7 @@ module Gitlab
class Factory < Status::Factory
def self.extended_statuses
[[Status::Build::Erased,
+ Status::Build::Scheduled,
Status::Build::Manual,
Status::Build::Canceled,
Status::Build::Created,
@@ -14,6 +17,7 @@ module Gitlab
Status::Build::Retryable],
[Status::Build::Failed],
[Status::Build::FailedAllowed,
+ Status::Build::Unschedule,
Status::Build::Play,
Status::Build::Stop],
[Status::Build::Action],
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index 703f0b9217b..d40454df737 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
@@ -10,9 +12,14 @@ module Gitlab
stuck_or_timeout_failure: 'stuck or timeout failure',
runner_system_failure: 'runner system failure',
missing_dependency_failure: 'missing dependency failure',
- runner_unsupported: 'unsupported runner'
+ runner_unsupported: 'unsupported runner',
+ stale_schedule: 'stale schedule',
+ job_execution_timeout: 'job execution timeout',
+ archived_failure: 'archived failure'
}.freeze
+ private_constant :REASONS
+
def status_tooltip
base_message
end
@@ -25,6 +32,10 @@ module Gitlab
build.failed?
end
+ def self.reasons
+ REASONS
+ end
+
private
def base_message
@@ -32,11 +43,11 @@ module Gitlab
end
def description
- "<br> (#{failure_reason_message})"
+ "- (#{failure_reason_message})"
end
def failure_reason_message
- REASONS.fetch(subject.failure_reason.to_sym)
+ self.class.reasons.fetch(subject.failure_reason.to_sym)
end
end
end
diff --git a/lib/gitlab/ci/status/build/failed_allowed.rb b/lib/gitlab/ci/status/build/failed_allowed.rb
index ca0046fb1f7..d7570fdd3e2 100644
--- a/lib/gitlab/ci/status/build/failed_allowed.rb
+++ b/lib/gitlab/ci/status/build/failed_allowed.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/manual.rb b/lib/gitlab/ci/status/build/manual.rb
index 042da6392d3..d01b09f1398 100644
--- a/lib/gitlab/ci/status/build/manual.rb
+++ b/lib/gitlab/ci/status/build/manual.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/pending.rb b/lib/gitlab/ci/status/build/pending.rb
index 9dd9a27ad57..95f668295dd 100644
--- a/lib/gitlab/ci/status/build/pending.rb
+++ b/lib/gitlab/ci/status/build/pending.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb
index a8b9ebf0803..c66b8ca5654 100644
--- a/lib/gitlab/ci/status/build/play.rb
+++ b/lib/gitlab/ci/status/build/play.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/retried.rb b/lib/gitlab/ci/status/build/retried.rb
index 6e190e4ee3c..b489dc68733 100644
--- a/lib/gitlab/ci/status/build/retried.rb
+++ b/lib/gitlab/ci/status/build/retried.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/retryable.rb b/lib/gitlab/ci/status/build/retryable.rb
index 5aeb8e51480..eb6b3f21604 100644
--- a/lib/gitlab/ci/status/build/retryable.rb
+++ b/lib/gitlab/ci/status/build/retryable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/scheduled.rb b/lib/gitlab/ci/status/build/scheduled.rb
new file mode 100644
index 00000000000..b3452eae189
--- /dev/null
+++ b/lib/gitlab/ci/status/build/scheduled.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Status
+ module Build
+ class Scheduled < Status::Extended
+ def illustration
+ {
+ image: 'illustrations/illustrations_scheduled-job_countdown.svg',
+ size: 'svg-394',
+ title: _("This is a delayed job to run in %{remainingTime}"),
+ content: _("This job will automatically run after it's timer finishes. " \
+ "Often they are used for incremental roll-out deploys " \
+ "to production environments. When unscheduled it converts " \
+ "into a manual action.")
+ }
+ end
+
+ def status_tooltip
+ "delayed manual action (%{remainingTime})"
+ end
+
+ def self.matches?(build, user)
+ build.scheduled? && build.scheduled_at
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/build/skipped.rb b/lib/gitlab/ci/status/build/skipped.rb
index 3e678d0baee..4fe2f7b3114 100644
--- a/lib/gitlab/ci/status/build/skipped.rb
+++ b/lib/gitlab/ci/status/build/skipped.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb
index dea838bfa39..a620e7ad126 100644
--- a/lib/gitlab/ci/status/build/stop.rb
+++ b/lib/gitlab/ci/status/build/stop.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/build/unschedule.rb b/lib/gitlab/ci/status/build/unschedule.rb
new file mode 100644
index 00000000000..9110839cb55
--- /dev/null
+++ b/lib/gitlab/ci/status/build/unschedule.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Status
+ module Build
+ class Unschedule < Status::Extended
+ def label
+ 'unschedule action'
+ end
+
+ def has_action?
+ can?(user, :update_build, subject)
+ end
+
+ def action_icon
+ 'time-out'
+ end
+
+ def action_title
+ 'Unschedule'
+ end
+
+ def action_button_title
+ _('Unschedule job')
+ end
+
+ def action_path
+ unschedule_project_job_path(subject.project, subject)
+ end
+
+ def action_method
+ :post
+ end
+
+ def self.matches?(build, user)
+ build.scheduled?
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/canceled.rb b/lib/gitlab/ci/status/canceled.rb
index e6195a60d4f..07f37732023 100644
--- a/lib/gitlab/ci/status/canceled.rb
+++ b/lib/gitlab/ci/status/canceled.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb
index 9d6a2f51c11..ea773ee9944 100644
--- a/lib/gitlab/ci/status/core.rb
+++ b/lib/gitlab/ci/status/core.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/created.rb b/lib/gitlab/ci/status/created.rb
index 846f00b83dd..fface4bb97b 100644
--- a/lib/gitlab/ci/status/created.rb
+++ b/lib/gitlab/ci/status/created.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/extended.rb b/lib/gitlab/ci/status/extended.rb
index 1e8101f8949..b72a28ed0b6 100644
--- a/lib/gitlab/ci/status/extended.rb
+++ b/lib/gitlab/ci/status/extended.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/external/common.rb b/lib/gitlab/ci/status/external/common.rb
index 9307545b5b1..4169f5b3210 100644
--- a/lib/gitlab/ci/status/external/common.rb
+++ b/lib/gitlab/ci/status/external/common.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/external/factory.rb b/lib/gitlab/ci/status/external/factory.rb
index 07b15bd8d97..91fafb940a8 100644
--- a/lib/gitlab/ci/status/external/factory.rb
+++ b/lib/gitlab/ci/status/external/factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb
index 15836c699c7..3446644eff8 100644
--- a/lib/gitlab/ci/status/factory.rb
+++ b/lib/gitlab/ci/status/factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/failed.rb b/lib/gitlab/ci/status/failed.rb
index 27ce85bd3ed..770ed7d4d5a 100644
--- a/lib/gitlab/ci/status/failed.rb
+++ b/lib/gitlab/ci/status/failed.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/group/common.rb b/lib/gitlab/ci/status/group/common.rb
index cfd4329a923..0b5ea0712ca 100644
--- a/lib/gitlab/ci/status/group/common.rb
+++ b/lib/gitlab/ci/status/group/common.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/group/factory.rb b/lib/gitlab/ci/status/group/factory.rb
index d118116cfc3..ee785856fdd 100644
--- a/lib/gitlab/ci/status/group/factory.rb
+++ b/lib/gitlab/ci/status/group/factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/manual.rb b/lib/gitlab/ci/status/manual.rb
index fc387e2fd25..50c92add400 100644
--- a/lib/gitlab/ci/status/manual.rb
+++ b/lib/gitlab/ci/status/manual.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/pending.rb b/lib/gitlab/ci/status/pending.rb
index 6780780db32..cea7e6ed938 100644
--- a/lib/gitlab/ci/status/pending.rb
+++ b/lib/gitlab/ci/status/pending.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/pipeline/blocked.rb b/lib/gitlab/ci/status/pipeline/blocked.rb
index bf7e484ee9b..ed13a439be0 100644
--- a/lib/gitlab/ci/status/pipeline/blocked.rb
+++ b/lib/gitlab/ci/status/pipeline/blocked.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/pipeline/common.rb b/lib/gitlab/ci/status/pipeline/common.rb
index 61bb07beb0f..7b34a2ea858 100644
--- a/lib/gitlab/ci/status/pipeline/common.rb
+++ b/lib/gitlab/ci/status/pipeline/common.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/pipeline/delayed.rb b/lib/gitlab/ci/status/pipeline/delayed.rb
new file mode 100644
index 00000000000..e61acdcd167
--- /dev/null
+++ b/lib/gitlab/ci/status/pipeline/delayed.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Status
+ module Pipeline
+ class Delayed < Status::Extended
+ def text
+ s_('CiStatusText|delayed')
+ end
+
+ def label
+ s_('CiStatusLabel|waiting for delayed job')
+ end
+
+ def self.matches?(pipeline, user)
+ pipeline.scheduled?
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb
index 17f9a75f436..5d1a8bbd924 100644
--- a/lib/gitlab/ci/status/pipeline/factory.rb
+++ b/lib/gitlab/ci/status/pipeline/factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
@@ -5,6 +7,7 @@ module Gitlab
class Factory < Status::Factory
def self.extended_statuses
[[Status::SuccessWarning,
+ Status::Pipeline::Delayed,
Status::Pipeline::Blocked]]
end
diff --git a/lib/gitlab/ci/status/running.rb b/lib/gitlab/ci/status/running.rb
index ee13905e46d..ac7dd74cdce 100644
--- a/lib/gitlab/ci/status/running.rb
+++ b/lib/gitlab/ci/status/running.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/scheduled.rb b/lib/gitlab/ci/status/scheduled.rb
new file mode 100644
index 00000000000..16ad1da89e3
--- /dev/null
+++ b/lib/gitlab/ci/status/scheduled.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Status
+ class Scheduled < Status::Core
+ def text
+ s_('CiStatusText|delayed')
+ end
+
+ def label
+ s_('CiStatusLabel|delayed')
+ end
+
+ def icon
+ 'status_scheduled'
+ end
+
+ def favicon
+ 'favicon_status_scheduled'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/skipped.rb b/lib/gitlab/ci/status/skipped.rb
index 0dbdc4de426..aaec1e1d201 100644
--- a/lib/gitlab/ci/status/skipped.rb
+++ b/lib/gitlab/ci/status/skipped.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb
index f60a7662075..f12daaa9676 100644
--- a/lib/gitlab/ci/status/stage/common.rb
+++ b/lib/gitlab/ci/status/stage/common.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb
index 4c37f084d07..58f4642510b 100644
--- a/lib/gitlab/ci/status/stage/factory.rb
+++ b/lib/gitlab/ci/status/stage/factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/success.rb b/lib/gitlab/ci/status/success.rb
index 731013ec017..020f2c5b89f 100644
--- a/lib/gitlab/ci/status/success.rb
+++ b/lib/gitlab/ci/status/success.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/status/success_warning.rb b/lib/gitlab/ci/status/success_warning.rb
index 32b4cf43e48..6632cd9b143 100644
--- a/lib/gitlab/ci/status/success_warning.rb
+++ b/lib/gitlab/ci/status/success_warning.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Status
diff --git a/lib/gitlab/ci/templates/Android.gitlab-ci.yml b/lib/gitlab/ci/templates/Android.gitlab-ci.yml
new file mode 100644
index 00000000000..6e138639b71
--- /dev/null
+++ b/lib/gitlab/ci/templates/Android.gitlab-ci.yml
@@ -0,0 +1,45 @@
+# Read more about this script on this blog post https://about.gitlab.com/2018/10/24/setting-up-gitlab-ci-for-android-projects/, by Jason Lenny
+image: openjdk:8-jdk
+
+variables:
+ ANDROID_COMPILE_SDK: "28"
+ ANDROID_BUILD_TOOLS: "28.0.2"
+ ANDROID_SDK_TOOLS: "4333796"
+
+before_script:
+ - apt-get --quiet update --yes
+ - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
+ - wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
+ - unzip -d android-sdk-linux android-sdk.zip
+ - echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
+ - echo y | android-sdk-linux/tools/bin/sdkmanager "platform-tools" >/dev/null
+ - echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
+ - export ANDROID_HOME=$PWD/android-sdk-linux
+ - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
+ - chmod +x ./gradlew
+ # temporarily disable checking for EPIPE error and use yes to accept all licenses
+ - set +o pipefail
+ - yes | android-sdk-linux/tools/bin/sdkmanager --licenses
+ - set -o pipefail
+
+stages:
+ - build
+ - test
+
+lintDebug:
+ stage: build
+ script:
+ - ./gradlew -Pci --console=plain :app:lintDebug -PbuildDir=lint
+
+assembleDebug:
+ stage: build
+ script:
+ - ./gradlew assembleDebug
+ artifacts:
+ paths:
+ - app/build/outputs/
+
+debugTests:
+ stage: test
+ script:
+ - ./gradlew -Pci --console=plain :app:testDebug
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
new file mode 100644
index 00000000000..c90976b2040
--- /dev/null
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -0,0 +1,930 @@
+# Auto DevOps
+# This CI/CD configuration provides a standard pipeline for
+# * building a Docker image (using a buildpack if necessary),
+# * storing the image in the container registry,
+# * running tests from a buildpack,
+# * running code quality analysis,
+# * creating a review app for each topic branch,
+# * and continuous deployment to production
+#
+# Test jobs may be disabled by setting environment variables:
+# * test: TEST_DISABLED
+# * code_quality: CODE_QUALITY_DISABLED
+# * license_management: LICENSE_MANAGEMENT_DISABLED
+# * performance: PERFORMANCE_DISABLED
+# * sast: SAST_DISABLED
+# * dependency_scanning: DEPENDENCY_SCANNING_DISABLED
+# * container_scanning: CONTAINER_SCANNING_DISABLED
+# * dast: DAST_DISABLED
+# * review: REVIEW_DISABLED
+# * stop_review: REVIEW_DISABLED
+#
+# The sast and sast_dashboard jobs are executed to guarantee full compatibility
+# with the group security dashboard and the security reports with old runners.
+# If you use only runners with version 11.5 or above, you can disable the sast
+# job by setting the OLD_REPORTS_DISABLED environment variable. If you use only
+# runners with version below 11.5, you can disable the sast_dashboard job by
+# setting the NEW_REPORTS_DISABLED environment variable.
+# The sast_dashboard job will be removed in the future, when the sast job will
+# use the new reports syntax.
+#
+# In order to deploy, you must have a Kubernetes cluster configured either
+# via a project integration, or via group/project variables.
+# AUTO_DEVOPS_DOMAIN must also be set as a variable at the group or project
+# level, or manually added below.
+#
+# Continuous deployment to production is enabled by default.
+# If you want to deploy to staging first, set STAGING_ENABLED environment variable.
+# If you want to enable incremental rollout, either manual or time based,
+# set INCREMENTAL_ROLLOUT_MODE environment variable to "manual" or "timed".
+# If you want to use canary deployments, set CANARY_ENABLED environment variable.
+#
+# If Auto DevOps fails to detect the proper buildpack, or if you want to
+# specify a custom buildpack, set a project variable `BUILDPACK_URL` to the
+# repository URL of the buildpack.
+# e.g. BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-ruby.git#v142
+# If you need multiple buildpacks, add a file to your project called
+# `.buildpacks` that contains the URLs, one on each line, in order.
+# Note: Auto CI does not work with multiple buildpacks yet
+
+image: alpine:latest
+
+variables:
+ # AUTO_DEVOPS_DOMAIN is the application deployment domain and should be set as a variable at the group or project level.
+ # AUTO_DEVOPS_DOMAIN: domain.example.com
+
+ POSTGRES_USER: user
+ POSTGRES_PASSWORD: testing-password
+ POSTGRES_ENABLED: "true"
+ POSTGRES_DB: $CI_ENVIRONMENT_SLUG
+
+ KUBERNETES_VERSION: 1.10.9
+ HELM_VERSION: 2.11.0
+
+ DOCKER_DRIVER: overlay2
+
+stages:
+ - build
+ - test
+ - review
+ - dast
+ - staging
+ - canary
+ - production
+ - incremental rollout 10%
+ - incremental rollout 25%
+ - incremental rollout 50%
+ - incremental rollout 100%
+ - performance
+ - cleanup
+
+build:
+ stage: build
+ image: docker:stable-git
+ services:
+ - docker:stable-dind
+ script:
+ - setup_docker
+ - build
+ only:
+ - branches
+
+test:
+ services:
+ - postgres:latest
+ variables:
+ POSTGRES_DB: test
+ stage: test
+ image: gliderlabs/herokuish:latest
+ script:
+ - setup_test_db
+ - cp -R . /tmp/app
+ - /bin/herokuish buildpack test
+ only:
+ - branches
+ except:
+ variables:
+ - $TEST_DISABLED
+
+code_quality:
+ stage: test
+ image: docker:stable
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - setup_docker
+ - code_quality
+ artifacts:
+ paths: [gl-code-quality-report.json]
+ only:
+ - branches
+ except:
+ variables:
+ - $CODE_QUALITY_DISABLED
+
+license_management:
+ stage: test
+ image:
+ name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable"
+ entrypoint: [""]
+ allow_failure: true
+ script:
+ - license_management
+ artifacts:
+ paths: [gl-license-management-report.json]
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\blicense_management\b/
+ except:
+ variables:
+ - $LICENSE_MANAGEMENT_DISABLED
+
+performance:
+ stage: performance
+ image: docker:stable
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - setup_docker
+ - performance
+ artifacts:
+ paths:
+ - performance.json
+ - sitespeed-results/
+ only:
+ refs:
+ - branches
+ except:
+ variables:
+ - $PERFORMANCE_DISABLED
+ - $KUBECONFIG == null
+
+sast:
+ stage: test
+ image: docker:stable
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - setup_docker
+ - sast
+ artifacts:
+ paths: [gl-sast-report.json]
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bsast\b/
+ except:
+ variables:
+ - $SAST_DISABLED
+ - $OLD_REPORTS_DISABLED
+
+sast_dashboard:
+ stage: test
+ image: docker:stable
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - setup_docker
+ - sast
+ artifacts:
+ reports:
+ sast: gl-sast-report.json
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bsast\b/
+ except:
+ variables:
+ - $SAST_DISABLED
+ - $NEW_REPORTS_DISABLED
+
+dependency_scanning:
+ stage: test
+ image: docker:stable
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - setup_docker
+ - dependency_scanning
+ artifacts:
+ paths: [gl-dependency-scanning-report.json]
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ except:
+ variables:
+ - $DEPENDENCY_SCANNING_DISABLED
+
+container_scanning:
+ stage: test
+ image: docker:stable
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - setup_docker
+ - container_scanning
+ artifacts:
+ paths: [gl-container-scanning-report.json]
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bcontainer_scanning\b/
+ except:
+ variables:
+ - $CONTAINER_SCANNING_DISABLED
+
+dast:
+ stage: dast
+ allow_failure: true
+ image: registry.gitlab.com/gitlab-org/security-products/zaproxy
+ variables:
+ POSTGRES_DB: "false"
+ script:
+ - dast
+ artifacts:
+ paths: [gl-dast-report.json]
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bdast\b/
+ except:
+ refs:
+ - master
+ variables:
+ - $DAST_DISABLED
+ - $KUBECONFIG == null
+
+review:
+ stage: review
+ script:
+ - check_kube_domain
+ - install_dependencies
+ - download_chart
+ - ensure_namespace
+ - initialize_tiller
+ - create_secret
+ - deploy
+ - persist_environment_url
+ environment:
+ name: review/$CI_COMMIT_REF_NAME
+ url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$AUTO_DEVOPS_DOMAIN
+ on_stop: stop_review
+ artifacts:
+ paths: [environment_url.txt]
+ only:
+ refs:
+ - branches
+ except:
+ refs:
+ - master
+ variables:
+ - $REVIEW_DISABLED
+ - $KUBECONFIG == null
+
+stop_review:
+ stage: cleanup
+ variables:
+ GIT_STRATEGY: none
+ script:
+ - install_dependencies
+ - initialize_tiller
+ - delete
+ environment:
+ name: review/$CI_COMMIT_REF_NAME
+ action: stop
+ when: manual
+ allow_failure: true
+ only:
+ refs:
+ - branches
+ except:
+ refs:
+ - master
+ variables:
+ - $REVIEW_DISABLED
+ - $KUBECONFIG == null
+
+# Staging deploys are disabled by default since
+# continuous deployment to production is enabled by default
+# If you prefer to automatically deploy to staging and
+# only manually promote to production, enable this job by setting
+# STAGING_ENABLED.
+
+staging:
+ stage: staging
+ script:
+ - check_kube_domain
+ - install_dependencies
+ - download_chart
+ - ensure_namespace
+ - initialize_tiller
+ - create_secret
+ - deploy
+ environment:
+ name: staging
+ url: http://$CI_PROJECT_PATH_SLUG-staging.$AUTO_DEVOPS_DOMAIN
+ only:
+ refs:
+ - master
+ variables:
+ - $STAGING_ENABLED
+ except:
+ variables:
+ - $KUBECONFIG == null
+
+# Canaries are also disabled by default, but if you want them,
+# and know what the downsides are, you can enable this by setting
+# CANARY_ENABLED.
+
+canary:
+ stage: canary
+ script:
+ - check_kube_domain
+ - install_dependencies
+ - download_chart
+ - ensure_namespace
+ - initialize_tiller
+ - create_secret
+ - deploy canary
+ environment:
+ name: production
+ url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
+ when: manual
+ only:
+ refs:
+ - master
+ variables:
+ - $CANARY_ENABLED
+ except:
+ variables:
+ - $KUBECONFIG == null
+
+.production: &production_template
+ stage: production
+ script:
+ - check_kube_domain
+ - install_dependencies
+ - download_chart
+ - ensure_namespace
+ - initialize_tiller
+ - create_secret
+ - deploy
+ - delete canary
+ - delete rollout
+ - persist_environment_url
+ environment:
+ name: production
+ url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
+ artifacts:
+ paths: [environment_url.txt]
+
+production:
+ <<: *production_template
+ only:
+ refs:
+ - master
+ except:
+ variables:
+ - $STAGING_ENABLED
+ - $CANARY_ENABLED
+ - $INCREMENTAL_ROLLOUT_ENABLED
+ - $INCREMENTAL_ROLLOUT_MODE
+ - $KUBECONFIG == null
+
+production_manual:
+ <<: *production_template
+ when: manual
+ allow_failure: false
+ only:
+ refs:
+ - master
+ variables:
+ - $STAGING_ENABLED
+ - $CANARY_ENABLED
+ except:
+ variables:
+ - $INCREMENTAL_ROLLOUT_ENABLED
+ - $INCREMENTAL_ROLLOUT_MODE
+ - $KUBECONFIG == null
+
+# This job implements incremental rollout on for every push to `master`.
+
+.rollout: &rollout_template
+ script:
+ - check_kube_domain
+ - install_dependencies
+ - download_chart
+ - ensure_namespace
+ - initialize_tiller
+ - create_secret
+ - deploy rollout $ROLLOUT_PERCENTAGE
+ - scale stable $((100-ROLLOUT_PERCENTAGE))
+ - delete canary
+ - persist_environment_url
+ environment:
+ name: production
+ url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
+ artifacts:
+ paths: [environment_url.txt]
+
+.manual_rollout_template: &manual_rollout_template
+ <<: *rollout_template
+ stage: production
+ when: manual
+ # This selectors are backward compatible mode with $INCREMENTAL_ROLLOUT_ENABLED (before 11.4)
+ only:
+ refs:
+ - master
+ variables:
+ - $INCREMENTAL_ROLLOUT_MODE == "manual"
+ - $INCREMENTAL_ROLLOUT_ENABLED
+ except:
+ variables:
+ - $INCREMENTAL_ROLLOUT_MODE == "timed"
+ - $KUBECONFIG == null
+
+.timed_rollout_template: &timed_rollout_template
+ <<: *rollout_template
+ when: delayed
+ start_in: 5 minutes
+ only:
+ refs:
+ - master
+ variables:
+ - $INCREMENTAL_ROLLOUT_MODE == "timed"
+ except:
+ variables:
+ - $KUBECONFIG == null
+
+timed rollout 10%:
+ <<: *timed_rollout_template
+ stage: incremental rollout 10%
+ variables:
+ ROLLOUT_PERCENTAGE: 10
+
+timed rollout 25%:
+ <<: *timed_rollout_template
+ stage: incremental rollout 25%
+ variables:
+ ROLLOUT_PERCENTAGE: 25
+
+timed rollout 50%:
+ <<: *timed_rollout_template
+ stage: incremental rollout 50%
+ variables:
+ ROLLOUT_PERCENTAGE: 50
+
+timed rollout 100%:
+ <<: *timed_rollout_template
+ <<: *production_template
+ stage: incremental rollout 100%
+ variables:
+ ROLLOUT_PERCENTAGE: 100
+
+rollout 10%:
+ <<: *manual_rollout_template
+ variables:
+ ROLLOUT_PERCENTAGE: 10
+
+rollout 25%:
+ <<: *manual_rollout_template
+ variables:
+ ROLLOUT_PERCENTAGE: 25
+
+rollout 50%:
+ <<: *manual_rollout_template
+ variables:
+ ROLLOUT_PERCENTAGE: 50
+
+rollout 100%:
+ <<: *manual_rollout_template
+ <<: *production_template
+ allow_failure: false
+
+# ---------------------------------------------------------------------------
+
+.auto_devops: &auto_devops |
+ # Auto DevOps variables and functions
+ [[ "$TRACE" ]] && set -x
+ auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${CI_ENVIRONMENT_SLUG}-postgres:5432/${POSTGRES_DB}
+ export DATABASE_URL=${DATABASE_URL-$auto_database_url}
+ export CI_APPLICATION_REPOSITORY=$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG
+ export CI_APPLICATION_TAG=$CI_COMMIT_SHA
+ export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID}
+ export TILLER_NAMESPACE=$KUBE_NAMESPACE
+ # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
+ export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+
+ function registry_login() {
+ if [[ -n "$CI_REGISTRY_USER" ]]; then
+ echo "Logging to GitLab Container Registry with CI credentials..."
+ docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
+ echo ""
+ fi
+ }
+
+ function container_scanning() {
+ registry_login
+
+ docker run -d --name db arminc/clair-db:latest
+ docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.1
+ apk add -U wget ca-certificates
+ docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
+ wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
+ mv clair-scanner_linux_amd64 clair-scanner
+ chmod +x clair-scanner
+ touch clair-whitelist.yml
+ retries=0
+ echo "Waiting for clair daemon to start"
+ while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done
+ ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
+ }
+
+ function code_quality() {
+ docker run --env SOURCE_CODE="$PWD" \
+ --volume "$PWD":/code \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
+ }
+
+ function license_management() {
+ /run.sh analyze .
+ }
+
+ function sast() {
+ case "$CI_SERVER_VERSION" in
+ *-ee)
+
+ # Deprecation notice for CONFIDENCE_LEVEL variable
+ if [ -z "$SAST_CONFIDENCE_LEVEL" -a "$CONFIDENCE_LEVEL" ]; then
+ SAST_CONFIDENCE_LEVEL="$CONFIDENCE_LEVEL"
+ echo "WARNING: CONFIDENCE_LEVEL is deprecated and MUST be replaced with SAST_CONFIDENCE_LEVEL"
+ fi
+
+ docker run --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" \
+ --volume "$PWD:/code" \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
+ ;;
+ *)
+ echo "GitLab EE is required"
+ ;;
+ esac
+ }
+
+ function dependency_scanning() {
+ case "$CI_SERVER_VERSION" in
+ *-ee)
+ docker run --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}" \
+ --volume "$PWD:/code" \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
+ ;;
+ *)
+ echo "GitLab EE is required"
+ ;;
+ esac
+ }
+
+ function get_replicas() {
+ track="${1:-stable}"
+ percentage="${2:-100}"
+
+ env_track=$( echo $track | tr -s '[:lower:]' '[:upper:]' )
+ env_slug=$( echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]' )
+
+ if [[ "$track" == "stable" ]] || [[ "$track" == "rollout" ]]; then
+ # for stable track get number of replicas from `PRODUCTION_REPLICAS`
+ eval new_replicas=\$${env_slug}_REPLICAS
+ if [[ -z "$new_replicas" ]]; then
+ new_replicas=$REPLICAS
+ fi
+ else
+ # for all tracks get number of replicas from `CANARY_PRODUCTION_REPLICAS`
+ eval new_replicas=\$${env_track}_${env_slug}_REPLICAS
+ if [[ -z "$new_replicas" ]]; then
+ eval new_replicas=\${env_track}_REPLICAS
+ fi
+ fi
+
+ replicas="${new_replicas:-1}"
+ replicas="$(($replicas * $percentage / 100))"
+
+ # always return at least one replicas
+ if [[ $replicas -gt 0 ]]; then
+ echo "$replicas"
+ else
+ echo 1
+ fi
+ }
+
+ function deploy() {
+ track="${1-stable}"
+ percentage="${2:-100}"
+ name="$CI_ENVIRONMENT_SLUG"
+
+ replicas="1"
+ service_enabled="true"
+ postgres_enabled="$POSTGRES_ENABLED"
+
+ # if track is different than stable,
+ # re-use all attached resources
+ if [[ "$track" != "stable" ]]; then
+ name="$name-$track"
+ service_enabled="false"
+ postgres_enabled="false"
+ fi
+
+ replicas=$(get_replicas "$track" "$percentage")
+
+ if [[ "$CI_PROJECT_VISIBILITY" != "public" ]]; then
+ secret_name='gitlab-registry'
+ else
+ secret_name=''
+ fi
+
+ if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then
+ helm upgrade --install \
+ --wait \
+ --set service.enabled="$service_enabled" \
+ --set releaseOverride="$CI_ENVIRONMENT_SLUG" \
+ --set image.repository="$CI_APPLICATION_REPOSITORY" \
+ --set image.tag="$CI_APPLICATION_TAG" \
+ --set image.pullPolicy=IfNotPresent \
+ --set image.secrets[0].name="$secret_name" \
+ --set application.track="$track" \
+ --set application.database_url="$DATABASE_URL" \
+ --set service.url="$CI_ENVIRONMENT_URL" \
+ --set replicaCount="$replicas" \
+ --set postgresql.enabled="$postgres_enabled" \
+ --set postgresql.nameOverride="postgres" \
+ --set postgresql.postgresUser="$POSTGRES_USER" \
+ --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
+ --set postgresql.postgresDatabase="$POSTGRES_DB" \
+ --set application.initializeCommand="$DB_INITIALIZE" \
+ --namespace="$KUBE_NAMESPACE" \
+ "$name" \
+ chart/
+
+ helm upgrade --reuse-values \
+ --wait \
+ --set application.initializeCommand="" \
+ --set application.migrateCommand="$DB_MIGRATE" \
+ --namespace="$KUBE_NAMESPACE" \
+ "$name" \
+ chart/
+ else
+ helm upgrade --install \
+ --wait \
+ --set service.enabled="$service_enabled" \
+ --set releaseOverride="$CI_ENVIRONMENT_SLUG" \
+ --set image.repository="$CI_APPLICATION_REPOSITORY" \
+ --set image.tag="$CI_APPLICATION_TAG" \
+ --set image.pullPolicy=IfNotPresent \
+ --set image.secrets[0].name="$secret_name" \
+ --set application.track="$track" \
+ --set application.database_url="$DATABASE_URL" \
+ --set service.url="$CI_ENVIRONMENT_URL" \
+ --set replicaCount="$replicas" \
+ --set postgresql.enabled="$postgres_enabled" \
+ --set postgresql.nameOverride="postgres" \
+ --set postgresql.postgresUser="$POSTGRES_USER" \
+ --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
+ --set postgresql.postgresDatabase="$POSTGRES_DB" \
+ --set application.migrateCommand="$DB_MIGRATE" \
+ --namespace="$KUBE_NAMESPACE" \
+ "$name" \
+ chart/
+ fi
+
+ kubectl rollout status -n "$KUBE_NAMESPACE" -w "deployment/$name"
+ }
+
+ function scale() {
+ track="${1-stable}"
+ percentage="${2-100}"
+ name="$CI_ENVIRONMENT_SLUG"
+
+ if [[ "$track" != "stable" ]]; then
+ name="$name-$track"
+ fi
+
+ replicas=$(get_replicas "$track" "$percentage")
+
+ if [[ -n "$(helm ls -q "^$name$")" ]]; then
+ helm upgrade --reuse-values \
+ --wait \
+ --set replicaCount="$replicas" \
+ --namespace="$KUBE_NAMESPACE" \
+ "$name" \
+ chart/
+ fi
+ }
+
+ function install_dependencies() {
+ apk add -U openssl curl tar gzip bash ca-certificates git
+ curl -L -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
+ curl -L -O https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk
+ apk add glibc-2.28-r0.apk
+ rm glibc-2.28-r0.apk
+
+ curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
+ mv linux-amd64/helm /usr/bin/
+ mv linux-amd64/tiller /usr/bin/
+ helm version --client
+ tiller -version
+
+ curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
+ chmod +x /usr/bin/kubectl
+ kubectl version --client
+ }
+
+ function setup_docker() {
+ if ! docker info &>/dev/null; then
+ if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
+ export DOCKER_HOST='tcp://localhost:2375'
+ fi
+ fi
+ }
+
+ function setup_test_db() {
+ if [ -z ${KUBERNETES_PORT+x} ]; then
+ DB_HOST=postgres
+ else
+ DB_HOST=localhost
+ fi
+ export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DB_HOST}:5432/${POSTGRES_DB}"
+ }
+
+ function download_chart() {
+ if [[ ! -d chart ]]; then
+ auto_chart=${AUTO_DEVOPS_CHART:-gitlab/auto-deploy-app}
+ auto_chart_name=$(basename $auto_chart)
+ auto_chart_name=${auto_chart_name%.tgz}
+ auto_chart_name=${auto_chart_name%.tar.gz}
+ else
+ auto_chart="chart"
+ auto_chart_name="chart"
+ fi
+
+ helm init --client-only
+ helm repo add gitlab https://charts.gitlab.io
+ if [[ ! -d "$auto_chart" ]]; then
+ helm fetch ${auto_chart} --untar
+ fi
+ if [ "$auto_chart_name" != "chart" ]; then
+ mv ${auto_chart_name} chart
+ fi
+
+ helm dependency update chart/
+ helm dependency build chart/
+ }
+
+ function ensure_namespace() {
+ kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
+ }
+
+ function check_kube_domain() {
+ if [ -z ${AUTO_DEVOPS_DOMAIN+x} ]; then
+ echo "In order to deploy or use Review Apps, AUTO_DEVOPS_DOMAIN variable must be set"
+ echo "You can do it in Auto DevOps project settings or defining a variable at group or project level"
+ echo "You can also manually add it in .gitlab-ci.yml"
+ false
+ else
+ true
+ fi
+ }
+
+ function build() {
+ registry_login
+
+ if [[ -f Dockerfile ]]; then
+ echo "Building Dockerfile-based application..."
+ docker build \
+ --build-arg HTTP_PROXY="$HTTP_PROXY" \
+ --build-arg http_proxy="$http_proxy" \
+ --build-arg HTTPS_PROXY="$HTTPS_PROXY" \
+ --build-arg https_proxy="$https_proxy" \
+ --build-arg FTP_PROXY="$FTP_PROXY" \
+ --build-arg ftp_proxy="$ftp_proxy" \
+ --build-arg NO_PROXY="$NO_PROXY" \
+ --build-arg no_proxy="$no_proxy" \
+ -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
+ else
+ echo "Building Heroku-based application using gliderlabs/herokuish docker image..."
+ docker run -i \
+ -e BUILDPACK_URL \
+ -e HTTP_PROXY \
+ -e http_proxy \
+ -e HTTPS_PROXY \
+ -e https_proxy \
+ -e FTP_PROXY \
+ -e ftp_proxy \
+ -e NO_PROXY \
+ -e no_proxy \
+ --name="$CI_CONTAINER_NAME" -v "$(pwd):/tmp/app:ro" gliderlabs/herokuish /bin/herokuish buildpack build
+ docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
+ docker rm "$CI_CONTAINER_NAME" >/dev/null
+ echo ""
+
+ echo "Configuring $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG docker image..."
+ docker create --expose 5000 --env PORT=5000 --name="$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" /bin/herokuish procfile start web
+ docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
+ docker rm "$CI_CONTAINER_NAME" >/dev/null
+ echo ""
+ fi
+
+ echo "Pushing to GitLab Container Registry..."
+ docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
+ echo ""
+ }
+
+ function initialize_tiller() {
+ echo "Checking Tiller..."
+
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr > /dev/null 2>&1 &
+ echo "Tiller is listening on ${HELM_HOST}"
+
+ if ! helm version --debug; then
+ echo "Failed to init Tiller."
+ return 1
+ fi
+ echo ""
+ }
+
+ function create_secret() {
+ echo "Create secret..."
+ if [[ "$CI_PROJECT_VISIBILITY" == "public" ]]; then
+ return
+ fi
+
+ kubectl create secret -n "$KUBE_NAMESPACE" \
+ docker-registry gitlab-registry \
+ --docker-server="$CI_REGISTRY" \
+ --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \
+ --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \
+ --docker-email="$GITLAB_USER_EMAIL" \
+ -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f -
+ }
+
+ function dast() {
+ export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
+
+ mkdir /zap/wrk/
+ /zap/zap-baseline.py -J gl-dast-report.json -t "$CI_ENVIRONMENT_URL" || true
+ cp /zap/wrk/gl-dast-report.json .
+ }
+
+ function performance() {
+ export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
+
+ mkdir gitlab-exporter
+ wget -O gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js
+
+ mkdir sitespeed-results
+
+ if [ -f .gitlab-urls.txt ]
+ then
+ sed -i -e 's@^@'"$CI_ENVIRONMENT_URL"'@' .gitlab-urls.txt
+ docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results .gitlab-urls.txt
+ else
+ docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL"
+ fi
+
+ mv sitespeed-results/data/performance.json performance.json
+ }
+
+ function persist_environment_url() {
+ echo $CI_ENVIRONMENT_URL > environment_url.txt
+ }
+
+ function delete() {
+ track="${1-stable}"
+ name="$CI_ENVIRONMENT_SLUG"
+
+ if [[ "$track" != "stable" ]]; then
+ name="$name-$track"
+ fi
+
+ if [[ -n "$(helm ls -q "^$name$")" ]]; then
+ helm delete --purge "$name"
+ fi
+ }
+
+before_script:
+ - *auto_devops
diff --git a/vendor/gitlab-ci-yml/Bash.gitlab-ci.yml b/lib/gitlab/ci/templates/Bash.gitlab-ci.yml
index 2d218b2e164..2d218b2e164 100644
--- a/vendor/gitlab-ci-yml/Bash.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Bash.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/C++.gitlab-ci.yml b/lib/gitlab/ci/templates/C++.gitlab-ci.yml
index c83c49d8c95..c83c49d8c95 100644
--- a/vendor/gitlab-ci-yml/C++.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/C++.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Chef.gitlab-ci.yml b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml
index 4d5b6484d6e..4d5b6484d6e 100644
--- a/vendor/gitlab-ci-yml/Chef.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Clojure.gitlab-ci.yml b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml
index f066285b1ad..f066285b1ad 100644
--- a/vendor/gitlab-ci-yml/Clojure.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml
index 36386a19fdc..36386a19fdc 100644
--- a/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Django.gitlab-ci.yml b/lib/gitlab/ci/templates/Django.gitlab-ci.yml
index 57afcbbe8b5..57afcbbe8b5 100644
--- a/vendor/gitlab-ci-yml/Django.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Django.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml b/lib/gitlab/ci/templates/Docker.gitlab-ci.yml
index eeefadaa019..eeefadaa019 100644
--- a/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Docker.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Elixir.gitlab-ci.yml b/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml
index cf9c731637c..cf9c731637c 100644
--- a/vendor/gitlab-ci-yml/Elixir.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Go.gitlab-ci.yml b/lib/gitlab/ci/templates/Go.gitlab-ci.yml
index d572d7a1edc..d572d7a1edc 100644
--- a/vendor/gitlab-ci-yml/Go.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Go.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Gradle.gitlab-ci.yml b/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
index 48d98dddfad..48d98dddfad 100644
--- a/vendor/gitlab-ci-yml/Gradle.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Grails.gitlab-ci.yml b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml
index 7fc698d50cf..7fc698d50cf 100644
--- a/vendor/gitlab-ci-yml/Grails.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml
diff --git a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
new file mode 100644
index 00000000000..04c21b4725d
--- /dev/null
+++ b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
@@ -0,0 +1,76 @@
+# This is an example .gitlab-ci.yml file to test (and optionally report the coverage
+# results of) your [Julia][1] packages. Please refer to the [documentation][2]
+# for more information about package development in Julia.
+#
+# Here, it is assumed that your Julia package is named `MyPackage`. Change it to
+# whatever name you have given to your package.
+#
+# [1]: http://julialang.org/
+# [2]: https://docs.julialang.org/en/v1/manual/documentation/index.html
+
+# Below is the template to run your tests in Julia
+.test_template: &test_definition
+ # Uncomment below if you would like to run the tests on specific references
+ # only, such as the branches `master`, `development`, etc.
+ # only:
+ # - master
+ # - development
+ script:
+ # Let's run the tests. Substitute `coverage = false` below, if you do not
+ # want coverage results.
+ - julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("MyPackage"); Pkg.test("MyPackage"; coverage = true)'
+ # Comment out below if you do not want coverage results.
+ - julia -e 'using Pkg; Pkg.add("Coverage");
+ import MyPackage; cd(joinpath(dirname(pathof(MyPackage)), ".."));
+ using Coverage; cl, tl = get_summary(process_folder());
+ println("(", cl/tl*100, "%) covered")'
+
+# Name a test and select an appropriate image.
+# images comes from Docker hub
+test:0.7:
+ image: julia:0.7
+ <<: *test_definition
+
+test:1.0:
+ image: julia:1.0
+ <<: *test_definition
+
+# Maybe you would like to test your package against the development branch:
+# test:1.1-dev (not sure there is such an image in docker, so not tested yet):
+# image: julia:v1.1-dev
+# # ... allowing for failures, since we are testing against the development
+# # branch:
+# allow_failure: true
+# <<: *test_definition
+
+# REMARK: Do not forget to enable the coverage feature for your project, if you
+# are using code coverage reporting above. This can be done by
+#
+# - Navigating to the `CI/CD Pipelines` settings of your project,
+# - Copying and pasting the default `Simplecov` regex example provided, i.e.,
+# `\(\d+.\d+\%\) covered` in the `test coverage parsing` textfield.
+
+# Example documentation deployment
+pages:
+ image: julia:0.7
+ stage: deploy
+ script:
+ - apt-get update -qq && apt-get install -y git # needed by Documenter
+ - julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("MyPackage");' # rebuild Julia (can be put somewhere else I'm sure
+ - julia -e 'using Pkg; import MyPackage; Pkg.add("Documenter")' # install Documenter
+ - julia --color=yes docs/make.jl # make documentation
+ - mv docs/build public # move to the directory picked up by Gitlab pages
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
+
+
+# WARNING: This template is using the `julia` images from [Docker
+# Hub][3]. One can use custom Julia images and/or the official ones found
+# in the same place. However, care must be taken to correctly locate the binary
+# file (`/opt/julia/bin/julia` above), which is usually given on the image's
+# description page.
+#
+# [3]: https://hub.docker.com/_/julia/
diff --git a/vendor/gitlab-ci-yml/LaTeX.gitlab-ci.yml b/lib/gitlab/ci/templates/LaTeX.gitlab-ci.yml
index a4aed36889e..a4aed36889e 100644
--- a/vendor/gitlab-ci-yml/LaTeX.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/LaTeX.gitlab-ci.yml
diff --git a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
new file mode 100644
index 00000000000..d0cad285572
--- /dev/null
+++ b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
@@ -0,0 +1,85 @@
+# Official framework image. Look for the different tagged releases at:
+# https://hub.docker.com/r/library/php
+image: php:latest
+
+# Pick zero or more services to be used on all builds.
+# Only needed when using a docker container to run your tests in.
+# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
+services:
+ - mysql:latest
+
+variables:
+ MYSQL_DATABASE: project_name
+ MYSQL_ROOT_PASSWORD: secret
+
+# This folder is cached between builds
+# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
+cache:
+ paths:
+ - vendor/
+ - node_modules/
+
+# This is a basic example for a gem or script which doesn't use
+# services such as redis or postgres
+before_script:
+ # Update packages
+ - apt-get update -yqq
+
+ # Prep for Node
+ - apt-get install gnupg -yqq
+
+ # Upgrade to Node 8
+ - curl -sL https://deb.nodesource.com/setup_8.x | bash -
+
+ # Install dependencies
+ - apt-get install git nodejs libcurl4-gnutls-dev libicu-dev libmcrypt-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libpq-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev -yqq
+
+ # Install php extensions
+ - docker-php-ext-install mbstring pdo_mysql curl json intl gd xml zip bz2 opcache
+
+ # Install & enable Xdebug for code coverage reports
+ - pecl install xdebug
+ - docker-php-ext-enable xdebug
+
+ # Install Composer and project dependencies.
+ - curl -sS https://getcomposer.org/installer | php
+ - php composer.phar install
+
+ # Install Node dependencies.
+ # comment this out if you don't have a node dependency
+ - npm install
+
+ # Copy over testing configuration.
+ # Don't forget to set the database config in .env.testing correctly
+ # DB_HOST=mysql
+ # DB_DATABASE=project_name
+ # DB_USERNAME=root
+ # DB_PASSWORD=secret
+ - cp .env.testing .env
+
+ # Run npm build
+ # comment this out if you don't have a frontend build
+ # you can change this to to your frontend building script like
+ # npm run build
+ - npm run dev
+
+ # Generate an application key. Re-cache.
+ - php artisan key:generate
+ - php artisan config:cache
+
+ # Run database migrations.
+ - php artisan migrate
+
+ # Run database seed
+ - php artisan db:seed
+
+test:
+ script:
+ # run laravel tests
+ - php vendor/bin/phpunit --coverage-text --colors=never
+
+ # run frontend tests
+ # if you have any task for testing frontend
+ # set it in your package.json script
+ # comment this out if you don't have a frontend test
+ - npm test
diff --git a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
new file mode 100644
index 00000000000..492b3d03db2
--- /dev/null
+++ b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
@@ -0,0 +1,102 @@
+---
+# Build JAVA applications using Apache Maven (http://maven.apache.org)
+# For docker image tags see https://hub.docker.com/_/maven/
+#
+# For general lifecycle information see https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
+#
+# This template will build and test your projects as well as create the documentation.
+#
+# * Caches downloaded dependencies and plugins between invocation.
+# * Verify but don't deploy merge requests.
+# * Deploy built artifacts from master branch only.
+# * Shows how to use multiple jobs in test stage for verifying functionality
+# with multiple JDKs.
+# * Uses site:stage to collect the documentation for multi-module projects.
+# * Publishes the documentation for `master` branch.
+
+variables:
+ # This will suppress any download for dependencies and plugins or upload messages which would clutter the console log.
+ # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
+ MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
+ # As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used
+ # when running from the command line.
+ # `installAtEnd` and `deployAtEnd` are only effective with recent version of the corresponding plugins.
+ MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"
+
+# Cache downloaded dependencies and plugins between builds.
+# To keep cache across branches add 'key: "$CI_JOB_NAME"'
+cache:
+ paths:
+ - .m2/repository
+
+# This will only validate and compile stuff and run e.g. maven-enforcer-plugin.
+# Because some enforcer rules might check dependency convergence and class duplications
+# we use `test-compile` here instead of `validate`, so the correct classpath is picked up.
+.validate: &validate
+ stage: build
+ script:
+ - 'mvn $MAVEN_CLI_OPTS test-compile'
+
+# For merge requests do not `deploy` but only run `verify`.
+# See https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
+.verify: &verify
+ stage: test
+ script:
+ - 'mvn $MAVEN_CLI_OPTS verify site site:stage'
+ except:
+ - master
+
+# Validate merge requests using JDK7
+validate:jdk7:
+ <<: *validate
+ image: maven:3.3.9-jdk-7
+
+# Validate merge requests using JDK8
+validate:jdk8:
+ <<: *validate
+ image: maven:3.3.9-jdk-8
+
+# Verify merge requests using JDK7
+verify:jdk7:
+ <<: *verify
+ image: maven:3.3.9-jdk-7
+
+# Verify merge requests using JDK8
+verify:jdk8:
+ <<: *verify
+ image: maven:3.3.9-jdk-8
+
+
+# For `master` branch run `mvn deploy` automatically.
+# Here you need to decide whether you want to use JDK7 or 8.
+# To get this working you need to define a volume while configuring your gitlab-ci-multi-runner.
+# Mount your `settings.xml` as `/root/.m2/settings.xml` which holds your secrets.
+# See https://maven.apache.org/settings.html
+deploy:jdk8:
+ # Use stage test here, so the pages job may later pickup the created site.
+ stage: test
+ script:
+ - 'mvn $MAVEN_CLI_OPTS deploy site site:stage'
+ only:
+ - master
+ # Archive up the built documentation site.
+ artifacts:
+ paths:
+ - target/staging
+ image: maven:3.3.9-jdk-8
+
+
+pages:
+ image: busybox:latest
+ stage: deploy
+ script:
+ # Because Maven appends the artifactId automatically to the staging path if you did define a parent pom,
+ # you might need to use `mv target/staging/YOUR_ARTIFACT_ID public` instead.
+ - mv target/staging public
+ dependencies:
+ - deploy:jdk8
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
diff --git a/vendor/gitlab-ci-yml/Mono.gitlab-ci.yml b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
index 3585f99760f..3585f99760f 100644
--- a/vendor/gitlab-ci-yml/Mono.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Nodejs.gitlab-ci.yml b/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml
index 41de1458582..41de1458582 100644
--- a/vendor/gitlab-ci-yml/Nodejs.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
index 290b9997084..290b9997084 100644
--- a/vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/PHP.gitlab-ci.yml b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
index 33f44ee9222..33f44ee9222 100644
--- a/vendor/gitlab-ci-yml/PHP.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Packer.gitlab-ci.yml b/lib/gitlab/ci/templates/Packer.gitlab-ci.yml
index fa296057c72..fa296057c72 100644
--- a/vendor/gitlab-ci-yml/Packer.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Packer.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Brunch.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
index 7fcc0b436b5..7fcc0b436b5 100644
--- a/vendor/gitlab-ci-yml/Pages/Brunch.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Doxygen.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml
index 791afdd23f1..791afdd23f1 100644
--- a/vendor/gitlab-ci-yml/Pages/Doxygen.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Gatsby.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
index 9df2a4797b2..9df2a4797b2 100644
--- a/vendor/gitlab-ci-yml/Pages/Gatsby.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/HTML.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml
index 249a168aa33..249a168aa33 100644
--- a/vendor/gitlab-ci-yml/Pages/HTML.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Harp.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
index dd3ef149668..dd3ef149668 100644
--- a/vendor/gitlab-ci-yml/Pages/Harp.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Hexo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml
index 02d02250bbf..02d02250bbf 100644
--- a/vendor/gitlab-ci-yml/Pages/Hexo.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Hugo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
index b8cfb0f56f6..b8cfb0f56f6 100644
--- a/vendor/gitlab-ci-yml/Pages/Hugo.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Hyde.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml
index f5b40f2b9f1..f5b40f2b9f1 100644
--- a/vendor/gitlab-ci-yml/Pages/Hyde.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/JBake.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml
index 7abfaf53e8e..7abfaf53e8e 100644
--- a/vendor/gitlab-ci-yml/Pages/JBake.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml
index 37f50554036..37f50554036 100644
--- a/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml
diff --git a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
new file mode 100644
index 00000000000..0e5fb410a4e
--- /dev/null
+++ b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
@@ -0,0 +1,42 @@
+# Jigsaw is a simple static sites generator with Laravel's Blade.
+#
+# Full project: https://github.com/tightenco/jigsaw
+
+image: php:7.2
+
+# These folders are cached between builds
+cache:
+ paths:
+ - vendor/
+ - node_modules/
+
+before_script:
+ # Update packages
+ - apt-get update -yqq
+
+ # Install dependencies
+ - apt-get install -yqq gnupg zlib1g-dev libpng-dev
+
+ # Install Node 8
+ - curl -sL https://deb.nodesource.com/setup_8.x | bash -
+ - apt-get install -yqq nodejs
+
+ # Install php extensions
+ - docker-php-ext-install zip
+
+ # Install Composer and project dependencies.
+ - curl -sS https://getcomposer.org/installer | php
+ - php composer.phar install
+
+ # Install Node dependencies.
+ - npm install
+
+pages:
+ script:
+ - npm run production
+ - mv build_production public
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
diff --git a/vendor/gitlab-ci-yml/Pages/Lektor.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml
index c5c44a5d86c..c5c44a5d86c 100644
--- a/vendor/gitlab-ci-yml/Pages/Lektor.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Metalsmith.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
index 50e8b7ccd46..50e8b7ccd46 100644
--- a/vendor/gitlab-ci-yml/Pages/Metalsmith.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Middleman.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml
index 9f4cc0574d6..9f4cc0574d6 100644
--- a/vendor/gitlab-ci-yml/Pages/Middleman.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Nanoc.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml
index b469b316ba5..b469b316ba5 100644
--- a/vendor/gitlab-ci-yml/Pages/Nanoc.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Octopress.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml
index 4762ec9acfd..4762ec9acfd 100644
--- a/vendor/gitlab-ci-yml/Pages/Octopress.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Pages/Pelican.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Pelican.gitlab-ci.yml
index c5f3154f587..c5f3154f587 100644
--- a/vendor/gitlab-ci-yml/Pages/Pelican.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Pelican.gitlab-ci.yml
diff --git a/lib/gitlab/ci/templates/Python.gitlab-ci.yml b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
new file mode 100644
index 00000000000..098abe4daf5
--- /dev/null
+++ b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
@@ -0,0 +1,51 @@
+# Official language image. Look for the different tagged releases at:
+# https://hub.docker.com/r/library/python/tags/
+image: python:latest
+
+# Change pip's cache directory to be inside the project directory since we can
+# only cache local items.
+variables:
+ PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
+
+# Pip's cache doesn't store the python packages
+# https://pip.pypa.io/en/stable/reference/pip_install/#caching
+#
+# If you want to also cache the installed packages, you have to install
+# them in a virtualenv and cache it as well.
+cache:
+ paths:
+ - .cache/pip
+ - venv/
+
+before_script:
+ - python -V # Print out python version for debugging
+ - pip install virtualenv
+ - virtualenv venv
+ - source venv/bin/activate
+
+test:
+ script:
+ - python setup.py test
+ - pip install tox flake8 # you can also use tox
+ - tox -e py36,flake8
+
+run:
+ script:
+ - python setup.py bdist_wheel
+ # an alternative approach is to install and run:
+ - pip install dist/*
+ # run the command here
+ artifacts:
+ paths:
+ - dist/*.whl
+
+pages:
+ script:
+ - pip install sphinx sphinx-rtd-theme
+ - cd doc ; make html
+ - mv build/html/ ../public/
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
diff --git a/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
index 93cb31f48c0..93cb31f48c0 100644
--- a/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml b/lib/gitlab/ci/templates/Rust.gitlab-ci.yml
index cab087c48c7..cab087c48c7 100644
--- a/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Rust.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/Scala.gitlab-ci.yml b/lib/gitlab/ci/templates/Scala.gitlab-ci.yml
index b4208ed9d7d..b4208ed9d7d 100644
--- a/vendor/gitlab-ci-yml/Scala.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Scala.gitlab-ci.yml
diff --git a/lib/gitlab/ci/templates/Swift.gitlab-ci.yml b/lib/gitlab/ci/templates/Swift.gitlab-ci.yml
new file mode 100644
index 00000000000..ba8a802ba4f
--- /dev/null
+++ b/lib/gitlab/ci/templates/Swift.gitlab-ci.yml
@@ -0,0 +1,30 @@
+# Lifted from: https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/
+# This file assumes an own GitLab CI runner, setup on a macOS system.
+stages:
+ - build
+ - archive
+
+build_project:
+ stage: build
+ script:
+ - xcodebuild clean -project ProjectName.xcodeproj -scheme SchemeName | xcpretty
+ - xcodebuild test -project ProjectName.xcodeproj -scheme SchemeName -destination 'platform=iOS Simulator,name=iPhone 8,OS=11.3' | xcpretty -s
+ tags:
+ - ios_11-3
+ - xcode_9-3
+ - macos_10-13
+
+archive_project:
+ stage: archive
+ script:
+ - xcodebuild clean archive -archivePath build/ProjectName -scheme SchemeName
+ - xcodebuild -exportArchive -exportFormat ipa -archivePath "build/ProjectName.xcarchive" -exportPath "build/ProjectName.ipa" -exportProvisioningProfile "ProvisioningProfileName"
+ only:
+ - master
+ artifacts:
+ paths:
+ - build/ProjectName.ipa
+ tags:
+ - ios_11-3
+ - xcode_9-3
+ - macos_10-13
diff --git a/vendor/gitlab-ci-yml/Terraform.gitlab-ci.yml b/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
index 7160fce26a8..7160fce26a8 100644
--- a/vendor/gitlab-ci-yml/Terraform.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
diff --git a/vendor/gitlab-ci-yml/dotNET.gitlab-ci.yml b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml
index fc3d4ecdbba..fc3d4ecdbba 100644
--- a/vendor/gitlab-ci-yml/dotNET.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml
diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb
index 93e219a21f9..8eccd262db9 100644
--- a/lib/gitlab/ci/trace.rb
+++ b/lib/gitlab/ci/trace.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Trace
diff --git a/lib/gitlab/ci/trace/chunked_io.rb b/lib/gitlab/ci/trace/chunked_io.rb
index bfe0c2a2c26..e9b3199d56e 100644
--- a/lib/gitlab/ci/trace/chunked_io.rb
+++ b/lib/gitlab/ci/trace/chunked_io.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# This class is compatible with IO class (https://ruby-doc.org/core-2.3.1/IO.html)
# source: https://gitlab.com/snippets/1685610
@@ -66,8 +68,8 @@ module Gitlab
end
end
- def read(length = nil, outbuf = "")
- out = ""
+ def read(length = nil, outbuf = nil)
+ out = []
length ||= size - tell
@@ -83,17 +85,18 @@ module Gitlab
length -= chunk_data.bytesize
end
+ out = out.join
+
# If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
if outbuf
- outbuf.slice!(0, outbuf.bytesize)
- outbuf << out
+ outbuf.replace(out)
end
out
end
def readline
- out = ""
+ out = []
until eof?
data = chunk_slice_from_offset
@@ -109,7 +112,7 @@ module Gitlab
end
end
- out
+ out.join
end
def write(data)
@@ -133,6 +136,7 @@ module Gitlab
invalidate_chunk_cache
end
+ # rubocop: disable CodeReuse/ActiveRecord
def truncate(offset)
raise ArgumentError, 'Outside of file' if offset > size || offset < 0
return if offset == size # Skip the following process as it doesn't affect anything
@@ -148,6 +152,7 @@ module Gitlab
ensure
invalidate_chunk_cache
end
+ # rubocop: enable CodeReuse/ActiveRecord
def flush
# no-op
@@ -206,9 +211,11 @@ module Gitlab
@chunks_cache = []
end
+ # rubocop: disable CodeReuse/ActiveRecord
def current_chunk
@chunks_cache[chunk_index] ||= trace_chunks.find_by(chunk_index: chunk_index)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def build_chunk
@chunks_cache[chunk_index] = ::Ci::BuildTraceChunk.new(build: build, chunk_index: chunk_index)
@@ -218,13 +225,17 @@ module Gitlab
current_chunk || build_chunk
end
+ # rubocop: disable CodeReuse/ActiveRecord
def trace_chunks
::Ci::BuildTraceChunk.where(build: build)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def calculate_size
trace_chunks.order(chunk_index: :desc).first.try(&:end_offset).to_i
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/ci/trace/section_parser.rb b/lib/gitlab/ci/trace/section_parser.rb
index c09089d6475..f33f8cc56c1 100644
--- a/lib/gitlab/ci/trace/section_parser.rb
+++ b/lib/gitlab/ci/trace/section_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Trace
diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb
index a71040e5e56..bd40fdf59b1 100644
--- a/lib/gitlab/ci/trace/stream.rb
+++ b/lib/gitlab/ci/trace/stream.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class Trace
@@ -129,8 +131,7 @@ module Gitlab
debris = ''
until (buf = read_backward(BUFFER_SIZE)).empty?
- buf += debris
- debris, *lines = buf.each_line.to_a
+ debris, *lines = (buf + debris).each_line.to_a
lines.reverse_each do |line|
yield(line.force_encoding(Encoding.default_external))
end
diff --git a/lib/gitlab/ci/variables/collection.rb b/lib/gitlab/ci/variables/collection.rb
index ad30b3f427c..a7b4e0348c2 100644
--- a/lib/gitlab/ci/variables/collection.rb
+++ b/lib/gitlab/ci/variables/collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Variables
diff --git a/lib/gitlab/ci/variables/collection/item.rb b/lib/gitlab/ci/variables/collection/item.rb
index 7da6d09d440..cf8958e34c2 100644
--- a/lib/gitlab/ci/variables/collection/item.rb
+++ b/lib/gitlab/ci/variables/collection/item.rb
@@ -1,11 +1,13 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
module Variables
class Collection
class Item
def initialize(key:, value:, public: true, file: false)
- raise ArgumentError, "`value` must be of type String, while it was: #{value.class}" unless
- value.is_a?(String) || value.nil?
+ raise ArgumentError, "`#{key}` must be of type String, while it was: #{value.class}" unless
+ value.is_a?(String)
@variable = {
key: key, value: value, public: public, file: file
diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb
index e829f2a95f8..e6ec400e476 100644
--- a/lib/gitlab/ci/yaml_processor.rb
+++ b/lib/gitlab/ci/yaml_processor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Ci
class YamlProcessor
@@ -16,7 +18,7 @@ module Gitlab
end
initial_parsing
- rescue Gitlab::Ci::Config::Loader::FormatError => e
+ rescue Gitlab::Ci::Config::ConfigError => e
raise ValidationError, e.message
end
@@ -49,7 +51,10 @@ module Gitlab
script: job[:script],
after_script: job[:after_script],
environment: job[:environment],
- retry: job[:retry]
+ retry: job[:retry],
+ parallel: job[:parallel],
+ instance: job[:instance],
+ start_in: job[:start_in]
}.compact }
end
@@ -101,7 +106,7 @@ module Gitlab
##
# Jobs
#
- @jobs = @ci_config.jobs
+ @jobs = Ci::Config::Normalizer.new(@ci_config.jobs).normalize_jobs
@jobs.each do |name, job|
# logical validation for job
diff --git a/lib/gitlab/ci_access.rb b/lib/gitlab/ci_access.rb
index def1373d8cf..d5d3eb804ae 100644
--- a/lib/gitlab/ci_access.rb
+++ b/lib/gitlab/ci_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# For backwards compatibility, generic CI (which is a build without a user) is
# allowed to :build_download_code without any other checks.
diff --git a/lib/gitlab/cleanup/project_uploads.rb b/lib/gitlab/cleanup/project_uploads.rb
index f55ab535efe..82a405362c2 100644
--- a/lib/gitlab/cleanup/project_uploads.rb
+++ b/lib/gitlab/cleanup/project_uploads.rb
@@ -38,6 +38,7 @@ module Gitlab
end
# Accepts a path in the form of "#{hex_secret}/#{filename}"
+ # rubocop: disable CodeReuse/ActiveRecord
def find_correct_path(upload_path)
upload = Upload.find_by(uploader: 'FileUploader', path: upload_path)
return unless upload && upload.local? && upload.model
@@ -52,6 +53,7 @@ module Gitlab
# I.e. the project record might be missing, which raises an exception.
nil
end
+ # rubocop: enable CodeReuse/ActiveRecord
def move_to_lost_and_found(path, dry_run)
new_path = path.sub(/\A#{ProjectUploadFileFinder::ABSOLUTE_UPLOAD_DIR}/, LOST_AND_FOUND)
@@ -107,18 +109,22 @@ module Gitlab
new(path_matched[1], path_matched[2])
end
+ # rubocop: disable CodeReuse/ActiveRecord
def orphan?
return true if full_path.nil? || upload_path.nil?
# It's possible to reduce to one query, but `where_full_path_in` is complex
!Upload.exists?(path: upload_path, model_id: project_id, model_type: 'Project', uploader: 'FileUploader')
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ # rubocop: disable CodeReuse/ActiveRecord
def project_id
@project_id ||= Project.where_full_path_in([full_path]).pluck(:id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/cleanup/remote_uploads.rb b/lib/gitlab/cleanup/remote_uploads.rb
index 45a5aea4fcd..eba1faacc3a 100644
--- a/lib/gitlab/cleanup/remote_uploads.rb
+++ b/lib/gitlab/cleanup/remote_uploads.rb
@@ -33,6 +33,7 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def each_orphan_file
# we want to skip files already moved to lost_and_found directory
lost_dir_match = "^#{lost_and_found_dir}\/"
@@ -50,6 +51,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def move_to_lost_and_found(file)
new_path = "#{lost_and_found_dir}/#{file.key}"
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index 7e7aaeeaa17..4ba921569ad 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class ClosingIssueExtractor
ISSUE_CLOSING_REGEX = begin
diff --git a/lib/gitlab/cluster/lifecycle_events.rb b/lib/gitlab/cluster/lifecycle_events.rb
new file mode 100644
index 00000000000..b05dca409d1
--- /dev/null
+++ b/lib/gitlab/cluster/lifecycle_events.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cluster
+ #
+ # LifecycleEvents lets Rails initializers register application startup hooks
+ # that are sensitive to forking. For example, to defer the creation of
+ # watchdog threads. This lets us abstract away the Unix process
+ # lifecycles of Unicorn, Sidekiq, Puma, Puma Cluster, etc.
+ #
+ # We have three lifecycle events.
+ #
+ # - before_fork (only in forking processes)
+ # - worker_start
+ # - before_master_restart (only in forking processes)
+ #
+ # Blocks will be executed in the order in which they are registered.
+ #
+ class LifecycleEvents
+ class << self
+ #
+ # Hook registration methods (called from initializers)
+ #
+ def on_worker_start(&block)
+ if in_clustered_environment?
+ # Defer block execution
+ (@worker_start_hooks ||= []) << block
+ else
+ yield
+ end
+ end
+
+ def on_before_fork(&block)
+ return unless in_clustered_environment?
+
+ # Defer block execution
+ (@before_fork_hooks ||= []) << block
+ end
+
+ def on_master_restart(&block)
+ return unless in_clustered_environment?
+
+ # Defer block execution
+ (@master_restart_hooks ||= []) << block
+ end
+
+ #
+ # Lifecycle integration methods (called from unicorn.rb, puma.rb, etc.)
+ #
+ def do_worker_start
+ @worker_start_hooks&.each do |block|
+ block.call
+ end
+ end
+
+ def do_before_fork
+ @before_fork_hooks&.each do |block|
+ block.call
+ end
+ end
+
+ def do_master_restart
+ @master_restart_hooks && @master_restart_hooks.each do |block|
+ block.call
+ end
+ end
+
+ # Puma doesn't use singletons (which is good) but
+ # this means we need to pass through whether the
+ # puma server is running in single mode or cluster mode
+ def set_puma_options(options)
+ @puma_options = options
+ end
+
+ private
+
+ def in_clustered_environment?
+ # Sidekiq doesn't fork
+ return false if Sidekiq.server?
+
+ # Unicorn always forks
+ return true if defined?(::Unicorn)
+
+ # Puma sometimes forks
+ return true if in_clustered_puma?
+
+ # Default assumption is that we don't fork
+ false
+ end
+
+ def in_clustered_puma?
+ return false unless defined?(::Puma)
+
+ @puma_options && @puma_options[:workers] && @puma_options[:workers] > 0
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cluster/puma_worker_killer_initializer.rb b/lib/gitlab/cluster/puma_worker_killer_initializer.rb
new file mode 100644
index 00000000000..4ed9a9a02ab
--- /dev/null
+++ b/lib/gitlab/cluster/puma_worker_killer_initializer.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cluster
+ class PumaWorkerKillerInitializer
+ def self.start(puma_options, puma_per_worker_max_memory_mb: 650)
+ require 'puma_worker_killer'
+
+ PumaWorkerKiller.config do |config|
+ # Note! ram is expressed in megabytes (whereas GITLAB_UNICORN_MEMORY_MAX is in bytes)
+ # Importantly RAM is for _all_workers (ie, the cluster),
+ # not each worker as is the case with GITLAB_UNICORN_MEMORY_MAX
+ worker_count = puma_options[:workers] || 1
+ # The Puma Worker Killer checks the total RAM used by both the master
+ # and worker processes. Bump the limits to N+1 instead of N workers
+ # to account for this:
+ # https://github.com/schneems/puma_worker_killer/blob/v0.1.0/lib/puma_worker_killer/puma_memory.rb#L57
+ config.ram = (worker_count + 1) * puma_per_worker_max_memory_mb
+
+ config.frequency = 20 # seconds
+
+ # We just want to limit to a fixed maximum, unrelated to the total amount
+ # of available RAM.
+ config.percent_usage = 0.98
+
+ # Ideally we'll never hit the maximum amount of memory. If so the worker
+ # is restarted already, thus periodically restarting workers shouldn't be
+ # needed.
+ config.rolling_restart_frequency = false
+ end
+
+ PumaWorkerKiller.start
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/color_schemes.rb b/lib/gitlab/color_schemes.rb
index 9c4664df903..a5e4065cf09 100644
--- a/lib/gitlab/color_schemes.rb
+++ b/lib/gitlab/color_schemes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# Module containing GitLab's syntax color scheme definitions and helper
# methods for accessing them.
diff --git a/lib/gitlab/config_helper.rb b/lib/gitlab/config_helper.rb
index 41880069e4c..b7aa03384b7 100644
--- a/lib/gitlab/config_helper.rb
+++ b/lib/gitlab/config_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab::ConfigHelper
def gitlab_config_features
Gitlab.config.gitlab.default_projects_features
diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb
index 2a0cb640a14..0ca99506311 100644
--- a/lib/gitlab/conflict/file.rb
+++ b/lib/gitlab/conflict/file.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
module Gitlab
module Conflict
class File
include Gitlab::Routing
include IconsHelper
+ include Gitlab::Utils::StrongMemoize
CONTEXT_LINES = 3
@@ -30,11 +33,8 @@ module Gitlab
end
def highlight_lines!
- their_file = lines.reject { |line| line.type == 'new' }.map(&:text).join("\n")
- our_file = lines.reject { |line| line.type == 'old' }.map(&:text).join("\n")
-
- their_highlight = Gitlab::Highlight.highlight(their_path, their_file, repository: repository).lines
- our_highlight = Gitlab::Highlight.highlight(our_path, our_file, repository: repository).lines
+ their_highlight = Gitlab::Highlight.highlight(their_path, their_lines, language: their_language).lines
+ our_highlight = Gitlab::Highlight.highlight(our_path, our_lines, language: our_language).lines
lines.each do |line|
line.rich_text =
@@ -158,7 +158,6 @@ module Gitlab
json_hash.tap do |json_hash|
if opts[:full_content]
json_hash[:content] = content
- json_hash[:blob_ace_mode] = our_blob && our_blob.language.try(:ace_mode)
else
json_hash[:sections] = sections if type.text?
json_hash[:type] = type
@@ -183,6 +182,34 @@ module Gitlab
raw_line[:line_new], parent_file: self)
end
end
+
+ def their_language
+ strong_memoize(:their_language) do
+ repository.gitattribute(their_path, 'gitlab-language')
+ end
+ end
+
+ def our_language
+ strong_memoize(:our_language) do
+ if our_path == their_path
+ their_language
+ else
+ repository.gitattribute(our_path, 'gitlab-language')
+ end
+ end
+ end
+
+ def their_lines
+ strong_memoize(:their_lines) do
+ lines.reject { |line| line.type == 'new' }.map(&:text).join("\n")
+ end
+ end
+
+ def our_lines
+ strong_memoize(:our_lines) do
+ lines.reject { |line| line.type == 'old' }.map(&:text).join("\n")
+ end
+ end
end
end
end
diff --git a/lib/gitlab/conflict/file_collection.rb b/lib/gitlab/conflict/file_collection.rb
index 65a65b67975..53406af2c4e 100644
--- a/lib/gitlab/conflict/file_collection.rb
+++ b/lib/gitlab/conflict/file_collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Conflict
class FileCollection
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 4c28489f45a..5ed6427072a 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class ContributionsCalendar
attr_reader :contributor
@@ -7,9 +9,14 @@ module Gitlab
def initialize(contributor, current_user = nil)
@contributor = contributor
@current_user = current_user
- @projects = ContributedProjectsFinder.new(contributor).execute(current_user)
+ @projects = if @contributor.include_private_contributions?
+ ContributedProjectsFinder.new(@contributor).execute(@contributor)
+ else
+ ContributedProjectsFinder.new(contributor).execute(current_user)
+ end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def activity_dates
return @activity_dates if @activity_dates.present?
@@ -25,25 +32,25 @@ module Gitlab
note_events = event_counts(date_from, :merge_requests)
.having(action: [Event::COMMENTED])
- union = Gitlab::SQL::Union.new([repo_events, issue_events, mr_events, note_events])
- events = Event.find_by_sql(union.to_sql).map(&:attributes)
+ events = Event
+ .from_union([repo_events, issue_events, mr_events, note_events])
+ .map(&:attributes)
@activity_dates = events.each_with_object(Hash.new {|h, k| h[k] = 0 }) do |event, activities|
activities[event["date"]] += event["total_amount"]
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def events_by_date(date)
return Event.none unless can_read_cross_project?
- events = Event.contributions.where(author_id: contributor.id)
+ Event.contributions.where(author_id: contributor.id)
.where(created_at: date.beginning_of_day..date.end_of_day)
.where(project_id: projects)
-
- # Use visible_to_user? instead of the complicated logic in activity_dates
- # because we're only viewing the events for a single day.
- events.select { |event| event.visible_to_user?(current_user) }
end
+ # rubocop: enable CodeReuse/ActiveRecord
def starting_year
1.year.ago.year
@@ -59,13 +66,14 @@ module Gitlab
Ability.allowed?(current_user, :read_cross_project)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def event_counts(date_from, feature)
t = Event.arel_table
# re-running the contributed projects query in each union is expensive, so
# use IN(project_ids...) instead. It's the intersection of two users so
# the list will be (relatively) short
- @contributed_project_ids ||= projects.uniq.pluck(:id)
+ @contributed_project_ids ||= projects.distinct.pluck(:id)
authed_projects = Project.where(id: @contributed_project_ids)
.with_feature_available_for_user(feature, current_user)
.reorder(nil)
@@ -87,5 +95,6 @@ module Gitlab
.where(conditions)
.where("events.project_id in (#{authed_projects.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/lib/gitlab/contributor.rb b/lib/gitlab/contributor.rb
index c41e92b620f..d74d5a86aa0 100644
--- a/lib/gitlab/contributor.rb
+++ b/lib/gitlab/contributor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class Contributor
attr_accessor :email, :name, :commits, :additions, :deletions
diff --git a/lib/gitlab/cross_project_access.rb b/lib/gitlab/cross_project_access.rb
index 6eaed51b64c..4ddc7e02d1b 100644
--- a/lib/gitlab/cross_project_access.rb
+++ b/lib/gitlab/cross_project_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class CrossProjectAccess
class << self
diff --git a/lib/gitlab/cross_project_access/check_collection.rb b/lib/gitlab/cross_project_access/check_collection.rb
index 88376232065..55527ba5e87 100644
--- a/lib/gitlab/cross_project_access/check_collection.rb
+++ b/lib/gitlab/cross_project_access/check_collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class CrossProjectAccess
class CheckCollection
diff --git a/lib/gitlab/cross_project_access/check_info.rb b/lib/gitlab/cross_project_access/check_info.rb
index e8a845c7f1e..2a9eacad680 100644
--- a/lib/gitlab/cross_project_access/check_info.rb
+++ b/lib/gitlab/cross_project_access/check_info.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class CrossProjectAccess
class CheckInfo
diff --git a/lib/gitlab/cross_project_access/class_methods.rb b/lib/gitlab/cross_project_access/class_methods.rb
index 90eac94800c..64ad30794d3 100644
--- a/lib/gitlab/cross_project_access/class_methods.rb
+++ b/lib/gitlab/cross_project_access/class_methods.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class CrossProjectAccess
module ClassMethods
diff --git a/lib/gitlab/crypto_helper.rb b/lib/gitlab/crypto_helper.rb
new file mode 100644
index 00000000000..68d0b5d8f8a
--- /dev/null
+++ b/lib/gitlab/crypto_helper.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module CryptoHelper
+ extend self
+
+ AES256_GCM_OPTIONS = {
+ algorithm: 'aes-256-gcm',
+ key: Settings.attr_encrypted_db_key_base_truncated,
+ iv: Settings.attr_encrypted_db_key_base_truncated[0..11]
+ }.freeze
+
+ def sha256(value)
+ salt = Settings.attr_encrypted_db_key_base_truncated
+ ::Digest::SHA256.base64digest("#{value}#{salt}")
+ end
+
+ def aes256_gcm_encrypt(value)
+ encrypted_token = Encryptor.encrypt(AES256_GCM_OPTIONS.merge(value: value))
+ Base64.encode64(encrypted_token)
+ end
+
+ def aes256_gcm_decrypt(value)
+ return unless value
+
+ encrypted_token = Base64.decode64(value)
+ Encryptor.decrypt(AES256_GCM_OPTIONS.merge(value: encrypted_token))
+ end
+ end
+end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 9147ef401da..477f9101e98 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -1,18 +1,20 @@
+# frozen_string_literal: true
+
module Gitlab
module CurrentSettings
class << self
def current_application_settings
- if RequestStore.active?
- RequestStore.fetch(:current_application_settings) { ensure_application_settings! }
- else
- ensure_application_settings!
- end
+ Gitlab::SafeRequestStore.fetch(:current_application_settings) { ensure_application_settings! }
end
def fake_application_settings(attributes = {})
Gitlab::FakeApplicationSettings.new(::ApplicationSetting.defaults.merge(attributes || {}))
end
+ def clear_in_memory_application_settings!
+ @in_memory_application_settings = nil
+ end
+
def method_missing(name, *args, &block)
current_application_settings.send(name, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb
index e3e3767cc75..304d60996a6 100644
--- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class BaseEventFetcher
diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb
index 86d708be0d6..36231b187cd 100644
--- a/lib/gitlab/cycle_analytics/base_query.rb
+++ b/lib/gitlab/cycle_analytics/base_query.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module BaseQuery
diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb
index 038d5a19bc4..e2d6a301734 100644
--- a/lib/gitlab/cycle_analytics/base_stage.rb
+++ b/lib/gitlab/cycle_analytics/base_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class BaseStage
diff --git a/lib/gitlab/cycle_analytics/code_event_fetcher.rb b/lib/gitlab/cycle_analytics/code_event_fetcher.rb
index 06357c9b377..591db3c35e6 100644
--- a/lib/gitlab/cycle_analytics/code_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/code_event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class CodeEventFetcher < BaseEventFetcher
diff --git a/lib/gitlab/cycle_analytics/code_stage.rb b/lib/gitlab/cycle_analytics/code_stage.rb
index 5f9dc9a4303..2e5f9ef5a40 100644
--- a/lib/gitlab/cycle_analytics/code_stage.rb
+++ b/lib/gitlab/cycle_analytics/code_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class CodeStage < BaseStage
diff --git a/lib/gitlab/cycle_analytics/event_fetcher.rb b/lib/gitlab/cycle_analytics/event_fetcher.rb
index 50e126cf00b..98a30a8fc97 100644
--- a/lib/gitlab/cycle_analytics/event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module EventFetcher
diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
index 1754f91dccb..30c6ead8968 100644
--- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class IssueEventFetcher < BaseEventFetcher
diff --git a/lib/gitlab/cycle_analytics/issue_stage.rb b/lib/gitlab/cycle_analytics/issue_stage.rb
index 7b03811efb2..4eae2da512c 100644
--- a/lib/gitlab/cycle_analytics/issue_stage.rb
+++ b/lib/gitlab/cycle_analytics/issue_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class IssueStage < BaseStage
diff --git a/lib/gitlab/cycle_analytics/metrics_tables.rb b/lib/gitlab/cycle_analytics/metrics_tables.rb
index f5d08c0b658..3e0302d308d 100644
--- a/lib/gitlab/cycle_analytics/metrics_tables.rb
+++ b/lib/gitlab/cycle_analytics/metrics_tables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module MetricsTables
diff --git a/lib/gitlab/cycle_analytics/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb
index 1e11e84a9cb..afefd09b614 100644
--- a/lib/gitlab/cycle_analytics/permissions.rb
+++ b/lib/gitlab/cycle_analytics/permissions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class Permissions
diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
index 086203b9ccc..db8ac3becea 100644
--- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class PlanEventFetcher < BaseEventFetcher
diff --git a/lib/gitlab/cycle_analytics/plan_stage.rb b/lib/gitlab/cycle_analytics/plan_stage.rb
index 1a0afb56b4f..513e4575be0 100644
--- a/lib/gitlab/cycle_analytics/plan_stage.rb
+++ b/lib/gitlab/cycle_analytics/plan_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class PlanStage < BaseStage
diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb
index 0fa2e87f673..6681cb42c90 100644
--- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class ProductionEventFetcher < IssueEventFetcher
diff --git a/lib/gitlab/cycle_analytics/production_helper.rb b/lib/gitlab/cycle_analytics/production_helper.rb
index d0ca62e46e4..aff65b150fb 100644
--- a/lib/gitlab/cycle_analytics/production_helper.rb
+++ b/lib/gitlab/cycle_analytics/production_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module ProductionHelper
diff --git a/lib/gitlab/cycle_analytics/production_stage.rb b/lib/gitlab/cycle_analytics/production_stage.rb
index 0fa8a65cb99..6fd7214dce7 100644
--- a/lib/gitlab/cycle_analytics/production_stage.rb
+++ b/lib/gitlab/cycle_analytics/production_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class ProductionStage < BaseStage
diff --git a/lib/gitlab/cycle_analytics/review_event_fetcher.rb b/lib/gitlab/cycle_analytics/review_event_fetcher.rb
index dada819a2a8..de100295281 100644
--- a/lib/gitlab/cycle_analytics/review_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/review_event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class ReviewEventFetcher < BaseEventFetcher
diff --git a/lib/gitlab/cycle_analytics/review_stage.rb b/lib/gitlab/cycle_analytics/review_stage.rb
index cfbbdc43fd9..294b656bc55 100644
--- a/lib/gitlab/cycle_analytics/review_stage.rb
+++ b/lib/gitlab/cycle_analytics/review_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class ReviewStage < BaseStage
diff --git a/lib/gitlab/cycle_analytics/stage.rb b/lib/gitlab/cycle_analytics/stage.rb
index 28e0455df59..1bd40a7aa18 100644
--- a/lib/gitlab/cycle_analytics/stage.rb
+++ b/lib/gitlab/cycle_analytics/stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module Stage
diff --git a/lib/gitlab/cycle_analytics/stage_summary.rb b/lib/gitlab/cycle_analytics/stage_summary.rb
index fc77bd86097..5198dd5b4eb 100644
--- a/lib/gitlab/cycle_analytics/stage_summary.rb
+++ b/lib/gitlab/cycle_analytics/stage_summary.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class StageSummary
diff --git a/lib/gitlab/cycle_analytics/staging_event_fetcher.rb b/lib/gitlab/cycle_analytics/staging_event_fetcher.rb
index 2f014153ca5..70ce82383b3 100644
--- a/lib/gitlab/cycle_analytics/staging_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/staging_event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class StagingEventFetcher < BaseEventFetcher
diff --git a/lib/gitlab/cycle_analytics/staging_stage.rb b/lib/gitlab/cycle_analytics/staging_stage.rb
index d5684bb9201..dbc2414ff66 100644
--- a/lib/gitlab/cycle_analytics/staging_stage.rb
+++ b/lib/gitlab/cycle_analytics/staging_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class StagingStage < BaseStage
diff --git a/lib/gitlab/cycle_analytics/summary/base.rb b/lib/gitlab/cycle_analytics/summary/base.rb
index a917ddccac7..709221c648e 100644
--- a/lib/gitlab/cycle_analytics/summary/base.rb
+++ b/lib/gitlab/cycle_analytics/summary/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module Summary
diff --git a/lib/gitlab/cycle_analytics/summary/commit.rb b/lib/gitlab/cycle_analytics/summary/commit.rb
index 550c1755a71..f0019b26fa2 100644
--- a/lib/gitlab/cycle_analytics/summary/commit.rb
+++ b/lib/gitlab/cycle_analytics/summary/commit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module Summary
diff --git a/lib/gitlab/cycle_analytics/summary/deploy.rb b/lib/gitlab/cycle_analytics/summary/deploy.rb
index 099d798aac6..3b56dc2a7bc 100644
--- a/lib/gitlab/cycle_analytics/summary/deploy.rb
+++ b/lib/gitlab/cycle_analytics/summary/deploy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module Summary
diff --git a/lib/gitlab/cycle_analytics/summary/issue.rb b/lib/gitlab/cycle_analytics/summary/issue.rb
index 9bbf7a2685f..51695c86192 100644
--- a/lib/gitlab/cycle_analytics/summary/issue.rb
+++ b/lib/gitlab/cycle_analytics/summary/issue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
module Summary
diff --git a/lib/gitlab/cycle_analytics/test_event_fetcher.rb b/lib/gitlab/cycle_analytics/test_event_fetcher.rb
index a2589c6601a..4d5ea5b7c34 100644
--- a/lib/gitlab/cycle_analytics/test_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/test_event_fetcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class TestEventFetcher < StagingEventFetcher
diff --git a/lib/gitlab/cycle_analytics/test_stage.rb b/lib/gitlab/cycle_analytics/test_stage.rb
index 0e9d235ca79..c31b664148b 100644
--- a/lib/gitlab/cycle_analytics/test_stage.rb
+++ b/lib/gitlab/cycle_analytics/test_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class TestStage < BaseStage
diff --git a/lib/gitlab/cycle_analytics/updater.rb b/lib/gitlab/cycle_analytics/updater.rb
index 953268ebd46..c642809a792 100644
--- a/lib/gitlab/cycle_analytics/updater.rb
+++ b/lib/gitlab/cycle_analytics/updater.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class Updater
diff --git a/lib/gitlab/cycle_analytics/usage_data.rb b/lib/gitlab/cycle_analytics/usage_data.rb
index 5122e3417ca..913ee373f54 100644
--- a/lib/gitlab/cycle_analytics/usage_data.rb
+++ b/lib/gitlab/cycle_analytics/usage_data.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module CycleAnalytics
class UsageData
diff --git a/lib/gitlab/daemon.rb b/lib/gitlab/daemon.rb
index bd14c7eece3..6d5fc4219fb 100644
--- a/lib/gitlab/daemon.rb
+++ b/lib/gitlab/daemon.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class Daemon
def self.initialize_instance(*args)
diff --git a/lib/gitlab/data_builder/build.rb b/lib/gitlab/data_builder/build.rb
index 2f1445a050a..3407380127e 100644
--- a/lib/gitlab/data_builder/build.rb
+++ b/lib/gitlab/data_builder/build.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DataBuilder
module Build
@@ -28,6 +30,7 @@ module Gitlab
build_finished_at: build.finished_at,
build_duration: build.duration,
build_allow_failure: build.allow_failure,
+ build_failure_reason: build.failure_reason,
# TODO: do we still need it?
project_id: project.id,
diff --git a/lib/gitlab/data_builder/note.rb b/lib/gitlab/data_builder/note.rb
index f573368e572..65601dcdf31 100644
--- a/lib/gitlab/data_builder/note.rb
+++ b/lib/gitlab/data_builder/note.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DataBuilder
module Note
diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb
index eb246d393a1..76c8b4ec5c2 100644
--- a/lib/gitlab/data_builder/pipeline.rb
+++ b/lib/gitlab/data_builder/pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DataBuilder
module Pipeline
@@ -26,7 +28,8 @@ module Gitlab
stages: pipeline.stages_names,
created_at: pipeline.created_at,
finished_at: pipeline.finished_at,
- duration: pipeline.duration
+ duration: pipeline.duration,
+ variables: pipeline.variables.map(&:hook_attrs)
}
end
diff --git a/lib/gitlab/data_builder/push.rb b/lib/gitlab/data_builder/push.rb
index c169c8fe135..9bf2f9291a8 100644
--- a/lib/gitlab/data_builder/push.rb
+++ b/lib/gitlab/data_builder/push.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DataBuilder
module Push
@@ -97,11 +99,15 @@ module Gitlab
}
end
- # This method provide a sample data generated with
+ # This method provides a sample data generated with
# existing project and commits to test webhooks
def build_sample(project, user)
+ # Use sample data if repo has no commit
+ # (expect the case of test service configuration settings)
+ return sample_data if project.empty_repo?
+
ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
- commits = project.repository.commits(project.default_branch.to_s, limit: 3) rescue []
+ commits = project.repository.commits(project.default_branch.to_s, limit: 3)
build(project, user, commits.last&.id, commits.first&.id, ref, commits)
end
diff --git a/lib/gitlab/data_builder/repository.rb b/lib/gitlab/data_builder/repository.rb
index c9c13ec6487..0e627fd623e 100644
--- a/lib/gitlab/data_builder/repository.rb
+++ b/lib/gitlab/data_builder/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DataBuilder
module Repository
diff --git a/lib/gitlab/data_builder/wiki_page.rb b/lib/gitlab/data_builder/wiki_page.rb
index 226974b698c..9368446fa59 100644
--- a/lib/gitlab/data_builder/wiki_page.rb
+++ b/lib/gitlab/data_builder/wiki_page.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DataBuilder
module WikiPage
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 8eacad078c8..68ed53cf64a 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
# The max value of INTEGER type is the same between MySQL and PostgreSQL:
@@ -99,11 +101,11 @@ module Gitlab
order = "#{field} #{direction}"
if postgresql?
- order << ' NULLS LAST'
+ order = "#{order} NULLS LAST"
else
# `field IS NULL` will be `0` for non-NULL columns and `1` for NULL
# columns. In the (default) ascending order, `0` comes first.
- order.prepend("#{field} IS NULL, ") if direction == 'ASC'
+ order = "#{field} IS NULL, #{order}" if direction == 'ASC'
end
order
@@ -113,11 +115,11 @@ module Gitlab
order = "#{field} #{direction}"
if postgresql?
- order << ' NULLS FIRST'
+ order = "#{order} NULLS FIRST"
else
# `field IS NULL` will be `0` for non-NULL columns and `1` for NULL
# columns. In the (default) ascending order, `0` comes first.
- order.prepend("#{field} IS NULL, ") if direction == 'DESC'
+ order = "#{field} IS NULL, #{order}" if direction == 'DESC'
end
order
@@ -184,7 +186,7 @@ module Gitlab
EOF
if return_ids
- sql << 'RETURNING id'
+ sql = "#{sql}RETURNING id"
end
result = connection.execute(sql)
@@ -249,5 +251,21 @@ module Gitlab
end
private_class_method :database_version
+
+ def self.add_post_migrate_path_to_rails(force: false)
+ return if ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS'] && !force
+
+ Rails.application.config.paths['db'].each do |db_path|
+ path = Rails.root.join(db_path, 'post_migrate').to_s
+
+ unless Rails.application.config.paths['db/migrate'].include? path
+ Rails.application.config.paths['db/migrate'] << path
+
+ # Rails memoizes migrations at certain points where it won't read the above
+ # path just yet. As such we must also update the following list of paths.
+ ActiveRecord::Migrator.migrations_paths << path
+ end
+ end
+ end
end
end
diff --git a/lib/gitlab/database/arel_methods.rb b/lib/gitlab/database/arel_methods.rb
index d7e3ce08b32..991e4152dcb 100644
--- a/lib/gitlab/database/arel_methods.rb
+++ b/lib/gitlab/database/arel_methods.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
module ArelMethods
diff --git a/lib/gitlab/database/count.rb b/lib/gitlab/database/count.rb
index 5f549ed2b3c..ea6529e2dc4 100644
--- a/lib/gitlab/database/count.rb
+++ b/lib/gitlab/database/count.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# For large tables, PostgreSQL can take a long time to count rows due to MVCC.
# We can optimize this by using the reltuples count as described in https://wiki.postgresql.org/wiki/Slow_Counting.
module Gitlab
diff --git a/lib/gitlab/database/date_time.rb b/lib/gitlab/database/date_time.rb
index 25e56998038..79d2caff151 100644
--- a/lib/gitlab/database/date_time.rb
+++ b/lib/gitlab/database/date_time.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
module DateTime
diff --git a/lib/gitlab/database/grant.rb b/lib/gitlab/database/grant.rb
index d32837f5793..862ab96c887 100644
--- a/lib/gitlab/database/grant.rb
+++ b/lib/gitlab/database/grant.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
# Model that can be used for querying permissions of a SQL user.
class Grant < ActiveRecord::Base
+ include FromUnion
+
self.table_name =
if Database.postgresql?
'information_schema.role_table_grants'
@@ -42,9 +46,7 @@ module Gitlab
.where("GRANTEE = CONCAT('\\'', REPLACE(CURRENT_USER(), '@', '\\'@\\''), '\\'')")
]
- union = SQL::Union.new(queries).to_sql
-
- Grant.from("(#{union}) privs").any?
+ Grant.from_union(queries, alias_as: 'privs').any?
end
end
end
diff --git a/lib/gitlab/database/median.rb b/lib/gitlab/database/median.rb
index f64e3d53138..0da5119a3ed 100644
--- a/lib/gitlab/database/median.rb
+++ b/lib/gitlab/database/median.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# https://www.periscopedata.com/blog/medians-in-sql.html
module Gitlab
module Database
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index f39b3b6eb5b..134d1e7a724 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
module MigrationHelpers
@@ -58,7 +60,6 @@ module Gitlab
if Database.postgresql?
options = options.merge({ algorithm: :concurrently })
- disable_statement_timeout
end
if index_exists?(table_name, column_name, options)
@@ -66,7 +67,9 @@ module Gitlab
return
end
- add_index(table_name, column_name, options)
+ disable_statement_timeout do
+ add_index(table_name, column_name, options)
+ end
end
# Removes an existed index, concurrently when supported
@@ -87,7 +90,6 @@ module Gitlab
if supports_drop_index_concurrently?
options = options.merge({ algorithm: :concurrently })
- disable_statement_timeout
end
unless index_exists?(table_name, column_name, options)
@@ -95,7 +97,9 @@ module Gitlab
return
end
- remove_index(table_name, options.merge({ column: column_name }))
+ disable_statement_timeout do
+ remove_index(table_name, options.merge({ column: column_name }))
+ end
end
# Removes an existing index, concurrently when supported
@@ -116,7 +120,6 @@ module Gitlab
if supports_drop_index_concurrently?
options = options.merge({ algorithm: :concurrently })
- disable_statement_timeout
end
unless index_exists_by_name?(table_name, index_name)
@@ -124,7 +127,9 @@ module Gitlab
return
end
- remove_index(table_name, options.merge({ name: index_name }))
+ disable_statement_timeout do
+ remove_index(table_name, options.merge({ name: index_name }))
+ end
end
# Only available on Postgresql >= 9.2
@@ -171,8 +176,6 @@ module Gitlab
on_delete = 'SET NULL' if on_delete == :nullify
end
- disable_statement_timeout
-
key_name = concurrent_foreign_key_name(source, column)
unless foreign_key_exists?(source, target, column: column)
@@ -199,7 +202,9 @@ module Gitlab
# while running.
#
# Note this is a no-op in case the constraint is VALID already
- execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{key_name};")
+ disable_statement_timeout do
+ execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{key_name};")
+ end
end
def foreign_key_exists?(source, target = nil, column: nil)
@@ -224,8 +229,48 @@ module Gitlab
# Long-running migrations may take more than the timeout allowed by
# the database. Disable the session's statement timeout to ensure
# migrations don't get killed prematurely. (PostgreSQL only)
+ #
+ # There are two possible ways to disable the statement timeout:
+ #
+ # - Per transaction (this is the preferred and default mode)
+ # - Per connection (requires a cleanup after the execution)
+ #
+ # When using a per connection disable statement, code must be inside
+ # a block so we can automatically execute `RESET ALL` after block finishes
+ # otherwise the statement will still be disabled until connection is dropped
+ # or `RESET ALL` is executed
def disable_statement_timeout
- execute('SET statement_timeout TO 0') if Database.postgresql?
+ # bypass disabled_statement logic when not using postgres, but still execute block when one is given
+ unless Database.postgresql?
+ if block_given?
+ yield
+ end
+
+ return
+ end
+
+ if block_given?
+ begin
+ execute('SET statement_timeout TO 0')
+
+ yield
+ ensure
+ execute('RESET ALL')
+ end
+ else
+ unless transaction_open?
+ raise <<~ERROR
+ Cannot call disable_statement_timeout() without a transaction open or outside of a transaction block.
+ If you don't want to use a transaction wrap your code in a block call:
+
+ disable_statement_timeout { # code that requires disabled statement here }
+
+ This will make sure statement_timeout is disabled before and reset after the block execution is finished.
+ ERROR
+ end
+
+ execute('SET LOCAL statement_timeout TO 0')
+ end
end
def true_value
@@ -367,30 +412,30 @@ module Gitlab
'in the body of your migration class'
end
- disable_statement_timeout
-
- transaction do
- if limit
- add_column(table, column, type, default: nil, limit: limit)
- else
- add_column(table, column, type, default: nil)
+ disable_statement_timeout do
+ transaction do
+ if limit
+ add_column(table, column, type, default: nil, limit: limit)
+ else
+ add_column(table, column, type, default: nil)
+ end
+
+ # Changing the default before the update ensures any newly inserted
+ # rows already use the proper default value.
+ change_column_default(table, column, default)
end
- # Changing the default before the update ensures any newly inserted
- # rows already use the proper default value.
- change_column_default(table, column, default)
- end
-
- begin
- update_column_in_batches(table, column, default, &block)
+ begin
+ update_column_in_batches(table, column, default, &block)
- change_column_null(table, column, false) unless allow_null
- # We want to rescue _all_ exceptions here, even those that don't inherit
- # from StandardError.
- rescue Exception => error # rubocop: disable all
- remove_column(table, column)
+ change_column_null(table, column, false) unless allow_null
+ # We want to rescue _all_ exceptions here, even those that don't inherit
+ # from StandardError.
+ rescue Exception => error # rubocop: disable all
+ remove_column(table, column)
- raise error
+ raise error
+ end
end
end
@@ -836,7 +881,7 @@ module Gitlab
columns(table).find { |column| column.name == name }
end
- # This will replace the first occurance of a string in a column with
+ # This will replace the first occurrence of a string in a column with
# the replacement
# On postgresql we can use `regexp_replace` for that.
# On mysql we find the location of the pattern, and overwrite it
@@ -894,7 +939,7 @@ database (#{dbname}) using a super user and running:
For MySQL you instead need to run:
- GRANT ALL PRIVILEGES ON *.* TO #{user}@'%'
+ GRANT ALL PRIVILEGES ON #{dbname}.* TO #{user}@'%'
Both queries will grant the user super user permissions, ensuring you don't run
into similar problems in the future (e.g. when new tables are created).
@@ -1030,6 +1075,10 @@ into similar problems in the future (e.g. when new tables are created).
connection.select_value(index_sql).to_i > 0
end
+
+ def mysql_compatible_index_length
+ Gitlab::Database.mysql? ? 20 : nil
+ end
end
end
end
diff --git a/lib/gitlab/database/multi_threaded_migration.rb b/lib/gitlab/database/multi_threaded_migration.rb
index 7ae5a4c17c8..1d39a3d0b57 100644
--- a/lib/gitlab/database/multi_threaded_migration.rb
+++ b/lib/gitlab/database/multi_threaded_migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
module MultiThreadedMigration
diff --git a/lib/gitlab/database/read_only_relation.rb b/lib/gitlab/database/read_only_relation.rb
index 4571ad122ce..2362208e5dd 100644
--- a/lib/gitlab/database/read_only_relation.rb
+++ b/lib/gitlab/database/read_only_relation.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
# Module that can be injected into a ActiveRecord::Relation to make it
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1.rb
index f333ff22300..2314246da55 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This module can be included in migrations to make it easier to rename paths
# of `Namespace` & `Project` models certain paths would become `reserved`.
#
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb
index 26ae6966746..f1dc3ed74fe 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
module RenameReservedPathsMigration
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
index 14de28a1d08..a5b42bbfdd9 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
module RenameReservedPathsMigration
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
index 73971af6a74..6bbad707f0f 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
module RenameReservedPathsMigration
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb
index 827aeb12a02..580be9fe267 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
module RenameReservedPathsMigration
diff --git a/lib/gitlab/database/sha_attribute.rb b/lib/gitlab/database/sha_attribute.rb
index b2d8ee81977..6516d6e648d 100644
--- a/lib/gitlab/database/sha_attribute.rb
+++ b/lib/gitlab/database/sha_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Database
BINARY_TYPE =
diff --git a/lib/gitlab/database/subquery.rb b/lib/gitlab/database/subquery.rb
new file mode 100644
index 00000000000..36e4559b554
--- /dev/null
+++ b/lib/gitlab/database/subquery.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Subquery
+ class << self
+ def self_join(relation)
+ t = relation.arel_table
+ t2 = if !Gitlab.rails5?
+ relation.arel.as('t2')
+ else
+ # Work around a bug in Rails 5, where LIMIT causes trouble
+ # See https://gitlab.com/gitlab-org/gitlab-ce/issues/51729
+ r = relation.limit(nil).arel
+ r.take(relation.limit_value) if relation.limit_value
+ r.as('t2')
+ end
+
+ relation.unscoped.joins(t.join(t2).on(t[:id].eq(t2[:id])).join_sources.first)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb
index 3192bf6f667..c63d9e5bb71 100644
--- a/lib/gitlab/dependency_linker.rb
+++ b/lib/gitlab/dependency_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
LINKERS = [
diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb
index d2360583741..ac2efe598b4 100644
--- a/lib/gitlab/dependency_linker/base_linker.rb
+++ b/lib/gitlab/dependency_linker/base_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class BaseLinker
diff --git a/lib/gitlab/dependency_linker/cartfile_linker.rb b/lib/gitlab/dependency_linker/cartfile_linker.rb
index 4f69f2c4ab2..0e33f0956dd 100644
--- a/lib/gitlab/dependency_linker/cartfile_linker.rb
+++ b/lib/gitlab/dependency_linker/cartfile_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class CartfileLinker < MethodLinker
diff --git a/lib/gitlab/dependency_linker/cocoapods.rb b/lib/gitlab/dependency_linker/cocoapods.rb
index 2fbde7da1b4..38eabe303de 100644
--- a/lib/gitlab/dependency_linker/cocoapods.rb
+++ b/lib/gitlab/dependency_linker/cocoapods.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
module Cocoapods
diff --git a/lib/gitlab/dependency_linker/composer_json_linker.rb b/lib/gitlab/dependency_linker/composer_json_linker.rb
index cfd4ec15125..22d2bead891 100644
--- a/lib/gitlab/dependency_linker/composer_json_linker.rb
+++ b/lib/gitlab/dependency_linker/composer_json_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class ComposerJsonLinker < PackageJsonLinker
diff --git a/lib/gitlab/dependency_linker/gemfile_linker.rb b/lib/gitlab/dependency_linker/gemfile_linker.rb
index bfea836bcb2..8ab219c4962 100644
--- a/lib/gitlab/dependency_linker/gemfile_linker.rb
+++ b/lib/gitlab/dependency_linker/gemfile_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class GemfileLinker < MethodLinker
diff --git a/lib/gitlab/dependency_linker/gemspec_linker.rb b/lib/gitlab/dependency_linker/gemspec_linker.rb
index f1783ee2ab4..b924ea86d89 100644
--- a/lib/gitlab/dependency_linker/gemspec_linker.rb
+++ b/lib/gitlab/dependency_linker/gemspec_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class GemspecLinker < MethodLinker
diff --git a/lib/gitlab/dependency_linker/godeps_json_linker.rb b/lib/gitlab/dependency_linker/godeps_json_linker.rb
index fe091baee6d..d24c137793e 100644
--- a/lib/gitlab/dependency_linker/godeps_json_linker.rb
+++ b/lib/gitlab/dependency_linker/godeps_json_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class GodepsJsonLinker < JsonLinker
diff --git a/lib/gitlab/dependency_linker/json_linker.rb b/lib/gitlab/dependency_linker/json_linker.rb
index a8ef25233d8..298d214df61 100644
--- a/lib/gitlab/dependency_linker/json_linker.rb
+++ b/lib/gitlab/dependency_linker/json_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class JsonLinker < BaseLinker
diff --git a/lib/gitlab/dependency_linker/method_linker.rb b/lib/gitlab/dependency_linker/method_linker.rb
index 0ffa2a83c93..d4d85bb3390 100644
--- a/lib/gitlab/dependency_linker/method_linker.rb
+++ b/lib/gitlab/dependency_linker/method_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class MethodLinker < BaseLinker
diff --git a/lib/gitlab/dependency_linker/package_json_linker.rb b/lib/gitlab/dependency_linker/package_json_linker.rb
index 330c95f0880..578e25f806a 100644
--- a/lib/gitlab/dependency_linker/package_json_linker.rb
+++ b/lib/gitlab/dependency_linker/package_json_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class PackageJsonLinker < JsonLinker
diff --git a/lib/gitlab/dependency_linker/podfile_linker.rb b/lib/gitlab/dependency_linker/podfile_linker.rb
index 60ad166ea17..def9b04cca9 100644
--- a/lib/gitlab/dependency_linker/podfile_linker.rb
+++ b/lib/gitlab/dependency_linker/podfile_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class PodfileLinker < GemfileLinker
diff --git a/lib/gitlab/dependency_linker/podspec_json_linker.rb b/lib/gitlab/dependency_linker/podspec_json_linker.rb
index d82237ed3f1..1a2493e7cc0 100644
--- a/lib/gitlab/dependency_linker/podspec_json_linker.rb
+++ b/lib/gitlab/dependency_linker/podspec_json_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class PodspecJsonLinker < JsonLinker
diff --git a/lib/gitlab/dependency_linker/podspec_linker.rb b/lib/gitlab/dependency_linker/podspec_linker.rb
index 924e55e9820..6b1758c5a43 100644
--- a/lib/gitlab/dependency_linker/podspec_linker.rb
+++ b/lib/gitlab/dependency_linker/podspec_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class PodspecLinker < MethodLinker
diff --git a/lib/gitlab/dependency_linker/requirements_txt_linker.rb b/lib/gitlab/dependency_linker/requirements_txt_linker.rb
index 9c9620bc36a..f630c13b760 100644
--- a/lib/gitlab/dependency_linker/requirements_txt_linker.rb
+++ b/lib/gitlab/dependency_linker/requirements_txt_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module DependencyLinker
class RequirementsTxtLinker < BaseLinker
diff --git a/lib/gitlab/diff/diff_refs.rb b/lib/gitlab/diff/diff_refs.rb
index 81df47964be..d4823f60826 100644
--- a/lib/gitlab/diff/diff_refs.rb
+++ b/lib/gitlab/diff/diff_refs.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class DiffRefs
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index d16a55720b7..8ba44dff06f 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class File
@@ -20,11 +22,13 @@ module Gitlab
DiffViewer::Image
].sort_by { |v| v.binary? ? 0 : 1 }.freeze
- def initialize(diff, repository:, diff_refs: nil, fallback_diff_refs: nil)
+ def initialize(diff, repository:, diff_refs: nil, fallback_diff_refs: nil, stats: nil)
@diff = diff
+ @stats = stats
@repository = repository
@diff_refs = diff_refs
@fallback_diff_refs = fallback_diff_refs
+ @unfolded = false
# Ensure items are collected in the the batch
new_blob_lazy
@@ -134,6 +138,24 @@ module Gitlab
Gitlab::Diff::Parser.new.parse(raw_diff.each_line, diff_file: self).to_a
end
+ # Changes diff_lines according to the given position. That is,
+ # it checks whether the position requires blob lines into the diff
+ # in order to be presented.
+ def unfold_diff_lines(position)
+ return unless position
+
+ unfolder = Gitlab::Diff::LinesUnfolder.new(self, position)
+
+ if unfolder.unfold_required?
+ @diff_lines = unfolder.unfolded_diff_lines
+ @unfolded = true
+ end
+ end
+
+ def unfolded?
+ @unfolded
+ end
+
def highlighted_diff_lines
@highlighted_diff_lines ||=
Gitlab::Diff::Highlight.new(self, repository: self.repository).highlight
@@ -165,11 +187,11 @@ module Gitlab
end
def added_lines
- diff_lines.count(&:added?)
+ @stats&.additions || diff_lines.count(&:added?)
end
def removed_lines
- diff_lines.count(&:removed?)
+ @stats&.deletions || diff_lines.count(&:removed?)
end
def file_identifier
@@ -211,13 +233,17 @@ module Gitlab
old_blob && new_blob && old_blob.binary? != new_blob.binary?
end
+ # rubocop: disable CodeReuse/ActiveRecord
def size
valid_blobs.map(&:size).sum
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def raw_size
valid_blobs.map(&:raw_size).sum
end
+ # rubocop: enable CodeReuse/ActiveRecord
def raw_binary?
try_blobs(:raw_binary?)
diff --git a/lib/gitlab/diff/file_collection/base.rb b/lib/gitlab/diff/file_collection/base.rb
index c79d8d3cb21..10df037a0dd 100644
--- a/lib/gitlab/diff/file_collection/base.rb
+++ b/lib/gitlab/diff/file_collection/base.rb
@@ -1,28 +1,37 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
module FileCollection
class Base
- attr_reader :project, :diff_options, :diff_refs, :fallback_diff_refs
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :project, :diff_options, :diff_refs, :fallback_diff_refs, :diffable
delegate :count, :size, :real_size, to: :diff_files
def self.default_options
- ::Commit.max_diff_options.merge(ignore_whitespace_change: false, expanded: false)
+ ::Commit.max_diff_options.merge(ignore_whitespace_change: false, expanded: false, include_stats: true)
end
def initialize(diffable, project:, diff_options: nil, diff_refs: nil, fallback_diff_refs: nil)
diff_options = self.class.default_options.merge(diff_options || {})
@diffable = diffable
- @diffs = diffable.raw_diffs(diff_options)
+ @include_stats = diff_options.delete(:include_stats)
@project = project
@diff_options = diff_options
@diff_refs = diff_refs
@fallback_diff_refs = fallback_diff_refs
+ @repository = project.repository
+ end
+
+ def diffs
+ @diffs ||= diffable.raw_diffs(diff_options)
end
def diff_files
- @diff_files ||= @diffs.decorate! { |diff| decorate_diff!(diff) }
+ @diff_files ||= diffs.decorate! { |diff| decorate_diff!(diff) }
end
def diff_file_with_old_path(old_path)
@@ -33,12 +42,37 @@ module Gitlab
diff_files.find { |diff_file| diff_file.new_path == new_path }
end
+ def clear_cache
+ # No-op
+ end
+
+ def write_cache
+ # No-op
+ end
+
private
+ def diff_stats_collection
+ strong_memoize(:diff_stats) do
+ # There are scenarios where we don't need to request Diff Stats,
+ # when caching for instance.
+ next unless @include_stats
+ next unless diff_refs
+
+ @repository.diff_stats(diff_refs.base_sha, diff_refs.head_sha)
+ end
+ end
+
def decorate_diff!(diff)
return diff if diff.is_a?(File)
- Gitlab::Diff::File.new(diff, repository: project.repository, diff_refs: diff_refs, fallback_diff_refs: fallback_diff_refs)
+ stats = diff_stats_collection&.find_by_path(diff.new_path)
+
+ Gitlab::Diff::File.new(diff,
+ repository: project.repository,
+ diff_refs: diff_refs,
+ fallback_diff_refs: fallback_diff_refs,
+ stats: stats)
end
end
end
diff --git a/lib/gitlab/diff/file_collection/commit.rb b/lib/gitlab/diff/file_collection/commit.rb
index 4dc297ec036..7b1d6171e82 100644
--- a/lib/gitlab/diff/file_collection/commit.rb
+++ b/lib/gitlab/diff/file_collection/commit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
module FileCollection
diff --git a/lib/gitlab/diff/file_collection/compare.rb b/lib/gitlab/diff/file_collection/compare.rb
index 20d8f891cc3..586c5cf87af 100644
--- a/lib/gitlab/diff/file_collection/compare.rb
+++ b/lib/gitlab/diff/file_collection/compare.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
module FileCollection
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb
index be25e1bab21..e29bf75f341 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
module FileCollection
class MergeRequestDiff < Base
+ extend ::Gitlab::Utils::Override
+
def initialize(merge_request_diff, diff_options:)
@merge_request_diff = merge_request_diff
@@ -13,70 +17,35 @@ module Gitlab
end
def diff_files
- # Make sure to _not_ send any method call to Gitlab::Diff::File
- # _before_ all of them were collected (`super`). Premature method calls will
- # trigger N+1 RPCs to Gitaly through BatchLoader records (Blob.lazy).
- #
diff_files = super
- diff_files.each { |diff_file| cache_highlight!(diff_file) if cacheable?(diff_file) }
- store_highlight_cache
+ diff_files.each { |diff_file| cache.decorate(diff_file) }
diff_files
end
- def real_size
- @merge_request_diff.real_size
+ override :write_cache
+ def write_cache
+ cache.write_if_empty
end
- def clear_cache!
- Rails.cache.delete(cache_key)
+ override :clear_cache
+ def clear_cache
+ cache.clear
end
def cache_key
- [@merge_request_diff, 'highlighted-diff-files', Gitlab::Diff::Line::SERIALIZE_KEYS, diff_options]
- end
-
- private
-
- def highlight_diff_file_from_cache!(diff_file, cache_diff_lines)
- diff_file.highlighted_diff_lines = cache_diff_lines.map do |line|
- Gitlab::Diff::Line.init_from_hash(line)
- end
+ cache.key
end
- #
- # If we find the highlighted diff files lines on the cache we replace existing diff_files lines (no highlighted)
- # for the highlighted ones, so we just skip their execution.
- # If the highlighted diff files lines are not cached we calculate and cache them.
- #
- # The content of the cache is a Hash where the key identifies the file and the values are Arrays of
- # hashes that represent serialized diff lines.
- #
- def cache_highlight!(diff_file)
- item_key = diff_file.file_identifier
-
- if highlight_cache[item_key]
- highlight_diff_file_from_cache!(diff_file, highlight_cache[item_key])
- else
- highlight_cache[item_key] = diff_file.highlighted_diff_lines.map(&:to_hash)
- end
- end
-
- def highlight_cache
- return @highlight_cache if defined?(@highlight_cache)
-
- @highlight_cache = Rails.cache.read(cache_key) || {}
- @highlight_cache_was_empty = @highlight_cache.empty?
- @highlight_cache
+ def real_size
+ @merge_request_diff.real_size
end
- def store_highlight_cache
- Rails.cache.write(cache_key, highlight_cache, expires_in: 1.week) if @highlight_cache_was_empty
- end
+ private
- def cacheable?(diff_file)
- @merge_request_diff.present? && diff_file.text? && diff_file.diffable?
+ def cache
+ @cache ||= Gitlab::Diff::HighlightCache.new(self)
end
end
end
diff --git a/lib/gitlab/diff/formatters/base_formatter.rb b/lib/gitlab/diff/formatters/base_formatter.rb
index 5e923b9e602..9704aed82c1 100644
--- a/lib/gitlab/diff/formatters/base_formatter.rb
+++ b/lib/gitlab/diff/formatters/base_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
module Formatters
diff --git a/lib/gitlab/diff/formatters/image_formatter.rb b/lib/gitlab/diff/formatters/image_formatter.rb
index ccd0d309972..5bc9f0c337f 100644
--- a/lib/gitlab/diff/formatters/image_formatter.rb
+++ b/lib/gitlab/diff/formatters/image_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
module Formatters
diff --git a/lib/gitlab/diff/formatters/text_formatter.rb b/lib/gitlab/diff/formatters/text_formatter.rb
index 01c7e9f51ab..f6e247ef665 100644
--- a/lib/gitlab/diff/formatters/text_formatter.rb
+++ b/lib/gitlab/diff/formatters/text_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
module Formatters
diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb
index 5c1baa19b66..d2484217ab9 100644
--- a/lib/gitlab/diff/highlight.rb
+++ b/lib/gitlab/diff/highlight.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class Highlight
@@ -24,7 +26,7 @@ module Gitlab
# ignore highlighting for "match" lines
next diff_line if diff_line.meta?
- rich_line = highlight_line(diff_line) || diff_line.text
+ rich_line = highlight_line(diff_line) || ERB::Util.html_escape(diff_line.text)
if line_inline_diffs = inline_diffs[i]
begin
@@ -37,7 +39,7 @@ module Gitlab
end
end
- diff_line.text = rich_line
+ diff_line.rich_text = rich_line
diff_line
end
@@ -79,7 +81,7 @@ module Gitlab
return [] unless blob
blob.load_all_data!
- Gitlab::Highlight.highlight(blob.path, blob.data, repository: repository).lines
+ blob.present.highlight.lines
end
end
end
diff --git a/lib/gitlab/diff/highlight_cache.rb b/lib/gitlab/diff/highlight_cache.rb
new file mode 100644
index 00000000000..e4390771db2
--- /dev/null
+++ b/lib/gitlab/diff/highlight_cache.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+#
+module Gitlab
+ module Diff
+ class HighlightCache
+ delegate :diffable, to: :@diff_collection
+ delegate :diff_options, to: :@diff_collection
+
+ def initialize(diff_collection, backend: Rails.cache)
+ @backend = backend
+ @diff_collection = diff_collection
+ end
+
+ # - Reads from cache
+ # - Assigns DiffFile#highlighted_diff_lines for cached files
+ def decorate(diff_file)
+ if content = read_file(diff_file)
+ diff_file.highlighted_diff_lines = content.map do |line|
+ Gitlab::Diff::Line.init_from_hash(line)
+ end
+ end
+ end
+
+ # It populates a Hash in order to submit a single write to the memory
+ # cache. This avoids excessive IO generated by N+1's (1 writing for
+ # each highlighted line or file).
+ def write_if_empty
+ return if cached_content.present?
+
+ @diff_collection.diff_files.each do |diff_file|
+ next unless cacheable?(diff_file)
+
+ diff_file_id = diff_file.file_identifier
+
+ cached_content[diff_file_id] = diff_file.highlighted_diff_lines.map(&:to_hash)
+ end
+
+ cache.write(key, cached_content, expires_in: 1.week)
+ end
+
+ def clear
+ cache.delete(key)
+ end
+
+ def key
+ [diffable, 'highlighted-diff-files', Gitlab::Diff::Line::SERIALIZE_KEYS, diff_options]
+ end
+
+ private
+
+ def read_file(diff_file)
+ cached_content[diff_file.file_identifier]
+ end
+
+ def cache
+ @backend
+ end
+
+ def cached_content
+ @cached_content ||= cache.read(key) || {}
+ end
+
+ def cacheable?(diff_file)
+ diffable.present? && diff_file.text? && diff_file.diffable?
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/image_point.rb b/lib/gitlab/diff/image_point.rb
index 1f157354ea4..a3ce032f8e2 100644
--- a/lib/gitlab/diff/image_point.rb
+++ b/lib/gitlab/diff/image_point.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class ImagePoint
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb
index 99970779c67..5815d1bae4a 100644
--- a/lib/gitlab/diff/inline_diff.rb
+++ b/lib/gitlab/diff/inline_diff.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class InlineDiff
@@ -67,10 +69,11 @@ module Gitlab
private
# Finds pairs of old/new line pairs that represent the same line that changed
+ # rubocop: disable CodeReuse/ActiveRecord
def find_changed_line_pairs(lines)
# Prefixes of all diff lines, indicating their types
# For example: `" - + -+ ---+++ --+ -++"`
- line_prefixes = lines.each_with_object("") { |line, s| s << (line[0] || ' ') }.gsub(/[^ +-]/, ' ')
+ line_prefixes = lines.each_with_object(+"") { |line, s| s << (line[0] || ' ') }.gsub(/[^ +-]/, ' ')
changed_line_pairs = []
line_prefixes.scan(LINE_PAIRS_PATTERN) do
@@ -89,6 +92,7 @@ module Gitlab
changed_line_pairs
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
private
diff --git a/lib/gitlab/diff/inline_diff_markdown_marker.rb b/lib/gitlab/diff/inline_diff_markdown_marker.rb
index c2a2eb15931..3c536c43a9e 100644
--- a/lib/gitlab/diff/inline_diff_markdown_marker.rb
+++ b/lib/gitlab/diff/inline_diff_markdown_marker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class InlineDiffMarkdownMarker < Gitlab::StringRangeMarker
diff --git a/lib/gitlab/diff/inline_diff_marker.rb b/lib/gitlab/diff/inline_diff_marker.rb
index 81e91ea0ab7..1bbde1ffd2a 100644
--- a/lib/gitlab/diff/inline_diff_marker.rb
+++ b/lib/gitlab/diff/inline_diff_marker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class InlineDiffMarker < Gitlab::StringRangeMarker
diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb
index 1faf7770634..f0c4977fc50 100644
--- a/lib/gitlab/diff/line.rb
+++ b/lib/gitlab/diff/line.rb
@@ -1,16 +1,19 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class Line
- SERIALIZE_KEYS = %i(line_code text type index old_pos new_pos).freeze
+ SERIALIZE_KEYS = %i(line_code rich_text text type index old_pos new_pos).freeze
- attr_reader :line_code, :type, :index, :old_pos, :new_pos
+ attr_reader :line_code, :type, :old_pos, :new_pos
attr_writer :rich_text
- attr_accessor :text
+ attr_accessor :text, :index
- def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil)
+ def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil, rich_text: nil)
@text, @type, @index = text, type, index
@old_pos, @new_pos = old_pos, new_pos
@parent_file = parent_file
+ @rich_text = rich_text
# When line code is not provided from cache store we build it
# using the parent_file(Diff::File or Conflict::File).
@@ -18,7 +21,14 @@ module Gitlab
end
def self.init_from_hash(hash)
- new(hash[:text], hash[:type], hash[:index], hash[:old_pos], hash[:new_pos], line_code: hash[:line_code])
+ new(hash[:text],
+ hash[:type],
+ hash[:index],
+ hash[:old_pos],
+ hash[:new_pos],
+ parent_file: hash[:parent_file],
+ line_code: hash[:line_code],
+ rich_text: hash[:rich_text])
end
def to_hash
@@ -78,16 +88,10 @@ module Gitlab
}
end
+ # We have to keep this here since it is still used for conflict resolution
+ # Conflict::File#as_json renders json diff lines in sections
def as_json(opts = nil)
- {
- line_code: line_code,
- type: type,
- old_line: old_line,
- new_line: new_line,
- text: text,
- rich_text: rich_text || text,
- meta_data: meta_positions
- }
+ DiffLineSerializer.new.represent(self)
end
private
diff --git a/lib/gitlab/diff/line_mapper.rb b/lib/gitlab/diff/line_mapper.rb
index cf71d47df8e..fba7bff4781 100644
--- a/lib/gitlab/diff/line_mapper.rb
+++ b/lib/gitlab/diff/line_mapper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# When provided a diff for a specific file, maps old line numbers to new line
# numbers and back, to find out where a specific line in a file was moved by the
# changes.
diff --git a/lib/gitlab/diff/lines_unfolder.rb b/lib/gitlab/diff/lines_unfolder.rb
new file mode 100644
index 00000000000..9306b7e16a2
--- /dev/null
+++ b/lib/gitlab/diff/lines_unfolder.rb
@@ -0,0 +1,235 @@
+# frozen_string_literal: true
+
+# Given a position, calculates which Blob lines should be extracted, treated and
+# injected in the current diff file lines in order to present a "unfolded" diff.
+module Gitlab
+ module Diff
+ class LinesUnfolder
+ include Gitlab::Utils::StrongMemoize
+
+ UNFOLD_CONTEXT_SIZE = 3
+
+ def initialize(diff_file, position)
+ @diff_file = diff_file
+ @blob = diff_file.old_blob
+ @position = position
+ @generate_top_match_line = true
+ @generate_bottom_match_line = true
+
+ # These methods update `@generate_top_match_line` and
+ # `@generate_bottom_match_line`.
+ @from_blob_line = calculate_from_blob_line!
+ @to_blob_line = calculate_to_blob_line!
+ end
+
+ # Returns merged diff lines with required blob lines with correct
+ # positions.
+ def unfolded_diff_lines
+ strong_memoize(:unfolded_diff_lines) do
+ next unless unfold_required?
+
+ merged_diff_with_blob_lines
+ end
+ end
+
+ # Returns the extracted lines from the old blob which should be merged
+ # with the current diff lines.
+ def blob_lines
+ strong_memoize(:blob_lines) do
+ # Blob lines, unlike diffs, doesn't start with an empty space for
+ # unchanged line, so the parsing and highlighting step can get fuzzy
+ # without the following change.
+ line_prefix = ' '
+ blob_as_diff_lines = @blob.data.each_line.map { |line| "#{line_prefix}#{line}" }
+
+ lines = Gitlab::Diff::Parser.new.parse(blob_as_diff_lines, diff_file: @diff_file).to_a
+
+ from = from_blob_line - 1
+ to = to_blob_line - 1
+
+ lines[from..to]
+ end
+ end
+
+ def unfold_required?
+ strong_memoize(:unfold_required) do
+ next false unless @diff_file.text?
+ next false unless @position.unchanged?
+ next false if @diff_file.new_file? || @diff_file.deleted_file?
+ next false unless @position.old_line
+ # Invalid position (MR import scenario)
+ next false if @position.old_line > @blob.lines.size
+ next false if @diff_file.diff_lines.empty?
+ next false if @diff_file.line_for_position(@position)
+ next false unless unfold_line
+
+ true
+ end
+ end
+
+ private
+
+ attr_reader :from_blob_line, :to_blob_line
+
+ def merged_diff_with_blob_lines
+ lines = @diff_file.diff_lines
+ match_line = unfold_line
+ insert_index = bottom? ? -1 : match_line.index
+
+ lines -= [match_line] unless bottom?
+
+ lines.insert(insert_index, *blob_lines_with_matches)
+
+ # The inserted blob lines have invalid indexes, so we need
+ # to reindex them.
+ reindex(lines)
+
+ lines
+ end
+
+ # Returns 'unchanged' blob lines with recalculated `old_pos` and
+ # `new_pos` and the recalculated new match line (needed if we for instance
+ # we unfolded once, but there are still folded lines).
+ def blob_lines_with_matches
+ old_pos = from_blob_line
+ new_pos = from_blob_line + offset
+
+ new_blob_lines = []
+
+ new_blob_lines.push(top_blob_match_line) if top_blob_match_line
+
+ blob_lines.each do |line|
+ new_blob_lines << Gitlab::Diff::Line.new(line.text, line.type, nil, old_pos, new_pos,
+ parent_file: @diff_file)
+
+ old_pos += 1
+ new_pos += 1
+ end
+
+ new_blob_lines.push(bottom_blob_match_line) if bottom_blob_match_line
+
+ new_blob_lines
+ end
+
+ def reindex(lines)
+ lines.each_with_index { |line, i| line.index = i }
+ end
+
+ def top_blob_match_line
+ strong_memoize(:top_blob_match_line) do
+ next unless @generate_top_match_line
+
+ old_pos = from_blob_line
+ new_pos = from_blob_line + offset
+
+ build_match_line(old_pos, new_pos)
+ end
+ end
+
+ def bottom_blob_match_line
+ strong_memoize(:bottom_blob_match_line) do
+ # The bottom line match addition is already handled on
+ # Diff::File#diff_lines_for_serializer
+ next if bottom?
+ next unless @generate_bottom_match_line
+
+ position = line_after_unfold_position.old_pos
+
+ old_pos = position
+ new_pos = position + offset
+
+ build_match_line(old_pos, new_pos)
+ end
+ end
+
+ def build_match_line(old_pos, new_pos)
+ blob_lines_length = blob_lines.length
+ old_line_ref = [old_pos, blob_lines_length].join(',')
+ new_line_ref = [new_pos, blob_lines_length].join(',')
+ new_match_line_str = "@@ -#{old_line_ref}+#{new_line_ref} @@"
+
+ Gitlab::Diff::Line.new(new_match_line_str, 'match', nil, old_pos, new_pos)
+ end
+
+ # Returns the first line position that should be extracted
+ # from `blob_lines`.
+ def calculate_from_blob_line!
+ return unless unfold_required?
+
+ from = comment_position - UNFOLD_CONTEXT_SIZE
+
+ # There's no line before the match if it's in the top-most
+ # position.
+ prev_line_number = line_before_unfold_position&.old_pos || 0
+
+ if from <= prev_line_number + 1
+ @generate_top_match_line = false
+ from = prev_line_number + 1
+ end
+
+ from
+ end
+
+ # Returns the last line position that should be extracted
+ # from `blob_lines`.
+ def calculate_to_blob_line!
+ return unless unfold_required?
+
+ to = comment_position + UNFOLD_CONTEXT_SIZE
+
+ return to if bottom?
+
+ next_line_number = line_after_unfold_position.old_pos
+
+ if to >= next_line_number - 1
+ @generate_bottom_match_line = false
+ to = next_line_number - 1
+ end
+
+ to
+ end
+
+ def offset
+ unfold_line.new_pos - unfold_line.old_pos
+ end
+
+ def line_before_unfold_position
+ return unless index = unfold_line&.index
+
+ @diff_file.diff_lines[index - 1] if index > 0
+ end
+
+ def line_after_unfold_position
+ return unless index = unfold_line&.index
+
+ @diff_file.diff_lines[index + 1] if index >= 0
+ end
+
+ def bottom?
+ strong_memoize(:bottom) do
+ @position.old_line > last_line.old_pos
+ end
+ end
+
+ # Returns the line which needed to be expanded in order to send a comment
+ # in `@position`.
+ def unfold_line
+ strong_memoize(:unfold_line) do
+ next last_line if bottom?
+
+ @diff_file.diff_lines.find do |line|
+ line.old_pos > comment_position && line.type == 'match'
+ end
+ end
+ end
+
+ def comment_position
+ @position.old_line
+ end
+
+ def last_line
+ @diff_file.diff_lines.last
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/parallel_diff.rb b/lib/gitlab/diff/parallel_diff.rb
index 0cb26fa45c8..77b65fea726 100644
--- a/lib/gitlab/diff/parallel_diff.rb
+++ b/lib/gitlab/diff/parallel_diff.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class ParallelDiff
diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb
index 7ae7ed286ed..4a47e4b80b6 100644
--- a/lib/gitlab/diff/parser.rb
+++ b/lib/gitlab/diff/parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Diff
class Parser
diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb
index 978962ab2eb..e8f98f52111 100644
--- a/lib/gitlab/diff/position.rb
+++ b/lib/gitlab/diff/position.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Defines a specific location, identified by paths line numbers and image coordinates,
# within a specific diff, identified by start, head and base commit ids.
module Gitlab
@@ -69,6 +71,10 @@ module Gitlab
JSON.generate(formatter.to_h, opts)
end
+ def as_json(opts = nil)
+ to_h.as_json(opts)
+ end
+
def type
formatter.line_age
end
@@ -97,25 +103,29 @@ module Gitlab
@diff_refs ||= DiffRefs.new(base_sha: base_sha, start_sha: start_sha, head_sha: head_sha)
end
+ def unfolded_diff?(repository)
+ diff_file(repository)&.unfolded?
+ end
+
def diff_file(repository)
return @diff_file if defined?(@diff_file)
@diff_file = begin
- if RequestStore.active?
- key = {
- project_id: repository.project.id,
- start_sha: start_sha,
- head_sha: head_sha,
- path: file_path
- }
-
- RequestStore.fetch(key) { find_diff_file(repository) }
- else
- find_diff_file(repository)
- end
+ key = {
+ project_id: repository.project.id,
+ start_sha: start_sha,
+ head_sha: head_sha,
+ path: file_path
+ }
+
+ Gitlab::SafeRequestStore.fetch(key) { find_diff_file(repository) }
end
end
+ def diff_options
+ { paths: paths, expanded: true, include_stats: false }
+ end
+
def diff_line(repository)
@diff_line ||= diff_file(repository)&.line_for_position(self)
end
@@ -130,7 +140,13 @@ module Gitlab
return unless diff_refs.complete?
return unless comparison = diff_refs.compare_in(repository.project)
- comparison.diffs(paths: paths, expanded: true).diff_files.first
+ file = comparison.diffs(diff_options).diff_files.first
+
+ # We need to unfold diff lines according to the position in order
+ # to correctly calculate the line code and trace position changes.
+ file&.unfold_diff_lines(self)
+
+ file
end
def get_formatter_class(type)
diff --git a/lib/gitlab/diff/position_tracer.rb b/lib/gitlab/diff/position_tracer.rb
index b68a1636814..af3df820422 100644
--- a/lib/gitlab/diff/position_tracer.rb
+++ b/lib/gitlab/diff/position_tracer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Finds the diff position in the new diff that corresponds to the same location
# specified by the provided position in the old diff.
module Gitlab
@@ -24,7 +26,7 @@ module Gitlab
# head of `feature` was commit B, resulting in the original diff A->B.
# Since creation, `master` was updated to C.
# Now `feature` is being updated to D, and the newly generated MR diff is C->D.
- # It is possible that C and D are direct decendants of A and B respectively,
+ # It is possible that C and D are direct descendants of A and B respectively,
# but this isn't necessarily the case as rebases and merges come into play.
#
# Suppose we have a diff note on the original diff A->B. Now that the MR
diff --git a/lib/gitlab/downtime_check.rb b/lib/gitlab/downtime_check.rb
index 941244694e2..31bb6810391 100644
--- a/lib/gitlab/downtime_check.rb
+++ b/lib/gitlab/downtime_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# Checks if a set of migrations requires downtime or not.
class DowntimeCheck
diff --git a/lib/gitlab/downtime_check/message.rb b/lib/gitlab/downtime_check/message.rb
index 543e62794c5..ec38bd769a3 100644
--- a/lib/gitlab/downtime_check/message.rb
+++ b/lib/gitlab/downtime_check/message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class DowntimeCheck
class Message
@@ -18,13 +20,13 @@ module Gitlab
def to_s
label = offline ? OFFLINE : ONLINE
- message = "[#{label}]: #{path}"
+ message = ["[#{label}]: #{path}"]
if reason?
- message += ":\n\n#{reason}\n\n"
+ message << ":\n\n#{reason}\n\n"
end
- message
+ message.join
end
def reason?
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index ee604e66154..5d9ecd651a0 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop: disable Rails/Output
module Gitlab
# Checks if a set of migrations requires downtime or not.
@@ -284,7 +286,7 @@ module Gitlab
end
def patch_name_from_branch(branch_name)
- branch_name.parameterize << '.patch'
+ "#{branch_name.parameterize}.patch"
end
def patch_url
@@ -432,9 +434,11 @@ module Gitlab
end
def conflicting_files_msg
- failed_files.reduce("The conflicts detected were as follows:\n") do |memo, file|
- memo << "\n - #{file}"
- end
+ header = "The conflicts detected were as follows:\n"
+ separator = "\n - "
+ failed_items = failed_files.join(separator)
+
+ "#{header}#{separator}#{failed_items}"
end
end
end
diff --git a/lib/gitlab/email/attachment_uploader.rb b/lib/gitlab/email/attachment_uploader.rb
index 83440ae227d..a826519b2dd 100644
--- a/lib/gitlab/email/attachment_uploader.rb
+++ b/lib/gitlab/email/attachment_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
class AttachmentUploader
diff --git a/lib/gitlab/email/handler.rb b/lib/gitlab/email/handler.rb
index e08b5be8984..cebedb19dcc 100644
--- a/lib/gitlab/email/handler.rb
+++ b/lib/gitlab/email/handler.rb
@@ -1,20 +1,23 @@
-require 'gitlab/email/handler/create_merge_request_handler'
-require 'gitlab/email/handler/create_note_handler'
-require 'gitlab/email/handler/create_issue_handler'
-require 'gitlab/email/handler/unsubscribe_handler'
+# frozen_string_literal: true
module Gitlab
module Email
module Handler
- HANDLERS = [
- UnsubscribeHandler,
- CreateNoteHandler,
- CreateMergeRequestHandler,
- CreateIssueHandler
- ].freeze
+ def self.handlers
+ @handlers ||= load_handlers
+ end
+
+ def self.load_handlers
+ [
+ UnsubscribeHandler,
+ CreateNoteHandler,
+ CreateMergeRequestHandler,
+ CreateIssueHandler
+ ]
+ end
def self.for(mail, mail_key)
- HANDLERS.find do |klass|
+ handlers.find do |klass|
handler = klass.new(mail, mail_key)
break handler if handler.can_handle?
end
diff --git a/lib/gitlab/email/handler/base_handler.rb b/lib/gitlab/email/handler/base_handler.rb
index 0bba433d04b..35bb49ad19a 100644
--- a/lib/gitlab/email/handler/base_handler.rb
+++ b/lib/gitlab/email/handler/base_handler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
module Handler
diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb
index fc8615afcae..69982efbbe6 100644
--- a/lib/gitlab/email/handler/create_issue_handler.rb
+++ b/lib/gitlab/email/handler/create_issue_handler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'gitlab/email/handler/base_handler'
module Gitlab
@@ -28,9 +30,11 @@ module Gitlab
record_name: 'issue')
end
+ # rubocop: disable CodeReuse/ActiveRecord
def author
@author ||= User.find_by(incoming_email_token: incoming_email_token)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def project
@project ||= Project.find_by_full_path(project_path)
diff --git a/lib/gitlab/email/handler/create_merge_request_handler.rb b/lib/gitlab/email/handler/create_merge_request_handler.rb
index 2316e58c3fc..5772727e855 100644
--- a/lib/gitlab/email/handler/create_merge_request_handler.rb
+++ b/lib/gitlab/email/handler/create_merge_request_handler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'gitlab/email/handler/base_handler'
require 'gitlab/email/handler/reply_processing'
@@ -32,18 +34,36 @@ module Gitlab
record_name: 'merge_request')
end
+ # rubocop: disable CodeReuse/ActiveRecord
def author
@author ||= User.find_by(incoming_email_token: incoming_email_token)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def project
@project ||= Project.find_by_full_path(project_path)
end
+ def metrics_params
+ super.merge(includes_patches: patch_attachments.any?)
+ end
+
private
+ def build_merge_request
+ MergeRequests::BuildService.new(project, author, merge_request_params).execute
+ end
+
def create_merge_request
- merge_request = MergeRequests::BuildService.new(project, author, merge_request_params).execute
+ merge_request = build_merge_request
+
+ if patch_attachments.any?
+ apply_patches_to_source_branch(start_branch: merge_request.target_branch)
+ remove_patch_attachments
+ # Rebuild the merge request as the source branch might just have
+ # been created, so we should re-validate.
+ merge_request = build_merge_request
+ end
if merge_request.errors.any?
merge_request
@@ -55,12 +75,42 @@ module Gitlab
def merge_request_params
params = {
source_project_id: project.id,
- source_branch: mail.subject,
+ source_branch: source_branch,
target_project_id: project.id
}
params[:description] = message if message.present?
params
end
+
+ def apply_patches_to_source_branch(start_branch:)
+ patches = patch_attachments.map { |patch| patch.body.decoded }
+
+ result = Commits::CommitPatchService
+ .new(project, author, branch_name: source_branch, patches: patches, start_branch: start_branch)
+ .execute
+
+ if result[:status] != :success
+ message = "Could not apply patches to #{source_branch}:\n#{result[:message]}"
+ raise InvalidAttachment, message
+ end
+ end
+
+ def remove_patch_attachments
+ patch_attachments.each { |patch| mail.parts.delete(patch) }
+ # reset the message, so it needs to be reporocessed when the attachments
+ # have been modified
+ @message = nil
+ end
+
+ def patch_attachments
+ @patches ||= mail.attachments
+ .select { |attachment| attachment.filename.ends_with?('.patch') }
+ .sort_by(&:filename)
+ end
+
+ def source_branch
+ @source_branch ||= mail.subject
+ end
end
end
end
diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb
index 379b114e957..c7c573595fa 100644
--- a/lib/gitlab/email/handler/create_note_handler.rb
+++ b/lib/gitlab/email/handler/create_note_handler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'gitlab/email/handler/base_handler'
require 'gitlab/email/handler/reply_processing'
diff --git a/lib/gitlab/email/handler/reply_processing.rb b/lib/gitlab/email/handler/reply_processing.rb
index 38b1425364f..ff6b2c729b2 100644
--- a/lib/gitlab/email/handler/reply_processing.rb
+++ b/lib/gitlab/email/handler/reply_processing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
module Handler
diff --git a/lib/gitlab/email/handler/unsubscribe_handler.rb b/lib/gitlab/email/handler/unsubscribe_handler.rb
index 56751e4e41e..d2f617b868a 100644
--- a/lib/gitlab/email/handler/unsubscribe_handler.rb
+++ b/lib/gitlab/email/handler/unsubscribe_handler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'gitlab/email/handler/base_handler'
module Gitlab
diff --git a/lib/gitlab/email/hook/additional_headers_interceptor.rb b/lib/gitlab/email/hook/additional_headers_interceptor.rb
index 064cb5e659a..aa2ef76069b 100644
--- a/lib/gitlab/email/hook/additional_headers_interceptor.rb
+++ b/lib/gitlab/email/hook/additional_headers_interceptor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
module Hook
diff --git a/lib/gitlab/email/hook/delivery_metrics_observer.rb b/lib/gitlab/email/hook/delivery_metrics_observer.rb
index 1c2985f6045..c7af485fcc5 100644
--- a/lib/gitlab/email/hook/delivery_metrics_observer.rb
+++ b/lib/gitlab/email/hook/delivery_metrics_observer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
module Hook
diff --git a/lib/gitlab/email/hook/disable_email_interceptor.rb b/lib/gitlab/email/hook/disable_email_interceptor.rb
index 7bb8b53f0c8..6b6b1d85109 100644
--- a/lib/gitlab/email/hook/disable_email_interceptor.rb
+++ b/lib/gitlab/email/hook/disable_email_interceptor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
module Hook
diff --git a/lib/gitlab/email/hook/email_template_interceptor.rb b/lib/gitlab/email/hook/email_template_interceptor.rb
index be0c4dd862e..13f8db2051d 100644
--- a/lib/gitlab/email/hook/email_template_interceptor.rb
+++ b/lib/gitlab/email/hook/email_template_interceptor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
module Hook
diff --git a/lib/gitlab/email/html_parser.rb b/lib/gitlab/email/html_parser.rb
index 50559a48973..77f299bcade 100644
--- a/lib/gitlab/email/html_parser.rb
+++ b/lib/gitlab/email/html_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
class HTMLParser
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index cd9d3a6483f..ec412e7a8b1 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Email
module Message
@@ -116,7 +118,7 @@ module Gitlab
end
def subject
- subject_text = '[Git]'
+ subject_text = ['[Git]']
subject_text << "[#{project.full_path}]"
subject_text << "[#{ref_name}]" if @action == :push
subject_text << ' '
@@ -134,6 +136,8 @@ module Gitlab
subject_action[0] = subject_action[0].capitalize
subject_text << "#{subject_action} #{ref_type} #{ref_name}"
end
+
+ subject_text.join
end
end
end
diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb
index d8c594ad0e7..d28f6b301fa 100644
--- a/lib/gitlab/email/receiver.rb
+++ b/lib/gitlab/email/receiver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'gitlab/email/handler'
# Inspired in great part by Discourse's Email::Receiver
@@ -18,6 +20,7 @@ module Gitlab
InvalidIssueError = Class.new(InvalidRecordError)
InvalidMergeRequestError = Class.new(InvalidRecordError)
UnknownIncomingEmail = Class.new(ProcessingError)
+ InvalidAttachment = Class.new(ProcessingError)
class Receiver
def initialize(raw)
diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb
index ae6b84607d6..2743f011ca6 100644
--- a/lib/gitlab/email/reply_parser.rb
+++ b/lib/gitlab/email/reply_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Inspired in great part by Discourse's Email::Receiver
module Gitlab
module Email
diff --git a/lib/gitlab/emoji.rb b/lib/gitlab/emoji.rb
index 89cf659bce4..ce1dfb0753c 100644
--- a/lib/gitlab/emoji.rb
+++ b/lib/gitlab/emoji.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Emoji
extend self
diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb
index d1fd5dfe0cb..a4a154c80f7 100644
--- a/lib/gitlab/encoding_helper.rb
+++ b/lib/gitlab/encoding_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module EncodingHelper
extend self
@@ -75,7 +77,7 @@ module Gitlab
end
def binary_stringio(str)
- StringIO.new(str || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
+ StringIO.new(str.freeze || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
end
private
diff --git a/lib/gitlab/environment.rb b/lib/gitlab/environment.rb
index 5e0dd6e7859..b1a9603d3a5 100644
--- a/lib/gitlab/environment.rb
+++ b/lib/gitlab/environment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Environment
def self.hostname
diff --git a/lib/gitlab/environment_logger.rb b/lib/gitlab/environment_logger.rb
index 407cc572656..862a516ca71 100644
--- a/lib/gitlab/environment_logger.rb
+++ b/lib/gitlab/environment_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class EnvironmentLogger < Gitlab::Logger
def self.file_name_noext
diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb
index d5d35dbd97f..0341f930b9c 100644
--- a/lib/gitlab/etag_caching/middleware.rb
+++ b/lib/gitlab/etag_caching/middleware.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module EtagCaching
class Middleware
diff --git a/lib/gitlab/etag_caching/router.rb b/lib/gitlab/etag_caching/router.rb
index 75167a6b088..08e30214b46 100644
--- a/lib/gitlab/etag_caching/router.rb
+++ b/lib/gitlab/etag_caching/router.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module EtagCaching
class Router
diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb
index 21172ff8d93..2395e7be026 100644
--- a/lib/gitlab/etag_caching/store.rb
+++ b/lib/gitlab/etag_caching/store.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module EtagCaching
class Store
diff --git a/lib/gitlab/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb
index 12b5e240962..d466d2a514c 100644
--- a/lib/gitlab/exclusive_lease.rb
+++ b/lib/gitlab/exclusive_lease.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'securerandom'
module Gitlab
diff --git a/lib/gitlab/exclusive_lease_helpers.rb b/lib/gitlab/exclusive_lease_helpers.rb
index e998548cff9..4aaf2474763 100644
--- a/lib/gitlab/exclusive_lease_helpers.rb
+++ b/lib/gitlab/exclusive_lease_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# This module provides helper methods which are intregrated with GitLab::ExclusiveLease
module ExclusiveLeaseHelpers
diff --git a/lib/gitlab/fake_application_settings.rb b/lib/gitlab/fake_application_settings.rb
index bb14a8cd9e7..db1aeeea8d3 100644
--- a/lib/gitlab/fake_application_settings.rb
+++ b/lib/gitlab/fake_application_settings.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class extends an OpenStruct object by adding predicate methods to mimic
# ActiveRecord access. We rely on the initial values being true or false to
# determine whether to define a predicate method because for a newly-added
@@ -5,12 +7,6 @@
# column type without parsing db/schema.rb.
module Gitlab
class FakeApplicationSettings < OpenStruct
- def initialize(options = {})
- super
-
- FakeApplicationSettings.define_predicate_methods(options)
- end
-
# Mimic ActiveRecord predicate methods for boolean values
def self.define_predicate_methods(options)
options.each do |key, value|
@@ -23,5 +19,23 @@ module Gitlab
end
end
end
+
+ def initialize(options = {})
+ super
+
+ FakeApplicationSettings.define_predicate_methods(options)
+ end
+
+ def key_restriction_for(type)
+ 0
+ end
+
+ def allowed_key_types
+ ApplicationSetting::SUPPORTED_KEY_TYPES
+ end
+
+ def pick_repository_storage
+ repository_storages.sample
+ end
end
end
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
index 4850a6c0430..1ae2f9dfd93 100644
--- a/lib/gitlab/favicon.rb
+++ b/lib/gitlab/favicon.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class Favicon
class << self
@@ -47,7 +49,7 @@ module Gitlab
end
def appearance
- RequestStore.store[:appearance] ||= (Appearance.current || Appearance.new)
+ Gitlab::SafeRequestStore[:appearance] ||= (Appearance.current || Appearance.new)
end
def appearance_favicon
diff --git a/lib/gitlab/file_detector.rb b/lib/gitlab/file_detector.rb
index 49bc9c0b671..2770469ca9f 100644
--- a/lib/gitlab/file_detector.rb
+++ b/lib/gitlab/file_detector.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'set'
module Gitlab
@@ -6,9 +8,9 @@ module Gitlab
module FileDetector
PATTERNS = {
# Project files
- readme: %r{\Areadme[^/]*\z}i,
+ readme: /\A(#{Regexp.union(*Gitlab::MarkupHelper::PLAIN_FILENAMES).source})(\.(#{Regexp.union(*Gitlab::MarkupHelper::EXTENSIONS).source}))?\z/i,
changelog: %r{\A(changelog|history|changes|news)[^/]*\z}i,
- license: %r{\A(licen[sc]e|copying)(\.[^/]+)?\z}i,
+ license: %r{\A((un)?licen[sc]e|copying)(\.[^/]+)?\z}i,
contributing: %r{\Acontributing[^/]*\z}i,
version: 'version',
avatar: /\Alogo\.(png|jpg|gif)\z/,
@@ -18,7 +20,6 @@ module Gitlab
# Configuration files
gitignore: '.gitignore',
- koding: '.koding.yml',
gitlab_ci: '.gitlab-ci.yml',
route_map: '.gitlab/route-map.yml',
diff --git a/lib/gitlab/file_finder.rb b/lib/gitlab/file_finder.rb
index af8270c8db8..b4db3f93c9c 100644
--- a/lib/gitlab/file_finder.rb
+++ b/lib/gitlab/file_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class finds files in a repository by name and content
# the result is joined and sorted by file name
module Gitlab
diff --git a/lib/gitlab/file_markdown_link_builder.rb b/lib/gitlab/file_markdown_link_builder.rb
new file mode 100644
index 00000000000..180140e7da2
--- /dev/null
+++ b/lib/gitlab/file_markdown_link_builder.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+# Builds the markdown link of a file
+# It needs the methods filename and secure_url (final destination url) to be defined.
+module Gitlab
+ module FileMarkdownLinkBuilder
+ include FileTypeDetection
+
+ def markdown_link
+ return unless name = markdown_name
+
+ markdown = "[#{name.gsub(']', '\\]')}](#{secure_url})"
+ markdown = "!#{markdown}" if image_or_video? || dangerous?
+ markdown
+ end
+
+ def markdown_name
+ return unless filename.present?
+
+ image_or_video? ? File.basename(filename, File.extname(filename)) : filename
+ end
+ end
+end
diff --git a/lib/gitlab/file_type_detection.rb b/lib/gitlab/file_type_detection.rb
new file mode 100644
index 00000000000..25ee07cf940
--- /dev/null
+++ b/lib/gitlab/file_type_detection.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+# File helpers methods.
+# It needs the method filename to be defined.
+module Gitlab
+ module FileTypeDetection
+ IMAGE_EXT = %w[png jpg jpeg gif bmp tiff ico].freeze
+ # We recommend using the .mp4 format over .mov. Videos in .mov format can
+ # still be used but you really need to make sure they are served with the
+ # proper MIME type video/mp4 and not video/quicktime or your videos won't play
+ # on IE >= 9.
+ # http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html
+ VIDEO_EXT = %w[mp4 m4v mov webm ogv].freeze
+ # These extension types can contain dangerous code and should only be embedded inline with
+ # proper filtering. They should always be tagged as "Content-Disposition: attachment", not "inline".
+ DANGEROUS_EXT = %w[svg].freeze
+
+ def image?
+ extension_match?(IMAGE_EXT)
+ end
+
+ def video?
+ extension_match?(VIDEO_EXT)
+ end
+
+ def image_or_video?
+ image? || video?
+ end
+
+ def dangerous?
+ extension_match?(DANGEROUS_EXT)
+ end
+
+ private
+
+ def extension_match?(extensions)
+ return false unless filename
+
+ extension = File.extname(filename).delete('.')
+ extensions.include?(extension.downcase)
+ end
+ end
+end
diff --git a/lib/gitlab/fogbugz_import/client.rb b/lib/gitlab/fogbugz_import/client.rb
index acb000e3e23..dd747a79673 100644
--- a/lib/gitlab/fogbugz_import/client.rb
+++ b/lib/gitlab/fogbugz_import/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'fogbugz'
module Gitlab
diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb
index a91de278cf3..431911d1eee 100644
--- a/lib/gitlab/fogbugz_import/importer.rb
+++ b/lib/gitlab/fogbugz_import/importer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module FogbugzImport
class Importer
@@ -79,6 +81,7 @@ module Gitlab
::Labels::FindOrCreateService.new(nil, project, params).execute(skip_authorization: true)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def user_info(person_id)
user_hash = user_map[person_id.to_s]
@@ -95,7 +98,9 @@ module Gitlab
{ name: user_name, gitlab_id: gitlab_id }
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def import_cases
return unless @cases
@@ -141,6 +146,7 @@ module Gitlab
import_issue_comments(issue, comments)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def opened_content(comments)
while comment = comments.shift
diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb
index 1918d5b208d..3c71031a8d9 100644
--- a/lib/gitlab/fogbugz_import/project_creator.rb
+++ b/lib/gitlab/fogbugz_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module FogbugzImport
class ProjectCreator
diff --git a/lib/gitlab/fogbugz_import/repository.rb b/lib/gitlab/fogbugz_import/repository.rb
index d1dc63db2b2..b958dcf6cbf 100644
--- a/lib/gitlab/fogbugz_import/repository.rb
+++ b/lib/gitlab/fogbugz_import/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module FogbugzImport
class Repository
diff --git a/lib/gitlab/gfm/reference_rewriter.rb b/lib/gitlab/gfm/reference_rewriter.rb
index 455814a9159..08d7db49ad7 100644
--- a/lib/gitlab/gfm/reference_rewriter.rb
+++ b/lib/gitlab/gfm/reference_rewriter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Gfm
##
@@ -31,19 +33,19 @@ module Gitlab
class ReferenceRewriter
RewriteError = Class.new(StandardError)
- def initialize(text, source_project, current_user)
+ def initialize(text, source_parent, current_user)
@text = text
- @source_project = source_project
+ @source_parent = source_parent
@current_user = current_user
@original_html = markdown(text)
@pattern = Gitlab::ReferenceExtractor.references_pattern
end
- def rewrite(target_project)
+ def rewrite(target_parent)
return @text unless needs_rewrite?
@text.gsub(@pattern) do |reference|
- unfold_reference(reference, Regexp.last_match, target_project)
+ unfold_reference(reference, Regexp.last_match, target_parent)
end
end
@@ -53,14 +55,14 @@ module Gitlab
private
- def unfold_reference(reference, match, target_project)
+ def unfold_reference(reference, match, target_parent)
before = @text[0...match.begin(0)]
after = @text[match.end(0)..-1]
referable = find_referable(reference)
return reference unless referable
- cross_reference = build_cross_reference(referable, target_project)
+ cross_reference = build_cross_reference(referable, target_parent)
return reference if reference == cross_reference
if cross_reference.nil?
@@ -72,17 +74,17 @@ module Gitlab
end
def find_referable(reference)
- extractor = Gitlab::ReferenceExtractor.new(@source_project,
+ extractor = Gitlab::ReferenceExtractor.new(@source_parent,
@current_user)
extractor.analyze(reference)
extractor.all.first
end
- def build_cross_reference(referable, target_project)
+ def build_cross_reference(referable, target_parent)
if referable.respond_to?(:project)
- referable.to_reference(target_project)
+ referable.to_reference(target_parent)
else
- referable.to_reference(@source_project, target_project: target_project)
+ referable.to_reference(@source_parent, target_project: target_parent)
end
end
@@ -91,7 +93,7 @@ module Gitlab
end
def markdown(text)
- Banzai.render(text, project: @source_project, no_original_data: true)
+ Banzai.render(text, project: @source_parent, no_original_data: true)
end
end
end
diff --git a/lib/gitlab/gfm/uploads_rewriter.rb b/lib/gitlab/gfm/uploads_rewriter.rb
index f7e66697da3..3f06badf5d9 100644
--- a/lib/gitlab/gfm/uploads_rewriter.rb
+++ b/lib/gitlab/gfm/uploads_rewriter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'fileutils'
module Gitlab
@@ -16,14 +18,15 @@ module Gitlab
@pattern = FileUploader::MARKDOWN_PATTERN
end
- def rewrite(target_project)
+ def rewrite(target_parent)
return @text unless needs_rewrite?
@text.gsub(@pattern) do |markdown|
file = find_file(@source_project, $~[:secret], $~[:file])
break markdown unless file.try(:exists?)
- moved = FileUploader.copy_to(file, target_project)
+ klass = target_parent.is_a?(Namespace) ? NamespaceFileUploader : FileUploader
+ moved = klass.copy_to(file, target_parent)
moved.markdown_link
end
end
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 2913a3e416d..c4aac228b2f 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'gitlab/encoding_helper'
module Gitlab
diff --git a/lib/gitlab/git/attributes_at_ref_parser.rb b/lib/gitlab/git/attributes_at_ref_parser.rb
index 26b5bd520d5..cbddf836ce8 100644
--- a/lib/gitlab/git/attributes_at_ref_parser.rb
+++ b/lib/gitlab/git/attributes_at_ref_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
# Parses root .gitattributes file at a given ref
diff --git a/lib/gitlab/git/attributes_parser.rb b/lib/gitlab/git/attributes_parser.rb
index 08f4d7d4f5c..8b9d74ae8e7 100644
--- a/lib/gitlab/git/attributes_parser.rb
+++ b/lib/gitlab/git/attributes_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
# Class for parsing Git attribute files and extracting the attributes for
diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb
index e25e15f5c80..b118eda37f8 100644
--- a/lib/gitlab/git/blame.rb
+++ b/lib/gitlab/git/blame.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class Blame
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 71857bd2d87..9dd1c484d59 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -1,15 +1,18 @@
+# frozen_string_literal: true
+
# Gitaly note: JV: seems to be completely migrated (behind feature flags).
module Gitlab
module Git
class Blob
- include Linguist::BlobHelper
+ include Gitlab::BlobHelper
include Gitlab::EncodingHelper
+ extend Gitlab::Git::WrapsGitalyErrors
# This number is the maximum amount of data that we want to display to
- # the user. We load as much as we can for encoding detection
- # (Linguist) and LFS pointer parsing. All other cases where we need full
- # blob data should use load_all_data!.
+ # the user. We load as much as we can for encoding detection and LFS
+ # pointer parsing. All other cases where we need full blob data should
+ # use load_all_data!.
MAX_DATA_DISPLAY_SIZE = 10.megabytes
# These limits are used as a heuristic to ignore files which can't be LFS
@@ -75,7 +78,7 @@ module Gitlab
# Returns array of Gitlab::Git::Blob
# Does not guarantee blob data will be set
def batch_lfs_pointers(repository, blob_ids)
- repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
repository.gitaly_blob_client.batch_lfs_pointers(blob_ids.to_a)
end
end
diff --git a/lib/gitlab/git/blob_snippet.rb b/lib/gitlab/git/blob_snippet.rb
deleted file mode 100644
index 68116e775c6..00000000000
--- a/lib/gitlab/git/blob_snippet.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# Gitaly note: JV: no RPC's here.
-
-module Gitlab
- module Git
- class BlobSnippet
- include Linguist::BlobHelper
-
- attr_accessor :ref
- attr_accessor :lines
- attr_accessor :filename
- attr_accessor :startline
-
- def initialize(ref, lines, startline, filename)
- @ref, @lines, @startline, @filename = ref, lines, startline, filename
- end
-
- def data
- lines&.join("\n")
- end
-
- def name
- filename
- end
-
- def size
- data.length
- end
-
- def mode
- nil
- end
- end
- end
-end
diff --git a/lib/gitlab/git/branch.rb b/lib/gitlab/git/branch.rb
index 6351cfb83e3..9447cfa0fb6 100644
--- a/lib/gitlab/git/branch.rb
+++ b/lib/gitlab/git/branch.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class Branch < Ref
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index 5b264868af0..4f05c4b73a1 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
# Gitlab::Git::Commit is a wrapper around Gitaly::GitCommit
module Gitlab
module Git
class Commit
include Gitlab::EncodingHelper
+ extend Gitlab::Git::WrapsGitalyErrors
attr_accessor :raw_commit, :head
@@ -53,16 +56,13 @@ module Gitlab
# Already a commit?
return commit_id if commit_id.is_a?(Gitlab::Git::Commit)
- # A rugged reference?
- commit_id = Gitlab::Git::Ref.dereference_object(commit_id)
-
# Some weird thing?
return nil unless commit_id.is_a?(String)
# This saves us an RPC round trip.
return nil if commit_id.include?(':')
- commit = repo.wrapped_gitaly_errors do
+ commit = wrapped_gitaly_errors do
repo.gitaly_commit_client.find_commit(commit_id)
end
@@ -103,7 +103,7 @@ module Gitlab
# Commit.between(repo, '29eda46b', 'master')
#
def between(repo, base, head)
- repo.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
repo.gitaly_commit_client.between(base, head)
end
end
@@ -127,10 +127,8 @@ module Gitlab
# :topo, or any combination of them (in an array). Commit ordering types
# are documented here:
# http://www.rubydoc.info/github/libgit2/rugged/Rugged#SORT_NONE-constant)
- #
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/326
def find_all(repo, options = {})
- repo.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
Gitlab::GitalyClient::CommitService.new(repo).find_all_commits(options)
end
end
@@ -147,7 +145,7 @@ module Gitlab
# relation to each other. The last 10 commits for a branch for example,
# should go through .where
def batch_by_oid(repo, oids)
- repo.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
repo.gitaly_commit_client.list_commits_by_oid(oids)
end
end
@@ -328,7 +326,6 @@ module Gitlab
entry = @repository.gitaly_commit_client.tree_entry(id, path, 1)
return unless entry
- # To be compatible with the rugged format
entry = entry.to_h
entry.delete(:data)
entry[:name] = File.basename(path)
@@ -346,8 +343,8 @@ module Gitlab
subject: message_split[0] ? message_split[0].chomp.b : "",
body: raw_commit.message.b,
parent_ids: raw_commit.parent_ids,
- author: gitaly_commit_author_from_rugged(raw_commit.author),
- committer: gitaly_commit_author_from_rugged(raw_commit.committer)
+ author: gitaly_commit_author_from_raw(raw_commit.author),
+ committer: gitaly_commit_author_from_raw(raw_commit.committer)
)
end
@@ -381,7 +378,7 @@ module Gitlab
SERIALIZE_KEYS
end
- def gitaly_commit_author_from_rugged(author_or_committer)
+ def gitaly_commit_author_from_raw(author_or_committer)
Gitaly::CommitAuthor.new(
name: author_or_committer[:name].b,
email: author_or_committer[:email].b,
diff --git a/lib/gitlab/git/commit_stats.rb b/lib/gitlab/git/commit_stats.rb
index ae6f554bc06..8815088d23c 100644
--- a/lib/gitlab/git/commit_stats.rb
+++ b/lib/gitlab/git/commit_stats.rb
@@ -1,8 +1,12 @@
+# frozen_string_literal: true
+
# Gitlab::Git::CommitStats counts the additions, deletions, and total changes
# in a commit.
module Gitlab
module Git
class CommitStats
+ include Gitlab::Git::WrapsGitalyErrors
+
attr_reader :id, :additions, :deletions, :total
# Instantiate a CommitStats object
@@ -14,7 +18,7 @@ module Gitlab
@deletions = 0
@total = 0
- repo.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_stats(repo, commit)
end
end
diff --git a/lib/gitlab/git/committer_with_hooks.rb b/lib/gitlab/git/committer_with_hooks.rb
deleted file mode 100644
index 4198be7c9c9..00000000000
--- a/lib/gitlab/git/committer_with_hooks.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-module Gitlab
- module Git
- class CommitterWithHooks < Gollum::Committer
- attr_reader :gl_wiki
-
- def initialize(gl_wiki, options = {})
- @gl_wiki = gl_wiki
- super(gl_wiki.gollum_wiki, options)
- end
-
- def commit
- # TODO: Remove after 10.8
- return super unless allowed_to_run_hooks?
-
- result = Gitlab::Git::OperationService.new(git_user, gl_wiki.repository).with_branch(
- @wiki.ref,
- start_branch_name: @wiki.ref
- ) do |start_commit|
- super(false)
- end
-
- result[:newrev]
- rescue Gitlab::Git::PreReceiveError => e
- message = "Custom Hook failed: #{e.message}"
- raise Gitlab::Git::Wiki::OperationError, message
- end
-
- private
-
- # TODO: Remove after 10.8
- def allowed_to_run_hooks?
- @options[:user_id] != 0 && @options[:username].present?
- end
-
- def git_user
- @git_user ||= Gitlab::Git::User.new(@options[:username],
- @options[:name],
- @options[:email],
- gitlab_id)
- end
-
- def gitlab_id
- Gitlab::GlId.gl_id_from_id_value(@options[:user_id])
- end
- end
- end
-end
diff --git a/lib/gitlab/git/compare.rb b/lib/gitlab/git/compare.rb
index 7cb842256d0..ab5245ba7cb 100644
--- a/lib/gitlab/git/compare.rb
+++ b/lib/gitlab/git/compare.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitaly note: JV: no RPC's here.
module Gitlab
diff --git a/lib/gitlab/git/conflict/file.rb b/lib/gitlab/git/conflict/file.rb
index f08dab59ce4..7ffe4a7ae81 100644
--- a/lib/gitlab/git/conflict/file.rb
+++ b/lib/gitlab/git/conflict/file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
module Conflict
diff --git a/lib/gitlab/git/conflict/parser.rb b/lib/gitlab/git/conflict/parser.rb
index fb5717dd556..20de8ebde4e 100644
--- a/lib/gitlab/git/conflict/parser.rb
+++ b/lib/gitlab/git/conflict/parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
module Conflict
diff --git a/lib/gitlab/git/conflict/resolution.rb b/lib/gitlab/git/conflict/resolution.rb
index ab9be683e15..04299a2d10c 100644
--- a/lib/gitlab/git/conflict/resolution.rb
+++ b/lib/gitlab/git/conflict/resolution.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
module Conflict
diff --git a/lib/gitlab/git/conflict/resolver.rb b/lib/gitlab/git/conflict/resolver.rb
index 6dc792c16b8..26e82643a4c 100644
--- a/lib/gitlab/git/conflict/resolver.rb
+++ b/lib/gitlab/git/conflict/resolver.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
module Conflict
class Resolver
+ include Gitlab::Git::WrapsGitalyErrors
+
ConflictSideMissing = Class.new(StandardError)
ResolutionError = Class.new(StandardError)
@@ -12,7 +16,7 @@ module Gitlab
end
def conflicts
- @conflicts ||= @target_repository.wrapped_gitaly_errors do
+ @conflicts ||= wrapped_gitaly_errors do
gitaly_conflicts_client(@target_repository).list_conflict_files.to_a
end
rescue GRPC::FailedPrecondition => e
@@ -22,7 +26,7 @@ module Gitlab
end
def resolve_conflicts(source_repository, resolution, source_branch:, target_branch:)
- source_repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_conflicts_client(source_repository).resolve_conflicts(@target_repository, resolution, source_branch, target_branch)
end
end
diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb
index b58296375ef..74a4633424f 100644
--- a/lib/gitlab/git/diff.rb
+++ b/lib/gitlab/git/diff.rb
@@ -1,6 +1,5 @@
-# Gitaly note: JV: needs RPC for Gitlab::Git::Diff.between.
+# frozen_string_literal: true
-# Gitlab::Git::Diff is a wrapper around native Rugged::Diff object
module Gitlab
module Git
class Diff
@@ -22,13 +21,17 @@ module Gitlab
alias_method :expanded?, :expanded
- SERIALIZE_KEYS = %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large).freeze
+ # The default maximum content size to display a diff patch.
+ #
+ # If this value ever changes, make sure to create a migration to update
+ # current records, and default of `ApplicationSettings#diff_max_patch_bytes`.
+ DEFAULT_MAX_PATCH_BYTES = 100.kilobytes
- # The maximum size of a diff to display.
- SIZE_LIMIT = 100.kilobytes
+ # This is a limitation applied on the source (Gitaly), therefore we don't allow
+ # persisting limits over that.
+ MAX_PATCH_BYTES_UPPER_BOUND = 500.kilobytes
- # The maximum size before a diff is collapsed.
- COLLAPSE_LIMIT = 10.kilobytes
+ SERIALIZE_KEYS = %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large).freeze
class << self
def between(repo, head, base, options = {}, *paths)
@@ -52,20 +55,31 @@ module Gitlab
repo.diff(common_commit, head, actual_options, *paths)
end
- # Return a copy of the +options+ hash containing only keys that can be
- # passed to Rugged. Allowed options are:
+ # Return a copy of the +options+ hash containing only recognized keys.
+ # Allowed options are:
#
# :ignore_whitespace_change ::
# If true, changes in amount of whitespace will be ignored.
#
- # :disable_pathspec_match ::
- # If true, the given +*paths+ will be applied as exact matches,
- # instead of as fnmatch patterns.
+ # :max_files ::
+ # Limit how many files will patches be allowed for before collapsing
+ #
+ # :max_lines ::
+ # Limit how many patch lines (across all files) will be allowed for
+ # before collapsing
#
+ # :limits ::
+ # A hash with additional limits to check before collapsing patches.
+ # Allowed keys are: `max_bytes`, `safe_max_files`, `safe_max_lines`
+ # and `safe_max_bytes`
+ #
+ # :expanded ::
+ # If false, patch raw data will not be included in the diff after
+ # `max_files`, `max_lines` or any of the limits in `limits` are
+ # exceeded
def filter_diff_options(options, default_options = {})
- allowed_options = [:ignore_whitespace_change,
- :disable_pathspec_match, :paths,
- :max_files, :max_lines, :limits, :expanded]
+ allowed_options = [:ignore_whitespace_change, :max_files, :max_lines,
+ :limits, :expanded]
if default_options
actual_defaults = default_options.dup
@@ -93,10 +107,30 @@ module Gitlab
#
# "Binary files a/file/path and b/file/path differ\n"
# This is used when we detect that a diff is binary
- # using CharlockHolmes when Rugged treats it as text.
+ # using CharlockHolmes.
def binary_message(old_path, new_path)
"Binary files #{old_path} and #{new_path} differ\n"
end
+
+ # Returns the limit of bytes a single diff file can reach before it
+ # appears as 'collapsed' for end-users.
+ # By convention, it's 10% of the persisted `diff_max_patch_bytes`.
+ #
+ # Example: If we have 100k for the `diff_max_patch_bytes`, it will be 10k by
+ # default.
+ #
+ # Patches surpassing this limit should still be persisted in the database.
+ def patch_safe_limit_bytes
+ patch_hard_limit_bytes / 10
+ end
+
+ # Returns the limit for a single diff file (patch).
+ #
+ # Patches surpassing this limit shouldn't be persisted in the database
+ # and will be presented as 'too large' for end-users.
+ def patch_hard_limit_bytes
+ Gitlab::CurrentSettings.diff_max_patch_bytes
+ end
end
def initialize(raw_diff, expanded: true)
@@ -106,8 +140,6 @@ module Gitlab
when Hash
init_from_hash(raw_diff)
prune_diff_if_eligible
- when Rugged::Patch, Rugged::Diff::Delta
- init_from_rugged(raw_diff)
when Gitlab::GitalyClient::Diff
init_from_gitaly(raw_diff)
prune_diff_if_eligible
@@ -144,7 +176,7 @@ module Gitlab
def too_large?
if @too_large.nil?
- @too_large = @diff.bytesize >= SIZE_LIMIT
+ @too_large = @diff.bytesize >= self.class.patch_hard_limit_bytes
else
@too_large
end
@@ -162,7 +194,7 @@ module Gitlab
def collapsed?
return @collapsed if defined?(@collapsed)
- @collapsed = !expanded && @diff.bytesize >= COLLAPSE_LIMIT
+ @collapsed = !expanded && @diff.bytesize >= self.class.patch_safe_limit_bytes
end
def collapse!
@@ -184,31 +216,6 @@ module Gitlab
private
- def init_from_rugged(rugged)
- if rugged.is_a?(Rugged::Patch)
- init_from_rugged_patch(rugged)
- d = rugged.delta
- else
- d = rugged
- end
-
- @new_path = encode!(d.new_file[:path])
- @old_path = encode!(d.old_file[:path])
- @a_mode = d.old_file[:mode].to_s(8)
- @b_mode = d.new_file[:mode].to_s(8)
- @new_file = d.added?
- @renamed_file = d.renamed?
- @deleted_file = d.deleted?
- end
-
- def init_from_rugged_patch(patch)
- # Don't bother initializing diffs that are too large. If a diff is
- # binary we're not going to display anything so we skip the size check.
- return if !patch.delta.binary? && prune_large_patch(patch)
-
- @diff = encode!(strip_diff_headers(patch.to_s))
- end
-
def init_from_hash(hash)
raw_diff = hash.symbolize_keys
@@ -226,6 +233,7 @@ module Gitlab
@new_file = diff.from_id == BLANK_SHA
@renamed_file = diff.from_path != diff.to_path
@deleted_file = diff.to_id == BLANK_SHA
+ @too_large = diff.too_large if diff.respond_to?(:too_large)
collapse! if diff.respond_to?(:collapsed) && diff.collapsed
end
@@ -237,47 +245,6 @@ module Gitlab
collapse!
end
end
-
- # If the patch surpasses any of the diff limits it calls the appropiate
- # prune method and returns true. Otherwise returns false.
- def prune_large_patch(patch)
- size = 0
-
- patch.each_hunk do |hunk|
- hunk.each_line do |line|
- size += line.content.bytesize
-
- if size >= SIZE_LIMIT
- too_large!
- return true # rubocop:disable Cop/AvoidReturnFromBlocks
- end
- end
- end
-
- if !expanded && size >= COLLAPSE_LIMIT
- collapse!
- return true
- end
-
- false
- end
-
- # Strip out the information at the beginning of the patch's text to match
- # Grit's output
- def strip_diff_headers(diff_text)
- # Delete everything up to the first line that starts with '---' or
- # 'Binary'
- diff_text.sub!(/\A.*?^(---|Binary)/m, '\1')
-
- if diff_text.start_with?('---', 'Binary')
- diff_text
- else
- # If the diff_text did not contain a line starting with '---' or
- # 'Binary', return the empty string. No idea why; we are just
- # preserving behavior from before the refactor.
- ''
- end
- end
end
end
end
diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb
index 219c69893ad..5c70cb6c66c 100644
--- a/lib/gitlab/git/diff_collection.rb
+++ b/lib/gitlab/git/diff_collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitaly note: JV: no RPC's here.
module Gitlab
@@ -11,7 +13,7 @@ module Gitlab
delegate :max_files, :max_lines, :max_bytes, :safe_max_files, :safe_max_lines, :safe_max_bytes, to: :limits
- def self.collection_limits(options = {})
+ def self.limits(options = {})
limits = {}
limits[:max_files] = options.fetch(:max_files, DEFAULT_LIMITS[:max_files])
limits[:max_lines] = options.fetch(:max_lines, DEFAULT_LIMITS[:max_lines])
@@ -19,13 +21,14 @@ module Gitlab
limits[:safe_max_files] = [limits[:max_files], DEFAULT_LIMITS[:max_files]].min
limits[:safe_max_lines] = [limits[:max_lines], DEFAULT_LIMITS[:max_lines]].min
limits[:safe_max_bytes] = limits[:safe_max_files] * 5.kilobytes # Average 5 KB per file
+ limits[:max_patch_bytes] = Gitlab::Git::Diff.patch_hard_limit_bytes
OpenStruct.new(limits)
end
def initialize(iterator, options = {})
@iterator = iterator
- @limits = self.class.collection_limits(options)
+ @limits = self.class.limits(options)
@enforce_limits = !!options.fetch(:limits, true)
@expanded = !!options.fetch(:expanded, true)
diff --git a/lib/gitlab/git/diff_stats_collection.rb b/lib/gitlab/git/diff_stats_collection.rb
new file mode 100644
index 00000000000..998c41497a2
--- /dev/null
+++ b/lib/gitlab/git/diff_stats_collection.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ class DiffStatsCollection
+ include Gitlab::Utils::StrongMemoize
+ include Enumerable
+
+ def initialize(diff_stats)
+ @collection = diff_stats
+ end
+
+ def each(&block)
+ @collection.each(&block)
+ end
+
+ def find_by_path(path)
+ indexed_by_path[path]
+ end
+
+ def paths
+ @collection.map(&:path)
+ end
+
+ private
+
+ def indexed_by_path
+ strong_memoize(:indexed_by_path) do
+ index_by { |stats| stats.path }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/gitlab_projects.rb b/lib/gitlab/git/gitlab_projects.rb
deleted file mode 100644
index 5ff15a787f0..00000000000
--- a/lib/gitlab/git/gitlab_projects.rb
+++ /dev/null
@@ -1,253 +0,0 @@
-module Gitlab
- module Git
- class GitlabProjects
- include Gitlab::Git::Popen
- include Gitlab::Utils::StrongMemoize
-
- # Name of shard where repositories are stored.
- # Example: nfs-file06
- attr_reader :shard_name
-
- # Relative path is a directory name for repository with .git at the end.
- # Example: gitlab-org/gitlab-test.git
- attr_reader :repository_relative_path
-
- # This is the path at which the gitlab-shell hooks directory can be found.
- # It's essential for integration between git and GitLab proper. All new
- # repositories should have their hooks directory symlinked here.
- attr_reader :global_hooks_path
-
- attr_reader :logger
-
- def initialize(shard_name, repository_relative_path, global_hooks_path:, logger:)
- @shard_name = shard_name
- @repository_relative_path = repository_relative_path
-
- @logger = logger
- @global_hooks_path = global_hooks_path
- @output = StringIO.new
- end
-
- def output
- io = @output.dup
- io.rewind
- io.read
- end
-
- # Absolute path to the repository.
- # Example: /home/git/repositorities/gitlab-org/gitlab-test.git
- # Probably will be removed when we fully migrate to Gitaly, part of
- # https://gitlab.com/gitlab-org/gitaly/issues/1124.
- def repository_absolute_path
- strong_memoize(:repository_absolute_path) do
- File.join(shard_path, repository_relative_path)
- end
- end
-
- def shard_path
- strong_memoize(:shard_path) do
- Gitlab.config.repositories.storages.fetch(shard_name).legacy_disk_path
- end
- end
-
- # Import project via git clone --bare
- # URL must be publicly cloneable
- def import_project(source, timeout)
- git_import_repository(source, timeout)
- end
-
- def fork_repository(new_shard_name, new_repository_relative_path)
- git_fork_repository(new_shard_name, new_repository_relative_path)
- end
-
- def fetch_remote(name, timeout, force:, tags:, ssh_key: nil, known_hosts: nil, prune: true)
- logger.info "Fetching remote #{name} for repository #{repository_absolute_path}."
- cmd = fetch_remote_command(name, tags, prune, force)
-
- setup_ssh_auth(ssh_key, known_hosts) do |env|
- run_with_timeout(cmd, timeout, repository_absolute_path, env).tap do |success|
- unless success
- logger.error "Fetching remote #{name} for repository #{repository_absolute_path} failed."
- end
- end
- end
- end
-
- def push_branches(remote_name, timeout, force, branch_names)
- logger.info "Pushing branches from #{repository_absolute_path} to remote #{remote_name}: #{branch_names}"
- cmd = %W(#{Gitlab.config.git.bin_path} push)
- cmd << '--force' if force
- cmd += %W(-- #{remote_name}).concat(branch_names)
-
- success = run_with_timeout(cmd, timeout, repository_absolute_path)
-
- unless success
- logger.error("Pushing branches to remote #{remote_name} failed.")
- end
-
- success
- end
-
- def delete_remote_branches(remote_name, branch_names)
- branches = branch_names.map { |branch_name| ":#{branch_name}" }
-
- logger.info "Pushing deleted branches from #{repository_absolute_path} to remote #{remote_name}: #{branch_names}"
- cmd = %W(#{Gitlab.config.git.bin_path} push -- #{remote_name}).concat(branches)
-
- success = run(cmd, repository_absolute_path)
-
- unless success
- logger.error("Pushing deleted branches to remote #{remote_name} failed.")
- end
-
- success
- end
-
- protected
-
- def run(*args)
- output, exitstatus = popen(*args)
- @output << output
-
- exitstatus&.zero?
- end
-
- def run_with_timeout(*args)
- output, exitstatus = popen_with_timeout(*args)
- @output << output
-
- exitstatus&.zero?
- rescue Timeout::Error
- @output.puts('Timed out')
-
- false
- end
-
- def mask_password_in_url(url)
- result = URI(url)
- result.password = "*****" unless result.password.nil?
- result.user = "*****" unless result.user.nil? # it's needed for oauth access_token
- result
- rescue
- url
- end
-
- def remove_origin_in_repo
- cmd = %W(#{Gitlab.config.git.bin_path} remote rm origin)
- run(cmd, repository_absolute_path)
- end
-
- # Builds a small shell script that can be used to execute SSH with a set of
- # custom options.
- #
- # Options are expanded as `'-oKey="Value"'`, so SSH will correctly interpret
- # paths with spaces in them. We trust the user not to embed single or double
- # quotes in the key or value.
- def custom_ssh_script(options = {})
- args = options.map { |k, v| %Q{'-o#{k}="#{v}"'} }.join(' ')
-
- [
- "#!/bin/sh",
- "exec ssh #{args} \"$@\""
- ].join("\n")
- end
-
- # Known hosts data and private keys can be passed to gitlab-shell in the
- # environment. If present, this method puts them into temporary files, writes
- # a script that can substitute as `ssh`, setting the options to respect those
- # files, and yields: { "GIT_SSH" => "/tmp/myScript" }
- def setup_ssh_auth(key, known_hosts)
- options = {}
-
- if key
- key_file = Tempfile.new('gitlab-shell-key-file')
- key_file.chmod(0o400)
- key_file.write(key)
- key_file.close
-
- options['IdentityFile'] = key_file.path
- options['IdentitiesOnly'] = 'yes'
- end
-
- if known_hosts
- known_hosts_file = Tempfile.new('gitlab-shell-known-hosts')
- known_hosts_file.chmod(0o400)
- known_hosts_file.write(known_hosts)
- known_hosts_file.close
-
- options['StrictHostKeyChecking'] = 'yes'
- options['UserKnownHostsFile'] = known_hosts_file.path
- end
-
- return yield({}) if options.empty?
-
- script = Tempfile.new('gitlab-shell-ssh-wrapper')
- script.chmod(0o755)
- script.write(custom_ssh_script(options))
- script.close
-
- yield('GIT_SSH' => script.path)
- ensure
- key_file&.close!
- known_hosts_file&.close!
- script&.close!
- end
-
- private
-
- def fetch_remote_command(name, tags, prune, force)
- %W(#{Gitlab.config.git.bin_path} fetch #{name} --quiet).tap do |cmd|
- cmd << '--prune' if prune
- cmd << '--force' if force
- cmd << (tags ? '--tags' : '--no-tags')
- end
- end
-
- def git_import_repository(source, timeout)
- # Skip import if repo already exists
- return false if File.exist?(repository_absolute_path)
-
- masked_source = mask_password_in_url(source)
-
- logger.info "Importing project from <#{masked_source}> to <#{repository_absolute_path}>."
- cmd = %W(#{Gitlab.config.git.bin_path} clone --bare -- #{source} #{repository_absolute_path})
-
- success = run_with_timeout(cmd, timeout, nil)
-
- unless success
- logger.error("Importing project from <#{masked_source}> to <#{repository_absolute_path}> failed.")
- FileUtils.rm_rf(repository_absolute_path)
- return false
- end
-
- Gitlab::Git::Repository.create_hooks(repository_absolute_path, global_hooks_path)
-
- # The project was imported successfully.
- # Remove the origin URL since it may contain password.
- remove_origin_in_repo
-
- true
- end
-
- def git_fork_repository(new_shard_name, new_repository_relative_path)
- from_path = repository_absolute_path
- new_shard_path = Gitlab.config.repositories.storages.fetch(new_shard_name).legacy_disk_path
- to_path = File.join(new_shard_path, new_repository_relative_path)
-
- # The repository cannot already exist
- if File.exist?(to_path)
- logger.error "fork-repository failed: destination repository <#{to_path}> already exists."
- return false
- end
-
- # Ensure the namepsace / hashed storage directory exists
- FileUtils.mkdir_p(File.dirname(to_path), mode: 0770)
-
- logger.info "Forking repository from <#{from_path}> to <#{to_path}>."
- cmd = %W(#{Gitlab.config.git.bin_path} clone --bare --no-local -- #{from_path} #{to_path})
-
- run(cmd, nil) && Gitlab::Git::Repository.create_hooks(to_path, global_hooks_path)
- end
- end
- end
-end
diff --git a/lib/gitlab/git/gitmodules_parser.rb b/lib/gitlab/git/gitmodules_parser.rb
index 4b505312f60..575e12390cd 100644
--- a/lib/gitlab/git/gitmodules_parser.rb
+++ b/lib/gitlab/git/gitmodules_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitaly note: JV: no RPC's here.
module Gitlab
diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb
deleted file mode 100644
index 94ff5b4980a..00000000000
--- a/lib/gitlab/git/hook.rb
+++ /dev/null
@@ -1,108 +0,0 @@
-# Gitaly note: JV: looks like this is only used by Gitlab::Git::HooksService in
-# app/services. We shouldn't bother migrating this until we know how
-# Gitlab::Git::HooksService will be migrated.
-
-module Gitlab
- module Git
- class Hook
- GL_PROTOCOL = 'web'.freeze
- attr_reader :name, :path, :repository
-
- def initialize(name, repository)
- @name = name
- @repository = repository
- @path = File.join(repo_path, 'hooks', name)
- end
-
- def repo_path
- repository.path
- end
-
- def exists?
- File.exist?(path)
- end
-
- def trigger(gl_id, gl_username, oldrev, newrev, ref)
- return [true, nil] unless exists?
-
- Bundler.with_clean_env do
- case name
- when "pre-receive", "post-receive"
- call_receive_hook(gl_id, gl_username, oldrev, newrev, ref)
- when "update"
- call_update_hook(gl_id, gl_username, oldrev, newrev, ref)
- end
- end
- end
-
- private
-
- def call_receive_hook(gl_id, gl_username, oldrev, newrev, ref)
- changes = [oldrev, newrev, ref].join(" ")
-
- exit_status = false
- exit_message = nil
-
- vars = {
- 'GL_ID' => gl_id,
- 'GL_USERNAME' => gl_username,
- 'PWD' => repo_path,
- 'GL_PROTOCOL' => GL_PROTOCOL,
- 'GL_REPOSITORY' => repository.gl_repository
- }
-
- options = {
- chdir: repo_path
- }
-
- Open3.popen3(vars, path, options) do |stdin, stdout, stderr, wait_thr|
- exit_status = true
- stdin.sync = true
-
- # in git, pre- and post- receive hooks may just exit without
- # reading stdin. We catch the exception to avoid a broken pipe
- # warning
- begin
- # inject all the changes as stdin to the hook
- changes.lines do |line|
- stdin.puts line
- end
- rescue Errno::EPIPE
- end
-
- stdin.close
-
- unless wait_thr.value == 0
- exit_status = false
- exit_message = retrieve_error_message(stderr, stdout)
- end
- end
-
- [exit_status, exit_message]
- end
-
- def call_update_hook(gl_id, gl_username, oldrev, newrev, ref)
- env = {
- 'GL_ID' => gl_id,
- 'GL_USERNAME' => gl_username,
- 'PWD' => repo_path
- }
-
- options = {
- chdir: repo_path
- }
-
- args = [ref, oldrev, newrev]
-
- stdout, stderr, status = Open3.capture3(env, path, *args, options)
- [status.success?, stderr.presence || stdout]
- end
-
- def retrieve_error_message(stderr, stdout)
- err_message = stderr.read
- err_message = err_message.blank? ? stdout.read : err_message
- err_message
- end
- end
- end
-end
diff --git a/lib/gitlab/git/hook_env.rb b/lib/gitlab/git/hook_env.rb
index 455e8451c10..892a069a3b7 100644
--- a/lib/gitlab/git/hook_env.rb
+++ b/lib/gitlab/git/hook_env.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitaly note: JV: no RPC's here.
module Gitlab
@@ -17,18 +19,18 @@ module Gitlab
].freeze
def self.set(gl_repository, env)
- return unless RequestStore.active?
+ return unless Gitlab::SafeRequestStore.active?
raise "missing gl_repository" if gl_repository.blank?
- RequestStore.store[:gitlab_git_env] ||= {}
- RequestStore.store[:gitlab_git_env][gl_repository] = whitelist_git_env(env)
+ Gitlab::SafeRequestStore[:gitlab_git_env] ||= {}
+ Gitlab::SafeRequestStore[:gitlab_git_env][gl_repository] = whitelist_git_env(env)
end
def self.all(gl_repository)
- return {} unless RequestStore.active?
+ return {} unless Gitlab::SafeRequestStore.active?
- h = RequestStore.fetch(:gitlab_git_env) { {} }
+ h = Gitlab::SafeRequestStore.fetch(:gitlab_git_env) { {} }
h.fetch(gl_repository, {})
end
diff --git a/lib/gitlab/git/hooks_service.rb b/lib/gitlab/git/hooks_service.rb
deleted file mode 100644
index e67cacdb95a..00000000000
--- a/lib/gitlab/git/hooks_service.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-module Gitlab
- module Git
- class HooksService
- attr_accessor :oldrev, :newrev, :ref
-
- def execute(pusher, repository, oldrev, newrev, ref)
- @repository = repository
- @gl_id = pusher.gl_id
- @gl_username = pusher.username
- @oldrev = oldrev
- @newrev = newrev
- @ref = ref
-
- %w(pre-receive update).each do |hook_name|
- status, message = run_hook(hook_name)
-
- unless status
- raise PreReceiveError, message
- end
- end
-
- yield(self).tap do
- run_hook('post-receive')
- end
- end
-
- private
-
- def run_hook(name)
- hook = Gitlab::Git::Hook.new(name, @repository)
- hook.trigger(@gl_id, @gl_username, oldrev, newrev, ref)
- end
- end
- end
-end
diff --git a/lib/gitlab/git/index.rb b/lib/gitlab/git/index.rb
index d94082a3e30..3b9b516308f 100644
--- a/lib/gitlab/git/index.rb
+++ b/lib/gitlab/git/index.rb
@@ -1,157 +1,9 @@
-# Gitaly note: JV: When the time comes I think we will want to copy this
-# class into Gitaly. None of its methods look like they should be RPC's.
-# The RPC's will be at a higher level.
+# frozen_string_literal: true
module Gitlab
module Git
class Index
IndexError = Class.new(StandardError)
-
- DEFAULT_MODE = 0o100644
-
- ACTIONS = %w(create create_dir update move delete).freeze
- ACTION_OPTIONS = %i(file_path previous_path content encoding).freeze
-
- attr_reader :repository, :raw_index
-
- def initialize(repository)
- @repository = repository
- @raw_index = repository.rugged.index
- end
-
- delegate :read_tree, :get, to: :raw_index
-
- def apply(action, options)
- validate_action!(action)
- public_send(action, options.slice(*ACTION_OPTIONS)) # rubocop:disable GitlabSecurity/PublicSend
- end
-
- def write_tree
- raw_index.write_tree(repository.rugged)
- end
-
- def dir_exists?(path)
- raw_index.find { |entry| entry[:path].start_with?("#{path}/") }
- end
-
- def create(options)
- options = normalize_options(options)
-
- if get(options[:file_path])
- raise IndexError, "A file with this name already exists"
- end
-
- add_blob(options)
- end
-
- def create_dir(options)
- options = normalize_options(options)
-
- if get(options[:file_path])
- raise IndexError, "A file with this name already exists"
- end
-
- if dir_exists?(options[:file_path])
- raise IndexError, "A directory with this name already exists"
- end
-
- options = options.dup
- options[:file_path] += '/.gitkeep'
- options[:content] = ''
-
- add_blob(options)
- end
-
- def update(options)
- options = normalize_options(options)
-
- file_entry = get(options[:file_path])
- unless file_entry
- raise IndexError, "A file with this name doesn't exist"
- end
-
- add_blob(options, mode: file_entry[:mode])
- end
-
- def move(options)
- options = normalize_options(options)
-
- file_entry = get(options[:previous_path])
- unless file_entry
- raise IndexError, "A file with this name doesn't exist"
- end
-
- if get(options[:file_path])
- raise IndexError, "A file with this name already exists"
- end
-
- raw_index.remove(options[:previous_path])
-
- add_blob(options, mode: file_entry[:mode])
- end
-
- def delete(options)
- options = normalize_options(options)
-
- unless get(options[:file_path])
- raise IndexError, "A file with this name doesn't exist"
- end
-
- raw_index.remove(options[:file_path])
- end
-
- private
-
- def normalize_options(options)
- options = options.dup
- options[:file_path] = normalize_path(options[:file_path]) if options[:file_path]
- options[:previous_path] = normalize_path(options[:previous_path]) if options[:previous_path]
- options
- end
-
- def normalize_path(path)
- unless path
- raise IndexError, "You must provide a file path"
- end
-
- pathname = Gitlab::Git::PathHelper.normalize_path(path.dup)
-
- pathname.each_filename do |segment|
- if segment == '..'
- raise IndexError, 'Path cannot include directory traversal'
- end
- end
-
- pathname.to_s
- end
-
- def add_blob(options, mode: nil)
- content = options[:content]
- unless content
- raise IndexError, "You must provide content"
- end
-
- content = Base64.decode64(content) if options[:encoding] == 'base64'
-
- detect = CharlockHolmes::EncodingDetector.new.detect(content)
- unless detect && detect[:type] == :binary
- # When writing to the repo directly as we are doing here,
- # the `core.autocrlf` config isn't taken into account.
- content.gsub!("\r\n", "\n") if repository.autocrlf
- end
-
- oid = repository.rugged.write(content, :blob)
-
- raw_index.add(path: options[:file_path], oid: oid, mode: mode || DEFAULT_MODE)
- rescue Rugged::IndexError => e
- raise IndexError, e.message
- end
-
- def validate_action!(action)
- unless ACTIONS.include?(action.to_s)
- raise ArgumentError, "Unknown action '#{action}'"
- end
- end
end
end
end
diff --git a/lib/gitlab/git/lfs_changes.rb b/lib/gitlab/git/lfs_changes.rb
index f0fab1e76a3..8e2a925dfea 100644
--- a/lib/gitlab/git/lfs_changes.rb
+++ b/lib/gitlab/git/lfs_changes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class LfsChanges
@@ -6,8 +8,8 @@ module Gitlab
@newrev = newrev
end
- def new_pointers(object_limit: nil, not_in: nil)
- @repository.gitaly_blob_client.get_new_lfs_pointers(@newrev, object_limit, not_in)
+ def new_pointers(object_limit: nil, not_in: nil, dynamic_timeout: nil)
+ @repository.gitaly_blob_client.get_new_lfs_pointers(@newrev, object_limit, not_in, dynamic_timeout)
end
def all_pointers
diff --git a/lib/gitlab/git/lfs_pointer_file.rb b/lib/gitlab/git/lfs_pointer_file.rb
index 2ae0a889590..b7019a221ac 100644
--- a/lib/gitlab/git/lfs_pointer_file.rb
+++ b/lib/gitlab/git/lfs_pointer_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class LfsPointerFile
diff --git a/lib/gitlab/git/operation_service.rb b/lib/gitlab/git/operation_service.rb
index 57d748343be..8797d3dce24 100644
--- a/lib/gitlab/git/operation_service.rb
+++ b/lib/gitlab/git/operation_service.rb
@@ -1,8 +1,8 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class OperationService
- include Gitlab::Git::Popen
-
BranchUpdate = Struct.new(:newrev, :repo_created, :branch_created) do
alias_method :repo_created?, :repo_created
alias_method :branch_created?, :branch_created
@@ -17,177 +17,6 @@ module Gitlab
)
end
end
-
- attr_reader :user, :repository
-
- def initialize(user, new_repository)
- if user
- user = Gitlab::Git::User.from_gitlab(user) unless user.respond_to?(:gl_id)
- @user = user
- end
-
- # Refactoring aid
- Gitlab::Git.check_namespace!(new_repository)
-
- @repository = new_repository
- end
-
- def add_branch(branch_name, newrev)
- ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
- oldrev = Gitlab::Git::BLANK_SHA
-
- update_ref_in_hooks(ref, newrev, oldrev)
- end
-
- def rm_branch(branch)
- ref = Gitlab::Git::BRANCH_REF_PREFIX + branch.name
- oldrev = branch.target
- newrev = Gitlab::Git::BLANK_SHA
-
- update_ref_in_hooks(ref, newrev, oldrev)
- end
-
- def add_tag(tag_name, newrev, options = {})
- ref = Gitlab::Git::TAG_REF_PREFIX + tag_name
- oldrev = Gitlab::Git::BLANK_SHA
-
- with_hooks(ref, newrev, oldrev) do |service|
- # We want to pass the OID of the tag object to the hooks. For an
- # annotated tag we don't know that OID until after the tag object
- # (raw_tag) is created in the repository. That is why we have to
- # update the value after creating the tag object. Only the
- # "post-receive" hook will receive the correct value in this case.
- raw_tag = repository.rugged.tags.create(tag_name, newrev, options)
- service.newrev = raw_tag.target_id
- end
- end
-
- def rm_tag(tag)
- ref = Gitlab::Git::TAG_REF_PREFIX + tag.name
- oldrev = tag.target
- newrev = Gitlab::Git::BLANK_SHA
-
- update_ref_in_hooks(ref, newrev, oldrev) do
- repository.rugged.tags.delete(tag_name)
- end
- end
-
- # Whenever `start_branch_name` is passed, if `branch_name` doesn't exist,
- # it would be created from `start_branch_name`.
- # If `start_repository` is passed, and the branch doesn't exist,
- # it would try to find the commits from it instead of current repository.
- def with_branch(
- branch_name,
- start_branch_name: nil,
- start_repository: repository,
- &block)
-
- Gitlab::Git.check_namespace!(start_repository)
- start_repository = RemoteRepository.new(start_repository) unless start_repository.is_a?(RemoteRepository)
-
- start_branch_name = nil if start_repository.empty?
-
- if start_branch_name && !start_repository.branch_exists?(start_branch_name)
- raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.relative_path}"
- end
-
- update_branch_with_hooks(branch_name) do
- repository.with_repo_branch_commit(
- start_repository,
- start_branch_name || branch_name,
- &block)
- end
- end
-
- def update_branch(branch_name, newrev, oldrev)
- ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
- update_ref_in_hooks(ref, newrev, oldrev)
- end
-
- private
-
- # Returns [newrev, should_run_after_create, should_run_after_create_branch]
- def update_branch_with_hooks(branch_name)
- update_autocrlf_option
-
- was_empty = repository.empty?
-
- # Make commit
- newrev = yield
-
- unless newrev
- raise Gitlab::Git::CommitError.new('Failed to create commit')
- end
-
- branch = repository.find_branch(branch_name)
- oldrev = find_oldrev_from_branch(newrev, branch)
-
- ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
- update_ref_in_hooks(ref, newrev, oldrev)
-
- BranchUpdate.new(newrev, was_empty, was_empty || Gitlab::Git.blank_ref?(oldrev))
- end
-
- def find_oldrev_from_branch(newrev, branch)
- return Gitlab::Git::BLANK_SHA unless branch
-
- oldrev = branch.target
-
- merge_base = repository.merge_base(newrev, branch.target)
- raise Gitlab::Git::Repository::InvalidRef unless merge_base
-
- if oldrev == merge_base
- oldrev
- else
- raise Gitlab::Git::CommitError.new('Branch diverged')
- end
- end
-
- def update_ref_in_hooks(ref, newrev, oldrev)
- with_hooks(ref, newrev, oldrev) do
- update_ref(ref, newrev, oldrev)
- end
- end
-
- def with_hooks(ref, newrev, oldrev)
- Gitlab::Git::HooksService.new.execute(
- user,
- repository,
- oldrev,
- newrev,
- ref) do |service|
-
- yield(service)
- end
- end
-
- # Gitaly note: JV: wait with migrating #update_ref until we know how to migrate its call sites.
- def update_ref(ref, newrev, oldrev)
- # We use 'git update-ref' because libgit2/rugged currently does not
- # offer 'compare and swap' ref updates. Without compare-and-swap we can
- # (and have!) accidentally reset the ref to an earlier state, clobbering
- # commits. See also https://github.com/libgit2/libgit2/issues/1534.
- command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z]
-
- output, status = popen(
- command,
- repository.path) do |stdin|
- stdin.write("update #{ref}\x00#{newrev}\x00#{oldrev}\x00")
- end
-
- unless status.zero?
- Gitlab::GitLogger.error("'git update-ref' in #{repository.path}: #{output}")
- raise Gitlab::Git::CommitError.new(
- "Could not update branch #{Gitlab::Git.branch_name(ref)}." \
- " Please refresh and try again.")
- end
- end
-
- def update_autocrlf_option
- if repository.autocrlf != :input
- repository.autocrlf = :input
- end
- end
end
end
end
diff --git a/lib/gitlab/git/patches/collection.rb b/lib/gitlab/git/patches/collection.rb
new file mode 100644
index 00000000000..ad6b5d32abc
--- /dev/null
+++ b/lib/gitlab/git/patches/collection.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ module Patches
+ class Collection
+ MAX_PATCH_SIZE = 2.megabytes
+
+ def initialize(one_or_more_patches)
+ @patches = Array(one_or_more_patches).map do |patch_content|
+ Gitlab::Git::Patches::Patch.new(patch_content)
+ end
+ end
+
+ def content
+ @patches.map(&:content).join("\n")
+ end
+
+ def valid_size?
+ size < MAX_PATCH_SIZE
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ # `@patches` is not an `ActiveRecord` relation, but an `Enumerable`
+ # We're using sum from `ActiveSupport`
+ def size
+ @size ||= @patches.sum(&:size)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/patches/commit_patches.rb b/lib/gitlab/git/patches/commit_patches.rb
new file mode 100644
index 00000000000..c62994432d3
--- /dev/null
+++ b/lib/gitlab/git/patches/commit_patches.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ module Patches
+ class CommitPatches
+ include Gitlab::Git::WrapsGitalyErrors
+
+ def initialize(user, repository, branch, patch_collection)
+ @user, @repository, @branch, @patches = user, repository, branch, patch_collection
+ end
+
+ def commit
+ repository.with_cache_hooks do
+ wrapped_gitaly_errors do
+ operation_service.user_commit_patches(user, branch, patches.content)
+ end
+ end
+ end
+
+ private
+
+ attr_reader :user, :repository, :branch, :patches
+
+ def operation_service
+ repository.raw.gitaly_operation_client
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/patches/patch.rb b/lib/gitlab/git/patches/patch.rb
new file mode 100644
index 00000000000..fe6ae1b5b00
--- /dev/null
+++ b/lib/gitlab/git/patches/patch.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ module Patches
+ class Patch
+ attr_reader :content
+
+ def initialize(content)
+ @content = content
+ end
+
+ def size
+ content.bytesize
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/path_helper.rb b/lib/gitlab/git/path_helper.rb
index 57b82a37d6c..e3a2031eeca 100644
--- a/lib/gitlab/git/path_helper.rb
+++ b/lib/gitlab/git/path_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitaly note: JV: no RPC's here.
module Gitlab
diff --git a/lib/gitlab/git/popen.rb b/lib/gitlab/git/popen.rb
deleted file mode 100644
index 7426688fc55..00000000000
--- a/lib/gitlab/git/popen.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# Gitaly note: JV: no RPC's here.
-
-require 'open3'
-
-module Gitlab
- module Git
- module Popen
- FAST_GIT_PROCESS_TIMEOUT = 15.seconds
-
- def popen(cmd, path, vars = {}, lazy_block: nil)
- unless cmd.is_a?(Array)
- raise "System commands must be given as an array of strings"
- end
-
- path ||= Dir.pwd
- vars['PWD'] = path
- options = { chdir: path }
-
- cmd_output = ""
- cmd_status = 0
- Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
- stdout.set_encoding(Encoding::ASCII_8BIT)
-
- # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082
- # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273
- err_reader = Thread.new { stderr.read }
-
- yield(stdin) if block_given?
- stdin.close
-
- if lazy_block
- cmd_output = lazy_block.call(stdout.lazy)
- cmd_status = 0
- break
- else
- cmd_output << stdout.read
- end
-
- cmd_output << err_reader.value
- cmd_status = wait_thr.value.exitstatus
- end
-
- [cmd_output, cmd_status]
- end
-
- def popen_with_timeout(cmd, timeout, path, vars = {})
- unless cmd.is_a?(Array)
- raise "System commands must be given as an array of strings"
- end
-
- path ||= Dir.pwd
- vars['PWD'] = path
-
- unless File.directory?(path)
- FileUtils.mkdir_p(path)
- end
-
- rout, wout = IO.pipe
- rerr, werr = IO.pipe
-
- pid = Process.spawn(vars, *cmd, out: wout, err: werr, chdir: path, pgroup: true)
- # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082
- # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273
- out_reader = Thread.new { rout.read }
- err_reader = Thread.new { rerr.read }
-
- begin
- # close write ends so we could read them
- wout.close
- werr.close
-
- status = process_wait_with_timeout(pid, timeout)
-
- cmd_output = out_reader.value
- cmd_output << err_reader.value # Copying the behaviour of `popen` which merges stderr into output
-
- [cmd_output, status.exitstatus]
- rescue Timeout::Error => e
- kill_process_group_for_pid(pid)
-
- raise e
- ensure
- wout.close unless wout.closed?
- werr.close unless werr.closed?
-
- rout.close
- rerr.close
- end
- end
-
- def process_wait_with_timeout(pid, timeout)
- deadline = timeout.seconds.from_now
- wait_time = 0.01
-
- while deadline > Time.now
- sleep(wait_time)
- _, status = Process.wait2(pid, Process::WNOHANG)
-
- return status unless status.nil?
- end
-
- raise Timeout::Error, "Timeout waiting for process ##{pid}"
- end
-
- def kill_process_group_for_pid(pid)
- Process.kill("KILL", -pid)
- Process.wait(pid)
- rescue Errno::ESRCH
- end
- end
- end
-end
diff --git a/lib/gitlab/git/pre_receive_error.rb b/lib/gitlab/git/pre_receive_error.rb
index ac1ab7c39d5..03caace6fce 100644
--- a/lib/gitlab/git/pre_receive_error.rb
+++ b/lib/gitlab/git/pre_receive_error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
#
diff --git a/lib/gitlab/git/push.rb b/lib/gitlab/git/push.rb
new file mode 100644
index 00000000000..b6577ba17f1
--- /dev/null
+++ b/lib/gitlab/git/push.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ class Push
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :ref, :oldrev, :newrev
+
+ def initialize(project, oldrev, newrev, ref)
+ @project = project
+ @oldrev = oldrev.presence || Gitlab::Git::BLANK_SHA
+ @newrev = newrev.presence || Gitlab::Git::BLANK_SHA
+ @ref = ref
+ end
+
+ def branch_name
+ strong_memoize(:branch_name) do
+ Gitlab::Git.branch_name(@ref)
+ end
+ end
+
+ def branch_added?
+ Gitlab::Git.blank_ref?(@oldrev)
+ end
+
+ def branch_removed?
+ Gitlab::Git.blank_ref?(@newrev)
+ end
+
+ def branch_updated?
+ branch_push? && !branch_added? && !branch_removed?
+ end
+
+ def force_push?
+ Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
+ end
+
+ def branch_push?
+ strong_memoize(:branch_push) do
+ Gitlab::Git.branch_ref?(@ref)
+ end
+ end
+
+ def modified_paths
+ unless branch_updated?
+ raise ArgumentError, 'Unable to calculate modified paths!'
+ end
+
+ strong_memoize(:modified_paths) do
+ @project.repository.diff_stats(@oldrev, @newrev).paths
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/raw_diff_change.rb b/lib/gitlab/git/raw_diff_change.rb
index 98de9328071..e1002af40f6 100644
--- a/lib/gitlab/git/raw_diff_change.rb
+++ b/lib/gitlab/git/raw_diff_change.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
# This class behaves like a struct with fields :blob_id, :blob_size, :operation, :old_path, :new_path
diff --git a/lib/gitlab/git/ref.rb b/lib/gitlab/git/ref.rb
index fa71a4e7ea7..eec91194949 100644
--- a/lib/gitlab/git/ref.rb
+++ b/lib/gitlab/git/ref.rb
@@ -1,4 +1,4 @@
-# Gitaly note: JV: probably no RPC's here (just one interaction with Rugged).
+# frozen_string_literal: true
module Gitlab
module Git
@@ -26,13 +26,6 @@ module Gitlab
str.gsub(%r{\Arefs/heads/}, '')
end
- # Gitaly: this method will probably be migrated indirectly via its call sites.
- def self.dereference_object(object)
- object = object.target while object.is_a?(Rugged::Tag::Annotation)
-
- object
- end
-
def initialize(repository, name, target, dereferenced_target)
@name = Gitlab::Git.ref_name(name)
@dereferenced_target = dereferenced_target
diff --git a/lib/gitlab/git/remote_mirror.rb b/lib/gitlab/git/remote_mirror.rb
index e4743b4db0a..e992d522e7f 100644
--- a/lib/gitlab/git/remote_mirror.rb
+++ b/lib/gitlab/git/remote_mirror.rb
@@ -1,13 +1,17 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class RemoteMirror
+ include Gitlab::Git::WrapsGitalyErrors
+
def initialize(repository, ref_name)
@repository = repository
@ref_name = ref_name
end
def update(only_branches_matching: [])
- @repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
@repository.gitaly_remote_client.update_remote_mirror(@ref_name, only_branches_matching)
end
end
diff --git a/lib/gitlab/git/remote_repository.rb b/lib/gitlab/git/remote_repository.rb
index f40e59a8dd0..234541d8145 100644
--- a/lib/gitlab/git/remote_repository.rb
+++ b/lib/gitlab/git/remote_repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
#
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 3e11355435b..993955d1a6b 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'tempfile'
require 'forwardable'
require "rubygems/package"
@@ -6,18 +8,10 @@ module Gitlab
module Git
class Repository
include Gitlab::Git::RepositoryMirroring
- include Gitlab::Git::Popen
+ include Gitlab::Git::WrapsGitalyErrors
include Gitlab::EncodingHelper
include Gitlab::Utils::StrongMemoize
- ALLOWED_OBJECT_DIRECTORIES_VARIABLES = %w[
- GIT_OBJECT_DIRECTORY
- GIT_ALTERNATE_OBJECT_DIRECTORIES
- ].freeze
- ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES = %w[
- GIT_OBJECT_DIRECTORY_RELATIVE
- GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE
- ].freeze
SEARCH_CONTEXT_LINES = 3
REV_LIST_COMMIT_LIMIT = 2_000
# In https://gitlab.com/gitlab-org/gitaly/merge_requests/698
@@ -73,7 +67,7 @@ module Gitlab
# Relative path of repo
attr_reader :relative_path
- attr_reader :gitlab_projects, :storage, :gl_repository, :relative_path
+ attr_reader :storage, :gl_repository, :relative_path
# This initializer method is only used on the client side (gitlab-ce).
# Gitaly-ruby uses a different initializer.
@@ -82,13 +76,6 @@ module Gitlab
@relative_path = relative_path
@gl_repository = gl_repository
- @gitlab_projects = Gitlab::Git::GitlabProjects.new(
- storage,
- relative_path,
- global_hooks_path: Gitlab.config.gitlab_shell.hooks_path,
- logger: Rails.logger
- )
-
@name = @relative_path.split("/").last
end
@@ -112,23 +99,6 @@ module Gitlab
raise Gitlab::Git::CommandError.new(e.message)
end
- # This method will be removed when Gitaly reaches v1.1.
- def rugged
- circuit_breaker.perform do
- Rugged::Repository.new(path, alternates: alternate_object_directories)
- end
- rescue Rugged::RepositoryError, Rugged::OSError
- raise NoRepository.new('no repository for such path')
- end
-
- def cleanup
- @rugged&.close
- end
-
- def circuit_breaker
- @circuit_breaker ||= Gitlab::Git::Storage::CircuitBreaker.for_storage(storage)
- end
-
def exists?
gitaly_repository_client.exists?
end
@@ -148,10 +118,6 @@ module Gitlab
end
end
- def reload_rugged
- @rugged = nil
- end
-
# Directly find a branch with a simple name (e.g. master)
#
def find_branch(name)
@@ -250,15 +216,6 @@ module Gitlab
end
end
- # Returns an Array of all ref names, except when it's matching pattern
- #
- # regexp - The pattern for ref names we don't want
- def all_ref_names_except(prefixes)
- rugged.references.reject do |ref|
- prefixes.any? { |p| ref.name.start_with?(p) }
- end.map(&:name)
- end
-
def archive_metadata(ref, storage_path, project_path, format = "tar.gz", append_sha:)
ref ||= root_ref
commit = Gitlab::Git::Commit.find(self, ref)
@@ -331,7 +288,7 @@ module Gitlab
(size.to_f / 1024).round(2)
end
- # Use the Rugged Walker API to build an array of commits.
+ # Build an array of commits.
#
# Usage.
# repo.log(
@@ -366,18 +323,9 @@ module Gitlab
end
end
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1233
def new_commits(newrev)
- gitaly_migrate(:new_commits) do |is_enabled|
- if is_enabled
- gitaly_ref_client.list_new_commits(newrev)
- else
- refs = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- rev_list(including: newrev, excluding: :all).split("\n").map(&:strip)
- end
-
- Gitlab::Git::Commit.batch_by_oid(self, refs)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.list_new_commits(newrev)
end
end
@@ -437,9 +385,9 @@ module Gitlab
end
# Returns the SHA of the most recent common ancestor of +from+ and +to+
- def merge_base(from, to)
+ def merge_base(*commits)
wrapped_gitaly_errors do
- gitaly_repository_client.find_merge_base(from, to)
+ gitaly_repository_client.find_merge_base(*commits)
end
end
@@ -472,6 +420,20 @@ module Gitlab
Gitlab::Git::DiffCollection.new(iterator, options)
end
+ def diff_stats(left_id, right_id)
+ if [left_id, right_id].any? { |ref| ref.blank? || Gitlab::Git.blank_ref?(ref) }
+ return empty_diff_stats
+ end
+
+ stats = wrapped_gitaly_errors do
+ gitaly_commit_client.diff_stats(left_id, right_id)
+ end
+
+ Gitlab::Git::DiffStatsCollection.new(stats)
+ rescue CommandError, TypeError
+ empty_diff_stats
+ end
+
# Returns a RefName for a given SHA
def ref_name_for_sha(ref_path, sha)
raise ArgumentError, "sha can't be empty" unless sha.present?
@@ -552,14 +514,8 @@ module Gitlab
end
def update_branch(branch_name, user:, newrev:, oldrev:)
- gitaly_migrate(:operation_user_update_branch) do |is_enabled|
- if is_enabled
- gitaly_operation_client.user_update_branch(branch_name, user, newrev, oldrev)
- else
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
- end
- end
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_update_branch(branch_name, user, newrev, oldrev)
end
end
@@ -606,19 +562,6 @@ module Gitlab
end
end
- def check_revert_content(target_commit, source_sha)
- args = [target_commit.sha, source_sha]
- args << { mainline: 1 } if target_commit.merge_commit?
-
- revert_index = rugged.revert_commit(*args)
- return false if revert_index.conflicts?
-
- tree_id = revert_index.write_tree(rugged)
- return false unless diff_exists?(source_sha, tree_id)
-
- tree_id
- end
-
def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
args = {
user: user,
@@ -634,12 +577,18 @@ module Gitlab
end
end
- def diff_exists?(sha1, sha2)
- rugged.diff(sha1, sha2).size > 0
- end
+ def update_submodule(user:, submodule:, commit_sha:, message:, branch:)
+ args = {
+ user: user,
+ submodule: submodule,
+ commit_sha: commit_sha,
+ branch: branch,
+ message: message
+ }
- def user_to_committer(user)
- Gitlab::Git.committer_hash(email: user.email, name: user.name)
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_update_submodule(args)
+ end
end
# Delete the specified branch from the repository
@@ -681,18 +630,12 @@ module Gitlab
end
end
- AUTOCRLF_VALUES = {
- "true" => true,
- "false" => false,
- "input" => :input
- }.freeze
+ def find_remote_root_ref(remote_name)
+ return unless remote_name.present?
- def autocrlf
- AUTOCRLF_VALUES[rugged.config['core.autocrlf']]
- end
-
- def autocrlf=(value)
- rugged.config['core.autocrlf'] = AUTOCRLF_VALUES.invert[value]
+ wrapped_gitaly_errors do
+ gitaly_remote_client.find_remote_root_ref(remote_name)
+ end
end
# Returns result like "git ls-files" , recursive and full file path
@@ -753,48 +696,6 @@ module Gitlab
end
end
- def with_repo_branch_commit(start_repository, start_branch_name)
- Gitlab::Git.check_namespace!(start_repository)
- start_repository = RemoteRepository.new(start_repository) unless start_repository.is_a?(RemoteRepository)
-
- return yield nil if start_repository.empty?
-
- if start_repository.same_repository?(self)
- yield commit(start_branch_name)
- else
- start_commit_id = start_repository.commit_id(start_branch_name)
-
- return yield nil unless start_commit_id
-
- if branch_commit = commit(start_commit_id)
- yield branch_commit
- else
- with_repo_tmp_commit(
- start_repository, start_branch_name, start_commit_id) do |tmp_commit|
- yield tmp_commit
- end
- end
- end
- end
-
- def with_repo_tmp_commit(start_repository, start_branch_name, sha)
- source_ref = start_branch_name
-
- unless Gitlab::Git.branch_ref?(source_ref)
- source_ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{source_ref}"
- end
-
- tmp_ref = fetch_ref(
- start_repository,
- source_ref: source_ref,
- target_ref: "refs/tmp/#{SecureRandom.hex}"
- )
-
- yield commit(sha)
- ensure
- delete_refs(tmp_ref) if tmp_ref
- end
-
def fetch_source_branch!(source_repository, source_branch, local_ref)
wrapped_gitaly_errors do
gitaly_repository_client.fetch_source_branch(source_repository, source_branch, local_ref)
@@ -824,21 +725,6 @@ module Gitlab
end
end
- # This method, fetch_ref, is used from within
- # Gitlab::Git::OperationService. OperationService will eventually only
- # exist in gitaly-ruby. When we delete OperationService from gitlab-ce
- # we can also remove fetch_ref.
- def fetch_ref(source_repository, source_ref:, target_ref:)
- Gitlab::Git.check_namespace!(source_repository)
- source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository)
-
- args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref})
- message, status = run_git(args, env: source_repository.fetch_env)
- raise Gitlab::Git::CommandError, message if status != 0
-
- target_ref
- end
-
# Refactoring aid; allows us to copy code from app/models/repository.rb
def commit(ref = 'HEAD')
Gitlab::Git::Commit.find(self, ref)
@@ -854,6 +740,26 @@ module Gitlab
end
end
+ # Fetch remote for repository
+ #
+ # remote - remote name
+ # ssh_auth - SSH known_hosts data and a private key to use for public-key authentication
+ # forced - should we use --force flag?
+ # no_tags - should we use --no-tags flag?
+ # prune - should we use --prune flag?
+ def fetch_remote(remote, ssh_auth: nil, forced: false, no_tags: false, prune: true)
+ wrapped_gitaly_errors do
+ gitaly_repository_client.fetch_remote(
+ remote,
+ ssh_auth: ssh_auth,
+ forced: forced,
+ no_tags: no_tags,
+ prune: prune,
+ timeout: GITLAB_PROJECTS_TIMEOUT
+ )
+ end
+ end
+
def blob_at(sha, path)
Gitlab::Git::Blob.find(self, sha, path) unless Gitlab::Git.blank_ref?(sha)
end
@@ -906,24 +812,6 @@ module Gitlab
end
end
- def push_remote_branches(remote_name, branch_names, forced: true)
- success = @gitlab_projects.push_branches(remote_name, GITLAB_PROJECTS_TIMEOUT, forced, branch_names)
-
- success || gitlab_projects_error
- end
-
- def delete_remote_branches(remote_name, branch_names)
- success = @gitlab_projects.delete_remote_branches(remote_name, branch_names)
-
- success || gitlab_projects_error
- end
-
- def delete_remote_branches(remote_name, branch_names)
- success = @gitlab_projects.delete_remote_branches(remote_name, branch_names)
-
- success || gitlab_projects_error
- end
-
def bundle_to_disk(save_path)
wrapped_gitaly_errors do
gitaly_repository_client.create_bundle(save_path)
@@ -998,23 +886,9 @@ module Gitlab
end
def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
- Gitlab::GitalyClient.migrate(method, status: status, &block)
- rescue GRPC::NotFound => e
- raise NoRepository.new(e)
- rescue GRPC::InvalidArgument => e
- raise ArgumentError.new(e)
- rescue GRPC::BadStatus => e
- raise CommandError.new(e)
- end
-
- def wrapped_gitaly_errors(&block)
- yield block
- rescue GRPC::NotFound => e
- raise NoRepository.new(e)
- rescue GRPC::InvalidArgument => e
- raise ArgumentError.new(e)
- rescue GRPC::BadStatus => e
- raise CommandError.new(e)
+ wrapped_gitaly_errors do
+ Gitlab::GitalyClient.migrate(method, status: status, &block)
+ end
end
def clean_stale_repository_files
@@ -1071,9 +945,10 @@ module Gitlab
end
end
- def shell_blame(sha, path)
- output, _status = run_git(%W(blame -p #{sha} -- #{path}))
- output
+ def list_last_commits_for_tree(sha, path, offset: 0, limit: 25)
+ wrapped_gitaly_errors do
+ gitaly_commit_client.list_last_commits_for_tree(sha, path, offset: offset, limit: limit)
+ end
end
def last_commit_for_path(sha, path)
@@ -1082,26 +957,6 @@ module Gitlab
end
end
- def rev_list(including: [], excluding: [], options: [], objects: false, &block)
- args = ['rev-list']
-
- args.push(*rev_list_param(including))
-
- exclude_param = *rev_list_param(excluding)
- if exclude_param.any?
- args.push('--not')
- args.push(*exclude_param)
- end
-
- args.push('--objects') if objects
-
- if options.any?
- args.push(*options)
- end
-
- run_git!(args, lazy_block: block)
- end
-
def checksum
# The exists? RPC is much cheaper, so we perform this request first
raise NoRepository, "Repository does not exists" unless exists?
@@ -1113,50 +968,16 @@ module Gitlab
private
+ def empty_diff_stats
+ Gitlab::Git::DiffStatsCollection.new([])
+ end
+
def uncached_has_local_branches?
wrapped_gitaly_errors do
gitaly_repository_client.has_local_branches?
end
end
- def run_git(args, chdir: path, env: {}, nice: false, lazy_block: nil, &block)
- cmd = [Gitlab.config.git.bin_path, *args]
- cmd.unshift("nice") if nice
-
- object_directories = alternate_object_directories
- if object_directories.any?
- env['GIT_ALTERNATE_OBJECT_DIRECTORIES'] = object_directories.join(File::PATH_SEPARATOR)
- end
-
- circuit_breaker.perform do
- popen(cmd, chdir, env, lazy_block: lazy_block, &block)
- end
- end
-
- def run_git!(args, chdir: path, env: {}, nice: false, lazy_block: nil, &block)
- output, status = run_git(args, chdir: chdir, env: env, nice: nice, lazy_block: lazy_block, &block)
-
- raise GitError, output unless status.zero?
-
- output
- end
-
- def run_git_with_timeout(args, timeout, env: {})
- circuit_breaker.perform do
- popen_with_timeout([Gitlab.config.git.bin_path, *args], timeout, path, env)
- end
- end
-
- def git_env_for_user(user)
- {
- 'GIT_COMMITTER_NAME' => user.name,
- 'GIT_COMMITTER_EMAIL' => user.email,
- 'GL_ID' => Gitlab::GlId.gl_id(user),
- 'GL_PROTOCOL' => Gitlab::Git::Hook::GL_PROTOCOL,
- 'GL_REPOSITORY' => gl_repository
- }
- end
-
def gitaly_merged_branch_names(branch_names, root_sha)
qualified_branch_names = branch_names.map { |b| "refs/heads/#{b}" }
@@ -1199,31 +1020,6 @@ module Gitlab
found_module && found_module['url']
end
- def alternate_object_directories
- relative_object_directories.map { |d| File.join(path, d) }
- end
-
- def relative_object_directories
- Gitlab::Git::HookEnv.all(gl_repository).values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
- end
-
- def sort_branches(branches, sort_by)
- case sort_by
- when 'name'
- branches.sort_by(&:name)
- when 'updated_desc'
- branches.sort do |a, b|
- b.dereferenced_target.committed_date <=> a.dereferenced_target.committed_date
- end
- when 'updated_asc'
- branches.sort do |a, b|
- a.dereferenced_target.committed_date <=> b.dereferenced_target.committed_date
- end
- else
- branches
- end
- end
-
# Returns true if the given ref name exists
#
# Ref names must start with `refs/`.
@@ -1238,14 +1034,6 @@ module Gitlab
def gitaly_delete_refs(*ref_names)
gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any?
end
-
- def gitlab_projects_error
- raise CommandError, @gitlab_projects.output
- end
-
- def rev_list_param(spec)
- spec == :all ? ['--all'] : spec
- end
end
end
end
diff --git a/lib/gitlab/git/repository_mirroring.rb b/lib/gitlab/git/repository_mirroring.rb
index 65eb5cc18cf..7e63a6dc7cb 100644
--- a/lib/gitlab/git/repository_mirroring.rb
+++ b/lib/gitlab/git/repository_mirroring.rb
@@ -1,35 +1,10 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
module RepositoryMirroring
def remote_branches(remote_name)
- gitaly_migrate(:ref_find_all_remote_branches) do |is_enabled|
- if is_enabled
- gitaly_ref_client.remote_branches(remote_name)
- else
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- rugged_remote_branches(remote_name)
- end
- end
- end
- end
-
- private
-
- def rugged_remote_branches(remote_name)
- branches = []
-
- rugged.references.each("refs/remotes/#{remote_name}/*").map do |ref|
- name = ref.name.sub(%r{\Arefs/remotes/#{remote_name}/}, '')
-
- begin
- target_commit = Gitlab::Git::Commit.find(self, ref.target.oid)
- branches << Gitlab::Git::Branch.new(self, name, ref.target, target_commit)
- rescue Rugged::ReferenceError
- # Omit invalid branch
- end
- end
-
- branches
+ gitaly_ref_client.remote_branches(remote_name)
end
end
end
diff --git a/lib/gitlab/git/storage.rb b/lib/gitlab/git/storage.rb
deleted file mode 100644
index 5933312b0b5..00000000000
--- a/lib/gitlab/git/storage.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Gitlab
- module Git
- module Storage
- class Inaccessible < StandardError
- attr_reader :retry_after
-
- def initialize(message = nil, retry_after = nil)
- super(message)
- @retry_after = retry_after
- end
- end
-
- CircuitOpen = Class.new(Inaccessible)
- Misconfiguration = Class.new(Inaccessible)
- Failing = Class.new(Inaccessible)
-
- REDIS_KEY_PREFIX = 'storage_accessible:'.freeze
- REDIS_KNOWN_KEYS = "#{REDIS_KEY_PREFIX}known_keys_set".freeze
-
- def self.redis
- Gitlab::Redis::SharedState
- end
- end
- end
-end
diff --git a/lib/gitlab/git/storage/checker.rb b/lib/gitlab/git/storage/checker.rb
deleted file mode 100644
index 391f0d70583..00000000000
--- a/lib/gitlab/git/storage/checker.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-module Gitlab
- module Git
- module Storage
- class Checker
- include CircuitBreakerSettings
-
- attr_reader :storage_path, :storage, :hostname, :logger
- METRICS_MUTEX = Mutex.new
- STORAGE_TIMING_BUCKETS = [0.1, 0.15, 0.25, 0.33, 0.5, 1, 1.5, 2.5, 5, 10, 15].freeze
-
- def self.check_all(logger = Rails.logger)
- threads = Gitlab.config.repositories.storages.keys.map do |storage_name|
- Thread.new do
- Thread.current[:result] = new(storage_name, logger).check_with_lease
- end
- end
-
- threads.map do |thread|
- thread.join
- thread[:result]
- end
- end
-
- def self.check_histogram
- @check_histogram ||=
- METRICS_MUTEX.synchronize do
- @check_histogram || Gitlab::Metrics.histogram(:circuitbreaker_storage_check_duration_seconds,
- 'Storage check time in seconds',
- {},
- STORAGE_TIMING_BUCKETS
- )
- end
- end
-
- def initialize(storage, logger = Rails.logger)
- @storage = storage
- config = Gitlab.config.repositories.storages[@storage]
- @storage_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { config.legacy_disk_path }
- @logger = logger
-
- @hostname = Gitlab::Environment.hostname
- end
-
- def check_with_lease
- lease_key = "storage_check:#{cache_key}"
- lease = Gitlab::ExclusiveLease.new(lease_key, timeout: storage_timeout)
- result = { storage: storage, success: nil }
-
- if uuid = lease.try_obtain
- result[:success] = check
-
- Gitlab::ExclusiveLease.cancel(lease_key, uuid)
- else
- logger.warn("#{hostname}: #{storage}: Skipping check, previous check still running")
- end
-
- result
- end
-
- def check
- if perform_access_check
- track_storage_accessible
- true
- else
- track_storage_inaccessible
- logger.error("#{hostname}: #{storage}: Not accessible.")
- false
- end
- end
-
- private
-
- def perform_access_check
- start_time = Gitlab::Metrics::System.monotonic_time
-
- Gitlab::Git::Storage::ForkedStorageCheck.storage_available?(storage_path, storage_timeout, access_retries)
- ensure
- execution_time = Gitlab::Metrics::System.monotonic_time - start_time
- self.class.check_histogram.observe({ storage: storage }, execution_time)
- end
-
- def track_storage_inaccessible
- first_failure = current_failure_info.first_failure || Time.now
- last_failure = Time.now
-
- Gitlab::Git::Storage.redis.with do |redis|
- redis.pipelined do
- redis.hset(cache_key, :first_failure, first_failure.to_i)
- redis.hset(cache_key, :last_failure, last_failure.to_i)
- redis.hincrby(cache_key, :failure_count, 1)
- redis.expire(cache_key, failure_reset_time)
- maintain_known_keys(redis)
- end
- end
- end
-
- def track_storage_accessible
- Gitlab::Git::Storage.redis.with do |redis|
- redis.pipelined do
- redis.hset(cache_key, :first_failure, nil)
- redis.hset(cache_key, :last_failure, nil)
- redis.hset(cache_key, :failure_count, 0)
- maintain_known_keys(redis)
- end
- end
- end
-
- def maintain_known_keys(redis)
- expire_time = Time.now.to_i + failure_reset_time
- redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, expire_time, cache_key)
- redis.zremrangebyscore(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, '-inf', Time.now.to_i)
- end
-
- def current_failure_info
- FailureInfo.load(cache_key)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/git/storage/circuit_breaker.rb b/lib/gitlab/git/storage/circuit_breaker.rb
deleted file mode 100644
index 62427ac9cc4..00000000000
--- a/lib/gitlab/git/storage/circuit_breaker.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-module Gitlab
- module Git
- module Storage
- class CircuitBreaker
- include CircuitBreakerSettings
-
- attr_reader :storage,
- :hostname
-
- delegate :last_failure, :failure_count, :no_failures?,
- to: :failure_info
-
- def self.for_storage(storage)
- cached_circuitbreakers = RequestStore.fetch(:circuitbreaker_cache) do
- Hash.new do |hash, storage_name|
- hash[storage_name] = build(storage_name)
- end
- end
-
- cached_circuitbreakers[storage]
- end
-
- def self.build(storage, hostname = Gitlab::Environment.hostname)
- config = Gitlab.config.repositories.storages[storage]
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- if !config.present?
- NullCircuitBreaker.new(storage, hostname, error: Misconfiguration.new("Storage '#{storage}' is not configured"))
- elsif !config.legacy_disk_path.present?
- NullCircuitBreaker.new(storage, hostname, error: Misconfiguration.new("Path for storage '#{storage}' is not configured"))
- else
- new(storage, hostname)
- end
- end
- end
-
- def initialize(storage, hostname)
- @storage = storage
- @hostname = hostname
- end
-
- def perform
- return yield unless enabled?
-
- check_storage_accessible!
-
- yield
- end
-
- def circuit_broken?
- return false if no_failures?
-
- failure_count > failure_count_threshold
- end
-
- private
-
- # The circuitbreaker can be enabled for the entire fleet using a Feature
- # flag.
- #
- # Enabling it for a single host can be done setting the
- # `GIT_STORAGE_CIRCUIT_BREAKER` environment variable.
- def enabled?
- ENV['GIT_STORAGE_CIRCUIT_BREAKER'].present? || Feature.enabled?('git_storage_circuit_breaker')
- end
-
- def failure_info
- @failure_info ||= FailureInfo.load(cache_key)
- end
-
- def check_storage_accessible!
- if circuit_broken?
- raise Gitlab::Git::Storage::CircuitOpen.new("Circuit for #{storage} is broken", failure_reset_time)
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/git/storage/circuit_breaker_settings.rb b/lib/gitlab/git/storage/circuit_breaker_settings.rb
deleted file mode 100644
index c9e225f187d..00000000000
--- a/lib/gitlab/git/storage/circuit_breaker_settings.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-module Gitlab
- module Git
- module Storage
- module CircuitBreakerSettings
- def failure_count_threshold
- application_settings.circuitbreaker_failure_count_threshold
- end
-
- def failure_reset_time
- application_settings.circuitbreaker_failure_reset_time
- end
-
- def storage_timeout
- application_settings.circuitbreaker_storage_timeout
- end
-
- def access_retries
- application_settings.circuitbreaker_access_retries
- end
-
- def check_interval
- application_settings.circuitbreaker_check_interval
- end
-
- def cache_key
- @cache_key ||= "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage}:#{hostname}"
- end
-
- private
-
- def application_settings
- Gitlab::CurrentSettings.current_application_settings
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/git/storage/failure_info.rb b/lib/gitlab/git/storage/failure_info.rb
deleted file mode 100644
index 387279c110d..00000000000
--- a/lib/gitlab/git/storage/failure_info.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module Gitlab
- module Git
- module Storage
- class FailureInfo
- attr_accessor :first_failure, :last_failure, :failure_count
-
- def self.reset_all!
- Gitlab::Git::Storage.redis.with do |redis|
- all_storage_keys = redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1)
- redis.del(*all_storage_keys) unless all_storage_keys.empty?
- end
-
- RequestStore.delete(:circuitbreaker_cache)
- end
-
- def self.load(cache_key)
- first_failure, last_failure, failure_count = Gitlab::Git::Storage.redis.with do |redis|
- redis.hmget(cache_key, :first_failure, :last_failure, :failure_count)
- end
-
- last_failure = Time.at(last_failure.to_i) if last_failure.present?
- first_failure = Time.at(first_failure.to_i) if first_failure.present?
-
- new(first_failure, last_failure, failure_count.to_i)
- end
-
- def initialize(first_failure, last_failure, failure_count)
- @first_failure = first_failure
- @last_failure = last_failure
- @failure_count = failure_count
- end
-
- def no_failures?
- first_failure.blank? && last_failure.blank? && failure_count == 0
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/git/storage/forked_storage_check.rb b/lib/gitlab/git/storage/forked_storage_check.rb
deleted file mode 100644
index 0a4e557b59b..00000000000
--- a/lib/gitlab/git/storage/forked_storage_check.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-module Gitlab
- module Git
- module Storage
- module ForkedStorageCheck
- extend self
-
- def storage_available?(path, timeout_seconds = 5, retries = 1)
- partial_timeout = timeout_seconds / retries
- status = timeout_check(path, partial_timeout)
-
- # If the status check did not succeed the first time, we retry a few
- # more times to avoid one-off failures
- current_attempts = 1
- while current_attempts < retries && !status.success?
- status = timeout_check(path, partial_timeout)
- current_attempts += 1
- end
-
- status.success?
- end
-
- def timeout_check(path, timeout_seconds)
- filesystem_check_pid = check_filesystem_in_process(path)
-
- deadline = timeout_seconds.seconds.from_now.utc
- wait_time = 0.01
- status = nil
-
- while status.nil?
-
- if deadline > Time.now.utc
- sleep(wait_time)
- _pid, status = Process.wait2(filesystem_check_pid, Process::WNOHANG)
- else
- Process.kill('KILL', filesystem_check_pid)
- # Blocking wait, so we are sure the process is gone before continuing
- _pid, status = Process.wait2(filesystem_check_pid)
- end
- end
-
- status
- end
-
- # This will spawn a new 2 processes to do the check:
- # The outer child (waiter) will spawn another child process (stater).
- #
- # The stater is the process is performing the actual filesystem check
- # the check might hang if the filesystem is acting up.
- # In this case we will send a `KILL` to the waiter, which will still
- # be responsive while the stater is hanging.
- def check_filesystem_in_process(path)
- spawn('ruby', '-e', ruby_check, path, [:out, :err] => '/dev/null')
- end
-
- def ruby_check
- <<~RUBY_FILESYSTEM_CHECK
- inner_pid = fork { File.stat(ARGV.first) }
- Process.waitpid(inner_pid)
- exit $?.exitstatus
- RUBY_FILESYSTEM_CHECK
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/git/storage/health.rb b/lib/gitlab/git/storage/health.rb
deleted file mode 100644
index 90bbe85fd37..00000000000
--- a/lib/gitlab/git/storage/health.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-module Gitlab
- module Git
- module Storage
- class Health
- attr_reader :storage_name, :info
-
- def self.prefix_for_storage(storage_name)
- "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage_name}:"
- end
-
- def self.for_all_storages
- storage_names = Gitlab.config.repositories.storages.keys
- results_per_storage = nil
-
- Gitlab::Git::Storage.redis.with do |redis|
- keys_per_storage = all_keys_for_storages(storage_names, redis)
- results_per_storage = load_for_keys(keys_per_storage, redis)
- end
-
- results_per_storage.map do |name, info|
- info.each { |i| i[:failure_count] = i[:failure_count].value.to_i }
- new(name, info)
- end
- end
-
- private_class_method def self.all_keys_for_storages(storage_names, redis)
- keys_per_storage = {}
- all_keys = redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1)
-
- storage_names.each do |storage_name|
- prefix = prefix_for_storage(storage_name)
-
- keys_per_storage[storage_name] = all_keys.select { |key| key.starts_with?(prefix) }
- end
-
- keys_per_storage
- end
-
- private_class_method def self.load_for_keys(keys_per_storage, redis)
- info_for_keys = {}
-
- redis.pipelined do
- keys_per_storage.each do |storage_name, keys_future|
- info_for_storage = keys_future.map do |key|
- { name: key, failure_count: redis.hget(key, :failure_count) }
- end
-
- info_for_keys[storage_name] = info_for_storage
- end
- end
-
- info_for_keys
- end
-
- def self.for_failing_storages
- for_all_storages.select(&:failing?)
- end
-
- def initialize(storage_name, info)
- @storage_name = storage_name
- @info = info
- end
-
- def failing_info
- @failing_info ||= info.select { |info_for_host| info_for_host[:failure_count] > 0 }
- end
-
- def failing?
- failing_info.any?
- end
-
- def failing_on_hosts
- @failing_on_hosts ||= failing_info.map do |info_for_host|
- info_for_host[:name].split(':').last
- end
- end
-
- def failing_circuit_breakers
- @failing_circuit_breakers ||= failing_on_hosts.map do |hostname|
- CircuitBreaker.build(storage_name, hostname)
- end
- end
-
- def total_failures
- @total_failures ||= failing_info.sum { |info_for_host| info_for_host[:failure_count] }
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/git/storage/null_circuit_breaker.rb b/lib/gitlab/git/storage/null_circuit_breaker.rb
deleted file mode 100644
index 261c936c689..00000000000
--- a/lib/gitlab/git/storage/null_circuit_breaker.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-module Gitlab
- module Git
- module Storage
- class NullCircuitBreaker
- include CircuitBreakerSettings
-
- # These will have actual values
- attr_reader :storage,
- :hostname
-
- # These will always have nil values
- attr_reader :storage_path
-
- delegate :last_failure, :failure_count, :no_failures?,
- to: :failure_info
-
- def initialize(storage, hostname, error: nil)
- @storage = storage
- @hostname = hostname
- @error = error
- end
-
- def perform
- @error ? raise(@error) : yield
- end
-
- def circuit_broken?
- !!@error
- end
-
- def backing_off?
- false
- end
-
- def failure_info
- @failure_info ||=
- if circuit_broken?
- Gitlab::Git::Storage::FailureInfo.new(Time.now,
- Time.now,
- failure_count_threshold)
- else
- Gitlab::Git::Storage::FailureInfo.new(nil,
- nil,
- 0)
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/git/tag.rb b/lib/gitlab/git/tag.rb
index bbf2ecdb1fa..ade708d0541 100644
--- a/lib/gitlab/git/tag.rb
+++ b/lib/gitlab/git/tag.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class Tag < Ref
diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb
index cb851b76a23..51542bcaaa2 100644
--- a/lib/gitlab/git/tree.rb
+++ b/lib/gitlab/git/tree.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class Tree
include Gitlab::EncodingHelper
+ extend Gitlab::Git::WrapsGitalyErrors
attr_accessor :id, :root_id, :name, :path, :flat_path, :type,
:mode, :commit_id, :submodule_url
@@ -15,7 +18,7 @@ module Gitlab
def where(repository, sha, path = nil, recursive = false)
path = nil if path == '' || path == '/'
- repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
repository.gitaly_commit_client.tree_entries(repository, sha, path, recursive)
end
end
@@ -50,51 +53,6 @@ module Gitlab
entry[:oid]
end
end
-
- def tree_entries_from_rugged(repository, sha, path, recursive)
- current_path_entries = get_tree_entries_from_rugged(repository, sha, path)
- ordered_entries = []
-
- current_path_entries.each do |entry|
- ordered_entries << entry
-
- if recursive && entry.dir?
- ordered_entries.concat(tree_entries_from_rugged(repository, sha, entry.path, true))
- end
- end
-
- ordered_entries
- end
-
- def get_tree_entries_from_rugged(repository, sha, path)
- commit = repository.lookup(sha)
- root_tree = commit.tree
-
- tree = if path
- id = find_id_by_path(repository, root_tree.oid, path)
- if id
- repository.lookup(id)
- else
- []
- end
- else
- root_tree
- end
-
- tree.map do |entry|
- new(
- id: entry[:oid],
- root_id: root_tree.oid,
- name: entry[:name],
- type: entry[:type],
- mode: entry[:filemode].to_s(8),
- path: path ? File.join(path, entry[:name]) : entry[:name],
- commit_id: sha
- )
- end
- rescue Rugged::ReferenceError
- []
- end
end
def initialize(options)
diff --git a/lib/gitlab/git/user.rb b/lib/gitlab/git/user.rb
index e573cd0e143..2c798844798 100644
--- a/lib/gitlab/git/user.rb
+++ b/lib/gitlab/git/user.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class User
attr_reader :username, :name, :email, :gl_id
def self.from_gitlab(gitlab_user)
- new(gitlab_user.username, gitlab_user.name, gitlab_user.email, Gitlab::GlId.gl_id(gitlab_user))
+ new(gitlab_user.username, gitlab_user.name, gitlab_user.commit_email, Gitlab::GlId.gl_id(gitlab_user))
end
def self.from_gitaly(gitaly_user)
diff --git a/lib/gitlab/git/util.rb b/lib/gitlab/git/util.rb
index 4708f22dcb3..03c2c1367b0 100644
--- a/lib/gitlab/git/util.rb
+++ b/lib/gitlab/git/util.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitaly note: JV: no RPC's here.
module Gitlab
diff --git a/lib/gitlab/git/version.rb b/lib/gitlab/git/version.rb
index 1e14e8b652a..64c89656167 100644
--- a/lib/gitlab/git/version.rb
+++ b/lib/gitlab/git/version.rb
@@ -1,8 +1,8 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
module Version
- extend Gitlab::Git::Popen
-
def self.git_version
Gitlab::VersionInfo.parse(Gitaly::Server.all.first.git_binary_version)
end
diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb
index 9d992be66eb..c43331bed60 100644
--- a/lib/gitlab/git/wiki.rb
+++ b/lib/gitlab/git/wiki.rb
@@ -1,15 +1,57 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class Wiki
+ include Gitlab::Git::WrapsGitalyErrors
+
DuplicatePageError = Class.new(StandardError)
OperationError = Class.new(StandardError)
+ DEFAULT_PAGINATION = Kaminari.config.default_per_page
+
CommitDetails = Struct.new(:user_id, :username, :name, :email, :message) do
def to_h
{ user_id: user_id, username: username, name: name, email: email, message: message }
end
end
- PageBlob = Struct.new(:name)
+
+ # GollumSlug inlines just enough knowledge from Gollum::Page to generate a
+ # slug, which is used when previewing pages that haven't been persisted
+ class GollumSlug
+ class << self
+ def cname(name, char_white_sub = '-', char_other_sub = '-')
+ if name.respond_to?(:gsub)
+ name.gsub(/\s/, char_white_sub).gsub(/[<>+]/, char_other_sub)
+ else
+ ''
+ end
+ end
+
+ def format_to_ext(format)
+ format == :markdown ? "md" : format.to_s
+ end
+
+ def canonicalize_filename(filename)
+ ::File.basename(filename, ::File.extname(filename)).tr('-', ' ')
+ end
+
+ def generate(title, format)
+ ext = format_to_ext(format.to_sym)
+ name = cname(title) + '.' + ext
+ canonical_name = canonicalize_filename(name)
+
+ path =
+ if name.include?('/')
+ name.sub(%r{/[^/]+$}, '/')
+ else
+ ''
+ end
+
+ path + cname(canonical_name, '-', '-')
+ end
+ end
+ end
attr_reader :repository
@@ -27,37 +69,37 @@ module Gitlab
end
def write_page(name, format, content, commit_details)
- @repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_write_page(name, format, content, commit_details)
end
end
def delete_page(page_path, commit_details)
- @repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_delete_page(page_path, commit_details)
end
end
def update_page(page_path, title, format, content, commit_details)
- @repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_update_page(page_path, title, format, content, commit_details)
end
end
def pages(limit: 0)
- @repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_get_all_pages(limit: limit)
end
end
def page(title:, version: nil, dir: nil)
- @repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_find_page(title: title, version: version, dir: dir)
end
end
def file(name, version)
- @repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_find_file(name, version)
end
end
@@ -67,14 +109,14 @@ module Gitlab
# :per_page - The number of items per page.
# :limit - Total number of items to return.
def page_versions(page_path, options = {})
- versions = @repository.wrapped_gitaly_errors do
+ versions = wrapped_gitaly_errors do
gitaly_wiki_client.page_versions(page_path, options)
end
# Gitaly uses gollum-lib to get the versions. Gollum defaults to 20
# per page, but also fetches 20 if `limit` or `per_page` < 20.
# Slicing returns an array with the expected number of items.
- slice_bound = options[:limit] || options[:per_page] || Gollum::Page.per_page
+ slice_bound = options[:limit] || options[:per_page] || DEFAULT_PAGINATION
versions[0..slice_bound]
end
@@ -83,51 +125,19 @@ module Gitlab
end
def preview_slug(title, format)
- # Adapted from gollum gem (Gollum::Wiki#preview_page) to avoid
- # using Rugged through a Gollum::Wiki instance
- page_class = Gollum::Page
- page = page_class.new(nil)
- ext = page_class.format_to_ext(format.to_sym)
- name = page_class.cname(title) + '.' + ext
- blob = PageBlob.new(name)
- page.populate(blob)
- page.url_path
+ GollumSlug.generate(title, format)
end
def page_formatted_data(title:, dir: nil, version: nil)
version = version&.id
- @repository.wrapped_gitaly_errors do
+ wrapped_gitaly_errors do
gitaly_wiki_client.get_formatted_data(title: title, dir: dir, version: version)
end
end
private
- def new_page(gollum_page)
- Gitlab::Git::WikiPage.new(gollum_page, new_version(gollum_page, gollum_page.version.id))
- end
-
- def new_version(gollum_page, commit_id)
- Gitlab::Git::WikiPageVersion.new(version(commit_id), gollum_page&.format)
- end
-
- def version(commit_id)
- commit_find_proc = -> { Gitlab::Git::Commit.find(@repository, commit_id) }
-
- if RequestStore.active?
- RequestStore.fetch([:wiki_version_commit, commit_id]) { commit_find_proc.call }
- else
- commit_find_proc.call
- end
- end
-
- def assert_type!(object, klass)
- unless object.is_a?(klass)
- raise ArgumentError, "expected a #{klass}, got #{object.inspect}"
- end
- end
-
def gitaly_wiki_client
@gitaly_wiki_client ||= Gitlab::GitalyClient::WikiService.new(@repository)
end
@@ -163,20 +173,6 @@ module Gitlab
Gitlab::Git::WikiPage.new(wiki_page, version)
end
end
-
- def committer_with_hooks(commit_details)
- Gitlab::Git::CommitterWithHooks.new(self, commit_details.to_h)
- end
-
- def with_committer_with_hooks(commit_details, &block)
- committer = committer_with_hooks(commit_details)
-
- yield committer
-
- committer.commit
-
- nil
- end
end
end
end
diff --git a/lib/gitlab/git/wiki_file.rb b/lib/gitlab/git/wiki_file.rb
index 84335aca4bc..c05a5adc00c 100644
--- a/lib/gitlab/git/wiki_file.rb
+++ b/lib/gitlab/git/wiki_file.rb
@@ -1,19 +1,16 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class WikiFile
attr_reader :mime_type, :raw_data, :name, :path
- # This class is meant to be serializable so that it can be constructed
- # by Gitaly and sent over the network to GitLab.
- #
- # Because Gollum::File is not serializable we must get all the data from
- # 'gollum_file' during initialization, and NOT store it in an instance
- # variable.
- def initialize(gollum_file)
- @mime_type = gollum_file.mime_type
- @raw_data = gollum_file.raw_data
- @name = gollum_file.name
- @path = gollum_file.path
+ # This class wraps Gitlab::GitalyClient::WikiFile
+ def initialize(gitaly_file)
+ @mime_type = gitaly_file.mime_type
+ @raw_data = gitaly_file.raw_data
+ @name = gitaly_file.name
+ @path = gitaly_file.path
end
end
end
diff --git a/lib/gitlab/git/wiki_page.rb b/lib/gitlab/git/wiki_page.rb
index 669ae11a423..f6cac398548 100644
--- a/lib/gitlab/git/wiki_page.rb
+++ b/lib/gitlab/git/wiki_page.rb
@@ -1,27 +1,19 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class WikiPage
attr_reader :url_path, :title, :format, :path, :version, :raw_data, :name, :text_data, :historical, :formatted_data
- # This class is meant to be serializable so that it can be constructed
- # by Gitaly and sent over the network to GitLab.
- #
- # Because Gollum::Page is not serializable we must get all the data from
- # 'gollum_page' during initialization, and NOT store it in an instance
- # variable.
- #
- # Note that 'version' is a WikiPageVersion instance which it itself
- # serializable. That means it's OK to store 'version' in an instance
- # variable.
- def initialize(gollum_page, version)
- @url_path = gollum_page.url_path
- @title = gollum_page.title
- @format = gollum_page.format
- @path = gollum_page.path
- @raw_data = gollum_page.raw_data
- @name = gollum_page.name
- @historical = gollum_page.historical?
- @formatted_data = gollum_page.formatted_data if gollum_page.is_a?(Gollum::Page)
+ # This class abstracts away Gitlab::GitalyClient::WikiPage
+ def initialize(gitaly_page, version)
+ @url_path = gitaly_page.url_path
+ @title = gitaly_page.title
+ @format = gitaly_page.format
+ @path = gitaly_page.path
+ @raw_data = gitaly_page.raw_data
+ @name = gitaly_page.name
+ @historical = gitaly_page.historical?
@version = version
end
diff --git a/lib/gitlab/git/wiki_page_version.rb b/lib/gitlab/git/wiki_page_version.rb
index 55f1afedcab..475a9d4d1b9 100644
--- a/lib/gitlab/git/wiki_page_version.rb
+++ b/lib/gitlab/git/wiki_page_version.rb
@@ -1,13 +1,10 @@
+# frozen_string_literal: true
+
module Gitlab
module Git
class WikiPageVersion
attr_reader :commit, :format
- # This class is meant to be serializable so that it can be constructed
- # by Gitaly and sent over the network to GitLab.
- #
- # Both 'commit' (a Gitlab::Git::Commit) and 'format' (a string) are
- # serializable.
def initialize(commit, format)
@commit = commit
@format = format
diff --git a/lib/gitlab/git/wraps_gitaly_errors.rb b/lib/gitlab/git/wraps_gitaly_errors.rb
new file mode 100644
index 00000000000..9963bcfbf1c
--- /dev/null
+++ b/lib/gitlab/git/wraps_gitaly_errors.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ module WrapsGitalyErrors
+ def wrapped_gitaly_errors(&block)
+ yield block
+ rescue GRPC::NotFound => e
+ raise Gitlab::Git::Repository::NoRepository.new(e)
+ rescue GRPC::InvalidArgument => e
+ raise ArgumentError.new(e)
+ rescue GRPC::BadStatus => e
+ raise Gitlab::Git::CommandError.new(e)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 35808149b90..802fa65dd63 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Check a user's access to perform a git action. All public methods in this
# class return an instance of `GitlabAccessStatus`
module Gitlab
@@ -7,6 +9,7 @@ module Gitlab
UnauthorizedError = Class.new(StandardError)
NotFoundError = Class.new(StandardError)
ProjectCreationError = Class.new(StandardError)
+ TimeoutError = Class.new(StandardError)
ProjectMovedError = Class.new(NotFoundError)
ERROR_MESSAGES = {
@@ -24,11 +27,18 @@ module Gitlab
cannot_push_to_read_only: "You can't push code to a read-only GitLab instance."
}.freeze
- DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }.freeze
- PUSH_COMMANDS = %w{ git-receive-pack }.freeze
+ INTERNAL_TIMEOUT = 50.seconds.freeze
+ LOG_HEADER = <<~MESSAGE
+ Push operation timed out
+
+ Timing information for debugging purposes:
+ MESSAGE
+
+ DOWNLOAD_COMMANDS = %w{git-upload-pack git-upload-archive}.freeze
+ PUSH_COMMANDS = %w{git-receive-pack}.freeze
ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS
- attr_reader :actor, :project, :protocol, :authentication_abilities, :namespace_path, :project_path, :redirected_path, :auth_result_type, :changes
+ attr_reader :actor, :project, :protocol, :authentication_abilities, :namespace_path, :project_path, :redirected_path, :auth_result_type, :changes, :logger
def initialize(actor, project, protocol, authentication_abilities:, namespace_path: nil, project_path: nil, redirected_path: nil, auth_result_type: nil)
@actor = actor
@@ -42,6 +52,7 @@ module Gitlab
end
def check(cmd, changes)
+ @logger = Checks::TimedLogger.new(timeout: INTERNAL_TIMEOUT, header: LOG_HEADER)
@changes = changes
check_protocol!
@@ -50,6 +61,10 @@ module Gitlab
check_authentication_abilities!(cmd)
check_command_disabled!(cmd)
check_command_existence!(cmd)
+
+ custom_action = check_custom_action(cmd)
+ return custom_action if custom_action
+
check_db_accessibility!(cmd)
ensure_project_on_push!(cmd, changes)
@@ -65,7 +80,7 @@ module Gitlab
check_push_access!
end
- true
+ ::Gitlab::GitAccessResult::Success.new
end
def guest_can_download_code?
@@ -92,6 +107,10 @@ module Gitlab
private
+ def check_custom_action(cmd)
+ nil
+ end
+
def check_valid_actor!
return unless actor.is_a?(Key)
@@ -259,14 +278,19 @@ module Gitlab
end
def check_single_change_access(change, skip_lfs_integrity_check: false)
- Checks::ChangeAccess.new(
+ change_access = Checks::ChangeAccess.new(
change,
user_access: user_access,
project: project,
skip_authorization: deploy_key?,
skip_lfs_integrity_check: skip_lfs_integrity_check,
- protocol: protocol
- ).exec
+ protocol: protocol,
+ logger: logger
+ )
+
+ change_access.exec
+ rescue Checks::TimedLogger::TimeoutError
+ raise TimeoutError, logger.full_message
end
def deploy_key
diff --git a/lib/gitlab/git_access_result/custom_action.rb b/lib/gitlab/git_access_result/custom_action.rb
new file mode 100644
index 00000000000..a05a4baed82
--- /dev/null
+++ b/lib/gitlab/git_access_result/custom_action.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GitAccessResult
+ class CustomAction
+ attr_reader :payload, :message
+
+ # Example of payload:
+ #
+ # {
+ # 'action' => 'geo_proxy_to_primary',
+ # 'data' => {
+ # 'api_endpoints' => %w{geo/proxy_git_push_ssh/info_refs geo/proxy_git_push_ssh/push},
+ # 'gl_username' => user.username,
+ # 'primary_repo' => geo_primary_http_url_to_repo(project_or_wiki)
+ # }
+ # }
+ #
+ def initialize(payload, message)
+ @payload = payload
+ @message = message
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git_access_result/success.rb b/lib/gitlab/git_access_result/success.rb
new file mode 100644
index 00000000000..7bb9f24cb0e
--- /dev/null
+++ b/lib/gitlab/git_access_result/success.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GitAccessResult
+ class Success
+ end
+ end
+end
diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb
index a5b3902ebf4..3f24001e4ee 100644
--- a/lib/gitlab/git_access_wiki.rb
+++ b/lib/gitlab/git_access_wiki.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class GitAccessWiki < GitAccess
ERROR_MESSAGES = {
diff --git a/lib/gitlab/git_logger.rb b/lib/gitlab/git_logger.rb
index 9e02ccc0f44..dac4ddd320f 100644
--- a/lib/gitlab/git_logger.rb
+++ b/lib/gitlab/git_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class GitLogger < Gitlab::Logger
def self.file_name_noext
diff --git a/lib/gitlab/git_post_receive.rb b/lib/gitlab/git_post_receive.rb
index e731e654f3c..cf2329e489d 100644
--- a/lib/gitlab/git_post_receive.rb
+++ b/lib/gitlab/git_post_receive.rb
@@ -11,8 +11,8 @@ module Gitlab
@changes = deserialize_changes(changes)
end
- def identify(revision)
- super(identifier, project, revision)
+ def identify
+ super(identifier)
end
def changes_refs
diff --git a/lib/gitlab/git_ref_validator.rb b/lib/gitlab/git_ref_validator.rb
index 40636fb204e..a90b69ff42b 100644
--- a/lib/gitlab/git_ref_validator.rb
+++ b/lib/gitlab/git_ref_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitaly note: JV: does not need to be migrated, works without a repo.
module Gitlab
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index c27972a84a4..8b455dc7696 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'base64'
require 'gitaly'
@@ -23,7 +25,7 @@ module Gitlab
stacks = most_invoked_stack.join('\n') if most_invoked_stack
msg = "GitalyClient##{call_site} called #{invocation_count} times from single request. Potential n+1?"
- msg << "\nThe following call site called into Gitaly #{max_call_stack} times:\n#{stacks}\n" if stacks
+ msg = "#{msg}\nThe following call site called into Gitaly #{max_call_stack} times:\n#{stacks}\n" if stacks
super(msg)
end
@@ -137,7 +139,7 @@ module Gitlab
ensure
duration = Gitlab::Metrics::System.monotonic_time - start
- # Keep track, seperately, for the performance bar
+ # Keep track, separately, for the performance bar
self.query_time += duration
gitaly_controller_action_duration_seconds.observe(
current_transaction_labels.merge(gitaly_service: service.to_s, rpc: rpc.to_s),
@@ -174,10 +176,29 @@ module Gitlab
end
private_class_method :current_transaction_labels
+ # For some time related tasks we can't rely on `Time.now` since it will be
+ # affected by Timecop in some tests, and the clock of some gitaly-related
+ # components (grpc's c-core and gitaly server) use system time instead of
+ # timecop's time, so tests will fail.
+ # `Time.at(Process.clock_gettime(Process::CLOCK_REALTIME))` will circumvent
+ # timecop.
+ def self.real_time
+ Time.at(Process.clock_gettime(Process::CLOCK_REALTIME))
+ end
+ private_class_method :real_time
+
+ def self.authorization_token(storage)
+ token = token(storage).to_s
+ issued_at = real_time.to_i.to_s
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, token, issued_at)
+
+ "v2.#{hmac}.#{issued_at}"
+ end
+ private_class_method :authorization_token
+
def self.request_kwargs(storage, timeout, remote_storage: nil)
- encoded_token = Base64.strict_encode64(token(storage).to_s)
metadata = {
- 'authorization' => "Bearer #{encoded_token}",
+ 'authorization' => "Bearer #{authorization_token(storage)}",
'client_name' => CLIENT_NAME
}
@@ -195,12 +216,7 @@ module Gitlab
return result unless timeout > 0
- # Do not use `Time.now` for deadline calculation, since it
- # will be affected by Timecop in some tests, but grpc's c-core
- # uses system time instead of timecop's time, so tests will fail
- # `Time.at(Process.clock_gettime(Process::CLOCK_REALTIME))` will
- # circumvent timecop
- deadline = Time.at(Process.clock_gettime(Process::CLOCK_REALTIME)) + timeout
+ deadline = real_time + timeout
result[:deadline] = deadline
result
@@ -302,7 +318,7 @@ module Gitlab
# Ensures that Gitaly is not being abuse through n+1 misuse etc
def self.enforce_gitaly_request_limits(call_site)
# Only count limits in request-response environments (not sidekiq for example)
- return unless RequestStore.active?
+ return unless Gitlab::SafeRequestStore.active?
# This is this actual number of times this call was made. Used for information purposes only
actual_call_count = increment_call_count("gitaly_#{call_site}_actual")
@@ -326,7 +342,7 @@ module Gitlab
end
def self.allow_n_plus_1_calls
- return yield unless RequestStore.active?
+ return yield unless Gitlab::SafeRequestStore.active?
begin
increment_call_count(:gitaly_call_count_exception_block_depth)
@@ -337,25 +353,25 @@ module Gitlab
end
def self.get_call_count(key)
- RequestStore.store[key] || 0
+ Gitlab::SafeRequestStore[key] || 0
end
private_class_method :get_call_count
def self.increment_call_count(key)
- RequestStore.store[key] ||= 0
- RequestStore.store[key] += 1
+ Gitlab::SafeRequestStore[key] ||= 0
+ Gitlab::SafeRequestStore[key] += 1
end
private_class_method :increment_call_count
def self.decrement_call_count(key)
- RequestStore.store[key] -= 1
+ Gitlab::SafeRequestStore[key] -= 1
end
private_class_method :decrement_call_count
# Returns an estimate of the number of Gitaly calls made for this
# request
def self.get_request_count
- return 0 unless RequestStore.active?
+ return 0 unless Gitlab::SafeRequestStore.active?
gitaly_migrate_count = get_call_count("gitaly_migrate_actual")
gitaly_call_count = get_call_count("gitaly_call_actual")
@@ -372,28 +388,28 @@ module Gitlab
end
def self.reset_counts
- return unless RequestStore.active?
+ return unless Gitlab::SafeRequestStore.active?
%w[migrate call].each do |call_site|
- RequestStore.store["gitaly_#{call_site}_actual"] = 0
- RequestStore.store["gitaly_#{call_site}_permitted"] = 0
+ Gitlab::SafeRequestStore["gitaly_#{call_site}_actual"] = 0
+ Gitlab::SafeRequestStore["gitaly_#{call_site}_permitted"] = 0
end
end
def self.add_call_details(details)
id = details.delete(:id)
- return unless id && RequestStore.active? && RequestStore.store[:peek_enabled]
+ return unless id && Gitlab::SafeRequestStore[:peek_enabled]
- RequestStore.store['gitaly_call_details'] ||= {}
- RequestStore.store['gitaly_call_details'][id] ||= {}
- RequestStore.store['gitaly_call_details'][id].merge!(details)
+ Gitlab::SafeRequestStore['gitaly_call_details'] ||= {}
+ Gitlab::SafeRequestStore['gitaly_call_details'][id] ||= {}
+ Gitlab::SafeRequestStore['gitaly_call_details'][id].merge!(details)
end
def self.list_call_details
- return {} unless RequestStore.active? && RequestStore.store[:peek_enabled]
+ return {} unless Gitlab::SafeRequestStore[:peek_enabled]
- RequestStore.store['gitaly_call_details'] || {}
+ Gitlab::SafeRequestStore['gitaly_call_details'] || {}
end
def self.expected_server_version
@@ -431,22 +447,22 @@ module Gitlab
# Count a stack. Used for n+1 detection
def self.count_stack
- return unless RequestStore.active?
+ return unless Gitlab::SafeRequestStore.active?
stack_string = Gitlab::Profiler.clean_backtrace(caller).drop(1).join("\n")
- RequestStore.store[:stack_counter] ||= Hash.new
+ Gitlab::SafeRequestStore[:stack_counter] ||= Hash.new
- count = RequestStore.store[:stack_counter][stack_string] || 0
- RequestStore.store[:stack_counter][stack_string] = count + 1
+ count = Gitlab::SafeRequestStore[:stack_counter][stack_string] || 0
+ Gitlab::SafeRequestStore[:stack_counter][stack_string] = count + 1
end
private_class_method :count_stack
# Returns a count for the stack which called Gitaly the most times. Used for n+1 detection
def self.max_call_count
- return 0 unless RequestStore.active?
+ return 0 unless Gitlab::SafeRequestStore.active?
- stack_counter = RequestStore.store[:stack_counter]
+ stack_counter = Gitlab::SafeRequestStore[:stack_counter]
return 0 unless stack_counter
stack_counter.values.max
@@ -455,9 +471,9 @@ module Gitlab
# Returns the stacks that calls Gitaly the most times. Used for n+1 detection
def self.max_stacks
- return nil unless RequestStore.active?
+ return nil unless Gitlab::SafeRequestStore.active?
- stack_counter = RequestStore.store[:stack_counter]
+ stack_counter = Gitlab::SafeRequestStore[:stack_counter]
return nil unless stack_counter
max = max_call_count
diff --git a/lib/gitlab/gitaly_client/attributes_bag.rb b/lib/gitlab/gitaly_client/attributes_bag.rb
index 198a1de91c7..3f1a0ef4888 100644
--- a/lib/gitlab/gitaly_client/attributes_bag.rb
+++ b/lib/gitlab/gitaly_client/attributes_bag.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
# This module expects an `ATTRS` const to be defined on the subclass
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index 1840bf45154..39547328210 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class BlobService
@@ -15,7 +17,7 @@ module Gitlab
)
response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_blob, request, timeout: GitalyClient.fast_timeout)
- data = ''
+ data = []
blob = nil
response.each do |msg|
if blob.nil?
@@ -27,6 +29,8 @@ module Gitlab
return nil if blob.oid.blank?
+ data = data.join
+
Gitlab::Git::Blob.new(
id: blob.oid,
size: blob.size,
@@ -72,7 +76,7 @@ module Gitlab
GitalyClient::BlobsStitcher.new(response)
end
- def get_new_lfs_pointers(revision, limit, not_in)
+ def get_new_lfs_pointers(revision, limit, not_in, dynamic_timeout = nil)
request = Gitaly::GetNewLFSPointersRequest.new(
repository: @gitaly_repo,
revision: encode_binary(revision),
@@ -85,7 +89,20 @@ module Gitlab
request.not_in_refs += not_in
end
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_new_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
+ timeout =
+ if dynamic_timeout
+ [dynamic_timeout, GitalyClient.medium_timeout].min
+ else
+ GitalyClient.medium_timeout
+ end
+
+ response = GitalyClient.call(
+ @gitaly_repo.storage_name,
+ :blob_service,
+ :get_new_lfs_pointers,
+ request,
+ timeout: timeout
+ )
map_lfs_pointers(response)
end
diff --git a/lib/gitlab/gitaly_client/blobs_stitcher.rb b/lib/gitlab/gitaly_client/blobs_stitcher.rb
index 5ca592ff812..01bab854082 100644
--- a/lib/gitlab/gitaly_client/blobs_stitcher.rb
+++ b/lib/gitlab/gitaly_client/blobs_stitcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class BlobsStitcher
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 6a97cd8ed17..4e46cb9f05c 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class CommitService
@@ -93,7 +95,7 @@ module Gitlab
response = GitalyClient.call(@repository.storage, :commit_service, :tree_entry, request, timeout: GitalyClient.medium_timeout)
entry = nil
- data = ''
+ data = []
response.each do |msg|
if entry.nil?
entry = msg
@@ -103,7 +105,7 @@ module Gitlab
data << msg.data
end
- entry.data = data
+ entry.data = data.join
entry unless entry.oid.blank?
end
@@ -148,6 +150,24 @@ module Gitlab
GitalyClient.call(@repository.storage, :commit_service, :count_commits, request, timeout: GitalyClient.medium_timeout).count
end
+ def list_last_commits_for_tree(revision, path, offset: 0, limit: 25)
+ request = Gitaly::ListLastCommitsForTreeRequest.new(
+ repository: @gitaly_repo,
+ revision: encode_binary(revision),
+ path: encode_binary(path.to_s),
+ offset: offset,
+ limit: limit
+ )
+
+ response = GitalyClient.call(@repository.storage, :commit_service, :list_last_commits_for_tree, request, timeout: GitalyClient.medium_timeout)
+
+ response.each_with_object({}) do |gitaly_response, hsh|
+ gitaly_response.commits.each do |commit_for_tree|
+ hsh[commit_for_tree.path] = Gitlab::Git::Commit.new(@repository, commit_for_tree.commit)
+ end
+ end
+ end
+
def last_commit_for_path(revision, path)
request = Gitaly::LastCommitForPathRequest.new(
repository: @gitaly_repo,
@@ -172,6 +192,17 @@ module Gitlab
consume_commits_response(response)
end
+ def diff_stats(left_commit_sha, right_commit_sha)
+ request = Gitaly::DiffStatsRequest.new(
+ repository: @gitaly_repo,
+ left_commit_id: left_commit_sha,
+ right_commit_id: right_commit_sha
+ )
+
+ response = GitalyClient.call(@repository.storage, :diff_service, :diff_stats, request, timeout: GitalyClient.medium_timeout)
+ response.flat_map(&:stats)
+ end
+
def find_all_commits(opts = {})
request = Gitaly::FindAllCommitsRequest.new(
repository: @gitaly_repo,
@@ -225,31 +256,33 @@ module Gitlab
)
response = GitalyClient.call(@repository.storage, :commit_service, :raw_blame, request, timeout: GitalyClient.medium_timeout)
- response.reduce("") { |memo, msg| memo << msg.data }
+ response.reduce([]) { |memo, msg| memo << msg.data }.join
end
def find_commit(revision)
- if RequestStore.active?
- # We don't use RequeStstore.fetch(key) { ... } directly because `revision`
- # can be a branch name, so we can't use it as a key as it could point
- # to another commit later on (happens a lot in tests).
+ if Gitlab::SafeRequestStore.active?
+ # We don't use Gitlab::SafeRequestStore.fetch(key) { ... } directly
+ # because `revision` can be a branch name, so we can't use it as a key
+ # as it could point to another commit later on (happens a lot in
+ # tests).
key = {
storage: @gitaly_repo.storage_name,
relative_path: @gitaly_repo.relative_path,
commit_id: revision
}
- return RequestStore[key] if RequestStore.exist?(key)
+ return Gitlab::SafeRequestStore[key] if Gitlab::SafeRequestStore.exist?(key)
commit = call_find_commit(revision)
return unless commit
key[:commit_id] = commit.id
- RequestStore[key] = commit
+ Gitlab::SafeRequestStore[key] = commit
else
call_find_commit(revision)
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def patch(revision)
request = Gitaly::CommitPatchRequest.new(
repository: @gitaly_repo,
@@ -259,6 +292,7 @@ module Gitlab
response.sum(&:data)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def commit_stats(revision)
request = Gitaly::CommitStatsRequest.new(
@@ -313,8 +347,8 @@ module Gitlab
request = Gitaly::ExtractCommitSignatureRequest.new(repository: @gitaly_repo, commit_id: commit_id)
response = GitalyClient.call(@repository.storage, :commit_service, :extract_commit_signature, request)
- signature = ''.b
- signed_text = ''.b
+ signature = +''.b
+ signed_text = +''.b
response.each do |message|
signature << message.signature
@@ -332,7 +366,7 @@ module Gitlab
request = Gitaly::GetCommitSignaturesRequest.new(repository: @gitaly_repo, commit_ids: commit_ids)
response = GitalyClient.call(@repository.storage, :commit_service, :get_commit_signatures, request, timeout: GitalyClient.fast_timeout)
- signatures = Hash.new { |h, k| h[k] = [''.b, ''.b] }
+ signatures = Hash.new { |h, k| h[k] = [+''.b, +''.b] }
current_commit_id = nil
response.each do |message|
@@ -351,7 +385,7 @@ module Gitlab
request = Gitaly::GetCommitMessagesRequest.new(repository: @gitaly_repo, commit_ids: commit_ids)
response = GitalyClient.call(@repository.storage, :commit_service, :get_commit_messages, request, timeout: GitalyClient.fast_timeout)
- messages = Hash.new { |h, k| h[k] = ''.b }
+ messages = Hash.new { |h, k| h[k] = +''.b }
current_commit_id = nil
response.each do |rpc_message|
@@ -369,7 +403,7 @@ module Gitlab
request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false)
request_params[:enforce_limits] = options.fetch(:limits, true)
request_params[:collapse_diffs] = !options.fetch(:expanded, true)
- request_params.merge!(Gitlab::Git::DiffCollection.collection_limits(options).to_h)
+ request_params.merge!(Gitlab::Git::DiffCollection.limits(options).to_h)
request = Gitaly::CommitDiffRequest.new(request_params)
response = GitalyClient.call(@repository.storage, :diff_service, :commit_diff, request, timeout: GitalyClient.medium_timeout)
diff --git a/lib/gitlab/gitaly_client/conflict_files_stitcher.rb b/lib/gitlab/gitaly_client/conflict_files_stitcher.rb
index c275a065bce..0e00f6e8c44 100644
--- a/lib/gitlab/gitaly_client/conflict_files_stitcher.rb
+++ b/lib/gitlab/gitaly_client/conflict_files_stitcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class ConflictFilesStitcher
@@ -17,7 +19,7 @@ module Gitlab
current_file = file_from_gitaly_header(gitaly_file.header)
else
- current_file.raw_content << gitaly_file.content
+ current_file.raw_content = "#{current_file.raw_content}#{gitaly_file.content}"
end
end
end
diff --git a/lib/gitlab/gitaly_client/conflicts_service.rb b/lib/gitlab/gitaly_client/conflicts_service.rb
index aa7e03301f5..6304f998563 100644
--- a/lib/gitlab/gitaly_client/conflicts_service.rb
+++ b/lib/gitlab/gitaly_client/conflicts_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class ConflictsService
diff --git a/lib/gitlab/gitaly_client/diff.rb b/lib/gitlab/gitaly_client/diff.rb
index d98a0ce988f..dd192ccde1a 100644
--- a/lib/gitlab/gitaly_client/diff.rb
+++ b/lib/gitlab/gitaly_client/diff.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class Diff
- ATTRS = %i(from_path to_path old_mode new_mode from_id to_id patch overflow_marker collapsed).freeze
+ ATTRS = %i(from_path to_path old_mode new_mode from_id to_id patch overflow_marker collapsed too_large).freeze
include AttributesBag
end
diff --git a/lib/gitlab/gitaly_client/diff_stitcher.rb b/lib/gitlab/gitaly_client/diff_stitcher.rb
index da243ee2d1a..98d327a7329 100644
--- a/lib/gitlab/gitaly_client/diff_stitcher.rb
+++ b/lib/gitlab/gitaly_client/diff_stitcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class DiffStitcher
@@ -20,7 +22,7 @@ module Gitlab
current_diff = GitalyClient::Diff.new(diff_params)
else
- current_diff.patch += diff_msg.raw_patch_data
+ current_diff.patch = "#{current_diff.patch}#{diff_msg.raw_patch_data}"
end
if diff_msg.end_of_patch
diff --git a/lib/gitlab/gitaly_client/health_check_service.rb b/lib/gitlab/gitaly_client/health_check_service.rb
index 6c1213f5e20..0c495f60633 100644
--- a/lib/gitlab/gitaly_client/health_check_service.rb
+++ b/lib/gitlab/gitaly_client/health_check_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class HealthCheckService
diff --git a/lib/gitlab/gitaly_client/namespace_service.rb b/lib/gitlab/gitaly_client/namespace_service.rb
index d4e982b649a..f0be3cbebd2 100644
--- a/lib/gitlab/gitaly_client/namespace_service.rb
+++ b/lib/gitlab/gitaly_client/namespace_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class NamespaceService
diff --git a/lib/gitlab/gitaly_client/notification_service.rb b/lib/gitlab/gitaly_client/notification_service.rb
index 326e6f7dafc..873c3e4086d 100644
--- a/lib/gitlab/gitaly_client/notification_service.rb
+++ b/lib/gitlab/gitaly_client/notification_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class NotificationService
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 54c78fdb680..c32c2c0b2fb 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class OperationService
@@ -230,6 +232,32 @@ module Gitlab
response.squash_sha
end
+ def user_update_submodule(user:, submodule:, commit_sha:, branch:, message:)
+ request = Gitaly::UserUpdateSubmoduleRequest.new(
+ repository: @gitaly_repo,
+ user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
+ commit_sha: commit_sha,
+ branch: encode_binary(branch),
+ submodule: encode_binary(submodule),
+ commit_message: encode_binary(message)
+ )
+
+ response = GitalyClient.call(
+ @repository.storage,
+ :operation_service,
+ :user_update_submodule,
+ request
+ )
+
+ if response.pre_receive_error.present?
+ raise Gitlab::Git::PreReceiveError, response.pre_receive_error
+ elsif response.commit_error.present?
+ raise Gitlab::Git::CommitError, response.commit_error
+ else
+ Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
+ end
+ end
+
def user_commit_files(
user, branch_name, commit_message, actions, author_email, author_name,
start_branch_name, start_repository)
@@ -273,6 +301,29 @@ module Gitlab
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
end
+ def user_commit_patches(user, branch_name, patches)
+ header = Gitaly::UserApplyPatchRequest::Header.new(
+ repository: @gitaly_repo,
+ user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
+ target_branch: encode_binary(branch_name)
+ )
+ reader = binary_stringio(patches)
+
+ chunks = Enumerator.new do |chunk|
+ chunk.yield Gitaly::UserApplyPatchRequest.new(header: header)
+
+ until reader.eof?
+ patch_chunk = reader.read(MAX_MSG_SIZE)
+
+ chunk.yield(Gitaly::UserApplyPatchRequest.new(patches: patch_chunk))
+ end
+ end
+
+ response = GitalyClient.call(@repository.storage, :operation_service, :user_apply_patch, chunks)
+
+ Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
+ end
+
private
def call_cherry_pick_or_revert(rpc, user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
@@ -333,7 +384,8 @@ module Gitlab
action: action[:action].upcase.to_sym,
file_path: encode_binary(action[:file_path]),
previous_path: encode_binary(action[:previous_path]),
- base64_content: action[:encoding] == 'base64'
+ base64_content: action[:encoding] == 'base64',
+ execute_filemode: !!action[:execute_filemode]
)
rescue RangeError
raise ArgumentError, "Unknown action '#{action[:action]}'"
diff --git a/lib/gitlab/gitaly_client/queue_enumerator.rb b/lib/gitlab/gitaly_client/queue_enumerator.rb
index b8018029552..3a412102abe 100644
--- a/lib/gitlab/gitaly_client/queue_enumerator.rb
+++ b/lib/gitlab/gitaly_client/queue_enumerator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class QueueEnumerator
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index 8acc22e809e..d5633d167ac 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class RefService
@@ -218,7 +220,7 @@ module Gitlab
request = Gitaly::GetTagMessagesRequest.new(repository: @gitaly_repo, tag_ids: tag_ids)
response = GitalyClient.call(@repository.storage, :ref_service, :get_tag_messages, request, timeout: GitalyClient.fast_timeout)
- messages = Hash.new { |h, k| h[k] = ''.b }
+ messages = Hash.new { |h, k| h[k] = +''.b }
current_tag_id = nil
response.each do |rpc_message|
diff --git a/lib/gitlab/gitaly_client/remote_service.rb b/lib/gitlab/gitaly_client/remote_service.rb
index 1381e033d4b..24e8a5e16d3 100644
--- a/lib/gitlab/gitaly_client/remote_service.rb
+++ b/lib/gitlab/gitaly_client/remote_service.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class RemoteService
+ include Gitlab::EncodingHelper
+
MAX_MSG_SIZE = 128.kilobytes.freeze
def self.exists?(remote_url)
@@ -52,6 +56,18 @@ module Gitlab
response.result
end
+ def find_remote_root_ref(remote_name)
+ request = Gitaly::FindRemoteRootRefRequest.new(
+ repository: @gitaly_repo,
+ remote: remote_name
+ )
+
+ response = GitalyClient.call(@storage, :remote_service,
+ :find_remote_root_ref, request)
+
+ encode_utf8(response.ref)
+ end
+
def update_remote_mirror(ref_name, only_branches_matching)
req_enum = Enumerator.new do |y|
y.yield Gitaly::UpdateRemoteMirrorRequest.new(
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 2956ed4b911..f968ebc2cbf 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class RepositoryService
@@ -56,9 +58,9 @@ module Gitlab
request = Gitaly::GetInfoAttributesRequest.new(repository: @gitaly_repo)
response = GitalyClient.call(@storage, :repository_service, :get_info_attributes, request, timeout: GitalyClient.fast_timeout)
- response.each_with_object("") do |message, attributes|
+ response.each_with_object([]) do |message, attributes|
attributes << message.attributes
- end
+ end.join
end
def fetch_remote(remote, ssh_auth:, forced:, no_tags:, timeout:, prune: true)
@@ -349,7 +351,7 @@ module Gitlab
f.write(message.data)
end
end
- # If the file is empty means that we recieved an empty stream, we delete the file
+ # If the file is empty means that we received an empty stream, we delete the file
FileUtils.rm(save_path) if File.zero?(save_path)
end
diff --git a/lib/gitlab/gitaly_client/server_service.rb b/lib/gitlab/gitaly_client/server_service.rb
index ad898278353..0ade6942db9 100644
--- a/lib/gitlab/gitaly_client/server_service.rb
+++ b/lib/gitlab/gitaly_client/server_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
# Meant for extraction of server data, and later maybe to perform misc task
diff --git a/lib/gitlab/gitaly_client/storage_service.rb b/lib/gitlab/gitaly_client/storage_service.rb
index eb0e910665b..4edcb0b8ba9 100644
--- a/lib/gitlab/gitaly_client/storage_service.rb
+++ b/lib/gitlab/gitaly_client/storage_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class StorageService
@@ -5,6 +7,14 @@ module Gitlab
@storage = storage
end
+ # Returns all directories in the git storage directory, lexically ordered
+ def list_directories(depth: 1)
+ request = Gitaly::ListDirectoriesRequest.new(storage_name: @storage, depth: depth)
+
+ GitalyClient.call(@storage, :storage_service, :list_directories, request)
+ .flat_map(&:paths)
+ end
+
# Delete all repositories in the storage. This is a slow and VERY DESTRUCTIVE operation.
def delete_all_repositories
request = Gitaly::DeleteAllRepositoriesRequest.new(storage_name: @storage)
diff --git a/lib/gitlab/gitaly_client/storage_settings.rb b/lib/gitlab/gitaly_client/storage_settings.rb
index 8e530de174d..754cccb6b3f 100644
--- a/lib/gitlab/gitaly_client/storage_settings.rb
+++ b/lib/gitlab/gitaly_client/storage_settings.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
# This is a chokepoint that is meant to help us stop remove all places
@@ -13,7 +15,7 @@ module Gitlab
Storage is invalid because it has no `path` key.
For source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example.
- If you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`.
+ If you're using the GitLab Development Kit, you can update your configuration running `gdk reconfigure`.
MSG
# This class will give easily recognizable NoMethodErrors
diff --git a/lib/gitlab/gitaly_client/util.rb b/lib/gitlab/gitaly_client/util.rb
index 9c19c51d412..dce5d6a8ad0 100644
--- a/lib/gitlab/gitaly_client/util.rb
+++ b/lib/gitlab/gitaly_client/util.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
module Util
diff --git a/lib/gitlab/gitaly_client/wiki_file.rb b/lib/gitlab/gitaly_client/wiki_file.rb
index 47c60c92484..ef2b23732d1 100644
--- a/lib/gitlab/gitaly_client/wiki_file.rb
+++ b/lib/gitlab/gitaly_client/wiki_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class WikiFile
diff --git a/lib/gitlab/gitaly_client/wiki_page.rb b/lib/gitlab/gitaly_client/wiki_page.rb
index a02d15db5dd..757a429fb8a 100644
--- a/lib/gitlab/gitaly_client/wiki_page.rb
+++ b/lib/gitlab/gitaly_client/wiki_page.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitalyClient
class WikiPage
diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb
index 75be7d1f5a0..2b3d622af4d 100644
--- a/lib/gitlab/gitaly_client/wiki_service.rb
+++ b/lib/gitlab/gitaly_client/wiki_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'stringio'
module Gitlab
@@ -110,7 +112,7 @@ module Gitlab
repository: @gitaly_repo,
page_path: encode_binary(page_path),
page: options[:page] || 1,
- per_page: options[:per_page] || Gollum::Page.per_page
+ per_page: options[:per_page] || Gitlab::Git::Wiki::DEFAULT_PAGINATION
)
stream = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_page_versions, request, timeout: GitalyClient.medium_timeout)
@@ -139,7 +141,7 @@ module Gitlab
next unless message.name.present? || wiki_file
if wiki_file
- wiki_file.raw_data << message.raw_data
+ wiki_file.raw_data = "#{wiki_file.raw_data}#{message.raw_data}"
else
wiki_file = GitalyClient::WikiFile.new(message.to_h)
# All gRPC strings in a response are frozen, so we get
@@ -160,7 +162,7 @@ module Gitlab
)
response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_formatted_data, request)
- response.reduce("") { |memo, msg| memo << msg.data }
+ response.reduce([]) { |memo, msg| memo << msg.data }.join
end
private
diff --git a/lib/gitlab/github_import.rb b/lib/gitlab/github_import.rb
index 65b5e30c70f..14a6d6443ec 100644
--- a/lib/gitlab/github_import.rb
+++ b/lib/gitlab/github_import.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GithubImport
def self.refmap
@@ -10,24 +12,6 @@ module Gitlab
Client.new(token_to_use, parallel: parallel)
end
- # Inserts a raw row and returns the ID of the inserted row.
- #
- # attributes - The attributes/columns to set.
- # relation - An ActiveRecord::Relation to use for finding the ID of the row
- # when using MySQL.
- def self.insert_and_return_id(attributes, relation)
- # We use bulk_insert here so we can bypass any queries executed by
- # callbacks or validation rules, as doing this wouldn't scale when
- # importing very large projects.
- result = Gitlab::Database
- .bulk_insert(relation.table_name, [attributes], return_ids: true)
-
- # MySQL doesn't support returning the IDs of a bulk insert in a way that
- # is not a pain, so in this case we'll issue an extra query instead.
- result.first ||
- relation.where(iid: attributes[:iid]).limit(1).pluck(:id).first
- end
-
# Returns the ID of the ghost user.
def self.ghost_user_id
key = 'github-import/ghost-user-id'
diff --git a/lib/gitlab/github_import/bulk_importing.rb b/lib/gitlab/github_import/bulk_importing.rb
index 147597289cf..da2f96b5c4b 100644
--- a/lib/gitlab/github_import/bulk_importing.rb
+++ b/lib/gitlab/github_import/bulk_importing.rb
@@ -15,10 +15,12 @@ module Gitlab
end
# Bulk inserts the given rows into the database.
- def bulk_insert(model, rows, batch_size: 100)
+ def bulk_insert(model, rows, batch_size: 100, pre_hook: nil)
rows.each_slice(batch_size) do |slice|
+ pre_hook.call(slice) if pre_hook
Gitlab::Database.bulk_insert(model.table_name, slice)
end
+ rows
end
end
end
diff --git a/lib/gitlab/github_import/importer/diff_note_importer.rb b/lib/gitlab/github_import/importer/diff_note_importer.rb
index 8274f37d358..d562958e955 100644
--- a/lib/gitlab/github_import/importer/diff_note_importer.rb
+++ b/lib/gitlab/github_import/importer/diff_note_importer.rb
@@ -13,7 +13,7 @@ module Gitlab
@note = note
@project = project
@client = client
- @user_finder = UserFinder.new(project, client)
+ @user_finder = GithubImport::UserFinder.new(project, client)
end
def execute
diff --git a/lib/gitlab/github_import/importer/issue_importer.rb b/lib/gitlab/github_import/importer/issue_importer.rb
index 31fefebf787..4226eee85cc 100644
--- a/lib/gitlab/github_import/importer/issue_importer.rb
+++ b/lib/gitlab/github_import/importer/issue_importer.rb
@@ -4,6 +4,8 @@ module Gitlab
module GithubImport
module Importer
class IssueImporter
+ include Gitlab::Import::DatabaseHelpers
+
attr_reader :project, :issue, :client, :user_finder, :milestone_finder,
:issuable_finder
@@ -19,7 +21,7 @@ module Gitlab
@issue = issue
@project = project
@client = client
- @user_finder = UserFinder.new(project, client)
+ @user_finder = GithubImport::UserFinder.new(project, client)
@milestone_finder = MilestoneFinder.new(project)
@issuable_finder = GithubImport::IssuableFinder.new(project, issue)
end
@@ -55,7 +57,11 @@ module Gitlab
updated_at: issue.updated_at
}
- GithubImport.insert_and_return_id(attributes, project.issues)
+ insert_and_return_id(attributes, project.issues).tap do |id|
+ # We use .insert_and_return_id which effectively disables all callbacks.
+ # Trigger iid logic here to make sure we track internal id values consistently.
+ project.issues.find(id).ensure_project_iid!
+ end
rescue ActiveRecord::InvalidForeignKey
# It's possible the project has been deleted since scheduling this
# job. In this case we'll just skip creating the issue.
diff --git a/lib/gitlab/github_import/importer/labels_importer.rb b/lib/gitlab/github_import/importer/labels_importer.rb
index a73033d35ba..80246fa1b77 100644
--- a/lib/gitlab/github_import/importer/labels_importer.rb
+++ b/lib/gitlab/github_import/importer/labels_importer.rb
@@ -10,11 +10,13 @@ module Gitlab
# project - An instance of `Project`.
# client - An instance of `Gitlab::GithubImport::Client`.
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(project, client)
@project = project
@client = client
@existing_labels = project.labels.pluck(:title).to_set
end
+ # rubocop: enable CodeReuse/ActiveRecord
def execute
bulk_insert(Label, build_labels)
diff --git a/lib/gitlab/github_import/importer/milestones_importer.rb b/lib/gitlab/github_import/importer/milestones_importer.rb
index c53480e828a..8d54b27374c 100644
--- a/lib/gitlab/github_import/importer/milestones_importer.rb
+++ b/lib/gitlab/github_import/importer/milestones_importer.rb
@@ -10,17 +10,29 @@ module Gitlab
# project - An instance of `Project`
# client - An instance of `Gitlab::GithubImport::Client`
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(project, client)
@project = project
@client = client
@existing_milestones = project.milestones.pluck(:iid).to_set
end
+ # rubocop: enable CodeReuse/ActiveRecord
def execute
- bulk_insert(Milestone, build_milestones)
+ # We insert records in bulk, by-passing any standard model callbacks.
+ # The pre_hook here makes sure we track internal ids consistently.
+ # Note this has to be called before performing an insert of a batch
+ # because we're outside a transaction scope here.
+ bulk_insert(Milestone, build_milestones, pre_hook: method(:track_greatest_iid))
build_milestones_cache
end
+ def track_greatest_iid(slice)
+ greatest_iid = slice.max { |e| e[:iid] }[:iid]
+
+ InternalId.track_greatest(nil, { project: project }, :milestones, greatest_iid, ->(_) { project.milestones.maximum(:iid) })
+ end
+
def build_milestones
build_database_rows(each_milestone)
end
diff --git a/lib/gitlab/github_import/importer/note_importer.rb b/lib/gitlab/github_import/importer/note_importer.rb
index c890f2df360..2b06d1b3baf 100644
--- a/lib/gitlab/github_import/importer/note_importer.rb
+++ b/lib/gitlab/github_import/importer/note_importer.rb
@@ -13,7 +13,7 @@ module Gitlab
@note = note
@project = project
@client = client
- @user_finder = UserFinder.new(project, client)
+ @user_finder = GithubImport::UserFinder.new(project, client)
end
def execute
diff --git a/lib/gitlab/github_import/importer/pull_request_importer.rb b/lib/gitlab/github_import/importer/pull_request_importer.rb
index 6b3688c4381..ae7c4cf1b38 100644
--- a/lib/gitlab/github_import/importer/pull_request_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_importer.rb
@@ -4,6 +4,8 @@ module Gitlab
module GithubImport
module Importer
class PullRequestImporter
+ include Gitlab::Import::MergeRequestHelpers
+
attr_reader :pull_request, :project, :client, :user_finder,
:milestone_finder, :issuable_finder
@@ -15,7 +17,7 @@ module Gitlab
@pull_request = pull_request
@project = project
@client = client
- @user_finder = UserFinder.new(project, client)
+ @user_finder = GithubImport::UserFinder.new(project, client)
@milestone_finder = MilestoneFinder.new(project)
@issuable_finder =
GithubImport::IssuableFinder.new(project, pull_request)
@@ -44,75 +46,27 @@ module Gitlab
description = MarkdownText
.format(pull_request.description, pull_request.author, author_found)
- # This work must be wrapped in a transaction as otherwise we can leave
- # behind incomplete data in the event of an error. This can then lead
- # to duplicate key errors when jobs are retried.
- MergeRequest.transaction do
- attributes = {
- iid: pull_request.iid,
- title: pull_request.truncated_title,
- description: description,
- source_project_id: project.id,
- target_project_id: project.id,
- source_branch: pull_request.formatted_source_branch,
- target_branch: pull_request.target_branch,
- state: pull_request.state,
- milestone_id: milestone_finder.id_for(pull_request),
- author_id: author_id,
- assignee_id: user_finder.assignee_id_for(pull_request),
- created_at: pull_request.created_at,
- updated_at: pull_request.updated_at
- }
-
- # When creating merge requests there are a lot of hooks that may
- # run, for many different reasons. Many of these hooks (e.g. the
- # ones used for rendering Markdown) are completely unnecessary and
- # may even lead to transaction timeouts.
- #
- # To ensure importing pull requests has a minimal impact and can
- # complete in a reasonable time we bypass all the hooks by inserting
- # the row and then retrieving it. We then only perform the
- # additional work that is strictly necessary.
- merge_request_id = GithubImport
- .insert_and_return_id(attributes, project.merge_requests)
+ attributes = {
+ iid: pull_request.iid,
+ title: pull_request.truncated_title,
+ description: description,
+ source_project_id: project.id,
+ target_project_id: project.id,
+ source_branch: pull_request.formatted_source_branch,
+ target_branch: pull_request.target_branch,
+ state: pull_request.state,
+ milestone_id: milestone_finder.id_for(pull_request),
+ author_id: author_id,
+ assignee_id: user_finder.assignee_id_for(pull_request),
+ created_at: pull_request.created_at,
+ updated_at: pull_request.updated_at
+ }
- [project.merge_requests.find(merge_request_id), false]
- end
- rescue ActiveRecord::InvalidForeignKey
- # It's possible the project has been deleted since scheduling this
- # job. In this case we'll just skip creating the merge request.
- []
- rescue ActiveRecord::RecordNotUnique
- # It's possible we previously created the MR, but failed when updating
- # the Git data. In this case we'll just continue working on the
- # existing row.
- [project.merge_requests.find_by(iid: pull_request.iid), true]
+ create_merge_request_without_hooks(project, attributes, pull_request.iid)
end
- def insert_git_data(merge_request, already_exists = false)
- # These fields are set so we can create the correct merge request
- # diffs.
- merge_request.source_branch_sha = pull_request.source_branch_sha
- merge_request.target_branch_sha = pull_request.target_branch_sha
-
- merge_request.keep_around_commit
-
- # MR diffs normally use an "after_save" hook to pull data from Git.
- # All of this happens in the transaction started by calling
- # create/save/etc. This in turn can lead to these transactions being
- # held open for much longer than necessary. To work around this we
- # first save the diff, then populate it.
- diff =
- if already_exists
- merge_request.merge_request_diffs.take ||
- merge_request.merge_request_diffs.build
- else
- merge_request.merge_request_diffs.build
- end
-
- diff.importing = true
- diff.save
- diff.save_git_content
+ def insert_git_data(merge_request, already_exists)
+ insert_or_replace_git_data(merge_request, pull_request.source_branch_sha, pull_request.target_branch_sha, already_exists)
end
end
end
diff --git a/lib/gitlab/github_import/importer/releases_importer.rb b/lib/gitlab/github_import/importer/releases_importer.rb
index 100f459fdcc..0e7c9ee0d00 100644
--- a/lib/gitlab/github_import/importer/releases_importer.rb
+++ b/lib/gitlab/github_import/importer/releases_importer.rb
@@ -10,11 +10,13 @@ module Gitlab
# project - An instance of `Project`
# client - An instance of `Gitlab::GithubImport::Client`
+ # rubocop: disable CodeReuse/ActiveRecord
def initialize(project, client)
@project = project
@client = client
@existing_tags = project.releases.pluck(:tag).to_set
end
+ # rubocop: enable CodeReuse/ActiveRecord
def execute
bulk_insert(Release, build_releases)
diff --git a/lib/gitlab/github_import/importer/repository_importer.rb b/lib/gitlab/github_import/importer/repository_importer.rb
index 01168abde6c..374dc9d3c00 100644
--- a/lib/gitlab/github_import/importer/repository_importer.rb
+++ b/lib/gitlab/github_import/importer/repository_importer.rb
@@ -14,11 +14,13 @@ module Gitlab
end
# Returns true if we should import the wiki for the project.
+ # rubocop: disable CodeReuse/ActiveRecord
def import_wiki?
client.repository(project.import_source)&.has_wiki &&
!project.wiki_repository_exists? &&
Gitlab::GitalyClient::RemoteService.exists?(wiki_url)
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Imports the repository data.
#
diff --git a/lib/gitlab/github_import/label_finder.rb b/lib/gitlab/github_import/label_finder.rb
index 9be071141db..d2479a8f565 100644
--- a/lib/gitlab/github_import/label_finder.rb
+++ b/lib/gitlab/github_import/label_finder.rb
@@ -18,6 +18,7 @@ module Gitlab
Caching.read_integer(cache_key_for(name))
end
+ # rubocop: disable CodeReuse/ActiveRecord
def build_cache
mapping = @project
.labels
@@ -28,6 +29,7 @@ module Gitlab
Caching.write_multiple(mapping)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def cache_key_for(name)
CACHE_KEY % { project: project.id, name: name }
diff --git a/lib/gitlab/github_import/milestone_finder.rb b/lib/gitlab/github_import/milestone_finder.rb
index 208d15dc144..5625730e796 100644
--- a/lib/gitlab/github_import/milestone_finder.rb
+++ b/lib/gitlab/github_import/milestone_finder.rb
@@ -21,6 +21,7 @@ module Gitlab
Caching.read_integer(cache_key_for(issuable.milestone_number))
end
+ # rubocop: disable CodeReuse/ActiveRecord
def build_cache
mapping = @project
.milestones
@@ -31,6 +32,7 @@ module Gitlab
Caching.write_multiple(mapping)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def cache_key_for(iid)
CACHE_KEY % { project: project.id, iid: iid }
diff --git a/lib/gitlab/github_import/representation/expose_attribute.rb b/lib/gitlab/github_import/representation/expose_attribute.rb
index c3405759631..d2438ee8094 100644
--- a/lib/gitlab/github_import/representation/expose_attribute.rb
+++ b/lib/gitlab/github_import/representation/expose_attribute.rb
@@ -6,7 +6,7 @@ module Gitlab
module ExposeAttribute
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
# Defines getter methods for the given attribute names.
#
# Example:
diff --git a/lib/gitlab/github_import/user_finder.rb b/lib/gitlab/github_import/user_finder.rb
index be1259662a7..30283f147ef 100644
--- a/lib/gitlab/github_import/user_finder.rb
+++ b/lib/gitlab/github_import/user_finder.rb
@@ -136,13 +136,17 @@ module Gitlab
Caching.write(ID_FOR_EMAIL_CACHE_KEY % email, gitlab_id)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def query_id_for_github_id(id)
User.for_github_id(id).pluck(:id).first
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def query_id_for_github_email(email)
User.by_any_email(email).pluck(:id).first
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Reads an ID from the cache.
#
diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb
index 38ef12491df..86474159f8b 100644
--- a/lib/gitlab/gitlab_import/client.rb
+++ b/lib/gitlab/gitlab_import/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitlabImport
class Client
diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb
index 195672f5a12..e84863deba8 100644
--- a/lib/gitlab/gitlab_import/importer.rb
+++ b/lib/gitlab/gitlab_import/importer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitlabImport
class Importer
@@ -22,22 +24,22 @@ module Gitlab
issues = client.issues(project_identifier)
issues.each do |issue|
- body = @formatter.author_line(issue["author"]["name"])
- body += issue["description"]
+ body = [@formatter.author_line(issue["author"]["name"])]
+ body << issue["description"]
comments = client.issue_comments(project_identifier, issue["iid"])
if comments.any?
- body += @formatter.comments_header
+ body << @formatter.comments_header
end
comments.each do |comment|
- body += @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
+ body << @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
end
project.issues.create!(
iid: issue["iid"],
- description: body,
+ description: body.join,
title: issue["title"],
state: issue["state"],
updated_at: issue["updated_at"],
@@ -52,10 +54,12 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def gitlab_user_id(project, gitlab_id)
user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'gitlab'", gitlab_id.to_s)
(user && user.id) || project.creator_id
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/gitlab_import/project_creator.rb b/lib/gitlab/gitlab_import/project_creator.rb
index 430b8c10058..35feea17351 100644
--- a/lib/gitlab/gitlab_import/project_creator.rb
+++ b/lib/gitlab/gitlab_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GitlabImport
class ProjectCreator
diff --git a/lib/gitlab/gl_id.rb b/lib/gitlab/gl_id.rb
index a53d156b41f..1ed842c2264 100644
--- a/lib/gitlab/gl_id.rb
+++ b/lib/gitlab/gl_id.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GlId
def self.gl_id(user)
diff --git a/lib/gitlab/gl_repository.rb b/lib/gitlab/gl_repository.rb
index 07c0abcce23..435b74806e7 100644
--- a/lib/gitlab/gl_repository.rb
+++ b/lib/gitlab/gl_repository.rb
@@ -1,9 +1,12 @@
+# frozen_string_literal: true
+
module Gitlab
module GlRepository
def self.gl_repository(project, is_wiki)
"#{is_wiki ? 'wiki' : 'project'}-#{project.id}"
end
+ # rubocop: disable CodeReuse/ActiveRecord
def self.parse(gl_repository)
match_data = /\A(project|wiki)-([1-9][0-9]*)\z/.match(gl_repository)
unless match_data
@@ -16,5 +19,6 @@ module Gitlab
[project, wiki]
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index deaa14c8434..15137140639 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop:disable Metrics/AbcSize
module Gitlab
@@ -6,7 +8,10 @@ module Gitlab
def add_gon_variables
gon.api_version = 'v4'
- gon.default_avatar_url = URI.join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
+ gon.default_avatar_url =
+ Gitlab::Utils.append_path(
+ Gitlab.config.gitlab.url,
+ ActionController::Base.helpers.image_path('no_avatar.png'))
gon.max_file_size = Gitlab::CurrentSettings.max_attachment_size
gon.asset_host = ActionController::Base.asset_host
gon.webpack_public_path = webpack_public_path
@@ -30,5 +35,20 @@ module Gitlab
gon.current_user_avatar_url = current_user.avatar_url
end
end
+
+ # Exposes the state of a feature flag to the frontend code.
+ #
+ # name - The name of the feature flag, e.g. `my_feature`.
+ # args - Any additional arguments to pass to `Feature.enabled?`. This allows
+ # you to check if a flag is enabled for a particular user.
+ def push_frontend_feature_flag(name, *args)
+ var_name = name.to_s.camelize(:lower)
+ enabled = Feature.enabled?(name, *args)
+
+ # Here the `true` argument signals gon that the value should be merged
+ # into any existing ones, instead of overwriting them. This allows you to
+ # use this method to push multiple feature flags.
+ gon.push({ features: { var_name => enabled } }, true)
+ end
end
end
diff --git a/lib/gitlab/google_code_import/client.rb b/lib/gitlab/google_code_import/client.rb
index b1dbf554e41..52d714880b5 100644
--- a/lib/gitlab/google_code_import/client.rb
+++ b/lib/gitlab/google_code_import/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GoogleCodeImport
class Client
diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
index 5070f4e3cfe..1e7203cb82a 100644
--- a/lib/gitlab/google_code_import/importer.rb
+++ b/lib/gitlab/google_code_import/importer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GoogleCodeImport
class Importer
@@ -78,6 +80,7 @@ module Gitlab
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def import_issues
return unless repo.issues
@@ -101,7 +104,7 @@ module Gitlab
if username.start_with?("@")
username = username[1..-1]
- if user = User.find_by(username: username)
+ if user = UserFinder.new(username).find_by_username
assignee_id = user.id
end
end
@@ -123,6 +126,7 @@ module Gitlab
import_issue_comments(issue, comments)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def import_issue_labels(raw_issue)
labels = []
diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb
index 326cfcaa8af..eaef85acb98 100644
--- a/lib/gitlab/google_code_import/project_creator.rb
+++ b/lib/gitlab/google_code_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GoogleCodeImport
class ProjectCreator
diff --git a/lib/gitlab/google_code_import/repository.rb b/lib/gitlab/google_code_import/repository.rb
index ad33fc2cad2..19627c8cd35 100644
--- a/lib/gitlab/google_code_import/repository.rb
+++ b/lib/gitlab/google_code_import/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GoogleCodeImport
class Repository
diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb
index 8a91e034377..e53c2d00743 100644
--- a/lib/gitlab/gpg.rb
+++ b/lib/gitlab/gpg.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Gpg
extend self
diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb
index 2716834f566..31bab20b044 100644
--- a/lib/gitlab/gpg/commit.rb
+++ b/lib/gitlab/gpg/commit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Gpg
class Commit
@@ -26,6 +28,7 @@ module Gitlab
!!(signature_text && signed_text)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def signature
return unless has_signature?
@@ -36,6 +39,7 @@ module Gitlab
@signature = create_cached_signature!
end
+ # rubocop: enable CodeReuse/ActiveRecord
def update_signature!(cached_signature)
using_keychain do |gpg_key|
@@ -113,9 +117,11 @@ module Gitlab
gpg_key&.verified_user_infos&.first || gpg_key&.user_infos&.first || {}
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_gpg_key(keyid)
GpgKey.find_by(primary_keyid: keyid) || GpgKeySubkey.find_by(keyid: keyid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/gpg/invalid_gpg_signature_updater.rb b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
index 1991911ef6a..d892d27a917 100644
--- a/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
+++ b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Gpg
class InvalidGpgSignatureUpdater
@@ -5,6 +7,7 @@ module Gitlab
@gpg_key = gpg_key
end
+ # rubocop: disable CodeReuse/ActiveRecord
def run
GpgSignature
.select(:id, :commit_sha, :project_id)
@@ -12,6 +15,7 @@ module Gitlab
.where(gpg_key_primary_keyid: @gpg_key.keyids)
.find_each { |sig| sig.gpg_commit&.update_signature!(sig) }
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb b/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb
index 0014ce2689b..9bb1e8fc7a2 100644
--- a/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb
+++ b/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GrapeLogging
module Formatters
@@ -6,7 +8,7 @@ module Gitlab
def call(severity, datetime, _, data)
time = data.delete :time
- data[:params] = utf8_encode_values(data[:params]) if data.has_key?(:params)
+ data[:params] = process_params(data)
attributes = {
time: datetime.utc.iso8601(3),
@@ -20,6 +22,14 @@ module Gitlab
private
+ def process_params(data)
+ return [] unless data.has_key?(:params)
+
+ data[:params]
+ .each_pair
+ .map { |k, v| { key: k, value: utf8_encode_values(v) } }
+ end
+
def utf8_encode_values(data)
case data
when Hash
diff --git a/lib/gitlab/grape_logging/loggers/perf_logger.rb b/lib/gitlab/grape_logging/loggers/perf_logger.rb
new file mode 100644
index 00000000000..e3b9c59bd6e
--- /dev/null
+++ b/lib/gitlab/grape_logging/loggers/perf_logger.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+# This module adds additional performance metrics to the grape logger
+module Gitlab
+ module GrapeLogging
+ module Loggers
+ class PerfLogger < ::GrapeLogging::Loggers::Base
+ def parameters(_, _)
+ { gitaly_calls: Gitlab::GitalyClient.get_request_count }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/grape_logging/loggers/queue_duration_logger.rb b/lib/gitlab/grape_logging/loggers/queue_duration_logger.rb
index 0adac79f25a..705e23adff2 100644
--- a/lib/gitlab/grape_logging/loggers/queue_duration_logger.rb
+++ b/lib/gitlab/grape_logging/loggers/queue_duration_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This grape_logging module (https://github.com/aserafin/grape_logging) makes it
# possible to log how much time an API request was queued by Workhorse.
module Gitlab
diff --git a/lib/gitlab/grape_logging/loggers/route_logger.rb b/lib/gitlab/grape_logging/loggers/route_logger.rb
new file mode 100644
index 00000000000..f3146b4dfd9
--- /dev/null
+++ b/lib/gitlab/grape_logging/loggers/route_logger.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# This grape_logging module (https://github.com/aserafin/grape_logging) makes it
+# possible to log the details of the action
+module Gitlab
+ module GrapeLogging
+ module Loggers
+ class RouteLogger < ::GrapeLogging::Loggers::Base
+ def parameters(request, _)
+ endpoint = request.env[Grape::Env::API_ENDPOINT]
+ route = endpoint&.route&.pattern&.origin
+
+ return {} unless route
+
+ { route: route }
+ rescue
+ # endpoint.route calls env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info]
+ # but env[Grape::Env::GRAPE_ROUTING_ARGS] is nil in the case of a 405 response
+ # so we're rescuing exceptions and bailing out
+ {}
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/grape_logging/loggers/user_logger.rb b/lib/gitlab/grape_logging/loggers/user_logger.rb
index fa172861967..6caa6c715e7 100644
--- a/lib/gitlab/grape_logging/loggers/user_logger.rb
+++ b/lib/gitlab/grape_logging/loggers/user_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This grape_logging module (https://github.com/aserafin/grape_logging) makes it
# possible to log the user who performed the Grape API action by retrieving
# the user context from the request environment.
diff --git a/lib/gitlab/graphql.rb b/lib/gitlab/graphql.rb
index 04a89432230..74c04e5380e 100644
--- a/lib/gitlab/graphql.rb
+++ b/lib/gitlab/graphql.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
StandardGraphqlError = Class.new(StandardError)
diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb
index 93a903915b0..5e48bf9043d 100644
--- a/lib/gitlab/graphql/authorize.rb
+++ b/lib/gitlab/graphql/authorize.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
# Allow fields to declare permissions their objects must have. The field
diff --git a/lib/gitlab/graphql/authorize/authorize_resource.rb b/lib/gitlab/graphql/authorize/authorize_resource.rb
index 40895686a8a..a56c4f6368d 100644
--- a/lib/gitlab/graphql/authorize/authorize_resource.rb
+++ b/lib/gitlab/graphql/authorize/authorize_resource.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
module Authorize
diff --git a/lib/gitlab/graphql/authorize/instrumentation.rb b/lib/gitlab/graphql/authorize/instrumentation.rb
index 6cb8e617f62..d638d2b43ee 100644
--- a/lib/gitlab/graphql/authorize/instrumentation.rb
+++ b/lib/gitlab/graphql/authorize/instrumentation.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
module Authorize
diff --git a/lib/gitlab/graphql/connections.rb b/lib/gitlab/graphql/connections.rb
index 2582ffeb2a8..fbccdfa7b08 100644
--- a/lib/gitlab/graphql/connections.rb
+++ b/lib/gitlab/graphql/connections.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
module Connections
diff --git a/lib/gitlab/graphql/connections/keyset_connection.rb b/lib/gitlab/graphql/connections/keyset_connection.rb
index abee2afe144..851054c0393 100644
--- a/lib/gitlab/graphql/connections/keyset_connection.rb
+++ b/lib/gitlab/graphql/connections/keyset_connection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
module Connections
@@ -6,6 +8,7 @@ module Gitlab
encode(node[order_field].to_s)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def sliced_nodes
@sliced_nodes ||=
begin
@@ -17,7 +20,9 @@ module Gitlab
sliced
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def paged_nodes
if first && last
raise Gitlab::Graphql::Errors::ArgumentError.new("Can only provide either `first` or `last`, not both")
@@ -29,6 +34,7 @@ module Gitlab
sliced_nodes.limit(limit_value)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/lib/gitlab/graphql/errors.rb b/lib/gitlab/graphql/errors.rb
index f8c7ec24be1..fe74549e322 100644
--- a/lib/gitlab/graphql/errors.rb
+++ b/lib/gitlab/graphql/errors.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
module Errors
diff --git a/lib/gitlab/graphql/expose_permissions.rb b/lib/gitlab/graphql/expose_permissions.rb
index e3779995406..365b7cca24f 100644
--- a/lib/gitlab/graphql/expose_permissions.rb
+++ b/lib/gitlab/graphql/expose_permissions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
module ExposePermissions
diff --git a/lib/gitlab/graphql/mount_mutation.rb b/lib/gitlab/graphql/mount_mutation.rb
index 8cab84d7a5f..9048967d4e1 100644
--- a/lib/gitlab/graphql/mount_mutation.rb
+++ b/lib/gitlab/graphql/mount_mutation.rb
@@ -5,7 +5,7 @@ module Gitlab
module MountMutation
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
def mount_mutation(mutation_class)
# Using an underscored field name symbol will make `graphql-ruby`
# standardize the field name
diff --git a/lib/gitlab/graphql/present.rb b/lib/gitlab/graphql/present.rb
index 2c7b64f1be9..7f69bf601d6 100644
--- a/lib/gitlab/graphql/present.rb
+++ b/lib/gitlab/graphql/present.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
module Present
diff --git a/lib/gitlab/graphql/present/instrumentation.rb b/lib/gitlab/graphql/present/instrumentation.rb
index f87fd147b15..ab03c40c22d 100644
--- a/lib/gitlab/graphql/present/instrumentation.rb
+++ b/lib/gitlab/graphql/present/instrumentation.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
module Present
diff --git a/lib/gitlab/graphql/variables.rb b/lib/gitlab/graphql/variables.rb
index ffbaf65b512..b13ea37c21f 100644
--- a/lib/gitlab/graphql/variables.rb
+++ b/lib/gitlab/graphql/variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphql
class Variables
diff --git a/lib/gitlab/graphs/commits.rb b/lib/gitlab/graphs/commits.rb
index c4ffc19df09..66e1b2e78b4 100644
--- a/lib/gitlab/graphs/commits.rb
+++ b/lib/gitlab/graphs/commits.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Graphs
class Commits
diff --git a/lib/gitlab/group_hierarchy.rb b/lib/gitlab/group_hierarchy.rb
index 42ded7c286f..c940ea7305e 100644
--- a/lib/gitlab/group_hierarchy.rb
+++ b/lib/gitlab/group_hierarchy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# Retrieving of parent or child groups based on a base ActiveRecord relation.
#
@@ -19,9 +21,11 @@ module Gitlab
# Returns the set of descendants of a given relation, but excluding the given
# relation
+ # rubocop: disable CodeReuse/ActiveRecord
def descendants
base_and_descendants.where.not(id: descendants_base.select(:id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Returns the set of ancestors of a given relation, but excluding the given
# relation
@@ -29,9 +33,11 @@ module Gitlab
# Passing an `upto` will stop the recursion once the specified parent_id is
# reached. So all ancestors *lower* than the specified ancestor will be
# included.
+ # rubocop: disable CodeReuse/ActiveRecord
def ancestors(upto: nil)
base_and_ancestors(upto: upto).where.not(id: ancestors_base.select(:id))
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Returns a relation that includes the ancestors_base set of groups
# and all their ancestors (recursively).
@@ -75,6 +81,7 @@ module Gitlab
# Rails thinking it's selecting data the usual way.
#
# If nested groups are not supported, ancestors_base is returned.
+ # rubocop: disable CodeReuse/ActiveRecord
def all_groups
return ancestors_base unless Group.supports_nested_groups?
@@ -84,20 +91,22 @@ module Gitlab
ancestors_table = ancestors.alias_to(groups_table)
descendants_table = descendants.alias_to(groups_table)
- union = SQL::Union.new([model.unscoped.from(ancestors_table),
- model.unscoped.from(descendants_table)])
-
relation = model
.unscoped
.with
.recursive(ancestors.to_arel, descendants.to_arel)
- .from("(#{union.to_sql}) #{model.table_name}")
+ .from_union([
+ model.unscoped.from(ancestors_table),
+ model.unscoped.from(descendants_table)
+ ])
read_only(relation)
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
+ # rubocop: disable CodeReuse/ActiveRecord
def base_and_ancestors_cte(stop_id = nil)
cte = SQL::RecursiveCTE.new(:base_and_ancestors)
@@ -113,7 +122,9 @@ module Gitlab
cte << parent_query
cte
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def base_and_descendants_cte
cte = SQL::RecursiveCTE.new(:base_and_descendants)
@@ -127,6 +138,7 @@ module Gitlab
cte
end
+ # rubocop: enable CodeReuse/ActiveRecord
def groups_table
model.arel_table
diff --git a/lib/gitlab/hashed_storage/migrator.rb b/lib/gitlab/hashed_storage/migrator.rb
index d11fcc6a3e3..1f29cf10cad 100644
--- a/lib/gitlab/hashed_storage/migrator.rb
+++ b/lib/gitlab/hashed_storage/migrator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HashedStorage
# Hashed Storage Migrator
@@ -22,6 +24,7 @@ module Gitlab
#
# @param [Object] start first project id for the range
# @param [Object] finish last project id for the range
+ # rubocop: disable CodeReuse/ActiveRecord
def bulk_migrate(start, finish)
projects = build_relation(start, finish)
@@ -29,6 +32,7 @@ module Gitlab
migrate(project)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Flag a project to be migrated
#
@@ -43,6 +47,7 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def build_relation(start, finish)
relation = Project
table = Project.arel_table
@@ -52,6 +57,7 @@ module Gitlab
relation
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/hashed_storage/rake_helper.rb b/lib/gitlab/hashed_storage/rake_helper.rb
index 303b05e6a9a..38f552fab03 100644
--- a/lib/gitlab/hashed_storage/rake_helper.rb
+++ b/lib/gitlab/hashed_storage/rake_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HashedStorage
module RakeHelper
@@ -21,6 +23,7 @@ module Gitlab
!range_from.nil? && range_from == range_to
end
+ # rubocop: disable CodeReuse/ActiveRecord
def self.project_id_batches(&block)
Project.with_unmigrated_storage.in_batches(of: batch_size, start: range_from, finish: range_to) do |relation| # rubocop: disable Cop/InBatches
ids = relation.pluck(:id)
@@ -28,20 +31,25 @@ module Gitlab
yield ids.min, ids.max
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def self.legacy_attachments_relation
Upload.joins(<<~SQL).where('projects.storage_version < :version OR projects.storage_version IS NULL', version: Project::HASHED_STORAGE_FEATURES[:attachments])
JOIN projects
ON (uploads.model_type='Project' AND uploads.model_id=projects.id)
SQL
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def self.hashed_attachments_relation
Upload.joins(<<~SQL).where('projects.storage_version >= :version', version: Project::HASHED_STORAGE_FEATURES[:attachments])
JOIN projects
ON (uploads.model_type='Project' AND uploads.model_id=projects.id)
SQL
end
+ # rubocop: enable CodeReuse/ActiveRecord
def self.relation_summary(relation_name, relation)
relation_count = relation.count
@@ -62,6 +70,7 @@ module Gitlab
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def self.listing(relation_name, relation)
relation_count = relation_summary(relation_name, relation)
return unless relation_count > 0
@@ -78,6 +87,7 @@ module Gitlab
break if index + 1 >= limit
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/health_checks/base_abstract_check.rb b/lib/gitlab/health_checks/base_abstract_check.rb
index 8b365dab185..1d31f59999c 100644
--- a/lib/gitlab/health_checks/base_abstract_check.rb
+++ b/lib/gitlab/health_checks/base_abstract_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
module BaseAbstractCheck
diff --git a/lib/gitlab/health_checks/db_check.rb b/lib/gitlab/health_checks/db_check.rb
index 08495c0a59e..2bcd25cd3cc 100644
--- a/lib/gitlab/health_checks/db_check.rb
+++ b/lib/gitlab/health_checks/db_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
class DbCheck
diff --git a/lib/gitlab/health_checks/gitaly_check.rb b/lib/gitlab/health_checks/gitaly_check.rb
index 1f623e0b6ec..898733fea5d 100644
--- a/lib/gitlab/health_checks/gitaly_check.rb
+++ b/lib/gitlab/health_checks/gitaly_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
class GitalyCheck
diff --git a/lib/gitlab/health_checks/metric.rb b/lib/gitlab/health_checks/metric.rb
index d62d9136886..62a5216d159 100644
--- a/lib/gitlab/health_checks/metric.rb
+++ b/lib/gitlab/health_checks/metric.rb
@@ -1,3 +1,6 @@
-module Gitlab::HealthChecks # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module Gitlab::HealthChecks
Metric = Struct.new(:name, :value, :labels)
end
diff --git a/lib/gitlab/health_checks/prometheus_text_format.rb b/lib/gitlab/health_checks/prometheus_text_format.rb
index b3c759b4730..2a8f9d31cd5 100644
--- a/lib/gitlab/health_checks/prometheus_text_format.rb
+++ b/lib/gitlab/health_checks/prometheus_text_format.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
class PrometheusTextFormat
diff --git a/lib/gitlab/health_checks/redis/cache_check.rb b/lib/gitlab/health_checks/redis/cache_check.rb
index 0eb9b77634a..0c8fe83893b 100644
--- a/lib/gitlab/health_checks/redis/cache_check.rb
+++ b/lib/gitlab/health_checks/redis/cache_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
module Redis
@@ -19,11 +21,13 @@ module Gitlab
result == 'PONG'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def check
catch_timeout 10.seconds do
Gitlab::Redis::Cache.with(&:ping)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/health_checks/redis/queues_check.rb b/lib/gitlab/health_checks/redis/queues_check.rb
index f322fe831b8..b1e33b9f459 100644
--- a/lib/gitlab/health_checks/redis/queues_check.rb
+++ b/lib/gitlab/health_checks/redis/queues_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
module Redis
@@ -19,11 +21,13 @@ module Gitlab
result == 'PONG'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def check
catch_timeout 10.seconds do
Gitlab::Redis::Queues.with(&:ping)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/health_checks/redis/redis_check.rb b/lib/gitlab/health_checks/redis/redis_check.rb
index 8ceb0a0aa46..f7e46fce134 100644
--- a/lib/gitlab/health_checks/redis/redis_check.rb
+++ b/lib/gitlab/health_checks/redis/redis_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
module Redis
diff --git a/lib/gitlab/health_checks/redis/shared_state_check.rb b/lib/gitlab/health_checks/redis/shared_state_check.rb
index 07e6f707998..285ac271929 100644
--- a/lib/gitlab/health_checks/redis/shared_state_check.rb
+++ b/lib/gitlab/health_checks/redis/shared_state_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
module Redis
@@ -19,11 +21,13 @@ module Gitlab
result == 'PONG'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def check
catch_timeout 10.seconds do
Gitlab::Redis::SharedState.with(&:ping)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/health_checks/result.rb b/lib/gitlab/health_checks/result.rb
index e323e2c9723..d32a6980eb8 100644
--- a/lib/gitlab/health_checks/result.rb
+++ b/lib/gitlab/health_checks/result.rb
@@ -1,3 +1,6 @@
-module Gitlab::HealthChecks # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module Gitlab::HealthChecks
Result = Struct.new(:success, :message, :labels)
end
diff --git a/lib/gitlab/health_checks/simple_abstract_check.rb b/lib/gitlab/health_checks/simple_abstract_check.rb
index 96945ce5b20..3588260d6eb 100644
--- a/lib/gitlab/health_checks/simple_abstract_check.rb
+++ b/lib/gitlab/health_checks/simple_abstract_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HealthChecks
module SimpleAbstractCheck
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 5408a1a6838..a4e60bbd828 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -1,20 +1,28 @@
+# frozen_string_literal: true
+
module Gitlab
class Highlight
- def self.highlight(blob_name, blob_content, repository: nil, plain: false)
- new(blob_name, blob_content, repository: repository)
+ TIMEOUT_BACKGROUND = 30.seconds
+ TIMEOUT_FOREGROUND = 3.seconds
+ MAXIMUM_TEXT_HIGHLIGHT_SIZE = 1.megabyte
+
+ def self.highlight(blob_name, blob_content, language: nil, plain: false)
+ new(blob_name, blob_content, language: language)
.highlight(blob_content, continue: false, plain: plain)
end
attr_reader :blob_name
- def initialize(blob_name, blob_content, repository: nil)
+ def initialize(blob_name, blob_content, language: nil)
@formatter = Rouge::Formatters::HTMLGitlab
- @repository = repository
+ @language = language
@blob_name = blob_name
@blob_content = blob_content
end
def highlight(text, continue: true, plain: false)
+ plain ||= text.length > MAXIMUM_TEXT_HIGHLIGHT_SIZE
+
highlighted_text = highlight_text(text, continue: continue, plain: plain)
highlighted_text = link_dependencies(text, highlighted_text) if blob_name
highlighted_text
@@ -31,11 +39,9 @@ module Gitlab
private
def custom_language
- language_name = @repository && @repository.gitattribute(@blob_name, 'gitlab-language')
-
- return nil unless language_name
+ return nil unless @language
- Rouge::Lexer.find_fancy(language_name)
+ Rouge::Lexer.find_fancy(@language)
end
def highlight_text(text, continue: true, plain: false)
@@ -51,11 +57,20 @@ module Gitlab
end
def highlight_rich(text, continue: true)
- @formatter.format(lexer.lex(text, continue: continue), tag: lexer.tag).html_safe
+ tag = lexer.tag
+ tokens = lexer.lex(text, continue: continue)
+ Timeout.timeout(timeout_time) { @formatter.format(tokens, tag: tag).html_safe }
+ rescue Timeout::Error => e
+ Gitlab::Sentry.track_exception(e)
+ highlight_plain(text)
rescue
highlight_plain(text)
end
+ def timeout_time
+ Sidekiq.server? ? TIMEOUT_BACKGROUND : TIMEOUT_FOREGROUND
+ end
+
def link_dependencies(text, highlighted_text)
Gitlab::DependencyLinker.link(blob_name, text, highlighted_text)
end
diff --git a/lib/gitlab/hook_data/base_builder.rb b/lib/gitlab/hook_data/base_builder.rb
index 4ffca356b29..d54175bce81 100644
--- a/lib/gitlab/hook_data/base_builder.rb
+++ b/lib/gitlab/hook_data/base_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HookData
class BaseBuilder
@@ -25,6 +27,7 @@ module Gitlab
markdown_text.gsub(MARKDOWN_SIMPLE_IMAGE) do
if $~[:image]
url = $~[:url]
+ url = "#{uploads_prefix}#{url}" if url.start_with?('/uploads')
url = "/#{url}" unless url.start_with?('/')
"![#{$~[:title]}](#{Gitlab.config.gitlab.url}#{url})"
@@ -33,6 +36,16 @@ module Gitlab
end
end
end
+
+ def uploads_prefix
+ project&.full_path || ''
+ end
+
+ def project
+ return unless object.respond_to?(:project)
+
+ object.project
+ end
end
end
end
diff --git a/lib/gitlab/hook_data/issuable_builder.rb b/lib/gitlab/hook_data/issuable_builder.rb
index f2eda398b8f..0803df65632 100644
--- a/lib/gitlab/hook_data/issuable_builder.rb
+++ b/lib/gitlab/hook_data/issuable_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HookData
class IssuableBuilder < BaseBuilder
@@ -28,7 +30,7 @@ module Gitlab
end
def safe_keys
- issuable_builder::SAFE_HOOK_ATTRIBUTES + issuable_builder::SAFE_HOOK_RELATIONS
+ issuable_builder.safe_hook_attributes + issuable_builder::SAFE_HOOK_RELATIONS
end
private
diff --git a/lib/gitlab/hook_data/issue_builder.rb b/lib/gitlab/hook_data/issue_builder.rb
index 0d71c748dc6..c99353b9d49 100644
--- a/lib/gitlab/hook_data/issue_builder.rb
+++ b/lib/gitlab/hook_data/issue_builder.rb
@@ -1,50 +1,54 @@
+# frozen_string_literal: true
+
module Gitlab
module HookData
class IssueBuilder < BaseBuilder
- SAFE_HOOK_ATTRIBUTES = %i[
- assignee_id
- author_id
- closed_at
- confidential
- created_at
- description
- due_date
- id
- iid
- last_edited_at
- last_edited_by_id
- milestone_id
- moved_to_id
- project_id
- relative_position
- state
- time_estimate
- title
- updated_at
- updated_by_id
- ].freeze
-
SAFE_HOOK_RELATIONS = %i[
assignees
labels
total_time_spent
].freeze
+ def self.safe_hook_attributes
+ %i[
+ assignee_id
+ author_id
+ closed_at
+ confidential
+ created_at
+ description
+ due_date
+ id
+ iid
+ last_edited_at
+ last_edited_by_id
+ milestone_id
+ moved_to_id
+ project_id
+ relative_position
+ state
+ time_estimate
+ title
+ updated_at
+ updated_by_id
+ ].freeze
+ end
+
alias_method :issue, :object
def build
attrs = {
- description: absolute_image_urls(issue.description),
- url: Gitlab::UrlBuilder.build(issue),
- total_time_spent: issue.total_time_spent,
- human_total_time_spent: issue.human_total_time_spent,
- human_time_estimate: issue.human_time_estimate,
- assignee_ids: issue.assignee_ids,
- assignee_id: issue.assignee_ids.first # This key is deprecated
+ description: absolute_image_urls(issue.description),
+ url: Gitlab::UrlBuilder.build(issue),
+ total_time_spent: issue.total_time_spent,
+ human_total_time_spent: issue.human_total_time_spent,
+ human_time_estimate: issue.human_time_estimate,
+ assignee_ids: issue.assignee_ids,
+ assignee_id: issue.assignee_ids.first # This key is deprecated
}
- issue.attributes.with_indifferent_access.slice(*SAFE_HOOK_ATTRIBUTES)
- .merge!(attrs)
+ issue.attributes.with_indifferent_access.slice(*self.class.safe_hook_attributes)
+ .merge!(attrs)
end
end
end
diff --git a/lib/gitlab/hook_data/merge_request_builder.rb b/lib/gitlab/hook_data/merge_request_builder.rb
index dfbed0597ed..ad38e26e40a 100644
--- a/lib/gitlab/hook_data/merge_request_builder.rb
+++ b/lib/gitlab/hook_data/merge_request_builder.rb
@@ -1,33 +1,37 @@
+# frozen_string_literal: true
+
module Gitlab
module HookData
class MergeRequestBuilder < BaseBuilder
- SAFE_HOOK_ATTRIBUTES = %i[
- assignee_id
- author_id
- created_at
- description
- head_pipeline_id
- id
- iid
- last_edited_at
- last_edited_by_id
- merge_commit_sha
- merge_error
- merge_params
- merge_status
- merge_user_id
- merge_when_pipeline_succeeds
- milestone_id
- source_branch
- source_project_id
- state
- target_branch
- target_project_id
- time_estimate
- title
- updated_at
- updated_by_id
- ].freeze
+ def self.safe_hook_attributes
+ %i[
+ assignee_id
+ author_id
+ created_at
+ description
+ head_pipeline_id
+ id
+ iid
+ last_edited_at
+ last_edited_by_id
+ merge_commit_sha
+ merge_error
+ merge_params
+ merge_status
+ merge_user_id
+ merge_when_pipeline_succeeds
+ milestone_id
+ source_branch
+ source_project_id
+ state
+ target_branch
+ target_project_id
+ time_estimate
+ title
+ updated_at
+ updated_by_id
+ ].freeze
+ end
SAFE_HOOK_RELATIONS = %i[
assignee
@@ -50,8 +54,8 @@ module Gitlab
human_time_estimate: merge_request.human_time_estimate
}
- merge_request.attributes.with_indifferent_access.slice(*SAFE_HOOK_ATTRIBUTES)
- .merge!(attrs)
+ merge_request.attributes.with_indifferent_access.slice(*self.class.safe_hook_attributes)
+ .merge!(attrs)
end
end
end
diff --git a/lib/gitlab/hook_data/note_builder.rb b/lib/gitlab/hook_data/note_builder.rb
index 81873e345d5..ae30ef6364b 100644
--- a/lib/gitlab/hook_data/note_builder.rb
+++ b/lib/gitlab/hook_data/note_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HookData
class NoteBuilder < BaseBuilder
diff --git a/lib/gitlab/hook_data/wiki_page_builder.rb b/lib/gitlab/hook_data/wiki_page_builder.rb
index 59c94a61cf2..67f06b1ca46 100644
--- a/lib/gitlab/hook_data/wiki_page_builder.rb
+++ b/lib/gitlab/hook_data/wiki_page_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module HookData
class WikiPageBuilder < BaseBuilder
diff --git a/lib/gitlab/http.rb b/lib/gitlab/http.rb
index 9aca3b0fb26..bcd9e2be35f 100644
--- a/lib/gitlab/http.rb
+++ b/lib/gitlab/http.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class is used as a proxy for all outbounding http connection
# coming from callbacks, services and hooks. The direct use of the HTTParty
# is discouraged because it can lead to several security problems, like SSRF
@@ -5,9 +7,16 @@
module Gitlab
class HTTP
BlockedUrlError = Class.new(StandardError)
+ RedirectionTooDeep = Class.new(StandardError)
include HTTParty # rubocop:disable Gitlab/HTTParty
connection_adapter ProxyHTTPConnectionAdapter
+
+ def self.perform_request(http_method, path, options, &block)
+ super
+ rescue HTTParty::RedirectionTooDeep
+ raise RedirectionTooDeep
+ end
end
end
diff --git a/lib/gitlab/http_io.rb b/lib/gitlab/http_io.rb
index ce24817db54..e768b8adb12 100644
--- a/lib/gitlab/http_io.rb
+++ b/lib/gitlab/http_io.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# This class is compatible with IO class (https://ruby-doc.org/core-2.3.1/IO.html)
# source: https://gitlab.com/snippets/1685610
@@ -73,8 +75,8 @@ module Gitlab
end
end
- def read(length = nil, outbuf = "")
- out = ""
+ def read(length = nil, outbuf = nil)
+ out = []
length ||= size - tell
@@ -90,17 +92,18 @@ module Gitlab
length -= chunk_data.bytesize
end
+ out = out.join
+
# If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
if outbuf
- outbuf.slice!(0, outbuf.bytesize)
- outbuf << out
+ outbuf.replace(out)
end
out
end
def readline
- out = ""
+ out = []
until eof?
data = get_chunk
@@ -116,7 +119,7 @@ module Gitlab
end
end
- out
+ out.join
end
def write(data)
@@ -158,14 +161,14 @@ module Gitlab
##
# Note: If provider does not return content_range, then we set it as we requested
# Provider: minio
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # - When the file size is larger than requested Content-range, the Content-range is included in responses with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responses with Net::HTTPPartialContent 206
# Provider: AWS
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # - When the file size is larger than requested Content-range, the Content-range is included in responses with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responses with Net::HTTPPartialContent 206
# Provider: GCS
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPOK 200
+ # - When the file size is larger than requested Content-range, the Content-range is included in responses with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responses with Net::HTTPOK 200
@chunk_range ||= (chunk_start...(chunk_start + @chunk.bytesize))
end
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 343487bc361..7e0398f09af 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module I18n
extend self
@@ -5,6 +7,7 @@ module Gitlab
AVAILABLE_LANGUAGES = {
'en' => 'English',
'es' => 'Español',
+ 'gl_ES' => 'Galego',
'de' => 'Deutsch',
'fr' => 'Français',
'pt_BR' => 'Português (Brasil)',
@@ -22,7 +25,8 @@ module Gitlab
'tr_TR' => 'Türkçe',
'id_ID' => 'Bahasa Indonesia',
'fil_PH' => 'Filipino',
- 'pl_PL' => 'Polski'
+ 'pl_PL' => 'Polski',
+ 'cs_CZ' => 'Čeština'
}.freeze
def available_locales
diff --git a/lib/gitlab/i18n/metadata_entry.rb b/lib/gitlab/i18n/metadata_entry.rb
index 36fc1bcdcb7..3764e379681 100644
--- a/lib/gitlab/i18n/metadata_entry.rb
+++ b/lib/gitlab/i18n/metadata_entry.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module I18n
class MetadataEntry
diff --git a/lib/gitlab/i18n/po_linter.rb b/lib/gitlab/i18n/po_linter.rb
index d8e7269a2c2..644ef8c8589 100644
--- a/lib/gitlab/i18n/po_linter.rb
+++ b/lib/gitlab/i18n/po_linter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module I18n
class PoLinter
diff --git a/lib/gitlab/i18n/translation_entry.rb b/lib/gitlab/i18n/translation_entry.rb
index 54adb98f42d..19c10b2e402 100644
--- a/lib/gitlab/i18n/translation_entry.rb
+++ b/lib/gitlab/i18n/translation_entry.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module I18n
class TranslationEntry
diff --git a/lib/gitlab/identifier.rb b/lib/gitlab/identifier.rb
index 3f3f10596c5..d5f94ad04f1 100644
--- a/lib/gitlab/identifier.rb
+++ b/lib/gitlab/identifier.rb
@@ -1,11 +1,11 @@
+# frozen_string_literal: true
+
# Detect user based on identifier like
-# key-13 or user-36 or last commit
+# key-13 or user-36
module Gitlab
module Identifier
- def identify(identifier, project = nil, newrev = nil)
- if identifier.blank?
- identify_using_commit(project, newrev)
- elsif identifier =~ /\Auser-\d+\Z/
+ def identify(identifier)
+ if identifier =~ /\Auser-\d+\Z/
# git push over http
identify_using_user(identifier)
elsif identifier =~ /\Akey-\d+\Z/
@@ -14,20 +14,8 @@ module Gitlab
end
end
- # Tries to identify a user based on a commit SHA.
- def identify_using_commit(project, ref)
- return if project.nil? && ref.nil?
-
- commit = project.commit(ref)
-
- return if !commit || !commit.author_email
-
- identify_with_cache(:email, commit.author_email) do
- commit.author
- end
- end
-
# Tries to identify a user based on a user identifier (e.g. "user-123").
+ # rubocop: disable CodeReuse/ActiveRecord
def identify_using_user(identifier)
user_id = identifier.gsub("user-", "")
@@ -35,6 +23,7 @@ module Gitlab
User.find_by(id: user_id)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Tries to identify a user based on an SSH key identifier (e.g. "key-123").
def identify_using_ssh_key(identifier)
diff --git a/lib/gitlab/import/database_helpers.rb b/lib/gitlab/import/database_helpers.rb
new file mode 100644
index 00000000000..5b3f30d894a
--- /dev/null
+++ b/lib/gitlab/import/database_helpers.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Import
+ module DatabaseHelpers
+ # Inserts a raw row and returns the ID of the inserted row.
+ #
+ # attributes - The attributes/columns to set.
+ # relation - An ActiveRecord::Relation to use for finding the ID of the row
+ # when using MySQL.
+ # rubocop: disable CodeReuse/ActiveRecord
+ def insert_and_return_id(attributes, relation)
+ # We use bulk_insert here so we can bypass any queries executed by
+ # callbacks or validation rules, as doing this wouldn't scale when
+ # importing very large projects.
+ result = Gitlab::Database
+ .bulk_insert(relation.table_name, [attributes], return_ids: true)
+
+ # MySQL doesn't support returning the IDs of a bulk insert in a way that
+ # is not a pain, so in this case we'll issue an extra query instead.
+ result.first ||
+ relation.where(iid: attributes[:iid]).limit(1).pluck(:id).first
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+end
diff --git a/lib/gitlab/import/logger.rb b/lib/gitlab/import/logger.rb
new file mode 100644
index 00000000000..ab3e822a4e9
--- /dev/null
+++ b/lib/gitlab/import/logger.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Import
+ class Logger < ::Gitlab::JsonLogger
+ def self.file_name_noext
+ 'importer'
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import/merge_request_creator.rb b/lib/gitlab/import/merge_request_creator.rb
new file mode 100644
index 00000000000..a01951b0762
--- /dev/null
+++ b/lib/gitlab/import/merge_request_creator.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+# This module is designed for importers that need to create many merge
+# requests quickly. When creating merge requests there are a lot of hooks
+# that may run, for many different reasons. Many of these hooks (e.g. the ones
+# used for rendering Markdown) are completely unnecessary and may even lead to
+# transaction timeouts.
+#
+# To ensure importing merge requests requests has a minimal impact and can
+# complete in a reasonable time we bypass all the hooks by inserting the row
+# and then retrieving it. We then only perform the additional work that is
+# strictly necessary.
+module Gitlab
+ module Import
+ class MergeRequestCreator
+ include ::Gitlab::Import::DatabaseHelpers
+ include ::Gitlab::Import::MergeRequestHelpers
+
+ attr_accessor :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def execute(attributes)
+ source_branch_sha = attributes.delete(:source_branch_sha)
+ target_branch_sha = attributes.delete(:target_branch_sha)
+ iid = attributes[:iid]
+
+ merge_request, already_exists = create_merge_request_without_hooks(project, attributes, iid)
+
+ if merge_request
+ insert_or_replace_git_data(merge_request, source_branch_sha, target_branch_sha, already_exists)
+ end
+
+ merge_request
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import/merge_request_helpers.rb b/lib/gitlab/import/merge_request_helpers.rb
new file mode 100644
index 00000000000..9215067d973
--- /dev/null
+++ b/lib/gitlab/import/merge_request_helpers.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Import
+ module MergeRequestHelpers
+ include DatabaseHelpers
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def create_merge_request_without_hooks(project, attributes, iid)
+ # This work must be wrapped in a transaction as otherwise we can leave
+ # behind incomplete data in the event of an error. This can then lead
+ # to duplicate key errors when jobs are retried.
+ MergeRequest.transaction do
+ # When creating merge requests there are a lot of hooks that may
+ # run, for many different reasons. Many of these hooks (e.g. the
+ # ones used for rendering Markdown) are completely unnecessary and
+ # may even lead to transaction timeouts.
+ #
+ # To ensure importing pull requests has a minimal impact and can
+ # complete in a reasonable time we bypass all the hooks by inserting
+ # the row and then retrieving it. We then only perform the
+ # additional work that is strictly necessary.
+ merge_request_id = insert_and_return_id(attributes, project.merge_requests)
+
+ merge_request = project.merge_requests.reload.find(merge_request_id)
+
+ # We use .insert_and_return_id which effectively disables all callbacks.
+ # Trigger iid logic here to make sure we track internal id values consistently.
+ merge_request.ensure_target_project_iid!
+
+ [merge_request, false]
+ end
+ rescue ActiveRecord::InvalidForeignKey
+ # It's possible the project has been deleted since scheduling this
+ # job. In this case we'll just skip creating the merge request.
+ []
+ rescue ActiveRecord::RecordNotUnique
+ # It's possible we previously created the MR, but failed when updating
+ # the Git data. In this case we'll just continue working on the
+ # existing row.
+ [project.merge_requests.find_by(iid: iid), true]
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def insert_or_replace_git_data(merge_request, source_branch_sha, target_branch_sha, already_exists = false)
+ # These fields are set so we can create the correct merge request
+ # diffs.
+ merge_request.source_branch_sha = source_branch_sha
+ merge_request.target_branch_sha = target_branch_sha
+
+ merge_request.keep_around_commit
+
+ # MR diffs normally use an "after_save" hook to pull data from Git.
+ # All of this happens in the transaction started by calling
+ # create/save/etc. This in turn can lead to these transactions being
+ # held open for much longer than necessary. To work around this we
+ # first save the diff, then populate it.
+ diff =
+ if already_exists
+ merge_request.merge_request_diffs.take ||
+ merge_request.merge_request_diffs.build
+ else
+ merge_request.merge_request_diffs.build
+ end
+
+ diff.importing = true
+ diff.save
+ diff.save_git_content
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+end
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index be3710c5b7f..f63a5ece71e 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
extend self
@@ -40,10 +42,6 @@ module Gitlab
"#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
end
- def object_storage?
- Feature.enabled?(:import_export_object_storage)
- end
-
def version
VERSION
end
diff --git a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
index 83134bb0769..d39b6fe5955 100644
--- a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
module AfterExportStrategies
@@ -53,7 +55,7 @@ module Gitlab
end
def self.lock_file_path(project)
- return unless project.export_path || object_storage?
+ return unless project.export_path || export_file_exists?
lock_path = project.import_export_shared.archive_path
@@ -83,8 +85,8 @@ module Gitlab
errors.full_messages.each { |msg| project.import_export_shared.add_error_message(msg) }
end
- def object_storage?
- project.export_project_object_exists?
+ def export_file_exists?
+ project.export_file_exists?
end
end
end
diff --git a/lib/gitlab/import_export/after_export_strategies/download_notification_strategy.rb b/lib/gitlab/import_export/after_export_strategies/download_notification_strategy.rb
index 4371a7eff56..1b391314a74 100644
--- a/lib/gitlab/import_export/after_export_strategies/download_notification_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/download_notification_strategy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
module AfterExportStrategies
diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
index dce8f89c0ab..b30900f7c61 100644
--- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
module AfterExportStrategies
@@ -23,7 +25,7 @@ module Gitlab
def strategy_execute
handle_response_error(send_file)
- project.remove_exported_project_file
+ project.remove_exports
end
def handle_response_error(response)
@@ -40,15 +42,11 @@ module Gitlab
def send_file
Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options) # rubocop:disable GitlabSecurity/PublicSend
ensure
- export_file.close if export_file && !object_storage?
+ export_file.close if export_file
end
def export_file
- if object_storage?
- project.import_export_upload.export_file.file.open
- else
- File.open(project.export_project_path)
- end
+ project.export_file.open
end
def send_file_options
@@ -63,11 +61,7 @@ module Gitlab
end
def export_size
- if object_storage?
- project.import_export_upload.export_file.file.size
- else
- File.size(project.export_project_path)
- end
+ project.export_file.file.size
end
end
end
diff --git a/lib/gitlab/import_export/after_export_strategy_builder.rb b/lib/gitlab/import_export/after_export_strategy_builder.rb
index 7eabcae2380..37394f46a99 100644
--- a/lib/gitlab/import_export/after_export_strategy_builder.rb
+++ b/lib/gitlab/import_export/after_export_strategy_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class AfterExportStrategyBuilder
diff --git a/lib/gitlab/import_export/attribute_cleaner.rb b/lib/gitlab/import_export/attribute_cleaner.rb
index 7c9fc5c15bb..93b37b7bc5f 100644
--- a/lib/gitlab/import_export/attribute_cleaner.rb
+++ b/lib/gitlab/import_export/attribute_cleaner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class AttributeCleaner
diff --git a/lib/gitlab/import_export/attributes_finder.rb b/lib/gitlab/import_export/attributes_finder.rb
index 0c8fda07294..409243e68a5 100644
--- a/lib/gitlab/import_export/attributes_finder.rb
+++ b/lib/gitlab/import_export/attributes_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class AttributesFinder
diff --git a/lib/gitlab/import_export/avatar_restorer.rb b/lib/gitlab/import_export/avatar_restorer.rb
index cfa595629f4..be1b97bd7a7 100644
--- a/lib/gitlab/import_export/avatar_restorer.rb
+++ b/lib/gitlab/import_export/avatar_restorer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class AvatarRestorer
@@ -19,7 +21,7 @@ module Gitlab
private
def avatar_export_file
- @avatar_export_file ||= Dir["#{avatar_export_path}/*"].first
+ @avatar_export_file ||= Dir["#{avatar_export_path}/**/*"].find { |f| File.file?(f) }
end
def avatar_export_path
diff --git a/lib/gitlab/import_export/avatar_saver.rb b/lib/gitlab/import_export/avatar_saver.rb
index 31ef0490cb3..47ca898c690 100644
--- a/lib/gitlab/import_export/avatar_saver.rb
+++ b/lib/gitlab/import_export/avatar_saver.rb
@@ -1,8 +1,8 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class AvatarSaver
- include Gitlab::ImportExport::CommandLineUtil
-
def initialize(project:, shared:)
@project = project
@shared = shared
@@ -14,19 +14,12 @@ module Gitlab
Gitlab::ImportExport::UploadsManager.new(
project: @project,
shared: @shared,
- relative_export_path: 'avatar',
- from: avatar_path
+ relative_export_path: 'avatar'
).save
rescue => e
@shared.error(e)
false
end
-
- private
-
- def avatar_path
- @project.avatar.path
- end
end
end
end
diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb
index 3adc44f8044..c9e2a6a78d9 100644
--- a/lib/gitlab/import_export/command_line_util.rb
+++ b/lib/gitlab/import_export/command_line_util.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
module CommandLineUtil
diff --git a/lib/gitlab/import_export/error.rb b/lib/gitlab/import_export/error.rb
index 788eedf2686..454dc778b6b 100644
--- a/lib/gitlab/import_export/error.rb
+++ b/lib/gitlab/import_export/error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
Error = Class.new(StandardError)
diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb
index 7fd66b4e244..05432f433e7 100644
--- a/lib/gitlab/import_export/file_importer.rb
+++ b/lib/gitlab/import_export/file_importer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class FileImporter
diff --git a/lib/gitlab/import_export/group_project_object_builder.rb b/lib/gitlab/import_export/group_project_object_builder.rb
index 6c2af770119..1c62591ed5a 100644
--- a/lib/gitlab/import_export/group_project_object_builder.rb
+++ b/lib/gitlab/import_export/group_project_object_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
# Given a class, it finds or creates a new object
diff --git a/lib/gitlab/import_export/hash_util.rb b/lib/gitlab/import_export/hash_util.rb
index d4adeeb3797..b6ce89a973b 100644
--- a/lib/gitlab/import_export/hash_util.rb
+++ b/lib/gitlab/import_export/hash_util.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class HashUtil
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index f69f98a78a3..b40eac3de9a 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -19,6 +19,9 @@ project_tree:
- milestone:
- events:
- :push_event_payload
+ - resource_label_events:
+ - label:
+ :priorities
- :issue_assignees
- snippets:
- :award_emoji
@@ -45,6 +48,9 @@ project_tree:
- milestone:
- events:
- :push_event_payload
+ - resource_label_events:
+ - label:
+ :priorities
- pipelines:
- notes:
- :author
@@ -64,6 +70,7 @@ project_tree:
- :create_access_levels
- :project_feature
- :custom_attributes
+ - :prometheus_metrics
- :project_badges
- :ci_cd_settings
@@ -85,6 +92,7 @@ excluded_attributes:
- :path
- :namespace_id
- :creator_id
+ - :pool_repository_id
- :import_url
- :import_status
- :avatar
@@ -108,6 +116,9 @@ excluded_attributes:
- :remote_mirror_available_overridden
- :description_html
- :repository_languages
+ prometheus_metrics:
+ - :common
+ - :identifier
snippets:
- :expired_at
merge_request_diff:
@@ -133,6 +144,18 @@ excluded_attributes:
- :event_id
project_badges:
- :group_id
+ resource_label_events:
+ - :reference
+ - :reference_html
+ - :epic_id
+ hooks:
+ - :token
+ - :encrypted_token
+ - :encrypted_token_iv
+ - :encrypted_url
+ - :encrypted_url_iv
+ services:
+ - :template
methods:
labels:
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index 4e179f63d8c..767f1b5de0e 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class Importer
@@ -92,8 +94,6 @@ module Gitlab
end
def remove_import_file
- return unless Gitlab::ImportExport.object_storage?
-
upload = @project.import_export_upload
return unless upload&.import_file&.file
diff --git a/lib/gitlab/import_export/json_hash_builder.rb b/lib/gitlab/import_export/json_hash_builder.rb
index b48f63bcd7e..477499e1688 100644
--- a/lib/gitlab/import_export/json_hash_builder.rb
+++ b/lib/gitlab/import_export/json_hash_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
# Generates a hash that conforms with http://apidock.com/rails/Hash/to_json
diff --git a/lib/gitlab/import_export/lfs_restorer.rb b/lib/gitlab/import_export/lfs_restorer.rb
index b28c3c161b7..345c7880e30 100644
--- a/lib/gitlab/import_export/lfs_restorer.rb
+++ b/lib/gitlab/import_export/lfs_restorer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class LfsRestorer
diff --git a/lib/gitlab/import_export/lfs_saver.rb b/lib/gitlab/import_export/lfs_saver.rb
index 29410e2331c..954f6f00078 100644
--- a/lib/gitlab/import_export/lfs_saver.rb
+++ b/lib/gitlab/import_export/lfs_saver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class LfsSaver
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index ac827cbe1ca..6be95a16513 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class MembersMapper
@@ -45,7 +47,7 @@ module Gitlab
end
def ensure_default_member!
- @project.project_members.destroy_all
+ @project.project_members.destroy_all # rubocop: disable DestroyAll
ProjectMember.create!(user: @user, access_level: ProjectMember::MAINTAINER, source_id: @project.id, importing: true)
end
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
index 62a1833b39c..040a70d6775 100644
--- a/lib/gitlab/import_export/merge_request_parser.rb
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class MergeRequestParser
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index 76b99b1de16..8cd4efd91cc 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class ProjectTreeRestorer
@@ -94,7 +96,10 @@ module Gitlab
end
def restore_project
- @project.update_columns(project_params)
+ Gitlab::Timeless.timeless(@project) do
+ @project.update(project_params)
+ end
+
@project
end
@@ -133,16 +138,25 @@ module Gitlab
return if tree_hash[relation_key].blank?
tree_array = [tree_hash[relation_key]].flatten
+ null_iid_pipelines = []
# Avoid keeping a possible heavy object in memory once we are done with it
- while relation_item = tree_array.shift
+ while relation_item = (tree_array.shift || null_iid_pipelines.shift)
+ if nil_iid_pipeline?(relation_key, relation_item) && tree_array.any?
+ # Move pipelines with NULL IIDs to the end
+ # so they don't clash with existing IIDs.
+ null_iid_pipelines << relation_item
+
+ next
+ end
+
# The transaction at this level is less speedy than one single transaction
# But we can't have it in the upper level or GC won't get rid of the AR objects
# after we save the batch.
Project.transaction do
process_sub_relation(relation, relation_item)
- # For every subrelation that hangs from Project, save the associated records alltogether
+ # For every subrelation that hangs from Project, save the associated records altogether
# This effectively batches all records per subrelation item, only keeping those in memory
# We have to keep in mind that more batch granularity << Memory, but >> Slowness
if save
@@ -196,7 +210,11 @@ module Gitlab
end
def excluded_keys_for_relation(relation)
- @reader.attributes_finder.find_excluded_keys(relation)
+ reader.attributes_finder.find_excluded_keys(relation)
+ end
+
+ def nil_iid_pipeline?(relation_key, relation_item)
+ relation_key == 'pipelines' && relation_item['iid'].nil?
end
end
end
diff --git a/lib/gitlab/import_export/project_tree_saver.rb b/lib/gitlab/import_export/project_tree_saver.rb
index 5510c0b8b2f..29f2dc80813 100644
--- a/lib/gitlab/import_export/project_tree_saver.rb
+++ b/lib/gitlab/import_export/project_tree_saver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class ProjectTreeSaver
diff --git a/lib/gitlab/import_export/reader.rb b/lib/gitlab/import_export/reader.rb
index e621c40fc7a..bc0d18e03fa 100644
--- a/lib/gitlab/import_export/reader.rb
+++ b/lib/gitlab/import_export/reader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class Reader
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 81807ed659c..097c7653754 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class RelationFactory
@@ -86,7 +88,6 @@ module Gitlab
case @relation_name
when :merge_request_diff_files then setup_diff
when :notes then setup_note
- when 'Ci::Pipeline' then setup_pipeline
end
update_user_references
@@ -94,6 +95,8 @@ module Gitlab
update_group_references
remove_duplicate_assignees
+ setup_pipeline if @relation_name == 'Ci::Pipeline'
+
reset_tokens!
remove_encrypted_attributes!
end
@@ -210,7 +213,7 @@ module Gitlab
def update_note_for_missing_author(author_name)
@relation_hash['note'] = '*Blank note*' if @relation_hash['note'].blank?
- @relation_hash['note'] += missing_author_note(@relation_hash['updated_at'], author_name)
+ @relation_hash['note'] = "#{@relation_hash['note']}#{missing_author_note(@relation_hash['updated_at'], author_name)}"
end
def admin_user?
diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb
index 5a9bbceac67..921a06b4023 100644
--- a/lib/gitlab/import_export/repo_restorer.rb
+++ b/lib/gitlab/import_export/repo_restorer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class RepoRestorer
diff --git a/lib/gitlab/import_export/repo_saver.rb b/lib/gitlab/import_export/repo_saver.rb
index 0c224bd1971..a60618dfcec 100644
--- a/lib/gitlab/import_export/repo_saver.rb
+++ b/lib/gitlab/import_export/repo_saver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class RepoSaver
diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb
index 3cd153a4fd2..72f575db095 100644
--- a/lib/gitlab/import_export/saver.rb
+++ b/lib/gitlab/import_export/saver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class Saver
@@ -18,7 +20,7 @@ module Gitlab
Rails.logger.info("Saved project export #{archive_file}")
- save_on_object_storage if use_object_storage?
+ save_upload
else
@shared.error(Gitlab::ImportExport::Error.new(error_message))
false
@@ -27,10 +29,8 @@ module Gitlab
@shared.error(e)
false
ensure
- if use_object_storage?
- remove_archive
- remove_export_path
- end
+ remove_archive
+ remove_export_path
end
private
@@ -51,7 +51,7 @@ module Gitlab
@archive_file ||= File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project))
end
- def save_on_object_storage
+ def save_upload
upload = ImportExportUpload.find_or_initialize_by(project: @project)
File.open(archive_file) { |file| upload.export_file = file }
@@ -59,12 +59,8 @@ module Gitlab
upload.save!
end
- def use_object_storage?
- Gitlab::ImportExport.object_storage?
- end
-
def error_message
- "Unable to save #{archive_file} into #{@shared.export_path}. Object storage enabled: #{use_object_storage?}"
+ "Unable to save #{archive_file} into #{@shared.export_path}."
end
end
end
diff --git a/lib/gitlab/import_export/shared.rb b/lib/gitlab/import_export/shared.rb
index 6d7c36ce38b..c13e6c1d83b 100644
--- a/lib/gitlab/import_export/shared.rb
+++ b/lib/gitlab/import_export/shared.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class Shared
diff --git a/lib/gitlab/import_export/statistics_restorer.rb b/lib/gitlab/import_export/statistics_restorer.rb
index bcdd9c12c85..3fafb01c37c 100644
--- a/lib/gitlab/import_export/statistics_restorer.rb
+++ b/lib/gitlab/import_export/statistics_restorer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class StatisticsRestorer
diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb
index 07875ebb56a..474e9d45566 100644
--- a/lib/gitlab/import_export/uploads_manager.rb
+++ b/lib/gitlab/import_export/uploads_manager.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class UploadsManager
@@ -5,21 +7,14 @@ module Gitlab
UPLOADS_BATCH_SIZE = 100
- def initialize(project:, shared:, relative_export_path: 'uploads', from: nil)
+ def initialize(project:, shared:, relative_export_path: 'uploads')
@project = project
@shared = shared
@relative_export_path = relative_export_path
- @from = from || default_uploads_path
end
def save
- copy_files(@from, uploads_export_path) if File.directory?(@from)
-
- if File.file?(@from) && @relative_export_path == 'avatar'
- copy_files(@from, File.join(uploads_export_path, @project.avatar.filename))
- end
-
- copy_from_object_storage
+ copy_project_uploads
true
rescue => e
@@ -48,19 +43,18 @@ module Gitlab
UploadService.new(@project, File.open(upload, 'r'), FileUploader, uploader_context).execute
end
- def copy_from_object_storage
- return unless Gitlab::ImportExport.object_storage?
-
+ def copy_project_uploads
each_uploader do |uploader|
next unless uploader.file
- next if uploader.upload.local? # Already copied, using the old method
- download_and_copy(uploader)
- end
- end
+ if uploader.upload.local?
+ next unless uploader.upload.exist?
- def default_uploads_path
- FileUploader.absolute_base_dir(@project)
+ copy_files(uploader.absolute_path, File.join(uploads_export_path, uploader.upload.path))
+ else
+ download_and_copy(uploader)
+ end
+ end
end
def uploads_export_path
diff --git a/lib/gitlab/import_export/uploads_restorer.rb b/lib/gitlab/import_export/uploads_restorer.rb
index 25f85936227..5f422dcbefa 100644
--- a/lib/gitlab/import_export/uploads_restorer.rb
+++ b/lib/gitlab/import_export/uploads_restorer.rb
@@ -1,31 +1,17 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class UploadsRestorer < UploadsSaver
def restore
- if Gitlab::ImportExport.object_storage?
- Gitlab::ImportExport::UploadsManager.new(
- project: @project,
- shared: @shared
- ).restore
- elsif File.directory?(uploads_export_path)
- copy_files(uploads_export_path, uploads_path)
-
- true
- else
- true # Proceed without uploads
- end
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared
+ ).restore
rescue => e
@shared.error(e)
false
end
-
- def uploads_path
- FileUploader.absolute_base_dir(@project)
- end
-
- def uploads_export_path
- @uploads_export_path ||= File.join(@shared.export_path, 'uploads')
- end
end
end
end
diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb
index b3f17af5661..be1066c30b2 100644
--- a/lib/gitlab/import_export/uploads_saver.rb
+++ b/lib/gitlab/import_export/uploads_saver.rb
@@ -1,8 +1,8 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class UploadsSaver
- include Gitlab::ImportExport::CommandLineUtil
-
def initialize(project:, shared:)
@project = project
@shared = shared
diff --git a/lib/gitlab/import_export/version_checker.rb b/lib/gitlab/import_export/version_checker.rb
index bd3c3ee3b2f..6d978d00ea5 100644
--- a/lib/gitlab/import_export/version_checker.rb
+++ b/lib/gitlab/import_export/version_checker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class VersionChecker
diff --git a/lib/gitlab/import_export/version_saver.rb b/lib/gitlab/import_export/version_saver.rb
index 7cf88298642..8230c0f1e77 100644
--- a/lib/gitlab/import_export/version_saver.rb
+++ b/lib/gitlab/import_export/version_saver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class VersionSaver
diff --git a/lib/gitlab/import_export/wiki_repo_saver.rb b/lib/gitlab/import_export/wiki_repo_saver.rb
index 2fd62c0fc7b..7303bcf61a4 100644
--- a/lib/gitlab/import_export/wiki_repo_saver.rb
+++ b/lib/gitlab/import_export/wiki_repo_saver.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class WikiRepoSaver < RepoSaver
diff --git a/lib/gitlab/import_export/wiki_restorer.rb b/lib/gitlab/import_export/wiki_restorer.rb
index f33bfb332ab..28b5e7449cd 100644
--- a/lib/gitlab/import_export/wiki_restorer.rb
+++ b/lib/gitlab/import_export/wiki_restorer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ImportExport
class WikiRestorer < RepoRestorer
diff --git a/lib/gitlab/import_formatter.rb b/lib/gitlab/import_formatter.rb
index 4e611e7f16c..d4ba4d1181d 100644
--- a/lib/gitlab/import_formatter.rb
+++ b/lib/gitlab/import_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class ImportFormatter
def comment(author, date, body)
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index f7f5c5787f6..f46bb837cf7 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitlab::ImportSources module
#
# Define import sources that can be used
diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb
index d323cb9dadf..20fc8226611 100644
--- a/lib/gitlab/incoming_email.rb
+++ b/lib/gitlab/incoming_email.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module IncomingEmail
UNSUBSCRIBE_SUFFIX = '+unsubscribe'.freeze
diff --git a/lib/gitlab/insecure_key_fingerprint.rb b/lib/gitlab/insecure_key_fingerprint.rb
index f85b6e9197f..e4f0e9d2c73 100644
--- a/lib/gitlab/insecure_key_fingerprint.rb
+++ b/lib/gitlab/insecure_key_fingerprint.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
#
# Calculates the fingerprint of a given key without using
diff --git a/lib/gitlab/issuable_metadata.rb b/lib/gitlab/issuable_metadata.rb
index 0c9de72329c..351d15605e0 100644
--- a/lib/gitlab/issuable_metadata.rb
+++ b/lib/gitlab/issuable_metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module IssuableMetadata
def issuable_meta_data(issuable_collection, collection_type)
diff --git a/lib/gitlab/issuable_sorter.rb b/lib/gitlab/issuable_sorter.rb
index d392214867a..42bbfb32d0b 100644
--- a/lib/gitlab/issuable_sorter.rb
+++ b/lib/gitlab/issuable_sorter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module IssuableSorter
class << self
diff --git a/lib/gitlab/issuables_count_for_state.rb b/lib/gitlab/issuables_count_for_state.rb
index 505810964bc..659fb1472d2 100644
--- a/lib/gitlab/issuables_count_for_state.rb
+++ b/lib/gitlab/issuables_count_for_state.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module Gitlab
# Class for counting and caching the number of issuables per state.
class IssuablesCountForState
- # The name of the RequestStore cache key.
+ # The name of the Gitlab::SafeRequestStore cache key.
CACHE_KEY = :issuables_count_for_state
# The state values that can be safely casted to a Symbol.
@@ -10,12 +12,7 @@ module Gitlab
# finder - The finder class to use for retrieving the issuables.
def initialize(finder)
@finder = finder
- @cache =
- if RequestStore.active?
- RequestStore[CACHE_KEY] ||= initialize_cache
- else
- initialize_cache
- end
+ @cache = Gitlab::SafeRequestStore[CACHE_KEY] ||= initialize_cache
end
def for_state_or_opened(state = nil)
diff --git a/lib/gitlab/issues_labels.rb b/lib/gitlab/issues_labels.rb
index b8ca7f2f55f..17c9cb969df 100644
--- a/lib/gitlab/issues_labels.rb
+++ b/lib/gitlab/issues_labels.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class IssuesLabels
class << self
diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb
index f7a8eae0be4..e97e961771c 100644
--- a/lib/gitlab/job_waiter.rb
+++ b/lib/gitlab/job_waiter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# JobWaiter can be used to wait for a number of Sidekiq jobs to complete.
#
diff --git a/lib/gitlab/json_logger.rb b/lib/gitlab/json_logger.rb
index 28e258196ca..3bff77731f6 100644
--- a/lib/gitlab/json_logger.rb
+++ b/lib/gitlab/json_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class JsonLogger < ::Gitlab::Logger
def self.file_name_noext
diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb
index 15c5ece2350..3748fd6b5ef 100644
--- a/lib/gitlab/kubernetes.rb
+++ b/lib/gitlab/kubernetes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# Helper methods to do with Kubernetes network services & resources
module Kubernetes
diff --git a/lib/gitlab/kubernetes/cluster_role_binding.rb b/lib/gitlab/kubernetes/cluster_role_binding.rb
new file mode 100644
index 00000000000..ebea8aff5be
--- /dev/null
+++ b/lib/gitlab/kubernetes/cluster_role_binding.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class ClusterRoleBinding
+ attr_reader :name, :cluster_role_name, :subjects
+
+ def initialize(name, cluster_role_name, subjects)
+ @name = name
+ @cluster_role_name = cluster_role_name
+ @subjects = subjects
+ end
+
+ def generate
+ ::Kubeclient::Resource.new.tap do |resource|
+ resource.metadata = metadata
+ resource.roleRef = role_ref
+ resource.subjects = subjects
+ end
+ end
+
+ private
+
+ def metadata
+ { name: name }
+ end
+
+ def role_ref
+ {
+ apiGroup: 'rbac.authorization.k8s.io',
+ kind: 'ClusterRole',
+ name: cluster_role_name
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/config_map.rb b/lib/gitlab/kubernetes/config_map.rb
index 9e55dae137c..0bcaaa03974 100644
--- a/lib/gitlab/kubernetes/config_map.rb
+++ b/lib/gitlab/kubernetes/config_map.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
class ConfigMap
diff --git a/lib/gitlab/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb
index 530ccf88053..03d38ec78fd 100644
--- a/lib/gitlab/kubernetes/helm.rb
+++ b/lib/gitlab/kubernetes/helm.rb
@@ -1,8 +1,14 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
module Helm
- HELM_VERSION = '2.7.2'.freeze
+ HELM_VERSION = '2.11.0'.freeze
+ KUBECTL_VERSION = '1.11.0'.freeze
NAMESPACE = 'gitlab-managed-apps'.freeze
+ SERVICE_ACCOUNT = 'tiller'.freeze
+ CLUSTER_ROLE_BINDING = 'tiller-admin'.freeze
+ CLUSTER_ROLE = 'cluster-admin'.freeze
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb
index d65374cc23b..fd3d187cbc3 100644
--- a/lib/gitlab/kubernetes/helm/api.rb
+++ b/lib/gitlab/kubernetes/helm/api.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
module Helm
@@ -9,7 +11,17 @@ module Gitlab
def install(command)
namespace.ensure_exists!
+
+ create_service_account(command)
+ create_cluster_role_binding(command)
create_config_map(command)
+
+ kubeclient.create_pod(command.pod_resource)
+ end
+
+ def update(command)
+ namespace.ensure_exists!
+ update_config_map(command)
kubeclient.create_pod(command.pod_resource)
end
@@ -32,15 +44,73 @@ module Gitlab
kubeclient.delete_pod(pod_name, namespace.name)
end
+ def get_config_map(config_map_name)
+ namespace.ensure_exists!
+
+ kubeclient.get_config_map(config_map_name, namespace.name)
+ end
+
private
attr_reader :kubeclient, :namespace
def create_config_map(command)
command.config_map_resource.tap do |config_map_resource|
- kubeclient.create_config_map(config_map_resource)
+ if config_map_exists?(config_map_resource)
+ kubeclient.update_config_map(config_map_resource)
+ else
+ kubeclient.create_config_map(config_map_resource)
+ end
+ end
+ end
+
+ def update_config_map(command)
+ command.config_map_resource.tap do |config_map_resource|
+ kubeclient.update_config_map(config_map_resource)
end
end
+
+ def create_service_account(command)
+ command.service_account_resource.tap do |service_account_resource|
+ break unless service_account_resource
+
+ if service_account_exists?(service_account_resource)
+ kubeclient.update_service_account(service_account_resource)
+ else
+ kubeclient.create_service_account(service_account_resource)
+ end
+ end
+ end
+
+ def create_cluster_role_binding(command)
+ command.cluster_role_binding_resource.tap do |cluster_role_binding_resource|
+ break unless cluster_role_binding_resource
+
+ if cluster_role_binding_exists?(cluster_role_binding_resource)
+ kubeclient.update_cluster_role_binding(cluster_role_binding_resource)
+ else
+ kubeclient.create_cluster_role_binding(cluster_role_binding_resource)
+ end
+ end
+ end
+
+ def config_map_exists?(resource)
+ kubeclient.get_config_map(resource.metadata.name, resource.metadata.namespace)
+ rescue ::Kubeclient::ResourceNotFoundError
+ false
+ end
+
+ def service_account_exists?(resource)
+ kubeclient.get_service_account(resource.metadata.name, resource.metadata.namespace)
+ rescue ::Kubeclient::ResourceNotFoundError
+ false
+ end
+
+ def cluster_role_binding_exists?(resource)
+ kubeclient.get_cluster_role_binding(resource.metadata.name)
+ rescue ::Kubeclient::ResourceNotFoundError
+ false
+ end
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/base_command.rb b/lib/gitlab/kubernetes/helm/base_command.rb
index afcfd109de0..2bcb428b25d 100644
--- a/lib/gitlab/kubernetes/helm/base_command.rb
+++ b/lib/gitlab/kubernetes/helm/base_command.rb
@@ -1,20 +1,18 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
module Helm
module BaseCommand
def pod_resource
- Gitlab::Kubernetes::Helm::Pod.new(self, namespace).generate
+ pod_service_account_name = rbac? ? service_account_name : nil
+
+ Gitlab::Kubernetes::Helm::Pod.new(self, namespace, service_account_name: pod_service_account_name).generate
end
def generate_script
<<~HEREDOC
- set -eo pipefail
- ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
- echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
- echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
- apk add -U wget ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{Gitlab::Kubernetes::Helm::HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
+ set -xeo pipefail
HEREDOC
end
@@ -26,6 +24,14 @@ module Gitlab
Gitlab::Kubernetes::ConfigMap.new(name, files).generate
end
+ def service_account_resource
+ nil
+ end
+
+ def cluster_role_binding_resource
+ nil
+ end
+
def file_names
files.keys
end
@@ -34,6 +40,10 @@ module Gitlab
raise "Not implemented"
end
+ def rbac?
+ raise "Not implemented"
+ end
+
def files
raise "Not implemented"
end
@@ -47,6 +57,10 @@ module Gitlab
def namespace
Gitlab::Kubernetes::Helm::NAMESPACE
end
+
+ def service_account_name
+ Gitlab::Kubernetes::Helm::SERVICE_ACCOUNT
+ end
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/client_command.rb b/lib/gitlab/kubernetes/helm/client_command.rb
new file mode 100644
index 00000000000..9940272a8bf
--- /dev/null
+++ b/lib/gitlab/kubernetes/helm/client_command.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ module Helm
+ module ClientCommand
+ def init_command
+ # Here we are always upgrading to the latest version of Tiller when
+ # installing an app. We ensure the helm version stored in the
+ # database is correct by also updating this after transition to
+ # :installed,:updated in Clusters::Concerns::ApplicationStatus
+ 'helm init --upgrade'
+ end
+
+ def wait_for_tiller_command
+ # This is necessary to give Tiller time to restart after upgrade.
+ # Ideally we'd be able to use --wait but cannot because of
+ # https://github.com/helm/helm/issues/4855
+ 'for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done'
+ end
+
+ def repository_command
+ ['helm', 'repo', 'add', name, repository].shelljoin if repository
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/helm/init_command.rb b/lib/gitlab/kubernetes/helm/init_command.rb
index a4546509515..88ed8572ffc 100644
--- a/lib/gitlab/kubernetes/helm/init_command.rb
+++ b/lib/gitlab/kubernetes/helm/init_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
module Helm
@@ -6,9 +8,10 @@ module Gitlab
attr_reader :name, :files
- def initialize(name:, files:)
+ def initialize(name:, files:, rbac:)
@name = name
@files = files
+ @rbac = rbac
end
def generate_script
@@ -17,15 +20,62 @@ module Gitlab
].join("\n")
end
+ def rbac?
+ @rbac
+ end
+
+ def service_account_resource
+ return unless rbac?
+
+ Gitlab::Kubernetes::ServiceAccount.new(service_account_name, namespace).generate
+ end
+
+ def cluster_role_binding_resource
+ return unless rbac?
+
+ subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: namespace }]
+
+ Gitlab::Kubernetes::ClusterRoleBinding.new(
+ cluster_role_binding_name,
+ cluster_role_name,
+ subjects
+ ).generate
+ end
+
private
def init_helm_command
- tls_flags = "--tiller-tls" \
- " --tiller-tls-verify --tls-ca-cert #{files_dir}/ca.pem" \
- " --tiller-tls-cert #{files_dir}/cert.pem" \
- " --tiller-tls-key #{files_dir}/key.pem"
+ command = %w[helm init] + init_command_flags
+
+ command.shelljoin
+ end
+
+ def init_command_flags
+ tls_flags + optional_service_account_flag
+ end
+
+ def tls_flags
+ [
+ '--tiller-tls',
+ '--tiller-tls-verify',
+ '--tls-ca-cert', "#{files_dir}/ca.pem",
+ '--tiller-tls-cert', "#{files_dir}/cert.pem",
+ '--tiller-tls-key', "#{files_dir}/key.pem"
+ ]
+ end
+
+ def optional_service_account_flag
+ return [] unless rbac?
+
+ ['--service-account', service_account_name]
+ end
+
+ def cluster_role_binding_name
+ Gitlab::Kubernetes::Helm::CLUSTER_ROLE_BINDING
+ end
- "helm init #{tls_flags} >/dev/null"
+ def cluster_role_name
+ Gitlab::Kubernetes::Helm::CLUSTER_ROLE
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/install_command.rb b/lib/gitlab/kubernetes/helm/install_command.rb
index 9672f80687e..961485005f7 100644
--- a/lib/gitlab/kubernetes/helm/install_command.rb
+++ b/lib/gitlab/kubernetes/helm/install_command.rb
@@ -1,56 +1,97 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
module Helm
class InstallCommand
include BaseCommand
+ include ClientCommand
- attr_reader :name, :files, :chart, :version, :repository
+ attr_reader :name, :files, :chart, :version, :repository, :preinstall, :postinstall
- def initialize(name:, chart:, files:, version: nil, repository: nil)
+ def initialize(name:, chart:, files:, rbac:, version: nil, repository: nil, preinstall: nil, postinstall: nil)
@name = name
@chart = chart
@version = version
+ @rbac = rbac
@files = files
@repository = repository
+ @preinstall = preinstall
+ @postinstall = postinstall
end
def generate_script
super + [
init_command,
+ wait_for_tiller_command,
repository_command,
- script_command
+ repository_update_command,
+ preinstall_command,
+ install_command,
+ postinstall_command
].compact.join("\n")
end
+ def rbac?
+ @rbac
+ end
+
private
- def init_command
- 'helm init --client-only >/dev/null'
+ def repository_update_command
+ 'helm repo update' if repository
+ end
+
+ def install_command
+ command = ['helm', 'install', chart] + install_command_flags
+
+ command.shelljoin
end
- def repository_command
- "helm repo add #{name} #{repository}" if repository
+ def preinstall_command
+ preinstall.join("\n") if preinstall
end
- def script_command
- init_flags = "--name #{name}#{optional_tls_flags}#{optional_version_flag}" \
- " --namespace #{Gitlab::Kubernetes::Helm::NAMESPACE}" \
- " -f /data/helm/#{name}/config/values.yaml"
+ def postinstall_command
+ postinstall.join("\n") if postinstall
+ end
+
+ def install_command_flags
+ name_flag = ['--name', name]
+ namespace_flag = ['--namespace', Gitlab::Kubernetes::Helm::NAMESPACE]
+ value_flag = ['-f', "/data/helm/#{name}/config/values.yaml"]
+
+ name_flag +
+ optional_tls_flags +
+ optional_version_flag +
+ optional_rbac_create_flag +
+ namespace_flag +
+ value_flag
+ end
+
+ def optional_rbac_create_flag
+ return [] unless rbac?
- "helm install #{chart} #{init_flags} >/dev/null\n"
+ # jupyterhub helm chart is using rbac.enabled
+ # https://github.com/jupyterhub/zero-to-jupyterhub-k8s/tree/master/jupyterhub
+ %w[--set rbac.create=true,rbac.enabled=true]
end
def optional_version_flag
- " --version #{version}" if version
+ return [] unless version
+
+ ['--version', version]
end
def optional_tls_flags
- return unless files.key?(:'ca.pem')
+ return [] unless files.key?(:'ca.pem')
- " --tls" \
- " --tls-ca-cert #{files_dir}/ca.pem" \
- " --tls-cert #{files_dir}/cert.pem" \
- " --tls-key #{files_dir}/key.pem"
+ [
+ '--tls',
+ '--tls-ca-cert', "#{files_dir}/ca.pem",
+ '--tls-cert', "#{files_dir}/cert.pem",
+ '--tls-key', "#{files_dir}/key.pem"
+ ]
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/pod.rb b/lib/gitlab/kubernetes/helm/pod.rb
index 6e5d3388405..75484f80070 100644
--- a/lib/gitlab/kubernetes/helm/pod.rb
+++ b/lib/gitlab/kubernetes/helm/pod.rb
@@ -1,10 +1,13 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
module Helm
class Pod
- def initialize(command, namespace_name)
+ def initialize(command, namespace_name, service_account_name: nil)
@command = command
@namespace_name = namespace_name
+ @service_account_name = service_account_name
end
def generate
@@ -12,18 +15,19 @@ module Gitlab
spec[:volumes] = volumes_specification
spec[:containers][0][:volumeMounts] = volume_mounts_specification
+ spec[:serviceAccountName] = service_account_name if service_account_name
::Kubeclient::Resource.new(metadata: metadata, spec: spec)
end
private
- attr_reader :command, :namespace_name, :kubeclient, :config_map
+ attr_reader :command, :namespace_name, :service_account_name
def container_specification
{
name: 'helm',
- image: 'alpine:3.6',
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/#{Gitlab::Kubernetes::Helm::HELM_VERSION}-kube-#{Gitlab::Kubernetes::Helm::KUBECTL_VERSION}",
env: generate_pod_env(command),
command: %w(/bin/sh),
args: %w(-c $(COMMAND_SCRIPT))
diff --git a/lib/gitlab/kubernetes/helm/upgrade_command.rb b/lib/gitlab/kubernetes/helm/upgrade_command.rb
new file mode 100644
index 00000000000..9daffc138b5
--- /dev/null
+++ b/lib/gitlab/kubernetes/helm/upgrade_command.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ module Helm
+ class UpgradeCommand
+ include BaseCommand
+ include ClientCommand
+
+ attr_reader :name, :chart, :version, :repository, :files
+
+ def initialize(name, chart:, files:, rbac:, version: nil, repository: nil)
+ @name = name
+ @chart = chart
+ @rbac = rbac
+ @version = version
+ @files = files
+ @repository = repository
+ end
+
+ def generate_script
+ super + [
+ init_command,
+ wait_for_tiller_command,
+ repository_command,
+ script_command
+ ].compact.join("\n")
+ end
+
+ def rbac?
+ @rbac
+ end
+
+ def pod_name
+ "upgrade-#{name}"
+ end
+
+ private
+
+ def script_command
+ upgrade_flags = "#{optional_version_flag}#{optional_tls_flags}" \
+ " --reset-values" \
+ " --install" \
+ " --namespace #{::Gitlab::Kubernetes::Helm::NAMESPACE}" \
+ " -f /data/helm/#{name}/config/values.yaml"
+
+ "helm upgrade #{name} #{chart}#{upgrade_flags}"
+ end
+
+ def optional_version_flag
+ " --version #{version}" if version
+ end
+
+ def optional_tls_flags
+ return unless files.key?(:'ca.pem')
+
+ " --tls" \
+ " --tls-ca-cert #{files_dir}/ca.pem" \
+ " --tls-cert #{files_dir}/cert.pem" \
+ " --tls-key #{files_dir}/key.pem"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb
new file mode 100644
index 00000000000..b947f6b551e
--- /dev/null
+++ b/lib/gitlab/kubernetes/kube_client.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require 'uri'
+
+module Gitlab
+ module Kubernetes
+ # Wrapper around Kubeclient::Client to dispatch
+ # the right message to the client that can respond to the message.
+ # We must have a kubeclient for each ApiGroup as there is no
+ # other way to use the Kubeclient gem.
+ #
+ # See https://github.com/abonas/kubeclient/issues/348.
+ class KubeClient
+ include Gitlab::Utils::StrongMemoize
+
+ SUPPORTED_API_GROUPS = {
+ core: { group: 'api', version: 'v1' },
+ rbac: { group: 'apis/rbac.authorization.k8s.io', version: 'v1' },
+ extensions: { group: 'apis/extensions', version: 'v1beta1' },
+ knative: { group: 'apis/serving.knative.dev', version: 'v1alpha1' }
+ }.freeze
+
+ SUPPORTED_API_GROUPS.each do |name, params|
+ client_method_name = "#{name}_client".to_sym
+
+ define_method(client_method_name) do
+ strong_memoize(client_method_name) do
+ build_kubeclient(params[:group], params[:version])
+ end
+ end
+ end
+
+ # Core API methods delegates to the core api group client
+ delegate :get_pods,
+ :get_secrets,
+ :get_config_map,
+ :get_namespace,
+ :get_pod,
+ :get_secret,
+ :get_service,
+ :get_service_account,
+ :delete_pod,
+ :create_config_map,
+ :create_namespace,
+ :create_pod,
+ :create_secret,
+ :create_service_account,
+ :update_config_map,
+ :update_service_account,
+ to: :core_client
+
+ # RBAC methods delegates to the apis/rbac.authorization.k8s.io api
+ # group client
+ delegate :create_cluster_role_binding,
+ :get_cluster_role_binding,
+ :update_cluster_role_binding,
+ to: :rbac_client
+
+ # RBAC methods delegates to the apis/rbac.authorization.k8s.io api
+ # group client
+ delegate :create_role_binding,
+ :get_role_binding,
+ :update_role_binding,
+ to: :rbac_client
+
+ # Deployments resource is currently on the apis/extensions api group
+ delegate :get_deployments,
+ to: :extensions_client
+
+ # non-entity methods that can only work with the core client
+ # as it uses the pods/log resource
+ delegate :get_pod_log,
+ :watch_pod_log,
+ to: :core_client
+
+ attr_reader :api_prefix, :kubeclient_options
+
+ def initialize(api_prefix, **kubeclient_options)
+ @api_prefix = api_prefix
+ @kubeclient_options = kubeclient_options
+ end
+
+ private
+
+ def build_kubeclient(api_group, api_version)
+ ::Kubeclient::Client.new(
+ join_api_url(api_prefix, api_group),
+ api_version,
+ **kubeclient_options
+ )
+ end
+
+ def join_api_url(api_prefix, api_path)
+ url = URI.parse(api_prefix)
+ prefix = url.path.sub(%r{/+\z}, '')
+
+ url.path = [prefix, api_path].join("/")
+
+ url.to_s
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/namespace.rb b/lib/gitlab/kubernetes/namespace.rb
index e6ff6160ab9..919f19c86d7 100644
--- a/lib/gitlab/kubernetes/namespace.rb
+++ b/lib/gitlab/kubernetes/namespace.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
class Namespace
@@ -10,9 +12,7 @@ module Gitlab
def exists?
@client.get_namespace(name)
- rescue ::Kubeclient::HttpError => ke
- raise ke unless ke.error_code == 404
-
+ rescue ::Kubeclient::ResourceNotFoundError
false
end
diff --git a/lib/gitlab/kubernetes/pod.rb b/lib/gitlab/kubernetes/pod.rb
index f3842cdf762..81317e532b2 100644
--- a/lib/gitlab/kubernetes/pod.rb
+++ b/lib/gitlab/kubernetes/pod.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Kubernetes
module Pod
diff --git a/lib/gitlab/kubernetes/role_binding.rb b/lib/gitlab/kubernetes/role_binding.rb
new file mode 100644
index 00000000000..cb0cb42d007
--- /dev/null
+++ b/lib/gitlab/kubernetes/role_binding.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class RoleBinding
+ def initialize(name:, role_name:, namespace:, service_account_name:)
+ @name = name
+ @role_name = role_name
+ @namespace = namespace
+ @service_account_name = service_account_name
+ end
+
+ def generate
+ ::Kubeclient::Resource.new.tap do |resource|
+ resource.metadata = metadata
+ resource.roleRef = role_ref
+ resource.subjects = subjects
+ end
+ end
+
+ private
+
+ attr_reader :name, :role_name, :namespace, :service_account_name
+
+ def metadata
+ { name: name, namespace: namespace }
+ end
+
+ def role_ref
+ {
+ apiGroup: 'rbac.authorization.k8s.io',
+ kind: 'ClusterRole',
+ name: role_name
+ }
+ end
+
+ def subjects
+ [
+ {
+ kind: 'ServiceAccount',
+ name: service_account_name,
+ namespace: namespace
+ }
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/service_account.rb b/lib/gitlab/kubernetes/service_account.rb
new file mode 100644
index 00000000000..d58fc1c3976
--- /dev/null
+++ b/lib/gitlab/kubernetes/service_account.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class ServiceAccount
+ attr_reader :name, :namespace_name
+
+ def initialize(name, namespace_name)
+ @name = name
+ @namespace_name = namespace_name
+ end
+
+ def generate
+ ::Kubeclient::Resource.new(metadata: metadata)
+ end
+
+ private
+
+ def metadata
+ {
+ name: name,
+ namespace: namespace_name
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/service_account_token.rb b/lib/gitlab/kubernetes/service_account_token.rb
new file mode 100644
index 00000000000..2e912b26c09
--- /dev/null
+++ b/lib/gitlab/kubernetes/service_account_token.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class ServiceAccountToken
+ attr_reader :name, :service_account_name, :namespace_name
+
+ def initialize(name, service_account_name, namespace_name)
+ @name = name
+ @service_account_name = service_account_name
+ @namespace_name = namespace_name
+ end
+
+ def generate
+ ::Kubeclient::Resource.new(metadata: metadata, type: service_acount_token_type)
+ end
+
+ private
+
+ # as per https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#to-create-additional-api-tokens
+ def service_acount_token_type
+ 'kubernetes.io/service-account-token'
+ end
+
+ def metadata
+ {
+ name: name,
+ namespace: namespace_name,
+ annotations: {
+ "kubernetes.io/service-account.name": service_account_name
+ }
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/language_data.rb b/lib/gitlab/language_data.rb
new file mode 100644
index 00000000000..bfdd7175198
--- /dev/null
+++ b/lib/gitlab/language_data.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module LanguageData
+ EXTENSION_MUTEX = Mutex.new
+
+ class << self
+ include Gitlab::Utils::StrongMemoize
+
+ def extensions
+ EXTENSION_MUTEX.synchronize do
+ strong_memoize(:extensions) do
+ Set.new.tap do |set|
+ YAML.load_file(Rails.root.join('vendor', 'languages.yml')).each do |_name, details|
+ details['extensions']&.each do |ext|
+ next unless ext.start_with?('.')
+
+ set << ext.downcase
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def clear_extensions!
+ EXTENSION_MUTEX.synchronize do
+ clear_memoization(:extensions)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/language_detection.rb b/lib/gitlab/language_detection.rb
index a41435fdb79..7600e60b904 100644
--- a/lib/gitlab/language_detection.rb
+++ b/lib/gitlab/language_detection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class LanguageDetection
MAX_LANGUAGES = 5
diff --git a/lib/gitlab/lazy.rb b/lib/gitlab/lazy.rb
index 99594577141..d7a22aa339e 100644
--- a/lib/gitlab/lazy.rb
+++ b/lib/gitlab/lazy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# A class that can be wrapped around an expensive method call so it's only
# executed when actually needed.
diff --git a/lib/gitlab/legacy_github_import/base_formatter.rb b/lib/gitlab/legacy_github_import/base_formatter.rb
index 2f07fde406c..0b19cf742ed 100644
--- a/lib/gitlab/legacy_github_import/base_formatter.rb
+++ b/lib/gitlab/legacy_github_import/base_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class BaseFormatter
@@ -10,6 +12,7 @@ module Gitlab
@formatter = Gitlab::ImportFormatter.new
end
+ # rubocop: disable CodeReuse/ActiveRecord
def create!
association = project.public_send(project_association) # rubocop:disable GitlabSecurity/PublicSend
@@ -17,6 +20,7 @@ module Gitlab
record.attributes = attributes
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def url
raw_data.url || ''
diff --git a/lib/gitlab/legacy_github_import/branch_formatter.rb b/lib/gitlab/legacy_github_import/branch_formatter.rb
index 80fe1d67209..1177751457f 100644
--- a/lib/gitlab/legacy_github_import/branch_formatter.rb
+++ b/lib/gitlab/legacy_github_import/branch_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class BranchFormatter < BaseFormatter
diff --git a/lib/gitlab/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb
index d8ed0ebca9d..bc952147667 100644
--- a/lib/gitlab/legacy_github_import/client.rb
+++ b/lib/gitlab/legacy_github_import/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class Client
diff --git a/lib/gitlab/legacy_github_import/comment_formatter.rb b/lib/gitlab/legacy_github_import/comment_formatter.rb
index d2c7a8ae9f4..d83cc4f6b3c 100644
--- a/lib/gitlab/legacy_github_import/comment_formatter.rb
+++ b/lib/gitlab/legacy_github_import/comment_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class CommentFormatter < BaseFormatter
diff --git a/lib/gitlab/legacy_github_import/importer.rb b/lib/gitlab/legacy_github_import/importer.rb
index b04d678cf98..43695451b87 100644
--- a/lib/gitlab/legacy_github_import/importer.rb
+++ b/lib/gitlab/legacy_github_import/importer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class Importer
@@ -113,6 +115,7 @@ module Gitlab
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def import_issues
fetch_resources(:issues, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues|
issues.each do |raw|
@@ -133,6 +136,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def import_pull_requests
fetch_resources(:pull_requests, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests|
@@ -193,6 +197,7 @@ module Gitlab
issuable.update_attribute(:label_ids, label_ids)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def import_comments(issuable_type)
resource_type = "#{issuable_type}_comments".to_sym
@@ -213,7 +218,9 @@ module Gitlab
create_comments(comments)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def create_comments(comments)
ActiveRecord::Base.no_touching do
comments.each do |raw|
@@ -238,6 +245,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def discard_inserted_comments(comments, last_note)
last_note_attrs = nil
diff --git a/lib/gitlab/legacy_github_import/issuable_formatter.rb b/lib/gitlab/legacy_github_import/issuable_formatter.rb
index de55382d3ad..1a0aefbbd62 100644
--- a/lib/gitlab/legacy_github_import/issuable_formatter.rb
+++ b/lib/gitlab/legacy_github_import/issuable_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class IssuableFormatter < BaseFormatter
@@ -55,12 +57,14 @@ module Gitlab
end
end
+ # rubocop: disable CodeReuse/ActiveRecord
def milestone
if raw_data.milestone.present?
milestone = MilestoneFormatter.new(project, raw_data.milestone)
project.milestones.find_by(milestone.find_condition)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/legacy_github_import/issue_formatter.rb b/lib/gitlab/legacy_github_import/issue_formatter.rb
index 4c8825ccf19..2f46e2e30d1 100644
--- a/lib/gitlab/legacy_github_import/issue_formatter.rb
+++ b/lib/gitlab/legacy_github_import/issue_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class IssueFormatter < IssuableFormatter
diff --git a/lib/gitlab/legacy_github_import/label_formatter.rb b/lib/gitlab/legacy_github_import/label_formatter.rb
index c3eed12e739..89200e794d8 100644
--- a/lib/gitlab/legacy_github_import/label_formatter.rb
+++ b/lib/gitlab/legacy_github_import/label_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class LabelFormatter < BaseFormatter
@@ -13,6 +15,7 @@ module Gitlab
:labels
end
+ # rubocop: disable CodeReuse/ActiveRecord
def create!
params = attributes.except(:project)
service = ::Labels::FindOrCreateService.new(nil, project, params)
@@ -22,6 +25,7 @@ module Gitlab
label
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/lib/gitlab/legacy_github_import/milestone_formatter.rb b/lib/gitlab/legacy_github_import/milestone_formatter.rb
index a565294384d..2fe1b4258d3 100644
--- a/lib/gitlab/legacy_github_import/milestone_formatter.rb
+++ b/lib/gitlab/legacy_github_import/milestone_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class MilestoneFormatter < BaseFormatter
diff --git a/lib/gitlab/legacy_github_import/project_creator.rb b/lib/gitlab/legacy_github_import/project_creator.rb
index 5e96eb16754..ca1a1b8e9bd 100644
--- a/lib/gitlab/legacy_github_import/project_creator.rb
+++ b/lib/gitlab/legacy_github_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class ProjectCreator
diff --git a/lib/gitlab/legacy_github_import/pull_request_formatter.rb b/lib/gitlab/legacy_github_import/pull_request_formatter.rb
index 94c2e99066a..5b847f13d4a 100644
--- a/lib/gitlab/legacy_github_import/pull_request_formatter.rb
+++ b/lib/gitlab/legacy_github_import/pull_request_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class PullRequestFormatter < IssuableFormatter
diff --git a/lib/gitlab/legacy_github_import/release_formatter.rb b/lib/gitlab/legacy_github_import/release_formatter.rb
index 3ed9d4f76da..8c0c17780ca 100644
--- a/lib/gitlab/legacy_github_import/release_formatter.rb
+++ b/lib/gitlab/legacy_github_import/release_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class ReleaseFormatter < BaseFormatter
diff --git a/lib/gitlab/legacy_github_import/user_formatter.rb b/lib/gitlab/legacy_github_import/user_formatter.rb
index 6d8055622f1..ec0e221b1ff 100644
--- a/lib/gitlab/legacy_github_import/user_formatter.rb
+++ b/lib/gitlab/legacy_github_import/user_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class UserFormatter
@@ -29,6 +31,7 @@ module Gitlab
.try(:id)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def find_by_external_uid
return nil unless id
@@ -40,6 +43,7 @@ module Gitlab
.first
.try(:id)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/legacy_github_import/wiki_formatter.rb b/lib/gitlab/legacy_github_import/wiki_formatter.rb
index 27f45875c7c..ea52be5ee0f 100644
--- a/lib/gitlab/legacy_github_import/wiki_formatter.rb
+++ b/lib/gitlab/legacy_github_import/wiki_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module LegacyGithubImport
class WikiFormatter
diff --git a/lib/gitlab/lfs_token.rb b/lib/gitlab/lfs_token.rb
index ead5d566871..fa44bd842b2 100644
--- a/lib/gitlab/lfs_token.rb
+++ b/lib/gitlab/lfs_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class LfsToken
attr_accessor :actor
diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb
index e58927a40b9..128a5dd8936 100644
--- a/lib/gitlab/logger.rb
+++ b/lib/gitlab/logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class Logger < ::Logger
def self.file_name
@@ -30,7 +32,7 @@ module Gitlab
end
def self.build
- RequestStore[self.cache_key] ||= new(self.full_log_path)
+ Gitlab::SafeRequestStore[self.cache_key] ||= new(self.full_log_path)
end
def self.full_log_path
diff --git a/lib/gitlab/mail_room.rb b/lib/gitlab/mail_room.rb
index db04356a5e9..78f2d83c1af 100644
--- a/lib/gitlab/mail_room.rb
+++ b/lib/gitlab/mail_room.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'yaml'
require 'json'
require_relative 'redis/queues' unless defined?(Gitlab::Redis::Queues)
diff --git a/lib/gitlab/manifest_import/manifest.rb b/lib/gitlab/manifest_import/manifest.rb
index 4d6034fb956..7208fe5bbc5 100644
--- a/lib/gitlab/manifest_import/manifest.rb
+++ b/lib/gitlab/manifest_import/manifest.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Class to parse manifest file and build a list of repositories for import
#
# <manifest>
@@ -63,7 +65,7 @@ module Gitlab
end
def repository_url(name)
- URI.join(remote, name).to_s
+ Gitlab::Utils.append_path(remote, name)
end
def remote
diff --git a/lib/gitlab/manifest_import/project_creator.rb b/lib/gitlab/manifest_import/project_creator.rb
index b5967c93735..837d65e5f7c 100644
--- a/lib/gitlab/manifest_import/project_creator.rb
+++ b/lib/gitlab/manifest_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ManifestImport
class ProjectCreator
diff --git a/lib/gitlab/markup_helper.rb b/lib/gitlab/markup_helper.rb
index 49285e35251..d419fa66e57 100644
--- a/lib/gitlab/markup_helper.rb
+++ b/lib/gitlab/markup_helper.rb
@@ -1,11 +1,14 @@
+# frozen_string_literal: true
+
module Gitlab
module MarkupHelper
extend self
- MARKDOWN_EXTENSIONS = %w(mdown mkd mkdn md markdown).freeze
- ASCIIDOC_EXTENSIONS = %w(adoc ad asciidoc).freeze
- OTHER_EXTENSIONS = %w(textile rdoc org creole wiki mediawiki rst).freeze
+ MARKDOWN_EXTENSIONS = %w[mdown mkd mkdn md markdown].freeze
+ ASCIIDOC_EXTENSIONS = %w[adoc ad asciidoc].freeze
+ OTHER_EXTENSIONS = %w[textile rdoc org creole wiki mediawiki rst].freeze
EXTENSIONS = MARKDOWN_EXTENSIONS + ASCIIDOC_EXTENSIONS + OTHER_EXTENSIONS
+ PLAIN_FILENAMES = %w[readme index].freeze
# Public: Determines if a given filename is compatible with GitHub::Markup.
#
@@ -41,7 +44,7 @@ module Gitlab
#
# Returns boolean
def plain?(filename)
- extension(filename) == 'txt' || filename.casecmp('readme').zero?
+ extension(filename) == 'txt' || plain_filename?(filename)
end
def previewable?(filename)
@@ -53,5 +56,9 @@ module Gitlab
def extension(filename)
File.extname(filename).downcase.delete('.')
end
+
+ def plain_filename?(filename)
+ PLAIN_FILENAMES.include?(filename.downcase)
+ end
end
end
diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb
index 7d63ca5627d..61ed20ad623 100644
--- a/lib/gitlab/metrics.rb
+++ b/lib/gitlab/metrics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
include Gitlab::Metrics::InfluxDb
diff --git a/lib/gitlab/metrics/background_transaction.rb b/lib/gitlab/metrics/background_transaction.rb
index 5919ebb1493..fe1722b1095 100644
--- a/lib/gitlab/metrics/background_transaction.rb
+++ b/lib/gitlab/metrics/background_transaction.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
class BackgroundTransaction < Transaction
diff --git a/lib/gitlab/metrics/delta.rb b/lib/gitlab/metrics/delta.rb
index bcf28eed84d..ab2d9e46390 100644
--- a/lib/gitlab/metrics/delta.rb
+++ b/lib/gitlab/metrics/delta.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Class for calculating the difference between two numeric values.
diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb
index 04135dac4ff..1359e973590 100644
--- a/lib/gitlab/metrics/influx_db.rb
+++ b/lib/gitlab/metrics/influx_db.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
module InfluxDb
@@ -86,7 +88,7 @@ module Gitlab
# Example:
#
# Gitlab::Metrics.measure(:find_by_username_duration) do
- # User.find_by_username(some_username)
+ # UserFinder.new(some_username).find_by_username
# end
#
# name - The name of the field to store the execution time in.
diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb
index 023e9963493..651e241362c 100644
--- a/lib/gitlab/metrics/instrumentation.rb
+++ b/lib/gitlab/metrics/instrumentation.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Module for instrumenting methods.
diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb
index f3290e3149c..85438011cb9 100644
--- a/lib/gitlab/metrics/method_call.rb
+++ b/lib/gitlab/metrics/method_call.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Class for tracking timing information about method calls
diff --git a/lib/gitlab/metrics/methods.rb b/lib/gitlab/metrics/methods.rb
index f79eb0cd1bf..447d03bfca4 100644
--- a/lib/gitlab/metrics/methods.rb
+++ b/lib/gitlab/metrics/methods.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop:disable Style/ClassVars
module Gitlab
diff --git a/lib/gitlab/metrics/methods/metric_options.rb b/lib/gitlab/metrics/methods/metric_options.rb
index 70e122d4e15..8e6ceb74c09 100644
--- a/lib/gitlab/metrics/methods/metric_options.rb
+++ b/lib/gitlab/metrics/methods/metric_options.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
module Methods
diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb
index bd0afe53c51..9e4d70a71ff 100644
--- a/lib/gitlab/metrics/metric.rb
+++ b/lib/gitlab/metrics/metric.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Class for storing details of a single metric (label, value, etc).
diff --git a/lib/gitlab/metrics/null_metric.rb b/lib/gitlab/metrics/null_metric.rb
index aabada5c21a..7dbd2a1f8e3 100644
--- a/lib/gitlab/metrics/null_metric.rb
+++ b/lib/gitlab/metrics/null_metric.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Mocks ::Prometheus::Client::Metric and all derived metrics
diff --git a/lib/gitlab/metrics/prometheus.rb b/lib/gitlab/metrics/prometheus.rb
index d41a855bff1..cab1edab48f 100644
--- a/lib/gitlab/metrics/prometheus.rb
+++ b/lib/gitlab/metrics/prometheus.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'prometheus/client'
module Gitlab
diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb
index 2d45765df3f..9aa97515961 100644
--- a/lib/gitlab/metrics/rack_middleware.rb
+++ b/lib/gitlab/metrics/rack_middleware.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Rack middleware for tracking Rails and Grape requests.
diff --git a/lib/gitlab/metrics/requests_rack_middleware.rb b/lib/gitlab/metrics/requests_rack_middleware.rb
index 0dc19f31d03..74c956ab5af 100644
--- a/lib/gitlab/metrics/requests_rack_middleware.rb
+++ b/lib/gitlab/metrics/requests_rack_middleware.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
class RequestsRackMiddleware
diff --git a/lib/gitlab/metrics/samplers/base_sampler.rb b/lib/gitlab/metrics/samplers/base_sampler.rb
index 37f90c4673d..6a062e93f0f 100644
--- a/lib/gitlab/metrics/samplers/base_sampler.rb
+++ b/lib/gitlab/metrics/samplers/base_sampler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'logger'
module Gitlab
diff --git a/lib/gitlab/metrics/samplers/influx_sampler.rb b/lib/gitlab/metrics/samplers/influx_sampler.rb
index ad97632e4eb..c4c38b23a55 100644
--- a/lib/gitlab/metrics/samplers/influx_sampler.rb
+++ b/lib/gitlab/metrics/samplers/influx_sampler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
module Samplers
diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb
index 7b2b3bedf04..232a58a7d69 100644
--- a/lib/gitlab/metrics/samplers/ruby_sampler.rb
+++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'prometheus/client/support/unicorn'
module Gitlab
diff --git a/lib/gitlab/metrics/samplers/unicorn_sampler.rb b/lib/gitlab/metrics/samplers/unicorn_sampler.rb
index ea325651fbb..4c4ec026823 100644
--- a/lib/gitlab/metrics/samplers/unicorn_sampler.rb
+++ b/lib/gitlab/metrics/samplers/unicorn_sampler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
module Samplers
diff --git a/lib/gitlab/metrics/sidekiq_metrics_exporter.rb b/lib/gitlab/metrics/sidekiq_metrics_exporter.rb
index 47b4af5d649..56e106b9612 100644
--- a/lib/gitlab/metrics/sidekiq_metrics_exporter.rb
+++ b/lib/gitlab/metrics/sidekiq_metrics_exporter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'webrick'
require 'prometheus/client/rack/exporter'
diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb
index df4bdf16847..0b4485feea9 100644
--- a/lib/gitlab/metrics/sidekiq_middleware.rb
+++ b/lib/gitlab/metrics/sidekiq_middleware.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Sidekiq middleware for tracking jobs.
diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb
index b600e8a2a50..c068f8017fd 100644
--- a/lib/gitlab/metrics/subscribers/action_view.rb
+++ b/lib/gitlab/metrics/subscribers/action_view.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
module Subscribers
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index c205f348023..a02dd850582 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
module Subscribers
@@ -6,9 +8,15 @@ module Gitlab
include Gitlab::Metrics::Methods
attach_to :active_record
+ IGNORABLE_SQL = %w{BEGIN COMMIT}.freeze
+
def sql(event)
return unless current_transaction
+ payload = event.payload
+
+ return if payload[:name] == 'SCHEMA' || IGNORABLE_SQL.include?(payload[:sql])
+
self.class.gitlab_sql_duration_seconds.observe(current_transaction.labels, event.duration / 1000.0)
current_transaction.increment(:sql_duration, event.duration, false)
diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb
index 250897a79c2..f633e1a9d7c 100644
--- a/lib/gitlab/metrics/subscribers/rails_cache.rb
+++ b/lib/gitlab/metrics/subscribers/rails_cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
module Subscribers
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb
index e60e245cf89..426496855e3 100644
--- a/lib/gitlab/metrics/system.rb
+++ b/lib/gitlab/metrics/system.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Module for gathering system/process statistics such as the memory usage.
diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb
index 9f903e96585..468d7cb56fc 100644
--- a/lib/gitlab/metrics/transaction.rb
+++ b/lib/gitlab/metrics/transaction.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
# Class for storing metrics information of a single transaction.
diff --git a/lib/gitlab/metrics/web_transaction.rb b/lib/gitlab/metrics/web_transaction.rb
index 723ca576aab..b2a43d46fb2 100644
--- a/lib/gitlab/metrics/web_transaction.rb
+++ b/lib/gitlab/metrics/web_transaction.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Metrics
class WebTransaction < Transaction
@@ -40,7 +42,7 @@ module Gitlab
# increasing the cardinality of our metrics, we limit the number of
# possible suffixes.
if suffix && ALLOWED_SUFFIXES.include?(suffix)
- action += ".#{suffix}"
+ action = "#{action}.#{suffix}"
end
{ controller: controller.class.name, action: action }
diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb
index 1fd8f147b44..d1a87c3b3bb 100644
--- a/lib/gitlab/middleware/go.rb
+++ b/lib/gitlab/middleware/go.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A dumb middleware that returns a Go HTML document if the go-get=1 query string
# is used irrespective if the namespace/project exists
module Gitlab
@@ -38,7 +40,7 @@ module Gitlab
def go_body(path)
config = Gitlab.config
- project_url = URI.join(config.gitlab.url, path)
+ project_url = Gitlab::Utils.append_path(config.gitlab.url, path)
import_prefix = strip_url(project_url.to_s)
repository_url = if Gitlab::CurrentSettings.enabled_git_access_protocol == 'ssh'
diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb
index 18f91db98fc..84c2f0d5720 100644
--- a/lib/gitlab/middleware/multipart.rb
+++ b/lib/gitlab/middleware/multipart.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitlab::Middleware::Multipart - a Rack::Multipart replacement
#
# Rack::Multipart leaves behind tempfiles in /tmp and uses valuable Ruby
@@ -82,9 +84,13 @@ module Gitlab
end
def open_file(params, key)
- ::UploadedFile.from_params(
- params, key,
- [FileUploader.root, Gitlab.config.uploads.storage_path])
+ allowed_paths = [
+ ::FileUploader.root,
+ Gitlab.config.uploads.storage_path,
+ File.join(Rails.root, 'public/uploads/tmp')
+ ]
+
+ ::UploadedFile.from_params(params, key, allowed_paths)
end
end
diff --git a/lib/gitlab/middleware/rails_queue_duration.rb b/lib/gitlab/middleware/rails_queue_duration.rb
index bc70b2459ef..96c6a0a7d28 100644
--- a/lib/gitlab/middleware/rails_queue_duration.rb
+++ b/lib/gitlab/middleware/rails_queue_duration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This Rack middleware is intended to measure the latency between
# gitlab-workhorse forwarding a request to the Rails application and the
# time this middleware is reached.
diff --git a/lib/gitlab/middleware/read_only.rb b/lib/gitlab/middleware/read_only.rb
index 7f63e39b3aa..83c52a6c6e0 100644
--- a/lib/gitlab/middleware/read_only.rb
+++ b/lib/gitlab/middleware/read_only.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Middleware
class ReadOnly
diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb
index 8dca431c005..89941a9efa0 100644
--- a/lib/gitlab/middleware/read_only/controller.rb
+++ b/lib/gitlab/middleware/read_only/controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Middleware
class ReadOnly
diff --git a/lib/gitlab/middleware/release_env.rb b/lib/gitlab/middleware/release_env.rb
index bfe8e113b5e..849cf8f759b 100644
--- a/lib/gitlab/middleware/release_env.rb
+++ b/lib/gitlab/middleware/release_env.rb
@@ -1,4 +1,7 @@
-module Gitlab # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module Gitlab
module Middleware
# Some of middleware would hold env for no good reason even after the
# request had already been processed, and we could not garbage collect
diff --git a/lib/gitlab/middleware/static.rb b/lib/gitlab/middleware/static.rb
index aa1e9dc0fdb..972fed2134c 100644
--- a/lib/gitlab/middleware/static.rb
+++ b/lib/gitlab/middleware/static.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Middleware
class Static < ActionDispatch::Static
diff --git a/lib/gitlab/multi_collection_paginator.rb b/lib/gitlab/multi_collection_paginator.rb
index fd5de73c526..5375077d7dc 100644
--- a/lib/gitlab/multi_collection_paginator.rb
+++ b/lib/gitlab/multi_collection_paginator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class MultiCollectionPaginator
attr_reader :first_collection, :second_collection, :per_page
@@ -53,6 +55,7 @@ module Gitlab
@first_collection_page_count = first_collection_page.total_pages
end
+ # rubocop: disable CodeReuse/ActiveRecord
def first_collection_last_page_size
return @first_collection_last_page_size if defined?(@first_collection_last_page_size)
@@ -60,5 +63,6 @@ module Gitlab
.except(:select)
.size
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/lib/gitlab/namespace_sanitizer.rb b/lib/gitlab/namespace_sanitizer.rb
new file mode 100644
index 00000000000..d755bbbcaf9
--- /dev/null
+++ b/lib/gitlab/namespace_sanitizer.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class NamespaceSanitizer
+ def self.sanitize(namespace)
+ namespace.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
+ end
+ end
+end
diff --git a/lib/gitlab/null_request_store.rb b/lib/gitlab/null_request_store.rb
new file mode 100644
index 00000000000..8db331dcb9f
--- /dev/null
+++ b/lib/gitlab/null_request_store.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+# Used by Gitlab::SafeRequestStore
+module Gitlab
+ # The methods `begin!`, `clear!`, and `end!` are not defined because they
+ # should only be called directly on `RequestStore`.
+ class NullRequestStore
+ def store
+ {}
+ end
+
+ def active?
+ end
+
+ def read(key)
+ end
+
+ def [](key)
+ end
+
+ def write(key, value)
+ value
+ end
+
+ def []=(key, value)
+ value
+ end
+
+ def exist?(key)
+ false
+ end
+
+ def fetch(key, &block)
+ yield
+ end
+
+ def delete(key, &block)
+ yield(key) if block_given?
+ end
+ end
+end
diff --git a/lib/gitlab/omniauth_initializer.rb b/lib/gitlab/omniauth_initializer.rb
index f33ea0880df..e0ac9eec1f2 100644
--- a/lib/gitlab/omniauth_initializer.rb
+++ b/lib/gitlab/omniauth_initializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class OmniauthInitializer
def initialize(devise_config)
diff --git a/lib/gitlab/optimistic_locking.rb b/lib/gitlab/optimistic_locking.rb
index d09bce642b0..ce4ba9f752b 100644
--- a/lib/gitlab/optimistic_locking.rb
+++ b/lib/gitlab/optimistic_locking.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module OptimisticLocking
module_function
diff --git a/lib/gitlab/other_markup.rb b/lib/gitlab/other_markup.rb
index fc3f21233dd..bc467486eee 100644
--- a/lib/gitlab/other_markup.rb
+++ b/lib/gitlab/other_markup.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# Parser/renderer for markups without other special support code.
module OtherMarkup
diff --git a/lib/gitlab/otp_key_rotator.rb b/lib/gitlab/otp_key_rotator.rb
index 22332474945..1d3200aa099 100644
--- a/lib/gitlab/otp_key_rotator.rb
+++ b/lib/gitlab/otp_key_rotator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# The +otp_key_base+ param is used to encrypt the User#otp_secret attribute.
#
@@ -26,6 +28,7 @@ module Gitlab
@filename = filename
end
+ # rubocop: disable CodeReuse/ActiveRecord
def rotate!(old_key:, new_key:)
old_key ||= Gitlab::Application.secrets.otp_key_base
@@ -47,7 +50,9 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def rollback!
ActiveRecord::Base.transaction do
CSV.foreach(filename, headers: HEADERS, return_headers: false) do |row|
@@ -55,6 +60,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/lib/gitlab/pages.rb b/lib/gitlab/pages.rb
index 981ef8faa9a..16df0700b08 100644
--- a/lib/gitlab/pages.rb
+++ b/lib/gitlab/pages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Pages
VERSION = File.read(Rails.root.join("GITLAB_PAGES_VERSION")).strip.freeze
diff --git a/lib/gitlab/pages_client.rb b/lib/gitlab/pages_client.rb
index 7b358a3bd1b..3626e53f84c 100644
--- a/lib/gitlab/pages_client.rb
+++ b/lib/gitlab/pages_client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class PagesClient
class << self
diff --git a/lib/gitlab/pages_transfer.rb b/lib/gitlab/pages_transfer.rb
index fb215f27cbd..a70dc826f97 100644
--- a/lib/gitlab/pages_transfer.rb
+++ b/lib/gitlab/pages_transfer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class PagesTransfer < ProjectTransfer
def root_dir
diff --git a/lib/gitlab/patch/draw_route.rb b/lib/gitlab/patch/draw_route.rb
new file mode 100644
index 00000000000..b00244a6e04
--- /dev/null
+++ b/lib/gitlab/patch/draw_route.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+# We're patching `ActionDispatch::Routing::Mapper` in
+# config/initializers/routing_draw.rb
+module Gitlab
+ module Patch
+ module DrawRoute
+ RoutesNotFound = Class.new(StandardError)
+
+ def draw(routes_name)
+ drawn_any = draw_ce(routes_name) | draw_ee(routes_name)
+
+ drawn_any || raise(RoutesNotFound.new("Cannot find #{routes_name}"))
+ end
+
+ def draw_ce(routes_name)
+ draw_route(route_path("config/routes/#{routes_name}.rb"))
+ end
+
+ def draw_ee(_)
+ true
+ end
+
+ def route_path(routes_name)
+ Rails.root.join(routes_name)
+ end
+
+ def draw_route(path)
+ if File.exist?(path)
+ instance_eval(File.read(path))
+ true
+ else
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/patch/prependable.rb b/lib/gitlab/patch/prependable.rb
new file mode 100644
index 00000000000..a9f6cfb19cb
--- /dev/null
+++ b/lib/gitlab/patch/prependable.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+# We're patching `ActiveSupport::Concern` in
+# config/initializers/0_as_concern.rb
+#
+# We want to patch `ActiveSupport::Concern` for two reasons:
+# 1. Allow defining class methods via: `class_methods` method
+# 2. Allow `prepended do; end` work like `included do; end`
+# If we don't need anything above, we don't need this patch nor the concern!
+
+# rubocop:disable Gitlab/ModuleWithInstanceVariables
+module Gitlab
+ module Patch
+ module Prependable
+ class MultiplePrependedBlocks < StandardError
+ def initialize
+ super "Cannot define multiple 'prepended' blocks for a Concern"
+ end
+ end
+
+ def prepend_features(base)
+ return false if prepended?(base)
+
+ super
+
+ if const_defined?(:ClassMethods)
+ klass_methods = const_get(:ClassMethods)
+ base.singleton_class.prepend klass_methods
+ base.instance_variable_set(:@_prepended_class_methods, klass_methods)
+ end
+
+ if instance_variable_defined?(:@_prepended_block)
+ base.class_eval(&@_prepended_block)
+ end
+
+ true
+ end
+
+ def class_methods
+ super
+
+ if instance_variable_defined?(:@_prepended_class_methods)
+ const_get(:ClassMethods).prepend @_prepended_class_methods
+ end
+ end
+
+ def prepended(base = nil, &block)
+ if base.nil?
+ raise MultiplePrependedBlocks if
+ instance_variable_defined?(:@_prepended_block)
+
+ @_prepended_block = block
+ else
+ super
+ end
+ end
+
+ def prepended?(base)
+ index = base.ancestors.index(base)
+
+ base.ancestors[0...index].index(self)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index 61653044433..fa68dead80b 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module PathRegex
extend self
@@ -39,7 +41,7 @@ module Gitlab
import
invites
jwt
- koding
+ login
notification_settings
oauth
profile
@@ -234,7 +236,7 @@ module Gitlab
def single_line_regexp(regex)
# Turns a multiline extended regexp into a single line one,
- # beacuse `rake routes` breaks on multiline regexes.
+ # because `rake routes` breaks on multiline regexes.
Regexp.new(regex.source.gsub(/\(\?#.+?\)/, '').gsub(/\s*/, ''), regex.options ^ Regexp::EXTENDED).freeze
end
end
diff --git a/lib/gitlab/performance_bar.rb b/lib/gitlab/performance_bar.rb
index 92a308a12dc..4b0c7b5c7f8 100644
--- a/lib/gitlab/performance_bar.rb
+++ b/lib/gitlab/performance_bar.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module PerformanceBar
ALLOWED_USER_IDS_KEY = 'performance_bar_allowed_user_ids:v2'.freeze
@@ -15,6 +17,7 @@ module Gitlab
Gitlab::CurrentSettings.performance_bar_allowed_group_id
end
+ # rubocop: disable CodeReuse/ActiveRecord
def self.allowed_user_ids
Rails.cache.fetch(ALLOWED_USER_IDS_KEY, expires_in: EXPIRY_TIME) do
group = Group.find_by_id(allowed_group_id)
@@ -26,6 +29,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def self.expire_allowed_user_ids_cache
Rails.cache.delete(ALLOWED_USER_IDS_KEY)
diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb
index f2825db59ae..37ff32b1296 100644
--- a/lib/gitlab/performance_bar/peek_query_tracker.rb
+++ b/lib/gitlab/performance_bar/peek_query_tracker.rb
@@ -23,7 +23,7 @@ module Gitlab
end
subscribe('sql.active_record') do |_, start, finish, _, data|
- if RequestStore.active? && RequestStore.store[:peek_enabled]
+ if Gitlab::SafeRequestStore.store[:peek_enabled]
# data[:cached] is only available starting from Rails 5.1.0
# https://github.com/rails/rails/blob/v5.1.0/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L113
# Before that, data[:name] was set to 'CACHE'
diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb
index 0d1cb16b378..23353f36025 100644
--- a/lib/gitlab/plugin.rb
+++ b/lib/gitlab/plugin.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Plugin
def self.files
diff --git a/lib/gitlab/plugin_logger.rb b/lib/gitlab/plugin_logger.rb
index c4f6ec3e21d..df3bd56fd2f 100644
--- a/lib/gitlab/plugin_logger.rb
+++ b/lib/gitlab/plugin_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class PluginLogger < Gitlab::Logger
def self.file_name_noext
diff --git a/lib/gitlab/polling_interval.rb b/lib/gitlab/polling_interval.rb
index fe4bdfe3831..0f69990df63 100644
--- a/lib/gitlab/polling_interval.rb
+++ b/lib/gitlab/polling_interval.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class PollingInterval
HEADER_NAME = 'Poll-Interval'.freeze
diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb
index d0cb7c1a7cf..7fa00d0c68c 100644
--- a/lib/gitlab/popen.rb
+++ b/lib/gitlab/popen.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'fileutils'
require 'open3'
@@ -11,7 +13,7 @@ module Gitlab
def popen(cmd, path = nil, vars = {}, &block)
result = popen_with_detail(cmd, path, vars, &block)
- [result.stdout << result.stderr, result.status&.exitstatus]
+ ["#{result.stdout}#{result.stderr}", result.status&.exitstatus]
end
# Returns Result
diff --git a/lib/gitlab/private_commit_email.rb b/lib/gitlab/private_commit_email.rb
new file mode 100644
index 00000000000..536fc9dae3a
--- /dev/null
+++ b/lib/gitlab/private_commit_email.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module PrivateCommitEmail
+ TOKEN = "_private".freeze
+
+ class << self
+ def regex
+ hostname_regexp = Regexp.escape(Gitlab::CurrentSettings.current_application_settings.commit_email_hostname)
+
+ /\A(?<id>([0-9]+))\-([^@]+)@#{hostname_regexp}\z/
+ end
+
+ def user_id_for_email(email)
+ match = email&.match(regex)
+ return unless match
+
+ match[:id].to_i
+ end
+
+ def user_ids_for_emails(emails)
+ emails.map { |email| user_id_for_email(email) }.compact.uniq
+ end
+
+ def for_user(user)
+ hostname = Gitlab::CurrentSettings.current_application_settings.commit_email_hostname
+
+ "#{user.id}-#{user.username}@#{hostname}"
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb
index c5bb4648572..4a62f367835 100644
--- a/lib/gitlab/profiler.rb
+++ b/lib/gitlab/profiler.rb
@@ -1,4 +1,6 @@
# coding: utf-8
+# frozen_string_literal: true
+
module Gitlab
module Profiler
FILTERED_STRING = '[FILTERED]'.freeze
@@ -34,6 +36,7 @@ module Gitlab
#
# - private_token: instead of providing a user instance, the token can be
# given as a string. Takes precedence over the user option.
+ # rubocop: disable CodeReuse/ActiveRecord
def self.profile(url, logger: nil, post_data: nil, user: nil, private_token: nil)
app = ActionDispatch::Integration::Session.new(Rails.application)
verb = :get
@@ -76,6 +79,7 @@ module Gitlab
result
end
+ # rubocop: enable CodeReuse/ActiveRecord
def self.create_custom_logger(logger, private_token: nil)
return unless logger
@@ -135,6 +139,7 @@ module Gitlab
result
end
+ # rubocop: disable CodeReuse/ActiveRecord
def self.log_load_times_by_model(logger)
return unless logger.respond_to?(:load_times_by_model)
@@ -146,6 +151,7 @@ module Gitlab
logger.info("#{model} total (#{query_count}): #{time.round(2)}ms")
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def self.print_by_total_time(result, options = {})
default_options = { sort_method: :total_time }
diff --git a/lib/gitlab/project_authorizations/with_nested_groups.rb b/lib/gitlab/project_authorizations/with_nested_groups.rb
index e3da1634fa5..448c3f3a7d8 100644
--- a/lib/gitlab/project_authorizations/with_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/with_nested_groups.rb
@@ -49,13 +49,11 @@ module Gitlab
.where('p_ns.share_with_group_lock IS FALSE')
]
- union = Gitlab::SQL::Union.new(relations)
-
ProjectAuthorization
.unscoped
.with
.recursive(cte.to_arel)
- .select_from_union(union)
+ .select_from_union(relations)
end
private
diff --git a/lib/gitlab/project_authorizations/without_nested_groups.rb b/lib/gitlab/project_authorizations/without_nested_groups.rb
index 7d0c00c7f36..ed2287dcc7e 100644
--- a/lib/gitlab/project_authorizations/without_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/without_nested_groups.rb
@@ -24,11 +24,9 @@ module Gitlab
user.groups.joins(:shared_projects).select_for_project_authorization
]
- union = Gitlab::SQL::Union.new(relations)
-
ProjectAuthorization
.unscoped
- .select_from_union(union)
+ .select_from_union(relations)
end
end
end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 62f9e538c04..04df881bf03 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class ProjectSearchResults < SearchResults
attr_reader :project, :repository_ref
@@ -29,6 +31,7 @@ module Gitlab
@blobs_count ||= blobs.count
end
+ # rubocop: disable CodeReuse/ActiveRecord
def limited_notes_count
return @limited_notes_count if defined?(@limited_notes_count)
@@ -42,6 +45,7 @@ module Gitlab
@limited_notes_count
end
+ # rubocop: enable CodeReuse/ActiveRecord
def wiki_blobs_count
@wiki_blobs_count ||= wiki_blobs.count
@@ -55,7 +59,8 @@ module Gitlab
ref = nil
filename = nil
basename = nil
- data = ""
+
+ data = []
startline = 0
result.each_line.each_with_index do |line, index|
@@ -76,8 +81,8 @@ module Gitlab
basename: basename,
ref: ref,
startline: startline,
- data: data,
- project_id: project ? project.id : nil
+ data: data.join,
+ project: project
)
end
@@ -118,9 +123,11 @@ module Gitlab
@notes ||= notes_finder(nil)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def notes_finder(type)
NotesFinder.new(project, @current_user, search: query, target_type: type).execute.user.order('updated_at DESC')
end
+ # rubocop: enable CodeReuse/ActiveRecord
def commits
@commits ||= find_commits(query)
diff --git a/lib/gitlab/project_service_logger.rb b/lib/gitlab/project_service_logger.rb
new file mode 100644
index 00000000000..9b0357d3161
--- /dev/null
+++ b/lib/gitlab/project_service_logger.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class ProjectServiceLogger < Gitlab::JsonLogger
+ def self.file_name_noext
+ 'integrations_json'
+ end
+ end
+end
diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb
index 08f6a54776f..3bfd6ee892c 100644
--- a/lib/gitlab/project_template.rb
+++ b/lib/gitlab/project_template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class ProjectTemplate
attr_reader :title, :name, :description, :preview
diff --git a/lib/gitlab/project_transfer.rb b/lib/gitlab/project_transfer.rb
index 690c38737c0..d8f1d1e2316 100644
--- a/lib/gitlab/project_transfer.rb
+++ b/lib/gitlab/project_transfer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# This class is used to move local, unhashed files owned by projects to their new location
class ProjectTransfer
diff --git a/lib/gitlab/prometheus/additional_metrics_parser.rb b/lib/gitlab/prometheus/additional_metrics_parser.rb
index bb1172f82a1..a240d090074 100644
--- a/lib/gitlab/prometheus/additional_metrics_parser.rb
+++ b/lib/gitlab/prometheus/additional_metrics_parser.rb
@@ -5,7 +5,7 @@ module Gitlab
MUTEX = Mutex.new
extend self
- def load_groups_from_yaml(file_name = 'additional_metrics.yml')
+ def load_groups_from_yaml(file_name)
yaml_metrics_raw(file_name).map(&method(:group_from_entry))
end
diff --git a/lib/gitlab/prometheus/metric_group.rb b/lib/gitlab/prometheus/metric_group.rb
index e91c6fb2e27..d696a8fc00c 100644
--- a/lib/gitlab/prometheus/metric_group.rb
+++ b/lib/gitlab/prometheus/metric_group.rb
@@ -4,10 +4,13 @@ module Gitlab
include ActiveModel::Model
attr_accessor :name, :priority, :metrics
+
validates :name, :priority, :metrics, presence: true
def self.common_metrics
- AdditionalMetricsParser.load_groups_from_yaml
+ ::PrometheusMetric.common.group_by(&:group_title).map do |name, metrics|
+ MetricGroup.new(name: name, priority: 0, metrics: metrics.map(&:to_query_metric))
+ end
end
# EE only
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
index 8534afcc849..fa86d2dfd6c 100644
--- a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
+++ b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
@@ -4,6 +4,7 @@ module Gitlab
class AdditionalMetricsDeploymentQuery < BaseQuery
include QueryAdditionalMetrics
+ # rubocop: disable CodeReuse/ActiveRecord
def query(deployment_id)
Deployment.find_by(id: deployment_id).try do |deployment|
query_metrics(
@@ -17,6 +18,7 @@ module Gitlab
)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
index e3af217b202..09f8f1103d2 100644
--- a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
+++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
@@ -4,6 +4,7 @@ module Gitlab
class AdditionalMetricsEnvironmentQuery < BaseQuery
include QueryAdditionalMetrics
+ # rubocop: disable CodeReuse/ActiveRecord
def query(environment_id)
::Environment.find_by(id: environment_id).try do |environment|
query_metrics(
@@ -13,6 +14,7 @@ module Gitlab
)
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/prometheus/queries/deployment_query.rb b/lib/gitlab/prometheus/queries/deployment_query.rb
index c2626581897..3a609a795ba 100644
--- a/lib/gitlab/prometheus/queries/deployment_query.rb
+++ b/lib/gitlab/prometheus/queries/deployment_query.rb
@@ -2,6 +2,7 @@ module Gitlab
module Prometheus
module Queries
class DeploymentQuery < BaseQuery
+ # rubocop: disable CodeReuse/ActiveRecord
def query(deployment_id)
Deployment.find_by(id: deployment_id).try do |deployment|
environment_slug = deployment.environment.slug
@@ -25,6 +26,7 @@ module Gitlab
}
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def self.transform_reactive_result(result)
result[:metrics] = result.delete :data
diff --git a/lib/gitlab/prometheus/queries/environment_query.rb b/lib/gitlab/prometheus/queries/environment_query.rb
index b62910c8de6..4d8b136d7af 100644
--- a/lib/gitlab/prometheus/queries/environment_query.rb
+++ b/lib/gitlab/prometheus/queries/environment_query.rb
@@ -2,6 +2,7 @@ module Gitlab
module Prometheus
module Queries
class EnvironmentQuery < BaseQuery
+ # rubocop: disable CodeReuse/ActiveRecord
def query(environment_id)
::Environment.find_by(id: environment_id).try do |environment|
environment_slug = environment.slug
@@ -19,6 +20,7 @@ module Gitlab
}
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
def self.transform_reactive_result(result)
result[:metrics] = result.delete :data
diff --git a/lib/gitlab/prometheus_client.rb b/lib/gitlab/prometheus_client.rb
index b66253a10e0..45828c77a33 100644
--- a/lib/gitlab/prometheus_client.rb
+++ b/lib/gitlab/prometheus_client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# Helper methods to interact with Prometheus network services & resources
class PrometheusClient
diff --git a/lib/gitlab/protocol_access.rb b/lib/gitlab/protocol_access.rb
index 2819c7d062c..efeb1e07d49 100644
--- a/lib/gitlab/protocol_access.rb
+++ b/lib/gitlab/protocol_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module ProtocolAccess
def self.allowed?(protocol)
diff --git a/lib/gitlab/proxy_http_connection_adapter.rb b/lib/gitlab/proxy_http_connection_adapter.rb
index d682289b632..a64cb47e77e 100644
--- a/lib/gitlab/proxy_http_connection_adapter.rb
+++ b/lib/gitlab/proxy_http_connection_adapter.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
# This class is part of the Gitlab::HTTP wrapper. Depending on the value
# of the global setting allow_local_requests_from_hooks_and_services this adapter
# will allow/block connection to internal IPs and/or urls.
#
-# This functionality can be overriden by providing the setting the option
+# This functionality can be overridden by providing the setting the option
# allow_local_requests = true in the request. For example:
# Gitlab::HTTP.get('http://www.gitlab.com', allow_local_requests: true)
#
diff --git a/lib/gitlab/query_limiting.rb b/lib/gitlab/query_limiting.rb
index 9f69a9e4a39..31e6b120e45 100644
--- a/lib/gitlab/query_limiting.rb
+++ b/lib/gitlab/query_limiting.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module QueryLimiting
# Returns true if we should enable tracking of query counts.
diff --git a/lib/gitlab/quick_actions/command_definition.rb b/lib/gitlab/quick_actions/command_definition.rb
index 96415271316..c682eb22890 100644
--- a/lib/gitlab/quick_actions/command_definition.rb
+++ b/lib/gitlab/quick_actions/command_definition.rb
@@ -2,13 +2,14 @@ module Gitlab
module QuickActions
class CommandDefinition
attr_accessor :name, :aliases, :description, :explanation, :params,
- :condition_block, :parse_params_block, :action_block
+ :condition_block, :parse_params_block, :action_block, :warning
def initialize(name, attributes = {})
@name = name
@aliases = attributes[:aliases] || []
@description = attributes[:description] || ''
+ @warning = attributes[:warning] || ''
@explanation = attributes[:explanation] || ''
@params = attributes[:params] || []
@condition_block = attributes[:condition_block]
@@ -33,11 +34,13 @@ module Gitlab
def explain(context, arg)
return unless available?(context)
- if explanation.respond_to?(:call)
- execute_block(explanation, context, arg)
- else
- explanation
- end
+ message = if explanation.respond_to?(:call)
+ execute_block(explanation, context, arg)
+ else
+ explanation
+ end
+
+ warning.empty? ? message : "#{message} (#{warning})"
end
def execute(context, arg)
@@ -61,6 +64,7 @@ module Gitlab
name: name,
aliases: aliases,
description: desc,
+ warning: warning,
params: prms
}
end
diff --git a/lib/gitlab/quick_actions/dsl.rb b/lib/gitlab/quick_actions/dsl.rb
index d82dccd0db5..192c7ec2ff5 100644
--- a/lib/gitlab/quick_actions/dsl.rb
+++ b/lib/gitlab/quick_actions/dsl.rb
@@ -31,6 +31,10 @@ module Gitlab
@description = block_given? ? block : text
end
+ def warning(message = '')
+ @warning = message
+ end
+
# Allows to define params for the next quick action.
# These params are shown in the autocomplete menu.
#
@@ -133,6 +137,7 @@ module Gitlab
name,
aliases: aliases,
description: @description,
+ warning: @warning,
explanation: @explanation,
params: @params,
condition_block: @condition_block,
@@ -150,6 +155,7 @@ module Gitlab
@explanation = nil
@params = nil
@condition_block = nil
+ @warning = nil
@parse_params_block = nil
end
end
diff --git a/lib/gitlab/quick_actions/extractor.rb b/lib/gitlab/quick_actions/extractor.rb
index 30c6806b68e..59f8dd889aa 100644
--- a/lib/gitlab/quick_actions/extractor.rb
+++ b/lib/gitlab/quick_actions/extractor.rb
@@ -29,7 +29,7 @@ module Gitlab
# commands = extractor.extract_commands(msg) #=> [['labels', '~foo ~"bar baz"']]
# msg #=> "hello\nworld"
# ```
- def extract_commands(content)
+ def extract_commands(content, only: nil)
return [content, []] unless content
content = content.dup
@@ -37,7 +37,7 @@ module Gitlab
commands = []
content.delete!("\r")
- content.gsub!(commands_regex) do
+ content.gsub!(commands_regex(only: only)) do
if $~[:cmd]
commands << [$~[:cmd].downcase, $~[:arg]].reject(&:blank?)
''
@@ -60,8 +60,8 @@ module Gitlab
# It looks something like:
#
# /^\/(?<cmd>close|reopen|...)(?:( |$))(?<arg>[^\/\n]*)(?:\n|$)/
- def commands_regex
- names = command_names.map(&:to_s)
+ def commands_regex(only:)
+ names = command_names(limit_to_commands: only).map(&:to_s)
@commands_regex ||= %r{
(?<code>
@@ -133,10 +133,14 @@ module Gitlab
[content, commands]
end
- def command_names
+ def command_names(limit_to_commands:)
command_definitions.flat_map do |command|
next if command.noop?
+ if limit_to_commands && (command.all_names & limit_to_commands).empty?
+ next
+ end
+
command.all_names
end.compact
end
diff --git a/lib/gitlab/recaptcha.rb b/lib/gitlab/recaptcha.rb
index c9efa28d7e7..6559c3e3c57 100644
--- a/lib/gitlab/recaptcha.rb
+++ b/lib/gitlab/recaptcha.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Recaptcha
def self.load_configurations!
diff --git a/lib/gitlab/reference_counter.rb b/lib/gitlab/reference_counter.rb
index bb26f1b610a..d2dbc6f5ef5 100644
--- a/lib/gitlab/reference_counter.rb
+++ b/lib/gitlab/reference_counter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class ReferenceCounter
REFERENCE_EXPIRE_TIME = 600
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 9ff82d628c0..00f817c2399 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor < Banzai::ReferenceExtractor
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 0f26fcfe8cb..7a1a2eaf6c0 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Regex
extend self
diff --git a/lib/gitlab/repo_path.rb b/lib/gitlab/repo_path.rb
index 4888184403c..202d310e237 100644
--- a/lib/gitlab/repo_path.rb
+++ b/lib/gitlab/repo_path.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module RepoPath
NotFoundError = Class.new(StandardError)
diff --git a/lib/gitlab/repository_cache.rb b/lib/gitlab/repository_cache.rb
index b1bf3ca4143..6b0808f5304 100644
--- a/lib/gitlab/repository_cache.rb
+++ b/lib/gitlab/repository_cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Interface to the Redis-backed cache store
module Gitlab
class RepositoryCache
@@ -5,13 +7,13 @@ module Gitlab
def initialize(repository, extra_namespace: nil, backend: Rails.cache)
@repository = repository
- @namespace = "#{repository.full_path}:#{repository.project.id}"
- @namespace += ":#{extra_namespace}" if extra_namespace
+ @namespace = "project:#{repository.project.id}"
+ @namespace = "#{@namespace}:#{extra_namespace}" if extra_namespace
@backend = backend
end
def cache_key(type)
- "#{type}:#{namespace}"
+ "#{namespace}:#{type}"
end
def expire(key)
@@ -29,5 +31,21 @@ module Gitlab
def read(key)
backend.read(cache_key(key))
end
+
+ def write(key, value)
+ backend.write(cache_key(key), value)
+ end
+
+ def fetch_without_caching_false(key, &block)
+ value = read(key)
+ return value if value
+
+ value = yield
+
+ # Don't cache false values
+ write(key, value) if value
+
+ value
+ end
end
end
diff --git a/lib/gitlab/repository_cache_adapter.rb b/lib/gitlab/repository_cache_adapter.rb
index 2ec871f0754..931298b5117 100644
--- a/lib/gitlab/repository_cache_adapter.rb
+++ b/lib/gitlab/repository_cache_adapter.rb
@@ -1,23 +1,82 @@
+# frozen_string_literal: true
+
module Gitlab
module RepositoryCacheAdapter
extend ActiveSupport::Concern
+ include Gitlab::Utils::StrongMemoize
class_methods do
- # Wraps around the given method and caches its output in Redis and an instance
- # variable.
+ # Caches and strongly memoizes the method.
#
# This only works for methods that do not take any arguments.
- def cache_method(name, fallback: nil, memoize_only: false)
- original = :"_uncached_#{name}"
+ #
+ # name - The name of the method to be cached.
+ # fallback - A value to fall back to if the repository does not exist, or
+ # in case of a Git error. Defaults to nil.
+ def cache_method(name, fallback: nil)
+ uncached_name = alias_uncached_method(name)
+
+ define_method(name) do
+ cache_method_output(name, fallback: fallback) do
+ __send__(uncached_name) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
- alias_method(original, name)
+ # Caches truthy values from the method. All values are strongly memoized,
+ # and cached in RequestStore.
+ #
+ # Currently only used to cache `exists?` since stale false values are
+ # particularly troublesome. This can occur, for example, when an NFS mount
+ # is temporarily down.
+ #
+ # This only works for methods that do not take any arguments.
+ #
+ # name - The name of the method to be cached.
+ def cache_method_asymmetrically(name)
+ uncached_name = alias_uncached_method(name)
define_method(name) do
- cache_method_output(name, fallback: fallback, memoize_only: memoize_only) do
- __send__(original) # rubocop:disable GitlabSecurity/PublicSend
+ cache_method_output_asymmetrically(name) do
+ __send__(uncached_name) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
+
+ # Strongly memoizes the method.
+ #
+ # This only works for methods that do not take any arguments.
+ #
+ # name - The name of the method to be memoized.
+ # fallback - A value to fall back to if the repository does not exist, or
+ # in case of a Git error. Defaults to nil. The fallback value
+ # is not memoized.
+ def memoize_method(name, fallback: nil)
+ uncached_name = alias_uncached_method(name)
+
+ define_method(name) do
+ memoize_method_output(name, fallback: fallback) do
+ __send__(uncached_name) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+
+ # Prepends "_uncached_" to the target method name
+ #
+ # Returns the uncached method name
+ def alias_uncached_method(name)
+ uncached_name = :"_uncached_#{name}"
+
+ alias_method(uncached_name, name)
+
+ uncached_name
+ end
+ end
+
+ # RequestStore-backed RepositoryCache to be used. Should be overridden by
+ # the including class
+ def request_store_cache
+ raise NotImplementedError
end
# RepositoryCache to be used. Should be overridden by the including class
@@ -30,65 +89,93 @@ module Gitlab
raise NotImplementedError
end
- # Caches the supplied block both in a cache and in an instance variable.
+ # Caches and strongly memoizes the supplied block.
#
- # The cache key and instance variable are named the same way as the value of
- # the `key` argument.
+ # name - The name of the method to be cached.
+ # fallback - A value to fall back to if the repository does not exist, or
+ # in case of a Git error. Defaults to nil.
+ def cache_method_output(name, fallback: nil, &block)
+ memoize_method_output(name, fallback: fallback) do
+ cache.fetch(name, &block)
+ end
+ end
+
+ # Caches truthy values from the supplied block. All values are strongly
+ # memoized, and cached in RequestStore.
#
- # This method will return `nil` if the corresponding instance variable is also
- # set to `nil`. This ensures we don't keep yielding the block when it returns
- # `nil`.
+ # Currently only used to cache `exists?` since stale false values are
+ # particularly troublesome. This can occur, for example, when an NFS mount
+ # is temporarily down.
#
- # key - The name of the key to cache the data in.
- # fallback - A value to fall back to in the event of a Git error.
- def cache_method_output(key, fallback: nil, memoize_only: false, &block)
- ivar = cache_instance_variable_name(key)
-
- if instance_variable_defined?(ivar)
- instance_variable_get(ivar)
- else
- # If the repository doesn't exist and a fallback was specified we return
- # that value inmediately. This saves us Rugged/gRPC invocations.
- return fallback unless fallback.nil? || cache.repository.exists?
-
- begin
- value =
- if memoize_only
- yield
- else
- cache.fetch(key, &block)
- end
-
- instance_variable_set(ivar, value)
- rescue Gitlab::Git::Repository::NoRepository
- # Even if the above `#exists?` check passes these errors might still
- # occur (for example because of a non-existing HEAD). We want to
- # gracefully handle this and not cache anything
- fallback
+ # name - The name of the method to be cached.
+ def cache_method_output_asymmetrically(name, &block)
+ memoize_method_output(name) do
+ request_store_cache.fetch(name) do
+ cache.fetch_without_caching_false(name, &block)
end
end
end
+ # Strongly memoizes the supplied block.
+ #
+ # name - The name of the method to be memoized.
+ # fallback - A value to fall back to if the repository does not exist, or
+ # in case of a Git error. Defaults to nil. The fallback value is
+ # not memoized.
+ def memoize_method_output(name, fallback: nil, &block)
+ no_repository_fallback(name, fallback: fallback) do
+ strong_memoize(memoizable_name(name), &block)
+ end
+ end
+
+ # Returns the fallback value if the repository does not exist
+ def no_repository_fallback(name, fallback: nil, &block)
+ # Avoid unnecessary gRPC invocations
+ return fallback if fallback && fallback_early?(name)
+
+ yield
+ rescue Gitlab::Git::Repository::NoRepository
+ # Even if the `#exists?` check in `fallback_early?` passes, these errors
+ # might still occur (for example because of a non-existing HEAD). We
+ # want to gracefully handle this and not memoize anything.
+ fallback
+ end
+
# Expires the caches of a specific set of methods
def expire_method_caches(methods)
- methods.each do |key|
- unless cached_methods.include?(key.to_sym)
- Rails.logger.error "Requested to expire non-existent method '#{key}' for Repository"
+ methods.each do |name|
+ unless cached_methods.include?(name.to_sym)
+ Rails.logger.error "Requested to expire non-existent method '#{name}' for Repository"
next
end
- cache.expire(key)
+ cache.expire(name)
- ivar = cache_instance_variable_name(key)
-
- remove_instance_variable(ivar) if instance_variable_defined?(ivar)
+ clear_memoization(memoizable_name(name))
end
+
+ expire_request_store_method_caches(methods)
end
private
- def cache_instance_variable_name(key)
- :"@#{key.to_s.tr('?!', '')}"
+ def memoizable_name(name)
+ "#{name.to_s.tr('?!', '')}"
+ end
+
+ def expire_request_store_method_caches(methods)
+ methods.each do |name|
+ request_store_cache.expire(name)
+ end
+ end
+
+ # All cached repository methods depend on the existence of a Git repository,
+ # so if the repository doesn't exist, we already know not to call it.
+ def fallback_early?(method_name)
+ # Avoid infinite loop
+ return false if method_name == :exists?
+
+ !exists?
end
end
end
diff --git a/lib/gitlab/repository_check_logger.rb b/lib/gitlab/repository_check_logger.rb
index 485b596ca57..e90b0a002af 100644
--- a/lib/gitlab/repository_check_logger.rb
+++ b/lib/gitlab/repository_check_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class RepositoryCheckLogger < Gitlab::Logger
def self.file_name_noext
diff --git a/lib/gitlab/request_context.rb b/lib/gitlab/request_context.rb
index fef536ecb0b..f8f8ec789ce 100644
--- a/lib/gitlab/request_context.rb
+++ b/lib/gitlab/request_context.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
module Gitlab
class RequestContext
class << self
def client_ip
- RequestStore[:client_ip]
+ Gitlab::SafeRequestStore[:client_ip]
end
end
@@ -13,7 +15,7 @@ module Gitlab
def call(env)
req = Rack::Request.new(env)
- RequestStore[:client_ip] = req.ip
+ Gitlab::SafeRequestStore[:client_ip] = req.ip
@app.call(env)
end
diff --git a/lib/gitlab/request_forgery_protection.rb b/lib/gitlab/request_forgery_protection.rb
index a502ad8a541..b1e478093d3 100644
--- a/lib/gitlab/request_forgery_protection.rb
+++ b/lib/gitlab/request_forgery_protection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A module to check CSRF tokens in requests.
# It's used in API helpers and OmniAuth.
# Usage: GitLab::RequestForgeryProtection.call(env)
diff --git a/lib/gitlab/request_profiler.rb b/lib/gitlab/request_profiler.rb
index 0c9ab759e81..64593153686 100644
--- a/lib/gitlab/request_profiler.rb
+++ b/lib/gitlab/request_profiler.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'fileutils'
module Gitlab
diff --git a/lib/gitlab/route_map.rb b/lib/gitlab/route_map.rb
index f3952657983..a555bf1d812 100644
--- a/lib/gitlab/route_map.rb
+++ b/lib/gitlab/route_map.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class RouteMap
FormatError = Class.new(StandardError)
diff --git a/lib/gitlab/routing.rb b/lib/gitlab/routing.rb
index 2c994536060..3b05f181ed2 100644
--- a/lib/gitlab/routing.rb
+++ b/lib/gitlab/routing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Routing
extend ActiveSupport::Concern
@@ -47,7 +49,7 @@ module Gitlab
#
# `request.fullpath` includes the querystring
new_path = request.path.sub(%r{/#{path}(/*)(?!.*#{path})}, "/-/#{path}\\1")
- new_path << "?#{request.query_string}" if request.query_string.present?
+ new_path = "#{new_path}?#{request.query_string}" if request.query_string.present?
new_path
end
diff --git a/lib/gitlab/safe_request_store.rb b/lib/gitlab/safe_request_store.rb
new file mode 100644
index 00000000000..4e82353adb6
--- /dev/null
+++ b/lib/gitlab/safe_request_store.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SafeRequestStore
+ NULL_STORE = Gitlab::NullRequestStore.new
+
+ class << self
+ # These methods should always run directly against RequestStore
+ delegate :clear!, :begin!, :end!, :active?, to: :RequestStore
+
+ # These methods will run against NullRequestStore if RequestStore is disabled
+ delegate :read, :[], :write, :[]=, :exist?, :fetch, :delete, to: :store
+ end
+
+ def self.store
+ if RequestStore.active?
+ RequestStore
+ else
+ NULL_STORE
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 1e45d074e0a..458737f31eb 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -1,9 +1,13 @@
+# frozen_string_literal: true
+
module Gitlab
class SearchResults
class FoundBlob
include EncodingHelper
+ include Presentable
+ include BlobLanguageFromGitAttributes
- attr_reader :id, :filename, :basename, :ref, :startline, :data, :project_id
+ attr_reader :id, :filename, :basename, :ref, :startline, :data, :project
def initialize(opts = {})
@id = opts.fetch(:id, nil)
@@ -13,6 +17,11 @@ module Gitlab
@startline = opts.fetch(:startline, nil)
@data = encode_utf8(opts.fetch(:data, nil))
@per_page = opts.fetch(:per_page, 20)
+ @project = opts.fetch(:project, nil)
+ # Some caller does not have project object (e.g. elastic search),
+ # yet they can trigger many calls in one go,
+ # causing duplicated queries.
+ # Allow those to just pass project_id instead.
@project_id = opts.fetch(:project_id, nil)
end
@@ -20,8 +29,12 @@ module Gitlab
filename
end
- def no_highlighting?
- false
+ def project_id
+ @project_id || @project&.id
+ end
+
+ def present
+ super(presenter_class: BlobPresenter)
end
end
@@ -62,10 +75,13 @@ module Gitlab
without_count ? collection.without_count : collection
end
+ # rubocop: disable CodeReuse/ActiveRecord
def limited_projects_count
@limited_projects_count ||= projects.limit(count_limit).count
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def limited_issues_count
return @limited_issues_count if @limited_issues_count
@@ -77,14 +93,19 @@ module Gitlab
sum = issues(public_only: true).limit(count_limit).count
@limited_issues_count = sum < count_limit ? issues.limit(count_limit).count : sum
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def limited_merge_requests_count
@limited_merge_requests_count ||= merge_requests.limit(count_limit).count
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def limited_milestones_count
@limited_milestones_count ||= milestones.limit(count_limit).count
end
+ # rubocop: enable CodeReuse/ActiveRecord
def single_commit_result?
false
@@ -100,6 +121,7 @@ module Gitlab
limit_projects.search(query)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def issues(finder_params = {})
issues = IssuesFinder.new(current_user, finder_params).execute
unless default_project_filter
@@ -115,13 +137,17 @@ module Gitlab
issues.reorder('updated_at DESC')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def milestones
milestones = Milestone.where(project_id: project_ids_relation)
milestones = milestones.search(query)
milestones.reorder('updated_at DESC')
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def merge_requests
merge_requests = MergeRequestsFinder.new(current_user).execute
unless default_project_filter
@@ -137,13 +163,16 @@ module Gitlab
merge_requests.reorder('updated_at DESC')
end
+ # rubocop: enable CodeReuse/ActiveRecord
def default_scope
'projects'
end
+ # rubocop: disable CodeReuse/ActiveRecord
def project_ids_relation
limit_projects.select(:id).reorder(nil)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb
index 98f005cb61b..84a51773276 100644
--- a/lib/gitlab/seeder.rb
+++ b/lib/gitlab/seeder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# :nocov:
module DeliverNever
def deliver_later
diff --git a/lib/gitlab/sentry.rb b/lib/gitlab/sentry.rb
index 6381e94c1d2..8079c5882c4 100644
--- a/lib/gitlab/sentry.rb
+++ b/lib/gitlab/sentry.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Sentry
def self.enabled?
@@ -5,7 +7,7 @@ module Gitlab
end
def self.context(current_user = nil)
- return unless self.enabled?
+ return unless enabled?
Raven.tags_context(locale: I18n.locale)
@@ -27,14 +29,22 @@ module Gitlab
#
# Provide an issue URL for follow up.
def self.track_exception(exception, issue_url: nil, extra: {})
+ track_acceptable_exception(exception, issue_url: issue_url, extra: extra)
+
+ raise exception if should_raise?
+ end
+
+ # This should be used when you do not want to raise an exception in
+ # development and test. If you need development and test to behave
+ # just the same as production you can use this instead of
+ # track_exception.
+ def self.track_acceptable_exception(exception, issue_url: nil, extra: {})
if enabled?
extra[:issue_url] = issue_url if issue_url
context # Make sure we've set everything we know in the context
Raven.capture_exception(exception, extra: extra)
end
-
- raise exception if should_raise?
end
def self.program_context
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
index b2d75aac1d0..2b7e12639be 100644
--- a/lib/gitlab/setup_helper.rb
+++ b/lib/gitlab/setup_helper.rb
@@ -1,3 +1,7 @@
+# frozen_string_literal: true
+
+require 'toml-rb'
+
module Gitlab
module SetupHelper
class << self
@@ -9,7 +13,7 @@ module Gitlab
# because it uses a Unix socket.
# For development and testing purposes, an extra storage is added to gitaly,
# which is not known to Rails, but must be explicitly stubbed.
- def gitaly_configuration_toml(gitaly_dir, gitaly_ruby: true)
+ def gitaly_configuration_toml(gitaly_dir, storage_paths, gitaly_ruby: true)
storages = []
address = nil
@@ -24,14 +28,14 @@ module Gitlab
address = val['gitaly_address']
end
- # https://gitlab.com/gitlab-org/gitaly/issues/1238
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- storages << { name: key, path: val.legacy_disk_path }
- end
+ storages << { name: key, path: storage_paths[key] }
end
if Rails.env.test?
- storages << { name: 'test_second_storage', path: Rails.root.join('tmp', 'tests', 'second_storage').to_s }
+ storage_path = Rails.root.join('tmp', 'tests', 'second_storage').to_s
+
+ FileUtils.mkdir(storage_path) unless File.exist?(storage_path)
+ storages << { name: 'test_second_storage', path: storage_path }
end
config = { socket_path: address.sub(/\Aunix:/, ''), storage: storages }
@@ -44,12 +48,12 @@ module Gitlab
end
# rubocop:disable Rails/Output
- def create_gitaly_configuration(dir, force: false)
+ def create_gitaly_configuration(dir, storage_paths, force: false)
config_path = File.join(dir, 'config.toml')
FileUtils.rm_f(config_path) if force
File.open(config_path, File::WRONLY | File::CREAT | File::EXCL) do |f|
- f.puts gitaly_configuration_toml(dir)
+ f.puts gitaly_configuration_toml(dir, storage_paths)
end
rescue Errno::EEXIST
puts "Skipping config.toml generation:"
diff --git a/lib/gitlab/shard_health_cache.rb b/lib/gitlab/shard_health_cache.rb
index 3f03f46d4b1..6612347438e 100644
--- a/lib/gitlab/shard_health_cache.rb
+++ b/lib/gitlab/shard_health_cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class ShardHealthCache
HEALTHY_SHARDS_KEY = 'gitlab-healthy-shards'.freeze
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index a17cd27e82d..c6a6fb9b5ce 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitaly note: SSH key operations are not part of Gitaly so will never be migrated.
require 'securerandom'
@@ -106,23 +108,6 @@ module Gitlab
success
end
- # Fetch remote for repository
- #
- # repository - an instance of Git::Repository
- # remote - remote name
- # ssh_auth - SSH known_hosts data and a private key to use for public-key authentication
- # forced - should we use --force flag?
- # no_tags - should we use --no-tags flag?
- #
- # Ex.
- # fetch_remote(my_repo, "upstream")
- #
- def fetch_remote(repository, remote, ssh_auth: nil, forced: false, no_tags: false, prune: true)
- wrapped_gitaly_errors do
- repository.gitaly_repository_client.fetch_remote(remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, timeout: git_timeout, prune: prune)
- end
- end
-
# Move repository reroutes to mv_directory which is an alias for
# mv_namespace. Given the underlying implementation is a move action,
# indescriminate of what the folders might be.
@@ -225,6 +210,7 @@ module Gitlab
# Ex.
# remove_keys_not_found_in_db
#
+ # rubocop: disable CodeReuse/ActiveRecord
def remove_keys_not_found_in_db
return unless self.authorized_keys_enabled?
@@ -243,6 +229,7 @@ module Gitlab
end
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Iterate over all ssh key IDs from gitlab shell, in batches
#
@@ -326,9 +313,11 @@ module Gitlab
# exists?(storage, 'gitlab')
# exists?(storage, 'gitlab/cookies.git')
#
+ # rubocop: disable CodeReuse/ActiveRecord
def exists?(storage, dir_name)
Gitlab::GitalyClient::NamespaceService.new(storage).exists?(dir_name)
end
+ # rubocop: enable CodeReuse/ActiveRecord
protected
@@ -368,15 +357,6 @@ module Gitlab
private
- def gitlab_projects(shard_name, disk_path)
- Gitlab::Git::GitlabProjects.new(
- shard_name,
- disk_path,
- global_hooks_path: Gitlab.config.gitlab_shell.hooks_path,
- logger: Rails.logger
- )
- end
-
def gitlab_shell_fast_execute(cmd)
output, status = gitlab_shell_fast_execute_helper(cmd)
diff --git a/lib/gitlab/shell_adapter.rb b/lib/gitlab/shell_adapter.rb
index 053dd4ab9e0..59fc6ee8dc8 100644
--- a/lib/gitlab/shell_adapter.rb
+++ b/lib/gitlab/shell_adapter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == GitLab Shell mixin
#
# Provide a shortcut to Gitlab::Shell instance by gitlab_shell
diff --git a/lib/gitlab/sherlock.rb b/lib/gitlab/sherlock.rb
index 6360527a7aa..a1471c9de47 100644
--- a/lib/gitlab/sherlock.rb
+++ b/lib/gitlab/sherlock.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'securerandom'
module Gitlab
diff --git a/lib/gitlab/sidekiq_config.rb b/lib/gitlab/sidekiq_config.rb
index c3d7814551c..01f60a98ad8 100644
--- a/lib/gitlab/sidekiq_config.rb
+++ b/lib/gitlab/sidekiq_config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'yaml'
require 'set'
diff --git a/lib/gitlab/sidekiq_logger.rb b/lib/gitlab/sidekiq_logger.rb
index c1dab87a432..ce82a6f04bb 100644
--- a/lib/gitlab/sidekiq_logger.rb
+++ b/lib/gitlab/sidekiq_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class SidekiqLogger < Gitlab::Logger
def self.file_name_noext
diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb
index a1f689d94d9..583a970bf4e 100644
--- a/lib/gitlab/sidekiq_status.rb
+++ b/lib/gitlab/sidekiq_status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# The SidekiqStatus module and its child classes can be used for checking if a
# Sidekiq job has been processed or not.
diff --git a/lib/gitlab/sidekiq_throttler.rb b/lib/gitlab/sidekiq_throttler.rb
deleted file mode 100644
index 5512afa45a8..00000000000
--- a/lib/gitlab/sidekiq_throttler.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Gitlab
- class SidekiqThrottler
- class << self
- def execute!
- if Gitlab::CurrentSettings.sidekiq_throttling_enabled?
- require 'sidekiq-limit_fetch'
-
- Gitlab::CurrentSettings.current_application_settings.sidekiq_throttling_queues.each do |queue|
- Sidekiq::Queue[queue].limit = queue_limit
- end
- end
- end
-
- private
-
- def queue_limit
- @queue_limit ||=
- begin
- factor = Gitlab::CurrentSettings.current_application_settings.sidekiq_throttling_factor
- (factor * Sidekiq.options[:concurrency]).ceil
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/sidekiq_versioning.rb b/lib/gitlab/sidekiq_versioning.rb
index 9683214ec18..8164a5a9d7a 100644
--- a/lib/gitlab/sidekiq_versioning.rb
+++ b/lib/gitlab/sidekiq_versioning.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module SidekiqVersioning
def self.install!
diff --git a/lib/gitlab/slash_commands/base_command.rb b/lib/gitlab/slash_commands/base_command.rb
index 466554e398c..0c76378d51c 100644
--- a/lib/gitlab/slash_commands/base_command.rb
+++ b/lib/gitlab/slash_commands/base_command.rb
@@ -40,9 +40,11 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def find_by_iid(iid)
collection.find_by(iid: iid)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/slash_commands/deploy.rb b/lib/gitlab/slash_commands/deploy.rb
index 93e00ab75a1..b308fd9637f 100644
--- a/lib/gitlab/slash_commands/deploy.rb
+++ b/lib/gitlab/slash_commands/deploy.rb
@@ -36,6 +36,7 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def find_action(from, to)
environment = project.environments.find_by(name: from)
return unless environment
@@ -50,6 +51,7 @@ module Gitlab
actions.first
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/slash_commands/issue_new.rb b/lib/gitlab/slash_commands/issue_new.rb
index 25f965e843d..6396b828dc7 100644
--- a/lib/gitlab/slash_commands/issue_new.rb
+++ b/lib/gitlab/slash_commands/issue_new.rb
@@ -3,7 +3,7 @@ module Gitlab
class IssueNew < IssueCommand
def self.match(text)
# we can not match \n with the dot by passing the m modifier as than
- # the title and description are not seperated
+ # the title and description are not separated
/\Aissue\s+(new|create)\s+(?<title>[^\n]*)\n*(?<description>(.|\n)*)/.match(text)
end
diff --git a/lib/gitlab/slash_commands/issue_search.rb b/lib/gitlab/slash_commands/issue_search.rb
index acba84b54b4..ee78f0f832e 100644
--- a/lib/gitlab/slash_commands/issue_search.rb
+++ b/lib/gitlab/slash_commands/issue_search.rb
@@ -9,6 +9,7 @@ module Gitlab
"issue search <your query>"
end
+ # rubocop: disable CodeReuse/ActiveRecord
def execute(match)
issues = collection.search(match[:query]).limit(QUERY_LIMIT)
@@ -18,6 +19,7 @@ module Gitlab
Presenters::Access.new(issues).not_found
end
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb
index 4f86b3e8f73..e360b552f89 100644
--- a/lib/gitlab/snippet_search_results.rb
+++ b/lib/gitlab/snippet_search_results.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class SnippetSearchResults < SearchResults
include SnippetsHelper
@@ -30,13 +32,17 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def snippet_titles
limit_snippets.search(query).order('updated_at DESC').includes(:author)
end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
def snippet_blobs
limit_snippets.search_code(query).order('updated_at DESC').includes(:author)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def default_scope
'snippet_blobs'
diff --git a/lib/gitlab/ssh_public_key.rb b/lib/gitlab/ssh_public_key.rb
index 6f63ea91ae8..47571239b5c 100644
--- a/lib/gitlab/ssh_public_key.rb
+++ b/lib/gitlab/ssh_public_key.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class SSHPublicKey
Technology = Struct.new(:name, :key_class, :supported_sizes)
@@ -26,7 +28,7 @@ module Gitlab
return key_content if parts.empty?
- parts.each_with_object("#{ssh_type} ").with_index do |(part, content), index|
+ parts.each_with_object(+"#{ssh_type} ").with_index do |(part, content), index|
content << part
if Gitlab::SSHPublicKey.new(content).valid?
diff --git a/lib/gitlab/storage_check.rb b/lib/gitlab/storage_check.rb
deleted file mode 100644
index fe81513c9ec..00000000000
--- a/lib/gitlab/storage_check.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative 'storage_check/cli'
-require_relative 'storage_check/gitlab_caller'
-require_relative 'storage_check/option_parser'
-require_relative 'storage_check/response'
-
-module Gitlab
- module StorageCheck
- ENDPOINT = '/-/storage_check'.freeze
- Options = Struct.new(:target, :token, :interval, :dryrun)
- end
-end
diff --git a/lib/gitlab/storage_check/cli.rb b/lib/gitlab/storage_check/cli.rb
deleted file mode 100644
index 9b64c8e033a..00000000000
--- a/lib/gitlab/storage_check/cli.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-module Gitlab
- module StorageCheck
- class CLI
- def self.start!(args)
- runner = new(Gitlab::StorageCheck::OptionParser.parse!(args))
- runner.start_loop
- end
-
- attr_reader :logger, :options
-
- def initialize(options)
- @options = options
- @logger = Logger.new(STDOUT)
- end
-
- def start_loop
- logger.info "Checking #{options.target} every #{options.interval} seconds"
-
- if options.dryrun
- logger.info "Dryrun, exiting..."
- return
- end
-
- begin
- loop do
- response = GitlabCaller.new(options).call!
- log_response(response)
- update_settings(response)
-
- sleep options.interval
- end
- rescue Interrupt
- logger.info "Ending storage-check"
- end
- end
-
- def update_settings(response)
- previous_interval = options.interval
-
- if response.valid?
- options.interval = response.check_interval || previous_interval
- end
-
- if previous_interval != options.interval
- logger.info "Interval changed: #{options.interval} seconds"
- end
- end
-
- def log_response(response)
- unless response.valid?
- return logger.error("Invalid response checking nfs storage: #{response.http_response.inspect}")
- end
-
- if response.responsive_shards.any?
- logger.debug("Responsive shards: #{response.responsive_shards.join(', ')}")
- end
-
- warnings = []
- if response.skipped_shards.any?
- warnings << "Skipped shards: #{response.skipped_shards.join(', ')}"
- end
-
- if response.failing_shards.any?
- warnings << "Failing shards: #{response.failing_shards.join(', ')}"
- end
-
- logger.warn(warnings.join(' - ')) if warnings.any?
- end
- end
- end
-end
diff --git a/lib/gitlab/storage_check/gitlab_caller.rb b/lib/gitlab/storage_check/gitlab_caller.rb
deleted file mode 100644
index 44952b68844..00000000000
--- a/lib/gitlab/storage_check/gitlab_caller.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'excon'
-
-module Gitlab
- module StorageCheck
- class GitlabCaller
- def initialize(options)
- @options = options
- end
-
- def call!
- Gitlab::StorageCheck::Response.new(get_response)
- rescue Errno::ECONNREFUSED, Excon::Error
- # Server not ready, treated as invalid response.
- Gitlab::StorageCheck::Response.new(nil)
- end
-
- def get_response
- scheme, *other_parts = URI.split(@options.target)
- socket_path = if scheme == 'unix'
- other_parts.compact.join
- end
-
- connection = Excon.new(@options.target, socket: socket_path)
- connection.post(path: Gitlab::StorageCheck::ENDPOINT,
- headers: headers)
- end
-
- def headers
- @headers ||= begin
- headers = {}
- headers['Content-Type'] = headers['Accept'] = 'application/json'
- headers['TOKEN'] = @options.token if @options.token
-
- headers
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/storage_check/option_parser.rb b/lib/gitlab/storage_check/option_parser.rb
deleted file mode 100644
index 66ed7906f97..00000000000
--- a/lib/gitlab/storage_check/option_parser.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module Gitlab
- module StorageCheck
- class OptionParser
- def self.parse!(args)
- # Start out with some defaults
- options = Gitlab::StorageCheck::Options.new(nil, nil, 1, false)
-
- parser = ::OptionParser.new do |opts|
- opts.banner = "Usage: bin/storage_check [options]"
-
- opts.on('-t=string', '--target string', 'URL or socket to trigger storage check') do |value|
- options.target = value
- end
-
- opts.on('-T=string', '--token string', 'Health token to use') { |value| options.token = value }
-
- opts.on('-i=n', '--interval n', ::OptionParser::DecimalInteger, 'Seconds between checks') do |value|
- options.interval = value
- end
-
- opts.on('-d', '--dryrun', "Output what will be performed, but don't start the process") do |value|
- options.dryrun = value
- end
- end
- parser.parse!(args)
-
- unless options.target
- raise ::OptionParser::InvalidArgument.new('Provide a URI to provide checks')
- end
-
- if URI.parse(options.target).scheme.nil?
- raise ::OptionParser::InvalidArgument.new('Add the scheme to the target, `unix://`, `https://` or `http://` are supported')
- end
-
- options
- end
- end
- end
-end
diff --git a/lib/gitlab/storage_check/response.rb b/lib/gitlab/storage_check/response.rb
deleted file mode 100644
index 326ab236e3e..00000000000
--- a/lib/gitlab/storage_check/response.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'json'
-
-module Gitlab
- module StorageCheck
- class Response
- attr_reader :http_response
-
- def initialize(http_response)
- @http_response = http_response
- end
-
- def valid?
- @http_response && (200...299).cover?(@http_response.status) &&
- @http_response.headers['Content-Type'].include?('application/json') &&
- parsed_response
- end
-
- def check_interval
- return nil unless parsed_response
-
- parsed_response['check_interval']
- end
-
- def responsive_shards
- divided_results[:responsive_shards]
- end
-
- def skipped_shards
- divided_results[:skipped_shards]
- end
-
- def failing_shards
- divided_results[:failing_shards]
- end
-
- private
-
- def results
- return [] unless parsed_response
-
- parsed_response['results']
- end
-
- def divided_results
- return @divided_results if @divided_results
-
- @divided_results = {}
- @divided_results[:responsive_shards] = []
- @divided_results[:skipped_shards] = []
- @divided_results[:failing_shards] = []
-
- results.each do |info|
- name = info['storage']
-
- case info['success']
- when true
- @divided_results[:responsive_shards] << name
- when false
- @divided_results[:failing_shards] << name
- else
- @divided_results[:skipped_shards] << name
- end
- end
-
- @divided_results
- end
-
- def parsed_response
- return @parsed_response if defined?(@parsed_response)
-
- @parsed_response = JSON.parse(@http_response.body)
- rescue JSON::JSONError
- @parsed_response = nil
- end
- end
- end
-end
diff --git a/lib/gitlab/string_placeholder_replacer.rb b/lib/gitlab/string_placeholder_replacer.rb
index 9a2219b7d77..62621255a53 100644
--- a/lib/gitlab/string_placeholder_replacer.rb
+++ b/lib/gitlab/string_placeholder_replacer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class StringPlaceholderReplacer
# This method accepts the following paras
diff --git a/lib/gitlab/string_range_marker.rb b/lib/gitlab/string_range_marker.rb
index c6ad997a4d4..780fe4c7725 100644
--- a/lib/gitlab/string_range_marker.rb
+++ b/lib/gitlab/string_range_marker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class StringRangeMarker
attr_accessor :raw_line, :rich_line, :html_escaped
diff --git a/lib/gitlab/string_regex_marker.rb b/lib/gitlab/string_regex_marker.rb
index b19aa6dea35..f1982ff914c 100644
--- a/lib/gitlab/string_regex_marker.rb
+++ b/lib/gitlab/string_regex_marker.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
module Gitlab
class StringRegexMarker < StringRangeMarker
+ # rubocop: disable CodeReuse/ActiveRecord
def mark(regex, group: 0, &block)
ranges = []
@@ -11,5 +14,6 @@ module Gitlab
super(ranges, &block)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/lib/gitlab/task_helpers.rb b/lib/gitlab/task_helpers.rb
index 922418966e9..224bb648d8f 100644
--- a/lib/gitlab/task_helpers.rb
+++ b/lib/gitlab/task_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rainbow/ext/string'
require 'gitlab/utils/strong_memoize'
@@ -39,7 +41,7 @@ module Gitlab
File.read('/etc/os-release').match(/PRETTY_NAME=\"(.+)\"/)[1]
end
- os_name.try(:squish!)
+ os_name.try(:squish)
end
# Prompt the user to input something
diff --git a/lib/gitlab/tcp_checker.rb b/lib/gitlab/tcp_checker.rb
index 6e24e46d0ea..f37a044b607 100644
--- a/lib/gitlab/tcp_checker.rb
+++ b/lib/gitlab/tcp_checker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class TcpChecker
attr_reader :remote_host, :remote_port, :local_host, :local_port, :error
diff --git a/lib/gitlab/template/base_template.rb b/lib/gitlab/template/base_template.rb
index 7393574ac13..699d747892c 100644
--- a/lib/gitlab/template/base_template.rb
+++ b/lib/gitlab/template/base_template.rb
@@ -1,21 +1,32 @@
module Gitlab
module Template
class BaseTemplate
- def initialize(path, project = nil)
+ attr_accessor :category
+
+ def initialize(path, project = nil, category: nil)
@path = path
+ @category = category
@finder = self.class.finder(project)
end
def name
File.basename(@path, self.class.extension)
end
+ alias_method :key, :name
def content
@finder.read(@path)
end
+ # Present for compatibility with license templates, which can replace text
+ # like `[fullname]` with a user-specified string. This is a no-op for
+ # other templates
+ def resolve!(_placeholders = {})
+ self
+ end
+
def to_json
- { name: name, content: content }
+ { key: key, name: name, content: content }
end
def <=>(other)
@@ -62,7 +73,7 @@ module Gitlab
directory = category_directory(category)
files = finder(project).list_files_for(directory)
- files.map { |f| new(f, project) }.sort
+ files.map { |f| new(f, project, category: category) }.sort
end
def category_directory(category)
diff --git a/lib/gitlab/template/finders/base_template_finder.rb b/lib/gitlab/template/finders/base_template_finder.rb
index 473b05257c6..a5105439b12 100644
--- a/lib/gitlab/template/finders/base_template_finder.rb
+++ b/lib/gitlab/template/finders/base_template_finder.rb
@@ -21,7 +21,7 @@ module Gitlab
def category_directory(category)
return @base_dir unless category.present?
- @base_dir + @categories[category]
+ File.join(@base_dir, @categories[category])
end
class << self
diff --git a/lib/gitlab/template/finders/global_template_finder.rb b/lib/gitlab/template/finders/global_template_finder.rb
index 831da45191f..b08d9a99e99 100644
--- a/lib/gitlab/template/finders/global_template_finder.rb
+++ b/lib/gitlab/template/finders/global_template_finder.rb
@@ -1,4 +1,4 @@
-# Searches and reads file present on Gitlab installation directory
+# Searches and reads file present on GitLab installation directory
module Gitlab
module Template
module Finders
diff --git a/lib/gitlab/template/finders/repo_template_finder.rb b/lib/gitlab/template/finders/repo_template_finder.rb
index 33f07fa0120..9140ace879f 100644
--- a/lib/gitlab/template/finders/repo_template_finder.rb
+++ b/lib/gitlab/template/finders/repo_template_finder.rb
@@ -1,4 +1,4 @@
-# Searches and reads files present on each Gitlab project repository
+# Searches and reads files present on each GitLab project repository
module Gitlab
module Template
module Finders
@@ -27,7 +27,7 @@ module Gitlab
directory = select_directory(file_name)
raise FileNotFoundError if directory.nil?
- category_directory(directory) + file_name
+ File.join(category_directory(directory), file_name)
end
def list_files_for(dir)
@@ -37,8 +37,8 @@ module Gitlab
entries = @repository.tree(:head, dir).entries
- names = entries.map(&:name)
- names.select { |f| f =~ self.class.filter_regex(@extension) }
+ paths = entries.map(&:path)
+ paths.select { |f| f =~ self.class.filter_regex(@extension) }
end
private
@@ -47,10 +47,10 @@ module Gitlab
return [] unless @commit
# Insert root as directory
- directories = ["", @categories.keys]
+ directories = ["", *@categories.keys]
directories.find do |category|
- path = category_directory(category) + file_name
+ path = File.join(category_directory(category), file_name)
@repository.blob_at(@commit.id, path)
end
end
diff --git a/lib/gitlab/template/gitlab_ci_yml_template.rb b/lib/gitlab/template/gitlab_ci_yml_template.rb
index fd040148a1e..deae53cc61b 100644
--- a/lib/gitlab/template/gitlab_ci_yml_template.rb
+++ b/lib/gitlab/template/gitlab_ci_yml_template.rb
@@ -20,7 +20,7 @@ module Gitlab
end
def base_dir
- Rails.root.join('vendor/gitlab-ci-yml')
+ Rails.root.join('lib/gitlab/ci/templates')
end
def finder(project = nil)
diff --git a/lib/gitlab/template_helper.rb b/lib/gitlab/template_helper.rb
index f24a01e6cf5..b0e01697a66 100644
--- a/lib/gitlab/template_helper.rb
+++ b/lib/gitlab/template_helper.rb
@@ -1,24 +1,11 @@
+# frozen_string_literal: true
+
module Gitlab
module TemplateHelper
- include Gitlab::Utils::StrongMemoize
-
def prepare_template_environment(file)
return unless file
- if Gitlab::ImportExport.object_storage?
- params[:import_export_upload] = ImportExportUpload.new(import_file: file)
- else
- FileUtils.mkdir_p(File.dirname(import_upload_path))
- FileUtils.copy_entry(file.path, import_upload_path)
-
- params[:import_source] = import_upload_path
- end
- end
-
- def import_upload_path
- strong_memoize(:import_upload_path) do
- Gitlab::ImportExport.import_upload_path(filename: tmp_filename)
- end
+ params[:import_export_upload] = ImportExportUpload.new(import_file: file)
end
def tmp_filename
diff --git a/lib/gitlab/temporarily_allow.rb b/lib/gitlab/temporarily_allow.rb
index 880e55f71df..000f8ca699d 100644
--- a/lib/gitlab/temporarily_allow.rb
+++ b/lib/gitlab/temporarily_allow.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module TemporarilyAllow
TEMPORARILY_ALLOW_MUTEX = Mutex.new
@@ -10,7 +12,7 @@ module Gitlab
end
def temporarily_allowed?(key)
- if RequestStore.active?
+ if Gitlab::SafeRequestStore.active?
temporarily_allow_request_store[key] > 0
else
TEMPORARILY_ALLOW_MUTEX.synchronize do
@@ -26,11 +28,11 @@ module Gitlab
end
def temporarily_allow_request_store
- RequestStore[:temporarily_allow] ||= Hash.new(0)
+ Gitlab::SafeRequestStore[:temporarily_allow] ||= Hash.new(0)
end
def temporarily_allow_add(key, value)
- if RequestStore.active?
+ if Gitlab::SafeRequestStore.active?
temporarily_allow_request_store[key] += value
else
TEMPORARILY_ALLOW_MUTEX.synchronize do
diff --git a/lib/gitlab/testing/request_inspector_middleware.rb b/lib/gitlab/testing/request_inspector_middleware.rb
index e387667480d..c251e78f5c5 100644
--- a/lib/gitlab/testing/request_inspector_middleware.rb
+++ b/lib/gitlab/testing/request_inspector_middleware.rb
@@ -35,11 +35,15 @@ module Gitlab
request_headers = env_http_headers(env)
status, headers, body = @app.call(env)
+ full_body = ''
+ body.each { |b| full_body << b }
+
request = OpenStruct.new(
url: url,
status_code: status,
request_headers: request_headers,
- response_headers: headers
+ response_headers: headers,
+ body: full_body
)
log_request request
diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb
index 694b01b272c..63860b9cb26 100644
--- a/lib/gitlab/themes.rb
+++ b/lib/gitlab/themes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# Module containing GitLab's application theme definitions and helper methods
# for accessing them.
diff --git a/lib/gitlab/time_tracking_formatter.rb b/lib/gitlab/time_tracking_formatter.rb
index d615c24149a..cc206010e74 100644
--- a/lib/gitlab/time_tracking_formatter.rb
+++ b/lib/gitlab/time_tracking_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module TimeTrackingFormatter
extend self
diff --git a/lib/gitlab/timeless.rb b/lib/gitlab/timeless.rb
index 76a1808c8ac..4f974c98c71 100644
--- a/lib/gitlab/timeless.rb
+++ b/lib/gitlab/timeless.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Timeless
def self.timeless(model, &block)
diff --git a/lib/gitlab/tree_summary.rb b/lib/gitlab/tree_summary.rb
new file mode 100644
index 00000000000..453d78e2f7b
--- /dev/null
+++ b/lib/gitlab/tree_summary.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class TreeSummary
+ include ::Gitlab::Utils::StrongMemoize
+
+ attr_reader :commit, :project, :path, :offset, :limit
+
+ attr_reader :resolved_commits
+ private :resolved_commits
+
+ def initialize(commit, project, params = {})
+ @commit = commit
+ @project = project
+
+ @path = params.fetch(:path, nil).presence
+ @offset = params.fetch(:offset, 0).to_i
+ @limit = (params.fetch(:limit, 25) || 25).to_i
+
+ # Ensure that if multiple tree entries share the same last commit, they share
+ # a ::Commit instance. This prevents us from rendering the same commit title
+ # multiple times
+ @resolved_commits = {}
+ end
+
+ # Creates a summary of the tree entries for a commit, within the window of
+ # entries defined by the offset and limit parameters. This consists of two
+ # return values:
+ #
+ # - An Array of Hashes containing the following keys:
+ # - file_name: The full path of the tree entry
+ # - type: One of :blob, :tree, or :submodule
+ # - commit: The last ::Commit to touch this entry in the tree
+ # - commit_path: URI of the commit in the web interface
+ # - An Array of the unique ::Commit objects in the first value
+ def summarize
+ summary = contents
+ .map { |content| build_entry(content) }
+ .tap { |summary| fill_last_commits!(summary) }
+
+ [summary, commits]
+ end
+
+ # Does the tree contain more entries after the given offset + limit?
+ def more?
+ all_contents[next_offset].present?
+ end
+
+ # The offset of the next batch of tree entries. If more? returns false, this
+ # batch will be empty
+ def next_offset
+ [all_contents.size + 1, offset + limit].min
+ end
+
+ private
+
+ def contents
+ all_contents[offset, limit]
+ end
+
+ def commits
+ resolved_commits.values
+ end
+
+ def repository
+ project.repository
+ end
+
+ def entry_path(entry)
+ File.join(*[path, entry[:file_name]].compact)
+ end
+
+ def build_entry(entry)
+ { file_name: entry.name, type: entry.type }
+ end
+
+ def fill_last_commits!(entries)
+ # Ensure the path is in "path/" format
+ ensured_path =
+ if path
+ File.join(*[path, ""])
+ end
+
+ commits_hsh = repository.list_last_commits_for_tree(commit.id, ensured_path, offset: offset, limit: limit)
+
+ entries.each do |entry|
+ path_key = entry_path(entry)
+ commit = cache_commit(commits_hsh[path_key])
+
+ if commit
+ entry[:commit] = commit
+ entry[:commit_path] = commit_path(commit)
+ end
+ end
+ end
+
+ def cache_commit(commit)
+ return nil unless commit.present?
+
+ resolved_commits[commit.id] ||= commit
+ end
+
+ def commit_path(commit)
+ Gitlab::Routing.url_helpers.project_commit_path(project, commit)
+ end
+
+ def all_contents
+ strong_memoize(:all_contents) do
+ [
+ *tree.trees,
+ *tree.blobs,
+ *tree.submodules
+ ]
+ end
+ end
+
+ def tree
+ strong_memoize(:tree) { repository.tree(commit.id, path) }
+ end
+ end
+end
diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb
index dc2d91dfa23..ba1137313d8 100644
--- a/lib/gitlab/untrusted_regexp.rb
+++ b/lib/gitlab/untrusted_regexp.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
# An untrusted regular expression is any regexp containing patterns sourced
# from user input.
diff --git a/lib/gitlab/update_path_error.rb b/lib/gitlab/update_path_error.rb
index 8947ecfb92e..bc066bf4143 100644
--- a/lib/gitlab/update_path_error.rb
+++ b/lib/gitlab/update_path_error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
UpdatePathError = Class.new(StandardError)
end
diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb
index 024be6aca44..ccab0e4dd73 100644
--- a/lib/gitlab/upgrader.rb
+++ b/lib/gitlab/upgrader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class Upgrader
def execute
diff --git a/lib/gitlab/uploads_transfer.rb b/lib/gitlab/uploads_transfer.rb
index 7d7400bdabf..e0e7084e27e 100644
--- a/lib/gitlab/uploads_transfer.rb
+++ b/lib/gitlab/uploads_transfer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class UploadsTransfer < ProjectTransfer
def root_dir
diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb
index 38be75b7482..86efe8ad114 100644
--- a/lib/gitlab/url_blocker.rb
+++ b/lib/gitlab/url_blocker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'resolv'
module Gitlab
@@ -30,7 +32,9 @@ module Gitlab
end
validate_localhost!(addrs_info) unless allow_localhost
+ validate_loopback!(addrs_info) unless allow_localhost
validate_local_network!(addrs_info) unless allow_local_network
+ validate_link_local!(addrs_info) unless allow_local_network
true
end
@@ -83,12 +87,25 @@ module Gitlab
raise BlockedUrlError, "Requests to localhost are not allowed"
end
+ def validate_loopback!(addrs_info)
+ return unless addrs_info.any? { |addr| addr.ipv4_loopback? || addr.ipv6_loopback? }
+
+ raise BlockedUrlError, "Requests to loopback addresses are not allowed"
+ end
+
def validate_local_network!(addrs_info)
return unless addrs_info.any? { |addr| addr.ipv4_private? || addr.ipv6_sitelocal? }
raise BlockedUrlError, "Requests to the local network are not allowed"
end
+ def validate_link_local!(addrs_info)
+ netmask = IPAddr.new('169.254.0.0/16')
+ return unless addrs_info.any? { |addr| addr.ipv6_linklocal? || netmask.include?(addr.ip_address) }
+
+ raise BlockedUrlError, "Requests to the link local network are not allowed"
+ end
+
def internal?(uri)
internal_web?(uri) || internal_shell?(uri)
end
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index e64033b0dba..f86d599e4cb 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class UrlBuilder
include Gitlab::Routing
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index 308a95d2f09..035268bc4f2 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
module Gitlab
class UrlSanitizer
ALLOWED_SCHEMES = %w[http https ssh git].freeze
def self.sanitize(content)
- regexp = URI::Parser.new.make_regexp(ALLOWED_SCHEMES)
+ regexp = URI::DEFAULT_PARSER.make_regexp(ALLOWED_SCHEMES)
content.gsub(regexp) { |url| new(url).masked_url }
rescue Addressable::URI::InvalidURIError
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 22c9638ecc0..069cd1f802a 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class UsageData
class << self
@@ -10,6 +12,7 @@ module Gitlab
.merge(features_usage_data)
.merge(components_usage_data)
.merge(cycle_analytics_usage_data)
+ .merge(usage_counters)
end
def to_json(force_refresh: false)
@@ -22,7 +25,7 @@ module Gitlab
hostname: Gitlab.config.gitlab.host,
version: Gitlab::VERSION,
installation_type: Gitlab::INSTALLATION_TYPE,
- active_user_count: User.active.count,
+ active_user_count: count(User.active),
recorded_at: Time.now,
edition: 'CE'
}
@@ -31,54 +34,60 @@ module Gitlab
end
# rubocop:disable Metrics/AbcSize
+ # rubocop: disable CodeReuse/ActiveRecord
def system_usage_data
{
counts: {
- boards: Board.count,
- ci_builds: ::Ci::Build.count,
- ci_internal_pipelines: ::Ci::Pipeline.internal.count,
- ci_external_pipelines: ::Ci::Pipeline.external.count,
- ci_pipeline_config_auto_devops: ::Ci::Pipeline.auto_devops_source.count,
- ci_pipeline_config_repository: ::Ci::Pipeline.repository_source.count,
- ci_runners: ::Ci::Runner.count,
- ci_triggers: ::Ci::Trigger.count,
- ci_pipeline_schedules: ::Ci::PipelineSchedule.count,
- auto_devops_enabled: ::ProjectAutoDevops.enabled.count,
- auto_devops_disabled: ::ProjectAutoDevops.disabled.count,
- deploy_keys: DeployKey.count,
- deployments: Deployment.count,
- environments: ::Environment.count,
- clusters: ::Clusters::Cluster.count,
- clusters_enabled: ::Clusters::Cluster.enabled.count,
- clusters_disabled: ::Clusters::Cluster.disabled.count,
- clusters_platforms_gke: ::Clusters::Cluster.gcp_installed.enabled.count,
- clusters_platforms_user: ::Clusters::Cluster.user_provided.enabled.count,
- clusters_applications_helm: ::Clusters::Applications::Helm.installed.count,
- clusters_applications_ingress: ::Clusters::Applications::Ingress.installed.count,
- clusters_applications_prometheus: ::Clusters::Applications::Prometheus.installed.count,
- clusters_applications_runner: ::Clusters::Applications::Runner.installed.count,
- in_review_folder: ::Environment.in_review_folder.count,
- groups: Group.count,
- issues: Issue.count,
- keys: Key.count,
- labels: Label.count,
- lfs_objects: LfsObject.count,
- merge_requests: MergeRequest.count,
- milestones: Milestone.count,
- notes: Note.count,
- pages_domains: PagesDomain.count,
- projects: Project.count,
- projects_imported_from_github: Project.where(import_type: 'github').count,
- protected_branches: ProtectedBranch.count,
- releases: Release.count,
- remote_mirrors: RemoteMirror.count,
- snippets: Snippet.count,
- todos: Todo.count,
- uploads: Upload.count,
- web_hooks: WebHook.count
+ assignee_lists: count(List.assignee),
+ boards: count(Board),
+ ci_builds: count(::Ci::Build),
+ ci_internal_pipelines: count(::Ci::Pipeline.internal),
+ ci_external_pipelines: count(::Ci::Pipeline.external),
+ ci_pipeline_config_auto_devops: count(::Ci::Pipeline.auto_devops_source),
+ ci_pipeline_config_repository: count(::Ci::Pipeline.repository_source),
+ ci_runners: count(::Ci::Runner),
+ ci_triggers: count(::Ci::Trigger),
+ ci_pipeline_schedules: count(::Ci::PipelineSchedule),
+ auto_devops_enabled: count(::ProjectAutoDevops.enabled),
+ auto_devops_disabled: count(::ProjectAutoDevops.disabled),
+ deploy_keys: count(DeployKey),
+ deployments: count(Deployment),
+ environments: count(::Environment),
+ clusters: count(::Clusters::Cluster),
+ clusters_enabled: count(::Clusters::Cluster.enabled),
+ clusters_disabled: count(::Clusters::Cluster.disabled),
+ clusters_platforms_gke: count(::Clusters::Cluster.gcp_installed.enabled),
+ clusters_platforms_user: count(::Clusters::Cluster.user_provided.enabled),
+ clusters_applications_helm: count(::Clusters::Applications::Helm.installed),
+ clusters_applications_ingress: count(::Clusters::Applications::Ingress.installed),
+ clusters_applications_prometheus: count(::Clusters::Applications::Prometheus.installed),
+ clusters_applications_runner: count(::Clusters::Applications::Runner.installed),
+ clusters_applications_knative: count(::Clusters::Applications::Knative.installed),
+ in_review_folder: count(::Environment.in_review_folder),
+ groups: count(Group),
+ issues: count(Issue),
+ keys: count(Key),
+ label_lists: count(List.label),
+ labels: count(Label),
+ lfs_objects: count(LfsObject),
+ merge_requests: count(MergeRequest),
+ milestone_lists: count(List.milestone),
+ milestones: count(Milestone),
+ notes: count(Note),
+ pages_domains: count(PagesDomain),
+ projects: count(Project),
+ projects_imported_from_github: count(Project.where(import_type: 'github')),
+ protected_branches: count(ProtectedBranch),
+ releases: count(Release),
+ remote_mirrors: count(RemoteMirror),
+ snippets: count(Snippet),
+ todos: count(Todo),
+ uploads: count(Upload),
+ web_hooks: count(WebHook)
}.merge(services_usage)
}
end
+ # rubocop: enable CodeReuse/ActiveRecord
def cycle_analytics_usage_data
Gitlab::CycleAnalytics::UsageData.new.to_json
@@ -101,6 +110,12 @@ module Gitlab
}
end
+ def usage_counters
+ {
+ web_ide_commits: Gitlab::WebIdeCommitsCounter.total_count
+ }
+ end
+
def components_usage_data
{
gitlab_pages: { enabled: Gitlab.config.pages.enabled, version: Gitlab::Pages::VERSION },
@@ -109,17 +124,41 @@ module Gitlab
}
end
+ # rubocop: disable CodeReuse/ActiveRecord
def services_usage
types = {
- JiraService: :projects_jira_active,
SlackService: :projects_slack_notifications_active,
SlackSlashCommandsService: :projects_slack_slash_active,
PrometheusService: :projects_prometheus_active
}
- results = Service.unscoped.where(type: types.keys, active: true).group(:type).count
- results.each_with_object({}) { |(key, value), response| response[types[key.to_sym]] = value }
+ results = count(Service.unscoped.where(type: types.keys, active: true).group(:type), fallback: Hash.new(-1))
+ types.each_with_object({}) { |(klass, key), response| response[key] = results[klass.to_s] || 0 }
+ .merge(jira_usage)
+ end
+
+ def jira_usage
+ # Jira Cloud does not support custom domains as per https://jira.atlassian.com/browse/CLOUD-6999
+ # so we can just check for subdomains of atlassian.net
+ services = count(
+ Service.unscoped.where(type: :JiraService, active: true)
+ .group("CASE WHEN properties LIKE '%.atlassian.net%' THEN 'cloud' ELSE 'server' END"),
+ fallback: Hash.new(-1)
+ )
+
+ {
+ projects_jira_server_active: services['server'] || 0,
+ projects_jira_cloud_active: services['cloud'] || 0,
+ projects_jira_active: services['server'] == -1 ? -1 : services.values.sum
+ }
+ end
+
+ def count(relation, fallback: -1)
+ relation.count
+ rescue ActiveRecord::StatementInvalid
+ fallback
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 27560abfb96..980a8014409 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class UserAccess
extend Gitlab::Cache::RequestCache
diff --git a/lib/gitlab/user_extractor.rb b/lib/gitlab/user_extractor.rb
new file mode 100644
index 00000000000..874599688bb
--- /dev/null
+++ b/lib/gitlab/user_extractor.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+# This class extracts all users found in a piece of text by the username or the
+# email address
+
+module Gitlab
+ class UserExtractor
+ # Not using `Devise.email_regexp` to filter out any chars that an email
+ # does not end with and not pinning the email to a start of end of a string.
+ EMAIL_REGEXP = /(?<email>([^@\s]+@[^@\s]+(?<!\W)))/
+ USERNAME_REGEXP = User.reference_pattern
+
+ def initialize(text)
+ @text = text
+ end
+
+ def users
+ return User.none unless @text.present?
+
+ @users ||= User.from_union(union_relations)
+ end
+
+ def usernames
+ matches[:usernames]
+ end
+
+ def emails
+ matches[:emails]
+ end
+
+ def references
+ @references ||= matches.values.flatten
+ end
+
+ def matches
+ @matches ||= {
+ emails: @text.scan(EMAIL_REGEXP).flatten.uniq,
+ usernames: @text.scan(USERNAME_REGEXP).flatten.uniq
+ }
+ end
+
+ private
+
+ def union_relations
+ relations = []
+
+ relations << User.by_any_email(emails) if emails.any?
+ relations << User.by_username(usernames) if usernames.any?
+
+ relations
+ end
+ end
+end
diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb
index aeda66763e8..9e59137a2c0 100644
--- a/lib/gitlab/utils.rb
+++ b/lib/gitlab/utils.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Utils
extend self
@@ -14,6 +16,11 @@ module Gitlab
str.force_encoding(Encoding::UTF_8)
end
+ # Append path to host, making sure there's one single / in between
+ def append_path(host, path)
+ "#{host.to_s.sub(%r{\/+$}, '')}/#{path.to_s.sub(%r{^\/+}, '')}"
+ end
+
# A slugified version of the string, suitable for inclusion in URLs and
# domain names. Rules:
#
diff --git a/lib/gitlab/utils/override.rb b/lib/gitlab/utils/override.rb
index 7b2a62fed48..d00921e6cdc 100644
--- a/lib/gitlab/utils/override.rb
+++ b/lib/gitlab/utils/override.rb
@@ -89,15 +89,19 @@ module Gitlab
def included(base = nil)
super
- queue_verification(base)
+ queue_verification(base) if base
end
- alias_method :prepended, :included
+ def prepended(base = nil)
+ super
+
+ queue_verification(base) if base
+ end
- def extended(mod)
+ def extended(mod = nil)
super
- queue_verification(mod.singleton_class)
+ queue_verification(mod.singleton_class) if mod
end
def queue_verification(base)
diff --git a/lib/gitlab/verify/uploads.rb b/lib/gitlab/verify/uploads.rb
index 73fc43cb590..201fcc7de7f 100644
--- a/lib/gitlab/verify/uploads.rb
+++ b/lib/gitlab/verify/uploads.rb
@@ -11,9 +11,11 @@ module Gitlab
private
+ # rubocop: disable CodeReuse/ActiveRecord
def all_relation
Upload.all.preload(:model)
end
+ # rubocop: enable CodeReuse/ActiveRecord
def local?(upload)
upload.local?
diff --git a/lib/gitlab/version_info.rb b/lib/gitlab/version_info.rb
index 6ee41e85cc9..aa6d5310161 100644
--- a/lib/gitlab/version_info.rb
+++ b/lib/gitlab/version_info.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class VersionInfo
include Comparable
diff --git a/lib/gitlab/view/presenter/base.rb b/lib/gitlab/view/presenter/base.rb
index 36162faa1eb..c3fd6d317aa 100644
--- a/lib/gitlab/view/presenter/base.rb
+++ b/lib/gitlab/view/presenter/base.rb
@@ -11,8 +11,8 @@ module Gitlab
attr_reader :subject
- def can?(user, action, overriden_subject = nil)
- super(user, action, overriden_subject || subject)
+ def can?(user, action, overridden_subject = nil)
+ super(user, action, overridden_subject || subject)
end
# delegate all #can? queries to the subject
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 2612208a927..a3c7de87765 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Gitlab::VisibilityLevel module
#
# Define allowed public modes that can be used for
diff --git a/lib/gitlab/web_ide_commits_counter.rb b/lib/gitlab/web_ide_commits_counter.rb
new file mode 100644
index 00000000000..1cd9b5295b9
--- /dev/null
+++ b/lib/gitlab/web_ide_commits_counter.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module WebIdeCommitsCounter
+ WEB_IDE_COMMITS_KEY = "WEB_IDE_COMMITS_COUNT".freeze
+
+ class << self
+ def increment
+ Gitlab::Redis::SharedState.with { |redis| redis.incr(WEB_IDE_COMMITS_KEY) }
+ end
+
+ def total_count
+ Gitlab::Redis::SharedState.with { |redis| redis.get(WEB_IDE_COMMITS_KEY).to_i }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/wiki_file_finder.rb b/lib/gitlab/wiki_file_finder.rb
index f97278f05cd..a00cd65594c 100644
--- a/lib/gitlab/wiki_file_finder.rb
+++ b/lib/gitlab/wiki_file_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class WikiFileFinder < FileFinder
attr_reader :repository
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index a9629a92a50..e1f777e9cd1 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'base64'
require 'json'
require 'securerandom'
@@ -22,18 +24,27 @@ module Gitlab
project = repository.project
- {
+ attrs = {
GL_ID: Gitlab::GlId.gl_id(user),
GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki),
GL_USERNAME: user&.username,
ShowAllRefs: show_all_refs,
Repository: repository.gitaly_repository.to_h,
RepoPath: 'ignored but not allowed to be empty in gitlab-workhorse',
+ GitConfigOptions: [],
GitalyServer: {
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
}
}
+
+ # Custom option for git-receive-pack command
+ receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
+ if receive_max_input_size > 0
+ attrs[:GitConfigOptions] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
+ end
+
+ attrs
end
def send_git_blob(repository, blob)
@@ -54,7 +65,7 @@ module Gitlab
def send_git_archive(repository, ref:, format:, append_sha:)
format ||= 'tar.gz'
- format.downcase!
+ format = format.downcase
params = repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format, append_sha: append_sha)
raise "Repository or ref not found" if params.empty?
diff --git a/lib/google_api/auth.rb b/lib/google_api/auth.rb
index 1aeaa387a49..56f056fd869 100644
--- a/lib/google_api/auth.rb
+++ b/lib/google_api/auth.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GoogleApi
class Auth
attr_reader :access_token, :redirect_uri, :state
@@ -14,7 +16,7 @@ module GoogleApi
client.auth_code.authorize_url(
redirect_uri: redirect_uri,
scope: scope,
- state: state # This is used for arbitary redirection
+ state: state # This is used for arbitrary redirection
)
end
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index 36859b4d025..e74ff6a9129 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'google/apis/compute_v1'
require 'google/apis/container_v1'
require 'google/apis/cloudbilling_v1'
@@ -50,7 +52,7 @@ module GoogleApi
service.get_zone_cluster(project_id, zone, cluster_id, options: user_agent_header)
end
- def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:)
+ def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:, legacy_abac:)
service = Google::Apis::ContainerV1::ContainerService.new
service.authorization = access_token
@@ -63,7 +65,7 @@ module GoogleApi
"machine_type": machine_type
},
"legacy_abac": {
- "enabled": true
+ "enabled": legacy_abac
}
}
}
diff --git a/lib/gt_one_coercion.rb b/lib/gt_one_coercion.rb
index ef2dc09767c..99be51bc8c6 100644
--- a/lib/gt_one_coercion.rb
+++ b/lib/gt_one_coercion.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GtOneCoercion < Virtus::Attribute
def coerce(value)
[1, value.to_i].max
diff --git a/lib/haml_lint/inline_javascript.rb b/lib/haml_lint/inline_javascript.rb
index adbed20f152..2e98227a05e 100644
--- a/lib/haml_lint/inline_javascript.rb
+++ b/lib/haml_lint/inline_javascript.rb
@@ -1,7 +1,10 @@
-unless Rails.env.production? # rubocop:disable Naming/FileName
- require 'haml_lint/haml_visitor'
- require 'haml_lint/linter'
- require 'haml_lint/linter_registry'
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+unless Rails.env.production?
+ require_dependency 'haml_lint/haml_visitor'
+ require_dependency 'haml_lint/linter'
+ require_dependency 'haml_lint/linter_registry'
module HamlLint
class Linter::InlineJavaScript < Linter
diff --git a/lib/json_web_token/hmac_token.rb b/lib/json_web_token/hmac_token.rb
new file mode 100644
index 00000000000..ceb1b9c913f
--- /dev/null
+++ b/lib/json_web_token/hmac_token.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'jwt'
+
+module JSONWebToken
+ class HMACToken < Token
+ IAT_LEEWAY = 60
+ JWT_ALGORITHM = 'HS256'
+
+ def initialize(secret)
+ super()
+
+ @secret = secret
+ end
+
+ def self.decode(token, secret, leeway: IAT_LEEWAY, verify_iat: true)
+ JWT.decode(token, secret, true, leeway: leeway, verify_iat: verify_iat, algorithm: JWT_ALGORITHM)
+ end
+
+ def encoded
+ JWT.encode(payload, secret, JWT_ALGORITHM)
+ end
+
+ private
+
+ attr_reader :secret
+ end
+end
diff --git a/lib/json_web_token/rsa_token.rb b/lib/json_web_token/rsa_token.rb
index d6d6af7089c..160e1e506f1 100644
--- a/lib/json_web_token/rsa_token.rb
+++ b/lib/json_web_token/rsa_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module JSONWebToken
class RSAToken < Token
attr_reader :key_file
diff --git a/lib/json_web_token/token.rb b/lib/json_web_token/token.rb
index 5b67715b0b2..c59beef02c9 100644
--- a/lib/json_web_token/token.rb
+++ b/lib/json_web_token/token.rb
@@ -1,15 +1,22 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
module JSONWebToken
class Token
attr_accessor :issuer, :subject, :audience, :id
attr_accessor :issued_at, :not_before, :expire_time
+ DEFAULT_NOT_BEFORE_TIME = 5
+ DEFAULT_EXPIRE_TIME = 60
+
def initialize
@id = SecureRandom.uuid
@issued_at = Time.now
# we give a few seconds for time shift
- @not_before = issued_at - 5.seconds
+ @not_before = issued_at - DEFAULT_NOT_BEFORE_TIME
# default 60 seconds should be more than enough for this authentication token
- @expire_time = issued_at + 1.minute
+ @expire_time = issued_at + DEFAULT_EXPIRE_TIME
@custom_payload = {}
end
diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb
index d80cd7d2a4e..293d0c563c5 100644
--- a/lib/mattermost/client.rb
+++ b/lib/mattermost/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
ClientError = Class.new(Mattermost::Error)
diff --git a/lib/mattermost/command.rb b/lib/mattermost/command.rb
index 704813dfdf0..a02745486d6 100644
--- a/lib/mattermost/command.rb
+++ b/lib/mattermost/command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
class Command < Client
def create(params)
diff --git a/lib/mattermost/error.rb b/lib/mattermost/error.rb
index dee6deb7974..054bd5457bd 100644
--- a/lib/mattermost/error.rb
+++ b/lib/mattermost/error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
Error = Class.new(StandardError)
end
diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb
index 2aa7a2f64d8..e2083848a8d 100644
--- a/lib/mattermost/session.rb
+++ b/lib/mattermost/session.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
class NoSessionError < Mattermost::Error
def message
diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb
index 95c2f6f9d6b..58120178f50 100644
--- a/lib/mattermost/team.rb
+++ b/lib/mattermost/team.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
class Team < Client
# Returns all teams that the current user is a member of
diff --git a/lib/microsoft_teams/activity.rb b/lib/microsoft_teams/activity.rb
index d2c420efdaf..207e90d2638 100644
--- a/lib/microsoft_teams/activity.rb
+++ b/lib/microsoft_teams/activity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MicrosoftTeams
class Activity
def initialize(title:, subtitle:, text:, image:)
diff --git a/lib/microsoft_teams/notifier.rb b/lib/microsoft_teams/notifier.rb
index 226ee1373db..c7dec09ba6b 100644
--- a/lib/microsoft_teams/notifier.rb
+++ b/lib/microsoft_teams/notifier.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MicrosoftTeams
class Notifier
def initialize(webhook)
diff --git a/lib/milestone_array.rb b/lib/milestone_array.rb
index 4ed8485b36a..461e73e9670 100644
--- a/lib/milestone_array.rb
+++ b/lib/milestone_array.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MilestoneArray
class << self
def sort(array, sort_method)
diff --git a/lib/mysql_zero_date.rb b/lib/mysql_zero_date.rb
index 64634f789da..216560148fa 100644
--- a/lib/mysql_zero_date.rb
+++ b/lib/mysql_zero_date.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Disable NO_ZERO_DATE mode for mysql in rails 5.
# We use zero date as a default value
# (config/initializers/active_record_mysql_timestamp.rb), in
diff --git a/lib/object_storage/direct_upload.rb b/lib/object_storage/direct_upload.rb
index 61a69e7ffe4..fd26663fef0 100644
--- a/lib/object_storage/direct_upload.rb
+++ b/lib/object_storage/direct_upload.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ObjectStorage
#
# The DirectUpload c;ass generates a set of presigned URLs
@@ -41,7 +43,9 @@ module ObjectStorage
GetURL: get_url,
StoreURL: store_url,
DeleteURL: delete_url,
- MultipartUpload: multipart_upload_hash
+ MultipartUpload: multipart_upload_hash,
+ CustomPutHeaders: true,
+ PutHeaders: upload_options
}.compact
end
@@ -87,7 +91,7 @@ module ObjectStorage
method: 'PUT',
bucket_name: bucket_name,
object_name: object_name,
- query: { uploadId: upload_id, partNumber: part_number },
+ query: { 'uploadId' => upload_id, 'partNumber' => part_number },
headers: upload_options
}, expire_at)
end
@@ -98,7 +102,7 @@ module ObjectStorage
method: 'POST',
bucket_name: bucket_name,
object_name: object_name,
- query: { uploadId: upload_id },
+ query: { 'uploadId' => upload_id },
headers: { 'Content-Type' => 'application/xml' }
}, expire_at)
end
@@ -109,7 +113,7 @@ module ObjectStorage
method: 'DELETE',
bucket_name: bucket_name,
object_name: object_name,
- query: { uploadId: upload_id }
+ query: { 'uploadId' => upload_id }
}, expire_at)
end
@@ -156,7 +160,7 @@ module ObjectStorage
end
def upload_options
- { 'Content-Type' => 'application/octet-stream' }
+ {}
end
def connection
diff --git a/lib/omni_auth/strategies/bitbucket.rb b/lib/omni_auth/strategies/bitbucket.rb
index ce1bdfe6ee4..6c914b4222a 100644
--- a/lib/omni_auth/strategies/bitbucket.rb
+++ b/lib/omni_auth/strategies/bitbucket.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'omniauth-oauth2'
module OmniAuth
diff --git a/lib/omni_auth/strategies/jwt.rb b/lib/omni_auth/strategies/jwt.rb
index ebdb5c7faf0..a792903fde7 100644
--- a/lib/omni_auth/strategies/jwt.rb
+++ b/lib/omni_auth/strategies/jwt.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'omniauth'
require 'jwt'
diff --git a/lib/peek/rblineprof/custom_controller_helpers.rb b/lib/peek/rblineprof/custom_controller_helpers.rb
index 9beb442bfa3..581cc6a37b4 100644
--- a/lib/peek/rblineprof/custom_controller_helpers.rb
+++ b/lib/peek/rblineprof/custom_controller_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Peek
module Rblineprof
module CustomControllerHelpers
@@ -41,7 +43,7 @@ module Peek
]
end.sort_by{ |a,b,c,d,e,f| -f }
- output = "<div class='modal-dialog modal-xl'><div class='modal-content'>"
+ output = ["<div class='modal-dialog modal-xl'><div class='modal-content'>"]
output << "<div class='modal-header'>"
output << "<h4>Line profiling: #{human_description(params[:lineprofiler])}</h4>"
output << "<button class='close' type='button' data-dismiss='modal' aria-label='close'><span aria-hidden='true'>&times;</span></button>"
@@ -93,7 +95,7 @@ module Peek
output << "</div></div></div>"
- response.body += "<div class='modal' id='modal-peek-line-profile' tabindex=-1>#{output}</div>".html_safe
+ response.body += "<div class='modal' id='modal-peek-line-profile' tabindex=-1>#{output.join}</div>".html_safe
end
ret
diff --git a/lib/peek/views/gitaly.rb b/lib/peek/views/gitaly.rb
index ab35f7a2258..860963ef94f 100644
--- a/lib/peek/views/gitaly.rb
+++ b/lib/peek/views/gitaly.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Peek
module Views
class Gitaly < View
diff --git a/lib/peek/views/host.rb b/lib/peek/views/host.rb
index 43c8a35c7ea..b77355ea11b 100644
--- a/lib/peek/views/host.rb
+++ b/lib/peek/views/host.rb
@@ -1,8 +1,13 @@
+# frozen_string_literal: true
+
module Peek
module Views
class Host < View
def results
- { hostname: Gitlab::Environment.hostname }
+ {
+ hostname: Gitlab::Environment.hostname,
+ canary: Gitlab::Utils.to_boolean(ENV['CANARY'])
+ }
end
end
end
diff --git a/lib/quality/helm_client.rb b/lib/quality/helm_client.rb
new file mode 100644
index 00000000000..cf1f03b35b5
--- /dev/null
+++ b/lib/quality/helm_client.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+require 'time'
+require_relative '../gitlab/popen' unless defined?(Gitlab::Popen)
+
+module Quality
+ class HelmClient
+ CommandFailedError = Class.new(StandardError)
+
+ attr_reader :namespace
+
+ RELEASE_JSON_ATTRIBUTES = %w[Name Revision Updated Status Chart AppVersion Namespace].freeze
+
+ Release = Struct.new(:name, :revision, :last_update, :status, :chart, :app_version, :namespace) do
+ def revision
+ @revision ||= self[:revision].to_i
+ end
+
+ def last_update
+ @last_update ||= Time.parse(self[:last_update])
+ end
+ end
+
+ # A single page of data and the corresponding page number.
+ Page = Struct.new(:releases, :number)
+
+ def initialize(namespace:)
+ @namespace = namespace
+ end
+
+ def releases(args: [])
+ each_release(args)
+ end
+
+ def delete(release_name:)
+ run_command([
+ 'delete',
+ %(--tiller-namespace "#{namespace}"),
+ '--purge',
+ release_name
+ ])
+ end
+
+ private
+
+ def run_command(command)
+ final_command = ['helm', *command].join(' ')
+ puts "Running command: `#{final_command}`" # rubocop:disable Rails/Output
+
+ result = Gitlab::Popen.popen_with_detail([final_command])
+
+ if result.status.success?
+ result.stdout.chomp.freeze
+ else
+ raise CommandFailedError, "The `#{final_command}` command failed (status: #{result.status}) with the following error:\n#{result.stderr}"
+ end
+ end
+
+ def raw_releases(args = [])
+ command = [
+ 'list',
+ %(--namespace "#{namespace}"),
+ %(--tiller-namespace "#{namespace}" --output json),
+ *args
+ ]
+ json = JSON.parse(run_command(command))
+
+ releases = json['Releases'].map do |json_release|
+ Release.new(*json_release.values_at(*RELEASE_JSON_ATTRIBUTES))
+ end
+
+ [releases, json['Next']]
+ rescue JSON::ParserError => ex
+ puts "Ignoring this JSON parsing error: #{ex}" # rubocop:disable Rails/Output
+ [[], nil]
+ end
+
+ # Fetches data from Helm and yields a Page object for every page
+ # of data, without loading all of them into memory.
+ #
+ # method - The Octokit method to use for getting the data.
+ # args - Arguments to pass to the `helm list` command.
+ def each_releases_page(args, &block)
+ return to_enum(__method__, args) unless block_given?
+
+ page = 1
+ offset = ''
+
+ loop do
+ final_args = args.dup
+ final_args << "--offset #{offset}" unless offset.to_s.empty?
+ collection, offset = raw_releases(final_args)
+
+ yield Page.new(collection, page += 1)
+
+ break if offset.to_s.empty?
+ end
+ end
+
+ # Iterates over all of the releases.
+ #
+ # args - Any arguments to pass to the `helm list` command.
+ def each_release(args, &block)
+ return to_enum(__method__, args) unless block_given?
+
+ each_releases_page(args) do |page|
+ page.releases.each do |release|
+ yield release
+ end
+ end
+ end
+ end
+end
diff --git a/lib/quality/kubernetes_client.rb b/lib/quality/kubernetes_client.rb
new file mode 100644
index 00000000000..2ff9e811425
--- /dev/null
+++ b/lib/quality/kubernetes_client.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require_relative '../gitlab/popen' unless defined?(Gitlab::Popen)
+
+module Quality
+ class KubernetesClient
+ CommandFailedError = Class.new(StandardError)
+
+ attr_reader :namespace
+
+ def initialize(namespace:)
+ @namespace = namespace
+ end
+
+ def cleanup(release_name:)
+ command = [
+ %(--namespace "#{namespace}"),
+ 'delete',
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa',
+ '--now',
+ %(-l release="#{release_name}")
+ ]
+
+ run_command(command)
+ end
+
+ private
+
+ def run_command(command)
+ final_command = ['kubectl', *command].join(' ')
+ puts "Running command: `#{final_command}`" # rubocop:disable Rails/Output
+
+ result = Gitlab::Popen.popen_with_detail([final_command])
+
+ if result.status.success?
+ result.stdout.chomp.freeze
+ else
+ raise CommandFailedError, "The `#{final_command}` command failed (status: #{result.status}) with the following error:\n#{result.stderr}"
+ end
+ end
+ end
+end
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index e877ab10248..e2a7d3ef5ba 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rouge
module Formatters
class HTMLGitlab < Rouge::Formatters::HTML
diff --git a/lib/rouge/plugins/common_mark.rb b/lib/rouge/plugins/common_mark.rb
index 8f9de061124..d240df5a0e0 100644
--- a/lib/rouge/plugins/common_mark.rb
+++ b/lib/rouge/plugins/common_mark.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A rouge plugin for CommonMark markdown engine.
# Used to highlight code generated by CommonMark.
diff --git a/lib/rspec_flaky/config.rb b/lib/rspec_flaky/config.rb
index 06e96f969f1..55c1d4747b4 100644
--- a/lib/rspec_flaky/config.rb
+++ b/lib/rspec_flaky/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RspecFlaky
class Config
def self.generate_report?
diff --git a/lib/rspec_flaky/example.rb b/lib/rspec_flaky/example.rb
index b6e790cbbab..3c1b05257a0 100644
--- a/lib/rspec_flaky/example.rb
+++ b/lib/rspec_flaky/example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RspecFlaky
# This is a wrapper class for RSpec::Core::Example
class Example
diff --git a/lib/rspec_flaky/flaky_example.rb b/lib/rspec_flaky/flaky_example.rb
index 6be24014d89..da5dbf06bc9 100644
--- a/lib/rspec_flaky/flaky_example.rb
+++ b/lib/rspec_flaky/flaky_example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RspecFlaky
# This represents a flaky RSpec example and is mainly meant to be saved in a JSON file
class FlakyExample < OpenStruct
diff --git a/lib/rspec_flaky/flaky_examples_collection.rb b/lib/rspec_flaky/flaky_examples_collection.rb
index dea23c325be..290a51766e9 100644
--- a/lib/rspec_flaky/flaky_examples_collection.rb
+++ b/lib/rspec_flaky/flaky_examples_collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_support/hash_with_indifferent_access'
require_relative 'flaky_example'
diff --git a/lib/rspec_flaky/listener.rb b/lib/rspec_flaky/listener.rb
index 9cd0c38cb55..19cc0baa2d3 100644
--- a/lib/rspec_flaky/listener.rb
+++ b/lib/rspec_flaky/listener.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'json'
require_dependency 'rspec_flaky/config'
diff --git a/lib/rspec_flaky/report.rb b/lib/rspec_flaky/report.rb
index 1c362fdd20d..9a0fb88c424 100644
--- a/lib/rspec_flaky/report.rb
+++ b/lib/rspec_flaky/report.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'json'
require 'time'
diff --git a/lib/static_model.rb b/lib/static_model.rb
index 60e2dd82e4e..86bf8d62f9a 100644
--- a/lib/static_model.rb
+++ b/lib/static_model.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database.
module StaticModel
extend ActiveSupport::Concern
- module ClassMethods
+ class_methods do
# Used by ActiveRecord's polymorphic association to set object_id
def primary_key
'id'
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 72eb8adcce2..fc984d737d5 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -17,7 +17,7 @@
## See installation.md#using-https for additional HTTPS configuration details.
upstream gitlab-workhorse {
- # Gitlab socket file,
+ # GitLab socket file,
# for Omnibus this would be: unix:/var/opt/gitlab/gitlab-workhorse/socket
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -112,7 +112,7 @@ server {
error_page 502 /502.html;
error_page 503 /503.html;
location ~ ^/(404|422|500|502|503)\.html$ {
- # Location to the Gitlab's public directory,
+ # Location to the GitLab's public directory,
# for Omnibus this would be: /opt/gitlab/embedded/service/gitlab-rails/public.
root /home/git/gitlab/public;
internal;
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 2e3799d5e1b..ba01e250bbb 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -21,7 +21,7 @@
## See installation.md#using-https for additional HTTPS configuration details.
upstream gitlab-workhorse {
- # Gitlab socket file,
+ # GitLab socket file,
# for Omnibus this would be: unix:/var/opt/gitlab/gitlab-workhorse/socket
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -162,7 +162,7 @@ server {
error_page 502 /502.html;
error_page 503 /503.html;
location ~ ^/(404|422|500|502|503)\.html$ {
- # Location to the Gitlab's public directory,
+ # Location to the GitLab's public directory,
# for Omnibus this would be: /opt/gitlab/embedded/service/gitlab-rails/public
root /home/git/gitlab/public;
internal;
diff --git a/lib/system_check.rb b/lib/system_check.rb
index 466c39904fa..7ffd7c03c5b 100644
--- a/lib/system_check.rb
+++ b/lib/system_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Library to perform System Checks
#
# Every Check is implemented as its own class inherited from SystemCheck::BaseCheck
diff --git a/lib/system_check/app/active_users_check.rb b/lib/system_check/app/active_users_check.rb
index 1d72c8d6903..8446c2fc2c8 100644
--- a/lib/system_check/app/active_users_check.rb
+++ b/lib/system_check/app/active_users_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class ActiveUsersCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/database_config_exists_check.rb b/lib/system_check/app/database_config_exists_check.rb
index d1fae192350..1769145ed63 100644
--- a/lib/system_check/app/database_config_exists_check.rb
+++ b/lib/system_check/app/database_config_exists_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class DatabaseConfigExistsCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/git_config_check.rb b/lib/system_check/app/git_config_check.rb
index d08a81639e3..4e8d607096c 100644
--- a/lib/system_check/app/git_config_check.rb
+++ b/lib/system_check/app/git_config_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitConfigCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/git_user_default_ssh_config_check.rb b/lib/system_check/app/git_user_default_ssh_config_check.rb
index ad41760dff2..6cd53779bfd 100644
--- a/lib/system_check/app/git_user_default_ssh_config_check.rb
+++ b/lib/system_check/app/git_user_default_ssh_config_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitUserDefaultSSHConfigCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/git_version_check.rb b/lib/system_check/app/git_version_check.rb
index 44ec888c197..994af3ab53e 100644
--- a/lib/system_check/app/git_version_check.rb
+++ b/lib/system_check/app/git_version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitVersionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/gitlab_config_exists_check.rb b/lib/system_check/app/gitlab_config_exists_check.rb
index 247aa0994e4..1cc5ead0d89 100644
--- a/lib/system_check/app/gitlab_config_exists_check.rb
+++ b/lib/system_check/app/gitlab_config_exists_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitlabConfigExistsCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/gitlab_config_up_to_date_check.rb b/lib/system_check/app/gitlab_config_up_to_date_check.rb
index c609e48e133..58c7e3039c8 100644
--- a/lib/system_check/app/gitlab_config_up_to_date_check.rb
+++ b/lib/system_check/app/gitlab_config_up_to_date_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitlabConfigUpToDateCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/init_script_exists_check.rb b/lib/system_check/app/init_script_exists_check.rb
index d246e058e86..d36dbe7d67d 100644
--- a/lib/system_check/app/init_script_exists_check.rb
+++ b/lib/system_check/app/init_script_exists_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class InitScriptExistsCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/init_script_up_to_date_check.rb b/lib/system_check/app/init_script_up_to_date_check.rb
index 53a47eb0f42..569c41df6e4 100644
--- a/lib/system_check/app/init_script_up_to_date_check.rb
+++ b/lib/system_check/app/init_script_up_to_date_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class InitScriptUpToDateCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/log_writable_check.rb b/lib/system_check/app/log_writable_check.rb
index 3e0c436d6ee..e26ad143eb8 100644
--- a/lib/system_check/app/log_writable_check.rb
+++ b/lib/system_check/app/log_writable_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class LogWritableCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/migrations_are_up_check.rb b/lib/system_check/app/migrations_are_up_check.rb
index 5eedbacce77..b12e9ac6bba 100644
--- a/lib/system_check/app/migrations_are_up_check.rb
+++ b/lib/system_check/app/migrations_are_up_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class MigrationsAreUpCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/orphaned_group_members_check.rb b/lib/system_check/app/orphaned_group_members_check.rb
index 2b46d36fe51..3e6ffb8190b 100644
--- a/lib/system_check/app/orphaned_group_members_check.rb
+++ b/lib/system_check/app/orphaned_group_members_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class OrphanedGroupMembersCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/projects_have_namespace_check.rb b/lib/system_check/app/projects_have_namespace_check.rb
index a6ec9f7665c..2bf2529acf1 100644
--- a/lib/system_check/app/projects_have_namespace_check.rb
+++ b/lib/system_check/app/projects_have_namespace_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class ProjectsHaveNamespaceCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/redis_version_check.rb b/lib/system_check/app/redis_version_check.rb
index a0610e73576..890f8b44d13 100644
--- a/lib/system_check/app/redis_version_check.rb
+++ b/lib/system_check/app/redis_version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class RedisVersionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/ruby_version_check.rb b/lib/system_check/app/ruby_version_check.rb
index 57bbabece1f..d73c39f2c3f 100644
--- a/lib/system_check/app/ruby_version_check.rb
+++ b/lib/system_check/app/ruby_version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class RubyVersionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/tmp_writable_check.rb b/lib/system_check/app/tmp_writable_check.rb
index 99a75e57abf..6687df091d3 100644
--- a/lib/system_check/app/tmp_writable_check.rb
+++ b/lib/system_check/app/tmp_writable_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class TmpWritableCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/uploads_directory_exists_check.rb b/lib/system_check/app/uploads_directory_exists_check.rb
index 7026d0ba075..940eff9d4cf 100644
--- a/lib/system_check/app/uploads_directory_exists_check.rb
+++ b/lib/system_check/app/uploads_directory_exists_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class UploadsDirectoryExistsCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/uploads_path_permission_check.rb b/lib/system_check/app/uploads_path_permission_check.rb
index 7df6c060254..4a49f3bc2bb 100644
--- a/lib/system_check/app/uploads_path_permission_check.rb
+++ b/lib/system_check/app/uploads_path_permission_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class UploadsPathPermissionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/uploads_path_tmp_permission_check.rb b/lib/system_check/app/uploads_path_tmp_permission_check.rb
index b276a81eac1..ae374f4707c 100644
--- a/lib/system_check/app/uploads_path_tmp_permission_check.rb
+++ b/lib/system_check/app/uploads_path_tmp_permission_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class UploadsPathTmpPermissionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/base_check.rb b/lib/system_check/base_check.rb
index 0f5742dd67f..e06245294c4 100644
--- a/lib/system_check/base_check.rb
+++ b/lib/system_check/base_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
# Base class for Checks. You must inherit from here
# and implement the methods below when necessary
diff --git a/lib/system_check/helpers.rb b/lib/system_check/helpers.rb
index 6227e461d24..07d479848fe 100644
--- a/lib/system_check/helpers.rb
+++ b/lib/system_check/helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module Helpers
include ::Gitlab::TaskHelpers
diff --git a/lib/system_check/incoming_email/foreman_configured_check.rb b/lib/system_check/incoming_email/foreman_configured_check.rb
index 1db7bf2b782..944913087da 100644
--- a/lib/system_check/incoming_email/foreman_configured_check.rb
+++ b/lib/system_check/incoming_email/foreman_configured_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module IncomingEmail
class ForemanConfiguredCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/incoming_email/imap_authentication_check.rb b/lib/system_check/incoming_email/imap_authentication_check.rb
index e55bea86d3f..613c2296375 100644
--- a/lib/system_check/incoming_email/imap_authentication_check.rb
+++ b/lib/system_check/incoming_email/imap_authentication_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module IncomingEmail
class ImapAuthenticationCheck < SystemCheck::BaseCheck
@@ -7,7 +9,7 @@ module SystemCheck
if config
try_connect_imap
else
- @error = "#{mail_room_config_path} does not have mailboxes setup"
+ @error = "#{mail_room_config_path} does not have mailboxes set up"
false
end
end
diff --git a/lib/system_check/incoming_email/initd_configured_check.rb b/lib/system_check/incoming_email/initd_configured_check.rb
index ea23b8ef49c..acb4b5a9e74 100644
--- a/lib/system_check/incoming_email/initd_configured_check.rb
+++ b/lib/system_check/incoming_email/initd_configured_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module IncomingEmail
class InitdConfiguredCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/incoming_email/mail_room_running_check.rb b/lib/system_check/incoming_email/mail_room_running_check.rb
index c1807501829..b7aead4624e 100644
--- a/lib/system_check/incoming_email/mail_room_running_check.rb
+++ b/lib/system_check/incoming_email/mail_room_running_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module IncomingEmail
class MailRoomRunningCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/orphans/namespace_check.rb b/lib/system_check/orphans/namespace_check.rb
index 09b57c7b408..53b2d8fd5b3 100644
--- a/lib/system_check/orphans/namespace_check.rb
+++ b/lib/system_check/orphans/namespace_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module Orphans
class NamespaceCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/orphans/repository_check.rb b/lib/system_check/orphans/repository_check.rb
index 2695c658874..ef8fe945f61 100644
--- a/lib/system_check/orphans/repository_check.rb
+++ b/lib/system_check/orphans/repository_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module Orphans
class RepositoryCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/simple_executor.rb b/lib/system_check/simple_executor.rb
index 99c9e984107..11818ae54f8 100644
--- a/lib/system_check/simple_executor.rb
+++ b/lib/system_check/simple_executor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
# Simple Executor is current default executor for GitLab
# It is a simple port from display logic in the old check.rake
diff --git a/lib/tasks/flay.rake b/lib/tasks/flay.rake
deleted file mode 100644
index 4bec013a141..00000000000
--- a/lib/tasks/flay.rake
+++ /dev/null
@@ -1,9 +0,0 @@
-desc 'Code duplication analyze via flay'
-task :flay do
- output = `bundle exec flay --mass 35 app/ lib/gitlab/ ee/ 2> #{File::NULL}`
-
- if output.include?("Similar code found") || output.include?("IDENTICAL code found")
- puts output
- exit 1
- end
-end
diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake
index c6942d22926..560a52053d8 100644
--- a/lib/tasks/gemojione.rake
+++ b/lib/tasks/gemojione.rake
@@ -86,7 +86,7 @@ namespace :gemojione do
SPRITESHEET_WIDTH = 860
SPRITESHEET_HEIGHT = 840
- # Setup a map to rename image files
+ # Set up a map to rename image files
emoji_unicode_string_to_name_map = {}
Gitlab::Emoji.emojis.each do |name, emoji_hash|
# Ignore aliases
diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake
index f431352b61e..a497d26312e 100644
--- a/lib/tasks/gettext.rake
+++ b/lib/tasks/gettext.rake
@@ -82,7 +82,7 @@ namespace :gettext do
# `gettext:find` writes touches to temp files to `stderr` which would cause
# `static-analysis` to report failures. We can ignore these.
- silence_stream($stderr) do
+ silence_sdterr do
Rake::Task['gettext:find'].invoke
end
@@ -118,4 +118,15 @@ namespace :gettext do
end
end
end
+
+ def silence_sdterr(&block)
+ old_stderr = $stderr.dup
+ $stderr.reopen(File::NULL)
+ $stderr.sync = true
+
+ yield
+ ensure
+ $stderr.reopen(old_stderr)
+ old_stderr.close
+ end
end
diff --git a/lib/tasks/gitlab/artifacts/migrate.rake b/lib/tasks/gitlab/artifacts/migrate.rake
index bfca4bfb3f7..e7634d2ed4f 100644
--- a/lib/tasks/gitlab/artifacts/migrate.rake
+++ b/lib/tasks/gitlab/artifacts/migrate.rake
@@ -15,7 +15,7 @@ namespace :gitlab do
build.artifacts_file.migrate!(ObjectStorage::Store::REMOTE)
build.artifacts_metadata.migrate!(ObjectStorage::Store::REMOTE)
- logger.info("Transferred artifacts of #{build.id} of #{build.artifacts_size} to object storage")
+ logger.info("Transferred artifact ID #{build.id} with size #{build.artifacts_size} to object storage")
rescue => e
logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
end
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index e5b5f3548e4..a2c3e32948f 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -1,6 +1,7 @@
namespace :gitlab do
desc 'GitLab | Check the configuration of GitLab and its environment'
task check: %w{gitlab:gitlab_shell:check
+ gitlab:gitaly:check
gitlab:sidekiq:check
gitlab:incoming_email:check
gitlab:ldap:check
@@ -44,13 +45,6 @@ namespace :gitlab do
start_checking "GitLab Shell"
check_gitlab_shell
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- check_repo_base_exists
- check_repo_base_is_not_symlink
- check_repo_base_user_and_group
- check_repo_base_permissions
- check_repos_hooks_directory_is_link
- end
check_gitlab_shell_self_test
finished_checking "GitLab Shell"
@@ -59,152 +53,6 @@ namespace :gitlab do
# Checks
########################
- def check_repo_base_exists
- puts "Repo base directory exists?"
-
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- repo_base_path = repository_storage.legacy_disk_path
- print "#{name}... "
-
- if File.exist?(repo_base_path)
- puts "yes".color(:green)
- else
- puts "no".color(:red)
- puts "#{repo_base_path} is missing".color(:red)
- try_fixing_it(
- "This should have been created when setting up GitLab Shell.",
- "Make sure it's set correctly in config/gitlab.yml",
- "Make sure GitLab Shell is installed correctly."
- )
- for_more_information(
- see_installation_guide_section "GitLab Shell"
- )
- fix_and_rerun
- end
- end
- end
-
- def check_repo_base_is_not_symlink
- puts "Repo storage directories are symlinks?"
-
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- repo_base_path = repository_storage.legacy_disk_path
- print "#{name}... "
-
- unless File.exist?(repo_base_path)
- puts "can't check because of previous errors".color(:magenta)
- break
- end
-
- unless File.symlink?(repo_base_path)
- puts "no".color(:green)
- else
- puts "yes".color(:red)
- try_fixing_it(
- "Make sure it's set to the real directory in config/gitlab.yml"
- )
- fix_and_rerun
- end
- end
- end
-
- def check_repo_base_permissions
- puts "Repo paths access is drwxrws---?"
-
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- repo_base_path = repository_storage.legacy_disk_path
- print "#{name}... "
-
- unless File.exist?(repo_base_path)
- puts "can't check because of previous errors".color(:magenta)
- break
- end
-
- if File.stat(repo_base_path).mode.to_s(8).ends_with?("2770")
- puts "yes".color(:green)
- else
- puts "no".color(:red)
- try_fixing_it(
- "sudo chmod -R ug+rwX,o-rwx #{repo_base_path}",
- "sudo chmod -R ug-s #{repo_base_path}",
- "sudo find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s"
- )
- for_more_information(
- see_installation_guide_section "GitLab Shell"
- )
- fix_and_rerun
- end
- end
- end
-
- def check_repo_base_user_and_group
- gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
- puts "Repo paths owned by #{gitlab_shell_ssh_user}:root, or #{gitlab_shell_ssh_user}:#{Gitlab.config.gitlab_shell.owner_group}?"
-
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- repo_base_path = repository_storage.legacy_disk_path
- print "#{name}... "
-
- unless File.exist?(repo_base_path)
- puts "can't check because of previous errors".color(:magenta)
- break
- end
-
- user_id = uid_for(gitlab_shell_ssh_user)
- root_group_id = gid_for('root')
- group_ids = [root_group_id, gid_for(Gitlab.config.gitlab_shell.owner_group)]
- if File.stat(repo_base_path).uid == user_id && group_ids.include?(File.stat(repo_base_path).gid)
- puts "yes".color(:green)
- else
- puts "no".color(:red)
- puts " User id for #{gitlab_shell_ssh_user}: #{user_id}. Groupd id for root: #{root_group_id}".color(:blue)
- try_fixing_it(
- "sudo chown -R #{gitlab_shell_ssh_user}:root #{repo_base_path}"
- )
- for_more_information(
- see_installation_guide_section "GitLab Shell"
- )
- fix_and_rerun
- end
- end
- end
-
- def check_repos_hooks_directory_is_link
- print "hooks directories in repos are links: ... "
-
- gitlab_shell_hooks_path = Gitlab.config.gitlab_shell.hooks_path
-
- unless Project.count > 0
- puts "can't check, you have no projects".color(:magenta)
- return
- end
-
- puts ""
-
- Project.find_each(batch_size: 100) do |project|
- print sanitized_message(project)
- project_hook_directory = File.join(project.repository.path_to_repo, "hooks")
-
- if project.empty_repo?
- puts "repository is empty".color(:magenta)
- elsif File.directory?(project_hook_directory) && File.directory?(gitlab_shell_hooks_path) &&
- (File.realpath(project_hook_directory) == File.realpath(gitlab_shell_hooks_path))
- puts 'ok'.color(:green)
- else
- puts "wrong or missing hooks".color(:red)
- try_fixing_it(
- sudo_gitlab("#{File.join(gitlab_shell_path, 'bin/create-hooks')} #{repository_storage_paths_args.join(' ')}"),
- 'Check the hooks_path in config/gitlab.yml',
- 'Check your gitlab-shell installation'
- )
- for_more_information(
- see_installation_guide_section "GitLab Shell"
- )
- fix_and_rerun
- end
- end
- end
-
def check_gitlab_shell_self_test
gitlab_shell_repo_base = gitlab_shell_path
check_cmd = File.expand_path('bin/check', gitlab_shell_repo_base)
@@ -247,6 +95,26 @@ namespace :gitlab do
end
end
+ namespace :gitaly do
+ desc 'GitLab | Check the health of Gitaly'
+ task check: :gitlab_environment do
+ warn_user_is_not_gitlab
+ start_checking 'Gitaly'
+
+ Gitlab::HealthChecks::GitalyCheck.readiness.each do |result|
+ print "#{result.labels[:shard]} ... "
+
+ if result.success
+ puts 'OK'.color(:green)
+ else
+ puts "FAIL: #{result.message}".color(:red)
+ end
+ end
+
+ finished_checking 'Gitaly'
+ end
+ end
+
namespace :sidekiq do
desc "GitLab | Check the configuration of Sidekiq"
task check: :gitlab_environment do
diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake
index c8a8863443e..e8ae5dfa540 100644
--- a/lib/tasks/gitlab/cleanup.rake
+++ b/lib/tasks/gitlab/cleanup.rake
@@ -1,40 +1,29 @@
-# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/954
-#
+# frozen_string_literal: true
+require 'set'
+
namespace :gitlab do
namespace :cleanup do
- HASHED_REPOSITORY_NAME = '@hashed'.freeze
-
desc "GitLab | Cleanup | Clean namespaces"
task dirs: :gitlab_environment do
- warn_user_is_not_gitlab
+ namespaces = Set.new(Namespace.pluck(:path))
+ namespaces << Storage::HashedProject::ROOT_PATH_PREFIX
- namespaces = Namespace.pluck(:path)
- namespaces << HASHED_REPOSITORY_NAME # add so that it will be ignored
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- git_base_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository_storage.legacy_disk_path }
- all_dirs = Dir.glob(git_base_path + '/*')
+ Gitaly::Server.all.each do |server|
+ all_dirs = Gitlab::GitalyClient::StorageService
+ .new(server.storage)
+ .list_directories(depth: 0)
+ .reject { |dir| dir.ends_with?('.git') || namespaces.include?(File.basename(dir)) }
- puts git_base_path.color(:yellow)
puts "Looking for directories to remove... "
-
- all_dirs.reject! do |dir|
- # skip if git repo
- dir =~ /.git$/
- end
-
- all_dirs.reject! do |dir|
- dir_name = File.basename dir
-
- # skip if namespace present
- namespaces.include?(dir_name)
- end
-
all_dirs.each do |dir_path|
if remove?
- if FileUtils.rm_rf dir_path
- puts "Removed...#{dir_path}".color(:red)
- else
- puts "Cannot remove #{dir_path}".color(:red)
+ begin
+ Gitlab::GitalyClient::NamespaceService.new(server.storage)
+ .remove(dir_path)
+
+ puts "Removed...#{dir_path}"
+ rescue StandardError => e
+ puts "Cannot remove #{dir_path}: #{e.message}".color(:red)
end
else
puts "Can be removed: #{dir_path}".color(:red)
@@ -49,29 +38,29 @@ namespace :gitlab do
desc "GitLab | Cleanup | Clean repositories"
task repos: :gitlab_environment do
- warn_user_is_not_gitlab
-
move_suffix = "+orphaned+#{Time.now.to_i}"
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- repo_root = Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository_storage.legacy_disk_path }
-
- # Look for global repos (legacy, depth 1) and normal repos (depth 2)
- IO.popen(%W(find #{repo_root} -mindepth 1 -maxdepth 2 -name *.git)) do |find|
- find.each_line do |path|
- path.chomp!
- repo_with_namespace = path
- .sub(repo_root, '')
- .sub(%r{^/*}, '')
- .chomp('.git')
- .chomp('.wiki')
-
- # TODO ignoring hashed repositories for now. But revisit to fully support
- # possible orphaned hashed repos
- next if repo_with_namespace.start_with?("#{HASHED_REPOSITORY_NAME}/") || Project.find_by_full_path(repo_with_namespace)
-
- new_path = path + move_suffix
- puts path.inspect + ' -> ' + new_path.inspect
- File.rename(path, new_path)
+
+ Gitaly::Server.all.each do |server|
+ Gitlab::GitalyClient::StorageService
+ .new(server.storage)
+ .list_directories
+ .each do |path|
+ repo_with_namespace = path.chomp('.git').chomp('.wiki')
+
+ # TODO ignoring hashed repositories for now. But revisit to fully support
+ # possible orphaned hashed repos
+ next if repo_with_namespace.start_with?(Storage::HashedProject::ROOT_PATH_PREFIX)
+ next if Project.find_by_full_path(repo_with_namespace)
+
+ new_path = path + move_suffix
+ puts path.inspect + ' -> ' + new_path.inspect
+
+ begin
+ Gitlab::GitalyClient::NamespaceService
+ .new(server.storage)
+ .rename(path, new_path)
+ rescue StandardError => e
+ puts "Error occured while moving the repository: #{e.message}".color(:red)
end
end
end
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 69166851816..74cd70c6e9f 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -51,6 +51,8 @@ namespace :gitlab do
if ActiveRecord::Base.connection.tables.count > 1
Rake::Task['db:migrate'].invoke
else
+ # Add post-migrate paths to ensure we mark all migrations as up
+ Gitlab::Database.add_post_migrate_path_to_rails(force: true)
Rake::Task['db:schema:load'].invoke
Rake::Task['db:seed_fu'].invoke
end
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index e9ca6404fe8..80de3d2ef51 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -1,13 +1,12 @@
namespace :gitlab do
namespace :gitaly do
desc "GitLab | Install or upgrade gitaly"
- task :install, [:dir, :repo] => :gitlab_environment do |t, args|
- require 'toml-rb'
-
+ task :install, [:dir, :storage_path, :repo] => :gitlab_environment do |t, args|
warn_user_is_not_gitlab
- unless args.dir.present?
- abort %(Please specify the directory where you want to install gitaly:\n rake "gitlab:gitaly:install[/home/git/gitaly]")
+ unless args.dir.present? && args.storage_path.present?
+ abort %(Please specify the directory where you want to install gitaly and the path for the default storage
+Usage: rake "gitlab:gitaly:install[/installation/dir,/storage/path]")
end
args.with_defaults(repo: 'https://gitlab.com/gitlab-org/gitaly.git')
@@ -27,7 +26,8 @@ namespace :gitlab do
"BUNDLE_PATH=#{Bundler.bundle_path}")
end
- Gitlab::SetupHelper.create_gitaly_configuration(args.dir)
+ storage_paths = { 'default' => args.storage_path }
+ Gitlab::SetupHelper.create_gitaly_configuration(args.dir, storage_paths)
Dir.chdir(args.dir) do
# In CI we run scripts/gitaly-test-build instead of this command
unless ENV['CI'].present?
@@ -35,17 +35,5 @@ namespace :gitlab do
end
end
end
-
- desc "GitLab | Print storage configuration in TOML format"
- task storage_config: :environment do
- require 'toml-rb'
-
- puts "# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}"
- puts "# This is in TOML format suitable for use in Gitaly's config.toml file."
-
- # Exclude gitaly-ruby configuration because that depends on the gitaly
- # installation directory.
- puts Gitlab::SetupHelper.gitaly_configuration_toml('', gitaly_ruby: false)
- end
end
end
diff --git a/lib/tasks/gitlab/ldap.rake b/lib/tasks/gitlab/ldap.rake
index c66a2a263dc..0459de27c96 100644
--- a/lib/tasks/gitlab/ldap.rake
+++ b/lib/tasks/gitlab/ldap.rake
@@ -1,7 +1,7 @@
namespace :gitlab do
namespace :ldap do
desc 'GitLab | LDAP | Rename provider'
- task :rename_provider, [:old_provider, :new_provider] => :environment do |_, args|
+ task :rename_provider, [:old_provider, :new_provider] => :gitlab_environment do |_, args|
old_provider = args[:old_provider] ||
prompt('What is the old provider? Ex. \'ldapmain\': '.color(:blue))
new_provider = args[:new_provider] ||
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 4fcbbbf8c9d..0ebc6f00793 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -92,9 +92,11 @@ namespace :gitlab do
def setup
warn_user_is_not_gitlab
+ ensure_write_to_authorized_keys_is_enabled
+
unless ENV['force'] == 'yes'
- puts "This will rebuild an authorized_keys file."
- puts "You will lose any data stored in authorized_keys file."
+ puts "This task will now rebuild the authorized_keys file."
+ puts "You will lose any data stored in the authorized_keys file."
ask_to_continue
puts ""
end
@@ -118,4 +120,44 @@ namespace :gitlab do
puts "Quitting...".color(:red)
exit 1
end
+
+ def ensure_write_to_authorized_keys_is_enabled
+ return if Gitlab::CurrentSettings.current_application_settings.authorized_keys_enabled
+
+ puts authorized_keys_is_disabled_warning
+
+ unless ENV['force'] == 'yes'
+ puts 'Do you want to permanently enable the "Write to authorized_keys file" setting now?'
+ ask_to_continue
+ end
+
+ puts 'Enabling the "Write to authorized_keys file" setting...'
+ Gitlab::CurrentSettings.current_application_settings.update!(authorized_keys_enabled: true)
+
+ puts 'Successfully enabled "Write to authorized_keys file"!'
+ puts ''
+ end
+
+ def authorized_keys_is_disabled_warning
+ <<-MSG.strip_heredoc
+ WARNING
+
+ The "Write to authorized_keys file" setting is disabled, which prevents
+ the file from being rebuilt!
+
+ It should be enabled for most GitLab installations. Large installations
+ may wish to disable it as part of speeding up SSH operations.
+
+ See https://docs.gitlab.com/ee/administration/operations/fast_ssh_key_lookup.html
+
+ If you did not intentionally disable this option in Admin Area > Settings,
+ then you may have been affected by the 9.3.0 bug in which the new setting
+ was disabled by default.
+
+ https://gitlab.com/gitlab-org/gitlab-ee/issues/2738
+
+ It was reverted in 9.3.1 and fixed in 9.3.3, however, if Settings were
+ saved while the setting was unchecked, then it is still disabled.
+ MSG
+ end
end
diff --git a/lib/tasks/gitlab/site_statistics.rake b/lib/tasks/gitlab/site_statistics.rake
new file mode 100644
index 00000000000..d97f11b2ed5
--- /dev/null
+++ b/lib/tasks/gitlab/site_statistics.rake
@@ -0,0 +1,15 @@
+namespace :gitlab do
+ desc "GitLab | Refresh Site Statistics counters"
+ task refresh_site_statistics: :environment do
+ puts 'Updating Site Statistics counters: '
+
+ print '* Repositories... '
+ SiteStatistic.transaction do
+ # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
+ ActiveRecord::Base.connection.execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql?
+ SiteStatistic.update_all('repositories_count = (SELECT COUNT(*) FROM projects)')
+ end
+ puts 'OK!'.color(:green)
+ puts
+ end
+end
diff --git a/lib/tasks/gitlab/traces.rake b/lib/tasks/gitlab/traces.rake
index ddcca69711f..5a232091a7e 100644
--- a/lib/tasks/gitlab/traces.rake
+++ b/lib/tasks/gitlab/traces.rake
@@ -18,5 +18,22 @@ namespace :gitlab do
logger.info("Scheduled #{job_ids.count} jobs. From #{job_ids.min} to #{job_ids.max}")
end
end
+
+ task migrate: :environment do
+ logger = Logger.new(STDOUT)
+ logger.info('Starting transfer of job traces')
+
+ Ci::Build.joins(:project)
+ .with_archived_trace_stored_locally
+ .find_each(batch_size: 10) do |build|
+ begin
+ build.job_artifacts_trace.file.migrate!(ObjectStorage::Store::REMOTE)
+
+ logger.info("Transferred job trace of #{build.id} to object storage")
+ rescue => e
+ logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
+ end
+ end
+ end
end
end
diff --git a/lib/tasks/gitlab/update_templates.rake b/lib/tasks/gitlab/update_templates.rake
index a25f7ce59c7..abe10f5580e 100644
--- a/lib/tasks/gitlab/update_templates.rake
+++ b/lib/tasks/gitlab/update_templates.rake
@@ -6,6 +6,8 @@ namespace :gitlab do
desc "GitLab | Update project templates"
task :update_project_templates do
+ include Gitlab::ImportExport::CommandLineUtil
+
if Rails.env.production?
puts "This rake task is not meant fo production instances".red
exit(1)
@@ -52,7 +54,7 @@ namespace :gitlab do
end
Projects::ImportExport::ExportService.new(project, admin).execute
- FileUtils.cp(project.export_project_path, template.archive_path)
+ download_or_copy_upload(project.export_file, template.archive_path)
Projects::DestroyService.new(admin, project).execute
puts "Exported #{template.name}".green
end
@@ -98,10 +100,6 @@ namespace :gitlab do
/(\.{1,2}|LICENSE|Global|\.gitignore)\z/
),
Template.new(
- "https://gitlab.com/gitlab-org/gitlab-ci-yml.git",
- /(\.{1,2}|LICENSE|CONTRIBUTING.md|Pages|autodeploy|\.gitlab-ci.yml)\z/
- ),
- Template.new(
"https://gitlab.com/gitlab-org/Dockerfile.git",
/(\.{1,2}|LICENSE|CONTRIBUTING.md|\.Dockerfile)\z/
)
diff --git a/lib/tasks/gitlab/uploads/migrate.rake b/lib/tasks/gitlab/uploads/migrate.rake
index f548a266b99..1c93609a006 100644
--- a/lib/tasks/gitlab/uploads/migrate.rake
+++ b/lib/tasks/gitlab/uploads/migrate.rake
@@ -1,6 +1,30 @@
namespace :gitlab do
namespace :uploads do
- desc 'GitLab | Uploads | Migrate the uploaded files to object storage'
+ namespace :migrate do
+ desc "GitLab | Uploads | Migrate all uploaded files to object storage"
+ task all: :environment do
+ categories = [%w(AvatarUploader Project :avatar),
+ %w(AvatarUploader Group :avatar),
+ %w(AvatarUploader User :avatar),
+ %w(AttachmentUploader Note :attachment),
+ %w(AttachmentUploader Appearance :logo),
+ %w(AttachmentUploader Appearance :header_logo),
+ %w(FaviconUploader Appearance :favicon),
+ %w(FileUploader Project),
+ %w(PersonalFileUploader Snippet),
+ %w(NamespaceFileUploader Snippet),
+ %w(FileUploader MergeRequest)]
+
+ categories.each do |args|
+ Rake::Task["gitlab:uploads:migrate"].invoke(*args)
+ Rake::Task["gitlab:uploads:migrate"].reenable
+ end
+ end
+ end
+
+ # The following is the actual rake task that migrates uploads of specified
+ # category to object storage
+ desc 'GitLab | Uploads | Migrate the uploaded files of specified type to object storage'
task :migrate, [:uploader_class, :model_class, :mounted_as] => :environment do |task, args|
batch_size = ENV.fetch('BATCH', 200).to_i
@to_store = ObjectStorage::Store::REMOTE
diff --git a/lib/tasks/haml-lint.rake b/lib/tasks/haml-lint.rake
index ad2d034b0b4..786efd14b1a 100644
--- a/lib/tasks/haml-lint.rake
+++ b/lib/tasks/haml-lint.rake
@@ -2,5 +2,16 @@ unless Rails.env.production?
require 'haml_lint/rake_task'
require 'haml_lint/inline_javascript'
+ # Workaround for warnings from parser/current
+ # Keep it even if it no longer emits any warnings,
+ # because we'll still see warnings in console/server anyway,
+ # and we don't need to break static-analysis for this.
+ task :haml_lint do
+ require 'parser'
+ def Parser.warn(*args)
+ puts(*args) # static-analysis ignores stdout if status is 0
+ end
+ end
+
HamlLint::RakeTask.new
end
diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake
index fc59b3f937d..a16d4c47273 100644
--- a/lib/tasks/import.rake
+++ b/lib/tasks/import.rake
@@ -9,7 +9,7 @@ class GithubImport
def initialize(token, gitlab_username, project_path, extras)
@options = { token: token }
@project_path = project_path
- @current_user = User.find_by(username: gitlab_username)
+ @current_user = UserFinder.new(gitlab_username).find_by_username
raise "GitLab user #{gitlab_username} not found. Please specify a valid username." unless @current_user
diff --git a/lib/tasks/lint.rake b/lib/tasks/lint.rake
index 006fcdd31a4..5d673a1a285 100644
--- a/lib/tasks/lint.rake
+++ b/lib/tasks/lint.rake
@@ -34,7 +34,6 @@ unless Rails.env.production?
config_lint
lint:haml
scss_lint
- flay
gettext:lint
gettext:updated_check
lint:static_verification
diff --git a/lib/tasks/migrate/add_limits_mysql.rake b/lib/tasks/migrate/add_limits_mysql.rake
index 9b05876034c..c77fa49d586 100644
--- a/lib/tasks/migrate/add_limits_mysql.rake
+++ b/lib/tasks/migrate/add_limits_mysql.rake
@@ -3,6 +3,7 @@ require Rails.root.join('db/migrate/markdown_cache_limits_to_mysql')
require Rails.root.join('db/migrate/merge_request_diff_file_limits_to_mysql')
require Rails.root.join('db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql')
require Rails.root.join('db/migrate/gpg_keys_limits_to_mysql')
+require Rails.root.join('db/migrate/prometheus_metrics_limits_to_mysql')
desc "GitLab | Add limits to strings in mysql database"
task add_limits_mysql: :environment do
@@ -12,4 +13,5 @@ task add_limits_mysql: :environment do
MergeRequestDiffFileLimitsToMysql.new.up
LimitsCiBuildTraceChunksRawDataForMysql.new.up
IncreaseMysqlTextLimitForGpgKeys.new.up
+ PrometheusMetricsLimitsToMysql.new.up
end
diff --git a/lib/tasks/tokens.rake b/lib/tasks/tokens.rake
index 81829668de8..eec024f9bbb 100644
--- a/lib/tasks/tokens.rake
+++ b/lib/tasks/tokens.rake
@@ -1,4 +1,7 @@
require_relative '../../app/models/concerns/token_authenticatable.rb'
+require_relative '../../app/models/concerns/token_authenticatable_strategies/base.rb'
+require_relative '../../app/models/concerns/token_authenticatable_strategies/insecure.rb'
+require_relative '../../app/models/concerns/token_authenticatable_strategies/digest.rb'
namespace :tokens do
desc "Reset all GitLab incoming email tokens"
@@ -26,13 +29,6 @@ class TmpUser < ActiveRecord::Base
self.table_name = 'users'
- def reset_incoming_email_token!
- write_new_token(:incoming_email_token)
- save!(validate: false)
- end
-
- def reset_feed_token!
- write_new_token(:feed_token)
- save!(validate: false)
- end
+ add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) }
+ add_authentication_token_field :feed_token
end
diff --git a/lib/unfold_form.rb b/lib/unfold_form.rb
index fcd01503d1b..05bb3ed7f1c 100644
--- a/lib/unfold_form.rb
+++ b/lib/unfold_form.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'gt_one_coercion'
class UnfoldForm
diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb
index 53e5ac02e42..aae542f02ac 100644
--- a/lib/uploaded_file.rb
+++ b/lib/uploaded_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "tempfile"
require "tmpdir"
require "fileutils"
diff --git a/lib/version_check.rb b/lib/version_check.rb
index 91ad07feee5..ccf7bb493db 100644
--- a/lib/version_check.rb
+++ b/lib/version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "base64"
# This class is used to build image URL to
diff --git a/locale/ar_SA/gitlab.po b/locale/ar_SA/gitlab.po
new file mode 100644
index 00000000000..4a2b56f2806
--- /dev/null
+++ b/locale/ar_SA/gitlab.po
@@ -0,0 +1,9452 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Arabic\n"
+"Language: ar_SA\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: ar\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 09:29\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index fe79729d6dd..6c6eeeb6580 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: bg\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:39\n"
+"PO-Revision-Date: 2018-10-02 09:31\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -110,9 +118,15 @@ msgstr[1] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -156,14 +170,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] "1 Ñхема"
msgstr[1] "%d Ñхеми"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1st contribution!"
msgstr ""
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Ðабор от графики отноÑно непрекъÑнатата интеграциÑ"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -312,13 +351,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr "ДобавÑне на лиценз"
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -390,6 +435,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -552,13 +612,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -648,12 +720,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ÐаиÑтина ли иÑкате да изтриете този план за Ñхема?"
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr ""
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -798,6 +882,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr "Преглед на файловете"
msgid "Browse files"
msgstr "Разглеждане на файловете"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1502,10 +1622,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1814,9 +1958,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr "Подаване"
msgid "CommitMessage|Add %{file_name}"
msgstr "ДобавÑне на „%{file_name}“"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "ПодаваниÑ"
@@ -1947,16 +2094,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr "РъководÑтво за ÑътрудничеÑтво"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr "Създаване на нов…"
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr "РазклонÑване"
-
msgid "CreateTag|Tag"
msgstr "Етикет"
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr "ЧаÑова зона за „Cron“"
msgid "Cron syntax"
msgstr "СинтакÑÐ¸Ñ Ð½Ð° „Cron“"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr "ПерÑонализирани ÑÑŠÐ±Ð¸Ñ‚Ð¸Ñ Ð·Ð° извеÑÑ‚ÑванÐ
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "ПерÑонализираните нива на извеÑÑ‚Ñване Ñа Ñъщите като нивата за учаÑтие. С перÑонализираните нива на извеÑÑ‚Ñване ще можете да получавате и извеÑÑ‚Ð¸Ñ Ð·Ð° избрани ÑъбитиÑ. За да научите повече, прегледайте %{notification_link}."
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "Ðнализ на циклите"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "Програмиране"
@@ -2274,6 +2442,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2283,6 +2457,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr "Задайте потребителÑки шаблон, използва
msgid "Delete"
msgstr "Изтриване"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "Име на папката"
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2690,6 +2912,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "Планът за Ñхема не може да бъде премахнат"
@@ -2906,6 +3176,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "Файлове"
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Филтриране по Ñъобщение"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "ТърÑене по път"
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr "Първо"
msgid "FirstPushedBy|pushed by"
msgstr "изпращане на промени от"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Разклонение"
-msgstr[1] "РазклонениÑ"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "Разклонение на"
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr "От Ñъздаването на проблема до внедрÑваÐ
msgid "From merge request merge until deploy to production"
msgstr "От прилагането на заÑвката за Ñливане до внедрÑването в крайната верÑиÑ"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,14 +3701,11 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "Към Вашето разклонение"
-
-msgid "GoToYourFork|Fork"
-msgstr "Разклонение"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr ""
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3445,6 +3860,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3457,19 +3875,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "Шаблон за интервала"
msgid "Introducing Cycle Analytics"
msgstr "ПредÑтавÑме Ви анализа на циклите"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,22 +4212,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
msgstr ""
-msgid "Koding"
+msgid "Job|Scroll to bottom"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "ПоÑÐ»ÐµÐ´Ð½Ð¸Ñ %d ден"
@@ -3820,6 +4349,9 @@ msgstr "ПоÑледна Ñхема"
msgid "Last commit"
msgstr "ПоÑледно подаване"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "добавите SSH ключ"
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr "ÐÑма хранилище"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "ÐÑма планове"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr "ÐÑма доÑтатъчно данни"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr "ÐеуÑпешно изпълнение на Ñхема"
msgid "NotificationEvent|Merge merge request"
msgstr "Прилагане на заÑвка за Ñливане"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "Ðов проблем"
@@ -4527,18 +5214,26 @@ msgstr "Филтър"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "СобÑтвеник"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr "Схема"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "СъÑтоÑние"
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr "Ñ ÐµÑ‚Ð°Ð¿Ð¸"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4911,33 +5666,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4947,6 +5777,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr "Проектът „%{project_name}“ беше обновен уÑпеÑ
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "ДоÑтъпът до проекта Ñ‚Ñ€Ñбва да бъде даван поотделно на вÑеки потребител."
@@ -5007,6 +5852,9 @@ msgstr "ИзнаÑÑнето на проекта започна. Ще получ
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -5034,18 +5882,48 @@ msgstr "Ðикога"
msgid "ProjectLifecycle|Stage"
msgstr "Етап"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr "ПрочетиМе"
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr "Премахване на проекта"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5552,10 +6648,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr "Изберете формата на архива"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr "Изберете целеви клон"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5639,6 +6750,9 @@ msgstr "Задайте парола на акаунта Ñи, за да може
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,21 +6765,24 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
-msgstr "ÐаÑтройка на „Koding“"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "зададете парола"
msgid "Settings"
msgstr ""
-msgid "Setup a specific Runner automatically"
-msgstr ""
-
msgid "Share"
msgstr ""
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5962,6 +7106,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr "Създайте %{new_merge_request} Ñ Ñ‚ÐµÐ·Ð¸ промени"
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr "Етикети"
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "Етапът на програмиране показва времето от първото подаване до Ñъздаването на заÑвката за Ñливане. Данните ще бъдат добавени тук автоматично Ñлед като бъде Ñъздадена първата заÑвка за Ñливане."
@@ -6150,6 +7318,9 @@ msgstr "СъвкупноÑтта от ÑÑŠÐ±Ð¸Ñ‚Ð¸Ñ Ð´Ð¾Ð±Ð°Ð²ÐµÐ½Ð¸ към дÐ
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "Връзката на разклонение беше премахната."
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "Етапът от цикъла на разработка"
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Етапът на планиране показва колко е времето от преходната Ñтъпка до изпращането на първото подаване. Това време ще бъде добавено автоматично Ñлед като изпратите първото Ñи подаване."
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "Етапът на преглед и одобрение показва времето от Ñъздаването на заÑвката за Ñливане до прилагането Ñ. Данните ще бъдат добавени автоматично Ñлед като приложите първата Ñи заÑвка за Ñливане."
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "Етапът на подготовка за издаване показва времето между прилагането на заÑвката за Ñливане и внедрÑването на кода в Ñредата на работещата крайна верÑиÑ. Данните ще бъдат добавени автоматично Ñлед като направите първото Ñи внедрÑване в крайната верÑиÑ."
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Етапът на теÑтване показва времето, което е нужно на „Gitlab CI“ да изпълни вÑÑка Ñхема от задачи за Ñвързаната заÑвка за Ñливане. Данните ще бъдат добавени автоматично Ñлед като приключи изпълнението на първата Ви Ñхема."
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "Времето, което отнема вÑеки Ð·Ð°Ð¿Ð¸Ñ Ð¾Ñ‚ данни за ÑÑŠÐ¾Ñ‚Ð²ÐµÑ‚Ð½Ð¸Ñ ÐµÑ‚Ð°Ð¿."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "СтойноÑтта, коÑто Ñе намира в Ñредата на поÑледователноÑтта от наблюдавани данни. Ðапример: медианата на 3, 5 и 9 е 5, а медианата на 3, 5, 7 и 8 е (5+7)/2 = 6."
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr "Това означава, че нÑма да можете да изпр
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr "Качване на нов файл"
msgid "Upload file"
msgstr "Качване на файл"
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr "щракнете за качване"
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6796,6 +8102,9 @@ msgstr "Използване на глобалната Ви наÑтройка Ð
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr "Можете да добавÑте файлове Ñамо когато
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr "Ðе можете да Ñъздавате повече проекти"
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr "ТрÑбва да Ñе впишете, за да отбележите проект ÑÑŠÑ Ð·Ð²ÐµÐ·Ð´Ð°"
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr "Ðуждаете Ñе от разрешение."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "ÐÑма да получавате никакви извеÑÑ‚Ð¸Ñ Ð¿Ð¾ е-поща"
@@ -7246,17 +8561,15 @@ msgstr "Вашето име"
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] "дни"
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "нова заÑвка за Ñливане"
@@ -7757,6 +9140,11 @@ msgstr "извеÑÑ‚Ð¸Ñ Ð¿Ð¾ е-поща"
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "родител"
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/ca_ES/gitlab.po b/locale/ca_ES/gitlab.po
new file mode 100644
index 00000000000..a957023bb25
--- /dev/null
+++ b/locale/ca_ES/gitlab.po
@@ -0,0 +1,9208 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Catalan\n"
+"Language: ca_ES\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: ca\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 11:52\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] "%d capa"
+msgstr[1] "%d capes"
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] "%d petició de fusió"
+msgstr[1] "%d peticions de fusió"
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] "%{text} %{files}"
+msgstr[1] "%{text} %{files} fitxers"
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] "1 grup"
+msgstr[1] "%d grups"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] "1 rol"
+msgstr[1] "%d rols"
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] "1 usuari"
+msgstr[1] "%d usuaris"
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr "2FA activat"
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr "404|No s'ha trobat la pàgina"
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr "Quant al GitLab"
+
+msgid "About GitLab CE"
+msgstr "Quant al GitLab CE"
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr "Informes d'abús"
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr "Compte"
+
+msgid "Account and limit"
+msgstr "Compte i límit"
+
+msgid "Active"
+msgstr "Actiu"
+
+msgid "Active Sessions"
+msgstr "Sessions actives"
+
+msgid "Activity"
+msgstr "Activitat"
+
+msgid "Add"
+msgstr "Afegeix"
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr "Afegeix un directori nou"
+
+msgid "Add reaction"
+msgstr "Afegeix una reacció"
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr "Afegeix usuaris al grup:"
+
+msgid "Add users to group"
+msgstr "Afegeix usuaris al grup"
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr "Text addicional"
+
+msgid "Admin Area"
+msgstr "Àrea d'administració"
+
+msgid "Admin Overview"
+msgstr "Informació general d'administració"
+
+msgid "Admin area"
+msgstr "Àrea d'administració"
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr "Avançat"
+
+msgid "Advanced settings"
+msgstr "Configuració avançada"
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr "Tots els usuaris"
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr "Anònim"
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr "Qualsevol"
+
+msgid "Any Label"
+msgstr "Qualsevol etiqueta"
+
+msgid "Appearance"
+msgstr "Aparença"
+
+msgid "Application"
+msgstr "Aplicació"
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr "Aplicació: %{name}"
+
+msgid "Applications"
+msgstr "Aplicacions"
+
+msgid "Apr"
+msgstr "abr"
+
+msgid "April"
+msgstr "abril"
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr "N'esteu segur?"
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr "Peticions de fusió assignades"
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr "agost"
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr "Autor"
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr "Color de fons"
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/cs_CZ/gitlab.po b/locale/cs_CZ/gitlab.po
index 5e433cec164..9801999299f 100644
--- a/locale/cs_CZ/gitlab.po
+++ b/locale/cs_CZ/gitlab.po
@@ -13,11 +13,14 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: cs\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 09:30\n"
-msgid " and"
+msgid " Status"
msgstr ""
+msgid " and"
+msgstr " a"
+
msgid " degraded on %d point"
msgid_plural " degraded on %d points"
msgstr[0] ""
@@ -41,20 +44,34 @@ msgstr[3] ""
msgid "%d commit"
msgid_plural "%d commits"
+msgstr[0] "%d commit"
+msgstr[1] "%d commity"
+msgstr[2] "%d commitů"
+msgstr[3] "%d commitů"
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] "%d commit pozadu"
+msgstr[1] "%d commity pozadu"
+msgstr[2] "%d commitů pozadu"
+msgstr[3] "%d commitů pozadu"
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d commit behind"
-msgid_plural "%d commits behind"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d exporter"
-msgid_plural "%d exporters"
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -88,13 +105,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -118,10 +128,10 @@ msgstr[3] ""
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%s další commit byl vynechán, aby se předešlo problémům s výkonem."
+msgstr[1] "%s další commity byly vynechány, aby se předešlo problémům s výkonem."
+msgstr[2] "%s dalších commitů bylo vynecháno, aby se předešlo problémům s výkonem."
+msgstr[3] "%s dalších commitů bylo vynecháno, aby se předešlo problémům s výkonem."
msgid "%{actionText} & %{openOrClose} %{noteable}"
msgstr ""
@@ -142,9 +152,15 @@ msgstr[3] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -152,10 +168,10 @@ msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
msgid "%{name}'s avatar"
-msgstr ""
+msgstr "Profilový obrázek %{name}"
msgid "%{nip_domain} can be used as an alternative to a custom domain."
-msgstr ""
+msgstr "%{nip_domain} lze použít jako alternativu vlastní domény."
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -170,7 +186,7 @@ msgid "%{openOrClose} %{noteable}"
msgstr ""
msgid "%{percent}%% complete"
-msgstr ""
+msgstr "%{percent}%% dokonÄeno"
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
@@ -187,21 +203,20 @@ msgstr[2] ""
msgstr[3] ""
msgid "%{text} is available"
-msgstr ""
+msgstr "%{text} je k dispozici"
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -242,6 +257,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -270,23 +292,37 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1st contribution!"
-msgstr ""
+msgstr "1. příspěvek!"
msgid "2FA enabled"
-msgstr ""
+msgstr "Dvoufaktorové ověření povoleno"
msgid "403|Please contact your GitLab administrator to get the permission."
msgstr ""
msgid "403|You don't have the permission to access this page."
-msgstr ""
+msgstr "403 | Nemáte oprávnění pro přístup na tuto stránku."
msgid "404|Make sure the address is correct and the page hasn't moved."
-msgstr ""
+msgstr "404 | Ujistěte se, že adresa je správná a stránka nebyla přesunuta."
msgid "404|Page Not Found"
-msgstr ""
+msgstr "404 | Stránka nebyla nalezena"
msgid "404|Please contact your GitLab administrator if you think this is a mistake."
msgstr ""
@@ -303,6 +339,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -318,12 +357,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -346,13 +391,13 @@ msgid "About auto deploy"
msgstr ""
msgid "About this feature"
-msgstr ""
+msgstr "Informace o tomto vylepšení"
msgid "Abuse Reports"
-msgstr ""
+msgstr "Hlášení o zneužití"
msgid "Abuse reports"
-msgstr ""
+msgstr "Hlášení o zneužití"
msgid "Accept terms"
msgstr ""
@@ -366,13 +411,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -405,15 +453,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr ""
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -421,10 +469,10 @@ msgid "Add new directory"
msgstr ""
msgid "Add reaction"
-msgstr ""
+msgstr "Přidat reakci"
msgid "Add todo"
-msgstr ""
+msgstr "Přidat úkol"
msgid "Add user(s) to the group:"
msgstr ""
@@ -432,9 +480,12 @@ msgstr ""
msgid "Add users to group"
msgstr ""
-msgid "Additional text"
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional text"
+msgstr "DodateÄný text"
+
msgid "Admin Area"
msgstr ""
@@ -444,24 +495,33 @@ msgstr ""
msgid "Admin area"
msgstr ""
-msgid "AdminArea|Stop all jobs"
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
msgstr ""
-msgid "AdminArea|Stop all jobs?"
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
msgstr ""
+msgid "AdminArea|Stop all jobs"
+msgstr "Zastavit všechny úlohy"
+
+msgid "AdminArea|Stop all jobs?"
+msgstr "Zastavit všechny úlohy?"
+
msgid "AdminArea|Stop jobs"
-msgstr ""
+msgstr "Zastavit úlohy"
msgid "AdminArea|Stopping jobs failed"
-msgstr ""
+msgstr "Zastavení úloh selhalo"
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
-msgstr ""
+msgstr "Chystáte se zastavit všechny úlohy. To způsobí přerušení všech aktuálně spuštěných úloh."
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -475,7 +535,7 @@ msgid "AdminSettings|Specify a domain to use by default for every project's Auto
msgstr ""
msgid "AdminUsers|Block user"
-msgstr ""
+msgstr "Blokovat uživatele"
msgid "AdminUsers|Delete User %{username} and contributions?"
msgstr ""
@@ -510,6 +570,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -537,6 +600,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -555,7 +621,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -606,13 +672,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -679,7 +757,7 @@ msgid "Any Label"
msgstr ""
msgid "Appearance"
-msgstr ""
+msgstr "Vzhled"
msgid "Application"
msgstr ""
@@ -691,23 +769,29 @@ msgid "Application: %{name}"
msgstr ""
msgid "Applications"
-msgstr ""
+msgstr "Aplikace"
msgid "Apr"
-msgstr ""
+msgstr "Dub"
msgid "April"
-msgstr ""
+msgstr "Duben"
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -724,31 +808,34 @@ msgid "Are you sure you want to unlock %{path_lock_path}?"
msgstr ""
msgid "Are you sure?"
+msgstr "Jste si jisti?"
+
+msgid "Artifact ID"
msgstr ""
msgid "Artifacts"
-msgstr ""
+msgstr "Artefakty"
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
msgstr ""
msgid "Assign custom color like #FF0000"
-msgstr ""
+msgstr "PÅ™iÅ™aÄte vlastní barvu jako například #FF0000"
msgid "Assign labels"
-msgstr ""
+msgstr "Přiřadit štítky"
msgid "Assign milestone"
-msgstr ""
+msgstr "Přiřadit milník"
msgid "Assign to"
-msgstr ""
+msgstr "Přiřadit k"
msgid "Assigned Issues"
msgstr ""
@@ -757,7 +844,7 @@ msgid "Assigned Merge Requests"
msgstr ""
msgid "Assigned to :name"
-msgstr ""
+msgstr "Přiřazeno k :name"
msgid "Assigned to me"
msgstr ""
@@ -765,7 +852,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -781,10 +868,10 @@ msgid "Audit Events"
msgstr ""
msgid "Aug"
-msgstr ""
+msgstr "Srp"
msgid "August"
-msgstr ""
+msgstr "Srpen"
msgid "Authentication Log"
msgstr ""
@@ -792,9 +879,12 @@ msgstr ""
msgid "Authentication log"
msgstr ""
-msgid "Author"
+msgid "Authentication method"
msgstr ""
+msgid "Author"
+msgstr "Autor"
+
msgid "Authorization code:"
msgstr ""
@@ -814,13 +904,13 @@ msgid "Authorized applications (%{size})"
msgstr ""
msgid "Authors: %{authors}"
-msgstr ""
+msgstr "Autoři: %{authors}"
msgid "Auto DevOps"
msgstr ""
msgid "Auto DevOps enabled"
-msgstr ""
+msgstr "Auto DevOps aktivní"
msgid "Auto DevOps, runners and job artifacts"
msgstr ""
@@ -852,6 +942,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -861,9 +954,12 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
-msgid "Available"
+msgid "Automatically marked as default internal user"
msgstr ""
+msgid "Available"
+msgstr "K dispozici"
+
msgid "Available group Runners : %{runners}"
msgstr ""
@@ -885,9 +981,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -927,6 +1020,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -954,9 +1050,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -1032,6 +1134,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1204,6 +1309,9 @@ msgstr ""
msgid "Browse files"
msgstr ""
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1216,6 +1324,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1228,9 +1339,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1243,39 +1351,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1303,6 +1405,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1360,6 +1465,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1522,6 +1633,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1531,6 +1645,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1540,12 +1657,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1558,10 +1684,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1588,6 +1714,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1612,6 +1744,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1624,6 +1759,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1639,6 +1777,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1663,6 +1804,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1675,15 +1819,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1708,12 +1843,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1756,6 +1885,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1765,6 +1897,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1777,6 +1915,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1792,9 +1933,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1831,12 +1969,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1849,9 +1993,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1870,9 +2020,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1882,6 +2029,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1935,6 +2085,9 @@ msgstr ""
msgid "CommitMessage|Add %{file_name}"
msgstr ""
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr ""
@@ -2007,16 +2160,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2106,6 +2256,9 @@ msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2139,6 +2292,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2244,9 +2406,6 @@ msgstr ""
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr ""
-
msgid "CreateTag|Tag"
msgstr ""
@@ -2262,6 +2421,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2274,6 +2436,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2283,6 +2448,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2292,6 +2460,9 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2304,6 +2475,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -2334,6 +2508,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2343,6 +2523,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2358,6 +2541,9 @@ msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2521,12 +2707,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2539,9 +2731,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2641,7 +2845,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2701,12 +2905,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2737,6 +2962,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2752,6 +2980,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2785,6 +3016,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2800,6 +3034,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2824,6 +3082,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2836,6 +3097,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2848,6 +3115,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2890,6 +3160,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2947,6 +3220,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -2968,6 +3244,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -2980,9 +3259,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -2995,6 +3283,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -3004,6 +3295,18 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -3031,19 +3334,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -3065,6 +3367,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3080,6 +3385,9 @@ msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3095,6 +3403,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3260,33 +3571,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3335,6 +3745,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3356,13 +3769,10 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr ""
-
-msgid "GoToYourFork|Fork"
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Google Code import"
@@ -3425,13 +3835,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3446,6 +3856,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3509,6 +3928,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3521,19 +3943,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3569,6 +3991,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3594,21 +4025,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3618,6 +4073,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3675,6 +4133,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3684,6 +4145,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3702,18 +4166,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3727,6 +4206,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3745,12 +4230,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3790,22 +4284,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
msgstr ""
-msgid "Koding"
+msgid "Job|Show complete raw"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3877,6 +4407,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3890,6 +4423,9 @@ msgstr ""
msgid "Last commit"
msgstr ""
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3914,6 +4450,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3941,6 +4480,64 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3953,9 +4550,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3974,6 +4577,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -4001,6 +4607,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -4055,9 +4664,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4106,15 +4727,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4130,6 +4751,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4232,9 +4856,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4253,9 +4892,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -4274,9 +4934,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4325,6 +4982,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4416,12 +5076,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4443,6 +5112,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4455,6 +5127,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4467,15 +5142,27 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr ""
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4494,6 +5181,9 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4527,6 +5217,9 @@ msgstr ""
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr ""
@@ -4599,18 +5292,28 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4674,9 +5377,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4707,9 +5422,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4728,6 +5449,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -4815,6 +5539,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4836,6 +5563,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4884,12 +5614,6 @@ msgstr ""
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4929,6 +5653,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4959,18 +5692,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4983,33 +5746,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -5019,6 +5857,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -5052,6 +5902,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -5079,6 +5932,9 @@ msgstr ""
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -5106,18 +5962,48 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5157,6 +6043,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5289,6 +6178,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5307,6 +6244,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5346,12 +6289,28 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5403,6 +6362,18 @@ msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5412,6 +6383,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5463,9 +6482,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5478,6 +6509,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5512,9 +6546,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5524,6 +6576,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5539,12 +6606,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5611,12 +6687,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5626,10 +6732,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5644,6 +6753,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr ""
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5677,6 +6789,12 @@ msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5686,6 +6804,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5713,6 +6834,9 @@ msgstr ""
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5725,19 +6849,22 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
-msgid "SetPasswordToCloneLink|set a password"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
-msgid "Settings"
+msgid "SetPasswordToCloneLink|set a password"
msgstr ""
-msgid "Setup a specific Runner automatically"
+msgid "Settings"
msgstr ""
msgid "Share"
@@ -5749,6 +6876,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5825,12 +6955,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5846,13 +6982,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5906,6 +7048,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5936,6 +7081,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5966,6 +7114,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5996,6 +7147,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -6038,6 +7192,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -6071,6 +7228,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6104,6 +7264,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6114,6 +7277,9 @@ msgstr[3] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6195,6 +7361,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6210,6 +7382,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6219,6 +7394,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
@@ -6228,6 +7406,9 @@ msgstr ""
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
@@ -6255,6 +7436,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -6282,6 +7466,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -6294,6 +7481,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -6309,6 +7499,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6318,6 +7514,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6327,9 +7526,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6366,6 +7580,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6417,9 +7643,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6429,6 +7676,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6444,15 +7694,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6659,12 +7924,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6692,7 +7969,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6707,6 +7984,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6722,6 +8002,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6749,6 +8032,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6764,24 +8056,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6818,9 +8125,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6845,15 +8158,15 @@ msgstr ""
msgid "Upload file"
msgstr ""
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr ""
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6869,6 +8182,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6878,6 +8194,9 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6911,6 +8230,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7202,16 +8524,19 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7226,9 +8551,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr ""
@@ -7238,9 +8560,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr ""
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7250,6 +8569,12 @@ msgstr ""
msgid "You need permission."
msgstr ""
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr ""
@@ -7328,19 +8653,15 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "assign yourself"
msgstr ""
@@ -7365,70 +8686,98 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7461,10 +8810,21 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7494,13 +8854,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
-msgstr ""
-
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7509,9 +8866,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7542,12 +8896,16 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7560,6 +8918,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7579,23 +8943,6 @@ msgstr[3] ""
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7614,12 +8961,22 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7629,9 +8986,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7663,6 +9026,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7711,6 +9077,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7750,9 +9119,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7774,9 +9149,26 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7795,9 +9187,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7822,6 +9223,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7840,6 +9244,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr ""
@@ -7849,6 +9256,13 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -7868,6 +9282,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7889,6 +9306,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/da_DK/gitlab.po b/locale/da_DK/gitlab.po
new file mode 100644
index 00000000000..9c7b37f7f18
--- /dev/null
+++ b/locale/da_DK/gitlab.po
@@ -0,0 +1,9208 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Danish\n"
+"Language: da_DK\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: da\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 09:30\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 53335658c82..07568765abb 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 09:28\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -110,9 +118,15 @@ msgstr[1] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -156,14 +170,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] ""
msgstr[1] ""
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1st contribution!"
msgstr ""
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Eine Sammlung von Graphen bezüglich kontinuierlicher Integration"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -312,13 +351,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "Zugriff auf fehlerhafte Speicher wurde vorübergehend deaktiviert, um die Wiederherstellung zu ermöglichen. Für den zukünftigen Zugriff, behebe bitte das Problem und setze danach die Speicherinformationen zurück."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr "Lizenz hinzufügen"
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -390,6 +435,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -552,13 +612,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -648,12 +720,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Bist Du sicher, dass Du diesen Pipeline-Zeitplan löschen möchtest?"
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr "Bist Du sicher?"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -798,6 +882,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr "Dateien durchsuchen"
msgid "Browse files"
msgstr "Dateien durchsuchen"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1502,10 +1622,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1814,9 +1958,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr "Commit"
msgid "CommitMessage|Add %{file_name}"
msgstr "%{file_name} hinzufügen"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "Commits"
@@ -1947,16 +2094,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr "Mitarbeitsanleitung"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr "Erstelle neues..."
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr "Ableger"
-
msgid "CreateTag|Tag"
msgstr "Tag "
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr "Cron Zeitzone"
msgid "Cron syntax"
msgstr "Cron Syntax"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr "Individuelle Benachrichtigungsereignisse"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "Individuelle Benachrichtigungsstufen sind identisch mit den Beteiligungsstufen. Mit individuellen Benachrichtigungsstufen erhältst Du ebenfalls Mitteilungen für ausgewählte Ereignisse. Für weitere Informationen lies %{notification_link}. "
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "Arbeitsablaufsanalysen"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "Entwicklung"
@@ -2274,6 +2442,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2283,6 +2457,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr "Erstelle ein individuelles Muster mittels Cron Syntax"
msgid "Delete"
msgstr "Löschen"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr "Details"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "Verzeichnisname"
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2690,6 +2912,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "Entfernung der Pipelineplanung fehlgeschlagen"
@@ -2906,6 +3176,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "Dateien"
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Filter nach Commit Nachricht"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "Finde über den Pfad"
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr "Erster"
msgid "FirstPushedBy|pushed by"
msgstr "übertragen von"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Ableger"
-msgstr[1] "Ableger"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "Ableger von"
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr "Von der Ticketbeschreibung bis zur Bereitstellung"
msgid "From merge request merge until deploy to production"
msgstr "Vom Umsetzen des Merge Request bis zur Bereitstellung auf dem Produktivsystem"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,14 +3701,11 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "Gehe zu Deinem Ableger"
-
-msgid "GoToYourFork|Fork"
-msgstr "Ableger"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr ""
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3445,6 +3860,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3457,19 +3875,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "Intervallmuster"
msgid "Introducing Cycle Analytics"
msgstr "Arbeitsablaufsanalysen vorgestellt"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,22 +4212,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
msgstr ""
-msgid "Koding"
+msgid "Job|Scroll to bottom"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Letzten %d Tag"
@@ -3820,6 +4349,9 @@ msgstr "Letzte Pipeline"
msgid "Last commit"
msgstr "Letzter Commit"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "einen SSH Schlüssel hinzufügst"
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr "Kein Repository"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "Keine Zeitpläne"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr "Nicht genügend Daten"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr "Fehlgeschlagene Pipeline"
msgid "NotificationEvent|Merge merge request"
msgstr "Merge Request umsetzen"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "Neues Ticket"
@@ -4527,18 +5214,26 @@ msgstr "Filter"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr "Ãœbersicht"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "Besitzer"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "Zustand der Pipeline"
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr "mit Stages"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr "Profil"
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4911,33 +5666,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4947,6 +5777,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr "Das Projekt '%{project_name}' wurde erfolgreich aktualisiert."
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "Jedem Nutzer muss explizit der Zugriff auf das Projekt gewährt werden."
@@ -5007,6 +5852,9 @@ msgstr "Export des Projektes gestartet. Ein Link zum herunterladen wir Dir per E
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "Abonnieren"
@@ -5034,18 +5882,48 @@ msgstr "Niemals"
msgid "ProjectLifecycle|Stage"
msgstr "Stage"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr "Lies mich"
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr "Projekt entfernen"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr "SSH-Schlüssel"
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5552,10 +6648,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr "Archivierungsformat auswählen"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr "Zielbranch auswählen"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5639,6 +6750,9 @@ msgstr "Lege ein Passwort für dein Konto fest, um mittels %{protocol} zu übert
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,21 +6765,24 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
-msgstr "Koding einrichten"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "ein Passwort festlegst"
msgid "Settings"
msgstr "Einstellungen"
-msgid "Setup a specific Runner automatically"
-msgstr ""
-
msgid "Share"
msgstr ""
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "Lege die folgende URL während des Runner Setups fest:"
@@ -5962,6 +7106,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr "Beginne einen %{new_merge_request} mit diesen Änderungen"
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr "Starte den Runner!"
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "Die Entwicklungsphase stellt die Zeit vom ersten Commit bis zum Erstellen eines Merge Requests dar. Sobald Du Deinen ersten Merge Request anlegst, werden dessen Daten automatisch ergänzt."
@@ -6150,6 +7318,9 @@ msgstr "Ereignisse, die für diese Phase ausgewertet wurden."
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "Die Beziehung des Ablegers wurde entfernt."
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "Die Phase des Entwicklungslebenszyklus."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Die Planungsphase stellt die Zeit von der vorherigen Phase bis zum Übertragen des ersten Commits dar. Sobald Du den ersten Commit überträgst, werden dessen Daten hier erscheinen."
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "Die Überprüfungsphase stellt die Zeit vom Anlegen eines Merge Requests bis dessen Umsetzung dar. Sobald Du Deinen ersten Merge Request abschließt, werden dessen Daten hier automatisch angezeigt."
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "Die Staging-Phase stellt die Zeit zwischen der Umsetzung eines Merge Requests und der Bereitstellung des Codes auf dem Produktivsystem dar. Sobald Du das erste Mal auf das Produktivsystem ausgeliefert hast, werden dessen Daten hier automatisch angezeigt."
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Die Testphase stellt die Zeit dar, die GitLab CI benötigt um die Pipelines von zugehörigen Merge Requests abzuarbeiten. Sobald die erste Pipeline abgeschlossen ist, werden deren Daten hier automatisch angezeigt."
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "Zeit, die für das jeweilige Ereignis in der Phase ermittelt wurde."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Median 5. Bei 3, 5, 7, 8 ist der Median (5+7)/2 = 6."
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "Es gibt ein Problem beim Zugriff auf den Gitspeicher:"
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr "Dies bedeutet, dass Du keinen Code übertragen kannst, bevor Du kein lee
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr "Eine Neue Datei hochladen"
msgid "Upload file"
msgstr "Eine Datei hochladen"
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr "Zum Upload klicken"
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "Benutze den folgenden Registrierungstoken während des Setups:"
@@ -6796,6 +8102,9 @@ msgstr "Benutze Deine globalen Benachrichtigungseinstellungen"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr "Du kannst Dateien nur hinzufügen, wenn Du dich auf einem Branch befinde
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr "Du hast die Projektbegrenzung erreicht."
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr "Du musst angemeldet sein, um ein Projekt zu favorisieren."
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr "Du brauchst eine Genehmigung."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "Du wirst keine Benachrichtigungen per E-Mail erhalten."
@@ -7246,17 +8561,15 @@ msgstr "Dein Name"
msgid "Your projects"
msgstr "Deine Projekte"
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] "Tage"
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "Neuer Merge Request"
@@ -7757,6 +9140,11 @@ msgstr "Benachrichtungsemail"
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "Vorgänger"
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po
index b50685514e1..5e14e94e6fc 100644
--- a/locale/en/gitlab.po
+++ b/locale/en/gitlab.po
@@ -882,9 +882,6 @@ msgstr ""
msgid "Set up CI"
msgstr ""
-msgid "Set up Koding"
-msgstr ""
-
msgid "Set up auto deploy"
msgstr ""
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index 1da61800729..84ef902d5e1 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: eo\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 09:29\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -110,9 +118,15 @@ msgstr[1] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -156,14 +170,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] "1 ĉenstablo"
msgstr[1] "%d ĉenstabloj"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1st contribution!"
msgstr ""
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Aro da diagramoj pri la seninterrompa integrado"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -312,13 +351,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr "Aldoni rajtigilon"
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -390,6 +435,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -552,13 +612,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -648,12 +720,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ĉu vi certe volas forigi ĉi tiun ĉenstablan planon?"
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr ""
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -798,6 +882,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr "Foliumi dosierojn"
msgid "Browse files"
msgstr "Elekti dosierojn"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1502,10 +1622,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1814,9 +1958,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr "Enmeti"
msgid "CommitMessage|Add %{file_name}"
msgstr "Aldoni „%{file_name}“"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "Enmetadoj"
@@ -1947,16 +2094,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr "Gvidlinioj por kontribuado"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr "Krei novan…"
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr "Disbranĉigi"
-
msgid "CreateTag|Tag"
msgstr "Etikedo"
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr "Horzono por Cron"
msgid "Cron syntax"
msgstr "La sintakso de Cron"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr "Propraj sciigaj eventoj"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "La propraj sciigaj niveloj estas la samaj kiel la niveloj de partoprenado. Uzante la proprajn sciigajn nivelojn, vi ricevos ankaÅ­ sciigojn por elektitaj de vi eventoj. Por lerni pli, bonvolu vidi %{notification_link}."
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "Cikla analizo"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "Programado"
@@ -2274,6 +2442,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2283,6 +2457,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr "Difini propran Åablonon, uzante la sintakson de Cron"
msgid "Delete"
msgstr "Forigi"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "Nomo de dosierujo"
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2690,6 +2912,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "Ne eblas forigi la ĉenstablan planon"
@@ -2906,6 +3176,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "Dosieroj"
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Filtri per mesaÄo"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "Trovi per dosierindiko"
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr "Unue"
msgid "FirstPushedBy|pushed by"
msgstr "alpuÅita de"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Disbranĉigo"
-msgstr[1] "Disbranĉigoj"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "Disbranĉigita el"
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr "De la kreado de la problemo Äis la disponigado en la publika versio"
msgid "From merge request merge until deploy to production"
msgstr "De la kunfandado de la peto pri kunfando Äis la disponigado en la publika versio"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,14 +3701,11 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "Al via disbranĉigo"
-
-msgid "GoToYourFork|Fork"
-msgstr "Disbranĉigo"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr ""
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3445,6 +3860,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3457,19 +3875,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "Intervala Åablono"
msgid "Introducing Cycle Analytics"
msgstr "Ni prezentas al vi la ciklan analizon"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,22 +4212,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
msgstr ""
-msgid "Koding"
+msgid "Job|Scroll to bottom"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "La lasta %d tago"
@@ -3820,6 +4349,9 @@ msgstr "Lasta ĉenstablo"
msgid "Last commit"
msgstr "Lasta enmetado"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "aldonos SSH-Ålosilon"
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr "Ne estas deponejo"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "Ne estas planoj"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr "Ne estas sufiĉe da datenoj"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr "Malsukcesa ĉenstablo"
msgid "NotificationEvent|Merge merge request"
msgstr "Apliki peton pri kunfando"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "Nova problemo"
@@ -4527,18 +5214,26 @@ msgstr "Filtrilo"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "Posedanto"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr "Ĉenstablo"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "Stato"
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr "kun etapoj"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4911,33 +5666,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4947,6 +5777,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr "La projekto „%{project_name}“ estis sukcese Äisdatigita."
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "Ĉiu uzanto devas akiri propran atingon al la projekto."
@@ -5007,6 +5852,9 @@ msgstr "La elporto de la projekto komenciÄis. Vi ricevos ligilon per retpoÅto
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -5034,18 +5882,48 @@ msgstr "Neniam"
msgid "ProjectLifecycle|Stage"
msgstr "Etapo"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr "LeguMin"
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr "Forigi la projekton"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5552,10 +6648,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr "Elektu formaton de arkivo"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr "Elektu celan branĉon"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5639,6 +6750,9 @@ msgstr "Kreu pasvorton por via konto por ebligi al vi eltiri kaj alpuÅi per %{p
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,21 +6765,24 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
-msgstr "Agordi „Koding“"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "kreos pasvorton"
msgid "Settings"
msgstr ""
-msgid "Setup a specific Runner automatically"
-msgstr ""
-
msgid "Share"
msgstr ""
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5962,6 +7106,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr "Kreu %{new_merge_request} kun ĉi tiuj ÅanÄoj"
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr "Etikedoj"
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "La etapo de programado montras la tempon de la unua enmetado Äis la kreado de la peto pri kunfando. La datenoj aldoniÄos aÅ­tomate ĉi tie post kiam vi kreas la unuan peton pri kunfando."
@@ -6150,6 +7318,9 @@ msgstr "La aro da eventoj, kiuj estas aldonitaj al la datenoj kolektitaj por la
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "La rilato de disbranĉigo estis forigita."
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "La etapo de la disvolva ciklo."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "La etapo de la plano montras la tempon de la antaÅ­a Åtupo Äis la alpuÅado de via unua enmetado. Ĉi tiu tempo aldoniÄos aÅ­tomate post kiam vi alpuÅas la unuan enmetadon."
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "La etapo de la kontrolo montras la tempon de la kreado de la peto pri kunfando Äis Äia aplikado. La datenoj aldoniÄos aÅ­tomate post kiam vi aplikos la unuan peton pri kunfando."
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "La etapo de preparo por eldono montras la tempon inter la aplikado de la peto pri kunfando kaj la disponigado de la kodo en la publika versio. La datenoj aldoniÄos aÅ­tomate post kiam vi faros la unuan disponigadon en la publika versio."
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "La etapo de testado montras kiom da tempo necesas al „GitLab CI“ por plenumi ĉiujn ĉenstablojn por la rilata peto pri kunfando. La datenoj aldoniÄos aÅ­tomate post kiam via unua ĉenstablo finiÄos."
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "La tempo, kiu estas necesa por ĉiu dateno kolektita de la etapo."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "La valoro, kiu troviÄas en la mezo de aro da rigardataj valoroj. Ekzemple: inter 3, 5 kaj 9, la mediano estas 5. Inter 3, 5, 7 kaj 8, la mediano estas (5+7)/2 = 6."
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr "Ĉi tiu signifas, ke vi ne povos alpuÅi kodon, antaÅ­ ol vi kreos malpl
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr "AlÅuti novan dosieron"
msgid "Upload file"
msgstr "AlÅuti dosieron"
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr "alklaku por alÅuti"
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6796,6 +8102,9 @@ msgstr "Uzi vian Äeneralan agordon pri la sciigoj"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr "Oni povas aldoni dosierojn nur kiam oni estas en branĉo"
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr "Vi ne povas krei pliajn projektojn"
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr "Oni devas ensaluti por steligi projekton"
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr "VI bezonas permeson."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "VI ne ricevos sciigojn per retpoÅto"
@@ -7246,17 +8561,15 @@ msgstr "Via nomo"
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] "tagoj"
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "novan peton pri kunfando"
@@ -7757,6 +9140,11 @@ msgstr "sciigoj per retpoÅto"
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "patro"
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index cbcfb392d0c..32e495695c6 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:39\n"
+"PO-Revision-Date: 2018-10-02 09:26\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -110,9 +118,15 @@ msgstr[1] "%{count} participantes"
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -156,14 +170,15 @@ msgstr "%{text} esta disponible"
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} más"
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] "1 pipeline"
msgstr[1] "%d pipelines"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1st contribution!"
msgstr "¡1ra contribución!"
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Una colección de gráficos sobre Integración Continua"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -312,13 +351,16 @@ msgstr "Tokens de acceso"
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr "Agregar Licencia"
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -390,6 +435,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "Detener todos los trabajos"
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr "Página de estado"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -552,13 +612,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr "Se produjo un error al obtener datos de la barra lateral"
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -648,12 +720,18 @@ msgstr "Abril"
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "¿Estás seguro que deseas eliminar esta programación del pipeline?"
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr "¿Estás seguro?"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr "Artefactos"
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr "Asignado a"
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr "Registro de Autenticación"
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -798,6 +882,9 @@ msgstr "Automáticamente construirán, probarán y desplegarán su aplicación c
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "Más información en %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr "Disponible"
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr "Iniciar con el commit seleccionado"
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr "Examinar archivos"
msgid "Browse files"
msgstr "Examinar archivos"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr ""
msgid "Close"
msgstr "Cerrar"
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1502,10 +1622,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr "Crear cluster de Kubernetes"
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr "Integración GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr "Proyecto Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr "Instalar"
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "cluster de Kubernetes"
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Integración de cluster de Kubernetes"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "Conozca más sobre los entornos"
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr "Tipo de máquina"
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Asegúrese de que su cuenta de Google cumpla con los siguientes requisitos:"
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr "Eliminar integración"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "Falló la solicitud para iniciar la instalación"
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Algo salió mal durante la instalación de %{title}"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr "Token"
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "Su cuenta debe tener %{link_to_kubernetes_engine}"
@@ -1814,9 +1958,6 @@ msgstr "documentación"
msgid "ClusterIntegration|help page"
msgstr "página de ayuda"
-msgid "ClusterIntegration|installing applications"
-msgstr "Instalando aplicaciones"
-
msgid "ClusterIntegration|meets the requirements"
msgstr "cumple con los requisitos"
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr "Cambio"
msgid "CommitMessage|Add %{file_name}"
msgstr "Agregar %{file_name}"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "Cambios"
@@ -1947,16 +2094,13 @@ msgstr "Confidencialidad"
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr "Guía de contribución"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr "Crear nuevo..."
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr "Bifurcar"
-
msgid "CreateTag|Tag"
msgstr "Etiqueta"
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr "Zona horaria del Cron"
msgid "Cron syntax"
msgstr "Sintaxis de Cron"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr "Eventos de notificaciones personalizadas"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "Los niveles de notificación personalizados son los mismos que los niveles participantes. Con los niveles de notificación personalizados, también recibirá notificaciones para eventos seleccionados. Para obtener más información, consulte %{notification_link}."
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "Código"
@@ -2274,6 +2442,12 @@ msgstr "Todos"
msgid "DashboardProjects|Personal"
msgstr "Personales"
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr "Dic"
@@ -2283,6 +2457,9 @@ msgstr "Diciembre"
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr "Definir un patrón personalizado con la sintaxis de cron"
msgid "Delete"
msgstr "Eliminar"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr "Detalles"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "Nombre del directorio"
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2690,6 +2912,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr "Actualizado"
msgid "Environments|You don't have any environments right now."
msgstr "No tiene ningún entorno en este momento."
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Se produjo un error al activar/desactivar la suscripción de notificación"
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "Error al eliminar la programación del pipeline"
@@ -2906,6 +3176,9 @@ msgstr "Febrero"
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "Archivos"
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Filtrar por mensaje del cambio"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "Buscar por ruta"
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr "Primer"
msgid "FirstPushedBy|pushed by"
msgstr "enviado por"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Bifurcación"
-msgstr[1] "Bifurcaciones"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "Bifurcado de"
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr "Desde la creación de la incidencia hasta el despliegue a producción"
msgid "From merge request merge until deploy to production"
msgstr "Desde la integración de la solicitud de fusión hasta el despliegue a producción"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,14 +3701,11 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "Ir a tu bifurcación"
-
-msgid "GoToYourFork|Fork"
-msgstr "Bifurcación"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr ""
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Prevenir que se comparta un proyecto de %{group} con otros grupos"
@@ -3445,6 +3860,9 @@ msgstr "No se encuentran grupos"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr "Crear un proyecto en este grupo."
@@ -3457,20 +3875,20 @@ msgstr "Editar grupo"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr "No pudiste dejar el grupo. Por favor, asegúrate que no seas el único propietario."
-msgid "GroupsTree|Filter by name..."
-msgstr "Filtrar por nombre..."
-
msgid "GroupsTree|Leave this group"
msgstr "Dejar este grupo"
msgid "GroupsTree|Loading groups"
msgstr "Cargando grupos"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "Lo sentimos, no existen grupos que coincidan con su búsqueda"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "Lo sentimos, no existen grupos ni proyectos que coincidan con su búsqueda"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
msgid "Have your users email"
msgstr ""
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr "Interno - cualquier usuario que haya iniciado sesión puede ver el grupo
msgid "Internal - The project can be accessed by any logged in user."
msgstr "Interno - cualquier usuario haya iniciado sesión puede acceder a este proyecto."
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "Patrón de intervalo"
msgid "Introducing Cycle Analytics"
msgstr "Introducción a Cycle Analytics"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,6 +4212,48 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
msgid "Jul"
msgstr ""
@@ -3734,12 +4266,6 @@ msgstr ""
msgid "June"
msgstr "Junio"
-msgid "Koding"
-msgstr ""
-
-msgid "Koding Dashboard"
-msgstr ""
-
msgid "Kubernetes"
msgstr ""
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Último %d día"
@@ -3820,6 +4349,9 @@ msgstr "Último Pipeline"
msgid "Last commit"
msgstr "Último cambio"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr "Conozca más"
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "agregar una clave SSH"
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr "No hay repositorio"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "No hay programaciones"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr "Ninguno"
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr "No hay suficientes datos"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr "Pipeline fallido"
msgid "NotificationEvent|Merge merge request"
msgstr "Integrar solicitud de fusión"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "Nueva incidencia"
@@ -4527,18 +5214,26 @@ msgstr "Filtrar"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr "Sólo los miembros de proyecto pueden comentar."
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr "Resumen"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "Propietario"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr "Pipeline"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "Estado del Pipeline"
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr "con etapas"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr "Preferencias"
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr "Perfil"
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4911,33 +5666,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr "Contraseña inválida"
msgid "Profiles|Invalid username"
msgstr "Nombre de usuario inválido"
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Escribe tu %{confirmationValue} para confirmar:"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "No tienes acceso para eliminar este usuario."
@@ -4947,6 +5777,18 @@ msgstr "Debes transferir o eliminar estos grupos antes de que puedas eliminar tu
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Actualmente su cuenta es propietaria de estos grupos:"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr "Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "El acceso al proyecto debe concederse explícitamente a cada usuario."
@@ -5007,6 +5852,9 @@ msgstr "Se inició la exportación del proyecto. Se enviará un enlace de descar
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "Suscribirse"
@@ -5034,18 +5882,48 @@ msgstr "Nunca"
msgid "ProjectLifecycle|Stage"
msgstr "Etapa"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr "Algo salió mal por nuestra parte."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Lo sentimos, no hay proyectos que coincidan con su búsqueda"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr "Público - El proyecto puede ser accedido sin ninguna autenticación."
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr "Léeme"
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr "Eliminar proyecto"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr "Repositorio"
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr "Llaves SSH"
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "Segundos antes de reinicializar información de fallas"
@@ -5552,10 +6648,13 @@ msgstr "Segundos a esperar para intentar acceder al almacenamiento"
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr "Seleccionar formato de archivo"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr "Selecciona una rama de destino"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5639,6 +6750,9 @@ msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a travÃ
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,21 +6765,24 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
-msgstr "Configurar Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "establecer una contraseña"
msgid "Settings"
msgstr "Configuración"
-msgid "Setup a specific Runner automatically"
-msgstr ""
-
msgid "Share"
msgstr ""
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5962,6 +7106,9 @@ msgstr "Proyectos favoritos"
msgid "Start a %{new_merge_request} with these changes"
msgstr "Iniciar una %{new_merge_request} con estos cambios"
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr "Sub-grupos"
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr "Etiquetas"
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr "Equipo"
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."
@@ -6150,6 +7318,9 @@ msgstr "La colección de eventos agregados a los datos recopilados para esa etap
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "La relación con la bifurcación se ha eliminado."
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "La etapa del ciclo de vida de desarrollo."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "La etapa de revisión muestra el tiempo desde la creación de la solicitud de fusión hasta que los cambios se fusionaron. Los datos se añadirán automáticamente después de fusionar su primera solicitud de fusión."
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "El tiempo utilizado por cada entrada de datos obtenido por esa etapa."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr "Esto significa que no puede enviar código hasta que cree un repositorio
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr "Desbloquear"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "Desbloqueado"
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr "Subir nuevo archivo"
msgid "Upload file"
msgstr "Subir archivo"
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr "Hacer clic para subir"
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6796,6 +8102,9 @@ msgstr "Utiliza tu configuración de notificación global"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr "Solo puedes agregar archivos cuando estás en una rama"
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr "Has alcanzado el límite de tu proyecto"
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr "Debes iniciar sesión para destacar un proyecto"
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr "Necesitas permisos."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "No recibirás ninguna notificación por correo electrónico"
@@ -7246,17 +8561,15 @@ msgstr "Tu nombre"
msgid "Your projects"
msgstr "Tus proyectos"
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] "días"
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "nueva solicitud de fusión"
@@ -7757,6 +9140,11 @@ msgstr "correos electrónicos de notificación"
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "padre"
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr "usuario"
diff --git a/locale/et_EE/gitlab.po b/locale/et_EE/gitlab.po
new file mode 100644
index 00000000000..bc6b3b67d42
--- /dev/null
+++ b/locale/et_EE/gitlab.po
@@ -0,0 +1,9208 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Estonian\n"
+"Language: et_EE\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: et\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 09:29\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/fil_PH/gitlab.po b/locale/fil_PH/gitlab.po
index b3275d60379..ecbf47aa928 100644
--- a/locale/fil_PH/gitlab.po
+++ b/locale/fil_PH/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: fil\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 09:29\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -110,9 +118,15 @@ msgstr[1] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -156,14 +170,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] ""
msgstr[1] ""
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1st contribution!"
msgstr ""
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -312,13 +351,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr ""
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -390,6 +435,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -552,13 +612,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -648,12 +720,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr ""
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -798,6 +882,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr ""
msgid "Browse files"
msgstr ""
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1502,10 +1622,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1814,9 +1958,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr ""
msgid "CommitMessage|Add %{file_name}"
msgstr ""
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr ""
@@ -1947,16 +2094,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr ""
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr ""
-
msgid "CreateTag|Tag"
msgstr ""
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -2274,6 +2442,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2283,6 +2457,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2690,6 +2912,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -2906,6 +3176,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,13 +3701,10 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
-msgid "Go to your fork"
+msgid "Go to"
msgstr ""
-msgid "GoToYourFork|Fork"
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Google Code import"
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3445,6 +3860,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3457,19 +3875,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,22 +4212,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
msgstr ""
-msgid "Koding"
+msgid "Job|Keep"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3820,6 +4349,9 @@ msgstr ""
msgid "Last commit"
msgstr ""
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr ""
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr ""
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr ""
@@ -4527,18 +5214,26 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr ""
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4911,33 +5666,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4947,6 +5777,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -5007,6 +5852,9 @@ msgstr ""
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -5034,18 +5882,48 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5552,10 +6648,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr ""
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5639,6 +6750,9 @@ msgstr ""
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,19 +6765,22 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
-msgid "SetPasswordToCloneLink|set a password"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
-msgid "Settings"
+msgid "SetPasswordToCloneLink|set a password"
msgstr ""
-msgid "Setup a specific Runner automatically"
+msgid "Settings"
msgstr ""
msgid "Share"
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5962,6 +7106,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
@@ -6150,6 +7318,9 @@ msgstr ""
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr ""
msgid "Upload file"
msgstr ""
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr ""
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6796,6 +8102,9 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr ""
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr ""
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr ""
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr ""
@@ -7246,17 +8561,15 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] ""
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr ""
@@ -7757,6 +9140,11 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index 165dc97055b..6e89ec73142 100644
--- a/locale/fr/gitlab.po
+++ b/locale/fr/gitlab.po
@@ -13,20 +13,23 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 11:46\n"
+
+msgid " Status"
+msgstr " Statut"
msgid " and"
-msgstr ""
+msgstr " et"
msgid " degraded on %d point"
msgid_plural " degraded on %d points"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "dégradé sur %d point"
+msgstr[1] "dégradé sur %d points"
msgid " improved on %d point"
msgid_plural " improved on %d points"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] " amélioré sur %d point"
+msgstr[1] " amélioré sur %d points"
msgid "%d changed file"
msgid_plural "%d changed files"
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] "%d exportateur"
msgstr[1] "%d exportateurs"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] "%d test en échec"
+msgstr[1] "%d tests en échec"
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] "%d test passé avec succès"
+msgstr[1] "%d tests passés avec succès"
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d ticket"
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] "%d métrique"
msgstr[1] "%d métriques"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] "%d changement à valider"
@@ -85,8 +93,8 @@ msgstr[1] "%d changements qui ne seront pas validés"
msgid "%d vulnerability"
msgid_plural "%d vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d vulnérabilité"
+msgstr[1] "%d vulnérabilités"
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
@@ -100,7 +108,7 @@ msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "%{commit_author_link} a créé %{commit_timeago}"
msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
-msgstr ""
+msgstr "%{counter_storage} (%{counter_repositories} dépôts, %{counter_build_artifacts} artefacts construits, %{counter_lfs_objects} LFS)"
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -110,9 +118,15 @@ msgstr[1] "%{count} participant·e·s"
msgid "%{filePath} deleted"
msgstr "%{filePath} supprimé"
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr "Les %{group_docs_link_start}groupes%{group_docs_link_end} vous permettent de gérer plusieurs projets et d’y collaborer. Les membres d’un groupe ont accès à tous ses projets."
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr "%{issuableType} sera supprimé ! Êtesâ€vous sûr ?"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} Démarré"
@@ -120,7 +134,7 @@ msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr "%{lock_path} est verrouillé par l’utilisateur GitLab %{lock_user_id}"
msgid "%{name}'s avatar"
-msgstr ""
+msgstr "Avatar de %{name}"
msgid "%{nip_domain} can be used as an alternative to a custom domain."
msgstr "%{nip_domain} peut être utilisé comme alternative à un domaine personnalisé."
@@ -156,14 +170,15 @@ msgstr "%{text} est disponible"
msgid "%{title} changes"
msgstr "Changements %{title}"
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "%{staged} changements prêts à être validés et %{unstaged} autres changements"
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr "%{usage_ping_link_start}En savoir plus%{usage_ping_link_end} sur les informations partagées avec GitLab Inc."
+
+msgid "+ %{count} more"
+msgstr "+ %{count} de plus"
+
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} de plus"
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] "une demande de fusion fermée"
msgstr[1] "%d demandes de fusion fermées"
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] "Un groupe"
+msgstr[1] "%d groupes"
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] "une demande de fusion fusionnée"
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] "1 pipeline"
msgstr[1] "%d pipelines"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] "Un rôle"
+msgstr[1] "%d rôles"
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] "Un utilisateur"
+msgstr[1] "%d utilisateurs"
+
msgid "1st contribution!"
msgstr "1ʳᵉ contribution !"
@@ -238,25 +268,28 @@ msgid "404|Please contact your GitLab administrator if you think this is a mista
msgstr "Veuillez contacter votre administrateur GitLab si vous pensez qu’il s’agit d’une erreur."
msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> ajoutera « Par <a href=\"#\">@johnsmith</a> » à tous les tickets et commentaires créés à l’origine par johnsmith@example.com, et tous les tickets initialement assignés à johnsmith@example.com seront assignés à <a href=\"#\">@johnsmith</a>."
msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"John Smith\"</code> ajoutera « Par John Smith » à tous les tickets et commentaires créés à l’origine par johnsmith@example.com."
msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> ajoutera « Par johnsm...@example.com » à tous les tickets et commentaires créés à l’origine par johnsmith@example.com. L’adresse de courriel ou le nom d’utilisateur est masqué pour garantir la confidentialité de l’utilisateur."
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> ajoutera « Par <a href=\"#\">johnsmith@example.com</a> » à tous les tickets et commentaires créés à l’origine par johnsmith@example.com. Par défaut, l’adresse de courriel ou le nom d’utilisateur est masqué pour garantir la confidentialité de l’utilisateur. Utilisez cette option si vous souhaitez afficher l’adresse de courriel complète."
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr "<strong>%{changedFilesLength} modifications non indexées</strong> et <strong>%{stagedFilesLength} modifications d’étape</strong>"
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
-msgstr ""
+msgstr "<strong>%{created_count}</strong> créé(s), <strong>%{accepted_count}</strong> accepté(s)."
msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
-msgstr "<strong>%{created_count}</strong> créés, <strong>%{closed_count}</strong> clos."
+msgstr "<strong>%{created_count}</strong> créé(s), <strong>%{closed_count}</strong> clos."
msgid "<strong>%{group_name}</strong> group members"
-msgstr ""
+msgstr "Membres du groupe <strong>%{group_name}</strong>"
msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
msgstr "<strong>%{pushes}</strong> poussées Git, plus de <strong>%{commits}</strong> commits effectués par <strong>%{people}</strong> contributeurs."
@@ -264,12 +297,18 @@ msgstr "<strong>%{pushes}</strong> poussées Git, plus de <strong>%{commits}</s
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>Supprime</strong> la branche source"
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "Un « exécuteur » est un processus qui exécute une tâche. Vous pouvez configurer autant d’exécuteurs que nécessaire."
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Un ensemble de graphiques concernant l’intégration continue (CI)"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr "Une branche par défaut ne peut pas être choisie pour un projet vide."
+
+msgid "A deleted user"
+msgstr "Un utilisateur supprimé"
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "Une nouvelle branche sera créée dans votre dépôt divergent (fork) et une nouvelle demande de fusion sera lancée."
@@ -277,22 +316,22 @@ msgid "A project is where you house your files (repository), plan your work (iss
msgstr "Un projet est l’endroit où vous hébergez vos fichiers (dépôt), planifiez votre travail (tickets) et publiez votre documentation (wiki), %{among_other_things_link}."
msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
-msgstr ""
+msgstr "Une expression rationnelle qui sera utilisée pour déterminer la sortie du test lors du traçage d’une tâche. Laissez vide pour désactiver"
msgid "A user with write access to the source branch selected this option"
msgstr "Une personne avec un accès en écriture à la branche source a sélectionné cette option"
msgid "About GitLab"
-msgstr ""
+msgstr "À propos de GitLab"
msgid "About GitLab CE"
-msgstr ""
+msgstr "À propos de GitLab CE"
msgid "About auto deploy"
msgstr "À propos de l’autoâ€déploiement"
msgid "About this feature"
-msgstr ""
+msgstr "À propos de cette fonctionnalité"
msgid "Abuse Reports"
msgstr "Rapports d’abus"
@@ -304,21 +343,24 @@ msgid "Accept terms"
msgstr "Accepter les conditions"
msgid "Accepted MR"
-msgstr ""
+msgstr "Demandes de fusion acceptées"
msgid "Access Tokens"
msgstr "Jetons d’accès"
msgid "Access denied! Please verify you can add deploy keys to this repository."
-msgstr ""
+msgstr "Accès refusé ! Veuillez vérifier que vous pouvez ajouter des clefs de déploiement à ce dépôt."
+
+msgid "Access expiration date"
+msgstr "Date d’expiration de l’accès"
msgid "Access to '%{classification_label}' not allowed"
-msgstr ""
+msgstr "Accès à « %{classification_label} » non autorisé"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "L’accès aux stockages défaillants a été temporairement désactivé pour permettre la récupération du montage. Réinitialisez les informations de stockage quand le problème sera résolu pour permettre à nouveau l’accès."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "Accédez à votre jeton d’exécuteur, personnalisez la configuration de votre pipeline et affichez l’état de votre pipeline et le rapport de couverture."
msgid "Account"
@@ -337,7 +379,7 @@ msgid "Activity"
msgstr "Activité"
msgid "Add"
-msgstr ""
+msgstr "Ajouter"
msgid "Add Changelog"
msgstr "Ajouter un journal des modifications"
@@ -346,22 +388,22 @@ msgid "Add Contribution guide"
msgstr "Ajouter un guide de contribution"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Ajoutez des Webhooks de groupe avec GitLab Enterprise Edition."
msgid "Add Kubernetes cluster"
msgstr "Ajouter une grappe de serveurs Kubernetes"
-msgid "Add License"
-msgstr "Ajouter une licence"
-
msgid "Add Readme"
msgstr "Ajouter un fichier Readme"
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
-msgstr ""
+msgstr "Ajouter un texte apparaissant dans toutes communications par courriel (%{character_limit} caractères maximum)"
+
+msgid "Add license"
+msgstr "Ajouter une licence"
msgid "Add new application"
-msgstr ""
+msgstr "Ajouter une nouvelle application"
msgid "Add new directory"
msgstr "Ajouter un nouveau dossier"
@@ -373,22 +415,31 @@ msgid "Add todo"
msgstr "Ajouter à la liste « à faire »"
msgid "Add user(s) to the group:"
-msgstr ""
+msgstr "Ajouter un ou des utilisateurs au groupe :"
msgid "Add users to group"
-msgstr ""
+msgstr "Ajouter des utilisateurs au groupe"
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr "L’ajout de nouvelles applications est désactivé dans votre instance GitLab. Veuillez contacter votre administrateur GitLab pour en obtenir la permission."
msgid "Additional text"
-msgstr ""
+msgstr "Texte supplémentaire"
msgid "Admin Area"
-msgstr ""
+msgstr "Espace d’administration"
msgid "Admin Overview"
-msgstr ""
+msgstr "Vue administrateur"
msgid "Admin area"
-msgstr ""
+msgstr "Espace d’administration"
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr "Vous êtes sur le point de supprimer définitivement l’utilisateur %{username}. Les tickets, demandes de fusion et les groupes qui leur sont liés seront transférés à un « utilisateur fantôme » du système. Pour éviter toute perte de données, envisagez plutôt d’utiliser la fonctionnalité « %{strong_start}bloquer l’utilisateur%{strong_end} ». L’action « %{strong_start}supprimer l’utilisateur%{strong_end} » ne peut être annulée ni faire l’objet d’une restauration."
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr "Vous êtes sur le point de supprimer définitivement l’utilisateur %{username}. Cette action supprimera également les tickets, les demandes de fusion et les groupes qui leur sont liés. Pour éviter toute perte de données, envisagez plutôt d’utiliser la fonctionnalité « %{strong_start}bloquer l’utilisateur%{strong_end} ». L’action « %{strong_start}supprimer l’utilisateur%{strong_end} » ne peut être annulée ni faire l’objet d’une restauration."
msgid "AdminArea|Stop all jobs"
msgstr "Arrêter toutes les tâches"
@@ -408,6 +459,9 @@ msgstr "Vous êtes sur le point d’arrêter toutes les tâches. Toutes les tâc
msgid "AdminHealthPageLink|health page"
msgstr "État des services"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr "Vous êtes sur le point de supprimer définitivement le projet %{projectName}, son dépôt et toutes les ressources qui lui sont liées, y compris les tickets, les demandes de fusion, etc. Après sa confirmation par un clic sur « %{strong_start}supprimer le projet%{strong_end} », l’action ne peut être annulée ni faire l’objet d’une restauration."
+
msgid "AdminProjects|Delete"
msgstr "Supprimer"
@@ -456,6 +510,9 @@ msgstr "Toutes les modifications sont validées"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "Toutes les fonctionnalités sont activées pour les projets vierges, à partir de modèles ou lors de l’importation, mais vous pouvez les désactiver ultérieurement dans les paramètres du projet."
+msgid "All users"
+msgstr "Tous les utilisateurs"
+
msgid "Allow commits from members who can merge to the target branch."
msgstr "Autoriser les commits des membres qui peuvent fusionner dans la branche cible."
@@ -478,22 +535,25 @@ msgid "Also called \"Relying party service URL\" or \"Reply URL\""
msgstr "Aussi appelée « adresse URL du service du tiers de confiance » ou « URL de réponse »"
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr ""
+msgstr "Vous pouvez également utiliser un %{personal_access_token_link}. Lorsque vous créerez votre jeton d’accès personnel, sélectionnez la portée <code>dépôt</code>, afin de permettre l’affichage d’une liste de vos dépôts publics et privés disponibles à la connexion."
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
-msgstr "Vous pouvez également utiliser un %{personal_access_token_link}. Lorsque vous créerez votre jeton d’accès, vous devrez sélectionner le champ <code>repo</code>, afin que nous puissions afficher une liste de vos dépôts publics et privés qui sont disponibles pour être importés."
+msgstr "Vous pouvez également utiliser un %{personal_access_token_link}. Lorsque vous créerez votre jeton d’accès personnel, sélectionnez la portée <code>dépôt</code>, afin de permettre l’affichage d’une liste de vos dépôts publics et privés qui pourront être importés."
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr "Une paire de clefs SSH sera automatiquement générée après soumission du formulaire. Pour plus d’informations, veuillez vous référer à la documentation."
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
-msgstr ""
+msgstr "Une application appelée %{link_to_client} demande l’accès à votre compte GitLab."
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
-msgstr ""
+msgstr "Un champ utilisateur Gitlab vide ajoutera le nom complet de l’utilisateur FogBugz (p. ex., « Par John Smith ») dans la description de tous les tickets et commentaires. Il associera ou assignera ces tickets et commentaires au créateur du projet."
msgid "An error accured whilst committing your changes."
-msgstr ""
+msgstr "Une erreur est survenue lors du commit de vos modifications."
msgid "An error has occurred"
-msgstr ""
+msgstr "Une erreur est survenue"
msgid "An error occured creating the new branch."
msgstr "Une erreur est survenue lors de la création de la nouvelle branche."
@@ -501,7 +561,7 @@ msgstr "Une erreur est survenue lors de la création de la nouvelle branche."
msgid "An error occured whilst fetching the job trace."
msgstr "Une erreur est survenue pendant le rapatriement de la trace de la tâche."
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr "Une erreur est survenue lors du rapatriement de dernier pipeline."
msgid "An error occured whilst loading all the files."
@@ -544,7 +604,7 @@ msgid "An error occurred while dismissing the alert. Refresh the page and try ag
msgstr "Une erreur s’est produite lors de la révocation de l’alerte. Actualisez la page et essayez à nouveau."
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
-msgstr "Une erreur s’est produite lors de la révocation de la mise en avant de la fonctionnalité. Actualisez la page et essayez de la révoquer à nouveau."
+msgstr "Une erreur s’est produite lors du rejet de la mise en avant de la fonctionnalité. Actualisez la page et essayez de la rejeter à nouveau."
msgid "An error occurred while fetching markdown preview"
msgstr "Une erreur s’est produite lors de la prévisualisation markdown"
@@ -552,20 +612,32 @@ msgstr "Une erreur s’est produite lors de la prévisualisation markdown"
msgid "An error occurred while fetching sidebar data"
msgstr "Une erreur s’est produite lors de la récupération des données de la barre latérale"
+msgid "An error occurred while fetching stages."
+msgstr "Une erreur est survenue lors de la récupération des étapes."
+
+msgid "An error occurred while fetching the job log."
+msgstr "Une erreur est survenue pendant la récupération du journal de la tâche."
+
+msgid "An error occurred while fetching the job."
+msgstr "Une erreur est survenue pendant la récupération de la tâche."
+
+msgid "An error occurred while fetching the jobs."
+msgstr "Une erreur est survenue pendant la récupération des tâches."
+
msgid "An error occurred while fetching the pipeline."
msgstr "Une erreur est survenue pendant la récupération du pipeline."
msgid "An error occurred while getting projects"
msgstr "Une erreur s’est produite lors de la récupération des projets"
-msgid "An error occurred while importing project: ${details}"
-msgstr "Une erreur s’est produite lors de l’importation du projet : ${details}"
+msgid "An error occurred while importing project: %{details}"
+msgstr "Une erreur est survenue lors de l’importation du projet : %{details}"
msgid "An error occurred while initializing path locks"
msgstr "Une erreur est survenue lors de l’initialisation des verrous des chemins d’accès"
msgid "An error occurred while loading commit signatures"
-msgstr ""
+msgstr "Une erreur s’est produite lors du chargement des signatures du commit"
msgid "An error occurred while loading diff"
msgstr "Une erreur s’est produite lors du chargement du diff"
@@ -595,7 +667,7 @@ msgid "An error occurred while retrieving diff"
msgstr "Une erreur s’est produite lors de la récupération du diff"
msgid "An error occurred while saving LDAP override status. Please try again."
-msgstr "Une erreur est survenue lors de l’enregistrement du statut du remplacement LDAP. Veuillez réessayer."
+msgstr "Une erreur est survenue lors de l’enregistrement du statut d’outrepassement de l’annuaire LDAP. Veuillez réessayer."
msgid "An error occurred while saving assignees"
msgstr "Une erreur s’est produite lors de l’enregistrement des destinataires"
@@ -607,34 +679,34 @@ msgid "An error occurred while unsubscribing to notifications."
msgstr "Une erreur est survenue lors du désabonnement aux notifications."
msgid "An error occurred while validating username"
-msgstr "Une erreur s’est produite lors de la validation du nom d’utilisateur·rice"
+msgstr "Une erreur s’est produite lors de la validation du nom d’utilisateur"
msgid "An error occurred. Please try again."
msgstr "Une erreur est survenue. Merci de réessayer."
msgid "Anonymous"
-msgstr ""
+msgstr "Anonyme"
msgid "Anti-spam verification"
-msgstr ""
+msgstr "Vérification antiâ€pourriel"
msgid "Any"
-msgstr ""
+msgstr "Tout"
msgid "Any Label"
-msgstr ""
+msgstr "Toute étiquette"
msgid "Appearance"
msgstr "Apparence"
msgid "Application"
-msgstr ""
+msgstr "Application"
msgid "Application Id"
-msgstr ""
+msgstr "Identifiant de l’application"
msgid "Application: %{name}"
-msgstr ""
+msgstr "Application : %{name}"
msgid "Applications"
msgstr "Applications"
@@ -648,17 +720,23 @@ msgstr "avril"
msgid "Archived project! Repository and other project resources are read-only"
msgstr "Projet archivé ! Le dépôt et les autres ressources du projet sont en lecture seule"
+msgid "Archived projects"
+msgstr "Projets archivés"
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Êtesâ€vous sûr·e de vouloir supprimer ce pipeline programmé ?"
msgid "Are you sure you want to lose unsaved changes?"
-msgstr ""
+msgstr "Êtesâ€vous vraiment prêt(e) à perdre les modifications non enregistrées ?"
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr "Êtesâ€vous sûr de vouloir générer une nouvelle paire de clefs ? Vous devrez copier la nouvelle clef publique sur le serveur distant pour que la mise en miroir refonctionne."
msgid "Are you sure you want to remove %{group_name}?"
-msgstr ""
+msgstr "Voulezâ€vous vraiment supprimer %{group_name} ?"
msgid "Are you sure you want to remove this identity?"
-msgstr "Êtes-vous sûr·e de vouloir supprimer cette identité ?"
+msgstr "Êtesâ€vous sûr(e) de vouloir supprimer cette identité ?"
msgid "Are you sure you want to reset registration token?"
msgstr "Êtesâ€vous sûr·e de vouloir réinitialiser le jeton d’inscription ?"
@@ -672,13 +750,16 @@ msgstr "Êtesâ€vous sûr·e de vouloir déverrouiller %{path_lock_path} ?"
msgid "Are you sure?"
msgstr "Êtesâ€vous certain(e) ?"
+msgid "Artifact ID"
+msgstr "Identifiant de l’artefact"
+
msgid "Artifacts"
msgstr "Artéfacts"
msgid "Ascending"
-msgstr ""
+msgstr "Croissant"
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr "Demandez au responsable du groupe de configurer un exécuteur de groupe."
msgid "Assertion consumer service URL"
@@ -711,11 +792,11 @@ msgstr "Assigné à moi"
msgid "Assignee"
msgstr "Assigné·e"
-msgid "Assignee boards not available with your current license"
-msgstr ""
+msgid "Assignee lists not available with your current license"
+msgstr "La liste des personnes assignées n’est pas disponible avec votre licence actuelle"
msgid "Assignee lists show all issues assigned to the selected user."
-msgstr ""
+msgstr "Les listes d’assignation montrent tous les bogues assignés à l’utilisateur sélectionné."
msgid "Assignee(s)"
msgstr "Assigné·e(s)"
@@ -724,7 +805,7 @@ msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Attachez un fichier par glisserâ€déposer ou %{upload_link}"
msgid "Audit Events"
-msgstr ""
+msgstr "Événements d’audit"
msgid "Aug"
msgstr "août"
@@ -736,34 +817,37 @@ msgid "Authentication Log"
msgstr "Journal d’authentification"
msgid "Authentication log"
-msgstr ""
+msgstr "Journal d’authentification"
+
+msgid "Authentication method"
+msgstr "Méthode d’authentification"
msgid "Author"
msgstr "Auteur·e"
msgid "Authorization code:"
-msgstr ""
+msgstr "Code d’autorisation :"
msgid "Authorization was granted by entering your username and password in the application."
-msgstr ""
+msgstr "L’autorisation a été accordée via la saisie de vos nom d’utilisateur et mot de passe dans l’application."
msgid "Authorize"
-msgstr ""
+msgstr "Autoriser"
msgid "Authorize %{link_to_client} to use your account?"
-msgstr ""
+msgstr "Autoriser %{link_to_client} à utiliser votre compte ?"
msgid "Authorized At"
-msgstr ""
+msgstr "Autorisé à"
msgid "Authorized applications (%{size})"
-msgstr ""
+msgstr "Applications autorisées (%{size})"
msgid "Authors: %{authors}"
msgstr "Auteur·e·s : %{authors}"
msgid "Auto DevOps"
-msgstr ""
+msgstr "Auto DevOps"
msgid "Auto DevOps enabled"
msgstr "Auto DevOps activé"
@@ -781,7 +865,7 @@ msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Auto Review Apps et Auto Deploy ont besoin d’un nom de domaine qui fonctionne correctement."
msgid "Auto-cancel redundant, pending pipelines"
-msgstr ""
+msgstr "Annuler automatiquement les pipelines redondants en attente"
msgid "AutoDevOps|Auto DevOps"
msgstr "Auto DevOps"
@@ -798,6 +882,9 @@ msgstr "Auto DevOps va automatiquement construire, tester et déployer votre app
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "Apprenezâ€en davantage en consultant la %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr "Le pipeline Auto DevOps a été activé et sera utilisé si aucun autre fichier de configuration d’intégration continue n’est trouvé. %{more_information_link}"
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr "Vous pouvez automatiquement générer et tester votre application si vous %{link_to_auto_devops_settings} pour ce projet. Vous pouvez aussi la déployer automatiquement, si vous %{link_to_add_kubernetes_cluster}."
@@ -807,6 +894,9 @@ msgstr "ajoutez une grappe de serveurs Kubernetes"
msgid "AutoDevOps|enable Auto DevOps"
msgstr "activez Auto DevOps"
+msgid "Automatically marked as default internal user"
+msgstr "Marqué automatiquement comme utilisateur interne par défaut"
+
msgid "Available"
msgstr "Disponible"
@@ -826,16 +916,13 @@ msgid "Background Color"
msgstr "Couleur de fond"
msgid "Background Jobs"
-msgstr ""
+msgstr "Tâches de fond"
msgid "Background color"
msgstr "Couleur d’arrièreâ€plan"
-msgid "Background jobs"
-msgstr "Tâches de fond"
-
msgid "Badges"
-msgstr "Badges"
+msgstr "Badges numériques"
msgid "Badges|A new badge was added."
msgstr "Un nouveau badge a été ajouté."
@@ -871,7 +958,10 @@ msgid "Badges|No badge image"
msgstr "Pas d’image de badge"
msgid "Badges|No image to preview"
-msgstr "Pas d’image à prévisualiser"
+msgstr "Aucune image à prévisualiser"
+
+msgid "Badges|Please fill in a valid URL"
+msgstr "Veuillez saisir une URL valide"
msgid "Badges|Project Badge"
msgstr "Badge de projet"
@@ -900,8 +990,14 @@ msgstr "Ce groupe n’a pas de badge"
msgid "Badges|This project has no badges"
msgstr "Ce projet n’a pas de badge"
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr "Vous êtes sur le point de supprimer ce badge. Les badges supprimés <strong>ne peuvent pas</strong> être restaurés."
+
msgid "Badges|Your badges"
-msgstr "Vos badges"
+msgstr "Vos badges numériques"
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr "p. ex. %{exampleUrl}"
msgid "Begin with the selected commit"
msgstr "Commencer avec le commit sélectionné"
@@ -910,19 +1006,19 @@ msgid "Below are examples of regex for existing tools:"
msgstr "Voici quelques exemples d’expressions rationnelles pour des outils existants :"
msgid "Below you will find all the groups that are public."
-msgstr ""
+msgstr "Vous trouverez ciâ€dessous tous les groupes publics."
msgid "Billing"
msgstr "Facturation"
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
-msgstr "%{group_name} est actuellement sur le calendrier de facturation %{plan_link}."
+msgstr "%{group_name} est actuellement sur le forfait %{plan_link}."
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
-msgstr "La rétrogradation automatique et la mise à niveau vers certains calendriers de facturation ne sont actuellement pas disponibles."
+msgstr "La rétrogradation automatique et la mise à niveau vers certains forfaits ne sont actuellement pas disponibles."
msgid "BillingPlans|Current plan"
-msgstr "Calendrier actuel"
+msgstr "Forfait actuel"
msgid "BillingPlans|Customer Support"
msgstr "Service client"
@@ -931,13 +1027,13 @@ msgid "BillingPlans|Downgrade"
msgstr "Rétrograder"
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
-msgstr ""
+msgstr "Renseignez-vous sur nos forfaits en lisant notre %{faq_link}, où essayez gratuitement l’édition Or sur GitLab.com pendant 30 jours."
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
-msgstr "En savoir plus sur chaque calendrier de facturation en lisant notre %{faq_link}."
+msgstr "Apprenezâ€en plus sur chacun des forfaits en lisant notre %{faq_link}."
msgid "BillingPlans|Manage plan"
-msgstr "Gérer le calendrier"
+msgstr "Gérer les forfaits"
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
msgstr "Veuillez contacter le %{customer_support_link} dans ce cas."
@@ -946,22 +1042,22 @@ msgid "BillingPlans|See all %{plan_name} features"
msgstr "Afficher toutes les caractéristiques de %{plan_name}"
msgid "BillingPlans|This group uses the plan associated with its parent group."
-msgstr "Ce groupe utilise le plan associé à son groupe parent."
+msgstr "Ce groupe utilise le forfait associé à son groupe parent."
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr "Pour gérer le calendrier de facturation de ce groupe, rendez vous à la section facturation de %{parent_billing_page_link}."
+msgstr "Pour gérer le forfait de ce groupe, rendez vous à la section facturation de %{parent_billing_page_link}."
msgid "BillingPlans|Upgrade"
msgstr "Mise à niveau"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
-msgstr "Vous êtes actuellement sur le calendrier %{plan_link}."
+msgstr "Vous êtes actuellement abonné au forfait %{plan_link}."
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
-msgstr ""
+msgstr "Votre essai gratuit sur GitLab.com a expiré le %{expiration_date}. %{learn_more_text}"
msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
-msgstr ""
+msgstr "Votre essai gratuit de l’édition Or <strong>expirera le %{expiration_date}</strong>. Vous pouvez en apprendre plus sur l’édition Or de GitLab.com en lisant notre %{features_link}."
msgid "BillingPlans|features"
msgstr "caractéristiques"
@@ -978,11 +1074,14 @@ msgstr "au prix annuel de %{price_per_year}"
msgid "BillingPlans|per user"
msgstr "par utilisateur"
+msgid "Bitbucket Server Import"
+msgstr "Importation d’un serveur Bitbucket"
+
msgid "Bitbucket import"
msgstr "Importation de Bitbucket"
msgid "Blog"
-msgstr ""
+msgstr "Blog"
msgid "Boards"
msgstr "Tableaux"
@@ -1047,10 +1146,10 @@ msgid "Branches|Delete protected branch '%{branch_name}'?"
msgstr "Supprimer la branche protégée « %{branch_name} » ?"
msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
-msgstr "La suppression de la branche « %{branch_name} » ne peut être annulée. Êtesâ€vous sûr·e ?"
+msgstr "La suppression de la branche « %{branch_name} » ne peut être annulée. Êtesâ€vous sûr(e) ?"
msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "La suppression des branches fusionnées ne peut être annulée. Êtesâ€vous sûr·e ?"
+msgstr "La suppression des branches fusionnées ne peut être annulée. Êtesâ€vous sûr(e) ?"
msgid "Branches|Filter by branch name"
msgstr "Filtrer par nom de branche"
@@ -1148,6 +1247,9 @@ msgstr "Parcourir les fichiers"
msgid "Browse files"
msgstr "Parcourir les fichiers"
+msgid "Built-In"
+msgstr "Intégré"
+
msgid "Business metrics (Custom)"
msgstr "Métriques commerciales (personnalisées)"
@@ -1160,6 +1262,9 @@ msgstr "Intégration et livraison continues"
msgid "CI / CD Settings"
msgstr "Paramètres CI / CD (intégration et livraison continues)"
+msgid "CI will run using the credentials assigned above."
+msgstr "L’intégration continue fonctionnera avec les paramètres d’authentification ciâ€dessus."
+
msgid "CI/CD"
msgstr "Intégration et livraison continues"
@@ -1172,9 +1277,6 @@ msgstr "Intégration et livraison continues pour dépôt externe"
msgid "CI/CD settings"
msgstr "Paramètres de l’intégration et de la livraison continues"
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr "Un %{ci_file} explicite a besoin d’être spécifié avant que vous puissiez commencer à utiliser l’intégration et la livraison continues."
-
msgid "CICD|Auto DevOps"
msgstr "Auto DevOps"
@@ -1187,44 +1289,38 @@ msgstr "Déploiement automatique pour « staging », déploiement manuel pour
msgid "CICD|Continuous deployment to production"
msgstr "Déploiement continu en production"
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr "Pipeline Auto DevOps par défaut"
+
msgid "CICD|Deployment strategy"
msgstr "Stratégie de déploiement"
msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr "La stratégie de déploiement nécessite un nom de domaine pour fonctionner correctement."
-msgid "CICD|Disable Auto DevOps"
-msgstr "Désactiver Auto DevOps"
-
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr "Ne définissez pas un domaine ici si vous mettez en place plusieurs grappes de serveurs Kubernetes avec Auto DevOps."
-msgid "CICD|Enable Auto DevOps"
-msgstr "Activer Auto DevOps"
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr "Utiliser la valeur par défaut de l’instance pour avoir Auto DevOps activé ou désactivé quand il n’y a pas de %{ci_file} spécifique au projet."
-
-msgid "CICD|Instance default (%{state})"
-msgstr "Valeur par défaut de l’instance (%{state})"
-
msgid "CICD|Jobs"
msgstr "Tâches"
msgid "CICD|Learn more about Auto DevOps"
msgstr "En savoir plus à propos d’Auto DevOps"
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
-msgstr "La configuration de pipeline de l’Auto DevOps sera utilisée lorsqu’il n’y a pas de %{ci_file} dans le projet."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr "Le pipeline Auto DevOps sera exécuté si aucun autre fichier de configuration n’est trouvé."
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr "Vous devez spécifier un domaine si vous voulez utiliser la revue automatique d’applications Auto Review Apps et le déploiement automatique d’étapes Auto Deploy stages."
+msgid "CICD|instance enabled"
+msgstr "instance activée"
+
msgid "Callback URL"
-msgstr ""
+msgstr "URL de retour"
msgid "Callback url"
-msgstr ""
+msgstr "URL de retour"
msgid "Can't find HEAD commit for this branch"
msgstr "Impossible de trouver le dernier commit (HEAD) pour cette branche"
@@ -1247,6 +1343,9 @@ msgstr "Empreinte du certificat"
msgid "Change Weight"
msgstr "Modifier le poids"
+msgid "Change template"
+msgstr "Changer de modèle"
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr "Modifiez cette valeur pour influencer la fréquence d’interrogation de l’interface utilisateur GitLab pour les mises à jour."
@@ -1293,10 +1392,10 @@ msgid "Cherry-pick this merge request"
msgstr "Picorer cette demande de fusion"
msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
-msgstr ""
+msgstr "Sélectionnez <strong>Créer une archive</strong> et attendez que l’archivage soit terminé."
msgid "Choose <strong>Next</strong> at the bottom of the page."
-msgstr ""
+msgstr "Cliquez sur <strong>Suivant</strong> au bas de la page."
msgid "Choose File ..."
msgstr "Choisir le fichier…"
@@ -1304,6 +1403,12 @@ msgstr "Choisir le fichier…"
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Choisissez une branche ou une étiquette (par exemple %{master}) ou entrez un commit (par exemple %{sha}) pour voir ce qui a changé ou pour créer une demande de fusion."
+msgid "Choose a template..."
+msgstr "Choisir un modèle…"
+
+msgid "Choose a type..."
+msgstr "Choisir un type…"
+
msgid "Choose any color."
msgstr "Choisissez n’importe quelle couleur."
@@ -1326,7 +1431,7 @@ msgid "Choose which repositories you want to import."
msgstr "Choisissez les dépôts que vous voulez importer."
msgid "Choose which shards you wish to synchronize to this secondary node."
-msgstr ""
+msgstr "Choisissez les fragments que vous souhaitez synchroniser avec ce nœud secondaire."
msgid "CiStatusLabel|canceled"
msgstr "annulé"
@@ -1335,7 +1440,7 @@ msgid "CiStatusLabel|created"
msgstr "créé"
msgid "CiStatusLabel|failed"
-msgstr "échoué"
+msgstr "en échec"
msgid "CiStatusLabel|manual action"
msgstr "action manuelle"
@@ -1365,7 +1470,7 @@ msgid "CiStatusText|created"
msgstr "créé"
msgid "CiStatusText|failed"
-msgstr "échoué"
+msgstr "en échec"
msgid "CiStatusText|manual"
msgstr "manuel"
@@ -1398,7 +1503,7 @@ msgid "CiVariable|All environments"
msgstr "Tous les environnements"
msgid "CiVariable|Create wildcard"
-msgstr ""
+msgstr "Créer un joker"
msgid "CiVariable|Error occured while saving variables"
msgstr "Une erreur s’est produite pendant la sauvegarde des variables"
@@ -1410,7 +1515,7 @@ msgid "CiVariable|Protected"
msgstr "Protégée"
msgid "CiVariable|Search environments"
-msgstr ""
+msgstr "Chercher des environnements"
msgid "CiVariable|Toggle protected"
msgstr "Changer l’état de protection"
@@ -1422,7 +1527,7 @@ msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "CircuitBreaker API"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
-msgstr ""
+msgstr "est indisponible : %{reason}"
msgid "Clear search input"
msgstr "Vider le champ de recherche"
@@ -1431,7 +1536,7 @@ msgid "Click any <strong>project name</strong> in the project list below to navi
msgstr "Cliquez sur n’importe quel <strong>nom de projet</strong> dans la liste des projets ciâ€dessous pour naviguer jusqu’au jalon du projet."
msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
-msgstr ""
+msgstr "Cliquez sur le bouton <strong>Télécharger</strong> et attendez que le téléchargement soit terminé."
msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
msgstr "Cliquez sur le bouton <strong>Promouvoir</strong> en haut à droite pour le promouvoir en tant que jalon de groupe."
@@ -1458,7 +1563,7 @@ msgid "Client authentication key password"
msgstr "Mot de passe de la clef d’authentification client"
msgid "Clients"
-msgstr ""
+msgstr "Clients"
msgid "Clone repository"
msgstr "Cloner le dépôt"
@@ -1466,6 +1571,9 @@ msgstr "Cloner le dépôt"
msgid "Close"
msgstr "Fermer"
+msgid "Close epic"
+msgstr "Clore l’épopée"
+
msgid "Closed"
msgstr "Fermé(e)"
@@ -1475,6 +1583,9 @@ msgstr "Tickets clos"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} a été installé avec succès sur votre grappe de serveurs Kubernetes"
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr "%{boldNotice} Ceci ajoutera quelques ressources supplémentaires comme un équilibreur de charge, ce qui peut engager des frais supplémentaires selon l’hébergeur de votre grappe de serveurs Kubernetes. Si vous utilisez Google Kubernetes Engine, vous pouvez %{pricingLink}."
+
msgid "ClusterIntegration|API URL"
msgstr "URL de l’API"
@@ -1484,12 +1595,21 @@ msgstr "Ajouter une grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Options avancées concernant l’intégration de cette grappe de serveurs Kubernetes"
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr "Après avoir installé Ingress, vous devrez faire pointer votre entrée DNS générique (wildcard) vers l’adresse IP externe générée afin que votre application puisse s’afficher après son déploiement. %{ingressHelpLink}"
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr "Une erreur est survenue lors de la tentative de récupération des zones du projet : %{error}"
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr "Une erreur est survenue lors de la tentative de récupération de vos projets : %{error}"
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr "Une erreur est survenue lors de la tentative de récupération des types de machines de la zone : %{error}"
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr "Une erreur est survenue lors de la tentative de contact de l’API Google Cloud. Veuillez réessayer plus tard."
+
msgid "ClusterIntegration|Applications"
msgstr "Applications"
@@ -1502,11 +1622,11 @@ msgstr "Certificat d’autorité de certification"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Ensemble de certificats des autorités de certification (format PEM)"
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "Choisissez les environnements de votre projet qui utiliseront cette grappe de serveurs Kubernetes."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr "Choisissez les applications à installer sur votre grappe de serveurs Kubernetes. L’installation de n’importe quelle des applications suivantes nécessite Helm Tiller."
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr "Contrôlez l’intégration de votre grappe de serveurs Kubernetes avec GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr "Choisissez lequel de vos environnements utilisera cette grappe de serveurs."
msgid "ClusterIntegration|Copy API URL"
msgstr "Copier l’URL de l’API"
@@ -1532,6 +1652,12 @@ msgstr "Créer une grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Did you know?"
msgstr "Le saviezâ€vous ?"
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr "Activez ou désactivez la connexion de GitLab à votre grappe de serveurs Kubernetes."
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr "Activez ce paramètre si vous utilisez le contrôle d’accès basé sur les rôles (RBAC)."
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "Entrez les détails de votre grappe de serveurs Kubernetes"
@@ -1556,6 +1682,9 @@ msgstr "Intégration GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "Exécuteur GitLab"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr "GitLab Runner se connecte au dépôt de ce projet et exécute les tâches d’intégration et livraison continues (CI/CD), en renvoyant les résultats et en déployant les applications en production."
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr "Projet Google Cloud Platform"
@@ -1568,6 +1697,9 @@ msgstr "Projet Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr "Helm rationalise l’installation et la gestion des applications Kubernetes. Tiller s’exécute sur votre grappe de serveurs Kubernetes et gère les publications de vos graphiques."
+
msgid "ClusterIntegration|Hide"
msgstr "Masquer"
@@ -1583,6 +1715,9 @@ msgstr "Ingress"
msgid "ClusterIntegration|Ingress IP Address"
msgstr "Adresse IP Ingress"
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr "Ingress vous permet de router les requêtes vers des services en fonction de l’hôte ou du chemin de la requête, en centralisant un certain nombre de services vers un seul point d’entrée."
+
msgid "ClusterIntegration|Install"
msgstr "Installer"
@@ -1607,6 +1742,9 @@ msgstr "Nom de l’hôte Jupyter"
msgid "ClusterIntegration|JupyterHub"
msgstr "JupyterHub"
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr "JupyterHub est une plaque tournante multiâ€utilisateur qui génère, gère et « proxifie » plusieurs instances du serveur de blocâ€notes monoâ€utilisateur Jupyter notebook. JupyterHub peut être utilisé pour servir des blocâ€notes aux élèves d’une classe, un groupe d’informaticiens d’une entreprise ou un groupe de scientifiques chercheurs."
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Grappe de serveurs Kubernetes"
@@ -1619,15 +1757,6 @@ msgstr "État de santé de la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Intégration d’une grappe de serveurs Kubernetes"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "L’intégration de la grappe de serveurs Kubernetes est désactivée pour ce projet."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "L’intégration de la grappe de serveurs Kubernetes est activée pour ce projet."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "L’intégration d’une grappe de serveurs Kubernetes est activée pour ce projet. La désactivation de cette intégration n’affectera pas votre grappe de serveurs Kubernetes, elle ne désactivera que temporairement sa connexion à GitLab."
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "La grappe de serveurs Kubernetes est en cours de création sur Google Kubernetes Engine…"
@@ -1652,12 +1781,6 @@ msgstr "En savoir plus sur %{help_link_start}Kubernetes%{help_link_end}."
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr "En savoir plus sur %{help_link_start}les zones%{help_link_end}."
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "En savoir plus sur les environnements"
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr "En savoir plus sur la configuration de la sécurité"
-
msgid "ClusterIntegration|Machine type"
msgstr "Type de machine"
@@ -1700,6 +1823,9 @@ msgstr "Veuillez entrer les informations d’accès de votre grappe de serveurs
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Veuillez vous assurer que votre compte Google répond aux exigences suivantes :"
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr "Faites pointer une entrée DNS générique (wildcard) sur cette adresse IP générée afin de pouvoir accéder à votre application après son déploiement."
+
msgid "ClusterIntegration|Project namespace"
msgstr "Espace de noms du projet"
@@ -1709,6 +1835,12 @@ msgstr "Espace de noms du projet (facultatif, unique)"
msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr "Prometheus est un système de supervision libre avec %{gitlabIntegrationLink} permettant de surveiller les applications déployées."
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr "Grappe de serveurs avec accès RBAC (expérimental)"
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr "Lisez notre %{link_to_help_page} sur l’intégration d’une grappe de serveurs Kubernetes."
@@ -1721,6 +1853,9 @@ msgstr "Retirer l’intégration"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "Supprimer la configuration de cette grappe de serveurs Kubernetes de ce projet. Cela ne supprimera pas votre grappe de serveurs Kubernetes actuelle."
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr "Vous pouvez remplacer ceci par un nom d’hôte personnalisé. Auquel cas, faites pointer ce nom d’hôte vers l’adresse IP d’Ingress ciâ€dessus."
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "La demande de lancement de l’installation a échoué"
@@ -1736,9 +1871,6 @@ msgstr "Rechercher des projets"
msgid "ClusterIntegration|Search zones"
msgstr "Rechercher les zones"
-msgid "ClusterIntegration|Security"
-msgstr "Sécurité"
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "Voir et modifier les détails de votre grappe de serveurs Kubernetes"
@@ -1775,12 +1907,18 @@ msgstr "Une erreur s’est produite lors de la création de votre grappe de serv
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Une erreur s’est produite lors de l’installation de %{title}"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr "La configuration par défaut de la grappe de serveurs permet d’accéder à un large éventail de fonctionnalités nécessaires pour construire et déployer, avec succès, une application conteneurisée."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr "L’adresse IP est en cours d’affectation. Si cela dure trop longtemps, veuillez vérifier votre grappe de serveurs Kubernetes ou vos quotas sur Google Kubernetes Engine."
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr "La configuration par défaut de la grappe de serveurs permet d’accéder à un large éventail de fonctionnalités nécessaires pour construire et déployer une application conteneurisée."
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "Ce compte doit disposer des autorisations pour créer une grappe de serveurs Kubernetes dans le %{link_to_container_project} spécifié ciâ€dessous"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr "Cette option vous permettra d’installer des applications sur des grappes de serveurs avec contrôle d’accès basé sur le rôle (RBAC)."
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "Activer la grappe de serveurs Kubernetes"
@@ -1793,9 +1931,15 @@ msgstr "Jeton"
msgid "ClusterIntegration|Validating project billing status"
msgstr "Validation de l’état de la facturation du projet"
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr "Nous n’avons pu vérifier que la facturation de l’un de vos projets sur Google Cloud Platform est bien activée. Veuillez réessayer."
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "Avec une grappe de serveurs Kubernetes associée à ce projet, vous pouvez utiliser des applications de revue, déployer vos applications, exécuter vos pipelines, et bien plus encore."
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr "Vous devez d’abord installer Helm Tiller avant d’installer les applications ciâ€dessous"
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "Votre compte doit disposer de %{link_to_kubernetes_engine}"
@@ -1814,9 +1958,6 @@ msgstr "documentation"
msgid "ClusterIntegration|help page"
msgstr "page d’aide"
-msgid "ClusterIntegration|installing applications"
-msgstr "installation des applications"
-
msgid "ClusterIntegration|meets the requirements"
msgstr "répond aux exigences"
@@ -1826,6 +1967,9 @@ msgstr "correctement configuré"
msgid "ClusterIntegration|sign up"
msgstr "s’inscrire"
+msgid "Code owners"
+msgstr "Propriétaires du code"
+
msgid "Cohorts"
msgstr "Cohortes"
@@ -1833,7 +1977,7 @@ msgid "Collapse"
msgstr "Réduire"
msgid "Collapse sidebar"
-msgstr "Réduire la barre latérale"
+msgstr "Masquer la barre latérale"
msgid "Comment & resolve discussion"
msgstr "Commenter et marquer la discussion comme résolue"
@@ -1875,6 +2019,9 @@ msgstr "Commit"
msgid "CommitMessage|Add %{file_name}"
msgstr "Ajout de %{file_name}"
+msgid "CommitWidget|authored"
+msgstr "réalisé"
+
msgid "Commits"
msgstr "Commits"
@@ -1947,23 +2094,20 @@ msgstr "Confidentialité"
msgid "Configure Gitaly timeouts."
msgstr "Configurer les délais d’expiration de Gitaly."
-msgid "Configure Sidekiq job throttling."
-msgstr "Configurer la limitation des tâches Sidekiq."
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr "Configurer les vérifications Git automatiques et la maintenance des dépôts."
msgid "Configure limits for web and API requests."
msgstr "Configurer les limites pour les requêtes Web et d’API."
-msgid "Configure push and pull mirrors."
-msgstr "Configurez les miroirs où récupérer et pousser le code."
+msgid "Configure push mirrors."
+msgstr "Configurez les miroirs où pousser le code."
msgid "Configure storage path and circuit breaker settings."
msgstr "Configurez les paramètres du chemin de stockage et du disjoncteur."
msgid "Configure the way a user creates a new account."
-msgstr "Configurez la façon dont un·e utilisateur·rice crée un nouveau compte."
+msgstr "Configurez la manière dont une personne crée un nouveau compte."
msgid "Connect"
msgstr "Connecter"
@@ -1987,7 +2131,7 @@ msgid "ContainerRegistry|Created"
msgstr "Créé"
msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
-msgstr "Inscrivezâ€vous d’abord au registre de conteneur GitLab à l’aide de votre nom d’utilisa·teur·trice GitLab et de votre mot de passe. Si vous avez %{link_2fa} vous devrez utiliser un %{link_token} :"
+msgstr "Inscrivezâ€vous d’abord au registre de conteneur GitLab à l’aide de votre nom d’utilisateur GitLab et de votre mot de passe. Si vous avez %{link_2fa} vous devrez utiliser un %{link_token} :"
msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
msgstr "GitLab prend en charge jusqu’à trois niveaux de noms d’image. Les exemples d’images suivants sont valides pour votre projet :"
@@ -2032,7 +2176,7 @@ msgid "Continue"
msgstr "Continuer"
msgid "Continue to the next step"
-msgstr ""
+msgstr "Passer à l’étape suivante"
msgid "Continuous Integration and Deployment"
msgstr "Intégration et déploiement continus"
@@ -2046,6 +2190,9 @@ msgstr "Contribution"
msgid "Contribution guide"
msgstr "Guide de contribution"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr "Contributions du <strong>%{calendar_date}</strong>"
+
msgid "Contributions per group member"
msgstr "Contributions par membre du groupe"
@@ -2079,6 +2226,15 @@ msgstr "Contrôle la concurrence maximale des opérations de vérification pour
msgid "ConvDev Index"
msgstr "Index ConvDev"
+msgid "Copy %{protocol} clone URL"
+msgstr "Copier l’URL %{protocol} de clonage"
+
+msgid "Copy HTTPS clone URL"
+msgstr "Copier l’URL HTTPS de clonage"
+
+msgid "Copy SSH clone URL"
+msgstr "Copier l’URL SSH de clonage"
+
msgid "Copy SSH public key to clipboard"
msgstr "Copier la clef SSH publique dans le presseâ€papiers"
@@ -2146,7 +2302,7 @@ msgid "Create file"
msgstr "Créer un fichier"
msgid "Create group"
-msgstr ""
+msgstr "Créer un groupe"
msgid "Create group label"
msgstr "Créer une étiquette de groupe"
@@ -2173,7 +2329,7 @@ msgid "Create new file"
msgstr "Créer un nouveau fichier"
msgid "Create new file or directory"
-msgstr ""
+msgstr "Créer un nouveau fichier ou répertoire"
msgid "Create new label"
msgstr "Créer une nouvelle étiquette"
@@ -2184,9 +2340,6 @@ msgstr "Créer un nouveau…"
msgid "Create project label"
msgstr "Créer une étiquette de projet"
-msgid "CreateNewFork|Fork"
-msgstr "Créer une divergence"
-
msgid "CreateTag|Tag"
msgstr "Étiquette"
@@ -2197,13 +2350,16 @@ msgid "Created"
msgstr "Créé"
msgid "Created At"
-msgstr ""
+msgstr "Créé à"
msgid "Created by me"
msgstr "Créé par moi"
+msgid "Created on"
+msgstr "Créé le"
+
msgid "Created on:"
-msgstr ""
+msgstr "Créé le :"
msgid "Creating epic"
msgstr "Création de l’épopée en cours"
@@ -2214,6 +2370,9 @@ msgstr "Fuseau horaire des tâches planifiées cron"
msgid "Cron syntax"
msgstr "Syntaxe de la planification cron"
+msgid "Current Branch"
+msgstr "Branche actuelle"
+
msgid "Current node"
msgstr "NÅ“ud actuel"
@@ -2223,6 +2382,9 @@ msgstr "Profil"
msgid "CurrentUser|Settings"
msgstr "Paramètres"
+msgid "Custom"
+msgstr "Personnalisé"
+
msgid "Custom CI config path"
msgstr "Chemin d’accès de la config d’intégration continue personnalisée"
@@ -2232,18 +2394,24 @@ msgstr "Événements de notification personnalisés"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "Les niveaux de notification personnalisés sont similaires aux niveaux de participation. Cependant, ils permettent de recevoir également des notifications pour une sélection d’événements. Pour plus d’information, vous pouvez consulter %{notification_link}."
+msgid "Custom project templates"
+msgstr "Modèles de projets personnalisés"
+
msgid "Customize colors"
msgstr "Personnaliser les couleurs"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "Personnalisez la manière dont les adresses de courriel et les noms d’utilisateur provenant de FogBugz sont importés dans GitLab. À la prochaine étape, vous pourrez sélectionner les projets que vous souhaitez importer."
msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "Personnalisez la manière dont les adresses de courriel et les noms d’utilisateur provenant de Google Code sont importés dans GitLab. À la prochaine étape, vous pourrez sélectionner les projets que vous souhaitez importer."
msgid "Cycle Analytics"
msgstr "Analyse de cycle"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr "Cycle Analytics donne un aperçu du temps qu’il faut pour qu’un projet passe d’une idée à sa mise en production."
+
msgid "CycleAnalyticsStage|Code"
msgstr "Code"
@@ -2266,7 +2434,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "Test"
msgid "Dashboard"
-msgstr ""
+msgstr "Tableau de bord"
msgid "DashboardProjects|All"
msgstr "Tous"
@@ -2274,6 +2442,12 @@ msgstr "Tous"
msgid "DashboardProjects|Personal"
msgstr "Personnels"
+msgid "Date picker"
+msgstr "Sélecteur de date"
+
+msgid "Debug"
+msgstr "Déboguer"
+
msgid "Dec"
msgstr "déc."
@@ -2283,14 +2457,17 @@ msgstr "décembre"
msgid "Decline and sign out"
msgstr "Refuser et se déconnecter"
+msgid "Default Branch"
+msgstr "Branche par défaut"
+
msgid "Default classification label"
msgstr "Étiquette de classement par défaut"
msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
+msgstr "Par défaut : importer directement l’adresse de courriel ou le nom d’utilisateur provenant de Google Code"
msgid "Default: Map a FogBugz account ID to a full name"
-msgstr ""
+msgstr "Par défaut : associer un identifiant de compte FogBugz à un nom complet"
msgid "Define a custom pattern with cron syntax"
msgstr "Définir un schéma personnalisé avec une syntaxe Cron"
@@ -2298,6 +2475,9 @@ msgstr "Définir un schéma personnalisé avec une syntaxe Cron"
msgid "Delete"
msgstr "Supprimer"
+msgid "Delete Package"
+msgstr "Supprimer le paquet"
+
msgid "Delete Snippet"
msgstr "Supprimer L’extrait de code"
@@ -2305,7 +2485,7 @@ msgid "Delete list"
msgstr "Supprimer la liste"
msgid "Deleted"
-msgstr ""
+msgstr "Supprimé"
msgid "Deny"
msgstr "Refuser"
@@ -2382,7 +2562,7 @@ msgid "DeployTokens|Copy deploy token to clipboard"
msgstr "Copier le jeton de déploiement dans le presseâ€papiers"
msgid "DeployTokens|Copy username to clipboard"
-msgstr "Copier le nom d’utilisateur·rice dans le presseâ€papiers"
+msgstr "Copier le nom d’utilisateur dans le presseâ€papiers"
msgid "DeployTokens|Create deploy token"
msgstr "Créer un jeton de déploiement"
@@ -2424,10 +2604,10 @@ msgid "DeployTokens|Use this token as a password. Make sure you save it - you wo
msgstr "Utilisez ce jeton en tant que mot de passe. Assurezâ€vous de le sauvegarder, vous n’aurez pas la possibilité d’y accéder à nouveau."
msgid "DeployTokens|Use this username as a login."
-msgstr "Utiliser ce nom d’utilisateur·rice comme identifiant."
+msgstr "Utiliser ce nom d’utilisateur comme identifiant."
msgid "DeployTokens|Username"
-msgstr "Nom d’utilisateur·rice"
+msgstr "Nom d’utilisateur"
msgid "DeployTokens|You are about to revoke"
msgstr "Vous êtes sur le point de révoquer"
@@ -2451,7 +2631,7 @@ msgid "Description templates allow you to define context-specific templates for
msgstr "Les modèles de description permettent de définir des modèles spécifiques au contexte et propres à votre projet pour les champs de description des tickets et des demandes de fusion."
msgid "Description:"
-msgstr ""
+msgstr "Description :"
msgid "Destroy"
msgstr "Détruire"
@@ -2459,12 +2639,18 @@ msgstr "Détruire"
msgid "Details"
msgstr "Détails"
+msgid "Detect host keys"
+msgstr "Détecter les clefs de l’hôte"
+
msgid "Diffs|No file name available"
msgstr "Aucun nom de fichier disponible"
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr "Quelque chose s’est mal passé lors de la rapatriement des lignes du diff."
+msgid "Direction"
+msgstr "Direction"
+
msgid "Directory name"
msgstr "Nom du dossier"
@@ -2477,9 +2663,21 @@ msgstr "Désactiver pour ce projet"
msgid "Disable group Runners"
msgstr "Désactiver les exécuteurs de groupe"
+msgid "Discard"
+msgstr "Rejeter"
+
+msgid "Discard all changes"
+msgstr "Rejeter tous les changements"
+
+msgid "Discard all unstaged changes?"
+msgstr "Rejeter toutes les modifications non indexées ?"
+
msgid "Discard changes"
msgstr "Abandonner les modifications"
+msgid "Discard changes to %{path}?"
+msgstr "Rejeter les modifications de %{path} ?"
+
msgid "Discard draft"
msgstr "Abandonner le brouillon"
@@ -2487,10 +2685,10 @@ msgid "Discover GitLab Geo."
msgstr "Découvrez GitLab Geo."
msgid "Discover projects, groups and snippets. Share your projects with others"
-msgstr ""
+msgstr "Découvrez des projets, des groupes et des extraits de code. Partagez vos projets avec d’autres personnes"
msgid "Dismiss"
-msgstr ""
+msgstr "Rejeter"
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Passer l’introduction à Cycle Analytics"
@@ -2499,7 +2697,7 @@ msgid "Dismiss Merge Request promotion"
msgstr "Rejeter la promotion de la demande de fusion"
msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
+msgstr "Voulezâ€vous personnaliser la manière dont les adresses de courriel et les noms d’utilisateurs issus de Google Code sont importés dans GitLab ?"
msgid "Documentation for popular identity providers"
msgstr "Documentation des principaux fournisseurs d’identité"
@@ -2565,13 +2763,13 @@ msgid "Edit Snippet"
msgstr "Modifier le fragment de code"
msgid "Edit application"
-msgstr ""
+msgstr "Modifier l’application"
msgid "Edit files in the editor and commit changes here"
msgstr "Modifier les fichiers dans l’éditeur et valider les modifications ici"
msgid "Edit group: %{group_name}"
-msgstr ""
+msgstr "Modifier le groupe : %{group_name}"
msgid "Edit identity for %{user_name}"
msgstr "Modifier l’identité de %{user_name}"
@@ -2579,7 +2777,7 @@ msgstr "Modifier l’identité de %{user_name}"
msgid "Elasticsearch"
msgstr "Elasticsearch"
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr "Intégration d’Elasticsearch. AWS Elasticsearch IAM."
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr "Activer reCAPTCHA ou Akismet et définir des limites d’adresse IP."
msgid "Enable the Performance Bar for a given group."
msgstr "Activer la barre de performance pour un groupe donné."
+msgid "Enable usage ping"
+msgstr "Activer la collecte des données d’utilisation"
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr "Activez la collecte des données d’utilisation afin d’avoir une vue d’ensemble de la manière dont vous utilisez les fonctionnalités de GitLab."
+
msgid "Enabled"
msgstr "activé"
msgid "Ends at (UTC)"
msgstr "Se termine à (UTC)"
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr "Entrez l’URL de votre serveur Bitbucket et votre jeton d’accès personnel ciâ€dessous"
+
+msgid "Enter the issue description"
+msgstr "Entrez la description du problème"
+
+msgid "Enter the issue title"
+msgstr "Entrez l’intitulé du ticket"
+
+msgid "Enter the merge request description"
+msgstr "Entrez la description de la demande de fusion"
+
+msgid "Enter the merge request title"
+msgstr "Entrez l’intitulé de la demande de fusion"
+
msgid "Environments"
msgstr "Environnements"
@@ -2655,10 +2874,10 @@ msgid "Environments|An error occurred while making the request."
msgstr "Une erreur s’est produite lors de la requête."
msgid "Environments|An error occurred while stopping the environment, please try again"
-msgstr ""
+msgstr "Une erreur s’est produite lors de l’arrêt de l’environnement. Veuillez réessayer"
msgid "Environments|Are you sure you want to stop this environment?"
-msgstr ""
+msgstr "Êtesâ€vous sûr(e) de vouloir arrêter cet environnement ?"
msgid "Environments|Commit"
msgstr "Commit"
@@ -2675,11 +2894,14 @@ msgstr "Environnement"
msgid "Environments|Environments"
msgstr "Environnements"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr "Les environnements sont des endroits où le code est déployé, tel que l’étape ou la production."
+
msgid "Environments|Job"
msgstr "Tâche"
msgid "Environments|Learn more about stopping environments"
-msgstr ""
+msgstr "En savoir plus sur l’arrêt des environnements"
msgid "Environments|New environment"
msgstr "Nouvel environnement"
@@ -2690,23 +2912,26 @@ msgstr "Aucun déploiement pour le moment"
msgid "Environments|No pod name has been specified"
msgstr "Aucun nom de « pod » n’a été spécifié"
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr "Notez que cette action arrêtera l’environnement, mais n’aura %{emphasisStart}aucun%{emphasisEnd} effet sur les déploiements existants en raison de l’absence de directive « arrêter l’action de l’environnement » dans le fichier de configuration %{ciConfigLinkEnd}.gitlab-ci.yml%{ciConfigLinkStart}."
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
-msgstr ""
+msgstr "Notez que cette action arrêtera l’environnement, mais n’aura %{emphasis_start}aucun%{emphasis_end} effet sur les déploiements existants en raison de l’absence de directive « arrêter l’action de l’environnement » dans le fichier de configuration %{ci_config_link_end}.gitlab-ci.yml%{ci_config_link_start}."
msgid "Environments|Open live environment"
-msgstr ""
+msgstr "Ouvrir l’environnement en cours"
msgid "Environments|Pod logs from"
msgstr "Journaux du pod depuis"
msgid "Environments|Re-deploy to environment"
-msgstr ""
+msgstr "Redéployer dans l’environnement"
msgid "Environments|Read more about environments"
msgstr "En savoir plus sur les environnements"
msgid "Environments|Rollback environment"
-msgstr ""
+msgstr "Restaurer l’environnement"
msgid "Environments|Show all"
msgstr "Tout afficher"
@@ -2723,6 +2948,9 @@ msgstr "Mis à jour"
msgid "Environments|You don't have any environments right now."
msgstr "Vous n’avez aucun environnement pour le moment."
+msgid "Environments|protected"
+msgstr "protégé"
+
msgid "Epic"
msgstr "Épopée"
@@ -2738,6 +2966,30 @@ msgstr "Feuille de route des épopées"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr "Les épopées vous permettent de gérer votre portefeuille de projets plus efficacement et avec moins d’efforts"
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr "Comment puisâ€je résoudre ceci ?"
+
+msgid "Epics|More information"
+msgstr "En savoir plus"
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr "Ces dates affectent la manière dont vos épopées apparaissent sur la feuille de route. Les dates des jalons proviennent des jalons attribués aux tickets de l’épopée. Vous pouvez également définir des dates fixes ou les supprimer complètement."
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr "Échéance"
+
+msgid "Epics|start"
+msgstr "commence"
+
+msgid "Error"
+msgstr "Erreur"
+
msgid "Error Reporting and Logging"
msgstr "Rapport d’erreur et journalisation"
@@ -2762,6 +3014,9 @@ msgstr "Erreur lors de la récupération des données d’utilisation."
msgid "Error loading branch data. Please try again."
msgstr "Erreur lors du chargement des données de branche. Veuillez réessayer."
+msgid "Error loading branches."
+msgstr "Erreur lors du chargement des branches."
+
msgid "Error loading last commit."
msgstr "Erreur lors du chargement du dernier commit."
@@ -2774,6 +3029,12 @@ msgstr "Erreur lors du chargement des demandes de fusion."
msgid "Error loading project data. Please try again."
msgstr "Erreur lors du chargement des données du projet. Veuillez réessayer."
+msgid "Error loading template types."
+msgstr "Erreur lors du chargement des types de modèles."
+
+msgid "Error loading template."
+msgstr "Erreur lors du chargement du modèle."
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Une erreur s’est produite lors de l’activation ou de la désactivation de l’abonnement aux notifications"
@@ -2786,6 +3047,9 @@ msgstr "Erreur lors de la mise à jour de l’état de la liste de tâches à fa
msgid "Error updating todo status."
msgstr "Erreur lors de la mise à jour du statut de tâche à faire."
+msgid "Error while loading the merge request. Please try again."
+msgstr "Erreur lors du chargement de la demande de fusion. Veuillez réessayer."
+
msgid "Estimated"
msgstr "Estimé"
@@ -2828,14 +3092,17 @@ msgstr "Tout étendre"
msgid "Expand sidebar"
msgstr "Étendre la barre latérale"
+msgid "Expiration date"
+msgstr "Date d’expiration"
+
msgid "Explore"
-msgstr ""
+msgstr "Explorer"
msgid "Explore GitLab"
msgstr "Explorer GitLab"
msgid "Explore Groups"
-msgstr ""
+msgstr "Explorer les groupes"
msgid "Explore groups"
msgstr "Explorer les groupes"
@@ -2859,16 +3126,16 @@ msgid "External authorization request timeout"
msgstr "Expiration du délai d’attente de la demande d’autorisation externe"
msgid "ExternalAuthorizationService|Classification Label"
-msgstr ""
+msgstr "Étiquette de classification"
msgid "ExternalAuthorizationService|Classification label"
-msgstr ""
+msgstr "Étiquette de classification"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
-msgstr ""
+msgstr "Lorsqu’aucune étiquette de classification n’est définie, l’étiquette par défaut « %{default_label} » sera utilisée."
msgid "Facebook"
-msgstr ""
+msgstr "Facebook"
msgid "Failed"
msgstr "Échec"
@@ -2885,6 +3152,9 @@ msgstr "Échec de la vérification des branches liées."
msgid "Failed to remove issue from board, please try again."
msgstr "Impossible de supprimer le ticket du tableau, veuillez réessayer."
+msgid "Failed to remove mirror."
+msgstr "Impossible de supprimer le miroir."
+
msgid "Failed to remove the pipeline schedule"
msgstr "Échec de la suppression du pipeline programmé"
@@ -2906,6 +3176,9 @@ msgstr "février"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "Les champs de cette page sont désormais non modifiables, vous pouvez configurer"
+msgid "File templates"
+msgstr "Modèles de fichiers"
+
msgid "Files"
msgstr "Fichiers"
@@ -2916,11 +3189,20 @@ msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and p
msgstr "Renseignez les champs ciâ€dessous, activez <strong>%{enable_label}</strong> et appuyez sur <strong>%{save_changes}</strong>"
msgid "Filter"
+msgstr "Filtrer"
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
msgstr ""
msgid "Filter by commit message"
msgstr "Filtrer par message de commit"
+msgid "Filter..."
+msgstr "Filtrer…"
+
msgid "Find by path"
msgstr "Rechercher par chemin d’accès"
@@ -2928,10 +3210,13 @@ msgid "Find file"
msgstr "Rechercher un fichier"
msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
+msgstr "Cherchez le fichier ZIP téléchargé et décompressezâ€le."
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
-msgstr ""
+msgstr "Cherchez le fichier <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> fraîchement extrait."
+
+msgid "Fingerprints"
+msgstr "Empreintes"
msgid "Finished"
msgstr "Terminé"
@@ -2942,6 +3227,18 @@ msgstr "En premier"
msgid "FirstPushedBy|pushed by"
msgstr "poussé par"
+msgid "Fixed date"
+msgstr "Date fixée"
+
+msgid "Fixed due date"
+msgstr "Date d’échéance fixée"
+
+msgid "Fixed start date"
+msgstr "Date de début fixe"
+
+msgid "Fixed:"
+msgstr "Fixée :"
+
msgid "FogBugz Email"
msgstr "Courriel de FogBugz"
@@ -2958,7 +3255,7 @@ msgid "FogBugz import"
msgstr "Importation de FogBugz"
msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
+msgstr "Veuillez suivre les étapes ciâ€dessous pour exporter les données de votre projet Google Code."
msgid "Font Color"
msgstr "Couleur de la police"
@@ -2969,17 +3266,18 @@ msgstr "Message de pied de page"
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr "Pour les projets internes, tout utilisateur connecté peut afficher les pipelines et accéder aux détails des tâches (journaux de sortie et artefacts)"
+msgid "For more information, go to the "
+msgstr "Pour plus d’informations, consultez "
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr "Pour plus d’informations, consultez la documentation sur la %{deactivating_usage_ping_link_start}désactivation de la collecte des données d’utilisation%{deactivating_usage_ping_link_end}."
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr "Pour les projets privés, tout membre (invité ou supérieur) peut afficher les pipelines et accéder aux détails des tâches (journaux de sortie et artefacts)"
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr "Pour les projets publics, tout le monde peut afficher des pipelines et accéder aux détails des tâches (journaux de sortie et artefacts)"
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Divergence"
-msgstr[1] "Divergences"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "Divergence issue de"
@@ -2999,16 +3297,19 @@ msgid "From %{provider_title}"
msgstr "De %{provider_title}"
msgid "From Bitbucket"
-msgstr ""
+msgstr "Depuis Bitbucket"
+
+msgid "From Bitbucket Server"
+msgstr "Depuis le serveur Bitbucket"
msgid "From FogBugz"
-msgstr ""
+msgstr "Depuis FogBugz"
msgid "From GitLab.com"
-msgstr ""
+msgstr "Depuis GitLab.com"
msgid "From Google Code"
-msgstr ""
+msgstr "Depuis Google Code"
msgid "From issue creation until deploy to production"
msgstr "Depuis la création du ticket jusqu’au déploiement en production"
@@ -3016,6 +3317,9 @@ msgstr "Depuis la création du ticket jusqu’au déploiement en production"
msgid "From merge request merge until deploy to production"
msgstr "Depuis la fusion de la demande de fusion jusqu’au déploiement en production"
+msgid "From milestones:"
+msgstr "À partir des jalons :"
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "À partir de l’affichage des détails de la grappe de serveurs Kubernetes, installez un exécuteur à partir de la liste des applications"
@@ -3031,6 +3335,9 @@ msgstr "Pipelines généraux"
msgid "Generate a default set of labels"
msgstr "Générer un jeu d’étiquettes par défaut"
+msgid "Geo"
+msgstr "Geo"
+
msgid "Geo Nodes"
msgstr "NÅ“uds Geo"
@@ -3053,16 +3360,16 @@ msgid "GeoNodes|Data replication lag"
msgstr "Latence de la réplication des données"
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
-msgstr ""
+msgstr "La désactivation d’un nÅ“ud arrête le processus de synchronisation. Êtesâ€vous sûr(e) ?"
msgid "GeoNodes|Does not match the primary storage configuration"
-msgstr ""
+msgstr "Ne correspond pas à la configuration du stockage principal"
msgid "GeoNodes|Failed"
msgstr "Échec"
msgid "GeoNodes|Full"
-msgstr ""
+msgstr "Complet"
msgid "GeoNodes|GitLab version"
msgstr "Version de GitLab"
@@ -3074,43 +3381,43 @@ msgid "GeoNodes|Health status"
msgstr "État de santé"
msgid "GeoNodes|Last event ID processed by cursor"
-msgstr ""
+msgstr "Dernier identifiant d’événement traité par le curseur"
msgid "GeoNodes|Last event ID seen from primary"
-msgstr ""
+msgstr "Dernier identifiant d’événement vu par le nœud primaire"
msgid "GeoNodes|Learn more about Repository checksum progress"
-msgstr ""
+msgstr "En savoir plus sur la progression du calcul de la somme de contrôle du dépôt"
msgid "GeoNodes|Learn more about Repository verification"
-msgstr ""
+msgstr "En savoir plus à propos de la vérification du dépôt"
msgid "GeoNodes|Learn more about Wiki checksum progress"
-msgstr ""
+msgstr "En savoir plus sur la progression du calcul de la somme de contrôle du Wiki"
msgid "GeoNodes|Learn more about Wiki verification"
-msgstr ""
+msgstr "En savoir plus à propos de la vérification du Wiki"
msgid "GeoNodes|Loading nodes"
-msgstr ""
+msgstr "Chargement des nœuds"
msgid "GeoNodes|Local LFS objects"
-msgstr ""
+msgstr "Objets LFS locaux"
msgid "GeoNodes|Local attachments"
-msgstr ""
+msgstr "Pièces jointes locales"
msgid "GeoNodes|Local job artifacts"
-msgstr ""
+msgstr "Artefacts de la tâche locale"
msgid "GeoNodes|New node"
msgstr "Nouveau nœud"
msgid "GeoNodes|Node Authentication was successfully repaired."
-msgstr ""
+msgstr "Le nœud d’authentification a été réparé avec succès."
msgid "GeoNodes|Node was successfully removed."
-msgstr ""
+msgstr "Le nœud a été supprimé avec succès."
msgid "GeoNodes|Not checksummed"
msgstr "Non vérifié par somme de contrôle"
@@ -3131,31 +3438,31 @@ msgid "GeoNodes|Repositories"
msgstr "Dépôts"
msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
-msgstr ""
+msgstr "Sommes de contrôle des dépôts vérifiées avec leurs homologues sur les nœuds secondaires"
msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
-msgstr ""
+msgstr "Dépôts vérifiés avec leurs homologues sur le nœud principal"
msgid "GeoNodes|Repository checksum progress"
-msgstr ""
+msgstr "Progression du calcul de la somme de contrôle du dépôt"
msgid "GeoNodes|Repository verification progress"
-msgstr ""
+msgstr "Progression de la vérification du dépôt"
msgid "GeoNodes|Selective"
msgstr "Sélectif"
msgid "GeoNodes|Something went wrong while changing node status"
-msgstr ""
+msgstr "Une erreur s’est produite lors du changement de statut du nœud"
msgid "GeoNodes|Something went wrong while fetching nodes"
-msgstr ""
+msgstr "Une erreur s’est produite lors de la récupération des nœuds"
msgid "GeoNodes|Something went wrong while removing node"
-msgstr ""
+msgstr "Une erreur s’est produite lors de la suppression du nœud"
msgid "GeoNodes|Something went wrong while repairing node"
-msgstr ""
+msgstr "Une erreur s’est produite lors de la réparation du nœud"
msgid "GeoNodes|Storage config"
msgstr "Configuration du stockage"
@@ -3179,52 +3486,151 @@ msgid "GeoNodes|Verified"
msgstr "Vérifié"
msgid "GeoNodes|Wiki checksum progress"
-msgstr ""
+msgstr "Progression du calcul de la somme de contrôle du wiki"
msgid "GeoNodes|Wiki verification progress"
-msgstr ""
+msgstr "Progression de la vérification du wiki"
msgid "GeoNodes|Wikis"
msgstr "Wikis"
msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
-msgstr ""
+msgstr "Sommes de contrôle des wikis vérifiées avec leurs homologues sur les nœuds secondaires"
msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
-msgstr ""
+msgstr "Wikis vérifiés avec leurs homologues sur le nœud principal"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
-msgstr ""
+msgstr "Vous avez configuré des nœuds Geo en utilisant une connexion HTTP non sécurisée. Nous recommandons l’utilisation de HTTPS."
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr "%{name} est programmé pour le reâ€téléchargement forcé"
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr "%{name} est programmé pour la revérification"
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr "%{name} est programmé pour la reâ€synchronisation"
msgid "Geo|All projects"
msgstr "Tous les projets"
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr "Impossible de supprimer l’entrée de suivi d’un projet existant."
+
+msgid "Geo|Error message"
+msgstr "Indication d’erreur"
+
+msgid "Geo|Failed"
+msgstr "En échec"
+
msgid "Geo|File sync capacity"
msgstr "Capacité de synchronisation de fichiers"
msgid "Geo|Groups to synchronize"
msgstr "Groupes à synchroniser"
+msgid "Geo|In sync"
+msgstr "Synchronisé"
+
+msgid "Geo|Last successful sync"
+msgstr "Dernière synchro réussie"
+
+msgid "Geo|Last sync attempt"
+msgstr "Dernière tentative de synchro"
+
+msgid "Geo|Last time verified"
+msgstr "Dernière vérification"
+
+msgid "Geo|Never"
+msgstr "Jamais"
+
+msgid "Geo|Next sync scheduled at"
+msgstr "Prochaine synchro programmée à"
+
+msgid "Geo|No errors"
+msgstr "Aucune erreur"
+
+msgid "Geo|Pending"
+msgstr "En attente"
+
+msgid "Geo|Pending synchronization"
+msgstr "En attente de synchronisation"
+
+msgid "Geo|Pending verification"
+msgstr "En attente de vérification"
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr "Le projet (identifiant : %{project_id}) n’existe plus sur le primaire. Vous pouvez supprimer cette entrée en toute sécurité, car cela ne supprimera aucune donnée sur le disque."
+
msgid "Geo|Projects in certain groups"
-msgstr ""
+msgstr "Projets de certains groupes"
msgid "Geo|Projects in certain storage shards"
-msgstr ""
+msgstr "Projets dans certains fragments de stockage"
+
+msgid "Geo|Recheck"
+msgstr "Revérifier"
+
+msgid "Geo|Redownload"
+msgstr "Reâ€télécharger"
+
+msgid "Geo|Remove"
+msgstr "Supprimer"
msgid "Geo|Repository sync capacity"
-msgstr ""
+msgstr "Capacité de synchronisation du dépôt"
+
+msgid "Geo|Resync"
+msgstr "Reâ€synchroniser"
+
+msgid "Geo|Retry count"
+msgstr "Nombre de tentatives"
+
+msgid "Geo|Retry counts"
+msgstr "Nombre de tentatives"
msgid "Geo|Select groups to replicate."
-msgstr ""
+msgstr "Sélectionner les groupes à répliquer."
msgid "Geo|Shards to synchronize"
-msgstr ""
+msgstr "Fragments à synchroniser"
+
+msgid "Geo|Status"
+msgstr "Statut"
+
+msgid "Geo|Synced"
+msgstr "Synchronisé"
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr "Synchro en échec — %{error}"
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr "L’entrée de suivi du projet (%{project_id}) a été supprimée avec succès."
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr "L’entrée de suivi va être supprimée. Êtesâ€vous sur(e) ?"
+
+msgid "Geo|Unknown state"
+msgstr "État inconnu"
msgid "Geo|Verification capacity"
-msgstr ""
+msgstr "Capacité de vérification"
+
+msgid "Geo|Verification failed - %{error}"
+msgstr "Vérification en échec — %{error}"
+
+msgid "Geo|Waiting for scheduler"
+msgstr "En attente de planification"
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr "Il faut une licence différente pour utiliser la réplication géographique"
+
+msgid "Get a free instance review"
+msgstr "Obtenez une revue d’instance gratuite"
msgid "Git"
-msgstr ""
+msgstr "Git"
msgid "Git repository URL"
msgstr "URL du dépôt Git"
@@ -3254,22 +3660,25 @@ msgid "GitLab Group Runners can execute code for all the projects in this group.
msgstr "Les exécuteurs de groupe peuvent exécuter du code pour tous les projets de ce groupe."
msgid "GitLab Import"
-msgstr ""
+msgstr "Importation depuis GitLab"
msgid "GitLab User"
-msgstr ""
+msgstr "Utilisateur GitLab"
msgid "GitLab project export"
-msgstr ""
+msgstr "Exportation de projet GitLab"
msgid "GitLab single sign on URL"
msgstr "URL d’authentification unique GitLab"
msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
-msgstr ""
+msgstr "GitLab va exécuter une tâche en arrièreâ€plan qui permettra de produire des fichiers CSV contenant des pseudonymes de la base de données GitLab qui seront téléversés dans le répertoire de stockage d’objets que vous avez configuré."
msgid "GitLab.com import"
-msgstr ""
+msgstr "Importation depuis GitLab.com"
+
+msgid "GitLab’s issue tracker"
+msgstr "Suivi de tickets de GitLab"
msgid "Gitaly"
msgstr "Gitaly"
@@ -3284,7 +3693,7 @@ msgid "Gitea Host URL"
msgstr "URL de l’hôte Gitea"
msgid "Gitea Import"
-msgstr ""
+msgstr "Importation depuis Gitea"
msgid "Go Back"
msgstr "Retour"
@@ -3292,20 +3701,17 @@ msgstr "Retour"
msgid "Go back"
msgstr "Retour"
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
-msgid "Go to your fork"
-msgstr "Aller à votre dépôt divergent"
+msgid "Go to"
+msgstr "Aller vers"
-msgid "GoToYourFork|Fork"
-msgstr "Dépôt divergent"
+msgid "Go to %{link_to_google_takeout}."
+msgstr "Consultez le site de %{link_to_google_takeout}."
msgid "Google Code import"
-msgstr ""
+msgstr "Importation depuis Google Code"
msgid "Google Takeout"
-msgstr ""
+msgstr "Google Takeout"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "L’authentification Google n’est pas %{link_to_documentation}. Demandez à votre administrat·eur·rice GitLab si vous souhaitez utiliser ce service."
@@ -3317,13 +3723,13 @@ msgid "Graph"
msgstr "Graphique"
msgid "Group"
-msgstr ""
+msgstr "Groupe"
msgid "Group CI/CD settings"
msgstr "Paramètres du groupe CI/CD"
msgid "Group Git LFS status:"
-msgstr ""
+msgstr "Statut du stockage LFS Git du groupe :"
msgid "Group ID"
msgstr "Identifiant du groupe"
@@ -3335,52 +3741,61 @@ msgid "Group avatar"
msgstr "Avatar de groupe"
msgid "Group details"
-msgstr ""
+msgstr "Détails du groupe"
msgid "Group info:"
-msgstr ""
+msgstr "Informations du groupe :"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "Les responsables de groupe peuvent créer des exécuteurs de groupe via %{link}"
msgid "Group: %{group_name}"
-msgstr ""
+msgstr "Groupe : %{group_name}"
msgid "GroupRoadmap|From %{dateWord}"
-msgstr ""
+msgstr "Depuis %{dateWord}"
msgid "GroupRoadmap|Loading roadmap"
-msgstr ""
+msgstr "Chargement de la feuille de route en cours"
msgid "GroupRoadmap|Something went wrong while fetching epics"
-msgstr ""
+msgstr "Une erreur s’est produite lors de la récupération des épopées"
msgid "GroupRoadmap|Sorry, no epics matched your search"
-msgstr ""
+msgstr "Désolé, aucune épopée ne correspond à votre recherche"
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
-msgstr ""
+msgstr "La feuille de route affiche la progression de vos épopées dans le temps"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Afin d’afficher la feuille de route, ajoutez une date de début ou d’échéance à l’une de vos épopées dans ce groupe ou ses sousâ€groupes. Dans la vue multiâ€mensuelle, seules les épopées du mois dernier, du mois courant et des cinq prochains mois sont affichées, du %{startDate} au %{endDate}."
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Pour afficher la feuille de route, ajoutez une date de début ou d’échéance à l’une de vos épopées dans ce groupe ou ses sousâ€groupes. Dans la vue multiâ€trimestrielle, seules les épopées du trimestre dernier, du trimestre courant et des quatre prochains trimestres sont affichées, du %{startDate} au %{endDate}."
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Pour afficher la feuille de route, ajoutez une date de début ou d’échéance à l’une de vos épopées dans ce groupe ou ses sousâ€groupes. Dans la vue multiâ€hebdomadaire, seules les épopées de la semaine dernière, de la semaine courante et des quatre prochaines semaines sont affichées, du %{startDate} au %{endDate}."
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Afin d’élargir votre recherche, modifiez ou supprimez des filtres. Dans la vue multiâ€mensuelle, seules les épopées du mois dernier, du mois courant et des cinq prochains mois sont affichées, du %{startDate} au %{endDate}."
msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Afin d’élargir votre recherche, modifiez ou supprimez des filtres. Dans la vue multiâ€trimestrielle, seules les épopées du trimestre dernier, du trimestre courant et des quatre prochains trimestres sont affichées, du %{startDate} au %{endDate}."
msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Afin d’élargir votre recherche, modifiez ou supprimez des filtres. Dans la vue multiâ€hebdomadaire, seules les épopées de la semaine dernière, de la semaine courante et des quatre prochaines semaines sont affichées, du %{startDate} au %{endDate}."
msgid "GroupRoadmap|Until %{dateWord}"
-msgstr ""
+msgstr "Jusqu’au %{dateWord}"
+
+msgid "GroupSettings|Badges"
+msgstr "Badges numériques"
+
+msgid "GroupSettings|Customize your group badges."
+msgstr "Personnalisez vos badges numériques de groupe."
+
+msgid "GroupSettings|Learn more about badges."
+msgstr "En savoir plus sur les badges numériques."
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Empêcher le partage d’un projet du groupe %{group} avec d’autres groupes"
@@ -3407,31 +3822,31 @@ msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name
msgstr "supprimer le partage avec verrou de groupe pour %{ancestor_group_name}"
msgid "Groups"
-msgstr ""
+msgstr "Groupes"
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr "Les groupes peuvent également être imbriqués en créant des %{subgroup_docs_link_start}sousâ€groupes%{subgroup_docs_link_end}."
msgid "GroupsDropdown|Frequently visited"
-msgstr ""
+msgstr "Fréquemment consultés"
msgid "GroupsDropdown|Groups you visit often will appear here"
-msgstr ""
+msgstr "Les projets que vous consultez souvent apparaîtront ici"
msgid "GroupsDropdown|Loading groups"
-msgstr ""
+msgstr "Chargement des groupes"
msgid "GroupsDropdown|Search your groups"
-msgstr ""
+msgstr "Chercher dans vos groupes"
msgid "GroupsDropdown|Something went wrong on our end."
-msgstr ""
+msgstr "Un problème est survenu de notre côté."
msgid "GroupsDropdown|Sorry, no groups matched your search"
-msgstr ""
+msgstr "Désolé, aucun groupe ne correspond à vos critères de recherche"
msgid "GroupsDropdown|This feature requires browser localStorage support"
-msgstr ""
+msgstr "Cette fonctionnalité nécessite la prise en charge du stockage local (localStorage) par votre navigateur"
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr "Un groupe est une collection de plusieurs projets."
@@ -3445,6 +3860,9 @@ msgstr "Aucun groupe trouvé"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "Vous pouvez gérer les autorisations des membres de votre groupe et accéder à chacun de ses projets."
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr "Êtes-vous sûr·e de vouloir quitter le groupe « %{fullName} » ?"
+
msgid "GroupsTree|Create a project in this group."
msgstr "Créez un projet dans ce groupe."
@@ -3457,23 +3875,23 @@ msgstr "Modifier le groupe"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr "Impossible de quitter le groupe. Veuillez vous assurer que vous n’êtes pas seul·e propriétaire."
-msgid "GroupsTree|Filter by name..."
-msgstr "Filtrer par nom…"
-
msgid "GroupsTree|Leave this group"
msgstr "Quitter ce groupe"
msgid "GroupsTree|Loading groups"
msgstr "Chargement des groupes"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "Désolé, aucun groupe ne correspond à vos critères de recherche"
+msgid "GroupsTree|No groups matched your search"
+msgstr "Aucun groupe ne correspond à votre recherche"
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr "Aucun groupe ni projet ne correspond à votre recherche"
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "Désolé, aucun groupe ni projet ne correspond à vos critères de recherche"
+msgid "GroupsTree|Search by name"
+msgstr "Rechercher par nom"
msgid "Have your users email"
-msgstr ""
+msgstr "Récupérer les adresses de courriel des utilisateurs"
msgid "Header message"
msgstr "Message d’enâ€tête"
@@ -3505,6 +3923,15 @@ msgstr "Page d’aide"
msgid "Help page text and support page url."
msgstr "Texte de la page d’aide et URL de la page de support."
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr "Voici la clef SSH publique qui doit être ajoutée sur le serveur distant. Pour plus d’informations, veuillez vous référer à la documentation."
+
+msgid "Hide host keys manual input"
+msgstr "Masquer la saisie manuelle des clefs d’hôtes"
+
+msgid "Hide payload"
+msgstr "Masquer la charge utile"
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "Masquer la valeur"
@@ -3528,21 +3955,45 @@ msgstr "Conditions générales d’utilisation et politique de confidentialité"
msgid "ID"
msgstr "Identifiant"
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr "Autoriser les aperçus en direct des projets JavaScript dans l’EDI Web à l’aide de l’évaluation côté client CodeSandbox."
+
+msgid "IDE|Back"
+msgstr "Retour"
+
+msgid "IDE|Client side evaluation"
+msgstr "Évaluation côté client"
+
msgid "IDE|Commit"
msgstr "Valider"
msgid "IDE|Edit"
msgstr "Modifier"
-msgid "IDE|Go back"
-msgstr "Revenir en arrière"
+msgid "IDE|Get started with Live Preview"
+msgstr "Commencer avec l’aperçu en direct"
+
+msgid "IDE|Go to project"
+msgstr "Aller au projet"
+
+msgid "IDE|Live Preview"
+msgstr "Aperçu en direct"
msgid "IDE|Open in file view"
msgstr "Ouvrir dans le visionneur de fichiers"
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr "Prévisualisez votre application Web avec l’évaluation côté client de l’EDI Web."
+
+msgid "IDE|Refresh preview"
+msgstr "Rafraîchir l’aperçu"
+
msgid "IDE|Review"
msgstr "Examiner"
+msgid "IP Address"
+msgstr "Adresse IP"
+
msgid "Identifier"
msgstr "Identifiant"
@@ -3552,8 +4003,11 @@ msgstr "Identités"
msgid "Identity provider single sign on URL"
msgstr "URL d’authentification unique du fournisseur d’identité"
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr "Si cette option est désactivée, une branche locale divergente ne sera pas automatiquement mise à jour avec les commits de son homologue distant, afin d’éviter toute perte de données locales. Si la branche par défaut (%{default_branch}) a divergé et ne peut pas être mise à jour, la mise en miroir échouera. Les autres branches divergentes sont ignorées silencieusement."
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
-msgstr "Si désactivé, le niveau d’accès dépendra des autorisations de l’utilisa·teur·trice dans le projet."
+msgstr "Si désactivé, le niveau d’accès dépendra des autorisations de l’utilisateur ou l’utilisatrice pour ce projet."
msgid "If enabled"
msgstr "Si activé"
@@ -3586,37 +4040,43 @@ msgid "Import Projects from Gitea"
msgstr "Importe des projets depuis Gitea"
msgid "Import all compatible projects"
-msgstr ""
+msgstr "Importer tous les projets compatibles"
msgid "Import all projects"
-msgstr ""
+msgstr "Importer tous les projets"
msgid "Import all repositories"
msgstr "Importer tous les dépôts"
msgid "Import an exported GitLab project"
-msgstr ""
+msgstr "Importer un projet GitLab exporté"
msgid "Import in progress"
msgstr "Importation en cours"
msgid "Import multiple repositories by uploading a manifest file."
-msgstr ""
+msgstr "Importez plusieurs dépôts en téléversant un fichier manifeste."
msgid "Import project"
-msgstr ""
+msgstr "Importer un projet"
msgid "Import projects from Bitbucket"
-msgstr ""
+msgstr "Importer des projets depuis Bitbucket"
+
+msgid "Import projects from Bitbucket Server"
+msgstr "Importer des projets depuis Bitbucket Server"
msgid "Import projects from FogBugz"
-msgstr ""
+msgstr "Importer des projets depuis FogBugz"
msgid "Import projects from GitLab.com"
-msgstr ""
+msgstr "Importer des projets depuis GitLab.com"
msgid "Import projects from Google Code"
-msgstr ""
+msgstr "Importer des projets depuis Google Code"
+
+msgid "Import repositories from Bitbucket Server"
+msgstr "Importer des dépôts à partir de Bitbucket Server"
msgid "Import repositories from GitHub"
msgstr "Importer des dépôts à partir de GitHub"
@@ -3625,31 +4085,46 @@ msgid "Import repository"
msgstr "Importer un dépôt"
msgid "ImportButtons|Connect repositories from"
-msgstr ""
+msgstr "Connecter des dépôts provenant de"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr ""
+msgstr "Améliorez le tableau des tickets avec Gitlab Entreprise Edition."
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Améliorez la gestion des tickets grâce à la pondération disponible dans la version Entreprise Edition de GitLab."
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Améliorez vos recherches avec la recherche globale avancée de GitLab Enterprise Edition."
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr "Afin d’activer les statistiques au niveau de l’instance, veuillez demander à un administrateur d’activer la %{usage_ping_link_start}collecte des données d’utilisation%{usage_ping_link_end}."
msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "À la prochaine étape, vous pourrez sélectionner les projets que vous souhaitez importer."
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr "Inclure un accord sur les conditions générales d’utilisation et la politique de confidentialité que tous les utilisateurs doivent accepter."
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr "Si besoin, ajouter le nom d’utilisateur dans l’URL : <code>https: //username@gitlab.company.com/group/project.git</code>."
+
msgid "Incompatible Project"
-msgstr ""
+msgstr "Projet incompatible"
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr "Indique si l’exécuteur peut choisir des tâches sans étiquettes (tags)"
msgid "Inline"
msgstr "En ligne"
+msgid "Input host keys manually"
+msgstr "Entrer les clefs d’hôte manuellement"
+
+msgid "Input your repository URL"
+msgstr "Entrez l’URL de votre dépôt"
+
msgid "Install GitLab Runner"
-msgstr ""
+msgstr "Installer GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "Installez un exécuteur sur Kubernetes"
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] "Instance"
msgstr[1] "Instances"
+msgid "Instance Statistics"
+msgstr "Statistiques de l’instance"
+
+msgid "Instance Statistics visibility"
+msgstr "Visibilité des statistiques de l’instance"
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr "L’instance ne prend pas en charge plusieurs grappes de serveurs Kubernetes"
@@ -3672,22 +4153,31 @@ msgid "Interested parties can even contribute by pushing commits if they want to
msgstr "Les personnes intéressées peuvent même contribuer en poussant des commits si elles le souhaitent."
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
-msgstr "Interne — le groupe ainsi que tous les projets internes sont accessibles à n’importe quel·le utilisa·teur·trice connecté·e."
+msgstr "Interne — le groupe ainsi que tous les projets internes sont accessibles à tout utilisateur connecté."
msgid "Internal - The project can be accessed by any logged in user."
msgstr "Interne — le projet est accessible à n’importe quel·le utilisa·teur·trice connecté·e."
+msgid "Internal users"
+msgstr "Utilisateurs internes"
+
msgid "Interval Pattern"
msgstr "Modèle d’intervalle"
msgid "Introducing Cycle Analytics"
msgstr "Introduction à l’analyseur de cycle"
+msgid "Invite"
+msgstr "Inviter"
+
+msgid "Issue"
+msgstr "Ticket"
+
msgid "Issue Boards"
msgstr "Tableaux des tickets"
msgid "Issue board focus mode"
-msgstr ""
+msgstr "Mode d’affichage du tableau des tickets"
msgid "Issue events"
msgstr "Événements du ticket"
@@ -3722,6 +4212,48 @@ msgstr "La tâche a été supprimée"
msgid "Jobs"
msgstr "Tâches"
+msgid "Job|Browse"
+msgstr "Parcourir"
+
+msgid "Job|Complete Raw"
+msgstr "Brut complet"
+
+msgid "Job|Download"
+msgstr "Télécharger"
+
+msgid "Job|Erase job log"
+msgstr "Effacer le journal de la tâche"
+
+msgid "Job|Job artifacts"
+msgstr "Artefacts de la tâche"
+
+msgid "Job|Job has been erased"
+msgstr "La tâche a été effacée"
+
+msgid "Job|Job has been erased by"
+msgstr "La tâche a été supprimée par"
+
+msgid "Job|Keep"
+msgstr "Garder"
+
+msgid "Job|Scroll to bottom"
+msgstr "Faire défiler vers le bas"
+
+msgid "Job|Scroll to top"
+msgstr "Faire défiler vers le haut"
+
+msgid "Job|Show complete raw"
+msgstr "Afficher la version brute"
+
+msgid "Job|The artifacts were removed"
+msgstr "Les artefacts ont été supprimés"
+
+msgid "Job|The artifacts will be removed in"
+msgstr "Les artefacts seront supprimés dans"
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr "Cette tâche est bloquée, car aucun exécuteur en ligne n’est attribué au projet."
+
msgid "Jul"
msgstr "juill."
@@ -3734,12 +4266,6 @@ msgstr "juin"
msgid "June"
msgstr "juin"
-msgid "Koding"
-msgstr "Koding"
-
-msgid "Koding Dashboard"
-msgstr ""
-
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -3765,7 +4291,7 @@ msgid "Kubernetes service integration has been deprecated. %{deprecated_message_
msgstr "L’intégration du service Kubernetes est obsolète. %{deprecated_message_content} vos grappes de serveurs Kubernetes en utilisant la nouvelle page <a href=\"%{url}\"/>Clusters Kubernetes</a>"
msgid "LFS"
-msgstr ""
+msgstr "LFS"
msgid "LFSStatus|Disabled"
msgstr "Désactivé"
@@ -3777,10 +4303,10 @@ msgid "Label"
msgstr "Étiquette"
msgid "Label actions dropdown"
-msgstr ""
+msgstr "Menu déroulant des actions sur les étiquettes"
msgid "Label lists show all issues with the selected label."
-msgstr ""
+msgstr "Les listes étiquetées affichent tous les tickets ayant l’étiquette sélectionnée."
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr "%{firstLabelName} +%{remainingLabelCount} de plus"
@@ -3809,6 +4335,9 @@ msgstr "<span>Promouvoir l’étiquette</span> %{labelTitle} <span>en étiquette
msgid "Labels|Promote Label"
msgstr "Promouvoir l’étiquette"
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr "La promotion de l’étiquette « %{labelTitle} » va la rendre disponible pour tous les projets du groupe %{groupName}. Les étiquettes de projet ayant le même intitulé seront fusionnées. Cette action est irréversible."
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Le dernier %d jour"
@@ -3820,6 +4349,9 @@ msgstr "Dernier pipeline"
msgid "Last commit"
msgstr "Dernier commit"
+msgid "Last contact"
+msgstr "Dernier contact"
+
msgid "Last edited %{date}"
msgstr "Dernière modification le %{date}"
@@ -3844,6 +4376,9 @@ msgstr "Derniers changements"
msgid "Learn more"
msgstr "En savoir plus"
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr "En savoir plus sur %{issue_boards_url}, afin de suivre des tickets dans de multiples listes, à l’aide des étiquettes, des personnes assignées et des jalons. Si une fonctionnalité concernant les tableaux des tickets vous manque, veuillez créer un ticket sur %{gitlab_issues_url}."
+
msgid "Learn more about Kubernetes"
msgstr "En savoir plus sur Kubernetes"
@@ -3866,31 +4401,93 @@ msgid "Leave project"
msgstr "Quitter le projet"
msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
+msgstr "Laisser les options « type de fichier » et « mode de livraison » à leurs valeurs par défaut."
msgid "License"
msgstr "Licence"
+msgid "LicenseManagement|Approve license"
+msgstr "Approuver la licence"
+
+msgid "LicenseManagement|Approve license?"
+msgstr "Approuver la licence ?"
+
+msgid "LicenseManagement|Approved"
+msgstr "Approuvée"
+
+msgid "LicenseManagement|Blacklist license"
+msgstr "Mettre la licence en liste noire"
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr "Mettre la licence en liste noire ?"
+
+msgid "LicenseManagement|Blacklisted"
+msgstr "En liste noire"
+
+msgid "LicenseManagement|License"
+msgstr "Licence"
+
+msgid "LicenseManagement|License Management"
+msgstr "Gestion des licences"
+
+msgid "LicenseManagement|License details"
+msgstr "Détails de la licence"
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr "Gérez les licences approuvées et sur liste noire pour ce projet."
+
+msgid "LicenseManagement|Packages"
+msgstr "Paquets"
+
+msgid "LicenseManagement|Remove license"
+msgstr "Supprimer la licence"
+
+msgid "LicenseManagement|Remove license?"
+msgstr "Supprimer la licence ?"
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr "Il n’y a actuellement aucune licence approuvée ou sur liste noire pour ce projet."
+
+msgid "LicenseManagement|URL"
+msgstr "URL"
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr "Vous êtes sur le point de supprimer la licence, %{name}, de ce projet."
+
+msgid "Licenses"
+msgstr "Licences"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] "Affichage limité à %d événement maximum"
+msgstr[1] "Affichage limité à %d événements maximum"
+
msgid "LinkedIn"
-msgstr ""
+msgstr "LinkedIn"
msgid "List"
msgstr "Liste"
msgid "List Your Gitea Repositories"
-msgstr ""
+msgstr "Lister vos dépôts Gitea"
msgid "List available repositories"
-msgstr ""
+msgstr "Lister les dépôts disponibles"
+
+msgid "List your Bitbucket Server repositories"
+msgstr "Lister vos dépôts BitBucket Server"
msgid "List your GitHub repositories"
msgstr "Lister vos dépôts GitHub"
+msgid "Live preview"
+msgstr "Prévisualisation"
+
msgid "Loading contribution stats for group members"
-msgstr ""
+msgstr "Chargement des statistiques de contribution des membres du groupe"
msgid "Loading the GitLab IDE..."
-msgstr "Chargement de l’IDE GitLab…"
+msgstr "Chargement de l’EDI de GitLab…"
msgid "Loading..."
msgstr "Chargement…"
@@ -3904,6 +4501,9 @@ msgstr "Verrouiller %{issuableDisplayName}"
msgid "Lock not found"
msgstr "Verrou non trouvé"
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr "Verrouiller ce·t·te %{issuableDisplayName} ? Seuls les <strong>membres du projet</strong> seront en mesure de commenter."
+
msgid "Lock to current projects"
msgstr "Verrouiller aux projets en cours"
@@ -3917,19 +4517,22 @@ msgid "Locked to current projects"
msgstr "Verrouillé aux projets en cours"
msgid "Locks give the ability to lock specific file or folder."
-msgstr ""
+msgstr "Les verrous permettent de verrouiller un fichier ou un dossier spécifique."
msgid "Logs"
-msgstr ""
+msgstr "Journaux"
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
-msgstr ""
+msgstr "Rendez chaque membre de votre équipe plus productif, quel que soit l’endroit où il se situe. GitLab Geo crée des miroirs en lecture seule de votre instance GitLab afin que vous puissiez réduire le temps nécessaire pour cloner et récupérer de gros dépôts."
msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
+msgstr "Assurezâ€vous d’être connecté avec le compte propriétaire des projets que vous souhaitez importer."
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
-msgstr ""
+msgstr "Gérez les dépôts Git avec des contrôles d’accès très précis permettant de sécuriser votre code. Effectuez des revues de code et renforcez la collaboration avec les demandes de fusion Git. Chaque projet peut également avoir un système de tickets de suivi et un wiki."
+
+msgid "Manage Web IDE features"
+msgstr "Gérer les fonctionnalités de l’EDI Web"
msgid "Manage access"
msgstr "Gestion des accès"
@@ -3938,10 +4541,10 @@ msgid "Manage all notifications"
msgstr "Gérer toutes les notifications"
msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
-msgstr ""
+msgstr "Gérez les applications pouvant utiliser GitLab en tant que fournisseur OAuth et les applications que vous avez autorisées à utiliser votre compte."
msgid "Manage applications that you've authorized to use your account."
-msgstr ""
+msgstr "Gérez les applications que vous avez autorisées à utiliser votre compte."
msgid "Manage group labels"
msgstr "Gérer les étiquettes de groupe"
@@ -3953,25 +4556,25 @@ msgid "Manage project labels"
msgstr "Gérer les étiquettes de projet"
msgid "Manage your group’s membership while adding another level of security with SAML."
-msgstr ""
+msgstr "Gérez les membres de votre groupe tout en ajoutant un niveau de sécurité supplémentaire avec SAML."
msgid "Manifest"
-msgstr ""
+msgstr "Manifeste"
msgid "Manifest file import"
-msgstr ""
+msgstr "Importation de fichier manifeste"
msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
+msgstr "Associer un identifiant de compte FogBugz à un utilisateur GitLab"
msgid "Map a Google Code user to a GitLab user"
-msgstr ""
+msgstr "Associer un utilisateur de Google Code à un utilisateur GitLab"
msgid "Map a Google Code user to a full email address"
-msgstr ""
+msgstr "Associer un utilisateur de Google Code à une adresse de courriel complète"
msgid "Map a Google Code user to a full name"
-msgstr ""
+msgstr "Associer un utilisateur de Google Code à un nom complet"
msgid "Mar"
msgstr "mars"
@@ -3985,9 +4588,21 @@ msgstr "Marquer comme fait"
msgid "Markdown enabled"
msgstr "Markdown activé"
+msgid "Maven Metadata"
+msgstr "Métadonnées Maven"
+
+msgid "Maven package"
+msgstr "Paquet Maven"
+
+msgid "Max access level"
+msgstr "Niveau d’accès maximum"
+
msgid "Maximum git storage failures"
msgstr "Nombre maximum d’échecs du stockage Git"
+msgid "Maximum job timeout"
+msgstr "Durée maximale d’exécution de la tâche"
+
msgid "May"
msgstr "mai"
@@ -3998,7 +4613,7 @@ msgid "Members"
msgstr "Membres"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
-msgstr ""
+msgstr "Les membres seront redirigés à cet emplacement lors de la connexion à votre groupe. Obtenezâ€le auprès de votre fournisseur d’identités, sous la dénomination « emplacement du service SSO », « point de terminaison d’émission de jetons SAML » ou « URL SAML 2.0 / W-Federation »."
msgid "Merge Request"
msgstr "Demande de fusion"
@@ -4019,7 +4634,7 @@ msgid "Merge request"
msgstr "Demande de fusion"
msgid "Merge request approvals"
-msgstr ""
+msgstr "Approbations de la demande de fusion"
msgid "Merge requests"
msgstr "Demandes de fusion"
@@ -4036,15 +4651,15 @@ msgstr "L’enregistrement du commentaire a échoué"
msgid "MergeRequests|Toggle comments for this file"
msgstr "Activer/désactiver les commentaires pour ce fichier"
-msgid "MergeRequests|Updating discussions failed"
-msgstr "Échec de la mise à jour des discussions"
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr "Afficher le fichier au commit %{commitId}"
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr "Afficher le fichier remplacé au commit %{commitId}"
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr "%{paragraphStart}a changé la description %{descriptionChangedTimes} fois %{timeDifferenceMinutes}%{paragraphEnd}"
+
msgid "Merged"
msgstr "Fusionnée"
@@ -4060,35 +4675,38 @@ msgstr "Métriques — Influx"
msgid "Metrics - Prometheus"
msgstr "Métriques — Prometheus"
+msgid "Metrics and profiling"
+msgstr "Statistiques et rapports"
+
msgid "Metrics|Business"
-msgstr ""
+msgstr "Affaires"
msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
-msgstr ""
+msgstr "Consultez la documentation sur l’intégration et la livraison continues (CI/CD) concernant le déploiement dans un environnement"
msgid "Metrics|Create metric"
-msgstr ""
+msgstr "Créer une métrique"
msgid "Metrics|Edit metric"
-msgstr ""
+msgstr "Modifier la métrique"
msgid "Metrics|Environment"
msgstr "Environnement"
msgid "Metrics|For grouping similar metrics"
-msgstr ""
+msgstr "Pour regrouper des métriques similaires"
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
-msgstr ""
+msgstr "Libellé de l’axe vertical du graphique. En général, l’unité de mesure. L’axe horizontal (axe X) représente toujours le temps."
msgid "Metrics|Learn about environments"
-msgstr ""
+msgstr "En savoir plus sur les environnements"
msgid "Metrics|Legend label (optional)"
-msgstr ""
+msgstr "Libellé de légende (facultatif)"
msgid "Metrics|Must be a valid PromQL query."
-msgstr ""
+msgstr "La requête doit être une requête PromQL valide."
msgid "Metrics|Name"
msgstr "Nom"
@@ -4121,50 +4739,65 @@ msgid "Metrics|There was an error getting environments information."
msgstr "Une erreur s’est produite lors de l’obtention des informations d’environnement."
msgid "Metrics|There was an error while retrieving metrics"
-msgstr ""
+msgstr "Une erreur est survenue lors de la récupération des métriques"
msgid "Metrics|Type"
msgstr "Type"
msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
-msgstr ""
+msgstr "Le point de terminaison Prometheus a renvoyé une réponse contenant des données de déploiement inattendues"
msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
-msgstr ""
+msgstr "Le point de terminaison Prometheus a renvoyé des données de mesure inattendues"
msgid "Metrics|Unit label"
-msgstr ""
+msgstr "Libellé de l’unité"
msgid "Metrics|Used as a title for the chart"
-msgstr ""
+msgstr "Utilisé comme titre pour le graphique"
msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
-msgstr ""
+msgstr "Utilisé si la requête ne renvoie qu’une seule série. Si elle renvoie plusieurs séries, leurs libellés de légende seront collectés à partir de la réponse."
msgid "Metrics|Y-axis label"
-msgstr ""
+msgstr "Libellé de l’axe Y"
msgid "Metrics|e.g. HTTP requests"
-msgstr ""
+msgstr "p. ex., requêtes HTTP"
msgid "Metrics|e.g. Requests/second"
-msgstr ""
+msgstr "p. ex., requêtes/seconde"
msgid "Metrics|e.g. Throughput"
-msgstr ""
+msgstr "p. ex., débit"
msgid "Metrics|e.g. rate(http_requests_total[5m])"
-msgstr ""
+msgstr "p. ex., rate(http_requests_total[5m])"
msgid "Metrics|e.g. req/sec"
-msgstr ""
+msgstr "p. ex., req/sec"
msgid "Milestone"
msgstr "Jalon"
+msgid "Milestone lists not available with your current license"
+msgstr "La liste des jalons n’est pas disponible avec votre licence actuelle"
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr "Les listes de jalon affichent tous les tickets à partir du jalon sélectionné."
+
msgid "Milestones"
msgstr "Jalons"
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr "Vous êtes sur le point de supprimer définitivement le jalon %{milestoneTitle} et de le supprimer de %{issuesWithCount} et %{mergeRequestsWithCount}. La suppression est irréversible."
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr "Vous êtes sur le point de supprimer définitivement le jalon %{milestoneTitle}. Ce jalon n’est actuellement référencé par aucun ticket ni aucune demande de fusion."
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr "<p>%{milestonePromotion}</p> %{finalWarning}"
+
msgid "Milestones|Delete milestone"
msgstr "Supprimer le jalon"
@@ -4183,9 +4816,30 @@ msgstr "Promouvoir %{milestoneTitle} en tant que jalon de groupe ?"
msgid "Milestones|Promote Milestone"
msgstr "Promouvoir le jalon"
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr "Promouvoir %{milestone} le rendra disponible pour tous les projets du groupe %{groupName}. Les jalons des projets portant le même nom seront fusionnés."
+
msgid "Milestones|This action cannot be reversed."
+msgstr "Cette action ne peut pas être annulée."
+
+msgid "Mirror a repository"
+msgstr "Créer un miroir de dépôt"
+
+msgid "Mirror direction"
+msgstr "Sens du miroir"
+
+msgid "Mirror repository"
msgstr ""
+msgid "Mirror user"
+msgstr "Utilisateur accédant au miroir"
+
+msgid "Mirrored repositories"
+msgstr "Dépôts mis en miroir"
+
+msgid "Mirroring repositories"
+msgstr "Dépôts miroir"
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "ajouter une clef SSH"
@@ -4202,10 +4856,7 @@ msgid "Months"
msgstr "Mois"
msgid "More"
-msgstr ""
-
-msgid "More actions"
-msgstr "Plus d’actions"
+msgstr "Plus"
msgid "More info"
msgstr "En savoir plus"
@@ -4217,7 +4868,7 @@ msgid "More information is available|here"
msgstr "ici"
msgid "Most stars"
-msgstr ""
+msgstr "Les plus étoilés"
msgid "Move"
msgstr "Déplacer"
@@ -4238,7 +4889,7 @@ msgid "Name your individual key via a title"
msgstr "Nommez votre clef personnelle avec un titre"
msgid "Name:"
-msgstr ""
+msgstr "Nom :"
msgid "Nav|Help"
msgstr "Aide"
@@ -4253,16 +4904,19 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "Se déconnecter et se reconnecter avec un autre compte"
msgid "Network"
-msgstr ""
+msgstr "Réseau"
+
+msgid "Never"
+msgstr "Jamais"
msgid "New"
msgstr "Nouveau"
msgid "New Application"
-msgstr ""
+msgstr "Nouvelle application"
msgid "New Group"
-msgstr ""
+msgstr "Nouveau groupe"
msgid "New Identity"
msgstr "Nouvelle identité"
@@ -4333,7 +4987,7 @@ msgid "New tag"
msgstr "Nouvelle étiquette"
msgid "New..."
-msgstr ""
+msgstr "Nouveau…"
msgid "No"
msgstr "Non"
@@ -4344,12 +4998,21 @@ msgstr "Aucune étiquette"
msgid "No assignee"
msgstr "Aucune personne assignée"
+msgid "No branches found"
+msgstr "Aucune branche trouvée"
+
msgid "No changes"
msgstr "Aucun changement"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "Aucune connexion n’a pu être établie avec un serveur Gitaly, veuillez vérifier votre journal !"
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr "Aucune image de conteneur stockée pour ce projet. Ajoutezâ€en une en suivant les instructions ciâ€dessous."
+
+msgid "No contributions were found"
+msgstr "Aucune contribution n’a été trouvée"
+
msgid "No due date"
msgstr "Aucune date d’échéance"
@@ -4366,13 +5029,16 @@ msgid "No files found."
msgstr "Aucun fichier trouvé."
msgid "No issues for the selected time period."
-msgstr ""
+msgstr "Aucun ticket pour la période sélectionnée."
msgid "No labels with such name or description"
-msgstr ""
+msgstr "Aucune étiquette avec un tel nom ou une telle description"
+
+msgid "No license. All rights reserved"
+msgstr "Aucune licence. Tous droits réservés"
msgid "No merge requests for the selected time period."
-msgstr ""
+msgstr "Aucune demande de fusion pour la période sélectionnée."
msgid "No merge requests found"
msgstr "Aucune demande de fusion trouvée"
@@ -4381,29 +5047,44 @@ msgid "No messages were logged"
msgstr "Aucun message n’a été enregistré"
msgid "No other labels with such name or description"
-msgstr ""
+msgstr "Aucune autre étiquette avec un tel nom ou une telle description"
+
+msgid "No packages stored for this project."
+msgstr "Aucun paquet stocké pour ce projet."
msgid "No prioritised labels with such name or description"
-msgstr ""
+msgstr "Aucune étiquette prioritaire avec un tel nom ou une telle description"
msgid "No public groups"
-msgstr ""
+msgstr "Aucun groupe public"
msgid "No pushes for the selected time period."
-msgstr ""
+msgstr "Rien n’a été poussé vers GIt durant la période sélectionnée."
msgid "No repository"
msgstr "Aucun dépôt"
+msgid "No runners found"
+msgstr "Aucun exécuteur trouvé"
+
msgid "No schedules"
msgstr "Aucune planification"
msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
+msgstr "Non, importer directement les adresses de courriel et les noms d’utilisateur existants."
+
+msgid "Nodes"
+msgstr "NÅ“uds"
msgid "None"
msgstr "Aucun·e"
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr "Certains commentaires ne sont pas affichés car vous comparez deux versions du diff."
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr "Certains commentaires ne sont pas affichés car vous consultez une ancienne version du diff."
+
msgid "Not allowed to merge"
msgstr "Non autorisé·e à fusionner"
@@ -4422,23 +5103,26 @@ msgstr "Pas confidentiel·le"
msgid "Not enough data"
msgstr "Données insuffisantes"
+msgid "Not now"
+msgstr "Pas maintenant"
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Notez que la branche principale « master » est automatiquement protégée. %{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "Remarque : En tant qu’administrateur ou administratrice, vous pouvez configurer %{github_integration_link}, ce qui vous permettra de vous authentifier via GitHub et de connecter des dépôts sans générer de jeton d’accès personnel."
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "Remarque : En tant qu’administra·teur·trice, vous pouvez configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et de permettre l’importation de dépôts sans générer de jeton d’accès personnel."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "Remarque : Envisagez de demander à votre administrateur ou administratrice GitLab de configurer %{github_integration_link}, ce qui vous permettra de vous authentifier via GitHub et de connecter des dépôts sans générer de jeton d’accès personnel."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "Remarque : Envisagez de demander à votre administra·teur·trice GitLab de configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et d’importer des dépôts sans générer de jeton d’accès personnel."
msgid "Notes|Are you sure you want to cancel creating this comment?"
-msgstr ""
+msgstr "Souhaitezâ€vous réellement annuler la création de ce commentaire ?"
msgid "Notification events"
msgstr "Événement de notifications"
@@ -4455,6 +5139,9 @@ msgstr "Pipeline en échec"
msgid "NotificationEvent|Merge merge request"
msgstr "Fusionner la demande de fusion"
+msgid "NotificationEvent|New epic"
+msgstr "Nouvelle épopée"
+
msgid "NotificationEvent|New issue"
msgstr "Nouveau ticket"
@@ -4527,18 +5214,26 @@ msgstr "Filtre"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr "Une fois importés, les dépôts peuvent être mis en miroir via SSH. Voir %{ssh_link}"
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] "Un élément de plus"
+msgstr[1] "%d éléments de plus"
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
+msgstr "Un ou plusieurs de vos projets Bitbucket ne peuvent être importés directement dans GitLab parce qu’ils utilisent Subversion ou Mercurial comme gestionnaire de versions au lieu de Git."
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
+msgstr "Un ou plusieurs de vos projets Google Code ne peuvent être importés directement dans GitLab parce qu’ils utilisent Subversion ou Mercurial comme gestionnaire de versions au lieu de Git."
-msgid "Online IDE integration settings."
-msgstr "Paramètres d’intégration de l’EDI en ligne."
+msgid "Only admins"
+msgstr "Seulement les administrateurs"
msgid "Only comments from the following commit are shown below"
msgstr "Seuls les commentaires du commit suivant sont affichés ciâ€dessous"
+msgid "Only mirror protected branches"
+msgstr "Ne mettre en miroir que les branches protégées"
+
msgid "Only project members can comment."
msgstr "Seuls les membres du projet peuvent commenter."
@@ -4552,16 +5247,16 @@ msgid "Open in Xcode"
msgstr "Ouvrir dans Xcode"
msgid "Open sidebar"
-msgstr ""
+msgstr "Ouvrir la barre latérale"
msgid "Open source software to collaborate on code"
-msgstr ""
+msgstr "Logiciel libre permettant de collaborer sur du code source"
msgid "Opened"
-msgstr ""
+msgstr "Ouvert"
msgid "Opened MR"
-msgstr ""
+msgstr "Demandes de fusion ouvertes"
msgid "Opened issues"
msgstr "Tickets ouverts"
@@ -4576,10 +5271,10 @@ msgid "Operations"
msgstr "Opérations"
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
-msgstr ""
+msgstr "Vous pouvez éventuellement %{link_to_customize} la manière dont les adresses de courriel et les noms d’utilisateur issus de FogBugz sont importés dans GitLab."
msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
+msgstr "Vous pouvez éventuellement %{link_to_customize} la manière dont les adresses de courriel et les noms d’utilisateur issus de Google Code sont importés dans GitLab."
msgid "Options"
msgstr "Options"
@@ -4602,14 +5297,26 @@ msgstr "Requêtes sortantes"
msgid "Overview"
msgstr "Vue d’ensemble"
+msgid "Overwrite diverged branches"
+msgstr "Écraser les branches divergentes"
+
msgid "Owner"
msgstr "Propriétaire"
+msgid "Package information"
+msgstr "Informations du paquet"
+
+msgid "Package was removed"
+msgstr "Le paquet a été supprimé"
+
+msgid "Packages"
+msgstr "Paquets"
+
msgid "Pages"
msgstr "Pages"
msgid "Pagination|Last »"
-msgstr "Dernière »"
+msgstr "Dernière ⇥"
msgid "Pagination|Next"
msgstr "Suivante"
@@ -4618,7 +5325,7 @@ msgid "Pagination|Prev"
msgstr "Précédente"
msgid "Pagination|« First"
-msgstr "« Première"
+msgstr "⇤ Première"
msgid "Part of merge request changes"
msgstr "Partie des modifications de la demande de fusion"
@@ -4630,14 +5337,20 @@ msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh
msgstr "Collez votre clef SSH publique, qui est habituellement située dans le fichier « ~/.ssh/id_rsa.pub » et commence par « ssh-rsa ». N’utilisez pas votre clef SSH privée !"
msgid "Path:"
-msgstr ""
+msgstr "Chemin d’accès :"
msgid "Pause"
msgstr "Pause"
+msgid "Paused Runners don't accept new jobs"
+msgstr "Les exécuteurs en pause n’acceptent pas de nouvelles tâches"
+
msgid "Pending"
msgstr "En attente"
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr "Les personnes sans autorisation ne recevront jamais de notifications et ne pourront pas commenter."
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr "Par tâche. Si une tâche dépasse ce seuil, elle sera marquée comme ayant échoué"
@@ -4656,6 +5369,9 @@ msgstr "Jeton d’accès personnel"
msgid "Pipeline"
msgstr "Pipeline"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr "Pipeline %{pipelineLinkStart} nᵒ %{pipelineId} %{pipelineLinkEnd} de %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+
msgid "Pipeline Health"
msgstr "État de santé du pipeline"
@@ -4743,6 +5459,9 @@ msgstr "CI Lint"
msgid "Pipelines|Clear Runner Caches"
msgstr "Vider les caches des exécuteurs"
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr "L’intégration continue peut aider à détecter les bogues en exécutant vos tests automatiquement, tandis que la livraison continue peut vous aider à déployer du code dans votre environnement de production."
+
msgid "Pipelines|Get started with Pipelines"
msgstr "Premiers pas avec les pipelines"
@@ -4764,6 +5483,9 @@ msgstr "Il n’y a actuellement pas de pipelines %{scope}."
msgid "Pipelines|There are currently no pipelines."
msgstr "Il n’y a actuellement aucun pipeline."
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr "Une erreur est survenue lors de la récupération des pipelines. Réessayez dans quelques instants ou contactez votre équipe d’assistance."
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "Ce projet n’est actuellement pas configuré pour exécuter des pipelines."
@@ -4812,12 +5534,6 @@ msgstr "avec les étapes"
msgid "Plain diff"
msgstr "Diff brut"
-msgid "Planned finish date"
-msgstr "Date de fin prévisionnelle"
-
-msgid "Planned start date"
-msgstr "Date de début prévisionnelle"
-
msgid "PlantUML"
msgstr "PlantUML"
@@ -4828,13 +5544,13 @@ msgid "Please accept the Terms of Service before continuing."
msgstr "Veuillez accepter les conditions générales d’utilisation avant de continuer."
msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
-msgstr ""
+msgstr "Veuillez les convertir en %{link_to_git} et repasser par %{link_to_import_flow}."
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
+msgstr "Veuillez les convertir en dépôts Git sur Google Code et repasser par %{link_to_import_flow}."
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
-msgstr ""
+msgstr "Veuillez noter que cette application n’est pas fournie par GitLab, vous devriez vérifier son authenticité avant d’autoriser son accès."
msgid "Please select at least one filter to see results"
msgstr "Veuillez sélectionner au moins un filtre pour voir les résultats"
@@ -4857,6 +5573,15 @@ msgstr "Préférences"
msgid "Preferences|Navigation theme"
msgstr "Thème de navigation"
+msgid "Press Enter or click to search"
+msgstr "Appuyez sur Entrée ou cliquez pour rechercher"
+
+msgid "Preview"
+msgstr "Aperçu"
+
+msgid "Preview payload"
+msgstr "Aperçu de la charge utile"
+
msgid "Primary"
msgstr "Principal"
@@ -4873,7 +5598,7 @@ msgid "Prioritized label"
msgstr "Étiquette prioritaire"
msgid "Private - Project access must be granted explicitly to each user."
-msgstr "Privé — l’accès au projet doit être autorisé explicitement pour chaque utilisa·teur·trice."
+msgstr "Privé — l’accès au projet doit être autorisé explicitement pour chaque utilisateur et utilisatrice."
msgid "Private - The group and its projects can only be viewed by members."
msgstr "Privé — le groupe ainsi que ses projets ne sont accessibles qu’à ses membres."
@@ -4885,7 +5610,16 @@ msgid "Profile"
msgstr "Profil"
msgid "Profile Settings"
-msgstr ""
+msgstr "Paramètres du profil"
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr "Vous êtes sur le point de supprimer définitivement %{yourAccount}, ainsi que tous les tickets, les demandes de fusion et les groupes liés à votre compte. Après confirmation, %{deleteAccount} ne peut être ni annulé ni restauré."
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr "Vous êtes sur le point de changer le nom d’utilisateur %{currentUsernameBold} en %{newUsernameBold}. Le profil et les projets seront redirigés vers l’espace de noms %{newUsername}, mais cette redirection expire si un nouvel utilisateur ou un nouveau groupe est créé avec l’ancien nom %{currentUsername}. Veuillez mettre à jour vos dépôts Git dès que possible."
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr "%{author_name} a fait une contribution privée"
msgid "Profiles|Account scheduled for removal."
msgstr "Compte programmé pour suppression."
@@ -4893,12 +5627,33 @@ msgstr "Compte programmé pour suppression."
msgid "Profiles|Add key"
msgstr "Ajouter une clef"
+msgid "Profiles|Add status emoji"
+msgstr "Ajouter un émoji de statut"
+
+msgid "Profiles|Avatar cropper"
+msgstr "Rogneur d’avatar"
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr "L’avatar sera supprimé. Êtesâ€vous sûr·e ?"
+
msgid "Profiles|Change username"
msgstr "Changer le nom d’utilisateur·rice"
+msgid "Profiles|Choose file..."
+msgstr "Choisir un fichier…"
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr "Afficher les contributions de projets privés sur votre profil public sans aucune information sur les projets, les dépôts ou les organisations."
+
+msgid "Profiles|Clear status"
+msgstr "Effacer le statut"
+
msgid "Profiles|Current path: %{path}"
msgstr "Chemin d’accès actuel : %{path}"
+msgid "Profiles|Current status"
+msgstr "État actuel"
+
msgid "Profiles|Delete Account"
msgstr "Supprimer un compte"
@@ -4911,35 +5666,110 @@ msgstr "Supprimer votre compte ?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr "Supprimer un compte aura les conséquences suivantes :"
+msgid "Profiles|Do not show on profile"
+msgstr "Ne pas montrer sur le profil"
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr "Ne pas afficher les informations personnelles liées à l’activité sur vos profils"
+
+msgid "Profiles|Edit Profile"
+msgstr "Modifier le profil"
+
msgid "Profiles|Invalid password"
msgstr "Mot de passe incorrect"
msgid "Profiles|Invalid username"
msgstr "Nom d’utilisateur incorrect"
+msgid "Profiles|Main settings"
+msgstr "Paramètres principaux"
+
+msgid "Profiles|No file chosen"
+msgstr "Aucun fichier choisi"
+
msgid "Profiles|Path"
msgstr "Chemin d’accès"
+msgid "Profiles|Position and size your new avatar"
+msgstr "Position et taille de votre nouvel avatar"
+
+msgid "Profiles|Private contributions"
+msgstr "Contributions privées"
+
+msgid "Profiles|Public Avatar"
+msgstr "Avatar public"
+
+msgid "Profiles|Remove avatar"
+msgstr "Supprimer l’avatar"
+
+msgid "Profiles|Set new profile picture"
+msgstr "Définir une nouvelle photo de profil"
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr "Certaines options ne sont pas disponibles pour les comptes LDAP"
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr "Parlezâ€nous de vous en moins de 250 caractères."
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr "La taille de fichier maximale autorisée est de 200 Kio."
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
-msgstr ""
+msgstr "Ceci ne ressemble pas à une clef SSH publique, êtesâ€vous sûr(e) de vouloir l’ajouter ?"
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr "Cette adresse de courriel sera affichée sur votre profil public."
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr "Cet émoji et ce message apparaîtront sur votre profil et partout dans l’interface."
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr "Cette fonctionnalité est expérimentale et les traductions ne sont pas encore complètes."
+
+msgid "Profiles|This information will appear on your profile."
+msgstr "Cette information apparaîtra sur votre profil."
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Saisissez votre %{confirmationValue} pour confirmer :"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
-msgstr ""
+msgstr "Commence généralement par « ssh-rsa […] »"
+
+msgid "Profiles|Update profile settings"
+msgstr "Mettre à jour les paramètres du profil"
msgid "Profiles|Update username"
-msgstr "Mettre à jour le nom d’utilisateur·rice"
+msgstr "Mettre à jour le nom d’utilisateur"
+
+msgid "Profiles|Upload new avatar"
+msgstr "Téléverser un nouvel avatar"
msgid "Profiles|Username change failed - %{message}"
-msgstr "Le changement de nom d’utilisateur·rice a échoué : %{message}"
+msgstr "Le changement de nom d’utilisateur a échoué : %{message}"
msgid "Profiles|Username successfully changed"
-msgstr "Changement de nom d’utilisa·teur·trice effectué"
+msgstr "Changement de nom d’utilisateur effectué"
+
+msgid "Profiles|Website"
+msgstr "Site Web"
+
+msgid "Profiles|What's your status?"
+msgstr "Quel est votre statut ?"
+
+msgid "Profiles|You can change your avatar here"
+msgstr "Vous pouvez changer votre avatar ici"
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr "Vous pouvez changer votre avatar ici ou supprimer l’avatar actuel et revenir à %{gravatar_link}"
+
+msgid "Profiles|You can upload your avatar here"
+msgstr "Vous pouvez téléverser votre avatar ici"
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr "Vous pouvez téléverser votre avatar ici ou le changer en %{gravatar_link}"
msgid "Profiles|You don't have access to delete this user."
-msgstr "Vous n’avez pas les autorisations suffisantes pour supprimer cet·te utilisa·teur·trice."
+msgstr "Vous n’avez pas les autorisations suffisantes pour supprimer cet utilisateur ou cette utilisatrice."
msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
msgstr "Vous devez transférer la propriété ou supprimer ces groupes avant de pouvoir supprimer votre compte."
@@ -4947,8 +5777,20 @@ msgstr "Vous devez transférer la propriété ou supprimer ces groupes avant de
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Votre compte est actuellement propriétaire des groupes suivants :"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr "Votre adresse de courriel a été automatiquement définie en fonction de votre compte %{provider_label}."
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr "Votre localisation a été automatiquement définie en fonction de votre compte %{provider_label}."
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr "Votre nom a été automatiquement défini en fonction de votre compte %{provider_label}, afin que les personnes que vous connaissez puissent vous identifier."
+
+msgid "Profiles|Your status"
+msgstr "Votre statut"
+
msgid "Profiles|e.g. My MacBook key"
-msgstr ""
+msgstr "p. ex., Ma clef MacBook"
msgid "Profiles|your account"
msgstr "votre compte"
@@ -4978,7 +5820,10 @@ msgid "Project '%{project_name}' was successfully updated."
msgstr "Mise à jour du projet « %{project_name} » effectuée."
msgid "Project Badges"
-msgstr "Badges de projet"
+msgstr "Badges numériques du projet"
+
+msgid "Project URL"
+msgstr "URL du projet"
msgid "Project access must be granted explicitly to each user."
msgstr "L’accès au projet doit être explicitement accordé à chaque utilisateur."
@@ -5005,19 +5850,22 @@ msgid "Project export started. A download link will be sent by email."
msgstr "L’exportation du projet a débuté. Un lien de téléchargement sera envoyé par courriel."
msgid "Project name"
-msgstr ""
+msgstr "Nom du projet"
+
+msgid "Project slug"
+msgstr "Identifiant « slug » du projet"
msgid "ProjectActivityRSS|Subscribe"
msgstr "S’abonner"
msgid "ProjectCreationLevel|Allowed to create projects"
-msgstr ""
+msgstr "Autorisé·e à créer des projets"
msgid "ProjectCreationLevel|Default project creation protection"
-msgstr ""
+msgstr "Protection de création de projets par défaut"
msgid "ProjectCreationLevel|Developers + Maintainers"
-msgstr ""
+msgstr "Développeurs et développeuses + Responsables"
msgid "ProjectCreationLevel|Maintainers"
msgstr "Responsables"
@@ -5034,38 +5882,68 @@ msgstr "Jamais"
msgid "ProjectLifecycle|Stage"
msgstr "Étape"
-msgid "ProjectPage|Project ID: %{project_id}"
+msgid "ProjectOverview|Fork"
+msgstr "Créer une divergence"
+
+msgid "ProjectOverview|Forks"
+msgstr "Divergences"
+
+msgid "ProjectOverview|Go to your fork"
+msgstr "Aller à votre divergence"
+
+msgid "ProjectOverview|Star"
+msgstr "Mettre une étoile"
+
+msgid "ProjectOverview|Unstar"
+msgstr "Supprimer l’étoile"
+
+msgid "ProjectOverview|You have reached your project limit"
msgstr ""
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr "Vous devez vous authentifier afin de pouvoir ajouter une étoile à un projet"
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr "Identifiant de projet : %{project_id}"
+
+msgid "ProjectSettings|Badges"
+msgstr "Badges numériques"
+
msgid "ProjectSettings|Contact an admin to change this setting."
-msgstr ""
+msgstr "Contactez un administrateur pour modifier ce paramètre."
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr "Personnalisez les badges numériques de votre projet."
msgid "ProjectSettings|Failed to protect the tag"
-msgstr ""
+msgstr "Impossible de protéger l’étiquette"
msgid "ProjectSettings|Failed to update tag!"
-msgstr ""
+msgstr "Impossible de mettre à jour l’étiquette !"
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr "En savoir plus sur les badges numériques."
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
-msgstr ""
+msgstr "Seuls les commits signés peuvent être poussés sur ce dépôt Git."
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr ""
+msgstr "Ce paramètre s’applique au niveau du serveur mais il peut être outrepassé par un administrateur."
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
-msgstr ""
+msgstr "Ce paramètre s’applique au niveau du serveur, mais il a été outrepassé pour ce projet."
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr ""
+msgstr "Ce paramètre s’appliquera à tous les projets à moins qu’un administrateur ou une administratrice ne l’outrepasse sur certains projets."
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr ""
+msgstr "Les utilisateurs et utilisatrices ne peuvent uniquement pousser sur ce dépôt que des commits qui ont été effectués avec une de leurs adresses de courriel vérifiées."
msgid "Projects"
msgstr "Projets"
msgid "Projects shared with %{group_name}"
-msgstr ""
+msgstr "Projets partagés avec %{group_name}"
msgid "ProjectsDropdown|Frequently visited"
msgstr "Les plus consultés"
@@ -5085,35 +5963,38 @@ msgstr "Un problème est survenu de notre côté."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Désolé, aucun projet ne correspond à votre recherche"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr "Cette fonctionnalité nécessite un navigateur prenant en charge localStorage"
+
msgid "PrometheusAlerts|Add alert"
-msgstr ""
+msgstr "Ajouter une alerte"
msgid "PrometheusAlerts|Alert set"
-msgstr ""
+msgstr "Alerte définie"
msgid "PrometheusAlerts|Edit alert"
-msgstr ""
+msgstr "Modifier l’alerte"
msgid "PrometheusAlerts|Error creating alert"
-msgstr ""
+msgstr "Erreur lors de la création de l’alerte"
msgid "PrometheusAlerts|Error deleting alert"
-msgstr ""
+msgstr "Erreur lors de la suppression de l’alerte"
msgid "PrometheusAlerts|Error fetching alert"
-msgstr ""
+msgstr "Erreur lors de la récupération de l’alerte"
msgid "PrometheusAlerts|Error saving alert"
-msgstr ""
+msgstr "Erreur lors de l’enregistrement de l’alerte"
msgid "PrometheusAlerts|No alert set"
-msgstr ""
+msgstr "Aucune alerte définie"
msgid "PrometheusAlerts|Operator"
-msgstr ""
+msgstr "Opérateur"
msgid "PrometheusAlerts|Threshold"
-msgstr ""
+msgstr "Seuil"
msgid "PrometheusDashboard|Time"
msgstr "Heure"
@@ -5140,16 +6021,16 @@ msgid "PrometheusService|Common metrics"
msgstr "Métriques communes"
msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
-msgstr ""
+msgstr "Les métriques courantes sont automatiquement supervisées grâce à une bibliothèque de métriques provenant d’exportateurs populaires."
msgid "PrometheusService|Custom metrics"
-msgstr ""
+msgstr "Métriques personnalisées"
msgid "PrometheusService|Finding and configuring metrics..."
msgstr "Recherche et configuration des métriques en cours…"
msgid "PrometheusService|Finding custom metrics..."
-msgstr ""
+msgstr "Recherche des métriques personnalisées en cours…"
msgid "PrometheusService|Install Prometheus on clusters"
msgstr "Installer Prometheus sur les grappes de serveurs"
@@ -5170,7 +6051,7 @@ msgid "PrometheusService|More information"
msgstr "Plus d’informations"
msgid "PrometheusService|New metric"
-msgstr ""
+msgstr "Nouvelle métrique"
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "URL de base de l’API Prometheus, telle que http://prometheus.example.com/"
@@ -5179,7 +6060,7 @@ msgid "PrometheusService|Prometheus is being automatically managed on your clust
msgstr "Prometheus est géré automatiquement sur vos grappes de serveurs"
msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
-msgstr ""
+msgstr "Ces métriques ne seront supervisées qu’après votre premier déploiement dans un environnement"
msgid "PrometheusService|Time-series monitoring service"
msgstr "Service de supervision de séries temporelles"
@@ -5206,16 +6087,64 @@ msgid "Promote to group label"
msgstr "Promouvoir en tant qu’étiquette de groupe"
msgid "Promotions|Don't show me this again"
-msgstr ""
+msgstr "Ne plus réafficher ce message"
msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
-msgstr ""
+msgstr "Les épopées vous permettent de gérer votre portefeuille de projets plus efficacement et avec moins d’efforts grâce au suivi de groupes de tickets qui partagent un thème commun au travers de différents projets et jalons."
msgid "Promotions|This feature is locked."
-msgstr ""
+msgstr "Cette fonctionnalité est verrouillée."
msgid "Promotions|Upgrade plan"
-msgstr ""
+msgstr "Mise à niveau du forfait"
+
+msgid "Protected"
+msgstr "Protégé"
+
+msgid "Protected Environments"
+msgstr "Environnements protégés"
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr "%{environment_name} sera accessible en écriture aux développeurs. Êtesâ€vous sûr de vouloir cela ?"
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr "Autorisé à déployer"
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr "ProtectedEnvironment|Choisissez qui est autorisé à déployer"
+
+msgid "ProtectedEnvironment|Environment"
+msgstr "Environnement"
+
+msgid "ProtectedEnvironment|Protect"
+msgstr "Protéger"
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr "Protéger les environnements afin de restreindre les déploiements aux personnes autorisées."
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr "Protéger un environnement"
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr "Environnements protégés (%{protected_environments_count})"
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr "Sélectionner un environnement"
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr "Il n’y a actuellement aucun environnement protégé, protégezâ€en un avec le formulaire ciâ€dessus."
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr "Déprotéger"
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr "Votre environnement ne peut pas être déprotégé"
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr "Votre environnement a été protégé."
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr "Votre environnement a été déprotégé"
msgid "Protip:"
msgstr "Astuce :"
@@ -5235,6 +6164,12 @@ msgstr "Public - le projet est accessible sans aucune authentification."
msgid "Public pipelines"
msgstr "Pipelines publics"
+msgid "Pull"
+msgstr "Récupérer"
+
+msgid "Push"
+msgstr "Push"
+
msgid "Push Rules"
msgstr "Règles de poussage Git"
@@ -5248,7 +6183,7 @@ msgid "Push to create a project"
msgstr "Pousser pour créer un projet"
msgid "PushRule|Committer restriction"
-msgstr ""
+msgstr "Restriction sur l’auteur des commits"
msgid "Pushed"
msgstr "Poussé"
@@ -5266,7 +6201,7 @@ msgid "Read more"
msgstr "Lire plus"
msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
-msgstr ""
+msgstr "Pour en savoir plus sur les autorisations de projet : <strong>%{link_to_help}</strong>"
msgid "Readme"
msgstr "LisezMoi"
@@ -5274,12 +6209,26 @@ msgstr "LisezMoi"
msgid "Real-time features"
msgstr "Fonctionnalités en temps réel"
+msgid "Recent searches"
+msgstr "Recherches récentes"
+
msgid "Reference:"
msgstr "Référence :"
msgid "Refresh"
msgstr "Actualiser"
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] "L’affichage sera réactualisé dans une seconde avec le statut mis à jour..."
+msgstr[1] "L’affichage sera réactualisé dans %d secondes avec le statut mis à jour…"
+
+msgid "Regenerate key"
+msgstr "Régénérer la clef"
+
+msgid "Regex pattern"
+msgstr "Expression rationnelle"
+
msgid "Register / Sign In"
msgstr "Inscription / Connexion"
@@ -5331,15 +6280,75 @@ msgstr "Supprimer la priorité"
msgid "Remove project"
msgstr "Supprimer le projet"
+msgid "Rename"
+msgstr "Renommer"
+
+msgid "Rename file"
+msgstr "Renommer le fichier"
+
+msgid "Rename folder"
+msgstr "Renommer le dossier"
+
+msgid "Reopen epic"
+msgstr "Rouvrir l’épopée"
+
msgid "Repair authentication"
msgstr "Réparer l’authentification"
msgid "Reply to this email directly or %{view_it_on_gitlab}."
-msgstr ""
+msgstr "Répondez directement à ce courriel ou %{view_it_on_gitlab}."
msgid "Repo by URL"
msgstr "Dépôt par URL"
+msgid "Reporting"
+msgstr "Rapports"
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr "%{failedString} et %{resolvedString}"
+
+msgid "Reports|Class"
+msgstr "Classe"
+
+msgid "Reports|Confidence"
+msgstr "Confiance"
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr "Rejeter la vulnérabilité"
+
+msgid "Reports|Execution time"
+msgstr "Durée d’exécution"
+
+msgid "Reports|Failure"
+msgstr "Échec"
+
+msgid "Reports|More info"
+msgstr "Plus d’informations"
+
+msgid "Reports|New Issue"
+msgstr "Nouveau ticket"
+
+msgid "Reports|Severity"
+msgstr "Sévérité"
+
+msgid "Reports|System output"
+msgstr "Sortie du système"
+
+msgid "Reports|Test summary"
+msgstr "Synthèse des tests"
+
+msgid "Reports|Test summary failed loading results"
+msgstr "Échec du chargement des résultats de la synthèse des tests"
+
+msgid "Reports|Test summary results are being parsed"
+msgstr "Les résultats de la synthèse des tests sont en cours d’analyse"
+
+msgid "Reports|Vulnerability"
+msgstr "Vulnérabilité"
+
+msgid "Reports|no changed test results"
+msgstr "aucun résultat de test modifié"
+
msgid "Repository"
msgstr "Dépôt"
@@ -5347,10 +6356,10 @@ msgid "Repository Settings"
msgstr "Paramètres du dépôt"
msgid "Repository URL"
-msgstr ""
+msgstr "URL du dépôt"
msgid "Repository has no locks."
-msgstr ""
+msgstr "Le dépôt n’a aucun verrou."
msgid "Repository maintenance"
msgstr "Maintenance du dépôt"
@@ -5362,13 +6371,13 @@ msgid "Repository storage"
msgstr "Stockage du dépôt"
msgid "RepositorySettingsAccessLevel|Select"
-msgstr ""
+msgstr "Sélectionner"
msgid "Request Access"
msgstr "Demander l’accès"
msgid "Requests Profiles"
-msgstr ""
+msgstr "Profils de requêtes"
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "Exiger que tous les utilisateurs acceptent les conditions générales d’utilisation et la politique de confidentialité quand ils accèdent à GitLab."
@@ -5391,8 +6400,20 @@ msgstr "Résoudre les conflits sur la branche source"
msgid "Resolve discussion"
msgstr "Résoudre la discussion"
+msgid "Response metrics (AWS ELB)"
+msgstr "Métriques de réponse (AWS ELB)"
+
msgid "Response metrics (Custom)"
-msgstr ""
+msgstr "Métriques de réponse (personnalisées)"
+
+msgid "Response metrics (HA Proxy)"
+msgstr "Métriques de réponse (HA Proxy)"
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr "Métriques de réponse (nginx Ingress)"
+
+msgid "Response metrics (NGINX)"
+msgstr "Métriques de réponse (nginx)"
msgid "Resume"
msgstr "Reprendre"
@@ -5406,6 +6427,9 @@ msgstr "Relancer cette tâche"
msgid "Retry verification"
msgstr "Relancer la vérification"
+msgid "Reveal Variables"
+msgstr "Révéler les variables"
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] "Révéler la valeur"
@@ -5421,7 +6445,7 @@ msgid "Review"
msgstr "Examiner"
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
-msgstr ""
+msgstr "Revoyez le processus de configuration des fournisseurs de service chez votre fournisseur d’identité — dans le cas présent, GitLab est le « fournisseur de service » ou le « tiers de confiance »."
msgid "Reviewing"
msgstr "Examen"
@@ -5430,17 +6454,35 @@ msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr "Examen (demande de fusion !%{mergeRequestId})"
msgid "Revoke"
-msgstr ""
+msgstr "Révoquer"
msgid "Roadmap"
msgstr "Feuille de route"
msgid "Run CI/CD pipelines for external repositories"
-msgstr ""
+msgstr "Exécuter des pipelines CI / CD pour les dépôts externes"
+
+msgid "Run untagged jobs"
+msgstr "Exécuter les tâches non étiquetées"
+
+msgid "Runner cannot be assigned to other projects"
+msgstr "L’exécuteur ne peut être affecté à d’autres projets"
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr "L’exécuteur exécute des tâches de tous les projets non attribués"
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr "L’exécuteur exécute des tâches de tous les projets non attribués de son groupe"
+
+msgid "Runner runs jobs from assigned projects"
+msgstr "L’exécuteur exécute des tâches de projets attribués"
msgid "Runner token"
msgstr "Jeton de l’exécuteur"
+msgid "Runner will not receive any new jobs"
+msgstr "L’exécuteur ne recevra aucune nouvelle tâche"
+
msgid "Runners"
msgstr "Exécuteurs"
@@ -5450,27 +6492,51 @@ msgstr "API des exécuteurs"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr "Les exécuteurs peuvent être placés sur différents utilisateurs et serveurs, voire sur votre machine locale."
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr "Les exécuteurs peuvent fonctionner sur différents serveurs, avec différents comptes d’utilisateur, y compris sur votre machine locale."
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr "Exécuteurs actuellement en ligne : %{active_runners_count}"
+
+msgid "Runners page"
+msgstr "Page des exécuteurs"
+
+msgid "Runners page."
+msgstr "Page des exécuteurs."
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr "Vous avez utilisé tout le temps de pipeline partagé de vos exécuteurs."
+
msgid "Running"
msgstr "En cours d’exécution"
msgid "SAML SSO"
-msgstr ""
+msgstr "Authentification unique SAML"
msgid "SAML SSO for %{group_name}"
-msgstr ""
+msgstr "Authentification unique SAML pour %{group_name}"
msgid "SAML Single Sign On"
-msgstr ""
+msgstr "Authentification unique SAML"
msgid "SAML Single Sign On Settings"
-msgstr ""
+msgstr "Paramètres d’authentification unique SAML"
+
+msgid "SAST"
+msgstr "SAST"
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
-msgstr ""
+msgstr "Empreinte SHA-1 du certificat de signature de jetons SAML. Obtenezâ€la auprès de votre fournisseur d’identité, parfois sous la dénomination « Thumbprint »."
msgid "SSH Keys"
msgstr "Clefs SSH"
+msgid "SSH host keys"
+msgstr "Clefs SSH de l’hôte"
+
+msgid "SSH public key"
+msgstr "Clef SSH publique"
+
msgid "SSL Verification"
msgstr "Vérification SSL"
@@ -5478,7 +6544,7 @@ msgid "Save"
msgstr "Enregistrer"
msgid "Save application"
-msgstr ""
+msgstr "Enregistrer l’application"
msgid "Save changes"
msgstr "Enregistrer les modifications"
@@ -5502,13 +6568,13 @@ msgid "Scheduling Pipelines"
msgstr "Planification des pipelines"
msgid "Scope"
-msgstr ""
+msgstr "Portée"
msgid "Scoped issue boards"
-msgstr ""
+msgstr "Tableaux de tickets à portée limitée"
msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
-msgstr ""
+msgstr "Faites défiler jusqu’à <strong>Hébergement de projet Google Code</strong> et activez le commutateur sur la droite."
msgid "Scroll to bottom"
msgstr "Faire défiler vers le bas"
@@ -5537,11 +6603,41 @@ msgstr "Rechercher des demandes de fusion"
msgid "Search milestones"
msgstr "Rechercher des jalons"
+msgid "Search or filter results..."
+msgstr "Rechercher ou filtrer les résultats…"
+
+msgid "Search or jump to…"
+msgstr "Rechercher ou aller à…"
+
msgid "Search project"
msgstr "Rechercher des projets"
msgid "Search users"
-msgstr "Rechercher des utilisa·teur·trice·s"
+msgstr "Rechercher des utilisateurs et utilisatrices"
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr "Dans tout GitLab"
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr "Les tickets que j’ai créés"
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr "Les tickets qui me sont assignés"
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr "Demandes de fusion que j’ai créées"
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr "Demandes de fusion qui me sont assignées"
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr "Dans tout GitLab"
+
+msgid "SearchAutocomplete|in this group"
+msgstr "Dans ce groupe"
+
+msgid "SearchAutocomplete|in this project"
+msgstr "Dans ce projet"
msgid "Seconds before reseting failure information"
msgstr "Nombre de secondes avant réinitialisation des informations d’échec"
@@ -5550,19 +6646,22 @@ msgid "Seconds to wait for a storage access attempt"
msgstr "Nombre de secondes d’attente avant une tentative d’accès au stockage"
msgid "Secret:"
-msgstr ""
+msgstr "Secret :"
+
+msgid "Security"
+msgstr "Sécurité"
msgid "Security Dashboard"
-msgstr ""
+msgstr "Tableau de bord de sécurité"
-msgid "Security report"
-msgstr ""
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr "Le tableau de bord de sécurité affiche le dernier rapport de sécurité. Utilisezâ€le pour rechercher et corriger les vulnérabilités."
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
-msgstr ""
+msgstr "Surveiller les vulnérabilités dans votre code"
msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
-msgstr ""
+msgstr "Pipeline %{pipelineLink} déclenché"
msgid "Select"
msgstr "Sélectionner"
@@ -5570,6 +6669,9 @@ msgstr "Sélectionner"
msgid "Select Archive Format"
msgstr "Sélectionnez le format de l’archive"
+msgid "Select a group to invite"
+msgstr "Sélectionnez un groupe à inviter"
+
msgid "Select a namespace to fork the project"
msgstr "Sélectionnez un espace de noms afin de créer une divergence du projet"
@@ -5595,7 +6697,7 @@ msgid "Select project to choose zone"
msgstr "Sélectionnez le projet afin de choisir la zone"
msgid "Select projects you want to import."
-msgstr ""
+msgstr "Sélectionnez les projets que vous souhaitez importer."
msgid "Select source branch"
msgstr "Sélectionner une branche source"
@@ -5603,15 +6705,24 @@ msgstr "Sélectionner une branche source"
msgid "Select target branch"
msgstr "Sélectionner une branche cible"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr "Sélectionnez la branche que vous souhaitez définir comme branche par défaut pour ce projet. Toutes les demandes de fusion et les commits seront automatiquement effectués sur cette branche, à moins que vous n’en spécifiez une autre."
+
+msgid "Select the custom project template source group."
+msgstr "Sélectionnez le groupe source de modèles de projet personnalisés."
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
-msgstr ""
+msgstr "La sélection d’un utilisateur de GitLab va ajouter un lien vers cet utilisateur dans les descriptions des tickets et des commentaires (p. ex., « Par <a href=\"#\">@johnsmith</a> »). Les tickets et commentaires seront également associés ou assignés à cet utilisateur."
msgid "Selective synchronization"
-msgstr ""
+msgstr "Synchronisation sélective"
msgid "Send email"
msgstr "Envoyer un courriel"
+msgid "Send usage data"
+msgstr "Envoyer des données d’utilisation"
+
msgid "Sep"
msgstr "sept."
@@ -5622,13 +6733,13 @@ msgid "Server version"
msgstr "Version du serveur"
msgid "Service Desk"
-msgstr ""
+msgstr "Service d’assistance"
msgid "Service Templates"
msgstr "Modèles de service"
msgid "Service URL"
-msgstr ""
+msgstr "URL du service"
msgid "Session expiration, projects limit and attachment size."
msgstr "Expiration de la session, restrictions des projets et taille des pièces jointes."
@@ -5639,6 +6750,9 @@ msgstr "Définissez un mot de passe pour votre compte afin de pouvoir récupére
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr "Définissez les valeurs par défaut et restreignez les niveaux de visibilité. Configurez les sources d’importation et le protocole d’accès pour Git."
+msgid "Set instance-wide template repository"
+msgstr "Définir un dépôt de modèles au niveau de l’instance"
+
msgid "Set max session time for web terminal."
msgstr "Définissez le temps maximal de la session pour le terminal Web."
@@ -5651,11 +6765,17 @@ msgstr "Définissez les exigences pour la connexion d’un utilisateur. Activez
msgid "Set up CI/CD"
msgstr "Configuration CI/CD"
-msgid "Set up Koding"
-msgstr "Configurer Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr "Configurer manuellement un exécuteur %{type}"
+
+msgid "Set up a specific Runner automatically"
+msgstr "Configurer automatiquement un exécuteur spécifique"
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
-msgstr ""
+msgstr "Configure les assertions, attributs et revendications (courriel, prénom et nom), ainsi que le NameID, conformément à %{docsLinkStart}la documentation %{icon}%{docsLinkEnd}"
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr "Configurez votre projet afin de pouvoir pousser et/ou récupérer automatiquement les modifications vers ou depuis un autre dépôt. Les branches, les étiquetets et les commits seront automatiquement synchronisés."
msgid "SetPasswordToCloneLink|set a password"
msgstr "définir un mot de passe"
@@ -5663,29 +6783,29 @@ msgstr "définir un mot de passe"
msgid "Settings"
msgstr "Paramètres"
-msgid "Setup a specific Runner automatically"
-msgstr "Configurer automatiquement un exécuteur spécifique"
-
msgid "Share"
msgstr "Partager"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
-msgstr ""
+msgstr "Partager le <strong>%{sso_label}</strong> avec les membres afin qu’ils puissent se connecter à votre groupe via votre fournisseur d’identité"
msgid "Shared Runners"
msgstr "Exécuteurs partagés"
+msgid "Shared projects"
+msgstr "Projets partagés"
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
-msgstr ""
+msgstr "En réinitialisant le compteur de minutes du pipeline pour cet espace de noms, les minutes actuellement utilisées seront mises à zéro."
msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
-msgstr ""
+msgstr "Réinitialiser le compteur de minutes du pipeline"
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
-msgstr ""
+msgstr "Réinitialiser les minutes du pipeline utilisées"
msgid "Sherlock Transactions"
-msgstr ""
+msgstr "Transactions Sherlock"
msgid "Show command"
msgstr "Afficher la commande"
@@ -5717,28 +6837,28 @@ msgid "Side-by-side"
msgstr "côte à côte"
msgid "Sidebar|Change weight"
-msgstr ""
+msgstr "Changer le poids"
msgid "Sidebar|None"
msgstr ""
msgid "Sidebar|Only numeral characters allowed"
-msgstr ""
+msgstr "Seuls les caractères numériques sont autorisés"
msgid "Sidebar|Weight"
-msgstr ""
+msgstr "Poids"
msgid "Sign in"
-msgstr ""
+msgstr "Connexion"
msgid "Sign in / Register"
-msgstr ""
+msgstr "Connexion / Inscription"
msgid "Sign in to %{group_name}"
-msgstr ""
+msgstr "Se connecter au groupe %{group_name}"
msgid "Sign in with Single Sign-On"
-msgstr ""
+msgstr "Se connecter avec une authentification unique"
msgid "Sign out"
msgstr "Se déconnecter"
@@ -5749,11 +6869,17 @@ msgstr "Restrictions de connexion"
msgid "Sign-up restrictions"
msgstr "Restrictions d’inscription"
+msgid "Size"
+msgstr "Taille"
+
msgid "Size and domain settings for static websites"
msgstr "Paramètres de taille et de domaine pour les sites Web statiques"
msgid "Slack application"
-msgstr ""
+msgstr "Application Slack"
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr "L’intégration de Slack permet d’interagir avec GitLab via des commandes slash dans une fenêtre de messagerie instantanée."
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr "Plus lent, mais permet de s’assurer que l’espace de travail du projet est vierge, comme il clone le dépôt à partir de zéro pour chaque tâche"
@@ -5770,17 +6896,23 @@ msgstr "Une erreur est survenue de notre côté."
msgid "Something went wrong on our end. Please try again!"
msgstr "Quelque chose s’est mal passé de notre côté. Veuillez réessayer."
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr "Une erreur est survenue lors de la tentative de modification de la confidentialité de ce ticket"
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr "Une erreur est survenue lors de la tentative de modification de l’état de verrouillage de ce·t·te %{issuableDisplayName}"
+
msgid "Something went wrong when toggling the button"
msgstr "Une erreur s’est produite lors du basculement du bouton"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr "Une erreur s’est produite lors de la fermeture du / de la %{issuable}. Veuillez réessayer plus tard"
-msgid "Something went wrong while fetching assignees list"
-msgstr ""
+msgid "Something went wrong while fetching %{listType} list"
+msgstr "Une erreur est survenue lors de la récupération de la liste de %{listType}"
msgid "Something went wrong while fetching group member contributions"
-msgstr ""
+msgstr "Une erreur s’est produite lors de la récupération des contributions des membres du groupe"
msgid "Something went wrong while fetching the projects."
msgstr "Une erreur s’est produite lors de la récupération des projets."
@@ -5798,7 +6930,7 @@ msgid "Something went wrong. Please try again."
msgstr "Quelque chose s’est mal passé. Veuillez réessayer."
msgid "Sorry, no epics matched your search"
-msgstr ""
+msgstr "Désolé, aucune épopée ne correspond à votre recherche"
msgid "Sort by"
msgstr "Trier par"
@@ -5830,6 +6962,9 @@ msgstr "Taille de groupe"
msgid "SortOptions|Largest repository"
msgstr "Taille de dépôt"
+msgid "SortOptions|Last Contact"
+msgstr "Contact le plus récent"
+
msgid "SortOptions|Last created"
msgstr "Créé récemment"
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr "Popularité décroissante"
+msgid "SortOptions|Most stars"
+msgstr "Avec le plus d’étoiles"
+
msgid "SortOptions|Name"
msgstr "Nom"
@@ -5890,6 +7028,9 @@ msgstr "Priorité"
msgid "SortOptions|Recent sign in"
msgstr "Date d’inscription décroissante"
+msgid "SortOptions|Start date"
+msgstr "Date de début"
+
msgid "SortOptions|Start later"
msgstr "Commence plus tard"
@@ -5897,7 +7038,7 @@ msgid "SortOptions|Start soon"
msgstr "Commence bientôt"
msgid "SortOptions|Weight"
-msgstr ""
+msgstr "Poids"
msgid "Source"
msgstr "Source"
@@ -5920,6 +7061,9 @@ msgstr "Protection antiâ€pourriel et antiâ€robot"
msgid "Specific Runners"
msgstr "Exécuteurs spécifiques"
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr "Spécifiez un motif d’expression rationnelle permettant l’identification des adresses de courriel des utilisateurs internes."
+
msgid "Specify the following URL during the Runner setup:"
msgstr "Spécifiez l’URL suivante lors de la configuration de l’exécuteur :"
@@ -5930,7 +7074,7 @@ msgid "Stage"
msgstr "Étape"
msgid "Stage & Commit"
-msgstr ""
+msgstr "Étape & commit"
msgid "Stage all changes"
msgstr "Marquer toutes les modifications comme une étape"
@@ -5945,7 +7089,7 @@ msgid "Staged %{type}"
msgstr "%{type} en étape"
msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Mettez une étoile sur une étiquette pour en faire une étiquette prioritaire. Ordonnez les étiquettes prioritaires pour changer leurs priorités relatives en les glissant."
+msgstr "Mettez une étoile sur une étiquette pour en faire une étiquette prioritaire. Ordonnez les étiquettes prioritaires pour changer leurs priorités relatives en les faisant glisser."
msgid "StarProject|Star"
msgstr "Mettre en favori"
@@ -5962,6 +7106,9 @@ msgstr "Projets favoris"
msgid "Start a %{new_merge_request} with these changes"
msgstr "Créer une %{new_merge_request} avec ces changements"
+msgid "Start date"
+msgstr "Date de début"
+
msgid "Start the Runner!"
msgstr "Démarrer l’exécuteur !"
@@ -5972,13 +7119,13 @@ msgid "Starts at (UTC)"
msgstr "Démarre à (UTC)"
msgid "State your message to activate"
-msgstr ""
+msgstr "Énoncez votre message à activer"
msgid "Status"
msgstr "État "
msgid "Stop impersonation"
-msgstr ""
+msgstr "Arrêter l’emprunt d’identité"
msgid "Stop this environment"
msgstr "Arrêter cet environnement"
@@ -5990,16 +7137,19 @@ msgid "Storage"
msgstr "Stockage"
msgid "Storage:"
-msgstr ""
+msgstr "Stockage :"
msgid "Subgroups"
msgstr "Sousâ€groupes"
+msgid "Subgroups and projects"
+msgstr "Sousâ€groupes et projets"
+
msgid "Submit as spam"
msgstr "Soumettre comme indésirable"
msgid "Submit search"
-msgstr ""
+msgstr "Soumettre la recherche"
msgid "Subscribe"
msgstr "S’abonner"
@@ -6017,16 +7167,19 @@ msgid "Sync information"
msgstr "Synchroniser les informations"
msgid "System Hooks"
-msgstr "« Hooks » système"
+msgstr "« Hooks » système"
msgid "System Info"
-msgstr ""
+msgstr "Informations système"
msgid "System header and footer:"
-msgstr ""
+msgstr "Enâ€tête et pied de page du système :"
msgid "System metrics (Custom)"
-msgstr ""
+msgstr "Métriques du système (personnalisées)"
+
+msgid "System metrics (Kubernetes)"
+msgstr "Métriques du système (Kubernetes)"
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
@@ -6036,6 +7189,9 @@ msgstr[1] "Étiquettes (%{tag_count})"
msgid "Tags"
msgstr "Étiquettes"
+msgid "Tags feed"
+msgstr "Flux d’étiquettes"
+
msgid "Tags:"
msgstr "Étiquettes :"
@@ -6117,6 +7273,12 @@ msgstr "Branche cible"
msgid "Team"
msgstr "Équipe"
+msgid "Template"
+msgstr "Modèle"
+
+msgid "Templates"
+msgstr "Modèles "
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr "Conditions générales d’utilisation et politique de confidentialité"
@@ -6124,13 +7286,16 @@ msgid "Terms of Service and Privacy Policy"
msgstr "Conditions générales d’utilisation et politique de confidentialité"
msgid "Test coverage parsing"
-msgstr ""
+msgstr "Analyse de la couverture des tests"
msgid "Thanks! Don't show me this again"
-msgstr ""
+msgstr "Merci ! Ne plus afficher ce message"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
-msgstr ""
+msgstr "La recherche globale avancée de Gitlab est un outil puissant qui vous fait gagner du temps. Au lieu de perdre du temps à recréer du code existant, vous pouvez maintenant faire des recherches dans le code d’autres équipes afin de vous aider sur votre projet."
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr "Les objets Git LFS <strong>ne sont pas</strong> synchronisés."
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr "Le système de suivi est un endroit où l’on peut ouvrir un ticket pour signaler des choses à améliorer ou des dysfonctionnements à résoudre dans un projet"
@@ -6139,7 +7304,10 @@ msgid "The Issue Tracker is the place to add things that need to be improved or
msgstr "Le système de suivi est un endroit où l’on peut ouvrir un ticket pour signaler des choses à améliorer ou des dysfonctionnements à résoudre dans un projet. Vous pouvez vous inscrire ou vous connecter pour créer des tickets de suivi pour ce projet."
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
-msgstr ""
+msgstr "Le certificat X.509 à utiliser lorsque l’authentification TLS mutuelle est requise pour communiquer avec le service d’autorisation externe. Si ce champ est vide, le certificat du serveur est tout de même validé lors de l’accès via HTTPS."
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr "Le surligneur de caractères vous aide à garder la longueur de l’objet à %{titleLength} caractères maximum et à faire des renvois à la ligne pour limiter les lignes du corps du message à %{bodyLength} caractères, afin de les rendre lisibles sous Git."
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "Le présentoir de code affiche le temps entre le premier commit et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."
@@ -6148,7 +7316,10 @@ msgid "The collection of events added to the data gathered for that stage."
msgstr "L’ensemble d’événements ajoutés aux données recueillies pour cette étape."
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr ""
+msgstr "La connexion expirera après %{timeout}. Pour les dépôts qui nécessitent plus de temps, utilisez une combinaison de clone et push."
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr "Le déploiement de cette tâche sur %{environmentLink} a échoué."
msgid "The fork relationship has been removed."
msgstr "La relation de divergence a été supprimée."
@@ -6166,10 +7337,10 @@ msgid "The number of attempts GitLab will make to access a storage."
msgstr "Le nombre de tentatives que GitLab va effectuer pour accéder à un stockage."
msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr ""
+msgstr "Nombre d’échecs avant que GitLab n’empêche tout accès au stockage. Ce nombre d’échecs peut être réinitialisé dans l’interface d’administration : %{link_to_health_page} ou en suivant la %{api_documentation_link}."
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
-msgstr ""
+msgstr "La phrase de passe permettant de déchiffrer la clef privée. Ceci est facultatif et la valeur est chiffrée au repos."
msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
msgstr "Le chemin d’accès au fichier de configuration de l’intégration continue. Par défaut, <code>.gitlab-ci.yml</code>"
@@ -6177,23 +7348,26 @@ msgstr "Le chemin d’accès au fichier de configuration de l’intégration con
msgid "The phase of the development lifecycle."
msgstr "La phase du cycle de développement."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr "La planification des pipelines permet l’exécution de pipelines programmés, de manière récurrente, pour des branches ou des étiquettes spécifiques. Ces pipelines programmés hériteront d’un accès limité aux projets en fonction de l’utilisateur qui leur est associé."
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "L’étape de planification montre le temps entre l’étape précédente et l’envoi de votre premier commit. Ce temps sera automatiquement ajouté quand vous pousserez votre premier commit."
msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
-msgstr ""
+msgstr "La clef privée à utiliser lorsqu’un certificat client est fourni. Cette valeur est chiffrée au repos."
msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
msgstr "L’étape de mise en production montre le temps nécessaire entre la création d’un ticket et le déploiement du code en production. Les données seront automatiquement ajoutées une fois que vous aurez complété le cycle complet, depuis l’idée jusqu’à la mise en production."
msgid "The project can be accessed by any logged in user."
-msgstr "Votre projet est accessible à n’importe quel·le utilisa·teur·trice authentifié·e."
+msgstr "Votre projet est accessible à tous les utilisateur et utilisatrices authentifiés."
msgid "The project can be accessed without any authentication."
msgstr "Votre projet est accessible sans aucune authentification."
msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
-msgstr ""
+msgstr "La collecte de données du pseudonymiseur est désactivée. Lorsque cette option est activée, GitLab exécute un travail en tâche de fond produisant des fichiers CSV pseudonymisés à partir de la base de données GitLab qui seront téléchargés dans le répertoire de stockage d’objets que vous avez configuré."
msgid "The repository for this project does not exist."
msgstr "Le dépôt de ce projet n’existe pas."
@@ -6204,11 +7378,14 @@ msgstr "Le dépôt de ce projet est vide"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr "Le dépôt doit être accessible via <code>http://</code>, <code>https://</code> ou <code>git://</code>."
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr "Le dépôt doit être accessible via <code>http://</code>, <code>https://</code>, <code>ssh://</code> ou <code>git://</code>."
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "L’étape d’évaluation montre le temps entre la création de la demande de fusion et la fusion effective de celleâ€ci. Ces données seront automatiquement ajoutées après que vous aurez fusionné votre première demande de fusion."
msgid "The roadmap shows the progress of your epics along a timeline"
-msgstr ""
+msgstr "La feuille de route affiche la progression de vos épopées dans le temps"
msgid "The secure token used by the Runner to checkout the project"
msgstr "Le jeton sécurisé utilisé par l’exécuteur pour vérifier (checkout) le projet"
@@ -6216,6 +7393,9 @@ msgstr "Le jeton sécurisé utilisé par l’exécuteur pour vérifier (checkout
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "L’étape de pré-production indique le temps entre l’acceptation d’une demande fusion et le déploiement du code dans l’environnent de production. Les données seront automatiquement ajoutées lorsque vous aurez fait votre première mise en production."
+msgid "The tabs below will be removed in a future version"
+msgstr "Les onglets ciâ€dessous seront supprimés dans une prochaine version"
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "L’étape de test montre le temps que que met l’intégration continue de GitLab pour exécuter chaque pipeline pour une demande de fusion donnée. Les données seront automatiquement ajoutées après que votre premier pipeline s’achèvera."
@@ -6226,20 +7406,29 @@ msgid "The time in seconds GitLab will try to access storage. After this time a
msgstr "Temps en secondes pendant lequel GitLab essaiera d’accéder au stockage. Après ce délai, une erreur d’expiration du délai d’attente sera déclenchée."
msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
-msgstr ""
+msgstr "Intervalle entre deux vérifications du stockage, en secondes. Si une précédente vérification n’est pas encore terminée lorsqu’une nouvelle doit commencer, cette dernière est sautée."
msgid "The time taken by each data entry gathered by that stage."
msgstr "Le temps pris par chaque entrée récoltée durant cette étape."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr "L’action de mise à jour expirera au bout de %{number_of_minutes} minutes. Pour les gros dépôts, utilisez une combinaison de clone et push."
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr "La collecte des données d’utilisation est désactivée et ne peut pas être configurée par le biais de ce formulaire."
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
+msgstr "La carte des utilisateurs (<code>user map</code>) est un document JSON qui met en correspondance les utilisateurs de Google Code qui ont participé à vos projets en précisant la manière dont leurs adresses de courriel et leurs noms d’utilisateur sont importés dans GitLab. Vous pouvez y apporter des modifications en changeant la valeur à droite du « <code>:</code> ». Assurezâ€vous de conserver les guillemets droits doubles (<code>\"</code>), les autres signes de ponctuation, ainsi que l’adresse de courriel ou le nom d’utilisateur à gauche du deuxâ€points."
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
-msgstr ""
+msgstr "La carte des utilisateurs met en correspondance les utilisateurs de FogBugz qui ont participé à vos projets en précisant la manière dont leurs adresses de courriel et leurs noms d’utilisateur sont importés dans GitLab. Vous pouvez y apporter des modifications en remplissant le tableau ciâ€dessous."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "La valeur située au point médian d’une série de valeur observée. Par exemple., entre 3, 5 et 9, le médian est 5. Entre 3, 5, 7 et 8, le médian est (5+7)/2 = 6."
+msgid "There are no archived projects yet"
+msgstr "Il n’y a pas encore de projets archivés"
+
msgid "There are no issues to show"
msgstr "Il n’y a aucun ticket à afficher"
@@ -6249,9 +7438,24 @@ msgstr "Il n’y a pas encore d’étiquette"
msgid "There are no merge requests to show"
msgstr "Il n’y a aucune demande de fusion à afficher"
+msgid "There are no projects shared with this group yet"
+msgstr "Il n’y a pas encore de projets partagés avec ce groupe"
+
+msgid "There are no staged changes"
+msgstr "Il n’y a aucune modification indexée"
+
+msgid "There are no unstaged changes"
+msgstr "Il n’y a aucune modification non indexée"
+
msgid "There are problems accessing Git storage: "
msgstr "Il y a des difficultés à accéder aux données Git : "
+msgid "There was an error adding a todo."
+msgstr "Une erreur est survenue lors de l’ajout d’une tâche à accomplir (todo)."
+
+msgid "There was an error deleting the todo."
+msgstr "Une erreur est survenue lors de la suppression de la tâche à accomplir (todo)."
+
msgid "There was an error loading users activity calendar."
msgstr "Une erreur s’est produite lors du chargement du calendrier d’activité des utilisateurs."
@@ -6274,19 +7478,31 @@ msgid "They can be managed using the %{link}."
msgstr "Ils peuvent être gérés en utilisant %{link}."
msgid "Third party offers"
-msgstr ""
+msgstr "Offres tierces"
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr "Cette instance de GitLab ne fournit aucun exécuteur partagé pour le moment. Les administra·teur·trice·s de l’instance peuvent enregistrer des exécuteurs partagés dans la zone d’administration."
msgid "This application was created by %{link_to_owner}."
-msgstr ""
+msgstr "Cette application a été créée par %{link_to_owner}."
msgid "This application will be able to:"
-msgstr ""
+msgstr "Cette application sera en mesure de :"
msgid "This board's scope is reduced"
-msgstr ""
+msgstr "La portée de ce tableau est réduite"
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr "Cette branche a changé depuis que vous y avez apporté des modifications. Souhaitezâ€vous créer une nouvelle branche ?"
+
+msgid "This container registry has been scheduled for deletion."
+msgstr "Ce registre de conteneur a été programmé pour suppression."
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr "Cette date est postérieure à la date d’échéance, cette épopée n’apparaîtra donc pas dans la feuille de route."
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr "Cette date est antérieure à la date de début, cette épopée n’apparaîtra donc pas dans la feuille de route."
msgid "This diff is collapsed."
msgstr "Ce diff est replié."
@@ -6295,10 +7511,10 @@ msgid "This directory"
msgstr "Ce répertoire"
msgid "This group"
-msgstr ""
+msgstr "Ce groupe"
msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
-msgstr ""
+msgstr "Ce groupe vous autorise à vous connecter avec votre compte d’authentification unique %{group_name}. Vous serez alors redirigé vers une page d’authentification externe."
msgid "This group does not provide any group Runners yet."
msgstr "Ce groupe ne fournit pas encore d’exécuteurs de groupe."
@@ -6319,7 +7535,7 @@ msgid "This issue is locked."
msgstr "Ce ticket est verrouillé."
msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
-msgstr "Cette tâche dépend de l’utilisa·teur·trice pour déclencher son processus. Elles sont souvent utilisées pour déployer du code dans des environnements de production"
+msgstr "Cette tâche dépend de l’utilisateur ou l’utilisatrice pour déclencher son processus. Elles sont souvent utilisées pour déployer du code dans des environnements de production"
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr "Cette tâche dépend des tâches en amont qui doivent réussir pour que celleâ€ci soit déclenchée"
@@ -6339,9 +7555,30 @@ msgstr "Cette tâche n’a pas encore été déclenchée"
msgid "This job has not started yet"
msgstr "Cette tâche n’a pas encore commencée"
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr "Cette tâche est un déploiement obsolète sur %{environmentLink}."
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr "Cette tâche est un déploiement obsolète sur %{environmentLink}. Afficher le déploiement le plus récent %{deploymentLink}."
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr "Cette tâche va effectuer un déploiement sur %{environmentLink} et écrasera le dernier %{deploymentLink}."
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr "Cette tâche va effectuer un déploiement sur %{environmentLink}."
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "Cette tâche est en attente d’être choisie par un exécuteur"
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr "Cette tâche est bloquée parce que vous n’avez aucun exécuteur actif en ligne auquel l’une des étiquettes suivantes est assignée :"
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr "Cette tâche est bloquée parce que vous n’avez aucun exécuteur actif pouvant la prendre en charge."
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr "Cette tâche est le déploiement le plus récent sur %{link}."
+
msgid "This job requires a manual action"
msgstr "Cette tâche nécessite une action manuelle"
@@ -6351,6 +7588,9 @@ msgstr "Cela signifie que vous ne pouvez pas pousser du code tant que vous n’a
msgid "This merge request is locked."
msgstr "Cette demande de fusion est verrouillée."
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr "Cette option est désactivée car vous n’avez pas les droits d’écriture sur la branche actuelle"
+
msgid "This option is disabled while you still have unstaged changes"
msgstr "Cette option est désactivée tant que vous avez des changements non indexés"
@@ -6366,20 +7606,35 @@ msgstr "Ce projet"
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr "Ce projet n’appartient pas à un groupe et ne peut donc pas faire appel à un exécuteur de groupe."
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr "Ce projet n’a pas de facturation activée. Afin de créer une grappe de serveurs, veuillez <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">activer la facturation<i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> et réessayer."
+
msgid "This repository"
msgstr "Ce dépôt"
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr "Cet exécuteur ne fonctionnera que sur les pipelines déclenchés sur des branches protégées"
+
msgid "This source diff could not be displayed because it is too large."
msgstr "Ce diff n’a pas pu être affiché car il est trop grand."
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr "Ce délai d’attente aura préséance lorsqu’il est inférieur au délai d’attente défini pour le projet"
+
msgid "This user has no identities"
-msgstr "Cet utilisa·teur·trice n’a aucune identité"
+msgstr "Cet utilisateur ou cette utilisatrice n’a aucune identité"
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr "Cet utilisateur sera l’auteur de tous les événements du flux d’activité résultant d’une mise à jour, comme la création de nouvelles branches ou les nouveaux commits poussés vers des branches existantes."
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr "Cet utilisateur sera l’auteur de tous les événements du flux d’activité résultant d’une mise à jour, comme la création de nouvelles branches ou les nouveaux commits poussés vers des branches existantes. Lors de la création ou lors de la réaffectation, vous ne pouvez assigner que vousâ€même comme utilisateur du miroir."
msgid "This will delete the custom metric, Are you sure?"
-msgstr ""
+msgstr "Ceci va supprimer la métrique personnalisée. Êtesâ€vous sûr(e) ?"
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr ""
+msgstr "Ces courriels deviennent automatiquement des tickets (dont les commentaires résultent de la conversation par courriel) répertoriés ici."
msgid "Time before an issue gets scheduled"
msgstr "Temps avant qu’un ticket ne soit planifié"
@@ -6391,7 +7646,7 @@ msgid "Time between merge request creation and merge/close"
msgstr "Temps entre la création d’une demande de fusion et sa fusion/clôture"
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
-msgstr ""
+msgstr "Délai d’attente, en secondes, d’une réponse du service externe. Lorsque le service ne répond pas à temps, l’accès sera refusé."
msgid "Time remaining"
msgstr "Temps restant"
@@ -6569,19 +7824,31 @@ msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_en
msgstr "Afin d’ajouter une clef SSH, vous devez soit %{generate_link_start}en génèrer une%{link_end}, soit utiliser une %{existing_link_start}clef existante%{link_end}."
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr ""
+msgstr "Pour connecter des dépôts GitHub, vous pouvez utiliser un %{personal_access_token_link}. Lorsque vous créerez votre jeton d’accès, vous devrez sélectionner la portée <code>dépôt</code>, afin de permettre l’affichage d’une liste de vos dépôts publics et privés disponibles à la connexion."
msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
-msgstr ""
+msgstr "Afin de connecter des dépôts GitHub, vous devez d’abord autoriser GitLab à accéder à la liste de vos dépôts GitHub :"
msgid "To connect an SVN repository, check out %{svn_link}."
-msgstr ""
+msgstr "Pour connecter un dépôt SVN, veuillez consulter %{svn_link}."
+
+msgid "To define internal users, first enable new users set to external"
+msgstr "Afin de définir les utilisateurs internes, veuillez d’abord activer les nouveaux utilisateurs définis comme externes"
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr "Afin d’activer et d’afficher les cohortes d’utilisateurs, consultez les %{application_settings_link_start}paramètres de l’application%{application_settings_link_end}."
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
-msgstr ""
+msgstr "Pour commencer, entrez votre URL FogBugz et vos informations de connexion ciâ€dessous. Dans les étapes suivantes, vous pourrez mettre en correspondance les comptes des utilisateurs et sélectionner les projets à importer."
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
-msgstr ""
+msgstr "Pour commencer, entrez l’URL de votre hôte Gitea et un %{link_to_personal_token}."
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr "Afin d’aider à améliorer GitLab et son expérience utilisateur, GitLab va recueillir périodiquement des informations sur son utilisation."
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr "Afin d’aider à améliorer GitLab, nous aimerions recueillir périodiquement des informations sur son utilisation. Vous pourrez modifier ceci à tout moment dans les %{settings_link_start}paramètres%{link_end}. %{info_link_start}Plus d’informations…%{link_end}"
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "Pour importer les dépôts GitHub, vous pouvez utiliser un %{personal_access_token_link}. Lorsque vous créez votre jeton d’accès, vous devrez sélectionner le champ <code>repo</code>, afin que nous puissions afficher une liste de vos dépôts publics et privés qui sont disponibles pour être importés."
@@ -6593,43 +7860,46 @@ msgid "To import an SVN repository, check out %{svn_link}."
msgstr "Pour importer un dépôt SVN, consultez %{svn_link}."
msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
-msgstr ""
+msgstr "Pour déplacer ou copier un projet GitLab entier depuis une autre installation de GitLab vers celleâ€ci, accédez à la page des paramètres du projet d’origine, générez un fichier d’exportation et téléversezâ€le ici."
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
-msgstr ""
+msgstr "Pour n’utiliser uniquement que les fonctionnalités d’intégration et livraison continues (CI / CD) pour un dépôt externe, choisissez <strong>Intégration et livraison continues (CI / CD) pour dépôt externe</strong>."
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
-msgstr ""
+msgstr "Pour configurer l’authentification SAML pour votre groupe via un fournisseur d’identité tel qu’Azure, Okta, Onelogin, Ping Identity ou votre fournisseur SAML 2.0 personnalisé :"
msgid "To start serving your jobs you can add Runners to your group"
msgstr "Pour commencer à exécuter vos tâches, vous pouvez ajouter des exécuteurs à votre groupe"
msgid "To this GitLab instance"
-msgstr ""
+msgstr "À cette instance de GitLab"
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
-msgstr "Pour valider vos configurations GitLab CI, allez dans 'CI / CD → Pipelines' dans votre projet, et cliquez sur le bouton 'CI Lint'."
+msgstr "Pour valider vos configurations GitLab CI, allez dans « CI / CD » → « Pipelines » dans votre projet, et cliquez sur le bouton « CI Lint »."
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
-msgstr ""
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr "Afin d’afficher la feuille de route, ajoutez une date de début ou d’échéance à l’une de vos épopées dans ce groupe ou ses sousâ€groupes. Dans la vue multiâ€mensuelle, seules les épopées du mois dernier, du mois courant et des cinq prochains mois sont affichées."
msgid "To widen your search, change or remove filters."
-msgstr ""
+msgstr "Afin d’élargir votre recherche, modifiez ou supprimez des filtres."
msgid "Todo"
msgstr "Tâche"
msgid "Todos"
-msgstr ""
+msgstr "À faire"
msgid "Toggle Sidebar"
msgstr "Afficher/masquer la barre latérale"
+msgid "Toggle commit description"
+msgstr "Afficher ou masquer la description du commit"
+
msgid "Toggle discussion"
msgstr "Basculer la discussion"
msgid "Toggle navigation"
-msgstr ""
+msgstr "Activer/désactiver la navigation"
msgid "Toggle sidebar"
msgstr "Afficher/masquer la barre latérale"
@@ -6640,11 +7910,14 @@ msgstr "État du commutateur : Inactif"
msgid "ToggleButton|Toggle Status: ON"
msgstr "État du commutateur : Actif"
+msgid "Token"
+msgstr "Jeton"
+
msgid "Too many changes to show."
msgstr "Trop de changements à afficher."
msgid "Total Contributions"
-msgstr ""
+msgstr "Total des contributions"
msgid "Total Time"
msgstr "Temps total"
@@ -6656,16 +7929,25 @@ msgid "Total: %{total}"
msgstr "Total : %{total}"
msgid "Track activity with Contribution Analytics."
-msgstr ""
+msgstr "Suivre l’activité avec l’analyse des contributions."
msgid "Track groups of issues that share a theme, across projects and milestones"
-msgstr ""
+msgstr "Suivre les groupes de tickets qui partagent un thème, entre différents projets et jalons"
msgid "Track time with quick actions"
msgstr "Suivre le temps estimé/passé avec les actions rapides"
msgid "Trending"
-msgstr ""
+msgstr "Tendance"
+
+msgid "Trigger"
+msgstr "Déclencheur"
+
+msgid "Trigger pipelines for mirror updates"
+msgstr "Déclencher des pipelines pour les mises à jour de miroirs"
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr "Déclenche les pipelines lorsque les branches ou les étiquettes sont mises à jour sur le dépôt en amont. Selon l’activité du dépôt en amont, cela peut considérablement augmenter la charge de vos exécuteurs d’intégration continue. N’activez ceci que si vous savez qu’ils peuvent tenir la charge."
msgid "Trigger this manual action"
msgstr "Déclencher cette action manuelle"
@@ -6677,16 +7959,25 @@ msgid "Try again"
msgstr "Veuillez réessayer"
msgid "Turn on Service Desk"
-msgstr ""
+msgstr "Activer le service d’assistance"
msgid "Twitter"
-msgstr ""
+msgstr "Twitter"
+
+msgid "Type"
+msgstr "Type"
msgid "Unable to load the diff. %{button_try_again}"
msgstr "Impossible de charger le diff. %{button_try_again}"
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
-msgstr ""
+msgstr "Impossible de vous connecter au groupe via SAML en raison de « %{reason} »"
+
+msgid "Unable to update this epic at this time."
+msgstr "Impossible de mettre à jour cette épopée pour le moment."
+
+msgid "Undo"
+msgstr "Annuler"
msgid "Unknown"
msgstr "Inconnu"
@@ -6694,12 +7985,18 @@ msgstr "Inconnu"
msgid "Unlock"
msgstr "Déverrouiller"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr "Déverrouiller %{issuableDisplayName} ? <strong>Tout le monde</strong> sera en mesure de commenter."
+
msgid "Unlocked"
msgstr "Déverrouillé"
msgid "Unresolve discussion"
msgstr "Marquer la discussion comme non résolue"
+msgid "Unstage"
+msgstr "Désindexer"
+
msgid "Unstage all changes"
msgstr "Désindexer les changements en étape"
@@ -6734,28 +8031,34 @@ msgid "Up to date"
msgstr "À jour"
msgid "Update"
-msgstr ""
+msgstr "Mettre à jour"
+
+msgid "Update now"
+msgstr "Mettre à jour maintenant"
msgid "Update your group name, description, avatar, and other general settings."
msgstr "Modifiez le nom du groupe, sa description, son logo et d’autres paramètres généraux."
+msgid "Updating"
+msgstr "Mise à jour en cours"
+
msgid "Upgrade your plan to activate Advanced Global Search."
-msgstr ""
+msgstr "Mettez à niveau votre forfait pour activer la recherche globale avancée."
msgid "Upgrade your plan to activate Contribution Analytics."
-msgstr ""
+msgstr "Mettez à niveau votre forfait pour activer l’analyse des contributions."
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr ""
+msgstr "Mettez à niveau votre forfait pour activer les webhooks de groupe."
msgid "Upgrade your plan to activate Issue weight."
-msgstr ""
+msgstr "Mettez à niveau votre forfait pour activer la pondération des tickets."
msgid "Upgrade your plan to improve Issue boards."
-msgstr ""
+msgstr "Mettez à niveau votre forfait pour améliorer les tableaux de tickets."
msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
-msgstr ""
+msgstr "Téléversez le fichier <code>GoogleCodeProjectHosting.json</code> ici :"
msgid "Upload New File"
msgstr "Téléverser un nouveau fichier"
@@ -6763,29 +8066,32 @@ msgstr "Téléverser un nouveau fichier"
msgid "Upload file"
msgstr "Téléverser un fichier"
-msgid "Upload new avatar"
-msgstr "Importer un nouvel avatar"
-
msgid "UploadLink|click to upload"
msgstr "Cliquez pour envoyer"
msgid "Upvotes"
msgstr "Votes positifs"
+msgid "Usage ping is not enabled"
+msgstr "L’envoi des données d’utilisation est désactivé"
+
msgid "Usage statistics"
msgstr "Statistiques d’utilisation"
msgid "Use <code>%{native_redirect_uri}</code> for local tests"
-msgstr ""
+msgstr "Utiliser <code>%{native_redirect_uri}</code> pour les tests locaux"
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr ""
+msgstr "Le service d’assistance (Service Desk) permet d’interagir avec vos utilisateurs (p. ex., pour offrir un support client) par courriel depuis GitLab"
msgid "Use group milestones to manage issues from multiple projects in the same milestone."
msgstr "Utilisez les jalons de groupe pour gérer les tickets de plusieurs projets dans le même jalon."
msgid "Use one line per URI"
-msgstr ""
+msgstr "Utilisez une ligne par URI"
+
+msgid "Use template"
+msgstr "Utiliser un modèle"
msgid "Use the following registration token during setup:"
msgstr "Utiliser le jeton d’inscription suivant pendant l’installation :"
@@ -6794,19 +8100,22 @@ msgid "Use your global notification setting"
msgstr "Utiliser vos paramètres de notification globaux"
msgid "Used by members to sign in to your group in GitLab"
-msgstr ""
+msgstr "Utilisé par les membres pour se connecter à votre groupe dans GitLab"
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr "Les cohortes d’utilisateurs ne sont affichées que lorsque la %{usage_ping_link_start}collecte des données d’utilisation%{usage_ping_link_end} est activée."
msgid "User Settings"
-msgstr ""
+msgstr "Paramètres de l’utilisateur"
msgid "User and IP Rate Limits"
msgstr "Limites de l’utilisateur et du débit IP"
msgid "User map"
-msgstr ""
+msgstr "Correspondance entre utilisateurs"
msgid "Users"
-msgstr "Utilisa·teur·trice·s"
+msgstr "Utilisateurs et utilisatrices"
msgid "Variables"
msgstr "Variables"
@@ -6824,13 +8133,16 @@ msgid "Various settings that affect GitLab performance."
msgstr "Divers paramètres qui affectent les performances de GitLab."
msgid "Verification information"
-msgstr ""
+msgstr "Informations de vérification"
msgid "Verified"
msgstr "Vérifié"
+msgid "Version"
+msgstr "Version"
+
msgid "View epics list"
-msgstr ""
+msgstr "Afficher la liste des épopées"
msgid "View file @ "
msgstr "Voir le fichier @ "
@@ -6839,10 +8151,10 @@ msgid "View group labels"
msgstr "Afficher les labels de groupe"
msgid "View issue"
-msgstr ""
+msgstr "Afficher le ticket"
msgid "View it on GitLab"
-msgstr ""
+msgstr "Voir sur GitLab"
msgid "View jobs"
msgstr "Afficher les tâches"
@@ -6866,10 +8178,10 @@ msgid "Visibility and access controls"
msgstr "Contrôles de visibilité et d’accès"
msgid "Visibility level:"
-msgstr ""
+msgstr "Niveau de visibilité :"
msgid "Visibility:"
-msgstr ""
+msgstr "Visibilité :"
msgid "VisibilityLevel|Internal"
msgstr "Interne"
@@ -6884,43 +8196,43 @@ msgid "VisibilityLevel|Unknown"
msgstr "Inconnu"
msgid "Want to see the data? Please ask an administrator for access."
-msgstr "Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."
+msgstr "Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."
msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
-msgstr ""
+msgstr "Nous avons détecté un potentiel courriel indésirable dans %{humanized_resource_name}. Veuillez résoudre le reCAPTCHA pour continuer."
msgid "We don't have enough data to show this stage."
-msgstr "Nous n'avons pas suffisamment de données pour afficher cette étape."
+msgstr "Nous n’avons pas suffisamment de données pour afficher cette étape."
msgid "We want to be sure it is you, please confirm you are not a robot."
-msgstr "Nous voulons être sûrs que c'est bien vous, merci de confirmer que vous n’êtes pas un robot."
+msgstr "Nous voulons nous assurer qu’il s’agit bien de vous, merci de confirmer que vous n’êtes pas un robot."
msgid "Web IDE"
-msgstr "Web IDE"
+msgstr "EDI Web"
msgid "Web terminal"
msgstr "Terminal Web"
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr ""
+msgstr "Les webhooks vous permettent de déclencher une URL si, par exemple, du nouveau code est poussé ou un nouveau ticket est créé. Vous pouvez configurer les webhooks pour écouter les événements spécifiques comme des poussées de code, des tickets ou des demandes de fusion. Les webhooks de groupes s’appliqueront à tous les projets dans un groupe, ce qui vous permet de normaliser la fonctionnalité du webhook dans votre groupe entier."
msgid "Weeks"
-msgstr ""
+msgstr "Semaines"
msgid "Weight"
msgstr "Poids"
msgid "Weight %{weight}"
-msgstr ""
+msgstr "Poids %{weight}"
msgid "When a runner is locked, it cannot be assigned to other projects"
msgstr "Lorsqu’un exécuteur est verrouillé, il ne peut pas être affecté à d’autres projets"
msgid "When enabled, users cannot use GitLab until the terms have been accepted."
-msgstr "Lorsque cette option est activée, les utilisateur•rice•s ne pourront pas utiliser GitLab tant que les conditions générales d’utilisation ne seront pas acceptés."
+msgstr "Lorsque cette option est activée, les utilisateurs et utilisarices ne pourront pas utiliser GitLab tant que les conditions générales d’utilisation ne seront pas acceptées."
msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
-msgstr ""
+msgstr "Lorsque vous laissez l’URL vide, des étiquettes de classification peuvent toujours être spécifiées sans désactiver les fonctionnalités interâ€projets ni effectuer de vérifications d’autorisation externes."
msgid "Wiki"
msgstr "Wiki"
@@ -6995,7 +8307,7 @@ msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
msgstr "Pour créer un lien vers une (nouvelle) page, il suffit de saisir %{link_example}"
msgid "WikiNewPagePlaceholder|how-to-setup"
-msgstr "consignes-installation"
+msgstr "guide-d-installation"
msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
msgstr "Astuce : vous pouvez saisir le chemin d’accès complet du nouveau fichier. Nous créerons automatiquement les répertoires manquants."
@@ -7004,13 +8316,13 @@ msgid "WikiNewPageTitle|New Wiki Page"
msgstr "Nouvelle Page Wiki"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
-msgstr "Êtes-vous sûr·e de vouloir supprimer cette page ?"
+msgstr "Êtesâ€vous sûr(e) de vouloir supprimer cette page ?"
msgid "WikiPageConfirmDelete|Delete page"
msgstr "Supprimer la page"
msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
-msgstr "Supprimer la page %{pageTitle} ?"
+msgstr "Supprimer la page %{pageTitle} ?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr "Quelqu’un a modifié la page en même temps que vous. Veuillez consulter %{page_link} et vérifiez que vos modifications ne suppriment pas involontairement les siennes."
@@ -7058,10 +8370,10 @@ msgid "Wiki|Wiki Pages"
msgstr "Pages du Wiki"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr ""
+msgstr "Grâce à l’analyse des contributions, vous pouvez avoir une vue d’ensemble de l’activité concernant les tickets, les demandes de fusion et les poussées Git de l’ensemble de votre organisation et de chacun de ses membres."
msgid "Withdraw Access Request"
-msgstr "Retirer la demande d'accès"
+msgstr "Retirer la demande d’accès"
msgid "Yes"
msgstr "Oui"
@@ -7070,10 +8382,10 @@ msgid "Yes, add it"
msgstr "Oui, l’ajouter"
msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
+msgstr "Oui, permettezâ€moi d’associer les utilisateurs de Google Code aux noms complets ou aux nom d’utilisateurs de GitLab."
msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
-msgstr ""
+msgstr "Vous êtes un administrateur ou une administratrice, ce qui signifie qu’accorder un accès à <strong>%{client_name}</strong> lui permettra d’interagir avec GitLab en tant qu’administrateur également. Faitesâ€le avec prudence."
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "Vous êtes sur le point de supprimer %{group_name}. Les groupes supprimés NE PEUVENT PAS être restaurés ! Êtes vous ABSOLUMENT sûr·e ?"
@@ -7085,13 +8397,13 @@ msgid "You are going to remove the fork relationship to source project %{forked_
msgstr "Vous êtes sur le point de supprimer la relation de divergence avec le projet source %{forked_from_project}. En êtesâ€vous ABSOLUMENT sûr·e ?"
msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
-msgstr "Vous allez transférer %{project_full_name} à un•e nouveau•elle propriétaire. Êtes-vous VRAIMENT sûr•e ?"
+msgstr "Vous allez transférer %{project_full_name} à un nouveau ou une nouvelle propriétaire. Êtesâ€vous VRAIMENT sûr(e) ?"
msgid "You are on a read-only GitLab instance."
msgstr "Vous êtes sur une instance GitLab en lecture seule."
msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
-msgstr ""
+msgstr "Vous êtes sur un nœud Geo secondaire <b>en lecture seule</b>. Si vous voulez apporter des modifications, vous devez le faire depuis cette page sur le %{primary_node}."
msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr "Vous pouvez %{linkStart}afficher les données brutes%{linkEnd} à la place."
@@ -7106,7 +8418,7 @@ msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
msgstr "Vous pouvez également tester votre .gitlab-ci.yml avec %{linkStart}Lint%{linkEnd}"
msgid "You can easily contribute to them by requesting to join these groups."
-msgstr ""
+msgstr "Vous pouvez facilement y contribuer en demandant à rejoindre ces groupes."
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr "Vous pouvez facilement installer un Exécuteur sur un cluster Kubernetes. %{link_to_help_page}"
@@ -7120,33 +8432,33 @@ msgstr "Vous ne pouvez ajouter de fichier que dans une branche"
msgid "You can only edit files when you are on a branch"
msgstr "Vous ne pouvez modifier des fichiers que dans une branche"
+msgid "You can reset runners registration token by pressing a button below."
+msgstr "Vous pouvez réinitialiser le jeton d’inscription des exécuteurs en appuyant sur un bouton ciâ€dessous."
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr "Vous pouvez résoudre le conflit de fusion Git soit en mode interactif, en cliquant sur les boutons « %{use_ours} » ou « %{use_theirs} », soit en modifiant directement les fichiers. Valider ces modifications dans la branche « %{branch_name} »"
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr "Vous pouvez configurer des tâches pour n’utiliser des exécuteurs qu’avec des étiquettes spécifiques. Séparez les étiquettes par des virgules."
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
-msgstr ""
+msgstr "Vous ne pouvez pas écrire sur une instance GitLab Geo secondaire en lecture seule. Veuillez utiliser le %{link_to_primary_node} à la place."
msgid "You cannot write to this read-only GitLab instance."
-msgstr "Vous ne pouvez pas écrire sur cette instance GitLab en lecture-seule."
-
-msgid "You do not have any assigned merge requests"
-msgstr "Aucune demande de fusion ne vous a été affectée"
+msgstr "Vous ne pouvez pas écrire sur cette instance GitLab en lecture seule."
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
-msgstr ""
+msgstr "Vous ne disposez pas des autorisations appropriées pour outrepasser les paramètres de synchronisation du groupe LDAP."
msgid "You don't have any applications"
-msgstr ""
+msgstr "Vous ne disposez d’aucune application"
msgid "You don't have any authorized applications"
-msgstr ""
+msgstr "Vous ne disposez d’aucune application autorisée"
msgid "You have no permissions"
msgstr "Vous n’avez pas les autorisations"
-msgid "You have not created any merge requests"
-msgstr "Vous n’avez créé aucune demande de fusion"
-
msgid "You have reached your project limit"
msgstr "Vous avez atteint votre limite de projet"
@@ -7156,18 +8468,21 @@ msgstr "Vous devez accepter les conditions générales d’utilisation et la pol
msgid "You must have maintainer access to force delete a lock"
msgstr "Seul un responsable peut forcer la suppression d’un verrou"
-msgid "You must sign in to star a project"
-msgstr "Vous devez vous connecter pour mettre un projet en favori"
-
msgid "You need a different license to enable FileLocks feature"
-msgstr ""
+msgstr "Vous avez besoin d’une licence différente pour activer la fonctionnalité de verrouillage de fichiers FileLocks"
msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
-msgstr ""
+msgstr "Pour aller plus loin, vous devez disposer de git-lfs en version %{min_git_lfs_version} (ou supérieure). Veuillez consulter https://git-lfs.github.com"
msgid "You need permission."
msgstr "Vous avez besoin d’une autorisation."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr "Vous allez perdre toutes les modifications apportées à ce fichier. Cette action ne peut pas être annulée."
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr "Vous allez perdre toutes les modifications non indexées que vous avez effectuées sur ce projet. Cette action ne peut pas être annulée."
+
msgid "You will not get any notifications via email"
msgstr "Vous ne recevrez aucune notification par courriel"
@@ -7184,7 +8499,7 @@ msgid "You will receive notifications only for comments in which you were @menti
msgstr "Vous ne recevrez de notifications que pour les commentaires où vous êtes @mentionné"
msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "Vous ne pourrez pas récupérer ou pousser de code par %{protocol} tant que vous n'aurez pas %{set_password_link} pour votre compte"
+msgstr "Vous ne pourrez pas récupérer ou pousser de code via %{protocol} tant que vous n’aurez pas %{set_password_link} pour votre compte"
msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
msgstr "Vous ne pourrez pas récupérer ou pousser de code par SSH tant que vous n’aurez pas %{add_ssh_key_link} dans votre profil"
@@ -7196,16 +8511,16 @@ msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Vous devrez utiliser différents noms de branches pour obtenir une comparaison valide."
msgid "You're receiving this email because %{reason}."
-msgstr ""
+msgstr "Vous recevez ce courriel parce que %{reason}."
msgid "You're receiving this email because of your account on %{host}."
-msgstr ""
+msgstr "Vous recevez ce courriel parce que vous possédez un compte sur %{host}."
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr "Vous recevez ce courriel en raison de votre compte sur %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgid "YouTube"
-msgstr ""
+msgstr "YouTube"
msgid "Your Groups"
msgstr "Vos groupes"
@@ -7223,10 +8538,10 @@ msgid "Your Todos"
msgstr "Vos tâches à faire"
msgid "Your applications (%{size})"
-msgstr ""
+msgstr "Vos applications (%{size})"
msgid "Your authorized applications"
-msgstr ""
+msgstr "Vos applications autorisées"
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr "Vos modifications peuvent être validées sur %{branch_name} car une demande de fusion est ouverte."
@@ -7246,17 +8561,15 @@ msgstr "Votre nom"
msgid "Your projects"
msgstr "Vos projets"
+msgid "a deleted user"
+msgstr "un utilisateur supprimé"
+
msgid "ago"
msgstr "auparavant "
msgid "among other things"
msgstr "entre autres choses"
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr "assignez vous"
@@ -7267,215 +8580,244 @@ msgid "by"
msgstr "par"
msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}En savoir plus à propos de l’analyse des conteneurs %{linkEndTag}"
msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}En savoir plus sur DAST%{linkEndTag}"
msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}En savoir plus sur l’analyse des dépendances%{linkEndTag}"
msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}En savoir plus sur SAST %{linkEndTag}"
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr "%{namespace} est affecté par « %{vulnerability} »."
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr "%{remainingPackagesCount} restant(s)"
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
msgid "ciReport|Class"
-msgstr ""
+msgstr "Classe"
msgid "ciReport|Code quality"
-msgstr ""
+msgstr "Qualité du code"
msgid "ciReport|Confidence"
+msgstr "Niveau de confiance"
+
+msgid "ciReport|Container scanning"
msgstr ""
msgid "ciReport|Container scanning detected"
-msgstr ""
+msgstr "Analyse de conteneur détectée"
msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
-msgstr ""
-
-msgid "ciReport|Container scanning is loading"
-msgstr ""
+msgstr "L’analyse des conteneurs permet la détection de vulnérabilités connues dans vos images Docker."
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
msgid "ciReport|DAST detected"
-msgstr ""
+msgstr "DAST détecté"
-msgid "ciReport|DAST is loading"
-msgstr ""
-
-msgid "ciReport|DAST resulted in error while loading results"
-msgstr ""
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr "L’analyse des dépendances a détecté une vulnérabilité connue dans les dépendances de votre code source."
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|Dependency scanning"
msgstr ""
msgid "ciReport|Dependency scanning detected"
-msgstr ""
-
-msgid "ciReport|Dependency scanning is loading"
-msgstr ""
-
-msgid "ciReport|Dependency scanning resulted in error while loading results"
-msgstr ""
+msgstr "Analyse de dépendances détectée"
msgid "ciReport|Description"
-msgstr ""
+msgstr "Description"
msgid "ciReport|Dismiss vulnerability"
-msgstr ""
+msgstr "Rejeter la vulnérabilité"
msgid "ciReport|Dismissed by"
-msgstr ""
+msgstr "Rejeté par"
msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
-msgstr ""
+msgstr "Le test de sécurité d’applications dynamique (Dynamic Application Security Testing — DAST) a détecté une vulnérabilité connue dans votre application Web."
msgid "ciReport|Failed to load %{reportName} report"
-msgstr ""
+msgstr "Impossible de charger le rapport %{reportName}"
msgid "ciReport|File"
-msgstr ""
+msgstr "Fichier"
msgid "ciReport|Fixed:"
-msgstr ""
+msgstr "Corrigé :"
msgid "ciReport|Identifiers"
-msgstr ""
+msgstr "Identifiants"
msgid "ciReport|Instances"
-msgstr ""
+msgstr "Instances"
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
-msgstr ""
+msgstr "En savoir plus sur l’interaction avec les rapports de sécurité (alpha)."
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] "Le gestionnaire de licences a détecté %d licence pour la seule branche source"
+msgstr[1] "Le gestionnaire de licences a détecté %d licences pour la seule branche source"
-msgid "ciReport|License management detected %{licenseInfo}"
-msgstr ""
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] "Le gestionnaire de licences a détecté %d nouvelle licence"
+msgstr[1] "Le gestionnaire de licences a détecté %d nouvelles licences"
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr "Le gestionnaire de licences n’a détecté aucune licence pour la seule branche source"
msgid "ciReport|License management detected no new licenses"
-msgstr ""
+msgstr "Le gestionnaire de licences n’a détecté aucune nouvelle licence"
msgid "ciReport|Links"
-msgstr ""
+msgstr "Liens"
msgid "ciReport|Loading %{reportName} report"
-msgstr ""
+msgstr "Chargement du rapport %{reportName}"
msgid "ciReport|Method"
-msgstr ""
+msgstr "Méthode"
msgid "ciReport|Namespace"
-msgstr ""
+msgstr "Espace de noms"
msgid "ciReport|No changes to code quality"
-msgstr ""
+msgstr "Aucun changement dans la qualité du code"
msgid "ciReport|No changes to performance metrics"
-msgstr ""
+msgstr "Aucun changement dans les indicateurs de performance"
msgid "ciReport|Performance metrics"
-msgstr ""
+msgstr "Indicateurs de performance"
msgid "ciReport|Revert dismissal"
-msgstr ""
-
-msgid "ciReport|SAST detected"
-msgstr ""
+msgstr "Annuler le rejet"
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
-msgstr ""
+msgid "ciReport|SAST detected"
+msgstr "SAST détecté"
msgid "ciReport|Security scanning"
-msgstr ""
+msgstr "Analyse de sécurité"
msgid "ciReport|Security scanning failed loading any results"
-msgstr ""
-
-msgid "ciReport|Security scanning is loading"
-msgstr ""
+msgstr "L’analyse de sécurité n’a pas réussi à charger de résultats"
msgid "ciReport|Severity"
-msgstr ""
+msgstr "Sévérité"
msgid "ciReport|Solution"
-msgstr ""
+msgstr "Solution"
msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
-msgstr ""
+msgstr "Le test de sécurité d’applications statique (Static Application Security Testing — SAST) a détecté une vulnérabilité connue dans votre application Web."
msgid "ciReport|There was an error creating the issue. Please try again."
-msgstr ""
+msgstr "Une erreur s’est produite lors de la création du ticket. Veuillez réessayer."
msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
-msgstr ""
+msgstr "Une erreur s’est produite lors du rejet de la vulnérabilité. Veuillez réessayer."
msgid "ciReport|There was an error loading DAST report"
-msgstr ""
+msgstr "Une erreur s’est produite lors du chargement du rapport DAST"
msgid "ciReport|There was an error loading SAST report"
-msgstr ""
+msgstr "Une erreur s’est produite lors du chargement du rapport SAST"
msgid "ciReport|There was an error loading container scanning report"
-msgstr ""
+msgstr "Une erreur s’est produite lors du chargement du rapport d’analyse du conteneur"
msgid "ciReport|There was an error loading dependency scanning report"
-msgstr ""
+msgstr "Une erreur s’est produite lors du chargement du rapport d’analyse des dépendances"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
-msgstr ""
-
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
+msgstr "Une erreur s’est produite lors de l’annulation du rejet. Veuillez réessayer."
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
-msgstr ""
+msgstr "Mise à niveau de %{name} de %{version} à %{fixed}."
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] "Utilisé par %{packagesString}"
+msgstr[1] "Utilisé par %{packagesString} et %{lastPackage}"
msgid "ciReport|View full report"
-msgstr ""
+msgstr "Voir le rapport complet"
msgid "ciReport|no vulnerabilities"
-msgstr ""
+msgstr "aucune vulnérabilité"
msgid "ciReport|on pipeline"
-msgstr ""
+msgstr "dans le pipeline"
msgid "command line instructions"
msgstr "instructions en ligne de commande"
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr "Vous êtes sur le point de désactiver la confidentialité. Cela signifie que <strong>tout le monde</strong> sera en mesure de voir et de laisser un commentaire sur ce ticket."
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr "Vous êtes sur le point de d’activer la confidentialité. Cela signifie que seuls les membres de l’équipe avec <strong>au moins un accès en tant que rapporteur</strong> seront en mesure de voir et de laisser des commentaires sur le ticket."
+
msgid "connecting"
msgstr "connexion en cours"
@@ -7483,7 +8825,7 @@ msgid "could not read private key, is the passphrase correct?"
msgstr "impossible de lire la clef privée, la phrase secrète estâ€elle correcte ?"
msgid "customize"
-msgstr ""
+msgstr "personnaliser"
msgid "day"
msgid_plural "days"
@@ -7493,24 +8835,11 @@ msgstr[1] "jours"
msgid "deploy token"
msgstr "jeton de déploiement"
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr "n’a détecté aucune vulnérabilité"
-
msgid "disabled"
msgstr "désactivé"
msgid "done"
-msgstr ""
+msgstr "terminé"
msgid "enabled"
msgstr "activé"
@@ -7524,26 +8853,40 @@ msgstr "pour ce projet"
msgid "here"
msgstr "ici"
+msgid "https://your-bitbucket-server"
+msgstr "https://votre-serveur-bitbucket"
+
msgid "import flow"
-msgstr ""
+msgstr "flux d’importation"
msgid "importing"
msgstr "importation en cours"
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] "instance terminée"
+msgstr[1] "instances terminées"
+
msgid "is invalid because there is downstream lock"
-msgstr ""
+msgstr "n’est pas valide, car il y a un verrou en aval"
msgid "is invalid because there is upstream lock"
-msgstr ""
+msgstr "n’est pas valide, car il y a un verrou en amont"
msgid "is not a valid X509 certificate."
-msgstr ""
+msgstr "n’est pas un certificat X.509 valide."
+
+msgid "issue boards"
+msgstr "tableaux des tickets"
msgid "latest version"
msgstr "dernière version"
+msgid "license management"
+msgstr "gestion des licences"
+
msgid "locked by %{path_lock_user_name} %{created_at}"
-msgstr ""
+msgstr "verrouillé par %{path_lock_user_name} %{created_at}"
msgid "merge request"
msgid_plural "merge requests"
@@ -7563,22 +8906,25 @@ msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasis
msgstr "%{metricsLinkStart}L’usage mémoire%{metricsLinkEnd} %{emphasisStart}est resté stable%{emphasisEnd} à %{memoryFrom}MO"
msgid "mrWidget|Add approval"
-msgstr ""
+msgstr "Ajouter une approbation"
msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr "Autoriser les commits des membres qui peuvent fusionner dans la branche cible"
msgid "mrWidget|An error occured while removing your approval."
-msgstr ""
+msgstr "Une erreur est survenue lors de la suppression de votre approbation."
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr "Une erreur est survenue lors de la récupération des données d’approbation pour cette demande de fusion."
msgid "mrWidget|An error occurred while submitting your approval."
-msgstr ""
+msgstr "Une erreur est survenue lors de l’envoi de votre approbation."
msgid "mrWidget|Approve"
-msgstr ""
+msgstr "Approuver"
msgid "mrWidget|Approved by"
-msgstr ""
+msgstr "Approuvée par"
msgid "mrWidget|Cancel automatic merge"
msgstr "Annuler la fusion automatique"
@@ -7619,6 +8965,9 @@ msgstr "Patchs par courriel"
msgid "mrWidget|Failed to load deployment statistics"
msgstr "Impossible de charger les statistiques de déploiement"
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr "La fusion rapide fastâ€forward n’est pas possible. Pour réaliser cette fusion, vous devez d’abord effectuer un « git rebase » en local."
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr "Si la branche %{branch} existe dans votre dépôt local, vous pouvez fusionner cette demande de fusion manuellement à l’aide de"
@@ -7641,26 +8990,32 @@ msgid "mrWidget|Merge locally"
msgstr "Fusionner localement"
msgid "mrWidget|Merge request approved"
-msgstr ""
+msgstr "Demande de fusion approuvée"
msgid "mrWidget|Merge request approved; you can approve additionally"
-msgstr ""
+msgstr "Demande de fusion approuvée ; vous pouvez ajouter votre approbation"
msgid "mrWidget|Merged by"
msgstr "Fusionnée par"
msgid "mrWidget|No Approval required"
-msgstr ""
+msgstr "Aucune approbation exigée"
msgid "mrWidget|No Approval required; you can still approve"
-msgstr ""
+msgstr "Aucune approbation exigée ; vous pouvez tout de même approuver"
msgid "mrWidget|Open in Web IDE"
-msgstr ""
+msgstr "Ouvrir dans l’EDI Web"
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr "Pipeline bloqué. Le pipeline de cette demande de fusion nécessite une action manuelle pour continuer"
msgid "mrWidget|Plain diff"
msgstr "Diff simple"
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr "Prête à être fusionnée automatiquement. Demandez à quelqu’un ayant un accès en écriture à ce dépôt d’effectuer cette fusionner"
+
msgid "mrWidget|Refresh"
msgstr "Actualiser"
@@ -7677,14 +9032,27 @@ msgid "mrWidget|Remove source branch"
msgstr "Supprimer la branche source"
msgid "mrWidget|Remove your approval"
-msgstr ""
+msgstr "Supprimer votre approbation"
msgid "mrWidget|Request to merge"
msgstr "Demande de fusion de"
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] "Nécessite encore une approbation"
+msgstr[1] "Nécessite encore %d approbations"
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] "Nécessite encore l’approbation de"
+msgstr[1] "Nécessite encore %d approbations de"
+
msgid "mrWidget|Resolve conflicts"
msgstr "Résoudre les conflits"
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr "Résolvez ces conflits ou demandez à une personne ayant un accès en écriture à ce dépôt d’effectuer la fusion localement"
+
msgid "mrWidget|Revert"
msgstr "Défaire"
@@ -7703,9 +9071,18 @@ msgstr "Les modifications n’ont pas été fusionnées dans"
msgid "mrWidget|The changes will be merged into"
msgstr "Les modifications seront fusionnées dans"
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr "Le pipeline de cette demande de fusion a échoué. Veuillez réexécutez la tâche ou pousser un nouveau commit pour résoudre le problème"
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr "La branche source HEAD a changé récemment. Veuillez recharger la page et vérifier les modifications avant d’effectuer la fusion"
+
msgid "mrWidget|The source branch has been removed"
msgstr "La branche source a été supprimée"
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr "La branche source est à %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} de la branche cible"
+
msgid "mrWidget|The source branch is being removed"
msgstr "La branche source est en cours de suppression"
@@ -7730,6 +9107,9 @@ msgstr "Cette demande de fusion est en cours de fusion"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "Ce projet est archivé, l’accès en écriture a été désactivé"
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr "Vous n’êtes pas autorisé à modifier directement ce projet. Veuillez créer un projet divergent afin d’effectuer des changements."
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "Vous pouvez fusionner cette demande de fusion manuellement à l’aide de la"
@@ -7748,6 +9128,9 @@ msgstr "dans"
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr "pour être fusionnée automatiquement lorsque le pipeline réussit"
+msgid "n/a"
+msgstr "non disponible"
+
msgid "new merge request"
msgstr "nouvelle demande de fusion"
@@ -7757,6 +9140,11 @@ msgstr "courriels de notification"
msgid "or"
msgstr "ou"
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] "sur un total de %d test"
+msgstr[1] "sur un total de %d tests"
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "parent"
@@ -7769,11 +9157,14 @@ msgid "personal access token"
msgstr "jeton d’accès personnel"
msgid "private key does not match certificate."
-msgstr ""
+msgstr "la clef privée ne correspond pas au certificat."
msgid "remaining"
msgstr "restant"
+msgid "remove"
+msgstr "supprimer"
+
msgid "remove due date"
msgstr "supprimer la date d’échéance"
@@ -7787,7 +9178,7 @@ msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} mettra à jour la somme du temps passé."
msgid "started"
-msgstr ""
+msgstr "démarré"
msgid "this document"
msgstr "ce document"
@@ -7795,14 +9186,17 @@ msgstr "ce document"
msgid "to help your contributors communicate effectively!"
msgstr "pour aider vos contributeurs à communiquer efficacement !"
+msgid "toggle collapse"
+msgstr "déplier ou replier"
+
msgid "username"
-msgstr "nom d’utilisa·teur·trice"
+msgstr "nom d’utilisateur"
msgid "uses Kubernetes clusters to deploy your code!"
-msgstr "utilise les clusters Kubernetes pour déployer votre code !"
+msgstr "utilise les grappes de serveurs Kubernetes pour déployer votre code !"
msgid "view it on GitLab"
-msgstr ""
+msgstr "voir sur GitLab"
msgid "with %{additions} additions, %{deletions} deletions."
msgstr "avec %{additions} ajouts, %{deletions} suppressions."
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e8bf7ae8f0e..f7782562af2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -19,6 +19,11 @@ msgstr ""
msgid " Status"
msgstr ""
+msgid "%d addition"
+msgid_plural "%d additions"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d changed file"
msgid_plural "%d changed files"
msgstr[0] ""
@@ -34,6 +39,11 @@ msgid_plural "%d commits behind"
msgstr[0] ""
msgstr[1] ""
+msgid "%d deleted"
+msgid_plural "%d deletions"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
@@ -93,6 +103,9 @@ msgstr ""
msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
msgstr ""
+msgid "%{count} more assignees"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -107,6 +120,9 @@ msgstr ""
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -119,23 +135,12 @@ msgstr ""
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
-msgstr ""
-
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
-msgstr ""
-
msgid "%{openOrClose} %{noteable}"
msgstr ""
msgid "%{percent}%% complete"
msgstr ""
-msgid "%{storage_name}: failed storage access attempt on host:"
-msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
msgstr[0] ""
@@ -150,6 +155,12 @@ msgstr ""
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -235,18 +246,30 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{group_name}</strong> group members"
msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A member of GitLab's abuse team will review your report as soon as possible."
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -283,10 +306,7 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
-msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
-msgstr ""
-
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Access expiration date"
msgstr ""
msgid "Account"
@@ -313,10 +333,19 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
+msgid "Add Readme"
msgstr ""
-msgid "Add Readme"
+msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
+msgstr ""
+
+msgid "Add a table"
+msgstr ""
+
+msgid "Add image comment"
+msgstr ""
+
+msgid "Add license"
msgstr ""
msgid "Add new application"
@@ -337,6 +366,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Admin Area"
msgstr ""
@@ -346,6 +378,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -361,7 +399,7 @@ msgstr ""
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
msgstr ""
-msgid "AdminHealthPageLink|health page"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
msgstr ""
msgid "AdminProjects|Delete"
@@ -397,7 +435,7 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
-msgid "Advanced"
+msgid "Advanced permissions, Large File Storage and Two-Factor authentication settings."
msgstr ""
msgid "Advanced settings"
@@ -418,6 +456,9 @@ msgstr ""
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
+msgid "Allow projects within this group to use Git LFS"
+msgstr ""
+
msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
@@ -427,12 +468,21 @@ msgstr ""
msgid "Allow requests to the local network from hooks and services."
msgstr ""
+msgid "Allow users to request access"
+msgstr ""
+
+msgid "Allow users to request access if visibility is public or internal."
+msgstr ""
+
msgid "Allows you to add and manage Kubernetes clusters."
msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication"
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -451,7 +501,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -493,13 +543,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while loading commit signatures"
@@ -553,7 +615,7 @@ msgstr ""
msgid "Application"
msgstr ""
-msgid "Application Id"
+msgid "Application ID"
msgstr ""
msgid "Application: %{name}"
@@ -571,9 +633,15 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Are you sure you want to erase this build?"
+msgstr ""
+
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
@@ -589,13 +657,16 @@ msgstr ""
msgid "Are you sure you want to reset the health check token?"
msgstr ""
+msgid "Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Are you sure?"
msgstr ""
msgid "Artifacts"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assign custom color like #FF0000"
@@ -706,6 +777,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -715,6 +789,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -736,9 +813,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -778,6 +852,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -805,9 +882,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -981,15 +1064,24 @@ msgstr ""
msgid "Browse files"
msgstr ""
+msgid "Business metrics (Custom)"
+msgstr ""
+
msgid "ByAuthor|by"
msgstr ""
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Charts"
+msgstr ""
+
msgid "CI / CD Settings"
msgstr ""
+msgid "CI/CD"
+msgstr ""
+
msgid "CI/CD configuration"
msgstr ""
@@ -1008,6 +1100,9 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
+msgid "CICD|Continuous deployment to production using timed incremental rollout"
+msgstr ""
+
msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
@@ -1035,9 +1130,6 @@ msgstr ""
msgid "Callback URL"
msgstr ""
-msgid "Callback url"
-msgstr ""
-
msgid "Can't find HEAD commit for this branch"
msgstr ""
@@ -1086,7 +1178,7 @@ msgstr ""
msgid "Chat"
msgstr ""
-msgid "Check interval"
+msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
msgid "Checking %{text} availability…"
@@ -1113,6 +1205,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1134,6 +1232,9 @@ msgstr ""
msgid "CiStatusLabel|created"
msgstr ""
+msgid "CiStatusLabel|delayed"
+msgstr ""
+
msgid "CiStatusLabel|failed"
msgstr ""
@@ -1152,6 +1253,9 @@ msgstr ""
msgid "CiStatusLabel|skipped"
msgstr ""
+msgid "CiStatusLabel|waiting for delayed job"
+msgstr ""
+
msgid "CiStatusLabel|waiting for manual action"
msgstr ""
@@ -1164,6 +1268,9 @@ msgstr ""
msgid "CiStatusText|created"
msgstr ""
+msgid "CiStatusText|delayed"
+msgstr ""
+
msgid "CiStatusText|failed"
msgstr ""
@@ -1209,7 +1316,7 @@ msgstr ""
msgid "CiVariable|Validation failed"
msgstr ""
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "Clear search"
msgstr ""
msgid "Clear search input"
@@ -1248,21 +1355,45 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|A Knative build extends Kubernetes and utilizes existing Kubernetes primitives to provide you with the ability to run on-cluster container builds from source. For example, you can write a build that uses Kubernetes-native resources to obtain your source code from a repository, build it into container a image, and then run that image."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|Add a Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Adding a Kubernetes cluster to your group will automatically share the cluster across all your projects. Use review apps, deploy your applications, and easily run your pipelines for all projects using the same cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Adding an integration to your group will share the cluster across all your projects."
+msgstr ""
+
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1275,10 +1406,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1305,6 +1436,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1329,6 +1466,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1338,9 +1478,15 @@ msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine project"
msgstr ""
+msgid "ClusterIntegration|Group cluster"
+msgstr ""
+
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1350,6 +1496,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1371,22 +1520,19 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster details"
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration"
+msgid "ClusterIntegration|Knative"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgid "ClusterIntegration|Knative Domain Name:"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
@@ -1398,7 +1544,7 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
@@ -1407,16 +1553,13 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
-msgstr ""
-
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Learn more about security configuration"
+msgid "ClusterIntegration|Learn more about group Kubernetes clusters"
msgstr ""
msgid "ClusterIntegration|Machine type"
@@ -1458,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project cluster"
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1467,6 +1616,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1479,6 +1634,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1494,9 +1652,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1533,13 +1688,13 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
msgstr ""
msgid "ClusterIntegration|Toggle Kubernetes cluster"
@@ -1551,9 +1706,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1572,9 +1733,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1593,12 +1751,18 @@ msgstr ""
msgid "Collapse sidebar"
msgstr ""
+msgid "Comment"
+msgstr ""
+
msgid "Comment & resolve discussion"
msgstr ""
msgid "Comment & unresolve discussion"
msgstr ""
+msgid "Comment form position"
+msgstr ""
+
msgid "Comments"
msgstr ""
@@ -1633,6 +1797,9 @@ msgstr ""
msgid "CommitMessage|Add %{file_name}"
msgstr ""
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr ""
@@ -1705,9 +1872,6 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
@@ -1717,7 +1881,7 @@ msgstr ""
msgid "Configure push mirrors."
msgstr ""
-msgid "Configure storage path and circuit breaker settings."
+msgid "Configure storage path settings."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -1792,9 +1956,15 @@ msgstr ""
msgid "Contribution"
msgstr ""
+msgid "Contribution Charts"
+msgstr ""
+
msgid "Contribution guide"
msgstr ""
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributors"
msgstr ""
@@ -1816,6 +1986,18 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy ID to clipboard"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy URL to clipboard"
msgstr ""
@@ -1831,15 +2013,24 @@ msgstr ""
msgid "Copy file path to clipboard"
msgstr ""
+msgid "Copy link"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy secret to clipboard"
+msgstr ""
+
msgid "Copy to clipboard"
msgstr ""
msgid "Copy token to clipboard"
msgstr ""
+msgid "Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1909,7 +2100,7 @@ msgstr ""
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
+msgid "Create your first page"
msgstr ""
msgid "CreateTag|Tag"
@@ -1948,6 +2139,9 @@ msgstr ""
msgid "Custom CI config path"
msgstr ""
+msgid "Custom hostname (for private commit emails)"
+msgstr ""
+
msgid "Custom notification events"
msgstr ""
@@ -1960,9 +2154,15 @@ msgstr ""
msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -2005,6 +2205,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default: Directly import the Google Code email address or username"
msgstr ""
@@ -2014,12 +2217,33 @@ msgstr ""
msgid "Define a custom pattern with cron syntax"
msgstr ""
+msgid "Define environments in the deploy stage(s) in <code>.gitlab-ci.yml</code> to track deployments here."
+msgstr ""
+
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgstr ""
+
+msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
+msgstr ""
+
+msgid "DelayedJobs|Start now"
+msgstr ""
+
+msgid "DelayedJobs|Unschedule"
+msgstr ""
+
+msgid "DelayedJobs|delayed"
+msgstr ""
+
msgid "Delete"
msgstr ""
msgid "Delete Snippet"
msgstr ""
+msgid "Delete comment"
+msgstr ""
+
msgid "Delete list"
msgstr ""
@@ -2157,6 +2381,12 @@ msgstr ""
msgid "DeployTokens|Your new project deploy token has been created."
msgstr ""
+msgid "Deployed to"
+msgstr ""
+
+msgid "Deploying to"
+msgstr ""
+
msgid "Deprioritize label"
msgstr ""
@@ -2172,6 +2402,12 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Diff content limits"
+msgstr ""
+
+msgid "Diff limits"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
@@ -2193,9 +2429,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2271,6 +2519,9 @@ msgstr ""
msgid "Edit application"
msgstr ""
+msgid "Edit environment"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
@@ -2313,9 +2564,6 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
-msgid "Enable or disable certain group features and choose access levels."
-msgstr ""
-
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -2325,15 +2573,36 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Ends at (UTC)"
msgstr ""
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
+msgid "Environments allow you to track deployments of your application %{link_to_read_more}."
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -2361,6 +2630,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2373,6 +2645,9 @@ msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2397,10 +2672,16 @@ msgstr ""
msgid "Environments|Stop environment"
msgstr ""
+msgid "Environments|Stopping"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
-msgid "Environments|You don't have any environments right now."
+msgid "Environments|You don't have any environments right now"
+msgstr ""
+
+msgid "Epic"
msgstr ""
msgid "Error"
@@ -2442,6 +2723,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2499,6 +2786,12 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explain the problem. If appropriate, provide a link to the relevant issue or comment."
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2517,6 +2810,9 @@ msgstr ""
msgid "Explore public groups"
msgstr ""
+msgid "External URL"
+msgstr ""
+
msgid "Facebook"
msgstr ""
@@ -2532,6 +2828,12 @@ msgstr ""
msgid "Failed to check related branches."
msgstr ""
+msgid "Failed to deploy to"
+msgstr ""
+
+msgid "Failed to load emoji list."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -2559,6 +2861,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -2571,6 +2876,9 @@ msgstr ""
msgid "Filter by commit message"
msgstr ""
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -2613,17 +2921,15 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -2678,6 +2984,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -2687,9 +2996,6 @@ msgstr ""
msgid "Git revision"
msgstr ""
-msgid "Git storage health information has been reset"
-msgstr ""
-
msgid "Git strategy for pipelines"
msgstr ""
@@ -2717,6 +3023,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -2744,12 +3053,6 @@ msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
-msgid "Go to your fork"
-msgstr ""
-
-msgid "GoToYourFork|Fork"
-msgstr ""
-
msgid "Google Code import"
msgstr ""
@@ -2780,9 +3083,18 @@ msgstr ""
msgid "Group Runners"
msgstr ""
+msgid "Group URL"
+msgstr ""
+
msgid "Group avatar"
msgstr ""
+msgid "Group description"
+msgstr ""
+
+msgid "Group description (optional)"
+msgstr ""
+
msgid "Group details"
msgstr ""
@@ -2792,13 +3104,22 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group name"
+msgstr ""
+
msgid "Group: %{group_name}"
msgstr ""
-msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgid "GroupSettings|Badges"
msgstr ""
-msgid "GroupSettings|Share with group lock"
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
@@ -2858,6 +3179,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -2870,19 +3194,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Health Check"
@@ -2912,6 +3236,9 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -2971,6 +3298,9 @@ msgstr ""
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3049,6 +3379,9 @@ msgstr ""
msgid "Import repository"
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
@@ -3061,6 +3394,9 @@ msgstr ""
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
@@ -3097,12 +3433,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3118,6 +3463,9 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues, merge requests, pushes and comments."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -3133,22 +3481,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
msgstr ""
-msgid "Koding"
+msgid "Job|Job has been erased"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3214,6 +3598,12 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Large File Storage"
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3225,12 +3615,18 @@ msgstr ""
msgid "Last commit"
msgstr ""
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
msgid "Last edited by %{name}"
msgstr ""
+msgid "Last reply by"
+msgstr ""
+
msgid "Last update"
msgstr ""
@@ -3249,6 +3645,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3273,6 +3672,11 @@ msgstr ""
msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
msgstr ""
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3285,6 +3689,9 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List view"
+msgstr ""
+
msgid "List your Bitbucket Server repositories"
msgstr ""
@@ -3309,6 +3716,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3381,7 +3791,40 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "Maximum git storage failures"
+msgid "MarkdownToolbar|Add a bullet list"
+msgstr ""
+
+msgid "MarkdownToolbar|Add a link"
+msgstr ""
+
+msgid "MarkdownToolbar|Add a numbered list"
+msgstr ""
+
+msgid "MarkdownToolbar|Add a table"
+msgstr ""
+
+msgid "MarkdownToolbar|Add a task list"
+msgstr ""
+
+msgid "MarkdownToolbar|Add bold text"
+msgstr ""
+
+msgid "MarkdownToolbar|Add italic text"
+msgstr ""
+
+msgid "MarkdownToolbar|Go full screen"
+msgstr ""
+
+msgid "MarkdownToolbar|Insert a quote"
+msgstr ""
+
+msgid "MarkdownToolbar|Insert code"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum job timeout"
msgstr ""
msgid "May"
@@ -3390,6 +3833,9 @@ msgstr ""
msgid "Median"
msgstr ""
+msgid "Member since %{date}"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -3423,15 +3869,21 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "MergeRequest|Filter files"
+msgstr ""
+
+msgid "MergeRequest|No files found"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -3447,6 +3899,12 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics for environment"
+msgstr ""
+
msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
msgstr ""
@@ -3480,6 +3938,12 @@ msgstr ""
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -3498,6 +3962,9 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. Existing project milestones with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Mirror a repository"
msgstr ""
@@ -3528,9 +3995,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More information"
msgstr ""
@@ -3558,6 +4022,9 @@ msgstr ""
msgid "Name:"
msgstr ""
+msgid "Naming, visibility"
+msgstr ""
+
msgid "Nav|Help"
msgstr ""
@@ -3582,6 +4049,9 @@ msgstr ""
msgid "New Application"
msgstr ""
+msgid "New Environment"
+msgstr ""
+
msgid "New Group"
msgstr ""
@@ -3614,6 +4084,9 @@ msgstr ""
msgid "New directory"
msgstr ""
+msgid "New environment"
+msgstr ""
+
msgid "New file"
msgstr ""
@@ -3656,6 +4129,9 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No Label"
+msgstr ""
+
msgid "No assignee"
msgstr ""
@@ -3668,6 +4144,12 @@ msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -3677,15 +4159,15 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No files found"
-msgstr ""
-
msgid "No files found."
msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests found"
msgstr ""
@@ -3704,6 +4186,9 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr ""
@@ -3713,6 +4198,12 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -3731,6 +4222,9 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -3743,6 +4237,18 @@ msgstr ""
msgid "Notes|Are you sure you want to cancel creating this comment?"
msgstr ""
+msgid "Notes|Collapse replies"
+msgstr ""
+
+msgid "Notes|Show all activity"
+msgstr ""
+
+msgid "Notes|Show comments only"
+msgstr ""
+
+msgid "Notes|Show history only"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -3812,9 +4318,6 @@ msgstr ""
msgid "November"
msgstr ""
-msgid "Number of access attempts"
-msgstr ""
-
msgid "Oct"
msgstr ""
@@ -3824,15 +4327,17 @@ msgstr ""
msgid "OfSearchInADropdown|Filter"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
-msgstr ""
-
msgid "Only admins"
msgstr ""
@@ -3848,6 +4353,9 @@ msgstr ""
msgid "Oops, are you sure?"
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Open in Xcode"
msgstr ""
@@ -3866,6 +4374,9 @@ msgstr ""
msgid "Operations"
msgstr ""
+msgid "Operations Dashboard"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -3917,15 +4428,24 @@ msgstr ""
msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
msgstr ""
+msgid "Path, transfer, remove"
+msgstr ""
+
msgid "Path:"
msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -3938,9 +4458,15 @@ msgstr ""
msgid "Permissions"
msgstr ""
+msgid "Permissions, LFS, 2FA"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
+msgid "Pick a name"
+msgstr ""
+
msgid "Pipeline"
msgstr ""
@@ -4028,6 +4554,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4049,18 +4578,30 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
+msgid "Pipeline|Commit"
+msgstr ""
+
msgid "Pipeline|Create for"
msgstr ""
msgid "Pipeline|Create pipeline"
msgstr ""
+msgid "Pipeline|Duration"
+msgstr ""
+
msgid "Pipeline|Existing branch name or tag"
msgstr ""
+msgid "Pipeline|Pipeline"
+msgstr ""
+
msgid "Pipeline|Run Pipeline"
msgstr ""
@@ -4070,6 +4611,12 @@ msgstr ""
msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
+msgid "Pipeline|Stages"
+msgstr ""
+
+msgid "Pipeline|Status"
+msgstr ""
+
msgid "Pipeline|Stop pipeline"
msgstr ""
@@ -4106,12 +4653,18 @@ msgstr ""
msgid "Please accept the Terms of Service before continuing."
msgstr ""
+msgid "Please choose a group URL with no special characters."
+msgstr ""
+
msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please fill in a descriptive name for your group."
+msgstr ""
+
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
@@ -4124,6 +4677,9 @@ msgstr ""
msgid "Please try again"
msgstr ""
+msgid "Please use this form to report users to GitLab who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Please wait while we import the repository for you. Refresh at will."
msgstr ""
@@ -4133,9 +4689,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
msgid "Preview"
msgstr ""
+msgid "Preview payload"
+msgstr ""
+
msgid "Prioritize"
msgstr ""
@@ -4163,6 +4725,12 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
@@ -4172,15 +4740,30 @@ msgstr ""
msgid "Profiles|Add status emoji"
msgstr ""
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
msgid "Profiles|Clear status"
msgstr ""
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4193,39 +4776,120 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Learn more"
+msgstr ""
+
+msgid "Profiles|Made a private contribution"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}"
+msgstr ""
+
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Use a private email - %{email}"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
msgid "Profiles|What's your status?"
msgstr ""
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4235,6 +4899,15 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
msgid "Profiles|Your status"
msgstr ""
@@ -4271,6 +4944,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -4298,6 +4974,9 @@ msgstr ""
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -4310,15 +4989,48 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "Projects"
msgstr ""
msgid "Projects shared with %{group_name}"
msgstr ""
+msgid "Projects that belong to a group are prefixed with the group namespace. Existing projects may be moved into a group."
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -4337,6 +5049,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusDashboard|Time"
msgstr ""
@@ -4355,9 +5070,6 @@ msgstr ""
msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
msgstr ""
-msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
-msgstr ""
-
msgid "PrometheusService|Common metrics"
msgstr ""
@@ -4415,6 +5127,9 @@ msgstr ""
msgid "Promote to group label"
msgstr ""
+msgid "Protected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -4448,6 +5163,9 @@ msgstr ""
msgid "Read more"
msgstr ""
+msgid "Read more about environments"
+msgstr ""
+
msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
msgstr ""
@@ -4457,12 +5175,26 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
+msgid "Register U2F device"
+msgstr ""
+
msgid "Register and see your runners for this group."
msgstr ""
@@ -4505,12 +5237,21 @@ msgstr ""
msgid "Remove avatar"
msgstr ""
+msgid "Remove group"
+msgstr ""
+
msgid "Remove priority"
msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Removed group can not be restored!"
+msgstr ""
+
+msgid "Removing group will cause all child projects and resources to be removed."
+msgstr ""
+
msgid "Rename"
msgstr ""
@@ -4523,6 +5264,12 @@ msgstr ""
msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr ""
+msgid "Report abuse to GitLab"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
msgid "Reports|%{failedString} and %{resolvedString}"
msgstr ""
@@ -4574,10 +5321,10 @@ msgstr ""
msgid "Requests Profiles"
msgstr ""
-msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgid "Require all users in this group to setup Two-factor authentication"
msgstr ""
-msgid "Reset git storage health information"
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
msgid "Reset health check access token"
@@ -4595,6 +5342,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -4607,6 +5369,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -4630,9 +5395,27 @@ msgstr ""
msgid "Revoke"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -4642,6 +5425,12 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
msgid "Runners page"
msgstr ""
@@ -4714,6 +5503,9 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
msgid "Search or jump to…"
msgstr ""
@@ -4747,13 +5539,10 @@ msgstr ""
msgid "SearchAutocomplete|in this project"
msgstr ""
-msgid "Seconds before reseting failure information"
-msgstr ""
-
-msgid "Seconds to wait for a storage access attempt"
+msgid "Secret"
msgstr ""
-msgid "Secret:"
+msgid "See metrics"
msgstr ""
msgid "Select"
@@ -4762,6 +5551,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr ""
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -4795,12 +5587,18 @@ msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -4834,7 +5632,13 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up new U2F device"
msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
@@ -4843,10 +5647,31 @@ msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
-msgid "Settings"
+msgid "SetStatusModal|Add status emoji"
+msgstr ""
+
+msgid "SetStatusModal|Clear status"
+msgstr ""
+
+msgid "SetStatusModal|Edit status"
+msgstr ""
+
+msgid "SetStatusModal|Remove status"
+msgstr ""
+
+msgid "SetStatusModal|Set a status"
+msgstr ""
+
+msgid "SetStatusModal|Set status"
msgstr ""
-msgid "Setup a specific Runner automatically"
+msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
+msgstr ""
+
+msgid "SetStatusModal|What's your status?"
+msgstr ""
+
+msgid "Settings"
msgstr ""
msgid "Share"
@@ -4855,6 +5680,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "Sherlock Transactions"
msgstr ""
@@ -4893,6 +5721,9 @@ msgstr ""
msgid "Sign in / Register"
msgstr ""
+msgid "Sign in via 2FA code"
+msgstr ""
+
msgid "Sign out"
msgstr ""
@@ -4920,12 +5751,24 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
+msgid "Something went wrong while fetching comments. Please try again."
+msgstr ""
+
+msgid "Something went wrong while fetching the environments for this merge request. Please try again."
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
@@ -4971,6 +5814,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -4995,6 +5841,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5052,6 +5901,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5094,6 +5946,12 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
+msgid "Start and due date"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5106,6 +5964,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop environment"
+msgstr ""
+
msgid "Stop impersonation"
msgstr ""
@@ -5115,6 +5976,9 @@ msgstr ""
msgid "Stopped"
msgstr ""
+msgid "Stopping this environment is currently not possible as a deployment is in progress"
+msgstr ""
+
msgid "Storage"
msgstr ""
@@ -5124,6 +5988,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -5139,6 +6006,9 @@ msgstr ""
msgid "Subscribe at project level"
msgstr ""
+msgid "Subscribed"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
@@ -5148,6 +6018,12 @@ msgstr ""
msgid "System Info"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -5156,6 +6032,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -5240,6 +6119,12 @@ msgstr ""
msgid "Template"
msgstr ""
+msgid "Templates"
+msgstr ""
+
+msgid "Terminal for environment"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -5258,12 +6143,18 @@ msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
msgid "The collection of events added to the data gathered for that stage."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
@@ -5276,18 +6167,15 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of attempts GitLab will make to access a storage."
-msgstr ""
-
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr ""
-
msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -5315,28 +6203,22 @@ msgstr ""
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
-msgid "The secure token used by the Runner to checkout the project"
-msgstr ""
-
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
-msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
-msgstr ""
-
-msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgid "The tabs below will be removed in a future version"
msgstr ""
-msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time taken by each data entry gathered by that stage."
msgstr ""
-msgid "The time taken by each data entry gathered by that stage."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The update action will time out after 15 minutes. For big repositories, use a clone/push combination."
+msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr ""
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
@@ -5348,6 +6230,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -5357,7 +6242,13 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -5393,6 +6284,12 @@ msgstr ""
msgid "This application will be able to:"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -5408,6 +6305,9 @@ msgstr ""
msgid "This is a confidential issue."
msgstr ""
+msgid "This is a delayed job to run in %{remainingTime}"
+msgstr ""
+
msgid "This is the author's first Merge Request to this project."
msgstr ""
@@ -5441,18 +6341,48 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is archived. Only the complete pipeline can be retried."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
+msgid "This job will automatically run after it's timer finishes. Often they are used for incremental roll-out deploys to production environments. When unscheduled it converts into a manual action."
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -5468,12 +6398,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have a wiki homepage yet"
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This setting can be overridden in each project."
+msgstr ""
+
+msgid "This setting will update the hostname that is used to generate private commit emails. %{learn_more}"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
@@ -5486,9 +6434,15 @@ msgstr ""
msgid "Time before an issue starts implementation"
msgstr ""
+msgid "Time before enforced"
+msgstr ""
+
msgid "Time between merge request creation and merge/close"
msgstr ""
+msgid "Time estimate"
+msgstr ""
+
msgid "Time remaining"
msgstr ""
@@ -5664,12 +6618,24 @@ msgstr ""
msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -5691,6 +6657,9 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
+msgid "Today"
+msgstr ""
+
msgid "Todo"
msgstr ""
@@ -5700,9 +6669,15 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
+msgid "Toggle file browser"
+msgstr ""
+
msgid "Toggle navigation"
msgstr ""
@@ -5715,6 +6690,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
+msgid "Tomorrow"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -5730,9 +6711,15 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Tree view"
+msgstr ""
+
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -5742,21 +6729,45 @@ msgstr ""
msgid "Try again"
msgstr ""
+msgid "Try again?"
+msgstr ""
+
+msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
+msgstr ""
+
msgid "Twitter"
msgstr ""
+msgid "Two-factor authentication"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
+msgid "Undo"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unschedule job"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -5790,13 +6801,16 @@ msgstr ""
msgid "Up to date"
msgstr ""
+msgid "Upcoming"
+msgstr ""
+
msgid "Update"
msgstr ""
msgid "Update now"
msgstr ""
-msgid "Update your group name, description, avatar, and other general settings."
+msgid "Update your group name, description, avatar, and visibility."
msgstr ""
msgid "Updating"
@@ -5811,15 +6825,15 @@ msgstr ""
msgid "Upload file"
msgstr ""
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr ""
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -5841,6 +6855,9 @@ msgstr ""
msgid "Use your global notification setting"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -5850,10 +6867,49 @@ msgstr ""
msgid "User map"
msgstr ""
-msgid "Users"
+msgid "UserProfile|Activity"
+msgstr ""
+
+msgid "UserProfile|Already reported for abuse"
+msgstr ""
+
+msgid "UserProfile|Contributed projects"
+msgstr ""
+
+msgid "UserProfile|Edit profile"
+msgstr ""
+
+msgid "UserProfile|Groups"
+msgstr ""
+
+msgid "UserProfile|Most Recent Activity"
+msgstr ""
+
+msgid "UserProfile|Overview"
+msgstr ""
+
+msgid "UserProfile|Personal projects"
msgstr ""
-msgid "User|Current status"
+msgid "UserProfile|Report abuse"
+msgstr ""
+
+msgid "UserProfile|Snippets"
+msgstr ""
+
+msgid "UserProfile|Subscribe"
+msgstr ""
+
+msgid "UserProfile|This user has a private profile"
+msgstr ""
+
+msgid "UserProfile|View all"
+msgstr ""
+
+msgid "UserProfile|View user in admin area"
+msgstr ""
+
+msgid "Users"
msgstr ""
msgid "Variables"
@@ -5874,6 +6930,15 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
+msgid "View app"
+msgstr ""
+
+msgid "View deployment"
+msgstr ""
+
msgid "View file @ "
msgstr ""
@@ -5901,9 +6966,15 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View the documentation"
+msgstr ""
+
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level"
+msgstr ""
+
msgid "Visibility level:"
msgstr ""
@@ -5931,6 +7002,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We heard back from your U2F device. You have been authenticated."
+msgstr ""
+
msgid "We want to be sure it is you, please confirm you are not a robot."
msgstr ""
@@ -5946,6 +7020,12 @@ msgstr ""
msgid "When enabled, users cannot use GitLab until the terms have been accepted."
msgstr ""
+msgid "Who can see this group?"
+msgstr ""
+
+msgid "Who will be able to see this group?"
+msgstr ""
+
msgid "Wiki"
msgstr ""
@@ -6093,6 +7173,9 @@ msgstr ""
msgid "Yes, let me map Google Code users to full names or GitLab users."
msgstr ""
+msgid "Yesterday"
+msgstr ""
+
msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
@@ -6120,9 +7203,6 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
-msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
-msgstr ""
-
msgid "You can easily contribute to them by requesting to join these groups."
msgstr ""
@@ -6141,15 +7221,27 @@ msgstr ""
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
+msgstr ""
+
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any subscriptions yet"
+msgstr ""
+
msgid "You don't have any applications"
msgstr ""
msgid "You don't have any authorized applications"
msgstr ""
+msgid "You don't have any deployments right now."
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
@@ -6162,10 +7254,16 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
+msgid "You need permission."
msgstr ""
-msgid "You need permission."
+msgid "You need to register a two-factor authentication app before you can set up a U2F device."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
msgstr ""
msgid "You will not get any notifications via email"
@@ -6222,12 +7320,18 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left."
+msgstr ""
+
msgid "Your applications (%{size})"
msgstr ""
msgid "Your authorized applications"
msgstr ""
+msgid "Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer)."
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -6237,6 +7341,9 @@ msgstr ""
msgid "Your comment will not be visible to the public."
msgstr ""
+msgid "Your device was successfully set up! Give it a name and register it with the GitLab server."
+msgstr ""
+
msgid "Your groups"
msgstr ""
@@ -6261,6 +7368,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -6284,12 +7397,18 @@ msgstr ""
msgid "enabled"
msgstr ""
+msgid "error code:"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
msgid "for this project"
msgstr ""
+msgid "from"
+msgstr ""
+
msgid "here"
msgstr ""
@@ -6302,6 +7421,12 @@ msgstr ""
msgid "importing"
msgstr ""
+msgid "issue boards"
+msgstr ""
+
+msgid "latest deployment"
+msgstr ""
+
msgid "latest version"
msgstr ""
@@ -6364,6 +7489,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -6391,9 +7519,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -6415,6 +7549,9 @@ msgstr ""
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -6433,9 +7570,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -6460,6 +7606,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -6478,6 +7627,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr ""
@@ -6509,6 +7661,11 @@ msgstr ""
msgid "remove due date"
msgstr ""
+msgid "reply"
+msgid_plural "replies"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "source"
msgstr ""
diff --git a/locale/gl_ES/gitlab.po b/locale/gl_ES/gitlab.po
index 401895f0a16..5e2dbce1104 100644
--- a/locale/gl_ES/gitlab.po
+++ b/locale/gl_ES/gitlab.po
@@ -13,11 +13,14 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: gl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 09:29\n"
-msgid " and"
+msgid " Status"
msgstr ""
+msgid " and"
+msgstr " e"
+
msgid " degraded on %d point"
msgid_plural " degraded on %d points"
msgstr[0] ""
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] "%d exportador"
msgstr[1] "%d exportadores"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d incidencia"
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -110,9 +118,15 @@ msgstr[1] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -156,14 +170,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] ""
msgstr[1] ""
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1st contribution!"
msgstr ""
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -312,13 +351,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr ""
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -390,6 +435,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -552,13 +612,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -648,12 +720,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr ""
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -798,6 +882,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr ""
msgid "Browse files"
msgstr ""
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1502,10 +1622,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1814,9 +1958,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr ""
msgid "CommitMessage|Add %{file_name}"
msgstr ""
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr ""
@@ -1947,16 +2094,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr ""
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr ""
-
msgid "CreateTag|Tag"
msgstr ""
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -2274,6 +2442,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2283,6 +2457,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2690,6 +2912,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -2906,6 +3176,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,13 +3701,10 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
-msgid "Go to your fork"
+msgid "Go to"
msgstr ""
-msgid "GoToYourFork|Fork"
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Google Code import"
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3445,6 +3860,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3457,19 +3875,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,22 +4212,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
msgstr ""
-msgid "Koding"
+msgid "Job|The artifacts were removed"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3820,6 +4349,9 @@ msgstr ""
msgid "Last commit"
msgstr ""
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr ""
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr ""
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr ""
@@ -4527,18 +5214,26 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr ""
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4911,33 +5666,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4947,6 +5777,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -5007,6 +5852,9 @@ msgstr ""
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -5034,18 +5882,48 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5552,10 +6648,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr ""
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5639,6 +6750,9 @@ msgstr ""
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,19 +6765,22 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
-msgid "SetPasswordToCloneLink|set a password"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
-msgid "Settings"
+msgid "SetPasswordToCloneLink|set a password"
msgstr ""
-msgid "Setup a specific Runner automatically"
+msgid "Settings"
msgstr ""
msgid "Share"
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5962,6 +7106,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
@@ -6150,6 +7318,9 @@ msgstr ""
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr ""
msgid "Upload file"
msgstr ""
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr ""
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6796,6 +8102,9 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr ""
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr ""
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr ""
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr ""
@@ -7246,17 +8561,15 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] ""
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr ""
@@ -7757,6 +9140,11 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/he_IL/gitlab.po b/locale/he_IL/gitlab.po
new file mode 100644
index 00000000000..9aadf885770
--- /dev/null
+++ b/locale/he_IL/gitlab.po
@@ -0,0 +1,9330 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Hebrew\n"
+"Language: he_IL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: he\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 09:26\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
diff --git a/locale/id_ID/gitlab.po b/locale/id_ID/gitlab.po
index 27b6e13db7e..641886e65b0 100644
--- a/locale/id_ID/gitlab.po
+++ b/locale/id_ID/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: id\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:39\n"
+"PO-Revision-Date: 2018-10-02 09:28\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -42,6 +45,14 @@ msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -58,10 +69,6 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -94,9 +101,15 @@ msgstr[0] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -138,13 +151,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -173,6 +188,10 @@ msgid "1 closed merge request"
msgid_plural "%d closed merge requests"
msgstr[0] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -189,6 +208,14 @@ msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] ""
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+
msgid "1st contribution!"
msgstr ""
@@ -222,6 +249,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -237,12 +267,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -285,13 +321,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -324,15 +363,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr ""
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -351,6 +390,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -363,6 +405,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -381,6 +429,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -429,6 +480,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -474,7 +531,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -525,13 +582,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -621,12 +690,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -645,13 +720,16 @@ msgstr ""
msgid "Are you sure?"
msgstr ""
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -684,7 +762,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -711,6 +789,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -771,6 +852,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -780,6 +864,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -804,9 +891,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -846,6 +930,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -873,9 +960,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -951,6 +1044,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1120,6 +1216,9 @@ msgstr ""
msgid "Browse files"
msgstr ""
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1132,6 +1231,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1144,9 +1246,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1159,39 +1258,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1219,6 +1312,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1276,6 +1372,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1438,6 +1540,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1447,6 +1552,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1456,12 +1564,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1474,10 +1591,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1504,6 +1621,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1528,6 +1651,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1540,6 +1666,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1555,6 +1684,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1579,6 +1711,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1591,15 +1726,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1624,12 +1750,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1672,6 +1792,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1681,6 +1804,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1693,6 +1822,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1708,9 +1840,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1747,12 +1876,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1765,9 +1900,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1786,9 +1927,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1798,6 +1936,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1845,6 +1986,9 @@ msgstr ""
msgid "CommitMessage|Add %{file_name}"
msgstr ""
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr ""
@@ -1917,16 +2061,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2016,6 +2157,9 @@ msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2049,6 +2193,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2154,9 +2307,6 @@ msgstr ""
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr ""
-
msgid "CreateTag|Tag"
msgstr ""
@@ -2172,6 +2322,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2184,6 +2337,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2193,6 +2349,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2202,6 +2361,9 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2214,6 +2376,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -2244,6 +2409,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2253,6 +2424,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2268,6 +2442,9 @@ msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2428,12 +2605,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2446,9 +2629,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2548,7 +2743,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2608,12 +2803,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2644,6 +2860,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2659,6 +2878,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2692,6 +2914,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2707,6 +2932,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2731,6 +2980,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2743,6 +2995,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2755,6 +3013,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2797,6 +3058,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2854,6 +3118,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -2875,6 +3142,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -2887,9 +3157,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -2902,6 +3181,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2911,6 +3193,18 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2938,16 +3232,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -2969,6 +3265,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -2984,6 +3283,9 @@ msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -2999,6 +3301,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3164,33 +3469,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3239,6 +3643,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3260,13 +3667,10 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
-msgid "Go to your fork"
+msgid "Go to"
msgstr ""
-msgid "GoToYourFork|Fork"
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Google Code import"
@@ -3329,13 +3733,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3350,6 +3754,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3413,6 +3826,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3425,19 +3841,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3473,6 +3889,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3495,21 +3920,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3519,6 +3968,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3576,6 +4028,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3585,6 +4040,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3603,18 +4061,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3625,6 +4098,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3643,12 +4122,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3688,22 +4176,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
msgstr ""
-msgid "Koding"
+msgid "Job|Show complete raw"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3775,6 +4299,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3785,6 +4312,9 @@ msgstr ""
msgid "Last commit"
msgstr ""
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3809,6 +4339,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3836,6 +4369,61 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3848,9 +4436,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3869,6 +4463,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3896,6 +4493,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3950,9 +4550,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4001,15 +4613,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4025,6 +4637,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4127,9 +4742,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4148,9 +4778,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -4169,9 +4820,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4220,6 +4868,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4308,12 +4959,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4335,6 +4995,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4347,6 +5010,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4359,15 +5025,27 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr ""
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4386,6 +5064,9 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4419,6 +5100,9 @@ msgstr ""
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr ""
@@ -4491,18 +5175,25 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4566,9 +5257,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4599,9 +5302,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4620,6 +5329,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -4707,6 +5419,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4728,6 +5443,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4776,12 +5494,6 @@ msgstr ""
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4821,6 +5533,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4851,18 +5572,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4875,33 +5626,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4911,6 +5737,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4944,6 +5782,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -4971,6 +5812,9 @@ msgstr ""
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -4998,18 +5842,48 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5049,6 +5923,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5181,6 +6058,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5199,6 +6124,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5238,12 +6169,25 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5295,6 +6239,18 @@ msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5304,6 +6260,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5355,9 +6359,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5370,6 +6386,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5401,9 +6420,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5413,6 +6450,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5428,12 +6480,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5500,12 +6561,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5515,10 +6606,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5533,6 +6627,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr ""
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5566,6 +6663,12 @@ msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5575,6 +6678,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5602,6 +6708,9 @@ msgstr ""
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5614,19 +6723,22 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
-msgid "SetPasswordToCloneLink|set a password"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
-msgid "Settings"
+msgid "SetPasswordToCloneLink|set a password"
msgstr ""
-msgid "Setup a specific Runner automatically"
+msgid "Settings"
msgstr ""
msgid "Share"
@@ -5638,6 +6750,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5711,12 +6826,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5732,13 +6853,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5792,6 +6919,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5822,6 +6952,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5852,6 +6985,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5882,6 +7018,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5924,6 +7063,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5957,6 +7099,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -5990,6 +7135,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -5997,6 +7145,9 @@ msgstr[0] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6078,6 +7229,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6093,6 +7250,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6102,6 +7262,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
@@ -6111,6 +7274,9 @@ msgstr ""
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
@@ -6138,6 +7304,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -6165,6 +7334,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -6177,6 +7349,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -6192,6 +7367,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6201,6 +7382,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6210,9 +7394,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6249,6 +7448,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6300,9 +7511,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6312,6 +7544,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6327,15 +7562,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6536,12 +7786,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6569,7 +7831,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6584,6 +7846,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6599,6 +7864,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6626,6 +7894,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6641,24 +7918,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6695,9 +7987,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6722,15 +8020,15 @@ msgstr ""
msgid "Upload file"
msgstr ""
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr ""
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6746,6 +8044,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6755,6 +8056,9 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6788,6 +8092,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7079,16 +8386,19 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7103,9 +8413,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr ""
@@ -7115,9 +8422,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr ""
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7127,6 +8431,12 @@ msgstr ""
msgid "You need permission."
msgstr ""
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr ""
@@ -7205,16 +8515,15 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-
msgid "assign yourself"
msgstr ""
@@ -7239,70 +8548,86 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7335,10 +8660,15 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7368,13 +8698,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
-msgstr ""
-
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7383,9 +8710,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7416,12 +8740,13 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7434,6 +8759,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7450,17 +8781,6 @@ msgstr[0] ""
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7479,12 +8799,19 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7494,9 +8821,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7525,6 +8858,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7573,6 +8909,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7612,9 +8951,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7636,9 +8981,20 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7657,9 +9013,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7684,6 +9049,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7702,6 +9070,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr ""
@@ -7711,6 +9082,10 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -7727,6 +9102,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7748,6 +9126,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index 14335f97bd6..3b43d563dc5 100644
--- a/locale/it/gitlab.po
+++ b/locale/it/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: it\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 09:28\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] "%d metrica"
msgstr[1] "%d metriche"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -110,9 +118,15 @@ msgstr[1] "%{count} partecipanti"
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -156,14 +170,15 @@ msgstr "%{text} è disponibile"
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} più"
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] "1 pipeline"
msgstr[1] "%d pipeline"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1st contribution!"
msgstr "Primo contributo!"
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Un insieme di grafici riguardo la Continuous Integration"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -312,13 +351,16 @@ msgstr "Token di accesso"
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "L'accesso agli storages è stato temporaneamente disabilitato per consentire il mount di ripristino. Resetta le info d'archiviazione dopo che l'issue è stato risolto per consentire nuovamente l'accesso."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr "Aggiungi Licenza"
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -390,6 +435,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr "Pagina di stato"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -552,13 +612,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr "Errore durante il recupero dei dati della barra laterale"
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -648,12 +720,18 @@ msgstr "Aprile"
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Sei sicuro di voler cancellare questa pipeline programmata?"
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr "Sei sicuro?"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr "Artefatti"
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr "Log di autenticazione"
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr "Autore"
@@ -798,6 +882,9 @@ msgstr "Farà automaticamente le build, i test e i rilasci della tua applicazion
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "Approfondisci: %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr "Disponibile"
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr "Esplora Files"
msgid "Browse files"
msgstr "Guarda i files"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr "Jobs"
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr "Clona repository"
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr "API URL"
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr "Applicazioni"
@@ -1502,10 +1622,10 @@ msgstr "Certificato CA"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Certificate Authority bundle (formato PEM)"
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr "Gitlab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr "Ingresso"
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr "Installa"
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr "Tipo di macchina"
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1814,9 +1958,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr "Commit"
msgid "CommitMessage|Add %{file_name}"
msgstr "Aggiungi %{file_name}"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "Commits"
@@ -1947,16 +2094,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr "Guida per contribuire"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr "Crea nuovo..."
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr "Fork"
-
msgid "CreateTag|Tag"
msgstr "Tag"
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr "Timezone del Cron"
msgid "Cron syntax"
msgstr "Sintassi Cron"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr "Eventi-Notifica personalizzati"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "I livelli di notifica personalizzati sono uguali a quelli di partecipazione. Con i livelli di notifica personalizzati riceverai anche notifiche per gli eventi da te scelti %{notification_link}."
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "Statistiche Cicliche"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "Codice"
@@ -2274,6 +2442,12 @@ msgstr "Tutti"
msgid "DashboardProjects|Personal"
msgstr "Personale"
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr "Dic"
@@ -2283,6 +2457,9 @@ msgstr "Dicembre"
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr "Definisci un patter personalizzato mediante la sintassi cron"
msgid "Delete"
msgstr "Elimina"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr "Dettagli"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "Nome cartella"
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr "Ambiente"
msgid "Environments|Environments"
msgstr "Ambienti"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr "Job"
@@ -2690,6 +2912,9 @@ msgstr "Ancora nessuna chiave di rilascio"
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr "Aggiornato"
msgid "Environments|You don't have any environments right now."
msgstr "Attualmente non hai alcun ambiente."
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Errore durante l'attivazione/disattivazione della sottoscrizione per l'iscrizione"
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "Impossibile rimuovere la pipeline pianificata"
@@ -2906,6 +3176,9 @@ msgstr "Febbraio"
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "Files"
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Filtra per messaggio di commit"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "Trova in percorso"
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr "Primo"
msgid "FirstPushedBy|pushed by"
msgstr "Push di"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Fork"
-msgstr[1] "Forks"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "Fork da"
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr "Dalla creazione di un issue fino al rilascio in produzione"
msgid "From merge request merge until deploy to production"
msgstr "Dalla richiesta di merge fino effettua il merge fino al rilascio in produzione"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,14 +3701,11 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "Vai il tuo fork"
-
-msgid "GoToYourFork|Fork"
-msgstr "Fork"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr ""
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Blocca la condivisione di un progetto di %{group} con altri gruppi"
@@ -3445,6 +3860,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3457,19 +3875,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "Intervallo di Pattern"
msgid "Introducing Cycle Analytics"
msgstr "Introduzione delle Analisi Cicliche"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,6 +4212,48 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
msgid "Jul"
msgstr "Lug"
@@ -3734,12 +4266,6 @@ msgstr "Giu"
msgid "June"
msgstr "Giugno"
-msgid "Koding"
-msgstr ""
-
-msgid "Koding Dashboard"
-msgstr ""
-
msgid "Kubernetes"
msgstr ""
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "L'ultimo %d giorno"
@@ -3820,6 +4349,9 @@ msgstr "Ultima Pipeline"
msgid "Last commit"
msgstr "Ultimo Commit"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr "Ultima modifica %{date}"
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "aggiungi una chiave SSH"
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr "Nessuna Repository"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "Nessuna pianificazione"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr "Nessuno"
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr "Dati insufficienti "
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr "Pipeline fallita"
msgid "NotificationEvent|Merge merge request"
msgstr "Completa la richiesta di merge"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "Nuovo issue"
@@ -4527,18 +5214,26 @@ msgstr "Filtra"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr "Solo i membri del progetto possono commentare."
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr "Panoramica"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "Proprietario"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr "Pipeline"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "Stato della Pipeline"
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr "con più stadi"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr "Preferenze"
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr "Profilo"
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr "Account pianificato per la rimozione."
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr "Elimina account"
@@ -4911,33 +5666,108 @@ msgstr "Eliminare il tuo account?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr "Password non valida"
msgid "Profiles|Invalid username"
msgstr "Username non valido"
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Inserisci il tuo %{confirmationValue} per confermare:"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "Non hai i permessi per eliminare questo utente."
@@ -4947,6 +5777,18 @@ msgstr "Devi trasferire la proprietà o eliminare questi gruppi prima che tu pos
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Il tuo account è attualmente proprietario in questi gruppi:"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr "Il Progetto '%{project_name}' è stato aggiornato con successo."
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "L'accesso al progetto dev'esser fornito esplicitamente ad ogni utente"
@@ -5007,6 +5852,9 @@ msgstr "Esportazione del progetto iniziata. Un link di download sarà inviato vi
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "Iscriviti"
@@ -5034,18 +5882,48 @@ msgstr "Mai"
msgid "ProjectLifecycle|Stage"
msgstr "Stadio"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr "Qualcosa è andato storto dalla nostra parte."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Siamo spiacenti, non ci sono progetti che corrispondono alla tua ricerca"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr "Public - Chiunque può accedere a questo progetto senza alcuna autentica
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr "Leggimi"
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr "Rimuovi progetto"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr "Chiavi SSH"
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5552,10 +6648,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr "Seleziona formato d'archivio"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr "Seleziona una branch di destinazione"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr "Set"
@@ -5639,6 +6750,9 @@ msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a travÃ
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,21 +6765,24 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
-msgstr "Configura Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "imposta una password"
msgid "Settings"
msgstr "Impostazioni"
-msgid "Setup a specific Runner automatically"
-msgstr ""
-
msgid "Share"
msgstr ""
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5962,6 +7106,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr "inizia una %{new_merge_request} con queste modifiche"
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "Lo stadio di programmazione mostra il tempo trascorso dal primo commit alla creazione di una richiesta di merge (MR). I dati saranno aggiunti una volta che avrai creato la prima richiesta di merge."
@@ -6150,6 +7318,9 @@ msgstr "L'insieme di eventi aggiunti ai dati raccolti per quello stadio."
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "La relazione del fork è stata rimossa"
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "Il ciclo vitale della fase di sviluppo."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Lo stadio di pianificazione mostra il tempo trascorso dal primo commit al suo step precedente. Questo periodo sarà disponibile automaticamente nel momento in cui farai il primo commit."
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "Lo stadio di revisione mostra il tempo tra una richiesta di merge al suo svolgimento effettivo. Questo dato sarà disponibile appena avrai completato una MR (Merger Request)"
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "Lo stadio di pre-rilascio mostra il tempo che trascorre da una MR (Richiesta di Merge) completata al suo rilascio in ambiente di produzione. Questa informazione sarà disponibile dal tuo primo rilascio in produzione"
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Lo stadio di test mostra il tempo che ogni Pipeline impiega per essere eseguita in ogni Richiesta di Merge correlata. L'informazione sarà disponibile automaticamente quando la tua prima Pipeline avrà finito d'esser eseguita."
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "Il tempo aggregato relativo eventi/data entry raccolto in quello stadio."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "Il valore falsato nel mezzo di una serie di dati osservati. ES: tra 3,5,9 il mediano è 5. Tra 3,5,7,8 il mediano è (5+7)/2 quindi 6."
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr "Questo significa che non è possibile effettuare push di codice fino a c
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr "Carica un nuovo file"
msgid "Upload file"
msgstr "Carica file"
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr "clicca per caricare"
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6796,6 +8102,9 @@ msgstr "Usa le tue impostazioni globali "
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr "Puoi aggiungere files solo quando sei in una branch"
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr "Hai raggiunto il tuo limite di progetto"
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr "Devi accedere per porre una star al progetto"
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr "Necessiti del permesso."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "Non riceverai alcuna notifica via email"
@@ -7246,17 +8561,15 @@ msgstr "Il tuo nome"
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr "fa"
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] "giorni"
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "Nuova richiesta di merge"
@@ -7757,6 +9140,11 @@ msgstr "Notifiche via email"
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
index acd78058d50..a254bfba027 100644
--- a/locale/ja/gitlab.po
+++ b/locale/ja/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ja\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:39\n"
+"PO-Revision-Date: 2018-10-02 11:46\n"
+
+msgid " Status"
+msgstr " ステータス"
msgid " and"
msgstr " ã¨"
@@ -42,6 +45,14 @@ msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d exporter"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d個ã®èª²é¡Œ"
@@ -58,10 +69,6 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] "%d メトリクス"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] "%d件ã®ã‚¹ãƒ†ãƒ¼ã‚¸æ¸ˆã¿å¤‰æ›´"
@@ -72,7 +79,7 @@ msgstr[0] "%d件ã®æœªã‚¹ãƒ†ãƒ¼ã‚¸ã®å¤‰æ›´"
msgid "%d vulnerability"
msgid_plural "%d vulnerabilities"
-msgstr[0] ""
+msgstr[0] "%d ã®è„†å¼±æ€§"
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
@@ -94,9 +101,15 @@ msgstr[0] "%{count} 人ã®å‚加者"
msgid "%{filePath} deleted"
msgstr "%{filePath} ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr "%{group_docs_link_start}グループ%{group_docs_link_end}を使用ã™ã‚‹ã¨ã€è¤‡æ•°ã®ãƒ—ロジェクトを管ç†ã—ã¦å…±åŒä½œæ¥­ã‚’è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚グループã®ãƒ¡ãƒ³ãƒãƒ¼ã¯ã€æ‰€å±žã™ã‚‹ãƒ—ロジェクトã®ã™ã¹ã¦ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚"
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 開始"
@@ -104,7 +117,7 @@ msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr "%{lock_path} ã¯GitLab ユーザー %{lock_user_id} ã«ã‚ˆã£ã¦ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™"
msgid "%{name}'s avatar"
-msgstr ""
+msgstr "%{name}ã®ã‚¢ãƒã‚¿ãƒ¼"
msgid "%{nip_domain} can be used as an alternative to a custom domain."
msgstr "%{nip_domain} ã¯ã€ã‚«ã‚¹ã‚¿ãƒ ãƒ‰ãƒ¡ã‚¤ãƒ³ã®ä»£æ›¿ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã™ã€‚"
@@ -130,7 +143,7 @@ msgstr[0] "%{storage_name}: 失敗ã—ãŸã‚¢ã‚¯ã‚»ã‚¹ã®è©¦è¡Œå›žæ•° %{failed_att
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
-msgstr[0] ""
+msgstr[0] "%{text} %{files} ファイル"
msgid "%{text} is available"
msgstr "%{text} ãŒåˆ©ç”¨ã§ãã¾ã™ã€‚"
@@ -138,13 +151,15 @@ msgstr "%{text} ãŒåˆ©ç”¨ã§ãã¾ã™ã€‚"
msgid "%{title} changes"
msgstr "%{title} ã®å¤‰æ›´"
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "%{unstaged} 件ã®æœªã‚¹ãƒ†ãƒ¼ã‚¸ã®å¤‰æ›´ã¨ã€ %{staged} 件ã®ã‚¹ãƒ†ãƒ¼ã‚¸æ¸ˆã¿å¤‰æ›´"
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr "+ 他 %{moreCount} 件"
@@ -173,6 +188,10 @@ msgid "1 closed merge request"
msgid_plural "%d closed merge requests"
msgstr[0] "%d件ã®ã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚ŒãŸãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] "%dグループ"
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] "%d件ã®ãƒžãƒ¼ã‚¸ã•ã‚ŒãŸãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
@@ -189,6 +208,14 @@ msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d 個ã®ãƒ‘イプライン"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] "%dユーザー"
+
msgid "1st contribution!"
msgstr "最åˆã®è²¢çŒ®!"
@@ -211,38 +238,47 @@ msgid "404|Please contact your GitLab administrator if you think this is a mista
msgstr "ã“ã®ãƒšãƒ¼ã‚¸ãŒæ­£ã—ããªã„å ´åˆã€GitLab 管ç†è€…ã«é€£çµ¡ã—ã¦ãã ã•ã„。"
msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> 㯠johnsmith@example.com ã«ã‚ˆã‚‹å…¨ã¦ã®èª²é¡Œã¨ã‚³ãƒ¡ãƒ³ãƒˆã« \"By <a href=\"#\">@johnsmith</a>\" を追加ã—ã¾ã™ã€‚ã¾ãŸã€ <a href=\"#\">@johnsmith</a> を元々 johnsmith@example.com ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ãŸå…¨ã¦ã®èª²é¡Œã®æ‹…当者ã¨ã—ã¦è¨­å®šã—ã¾ã™ã€‚"
msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"John Smith\"</code> ã¯ã€johnsmith@example.com ã«ã‚ˆã£ã¦å…ƒã€…作æˆã•ã‚ŒãŸå…¨ã¦ã®èª²é¡Œã¨ã‚³ãƒ¡ãƒ³ãƒˆã« \"By John Smith\" を追加ã—ã¾ã™ã€‚"
msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> ã¯ã€johnsmith@example.com ãŒä½œæˆã—ãŸå…¨ã¦ã®èª²é¡Œã¨ã‚³ãƒ¡ãƒ³ãƒˆã« \"By johnsm...@example.com\" を追加ã—ã¾ã™ã€‚ã“ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚„ユーザーåã‚’éš ã—ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ—ライãƒã‚·ãƒ¼ã‚’ä¿è­·ã•ã‚Œã¾ã™ã€‚"
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> ã¯ã€johnsmith@example.com ãŒä½œæˆã—ãŸå…¨ã¦ã®èª²é¡Œã¨ã‚³ãƒ¡ãƒ³ãƒˆã« \"By <a href=\"#\">johnsmith@example.com</a>\" を追加ã—ã¾ã™ã€‚デフォルトã§ã€ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚„ユーザーåã‚’éš ã—ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ—ライãƒã‚·ãƒ¼ã‚’ä¿è­·ã•ã‚Œã¾ã™ã€‚メールアドレスを全ã¦è¡¨ç¤ºã—ãŸã„å ´åˆã€ã“ã®æ–¹æ³•ã‚’指定ã—ã¦ãã ã•ã„。"
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
msgstr ""
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
-msgstr ""
+msgstr "<strong>%{created_count}</strong>を作æˆã€<strong>%{closed_count}</strong>個をクローズã—ã¾ã—ãŸã€‚"
msgid "<strong>%{group_name}</strong> group members"
-msgstr ""
+msgstr "<strong>%{group_name}</strong> グループã®ãƒ¡ãƒ³ãƒãƒ¼"
msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
-msgstr ""
+msgstr "<strong>%{pushes}</strong>回ã®ãƒ—ッシュã€<strong>%{commits}</strong>回以上ã®ã‚³ãƒŸãƒƒãƒˆãŒè²¢çŒ®è€…<strong>%{people}</strong>ã«ã‚ˆã£ã¦è¡Œã‚ã‚Œã¾ã—ãŸã€‚"
msgid "<strong>Removes</strong> source branch"
msgstr "ソースブランãƒã‚’<strong>削除</strong>"
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
-msgstr "「Runnerã€ã¯ã‚¸ãƒ§ãƒ–を実行ã™ã‚‹ãƒ—ロセスã§ã™ã€‚å¿…è¦ãªæ•°ã® Runner ã‚’ä»»æ„ã«ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—ã§ãã¾ã™ã€‚"
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "CIã«ã¤ã„ã¦ã®ã‚°ãƒ©ãƒ•"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "フォークã«æ–°ã—ã„ブランãƒãŒä½œæˆã•ã‚Œã€æ–°ã—ã„マージリクエストãŒé–‹å§‹ã—ã¾ã™ã€‚"
@@ -256,10 +292,10 @@ msgid "A user with write access to the source branch selected this option"
msgstr "ã“ã®ã‚ªãƒ—ションをé¸æŠžã—ãŸã‚½ãƒ¼ã‚¹ãƒ–ランãƒã¸ã®æ›¸ãè¾¼ã¿ã‚’許å¯ã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼"
msgid "About GitLab"
-msgstr ""
+msgstr "GitLab ã«ã¤ã„ã¦"
msgid "About GitLab CE"
-msgstr ""
+msgstr "GitLab CE ã«ã¤ã„ã¦"
msgid "About auto deploy"
msgstr "自動デプロイã«ã¤ã„ã¦"
@@ -283,16 +319,19 @@ msgid "Access Tokens"
msgstr "アクセス トークン"
msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr "アクセスãŒæ‹’å¦ã•ã‚Œã¾ã—ãŸï¼ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã«ãƒ‡ãƒ—ロイキーを追加ã§ãã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
+
+msgid "Access expiration date"
msgstr ""
msgid "Access to '%{classification_label}' not allowed"
-msgstr ""
+msgstr "'%{classification_label}'ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "mount ã«ã‚ˆã£ã¦å¾©æ—§ã§ãるよã†ã«ã€å¤±æ•—ãŒç™ºç”Ÿã—ã¦ã„るストレージã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’一時的ã«æŠ‘æ­¢ã—ã¾ã—ãŸã€‚å†åº¦ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ãŸã‚ã«ã¯ã€å•é¡Œã‚’解決ã—ã¦ã‹ã‚‰ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸æƒ…報をリセットã—ã¦ãã ã•ã„。"
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
-msgstr ""
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr "Runner トークンã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã€ãƒ‘イプラインã®è¨­å®šã‚’カスタマイズã€ãã—ã¦ãƒ‘イプラインã®çŠ¶æ…‹ã¨ã‚«ãƒãƒ¬ãƒƒã‚¸ãƒ¬ãƒãƒ¼ãƒˆã‚’閲覧ã—ã¾ã™ã€‚"
msgid "Account"
msgstr "アカウント"
@@ -324,17 +363,17 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr "Kubernetes クラスターを追加"
-msgid "Add License"
-msgstr "ライセンスを追加"
-
msgid "Add Readme"
msgstr "Readmeを追加"
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr "ã™ã¹ã¦ã®ãƒ¡ãƒ¼ãƒ«ã«è¡¨ç¤ºã™ã‚‹ãƒ†ã‚­ã‚¹ãƒˆã‚’追加ã—ã¾ã™ã€‚ ãŸã ã—ã€%{character_limit} 文字ã®åˆ¶é™ãŒã‚ã‚Šã¾ã™ã€‚"
+
+msgid "Add license"
msgstr ""
msgid "Add new application"
-msgstr ""
+msgstr "æ–°ã—ã„アプリケーションを追加"
msgid "Add new directory"
msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’追加"
@@ -349,6 +388,9 @@ msgid "Add user(s) to the group:"
msgstr ""
msgid "Add users to group"
+msgstr "ユーザーをグループã¸è¿½åŠ "
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
msgid "Additional text"
@@ -358,11 +400,17 @@ msgid "Admin Area"
msgstr ""
msgid "Admin Overview"
-msgstr ""
+msgstr "管ç†è€…用概è¦"
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "å…¨ã¦ã®ã‚¸ãƒ§ãƒ–ã‚’åœæ­¢"
@@ -381,6 +429,9 @@ msgstr "å…¨ã¦ã®ã‚¸ãƒ§ãƒ–ã‚’åœæ­¢ã—ã¾ã™ã€‚ã“ã‚Œã«ã‚ˆã‚Šç¾åœ¨å®Ÿè¡Œä¸­ã®
msgid "AdminHealthPageLink|health page"
msgstr "ステータスページ"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr "削除"
@@ -429,11 +480,14 @@ msgstr "ã™ã¹ã¦ã®å¤‰æ›´ãŒã‚³ãƒŸãƒƒãƒˆã•ã‚Œã¦ã„ã¾ã™"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "空ã®ãƒ—ロジェクトã€ãƒ†ãƒ³ãƒ—レートã‹ã‚‰ã€ã¾ãŸã¯ã‚¤ãƒ³ãƒãƒ¼ãƒˆæ™‚ã«ã™ã¹ã¦ã®æ©Ÿèƒ½ãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã¾ã™ãŒã€å¾Œã§ãƒ—ロジェクト設定ã§ç„¡åŠ¹ã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+msgid "All users"
+msgstr "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
msgid "Allow commits from members who can merge to the target branch."
msgstr "ターゲットブランãƒã«ãƒžãƒ¼ã‚¸ã§ãるメンãƒãƒ¼ã‹ã‚‰ã®ã‚³ãƒŸãƒƒãƒˆã‚’許å¯ã—ã¾ã™ã€‚"
msgid "Allow public access to pipelines and job details, including output logs and artifacts"
-msgstr ""
+msgstr "パイプラインã«åŠ ãˆã€ãƒ­ã‚°ã‚„æˆæžœç‰©ãªã©ã®ã‚ˆã†ãªã‚¸ãƒ§ãƒ–ã®è©³ç´°ãªæƒ…å ±ã«å¯¾ã—ã¦ã€ãƒ‘ブリックレベルã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã—ã¾ã™ã€‚"
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Asciidocドキュメントã§ã®PlantUML図ã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ã‚’許å¯ã—ã¾ã™ã€‚"
@@ -445,58 +499,61 @@ msgid "Allows you to add and manage Kubernetes clusters."
msgstr "Kubernetes クラスターを追加ãŠã‚ˆã³ç®¡ç†ã§ãã¾ã™ã€‚"
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
-msgstr ""
+msgstr "\"Issuer\" ã¾ãŸã¯ \"Relying party trust identifier\" ã¨ã‚‚呼ã°ã‚Œã¾ã™"
msgid "Also called \"Relying party service URL\" or \"Reply URL\""
-msgstr ""
+msgstr "\"Relying party service URL\" ã¾ãŸã¯ \"Reply URL\" ã¨ã‚‚呼ã°ã‚Œã¾ã™"
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr ""
+msgstr "ã‚ã‚‹ã„ã¯ã€ %{personal_access_token_link} を使用ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚パーソナルアクセストークンを作æˆã™ã‚‹éš›ã«ã€<code>repo</code> スコープをé¸æŠžã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ã“ã‚Œã«ã‚ˆã‚Šã€æŽ¥ç¶šå¯èƒ½ãªå…¬é–‹ãƒªãƒã‚¸ãƒˆãƒªã¨ãƒ—ライベートリãƒã‚¸ãƒˆãƒªã®ä¸€è¦§ã‚’表示ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "ã‚ã‚‹ã„ã¯ã€ %{personal_access_token_link} を使用ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚パーソナルアクセストークンを作æˆã™ã‚‹éš›ã«ã€<code>repo</code>スコープをé¸æŠžã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ã“ã‚Œã«ã‚ˆã‚Šã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹ã“ã¨ãŒã§ãるパブリックリãƒã‚¸ãƒˆãƒªã¨ãƒ—ライベートリãƒã‚¸ãƒˆãƒªã®ä¸€è¦§ã‚’表示ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
-msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr "アプリケーション㮠%{link_to_client} ãŒã‚ãªãŸã® GitLab アカウントã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’è¦æ±‚ã—ã¦ã„ã¾ã™ã€‚"
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
-msgstr ""
+msgstr "GitLab ユーザフィールドãŒç©ºã®å ´åˆã€ã™ã¹ã¦ã®å•é¡Œã¨ã‚³ãƒ¡ãƒ³ãƒˆã®èª¬æ˜Žã« FogBugz ユーザã®ãƒ•ãƒ«ãƒãƒ¼ãƒ  (例ãˆã°ã€By John Smith)を追加ã—ã¾ã™ã€‚ã¾ãŸã€ã“れらã®å•é¡Œã‚„コメントをプロジェクト作æˆè€…ã«é–¢é€£ä»˜ã‘ã‚‹ã‹ã€ã¾ãŸã¯å‰²ã‚Šå½“ã¦ã¾ã™ã€‚"
msgid "An error accured whilst committing your changes."
-msgstr ""
+msgstr "変更ã®ã‚³ãƒŸãƒƒãƒˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error has occurred"
-msgstr ""
+msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occured creating the new branch."
msgstr "æ–°ã—ã„ブランãƒã®ä½œæˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occured whilst fetching the job trace."
-msgstr ""
+msgstr "ジョブトレースã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-msgid "An error occured whilst fetching the latest pipline."
-msgstr ""
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr "最新ã®ãƒ‘イプラインã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occured whilst loading all the files."
msgstr ""
msgid "An error occured whilst loading the file content."
-msgstr ""
+msgstr "ファイルã®å†…容を読込中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occured whilst loading the file."
-msgstr ""
+msgstr "ファイルを読込中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occured whilst loading the merge request changes."
-msgstr ""
+msgstr "マージリクエストã®å¤‰æ›´ã‚’読込中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occured whilst loading the merge request version data."
-msgstr ""
+msgstr "マージリクエストã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…報を読込中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occured whilst loading the merge request."
-msgstr ""
+msgstr "マージリクエストã®èª­è¾¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occured whilst loading the pipelines jobs."
-msgstr ""
+msgstr "パイプラインジョブã®èª­è¾¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred previewing the blob"
msgstr "Blobã®ãƒ—レビュー中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -505,13 +562,13 @@ msgid "An error occurred when toggling the notification subscription"
msgstr "通知購読ã®åˆ‡ã‚Šæ›¿ãˆæ™‚ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred when updating the issue weight"
-msgstr ""
+msgstr "課題ã®ã‚¦ã‚¨ã‚¤ãƒˆæ›´æ–°æ™‚ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while adding approver"
-msgstr ""
+msgstr "承èªè€…ã®è¿½åŠ ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while detecting host keys"
-msgstr ""
+msgstr "ホストキーã®æ¤œå‡ºä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while dismissing the alert. Refresh the page and try again."
msgstr "アラートã®æ¶ˆåŽ»ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ページを更新ã—ã¦ã‚„ã‚Šç›´ã—ã¦ä¸‹ã•ã„。"
@@ -525,17 +582,29 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr "サイドãƒãƒ¼ã®ãƒ‡ãƒ¼ã‚¿å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr "パイプラインã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
-msgstr ""
+msgstr "パスロックã®åˆæœŸåŒ–中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while loading commit signatures"
msgstr ""
@@ -553,7 +622,7 @@ msgid "An error occurred while making the request."
msgstr "リクエスト作æˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while removing approver"
-msgstr ""
+msgstr "承èªè€…ã®å‰Šé™¤ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while rendering KaTeX"
msgstr "KaTeXã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -568,16 +637,16 @@ msgid "An error occurred while retrieving diff"
msgstr "差分をå–å¾—ã®éš›ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while saving LDAP override status. Please try again."
-msgstr ""
+msgstr "LDAP ã®ã‚ªãƒ¼ãƒãƒ¼ãƒ©ã‚¤ãƒ‰çŠ¶æ…‹ã‚’ä¿å­˜ã™ã‚‹ã¨ãã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。"
msgid "An error occurred while saving assignees"
msgstr "担当者ã®ç™»éŒ²ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while subscribing to notifications."
-msgstr ""
+msgstr "通知ã®è³¼èª­ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while unsubscribing to notifications."
-msgstr ""
+msgstr "通知ã®è³¼èª­ã‚’解除中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while validating username"
msgstr "ユーザåã®æ¤œè¨¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -586,13 +655,13 @@ msgid "An error occurred. Please try again."
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚å†åº¦ãŠè©¦ã—ãã ã•ã„。"
msgid "Anonymous"
-msgstr ""
+msgstr "匿å"
msgid "Anti-spam verification"
-msgstr ""
+msgstr "スパム対策ã®æ¤œè¨¼"
msgid "Any"
-msgstr ""
+msgstr "ä»»æ„ã®"
msgid "Any Label"
msgstr "ä»»æ„ã®ãƒ©ãƒ™ãƒ«"
@@ -604,7 +673,7 @@ msgid "Application"
msgstr "アプリケーション"
msgid "Application Id"
-msgstr ""
+msgstr "アプリケーション ID"
msgid "Application: %{name}"
msgstr ""
@@ -621,14 +690,20 @@ msgstr "4月"
msgid "Archived project! Repository and other project resources are read-only"
msgstr "ã“ã®ãƒ—ロジェクトã¯ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã•ã‚Œã¦ã„ã¾ã™ã€‚リãƒã‚¸ãƒˆãƒªãŠã‚ˆã³ãã®ä»–ã®ãƒ—ロジェクトリソースã¯èª­ã¿å–り専用ã§ã™"
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ã“ã®ãƒ‘イプラインスケジュールを削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Are you sure you want to lose unsaved changes?"
+msgstr "変更ãŒä¿å­˜ã•ã‚Œã¦ã„ã¾ã›ã‚“ãŒç ´æ£„ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
-msgstr ""
+msgstr "ã“ã® %{group_name} を削除ã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Are you sure you want to remove this identity?"
msgstr "ã“ã® ID を削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
@@ -640,22 +715,25 @@ msgid "Are you sure you want to reset the health check token?"
msgstr "本当ã«ãƒ˜ãƒ«ã‚¹ãƒã‚§ãƒƒã‚¯ãƒˆãƒ¼ã‚¯ãƒ³ã‚’リセットã—ã¾ã™ã‹ï¼Ÿ"
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr ""
+msgstr "%{path_lock_path} ã®ãƒ­ãƒƒã‚¯ã‚’解除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Are you sure?"
msgstr "本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr "アーティファクト"
msgid "Ascending"
msgstr "昇順"
-msgid "Ask your group maintainer to setup a group Runner."
-msgstr "グループ Runner ã®è¨­å®šã¯ã€ã‚°ãƒ«ãƒ¼ãƒ—ã® Maintainer ã«ä¾é ¼ã—ã¦ãã ã•ã„。"
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
msgid "Assertion consumer service URL"
-msgstr ""
+msgstr "アサーション コンシューマー サービス URL"
msgid "Assign custom color like #FF0000"
msgstr "#FF0000ã®ã‚ˆã†ãªã‚«ã‚¹ã‚¿ãƒ ã‚«ãƒ©ãƒ¼ã‚’割り当ã¦ã‚‹"
@@ -684,11 +762,11 @@ msgstr "自分ã«å‰²ã‚Šå½“ã¦ã‚‹"
msgid "Assignee"
msgstr "担当者"
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
-msgstr ""
+msgstr "担当者一覧ã«ã¯ã€é¸æŠžã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ã‚‹ã™ã¹ã¦ã®èª²é¡ŒãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚"
msgid "Assignee(s)"
msgstr "担当者"
@@ -709,6 +787,9 @@ msgid "Authentication Log"
msgstr "èªè¨¼ãƒ­ã‚°"
msgid "Authentication log"
+msgstr "èªè¨¼ãƒ­ã‚°"
+
+msgid "Authentication method"
msgstr ""
msgid "Author"
@@ -718,10 +799,10 @@ msgid "Authorization code:"
msgstr ""
msgid "Authorization was granted by entering your username and password in the application."
-msgstr ""
+msgstr "ã“ã®ã‚¢ãƒ—リケーションã«ã‚ãªãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¨ãƒ‘スワードãŒå…¥åŠ›ã•ã‚ŒãŸã®ã§ã€æ‰¿èªãŒè¨±å¯ã•ã‚Œã¾ã—ãŸã€‚"
msgid "Authorize"
-msgstr ""
+msgstr "承èªã™ã‚‹"
msgid "Authorize %{link_to_client} to use your account?"
msgstr ""
@@ -736,7 +817,7 @@ msgid "Authors: %{authors}"
msgstr "作æˆè€…: %{authors}"
msgid "Auto DevOps"
-msgstr ""
+msgstr "Auto DevOps"
msgid "Auto DevOps enabled"
msgstr "Auto DevOps ãŒæœ‰åŠ¹ã§ã™"
@@ -771,6 +852,9 @@ msgstr "ã‚らã‹ã˜ã‚定義ã•ã‚ŒãŸ CI/CD ã®æ§‹æˆã‚’基ã«è‡ªå‹•çš„ã«ã‚¢ãƒ
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "詳ã—ãã¯ã€ %{link_to_documentation} を見ã¦ãã ã•ã„。"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr "ã“ã®ãƒ—ロジェクト㮠%{link_to_auto_devops_settings} ã‚’ã™ã‚‹å ´åˆã€è‡ªå‹•ãƒ“ルドãŠã‚ˆã³ãƒ†ã‚¹ãƒˆã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚%{link_to_add_kubernetes_cluster} ã—ãŸå ´åˆã€è‡ªå‹•ãƒ‡ãƒ—ロイã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
@@ -780,6 +864,9 @@ msgstr "Kubernetes クラスターを追加"
msgid "AutoDevOps|enable Auto DevOps"
msgstr "Auto DevOps を有効ã«ã™ã‚‹"
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr "利用å¯èƒ½"
@@ -799,14 +886,11 @@ msgid "Background Color"
msgstr "背景色"
msgid "Background Jobs"
-msgstr ""
+msgstr "ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚¸ãƒ§ãƒ–"
msgid "Background color"
msgstr "背景色"
-msgid "Background jobs"
-msgstr "ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚¸ãƒ§ãƒ–"
-
msgid "Badges"
msgstr "ãƒãƒƒã‚¸"
@@ -846,6 +930,9 @@ msgstr "ãƒãƒƒã‚¸ç”»åƒãªã—"
msgid "Badges|No image to preview"
msgstr "プレビューã™ã‚‹ç”»åƒã¯ã‚ã‚Šã¾ã›ã‚“"
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr "プロジェクト ãƒãƒƒã‚¸"
@@ -873,9 +960,15 @@ msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ãƒãƒƒã‚¸ã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "Badges|This project has no badges"
msgstr "ã“ã®ãƒ—ロジェクトã«ãƒãƒƒã‚¸ã¯ã‚ã‚Šã¾ã›ã‚“"
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr "ãƒãƒƒã‚¸"
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr "é¸æŠžã—ãŸã‚³ãƒŸãƒƒãƒˆã§ã¯ã˜ã‚ã‚‹"
@@ -883,16 +976,16 @@ msgid "Below are examples of regex for existing tools:"
msgstr ""
msgid "Below you will find all the groups that are public."
-msgstr ""
+msgstr "以下ã«å…¬é–‹ã•ã‚Œã¦ã„る全グループを表示ã—ã¾ã™ã€‚"
msgid "Billing"
msgstr "請求"
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
-msgstr ""
+msgstr "%{group_name} ã®ç¾åœ¨ã®ãƒ—ランã¯%{plan_link} ã§ã™ã€‚"
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
-msgstr ""
+msgstr "ã„ãã¤ã‹ã®æ”¯æ‰•ã„プランã§ã¯ã€è‡ªå‹•ã‚¢ãƒƒãƒ—グレードã€ãƒ€ã‚¦ãƒ³ã‚°ãƒ¬ãƒ¼ãƒ‰ãŒç¾åœ¨åˆ©ç”¨ã§ãã¾ã›ã‚“。"
msgid "BillingPlans|Current plan"
msgstr "ç¾åœ¨ã®ãƒ—ラン"
@@ -907,28 +1000,28 @@ msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or st
msgstr ""
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
-msgstr ""
+msgstr "å„プランã®è©³ç´°ã«ã¤ã„ã¦ã¯ã€ %{faq_link} ã‚’ã”確èªãã ã•ã„。"
msgid "BillingPlans|Manage plan"
msgstr "プランã®ç®¡ç†"
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
-msgstr ""
+msgstr "%{customer_support_link} ã¾ã§ã”連絡ãã ã•ã„。"
msgid "BillingPlans|See all %{plan_name} features"
-msgstr ""
+msgstr "%{plan_name} ã®ã™ã¹ã¦ã®æ©Ÿèƒ½ã‚’見る"
msgid "BillingPlans|This group uses the plan associated with its parent group."
-msgstr ""
+msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¯ã€è¦ªã‚°ãƒ«ãƒ¼ãƒ—ã¨åŒã˜ãƒ—ランを使用ã—ã¾ã™ã€‚"
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr ""
+msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã®ãƒ—ランã®ç®¡ç†ã¯ã€%{parent_billing_page_link} ã®è«‹æ±‚ã®ã‚»ã‚¯ã‚·ãƒ§ãƒ³ã‚’ã”覧ãã ã•ã„。"
msgid "BillingPlans|Upgrade"
msgstr "アップグレード"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
-msgstr ""
+msgstr "ç¾åœ¨ã®ãƒ—ランã¯%{plan_link} ã§ã™ã€‚"
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
msgstr ""
@@ -937,25 +1030,28 @@ msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}
msgstr ""
msgid "BillingPlans|features"
-msgstr ""
+msgstr "機能"
msgid "BillingPlans|frequently asked questions"
msgstr ""
msgid "BillingPlans|monthly"
-msgstr ""
+msgstr "月é¡"
msgid "BillingPlans|paid annually at %{price_per_year}"
msgstr ""
msgid "BillingPlans|per user"
+msgstr "1ユーザーã«ã¤ã"
+
+msgid "Bitbucket Server Import"
msgstr ""
msgid "Bitbucket import"
-msgstr ""
+msgstr "Bitbucket インãƒãƒ¼ãƒˆ"
msgid "Blog"
-msgstr ""
+msgstr "ブログ"
msgid "Boards"
msgstr "ボード"
@@ -1076,7 +1172,7 @@ msgid "Branches|Stale branches"
msgstr "å¤ã„ブランãƒ"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
-msgstr ""
+msgstr "ã“ã®ãƒ–ランãƒã¯ã‚¢ãƒƒãƒ—ストリームã‹ã‚‰åˆ†å²ã—ã¦ã„ã‚‹ãŸã‚自動ã§ã‚¢ãƒƒãƒ—デートã§ãã¾ã›ã‚“。"
msgid "Branches|The default branch cannot be deleted"
msgstr "デフォルトブランãƒã¯å‰Šé™¤ã§ãã¾ã›ã‚“"
@@ -1091,13 +1187,13 @@ msgid "Branches|To confirm, type %{branch_name_confirmation}:"
msgstr "確èªã®ãŸã‚ã€%{branch_name_confirmation} を入力ã—ã¦ãã ã•ã„"
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
-msgstr ""
+msgstr "ローカルã®å¤‰æ›´å†…容を破棄ã—ã¦ã€ã‚¢ãƒƒãƒ—ストリームã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ãƒ–ランãƒã‚’上書ãã™ã‚‹ã«ã¯ã€ã“ã“ã§ãれらを削除ã—ã€ä¸Šã®ã€Œä»Šã™ãæ›´æ–°ã€ã‚’クリックã—ã¦ãã ã•ã„。"
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
msgstr "ä¿è­·ã•ã‚ŒãŸ %{branch_name} ブランãƒã‚’削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚"
msgid "Branches|diverged from upstream"
-msgstr ""
+msgstr "アップストリームã‹ã‚‰åˆ†å²"
msgid "Branches|merged"
msgstr "マージ済ã¿"
@@ -1120,6 +1216,9 @@ msgstr "ファイルを表示"
msgid "Browse files"
msgstr "ファイルを表示"
+msgid "Built-In"
+msgstr "ビルトイン"
+
msgid "Business metrics (Custom)"
msgstr "ビジãƒã‚¹ãƒ¡ãƒˆãƒªã‚¯ã‚¹ï¼ˆã‚«ã‚¹ã‚¿ãƒ ï¼‰"
@@ -1132,6 +1231,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr "CI / CD 設定"
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr "CI/CD"
@@ -1139,14 +1241,11 @@ msgid "CI/CD configuration"
msgstr "CI/CD 設定"
msgid "CI/CD for external repo"
-msgstr ""
+msgstr "外部リãƒã‚¸ãƒˆãƒªç”¨ CI/CD"
msgid "CI/CD settings"
msgstr "CI/CD 設定"
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr "継続的インテグレーションã¨ãƒ‡ãƒªãƒãƒªãƒ¼ã‚’使用ã™ã‚‹ã«ã¯ã€æ˜Žç¤ºçš„ã« %{ci_file} を指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
-
msgid "CICD|Auto DevOps"
msgstr "Auto DevOps"
@@ -1159,26 +1258,17 @@ msgstr "ステージングã¸ã®è‡ªå‹•ãƒ‡ãƒ—ロイã€æœ¬ç•ªç’°å¢ƒã¸ã®æ‰‹å‹•ãƒ‡
msgid "CICD|Continuous deployment to production"
msgstr "本番環境ã¸ã®ç¶™ç¶šçš„デプロイ"
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
msgid "CICD|Deployment strategy"
msgstr "デプロイ戦略"
msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr "デプロイ戦略を正ã—ã動作ã•ã›ã‚‹ãŸã‚ã«ã¯ãƒ‰ãƒ¡ã‚¤ãƒ³åãŒå¿…è¦ã§ã™ã€‚"
-msgid "CICD|Disable Auto DevOps"
-msgstr "Auto DevOps を無効ã«ã™ã‚‹"
-
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
-msgstr ""
-
-msgid "CICD|Enable Auto DevOps"
-msgstr "Auto DevOps を有効ã«ã™ã‚‹"
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr "プロジェクト固有㮠%{ci_file} ãŒç„¡ã„å ´åˆã€ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã«å¾“ã„ Auto DevOps ã®æœ‰åŠ¹ã¨ç„¡åŠ¹ã‚’切り替ãˆã¾ã™ã€‚"
-
-msgid "CICD|Instance default (%{state})"
-msgstr "インスタンスã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆï¼ˆ%{state})"
+msgstr "Auto DevOps 㧠Auto マルムKubernetes クラスターをセットアップã™ã‚‹å ´åˆã¯ã€ã“ã“ã§ãƒ‰ãƒ¡ã‚¤ãƒ³ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—ã‚’è¡Œã‚ãªã„ã§ãã ã•ã„。"
msgid "CICD|Jobs"
msgstr "ジョブ"
@@ -1186,17 +1276,20 @@ msgstr "ジョブ"
msgid "CICD|Learn more about Auto DevOps"
msgstr "Auto DevOps ã®è©³ç´°"
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
-msgstr "プロジェクト㫠%{ci_file} ãŒãªã„å ´åˆã€Auto DevOps ã®ãƒ‘イプライン設定ãŒä½¿ç”¨ã•ã‚Œã¾ã™ã€‚"
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr "自動 Review Apps ã¨è‡ªå‹•ãƒ‡ãƒ—ロイステージを使用ã—ãŸã„å ´åˆã¯ã€ãƒ‰ãƒ¡ã‚¤ãƒ³ã‚’指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+msgid "CICD|instance enabled"
+msgstr "インスタンスãŒæœ‰åŠ¹"
+
msgid "Callback URL"
-msgstr ""
+msgstr "コールãƒãƒƒã‚¯ URL"
msgid "Callback url"
-msgstr ""
+msgstr "コールãƒãƒƒã‚¯ URL"
msgid "Can't find HEAD commit for this branch"
msgstr "ã“ã®ãƒ–ランãƒã«ã¯ HEAD コミットãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
@@ -1217,7 +1310,10 @@ msgid "Certificate fingerprint"
msgstr ""
msgid "Change Weight"
-msgstr ""
+msgstr "ウェイトを変更ã™ã‚‹"
+
+msgid "Change template"
+msgstr "テンプレートを変更"
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr "GitLab UI ã®æ›´æ–°é »åº¦ã‚’変更ã™ã‚‹ã«ã¯ã“ã®å€¤ã‚’変更ã—ã¦ãã ã•ã„。"
@@ -1276,6 +1372,12 @@ msgstr "ファイルをé¸æŠž..."
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "変更内容を確èªã—ãŸã‚Šãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’作æˆã™ã‚‹ãŸã‚ã«ã€Branch/tag (例: %{master}) ã‚’é¸æŠžã™ã‚‹ã‹ã€ã‚³ãƒŸãƒƒãƒˆID(例: %{sha})を入力ã—ã¦ãã ã•ã„。"
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr "カラーをé¸æŠžã—ã¦ãã ã•ã„。"
@@ -1289,10 +1391,10 @@ msgid "Choose the top-level group for your repository imports."
msgstr ""
msgid "Choose which groups you wish to synchronize to this secondary node."
-msgstr ""
+msgstr "åŒæœŸã•ã›ãŸã„セカンダリノードã®ã‚°ãƒ«ãƒ¼ãƒ—ã‚’é¸æŠžã—ã¦ãã ã•ã„。"
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
-msgstr ""
+msgstr "CI/CD パイプラインを実行ã—ãŸã„リãƒã‚¸ãƒˆãƒªã‚’é¸æŠžã—ã¦ãã ã•ã„。"
msgid "Choose which repositories you want to import."
msgstr "インãƒãƒ¼ãƒˆã—ãŸã„リãƒã‚¸ãƒˆãƒªã‚’é¸æŠžã—ã¦ãã ã•ã„。"
@@ -1382,7 +1484,7 @@ msgid "CiVariable|Protected"
msgstr "ä¿è­·"
msgid "CiVariable|Search environments"
-msgstr ""
+msgstr "環境を検索"
msgid "CiVariable|Toggle protected"
msgstr "ä¿è­·ã®åˆ‡ã‚Šæ›¿ãˆ"
@@ -1391,7 +1493,7 @@ msgid "CiVariable|Validation failed"
msgstr "検証ã«å¤±æ•—ã—ã¾ã—ãŸ"
msgid "CircuitBreakerApiLink|circuitbreaker api"
-msgstr ""
+msgstr "Circuit Breaker API"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
@@ -1421,16 +1523,16 @@ msgid "Click to expand text"
msgstr "クリックã—ã¦ãƒ†ã‚­ã‚¹ãƒˆã‚’展開ã™ã‚‹"
msgid "Client authentication certificate"
-msgstr ""
+msgstr "クライアントèªè¨¼è¨¼æ˜Žæ›¸"
msgid "Client authentication key"
msgstr "クライアントèªè¨¼ã‚­ãƒ¼"
msgid "Client authentication key password"
-msgstr ""
+msgstr "クライアントèªè¨¼ã‚­ãƒ¼ã®ãƒ‘スワード"
msgid "Clients"
-msgstr ""
+msgstr "クライアント"
msgid "Clone repository"
msgstr "リãƒã‚¸ãƒˆãƒªã‚’クローン"
@@ -1438,15 +1540,21 @@ msgstr "リãƒã‚¸ãƒˆãƒªã‚’クローン"
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
msgid "Closed issues"
-msgstr ""
+msgstr "クローズã—ãŸèª²é¡Œ"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} ã¯æ­£å¸¸ã« Kubernetes クラスターã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¾ã—ãŸ"
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr "API URL"
@@ -1456,12 +1564,21 @@ msgstr "Kubernetes クラスターを追加"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "ã“ã® Kubernetes クラスター統åˆã«é–¢ã™ã‚‹è©³ç´°ã‚ªãƒ—ション"
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr "プロジェクトゾーンã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: %{error}"
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr "プロジェクトã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: %{error}"
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr "アプリケーション"
@@ -1474,11 +1591,11 @@ msgstr "CA 証明書"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "èªè¨¼å±€ãƒãƒ³ãƒ‰ãƒ« (PEMå½¢å¼)"
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "ã©ã®ãƒ—ロジェクト環境ã§ã“ã® Kubernetes クラスターを使ã†ã‹é¸æŠžã—ã¦ãã ã•ã„。"
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr "Kubernetes クラスター㨠GitLab ã®çµ±åˆæ–¹æ³•ã®åˆ¶å¾¡"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
msgid "ClusterIntegration|Copy API URL"
msgstr "API URLをコピー"
@@ -1504,6 +1621,12 @@ msgstr "Kubernetes クラスターを作æˆ"
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "Kubernetes クラスターã®è©³ç´°ã‚’入力ã—ã¦ãã ã•ã„"
@@ -1528,6 +1651,9 @@ msgstr "GitLabã‚’çµ±åˆ"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr "Google Cloud Platform プロジェクト"
@@ -1540,6 +1666,9 @@ msgstr "Google Kubernetes Engine プロジェクト"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr "éžè¡¨ç¤º"
@@ -1547,7 +1676,7 @@ msgid "ClusterIntegration|If you are setting up multiple clusters and are using
msgstr ""
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
-msgstr ""
+msgstr "クラスターã®ç¨¼åƒçŠ¶æ³ã‚’表示ã™ã‚‹ã«ã¯ã€å¿…須事項をåŽé›†ã™ã‚‹ãŸã‚ã® Prometheus を設置ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
@@ -1555,6 +1684,9 @@ msgstr "Ingress"
msgid "ClusterIntegration|Ingress IP Address"
msgstr "Ingress 㮠IP アドレス"
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr "インストール"
@@ -1579,6 +1711,9 @@ msgstr "Jupyter ã®ãƒ›ã‚¹ãƒˆå"
msgid "ClusterIntegration|JupyterHub"
msgstr "JupyterHub"
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Kubernetes クラスター"
@@ -1586,20 +1721,11 @@ msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Kubernetes クラスターã®è©³ç´°"
msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
+msgstr "Kubernetes クラスターã®ç¨¼åƒçŠ¶æ…‹"
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Kubernetes クラスターã®çµ±åˆ"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "Kubernetes クラスターã®çµ±åˆãŒã“ã®ãƒ—ロジェクトã§ç„¡åŠ¹ã§ã™ã€‚"
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "Kubernetes クラスターã®çµ±åˆãŒã“ã®ãƒ—ロジェクトã§æœ‰åŠ¹ã§ã™ã€‚"
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "Kubernetes クラスターã®çµ±åˆãŒã“ã®ãƒ—ロジェクトã§æœ‰åŠ¹ã§ã™ã€‚ã“ã®çµ±åˆã‚’無効ã«ã—ã¦ã‚‚ Kubernetes クラスターã«ã¯å½±éŸ¿ã›ãšã€GitLab ã¨ã®æŽ¥ç¶šãŒä¸€æ™‚çš„ã«åˆ‡æ–­ã•ã‚Œã‚‹ã ã‘ã§ã™ã€‚"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes クラスターを Google Kubernetes Engine 上ã«ä½œæˆã—ã¦ã„ã¾ã™..."
@@ -1624,12 +1750,6 @@ msgstr "%{help_link_start}Kubernetes%{help_link_end}ã®è©³ç´°ã€‚"
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr "%{help_link_start}ゾーン%{help_link_end}ã®è©³ç´°ã€‚"
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "環境ã®è©³ç´°"
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr "セキュリティ設定ã®è©³ç´°"
-
msgid "ClusterIntegration|Machine type"
msgstr "マシンタイプ"
@@ -1667,11 +1787,14 @@ msgid "ClusterIntegration|Number of nodes"
msgstr "ノード数"
msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
-msgstr ""
+msgstr "Kubernetes クラスターã®ã‚¢ã‚¯ã‚»ã‚¹æƒ…報を入力ã—ã¦ãã ã•ã„。ä¸æ˜Žç‚¹ã¯ %{link_to_help_page} ã® Kubernetes ã®é …目をã”覧ãã ã•ã„。"
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Google アカウントãŒæ¬¡ã®è¦ä»¶ã‚’満ãŸã—ã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr "プロジェクトã®åå‰ç©ºé–“"
@@ -1681,6 +1804,12 @@ msgstr "プロジェクトã®åå‰ç©ºé–“ (çœç•¥å¯èƒ½ã€ä¸€æ„)"
msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr "Kubernetes クラスターã®çµ±åˆã«ã¤ã„ã¦ã¯ã€%{link_to_help_page} ã‚’ãŠèª­ã¿ãã ã•ã„。"
@@ -1693,6 +1822,9 @@ msgstr "çµ±åˆã‚’削除"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "プロジェクトã‹ã‚‰ Kubernetes クラスターã®è¨­å®šã‚’削除ã—ã¾ã™ã€‚ãªãŠã€å®Ÿéš›ã® Kubernetes クラスターã¯å‰Šé™¤ã•ã‚Œã¾ã›ã‚“。"
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "インストール開始ã«å¤±æ•—ã—ã¾ã—ãŸ"
@@ -1708,9 +1840,6 @@ msgstr "プロジェクトã®æ¤œç´¢"
msgid "ClusterIntegration|Search zones"
msgstr "ゾーンを検索"
-msgid "ClusterIntegration|Security"
-msgstr "セキュリティ"
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "Kubernetes クラスターã®è©³ç´°ã‚’閲覧ã€ç·¨é›†ã™ã‚‹"
@@ -1747,12 +1876,18 @@ msgstr "Google Kubernetes Engine 上㮠Kubernetes クラスターを作æˆä¸­ã
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "%{title} ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr "既定ã®ã‚¯ãƒ©ã‚¹ã‚¿ãƒ¼è¨­å®šã§ã¯ã€ã‚³ãƒ³ãƒ†ãƒŠåŒ–ã•ã‚ŒãŸã‚¢ãƒ—リケーションを正常ã«ãƒ“ルドやデプロイã™ã‚‹ãŸã‚ã«å¿…è¦ãªæ§˜ã€…ãªæ©Ÿèƒ½ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚"
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ %{link_to_container_project} 㧠Kubernetes クラスターを作æˆã™ã‚‹ã®ã«ä»¥ä¸‹ã®æ¨©é™ãŒå¿…è¦ã§ã™"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "Kubernetes クラスターを切り替ãˆ"
@@ -1765,9 +1900,15 @@ msgstr "トークン"
msgid "ClusterIntegration|Validating project billing status"
msgstr "プロジェクトã®è«‹æ±‚ステータスを検証ã—ã¦ã„ã¾ã™"
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "ã“ã®ãƒ—ロジェクト㫠Kubernetes クラスターを関連付ã‘ã‚‹ã“ã¨ã§ã€Review Apps ã®ä½¿ç”¨ã€ã‚¢ãƒ—リケーションã®ãƒ‡ãƒ—ロイã€ãƒ‘イプラインã®å®Ÿè¡Œãªã©ã‚’ç°¡å˜ã«è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚"
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "アカウント㫠%{link_to_kubernetes_engine} ãŒå¿…è¦ã§ã™ã€‚"
@@ -1786,9 +1927,6 @@ msgstr "ドキュメント"
msgid "ClusterIntegration|help page"
msgstr "ヘルプ ページ"
-msgid "ClusterIntegration|installing applications"
-msgstr "アプリケーションをインストールã—ã¦ã„ã¾ã™"
-
msgid "ClusterIntegration|meets the requirements"
msgstr "å¿…è¦æ¡ä»¶"
@@ -1798,6 +1936,9 @@ msgstr "æ­£ã—ã設定ã•ã‚Œã¦ã„ã‚‹"
msgid "ClusterIntegration|sign up"
msgstr "æ–°è¦ç™»éŒ²"
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1845,6 +1986,9 @@ msgstr "コミット"
msgid "CommitMessage|Add %{file_name}"
msgstr "%{file_name} を追加"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "コミット"
@@ -1858,7 +2002,7 @@ msgid "Commits per day of month"
msgstr ""
msgid "Commits per weekday"
-msgstr ""
+msgstr "週ã”ã¨ã®ã‚³ãƒŸãƒƒãƒˆæ•°"
msgid "Commits|An error occurred while fetching merge requests data."
msgstr "マージリクエストデータã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -1909,24 +2053,21 @@ msgid "CompareBranches|There isn't anything to compare."
msgstr "比較ã™ã‚‹ã‚‚ã®ã¯ã‚ã‚Šã¾ã›ã‚“。"
msgid "Confidential"
-msgstr ""
+msgstr "éžå…¬é–‹"
msgid "Confidentiality"
-msgstr ""
+msgstr "機密性"
msgid "Configure Gitaly timeouts."
msgstr "Gitaly ã®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã‚’設定ã—ã¾ã™ã€‚"
-msgid "Configure Sidekiq job throttling."
-msgstr "Sidekiq job throttling ã®è¨­å®š"
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr "リãƒã‚¸ãƒˆãƒªã«å¯¾ã—ã¦è‡ªå‹•å®Ÿè¡Œã™ã‚‹ Git ãƒã‚§ãƒƒã‚¯ã¨ãƒã‚¦ã‚¹ã‚­ãƒ¼ãƒ”ングを設定ã—ã¾ã™ã€‚"
msgid "Configure limits for web and API requests."
msgstr "ウェブãŠã‚ˆã³APIリクエストã®åˆ¶é™ã‚’設定ã™ã‚‹ã€‚"
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2016,6 +2157,9 @@ msgstr "貢献度"
msgid "Contribution guide"
msgstr "貢献者å‘ã‘ガイド"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr "グループメンãƒãƒ¼ã®è²¢çŒ®åº¦"
@@ -2038,17 +2182,26 @@ msgid "Control the display of third party offers."
msgstr ""
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr ""
+msgstr "ã“ã®ã‚»ã‚«ãƒ³ãƒ€ãƒªãƒŽãƒ¼ãƒ‰ã® LFS / attachment ãƒãƒƒã‚¯ãƒ•ã‚£ãƒ«ã®æœ€å¤§ä¸¦è¡Œæ€§ã‚’制御ã™ã‚‹"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr ""
+msgstr "ã“ã®ã‚»ã‚«ãƒ³ãƒ€ãƒªãƒŽãƒ¼ãƒ‰ã®ãƒªãƒã‚¸ãƒˆãƒªãƒãƒƒã‚¯ãƒ•ã‚£ãƒ«ã®æœ€å¤§åŒæ™‚実行性を制御ã™ã‚‹"
msgid "Control the maximum concurrency of verification operations for this Geo node"
-msgstr ""
+msgstr "ã“ã® Geo ノードã®æ¤œè¨¼æ“作ã®æœ€å¤§åŒæ™‚実行数を制御ã™ã‚‹"
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr "SSH 公開éµã‚’クリップボードã«ã‚³ãƒ”ー"
@@ -2154,9 +2307,6 @@ msgstr "æ–°è¦ä½œæˆ"
msgid "Create project label"
msgstr "プロジェクトラベルを作æˆ"
-msgid "CreateNewFork|Fork"
-msgstr "フォーク"
-
msgid "CreateTag|Tag"
msgstr "ã‚¿ã‚°"
@@ -2172,6 +2322,9 @@ msgstr ""
msgid "Created by me"
msgstr "自分ãŒä½œæˆ"
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2184,6 +2337,9 @@ msgstr "Cron ã®ã‚¿ã‚¤ãƒ ã‚¾ãƒ¼ãƒ³"
msgid "Cron syntax"
msgstr "Cron ã®æ§‹æ–‡"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr "ç¾åœ¨ã®ãƒŽãƒ¼ãƒ‰"
@@ -2193,6 +2349,9 @@ msgstr "プロフィール"
msgid "CurrentUser|Settings"
msgstr "設定"
+msgid "Custom"
+msgstr "カスタム"
+
msgid "Custom CI config path"
msgstr ""
@@ -2202,6 +2361,9 @@ msgstr "カスタム通知設定"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "\"カスタム\" ã®é€šçŸ¥ãƒ¬ãƒ™ãƒ«ã®åŸºæœ¬ã¯ \"å‚加\" ã¨åŒã˜ã§ã™ã€‚ã¾ãŸã€ã‚«ã‚¹ã‚¿ãƒ é€šçŸ¥ã«è¨­å®šã™ã‚‹ã“ã¨ã§é¸æŠžã—ãŸã‚«ã‚¹ã‚¿ãƒ ã‚¤ãƒ™ãƒ³ãƒˆã®é€šçŸ¥ã‚’å—ã‘å–ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚ã‚‚ã£ã¨è©³ã—ã知りãŸã„å ´åˆã¯ %{notification_link} を見ã¦ãã ã•ã„。"
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr "カスタムカラー"
@@ -2214,6 +2376,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "サイクル分æž"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "コード"
@@ -2236,7 +2401,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "テスト"
msgid "Dashboard"
-msgstr ""
+msgstr "ダッシュボード"
msgid "DashboardProjects|All"
msgstr "ã™ã¹ã¦"
@@ -2244,6 +2409,12 @@ msgstr "ã™ã¹ã¦"
msgid "DashboardProjects|Personal"
msgstr "個人"
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr "デãƒãƒƒã‚°"
+
msgid "Dec"
msgstr "12月"
@@ -2253,9 +2424,12 @@ msgstr "12月"
msgid "Decline and sign out"
msgstr "辞退ã—ã¦ã‚µã‚¤ãƒ³ã‚¢ã‚¦ãƒˆ"
-msgid "Default classification label"
+msgid "Default Branch"
msgstr ""
+msgid "Default classification label"
+msgstr "デフォルト分類ラベル"
+
msgid "Default: Directly import the Google Code email address or username"
msgstr ""
@@ -2268,6 +2442,9 @@ msgstr "Cron 構文ã§ã‚«ã‚¹ã‚¿ãƒ ãªãƒ‘ターンを指定ã™ã‚‹"
msgid "Delete"
msgstr "削除"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr "スニペットを削除"
@@ -2275,10 +2452,10 @@ msgid "Delete list"
msgstr "リストを削除ã™ã‚‹"
msgid "Deleted"
-msgstr ""
+msgstr "削除完了"
msgid "Deny"
-msgstr ""
+msgstr "æ‹’å¦"
msgid "Deploy"
msgid_plural "Deploys"
@@ -2408,7 +2585,7 @@ msgid "DeployTokens|Your new project deploy token has been created."
msgstr "æ–°ã—ã„プロジェクトデプロイトークンãŒä½œæˆã•ã‚Œã¾ã—ãŸã€‚"
msgid "Deprioritize label"
-msgstr ""
+msgstr "éžå„ªå…ˆãƒ©ãƒ™ãƒ«"
msgid "Descending"
msgstr "é™é †"
@@ -2417,21 +2594,27 @@ msgid "Description"
msgstr "説明"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
-msgstr ""
+msgstr "Description テンプレートを使用ã™ã‚‹ã¨ã€ãƒ—ロジェクトã®èª²é¡ŒãŠã‚ˆã³ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆèª¬æ˜Žã«å¯¾ã™ã‚‹ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆå›ºæœ‰ã®ãƒ†ãƒ³ãƒ—レートを定義ã§ãã¾ã™ã€‚"
msgid "Description:"
msgstr ""
msgid "Destroy"
-msgstr ""
+msgstr "破棄"
msgid "Details"
msgstr "詳細"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr "使用å¯èƒ½ãªãƒ•ã‚¡ã‚¤ãƒ«åãŒã‚ã‚Šã¾ã›ã‚“"
msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr "diff 行をå–得中ã«ä½•ã‹å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "Direction"
msgstr ""
msgid "Directory name"
@@ -2444,22 +2627,34 @@ msgid "Disable for this project"
msgstr "ã“ã®ãƒ—ロジェクトã§ã¯ç„¡åŠ¹ã«ã™ã‚‹"
msgid "Disable group Runners"
+msgstr "グループ Runner を無効ã«ã™ã‚‹"
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
msgstr ""
msgid "Discard changes"
msgstr "変更を破棄ã™ã‚‹"
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr "下書ãを破棄"
msgid "Discover GitLab Geo."
-msgstr ""
+msgstr "GitLab Geo ã«ã¤ã„ã¦"
msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr ""
msgid "Dismiss"
-msgstr ""
+msgstr "解除"
msgid "Dismiss Cycle Analytics introduction box"
msgstr "サイクル分æžã®ç´¹ä»‹ã‚’é–‰ã˜ã‚‹"
@@ -2471,7 +2666,7 @@ msgid "Do you want to customize how Google Code email addresses and usernames ar
msgstr ""
msgid "Documentation for popular identity providers"
-msgstr ""
+msgstr "一般的㪠ID プロãƒã‚¤ãƒ€ã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ"
msgid "Domain"
msgstr "ドメイン"
@@ -2516,7 +2711,7 @@ msgid "Due date"
msgstr "期é™"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
-msgstr ""
+msgstr "ã“ã®ãƒ—ロセスã®é€”中ã§ã€GitLab å´ã‹ã‚‰ã® URL ã‚’èžã‹ã‚Œã‚‹ã®ã§ã€æ¬¡ã® URL を使用ã—ã¦ãã ã•ã„。"
msgid "Each Runner can be in one of the following states:"
msgstr ""
@@ -2534,34 +2729,34 @@ msgid "Edit Snippet"
msgstr "スニペットを編集"
msgid "Edit application"
-msgstr ""
+msgstr "アプリケーションã®ç·¨é›†"
msgid "Edit files in the editor and commit changes here"
-msgstr ""
+msgstr "エディターã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’編集ã—ã€ã“ã“ã§å¤‰æ›´ã‚’コミットã—ã¾ã™"
msgid "Edit group: %{group_name}"
-msgstr ""
+msgstr "グループを編集:%{group_name}"
msgid "Edit identity for %{user_name}"
-msgstr ""
+msgstr "%{user_name} ã® ID を編集ã™ã‚‹"
msgid "Elasticsearch"
msgstr "Elasticsearch"
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
-msgstr ""
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr "Elasticsearch ã®çµ±åˆã€‚Elasticsearch AWS IAM。"
msgid "Email"
msgstr "メール"
msgid "Email patch"
-msgstr ""
+msgstr "パッãƒã‚’メールã™ã‚‹"
msgid "Emails"
msgstr "メール"
msgid "Embed"
-msgstr ""
+msgstr "埋ã‚è¾¼ã¿"
msgid "Enable"
msgstr "有効化ã™ã‚‹"
@@ -2573,19 +2768,19 @@ msgid "Enable Pseudonymizer data collection"
msgstr ""
msgid "Enable SAML authentication for this group"
-msgstr ""
+msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã® SAML èªè¨¼ã‚’有効ã«ã™ã‚‹"
msgid "Enable Sentry for error reporting and logging."
-msgstr ""
+msgstr "エラー報告ã¨ãƒ­ã‚°è¨˜éŒ²ã®ãŸã‚ã« Sentry を有効ã«ã™ã‚‹ã€‚"
msgid "Enable and configure InfluxDB metrics."
-msgstr ""
+msgstr "InfluxDB ã®ãƒ¡ãƒˆãƒªã‚¯ã‚¹ã‚’有効ã«ã—ã¦è¨­å®šã™ã‚‹ã€‚"
msgid "Enable and configure Prometheus metrics."
-msgstr ""
+msgstr "Prometheus ã®ãƒ¡ãƒˆãƒªã‚¯ã‚¹ã‚’有効ã«ã—ã¦è¨­å®šã™ã‚‹ã€‚"
msgid "Enable classification control using an external service"
-msgstr ""
+msgstr "外部サービスを使用ã—ã¦ã€åˆ†é¡žåˆ¶å¾¡ã‚’有効ã«ã™ã‚‹ã€‚"
msgid "Enable for this project"
msgstr "ã“ã®ãƒ—ロジェクトã§ã¯æœ‰åŠ¹ã«ã™ã‚‹"
@@ -2603,17 +2798,38 @@ msgid "Enable or disable version check and usage ping."
msgstr "ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãƒã‚§ãƒƒã‚¯ã¨ä½¿ç”¨çŠ¶æ³ã® ping を有効ã«ã™ã‚‹ã€‚"
msgid "Enable reCAPTCHA or Akismet and set IP limits."
-msgstr ""
+msgstr "reCAPTCHA ã¾ãŸã¯ Akismet ã®æœ‰åŠ¹ã€ãŠã‚ˆã³ IP 制é™ã‚’設定ã™ã‚‹ã€‚"
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr "有効"
msgid "Ends at (UTC)"
msgstr "終了時刻 (UTC)"
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr "環境"
@@ -2644,6 +2860,9 @@ msgstr "環境"
msgid "Environments|Environments"
msgstr "環境一覧"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr "ジョブ"
@@ -2657,6 +2876,9 @@ msgid "Environments|No deployments yet"
msgstr "未デプロイ"
msgid "Environments|No pod name has been specified"
+msgstr "pod åãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
msgstr ""
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
@@ -2666,13 +2888,13 @@ msgid "Environments|Open live environment"
msgstr ""
msgid "Environments|Pod logs from"
-msgstr ""
+msgstr " 㮠pod ログ"
msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
-msgstr ""
+msgstr "環境ã®è©³ç´°ã«ã¤ã„ã¦èª­ã‚€"
msgid "Environments|Rollback environment"
msgstr ""
@@ -2692,26 +2914,53 @@ msgstr "更新済ã¿"
msgid "Environments|You don't have any environments right now."
msgstr ""
-msgid "Epic"
+msgid "Environments|protected"
msgstr ""
+msgid "Epic"
+msgstr "エピック"
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
msgid "Epics"
-msgstr ""
+msgstr "エピック"
msgid "Epics Roadmap"
-msgstr ""
+msgstr "エピック ロードマップ"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr "エピックを使用ã™ã‚‹ã¨ã€ãƒ—ロジェクトã®ãƒãƒ¼ãƒˆãƒ•ã‚©ãƒªã‚ªã‚’より効率的ã‹ã¤å°‘ãªã„労力ã§ç®¡ç†ã§ãã¾ã™"
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
msgstr ""
msgid "Error Reporting and Logging"
msgstr "エラー報告ã¨ãƒ­ã‚°"
msgid "Error creating epic"
-msgstr ""
+msgstr "エピックã®ä½œæˆä¸­ã«ã‚¨ãƒ©ãƒ¼"
msgid "Error fetching contributors data."
msgstr ""
@@ -2723,14 +2972,17 @@ msgid "Error fetching network graph."
msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚°ãƒ©ãƒ•ã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Error fetching refs"
-msgstr ""
+msgstr "å‚ç…§ã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿ"
msgid "Error fetching usage ping data."
-msgstr ""
+msgstr "Ping データå–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Error loading branch data. Please try again."
msgstr "ブランムデータã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr "ç›´è¿‘ã®ã‚³ãƒŸãƒƒãƒˆã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -2743,6 +2995,12 @@ msgstr "マージリクエストをロード中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ
msgid "Error loading project data. Please try again."
msgstr "プロジェクトデータã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "通知購読ã®åˆ‡ã‚Šæ›¿ãˆã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -2755,6 +3013,9 @@ msgstr "ã™ã¹ã¦ã® TODO ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’更新中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—
msgid "Error updating todo status."
msgstr "TODO ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’更新中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr "見ç©"
@@ -2797,6 +3058,9 @@ msgstr "ã™ã¹ã¦å±•é–‹"
msgid "Expand sidebar"
msgstr "サイドãƒãƒ¼ã‚’é–‹ã"
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2807,7 +3071,7 @@ msgid "Explore Groups"
msgstr ""
msgid "Explore groups"
-msgstr ""
+msgstr "グループを探索"
msgid "Explore projects"
msgstr "プロジェクトを探ã™"
@@ -2825,7 +3089,7 @@ msgid "External authorization denied access to this project"
msgstr ""
msgid "External authorization request timeout"
-msgstr ""
+msgstr "外部èªè¨¼å‡¦ç†ãŒã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã—ã¾ã—ãŸ"
msgid "ExternalAuthorizationService|Classification Label"
msgstr "分類ラベル"
@@ -2834,10 +3098,10 @@ msgid "ExternalAuthorizationService|Classification label"
msgstr "分類ラベル"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
-msgstr ""
+msgstr "ラベルãŒåˆ†é¡žã•ã‚Œã¦ã„ãªã„ã¨ãã¯ã€`%{default_label}` ãŒæ—¢å®šã®ãƒ©ãƒ™ãƒ«ã¨ã—ã¦ä½¿ç”¨ã•ã‚Œã¾ã™ã€‚"
msgid "Facebook"
-msgstr ""
+msgstr "Facebook"
msgid "Failed"
msgstr "失敗"
@@ -2854,6 +3118,9 @@ msgstr "関連ã™ã‚‹ãƒ–ランãƒã®ç¢ºèªã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
msgid "Failed to remove issue from board, please try again."
msgstr "ボードã®èª²é¡Œã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "パイプラインスケジュールを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ"
@@ -2875,6 +3142,9 @@ msgstr "2月"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã®é …ç›®ã¯ç·¨é›†ã§ããªã„設定ã§ã™ã€‚次㮠Kubernetes クラスターを設定ã§ãã¾ã™ã€‚"
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "ファイル"
@@ -2882,14 +3152,23 @@ msgid "Files (%{human_size})"
msgstr "ファイル (%{human_size})"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
-msgstr ""
+msgstr "下記項目ã«å¿…è¦äº‹é …を入力ã—〠<strong>%{enable_label}</strong>をオンã«ã—ã¦ã€ <strong>%{save_changes}</strong>を押ã—ã¦ãã ã•ã„。"
msgid "Filter"
+msgstr "フィルター"
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
msgstr ""
msgid "Filter by commit message"
msgstr "コミットメッセージã§çµžã‚Šè¾¼ã¿"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "パスã§æ¤œç´¢"
@@ -2902,6 +3181,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr "完了"
@@ -2911,6 +3193,18 @@ msgstr "åˆå›ž"
msgid "FirstPushedBy|pushed by"
msgstr "プッシュã—ãŸäºº"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2938,16 +3232,18 @@ msgstr "フッターメッセージ"
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "フォーク"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "フォーク元"
@@ -2969,6 +3265,9 @@ msgstr "%{provider_title}ã‹ã‚‰"
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -2984,6 +3283,9 @@ msgstr "課題ãŒç™»éŒ²ã•ã‚Œã¦ã‹ã‚‰ãƒ—ロダクションã«ãƒ‡ãƒ—ロイã•ã‚Œ
msgid "From merge request merge until deploy to production"
msgstr "マージリクエストãŒãƒžãƒ¼ã‚¸ã•ã‚Œã¦ã‹ã‚‰ãƒ—ロダクションã«ãƒ‡ãƒ—ロイã•ã‚Œã‚‹ã¾ã§"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Kubernetes クラスターã®è©³ç´°ç”»é¢ã‚’介ã—ã¦ã€ã‚¢ãƒ—リケーションリストã‹ã‚‰ Runner をインストールã—ã¾ã™ã€‚"
@@ -2999,17 +3301,20 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr "åˆæœŸè¨­å®šãƒ©ãƒ™ãƒ«ã‚»ãƒƒãƒˆã‚’生æˆã™ã‚‹"
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
-msgstr ""
+msgstr "Geo ã¯ã€GitLab インスタンスを他ã®åœ°ç†çš„ãªå ´æ‰€ã«è¤‡è£½ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "GeoNodeSyncStatus|Node is failing or broken."
msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
-msgstr ""
+msgstr "ノードãŒé…延ã€éŽè² è·ãŒã‹ã‹ã£ã¦ã„ã‚‹ã€ã¾ãŸã¯åœæ­¢ã‹ã‚‰ã®å›žå¾©ç›´å¾Œã§ã™ã€‚"
msgid "GeoNodes|Checksummed"
msgstr ""
@@ -3024,19 +3329,19 @@ msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
msgstr ""
msgid "GeoNodes|Does not match the primary storage configuration"
-msgstr ""
+msgstr "プライマリストレージ構æˆã¨ä¸€è‡´ã—ã¾ã›ã‚“"
msgid "GeoNodes|Failed"
msgstr "失敗"
msgid "GeoNodes|Full"
-msgstr ""
+msgstr "全体"
msgid "GeoNodes|GitLab version"
-msgstr ""
+msgstr "GitLab ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
msgid "GeoNodes|GitLab version does not match the primary node version"
-msgstr ""
+msgstr "GitLab ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒå„ªå…ˆãƒŽãƒ¼ãƒ‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¨ä¸€è‡´ã—ã¾ã›ã‚“"
msgid "GeoNodes|Health status"
msgstr ""
@@ -3054,19 +3359,19 @@ msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
msgid "GeoNodes|Learn more about Wiki checksum progress"
-msgstr ""
+msgstr "Wiki ã®ãƒã‚§ãƒƒã‚¯ã‚µãƒ ã®é€²æ—ã®è©³ç´°"
msgid "GeoNodes|Learn more about Wiki verification"
-msgstr ""
+msgstr "Wiki ã®æ¤œè¨¼ã®è©³ç´°"
msgid "GeoNodes|Loading nodes"
-msgstr ""
+msgstr "ノードã®èª­ã¿è¾¼ã¿ä¸­"
msgid "GeoNodes|Local LFS objects"
-msgstr ""
+msgstr "ローカル㮠LFS オブジェクト"
msgid "GeoNodes|Local attachments"
-msgstr ""
+msgstr "ローカルã®æ·»ä»˜ãƒ•ã‚¡ã‚¤ãƒ«"
msgid "GeoNodes|Local job artifacts"
msgstr ""
@@ -3075,34 +3380,34 @@ msgid "GeoNodes|New node"
msgstr "æ–°è¦ãƒŽãƒ¼ãƒ‰"
msgid "GeoNodes|Node Authentication was successfully repaired."
-msgstr ""
+msgstr "èªè¨¼ãƒŽãƒ¼ãƒ‰ã®ä¿®å¾©ã«æˆåŠŸã—ã¾ã—ãŸã€‚"
msgid "GeoNodes|Node was successfully removed."
msgstr "ノードã®å‰Šé™¤ã«æˆåŠŸã—ã¾ã—ãŸã€‚"
msgid "GeoNodes|Not checksummed"
-msgstr ""
+msgstr "ãƒã‚§ãƒƒã‚¯ã‚µãƒ ãªã—"
msgid "GeoNodes|Out of sync"
-msgstr ""
+msgstr "åŒæœŸå¯¾è±¡å¤–"
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
msgid "GeoNodes|Replication slot WAL"
-msgstr ""
+msgstr "WAL レプリケーションスロット"
msgid "GeoNodes|Replication slots"
-msgstr ""
+msgstr "レプリケーションスロット"
msgid "GeoNodes|Repositories"
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒª"
msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
-msgstr ""
+msgstr "セカンダリノードã®å¯¾å¿œãƒªãƒã‚¸ãƒˆãƒªã¨ã®ãƒã‚§ãƒƒã‚¯ã‚µãƒ ãŒæ¤œè¨¼æ¸ˆã¿ã®ãƒªãƒã‚¸ãƒˆãƒª"
msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
-msgstr ""
+msgstr "プライマリノードã®å¯¾å¿œãƒªãƒã‚¸ãƒˆãƒªã¨ã®æ¤œè¨¼ãŒå®Œäº†ã—ãŸãƒªãƒã‚¸ãƒˆãƒª"
msgid "GeoNodes|Repository checksum progress"
msgstr ""
@@ -3111,10 +3416,10 @@ msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
-msgstr ""
+msgstr "é¸æŠžçš„"
msgid "GeoNodes|Something went wrong while changing node status"
-msgstr ""
+msgstr "ノードã®çŠ¶æ…‹ã‚’変更中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "GeoNodes|Something went wrong while fetching nodes"
msgstr "ノード読ã¿è¾¼ã¿ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -3123,72 +3428,171 @@ msgid "GeoNodes|Something went wrong while removing node"
msgstr "ノードã®å‰Šé™¤ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "GeoNodes|Something went wrong while repairing node"
-msgstr ""
+msgstr "ノードã®ä¿®å¾©ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "GeoNodes|Storage config"
msgstr "ストレージã®è¨­å®š"
msgid "GeoNodes|Sync settings"
-msgstr ""
+msgstr "åŒæœŸè¨­å®š"
msgid "GeoNodes|Synced"
-msgstr ""
+msgstr "åŒæœŸæ¸ˆã¿"
msgid "GeoNodes|Unused slots"
-msgstr ""
+msgstr "未使用ã®ã‚¹ãƒ­ãƒƒãƒˆ"
msgid "GeoNodes|Unverified"
-msgstr ""
+msgstr "未検証"
msgid "GeoNodes|Used slots"
-msgstr ""
+msgstr "使用済ã¿ã®ã‚¹ãƒ­ãƒƒãƒˆ"
msgid "GeoNodes|Verified"
-msgstr ""
+msgstr "検証済ã¿"
msgid "GeoNodes|Wiki checksum progress"
-msgstr ""
+msgstr " Wiki ã®ãƒã‚§ãƒƒã‚¯ã‚µãƒ ã®é€²æ—状æ³"
msgid "GeoNodes|Wiki verification progress"
-msgstr ""
+msgstr "Wiki 検証ã®é€²æ—状æ³"
msgid "GeoNodes|Wikis"
-msgstr ""
+msgstr "Wiki"
msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
-msgstr ""
+msgstr "セカンダリノードã®å¯¾å¿œ Wiki ã¨ã®ãƒã‚§ãƒƒã‚¯ã‚µãƒ ãŒæ¤œè¨¼æ¸ˆã¿ã® Wiki"
msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr "安全ã§ãªã„ HTTP 接続を使用ã—㦠Geo ノードを設定ã—ã¾ã—ãŸã€‚HTTPS 通信ã®ä½¿ç”¨ã‚’推奨ã—ã¾ã™ã€‚"
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
msgstr ""
msgid "Geo|All projects"
+msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクト"
+
+msgid "Geo|Could not remove tracking entry for an existing project."
msgstr ""
-msgid "Geo|File sync capacity"
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
msgstr ""
+msgid "Geo|File sync capacity"
+msgstr "ファイルåŒæœŸå®¹é‡"
+
msgid "Geo|Groups to synchronize"
+msgstr "åŒæœŸã‚°ãƒ«ãƒ¼ãƒ—"
+
+msgid "Geo|In sync"
msgstr ""
-msgid "Geo|Projects in certain groups"
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
msgstr ""
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr "特定グループã®ãƒ—ロジェクト"
+
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
+msgstr "リãƒã‚¸ãƒˆãƒªåŒæœŸå®¹é‡"
+
+msgid "Geo|Resync"
msgstr ""
-msgid "Geo|Select groups to replicate."
+msgid "Geo|Retry count"
msgstr ""
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr "é¸æŠžã—ãŸã‚°ãƒ«ãƒ¼ãƒ—を複製ã™ã‚‹"
+
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
+msgstr "検証能力"
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
msgstr ""
msgid "Git"
@@ -3225,13 +3629,13 @@ msgid "GitLab Import"
msgstr ""
msgid "GitLab User"
-msgstr ""
+msgstr "GitLab ユーザー"
msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
-msgstr ""
+msgstr "GitLab シングルサインオン㮠URL"
msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
msgstr ""
@@ -3239,6 +3643,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr "Gitaly"
@@ -3260,14 +3667,11 @@ msgstr "戻る"
msgid "Go back"
msgstr "å‰ã«æˆ»ã‚‹"
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "自分ã®ãƒ•ã‚©ãƒ¼ã‚¯ã¸ç§»å‹•"
-
-msgid "GoToYourFork|Fork"
-msgstr "フォーク"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr ""
@@ -3285,7 +3689,7 @@ msgid "Graph"
msgstr "グラフ"
msgid "Group"
-msgstr ""
+msgstr "グループ"
msgid "Group CI/CD settings"
msgstr "CI/CD グループ設定"
@@ -3303,7 +3707,7 @@ msgid "Group avatar"
msgstr ""
msgid "Group details"
-msgstr ""
+msgstr "グループã®è©³ç´°"
msgid "Group info:"
msgstr ""
@@ -3315,39 +3719,48 @@ msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
-msgstr ""
+msgstr "%{dateWord} ã‹ã‚‰"
msgid "GroupRoadmap|Loading roadmap"
msgstr "ロードマップã®èª­ã¿è¾¼ã¿ä¸­"
msgid "GroupRoadmap|Something went wrong while fetching epics"
-msgstr ""
+msgstr "エピックã®èª­ã¿è¾¼ã¿ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "GroupRoadmap|Sorry, no epics matched your search"
-msgstr ""
+msgstr "申ã—訳ã‚ã‚Šã¾ã›ã‚“ã€æ¤œç´¢ã«ãƒžãƒƒãƒã™ã‚‹ã‚¨ãƒ”ックã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
-msgstr ""
+msgstr "ロードマップã¯æ™‚é–“ã®çµŒéŽã¨ã¨ã‚‚ã«ã‚¨ãƒ”ックã®é€²è¡Œã‚’表示ã—ã¾ã™"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "検索対象を広ã’ã‚‹ã«ã¯ã€ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’変更ã™ã‚‹ã‹å‰Šé™¤ã—ã¦ãã ã•ã„。月表示ã§ã¯ã€å…ˆæœˆã€ä»Šæœˆã‚‚ã—ãã¯5ヶ月先ã¾ã§ã®ã‚¨ãƒ”ックã®ã¿ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ &ndash; %{startDate} 〜 %{endDate}"
msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "検索対象を広ã’ã‚‹ã«ã¯ã€ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’変更ã™ã‚‹ã‹å‰Šé™¤ã—ã¦ãã ã•ã„。四åŠæœŸè¡¨ç¤ºã§ã¯ã€å‰å››åŠæœŸã€å½“å››åŠæœŸã‚‚ã—ãã¯4ã¤å…ˆã®å››åŠæœŸã¾ã§ã®ã‚¨ãƒ”ックã®ã¿ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ &ndash; %{startDate} 〜 %{endDate}"
msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "検索対象を広ã’ã‚‹ã«ã¯ã€ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’変更ã™ã‚‹ã‹å‰Šé™¤ã—ã¦ãã ã•ã„。週表示ã§ã¯ã€å…ˆé€±ã€ä»Šé€±ã‚‚ã—ãã¯4週先ã¾ã§ã®ã‚¨ãƒ”ックã®ã¿ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ &ndash; %{startDate} 〜 %{endDate}"
msgid "GroupRoadmap|Until %{dateWord}"
+msgstr "%{dateWord} ã¾ã§"
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
msgstr ""
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
@@ -3375,7 +3788,7 @@ msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name
msgstr "グループã®ãƒ­ãƒƒã‚¯ã‚’ %{ancestor_group_name} ã®å…±æœ‰ã‹ã‚‰å‰Šé™¤"
msgid "Groups"
-msgstr ""
+msgstr "グループ"
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr "グループã¯ã€%{subgroup_docs_link_start}サブグループ%{subgroup_docs_link_end}を作æˆã™ã‚‹ã“ã¨ã§ãƒã‚¹ãƒˆã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
@@ -3387,7 +3800,7 @@ msgid "GroupsDropdown|Groups you visit often will appear here"
msgstr "よã使ã†ã‚°ãƒ«ãƒ¼ãƒ—ã¯ã“ã“ã«è¡¨ç¤ºã•ã‚Œã¾ã™"
msgid "GroupsDropdown|Loading groups"
-msgstr ""
+msgstr "グループã®èª­ã¿è¾¼ã¿ä¸­"
msgid "GroupsDropdown|Search your groups"
msgstr ""
@@ -3413,6 +3826,9 @@ msgstr "グループã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "グループメンãƒãƒ¼ã®æ¨©é™ç®¡ç†ã€ãŠã‚ˆã³ã‚°ãƒ«ãƒ¼ãƒ—内ã®å„プロジェクトã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©é™ã‚’管ç†ã§ãã¾ã™ã€‚"
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ãƒ—ロジェクトを作æˆã™ã‚‹ã€‚"
@@ -3425,23 +3841,23 @@ msgstr "グループを編集"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr "グループã®å”¯ä¸€ã®ã‚ªãƒ¼ãƒŠãƒ¼ã¨ãªã£ã¦ã„ã‚‹ãŸã‚ã€ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã‹ã‚‰é›¢è„±ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。"
-msgid "GroupsTree|Filter by name..."
-msgstr "åå‰ã§çµžã‚Šè¾¼ã¿..."
-
msgid "GroupsTree|Leave this group"
msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã‹ã‚‰é›¢è„±ã™ã‚‹"
msgid "GroupsTree|Loading groups"
msgstr "グループã®èª­ã¿è¾¼ã¿ä¸­"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "ã™ã¿ã¾ã›ã‚“ã€æ¤œç´¢ã«ä¸€è‡´ã™ã‚‹ã‚°ãƒ«ãƒ¼ãƒ—ãŒå­˜åœ¨ã—ã¾ã›ã‚“"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "ã™ã¿ã¾ã›ã‚“ã€æ¤œç´¢ã«ä¸€è‡´ã™ã‚‹ãƒ—ロジェクトã‹ã‚°ãƒ«ãƒ¼ãƒ—ãŒå­˜åœ¨ã—ã¾ã›ã‚“"
+msgid "GroupsTree|Search by name"
+msgstr ""
msgid "Have your users email"
-msgstr ""
+msgstr "ユーザーã«ãƒ¡ãƒ¼ãƒ«ã‚’é€ä¿¡"
msgid "Header message"
msgstr "ヘッダーメッセージ"
@@ -3473,6 +3889,15 @@ msgstr "ヘルプページ"
msgid "Help page text and support page url."
msgstr "ヘルプページテキストã¨ã‚µãƒãƒ¼ãƒˆãƒšãƒ¼ã‚¸URL。"
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "éžè¡¨ç¤º"
@@ -3490,35 +3915,62 @@ msgid "I accept the %{terms_link}"
msgstr "%{terms_link} ã«åŒæ„ã™ã‚‹"
msgid "I accept the|Terms of Service and Privacy Policy"
-msgstr ""
+msgstr "利用è¦ç´„ã¨ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼"
msgid "ID"
msgstr "ID"
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr "コミット"
msgid "IDE|Edit"
msgstr "編集"
-msgid "IDE|Go back"
-msgstr "戻る"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
msgid "IDE|Open in file view"
msgstr "ファイルビューã§é–‹ã"
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr "レビュー"
-msgid "Identifier"
+msgid "IP Address"
msgstr ""
+msgid "Identifier"
+msgstr "識別å­"
+
msgid "Identities"
-msgstr ""
+msgstr "ID"
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3529,7 +3981,7 @@ msgid "If enabled, access to projects will be validated on an external service u
msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
-msgstr ""
+msgstr "GitHub を使用ã—ã¦ã„ã‚‹å ´åˆã€GitHub 上ã®ã‚³ãƒŸãƒƒãƒˆã‚„プルリクエスã‹ã‚‰ãƒ‘イプラインã®çŠ¶æ…‹ã‚’確èªã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚%{more_info_link}"
msgid "If you already have files you can push them using the %{link_to_cli} below."
msgstr ""
@@ -3576,6 +4028,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3585,6 +4040,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr "リãƒã‚¸ãƒˆãƒªã‚’ GitHub ã‹ã‚‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹"
@@ -3603,52 +4061,82 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒåŒæ„ã—ãªã‘ã‚Œã°ãªã‚‰ãªã„利用è¦ç´„ã¨ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼ã‚’å«ã‚ã¾ã™ã€‚"
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
msgstr ""
msgid "Incompatible Project"
+msgstr "互æ›æ€§ã®ãªã„プロジェクト"
+
+msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
msgid "Inline"
+msgstr "インライン"
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
msgstr ""
msgid "Install GitLab Runner"
msgstr ""
msgid "Install Runner on Kubernetes"
-msgstr ""
+msgstr "Kubernetes 㫠Runner をインストール"
msgid "Instance"
msgid_plural "Instances"
-msgstr[0] ""
+msgstr[0] "インスタンス"
-msgid "Instance does not support multiple Kubernetes clusters"
+msgid "Instance Statistics"
msgstr ""
-msgid "Integrations"
+msgid "Instance Statistics visibility"
msgstr ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr "インスタンスã¯ãƒžãƒ«ãƒ Kubernetes クラスターをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“"
+
+msgid "Integrations"
+msgstr "インテグレーション"
+
msgid "Integrations Settings"
msgstr ""
msgid "Interested parties can even contribute by pushing commits if they want to."
-msgstr ""
+msgstr "貢献をã—ãŸã„関係者ã¯ã€ã‚³ãƒŸãƒƒãƒˆã‚’プッシュã™ã‚‹ã“ã¨ã§è²¢çŒ®ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
-msgstr ""
+msgstr "内部 - グループãŠã‚ˆã³å†…部プロジェクトã¯ã€ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ã„ã‚‹ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒè¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "é–“éš”ã®ãƒ‘ターン"
msgid "Introducing Cycle Analytics"
msgstr "サイクル分æžã®ã”紹介"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3659,7 +4147,7 @@ msgid "Issue events"
msgstr "課題イベント"
msgid "IssueBoards|Board"
-msgstr ""
+msgstr "ボード"
msgid "IssueBoards|Boards"
msgstr ""
@@ -3680,7 +4168,7 @@ msgid "January"
msgstr "1月"
msgid "Job"
-msgstr ""
+msgstr "ジョブ"
msgid "Job has been erased"
msgstr "ジョブãŒæ¶ˆåŽ»ã•ã‚Œã¾ã—ãŸ"
@@ -3688,6 +4176,48 @@ msgstr "ジョブãŒæ¶ˆåŽ»ã•ã‚Œã¾ã—ãŸ"
msgid "Jobs"
msgstr "ジョブ"
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
msgid "Jul"
msgstr "7月"
@@ -3700,12 +4230,6 @@ msgstr "6月"
msgid "June"
msgstr "6月"
-msgid "Koding"
-msgstr "Koding"
-
-msgid "Koding Dashboard"
-msgstr ""
-
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -3716,7 +4240,7 @@ msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
msgstr ""
msgid "Kubernetes cluster integration was not removed."
-msgstr ""
+msgstr "Kubernetes クラスターã®çµ±åˆã¯å‰Šé™¤ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸã€‚"
msgid "Kubernetes cluster integration was successfully removed."
msgstr ""
@@ -3728,7 +4252,7 @@ msgid "Kubernetes configured"
msgstr "Kubernetes 構æˆæ¸ˆã¿"
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
-msgstr ""
+msgstr "Kubernetes サービスã®çµ±åˆã¯å»ƒæ­¢ã•ã‚Œã¾ã—ãŸã€‚%{deprecated_message_content} æ–°ã—ã„<a href=\"%{url}\"/>Kubernetes クラスター</a> ã®ãƒšãƒ¼ã‚¸ã‚’使用ã—ã¦ãã ã•ã„"
msgid "LFS"
msgstr "LFS"
@@ -3746,7 +4270,7 @@ msgid "Label actions dropdown"
msgstr ""
msgid "Label lists show all issues with the selected label."
-msgstr ""
+msgstr "ラベル一覧ã«ã¯ã€é¸æŠžã—ãŸãƒ©ãƒ™ãƒ«ãŒä»˜ã„ãŸã™ã¹ã¦ã®èª²é¡ŒãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚"
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
@@ -3764,17 +4288,20 @@ msgid "Labels can be applied to %{features}. Group labels are available for any
msgstr "ラベルを%{features} ã«é©ç”¨ã§ãã¾ã™ã€‚グループラベルã¯ã‚°ãƒ«ãƒ¼ãƒ—内ã®ã™ã¹ã¦ã®ãƒ—ロジェクトã§ä½¿ç”¨ã§ãã¾ã™ã€‚"
msgid "Labels can be applied to issues and merge requests to categorize them."
-msgstr ""
+msgstr "ラベルを使用ã—ã¦ã€èª²é¡Œã‚„マージリクエストを分類ã§ãã¾ã™ã€‚"
msgid "Labels can be applied to issues and merge requests."
-msgstr ""
+msgstr "ラベルã¯èª²é¡Œã¨ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«é©ç”¨ã§ãã¾ã™ã€‚"
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
-msgstr ""
+msgstr "%{labelTitle} <span>ラベルをグループラベルã«æ˜‡æ ¼ã—ã¾ã™ã‹ï¼Ÿ</span>"
msgid "Labels|Promote Label"
msgstr "ラベルã®æ˜‡æ ¼"
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "éŽåŽ»%d日間"
@@ -3785,6 +4312,9 @@ msgstr "最新パイプライン"
msgid "Last commit"
msgstr "最新コミット"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr "最終編集日 %{date}"
@@ -3795,7 +4325,7 @@ msgid "Last update"
msgstr "最終更新"
msgid "Last updated"
-msgstr ""
+msgstr "最終更新"
msgid "LastPushEvent|You pushed to"
msgstr "ã“ã“ã¸ãƒ—ッシュã—ã¾ã—ãŸ"
@@ -3809,11 +4339,14 @@ msgstr "最新ã®å¤‰æ›´"
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
msgid "Learn more about protected branches"
-msgstr ""
+msgstr "ä¿è­·ã•ã‚ŒãŸãƒ–ランãƒã«ã¤ã„ã¦ã®è©³ç´°"
msgid "Learn more in the"
msgstr "詳ã—ã見る:"
@@ -3836,9 +4369,64 @@ msgstr ""
msgid "License"
msgstr "ライセンス"
-msgid "LinkedIn"
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr "ライセンス"
+
+msgid "LicenseManagement|License Management"
msgstr ""
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr "パッケージ"
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr "URL"
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr "ライセンス"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+
+msgid "LinkedIn"
+msgstr "LinkedIn"
+
msgid "List"
msgstr "リスト"
@@ -3848,9 +4436,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr "GitHub リãƒã‚¸ãƒˆãƒªã‚’一覧表示"
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3869,17 +4463,20 @@ msgstr "ロック %{issuableDisplayName}"
msgid "Lock not found"
msgstr "ロックãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
-msgid "Lock to current projects"
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
msgstr ""
+msgid "Lock to current projects"
+msgstr "ç¾åœ¨ã®ãƒ—ロジェクトをロックã™ã‚‹"
+
msgid "Locked"
msgstr "ロック中"
msgid "Locked Files"
-msgstr ""
+msgstr "ロックã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«"
msgid "Locked to current projects"
-msgstr ""
+msgstr "ç¾åœ¨ã®ãƒ—ロジェクトã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™"
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
@@ -3896,11 +4493,14 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
msgid "Manage all notifications"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®é€šçŸ¥ã‚’管ç†"
msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
msgstr ""
@@ -3918,7 +4518,7 @@ msgid "Manage project labels"
msgstr "プロジェクトラベルã®ç®¡ç†"
msgid "Manage your group’s membership while adding another level of security with SAML."
-msgstr ""
+msgstr "グループã®ãƒ¡ãƒ³ãƒãƒ¼ã‚·ãƒƒãƒ—を管ç†ã—ãªãŒã‚‰ã€SAML ã§åˆ¥ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¬ãƒ™ãƒ«ã‚’追加ã—ã¾ã™ã€‚"
msgid "Manifest"
msgstr ""
@@ -3950,9 +4550,21 @@ msgstr "Todo を完了ã«ã™ã‚‹"
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr "5月"
@@ -3963,7 +4575,7 @@ msgid "Members"
msgstr "メンãƒãƒ¼"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
-msgstr ""
+msgstr "メンãƒãƒ¼ãŒã‚°ãƒ«ãƒ¼ãƒ—ã«ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹ã¨ã€ã“ã“ã«ãƒ•ã‚©ãƒ¯ãƒ¼ãƒ‰ã•ã‚Œã¾ã™ã€‚ã“ã‚Œã¯IDプロãƒã‚¤ãƒ€ãƒ¼ã‹ã‚‰å–å¾—ã§ãã¾ã™ã€‚IDプロãƒã‚¤ãƒ€ãƒ¼ã¯\"SSO Service Location\"ã€\"SAML Token Issuance Endpoint\"ã€\"SAML 2.0/W-Federation URL\"ãªã©ã¨ã‚‚呼ã°ã‚Œã¾ã™ã€‚"
msgid "Merge Request"
msgstr "マージリクエスト"
@@ -3984,7 +4596,7 @@ msgid "Merge request"
msgstr "マージリクエスト"
msgid "Merge request approvals"
-msgstr ""
+msgstr "マージリクエスト承èª"
msgid "Merge requests"
msgstr "マージリクエスト"
@@ -4001,15 +4613,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr "検討ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ"
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr "マージ済ã¿"
@@ -4023,25 +4635,28 @@ msgid "Metrics - Influx"
msgstr "メトリクス - Influx"
msgid "Metrics - Prometheus"
+msgstr "メトリクス - Prometheus"
+
+msgid "Metrics and profiling"
msgstr ""
msgid "Metrics|Business"
-msgstr ""
+msgstr "ビジãƒã‚¹"
msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
msgstr ""
msgid "Metrics|Create metric"
-msgstr ""
+msgstr "メトリクスを作æˆ"
msgid "Metrics|Edit metric"
-msgstr ""
+msgstr "メトリクスを編集"
msgid "Metrics|Environment"
msgstr "環境"
msgid "Metrics|For grouping similar metrics"
-msgstr ""
+msgstr "é¡žä¼¼ã®ãƒ¡ãƒˆãƒªã‚¯ã‚¹ã‚’グループ化"
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
@@ -4050,31 +4665,31 @@ msgid "Metrics|Learn about environments"
msgstr ""
msgid "Metrics|Legend label (optional)"
-msgstr ""
+msgstr "汎用ラベル(オプション)"
msgid "Metrics|Must be a valid PromQL query."
-msgstr ""
+msgstr "有効㪠PromQL クエリã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
msgid "Metrics|Name"
msgstr "åå‰"
msgid "Metrics|New metric"
-msgstr ""
+msgstr "æ–°è¦ãƒ¡ãƒˆãƒªã‚¯ã‚¹"
msgid "Metrics|No deployed environments"
msgstr ""
msgid "Metrics|Prometheus Query Documentation"
-msgstr ""
+msgstr "Prometheus クエリã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ"
msgid "Metrics|Query"
-msgstr ""
+msgstr "クエリ"
msgid "Metrics|Response"
msgstr "レスãƒãƒ³ã‚¹"
msgid "Metrics|System"
-msgstr ""
+msgstr "システム"
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -4098,25 +4713,25 @@ msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
msgstr ""
msgid "Metrics|Unit label"
-msgstr ""
+msgstr "ユニットラベル"
msgid "Metrics|Used as a title for the chart"
-msgstr ""
+msgstr "ãƒãƒ£ãƒ¼ãƒˆã®ã‚¿ã‚¤ãƒˆãƒ«ã¨ã—ã¦ä½¿ç”¨ã•ã‚Œã¾ã™"
msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
msgstr ""
msgid "Metrics|Y-axis label"
-msgstr ""
+msgstr "Y軸ラベル"
msgid "Metrics|e.g. HTTP requests"
-msgstr ""
+msgstr "例:HTTP リクエスト"
msgid "Metrics|e.g. Requests/second"
msgstr ""
msgid "Metrics|e.g. Throughput"
-msgstr ""
+msgstr "例:スループット"
msgid "Metrics|e.g. rate(http_requests_total[5m])"
msgstr ""
@@ -4127,9 +4742,24 @@ msgstr ""
msgid "Milestone"
msgstr "マイルストーン"
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr "マイルストーン"
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr "マイルストーンã®å‰Šé™¤"
@@ -4148,9 +4778,30 @@ msgstr "%{milestoneTitle} をグループマイルストーンã«æ˜‡æ ¼ã—ã¾ã™
msgid "Milestones|Promote Milestone"
msgstr "マイルストーンã®æ˜‡æ ¼"
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr "ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。"
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "SSH éµã‚’追加"
@@ -4164,14 +4815,11 @@ msgid "Monitoring"
msgstr "監視"
msgid "Months"
-msgstr ""
+msgstr "月"
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4191,7 +4839,7 @@ msgid "Move issue"
msgstr ""
msgid "Multiple issue boards"
-msgstr ""
+msgstr "マルãƒèª²é¡Œãƒœãƒ¼ãƒ‰"
msgid "Name"
msgstr "åå‰"
@@ -4203,7 +4851,7 @@ msgid "Name your individual key via a title"
msgstr ""
msgid "Name:"
-msgstr ""
+msgstr "åå‰:"
msgid "Nav|Help"
msgstr "ヘルプ"
@@ -4218,16 +4866,19 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹"
msgid "Network"
+msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯"
+
+msgid "Never"
msgstr ""
msgid "New"
-msgstr ""
+msgstr "æ–°ã—ã„"
msgid "New Application"
-msgstr ""
+msgstr "æ–°ã—ã„アプリケーション"
msgid "New Group"
-msgstr ""
+msgstr "æ–°ã—ã„グループ"
msgid "New Identity"
msgstr "æ–°ã—ã„ ID"
@@ -4243,10 +4894,10 @@ msgid "New Pipeline Schedule"
msgstr "æ–°è¦ãƒ‘イプラインスケジュール"
msgid "New Snippet"
-msgstr ""
+msgstr "æ–°è¦ã‚¹ãƒ‹ãƒšãƒƒãƒˆ"
msgid "New Snippets"
-msgstr ""
+msgstr "æ–°è¦ã‚¹ãƒ‹ãƒšãƒƒãƒˆ"
msgid "New branch"
msgstr "æ–°è¦ãƒ–ランãƒ"
@@ -4258,7 +4909,7 @@ msgid "New directory"
msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª"
msgid "New epic"
-msgstr ""
+msgstr "æ–°ã—ã„エピック"
msgid "New file"
msgstr "æ–°è¦ãƒ•ã‚¡ã‚¤ãƒ«"
@@ -4267,7 +4918,7 @@ msgid "New group"
msgstr "æ–°è¦ã‚°ãƒ«ãƒ¼ãƒ—"
msgid "New identity"
-msgstr ""
+msgstr "æ–°ã—ã„ ID"
msgid "New issue"
msgstr "æ–°è¦èª²é¡Œ"
@@ -4297,23 +4948,32 @@ msgid "New tag"
msgstr "æ–°è¦ã‚¿ã‚°"
msgid "New..."
-msgstr ""
+msgstr "æ–°è¦...\t"
msgid "No"
msgstr "ã„ã„ãˆ"
msgid "No Label"
-msgstr ""
+msgstr "ラベルãŒã‚ã‚Šã¾ã›ã‚“"
msgid "No assignee"
msgstr "担当者ãªã—"
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr "変更ãªã—"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "Gitaly サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ログを確èªã—ã¦ãã ã•ã„ï¼"
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr "期é™ãªã—"
@@ -4324,7 +4984,7 @@ msgid "No file chosen"
msgstr "ファイルãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "No files found"
-msgstr ""
+msgstr "ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
msgid "No files found."
msgstr "ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。"
@@ -4335,6 +4995,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4347,6 +5010,9 @@ msgstr "メッセージã¯è¨˜éŒ²ã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4359,15 +5025,27 @@ msgstr ""
msgid "No repository"
msgstr "リãƒã‚¸ãƒˆãƒªãŒã‚ã‚Šã¾ã›ã‚“"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "スケジュールãªã—"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr "ãªã—"
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr "マージã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
@@ -4381,22 +5059,25 @@ msgid "Not available for protected branches"
msgstr "ä¿è­·ã•ã‚ŒãŸãƒ–ランãƒã§ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“"
msgid "Not confidential"
-msgstr ""
+msgstr "機密ã§ã¯ãªã„"
msgid "Not enough data"
msgstr "データä¸è¶³"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "マスターブランãƒã¯è‡ªå‹•çš„ã«ä¿è­·ã•ã‚Œã‚‹ã“ã¨ã«ç•™æ„ã—ã¦ãã ã•ã„。%{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "注: 管ç†è€…ã¨ã—ã¦%{github_integration_link} を設定ã™ã‚‹ã¨ã€GitHub 経由ã®ãƒ­ã‚°ã‚¤ãƒ³ã‚’許å¯ã—ã€å€‹äººç”¨ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’生æˆã›ãšã«ãƒªãƒã‚¸ãƒˆãƒªã«æŽ¥ç¶šã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "注: GitLab ã®ç®¡ç†è€…ã«%{github_integration_link} を設定ã—ã¦ã€GitHub 経由ã®ãƒ­ã‚°ã‚¤ãƒ³ã‚’許å¯ã—ã€å€‹äººç”¨ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’生æˆã›ãšã«ãƒªãƒã‚¸ãƒˆãƒªã«æŽ¥ç¶šã§ããªã„ã‹å•ã„åˆã‚ã›ãã ã•ã„。"
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
@@ -4419,6 +5100,9 @@ msgstr "パイプラインã«å¤±æ•—"
msgid "NotificationEvent|Merge merge request"
msgstr "マージリクエストをマージ"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "æ–°è¦èª²é¡Œ"
@@ -4489,7 +5173,11 @@ msgid "OfSearchInADropdown|Filter"
msgstr "フィルター"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
-msgstr ""
+msgstr "インãƒãƒ¼ãƒˆã™ã‚‹ã¨ãƒªãƒã‚¸ãƒˆãƒªã¯ SSH ã§ãƒŸãƒ©ãƒ¼ãƒªãƒ³ã‚°ã§ãã¾ã™ã€‚詳細㯠%{ssh_link} ã‚’å‚ç…§ã—ã¦ãã ã•ã„。"
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
@@ -4497,10 +5185,13 @@ msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
+msgstr "次ã®ã‚³ãƒŸãƒƒãƒˆã®ã‚³ãƒ¡ãƒ³ãƒˆã®ã¿ãŒä¸‹ã«è¡¨ç¤ºã•ã‚Œã¾ã™"
+
+msgid "Only mirror protected branches"
msgstr ""
msgid "Only project members can comment."
@@ -4516,7 +5207,7 @@ msgid "Open in Xcode"
msgstr "Xcode ã§é–‹ã"
msgid "Open sidebar"
-msgstr ""
+msgstr "サイドãƒãƒ¼ã‚’é–‹ã"
msgid "Open source software to collaborate on code"
msgstr ""
@@ -4549,16 +5240,16 @@ msgid "Options"
msgstr "オプション"
msgid "Or you can choose one of the suggested colors below"
-msgstr ""
+msgstr "ã¾ãŸã¯ã€ä¸‹è¨˜ã®ã„ãšã‚Œã‹ã®è‰²ã‚’é¸æŠžã§ãã¾ã™"
msgid "Other Labels"
msgstr "ãã®ä»–ã®ãƒ©ãƒ™ãƒ«"
msgid "Other information"
-msgstr ""
+msgstr "ãã®ä»–ã®æƒ…å ±"
msgid "Otherwise it is recommended you start with one of the options below."
-msgstr ""
+msgstr "ãã†ã§ãªã„å ´åˆã¯ã€æ¬¡ã®ã„ãšã‚Œã‹ã®ã‚ªãƒ—ションã§é–‹å§‹ã™ã‚‹ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚"
msgid "Outbound requests"
msgstr ""
@@ -4566,9 +5257,21 @@ msgstr ""
msgid "Overview"
msgstr "概è¦"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "オーナー"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr "パッケージ"
+
msgid "Pages"
msgstr "Pages"
@@ -4576,37 +5279,43 @@ msgid "Pagination|Last »"
msgstr "最後 »"
msgid "Pagination|Next"
-msgstr ""
+msgstr "次ページ"
msgid "Pagination|Prev"
-msgstr ""
+msgstr "å‰ãƒšãƒ¼ã‚¸"
msgid "Pagination|« First"
msgstr "« 最åˆ"
msgid "Part of merge request changes"
-msgstr ""
+msgstr "マージリクエストã§ã®å¤‰æ›´ç®‡æ‰€"
msgid "Password"
msgstr "パスワード"
msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
-msgstr ""
+msgstr "ã‚ãªãŸã® SSH 公開éµã‚’貼り付ã‘ã¾ã™ã€‚ã“ã®éµã¯é€šå¸¸ã€ãƒ•ã‚¡ã‚¤ãƒ« '~/.ssh/id_rsa.pub' 内ã«ã‚ã‚Šã€'ssh-rsa' ã¨ã„ã†æ–‡å­—列ã‹ã‚‰å§‹ã¾ã‚Šã¾ã™ã€‚SSH ã®ç§˜å¯†éµã‚’使用ã—ãªã„よã†ã«ã—ã¦ãã ã•ã„。"
msgid "Path:"
-msgstr ""
+msgstr "パス:"
msgid "Pause"
+msgstr "åœæ­¢"
+
+msgid "Paused Runners don't accept new jobs"
msgstr ""
msgid "Pending"
msgstr "ä¿ç•™ä¸­"
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
msgid "Perform advanced options such as changing path, transferring, or removing the group."
-msgstr ""
+msgstr "パスã®å¤‰æ›´ã€è»¢é€ã€ã‚°ãƒ«ãƒ¼ãƒ—ã®å‰Šé™¤ãªã©ã®é«˜åº¦ãªã‚ªãƒ—ションを実行ã—ã¾ã™ã€‚"
msgid "Performance optimization"
msgstr "パフォーマンスã®æœ€é©åŒ–"
@@ -4620,6 +5329,9 @@ msgstr "個人ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³"
msgid "Pipeline"
msgstr "パイプライン"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "パイプラインã®é€²æ—状æ³"
@@ -4630,7 +5342,7 @@ msgid "Pipeline Schedules"
msgstr "パイプラインスケジュール"
msgid "Pipeline quota"
-msgstr ""
+msgstr "パイプラインã®ã‚¯ã‚©ãƒ¼ã‚¿"
msgid "Pipeline triggers"
msgstr ""
@@ -4705,16 +5417,19 @@ msgid "Pipelines|CI Lint"
msgstr "CI Lint"
msgid "Pipelines|Clear Runner Caches"
+msgstr "Runner ã®ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’削除"
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
msgstr ""
msgid "Pipelines|Get started with Pipelines"
-msgstr ""
+msgstr "パイプラインã®åˆ©ç”¨ã‚’開始ã™ã‚‹"
msgid "Pipelines|Loading Pipelines"
msgstr "パイプラインを読ã¿è¾¼ã¿ä¸­"
msgid "Pipelines|Project cache successfully reset."
-msgstr ""
+msgstr "プロジェクトã®ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’正常ã«ãƒªã‚»ãƒƒãƒˆã—ã¾ã—ãŸã€‚"
msgid "Pipelines|Run Pipeline"
msgstr "パイプライン実行"
@@ -4723,19 +5438,22 @@ msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
msgid "Pipelines|There are currently no %{scope} pipelines."
-msgstr ""
+msgstr "%{scope} パイプラインã¯ç¾åœ¨ã‚ã‚Šã¾ã›ã‚“。"
msgid "Pipelines|There are currently no pipelines."
+msgstr "パイプラインã¯ç¾åœ¨ã‚ã‚Šã¾ã›ã‚“。"
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
-msgstr ""
+msgstr "ã“ã®ãƒ—ロジェクトã¯ç¾åœ¨ãƒ‘イプラインを実行ã™ã‚‹ã‚ˆã†ã«è¨­å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。"
msgid "Pipeline|Create for"
-msgstr ""
+msgstr "実行対象"
msgid "Pipeline|Create pipeline"
-msgstr ""
+msgstr "パイプラインを作æˆ"
msgid "Pipeline|Existing branch name or tag"
msgstr ""
@@ -4747,7 +5465,7 @@ msgid "Pipeline|Search branches"
msgstr "ブランãƒã®æ¤œç´¢"
msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
-msgstr ""
+msgstr "ã“ã®å®Ÿè¡Œã§ä½¿ç”¨ã•ã‚Œã‚‹å¤‰æ•°ã®å€¤ã‚’指定ã—ã¾ã™ã€‚ %{settings_link} ã§æŒ‡å®šã•ã‚ŒãŸå€¤ãŒãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã§ä½¿ç”¨ã•ã‚Œã¾ã™ã€‚"
msgid "Pipeline|Stop pipeline"
msgstr "パイプラインã®åœæ­¢"
@@ -4776,17 +5494,11 @@ msgstr "ステージã‚ã‚Š"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr "PlantUML"
msgid "Play"
-msgstr ""
+msgstr "実行"
msgid "Please accept the Terms of Service before continuing."
msgstr "続ã‘ã‚‹å‰ã«ã€åˆ©ç”¨è¦ç´„ã«åŒæ„ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
@@ -4801,7 +5513,7 @@ msgid "Please note that this application is not provided by GitLab and you shoul
msgstr ""
msgid "Please select at least one filter to see results"
-msgstr ""
+msgstr "çµæžœã‚’表示ã™ã‚‹ã«ã¯ã€å°‘ãªãã¨ã‚‚1ã¤ã®ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’é¸æŠžã—ã¦ãã ã•ã„"
msgid "Please solve the reCAPTCHA"
msgstr "reCAPTCHA を解決ã—ã¦ãã ã•ã„"
@@ -4810,37 +5522,46 @@ msgid "Please try again"
msgstr "ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„"
msgid "Please wait while we connect to your repository. Refresh at will."
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã«æŽ¥ç¶šã§ãã‚‹ã¾ã§ã€ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„。完了ã™ã‚‹ã¨ã€ç”»é¢ãŒè‡ªå‹•çš„ã«æ›´æ–°ã•ã‚Œã¾ã™ã€‚"
msgid "Please wait while we import the repository for you. Refresh at will."
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã‚’インãƒãƒ¼ãƒˆã—ã¦ã„ã‚‹ã®ã§ã€ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„。完了ã™ã‚‹ã¨ã€ç”»é¢ãŒè‡ªå‹•çš„ã«æ›´æ–°ã•ã‚Œã¾ã™ã€‚"
msgid "Preferences"
msgstr "基本設定"
msgid "Preferences|Navigation theme"
+msgstr "ナビゲーションテーマ"
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
msgstr ""
msgid "Primary"
msgstr ""
msgid "Prioritize"
-msgstr ""
+msgstr "優先順ä½ã‚’付ã‘ã‚‹"
msgid "Prioritize label"
-msgstr ""
+msgstr "ラベルã«å„ªå…ˆé †ä½ã‚’ã¤ã‘ã‚‹"
msgid "Prioritized Labels"
-msgstr ""
+msgstr "優先ラベル"
msgid "Prioritized label"
-msgstr ""
+msgstr "優先ラベル"
msgid "Private - Project access must be granted explicitly to each user."
-msgstr ""
+msgstr "プライベート - å„ユーザーã«ãƒ—ロジェクトã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©é™ã‚’明示的ã«è¨±å¯ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
msgid "Private - The group and its projects can only be viewed by members."
-msgstr ""
+msgstr "プライベート - グループã¨ãƒ—ロジェクトã¯ãƒ¡ãƒ³ãƒãƒ¼ã®ã¿ãŒé–²è¦§ã§ãã¾ã™ã€‚"
msgid "Private projects can be created in your personal namespace with:"
msgstr ""
@@ -4851,18 +5572,48 @@ msgstr "プロフィール"
msgid "Profile Settings"
msgstr ""
-msgid "Profiles|Account scheduled for removal."
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
msgstr ""
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr "削除予定ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€‚"
+
msgid "Profiles|Add key"
+msgstr "キーを追加"
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
msgstr ""
msgid "Profiles|Change username"
msgstr "ユーザーåã®å¤‰æ›´"
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr "ç¾åœ¨ã®ãƒ‘ス: %{path}"
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr "アカウント削除"
@@ -4875,40 +5626,127 @@ msgstr "ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Profiles|Deleting an account has the following effects:"
msgstr "アカウントを削除ã™ã‚‹ã¨æ¬¡ã®ã‚ˆã†ãªå½±éŸ¿ãŒã‚ã‚Šã¾ã™:"
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr "パスワードãŒæ­£ã—ãã‚ã‚Šã¾ã›ã‚“"
msgid "Profiles|Invalid username"
msgstr "ユーザーåãŒæ­£ã—ãã‚ã‚Šã¾ã›ã‚“"
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr "パス"
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "確èªã®ãŸã‚ %{confirmationValue} を入力ã—ã¦ãã ã•ã„:"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr "ユーザーåã‚’æ›´æ–°"
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr "ユーザーåã®å¤‰æ›´ã«å¤±æ•—ã—ã¾ã—㟠- %{message}"
msgid "Profiles|Username successfully changed"
msgstr "ユーザーåã¯æ­£å¸¸ã«å¤‰æ›´ã•ã‚Œã¾ã—ãŸ"
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’削除ã™ã‚‹æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“"
msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
-msgstr ""
+msgstr "アカウントを削除ã™ã‚‹å‰ã«ã“れらã®ã‚°ãƒ«ãƒ¼ãƒ—を削除ã€ã¾ãŸã¯æ‰€æœ‰æ¨©ã‚’譲渡ã—ã¦ãã ã•ã„。"
msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ç¾åœ¨ã“れらã®ã‚°ãƒ«ãƒ¼ãƒ—ã®ã‚ªãƒ¼ãƒŠãƒ¼ã§ã™:"
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
msgstr ""
msgid "Profiles|e.g. My MacBook key"
@@ -4918,7 +5756,7 @@ msgid "Profiles|your account"
msgstr "ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ"
msgid "Profiling - Performance bar"
-msgstr ""
+msgstr "プロファイリング - パフォーマンスãƒãƒ¼"
msgid "Programming languages used in this repository"
msgstr "ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã§ä½¿ç”¨ã•ã‚Œã¦ã„るプログラミング言語"
@@ -4944,6 +5782,9 @@ msgstr "'%{project_name}' プロジェクトã¯æ­£å¸¸ã«æ›´æ–°ã•ã‚Œã¾ã—ãŸã€‚
msgid "Project Badges"
msgstr "プロジェクトãƒãƒƒã‚¸"
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "ユーザーã”ã¨ã«ãƒ—ロジェクトアクセスã®æ¨©é™ã‚’指定ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。"
@@ -4969,22 +5810,25 @@ msgid "Project export started. A download link will be sent by email."
msgstr "プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’開始ã—ã¾ã—ãŸã€‚ダウンロードã®ãƒªãƒ³ã‚¯ã¯ãƒ¡ãƒ¼ãƒ«ã§é€ä¿¡ã—ã¾ã™"
msgid "Project name"
+msgstr "プロジェクトå"
+
+msgid "Project slug"
msgstr ""
msgid "ProjectActivityRSS|Subscribe"
msgstr "講読"
msgid "ProjectCreationLevel|Allowed to create projects"
-msgstr ""
+msgstr "プロジェクトã®ä½œæˆã‚’許å¯ã™ã‚‹"
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
msgid "ProjectCreationLevel|Developers + Maintainers"
-msgstr ""
+msgstr "Developers + Maintainers"
msgid "ProjectCreationLevel|Maintainers"
-msgstr ""
+msgstr "Maintainers"
msgid "ProjectCreationLevel|No one"
msgstr ""
@@ -4998,10 +5842,37 @@ msgstr "記録ãªã—"
msgid "ProjectLifecycle|Stage"
msgstr "ステージ"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr "ãƒãƒƒã‚¸"
+
msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr "ã“ã®è¨­å®šã‚’変更ã™ã‚‹ã«ã¯ç®¡ç†è€…ã«é€£çµ¡ã—ã¦ãã ã•ã„。"
+
+msgid "ProjectSettings|Customize your project badges."
msgstr ""
msgid "ProjectSettings|Failed to protect the tag"
@@ -5010,9 +5881,12 @@ msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
-msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgid "ProjectSettings|Learn more about badges."
msgstr ""
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr "ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã«ã¯ç½²åã•ã‚ŒãŸã‚³ãƒŸãƒƒãƒˆã®ã¿ãƒ—ッシュã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -5038,17 +5912,20 @@ msgid "ProjectsDropdown|Loading projects"
msgstr ""
msgid "ProjectsDropdown|Projects you visit often will appear here"
-msgstr ""
+msgstr "よã使ã†ãƒ—ロジェクトã¯ã“ã“ã«è¡¨ç¤ºã•ã‚Œã¾ã™"
msgid "ProjectsDropdown|Search your projects"
msgstr "プロジェクトを検索"
msgid "ProjectsDropdown|Something went wrong on our end."
-msgstr ""
+msgstr "å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "検索æ¡ä»¶ã«ä¸€è‡´ã—ãŸãƒ—ロジェクトã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5086,7 +5963,7 @@ msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr "%{metrics} ã® %{exporters} ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ"
msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
-msgstr ""
+msgstr "<p class=\"text-tertiary\"><a href=\"%{docsUrl}\">共通メトリクス</a>ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</p>"
msgid "PrometheusService|Active"
msgstr "アクティブ"
@@ -5095,28 +5972,28 @@ msgid "PrometheusService|Auto configuration"
msgstr ""
msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
-msgstr ""
+msgstr "プロジェクト環境を監視ã™ã‚‹ãŸã‚ã€ã‚¯ãƒ©ã‚¹ã‚¿ãƒ¼ã« Prometheus を自動的ã«ãƒ‡ãƒ—ロイã—設定ã™ã‚‹"
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
msgstr ""
msgid "PrometheusService|Common metrics"
-msgstr ""
+msgstr "共通メトリクス"
msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
msgstr ""
msgid "PrometheusService|Custom metrics"
-msgstr ""
+msgstr "カスタムメトリクス"
msgid "PrometheusService|Finding and configuring metrics..."
-msgstr ""
+msgstr "メトリクスã®æ¤œç´¢ã¨è¨­å®š..."
msgid "PrometheusService|Finding custom metrics..."
msgstr "カスタムメトリクスを検索..."
msgid "PrometheusService|Install Prometheus on clusters"
-msgstr ""
+msgstr "クラスター㫠Prometheus をインストール"
msgid "PrometheusService|Manage clusters"
msgstr "クラスター管ç†"
@@ -5125,13 +6002,13 @@ msgid "PrometheusService|Manual configuration"
msgstr "手動構æˆ"
msgid "PrometheusService|Metrics"
-msgstr ""
+msgstr "メトリクス一覧"
msgid "PrometheusService|Missing environment variable"
msgstr "未設定ã®ç’°å¢ƒå¤‰æ•°"
msgid "PrometheusService|More information"
-msgstr ""
+msgstr "詳細情報"
msgid "PrometheusService|New metric"
msgstr "æ–°è¦ãƒ¡ãƒˆãƒªã‚¯ã‚¹"
@@ -5140,19 +6017,19 @@ msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example
msgstr ""
msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
-msgstr ""
+msgstr "Prometheus ã¯ã‚¯ãƒ©ã‚¹ã‚¿ãƒ¼ä¸Šã§è‡ªå‹•çš„ã«ç®¡ç†ã•ã‚Œã¦ã„ã¾ã™"
msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
msgstr ""
msgid "PrometheusService|Time-series monitoring service"
-msgstr ""
+msgstr "時系列監視サービス"
msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
msgstr ""
msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
-msgstr ""
+msgstr "クラスター㫠Prometheus をインストールã™ã‚‹ã«ã¯ã€ä»¥ä¸‹ã®æ‰‹å‹•è¨­å®šã‚’無効ã«ã—ã¦ãã ã•ã„"
msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
msgstr ""
@@ -5161,19 +6038,19 @@ msgid "Promote"
msgstr "昇格"
msgid "Promote these project milestones into a group milestone."
-msgstr ""
+msgstr "ã“れらã®ãƒ—ロジェクトマイルストーンをグループマイルストーンã«æ˜‡æ ¼ã™ã‚‹"
msgid "Promote to Group Milestone"
msgstr "グループマイルストーンã«æ˜‡æ ¼"
msgid "Promote to group label"
-msgstr ""
+msgstr "グループラベルã¸æ˜‡æ ¼"
msgid "Promotions|Don't show me this again"
msgstr "次回ã‹ã‚‰ã¯ã“ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’表示ã—ãªã„"
msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
-msgstr ""
+msgstr "エピックを使用ã™ã‚‹ã¨ã€ãƒ—ロジェクトやマイルストーンをã¾ãŸãŒã‚‹å…±é€šãƒ†ãƒ¼ãƒžã®èª²é¡Œã‚°ãƒ«ãƒ¼ãƒ—を追跡ã§ãã€ãƒ—ロジェクトã®ãƒãƒ¼ãƒˆãƒ•ã‚©ãƒªã‚ªã‚’より効率的ã«ç°¡å˜ã«ç®¡ç†ã§ãã¾ã™ã€‚"
msgid "Promotions|This feature is locked."
msgstr "ã“ã®æ©Ÿèƒ½ã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
@@ -5181,6 +6058,54 @@ msgstr "ã“ã®æ©Ÿèƒ½ã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
msgid "Promotions|Upgrade plan"
msgstr "プランをアップグレード"
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5191,7 +6116,7 @@ msgid "Pseudonymizer data collection"
msgstr ""
msgid "Public - The group and any public projects can be viewed without any authentication."
-msgstr ""
+msgstr "公開- グループãŠã‚ˆã³å…¬é–‹ãƒ—ロジェクトã¯èªè¨¼ç„¡ã—ã§é–²è¦§ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™"
msgid "Public - The project can be accessed without any authentication."
msgstr "公開 - プロジェクトã¯èªè¨¼ç„¡ã—ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™"
@@ -5199,6 +6124,12 @@ msgstr "公開 - プロジェクトã¯èªè¨¼ç„¡ã—ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™"
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5209,22 +6140,22 @@ msgid "Push project from command line"
msgstr "コマンドラインã‹ã‚‰ãƒ—ロジェクトをプッシュ"
msgid "Push to create a project"
-msgstr ""
+msgstr "プッシュã—ã¦ãƒ—ロジェクトを作æˆã™ã‚‹"
msgid "PushRule|Committer restriction"
-msgstr ""
+msgstr "コミッター制é™"
msgid "Pushed"
msgstr "プッシュ済ã¿"
msgid "Pushes"
-msgstr ""
+msgstr "プッシュ"
msgid "Quarters"
msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes."
-msgstr ""
+msgstr "クイックアクションã¯èª²é¡Œã®èª¬æ˜Žã¨ã‚³ãƒ¡ãƒ³ãƒˆæ¬„ã§ä½¿ç”¨ã§ãã¾ã™ã€‚"
msgid "Read more"
msgstr "続ãを読む"
@@ -5238,17 +6169,30 @@ msgstr "Readme"
msgid "Real-time features"
msgstr "リアルタイム機能"
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr "å‚ç…§:"
msgid "Refresh"
+msgstr "æ›´æ–°"
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
msgstr ""
msgid "Register / Sign In"
msgstr "登録 / サインイン"
msgid "Register and see your runners for this group."
-msgstr ""
+msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã® Runner ã®ç™»éŒ²ã¨ç¢ºèª"
msgid "Register and see your runners for this project."
msgstr ""
@@ -5290,20 +6234,80 @@ msgid "Remove avatar"
msgstr "ã‚¢ãƒã‚¿ãƒ¼ã‚’削除"
msgid "Remove priority"
-msgstr ""
+msgstr "優先度を削除"
msgid "Remove project"
msgstr "プロジェクトを削除"
-msgid "Repair authentication"
+msgid "Rename"
msgstr ""
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr "èªè¨¼ã®ä¿®å¾©"
+
msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr ""
msgid "Repo by URL"
msgstr "リãƒã‚¸ãƒˆãƒª URL"
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr "リãƒã‚¸ãƒˆãƒª"
@@ -5314,19 +6318,19 @@ msgid "Repository URL"
msgstr ""
msgid "Repository has no locks."
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã›ã‚“。"
msgid "Repository maintenance"
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã®ä¿å®ˆ"
msgid "Repository mirror"
msgstr "リãƒã‚¸ãƒˆãƒªãƒŸãƒ©ãƒ¼"
msgid "Repository storage"
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸"
msgid "RepositorySettingsAccessLevel|Select"
-msgstr ""
+msgstr "é¸æŠž"
msgid "Request Access"
msgstr "アクセス権é™ã‚’リクエストã™ã‚‹"
@@ -5335,7 +6339,7 @@ msgid "Requests Profiles"
msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
-msgstr ""
+msgstr "GitLab ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹éš›ã«ã€ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒåˆ©ç”¨è¦ç´„ã«åŒæ„ã™ã‚‹ã“ã¨ã‚’è¦æ±‚ã—ã¾ã™ã€‚"
msgid "Reset git storage health information"
msgstr "git ストレージã®æ­£å¸¸æ€§æƒ…報をリセット"
@@ -5355,14 +6359,26 @@ msgstr "ソースブランãƒã§ã®ç«¶åˆã‚’解決ã™ã‚‹"
msgid "Resolve discussion"
msgstr "検討を解決"
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr "å†é–‹"
msgid "Retry"
-msgstr ""
+msgstr "å†è©¦è¡Œ"
msgid "Retry this job"
msgstr "ジョブをå†è©¦è¡Œã—ã¦ãã ã•ã„"
@@ -5370,9 +6386,12 @@ msgstr "ジョブをå†è©¦è¡Œã—ã¦ãã ã•ã„"
msgid "Retry verification"
msgstr "検証をå†è©¦è¡Œã—ã¦ãã ã•ã„"
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
-msgstr[0] ""
+msgstr[0] "値を表示ã™ã‚‹"
msgid "Revert this commit"
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã‚’リãƒãƒ¼ãƒˆ"
@@ -5387,7 +6406,7 @@ msgid "Review the process for configuring service providers in your identity pro
msgstr ""
msgid "Reviewing"
-msgstr ""
+msgstr "レビュー中"
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
@@ -5399,46 +6418,88 @@ msgid "Roadmap"
msgstr "ロードマップ"
msgid "Run CI/CD pipelines for external repositories"
+msgstr "外部リãƒã‚¸ãƒˆãƒªç”¨ CI/CD パイプラインを実行"
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
msgstr ""
msgid "Runner token"
+msgstr "Runner トークン"
+
+msgid "Runner will not receive any new jobs"
msgstr ""
msgid "Runners"
-msgstr ""
+msgstr "Runner"
msgid "Runners API"
-msgstr ""
+msgstr "Runner API"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr "Runner ã¯åˆ¥ã€…ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€ã‚µãƒ¼ãƒãƒ¼ã€ã•ã‚‰ã«ã¯ã‚ãªãŸã®ãƒ­ãƒ¼ã‚«ãƒ«ãƒžã‚·ãƒ¼ãƒ³ã«ã‚‚é…ç½®ã§ãã¾ã™ã€‚"
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
msgstr ""
-msgid "Running"
+msgid "Runners currently online: %{active_runners_count}"
msgstr ""
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr "稼åƒä¸­"
+
msgid "SAML SSO"
msgstr "SAML SSO"
msgid "SAML SSO for %{group_name}"
-msgstr ""
+msgstr "%{group_name} 用㮠SAML SSO"
msgid "SAML Single Sign On"
-msgstr ""
+msgstr "SAML シングル サインオン"
msgid "SAML Single Sign On Settings"
-msgstr ""
+msgstr "SAML シングル サインオンã®è¨­å®š"
+
+msgid "SAST"
+msgstr "SAST"
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
-msgstr ""
+msgstr "SAML トークン㮠SHA1 フィンガープリントã§è¨¼æ˜Žæ›¸ã«ç½²åã—ã¾ã™ã€‚ã“ã‚Œã¯ã‚µãƒ ãƒ—リントã¨å‘¼ã°ã‚Œã‚¢ã‚¤ãƒ‡ãƒ³ãƒ†ã‚£ãƒ†ã‚£ãƒ—ロãƒã‚¤ãƒ€ãƒ¼ã‹ã‚‰å–å¾—ã§ãã¾ã™ã€‚"
msgid "SSH Keys"
msgstr "SSH éµ"
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
msgid "Save"
-msgstr ""
+msgstr "ä¿å­˜"
msgid "Save application"
msgstr ""
@@ -5500,26 +6561,59 @@ msgstr "マージリクエストを検索"
msgid "Search milestones"
msgstr "マイルストーンを検索"
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr "プロジェクトを検索"
msgid "Search users"
msgstr "ユーザーを検索"
-msgid "Seconds before reseting failure information"
+msgid "SearchAutocomplete|All GitLab"
msgstr ""
-msgid "Seconds to wait for a storage access attempt"
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
msgstr ""
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr "失敗情報をリセットã™ã‚‹ã¾ã§ã®ç§’æ•°"
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr "ストレージアクセスをå†è©¦è¡Œã™ã‚‹ã¾ã§ã®ç§’æ•°"
+
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr "セキュリティ"
+
msgid "Security Dashboard"
-msgstr ""
+msgstr "セキュリティダッシュボード"
-msgid "Security report"
-msgstr "セキュリティレãƒãƒ¼ãƒˆ"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
msgstr ""
@@ -5533,14 +6627,17 @@ msgstr "é¸æŠž"
msgid "Select Archive Format"
msgstr "アーカイブã®ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆã‚’é¸æŠž"
-msgid "Select a namespace to fork the project"
+msgid "Select a group to invite"
msgstr ""
+msgid "Select a namespace to fork the project"
+msgstr "プロジェクトをフォークã™ã‚‹åå‰ç©ºé–“ã‚’é¸æŠžã—ã¦ãã ã•ã„"
+
msgid "Select a timezone"
msgstr "タイムゾーンをé¸æŠž"
msgid "Select an existing Kubernetes cluster or create a new one"
-msgstr ""
+msgstr "既存㮠Kubernetes クラスターをé¸æŠžã™ã‚‹ã‹ã€æ–°ã—ã„ã‚‚ã®ã‚’作æˆ"
msgid "Select assignee"
msgstr "担当者をé¸æŠž"
@@ -5566,6 +6663,12 @@ msgstr "ソースブランãƒã‚’é¸æŠž"
msgid "Select target branch"
msgstr "ターゲットブランãƒã‚’é¸æŠž"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5575,6 +6678,9 @@ msgstr ""
msgid "Send email"
msgstr "メールをé€ä¿¡"
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr "9月"
@@ -5585,16 +6691,16 @@ msgid "Server version"
msgstr "サーãƒãƒ¼ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
msgid "Service Desk"
-msgstr ""
+msgstr "サービスデスク"
msgid "Service Templates"
msgstr "サービス テンプレート"
msgid "Service URL"
-msgstr ""
+msgstr "サービス URL"
msgid "Session expiration, projects limit and attachment size."
-msgstr ""
+msgstr "セッションã®æœ‰åŠ¹æœŸé™ã€ãƒ—ロジェクトã®ä¸Šé™ã€æ·»ä»˜ãƒ•ã‚¡ã‚¤ãƒ«ã®ã‚µã‚¤ã‚º"
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "%{protocol} プロコトル経由ã§ãƒ—ルã€ãƒ—ッシュã™ã‚‹ãŸã‚ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ‘スワードを設定。"
@@ -5602,6 +6708,9 @@ msgstr "%{protocol} プロコトル経由ã§ãƒ—ルã€ãƒ—ッシュã™ã‚‹ãŸã‚ã«
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5614,30 +6723,36 @@ msgstr ""
msgid "Set up CI/CD"
msgstr "CI/CD を設定"
-msgid "Set up Koding"
-msgstr "Koding を設定"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "パスワードを設定"
msgid "Settings"
msgstr "設定"
-msgid "Setup a specific Runner automatically"
-msgstr ""
-
msgid "Share"
msgstr "共有"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
-msgstr ""
+msgstr "メンãƒãƒ¼ã¨<strong>%{sso_label}</strong>を共有ã™ã‚‹ã¨ã€è‡ªèº«ã® ID プロãƒã‚¤ãƒ€ãƒ¼ã§ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™"
msgid "Shared Runners"
msgstr "共有 Runner"
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5654,7 +6769,7 @@ msgid "Show command"
msgstr "コマンドを表示"
msgid "Show complete raw log"
-msgstr ""
+msgstr "完全ãªç”Ÿãƒ­ã‚°ã‚’表示ã™ã‚‹"
msgid "Show latest version"
msgstr ""
@@ -5679,25 +6794,25 @@ msgid "Side-by-side"
msgstr ""
msgid "Sidebar|Change weight"
-msgstr ""
+msgstr "ウェイトを変更"
msgid "Sidebar|None"
-msgstr ""
+msgstr "ãªã—"
msgid "Sidebar|Only numeral characters allowed"
msgstr "æ•°å­—ã®ã¿ä½¿ç”¨ã§ãã¾ã™"
msgid "Sidebar|Weight"
-msgstr ""
+msgstr "ウェイト"
msgid "Sign in"
-msgstr ""
+msgstr "サインイン"
msgid "Sign in / Register"
msgstr ""
msgid "Sign in to %{group_name}"
-msgstr ""
+msgstr "%{group_name} ã«ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã—ã¾ã™"
msgid "Sign in with Single Sign-On"
msgstr "シングルサインオンã§ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹"
@@ -5706,17 +6821,23 @@ msgid "Sign out"
msgstr "サインアウト"
msgid "Sign-in restrictions"
-msgstr ""
+msgstr "サインインã®åˆ¶é™"
msgid "Sign-up restrictions"
msgstr "サインアップã®åˆ¶é™"
+msgid "Size"
+msgstr "サイズ"
+
msgid "Size and domain settings for static websites"
msgstr "é™çš„ãªã‚¦ã‚§ãƒ–サイトã®ã‚µã‚¤ã‚ºã¨ãƒ‰ãƒ¡ã‚¤ãƒ³ã®è¨­å®š"
msgid "Slack application"
msgstr "Slack アプリケーション"
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5732,17 +6853,23 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr "ボタンã®åˆ‡ã‚Šæ›¿ãˆä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr ""
+msgstr "%{issuable} を解決中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚ã¨ã§ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
-msgstr ""
+msgstr "グループメンãƒãƒ¼ã®è²¢çŒ®åº¦ã‚’å–得中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "Something went wrong while fetching the projects."
msgstr ""
@@ -5751,16 +6878,16 @@ msgid "Something went wrong while fetching the registry list."
msgstr ""
msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr ""
+msgstr "%{issuable} ã®å†é–‹å‡¦ç†ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚ã¨ã§ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "ã“ã®æ¤œè¨Žã‚’解決ã—ã¦ã„ã‚‹ã¨ãã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "Something went wrong. Please try again."
-msgstr ""
+msgstr "å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚å†è©¦è¡Œã—ã¦ãã ã•ã„。"
msgid "Sorry, no epics matched your search"
-msgstr ""
+msgstr "申ã—訳ã‚ã‚Šã¾ã›ã‚“ã€æ¤œç´¢ã«ä¸€è‡´ã™ã‚‹ã‚¨ãƒ”ックã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "Sort by"
msgstr ""
@@ -5792,6 +6919,9 @@ msgstr "グループã®å¤§ãã„é †"
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr "最新作æˆé †"
@@ -5805,23 +6935,26 @@ msgid "SortOptions|Least popular"
msgstr "人気順"
msgid "SortOptions|Less weight"
-msgstr ""
+msgstr "ウェイトãŒå°ã•ã„é †"
msgid "SortOptions|Milestone"
msgstr "マイルストーン順"
msgid "SortOptions|Milestone due later"
-msgstr ""
+msgstr "マイルストーン期é™ã®é…ã„é †"
msgid "SortOptions|Milestone due soon"
-msgstr ""
+msgstr "マイルストーン期é™ã®è¿‘ã„é †"
msgid "SortOptions|More weight"
-msgstr ""
+msgstr "ウエイトãŒå¤§ãã„é †"
msgid "SortOptions|Most popular"
msgstr "人気順"
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr "åå‰"
@@ -5852,6 +6985,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr "最近ã®ã‚µã‚¤ãƒ³ã‚¤ãƒ³é †"
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr "å¤ã„é †"
@@ -5859,7 +6995,7 @@ msgid "SortOptions|Start soon"
msgstr "æ–°ã—ã„é †"
msgid "SortOptions|Weight"
-msgstr ""
+msgstr "ウエイト"
msgid "Source"
msgstr "ソース"
@@ -5882,11 +7018,14 @@ msgstr "スパムã¨ã‚¢ãƒ³ãƒãƒœãƒƒãƒˆä¿è­·"
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "Runner セットアップã®éš›ã«æ¬¡ã® URL を指定ã—ã¦ãã ã•ã„:"
msgid "Squash commits"
-msgstr ""
+msgstr "コミットを1ã¤ã«ã¾ã¨ã‚ã‚‹"
msgid "Stage"
msgstr "ステージ"
@@ -5907,7 +7046,7 @@ msgid "Staged %{type}"
msgstr ""
msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
+msgstr "優先ラベルã«ã™ã‚‹ã«ã¯ã€ãƒ©ãƒ™ãƒ«ã«ã‚¹ã‚¿ãƒ¼ã‚’付ã‘ã¾ã™ã€‚ドラッグã—ã¦ã€å„ªå…ˆé †ä½ã‚’並ã¹æ›¿ãˆã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "StarProject|Star"
msgstr "スターを付ã‘ã‚‹"
@@ -5924,6 +7063,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr "ã“ã®å¤‰æ›´ã§ %{new_merge_request} を作æˆã™ã‚‹"
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr "Runner ã‚’èµ·å‹•!"
@@ -5957,6 +7099,9 @@ msgstr ""
msgid "Subgroups"
msgstr "サブグループ"
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -5964,32 +7109,35 @@ msgid "Submit search"
msgstr ""
msgid "Subscribe"
-msgstr ""
+msgstr "購読ã™ã‚‹"
msgid "Subscribe at group level"
-msgstr ""
+msgstr "グループレベルã§è³¼èª­ã™ã‚‹"
msgid "Subscribe at project level"
-msgstr ""
+msgstr "プロジェクトレベルã§è³¼èª­ã™ã‚‹"
msgid "Switch branch/tag"
msgstr "ブランãƒãƒ»ã‚¿ã‚°åˆ‡ã‚Šæ›¿ãˆ"
msgid "Sync information"
-msgstr ""
+msgstr "åŒæœŸæƒ…å ±"
msgid "System Hooks"
msgstr "システムフック"
msgid "System Info"
-msgstr ""
+msgstr "システム情報"
msgid "System header and footer:"
-msgstr ""
+msgstr "システムヘッダーã¨ãƒ•ãƒƒã‚¿ãƒ¼ï¼š"
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] "タグ(%{tag_count})"
@@ -5997,6 +7145,9 @@ msgstr[0] "タグ(%{tag_count})"
msgid "Tags"
msgstr "ã‚¿ã‚°"
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr "ã‚¿ã‚°:"
@@ -6004,10 +7155,10 @@ msgid "TagsPage|Browse commits"
msgstr ""
msgid "TagsPage|Browse files"
-msgstr ""
+msgstr "ファイルを表示"
msgid "TagsPage|Can't find HEAD commit for this tag"
-msgstr ""
+msgstr "ã“ã®ã‚¿ã‚°ã® HEAD コミットãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
msgid "TagsPage|Cancel"
msgstr "キャンセル"
@@ -6055,7 +7206,7 @@ msgid "TagsPage|Tags"
msgstr "タグ一覧"
msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
-msgstr ""
+msgstr "ã‚¿ã‚°ã¯ã€å±¥æ­´ã®ç‰¹å®šã®ä½ç½®ã‚’é‡è¦ãªã‚‚ã®ã¨ã—ã¦ãƒžãƒ¼ã‚¯ã™ã‚‹æ©Ÿèƒ½ã‚’æä¾›ã—ã¾ã™"
msgid "TagsPage|This tag has no release notes."
msgstr "ã“ã®ã‚¿ã‚°ã«ã¯ãƒªãƒªãƒ¼ã‚¹ãƒŽãƒ¼ãƒˆãŒã‚ã‚Šã¾ã›ã‚“。"
@@ -6067,7 +7218,7 @@ msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
-msgstr ""
+msgstr "ä¿è­·"
msgid "Target Branch"
msgstr "ターゲットブランãƒ"
@@ -6078,11 +7229,17 @@ msgstr "ターゲットブランãƒ"
msgid "Team"
msgstr "ãƒãƒ¼ãƒ "
+msgid "Template"
+msgstr "テンプレート"
+
+msgid "Templates"
+msgstr "テンプレート"
+
msgid "Terms of Service Agreement and Privacy Policy"
-msgstr ""
+msgstr "利用è¦ç´„ã¨ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼"
msgid "Terms of Service and Privacy Policy"
-msgstr ""
+msgstr "利用è¦ç´„ã¨ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼"
msgid "Test coverage parsing"
msgstr ""
@@ -6091,6 +7248,9 @@ msgid "Thanks! Don't show me this again"
msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr "GitLab ã®é«˜åº¦ãªã‚°ãƒ­ãƒ¼ãƒãƒ«æ¤œç´¢ã¯ã€æ™‚間を節約ã§ãる強力ãªæ¤œç´¢ã‚µãƒ¼ãƒ“スã§ã™ã€‚é‡è¤‡ã—ãŸã‚³ãƒ¼ãƒ‰ã®ä½œæˆã‚’ã—ã¦æ™‚間を無駄ã«ã™ã‚‹ã“ã¨ãªãã€è‡ªèº«ã®ãƒ—ロジェクトã®åŠ©ã‘ã¨ãªã‚‹å¤–ã®ãƒãƒ¼ãƒ ã®ã‚³ãƒ¼ãƒ‰ã‚’検索ã™ã‚‹ã“ã¨ãŒã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚"
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
@@ -6100,6 +7260,9 @@ msgid "The Issue Tracker is the place to add things that need to be improved or
msgstr "課題トラッカーã¯ã€ãƒ—ロジェクトを改善ã—ãŸã‚Šè§£æ±ºã—ãŸã‚Šã™ã‚‹ãŸã‚ã«å¿…è¦ãªæƒ…報を追加ã™ã‚‹å ´æ‰€ã§ã™ã€‚ã“ã®ãƒ—ロジェクトã«èª²é¡Œã‚’作æˆã™ã‚‹ãŸã‚ã«ã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ç™»éŒ²ã¾ãŸã¯ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã—ã¦ãã ã•ã„。"
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr "外部èªè¨¼ã‚µãƒ¼ãƒ“スã¨ã®é€šä¿¡ã«ç›¸äº’ TLS ãŒå¿…è¦ãªå ´åˆã«ä½¿ç”¨ã™ã‚‹ X509 証明書。空白ã®ã¾ã¾ã«ã™ã‚‹ã¨ã€HTTPS 経由ã§ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã¨ãã«ã‚µãƒ¼ãƒè¨¼æ˜Žæ›¸ã®æ¤œè¨¼ãŒè¡Œã‚ã‚Œã¾ã™ã€‚"
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -6111,6 +7274,9 @@ msgstr "ã“ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã§è¨ˆæ¸¬ãƒ‡ãƒ¼ã‚¿ã«è¿½åŠ ã•ã‚ŒãŸã‚¤ãƒ™ãƒ³ãƒˆãƒªã‚¹
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "フォークã®ãƒªãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚"
@@ -6121,7 +7287,7 @@ msgid "The issue stage shows the time it takes from creating an issue to assigni
msgstr "課題ステージã§ã¯ã€èª²é¡ŒãŒç™»éŒ²ã•ã‚Œã¦ã‹ã‚‰ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã‚‹ã‹ã€èª²é¡Œãƒœãƒ¼ãƒ‰ã®ãƒªã‚¹ãƒˆã«è¿½åŠ ã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®ãƒªã‚¹ãƒˆã«è¡¨ç¤ºã™ã‚‹ã«ã¯èª²é¡Œã‚’最åˆã«ä½œæˆã—ã¦ãã ã•ã„。"
msgid "The maximum file size allowed is 200KB."
-msgstr ""
+msgstr "許å¯ã•ã‚Œã‚‹æœ€å¤§ãƒ•ã‚¡ã‚¤ãƒ«ã‚µã‚¤ã‚ºã¯ 200KB ã§ã™ã€‚"
msgid "The number of attempts GitLab will make to access a storage."
msgstr "GitLab ã«ã‚ˆã‚‹ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹è©¦è¡Œå›žæ•°"
@@ -6130,7 +7296,7 @@ msgid "The number of failures after which GitLab will completely prevent access
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
-msgstr ""
+msgstr "秘密éµã®å¾©å·ã«å¿…è¦ã¨ãªã‚‹ãƒ‘スフレーズ。ã“ã‚Œã¯ã‚ªãƒ—ションã§ã€å€¤ã¯æš—å·åŒ–ã—ã¦ä¿å­˜ã•ã‚Œã¾ã™ã€‚"
msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
msgstr ""
@@ -6138,11 +7304,14 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "開発ライフサイクルã®æ®µéšŽ"
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "計画ステージã§ã¯ã€èª²é¡Œã‚¹ãƒ†ãƒ¼ã‚¸ã«ç™»éŒ²ã•ã‚Œã¦ã‹ã‚‰ãƒ—ッシュã•ã‚ŒãŸæœ€åˆã®ã‚³ãƒŸãƒƒãƒˆæ™‚刻ã¾ã§ã®æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚最åˆã®ã‚³ãƒŸãƒƒãƒˆãŒãƒ—ッシュã•ã‚Œã¨ãã«è‡ªå‹•çš„ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚"
msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
-msgstr ""
+msgstr "クライアント証明書ãŒæä¾›ã•ã‚Œã‚‹ã¨ãã«ä½¿ç”¨ã™ã‚‹ç§˜å¯†éµã€‚ã“ã®å€¤ã¯æš—å·åŒ–ã—ã¦ä¿å­˜ã•ã‚Œã¾ã™ã€‚"
msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
msgstr "プロダクションステージã§ã¯ã€èª²é¡ŒãŒä½œæˆã•ã‚Œã¦ã‹ã‚‰ãƒ—ロダクションã¸ãƒ‡ãƒ—ロイã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚アイディアã®æ™‚点ã‹ã‚‰ãƒ—ロダクションã¾ã§ã®å…¨ã‚¹ãƒ†ãƒ¼ã‚¸ãŒå®Œäº†ã—ãŸã¨ãã«è‡ªå‹•çš„ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚"
@@ -6165,6 +7334,9 @@ msgstr "ã“ã®ãƒ—ロジェクトã«ãƒªãƒã‚¸ãƒˆãƒªã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "レビューステージã¨ã¯ã€ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’作æˆã—ã¦ã‹ã‚‰ãƒžãƒ¼ã‚¸ã™ã‚‹ã¾ã§ã®æ™‚é–“ã§ã™ã€‚ã“ã®ãƒ‡ãƒ¼ã‚¿ã¯æœ€åˆã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒãƒžãƒ¼ã‚¸ã•ã‚ŒãŸã¨ãã«è‡ªå‹•çš„ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚"
@@ -6177,14 +7349,17 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "ステージングステージã§ã¯ã€ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒãƒžãƒ¼ã‚¸ã•ã‚Œã¦ã‹ã‚‰ã‚³ãƒ¼ãƒ‰ãŒãƒ—ロダクション環境ã«ãƒ‡ãƒ—ロイã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®ãƒ‡ãƒ¼ã‚¿ã¯æœ€åˆã«ãƒ—ロダクションã«ãƒ‡ãƒ—ロイã—ãŸã¨ãã«è‡ªå‹•çš„ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚"
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "テスティングステージã§ã¯ã€GitLab CI ãŒé–¢é€£ã™ã‚‹ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®å„パイプラインを実行ã™ã‚‹æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®ãƒ‡ãƒ¼ã‚¿ã¯æœ€åˆã®ãƒ‘イプラインãŒå®Œäº†ã—ãŸã¨ãã«è‡ªå‹•çš„ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚"
msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
-msgstr ""
+msgstr "GitLab ã¯éšœå®³æƒ…報を秒å˜ä½ã§ä¿å­˜ã—ã¦ã„ã¾ã™ã€‚ã“ã®é–“ã«éšœå®³ãŒç™ºç”Ÿã—ãªã‹ã£ãŸå ´åˆã€ãƒžã‚¦ãƒ³ãƒˆã«é–¢ã™ã‚‹æƒ…å ±ã¯ãƒªã‚»ãƒƒãƒˆã•ã‚Œã¾ã™ã€‚"
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
-msgstr ""
+msgstr "GitLab ãŒã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã«ã‚¢ã‚¯ã‚»ã‚¹ã—よã†ã¨ã™ã‚‹æ™‚間(秒)。ã“ã®æ™‚é–“ãŒçµŒéŽã™ã‚‹ã¨ã€ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã™ã€‚"
msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
@@ -6192,6 +7367,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "ã“ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã«åŽé›†ã•ã‚ŒãŸãƒ‡ãƒ¼ã‚¿æ¯Žã®æ™‚é–“"
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6201,6 +7382,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "得られãŸä¸€é€£ã®ãƒ‡ãƒ¼ã‚¿ã‚’å°ã•ã„é †ã«ä¸¦ã¹ãŸã¨ãã«ä¸­å¤®ã«ä½ç½®ã™ã‚‹å€¤ã€‚例ãˆã°ã€3, 5, 9 ã®ä¸­å¤®å€¤ã¯ 5。3, 5, 7, 8 ã®ä¸­å¤®å€¤ã¯ (5+7)/2 = 6。"
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr "表示ã™ã‚‹èª²é¡ŒãŒã‚ã‚Šã¾ã›ã‚“"
@@ -6210,9 +7394,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr "表示ã™ã‚‹ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã‚Šã¾ã›ã‚“"
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "Git ストレージã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã«å•é¡ŒãŒã‚ã‚Šã¾ã™: "
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr "ユーザーã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティカレンダーã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -6232,13 +7431,13 @@ msgid "There was an error when unsubscribing from this label."
msgstr ""
msgid "They can be managed using the %{link}."
-msgstr ""
+msgstr "%{link}を使用ã—ã¦ãれらを管ç†ã§ãã¾ã™ã€‚"
msgid "Third party offers"
msgstr ""
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
-msgstr ""
+msgstr "ã“ã® GitLab インスタンスã¯ã¾ã å…±æœ‰ Runner ã‚’æä¾›ã—ã¦ã„ã¾ã›ã‚“。インスタンス管ç†è€…ã¯ç®¡ç†è€…エリアã§å…±æœ‰ Runner を登録ã§ãã¾ã™ã€‚"
msgid "This application was created by %{link_to_owner}."
msgstr ""
@@ -6249,6 +7448,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6259,28 +7470,28 @@ msgid "This group"
msgstr ""
msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
-msgstr ""
+msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã§ã¯ã€ã‚ãªãŸã®%{group_name}シングルサインオンアカウントã§ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã§ãã¾ã™ã€‚シングルサインオン利用時ã¯ã€å¤–部ã®ã‚µã‚¤ãƒ³ã‚¤ãƒ³ãƒšãƒ¼ã‚¸ã«ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã•ã‚Œã¾ã™ã€‚"
msgid "This group does not provide any group Runners yet."
-msgstr ""
+msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¯ã¾ã ã‚°ãƒ«ãƒ¼ãƒ— Runner ã‚’æä¾›ã—ã¦ã„ã¾ã›ã‚“。"
msgid "This is a confidential issue."
-msgstr ""
+msgstr "ã“ã‚Œã¯éžå…¬é–‹ã®èª²é¡Œã§ã™ã€‚"
msgid "This is the author's first Merge Request to this project."
msgstr ""
msgid "This issue is confidential"
-msgstr ""
+msgstr "ã“ã®èª²é¡Œã¯éžå…¬é–‹è¨­å®šã§ã™"
msgid "This issue is confidential and locked."
-msgstr ""
+msgstr "ã“ã®èª²é¡Œã¯éžå…¬é–‹ã§ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™"
msgid "This issue is locked."
msgstr "ã“ã®èª²é¡Œã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
-msgstr ""
+msgstr "ã“ã®ã‚¸ãƒ§ãƒ–ã¯ãƒ—ロセスをトリガーã«ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ä¾å­˜ã—ã¾ã™ã€‚多ãã®å ´åˆã€æœ¬ç•ªç’°å¢ƒã¸ã‚³ãƒ¼ãƒ‰ã‚’デプロイã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã•ã‚Œã¾ã™ã€‚"
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
@@ -6300,7 +7511,28 @@ msgstr "ã“ã®ã‚¸ãƒ§ãƒ–ã¯ã¾ã å®Ÿè¡Œã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "This job has not started yet"
msgstr "ジョブã¯ã¾ã é–‹å§‹ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr "ã“ã®ã‚¸ãƒ§ãƒ–ã¯ä¿ç•™ä¸­ã§ Runner ãŒå‹•ä½œã™ã‚‹ã®ã‚’å¾…ã£ã¦ã„ã¾ã™ã€‚"
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
msgstr ""
msgid "This job requires a manual action"
@@ -6310,10 +7542,13 @@ msgid "This means you can not push code until you create an empty repository or
msgstr ""
msgid "This merge request is locked."
+msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
msgid "This option is disabled while you still have unstaged changes"
-msgstr ""
+msgstr "ã¾ã ã‚¹ãƒ†ãƒ¼ã‚¸ãƒ³ã‚°ã•ã‚Œã¦ã„ãªã„変更ãŒã‚ã‚‹ã¨ã€ã“ã®ã‚ªãƒ—ションã¯ç„¡åŠ¹ã«ãªã‚Šã¾ã™"
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr "複数ã®ãƒ—ロジェクト間ã§èª­ã¿è¾¼ã¿ãŒè¨±å¯ã•ã‚Œã¦ã„ãªã„ãŸã‚ã€ã“ã®ãƒšãƒ¼ã‚¸ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。"
@@ -6325,22 +7560,37 @@ msgid "This project"
msgstr "プロジェクト"
msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr "ã“ã®ãƒ—ロジェクトã¯ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‰€å±žã—ã¦ã„ãªã„ãŸã‚ã€ã‚°ãƒ«ãƒ¼ãƒ— Runner を利用ã§ãã¾ã›ã‚“。"
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
msgstr ""
msgid "This repository"
msgstr "リãƒã‚¸ãƒˆãƒª"
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr ""
+msgstr "ã“ã“ã«ãƒªã‚¹ãƒˆã‚¢ãƒƒãƒ—ã•ã‚Œã¦ã„ã‚‹ã“れらã®ãƒ¡ãƒ¼ãƒ«ã¯è‡ªå‹•çš„ã«èª²é¡Œã«ãªã‚Šã¾ã™ã€‚(メールã®ã‚„ã‚Šå–ã‚Šã¯èª²é¡Œã®ã‚³ãƒ¡ãƒ³ãƒˆã«ãªã‚Šã¾ã™ï¼‰"
msgid "Time before an issue gets scheduled"
msgstr "課題ãŒè¨ˆç”»ã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“"
@@ -6352,7 +7602,7 @@ msgid "Time between merge request creation and merge/close"
msgstr "マージリクエストãŒä½œæˆã•ã‚Œã¦ã‹ã‚‰ãƒžãƒ¼ã‚¸ã¾ãŸã¯ã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“"
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
-msgstr ""
+msgstr "外部サービスã‹ã‚‰ã®å¿œç­”時間(秒å˜ä½ï¼‰ã‚’設定ã—ã¾ã™ã€‚設定時間内ã«å¿œç­”ãŒç„¡ã„å ´åˆã€ã‚¢ã‚¯ã‚»ã‚¹ãŒæ‹’å¦ã•ã‚Œã¾ã™ã€‚"
msgid "Time remaining"
msgstr "残り時間"
@@ -6361,7 +7611,7 @@ msgid "Time spent"
msgstr "経éŽæ™‚é–“"
msgid "Time tracking"
-msgstr ""
+msgstr "タイムトラッキング"
msgid "Time until first merge request"
msgstr "最åˆã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¾ã§ã®æ™‚é–“"
@@ -6382,7 +7632,7 @@ msgid "Timeago|%s days remaining"
msgstr "残り %s日間"
msgid "Timeago|%s hours ago"
-msgstr ""
+msgstr "%s 時間å‰"
msgid "Timeago|%s hours remaining"
msgstr "残り %s時間"
@@ -6502,7 +7752,7 @@ msgid "Timeago|right now"
msgstr "今"
msgid "Timeout"
-msgstr ""
+msgstr "タイムアウト"
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -6531,9 +7781,15 @@ msgid "To connect GitHub repositories, you can use a %{personal_access_token_lin
msgstr ""
msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
-msgstr ""
+msgstr "GitHub リãƒã‚¸ãƒˆãƒªã«æŽ¥ç¶šã™ã‚‹ã«ã¯ã€GitLab ãŒã‚ãªãŸã® GitHub リãƒã‚¸ãƒˆãƒªä¸€è¦§ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ã‚’許å¯ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ï¼š"
msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr "SVN リãƒã‚¸ãƒˆãƒªã«æŽ¥ç¶šã™ã‚‹ã¨ãã¯ã€%{svn_link} ã‚’ã”確èªãã ã•ã„。"
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
msgstr ""
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
@@ -6542,14 +7798,20 @@ msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
-msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
msgstr ""
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr "GitHub リãƒã‚¸ãƒˆãƒªã‚’インãƒãƒ¼ãƒˆã™ã‚‹ãŸã‚ã« %{personal_access_token_link} を使用ã§ãã¾ã™ã€‚個人用アクセストークンを作æˆã™ã‚‹éš›ã¯ã€ <code>repo</code> スコープをé¸æŠžã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ã“ã‚Œã«ã‚ˆã‚Šã‚¤ãƒ³ãƒãƒ¼ãƒˆã§ãる公開・éžå…¬é–‹ãƒªãƒã‚¸ãƒˆãƒªã®ä¸€è¦§ã‚’表示ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+
msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
-msgstr ""
+msgstr "SVN リãƒã‚¸ãƒˆãƒªã‚’インãƒãƒ¼ãƒˆã™ã‚‹ã«ã¯ã€%{svn_link} ã‚’ã”確èªãã ã•ã„。"
msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
msgstr ""
@@ -6567,9 +7829,9 @@ msgid "To this GitLab instance"
msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
-msgstr ""
+msgstr "GitLab CI ã®è¨­å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’検証ã™ã‚‹ã«ã¯ã€ãƒ—ロジェクト内ã®ã€ŒCI / CD→Pipelinesã€ã«è¡Œãã€ã€ŒCI Lintã€ãƒœã‚¿ãƒ³ã‚’クリックã—ã¦ãã ã•ã„。"
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6582,6 +7844,9 @@ msgid "Todos"
msgstr ""
msgid "Toggle Sidebar"
+msgstr "サイドãƒãƒ¼ã‚’切り替ãˆ"
+
+msgid "Toggle commit description"
msgstr ""
msgid "Toggle discussion"
@@ -6591,7 +7856,7 @@ msgid "Toggle navigation"
msgstr ""
msgid "Toggle sidebar"
-msgstr ""
+msgstr "サイドãƒãƒ¼ã‚’切り替ãˆ"
msgid "ToggleButton|Toggle Status: OFF"
msgstr ""
@@ -6599,6 +7864,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr "トークン"
+
msgid "Too many changes to show."
msgstr ""
@@ -6626,6 +7894,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr "トリガー"
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6639,6 +7916,9 @@ msgid "Turn on Service Desk"
msgstr ""
msgid "Twitter"
+msgstr "Twitter"
+
+msgid "Type"
msgstr ""
msgid "Unable to load the diff. %{button_try_again}"
@@ -6647,18 +7927,30 @@ msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr "アンロック"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr "検討を未解決ã«ã™ã‚‹"
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr "ã™ã¹ã¦ã®å¤‰æ›´ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã‚’å–り消ã™"
@@ -6678,13 +7970,13 @@ msgid "Unstar"
msgstr "スターを外ã™"
msgid "Unsubscribe"
-msgstr ""
+msgstr "購読を解除"
msgid "Unsubscribe at group level"
-msgstr ""
+msgstr "グループレベルã§è³¼èª­ã‚’解除"
msgid "Unsubscribe at project level"
-msgstr ""
+msgstr "プロジェクトレベルã§è³¼èª­ã‚’解除"
msgid "Unverified"
msgstr ""
@@ -6693,11 +7985,17 @@ msgid "Up to date"
msgstr "最新"
msgid "Update"
+msgstr "アップデート"
+
+msgid "Update now"
msgstr ""
msgid "Update your group name, description, avatar, and other general settings."
msgstr "グループåã€èª¬æ˜Žã€ã‚¢ãƒã‚¿ãƒ¼ã€ãã®ä»–ã®ä¸€èˆ¬è¨­å®šã‚’æ›´æ–°ã—ã¾ã™ã€‚"
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6705,13 +8003,13 @@ msgid "Upgrade your plan to activate Contribution Analytics."
msgstr ""
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr ""
+msgstr "グループ ウェブフックを有効ã«ã™ã‚‹ãŸã‚ã«ã€ãƒ—ランをアップグレード"
msgid "Upgrade your plan to activate Issue weight."
-msgstr ""
+msgstr "課題ã®ã‚¦ã‚§ã‚¤ãƒˆã‚’有効ã«ã™ã‚‹ãŸã‚ã«ã€ãƒ—ランをアップグレード"
msgid "Upgrade your plan to improve Issue boards."
-msgstr ""
+msgstr "課題ボードã®æ©Ÿèƒ½ã‚’強化ã™ã‚‹ãŸã‚ã«ã€ãƒ—ランをアップグレード"
msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
msgstr ""
@@ -6722,15 +8020,15 @@ msgstr "æ–°è¦ãƒ•ã‚¡ã‚¤ãƒ«ã‚’アップロード"
msgid "Upload file"
msgstr "ファイルをアップロード"
-msgid "Upload new avatar"
-msgstr "æ–°ã—ã„ã‚¢ãƒã‚¿ãƒ¼ã‚’アップロード"
-
msgid "UploadLink|click to upload"
msgstr "クリックã—ã¦ã‚¢ãƒƒãƒ—ロード"
msgid "Upvotes"
msgstr "ã„ã„ã­"
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr "使用状æ³ã®çµ±è¨ˆ"
@@ -6738,14 +8036,17 @@ msgid "Use <code>%{native_redirect_uri}</code> for local tests"
msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr ""
+msgstr "サービスデスクを使用ã—ã¦ã€GitLab 内ã®ãƒ¡ãƒ¼ãƒ«ã§ãƒ¦ãƒ¼ã‚¶ã¨æŽ¥ç¶šï¼ˆä¾‹ï¼šé¡§å®¢ã‚µãƒãƒ¼ãƒˆï¼‰"
msgid "Use group milestones to manage issues from multiple projects in the same milestone."
-msgstr ""
+msgstr "グループマイルストーンを使用ã—ã¦ã€åŒã˜ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³å†…ã®è¤‡æ•°ã®ãƒ—ロジェクトã®èª²é¡Œã‚’管ç†ã—ã¾ã™ã€‚"
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "セットアップã®éš›ã«æ¬¡ã®ç™»éŒ²ãƒˆãƒ¼ã‚¯ãƒ³ã‚’使用ã—ã¦ãã ã•ã„:"
@@ -6753,6 +8054,9 @@ msgid "Use your global notification setting"
msgstr "全体通知設定を利用"
msgid "Used by members to sign in to your group in GitLab"
+msgstr "メンãƒãƒ¼ãŒ GitLab ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã•ã‚Œã¾ã™"
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
msgstr ""
msgid "User Settings"
@@ -6771,7 +8075,7 @@ msgid "Variables"
msgstr "変数"
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
-msgstr ""
+msgstr "変数㯠Runner を介ã—ã¦ç’°å¢ƒã«é©ç”¨ã•ã‚Œã¾ã™ã€‚ã¾ãŸã€ä¿è­·ã•ã‚ŒãŸãƒ–ランãƒã‚„ã‚¿ã‚°ã ã‘ã«å¤‰æ•°ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’制é™ã—ã€å¤‰æ•°ã‚’ä¿è­·ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã“ã‚Œã«ã‚ˆã‚Šã€ãƒ‘スワードã€ç§˜å¯†éµãªã©ã‚’æ ¼ç´ã™ã‚‹ãŸã‚ã«å¤‰æ•°ã‚’使用ã§ãã¾ã™ã€‚"
msgid "Various container registry settings."
msgstr "å„種コンテナレジストリã®è¨­å®šã€‚"
@@ -6788,6 +8092,9 @@ msgstr ""
msgid "Verified"
msgstr "検証済ã¿"
+msgid "Version"
+msgstr "ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
+
msgid "View epics list"
msgstr ""
@@ -6804,13 +8111,13 @@ msgid "View it on GitLab"
msgstr ""
msgid "View jobs"
-msgstr ""
+msgstr "ジョブを表示"
msgid "View labels"
msgstr "ラベルを表示"
msgid "View log"
-msgstr ""
+msgstr "ログã®è¡¨ç¤º"
msgid "View open merge request"
msgstr "オープンãªãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’表示"
@@ -6852,7 +8159,7 @@ msgid "We don't have enough data to show this stage."
msgstr "データä¸è¶³ã®ãŸã‚ã€ã“ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã®è¡¨ç¤ºã¯ã§ãã¾ã›ã‚“。"
msgid "We want to be sure it is you, please confirm you are not a robot."
-msgstr ""
+msgstr "本人確èªã®ãŸã‚ã€ã‚ãªãŸãŒãƒ­ãƒœãƒƒãƒˆã§ãªã„ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
msgid "Web IDE"
msgstr "Web IDE"
@@ -6867,16 +8174,16 @@ msgid "Weeks"
msgstr ""
msgid "Weight"
-msgstr ""
+msgstr "ウェイト"
msgid "Weight %{weight}"
-msgstr ""
+msgstr "ウェイト %{weight}"
msgid "When a runner is locked, it cannot be assigned to other projects"
msgstr "Runner ãŒãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã‚‹å ´åˆã€ä»–ã®ãƒ—ロジェクトã«å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“"
msgid "When enabled, users cannot use GitLab until the terms have been accepted."
-msgstr ""
+msgstr "有効ã«ã—ãŸå ´åˆã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒè¦ç´„ã«åŒæ„ã•ã‚Œã‚‹ã¾ã§ GitLab を使用ã§ãã¾ã›ã‚“。"
msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
@@ -6897,7 +8204,7 @@ msgid "WikiClone|It is recommended to install %{markdown} so that GFM features r
msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
-msgstr ""
+msgstr "Gollum ã‚’èµ·å‹•ã—ã¦ãƒ­ãƒ¼ã‚«ãƒ«ã§ç·¨é›†ã™ã‚‹"
msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
msgstr ""
@@ -6906,31 +8213,31 @@ msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
-msgstr ""
+msgstr "Wiki ã®æ”¹å–„ã‚’æ案ã™ã‚‹"
msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
-msgstr ""
+msgstr "Wiki ページを追加ã™ã‚‹ã«ã¯ã€ãƒ—ロジェクトメンãƒãƒ¼ã§ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。ã“ã®ãƒ—ロジェクト㮠Wiki を改善ã™ã‚‹æ–¹æ³•ã®æ案ãŒã‚ã‚‹å ´åˆã¯ã€ %{issues_link} ã§å•é¡Œã‚’é–‹ãã“ã¨ã‚’検討ã—ã¦ãã ã•ã„。"
msgid "WikiEmptyIssueMessage|issue tracker"
msgstr ""
msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
-msgstr ""
+msgstr "Wiki ã¯ãƒ—ロジェクトã«é–¢ã™ã‚‹ã™ã¹ã¦ã®è©³ç´°ã‚’ä¿å­˜ã™ã‚‹å ´æ‰€ã§ã™ã€‚ã“ã‚Œã«ã¯ã€ãƒ—ロジェクトを作æˆã—ãŸç†ç”±ã€ãƒ—ロジェクトã®åŽŸå‰‡ã€ãƒ—ロジェクトã®ä½¿ç”¨æ–¹æ³•ãªã©ãŒå«ã¾ã‚Œã¾ã™ã€‚"
msgid "WikiEmpty|Create your first page"
-msgstr ""
+msgstr "最åˆã®ãƒšãƒ¼ã‚¸ã‚’作æˆ"
msgid "WikiEmpty|Suggest wiki improvement"
-msgstr ""
+msgstr "Wiki ã®æ”¹å–„ã‚’æ案ã™ã‚‹"
msgid "WikiEmpty|The wiki lets you write documentation for your project"
-msgstr ""
+msgstr "Wiki ã§ã¯ã€ãƒ—ロジェクトã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’書ãã“ã¨ãŒã§ãã¾ã™"
msgid "WikiEmpty|This project has no wiki pages"
-msgstr ""
+msgstr "ã“ã®ãƒ—ロジェクト㫠Wiki ページã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "WikiEmpty|You must be a project member in order to add wiki pages."
-msgstr ""
+msgstr "Wiki ページを追加ã™ã‚‹ã«ã¯ã€ãƒ—ロジェクトメンãƒãƒ¼ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã®å¤ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒè¡¨ç¤ºã•ã‚Œã¦ã„ã¾ã™ã€‚"
@@ -6972,7 +8279,7 @@ msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
msgstr "%{pageTitle} ページを削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
-msgstr ""
+msgstr "誰ã‹ãŒåŒã˜ã‚¿ã‚¤ãƒŸãƒ³ã‚°ã§ãƒšãƒ¼ã‚¸ã‚’編集ã—ã¾ã—ãŸã€‚%{page_link} を確èªã—ã¦ã€å¤‰æ›´ç‚¹ãŒæ„図ã›ãšå‰Šé™¤ã•ã‚Œãªã„よã†ã«æ°—ã‚’ã¤ã‘ã¦ãã ã•ã„。"
msgid "WikiPageConflictMessage|the page"
msgstr "ページ"
@@ -6984,10 +8291,10 @@ msgid "WikiPageEdit|Update %{page_title}"
msgstr "%{page_title} ã‚’æ›´æ–°"
msgid "WikiPage|Page slug"
-msgstr ""
+msgstr "æžè‘‰ãƒšãƒ¼ã‚¸"
msgid "WikiPage|Write your content or drag files here…"
-msgstr ""
+msgstr "ã“ã“ã«ãƒ†ã‚­ã‚¹ãƒˆã‚’記入ã™ã‚‹ã‹ã€ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ドラッグã—ã¦ãã ã•ã„。"
msgid "Wiki|Create Page"
msgstr "ページを作æˆ"
@@ -6999,7 +8306,7 @@ msgid "Wiki|Edit Page"
msgstr "ページを編集"
msgid "Wiki|More Pages"
-msgstr ""
+msgstr "ãã®ä»–ã®ãƒšãƒ¼ã‚¸"
msgid "Wiki|New page"
msgstr "æ–°ã—ã„ページ"
@@ -7038,13 +8345,13 @@ msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored!
msgstr "%{group_name} グループを削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚ 削除ã•ã‚ŒãŸã‚°ãƒ«ãƒ¼ãƒ—ã¯çµ¶å¯¾ã«å…ƒã«æˆ»ã›ã¾ã›ã‚“ï¼æœ¬å½“ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
-msgstr ""
+msgstr "%{project_full_name} プロジェクトを削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚削除ã•ã‚ŒãŸãƒ—ロジェクトã¯çµ¶å¯¾ã«å…ƒã«ã¯æˆ»ã›ã¾ã›ã‚“ï¼æœ¬å½“ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
msgstr "å…ƒã®ãƒ—ロジェクト (%{forked_from_project}) ã¨ã®ãƒªãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
-msgstr ""
+msgstr "%{project_full_name} を別ã®ã‚ªãƒ¼ãƒŠãƒ¼ã«å§”è­²ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "You are on a read-only GitLab instance."
msgstr ""
@@ -7059,16 +8366,16 @@ msgid "You can also create a project from the command line."
msgstr "コマンドラインã‹ã‚‰ãƒ—ロジェクトを作æˆã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
msgid "You can also star a label to make it a priority label."
-msgstr ""
+msgstr "ラベルã«ã‚¹ã‚¿ãƒ¼ã‚’付ã‘ã¦å„ªå…ˆãƒ©ãƒ™ãƒ«ã«ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
-msgstr ""
+msgstr "%{linkStart}Lint%{linkEnd}㧠.gitlab-ci.yml をテストã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™"
msgid "You can easily contribute to them by requesting to join these groups."
msgstr ""
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
-msgstr ""
+msgstr "Kubernetes クラスター㫠Runner ã‚’ç°¡å˜ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã§ãã¾ã™ã€‚%{link_to_help_page}"
msgid "You can move around the graph by using the arrow keys."
msgstr ""
@@ -7079,18 +8386,21 @@ msgstr "ファイルを追加ã™ã‚‹ã«ã¯ã€ã©ã“ã‹ã®ãƒ–ランãƒã«ã„ãªã‘
msgid "You can only edit files when you are on a branch"
msgstr "ファイルを編集ã™ã‚‹ã«ã¯ã€ã©ã“ã‹ã®ãƒ–ランãƒã«ã„ãªã‘ã‚Œã°ã„ã‘ã¾ã›ã‚“"
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
-msgid "You do not have any assigned merge requests"
-msgstr "割り当ã¦ã‚‰ã‚Œã¦ã„るマージリクエストã¯ã‚ã‚Šã¾ã›ã‚“"
-
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
@@ -7101,25 +8411,19 @@ msgid "You don't have any authorized applications"
msgstr ""
msgid "You have no permissions"
-msgstr ""
-
-msgid "You have not created any merge requests"
-msgstr ""
+msgstr "権é™ãŒã‚ã‚Šã¾ã›ã‚“"
msgid "You have reached your project limit"
msgstr "プロジェクト数ã®ä¸Šé™ã«é”ã—ã¦ã„ã¾ã™"
msgid "You must accept our Terms of Service and privacy policy in order to register an account"
-msgstr ""
+msgstr "アカウントを登録ã™ã‚‹ã«ã¯ã€åˆ©ç”¨è¦ç´„ã¨ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼ã«åŒæ„ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™"
msgid "You must have maintainer access to force delete a lock"
-msgstr ""
-
-msgid "You must sign in to star a project"
-msgstr "プロジェクトã«ã‚¹ã‚¿ãƒ¼ã‚’ã¤ã‘ãŸã„å ´åˆã¯ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãã ã•ã„"
+msgstr "ロックを強制的ã«å‰Šé™¤ã™ã‚‹ã«ã¯ã€Maintainer ã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©ãŒå¿…è¦ã§ã™"
msgid "You need a different license to enable FileLocks feature"
-msgstr ""
+msgstr "ファイルロック機能を有効ã«ã™ã‚‹ã«ã¯åˆ¥ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ãŒå¿…è¦ã§ã™"
msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
msgstr ""
@@ -7127,6 +8431,12 @@ msgstr ""
msgid "You need permission."
msgstr "権é™ãŒå¿…è¦ã§ã™"
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "通知メールをé€ä¿¡ã—ã¾ã›ã‚“"
@@ -7149,7 +8459,7 @@ msgid "You won't be able to pull or push project code via SSH until you %{add_ss
msgstr ""
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr ""
+msgstr "SSH éµã‚’プロフィールã«è¿½åŠ ã—ãªã„é™ã‚Šã€SSH 経由ã§ãƒ—ロジェクトã®ã‚³ãƒ¼ãƒ‰ã‚’プルã—ãŸã‚Šãƒ—ッシュã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。"
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "有効ãªæ¯”較を行ã†ãŸã‚ã«ã¯ã€ç•°ãªã‚‹ãƒ–ランãƒåを使用ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
@@ -7164,13 +8474,13 @@ msgid "You're receiving this email because of your account on %{host}. %{manage_
msgstr "ã“ã®ãƒ¡ãƒ¼ãƒ«ã¯ %{host} ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆå®›ã«é€ä¿¡ã•ã‚Œã¾ã—ãŸã€‚ %{manage_notifications_link}&middot;%{help_link}"
msgid "YouTube"
-msgstr ""
+msgstr "YouTube"
msgid "Your Groups"
msgstr "所属グループ"
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
-msgstr ""
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã® Kubernetes クラスター情報ã¯ç·¨é›†å¯èƒ½ã§ã™ãŒã€ç„¡åŠ¹ã«ã—ã¦å†è¨­å®šã™ã‚‹ã“ã¨ã‚’推奨ã—ã¾ã™"
msgid "Your Projects (default)"
msgstr "プロジェクト(デフォルト)"
@@ -7188,7 +8498,7 @@ msgid "Your authorized applications"
msgstr ""
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
-msgstr ""
+msgstr "マージリクエストãŒé–‹ã„ã¦ã„ã‚‹ãŸã‚ã€å¤‰æ›´ã¯ %{branch_name} ã«ã‚³ãƒŸãƒƒãƒˆã§ãã¾ã™ã€‚"
msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
msgstr ""
@@ -7205,15 +8515,14 @@ msgstr "åå‰"
msgid "Your projects"
msgstr "ã‚ãªãŸã®ãƒ—ロジェクト"
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr "å‰"
msgid "among other things"
-msgstr ""
-
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
+msgstr "ãã®ä»–ã®ã‚‚ã®"
msgid "assign yourself"
msgstr "自分ã«å‰²ã‚Šå½“ã¦"
@@ -7225,7 +8534,7 @@ msgid "by"
msgstr ""
msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}コンテナスキャンã®è©³ç´°%{linkEndTag}"
msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
msgstr ""
@@ -7237,30 +8546,55 @@ msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr "%{namespace} 㯠%{vulnerability} ã®å½±éŸ¿ã‚’å—ã‘ã¾ã™ã€‚"
+
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
msgid "ciReport|Class"
@@ -7272,44 +8606,35 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
-msgstr ""
+msgid "ciReport|Container scanning detected"
+msgstr "コンテナスキャンãŒæ¤œå‡ºã•ã‚Œã¾ã—ãŸ"
-msgid "ciReport|Container scanning is loading"
-msgstr ""
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr "コンテナスキャンã¯ã€Docker イメージã«å­˜åœ¨ã™ã‚‹æ—¢çŸ¥ã®è„†å¼±æ€§ã‚’検出ã—ã¾ã™ã€‚"
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
msgid "ciReport|DAST detected"
-msgstr ""
+msgstr "DAST ãŒæ¤œå‡ºã•ã‚Œã¾ã—ãŸ"
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
-msgstr ""
-
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|Dependency scanning"
msgstr ""
msgid "ciReport|Dependency scanning detected"
-msgstr ""
-
-msgid "ciReport|Dependency scanning is loading"
-msgstr ""
-
-msgid "ciReport|Dependency scanning resulted in error while loading results"
-msgstr ""
+msgstr "ä¾å­˜é–¢ä¿‚スキャンãŒæ¤œå‡ºã•ã‚Œã¾ã—ãŸ"
msgid "ciReport|Description"
msgstr "説明"
msgid "ciReport|Dismiss vulnerability"
-msgstr ""
+msgstr "脆弱性を無視ã™ã‚‹"
msgid "ciReport|Dismissed by"
msgstr ""
@@ -7335,10 +8660,15 @@ msgstr "インスタンス"
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7354,7 +8684,7 @@ msgid "ciReport|Method"
msgstr ""
msgid "ciReport|Namespace"
-msgstr ""
+msgstr "åå‰ç©ºé–“"
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -7368,13 +8698,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
-msgstr ""
-
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7383,9 +8710,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7396,37 +8720,38 @@ msgid "ciReport|Static Application Security Testing (SAST) detects known vulnera
msgstr ""
msgid "ciReport|There was an error creating the issue. Please try again."
-msgstr ""
+msgstr "課題を作æˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
-msgstr ""
+msgstr "脆弱性を無視ã™ã‚‹éš›ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "ciReport|There was an error loading DAST report"
-msgstr ""
+msgstr "DAST レãƒãƒ¼ãƒˆã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ciReport|There was an error loading SAST report"
-msgstr ""
+msgstr "SAST レãƒãƒ¼ãƒˆã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ciReport|There was an error loading container scanning report"
-msgstr ""
+msgstr "コンテナスキャンレãƒãƒ¼ãƒˆã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ciReport|There was an error loading dependency scanning report"
-msgstr ""
+msgstr "ä¾å­˜é–¢ä¿‚スキャンレãƒãƒ¼ãƒˆã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
-msgstr ""
+msgstr "%{name} ã‚’ %{version} ã‹ã‚‰ %{fixed} ã¸ã‚¢ãƒƒãƒ—グレードã—ã¦ãã ã•ã„。"
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
-msgstr ""
+msgstr "脆弱性ãªã—"
msgid "ciReport|on pipeline"
msgstr ""
@@ -7434,14 +8759,20 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr "接続中"
msgid "could not read private key, is the passphrase correct?"
-msgstr ""
+msgstr "秘密éµã‚’読ã¿å–ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚パスフレーズã¯æ­£ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "customize"
-msgstr ""
+msgstr "カスタマイズ"
msgid "day"
msgid_plural "days"
@@ -7450,17 +8781,6 @@ msgstr[0] "æ—¥"
msgid "deploy token"
msgstr "デプロイトークン"
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr "無効"
@@ -7468,7 +8788,7 @@ msgid "done"
msgstr ""
msgid "enabled"
-msgstr ""
+msgstr "有効"
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
@@ -7479,12 +8799,19 @@ msgstr "ã“ã®ãƒ—ロジェクトã§ã¯"
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr "https://your-bitbucket-server"
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr "インãƒãƒ¼ãƒˆä¸­"
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7494,9 +8821,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7508,31 +8841,34 @@ msgid "mrWidget| Please restore it or use a different %{missingBranchName} branc
msgstr "ブランãƒã‚’復元ã™ã‚‹ã‹ã€åˆ¥ã® %{missingBranchName} ブランãƒã‚’使用ã—ã¦ãã ã•ã„"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} メモリ %{metricsLinkEnd} 使用率㌠%{memoryFrom} MBã‹ã‚‰ %{memoryTo} MB㸠%{emphasisStart} 減少 %{emphasisEnd}"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} メモリ %{metricsLinkEnd} 使用率㌠%{memoryFrom} MBã‹ã‚‰ %{memoryTo} MB㸠%{emphasisStart} 増加 %{emphasisEnd}"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} メモリ %{metricsLinkEnd} 使用率㯠%{memoryFrom} MB㧠%{emphasisStart} 変化ãªã— %{emphasisEnd}"
msgid "mrWidget|Add approval"
-msgstr ""
+msgstr "承èªã‚’追加"
msgid "mrWidget|Allows commits from members who can merge to the target branch"
-msgstr ""
+msgstr "ターゲットブランãƒã«ãƒžãƒ¼ã‚¸ã§ãるメンãƒãƒ¼ã‹ã‚‰ã®ã‚³ãƒŸãƒƒãƒˆã‚’許å¯ã™ã‚‹"
msgid "mrWidget|An error occured while removing your approval."
+msgstr "承èªã‚’削除中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
msgstr ""
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
-msgstr ""
+msgstr "承èª"
msgid "mrWidget|Approved by"
-msgstr ""
+msgstr "承èªè€…"
msgid "mrWidget|Cancel automatic merge"
msgstr "自動マージã®ã‚­ãƒ£ãƒ³ã‚»ãƒ«"
@@ -7544,10 +8880,10 @@ msgid "mrWidget|Checking ability to merge automatically"
msgstr ""
msgid "mrWidget|Cherry-pick"
-msgstr ""
+msgstr "ãƒã‚§ãƒªãƒ¼ãƒ”ック"
msgid "mrWidget|Cherry-pick this merge request in a new merge request"
-msgstr ""
+msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ãƒã‚§ãƒªãƒ¼ãƒ”ックã—ã¦ã€æ–°ã—ã„マージリクエストを作æˆã™ã‚‹"
msgid "mrWidget|Closed"
msgstr ""
@@ -7568,9 +8904,12 @@ msgid "mrWidget|Did not close"
msgstr ""
msgid "mrWidget|Email patches"
-msgstr ""
+msgstr "メールパッãƒ"
msgid "mrWidget|Failed to load deployment statistics"
+msgstr "デプロイ統計ã®ãƒ­ãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
msgstr ""
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
@@ -7580,7 +8919,7 @@ msgid "mrWidget|If the %{missingBranchName} branch exists in your local reposito
msgstr ""
msgid "mrWidget|Loading deployment statistics"
-msgstr ""
+msgstr "デプロイ統計を読ã¿è¾¼ã¿ä¸­"
msgid "mrWidget|Mentions"
msgstr "メンション"
@@ -7589,10 +8928,10 @@ msgid "mrWidget|Merge"
msgstr "マージ"
msgid "mrWidget|Merge failed."
-msgstr ""
+msgstr "マージã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
msgid "mrWidget|Merge locally"
-msgstr ""
+msgstr "ローカルã§ãƒžãƒ¼ã‚¸"
msgid "mrWidget|Merge request approved"
msgstr ""
@@ -7604,17 +8943,23 @@ msgid "mrWidget|Merged by"
msgstr "マージ作業者"
msgid "mrWidget|No Approval required"
-msgstr ""
+msgstr "承èªã¯ä¸è¦"
msgid "mrWidget|No Approval required; you can still approve"
-msgstr ""
+msgstr "承èªã¯ä¸è¦ã§ã™ãŒã€æ‰¿èªã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™"
msgid "mrWidget|Open in Web IDE"
+msgstr "Web IDE ã§é–‹ã"
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
msgstr ""
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr "æ›´æ–°"
@@ -7631,14 +8976,25 @@ msgid "mrWidget|Remove source branch"
msgstr "ソースブランãƒã‚’削除ã™ã‚‹"
msgid "mrWidget|Remove your approval"
-msgstr ""
+msgstr "承èªã‚’削除ã™ã‚‹"
msgid "mrWidget|Request to merge"
msgstr "マージをリクエスト"
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr "競åˆã‚’解決ã™ã‚‹"
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr "リãƒãƒ¼ãƒˆ"
@@ -7652,14 +9008,23 @@ msgid "mrWidget|The changes were merged into"
msgstr ""
msgid "mrWidget|The changes were not merged into"
-msgstr ""
+msgstr "ã“ã®å¤‰æ›´ã¯æ¬¡ã®ãƒ–ランãƒã«ãƒžãƒ¼ã‚¸ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
msgid "mrWidget|The changes will be merged into"
+msgstr "ã“ã®å¤‰æ›´ã¯æ¬¡ã®ãƒ–ランãƒã«ãƒžãƒ¼ã‚¸ã•ã‚Œã¾ã™"
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
msgstr ""
msgid "mrWidget|The source branch has been removed"
msgstr "ã“ã®ã‚½ãƒ¼ã‚¹ãƒ–ランãƒã¯å‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr "ã“ã®ã‚½ãƒ¼ã‚¹ãƒ–ランãƒã¯å‰Šé™¤ã•ã‚Œã‚ˆã†ã¨ã—ã¦ã„ã¾ã™"
@@ -7684,9 +9049,12 @@ msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ãƒžãƒ¼ã‚¸å®Ÿè¡Œä¸­ã§ã™"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "ã“ã®ãƒ—ロジェクトã¯ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã•ã‚Œã¦ã„ã‚‹ãŸã‚ã€æ›¸ãè¾¼ã¿ã¯ç„¡åŠ¹ã§ã™ã€‚"
-msgid "mrWidget|You can merge this merge request manually using the"
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
msgstr ""
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’手動ã§ãƒžãƒ¼ã‚¸ã§ãã¾ã™"
+
msgid "mrWidget|You can remove source branch now"
msgstr ""
@@ -7702,6 +9070,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr "パイプラインãŒæˆåŠŸã—ãŸã¨ãã¯è‡ªå‹•çš„ã«ãƒžãƒ¼ã‚¸ã•ã‚Œã¾ã™"
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "æ–°è¦ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
@@ -7711,6 +9082,10 @@ msgstr "メール通知"
msgid "or"
msgstr "ã¾ãŸã¯"
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "親"
@@ -7722,22 +9097,25 @@ msgid "personal access token"
msgstr "個人ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³"
msgid "private key does not match certificate."
-msgstr ""
+msgstr "秘密éµãŒè¨¼æ˜Žæ›¸ã¨ä¸€è‡´ã—ã¾ã›ã‚“。"
msgid "remaining"
msgstr "残り"
-msgid "remove due date"
+msgid "remove"
msgstr ""
+msgid "remove due date"
+msgstr "期é™ã‚’削除"
+
msgid "remove weight"
-msgstr ""
+msgstr "ウェイトを削除"
msgid "source"
msgstr "ソース"
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
-msgstr ""
+msgstr "%{slash_command} ã¯ç´¯è¨ˆçµŒéŽæ™‚é–“ã‚’æ›´æ–°ã—ã¾ã™"
msgid "started"
msgstr ""
@@ -7748,19 +9126,22 @@ msgstr "ã“ã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ"
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr "ユーザーå"
msgid "uses Kubernetes clusters to deploy your code!"
-msgstr ""
+msgstr "コードをデプロイã™ã‚‹ãŸã‚ã« Kubernetes クラスターを使用ã™ã‚‹ã€‚"
msgid "view it on GitLab"
-msgstr ""
+msgstr "GitLab ã§è¦‹ã‚‹"
msgid "with %{additions} additions, %{deletions} deletions."
-msgstr ""
+msgstr "%{additions} 件ã®è¿½åŠ ã¨ %{deletions} 件ã®å‰Šé™¤ãŒã‚ã‚Šã¾ã™ã€‚"
msgid "within %d minute "
msgid_plural "within %d minutes "
-msgstr[0] ""
+msgstr[0] "%d 分以内"
diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po
index 36a0d3179b2..daced0494cb 100644
--- a/locale/ko/gitlab.po
+++ b/locale/ko/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ko\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 12:38\n"
+"PO-Revision-Date: 2018-10-02 09:28\n"
+
+msgid " Status"
+msgstr " ìƒíƒœ"
msgid " and"
msgstr " 그리고"
@@ -42,6 +45,14 @@ msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d 내보내기"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] "%dê±´ì˜ í…ŒìŠ¤íŠ¸ê°€ 실패하였습니다."
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] "%dê±´ì˜ í…ŒìŠ¤íŠ¸ 결과를 고쳤습니다."
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d ì´ìŠˆ"
@@ -58,10 +69,6 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] "%d 측정치"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] "%dê°œì˜ìƒˆ ë¼ì´ì„ ìŠ¤"
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] "%dê°œì˜ ìŠ¤í…Œì´ì§•ëœ 변경사항"
@@ -94,9 +101,15 @@ msgstr[0] "%{count} ëª…ì˜ ì°¸ì—¬ìž"
msgid "%{filePath} deleted"
msgstr "%{filePath} ì‚­ì œë¨"
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr "%{group_docs_link_start}그룹%{group_docs_link_end}ì„ ì‚¬ìš©í•˜ë©´ 여러 프로ì íŠ¸ë¥¼ 관리하고 ê³µë™ ìž‘ì—…ì„ ìˆ˜í–‰ í•  수 있습니다. 그룹 회ì›ì€ 모든 프로ì íŠ¸ì— 액세스 í•  수 있습니다."
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr "%{issuableType}ì´ ì‚­ì œë©ë‹ˆë‹¤! 확실합니까?"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 시작ë¨"
@@ -138,13 +151,15 @@ msgstr "%{text} 사용 가능"
msgid "%{title} changes"
msgstr "%{title} 변경"
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] "%{type} ì—ì„œ ì·¨ì•½ì  %{vulnerabilityCount} 개를 찾았습니다"
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "%{unstaged} ê±´ì´ ìŠ¤í…Œì´ì§•ë˜ì§€ 않았고, %{staged} ê±´ì´ ìŠ¤í…Œì´ì§• ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} ë”"
@@ -173,6 +188,10 @@ msgid "1 closed merge request"
msgid_plural "%d closed merge requests"
msgstr[0] "%dê±´ì˜ ë¨¸ì§€ë¦¬í€˜ìŠ¤íŠ¸ 닫힘"
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] "%d 그룹"
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] "%dê±´ì˜ ë¨¸ì§€ë¦¬í€˜ìŠ¤íŠ¸ 머지ë¨"
@@ -189,26 +208,34 @@ msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d 파ì´í”„ë¼ì¸"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+
msgid "1st contribution!"
-msgstr ""
+msgstr "첫번째 기여!"
msgid "2FA enabled"
msgstr "2FA 사용"
msgid "403|Please contact your GitLab administrator to get the permission."
-msgstr "403|ê¶Œí•œì„ ì–»ìœ¼ë ¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì‹­ì‹œì˜¤."
+msgstr "ê¶Œí•œì„ ì–»ìœ¼ë ¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì‹­ì‹œì˜¤."
msgid "403|You don't have the permission to access this page."
-msgstr "403|ì´ íŽ˜ì´ì§€ì— 대한 액세스 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
+msgstr "ì´ íŽ˜ì´ì§€ì— 대한 액세스 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
msgid "404|Make sure the address is correct and the page hasn't moved."
-msgstr "404|주소가 정확하고 페ì´ì§€ê°€ ì´ë™í•˜ì§€ 않았는지 확ì¸í•˜ì‹­ì‹œì˜¤."
+msgstr "주소가 정확하고 페ì´ì§€ê°€ ì´ë™í•˜ì§€ 않았는지 확ì¸í•˜ì‹­ì‹œì˜¤."
msgid "404|Page Not Found"
-msgstr "404|페ì´ì§€ë¥¼ ì°¾ì„ ìˆ˜ 없습니다"
+msgstr "페ì´ì§€ë¥¼ ì°¾ì„ ìˆ˜ 없습니다"
msgid "404|Please contact your GitLab administrator if you think this is a mistake."
-msgstr "404|ì´ê²ƒì´ ì‹¤ìˆ˜ì— ì˜í•œ 것ì´ë¼ê³  ìƒê°í•œë‹¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
+msgstr "ì´ê²ƒì´ ì‹¤ìˆ˜ì— ì˜í•œ 것ì´ë¼ê³  ìƒê°í•œë‹¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
msgstr ""
@@ -222,6 +249,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -237,12 +267,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
-msgstr "'러너(Runner)'는 ìž‘ì—…ì„ ì‹¤í–‰í•˜ëŠ” 프로세스입니다. 필요한 ë§Œí¼ ëŸ¬ë„ˆë¥¼ ì…‹ì—…í•  수 있습니다."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "지ì†ì ì¸ í†µí•©ì— ê´€í•œ 그래프 모ìŒ"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -285,13 +321,16 @@ msgstr "액세스 토í°"
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "오ë™ìž‘ì¤‘ì¸ ì €ìž¥ê³µê°„ì— ëŒ€í•œ ì ‘ê·¼ì´ ë³µêµ¬ ìž‘ì—…ì„ ìœ„í•´ 마운트할 수 있ë„ë¡ ìž„ì‹œë¡œ 허용ë˜ì—ˆìŠµë‹ˆë‹¤. 문제가 í•´ê²°ëœ í›„ 다시 ì ‘ê·¼ì„ í—ˆìš©í•  수 있게 저장공간 정보를 리셋 해주세요."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -310,7 +349,7 @@ msgid "Activity"
msgstr "활ë™"
msgid "Add"
-msgstr ""
+msgstr "추가"
msgid "Add Changelog"
msgstr "변경 로그 추가"
@@ -322,10 +361,7 @@ msgid "Add Group Webhooks and GitLab Enterprise Edition."
msgstr ""
msgid "Add Kubernetes cluster"
-msgstr ""
-
-msgid "Add License"
-msgstr "ë¼ì´ì„ ìŠ¤ 추가"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 추가"
msgid "Add Readme"
msgstr ""
@@ -333,62 +369,77 @@ msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr "ë¼ì´ì„ ìŠ¤ 추가"
+
msgid "Add new application"
-msgstr ""
+msgstr "새 애플리케ì´ì…˜ 추가"
msgid "Add new directory"
msgstr "새 디렉토리 추가"
msgid "Add reaction"
-msgstr ""
+msgstr "ë°˜ì‘ ì¶”ê°€"
msgid "Add todo"
-msgstr ""
+msgstr "í•  ì¼ ì¶”ê°€"
msgid "Add user(s) to the group:"
msgstr ""
msgid "Add users to group"
+msgstr "ê·¸ë£¹ì— ì‚¬ìš©ìž ì¶”ê°€"
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
msgid "Additional text"
msgstr ""
msgid "Admin Area"
-msgstr ""
+msgstr "ê´€ë¦¬ìž ì˜ì—­"
msgid "Admin Overview"
msgstr ""
msgid "Admin area"
+msgstr "ê´€ë¦¬ìž ì˜ì—­"
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
msgstr ""
-msgid "AdminArea|Stop all jobs"
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
msgstr ""
+msgid "AdminArea|Stop all jobs"
+msgstr "모든 작업 중지"
+
msgid "AdminArea|Stop all jobs?"
-msgstr ""
+msgstr "모든 ìž‘ì—…ì„ ì¤‘ì§€í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
msgid "AdminArea|Stop jobs"
-msgstr ""
+msgstr "작업 중지"
msgid "AdminArea|Stopping jobs failed"
-msgstr ""
+msgstr "ìž‘ì—… ì¤‘ì§€ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤."
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
-msgstr ""
+msgstr "모든 ìž‘ì—…ì„ ì¤‘ì§€í•©ë‹ˆë‹¤. 현재 ì‹¤í–‰ì¤‘ì¸ ëª¨ë“  ìž‘ì—…ì´ ì¤‘ì§€ë©ë‹ˆë‹¤."
msgid "AdminHealthPageLink|health page"
msgstr "ìƒíƒœ 페ì´ì§€"
-msgid "AdminProjects|Delete"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
msgstr ""
+msgid "AdminProjects|Delete"
+msgstr "삭제"
+
msgid "AdminProjects|Delete Project %{projectName}?"
-msgstr ""
+msgstr "%{projectName} 프로ì íŠ¸ë¥¼ 삭제하시겠습니까?"
msgid "AdminProjects|Delete project"
-msgstr ""
+msgstr "프로ì íŠ¸ ì‚­ì œ"
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
msgstr ""
@@ -409,7 +460,7 @@ msgid "AdminUsers|Delete user and contributions"
msgstr ""
msgid "AdminUsers|To confirm, type %{projectName}"
-msgstr ""
+msgstr "확ì¸ì„ 위해 %{projectName} 를 입력해주세요."
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
@@ -424,11 +475,14 @@ msgid "All"
msgstr "ì „ì²´"
msgid "All changes are committed"
-msgstr ""
+msgstr "모든 ë³€ê²½ì‚¬í•­ì´ ì»¤ë°‹ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr "모든 사용ìž"
+
msgid "Allow commits from members who can merge to the target branch."
msgstr "ëŒ€ìƒ ë¸Œëžœì¹˜ì— ë¨¸ì§€í•  수 있는 ë©¤ë²„ì˜ ì»¤ë°‹ì„ í—ˆìš©í•©ë‹ˆë‹¤."
@@ -456,6 +510,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -466,25 +523,25 @@ msgid "An error accured whilst committing your changes."
msgstr ""
msgid "An error has occurred"
-msgstr ""
+msgstr "ì—러가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occured creating the new branch."
-msgstr ""
+msgstr "새 브랜치를 만드는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
-msgstr ""
+msgstr "모든 파ì¼ì„ 로드하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occured whilst loading the file content."
-msgstr ""
+msgstr "íŒŒì¼ ë‚´ìš©ì„ ë¡œë“œí•˜ëŠ” ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occured whilst loading the file."
-msgstr ""
+msgstr "파ì¼ì„ 로드하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occured whilst loading the merge request changes."
msgstr ""
@@ -505,7 +562,7 @@ msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
-msgstr ""
+msgstr "ì´ìŠˆ ì¤‘ìš”ë„ ì—…ë°ì´íŠ¸ 중 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while adding approver"
msgstr ""
@@ -520,18 +577,30 @@ msgid "An error occurred while dismissing the feature highlight. Refresh the pag
msgstr "기능 ê°•ì¡° 표시를 해제하는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 페ì´ì§€ë¥¼ 새로고침하고, 다시 ì‹œë„해주세요."
msgid "An error occurred while fetching markdown preview"
-msgstr ""
+msgstr "마í¬ë‹¤ìš´ 미리보기를 가져 오는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching sidebar data"
msgstr ""
-msgid "An error occurred while fetching the pipeline."
+msgid "An error occurred while fetching stages."
+msgstr "스테ì´ì§€ë¥¼ 가져 오는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
+
+msgid "An error occurred while fetching the job log."
msgstr ""
-msgid "An error occurred while getting projects"
+msgid "An error occurred while fetching the job."
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr "파ì´í”„ë¼ì¸ì„ ë°˜ì˜í•˜ë˜ 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+
+msgid "An error occurred while getting projects"
+msgstr "프로ì íŠ¸ë¥¼ 가져오는 ë™ì•ˆ 오류가 ë°œìƒ í–ˆìŠµë‹ˆë‹¤."
+
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -544,10 +613,10 @@ msgid "An error occurred while loading diff"
msgstr ""
msgid "An error occurred while loading filenames"
-msgstr ""
+msgstr "íŒŒì¼ ì´ë¦„ì„ ë¡œë“œí•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading the file"
-msgstr ""
+msgstr "íŒŒì¼ ë¡œë“œ 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
msgid "An error occurred while making the request."
msgstr ""
@@ -571,13 +640,13 @@ msgid "An error occurred while saving LDAP override status. Please try again."
msgstr ""
msgid "An error occurred while saving assignees"
-msgstr ""
+msgstr "담당ìžë¥¼ 저장하는 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
msgid "An error occurred while subscribing to notifications."
-msgstr ""
+msgstr "ì•Œë¦¼ì„ êµ¬ë…하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while unsubscribing to notifications."
-msgstr ""
+msgstr "알림 구ë…ì„ í•´ì œí•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while validating username"
msgstr ""
@@ -586,13 +655,13 @@ msgid "An error occurred. Please try again."
msgstr "오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "Anonymous"
-msgstr ""
+msgstr "ìµëª…"
msgid "Anti-spam verification"
-msgstr ""
+msgstr "안티 스팸 ê²€ì¦"
msgid "Any"
-msgstr ""
+msgstr "Any"
msgid "Any Label"
msgstr ""
@@ -601,7 +670,7 @@ msgid "Appearance"
msgstr ""
msgid "Application"
-msgstr ""
+msgstr "어플리케ì´ì…˜"
msgid "Application Id"
msgstr ""
@@ -613,7 +682,7 @@ msgid "Applications"
msgstr "어플리케ì´ì…˜"
msgid "Apr"
-msgstr ""
+msgstr "4ì›”"
msgid "April"
msgstr ""
@@ -621,10 +690,16 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ì´ íŒŒì´í”„ë¼ì¸ ìŠ¤ì¼€ì¥´ì„ ì‚­ì œ 하시겠습니까?"
msgid "Are you sure you want to lose unsaved changes?"
+msgstr "저장ë˜ì§€ ì•Šì€ ë³€ê²½ì‚¬í•­ì„ ì‚­ì œí•˜ê² ìŠµë‹ˆê¹Œ?"
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
@@ -645,14 +720,17 @@ msgstr ""
msgid "Are you sure?"
msgstr "확실합니까?"
-msgid "Artifacts"
+msgid "Artifact ID"
msgstr ""
+msgid "Artifacts"
+msgstr "결과물"
+
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
-msgstr "그룹 관리ìžì—게 그룹 Runner 를 설정하ë„ë¡ ìš”ì²­í•˜ì„¸ìš”"
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
msgid "Assertion consumer service URL"
msgstr ""
@@ -661,16 +739,16 @@ msgid "Assign custom color like #FF0000"
msgstr ""
msgid "Assign labels"
-msgstr ""
+msgstr "ë¼ë²¨ 지정"
msgid "Assign milestone"
-msgstr ""
+msgstr "마ì¼ìŠ¤í†¤ 지정"
msgid "Assign to"
msgstr ""
msgid "Assigned Issues"
-msgstr ""
+msgstr "í• ë‹¹ëœ ì´ìŠˆ"
msgid "Assigned Merge Requests"
msgstr ""
@@ -679,12 +757,12 @@ msgid "Assigned to :name"
msgstr ""
msgid "Assigned to me"
-msgstr ""
+msgstr "나ì—게 할당 ë¨"
msgid "Assignee"
-msgstr ""
+msgstr "담당ìž"
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -700,22 +778,25 @@ msgid "Audit Events"
msgstr ""
msgid "Aug"
-msgstr ""
+msgstr "8ì›”"
msgid "August"
-msgstr ""
+msgstr "8ì›”"
msgid "Authentication Log"
-msgstr ""
+msgstr "ì¸ì¦ 로그"
msgid "Authentication log"
msgstr ""
-msgid "Author"
+msgid "Authentication method"
msgstr ""
+msgid "Author"
+msgstr "작성ìž"
+
msgid "Authorization code:"
-msgstr ""
+msgstr "ì¸ì¦ 코드:"
msgid "Authorization was granted by entering your username and password in the application."
msgstr ""
@@ -724,7 +805,7 @@ msgid "Authorize"
msgstr ""
msgid "Authorize %{link_to_client} to use your account?"
-msgstr ""
+msgstr "%{link_to_client} ì„ ë‚´ 계정으로 ì¸ì¦í•˜ì‹œê² ì–´ìš”?"
msgid "Authorized At"
msgstr ""
@@ -736,10 +817,10 @@ msgid "Authors: %{authors}"
msgstr ""
msgid "Auto DevOps"
-msgstr ""
+msgstr "ìžë™ DevOps"
msgid "Auto DevOps enabled"
-msgstr ""
+msgstr "ìžë™ DevOps 활성화ë¨"
msgid "Auto DevOps, runners and job artifacts"
msgstr "ìžë™ DevOps, Runner ìž‘ì—… artifacts"
@@ -771,14 +852,20 @@ msgstr "애플리케ì´ì…˜ì˜ 빌드, 테스트, ë°°í¬ê°€ ì‚¬ì „ì— ì •ì˜ëœ C
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "ë” ì•Œì•„ë³´ê¸° %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes cluster 추가"
msgid "AutoDevOps|enable Auto DevOps"
-msgstr "AutoDevOps | Auto DevOps 활성화"
+msgstr "Auto DevOps 활성화"
+
+msgid "Automatically marked as default internal user"
+msgstr ""
msgid "Available"
msgstr ""
@@ -790,13 +877,13 @@ msgid "Available group Runners : %{runners}."
msgstr "사용 가능한 그룹 Runner: %{runners}."
msgid "Avatar will be removed. Are you sure?"
-msgstr ""
+msgstr "아바타가 ì‚­ì œë©ë‹ˆë‹¤. 확실합니까?"
msgid "Average per day: %{average}"
msgstr ""
msgid "Background Color"
-msgstr ""
+msgstr "배경 색"
msgid "Background Jobs"
msgstr ""
@@ -804,29 +891,26 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
-msgstr ""
+msgstr "배지"
msgid "Badges|A new badge was added."
-msgstr ""
+msgstr "새로운 배지가 추가ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Badges|Add badge"
-msgstr ""
+msgstr "배지 추가"
msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
msgstr ""
msgid "Badges|Badge image URL"
-msgstr ""
+msgstr "배지 ì´ë¯¸ì§€ URL"
msgid "Badges|Badge image preview"
-msgstr ""
+msgstr "배지 ì´ë¯¸ì§€ 미리 보기"
msgid "Badges|Delete badge"
-msgstr ""
+msgstr "배지 삭제"
msgid "Badges|Delete badge?"
msgstr ""
@@ -835,28 +919,31 @@ msgid "Badges|Deleting the badge failed, please try again."
msgstr ""
msgid "Badges|Group Badge"
-msgstr ""
+msgstr "그룹 배지"
msgid "Badges|Link"
-msgstr ""
+msgstr "ë§í¬"
msgid "Badges|No badge image"
msgstr ""
msgid "Badges|No image to preview"
+msgstr "미리 ë³¼ ì´ë¯¸ì§€ê°€ 없습니다"
+
+msgid "Badges|Please fill in a valid URL"
msgstr ""
msgid "Badges|Project Badge"
-msgstr ""
+msgstr "프로ì íŠ¸ 배지"
msgid "Badges|Reload badge image"
msgstr ""
msgid "Badges|Save changes"
-msgstr ""
+msgstr "변경 사항 저장"
msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
-msgstr ""
+msgstr "배지 저장 실패, 입력한 URLì„ í™•ì¸í•˜ì‹œê³  다시 ì‹œë„하십시오."
msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
msgstr ""
@@ -873,7 +960,13 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
+msgstr "내 배지"
+
+msgid "Badges|e.g. %{exampleUrl}"
msgstr ""
msgid "Begin with the selected commit"
@@ -886,7 +979,7 @@ msgid "Below you will find all the groups that are public."
msgstr ""
msgid "Billing"
-msgstr ""
+msgstr "결제"
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
msgstr ""
@@ -895,22 +988,22 @@ msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently n
msgstr ""
msgid "BillingPlans|Current plan"
-msgstr ""
+msgstr "현재 요금제"
msgid "BillingPlans|Customer Support"
-msgstr ""
+msgstr "ê³ ê° ì§€ì›"
msgid "BillingPlans|Downgrade"
-msgstr ""
+msgstr "다운그레ì´ë“œ"
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
-msgstr ""
+msgstr "%{faq_link}ì„ ì½ìŒìœ¼ë¡œì¨ í”Œëžœì— ëŒ€í•´ ë” ìžì„¸ížˆ 알아보거나 GitLab.com Gold 무료 30 ì¼ í‰ê°€íŒì„ 시작하세요."
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
-msgstr ""
+msgstr "%{faq_link} ì„ ì½ì–´ í”Œëžœì— ëŒ€í•´ ìžì„¸ížˆ 알아보십시오."
msgid "BillingPlans|Manage plan"
-msgstr ""
+msgstr "플랜 관리"
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
msgstr ""
@@ -925,30 +1018,33 @@ msgid "BillingPlans|To manage the plan for this group, visit the billing section
msgstr ""
msgid "BillingPlans|Upgrade"
-msgstr ""
+msgstr "업그레ì´ë“œ"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
-msgstr ""
+msgstr "현재 %{plan_link} í”Œëžœì„ ì´ìš©ì¤‘입니다."
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
-msgstr ""
+msgstr "GitLab.com í‰ê°€íŒì´ %{expiration_date}ì— ë§Œë£Œë˜ì—ˆìŠµë‹ˆë‹¤. %{learn_more_text}"
msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
-msgstr ""
+msgstr "골드 ì²´í—˜íŒì´ <strong>%{expiration_date} í›„ì— ë§Œë£Œë©ë‹ˆë‹¤.</strong>. GitLab.com Goldì— ëŒ€í•œ ìžì„¸í•œ 정보는 %{features_link}ì—ì„œ ì½ì–´ë³¼ 수 있습니다."
msgid "BillingPlans|features"
-msgstr ""
+msgstr "기능"
msgid "BillingPlans|frequently asked questions"
-msgstr ""
+msgstr "ìžì£¼ 묻는 질문"
msgid "BillingPlans|monthly"
-msgstr ""
+msgstr "ì›”"
msgid "BillingPlans|paid annually at %{price_per_year}"
msgstr ""
msgid "BillingPlans|per user"
+msgstr "ì‚¬ìš©ìž ë‹¹"
+
+msgid "Bitbucket Server Import"
msgstr ""
msgid "Bitbucket import"
@@ -995,7 +1091,7 @@ msgid "Branches|Active branches"
msgstr ""
msgid "Branches|All"
-msgstr ""
+msgstr "모ë‘"
msgid "Branches|Cant find HEAD commit for this branch"
msgstr ""
@@ -1031,7 +1127,7 @@ msgid "Branches|Merged into %{default_branch}"
msgstr ""
msgid "Branches|New branch"
-msgstr ""
+msgstr "새 브랜치"
msgid "Branches|No branches to show"
msgstr ""
@@ -1040,25 +1136,25 @@ msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot
msgstr ""
msgid "Branches|Only a project maintainer or owner can delete a protected branch"
-msgstr ""
+msgstr "프로ì íŠ¸ ê´€ë¦¬ìž ë˜ëŠ” ì†Œìœ ìž ë§Œ 보호 브랜치를 삭제할 수 있습니다."
msgid "Branches|Overview"
-msgstr ""
+msgstr "개요"
msgid "Branches|Protected branches can be managed in %{project_settings_link}."
-msgstr ""
+msgstr "ë³´í˜¸ëœ ë¸Œëžœì¹˜ëŠ” %{project_settings_link} ì—ì„œ 관리할 수 있습니다."
msgid "Branches|Show active branches"
-msgstr ""
+msgstr "í™œì„±ëœ ë¸Œëžœì¹˜ 보기"
msgid "Branches|Show all branches"
-msgstr ""
+msgstr "모든 브랜치 보기"
msgid "Branches|Show more active branches"
-msgstr ""
+msgstr "í™œì„±ëœ ë¸Œëžœì¹˜ ë” ë³´ê¸°"
msgid "Branches|Show more stale branches"
-msgstr ""
+msgstr "ì•ˆì •í™”ëœ ë¸Œëžœì¹˜ ë” ë³´ê¸°"
msgid "Branches|Show overview of the branches"
msgstr ""
@@ -1120,6 +1216,9 @@ msgstr "íŒŒì¼ ì°¾ì•„ë³´ê¸°"
msgid "Browse files"
msgstr "íŒŒì¼ ì°¾ì•„ë³´ê¸°"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1132,6 +1231,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1139,19 +1241,16 @@ msgid "CI/CD configuration"
msgstr ""
msgid "CI/CD for external repo"
-msgstr ""
+msgstr "외부 저장소용 CI/CD"
msgid "CI/CD settings"
msgstr "CI/CD 설정"
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr "CICD|Continuous Integration ê³¼ Delivery 를 사용하기 위해서는 %{ci_file} ì´ ë°˜ë“œì‹œ 명시ë˜ì–´ì•¼ 합니다."
-
msgid "CICD|Auto DevOps"
-msgstr "CICD|Auto DevOps"
+msgstr "Auto DevOps"
msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
-msgstr "CICD|Auto DevOps는 ì‚¬ì „ì— ì •ì˜ëœ Continuous Integrationê³¼ Delivery ì„¤ì •ì„ ë°”íƒ•ìœ¼ë¡œí•˜ì—¬ ìžë™ìœ¼ë¡œ 빌드, 테스트 그리고 ë°°í¬ë¥¼ 수행합니다."
+msgstr "Auto DevOps는 ì‚¬ì „ì— ì •ì˜ëœ Continuous Integrationê³¼ Delivery ì„¤ì •ì„ ë°”íƒ•ìœ¼ë¡œí•˜ì—¬ ìžë™ìœ¼ë¡œ 빌드, 테스트 그리고 ë°°í¬ë¥¼ 수행합니다."
msgid "CICD|Automatic deployment to staging, manual deployment to production"
msgstr ""
@@ -1159,44 +1258,38 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
-msgstr ""
+msgid "CICD|Deployment strategy"
+msgstr "ë°°í¬ ì „ëžµ"
-msgid "CICD|Disable Auto DevOps"
-msgstr "CICD|Auto DevOps 비활성화"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr "ë°°í¬ ì „ëžµì´ ì˜¬ë°”ë¥´ê²Œ ìž‘ë™í•˜ë ¤ë©´ ë„ë©”ì¸ ì´ë¦„ì´ í•„ìš”í•©ë‹ˆë‹¤."
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr "CICD|Auto DevOps 활성화"
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr "CICD|프로ì íŠ¸ì— %{ci_file} 파ì¼ì´ ì •ì˜ë˜ì–´ìžˆì§€ 않다면, Auto DevOps ê°€ 활성화 í˜¹ì€ ë¹„í™œì„±í™” ë  ìˆ˜ 있ë„ë¡ ì¸ìŠ¤í„´ìŠ¤ ê¸°ë³¸ê°’ì„ ë”°ë¥´ì„¸ìš”."
-
-msgid "CICD|Instance default (%{state})"
-msgstr "CICD|ì¸ìŠ¤í„´ìŠ¤ 기본값 (%{state})"
-
msgid "CICD|Jobs"
-msgstr ""
+msgstr "ìž‘ì—…"
msgid "CICD|Learn more about Auto DevOps"
-msgstr "CICD|Auto DevOpsì— ëŒ€í•´ ë” ì•Œì•„ë³´ê¸°"
+msgstr "Auto DevOpsì— ëŒ€í•´ ë” ì•Œì•„ë³´ê¸°"
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
-msgstr "CICD|프로ì íŠ¸ ë‚´ì— %{ci_file} 파ì¼ì´ 존재하지 않으면, Auto DevOps 파ì´í”„ë¼ì¸ ì„¤ì •ì´ ì‚¬ìš©ë©ë‹ˆë‹¤."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr "ì¸ìŠ¤í„´ìŠ¤ 사용"
+
msgid "Callback URL"
-msgstr ""
+msgstr "콜백 URL"
msgid "Callback url"
-msgstr ""
+msgstr "콜백 url"
msgid "Can't find HEAD commit for this branch"
msgstr ""
@@ -1205,13 +1298,13 @@ msgid "Cancel"
msgstr "취소"
msgid "Cancel this job"
-msgstr ""
+msgstr "ì´ ìž‘ì—… 취소"
msgid "Cannot be merged automatically"
msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
-msgstr ""
+msgstr "ì´ë¯¸ êµ¬ì„±ëœ Kubernetes í´ëŸ¬ìŠ¤í„°ëŠ” 수정할 수 없습니다"
msgid "Certificate fingerprint"
msgstr ""
@@ -1219,6 +1312,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1253,10 +1349,10 @@ msgid "Check interval"
msgstr ""
msgid "Checking %{text} availability…"
-msgstr ""
+msgstr "%{text}ì´(ê°€) 사용 가능한지 í™•ì¸ ì¤‘â€¦"
msgid "Checking branch availability..."
-msgstr ""
+msgstr "브랜치가 사용 가능한지 í™•ì¸ ì¤‘..."
msgid "Cherry-pick this commit"
msgstr "ì´ ì»¤ë°‹ì„ Cherry-pick"
@@ -1271,19 +1367,25 @@ msgid "Choose <strong>Next</strong> at the bottom of the page."
msgstr ""
msgid "Choose File ..."
-msgstr ""
+msgstr "íŒŒì¼ ì„ íƒ ..."
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
-msgid "Choose any color."
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
msgstr ""
+msgid "Choose any color."
+msgstr "아무 색ìƒì„ ì„ íƒí•´ 주세요."
+
msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
msgstr ""
msgid "Choose file..."
-msgstr ""
+msgstr "íŒŒì¼ ì„ íƒâ€¦"
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -1295,7 +1397,7 @@ msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr ""
msgid "Choose which repositories you want to import."
-msgstr ""
+msgstr "가져올 저장소를 ì„ íƒí•´ì£¼ì„¸ìš”."
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
@@ -1415,7 +1517,7 @@ msgid "Click the button below to begin the install process by navigating to the
msgstr ""
msgid "Click to expand it."
-msgstr ""
+msgstr "í´ë¦­í•˜ì—¬ 확장하십시오."
msgid "Click to expand text"
msgstr "í…스트를 í´ë¦­í•˜ì—¬ 확장하세요."
@@ -1436,35 +1538,50 @@ msgid "Clone repository"
msgstr ""
msgid "Close"
+msgstr "닫기"
+
+msgid "Close epic"
msgstr ""
msgid "Closed"
-msgstr ""
+msgstr "닫힘"
msgid "Closed issues"
-msgstr ""
+msgstr "닫힌 ì´ìŠˆ"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 추가"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì— ëŒ€í•œ 고급 옵션"
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr ""
+msgstr "프로ì íŠ¸ ì˜ì—­ì„ 가져 오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤: %{error}"
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr "애플리케ì´ì…˜"
+
msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
msgstr ""
@@ -1474,10 +1591,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1504,6 +1621,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ì˜ 세부 ì •ë³´ ìž…ë ¥"
@@ -1528,6 +1651,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr "ClusterIntegration|GitLab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1540,6 +1666,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1555,6 +1684,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1579,6 +1711,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°"
@@ -1591,20 +1726,11 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 통합"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해 Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì´ ë¹„í™œì„±í™”ë˜ì–´ìžˆìŠµë‹ˆë‹¤."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해 Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì´ í™œì„±í™”ë˜ì–´ìžˆìŠµë‹ˆë‹¤."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì´ í™œì„±í™”ë˜ì–´ìžˆìŠµë‹ˆë‹¤. ì´ í†µí•©ì„ ë¹„í™œì„±í™”í•´ë„ ê¸°ì¡´ Kubernetes í´ëŸ¬ìŠ¤í„°ì— ì˜í–¥ì„ 주지는 ì•Šê³  GitLabì˜ ì—°ê²°ì„ ì¼ì‹œì ìœ¼ë¡œë§Œ 해제합니다."
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ê°€ Google Kubernetes Engineì—ì„œ ìƒì„± 중입니다..."
msgid "ClusterIntegration|Kubernetes cluster name"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° ì´ë¦„"
msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
msgstr ""
@@ -1624,12 +1750,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "í™˜ê²½ì— ëŒ€í•´ ìžì„¸ížˆ 알아보십시오."
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr "머신 타입"
@@ -1637,13 +1757,13 @@ msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to crea
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 만들려면 %{link_to_requirements} ë§í¬ì—ì„œ ê³„ì •ì˜ í•„ìˆ˜ì‚¬í•­ì„ í™•ì¸í•˜ì„¸ìš”."
msgid "ClusterIntegration|Manage"
-msgstr ""
+msgstr "관리"
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
msgid "ClusterIntegration|More information"
-msgstr ""
+msgstr "ë” ë§Žì€ ì •ë³´"
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
@@ -1652,7 +1772,7 @@ msgid "ClusterIntegration|No machine types matched your search"
msgstr ""
msgid "ClusterIntegration|No projects found"
-msgstr ""
+msgstr "프로ì íŠ¸ ì—†ìŒ"
msgid "ClusterIntegration|No projects matched your search"
msgstr ""
@@ -1672,6 +1792,9 @@ msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ì— 대한 액세스 정보를 입력하십시오
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "ë‹¹ì‹ ì˜ Google ê³„ì •ì´ ë‹¤ìŒì˜ 요구 ì‚¬í•­ì„ ì¶©ì¡±í•˜ëŠ”ì§€ í™•ì¸ í•˜ì„¸ìš”."
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1681,6 +1804,12 @@ msgstr "프로ì íŠ¸ 네임 스페ì´ìŠ¤ (optional, unique)"
msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 통합 알아보기 %{link_to_help_page}"
@@ -1693,6 +1822,9 @@ msgstr "통합 제거"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "ì´ í”„ë¡œì íŠ¸ì—ì„œ Kubernetes í´ëŸ¬ìŠ¤í„° êµ¬ì„±ì„ ì œê±°í•˜ì‹­ì‹œì˜¤. ì´ë ‡ê²Œí•´ë„ 실제 Kubernetes í´ëŸ¬ìŠ¤í„°ëŠ” ì‚­ì œë˜ì§€ 않습니다."
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "설치 시작 요청 실패"
@@ -1703,14 +1835,11 @@ msgid "ClusterIntegration|Search machine types"
msgstr ""
msgid "ClusterIntegration|Search projects"
-msgstr ""
+msgstr "ClusterIntegration|프로ì íŠ¸ 검색"
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ì˜ 세부 ì •ë³´ 보기 ë° ìˆ˜ì •"
@@ -1718,7 +1847,7 @@ msgid "ClusterIntegration|Select machine type"
msgstr ""
msgid "ClusterIntegration|Select project"
-msgstr ""
+msgstr "프로ì íŠ¸ ì„ íƒ"
msgid "ClusterIntegration|Select project and zone to choose machine type"
msgstr ""
@@ -1747,12 +1876,18 @@ msgstr "구글 Kubernetes ì—”ì§„ì— Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 만드는 ë™ì•ˆ
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "설치하는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. %{title}"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "%{link_to_container_project} 프로ì íŠ¸ì— Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 만들기 위해서는 ì´ ê³„ì •ì— ì•„ëž˜ì— ëª…ì‹œëœ ê¶Œí•œì´ í•„ìš”í•©ë‹ˆë‹¤"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 토글"
@@ -1765,9 +1900,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "ì´ í”„ë¡œì íŠ¸ì— ì—°ê²°ëœ Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 통해 어플리케ì´ì…˜ì„ 리뷰, ë°°í¬í•  수 있고, 파ì´í”„ë¼ì¸ì„ 실행할 수 있습니다. ê·¸ 외ì—ë„ ë§Žì€ ì¼ì„ 훨씬 쉬운 방법으로 수행할 수 있습니다."
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1786,9 +1927,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr "ë„ì›€ë§ íŽ˜ì´ì§€"
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr "요구 ì‚¬í•­ì„ ì¶©ì¡±"
@@ -1798,6 +1936,9 @@ msgstr "ì •ìƒì ìœ¼ë¡œ 구성ë˜ì—ˆìŒ"
msgid "ClusterIntegration|sign up"
msgstr "ClusterIntegration|가입"
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1845,6 +1986,9 @@ msgstr "커밋"
msgid "CommitMessage|Add %{file_name}"
msgstr "%{file_name} 추가"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "커밋"
@@ -1917,16 +2061,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -1936,13 +2077,13 @@ msgid "Configure the way a user creates a new account."
msgstr ""
msgid "Connect"
-msgstr ""
+msgstr "ì—°ê²°"
msgid "Connect all repositories"
-msgstr ""
+msgstr "모든 저장소 연결"
msgid "Connect repositories from GitHub"
-msgstr ""
+msgstr "GitHub으로부터 저장소 연결"
msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
msgstr ""
@@ -2016,6 +2157,9 @@ msgstr ""
msgid "Contribution guide"
msgstr "ê¸°ì—¬ì— ëŒ€í•œ 안내"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2032,7 +2176,7 @@ msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limi
msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
-msgstr ""
+msgstr "잠시만 기다려주십시오, ì´ íŽ˜ì´ì§€ëŠ” 준비가 완료ë˜ë©´ ìžë™ìœ¼ë¡œ 새로고침ë©ë‹ˆë‹¤."
msgid "Control the display of third party offers."
msgstr ""
@@ -2049,6 +2193,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2080,7 +2233,7 @@ msgid "Copy token to clipboard"
msgstr ""
msgid "Create"
-msgstr ""
+msgstr "만들기"
msgid "Create New Directory"
msgstr "새 디렉토리 만들기"
@@ -2134,7 +2287,7 @@ msgid "Create merge request and branch"
msgstr ""
msgid "Create new branch"
-msgstr ""
+msgstr "새 브랜치 ìƒì„±"
msgid "Create new directory"
msgstr "새 디렉토리 만들기"
@@ -2143,10 +2296,10 @@ msgid "Create new file"
msgstr "새 íŒŒì¼ ë§Œë“¤ê¸°"
msgid "Create new file or directory"
-msgstr ""
+msgstr "새 íŒŒì¼ ë˜ëŠ” 디렉토리 만들기"
msgid "Create new label"
-msgstr ""
+msgstr "새 ë¼ë²¨ 만들기"
msgid "Create new..."
msgstr "새로 만들기 ..."
@@ -2154,9 +2307,6 @@ msgstr "새로 만들기 ..."
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr "í¬í¬"
-
msgid "CreateTag|Tag"
msgstr "태그"
@@ -2170,6 +2320,9 @@ msgid "Created At"
msgstr ""
msgid "Created by me"
+msgstr "ë‚˜ì— ì˜í•´ ìƒì„±ë¨"
+
+msgid "Created on"
msgstr ""
msgid "Created on:"
@@ -2184,14 +2337,20 @@ msgstr "Cron 시간대"
msgid "Cron syntax"
msgstr "í¬ë¡  구문"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
msgid "CurrentUser|Profile"
-msgstr "CurrentUser|프로파ì¼"
+msgstr "프로파ì¼"
msgid "CurrentUser|Settings"
-msgstr "CurrentUser|설정"
+msgstr "설정"
+
+msgid "Custom"
+msgstr ""
msgid "Custom CI config path"
msgstr ""
@@ -2202,6 +2361,9 @@ msgstr "ì‚¬ìš©ìž ì •ì˜ ì•Œë¦¼ ì´ë²¤íŠ¸"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "ì‚¬ìš©ìž ì •ì˜ ì•Œë¦¼ ìˆ˜ì¤€ì€ ì°¸ì—¬ 수준과 ë™ì¼í•©ë‹ˆë‹¤. 맞춤 알림 ìˆ˜ì¤€ì„ ì‚¬ìš©í•˜ë©´ ì¼ë¶€ ì´ë²¤íŠ¸ì— 대한 ì•Œë¦¼ë„ ë°›ê²Œë©ë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ %{notification_link}ì„ í™•ì¸í•˜ì‹­ì‹œì˜¤."
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2214,6 +2376,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "사ì´í´ 분ì„"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "코드"
@@ -2236,7 +2401,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "테스트"
msgid "Dashboard"
-msgstr ""
+msgstr "대시보드"
msgid "DashboardProjects|All"
msgstr "모든"
@@ -2244,15 +2409,24 @@ msgstr "모든"
msgid "DashboardProjects|Personal"
msgstr "ê°œì¸"
-msgid "Dec"
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
msgstr ""
+msgid "Dec"
+msgstr "12ì›”"
+
msgid "December"
msgstr ""
msgid "Decline and sign out"
msgstr "취소 ë° ë¡œê·¸ì•„ì›ƒ"
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2268,6 +2442,9 @@ msgstr "cron êµ¬ë¬¸ì„ ì‚¬ìš©í•˜ì—¬ ì‚¬ìš©ìž ì •ì˜ íŒ¨í„´ ì •ì˜"
msgid "Delete"
msgstr "삭제 "
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2288,52 +2465,52 @@ msgid "Deploy Keys"
msgstr "ë°°í¬ í‚¤"
msgid "DeployKeys|+%{count} others"
-msgstr "DeployKeys|+%{count} 기타"
+msgstr "+%{count} 기타"
msgid "DeployKeys|Current project"
-msgstr "DeployKeys|현재 프로ì íŠ¸"
+msgstr "현재 프로ì íŠ¸"
msgid "DeployKeys|Deploy key"
-msgstr "DeployKeys|ë°°í¬ í‚¤"
+msgstr "ë°°í¬ í‚¤"
msgid "DeployKeys|Enabled deploy keys"
-msgstr "DeployKeys|í™œì„±í™”ëœ ë°°í¬ í‚¤"
+msgstr "í™œì„±í™”ëœ ë°°í¬ í‚¤"
msgid "DeployKeys|Error enabling deploy key"
-msgstr "DeployKeys|ë°°í¬í‚¤ 사용 오류"
+msgstr "ë°°í¬í‚¤ 사용 오류"
msgid "DeployKeys|Error getting deploy keys"
-msgstr "DeployKeys|ë°°í¬í‚¤ 가져오기 오류"
+msgstr "ë°°í¬í‚¤ 가져오기 오류"
msgid "DeployKeys|Error removing deploy key"
-msgstr "DeployKeys|ë°°í¬í‚¤ 제거 오류"
+msgstr "ë°°í¬í‚¤ 제거 오류"
msgid "DeployKeys|Expand %{count} other projects"
msgstr "DeployKeys|확장 %{count} 기타 프로ì íŠ¸"
msgid "DeployKeys|Loading deploy keys"
-msgstr "DeployKeys|ë°°í¬í‚¤ 로드"
+msgstr "ë°°í¬í‚¤ 로드"
msgid "DeployKeys|No deploy keys found. Create one with the form above."
-msgstr "DeployKeys|ë°°í¬í‚¤ê°€ 존재하지 않습니다. ìœ„ì˜ ì–‘ì‹ì„ ì´ìš©í•´ 만드세요."
+msgstr "ë°°í¬í‚¤ê°€ 존재하지 않습니다. ìœ„ì˜ ì–‘ì‹ì„ ì´ìš©í•´ 만드세요."
msgid "DeployKeys|Privately accessible deploy keys"
-msgstr "DeployKeys|비공개ì ìœ¼ë¡œ 엑세스 가능한 ë°°í¬í‚¤"
+msgstr "비공개ì ìœ¼ë¡œ 엑세스 가능한 ë°°í¬í‚¤"
msgid "DeployKeys|Project usage"
-msgstr "DeployKeys|프로ì íŠ¸ 사용현황"
+msgstr "프로ì íŠ¸ 사용현황"
msgid "DeployKeys|Publicly accessible deploy keys"
-msgstr "DeployKeys|공개ì ìœ¼ë¡œ 엑세스 가능한 ë°°í¬í‚¤"
+msgstr "공개ì ìœ¼ë¡œ 엑세스 가능한 ë°°í¬í‚¤"
msgid "DeployKeys|Read access only"
-msgstr "DeployKeys|ì½ê¸° 엑세스 ì „ìš©"
+msgstr "ì½ê¸° 엑세스 ì „ìš©"
msgid "DeployKeys|Write access allowed"
-msgstr "DeployKeys|쓰기 액세스 허용"
+msgstr "쓰기 액세스 허용"
msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
-msgstr "DeployKeys|ì´ ë°°í¬í‚¤ë¥¼ 제거하려고 합니다. 확실합니까?"
+msgstr "ì´ ë°°í¬í‚¤ë¥¼ 제거하려고 합니다. 확실합니까?"
msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
msgstr ""
@@ -2428,12 +2605,18 @@ msgstr ""
msgid "Details"
msgstr "ìƒì„¸"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "디렉토리 ì´ë¦„"
@@ -2446,9 +2629,21 @@ msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해 사용 중지"
msgid "Disable group Runners"
msgstr "그룹 Runner 사용 중지"
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr "변경 ì‚¬í•­ì„ ì·¨ì†Œ"
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2459,7 +2654,7 @@ msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr ""
msgid "Dismiss"
-msgstr ""
+msgstr "닫기"
msgid "Dismiss Cycle Analytics introduction box"
msgstr "사ì´í´ ë¶„ì„ ì†Œê°œ 박스 제거"
@@ -2480,7 +2675,7 @@ msgid "Don't show again"
msgstr "다시 표시하지 ì•ŠìŒ"
msgid "Done"
-msgstr ""
+msgstr "완료"
msgid "Download"
msgstr "다운로드"
@@ -2540,19 +2735,19 @@ msgid "Edit files in the editor and commit changes here"
msgstr ""
msgid "Edit group: %{group_name}"
-msgstr ""
+msgstr "그룹 편집: %{group_name}"
msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
-msgstr ""
+msgstr "Elasticsearch"
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
-msgstr ""
+msgstr "ì´ë©”ì¼"
msgid "Email patch"
msgstr ""
@@ -2564,10 +2759,10 @@ msgid "Embed"
msgstr ""
msgid "Enable"
-msgstr ""
+msgstr "사용"
msgid "Enable Auto DevOps"
-msgstr ""
+msgstr "ìžë™ DevOps 활성화"
msgid "Enable Pseudonymizer data collection"
msgstr ""
@@ -2608,12 +2803,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
-msgid "Enabled"
+msgid "Enable usage ping"
msgstr ""
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr "활성화ë¨"
+
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr "ì´ìŠˆ 설명 ìž…ë ¥"
+
+msgid "Enter the issue title"
+msgstr "ì´ìŠˆ 제목 ìž…ë ¥"
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr "환경"
@@ -2630,7 +2846,7 @@ msgid "Environments|Are you sure you want to stop this environment?"
msgstr ""
msgid "Environments|Commit"
-msgstr ""
+msgstr "커밋"
msgid "Environments|Deploy to..."
msgstr ""
@@ -2644,9 +2860,12 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Job"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
msgstr ""
+msgid "Environments|Job"
+msgstr "ìž‘ì—…"
+
msgid "Environments|Learn more about stopping environments"
msgstr ""
@@ -2659,6 +2878,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2681,10 +2903,10 @@ msgid "Environments|Show all"
msgstr ""
msgid "Environments|Stop"
-msgstr ""
+msgstr "중지"
msgid "Environments|Stop environment"
-msgstr ""
+msgstr "중지 환경"
msgid "Environments|Updated"
msgstr ""
@@ -2692,8 +2914,11 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr "보호ë¨"
+
msgid "Epic"
-msgstr ""
+msgstr "ì—픽"
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -2707,6 +2932,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr "시작"
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2731,6 +2980,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2743,6 +2995,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2755,6 +3013,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr "예ìƒ"
@@ -2786,22 +3047,25 @@ msgid "Every week (Sundays at 4:00am)"
msgstr "매주 (ì¼ìš”ì¼ ì˜¤ì „ 4ì‹œì—)"
msgid "Everyone can contribute"
-msgstr ""
+msgstr "모ë‘ê°€ 기여할 수 있ìŒ."
msgid "Expand"
-msgstr ""
+msgstr "펼치기"
msgid "Expand all"
-msgstr ""
+msgstr "ëª¨ë‘ í™•ìž¥"
msgid "Expand sidebar"
msgstr "사ì´ë“œë°” 확장"
-msgid "Explore"
+msgid "Expiration date"
msgstr ""
+msgid "Explore"
+msgstr "íƒìƒ‰"
+
msgid "Explore GitLab"
-msgstr ""
+msgstr "GitLab 둘러보기"
msgid "Explore Groups"
msgstr ""
@@ -2837,10 +3101,10 @@ msgid "ExternalAuthorizationService|When no classification label is set the defa
msgstr ""
msgid "Facebook"
-msgstr ""
+msgstr "Facebook"
msgid "Failed"
-msgstr ""
+msgstr "실패"
msgid "Failed Jobs"
msgstr ""
@@ -2854,6 +3118,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "파ì´í”„ë¼ì¸ ìŠ¤ì¼€ì¤„ì„ ì œê±°í•˜ì§€ 못했습니다."
@@ -2867,7 +3134,7 @@ msgid "Faster as it re-uses the project workspace (falling back to clone if it d
msgstr ""
msgid "Feb"
-msgstr ""
+msgstr "2ì›”"
msgid "February"
msgstr ""
@@ -2875,6 +3142,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "파ì¼"
@@ -2887,9 +3157,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "커밋 메시지로 필터"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "경로로 찾기"
@@ -2902,6 +3181,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2911,6 +3193,18 @@ msgstr "처ìŒ"
msgid "FirstPushedBy|pushed by"
msgstr "푸시한 사용ìž"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2938,16 +3232,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "í¬í¬"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "í¬í¬í•œ 사용ìž"
@@ -2958,10 +3254,10 @@ msgid "Forking in progress"
msgstr ""
msgid "Format"
-msgstr ""
+msgstr "í¬ë§·"
msgid "Found errors in your .gitlab-ci.yml:"
-msgstr ""
+msgstr ".gitlab-ci.ymlì—ì„œ 오류를 발견했습니다:"
msgid "From %{provider_title}"
msgstr ""
@@ -2969,6 +3265,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -2984,6 +3283,9 @@ msgstr "ì´ìŠˆ ìƒì„±ì—ì„œ 프로ë•ì…˜ ë°°í¬ê¹Œì§€"
msgid "From merge request merge until deploy to production"
msgstr "머지 리퀘스트(MR) 머지ì—ì„œ 프로ë•ì…˜ í™˜ê²½ì— ë°°í¬ê¹Œì§€"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 세부사항 ë³´ê¸°ì˜ ì• í”Œë¦¬ì¼€ì´ì…˜ 목ë¡ì—ì„œ Runner를 설치하십시오."
@@ -2999,6 +3301,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3164,39 +3469,138 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
-msgid "Git"
+msgid "Geo|Verification failed - %{error}"
msgstr ""
-msgid "Git repository URL"
+msgid "Geo|Waiting for scheduler"
msgstr ""
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr "Git"
+
+msgid "Git repository URL"
+msgstr "Git 저장소 URL"
+
msgid "Git revision"
msgstr "Git 리비전"
@@ -3207,10 +3611,10 @@ msgid "Git strategy for pipelines"
msgstr ""
msgid "Git version"
-msgstr ""
+msgstr "Git 버전"
msgid "GitHub import"
-msgstr ""
+msgstr "GitHub 가져오기"
msgid "GitLab CI Linter has been moved"
msgstr ""
@@ -3239,14 +3643,17 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr "GitLabì˜ ì´ìŠˆ 트래커"
+
msgid "Gitaly"
-msgstr ""
+msgstr "Gitaly"
msgid "Gitaly Servers"
msgstr "Gitaly 서버"
msgid "Gitaly|Address"
-msgstr ""
+msgstr "주소"
msgid "Gitea Host URL"
msgstr ""
@@ -3255,25 +3662,22 @@ msgid "Gitea Import"
msgstr ""
msgid "Go Back"
-msgstr ""
+msgstr "ì´ì „으로"
msgid "Go back"
-msgstr ""
+msgstr "뒤로 가기"
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "ë‹¹ì‹ ì˜ í¬í¬ë¡œ ì´ë™í•˜ì„¸ìš”"
-
-msgid "GoToYourFork|Fork"
-msgstr "í¬í¬"
+msgid "Go to %{link_to_google_takeout}."
+msgstr "%{link_to_google_takeout}ë¡œ ì´ë™í•˜ì‹­ì‹œì˜¤."
msgid "Google Code import"
-msgstr ""
+msgstr "Google Code 가져오기"
msgid "Google Takeout"
-msgstr ""
+msgstr "Google Takeout"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Google ì¸ì¦ì„ 사용할 수 없습니다. %{link_to_documentation} GitLab 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
@@ -3285,7 +3689,7 @@ msgid "Graph"
msgstr "그래프"
msgid "Group"
-msgstr ""
+msgstr "그룹"
msgid "Group CI/CD settings"
msgstr "그룹 CI/CD 설정"
@@ -3300,13 +3704,13 @@ msgid "Group Runners"
msgstr "그룹 Runner"
msgid "Group avatar"
-msgstr ""
+msgstr "그룹 아바타"
msgid "Group details"
-msgstr ""
+msgstr "그룹 세부 정보"
msgid "Group info:"
-msgstr ""
+msgstr "그룹 정보:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
@@ -3329,13 +3733,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3350,6 +3754,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "%{group} ë‚´ì˜ í”„ë¡œì íŠ¸ë¥¼ 다른 ê·¸ë£¹ì— ê³µìœ í•  수 없게 합니다."
@@ -3413,6 +3826,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3425,20 +3841,20 @@ msgstr "그룹 편집"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr "ì´ ê·¸ë£¹ 떠나기"
msgid "GroupsTree|Loading groups"
msgstr "그룹 로딩중입니다"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "죄송합니다, 검색과 ì¼ì¹˜í•˜ëŠ” ê·¸ë£¹ì´ ì—†ìŠµë‹ˆë‹¤"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "죄송합니다, 검색과 ì¼ì¹˜í•˜ëŠ” 그룹 í˜¹ì€ í”„ë¡ì íŠ¸ê°€ 없습니다"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
msgid "Have your users email"
msgstr ""
@@ -3465,7 +3881,7 @@ msgid "HealthCheck|Unhealthy"
msgstr "비정ìƒ"
msgid "Help"
-msgstr ""
+msgstr "ë„움ë§"
msgid "Help page"
msgstr ""
@@ -3473,6 +3889,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3493,23 +3918,47 @@ msgid "I accept the|Terms of Service and Privacy Policy"
msgstr ""
msgid "ID"
+msgstr "ID"
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
msgstr ""
msgid "IDE|Commit"
-msgstr "IDE|커밋"
+msgstr "커밋"
msgid "IDE|Edit"
-msgstr "IDE|편집"
+msgstr "편집"
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
-msgid "IDE|Go back"
-msgstr "IDE|뒤로"
+msgid "IDE|Live Preview"
+msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr "리뷰"
+msgid "IP Address"
+msgstr "IP 주소"
+
msgid "Identifier"
msgstr ""
@@ -3519,11 +3968,14 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
msgid "If enabled"
-msgstr ""
+msgstr "만약 활성화 ëœ ê²½ìš°"
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -3535,7 +3987,7 @@ msgid "If you already have files you can push them using the %{link_to_cli} belo
msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
-msgstr ""
+msgstr "HTTP 저장소를 공개ì ìœ¼ë¡œ 액세스할 수 없는 경우, URLì— ì¸ì¦ 정보를 추가하십시오:<code>https://username:password@gitlab.company.com/group/project.git</code>."
msgid "ImageDiffViewer|2-up"
msgstr ""
@@ -3547,19 +3999,19 @@ msgid "ImageDiffViewer|Swipe"
msgstr ""
msgid "Import"
-msgstr ""
+msgstr "가져오기"
msgid "Import Projects from Gitea"
msgstr ""
msgid "Import all compatible projects"
-msgstr ""
+msgstr "호환ë˜ëŠ” 모든 프로ì íŠ¸ 가져오기"
msgid "Import all projects"
-msgstr ""
+msgstr "모든 프로ì íŠ¸ 가져오기"
msgid "Import all repositories"
-msgstr ""
+msgstr "모든 저장소 가져오기"
msgid "Import an exported GitLab project"
msgstr ""
@@ -3571,11 +4023,14 @@ msgid "Import multiple repositories by uploading a manifest file."
msgstr ""
msgid "Import project"
-msgstr ""
+msgstr "프로ì íŠ¸ 가져오기"
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3585,6 +4040,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3603,18 +4061,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
+msgstr "호환ë˜ì§€ 않는 프로ì íŠ¸"
+
+msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3625,6 +4098,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3643,12 +4122,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "주기 패턴"
msgid "Introducing Cycle Analytics"
msgstr "Cycle Analytics 소개"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3659,10 +4147,10 @@ msgid "Issue events"
msgstr "ì´ìŠˆ ì´ë²¤íŠ¸"
msgid "IssueBoards|Board"
-msgstr ""
+msgstr "보드"
msgid "IssueBoards|Boards"
-msgstr ""
+msgstr "보드"
msgid "Issues"
msgstr "ì´ìŠˆ"
@@ -3674,38 +4162,74 @@ msgid "Issues closed"
msgstr ""
msgid "Jan"
-msgstr ""
+msgstr "1ì›”"
msgid "January"
msgstr ""
msgid "Job"
-msgstr ""
+msgstr "Job"
msgid "Job has been erased"
msgstr ""
msgid "Jobs"
+msgstr "Jobs"
+
+msgid "Job|Browse"
+msgstr "íƒìƒ‰"
+
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jul"
+msgid "Job|Download"
+msgstr "다운로드"
+
+msgid "Job|Erase job log"
+msgstr "작업 로그 지우기"
+
+msgid "Job|Job artifacts"
msgstr ""
-msgid "July"
+msgid "Job|Job has been erased"
+msgstr "ìž‘ì—…ì´ ì‚­ì œë˜ì—ˆìŠµë‹ˆë‹¤"
+
+msgid "Job|Job has been erased by"
msgstr ""
-msgid "Jun"
+msgid "Job|Keep"
msgstr ""
-msgid "June"
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
msgstr ""
-msgid "Koding"
+msgid "Job|Show complete raw"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|The artifacts were removed"
msgstr ""
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr "7ì›”"
+
+msgid "July"
+msgstr "7ì›”"
+
+msgid "Jun"
+msgstr "6ì›”"
+
+msgid "June"
+msgstr "6ì›”"
+
msgid "Kubernetes"
msgstr ""
@@ -3731,7 +4255,7 @@ msgid "Kubernetes service integration has been deprecated. %{deprecated_message_
msgstr "Kubernetes 서비스 í†µí•©ì€ ë” ì´ìƒ 사용ë˜ì§€ 않습니다. %{deprecated_message_content} 새로운 <a href=\"%{url}\"/>Kubernetes í´ëŸ¬ìŠ¤í„°</a> 페ì´ì§€"
msgid "LFS"
-msgstr ""
+msgstr "LFS"
msgid "LFSStatus|Disabled"
msgstr "Disabled"
@@ -3775,6 +4299,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "최근 %d ì¼"
@@ -3785,6 +4312,9 @@ msgstr "최근 파ì´í”„ë¼ì¸"
msgid "Last commit"
msgstr "최근 커밋"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3809,6 +4339,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3834,34 +4367,95 @@ msgid "Leave the \"File type\" and \"Delivery method\" options on their default
msgstr ""
msgid "License"
+msgstr "ë¼ì´ì„¼ìŠ¤"
+
+msgid "LicenseManagement|Approve license"
msgstr ""
-msgid "LinkedIn"
+msgid "LicenseManagement|Approve license?"
msgstr ""
-msgid "List"
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr "ë¼ì´ì„¼ìŠ¤"
+
+msgid "LicenseManagement|License Management"
+msgstr "ë¼ì´ì„¼ìŠ¤ 관리"
+
+msgid "LicenseManagement|License details"
+msgstr "ë¼ì´ì„ ìŠ¤ ì •ë³´"
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr "ë¼ì´ì„¼ìŠ¤ 제거"
+
+msgid "LicenseManagement|Remove license?"
msgstr ""
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr "ë¼ì´ì„¼ìŠ¤"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+
+msgid "LinkedIn"
+msgstr "LinkedIn"
+
+msgid "List"
+msgstr "목ë¡"
+
msgid "List Your Gitea Repositories"
msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr "실시간 미리보기"
+
msgid "Loading contribution stats for group members"
msgstr ""
msgid "Loading the GitLab IDE..."
-msgstr ""
+msgstr "GitLab IDE 로드 중..."
msgid "Loading..."
msgstr ""
msgid "Lock"
-msgstr ""
+msgstr "잠금"
msgid "Lock %{issuableDisplayName}"
msgstr "%{issuableDisplayName} 잠금"
@@ -3869,11 +4463,14 @@ msgstr "%{issuableDisplayName} 잠금"
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
msgid "Locked"
-msgstr ""
+msgstr "ìž ê¹€"
msgid "Locked Files"
msgstr ""
@@ -3896,6 +4493,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3939,7 +4539,7 @@ msgid "Map a Google Code user to a full name"
msgstr ""
msgid "Mar"
-msgstr ""
+msgstr "3ì›”"
msgid "March"
msgstr ""
@@ -3950,12 +4550,24 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
-msgid "May"
+msgid "Maximum job timeout"
msgstr ""
+msgid "May"
+msgstr "5ì›”"
+
msgid "Median"
msgstr "중앙값"
@@ -4001,18 +4613,18 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
-msgid "Merged"
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
msgstr ""
+msgid "Merged"
+msgstr "머지ë¨"
+
msgid "Messages"
msgstr "메시지"
@@ -4025,6 +4637,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4127,9 +4742,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4148,17 +4778,38 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "SSH 키 추가"
msgid "Modal|Cancel"
-msgstr ""
+msgstr "취소"
msgid "Modal|Close"
-msgstr ""
+msgstr "닫기"
msgid "Monitoring"
msgstr "모니터ë§"
@@ -4169,9 +4820,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4185,7 +4833,7 @@ msgid "Most stars"
msgstr ""
msgid "Move"
-msgstr ""
+msgstr "ì´ë™"
msgid "Move issue"
msgstr ""
@@ -4194,7 +4842,7 @@ msgid "Multiple issue boards"
msgstr ""
msgid "Name"
-msgstr ""
+msgstr "ì´ë¦„"
msgid "Name new label"
msgstr "새 ë¼ë²¨ ì´ë¦„ 지정"
@@ -4206,10 +4854,10 @@ msgid "Name:"
msgstr ""
msgid "Nav|Help"
-msgstr ""
+msgstr "ë„움ë§"
msgid "Nav|Home"
-msgstr ""
+msgstr "홈"
msgid "Nav|Sign In / Register"
msgstr ""
@@ -4220,9 +4868,12 @@ msgstr ""
msgid "Network"
msgstr ""
-msgid "New"
+msgid "Never"
msgstr ""
+msgid "New"
+msgstr "신규"
+
msgid "New Application"
msgstr ""
@@ -4273,7 +4924,7 @@ msgid "New issue"
msgstr "새 ì´ìŠˆ"
msgid "New label"
-msgstr ""
+msgstr "새 ë¼ë²¨"
msgid "New merge request"
msgstr "새 머지 리퀘스트(MR)"
@@ -4282,7 +4933,7 @@ msgid "New pipelines will cancel older, pending pipelines on the same branch"
msgstr ""
msgid "New project"
-msgstr ""
+msgstr "새 프로ì íŠ¸"
msgid "New schedule"
msgstr "새 ì¼ì •"
@@ -4291,29 +4942,38 @@ msgid "New snippet"
msgstr "새 스니펫"
msgid "New subgroup"
-msgstr ""
+msgstr "새 서브그룹"
msgid "New tag"
msgstr "새 태그 "
msgid "New..."
-msgstr ""
+msgstr "새로운..."
msgid "No"
-msgstr ""
+msgstr "아니오"
msgid "No Label"
-msgstr ""
+msgstr "ë¼ë²¨ ì—†ìŒ"
msgid "No assignee"
msgstr "ë‹´ë‹¹ìž ì—†ìŒ"
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "Gitaly Serverì— ì—°ê²°í•  수 없습니다. 로그를 확ì¸í•˜ì‹­ì‹œì˜¤!"
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr "기한 ì—†ìŒ"
@@ -4335,6 +4995,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4347,6 +5010,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4359,13 +5025,25 @@ msgstr ""
msgid "No repository"
msgstr "저장소 ì—†ìŒ"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "ì¼ì • ì—†ìŒ"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
+msgstr "ì—†ìŒ"
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
msgstr ""
msgid "Not allowed to merge"
@@ -4386,6 +5064,9 @@ msgstr "비밀 아님"
msgid "Not enough data"
msgstr "ë°ì´í„°ê°€ 충분하지 않습니다."
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4419,6 +5100,9 @@ msgstr "실패한 파ì´í”„ë¼ì¸"
msgid "NotificationEvent|Merge merge request"
msgstr "머지 리퀘스트(MR) 머지하기"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "새 ì´ìŠˆ"
@@ -4468,7 +5152,7 @@ msgid "Notifications on"
msgstr ""
msgid "Nov"
-msgstr ""
+msgstr "11ì›”"
msgid "November"
msgstr ""
@@ -4477,10 +5161,10 @@ msgid "Number of access attempts"
msgstr "ì ‘ê·¼ ì‹œë„ íšŸìˆ˜"
msgid "OK"
-msgstr ""
+msgstr "확ì¸"
msgid "Oct"
-msgstr ""
+msgstr "10ì›”"
msgid "October"
msgstr ""
@@ -4491,29 +5175,36 @@ msgstr "í•„í„°"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
-msgid "Only project members can comment."
+msgid "Only mirror protected branches"
msgstr ""
+msgid "Only project members can comment."
+msgstr "프로ì íŠ¸ 구성ì›ë§Œ ëŒ“ê¸€ì„ ë‹¬ 수 있습니다."
+
msgid "Oops, are you sure?"
msgstr ""
msgid "Open"
-msgstr ""
+msgstr "열기"
msgid "Open in Xcode"
-msgstr ""
+msgstr "Xcodeì—ì„œ 열기"
msgid "Open sidebar"
msgstr ""
@@ -4566,14 +5257,26 @@ msgstr ""
msgid "Overview"
msgstr "개요"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "소유ìž"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
msgid "Pagination|Last »"
-msgstr ""
+msgstr "마지막 »"
msgid "Pagination|Next"
msgstr ""
@@ -4599,9 +5302,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4620,6 +5329,9 @@ msgstr ""
msgid "Pipeline"
msgstr "파ì´í”„ë¼ì¸"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "파ì´í”„ë¼ì¸ ìƒíƒœ"
@@ -4707,6 +5419,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr "Runner ìºì‹œ 정리"
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr "파ì´í”„ ë¼ì¸ìœ¼ë¡œ 시작하기"
@@ -4728,6 +5443,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4776,12 +5494,6 @@ msgstr "스테ì´ì§•"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4821,6 +5533,15 @@ msgstr "환경 설정"
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4851,18 +5572,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr "ê³„ì •ì´ ì‚­ì œë  ì˜ˆì •ìž…ë‹ˆë‹¤."
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
+msgstr "사용ìžëª… 변경"
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
msgstr ""
+msgid "Profiles|Clear status"
+msgstr "ìƒíƒœ 지우기"
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4875,33 +5626,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr "ê³„ì •ì„ ì‚­ì œí•˜ë©´ 다ìŒê³¼ ê°™ì€ ì˜í–¥ì´ 있습니다:"
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr "ìž˜ëª»ëœ íŒ¨ìŠ¤ì›Œë“œ"
msgid "Profiles|Invalid username"
msgstr "ìž˜ëª»ëœ ì‚¬ìš©ìžì´ë¦„"
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "확ì¸ì„ 위해 %{confirmationValue} 를 입력하세요."
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr "ìƒíƒœëŠ” 어떤가요?"
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "ì´ ì‚¬ìš©ìžë¥¼ 삭제할 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
@@ -4911,6 +5737,18 @@ msgstr "ë‹¹ì‹ ì˜ ê³„ì •ì„ ì‚­ì œí•˜ê¸° ì „ì— ì´ ê·¸ë£¹ë“¤ì˜ ì†Œìœ ê¶Œì„ ì
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "ë‹¹ì‹ ì˜ ê³„ì •ì€ í˜„ìž¬ ë‹¤ìŒ ê·¸ë£¹ë“¤ì˜ ì†Œìœ ìžìž…니다:"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr "ë‚˜ì˜ ìƒíƒœ"
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4944,6 +5782,9 @@ msgstr "'%{project_name}'프로ì íŠ¸ê°€ 성공ì ìœ¼ë¡œ ì—…ë°ì´íŠ¸ë˜ì—ˆìŠµë‹
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr "프로ì íŠ¸ URL"
+
msgid "Project access must be granted explicitly to each user."
msgstr "프로ì íŠ¸ 액세스는 ê° ì‚¬ìš©ìžì—게 명시ì ìœ¼ë¡œ 부여ë˜ì–´ì•¼í•©ë‹ˆë‹¤."
@@ -4969,6 +5810,9 @@ msgid "Project export started. A download link will be sent by email."
msgstr "프로ì íŠ¸ 내보내기가 시작ë˜ì—ˆìŠµë‹ˆë‹¤. 다운로드 ë§í¬ëŠ” ì´ë©”ì¼ë¡œ 전송ë©ë‹ˆë‹¤."
msgid "Project name"
+msgstr "프로ì íŠ¸ ì´ë¦„"
+
+msgid "Project slug"
msgstr ""
msgid "ProjectActivityRSS|Subscribe"
@@ -4998,10 +5842,37 @@ msgstr "Never"
msgid "ProjectLifecycle|Stage"
msgstr "스테ì´ì§•"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr "ì´ ì„¤ì •ì„ ë³€ê²½í•˜ë ¤ë©´ 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
+
+msgid "ProjectSettings|Customize your project badges."
msgstr ""
msgid "ProjectSettings|Failed to protect the tag"
@@ -5010,6 +5881,9 @@ msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5049,6 +5923,9 @@ msgstr "문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5181,6 +6058,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr "Protip:"
@@ -5199,6 +6124,12 @@ msgstr "공개 - ì´ í”„ë¡œì íŠ¸ëŠ” ì–´ë–¤ ì¸ì¦ ì—†ì´ë„ 접근할 수 있ìŠ
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5233,17 +6164,30 @@ msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
msgstr ""
msgid "Readme"
-msgstr ""
+msgstr "Readme"
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr "ë“±ë¡ / 로그ì¸"
@@ -5281,7 +6225,7 @@ msgid "Remind later"
msgstr "ë‚˜ì¤‘ì— ë‹¤ì‹œ 알림"
msgid "Remove"
-msgstr ""
+msgstr "삭제"
msgid "Remove Runner"
msgstr ""
@@ -5295,6 +6239,18 @@ msgstr ""
msgid "Remove project"
msgstr "프로ì íŠ¸ ì‚­ì œ"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5304,6 +6260,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5355,12 +6359,24 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
-msgid "Resume"
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
msgstr ""
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr "재개"
+
msgid "Retry"
msgstr ""
@@ -5370,6 +6386,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] "값 표시"
@@ -5381,7 +6400,7 @@ msgid "Revert this merge request"
msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR) ë˜ëŒë¦¬ê¸°"
msgid "Review"
-msgstr ""
+msgstr "리뷰"
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -5401,9 +6420,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr "Runners"
@@ -5413,6 +6450,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5428,17 +6480,26 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr "SSH 키"
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
msgid "Save"
-msgstr ""
+msgstr "저장"
msgid "Save application"
msgstr ""
@@ -5480,7 +6541,7 @@ msgid "Scroll to top"
msgstr ""
msgid "Search"
-msgstr ""
+msgstr "검색"
msgid "Search branches"
msgstr ""
@@ -5500,12 +6561,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr "프로ì íŠ¸ 검색"
msgid "Search users"
msgstr "ì‚¬ìš©ìž ê²€ìƒ‰"
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "실패 정보를 리셋하기까지 ë‚¨ì€ ì‹œê°„(ì´ˆ)"
@@ -5515,10 +6606,13 @@ msgstr "저장공간 ì ‘ê·¼ ì‹œë„를 위해 대기할 시간 (ì´ˆ)"
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5528,11 +6622,14 @@ msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
msgstr ""
msgid "Select"
-msgstr ""
+msgstr "ì„ íƒ"
msgid "Select Archive Format"
msgstr "ì•„ì¹´ì´ë¸Œ í¬ë§· ì„ íƒ"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5566,6 +6663,12 @@ msgstr ""
msgid "Select target branch"
msgstr "ëŒ€ìƒ ë¸Œëžœì¹˜ ì„ íƒ"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5575,9 +6678,12 @@ msgstr ""
msgid "Send email"
msgstr ""
-msgid "Sep"
+msgid "Send usage data"
msgstr ""
+msgid "Sep"
+msgstr "9ì›”"
+
msgid "September"
msgstr ""
@@ -5602,6 +6708,9 @@ msgstr "%{protocol} í”„ë¡œí† ì½œì„ í†µí•´ Pull 하거나 Push하려면 계정ì—
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5614,21 +6723,24 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
-msgstr "Koding 설정"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "패스워드 설정"
msgid "Settings"
msgstr "설정"
-msgid "Setup a specific Runner automatically"
-msgstr "특정 Runner ìžë™ 설정"
-
msgid "Share"
msgstr ""
@@ -5638,6 +6750,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5711,12 +6826,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5732,13 +6853,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr "ë²„íŠ¼ì„ í† ê¸€í•˜ë˜ ì¤‘ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5792,6 +6919,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5822,6 +6952,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5852,6 +6985,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr "ì´ì „ 시작"
@@ -5882,6 +7018,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "Runner 설정 중 ë‹¤ìŒ URLì„ ì§€ì •í•˜ì„¸ìš”."
@@ -5901,7 +7040,7 @@ msgid "Stage changes"
msgstr ""
msgid "Staged"
-msgstr ""
+msgstr "스테ì´ì§€"
msgid "Staged %{type}"
msgstr ""
@@ -5924,6 +7063,9 @@ msgstr "ë³„í‘œëœ í”„ë¡œì íŠ¸"
msgid "Start a %{new_merge_request} with these changes"
msgstr "ì´ ë³€ê²½ 사항으로 %{new_merge_request} ì„ ì‹œìž‘í•˜ì‹­ì‹œì˜¤."
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr "Runner 시작!"
@@ -5937,7 +7079,7 @@ msgid "State your message to activate"
msgstr ""
msgid "Status"
-msgstr ""
+msgstr "ìƒíƒœ"
msgid "Stop impersonation"
msgstr ""
@@ -5957,6 +7099,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -5990,6 +7135,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -5997,6 +7145,9 @@ msgstr[0] ""
msgid "Tags"
msgstr "태그 "
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6052,7 +7203,7 @@ msgid "TagsPage|Sort by"
msgstr ""
msgid "TagsPage|Tags"
-msgstr ""
+msgstr "태그"
msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
msgstr "태그는 특정 지ì ì„ 중요하다고 표시하는 기능입니다."
@@ -6076,6 +7227,12 @@ msgid "Target branch"
msgstr ""
msgid "Team"
+msgstr "팀"
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
msgstr ""
msgid "Terms of Service Agreement and Privacy Policy"
@@ -6093,6 +7250,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6102,6 +7262,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "Coding Stage는 첫 번째 커밋ì—서부터 머지 리퀘스트(MR) ìƒì„±ê¹Œì§€ì˜ ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. 첫 번째 머지 리퀘스트(MR)ì„ ìƒì„±í•˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ ì—¬ê¸°ì— ì¶”ê°€ë©ë‹ˆë‹¤."
@@ -6111,6 +7274,9 @@ msgstr "해당 단계ì—ì„œ 수집 ëœ ë°ì´í„°ê°€ ì´ë²¤íŠ¸ 모ìŒì— 추가ë
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "í¬í¬ 관계가 제거ë˜ì—ˆìŠµë‹ˆë‹¤."
@@ -6138,6 +7304,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "개발 ìˆ˜ëª…ì£¼ê¸°ì˜ ë‹¨ê³„."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "ê³„íš ë‹¨ê³„ì—서는 ì´ì „ 단계ì—ì„œ 첫 번째 커밋 ì‹œê°„ì´ í‘œì‹œë©ë‹ˆë‹¤. ì´ ì‹œê°„ì€ ì²« 번째 ì»¤ë°‹ì„ ëˆ„ë¥´ë©´ ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
@@ -6165,6 +7334,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "Review 단계ì—서는 머지 리퀘스트(MR)를 작성한 후 ë¨¸ì§€í•˜ê¸°ê¹Œì§€ì˜ ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. ë°ì´í„°ëŠ” 첫 번째 머지 리퀘스트(MR)ì„ ë¨¸ì§€ í•œ í›„ì— ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
@@ -6177,6 +7349,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "Staging 단계ì—서는 MR 머지과 프로ë•ì…˜ í™˜ê²½ì— ì½”ë“œ ë°°í¬ ì‚¬ì´ì˜ ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. ë°ì´í„°ë¥¼ Production í™˜ê²½ì— ì²˜ìŒ ë°°í¬í•˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "테스트 단계ì—서는 GitLab CIê°€ 관련 머지 리퀘스트(MR)ì„ ìœ„í•´ 모든 파ì´í”„ë¼ì¸ì„ 실행하는 ë° ê±¸ë¦¬ëŠ” ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. 첫 번째 파ì´í”„ë¼ì¸ ì‹¤í–‰ì´ ì™„ë£Œë˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
@@ -6192,6 +7367,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "해당 단계ì—ì„œ 수집 í•œ ê° ë°ì´í„° ìž…ë ¥ì— ì†Œìš” ëœ ì‹œê°„"
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6201,6 +7382,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "ê°’ì€ ì¼ë ¨ì˜ 관측 ê°’ 중ì ì— 있습니다. 예를 들어, 3, 5, 9 사ì´ì˜ 중간 ê°’ì€ 5입니다. 3, 5, 7, 8 사ì´ì˜ 중간 ê°’ì€ (5 + 7) / 2 = 6입니다."
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6210,9 +7394,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "git storageì— ì ‘ê·¼í•˜ëŠ”ë° ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. "
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6249,6 +7448,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6300,9 +7511,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "ì´ ìž‘ì—…ì€ ëŒ€ê¸° ìƒíƒœì´ë©° Runnerê°€ 실행하기를 기다리고 있습니다."
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6312,6 +7544,9 @@ msgstr "즉, 빈 저장소를 만들거나 기존 저장소를 가져올 때까ì
msgid "This merge request is locked."
msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR)는 잠겨있습니다."
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6327,15 +7562,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr "ì´ ì €ìž¥ì†Œ"
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6516,7 +7766,7 @@ msgid "Time|s"
msgstr "ì´ˆ"
msgid "Tip:"
-msgstr ""
+msgstr "íŒ:"
msgid "Title"
msgstr ""
@@ -6536,12 +7786,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6569,14 +7831,14 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
msgstr ""
msgid "Todo"
-msgstr ""
+msgstr "Todo"
msgid "Todos"
msgstr ""
@@ -6584,6 +7846,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6599,6 +7864,9 @@ msgstr "토글 ìƒíƒœ: OFF"
msgid "ToggleButton|Toggle Status: ON"
msgstr "토글 ìƒíƒœ: ON"
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6626,6 +7894,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6641,16 +7918,28 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
+msgstr "잠금 해제"
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
msgstr ""
msgid "Unlocked"
@@ -6659,6 +7948,9 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6695,9 +7987,15 @@ msgstr "최신 ìƒíƒœìž…니다"
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6722,15 +8020,15 @@ msgstr "새 íŒŒì¼ ì—…ë¡œë“œ"
msgid "Upload file"
msgstr "íŒŒì¼ ì—…ë¡œë“œ"
-msgid "Upload new avatar"
-msgstr "새 아바타 업로드"
-
msgid "UploadLink|click to upload"
msgstr "업로드하려면 í´ë¦­í•˜ì‹­ì‹œì˜¤."
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6746,6 +8044,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "설정 ì¤‘ì— ë‹¤ìŒ ë“±ë¡ í† í° ì´ìš© : "
@@ -6755,6 +8056,9 @@ msgstr "전체 알림 설정 사용"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6788,6 +8092,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7023,7 +8330,7 @@ msgid "Withdraw Access Request"
msgstr "액세스 요청 철회"
msgid "Yes"
-msgstr ""
+msgstr "예"
msgid "Yes, add it"
msgstr ""
@@ -7079,18 +8386,21 @@ msgstr "ë¸Œëžœì¹˜ì— ìžˆì„ ë•Œì—만 파ì¼ì„ 추가 í•  수 있습니다."
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr "ì½ê¸° ì „ìš© GitLab ì¸ìŠ¤í„´ìŠ¤ì—는 쓰기가 불가능합니다."
-msgid "You do not have any assigned merge requests"
-msgstr ""
-
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
@@ -7103,9 +8413,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr "프로ì íŠ¸ ìˆ«ìž í•œë„ì— ë„달했습니다."
@@ -7115,9 +8422,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr "프로ì íŠ¸ì— 별표를 표시하려면 ë¡œê·¸ì¸ í•´ì•¼ 합니다."
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7127,6 +8431,12 @@ msgstr ""
msgid "You need permission."
msgstr "ê¶Œí•œì´ í•„ìš”í•©ë‹ˆë‹¤."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "ì´ë©”ì¼ë¡œ ì•Œë¦¼ì„ ë°›ì§€ 않습니다."
@@ -7205,16 +8515,15 @@ msgstr "ê·€í•˜ì˜ ì´ë¦„"
msgid "Your projects"
msgstr ""
-msgid "ago"
+msgid "a deleted user"
msgstr ""
+msgid "ago"
+msgstr "ì „"
+
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-
msgid "assign yourself"
msgstr "ìžì‹ ì„ 담당ìžë¡œ 지정"
@@ -7222,7 +8531,7 @@ msgid "branch name"
msgstr ""
msgid "by"
-msgstr ""
+msgstr "by"
msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
msgstr ""
@@ -7239,70 +8548,86 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7335,10 +8660,15 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7368,13 +8698,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
-msgstr ""
-
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7383,9 +8710,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7416,12 +8740,13 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7434,6 +8759,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7450,17 +8781,6 @@ msgstr[0] "ì¼"
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7479,12 +8799,19 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7494,9 +8821,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7505,16 +8838,16 @@ msgid_plural "merge requests"
msgstr[0] ""
msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
-msgstr ""
+msgstr "브랜치를 ë³µì›í•˜ê±°ë‚˜ 다른 %{missingBranchName} 브랜치를 사용해주세요."
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} 메모리 사용률 %{metricsLinkEnd} ì´ %{memoryFrom}MB ì—ì„œ %{memoryTo}MB ë¡œ %{emphasisStart} ê°ì†Œí•˜ì˜€ìŠµë‹ˆë‹¤. %{emphasisEnd}"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} 메모리 사용률 %{metricsLinkEnd} ì´ %{memoryFrom}MB ì—ì„œ %{memoryTo}MB ë¡œ %{emphasisStart} ì¦ê°€í•˜ì˜€ìŠµë‹ˆë‹¤. %{emphasisEnd}"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} 메모리 사용률 %{metricsLinkEnd} ì´ %{memoryFrom}MB ì—ì„œ %{emphasisStart} 변하지 않았습니다. %{emphasisEnd}"
msgid "mrWidget|Add approval"
msgstr ""
@@ -7525,6 +8858,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7535,64 +8871,67 @@ msgid "mrWidget|Approved by"
msgstr ""
msgid "mrWidget|Cancel automatic merge"
-msgstr ""
+msgstr "ìžë™ 머지 취소"
msgid "mrWidget|Check out branch"
-msgstr ""
+msgstr "브랜치 ì²´í¬ì•„웃"
msgid "mrWidget|Checking ability to merge automatically"
-msgstr ""
+msgstr "ìžë™ 머지 기능 ì²´í¬"
msgid "mrWidget|Cherry-pick"
-msgstr ""
+msgstr "체리픽"
msgid "mrWidget|Cherry-pick this merge request in a new merge request"
-msgstr ""
+msgstr "새로운 머지 리퀘스트(MR)ì—ì„œ ì´ ë¨¸ì§€ 리퀘스트(MR)으로 체리픽"
msgid "mrWidget|Closed"
-msgstr ""
+msgstr "닫힘"
msgid "mrWidget|Closed by"
-msgstr ""
+msgstr "ë‹«ìŒ:"
msgid "mrWidget|Closes"
-msgstr ""
+msgstr "ë‹«ìŒ"
msgid "mrWidget|Create an issue to resolve them later"
-msgstr ""
+msgstr "ë‚˜ì¤‘ì— í•´ê²°í•  수 있ë„ë¡ ì´ìŠˆë¥¼ 만듭니다"
msgid "mrWidget|Deployment statistics are not available currently"
-msgstr ""
+msgstr "ë°°í¬ í†µê³„ëŠ” ì•„ì§ ì‚¬ìš©í•  수 없습니다."
msgid "mrWidget|Did not close"
-msgstr ""
+msgstr "닫히지 ì•ŠìŒ"
msgid "mrWidget|Email patches"
-msgstr ""
+msgstr "ì´ë©”ì¼ íŒ¨ì¹˜"
msgid "mrWidget|Failed to load deployment statistics"
+msgstr "ë°°í¬ í†µê³„ë¥¼ ë¡œë“œí•˜ëŠ”ë° ì‹¤íŒ¨í•˜ì˜€ìŠµë‹ˆë‹¤."
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
msgstr ""
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
-msgstr "mrWidget %{branch} 브랜치가 로컬 ì €ìž¥ì†Œì— ìžˆìœ¼ë©´ ì´ ë¨¸ì§€ 리퀘스트(MR)를 다ìŒê³¼ ê°™ì´ ìˆ˜ë™ìœ¼ë¡œ 머지할 수 있습니다."
+msgstr ""
msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
-msgstr ""
+msgstr "만약 %{missingBranchName} 브랜치가 ë‹¹ì‹ ì˜ ë¡œì»¬ ì €ìž¥ì†Œì— ì¡´ìž¬í•œë‹¤ë©´, ëª…ë ¹ì¤„ì„ ì‚¬ìš©í•˜ì—¬ 수ë™ìœ¼ë¡œ 머지할 수 있습니다."
msgid "mrWidget|Loading deployment statistics"
-msgstr ""
+msgstr "ë°°í¬ í†µê³„ 로딩중"
msgid "mrWidget|Mentions"
-msgstr ""
+msgstr "멘션"
msgid "mrWidget|Merge"
-msgstr ""
+msgstr "머지"
msgid "mrWidget|Merge failed."
-msgstr ""
+msgstr "머지 실패."
msgid "mrWidget|Merge locally"
-msgstr ""
+msgstr "로컬ì—ì„œ 머지"
msgid "mrWidget|Merge request approved"
msgstr ""
@@ -7601,7 +8940,7 @@ msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
msgid "mrWidget|Merged by"
-msgstr ""
+msgstr "머지:"
msgid "mrWidget|No Approval required"
msgstr ""
@@ -7612,94 +8951,126 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
+msgstr "Plain diff"
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
msgstr ""
msgid "mrWidget|Refresh"
-msgstr ""
+msgstr "새로고침"
msgid "mrWidget|Refresh now"
-msgstr ""
+msgstr "지금 새로고침"
msgid "mrWidget|Refreshing now"
-msgstr ""
+msgstr "지금 새로고침 중"
msgid "mrWidget|Remove Source Branch"
-msgstr ""
+msgstr "소스 브랜치 삭제"
msgid "mrWidget|Remove source branch"
-msgstr ""
+msgstr "소스 브랜치 삭제"
msgid "mrWidget|Remove your approval"
msgstr ""
msgid "mrWidget|Request to merge"
-msgstr ""
+msgstr "머지 리퀘스트(MR)"
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
msgid "mrWidget|Resolve conflicts"
+msgstr "ì¶©ëŒ í•´ê²°"
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
msgstr ""
msgid "mrWidget|Revert"
-msgstr ""
+msgstr "ë˜ëŒë¦¬ê¸°"
msgid "mrWidget|Revert this merge request in a new merge request"
-msgstr ""
+msgstr "새로운 머지 리퀘스트(MR)ì—ì„œ ì´ ë¨¸ì§€ 리퀘스트(MR)ë¡œ ë˜ëŒë¦¬ê¸°"
msgid "mrWidget|Set by"
-msgstr ""
+msgstr "설정:"
msgid "mrWidget|The changes were merged into"
-msgstr ""
+msgstr "변경 ì‚¬í•­ì´ ë¨¸ì§€ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "mrWidget|The changes were not merged into"
-msgstr ""
+msgstr "변경 ì‚¬í•­ì´ ë¨¸ì§€ë˜ì§€ 않았습니다."
msgid "mrWidget|The changes will be merged into"
+msgstr "변경 ì‚¬í•­ì´ ë¨¸ì§€ë  ê²ƒ 입니다."
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
msgstr ""
msgid "mrWidget|The source branch has been removed"
+msgstr "소스 브랜치가 제거ë˜ì—ˆìŠµë‹ˆë‹¤."
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
msgstr ""
msgid "mrWidget|The source branch is being removed"
-msgstr ""
+msgstr "소스 브랜치가 제거ë˜ëŠ” 중입니다"
msgid "mrWidget|The source branch will be removed"
-msgstr ""
+msgstr "소스 브랜치가 ì œê±°ë  ê²ƒ 입니다."
msgid "mrWidget|The source branch will not be removed"
-msgstr ""
+msgstr "소스 브랜치가 제거ë˜ì§€ ì•Šì„ ê²ƒ 입니다."
msgid "mrWidget|There are merge conflicts"
-msgstr ""
+msgstr "머지 충ëŒì´ 있습니다."
msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
-msgstr ""
+msgstr "í•´ê²°ë˜ì§€ ì•Šì€ í† ë¡ ì´ ìžˆìŠµë‹ˆë‹¤. ì´ í† ë¡ ì„ í•´ê²°í•˜ì‹­ì‹œì˜¤"
msgid "mrWidget|This merge request failed to be merged automatically"
-msgstr ""
+msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR)를 ìžë™ìœ¼ë¡œ ë¨¸ì§€í•˜ëŠ”ë° ì‹¤íŒ¨í•˜ì˜€ìŠµë‹ˆë‹¤."
msgid "mrWidget|This merge request is in the process of being merged"
-msgstr ""
+msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR)를 머지중입니다."
msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr "ì´ í”„ë¡œì íŠ¸ëŠ” ë³´ê´€ë˜ì—ˆê³ , 쓰기 ì ‘ê·¼ì´ ë¹„í™œì„±í™”ë˜ì—ˆìŠµë‹ˆë‹¤."
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
msgstr ""
msgid "mrWidget|You can merge this merge request manually using the"
-msgstr ""
+msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR)를 수ë™ìœ¼ë¡œ 머지할 수 있습니다."
msgid "mrWidget|You can remove source branch now"
-msgstr ""
+msgstr "지금 소스 브랜치를 삭제할 수 있습니다."
msgid "mrWidget|branch does not exist."
-msgstr ""
+msgstr "브랜치가 존재하지 않습니다."
msgid "mrWidget|command line"
-msgstr ""
+msgstr "명령줄"
msgid "mrWidget|into"
-msgstr ""
+msgstr "로"
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr "파ì´í”„ë¼ì¸ì´ 성공하면 ìžë™ìœ¼ë¡œ 머지ë©ë‹ˆë‹¤."
+
+msgid "n/a"
msgstr ""
msgid "new merge request"
@@ -7711,6 +9082,10 @@ msgstr "알림 ì´ë©”ì¼"
msgid "or"
msgstr "ë˜ëŠ”"
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "부모"
@@ -7727,6 +9102,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7748,6 +9126,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr "사용ìžëª…"
diff --git a/locale/mn_MN/gitlab.po b/locale/mn_MN/gitlab.po
new file mode 100644
index 00000000000..cf2c7224171
--- /dev/null
+++ b/locale/mn_MN/gitlab.po
@@ -0,0 +1,9208 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Mongolian\n"
+"Language: mn_MN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: mn\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 09:28\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/nb_NO/gitlab.po b/locale/nb_NO/gitlab.po
new file mode 100644
index 00000000000..df490378e9c
--- /dev/null
+++ b/locale/nb_NO/gitlab.po
@@ -0,0 +1,9208 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Norwegian Bokmal\n"
+"Language: nb_NO\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: nb\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 09:27\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po
index 3e1250a3265..8f9c3161a26 100644
--- a/locale/nl_NL/gitlab.po
+++ b/locale/nl_NL/gitlab.po
@@ -13,10 +13,13 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: nl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 09:30\n"
+
+msgid " Status"
+msgstr " Status"
msgid " and"
-msgstr ""
+msgstr " en"
msgid " degraded on %d point"
msgid_plural " degraded on %d points"
@@ -40,53 +43,58 @@ msgstr[1] "%d commits"
msgid "%d commit behind"
msgid_plural "%d commits behind"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d commit achterlopend"
+msgstr[1] "%d commits achterlopend"
msgid "%d exporter"
msgid_plural "%d exporters"
+msgstr[0] "%d exporter"
+msgstr[1] "%d exporters"
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
msgstr[0] ""
msgstr[1] ""
-msgid "%d issue"
-msgid_plural "%d issues"
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
msgstr[0] ""
msgstr[1] ""
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] "%d probleem"
+msgstr[1] "%d problemen"
+
msgid "%d layer"
msgid_plural "%d layers"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d laag"
+msgstr[1] "%d lagen"
msgid "%d merge request"
msgid_plural "%d merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d merge request"
+msgstr[1] "%d merge requests"
msgid "%d metric"
msgid_plural "%d metrics"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d metriek"
+msgstr[1] "%d statistieken"
msgid "%d staged change"
msgid_plural "%d staged changes"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d bepaalde wijziging"
+msgstr[1] "%d bepaalde wijzigingen"
msgid "%d unstaged change"
msgid_plural "%d unstaged changes"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d onbepaalde wijziging"
+msgstr[1] "%d onbepaalde wijzigingen"
msgid "%d vulnerability"
msgid_plural "%d vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d veiligheidsprobleem"
+msgstr[1] "%d veiligheidsproblemen"
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
@@ -94,10 +102,10 @@ msgstr[0] "%s andere commit is weggelaten om prestatieproblemen te voorkomen."
msgstr[1] "%s andere commits zijn weggelaten om prestatieproblemen te voorkomen."
msgid "%{actionText} & %{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
-msgstr ""
+msgstr "%{commit_author_link} schreef %{commit_timeago}"
msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
msgstr ""
@@ -108,19 +116,25 @@ msgstr[0] "%{count} deelnemer"
msgstr[1] "%{count} deelnemers"
msgid "%{filePath} deleted"
+msgstr "%{filePath} verwijderd"
+
+msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
-msgid "%{loadingIcon} Started"
+msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{loadingIcon} Started"
+msgstr "%{loadingIcon} Gestart"
+
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
-msgstr ""
+msgstr "%{lock_path} is vergrendeld door GitLab Gebruiker %{lock_user_id}"
msgid "%{name}'s avatar"
-msgstr ""
+msgstr "%{name};'s avatar"
msgid "%{nip_domain} can be used as an alternative to a custom domain."
msgstr "%{nip_domain} kan worden gebruikt als een alternatief voor een eigen domein."
@@ -129,16 +143,16 @@ msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commi
msgstr "%{number_commits_behind} commits achter %{default_branch}, %{number_commits_ahead} commits voor"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
-msgstr ""
+msgstr "%{number_of_failures} van %{maximum_failures} mislukte pogingen. GitLab zal toegang verlenen bij de volgende poging."
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr ""
msgid "%{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{openOrClose} %{noteable}"
msgid "%{percent}%% complete"
-msgstr ""
+msgstr "%{percent}%% compleet"
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
@@ -151,76 +165,92 @@ msgstr[0] ""
msgstr[1] ""
msgid "%{text} is available"
-msgstr ""
+msgstr "%{text} is beschikbaar"
msgid "%{title} changes"
+msgstr "%{title} wijzigingen"
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
-msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgid "+ %{count} more"
msgstr ""
msgid "+ %{moreCount} more"
-msgstr ""
+msgstr "+ %{moreCount} meer"
msgid "- Runner is active and can process any new jobs"
-msgstr ""
+msgstr "- Runner is actief en kan nieuwe taken verwerken"
msgid "- Runner is paused and will not receive any new jobs"
-msgstr ""
+msgstr "- Runner is onderbroken en zal geen nieuwe taken ontvangen"
msgid "- show less"
-msgstr ""
+msgstr "- toon minder"
msgid "1 %{type} addition"
msgid_plural "%{count} %{type} additions"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 %{type} toevoeging"
+msgstr[1] "%{count} %{type} toevoegingen"
msgid "1 %{type} modification"
msgid_plural "%{count} %{type} modifications"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 %{type} aanpassing"
+msgstr[1] "%{count} %{type} aanpassingen"
msgid "1 closed issue"
msgid_plural "%d closed issues"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 gesloten issue"
+msgstr[1] "%d gesloten issues"
msgid "1 closed merge request"
msgid_plural "%d closed merge requests"
+msgstr[0] "1 gesloten merge request"
+msgstr[1] "%d gesloten merge requests"
+
+msgid "1 group"
+msgid_plural "%d groups"
msgstr[0] ""
msgstr[1] ""
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 samengevoegde merge request"
+msgstr[1] "%d samengevoegde merge requests"
msgid "1 open issue"
msgid_plural "%d open issues"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 open issue"
+msgstr[1] "%d open issues"
msgid "1 open merge request"
msgid_plural "%d open merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 open merge request"
+msgstr[1] "%d open merge requests"
msgid "1 pipeline"
msgid_plural "%d pipelines"
+msgstr[0] "1 pipeline"
+msgstr[1] "%d pipelines"
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
msgstr[0] ""
msgstr[1] ""
msgid "1st contribution!"
-msgstr ""
+msgstr "1e bijdrage!"
msgid "2FA enabled"
-msgstr ""
+msgstr "2FA ingeschakeld"
msgid "403|Please contact your GitLab administrator to get the permission."
msgstr ""
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -283,25 +322,25 @@ msgid "A user with write access to the source branch selected this option"
msgstr ""
msgid "About GitLab"
-msgstr ""
+msgstr "Over GitLab"
msgid "About GitLab CE"
-msgstr ""
+msgstr "Over GitLab CE"
msgid "About auto deploy"
msgstr "Over auto deploy"
msgid "About this feature"
-msgstr ""
+msgstr "Over deze functie"
msgid "Abuse Reports"
msgstr "Misbruik rapporten"
msgid "Abuse reports"
-msgstr ""
+msgstr "Misbruik rapporten"
msgid "Accept terms"
-msgstr ""
+msgstr "Voorwaarden accepteren"
msgid "Accepted MR"
msgstr ""
@@ -312,20 +351,23 @@ msgstr "Toegangstokens"
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
msgstr "Account"
msgid "Account and limit"
-msgstr ""
+msgstr "Account en limiet"
msgid "Active"
msgstr "Actief"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr "Licentie toevoegen"
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -388,6 +433,12 @@ msgid "Admin Overview"
msgstr ""
msgid "Admin area"
+msgstr "Admin omgeving"
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
msgstr ""
msgid "AdminArea|Stop all jobs"
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -514,7 +574,7 @@ msgid "An error occured whilst loading the file."
msgstr ""
msgid "An error occured whilst loading the merge request changes."
-msgstr ""
+msgstr "Er is een fout opgetreden tijdens het laden van de merge request wijzigingen."
msgid "An error occured whilst loading the merge request version data."
msgstr ""
@@ -552,13 +612,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -628,7 +700,7 @@ msgid "Appearance"
msgstr "Uiterlijk"
msgid "Application"
-msgstr ""
+msgstr "Applicatie"
msgid "Application Id"
msgstr ""
@@ -648,12 +720,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr ""
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr "Auteur"
@@ -798,6 +882,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr "Door bestanden bladeren"
msgid "Browse files"
msgstr "Door bestanden bladeren"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1502,10 +1622,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1814,9 +1958,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr "Commit"
msgid "CommitMessage|Add %{file_name}"
msgstr "%{file_name} toevoegen"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "Commits"
@@ -1947,16 +2094,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr ""
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr ""
-
msgid "CreateTag|Tag"
msgstr ""
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "Code"
@@ -2274,6 +2442,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2283,6 +2457,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2690,6 +2912,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -2906,6 +3176,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,13 +3701,10 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
-msgid "Go to your fork"
+msgid "Go to"
msgstr ""
-msgid "GoToYourFork|Fork"
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Google Code import"
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3445,6 +3860,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3457,19 +3875,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,22 +4212,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
msgstr ""
-msgid "Koding"
+msgid "Jun"
msgstr ""
-msgid "Koding Dashboard"
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3820,6 +4349,9 @@ msgstr ""
msgid "Last commit"
msgstr ""
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr ""
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr ""
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr ""
@@ -4527,18 +5214,26 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr ""
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4911,33 +5666,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4947,6 +5777,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -5007,6 +5852,9 @@ msgstr ""
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -5034,18 +5882,48 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5552,10 +6648,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr ""
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5639,6 +6750,9 @@ msgstr ""
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,19 +6765,22 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
-msgid "SetPasswordToCloneLink|set a password"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
-msgid "Settings"
+msgid "SetPasswordToCloneLink|set a password"
msgstr ""
-msgid "Setup a specific Runner automatically"
+msgid "Settings"
msgstr ""
msgid "Share"
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5962,6 +7106,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
@@ -6150,6 +7318,9 @@ msgstr ""
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr ""
msgid "Upload file"
msgstr ""
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr ""
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6796,6 +8102,9 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr ""
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr ""
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr ""
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr ""
@@ -7246,17 +8561,15 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
-msgstr ""
-
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] ""
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr ""
@@ -7757,6 +9140,11 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po
index 67c1abe6bf7..ebe5ee77d71 100644
--- a/locale/pl_PL/gitlab.po
+++ b/locale/pl_PL/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: pl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:39\n"
+"PO-Revision-Date: 2018-10-02 09:27\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -60,6 +63,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -88,13 +105,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -142,9 +152,15 @@ msgstr[3] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -192,16 +208,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -242,6 +257,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -270,6 +292,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1st contribution!"
msgstr ""
@@ -303,6 +339,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -318,12 +357,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -366,13 +411,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -405,15 +453,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr ""
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -432,6 +480,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -444,6 +495,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -462,6 +519,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -510,6 +570,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -537,6 +600,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -555,7 +621,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -606,13 +672,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -702,12 +780,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -726,13 +810,16 @@ msgstr ""
msgid "Are you sure?"
msgstr ""
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -765,7 +852,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -792,6 +879,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -852,6 +942,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -861,6 +954,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -885,9 +981,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -927,6 +1020,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -954,9 +1050,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -1032,6 +1134,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1204,6 +1309,9 @@ msgstr ""
msgid "Browse files"
msgstr ""
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1216,6 +1324,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1228,9 +1339,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1243,39 +1351,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1303,6 +1405,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1360,6 +1465,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1522,6 +1633,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1531,6 +1645,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1540,12 +1657,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1558,10 +1684,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1588,6 +1714,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1612,6 +1744,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1624,6 +1759,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1639,6 +1777,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1663,6 +1804,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1675,15 +1819,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1708,12 +1843,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1756,6 +1885,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1765,6 +1897,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1777,6 +1915,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1792,9 +1933,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1831,12 +1969,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1849,9 +1993,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1870,9 +2020,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1882,6 +2029,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1935,6 +2085,9 @@ msgstr ""
msgid "CommitMessage|Add %{file_name}"
msgstr ""
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr ""
@@ -2007,16 +2160,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2106,6 +2256,9 @@ msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2139,6 +2292,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2244,9 +2406,6 @@ msgstr ""
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr ""
-
msgid "CreateTag|Tag"
msgstr ""
@@ -2262,6 +2421,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2274,6 +2436,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2283,6 +2448,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2292,6 +2460,9 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2304,6 +2475,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -2334,6 +2508,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2343,6 +2523,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2358,6 +2541,9 @@ msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2521,12 +2707,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2539,9 +2731,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2641,7 +2845,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2701,12 +2905,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2737,6 +2962,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2752,6 +2980,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2785,6 +3016,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2800,6 +3034,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2824,6 +3082,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2836,6 +3097,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2848,6 +3115,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2890,6 +3160,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2947,6 +3220,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -2968,6 +3244,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -2980,9 +3259,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -2995,6 +3283,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -3004,6 +3295,18 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -3031,19 +3334,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -3065,6 +3367,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3080,6 +3385,9 @@ msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3095,6 +3403,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3260,33 +3571,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3335,6 +3745,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3356,13 +3769,10 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
-msgid "Go to your fork"
+msgid "Go to"
msgstr ""
-msgid "GoToYourFork|Fork"
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Google Code import"
@@ -3425,13 +3835,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3446,6 +3856,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3509,6 +3928,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3521,19 +3943,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3569,6 +3991,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3594,21 +4025,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3618,6 +4073,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3675,6 +4133,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3684,6 +4145,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3702,18 +4166,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3727,6 +4206,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3745,12 +4230,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3790,22 +4284,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
msgstr ""
-msgid "Koding"
+msgid "Job|Job artifacts"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3877,6 +4407,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3890,6 +4423,9 @@ msgstr ""
msgid "Last commit"
msgstr ""
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3914,6 +4450,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3941,6 +4480,64 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3953,9 +4550,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3974,6 +4577,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -4001,6 +4607,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -4055,9 +4664,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4106,15 +4727,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4130,6 +4751,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4232,9 +4856,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4253,9 +4892,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -4274,9 +4934,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4325,6 +4982,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4416,12 +5076,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4443,6 +5112,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4455,6 +5127,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4467,15 +5142,27 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr ""
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4494,6 +5181,9 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4527,6 +5217,9 @@ msgstr ""
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr ""
@@ -4599,18 +5292,28 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4674,9 +5377,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4707,9 +5422,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4728,6 +5449,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -4815,6 +5539,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4836,6 +5563,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4884,12 +5614,6 @@ msgstr ""
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4929,6 +5653,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4959,18 +5692,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4983,33 +5746,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -5019,6 +5857,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -5052,6 +5902,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -5079,6 +5932,9 @@ msgstr ""
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -5106,18 +5962,48 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5157,6 +6043,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5289,6 +6178,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5307,6 +6244,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5346,12 +6289,28 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5403,6 +6362,18 @@ msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5412,6 +6383,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5463,9 +6482,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5478,6 +6509,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5512,9 +6546,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5524,6 +6576,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5539,12 +6606,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5611,12 +6687,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5626,10 +6732,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5644,6 +6753,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr ""
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5677,6 +6789,12 @@ msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5686,6 +6804,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5713,6 +6834,9 @@ msgstr ""
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5725,19 +6849,22 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
-msgid "SetPasswordToCloneLink|set a password"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
-msgid "Settings"
+msgid "SetPasswordToCloneLink|set a password"
msgstr ""
-msgid "Setup a specific Runner automatically"
+msgid "Settings"
msgstr ""
msgid "Share"
@@ -5749,6 +6876,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5825,12 +6955,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5846,13 +6982,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5906,6 +7048,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5936,6 +7081,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5966,6 +7114,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5996,6 +7147,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -6038,6 +7192,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -6071,6 +7228,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6104,6 +7264,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6114,6 +7277,9 @@ msgstr[3] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6195,6 +7361,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6210,6 +7382,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6219,6 +7394,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
@@ -6228,6 +7406,9 @@ msgstr ""
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
@@ -6255,6 +7436,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -6282,6 +7466,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -6294,6 +7481,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -6309,6 +7499,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6318,6 +7514,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6327,9 +7526,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6366,6 +7580,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6417,9 +7643,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6429,6 +7676,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6444,15 +7694,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6659,12 +7924,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6692,7 +7969,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6707,6 +7984,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6722,6 +8002,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6749,6 +8032,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6764,24 +8056,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6818,9 +8125,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6845,15 +8158,15 @@ msgstr ""
msgid "Upload file"
msgstr ""
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr ""
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6869,6 +8182,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6878,6 +8194,9 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6911,6 +8230,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7202,16 +8524,19 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7226,9 +8551,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr ""
@@ -7238,9 +8560,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr ""
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7250,6 +8569,12 @@ msgstr ""
msgid "You need permission."
msgstr ""
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr ""
@@ -7328,19 +8653,15 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "assign yourself"
msgstr ""
@@ -7365,70 +8686,98 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7461,10 +8810,21 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7494,13 +8854,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
-msgstr ""
-
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7509,9 +8866,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7542,12 +8896,16 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7560,6 +8918,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7579,23 +8943,6 @@ msgstr[3] ""
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7614,12 +8961,22 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7629,9 +8986,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7663,6 +9026,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7711,6 +9077,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7750,9 +9119,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7774,9 +9149,26 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7795,9 +9187,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7822,6 +9223,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7840,6 +9244,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr ""
@@ -7849,6 +9256,13 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -7868,6 +9282,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7889,6 +9306,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 6d371f8b818..39aa1256e4b 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: pt-BR\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:39\n"
+"PO-Revision-Date: 2018-10-02 12:08\n"
+
+msgid " Status"
+msgstr " Status"
msgid " and"
msgstr " e"
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] "%d exporter"
msgstr[1] "%d exporters"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] "%d resultado do teste com falha"
+msgstr[1] "%d resultados do teste com falha"
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] "%d resultado do teste corrigido"
+msgstr[1] "%d resultados do teste corrigidos"
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d issue"
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] "%d métrica"
msgstr[1] "%d métricas"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] "%d nova licença"
-msgstr[1] "%d novas licenças"
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] "%d mudança na lista de commit"
@@ -97,7 +105,7 @@ msgid "%{actionText} & %{openOrClose} %{noteable}"
msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
-msgstr "%{commit_author_link} fez commit à %{commit_timeago}"
+msgstr "%{commit_author_link} fez commit %{commit_timeago}"
msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
msgstr "%{counter_storage} (%{counter_repositories} repositórios, %{counter_build_artifacts} artefatos de build, %{counter_lfs_objects} LFS)"
@@ -110,9 +118,15 @@ msgstr[1] "%{count} participantes"
msgid "%{filePath} deleted"
msgstr "%{filePath} excluído"
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr "%{group_docs_link_start}Grupos%{group_docs_link_end} permitem que você gerencie e colabore em vários projetos. Os membros de um grupo têm acesso a todos os seus projetos."
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} Iniciado"
@@ -156,14 +170,15 @@ msgstr "%{text} está disponível"
msgid "%{title} changes"
msgstr "Alterações de %{title}"
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] "%{type} detectou 1 vulnerabilidade"
-msgstr[1] "%{type} detectou %{vulnerabilityCount} vulnerabilidades"
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "%{unstaged} mudanças fora e %{staged} mudanças dentro da lista de commit"
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr "%{moreCount} mais"
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] "1 merge request fechado"
msgstr[1] "%d merge requests fechados"
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] "1 grupo"
+msgstr[1] "%d grupos"
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] "1 merge request com merge"
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] "1 pipeline"
msgstr[1] "%d pipelines"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] "1 usuário"
+msgstr[1] "%d usuários"
+
msgid "1st contribution!"
msgstr "1ª contribuição!"
@@ -241,12 +271,15 @@ msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a h
msgstr "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> adicionará \"Por <a href=\"#\">@johnsmith</a>\" a todas as issues e comentários originalmente criados por johnsmith@example.com e definirá <a href=\"#\">@johnsmith</a> como o responsável em todas as issues originalmente atribuídas a johnsmith@example.com."
msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"John Smith\"</code> adicionará \"Por John Smith\" a todas as issues e comentários originalmente criados por johnsmith@example.com."
msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> adicionará \"Por johnsm...@example.com\" a todas as issues e comentários originalmente criados por johnsmith@example.com. O endereço de e-mail ou nome de usuário é mascarado para garantir a privacidade do usuário."
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> adicionará \"Por <a href=\"#\">johnsmith@example.com</a>\" a todas as issues e comentários originalmente criados por johnsmith@example.com. Por padrão, o endereço de e-mail ou nome de usuário é mascarado para garantir a privacidade do usuário. Use esta opção se você quiser mostrar o endereço de e-mail completo."
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
msgstr ""
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
@@ -256,7 +289,7 @@ msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</stron
msgstr "<strong>%{created_count}</strong> criado, <strong>%{closed_count}</strong> fechado."
msgid "<strong>%{group_name}</strong> group members"
-msgstr ""
+msgstr "Membros do grupo <strong>%{group_name}</strong>"
msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
msgstr "<strong>%{pushes}</strong> pushes, mais que <strong>%{commits}</strong> commits por <strong>%{people}</strong> contribuidores."
@@ -264,12 +297,18 @@ msgstr "<strong>%{pushes}</strong> pushes, mais que <strong>%{commits}</strong>
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>Remover</strong> branch de origem"
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
-msgstr "'Runner' é um processo que executa um job. Você pode configurar quantos Runners você precisar."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Uma coleção de gráficos sobre Integração Contínua"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "Um novo \"branch\" será criado no seu \"fork\" e um novo merge request será iniciado."
@@ -312,13 +351,16 @@ msgstr "Tokens de acesso"
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr "Acesso negado! Por favor, verifique se você pode adicionar chaves de deploy neste repositório."
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr "Acesso a '%{classification_label}' não permitido"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "Os acessos à storages com defeito foram temporariamente desabilitados para permitir a sua remontagem. Redefina as informações de armazenamento depois que o problema foi resolvido para permitir o acesso de novo."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "Acesse seu runner token, personalize sua configuração de pipeline e visualize o status do seu pipeline e o relatório de coverage."
msgid "Account"
@@ -351,15 +393,15 @@ msgstr "Adicione Webhooks de grupo e GitLab Enterprise Edition."
msgid "Add Kubernetes cluster"
msgstr "Adicionar cluster Kubernetes"
-msgid "Add License"
-msgstr "Adicionar Licença"
-
msgid "Add Readme"
msgstr "Adicionar leia-me"
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "Coloque um texto adicional para aparecer em todas as comunicações por email. Limite de %{character_limit} caracteres"
+msgid "Add license"
+msgstr "Adicionar licença"
+
msgid "Add new application"
msgstr "Adicionar novo aplicativo"
@@ -378,6 +420,9 @@ msgstr "Adicionar usuário(s) ao grupo:"
msgid "Add users to group"
msgstr "Adicionar usuários ao grupo"
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr "Texto adicional"
@@ -390,6 +435,12 @@ msgstr "Visão Geral do Administrador"
msgid "Admin area"
msgstr "Ãrea do administrador"
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "Parar todos os processos"
@@ -408,6 +459,9 @@ msgstr "Você parará todos os processos. Os processos em execução serão abru
msgid "AdminHealthPageLink|health page"
msgstr "página de saúde"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr "Excluir"
@@ -456,6 +510,9 @@ msgstr "Houve commit com todas as mudanças"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "Todas as funcionalidades estão habilitadas para projetos em branco, a partir de templates ou ao importar, mas você pode desativá-los posteriormente nas configurações do projeto."
+msgid "All users"
+msgstr "Todos os usuários"
+
msgid "Allow commits from members who can merge to the target branch."
msgstr "Permitir commits de membros que podem fazer merge ao branch de destino."
@@ -483,11 +540,14 @@ msgstr "Alternativamente, você pode usar um %{personal_access_token_link}. Quan
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "Alternativamente, você pode usar um %{personal_access_token_link}. Quando você cria seu Token de Acesso Pessoal, você precisará selecionar o escopo do <code>repositório</code>, para que possamos exibir uma lista de seus repositórios públicos e privados que estão disponíveis para se importar."
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr "Um aplicativo chamado %{link_to_client} está solicitando acesso à sua conta do GitLab."
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
-msgstr ""
+msgstr "Um campo vazio do usuário do GitLab adicionará o nome completo do usuário do FogBugz (por exemplo, \"Por John Smith\") na descrição de todos os issues e comentários. Ele também irá associar e/ou atribuir essas issues e comentários ao criador do projeto."
msgid "An error accured whilst committing your changes."
msgstr "Ocorreu um erro ao fazer commit de suas alterações."
@@ -501,7 +561,7 @@ msgstr "Um erro ocorreu ao criar o novo branch."
msgid "An error occured whilst fetching the job trace."
msgstr "Ocorreu um erro ao carregar o rastro da tarefa."
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr "Ocorreu um erro ao carregar o último pipeline."
msgid "An error occured whilst loading all the files."
@@ -552,20 +612,32 @@ msgstr "Erro ao gerar pré-visualização do markdown"
msgid "An error occurred while fetching sidebar data"
msgstr "Erro ao recuperar informações da barra lateral"
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr "Erro ao recuperar informações da pipeline."
msgid "An error occurred while getting projects"
msgstr "Erro ao recuperar projetos"
-msgid "An error occurred while importing project: ${details}"
-msgstr "Ocorreu um erro ao importar projeto: ${details}"
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
msgid "An error occurred while initializing path locks"
msgstr "Ocorreu um erro ao inicializar travas de caminhos"
msgid "An error occurred while loading commit signatures"
-msgstr ""
+msgstr "Erro ao carregar as assinaturas de commit"
msgid "An error occurred while loading diff"
msgstr "Erro ao carregar o Diff"
@@ -622,7 +694,7 @@ msgid "Any"
msgstr "Qualquer um"
msgid "Any Label"
-msgstr "Qualquer Label"
+msgstr "Qualquer etiqueta"
msgid "Appearance"
msgstr "Aparência"
@@ -648,10 +720,16 @@ msgstr "Abril"
msgid "Archived project! Repository and other project resources are read-only"
msgstr "Projeto arquivado! Repositório e outros recursos de projeto são somente leitura"
+msgid "Archived projects"
+msgstr "Projetos arquivados"
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Tem certeza que deseja excluir este agendamento de pipeline?"
msgid "Are you sure you want to lose unsaved changes?"
+msgstr "Você tem certeza de que quer perder as alterações não salvas?"
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
@@ -672,14 +750,17 @@ msgstr "Você tem certeza que quer destravar %{path_lock_path}?"
msgid "Are you sure?"
msgstr "Você tem certeza?"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr "Artefatos"
msgid "Ascending"
msgstr "Ascendente"
-msgid "Ask your group maintainer to setup a group Runner."
-msgstr "Solicite a um mantenedor do grupo para configurar um Runner de grupo."
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
msgid "Assertion consumer service URL"
msgstr "URL de serviço do consumidor de asserção"
@@ -688,7 +769,7 @@ msgid "Assign custom color like #FF0000"
msgstr "Coloque uma cor personalizada, como #FF0000"
msgid "Assign labels"
-msgstr "Atribuir labels"
+msgstr "Atribuir etiquetas"
msgid "Assign milestone"
msgstr "Atribuir milestone"
@@ -711,8 +792,8 @@ msgstr "Atribuído a mim"
msgid "Assignee"
msgstr "Responsável"
-msgid "Assignee boards not available with your current license"
-msgstr "Quadro de responsáveis não disponível com sua licença atual"
+msgid "Assignee lists not available with your current license"
+msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
msgstr "Listas de responsáveis mostram todas as issues atribuídas ao usuário selecionado."
@@ -738,6 +819,9 @@ msgstr "Log de autenticação"
msgid "Authentication log"
msgstr "Log de autenticação"
+msgid "Authentication method"
+msgstr "Método de autenticação"
+
msgid "Author"
msgstr "Autor"
@@ -798,6 +882,9 @@ msgstr "Ele gerará a build, testará e fará deploy de sua aplicação automati
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "Saiba mais em %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr "Você pode automaticamente construir e testar sua aplicação, se você %{link_to_auto_devops_settings} para este projeto. Você pode também fazer o deploy automaticamente, se você %{link_to_add_kubernetes_cluster}."
@@ -807,6 +894,9 @@ msgstr "adicionar um cluster Kubernetes"
msgid "AutoDevOps|enable Auto DevOps"
msgstr "ativar Auto DevOps"
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr "Disponível"
@@ -831,9 +921,6 @@ msgstr "Tarefas em Segundo Plano"
msgid "Background color"
msgstr "Cor do plano de fundo"
-msgid "Background jobs"
-msgstr "Tarefas em segundo plano"
-
msgid "Badges"
msgstr "Selos"
@@ -844,7 +931,7 @@ msgid "Badges|Add badge"
msgstr "Novo selo"
msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
-msgstr "Fala ao adicionar selo, por favor, cheque as URLs inseridas e tente novamente."
+msgstr "Falha ao adicionar selo, por favor, cheque os URLs inseridos e tente novamente."
msgid "Badges|Badge image URL"
msgstr "URL da imagem do selo"
@@ -873,6 +960,9 @@ msgstr "Sem imagem"
msgid "Badges|No image to preview"
msgstr "Sem imagem para pré-visualizar"
+msgid "Badges|Please fill in a valid URL"
+msgstr "Por favor, preencha um URL válido"
+
msgid "Badges|Project Badge"
msgstr "Selo de projeto"
@@ -883,7 +973,7 @@ msgid "Badges|Save changes"
msgstr "Salvar alterações"
msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
-msgstr "Erro ao salvar selo, por favor, cheque as URLs digitadas e tente novamente."
+msgstr "Erro ao salvar selo, por favor, cheque os URLs digitados e tente novamente."
msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
msgstr "Badges | As variáveis de %{docsLinkStart}%{docsLinkEnd} oferece suporte a GitLab: %{placeholders}"
@@ -900,9 +990,15 @@ msgstr "Esse grupo não tem selos"
msgid "Badges|This project has no badges"
msgstr "Esse projeto não tem selos"
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr "Você está prestes a excluir este selo. Selos excluídos <strong>não podem</strong> ser restaurados."
+
msgid "Badges|Your badges"
msgstr "Seus selos"
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr "por exemplo, %{exampleUrl}"
+
msgid "Begin with the selected commit"
msgstr "Comece com o commit selecionado"
@@ -978,14 +1074,17 @@ msgstr "%{price_per_year} pago anualmente"
msgid "BillingPlans|per user"
msgstr "por usuário"
-msgid "Bitbucket import"
+msgid "Bitbucket Server Import"
msgstr ""
+msgid "Bitbucket import"
+msgstr "Importar do Bitbucket"
+
msgid "Blog"
msgstr "Blog"
msgid "Boards"
-msgstr "Boards"
+msgstr "Painéis"
msgid "Branch %{branchName} was not found in this project's repository."
msgstr "O branch %{branchName} não foi encontrado no repositório deste projeto."
@@ -1148,6 +1247,9 @@ msgstr "Acessar arquivos"
msgid "Browse files"
msgstr "Navegar pelos arquivos"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr "Métricas de negócios (personalizadas)"
@@ -1160,6 +1262,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr "Configurações de CI / CD"
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr "CI/CD"
@@ -1172,9 +1277,6 @@ msgstr "CI/CD para um repositório externo"
msgid "CI/CD settings"
msgstr "Configurações de CI/CD"
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr "Um arquivo %{ci_file} precisa ser especificado antes de você conseguir utilizar a integração e entrega contínua."
-
msgid "CICD|Auto DevOps"
msgstr "Auto DevOps"
@@ -1187,44 +1289,38 @@ msgstr "Deploy automático para staging, deploy manual para produção"
msgid "CICD|Continuous deployment to production"
msgstr "Deploy contínuo para produção"
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
msgid "CICD|Deployment strategy"
msgstr "Estratégia de deploy"
msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr "Estratégia de deploy precisa de um nome de domínio para funcionar corretamente."
-msgid "CICD|Disable Auto DevOps"
-msgstr "Desabilitar DevOps"
-
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr "Não configure um domínio aqui se você estiver configurando vários clusters de Kubernetes com o Auto DevOps."
-msgid "CICD|Enable Auto DevOps"
-msgstr "Ativar DevOps"
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr "Siga o padrão da instância para ter o Auto DevOps ativado ou desativado quando não houver nenhum %{ci_file} específico do projeto."
-
-msgid "CICD|Instance default (%{state})"
-msgstr "Instância padrão (%{state})"
-
msgid "CICD|Jobs"
msgstr "Jobs"
msgid "CICD|Learn more about Auto DevOps"
msgstr "Aprender mais sobre Auto DevOps"
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
-msgstr "As configurações de pipeline do Auto DevOps serão utilizadas quando não existir %{ci_file} no projeto."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr "Você precisa especificar um domínio se você quiser usar Apps de Revisão Automáticos e Estágios de Deploy automáticos."
-msgid "Callback URL"
+msgid "CICD|instance enabled"
msgstr ""
+msgid "Callback URL"
+msgstr "URL de Retorno"
+
msgid "Callback url"
-msgstr ""
+msgstr "URL de retorno"
msgid "Can't find HEAD commit for this branch"
msgstr "Não é possível encontrar o commit HEAD para este branch"
@@ -1247,6 +1343,9 @@ msgstr "Impressão digital do certificado"
msgid "Change Weight"
msgstr "Alterar Peso"
+msgid "Change template"
+msgstr "Mudar modelo"
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr "Altere esse valor para influenciar com que frequência a interface do usuário do GitLab pesquisa atualizações."
@@ -1304,6 +1403,12 @@ msgstr "Escolha o arquivo ..."
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Escolha a branch/tag (ex: %{master}) ou número do commit (ex: %{sha}) para ver o que mudou ou para criar um merge request."
+msgid "Choose a template..."
+msgstr "Escolha um modelo..."
+
+msgid "Choose a type..."
+msgstr "Escolha um tipo..."
+
msgid "Choose any color."
msgstr "Escolha qualquer cor."
@@ -1314,7 +1419,7 @@ msgid "Choose file..."
msgstr "Escolha o arquivo..."
msgid "Choose the top-level group for your repository imports."
-msgstr ""
+msgstr "Escolha o grupo principal para importar seus repositórios."
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr "Escolha quais grupos você deseja sincronizar nesse nó secundário."
@@ -1466,6 +1571,9 @@ msgstr "Clonar repositório"
msgid "Close"
msgstr "Fechar"
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr "Fechado"
@@ -1475,6 +1583,9 @@ msgstr "Issues Fechadas"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} foi instalado com sucesso no seu cluster Kubernetes"
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr "API URL"
@@ -1484,12 +1595,21 @@ msgstr "Adicionar cluster Kubernetes"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Opções avançadas na integração deste cluster Kubernetes"
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr "Erro ao recuperar zonas de projeto: %{error}"
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr "Erro ao recuperar seus projetos: %{error}"
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr "Aplicações"
@@ -1502,11 +1622,11 @@ msgstr "Certificado CA"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Pacote de autoridade certificadora (Formato PEM)"
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "Escolha qual dos ambientes do seu projeto usará este cluster Kubernetes."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr "Controle como seu cluster Kubernetes se integra com o GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
msgid "ClusterIntegration|Copy API URL"
msgstr "Copiar URL da API"
@@ -1532,6 +1652,12 @@ msgstr "Integração de Clusters | Criar cluster Kubernetes"
msgid "ClusterIntegration|Did you know?"
msgstr "Você sabia?"
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "Digite detalhes para seu cluster Kubernetes"
@@ -1539,7 +1665,7 @@ msgid "ClusterIntegration|Environment scope"
msgstr "Escopo de ambiente"
msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
-msgstr "Cada nova conta no Google Cloud Plataform (GCP) recebe US$300 em créditos na %{sign_up_link}. Em parceria com o Google, o Gitlab pode oferecer um adicional de US$200 para novas contas do GCP para começar a usar a integração GKE do GitLab."
+msgstr "Cada nova conta no Google Cloud Plataform (GCP) recebe US$300 em créditos por se %{sign_up_link}. Em parceria com o Google, o Gitlab pode oferecer um adicional de US$200 para novas contas do GCP para começar a usar a integração GKE do GitLab."
msgid "ClusterIntegration|Fetching machine types"
msgstr "Recuperando tipos de máquina"
@@ -1556,6 +1682,9 @@ msgstr "Integração GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "Gitlab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr "Google Cloud Platform projeto"
@@ -1568,6 +1697,9 @@ msgstr "Projeto do Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr "Ocultar"
@@ -1583,6 +1715,9 @@ msgstr "Ingressar"
msgid "ClusterIntegration|Ingress IP Address"
msgstr "Endereço IP de entrada"
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr "Instalar"
@@ -1607,6 +1742,9 @@ msgstr "Nome do host Jupyter"
msgid "ClusterIntegration|JupyterHub"
msgstr "JupyterHub"
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Cluter Kubernetes"
@@ -1619,15 +1757,6 @@ msgstr "Saúde do cluster Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Integração com o cluster Kubernetes"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "Integração com o cluster Kubernetes está desabilitada para esse projeto."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "Integração com o cluster Kubernetes está ativada para esse projeto."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "Integração com cluster Kubernetes está ativada para esse projeto. Desabilitar essa integração não afetará seu cluster Kubernetes, somente desligará a conexão do GitLab com o mesmo."
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "O cluster Kubernetes está sendo criado no Google Kubernetes Engine..."
@@ -1652,12 +1781,6 @@ msgstr "Saiba mais sobre os %{help_link_start}Kubernetes%{help_link_end}."
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr "Saiba mais sobre as %{help_link_start}zonas%{help_link_end}."
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "Ler mais sobre ambientes"
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr "Ler mais sobre configurações de segurança"
-
msgid "ClusterIntegration|Machine type"
msgstr "Tipo de máquina"
@@ -1700,6 +1823,9 @@ msgstr "Por favor, entre com as informações de acesso para seu cluter Kubernet
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Por favor, tenha certeza que sua conta no Google cumpre com os requisitos:"
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr "Namespace do projeto"
@@ -1709,6 +1835,12 @@ msgstr "Namespace do projeto (opcional, único)"
msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr "Leia nosso %{link_to_help_page} em integração de cluter Kubernetes."
@@ -1721,6 +1853,9 @@ msgstr "Remover integração"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "Remover configuração desse cluster Kubernetes para esse projeto. Isso não apagará seu cluster Kubernetes atual."
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr "Substitua isso por seu próprio nome de host, se desejar. Se você fizer isso, aponte o nome do host para o endereço IP de entrada acima."
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "Solicitação para início de instalação falhou"
@@ -1736,9 +1871,6 @@ msgstr "Pesquisar projetos"
msgid "ClusterIntegration|Search zones"
msgstr "Pesquisar zonas"
-msgid "ClusterIntegration|Security"
-msgstr "Segurança"
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "Veja e edite os detalhes de seus cluster Kubernates"
@@ -1775,12 +1907,18 @@ msgstr "Erro ao criar cluster Kubernetes no Google Kubernetes Engine"
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Algo deu errado ao instalar %{title}"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr "O configuração de cluster padrão permite acesso a uma gama de funcionalidades necessárias para gerar build e publicar aplicações em container."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr "O endereço IP está em processo de atribuição. Verifique seu cluster ou cotas do Kubernetes no Google Kubernetes Engine se demorar muito."
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "Essa conta precisa de permissões para criar um cluster Kubernetes no %{link_to_container_project} especificado"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "Alternar cluster Kubernetes"
@@ -1793,9 +1931,15 @@ msgstr "Token"
msgid "ClusterIntegration|Validating project billing status"
msgstr "Validando status de faturamento do projeto"
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr "Não foi possível verificar se um dos seus projetos no GCP possui o faturamento ativado. Por favor, tente novamente."
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "Com um cluster Kubernetes associado a esse projeto, você pode utilizar apps de revisão, publicar suas aplicações, executar suas pipelines e muito mais de um jeito simples."
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "Sua conta precisa de %{link_to_kubernetes_engine}"
@@ -1814,9 +1958,6 @@ msgstr "documentação"
msgid "ClusterIntegration|help page"
msgstr "ajuda"
-msgid "ClusterIntegration|installing applications"
-msgstr "Instalando aplicações"
-
msgid "ClusterIntegration|meets the requirements"
msgstr "atende aos requisitos"
@@ -1826,6 +1967,9 @@ msgstr "configurado corretamente"
msgid "ClusterIntegration|sign up"
msgstr "cadastrar"
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr "Cohorts"
@@ -1875,6 +2019,9 @@ msgstr "Commit"
msgid "CommitMessage|Add %{file_name}"
msgstr "Adicionar %{file_name}"
+msgid "CommitWidget|authored"
+msgstr "Escrito"
+
msgid "Commits"
msgstr "Commits"
@@ -1947,17 +2094,14 @@ msgstr "Confidencialidade"
msgid "Configure Gitaly timeouts."
msgstr "Configurar timeouts do Gitaly."
-msgid "Configure Sidekiq job throttling."
-msgstr "Configurar otimização de jobs no Sidekiq."
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr "Configurar housekeeping e checagens do git nos repositórios."
msgid "Configure limits for web and API requests."
msgstr "Configurar limites para web e requisições para API."
-msgid "Configure push and pull mirrors."
-msgstr "Configurar push e pull de espelhamentos."
+msgid "Configure push mirrors."
+msgstr ""
msgid "Configure storage path and circuit breaker settings."
msgstr "Configurar caminho de armazenamento e circuit breaker."
@@ -2046,6 +2190,9 @@ msgstr "Contribuições"
msgid "Contribution guide"
msgstr "Guia de contribuição"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr "Contribuições por membro do grupo"
@@ -2077,6 +2224,15 @@ msgid "Control the maximum concurrency of verification operations for this Geo n
msgstr "Controlar a concorrência máxima de operações de verificação para esse nó Geo"
msgid "ConvDev Index"
+msgstr "Ãndice ConvDev"
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
msgstr ""
msgid "Copy SSH public key to clipboard"
@@ -2149,13 +2305,13 @@ msgid "Create group"
msgstr "Criar grupo"
msgid "Create group label"
-msgstr "Criar Label de grupo"
+msgstr "Criar etiqueta de grupo"
msgid "Create issue"
msgstr "Criar issue"
msgid "Create lists from labels. Issues with that label appear in that list."
-msgstr "Criar lista a partir de labels. Issues com labels aparecem nestas listas."
+msgstr "Criar lista a partir de etiquetas. Issues com etiquetas aparecem nestas listas."
msgid "Create merge request"
msgstr "Criar merge request"
@@ -2176,16 +2332,13 @@ msgid "Create new file or directory"
msgstr "Criar novo arquivo ou diretório"
msgid "Create new label"
-msgstr "Criar nova label"
+msgstr "Criar nova etiqueta"
msgid "Create new..."
msgstr "Criar novo..."
msgid "Create project label"
-msgstr "Criar Label de projeto"
-
-msgid "CreateNewFork|Fork"
-msgstr "Fork"
+msgstr "Criar etiqueta de projeto"
msgid "CreateTag|Tag"
msgstr "Tag"
@@ -2202,6 +2355,9 @@ msgstr "Criado em"
msgid "Created by me"
msgstr "Criado por mim"
+msgid "Created on"
+msgstr "Criado em"
+
msgid "Created on:"
msgstr "Criado em:"
@@ -2214,6 +2370,9 @@ msgstr "Fuso horário do cron"
msgid "Cron syntax"
msgstr "Sintaxe do cron"
+msgid "Current Branch"
+msgstr "Branch atual"
+
msgid "Current node"
msgstr "Nó atual"
@@ -2223,6 +2382,9 @@ msgstr "Perfil"
msgid "CurrentUser|Settings"
msgstr "Configurações"
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr "Caminho de configuração do IC personalizado"
@@ -2232,18 +2394,24 @@ msgstr "Eventos de notificação personalizados"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "Níveis de notificação personalizados são equivalentes a níveis de participação. Com níveis de notificação personalizados você também será notificado sobre eventos selecionados. Para mais informações, visite %{notification_link}."
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr "Personalizar cores"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "Personalize como os endereços de e-mail e nomes de usuário do FogBugz são importados para o GitLab. Na próxima etapa, você poderá selecionar os projetos que deseja importar."
msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "Personalize como os endereços de e-mail e nomes de usuário do Google Code são importados para o GitLab. Na próxima etapa, você poderá selecionar os projetos que deseja importar."
msgid "Cycle Analytics"
msgstr "Análise de Ciclo"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "Código"
@@ -2274,6 +2442,12 @@ msgstr "Todos"
msgid "DashboardProjects|Personal"
msgstr "Pessoal"
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr "Dez"
@@ -2283,8 +2457,11 @@ msgstr "Dezembro"
msgid "Decline and sign out"
msgstr "Recusar e sair"
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
-msgstr "Label de classificação padrão"
+msgstr "Etiqueta de classificação padrão"
msgid "Default: Directly import the Google Code email address or username"
msgstr "Padrão: Importar diretamente o endereço de e-mail ou nome de usuário do Google Code"
@@ -2298,6 +2475,9 @@ msgstr "Defina um padrão personalizado utilizando a sintaxe do cron"
msgid "Delete"
msgstr "Excluir"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr "Excluir Snippet"
@@ -2439,7 +2619,7 @@ msgid "DeployTokens|Your new project deploy token has been created."
msgstr "Seu novo token de deploy de projeto foi criado."
msgid "Deprioritize label"
-msgstr "Despriorizar label"
+msgstr "Despriorizar etiqueta"
msgid "Descending"
msgstr "Decrescente"
@@ -2454,17 +2634,23 @@ msgid "Description:"
msgstr "Descrição:"
msgid "Destroy"
-msgstr ""
+msgstr "Destruir"
msgid "Details"
msgstr "Detalhes"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr "Nenhum nome de arquivo disponível"
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr "Algo errado aconteceu ao buscar linhas de comparação."
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "Nome do diretório"
@@ -2477,9 +2663,21 @@ msgstr "Desativar para este projeto"
msgid "Disable group Runners"
msgstr "Desabilitar runners de grupo"
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr "Rejeitar alterações"
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr "Descartar rascunho"
@@ -2490,7 +2688,7 @@ msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr "Descubra projetos, grupos e snippets. Compartilhe seus projetos com outras pessoas"
msgid "Dismiss"
-msgstr ""
+msgstr "Ignorar"
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Ignorar introdução do Cycle Analytics"
@@ -2499,7 +2697,7 @@ msgid "Dismiss Merge Request promotion"
msgstr "Descartar promoção de Merge Request"
msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
+msgstr "Deseja personalizar como os endereços de e-mail e nomes de usuário do Google Code são importados para o GitLab?"
msgid "Documentation for popular identity providers"
msgstr "Documentação para provedores de identidade populares"
@@ -2547,7 +2745,7 @@ msgid "Due date"
msgstr "Validade"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
-msgstr "Durante esse processo, você será perguntado por URLs do lado do GitLab. Use as URLs mostradas abaixo."
+msgstr "Durante esse processo, você será perguntado por URLs do lado do GitLab. Use os URLs mostrados abaixo."
msgid "Each Runner can be in one of the following states:"
msgstr "Cada runner pode estar em um dos seguintes estados:"
@@ -2556,7 +2754,7 @@ msgid "Edit"
msgstr "Alterar"
msgid "Edit Label"
-msgstr "Editar Label"
+msgstr "Editar etiqueta"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Alterar Agendamento do Pipeline %{id}"
@@ -2579,7 +2777,7 @@ msgstr "Editar identidade para %{user_name}"
msgid "Elasticsearch"
msgstr "Elasticsearch"
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr "Integração com Elasticsearch. Elasticsearch AWS IAM."
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr "Ativar reCAPTCHA ou Akismet e definir seus limites de IP."
msgid "Enable the Performance Bar for a given group."
msgstr "Ative a barra de desempenho para um determinado grupo."
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr "Habilitado"
msgid "Ends at (UTC)"
msgstr "Termina em (UTC)"
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr "Digite a descrição da issue"
+
+msgid "Enter the issue title"
+msgstr "Digite o título da issue"
+
+msgid "Enter the merge request description"
+msgstr "Digite a descrição do merge request"
+
+msgid "Enter the merge request title"
+msgstr "Digite o título do merge request"
+
msgid "Environments"
msgstr "Ambientes"
@@ -2655,16 +2874,16 @@ msgid "Environments|An error occurred while making the request."
msgstr "Um erro ocorreu ao fazer a requisição."
msgid "Environments|An error occurred while stopping the environment, please try again"
-msgstr ""
+msgstr "Ocorreu um erro ao parar o ambiente, por favor, tente novamente"
msgid "Environments|Are you sure you want to stop this environment?"
-msgstr ""
+msgstr "Você tem certeza de que deseja parar este ambiente?"
msgid "Environments|Commit"
msgstr "Commit"
msgid "Environments|Deploy to..."
-msgstr ""
+msgstr "Implantar para..."
msgid "Environments|Deployment"
msgstr "Deploy"
@@ -2675,11 +2894,14 @@ msgstr "Ambiente"
msgid "Environments|Environments"
msgstr "Ambientes"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr "Job"
msgid "Environments|Learn more about stopping environments"
-msgstr ""
+msgstr "Saiba mais sobre como parar ambientes"
msgid "Environments|New environment"
msgstr "Novo ambiente"
@@ -2690,9 +2912,12 @@ msgstr "Nenhum deploy"
msgid "Environments|No pod name has been specified"
msgstr "Nenhum nome pod foi especificado"
-msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr "Observe que essa ação interromperá o ambiente, mas %{emphasis_start}não%{emphasis_end} terá efeito em qualquer implementação existente devido a nenhuma \"ação de parada do ambiente\" estar definida no arquivo %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end}."
+
msgid "Environments|Open live environment"
msgstr "Abrir ambiente ao vivo"
@@ -2700,22 +2925,22 @@ msgid "Environments|Pod logs from"
msgstr "Logs de pod de"
msgid "Environments|Re-deploy to environment"
-msgstr ""
+msgstr "Reimplantar no ambiente"
msgid "Environments|Read more about environments"
msgstr "Ler mais sobre ambiente"
msgid "Environments|Rollback environment"
-msgstr ""
+msgstr "Reverter ambiente"
msgid "Environments|Show all"
msgstr "Mostrar tudo"
msgid "Environments|Stop"
-msgstr ""
+msgstr "Parar"
msgid "Environments|Stop environment"
-msgstr ""
+msgstr "Parar ambiente"
msgid "Environments|Updated"
msgstr "Atualizado"
@@ -2723,6 +2948,9 @@ msgstr "Atualizado"
msgid "Environments|You don't have any environments right now."
msgstr "Você não tem nenhum ambiente."
+msgid "Environments|protected"
+msgstr "protegido"
+
msgid "Epic"
msgstr "Epic"
@@ -2738,6 +2966,30 @@ msgstr "Roadmap de epics"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr "Epics permitem gerenciar seu portfólio de projetos de forma mais eficiente e com menos esforço"
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr "Como posso resolver isso?"
+
+msgid "Epics|More information"
+msgstr "Mais informações"
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr "Erro"
+
msgid "Error Reporting and Logging"
msgstr "Relatório e registro de erros"
@@ -2748,7 +3000,7 @@ msgid "Error fetching contributors data."
msgstr "Erro ao recuperar informações de contribuintes."
msgid "Error fetching labels."
-msgstr "Erro ao carregar labels."
+msgstr "Erro ao carregar etiquetas."
msgid "Error fetching network graph."
msgstr "Erro ao recuperar gráfico de rede."
@@ -2762,11 +3014,14 @@ msgstr "Erro ao recupera dados de ping."
msgid "Error loading branch data. Please try again."
msgstr "Erro ao carregar dados de branch. Por favor, tente novamente."
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr "Erro ao carregar último commit."
msgid "Error loading markdown preview"
-msgstr ""
+msgstr "Erro ao carregar a pré-visualização do markdown"
msgid "Error loading merge requests."
msgstr "Erro ao carregar merge requests."
@@ -2774,11 +3029,17 @@ msgstr "Erro ao carregar merge requests."
msgid "Error loading project data. Please try again."
msgstr "Erro ao carregar dados do projeto. Por favor, tente novamente."
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Erro ao alterar configuração de notificação de assinatura"
msgid "Error saving label update."
-msgstr "Erro ao salvar alteração de label."
+msgstr "Erro ao salvar alteração de etiqueta."
msgid "Error updating status for all todos."
msgstr "Erro ao atualizar status para todas as tarefas."
@@ -2786,6 +3047,9 @@ msgstr "Erro ao atualizar status para todas as tarefas."
msgid "Error updating todo status."
msgstr "Erro ao atualizar status das tarefas."
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr "Estimativa"
@@ -2828,6 +3092,9 @@ msgstr "Expandir tudo"
msgid "Expand sidebar"
msgstr "Expandir barra lateral"
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr "Explorar"
@@ -2859,13 +3126,13 @@ msgid "External authorization request timeout"
msgstr "A requisição de autorização externa esgotou o tempo limite"
msgid "ExternalAuthorizationService|Classification Label"
-msgstr "Label de classificação"
+msgstr "Etiqueta de classificação"
msgid "ExternalAuthorizationService|Classification label"
-msgstr "Label de classificação"
+msgstr "Etiqueta de classificação"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
-msgstr "Quando nenhum label de classificação está definido, o label padrão `%{default_label}` será usado."
+msgstr "Quando nenhum etiqueta de classificação está definida, a etiqueta padrão `%{default_label}` será usada."
msgid "Facebook"
msgstr "Facebook"
@@ -2883,7 +3150,10 @@ msgid "Failed to check related branches."
msgstr "Falha ao procurar por branches relacionadas."
msgid "Failed to remove issue from board, please try again."
-msgstr "Falha ao remover issue do board, por favor, tente novamente."
+msgstr "Falha ao remover issue do painel, por favor, tente novamente."
+
+msgid "Failed to remove mirror."
+msgstr ""
msgid "Failed to remove the pipeline schedule"
msgstr "Erro ao excluir o agendamento do pipeline"
@@ -2906,6 +3176,9 @@ msgstr "Fevereiro"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "Campos nessa página não são mais editáveis, você pode configurar"
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "Arquivos"
@@ -2916,11 +3189,20 @@ msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and p
msgstr "Preencha nos campos abaixo, ative o <strong>%{enable_label}</strong> e pressione <strong>%{save_changes}</strong>"
msgid "Filter"
+msgstr "Filtro"
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
msgstr ""
msgid "Filter by commit message"
msgstr "Filtrar por mensagem de commit"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "Localizar por caminho"
@@ -2931,7 +3213,10 @@ msgid "Find the downloaded ZIP file and decompress it."
msgstr "Encontre o arquivo ZIP baixado e extraia-o."
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
-msgstr ""
+msgstr "Encontre o arquivo recém-extraído <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code>."
+
+msgid "Fingerprints"
+msgstr "Impressões digitais"
msgid "Finished"
msgstr "Finalizado"
@@ -2942,23 +3227,35 @@ msgstr "Primeiro"
msgid "FirstPushedBy|pushed by"
msgstr "publicado por"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr "E-mail do FogBugz"
msgid "FogBugz Import"
-msgstr ""
+msgstr "Importação do FogBugz"
msgid "FogBugz Password"
msgstr "Senha do FogBugz"
msgid "FogBugz URL"
-msgstr ""
+msgstr "URL do FogBugz"
msgid "FogBugz import"
-msgstr ""
+msgstr "Importação do FogBugz"
msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
+msgstr "Siga os passos abaixo para exportar os dados do seu projeto do Google Code."
msgid "Font Color"
msgstr "Cor da Fonte"
@@ -2969,17 +3266,18 @@ msgstr "Mensagem do Rodapé"
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr "Para projetos internos, qualquer usuário conectado pode visualizar pipelines e acessar detalhes do job (logs de saída e artefatos)"
+msgid "For more information, go to the "
+msgstr "Para mais informações, vá para o "
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr "Para projetos privados, qualquer membro (guest ou superior) pode visualizar pipelines e acessar detalhes do trabalho (logs de saída e artefatos)"
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr "Para projetos públicos, qualquer pessoa pode visualizar pipelines e acessar detalhes do trabalho (logs de saída e artefatos)"
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Fork"
-msgstr[1] "Forks"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "Fork criado a partir de"
@@ -2999,16 +3297,19 @@ msgid "From %{provider_title}"
msgstr "De %{provider_title}"
msgid "From Bitbucket"
-msgstr ""
+msgstr "Do Bitbucket"
+
+msgid "From Bitbucket Server"
+msgstr "Do servidor do Bitbucket"
msgid "From FogBugz"
-msgstr ""
+msgstr "Do FogBugz"
msgid "From GitLab.com"
-msgstr ""
+msgstr "Do GitLab.com"
msgid "From Google Code"
-msgstr ""
+msgstr "Do Google Code"
msgid "From issue creation until deploy to production"
msgstr "Da abertura de tarefas até a implantação para a produção"
@@ -3016,6 +3317,9 @@ msgstr "Da abertura de tarefas até a implantação para a produção"
msgid "From merge request merge until deploy to production"
msgstr "Do merge request até a implantação em produção"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Na visualização de detalhes do cluster do Kubernetes, instale o Runner pela lista de aplicativos"
@@ -3029,7 +3333,10 @@ msgid "General pipelines"
msgstr "Pipelines Gerais"
msgid "Generate a default set of labels"
-msgstr "Gerar labels padrão"
+msgstr "Gerar etiquetas padrão"
+
+msgid "Geo"
+msgstr ""
msgid "Geo Nodes"
msgstr "Nós do Geo"
@@ -3196,33 +3503,132 @@ msgstr "Wikis verificados com suas contrapartes no nó Primário"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr "Você tem nós Geo configurados usando uma conexão HTTP insegura. Recomendamos o uso de HTTPS."
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr "Todos os projetos"
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr "Capacidade de sincronização de arquivo"
msgid "Geo|Groups to synchronize"
msgstr "Grupos para sincronizar"
+msgid "Geo|In sync"
+msgstr "Em sincronia"
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr "Última vez verificada"
+
+msgid "Geo|Never"
+msgstr "Nunca"
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr "Sem erros"
+
+msgid "Geo|Pending"
+msgstr "Pendente"
+
+msgid "Geo|Pending synchronization"
+msgstr "Sincronização pendente"
+
+msgid "Geo|Pending verification"
+msgstr "Verificação pendente"
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr "Projetos em certos grupos"
msgid "Geo|Projects in certain storage shards"
msgstr "Projetos em certos pedaços de armazenamento"
+msgid "Geo|Recheck"
+msgstr "Verificar novamente"
+
+msgid "Geo|Redownload"
+msgstr "Baixar novamente"
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr "Capacidade de sincronização de repositório"
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr "Selecione grupos para replicar."
msgid "Geo|Shards to synchronize"
msgstr "Shards para sincronizar"
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr "Capacidade de verificação"
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr "Git"
@@ -3254,13 +3660,13 @@ msgid "GitLab Group Runners can execute code for all the projects in this group.
msgstr "Os GitLab Group Runners podem executar código para todos os projetos neste grupo."
msgid "GitLab Import"
-msgstr ""
+msgstr "Importação do GitLab"
msgid "GitLab User"
-msgstr ""
+msgstr "Usuário GitLab"
msgid "GitLab project export"
-msgstr ""
+msgstr "Exportação do projeto GitLab"
msgid "GitLab single sign on URL"
msgstr "URL de logon único do GitLab"
@@ -3269,6 +3675,9 @@ msgid "GitLab will run a background job that will produce pseudonymized CSVs of
msgstr "O GitLab executará um trabalho em segundo plano que produzirá CSVs com pseudônimos do banco de dados do GitLab que serão carregados no diretório de armazenamento de objetos configurado."
msgid "GitLab.com import"
+msgstr "Importação do GitLab.com"
+
+msgid "GitLab’s issue tracker"
msgstr ""
msgid "Gitaly"
@@ -3281,10 +3690,10 @@ msgid "Gitaly|Address"
msgstr "Endereço"
msgid "Gitea Host URL"
-msgstr ""
+msgstr "URL do host do Gitea"
msgid "Gitea Import"
-msgstr ""
+msgstr "Importação do Gitea"
msgid "Go Back"
msgstr "Voltar"
@@ -3292,20 +3701,17 @@ msgstr "Voltar"
msgid "Go back"
msgstr "Voltar"
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "Ir para seu fork"
-
-msgid "GoToYourFork|Fork"
-msgstr "Fork"
+msgid "Go to %{link_to_google_takeout}."
+msgstr "Ir para %{link_to_google_takeout}."
msgid "Google Code import"
-msgstr ""
+msgstr "Importação do Google Code"
msgid "Google Takeout"
-msgstr ""
+msgstr "Google Takeout"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Autenticação do Google não está %{link_to_documentation}. Peça ao administrador do Gitlab se você deseja usar esse serviço."
@@ -3323,7 +3729,7 @@ msgid "Group CI/CD settings"
msgstr "Configurações de CI/CD do grupo"
msgid "Group Git LFS status:"
-msgstr ""
+msgstr "Status do LFS do grupo Git:"
msgid "Group ID"
msgstr "ID do grupo"
@@ -3361,14 +3767,14 @@ msgstr "Desculpe, nenhum epic corresponde à sua pesquisa"
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr "O roadmap mostra o progresso de seus epics ao longo de uma linha do tempo"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "Para visualizar o roadmap, adicione uma data de início ou término planejada a um dos seus epics nesse grupo ou em seus subgrupos. Na visão mensal, apenas epics no mês passado, no mês atual e nos próximos 5 meses são exibidos &ndash; de %{startDate} a %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "Para visualizar o roadmap, adicione uma data de início ou término planejada a um dos seus epics nesse grupo ou em seus subgrupos. Na visão trimestral, apenas epics no trimestre passado, no trimestre atual e nos próximos 4 trimestres são exibidos &ndash; de %{startDate} a %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "Para visualizar o roadmap, adicione uma data de início ou término planejada a um dos seus epics nesse grupo ou em seus subgrupos. Na visão semanal, apenas epics na semana passada, na semana atual e nas próximas 4 semanas são exibidos &ndash; de %{startDate} a %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr "Para ampliar sua pesquisa, alterar ou remover filtros. Na visão mensal, apenas epics no mês passado, no mês atual e nos próximos 5 meses são exibidos &ndash; de %{startDate} a %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr "Para ampliar sua pesquisa, alterar ou remover filtros. Na visão semanal
msgid "GroupRoadmap|Until %{dateWord}"
msgstr "Até %{dateWord}"
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Bloquear compartilhamento de projetos do grupo %{group} com outros grupos"
@@ -3445,6 +3860,9 @@ msgstr "Nenhum grupo encontrado"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "Você pode gerenciar permissões de membros e acesso do seu grupo para cada projeto no grupo."
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr "Criar um projeto nesse grupo."
@@ -3457,20 +3875,20 @@ msgstr "Editar grupo"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr "Falha ao deixar o grupo. Por favor, verifique se você é o único dono."
-msgid "GroupsTree|Filter by name..."
-msgstr "Filtrar por nome..."
-
msgid "GroupsTree|Leave this group"
msgstr "Deixar o grupo"
msgid "GroupsTree|Loading groups"
msgstr "Carregando grupos"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "Desculpe, nenhum grupo corresponde à sua pesquisa"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "Desculpe, nenhum grupo ou projeto correspondem à sua pesquisa"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
msgid "Have your users email"
msgstr "Tem o e-mail de seus usuários"
@@ -3505,6 +3923,15 @@ msgstr "Página de ajuda"
msgid "Help page text and support page url."
msgstr "Texto da página de ajuda e Url da página de suporte."
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "Ocultar valor"
@@ -3528,21 +3955,45 @@ msgstr "Termos de Serviço e Política de Privacidade"
msgid "ID"
msgstr "ID"
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr "Commit"
msgid "IDE|Edit"
msgstr "Editar"
-msgid "IDE|Go back"
-msgstr "Voltar"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
msgid "IDE|Open in file view"
msgstr "Abrir na visualização de arquivos"
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr "Revisar"
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr "Identificador"
@@ -3552,6 +4003,9 @@ msgstr "Identidades"
msgid "Identity provider single sign on URL"
msgstr "URL de logon único de provedor de identidade"
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr "Se desativado, o nível de acesso irá depender das permissões do usuário no projeto."
@@ -3559,7 +4013,7 @@ msgid "If enabled"
msgstr "Se ativado"
msgid "If enabled, access to projects will be validated on an external service using their classification label."
-msgstr "Se ativado, o acesso aos projetos será validado em um serviço externo usando seu rótulo de classificação."
+msgstr "Se ativado, o acesso aos projetos será validado em um serviço externo usando sua etiqueta de classificação."
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "Se estiver usando o GitHub, você verá os status do pipeline no GitHub para seus commits e pull requests. %{more_info_link}"
@@ -3609,6 +4063,9 @@ msgstr "Importar projeto"
msgid "Import projects from Bitbucket"
msgstr "Importar projetos do Bitbucket"
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr "Importar projetos do FogBugz"
@@ -3618,6 +4075,9 @@ msgstr "Importar projetos do GitLab.com"
msgid "Import projects from Google Code"
msgstr "Importar projetos do Google Code"
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr "Importar repositórios do GitHub"
@@ -3628,7 +4088,7 @@ msgid "ImportButtons|Connect repositories from"
msgstr "Conectar repositórios de"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr "Melhore os quadros de issues com GitLab Enterprise Edition."
+msgstr "Melhore os painéis com GitLab Enterprise Edition."
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
msgstr "Melhore o gerenciamento de issues com o peso de issues e GitLab Enterprise Edition."
@@ -3636,18 +4096,33 @@ msgstr "Melhore o gerenciamento de issues com o peso de issues e GitLab Enterpri
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr "Melhore a pesquisa com Advanced Global Search e GitLab Enterprise Edition."
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr "Na próxima etapa, você poderá selecionar os projetos que deseja importar."
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr "Inclua um contrato de Termos de Serviço e uma Política de Privacidade que todos os usuários devem aceitar."
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
+msgstr "Projeto Incompatível"
+
+msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
msgid "Inline"
msgstr "Em linha"
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr "Instalar o GitLab Runner"
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] "Instância"
msgstr[1] "Instâncias"
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr "A instância não suporta múltiplos clusters Kubernetes"
@@ -3677,26 +4158,35 @@ msgstr "Interno - O grupo e projetos internos podem ser visualizados por qualque
msgid "Internal - The project can be accessed by any logged in user."
msgstr "Interno - O projeto pode ser acessado por qualquer usuário autenticado."
+msgid "Internal users"
+msgstr "Usuários internos"
+
msgid "Interval Pattern"
msgstr "Padrão de intervalo"
msgid "Introducing Cycle Analytics"
msgstr "Apresentando a Análise de Ciclo"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
-msgstr "Quadros de issues"
+msgstr "Painéis"
msgid "Issue board focus mode"
-msgstr "Modo de foco no quadro de issues"
+msgstr "Modo de foco nos painéis"
msgid "Issue events"
msgstr "Eventos de issue"
msgid "IssueBoards|Board"
-msgstr "Board"
+msgstr "Painel"
msgid "IssueBoards|Boards"
-msgstr "Quadros"
+msgstr "Painéis"
msgid "Issues"
msgstr "Issues"
@@ -3722,6 +4212,48 @@ msgstr "O job foi apagado"
msgid "Jobs"
msgstr "Jobs"
+msgid "Job|Browse"
+msgstr "Navegar"
+
+msgid "Job|Complete Raw"
+msgstr "Raw completo"
+
+msgid "Job|Download"
+msgstr "Baixar"
+
+msgid "Job|Erase job log"
+msgstr "Apagar log da tarefa"
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr "A tarefa foi apagada"
+
+msgid "Job|Job has been erased by"
+msgstr "A tarefa foi apagada por"
+
+msgid "Job|Keep"
+msgstr "Manter"
+
+msgid "Job|Scroll to bottom"
+msgstr "Rolar para baixo"
+
+msgid "Job|Scroll to top"
+msgstr "Rolar para o topo"
+
+msgid "Job|Show complete raw"
+msgstr "Mostrar raw completo"
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
msgid "Jul"
msgstr "Jul"
@@ -3734,12 +4266,6 @@ msgstr "Jun"
msgid "June"
msgstr "Junho"
-msgid "Koding"
-msgstr "Koding"
-
-msgid "Koding Dashboard"
-msgstr ""
-
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -3774,13 +4300,13 @@ msgid "LFSStatus|Enabled"
msgstr "Habilitado"
msgid "Label"
-msgstr "Label"
+msgstr "Etiqueta"
msgid "Label actions dropdown"
msgstr "Dropdown de ações de etiqueta"
msgid "Label lists show all issues with the selected label."
-msgstr "Listas de etiquetas mostram todas as issues com a etiqueta selecionada."
+msgstr "Listas de etiqueta mostram todas as issues com a etiqueta selecionada."
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr "%{firstLabelName} +%{remainingLabelCount} mais"
@@ -3795,19 +4321,22 @@ msgid "Labels"
msgstr "Etiquetas"
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Labels podem ser aplicadas a %{features}. Labels de grupo estão disponíveis para qualquer projeto dentro do grupo."
+msgstr "Etiquetas podem ser aplicadas à %{features}. Etiquetas de grupo estão disponíveis para qualquer projeto dentro do grupo."
msgid "Labels can be applied to issues and merge requests to categorize them."
-msgstr "Labels podem ser aplicadas a issues e merge requests para categorizá-los."
+msgstr "Etiquetas podem ser aplicadas a issues e merge requests para categorizá-los."
msgid "Labels can be applied to issues and merge requests."
msgstr "Etiquetas podem ser aplicadas a issues e merge requests."
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
-msgstr "<span>Promover label</span> %{labelTitle} <span>para Label do Grupo?</span>"
+msgstr "<span>Promover a etiqueta</span> %{labelTitle} <span>para etiqueta do Grupo?</span>"
msgid "Labels|Promote Label"
-msgstr "Promover Label"
+msgstr "Promover etiqueta"
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr "Promover %{labelTitle} irá disponibilizá-la para todos os projetos dentro de %{groupName}. Etiquetas de projetos existentes com o mesmo título serão mescladas. Esta ação não pode ser revertida."
msgid "Last %d day"
msgid_plural "Last %d days"
@@ -3820,6 +4349,9 @@ msgstr "Último Pipeline"
msgid "Last commit"
msgstr "Último commit"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr "Última edição em %{date}"
@@ -3844,6 +4376,9 @@ msgstr "Últimas modificações"
msgid "Learn more"
msgstr "Saiba mais"
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr "Saiba mais sobre %{issue_boards_url}, para acompanhar issues em diversas listas, usando etiquetas, atribuições e milestones. Se você notar algo faltando nos painéis, por favor, crie uma issue em %{gitlab_issues_url}."
+
msgid "Learn more about Kubernetes"
msgstr "Saiba mais sobre o Kubernetes"
@@ -3866,11 +4401,67 @@ msgid "Leave project"
msgstr "Sair do projeto"
msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
+msgstr "Deixe as opções \"Tipo de arquivo\" e \"Método de entrega\" em seus valores padrão."
msgid "License"
msgstr "Licença"
+msgid "LicenseManagement|Approve license"
+msgstr "Aprovar licença"
+
+msgid "LicenseManagement|Approve license?"
+msgstr "Aprovar licença?"
+
+msgid "LicenseManagement|Approved"
+msgstr "Aprovada"
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr "Adicionar licença à lista negra?"
+
+msgid "LicenseManagement|Blacklisted"
+msgstr "Na lista negra"
+
+msgid "LicenseManagement|License"
+msgstr "Licença"
+
+msgid "LicenseManagement|License Management"
+msgstr "Gerenciamento de Licenças"
+
+msgid "LicenseManagement|License details"
+msgstr "Detalhes da licença"
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr "Pacotes"
+
+msgid "LicenseManagement|Remove license"
+msgstr "Remover licença"
+
+msgid "LicenseManagement|Remove license?"
+msgstr "Remover licença?"
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr "URL"
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr "Licenças"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr "LinkedIn"
@@ -3883,9 +4474,15 @@ msgstr "Listar os seus Repositórios do Gitea"
msgid "List available repositories"
msgstr "Listar repositórios disponíveis"
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr "Listar os seus repositórios no GitHub"
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr "Carregando estados de contribuição para membros de grupo"
@@ -3904,6 +4501,9 @@ msgstr "Bloquear %{issuableDisplayName}"
msgid "Lock not found"
msgstr "Bloqueio não encontrado"
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr "Travar para projetos existentes"
@@ -3920,15 +4520,18 @@ msgid "Locks give the ability to lock specific file or folder."
msgstr "Travas possibilitam travar um arquivo ou uma pasta específica."
msgid "Logs"
-msgstr ""
+msgstr "Logs"
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr "Faça com que todos em sua equipe sejam mais produtivos, independentemente da localização deles. O GitLab Geo cria espelhos somente leitura de sua instância do GitLab para que você possa reduzir o tempo necessário para clonar e buscar grandes repositórios."
msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
+msgstr "Certifique-se de que você está conectado à conta que possui os projetos que você deseja importar."
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr "Gerencie repositórios Git com controles de acesso refinados que mantêm seu código seguro. Realize revisões de código e aprimore a colaboração com merge requests. Cada projeto também pode ter um rastreador de issue e um wiki."
+
+msgid "Manage Web IDE features"
msgstr ""
msgid "Manage access"
@@ -3944,13 +4547,13 @@ msgid "Manage applications that you've authorized to use your account."
msgstr "Gerencie aplicativos que você autorizou a utilizar sua conta."
msgid "Manage group labels"
-msgstr "Gerenciar Labels de grupo"
+msgstr "Gerenciar etiquetas de grupo"
msgid "Manage labels"
msgstr "Gerenciar etiquetas"
msgid "Manage project labels"
-msgstr "Gerenciar Labels de projetos"
+msgstr "Gerenciar etiquetas de projetos"
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr "Gerencie a qualidade de membros do seu grupo ao adicionar outro nível de segurança com o SAML."
@@ -3962,16 +4565,16 @@ msgid "Manifest file import"
msgstr "Importação de arquivo de manifesto"
msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
+msgstr "Associar um ID de conta do FogBugz para um usuário do GitLab"
msgid "Map a Google Code user to a GitLab user"
-msgstr ""
+msgstr "Associar um usuário do Google Code para um usuário do GitLab"
msgid "Map a Google Code user to a full email address"
-msgstr ""
+msgstr "Associar usuário do Google Code para um endereço de e-mail completo"
msgid "Map a Google Code user to a full name"
-msgstr ""
+msgstr "Associar usuário do Google Code para um nome completo"
msgid "Mar"
msgstr "Mar"
@@ -3985,9 +4588,21 @@ msgstr "Marcar como concluído"
msgid "Markdown enabled"
msgstr "Markdown habilitado"
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "Máximo de falhas do git storage"
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr "Mai"
@@ -4036,15 +4651,15 @@ msgstr "Falha ao salvar comentário"
msgid "MergeRequests|Toggle comments for this file"
msgstr "Ativar/desativar comentários para este arquivo"
-msgid "MergeRequests|Updating discussions failed"
-msgstr "A atualização de discussões falhou"
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr "Visualizar o arquivo @ %{commitId}"
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr "Ver arquivo substituído @ %{commitId}"
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr "Merge realizado"
@@ -4060,6 +4675,9 @@ msgstr "Métricas - Influx"
msgid "Metrics - Prometheus"
msgstr "Métricas - Prometheus"
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr "Negócios"
@@ -4079,7 +4697,7 @@ msgid "Metrics|For grouping similar metrics"
msgstr "Para agrupar métricas similares"
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
-msgstr "Rotula do eixo vertical do gráfico. Geralmente, o tipo da unidade que está sendo mapeada. O eixo horizontal (eixo X) representa sempre o tempo."
+msgstr "Rótulo do eixo vertical do gráfico. Geralmente, o tipo da unidade que está sendo mapeada. O eixo horizontal (eixo X) representa sempre o tempo."
msgid "Metrics|Learn about environments"
msgstr "Aprenda sobre ambientes"
@@ -4097,74 +4715,89 @@ msgid "Metrics|New metric"
msgstr "Nova métrica"
msgid "Metrics|No deployed environments"
-msgstr ""
+msgstr "Nenhum ambiente implantado"
msgid "Metrics|Prometheus Query Documentation"
-msgstr ""
+msgstr "Documentação de consulta do Prometheus"
msgid "Metrics|Query"
-msgstr ""
+msgstr "Consulta"
msgid "Metrics|Response"
-msgstr ""
+msgstr "Resposta"
msgid "Metrics|System"
msgstr "Sistema"
msgid "Metrics|There was an error fetching the environments data, please try again"
-msgstr ""
+msgstr "Houve um erro ao obter os dados do ambiente; por favor, tente novamente"
msgid "Metrics|There was an error getting deployment information."
-msgstr ""
+msgstr "Houve um erro ao obter informações de deploy."
msgid "Metrics|There was an error getting environments information."
-msgstr ""
+msgstr "Houve um erro ao obter informações dos ambientes."
msgid "Metrics|There was an error while retrieving metrics"
-msgstr ""
+msgstr "Ocorreu um erro ao recuperar as métricas"
msgid "Metrics|Type"
msgstr "Tipo"
msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
-msgstr ""
+msgstr "Resposta de dados de implantação inesperada da outra ponta do prometheus"
msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
-msgstr ""
+msgstr "Resposta de dados de métricas inesperada da outra ponta do prometheus"
msgid "Metrics|Unit label"
-msgstr ""
+msgstr "Rótulo de unidade"
msgid "Metrics|Used as a title for the chart"
msgstr "Usado como um título para o gráfico"
msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
-msgstr ""
+msgstr "Usado se a consulta retornar uma única série. Se ele retornar várias séries, seus rótulos de legenda serão retirados da resposta."
msgid "Metrics|Y-axis label"
-msgstr ""
+msgstr "Rótulo do eixo Y"
msgid "Metrics|e.g. HTTP requests"
-msgstr ""
+msgstr "ex., requisições HTTP"
msgid "Metrics|e.g. Requests/second"
-msgstr ""
+msgstr "ex., Requisições/segundo"
msgid "Metrics|e.g. Throughput"
-msgstr ""
+msgstr "ex., Taxa de transferência"
msgid "Metrics|e.g. rate(http_requests_total[5m])"
-msgstr ""
+msgstr "ex., rate(http_requests_total[5m])"
msgid "Metrics|e.g. req/sec"
-msgstr ""
+msgstr "ex., req/seg"
msgid "Milestone"
msgstr "Milestone"
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr "Milestones"
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr "Excluir Milestone"
@@ -4183,9 +4816,30 @@ msgstr "Promover Milestone %{milestoneTitle} de projeto para Milestone de grupo?
msgid "Milestones|Promote Milestone"
msgstr "Promover Milestone"
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr "Essa ação não pode ser revertida."
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "adicione uma chave SSH"
@@ -4204,9 +4858,6 @@ msgstr "Meses"
msgid "More"
msgstr "Mais"
-msgid "More actions"
-msgstr "Mais ações"
-
msgid "More info"
msgstr "Mais informações"
@@ -4226,16 +4877,16 @@ msgid "Move issue"
msgstr "Mover issue"
msgid "Multiple issue boards"
-msgstr ""
+msgstr "Multiplos painéis"
msgid "Name"
msgstr "Nome"
msgid "Name new label"
-msgstr "Nome da nova label"
+msgstr "Nome da nova etiqueta"
msgid "Name your individual key via a title"
-msgstr ""
+msgstr "Nomeie sua chave individual por meio de um título"
msgid "Name:"
msgstr "Nome:"
@@ -4253,19 +4904,22 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "Saia e faça login com uma conta diferente"
msgid "Network"
+msgstr "Rede"
+
+msgid "Never"
msgstr ""
msgid "New"
msgstr ""
msgid "New Application"
-msgstr ""
+msgstr "Novo aplicativo"
msgid "New Group"
-msgstr ""
+msgstr "Novo Grupo"
msgid "New Identity"
-msgstr ""
+msgstr "Nova identidade"
msgid "New Issue"
msgid_plural "New Issues"
@@ -4273,16 +4927,16 @@ msgstr[0] "Nova Issue"
msgstr[1] "Novas Issues"
msgid "New Label"
-msgstr ""
+msgstr "Nova etiqueta"
msgid "New Pipeline Schedule"
msgstr "Novo Agendamento de Pipeline"
msgid "New Snippet"
-msgstr ""
+msgstr "Novo Snippet"
msgid "New Snippets"
-msgstr ""
+msgstr "Novos Snippets"
msgid "New branch"
msgstr "Novo branch"
@@ -4294,7 +4948,7 @@ msgid "New directory"
msgstr "Novo diretório"
msgid "New epic"
-msgstr ""
+msgstr "Novo épico"
msgid "New file"
msgstr "Novo arquivo"
@@ -4303,19 +4957,19 @@ msgid "New group"
msgstr "Novo grupo"
msgid "New identity"
-msgstr ""
+msgstr "Nova identidade"
msgid "New issue"
msgstr "Nova issue"
msgid "New label"
-msgstr "Nova label"
+msgstr "Nova etiqueta"
msgid "New merge request"
msgstr "Novo merge request"
msgid "New pipelines will cancel older, pending pipelines on the same branch"
-msgstr ""
+msgstr "Novos pipelines cancelarão pipelines pendentes mais antigos no mesmo branch"
msgid "New project"
msgstr "Novo projeto"
@@ -4333,23 +4987,32 @@ msgid "New tag"
msgstr "Nova tag"
msgid "New..."
-msgstr ""
+msgstr "Novo..."
msgid "No"
msgstr "Não"
msgid "No Label"
-msgstr ""
+msgstr "Sem etiqueta"
msgid "No assignee"
msgstr "Sem responsável"
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr "Sem alterarções"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "Nenhuma conexão pode ser feita para um servidor Gitaly, por favor check os logs!"
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr "Sem validade"
@@ -4366,13 +5029,16 @@ msgid "No files found."
msgstr "Nenhum arquivo encontrado."
msgid "No issues for the selected time period."
-msgstr ""
+msgstr "Sem issues para o período de tempo selecionado."
msgid "No labels with such name or description"
+msgstr "Sem etiquetas com esse nome ou descrição"
+
+msgid "No license. All rights reserved"
msgstr ""
msgid "No merge requests for the selected time period."
-msgstr ""
+msgstr "Sem merge requests para o período de tempo selecionado."
msgid "No merge requests found"
msgstr "Nenhum merge requests encontrado"
@@ -4381,29 +5047,44 @@ msgid "No messages were logged"
msgstr "Nenhuma mensagem foi registrada"
msgid "No other labels with such name or description"
+msgstr "Sem outras etiquetas com esse nome ou descrição"
+
+msgid "No packages stored for this project."
msgstr ""
msgid "No prioritised labels with such name or description"
-msgstr ""
+msgstr "Sem etiquetas priorizadas com esse nome ou descrição"
msgid "No public groups"
-msgstr ""
+msgstr "Nenhum grupo público"
msgid "No pushes for the selected time period."
-msgstr ""
+msgstr "Nenhum push para o período de tempo selecionado."
msgid "No repository"
msgstr "Nenhum repositório"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "Nenhum agendamento"
msgid "No, directly import the existing email addresses and usernames."
+msgstr "Não, importe diretamente os endereços de e-mail e nomes de usuários existentes."
+
+msgid "Nodes"
msgstr ""
msgid "None"
msgstr "Nenhum"
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr "Merge não permitido"
@@ -4422,23 +5103,26 @@ msgstr "Não confidencial"
msgid "Not enough data"
msgstr "Dados insuficientes"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Observe que o branch master é automaticamente protegido. %{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "Nota: Como administrador, você pode configurar o %{github_integration_link}, que permitirá o login via GitHub e permitirá a conexão com repositórios sem gerar um Token de Acesso Pessoal."
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "Nota: Como administrador, você pode configurar o %{github_integration_link}, que permitirá o login via GitHub e permitirá a importação de repositórios sem gerar um Token de Acesso Pessoal."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "Nota: Considere pedir ao seu administrador do GitLab para configurar %{github_integration_link}, o que permitirá o login via GitHub e permitirá a conexão com repositórios sem gerar um Token de Acesso Pessoal."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "Nota: Considere pedir ao seu administrador do GitLab para configurar %{github_integration_link}, o que permitirá o login via GitHub e permitir a importação de repositórios sem gerar um Token de Acesso Pessoal."
msgid "Notes|Are you sure you want to cancel creating this comment?"
-msgstr ""
+msgstr "Você tem certeza que quer cancelar a criação deste comentário?"
msgid "Notification events"
msgstr "Eventos de notificação"
@@ -4455,6 +5139,9 @@ msgstr "Falha no pipeline"
msgid "NotificationEvent|Merge merge request"
msgstr "Aceitar merge request"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "Nova issue"
@@ -4513,7 +5200,7 @@ msgid "Number of access attempts"
msgstr "Número de tentativas de acesso"
msgid "OK"
-msgstr ""
+msgstr "OK"
msgid "Oct"
msgstr "Out"
@@ -4525,28 +5212,36 @@ msgid "OfSearchInADropdown|Filter"
msgstr "Filtrar"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
-msgstr ""
+msgstr "Uma vez importados, repositórios podem ser espelhados por SSH. Leia mais em %{ssh_link}"
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
+msgstr "Um ou mais dos seus projetos do Bitbucket não podem ser importados diretamente no GitLab porque eles usam Subversion ou Mercurial para o controle de versão, ao invés de Git."
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
+msgstr "Um ou mais dos seus projetos do Google Code não podem ser importados diretamente no GitLab porque eles usam Subversion ou Mercurial para o controle de versão, ao invés de Git."
-msgid "Online IDE integration settings."
-msgstr "Configurações de integração on-line do IDE."
+msgid "Only admins"
+msgstr ""
msgid "Only comments from the following commit are shown below"
+msgstr "Somente comentários do commit a seguir são mostrados abaixo"
+
+msgid "Only mirror protected branches"
msgstr ""
msgid "Only project members can comment."
msgstr "Somente membros do projeto podem comentar."
msgid "Oops, are you sure?"
-msgstr ""
+msgstr "Oops, você tem certeza?"
msgid "Open"
-msgstr ""
+msgstr "Abrir"
msgid "Open in Xcode"
msgstr "Abrir no Xcode"
@@ -4555,7 +5250,7 @@ msgid "Open sidebar"
msgstr "Abrir barra lateral"
msgid "Open source software to collaborate on code"
-msgstr ""
+msgstr "Software de código aberto para colaborar no código"
msgid "Opened"
msgstr "Aberto"
@@ -4576,22 +5271,22 @@ msgid "Operations"
msgstr "Operações"
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
-msgstr ""
+msgstr "Opcionalmente, você pode %{link_to_customize} como os endereços de e-mail e nomes de usuários do FogBugz são importados para o GitLab."
msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
+msgstr "Opcionalmente, você pode %{link_to_customize} como os endereços de e-mail e nomes de usuários do Google Code são importados para o GitLab."
msgid "Options"
msgstr "Opções"
msgid "Or you can choose one of the suggested colors below"
-msgstr ""
+msgstr "Ou você pode escolher uma das cores sugeridas abaixo"
msgid "Other Labels"
-msgstr "Outros Labels"
+msgstr "Outras etiquetas"
msgid "Other information"
-msgstr ""
+msgstr "Outra informação"
msgid "Otherwise it is recommended you start with one of the options below."
msgstr "Caso contrário, é recomendado que você inicie com uma das opções abaixo."
@@ -4602,9 +5297,21 @@ msgstr "Pedidos de saída"
msgid "Overview"
msgstr "Visão geral"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "Proprietário"
+msgid "Package information"
+msgstr "Informações do pacote"
+
+msgid "Package was removed"
+msgstr "O pacote foi removido"
+
+msgid "Packages"
+msgstr "Pacotes"
+
msgid "Pages"
msgstr "Páginas"
@@ -4630,17 +5337,23 @@ msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh
msgstr "Cole a sua chave SSH pública, que geralmente é encontrada no arquivo '~/.ssh/id_rsa.pub' e começa com 'ssh-rsa'. Não use a sua chave SSH privada."
msgid "Path:"
-msgstr ""
+msgstr "Caminho:"
msgid "Pause"
msgstr "Pausar"
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr "Pendente"
-msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgid "People without permission will never get a notification and won't be able to comment."
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr "Por trabalho. Se um trabalho ultrapassar esse limite, ele será marcado como falho"
+
msgid "Perform advanced options such as changing path, transferring, or removing the group."
msgstr "Execute opções avançadas, como alterar o caminho, transferir ou remover o grupo."
@@ -4656,6 +5369,9 @@ msgstr "Token de Acesso Pessoal"
msgid "Pipeline"
msgstr "Pipeline"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "Saúde da Pipeline"
@@ -4666,10 +5382,10 @@ msgid "Pipeline Schedules"
msgstr "Agendamentos da Pipeline"
msgid "Pipeline quota"
-msgstr ""
+msgstr "Quota de pipeline"
msgid "Pipeline triggers"
-msgstr ""
+msgstr "Gatilho de pipeline"
msgid "PipelineCharts|Failed:"
msgstr "Falhou:"
@@ -4743,6 +5459,9 @@ msgstr "CI Lint"
msgid "Pipelines|Clear Runner Caches"
msgstr "Limpar cache dos Runners"
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr "Saiba como funcionam as pipelines"
@@ -4764,6 +5483,9 @@ msgstr "Atualmente, não há pipelines de %{scope}."
msgid "Pipelines|There are currently no pipelines."
msgstr "Atualmente não há pipelines."
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "Este projeto não está atualmente configurado para executar pipelines."
@@ -4810,13 +5532,7 @@ msgid "Pipeline|with stages"
msgstr "com etapas"
msgid "Plain diff"
-msgstr ""
-
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
+msgstr "Diff simples"
msgid "PlantUML"
msgstr "PlantUML"
@@ -4828,13 +5544,13 @@ msgid "Please accept the Terms of Service before continuing."
msgstr "Por favor, aceite os Termos de Serviço antes de continuar."
msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
-msgstr ""
+msgstr "Por favor, converta-os para %{link_to_git} e passe pelo %{link_to_import_flow} novamente."
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
+msgstr "Por favor, converta-os em Git no Google Code e passe pelo %{link_to_import_flow} novamente."
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
-msgstr ""
+msgstr "Por favor, note que esse aplicativo não é fornecido pelo GitLab e você deve verificar a sua autenticidade antes de permitir o acesso."
msgid "Please select at least one filter to see results"
msgstr "Por favor selecione pelo menos um filtro para ver os resultados"
@@ -4846,7 +5562,7 @@ msgid "Please try again"
msgstr "Por favor, tente novamente"
msgid "Please wait while we connect to your repository. Refresh at will."
-msgstr ""
+msgstr "Por favor, aguarde enquanto conectamos ao seu repositório. Atualize à vontade."
msgid "Please wait while we import the repository for you. Refresh at will."
msgstr "Por favor, aguarde enquanto importamos o repositório para você. Atualize à vontade."
@@ -4857,20 +5573,29 @@ msgstr "Preferências"
msgid "Preferences|Navigation theme"
msgstr "Tema de navegação"
-msgid "Primary"
+msgid "Press Enter or click to search"
+msgstr "Pressione Enter ou clique para pesquisar"
+
+msgid "Preview"
+msgstr "Pré-visualizar"
+
+msgid "Preview payload"
msgstr ""
+msgid "Primary"
+msgstr "Primário"
+
msgid "Prioritize"
msgstr "Priorizar"
msgid "Prioritize label"
-msgstr "Priorizar label"
+msgstr "Priorizar etiqueta"
msgid "Prioritized Labels"
-msgstr "Labels Priorizadas"
+msgstr "Etiquetas priorizadas"
msgid "Prioritized label"
-msgstr "Label priorizada"
+msgstr "Etiqueta priorizada"
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Privado - O acesso ao projeto deve ser concedido explicitamente para cada usuário."
@@ -4885,6 +5610,15 @@ msgid "Profile"
msgstr "Perfil"
msgid "Profile Settings"
+msgstr "Configurações do perfil"
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr "Você está prestes a excluir permanentemente a %{yourAccount}, e todas as issues, merge requests e grupos vinculados a sua conta. Depois de confirmar clicando em %{deleteAccount}, isso não poderá ser desfeito ou recuperado."
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr "Você vai alterar o nome de usuário %{currentUsernameBold} para %{newUsernameBold}. O perfil e os projetos serão redirecionados para %{newUsername} mas esse redirecionamento expirará quando %{currentUsername} for registrado por outro usuário ou grupo. Por favor, atualize seus repositórios remotos Git o mais rápido possível."
+
+msgid "Profiles|%{author_name} made a private contribution"
msgstr ""
msgid "Profiles|Account scheduled for removal."
@@ -4893,12 +5627,33 @@ msgstr "Conta agendada para remoção."
msgid "Profiles|Add key"
msgstr "Adicionar chave"
+msgid "Profiles|Add status emoji"
+msgstr "Adicionar emoji de status"
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Alterar nome de usuário"
+msgid "Profiles|Choose file..."
+msgstr "Escolher arquivo..."
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr "Limpar status"
+
msgid "Profiles|Current path: %{path}"
msgstr "Caminho atual: %{path}"
+msgid "Profiles|Current status"
+msgstr "Status atual"
+
msgid "Profiles|Delete Account"
msgstr "Excluir conta"
@@ -4911,33 +5666,108 @@ msgstr "Deseja apagar sua conta?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr "Apagando conta tem os seguintes efeitos:"
+msgid "Profiles|Do not show on profile"
+msgstr "Não mostrar no perfil"
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr "Não exibir informações pessoais relacionadas à atividade em seus perfis"
+
+msgid "Profiles|Edit Profile"
+msgstr "Editar perfil"
+
msgid "Profiles|Invalid password"
msgstr "Senha inválida"
msgid "Profiles|Invalid username"
msgstr "Nome de usuário inválido"
+msgid "Profiles|Main settings"
+msgstr "Configurações principais"
+
+msgid "Profiles|No file chosen"
+msgstr "Nenhum arquivo escolhido"
+
msgid "Profiles|Path"
msgstr "Caminho"
+msgid "Profiles|Position and size your new avatar"
+msgstr "Posicione e dimensione seu novo avatar"
+
+msgid "Profiles|Private contributions"
+msgstr "Contribuições privadas"
+
+msgid "Profiles|Public Avatar"
+msgstr "Avatar público"
+
+msgid "Profiles|Remove avatar"
+msgstr "Remover avatar"
+
+msgid "Profiles|Set new profile picture"
+msgstr "Definir nova foto de perfil"
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr "Conte-nos sobre você em menos de 250 caracteres."
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr "O tamanho máximo de arquivo permitido é de 200KB."
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr "Isso não se parece com uma chave pública SSH, você tem certeza que gostaria de adicioná-la?"
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr "Este e-mail será exibido no seu perfil público."
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr "Este emoji e mensagem aparecerão no seu perfil e em toda a interface."
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr "Esta informação aparecerá no seu perfil."
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
-msgstr "Escreva %{confirmationValue} para confirmar:"
+msgstr "Escreva sua %{confirmationValue} para confirmar:"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr "Geralmente se inicia com \"ssh-rsa …\""
+msgid "Profiles|Update profile settings"
+msgstr "Atualizar configurações do perfil"
+
msgid "Profiles|Update username"
msgstr "Atualizar nome de usuário"
+msgid "Profiles|Upload new avatar"
+msgstr "Enviar novo avatar"
+
msgid "Profiles|Username change failed - %{message}"
msgstr "Falha na alteração de nome de usuário - %{message}"
msgid "Profiles|Username successfully changed"
msgstr "Alteração de nome de usuário realizada com sucesso"
+msgid "Profiles|Website"
+msgstr "Website"
+
+msgid "Profiles|What's your status?"
+msgstr "Qual é o seu status?"
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "Você não tem permissão para apagar esse usuário."
@@ -4947,9 +5777,21 @@ msgstr "Você precisa delegar outro usuário para ser dono ou apagar esses grupo
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Sua conta é atualmente proprietária dos seguintes grupos:"
-msgid "Profiles|e.g. My MacBook key"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
msgstr ""
+msgid "Profiles|Your status"
+msgstr "Seu status"
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr "por exemplo, Chave do meu MacBook"
+
msgid "Profiles|your account"
msgstr "sua conta"
@@ -4980,6 +5822,9 @@ msgstr "Projeto '%{project_name}' atualizado com sucesso."
msgid "Project Badges"
msgstr "Selos de projeto"
+msgid "Project URL"
+msgstr "URL do projeto"
+
msgid "Project access must be granted explicitly to each user."
msgstr "Acesso ao projeto deve ser concedido explicitamente para cada usuário."
@@ -5005,25 +5850,28 @@ msgid "Project export started. A download link will be sent by email."
msgstr "Exportação do projeto iniciada. Um link para baixá-la será enviado por email."
msgid "Project name"
-msgstr ""
+msgstr "Nome do projeto"
+
+msgid "Project slug"
+msgstr "Slug do projeto"
msgid "ProjectActivityRSS|Subscribe"
msgstr "Inscreva-se"
msgid "ProjectCreationLevel|Allowed to create projects"
-msgstr ""
+msgstr "Permitido criar projetos"
msgid "ProjectCreationLevel|Default project creation protection"
-msgstr ""
+msgstr "Proteção padrão de criação de projeto"
msgid "ProjectCreationLevel|Developers + Maintainers"
-msgstr ""
+msgstr "Desenvolvedores + Mantenedores"
msgid "ProjectCreationLevel|Maintainers"
-msgstr ""
+msgstr "Mantenedores"
msgid "ProjectCreationLevel|No one"
-msgstr ""
+msgstr "Ninguém"
msgid "ProjectFileTree|Name"
msgstr "Nome"
@@ -5034,29 +5882,59 @@ msgstr "Nunca"
msgid "ProjectLifecycle|Stage"
msgstr "Etapa"
-msgid "ProjectPage|Project ID: %{project_id}"
+msgid "ProjectOverview|Fork"
msgstr ""
-msgid "ProjectSettings|Contact an admin to change this setting."
+msgid "ProjectOverview|Forks"
msgstr ""
-msgid "ProjectSettings|Failed to protect the tag"
+msgid "ProjectOverview|Go to your fork"
msgstr ""
-msgid "ProjectSettings|Failed to update tag!"
+msgid "ProjectOverview|Star"
msgstr ""
-msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgid "ProjectOverview|Unstar"
msgstr ""
-msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgid "ProjectOverview|You have reached your project limit"
msgstr ""
-msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgid "ProjectOverview|You must sign in to star a project"
msgstr ""
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr "ID do Projeto: %{project_id}"
+
+msgid "ProjectSettings|Badges"
+msgstr "Selos"
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr "Contate um administrador para alterar essa configuração."
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr "Personalize os selos do seu projeto."
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr "Falha ao proteger a tag"
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr "Falha ao atualizar a tag!"
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr "Saiba mais sobre os selos."
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr "Apenas para commits assinados pode-se fazer push para este repositório."
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr "Essa configuração está aplicada no nível do servidor e pode ser sobrescrita por um administrador."
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr "Essa configuração está aplicada no nível do servidor, mas foi sobrescrita para este projeto."
+
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr ""
+msgstr "Essa configuração será aplicada a todos os projetos a menos que seja sobrescrita por um administrador."
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
msgstr "Usuários só podem fazer push para este repositório com commits que contenham um de seus e-mails verificados."
@@ -5065,7 +5943,7 @@ msgid "Projects"
msgstr "Projetos"
msgid "Projects shared with %{group_name}"
-msgstr ""
+msgstr "Projetos compartilhados com %{group_name}"
msgid "ProjectsDropdown|Frequently visited"
msgstr "Visitados frequentemente"
@@ -5085,35 +5963,38 @@ msgstr "Algo deu errado do nosso lado."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Desculpe, nenhum projeto corresponde a sua pesquisa"
-msgid "PrometheusAlerts|Add alert"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
msgstr ""
+msgid "PrometheusAlerts|Add alert"
+msgstr "Adicionar alerta"
+
msgid "PrometheusAlerts|Alert set"
-msgstr ""
+msgstr "PrometheusAlerts|Definir Alerta"
msgid "PrometheusAlerts|Edit alert"
-msgstr ""
+msgstr "Editar alerta"
msgid "PrometheusAlerts|Error creating alert"
-msgstr ""
+msgstr "Erro ao criar alerta"
msgid "PrometheusAlerts|Error deleting alert"
-msgstr ""
+msgstr "Erro ao excluir alerta"
msgid "PrometheusAlerts|Error fetching alert"
-msgstr ""
+msgstr "Erro ao buscar alerta"
msgid "PrometheusAlerts|Error saving alert"
-msgstr ""
+msgstr "Erro ao salvar alerta"
msgid "PrometheusAlerts|No alert set"
-msgstr ""
+msgstr "Nenhum alerta definido"
msgid "PrometheusAlerts|Operator"
-msgstr ""
+msgstr "Operador"
msgid "PrometheusAlerts|Threshold"
-msgstr ""
+msgstr "Tolerância"
msgid "PrometheusDashboard|Time"
msgstr "Tempo"
@@ -5140,16 +6021,16 @@ msgid "PrometheusService|Common metrics"
msgstr "Métricas comuns"
msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
-msgstr ""
+msgstr "Métricas comuns são monitoradas automaticamente com base em uma biblioteca de métricas de exportadores populares."
msgid "PrometheusService|Custom metrics"
-msgstr ""
+msgstr "Métricas personalizadas"
msgid "PrometheusService|Finding and configuring metrics..."
msgstr "Encontrando e configurando métricas..."
msgid "PrometheusService|Finding custom metrics..."
-msgstr ""
+msgstr "Encontrando métricas personalizadas..."
msgid "PrometheusService|Install Prometheus on clusters"
msgstr "Instale o Prometheus nos clusters"
@@ -5170,7 +6051,7 @@ msgid "PrometheusService|More information"
msgstr "Mais informações"
msgid "PrometheusService|New metric"
-msgstr ""
+msgstr "Nova métrica"
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "URL da API base do Prometheus. como http://prometheus.example.com/"
@@ -5179,7 +6060,7 @@ msgid "PrometheusService|Prometheus is being automatically managed on your clust
msgstr "Prometheus está sendo automaticamente gerenciado nos seus clusters"
msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
-msgstr ""
+msgstr "Essas métricas serão monitoradas após sua primeira implantação para um ambiente"
msgid "PrometheusService|Time-series monitoring service"
msgstr "Serviço de monitoramento de tempo-de-série"
@@ -5206,25 +6087,73 @@ msgid "Promote to group label"
msgstr "Promover para etiqueta de grupo"
msgid "Promotions|Don't show me this again"
-msgstr ""
+msgstr "Não me mostre isso novamente"
msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
-msgstr ""
+msgstr "Epics permitem que você gerencie seu portfólio de projetos com mais eficiência e com menos esforço rastreando grupos de issues que compartilham um tema, entre projetos e milestones."
msgid "Promotions|This feature is locked."
-msgstr ""
+msgstr "Esse recurso está bloqueado."
msgid "Promotions|Upgrade plan"
+msgstr "Aprimorar plano"
+
+msgid "Protected"
+msgstr "Protegido"
+
+msgid "Protected Environments"
+msgstr "Ambientes Protegidos"
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr "Ambiente"
+
+msgid "ProtectedEnvironment|Protect"
+msgstr "Proteger"
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr "Desproteger"
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
msgstr ""
msgid "Protip:"
msgstr "Dicas:"
msgid "Provider"
-msgstr ""
+msgstr "Provedor"
msgid "Pseudonymizer data collection"
-msgstr ""
+msgstr "Coleção de dados Pseudonymizer"
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Público - O grupo e seus projetos podem ser visualizados por todos sem autenticação."
@@ -5235,8 +6164,14 @@ msgstr "Público - O projeto pode ser acessado sem nenhuma autenticação."
msgid "Public pipelines"
msgstr "Pipelines públicos"
+msgid "Pull"
+msgstr "Pull"
+
+msgid "Push"
+msgstr "Push"
+
msgid "Push Rules"
-msgstr ""
+msgstr "Regras de push"
msgid "Push events"
msgstr "Eventos de push"
@@ -5248,16 +6183,16 @@ msgid "Push to create a project"
msgstr "Push para criar um projeto"
msgid "PushRule|Committer restriction"
-msgstr ""
+msgstr "Restrição de committer"
msgid "Pushed"
-msgstr ""
+msgstr "Push realizado"
msgid "Pushes"
-msgstr ""
+msgstr "Pushes"
msgid "Quarters"
-msgstr ""
+msgstr "Trimestres"
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "Ações rápidas podem ser usadas nas descrições das issues e nas caixas de comentário."
@@ -5266,7 +6201,7 @@ msgid "Read more"
msgstr "Leia mais"
msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
-msgstr ""
+msgstr "Saiba mais sobre as permissões do projeto <strong>%{link_to_help}</strong>"
msgid "Readme"
msgstr "Leia-me"
@@ -5274,10 +6209,24 @@ msgstr "Leia-me"
msgid "Real-time features"
msgstr "Recursos em tempo real"
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr "Referência:"
msgid "Refresh"
+msgstr "Atualizar"
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr "Gerar uma nova chave"
+
+msgid "Regex pattern"
msgstr ""
msgid "Register / Sign In"
@@ -5287,7 +6236,7 @@ msgid "Register and see your runners for this group."
msgstr "Registre-se e veja seus runners para este grupo."
msgid "Register and see your runners for this project."
-msgstr ""
+msgstr "Registre-se e veja seus runners para este projeto."
msgid "Registry"
msgstr "Registro"
@@ -5331,26 +6280,86 @@ msgstr "Remover prioridade"
msgid "Remove project"
msgstr "Remover projeto"
-msgid "Repair authentication"
+msgid "Rename"
+msgstr "Renomear"
+
+msgid "Rename file"
+msgstr "Renomear arquivo"
+
+msgid "Rename folder"
+msgstr "Renomear pasta"
+
+msgid "Reopen epic"
msgstr ""
+msgid "Repair authentication"
+msgstr "Corrigir autenticação"
+
msgid "Reply to this email directly or %{view_it_on_gitlab}."
-msgstr ""
+msgstr "Responda a este e-mail diretamente ou %{view_it_on_gitlab}."
msgid "Repo by URL"
+msgstr "Repositório por URL"
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr "Classe"
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr "Tempo de execução"
+
+msgid "Reports|Failure"
+msgstr "Falha"
+
+msgid "Reports|More info"
+msgstr "Mais informações"
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr "Gravidade"
+
+msgid "Reports|System output"
+msgstr "Saída do sistema"
+
+msgid "Reports|Test summary"
+msgstr "Resumo do teste"
+
+msgid "Reports|Test summary failed loading results"
+msgstr "Resumo do teste falhou ao carregar os resultados"
+
+msgid "Reports|Test summary results are being parsed"
+msgstr "Os resultados do resumo de teste estão sendo analisados"
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
msgstr ""
msgid "Repository"
msgstr "Repositório"
msgid "Repository Settings"
-msgstr ""
+msgstr "Configurações do Repositório"
msgid "Repository URL"
-msgstr ""
+msgstr "URL do repositório"
msgid "Repository has no locks."
-msgstr ""
+msgstr "O repositório não possui bloqueios."
msgid "Repository maintenance"
msgstr "Manutenção do repositório"
@@ -5362,13 +6371,13 @@ msgid "Repository storage"
msgstr "Armazenamento do Repositório"
msgid "RepositorySettingsAccessLevel|Select"
-msgstr ""
+msgstr "Selecionar"
msgid "Request Access"
msgstr "Solicitar acesso"
msgid "Requests Profiles"
-msgstr ""
+msgstr "Solicita Perfis"
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "Exija que todos os usuários aceitem Termos de Serviço e Política de Privacidade quando acessarem o GitLab."
@@ -5383,7 +6392,7 @@ msgid "Reset runners registration token"
msgstr "Recriar o token de registro de runners"
msgid "Resolve all discussions in new issue"
-msgstr ""
+msgstr "Resolver todas discussões em novo issue"
msgid "Resolve conflicts on source branch"
msgstr "Resolver conflitos na branch de origem"
@@ -5391,7 +6400,19 @@ msgstr "Resolver conflitos na branch de origem"
msgid "Resolve discussion"
msgstr "Resolver discussão"
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
+msgstr "Métricas de resposta (personalizadas)"
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
msgstr ""
msgid "Resume"
@@ -5406,6 +6427,9 @@ msgstr "Tentar novamente este trabalho"
msgid "Retry verification"
msgstr "Tentar novamente a verificação"
+msgid "Reveal Variables"
+msgstr "Revelar variáveis"
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] "Mostrar valor"
@@ -5421,7 +6445,7 @@ msgid "Review"
msgstr "Revisar"
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
-msgstr ""
+msgstr "Revise o processo de configuração de provedores de serviços em seu provedor de identidade - nesse caso, o GitLab é o \"provedor de serviços\" ou a \"terceiro\"."
msgid "Reviewing"
msgstr "Revisão"
@@ -5430,15 +6454,33 @@ msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr "Revisando (merge request !%{mergeRequestId})"
msgid "Revoke"
-msgstr ""
+msgstr "Revogar"
msgid "Roadmap"
-msgstr ""
+msgstr "Roadmap"
msgid "Run CI/CD pipelines for external repositories"
+msgstr "Executar pipelines CI/CD para repositórios externos"
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
msgstr ""
msgid "Runner token"
+msgstr "Token de runner"
+
+msgid "Runner will not receive any new jobs"
msgstr ""
msgid "Runners"
@@ -5450,35 +6492,59 @@ msgstr "Runners de API"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr "Os corredores podem ser colocados em usuários, servidores e até mesmo em sua máquina local."
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr "Executando"
msgid "SAML SSO"
-msgstr ""
+msgstr "SAML SSO"
msgid "SAML SSO for %{group_name}"
-msgstr ""
+msgstr "SAML SSO para %{group_name}"
msgid "SAML Single Sign On"
-msgstr ""
+msgstr "SAML Single Sign On"
msgid "SAML Single Sign On Settings"
+msgstr "Configurações de SAML Single Sign On"
+
+msgid "SAST"
msgstr ""
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
-msgstr ""
+msgstr "Impressão digital SHA1 do certificado de assinatura de token SAML. Obtenha isso do seu provedor de identidade, onde ele também pode ser chamado de \"Thumbprint\"."
msgid "SSH Keys"
msgstr "Chaves SSH"
-msgid "SSL Verification"
+msgid "SSH host keys"
msgstr ""
+msgid "SSH public key"
+msgstr "Chave SSH pública"
+
+msgid "SSL Verification"
+msgstr "Verificação SSL"
+
msgid "Save"
-msgstr ""
+msgstr "Salvar"
msgid "Save application"
-msgstr ""
+msgstr "Salvar aplicativo"
msgid "Save changes"
msgstr "Salvar alterações"
@@ -5502,13 +6568,13 @@ msgid "Scheduling Pipelines"
msgstr "Agendando pipelines"
msgid "Scope"
-msgstr ""
+msgstr "Escopo"
msgid "Scoped issue boards"
-msgstr ""
+msgstr "Painéis de escopo"
msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
-msgstr ""
+msgstr "Role para baixo até <strong>Google Code Project Hosting</strong> e ative a opção à direita."
msgid "Scroll to bottom"
msgstr "Rolar até o final"
@@ -5537,12 +6603,42 @@ msgstr "Pesquisar merge requests"
msgid "Search milestones"
msgstr "Pesquisar milestones"
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr "Pesquise ou pule para…"
+
msgid "Search project"
msgstr "Procurar projeto"
msgid "Search users"
msgstr "Procurar usuários"
+msgid "SearchAutocomplete|All GitLab"
+msgstr "Todo o GitLab"
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr "Merge requests que eu criei"
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr "Merge requests atribuídas a mim"
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr "em todo o GitLab"
+
+msgid "SearchAutocomplete|in this group"
+msgstr "neste grupo"
+
+msgid "SearchAutocomplete|in this project"
+msgstr "neste projeto"
+
msgid "Seconds before reseting failure information"
msgstr "Segundos antes de redefinir as informações de falha"
@@ -5550,19 +6646,22 @@ msgid "Seconds to wait for a storage access attempt"
msgstr "Segundo de espera para tentativa de acesso ao storage"
msgid "Secret:"
-msgstr ""
+msgstr "Secreto:"
+
+msgid "Security"
+msgstr "Segurança"
msgid "Security Dashboard"
-msgstr ""
+msgstr "Painel de controle de segurança"
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
-msgstr ""
+msgstr "Monitorar vulnerabilidades no seu código"
msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
-msgstr ""
+msgstr "Pipeline %{pipelineLink} acionado"
msgid "Select"
msgstr "Selecionar"
@@ -5570,6 +6669,9 @@ msgstr "Selecionar"
msgid "Select Archive Format"
msgstr "Selecionar Formato do Arquivo"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr "Selecione um namespace para realizar o fork do projeto"
@@ -5595,7 +6697,7 @@ msgid "Select project to choose zone"
msgstr "Selecione o projeto para escolher a zona"
msgid "Select projects you want to import."
-msgstr ""
+msgstr "Selecione os projetos que você deseja importar."
msgid "Select source branch"
msgstr "Selecionar branch de origem"
@@ -5603,15 +6705,24 @@ msgstr "Selecionar branch de origem"
msgid "Select target branch"
msgstr "Selecionar branch de destino"
-msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
msgstr ""
-msgid "Selective synchronization"
+msgid "Select the custom project template source group."
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr "A seleção de um usuário do GitLab adicionará um link para o usuário do GitLab nas descrições de issues e comentários (por exemplo, \"Por <a href=\"#\">@johnsmith</a>\"). Isto também associará e/ou atribuirá esses issues e comentários ao usuário selecionado."
+
+msgid "Selective synchronization"
+msgstr "Sincronização seletiva"
+
msgid "Send email"
msgstr "Enviar e-mail"
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr "Set"
@@ -5628,7 +6739,7 @@ msgid "Service Templates"
msgstr "Modelos de serviço"
msgid "Service URL"
-msgstr ""
+msgstr "URL de serviço"
msgid "Session expiration, projects limit and attachment size."
msgstr "Expiração de sessão, limite de projetos e tamanho de anexo."
@@ -5639,6 +6750,9 @@ msgstr "Defina uma senha para sua conta para aceitar ou entregar código via %{p
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr "Definir padrão e restringir os níveis de visibilidade. Configurar fontes de importação e protocolo de acesso git."
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr "Defina o tempo máximo da sessão para o terminal da web."
@@ -5651,10 +6765,16 @@ msgstr "Definir requisitos para um usuário entrar. Ative a autenticação obrig
msgid "Set up CI/CD"
msgstr "Configurar CI/CD"
-msgid "Set up Koding"
-msgstr "Configurar Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr "Configurar asserções/atributos/alegações (email, first_name, last_name) e NameID de acordo com %{docsLinkStart}a documentação %{icon}%{docsLinkEnd}"
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
@@ -5663,29 +6783,29 @@ msgstr "defina uma senha"
msgid "Settings"
msgstr "Configurações"
-msgid "Setup a specific Runner automatically"
-msgstr "Configurar um Runner específico automaticamente"
-
msgid "Share"
msgstr "Compartilhar"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
-msgstr ""
+msgstr "Compartilhe o <strong>%{sso_label}</strong> com membros de forma que eles possam entrar em seu grupo por meio de seu provedor de identidade"
msgid "Shared Runners"
msgstr "Runners Compartilhados"
+msgid "Shared projects"
+msgstr "Projetos compartilhados"
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
-msgstr ""
+msgstr "Ao redefinir os minutos de pipeline para esse espaço de nomes, os minutos atualmente usados serão definido para zero."
msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
-msgstr ""
+msgstr "Redefinir minutos de pipeline"
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
-msgstr ""
+msgstr "Redefinir minutos usados de pipeline"
msgid "Sherlock Transactions"
-msgstr ""
+msgstr "Transações de Sherlock"
msgid "Show command"
msgstr "Exibir comando"
@@ -5694,10 +6814,10 @@ msgid "Show complete raw log"
msgstr "Visualizar raw log completo"
msgid "Show latest version"
-msgstr ""
+msgstr "Mostrar a versão mais recente"
msgid "Show latest version of the diff"
-msgstr ""
+msgstr "Mostrar a versão mais recente do diff"
msgid "Show parent pages"
msgstr "Mostrar páginas acima"
@@ -5706,7 +6826,7 @@ msgid "Show parent subgroups"
msgstr "Mostrar subgrupos acima"
msgid "Show whitespace changes"
-msgstr ""
+msgstr "Mostrar as alterações de espaço em branco"
msgid "Showing %d event"
msgid_plural "Showing %d events"
@@ -5714,31 +6834,31 @@ msgstr[0] "Mostrando %d evento"
msgstr[1] "Mostrando %d eventos"
msgid "Side-by-side"
-msgstr ""
+msgstr "Lado a lado"
msgid "Sidebar|Change weight"
-msgstr ""
+msgstr "Alterar peso"
msgid "Sidebar|None"
-msgstr ""
+msgstr "Nenhum"
msgid "Sidebar|Only numeral characters allowed"
-msgstr ""
+msgstr "Apenas caracteres numéricos permitidos"
msgid "Sidebar|Weight"
-msgstr ""
+msgstr "Peso"
msgid "Sign in"
-msgstr ""
+msgstr "Entrar"
msgid "Sign in / Register"
-msgstr ""
+msgstr "Entrar / Criar conta"
msgid "Sign in to %{group_name}"
-msgstr ""
+msgstr "Entre em %{group_name}"
msgid "Sign in with Single Sign-On"
-msgstr ""
+msgstr "Entre com logon único"
msgid "Sign out"
msgstr "Sair"
@@ -5749,14 +6869,20 @@ msgstr "Restrições de login"
msgid "Sign-up restrictions"
msgstr "Restrições de cadastro"
+msgid "Size"
+msgstr "Tamanho"
+
msgid "Size and domain settings for static websites"
msgstr "Configurações de tamanho e domínio para sites estáticos"
msgid "Slack application"
+msgstr "Aplicativo Slack"
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
msgstr ""
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
-msgstr ""
+msgstr "Mais lento, mas certifica-se que o espaço de trabalho do projeto está íntegro enquanto clona o repositório do zero para cada tarefa"
msgid "Snippets"
msgstr "Snippets"
@@ -5768,19 +6894,25 @@ msgid "Something went wrong on our end."
msgstr "Algo deu errado do nosso lado."
msgid "Something went wrong on our end. Please try again!"
+msgstr "Algo deu errado no nosso lado. Por favor, tente novamente!"
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
msgstr ""
msgid "Something went wrong when toggling the button"
msgstr "Algo deu errado ao alternar o botão"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr ""
+msgstr "Algo deu errado ao fechar o %{issuable}. Por favor, tente novamente depois"
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
-msgstr ""
+msgstr "Algo deu errado ao obter as contribuições de membro do grupo"
msgid "Something went wrong while fetching the projects."
msgstr "Algo deu errado ao recuperar os projetos."
@@ -5792,13 +6924,13 @@ msgid "Something went wrong while reopening the %{issuable}. Please try again la
msgstr "Alguma coisa deu errado ao reabrir o %{issuable}. Por favor, tente novamente depois"
msgid "Something went wrong while resolving this discussion. Please try again."
-msgstr ""
+msgstr "Algo deu errado ao resolver essa discussão. Por favor, tente novamente."
msgid "Something went wrong. Please try again."
msgstr "Algo deu errado. Por favor, tente novamente."
msgid "Sorry, no epics matched your search"
-msgstr ""
+msgstr "Desculpe, nenhum épico corresponde à sua pesquisa"
msgid "Sort by"
msgstr "Ordenar por"
@@ -5822,7 +6954,7 @@ msgid "SortOptions|Due soon"
msgstr "Data de vencimento mais próxima"
msgid "SortOptions|Label priority"
-msgstr "Prioridade de label"
+msgstr "Prioridade de etiqueta"
msgid "SortOptions|Largest group"
msgstr "Maior grupo"
@@ -5830,6 +6962,9 @@ msgstr "Maior grupo"
msgid "SortOptions|Largest repository"
msgstr "Maior repositório"
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr "Últimos criados"
@@ -5843,7 +6978,7 @@ msgid "SortOptions|Least popular"
msgstr "Menos populares"
msgid "SortOptions|Less weight"
-msgstr ""
+msgstr "Menos peso"
msgid "SortOptions|Milestone"
msgstr "Milestone"
@@ -5855,11 +6990,14 @@ msgid "SortOptions|Milestone due soon"
msgstr "Milestone de fim mais próximo"
msgid "SortOptions|More weight"
-msgstr ""
+msgstr "Mais peso"
msgid "SortOptions|Most popular"
msgstr "Mais populares"
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr "Nome"
@@ -5890,6 +7028,9 @@ msgstr "Prioridade"
msgid "SortOptions|Recent sign in"
msgstr "Assinados mais novos"
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr "Iniciar mais tarde"
@@ -5897,7 +7038,7 @@ msgid "SortOptions|Start soon"
msgstr "Iniciar mais próximo"
msgid "SortOptions|Weight"
-msgstr ""
+msgstr "Peso"
msgid "Source"
msgstr "Origem"
@@ -5920,8 +7061,11 @@ msgstr "Proteção contra spam e anti-bot"
msgid "Specific Runners"
msgstr "Runners Específicos"
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
-msgstr "Especifique a seguinte URL durante a configuração do Runner:"
+msgstr "Especifique o seguinte URL durante a configuração do Runner:"
msgid "Squash commits"
msgstr "Squash commits"
@@ -5945,7 +7089,7 @@ msgid "Staged %{type}"
msgstr "%{type} na lista para commit"
msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Coloque uma estrela em um label para dar prioridade. Altere a ordem das labels priorizadas arrastando-as para alterar sua prioridade."
+msgstr "Coloque uma estrela em uma etiqueta para dar prioridade. Altere a ordem das etiquetas priorizadas arrastando-as para alterar sua prioridade."
msgid "StarProject|Star"
msgstr "Marcar como favorito"
@@ -5962,6 +7106,9 @@ msgstr "Projetos favoritos"
msgid "Start a %{new_merge_request} with these changes"
msgstr "Iniciar um %{new_merge_request} a partir dessas alterações"
+msgid "Start date"
+msgstr "Data de início"
+
msgid "Start the Runner!"
msgstr "Inicie o Runner!"
@@ -5972,13 +7119,13 @@ msgid "Starts at (UTC)"
msgstr "Começa em (UTC)"
msgid "State your message to activate"
-msgstr ""
+msgstr "Regitre sua mensagem para ativar"
msgid "Status"
msgstr "Status"
msgid "Stop impersonation"
-msgstr ""
+msgstr "Parar representação"
msgid "Stop this environment"
msgstr "Parar este ambiente"
@@ -5990,16 +7137,19 @@ msgid "Storage"
msgstr "Armazenamento"
msgid "Storage:"
-msgstr ""
+msgstr "Armazenamento:"
msgid "Subgroups"
msgstr "Subgrupos"
+msgid "Subgroups and projects"
+msgstr "Subgrupos e projetos"
+
msgid "Submit as spam"
-msgstr ""
+msgstr "Enviar como spam"
msgid "Submit search"
-msgstr ""
+msgstr "Buscar"
msgid "Subscribe"
msgstr "Inscrever-se"
@@ -6014,18 +7164,21 @@ msgid "Switch branch/tag"
msgstr "Trocar branch/tag"
msgid "Sync information"
-msgstr ""
+msgstr "Informação de sincronização"
msgid "System Hooks"
msgstr "Hooks do sistema"
msgid "System Info"
-msgstr ""
+msgstr "Informações do Sistema"
msgid "System header and footer:"
-msgstr ""
+msgstr "Cabeçalho e rodapé de sistema:"
msgid "System metrics (Custom)"
+msgstr "Métricas de sistema (Personalizado)"
+
+msgid "System metrics (Kubernetes)"
msgstr ""
msgid "Tag (%{tag_count})"
@@ -6036,6 +7189,9 @@ msgstr[1] "Tags (%{tag_count})"
msgid "Tags"
msgstr "Tags"
+msgid "Tags feed"
+msgstr "Feed de tags"
+
msgid "Tags:"
msgstr "Tags:"
@@ -6103,7 +7259,7 @@ msgid "TagsPage|Use git tag command to add a new one:"
msgstr "Use o comando \"git tag\" para adiciona uma nova tag:"
msgid "TagsPage|Write your release notes or drag files here…"
-msgstr ""
+msgstr "Escreva suas notas de lançamento ou arraste arquivos aqui…"
msgid "TagsPage|protected"
msgstr "protegido"
@@ -6117,6 +7273,12 @@ msgstr "Branch de destino"
msgid "Team"
msgstr "Equipe"
+msgid "Template"
+msgstr "Modelo"
+
+msgid "Templates"
+msgstr "Modelos"
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr "Contrato de Termos de Serviço e Política de Privacidade"
@@ -6124,12 +7286,15 @@ msgid "Terms of Service and Privacy Policy"
msgstr "Termos de Serviço e Política de Privacidade"
msgid "Test coverage parsing"
-msgstr ""
+msgstr "Análise de cobertura de teste"
msgid "Thanks! Don't show me this again"
-msgstr ""
+msgstr "Obrigado! Não me mostre isso novamente"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr "O Advanced Global Search no GitLab é um poderoso serviço de pesquisa que economiza seu tempo. Em vez de criar código duplicado e perder tempo, você pode agora procurar código dentro de outras equipes que podem ajudar seu próprio projeto."
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
@@ -6139,6 +7304,9 @@ msgid "The Issue Tracker is the place to add things that need to be improved or
msgstr "Issue Tracker é o lugar para adicionar coisas que precisam ser melhoradas ou resolvidas em um projeto. Você precisa se registrar ou fazer login para criar alguma Issue para este projeto."
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr "O Certificado X509 a ser usado quando o TLS mútuo é necessário para se comunicar com o serviço de autorização externa. Se deixado em branco, o certificado do servidor ainda será validado ao acessar por HTTPS."
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -6148,6 +7316,9 @@ msgid "The collection of events added to the data gathered for that stage."
msgstr "A coleção de eventos adicionados aos dados coletados para essa etapa."
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr "A conexão expirará após %{timeout}. Para repositórios que demoram mais tempo, use a combinação clone/push."
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
msgstr ""
msgid "The fork relationship has been removed."
@@ -6157,7 +7328,7 @@ msgid "The import will time out after %{timeout}. For repositories that take lon
msgstr "A importação expirará após %{timeout}. Para repositórios que demoram mais tempo, use a combinação clone/push."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
-msgstr "A etapa de planejamento mostra o tempo que se leva desde a criação de uma issue até sua atribuição à um milestone, ou sua adição a uma lista no seu Issue Board. Comece a criar issues para ver dados para esta etapa."
+msgstr "A etapa de planejamento mostra o tempo que se leva desde a criação de uma issue até sua atribuição à um milestone, ou sua adição a uma lista no seu painel. Comece a criar issues para ver dados para esta etapa."
msgid "The maximum file size allowed is 200KB."
msgstr "O tamanho máximo do arquivo é de 200KB."
@@ -6166,22 +7337,25 @@ msgid "The number of attempts GitLab will make to access a storage."
msgstr "O número de tentativas que gitlab fará para acessar um storage."
msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr ""
+msgstr "O número de falhas para que GitLab impeça completamente o acesso ao armazenamento. O número de falhas pode ser redefinido na interface do administrador: %{link_to_health_page} ou usando a %{api_documentation_link}."
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
-msgstr ""
+msgstr "A senha necessária para descriptografar a chave privada. Isso é opcional e o seu valor é criptografado."
msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
-msgstr ""
+msgstr "O caminho para o arquivo de configuração de CI. O padrão é <code>.gitlab-ci.yml</code>"
msgid "The phase of the development lifecycle."
msgstr "A fase do ciclo de vida do desenvolvimento."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "A etapa de planejamento mostra o tempo do passo anterior até a publicação de seu primeiro conjunto de mudanças. Este tempo será adicionado automaticamente assim que você enviar seu primeiro conjunto de mudanças."
msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
-msgstr ""
+msgstr "A chave privada a ser usada quando um certificado de cliente é fornecido. Este valor é criptografado."
msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
msgstr "A etapa de produção mostra o tempo total que leva entre criar uma issue e implantar o código em produção. Os dados serão adicionados automaticamente assim que você completar todo o ciclo de produção."
@@ -6193,7 +7367,7 @@ msgid "The project can be accessed without any authentication."
msgstr "O projeto pode ser acessado sem a necessidade de autenticação."
msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
-msgstr ""
+msgstr "A coleção de dados pseudonymizer está desativada. Quando ativada, o GitLab executará um trabalho em segundo plano que produzirá CSVs com pseudônimos do banco de dados do GitLab que serão carregados no diretório de armazenamento de objetos configurado."
msgid "The repository for this project does not exist."
msgstr "Não existe repositório para este projeto."
@@ -6204,18 +7378,24 @@ msgstr "O repositório para este projeto está vazio"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr "O repositório deve ser acessível por <code>http://</code>, <code>https://</code> ou <code>git://</code>."
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "A etapa de revisão mostra o tempo de criação de uma solicitação de incorporação até sua aceitação. Os dados serão automaticamente adicionados depois que sua primeira solicitação de incorporação for aceita."
msgid "The roadmap shows the progress of your epics along a timeline"
-msgstr ""
+msgstr "O roadmap mostra o progresso de seus epics ao longo de uma linha do tempo"
msgid "The secure token used by the Runner to checkout the project"
-msgstr ""
+msgstr "O token seguro usado pelo Runner para fazer checkout do projeto"
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "A etapa de homologação mostra o tempo entre o aceite da solicitação de incorporação e a implantação do código no ambiente de produção. Os dados serão automaticamente adicionados depois que você implantar em produção pela primeira vez."
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "A etapa de testes mostra o tempo que o GitLab CI leva para executar cada pipeline para a solicitação de incorporação associada. Os dados serão automaticamente adicionados após a conclusão do primeiro pipeline."
@@ -6226,32 +7406,56 @@ msgid "The time in seconds GitLab will try to access storage. After this time a
msgstr "Tempo em segundos que o GitLab tentará acessar o storage. Depois desse tempo, um erro de tempo excedido será disparado."
msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
-msgstr ""
+msgstr "O tempo em segundos entre as verificações de armazenamento. Quando uma verificação anterior ainda não tiver sido concluída, o GitLab pulará a próxima verificação."
msgid "The time taken by each data entry gathered by that stage."
msgstr "O tempo necessário por cada entrada de dados reunida por essa etapa."
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr ""
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr "O mapa do usuário é um documento JSON que mapeia os usuários do Google Code que participaram de seus projetos para a maneira como seus endereços de e-mail e nomes de usuários são importados para o GitLab. Você pode mudar isso alterando o valor no lado direito de <code>:</code>. Certifique-se de preservar as aspas duplas adjacentes, outros sinais de pontuação e o endereço de e-mail ou nome de usuário no lado esquerdo."
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr "O mapa do usuário é um mapeamento dos usuários do FogBugz que participaram de seus projetos para a maneira como seus endereços de e-mail e nomes de usuários serão importados para o GitLab. Você pode alterar isso preenchendo a tabela abaixo."
+
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "O valor situado no ponto médio de uma série de valores observados. Ex., entre 3, 5, 9, a mediana é 5. Entre 3, 5, 7, 8, a mediana é (5+7)/2 = 6."
+msgid "There are no archived projects yet"
+msgstr "Ainda não há projetos arquivados"
+
msgid "There are no issues to show"
msgstr "Não há issues para mostrar"
msgid "There are no labels yet"
-msgstr "Não há etiquetas ainda"
+msgstr "Ainda não há etiquetas"
msgid "There are no merge requests to show"
msgstr "Não há merge requests pra mostrar"
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "Há problemas para acessar o storage Git: "
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr "Erro ao carregar calendário de atividades."
@@ -6259,46 +7463,58 @@ msgid "There was an error saving your notification settings."
msgstr "Erro ao salvar suas configurações de notificação."
msgid "There was an error subscribing to this label."
-msgstr "Erro ao se inscrever nessa label."
+msgstr "Erro ao se inscrever nessa etiqueta."
msgid "There was an error when reseting email token."
msgstr "Erro ao redefinir token do email."
msgid "There was an error when subscribing to this label."
-msgstr "Erro ao se inscrever nessa label."
+msgstr "Erro ao se inscrever nessa etiqueta."
msgid "There was an error when unsubscribing from this label."
-msgstr "Erro ao se anular a inscrição dessa label."
+msgstr "Erro ao se anular a inscrição dessa etiqueta."
msgid "They can be managed using the %{link}."
msgstr "Eles podem ser gerenciados usando o %{link}."
msgid "Third party offers"
-msgstr ""
+msgstr "Ofertas de terceiros"
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr "Esta instância do GitLab ainda não fornece nenhum Runner compartilhado. Os administradores da instância podem registrar os Runners compartilhados na área de administração."
msgid "This application was created by %{link_to_owner}."
-msgstr ""
+msgstr "Esse aplicativo foi criado por %{link_to_owner}."
msgid "This application will be able to:"
-msgstr ""
+msgstr "Esse aplicativo será capaz de:"
msgid "This board's scope is reduced"
+msgstr "O escopo deste painel está reduzido"
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
-msgid "This diff is collapsed."
+msgid "This container registry has been scheduled for deletion."
msgstr ""
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr "Esse diff está recolhido."
+
msgid "This directory"
msgstr "Esse diretório"
msgid "This group"
-msgstr ""
+msgstr "Esse grupo"
msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
-msgstr ""
+msgstr "Este grupo permite que você entre com sua conta de logon único de %{group_name}. Isso irá redirecioná-lo para uma página de login externa."
msgid "This group does not provide any group Runners yet."
msgstr "Este grupo não fornece nenhum grupo de Runners ainda."
@@ -6339,9 +7555,30 @@ msgstr "Esta build ainda não foi acionada"
msgid "This job has not started yet"
msgstr "Esse processo ainda não começou"
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "Esta tarefa está em estado pendente e está esperando para ser escolhido por um runner"
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr "Este Job exige uma ação manual"
@@ -6351,6 +7588,9 @@ msgstr "Isto significa que você não pode entregar código até que crie um rep
msgid "This merge request is locked."
msgstr "Esse merge request está bloqueado."
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr "Esta opção ficará desativada enquanto você ainda tiver alterações fora da lista para commit"
@@ -6366,18 +7606,33 @@ msgstr "Esse projeto"
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr "Este projeto não pertence a um grupo e, portanto, não pode usar os Runners do grupo."
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr "Esse repositório"
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
+msgstr "Esse diff de origem não pôde ser exibido porque é muito grande."
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
msgstr ""
msgid "This user has no identities"
+msgstr "Esse usuário não tem identidades"
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
-msgid "This will delete the custom metric, Are you sure?"
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
msgstr ""
+msgid "This will delete the custom metric, Are you sure?"
+msgstr "Isso vai excluir a métrica personalizada. Você tem certeza?"
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr "Esses e-mails se tornam automaticamente issues (com os comentários se tornando a conversa por e-mail) listados aqui."
@@ -6391,7 +7646,7 @@ msgid "Time between merge request creation and merge/close"
msgstr "Tempo entre a criação da solicitação de incorporação e a aceitação/fechamento"
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
-msgstr ""
+msgstr "Tempo em segundos o GitLab aguardará uma resposta do serviço externo. Quando o serviço não responder a tempo, o acesso será negado."
msgid "Time remaining"
msgstr "Tempo restante"
@@ -6541,7 +7796,7 @@ msgid "Timeago|right now"
msgstr "agora mesmo"
msgid "Timeout"
-msgstr ""
+msgstr "Tempo limite"
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -6560,7 +7815,7 @@ msgid "Tip:"
msgstr "Dica:"
msgid "Title"
-msgstr ""
+msgstr "Título"
msgid "To GitLab"
msgstr "Para o GitLab"
@@ -6569,18 +7824,30 @@ msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_en
msgstr "Para adicionar uma chave SSH, você precisa %{generate_link_start}gerar uma%{link_end} ou usar uma %{existing_link_start}chave existente%{link_end}."
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr ""
+msgstr "Para conectar repositórios do GitHub, você pode usar um %{personal_access_token_link}. Ao criar seu Token de Acesso Pessoal, você precisará selecionar o escopo do <code>repositório</code>, então podemos exibir uma lista de seus repositórios públicos e privados que estão disponíveis para conexão."
msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
-msgstr ""
+msgstr "Para conectar repositórios do GitHub, primeiro você precisa autorizar o GitLab a acessar a lista de seus repositórios do GitHub:"
msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr "Para conectar um repositório SVN, confira %{svn_link}."
+
+msgid "To define internal users, first enable new users set to external"
msgstr ""
-msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr "Para começar, insira seu URL do FogBugz e as informações de login abaixo. Nas próximas etapas, você poderá associar usuários e selecionar os projetos que deseja importar."
+
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr "Para começar, insira seu URL de Host do Gitea e um %{link_to_personal_token}."
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
msgstr ""
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
@@ -6593,43 +7860,46 @@ msgid "To import an SVN repository, check out %{svn_link}."
msgstr "Para importar um repositório SVN, confira %{svn_link}."
msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
-msgstr ""
+msgstr "Para mover ou copiar todo um projeto do GitLab de outra instalação do GitLab para esta, navegue até a página de configurações do projeto original, gere um arquivo de exportação e envie ele aqui."
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
-msgstr ""
+msgstr "Para usar apenas recursos CI/CD para um repositório externo, escolha <strong>CI/CD para repo externo</strong>."
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
-msgstr ""
+msgstr "Para configurar a autenticação SAML para o seu grupo por meio de um provedor de identidade, como Azure, Okta, Onelogin, Ping Identity ou seu provedor SAML 2.0 personalizado:"
msgid "To start serving your jobs you can add Runners to your group"
msgstr "Para começar a servir suas tarefas, você pode adicionar Runners ao seu grupo"
msgid "To this GitLab instance"
-msgstr ""
+msgstr "Para esta instância do GitLab"
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr "Para validar suas configurações de CI do GitLab, vá para 'CI/CD → Pipelines' dentro do seu projeto e clique no botão 'CI Lint'."
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
-msgstr ""
+msgstr "Para ampliar sua pesquisa, alterar ou remover filtros."
msgid "Todo"
msgstr "Pendente"
msgid "Todos"
-msgstr ""
+msgstr "Afazeres"
msgid "Toggle Sidebar"
msgstr "Alternar barra lateral"
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr "Alternar discussão"
msgid "Toggle navigation"
-msgstr ""
+msgstr "Alternar navegação"
msgid "Toggle sidebar"
msgstr "Ativar/Desativar barra lateral"
@@ -6640,8 +7910,11 @@ msgstr "Mudar Status: Desligado"
msgid "ToggleButton|Toggle Status: ON"
msgstr "Mudar Status: Ligado"
+msgid "Token"
+msgstr "Token"
+
msgid "Too many changes to show."
-msgstr ""
+msgstr "Alterações demais para mostrar."
msgid "Total Contributions"
msgstr "Contribuições totais"
@@ -6656,22 +7929,31 @@ msgid "Total: %{total}"
msgstr "Total: %{total}"
msgid "Track activity with Contribution Analytics."
-msgstr ""
+msgstr "Acompanhe atividades com a Análise de Contribuições."
msgid "Track groups of issues that share a theme, across projects and milestones"
-msgstr ""
+msgstr "Acompanhe grupos de issues que compartilhem um tema, em projetos e milestones"
msgid "Track time with quick actions"
msgstr "Acompanhe o tempo com ações rápidas"
msgid "Trending"
+msgstr "Mais populares"
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
msgstr ""
msgid "Trigger this manual action"
msgstr "Acionar esta ação manual"
msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
-msgstr ""
+msgstr "Os gatilhos podem forçar uma build numa branch ou tag específica através da API. Esses tokens representarão seu usuário associado, incluindo seu acesso a projetos e suas permissões de projeto."
msgid "Try again"
msgstr "Tente novamente"
@@ -6680,26 +7962,41 @@ msgid "Turn on Service Desk"
msgstr "Ativar o Balcão de Atendimento"
msgid "Twitter"
-msgstr ""
+msgstr "Twitter"
+
+msgid "Type"
+msgstr "Tipo"
msgid "Unable to load the diff. %{button_try_again}"
msgstr "Não é possível carregar o diff. %{button_try_again}"
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr "Não é possível inscrever você no grupo com SAML devido a \"%{reason}\""
+
+msgid "Unable to update this epic at this time."
msgstr ""
+msgid "Undo"
+msgstr "Desfazer"
+
msgid "Unknown"
-msgstr ""
+msgstr "Desconhecido"
msgid "Unlock"
msgstr "Desbloquear"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "Desbloqueado"
msgid "Unresolve discussion"
msgstr "Reabrir discussão"
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr "Retirar tudo da lista para commit"
@@ -6734,11 +8031,17 @@ msgid "Up to date"
msgstr "Atualizado"
msgid "Update"
-msgstr ""
+msgstr "Atualizar"
+
+msgid "Update now"
+msgstr "Atualizar agora"
msgid "Update your group name, description, avatar, and other general settings."
msgstr "Atualize o nome do seu grupo, descrição, avatar e outras configurações gerais."
+msgid "Updating"
+msgstr "Atualizando"
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "Aprimore seu plano para ativar a Pesquisa Global Avançada."
@@ -6746,16 +8049,16 @@ msgid "Upgrade your plan to activate Contribution Analytics."
msgstr "Aprimore seu plano para ativar a Análise de Contribuição."
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr ""
+msgstr "Aprimore seu plano para ativar os Webhooks de Grupo."
msgid "Upgrade your plan to activate Issue weight."
-msgstr ""
+msgstr "Faça upgrade de plano para ativar o peso nas Issues."
msgid "Upgrade your plan to improve Issue boards."
-msgstr ""
+msgstr "Faça upgrade de plano para melhorar os painéis."
msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
-msgstr ""
+msgstr "Envie o <code>GoogleCodeProjectHosting.json</code> aqui:"
msgid "Upload New File"
msgstr "Enviar Novo Arquivo"
@@ -6763,20 +8066,20 @@ msgstr "Enviar Novo Arquivo"
msgid "Upload file"
msgstr "Enviar arquivo"
-msgid "Upload new avatar"
-msgstr "Fazer upload de nova imagem"
-
msgid "UploadLink|click to upload"
msgstr "clique para fazer upload"
msgid "Upvotes"
msgstr "Votos positivos"
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr "Estatísticas de uso"
msgid "Use <code>%{native_redirect_uri}</code> for local tests"
-msgstr ""
+msgstr "Use <code>%{native_redirect_uri}</code> para testes locais"
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr "Use o Balcão de Atendimento para se conectar com seus usuários (por exemplo, para oferecer suporte ao cliente) por email dentro do GitLab"
@@ -6785,7 +8088,10 @@ msgid "Use group milestones to manage issues from multiple projects in the same
msgstr "Use milestones de grupo para gerenciar problemas de vários projetos no mesmo milestone."
msgid "Use one line per URI"
-msgstr ""
+msgstr "Use uma linha por URI"
+
+msgid "Use template"
+msgstr "Utilizar modelo"
msgid "Use the following registration token during setup:"
msgstr "Use o seguinte token de registro durante a configuração:"
@@ -6794,6 +8100,9 @@ msgid "Use your global notification setting"
msgstr "Utilizar configuração de notificação global"
msgid "Used by members to sign in to your group in GitLab"
+msgstr "Utilizado pelos membros para entrar em seu grupo no GitLab"
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
msgstr ""
msgid "User Settings"
@@ -6803,10 +8112,10 @@ msgid "User and IP Rate Limits"
msgstr "Limites de Taxa de Usuário e IP"
msgid "User map"
-msgstr ""
+msgstr "Mapa do usuário"
msgid "Users"
-msgstr ""
+msgstr "Usuários"
msgid "Variables"
msgstr "Variáveis"
@@ -6824,25 +8133,28 @@ msgid "Various settings that affect GitLab performance."
msgstr "Várias configurações que afetam o desempenho do GitLab."
msgid "Verification information"
-msgstr ""
+msgstr "Informação de verificação"
msgid "Verified"
msgstr "Verificado"
+msgid "Version"
+msgstr "Versão"
+
msgid "View epics list"
-msgstr ""
+msgstr "Ve lista de épicos"
msgid "View file @ "
msgstr "Ver arquivo @ "
msgid "View group labels"
-msgstr "Visualizar labels do grupo"
+msgstr "Visualizar etiquetas de grupo"
msgid "View issue"
-msgstr ""
+msgstr "Ver issue"
msgid "View it on GitLab"
-msgstr ""
+msgstr "Ver no GitLab"
msgid "View jobs"
msgstr "Visualizar tarefas"
@@ -6857,7 +8169,7 @@ msgid "View open merge request"
msgstr "Ver merge request aberto"
msgid "View project labels"
-msgstr "Ver labels do projeto"
+msgstr "Ver etiquetas de projeto"
msgid "View replaced file @ "
msgstr "Ver arquivo substituído @ "
@@ -6866,10 +8178,10 @@ msgid "Visibility and access controls"
msgstr "Visibilidade e controles de acesso"
msgid "Visibility level:"
-msgstr ""
+msgstr "Nível de visibilidade:"
msgid "Visibility:"
-msgstr ""
+msgstr "Visibilidade:"
msgid "VisibilityLevel|Internal"
msgstr "Interno"
@@ -6887,7 +8199,7 @@ msgid "Want to see the data? Please ask an administrator for access."
msgstr "Precisa visualizar os dados? Solicite acesso ao administrador."
msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
-msgstr ""
+msgstr "Detectamos spam potencial no %{humanized_resource_name}. Por favor, resolva o reCAPTCHA para continuar."
msgid "We don't have enough data to show this stage."
msgstr "Esta etapa não possui dados suficientes para exibição."
@@ -6902,16 +8214,16 @@ msgid "Web terminal"
msgstr "Terminal Web"
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr "Webhooks permitem que você acione uma URL se, por exemplo, um novo código for pushado ou se uma nova issue for criada. Você pode configurar os webhooks para interagir com eventos específicos como pushes, issues ou merge requests. Webhooks de grupos serão aplicados para todos os projetos em um grupo, permitindo que você padronize o funcionamento em todo o grupo."
+msgstr "Webhooks permitem que você acione um URL se, por exemplo, um novo código for feito push ou se uma nova issue for criada. Você pode configurar os webhooks para interagir com eventos específicos como pushes, issues ou merge requests. Webhooks de grupos serão aplicados para todos os projetos em um grupo, permitindo que você padronize o funcionamento em todo o grupo."
msgid "Weeks"
-msgstr ""
+msgstr "Semanas"
msgid "Weight"
-msgstr ""
+msgstr "Peso"
msgid "Weight %{weight}"
-msgstr ""
+msgstr "Peso %{weight}"
msgid "When a runner is locked, it cannot be assigned to other projects"
msgstr "Quando um runner está bloqueado, não pode ser atribuído a outros projetos"
@@ -7058,7 +8370,7 @@ msgid "Wiki|Wiki Pages"
msgstr "Páginas Wiki"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr ""
+msgstr "Com a análise de contribuições, você pode ter uma visão geral da atividade de issues, solicitações de merge e eventos de push da sua organização e de seus membros."
msgid "Withdraw Access Request"
msgstr "Remover Requisição de Acesso"
@@ -7067,13 +8379,13 @@ msgid "Yes"
msgstr "Sim"
msgid "Yes, add it"
-msgstr ""
+msgstr "Sim, adicionar"
msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
+msgstr "Sim, deixe-me associar usuários do Google Code para nomes completos ou usuários do GitLab."
msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
-msgstr ""
+msgstr "Você é um administrador, o que significa que conceder acesso a <strong>%{client_name}</strong> permitirá que eles também interajam com o GitLab como administrador. Prossiga com cuidado."
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "Você vai remover %{group_name}. Grupos removidos NÃO PODEM ser restaurados! Você está ABSOLUTAMENTE certo?"
@@ -7091,22 +8403,22 @@ msgid "You are on a read-only GitLab instance."
msgstr "Você está em uma instância somente-leitura do GitLab."
msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
-msgstr ""
+msgstr "Você está em um Geo node <b>somente leitura</b> secundário. Se você quiser fazer alguma alteração, você precisa visitar essa página no %{primary_node}."
msgid "You can %{linkStart}view the blob%{linkEnd} instead."
-msgstr ""
+msgstr "Você pode %{linkStart}visualizar o blob%{linkEnd} em vez disso."
msgid "You can also create a project from the command line."
msgstr "Você também pode criar um projeto a partir da linha de comando."
msgid "You can also star a label to make it a priority label."
-msgstr "Você também pode marcar uma label para torná-la uma label de prioridade."
+msgstr "Você também pode marcar uma etiqueta para torná-la uma etiqueta de prioridade."
msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
msgstr "Você também pode testar o seu .gitlab-ci.yml no %{linkStart}Lint%{linkEnd}"
msgid "You can easily contribute to them by requesting to join these groups."
-msgstr ""
+msgstr "Você pode facilmente contribuir para eles pedindo para se juntar nesses grupos."
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr "Você pode instalar facilmente um Runner em um cluster Kubernetes. %{link_to_help_page}"
@@ -7120,33 +8432,33 @@ msgstr "Você somente pode adicionar arquivos quando estiver em um branch"
msgid "You can only edit files when you are on a branch"
msgstr "Você só pode editar arquivos quando estiver em um branch"
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr "Você pode resolver o conflito de merge usando o modo Interativo, escolhendo os botões %{use_ours} ou %{use_theirs} ou editando os arquivos diretamente. Confirme essas alterações em %{branch_name}"
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr "Você não pode escrever numa instância secundária de somente leitura do GitLab Geo. Por favor use %{link_to_primary_node} em vez disso."
+
msgid "You cannot write to this read-only GitLab instance."
msgstr "Você não pode escrever nesta instância somente-leitura do GitLab."
-msgid "You do not have any assigned merge requests"
-msgstr "Você não atribuiu nenhum merge request"
-
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
-msgstr ""
+msgstr "Você não tem as permissões corretas para substituir as configurações de sincronização do grupo LDAP."
msgid "You don't have any applications"
-msgstr ""
+msgstr "Você não tem nenhum aplicativo"
msgid "You don't have any authorized applications"
-msgstr ""
+msgstr "Você não tem nenhum aplicativo autorizado"
msgid "You have no permissions"
msgstr "Você não tem permissão"
-msgid "You have not created any merge requests"
-msgstr "Você não criou merge request"
-
msgid "You have reached your project limit"
msgstr "Você atingiu o limite de seu projeto"
@@ -7156,18 +8468,21 @@ msgstr "Você deve aceitar nossos Termos de Serviço e política de privacidade
msgid "You must have maintainer access to force delete a lock"
msgstr "Você deve ter o acesso de mantenedor para apagar um bloqueio"
-msgid "You must sign in to star a project"
-msgstr "Você deve estar autenticado para marcar um projeto"
-
msgid "You need a different license to enable FileLocks feature"
-msgstr ""
+msgstr "Você precisa de uma licença diferente para ativar o recurso FileLocks"
msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
-msgstr ""
+msgstr "Você precisa do git-lfs na versão %{min_git_lfs_version} (ou maior) para continuar. Por favor, visite https://git-lfs.github.com"
msgid "You need permission."
msgstr "Você precisa de permissão."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "Você não será notificado por email"
@@ -7196,16 +8511,16 @@ msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Você precisará usar nomes de branch diferentes para obter uma comparação válida."
msgid "You're receiving this email because %{reason}."
-msgstr ""
+msgstr "Você está recebendo este e-mail porque %{reason}."
msgid "You're receiving this email because of your account on %{host}."
-msgstr ""
+msgstr "Você está recebendo este e-mail devido à sua conta no %{host}."
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr "Você está recebendo este e-mail devido à sua conta no %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgid "YouTube"
-msgstr ""
+msgstr "YouTube"
msgid "Your Groups"
msgstr "Seus Grupos"
@@ -7223,10 +8538,10 @@ msgid "Your Todos"
msgstr "Seus lembretes"
msgid "Your applications (%{size})"
-msgstr ""
+msgstr "Seus aplicativos (%{size})"
msgid "Your authorized applications"
-msgstr ""
+msgstr "Seus aplicativos autorizados"
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr "Você pode fazer commit de suas alterações para %{branch_name} porque um merge request está aberto."
@@ -7246,17 +8561,15 @@ msgstr "Seu nome"
msgid "Your projects"
msgstr "Seus projetos"
+msgid "a deleted user"
+msgstr "um usuário excluído"
+
msgid "ago"
msgstr "atrás"
msgid "among other things"
msgstr "entre outras coisas"
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr "atribuir a si mesmo"
@@ -7264,211 +8577,234 @@ msgid "branch name"
msgstr "nome da branch"
msgid "by"
-msgstr ""
+msgstr "por"
msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}Saiba mais sobre Verificação de Container %{linkEndTag}"
msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}Saiba mais sobre o DAST %{linkEndTag}"
msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}Saiba mais sobre a Verificação de Dependência %{linkEndTag}"
msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
-msgstr ""
+msgstr "%{linkStartTag}Saiba mais sobre o SAST %{linkEndTag}"
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr "%{namespace} é afetado por %{vulnerability}."
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr "mais %{remainingPackagesCount}"
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
+msgid "ciReport|Class"
+msgstr "Classe"
+
msgid "ciReport|Code quality"
-msgstr ""
+msgstr "Qualidade do código"
msgid "ciReport|Confidence"
+msgstr "Confiança"
+
+msgid "ciReport|Container scanning"
msgstr ""
msgid "ciReport|Container scanning detected"
-msgstr ""
+msgstr "Verificação de contêiner detectada"
msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
-msgstr ""
-
-msgid "ciReport|Container scanning is loading"
-msgstr ""
+msgstr "A varredura de contêiner detectou vulnerabilidades conhecidas em suas imagens docker."
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
msgid "ciReport|DAST detected"
-msgstr ""
-
-msgid "ciReport|DAST is loading"
-msgstr ""
+msgstr "DAST detectado"
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|Dependency scanning"
msgstr ""
msgid "ciReport|Dependency scanning detected"
-msgstr ""
-
-msgid "ciReport|Dependency scanning is loading"
-msgstr ""
-
-msgid "ciReport|Dependency scanning resulted in error while loading results"
-msgstr ""
+msgstr "Verificação de dependência detectada"
msgid "ciReport|Description"
-msgstr ""
+msgstr "Descrição"
msgid "ciReport|Dismiss vulnerability"
-msgstr ""
+msgstr "Ignorar vulnerabilidade"
msgid "ciReport|Dismissed by"
-msgstr ""
+msgstr "Ignorado por"
msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
-msgstr ""
+msgstr "O Teste de Segurança de Aplicativos Dinâmicos (DAST) detecta vulnerabilidades conhecidas em seu aplicativo da web."
msgid "ciReport|Failed to load %{reportName} report"
-msgstr ""
+msgstr "Falha ao carregar o relatório %{reportName}"
msgid "ciReport|File"
-msgstr ""
+msgstr "Arquivo"
msgid "ciReport|Fixed:"
-msgstr ""
+msgstr "Corrigido:"
msgid "ciReport|Identifiers"
-msgstr ""
+msgstr "Identificadores"
msgid "ciReport|Instances"
-msgstr ""
+msgstr "Instâncias"
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
-msgstr ""
+msgstr "Saiba mais sobre como interagir com relatórios de segurança (Alpha)."
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
-msgstr ""
+msgstr "Gerenciamento de licenças não detectou novas licenças"
msgid "ciReport|Links"
-msgstr ""
+msgstr "Links"
msgid "ciReport|Loading %{reportName} report"
-msgstr ""
+msgstr "Carregando relatório %{reportName}"
msgid "ciReport|Method"
-msgstr ""
+msgstr "Método"
msgid "ciReport|Namespace"
-msgstr ""
+msgstr "Namespace"
msgid "ciReport|No changes to code quality"
-msgstr ""
+msgstr "Sem mudanças na qualidade do código"
msgid "ciReport|No changes to performance metrics"
-msgstr ""
+msgstr "Sem mudanças nas métricas de desempenho"
msgid "ciReport|Performance metrics"
-msgstr ""
+msgstr "Métricas de desempenho"
msgid "ciReport|Revert dismissal"
-msgstr ""
-
-msgid "ciReport|SAST detected"
-msgstr ""
+msgstr "Reverter ignorar"
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
-msgstr ""
+msgid "ciReport|SAST detected"
+msgstr "SAST detectado"
msgid "ciReport|Security scanning"
-msgstr ""
+msgstr "Verificação de segurança"
msgid "ciReport|Security scanning failed loading any results"
-msgstr ""
-
-msgid "ciReport|Security scanning is loading"
-msgstr ""
+msgstr "A verificação de segurança falhou ao carregar resultados"
msgid "ciReport|Severity"
-msgstr ""
+msgstr "Severidade"
msgid "ciReport|Solution"
-msgstr ""
+msgstr "Solução"
msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
-msgstr ""
+msgstr "O Teste de Segurança de Aplicativos Estáticos (SAST) detecta vulnerabilidades conhecidas em seu código-fonte."
msgid "ciReport|There was an error creating the issue. Please try again."
-msgstr ""
+msgstr "Ocorreu um erro ao criar a issue. Por favor, tente novamente."
msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
-msgstr ""
+msgstr "Ocorreu um erro ao descartar a vulnerabilidade. Por favor, tente novamente."
msgid "ciReport|There was an error loading DAST report"
-msgstr ""
+msgstr "Ocorreu um erro ao carregar o relatório DAST"
msgid "ciReport|There was an error loading SAST report"
-msgstr ""
+msgstr "Ocorreu um erro ao carregar o relatório SAST"
msgid "ciReport|There was an error loading container scanning report"
-msgstr ""
+msgstr "Ocorreu um erro ao carregar o relatório de verificação do contêiner"
msgid "ciReport|There was an error loading dependency scanning report"
-msgstr ""
+msgstr "Ocorreu um erro ao carregar o relatório de verificação de dependência"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
-msgstr ""
-
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
+msgstr "Houve um erro ao reverter o descarte. Por favor, tente novamente."
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
-msgstr ""
+msgstr "Atualizar %{name} de %{version} para %{fixed}."
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] "Usado por %{packagesString}"
+msgstr[1] "Usado por %{packagesString} e %{lastPackage}"
msgid "ciReport|View full report"
-msgstr ""
+msgstr "Visualizar relatório completo"
msgid "ciReport|no vulnerabilities"
-msgstr ""
+msgstr "sem vulnerabilidades"
msgid "ciReport|on pipeline"
msgstr "na pipeline"
@@ -7476,14 +8812,20 @@ msgstr "na pipeline"
msgid "command line instructions"
msgstr "instruções da linha de comando"
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr "conectando"
msgid "could not read private key, is the passphrase correct?"
-msgstr ""
+msgstr "não foi possível ler a chave privada, a senha está correta?"
msgid "customize"
-msgstr ""
+msgstr "personalizar"
msgid "day"
msgid_plural "days"
@@ -7493,24 +8835,11 @@ msgstr[1] "dias"
msgid "deploy token"
msgstr "token de deploy"
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr "desabilitado"
msgid "done"
-msgstr ""
+msgstr "concluído"
msgid "enabled"
msgstr "habilitado"
@@ -7522,28 +8851,42 @@ msgid "for this project"
msgstr "para este projeto"
msgid "here"
-msgstr ""
+msgstr "aqui"
+
+msgid "https://your-bitbucket-server"
+msgstr "https://seu-servidor-do-bitbucket"
msgid "import flow"
-msgstr ""
+msgstr "fluxo de importação"
msgid "importing"
msgstr "importando"
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
-msgstr ""
+msgstr "é inválido porque há bloqueio no recebimento de dados"
msgid "is invalid because there is upstream lock"
-msgstr ""
+msgstr "é inválido porque há bloqueio no envio de dados"
msgid "is not a valid X509 certificate."
-msgstr ""
+msgstr "não é um certificado X509 válido."
+
+msgid "issue boards"
+msgstr "painéis"
msgid "latest version"
-msgstr ""
+msgstr "versão mais recente"
+
+msgid "license management"
+msgstr "gerenciamento de licenças"
msgid "locked by %{path_lock_user_name} %{created_at}"
-msgstr ""
+msgstr "bloqueador por %{path_lock_user_name} %{created_at}"
msgid "merge request"
msgid_plural "merge requests"
@@ -7563,28 +8906,31 @@ msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasis
msgstr "%{metricsLinkStart} Memória %{metricsLinkEnd} uso é %{emphasisStart} inalterado %{emphasisEnd} em %{memoryFrom}MB"
msgid "mrWidget|Add approval"
-msgstr ""
+msgstr "Adicionar aprovação"
msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr "Permite commits de membros que podem fazer merge ao branch de destino"
msgid "mrWidget|An error occured while removing your approval."
+msgstr "Ocorreu um erro ao remover sua aprovação."
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
msgstr ""
msgid "mrWidget|An error occurred while submitting your approval."
-msgstr ""
+msgstr "Ocorreu um erro ao enviar sua aprovação."
msgid "mrWidget|Approve"
-msgstr ""
+msgstr "Aprovar"
msgid "mrWidget|Approved by"
-msgstr ""
+msgstr "Aprovado por"
msgid "mrWidget|Cancel automatic merge"
msgstr "Cancelar merge request automático"
msgid "mrWidget|Check out branch"
-msgstr "Fazer checkout de branch"
+msgstr "Checkout branch"
msgid "mrWidget|Checking ability to merge automatically"
msgstr "Verificando a capacidade de merge automaticamente"
@@ -7602,7 +8948,7 @@ msgid "mrWidget|Closed by"
msgstr "Fechado por"
msgid "mrWidget|Closes"
-msgstr ""
+msgstr "Fecha"
msgid "mrWidget|Create an issue to resolve them later"
msgstr "Criar uma issue para resolvê-los mais tarde"
@@ -7619,6 +8965,9 @@ msgstr "Email patches"
msgid "mrWidget|Failed to load deployment statistics"
msgstr "Falha ao carregar estatísticas de deploy"
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr "Se o branch %{branch} existir em seu repositório local, você poderá fazer o merge request manualmente usando o"
@@ -7641,26 +8990,32 @@ msgid "mrWidget|Merge locally"
msgstr "Fazer merge localmente"
msgid "mrWidget|Merge request approved"
-msgstr ""
+msgstr "Merge request aprovado"
msgid "mrWidget|Merge request approved; you can approve additionally"
-msgstr ""
+msgstr "Merge request aprovado; você pode adicionalmente"
msgid "mrWidget|Merged by"
msgstr "Merge realizado por"
msgid "mrWidget|No Approval required"
-msgstr ""
+msgstr "Nenhuma aprovação necessária"
msgid "mrWidget|No Approval required; you can still approve"
-msgstr ""
+msgstr "Nenhuma aprovação necessária; você ainda pode aprovar"
msgid "mrWidget|Open in Web IDE"
msgstr "Abrir na Web IDE"
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr "Diff em texto"
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr "Atualizar"
@@ -7677,14 +9032,27 @@ msgid "mrWidget|Remove source branch"
msgstr "Remover branch de origem"
msgid "mrWidget|Remove your approval"
-msgstr ""
+msgstr "Remover sua aprovação"
msgid "mrWidget|Request to merge"
-msgstr "Solicitar merge"
+msgstr "Merge de"
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
msgid "mrWidget|Resolve conflicts"
msgstr "Resolver conflitos"
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr "Reverter"
@@ -7703,9 +9071,18 @@ msgstr "Não houve merge para as mudanças em"
msgid "mrWidget|The changes will be merged into"
msgstr "Será feito merge das alterações em"
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr "O branch de origem foi removido"
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr "O branch de origem está sendo removido"
@@ -7730,6 +9107,9 @@ msgstr "Esse merge request está em processamento"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "Este projeto está arquivado, a escrita foi desativada"
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "Você pode fazer merge manualmente usando o"
@@ -7743,11 +9123,14 @@ msgid "mrWidget|command line"
msgstr "linha de comando"
msgid "mrWidget|into"
-msgstr "dentro"
+msgstr "para"
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr "para ser realizado merge automaticamente quando o pipeline for bem sucedido"
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "novo merge request"
@@ -7757,6 +9140,11 @@ msgstr "emails de notificação"
msgid "or"
msgstr "ou"
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "pai"
@@ -7769,16 +9157,19 @@ msgid "personal access token"
msgstr "token de acesso pessoal"
msgid "private key does not match certificate."
-msgstr ""
+msgstr "chave privada não corresponde ao certificado."
msgid "remaining"
msgstr "restante"
+msgid "remove"
+msgstr "remover"
+
msgid "remove due date"
msgstr "remover a data de vencimento"
msgid "remove weight"
-msgstr ""
+msgstr "remover peso"
msgid "source"
msgstr "origem"
@@ -7787,7 +9178,7 @@ msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} irá atualizar a soma do tempo gasto."
msgid "started"
-msgstr ""
+msgstr "iniciado"
msgid "this document"
msgstr "este documento"
@@ -7795,6 +9186,9 @@ msgstr "este documento"
msgid "to help your contributors communicate effectively!"
msgstr "para ajudar seus contribuintes à se comunicar de maneira eficaz!"
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr "nome do usuário"
@@ -7802,7 +9196,7 @@ msgid "uses Kubernetes clusters to deploy your code!"
msgstr "use clusters Kubernetes para deploy do seu código!"
msgid "view it on GitLab"
-msgstr ""
+msgstr "ver no GitLab"
msgid "with %{additions} additions, %{deletions} deletions."
msgstr "com %{additions} adições, %{deletions} remoções."
diff --git a/locale/ro_RO/gitlab.po b/locale/ro_RO/gitlab.po
new file mode 100644
index 00000000000..a6b70d486fb
--- /dev/null
+++ b/locale/ro_RO/gitlab.po
@@ -0,0 +1,9269 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Romanian\n"
+"Language: ro_RO\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2);\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: ro\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 09:27\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
index e5627672761..739f96cba9a 100644
--- a/locale/ru/gitlab.po
+++ b/locale/ru/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:39\n"
+"PO-Revision-Date: 2018-10-02 09:26\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -60,6 +63,20 @@ msgstr[1] "%d ÑкÑпортера"
msgstr[2] "%d ÑкÑпортеров"
msgstr[3] "%d ÑкÑпортеров"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d обÑуждение"
@@ -88,13 +105,6 @@ msgstr[1] "%d метрики"
msgstr[2] "%d метрик"
msgstr[3] "%d метрик"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] "%d запиÑанное изменение"
@@ -104,7 +114,7 @@ msgstr[3] "%d запиÑанных изменений"
msgid "%d unstaged change"
msgid_plural "%d unstaged changes"
-msgstr[0] "%d не запиÑанное изменение"
+msgstr[0] "%d незапиÑанных изменений"
msgstr[1] "%d не запиÑанных изменений"
msgstr[2] "%d не запиÑанных изменений"
msgstr[3] "%d не запиÑанных изменений"
@@ -142,9 +152,15 @@ msgstr[3] "%{count} учаÑтников"
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} Запущено"
@@ -192,16 +208,15 @@ msgstr "%{text} доÑтупен"
msgid "%{title} changes"
msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² %{title}"
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "%{unstaged} не зафикÑированных и %{staged} зафикÑированных изменений"
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr "+ ещё %{moreCount}"
@@ -242,6 +257,13 @@ msgstr[1] "%d закрытых запроÑов на ÑлиÑние"
msgstr[2] "%d закрытых запроÑов на ÑлиÑние"
msgstr[3] "%d закрытых запроÑов на ÑлиÑние"
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] "1 объединенный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ ÑлиÑние"
@@ -270,6 +292,20 @@ msgstr[1] "%d Ñборочных линии"
msgstr[2] "%d Ñборочных линий"
msgstr[3] "%d Ñборочных линий"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1st contribution!"
msgstr "Первый вклад!"
@@ -303,6 +339,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -318,12 +357,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>УдалÑет</strong> иÑходную ветку"
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
-msgstr "«Runner» - Ñто процеÑÑ, который выполнÑет задание (обработчиков заданий). Ð’Ñ‹ можете наÑтроить Ñтолько таких процеÑÑов, Ñколько вам нужно."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Графики непрерывной интеграции (CI)"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -366,13 +411,16 @@ msgstr "Токены ДоÑтупа"
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "ДоÑтуп к вышедшим из ÑÑ‚Ñ€Ð¾Ñ Ñ…Ñ€Ð°Ð½Ð¸Ð»Ð¸Ñ‰Ð°Ð¼ временно отключен Ð´Ð»Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ñти Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² целÑÑ… воÑÑтановлениÑ. СброÑьте информацию о хранилищах поÑле уÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹, чтобы разрешить доÑтуп."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -405,15 +453,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr "Добавить Kubernetes клаÑтер"
-msgid "Add License"
-msgstr "Добавить Лицензию"
-
msgid "Add Readme"
msgstr "Добавить Информацию"
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -432,6 +480,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -444,6 +495,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "ОÑтановить вÑе заданиÑ"
@@ -462,6 +519,9 @@ msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ оÑтановить вÑе заданиÑ.
msgid "AdminHealthPageLink|health page"
msgstr "Ñтраница работоÑпоÑобноÑти"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr "Удалить"
@@ -510,6 +570,9 @@ msgstr "Ð’Ñе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ñ„Ð¸ÐºÑированы"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "Ð’Ñе функции ÑиÑтемы включаютÑÑ Ð´Ð»Ñ Ð¿ÑƒÑÑ‚Ñ‹Ñ… проектов, Ñозданных из шаблонов или импортированных, но вы можете отключить их впоÑледÑтвии в наÑтройках проекта."
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -537,6 +600,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -555,7 +621,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -606,13 +672,25 @@ msgstr "Произошла ошибка при предварительном п
msgid "An error occurred while fetching sidebar data"
msgstr "Произошла ошибка при получении денег данных Ð´Ð»Ñ Ð±Ð¾ÐºÐ¾Ð²Ð¾Ð¹ панели"
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr "Произошла ошибка при получении Ñборочной линии."
msgid "An error occurred while getting projects"
msgstr "Произошла ошибка при получении ÑпиÑка проектов"
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -702,12 +780,18 @@ msgstr "Ðпрель"
msgid "Archived project! Repository and other project resources are read-only"
msgstr "Ðрхивный проект! Репозиторий и другие реÑурÑÑ‹ проекта доÑтупны только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ"
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ð’Ñ‹ дейÑтвительно хотите удалить Ñто раÑпиÑание Ñборочной линии?"
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -726,13 +810,16 @@ msgstr ""
msgid "Are you sure?"
msgstr "Вы уверены?"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr "Ðртефакты"
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -765,7 +852,7 @@ msgstr "Ðазначить мне"
msgid "Assignee"
msgstr "ОтветÑтвенный"
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -792,6 +879,9 @@ msgstr "Журнал аутентификации"
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr "Ðвтор"
@@ -852,6 +942,9 @@ msgstr "Ð”Ð»Ñ Ñтого проекта может быть активировÐ
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "Подробнее по ÑÑылке %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr "Ð’Ñ‹ можете автоматичеÑки Ñобирать и теÑтировать Ñвое приложение, еÑли Ð´Ð»Ñ Ñтого проекта %{link_to_auto_devops_settings}. Ð’Ñ‹ также можете автоматичеÑки развернуть Ñвое приложение, еÑли вы %{link_to_add_kubernetes_cluster}."
@@ -861,6 +954,9 @@ msgstr "добавите клаÑтер Kubernetes"
msgid "AutoDevOps|enable Auto DevOps"
msgstr " включить ÐвтоматичеÑкий DevOps"
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr "ДоÑтупен"
@@ -885,9 +981,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr "Фоновые заданиÑ"
-
msgid "Badges"
msgstr "Значки"
@@ -927,6 +1020,9 @@ msgstr "Ðет Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐºÐ°"
msgid "Badges|No image to preview"
msgstr "Ðет Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð²Ð°Ñ€Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ проÑмотра"
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr "Значок Проекта"
@@ -954,9 +1050,15 @@ msgstr "Эта группа не имеет значков"
msgid "Badges|This project has no badges"
msgstr "Этот проект не имеет значков"
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr "Ваши значки"
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr "Ðачать Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð¾Ð³Ð¾ коммита"
@@ -1032,6 +1134,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1204,6 +1309,9 @@ msgstr "ПроÑмотр файлов"
msgid "Browse files"
msgstr "ПроÑмотр файлов"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1216,6 +1324,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1228,9 +1339,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr "ÐаÑтройки CI/CD"
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr "ÐвтоматичеÑкий DevOps"
@@ -1243,39 +1351,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
msgid "CICD|Deployment strategy"
msgstr ""
msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
-msgid "CICD|Disable Auto DevOps"
-msgstr "Отключить Auto DevOps"
-
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr "Включить Auto DevOps"
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr "ЭкземплÑÑ€ по умолчанию (%{state})"
-
msgid "CICD|Jobs"
msgstr "ЗаданиÑ"
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1303,6 +1405,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr "Измените Ñто значение, чтобы уÑтановить как чаÑто GitLab UI запрашивает обновлениÑ."
@@ -1360,6 +1465,12 @@ msgstr "Выберите Файл..."
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Выберите ветку/тег (например, %{master}) или введите коммит (например, %{sha}), чтобы увидеть, что изменилоÑÑŒ или Ñоздать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние."
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr "Выберите любой цвет."
@@ -1522,6 +1633,9 @@ msgstr "Клонировать репозиторий"
msgid "Close"
msgstr "Закрыть"
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1531,6 +1645,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} уÑпешно уÑтановлены на вашем клаÑтере Kubernetes"
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr "ÐÐ´Ñ€ÐµÑ API"
@@ -1540,12 +1657,21 @@ msgstr "Добавить клаÑтер Kubernetes"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Дополнительные опции Ð´Ð»Ñ Ñтой интеграции Ñ ÐºÐ»Ð°Ñтером Kubernetes"
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr "Произошла ошибка при попытке Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð·Ð¾Ð½ проекта: %{error}"
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr "Произошла ошибка при попытке Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð²Ð°ÑˆÐ¸Ñ… проектов: %{error}"
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr "ПриложениÑ"
@@ -1558,11 +1684,11 @@ msgstr "Сертификат удоÑтоверÑющего центра"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Комплект Ñертификатов удоÑтоверÑющего центра (формат PEM)"
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "Выберите, какие Ñреды вашего проекта будут иÑпользовать Ñтот клаÑтер Kubernetes."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr "УправлÑйте тем, как ваш клаÑтер Kubernetes интегрируетÑÑ Ñ GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
msgid "ClusterIntegration|Copy API URL"
msgstr "Скопировать Ð°Ð´Ñ€ÐµÑ API"
@@ -1588,6 +1714,12 @@ msgstr "Создать клаÑтер Kubernetes"
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "Введите ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ вашем клаÑтере Kubernetes"
@@ -1612,6 +1744,9 @@ msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ Ñ GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1624,6 +1759,9 @@ msgstr "Проект Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1639,6 +1777,9 @@ msgstr "Ingress"
msgid "ClusterIntegration|Ingress IP Address"
msgstr "IP-Ð°Ð´Ñ€ÐµÑ Ingress"
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr "УÑтановить"
@@ -1663,6 +1804,9 @@ msgstr "Ð˜Ð¼Ñ Ñ…Ð¾Ñта ÑервиÑа Jupyter"
msgid "ClusterIntegration|JupyterHub"
msgstr "Хаб ÑервиÑа Jupyter"
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "КлаÑтер Kubernetes"
@@ -1675,15 +1819,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтера Kubernetes"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтера Kubernetes Ð´Ð»Ñ Ñтого проекта отключена."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтера Kubernetes Ð´Ð»Ñ Ñтого проекта включена."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "Ð”Ð»Ñ Ñтого проекта включена Ð¸Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтера Kubernetes. Отключение интеграции не повлиÑет на клаÑтер Kubernetes, но Ñоединение Ñ GitLab будет временно отключено."
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "КлаÑтер Kubernetes ÑоздаётÑÑ Ð² Google Kubernetes Engine..."
@@ -1708,12 +1843,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "Узнайте больше о Ñредах"
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr "Узнайте больше о наÑтройке безопаÑноÑти"
-
msgid "ClusterIntegration|Machine type"
msgstr "Тип машины"
@@ -1756,6 +1885,9 @@ msgstr "ПожалуйÑта, укажите параметры доÑтупа Ð
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "ПожалуйÑта, убедитеÑÑŒ, что ваш аккаунт Google отвечает Ñледующим требованиÑм:"
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr "ПроÑтранÑтво имён проекта"
@@ -1765,6 +1897,12 @@ msgstr "ПроÑтранÑтво имен проекта (необÑзатель
msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr "Прочтите нашу документацию %{link_to_help_page} по интеграции клаÑтера Kubernetes."
@@ -1777,6 +1915,9 @@ msgstr "Удалить интеграцию"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "Удалить Ñту конфигурацию клаÑтера Kubernetes из Ñтого проекта. Это не приведет к удалению вашего фактичеÑкого клаÑтера Kubernetes."
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "Ðе удалоÑÑŒ выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° запуÑк процеÑÑа уÑтановки"
@@ -1792,9 +1933,6 @@ msgstr "ПоиÑк проектов"
msgid "ClusterIntegration|Search zones"
msgstr "ПоиÑк зон"
-msgid "ClusterIntegration|Security"
-msgstr "БезопаÑноÑÑ‚ÑŒ"
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "ПроÑмотр и редактирование информации о вашем клаÑтере Kubernetes"
@@ -1831,12 +1969,18 @@ msgstr "Что-то пошло не так при Ñоздании клаÑтеÑ
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Произошли ошибки во Ð²Ñ€ÐµÐ¼Ñ ÑƒÑтановки %{title}"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr "По умолчанию ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтера предоÑтавлÑет доÑтуп к широкому набору функциональных возможноÑтей, необходимых Ð´Ð»Ñ ÑƒÑпешного ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¸ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ð¹ в контейнерах."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr " У Ñтой учетной запиÑи должны быть Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð½Ð° Ñоздание клаÑтера Kubernetes в %{link_to_container_project} указанных ниже"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "Переключить КлаÑтер Kubernetes"
@@ -1849,9 +1993,15 @@ msgstr "Токен"
msgid "ClusterIntegration|Validating project billing status"
msgstr "Проверка ÑтатуÑа тарификации проекта"
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "ЕÑли привÑзать клаÑтер Kubernetes к Ñтому проекту, вы Ñ Ð»Ñ‘Ð³ÐºÐ¾Ñтью Ñможете иÑпользовать Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ€ÐµÐ²ÑŒÑŽ, развертывать ваши приложениÑ, запуÑкать Ñборочные линии и многое другое."
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "Ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ должна иметь %{link_to_kubernetes_engine}"
@@ -1870,9 +2020,6 @@ msgstr "документациÑ"
msgid "ClusterIntegration|help page"
msgstr "Ñтраница Ñправки"
-msgid "ClusterIntegration|installing applications"
-msgstr "ClusterIntegration | уÑтановка приложений"
-
msgid "ClusterIntegration|meets the requirements"
msgstr "отвечает требованиÑм"
@@ -1882,6 +2029,9 @@ msgstr "правильно наÑтроен"
msgid "ClusterIntegration|sign up"
msgstr "зарегиÑтрироватьÑÑ"
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1935,6 +2085,9 @@ msgstr "Коммит"
msgid "CommitMessage|Add %{file_name}"
msgstr "Добавить %{file_name}"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "Коммиты"
@@ -2007,16 +2160,13 @@ msgstr "КонфиденциальноÑÑ‚ÑŒ"
msgid "Configure Gitaly timeouts."
msgstr "ÐаÑтройка таймаутов Gitaly."
-msgid "Configure Sidekiq job throttling."
-msgstr "ÐаÑтройте фактор уÑÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ очереди иÑполнителей фоновых заданий Sidekiq."
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr "ÐаÑтройка ограничений Ð´Ð»Ñ Web и API запроÑов."
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2106,6 +2256,9 @@ msgstr "УчаÑтие"
msgid "Contribution guide"
msgstr "РуководÑтво учаÑтника"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2139,6 +2292,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2244,9 +2406,6 @@ msgstr "Ðовый"
msgid "Create project label"
msgstr "Создать метку проекта"
-msgid "CreateNewFork|Fork"
-msgstr "Ответвить"
-
msgid "CreateTag|Tag"
msgstr "Тег"
@@ -2262,6 +2421,9 @@ msgstr ""
msgid "Created by me"
msgstr "Создано мной"
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2274,6 +2436,9 @@ msgstr "Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð·Ð¾Ð½Ð° Cron"
msgid "Cron syntax"
msgstr "СинтакÑÐ¸Ñ Cron"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2283,6 +2448,9 @@ msgstr "Профиль"
msgid "CurrentUser|Settings"
msgstr "ÐаÑтройки"
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2292,6 +2460,9 @@ msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð½Ð°Ñтраиваемых уведомлений"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "ÐаÑтраиваемые уровни уведомлений аналогичны уровню уведомлений в ÑоответÑтвии Ñ ÑƒÑ‡Ð°Ñтием. С наÑтраиваемыми уровнÑми уведомлений вы также будете получать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾ выбранных ÑобытиÑÑ…. Чтобы узнать больше, поÑмотрите %{notification_link}."
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2304,6 +2475,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "Ðналитика Цикла"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "ÐапиÑание кода"
@@ -2334,6 +2508,12 @@ msgstr "Ð’Ñе"
msgid "DashboardProjects|Personal"
msgstr "Личные"
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr "Дек."
@@ -2343,6 +2523,9 @@ msgstr "Декабрь"
msgid "Decline and sign out"
msgstr "Отклонить и выйти"
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2358,6 +2541,9 @@ msgstr "Определить наÑтраиваемый шаблон Ñ Ñинт
msgid "Delete"
msgstr "Удалить"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2521,12 +2707,18 @@ msgstr ""
msgid "Details"
msgstr "ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° недоÑтупно"
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "Ð˜Ð¼Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð°"
@@ -2539,9 +2731,21 @@ msgstr "Отключить Ð´Ð»Ñ Ñтого проекта"
msgid "Disable group Runners"
msgstr "Выключить групповые обработчиков заданий"
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr "Отменить изменениÑ"
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr "Удалить черновик"
@@ -2641,7 +2845,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2701,12 +2905,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr "Включите панель производительноÑти Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð¹ группы."
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr "ЗаканчиваетÑÑ Ð² (UTC)"
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr "Среды"
@@ -2737,6 +2962,9 @@ msgstr "Окружение"
msgid "Environments|Environments"
msgstr "ОкружениÑ"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr "Задание"
@@ -2752,6 +2980,9 @@ msgstr "Еще нет развертываний"
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2785,6 +3016,9 @@ msgstr "Обновлено"
msgid "Environments|You don't have any environments right now."
msgstr "Ð’Ñ‹ пока не наÑтроили ни одного окружениÑ."
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2800,6 +3034,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr "Отчеты об ошибках и журналирование"
@@ -2824,6 +3082,9 @@ msgstr "Ошибка при получении данных об иÑпользÐ
msgid "Error loading branch data. Please try again."
msgstr "Ошибка загрузки данных ветки. ПожалуйÑта, попробуйте еще раз."
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr "Ошибка загрузки поÑледнего коммита."
@@ -2836,6 +3097,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr "Ошибка загрузки данных проекта. ПожалуйÑта, попробуйте еще раз."
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Произошла ошибка при переключении подпиÑки на оповещение"
@@ -2848,6 +3115,9 @@ msgstr "Ошибка при обновлении ÑтатуÑа Ð´Ð»Ñ Ð²Ñех
msgid "Error updating todo status."
msgstr "Ошибка при обновлении ÑтатуÑа дела."
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2890,6 +3160,9 @@ msgstr "Развернуть вÑе"
msgid "Expand sidebar"
msgstr "Развернуть боковую панель"
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2947,6 +3220,9 @@ msgstr "Ðе удалоÑÑŒ проверить ÑвÑзанные ветки."
msgid "Failed to remove issue from board, please try again."
msgstr "Ошибка при удалении обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ñ Ð´Ð¾Ñки, повторите попытку."
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "Ðе удалоÑÑŒ удалить раÑпиÑание Ñборочной линии"
@@ -2968,6 +3244,9 @@ msgstr "Февраль"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "ÐŸÐ¾Ð»Ñ Ð½Ð° Ñтой Ñтранице ÑÐµÐ¹Ñ‡Ð°Ñ Ð½ÐµÐ´Ð¾Ñтупны Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ, вы можете наÑтроить"
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "Файлы"
@@ -2980,9 +3259,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Фильтр по комментариÑми к коммитам"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "ПоиÑк по пути"
@@ -2995,6 +3283,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr "Завершено"
@@ -3004,6 +3295,18 @@ msgstr "Первый"
msgid "FirstPushedBy|pushed by"
msgstr "отправлено автором"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -3031,19 +3334,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Ответвление"
-msgstr[1] "ОтветвлениÑ"
-msgstr[2] "Ответвлений"
-msgstr[3] "ОтветвлениÑ"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "Ответвлено от"
@@ -3065,6 +3367,9 @@ msgstr "Из %{provider_title}"
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3080,6 +3385,9 @@ msgstr "От ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð´Ð¾ развертывани
msgid "From merge request merge until deploy to production"
msgstr "От запроÑа на ÑлиÑние до Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð² рабочей Ñреде"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3095,6 +3403,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr "Создать Ñтандартный набор меток"
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3260,33 +3571,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3335,6 +3745,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr "Gitaly"
@@ -3356,14 +3769,11 @@ msgstr "ВернутьÑÑ Ð½Ð°Ð·Ð°Ð´"
msgid "Go back"
msgstr "ВернутьÑÑ"
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "Перейти к вашему ответвлению"
-
-msgid "GoToYourFork|Fork"
-msgstr "Ответвление"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr ""
@@ -3425,13 +3835,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3446,6 +3856,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Запретить публикацию проектов из %{group} в других группах"
@@ -3509,6 +3928,9 @@ msgstr "Группы не найдены"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "Ð’Ñ‹ можете управлÑÑ‚ÑŒ правами и доÑтупом учаÑтников вашей группы к каждому проекту в группе."
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr "Создать проект в Ñтой группе."
@@ -3521,20 +3943,20 @@ msgstr "Редактировать группу"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr "Ðе удалоÑÑŒ покинуть группу. ПожалуйÑта, убедитеÑÑŒ, что вы не единÑтвенный владелец."
-msgid "GroupsTree|Filter by name..."
-msgstr "Фильтр по имени..."
-
msgid "GroupsTree|Leave this group"
msgstr "Покинуть Ñту группу"
msgid "GroupsTree|Loading groups"
msgstr "Загрузка групп"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "К Ñожалению, по вашему запроÑу групп не найдено"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "К Ñожалению, по вашему запроÑу групп или проектов не найдено"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
msgid "Have your users email"
msgstr ""
@@ -3569,6 +3991,15 @@ msgstr "Страница Ñправки"
msgid "Help page text and support page url."
msgstr "ТекÑÑ‚ Ñтраницы Ñправки и Url-Ð°Ð´Ñ€ÐµÑ Ñтраницы поддержки."
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "Скрыть значение"
@@ -3594,21 +4025,45 @@ msgstr ""
msgid "ID"
msgstr "ID"
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr "Коммит"
msgid "IDE|Edit"
msgstr "Редактировать"
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr "Обзор"
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3618,6 +4073,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3675,6 +4133,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3684,6 +4145,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr "Импорт репозиториев из GitHub"
@@ -3702,18 +4166,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3727,6 +4206,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr "ЭкземплÑÑ€ не поддерживает неÑколько клаÑтеров Kubernetes"
@@ -3745,12 +4230,21 @@ msgstr "Внутренний - Группу и включённые в неё п
msgid "Internal - The project can be accessed by any logged in user."
msgstr "Внутренний - Проект доÑтупен любому зарегиÑтрированному пользователю."
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "Шаблон интервала"
msgid "Introducing Cycle Analytics"
msgstr "Внедрение Цикла Ðналитик"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3790,6 +4284,48 @@ msgstr "Фоновое задание было удалено"
msgid "Jobs"
msgstr "ЗаданиÑ"
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
msgid "Jul"
msgstr "Июл."
@@ -3802,12 +4338,6 @@ msgstr "Июн."
msgid "June"
msgstr "Июнь"
-msgid "Koding"
-msgstr "Koding"
-
-msgid "Koding Dashboard"
-msgstr ""
-
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -3877,6 +4407,9 @@ msgstr "<span>ПовыÑить метку</span> %{labelTitle} <span>до гру
msgid "Labels|Promote Label"
msgstr "ПеренеÑти Метку"
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "ПоÑледний %d день"
@@ -3890,6 +4423,9 @@ msgstr "ПоÑледнÑÑ Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð›Ð¸Ð½Ð¸Ñ"
msgid "Last commit"
msgstr "ПоÑледний коммит"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr "Дата поÑледнего изменениÑ: %{date}"
@@ -3914,6 +4450,9 @@ msgstr "ПоÑледние изменениÑ"
msgid "Learn more"
msgstr "Подробнее"
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr "Подробнее о Kubernates"
@@ -3941,6 +4480,64 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3953,9 +4550,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr "СпиÑок ваших репозиториев на GitHub"
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3974,6 +4577,9 @@ msgstr "Заблокировать %{issuableDisplayName}"
msgid "Lock not found"
msgstr "Блокировка не найдена"
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -4001,6 +4607,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -4055,9 +4664,21 @@ msgstr "Отметить как Ñделанное"
msgid "Markdown enabled"
msgstr "Включен режим Markdown"
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "МакÑимальное количеÑтво Ñбоев хранилища git"
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr "Май"
@@ -4106,15 +4727,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr "Слито"
@@ -4130,6 +4751,9 @@ msgstr "Метрики - Influx"
msgid "Metrics - Prometheus"
msgstr "Метрики - Prometheus"
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4232,9 +4856,24 @@ msgstr ""
msgid "Milestone"
msgstr "Этап"
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr "Этапы"
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr "Удалить Ñтап"
@@ -4253,9 +4892,30 @@ msgstr "ПовыÑить %{milestoneTitle} до группового Ñтапа?
msgid "Milestones|Promote Milestone"
msgstr "ПовыÑить Ñтап"
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "добавить ключ SSH"
@@ -4274,9 +4934,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4325,6 +4982,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4416,12 +5076,21 @@ msgstr ""
msgid "No assignee"
msgstr "Ðет ответÑтвенного"
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr "Ðет изменений"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr "Плановый Ñрок не указан"
@@ -4443,6 +5112,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4455,6 +5127,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4467,15 +5142,27 @@ msgstr ""
msgid "No repository"
msgstr "Ðет репозиториÑ"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "Ðет раÑпиÑаний"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr "ПуÑто"
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr "СлиÑние не допуÑкаетÑÑ"
@@ -4494,6 +5181,9 @@ msgstr "Ðе конфиденциально"
msgid "Not enough data"
msgstr "ÐедоÑтаточно данных"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Обратите внимание, что маÑтер ветка автоматичеÑки защищена. %{link_to_protected_branches}"
@@ -4527,6 +5217,9 @@ msgstr "Ðеудача в Ñборочной линии"
msgid "NotificationEvent|Merge merge request"
msgstr "Влит Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "Ðовое обÑуждение"
@@ -4599,18 +5292,28 @@ msgstr "Фильтр"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr "Только учаÑтники проекта могут оÑтавлÑÑ‚ÑŒ комментарии."
@@ -4674,9 +5377,21 @@ msgstr "ИÑходÑщие запроÑÑ‹"
msgid "Overview"
msgstr "Обзор"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "Владелец"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr "Страницы"
@@ -4707,9 +5422,15 @@ msgstr ""
msgid "Pause"
msgstr "ПриоÑтановить"
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr "В ожидании"
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4728,6 +5449,9 @@ msgstr "ПерÑональный Токен ДоÑтупа"
msgid "Pipeline"
msgstr "Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "РаботоÑпоÑобноÑÑ‚ÑŒ Сборочной Линии"
@@ -4815,6 +5539,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr "Ðачало работы Ñо Ñборочными линиÑми"
@@ -4836,6 +5563,9 @@ msgstr "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÑ‚ %{scope} Ñборочных ли
msgid "Pipelines|There are currently no pipelines."
msgstr "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÑ‚ Ñборочных линий."
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "Этот проект в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½Ðµ наÑтроен Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка Ñборочных линий."
@@ -4884,12 +5614,6 @@ msgstr "Ñо ÑтадиÑми"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr "PlantUML"
@@ -4929,6 +5653,15 @@ msgstr "ПредпочтениÑ"
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4959,18 +5692,48 @@ msgstr "Профиль"
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ запланирована к удалению."
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Изменить Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr "Текущий путь: %{path}"
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr "Удалить Учетную запиÑÑŒ"
@@ -4983,33 +5746,108 @@ msgstr "Удалить Ñвою учетную запиÑÑŒ?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr "Удаление учетной запиÑи приведет к Ñледующим поÑледÑтвиÑм:"
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr "Ðеверный пароль"
msgid "Profiles|Invalid username"
msgstr "Ðеверное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr "Путь"
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Введите значение %{confirmationValue} Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ:"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr "Обновить Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr "Ошибка Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ - %{message}"
msgid "Profiles|Username successfully changed"
msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÑƒÑпешно изменено"
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "У Ð²Ð°Ñ Ð½ÐµÑ‚ прав на удаление Ñтого пользователÑ."
@@ -5019,6 +5857,18 @@ msgstr "Перед удалением учётной запиÑи, вам нео
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ ÑвлÑетÑÑ Ð²Ð»Ð°Ð´ÐµÐ»ÑŒÑ†ÐµÐ¼ Ñледующих групп:"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -5052,6 +5902,9 @@ msgstr "Проект '%{project_name}' уÑпешно обновлен."
msgid "Project Badges"
msgstr "Значки Проекта"
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "ДоÑтуп к проекту должен предоÑтавлÑÑ‚ÑŒÑÑ Ñвно каждому пользователю."
@@ -5079,6 +5932,9 @@ msgstr "Ðачат ÑкÑпорт проекта. СÑылка Ð´Ð»Ñ Ñкачи
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "ПодпиÑатьÑÑ"
@@ -5106,18 +5962,48 @@ msgstr "Ðикогда"
msgid "ProjectLifecycle|Stage"
msgstr "Этап"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5157,6 +6043,9 @@ msgstr "У Ð½Ð°Ñ Ñ‡Ñ‚Ð¾-то пошло не так."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "К Ñожалению, по вашему запроÑу проекты не найдены"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5289,6 +6178,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr "ПодÑказка:"
@@ -5307,6 +6244,12 @@ msgstr "Публичный - ДоÑтуп к проекту возможен бÐ
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5346,12 +6289,28 @@ msgstr "ИнÑтрукциÑ"
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr "СÑылка:"
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr "РегиÑÑ‚Ñ€Ð°Ñ†Ð¸Ñ / Вход"
@@ -5403,6 +6362,18 @@ msgstr ""
msgid "Remove project"
msgstr "Удалить проект"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5412,6 +6383,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr "Репозиторий"
@@ -5463,9 +6482,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr "Закрыть диÑкуÑÑию"
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5478,6 +6509,9 @@ msgstr "Повторить Ñто фоновое задание"
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] "Показать значение"
@@ -5512,9 +6546,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr "Обработчики заданий"
@@ -5524,6 +6576,21 @@ msgstr "API обработчиков заданий"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr "Обработчики заданий могут запуÑкатьÑÑ Ñƒ отдельных пользователей, Ñерверах и даже на вашей локальной машине."
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr "ВыполнÑетÑÑ"
@@ -5539,12 +6606,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr "SSH Ключи"
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5611,12 +6687,42 @@ msgstr ""
msgid "Search milestones"
msgstr "ПоиÑк Ñтапов"
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr "ПоиÑк проекта"
msgid "Search users"
msgstr "ПоиÑк пользователей"
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "Секунд до очиÑтки информации о ÑбоÑÑ…"
@@ -5626,10 +6732,13 @@ msgstr "Секунд задержки между попытками доÑтуп
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5644,6 +6753,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr "Выбрать формат архива"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5677,6 +6789,12 @@ msgstr ""
msgid "Select target branch"
msgstr "Выбор целевой ветки"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5686,6 +6804,9 @@ msgstr ""
msgid "Send email"
msgstr "Отправить Ñлектронное пиÑьмо"
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr "Сент."
@@ -5713,6 +6834,9 @@ msgstr "УÑтановите пароль в Ñвоем аккаунте, что
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr "УÑтановить макÑимальное Ð²Ñ€ÐµÐ¼Ñ ÑеанÑа Ð´Ð»Ñ Ð²ÐµÐ±-терминала."
@@ -5725,21 +6849,24 @@ msgstr ""
msgid "Set up CI/CD"
msgstr "ÐаÑтройка CI/CD"
-msgid "Set up Koding"
-msgstr "ÐаÑтройка Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "уÑтановите пароль"
msgid "Settings"
msgstr "ÐаÑтройки"
-msgid "Setup a specific Runner automatically"
-msgstr "ÐаÑтроить конкретный обработчик заданий автоматичеÑки"
-
msgid "Share"
msgstr "ПоделитьÑÑ"
@@ -5749,6 +6876,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "Общие обработчики заданий"
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5825,12 +6955,18 @@ msgstr "ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñ…Ð¾Ð´Ð°"
msgid "Sign-up restrictions"
msgstr "ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ñ€ÐµÐ³Ð¸Ñтрации"
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr "ÐаÑтройки размера и доменных имён Ð´Ð»Ñ ÑтатичеÑких веб-Ñайтов"
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5846,13 +6982,19 @@ msgstr "Что-то пошло не так Ñ Ð½Ð°ÑˆÐµÐ¹ Ñтороны."
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr "Что-то пошло не так при переключении кнопки"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5906,6 +7048,9 @@ msgstr "ÐšÑ€ÑƒÐ¿Ð½ÐµÐ¹ÑˆÐ°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð°"
msgid "SortOptions|Largest repository"
msgstr "Крупнейший репозиторий"
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr "ПоÑледние Ñозданные"
@@ -5936,6 +7081,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr "Ðаиболее популÑрный"
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr "ИмÑ"
@@ -5966,6 +7114,9 @@ msgstr "Приоритет"
msgid "SortOptions|Recent sign in"
msgstr "Ðедавно заходившие"
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr "Ðачатые позже"
@@ -5996,6 +7147,9 @@ msgstr "Защита от Ñпама и ботов"
msgid "Specific Runners"
msgstr "Конкретные обработчики заданий"
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "Укажите Ñледующий URL во Ð²Ñ€ÐµÐ¼Ñ Ð½Ð°Ñтройки Gitlab Runner:"
@@ -6024,7 +7178,7 @@ msgid "Star a label to make it a priority label. Order the prioritized labels to
msgstr ""
msgid "StarProject|Star"
-msgstr "Отметить"
+msgstr "В избранное"
msgid "Starred Projects"
msgstr "Избранные проекты"
@@ -6033,11 +7187,14 @@ msgid "Starred Projects' Activity"
msgstr "ÐктивноÑÑ‚ÑŒ в избранных проектах"
msgid "Starred projects"
-msgstr "Отмеченные проекты"
+msgstr "Избранные проекты"
msgid "Start a %{new_merge_request} with these changes"
msgstr "Ðачать %{new_merge_request} Ñ Ñтих изменений"
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr "ЗапуÑтить GitLab Runner!"
@@ -6071,6 +7228,9 @@ msgstr ""
msgid "Subgroups"
msgstr "Подгруппы"
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6104,6 +7264,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] "Тег (%{tag_count})"
@@ -6114,6 +7277,9 @@ msgstr[3] "Тегов (%{tag_count})"
msgid "Tags"
msgstr "Теги"
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr "Теги:"
@@ -6195,6 +7361,12 @@ msgstr "Ð¦ÐµÐ»ÐµÐ²Ð°Ñ Ð²ÐµÑ‚ÐºÐ°"
msgid "Team"
msgstr "Команда"
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6210,6 +7382,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr "Трекер обÑуждений - Ñто меÑто, где можно добавить вещи, которые необходимо улучшить или решить в проекте"
@@ -6219,6 +7394,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "Этап напиÑÐ°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð° показывает Ð²Ñ€ÐµÐ¼Ñ Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ коммита до ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа на ÑлиÑние. Данные автоматичеÑки добавÑÑ‚ÑÑ Ñюда поÑле того, как вы Ñоздать Ñвой первый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние."
@@ -6228,6 +7406,9 @@ msgstr "ÐšÐ¾Ð»Ð»ÐµÐºÑ†Ð¸Ñ Ñобытий добавленных в данные
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "СвÑзь Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ð²Ð»ÐµÐ½Ð¸ÐµÐ¼ удалена."
@@ -6255,6 +7436,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "Фаза жизненного цикла разработки."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Этап Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚ Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ предыдущего шага до отправки первого коммита. ДобавлÑетÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки, как только отправите Ñвой первый коммит."
@@ -6282,6 +7466,9 @@ msgstr "Репозиторий Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ проекта пуÑтой
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr "Репозиторий должен быть доÑтупен через протоколы <code>http: //</code>, <code>https: //</code> или <code>git: //</code>."
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "Этап обзора показывает Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа ÑлиÑÐ½Ð¸Ñ Ð´Ð¾ его выполнениÑ. Данные будут автоматичеÑки добавлены поÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ запроÑа на ÑлиÑние."
@@ -6294,6 +7481,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "Этап поÑтановки показывает Ð²Ñ€ÐµÐ¼Ñ Ð¼ÐµÐ¶Ð´Ñƒ ÑлиÑнием \"MR\" и развертыванием кода в производÑтвенной Ñреде. Данные будут автоматичеÑки добавлены поÑле Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð² производÑтве первый раз."
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Этап теÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚ времÑ, которое GitLab CI занимает Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка каждой Ñборочной линии Ð´Ð»Ñ ÑоответÑтвующего запроÑа на ÑлиÑние. Данные будут автоматичеÑки добавлены поÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ вашей первой Ñборочной линии."
@@ -6309,6 +7499,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "ВремÑ, затраченное каждым Ñлементом, Ñобранным на Ñтом Ñтапе."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6318,6 +7514,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "Среднее значение в Ñ€Ñду. Пример: между 3, 5, 9, Ñреднее 5, между 3, 5, 7, 8, Ñреднее (5+7)/2 = 6."
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr "Ðет обÑуждений, которые можно показать"
@@ -6327,9 +7526,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "Проблемы Ñ Ð´Ð¾Ñтупом к Git хранилищу: "
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6366,6 +7580,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6417,9 +7643,30 @@ msgstr "Это фоновое задание еще не запущено"
msgid "This job has not started yet"
msgstr "Эта задача еще не запущена"
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "Это фоновое задание находитÑÑ Ð² ÑоÑтоÑнии Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¸ ожидает запуÑка процеÑÑа иÑполнениÑ"
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr "Это фоновое задание требует ручных дейÑтвий"
@@ -6429,6 +7676,9 @@ msgstr "Это означает, что вы не можете отправитÑ
msgid "This merge request is locked."
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние заблокирован."
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6444,15 +7694,30 @@ msgstr "Этот проект"
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr "Этот проект не отноÑитÑÑ Ð½Ð¸ к какой группе и поÑтому не может иÑпользовать групповые обработчики заданий."
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr "Этот репозиторий"
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6659,12 +7924,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6692,7 +7969,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6707,6 +7984,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6722,6 +8002,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6749,6 +8032,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr "ЗапуÑтить Ñто дейÑтвие вручную"
@@ -6764,24 +8056,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr "Ðе удаетÑÑ Ð·Ð°Ð³Ñ€ÑƒÐ·Ð¸Ñ‚ÑŒ отличиÑ. %{button_try_again}"
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr "Разблокировать"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "Разблокировано"
msgid "Unresolve discussion"
msgstr "Переоткрыть диÑкуÑÑию"
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6798,7 +8105,7 @@ msgid "Unstaged and staged %{type}"
msgstr ""
msgid "Unstar"
-msgstr "СнÑÑ‚ÑŒ отметку"
+msgstr "Убрать из избранного"
msgid "Unsubscribe"
msgstr ""
@@ -6818,9 +8125,15 @@ msgstr "Ðктуальный"
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6845,15 +8158,15 @@ msgstr "Загрузить новый файл"
msgid "Upload file"
msgstr "Загрузить файл"
-msgid "Upload new avatar"
-msgstr "Загрузить аватар"
-
msgid "UploadLink|click to upload"
msgstr "кликните Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
msgid "Upvotes"
msgstr "ГолоÑа \"за\""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr "СтатиÑтика иÑпользованиÑ"
@@ -6869,6 +8182,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "ИÑпользуйте Ñледующий токен региÑтрации в процеÑÑе уÑтановки:"
@@ -6878,6 +8194,9 @@ msgstr "ИÑпользуютÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ‹Ð¹ наÑтройки увеÐ
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6911,6 +8230,9 @@ msgstr ""
msgid "Verified"
msgstr "Проверено"
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7202,18 +8524,21 @@ msgstr "Ð’Ñ‹ можете добавлÑÑ‚ÑŒ только файлы, когда
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr "Ð’Ñ‹ не можете запиÑывать на Ñтот ÑкземплÑÑ€ \"только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ\" клаÑтера GitLab."
-msgid "You do not have any assigned merge requests"
-msgstr ""
-
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
@@ -7226,9 +8551,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr "Ð’Ñ‹ доÑтигли Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð² вашем проекте"
@@ -7238,9 +8560,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr "Ðеобходимо войти, чтобы оценить проект"
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7250,6 +8569,12 @@ msgstr ""
msgid "You need permission."
msgstr "Вам нужно разрешение."
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "Ð’Ñ‹ не получите никаких уведомлений по Ñлектронной почте"
@@ -7328,19 +8653,15 @@ msgstr "Ваше имÑ"
msgid "Your projects"
msgstr "Ваши проекты"
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "assign yourself"
msgstr "назначить ÑебÑ"
@@ -7365,70 +8686,98 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7461,10 +8810,21 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7494,13 +8854,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7509,9 +8866,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7542,12 +8896,16 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7560,6 +8918,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7579,23 +8943,6 @@ msgstr[3] "дней"
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr "отключено"
@@ -7614,12 +8961,22 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7629,9 +8986,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7663,6 +9026,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7711,6 +9077,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7750,9 +9119,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7774,9 +9149,26 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr "Разрешить конфликты"
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7795,9 +9187,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7822,6 +9223,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7840,6 +9244,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "новый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
@@ -7849,6 +9256,13 @@ msgstr "email Ð´Ð»Ñ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ð¹"
msgid "or"
msgstr "или"
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "владелец"
@@ -7868,6 +9282,9 @@ msgstr ""
msgid "remaining"
msgstr "оÑталоÑÑŒ"
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr "убрать плановый Ñрок"
@@ -7889,6 +9306,9 @@ msgstr "Ñтот документ"
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr "Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
diff --git a/locale/sq_AL/gitlab.po b/locale/sq_AL/gitlab.po
new file mode 100644
index 00000000000..75df8a29e6b
--- /dev/null
+++ b/locale/sq_AL/gitlab.po
@@ -0,0 +1,9208 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Albanian\n"
+"Language: sq_AL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: sq\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-10-02 09:26\n"
+
+msgid " Status"
+msgstr ""
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
+msgstr ""
+
+msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr ""
+
+msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
+msgstr ""
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access expiration date"
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr ""
+
+msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
+msgstr ""
+
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "All users"
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Archived projects"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
+msgid "Are you sure you want to reset registration token?"
+msgstr ""
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr ""
+
+msgid "Are you sure you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifact ID"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee lists not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Authentication method"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Automatically marked as default internal user"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Built-In"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "CICD|instance enabled"
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change template"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
+msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgstr ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Close epic"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Code owners"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "CommitWidget|authored"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current Branch"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Custom project templates"
+msgstr ""
+
+msgid "Customize colors"
+msgstr ""
+
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default Branch"
+msgstr ""
+
+msgid "Default classification label"
+msgstr ""
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Package"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Detect host keys"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Direction"
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard changes to %{path}?"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
+msgid "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Environments|protected"
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading branches."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+msgstr ""
+
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
+msgid "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Expiration date"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove mirror."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "File templates"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Filter..."
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Fingerprints"
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From milestones:"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "GitLab’s issue tracker"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgstr ""
+
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
+msgid "GroupsTree|Create a project in this group."
+msgstr ""
+
+msgid "GroupsTree|Create a subgroup in this group."
+msgstr ""
+
+msgid "GroupsTree|Edit group"
+msgstr ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
+msgid "Health Check"
+msgstr ""
+
+msgid "Health information can be retrieved from the following endpoints. More information is available"
+msgstr ""
+
+msgid "HealthCheck|Access token is"
+msgstr ""
+
+msgid "HealthCheck|Healthy"
+msgstr ""
+
+msgid "HealthCheck|No Health Problems Detected"
+msgstr ""
+
+msgid "HealthCheck|Unhealthy"
+msgstr ""
+
+msgid "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "IP Address"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgstr ""
+
+msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
+msgstr ""
+
+msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Internal users"
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last contact"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Live preview"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "Maximum job timeout"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics and profiling"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "Never"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No branches found"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No license. All rights reserved"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No packages stored for this project."
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No runners found"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "Nodes"
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Not now"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New epic"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "Only admins"
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only mirror protected branches"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Options"
+msgstr ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Overwrite diverged branches"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
+msgid "PipelineSchedules|Variables"
+msgstr ""
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Current status"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update profile settings"
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project Badges"
+msgstr ""
+
+msgid "Project URL"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project details"
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project name"
+msgstr ""
+
+msgid "Project slug"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Badges"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Recent searches"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+msgstr ""
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr ""
+
+msgid "Related Merged Requests"
+msgstr ""
+
+msgid "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal Variables"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+msgstr ""
+
+msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
+msgstr ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SAST"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a group to invite"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Send usage data"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set instance-wide template repository"
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "Shared projects"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last Contact"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Most stars"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start date"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Subgroups and projects"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags feed"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
+
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr ""
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr ""
+
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+msgstr ""
+
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr ""
+
+msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
+msgstr ""
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr ""
+
+msgid "The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr ""
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr ""
+
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr ""
+
+msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr ""
+
+msgid "There are no archived projects yet"
+msgstr ""
+
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr ""
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle commit description"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Token"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update now"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Updating"
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage ping is not enabled"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
+msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
+msgid "You need permission."
+msgstr ""
+
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr ""
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "a deleted user"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|DAST"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "issue boards"
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "license management"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "n/a"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "toggle collapse"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/tr_TR/gitlab.po b/locale/tr_TR/gitlab.po
index e145780c68f..1a8d2faf307 100644
--- a/locale/tr_TR/gitlab.po
+++ b/locale/tr_TR/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: tr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:39\n"
+"PO-Revision-Date: 2018-10-02 09:26\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -48,6 +51,16 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -68,11 +81,6 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -110,9 +118,15 @@ msgstr[1] "%{count} katılımcı"
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -156,14 +170,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} daha fazla"
@@ -196,6 +211,11 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -216,6 +236,16 @@ msgid_plural "%d pipelines"
msgstr[0] ""
msgstr[1] ""
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1st contribution!"
msgstr "İlk katkı!"
@@ -249,6 +279,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -264,12 +297,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -312,13 +351,16 @@ msgstr "Erişim anahtarları"
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -351,15 +393,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr "Lisans Ekle"
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -378,6 +420,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -390,6 +435,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "Tüm işleri durdur"
@@ -408,6 +459,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -483,6 +540,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -501,7 +561,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -552,13 +612,25 @@ msgstr "Markdown ön izlemesi yüklenirken hata oluştu"
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr "Projeler yüklenirken bir hata oluştu"
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -648,12 +720,18 @@ msgstr "Nisan"
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -672,13 +750,16 @@ msgstr ""
msgid "Are you sure?"
msgstr "Emin misiniz?"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -711,7 +792,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -738,6 +819,9 @@ msgstr "Kimlik Doğrulama Günlüğü"
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr "Yazar"
@@ -798,6 +882,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -807,6 +894,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr "Kullanılabilir"
@@ -831,9 +921,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -873,6 +960,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -900,9 +990,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -978,6 +1074,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1148,6 +1247,9 @@ msgstr ""
msgid "Browse files"
msgstr ""
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1160,6 +1262,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1172,9 +1277,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1187,39 +1289,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1247,6 +1343,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1304,6 +1403,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1466,6 +1571,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1475,6 +1583,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1484,12 +1595,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1502,10 +1622,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1532,6 +1652,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1556,6 +1682,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1568,6 +1697,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1583,6 +1715,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1607,6 +1742,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1619,15 +1757,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1652,12 +1781,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1700,6 +1823,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1709,6 +1835,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1721,6 +1853,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1736,9 +1871,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1775,12 +1907,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1793,9 +1931,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1814,9 +1958,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1826,6 +1967,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1875,6 +2019,9 @@ msgstr ""
msgid "CommitMessage|Add %{file_name}"
msgstr ""
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr ""
@@ -1947,16 +2094,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2046,6 +2190,9 @@ msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2079,6 +2226,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2184,9 +2340,6 @@ msgstr ""
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr ""
-
msgid "CreateTag|Tag"
msgstr ""
@@ -2202,6 +2355,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2214,6 +2370,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2223,6 +2382,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2232,6 +2394,9 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2244,6 +2409,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -2274,6 +2442,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2283,6 +2457,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2298,6 +2475,9 @@ msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2459,12 +2639,18 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2477,9 +2663,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2579,7 +2777,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2639,12 +2837,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2675,6 +2894,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2690,6 +2912,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2723,6 +2948,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2738,6 +2966,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2762,6 +3014,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2774,6 +3029,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2786,6 +3047,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2828,6 +3092,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2885,6 +3152,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -2906,6 +3176,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -2918,9 +3191,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -2933,6 +3215,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2942,6 +3227,18 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2969,17 +3266,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -3001,6 +3299,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -3016,6 +3317,9 @@ msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -3031,6 +3335,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3196,33 +3503,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3271,6 +3677,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3292,13 +3701,10 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr ""
-
-msgid "GoToYourFork|Fork"
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Google Code import"
@@ -3361,13 +3767,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3382,6 +3788,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3445,6 +3860,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3457,19 +3875,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3505,6 +3923,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3528,21 +3955,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3552,6 +4003,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3609,6 +4063,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3618,6 +4075,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3636,18 +4096,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3659,6 +4134,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3677,12 +4158,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3722,22 +4212,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
msgstr ""
-msgid "Koding"
+msgid "Job|Keep"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3809,6 +4335,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3820,6 +4349,9 @@ msgstr ""
msgid "Last commit"
msgstr ""
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3844,6 +4376,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3871,6 +4406,62 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3883,9 +4474,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3904,6 +4501,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3931,6 +4531,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3985,9 +4588,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4036,15 +4651,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4060,6 +4675,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4162,9 +4780,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4183,9 +4816,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -4204,9 +4858,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4255,6 +4906,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4344,12 +4998,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4371,6 +5034,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4383,6 +5049,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4395,15 +5064,27 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr ""
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4422,6 +5103,9 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4455,6 +5139,9 @@ msgstr ""
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr ""
@@ -4527,18 +5214,26 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4602,9 +5297,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4635,9 +5342,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4656,6 +5369,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -4743,6 +5459,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4764,6 +5483,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4812,12 +5534,6 @@ msgstr ""
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4857,6 +5573,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4887,18 +5612,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4911,33 +5666,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4947,6 +5777,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4980,6 +5822,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -5007,6 +5852,9 @@ msgstr ""
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -5034,18 +5882,48 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5085,6 +5963,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5217,6 +6098,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5235,6 +6164,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5274,12 +6209,26 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5331,6 +6280,18 @@ msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5340,6 +6301,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -5391,9 +6400,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5406,6 +6427,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5438,9 +6462,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5450,6 +6492,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5465,12 +6522,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5537,12 +6603,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5552,10 +6648,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5570,6 +6669,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr ""
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5603,6 +6705,12 @@ msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5612,6 +6720,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5639,6 +6750,9 @@ msgstr ""
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5651,19 +6765,22 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
-msgid "SetPasswordToCloneLink|set a password"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
-msgid "Settings"
+msgid "SetPasswordToCloneLink|set a password"
msgstr ""
-msgid "Setup a specific Runner automatically"
+msgid "Settings"
msgstr ""
msgid "Share"
@@ -5675,6 +6792,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5749,12 +6869,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5770,13 +6896,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5830,6 +6962,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5860,6 +6995,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5890,6 +7028,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5920,6 +7061,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
@@ -5962,6 +7106,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr ""
@@ -5995,6 +7142,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -6028,6 +7178,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -6036,6 +7189,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6117,6 +7273,12 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6132,6 +7294,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6141,6 +7306,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
@@ -6150,6 +7318,9 @@ msgstr ""
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
@@ -6177,6 +7348,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -6204,6 +7378,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -6216,6 +7393,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -6231,6 +7411,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6240,6 +7426,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6249,9 +7438,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6288,6 +7492,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6339,9 +7555,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6351,6 +7588,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6366,15 +7606,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6577,12 +7832,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6610,7 +7877,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6625,6 +7892,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6640,6 +7910,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6667,6 +7940,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6682,24 +7964,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6736,9 +8033,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6763,15 +8066,15 @@ msgstr ""
msgid "Upload file"
msgstr ""
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr ""
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6787,6 +8090,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -6796,6 +8102,9 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6829,6 +8138,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7120,16 +8432,19 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7144,9 +8459,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr ""
@@ -7156,9 +8468,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr ""
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7168,6 +8477,12 @@ msgstr ""
msgid "You need permission."
msgstr ""
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr ""
@@ -7246,17 +8561,15 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "assign yourself"
msgstr ""
@@ -7281,70 +8594,90 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7377,10 +8710,17 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7410,13 +8750,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST is loading"
-msgstr ""
-
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7425,9 +8762,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7458,12 +8792,14 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7476,6 +8812,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7493,19 +8835,6 @@ msgstr[1] ""
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7524,12 +8853,20 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7539,9 +8876,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7571,6 +8914,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7619,6 +8965,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7658,9 +9007,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7682,9 +9037,22 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7703,9 +9071,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7730,6 +9107,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7748,6 +9128,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr ""
@@ -7757,6 +9140,11 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -7774,6 +9162,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7795,6 +9186,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
index d7a4fb0d6b7..30f1b5769d6 100644
--- a/locale/uk/gitlab.po
+++ b/locale/uk/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:41\n"
+"PO-Revision-Date: 2018-10-02 09:31\n"
+
+msgid " Status"
+msgstr " СтатуÑ"
msgid " and"
msgstr " Ñ–"
@@ -60,6 +63,20 @@ msgstr[1] "%d екÑпортера"
msgstr[2] "%d екÑпортерів"
msgstr[3] "%d екÑпортерів"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] "%d невдалий результат теÑту"
+msgstr[1] "%d невдалі результати теÑтів"
+msgstr[2] "%d невдалих результатів теÑтів"
+msgstr[3] "%d невдалих результатів теÑтів"
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] "%d виправлений результат теÑту"
+msgstr[1] "%d виправлені результати теÑту"
+msgstr[2] "%d виправлених результатів теÑту"
+msgstr[3] "%d виправлених результатів теÑту"
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d проблема"
@@ -88,13 +105,6 @@ msgstr[1] "%d метрики"
msgstr[2] "%d метрик"
msgstr[3] "%d метрик"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] "%d нова ліцензіÑ"
-msgstr[1] "%d нових ліцензій"
-msgstr[2] "%d нових ліцензій"
-msgstr[3] "%d нових ліцензій"
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] "%d проіндекÑована зміна"
@@ -130,7 +140,7 @@ msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "%{commit_author_link} закомітив %{commit_timeago}"
msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
-msgstr ""
+msgstr "%{counter_storage} (%{counter_repositories} репозиторій, %{counter_build_artifacts} артефактів збірки, %{counter_lfs_objects} LFS)"
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -142,9 +152,15 @@ msgstr[3] "%{count} учаÑтників"
msgid "%{filePath} deleted"
msgstr "%{filePath} видалено"
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr "%{firstLabel} +%{labelCount} більше"
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr "%{group_docs_link_start}Групи%{group_docs_link_end} дозволÑÑŽÑ‚ÑŒ вам керувати Ñ– взаємодіÑти між кількома проектами. Члени групи мають доÑтуп до уÑÑ–Ñ… Ñ—Ñ— проектів."
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr "%{issuableType} буде видалено! Ви впевнені?"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} Початок"
@@ -181,10 +197,10 @@ msgstr[3] "%{storage_name}: %{failed_attempts} невдалих Ñпроб доÑ
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%{text} %{files}"
+msgstr[1] "%{text} %{files} файли"
+msgstr[2] "%{text} %{files} файлів"
+msgstr[3] "%{text} %{files} файлів"
msgid "%{text} is available"
msgstr "%{text} доÑтупний"
@@ -192,16 +208,15 @@ msgstr "%{text} доÑтупний"
msgid "%{title} changes"
msgstr "%{title} зміни"
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] "%{type} виÑвило 1 вразливіÑÑ‚ÑŒ"
-msgstr[1] "%{type} виÑвило %{vulnerabilityCount} вразливоÑтей"
-msgstr[2] "%{type} виÑвило %{vulnerabilityCount} вразливоÑтей"
-msgstr[3] "%{type} виÑвило %{vulnerabilityCount} вразливоÑтей"
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "%{unstaged} неіндекÑованих та %{staged} проіндекÑованих змін"
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr "%{usage_ping_link_start}ДовідатиÑÑŒ більше%{usage_ping_link_end} про те, Ñкою інформацією Ви ділитеÑÑŒ із GitLab Inc."
+
+msgid "+ %{count} more"
+msgstr "+ ще %{count}"
+
msgid "+ %{moreCount} more"
msgstr "+ ще %{moreCount}"
@@ -242,6 +257,13 @@ msgstr[1] "%d закритих запити на злиттÑ"
msgstr[2] "%d закритих запитів на злиттÑ"
msgstr[3] "%d закритих запитів на злиттÑ"
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] "1 група"
+msgstr[1] "%d групи"
+msgstr[2] "%d груп"
+msgstr[3] "%d груп"
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] "1 заÑтоÑований запит на злиттÑ"
@@ -270,6 +292,20 @@ msgstr[1] "%d конвеєра"
msgstr[2] "%d конвеєрів"
msgstr[3] "%d конвеєрів"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] "1 роль"
+msgstr[1] "%d ролі"
+msgstr[2] "%d ролей"
+msgstr[3] "%d ролей"
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] "1 кориÑтувач"
+msgstr[1] "%d кориÑтувачі"
+msgstr[2] "%d кориÑтувачів"
+msgstr[3] "%d кориÑтувачів"
+
msgid "1st contribution!"
msgstr "Перший внеÑок!"
@@ -292,16 +328,19 @@ msgid "404|Please contact your GitLab administrator if you think this is a mista
msgstr "Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора GitLab, Ñкщо ви вважаєте, що це помилка."
msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> додаÑÑ‚ÑŒ \"<a href=\"#\">@johnsmith</a>\" до вÑÑ–Ñ… проблем та коментарів, що були Ñтворені johnsmith@example.com, а також призначить на <a href=\"#\">@johnsmith</a> уÑÑ– проблеми, Ñкі були призначені на на johnsmith@example.com."
msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"John Smith\"</code> додаÑÑ‚ÑŒ \"John Smith\" до уÑÑ–Ñ… проблем та коментарів, Ñкі були Ñтворені johnsmith@example.com."
msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> додаÑÑ‚ÑŒ \"johnsm...@example.com\" до уÑÑ–Ñ… проблем та коментарів, Ñкі були Ñтворені johnsmith@example.com. Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача та його електронна адреÑа замаÑковані Ð´Ð»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð´ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾ÑÑ‚Ñ–."
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
+msgstr "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> додаÑÑ‚ÑŒ \"<a href=\"#\">johnsmith@example.com</a>\" до вÑÑ–Ñ… проблем та коментарів, Ñкі були Ñтворені johnsmith@example.com. За замовчуваннÑм Ñ–Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача та його електронна адреÑа заблоковані Ð´Ð»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð´ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾ÑÑ‚Ñ–. ВикориÑтовуйте цю опцію, Ñкщо ви хочете показувати електронну адреÑу повніÑÑ‚ÑŽ."
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr "<strong>%{changedFilesLength} неіндекÑованих</strong> та <strong>%{stagedFilesLength} індекÑованих</strong> змін"
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr "<strong>%{created_count}</strong> Ñтворено, <strong>%{accepted_count}</strong> прийнÑто."
@@ -310,28 +349,34 @@ msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</stron
msgstr "<strong>%{created_count}</strong> Ñтворено, <strong>%{closed_count}</strong> закрито."
msgid "<strong>%{group_name}</strong> group members"
-msgstr ""
+msgstr "<strong>%{group_name}</strong> кориÑтувачі групи"
msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
-msgstr ""
+msgstr "<strong>%{pushes}</strong> відправок (push), більше ніж <strong>%{commits}</strong> зафікÑовано<strong>%{people}</strong> учаÑниками."
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>ВидалÑÑ”</strong> гілку-джерело"
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "'Runner' — це процеÑ, Ñкий виконує завданнÑ. Ви можете Ñтворити потрібну кількіÑÑ‚ÑŒ Runner'ів."
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Ðабір графіків відноÑно безперервної інтеграції"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr "Гілку за замовчуваннÑм не може бути обрано Ð´Ð»Ñ Ð¿Ð¾Ñ€Ð¾Ð¶Ð½ÑŒÐ¾Ð³Ð¾ проекту."
+
+msgid "A deleted user"
+msgstr "Видалений кориÑтувач"
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "У вашому форку буде Ñтворено нову гілку, а також буде ініційований новий запит на злиттÑ."
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
-msgstr "Проект — це міÑце де ви можете розміщувати Ñвої файли (репозиторій), планувати роботу (проблеми) Ñ– публікувати документацію (wiki), %{among_other_things_link}."
+msgstr "Проект — це міÑце де ви можете розміщувати Ñвої файли (репозиторій), планувати роботу (проблеми) Ñ– публікувати документацію (вікі), %{among_other_things_link}."
msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
-msgstr ""
+msgstr "РегулÑрний вираз, Ñкий буде викориÑтовуватиÑÑ Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ результатів Ð¿Ð¾ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ‚ÐµÑтами в завданні. Залиште пуÑтим Ð´Ð»Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ"
msgid "A user with write access to the source branch selected this option"
msgstr "КориÑтувач із правом запиÑу в гілку-джерело вибрав цей варіант"
@@ -364,7 +409,10 @@ msgid "Access Tokens"
msgstr "Токени доÑтупу"
msgid "Access denied! Please verify you can add deploy keys to this repository."
-msgstr ""
+msgstr "ДоÑтуп заборонено! Будь-лаÑка, перевірте, чи ви можете додавати ключі Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾ цього Ñховища."
+
+msgid "Access expiration date"
+msgstr "Дата Ð¿Ñ€Ð¸Ð¿Ð¸Ð½ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупу"
msgid "Access to '%{classification_label}' not allowed"
msgstr "ДоÑтуп до \"%{classification_label}\" заборонено"
@@ -372,8 +420,8 @@ msgstr "ДоÑтуп до \"%{classification_label}\" заборонено"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "ДоÑтуп до Ñховищ, що вийшли з ладу, тимчаÑово прибраний Ð·Ð°Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ. ПіÑÐ»Ñ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ обнуліть інформацію Ñховища Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупу."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
-msgstr ""
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr "Отримайте доÑтуп до Gitlab Runner токену, налаштуйте конфігурацію конвеєра та переглÑньте його ÑтатуÑ, а також звіт про покриттÑ."
msgid "Account"
msgstr "Обліковий запиÑ"
@@ -405,17 +453,17 @@ msgstr "Додайте групові веб-гуки та GitLab Enterprise Edi
msgid "Add Kubernetes cluster"
msgstr "Додати Kubernetes-клаÑтер"
-msgid "Add License"
-msgstr "Додати ліцензію"
-
msgid "Add Readme"
msgstr "Додати інÑтрукцію"
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "Створіть додатковий текÑÑ‚, Ñкий буде приÑутній у вÑÑ–Ñ… повідомленнÑÑ… електронної пошти. МакÑимальна кількіÑÑ‚ÑŒ Ñимволів — %{character_limit}"
+msgid "Add license"
+msgstr "Додати ліцензію"
+
msgid "Add new application"
-msgstr ""
+msgstr "Додати новий додаток"
msgid "Add new directory"
msgstr "Додати новий каталог"
@@ -427,11 +475,14 @@ msgid "Add todo"
msgstr "Додати задачу"
msgid "Add user(s) to the group:"
-msgstr ""
+msgstr "Додати кориÑтувачів до групу:"
msgid "Add users to group"
msgstr "Додати кориÑтувача до групи"
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð½Ð¾Ð²Ð¸Ñ… проектів Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ інÑтанÑу GitLab заборонено. ЗвернітьÑÑ Ð´Ð¾ Ñвого адмініÑтратора GitLab, щоб отримати дозвіл"
+
msgid "Additional text"
msgstr "Додатковий текÑÑ‚"
@@ -444,6 +495,12 @@ msgstr "ОглÑд адмініÑтратора"
msgid "Admin area"
msgstr "ОблаÑÑ‚ÑŒ адмініÑтратора"
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "Зупинити вÑÑ– завданнÑ"
@@ -462,6 +519,9 @@ msgstr "Зараз ви зупинете вÑÑ– завданнÑ. Це обірÐ
msgid "AdminHealthPageLink|health page"
msgstr "Ñторінка ÑтатуÑу"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr "Видалити"
@@ -510,11 +570,14 @@ msgstr "Ð’ÑÑ– зміни закомічені"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "Ð’ÑÑ– функції Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… проектів берутьÑÑ Ñ–Ð· шаблонів або під Ñ‡Ð°Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ, але ви можете вимикати Ñ—Ñ… пізніше в налаштуваннÑÑ… проекту."
+msgid "All users"
+msgstr "Ð’ÑÑ– кориÑтувачі"
+
msgid "Allow commits from members who can merge to the target branch."
msgstr "Дозволити коміти від учаÑників, Ñкі можуть зливати в цільову гілку."
msgid "Allow public access to pipelines and job details, including output logs and artifacts"
-msgstr ""
+msgstr "Дозволити публічний доÑтуп до конвеєрів Ñ– завдань, включно з логами та артефактами"
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Дозволити Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ñ–Ð°Ð³Ñ€Ð°Ð¼ PlantUML в документах Asciidoc."
@@ -537,26 +600,29 @@ msgstr "Крім того, ви можете викориÑтовувати %{pe
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "Крім того, ви можете викориÑтовувати %{personal_access_token_link}. Коли ви Ñтворюватимете Ñвій перÑональний токен доÑтупу, вам потрібно буде вибрати облаÑÑ‚ÑŒ <code>repo</code>, щоб ми могли відобразити ÑпиÑок ваших публічних та приватних репозиторіїв, доÑтупних Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ."
-msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr "Додаток під назвою %{link_to_client} запитує доÑтуп до вашого GitLab аккаунту."
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
-msgstr ""
+msgstr "Порожнє поле Gitlab-кориÑтувача буде заповнено іменем кориÑтувача з FogBugz (наприклад \"John Smith\") в опиÑÑ– вÑÑ–Ñ… проблем та коментарів. Крім того ці проблеми та коментарі будуть аÑоційовані з та/або призначені на автора проекту."
msgid "An error accured whilst committing your changes."
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при коміті ваших змін."
msgid "An error has occurred"
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
msgid "An error occured creating the new branch."
msgstr "Помилка при Ñтворенні нової гілки."
msgid "An error occured whilst fetching the job trace."
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні логу завданнÑ."
-msgid "An error occured whilst fetching the latest pipline."
-msgstr ""
+msgid "An error occured whilst fetching the latest pipeline."
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні даних оÑтаннього конвеєра."
msgid "An error occured whilst loading all the files."
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при завантаженні вÑÑ–Ñ… файлів."
@@ -568,16 +634,16 @@ msgid "An error occured whilst loading the file."
msgstr "Помилка при завантаженні файлу."
msgid "An error occured whilst loading the merge request changes."
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при завантаженні змін запиту на злиттÑ."
msgid "An error occured whilst loading the merge request version data."
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при завантаженні даних верÑÑ–Ñ— запиту на злиттÑ."
msgid "An error occured whilst loading the merge request."
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при завантаженні запиту на злиттÑ."
msgid "An error occured whilst loading the pipelines jobs."
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при завантаженні завдань конвеєру."
msgid "An error occurred previewing the blob"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾Ð³Ð¾ переглÑду об'єкта"
@@ -606,20 +672,32 @@ msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при попередньому перег
msgid "An error occurred while fetching sidebar data"
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… Ð´Ð»Ñ Ð±Ñ–Ñ‡Ð½Ð¾Ñ— панелі"
+msgid "An error occurred while fetching stages."
+msgstr "Помилка при отриманні Ñтадій."
+
+msgid "An error occurred while fetching the job log."
+msgstr "Помилка при отриманні логів завданнÑ."
+
+msgid "An error occurred while fetching the job."
+msgstr "Помилка при отриманні завданнÑ."
+
+msgid "An error occurred while fetching the jobs."
+msgstr "Помилка при отриманні завдань."
+
msgid "An error occurred while fetching the pipeline."
msgstr "Помилка при отриманні данних конвеєра."
msgid "An error occurred while getting projects"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні проектів"
-msgid "An error occurred while importing project: ${details}"
-msgstr "При імпортуванні проекту ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°: ${details}"
+msgid "An error occurred while importing project: %{details}"
+msgstr "При імпортуванні проекту ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°: %{details}"
msgid "An error occurred while initializing path locks"
msgstr "Помилка при ініціалізації Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ… шлÑхів"
msgid "An error occurred while loading commit signatures"
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при завантаженні Ñигнатур коміту"
msgid "An error occurred while loading diff"
msgstr "Помилка при завантаженні разниці (diff)"
@@ -667,13 +745,13 @@ msgid "An error occurred. Please try again."
msgstr "СталаÑÑŒ помилка. Спробуйте ще раз."
msgid "Anonymous"
-msgstr ""
+msgstr "Ðнонімно"
msgid "Anti-spam verification"
-msgstr ""
+msgstr "Перевірка проти Ñпаму"
msgid "Any"
-msgstr ""
+msgstr "Будь-Ñкий"
msgid "Any Label"
msgstr "Будь-Ñка мітка"
@@ -682,13 +760,13 @@ msgid "Appearance"
msgstr "Зовнішній виглÑд"
msgid "Application"
-msgstr ""
+msgstr "Додаток"
msgid "Application Id"
-msgstr ""
+msgstr "Id додатку"
msgid "Application: %{name}"
-msgstr ""
+msgstr "Додаток: %{name}"
msgid "Applications"
msgstr "ЗаÑтоÑунки"
@@ -702,17 +780,23 @@ msgstr "квітень"
msgid "Archived project! Repository and other project resources are read-only"
msgstr "Ðрхівований проект! Репозиторій та інші реÑурÑи проекту доÑтупні лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ"
+msgid "Archived projects"
+msgstr "Заархівовані проекти"
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ви впевнені, що хочете видалити цей розклад Ð´Ð»Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ð°?"
msgid "Are you sure you want to lose unsaved changes?"
+msgstr "Ви впевнені, що бажаєте втратити незбережені зміни?"
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
-msgstr ""
+msgstr "Ви впевнені, що хочете видалити %{group_name}?"
msgid "Are you sure you want to remove this identity?"
-msgstr ""
+msgstr "Ви впевнені, що хочете видалити цю ідентифікацію?"
msgid "Are you sure you want to reset registration token?"
msgstr "Ви впевнені, що бажаєте перегенерувати реєÑтраційний токен?"
@@ -726,14 +810,17 @@ msgstr "Ви впевнені, що хочете розблокувати %{path
msgid "Are you sure?"
msgstr "Ви впевнені?"
+msgid "Artifact ID"
+msgstr "ID артефакту"
+
msgid "Artifacts"
msgstr "Ðртефакти"
msgid "Ascending"
msgstr "За зроÑтаннÑм"
-msgid "Ask your group maintainer to setup a group Runner."
-msgstr "ПопроÑÑ–Ñ‚ÑŒ керівника групи, щоб налаштувати груповий Runner."
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr "ЗвернітьÑÑ Ð´Ð¾ керівника групи, щоб налаштувати груповий Runner."
msgid "Assertion consumer service URL"
msgstr "URL-адреÑа Ñлужби обробника тверджень"
@@ -765,8 +852,8 @@ msgstr "Призначено мені"
msgid "Assignee"
msgstr "Виконавець"
-msgid "Assignee boards not available with your current license"
-msgstr "Дошки виконавців не доÑтупні з вашою поточною ліцензією"
+msgid "Assignee lists not available with your current license"
+msgstr "СпиÑки виконавців не доÑтупні з вашою поточною ліцензією"
msgid "Assignee lists show all issues assigned to the selected user."
msgstr "СпиÑки виконавців показують уÑÑ– проблеми, призначені вибраному кориÑтувачу."
@@ -778,7 +865,7 @@ msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Прикріпити файл за допомогою перетÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ %{upload_link}"
msgid "Audit Events"
-msgstr ""
+msgstr "Події аудиту"
msgid "Aug"
msgstr "Ñерп."
@@ -790,34 +877,37 @@ msgid "Authentication Log"
msgstr "Журнал автентифікації"
msgid "Authentication log"
-msgstr ""
+msgstr "Журнал автентифікації"
+
+msgid "Authentication method"
+msgstr "Метод автентифікації"
msgid "Author"
msgstr "Ðвтор"
msgid "Authorization code:"
-msgstr ""
+msgstr "Код авторизації:"
msgid "Authorization was granted by entering your username and password in the application."
-msgstr ""
+msgstr "ÐÐ²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´Ð±ÑƒÐ»Ð°ÑÑ Ð¿Ñ–ÑÐ»Ñ Ð²Ð²Ð¾Ð´Ñƒ вашого імені та паролю у заÑтоÑунку."
msgid "Authorize"
-msgstr ""
+msgstr "ÐвторизаціÑ"
msgid "Authorize %{link_to_client} to use your account?"
-msgstr ""
+msgstr "ÐвторизуватиÑÑ %{link_to_client} викориÑтовуючи ваш аккаунт?"
msgid "Authorized At"
-msgstr ""
+msgstr "Ðвторизовано у"
msgid "Authorized applications (%{size})"
-msgstr ""
+msgstr "Ðвторизовані заÑтоÑунки: (%{size})"
msgid "Authors: %{authors}"
msgstr "Ðвтори: %{authors}"
msgid "Auto DevOps"
-msgstr ""
+msgstr "Auto DevOps"
msgid "Auto DevOps enabled"
msgstr "Auto DevOps увімкнено"
@@ -835,7 +925,7 @@ msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Ð”Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Auto Review Apps та Auto Deploy необхідно вказати доменне ім’Ñ."
msgid "Auto-cancel redundant, pending pipelines"
-msgstr ""
+msgstr "Ðвтоматичне ÑкаÑÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð°Ð´Ð»Ð¸ÑˆÐºÐ¾Ð²Ð¸Ñ…, очікуючих конвеєрів"
msgid "AutoDevOps|Auto DevOps"
msgstr "Auto DevOps"
@@ -852,6 +942,9 @@ msgstr "AutoDevOps буде автоматично збирати, теÑтувÐ
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ в %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr "Ви можете автоматично збирати й теÑтувати ваш заÑтоÑунок, Ñкщо %{link_to_auto_devops_settings} Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту. Ви також можете його автоматично розгортати, Ñкщо %{link_to_add_kubernetes_cluster}."
@@ -861,6 +954,9 @@ msgstr "додати Kubernetes-клаÑтер"
msgid "AutoDevOps|enable Auto DevOps"
msgstr "Увімкнути Auto DevOps"
+msgid "Automatically marked as default internal user"
+msgstr "Ðвтоматично позначено Ñк внутрішній кориÑтувач за замовчуваннÑм"
+
msgid "Available"
msgstr "ДоÑтупно"
@@ -880,14 +976,11 @@ msgid "Background Color"
msgstr "Колір фону"
msgid "Background Jobs"
-msgstr ""
+msgstr "Фонові завданнÑ"
msgid "Background color"
msgstr "Колір фону"
-msgid "Background jobs"
-msgstr "Фонові завданнÑ"
-
msgid "Badges"
msgstr "Значки"
@@ -927,6 +1020,9 @@ msgstr "Значок Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñутній"
msgid "Badges|No image to preview"
msgstr "Ðемає Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾Ð³Ð¾ переглÑду"
+msgid "Badges|Please fill in a valid URL"
+msgstr "Будь лаÑка, введіть дійÑну URL-адреÑу"
+
msgid "Badges|Project Badge"
msgstr "Значок проекту"
@@ -954,17 +1050,23 @@ msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð° не має значків"
msgid "Badges|This project has no badges"
msgstr "У цього проекту немає значків"
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr "Ви збираєтеÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ цей значок. Вилучені значки <strong>не можуть</strong> бути відновлені."
+
msgid "Badges|Your badges"
msgstr "Ваші значки"
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr "напр. %{exampleUrl}"
+
msgid "Begin with the selected commit"
msgstr "Почати із виділеного коміту"
msgid "Below are examples of regex for existing tools:"
-msgstr ""
+msgstr "Ðижче наведені приклади регулÑрних виразів Ð´Ð»Ñ Ñ–Ñнуючих інÑтрументів:"
msgid "Below you will find all the groups that are public."
-msgstr ""
+msgstr "Ðижче ви знайдете вÑÑ– загальнодоÑтупні групи."
msgid "Billing"
msgstr "Білінг"
@@ -985,7 +1087,7 @@ msgid "BillingPlans|Downgrade"
msgstr "Понизити"
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
-msgstr ""
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про кожен план, прочитавши наш %{faq_link}, або розпочніть безкоштовну 30-денну пробну верÑÑ–ÑŽ GitLab.com Gold."
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про кожен план, читаючи наш %{faq_link}."
@@ -1012,13 +1114,13 @@ msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr "Зараз ви викориÑтовуєте план %{plan_link}."
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
-msgstr ""
+msgstr "Ваша пробна верÑÑ–Ñ GitLab.com закінчилаÑÑ %{expiration_date}. %{learn_more_text}"
msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
-msgstr ""
+msgstr "Ваша пробна Gold верÑÑ–Ñ <strong>закінчитьÑÑ Ð¿Ñ–ÑÐ»Ñ %{expiration_date}</strong>. Ви можете дізнатиÑÑŒ більше про GitLab.com Gold верÑÑ–ÑŽ, прочитавши наш %{features_link}."
msgid "BillingPlans|features"
-msgstr ""
+msgstr "функції"
msgid "BillingPlans|frequently asked questions"
msgstr "ЧаÑÑ‚Ñ– питаннÑ"
@@ -1032,17 +1134,20 @@ msgstr "ОплачуєтьÑÑ Ñ‰Ð¾Ñ€Ñ–Ñ‡Ð½Ð¾ %{price_per_year}"
msgid "BillingPlans|per user"
msgstr "За кориÑтувача"
+msgid "Bitbucket Server Import"
+msgstr "Імпорт з Bitbucket Server"
+
msgid "Bitbucket import"
msgstr "Імпорт з Bitbucket"
msgid "Blog"
-msgstr ""
+msgstr "Блог"
msgid "Boards"
msgstr "Дошки"
msgid "Branch %{branchName} was not found in this project's repository."
-msgstr ""
+msgstr "Гілка %{branchName} відÑÑƒÑ‚Ð½Ñ Ð² репозиторії цього проекту."
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
@@ -1204,6 +1309,9 @@ msgstr "ПереглÑд файлів"
msgid "Browse files"
msgstr "ПереглÑд файлів"
+msgid "Built-In"
+msgstr "Вбудований"
+
msgid "Business metrics (Custom)"
msgstr "Ð‘Ñ–Ð·Ð½ÐµÑ Ð¼ÐµÑ‚Ñ€Ð¸ÐºÐ¸ (ВлаÑні)"
@@ -1216,6 +1324,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
+msgid "CI will run using the credentials assigned above."
+msgstr "CI буде працювати з викориÑтаннÑм облікових даних, визначених вище."
+
msgid "CI/CD"
msgstr "CI/CD"
@@ -1228,9 +1339,6 @@ msgstr "CI/CD Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ репозиторіÑ"
msgid "CI/CD settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr "Ðеобхідно вказати %{ci_file} перед тим, Ñк ви зможете викориÑтовувати безперервну інтреграцію та розгортаннÑ."
-
msgid "CICD|Auto DevOps"
msgstr "Auto DevOps"
@@ -1243,47 +1351,41 @@ msgstr "Ðвтоматичне Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° staging, ручне Ñ
msgid "CICD|Continuous deployment to production"
msgstr "Безперервне Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
msgid "CICD|Deployment strategy"
msgstr "Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ñ–Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr "Ðеобхідно вказати доменне Ñ–Ð¼â€™Ñ Ð´Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Ñтратегії розгортаннÑ."
-msgid "CICD|Disable Auto DevOps"
-msgstr "Вимкнути Auto DevOps"
-
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr "Ðе вказуйте тут домен, Ñкщо ви налаштовуєте декілька клаÑтерів Kubernetes за допомогою Auto DevOps."
-msgid "CICD|Enable Auto DevOps"
-msgstr "Увімкнути Auto DevOps"
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr "ВикориÑтовувати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½ÑтанÑу за замовчаннÑм Ð´Ð»Ñ ÑƒÐ²Ñ–Ð¼ÐºÐµÐ½Ð½Ñ Ð°Ð±Ð¾ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Auto DevOps, Ñкщо у проекті відÑутній %{ci_file}."
-
-msgid "CICD|Instance default (%{state})"
-msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½ÑтанÑу за замовчуваннÑм (%{state})"
-
msgid "CICD|Jobs"
msgstr "ЗавданнÑ"
msgid "CICD|Learn more about Auto DevOps"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Auto DevOps"
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
-msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñƒ Auto DevOps буде заÑтоÑовуватиÑÑ, коли в проекті немає %{ci_file}."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr "Вам потрібно вказати домен, Ñкщо ви хочете викориÑтовувати Auto Review Apps та Auto Deploy."
-msgid "Callback URL"
+msgid "CICD|instance enabled"
msgstr ""
+msgid "Callback URL"
+msgstr "URL зворотнього виклику"
+
msgid "Callback url"
-msgstr ""
+msgstr "URL зворотнього виклику"
msgid "Can't find HEAD commit for this branch"
-msgstr ""
+msgstr "Ðе можу знайти HEAD-коміт Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— гілки"
msgid "Cancel"
msgstr "СкаÑувати"
@@ -1301,7 +1403,10 @@ msgid "Certificate fingerprint"
msgstr "Відбиток Ñертифіката"
msgid "Change Weight"
-msgstr ""
+msgstr "Змінити вагу"
+
+msgid "Change template"
+msgstr "Змінити шаблон"
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr "Змініть це значеннÑ, щоб вплинути на чаÑтоту Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñу GitLab."
@@ -1328,7 +1433,7 @@ msgid "Changes are shown as if the <b>source</b> revision was being merged into
msgstr "Зміни відображаютьÑÑ Ñ‚Ð°Ðº, ніби <b>редакціÑ-джерело</b> була злита в <b>цільову редакцію</b>."
msgid "Charts"
-msgstr "Графіки"
+msgstr "СтатиÑтика"
msgid "Chat"
msgstr "Чат"
@@ -1349,10 +1454,10 @@ msgid "Cherry-pick this merge request"
msgstr "Вибрати (cherry-pick) цей запит на злиттÑ"
msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
-msgstr ""
+msgstr "Оберіть <strong>Створити архів</strong> Ñ– чекайте, поки Ð°Ñ€Ñ…Ñ–Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ завершено."
msgid "Choose <strong>Next</strong> at the bottom of the page."
-msgstr ""
+msgstr "Оберіть <strong>Далі</strong> внизу Ñторінки."
msgid "Choose File ..."
msgstr "Виберіть файл ..."
@@ -1360,17 +1465,23 @@ msgstr "Виберіть файл ..."
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Виберіть гілку чи тег (напр. %{master}) або введіть коміт (напр. %{sha}) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду змін або Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ."
+msgid "Choose a template..."
+msgstr "Виберіть тему-шаблон..."
+
+msgid "Choose a type..."
+msgstr "Виберіть тип..."
+
msgid "Choose any color."
msgstr "Вибрати будь-Ñкий колір."
msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
-msgstr ""
+msgstr "Оберіть між <code>clone</code> та<code>fetch</code> щоб отримати найновіший код програми"
msgid "Choose file..."
msgstr "Виберіть файл..."
msgid "Choose the top-level group for your repository imports."
-msgstr ""
+msgstr "Оберіть групу найвищого Ñ€Ñ–Ð²Ð½Ñ Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ репозиторіїв."
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr "Виберіть групи Ð´Ð»Ñ Ñинхронізації на цей вторинний вузол."
@@ -1487,13 +1598,13 @@ msgid "Click any <strong>project name</strong> in the project list below to navi
msgstr "Клікніть по будь-Ñкому <strong>імені проекту</strong> зі ÑпиÑку нижче Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб перейти до етапу проекту."
msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
-msgstr ""
+msgstr "ÐатиÑніть кнопку <strong>ЗавантаженнÑ</strong> Ñ– зачекайте поки Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð½Ðµ завершитьÑÑ."
msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
msgstr "ÐатиÑніть кнопку <strong>ПеренеÑти</strong> у правому верхному куті щоб перенеÑти етап на рівень групи."
msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
-msgstr ""
+msgstr "ÐатиÑніть кнопку <strong>Обрати нічого</strong> Ñправа, оÑкільки нам потрібен лише \"ХоÑтинг проектів Google Code\"."
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr "ÐатиÑніть кнопку нижче, щоб розпочати Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð²ÑтановленнÑ, перейшовши на Ñторінку Kubernetes"
@@ -1522,6 +1633,9 @@ msgstr "Клонувати репозиторій"
msgid "Close"
msgstr "Закрити"
+msgid "Close epic"
+msgstr "Закрити епік"
+
msgid "Closed"
msgstr "Закрито"
@@ -1531,6 +1645,9 @@ msgstr "Закриті проблеми"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} були уÑпішно вÑтановлені на ваш Kubernetes-клаÑтер"
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr "API URL"
@@ -1540,12 +1657,21 @@ msgstr "Додати Kubernetes клаÑтер"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Детальні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— із цим Kubernetes-клаÑтером"
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr "Помилка при отриманні зон проекту: %{error}"
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr "Помилка при отриманні ваших проектів: %{error}"
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr "ЗаÑтоÑунки"
@@ -1558,11 +1684,11 @@ msgstr "Сертифікат центру Ñертифікації"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Ðабір Ñертифікатів (формат PEM)"
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "Виберіть Ñкі з Ñередовищ вашого проекту викориÑтовуватимуть цей Kubernetes-клаÑтер."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr "Керуйте ÑпоÑобом інтеграції вашого Kubernetes-клаÑтера з GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
msgid "ClusterIntegration|Copy API URL"
msgstr "Скопіювати API URL"
@@ -1586,6 +1712,12 @@ msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr "Створити Kubernetes-клаÑтер"
msgid "ClusterIntegration|Did you know?"
+msgstr "Чи знаєте ви?"
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
msgstr ""
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
@@ -1595,7 +1727,7 @@ msgid "ClusterIntegration|Environment scope"
msgstr "Межі Ñередовищ"
msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
-msgstr ""
+msgstr "Кожен новий обліковий Ð·Ð°Ð¿Ð¸Ñ Ð² Google Cloud Platform (GCP) отримує $300 на Ñвій рахунок при %{sign_up_link}. GitLab (у партнерÑтві із Google) пропонує додаткові $200 Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… облікових запиÑів GCP, Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸ÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ”ÑŽ GitLab з Google Kubernetes Engine."
msgid "ClusterIntegration|Fetching machine types"
msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ‚Ð¸Ð¿Ñ–Ð² машин"
@@ -1612,6 +1744,9 @@ msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ð· GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr "Проект Google Cloud Platform"
@@ -1624,11 +1759,14 @@ msgstr "проекті Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
-msgid "ClusterIntegration|Hide"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr "Приховати"
+
msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
-msgstr ""
+msgstr "Якщо ви налаштовуєте декілька клаÑтерів Ñ– викориÑтовуєте Auto DevOps, %{help_link_start}прочитайте Ð´Ð»Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ це%{help_link_end}."
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб показати працездатніÑÑ‚ÑŒ клаÑтера, нам потрібно буде забезпечити ваш клаÑтер з Prometheus Ð´Ð»Ñ Ð·Ð±Ð¾Ñ€Ñƒ необхідних даних."
@@ -1639,6 +1777,9 @@ msgstr "Ingress"
msgid "ClusterIntegration|Ingress IP Address"
msgstr "Ingress IP-адреÑа"
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr "Ð’Ñтановити"
@@ -1663,6 +1804,9 @@ msgstr "Ð†Ð¼â€™Ñ Ñ…Ð¾Ñта Jupyter"
msgid "ClusterIntegration|JupyterHub"
msgstr "JupyterHub"
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Kubernetes-клаÑтер"
@@ -1675,15 +1819,6 @@ msgstr "Стан Kubernetes-клаÑтера"
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· Kubernetes-клаÑтером"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· Kubernetes-клаÑтером вимкнена Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· Kubernetes-клаÑтером увімкнена Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· Kubernetes-клаÑтером увімкнена Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту. Ð’Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— інтеграції не вплине на клаÑтер, а лише тимчаÑово перерве Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· GitLab."
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes-клаÑтер ÑтворюєтьÑÑ Ð½Ð° Google Kubernetes Engine..."
@@ -1708,12 +1843,6 @@ msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{help_link_start}Kubernetes%{h
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{help_link_start}зони%{help_link_end}."
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñередовища"
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про конфігурацію безпеки"
-
msgid "ClusterIntegration|Machine type"
msgstr "Тип машини"
@@ -1756,6 +1885,9 @@ msgstr "Введіть інформацію про доÑтуп до Ñвого
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Будь-лаÑка впевнітьÑÑ, що ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Google задовольнÑÑ” наÑтупним вимогам:"
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr "ПроÑÑ‚Ñ–Ñ€ імен проекту"
@@ -1765,6 +1897,12 @@ msgstr "ПроÑÑ‚Ñ–Ñ€ імен проекту (не обов’Ñзковий,
msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr "ПереглÑньте нашу %{link_to_help_page} про інтеграцію із Kubernetes."
@@ -1777,6 +1915,9 @@ msgstr "Видалити інтеграцію"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "Видалити конфігурацію Kubernetes-клаÑтера Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту. Це не призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñамого клаÑтера."
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "Запит про початок вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ виконано"
@@ -1792,9 +1933,6 @@ msgstr "Пошук проектів"
msgid "ClusterIntegration|Search zones"
msgstr "Пошук зон"
-msgid "ClusterIntegration|Security"
-msgstr "Безпека"
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "ПереглÑнути та редагувати параметри вашого Kubernetes-клаÑтера"
@@ -1831,12 +1969,18 @@ msgstr "Помилка при Ñтворенні вашого Kubernetes-клаÑ
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ %{title} ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr "Стандартна ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ ÐºÐ»Ð°Ñтера надає доÑтуп до широкого набору функцій, необхідних Ð´Ð»Ñ ÑƒÑпішного ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð° Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð·Ð°ÑтоÑунків-контейнерів."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "Цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ мати наÑтупні права Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Kubernetes-клаÑтера в %{link_to_container_project}"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "Увімкнути/вимкнути Kubernetes-клаÑтер"
@@ -1849,9 +1993,15 @@ msgstr "Токен"
msgid "ClusterIntegration|Validating project billing status"
msgstr "Перевірка Ñтану білінгу проекта"
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr "Ми не змогли перевірити, що один із ваших проектів в GCP має ввімкнений білінг. Будь лаÑка, Ñпробуйте ще раз."
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "За допомогою підключеного до цього проекту Kubernetes-клаÑтера, ви можете викориÑтовувати Review Apps, розгортати ваші проекти, запуÑкати конвеєри збірки тощо."
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ мати %{link_to_kubernetes_engine}"
@@ -1870,9 +2020,6 @@ msgstr "документації"
msgid "ClusterIntegration|help page"
msgstr "Ñторінка допомоги"
-msgid "ClusterIntegration|installing applications"
-msgstr "вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð°ÑтоÑунків"
-
msgid "ClusterIntegration|meets the requirements"
msgstr "задовольнÑÑ” вимогам"
@@ -1882,8 +2029,11 @@ msgstr "правильно налаштований"
msgid "ClusterIntegration|sign up"
msgstr "зареєÑтрувати"
+msgid "Code owners"
+msgstr "ВлаÑники коду"
+
msgid "Cohorts"
-msgstr ""
+msgstr "Когорти"
msgid "Collapse"
msgstr "Згорнути"
@@ -1935,6 +2085,9 @@ msgstr "Коміт"
msgid "CommitMessage|Add %{file_name}"
msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %{file_name}"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "Коміти"
@@ -2007,17 +2160,14 @@ msgstr "КонфіденційніÑÑ‚ÑŒ"
msgid "Configure Gitaly timeouts."
msgstr "Ðалаштувати таймаути Gitaly."
-msgid "Configure Sidekiq job throttling."
-msgstr "Ðалаштувати чаÑтоту завдань Sidekiq."
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr "Ðалаштувати автоматичні перевірки git Ñ– Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð² репозиторіÑÑ…."
msgid "Configure limits for web and API requests."
msgstr "Ðалаштуйте Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð²ÐµÐ± та API запитів."
-msgid "Configure push and pull mirrors."
-msgstr ""
+msgid "Configure push mirrors."
+msgstr "Ðалаштуйте вихідні дзеркала."
msgid "Configure storage path and circuit breaker settings."
msgstr "Ðалаштуйте шлÑÑ… до Ñховищ та circuit breaker."
@@ -2035,7 +2185,7 @@ msgid "Connect repositories from GitHub"
msgstr "Підключити репозиторії з GitHub"
msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
-msgstr ""
+msgstr "Підключіть ваші зовнішні репозиторії, Ñ– CI/CD конвеєри будуть запуÑкатиÑÑ Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… комітів. Створений GitLab-проект буде мати лише CI/CD фунції."
msgid "Connecting..."
msgstr "З'єднаннÑ..."
@@ -2062,7 +2212,7 @@ msgid "ContainerRegistry|No tags in Container Registry for this container image.
msgstr "Ð’ РеєÑтрі Контейнерів немає тегів Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ образу."
msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
-msgstr "ПіÑÐ»Ñ Ð²Ñ…Ð¾Ð´Ñƒ ви можете Ñтворювати та завантажувати образи контейнерів, викориÑтовуючи звичайні %{build} та %{push} команди"
+msgstr "ПіÑÐ»Ñ Ð²Ñ…Ð¾Ð´Ñƒ ви можете Ñтворювати та надÑилати образи контейнерів, викориÑтовуючи звичайні %{build} та %{push} команди"
msgid "ContainerRegistry|Remove repository"
msgstr "Видалити репозиторій"
@@ -2092,13 +2242,13 @@ msgid "Continue"
msgstr "Продовжити"
msgid "Continue to the next step"
-msgstr ""
+msgstr "Перейти до наÑтупного кроку"
msgid "Continuous Integration and Deployment"
msgstr "Безперервна Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ‚Ð° розгортаннÑ"
msgid "Contribute to GitLab"
-msgstr ""
+msgstr "Зробити внеÑок в GitLab"
msgid "Contribution"
msgstr "ВнеÑок"
@@ -2106,6 +2256,9 @@ msgstr "ВнеÑок"
msgid "Contribution guide"
msgstr "ІнÑÑ‚Ñ€ÑƒÐºÑ†Ñ–Ñ Ð´Ð»Ñ ÑƒÑ‡Ð°Ñників"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr "ВнеÑки за <strong>%{calendar_date}</strong>"
+
msgid "Contributions per group member"
msgstr "КількіÑÑ‚ÑŒ внеÑків на кожного учаÑника групи"
@@ -2125,7 +2278,7 @@ msgid "ContributorsPage|Please wait a moment, this page will automatically refre
msgstr "Будь лаÑка, зачекайте, Ñ†Ñ Ñторінка автоматично оновитьÑÑ, коли буде готова."
msgid "Control the display of third party offers."
-msgstr ""
+msgstr "Керувати відображеннÑм Ñторонніх пропозицій."
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr "Задати макÑимальну кількіÑÑ‚ÑŒ потоків Ð´Ð»Ñ Ñ„Ð¾Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ LFS/вкладень Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ вторинного вузла"
@@ -2139,6 +2292,15 @@ msgstr "Ð’Ñтановіть макÑимальну кількіÑÑ‚ÑŒ параÐ
msgid "ConvDev Index"
msgstr "Ð†Ð½Ð´ÐµÐºÑ ConvDev"
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr "Скопіюйте відкритий SSH-ключ в буфер обміну"
@@ -2244,9 +2406,6 @@ msgstr "Створити..."
msgid "Create project label"
msgstr "Створити мітку проекту"
-msgid "CreateNewFork|Fork"
-msgstr "Форк"
-
msgid "CreateTag|Tag"
msgstr "Тег"
@@ -2257,13 +2416,16 @@ msgid "Created"
msgstr "Створено"
msgid "Created At"
-msgstr ""
+msgstr "Створено в"
msgid "Created by me"
msgstr "Створено мною"
+msgid "Created on"
+msgstr "Створений"
+
msgid "Created on:"
-msgstr ""
+msgstr "Створено:"
msgid "Creating epic"
msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐµÐ¿Ñ–ÐºÑƒ"
@@ -2274,6 +2436,9 @@ msgstr "ЧаÑовий поÑÑ Cron"
msgid "Cron syntax"
msgstr "СинтакÑÐ¸Ñ Cron"
+msgid "Current Branch"
+msgstr "Поточна гілка"
+
msgid "Current node"
msgstr "Поточний вузол"
@@ -2283,6 +2448,9 @@ msgstr "Профіль"
msgid "CurrentUser|Settings"
msgstr "ÐалаштуваннÑ"
+msgid "Custom"
+msgstr "ВлаÑний"
+
msgid "Custom CI config path"
msgstr "КориÑтувацький шлÑÑ… до CI config"
@@ -2292,18 +2460,24 @@ msgstr "КориÑтувацькі Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "Спеціальні рівні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñпівпадають з рівнем учаÑÑ‚Ñ–. За допомогою Ñпеціальних рівнів Ñповіщень ви також отримуватимете ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ вибрані події. Щоб дізнатиÑÑŒ більше, переглÑньте %{notification_link}."
+msgid "Custom project templates"
+msgstr "ВлаÑні шаблони проектів"
+
msgid "Customize colors"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñ–Ð²"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "Ðалаштуйте, Ñк адреÑи електронної пошти та імена кориÑтувачів FogBugz імпортуютьÑÑ Ð² GitLab. Ðа наÑтупному кроці ви зможете вибрати проекти, Ñкі потрібно імпортувати."
msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "Ðалаштуйте, Ñк адреÑи електронної пошти та імена кориÑтувачів Google Code імпортуютьÑÑ Ð² GitLab. Ðа наÑтупному кроці ви зможете вибрати проекти, Ñкі потрібно імпортувати."
msgid "Cycle Analytics"
msgstr "Ðналіз циклу"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr "Ðналітика циклу дає інформацію про те, Ñкільки чаÑу потрібно, щоб пройти шлÑÑ… від ідеї до production у вашому проекті."
+
msgid "CycleAnalyticsStage|Code"
msgstr "ÐапиÑÐ°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ"
@@ -2334,6 +2508,12 @@ msgstr "Ð’ÑÑ–"
msgid "DashboardProjects|Personal"
msgstr "ОÑобиÑÑ‚Ñ–"
+msgid "Date picker"
+msgstr "Вибір дати"
+
+msgid "Debug"
+msgstr "Відладка"
+
msgid "Dec"
msgstr "груд."
@@ -2343,14 +2523,17 @@ msgstr "грудень"
msgid "Decline and sign out"
msgstr "Відхити та вийти"
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr "Мітка клаÑифікації за замовчуваннÑм"
msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
+msgstr "За замовчуваннÑм: безпоÑередньо імпортувати адреÑу електронної пошти або ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Google Code"
msgid "Default: Map a FogBugz account ID to a full name"
-msgstr ""
+msgstr "По замовчуванню: викориÑтовувати ідентифікатор облікового запиÑу FogBugz Ñк повне ім'Ñ"
msgid "Define a custom pattern with cron syntax"
msgstr "Визначте влаÑний шаблон за допомогою ÑинтакÑиÑу cron"
@@ -2358,6 +2541,9 @@ msgstr "Визначте влаÑний шаблон за допомогою ÑÐ
msgid "Delete"
msgstr "Видалити"
+msgid "Delete Package"
+msgstr "Видалити пакет"
+
msgid "Delete Snippet"
msgstr "Видалити Ñніпет"
@@ -2368,7 +2554,7 @@ msgid "Deleted"
msgstr "Видалено"
msgid "Deny"
-msgstr ""
+msgstr "Заборонити"
msgid "Deploy"
msgid_plural "Deploys"
@@ -2414,7 +2600,7 @@ msgid "DeployKeys|Privately accessible deploy keys"
msgstr "Приватні ключі розгортаннÑ"
msgid "DeployKeys|Project usage"
-msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñƒ проектах"
+msgstr "ВикориÑтовуєтьÑÑ Ñƒ проектах"
msgid "DeployKeys|Publicly accessible deploy keys"
msgstr "Публічні колючі розгортаннÑ"
@@ -2513,19 +2699,25 @@ msgid "Description templates allow you to define context-specific templates for
msgstr "Шаблони опиÑу дозволÑÑŽÑ‚ÑŒ визначити конкретні шаблони обговорень та запитів на Ð·Ð»Ð¸Ð²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проекту."
msgid "Description:"
-msgstr ""
+msgstr "ОпиÑ:"
msgid "Destroy"
-msgstr ""
+msgstr "Знищити"
msgid "Details"
msgstr "Деталі"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr "Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ не доÑтупне"
msgid "Diffs|Something went wrong while fetching diff lines."
-msgstr ""
+msgstr "Проблема при отриманні Ñ€Ñдків відмінноÑтей."
+
+msgid "Direction"
+msgstr "ÐапрÑмок"
msgid "Directory name"
msgstr "Ім'Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ñƒ"
@@ -2539,9 +2731,21 @@ msgstr "Вимкнути Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
msgid "Disable group Runners"
msgstr "Вимкнути групові Runner'и"
+msgid "Discard"
+msgstr "СкаÑувати"
+
+msgid "Discard all changes"
+msgstr "СкаÑувати вÑÑ– зміни"
+
+msgid "Discard all unstaged changes?"
+msgstr "СкаÑувати вÑÑ– неіндекÑовані зміни?"
+
msgid "Discard changes"
msgstr "Відхилити зміни"
+msgid "Discard changes to %{path}?"
+msgstr "СкаÑувати зміни до %{path}?"
+
msgid "Discard draft"
msgstr "Видалити чернетку"
@@ -2549,19 +2753,19 @@ msgid "Discover GitLab Geo."
msgstr "Відкрийте GitLab Geo."
msgid "Discover projects, groups and snippets. Share your projects with others"
-msgstr ""
+msgstr "Відкрийте Ð´Ð»Ñ Ñебе групи, проекти та фрагменти коду. ПоділітьÑÑ Ñвоїми проектами з іншими"
msgid "Dismiss"
-msgstr ""
+msgstr "Відхилити"
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Відхилити блок вÑтупу до Ðналитики Циклу"
msgid "Dismiss Merge Request promotion"
-msgstr ""
+msgstr "Видалити Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ запит на злиттÑ"
msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
+msgstr "Ви хочете налаштувати, Ñк адреÑи електронної пошти та імена кориÑтувачів будуть імпортовані з Google Code в GitLab?"
msgid "Documentation for popular identity providers"
msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð´Ð»Ñ Ð¿Ð¾ÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ… провайдерів ідентифікації"
@@ -2627,28 +2831,28 @@ msgid "Edit Snippet"
msgstr "Редагувати Ñніпет"
msgid "Edit application"
-msgstr ""
+msgstr "Редагувати заÑтоÑунок"
msgid "Edit files in the editor and commit changes here"
msgstr "Редагуйте файли в редакторі і закомітьте зміни тут"
msgid "Edit group: %{group_name}"
-msgstr ""
+msgstr "Редагувати групу: %{group_name}"
msgid "Edit identity for %{user_name}"
-msgstr ""
+msgstr "Редагувати ідентифікацію Ð´Ð»Ñ %{user_name}"
msgid "Elasticsearch"
msgstr "Elasticsearch"
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ð· Elasticsearch. Elasticsearch AWS IAM."
msgid "Email"
msgstr "Електронна пошта"
msgid "Email patch"
-msgstr ""
+msgstr "Email-патч"
msgid "Emails"
msgstr "ÐдреÑи електронної пошти"
@@ -2663,10 +2867,10 @@ msgid "Enable Auto DevOps"
msgstr "Увімкнути Auto DevOps"
msgid "Enable Pseudonymizer data collection"
-msgstr ""
+msgstr "Увімкнути збір даних Ð´Ð»Ñ Pseudonymizer"
msgid "Enable SAML authentication for this group"
-msgstr ""
+msgstr "Увімкнути автентифікацію SAML Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи"
msgid "Enable Sentry for error reporting and logging."
msgstr "Увімкнути Sentry Ð´Ð»Ñ Ð·Ð²Ñ–Ñ‚Ñ–Ð² про помилки та логуваннÑ."
@@ -2678,7 +2882,7 @@ msgid "Enable and configure Prometheus metrics."
msgstr "Включити і налаштувати метрики Prometheus."
msgid "Enable classification control using an external service"
-msgstr ""
+msgstr "Увімкнути контроль за клаÑифікацією за допомогою зовнішньої Ñлужби"
msgid "Enable for this project"
msgstr "Увімкнути Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
@@ -2690,7 +2894,7 @@ msgid "Enable or disable certain group features and choose access levels."
msgstr "Увімкніть або вимкніть певні функції групи та виберіть рівні доÑтупу."
msgid "Enable or disable the Pseudonymizer data collection."
-msgstr ""
+msgstr "Увімкнути чи вимкнути збір даних Ð´Ð»Ñ Pseudonymizer."
msgid "Enable or disable version check and usage ping."
msgstr "Увімкнути чи вимкнути перевірку верÑÑ–Ñ— та надÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… про викориÑтаннÑ."
@@ -2701,12 +2905,33 @@ msgstr "Увімкнути reCAPTCHA або Akismet та вÑтановити о
msgid "Enable the Performance Bar for a given group."
msgstr "Увімкнути панель продуктивноÑÑ‚Ñ– Ð´Ð»Ñ Ð´Ð°Ð½Ð¾Ñ— групи."
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr "Увімкнено"
msgid "Ends at (UTC)"
msgstr "ЗавершуєтьÑÑ Ð¾ (за Грінвічем)"
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr "Введіть Ð¾Ð¿Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸"
+
+msgid "Enter the issue title"
+msgstr "Введіть назву проблеми"
+
+msgid "Enter the merge request description"
+msgstr "Введіть Ð¾Ð¿Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ"
+
+msgid "Enter the merge request title"
+msgstr "Введіть назву запиту на злиттÑ"
+
msgid "Environments"
msgstr "Середовища"
@@ -2717,16 +2942,16 @@ msgid "Environments|An error occurred while making the request."
msgstr "Під Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
msgid "Environments|An error occurred while stopping the environment, please try again"
-msgstr ""
+msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·ÑƒÐ¿Ð¸Ð½ÐºÐ¸ Ñередовища, будь лаÑка, Ñпробуйте ще раз"
msgid "Environments|Are you sure you want to stop this environment?"
-msgstr ""
+msgstr "Ви впевнені що хочете зупинити це Ñередовище?"
msgid "Environments|Commit"
msgstr "Коміт"
msgid "Environments|Deploy to..."
-msgstr ""
+msgstr "Розгортати до..."
msgid "Environments|Deployment"
msgstr "РозгортаннÑ"
@@ -2737,11 +2962,14 @@ msgstr "Середовище"
msgid "Environments|Environments"
msgstr "Середовища"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr "ЗавданнÑ"
msgid "Environments|Learn more about stopping environments"
-msgstr ""
+msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про зупинку Ñередовищ"
msgid "Environments|New environment"
msgstr "Ðове Ñередовище"
@@ -2750,34 +2978,37 @@ msgid "Environments|No deployments yet"
msgstr "Ще немає розгортань"
msgid "Environments|No pod name has been specified"
+msgstr "Ðе вказано Ñ–Ð¼â€™Ñ pod’а"
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
msgstr ""
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
-msgstr ""
+msgstr "Зверніть увагу, що Ñ†Ñ Ð´Ñ–Ñ Ð·ÑƒÐ¿Ð¸Ð½Ð¸Ñ‚ÑŒ Ñередовище, але це %{emphasis_start}не%{emphasis_end} впливатиме на будь-Ñке Ñ–Ñнуюче Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· відÑутніÑÑ‚ÑŒ операції зупинки в файлі %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end}."
msgid "Environments|Open live environment"
-msgstr ""
+msgstr "Відкрити працююче Ñередовище"
msgid "Environments|Pod logs from"
-msgstr ""
+msgstr "Журнал Pod’а"
msgid "Environments|Re-deploy to environment"
-msgstr ""
+msgstr "Повторно розгорнути в Ñередовищі"
msgid "Environments|Read more about environments"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñередовища"
msgid "Environments|Rollback environment"
-msgstr ""
+msgstr "Відкотити Ñередовище"
msgid "Environments|Show all"
msgstr "Показати вÑÑ–"
msgid "Environments|Stop"
-msgstr ""
+msgstr "Зупинити"
msgid "Environments|Stop environment"
-msgstr ""
+msgstr "Зупинити Ñередовище"
msgid "Environments|Updated"
msgstr "Оновлено"
@@ -2785,6 +3016,9 @@ msgstr "Оновлено"
msgid "Environments|You don't have any environments right now."
msgstr "Ви поки не налаштували жодного Ñередовища."
+msgid "Environments|protected"
+msgstr "захищені"
+
msgid "Epic"
msgstr "Епік"
@@ -2800,11 +3034,35 @@ msgstr "План-графік епіків"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr "Епіки дозволÑÑŽÑ‚ÑŒ керувати вашим портфелем проектів ефективніше та з меншими зуÑиллÑми"
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при збереженні дати %{epicDateType}"
+
+msgid "Epics|How can I solve this?"
+msgstr "Як Ñ Ð¼Ð¾Ð¶Ñƒ це вирішити?"
+
+msgid "Epics|More information"
+msgstr "Детальніше"
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr "Помилка"
+
msgid "Error Reporting and Logging"
msgstr "Звіти про помилки та логуваннÑ"
msgid "Error creating epic"
-msgstr ""
+msgstr "Помилка при Ñтворенні епіку"
msgid "Error fetching contributors data."
msgstr "Помилка Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… учаÑників."
@@ -2824,11 +3082,14 @@ msgstr "Помилка при отриманні данних про викорÐ
msgid "Error loading branch data. Please try again."
msgstr "Помилка при завантаженні даних про гілку. Будь лаÑка, Ñпробуйте знову."
+msgid "Error loading branches."
+msgstr "Помилка при завантаженні гілок."
+
msgid "Error loading last commit."
msgstr "Помилка при завантаженні оÑтаннього коміту."
msgid "Error loading markdown preview"
-msgstr ""
+msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾Ð³Ð¾ переглÑду markdown"
msgid "Error loading merge requests."
msgstr "Помилка при завантаженні запитів на злиттÑ."
@@ -2836,6 +3097,12 @@ msgstr "Помилка при завантаженні запитів на злÐ
msgid "Error loading project data. Please try again."
msgstr "Помилка при завантаженні даних проекту. Будь лаÑка, Ñпробуйте знову."
+msgid "Error loading template types."
+msgstr "Помилка при завантаженні типів шаблонів."
+
+msgid "Error loading template."
+msgstr "Помилка при завантаженні шаблону."
+
msgid "Error occurred when toggling the notification subscription"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñки на ÑповіщеннÑ"
@@ -2848,6 +3115,9 @@ msgstr "Помилка Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑтатуÑу Ð´Ð»Ñ Ð²ÑÑ–Ñ… задÐ
msgid "Error updating todo status."
msgstr "Помилка при оновленні ÑтатуÑу задачі."
+msgid "Error while loading the merge request. Please try again."
+msgstr "Помилка при завантаженні запита на злиттÑ. Будь лаÑка, Ñпробуйте знову."
+
msgid "Estimated"
msgstr "За оцінками"
@@ -2879,7 +3149,7 @@ msgid "Every week (Sundays at 4:00am)"
msgstr "Ð©Ð¾Ñ‚Ð¸Ð¶Ð½Ñ (в неділю о 4:00 ранку)"
msgid "Everyone can contribute"
-msgstr ""
+msgstr "Кожен може зробити Ñвій внеÑок"
msgid "Expand"
msgstr "Розгорнути"
@@ -2890,14 +3160,17 @@ msgstr "Розгорнути вÑе"
msgid "Expand sidebar"
msgstr "Розгорніть бічну панель"
-msgid "Explore"
+msgid "Expiration date"
msgstr ""
+msgid "Explore"
+msgstr "ОглÑд"
+
msgid "Explore GitLab"
-msgstr ""
+msgstr "ОглÑд GitLab"
msgid "Explore Groups"
-msgstr ""
+msgstr "ОглÑд Груп"
msgid "Explore groups"
msgstr "ОглÑд груп"
@@ -2909,7 +3182,7 @@ msgid "Explore public groups"
msgstr "ПереглÑнути публічні групи"
msgid "External Classification Policy Authorization"
-msgstr ""
+msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ ÐšÐ»Ð°ÑÐ¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ ÐŸÐ¾Ð»Ñ–Ñ‚Ð¸ÐºÐ¸ Ðвторизації"
msgid "External authentication"
msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ"
@@ -2947,6 +3220,9 @@ msgstr "Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ пов’Ñзані гілки.
msgid "Failed to remove issue from board, please try again."
msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ проблему з дошки, будь лаÑка, Ñпробуйте ще раз."
+msgid "Failed to remove mirror."
+msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ дзеркало."
+
msgid "Failed to remove the pipeline schedule"
msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ розклад конвеєра"
@@ -2957,7 +3233,7 @@ msgid "Failure"
msgstr "Помилка"
msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
-msgstr ""
+msgstr "Швидше, бо повтоно викориÑтовує робочу облаÑÑ‚ÑŒ проекту (викориÑтовуючи clone, Ñкщо та відÑутнÑ)"
msgid "Feb"
msgstr "лют."
@@ -2968,6 +3244,9 @@ msgstr "лютий"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "ÐŸÐ¾Ð»Ñ Ð½Ð° цій Ñторінці зараз недоÑтупні Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ, ви можете налаштувати"
+msgid "File templates"
+msgstr "Шаблони файлів"
+
msgid "Files"
msgstr "Файли"
@@ -2978,11 +3257,20 @@ msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and p
msgstr "Заповніть Ð¿Ð¾Ð»Ñ Ð½Ð¸Ð¶Ñ‡Ðµ, увімкніть <strong>%{enable_label}</strong> та натиÑніть <strong>%{save_changes}</strong>"
msgid "Filter"
-msgstr ""
+msgstr "Фільтр"
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr "Фільтрувати закриті за %{issuable_type}."
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr "Фільтрувати відкриті за %{issuable_type}."
msgid "Filter by commit message"
msgstr "Фільтрувати за коміт-повідомленнÑм"
+msgid "Filter..."
+msgstr "Фільтр..."
+
msgid "Find by path"
msgstr "Пошук по шлÑху"
@@ -2990,10 +3278,13 @@ msgid "Find file"
msgstr "Знайти файл"
msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
+msgstr "Знайдіть завантажений ZIP-файл і розпакуйте його."
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
-msgstr ""
+msgstr "Знайдіть щойно розпакований <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> файл."
+
+msgid "Fingerprints"
+msgstr "Відбитки пальців"
msgid "Finished"
msgstr "Завершено"
@@ -3004,23 +3295,35 @@ msgstr "Перший"
msgid "FirstPushedBy|pushed by"
msgstr "відправлено"
-msgid "FogBugz Email"
+msgid "Fixed date"
+msgstr "Дата виправленнÑ"
+
+msgid "Fixed due date"
msgstr ""
+msgid "Fixed start date"
+msgstr "Виправлена дата початку"
+
+msgid "Fixed:"
+msgstr "Виправлено:"
+
+msgid "FogBugz Email"
+msgstr "ÐдреÑа електронної пошти FogBugz"
+
msgid "FogBugz Import"
msgstr "Імпорт з FogBugz"
msgid "FogBugz Password"
-msgstr ""
+msgstr "Пароль FogBugz"
msgid "FogBugz URL"
-msgstr ""
+msgstr "URL-адреÑа FogBugz"
msgid "FogBugz import"
msgstr "Імпорт з FogBugz"
msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
+msgstr "Виконайте наведені нижче кроки, щоб екÑпортувати дані проекту з Google Code."
msgid "Font Color"
msgstr "Колір шрифту"
@@ -3029,20 +3332,19 @@ msgid "Footer message"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² футері"
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
-msgstr ""
+msgstr "Ð”Ð»Ñ Ð²Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ–Ñ… проектів будь-Ñкий зареєÑтрований кориÑтувач може переглÑдати конвеєри та отримати доÑтуп до інформації про роботу (логи та артефакти)"
+
+msgid "For more information, go to the "
+msgstr "Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації, відвідайте "
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr "Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації, переглÑньте документацію по %{deactivating_usage_ping_link_start}деактивації даних про викориÑтаннÑ%{deactivating_usage_ping_link_end}."
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
-msgstr ""
+msgstr "Ð”Ð»Ñ Ð¿Ñ€Ð¸Ð²Ð°Ñ‚Ð½Ð¸Ñ… проектів будь-Ñкий кориÑтувач (гіÑÑ‚ÑŒ або вище) може переглÑдати конвеєри та отримати доÑтуп до інформації про роботу (логи та артефакти)"
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
-msgstr ""
-
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "Форк"
-msgstr[1] "Форки"
-msgstr[2] "Форків"
-msgstr[3] "Форків"
+msgstr "Ð”Ð»Ñ Ð¿ÑƒÐ±Ð»Ñ–Ñ‡Ð½Ð¸Ñ… проектів будь-Ñкий зареєÑтрований кориÑтувач може переглÑдати конвеєри та отримати доÑтуп до інформації про роботу (логи та артефакти)"
msgid "ForkedFromProjectPath|Forked from"
msgstr "Форк від"
@@ -3063,16 +3365,19 @@ msgid "From %{provider_title}"
msgstr "З %{provider_title}"
msgid "From Bitbucket"
-msgstr ""
+msgstr "З Bitbucket"
+
+msgid "From Bitbucket Server"
+msgstr "З Bitbucket Server"
msgid "From FogBugz"
-msgstr ""
+msgstr "З FogBugz"
msgid "From GitLab.com"
-msgstr ""
+msgstr "З GitLab.com"
msgid "From Google Code"
-msgstr ""
+msgstr "З Google Code"
msgid "From issue creation until deploy to production"
msgstr "З моменту ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ до Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
@@ -3080,6 +3385,9 @@ msgstr "З моменту ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ до розгорÑ
msgid "From merge request merge until deploy to production"
msgstr "Від Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð¾ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
+msgid "From milestones:"
+msgstr "З етапів:"
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Із Ñторінки деталей Kubernetes-клаÑтера, вÑтановіть runner зі ÑпиÑку заÑтоÑунків"
@@ -3095,6 +3403,9 @@ msgstr "Загальні конвеєри"
msgid "Generate a default set of labels"
msgstr "Створити Ñтандартний набір міток"
+msgid "Geo"
+msgstr "Geo"
+
msgid "Geo Nodes"
msgstr "Гео-Вузли"
@@ -3150,10 +3461,10 @@ msgid "GeoNodes|Learn more about Repository verification"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про перевірку репозиторію"
msgid "GeoNodes|Learn more about Wiki checksum progress"
-msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñтан обчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми wiki"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñтан обчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми вікі"
msgid "GeoNodes|Learn more about Wiki verification"
-msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про перевірку wiki"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про перевірку вікі"
msgid "GeoNodes|Loading nodes"
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð²ÑƒÐ·Ð»Ñ–Ð²"
@@ -3243,53 +3554,152 @@ msgid "GeoNodes|Verified"
msgstr "Підтверджені"
msgid "GeoNodes|Wiki checksum progress"
-msgstr "Стан обчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми wiki"
+msgstr "Стан обчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми вікі"
msgid "GeoNodes|Wiki verification progress"
-msgstr "Стан перевірки wiki"
+msgstr "Стан перевірки вікі"
msgid "GeoNodes|Wikis"
-msgstr "Wiki"
+msgstr "Вікі"
msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
-msgstr "Контрольні Ñуми wiki обчиÑлено Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ Ñ—Ñ… копій на вторинних вузлах"
+msgstr "Контрольні Ñуми вікі обчиÑлено Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ Ñ—Ñ… копій на вторинних вузлах"
msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
-msgstr "Wiki перевірено із їхніми копіÑми на первинному вузлі"
+msgstr "Вікі перевірено із їхніми копіÑми на первинному вузлі"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr "Ви налаштували Geo-вузли через незахищене HTTP-з’єднаннÑ. Ми рекомендуємо викориÑтовувати HTTPS."
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr "%{name} заплановано Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÑƒÑового повторного завантаженнÑ"
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr "%{name} заплановано Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð½Ð¾Ñ— перевірки"
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr "%{name} заплановано Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð½Ð¾Ñ— Ñинхронізації"
+
msgid "Geo|All projects"
msgstr "Ð’ÑÑ– проекти"
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилку"
+
+msgid "Geo|Failed"
+msgstr "Ðевдало"
+
msgid "Geo|File sync capacity"
msgstr "ПропуÑкна здатніÑÑ‚ÑŒ Ñинхронізації файлів"
msgid "Geo|Groups to synchronize"
msgstr "Групи Ð´Ð»Ñ Ñинхронізації"
+msgid "Geo|In sync"
+msgstr "Синхронізовано"
+
+msgid "Geo|Last successful sync"
+msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ ÑƒÑпішна ÑинхронізаціÑ"
+
+msgid "Geo|Last sync attempt"
+msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ Ñпроба Ñинхронізації"
+
+msgid "Geo|Last time verified"
+msgstr "ОÑтанній Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸"
+
+msgid "Geo|Never"
+msgstr "Ðіколи"
+
+msgid "Geo|Next sync scheduled at"
+msgstr "ÐаÑтупна ÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð·Ð°Ð¿Ð»Ð°Ð½Ð¾Ð²Ð°Ð½Ð° на"
+
+msgid "Geo|No errors"
+msgstr "Без помилок"
+
+msgid "Geo|Pending"
+msgstr "В очікуванні"
+
+msgid "Geo|Pending synchronization"
+msgstr "ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñинхронізації"
+
+msgid "Geo|Pending verification"
+msgstr "ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸"
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr "Проекти в певних групах"
msgid "Geo|Projects in certain storage shards"
msgstr "Проекти в певних Ñегментах Ñховищ"
+msgid "Geo|Recheck"
+msgstr "Повторна перевірка"
+
+msgid "Geo|Redownload"
+msgstr "Повторне завантаженнÑ"
+
+msgid "Geo|Remove"
+msgstr "Видалити"
+
msgid "Geo|Repository sync capacity"
msgstr "ПропуÑкна здатніÑÑ‚ÑŒ Ñинхронізації репозиторіїв"
+msgid "Geo|Resync"
+msgstr "Повторна ÑинхронізаціÑ"
+
+msgid "Geo|Retry count"
+msgstr "КількіÑÑ‚ÑŒ Ñпроб"
+
+msgid "Geo|Retry counts"
+msgstr "КількоÑÑ‚Ñ– Ñпроб"
+
msgid "Geo|Select groups to replicate."
msgstr "Виберіть групи Ð´Ð»Ñ Ñ€ÐµÐ¿Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ—."
msgid "Geo|Shards to synchronize"
msgstr "Сегменти Ð´Ð»Ñ Ñинхронізації"
+msgid "Geo|Status"
+msgstr "СтатуÑ"
+
+msgid "Geo|Synced"
+msgstr "Синхронізовано"
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr "Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð½ÐµÐ²Ð´Ð°Ð»Ð°: %{error}"
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr "Ðевідомий Ñтан"
+
msgid "Geo|Verification capacity"
msgstr "ПропуÑкна здатніÑÑ‚ÑŒ перевірки"
-msgid "Git"
+msgid "Geo|Verification failed - %{error}"
+msgstr "Перевірка невдала: %{error}"
+
+msgid "Geo|Waiting for scheduler"
+msgstr "ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð»Ð°Ð½ÑƒÐ²Ð°Ð»ÑŒÐ½Ð¸ÐºÐ°"
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr "Вам потрібна інша Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð½Ð° викориÑÑ‚Ð°Ð½Ð½Ñ Ð³ÐµÐ¾Ð³Ñ€Ð°Ñ„Ñ–Ñ‡Ð½Ð¾Ñ— реплікації"
+
+msgid "Get a free instance review"
msgstr ""
+msgid "Git"
+msgstr "Git"
+
msgid "Git repository URL"
msgstr "URL Git-репозиторіÑ"
@@ -3300,7 +3710,7 @@ msgid "Git storage health information has been reset"
msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Git була Ñкинута"
msgid "Git strategy for pipelines"
-msgstr ""
+msgstr "Git Ñтратегії Ð´Ð»Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð²"
msgid "Git version"
msgstr "Git-верÑÑ–Ñ"
@@ -3318,22 +3728,25 @@ msgid "GitLab Group Runners can execute code for all the projects in this group.
msgstr "Групові Runner'и Gitlab можуть виконувати код Ð´Ð»Ñ ÑƒÑÑ–Ñ… проектів у цій групі."
msgid "GitLab Import"
-msgstr ""
+msgstr "Імпорт з GitLab"
msgid "GitLab User"
-msgstr ""
+msgstr "GitLab КориÑтувач"
msgid "GitLab project export"
-msgstr ""
+msgstr "ЕкÑпорт проекту GitLab"
msgid "GitLab single sign on URL"
msgstr "URL єдиного входу GitLab"
msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
-msgstr ""
+msgstr "GitLab запуÑтить фонове завданнÑ, Ñке буде оброблÑти пÑевдонімізовані CSV-файли бази даних GitLab, Ñкі будуть надіÑлані до вашого налаштованого Ñховища об'єктів."
msgid "GitLab.com import"
-msgstr ""
+msgstr "Імпорт з GitLab.com"
+
+msgid "GitLab’s issue tracker"
+msgstr "Трекер проблем GitLab"
msgid "Gitaly"
msgstr "Gitaly"
@@ -3345,10 +3758,10 @@ msgid "Gitaly|Address"
msgstr "ÐдреÑа"
msgid "Gitea Host URL"
-msgstr ""
+msgstr "URL-адреÑа хоÑту Gitea"
msgid "Gitea Import"
-msgstr ""
+msgstr "Імпорт з Gitea"
msgid "Go Back"
msgstr "ПовернутиÑÑ"
@@ -3356,20 +3769,17 @@ msgstr "ПовернутиÑÑ"
msgid "Go back"
msgstr "ПовернутиÑÑ"
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
-msgid "Go to your fork"
-msgstr "Перейти до вашого форку"
+msgid "Go to"
+msgstr "Перейти до"
-msgid "GoToYourFork|Fork"
-msgstr "Форк"
+msgid "Go to %{link_to_google_takeout}."
+msgstr "Перейти до %{link_to_google_takeout}."
msgid "Google Code import"
-msgstr ""
+msgstr "Імпорт з Google Code"
msgid "Google Takeout"
-msgstr ""
+msgstr "Google Takeout"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "ÐÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Google не %{link_to_documentation}. ПопроÑÑ–Ñ‚ÑŒ Ñвого адмініÑтратора GitLab, Ñкщо ви хочете ÑкориÑтатиÑÑ Ñ†Ð¸Ð¼ ÑервіÑом."
@@ -3381,13 +3791,13 @@ msgid "Graph"
msgstr "Графік"
msgid "Group"
-msgstr ""
+msgstr "Група"
msgid "Group CI/CD settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD групи"
msgid "Group Git LFS status:"
-msgstr ""
+msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð³Ñ€ÑƒÐ¿Ð¸ Git LFS:"
msgid "Group ID"
msgstr "Ідентифікатор групи"
@@ -3396,13 +3806,13 @@ msgid "Group Runners"
msgstr "Групові Runner'и"
msgid "Group avatar"
-msgstr ""
+msgstr "Ðватар групи"
msgid "Group details"
-msgstr ""
+msgstr "Деталі групи"
msgid "Group info:"
-msgstr ""
+msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ групу:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "Керівники групи можуть зареєÑтрувати групові runner'и через %{link}"
@@ -3425,27 +3835,36 @@ msgstr "Вибачте, жоден епік не задовольнÑÑ” крит
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr "План-графік епіків відображає Ñтан ваших епіків у чаÑÑ–"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку додайте заплановані дати початку та Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків у цій групі або Ñ—Ñ— підгрупах. При поміÑÑчному переглÑді показуютьÑÑ Ð»Ð¸ÑˆÐµ епіки за попередній, поточний та наÑтупні 5 міÑÑців: від %{startDate} до %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку додайте заплановані дати початку та Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків у цій групі або Ñ—Ñ— підгрупах. При поквартальному переглÑді показуютьÑÑ Ð»Ð¸ÑˆÐµ епіки за попередній, поточний та наÑтупні 4 квартали: від %{startDate} до %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ змініть або видаліть фільтри. При поміÑÑчному переглÑді показуютьÑÑ ÐµÐ¿Ñ–ÐºÐ¸ лише за попередній, поточний та наÑтупні 5 міÑÑців: &ndash; від %{startDate} до %{endDate}."
msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ змініть або видаліть фільтри. При поквартальному переглÑді показуютьÑÑ ÐµÐ¿Ñ–ÐºÐ¸ лише за попередній, поточний та наÑтупні 4 квартали: &ndash; від %{startDate} до %{endDate}."
msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ змініть або видаліть фільтри. При потижневому переглÑді показуютьÑÑ ÐµÐ¿Ñ–ÐºÐ¸ лише за попередній, поточний та наÑтупні 4 тижні: &ndash; від %{startDate} до %{endDate}."
msgid "GroupRoadmap|Until %{dateWord}"
msgstr "До %{dateWord}"
+msgid "GroupSettings|Badges"
+msgstr "Значки"
+
+msgid "GroupSettings|Customize your group badges."
+msgstr "Ðалаштувати значки групи."
+
+msgid "GroupSettings|Learn more about badges."
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про значки."
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Заборонити Ñпільний доÑтуп до проекту в рамках %{group} з іншими групами"
@@ -3474,28 +3893,28 @@ msgid "Groups"
msgstr "Групи"
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
-msgstr ""
+msgstr "Групи також можуть бути вкладеними при викориÑтанні %{subgroup_docs_link_start}підгруп%{subgroup_docs_link_end}."
msgid "GroupsDropdown|Frequently visited"
-msgstr ""
+msgstr "ЧаÑто відвідувані"
msgid "GroupsDropdown|Groups you visit often will appear here"
-msgstr ""
+msgstr "Групи, Ñкі ви чаÑто відвідуєте, будуть відображені тут"
msgid "GroupsDropdown|Loading groups"
-msgstr ""
+msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿"
msgid "GroupsDropdown|Search your groups"
-msgstr ""
+msgstr "Пошук по ваших групах"
msgid "GroupsDropdown|Something went wrong on our end."
-msgstr ""
+msgstr "ЩоÑÑŒ пішло не так з нашого боку."
msgid "GroupsDropdown|Sorry, no groups matched your search"
-msgstr ""
+msgstr "Ðа жаль жодна группа не задовольнÑÑ” параметрам вашого запиту"
msgid "GroupsDropdown|This feature requires browser localStorage support"
-msgstr ""
+msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±ÑƒÑ” підтримки localStorage вашим браузером"
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr "Група — набір із декількох проектів."
@@ -3509,6 +3928,9 @@ msgstr "Групи не знайдені"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "Ви можете керувати правами доÑтупу членів групи мати доÑтуп до кожного проекту в ній."
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr "Ви впевнені, що хочете залишити групу \"%{fullName}\"?"
+
msgid "GroupsTree|Create a project in this group."
msgstr "Створити проект у групі."
@@ -3521,20 +3943,20 @@ msgstr "Редагувати групу"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr "Ðе вдалоÑÑ Ð·Ð°Ð»Ð¸ÑˆÐ¸Ñ‚Ð¸ групу. Будь-лаÑка впевнітьÑÑ, що ви не єдитий влаÑник."
-msgid "GroupsTree|Filter by name..."
-msgstr "Фільтрувати за іменем…"
-
msgid "GroupsTree|Leave this group"
msgstr "Залишити цю группу"
msgid "GroupsTree|Loading groups"
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "Ðа жаль жодна группа не задовольнÑÑ” параметрам вашого запиту"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "Ðа жаль жодна группа чи проект не задовольнÑÑ” параметрам вашого запиту"
+msgid "GroupsTree|Search by name"
+msgstr "Пошук за іменем"
msgid "Have your users email"
msgstr "Електронна пошта Ð´Ð»Ñ Ð·Ð²ÐµÑ€Ñ‚Ð°Ð½ÑŒ кориÑтувачів"
@@ -3569,6 +3991,15 @@ msgstr "Сторінка довідки"
msgid "Help page text and support page url."
msgstr "ТекÑÑ‚ Ñторінки довідки та url-адреÑа Ñторінки підтримки."
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr "Це відкритий (публічний) SSH ключ, Ñкий потрібно додати на віддалений Ñервер. Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації, звернітьÑÑ Ð´Ð¾ документації."
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "Сховати значеннÑ"
@@ -3577,7 +4008,7 @@ msgstr[2] "Сховати значень"
msgstr[3] "Сховати значень"
msgid "Hide whitespace changes"
-msgstr ""
+msgstr "Приховати зміни пробілів"
msgid "History"
msgstr "ІÑторіÑ"
@@ -3594,35 +4025,62 @@ msgstr "Я погоджуюÑÑŒ з Правилами кориÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ
msgid "ID"
msgstr "ID"
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr "Ðазад"
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr "Коміт"
msgid "IDE|Edit"
msgstr "Редагувати"
-msgid "IDE|Go back"
-msgstr "ПовернутиÑÑ Ð½Ð°Ð·Ð°Ð´"
+msgid "IDE|Get started with Live Preview"
+msgstr "Розпочати роботу із попереднім переглÑдом"
+
+msgid "IDE|Go to project"
+msgstr "Перейти до проекту"
+
+msgid "IDE|Live Preview"
+msgstr "Попередній переглÑд"
msgid "IDE|Open in file view"
msgstr "Відкрити Ñк файл"
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr "Оновити попередній переглÑд"
+
msgid "IDE|Review"
msgstr "ОглÑд"
+msgid "IP Address"
+msgstr "IP-адреÑа"
+
msgid "Identifier"
msgstr "Ідентифікатор"
msgid "Identities"
-msgstr ""
+msgstr "ІдентифікаціÑ"
msgid "Identity provider single sign on URL"
msgstr "URL єдиного входу провайдера ідентифікації"
-msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr "Якщо це відключено, то рівень доÑтупу буде залежати від дозволів кориÑтувача в проекті."
+
msgid "If enabled"
-msgstr ""
+msgstr "Якщо увімкнено"
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr "Якщо це дозволено, доÑтуп до проектів буде перевірÑтиÑÑ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾ÑŽ Ñлужбою з викориÑтаннÑм Ñ—Ñ… мітки клаÑифікації."
@@ -3661,28 +4119,34 @@ msgid "Import all repositories"
msgstr "Імпорт вÑÑ–Ñ… репозиторіїв"
msgid "Import an exported GitLab project"
-msgstr ""
+msgstr "Імпортувати екÑпортований проект GitLab"
msgid "Import in progress"
msgstr "Імпорт триває"
msgid "Import multiple repositories by uploading a manifest file."
-msgstr ""
+msgstr "Імпортувати кілька репозиторіїв, надіÑлавши файл маніфеÑту."
msgid "Import project"
-msgstr ""
+msgstr "Імпорт проекту"
msgid "Import projects from Bitbucket"
-msgstr ""
+msgstr "Імпортувати проекти з Bitbucket"
+
+msgid "Import projects from Bitbucket Server"
+msgstr "Імпортувати проекти з Bitbucket Server"
msgid "Import projects from FogBugz"
-msgstr ""
+msgstr "Імпортувати проекти з FogBugz"
msgid "Import projects from GitLab.com"
-msgstr ""
+msgstr "Імпортувати проекти з GitLab.com"
msgid "Import projects from Google Code"
-msgstr ""
+msgstr "Імпортувати проекти з Google Code"
+
+msgid "Import repositories from Bitbucket Server"
+msgstr "Імпортувати репозиторії з Bitbucket Server"
msgid "Import repositories from GitHub"
msgstr "Імпорт репозиторіїв з GitHub"
@@ -3702,21 +4166,36 @@ msgstr "Покращити ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð°Ð¼Ð¸ з можл
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr "Покращити пошук за допомогою розширеного глобального пошук в верÑÑ–Ñ— GitLab Enterprise Edition."
-msgid "In the next step, you'll be able to select the projects you want to import."
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
msgstr ""
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr "Ðа наÑтупному кроці ви зможете вибрати проекти, Ñкі хочете імпортувати."
+
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr "Включити угоду про Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг та правила конфіденційноÑÑ‚Ñ–, Ñкі повинні прийнÑти вÑÑ– кориÑтувачі."
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
+msgstr "ÐеÑуміÑний проект"
+
+msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
msgid "Inline"
msgstr "Вбудований"
-msgid "Install GitLab Runner"
+msgid "Input host keys manually"
msgstr ""
+msgid "Input your repository URL"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr "Ð’Ñтановити GitLab Runner"
+
msgid "Install Runner on Kubernetes"
msgstr "Ð’Ñтановити Runner на Kubernetes"
@@ -3727,6 +4206,12 @@ msgstr[1] "ІнÑтанÑів"
msgstr[2] "ІнÑтанÑів"
msgstr[3] "ІнÑтанÑів"
+msgid "Instance Statistics"
+msgstr "СтатиÑтика інÑтанÑа"
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr "Цей інÑÑ‚Ð°Ð½Ñ Ð½Ðµ підтримує декілька Kubernetes-клаÑтерів"
@@ -3745,14 +4230,23 @@ msgstr "Ð’Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ â€” будь-Ñкий автентифікованиÐ
msgid "Internal - The project can be accessed by any logged in user."
msgstr "Внутрішній — будь-Ñкий автентифікований кориÑтувач має доÑтуп до цього проекту."
+msgid "Internal users"
+msgstr "Внутрішні кориÑтувачі"
+
msgid "Interval Pattern"
msgstr "Шаблон інтервалу"
msgid "Introducing Cycle Analytics"
msgstr "ПредÑтавлÑємо аналітику циклу"
+msgid "Invite"
+msgstr "ЗапрошеннÑ"
+
+msgid "Issue"
+msgstr "Проблема"
+
msgid "Issue Boards"
-msgstr ""
+msgstr "Дошки Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼"
msgid "Issue board focus mode"
msgstr "Режим фокуÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð´Ð¾ÑˆÐºÐ¸ обговорень"
@@ -3790,6 +4284,48 @@ msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ Ñтерте"
msgid "Jobs"
msgstr "ЗавданнÑ"
+msgid "Job|Browse"
+msgstr "ПереглÑнути"
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr "Завантажити"
+
+msgid "Job|Erase job log"
+msgstr "Видалити лог завданнÑ"
+
+msgid "Job|Job artifacts"
+msgstr "Ðртефакти завдань"
+
+msgid "Job|Job has been erased"
+msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ Ñтерте"
+
+msgid "Job|Job has been erased by"
+msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ Ñтерте"
+
+msgid "Job|Keep"
+msgstr "Залишити"
+
+msgid "Job|Scroll to bottom"
+msgstr "Прокрутити вниз"
+
+msgid "Job|Scroll to top"
+msgstr "Прокрутити вгору"
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
msgid "Jul"
msgstr "лип."
@@ -3802,12 +4338,6 @@ msgstr "чер."
msgid "June"
msgstr "червень"
-msgid "Koding"
-msgstr "Koding"
-
-msgid "Koding Dashboard"
-msgstr ""
-
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -3877,6 +4407,9 @@ msgstr "<span>ПеренеÑти мітку</span> %{labelTitle} <span>на рі
msgid "Labels|Promote Label"
msgstr "ПеренеÑти мітку"
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "ОÑтанній %d день"
@@ -3890,6 +4423,9 @@ msgstr "ОÑтанній Конвеєр"
msgid "Last commit"
msgstr "ОÑтанній коміт"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr "ОÑтанні зміни %{date}"
@@ -3914,6 +4450,9 @@ msgstr "ОÑтанні зміни"
msgid "Learn more"
msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ"
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{issue_boards_url}, щоб Ñтежити за проблемами в кількох ÑпиÑках, викориÑтовуючи мітки, виконавців та етапи. Якщо вам чогоÑÑŒ не виÑтачає в дошках обговорень проблем, Ñтворіть проблему на %{gitlab_issues_url}."
+
msgid "Learn more about Kubernetes"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Kubernetes"
@@ -3936,26 +4475,90 @@ msgid "Leave project"
msgstr "Залишити проект"
msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
+msgstr "Залиште параметри \"Тип файлу\" та \"Метод доÑтавки\" із значеннÑми по замовчуванню."
msgid "License"
msgstr "ЛіцензіÑ"
+msgid "LicenseManagement|Approve license"
+msgstr "Затвердити ліцензію"
+
+msgid "LicenseManagement|Approve license?"
+msgstr "Затвердити ліцензію?"
+
+msgid "LicenseManagement|Approved"
+msgstr "Затверджено"
+
+msgid "LicenseManagement|Blacklist license"
+msgstr "ЗанеÑти ліцензію в чорний ÑпиÑок"
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr "ЗанеÑти ліцензію в чорний ÑпиÑок?"
+
+msgid "LicenseManagement|Blacklisted"
+msgstr "Ð’ чорному ÑпиÑку"
+
+msgid "LicenseManagement|License"
+msgstr "ЛіцензіÑ"
+
+msgid "LicenseManagement|License Management"
+msgstr "Керувати ЛіцензіÑми"
+
+msgid "LicenseManagement|License details"
+msgstr "Деталі ліцензії"
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr "Ð£Ð¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð¸Ð¼Ð¸ ліцензіÑми та чорним ÑпиÑком ліцензій Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
+
+msgid "LicenseManagement|Packages"
+msgstr "Пакети"
+
+msgid "LicenseManagement|Remove license"
+msgstr "Видалити ліцензію"
+
+msgid "LicenseManagement|Remove license?"
+msgstr "Видалити ліцензію?"
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr "Ðаразі немає затверджених ліцензій чи ліцензій в чорному ÑпиÑку Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
+
+msgid "LicenseManagement|URL"
+msgstr "URL"
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr "Ви збираєтеÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ ліцензію %{name} із цього проекту."
+
+msgid "Licenses"
+msgstr "Ліцензії"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "LinkedIn"
-msgstr ""
+msgstr "LinkedIn"
msgid "List"
msgstr "СпиÑок"
msgid "List Your Gitea Repositories"
-msgstr ""
+msgstr "СпиÑок ваших репозиторіїв Gitea"
msgid "List available repositories"
+msgstr "СпиÑок доÑтупних репозиторіїв"
+
+msgid "List your Bitbucket Server repositories"
msgstr ""
msgid "List your GitHub repositories"
msgstr "СпиÑок ваших репозиторіїв GitHub"
+msgid "Live preview"
+msgstr "Попередній переглÑд"
+
msgid "Loading contribution stats for group members"
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÑтатиÑтики учаÑників групи"
@@ -3974,6 +4577,9 @@ msgstr "Заблокувати %{issuableDisplayName}"
msgid "Lock not found"
msgstr "Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ знайдено"
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr "Заблокувати цю %{issuableDisplayName}? Лише <strong>учаÑники проекту</strong> зможуть коментувати."
+
msgid "Lock to current projects"
msgstr "Закріпити за поточними проектами"
@@ -3990,28 +4596,31 @@ msgid "Locks give the ability to lock specific file or folder."
msgstr "Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ð¾Ð¶Ðµ бути заÑтоÑоване до конкретного файлу або директорії."
msgid "Logs"
-msgstr ""
+msgstr "Логи"
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr "Зробіть кожного учаÑника команди більш продуктивним незалежно від його міÑцезнаходженнÑ. GitLab Geo Ñтворює копії \"тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ\" вашого GitLab Ñервера, щоб Ñкоротити Ñ‡Ð°Ñ Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ– Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ з великих репозиторіїв."
msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
+msgstr "ПереконайтеÑÑ, що ви ввійшли за допомогою облікового запиÑу, Ñкому належать проекти, що потрібно імпортувати."
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
-msgstr ""
+msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñми Git за допомогою детального контролю доÑтупу збереже ваш код в безпеці. Виконуйте переглÑд коду та покращуйте Ñпівпрацю за допомогою запитів на злиттÑ. Кожен проект може також мати трекер проблем та вікі."
+
+msgid "Manage Web IDE features"
+msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñми веб-IDE"
msgid "Manage access"
-msgstr ""
+msgstr "Керувати доÑтупом"
msgid "Manage all notifications"
msgstr "Керувати вÑіма ÑповіщеннÑми"
msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
-msgstr ""
+msgstr "Керувати програмами, Ñкі можуть викориÑтовувати GitLab Ñк поÑтачальника OAuth, а також програми, Ñким ви Ñамі дозволили викориÑтовувати в вашому обліковому запиÑÑ–."
msgid "Manage applications that you've authorized to use your account."
-msgstr ""
+msgstr "Керувати програмами, Ñким ви дозволили викориÑтовувати Ñвій обліковий запиÑ."
msgid "Manage group labels"
msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ñ–Ñ‚ÐºÐ°Ð¼Ð¸ групи"
@@ -4026,22 +4635,22 @@ msgid "Manage your group’s membership while adding another level of security w
msgstr "Керуйте членÑтвом у вашій групі додаючи ще один рівень безпеки із SAML."
msgid "Manifest"
-msgstr ""
+msgstr "МаніфеÑÑ‚"
msgid "Manifest file import"
-msgstr ""
+msgstr "Імпортувати файл маніфеÑту"
msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
+msgstr "Зв’Ñзати обліковий Ð·Ð°Ð¿Ð¸Ñ FogBugz з кориÑтувачем GitLab"
msgid "Map a Google Code user to a GitLab user"
-msgstr ""
+msgstr "Зв’Ñзати кориÑтувача Google Code з кориÑтувачем GitLab"
msgid "Map a Google Code user to a full email address"
-msgstr ""
+msgstr "Зв’Ñзати кориÑтувача Google Code із адреÑою електронної пошти"
msgid "Map a Google Code user to a full name"
-msgstr ""
+msgstr "Зв’Ñзати кориÑтувача Google Code із повним іменем"
msgid "Mar"
msgstr "бер."
@@ -4055,9 +4664,21 @@ msgstr "Відмітити Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¸Ð¼"
msgid "Markdown enabled"
msgstr "Markdown увімкнено"
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr "Пакет Maven"
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "МакÑимальна кількіÑÑ‚ÑŒ невдач в Ñховищі даних git"
+msgid "Maximum job timeout"
+msgstr "МакÑимальний Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ"
+
msgid "May"
msgstr "травень"
@@ -4080,7 +4701,7 @@ msgid "Merge Requests"
msgstr "Запити на злиттÑ"
msgid "Merge Requests created"
-msgstr ""
+msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð±ÑƒÐ»Ð¾ Ñтворено"
msgid "Merge events"
msgstr "Події злиттÑ"
@@ -4098,21 +4719,21 @@ msgid "Merge requests are a place to propose changes you've made to a project an
msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ â€” це ÑпоÑіб запропонувати Ñвої зміни до проекту Ñ– обговорити Ñ—Ñ… із іншими"
msgid "MergeRequests|Resolve this discussion in a new issue"
-msgstr ""
+msgstr "Вирішити це Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð² новій проблемі"
msgid "MergeRequests|Saving the comment failed"
-msgstr ""
+msgstr "Помилка при збереженні коментарÑ"
msgid "MergeRequests|Toggle comments for this file"
-msgstr ""
-
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
+msgstr "Увімкнути або вимкнути коментарі Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ файлу"
msgid "MergeRequests|View file @ %{commitId}"
msgstr "ПереглÑнути файл @ %{commitId}"
msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr "ПереглÑнути замінений файл Ñтаном на %{commitId}"
+
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
msgstr ""
msgid "Merged"
@@ -4130,11 +4751,14 @@ msgstr "Метрики - Influx"
msgid "Metrics - Prometheus"
msgstr "Метрики - Prometheus"
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr "БізнеÑ"
msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
-msgstr ""
+msgstr "Перевірте документацію CI/CD щодо Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð² Ñередовищі"
msgid "Metrics|Create metric"
msgstr "Створити метрику"
@@ -4152,7 +4776,7 @@ msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit
msgstr "Ðазва вертикальної оÑÑ– графіка. Зазвичай це — одиниці вимірюваннÑ. Горизонтальна віÑÑŒ (віÑÑŒ X) завжди відображає чаÑ."
msgid "Metrics|Learn about environments"
-msgstr ""
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñередовища"
msgid "Metrics|Legend label (optional)"
msgstr "Заголовок легенди (необов’Ñзковий)"
@@ -4167,7 +4791,7 @@ msgid "Metrics|New metric"
msgstr "Ðова метрика"
msgid "Metrics|No deployed environments"
-msgstr ""
+msgstr "Ðемає розгорнутих Ñередовищ"
msgid "Metrics|Prometheus Query Documentation"
msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð¿Ð¾ запитам Prometheus"
@@ -4182,25 +4806,25 @@ msgid "Metrics|System"
msgstr "СиÑтема"
msgid "Metrics|There was an error fetching the environments data, please try again"
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про Ñередовища. Будь лаÑка, Ñпробуйте ще раз"
msgid "Metrics|There was an error getting deployment information."
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про розгортаннÑ."
msgid "Metrics|There was an error getting environments information."
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про Ñередовища."
msgid "Metrics|There was an error while retrieving metrics"
-msgstr ""
+msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¼ÐµÑ‚Ñ€Ð¸Ðº"
msgid "Metrics|Type"
msgstr "Тип"
msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
-msgstr ""
+msgstr "Ðеочікувана відповідь про Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð²Ñ–Ð´ ендпойнта Prometheus"
msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
-msgstr ""
+msgstr "Ðеочікувана відповідь про дані метрики від ендпойнта Prometheus"
msgid "Metrics|Unit label"
msgstr "Одиниці вимірюваннÑ"
@@ -4232,9 +4856,24 @@ msgstr "напр. зап/Ñек"
msgid "Milestone"
msgstr "Етап"
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr "Етапи"
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr "Видалити етап"
@@ -4253,9 +4892,30 @@ msgstr "ПеренеÑти %{milestoneTitle} на рівень групи?"
msgid "Milestones|Promote Milestone"
msgstr "ПеренеÑти етап"
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr "Цю дію не можна ÑкаÑувати."
+msgid "Mirror a repository"
+msgstr "Віддзеркалити репозиторій"
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr "Віддзеркалити репозиторій"
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "не додаÑте SSH ключ"
@@ -4272,10 +4932,7 @@ msgid "Months"
msgstr "МіÑÑці"
msgid "More"
-msgstr ""
-
-msgid "More actions"
-msgstr "Додаткові дії"
+msgstr "Більше"
msgid "More info"
msgstr "Детальніше"
@@ -4287,7 +4944,7 @@ msgid "More information is available|here"
msgstr "тут"
msgid "Most stars"
-msgstr ""
+msgstr "Ðайбільше зірок"
msgid "Move"
msgstr "ПереміÑтити"
@@ -4305,10 +4962,10 @@ msgid "Name new label"
msgstr "Ðазвіть нову мітку"
msgid "Name your individual key via a title"
-msgstr ""
+msgstr "Ðазвіть ваш індивідуальний ключ за допомогою заголовку"
msgid "Name:"
-msgstr ""
+msgstr "Ім’Ñ:"
msgid "Nav|Help"
msgstr "Допомога"
@@ -4323,19 +4980,22 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "Вийти із зайти під інший обліковим запиÑом"
msgid "Network"
-msgstr ""
+msgstr "Мережа"
+
+msgid "Never"
+msgstr "Ðіколи"
msgid "New"
msgstr "Ðовий"
msgid "New Application"
-msgstr ""
+msgstr "Ðовий додаток"
msgid "New Group"
-msgstr ""
+msgstr "Ðова група"
msgid "New Identity"
-msgstr "Ðова ідентичніÑÑ‚ÑŒ"
+msgstr "Ðова ідентифікаціÑ"
msgid "New Issue"
msgid_plural "New Issues"
@@ -4375,7 +5035,7 @@ msgid "New group"
msgstr "Ðова група"
msgid "New identity"
-msgstr "Ðова ідентичніÑÑ‚ÑŒ"
+msgstr "Ðова ідентифікаціÑ"
msgid "New issue"
msgstr "Ðова проблема"
@@ -4387,7 +5047,7 @@ msgid "New merge request"
msgstr "Ðовий запит на злиттÑ"
msgid "New pipelines will cancel older, pending pipelines on the same branch"
-msgstr ""
+msgstr "Ðові конвеєри ÑкаÑують Ñтарі, що очікують на тій же гілці"
msgid "New project"
msgstr "Ðовий проект"
@@ -4405,7 +5065,7 @@ msgid "New tag"
msgstr "Ðовий тег"
msgid "New..."
-msgstr ""
+msgstr "Ðовий..."
msgid "No"
msgstr "ÐÑ–"
@@ -4416,12 +5076,21 @@ msgstr "Без Мітки"
msgid "No assignee"
msgstr "Ðемає виконавцÑ"
+msgid "No branches found"
+msgstr "Гілок не знайдено"
+
msgid "No changes"
msgstr "Ðемає змін"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "Ðеможливо з'єднатиÑÑŒ із Ñервером Gitaly, будь лаÑка, перевірте логи!"
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr "Ð’ цьому проекті немає жодного образа контейнера. Додайте його за інÑтрукціÑми вище."
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr "Ðемає"
@@ -4438,13 +5107,16 @@ msgid "No files found."
msgstr "Ðе знайдено жодного файлу."
msgid "No issues for the selected time period."
-msgstr ""
+msgstr "Ðемає проблем за вибраний період чаÑу."
msgid "No labels with such name or description"
+msgstr "Ðемає міток з таким іменем або опиÑом"
+
+msgid "No license. All rights reserved"
msgstr ""
msgid "No merge requests for the selected time period."
-msgstr ""
+msgstr "Ðемає запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð° вибраний період чаÑу."
msgid "No merge requests found"
msgstr "Ðе знайдено жодного запиту на злиттÑ"
@@ -4453,29 +5125,44 @@ msgid "No messages were logged"
msgstr "Ðемає повідомлень у журналі"
msgid "No other labels with such name or description"
-msgstr ""
+msgstr "Ðемає інших міток з таким іменем або опиÑом"
+
+msgid "No packages stored for this project."
+msgstr "В цьому проекті немає пакетів."
msgid "No prioritised labels with such name or description"
-msgstr ""
+msgstr "Ðемає пріоритетних міток з таким іменем або опиÑом"
msgid "No public groups"
-msgstr ""
+msgstr "Ðемає публічних груп"
msgid "No pushes for the selected time period."
-msgstr ""
+msgstr "Ðемає відправок (push) за вказаний період чаÑу."
msgid "No repository"
msgstr "Ðемає репозиторію"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "Ðемає розкладів"
msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
+msgstr "ÐÑ–, безпоÑередньо імпортувати Ñ–Ñнуючі адреÑи електронної пошти та імена кориÑтувачів."
+
+msgid "Nodes"
+msgstr "Вузли"
msgid "None"
msgstr "Ðемає"
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr "Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ допуÑкаєтьÑÑ"
@@ -4494,23 +5181,26 @@ msgstr "Ðе конфіденційно"
msgid "Not enough data"
msgstr "ÐедоÑтатньо даних"
+msgid "Not now"
+msgstr "Пізніше"
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Майте на увазі, що гілка master захищена автоматично. %{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "Примітка: Ñк адмініÑтратор ви можете налаштувати %{github_integration_link}, що дозволить входити через GitHub Ñ– підключати репозиторії без ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾ÑобиÑтого токену доÑтупу."
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "Примітка: Ñк адмініÑтратор ви можете налаштувати %{github_integration_link}, що дозволить входити через GitHub Ñ– імпортувати репозиторії без ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾ÑобиÑтого токену доÑтупу."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "Примітка: звернітьÑÑ Ð´Ð¾ вашого адмініÑтратора GitLab, щоб налаштувати %{github_integration_link}, Ñкий дозволить входити за допомогою GitHub Ñ– приєднувати репозиторії без генерації перÑонального токена доÑтупу."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "Примітка: звернітьÑÑ Ð´Ð¾ вашого адмініÑтратора GitLab, щоб налаштувати %{github_integration_link}, Ñкий дозволить входити за допомогою GitHub та імпортувати репозиторії без генерації перÑонального токена доÑтупу."
msgid "Notes|Are you sure you want to cancel creating this comment?"
-msgstr ""
+msgstr "Ви впевнені, що хочете ÑкаÑувати цей коментар?"
msgid "Notification events"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ події"
@@ -4527,6 +5217,9 @@ msgstr "Ðевдача в конвеєрі"
msgid "NotificationEvent|Merge merge request"
msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾"
+msgid "NotificationEvent|New epic"
+msgstr "Ðовий епік"
+
msgid "NotificationEvent|New issue"
msgstr "Ðова проблема"
@@ -4597,37 +5290,47 @@ msgid "OfSearchInADropdown|Filter"
msgstr "Фільтр"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
-msgstr ""
+msgstr "ПіÑÐ»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ репозиторії можуть бути віддзеркалені через SSH. ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ %{ssh_link}"
+
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
+msgstr "Один або декілька ваших проектів Bitbucket не можна імпортувати безпоÑередньо в GitLab, оÑкільки вони викориÑтовують Subversion або Mercurial Ð´Ð»Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŽ верÑій заміÑÑ‚ÑŒ Git."
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
+msgstr "Один або декілька ваших проектів Google Code не можна імпортувати безпоÑередньо в GitLab, оÑкільки вони викориÑтовують Subversion або Mercurial Ð´Ð»Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŽ верÑій заміÑÑ‚ÑŒ Git."
-msgid "Online IDE integration settings."
-msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— із онлайн IDE."
+msgid "Only admins"
+msgstr "Тільки Ðдміни"
msgid "Only comments from the following commit are shown below"
msgstr "Ðижче наведено лише коментарі з наÑтупного коміту"
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr "Тільки учаÑники проекту можуть залишати коментарі."
msgid "Oops, are you sure?"
-msgstr ""
+msgstr "Ой, а ви впевнені?"
msgid "Open"
-msgstr "Відкрити"
+msgstr "Відкриті"
msgid "Open in Xcode"
msgstr "Відкрити в Xcode"
msgid "Open sidebar"
-msgstr ""
+msgstr "Розгорніть бічну панель"
msgid "Open source software to collaborate on code"
-msgstr ""
+msgstr "Відкрите програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñпільної роботи над кодом"
msgid "Opened"
msgstr "Відкрито"
@@ -4648,10 +5351,10 @@ msgid "Operations"
msgstr "Операції"
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
-msgstr ""
+msgstr "За необхідноÑÑ‚Ñ– ви можете %{link_to_customize} Ñк адреÑи електронної почти та імена кориÑтувачів FobBugz будуть імпортовані у GitLab."
msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
+msgstr "За необхідноÑÑ‚Ñ– ви можете %{link_to_customize} Ñк адреÑи електронної почти та імена кориÑтувачів Google Code будуть імпортовані у GitLab."
msgid "Options"
msgstr "Параметри"
@@ -4674,9 +5377,21 @@ msgstr "Вихідні запити"
msgid "Overview"
msgstr "ОглÑд"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "ВлаÑник"
+msgid "Package information"
+msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ пакет"
+
+msgid "Package was removed"
+msgstr "Пакет був видалений"
+
+msgid "Packages"
+msgstr "Пакети"
+
msgid "Pages"
msgstr "Сторінки"
@@ -4699,20 +5414,26 @@ msgid "Password"
msgstr "Пароль"
msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
-msgstr ""
+msgstr "Ð’Ñтавте Ñвій відкритий ключ SSH, Ñкий зазвичай знаходитьÑÑ Ñƒ файлі '~/.ssh/id_rsa.pub' Ñ– починаєтьÑÑ Ð· 'ssh-rsa'. Ðе викориÑтовуйте Ñвій приватний ключ SSH."
msgid "Path:"
-msgstr ""
+msgstr "ШлÑÑ…:"
msgid "Pause"
msgstr "Призупинити"
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr "В очікуванні"
-msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgid "People without permission will never get a notification and won't be able to comment."
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr "За завданнÑ. Якщо воно переходить цей поріг, то Ñтає позначеним Ñк невдале"
+
msgid "Perform advanced options such as changing path, transferring, or removing the group."
msgstr "Виконуйте такі розширені операції, Ñк зміна шлÑху, перенеÑÐµÐ½Ð½Ñ Ñ‡Ð¸ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿Ð¸."
@@ -4728,6 +5449,9 @@ msgstr "Токену перÑонального доÑтупу"
msgid "Pipeline"
msgstr "Конвеєр"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "Стан Конвеєра"
@@ -4741,7 +5465,7 @@ msgid "Pipeline quota"
msgstr "Квота на конвеєри"
msgid "Pipeline triggers"
-msgstr ""
+msgstr "Тригери конвеєру"
msgid "PipelineCharts|Failed:"
msgstr "Ðевдалі:"
@@ -4815,6 +5539,9 @@ msgstr "Перевірка конфігурації (CI Lint)"
msgid "Pipelines|Clear Runner Caches"
msgstr "ОчиÑтити кеш Runner'ів"
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr "Розпочати роботу з Конвеєрами"
@@ -4836,6 +5563,9 @@ msgstr "Ð’ даний Ñ‡Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” %{scope} конвеєрів."
msgid "Pipelines|There are currently no pipelines."
msgstr "Ð’ даний Ñ‡Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” конвеєрів."
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "Цей проект в даний Ñ‡Ð°Ñ Ð½Ðµ налаштований Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑку конвеєрів."
@@ -4882,13 +5612,7 @@ msgid "Pipeline|with stages"
msgstr "зі ÑтадіÑми"
msgid "Plain diff"
-msgstr ""
-
-msgid "Planned finish date"
-msgstr "Запланована дата завершеннÑ"
-
-msgid "Planned start date"
-msgstr "Запланована дата початку"
+msgstr "ПроÑте порівнÑннÑ"
msgid "PlantUML"
msgstr "PlantUML"
@@ -4900,13 +5624,13 @@ msgid "Please accept the Terms of Service before continuing."
msgstr "Будь лаÑка, Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð¹Ð¼Ñ–Ñ‚ÑŒ умови Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг."
msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
-msgstr ""
+msgstr "Будь лаÑка Ñконвертуйте Ñ—Ñ… в %{link_to_git} Ñ– виконайте знову %{link_to_import_flow}."
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
+msgstr "Будь лаÑка Ñконвертуйте Ñ—Ñ… в Git на Google Code, Ñ– виконайте знову %{link_to_import_flow}."
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
-msgstr ""
+msgstr "Зверніть увагу, що Ñ†Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð° не Ñ” чаÑтиною GitLab, Ñ– ви повинні впевнитиÑÑ Ñƒ Ñ—Ñ— безпеці, перш ніж надавати доÑтуп."
msgid "Please select at least one filter to see results"
msgstr "Будь лаÑка виберіть хоча б один фільтр, щоб побачити результати"
@@ -4929,6 +5653,15 @@ msgstr "ÐалаштуваннÑ"
msgid "Preferences|Navigation theme"
msgstr "Тема навігації"
+msgid "Press Enter or click to search"
+msgstr "Ð”Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ натиÑніть Enter або клікніть"
+
+msgid "Preview"
+msgstr "Попередній переглÑд"
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr "Головний"
@@ -4957,7 +5690,16 @@ msgid "Profile"
msgstr "Профіль"
msgid "Profile Settings"
-msgstr ""
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ"
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr "Ви збираєтеÑÑ Ð¾Ñтаточно видалити %{yourAccount}, а також вÑÑ– проблеми, запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° групи, пов'Ñзані з вашим обліковим запиÑом. ПіÑÐ»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ %{deleteAccount}, його неможливо буде відновити."
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr "Ви збираєтеÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача %{currentUsernameBold} на %{newUsernameBold}. Профіль та проекти будуть перенаправлÑтиÑÑ Ð½Ð° проÑÑ‚Ñ–Ñ€ імен %{newUsername}, але таке Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡Ð¸Ñ‚ÑŒÑÑ, коли проÑÑ‚Ñ–Ñ€ імен %{currentUsername} буде зареєÑтровано на іншого кориÑтувача або групу. Будь лаÑка, оновіть віддалені адреÑи в репозиторіÑÑ… Git Ñкомога швидше."
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr "%{author_name} Ñтворив приватний внеÑок"
msgid "Profiles|Account scheduled for removal."
msgstr "Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð¿Ð»Ð°Ð½Ð¾Ð²Ð°Ð½Ð¸Ð¹ Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ."
@@ -4965,12 +5707,33 @@ msgstr "Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð¿Ð»Ð°Ð½Ð¾Ð²Ð°Ð½Ð¸Ð¹ Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»
msgid "Profiles|Add key"
msgstr "Додати ключ"
+msgid "Profiles|Add status emoji"
+msgstr "Додати Ñмайлик-ÑтатуÑ"
+
+msgid "Profiles|Avatar cropper"
+msgstr "Обрізка аватарів"
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr "Ðватар буде видалено. Ви впевнені?"
+
msgid "Profiles|Change username"
msgstr "Змінити ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
+msgid "Profiles|Choose file..."
+msgstr "Вибрати файл..."
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr "Виберіть Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ внеÑків до приватних репозиторіїв у вашому публічному профілі без інформації про проекти, репозиторії або організації."
+
+msgid "Profiles|Clear status"
+msgstr "ОчиÑтити ÑтатуÑ"
+
msgid "Profiles|Current path: %{path}"
msgstr "Поточний шлÑÑ…: %{path}"
+msgid "Profiles|Current status"
+msgstr "Поточний Ñтан"
+
msgid "Profiles|Delete Account"
msgstr "Видалити обліковий запиÑ"
@@ -4983,33 +5746,108 @@ msgstr "Видалити ваш обліковий запиÑ?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу неÑе наÑтупні наÑлідки:"
+msgid "Profiles|Do not show on profile"
+msgstr "Ðе відображати у профілі"
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr "Ðе відображати оÑобиÑту інформацію, пов’Ñзану із активноÑÑ‚Ñми, у ваших профілÑÑ…"
+
+msgid "Profiles|Edit Profile"
+msgstr "Редагувати профіль"
+
msgid "Profiles|Invalid password"
msgstr "Ðеправильний пароль"
msgid "Profiles|Invalid username"
msgstr "Ðеправильне ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
+msgid "Profiles|Main settings"
+msgstr "Головні налаштуваннÑ"
+
+msgid "Profiles|No file chosen"
+msgstr "Файл не вибрано"
+
msgid "Profiles|Path"
msgstr "ШлÑÑ…"
+msgid "Profiles|Position and size your new avatar"
+msgstr "Спозиціонуйте ваш аватар та задайте його розмір"
+
+msgid "Profiles|Private contributions"
+msgstr "Приватні внеÑки"
+
+msgid "Profiles|Public Avatar"
+msgstr "Публічний аватар"
+
+msgid "Profiles|Remove avatar"
+msgstr "Видалити аватар"
+
+msgid "Profiles|Set new profile picture"
+msgstr "Ð’Ñтановити нове Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ"
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr "ДеÑкі параметри недоÑтупні Ð´Ð»Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ñ… запиÑів LDAP"
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr "Розкажіть про Ñебе в межах 250 Ñимволів."
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr "МакÑимальний розмір файлу 200КБ."
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
-msgstr ""
+msgstr "Це не Ñхоже на публічниц ключ SSH. Ви впевнені, що хочете його додати?"
+
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr "Ð¦Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð° адреÑа буде відображатиÑÑ Ñƒ вашому публічному профілі."
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr "Цей Ñмайлик та Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ´ÑƒÑ‚ÑŒ показані у вашому профілі та в інтерфейÑÑ–."
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ñ” екÑпериментальною Ñ– переклади ще не завершені."
+
+msgid "Profiles|This information will appear on your profile."
+msgstr "Ð¦Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ´Ðµ відображатиÑÑ Ñƒ вашому профілі."
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Введіть ваш %{confirmationValue} Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ:"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
-msgstr ""
+msgstr "Зазвичай починаєтьÑÑ Ð· \"ssh-rsa ...\""
+
+msgid "Profiles|Update profile settings"
+msgstr "Оновити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ"
msgid "Profiles|Update username"
msgstr "Оновити ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
+msgid "Profiles|Upload new avatar"
+msgstr "Завантажити новий аватар"
+
msgid "Profiles|Username change failed - %{message}"
msgstr "Помилка при збереженні імені кориÑтувача - %{message}"
msgid "Profiles|Username successfully changed"
msgstr "Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача уÑпішно збережено"
+msgid "Profiles|Website"
+msgstr "ВебÑайт"
+
+msgid "Profiles|What's your status?"
+msgstr "Який ваш ÑтатуÑ?"
+
+msgid "Profiles|You can change your avatar here"
+msgstr "Тут ви можете змінити Ñвій аватар"
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr "Тут ви можете змінити Ñвій аватар або видалити його та повернутиÑÑ Ð´Ð¾ %{gravatar_link}"
+
+msgid "Profiles|You can upload your avatar here"
+msgstr "Тут ви можете завантажити Ñвій аватар"
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr "Тут ви можете завантажити Ñвій аватар або змінити його на %{gravatar_link}"
+
msgid "Profiles|You don't have access to delete this user."
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ кориÑтувача."
@@ -5019,8 +5857,20 @@ msgstr "Вам необхідно змінити влаÑника або видÐ
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ” влаÑником в цих групах:"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr "Ваша адреÑа електронної пошти була автоматично вÑтановлена на оÑнові вашого облікового запиÑу %{provider_label}."
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr "Ваше міÑÑ†ÐµÐ·Ð½Ð°Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ автоматично вÑтановлено на оÑнові вашого облікового запиÑу %{provider_label}."
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr "Ваше Ñ–Ð¼â€™Ñ Ð±ÑƒÐ»Ð¾ автоматично вÑтановлено на оÑнові вашого облікового запиÑу %{provider_label} щоб люди могли Ð²Ð°Ñ Ð²Ð¿Ñ–Ð·Ð½Ð°Ñ‚Ð¸."
+
+msgid "Profiles|Your status"
+msgstr "Ваш ÑтатуÑ"
+
msgid "Profiles|e.g. My MacBook key"
-msgstr ""
+msgstr "наприклад, мій ключ MacBook"
msgid "Profiles|your account"
msgstr "ваш обліковий запиÑ"
@@ -5052,6 +5902,9 @@ msgstr "Проект '%{project_name}' уÑпішно оновлено."
msgid "Project Badges"
msgstr "Значки проекту"
+msgid "Project URL"
+msgstr "URL-адреÑа проекту"
+
msgid "Project access must be granted explicitly to each user."
msgstr "ДоÑтуп до проекту повинен надаватиÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ кориÑтувачеві."
@@ -5077,7 +5930,10 @@ msgid "Project export started. A download link will be sent by email."
msgstr "Розпочато екÑпорт проекту. ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ ÑÐºÐ°Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ надіÑлана електронною поштою."
msgid "Project name"
-msgstr ""
+msgstr "Ðазва проекту"
+
+msgid "Project slug"
+msgstr "ШлÑÑ… проекту"
msgid "ProjectActivityRSS|Subscribe"
msgstr "ПідпиÑатиÑÑ"
@@ -5106,18 +5962,48 @@ msgstr "Ðіколи"
msgid "ProjectLifecycle|Stage"
msgstr "СтадіÑ"
-msgid "ProjectPage|Project ID: %{project_id}"
+msgid "ProjectOverview|Fork"
+msgstr "Форк"
+
+msgid "ProjectOverview|Forks"
+msgstr "Форки"
+
+msgid "ProjectOverview|Go to your fork"
+msgstr "Перейти до вашого форку"
+
+msgid "ProjectOverview|Star"
+msgstr "В обрані"
+
+msgid "ProjectOverview|Unstar"
+msgstr "Видалити із обраних"
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
msgstr ""
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr "ID проекту: %{project_id}"
+
+msgid "ProjectSettings|Badges"
+msgstr "Значки"
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "ЗвернітьÑÑ Ð´Ð¾ адмініÑтратора, щоб змінити це налаштуваннÑ."
+msgid "ProjectSettings|Customize your project badges."
+msgstr "Ðалаштувати значки проекту."
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr "Помилка при захиÑÑ‚Ñ– тегу"
msgid "ProjectSettings|Failed to update tag!"
msgstr "Помилка при оновленні тегу!"
+msgid "ProjectSettings|Learn more about badges."
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про значки."
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "Тільки підпиÑані коміти можуть бути надіÑлані в цей репозиторій."
@@ -5137,7 +6023,7 @@ msgid "Projects"
msgstr "Проекти"
msgid "Projects shared with %{group_name}"
-msgstr ""
+msgstr "Спільні проекти з %{group_name}"
msgid "ProjectsDropdown|Frequently visited"
msgstr "ЧаÑто відвідувані"
@@ -5157,35 +6043,38 @@ msgstr "ЩоÑÑŒ пішло не так з нашого боку."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Ðа жаль, по вашоу запиту проектів не знайдено"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±ÑƒÑ” підтримки localStorage вашим браузером"
+
msgid "PrometheusAlerts|Add alert"
-msgstr ""
+msgstr "Додати попередженнÑ"
msgid "PrometheusAlerts|Alert set"
-msgstr ""
+msgstr "Ðабір попереджень"
msgid "PrometheusAlerts|Edit alert"
-msgstr ""
+msgstr "Редагувати попередженнÑ"
msgid "PrometheusAlerts|Error creating alert"
-msgstr ""
+msgstr "Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ"
msgid "PrometheusAlerts|Error deleting alert"
-msgstr ""
+msgstr "Помилка Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ"
msgid "PrometheusAlerts|Error fetching alert"
-msgstr ""
+msgstr "Помилка Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ"
msgid "PrometheusAlerts|Error saving alert"
-msgstr ""
+msgstr "Помилка Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ"
msgid "PrometheusAlerts|No alert set"
-msgstr ""
+msgstr "Ðе вÑтановлено попередженнÑ"
msgid "PrometheusAlerts|Operator"
-msgstr ""
+msgstr "Оператор"
msgid "PrometheusAlerts|Threshold"
-msgstr ""
+msgstr "Поріг"
msgid "PrometheusDashboard|Time"
msgstr "ЧаÑ"
@@ -5289,6 +6178,54 @@ msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð°."
msgid "Promotions|Upgrade plan"
msgstr "Перейти на вищий тарифний план"
+msgid "Protected"
+msgstr "Захищено"
+
+msgid "Protected Environments"
+msgstr "Захищені Ñередовища"
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr "Середовище"
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr "Виберіть Ñередовище"
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr "ЗнÑти захиÑÑ‚"
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr "Підказка:"
@@ -5296,7 +6233,7 @@ msgid "Provider"
msgstr "ПоÑтачальник"
msgid "Pseudonymizer data collection"
-msgstr ""
+msgstr "Збір даних Pseudonymizer"
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Публічна — група та вÑÑ– публічні проекти можуть переглÑдатиÑÑ Ð±ÐµÐ· автентифікації."
@@ -5307,6 +6244,12 @@ msgstr "Публічний — проект может переглÑдатиÑÑ
msgid "Public pipelines"
msgstr "Публічні конвеєри"
+msgid "Pull"
+msgstr "Отримати (pull)"
+
+msgid "Push"
+msgstr "Відправити (push)"
+
msgid "Push Rules"
msgstr "Push-правила"
@@ -5326,7 +6269,7 @@ msgid "Pushed"
msgstr "Відправлено"
msgid "Pushes"
-msgstr ""
+msgstr "Відправки (push)"
msgid "Quarters"
msgstr "Квартали"
@@ -5338,7 +6281,7 @@ msgid "Read more"
msgstr "Докладніше"
msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
-msgstr ""
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про права доÑтупу в проекті <strong>%{link_to_help}</strong>"
msgid "Readme"
msgstr "ІнÑтрукціÑ"
@@ -5346,12 +6289,28 @@ msgstr "ІнÑтрукціÑ"
msgid "Real-time features"
msgstr "Фунції реального чаÑу"
+msgid "Recent searches"
+msgstr "ОÑтанні пошукові запити"
+
msgid "Reference:"
msgstr "ПоÑиланнÑ:"
msgid "Refresh"
msgstr "Оновити"
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Regenerate key"
+msgstr "Створити ключ заново"
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr "ЗареєÑтруватиÑÑ / Увійти"
@@ -5403,15 +6362,75 @@ msgstr "Видалити пріоритет"
msgid "Remove project"
msgstr "Видалити проект"
+msgid "Rename"
+msgstr "Перейменувати"
+
+msgid "Rename file"
+msgstr "Перейменувати файл"
+
+msgid "Rename folder"
+msgstr "Перейменувати папку"
+
+msgid "Reopen epic"
+msgstr "Повторне Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ ÐµÐ¿Ñ–ÐºÑƒ"
+
msgid "Repair authentication"
msgstr "Відновити аутентифікацію"
msgid "Reply to this email directly or %{view_it_on_gitlab}."
-msgstr ""
+msgstr "ВідповіÑти на це електронне Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð±ÐµÐ·Ð¿Ð¾Ñередньо або %{view_it_on_gitlab}."
msgid "Repo by URL"
msgstr "Репозиторії по URL"
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr "%{failedString} та %{resolvedString}"
+
+msgid "Reports|Class"
+msgstr "КлаÑ"
+
+msgid "Reports|Confidence"
+msgstr "ВпевненіÑÑ‚ÑŒ"
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr "Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ"
+
+msgid "Reports|Failure"
+msgstr "Помилка"
+
+msgid "Reports|More info"
+msgstr "Детальніше"
+
+msgid "Reports|New Issue"
+msgstr "Ðова проблема"
+
+msgid "Reports|Severity"
+msgstr "СерйозніÑÑ‚ÑŒ"
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr "ВразливіÑÑ‚ÑŒ"
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr "Репозиторій"
@@ -5419,7 +6438,7 @@ msgid "Repository Settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ"
msgid "Repository URL"
-msgstr ""
+msgstr "URL репозиторіÑ"
msgid "Repository has no locks."
msgstr "Репозиторій не має блокувань."
@@ -5440,7 +6459,7 @@ msgid "Request Access"
msgstr "Запит доÑтупу"
msgid "Requests Profiles"
-msgstr ""
+msgstr "ÐŸÑ€Ð¾Ñ„Ñ–Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð²"
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "Вимагати від уÑÑ–Ñ… кориÑтувачів приймати умови Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг та політику конфіденційноÑÑ‚Ñ–, коли вони отримують доÑтуп до GitLab."
@@ -5455,7 +6474,7 @@ msgid "Reset runners registration token"
msgstr "Перегенерувати реєÑтраційний токен runner-ів"
msgid "Resolve all discussions in new issue"
-msgstr ""
+msgstr "Вирішити вÑÑ– Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð² новій проблемі"
msgid "Resolve conflicts on source branch"
msgstr "Вирішити конфлікти у гілці-джерелі"
@@ -5463,9 +6482,21 @@ msgstr "Вирішити конфлікти у гілці-джерелі"
msgid "Resolve discussion"
msgstr "Завершити обговореннÑ"
+msgid "Response metrics (AWS ELB)"
+msgstr "Метрики відповідей (AWS ELB)"
+
msgid "Response metrics (Custom)"
msgstr "Метрики відповідей (ВлаÑні)"
+msgid "Response metrics (HA Proxy)"
+msgstr "Метрики відповідей (HA Proxy)"
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr "Метрики відповідей (NGINX Ingress)"
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr "Продовжити"
@@ -5478,6 +6509,9 @@ msgstr "Повторити це завданнÑ"
msgid "Retry verification"
msgstr "Повторити перевірку"
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] "Показати значеннÑ"
@@ -5504,7 +6538,7 @@ msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr "Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ (запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ !%{mergeRequestId})"
msgid "Revoke"
-msgstr ""
+msgstr "Відкликати"
msgid "Roadmap"
msgstr "План-графік"
@@ -5512,9 +6546,27 @@ msgstr "План-графік"
msgid "Run CI/CD pipelines for external repositories"
msgstr "ЗапуÑтити CI/CD конвеєри Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ–Ñ… репозиторіїв"
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr "Токен Runner'а"
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr "Runner'и"
@@ -5524,6 +6576,21 @@ msgstr "API Runner’ів"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr "Runner’и можуть розміщуватиÑÑ Ñƒ різних кориÑтувачів, на Ñерверах Ñ– навіть на вашій локальній машині."
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr "Сторінка Runner'ів"
+
+msgid "Runners page."
+msgstr "Сторінка Runner'ів."
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr "ВиконуєтьÑÑ"
@@ -5539,12 +6606,21 @@ msgstr "Єдиний вхід SAML"
msgid "SAML Single Sign On Settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ”Ð´Ð¸Ð½Ð¾Ð³Ð¾ входу SAML"
+msgid "SAST"
+msgstr "SAST"
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr "Відбиток SHA1 Ñертифікату Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸ÑÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¾ÐºÐµÐ½Ñ–Ð² SAML. Отримайте його від провайдера ідентифікації, де він також може називатиÑÑ \"Thumbprint\"."
msgid "SSH Keys"
msgstr "Ключі SSH"
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr "Перевірка SSL"
@@ -5552,7 +6628,7 @@ msgid "Save"
msgstr "Зберегти"
msgid "Save application"
-msgstr ""
+msgstr "Зберегти заÑтоÑунок"
msgid "Save changes"
msgstr "Зберегти зміни"
@@ -5576,13 +6652,13 @@ msgid "Scheduling Pipelines"
msgstr "ÐŸÐ»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð²"
msgid "Scope"
-msgstr ""
+msgstr "ОбÑÑг"
msgid "Scoped issue boards"
msgstr "Тематичні дошки проблем"
msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
-msgstr ""
+msgstr "Прокрутіть вниз до <strong>Google Code Project Hosting</strong> і увімкніть перемикач праворуч."
msgid "Scroll to bottom"
msgstr "Прокрутити вниз"
@@ -5611,12 +6687,42 @@ msgstr "Пошук у запитах на злиттÑ"
msgid "Search milestones"
msgstr "Пошук етапів"
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr "Пошук в проекті"
msgid "Search users"
msgstr "Пошук кориÑтувачів"
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr "в цій групі"
+
+msgid "SearchAutocomplete|in this project"
+msgstr "в цьому проекті"
+
msgid "Seconds before reseting failure information"
msgstr "КількіÑÑ‚ÑŒ Ñекунд до ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про збої"
@@ -5624,19 +6730,22 @@ msgid "Seconds to wait for a storage access attempt"
msgstr "КількіÑÑ‚ÑŒ Ñекунд Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ´ повторною Ñпробою доÑтупу до Ñховища даних"
msgid "Secret:"
-msgstr ""
+msgstr "Секрет:"
+
+msgid "Security"
+msgstr "Безпека"
msgid "Security Dashboard"
msgstr "Панель безпеки"
-msgid "Security report"
-msgstr "Звіт про безпеку"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
-msgstr ""
+msgstr "Моніторинг вразливоÑтей у вашому коді"
msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
-msgstr ""
+msgstr "Конвеєр %{pipelineLink} запущено"
msgid "Select"
msgstr "Вибрати"
@@ -5644,6 +6753,9 @@ msgstr "Вибрати"
msgid "Select Archive Format"
msgstr "Виберіть формат архіву"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr "Виберіть проÑÑ‚Ñ–Ñ€ імен Ð´Ð»Ñ Ñ„Ð¾Ñ€ÐºÑƒ проекту"
@@ -5669,7 +6781,7 @@ msgid "Select project to choose zone"
msgstr "Вибрати проект Ð´Ð»Ñ Ð²Ð¸Ð±Ð¾Ñ€Ñƒ зони"
msgid "Select projects you want to import."
-msgstr ""
+msgstr "Виберіть проекти, Ñкі ви хочете імпортувати."
msgid "Select source branch"
msgstr "Виберіть гілку-джерело"
@@ -5677,15 +6789,24 @@ msgstr "Виберіть гілку-джерело"
msgid "Select target branch"
msgstr "Вибір цільової гілки"
-msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr "При виборі кориÑтувача Gitlab поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° нього буде додане до опиÑу проблем та коментарів (напр. \"<a href=\"#\"> @johnsmith</a>\"). Також це призведе до аÑоціації та/або Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ†Ð¸Ñ… проблем та коментарів на вибраного кориÑтувача."
+
msgid "Selective synchronization"
msgstr "Вибіркова ÑинхронізаціÑ"
msgid "Send email"
msgstr "ÐадіÑлати лиÑта"
+msgid "Send usage data"
+msgstr "Відправити дані про викориÑтаннÑ"
+
msgid "Sep"
msgstr "вер."
@@ -5696,7 +6817,7 @@ msgid "Server version"
msgstr "ВерÑÑ–Ñ Ñервера"
msgid "Service Desk"
-msgstr ""
+msgstr "Service Desk"
msgid "Service Templates"
msgstr "Шаблони ÑервіÑів"
@@ -5713,6 +6834,9 @@ msgstr "Ð’Ñтановіть пароль Ð´Ð»Ñ Ñвого облікового
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr "Ð’Ñтановіть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° замовчуваннÑм Ñ– обмежте рівні видимоÑÑ‚Ñ–. Ðалаштуйте джерела імпорту Ñ– протокол доÑтупу git."
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr "МакÑимальний термін дії ÑеÑÑ–Ñ— Ð´Ð»Ñ Ð²ÐµÐ±-терміналу."
@@ -5725,21 +6849,24 @@ msgstr "Ð’Ñтановіть вимоги Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ кориÑтувач
msgid "Set up CI/CD"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
-msgid "Set up Koding"
-msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr "Ðвтоматично налаштувати Ñпецифічний runner"
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr "Ðалаштуйте твердженнÑ/атрибути (email, ім'Ñ, прізвище) Ñ– NameID відповідно до %{docsLinkStart} документації %{icon}%{docsLinkEnd}"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "вÑтановити пароль"
msgid "Settings"
msgstr "ÐалаштуваннÑ"
-msgid "Setup a specific Runner automatically"
-msgstr "Ðвтоматично налаштувати Ñпецифічний runner"
-
msgid "Share"
msgstr "ПоділитиÑÑ"
@@ -5749,6 +6876,9 @@ msgstr "ПоділітьÑÑ <strong>%{sso_label}</strong> із учаÑника
msgid "Shared Runners"
msgstr "Загальні Runner'и"
+msgid "Shared projects"
+msgstr "Спільні проекти"
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr "При обнуленні хвилин конвеєрів Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проÑтору імен, кількіÑÑ‚ÑŒ вже викориÑтаних хвилин буде дорівнювати 0."
@@ -5759,7 +6889,7 @@ msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr "Обнулити викориÑтані хвилини в конвеєрі"
msgid "Sherlock Transactions"
-msgstr ""
+msgstr "Sherlock транзакції"
msgid "Show command"
msgstr "Показати команду"
@@ -5771,7 +6901,7 @@ msgid "Show latest version"
msgstr "Показати оÑтанню верÑÑ–ÑŽ"
msgid "Show latest version of the diff"
-msgstr ""
+msgstr "Показати оÑтанню верÑÑ–ÑŽ порівнÑннÑ"
msgid "Show parent pages"
msgstr "Показати батьківÑькі Ñторінки"
@@ -5780,7 +6910,7 @@ msgid "Show parent subgroups"
msgstr "Показати батьківÑькі підгрупи"
msgid "Show whitespace changes"
-msgstr ""
+msgstr "Показати зміни пробілів"
msgid "Showing %d event"
msgid_plural "Showing %d events"
@@ -5790,7 +6920,7 @@ msgstr[2] "Показано %d подій"
msgstr[3] "Показано %d подій"
msgid "Side-by-side"
-msgstr ""
+msgstr "Поруч"
msgid "Sidebar|Change weight"
msgstr "Змінити вагу"
@@ -5805,10 +6935,10 @@ msgid "Sidebar|Weight"
msgstr "Вага"
msgid "Sign in"
-msgstr ""
+msgstr "Увійти"
msgid "Sign in / Register"
-msgstr ""
+msgstr "Увійти або зареєÑтруватиÑÑ"
msgid "Sign in to %{group_name}"
msgstr "Увійти в %{group_name}"
@@ -5825,15 +6955,21 @@ msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ"
msgid "Sign-up restrictions"
msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації"
+msgid "Size"
+msgstr "Розмір"
+
msgid "Size and domain settings for static websites"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð·Ð¼Ñ–Ñ€Ñƒ та домену Ð´Ð»Ñ Ñтатичних веб-Ñайтів"
msgid "Slack application"
msgstr "заÑтоÑунок Slack"
-msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr "Повільніше, але гарантує, що робоча облаÑÑ‚ÑŒ проекту чиÑтою, оÑкільки воно клонує Ñховище з Ð½ÑƒÐ»Ñ Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ завданнÑ"
+
msgid "Snippets"
msgstr "Сніпети"
@@ -5844,7 +6980,13 @@ msgid "Something went wrong on our end."
msgstr "ЩоÑÑŒ пішло не так з нашого боку."
msgid "Something went wrong on our end. Please try again!"
-msgstr ""
+msgstr "ЩоÑÑŒ пішло не так на нашій Ñтороні. Будь-лаÑка, Ñпробуйте ще раз!"
+
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr "Помилка при зміні конфіденційноÑÑ‚Ñ– цієї проблеми"
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr "ЩоÑÑŒ пішло не так, при Ñпробі зміни Ñтану Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ %{issuableDisplayName}"
msgid "Something went wrong when toggling the button"
msgstr "Помилка при перемиканні кнопки"
@@ -5852,8 +6994,8 @@ msgstr "Помилка при перемиканні кнопки"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr "Помилка при закритті %{issuable}. Будь лаÑка, Ñпробуйте пізніше"
-msgid "Something went wrong while fetching assignees list"
-msgstr "Помилка при отриманні ÑпиÑку виконавців"
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
msgid "Something went wrong while fetching group member contributions"
msgstr "Помилка при завантаженні внеÑків учаÑників групи"
@@ -5906,6 +7048,9 @@ msgstr "Ðайбільша група"
msgid "SortOptions|Largest repository"
msgstr "Ðайбільший репозиторій"
+msgid "SortOptions|Last Contact"
+msgstr "ОÑтанній контакт"
+
msgid "SortOptions|Last created"
msgstr "ОÑтанній Ñтворений"
@@ -5936,6 +7081,9 @@ msgstr "Більша вага"
msgid "SortOptions|Most popular"
msgstr "Ðайбільш популÑрний"
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr "Ім'Ñ"
@@ -5966,6 +7114,9 @@ msgstr "Пріоритет"
msgid "SortOptions|Recent sign in"
msgstr "Ðещодавно зареєÑтровані"
+msgid "SortOptions|Start date"
+msgstr "Дата початку"
+
msgid "SortOptions|Start later"
msgstr "Розпочатий пізніше"
@@ -5996,6 +7147,9 @@ msgstr "ЗахиÑÑ‚ від Ñпаму Ñ– ботів"
msgid "Specific Runners"
msgstr "Спеціальні Runner’и"
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "Зазначте наÑтупний URL під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Runner-а:"
@@ -6006,7 +7160,7 @@ msgid "Stage"
msgstr "СтадіÑ"
msgid "Stage & Commit"
-msgstr ""
+msgstr "ПроіндекÑувати та закомітити"
msgid "Stage all changes"
msgstr "ПроіндекÑувати вÑÑ– зміни"
@@ -6038,6 +7192,9 @@ msgstr "Обрані проекти"
msgid "Start a %{new_merge_request} with these changes"
msgstr "Почати %{new_merge_request} з цими змінами"
+msgid "Start date"
+msgstr "Дата початку"
+
msgid "Start the Runner!"
msgstr "ЗапуÑÑ‚Ñ–Ñ‚ÑŒ Runner!"
@@ -6054,7 +7211,7 @@ msgid "Status"
msgstr "СтатуÑ"
msgid "Stop impersonation"
-msgstr ""
+msgstr "Зупинити уоÑобленнÑ"
msgid "Stop this environment"
msgstr "Зупинити це Ñередовище"
@@ -6066,16 +7223,19 @@ msgid "Storage"
msgstr "Сховище"
msgid "Storage:"
-msgstr ""
+msgstr "Сховище:"
msgid "Subgroups"
msgstr "Підгрупи"
-msgid "Submit as spam"
+msgid "Subgroups and projects"
msgstr ""
+msgid "Submit as spam"
+msgstr "Позначити Ñк Ñпам"
+
msgid "Submit search"
-msgstr ""
+msgstr "ÐадіÑлати пошук"
msgid "Subscribe"
msgstr "ПідпиÑатиÑÑ"
@@ -6096,7 +7256,7 @@ msgid "System Hooks"
msgstr "СиÑтемні гуки"
msgid "System Info"
-msgstr ""
+msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ÑиÑтему"
msgid "System header and footer:"
msgstr "Заголовок Ñ– футер ÑиÑтеми:"
@@ -6104,6 +7264,9 @@ msgstr "Заголовок Ñ– футер ÑиÑтеми:"
msgid "System metrics (Custom)"
msgstr "СиÑтемні метрики (ВлаÑні)"
+msgid "System metrics (Kubernetes)"
+msgstr "СиÑтемні метрики (Kubernetes)"
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] "Тег (%{tag_count})"
@@ -6114,6 +7277,9 @@ msgstr[3] "Тегів (%{tag_count})"
msgid "Tags"
msgstr "Теги"
+msgid "Tags feed"
+msgstr "Канал тегів"
+
msgid "Tags:"
msgstr "Теги:"
@@ -6195,6 +7361,12 @@ msgstr "Цільова гілка"
msgid "Team"
msgstr "Команда"
+msgid "Template"
+msgstr "Шаблон"
+
+msgid "Templates"
+msgstr "Шаблони"
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr "Угода про Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг Ñ– політика конфіденційноÑÑ‚Ñ–"
@@ -6202,12 +7374,15 @@ msgid "Terms of Service and Privacy Policy"
msgstr "Правилами кориÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑервіÑом Ñ– політика конфіденційноÑÑ‚Ñ–"
msgid "Test coverage parsing"
-msgstr ""
+msgstr "Пошук результатів Ð¿Ð¾ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ‚ÐµÑтами"
msgid "Thanks! Don't show me this again"
msgstr "ДÑкую! Більше не показувати це повідомленнÑ"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr "Розширений глобальний пошук в GitLab — це потужний інÑтрумент Ñкий заощаджує ваш чаÑ. ЗаміÑÑ‚ÑŒ Ð´ÑƒÐ±Ð»ÑŽÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ Ñ– витрати чаÑу, ви можете шукати код інших команд, Ñкий може допомогти у вашому проекті."
+
+msgid "The Git LFS objects will <strong>not</strong> be synced."
msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
@@ -6217,6 +7392,9 @@ msgid "The Issue Tracker is the place to add things that need to be improved or
msgstr "Трекер проблем — це міÑце, де можна додати речі, Ñкі потрібно покращити або розв’Ñзати в проекті. Ви можете зареєÑтруватиÑÑ Ð°Ð±Ð¾ увійти, щоб Ñтворювати проблеми в цьому проекті."
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgstr "Сертифікат X509 викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð²Ð·Ð°Ñ”Ð¼Ð½Ð¾Ñ— перевірки автентичноÑÑ‚Ñ– TLS Ñ– необхідний Ð´Ð»Ñ Ð·Ð²'Ñзку з зовнішньою Ñлужбою авторизації. Якщо залишити порожнім, Ñертифікат Ñервера буде перевірÑтиÑÑŒ при доÑтупі через HTTPS."
+
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -6226,6 +7404,9 @@ msgid "The collection of events added to the data gathered for that stage."
msgstr "ÐšÐ¾Ð»ÐµÐºÑ†Ñ–Ñ Ð¿Ð¾Ð´Ñ–Ð¹ додана до даних, зібраних Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr "Ð—â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ припинено піÑÐ»Ñ %{timeout}. Ð”Ð»Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð², Ñким потрібно більше чаÑу, викориÑтовуйте комбінацію clone/push."
+
+msgid "The deployment of this job to %{environmentLink} did not succeed."
msgstr ""
msgid "The fork relationship has been removed."
@@ -6244,22 +7425,25 @@ msgid "The number of attempts GitLab will make to access a storage."
msgstr "КількіÑÑ‚ÑŒ Ñпроб, Ñкі зробить GitLab Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу до Ñховища даних."
msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr ""
+msgstr "КількіÑÑ‚ÑŒ збоїв піÑÐ»Ñ Ñ‡Ð¾Ð³Ð¾ Gitlab повніÑÑ‚ÑŽ заблокує доÑтуп до Ñховища данних. Лічильник кількоÑÑ‚Ñ– збоїв може бути Ñкинутий в інтерфейÑÑ– адмініÑтратора %{link_to_health_page}, або через %{api_documentation_link}."
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
-msgstr ""
+msgstr "Пароль, Ñкий потрібен Ð´Ð»Ñ Ð´ÐµÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð²Ð°Ñ‚Ð½Ð¾Ð³Ð¾ ключа. Він Ñ” необов’Ñзковим Ñ– зберігаєтьÑÑ Ñƒ зашифрованому виглÑді."
msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
-msgstr ""
+msgstr "ШлÑÑ… до кофігураційного файлу CI. За замовчуваннÑм — <code>.gitlab-ci.yml</code>"
msgid "The phase of the development lifecycle."
msgstr "Фаза життєвого циклу розробки."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr "Розклад конвеєрів запуÑкає Ñ—Ñ… в майбутньому, Ð´Ð»Ñ Ð¿ÐµÐ²Ð½Ð¸Ñ… гілок або тегів. Заплановані конвеєри уÑпадковують Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð½Ð° доÑтуп до проекту на оÑнові пов'Ñзаного з ними кориÑтувача."
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ ÐŸÐ»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶Ð°Ñ” Ñ‡Ð°Ñ Ð²Ñ–Ð´ попереднього кроку до першого коміту. ДодаєтьÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾, Ñк тільки відправитьÑÑ Ð¿ÐµÑ€ÑˆÐ¸Ð¹ коміт."
msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
-msgstr ""
+msgstr "Приватний ключ, Ñкий викориÑтовуєтьÑÑ Ð¿Ñ€Ð¸ наданні клієнтÑького Ñертифіката. Його Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¾."
msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Production показує загальний Ñ‡Ð°Ñ Ð¼Ñ–Ð¶ ÑтвореннÑм проблеми та розгортаннÑм коду у production. Дані будуть автоматично додані піÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿Ð¾Ð²Ð½Ð¾Ñ— ідеї до production циклу."
@@ -6271,7 +7455,7 @@ msgid "The project can be accessed without any authentication."
msgstr "ДоÑтуп до проекту можливий без будь-Ñкої перевірки автентичноÑÑ‚Ñ–."
msgid "The pseudonymizer data collection is disabled. When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
-msgstr ""
+msgstr "Збір даних пÑевдонімізованих данних вимкнено. Коли його буде увімкнено GitLab запуÑтить фонове завданнÑ, Ñке буде Ñтворювати пÑевдонімізовані CSV-файли бази даних GitLab, Ñкі будуть надÑилатиÑÑ Ð´Ð¾ вашого налаштованого Ñховища об'єктів."
msgid "The repository for this project does not exist."
msgstr "Репозиторій Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту не Ñ–Ñнує."
@@ -6282,6 +7466,9 @@ msgstr "Репозиторій цього проекту порожній"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr "Репозиторій має бути доÑтупним через <code>http://</code>, <code>https://</code> або <code>git://</code>."
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾ÐºÐ°Ð·ÑƒÑ” Ñ‡Ð°Ñ Ð²Ñ–Ð´ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ про об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð´Ð¾ його виконаннÑ. Дані будуть автоматично додані піÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ запиту на злиттÑ."
@@ -6289,11 +7476,14 @@ msgid "The roadmap shows the progress of your epics along a timeline"
msgstr "План-графік показує Ñтан ваших епіків у чаÑÑ–"
msgid "The secure token used by the Runner to checkout the project"
-msgstr ""
+msgstr "Секретний токен, Ñкий викориÑтовує runner Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, що отримати код проекту"
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Staging показує Ñ‡Ð°Ñ Ð¼Ñ–Ð¶ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° розгортаннÑм коду у production. Дані автоматично додаютьÑÑ Ð¿Ñ–ÑÐ»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñƒ production вперше."
+msgid "The tabs below will be removed in a future version"
+msgstr "Вкладки нижче будуть видалені в майбутній верÑÑ–Ñ—"
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Ð¢ÐµÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ÐºÐ°Ð·ÑƒÑ” чаÑ, Ñкий GitLab CI витрачає Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ конвеєра Ð´Ð»Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð¾Ð³Ð¾ запиту злиттÑ. Дані будуть автоматично додані піÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ конвеєра."
@@ -6304,32 +7494,56 @@ msgid "The time in seconds GitLab will try to access storage. After this time a
msgstr "КількіÑÑ‚ÑŒ Ñекунд, протÑгом Ñкої GitLab намагатиметьÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ доÑтуп до Ñховища даних. По завершенню цього періоду буде згенерована помилка про Ð¿ÐµÑ€ÐµÐ²Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð»Ñ–Ð¼Ñ–Ñ‚Ñƒ чаÑу."
msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
-msgstr ""
+msgstr "Ð§Ð°Ñ Ñƒ Ñекундах між перевірками Ñховища. Якщо Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ° ще на завершена, GitLab пропуÑтить наÑтупну."
msgid "The time taken by each data entry gathered by that stage."
msgstr "ЧаÑ, витрачений на кожен елемент, зібраний на цій Ñтадії."
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr ""
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr "Мапа кориÑтувачів — це JSON-документ, Ñкий задає Ñк адреÑи електронної пошти та імена кориÑтувачів Google Code, що приймали учаÑÑ‚ÑŒ у ваших проектах будуть імпортовані у GitLab. Ви можете змінити його шлÑхом зміни значень, що ÑтоÑÑ‚ÑŒ Ñправа від <code>:</code>. Ðе видалÑйте лапки та інші знаки пунктуації, а також не змінюйте адреÑи електронної пошти чи імена кориÑтувачів зліва."
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr "Мапа кориÑтувачів — це правила Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів FogBugz, Ñкі приймали учаÑÑ‚ÑŒ у ваших проектах до Gitlab (зокрема Ñ—Ñ… імен та Ð°Ð´Ñ€ÐµÑ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти). Ви можете вноÑити зміни шлÑхом Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– нижче."
+
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "Середнє Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð² Ñ€Ñдку. Приклад: між 3, 5, 9, Ñередніми 5, між 3, 5, 7, 8, Ñередніми (5 + 7) / 2 = 6."
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr "Ðемає проблем Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
msgid "There are no labels yet"
-msgstr ""
+msgstr "Тут ще немає міток"
msgid "There are no merge requests to show"
msgstr "Ðемає запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr "Ðемає індекÑованих змін"
+
+msgid "There are no unstaged changes"
+msgstr "Ðемає неіндекÑованих змін"
+
msgid "There are problems accessing Git storage: "
msgstr "Є проблеми з доÑтупом до Ñховища git: "
+msgid "There was an error adding a todo."
+msgstr "Помилка при додаванні задачі."
+
+msgid "There was an error deleting the todo."
+msgstr "Помилка при видаленні задачі."
+
msgid "There was an error loading users activity calendar."
msgstr "Помилка при завантаженні ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾ÑÑ‚Ñ– кориÑтувачів."
@@ -6352,31 +7566,43 @@ msgid "They can be managed using the %{link}."
msgstr "Ðими можна керувати за допомогою %{link}."
msgid "Third party offers"
-msgstr ""
+msgstr "Сторонні пропозиції"
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr "Цей інÑÑ‚Ð°Ð½Ñ GitLab ще немає загальних Runner'ів. ÐдмініÑтратори можуть Ñ—Ñ… зареєÑтрувати у Ñпеціальному розділі конфігурації."
msgid "This application was created by %{link_to_owner}."
-msgstr ""
+msgstr "Цей заÑтоÑунок було Ñтворено %{link_to_owner}."
msgid "This application will be able to:"
-msgstr ""
+msgstr "Ð¦Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð° зможе:"
msgid "This board's scope is reduced"
+msgstr "ВидиміÑÑ‚ÑŒ цієї дошки обмежена"
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr "Ð¦Ñ Ð³Ñ–Ð»ÐºÐ° була змінена піÑÐ»Ñ Ñ‚Ð¾Ð³Ð¾ моменту, коли ви почали Ñ—Ñ— редагувати. Ви хотіли б Ñтворити нову?"
+
+msgid "This container registry has been scheduled for deletion."
msgstr ""
-msgid "This diff is collapsed."
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
msgstr ""
+msgid "This diff is collapsed."
+msgstr "Це порівнÑÐ½Ð½Ñ Ð·Ð³Ð¾Ñ€Ð½ÑƒÑ‚Ð¾."
+
msgid "This directory"
msgstr "Цей каталог"
msgid "This group"
-msgstr ""
+msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð°"
msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
-msgstr ""
+msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð° дозволÑÑ” вхід через обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ”Ð´Ð¸Ð½Ð¾Ð³Ð¾ входу %{group_name}. Ви будете перенаправлені на зовнішню Ñторінку авторизації."
msgid "This group does not provide any group Runners yet."
msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð° ще не має жодного групового Runner’а."
@@ -6417,9 +7643,30 @@ msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñ‰Ðµ не було запущене"
msgid "This job has not started yet"
msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñ‰Ðµ не запуÑтилоÑÑ"
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ±ÑƒÐ²Ð°Ñ” в Ñтані Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñ– чекає на запуÑк Runner"
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼Ð°Ð³Ð°Ñ” ручних дій"
@@ -6429,6 +7676,9 @@ msgstr "Це означає, що ви не можете відправлÑти
msgid "This merge request is locked."
msgstr "Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾."
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr "Ð¦Ñ Ð¾Ð¿Ñ†Ñ–Ñ Ð½ÐµÐ´Ð¾Ñтупна, поки у Ð²Ð°Ñ Ñ” неіндекÑовані зміни"
@@ -6444,20 +7694,35 @@ msgstr "Цей проект"
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr "Цей проект не входить до жодної групи Ñ– тому не може викориÑтовувати групові Runner’и."
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr "Цей репозиторій"
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
+msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð·Ð¸Ñ‚Ð¸ порівнÑннÑ, оÑкільки воно завелике."
+
+msgid "This timeout will take precedence when lower than Project-defined timeout"
msgstr ""
msgid "This user has no identities"
+msgstr "Цей кориÑтувач не має ідентифікацій"
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
-msgid "This will delete the custom metric, Are you sure?"
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
msgstr ""
+msgid "This will delete the custom metric, Are you sure?"
+msgstr "Це призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ð»Ð°Ñної метрики, ви впевнені?"
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr ""
+msgstr "Ці Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти автоматично Ñтануть обговореннÑми проблем, Ñкі відображатимутьÑÑ Ñ‚ÑƒÑ‚ (причому коментарі Ñтануть чаÑтиною перепиÑки)."
msgid "Time before an issue gets scheduled"
msgstr "Ð§Ð°Ñ Ð´Ð¾ початку потраплÑÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ в планувальник"
@@ -6469,7 +7734,7 @@ msgid "Time between merge request creation and merge/close"
msgstr "Ð§Ð°Ñ Ð¼Ñ–Ð¶ ÑтвореннÑм запиту Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– його виконаннÑм або закриттÑм"
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
-msgstr ""
+msgstr "Ð§Ð°Ñ Ð² Ñекундах, протÑгом Ñкого GitLab чекатиме відповіді від зовнішньої Ñлужби. Якщо вона не відповіÑÑ‚ÑŒ вчаÑно, доÑтуп буде заборонений."
msgid "Time remaining"
msgstr "ЗалишилоÑÑ Ñ‡Ð°Ñу"
@@ -6642,13 +7907,13 @@ msgid "Tip:"
msgstr "Порада:"
msgid "Title"
-msgstr ""
+msgstr "Заголовок"
msgid "To GitLab"
msgstr "Ð’ GitLab"
msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб додати ключ SSH вам необхідно %{generate_link_start}згенерувати його%{link_end} або викориÑтати %{existing_link_start}Ñ–Ñнуючий ключ%{link_end}."
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr "Ð”Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð² з GitHub, ви можете викориÑтовувати %{personal_access_token_link}. Коли ви Ñтворюватимете ваш перÑональний токен доÑтупу, вам потрібно буде вибрати облаÑÑ‚ÑŒ дії <code>repo</code>, щоб ми могли відобразити ÑпиÑок ваших публічних та приватних репозиторіїв, доÑтупних Ð´Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ."
@@ -6659,10 +7924,22 @@ msgstr "Ð”Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð² з GitHub, ви Ñ
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr "Ð”Ð»Ñ Ð¿Ñ€Ð¸Ñ”Ð´Ð½Ð°Ð½Ð½Ñ SVN-репозиторію, переглÑньте %{svn_link}."
-msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgid "To define internal users, first enable new users set to external"
msgstr ""
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr "Ð”Ð»Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ введіть URL-адреÑу FogBugz та параметри входу нижче. Далі ви зможете перенеÑти кориÑтувачів та вибрати проекти Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ."
+
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr "Спочатку введіть адреÑу Ñервера GÑ–tea Ñ– %{link_to_personal_token}."
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
msgstr ""
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
@@ -6675,7 +7952,7 @@ msgid "To import an SVN repository, check out %{svn_link}."
msgstr "Ð”Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ SVN-репозиторію, переглÑньте %{svn_link}."
msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
-msgstr ""
+msgstr "Щоб переміÑтити або Ñкопіювати веÑÑŒ проект GitLab з іншої інÑталÑції GitLab до цього, перейдіть на Ñторінку налаштувань оригіналу проекту, Ñтворіть файл екÑпорту та надішліть його Ñюди."
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr "Щоб викориÑтовувати лише функції CI/CD Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ репозиторію, виберіть <strong>CI/CD Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ репозиторію</strong>."
@@ -6687,13 +7964,13 @@ msgid "To start serving your jobs you can add Runners to your group"
msgstr "Ð”Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¸Ñ… завдань ви можете додати Runner’и до вашої групи"
msgid "To this GitLab instance"
-msgstr ""
+msgstr "До цього інÑтанÑу GitLab"
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr "Щоб перевірити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ GitLab CI, перейдіть до \"CI / CD → Конвеєри\" у вашому проекті та натиÑніть кнопку \"Перевірка конфігурації (CI Lint)\"."
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
-msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку додайте заплановані дати початку та Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків у цій групі або Ñ—Ñ— підгрупах. ВідображаютьÑÑ Ð»Ð¸ÑˆÐµ епіки за попередні та наÑтупні 3 міÑÑці."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
msgid "To widen your search, change or remove filters."
msgstr "Щоб розширити пошук, змініть або видаліть фільтри."
@@ -6702,16 +7979,19 @@ msgid "Todo"
msgstr "Задача"
msgid "Todos"
-msgstr ""
+msgstr "Задачі"
msgid "Toggle Sidebar"
msgstr "Перемикач бічної панелі"
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr "Перемикач диÑкуÑÑ–Ñ—"
msgid "Toggle navigation"
-msgstr ""
+msgstr "Переключити навігацію"
msgid "Toggle sidebar"
msgstr "Перемикач бічної панелі"
@@ -6722,8 +8002,11 @@ msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ñ‡Ð°: ВИМКÐЕÐО"
msgid "ToggleButton|Toggle Status: ON"
msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ñ‡Ð°: УВІМКÐЕÐО"
+msgid "Token"
+msgstr "Токен"
+
msgid "Too many changes to show."
-msgstr ""
+msgstr "Забагато змін Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ."
msgid "Total Contributions"
msgstr "Загальна кількіÑÑ‚ÑŒ внеÑків"
@@ -6747,13 +8030,22 @@ msgid "Track time with quick actions"
msgstr "ВідÑтежуйте Ñ‡Ð°Ñ Ð·Ð° допомогою швидких дій"
msgid "Trending"
+msgstr "ПопулÑрні"
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
msgstr ""
msgid "Trigger this manual action"
msgstr "ЗапуÑтити цю ручну дію"
msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
-msgstr ""
+msgstr "Тригери можуть примуÑово перезібрати гілку або тег через API. Ці токени дозволÑÑŽÑ‚ÑŒ діÑти від імені кориÑтувача: в тому чиÑлі отримувати доÑтуп до його проектів та проектних прав доÑтупу."
msgid "Try again"
msgstr "Спробуйте ще раз"
@@ -6762,7 +8054,10 @@ msgid "Turn on Service Desk"
msgstr "Ввімкнути Service Desk"
msgid "Twitter"
-msgstr ""
+msgstr "Twitter"
+
+msgid "Type"
+msgstr "Тип"
msgid "Unable to load the diff. %{button_try_again}"
msgstr "Ðеможливо завантажити порівнÑÐ½Ð½Ñ (diff). %{button_try_again}"
@@ -6770,18 +8065,30 @@ msgstr "Ðеможливо завантажити порівнÑÐ½Ð½Ñ (diff). %
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr "Ðеможливо увійти до групи за допомогою SAML через \"%{reason}\""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr "СкаÑувати"
+
msgid "Unknown"
msgstr "Ðевідомо"
msgid "Unlock"
msgstr "Розблокувати"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr "Розблокувати %{issuableDisplayName}? <strong>Будь-хто</strong> зможе залишати коментарі."
+
msgid "Unlocked"
msgstr "Розблоковано"
msgid "Unresolve discussion"
msgstr "Повторно відкрити обговореннÑ"
+msgid "Unstage"
+msgstr "Ðе індекÑувати"
+
msgid "Unstage all changes"
msgstr "Ðе індекÑувати вÑÑ– зміни"
@@ -6816,11 +8123,17 @@ msgid "Up to date"
msgstr "Ðктуальний"
msgid "Update"
-msgstr ""
+msgstr "Оновити"
+
+msgid "Update now"
+msgstr "Оновити зараз"
msgid "Update your group name, description, avatar, and other general settings."
msgstr "Оновіть Ñ–Ð¼â€™Ñ Ð³Ñ€ÑƒÐ¿Ð¸, опиÑ, аватар та інші загальні налаштуваннÑ."
+msgid "Updating"
+msgstr "ОновленнÑ"
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "Перейдіть на вищий тарифний план щоб активувати Покращений Глобальний Пошук."
@@ -6837,28 +8150,28 @@ msgid "Upgrade your plan to improve Issue boards."
msgstr "Перейдіть на вищий тарифний план щоб покращити дошки обговорень."
msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
-msgstr ""
+msgstr "ÐадіÑлати <code>GoogleCodeProjectHosting.json</code> тут:"
msgid "Upload New File"
-msgstr "Завантажити новий файл"
+msgstr "ÐадіÑлати новий файл"
msgid "Upload file"
-msgstr "Завантажити файл"
-
-msgid "Upload new avatar"
-msgstr "Завантажити новий аватар"
+msgstr "ÐадіÑлати файл"
msgid "UploadLink|click to upload"
-msgstr "ÐатиÑніть, щоб завантажити"
+msgstr "ÐатиÑніть, щоб надіÑлати"
msgid "Upvotes"
msgstr "Лайки"
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr "СтатиÑтика викориÑтаннÑ"
msgid "Use <code>%{native_redirect_uri}</code> for local tests"
-msgstr ""
+msgstr "ВикориÑтовувати <code>%{native_redirect_uri}</code> Ð´Ð»Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¸Ñ… теÑтів"
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr "ВикориÑтовуйте Service Desk Ð´Ð»Ñ Ð·Ð²â€™Ñзку з вашими кориÑтувачами (наприклад, щоб запропонувати клієнтÑьку підтримку) через електронну пошту безпоÑередньо із GitLab"
@@ -6867,7 +8180,10 @@ msgid "Use group milestones to manage issues from multiple projects in the same
msgstr "ВикориÑтовуйте групові етапи, щоб керувати у одному етапі проблеми з різних проектів."
msgid "Use one line per URI"
-msgstr ""
+msgstr "ВикориÑтовуйте один Ñ€Ñдок Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ URI"
+
+msgid "Use template"
+msgstr "ВикориÑтовувати шаблон"
msgid "Use the following registration token during setup:"
msgstr "ВикориÑтовувати токен під Ñ‡Ð°Ñ ÑƒÑтановки:"
@@ -6878,6 +8194,9 @@ msgstr "ВикориÑтовуютьÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ– налаштуван
msgid "Used by members to sign in to your group in GitLab"
msgstr "ВикориÑтовуєтьÑÑ ÑƒÑ‡Ð°Ñниками Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ у вашу групу в GitLab"
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
@@ -6885,7 +8204,7 @@ msgid "User and IP Rate Limits"
msgstr "Ліміти чаÑтоти Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів та IP"
msgid "User map"
-msgstr ""
+msgstr "Мапа кориÑтувачів"
msgid "Users"
msgstr "КориÑтувачі"
@@ -6911,6 +8230,9 @@ msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ перевірку"
msgid "Verified"
msgstr "Підтверджено"
+msgid "Version"
+msgstr "ВерÑÑ–Ñ"
+
msgid "View epics list"
msgstr "ПереглÑнути ÑпиÑок епіків"
@@ -6924,7 +8246,7 @@ msgid "View issue"
msgstr "ПереглÑнути проблему"
msgid "View it on GitLab"
-msgstr ""
+msgstr "ПереглÑнути це на GitLab"
msgid "View jobs"
msgstr "ПереглÑнути завданнÑ"
@@ -6948,10 +8270,10 @@ msgid "Visibility and access controls"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð¸Ð¼Ð¾ÑÑ‚Ñ– та доÑтупу"
msgid "Visibility level:"
-msgstr ""
+msgstr "Рівень видимоÑÑ‚Ñ–:"
msgid "Visibility:"
-msgstr ""
+msgstr "ВидиміÑÑ‚ÑŒ:"
msgid "VisibilityLevel|Internal"
msgstr "Внутрішній"
@@ -6969,7 +8291,7 @@ msgid "Want to see the data? Please ask an administrator for access."
msgstr "Хочете побачити дані? Будь лаÑка, попроÑить у адмініÑтратора доÑтуп."
msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
-msgstr ""
+msgstr "Ми виÑвили потенційний Ñпам у %{humanized_resource_name}. Будь лаÑка, введіть цей код Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ reCAPTCHA, щоб продовжити."
msgid "We don't have enough data to show this stage."
msgstr "Ми не маємо доÑтатньо даних Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
@@ -7005,10 +8327,10 @@ msgid "When leaving the URL blank, classification labels can still be specified
msgstr "Якщо залишити URL порожнім, можна вÑтановлювати мітки клаÑифікації без Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ð¹ проекту та Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ñ— авторизації."
msgid "Wiki"
-msgstr "Wiki"
+msgstr "Вікі"
msgid "WikiClone|Clone your wiki"
-msgstr "Клонувати ваш wiki"
+msgstr "Клонувати ваш вікі"
msgid "WikiClone|Git Access"
msgstr "Git доÑтуп"
@@ -7029,31 +8351,31 @@ msgid "WikiEdit|There is already a page with the same title in that path."
msgstr "Вже Ñ–Ñнує Ñторінка з таким шлÑхом Ñ– заголовком."
msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
-msgstr "Запропонувати Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ wiki"
+msgstr "Запропонувати Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÑ–"
msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
-msgstr "Ви маєте бути учаÑником проекту Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб додавати wiki Ñторінки. Якщо у Ð²Ð°Ñ Ñ” пропозиції ÑтоÑовно Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ wiki цього проекту, відкрийте проблему в %{issues_link}."
+msgstr "Ви маєте бути учаÑником проекту Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб додавати вікі Ñторінки. Якщо у Ð²Ð°Ñ Ñ” пропозиції ÑтоÑовно Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÑ– цього проекту, відкрийте проблему в %{issues_link}."
msgid "WikiEmptyIssueMessage|issue tracker"
msgstr "РеєÑÑ‚Ñ€ проблем"
msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
-msgstr "Wiki дозволÑÑ” зберігати інформацію про ваш проект. Ðаприклад причину ÑтвореннÑ, принципи, Ñк його викориÑтовувати Ñ– Ñ‚. д."
+msgstr "Вікі дозволÑÑ” зберігати інформацію про ваш проект. Ðаприклад причину ÑтвореннÑ, принципи, Ñк його викориÑтовувати Ñ– Ñ‚. д."
msgid "WikiEmpty|Create your first page"
msgstr "Створіть вашу першу Ñторінку"
msgid "WikiEmpty|Suggest wiki improvement"
-msgstr "Запропонувати Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ wiki"
+msgstr "Запропонувати Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÑ–"
msgid "WikiEmpty|The wiki lets you write documentation for your project"
-msgstr "Wiki дозволÑÑŽÑ‚ÑŒ пиÑати документацію Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проекту"
+msgstr "Вікі дозволÑÑŽÑ‚ÑŒ пиÑати документацію Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проекту"
msgid "WikiEmpty|This project has no wiki pages"
-msgstr "Цей проект не має wiki Ñторінок"
+msgstr "Цей проект не має вікі Ñторінок"
msgid "WikiEmpty|You must be a project member in order to add wiki pages."
-msgstr "Ви повинні бути учаÑником проекту Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб додавати wiki Ñторінки."
+msgstr "Ви повинні бути учаÑником проекту Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб додавати вікі Ñторінки."
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "Це — Ñтара верÑÑ–Ñ Ñторінки."
@@ -7083,7 +8405,7 @@ msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We wi
msgstr "Порада: можна вказати повний шлÑÑ… до нового файлу. Ми автоматично Ñтворимо вÑÑ– відÑутні каталоги."
msgid "WikiNewPageTitle|New Wiki Page"
-msgstr "Ðова wiki-Ñторінка"
+msgstr "Ðова вікі-Ñторінка"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr "Ви дійÑно бажаєте видалити цю Ñторінку?"
@@ -7137,7 +8459,7 @@ msgid "Wiki|Pages"
msgstr "Сторінки"
msgid "Wiki|Wiki Pages"
-msgstr "Wiki-Ñторінки"
+msgstr "Вікі-Ñторінки"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
msgstr "З аналітикою контриб’юторів ви може вивчати активніÑÑ‚ÑŒ в обговореннÑÑ…, запитах на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– подій відправки коду Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— організації Ñ– Ñ—Ñ— учаÑників."
@@ -7149,13 +8471,13 @@ msgid "Yes"
msgstr "Так"
msgid "Yes, add it"
-msgstr ""
+msgstr "Так, додати це"
msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
+msgstr "Так, дозволити мені зв’Ñзати кориÑтувачів Google Code із повними іменами кориÑтувачів GitLab."
msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
-msgstr ""
+msgstr "Ви — адмініÑтратор, а це означає, що Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу Ð´Ð»Ñ <strong>%{client_name}</strong> дозволить їм взаємодіÑти з GitLab Ñк адмініÑтратору. Продовжуйте обережно."
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "Ви хочете видалити %{group_name}. Видалені групи ÐЕ МОЖÐРбуду відновити! Ви ÐБСОЛЮТÐО впевнені?"
@@ -7176,7 +8498,7 @@ msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make ch
msgstr "Ви знаходитеÑÑŒ на вторинному <b>лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ</b> Geo-вузлі. Якщо ви хочете внеÑти будь-Ñкі зміни, ви повинні відвідати %{primary_node}."
msgid "You can %{linkStart}view the blob%{linkEnd} instead."
-msgstr ""
+msgstr "ЗаміÑÑ‚ÑŒ цього ви можете %{linkStart}переглÑнути бінарні дані%{linkEnd}."
msgid "You can also create a project from the command line."
msgstr "Ви також можете Ñтворити проект із командного Ñ€Ñдка."
@@ -7188,7 +8510,7 @@ msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
msgstr "Ви також можете перевірити Ñвій .gitlab-ci.yml за допомогою %{linkStart}Lint%{linkEnd}"
msgid "You can easily contribute to them by requesting to join these groups."
-msgstr ""
+msgstr "Ви можете легко робити внеÑки до них, запроÑивши доÑтуп до цих груп."
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr "Ви можете легко вÑтановити Runner на клаÑтері Kubernetes. %{link_to_help_page}"
@@ -7202,33 +8524,33 @@ msgstr "Ви можете додавати файли тільки коли пе
msgid "You can only edit files when you are on a branch"
msgstr "Ви можете редагувати файли, лише перебуваючи у ÑкійÑÑŒ гілці"
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr "Ви можете розв’Ñзати цей конфлікт Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð° допомогою інтерактивного режиму (викориÑтовуючи кнопки %{use_ours} та %{use_theirs}), або безпоÑередньо редагуючи файли. Закомітити зміни у %{branch_name}"
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr "Ви не можете запиÑувати на вторинні інÑтанÑи \"тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ\" GitLab Geo. Будь лаÑка викориÑтовуйте %{link_to_primary_node}."
msgid "You cannot write to this read-only GitLab instance."
msgstr "Ви не можете запиÑувати на цей \"тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ\" інÑÑ‚Ð°Ð½Ñ GitLab."
-msgid "You do not have any assigned merge requests"
-msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” призначених запитів на злиттÑ"
-
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” необхідних прав доÑтупу, щоб перевизначити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñинхронізації LDAP-груп."
msgid "You don't have any applications"
-msgstr ""
+msgstr "Ви не маєте ніÑких заÑтоÑунків"
msgid "You don't have any authorized applications"
-msgstr ""
+msgstr "Ви не маєте ніÑких авторизованих заÑтоÑунків"
msgid "You have no permissions"
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” прав доÑтупу"
-msgid "You have not created any merge requests"
-msgstr "Ви ще не Ñтворювали запитів на злиттÑ"
-
msgid "You have reached your project limit"
msgstr "Ви доÑÑгли Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð² вашому проекті"
@@ -7238,18 +8560,21 @@ msgstr "Ви повинні прийнÑти правила кориÑтуван
msgid "You must have maintainer access to force delete a lock"
msgstr "Ви повинні мати доÑтуп керівника Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÑƒÑового Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ "
-msgid "You must sign in to star a project"
-msgstr "Ðеобхідно увійти, щоб оцінити проект"
-
msgid "You need a different license to enable FileLocks feature"
msgstr "Ð”Ð»Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ— функції Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¤Ð°Ð¹Ð»Ñ–Ð² вам потрібна інша ліцензіÑ"
msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
-msgstr ""
+msgstr "Вам потрібна верÑÑ–Ñ git-lfs верÑÑ–Ñ— %{min_git_lfs_version} (або новіша), щоб продовжити. Будь лаÑка, відвідайте Ñторінку https://git-lfs.github.com"
msgid "You need permission."
msgstr "Вам потрібен дозвіл"
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "Ви не отримаєте ніÑких повідомлень по електронній пошті"
@@ -7278,16 +8603,16 @@ msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Вам необхідно викориÑтовувати різні імена гілок Ð´Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ð³Ð¾ порівнÑннÑ."
msgid "You're receiving this email because %{reason}."
-msgstr ""
+msgstr "Ви отримали цей електронний лиÑÑ‚, оÑкільки %{reason}."
msgid "You're receiving this email because of your account on %{host}."
-msgstr ""
+msgstr "Ви отримали цей електронний лиÑÑ‚, оÑкільки ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ€Ð¾Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð¸Ð¹ на %{host}."
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr "Ви отримали це Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð° %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgid "YouTube"
-msgstr ""
+msgstr "YouTube"
msgid "Your Groups"
msgstr "Ваші групи"
@@ -7305,10 +8630,10 @@ msgid "Your Todos"
msgstr "Ваші Задачі"
msgid "Your applications (%{size})"
-msgstr ""
+msgstr "Ваші заÑтоÑунки (%{size})"
msgid "Your authorized applications"
-msgstr ""
+msgstr "Ваші авторизовані заÑтоÑунки"
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr "Ваші зміни можуть бути закомічені до %{branch_name}, оÑкільки запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸Ð¹."
@@ -7328,19 +8653,15 @@ msgstr "Ваше ім'Ñ"
msgid "Your projects"
msgstr "Ваші проекти"
+msgid "a deleted user"
+msgstr "видалений кориÑтувач"
+
msgid "ago"
msgstr "тому"
msgid "among other things"
msgstr "тощо"
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] "та 1 виправлена вразливіÑÑ‚ÑŒ"
-msgstr[1] "Ñ– %d виправлених вразливоÑтей"
-msgstr[2] "Ñ– %d виправлених вразливоÑтей"
-msgstr[3] "Ñ– %d виправлених вразливоÑтей"
-
msgid "assign yourself"
msgstr "призначити Ñебе"
@@ -7365,29 +8686,66 @@ msgstr "%{linkStartTag}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про SAST%{linkEndTag
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr "%{vulnerability} впливає на %{namespace}."
-msgid "ciReport|%{packagesString} and "
-msgstr "%{packagesString} та "
-
-msgid "ciReport|%{packagesString} and %{lastPackage}"
-msgstr "%{packagesString} та %{lastPackage}"
-
msgid "ciReport|%{remainingPackagesCount} more"
msgstr "%{remainingPackagesCount} більше"
-msgid "ciReport|%{reportName} is loading"
-msgstr "%{reportName} завантажуєтьÑÑ"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
-msgstr "%{reportName} видав помилку при завантаженні результатів"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
-msgstr "%{type} не виÑвило нових вразливоÑтей"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
-msgstr "%{type} не виÑвило вразливоÑтей"
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
-msgstr "%{type} не виÑвив вразливоÑтей"
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
+
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
msgid "ciReport|Class"
msgstr "КлаÑ"
@@ -7398,39 +8756,30 @@ msgstr "ЯкіÑÑ‚ÑŒ коду"
msgid "ciReport|Confidence"
msgstr "ВпевненіÑÑ‚ÑŒ"
+msgid "ciReport|Container scanning"
+msgstr ""
+
msgid "ciReport|Container scanning detected"
msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² виÑвило"
msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у ваших Docker образах."
-msgid "ciReport|Container scanning is loading"
-msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² завантажуєтьÑÑ"
-
-msgid "ciReport|Container scanning resulted in error while loading results"
-msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² видало помилку при завантаженні результатів"
+msgid "ciReport|DAST"
+msgstr ""
msgid "ciReport|DAST detected"
msgstr "DAST виÑвив"
-msgid "ciReport|DAST is loading"
-msgstr "DAST завантажуєтьÑÑ"
-
-msgid "ciReport|DAST resulted in error while loading results"
-msgstr "DAST видав помилку при завантаженні результатів"
-
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у залежноÑÑ‚ÑÑ… вашого коду."
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
msgid "ciReport|Dependency scanning detected"
msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей виÑвило"
-msgid "ciReport|Dependency scanning is loading"
-msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей завантажуєтьÑÑ"
-
-msgid "ciReport|Dependency scanning resulted in error while loading results"
-msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей видало помилку при завантаженні результатів"
-
msgid "ciReport|Description"
msgstr "ОпиÑ"
@@ -7459,13 +8808,24 @@ msgid "ciReport|Instances"
msgstr "ІнÑтанÑи"
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
-msgstr ""
+msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про взаємодію з звітами безпеки (Ðльфа)."
-msgid "ciReport|Learn more about whitelisting"
-msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про білий ÑпиÑок"
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
-msgid "ciReport|License management detected %{licenseInfo}"
-msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми виÑвило %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми виÑвило %d нову ліцензію"
+msgstr[1] "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми виÑвило %d нові ліцензії"
+msgstr[2] "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми виÑвило %d нових ліцензій"
+msgstr[3] "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми виÑвило %d нових ліцензій"
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
msgid "ciReport|License management detected no new licenses"
msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми не виÑвило нових ліцензій"
@@ -7494,24 +8854,18 @@ msgstr "Показники продуктивноÑÑ‚Ñ–"
msgid "ciReport|Revert dismissal"
msgstr "Відмінити відхиленнÑ"
+msgid "ciReport|SAST"
+msgstr ""
+
msgid "ciReport|SAST detected"
msgstr "SAST виÑвив"
-msgid "ciReport|SAST is loading"
-msgstr "SAST завантажуєтьÑÑ"
-
-msgid "ciReport|SAST resulted in error while loading results"
-msgstr "SAST видав помилку при завантаженні результатів"
-
msgid "ciReport|Security scanning"
msgstr "Перевірка безпеки"
msgid "ciReport|Security scanning failed loading any results"
msgstr "Помилка при завантаженні результатів перевірки безпеки"
-msgid "ciReport|Security scanning is loading"
-msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ завантажуєтьÑÑ"
-
msgid "ciReport|Severity"
msgstr "СерйозніÑÑ‚ÑŒ"
@@ -7540,16 +8894,20 @@ msgid "ciReport|There was an error loading dependency scanning report"
msgstr "Помилка при завантаженні звіту по Ñкануванню залежноÑтей"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
-msgstr ""
-
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr "Ðезатверджені вразливоÑÑ‚Ñ– (червоні) можуть бути відмічені Ñк затверджені."
+msgstr "Помилка при відміні відхиленнÑ. Будь лаÑка, Ñпробуйте знову."
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "Оновити %{name} з %{version} до %{fixed}."
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] "ВикориÑтовуєтьÑÑ %{packagesString}"
+msgstr[1] "ВикориÑтовуєтьÑÑ %{packagesString} Ñ– %{lastPackage}"
+msgstr[2] "ВикориÑтовуєтьÑÑ %{packagesString} Ñ– %{lastPackage}"
+msgstr[3] "ВикориÑтовуєтьÑÑ %{packagesString} Ñ– %{lastPackage}"
+
msgid "ciReport|View full report"
-msgstr ""
+msgstr "ПереглÑнути повний звіт"
msgid "ciReport|no vulnerabilities"
msgstr "немає вразливоÑтей"
@@ -7560,6 +8918,12 @@ msgstr "в конвеєрі"
msgid "command line instructions"
msgstr "інÑтрукції Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð³Ð¾ Ñ€Ñдка"
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr "Ви вимикаєте конфіденційніÑÑ‚ÑŒ. Це означає, що <strong>будь-хто</strong> зможе бачити Ñ– залишати коментарі Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— проблеми."
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr "Ви вмикаєте конфіденційніÑÑ‚ÑŒ. Це означає що лише члени команди <strong>Ñ€Ñ–Ð²Ð½Ñ Ñ€ÐµÐ¿Ð¾Ñ€Ñ‚ÐµÑ€ або вище</strong> матимуть змогу бачити та залишати коментарі Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— проблеми."
+
msgid "connecting"
msgstr "з'єднаннÑ"
@@ -7567,7 +8931,7 @@ msgid "could not read private key, is the passphrase correct?"
msgstr "неможливо зчитати приватний ключ, чи є пароль правильним?"
msgid "customize"
-msgstr ""
+msgstr "налаштувати"
msgid "day"
msgid_plural "days"
@@ -7579,28 +8943,11 @@ msgstr[3] "днів"
msgid "deploy token"
msgstr "токен Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] "виÑвлено %d виправлену вразливіÑÑ‚ÑŒ"
-msgstr[1] "виÑвлено %d виправлених вразливоÑтей"
-msgstr[2] "виÑвлено %d виправлених вразливоÑтей"
-msgstr[3] "виÑвлено %d виправлених вразливоÑтей"
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] "виÑвлено %d нову вразливіÑÑ‚ÑŒ"
-msgstr[1] "виÑвлено %d нових вразливоÑтей"
-msgstr[2] "виÑвлено %d нових вразливоÑтей"
-msgstr[3] "виÑвлено %d нових вразливоÑтей"
-
-msgid "detected no vulnerabilities"
-msgstr "не виÑвило вразливоÑтей"
-
msgid "disabled"
msgstr "вимкнено"
msgid "done"
-msgstr ""
+msgstr "готово"
msgid "enabled"
msgstr "увімкнено"
@@ -7614,12 +8961,22 @@ msgstr "Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
msgid "here"
msgstr "тут"
+msgid "https://your-bitbucket-server"
+msgstr "https://your-bitbucket-server"
+
msgid "import flow"
-msgstr ""
+msgstr "процедура імпорту"
msgid "importing"
msgstr "імпорт"
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] "завершений інÑтанÑ"
+msgstr[1] "завершені інÑтанÑи"
+msgstr[2] "завершених інÑтанÑів"
+msgstr[3] "завершених інÑтанÑів"
+
msgid "is invalid because there is downstream lock"
msgstr "неправильний через наÑвніÑÑ‚ÑŒ блокувань на нижчих рівнÑÑ…"
@@ -7629,9 +8986,15 @@ msgstr "неправильний через наÑвніÑÑ‚ÑŒ блокуванÑ
msgid "is not a valid X509 certificate."
msgstr "не відповідний Ñертифікат X509."
+msgid "issue boards"
+msgstr "дошки Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼"
+
msgid "latest version"
msgstr "оÑÑ‚Ð°Ð½Ð½Ñ Ð²ÐµÑ€ÑÑ–Ñ"
+msgid "license management"
+msgstr "керувати ліцензіÑми"
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr "заблоковано %{path_lock_user_name} %{created_at}"
@@ -7663,8 +9026,11 @@ msgstr "ДозволÑÑ” коміти від учаÑників, Ñкі можу
msgid "mrWidget|An error occured while removing your approval."
msgstr "Під Ñ‡Ð°Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr "Помилка при отриманні даних про Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиту на злиттÑ."
+
msgid "mrWidget|An error occurred while submitting your approval."
-msgstr ""
+msgstr "Помилка при обробці вашого затвердженнÑ."
msgid "mrWidget|Approve"
msgstr "Затвердити"
@@ -7711,6 +9077,9 @@ msgstr "Email-патчі"
msgid "mrWidget|Failed to load deployment statistics"
msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ ÑтатиÑтику розгортаннÑ"
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr "Якщо гілка %{branch} Ñ–Ñнує у вашому локальному репозиторії, то ви можете заÑтоÑувати цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ за допомогою"
@@ -7733,10 +9102,10 @@ msgid "mrWidget|Merge locally"
msgstr "Злити локально"
msgid "mrWidget|Merge request approved"
-msgstr ""
+msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð¾"
msgid "mrWidget|Merge request approved; you can approve additionally"
-msgstr ""
+msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð±ÑƒÐ»Ð¾ затверджено; Ви можете затвердити додатково"
msgid "mrWidget|Merged by"
msgstr "Злито"
@@ -7748,11 +9117,17 @@ msgid "mrWidget|No Approval required; you can still approve"
msgstr "Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½Ðµ Ñ” обов’Ñзковим; але ви вÑе одно можете це зробити"
msgid "mrWidget|Open in Web IDE"
+msgstr "Відкрити у Web IDE"
+
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
msgstr ""
msgid "mrWidget|Plain diff"
msgstr "ПроÑте порівнÑÐ½Ð½Ñ (diff)"
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr "Оновити"
@@ -7774,9 +9149,26 @@ msgstr "Видалити ваше затвердженнÑ"
msgid "mrWidget|Request to merge"
msgstr "Запит на злиттÑ"
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr "Вирішити конфлікти"
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr "Ðнулювати"
@@ -7795,9 +9187,18 @@ msgstr "Зміни не були злиті в"
msgid "mrWidget|The changes will be merged into"
msgstr "Зміни будуть злиті в"
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr "Гілку-джерело видалено"
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr "Гілка-джерело в процеÑÑ– видаленнÑ"
@@ -7822,6 +9223,9 @@ msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð² процеÑÑ– виконаннÑ"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "Цей проект заархівований, доÑтуп до запиÑу було відключено"
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "Ви можете прийнÑти цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ за допомогою"
@@ -7840,6 +9244,9 @@ msgstr "в"
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr "буде злито автоматично, коли конвеєр завершитьÑÑ ÑƒÑпішно"
+msgid "n/a"
+msgstr "н/д"
+
msgid "new merge request"
msgstr "Ðовий запит на злиттÑ"
@@ -7849,6 +9256,13 @@ msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою"
msgid "or"
msgstr "або"
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "батьківÑький об’єкт"
@@ -7868,6 +9282,9 @@ msgstr "приватний ключ не відповідає Ñертифіка
msgid "remaining"
msgstr "залишилоÑÑŒ"
+msgid "remove"
+msgstr "видалити"
+
msgid "remove due date"
msgstr "видалити заплановану дату завершеннÑ"
@@ -7881,7 +9298,7 @@ msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} оновлює Ñуму витраченого чаÑу."
msgid "started"
-msgstr ""
+msgstr "розпочато"
msgid "this document"
msgstr "цей документ"
@@ -7889,6 +9306,9 @@ msgstr "цей документ"
msgid "to help your contributors communicate effectively!"
msgstr "щоб допомогти вашим контриб’юторам ефективно ÑпілкуватиÑÑ!"
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr "ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
@@ -7896,7 +9316,7 @@ msgid "uses Kubernetes clusters to deploy your code!"
msgstr "викориÑтовує клаÑтери Kubernetes Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ!"
msgid "view it on GitLab"
-msgstr ""
+msgstr "переглÑнути це на GitLab"
msgid "with %{additions} additions, %{deletions} deletions."
msgstr "з %{additions} додаваннÑми Ñ– %{deletions} видаленнÑми."
diff --git a/locale/unfound_translations.rb b/locale/unfound_translations.rb
new file mode 100644
index 00000000000..0826d64049b
--- /dev/null
+++ b/locale/unfound_translations.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# Dynamic translations which needs to be marked by `N_` so they can be found by `rake gettext:find`, see:
+# https://github.com/grosser/gettext_i18n_rails#unfound-translations-with-rake-gettextfind
+
+# NotificationSetting.email_events
+N_('NotificationEvent|New note')
+N_('NotificationEvent|New issue')
+N_('NotificationEvent|Reopen issue')
+N_('NotificationEvent|Close issue')
+N_('NotificationEvent|Reassign issue')
+N_('NotificationEvent|New merge request')
+N_('NotificationEvent|Close merge request')
+N_('NotificationEvent|Reassign merge request')
+N_('NotificationEvent|Merge merge request')
+N_('NotificationEvent|Failed pipeline')
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 07537eda598..19fa17eaff1 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-CN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 11:46\n"
+
+msgid " Status"
+msgstr "状æ€"
msgid " and"
msgstr "和"
@@ -42,6 +45,14 @@ msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d 导出器"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] "%d 个失败的测试结果"
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] "%d 个确定的测试结果"
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d 个议题"
@@ -58,10 +69,6 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] "%d 个指标"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] "%d 个新许å¯è¯"
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] "%d 个已暂存的修改"
@@ -85,7 +92,7 @@ msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "ç”± %{commit_author_link} æ交于 %{commit_timeago}"
msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
-msgstr ""
+msgstr "%{counter_storage} (%{counter_repositories} 个存储库, %{counter_build_artifacts} 个构建产物, %{counter_lfs_objects} 个LFS对象)"
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -94,9 +101,15 @@ msgstr[0] "%{count} ä½å‚与者"
msgid "%{filePath} deleted"
msgstr "%{filePath} 已删除"
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr "%{firstLabel} +%{labelCount} 更多"
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr "%{group_docs_link_start}群组%{group_docs_link_end} å…许您管ç†ã€å作多个项目。群组的æˆå‘˜å¯ä»¥è®¿é—®ç¾¤ç»„下的所有项目。"
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr "%{issuableType} 将被删除ï¼æ‚¨ç¡®å®šå—?"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 已开始"
@@ -130,7 +143,7 @@ msgstr[0] "%{storage_name}:已 %{failed_attempts} 次å°è¯•è®¿é—®å­˜å‚¨å¤±è´¥ï
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
-msgstr[0] ""
+msgstr[0] "%{text}%{files}"
msgid "%{text} is available"
msgstr "%{text}å¯ç”¨"
@@ -138,13 +151,15 @@ msgstr "%{text}å¯ç”¨"
msgid "%{title} changes"
msgstr "%{title}更改"
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] "%{type}检测到%{vulnerabilityCount}个æ¼æ´ž"
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "%{unstaged}个未暂存的更改åŠ%{staged}个已暂存的更改"
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr "%{usage_ping_link_start}了解更多%{usage_ping_link_end}关于GitLab Inc.的共享信æ¯ã€‚"
+
+msgid "+ %{count} more"
+msgstr "+ 其余 %{count} 项"
+
msgid "+ %{moreCount} more"
msgstr "+ 其余 %{moreCount} 项"
@@ -173,6 +188,10 @@ msgid "1 closed merge request"
msgid_plural "%d closed merge requests"
msgstr[0] "%d 个已关闭的åˆå¹¶è¯·æ±‚"
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] "%d 个群组"
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] "%d 个已åˆå¹¶çš„åˆå¹¶è¯·æ±‚"
@@ -189,6 +208,14 @@ msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d æ¡æµæ°´çº¿"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] "%d 个角色"
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] "%d ä½ç”¨æˆ·"
+
msgid "1st contribution!"
msgstr "最高贡献"
@@ -211,16 +238,19 @@ msgid "404|Please contact your GitLab administrator if you think this is a mista
msgstr "如果您觉得这是一个错误的æ示信æ¯ï¼Œè¯·è”系您的 GitLab 管ç†å‘˜ã€‚"
msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
+msgstr "<code>“johnsmith@example.comâ€ï¼šâ€œ@ johnsmithâ€</code> 将会把“By <a href=\"#\">@johnsmith</a>â€æ·»åŠ åˆ°åŽŸæœ¬ç”±johnsmith@example.com创建的所有议题和评论中,并将 <a href=\"#\">@johnsmith</a> 设为原本分é…ç»™johnsmith@example.com所有问题的å—让人。"
msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
+msgstr "<code>“johnsmith@example.comâ€ï¼šâ€œ@ johnsmithâ€</code> 将会把\"By John Smith\"添加到原本由johnsmith@example.com创建的所有议题和评论中。"
msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
+msgstr "<code>“johnsmith@example.comâ€ï¼šâ€œ@ johnsmithâ€</code> 将会把\"By johnsm...@example.com\"添加到原本由johnsmith@example.com创建的所有议题和评论中。 为ä¿æŠ¤ç”¨æˆ·çš„éšç§ï¼Œç”µå­é‚®ä»¶åœ°å€æˆ–用户å将被å±è”½ã€‚"
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
+msgstr "<code>“johnsmith@example.comâ€ï¼šâ€œ@ johnsmithâ€</code> 将会把“By <a href=\"#\">@johnsmith</a>â€æ·»åŠ åˆ°åŽŸæœ¬ç”±johnsmith@example.com创建的所有议题和评论中。 为ä¿æŠ¤ç”¨æˆ·çš„éšç§ï¼Œç”µå­é‚®ä»¶åœ°å€æˆ–用户å默认将被å±è”½ã€‚如需显示完整邮件地å€ï¼Œå¯ä½¿ç”¨æ­¤é€‰é¡¹ã€‚"
+
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr "<strong>%{changedFilesLength} å–消暂存</strong> å’Œ <strong>%{stagedFilesLength} æš‚å­˜</strong> å˜æ›´å†…容"
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr "已创建 <strong>%{created_count}</strong> 个, 已关闭 <strong>%{accepted_count}</strong> 个。"
@@ -229,7 +259,7 @@ msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</stron
msgstr "已创建 <strong>%{created_count}</strong> 个, 已关闭 <strong>%{closed_count}</strong> 个。"
msgid "<strong>%{group_name}</strong> group members"
-msgstr ""
+msgstr "<strong>%{group_name}</strong> 群组æˆå‘˜"
msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
msgstr "<strong>%{pushes}</strong> 个推é€ï¼Œè¶…å‰ <strong>%{people}</strong> å贡献者的 <strong>%{commits}</strong> 个æ交。"
@@ -237,12 +267,18 @@ msgstr "<strong>%{pushes}</strong> 个推é€ï¼Œè¶…å‰ <strong>%{people}</strong>
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>删除</strong>æºåˆ†æ”¯"
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "Runner是一个执行任务的进程。您å¯ä»¥æ ¹æ®éœ€è¦é…置任æ„æ•°é‡çš„Runner。"
msgid "A collection of graphs regarding Continuous Integration"
msgstr "æŒç»­é›†æˆæ•°æ®å›¾"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr "无法为空项目选择默认分支。"
+
+msgid "A deleted user"
+msgstr "已删除的用户"
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "将在派生(fork)项目中中创建一个新的分支, 并开å¯ä¸€ä¸ªæ–°çš„åˆå¹¶è¯·æ±‚。"
@@ -256,10 +292,10 @@ msgid "A user with write access to the source branch selected this option"
msgstr "具有对æºåˆ†æ”¯çš„写入æƒé™çš„用户选择了此选项"
msgid "About GitLab"
-msgstr ""
+msgstr "关于 GitLab"
msgid "About GitLab CE"
-msgstr ""
+msgstr "关于 GitLab CE"
msgid "About auto deploy"
msgstr "关于自动部署"
@@ -283,7 +319,10 @@ msgid "Access Tokens"
msgstr "访问令牌"
msgid "Access denied! Please verify you can add deploy keys to this repository."
-msgstr ""
+msgstr "æ‹’ç»è®¿é—®ï¼è¯·æ ¸æŸ¥æ‚¨æ˜¯å¦æœ‰æƒé™å°†éƒ¨ç½²å¯†é’¥æ·»åŠ åˆ°æ­¤ä»“库。"
+
+msgid "Access expiration date"
+msgstr "访问到期日期"
msgid "Access to '%{classification_label}' not allowed"
msgstr "ä¸å…许访问%{classification_label}"
@@ -291,7 +330,7 @@ msgstr "ä¸å…许访问%{classification_label}"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "为方便修å¤æŒ‚载问题,访问故障存储已被暂时ç¦ç”¨ã€‚在问题解决åŽè¯·é‡ç½®å­˜å‚¨è¿è¡ŒçŠ¶å†µä¿¡æ¯ï¼Œä»¥å…许å†æ¬¡è®¿é—®ã€‚"
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "访问您的 runner 令牌,自定义æµæ°´çº¿é…置,以åŠæŸ¥çœ‹æµæ°´çº¿çŠ¶æ€å’Œè¦†ç›–率报告。"
msgid "Account"
@@ -324,17 +363,17 @@ msgstr "添加组 Webhooks å’Œ GitLab ä¼ä¸šç‰ˆã€‚"
msgid "Add Kubernetes cluster"
msgstr "添加 Kubernetes 集群"
-msgid "Add License"
-msgstr "添加许å¯è¯"
-
msgid "Add Readme"
msgstr "添加自述文件"
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "添加包å«åœ¨æ‰€æœ‰ç”µå­é‚®ä»¶ä¸­çš„附加文本。 长度ä¸è¶…过%{character_limit} 字符"
+msgid "Add license"
+msgstr "添加许å¯è¯"
+
msgid "Add new application"
-msgstr ""
+msgstr "新建应用"
msgid "Add new directory"
msgstr "添加目录"
@@ -346,22 +385,31 @@ msgid "Add todo"
msgstr "添加待办事项"
msgid "Add user(s) to the group:"
-msgstr ""
+msgstr "å‘群组添加用户"
msgid "Add users to group"
-msgstr ""
+msgstr "将用户加入群组"
+
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr "当å‰GitLab实例ç¦æ­¢æ·»åŠ æ–°åº”用程åºã€‚请è”系您的GitLab管ç†å‘˜ä»¥èŽ·å¾—相关æƒé™ã€‚"
msgid "Additional text"
msgstr "附加文本"
msgid "Admin Area"
-msgstr ""
+msgstr "管ç†ä¸­å¿ƒ"
msgid "Admin Overview"
-msgstr ""
+msgstr "管ç†æ¦‚览"
msgid "Admin area"
-msgstr ""
+msgstr "管ç†ä¸­å¿ƒ"
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr "您å³å°†æ°¸ä¹…删除用户 %{username}。该用户的议题ã€åˆå¹¶è¯·æ±‚以åŠé“¾æŽ¥åˆ°è¯¥ç”¨æˆ·çš„群组将被转移到系统的“Ghost用户â€ã€‚为é¿å…æ•°æ®ä¸¢å¤±ï¼Œå»ºè®®æ‚¨ä½¿ç”¨ %{strong_start}ç¦ç”¨ç”¨æˆ·%{strong_end} 功能。一旦您 %{strong_start}删除用户%{strong_end},将无法撤消或æ¢å¤ã€‚"
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr "您å³å°†æ°¸ä¹…删除用户 %{username}。此æ“作会删除该用户的所有议题ã€åˆå¹¶è¯·æ±‚以åŠé“¾æŽ¥åˆ°è¯¥ç”¨æˆ·çš„群组。为é¿å…æ•°æ®ä¸¢å¤±ï¼Œå»ºè®®æ‚¨ä½¿ç”¨ %{strong_start}ç¦ç”¨ç”¨æˆ·%{strong_end} 功能。一旦您 %{strong_start}删除用户%{strong_end},将无法撤消或æ¢å¤ã€‚"
msgid "AdminArea|Stop all jobs"
msgstr "åœæ­¢æ‰€æœ‰ä½œä¸š"
@@ -376,11 +424,14 @@ msgid "AdminArea|Stopping jobs failed"
msgstr "åœæ­¢ä½œä¸šå¤±è´¥"
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
-msgstr "您å³å°†åœæ­¢æ‰€æœ‰ä½œä¸šã€‚所有正在è¿è¡Œçš„都会被åœæ­¢ã€‚"
+msgstr "您å³å°†åœæ­¢æ‰€æœ‰ä½œä¸šã€‚这会中断并结æŸæ‰€æœ‰æ­£åœ¨è¿è¡Œçš„作业。"
msgid "AdminHealthPageLink|health page"
msgstr "è¿è¡ŒçŠ¶å†µé¡µé¢"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr "您å³å°†æ°¸ä¹…删除项目 %{projectName},其存储库以åŠæ‰€æœ‰ç›¸å…³èµ„æºï¼ŒåŒ…括问题ã€åˆå¹¶è¯·æ±‚等。一旦确认并点击 %{strong_start}删除项目%{strong_end},将无法撤消或æ¢å¤ã€‚"
+
msgid "AdminProjects|Delete"
msgstr "删除"
@@ -429,6 +480,9 @@ msgstr "所有更改å‡å·²æ交"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "从模æ¿æˆ–导入时为空白项目将å¯ç”¨æ‰€æœ‰åŠŸèƒ½ï¼Œä½†å¯ä»¥åœ¨é¡¹ç›®è®¾ç½®ä¸­å°†å…¶ç¦ç”¨ã€‚"
+msgid "All users"
+msgstr "所有用户"
+
msgid "Allow commits from members who can merge to the target branch."
msgstr "具有åˆå¹¶åˆ°ç›®æ ‡åˆ†æ”¯æƒé™çš„æˆå‘˜å…许æ交"
@@ -456,29 +510,32 @@ msgstr "此外,也å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。创建 Personal
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "此外,也å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。创建Personal Access Token时,在范围中需选择 <code>repo</code> ,以便显示å¯ä¾›å¯¼å…¥å…¬å¼€å’Œç§æœ‰çš„仓库列表"
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr "在æ交表å•æ—¶å°†è‡ªåŠ¨ç”Ÿæˆ SSH 密钥。有关详细信æ¯, 请å‚阅文档。"
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
-msgstr ""
+msgstr "应用%{link_to_client}请求访问您的 GitLab å¸æˆ·ã€‚"
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
-msgstr ""
+msgstr "空GitLab用户字段将在所有问题和注释的æ述中添加FogBugz用户的全å(例如“By John Smithâ€ï¼‰ã€‚它还将与项目创建者关è”å’Œ/或分é…这些问题和评论。"
msgid "An error accured whilst committing your changes."
msgstr "æ交更改时å‘生错误。"
msgid "An error has occurred"
-msgstr ""
+msgstr "å‘生错误"
msgid "An error occured creating the new branch."
-msgstr "创建新分支时å‘生错误。"
+msgstr "创建分支时å‘生错误。"
msgid "An error occured whilst fetching the job trace."
msgstr "获å–作业日志时å‘生错误。"
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr "获å–æµæ°´çº¿æ—¶å‘生错误。"
msgid "An error occured whilst loading all the files."
-msgstr "所有文件都无法加载。"
+msgstr "加载文件时å‘生错误。"
msgid "An error occured whilst loading the file content."
msgstr "加载文件评论时å‘生错误。"
@@ -525,20 +582,32 @@ msgstr "èŽ·å– markdown 预览时出错"
msgid "An error occurred while fetching sidebar data"
msgstr "获å–侧边æ æ•°æ®æ—¶å‘生错误"
+msgid "An error occurred while fetching stages."
+msgstr "获å–阶段时å‘生错误"
+
+msgid "An error occurred while fetching the job log."
+msgstr "获å–作业日志时å‘生错误。"
+
+msgid "An error occurred while fetching the job."
+msgstr "获å–作业详情时å‘生错误"
+
+msgid "An error occurred while fetching the jobs."
+msgstr "获å–作业列表时å‘生错误"
+
msgid "An error occurred while fetching the pipeline."
-msgstr "获å–æµæ°´çº¿æ—¶å‡ºé”™"
+msgstr "获å–æµæ°´çº¿æ—¶å‘生错误"
msgid "An error occurred while getting projects"
msgstr "获å–项目时å‘生错误"
-msgid "An error occurred while importing project: ${details}"
-msgstr "在导入项目时å‘生错误:${details}"
+msgid "An error occurred while importing project: %{details}"
+msgstr "在导入项目时å‘生错误:%{details}"
msgid "An error occurred while initializing path locks"
msgstr "åˆå§‹åŒ–路径é”æ—¶å‘生错误"
msgid "An error occurred while loading commit signatures"
-msgstr ""
+msgstr "加载æ交签åæ—¶å‘生错误"
msgid "An error occurred while loading diff"
msgstr "加载差异时å‘生错误"
@@ -586,13 +655,13 @@ msgid "An error occurred. Please try again."
msgstr "å‘生了错误,请å†è¯•ä¸€æ¬¡ã€‚"
msgid "Anonymous"
-msgstr ""
+msgstr "匿å"
msgid "Anti-spam verification"
-msgstr ""
+msgstr "å垃圾邮件验è¯"
msgid "Any"
-msgstr ""
+msgstr "任何"
msgid "Any Label"
msgstr "任何标记"
@@ -601,16 +670,16 @@ msgid "Appearance"
msgstr "外观"
msgid "Application"
-msgstr ""
+msgstr "应用"
msgid "Application Id"
-msgstr ""
+msgstr "应用 ID"
msgid "Application: %{name}"
-msgstr ""
+msgstr "应用:%{name}"
msgid "Applications"
-msgstr "应用程åº"
+msgstr "应用"
msgid "Apr"
msgstr "å››"
@@ -621,14 +690,20 @@ msgstr "四月"
msgid "Archived project! Repository and other project resources are read-only"
msgstr "已归档项目ï¼ä»“库和其他项目资æºå‡ä¸ºåªè¯»"
+msgid "Archived projects"
+msgstr "已存档项目"
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "确定è¦åˆ é™¤æ­¤æµæ°´çº¿è®¡åˆ’å—?"
msgid "Are you sure you want to lose unsaved changes?"
-msgstr ""
+msgstr "确定è¦æ”¾å¼ƒæœªä¿å­˜çš„更改å—?"
+
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr "您确定è¦é‡æ–°ç”Ÿæˆå…¬é’¥å—?在镜åƒå†æ¬¡è¿è¡Œä¹‹å‰ï¼Œæ‚¨å¿…须更新远程æœåŠ¡å™¨ä¸Šçš„公钥。"
msgid "Are you sure you want to remove %{group_name}?"
-msgstr ""
+msgstr "确定移除群组 %{group_name} å—?"
msgid "Are you sure you want to remove this identity?"
msgstr "你确定è¦åˆ é™¤è¿™ä¸ªèº«ä»½æ ‡è¯†å—?"
@@ -645,13 +720,16 @@ msgstr "你确定è¦è§£é” %{path_lock_path} å—?"
msgid "Are you sure?"
msgstr "确定å—?"
+msgid "Artifact ID"
+msgstr "作业产物ID"
+
msgid "Artifacts"
msgstr "产物"
msgid "Ascending"
msgstr "å‡åºæŽ’列"
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr "请群组维护者é…置一个群组级 Runner。"
msgid "Assertion consumer service URL"
@@ -684,8 +762,8 @@ msgstr "已分派给我"
msgid "Assignee"
msgstr "指派人"
-msgid "Assignee boards not available with your current license"
-msgstr "当å‰è®¸å¯è¯æ— æ³•ä½¿ç”¨æŒ‡æ´¾çœ‹æ¿"
+msgid "Assignee lists not available with your current license"
+msgstr "当å‰è®¸å¯è¯æ— æ³•ä½¿ç”¨æŒ‡æ´¾åˆ—表"
msgid "Assignee lists show all issues assigned to the selected user."
msgstr "指派列表显示分é…给选定用户的所有议题。"
@@ -709,34 +787,37 @@ msgid "Authentication Log"
msgstr "认è¯æ—¥å¿—"
msgid "Authentication log"
-msgstr ""
+msgstr "认è¯æ—¥å¿—"
+
+msgid "Authentication method"
+msgstr "验è¯æ–¹å¼"
msgid "Author"
msgstr "作者"
msgid "Authorization code:"
-msgstr ""
+msgstr "授æƒç ï¼š"
msgid "Authorization was granted by entering your username and password in the application."
-msgstr ""
+msgstr "在应用中输入您的用户å和密ç å³å®ŒæˆæŽˆæƒã€‚"
msgid "Authorize"
-msgstr ""
+msgstr "授æƒ"
msgid "Authorize %{link_to_client} to use your account?"
-msgstr ""
+msgstr "æŽˆæƒ %{link_to_client} 使用您的å¸æˆ·ï¼Ÿ"
msgid "Authorized At"
-msgstr ""
+msgstr "授æƒäºŽ"
msgid "Authorized applications (%{size})"
-msgstr ""
+msgstr "已授æƒåº”用 (%{size})"
msgid "Authors: %{authors}"
msgstr "作者:%{authors}"
msgid "Auto DevOps"
-msgstr ""
+msgstr "Auto DevOps"
msgid "Auto DevOps enabled"
msgstr "å¯ç”¨Auto DevOps"
@@ -771,6 +852,9 @@ msgstr "将根æ®é¢„定义的 CI/CD é…置自动构建ã€æµ‹è¯•å’Œéƒ¨ç½²åº”用ç¨
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "想了解更多请访问 %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr "Auto DevOpsæµæ°´çº¿å·²å¯ç”¨ã€‚如果未找到CIé…置文件,将使用该æµæ°´çº¿ã€‚ %{more_information_link}"
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr "如果当å‰é¡¹ç›®%{link_to_auto_devops_settings}, å¯ä»¥è‡ªåŠ¨çš„构建和测试应用。如果已%{link_to_add_kubernetes_cluster},则也å¯ä»¥å®žçŽ°è‡ªåŠ¨éƒ¨ç½²ã€‚"
@@ -780,6 +864,9 @@ msgstr "添加Kubernetes集群"
msgid "AutoDevOps|enable Auto DevOps"
msgstr "å¯ç”¨Auto DevOps"
+msgid "Automatically marked as default internal user"
+msgstr "自动标记为默认内部用户"
+
msgid "Available"
msgstr "å¯ç”¨çš„"
@@ -799,14 +886,11 @@ msgid "Background Color"
msgstr "背景é¡è‰²"
msgid "Background Jobs"
-msgstr ""
+msgstr "åŽå°ä½œä¸š"
msgid "Background color"
msgstr "背景颜色"
-msgid "Background jobs"
-msgstr "åŽå°ä½œä¸š"
-
msgid "Badges"
msgstr "徽章"
@@ -846,6 +930,9 @@ msgstr "无徽章图åƒ"
msgid "Badges|No image to preview"
msgstr "无图åƒå¯é¢„览"
+msgid "Badges|Please fill in a valid URL"
+msgstr "请输入正确的 URL"
+
msgid "Badges|Project Badge"
msgstr "项目徽章"
@@ -873,9 +960,15 @@ msgstr "当å‰ç¾¤ç»„无徽章"
msgid "Badges|This project has no badges"
msgstr "当å‰é¡¹ç›®æ— å¾½ç« "
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr "您å³å°†åˆ é™¤æ­¤å¾½ç« ã€‚å¾½ç« è¢«åˆ é™¤åŽ <strong>ä¸èƒ½</strong> æ¢å¤ã€‚"
+
msgid "Badges|Your badges"
msgstr "您的徽章"
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr "例如 %{exampleUrl}"
+
msgid "Begin with the selected commit"
msgstr "从选定的æ交开始"
@@ -883,7 +976,7 @@ msgid "Below are examples of regex for existing tools:"
msgstr "以下是现有工具的正则表达å¼ç¤ºä¾‹ï¼š"
msgid "Below you will find all the groups that are public."
-msgstr ""
+msgstr "您将在下é¢æ‰¾åˆ°æ‰€æœ‰å…¬å¼€çš„群组。"
msgid "Billing"
msgstr "计费"
@@ -904,7 +997,7 @@ msgid "BillingPlans|Downgrade"
msgstr "é™çº§"
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
-msgstr ""
+msgstr "通过阅读我们的 %{faq_link}了解有关æ¯ä¸ªè®¡åˆ’的更多信æ¯ï¼Œæˆ–者开始å…费试用GitLab.comçš„Gold计划30天。"
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr "请查阅%{faq_link} 进一步了解æ¯ä¸ªè®¡åˆ’的相关信æ¯ã€‚"
@@ -931,13 +1024,13 @@ msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr "您目å‰æ­£åœ¨ä½¿ç”¨ %{plan_link} 方案。"
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
-msgstr ""
+msgstr "您的GitLab.com试用期已在%{expiration_date}结æŸã€‚%{learn_more_text}"
msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
-msgstr ""
+msgstr "您的Gold计划试用将 <strong>于 %{expiration_date}过期</strong>。您å¯ä»¥é€šè¿‡é˜…读%{features_link}æ¥è¿›ä¸€æ­¥äº†è§£GitLab.com Gold计划的相关信æ¯ã€‚"
msgid "BillingPlans|features"
-msgstr ""
+msgstr "功能"
msgid "BillingPlans|frequently asked questions"
msgstr "常问问题"
@@ -951,11 +1044,14 @@ msgstr "æ¯å¹´æ”¯ä»˜ %{price_per_year}"
msgid "BillingPlans|per user"
msgstr "æ¯ç”¨æˆ·"
+msgid "Bitbucket Server Import"
+msgstr "BitbucketæœåŠ¡å™¨å¯¼å…¥"
+
msgid "Bitbucket import"
-msgstr ""
+msgstr "从 Bitbucket 导入"
msgid "Blog"
-msgstr ""
+msgstr "åšå®¢"
msgid "Boards"
msgstr "看æ¿"
@@ -1120,6 +1216,9 @@ msgstr "æµè§ˆæ–‡ä»¶"
msgid "Browse files"
msgstr "æµè§ˆæ–‡ä»¶"
+msgid "Built-In"
+msgstr "内置"
+
msgid "Business metrics (Custom)"
msgstr "业务指标(自定义)"
@@ -1132,6 +1231,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr "CI/CD 设置"
+msgid "CI will run using the credentials assigned above."
+msgstr "CI将使用上é¢æŒ‡å®šçš„凭æ®è¿è¡Œã€‚"
+
msgid "CI/CD"
msgstr "æŒç»­é›†æˆ/æŒç»­éƒ¨ç½²"
@@ -1144,9 +1246,6 @@ msgstr "为外部仓库设置的æŒç»­é›†æˆ/æŒç»­éƒ¨ç½²"
msgid "CI/CD settings"
msgstr "CI/CD 设置"
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr "在开始使用æŒç»­é›†æˆå’ŒæŒç»­äº¤ä»˜ä¹‹å‰ï¼Œéœ€è¦æ˜Žç¡®æŒ‡å®š %{ci_file}。"
-
msgid "CICD|Auto DevOps"
msgstr "Auto DevOps"
@@ -1159,44 +1258,38 @@ msgstr "自动部署到预å‘布环境,手动部署到生产环境"
msgid "CICD|Continuous deployment to production"
msgstr "æŒç»­éƒ¨ç½²åˆ°ç”Ÿäº§çŽ¯å¢ƒ"
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr "默认为Auto DevOpsæµæ°´çº¿"
+
msgid "CICD|Deployment strategy"
msgstr "部署策略"
msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr "部署策略需è¦ä¸€ä¸ªåŸŸåæ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
-msgid "CICD|Disable Auto DevOps"
-msgstr "ç¦ç”¨ Auto DevOps"
-
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr "如果您使用Auto DevOps设置多个Kubernetes群集,请ä¸è¦åœ¨æ­¤å¤„设置域。"
-msgid "CICD|Enable Auto DevOps"
-msgstr "å¯ç”¨Auto DevOps"
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr "项目ä¸å­˜åœ¨ %{ci_file} 时,将会使用系统默认设置å¯ç”¨æˆ–ç¦ç”¨Auto DevOps。"
-
-msgid "CICD|Instance default (%{state})"
-msgstr "系统默认 (%{state})"
-
msgid "CICD|Jobs"
msgstr "作业"
msgid "CICD|Learn more about Auto DevOps"
msgstr "了解更多Auto DevOps的相关信æ¯"
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
-msgstr "当项目中没有 %{ci_file} 时,将使用 Auto DevOpsæµæ°´çº¿é…置。"
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr "在未找到备用CIé…置文件时使用Auto DevOpsæµæ°´çº¿ã€‚"
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr "如需使用自动化应用程åºè¯„审和自动部署,请指定域å。"
+msgid "CICD|instance enabled"
+msgstr "å·²å¯ç”¨çš„实例"
+
msgid "Callback URL"
-msgstr ""
+msgstr "回调 URL"
msgid "Callback url"
-msgstr ""
+msgstr "回调 URL"
msgid "Can't find HEAD commit for this branch"
msgstr "无法找到此分支的 HEAD æ交"
@@ -1219,6 +1312,9 @@ msgstr "è¯ä¹¦æŒ‡çº¹"
msgid "Change Weight"
msgstr "更改æƒé‡"
+msgid "Change template"
+msgstr "更改模æ¿"
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr "更改此值以影å“GitLab UI拉å–更新的频率。"
@@ -1265,10 +1361,10 @@ msgid "Cherry-pick this merge request"
msgstr "拣选此åˆå¹¶è¯·æ±‚"
msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
-msgstr ""
+msgstr "选择<strong>创建归档</strong> 并等待归档过程完æˆã€‚"
msgid "Choose <strong>Next</strong> at the bottom of the page."
-msgstr ""
+msgstr "选择页é¢åº•éƒ¨çš„<strong>下一步</strong>。"
msgid "Choose File ..."
msgstr "选择文件 ……"
@@ -1276,6 +1372,12 @@ msgstr "选择文件 ……"
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "选择分支/标签(例如%{master})或输入æ交(例如%{sha})以查看更改内容或创建åˆå¹¶è¯·æ±‚。"
+msgid "Choose a template..."
+msgstr "选择模æ¿..."
+
+msgid "Choose a type..."
+msgstr "选择类型..."
+
msgid "Choose any color."
msgstr "选择任何颜色。"
@@ -1286,7 +1388,7 @@ msgid "Choose file..."
msgstr "选择文件..."
msgid "Choose the top-level group for your repository imports."
-msgstr ""
+msgstr "选择存储库导入的顶级群组。"
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr "选择您希望与此次è¦èŠ‚点åŒæ­¥çš„群组。"
@@ -1403,13 +1505,13 @@ msgid "Click any <strong>project name</strong> in the project list below to navi
msgstr "å•å‡»ä¸‹é¢é¡¹ç›®åˆ—表中的任何 <strong>项目å称</strong> 跳转到项目里程碑。"
msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
-msgstr ""
+msgstr "点击 <strong>下载</strong> 按钮,等待下载完æˆã€‚"
msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
msgstr "点击å³ä¸Šè§’çš„ <strong>å‡çº§</strong> 按钮以å‡çº§åˆ°åˆ°ç¾¤ç»„里程碑。"
msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
-msgstr ""
+msgstr "请点击å³è¾¹çš„ <strong>æ— </strong> 按钮,因为我们åªéœ€è¦â€œGoogle Code项目托管â€ã€‚"
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr "点击下é¢çš„按钮转到Kubernetes页é¢å¼€å§‹å®‰è£…过程"
@@ -1430,7 +1532,7 @@ msgid "Client authentication key password"
msgstr "客户端认è¯å¯†é’¥å¯†ç "
msgid "Clients"
-msgstr ""
+msgstr "客户端"
msgid "Clone repository"
msgstr "克隆仓库"
@@ -1438,6 +1540,9 @@ msgstr "克隆仓库"
msgid "Close"
msgstr "关闭"
+msgid "Close epic"
+msgstr "关闭å²è¯—故事"
+
msgid "Closed"
msgstr "已关闭"
@@ -1447,6 +1552,9 @@ msgstr "已关闭议题"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} å·²æˆåŠŸå®‰è£…到Kubernetes集群上"
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr "%{boldNotice} 这将添加一些é¢å¤–的资æºï¼Œå¦‚è´Ÿè½½å‡è¡¡å™¨ï¼Œè¿™å¯èƒ½ä¼šäº§ç”Ÿé¢å¤–çš„æˆæœ¬ï¼Œå…·ä½“å–决于您安装Kubernetes集群的托管æœåŠ¡æ供商。如果您使用的是Google Kubernetes Engine,则å¯ä»¥ %{pricingLink}。"
+
msgid "ClusterIntegration|API URL"
msgstr "API地å€"
@@ -1456,12 +1564,21 @@ msgstr "添加 Kubernetes 集群"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Kubernetes集群集æˆçš„高级选项"
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr "安装IngressåŽï¼Œæ‚¨éœ€è¦åœ¨ç”Ÿæˆçš„外部IP地å€ä¸ŠæŒ‡å‘DNS,以便在部署åŽæŸ¥çœ‹æ‚¨çš„应用程åºã€‚ %{ingressHelpLink}"
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr "å°è¯•èŽ·å–项目地域时å‘生错误:%{error}"
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr "å°è¯•èŽ·å–您的项目时å‘生错误:%{error}"
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr "å°è¯•èŽ·å–设备类型时å‘生错误:%{error}"
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr "å°è¯•è”ç³»Google Cloud APIæ—¶å‘生错误。请ç¨åŽå†è¯•ã€‚"
+
msgid "ClusterIntegration|Applications"
msgstr "应用程åº"
@@ -1474,11 +1591,11 @@ msgstr "CAè¯ä¹¦"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "è¯ä¹¦æŽˆæƒåŒ…(PEMæ ¼å¼)"
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "请选择使用此Kubernetes集群的环境。"
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr "选择è¦åœ¨ Kubernetes 群集上安装的应用程åºã€‚安装以下任何一个应用å‰éœ€è¦å…ˆå®‰è£…Helm Tiller。"
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr "控制Kubernetes集群与GitLab集æˆæ–¹å¼"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr "请选择使用此Kubernetes群集的环境。"
msgid "ClusterIntegration|Copy API URL"
msgstr "å¤åˆ¶API地å€"
@@ -1502,7 +1619,13 @@ msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr "创建Kubernetes集群"
msgid "ClusterIntegration|Did you know?"
-msgstr ""
+msgstr "你是å¦äº†è§£ï¼Ÿ"
+
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr "å¯ç”¨æˆ–ç¦ç”¨GitLab与Kubernetes群集的连接。"
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr "如果使用基于角色的访问控制(RBAC),请å¯ç”¨æ­¤è®¾ç½®ã€‚"
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "输入Kubernetes集群的详细信æ¯"
@@ -1528,6 +1651,9 @@ msgstr "GitLab集æˆ"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr "GitLab Runner连接到该项目的存储库并执行CI / CD作业,将结果推回,并部署应用程åºåˆ°ç”Ÿäº§çŽ¯å¢ƒã€‚"
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr "Google 云平å°é¡¹ç›®"
@@ -1540,8 +1666,11 @@ msgstr "Google Kubernetes Engine 项目"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr "Helm简化了Kubernetes应用程åºçš„安装和管ç†ã€‚ Tiller在您的Kubernetes集群内部è¿è¡Œï¼Œå¹¶ç®¡ç†å›¾è¡¨çš„å‘布。"
+
msgid "ClusterIntegration|Hide"
-msgstr ""
+msgstr "éšè—"
msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
msgstr "如果您正在设置多个群集并使用Auto DevOps,请%{help_link_start}先查阅此处%{help_link_end}。"
@@ -1555,6 +1684,9 @@ msgstr "Ingress"
msgid "ClusterIntegration|Ingress IP Address"
msgstr "Ingress IP地å€"
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr "Ingress为您æ供了一ç§åŸºäºŽè¯·æ±‚主机或路径将请求路由到æœåŠ¡çš„方法,将多个æœåŠ¡é›†ä¸­åˆ°ä¸€ä¸ªå…¥å£ç‚¹ã€‚"
+
msgid "ClusterIntegration|Install"
msgstr "安装"
@@ -1579,6 +1711,9 @@ msgstr "Jupyter主机å"
msgid "ClusterIntegration|JupyterHub"
msgstr "JupyterHub"
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr "JupyterHub是一个多用户Hub,它生æˆï¼Œç®¡ç†å’Œä»£ç†å•ç”¨æˆ· Jupyter笔记本æœåŠ¡å™¨çš„多个实例。 JupyterHubå¯ç”¨äºŽä¸ºä¸€ç±»å­¦ç”Ÿï¼Œä¼ä¸šæ•°æ®ç§‘å­¦å°ç»„或科研å°ç»„æ供笔记本电脑。"
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Kubernetes 集群"
@@ -1591,15 +1726,6 @@ msgstr "Kubernetes集群è¿è¡ŒçŠ¶å†µ"
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Kubernetes集群集æˆ"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "此项目已ç¦ç”¨ Kubernetes 集群集æˆã€‚"
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "此项目已å¯ç”¨ Kubernetes 集群集æˆã€‚"
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "此项目已å¯ç”¨ Kubernetes 集群集æˆã€‚ç¦ç”¨æ­¤é›†æˆä¸ä¼šå½±å“ Kubernetes 集群本身, åªä¼šæš‚时关闭 GitLab 与其连接。"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "正在Google Kubernetes Engine上创建Kubernetes集群..."
@@ -1624,12 +1750,6 @@ msgstr "进一步了解 %{help_link_start}Kubernetes%{help_link_end}。"
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr "进一步了解 %{help_link_start}地域%{help_link_end}。"
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "进一步了解有关环境的信æ¯"
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr "进一步了解安全相关é…ç½®"
-
msgid "ClusterIntegration|Machine type"
msgstr "机器类型"
@@ -1672,6 +1792,9 @@ msgstr "请输入Kubernetes集群的访问信æ¯ã€‚如需帮助,å¯ä»¥é˜…读Ku
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "请确ä¿æ‚¨çš„ Google å¸æˆ·ç¬¦åˆä»¥ä¸‹è¦æ±‚:"
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr "å°†DNS指å‘生æˆçš„这个IP地å€ï¼Œä»¥ä¾¿åœ¨éƒ¨ç½²åŽè®¿é—®æ‚¨çš„应用程åºã€‚"
+
msgid "ClusterIntegration|Project namespace"
msgstr "项目命å空间"
@@ -1681,6 +1804,12 @@ msgstr "项目命å空间(å¯é€‰ï¼Œå”¯ä¸€)"
msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr "Prometheus是一个开æºç›‘控系统,其中 %{gitlabIntegrationLink} 用于监控已部署的应用程åºã€‚"
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr "å¯ç”¨ RBAC 的群集 (实验功能)"
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr "请阅读关于Kubernetes集群集æˆçš„%{link_to_help_page}。"
@@ -1693,6 +1822,9 @@ msgstr "删除集æˆ"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "从当å‰é¡¹ç›®ä¸­åˆ é™¤æ­¤Kubernetes集群的é…置。该æ“作并ä¸ä¼šåˆ é™¤å®žé™…Kubernetes集群。"
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr "如果需è¦ï¼Œå¯å°†å…¶æ›¿æ¢ä¸ºæ‚¨è‡ªå·±çš„主机å。如果这样åšï¼Œè¯·å°†ä¸»æœºå从上级指å‘Ingress IP地å€ã€‚"
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "请求安装失败"
@@ -1708,9 +1840,6 @@ msgstr "æœç´¢é¡¹ç›®"
msgid "ClusterIntegration|Search zones"
msgstr "æœç´¢åœ°åŸŸ"
-msgid "ClusterIntegration|Security"
-msgstr "安全"
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "查看并编辑Kubernetes集群的详细信æ¯"
@@ -1747,12 +1876,18 @@ msgstr "在 Google Kubernetes Engine 上创建Kubernetes集群时å‘生错误"
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "安装 %{title} æ—¶å‘生故障"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr "默认集群é…ç½®æ供了æˆåŠŸæž„建和部署容器化应用所需的大é‡ç›¸å…³åŠŸèƒ½ã€‚"
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr "IP地å€æ­£åœ¨åˆ†é…中。如果花费时间过长,请检查您的Kubernetes集群或谷歌Kubernetes引擎(GKE) 上的é…é¢ã€‚"
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr "默认集群é…ç½®æ供了æˆåŠŸæž„建和部署容器化应用所需的众多相关功能。"
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "该å¸æˆ·éœ€å…·å¤‡åœ¨ä¸‹é¢æŒ‡å®šçš„%{link_to_container_project}中创建 Kubernetes集群的æƒé™"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr "此选项å…许您在å¯ç”¨RBAC的群集上安装应用程åºã€‚"
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "开关Kubernetes 集群"
@@ -1765,9 +1900,15 @@ msgstr "令牌"
msgid "ClusterIntegration|Validating project billing status"
msgstr "验è¯é¡¹ç›®è´¦å•çŠ¶æ€"
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr "无法验è¯æ‚¨åœ¨ GCP 上的æŸä¸ªé¡¹ç›®æ˜¯å¦å¯ç”¨äº†è®¡è´¹ã€‚请é‡è¯•ã€‚"
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "使用与此项目关è”çš„Kubernetes集群,å¯ä»¥æ–¹ä¾¿åœ°ä½¿ç”¨å®¡é˜…应用,部署应用程åºï¼Œè¿è¡Œæµæ°´çº¿ç­‰ç­‰ã€‚"
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr "在安装以下应用程åºä¹‹å‰ï¼Œå¿…须先安装Helm Tiller"
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "您的å¸æˆ·å¿…须拥有%{link_to_kubernetes_engine}"
@@ -1786,9 +1927,6 @@ msgstr "文档"
msgid "ClusterIntegration|help page"
msgstr "帮助页é¢"
-msgid "ClusterIntegration|installing applications"
-msgstr "安装应用程åº"
-
msgid "ClusterIntegration|meets the requirements"
msgstr "符åˆè¦æ±‚"
@@ -1798,11 +1936,14 @@ msgstr "正确é…ç½®"
msgid "ClusterIntegration|sign up"
msgstr "注册"
+msgid "Code owners"
+msgstr "代ç æ‰€æœ‰è€…"
+
msgid "Cohorts"
-msgstr ""
+msgstr "世代表"
msgid "Collapse"
-msgstr "收起"
+msgstr "折å "
msgid "Collapse sidebar"
msgstr "折å ä¾§è¾¹æ "
@@ -1845,6 +1986,9 @@ msgstr "æ交"
msgid "CommitMessage|Add %{file_name}"
msgstr "添加 %{file_name}"
+msgid "CommitWidget|authored"
+msgstr "作者"
+
msgid "Commits"
msgstr "æ交"
@@ -1912,22 +2056,19 @@ msgid "Confidential"
msgstr "机密"
msgid "Confidentiality"
-msgstr "ç§å¯†æ€§"
+msgstr "ä¿å¯†æ€§"
msgid "Configure Gitaly timeouts."
msgstr "é…ç½®Gitaly超时时间。"
-msgid "Configure Sidekiq job throttling."
-msgstr "é…ç½® Sidekiq 作业é™åˆ¶ã€‚"
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr "在仓库上é…置自动git检查和仓库整ç†ã€‚"
msgid "Configure limits for web and API requests."
msgstr "é…ç½® web å’Œ API 请求é™åˆ¶ã€‚"
-msgid "Configure push and pull mirrors."
-msgstr "é…ç½®PushåŠPullé•œåƒ"
+msgid "Configure push mirrors."
+msgstr "设置推é€çš„é•œåƒã€‚"
msgid "Configure storage path and circuit breaker settings."
msgstr "é…置存储路径åŠæ–­è·¯å™¨è®¾ç½®ã€‚"
@@ -2002,7 +2143,7 @@ msgid "Continue"
msgstr "继续"
msgid "Continue to the next step"
-msgstr ""
+msgstr "转到下一步"
msgid "Continuous Integration and Deployment"
msgstr "æŒç»­é›†æˆå’Œéƒ¨ç½²"
@@ -2016,6 +2157,9 @@ msgstr "贡献"
msgid "Contribution guide"
msgstr "贡献指å—"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr "<strong>%{calendar_date}</strong>的贡献"
+
msgid "Contributions per group member"
msgstr "æ¯å群组æˆå‘˜çš„贡献"
@@ -2035,7 +2179,7 @@ msgid "ContributorsPage|Please wait a moment, this page will automatically refre
msgstr "请ç¨å€™ï¼Œå›¾è¡¨æž„建完æˆåŽé¡µé¢ä¼šè‡ªåŠ¨åˆ·æ–°ã€‚"
msgid "Control the display of third party offers."
-msgstr ""
+msgstr "控制第三方优惠的显示。"
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr "控制此次è¦èŠ‚点的 åŒæ­¥LFS/附件的最大并å‘"
@@ -2047,7 +2191,16 @@ msgid "Control the maximum concurrency of verification operations for this Geo n
msgstr "控制此Geo节点的校验æ“作的最大并å‘性"
msgid "ConvDev Index"
-msgstr ""
+msgstr "ConvDev指数"
+
+msgid "Copy %{protocol} clone URL"
+msgstr "å¤åˆ¶ %{protocol} 克隆URL"
+
+msgid "Copy HTTPS clone URL"
+msgstr "å¤åˆ¶HTTPS克隆URL"
+
+msgid "Copy SSH clone URL"
+msgstr "å¤åˆ¶SSH克隆URL"
msgid "Copy SSH public key to clipboard"
msgstr "å°† SSH 公钥å¤åˆ¶åˆ°å‰ªè´´æ¿"
@@ -2068,7 +2221,7 @@ msgid "Copy file path to clipboard"
msgstr "将文件路径å¤åˆ¶åˆ°å‰ªè´´æ¿"
msgid "Copy incoming email address to clipboard"
-msgstr ""
+msgstr "将接收邮件地å€å¤åˆ¶åˆ°å‰ªè´´æ¿"
msgid "Copy reference to clipboard"
msgstr "将索引å¤åˆ¶åˆ°å‰ªè´´æ¿"
@@ -2077,7 +2230,7 @@ msgid "Copy to clipboard"
msgstr "å¤åˆ¶åˆ°å‰ªè´´æ¿"
msgid "Copy token to clipboard"
-msgstr ""
+msgstr "将令牌å¤åˆ¶åˆ°å‰ªè´´æ¿"
msgid "Create"
msgstr "创建"
@@ -2092,7 +2245,7 @@ msgid "Create a new branch and merge request"
msgstr "创建一个新的分支åŠåˆå¹¶è¯·æ±‚"
msgid "Create a new issue"
-msgstr ""
+msgstr "创建新议题"
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "在å¸æˆ·ä¸Šåˆ›å»ºä¸ªäººè®¿é—®ä»¤ç‰Œï¼Œä»¥é€šè¿‡ %{protocol} æ¥æ‹‰å–或推é€ã€‚"
@@ -2116,7 +2269,7 @@ msgid "Create file"
msgstr "创建文件"
msgid "Create group"
-msgstr ""
+msgstr "创建群组"
msgid "Create group label"
msgstr "创建群组标记"
@@ -2143,7 +2296,7 @@ msgid "Create new file"
msgstr "创建新文件"
msgid "Create new file or directory"
-msgstr ""
+msgstr "创建新文件或目录"
msgid "Create new label"
msgstr "创建新标记"
@@ -2154,9 +2307,6 @@ msgstr "创建..."
msgid "Create project label"
msgstr "创建项目标记"
-msgid "CreateNewFork|Fork"
-msgstr "派生"
-
msgid "CreateTag|Tag"
msgstr "标签"
@@ -2167,13 +2317,16 @@ msgid "Created"
msgstr "已创建"
msgid "Created At"
-msgstr ""
+msgstr "创建于"
msgid "Created by me"
msgstr "由我创建"
+msgid "Created on"
+msgstr "创建于"
+
msgid "Created on:"
-msgstr ""
+msgstr "创建于:"
msgid "Creating epic"
msgstr "创建å²è¯—故事中"
@@ -2184,6 +2337,9 @@ msgstr "Cron 时区"
msgid "Cron syntax"
msgstr "Cron 语法"
+msgid "Current Branch"
+msgstr "当å‰åˆ†æ”¯"
+
msgid "Current node"
msgstr "当å‰èŠ‚点"
@@ -2193,6 +2349,9 @@ msgstr "用户资料"
msgid "CurrentUser|Settings"
msgstr "设置"
+msgid "Custom"
+msgstr "自定义"
+
msgid "Custom CI config path"
msgstr "自定义CIé…置路径"
@@ -2202,18 +2361,24 @@ msgstr "自定义通知事件"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "自定义通知级别继承自å‚与级别。使用自定义通知级别,您会收到å‚与级别åŠé€‰å®šäº‹ä»¶çš„通知。想了解更多信æ¯ï¼Œè¯·æŸ¥çœ‹ %{notification_link}."
+msgid "Custom project templates"
+msgstr "自定义项目模æ¿"
+
msgid "Customize colors"
msgstr "自定义颜色"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "自定义如何将FogBugz电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLab。下一步将选择è¦å¯¼å…¥çš„项目。"
msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "自定义如何将Google Code电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLab。下一步将选择è¦å¯¼å…¥çš„项目。"
msgid "Cycle Analytics"
msgstr "周期分æž"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr "周期分æžæ¦‚述了项目从想法到产å“实现的å„阶段所需的时间。"
+
msgid "CycleAnalyticsStage|Code"
msgstr "ç¼–ç "
@@ -2236,7 +2401,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "测试"
msgid "Dashboard"
-msgstr ""
+msgstr "仪表盘"
msgid "DashboardProjects|All"
msgstr "所有"
@@ -2244,6 +2409,12 @@ msgstr "所有"
msgid "DashboardProjects|Personal"
msgstr "个人"
+msgid "Date picker"
+msgstr "日期选择器"
+
+msgid "Debug"
+msgstr "调试"
+
msgid "Dec"
msgstr "å二"
@@ -2253,14 +2424,17 @@ msgstr "å二月"
msgid "Decline and sign out"
msgstr "æ‹’ç»å¹¶é€€å‡º"
+msgid "Default Branch"
+msgstr "默认分支"
+
msgid "Default classification label"
msgstr "默认分类标记"
msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
+msgstr "默认:直接导入Google Code电å­é‚®ä»¶åœ°å€æˆ–用户å"
msgid "Default: Map a FogBugz account ID to a full name"
-msgstr ""
+msgstr "默认:将FogBugzå¸æˆ·ID映射为全å"
msgid "Define a custom pattern with cron syntax"
msgstr "使用 Cron 语法定义自定义模å¼"
@@ -2268,17 +2442,20 @@ msgstr "使用 Cron 语法定义自定义模å¼"
msgid "Delete"
msgstr "删除"
+msgid "Delete Package"
+msgstr "删除文件包"
+
msgid "Delete Snippet"
msgstr "删除代ç ç‰‡æ®µ"
msgid "Delete list"
-msgstr "删除清å•"
+msgstr "删除列表"
msgid "Deleted"
-msgstr ""
+msgstr "已删除"
msgid "Deny"
-msgstr ""
+msgstr "æ‹’ç»"
msgid "Deploy"
msgid_plural "Deploys"
@@ -2420,20 +2597,26 @@ msgid "Description templates allow you to define context-specific templates for
msgstr "æ述模æ¿å…许您为项目的问题和åˆå¹¶è¯·æ±‚定义æ述字段的特定模æ¿ã€‚"
msgid "Description:"
-msgstr ""
+msgstr "æè¿°:"
msgid "Destroy"
-msgstr ""
+msgstr "删除"
msgid "Details"
msgstr "详情"
+msgid "Detect host keys"
+msgstr "检测主机密钥"
+
msgid "Diffs|No file name available"
msgstr "没有å¯ç”¨çš„文件å"
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr "获å–差异线时å‘生错误。"
+msgid "Direction"
+msgstr "æ–¹å‘"
+
msgid "Directory name"
msgstr "目录å称"
@@ -2446,9 +2629,21 @@ msgstr "在此项目中ç¦ç”¨"
msgid "Disable group Runners"
msgstr "ç¦ç”¨ç¾¤ç»„Runner"
+msgid "Discard"
+msgstr "放弃"
+
+msgid "Discard all changes"
+msgstr "放弃所有更改"
+
+msgid "Discard all unstaged changes?"
+msgstr "放弃所有未暂存的修改?"
+
msgid "Discard changes"
msgstr "放弃更改"
+msgid "Discard changes to %{path}?"
+msgstr "放弃对 %{path} 的更改å—?"
+
msgid "Discard draft"
msgstr "å–消"
@@ -2456,10 +2651,10 @@ msgid "Discover GitLab Geo."
msgstr "å‘现GitLab Geo。"
msgid "Discover projects, groups and snippets. Share your projects with others"
-msgstr ""
+msgstr "æµè§ˆé¡¹ç›®ï¼Œç¾¤ç»„和代ç ç‰‡æ®µã€‚与他人分享您的项目"
msgid "Dismiss"
-msgstr ""
+msgstr "忽略"
msgid "Dismiss Cycle Analytics introduction box"
msgstr "关闭循环分æžä»‹ç»æ¡†"
@@ -2468,7 +2663,7 @@ msgid "Dismiss Merge Request promotion"
msgstr "关闭åˆå¹¶è¯·æ±‚推广"
msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
+msgstr "您想自定义如何将Google Code电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLabå—?"
msgid "Documentation for popular identity providers"
msgstr "常è§çš„身份验è¯æ供商的相关文档"
@@ -2534,13 +2729,13 @@ msgid "Edit Snippet"
msgstr "编辑代ç ç‰‡æ®µ"
msgid "Edit application"
-msgstr ""
+msgstr "编辑应用"
msgid "Edit files in the editor and commit changes here"
msgstr "在编辑器中编辑文件并在这里​​æ交å˜æ›´å†…容"
msgid "Edit group: %{group_name}"
-msgstr ""
+msgstr "编辑群组:%{group_name}"
msgid "Edit identity for %{user_name}"
msgstr "编辑 %{user_name} 的身份信æ¯"
@@ -2548,7 +2743,7 @@ msgstr "编辑 %{user_name} 的身份信æ¯"
msgid "Elasticsearch"
msgstr "Elasticsearch"
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr "Elasticsearch集æˆåŠElasticsearch AWS IAM。"
msgid "Email"
@@ -2600,7 +2795,7 @@ msgid "Enable or disable the Pseudonymizer data collection."
msgstr "å¯ç”¨æˆ–ç¦ç”¨åŒ¿å化数æ®æ”¶é›†."
msgid "Enable or disable version check and usage ping."
-msgstr "å¯ç”¨æˆ–ç¦ç”¨ç‰ˆæœ¬æ£€æŸ¥åŠä½¿ç”¨ping。"
+msgstr "å¯ç”¨æˆ–ç¦ç”¨ç‰ˆæœ¬æ£€æŸ¥åŠä½¿ç”¨æƒ…况检测(usage ping)。"
msgid "Enable reCAPTCHA or Akismet and set IP limits."
msgstr "å¯ç”¨reCAPTCHA或Akismet并设置IPé™åˆ¶ã€‚"
@@ -2608,12 +2803,33 @@ msgstr "å¯ç”¨reCAPTCHA或Akismet并设置IPé™åˆ¶ã€‚"
msgid "Enable the Performance Bar for a given group."
msgstr "对指定群组å¯ç”¨æ€§èƒ½æ ã€‚"
+msgid "Enable usage ping"
+msgstr "å¯ç”¨ä½¿ç”¨æƒ…况检测(usage ping)"
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr "å¯ç”¨ä½¿ç”¨æƒ…况检测(usage ping)以从功能角度总体上了解您如何使用GitLab。"
+
msgid "Enabled"
msgstr "å·²å¯ç”¨"
msgid "Ends at (UTC)"
msgstr "结æŸäºŽ(UTC)"
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr "输入您的BitbucketæœåŠ¡å™¨URL和个人访问令牌"
+
+msgid "Enter the issue description"
+msgstr "输入议题æè¿°"
+
+msgid "Enter the issue title"
+msgstr "输入议题标题"
+
+msgid "Enter the merge request description"
+msgstr "输入åˆå¹¶è¯·æ±‚说明"
+
+msgid "Enter the merge request title"
+msgstr "输入åˆå¹¶è¯·æ±‚标题"
+
msgid "Environments"
msgstr "环境"
@@ -2624,16 +2840,16 @@ msgid "Environments|An error occurred while making the request."
msgstr "å‘é€è¯·æ±‚æ—¶å‘生错误。"
msgid "Environments|An error occurred while stopping the environment, please try again"
-msgstr ""
+msgstr "终止环境时å‘生错误,请ç¨åŽé‡è¯•"
msgid "Environments|Are you sure you want to stop this environment?"
-msgstr ""
+msgstr "是å¦ç¡®å®šç»ˆæ­¢å½“å‰çŽ¯å¢ƒï¼Ÿ"
msgid "Environments|Commit"
msgstr "æ交"
msgid "Environments|Deploy to..."
-msgstr ""
+msgstr "部署到..."
msgid "Environments|Deployment"
msgstr "部署"
@@ -2644,11 +2860,14 @@ msgstr "环境"
msgid "Environments|Environments"
msgstr "环境"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr "环境是指部署代ç çš„ä½ç½®ï¼Œä¾‹å¦‚预生产或生产。"
+
msgid "Environments|Job"
msgstr "作业"
msgid "Environments|Learn more about stopping environments"
-msgstr ""
+msgstr "了解更多关于如何终止环境的信æ¯"
msgid "Environments|New environment"
msgstr "新建环境"
@@ -2659,32 +2878,35 @@ msgstr "未部署"
msgid "Environments|No pod name has been specified"
msgstr "未指定podå称"
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr "注æ„:继续æ“作将终止当å‰çŽ¯å¢ƒï¼ç”±äºŽæœªåœ¨%{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd}文件中定义“终止环境æ“作â€ï¼Œå› æ­¤%{emphasisStart}ä¸ä¼š%{emphasisEnd}å½±å“å·²ç»å­˜åœ¨çš„部署。"
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
-msgstr ""
+msgstr "注æ„:继续æ“作将终止当å‰çŽ¯å¢ƒï¼ç”±äºŽæœªåœ¨%{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end}文件中定义“终止环境æ“作â€ï¼Œå› æ­¤%{emphasis_start}ä¸ä¼š%{emphasis_end}å½±å“å·²ç»å­˜åœ¨çš„部署。"
msgid "Environments|Open live environment"
-msgstr ""
+msgstr "打开è¿è¡Œä¸­çš„环境"
msgid "Environments|Pod logs from"
msgstr "Pod日志æ¥è‡ªäºŽ"
msgid "Environments|Re-deploy to environment"
-msgstr ""
+msgstr "é‡æ–°éƒ¨ç½²è‡³çŽ¯å¢ƒ"
msgid "Environments|Read more about environments"
msgstr "了解有关环境的更多信æ¯"
msgid "Environments|Rollback environment"
-msgstr ""
+msgstr "回滚环境"
msgid "Environments|Show all"
msgstr "显示全部"
msgid "Environments|Stop"
-msgstr ""
+msgstr "终止"
msgid "Environments|Stop environment"
-msgstr ""
+msgstr "终止环境"
msgid "Environments|Updated"
msgstr "已更新"
@@ -2692,6 +2914,9 @@ msgstr "已更新"
msgid "Environments|You don't have any environments right now."
msgstr "当å‰æœªè®¾ç½®çŽ¯å¢ƒ"
+msgid "Environments|protected"
+msgstr "å—ä¿æŠ¤çš„"
+
msgid "Epic"
msgstr "å²è¯—故事"
@@ -2707,6 +2932,30 @@ msgstr "å²è¯—故事路线图"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr "利用å²è¯—故事(Epics),产å“线管ç†ä¼šå˜å¾—æ›´è½»æ¾ä¸”更高效"
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr "ä¿å­˜ %{epicDateType} 日期时å‘生错误"
+
+msgid "Epics|How can I solve this?"
+msgstr "我该如何解决该问题?"
+
+msgid "Epics|More information"
+msgstr "更多信æ¯"
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr "这些日期会影å“å²è¯—故事在路线图中的显示方å¼ã€‚里程碑日期æ¥è‡ªäºŽå²è¯—故事中的议题所属的里程碑。您还å¯ä»¥è®¾ç½®å›ºå®šæ—¥æœŸæˆ–完全删除它们。"
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr "如需根æ®é‡Œç¨‹ç¢‘æ¥å®‰æŽ’å²è¯—故事的 %{epicDateType} 日期,请为å²è¯—故事中的任一议题指定带有 %{epicDateType} 日期的里程碑。"
+
+msgid "Epics|due"
+msgstr "到期"
+
+msgid "Epics|start"
+msgstr "开始"
+
+msgid "Error"
+msgstr "错误"
+
msgid "Error Reporting and Logging"
msgstr "错误报告和日志记录"
@@ -2726,16 +2975,19 @@ msgid "Error fetching refs"
msgstr "获å–refs时出错。"
msgid "Error fetching usage ping data."
-msgstr "获å–使用情况(usage ping) æ•°æ®æ—¶å‡ºé”™ã€‚"
+msgstr "获å–使用情况(usage ping)æ•°æ®æ—¶å‡ºé”™ã€‚"
msgid "Error loading branch data. Please try again."
msgstr "加载分支数æ®å¤±è´¥ï¼Œè¯·é‡è¯•ã€‚"
+msgid "Error loading branches."
+msgstr "加载分支时出错。"
+
msgid "Error loading last commit."
msgstr "加载最åŽä¸€æ¬¡æ交失败。"
msgid "Error loading markdown preview"
-msgstr ""
+msgstr "加载Markdown预览时出错"
msgid "Error loading merge requests."
msgstr "加载åˆå¹¶è¯·æ±‚时出错。"
@@ -2743,6 +2995,12 @@ msgstr "加载åˆå¹¶è¯·æ±‚时出错。"
msgid "Error loading project data. Please try again."
msgstr "加载项目数æ®å¤±è´¥ï¼Œè¯·é‡è¯•ã€‚"
+msgid "Error loading template types."
+msgstr "加载模æ¿ç±»åž‹æ—¶å‡ºé”™ã€‚"
+
+msgid "Error loading template."
+msgstr "加载模æ¿æ—¶å‡ºé”™ã€‚"
+
msgid "Error occurred when toggling the notification subscription"
msgstr "切æ¢é€šçŸ¥è®¢é˜…æ—¶å‘生错误"
@@ -2755,6 +3013,9 @@ msgstr "更新所有待办事项的状æ€æ—¶å‡ºé”™ã€‚"
msgid "Error updating todo status."
msgstr "更新待办事项状æ€æ—¶å‡ºé”™ã€‚"
+msgid "Error while loading the merge request. Please try again."
+msgstr "加载åˆå¹¶è¯·æ±‚时出错。请å†è¯•ä¸€æ¬¡ã€‚"
+
msgid "Estimated"
msgstr "预计"
@@ -2786,7 +3047,7 @@ msgid "Every week (Sundays at 4:00am)"
msgstr "æ¯å‘¨æ‰§è¡Œï¼ˆå‘¨æ—¥å‡Œæ™¨ 4 点)"
msgid "Everyone can contribute"
-msgstr ""
+msgstr "人人皆å¯è´¡çŒ®"
msgid "Expand"
msgstr "展开"
@@ -2797,20 +3058,23 @@ msgstr "展开全部"
msgid "Expand sidebar"
msgstr "展开侧边æ "
+msgid "Expiration date"
+msgstr "到期时间"
+
msgid "Explore"
-msgstr ""
+msgstr "探索"
msgid "Explore GitLab"
-msgstr ""
+msgstr "探索GitLab"
msgid "Explore Groups"
-msgstr ""
+msgstr "æµè§ˆç¾¤ç»„"
msgid "Explore groups"
msgstr "æµè§ˆç¾¤ç»„"
msgid "Explore projects"
-msgstr "查看项目"
+msgstr "æµè§ˆé¡¹ç›®"
msgid "Explore public groups"
msgstr "æœç´¢å…¬å…±ç¾¤ç»„"
@@ -2837,7 +3101,7 @@ msgid "ExternalAuthorizationService|When no classification label is set the defa
msgstr "未设置分类标签的时候,将使用默认的分类标签`%{default_label}`。"
msgid "Facebook"
-msgstr ""
+msgstr "Facebook"
msgid "Failed"
msgstr "已失败"
@@ -2854,6 +3118,9 @@ msgstr "无法检查相关分支。"
msgid "Failed to remove issue from board, please try again."
msgstr "无法从看æ¿ç§»é™¤è®®é¢˜ï¼Œè¯·é‡è¯•ã€‚"
+msgid "Failed to remove mirror."
+msgstr "删除镜åƒå¤±è´¥ã€‚"
+
msgid "Failed to remove the pipeline schedule"
msgstr "无法删除æµæ°´çº¿è®¡åˆ’"
@@ -2875,6 +3142,9 @@ msgstr "二月"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "当å‰é¡µé¢ä¸Šçš„字段ä¸å¯ç¼–辑,å¯ä»¥é…ç½®"
+msgid "File templates"
+msgstr "文件模æ¿"
+
msgid "Files"
msgstr "文件"
@@ -2885,11 +3155,20 @@ msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and p
msgstr "填写下é¢çš„字段,å¯ç”¨<strong>%{enable_label}</strong>,然åŽç‚¹å‡»<strong>%{save_changes}</strong>"
msgid "Filter"
+msgstr "筛选器"
+
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
msgstr ""
msgid "Filter by commit message"
msgstr "按æ交消æ¯è¿‡æ»¤"
+msgid "Filter..."
+msgstr "过滤..."
+
msgid "Find by path"
msgstr "按路径查找"
@@ -2897,10 +3176,13 @@ msgid "Find file"
msgstr "查找文件"
msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
+msgstr "找到下载的ZIP文件并解压缩。"
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
-msgstr ""
+msgstr "查找新æå–çš„ <code>Takeout/Google Code项目托管/GoogleCodeProjectHosting. json</code> 文件。"
+
+msgid "Fingerprints"
+msgstr "指纹"
msgid "Finished"
msgstr "已完æˆ"
@@ -2911,23 +3193,35 @@ msgstr "首次推é€"
msgid "FirstPushedBy|pushed by"
msgstr "推é€è€…:"
+msgid "Fixed date"
+msgstr "ä¿®å¤æ—¥æœŸ"
+
+msgid "Fixed due date"
+msgstr "固定截止日期"
+
+msgid "Fixed start date"
+msgstr "ä¿®å¤å¼€å§‹æ—¥æœŸ"
+
+msgid "Fixed:"
+msgstr "ä¿®å¤ï¼š"
+
msgid "FogBugz Email"
-msgstr ""
+msgstr "FogBugz电å­é‚®ä»¶"
msgid "FogBugz Import"
-msgstr ""
+msgstr "FogBugz导入"
msgid "FogBugz Password"
-msgstr ""
+msgstr "FogBugz密ç "
msgid "FogBugz URL"
-msgstr ""
+msgstr "FogBugz网å€"
msgid "FogBugz import"
-msgstr ""
+msgstr "FogBugz导入"
msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
+msgstr "请按照以下步骤导出您在Google Code上的项目数æ®ã€‚"
msgid "Font Color"
msgstr "字体颜色"
@@ -2938,16 +3232,18 @@ msgstr "页脚消æ¯"
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr "对于内部项目,任何已登录的用户都å¯ä»¥æŸ¥çœ‹æµæ°´çº¿å¹¶è®¿é—®ä½œä¸šè¯¦æƒ…(输出日志和工件)"
+msgid "For more information, go to the "
+msgstr "如需了解详细信æ¯ï¼Œè¯·å‚阅"
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr "欲了解更多相关信æ¯ï¼Œè¯·å‚阅 %{deactivating_usage_ping_link_start}使用情况检测(usage ping)%{deactivating_usage_ping_link_end}的文档。"
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr "对于ç§æœ‰é¡¹ç›®ï¼Œä»»ä½•æˆå‘˜ï¼ˆè®¿å®¢æˆ–更高级别)都å¯ä»¥æŸ¥çœ‹æµæ°´çº¿å¹¶è®¿é—®ä½œä¸šè¯¦æƒ…(输出日志和工件)"
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr "对于公共项目,任何人都å¯ä»¥æŸ¥çœ‹æµæ°´çº¿å¹¶è®¿é—®ä½œä¸šè¯¦æƒ…(输出日志和工件)"
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "派生"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "派生自"
@@ -2967,16 +3263,19 @@ msgid "From %{provider_title}"
msgstr "æ¥è‡ª %{provider_title}"
msgid "From Bitbucket"
-msgstr ""
+msgstr "æ¥è‡ªBitbucket"
+
+msgid "From Bitbucket Server"
+msgstr "æ¥è‡ª Bitbucket Server"
msgid "From FogBugz"
-msgstr ""
+msgstr "æ¥è‡ªFogBugz"
msgid "From GitLab.com"
-msgstr ""
+msgstr "æ¥è‡ªGitLab.com"
msgid "From Google Code"
-msgstr ""
+msgstr "æ¥è‡ªGoogle Code"
msgid "From issue creation until deploy to production"
msgstr "从创建议题到部署至生产环境"
@@ -2984,6 +3283,9 @@ msgstr "从创建议题到部署至生产环境"
msgid "From merge request merge until deploy to production"
msgstr "从åˆå¹¶è¯·æ±‚被åˆå¹¶åŽåˆ°éƒ¨ç½²è‡³ç”Ÿäº§çŽ¯å¢ƒ"
+msgid "From milestones:"
+msgstr "æ¥è‡ªé‡Œç¨‹ç¢‘:"
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "在Kubernetes集群详细信æ¯è§†å›¾ä¸­ï¼Œä»Žåº”用程åºåˆ—表中安装Runner"
@@ -2999,6 +3301,9 @@ msgstr "一般æµæ°´çº¿"
msgid "Generate a default set of labels"
msgstr "生æˆä¸€ç»„默认的标记"
+msgid "Geo"
+msgstr "Geo"
+
msgid "Geo Nodes"
msgstr "Geo 节点"
@@ -3164,36 +3469,135 @@ msgstr "已与主节点上对应项验è¯çš„Wiki"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr "当å‰Geo节点é…置使用ä¸å®‰å…¨çš„HTTP连接, 建议使用HTTPS。"
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr "%{name} 计划强制é‡æ–°ä¸‹è½½"
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr "%{name} 计划é‡æ–°æ£€æŸ¥"
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr "%{name} 已计划é‡æ–°åŒæ­¥"
+
msgid "Geo|All projects"
msgstr "所有项目"
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr "错误消æ¯"
+
+msgid "Geo|Failed"
+msgstr "失败"
+
msgid "Geo|File sync capacity"
msgstr "文件åŒæ­¥å®¹é‡"
msgid "Geo|Groups to synchronize"
msgstr "需åŒæ­¥çš„群组"
+msgid "Geo|In sync"
+msgstr "å·²åŒæ­¥"
+
+msgid "Geo|Last successful sync"
+msgstr "最近一次æˆåŠŸçš„åŒæ­¥"
+
+msgid "Geo|Last sync attempt"
+msgstr "最近一次å°è¯•åŒæ­¥"
+
+msgid "Geo|Last time verified"
+msgstr "最近一次验è¯"
+
+msgid "Geo|Never"
+msgstr "从ä¸"
+
+msgid "Geo|Next sync scheduled at"
+msgstr "下一次åŒæ­¥å®‰æŽ’在"
+
+msgid "Geo|No errors"
+msgstr "无错误"
+
+msgid "Geo|Pending"
+msgstr "待定"
+
+msgid "Geo|Pending synchronization"
+msgstr "å¾…åŒæ­¥"
+
+msgid "Geo|Pending verification"
+msgstr "待验è¯"
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr "指定群组中的项目"
msgid "Geo|Projects in certain storage shards"
msgstr "特定存储片中的项目"
+msgid "Geo|Recheck"
+msgstr "é‡æ–°æ£€æŸ¥"
+
+msgid "Geo|Redownload"
+msgstr "é‡æ–°ä¸‹è½½"
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr "仓库åŒæ­¥å®¹é‡"
+msgid "Geo|Resync"
+msgstr "é‡æ–°åŒæ­¥"
+
+msgid "Geo|Retry count"
+msgstr "é‡è¯•è®¡æ•°"
+
+msgid "Geo|Retry counts"
+msgstr "é‡è¯•è®¡æ•°"
+
msgid "Geo|Select groups to replicate."
msgstr "选择è¦å¤åˆ¶çš„群组。"
msgid "Geo|Shards to synchronize"
msgstr "需åŒæ­¥çš„存储片"
+msgid "Geo|Status"
+msgstr "状æ€"
+
+msgid "Geo|Synced"
+msgstr "åŒæ­¥"
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr "åŒæ­¥å¤±è´¥ - %{error}"
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr "未知状æ€"
+
msgid "Geo|Verification capacity"
msgstr "校验能力"
-msgid "Git"
+msgid "Geo|Verification failed - %{error}"
+msgstr "验è¯å¤±è´¥ - %{error}"
+
+msgid "Geo|Waiting for scheduler"
+msgstr "等待调度"
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr "您需è¦ä¸åŒçš„许å¯è¯æ‰èƒ½ä½¿ç”¨ geo å¤åˆ¶"
+
+msgid "Get a free instance review"
msgstr ""
+msgid "Git"
+msgstr "Git"
+
msgid "Git repository URL"
msgstr "Git仓库URL"
@@ -3222,13 +3626,13 @@ msgid "GitLab Group Runners can execute code for all the projects in this group.
msgstr "Gitlab群组Runnerå¯ä»¥ç”¨æ¥è¿è¡Œç¾¤ç»„内所有项目的代ç ã€‚"
msgid "GitLab Import"
-msgstr ""
+msgstr "GitLab导入"
msgid "GitLab User"
-msgstr ""
+msgstr "GitLab用户"
msgid "GitLab project export"
-msgstr ""
+msgstr "GitLab项目导出"
msgid "GitLab single sign on URL"
msgstr "GitLab SSO 地å€"
@@ -3237,7 +3641,10 @@ msgid "GitLab will run a background job that will produce pseudonymized CSVs of
msgstr "GitLabå°†è¿è¡ŒåŽå°ä»»åŠ¡ï¼Œç”Ÿæˆæ•°æ®åº“的匿å化CSV,并上传到预先设定的对象存储目录。"
msgid "GitLab.com import"
-msgstr ""
+msgstr "从GitLab.com导入"
+
+msgid "GitLab’s issue tracker"
+msgstr "GitLab 问题跟踪器"
msgid "Gitaly"
msgstr "Gitaly"
@@ -3249,10 +3656,10 @@ msgid "Gitaly|Address"
msgstr "地å€"
msgid "Gitea Host URL"
-msgstr ""
+msgstr "Gitea 主机地å€"
msgid "Gitea Import"
-msgstr ""
+msgstr "从Gitea导入"
msgid "Go Back"
msgstr "返回"
@@ -3260,20 +3667,17 @@ msgstr "返回"
msgid "Go back"
msgstr "返回"
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
-msgid "Go to your fork"
-msgstr "跳转到派生项目"
+msgid "Go to"
+msgstr "转到"
-msgid "GoToYourFork|Fork"
-msgstr "跳转到派生项目"
+msgid "Go to %{link_to_google_takeout}."
+msgstr "转至 %{link_to_google_takeout}。"
msgid "Google Code import"
-msgstr ""
+msgstr "从Google Code导入"
msgid "Google Takeout"
-msgstr ""
+msgstr "Google Takeout"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Google 身份验è¯ä¸æ˜¯%{link_to_documentation}。如果您想使用此æœåŠ¡ï¼Œè¯·å’¨è¯¢æ‚¨çš„ GitLab 管ç†å‘˜ã€‚"
@@ -3282,16 +3686,16 @@ msgid "Got it!"
msgstr "了解ï¼"
msgid "Graph"
-msgstr "图表"
+msgstr "分æžå›¾"
msgid "Group"
-msgstr ""
+msgstr "群组"
msgid "Group CI/CD settings"
msgstr "群组 CI/CD 设置"
msgid "Group Git LFS status:"
-msgstr ""
+msgstr "群组Git LFS状æ€ï¼š"
msgid "Group ID"
msgstr "群组 ID"
@@ -3300,19 +3704,19 @@ msgid "Group Runners"
msgstr "群组Runner"
msgid "Group avatar"
-msgstr ""
+msgstr "群组头åƒ"
msgid "Group details"
-msgstr ""
+msgstr "群组详细信æ¯"
msgid "Group info:"
-msgstr ""
+msgstr "群组信æ¯"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "群组维护者å¯ä»¥åœ¨é€šè¿‡ %{link} 注册群组级 Runner"
msgid "Group: %{group_name}"
-msgstr ""
+msgstr "群组:%{group_name}"
msgid "GroupRoadmap|From %{dateWord}"
msgstr "从 %{dateWord} 起"
@@ -3329,14 +3733,14 @@ msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ä»»ä½•ç¬¦åˆæ¡ä»¶çš„å²è¯—故事"
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr "路线图显示了å²è¯—故事沿ç€æ—¶é—´çº¿çš„进展情况"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在月视图中,åªæ˜¾ç¤ºä¸Šä¸ªæœˆï¼Œæœ¬æœˆä»¥åŠæŽ¥ä¸‹æ¥5个月的å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "如需查看路线图,请将开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在月视图中,åªæ˜¾ç¤ºä¸Šä¸ªæœˆï¼Œæœ¬æœˆä»¥åŠæŽ¥ä¸‹æ¥5个月的å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在季度视图中,åªæ˜¾ç¤ºä¸Šä¸ªå­£åº¦ï¼Œæœ¬å­£åº¦ä»¥åŠæŽ¥ä¸‹æ¥4个季度的å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "如需查看路线图,请将开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在季度视图中,åªæ˜¾ç¤ºä¸Šä¸ªå­£åº¦ï¼Œæœ¬å­£åº¦ä»¥åŠæŽ¥ä¸‹æ¥4个季度的å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在周视图中,åªæ˜¾ç¤ºä¸Šå‘¨ï¼Œæœ¬å‘¨ä»¥åŠæŽ¥ä¸‹æ¥å››å‘¨çš„å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "如需查看路线图,请将开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在周视图中,åªæ˜¾ç¤ºä¸Šå‘¨ï¼Œæœ¬å‘¨ä»¥åŠæŽ¥ä¸‹æ¥å››å‘¨çš„å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除过滤器。在月视图中,åªæ˜¾ç¤ºä¸Šæœˆï¼Œæœ¬æœˆå’ŒæŽ¥ä¸‹æ¥çš„四个月的å²è¯—故事 &ndash; 从 %{startDate} 到 %{endDate} 。"
@@ -3350,6 +3754,15 @@ msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除过滤器。在周视图中,
msgid "GroupRoadmap|Until %{dateWord}"
msgstr "直到 %{dateWord}"
+msgid "GroupSettings|Badges"
+msgstr "徽章"
+
+msgid "GroupSettings|Customize your group badges."
+msgstr "自定义群组徽章。"
+
+msgid "GroupSettings|Learn more about badges."
+msgstr "了解有关徽章的更多信æ¯ã€‚"
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "ç¦æ­¢ä¸Žå…¶ä»–群组共享 %{group} 中的项目"
@@ -3375,7 +3788,7 @@ msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name
msgstr "从 %{ancestor_group_name} 中删除共享群组é”"
msgid "Groups"
-msgstr ""
+msgstr "群组"
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr "也å¯ä»¥é€šè¿‡åˆ›å»º %{subgroup_docs_link_start}å­ç¾¤ç»„æ¥åµŒå¥—群组%{subgroup_docs_link_end}。"
@@ -3396,7 +3809,7 @@ msgid "GroupsDropdown|Something went wrong on our end."
msgstr "å‘生了内部错误."
msgid "GroupsDropdown|Sorry, no groups matched your search"
-msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ç¬¦åˆæ¡ä»¶çš„群组"
+msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ä»»ä½•ç¬¦åˆçš„群组"
msgid "GroupsDropdown|This feature requires browser localStorage support"
msgstr "此功能需è¦æµè§ˆå™¨æœ¬åœ°å­˜å‚¨æ”¯æŒ"
@@ -3413,6 +3826,9 @@ msgstr "找ä¸åˆ°ç¾¤ç»„"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "您å¯ä»¥ç®¡ç†ç¾¤ç»„æˆå‘˜çš„æƒé™å¹¶è®¿é—®ç¾¤ç»„中的æ¯ä¸ªé¡¹ç›®ã€‚"
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr "您确定è¦é€€å‡ºç¾¤ç»„“%{fullName}â€å—?"
+
msgid "GroupsTree|Create a project in this group."
msgstr "在此群组中创建一个项目。"
@@ -3425,20 +3841,20 @@ msgstr "编辑群组"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr "无法离开群组。请确ä¿æ‚¨ä¸æ˜¯å”¯ä¸€çš„所有者。"
-msgid "GroupsTree|Filter by name..."
-msgstr "按å称过滤..."
-
msgid "GroupsTree|Leave this group"
msgstr "离开这个群组"
msgid "GroupsTree|Loading groups"
msgstr "加载群组中"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ä»»ä½•ç¬¦åˆçš„群组"
+msgid "GroupsTree|No groups matched your search"
+msgstr "没有æœç´¢åˆ°ä»»ä½•ç¬¦åˆçš„群组"
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰ä»»ä½•ç¾¤ç»„或项目符åˆæ‚¨çš„æœç´¢"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr "没有任何群组或项目符åˆæ‚¨çš„æœç´¢"
+
+msgid "GroupsTree|Search by name"
+msgstr "按å称æœç´¢"
msgid "Have your users email"
msgstr "请让用户å‘é€ç”µå­é‚®ä»¶è‡³"
@@ -3473,6 +3889,15 @@ msgstr "帮助页é¢"
msgid "Help page text and support page url."
msgstr "帮助页é¢æ–‡æœ¬å’Œæ”¯æŒé¡µé¢ç½‘å€ã€‚"
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr "以下是需è¦æ·»åŠ åˆ°è¿œç¨‹æœåŠ¡å™¨çš„SSH公钥。有关更多信æ¯ï¼Œè¯·å‚阅文档。"
+
+msgid "Hide host keys manual input"
+msgstr "手工输入éšè—热键"
+
+msgid "Hide payload"
+msgstr "éšè—有效数æ®"
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "éšè—值"
@@ -3495,21 +3920,45 @@ msgstr "æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–"
msgid "ID"
msgstr "ID"
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr "å…许Web IDE中的JavaScript项目使用CodeSandbox客户端的实时预览。"
+
+msgid "IDE|Back"
+msgstr "返回"
+
+msgid "IDE|Client side evaluation"
+msgstr "客户端评估"
+
msgid "IDE|Commit"
msgstr "æ交"
msgid "IDE|Edit"
msgstr "编辑"
-msgid "IDE|Go back"
-msgstr "返回"
+msgid "IDE|Get started with Live Preview"
+msgstr "开始时预览"
+
+msgid "IDE|Go to project"
+msgstr "转到项目"
+
+msgid "IDE|Live Preview"
+msgstr "实时预览"
msgid "IDE|Open in file view"
msgstr "在文件视图中打开"
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr "IDE|使用Web IDE客户端评估æ¥é¢„览您的web应用。"
+
+msgid "IDE|Refresh preview"
+msgstr "刷新预览"
+
msgid "IDE|Review"
msgstr "审阅"
+msgid "IP Address"
+msgstr "IP地å€"
+
msgid "Identifier"
msgstr "身份标识"
@@ -3519,6 +3968,9 @@ msgstr "身份标识"
msgid "Identity provider single sign on URL"
msgstr "身份验è¯æ供商å•ç‚¹ç™»å½•URL"
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr "如果ç¦ç”¨ï¼Œåˆ™ä¸ä¼šä½¿ç”¨è¿œç¨‹å‰¯æœ¬çš„æ交自动更新分å‰çš„本地分支,以防止本地数æ®ä¸¢å¤±ã€‚如果默认分支 (%{default_branch}) 已分å‰ä¸”无法更新,则镜åƒå°†å¤±è´¥ã€‚其他分å‰çš„分支默默被忽略。"
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr "如果ç¦ç”¨ï¼Œåˆ™è®¿é—®çº§åˆ«å°†å–决于用户在项目中的æƒé™ã€‚"
@@ -3550,40 +4002,46 @@ msgid "Import"
msgstr "导入"
msgid "Import Projects from Gitea"
-msgstr ""
+msgstr "从Gitea导入项目"
msgid "Import all compatible projects"
-msgstr ""
+msgstr "导入所有兼容的项目"
msgid "Import all projects"
-msgstr ""
+msgstr "导入所有项目"
msgid "Import all repositories"
msgstr "导入所有仓库"
msgid "Import an exported GitLab project"
-msgstr ""
+msgstr "导入一个从GitLab导出的项目"
msgid "Import in progress"
msgstr "正在导入"
msgid "Import multiple repositories by uploading a manifest file."
-msgstr ""
+msgstr "通过上传manifest文件导入多个仓库"
msgid "Import project"
-msgstr ""
+msgstr "导入项目"
msgid "Import projects from Bitbucket"
-msgstr ""
+msgstr "从Bitbucket导入项目"
+
+msgid "Import projects from Bitbucket Server"
+msgstr "从Bitbucket导入项目"
msgid "Import projects from FogBugz"
-msgstr ""
+msgstr "从FogBugz导入项目"
msgid "Import projects from GitLab.com"
-msgstr ""
+msgstr "从GitLab.com导入项目"
msgid "Import projects from Google Code"
-msgstr ""
+msgstr "从Google Code导入项目"
+
+msgid "Import repositories from Bitbucket Server"
+msgstr "从Bitbucket导入存储库"
msgid "Import repositories from GitHub"
msgstr "从 GitHub 导入仓库"
@@ -3603,20 +4061,35 @@ msgstr "使用GitLab ä¼ä¸šç‰ˆè®®é¢˜æƒé‡å¸¦æ¥çš„增强议题管ç†åŠŸèƒ½ã€‚"
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr "使用GitLabä¼ä¸šç‰ˆå…¨å±€æœç´¢å¸¦æ¥çš„增强æœç´¢åŠŸèƒ½"
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr "è¦å¯ç”¨å®žä¾‹çº§åˆ†æžï¼Œè¯·è¦æ±‚管ç†å‘˜å¯ç”¨ %{usage_ping_link_start}使用情况检测(usage ping)%{usage_ping_link_end}。"
+
msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
+msgstr "继续下一步,选择想è¦å¯¼å…¥çš„项目"
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr "包括所有用户必须接å—çš„æœåŠ¡æ¡æ¬¾å议和éšç§æ”¿ç­–。"
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr "如果需è¦ï¼Œè¯·åœ¨URL中包å«ç”¨æˆ·å: <code>https://username@gitlab.company.com/group/project.git</code>。"
+
msgid "Incompatible Project"
-msgstr ""
+msgstr "ä¸å…¼å®¹çš„项目"
+
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr "指示此runner是å¦å¯ä»¥é€‰æ‹©æ²¡æœ‰æ ‡è®°çš„作业"
msgid "Inline"
msgstr "内è”"
+msgid "Input host keys manually"
+msgstr "手动输入主机密钥"
+
+msgid "Input your repository URL"
+msgstr "输入您的存储库URL"
+
msgid "Install GitLab Runner"
-msgstr ""
+msgstr "安装GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "在Kubernetes上安装Runner"
@@ -3625,11 +4098,17 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] "实例"
+msgid "Instance Statistics"
+msgstr "实例统计"
+
+msgid "Instance Statistics visibility"
+msgstr "实例统计信æ¯å¯è§æ€§"
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr "实例ä¸æ”¯æŒå¤šä¸ªKubernetes集群"
msgid "Integrations"
-msgstr "导入所有仓库"
+msgstr "集æˆ"
msgid "Integrations Settings"
msgstr "集æˆè®¾ç½®"
@@ -3643,12 +4122,21 @@ msgstr "内部 - 任何登录的用户都å¯ä»¥æŸ¥çœ‹è¯¥ç¾¤ç»„和任何内部项
msgid "Internal - The project can be accessed by any logged in user."
msgstr "内部 - å¯ä»¥é€šè¿‡ä»»ä½•ç™»å½•ç”¨æˆ·è®¿é—®è¯¥é¡¹ç›®ã€‚"
+msgid "Internal users"
+msgstr "内部用户"
+
msgid "Interval Pattern"
msgstr "循环周期"
msgid "Introducing Cycle Analytics"
msgstr "周期分æžç®€ä»‹"
+msgid "Invite"
+msgstr "邀请"
+
+msgid "Issue"
+msgstr "议题"
+
msgid "Issue Boards"
msgstr "议题看æ¿"
@@ -3671,7 +4159,7 @@ msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are sear
msgstr "议题å¯ä»¥æ˜¯ç¼ºé™·ï¼Œä»»åŠ¡æˆ–è¦è®¨è®ºçš„想法。此外,å¯ä»¥é€šè¿‡æœç´¢å’Œè¿‡æ»¤æ¥æŸ¥æ‰¾è®®é¢˜ã€‚"
msgid "Issues closed"
-msgstr "议题已关闭"
+msgstr "关闭议题"
msgid "Jan"
msgstr "一"
@@ -3688,6 +4176,48 @@ msgstr "作业已被删除"
msgid "Jobs"
msgstr "作业"
+msgid "Job|Browse"
+msgstr "æµè§ˆ"
+
+msgid "Job|Complete Raw"
+msgstr "完整原"
+
+msgid "Job|Download"
+msgstr "下载"
+
+msgid "Job|Erase job log"
+msgstr "删除作业日志"
+
+msgid "Job|Job artifacts"
+msgstr "作业产物"
+
+msgid "Job|Job has been erased"
+msgstr "作业已被删除"
+
+msgid "Job|Job has been erased by"
+msgstr "作业已被删除"
+
+msgid "Job|Keep"
+msgstr "ä¿æŒ"
+
+msgid "Job|Scroll to bottom"
+msgstr "滚动到底部"
+
+msgid "Job|Scroll to top"
+msgstr "滚动到顶部"
+
+msgid "Job|Show complete raw"
+msgstr "显示完整æº"
+
+msgid "Job|The artifacts were removed"
+msgstr "作业产物已被删除"
+
+msgid "Job|The artifacts will be removed in"
+msgstr "作业产物将被删除于"
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr "此作业已åœæ­¢ï¼Œå› ä¸ºæ²¡æœ‰ä»»ä½•åœ¨çº¿çš„runner被分é…给该项目。"
+
msgid "Jul"
msgstr "七"
@@ -3700,12 +4230,6 @@ msgstr "å…­"
msgid "June"
msgstr "六月"
-msgid "Koding"
-msgstr "Koding"
-
-msgid "Koding Dashboard"
-msgstr ""
-
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -3775,6 +4299,9 @@ msgstr "<span>将标记</span> %{labelTitle} <span>å‡çº§ä¸ºç¾¤ç»„标记?</spa
msgid "Labels|Promote Label"
msgstr "å‡çº§æ ‡è®°"
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr "æå‡ %{labelTitle} 将使其å¯ç”¨äºŽ %{groupName} 内的所有项目。现有的åŒå项目标记将被åˆå¹¶ã€‚该æ“作ä¸å¯æ’¤é”€ã€‚"
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "最近 %d 天"
@@ -3785,6 +4312,9 @@ msgstr "最新æµæ°´çº¿"
msgid "Last commit"
msgstr "最åŽæ交"
+msgid "Last contact"
+msgstr "最åŽè”ç³»"
+
msgid "Last edited %{date}"
msgstr "最åŽä¿®æ”¹ %{date}"
@@ -3809,6 +4339,9 @@ msgstr "最新更改"
msgid "Learn more"
msgstr "进一步了解"
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr "为了了解更多关于 %{issue_boards_url},在多个列表中ä¿æŒå¯¹è®®é¢˜çš„追踪,您å¯ä»¥ä½¿ç”¨ä½¿ç”¨æ ‡è®°ï¼ŒæŒ‡æ´¾äººï¼Œå’Œé‡Œç¨‹ç¢‘。 如果你å‘现议题看æ¿ä¸Šä¸¢å¤±äº†ä¸€äº›ä¿¡æ¯ï¼Œè¯·åœ¨ %{gitlab_issues_url} 创建一个议题。"
+
msgid "Learn more about Kubernetes"
msgstr "进一步了解关于Kubernetesçš„ä¿¡æ¯"
@@ -3831,26 +4364,87 @@ msgid "Leave project"
msgstr "退出项目"
msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
+msgstr "使用默认值设定 \"文件类型\" 和 \"交付方法\""
msgid "License"
msgstr "许å¯è¯"
+msgid "LicenseManagement|Approve license"
+msgstr "批准许å¯è¯"
+
+msgid "LicenseManagement|Approve license?"
+msgstr "批准许å¯è¯ï¼Ÿ"
+
+msgid "LicenseManagement|Approved"
+msgstr "已批准"
+
+msgid "LicenseManagement|Blacklist license"
+msgstr "许å¯è¯é»‘åå•"
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr "许å¯è¯é»‘åå•ï¼Ÿ"
+
+msgid "LicenseManagement|Blacklisted"
+msgstr "黑åå•"
+
+msgid "LicenseManagement|License"
+msgstr "许å¯è¯"
+
+msgid "LicenseManagement|License Management"
+msgstr "许å¯è¯ç®¡ç†"
+
+msgid "LicenseManagement|License details"
+msgstr "许å¯è¯ä¿¡æ¯"
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr "管ç†æ­¤é¡¹ç›®çš„已批准和列入黑åå•çš„许å¯è¯ã€‚"
+
+msgid "LicenseManagement|Packages"
+msgstr "包"
+
+msgid "LicenseManagement|Remove license"
+msgstr "删除许å¯è¯"
+
+msgid "LicenseManagement|Remove license?"
+msgstr "删除许å¯è¯ï¼Ÿ"
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr "此项目目å‰æ²¡æœ‰å·²æ‰¹å‡†æˆ–列入黑åå•çš„许å¯è¯ã€‚"
+
+msgid "LicenseManagement|URL"
+msgstr "URL"
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr "您å³å°†ä»Žé¡¹ç›® %{name} 中删除许å¯è¯ã€‚"
+
+msgid "Licenses"
+msgstr "许å¯è¯"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] "最多显示 %d 个事件"
+
msgid "LinkedIn"
-msgstr ""
+msgstr "领英(LinkedIn)"
msgid "List"
msgstr "列表"
msgid "List Your Gitea Repositories"
-msgstr ""
+msgstr "列出Gitea存储库"
msgid "List available repositories"
-msgstr ""
+msgstr "列出å¯ç”¨å­˜å‚¨åº“"
+
+msgid "List your Bitbucket Server repositories"
+msgstr "列出您的 Bitbucket 库"
msgid "List your GitHub repositories"
msgstr "列出GitHub仓库"
+msgid "Live preview"
+msgstr "实时预览"
+
msgid "Loading contribution stats for group members"
msgstr "加载群组æˆå‘˜çš„贡献统计信æ¯"
@@ -3869,6 +4463,9 @@ msgstr "é”定 %{issuableDisplayName}"
msgid "Lock not found"
msgstr "未找到é”"
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr "é”定%{issuableDisplayName}?åªæœ‰ <strong>项目æˆå‘˜</strong> å¯ä»¥å‘表评论。"
+
msgid "Lock to current projects"
msgstr "é”定到当å‰é¡¹ç›®"
@@ -3885,28 +4482,31 @@ msgid "Locks give the ability to lock specific file or folder."
msgstr "加é”å¯ä»¥é”定特定的文件或文件夹。"
msgid "Logs"
-msgstr ""
+msgstr "日志"
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr "GitLab Geo å¯ä»¥åˆ›å»º GitLab 实例的åªè¯»é•œåƒ, 使得从远端克隆和拉å–大型代ç ä»“库的时间大大缩短,从而æ高团队æˆå‘˜çš„工作效率。"
msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
+msgstr "ç¡®ä¿æ‚¨å·²ç™»å½•åˆ°æ¬²å¯¼å…¥é¡¹ç›®æ‰€æœ‰è€…çš„å¸æˆ·ã€‚"
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
-msgstr ""
+msgstr "通过细粒度的访问控制æ¥ç®¡ç†Git存储库,确ä¿æ‚¨çš„代ç å®‰å…¨ã€‚执行代ç å®¡æŸ¥å¹¶é€šè¿‡åˆå¹¶è¯·æ±‚的实现更紧密的开å‘å作。æ¯ä¸ªé¡¹ç›®è¿˜å¯ä»¥é…置议题跟踪和wiki。"
+
+msgid "Manage Web IDE features"
+msgstr "管ç†Web IDE功能"
msgid "Manage access"
-msgstr ""
+msgstr "管ç†æƒé™"
msgid "Manage all notifications"
msgstr "管ç†å…¨éƒ¨é€šçŸ¥"
msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
-msgstr ""
+msgstr "管ç†å¯ä»¥å°†GitLab用作OAuthæ供程åºçš„应用程åºï¼Œä»¥åŠæ‚¨å·²æŽˆæƒä½¿ç”¨æ‚¨çš„å¸æˆ·çš„应用程åºã€‚"
msgid "Manage applications that you've authorized to use your account."
-msgstr ""
+msgstr "管ç†æ‚¨æŽˆæƒä½¿ç”¨å¸æˆ·çš„应用程åºã€‚"
msgid "Manage group labels"
msgstr "管ç†ç¾¤ç»„标记"
@@ -3921,22 +4521,22 @@ msgid "Manage your group’s membership while adding another level of security w
msgstr "通过SAML管ç†ç¾¤ç»„æˆå‘˜ï¼Œè¿›ä¸€æ­¥æ高安全性。"
msgid "Manifest"
-msgstr ""
+msgstr "Manifest"
msgid "Manifest file import"
-msgstr ""
+msgstr "Manifest文件导入"
msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
+msgstr "å°†FogBugzå¸æˆ·ID映射为GitLab用户"
msgid "Map a Google Code user to a GitLab user"
-msgstr ""
+msgstr "将Google Code用户映射为GitLab用户"
msgid "Map a Google Code user to a full email address"
-msgstr ""
+msgstr "å°†Google Code用户映射为完整的电å­é‚®ä»¶åœ°å€"
msgid "Map a Google Code user to a full name"
-msgstr ""
+msgstr "å°†Google Code用户映射为全å"
msgid "Mar"
msgstr "三"
@@ -3950,9 +4550,21 @@ msgstr "标记为已完æˆ"
msgid "Markdown enabled"
msgstr "支æŒMarkdownæ ¼å¼"
+msgid "Maven Metadata"
+msgstr "Maven 元数æ®"
+
+msgid "Maven package"
+msgstr "Maven 包"
+
+msgid "Max access level"
+msgstr "最高访问级别"
+
msgid "Maximum git storage failures"
msgstr "最大 git 存储失败次数"
+msgid "Maximum job timeout"
+msgstr "最大作业超时"
+
msgid "May"
msgstr "五"
@@ -3975,7 +4587,7 @@ msgid "Merge Requests"
msgstr "åˆå¹¶è¯·æ±‚"
msgid "Merge Requests created"
-msgstr "已创建åˆå¹¶è¯·æ±‚"
+msgstr "创建åˆå¹¶è¯·æ±‚"
msgid "Merge events"
msgstr "åˆå¹¶äº‹ä»¶"
@@ -4001,15 +4613,15 @@ msgstr "ä¿å­˜è¯„论失败"
msgid "MergeRequests|Toggle comments for this file"
msgstr "切æ¢æ­¤æ–‡ä»¶çš„讨论"
-msgid "MergeRequests|Updating discussions failed"
-msgstr "更新讨论失败"
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr "查看文件 @ %{commitId}"
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr "查看已替æ¢æ–‡ä»¶ @ %{commitId}"
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr " %{paragraphStart}å°†æ述更改为 %{descriptionChangedTimes} 次 %{timeDifferenceMinutes}%{paragraphEnd}"
+
msgid "Merged"
msgstr "å·²åˆå¹¶"
@@ -4025,6 +4637,9 @@ msgstr "指标 - Influx"
msgid "Metrics - Prometheus"
msgstr "指标 - Prometheus"
+msgid "Metrics and profiling"
+msgstr "指标和分æž"
+
msgid "Metrics|Business"
msgstr "业务"
@@ -4062,7 +4677,7 @@ msgid "Metrics|New metric"
msgstr "创建指标"
msgid "Metrics|No deployed environments"
-msgstr "未部署的环境"
+msgstr "应用没有部署到任何环境"
msgid "Metrics|Prometheus Query Documentation"
msgstr "Prometheus查询文档"
@@ -4086,7 +4701,7 @@ msgid "Metrics|There was an error getting environments information."
msgstr "获å–环境信æ¯æ—¶å‡ºé”™ã€‚"
msgid "Metrics|There was an error while retrieving metrics"
-msgstr ""
+msgstr "读å–指标时出错"
msgid "Metrics|Type"
msgstr "类型"
@@ -4127,9 +4742,24 @@ msgstr "例如:次请求/秒"
msgid "Milestone"
msgstr "里程碑"
+msgid "Milestone lists not available with your current license"
+msgstr "当å‰è®¸å¯è¯æ— æ³•ä½¿ç”¨é‡Œç¨‹ç¢‘列表"
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr "里程碑列表显示所选里程碑的所有议题。"
+
msgid "Milestones"
msgstr "里程碑"
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr "您å³å°†æ°¸ä¹…删除里程碑 %{milestoneTitle} 并将其从 %{issuesWithCount} å’Œ %{mergeRequestsWithCount} 删除。删除åŽï¼Œæ— æ³•æ’¤æ¶ˆæˆ–æ¢å¤ã€‚"
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr "您å³å°†æ°¸ä¹…删除里程碑 %{milestoneTitle}。此里程碑当å‰æœªç”¨äºŽä»»ä½•è®®é¢˜æˆ–åˆå¹¶è¯·æ±‚。"
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr "<p>%{milestonePromotion}</p> %{finalWarning}"
+
msgid "Milestones|Delete milestone"
msgstr "删除里程碑"
@@ -4148,9 +4778,30 @@ msgstr "å°† %{milestoneTitle} å‡çº§ä¸ºç¾¤ç»„里程碑?"
msgid "Milestones|Promote Milestone"
msgstr "å‡çº§é‡Œç¨‹ç¢‘"
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr "æå‡ %{milestone} 将使其å¯ç”¨äºŽ %{groupName} 内的所有项目。现有的åŒå项目里程碑将被åˆå¹¶ã€‚ "
+
msgid "Milestones|This action cannot be reversed."
msgstr "该æ“作无法撤销。"
+msgid "Mirror a repository"
+msgstr "é•œåƒå­˜å‚¨åº“"
+
+msgid "Mirror direction"
+msgstr "é•œåƒæ–¹å‘"
+
+msgid "Mirror repository"
+msgstr "é•œåƒå­˜å‚¨åº“"
+
+msgid "Mirror user"
+msgstr "é•œåƒç”¨æˆ·"
+
+msgid "Mirrored repositories"
+msgstr "é•œåƒçš„存储库"
+
+msgid "Mirroring repositories"
+msgstr "é•œåƒå­˜å‚¨åº“"
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "新建 SSH 公钥"
@@ -4167,10 +4818,7 @@ msgid "Months"
msgstr "月"
msgid "More"
-msgstr ""
-
-msgid "More actions"
-msgstr "更多æ“作"
+msgstr "更多"
msgid "More info"
msgstr "更多信æ¯"
@@ -4182,7 +4830,7 @@ msgid "More information is available|here"
msgstr "帮助文档"
msgid "Most stars"
-msgstr ""
+msgstr "最多星标"
msgid "Move"
msgstr "移动"
@@ -4203,7 +4851,7 @@ msgid "Name your individual key via a title"
msgstr "通过标题命å您的个人密钥"
msgid "Name:"
-msgstr ""
+msgstr "å称:"
msgid "Nav|Help"
msgstr "帮助"
@@ -4218,16 +4866,19 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "退出并登录到其他账å·"
msgid "Network"
-msgstr ""
+msgstr "网络"
+
+msgid "Never"
+msgstr "从ä¸"
msgid "New"
msgstr "新增事项"
msgid "New Application"
-msgstr ""
+msgstr "新建应用"
msgid "New Group"
-msgstr ""
+msgstr "新建群组"
msgid "New Identity"
msgstr "新建身份标识"
@@ -4297,7 +4948,7 @@ msgid "New tag"
msgstr "新建标签"
msgid "New..."
-msgstr ""
+msgstr "新建..."
msgid "No"
msgstr "å¦"
@@ -4308,12 +4959,21 @@ msgstr "无标记"
msgid "No assignee"
msgstr "未指派"
+msgid "No branches found"
+msgstr "未å‘现分支"
+
msgid "No changes"
msgstr "æ— å˜æ›´å†…容"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "无法连接到GitalyæœåŠ¡å™¨ï¼Œè¯·æ£€æŸ¥ç›¸å…³æ—¥å¿—ï¼"
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr "此项目当å‰æœªå­˜å‚¨å®¹å™¨é•œåƒã€‚如需使用,请å‚照上述说明新建容器镜åƒã€‚"
+
+msgid "No contributions were found"
+msgstr "未找到任何贡献者"
+
msgid "No due date"
msgstr "无截止日期"
@@ -4333,7 +4993,10 @@ msgid "No issues for the selected time period."
msgstr "所选时间段没有议题。"
msgid "No labels with such name or description"
-msgstr ""
+msgstr "没有具有此类å称或æ述的标记"
+
+msgid "No license. All rights reserved"
+msgstr "没有许å¯è¯ã€‚ 所有æƒä¿ç•™"
msgid "No merge requests for the selected time period."
msgstr "所选时间段没有åˆå¹¶è¯·æ±‚。"
@@ -4345,13 +5008,16 @@ msgid "No messages were logged"
msgstr "未记录任何消æ¯"
msgid "No other labels with such name or description"
-msgstr ""
+msgstr "没有其他具有此类å称或æ述的标记"
+
+msgid "No packages stored for this project."
+msgstr "没有为此项目存储的包。"
msgid "No prioritised labels with such name or description"
-msgstr ""
+msgstr "没有具有此类å称或æ述的优先标记"
msgid "No public groups"
-msgstr ""
+msgstr "无公共群组"
msgid "No pushes for the selected time period."
msgstr "所选时间段没有推é€ã€‚"
@@ -4359,15 +5025,27 @@ msgstr "所选时间段没有推é€ã€‚"
msgid "No repository"
msgstr "没有仓库"
+msgid "No runners found"
+msgstr "没有å‘现Runner"
+
msgid "No schedules"
msgstr "没有计划"
msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
+msgstr "å¦, 请直接导入现有电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å。"
+
+msgid "Nodes"
+msgstr "节点"
msgid "None"
msgstr "æ— "
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr "并éžæ‰€æœ‰æ³¨é‡Šéƒ½ä¼šæ˜¾ç¤ºï¼Œå› ä¸ºæ‚¨æ­£åœ¨æ¯”较两个版本的差异。"
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr "并éžæ‰€æœ‰æ³¨é‡Šéƒ½æ˜¾ç¤ºï¼Œå› ä¸ºæ‚¨æ­£åœ¨æŸ¥çœ‹æ—§ç‰ˆæœ¬çš„差异。"
+
msgid "Not allowed to merge"
msgstr "ä¸å…许åˆå¹¶"
@@ -4386,6 +5064,9 @@ msgstr "éžæœºå¯†"
msgid "Not enough data"
msgstr "æ•°æ®ä¸è¶³"
+msgid "Not now"
+msgstr "æš‚ä¸"
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "请注æ„,master分支自动å—ä¿æŠ¤ã€‚%{link_to_protected_branches}"
@@ -4402,7 +5083,7 @@ msgid "Note: Consider asking your GitLab administrator to configure %{github_int
msgstr "æ示:如GitLab管ç†å‘˜é…ç½® %{github_integration_link},将å…许通过GitHub登录并å…许导入Github代ç ä»“库而ä¸éœ€è¦ä¸ªäººè®¿é—®ä»¤ç‰Œã€‚"
msgid "Notes|Are you sure you want to cancel creating this comment?"
-msgstr ""
+msgstr "确定è¦å–消此评论å—?"
msgid "Notification events"
msgstr "通知事件"
@@ -4419,6 +5100,9 @@ msgstr "æµæ°´çº¿å¤±è´¥"
msgid "NotificationEvent|Merge merge request"
msgstr "åˆå¹¶è¯·æ±‚被åˆå¹¶"
+msgid "NotificationEvent|New epic"
+msgstr "新建å²è¯—"
+
msgid "NotificationEvent|New issue"
msgstr "新建议题"
@@ -4491,18 +5175,25 @@ msgstr "过滤"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr "仓库导入åŽï¼Œå¯ä»¥é€šè¿‡ SSH 拉å–é•œåƒã€‚了解更多 %{ssh_link}"
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
+msgstr "您的一个或多个Bitbucket项目无法直接导入GitLab,因为它们使用Subversion或Mercurial进行版本控制,而ä¸æ˜¯Git。"
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
+msgstr "您的一个或多个Google Code项目无法直接导入GitLab,因为它们使用Subversion或Mercurial进行版本控制,而ä¸æ˜¯Git。"
-msgid "Online IDE integration settings."
-msgstr "在线IDE集æˆè®¾ç½®ã€‚"
+msgid "Only admins"
+msgstr "仅管ç†å‘˜"
msgid "Only comments from the following commit are shown below"
msgstr "下é¢ä»…显示æ¥è‡ªä»¥ä¸‹æ交的评论"
+msgid "Only mirror protected branches"
+msgstr "åªé•œåƒå—ä¿æŠ¤çš„分支"
+
msgid "Only project members can comment."
msgstr "åªæœ‰é¡¹ç›®æˆå‘˜å¯ä»¥å‘表评论。"
@@ -4516,10 +5207,10 @@ msgid "Open in Xcode"
msgstr "用Xcode打开"
msgid "Open sidebar"
-msgstr ""
+msgstr "打开侧边æ "
msgid "Open source software to collaborate on code"
-msgstr ""
+msgstr "用于代ç å¼€å‘å作的开æºè½¯ä»¶"
msgid "Opened"
msgstr "已打开"
@@ -4540,10 +5231,10 @@ msgid "Operations"
msgstr "è¿ç»´"
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
-msgstr ""
+msgstr "您å¯ä»¥é€‰æ‹© %{link_to_customize} FogBugz的电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å如何被导入到GitLab。"
msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
+msgstr "您也å¯ä»¥%{link_to_customize} FogBugz的电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLabçš„æ–¹å¼ã€‚"
msgid "Options"
msgstr "æ“作"
@@ -4566,9 +5257,21 @@ msgstr "外å‘请求"
msgid "Overview"
msgstr "概览"
+msgid "Overwrite diverged branches"
+msgstr "覆盖分å‰åˆ†æ”¯"
+
msgid "Owner"
msgstr "所有者"
+msgid "Package information"
+msgstr "包信æ¯"
+
+msgid "Package was removed"
+msgstr "包已被删除"
+
+msgid "Packages"
+msgstr "包"
+
msgid "Pages"
msgstr "Pages"
@@ -4594,14 +5297,20 @@ msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh
msgstr "粘贴您的 SSH 公钥,通常包å«åœ¨ '~/.ssh/id_rsa.pub' 文件中,并以 'ssh-rsa' 开头。ä¸è¦ä½¿ç”¨æ‚¨çš„ SSH ç§é’¥ã€‚"
msgid "Path:"
-msgstr ""
+msgstr "路径:"
msgid "Pause"
msgstr "æš‚åœ"
+msgid "Paused Runners don't accept new jobs"
+msgstr "æš‚åœçš„ Runner ä¸æŽ¥å—新作业"
+
msgid "Pending"
msgstr "等待中"
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr "未ç»è®¸å¯çš„用户将无法收到通知,也无法评论。"
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr "æ¯ä¸ªä½œä¸šã€‚如果作业超过此阈值,则会将其标记为失败"
@@ -4620,6 +5329,9 @@ msgstr "个人访问凭è¯"
msgid "Pipeline"
msgstr "æµæ°´çº¿"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr "æµæ°´çº¿ %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} æ¥è‡ª %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+
msgid "Pipeline Health"
msgstr "æµæ°´çº¿è¿è¡ŒçŠ¶å†µæŒ‡æ ‡"
@@ -4707,6 +5419,9 @@ msgstr "CI é…置检查(CI Lint)"
msgid "Pipelines|Clear Runner Caches"
msgstr "清除Runner缓存"
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr "æŒç»­é›†æˆå¯ä»¥é€šè¿‡è‡ªåŠ¨è¿è¡Œæµ‹è¯•æ¥å¸®åŠ©æ•èŽ·é”™è¯¯ï¼Œè€ŒæŒç»­éƒ¨ç½²å¯ä»¥å¸®åŠ©æ‚¨å‘生产环境交付代ç ã€‚"
+
msgid "Pipelines|Get started with Pipelines"
msgstr "æµæ°´çº¿å…¥é—¨"
@@ -4728,6 +5443,9 @@ msgstr "当å‰æ²¡æœ‰ %{scope}çš„æµæ°´çº¿ã€‚"
msgid "Pipelines|There are currently no pipelines."
msgstr "当å‰æ²¡æœ‰æµæ°´çº¿ã€‚"
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr "获å–æµæ°´çº¿æ—¶å‡ºçŽ°é”™è¯¯ã€‚请ç¨åŽé‡è¯•æˆ–å°è¯•è”系您的支æŒå›¢é˜Ÿã€‚"
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "此项目当å‰æœªé…ç½®è¿è¡Œæµæ°´çº¿ã€‚"
@@ -4776,12 +5494,6 @@ msgstr "于阶段"
msgid "Plain diff"
msgstr "文本差异"
-msgid "Planned finish date"
-msgstr "计划完æˆæ—¥æœŸ"
-
-msgid "Planned start date"
-msgstr "计划开始日期"
-
msgid "PlantUML"
msgstr "PlantUML"
@@ -4792,13 +5504,13 @@ msgid "Please accept the Terms of Service before continuing."
msgstr "请接å—æœåŠ¡æ¡æ¬¾ä»¥ç»§ç»­ã€‚"
msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
-msgstr ""
+msgstr "请将它们先%{link_to_git}, 然åŽå†æ¬¡ä½¿ç”¨%{link_to_import_flow}。"
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
+msgstr "请将它们先在Google Code中转为Git, 然åŽå†æ¬¡ä½¿ç”¨%{link_to_import_flow}。"
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
-msgstr ""
+msgstr "请注æ„,GitLabä¸æ供此应用程åºï¼Œæ‚¨åº”该在å…许访问之å‰éªŒè¯å…¶çœŸå®žæ€§ã€‚"
msgid "Please select at least one filter to see results"
msgstr "请至少选择一个过滤器æ¥æŸ¥çœ‹ç»“æžœ"
@@ -4821,6 +5533,15 @@ msgstr "å好设置"
msgid "Preferences|Navigation theme"
msgstr "导航主题"
+msgid "Press Enter or click to search"
+msgstr "按 回车键或å•å‡»ä»¥æœç´¢"
+
+msgid "Preview"
+msgstr "预览"
+
+msgid "Preview payload"
+msgstr "预览有效负载"
+
msgid "Primary"
msgstr "主è¦"
@@ -4849,7 +5570,16 @@ msgid "Profile"
msgstr "用户资料"
msgid "Profile Settings"
-msgstr ""
+msgstr "账户设置"
+
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr "您å³å°†æ°¸ä¹…删除 %{yourAccount},以åŠä¸Žæ‚¨çš„å¸æˆ·å…³è”的所有议题,åˆå¹¶è¯·æ±‚和群组。一旦确认 %{deleteAccount},此æ“作便无法撤销和æ¢å¤ã€‚"
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr "您将更改用户å %{currentUsernameBold} 为 %{newUsernameBold}。é…置文件和项目将é‡å®šå‘到 %{newUsername} 命å空间,但是一旦 %{currentUsername} 命å空间被å¦ä¸€ä¸ªç”¨æˆ·æˆ–组注册,此é‡å®šå‘将过期。请尽快更新您的远端Git仓库。"
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr "%{author_name} 作出了ç§æœ‰è´¡çŒ®"
msgid "Profiles|Account scheduled for removal."
msgstr "å¸æˆ·å·²å®‰æŽ’被删除。"
@@ -4857,12 +5587,33 @@ msgstr "å¸æˆ·å·²å®‰æŽ’被删除。"
msgid "Profiles|Add key"
msgstr "添加密钥"
+msgid "Profiles|Add status emoji"
+msgstr "在状æ€ä¸­æ·»åŠ è¡¨æƒ…符å·"
+
+msgid "Profiles|Avatar cropper"
+msgstr "头åƒè£å‰ª"
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr "å³å°†åˆ é™¤å¤´åƒã€‚确定继续å—?"
+
msgid "Profiles|Change username"
msgstr "更改用户å"
+msgid "Profiles|Choose file..."
+msgstr "选择文件..."
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr "选择在公开个人资料中显示ç§æœ‰é¡¹ç›®çš„贡献,但ä¸æ˜¾ç¤ºä»»ä½•é¡¹ç›®ï¼Œå­˜å‚¨åº“或组织信æ¯ã€‚"
+
+msgid "Profiles|Clear status"
+msgstr "清除状æ€"
+
msgid "Profiles|Current path: %{path}"
msgstr "当å‰è·¯å¾„: %{path}"
+msgid "Profiles|Current status"
+msgstr "当å‰çŠ¶æ€"
+
msgid "Profiles|Delete Account"
msgstr "删除å¸æˆ·"
@@ -4875,33 +5626,108 @@ msgstr "删除您的å¸æˆ·?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr "删除å¸æˆ·å…·æœ‰ä»¥ä¸‹æ•ˆæžœï¼š"
+msgid "Profiles|Do not show on profile"
+msgstr "ä¸åœ¨ä¸ªäººèµ„料中显示"
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr "ä¸è¦åœ¨ä¸ªäººèµ„料上显示与活动相关的个人信æ¯"
+
+msgid "Profiles|Edit Profile"
+msgstr "编辑个人资料"
+
msgid "Profiles|Invalid password"
msgstr "密ç æ— æ•ˆ"
msgid "Profiles|Invalid username"
msgstr "用户å无效"
+msgid "Profiles|Main settings"
+msgstr "主è¦è®¾ç½®"
+
+msgid "Profiles|No file chosen"
+msgstr "未选择文件"
+
msgid "Profiles|Path"
msgstr "路径"
+msgid "Profiles|Position and size your new avatar"
+msgstr "您新头åƒçš„ä½ç½®å’Œå¤§å°"
+
+msgid "Profiles|Private contributions"
+msgstr "ç§äººè´¡çŒ®è€…"
+
+msgid "Profiles|Public Avatar"
+msgstr "公共头åƒ"
+
+msgid "Profiles|Remove avatar"
+msgstr "删除头åƒ"
+
+msgid "Profiles|Set new profile picture"
+msgstr "设置新个人资料图片"
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr "æŸäº›é€‰é¡¹å¯¹äºŽ LDAP å¸æˆ·ä¸å¯ç”¨"
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr "在少于250个字符的情况下介ç»æ‚¨è‡ªå·±ã€‚"
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr "å…许的最大文件大å°ä¸º200KB。"
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr "这看起æ¥ä¸åƒ SSH 公钥,确定è¦æ·»åŠ å®ƒå—?"
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr "此电å­é‚®ä»¶å°†æ˜¾ç¤ºåœ¨æ‚¨çš„公开个人资料中。"
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr "这个表情符å·å’Œè¿™æ¡æ¶ˆæ¯ä¼šåœ¨æ‚¨çš„个人资料和整个工作界é¢ä¸­å‡ºçŽ°ã€‚"
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr "此功能是实验性的,翻译尚未完æˆã€‚"
+
+msgid "Profiles|This information will appear on your profile."
+msgstr "此信æ¯å°†æ˜¾ç¤ºåœ¨æ‚¨çš„个人资料中。"
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "键入您的 %{confirmationValue} 以确认:"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr "通常以“ssh-rsa ...â€å¼€å¤´"
+msgid "Profiles|Update profile settings"
+msgstr "更新个人资料设置"
+
msgid "Profiles|Update username"
msgstr "更新用户å"
+msgid "Profiles|Upload new avatar"
+msgstr "上传新头åƒ"
+
msgid "Profiles|Username change failed - %{message}"
msgstr "用户å更改失败 - %{message}"
msgid "Profiles|Username successfully changed"
msgstr "用户å更改æˆåŠŸ"
+msgid "Profiles|Website"
+msgstr "网站"
+
+msgid "Profiles|What's your status?"
+msgstr "你当å‰çš„状æ€ï¼Ÿ"
+
+msgid "Profiles|You can change your avatar here"
+msgstr "å¯ä»¥åœ¨è¿™é‡Œä¿®æ”¹æ‚¨çš„头åƒ"
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr "您å¯ä»¥åœ¨è¿™é‡Œä¿®æ”¹å¤´åƒæˆ–删除当å‰å¤´åƒå¹¶æ¢å¤ä¸º %{gravatar_link}"
+
+msgid "Profiles|You can upload your avatar here"
+msgstr "å¯ä»¥åœ¨è¿™é‡Œä¸Šä¼ æ‚¨çš„头åƒ"
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr "å¯ä»¥åœ¨è¿™é‡Œä¸Šä¼ æ‚¨çš„头åƒæˆ–者从 %{gravatar_link} 修改头åƒ"
+
msgid "Profiles|You don't have access to delete this user."
msgstr "您无æƒåˆ é™¤æ­¤ç”¨æˆ·ã€‚"
@@ -4911,6 +5737,18 @@ msgstr "您必须转移所有æƒæˆ–删除这些群组,然åŽæ‰èƒ½åˆ é™¤æ‚¨çš„
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "您的å¸æˆ·ç›®å‰æ˜¯è¿™äº›ç¾¤ç»„的所有者:"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr "您的邮件地å€æ˜¯æ ¹æ®æ‚¨çš„ %{provider_label} å¸æˆ·è‡ªåŠ¨è®¾ç½®çš„。"
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr "您的ä½ç½®æ˜¯åŸºäºŽæ‚¨çš„ %{provider_label} å¸æˆ·è‡ªåŠ¨è®¾ç½®çš„。"
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr "您的姓å是根æ®æ‚¨çš„ %{provider_label} å¸æˆ·è‡ªåŠ¨è®¾ç½®çš„,因此您认识的人å¯ä»¥è¯†åˆ«æ‚¨ã€‚"
+
+msgid "Profiles|Your status"
+msgstr "你的状æ€"
+
msgid "Profiles|e.g. My MacBook key"
msgstr "例如: My MacBook Key"
@@ -4944,6 +5782,9 @@ msgstr "项目 '%{project_name}' 已更新完æˆã€‚"
msgid "Project Badges"
msgstr "项目徽章"
+msgid "Project URL"
+msgstr "项目 URL"
+
msgid "Project access must be granted explicitly to each user."
msgstr "项目访问æƒé™å¿…须明确授æƒç»™æ¯ä¸ªç”¨æˆ·ã€‚"
@@ -4969,7 +5810,10 @@ msgid "Project export started. A download link will be sent by email."
msgstr "项目导出已开始。下载链接将通过电å­é‚®ä»¶å‘é€ã€‚"
msgid "Project name"
-msgstr ""
+msgstr "项目å称"
+
+msgid "Project slug"
+msgstr "项目标识串(slug)"
msgid "ProjectActivityRSS|Subscribe"
msgstr "订阅"
@@ -4998,18 +5842,48 @@ msgstr "从未"
msgid "ProjectLifecycle|Stage"
msgstr "阶段"
+msgid "ProjectOverview|Fork"
+msgstr "派生"
+
+msgid "ProjectOverview|Forks"
+msgstr "派生"
+
+msgid "ProjectOverview|Go to your fork"
+msgstr "转至您的派生"
+
+msgid "ProjectOverview|Star"
+msgstr "星标"
+
+msgid "ProjectOverview|Unstar"
+msgstr "å–消星标"
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr "您已达到项目数é‡é™åˆ¶"
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr "登录åŽæ‰èƒ½æ˜Ÿæ ‡é¡¹ç›®"
+
msgid "ProjectPage|Project ID: %{project_id}"
-msgstr ""
+msgstr "项目ID:%{project_id}"
+
+msgid "ProjectSettings|Badges"
+msgstr "徽章"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "è”系管ç†å‘˜æ›´æ”¹æ­¤è®¾ç½®ã€‚"
+msgid "ProjectSettings|Customize your project badges."
+msgstr "自定义你的项目徽章。"
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr "ä¿æŠ¤æ ‡ç­¾å¤±è´¥"
msgid "ProjectSettings|Failed to update tag!"
msgstr "更新标签失败ï¼"
+msgid "ProjectSettings|Learn more about badges."
+msgstr "了解有关徽章的更多信æ¯ã€‚"
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "åªæœ‰å·²ç­¾ç½²æ交æ‰å¯ä»¥æŽ¨é€åˆ°æ­¤ä»“库。"
@@ -5029,7 +5903,7 @@ msgid "Projects"
msgstr "项目"
msgid "Projects shared with %{group_name}"
-msgstr ""
+msgstr "与 %{group_name} 共享的项目"
msgid "ProjectsDropdown|Frequently visited"
msgstr "ç»å¸¸è®¿é—®"
@@ -5049,35 +5923,38 @@ msgstr "å‘生了内部错误"
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ç¬¦åˆæ¡ä»¶çš„项目"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr "此功能需è¦æµè§ˆå™¨æ”¯æŒæœ¬åœ°å­˜å‚¨"
+
msgid "PrometheusAlerts|Add alert"
-msgstr ""
+msgstr "添加警报"
msgid "PrometheusAlerts|Alert set"
-msgstr ""
+msgstr "警报设置"
msgid "PrometheusAlerts|Edit alert"
-msgstr ""
+msgstr "编辑警报"
msgid "PrometheusAlerts|Error creating alert"
-msgstr ""
+msgstr "创建警报时出错"
msgid "PrometheusAlerts|Error deleting alert"
-msgstr ""
+msgstr "删除警报时出错"
msgid "PrometheusAlerts|Error fetching alert"
-msgstr ""
+msgstr "获å–警报时出错"
msgid "PrometheusAlerts|Error saving alert"
-msgstr ""
+msgstr "ä¿å­˜è­¦æŠ¥æ—¶å‡ºé”™"
msgid "PrometheusAlerts|No alert set"
-msgstr ""
+msgstr "无警报设定"
msgid "PrometheusAlerts|Operator"
-msgstr ""
+msgstr "æ“作符"
msgid "PrometheusAlerts|Threshold"
-msgstr ""
+msgstr "阈值"
msgid "PrometheusDashboard|Time"
msgstr "时间"
@@ -5181,6 +6058,54 @@ msgstr "此功能已é”定"
msgid "Promotions|Upgrade plan"
msgstr "å‡çº§è®¢é˜…计划"
+msgid "Protected"
+msgstr "å—ä¿æŠ¤"
+
+msgid "Protected Environments"
+msgstr "å—ä¿æŠ¤çš„环境"
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr "%{environment_name} 对于开å‘人员æ¥è¯´æ˜¯å¯å†™çš„。你确定å—?"
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr "å…许部署"
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr "选择å…许部署的人员"
+
+msgid "ProtectedEnvironment|Environment"
+msgstr "环境"
+
+msgid "ProtectedEnvironment|Protect"
+msgstr "ä¿æŠ¤"
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr "ä¿æŠ¤çŽ¯å¢ƒä»¥é™åˆ¶å¯æ‰§è¡Œéƒ¨ç½²çš„人员。"
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr "ä¿æŠ¤çŽ¯å¢ƒ"
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr "å—ä¿æŠ¤çš„环境 (%{protected_environments_count})"
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr "选择一个环境"
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr "ç›®å‰æ²¡æœ‰å—ä¿æŠ¤çš„环境,请使用上述表å•ä¿æŠ¤çŽ¯å¢ƒã€‚"
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr "å–消ä¿æŠ¤"
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr "您的环境无法å—到ä¿æŠ¤"
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr "您的环境已å—到ä¿æŠ¤ã€‚"
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr "环境已ç»ä¸è¢«ä¿æŠ¤"
+
msgid "Protip:"
msgstr "专家æ示:"
@@ -5199,6 +6124,12 @@ msgstr "公开 - 无需任何身份验è¯å³å¯è®¿é—®è¯¥é¡¹ç›®ã€‚"
msgid "Public pipelines"
msgstr "公共æµæ°´çº¿"
+msgid "Pull"
+msgstr "拉å–"
+
+msgid "Push"
+msgstr "推é€"
+
msgid "Push Rules"
msgstr "推é€è§„则"
@@ -5230,7 +6161,7 @@ msgid "Read more"
msgstr "进一步了解"
msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
-msgstr ""
+msgstr "了解有关项目æƒé™çš„æ›´å¤šä¿¡æ¯ <strong>%{link_to_help}</strong>"
msgid "Readme"
msgstr "自述文件"
@@ -5238,12 +6169,25 @@ msgstr "自述文件"
msgid "Real-time features"
msgstr "实时功能"
+msgid "Recent searches"
+msgstr "最近的æœç´¢"
+
msgid "Reference:"
msgstr "引用:"
msgid "Refresh"
msgstr "刷新"
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] "%d 秒åŽåˆ·æ–°ä»¥æ˜¾ç¤ºæ›´æ–°çŠ¶æ€..."
+
+msgid "Regenerate key"
+msgstr "é‡æ–°ç”Ÿæˆå¯†é’¥"
+
+msgid "Regex pattern"
+msgstr "正则表达å¼"
+
msgid "Register / Sign In"
msgstr "注册/登录"
@@ -5251,7 +6195,7 @@ msgid "Register and see your runners for this group."
msgstr "注册并查看当å‰ç¾¤ç»„çš„Runner。"
msgid "Register and see your runners for this project."
-msgstr "注册和查看这个项目的 Runner"
+msgstr "注册和查看当å‰é¡¹ç›®çš„ Runner"
msgid "Registry"
msgstr "注册表"
@@ -5295,15 +6239,75 @@ msgstr "删除优先级"
msgid "Remove project"
msgstr "删除项目"
+msgid "Rename"
+msgstr "é‡å‘½å"
+
+msgid "Rename file"
+msgstr "é‡å‘½å文件"
+
+msgid "Rename folder"
+msgstr "é‡å‘½å文件夹"
+
+msgid "Reopen epic"
+msgstr "é‡æ–°å¼€å¯å²è¯—"
+
msgid "Repair authentication"
msgstr "ä¿®å¤è®¤è¯"
msgid "Reply to this email directly or %{view_it_on_gitlab}."
-msgstr ""
+msgstr "直接回å¤æ­¤é‚®ä»¶æˆ– %{view_it_on_gitlab}。"
msgid "Repo by URL"
msgstr "从 URL 导入仓库"
+msgid "Reporting"
+msgstr "报告"
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr "%{failedString} 和 %{resolvedString}"
+
+msgid "Reports|Class"
+msgstr "ç±»"
+
+msgid "Reports|Confidence"
+msgstr "置信水平"
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr "忽略æ¼æ´ž"
+
+msgid "Reports|Execution time"
+msgstr "执行时间"
+
+msgid "Reports|Failure"
+msgstr "失败"
+
+msgid "Reports|More info"
+msgstr "更多信æ¯"
+
+msgid "Reports|New Issue"
+msgstr "新建议题"
+
+msgid "Reports|Severity"
+msgstr "严é‡æ€§"
+
+msgid "Reports|System output"
+msgstr "系统输出"
+
+msgid "Reports|Test summary"
+msgstr "测试摘è¦"
+
+msgid "Reports|Test summary failed loading results"
+msgstr "测试摘è¦åŠ è½½å¤±è´¥"
+
+msgid "Reports|Test summary results are being parsed"
+msgstr "正在分æžæµ‹è¯•æ‘˜è¦"
+
+msgid "Reports|Vulnerability"
+msgstr "æ¼æ´ž"
+
+msgid "Reports|no changed test results"
+msgstr "测试结果没有å˜åŒ–"
+
msgid "Repository"
msgstr "仓库"
@@ -5311,7 +6315,7 @@ msgid "Repository Settings"
msgstr "存储库设置"
msgid "Repository URL"
-msgstr ""
+msgstr "仓库地å€"
msgid "Repository has no locks."
msgstr "当å‰ä»“库无加é”文件。"
@@ -5332,7 +6336,7 @@ msgid "Request Access"
msgstr "申请æƒé™"
msgid "Requests Profiles"
-msgstr ""
+msgstr "请求分æž"
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "è¦æ±‚所有用户在访问GitLab时接å—æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–。"
@@ -5355,9 +6359,21 @@ msgstr "在æºåˆ†æ”¯ä¸Šè§£å†³å†²çª"
msgid "Resolve discussion"
msgstr "解决讨论"
+msgid "Response metrics (AWS ELB)"
+msgstr "å“应指标(AWS ELB)"
+
msgid "Response metrics (Custom)"
msgstr "å“应指标(自定义)"
+msgid "Response metrics (HA Proxy)"
+msgstr "å“应指标(HA Proxy)"
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr "å“应指标(NGINX Ingress)"
+
+msgid "Response metrics (NGINX)"
+msgstr "å“应指标(NGINX)"
+
msgid "Resume"
msgstr "æ¢å¤"
@@ -5370,6 +6386,9 @@ msgstr "é‡è¯•å½“å‰ä½œä¸š"
msgid "Retry verification"
msgstr "é‡è¯•éªŒè¯"
+msgid "Reveal Variables"
+msgstr "显示å˜é‡"
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] "显示值"
@@ -5393,7 +6412,7 @@ msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr "审核(åˆå¹¶è¯·æ±‚ !%{mergeRequestId})"
msgid "Revoke"
-msgstr ""
+msgstr "撤销"
msgid "Roadmap"
msgstr "路线图"
@@ -5401,9 +6420,27 @@ msgstr "路线图"
msgid "Run CI/CD pipelines for external repositories"
msgstr "使用外部仓库的CI/CDæµæ°´çº¿"
+msgid "Run untagged jobs"
+msgstr "è¿è¡Œæœªæ ‡è®°çš„作业"
+
+msgid "Runner cannot be assigned to other projects"
+msgstr "无法将Runner分é…给其他项目"
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr "Runnerå°†è¿è¡Œæ‰€æœ‰æœªæŒ‡å®šçš„项目的作业"
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr "Runnerå°†è¿è¡Œç¾¤ç»„中所有未指定项目的作业"
+
+msgid "Runner runs jobs from assigned projects"
+msgstr "Runnerå°†è¿è¡ŒæŒ‡å®šé¡¹ç›®çš„作业"
+
msgid "Runner token"
msgstr "Runner 令牌"
+msgid "Runner will not receive any new jobs"
+msgstr "Runnerä¸ä¼šæŽ¥å—新的作业"
+
msgid "Runners"
msgstr "Runner"
@@ -5413,6 +6450,21 @@ msgstr "Runners API"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr "Runnerå¯ä»¥æ”¾åœ¨ä¸åŒçš„用户ã€æœåŠ¡å™¨ï¼Œç”šè‡³æœ¬åœ°æœºå™¨ä¸Šã€‚"
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr "Runnerå¯ä»¥æ”¾åœ¨ä¸åŒçš„用户ã€æœåŠ¡å™¨ï¼Œç”šè‡³æœ¬åœ°æœºå™¨ä¸Šã€‚"
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr "当å‰åœ¨çº¿Runner: %{active_runners_count}"
+
+msgid "Runners page"
+msgstr "è¿è¡Œå™¨é¡µé¢"
+
+msgid "Runners page."
+msgstr "è¿è¡Œå™¨é¡µé¢ã€‚"
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr "您已ç»ä½¿ç”¨äº†æ‰€æœ‰å…±äº«Runnerçš„æµæ°´çº¿æ—¶é—´ã€‚"
+
msgid "Running"
msgstr "è¿è¡Œä¸­"
@@ -5428,12 +6480,21 @@ msgstr "SAML å•ç‚¹ç™»å½•"
msgid "SAML Single Sign On Settings"
msgstr "SAML å•ç‚¹ç™»å½•è®¾ç½®"
+msgid "SAST"
+msgstr "SAST"
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr "SAML令牌签åè¯ä¹¦çš„SHA1指纹。请从身份验è¯æ供商处获å–(也å¯ä»¥è¢«ç§°ä¸ºâ€œæŒ‡çº¹â€ï¼‰ã€‚"
msgid "SSH Keys"
msgstr "SSH 密钥"
+msgid "SSH host keys"
+msgstr "SSH主机密钥"
+
+msgid "SSH public key"
+msgstr "SSH公钥"
+
msgid "SSL Verification"
msgstr "SSL验è¯"
@@ -5441,7 +6502,7 @@ msgid "Save"
msgstr "ä¿å­˜"
msgid "Save application"
-msgstr ""
+msgstr "ä¿å­˜åº”用"
msgid "Save changes"
msgstr "ä¿å­˜ä¿®æ”¹"
@@ -5465,13 +6526,13 @@ msgid "Scheduling Pipelines"
msgstr "æµæ°´çº¿è®¡åˆ’"
msgid "Scope"
-msgstr ""
+msgstr "范围"
msgid "Scoped issue boards"
msgstr "指定范围的议题看æ¿"
msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
-msgstr ""
+msgstr "å‘下滚动到 <strong>Google Code项目托管</strong> 并通过å³ä¾§çš„开关å¯ç”¨ã€‚"
msgid "Scroll to bottom"
msgstr "滚动到底部"
@@ -5500,12 +6561,42 @@ msgstr "æœç´¢åˆå¹¶è¯·æ±‚"
msgid "Search milestones"
msgstr "æœç´¢é‡Œç¨‹ç¢‘"
+msgid "Search or filter results..."
+msgstr "æœç´¢æˆ–过滤结果......"
+
+msgid "Search or jump to…"
+msgstr "æœç´¢æˆ–转到..."
+
msgid "Search project"
msgstr "æœç´¢é¡¹ç›®"
msgid "Search users"
msgstr "æœç´¢ç”¨æˆ·"
+msgid "SearchAutocomplete|All GitLab"
+msgstr "在整个 GitLab"
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr "我创建的问题"
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr "分é…给我的问题"
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr "我创建的åˆå¹¶è¯·æ±‚"
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr "分é…给我的åˆå¹¶è¯·æ±‚"
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr "在整个 GitLab"
+
+msgid "SearchAutocomplete|in this group"
+msgstr "在此群组"
+
+msgid "SearchAutocomplete|in this project"
+msgstr "在此项目"
+
msgid "Seconds before reseting failure information"
msgstr "é‡ç½®å¤±è´¥ä¿¡æ¯ç­‰å¾…时间(秒)"
@@ -5513,13 +6604,16 @@ msgid "Seconds to wait for a storage access attempt"
msgstr "等待存储访问å°è¯•æ—¶é—´(秒)"
msgid "Secret:"
-msgstr ""
+msgstr "密ç :"
+
+msgid "Security"
+msgstr "安全"
msgid "Security Dashboard"
msgstr "安全仪表盘"
-msgid "Security report"
-msgstr "安全报告"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr "安全仪表æ¿æ˜¾ç¤ºæœ€æ–°çš„安全报告。用它æ¥æŸ¥æ‰¾å’Œä¿®å¤æ¼æ´žã€‚"
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
msgstr "监控代ç ä¸­çš„æ¼æ´ž"
@@ -5533,6 +6627,9 @@ msgstr "选择"
msgid "Select Archive Format"
msgstr "选择下载格å¼"
+msgid "Select a group to invite"
+msgstr "选择è¦é‚€è¯·çš„组"
+
msgid "Select a namespace to fork the project"
msgstr "选择一个命å空间æ¥æ´¾ç”Ÿé¡¹ç›®"
@@ -5558,7 +6655,7 @@ msgid "Select project to choose zone"
msgstr "按项目选择地域"
msgid "Select projects you want to import."
-msgstr ""
+msgstr "选择è¦å¯¼å…¥çš„项目。"
msgid "Select source branch"
msgstr "选择æºåˆ†æ”¯"
@@ -5566,8 +6663,14 @@ msgstr "选择æºåˆ†æ”¯"
msgid "Select target branch"
msgstr "选择目标分支"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr "选择当å‰é¡¹ç›®çš„默认分支。除éžå¦è¡ŒæŒ‡å®šï¼Œå¦åˆ™æ‰€æœ‰åˆå¹¶è¯·æ±‚å’Œæ交都将指å‘此分支。"
+
+msgid "Select the custom project template source group."
+msgstr "选择自定义项目模æ¿æºç»„。"
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
-msgstr ""
+msgstr "选中GitLab用户将在议题和评论的æ述中加入指å‘该GitLab用户的链接(例如“By <a href=\"#\">@johnsmith</a>â€ï¼‰ã€‚它还将与所选用户关è”å’Œ/或分é…这些议题和评论。"
msgid "Selective synchronization"
msgstr "选择性åŒæ­¥"
@@ -5575,6 +6678,9 @@ msgstr "选择性åŒæ­¥"
msgid "Send email"
msgstr "å‘é€ç”µå­é‚®ä»¶"
+msgid "Send usage data"
+msgstr "å‘é€çš„使用情况数æ®"
+
msgid "Sep"
msgstr "ä¹"
@@ -5602,6 +6708,9 @@ msgstr "为账å·åˆ›å»ºä¸€ä¸ªç”¨äºŽæŽ¨é€æˆ–拉å–çš„ %{protocol} 密ç ã€‚"
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr "设定缺çœåŠå—é™å¯è§æ€§çº§åˆ«ã€‚é…置导入æ¥æºåŠgit访问å议。"
+msgid "Set instance-wide template repository"
+msgstr "设置实例范围的模æ¿å­˜å‚¨åº“"
+
msgid "Set max session time for web terminal."
msgstr "为Web终端设置最长会è¯æ—¶é—´ã€‚"
@@ -5614,21 +6723,24 @@ msgstr "设定用户登录的æ¡ä»¶ã€‚å¯ç”¨å¼ºåˆ¶åŒé‡è®¤è¯ã€‚"
msgid "Set up CI/CD"
msgstr "é…ç½® CI/CD"
-msgid "Set up Koding"
-msgstr "设置 Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr "手动设置%{type}Runner "
+
+msgid "Set up a specific Runner automatically"
+msgstr "自动创建专用Runner"
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr "æ ¹æ®%{docsLinkStart}文档%{icon}%{docsLinkEnd}设置断言/属性/声明(email,first_name,last_name)和NameID"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr "设置项目以自动推é€å’Œ/或从å¦ä¸€ä¸ªå­˜å‚¨åº“中æå–更改。分支,标签和æ交将自动åŒæ­¥ã€‚"
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "设置密ç "
msgid "Settings"
msgstr "设置"
-msgid "Setup a specific Runner automatically"
-msgstr "自动创建专用Runner"
-
msgid "Share"
msgstr "分享"
@@ -5638,6 +6750,9 @@ msgstr "分享<strong>%{sso_label}</strong> 给组员,以便他们å¯ä»¥é€šè¿‡
msgid "Shared Runners"
msgstr "共享Runner"
+msgid "Shared projects"
+msgstr "已分享项目"
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr "通过é‡ç½®æ­¤å‘½å空间的æµæ°´çº¿åˆ†é’Ÿæ•°ï¼Œå½“å‰ä½¿ç”¨çš„分钟数将被归零。"
@@ -5648,7 +6763,7 @@ msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr "é‡ç½®å·²ç”¨æµæ°´çº¿åˆ†é’Ÿæ•°"
msgid "Sherlock Transactions"
-msgstr ""
+msgstr "Sherlock事物"
msgid "Show command"
msgstr "显示相关命令"
@@ -5691,10 +6806,10 @@ msgid "Sidebar|Weight"
msgstr "æƒé‡"
msgid "Sign in"
-msgstr ""
+msgstr "登录"
msgid "Sign in / Register"
-msgstr ""
+msgstr "登录/注册"
msgid "Sign in to %{group_name}"
msgstr "登录到 %{group_name}"
@@ -5711,12 +6826,18 @@ msgstr "登录é™åˆ¶"
msgid "Sign-up restrictions"
msgstr "注册é™åˆ¶"
+msgid "Size"
+msgstr "大å°"
+
msgid "Size and domain settings for static websites"
msgstr "é™æ€ç½‘站的大å°å’ŒåŸŸè®¾ç½®"
msgid "Slack application"
msgstr "Slack应用"
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr "Slack集æˆå…许您通过èŠå¤©çª—å£ä¸­çš„shash命令与GitLab交互。"
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr "更慢,但能确ä¿é¡¹ç›®å·¥ä½œç©ºé—´ä¸ŽåŽŸå§‹ç‰ˆæœ¬ä¸€è‡´ï¼›å› å…¶å¯¹æ¯ä¸ªä½œä¸šå‡ä»Žå¤´å¼€å§‹å…‹éš†ä»“库"
@@ -5732,14 +6853,20 @@ msgstr "出错了,抱歉。"
msgid "Something went wrong on our end. Please try again!"
msgstr "æœåŠ¡å™¨ç«¯å‡ºçŽ°é—®é¢˜ï¼Œè¯·é‡è¯•ã€‚"
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr "试图改å˜è¿™ä¸ªè®®é¢˜çš„ç§å¯†æ€§æ—¶å‡ºçŽ°é”™è¯¯"
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr "è¯•å›¾æ”¹å˜ %{issuableDisplayName} çš„é”定状æ€æ—¶å‡ºé”™äº†"
+
msgid "Something went wrong when toggling the button"
msgstr "点击按钮时出错"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr "关闭 %{issuable} 时出错。请ç¨åŽé‡è¯•"
-msgid "Something went wrong while fetching assignees list"
-msgstr "获å–指派人列表时å‘生错误"
+msgid "Something went wrong while fetching %{listType} list"
+msgstr "åœ¨èŽ·å– %{listType} 列表时出错了"
msgid "Something went wrong while fetching group member contributions"
msgstr "获å–群组æˆå‘˜è´¡çŒ®æ—¶å‡ºé”™"
@@ -5792,6 +6919,9 @@ msgstr "最大群组"
msgid "SortOptions|Largest repository"
msgstr "最大仓库"
+msgid "SortOptions|Last Contact"
+msgstr "最åŽè”系人"
+
msgid "SortOptions|Last created"
msgstr "最近创建"
@@ -5822,6 +6952,9 @@ msgstr "增加æƒé‡"
msgid "SortOptions|Most popular"
msgstr "最å—欢迎"
+msgid "SortOptions|Most stars"
+msgstr "最多星标"
+
msgid "SortOptions|Name"
msgstr "å称"
@@ -5852,6 +6985,9 @@ msgstr "优先"
msgid "SortOptions|Recent sign in"
msgstr "最近登录"
+msgid "SortOptions|Start date"
+msgstr "开始日期"
+
msgid "SortOptions|Start later"
msgstr "ç¨åŽå¼€å§‹"
@@ -5882,6 +7018,9 @@ msgstr "垃圾邮件åŠé˜²æœºå™¨äººä¿æŠ¤"
msgid "Specific Runners"
msgstr "专用Runner"
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr "指定电å­é‚®ä»¶åœ°å€æ­£åˆ™è¡¨è¾¾å¼æ¨¡å¼ä»¥æ ‡è¯†é»˜è®¤å†…部用户。"
+
msgid "Specify the following URL during the Runner setup:"
msgstr "在 Runner 设置时指定以下 URL:"
@@ -5924,6 +7063,9 @@ msgstr "已星标项目"
msgid "Start a %{new_merge_request} with these changes"
msgstr "由此更改 %{new_merge_request}"
+msgid "Start date"
+msgstr "开始日期"
+
msgid "Start the Runner!"
msgstr "å¯åŠ¨ Runner!"
@@ -5940,7 +7082,7 @@ msgid "Status"
msgstr "状æ€"
msgid "Stop impersonation"
-msgstr ""
+msgstr "åœæ­¢èº«ä»½æ¨¡æ‹Ÿ"
msgid "Stop this environment"
msgstr "åœæ­¢å½“å‰çŽ¯å¢ƒ"
@@ -5952,16 +7094,19 @@ msgid "Storage"
msgstr "存储"
msgid "Storage:"
-msgstr ""
+msgstr "存储:"
msgid "Subgroups"
msgstr "å­ç¾¤ç»„"
+msgid "Subgroups and projects"
+msgstr "å­ç»„和项目"
+
msgid "Submit as spam"
msgstr "垃圾信æ¯ä¸¾æŠ¥"
msgid "Submit search"
-msgstr ""
+msgstr "æ交æœç´¢"
msgid "Subscribe"
msgstr "订阅"
@@ -5982,7 +7127,7 @@ msgid "System Hooks"
msgstr "系统钩å­"
msgid "System Info"
-msgstr ""
+msgstr "系统信æ¯"
msgid "System header and footer:"
msgstr "系统页头和页尾:"
@@ -5990,6 +7135,9 @@ msgstr "系统页头和页尾:"
msgid "System metrics (Custom)"
msgstr "系统指标(自定义)"
+msgid "System metrics (Kubernetes)"
+msgstr "系统指标(Kubernetes)"
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] "标签(%{tag_count})"
@@ -5997,6 +7145,9 @@ msgstr[0] "标签(%{tag_count})"
msgid "Tags"
msgstr "标签"
+msgid "Tags feed"
+msgstr "标签动æ€"
+
msgid "Tags:"
msgstr "标签:"
@@ -6078,6 +7229,12 @@ msgstr "目标分支"
msgid "Team"
msgstr "团队"
+msgid "Template"
+msgstr "模æ¿"
+
+msgid "Templates"
+msgstr "模æ¿"
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr "æœåŠ¡æ¡æ¬¾å议和éšç§æ”¿ç­–"
@@ -6093,6 +7250,9 @@ msgstr "ä¸å†æ˜¾ç¤º"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr "GitLab 中的高级全局æœç´¢åŠŸèƒ½æ˜¯ä¸€ä¸ªå¼ºå¤§ä¸”节çœæ‚¨çš„时间的æœç´¢æœåŠ¡ã€‚您å¯ä»¥æœç´¢å…¶ä»–团队的代ç ä»¥å¸®åŠ©æ‚¨å®Œå–„自己项目中的代ç ã€‚从而é¿å…创建é‡å¤çš„代ç æˆ–浪费时间。"
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr "Git LFS对象将<strong>ä¸ä¼š</strong>被åŒæ­¥ã€‚"
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr "议题跟踪用于管ç†éœ€æ±‚改进或者解决的问题"
@@ -6102,6 +7262,9 @@ msgstr "议题跟踪用于管ç†éœ€æ±‚改进或者解决的问题。请注册或
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "在需è¦ç›¸äº’ TLS 与外部授æƒæœåŠ¡é€šä¿¡æ—¶ä½¿ç”¨çš„ X509 è¯ä¹¦ã€‚如果ä¿ç•™ä¸ºç©º, 则在访问 HTTPS æ—¶ä»ç„¶éªŒè¯æœåŠ¡å™¨è¯ä¹¦ã€‚"
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr "字符çªå‡ºæ˜¾ç¤ºå™¨å¸®åŠ©æ‚¨å°†ä¸»é¢˜è¡Œä¿æŒä¸º %{titleLength} 字符并将正文包装为 %{bodyLength} 以便它们在git中å¯è¯»ã€‚"
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "ç¼–ç é˜¶æ®µæ¦‚述了从第一次æ交到创建åˆå¹¶è¯·æ±‚的时间。创建第一个åˆå¹¶è¯·æ±‚åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -6111,6 +7274,9 @@ msgstr "与该阶段相关的事件集åˆã€‚"
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr "该连接将在 %{timeout}åŽè¶…时。如仓库导入耗时超过该时间,请使用克隆/推é€ç»„åˆã€‚"
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr "将此作业部署为 %{environmentLink} 并未æˆåŠŸã€‚"
+
msgid "The fork relationship has been removed."
msgstr "派生关系已被删除。"
@@ -6127,7 +7293,7 @@ msgid "The number of attempts GitLab will make to access a storage."
msgstr "GitLab 访问存储的次数。"
msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr ""
+msgstr "访问存储最大失败次数,达到åŽå°†å¯¼è‡´GitLab完全阻止。å¯ä»¥åœ¨ç®¡ç†ç•Œé¢ä¸­é‡ç½®å¤±è´¥æ¬¡æ•°ï¼š %{link_to_health_page} 或使用 %{api_documentation_link} 。"
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "解密ç§é’¥æ‰€éœ€çš„密ç çŸ­è¯­ã€‚该项为å¯é€‰é¡¹, 并且内容被加密存储。"
@@ -6138,6 +7304,9 @@ msgstr "CIé…置文件的路径。默认为 <code>.gitlab-ci.yml</code>"
msgid "The phase of the development lifecycle."
msgstr "项目生命周期中的å„个阶段。"
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr "针对特定分支或标签,æµæ°´çº¿è®¡åˆ’将在未æ¥é‡å¤è¿è¡Œæµæ°´çº¿ã€‚这些计划的æµæ°´çº¿å°†ä»Žå…³è”用户继承有é™çš„项目访问æƒé™ã€‚"
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "计划阶段概述了从议题添加到日程到推é€é¦–次æ交的时间。当首次推é€æ交åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -6165,6 +7334,9 @@ msgstr "该项目的仓库是空的"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr "该仓库必须å¯é€šè¿‡<code>http://</code>, <code>https://</code> 或 <code>git://</code>进行访问。"
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr "该仓库必须å¯é€šè¿‡ <code>http://</code>,<code>https://</code>,<code>ssh://</code> å’Œ <code>git://</code>进行访问。"
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "审阅阶段概述了从创建åˆå¹¶è¯·æ±‚到被åˆå¹¶çš„时间。当创建第一个åˆå¹¶è¯·æ±‚åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -6177,6 +7349,9 @@ msgstr "Runner用于签出项目的安全令牌"
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "预å‘布阶段概述了从åˆå¹¶è¯·æ±‚被åˆå¹¶åˆ°éƒ¨ç½²è‡³ç”Ÿäº§çŽ¯å¢ƒçš„总时间。首次部署到生产环境åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
+msgid "The tabs below will be removed in a future version"
+msgstr "以下选项å¡å°†åœ¨ä»¥åŽçš„版本中删除"
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "测试阶段概述了 GitLab CI 为相关åˆå¹¶è¯·æ±‚è¿è¡Œæ¯ä¸ªæµæ°´çº¿æ‰€éœ€çš„时间。当第一个æµæ°´çº¿è¿è¡Œå®ŒæˆåŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -6187,20 +7362,29 @@ msgid "The time in seconds GitLab will try to access storage. After this time a
msgstr "GitLab å°†å°è¯•è®¿é—®å­˜å‚¨çš„时间(秒)。在此时间之åŽå°†å¼•å‘超时错误。"
msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
-msgstr ""
+msgstr "存储检查之间的秒数。如果当å‰æ£€æŸ¥å°šæœªå®Œæˆï¼ŒGitLab将跳过下一次检查。"
msgid "The time taken by each data entry gathered by that stage."
msgstr "该阶段æ¯æ¡æ•°æ®æ‰€èŠ±çš„时间"
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr "æ›´æ–°æ“作将在 %{number_of_minutes} 分钟åŽè¶…时。对于大型存储库,请使用clone/push组åˆã€‚"
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr "使用情况检测(usage ping)å·²ç¦ç”¨ï¼Œæ— æ³•é€šè¿‡æ­¤è¡¨å•è¿›è¡Œé…置。"
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
+msgstr "用户映射是一个JSON文档,将å‚与项目的Google Code用户映射到他们将导入GitLab的电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·åçš„æ–¹å¼ã€‚您å¯ä»¥é€šè¿‡æ›´æ”¹ <code>:</code>å³ä¾§çš„值æ¥æ›´æ”¹æ­¤å€¼ã€‚请务必在左侧ä¿ç•™å‘¨å›´çš„åŒå¼•å·ï¼Œå…¶ä»–标点符å·ä»¥åŠç”µå­é‚®ä»¶åœ°å€æˆ–用户å。"
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
-msgstr ""
+msgstr "用户映射是å‚与项目的 FogBugz 用户的电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å将被导入 GitLab çš„æ–¹å¼ã€‚您å¯ä»¥é€šè¿‡ä»¥ä¸‹è¡¨æ ¼æ¥ä¿®æ”¹æ˜ å°„关系。"
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "中ä½æ•°æ˜¯ä¸€ä¸ªæ•°åˆ—中最中间的值。例如在 3ã€5ã€9 之间,中ä½æ•°æ˜¯ 5。在 3ã€5ã€7ã€8 之间,中ä½æ•°æ˜¯ (5 + 7)/ 2 = 6。"
+msgid "There are no archived projects yet"
+msgstr "ç›®å‰è¿˜æ²¡æœ‰å·²å½’档的项目"
+
msgid "There are no issues to show"
msgstr "当å‰æ— è®®é¢˜"
@@ -6210,9 +7394,24 @@ msgstr "ç›®å‰è¿˜æ²¡æœ‰æ ‡ç­¾"
msgid "There are no merge requests to show"
msgstr "当å‰æ— åˆå¹¶è¯·æ±‚"
+msgid "There are no projects shared with this group yet"
+msgstr "还没有与该群组共享的项目"
+
+msgid "There are no staged changes"
+msgstr "没有暂存的修改"
+
+msgid "There are no unstaged changes"
+msgstr "没有未暂存的修改"
+
msgid "There are problems accessing Git storage: "
msgstr "访问 Git 存储时出现问题:"
+msgid "There was an error adding a todo."
+msgstr "添加待办事项时出现错误"
+
+msgid "There was an error deleting the todo."
+msgstr "删除待办事项时出现错误。"
+
msgid "There was an error loading users activity calendar."
msgstr "加载用户活动日历时出错。"
@@ -6235,19 +7434,31 @@ msgid "They can be managed using the %{link}."
msgstr "他们å¯ä»¥é€šè¿‡ %{link} 进行管ç†ã€‚"
msgid "Third party offers"
-msgstr ""
+msgstr "第三方优惠"
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr "æ­¤GitLab实例尚未æ供任何共享Runner。管ç†å‘˜å¯ä»¥åœ¨ç®¡ç†åŒºåŸŸä¸­æ³¨å†Œå…±äº«Runner。"
msgid "This application was created by %{link_to_owner}."
-msgstr ""
+msgstr "这个应用程åºæ˜¯ç”± %{link_to_owner} 创建的。"
msgid "This application will be able to:"
-msgstr ""
+msgstr "此应用程åºå°†å¯ä»¥ï¼š"
msgid "This board's scope is reduced"
-msgstr ""
+msgstr "此看æ¿èŒƒå›´ç¼©å°äº†"
+
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr "自您开始编辑åŽ, 此分支已更改。您想创建一个新的分支å—?"
+
+msgid "This container registry has been scheduled for deletion."
+msgstr "此容器注册表已安排删除。"
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr "此日期在截止日期之åŽï¼Œå› æ­¤è¯¥å²è¯—故事ä¸ä¼šå‡ºçŽ°åœ¨è·¯çº¿å›¾ä¸­ã€‚"
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr "此日期在开始日期之å‰ï¼Œå› æ­¤è¯¥å²è¯—故事ä¸ä¼šå‡ºçŽ°åœ¨è·¯çº¿å›¾ä¸­ã€‚"
msgid "This diff is collapsed."
msgstr "此差异已折å ã€‚"
@@ -6256,7 +7467,7 @@ msgid "This directory"
msgstr "当å‰ç›®å½•"
msgid "This group"
-msgstr ""
+msgstr "当å‰ç¾¤ç»„"
msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
msgstr "此群组å…许您使用%{group_name} å•ç‚¹ç™»å½•å¸æˆ·ç™»å½•ã€‚这将会é‡å®šå‘到外部登录页é¢ã€‚"
@@ -6300,9 +7511,30 @@ msgstr "作业还未被触å‘"
msgid "This job has not started yet"
msgstr "作业还未开始"
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr "此项作业已过时,无法部署到 %{environmentLink}。"
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr "此项作业已过时,无法部署到 %{environmentLink}。查看最新的部署 %{deploymentLink}。"
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr "此作业将创建部署为 %{environmentLink} 并将最åŽä¸€æ¬¡éƒ¨ç½²%{deploymentLink}覆盖 。"
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr "这项工作正在创建一个 %{environmentLink}的部署。"
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "作业挂起中,等待进入队列"
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr "此作业已åœæ­¢ã€‚因为分é…有如下标签的Runner都ä¸åœ¨çº¿ï¼š"
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr "此作业已åœæ­¢ã€‚因为没有活动的Runnerå¯ä»¥å¤„ç†æ­¤é¡¹ä½œä¸šã€‚"
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr "此作业最近部署到 %{link}。"
+
msgid "This job requires a manual action"
msgstr "作业需手工æ“作"
@@ -6312,6 +7544,9 @@ msgstr "在创建一个空的仓库或导入现有仓库之å‰ï¼Œå°†æ— æ³•æŽ¨é€
msgid "This merge request is locked."
msgstr "æ­¤åˆå¹¶è¯·æ±‚å·²é”定。"
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr "由于您没有当å‰åˆ†æ”¯çš„写入æƒé™ï¼Œå› æ­¤ç¦ç”¨æ­¤é€‰é¡¹"
+
msgid "This option is disabled while you still have unstaged changes"
msgstr "有未暂存更改时此选项会被ç¦ç”¨ã€‚"
@@ -6327,15 +7562,30 @@ msgstr "当å‰é¡¹ç›®"
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr "该项目ä¸å±žäºŽä»»ä½•ç¾¤ç»„,因此ä¸èƒ½ä½¿ç”¨ç¾¤ç»„Runner。"
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr "此项目未å¯ç”¨è´¦å•ã€‚è¦åˆ›å»ºç¾¤é›†ï¼Œè¯· <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">å¯ç”¨è´¦å• <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> 并é‡è¯•ã€‚"
+
msgid "This repository"
msgstr "当å‰ä»“库"
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr "æ­¤Runner仅在å—ä¿æŠ¤åˆ†æ”¯ä¸Šè§¦å‘çš„æµæ°´çº¿ä¸Šè¿è¡Œ"
+
msgid "This source diff could not be displayed because it is too large."
msgstr "此代ç å·®å¼‚无法显示,因为它太大了。"
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr "当低于项目定义的超时时间时,此超时将优先"
+
msgid "This user has no identities"
msgstr "该用户没有身份标识"
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr "此用户将æˆä¸ºæ´»åŠ¨æµä¸­æ‰€æœ‰äº‹ä»¶çš„作者,例如创建新分支或者推é€æ–°æ交到现有分支。"
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr "此用户将æˆä¸ºæ´»åŠ¨æµä¸­æ‰€æœ‰äº‹ä»¶çš„作者,例如创建新分支或者推é€æ–°æ交到现有分支。在创建或é‡æ–°æŒ‡å®šæ—¶æ‚¨ä»…å¯å°†è‡ªå·±æŒ‡å®šä¸ºé•œåƒç”¨æˆ·ã€‚"
+
msgid "This will delete the custom metric, Are you sure?"
msgstr "æ­¤æ“作将删除自定义指标,确定继续å—?"
@@ -6536,11 +7786,23 @@ msgstr "è¦è¿žæŽ¥GitHub存储库,首先需è¦æŽˆæƒGitLab访问列表中的Git
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr "如è¦è¿žæŽ¥SVN仓库,请查看 %{svn_link}。"
+msgid "To define internal users, first enable new users set to external"
+msgstr "è¦å®šä¹‰å†…部用户,请首先å¯ç”¨è®¾ç½®ä¸ºå¤–部的新用户"
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr "è¦å¯ç”¨å®ƒå¹¶æŸ¥çœ‹ç”¨æˆ·ä¸–代表,请访问 %{application_settings_link_start}应用程åºè®¾ç½®%{application_settings_link_end}。"
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
-msgstr ""
+msgstr "首先请在下é¢è¾“入您的FogBugz URL和登录信æ¯ã€‚下一步,您将å¯ä»¥æ˜ å°„用户并选择è¦å¯¼å…¥çš„项目。"
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
-msgstr ""
+msgstr "首先,请您输入您的 Gitea æœåŠ¡å™¨åœ°å€å’Œä¸€ä¸ª %{link_to_personal_token}。"
+
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr "为了帮助改进 GitLab åŠå…¶ç”¨æˆ·ä½“验, GitLab 将定期收集使用信æ¯ã€‚"
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr "为了帮助改进GitLab,我们希望定期收集使用信æ¯ã€‚è¿™å¯ä»¥é€šè¿‡ %{settings_link_start}设置%{link_end}éšæ—¶æ›´æ”¹ã€‚ %{info_link_start}更多信æ¯%{link_end}"
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}导入GitHub仓库。当创建个人访问令牌时,需è¦é€‰æ‹© <code>repo</code> 范围,以显示å¯å¯¼å…¥çš„公共和ç§æœ‰çš„仓库列表。"
@@ -6552,7 +7814,7 @@ msgid "To import an SVN repository, check out %{svn_link}."
msgstr "è¦å¯¼å…¥SVN仓库,请查看 %{svn_link}。"
msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
-msgstr ""
+msgstr "如需将整个GitLab项目从å¦ä¸€ä¸ªGitLabæœåŠ¡å™¨ç§»åŠ¨æˆ–å¤åˆ¶åˆ°æ­¤æœåŠ¡å™¨ï¼Œè¯·è®¿é—®åŽŸé¡¹ç›®çš„设置页é¢ï¼Œç”Ÿæˆå¯¼å‡ºæ–‡ä»¶ï¼Œç„¶åŽåœ¨æ­¤å¤„上载。"
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr "è¦ä»…为外部仓库使用CI / CD功能时,请选择</strong>使用外部仓库è¿è¡ŒCI/CD<strong>。"
@@ -6564,13 +7826,13 @@ msgid "To start serving your jobs you can add Runners to your group"
msgstr "è¦å¼€å§‹æ‰§è¡Œä»»åŠ¡ï¼Œè¯·æŠŠRunner加到群组中"
msgid "To this GitLab instance"
-msgstr ""
+msgstr "转至此GitLab实例"
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr "如需验è¯GitLab CI设置,请访问当å‰é¡¹ç›®çš„'CI/CD → æµæ°´çº¿',然åŽç‚¹å‡»'CI Lint'按钮。"
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
-msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。åªæ˜¾ç¤ºè¿‡åŽ»3个月和接下æ¥3个月的å²è¯—故事。"
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在月视图中,åªæ˜¾ç¤ºä¸Šä¸ªæœˆï¼Œæœ¬æœˆä»¥åŠæŽ¥ä¸‹æ¥5个月的å²è¯—故事."
msgid "To widen your search, change or remove filters."
msgstr "需è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–移除过滤æ¡ä»¶ã€‚"
@@ -6579,16 +7841,19 @@ msgid "Todo"
msgstr "待办事项"
msgid "Todos"
-msgstr ""
+msgstr "待办事项"
msgid "Toggle Sidebar"
msgstr "切æ¢ä¾§è¾¹æ "
+msgid "Toggle commit description"
+msgstr "切æ¢æ交æè¿°"
+
msgid "Toggle discussion"
msgstr "开关讨论"
msgid "Toggle navigation"
-msgstr ""
+msgstr "切æ¢å¯¼èˆª"
msgid "Toggle sidebar"
msgstr "切æ¢è¾¹æ "
@@ -6599,6 +7864,9 @@ msgstr "切æ¢çŠ¶æ€ï¼šå…³é—­"
msgid "ToggleButton|Toggle Status: ON"
msgstr "切æ¢çŠ¶æ€ï¼šå¼€å¯"
+msgid "Token"
+msgstr "令牌"
+
msgid "Too many changes to show."
msgstr "è¦æ˜¾ç¤ºçš„å˜æ›´å¤ªå¤šã€‚"
@@ -6624,7 +7892,16 @@ msgid "Track time with quick actions"
msgstr "使用快æ·æ“作æ¥ç»Ÿè®¡å·¥æ—¶"
msgid "Trending"
-msgstr ""
+msgstr "趋势"
+
+msgid "Trigger"
+msgstr "触å‘器"
+
+msgid "Trigger pipelines for mirror updates"
+msgstr "触å‘é•œåƒæ›´æ–°çš„æµæ°´çº¿"
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr "从上游存储库更新分支或标记时触å‘æµæ°´çº¿ã€‚如果上游存储库更新频ç¹ï¼Œè¿™å¯èƒ½ä¼šå¤§å¤§å¢žåŠ CI Runnerçš„è´Ÿè·ã€‚åªæœ‰å½“你知é“CI Runner的处ç†èƒ½åŠ›èƒ½å¤Ÿæ‰¿å—这样的负è·æ—¶ï¼Œä½ æ‰åº”å¯ç”¨æ­¤åŠŸèƒ½ã€‚"
msgid "Trigger this manual action"
msgstr "触å‘此手动æ“作"
@@ -6639,7 +7916,10 @@ msgid "Turn on Service Desk"
msgstr "å¯ç”¨æœåŠ¡å°"
msgid "Twitter"
-msgstr ""
+msgstr "Twitter"
+
+msgid "Type"
+msgstr "类型"
msgid "Unable to load the diff. %{button_try_again}"
msgstr "无法加载差异。 %{button_try_again}"
@@ -6647,18 +7927,30 @@ msgstr "无法加载差异。 %{button_try_again}"
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr "由于\"%{reason}\"的原因,您暂时ä¸èƒ½è¿›å…¥é…置了SAML 的群组"
+msgid "Unable to update this epic at this time."
+msgstr "当å‰æ— æ³•æ›´æ–°æ­¤å²è¯—故事。"
+
+msgid "Undo"
+msgstr "撤消"
+
msgid "Unknown"
msgstr "未知的"
msgid "Unlock"
msgstr "解é”"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr "è§£é” %{issuableDisplayName} ? <strong>所有人</strong> 都将å¯ä»¥å‘表评论。"
+
msgid "Unlocked"
msgstr "已解é”"
msgid "Unresolve discussion"
msgstr "待解决的讨论"
+msgid "Unstage"
+msgstr "未暂存"
+
msgid "Unstage all changes"
msgstr "å–消全部暂存更改"
@@ -6693,11 +7985,17 @@ msgid "Up to date"
msgstr "已是最新"
msgid "Update"
-msgstr ""
+msgstr "æ›´æ–°"
+
+msgid "Update now"
+msgstr "ç«‹å³æ›´æ–°"
msgid "Update your group name, description, avatar, and other general settings."
msgstr "更新您的群组å称ã€è¯´æ˜Žã€å¤´åƒä»¥åŠå…¶å®ƒé€šç”¨è®¾ç½®ã€‚"
+msgid "Updating"
+msgstr "更新中"
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "å‡çº§æ‚¨çš„订阅计划以å¯ç”¨é«˜çº§å…¨å±€æœç´¢ã€‚"
@@ -6714,7 +8012,7 @@ msgid "Upgrade your plan to improve Issue boards."
msgstr "å‡çº§æ‚¨çš„订阅计划以使用增强的议题看æ¿ã€‚"
msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
-msgstr ""
+msgstr "在这里上传 <code>GoogleCodeProjectHosting.json</code>:"
msgid "Upload New File"
msgstr "上传新文件"
@@ -6722,20 +8020,20 @@ msgstr "上传新文件"
msgid "Upload file"
msgstr "上传文件"
-msgid "Upload new avatar"
-msgstr "上传新头åƒ"
-
msgid "UploadLink|click to upload"
msgstr "点击上传"
msgid "Upvotes"
msgstr "顶"
+msgid "Usage ping is not enabled"
+msgstr "使用情况检测(usage ping)未å¯ç”¨"
+
msgid "Usage statistics"
msgstr "使用情况统计"
msgid "Use <code>%{native_redirect_uri}</code> for local tests"
-msgstr ""
+msgstr "使用<code>%{native_redirect_uri}</code>进行本地测试"
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr "在GitLab内部使用æœåŠ¡å°é€šè¿‡ç”µå­é‚®ä»¶ä¸Žç”¨æˆ·è”系(例如æ供客户支æŒï¼‰"
@@ -6744,7 +8042,10 @@ msgid "Use group milestones to manage issues from multiple projects in the same
msgstr "使用群组里程碑å¯ä»¥ç»Ÿä¸€ç®¡ç†å¤šä¸ªé¡¹ç›®ä¸­åŒä¸€é‡Œç¨‹ç¢‘的议题。"
msgid "Use one line per URI"
-msgstr ""
+msgstr "æ¯ä¸ªURIå ä¸€è¡Œ"
+
+msgid "Use template"
+msgstr "使用模æ¿"
msgid "Use the following registration token during setup:"
msgstr "在安装过程中使用以下注册令牌:"
@@ -6755,14 +8056,17 @@ msgstr "使用全局通知设置"
msgid "Used by members to sign in to your group in GitLab"
msgstr "ä¾›æˆå‘˜ç™»å½•æ‚¨çš„GitLab群组"
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr "用户世代表仅在å¯ç”¨ %{usage_ping_link_start}使用情况检测(usage ping)%{usage_ping_link_end} 时显示。"
+
msgid "User Settings"
-msgstr ""
+msgstr "用户设置"
msgid "User and IP Rate Limits"
msgstr "用户和IP频率é™åˆ¶"
msgid "User map"
-msgstr ""
+msgstr "用户映射"
msgid "Users"
msgstr "用户"
@@ -6788,6 +8092,9 @@ msgstr "验è¯ä¿¡æ¯"
msgid "Verified"
msgstr "已验è¯"
+msgid "Version"
+msgstr "版本"
+
msgid "View epics list"
msgstr "查看å²è¯—故事列表"
@@ -6801,7 +8108,7 @@ msgid "View issue"
msgstr "查看议题"
msgid "View it on GitLab"
-msgstr ""
+msgstr "使用GitLab查看"
msgid "View jobs"
msgstr "查看作业"
@@ -6825,10 +8132,10 @@ msgid "Visibility and access controls"
msgstr "å¯è§æ€§ä¸Žè®¿é—®æŽ§åˆ¶"
msgid "Visibility level:"
-msgstr ""
+msgstr "å¯è§æ€§çº§åˆ«:"
msgid "Visibility:"
-msgstr ""
+msgstr "å¯è§æ€§ï¼š"
msgid "VisibilityLevel|Internal"
msgstr "内部"
@@ -6846,7 +8153,7 @@ msgid "Want to see the data? Please ask an administrator for access."
msgstr "æƒé™ä¸è¶³ã€‚如需查看相关数æ®ï¼Œè¯·å‘管ç†å‘˜ç”³è¯·æƒé™ã€‚"
msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
-msgstr ""
+msgstr "我们在 %{humanized_resource_name} 检测到潜在滥用行为。请输入此reCAPTCHA验è¯ç å¹¶ç»§ç»­ã€‚"
msgid "We don't have enough data to show this stage."
msgstr "该阶段的数æ®ä¸è¶³ï¼Œæ— æ³•æ˜¾ç¤ºã€‚"
@@ -7029,10 +8336,10 @@ msgid "Yes, add it"
msgstr "是的,添加它"
msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
+msgstr "是的,让我将Google Code用户映射到全å或GitLab用户。"
msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
-msgstr ""
+msgstr "您是一å管ç†å‘˜ï¼Œè¿™æ„味ç€æŽˆäºˆå¯¹ <strong>%{client_name}</strong> 访问æƒé™å°†å…许他们作为管ç†å‘˜ä¸ŽGitLab进行交互。请谨慎æ“作。"
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "å³å°†åˆ é™¤ %{group_name}。已删除的群组无法æ¢å¤ï¼ç¡®å®šç»§ç»­å—?"
@@ -7065,7 +8372,7 @@ msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
msgstr "您也å¯ä»¥é€šè¿‡%{linkStart}Lint%{linkEnd}测试.gitlab-ci.yml"
msgid "You can easily contribute to them by requesting to join these groups."
-msgstr ""
+msgstr "您å¯ä»¥è¯·æ±‚加入这些群组,以便为群组项目åšå‡ºè´¡çŒ®ã€‚"
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr "å¯ä»¥è½»æ¾åœ°åœ¨Kubernetes集群上安装Runner。 %{link_to_help_page}"
@@ -7079,33 +8386,33 @@ msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"
msgid "You can only edit files when you are on a branch"
msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šç¼–辑文件"
+msgid "You can reset runners registration token by pressing a button below."
+msgstr "您å¯ä»¥é€šè¿‡ä¸‹é¢çš„按钮é‡ç½®Runner注册令牌。"
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr "您å¯ä»¥ä½¿ç”¨äº¤äº’模å¼ï¼Œé€šè¿‡é€‰æ‹© %{use_ours} 或 %{use_theirs} 按钮æ¥è§£å†³åˆå¹¶å†²çªã€‚也å¯ä»¥é€šè¿‡ç›´æŽ¥ç¼–辑文件æ¥è§£å†³åˆå¹¶å†²çªã€‚然åŽå°†è¿™äº›æ›´æ”¹æ交到 %{branch_name}"
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr "您å¯ä»¥å°†ä½œä¸šè®¾ç½®ä¸ºä»…使用具有特定标签的Runners。请使用逗å·åˆ†éš”ä¸åŒæ ‡ç­¾ã€‚"
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr "您ä¸èƒ½å†™å…¥åªè¯»çš„æ¬¡è¦ GitLab Geo 实例。请改用%{link_to_primary_node}。"
msgid "You cannot write to this read-only GitLab instance."
msgstr "您ä¸èƒ½å†™å…¥è¿™ä¸ªåªè¯»çš„ GitLab 实例。"
-msgid "You do not have any assigned merge requests"
-msgstr "没有任何指派给您的åˆå¹¶è¯·æ±‚"
-
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr "您没有正确的æƒé™æ¥æ›´æ”¹LDAP组åŒæ­¥ä¸­çš„设置。"
msgid "You don't have any applications"
-msgstr ""
+msgstr "你没有任何应用程åº"
msgid "You don't have any authorized applications"
-msgstr ""
+msgstr "您没有任何授æƒçš„应用"
msgid "You have no permissions"
msgstr "没有æƒé™"
-msgid "You have not created any merge requests"
-msgstr "您尚未创建任何åˆå¹¶è¯·æ±‚"
-
msgid "You have reached your project limit"
msgstr "您已达到项目数é‡é™åˆ¶"
@@ -7115,9 +8422,6 @@ msgstr "您必须接å—我们的æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–æ‰èƒ½æ³¨å†Œå¸æˆ·"
msgid "You must have maintainer access to force delete a lock"
msgstr "必须拥有维护者æƒé™æ‰èƒ½å¼ºåˆ¶åˆ é™¤é”"
-msgid "You must sign in to star a project"
-msgstr "必须登录æ‰èƒ½å¯¹é¡¹ç›®åŠ æ˜Ÿæ ‡"
-
msgid "You need a different license to enable FileLocks feature"
msgstr "需è¦ä½¿ç”¨ä¸Žå½“å‰ä¸åŒçš„许å¯(license) æ‰èƒ½å¯ç”¨FileLocks功能"
@@ -7127,6 +8431,12 @@ msgstr "您需è¦git-lfs版本 %{min_git_lfs_version} (或更高版本)æ‰èƒ
msgid "You need permission."
msgstr "需è¦ç›¸å…³çš„æƒé™ã€‚"
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr "您将丢失对此文件所åšçš„所有更改。此æ“作无法撤消。"
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr "您将丢失在此项目中所åšçš„所有未暂存的修改,此æ“作无法撤消。"
+
msgid "You will not get any notifications via email"
msgstr "ä¸ä¼šæ”¶åˆ°ä»»ä½•é€šçŸ¥é‚®ä»¶"
@@ -7155,16 +8465,16 @@ msgid "You'll need to use different branch names to get a valid comparison."
msgstr "需è¦ä½¿ç”¨ä¸åŒçš„分支æ‰èƒ½è¿›è¡Œæœ‰æ•ˆçš„比较。"
msgid "You're receiving this email because %{reason}."
-msgstr ""
+msgstr "您收到此电å­é‚®ä»¶ï¼Œæ˜¯å› ä¸º%{reason}。"
msgid "You're receiving this email because of your account on %{host}."
-msgstr ""
+msgstr "您收到这å°ç”µå­é‚®ä»¶ï¼Œæ˜¯å› ä¸ºæ‚¨åœ¨ %{host} 拥有å¸æˆ·ã€‚"
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr "您收到这å°ç”µå­é‚®ä»¶æ˜¯å› ä¸ºä½ åœ¨ %{host} 拥有å¸æˆ·ã€‚ %{manage_notifications_link} &middot; %{help_link}"
msgid "YouTube"
-msgstr ""
+msgstr "YouTube"
msgid "Your Groups"
msgstr "您的群组"
@@ -7182,10 +8492,10 @@ msgid "Your Todos"
msgstr "您的待办事项"
msgid "Your applications (%{size})"
-msgstr ""
+msgstr "你的应用程åº(%{size})"
msgid "Your authorized applications"
-msgstr ""
+msgstr "您已授æƒçš„应用"
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr "åˆå¹¶è¯·æ±‚已开å¯ï¼Œå¯ä»¥æ交å˜æ›´åˆ°%{branch_name}。"
@@ -7205,16 +8515,15 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr "您的项目"
+msgid "a deleted user"
+msgstr "已删除的用户"
+
msgid "ago"
msgstr "å‰"
msgid "among other things"
msgstr "åŠå…¶ä»–功能"
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] "åŠ%d个已修å¤çš„æ¼æ´ž"
-
msgid "assign yourself"
msgstr "分é…给自己"
@@ -7239,29 +8548,54 @@ msgstr "%{linkStartTag}了解更多有关SAST %{linkEndTag}çš„ä¿¡æ¯"
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr "ciReport|%{namespace} å— %{vulnerability} å½±å“。"
-msgid "ciReport|%{packagesString} and "
-msgstr "%{packagesString} 和"
-
-msgid "ciReport|%{packagesString} and %{lastPackage}"
-msgstr "%{packagesString} 和 %{lastPackage}"
-
msgid "ciReport|%{remainingPackagesCount} more"
msgstr "还有%{remainingPackagesCount} 个"
-msgid "ciReport|%{reportName} is loading"
-msgstr "%{reportName} 加载中"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
-msgstr "加载%{reportName} 时出错"
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
-msgstr "%{type} 未å‘现新的安全æ¼æ´ž"
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
-msgstr "%{type} 未å‘现安全æ¼æ´ž"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
-msgstr "%{type} 未å‘现安全æ¼æ´ž"
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
msgid "ciReport|Class"
msgstr "ç±»"
@@ -7272,39 +8606,30 @@ msgstr "代ç è´¨é‡"
msgid "ciReport|Confidence"
msgstr "置信水平"
+msgid "ciReport|Container scanning"
+msgstr ""
+
msgid "ciReport|Container scanning detected"
msgstr "容器安全扫æ检测到"
msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr "容器扫æå¯ä»¥æ£€æµ‹Dockeré•œåƒä¸­ä¸­å·²çŸ¥çš„安全æ¼æ´žã€‚"
-msgid "ciReport|Container scanning is loading"
-msgstr "容器安全扫æ加载中"
-
-msgid "ciReport|Container scanning resulted in error while loading results"
-msgstr "加载容器扫æ结果时出错"
+msgid "ciReport|DAST"
+msgstr ""
msgid "ciReport|DAST detected"
msgstr "DAST检测到"
-msgid "ciReport|DAST is loading"
-msgstr "DAST加载中"
-
-msgid "ciReport|DAST resulted in error while loading results"
-msgstr "加载DAST结果时出错"
-
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr "ä¾èµ–项扫æå¯ä»¥æ£€æµ‹æºä»£ç ä¾èµ–项中已知的æ¼æ´žã€‚"
+msgid "ciReport|Dependency scanning"
+msgstr ""
+
msgid "ciReport|Dependency scanning detected"
msgstr "ä¾èµ–关系扫æ检测到"
-msgid "ciReport|Dependency scanning is loading"
-msgstr "ä¾èµ–项扫æ加载中"
-
-msgid "ciReport|Dependency scanning resulted in error while loading results"
-msgstr "加载ä¾èµ–项扫æ结果时出错"
-
msgid "ciReport|Description"
msgstr "说明"
@@ -7335,11 +8660,16 @@ msgstr "实例"
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr "了解有关安全报告(Alpha)的更多信æ¯ã€‚"
-msgid "ciReport|Learn more about whitelisting"
-msgstr "了解更多关于白åå•çš„ä¿¡æ¯"
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] "许å¯è¯ç®¡ç†ä»…检测到æºåˆ†æ”¯çš„ %d 许å¯è¯"
-msgid "ciReport|License management detected %{licenseInfo}"
-msgstr "许å¯è¯ç®¡ç†æ£€æµ‹åˆ° %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] "许å¯è¯ç®¡ç†æ£€æµ‹åˆ° %d 个新的许å¯è¯"
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr "许å¯è¯ç®¡ç†ä»…未检测到æºåˆ†æ”¯ä¸Šçš„许å¯è¯"
msgid "ciReport|License management detected no new licenses"
msgstr "许å¯è¯ç®¡ç†æœªæ£€æµ‹åˆ°æ–°çš„许å¯è¯"
@@ -7368,24 +8698,18 @@ msgstr "性能指标"
msgid "ciReport|Revert dismissal"
msgstr "å–消忽略"
+msgid "ciReport|SAST"
+msgstr ""
+
msgid "ciReport|SAST detected"
msgstr "SAST检测到"
-msgid "ciReport|SAST is loading"
-msgstr "SAST加载中"
-
-msgid "ciReport|SAST resulted in error while loading results"
-msgstr "加载SAST结果时出错"
-
msgid "ciReport|Security scanning"
msgstr "安全扫æ"
msgid "ciReport|Security scanning failed loading any results"
msgstr "安全扫æ无法加载任何结果"
-msgid "ciReport|Security scanning is loading"
-msgstr "安全扫æ加载中"
-
msgid "ciReport|Severity"
msgstr "严é‡æ€§"
@@ -7416,14 +8740,15 @@ msgstr "加载ä¾èµ–项扫æ报告时出错"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "å–消忽略æ“作时å‘生错误。请å†è¯•ä¸€æ¬¡ã€‚"
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr "未批准的æ¼æ´ž (红色) å¯ä»¥æ ‡è®°ä¸ºå·²æ‰¹å‡†ã€‚"
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "å°† %{name} 从 %{version} å‡çº§åˆ° %{fixed}。"
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] "已使用 %{packagesString} 和 %{lastPackage}"
+
msgid "ciReport|View full report"
-msgstr ""
+msgstr "查看完整报告"
msgid "ciReport|no vulnerabilities"
msgstr "未检测到安全æ¼æ´ž"
@@ -7434,6 +8759,12 @@ msgstr "于æµæ°´çº¿"
msgid "command line instructions"
msgstr "命令行指å—"
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr "å³å°†å…³é—­ç§å¯†æ€§ã€‚这将使得 <strong>所有用户</strong>都å¯ä»¥æŸ¥çœ‹å¹¶ä¸”评论当å‰è®®é¢˜ã€‚"
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr "å³å°†è®¾ç½®ç§å¯†æ€§ã€‚这将使得 <strong>至少有Reporter以上æƒé™</strong>的团队æˆå‘˜æ‰èƒ½æŸ¥çœ‹å¹¶ä¸”评论当å‰è®®é¢˜ã€‚"
+
msgid "connecting"
msgstr "连接中"
@@ -7441,7 +8772,7 @@ msgid "could not read private key, is the passphrase correct?"
msgstr "无法读å–ç§é’¥ï¼Œå¯†ç çŸ­è¯­æ˜¯å¦æ­£ç¡®ï¼Ÿ"
msgid "customize"
-msgstr ""
+msgstr "自定义"
msgid "day"
msgid_plural "days"
@@ -7450,22 +8781,11 @@ msgstr[0] "天"
msgid "deploy token"
msgstr "部署令牌"
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] "检测到%d个安全æ¼æ´žå·²ä¿®å¤"
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] "检测到%d个新的安全æ¼æ´ž"
-
-msgid "detected no vulnerabilities"
-msgstr "未检测到安全æ¼æ´ž"
-
msgid "disabled"
msgstr "å·²ç¦ç”¨"
msgid "done"
-msgstr ""
+msgstr "完æˆ"
msgid "enabled"
msgstr "å·²å¯ç”¨"
@@ -7479,12 +8799,19 @@ msgstr "对于这个项目"
msgid "here"
msgstr "此处"
+msgid "https://your-bitbucket-server"
+msgstr "https://your-bitbucket-server"
+
msgid "import flow"
-msgstr ""
+msgstr "导入æµç¨‹"
msgid "importing"
msgstr "导入中"
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] "实例已完æˆ"
+
msgid "is invalid because there is downstream lock"
msgstr "因下游é”定而无效"
@@ -7494,9 +8821,15 @@ msgstr "因上游é”定而无效"
msgid "is not a valid X509 certificate."
msgstr "ä¸æ˜¯æœ‰æ•ˆçš„X509è¯ä¹¦ã€‚"
+msgid "issue boards"
+msgstr "议题看æ¿"
+
msgid "latest version"
msgstr "最新版本"
+msgid "license management"
+msgstr "许å¯è¯ç®¡ç†"
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr "被 %{path_lock_user_name} 在 %{created_at} é”定"
@@ -7525,8 +8858,11 @@ msgstr "具有åˆå¹¶åˆ°ç›®æ ‡åˆ†æ”¯æƒé™çš„æˆå‘˜å…许æ交"
msgid "mrWidget|An error occured while removing your approval."
msgstr "删除您的批准时å‘生错误。"
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr "读å–æ­¤åˆå¹¶è¯·æ±‚的批准数æ®æ—¶å‘生错误。"
+
msgid "mrWidget|An error occurred while submitting your approval."
-msgstr ""
+msgstr "æ交批准时å‘生错误。"
msgid "mrWidget|Approve"
msgstr "批准"
@@ -7573,6 +8909,9 @@ msgstr "通过电å­é‚®ä»¶å‘出补ä¸"
msgid "mrWidget|Failed to load deployment statistics"
msgstr "无法加载部署统计信æ¯"
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr "无法进行快进åˆå¹¶ã€‚è¦åˆå¹¶æ­¤è¯·æ±‚,请先在本地进行rebase。"
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr "如果 %{branch} 分支存在于本地仓库中,则å¯ä»¥æ‰‹åŠ¨åˆå¹¶è¯¥åˆå¹¶è¯·æ±‚。需使用"
@@ -7595,10 +8934,10 @@ msgid "mrWidget|Merge locally"
msgstr "本地åˆå¹¶"
msgid "mrWidget|Merge request approved"
-msgstr ""
+msgstr "åˆå¹¶è¯·æ±‚已批准"
msgid "mrWidget|Merge request approved; you can approve additionally"
-msgstr ""
+msgstr "åˆå¹¶è¯·æ±‚已被批准; ä»å¯ä»¥æ·»åŠ é¢å¤–批准"
msgid "mrWidget|Merged by"
msgstr "åˆå¹¶è€…:"
@@ -7607,14 +8946,20 @@ msgid "mrWidget|No Approval required"
msgstr "无需批准"
msgid "mrWidget|No Approval required; you can still approve"
-msgstr "批准已满足è¦æ±‚; 您ä»ç„¶å¯ä»¥ç»§ç»­æ·»åŠ é¢å¤–批准"
+msgstr "批准已满足åˆå¹¶è¦æ±‚; 您ä»ç„¶å¯ä»¥ç»§ç»­æ·»åŠ é¢å¤–批准"
msgid "mrWidget|Open in Web IDE"
msgstr "在Web IDE中打开"
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr "æµæ°´çº¿è¢«é˜»æ­¢ã€‚æ­¤åˆå¹¶è¯·æ±‚çš„æµæ°´çº¿éœ€è¦æ‰‹åŠ¨æ“作æ‰èƒ½ç»§ç»­"
+
msgid "mrWidget|Plain diff"
msgstr "文本差异"
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr "准备自动åˆå¹¶ã€‚询问具有此存储库的写访问æƒé™çš„人æ¥åˆå¹¶æ­¤è¯·æ±‚"
+
msgid "mrWidget|Refresh"
msgstr "刷新"
@@ -7636,9 +8981,20 @@ msgstr "删除您的批准"
msgid "mrWidget|Request to merge"
msgstr "请求åˆå¹¶"
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] "éœ€è¦ %d 次批准"
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] "需è¦é¢å¤– %d 个批准"
+
msgid "mrWidget|Resolve conflicts"
msgstr "解决冲çª"
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr "解决这些冲çªæˆ–询问对此存储库具有写入æƒé™çš„人员在本地åˆå¹¶å®ƒ"
+
msgid "mrWidget|Revert"
msgstr "还原"
@@ -7657,9 +9013,18 @@ msgstr "更改未åˆå¹¶åˆ°"
msgid "mrWidget|The changes will be merged into"
msgstr "更改将被åˆå¹¶åˆ°"
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr "æ­¤åˆå¹¶è¯·æ±‚çš„æµæ°´çº¿å¤±è´¥ã€‚请é‡è¯•è¯¥ä½œä¸šæˆ–推é€æ–°çš„æ交以修å¤å¤±è´¥"
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr "æºåˆ†æ”¯HEAD最近已更改。请在é‡æ–°åˆå¹¶ä¹‹å‰é‡æ–°åŠ è½½é¡µé¢å¹¶æŸ¥çœ‹æ›´æ”¹"
+
msgid "mrWidget|The source branch has been removed"
msgstr "æºåˆ†æ”¯å·²è¢«åˆ é™¤"
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr "æºåˆ†æ”¯æ˜¯ %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} 的目标分支"
+
msgid "mrWidget|The source branch is being removed"
msgstr "æºåˆ†æ”¯æ­£åœ¨è¢«åˆ é™¤"
@@ -7684,6 +9049,9 @@ msgstr "该åˆå¹¶è¯·æ±‚正在被åˆå¹¶"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "该项目已存档,ç¦æ­¢å†™å…¥"
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr "ä¸å…许直接编辑此项目。请派生(fork)åŽè¿›è¡Œæ›´æ”¹ã€‚"
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "å¯ä»¥æ‰‹åŠ¨åˆå¹¶æ­¤åˆå¹¶è¯·æ±‚,使用以下"
@@ -7702,6 +9070,9 @@ msgstr "å…¥"
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr "æµæ°´çº¿æˆåŠŸæ—¶è‡ªåŠ¨åˆå¹¶"
+msgid "n/a"
+msgstr "ä¸é€‚用"
+
msgid "new merge request"
msgstr "新建åˆå¹¶è¯·æ±‚"
@@ -7711,6 +9082,10 @@ msgstr "通知邮件"
msgid "or"
msgstr "或"
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] "总共 %d 次测试中"
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "上级"
@@ -7727,6 +9102,9 @@ msgstr "ç§é’¥ä¸Žè¯ä¹¦ä¸åŒ¹é…。"
msgid "remaining"
msgstr "剩余"
+msgid "remove"
+msgstr "移除"
+
msgid "remove due date"
msgstr "删除截止日期"
@@ -7740,7 +9118,7 @@ msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} 将会更新消耗的总时长。"
msgid "started"
-msgstr ""
+msgstr "已开始"
msgid "this document"
msgstr "此文档"
@@ -7748,6 +9126,9 @@ msgstr "此文档"
msgid "to help your contributors communicate effectively!"
msgstr "帮助您的贡献者进行有效沟通ï¼"
+msgid "toggle collapse"
+msgstr "切æ¢æŠ˜å "
+
msgid "username"
msgstr "用户å"
@@ -7755,7 +9136,7 @@ msgid "uses Kubernetes clusters to deploy your code!"
msgstr "使用 Kubernetes 集群æ¥éƒ¨ç½²ä»£ç ï¼"
msgid "view it on GitLab"
-msgstr ""
+msgstr "使用GitLab查看"
msgid "with %{additions} additions, %{deletions} deletions."
msgstr "å…± %{additions} æ¡æ–°å¢ž, %{deletions} æ¡åˆ é™¤."
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index b37b7aec35d..6987f307dbb 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-HK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:40\n"
+"PO-Revision-Date: 2018-10-02 09:30\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr ""
@@ -42,6 +45,14 @@ msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -58,10 +69,6 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] ""
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] ""
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
@@ -94,9 +101,15 @@ msgstr[0] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -138,13 +151,15 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] ""
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr ""
@@ -173,6 +188,10 @@ msgid "1 closed merge request"
msgid_plural "%d closed merge requests"
msgstr[0] ""
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -189,6 +208,14 @@ msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d æ¢æµæ°´ç·š"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+
msgid "1st contribution!"
msgstr ""
@@ -222,6 +249,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr ""
@@ -237,12 +267,18 @@ msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "相關æŒçºŒé›†æˆçš„圖åƒé›†åˆ"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -285,13 +321,16 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "å› æ¢å¾©å®‰è£ï¼Œè¨ªå•æ•…障存儲已被暫時ç¦ç”¨ã€‚在å•é¡Œè§£æ±ºå¾Œå°‡é‡ç½®å­˜å„²ä¿¡æ¯ï¼Œä»¥ä¾¿å†æ¬¡è¨ªå•ã€‚"
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
@@ -324,15 +363,15 @@ msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
-msgid "Add License"
-msgstr "添加許å¯è­‰"
-
msgid "Add Readme"
msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr ""
@@ -351,6 +390,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr ""
@@ -363,6 +405,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -381,6 +429,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -429,6 +480,9 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
@@ -456,6 +510,9 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -474,7 +531,7 @@ msgstr ""
msgid "An error occured whilst fetching the job trace."
msgstr ""
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr ""
msgid "An error occured whilst loading all the files."
@@ -525,13 +582,25 @@ msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -621,12 +690,18 @@ msgstr ""
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·šè¨ˆåŠƒå—Žï¼Ÿ"
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
@@ -645,13 +720,16 @@ msgstr ""
msgid "Are you sure?"
msgstr "確定嗎?"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr ""
msgid "Ascending"
msgstr ""
-msgid "Ask your group maintainer to setup a group Runner."
+msgid "Ask your group maintainer to set up a group Runner."
msgstr ""
msgid "Assertion consumer service URL"
@@ -684,7 +762,7 @@ msgstr ""
msgid "Assignee"
msgstr ""
-msgid "Assignee boards not available with your current license"
+msgid "Assignee lists not available with your current license"
msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
@@ -711,6 +789,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -771,6 +852,9 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr ""
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -780,6 +864,9 @@ msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr ""
@@ -804,9 +891,6 @@ msgstr ""
msgid "Background color"
msgstr ""
-msgid "Background jobs"
-msgstr ""
-
msgid "Badges"
msgstr ""
@@ -846,6 +930,9 @@ msgstr ""
msgid "Badges|No image to preview"
msgstr ""
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr ""
@@ -873,9 +960,15 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
@@ -951,6 +1044,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr ""
@@ -1120,6 +1216,9 @@ msgstr "ç€è¦½æ–‡ä»¶"
msgid "Browse files"
msgstr "ç€è¦½æ–‡ä»¶"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr ""
@@ -1132,6 +1231,9 @@ msgstr ""
msgid "CI / CD Settings"
msgstr ""
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -1144,9 +1246,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr ""
-
msgid "CICD|Auto DevOps"
msgstr ""
@@ -1159,39 +1258,33 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
-msgstr ""
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr ""
-
-msgid "CICD|Instance default (%{state})"
-msgstr ""
-
msgid "CICD|Jobs"
msgstr ""
msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr ""
@@ -1219,6 +1312,9 @@ msgstr ""
msgid "Change Weight"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -1276,6 +1372,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr ""
@@ -1438,6 +1540,9 @@ msgstr ""
msgid "Close"
msgstr ""
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -1447,6 +1552,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1456,12 +1564,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1474,10 +1591,10 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -1504,6 +1621,12 @@ msgstr ""
msgid "ClusterIntegration|Did you know?"
msgstr ""
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1528,6 +1651,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1540,6 +1666,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1555,6 +1684,9 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
@@ -1579,6 +1711,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1591,15 +1726,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -1624,12 +1750,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
-
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -1672,6 +1792,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1681,6 +1804,12 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1693,6 +1822,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1708,9 +1840,6 @@ msgstr ""
msgid "ClusterIntegration|Search zones"
msgstr ""
-msgid "ClusterIntegration|Security"
-msgstr ""
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -1747,12 +1876,18 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
@@ -1765,9 +1900,15 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -1786,9 +1927,6 @@ msgstr ""
msgid "ClusterIntegration|help page"
msgstr ""
-msgid "ClusterIntegration|installing applications"
-msgstr ""
-
msgid "ClusterIntegration|meets the requirements"
msgstr ""
@@ -1798,6 +1936,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
@@ -1845,6 +1986,9 @@ msgstr "æ交"
msgid "CommitMessage|Add %{file_name}"
msgstr "添加 %{file_name}"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "æ交"
@@ -1917,16 +2061,13 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
-msgid "Configure Sidekiq job throttling."
-msgstr ""
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
-msgid "Configure push and pull mirrors."
+msgid "Configure push mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -2016,6 +2157,9 @@ msgstr ""
msgid "Contribution guide"
msgstr "è²¢ç»æŒ‡å—"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr ""
@@ -2049,6 +2193,15 @@ msgstr ""
msgid "ConvDev Index"
msgstr ""
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -2154,9 +2307,6 @@ msgstr "創建..."
msgid "Create project label"
msgstr ""
-msgid "CreateNewFork|Fork"
-msgstr "派生"
-
msgid "CreateTag|Tag"
msgstr "標籤"
@@ -2172,6 +2322,9 @@ msgstr ""
msgid "Created by me"
msgstr ""
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr ""
@@ -2184,6 +2337,9 @@ msgstr "Cron 時å€"
msgid "Cron syntax"
msgstr "Cron 語法"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr ""
@@ -2193,6 +2349,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr ""
@@ -2202,6 +2361,9 @@ msgstr "自定義通知事件"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "自定義通知級別繼承自åƒèˆ‡ç´šåˆ¥ã€‚使用自定義通知級別,您會收到åƒèˆ‡ç´šåˆ¥åŠé¸å®šäº‹ä»¶çš„通知。想了解更多信æ¯ï¼Œè«‹æŸ¥çœ‹ %{notification_link}."
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr ""
@@ -2214,6 +2376,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "週期分æž"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "編碼"
@@ -2244,6 +2409,12 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2253,6 +2424,9 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr ""
@@ -2268,6 +2442,9 @@ msgstr "使用 Cron 語法定義自定義模å¼"
msgid "Delete"
msgstr "刪除"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr ""
@@ -2428,12 +2605,18 @@ msgstr ""
msgid "Details"
msgstr "詳情"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "目錄å稱"
@@ -2446,9 +2629,21 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
@@ -2548,7 +2743,7 @@ msgstr ""
msgid "Elasticsearch"
msgstr ""
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
@@ -2608,12 +2803,33 @@ msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2644,6 +2860,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2659,6 +2878,9 @@ msgstr ""
msgid "Environments|No pod name has been specified"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2692,6 +2914,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -2707,6 +2932,30 @@ msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr ""
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2731,6 +2980,9 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
@@ -2743,6 +2995,12 @@ msgstr ""
msgid "Error loading project data. Please try again."
msgstr ""
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -2755,6 +3013,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2797,6 +3058,9 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr ""
@@ -2854,6 +3118,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "無法刪除æµæ°´ç·šè¨ˆåŠƒ"
@@ -2875,6 +3142,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "文件"
@@ -2887,9 +3157,18 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "按æ交消æ¯éŽæ¿¾"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "按路徑查找"
@@ -2902,6 +3181,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2911,6 +3193,18 @@ msgstr "首次推é€"
msgid "FirstPushedBy|pushed by"
msgstr "推é€è€…:"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -2938,16 +3232,18 @@ msgstr ""
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "派生"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "派生自"
@@ -2969,6 +3265,9 @@ msgstr ""
msgid "From Bitbucket"
msgstr ""
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr ""
@@ -2984,6 +3283,9 @@ msgstr "從創建議題到部署到生產環境"
msgid "From merge request merge until deploy to production"
msgstr "從åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²è‡³ç”Ÿç”¢ç’°å¢ƒ"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
@@ -2999,6 +3301,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr ""
@@ -3164,33 +3469,132 @@ msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr ""
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr ""
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr ""
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -3239,6 +3643,9 @@ msgstr ""
msgid "GitLab.com import"
msgstr ""
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -3260,14 +3667,11 @@ msgstr ""
msgid "Go back"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "跳轉到派生項目"
-
-msgid "GoToYourFork|Fork"
-msgstr "跳轉到派生項目"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr ""
@@ -3329,13 +3733,13 @@ msgstr ""
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
@@ -3350,6 +3754,15 @@ msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -3413,6 +3826,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr ""
@@ -3425,19 +3841,19 @@ msgstr ""
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
-msgid "GroupsTree|Filter by name..."
-msgstr ""
-
msgid "GroupsTree|Leave this group"
msgstr ""
msgid "GroupsTree|Loading groups"
msgstr ""
-msgid "GroupsTree|Sorry, no groups matched your search"
+msgid "GroupsTree|No groups matched your search"
msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
msgstr ""
msgid "Have your users email"
@@ -3473,6 +3889,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -3495,21 +3920,45 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3519,6 +3968,9 @@ msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
@@ -3576,6 +4028,9 @@ msgstr ""
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr ""
@@ -3585,6 +4040,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -3603,18 +4061,33 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3625,6 +4098,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] ""
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -3643,12 +4122,21 @@ msgstr ""
msgid "Internal - The project can be accessed by any logged in user."
msgstr ""
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "循環週期"
msgid "Introducing Cycle Analytics"
msgstr "週期分æžç°¡ä»‹"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr ""
@@ -3688,22 +4176,58 @@ msgstr ""
msgid "Jobs"
msgstr ""
-msgid "Jul"
+msgid "Job|Browse"
msgstr ""
-msgid "July"
+msgid "Job|Complete Raw"
msgstr ""
-msgid "Jun"
+msgid "Job|Download"
msgstr ""
-msgid "June"
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
msgstr ""
-msgid "Koding"
+msgid "Job|The artifacts were removed"
msgstr ""
-msgid "Koding Dashboard"
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
msgstr ""
msgid "Kubernetes"
@@ -3775,6 +4299,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "最近 %d 天"
@@ -3785,6 +4312,9 @@ msgstr "最新æµæ°´ç·š"
msgid "Last commit"
msgstr "最後æ交"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr ""
@@ -3809,6 +4339,9 @@ msgstr ""
msgid "Learn more"
msgstr ""
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr ""
@@ -3836,6 +4369,61 @@ msgstr ""
msgid "License"
msgstr ""
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3848,9 +4436,15 @@ msgstr ""
msgid "List available repositories"
msgstr ""
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr ""
@@ -3869,6 +4463,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3896,6 +4493,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr ""
@@ -3950,9 +4550,21 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -4001,15 +4613,15 @@ msgstr ""
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "MergeRequests|Updating discussions failed"
-msgstr ""
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -4025,6 +4637,9 @@ msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr ""
@@ -4127,9 +4742,24 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -4148,9 +4778,30 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "添加壹個 SSH 公鑰"
@@ -4169,9 +4820,6 @@ msgstr ""
msgid "More"
msgstr ""
-msgid "More actions"
-msgstr ""
-
msgid "More info"
msgstr ""
@@ -4220,6 +4868,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -4308,12 +4959,21 @@ msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -4335,6 +4995,9 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr ""
@@ -4347,6 +5010,9 @@ msgstr ""
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4359,15 +5025,27 @@ msgstr ""
msgid "No repository"
msgstr "沒有存儲庫"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "沒有計劃"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -4386,6 +5064,9 @@ msgstr ""
msgid "Not enough data"
msgstr "數據ä¸è¶³"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -4419,6 +5100,9 @@ msgstr "æµæ°´ç·šå¤±æ•—"
msgid "NotificationEvent|Merge merge request"
msgstr "åˆä½µè«‹æ±‚被åˆä½µ"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "新增議題"
@@ -4491,18 +5175,25 @@ msgstr "篩é¸"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+msgid "Only admins"
msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4566,9 +5257,21 @@ msgstr ""
msgid "Overview"
msgstr ""
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "所有者"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -4599,9 +5302,15 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4620,6 +5329,9 @@ msgstr ""
msgid "Pipeline"
msgstr "æµæ°´ç·š"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "æµæ°´ç·šå¥åº·æŒ‡æ¨™"
@@ -4707,6 +5419,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4728,6 +5443,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4776,12 +5494,6 @@ msgstr "於階段"
msgid "Plain diff"
msgstr ""
-msgid "Planned finish date"
-msgstr ""
-
-msgid "Planned start date"
-msgstr ""
-
msgid "PlantUML"
msgstr ""
@@ -4821,6 +5533,15 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr ""
@@ -4851,18 +5572,48 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -4875,33 +5626,108 @@ msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -4911,6 +5737,18 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -4944,6 +5782,9 @@ msgstr "é …ç›® '%{project_name}' 已更新完æˆã€‚"
msgid "Project Badges"
msgstr ""
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "項目訪å•æ¬Šé™å¿…須明確授權給æ¯å€‹ç”¨æˆ¶ã€‚"
@@ -4971,6 +5812,9 @@ msgstr "項目導出已開始。下載éˆæŽ¥å°‡é€šéŽé›»å­éƒµä»¶ç™¼é€ã€‚"
msgid "Project name"
msgstr ""
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "訂閱"
@@ -4998,18 +5842,48 @@ msgstr "從未"
msgid "ProjectLifecycle|Stage"
msgstr "階段"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -5049,6 +5923,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr ""
@@ -5181,6 +6058,54 @@ msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -5199,6 +6124,12 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -5238,12 +6169,25 @@ msgstr "自述文件"
msgid "Real-time features"
msgstr ""
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr ""
msgid "Refresh"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
@@ -5295,6 +6239,18 @@ msgstr ""
msgid "Remove project"
msgstr "刪除項目"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr ""
@@ -5304,6 +6260,54 @@ msgstr ""
msgid "Repo by URL"
msgstr ""
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr "存儲庫"
@@ -5355,9 +6359,21 @@ msgstr ""
msgid "Resolve discussion"
msgstr ""
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr ""
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -5370,6 +6386,9 @@ msgstr ""
msgid "Retry verification"
msgstr ""
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -5401,9 +6420,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr ""
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr ""
@@ -5413,6 +6450,21 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -5428,12 +6480,21 @@ msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr ""
@@ -5500,12 +6561,42 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
@@ -5515,10 +6606,13 @@ msgstr ""
msgid "Secret:"
msgstr ""
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr ""
-msgid "Security report"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
@@ -5533,6 +6627,9 @@ msgstr ""
msgid "Select Archive Format"
msgstr "é¸æ“‡ä¸‹è¼‰æ ¼å¼"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr ""
@@ -5566,6 +6663,12 @@ msgstr ""
msgid "Select target branch"
msgstr "é¸æ“‡ç›®æ¨™åˆ†æ”¯"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5575,6 +6678,9 @@ msgstr ""
msgid "Send email"
msgstr ""
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr ""
@@ -5602,6 +6708,9 @@ msgstr "為賬號添加壹個用於推é€æˆ–拉å–çš„ %{protocol} 密碼。"
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr ""
@@ -5614,21 +6723,24 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
-msgid "Set up Koding"
-msgstr "設置 Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "設置密碼"
msgid "Settings"
msgstr ""
-msgid "Setup a specific Runner automatically"
-msgstr ""
-
msgid "Share"
msgstr ""
@@ -5638,6 +6750,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -5711,12 +6826,18 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
@@ -5732,13 +6853,19 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching assignees list"
+msgid "Something went wrong while fetching %{listType} list"
msgstr ""
msgid "Something went wrong while fetching group member contributions"
@@ -5792,6 +6919,9 @@ msgstr ""
msgid "SortOptions|Largest repository"
msgstr ""
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr ""
@@ -5822,6 +6952,9 @@ msgstr ""
msgid "SortOptions|Most popular"
msgstr ""
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr ""
@@ -5852,6 +6985,9 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr ""
@@ -5882,6 +7018,9 @@ msgstr ""
msgid "Specific Runners"
msgstr ""
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "在 Runner 設置時指定以下 URL:"
@@ -5924,6 +7063,9 @@ msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr "由此更改 %{new_merge_request}"
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr "é‹ä½œ Runner!"
@@ -5957,6 +7099,9 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr ""
@@ -5990,6 +7135,9 @@ msgstr ""
msgid "System metrics (Custom)"
msgstr ""
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -5997,6 +7145,9 @@ msgstr[0] ""
msgid "Tags"
msgstr "標籤"
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr ""
@@ -6078,6 +7229,12 @@ msgstr ""
msgid "Team"
msgstr "團隊"
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
@@ -6093,6 +7250,9 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -6102,6 +7262,9 @@ msgstr ""
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "編碼階段概述了從第壹次æ交到創建åˆä½µè«‹æ±‚的時間。創建第壹個åˆä½µè«‹æ±‚後,數據將自動添加到此處。"
@@ -6111,6 +7274,9 @@ msgstr "與該階段相關的事件。"
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "派生關係已被刪除。"
@@ -6138,6 +7304,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "項目生命週期中的å„個階段。"
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "計劃階段概述了從議題添加到日程到推é€é¦–次æ交的時間。當首次推é€æ交後,數據將自動添加到此處。"
@@ -6165,6 +7334,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "評審階段概述了從創建åˆä½µè«‹æ±‚到åˆä½µçš„時間。當創建第壹個åˆä½µè«‹æ±‚後,數據將自動添加到此處。"
@@ -6177,6 +7349,9 @@ msgstr ""
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "é ç™¼å¸ƒéšŽæ®µæ¦‚述了åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²ä»£ç¢¼åˆ°ç”Ÿç”¢ç’°å¢ƒçš„總時間。當首次部署到生產環境後,數據將自動添加到此處。"
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "測試階段概述了 GitLab CI 為相關åˆä½µè«‹æ±‚é‹è¡Œæ¯å€‹æµæ°´ç·šæ‰€éœ€çš„時間。當第壹個æµæ°´ç·šé‹è¡Œå®Œæˆå¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ­¤è™•ã€‚"
@@ -6192,6 +7367,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "該階段æ¯æ¢æ•¸æ“šæ‰€èŠ±çš„時間"
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6201,6 +7382,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "中ä½æ•¸æ˜¯å£¹å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間,中ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,中ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr ""
@@ -6210,9 +7394,24 @@ msgstr ""
msgid "There are no merge requests to show"
msgstr ""
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "è¨ªå• Git 存儲時出ç¾å•é¡Œï¼š"
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -6249,6 +7448,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -6300,9 +7511,30 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -6312,6 +7544,9 @@ msgstr "在創建壹個空的存儲庫或導入ç¾æœ‰å­˜å„²åº«ä¹‹å‰ï¼Œæ‚¨å°‡ç„¡
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -6327,15 +7562,30 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -6536,12 +7786,24 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -6569,7 +7831,7 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
msgstr ""
msgid "To widen your search, change or remove filters."
@@ -6584,6 +7846,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr ""
@@ -6599,6 +7864,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -6626,6 +7894,15 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -6641,24 +7918,39 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr ""
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr ""
@@ -6695,9 +7987,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -6722,15 +8020,15 @@ msgstr "上傳新文件"
msgid "Upload file"
msgstr "上傳文件"
-msgid "Upload new avatar"
-msgstr ""
-
msgid "UploadLink|click to upload"
msgstr "點擊上傳"
msgid "Upvotes"
msgstr ""
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr ""
@@ -6746,6 +8044,9 @@ msgstr ""
msgid "Use one line per URI"
msgstr ""
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "在安è£éŽç¨‹ä¸­ä½¿ç”¨ä»¥ä¸‹è¨»å†Šä»¤ç‰Œï¼š"
@@ -6755,6 +8056,9 @@ msgstr "使用全局通知設置"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
+msgstr ""
+
msgid "User Settings"
msgstr ""
@@ -6788,6 +8092,9 @@ msgstr ""
msgid "Verified"
msgstr ""
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -7079,16 +8386,19 @@ msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
msgstr ""
-msgid "You cannot write to this read-only GitLab instance."
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You cannot write to this read-only GitLab instance."
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
@@ -7103,9 +8413,6 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
-msgid "You have not created any merge requests"
-msgstr ""
-
msgid "You have reached your project limit"
msgstr "您已é”到項目數é‡é™åˆ¶"
@@ -7115,9 +8422,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
-msgid "You must sign in to star a project"
-msgstr "必須登錄æ‰èƒ½å°é …目加星標"
-
msgid "You need a different license to enable FileLocks feature"
msgstr ""
@@ -7127,6 +8431,12 @@ msgstr ""
msgid "You need permission."
msgstr "需è¦ç›¸é—œçš„權é™ã€‚"
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "ä¸æœƒæ”¶åˆ°ä»»ä½•é€šçŸ¥éƒµä»¶"
@@ -7205,16 +8515,15 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr ""
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr ""
msgid "among other things"
msgstr ""
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-
msgid "assign yourself"
msgstr ""
@@ -7239,70 +8548,86 @@ msgstr ""
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr ""
-msgid "ciReport|%{packagesString} and "
+msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
-msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
-msgid "ciReport|%{remainingPackagesCount} more"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} is loading"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
+msgid "ciReport|%{reportType} is loading"
msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
msgstr ""
-msgid "ciReport|Class"
+msgid "ciReport|(errors when loading results)"
msgstr ""
-msgid "ciReport|Code quality"
+msgid "ciReport|(is loading)"
msgstr ""
-msgid "ciReport|Confidence"
+msgid "ciReport|(is loading, errors when loading results)"
msgstr ""
-msgid "ciReport|Container scanning detected"
+msgid "ciReport|Class"
msgstr ""
-msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|Container scanning is loading"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Container scanning resulted in error while loading results"
+msgid "ciReport|Container scanning"
msgstr ""
-msgid "ciReport|DAST detected"
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|DAST is loading"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|DAST resulted in error while loading results"
+msgid "ciReport|DAST"
msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgid "ciReport|DAST detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr ""
-msgid "ciReport|Dependency scanning is loading"
+msgid "ciReport|Dependency scanning"
msgstr ""
-msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgid "ciReport|Dependency scanning detected"
msgstr ""
msgid "ciReport|Description"
@@ -7335,10 +8660,15 @@ msgstr ""
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
-msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
+
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
-msgid "ciReport|License management detected %{licenseInfo}"
+msgid "ciReport|License management detected no licenses for the source branch only"
msgstr ""
msgid "ciReport|License management detected no new licenses"
@@ -7368,13 +8698,10 @@ msgstr ""
msgid "ciReport|Revert dismissal"
msgstr ""
-msgid "ciReport|SAST detected"
-msgstr ""
-
-msgid "ciReport|SAST is loading"
+msgid "ciReport|SAST"
msgstr ""
-msgid "ciReport|SAST resulted in error while loading results"
+msgid "ciReport|SAST detected"
msgstr ""
msgid "ciReport|Security scanning"
@@ -7383,9 +8710,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Security scanning is loading"
-msgstr ""
-
msgid "ciReport|Severity"
msgstr ""
@@ -7416,12 +8740,13 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr ""
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7434,6 +8759,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -7450,17 +8781,6 @@ msgstr[0] "天"
msgid "deploy token"
msgstr ""
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-
-msgid "detected no vulnerabilities"
-msgstr ""
-
msgid "disabled"
msgstr ""
@@ -7479,12 +8799,19 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr ""
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -7494,9 +8821,15 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr ""
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -7525,6 +8858,9 @@ msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7573,6 +8909,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -7612,9 +8951,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -7636,9 +8981,20 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -7657,9 +9013,18 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr ""
@@ -7684,6 +9049,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -7702,6 +9070,9 @@ msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "新建åˆä½µè«‹æ±‚"
@@ -7711,6 +9082,10 @@ msgstr "通知郵件"
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "父級"
@@ -7727,6 +9102,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -7748,6 +9126,9 @@ msgstr ""
msgid "to help your contributors communicate effectively!"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index 0ca496b0395..4e0b07e717f 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -13,7 +13,10 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-TW\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2018-08-01 11:41\n"
+"PO-Revision-Date: 2018-10-02 09:30\n"
+
+msgid " Status"
+msgstr ""
msgid " and"
msgstr " 和"
@@ -42,6 +45,14 @@ msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d 導出"
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d 個議題"
@@ -58,10 +69,6 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] "%d 指標"
-msgid "%d new license"
-msgid_plural "%d new licenses"
-msgstr[0] "%d 個新授權"
-
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] "%d 個暫存變更"
@@ -94,9 +101,15 @@ msgstr[0] "%{count} åƒèˆ‡è€…"
msgid "%{filePath} deleted"
msgstr "已刪除 %{filePath}"
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr "%{group_docs_link_start}群組%{group_docs_link_end} å…許您管ç†ã€å”作多個專案。群組的æˆå“¡å¯ä»¥è¨ªå•ç¾¤çµ„下的所有專案。"
+msgid "%{issuableType} will be removed! Are you sure?"
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 開始"
@@ -138,13 +151,15 @@ msgstr "%{text} å¯ç”¨"
msgid "%{title} changes"
msgstr "%{title} 變更"
-msgid "%{type} detected 1 vulnerability"
-msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
-msgstr[0] "%{type} åµæ¸¬åˆ° %{vulnerabilityCount} 個æ¼æ´ž"
-
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "%{unstaged} 個未暫存和 %{staged} 個已暫存變更"
+msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
+msgstr ""
+
+msgid "+ %{count} more"
+msgstr ""
+
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} 更多"
@@ -173,6 +188,10 @@ msgid "1 closed merge request"
msgid_plural "%d closed merge requests"
msgstr[0] "%d 個關閉的åˆä½µè«‹æ±‚"
+msgid "1 group"
+msgid_plural "%d groups"
+msgstr[0] ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] "%d 個已åˆä½µçš„åˆä½µè«‹æ±‚"
@@ -189,6 +208,14 @@ msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d æ¢æµæ°´ç·š"
+msgid "1 role"
+msgid_plural "%d roles"
+msgstr[0] ""
+
+msgid "1 user"
+msgid_plural "%d users"
+msgstr[0] ""
+
msgid "1st contribution!"
msgstr "第一次å”作"
@@ -222,6 +249,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
msgstr "建立 <strong>%{created_count}</strong> 個,åŒæ„ <strong>%{accepted_count}</strong> 個。"
@@ -237,12 +267,18 @@ msgstr "æŽ¨é€ <strong>%{pushes}</strong> 個,<strong>%{people}</strong> 個è²
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>刪除</strong>來æºåˆ†æ”¯"
-msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
-msgstr "一個「執行器ã€æ˜¯ä¸€å€‹åŸ·è¡Œå·¥ä½œçš„進程。你å¯ä»¥å®‰è£ä½ éœ€è¦çš„多個執行器。"
+msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
+msgstr ""
msgid "A collection of graphs regarding Continuous Integration"
msgstr "æŒçºŒæ•´åˆç›¸é—œçš„圖表"
+msgid "A default branch cannot be chosen for an empty project."
+msgstr ""
+
+msgid "A deleted user"
+msgstr ""
+
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "將會å†å‰µå»ºä¸€å€‹æ–°çš„分支,並建立一個新的åˆä½µè«‹æ±‚。"
@@ -285,13 +321,16 @@ msgstr "å­˜å–憑證"
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr "å­˜å–被拒ï¼è«‹é©—證您是å¦å¯ä»¥åœ¨æ­¤ç‰ˆæœ¬åº«éƒ¨å±¬é‡‘鑰。"
+msgid "Access expiration date"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr "ä¸å…許存å–「%{classification_label}ã€"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "已暫時åœç”¨å¤±æ•—çš„ Git 儲存空間。當儲存空間æ¢å¾©æ­£å¸¸å¾Œï¼Œè«‹é‡ç½®å„²å­˜ç©ºé–“å¥åº·æŒ‡æ•¸ã€‚"
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "å­˜å–您執行器憑證,自定義您的æµæ°´ç·šé…置,並查看你的æµæ°´ç¾ç‹€æ…‹åŠæ¸¬è©¦æ¶µè“‹çŽ‡å ±å‘Šã€‚"
msgid "Account"
@@ -324,15 +363,15 @@ msgstr "新增群組 Webhook å’Œ GitLab ä¼æ¥­ç‰ˆæœ¬ã€‚"
msgid "Add Kubernetes cluster"
msgstr "增加 Kubernetes å¢é›†"
-msgid "Add License"
-msgstr "新增授權æ¢æ¬¾"
-
msgid "Add Readme"
msgstr "增加說明檔案"
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "增加顯示於所有電å­éƒµä»¶å°è©±çš„附加文字。最多 %{character_limit} 個字。"
+msgid "Add license"
+msgstr ""
+
msgid "Add new application"
msgstr "建立新應用程å¼"
@@ -351,6 +390,9 @@ msgstr "加入使用者至群組:"
msgid "Add users to group"
msgstr "加入使用者到群組"
+msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
+msgstr ""
+
msgid "Additional text"
msgstr "附加文字"
@@ -363,6 +405,12 @@ msgstr "管ç†å“¡æ¦‚覽"
msgid "Admin area"
msgstr "管ç†å€å¡Š"
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "åœæ­¢æ‰€æœ‰ä»»å‹™"
@@ -381,6 +429,9 @@ msgstr "您將åœæ­¢æ‰€æœ‰ä»»å‹™ï¼Œé€™å°‡æœƒæš«åœæ‰€æœ‰æ­£åœ¨é‹è¡Œçš„任務。
msgid "AdminHealthPageLink|health page"
msgstr "系統狀態"
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr "刪除"
@@ -429,6 +480,9 @@ msgstr "所有改變都已經æ交"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "從模æ¿å»ºç«‹æˆ–導入專案時將啟用所有功能,您å¯ä»¥åœ¨å°ˆæ¡ˆè¨­ç½®ä¸­å°‡å…¶é—œé–‰ã€‚"
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr "å…許å¯ä»¥åˆä½µè‡³ç›®æ¨™åˆ†æ”¯çš„æˆå“¡æ交"
@@ -456,6 +510,9 @@ msgstr "或者,你å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。當你建立你
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "或者,你å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。當你建立你的個人存å–權æ–時,你將需è¦é¸æ“‡<code>檔案庫</code>範åœï¼Œæ‰€ä»¥æˆ‘們將會顯示你公開åŠç§äººçš„檔案庫清單進行匯入。"
+msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
+msgstr ""
+
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr "一個自稱 %{link_to_client} 的應用程å¼è«‹æ±‚您 GitLab 帳號的存å–權。"
@@ -474,7 +531,7 @@ msgstr "創建新分支時發生錯誤。"
msgid "An error occured whilst fetching the job trace."
msgstr "å–得工作追蹤資訊時發生錯誤"
-msgid "An error occured whilst fetching the latest pipline."
+msgid "An error occured whilst fetching the latest pipeline."
msgstr "å–得最新æµæ°´ç·šæ™‚發生錯誤"
msgid "An error occured whilst loading all the files."
@@ -525,14 +582,26 @@ msgstr "è®€å– markdown é è¦½æ™‚發生錯誤"
msgid "An error occurred while fetching sidebar data"
msgstr "讀å–å´é‚Šæ¬„資料時發生錯誤"
+msgid "An error occurred while fetching stages."
+msgstr ""
+
+msgid "An error occurred while fetching the job log."
+msgstr ""
+
+msgid "An error occurred while fetching the job."
+msgstr ""
+
+msgid "An error occurred while fetching the jobs."
+msgstr ""
+
msgid "An error occurred while fetching the pipeline."
msgstr "讀å–æµæ°´ç·šæ™‚發生錯誤"
msgid "An error occurred while getting projects"
msgstr "讀å–專案時發生錯誤"
-msgid "An error occurred while importing project: ${details}"
-msgstr "匯入專案時發生錯誤: ${details}"
+msgid "An error occurred while importing project: %{details}"
+msgstr ""
msgid "An error occurred while initializing path locks"
msgstr "åˆå§‹åŒ–路徑鎖時發生錯誤"
@@ -621,12 +690,18 @@ msgstr "四月"
msgid "Archived project! Repository and other project resources are read-only"
msgstr "這是個已經å°å­˜çš„專案ï¼å…¶æª”案庫與其其他專案資æºç‚ºå”¯è®€ç‹€æ…‹ã€‚"
+msgid "Archived projects"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·šæŽ’程嗎?"
msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
+msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
+msgstr ""
+
msgid "Are you sure you want to remove %{group_name}?"
msgstr "確定移除 %{group_name}?"
@@ -645,14 +720,17 @@ msgstr "你確定è¦è§£éŽ– %{path_lock_path} ?"
msgid "Are you sure?"
msgstr "確定嗎?"
+msgid "Artifact ID"
+msgstr ""
+
msgid "Artifacts"
msgstr "產物"
msgid "Ascending"
msgstr "é †åº"
-msgid "Ask your group maintainer to setup a group Runner."
-msgstr "è©¢å•æ‚¨çš„群組維護者以安è£ç¾¤çµ„執行器。"
+msgid "Ask your group maintainer to set up a group Runner."
+msgstr ""
msgid "Assertion consumer service URL"
msgstr "Assertion 客戶æœå‹™é€£çµ"
@@ -684,8 +762,8 @@ msgstr "指派給我"
msgid "Assignee"
msgstr "指派人"
-msgid "Assignee boards not available with your current license"
-msgstr "您目å‰çš„授權並ä¸æ”¯æ´æŒ‡æ´¾çœ‹æ¿"
+msgid "Assignee lists not available with your current license"
+msgstr ""
msgid "Assignee lists show all issues assigned to the selected user."
msgstr "指派列表顯示了分é…給é¸å®šä½¿ç”¨è€…的所有議題"
@@ -711,6 +789,9 @@ msgstr "登入紀錄"
msgid "Authentication log"
msgstr "èªè­‰è¨˜éŒ„"
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr "作者"
@@ -771,6 +852,9 @@ msgstr "將根據設定的的 CI / CD æµç¨‹è‡ªå‹•å»ºæ§‹ã€æ¸¬è©¦å’Œéƒ¨ç½²æ‡‰ç”¨
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "了解更多於 %{link_to_documentation}"
+msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
+msgstr ""
+
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr "如果 %{link_to_auto_devops_settings} 這個專案,您å¯ä»¥è‡ªå‹•å»ºç½®å’Œæ¸¬è©¦ä½ çš„æ‡‰ç”¨ç¨‹å¼ %{link_to_add_kubernetes_cluster} 則å¯ä»¥è®“你自動部署"
@@ -780,6 +864,9 @@ msgstr "添加 Kubernetes å¢é›†"
msgid "AutoDevOps|enable Auto DevOps"
msgstr "啟用自動 DevOps"
+msgid "Automatically marked as default internal user"
+msgstr ""
+
msgid "Available"
msgstr "能é‹åšçš„"
@@ -804,9 +891,6 @@ msgstr "背景作業"
msgid "Background color"
msgstr "背景é¡è‰²"
-msgid "Background jobs"
-msgstr "背景工作"
-
msgid "Badges"
msgstr "徽章"
@@ -846,6 +930,9 @@ msgstr "沒有徽章圖片"
msgid "Badges|No image to preview"
msgstr "沒有圖片å¯ä»¥é è¦½"
+msgid "Badges|Please fill in a valid URL"
+msgstr ""
+
msgid "Badges|Project Badge"
msgstr "專案徽章"
@@ -873,9 +960,15 @@ msgstr "此群組無徽章。"
msgid "Badges|This project has no badges"
msgstr "此專案無徽章"
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr "您的徽章"
+msgid "Badges|e.g. %{exampleUrl}"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr "從é¸å®šçš„變更紀錄開始"
@@ -931,10 +1024,10 @@ msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr "ä½ ç›®å‰æ­£åœ¨ä½¿ç”¨æ–¹æ¡ˆ %{plan_link}"
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
-msgstr ""
+msgstr "您的 GitLab.com 試用版已經於 %{expiration_date} éŽæœŸã€‚%{learn_more_text}"
msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
-msgstr ""
+msgstr "您的 Gold 試用版將會 <strong>在 %{expiration_date} 之後éŽæœŸ</strong>。您å¯ä»¥é€éŽé–±è®€æˆ‘們的 %{features_link} 了解關於 GitLab.com Gold 的更多資訊。"
msgid "BillingPlans|features"
msgstr "功能"
@@ -951,6 +1044,9 @@ msgstr "æ¯å¹´æ”¶å– %{price_per_year}"
msgid "BillingPlans|per user"
msgstr "æ¯ä½ä½¿ç”¨è€…"
+msgid "Bitbucket Server Import"
+msgstr ""
+
msgid "Bitbucket import"
msgstr "匯入 Bitbucket"
@@ -1120,6 +1216,9 @@ msgstr "ç€è¦½æª”案"
msgid "Browse files"
msgstr "ç€è¦½æª”案"
+msgid "Built-In"
+msgstr ""
+
msgid "Business metrics (Custom)"
msgstr "ä¼æ¥­æŒ‡æ¨™ï¼ˆè‡ªè¨‚)"
@@ -1132,6 +1231,9 @@ msgstr "CI / CD"
msgid "CI / CD Settings"
msgstr "CI / CD 設定"
+msgid "CI will run using the credentials assigned above."
+msgstr ""
+
msgid "CI/CD"
msgstr "CI / CD"
@@ -1144,9 +1246,6 @@ msgstr "外部儲存庫的 CI / CD"
msgid "CI/CD settings"
msgstr "CI / CD 設定"
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
-msgstr "明確的 %{ci_file} 需è¦åœ¨ä½ å¯ä»¥é–‹å§‹ä½¿ç”¨ã€ŒæŒçºŒæ•´åˆã€èˆ‡ã€Œå‚³éžã€ä¹‹å‰æŒ‡å®šã€‚"
-
msgid "CICD|Auto DevOps"
msgstr "自動 DevOps"
@@ -1159,39 +1258,33 @@ msgstr "至動部屬到模擬環境,手動部屬到正å¼ç’°å¢ƒ"
msgid "CICD|Continuous deployment to production"
msgstr "æŒçºŒéƒ¨å±¬åˆ°æ­£å¼ç’°å¢ƒ"
+msgid "CICD|Default to Auto DevOps pipeline"
+msgstr ""
+
msgid "CICD|Deployment strategy"
msgstr "部屬策略"
msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr "部屬策略需è¦ç¶²åŸŸæ‰èƒ½æ­£å¸¸å·¥ä½œ"
-msgid "CICD|Disable Auto DevOps"
-msgstr "åœç”¨è‡ªå‹• DevOps"
-
msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
msgstr "如果你設定了多個使用「自動 DevOpsã€çš„ Kubernetes å¢é›†ï¼Œè«‹ä¸è¦åœ¨é€™è£¡è¨­å®šç¶²åŸŸã€‚"
-msgid "CICD|Enable Auto DevOps"
-msgstr "啟用自動 DevOps"
-
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
-msgstr "跟隨著實例é è¨­å€¼ï¼Œä»¥åœ¨æ²’有專案指定 %{ci_file} 的情æ³ä¸‹å•Ÿç”¨æˆ–åœç”¨è‡ªå‹• DevOps 環境。"
-
-msgid "CICD|Instance default (%{state})"
-msgstr "é è¨­(%{state})"
-
msgid "CICD|Jobs"
msgstr "作業"
msgid "CICD|Learn more about Auto DevOps"
msgstr "學習更多關於 Auto DevOps 的相關訊æ¯"
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
-msgstr "當專案沒有 %{ci_file} 檔案,將啟用 Auto DevOps çš„æµæ°´ç·šè¨­ç½®ã€‚"
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr "如果你想è¦ä½¿ç”¨è‡ªå‹•è¤‡é–±ç¨‹å¼åŠè‡ªå‹•éƒ¨ç½²ï¼Œè«‹æŒ‡å®šç¶²åŸŸã€‚"
+msgid "CICD|instance enabled"
+msgstr ""
+
msgid "Callback URL"
msgstr "回呼 URL"
@@ -1219,6 +1312,9 @@ msgstr "憑證指紋"
msgid "Change Weight"
msgstr "變更權é‡"
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr "更改此數值將影響到 GitLab UI 拉å–更新的頻率。"
@@ -1265,10 +1361,10 @@ msgid "Cherry-pick this merge request"
msgstr "挑é¸æ­¤åˆä½µè«‹æ±‚"
msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
-msgstr ""
+msgstr "é¸æ“‡ <strong>建立å°å­˜æª”</strong> 並等待å°å­˜å®Œæˆã€‚"
msgid "Choose <strong>Next</strong> at the bottom of the page."
-msgstr ""
+msgstr "é¸æ“‡é é¢åº•éƒ¨çš„ <strong>下一步</strong>。"
msgid "Choose File ..."
msgstr "é¸æ“‡æª”案⋯"
@@ -1276,6 +1372,12 @@ msgstr "é¸æ“‡æª”案⋯"
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "é¸æ“‡åˆ†æ”¯/標籤(例如:%{master})或者輸入更動紀錄(例如:%{sha})以查看更改內容或建立åˆä½µè«‹æ±‚。"
+msgid "Choose a template..."
+msgstr ""
+
+msgid "Choose a type..."
+msgstr ""
+
msgid "Choose any color."
msgstr "é¸å–é¡è‰²ã€‚"
@@ -1403,7 +1505,7 @@ msgid "Click any <strong>project name</strong> in the project list below to navi
msgstr "在專案列表點擊任何<strong>專案å稱</strong>,將轉跳到專案的里程碑。"
msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
-msgstr ""
+msgstr "點擊 <strong>下載</strong> 按鈕並等待下載完æˆã€‚"
msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
msgstr "點擊左上角的<strong>å‡ç´š</strong>按鈕,將æå‡è‡³ç¾¤çµ„里程碑。"
@@ -1438,6 +1540,9 @@ msgstr "複製檔案庫"
msgid "Close"
msgstr "關閉"
+msgid "Close epic"
+msgstr ""
+
msgid "Closed"
msgstr "已關閉"
@@ -1447,6 +1552,9 @@ msgstr "已關閉議題"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} 已經æˆåŠŸå®‰è£åˆ°æ‚¨çš„ Kubernetes å¢é›†"
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr "Api 網å€"
@@ -1456,12 +1564,21 @@ msgstr "增加 Kubernetes å¢é›†"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Kubernetes å¢é›†æ•´åˆçš„進階設定"
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr "當嘗試ç²å–專案å€åŸŸæ™‚錯誤發生:%{error}"
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr "當嘗試ç²å–您的專案時發生錯誤:%{error}"
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr "應用程å¼"
@@ -1474,11 +1591,11 @@ msgstr "CAèªè­‰"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "憑證 (PEMæ ¼å¼)"
-msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "é¸æ“‡æ‚¨è¦ä½¿ç”¨æ­¤ Kubernetes å¢é›†çš„專案環境"
+msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
+msgstr ""
-msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr "控制 Kubernetes å¢é›†å¦‚何與 GitLab æ•´åˆ"
+msgid "ClusterIntegration|Choose which of your environments will use this cluster."
+msgstr ""
msgid "ClusterIntegration|Copy API URL"
msgstr "複製 API 連çµ"
@@ -1504,6 +1621,12 @@ msgstr "建立 Kubernetes å¢é›†"
msgid "ClusterIntegration|Did you know?"
msgstr "你知é“嗎?"
+msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "輸入您的 Kubernetes å¢é›†è©³ç´°è³‡è¨Š"
@@ -1528,6 +1651,9 @@ msgstr "GitLab æ•´åˆ"
msgid "ClusterIntegration|GitLab Runner"
msgstr "Gitlab Runner"
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr "Google 雲端專案"
@@ -1540,6 +1666,9 @@ msgstr "Google Kubernetes Engine 專案"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr "éš±è—"
@@ -1555,6 +1684,9 @@ msgstr "å…¥å£"
msgid "ClusterIntegration|Ingress IP Address"
msgstr "å…¥å£ IP ä½ç½®"
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr "安è£"
@@ -1579,6 +1711,9 @@ msgstr "Jupyter 主機å稱"
msgid "ClusterIntegration|JupyterHub"
msgstr "JupyterHub"
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Kubernetes å¢é›†"
@@ -1591,15 +1726,6 @@ msgstr "Kubernetes å¢é›†å¥åº·ç¨‹åº¦"
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Kubernetes å¢é›†æ•´åˆ"
-msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "此專案ç¦ç”¨ Kubernetes å¢é›†æ•´åˆ"
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "此專案已啟用 Kubernetes å¢é›†æ•´åˆ"
-
-msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "此專案已經啟用 Kubernetes å¢é›†æ•´åˆã€‚關閉此整åˆä¸¦ä¸æœƒå¼•éŸ¿æ‚¨çš„ Kubernetes å¢é›†ï¼Œä»–åªæœƒæš«æ™‚性的關閉 GitLab 與它的連çµã€‚"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes å¢é›†å·²ç¶“在 Google Kubernetes Engine 上被建立"
@@ -1624,12 +1750,6 @@ msgstr "瞭解有關 %{help_link_start}Kubernetes%{help_link_end} 的詳細資è¨
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr "瞭解有關 %{help_link_start}å€åŸŸ%{help_link_end} 的詳細資訊。"
-msgid "ClusterIntegration|Learn more about environments"
-msgstr "了解關於環境的更多資訊"
-
-msgid "ClusterIntegration|Learn more about security configuration"
-msgstr "了解更多安全性設定"
-
msgid "ClusterIntegration|Machine type"
msgstr "機器型別"
@@ -1672,6 +1792,9 @@ msgstr "請為你的 Kubernetes å¢é›†è¼¸å…¥å­˜å–訊æ¯ï¼Œå¦‚果你需è¦å¹«åŠ©
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "請確èªä½ çš„ Google 帳號是å¦ç¬¦åˆé€™äº›æ¢ä»¶"
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr "專案命å空間"
@@ -1681,6 +1804,12 @@ msgstr "專案命å空間(é¸å¡«ï¼Œä¸å¯é‡è¤‡ï¼‰"
msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
+msgid "ClusterIntegration|RBAC-enabled cluster (experimental)"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr "閱讀我們關於 Kubernetes å¢é›†æ•´åˆçš„ %{link_to_help_page}。"
@@ -1693,6 +1822,9 @@ msgstr "刪除整åˆ"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "刪除此 Kubernetes å¢é›†è¨­å®šï¼Œé€™ä¸æœƒåˆªé™¤å¯¦éš›çš„ Kubernetes å¢é›†ã€‚"
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "請求安è£å¤±æ•—"
@@ -1708,9 +1840,6 @@ msgstr "æœå°‹å°ˆæ¡ˆ"
msgid "ClusterIntegration|Search zones"
msgstr "æœå°‹å€åŸŸ"
-msgid "ClusterIntegration|Security"
-msgstr "安全"
-
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "查看與編輯您的 Kubernetes å¢é›†è©³ç´°è³‡è¨Š"
@@ -1747,12 +1876,18 @@ msgstr "在 Google Kubernetes Engine 上建立å¢é›†æ™‚發生錯誤"
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "å®‰è£ %{title} 時發生錯誤"
-msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr "é è¨­çš„å¢é›†è¨­å®šï¼Œå…許存å–廣泛的功能,用以建置和部署å¯ä»¥ä½¿ç”¨å®¹å™¨æŠ€è¡“擴充的應用程å¼ã€‚"
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "此帳戶必須æ“有在 %{link_to_container_project} 中建立 Kubernetes å¢é›†çš„權é™"
+msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "åˆ‡æ› Kubernetes å¢é›†"
@@ -1765,9 +1900,15 @@ msgstr "權æ–"
msgid "ClusterIntegration|Validating project billing status"
msgstr "正在驗證è£ç½®è¨ˆè²»ç‹€æ…‹"
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "當å¢é›†é€£çµåˆ°æ­¤å°ˆæ¡ˆï¼Œä½ å¯ä»¥ä½¿ç”¨è¤‡é–±æ‡‰ç”¨ï¼Œéƒ¨ç½²ä½ çš„應用程å¼ï¼ŒåŸ·è¡Œä½ çš„æµæ°´ç·šï¼Œé‚„有更多容易上手的方å¼å¯ä»¥ä½¿ç”¨ã€‚"
+msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
+msgstr ""
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "您的帳號必須有 %{link_to_kubernetes_engine}"
@@ -1786,9 +1927,6 @@ msgstr "文件"
msgid "ClusterIntegration|help page"
msgstr "說明é é¢"
-msgid "ClusterIntegration|installing applications"
-msgstr "安è£æ‡‰ç”¨ç¨‹åº"
-
msgid "ClusterIntegration|meets the requirements"
msgstr "符åˆéœ€æ±‚"
@@ -1798,6 +1936,9 @@ msgstr "設定正確"
msgid "ClusterIntegration|sign up"
msgstr "註冊"
+msgid "Code owners"
+msgstr ""
+
msgid "Cohorts"
msgstr "Cohorts"
@@ -1845,6 +1986,9 @@ msgstr "é€äº¤"
msgid "CommitMessage|Add %{file_name}"
msgstr "建立 %{file_name}"
+msgid "CommitWidget|authored"
+msgstr ""
+
msgid "Commits"
msgstr "更動記錄"
@@ -1917,17 +2061,14 @@ msgstr "機密的"
msgid "Configure Gitaly timeouts."
msgstr "設定 Gitaly 延é²éŽä¹…。"
-msgid "Configure Sidekiq job throttling."
-msgstr "設定 Sidekiq 工作é™åˆ¶ã€‚"
-
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr "於檔案庫上設定自動 Git 檢查與內務管ç†"
msgid "Configure limits for web and API requests."
msgstr "為網路與 API 請求設定é™åˆ¶ã€‚"
-msgid "Configure push and pull mirrors."
-msgstr "設定推é€å’Œæå–çš„é¡åƒã€‚"
+msgid "Configure push mirrors."
+msgstr ""
msgid "Configure storage path and circuit breaker settings."
msgstr "設定儲存路徑與斷路器設定。"
@@ -2016,6 +2157,9 @@ msgstr "è²¢ç»"
msgid "Contribution guide"
msgstr "å”作指å—"
+msgid "Contributions for <strong>%{calendar_date}</strong>"
+msgstr ""
+
msgid "Contributions per group member"
msgstr "æ¯å€‹ç¾¤çµ„æˆå“¡çš„è²¢ç»"
@@ -2049,6 +2193,15 @@ msgstr "控制此 Geo 節點驗證æ“作的最大並行é‡"
msgid "ConvDev Index"
msgstr "ConvDev 索引"
+msgid "Copy %{protocol} clone URL"
+msgstr ""
+
+msgid "Copy HTTPS clone URL"
+msgstr ""
+
+msgid "Copy SSH clone URL"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr "複製 SSH 公鑰到剪貼簿"
@@ -2154,9 +2307,6 @@ msgstr "建立..."
msgid "Create project label"
msgstr "建立專案標籤"
-msgid "CreateNewFork|Fork"
-msgstr "分支"
-
msgid "CreateTag|Tag"
msgstr "建立標籤"
@@ -2172,6 +2322,9 @@ msgstr "建立於"
msgid "Created by me"
msgstr "由我創建"
+msgid "Created on"
+msgstr ""
+
msgid "Created on:"
msgstr "建立於:"
@@ -2184,6 +2337,9 @@ msgstr "Cron 時å€"
msgid "Cron syntax"
msgstr "Cron 語法"
+msgid "Current Branch"
+msgstr ""
+
msgid "Current node"
msgstr "ç›®å‰ç¯€é»ž"
@@ -2193,6 +2349,9 @@ msgstr "個人資料"
msgid "CurrentUser|Settings"
msgstr "設定"
+msgid "Custom"
+msgstr ""
+
msgid "Custom CI config path"
msgstr "自定義 CI é…置路徑"
@@ -2202,6 +2361,9 @@ msgstr "自訂事件通知"
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "自訂通知的等級與åƒèˆ‡åº¦è¨­å®šç›¸åŒã€‚使用自訂通知讓你åªæ”¶åˆ°ç‰¹å®šçš„事件通知。想了解更多資訊,請查閱 %{notification_link} 。"
+msgid "Custom project templates"
+msgstr ""
+
msgid "Customize colors"
msgstr "自訂é¡è‰²"
@@ -2214,6 +2376,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr "週期分æž"
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr "程å¼é–‹ç™¼"
@@ -2244,6 +2409,12 @@ msgstr "全部"
msgid "DashboardProjects|Personal"
msgstr "個人"
+msgid "Date picker"
+msgstr ""
+
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr "å二月"
@@ -2253,6 +2424,9 @@ msgstr "å二月"
msgid "Decline and sign out"
msgstr "拒絕並登出"
+msgid "Default Branch"
+msgstr ""
+
msgid "Default classification label"
msgstr "é è¨­åˆ†é¡žæ¨™ç±¤"
@@ -2268,6 +2442,9 @@ msgstr "使用 Cron 語法自訂排程"
msgid "Delete"
msgstr "刪除"
+msgid "Delete Package"
+msgstr ""
+
msgid "Delete Snippet"
msgstr "刪除片段"
@@ -2428,12 +2605,18 @@ msgstr "銷毀"
msgid "Details"
msgstr "細節"
+msgid "Detect host keys"
+msgstr ""
+
msgid "Diffs|No file name available"
msgstr "沒有å¯ç”¨çš„檔案å稱"
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr "讀å–差異檔時發生了一些錯誤."
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr "目錄å稱"
@@ -2446,9 +2629,21 @@ msgstr "為此專案åœç”¨"
msgid "Disable group Runners"
msgstr "åœç”¨ç¾¤çµ„執行器"
+msgid "Discard"
+msgstr ""
+
+msgid "Discard all changes"
+msgstr ""
+
+msgid "Discard all unstaged changes?"
+msgstr ""
+
msgid "Discard changes"
msgstr "撤銷變更"
+msgid "Discard changes to %{path}?"
+msgstr ""
+
msgid "Discard draft"
msgstr "放棄è‰ç¨¿"
@@ -2548,7 +2743,7 @@ msgstr "編輯 %{user_name} 的身份"
msgid "Elasticsearch"
msgstr "Elasticsearch"
-msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr "Elasticsearch æ•´åˆã€‚ Elasticsearch AWS IAM。"
msgid "Email"
@@ -2608,12 +2803,33 @@ msgstr "啟用 reCAPTCHA 或 Akismet 並設定 IP 上é™ã€‚"
msgid "Enable the Performance Bar for a given group."
msgstr "啟用æ供群組的效能桿。"
+msgid "Enable usage ping"
+msgstr ""
+
+msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
+msgstr ""
+
msgid "Enabled"
msgstr "已啟用"
msgid "Ends at (UTC)"
msgstr "æ–¼ (UTC) çµæŸ"
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr "環境"
@@ -2644,6 +2860,9 @@ msgstr "環境"
msgid "Environments|Environments"
msgstr "環境"
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr "任務"
@@ -2659,6 +2878,9 @@ msgstr "尚未部署"
msgid "Environments|No pod name has been specified"
msgstr "沒有指定 pod å稱"
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -2692,6 +2914,9 @@ msgstr "已更新"
msgid "Environments|You don't have any environments right now."
msgstr "你還沒有設置環境"
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr "Epic"
@@ -2707,6 +2932,30 @@ msgstr "Epics 開發è—圖"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
msgstr "Epics 讓你能更有效率且花費更少功夫的管ç†æ‚¨å°ˆæ¡ˆçš„組åˆã€‚"
+msgid "Epics|An error occurred while saving %{epicDateType} date"
+msgstr ""
+
+msgid "Epics|How can I solve this?"
+msgstr ""
+
+msgid "Epics|More information"
+msgstr ""
+
+msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
+msgstr ""
+
+msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
+msgstr ""
+
+msgid "Epics|due"
+msgstr ""
+
+msgid "Epics|start"
+msgstr ""
+
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr "錯誤報告與日誌"
@@ -2731,6 +2980,9 @@ msgstr "讀å–ä½¿ç”¨æƒ…æ³ ping 資料時發生錯誤。"
msgid "Error loading branch data. Please try again."
msgstr "載入分支資料時錯誤,請é‡è©¦ã€‚"
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr "載入最新更動紀錄時失敗。"
@@ -2743,6 +2995,12 @@ msgstr "載入åˆä½µè«‹æ±‚時錯誤。"
msgid "Error loading project data. Please try again."
msgstr "載入專案資料時錯誤,請é‡è©¦ã€‚"
+msgid "Error loading template types."
+msgstr ""
+
+msgid "Error loading template."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "切æ›è¨‚閱通知時發生錯誤"
@@ -2755,6 +3013,9 @@ msgstr "更新代辦事項清單時發生錯誤"
msgid "Error updating todo status."
msgstr "更新代辦事項時發生錯誤。"
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr "é ä¼°"
@@ -2797,6 +3058,9 @@ msgstr "展開全部"
msgid "Expand sidebar"
msgstr "展開å´é‚Šæ¬„"
+msgid "Expiration date"
+msgstr ""
+
msgid "Explore"
msgstr "ç€è¦½"
@@ -2854,6 +3118,9 @@ msgstr "檢查相關分支失敗。"
msgid "Failed to remove issue from board, please try again."
msgstr "從看æ¿åˆªé™¤è­°é¡Œæ™‚發生錯誤,請ç¨å€™é‡è©¦"
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr "無法刪除æµæ°´ç·šæŽ’程"
@@ -2875,6 +3142,9 @@ msgstr "二月"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "æ­¤é é¢æ¬„ä½ç¾åœ¨ç„¡æ³•ç·¨è¼¯ï¼Œä½ å¯ä»¥è¨­ç½®"
+msgid "File templates"
+msgstr ""
+
msgid "Files"
msgstr "檔案"
@@ -2887,9 +3157,18 @@ msgstr "填寫以下的欄ä½ï¼Œé–‹å•Ÿ <strong>%{enable_label}</strong> 後按ä¸
msgid "Filter"
msgstr ""
+msgid "Filter by %{issuable_type} that are currently closed."
+msgstr ""
+
+msgid "Filter by %{issuable_type} that are currently opened."
+msgstr ""
+
msgid "Filter by commit message"
msgstr "以更動說明篩é¸"
+msgid "Filter..."
+msgstr ""
+
msgid "Find by path"
msgstr "以路徑æœå°‹"
@@ -2902,6 +3181,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprints"
+msgstr ""
+
msgid "Finished"
msgstr "已完æˆ"
@@ -2911,6 +3193,18 @@ msgstr "首次"
msgid "FirstPushedBy|pushed by"
msgstr "推é€è€…:"
+msgid "Fixed date"
+msgstr ""
+
+msgid "Fixed due date"
+msgstr ""
+
+msgid "Fixed start date"
+msgstr ""
+
+msgid "Fixed:"
+msgstr ""
+
msgid "FogBugz Email"
msgstr "FogBugz é›»å­ä¿¡ç®±"
@@ -2938,16 +3232,18 @@ msgstr "é å°¾è¨Šæ¯"
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr "å°æ–¼å…§éƒ¨å°ˆæ¡ˆï¼Œä»»ä½•ç™»éŒ„的使用者都å¯ä»¥æŸ¥çœ‹æµæ°´ç·šä¸¦å­˜å–工作詳細資訊 (輸出日誌和產物)"
+msgid "For more information, go to the "
+msgstr ""
+
+msgid "For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}."
+msgstr ""
+
msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
msgstr "å°æ–¼ç§äººå°ˆæ¡ˆï¼Œä»»ä½•æˆå“¡ (訪客或以上) 都å¯ä»¥æŸ¥çœ‹æµæ°´ç·šä¸¦å­˜å–工作詳細資訊 (輸出日誌和產物)"
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr "å°æ–¼å…¬é–‹å°ˆæ¡ˆï¼Œä»»ä½•äººéƒ½å¯ä»¥æŸ¥çœ‹æµæ°´ç·šä¸¦å­˜å–工作詳細資訊 (輸出日誌和產物)"
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] "分支"
-
msgid "ForkedFromProjectPath|Forked from"
msgstr "分支自"
@@ -2969,6 +3265,9 @@ msgstr "來自 %{provider_title}"
msgid "From Bitbucket"
msgstr "從 Bitbucket"
+msgid "From Bitbucket Server"
+msgstr ""
+
msgid "From FogBugz"
msgstr "從 FogBugz"
@@ -2984,6 +3283,9 @@ msgstr "從議題建立直到部署至營é‹ç’°å¢ƒ"
msgid "From merge request merge until deploy to production"
msgstr "從請求被åˆä½µå¾Œç›´åˆ°éƒ¨ç½²è‡³ç‡Ÿé‹ç’°å¢ƒ"
+msgid "From milestones:"
+msgstr ""
+
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "從 Kubernetes å¢é›†è©³ç´°è³‡æ–™é ï¼Œå¾žæ‡‰ç”¨ç¨‹å¼åˆ—表中安è£é‹è¡Œå™¨"
@@ -2999,6 +3301,9 @@ msgstr "一般æµæ°´ç·š"
msgid "Generate a default set of labels"
msgstr "產生é è¨­çš„標籤"
+msgid "Geo"
+msgstr ""
+
msgid "Geo Nodes"
msgstr "Geo 節點"
@@ -3164,33 +3469,132 @@ msgstr "Wiki 已在主è¦ç¯€é»žçš„å°æ‡‰æ–¹é©—證完æˆ"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr "您使用ä¸å®‰å…¨çš„ HTTP 連線設定 Geo 節點。我們建議使用 HTTPS。"
+msgid "Geo|%{name} is scheduled for forced re-download"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-check"
+msgstr ""
+
+msgid "Geo|%{name} is scheduled for re-sync"
+msgstr ""
+
msgid "Geo|All projects"
msgstr "所有專案"
+msgid "Geo|Could not remove tracking entry for an existing project."
+msgstr ""
+
+msgid "Geo|Error message"
+msgstr ""
+
+msgid "Geo|Failed"
+msgstr ""
+
msgid "Geo|File sync capacity"
msgstr "檔案åŒæ­¥é‡"
msgid "Geo|Groups to synchronize"
msgstr "è¦åŒæ­¥çš„群組"
+msgid "Geo|In sync"
+msgstr ""
+
+msgid "Geo|Last successful sync"
+msgstr ""
+
+msgid "Geo|Last sync attempt"
+msgstr ""
+
+msgid "Geo|Last time verified"
+msgstr ""
+
+msgid "Geo|Never"
+msgstr ""
+
+msgid "Geo|Next sync scheduled at"
+msgstr ""
+
+msgid "Geo|No errors"
+msgstr ""
+
+msgid "Geo|Pending"
+msgstr ""
+
+msgid "Geo|Pending synchronization"
+msgstr ""
+
+msgid "Geo|Pending verification"
+msgstr ""
+
+msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
+msgstr ""
+
msgid "Geo|Projects in certain groups"
msgstr "æŸäº›ç¾¤çµ„中的專案"
msgid "Geo|Projects in certain storage shards"
msgstr "æŸäº›å„²å­˜ç©ºé–“碎片中的專案"
+msgid "Geo|Recheck"
+msgstr ""
+
+msgid "Geo|Redownload"
+msgstr ""
+
+msgid "Geo|Remove"
+msgstr ""
+
msgid "Geo|Repository sync capacity"
msgstr "版本庫åŒæ­¥é‡"
+msgid "Geo|Resync"
+msgstr ""
+
+msgid "Geo|Retry count"
+msgstr ""
+
+msgid "Geo|Retry counts"
+msgstr ""
+
msgid "Geo|Select groups to replicate."
msgstr "é¸æ“‡è¦è¤‡è£½çš„群組。"
msgid "Geo|Shards to synchronize"
msgstr "è¦åŒæ­¥çš„碎片"
+msgid "Geo|Status"
+msgstr ""
+
+msgid "Geo|Synced"
+msgstr ""
+
+msgid "Geo|Synchronization failed - %{error}"
+msgstr ""
+
+msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
+msgstr ""
+
+msgid "Geo|Tracking entry will be removed. Are you sure?"
+msgstr ""
+
+msgid "Geo|Unknown state"
+msgstr ""
+
msgid "Geo|Verification capacity"
msgstr "驗證能力"
+msgid "Geo|Verification failed - %{error}"
+msgstr ""
+
+msgid "Geo|Waiting for scheduler"
+msgstr ""
+
+msgid "Geo|You need a different license to use Geo replication"
+msgstr ""
+
+msgid "Get a free instance review"
+msgstr ""
+
msgid "Git"
msgstr "Git"
@@ -3239,6 +3643,9 @@ msgstr "GitLab 將會在後å°é€²è¡Œç”¢ç”Ÿ GitLab 資料庫å‡å CSV 的作業ï
msgid "GitLab.com import"
msgstr "匯入 GitLab.com"
+msgid "GitLab’s issue tracker"
+msgstr ""
+
msgid "Gitaly"
msgstr "Gitaly"
@@ -3260,14 +3667,11 @@ msgstr "返回"
msgid "Go back"
msgstr "上一é "
-msgid "Go to %{link_to_google_takeout}."
+msgid "Go to"
msgstr ""
-msgid "Go to your fork"
-msgstr "å‰å¾€æ‚¨çš„分支"
-
-msgid "GoToYourFork|Fork"
-msgstr "å‰å¾€æ‚¨çš„分支"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Google Code import"
msgstr "匯入 Google Code"
@@ -3329,14 +3733,14 @@ msgstr "抱歉,沒有任何 Epic 符åˆæ‚¨çš„æœå°‹ã€‚"
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
msgstr "開發è—圖顯示時間軸上您的 Epic 進度"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "è‹¥è¦æª¢è¦–開發è—圖,請新增這個群組或其å­ç¾¤çµ„中一個 Epic 的計畫開始與完æˆæ—¥æœŸã€‚月檢視中,åªæœƒé¡¯ç¤ºä¸Šå€‹æœˆã€é€™å€‹æœˆã€å’ŒæŽ¥ä¸‹ä¾†äº”個月的 Epic &ndash; 從 %{startDate} 到 %{endDate}。"
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "è‹¥è¦æª¢è¦–開發è—圖,請新增這個群組或其å­ç¾¤çµ„中一個 Epic 的計畫開始與完æˆæ—¥æœŸã€‚季度檢視中,åªæœƒé¡¯ç¤ºä¸Šå€‹å­£åº¦ã€é€™å€‹å­£åº¦ã€å’ŒæŽ¥ä¸‹ä¾†å››å€‹å­£åº¦çš„ Epic &ndash; 從 %{startDate} 到 %{endDate}。"
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "è‹¥è¦æª¢è¦–開發è—圖,請新增這個群組或其å­ç¾¤çµ„中一個 Epic 的計畫開始與完æˆæ—¥æœŸã€‚週檢視中,åªæœƒé¡¯ç¤ºä¸Šé€±ã€é€™é€±ã€å’ŒæŽ¥ä¸‹ä¾†å››é€±çš„ Epic &ndash; 從 %{startDate} 到 %{endDate}。"
+msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
msgstr "è‹¥è¦æ“´å¤§æ‚¨çš„æœå°‹ï¼Œè«‹å˜—試修改或移除篩é¸å™¨ã€‚月檢視中,åªæœƒé¡¯ç¤ºä¸Šå€‹æœˆã€é€™å€‹æœˆã€å’ŒæŽ¥ä¸‹ä¾†äº”個月的 Epic &ndash; 從 %{startDate} 到 %{endDate}。"
@@ -3350,6 +3754,15 @@ msgstr "è‹¥è¦æ“´å¤§æ‚¨çš„æœå°‹ï¼Œè«‹å˜—試修改或移除篩é¸å™¨ã€‚週檢視
msgid "GroupRoadmap|Until %{dateWord}"
msgstr "直到 %{dateWord}"
+msgid "GroupSettings|Badges"
+msgstr ""
+
+msgid "GroupSettings|Customize your group badges."
+msgstr ""
+
+msgid "GroupSettings|Learn more about badges."
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "ç¦æ­¢èˆ‡å…¶ä»–群組共享 %{group} 中的專案"
@@ -3413,6 +3826,9 @@ msgstr "找ä¸åˆ°ç¾¤çµ„"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "ä½ å¯ä»¥ç®¡ç†ç¾¤çµ„內所有æˆå“¡çš„æ¯å€‹å°ˆæ¡ˆçš„å­˜å–權é™"
+msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
+msgstr ""
+
msgid "GroupsTree|Create a project in this group."
msgstr "在此群組建立新的專案"
@@ -3425,20 +3841,20 @@ msgstr "編輯群組"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr "無法離開群組,請確ä¿æ‚¨ä¸æ˜¯å”¯ä¸€çš„æ“有者。"
-msgid "GroupsTree|Filter by name..."
-msgstr "以å稱篩é¸â‹¯"
-
msgid "GroupsTree|Leave this group"
msgstr "離開此群組"
msgid "GroupsTree|Loading groups"
msgstr "群組讀å–中"
-msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr "ä¸å¥½æ„æ€ï¼Œæ²’有æœå°‹åˆ°ä»»ä½•ç¬¦åˆæ¢ä»¶çš„群組"
+msgid "GroupsTree|No groups matched your search"
+msgstr ""
-msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr "ä¸å¥½æ„æ€ï¼Œæ²’有æœå°‹åˆ°ä»»ä½•ç¬¦åˆæ¢ä»¶çš„群組或專案"
+msgid "GroupsTree|No groups or projects matched your search"
+msgstr ""
+
+msgid "GroupsTree|Search by name"
+msgstr ""
msgid "Have your users email"
msgstr "有來自您使用者的信件"
@@ -3473,6 +3889,15 @@ msgstr "說明é é¢"
msgid "Help page text and support page url."
msgstr "說明é é¢æ–‡å­—與支æ´é é¢é€£çµ"
+msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
+msgstr ""
+
+msgid "Hide host keys manual input"
+msgstr ""
+
+msgid "Hide payload"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "éš±è—資料"
@@ -3495,21 +3920,45 @@ msgstr "æœå‹™æ¢æ¬¾å’Œéš±ç§æ”¿ç­–"
msgid "ID"
msgstr "ID"
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr "æ交"
msgid "IDE|Edit"
msgstr "編輯"
-msgid "IDE|Go back"
-msgstr "返回"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
+msgstr ""
msgid "IDE|Open in file view"
msgstr "以檔案顯示開啟"
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr "審閱"
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr "識別碼"
@@ -3519,6 +3968,9 @@ msgstr "身份"
msgid "Identity provider single sign on URL"
msgstr "身份æ供者的 URL 單一登入"
+msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
+msgstr ""
+
msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr "如果ç¦ç”¨ï¼Œå­˜å–等級將å–決於使用者在專案中的權é™ã€‚"
@@ -3576,6 +4028,9 @@ msgstr "匯入專案"
msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
msgid "Import projects from FogBugz"
msgstr "從 FogBugz 匯入專案"
@@ -3585,6 +4040,9 @@ msgstr ""
msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr "從 GitHub 匯入檔案庫"
@@ -3603,18 +4061,33 @@ msgstr "é€éŽè­°é¡Œæ¬Šé‡å’Œ GitLab ä¼æ¥­ç‰ˆæœ¬æ”¹å–„議題管ç†ã€‚"
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr "使用進階全域æœå°‹å’Œ GitLab ä¼æ¥­ç‰ˆæœ¬ä¾†æ”¹é€²æœå°‹ã€‚"
+msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
+msgstr ""
+
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr "包括所有用戶必須接å—çš„æœå‹™æ¢æ¬¾å”議和隱ç§æ”¿ç­–。"
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr "ä¸ç›¸å®¹çš„專案"
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr "內嵌"
+msgid "Input host keys manually"
+msgstr ""
+
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr "å®‰è£ GitHub 執行器"
@@ -3625,6 +4098,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] "實例"
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
+msgstr ""
+
msgid "Instance does not support multiple Kubernetes clusters"
msgstr "主機沒有支æ´å¤šå€‹ Kubernetes å¢é›†"
@@ -3643,12 +4122,21 @@ msgstr "內部 - 任何登入的使用者都å¯ä»¥æŸ¥çœ‹è©²ç¾¤çµ„åŠå…¶å°ˆæ¡ˆ"
msgid "Internal - The project can be accessed by any logged in user."
msgstr "內部 - 任何登入的使用者都å¯ä»¥å­˜å–此專案"
+msgid "Internal users"
+msgstr ""
+
msgid "Interval Pattern"
msgstr "循環週期"
msgid "Introducing Cycle Analytics"
msgstr "週期分æžç°¡ä»‹"
+msgid "Invite"
+msgstr ""
+
+msgid "Issue"
+msgstr ""
+
msgid "Issue Boards"
msgstr "議題看æ¿"
@@ -3688,6 +4176,48 @@ msgstr "工作已被抹除"
msgid "Jobs"
msgstr "任務"
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Complete Raw"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Erase job log"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed in"
+msgstr ""
+
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
msgid "Jul"
msgstr "七月"
@@ -3700,12 +4230,6 @@ msgstr "六月"
msgid "June"
msgstr "六月"
-msgid "Koding"
-msgstr "Koding"
-
-msgid "Koding Dashboard"
-msgstr "Koding 控制é¢æ¿"
-
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -3775,6 +4299,9 @@ msgstr "<span>è¦è®“標籤</span> %{labelTitle} <span>æå‡åˆ°ç¾¤çµ„標籤嗎ï¼
msgid "Labels|Promote Label"
msgstr "æå‡æ¨™ç±¤"
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "最近 %d 天"
@@ -3785,6 +4312,9 @@ msgstr "最新æµæ°´ç·š"
msgid "Last commit"
msgstr "最後更動記錄"
+msgid "Last contact"
+msgstr ""
+
msgid "Last edited %{date}"
msgstr "最後編輯於 %{date}"
@@ -3809,6 +4339,9 @@ msgstr "最新修改"
msgid "Learn more"
msgstr "進一步了解"
+msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
+msgstr ""
+
msgid "Learn more about Kubernetes"
msgstr "了解更多端於 Kubernetes 的資訊"
@@ -3836,6 +4369,61 @@ msgstr ""
msgid "License"
msgstr "授權"
+msgid "LicenseManagement|Approve license"
+msgstr ""
+
+msgid "LicenseManagement|Approve license?"
+msgstr ""
+
+msgid "LicenseManagement|Approved"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license"
+msgstr ""
+
+msgid "LicenseManagement|Blacklist license?"
+msgstr ""
+
+msgid "LicenseManagement|Blacklisted"
+msgstr ""
+
+msgid "LicenseManagement|License"
+msgstr ""
+
+msgid "LicenseManagement|License Management"
+msgstr ""
+
+msgid "LicenseManagement|License details"
+msgstr ""
+
+msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
+msgstr ""
+
+msgid "LicenseManagement|Packages"
+msgstr ""
+
+msgid "LicenseManagement|Remove license"
+msgstr ""
+
+msgid "LicenseManagement|Remove license?"
+msgstr ""
+
+msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
+msgstr ""
+
+msgid "LicenseManagement|URL"
+msgstr ""
+
+msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
+msgstr ""
+
+msgid "Licenses"
+msgstr ""
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+
msgid "LinkedIn"
msgstr "LinkedIn"
@@ -3848,9 +4436,15 @@ msgstr "列出您的 Gitea 版本庫"
msgid "List available repositories"
msgstr "列出å¯ç”¨çš„版本庫"
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr "列出您 GitHub 的檔案庫"
+msgid "Live preview"
+msgstr ""
+
msgid "Loading contribution stats for group members"
msgstr "正在讀å–群組æˆå“¡çš„è²¢ç»çµ±è¨ˆ"
@@ -3869,6 +4463,9 @@ msgstr "鎖定 %{issuableDisplayName}"
msgid "Lock not found"
msgstr "找ä¸åˆ°éŽ–定的檔案"
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr "鎖定目å‰å°ˆæ¡ˆ"
@@ -3896,6 +4493,9 @@ msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage Web IDE features"
+msgstr ""
+
msgid "Manage access"
msgstr "管ç†å­˜å–"
@@ -3930,7 +4530,7 @@ msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Map a Google Code user to a GitLab user"
-msgstr ""
+msgstr "å°æ‡‰Google Code åŠ GitLab 中的使用者"
msgid "Map a Google Code user to a full email address"
msgstr ""
@@ -3950,9 +4550,21 @@ msgstr "標記「å³å°‡å®Œæˆã€ç‚ºå®Œæˆã€‚"
msgid "Markdown enabled"
msgstr "已啟用 Markdown"
+msgid "Maven Metadata"
+msgstr ""
+
+msgid "Maven package"
+msgstr ""
+
+msgid "Max access level"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "最大 git 儲存失敗"
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr "五月"
@@ -4001,15 +4613,15 @@ msgstr "儲存評論失敗"
msgid "MergeRequests|Toggle comments for this file"
msgstr "切æ›æ­¤æ–‡ä»¶çš„註釋"
-msgid "MergeRequests|Updating discussions failed"
-msgstr "更新討論失敗"
-
msgid "MergeRequests|View file @ %{commitId}"
msgstr "查看文件@ %{commitId}"
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr "查看替æ›æ–‡ä»¶@ %{commitId}"
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr "å·²åˆä½µ"
@@ -4025,6 +4637,9 @@ msgstr "指標 - Influx"
msgid "Metrics - Prometheus"
msgstr "指標 - Prometheus"
+msgid "Metrics and profiling"
+msgstr ""
+
msgid "Metrics|Business"
msgstr "ä¼æ¥­"
@@ -4127,9 +4742,24 @@ msgstr "例如:æ¯ç§’請求"
msgid "Milestone"
msgstr "里程碑"
+msgid "Milestone lists not available with your current license"
+msgstr ""
+
+msgid "Milestone lists show all issues from the selected milestone."
+msgstr ""
+
msgid "Milestones"
msgstr "里程碑"
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests."
+msgstr ""
+
+msgid "Milestones|<p>%{milestonePromotion}</p> %{finalWarning}"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr "刪除里程碑"
@@ -4148,9 +4778,30 @@ msgstr "å°‡ %{milestoneTitle} æå‡æˆç¾¤çµ„里程碑?"
msgid "Milestones|Promote Milestone"
msgstr "推動里程碑"
+msgid "Milestones|Promoting %{milestone} will make it available for all projects inside %{groupName}. Existing project milestones with the same name will be merged. "
+msgstr ""
+
msgid "Milestones|This action cannot be reversed."
msgstr "這動作無法復原。"
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirror user"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "新增 SSH 金鑰"
@@ -4169,9 +4820,6 @@ msgstr "月"
msgid "More"
msgstr "更多"
-msgid "More actions"
-msgstr "更多動作"
-
msgid "More info"
msgstr "更多資訊"
@@ -4220,6 +4868,9 @@ msgstr "登出,並使用其他帳號登入"
msgid "Network"
msgstr "網路"
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr "新增"
@@ -4308,12 +4959,21 @@ msgstr "沒有標籤"
msgid "No assignee"
msgstr "未指派"
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr "沒有改變"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "無法連接到 Gitaly 伺æœå™¨ï¼Œè«‹æª¢æŸ¥æ‚¨çš„日誌ï¼"
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
+msgid "No contributions were found"
+msgstr ""
+
msgid "No due date"
msgstr "沒有到期日"
@@ -4335,6 +4995,9 @@ msgstr "é¸å–的時間範åœä¸­æ²’有議題。"
msgid "No labels with such name or description"
msgstr ""
+msgid "No license. All rights reserved"
+msgstr ""
+
msgid "No merge requests for the selected time period."
msgstr "é¸å–的時間範åœä¸­æ²’有åˆä½µè«‹æ±‚。"
@@ -4347,6 +5010,9 @@ msgstr "沒有消æ¯è¢«è¨˜éŒ„"
msgid "No other labels with such name or description"
msgstr ""
+msgid "No packages stored for this project."
+msgstr ""
+
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -4359,15 +5025,27 @@ msgstr "é¸å–的時間範åœä¸­æ²’有推é€ã€‚"
msgid "No repository"
msgstr "找ä¸åˆ°æª”案庫"
+msgid "No runners found"
+msgstr ""
+
msgid "No schedules"
msgstr "沒有排程"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "Nodes"
+msgstr ""
+
msgid "None"
msgstr "ç„¡"
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr "ä¸å…許åˆä½µ"
@@ -4386,6 +5064,9 @@ msgstr "éžæ©Ÿå¯†"
msgid "Not enough data"
msgstr "資料ä¸è¶³"
+msgid "Not now"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "請注æ„,master 分支é è¨­ç‚ºå—ä¿è­·çš„。 %{link_to_protected_branches}"
@@ -4419,6 +5100,9 @@ msgstr "æµæ°´ç·šå¤±æ•—"
msgid "NotificationEvent|Merge merge request"
msgstr "åˆä½µè«‹æ±‚被åˆä½µ"
+msgid "NotificationEvent|New epic"
+msgstr ""
+
msgid "NotificationEvent|New issue"
msgstr "新增議題"
@@ -4491,18 +5175,25 @@ msgstr "篩é¸"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr "一旦匯入,就å¯ä»¥é€éŽ SSH é¡åƒç‰ˆæœ¬åº«ã€‚閱讀 %{ssh_link} 以了解更多資訊"
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
-msgstr "線上 IDE æ•´åˆè¨­å®šã€‚"
+msgid "Only admins"
+msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr "下é¢åƒ…顯示來自以下æ交的註釋"
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr "åªæœ‰ç¾¤çµ„æˆå“¡æ‰èƒ½ç•™è¨€ã€‚"
@@ -4566,9 +5257,21 @@ msgstr "Outbound 請求"
msgid "Overview"
msgstr "總覽"
+msgid "Overwrite diverged branches"
+msgstr ""
+
msgid "Owner"
msgstr "所有權"
+msgid "Package information"
+msgstr ""
+
+msgid "Package was removed"
+msgstr ""
+
+msgid "Packages"
+msgstr ""
+
msgid "Pages"
msgstr "é é¢"
@@ -4599,9 +5302,15 @@ msgstr "ä½ç½®ï¼š"
msgid "Pause"
msgstr "æš«åœ"
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr "等待處ç†"
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr "æ¯ä»½ä»»å‹™ã€‚如果任務通éŽæ­¤é–¾å€¼ï¼Œå®ƒå°‡è¢«æ¨™è¨˜ç‚ºå¤±æ•—"
@@ -4620,6 +5329,9 @@ msgstr "個人訪å•æ†‘è­‰"
msgid "Pipeline"
msgstr "æµæ°´ç·š"
+msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
+msgstr ""
+
msgid "Pipeline Health"
msgstr "æµæ°´ç·šå¥åº·æŒ‡æ•¸"
@@ -4707,6 +5419,9 @@ msgstr "CI Lint"
msgid "Pipelines|Clear Runner Caches"
msgstr "清除é‹è¡Œå™¨å¿«å–"
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr "開始"
@@ -4728,6 +5443,9 @@ msgstr "ç›®å‰æ²’有 %{scope} æµæ°´ç·šã€‚"
msgid "Pipelines|There are currently no pipelines."
msgstr "ç›®å‰æ²’有æµæ°´ç·šã€‚"
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "這個專案目å‰é‚„沒設定æµæ°´ç·šã€‚"
@@ -4776,12 +5494,6 @@ msgstr "於階段"
msgid "Plain diff"
msgstr "本文差異"
-msgid "Planned finish date"
-msgstr "計畫完æˆæ—¥æœŸ"
-
-msgid "Planned start date"
-msgstr "計畫開始日期"
-
msgid "PlantUML"
msgstr "PlantUML"
@@ -4821,6 +5533,15 @@ msgstr "å好設定"
msgid "Preferences|Navigation theme"
msgstr "導航主題"
+msgid "Press Enter or click to search"
+msgstr ""
+
+msgid "Preview"
+msgstr ""
+
+msgid "Preview payload"
+msgstr ""
+
msgid "Primary"
msgstr "主è¦"
@@ -4851,18 +5572,48 @@ msgstr "個人資料"
msgid "Profile Settings"
msgstr "個人資料設定"
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
+msgid "Profiles|%{author_name} made a private contribution"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr "帳號將會被刪除"
msgid "Profiles|Add key"
msgstr "新增金鑰"
+msgid "Profiles|Add status emoji"
+msgstr ""
+
+msgid "Profiles|Avatar cropper"
+msgstr ""
+
+msgid "Profiles|Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "變更使用者å稱"
+msgid "Profiles|Choose file..."
+msgstr ""
+
+msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
+msgstr ""
+
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr "ç›®å‰è·¯å¾‘:%{path}"
+msgid "Profiles|Current status"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr "刪除帳號"
@@ -4875,33 +5626,108 @@ msgstr "刪除您的帳號?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr "刪除帳號將會造æˆä»¥ä¸‹å½±éŸ¿ï¼š"
+msgid "Profiles|Do not show on profile"
+msgstr ""
+
+msgid "Profiles|Don't display activity-related personal information on your profiles"
+msgstr ""
+
+msgid "Profiles|Edit Profile"
+msgstr ""
+
msgid "Profiles|Invalid password"
msgstr "無效的密碼"
msgid "Profiles|Invalid username"
msgstr "無效的使用者å稱"
+msgid "Profiles|Main settings"
+msgstr ""
+
+msgid "Profiles|No file chosen"
+msgstr ""
+
msgid "Profiles|Path"
msgstr "ä½ç½®"
+msgid "Profiles|Position and size your new avatar"
+msgstr ""
+
+msgid "Profiles|Private contributions"
+msgstr ""
+
+msgid "Profiles|Public Avatar"
+msgstr ""
+
+msgid "Profiles|Remove avatar"
+msgstr ""
+
+msgid "Profiles|Set new profile picture"
+msgstr ""
+
+msgid "Profiles|Some options are unavailable for LDAP accounts"
+msgstr ""
+
+msgid "Profiles|Tell us about yourself in fewer than 250 characters."
+msgstr ""
+
+msgid "Profiles|The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr "這看起來ä¸åƒæ˜¯ SSH 公鑰,您確定è¦å¢žåŠ å®ƒå—Žï¼Ÿ"
+msgid "Profiles|This email will be displayed on your public profile."
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
+msgid "Profiles|This feature is experimental and translations are not complete yet."
+msgstr ""
+
+msgid "Profiles|This information will appear on your profile."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "輸入您的 %{confirmationValue} 以確èªï¼š"
msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr "通常以 \"ssh-rsa …\" 起頭"
+msgid "Profiles|Update profile settings"
+msgstr ""
+
msgid "Profiles|Update username"
msgstr "更新使用者å稱"
+msgid "Profiles|Upload new avatar"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr "使用者å稱更改失敗 - %{message}"
msgid "Profiles|Username successfully changed"
msgstr "使用者å稱順利變更"
+msgid "Profiles|Website"
+msgstr ""
+
+msgid "Profiles|What's your status?"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here"
+msgstr ""
+
+msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here"
+msgstr ""
+
+msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "您沒有權é™åˆªé™¤æ­¤å¸³è™Ÿ"
@@ -4911,6 +5737,18 @@ msgstr "你必須轉æ›ä½ çš„所有權或在你刪除你帳號å‰åˆªé™¤é€™äº›ç¾¤
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "你的帳號目å‰æ“有這些群組:"
+msgid "Profiles|Your email address was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your location was automatically set based on your %{provider_label} account."
+msgstr ""
+
+msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
+msgstr ""
+
+msgid "Profiles|Your status"
+msgstr ""
+
msgid "Profiles|e.g. My MacBook key"
msgstr "例如:我的 MacBook 金鑰"
@@ -4944,6 +5782,9 @@ msgstr "專案 '%{project_name}' 更新完æˆã€‚"
msgid "Project Badges"
msgstr "專案徽章"
+msgid "Project URL"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "專案權é™å¿…須一一指派給æ¯å€‹ä½¿ç”¨è€…。"
@@ -4971,6 +5812,9 @@ msgstr "專案導出已開始。完æˆå¾Œä¸‹è¼‰é€£çµæœƒé€åˆ°æ‚¨çš„信箱。"
msgid "Project name"
msgstr "專案å稱"
+msgid "Project slug"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "訂閱"
@@ -4998,18 +5842,48 @@ msgstr "從未"
msgid "ProjectLifecycle|Stage"
msgstr "階段"
+msgid "ProjectOverview|Fork"
+msgstr ""
+
+msgid "ProjectOverview|Forks"
+msgstr ""
+
+msgid "ProjectOverview|Go to your fork"
+msgstr ""
+
+msgid "ProjectOverview|Star"
+msgstr ""
+
+msgid "ProjectOverview|Unstar"
+msgstr ""
+
+msgid "ProjectOverview|You have reached your project limit"
+msgstr ""
+
+msgid "ProjectOverview|You must sign in to star a project"
+msgstr ""
+
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Badges"
+msgstr ""
+
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "è¯çµ¡ç®¡ç†å“¡ä»¥è®Šæ›´é€™å€‹è¨­å®šã€‚"
+msgid "ProjectSettings|Customize your project badges."
+msgstr ""
+
msgid "ProjectSettings|Failed to protect the tag"
msgstr "無法ä¿è­·æ¨™ç±¤"
msgid "ProjectSettings|Failed to update tag!"
msgstr "無法更新標籤ï¼"
+msgid "ProjectSettings|Learn more about badges."
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "åªæœ‰å·²ç°½ç½²çš„變更æ‰èƒ½æŽ¨é€åˆ°ç‰ˆæœ¬åº«ã€‚"
@@ -5049,6 +5923,9 @@ msgstr "發生了內部錯誤"
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "抱歉,沒有符åˆæœå°‹æ¢ä»¶çš„專案"
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusAlerts|Add alert"
msgstr "增加警報"
@@ -5181,6 +6058,54 @@ msgstr "這個功能已被鎖定。"
msgid "Promotions|Upgrade plan"
msgstr "å‡ç´šæ–¹æ¡ˆ"
+msgid "Protected"
+msgstr ""
+
+msgid "Protected Environments"
+msgstr ""
+
+msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
+msgstr ""
+
+msgid "ProtectedEnvironment|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Choose who is allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironment|Environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
+msgstr ""
+
+msgid "ProtectedEnvironment|Protect an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
+msgstr ""
+
+msgid "ProtectedEnvironment|Select an environment"
+msgstr ""
+
+msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
+msgstr ""
+
+msgid "ProtectedEnvironment|Unprotect"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment can't be unprotected"
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been protected."
+msgstr ""
+
+msgid "ProtectedEnvironment|Your environment has been unprotected"
+msgstr ""
+
msgid "Protip:"
msgstr "æ示:"
@@ -5199,6 +6124,12 @@ msgstr "公開 - 無須任何身份驗證å³å¯å­˜å–該專案"
msgid "Public pipelines"
msgstr "公共æµæ°´ç·š"
+msgid "Pull"
+msgstr ""
+
+msgid "Push"
+msgstr ""
+
msgid "Push Rules"
msgstr "推é€è¦å‰‡"
@@ -5238,12 +6169,25 @@ msgstr "說明檔"
msgid "Real-time features"
msgstr "å³æ™‚功能"
+msgid "Recent searches"
+msgstr ""
+
msgid "Reference:"
msgstr "åƒè€ƒä¾†æº:"
msgid "Refresh"
msgstr "é‡æ–°æ•´ç†"
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+
+msgid "Regenerate key"
+msgstr ""
+
+msgid "Regex pattern"
+msgstr ""
+
msgid "Register / Sign In"
msgstr "註冊 / 登入"
@@ -5295,6 +6239,18 @@ msgstr "刪除優先權"
msgid "Remove project"
msgstr "刪除專案"
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reopen epic"
+msgstr ""
+
msgid "Repair authentication"
msgstr "修復èªè­‰"
@@ -5304,6 +6260,54 @@ msgstr ""
msgid "Repo by URL"
msgstr "來自 URL 的版本庫"
+msgid "Reporting"
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Confidence"
+msgstr ""
+
+msgid "Reports|Dismiss Vulnerability"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|More info"
+msgstr ""
+
+msgid "Reports|New Issue"
+msgstr ""
+
+msgid "Reports|Severity"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|Vulnerability"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr "檔案庫"
@@ -5355,9 +6359,21 @@ msgstr "解決來æºåˆ†æ”¯ä¸Šçš„è¡çª"
msgid "Resolve discussion"
msgstr "關閉討論"
+msgid "Response metrics (AWS ELB)"
+msgstr ""
+
msgid "Response metrics (Custom)"
msgstr "接收指標 (自訂)"
+msgid "Response metrics (HA Proxy)"
+msgstr ""
+
+msgid "Response metrics (NGINX Ingress)"
+msgstr ""
+
+msgid "Response metrics (NGINX)"
+msgstr ""
+
msgid "Resume"
msgstr "繼續"
@@ -5370,6 +6386,9 @@ msgstr "é‡è©¦æ­¤å·¥ä½œ"
msgid "Retry verification"
msgstr "é‡è©¦é©—è­‰"
+msgid "Reveal Variables"
+msgstr ""
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] "顯示隱è—的資料"
@@ -5401,9 +6420,27 @@ msgstr "開發è—圖"
msgid "Run CI/CD pipelines for external repositories"
msgstr "執行外部版本庫的 CI / CD æµæ°´ç·šã€‚"
+msgid "Run untagged jobs"
+msgstr ""
+
+msgid "Runner cannot be assigned to other projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects"
+msgstr ""
+
+msgid "Runner runs jobs from all unassigned projects in its group"
+msgstr ""
+
+msgid "Runner runs jobs from assigned projects"
+msgstr ""
+
msgid "Runner token"
msgstr "執行器憑證"
+msgid "Runner will not receive any new jobs"
+msgstr ""
+
msgid "Runners"
msgstr "執行器"
@@ -5413,6 +6450,21 @@ msgstr "執行器 API"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr "執行器å¯æ”¾ç½®æ–¼ä¸åŒçš„使用者ã€ä¼ºæœå™¨ï¼Œç”šè‡³åœ¨æ‚¨çš„本地機器上。"
+msgid "Runners can be placed on separate users, servers, even on your local machine."
+msgstr ""
+
+msgid "Runners currently online: %{active_runners_count}"
+msgstr ""
+
+msgid "Runners page"
+msgstr ""
+
+msgid "Runners page."
+msgstr ""
+
+msgid "Runners|You have used all your shared Runners pipeline minutes."
+msgstr ""
+
msgid "Running"
msgstr "執行中"
@@ -5428,12 +6480,21 @@ msgstr "SAML 單一登入"
msgid "SAML Single Sign On Settings"
msgstr "SAML 單一登入設定"
+msgid "SAST"
+msgstr ""
+
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr "SAML 憑證登入憑證的 SHA1 指紋。從您的身份æ供者å–得這個指紋 -- 它å¯èƒ½è¢«ç¨±ç‚ºã€ŒThumbprintã€"
msgid "SSH Keys"
msgstr "SSH 金鑰"
+msgid "SSH host keys"
+msgstr ""
+
+msgid "SSH public key"
+msgstr ""
+
msgid "SSL Verification"
msgstr "SSL é©—è­‰"
@@ -5500,12 +6561,42 @@ msgstr "æœå°‹åˆä½µè«‹æ±‚"
msgid "Search milestones"
msgstr "æœå°‹é‡Œç¨‹ç¢‘"
+msgid "Search or filter results..."
+msgstr ""
+
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr "æœå°‹å°ˆæ¡ˆ"
msgid "Search users"
msgstr "æœå°‹ä½¿ç”¨è€…"
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "é‡ç½®å¤±æ•—訊æ¯ç­‰å¾…時間(秒)"
@@ -5515,11 +6606,14 @@ msgstr "等待存å–儲存空間的嘗試時間(秒)"
msgid "Secret:"
msgstr "金鑰:"
+msgid "Security"
+msgstr ""
+
msgid "Security Dashboard"
msgstr "安全儀表æ¿"
-msgid "Security report"
-msgstr "安全性報告"
+msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
msgid "SecurityDashboard|Monitor vulnerabilities in your code"
msgstr "監控程å¼ç¢¼ä¸­çš„æ¼æ´ž"
@@ -5533,6 +6627,9 @@ msgstr "é¸æ“‡"
msgid "Select Archive Format"
msgstr "é¸æ“‡ä¸‹è¼‰æ ¼å¼"
+msgid "Select a group to invite"
+msgstr ""
+
msgid "Select a namespace to fork the project"
msgstr "é¸æ“‡ä¸€å€‹å‘½å空間,以複製這個專案"
@@ -5566,6 +6663,12 @@ msgstr "é¸æ“‡ä¾†æºåˆ†æ”¯"
msgid "Select target branch"
msgstr "é¸æ“‡ç›®æ¨™åˆ†æ”¯"
+msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
+msgstr ""
+
+msgid "Select the custom project template source group."
+msgstr ""
+
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
@@ -5575,6 +6678,9 @@ msgstr "é¸æ“‡æ€§åŒæ­¥"
msgid "Send email"
msgstr "傳é€é›»å­éƒµä»¶"
+msgid "Send usage data"
+msgstr ""
+
msgid "Sep"
msgstr "ä¹æœˆ"
@@ -5602,6 +6708,9 @@ msgstr "請先設定密碼,æ‰èƒ½ä½¿ç”¨ %{protocol} 來上傳或下載。"
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr "設定為é è¨­å€¼ï¼Œä¸¦é™åˆ¶å¯è¦‹ç­‰ç´šã€‚設定匯入來æºå’Œ git å­˜å–連接å”定。"
+msgid "Set instance-wide template repository"
+msgstr ""
+
msgid "Set max session time for web terminal."
msgstr "為網é çµ‚端器設定最長工作階段時間"
@@ -5614,21 +6723,24 @@ msgstr "設定使用者登入的需求。啟用強制性的兩步驟驗證。"
msgid "Set up CI/CD"
msgstr "設定 CI/CD"
-msgid "Set up Koding"
-msgstr "設定 Koding"
+msgid "Set up a %{type} Runner manually"
+msgstr ""
+
+msgid "Set up a specific Runner automatically"
+msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr "根據 %{docsLinkStart}這個文件%{icon}%{docsLinkEnd} 設定斷言 / 屬性 / è²æ˜Ž (email, first_name, last_name) å’Œ NameID"
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr "設定密碼"
msgid "Settings"
msgstr "設定"
-msgid "Setup a specific Runner automatically"
-msgstr "自動設置特定的執行器"
-
msgid "Share"
msgstr "分享"
@@ -5638,6 +6750,9 @@ msgstr "與æˆå“¡åˆ†äº« <strong>%{sso_label}</strong>,使他們能é€éŽæ‚¨çš„
msgid "Shared Runners"
msgstr "分享的執行器"
+msgid "Shared projects"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr "通éŽé‡è¨­æ­¤å‘½å空間的æµæ°´ç·šåŸ·è¡Œæ™‚間,目å‰ä½¿ç”¨çš„執行時間將會被歸零。"
@@ -5711,12 +6826,18 @@ msgstr "登錄é™åˆ¶"
msgid "Sign-up restrictions"
msgstr "註冊é™åˆ¶"
+msgid "Size"
+msgstr ""
+
msgid "Size and domain settings for static websites"
msgstr "為éœæ…‹ç¶²ç«™çš„大å°èˆ‡ç¶²åŸŸè¨­å®š"
msgid "Slack application"
msgstr "Slack 應用程å¼"
+msgid "Slack integration allows you to interact with GitLab via slash commands in a chat window."
+msgstr ""
+
msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr "速度較慢,但確ä¿é …目工作空間是原始的,因為它為æ¯é …工作從頭開始複製檔案庫"
@@ -5732,14 +6853,20 @@ msgstr "發生了錯誤。"
msgid "Something went wrong on our end. Please try again!"
msgstr "未知錯誤,請é‡è©¦!"
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr "切æ›æŒ‰éˆ•æ™‚發生未知的錯誤"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr "在關閉議題 %{issuable} 時出ç¾å•é¡Œï¼Œè«‹ç¨å¾Œå†è©¦"
-msgid "Something went wrong while fetching assignees list"
-msgstr "抓å–指派者清單時發生錯誤。"
+msgid "Something went wrong while fetching %{listType} list"
+msgstr ""
msgid "Something went wrong while fetching group member contributions"
msgstr "å–得群組æˆå“¡è²¢ç»æ™‚發生錯誤。"
@@ -5792,6 +6919,9 @@ msgstr "最大群組"
msgid "SortOptions|Largest repository"
msgstr "最大儲存庫"
+msgid "SortOptions|Last Contact"
+msgstr ""
+
msgid "SortOptions|Last created"
msgstr "最近建立"
@@ -5822,6 +6952,9 @@ msgstr "加大權é‡"
msgid "SortOptions|Most popular"
msgstr "最å—æ­¡è¿Ž"
+msgid "SortOptions|Most stars"
+msgstr ""
+
msgid "SortOptions|Name"
msgstr "å稱"
@@ -5852,6 +6985,9 @@ msgstr "優先"
msgid "SortOptions|Recent sign in"
msgstr "最近登入"
+msgid "SortOptions|Start date"
+msgstr ""
+
msgid "SortOptions|Start later"
msgstr "ç¨å¾Œé–‹å§‹"
@@ -5882,6 +7018,9 @@ msgstr "垃圾內容與防機器人ä¿è­·"
msgid "Specific Runners"
msgstr "指定執行器"
+msgid "Specify an e-mail address regex pattern to identify default internal users."
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "åœ¨å®‰è£ Runner 時指定以下 URL:"
@@ -5924,6 +7063,9 @@ msgstr "星標項目"
msgid "Start a %{new_merge_request} with these changes"
msgstr "以這些改動建立一個新的 %{new_merge_request} "
+msgid "Start date"
+msgstr ""
+
msgid "Start the Runner!"
msgstr "å•Ÿå‹• Runner!"
@@ -5957,6 +7099,9 @@ msgstr "儲存空間:"
msgid "Subgroups"
msgstr "å­ç¾¤çµ„"
+msgid "Subgroups and projects"
+msgstr ""
+
msgid "Submit as spam"
msgstr "以垃圾訊æ¯æ交"
@@ -5990,6 +7135,9 @@ msgstr "系統標頭與尾端:"
msgid "System metrics (Custom)"
msgstr "系統指標 (自訂)"
+msgid "System metrics (Kubernetes)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] "標籤(%{tag_count})"
@@ -5997,6 +7145,9 @@ msgstr[0] "標籤(%{tag_count})"
msgid "Tags"
msgstr "標籤"
+msgid "Tags feed"
+msgstr ""
+
msgid "Tags:"
msgstr "標籤:"
@@ -6078,6 +7229,12 @@ msgstr "目標分支"
msgid "Team"
msgstr "團隊"
+msgid "Template"
+msgstr ""
+
+msgid "Templates"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr "æœå‹™æ¢æ¬¾å”議和隱ç§æ”¿ç­–"
@@ -6093,6 +7250,9 @@ msgstr "æ„Ÿè¬ï¼è«‹ä¸è¦å†æ¬¡é¡¯ç¤º"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr "GitLab 的進階全域æœå°‹åŠŸèƒ½æ˜¯éžå¸¸å¼·å¤§çš„æœå°‹æœå‹™ã€‚您å¯ä»¥ç«‹åˆ»æœå°‹å…¶ä»–團隊的代碼以幫助您完善自己項目中的代碼。從而é¿å…建立é‡è¤‡çš„程å¼ç¢¼æµªè²»æ™‚間。"
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr "議題追蹤器是添加專案中需è¦æ”¹é€²æˆ–解決å•é¡Œçš„地方。"
@@ -6102,6 +7262,9 @@ msgstr "議題追蹤器是添加專案中需è¦æ”¹é€²æˆ–解決å•é¡Œçš„地方。
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "當需è¦ç›¸äº’ TLS 與外部èªè­‰æœå‹™è¯çµ¡æ™‚,所使用的 X509 憑證。如果留白,使用 HTTPS 連線時伺æœå™¨çš„憑證ä»ç„¶ç‚ºã€Œå·²èªè­‰ã€ã€‚"
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "程å¼é–‹ç™¼éšŽæ®µé¡¯ç¤ºå¾žç¬¬ä¸€æ¬¡æ›´å‹•è¨˜éŒ„到建立åˆä½µè«‹æ±‚的時間。建立第一個åˆä½µè«‹æ±‚後,資料將自動填入。"
@@ -6111,6 +7274,9 @@ msgstr "該階段中的相關事件集åˆã€‚"
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr "該連線將在 %{timeout} 之後超時。å°æ–¼éœ€è¦ä½¿ç”¨æ›´é•·æ™‚間的版本庫,請使用 clone / push 組åˆã€‚"
+msgid "The deployment of this job to %{environmentLink} did not succeed."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr "分支與主幹間的關è¯å·²è¢«åˆªé™¤ã€‚"
@@ -6138,6 +7304,9 @@ msgstr "CI 設定檔的路徑。默èªç‚º <code>.gitlab-ci.yml</code>"
msgid "The phase of the development lifecycle."
msgstr "專案開發週期的å„個階段。"
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "計劃階段顯示從更動記錄被排程至第一個推é€çš„時間。第一次推é€ä¹‹å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"
@@ -6165,6 +7334,9 @@ msgstr "這個專案的檔案庫是空的"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr "該存儲庫必須å¯é€šéŽ <code>http://</code>, <code>https://</code> 或 <code>git://</code>訪å•ã€‚"
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "複閱階段顯示從åˆä½µè«‹æ±‚建立後至被åˆä½µçš„時間。當建立第一個åˆä½µè«‹æ±‚後,資料將自動填入。"
@@ -6177,6 +7349,9 @@ msgstr "此安全憑證使用於執行器簽出專案"
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr "試營é‹æ®µé¡¯ç¤ºå¾žåˆä½µè«‹æ±‚被åˆä½µå¾Œè‡³éƒ¨ç½²ç‡Ÿé‹çš„時間。當第一次部署營é‹å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥"
+msgid "The tabs below will be removed in a future version"
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "測試階段顯示相關åˆä½µè«‹æ±‚çš„æµæ°´ç·šæ‰€èŠ±çš„時間。當第一個æµæ°´ç·šåŸ·è¡Œå®Œç•¢å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"
@@ -6192,6 +7367,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "該階段中æ¯ä¸€å€‹è³‡æ–™é …目所花的時間。"
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
+msgid "The usage ping is disabled, and cannot be configured through this form."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -6201,6 +7382,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "中ä½æ•¸æ˜¯ä¸€å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間,中ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,中ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"
+msgid "There are no archived projects yet"
+msgstr ""
+
msgid "There are no issues to show"
msgstr "沒有å¯ä»¥é¡¯ç¤ºçš„è­°é¡Œ"
@@ -6210,9 +7394,24 @@ msgstr "ç›®å‰é‚„沒有標籤"
msgid "There are no merge requests to show"
msgstr "沒有任何的åˆä½µè«‹æ±‚"
+msgid "There are no projects shared with this group yet"
+msgstr ""
+
+msgid "There are no staged changes"
+msgstr ""
+
+msgid "There are no unstaged changes"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "å­˜å– Git 儲存空間時出ç¾å•é¡Œï¼š"
+msgid "There was an error adding a todo."
+msgstr ""
+
+msgid "There was an error deleting the todo."
+msgstr ""
+
msgid "There was an error loading users activity calendar."
msgstr "讀å–使用者行事曆活動時發生錯誤"
@@ -6249,6 +7448,18 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr "已縮å°æ­¤çœ‹æ¿ç¯„åœ"
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
+msgid "This container registry has been scheduled for deletion."
+msgstr ""
+
+msgid "This date is after the due date, so this epic won't appear in the roadmap."
+msgstr ""
+
+msgid "This date is before the start date, so this epic won't appear in the roadmap."
+msgstr ""
+
msgid "This diff is collapsed."
msgstr "此差異已折疊。"
@@ -6300,9 +7511,30 @@ msgstr "這份任務還沒被觸發"
msgid "This job has not started yet"
msgstr "這份任務還沒有開始執行"
+msgid "This job is an out-of-date deployment to %{environmentLink}."
+msgstr ""
+
+msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
+msgstr ""
+
+msgid "This job is creating a deployment to %{environmentLink}."
+msgstr ""
+
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "這份任務ä½æ–¼ç­‰å¾…狀態,等待 Runner 來執行"
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
+msgid "This job is the most recent deployment to %{link}."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr "這份任務需è¦æ‰‹å‹•åŸ·è¡Œ"
@@ -6312,6 +7544,9 @@ msgstr "這代表在您建立一個空的檔案庫或是匯入一個ç¾å­˜çš„檔
msgid "This merge request is locked."
msgstr "這個åˆä½µè«‹æ±‚已被鎖定。"
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr "當您ä»æ“有未暫存的變更時,此é¸é …將會是åœç”¨çš„"
@@ -6327,15 +7562,30 @@ msgstr "這個專案"
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr "此專案ä¸å±¬æ–¼ä¸€å€‹ç¾¤çµ„,因此ä¸èƒ½ä½¿ç”¨ç¾¤çµ„執行器。"
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr "這個檔案庫"
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr "此原始碼差異無法顯示,因為它太大。"
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr "該使用者沒有身份"
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr "這會刪除自訂指標資料,你確定嗎?"
@@ -6536,12 +7786,24 @@ msgstr "è¦é€£æŽ¥ GitHub 版本庫,您首先需è¦æŽˆæ¬Š GitLab å­˜å–您的 G
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr "è‹¥è¦é€£æŽ¥åˆ°ä¸€å€‹ SVN 版本庫,請åƒé–± %{svn_link}。"
+msgid "To define internal users, first enable new users set to external"
+msgstr ""
+
+msgid "To enable it and see User Cohorts, visit %{application_settings_link_start}application settings%{application_settings_link_end}."
+msgstr ""
+
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgstr ""
+
+msgid "To help improve GitLab, we would like to periodically collect usage information. This can be changed at any time in %{settings_link_start}Settings%{link_end}. %{info_link_start}More Information%{link_end}"
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "è¦åŒ¯å…¥ GitHub 存儲庫,你å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。當你建立你的個人存å–權æ–時,你將需è¦é¸æ“‡<code>檔案庫</code>範åœï¼Œæ‰€ä»¥æˆ‘們將會顯示你公開åŠç§äººçš„檔案庫清單進行匯入。"
@@ -6569,8 +7831,8 @@ msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr "è‹¥è¦é©—證您的 GitLab CI 設定,請å‰å¾€æ‚¨å°ˆæ¡ˆå…§çš„「CI/CD → æµæ°´ç·šã€ï¼Œä¸¦æŒ‰ä¸‹ã€ŒCI Lintã€æŒ‰éˆ•ã€‚"
-msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
-msgstr "è‹¥è¦æª¢è¦–開發è—圖,請在您群組或å­ç¾¤çµ„中的其中一個 Epic 中增加計畫開始或完æˆæ™‚間。åªæœƒé¡¯ç¤ºæœ€è¿‘三個月或接下來三個月的 Epic。"
+msgid "To view the roadmap, add a start or due date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown."
+msgstr ""
msgid "To widen your search, change or remove filters."
msgstr "è‹¥è¦æ“´å¤§æœå°‹ç¯„åœï¼Œè«‹è®Šæ›´æˆ–移除篩é¸å™¨ã€‚"
@@ -6584,6 +7846,9 @@ msgstr "待辦事項"
msgid "Toggle Sidebar"
msgstr "展開 / 收起å´é‚Šæ¬„"
+msgid "Toggle commit description"
+msgstr ""
+
msgid "Toggle discussion"
msgstr "切æ›è¨Žè«–"
@@ -6599,6 +7864,9 @@ msgstr "切æ›ç‹€æ…‹ï¼šé—œé–‰"
msgid "ToggleButton|Toggle Status: ON"
msgstr "切æ›ç‹€æ…‹ï¼šé–‹å•Ÿ"
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr "太多的更改。"
@@ -6624,6 +7892,15 @@ msgid "Track time with quick actions"
msgstr "快速使用時間追蹤工具"
msgid "Trending"
+msgstr "趨勢分æž"
+
+msgid "Trigger"
+msgstr ""
+
+msgid "Trigger pipelines for mirror updates"
+msgstr ""
+
+msgid "Trigger pipelines when branches or tags are updated from the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
msgstr ""
msgid "Trigger this manual action"
@@ -6641,24 +7918,39 @@ msgstr "é–‹å•Ÿæœå‹™å€"
msgid "Twitter"
msgstr "Twitter"
+msgid "Type"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr "無法載入差異。%{button_try_again}"
msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr "因為「%{reason}ã€ï¼Œæ‰€ä»¥ç„¡æ³•è®“ä½ é€éŽ SAML 登入這個群組。"
+msgid "Unable to update this epic at this time."
+msgstr ""
+
+msgid "Undo"
+msgstr ""
+
msgid "Unknown"
msgstr "未知"
msgid "Unlock"
msgstr "解鎖"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "已解鎖"
msgid "Unresolve discussion"
msgstr "é‡æ–°è¨Žè«–"
+msgid "Unstage"
+msgstr ""
+
msgid "Unstage all changes"
msgstr "å–消暫存所有變更"
@@ -6695,9 +7987,15 @@ msgstr "已經是最新"
msgid "Update"
msgstr "æ›´æ–°"
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr "更新您的群組å稱ã€èªªæ˜Žã€é ­åƒèˆ‡å…¶ä»–一般設定。"
+msgid "Updating"
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "å‡ç´šæ‚¨çš„方案以啟用進階全局æœå°‹ã€‚"
@@ -6722,15 +8020,15 @@ msgstr "上傳新檔案"
msgid "Upload file"
msgstr "上傳檔案"
-msgid "Upload new avatar"
-msgstr "上傳大頭貼"
-
msgid "UploadLink|click to upload"
msgstr "點擊上傳"
msgid "Upvotes"
msgstr "讚"
+msgid "Usage ping is not enabled"
+msgstr ""
+
msgid "Usage statistics"
msgstr "使用情形統計資料"
@@ -6746,6 +8044,9 @@ msgstr "使用群組里程碑從多個專案中於åŒä¸€å€‹é‡Œç¨‹ç¢‘管ç†è­°é¡Œ
msgid "Use one line per URI"
msgstr "æ¯å€‹ URL 使用一行"
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "在安è£éŽç¨‹ä¸­ä½¿ç”¨æ­¤è¨»å†Šæ†‘è­‰:"
@@ -6755,9 +8056,12 @@ msgstr "使用全域通知設定"
msgid "Used by members to sign in to your group in GitLab"
msgstr "用來讓æˆå“¡ç™»å…¥æ‚¨åœ¨ GitLab 的群組"
-msgid "User Settings"
+msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
msgstr ""
+msgid "User Settings"
+msgstr "使用者設定"
+
msgid "User and IP Rate Limits"
msgstr "使用者與 IP 速率é™åˆ¶"
@@ -6788,6 +8092,9 @@ msgstr "èªè­‰è³‡è¨Š"
msgid "Verified"
msgstr "已驗證"
+msgid "Version"
+msgstr ""
+
msgid "View epics list"
msgstr "檢視 Epic 列表"
@@ -6801,7 +8108,7 @@ msgid "View issue"
msgstr "檢視議題"
msgid "View it on GitLab"
-msgstr ""
+msgstr "在 GitLab 上檢視"
msgid "View jobs"
msgstr "查看工作"
@@ -7079,18 +8386,21 @@ msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šå»ºç«‹æª”案"
msgid "You can only edit files when you are on a branch"
msgstr "您åªèƒ½åœ¨åˆ†æ”¯ä¸Šç·¨è¼¯æ–‡ä»¶"
+msgid "You can reset runners registration token by pressing a button below."
+msgstr ""
+
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr "您å¯ä»¥é€éŽä½¿ç”¨äº’動模å¼é¸æ“‡ %{use_ours} 或 %{use_theirs} 按鈕ã€æˆ–者是直接編輯檔案來解決åˆä½µè¡çªï¼Œä¸¦å°‡é€™äº›è®Šæ›´æ交到 %{branch_name}。"
+msgid "You can set up jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr "您ä¸èƒ½å¯«å…¥åˆ°å”¯è®€çš„æ¬¡è¦ GitLab Geo 主機。請改用 %{link_to_primary_node}。"
msgid "You cannot write to this read-only GitLab instance."
msgstr "您ä¸èƒ½ä¿®æ”¹é€™å€‹å”¯è®€çš„ GitLab 主機。"
-msgid "You do not have any assigned merge requests"
-msgstr "您沒有被分é…çš„åˆä½µè«‹æ±‚"
-
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr "您沒有權é™ä¾†è¦†è“‹ LDAP 群組åŒæ­¥è¨­å®šã€‚"
@@ -7103,9 +8413,6 @@ msgstr ""
msgid "You have no permissions"
msgstr "你沒有權é™"
-msgid "You have not created any merge requests"
-msgstr "您尚未創建任何åˆä½µè«‹æ±‚"
-
msgid "You have reached your project limit"
msgstr "您已é”到專案數é‡é™åˆ¶"
@@ -7115,9 +8422,6 @@ msgstr "您必須接å—我們的æœå‹™æ¢æ¬¾å’Œéš±ç§æ”¿ç­–æ‰èƒ½è¨»å†Šå¸³æˆ¶"
msgid "You must have maintainer access to force delete a lock"
msgstr "您必須æ“有維護者存å–æ‰èƒ½å¼·åˆ¶ç§»é™¤éŽ–定"
-msgid "You must sign in to star a project"
-msgstr "必須登入æ‰èƒ½æ”¶è—專案"
-
msgid "You need a different license to enable FileLocks feature"
msgstr "您需è¦ä½¿ç”¨ä¸åŒçš„授權以啟用 FileLocks 功能"
@@ -7127,6 +8431,12 @@ msgstr "æ‚¨éœ€è¦ git-lfs 版本 %{min_git_lfs_version} (或更高版本) æ‰èƒ½
msgid "You need permission."
msgstr "需è¦æ¬Šé™æ‰èƒ½é€™éº¼åšã€‚"
+msgid "You will loose all changes you've made to this file. This action cannot be undone."
+msgstr ""
+
+msgid "You will loose all the unstaged changes you've made in this project. This action cannot be undone."
+msgstr ""
+
msgid "You will not get any notifications via email"
msgstr "ä¸æœƒæ”¶åˆ°ä»»ä½•é€šçŸ¥éƒµä»¶"
@@ -7205,16 +8515,15 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr "你的計劃"
+msgid "a deleted user"
+msgstr ""
+
msgid "ago"
msgstr "之å‰"
msgid "among other things"
msgstr "除了其他事情"
-msgid "and 1 fixed vulnerability"
-msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] "å’Œ %d 個已經修復的æ¼æ´ž"
-
msgid "assign yourself"
msgstr "指派給自己"
@@ -7239,29 +8548,54 @@ msgstr "%{linkStartTag}關於 SAST 的更多資訊%{linkEndTag}"
msgid "ciReport|%{namespace} is affected by %{vulnerability}."
msgstr "%{namespace} 被 %{vulnerability} 影響"
-msgid "ciReport|%{packagesString} and "
-msgstr "%{packagesString} 和 "
-
-msgid "ciReport|%{packagesString} and %{lastPackage}"
-msgstr "%{packagesString} 和 %{lastPackage}"
-
msgid "ciReport|%{remainingPackagesCount} more"
msgstr "%{remainingPackagesCount} 更多"
-msgid "ciReport|%{reportName} is loading"
-msgstr "正在載入 %{reportName}"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
+msgstr[0] ""
+
+msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
+msgstr ""
+
+msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
+msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
-msgid "ciReport|%{reportName} resulted in error while loading results"
-msgstr "載入çµæžœæ™‚å›  %{reportName} 而導致錯誤"
+msgid "ciReport|%{reportType} detected no vulnerabilities"
+msgstr ""
-msgid "ciReport|%{type} detected no new security vulnerabilities"
-msgstr "%{type} 未åµæ¸¬åˆ°æ–°çš„安全性æ¼æ´ž"
+msgid "ciReport|%{reportType} is loading"
+msgstr ""
-msgid "ciReport|%{type} detected no security vulnerabilities"
-msgstr "%{type} 未åµæ¸¬åˆ°å®‰å…¨æ€§æ¼æ´ž"
+msgid "ciReport|%{reportType}: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|%{type} detected no vulnerabilities"
-msgstr "%{type} 未åµæ¸¬åˆ°æ¼æ´ž"
+msgid "ciReport|(errors when loading results)"
+msgstr ""
+
+msgid "ciReport|(is loading)"
+msgstr ""
+
+msgid "ciReport|(is loading, errors when loading results)"
+msgstr ""
msgid "ciReport|Class"
msgstr "類別"
@@ -7272,39 +8606,30 @@ msgstr "程å¼ç¢¼å“質"
msgid "ciReport|Confidence"
msgstr "ä¿¡ä»»"
+msgid "ciReport|Container scanning"
+msgstr ""
+
msgid "ciReport|Container scanning detected"
msgstr "åµæ¸¬åˆ°å®¹å™¨æŽƒæ"
msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr "容器掃æåµæ¸¬åœ¨æ‚¨çš„ Docker 映åƒä¸­çš„已知æ¼æ´žã€‚"
-msgid "ciReport|Container scanning is loading"
-msgstr "正在載入容器掃æ"
-
-msgid "ciReport|Container scanning resulted in error while loading results"
-msgstr "當載入çµæžœæ™‚,因容器掃æ而導致錯誤"
+msgid "ciReport|DAST"
+msgstr ""
msgid "ciReport|DAST detected"
msgstr "åµæ¸¬åˆ° DAST"
-msgid "ciReport|DAST is loading"
-msgstr "正在載入 DAST"
-
-msgid "ciReport|DAST resulted in error while loading results"
-msgstr "載入çµæžœæ™‚,因 DAST 而導致錯誤"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
+msgstr ""
-msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
-msgstr "相ä¾æ€§æŽƒæåµæ¸¬æ‚¨åŽŸå§‹ç¢¼ç›¸ä¾æ€§çš„已知æ¼æ´žã€‚"
+msgid "ciReport|Dependency scanning"
+msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr "åµæ¸¬åˆ°ç›¸ä¾æ€§æŽƒæ"
-msgid "ciReport|Dependency scanning is loading"
-msgstr "正在載入相ä¾æ€§æŽƒæ"
-
-msgid "ciReport|Dependency scanning resulted in error while loading results"
-msgstr "當載入çµæžœæ™‚因相ä¾æ€§æŽƒæ而導致錯誤"
-
msgid "ciReport|Description"
msgstr "說明"
@@ -7335,11 +8660,16 @@ msgstr "實例"
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr "了解關於和安全性報告 (Alpha) 互動的更多資訊。"
-msgid "ciReport|Learn more about whitelisting"
-msgstr "了解關於白å單的更多資訊"
+msgid "ciReport|License management detected %d license for the source branch only"
+msgid_plural "ciReport|License management detected %d licenses for the source branch only"
+msgstr[0] ""
-msgid "ciReport|License management detected %{licenseInfo}"
-msgstr "授權管ç†åµæ¸¬åˆ° %{licenseInfo}"
+msgid "ciReport|License management detected %d new license"
+msgid_plural "ciReport|License management detected %d new licenses"
+msgstr[0] ""
+
+msgid "ciReport|License management detected no licenses for the source branch only"
+msgstr ""
msgid "ciReport|License management detected no new licenses"
msgstr "授權管ç†æœªåµæ¸¬åˆ°æ–°æŽˆæ¬Š"
@@ -7368,24 +8698,18 @@ msgstr "效能指標"
msgid "ciReport|Revert dismissal"
msgstr "撤回忽略"
+msgid "ciReport|SAST"
+msgstr ""
+
msgid "ciReport|SAST detected"
msgstr "åµæ¸¬åˆ° SAST"
-msgid "ciReport|SAST is loading"
-msgstr "正在載入 SAST"
-
-msgid "ciReport|SAST resulted in error while loading results"
-msgstr "當載入çµæžœæ™‚,因 SAST 而導致錯誤"
-
msgid "ciReport|Security scanning"
msgstr "安全性掃æ"
msgid "ciReport|Security scanning failed loading any results"
msgstr "安全性掃æ無法載入任何çµæžœ"
-msgid "ciReport|Security scanning is loading"
-msgstr "正在載入安全掃æ"
-
msgid "ciReport|Severity"
msgstr "åš´é‡æ€§"
@@ -7416,12 +8740,13 @@ msgstr "載入相ä¾æ€§æŽƒæ報告時發生錯誤。"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "撤回é§å›žæ™‚發生錯誤。請é‡è©¦ã€‚"
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
-msgstr "未批准的æ¼æ´ž (紅色) å¯ä»¥æ¨™è¨˜ç‚ºå·²æ‰¹å‡†ã€‚"
-
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "å°‡ %{name} 從 %{version} å‡ç´šåˆ° %{fixed}。"
+msgid "ciReport|Used by %{packagesString}"
+msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
+msgstr[0] ""
+
msgid "ciReport|View full report"
msgstr ""
@@ -7434,6 +8759,12 @@ msgstr "æµæ°´ç·šä¸Š"
msgid "command line instructions"
msgstr "指令說明"
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr "連線中"
@@ -7450,17 +8781,6 @@ msgstr[0] "æ—¥"
msgid "deploy token"
msgstr "部署憑證"
-msgid "detected %d fixed vulnerability"
-msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] "åµæ¸¬åˆ° %d 個已修復æ¼æ´ž"
-
-msgid "detected %d new vulnerability"
-msgid_plural "detected %d new vulnerabilities"
-msgstr[0] "åµæ¸¬åˆ° %d 個新æ¼æ´ž"
-
-msgid "detected no vulnerabilities"
-msgstr "未åµæ¸¬åˆ°æ¼æ´ž"
-
msgid "disabled"
msgstr "å·²åœç”¨"
@@ -7479,12 +8799,19 @@ msgstr "為此專案"
msgid "here"
msgstr "這裡"
+msgid "https://your-bitbucket-server"
+msgstr ""
+
msgid "import flow"
msgstr ""
msgid "importing"
msgstr "輸入"
+msgid "instance completed"
+msgid_plural "instances completed"
+msgstr[0] ""
+
msgid "is invalid because there is downstream lock"
msgstr "因為下游鎖定,所以無效"
@@ -7494,9 +8821,15 @@ msgstr "因上游鎖定而無效"
msgid "is not a valid X509 certificate."
msgstr "éžæœ‰æ•ˆ X509 憑證。"
+msgid "issue boards"
+msgstr ""
+
msgid "latest version"
msgstr "最新版本"
+msgid "license management"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr "由 %{path_lock_user_name} 於 %{created_at} 鎖定"
@@ -7525,6 +8858,9 @@ msgstr "å…許å¯ä»¥åˆä½µåˆ°ç›®æ¨™åˆ†æ”¯çš„æˆå“¡æ交"
msgid "mrWidget|An error occured while removing your approval."
msgstr "移除您的批准時發生錯誤。"
+msgid "mrWidget|An error occured while retrieving approval data for this merge request."
+msgstr ""
+
msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
@@ -7573,6 +8909,9 @@ msgstr "Email 補ä¸"
msgid "mrWidget|Failed to load deployment statistics"
msgstr "讀å–部署統計資料時發生錯誤"
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr "如果 %{branch} 分支存在於您的本地檔案庫,你å¯ä»¥æ‰‹å‹•åˆä½µæ­¤åˆä½µè«‹æ±‚,藉由"
@@ -7612,9 +8951,15 @@ msgstr "ä¸éœ€è¦æ‰¹å‡†ï¼›ä½†æ‚¨ä»å¯ä»¥æ‰¹å‡†"
msgid "mrWidget|Open in Web IDE"
msgstr "åœ¨ç¶²é  IDE 中開啟"
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr "本文差異"
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr "é‡æ–°æ•´ç†"
@@ -7636,9 +8981,20 @@ msgstr "移除您的批准"
msgid "mrWidget|Request to merge"
msgstr "請求åˆä½µ"
+msgid "mrWidget|Requires 1 more approval"
+msgid_plural "mrWidget|Requires %d more approvals"
+msgstr[0] ""
+
+msgid "mrWidget|Requires 1 more approval by"
+msgid_plural "mrWidget|Requires %d more approvals by"
+msgstr[0] ""
+
msgid "mrWidget|Resolve conflicts"
msgstr "解決è¡çª"
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr "還原"
@@ -7657,9 +9013,18 @@ msgstr "這些更改將ä¸æœƒåˆä½µåˆ°"
msgid "mrWidget|The changes will be merged into"
msgstr "這些更改將會åˆä½µåˆ°"
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr "來æºåˆ†æ”¯å·²ç¶“被刪除"
+msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
+msgstr ""
+
msgid "mrWidget|The source branch is being removed"
msgstr "來æºåˆ†æ”¯æ­£åœ¨è¢«åˆªé™¤"
@@ -7684,6 +9049,9 @@ msgstr "æ­¤åˆä½µè«‹æ±‚正在被åˆä½µ"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "這個專案已經被打包,無法寫入"
+msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
+msgstr ""
+
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "ä½ å¯ä»¥æ‰‹å‹•åˆä½µé€™å€‹åˆä½µè«‹æ±‚,藉由"
@@ -7702,6 +9070,9 @@ msgstr "進入"
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr "當æµæ°´ç·šå®Œæˆæ™‚將會被自動åˆä½µ"
+msgid "n/a"
+msgstr ""
+
msgid "new merge request"
msgstr "建立åˆä½µè«‹æ±‚"
@@ -7711,6 +9082,10 @@ msgstr "通知信"
msgid "or"
msgstr "或"
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "上層"
@@ -7727,6 +9102,9 @@ msgstr "ç§é‘°ä¸ç¬¦åˆæ†‘證。"
msgid "remaining"
msgstr "剩餘"
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr "刪除截止日期"
@@ -7748,6 +9126,9 @@ msgstr "這份文件"
msgid "to help your contributors communicate effectively!"
msgstr "å”助你的貢ç»è€…進行有效的æºé€šï¼"
+msgid "toggle collapse"
+msgstr ""
+
msgid "username"
msgstr "使用者å稱"
diff --git a/package.json b/package.json
index 975dd2619d7..ac4b4672c86 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"karma-coverage": "BABEL_ENV=coverage karma start --single-run true config/karma.config.js",
"karma-start": "BABEL_ENV=karma karma start config/karma.config.js",
"postinstall": "node ./scripts/frontend/postinstall.js",
- "prettier-staged": "node ./scripts/frontend/prettier.js",
+ "prettier-staged": "node ./scripts/frontend/prettier.js check",
"prettier-staged-save": "node ./scripts/frontend/prettier.js save",
"prettier-all": "node ./scripts/frontend/prettier.js check-all",
"prettier-all-save": "node ./scripts/frontend/prettier.js save-all",
@@ -18,24 +18,25 @@
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
},
"dependencies": {
- "@gitlab-org/gitlab-svgs": "^1.27.0",
- "@gitlab-org/gitlab-ui": "1.0.5",
+ "@babel/core": "^7.1.2",
+ "@babel/plugin-proposal-class-properties": "^7.1.0",
+ "@babel/plugin-proposal-json-strings": "^7.0.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+ "@babel/plugin-syntax-import-meta": "^7.0.0",
+ "@babel/preset-env": "^7.1.0",
+ "@gitlab/svgs": "^1.38.0",
+ "@gitlab/ui": "^1.11.0",
"autosize": "^4.0.0",
"axios": "^0.17.1",
- "babel-core": "^6.26.3",
- "babel-loader": "^7.1.5",
- "babel-plugin-transform-define": "^1.3.0",
- "babel-preset-latest": "^6.24.1",
- "babel-preset-stage-2": "^6.24.1",
- "blackst0ne-mermaid": "^7.1.0-fixed",
- "bootstrap": "~4.1.1",
+ "babel-loader": "^8.0.4",
+ "bootstrap": "4.1.1",
"brace-expansion": "^1.1.8",
"cache-loader": "^1.2.2",
"chart.js": "1.0.2",
"classlist-polyfill": "^1.2.0",
"clipboard": "^1.7.1",
"codesandbox-api": "^0.0.18",
- "compression-webpack-plugin": "^1.1.11",
+ "compression-webpack-plugin": "^2.0.0",
"core-js": "^2.4.1",
"cropper": "^2.3.0",
"css-loader": "^1.0.0",
@@ -55,7 +56,7 @@
"dropzone": "^4.2.0",
"emoji-unicode-version": "^0.2.1",
"exports-loader": "^0.7.0",
- "file-loader": "^1.1.11",
+ "file-loader": "^2.0.0",
"formdata-polyfill": "^3.0.11",
"fuzzaldrin-plus": "^0.5.0",
"glob": "^7.1.2",
@@ -67,10 +68,11 @@
"js-cookie": "^2.1.3",
"jszip": "^3.1.3",
"jszip-utils": "^0.0.2",
- "katex": "^0.8.3",
+ "katex": "^0.9.0",
"marked": "^0.3.12",
- "monaco-editor": "0.13.1",
- "monaco-editor-webpack-plugin": "^1.4.0",
+ "mermaid": "^8.0.0-rc.8",
+ "monaco-editor": "^0.14.3",
+ "monaco-editor-webpack-plugin": "^1.5.4",
"mousetrap": "^1.4.6",
"pikaday": "^1.6.1",
"popper.js": "^1.14.3",
@@ -85,61 +87,59 @@
"sortablejs": "^1.7.0",
"sql.js": "^0.4.0",
"stickyfilljs": "^2.0.5",
- "style-loader": "^0.21.0",
+ "style-loader": "^0.23.0",
"svg4everybody": "2.1.9",
"three": "^0.84.0",
"three-orbit-controls": "^82.1.0",
"three-stl-loader": "^1.0.4",
"timeago.js": "^3.0.2",
"underscore": "^1.9.0",
- "url-loader": "^1.0.1",
+ "url-loader": "^1.1.1",
"visibilityjs": "^1.2.4",
- "vue": "^2.5.16",
- "vue-loader": "^15.2.4",
+ "vue": "^2.5.17",
+ "vue-loader": "^15.4.2",
"vue-resource": "^1.5.0",
"vue-router": "^3.0.1",
- "vue-template-compiler": "^2.5.16",
+ "vue-template-compiler": "^2.5.17",
"vue-virtual-scroll-list": "^1.2.5",
"vuex": "^3.0.1",
- "webpack": "^4.16.0",
- "webpack-bundle-analyzer": "^2.13.1",
- "webpack-cli": "^3.0.8",
+ "webpack": "^4.19.1",
+ "webpack-bundle-analyzer": "^3.0.2",
+ "webpack-cli": "^3.1.0",
"webpack-stats-plugin": "^0.2.1",
"worker-loader": "^2.0.0",
"xterm": "^3.5.0"
},
"devDependencies": {
+ "@gitlab/eslint-config": "^1.1.0",
"axios-mock-adapter": "^1.15.0",
- "babel-eslint": "^8.2.3",
- "babel-plugin-istanbul": "^4.1.6",
- "babel-plugin-rewire": "^1.1.0",
+ "babel-plugin-istanbul": "^5.1.0",
+ "babel-plugin-rewire": "^1.2.0",
"babel-template": "^6.26.0",
"babel-types": "^6.26.0",
"chalk": "^2.4.1",
- "commander": "^2.15.1",
- "eslint": "~4.12.1",
- "eslint-config-airbnb-base": "^12.1.0",
- "eslint-import-resolver-webpack": "^0.10.0",
- "eslint-plugin-filenames": "^1.2.0",
- "eslint-plugin-html": "4.0.3",
- "eslint-plugin-import": "^2.12.0",
- "eslint-plugin-jasmine": "^2.1.0",
- "eslint-plugin-promise": "^3.8.0",
- "eslint-plugin-vue": "^4.5.0",
- "ignore": "^3.3.7",
+ "commander": "^2.18.0",
+ "eslint": "~5.6.0",
+ "eslint-import-resolver-webpack": "^0.10.1",
+ "eslint-plugin-html": "4.0.5",
+ "eslint-plugin-import": "^2.14.0",
+ "eslint-plugin-jasmine": "^2.10.1",
+ "gettext-extractor": "^3.3.2",
+ "gettext-extractor-vue": "^4.0.1",
"istanbul": "^0.4.5",
"jasmine-core": "^2.9.0",
"jasmine-diff": "^0.1.3",
"jasmine-jquery": "^2.1.1",
- "karma": "^2.0.4",
+ "karma": "^3.0.0",
"karma-chrome-launcher": "^2.2.0",
- "karma-coverage-istanbul-reporter": "^1.4.2",
+ "karma-coverage-istanbul-reporter": "^2.0.4",
"karma-jasmine": "^1.1.2",
+ "karma-junit-reporter": "^1.2.0",
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.0-beta.0",
- "nodemon": "^1.18.2",
- "prettier": "1.12.1",
- "webpack-dev-server": "^3.1.4"
+ "nodemon": "^1.18.4",
+ "prettier": "1.14.3",
+ "webpack-dev-server": "^3.1.8"
}
}
diff --git a/public/robots.txt b/public/robots.txt
index 1f9d42f4adc..ea931e1a223 100644
--- a/public/robots.txt
+++ b/public/robots.txt
@@ -21,6 +21,8 @@ Disallow: /groups/new
Disallow: /groups/*/edit
Disallow: /users
Disallow: /help
+# Only specifically allow the Sign In page to avoid very ugly search results
+Allow: /users/sign_in
# Global snippets
User-Agent: *
diff --git a/qa/Dockerfile b/qa/Dockerfile
index abf2184e1e2..9956ced0ef6 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -3,10 +3,20 @@ LABEL maintainer "Grzegorz Bizon <grzegorz@gitlab.com>"
ENV DEBIAN_FRONTEND noninteractive
##
+# Add support for stretch-backports
+#
+RUN echo "deb http://ftp.debian.org/debian stretch-backports main" >> /etc/apt/sources.list
+
+##
# Update APT sources and install some dependencies
#
RUN sed -i "s/httpredir.debian.org/ftp.us.debian.org/" /etc/apt/sources.list
-RUN apt-get update && apt-get install -y wget git unzip xvfb
+RUN apt-get update && apt-get install -y wget unzip xvfb
+
+##
+# Install some packages from backports
+#
+RUN apt-get -y -t stretch-backports install git git-lfs
##
# Install Docker
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 1bc424335f8..8d28fcacc05 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -32,7 +32,7 @@ GEM
diff-lcs (1.3)
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
- ffi (1.9.18)
+ ffi (1.9.25)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (0.9.1)
@@ -77,7 +77,7 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.0)
- rubyzip (1.2.1)
+ rubyzip (1.2.2)
selenium-webdriver (3.8.0)
childprocess (~> 0.5)
rubyzip (~> 1.0)
@@ -103,4 +103,4 @@ DEPENDENCIES
selenium-webdriver (~> 3.8.0)
BUNDLED WITH
- 1.16.1
+ 1.16.4
diff --git a/qa/README.md b/qa/README.md
index be4cf89ebbc..746bd5cf94b 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -35,7 +35,7 @@ following call would login to a local [GDK] instance and run all specs in
`qa/specs/features`:
```
-bin/qa Test::Instance http://localhost:3000
+bin/qa Test::Instance::All http://localhost:3000
```
### Writing tests
@@ -48,14 +48,14 @@ You can also supply specific tests to run as another parameter. For example, to
run the repository-related specs, you can execute:
```
-bin/qa Test::Instance http://localhost qa/specs/features/repository/
+bin/qa Test::Instance::All http://localhost qa/specs/features/repository/
```
Since the arguments would be passed to `rspec`, you could use all `rspec`
options there. For example, passing `--backtrace` and also line number:
```
-bin/qa Test::Instance http://localhost qa/specs/features/project/create_spec.rb:3 --backtrace
+bin/qa Test::Instance::All http://localhost qa/specs/features/project/create_spec.rb:3 --backtrace
```
### Overriding the authenticated user
@@ -67,7 +67,7 @@ If you need to authenticate as a different user, you can provide the
`GITLAB_USERNAME` and `GITLAB_PASSWORD` environment variables:
```
-GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password bin/qa Test::Instance https://gitlab.example.com
+GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password bin/qa Test::Instance::All https://gitlab.example.com
```
If your user doesn't have permission to default sandbox group
@@ -75,16 +75,10 @@ If your user doesn't have permission to default sandbox group
`GITLAB_SANDBOX_NAME`:
```
-GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance https://gitlab.example.com
+GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance::All https://gitlab.example.com
```
-In addition, the `GITLAB_USER_TYPE` can be set to "ldap" to sign in as an LDAP user:
-
-```
-GITLAB_USER_TYPE=ldap GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance https://gitlab.example.com
-```
-
-All [supported environment variables are here](https://gitlab.com/gitlab-org/gitlab-qa#supported-environment-variables).
+All [supported environment variables are here](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#supported-environment-variables).
### Building a Docker image to test
diff --git a/qa/qa.rb b/qa/qa.rb
index 0b48cf58766..c0d5244dbfa 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
$: << File.expand_path(File.dirname(__FILE__))
Encoding.default_external = 'UTF-8'
@@ -14,6 +16,9 @@ module QA
autoload :Browser, 'qa/runtime/browser'
autoload :Env, 'qa/runtime/env'
autoload :Address, 'qa/runtime/address'
+ autoload :Path, 'qa/runtime/path'
+ autoload :Fixtures, 'qa/runtime/fixtures'
+ autoload :Logger, 'qa/runtime/logger'
module API
autoload :Client, 'qa/runtime/api/client'
@@ -31,40 +36,40 @@ module QA
##
# GitLab QA fabrication mechanisms
#
- module Factory
- autoload :Base, 'qa/factory/base'
- autoload :Dependency, 'qa/factory/dependency'
- autoload :Product, 'qa/factory/product'
-
- module Resource
- autoload :Sandbox, 'qa/factory/resource/sandbox'
- autoload :Group, 'qa/factory/resource/group'
- autoload :Issue, 'qa/factory/resource/issue'
- autoload :Project, 'qa/factory/resource/project'
- autoload :MergeRequest, 'qa/factory/resource/merge_request'
- autoload :ProjectImportedFromGithub, 'qa/factory/resource/project_imported_from_github'
- autoload :MergeRequestFromFork, 'qa/factory/resource/merge_request_from_fork'
- autoload :DeployKey, 'qa/factory/resource/deploy_key'
- autoload :Branch, 'qa/factory/resource/branch'
- autoload :SecretVariable, 'qa/factory/resource/secret_variable'
- autoload :Runner, 'qa/factory/resource/runner'
- autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
- autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster'
- autoload :User, 'qa/factory/resource/user'
- autoload :ProjectMilestone, 'qa/factory/resource/project_milestone'
- autoload :Wiki, 'qa/factory/resource/wiki'
- autoload :File, 'qa/factory/resource/file'
- autoload :Fork, 'qa/factory/resource/fork'
- end
+ module Resource
+ autoload :ApiFabricator, 'qa/resource/api_fabricator'
+ autoload :Base, 'qa/resource/base'
+
+ autoload :Sandbox, 'qa/resource/sandbox'
+ autoload :Group, 'qa/resource/group'
+ autoload :Issue, 'qa/resource/issue'
+ autoload :Project, 'qa/resource/project'
+ autoload :Label, 'qa/resource/label'
+ autoload :MergeRequest, 'qa/resource/merge_request'
+ autoload :ProjectImportedFromGithub, 'qa/resource/project_imported_from_github'
+ autoload :MergeRequestFromFork, 'qa/resource/merge_request_from_fork'
+ autoload :DeployKey, 'qa/resource/deploy_key'
+ autoload :DeployToken, 'qa/resource/deploy_token'
+ autoload :Branch, 'qa/resource/branch'
+ autoload :CiVariable, 'qa/resource/ci_variable'
+ autoload :Runner, 'qa/resource/runner'
+ autoload :PersonalAccessToken, 'qa/resource/personal_access_token'
+ autoload :KubernetesCluster, 'qa/resource/kubernetes_cluster'
+ autoload :User, 'qa/resource/user'
+ autoload :ProjectMilestone, 'qa/resource/project_milestone'
+ autoload :Wiki, 'qa/resource/wiki'
+ autoload :File, 'qa/resource/file'
+ autoload :Fork, 'qa/resource/fork'
+ autoload :SSHKey, 'qa/resource/ssh_key'
module Repository
- autoload :Push, 'qa/factory/repository/push'
- autoload :ProjectPush, 'qa/factory/repository/project_push'
- autoload :WikiPush, 'qa/factory/repository/wiki_push'
+ autoload :Push, 'qa/resource/repository/push'
+ autoload :ProjectPush, 'qa/resource/repository/project_push'
+ autoload :WikiPush, 'qa/resource/repository/wiki_push'
end
module Settings
- autoload :HashedStorage, 'qa/factory/settings/hashed_storage'
+ autoload :HashedStorage, 'qa/resource/settings/hashed_storage'
end
end
@@ -77,7 +82,6 @@ module QA
#
autoload :Bootable, 'qa/scenario/bootable'
autoload :Actable, 'qa/scenario/actable'
- autoload :Taggable, 'qa/scenario/taggable'
autoload :Template, 'qa/scenario/template'
##
@@ -85,15 +89,23 @@ module QA
#
module Test
autoload :Instance, 'qa/scenario/test/instance'
+ module Instance
+ autoload :All, 'qa/scenario/test/instance/all'
+ autoload :Smoke, 'qa/scenario/test/instance/smoke'
+ end
module Integration
autoload :Github, 'qa/scenario/test/integration/github'
- autoload :LDAP, 'qa/scenario/test/integration/ldap'
+ autoload :LDAPNoTLS, 'qa/scenario/test/integration/ldap_no_tls'
+ autoload :LDAPTLS, 'qa/scenario/test/integration/ldap_tls'
+ autoload :InstanceSAML, 'qa/scenario/test/integration/instance_saml'
autoload :Kubernetes, 'qa/scenario/test/integration/kubernetes'
autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
+ autoload :ObjectStorage, 'qa/scenario/test/integration/object_storage'
end
module Sanity
+ autoload :Framework, 'qa/scenario/test/sanity/framework'
autoload :Selectors, 'qa/scenario/test/sanity/selectors'
end
end
@@ -112,6 +124,7 @@ module QA
module Main
autoload :Login, 'qa/page/main/login'
+ autoload :Menu, 'qa/page/main/menu'
autoload :OAuth, 'qa/page/main/oauth'
autoload :SignUp, 'qa/page/main/sign_up'
end
@@ -120,13 +133,6 @@ module QA
autoload :Common, 'qa/page/settings/common'
end
- module Menu
- autoload :Main, 'qa/page/menu/main'
- autoload :Side, 'qa/page/menu/side'
- autoload :Admin, 'qa/page/menu/admin'
- autoload :Profile, 'qa/page/menu/profile'
- end
-
module Dashboard
autoload :Projects, 'qa/page/dashboard/projects'
autoload :Groups, 'qa/page/dashboard/groups'
@@ -150,6 +156,7 @@ module QA
autoload :New, 'qa/page/project/new'
autoload :Show, 'qa/page/project/show'
autoload :Activity, 'qa/page/project/activity'
+ autoload :Menu, 'qa/page/project/menu'
module Import
autoload :Github, 'qa/page/project/import/github'
@@ -171,10 +178,12 @@ module QA
autoload :Repository, 'qa/page/project/settings/repository'
autoload :CICD, 'qa/page/project/settings/ci_cd'
autoload :DeployKeys, 'qa/page/project/settings/deploy_keys'
+ autoload :DeployTokens, 'qa/page/project/settings/deploy_tokens'
autoload :ProtectedBranches, 'qa/page/project/settings/protected_branches'
- autoload :SecretVariables, 'qa/page/project/settings/secret_variables'
+ autoload :CiVariables, 'qa/page/project/settings/ci_variables'
autoload :Runners, 'qa/page/project/settings/runners'
autoload :MergeRequest, 'qa/page/project/settings/merge_request'
+ autoload :Members, 'qa/page/project/settings/members'
end
module Issue
@@ -193,6 +202,11 @@ module QA
end
module Operations
+ module Environments
+ autoload :Index, 'qa/page/project/operations/environments/index'
+ autoload :Show, 'qa/page/project/operations/environments/show'
+ end
+
module Kubernetes
autoload :Index, 'qa/page/project/operations/kubernetes/index'
autoload :Add, 'qa/page/project/operations/kubernetes/add'
@@ -206,14 +220,16 @@ module QA
autoload :New, 'qa/page/project/wiki/new'
autoload :Show, 'qa/page/project/wiki/show'
end
- end
- module Shared
- autoload :ClonePanel, 'qa/page/shared/clone_panel'
+ module WebIDE
+ autoload :Edit, 'qa/page/project/web_ide/edit'
+ end
end
module Profile
+ autoload :Menu, 'qa/page/profile/menu'
autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
+ autoload :SSHKeys, 'qa/page/profile/ssh_keys'
end
module Issuable
@@ -224,15 +240,25 @@ module QA
autoload :Banner, 'qa/page/layout/banner'
end
+ module Label
+ autoload :New, 'qa/page/label/new'
+ autoload :Index, 'qa/page/label/index'
+ end
+
module MergeRequest
autoload :New, 'qa/page/merge_request/new'
autoload :Show, 'qa/page/merge_request/show'
end
module Admin
+ autoload :Menu, 'qa/page/admin/menu'
+
module Settings
- autoload :RepositoryStorage, 'qa/page/admin/settings/repository_storage'
- autoload :Main, 'qa/page/admin/settings/main'
+ autoload :Repository, 'qa/page/admin/settings/repository'
+
+ module Component
+ autoload :RepositoryStorage, 'qa/page/admin/settings/component/repository_storage'
+ end
end
end
@@ -245,8 +271,16 @@ module QA
# Classes describing components that are used by several pages.
#
module Component
+ autoload :ClonePanel, 'qa/page/component/clone_panel'
autoload :Dropzone, 'qa/page/component/dropzone'
+ autoload :GroupsFilter, 'qa/page/component/groups_filter'
autoload :Select2, 'qa/page/component/select2'
+ autoload :DropdownFilter, 'qa/page/component/dropdown_filter'
+ autoload :UsersSelect, 'qa/page/component/users_select'
+
+ module Issuable
+ autoload :Common, 'qa/page/component/issuable/common'
+ end
end
end
@@ -276,6 +310,26 @@ module QA
autoload :Config, 'qa/specs/config'
autoload :Runner, 'qa/specs/runner'
end
+
+ ##
+ # Classes that describe the structure of vendor/third party application pages
+ #
+ module Vendor
+ module SAMLIdp
+ module Page
+ autoload :Base, 'qa/vendor/saml_idp/page/base'
+ autoload :Login, 'qa/vendor/saml_idp/page/login'
+ end
+ end
+ end
+
+ # Classes that provide support to other parts of the framework.
+ #
+ module Support
+ module Page
+ autoload :Logging, 'qa/support/page/logging'
+ end
+ end
end
QA::Runtime::Release.extend_autoloads!
diff --git a/qa/qa/factory/base.rb b/qa/qa/factory/base.rb
deleted file mode 100644
index 7a532ce534b..00000000000
--- a/qa/qa/factory/base.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-require 'forwardable'
-
-module QA
- module Factory
- class Base
- extend SingleForwardable
-
- def_delegators :evaluator, :dependency, :dependencies
- def_delegators :evaluator, :product, :attributes
-
- def fabricate!(*_args)
- raise NotImplementedError
- end
-
- def self.fabricate!(*args)
- new.tap do |factory|
- yield factory if block_given?
-
- dependencies.each do |name, signature|
- Factory::Dependency.new(name, factory, signature).build!
- end
-
- factory.fabricate!(*args)
-
- break Factory::Product.populate!(factory)
- end
- end
-
- def self.evaluator
- @evaluator ||= Factory::Base::DSL.new(self)
- end
-
- class DSL
- attr_reader :dependencies, :attributes
-
- def initialize(base)
- @base = base
- @dependencies = {}
- @attributes = {}
- end
-
- def dependency(factory, as:, &block)
- as.tap do |name|
- @base.class_eval { attr_accessor name }
-
- Dependency::Signature.new(factory, block).tap do |signature|
- @dependencies.store(name, signature)
- end
- end
- end
-
- def product(attribute, &block)
- Product::Attribute.new(attribute, block).tap do |signature|
- @attributes.store(attribute, signature)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/dependency.rb b/qa/qa/factory/dependency.rb
deleted file mode 100644
index fc5dc82ce29..00000000000
--- a/qa/qa/factory/dependency.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module QA
- module Factory
- class Dependency
- Signature = Struct.new(:factory, :block)
-
- def initialize(name, factory, signature)
- @name = name
- @factory = factory
- @signature = signature
- end
-
- def overridden?
- !!@factory.public_send(@name)
- end
-
- def build!
- return if overridden?
-
- Builder.new(@signature, @factory).fabricate!.tap do |product|
- @factory.public_send("#{@name}=", product)
- end
- end
-
- class Builder
- def initialize(signature, caller_factory)
- @factory = signature.factory
- @block = signature.block
- @caller_factory = caller_factory
- end
-
- def fabricate!
- @factory.fabricate! do |factory|
- @block&.call(factory, @caller_factory)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/product.rb b/qa/qa/factory/product.rb
deleted file mode 100644
index 996b7f14f61..00000000000
--- a/qa/qa/factory/product.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require 'capybara/dsl'
-
-module QA
- module Factory
- class Product
- include Capybara::DSL
-
- Attribute = Struct.new(:name, :block)
-
- def initialize
- @location = current_url
- end
-
- def visit!
- visit @location
- end
-
- def self.populate!(factory)
- new.tap do |product|
- factory.class.attributes.each_value do |attribute|
- product.instance_exec(factory, attribute.block) do |factory, block|
- value = block.call(factory)
- product.define_singleton_method(attribute.name) { value }
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/repository/project_push.rb b/qa/qa/factory/repository/project_push.rb
deleted file mode 100644
index 4f78098d348..00000000000
--- a/qa/qa/factory/repository/project_push.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-module QA
- module Factory
- module Repository
- class ProjectPush < Factory::Repository::Push
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'project-with-code'
- project.description = 'Project with repository'
- end
-
- product :output do |factory|
- factory.output
- end
-
- product(:project) { |factory| factory.project }
-
- def initialize
- @file_name = 'file.txt'
- @file_content = '# This is test project'
- @commit_message = "This is a test commit"
- @branch_name = 'master'
- @new_branch = true
- end
-
- def repository_uri
- @repository_uri ||= begin
- project.visit!
- Page::Project::Show.act do
- choose_repository_clone_http
- repository_location.uri
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb
deleted file mode 100644
index 5b7ebf6c41f..00000000000
--- a/qa/qa/factory/repository/push.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-require 'pathname'
-
-module QA
- module Factory
- module Repository
- class Push < Factory::Base
- attr_accessor :file_name, :file_content, :commit_message,
- :branch_name, :new_branch, :output, :repository_uri,
- :user
-
- attr_writer :remote_branch
-
- def initialize
- @file_name = 'file.txt'
- @file_content = '# This is test file'
- @commit_message = "This is a test commit"
- @branch_name = 'master'
- @new_branch = true
- @repository_uri = ""
- end
-
- def remote_branch
- @remote_branch ||= branch_name
- end
-
- def directory=(dir)
- raise "Must set directory as a Pathname" unless dir.is_a?(Pathname)
-
- @directory = dir
- end
-
- def fabricate!
- Git::Repository.perform do |repository|
- repository.uri = repository_uri
-
- repository.use_default_credentials
- username = 'GitLab QA'
- email = 'root@gitlab.com'
-
- if user
- repository.username = user.username
- repository.password = user.password
- username = user.name
- email = user.email
- end
-
- repository.clone
- repository.configure_identity(username, email)
-
- if new_branch
- repository.checkout_new_branch(branch_name)
- else
- repository.checkout(branch_name)
- end
-
- if @directory
- @directory.each_child do |f|
- repository.add_file(f.basename, f.read) if f.file?
- end
- else
- repository.add_file(file_name, file_content)
- end
-
- repository.commit(commit_message)
- @output = repository.push_changes("#{branch_name}:#{remote_branch}")
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/repository/wiki_push.rb b/qa/qa/factory/repository/wiki_push.rb
deleted file mode 100644
index fb7c2bb660d..00000000000
--- a/qa/qa/factory/repository/wiki_push.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module QA
- module Factory
- module Repository
- class WikiPush < Factory::Repository::Push
- dependency Factory::Resource::Wiki, as: :wiki do |wiki|
- wiki.title = 'Home'
- wiki.content = '# My First Wiki Content'
- wiki.message = 'Update home'
- end
-
- def initialize
- @file_name = 'Home.md'
- @file_content = '# Welcome to My Wiki'
- @commit_message = 'Updating Home Page'
- @branch_name = 'master'
- @new_branch = false
- end
-
- def repository_uri
- @repository_uri ||= begin
- wiki.visit!
- Page::Project::Wiki::Show.act do
- go_to_clone_repository
- choose_repository_clone_http
- repository_location.uri
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/branch.rb b/qa/qa/factory/resource/branch.rb
deleted file mode 100644
index bc252bf3148..00000000000
--- a/qa/qa/factory/resource/branch.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-module QA
- module Factory
- module Resource
- class Branch < Factory::Base
- attr_accessor :project, :branch_name,
- :allow_to_push, :allow_to_merge, :protected
-
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'protected-branch-project'
- end
-
- def initialize
- @branch_name = 'test/branch'
- @allow_to_push = true
- @allow_to_merge = true
- @protected = false
- end
-
- def fabricate!
- project.visit!
-
- Factory::Repository::ProjectPush.fabricate! do |resource|
- resource.project = project
- resource.file_name = 'kick-off.txt'
- resource.commit_message = 'First commit'
- end
-
- branch = Factory::Repository::ProjectPush.fabricate! do |resource|
- resource.project = project
- resource.file_name = 'README.md'
- resource.commit_message = 'Add readme'
- resource.branch_name = 'master'
- resource.new_branch = false
- resource.remote_branch = @branch_name
- end
-
- Page::Project::Show.perform do |page|
- page.wait { page.has_content?(branch_name) }
- end
-
- # The upcoming process will make it access the Protected Branches page,
- # select the already created branch and protect it according
- # to `allow_to_push` variable.
- return branch unless @protected
-
- Page::Menu::Side.act do
- click_repository_settings
- end
-
- Page::Project::Settings::Repository.perform do |setting|
- setting.expand_protected_branches do |page|
- page.select_branch(branch_name)
-
- if allow_to_push
- page.allow_devs_and_maintainers_to_push
- else
- page.allow_no_one_to_push
- end
-
- if allow_to_merge
- page.allow_devs_and_maintainers_to_merge
- else
- page.allow_no_one_to_merge
- end
-
- page.wait(reload: false) do
- !page.first('.btn-create').disabled?
- end
-
- page.protect_branch
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/deploy_key.rb b/qa/qa/factory/resource/deploy_key.rb
deleted file mode 100644
index ea8a3ad687d..00000000000
--- a/qa/qa/factory/resource/deploy_key.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-module QA
- module Factory
- module Resource
- class DeployKey < Factory::Base
- attr_accessor :title, :key
-
- product :fingerprint do |resource|
- Page::Project::Settings::Repository.act do
- expand_deploy_keys do |key|
- key_offset = key.key_titles.index do |title|
- title.text == resource.title
- end
-
- key.key_fingerprints[key_offset].text
- end
- end
- end
-
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'project-to-deploy'
- project.description = 'project for adding deploy key test'
- end
-
- def fabricate!
- project.visit!
-
- Page::Menu::Side.act do
- click_repository_settings
- end
-
- Page::Project::Settings::Repository.perform do |setting|
- setting.expand_deploy_keys do |page|
- page.fill_key_title(title)
- page.fill_key_value(key)
-
- page.add_key
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/file.rb b/qa/qa/factory/resource/file.rb
deleted file mode 100644
index 2016d10ddae..00000000000
--- a/qa/qa/factory/resource/file.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module QA
- module Factory
- module Resource
- class File < Factory::Base
- attr_accessor :name,
- :content,
- :commit_message
-
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'project-with-new-file'
- end
-
- def initialize
- @name = 'QA Test - File name'
- @content = 'QA Test - File content'
- @commit_message = 'QA Test - Commit message'
- end
-
- def fabricate!
- project.visit!
-
- Page::Project::Show.act { go_to_new_file! }
-
- Page::File::Form.perform do |page|
- page.add_name(@name)
- page.add_content(@content)
- page.add_commit_message(@commit_message)
- page.commit_changes
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/fork.rb b/qa/qa/factory/resource/fork.rb
deleted file mode 100644
index 1d0c76a3d30..00000000000
--- a/qa/qa/factory/resource/fork.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module QA
- module Factory
- module Resource
- class Fork < Factory::Base
- dependency Factory::Repository::ProjectPush, as: :push
-
- dependency Factory::Resource::User, as: :user
-
- product(:user) { |factory| factory.user }
-
- def fabricate!
- push.project.visit!
- Page::Project::Show.act { fork_project }
-
- Page::Project::Fork::New.perform do |fork_new|
- fork_new.choose_namespace(user.name)
- end
-
- Page::Layout::Banner.act { has_notice?('The project was successfully forked.') }
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/group.rb b/qa/qa/factory/resource/group.rb
deleted file mode 100644
index 531fccd2ad8..00000000000
--- a/qa/qa/factory/resource/group.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-module QA
- module Factory
- module Resource
- class Group < Factory::Base
- attr_writer :path, :description
-
- dependency Factory::Resource::Sandbox, as: :sandbox
-
- def initialize
- @path = Runtime::Namespace.name
- @description = "QA test run at #{Runtime::Namespace.time}"
- end
-
- def fabricate!
- sandbox.visit!
-
- Page::Group::Show.perform do |page|
- if page.has_subgroup?(@path)
- page.go_to_subgroup(@path)
- else
- page.go_to_new_subgroup
-
- Page::Group::New.perform do |group|
- group.set_path(@path)
- group.set_description(@description)
- group.set_visibility('Public')
- group.create
- end
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/issue.rb b/qa/qa/factory/resource/issue.rb
deleted file mode 100644
index 95f48e20b3e..00000000000
--- a/qa/qa/factory/resource/issue.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module QA
- module Factory
- module Resource
- class Issue < Factory::Base
- attr_writer :title, :description, :project
-
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'project-for-issues'
- project.description = 'project for adding issues'
- end
-
- product :title do
- Page::Project::Issue::Show.act { issue_title }
- end
-
- def fabricate!
- project.visit!
-
- Page::Project::Show.act do
- go_to_new_issue
- end
-
- Page::Project::Issue::New.perform do |page|
- page.add_title(@title)
- page.add_description(@description)
- page.create_new_issue
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/kubernetes_cluster.rb b/qa/qa/factory/resource/kubernetes_cluster.rb
deleted file mode 100644
index ef2ea72b170..00000000000
--- a/qa/qa/factory/resource/kubernetes_cluster.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-require 'securerandom'
-
-module QA
- module Factory
- module Resource
- class KubernetesCluster < Factory::Base
- attr_writer :project, :cluster,
- :install_helm_tiller, :install_ingress, :install_prometheus, :install_runner
-
- product :ingress_ip do
- Page::Project::Operations::Kubernetes::Show.perform do |page|
- page.ingress_ip
- end
- end
-
- def fabricate!
- @project.visit!
-
- Page::Menu::Side.act { click_operations_kubernetes }
-
- Page::Project::Operations::Kubernetes::Index.perform do |page|
- page.add_kubernetes_cluster
- end
-
- Page::Project::Operations::Kubernetes::Add.perform do |page|
- page.add_existing_cluster
- end
-
- Page::Project::Operations::Kubernetes::AddExisting.perform do |page|
- page.set_cluster_name(@cluster.cluster_name)
- page.set_api_url(@cluster.api_url)
- page.set_ca_certificate(@cluster.ca_certificate)
- page.set_token(@cluster.token)
- page.add_cluster!
- end
-
- if @install_helm_tiller
- Page::Project::Operations::Kubernetes::Show.perform do |page|
- # We must wait a few seconds for permissions to be setup correctly for new cluster
- sleep 10
-
- # Helm must be installed before everything else
- page.install!(:helm)
- page.await_installed(:helm)
-
- page.install!(:ingress) if @install_ingress
- page.install!(:prometheus) if @install_prometheus
- page.install!(:runner) if @install_runner
-
- page.await_installed(:ingress) if @install_ingress
- page.await_installed(:prometheus) if @install_prometheus
- page.await_installed(:runner) if @install_runner
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb
deleted file mode 100644
index ddb62bd0a68..00000000000
--- a/qa/qa/factory/resource/merge_request.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require 'securerandom'
-
-module QA
- module Factory
- module Resource
- class MergeRequest < Factory::Base
- attr_accessor :title,
- :description,
- :source_branch,
- :target_branch,
- :assignee,
- :milestone,
- :labels
-
- product :project do |factory|
- factory.project
- end
-
- product :source_branch do |factory|
- factory.source_branch
- end
-
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'project-with-merge-request'
- end
-
- dependency Factory::Repository::ProjectPush, as: :target do |push, factory|
- factory.project.visit!
- push.project = factory.project
- push.branch_name = 'master'
- push.remote_branch = factory.target_branch
- end
-
- dependency Factory::Repository::ProjectPush, as: :source do |push, factory|
- push.project = factory.project
- push.branch_name = factory.target_branch
- push.remote_branch = factory.source_branch
- push.file_name = "added_file.txt"
- push.file_content = "File Added"
- end
-
- def initialize
- @title = 'QA test - merge request'
- @description = 'This is a test merge request'
- @source_branch = "qa-test-feature-#{SecureRandom.hex(8)}"
- @target_branch = "master"
- @assignee = nil
- @milestone = nil
- @labels = []
- end
-
- def fabricate!
- project.visit!
- Page::Project::Show.act { new_merge_request }
- Page::MergeRequest::New.perform do |page|
- page.fill_title(@title)
- page.fill_description(@description)
- page.choose_milestone(@milestone) if @milestone
- page.create_merge_request
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/merge_request_from_fork.rb b/qa/qa/factory/resource/merge_request_from_fork.rb
deleted file mode 100644
index 6caaf65f673..00000000000
--- a/qa/qa/factory/resource/merge_request_from_fork.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module QA
- module Factory
- module Resource
- class MergeRequestFromFork < MergeRequest
- attr_accessor :fork_branch
-
- dependency Factory::Resource::Fork, as: :fork
-
- dependency Factory::Repository::ProjectPush, as: :push do |push, factory|
- push.project = factory.fork
- push.branch_name = factory.fork_branch
- push.file_name = 'file2.txt'
- push.user = factory.fork.user
- end
-
- def fabricate!
- fork.visit!
- Page::Project::Show.act { new_merge_request }
- Page::MergeRequest::New.act { create_merge_request }
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/personal_access_token.rb b/qa/qa/factory/resource/personal_access_token.rb
deleted file mode 100644
index 514e3615d18..00000000000
--- a/qa/qa/factory/resource/personal_access_token.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module QA
- module Factory
- module Resource
- ##
- # Create a personal access token that can be used by the api
- #
- class PersonalAccessToken < Factory::Base
- attr_accessor :name
-
- product :access_token do
- Page::Profile::PersonalAccessTokens.act { created_access_token }
- end
-
- def fabricate!
- Page::Menu::Main.act { go_to_profile_settings }
- Page::Menu::Profile.act { click_access_tokens }
-
- Page::Profile::PersonalAccessTokens.perform do |page|
- page.fill_token_name(name || 'api-test-token')
- page.check_api
- page.create_token
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/project.rb b/qa/qa/factory/resource/project.rb
deleted file mode 100644
index 7fff22b5468..00000000000
--- a/qa/qa/factory/resource/project.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require 'securerandom'
-
-module QA
- module Factory
- module Resource
- class Project < Factory::Base
- attr_writer :description
- attr_reader :name
-
- dependency Factory::Resource::Group, as: :group
-
- product :name do |factory|
- factory.name
- end
-
- product :repository_ssh_location do
- Page::Project::Show.act do
- choose_repository_clone_ssh
- repository_location
- end
- end
-
- def initialize
- @description = 'My awesome project'
- end
-
- def name=(raw_name)
- @name = "#{raw_name}-#{SecureRandom.hex(8)}"
- end
-
- def fabricate!
- group.visit!
-
- Page::Group::Show.act { go_to_new_project }
-
- Page::Project::New.perform do |page|
- page.choose_test_namespace
- page.choose_name(@name)
- page.add_description(@description)
- page.set_visibility('Public')
- page.create_new_project
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/project_imported_from_github.rb b/qa/qa/factory/resource/project_imported_from_github.rb
deleted file mode 100644
index df2a3340d60..00000000000
--- a/qa/qa/factory/resource/project_imported_from_github.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require 'securerandom'
-
-module QA
- module Factory
- module Resource
- class ProjectImportedFromGithub < Resource::Project
- attr_writer :personal_access_token, :github_repository_path
-
- dependency Factory::Resource::Group, as: :group
-
- product :name do |factory|
- factory.name
- end
-
- def fabricate!
- group.visit!
-
- Page::Group::Show.act { go_to_new_project }
-
- Page::Project::New.perform do |page|
- page.go_to_import_project
- end
-
- Page::Project::New.perform do |page|
- page.go_to_github_import
- end
-
- Page::Project::Import::Github.perform do |page|
- page.add_personal_access_token(@personal_access_token)
- page.list_repos
- page.import!(@github_repository_path, @name)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/project_milestone.rb b/qa/qa/factory/resource/project_milestone.rb
deleted file mode 100644
index 47a5e74204f..00000000000
--- a/qa/qa/factory/resource/project_milestone.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-module QA
- module Factory
- module Resource
- class ProjectMilestone < Factory::Base
- attr_accessor :description
- attr_reader :title
-
- dependency Factory::Resource::Project, as: :project
-
- product(:title) { |factory| factory.title }
-
- def title=(title)
- @title = "#{title}-#{SecureRandom.hex(4)}"
- @description = 'A milestone'
- end
-
- def fabricate!
- project.visit!
-
- Page::Menu::Side.act do
- click_issues
- click_milestones
- end
-
- Page::Project::Milestone::Index.act { click_new_milestone }
-
- Page::Project::Milestone::New.perform do |milestone_new|
- milestone_new.set_title(@title)
- milestone_new.set_description(@description)
- milestone_new.create_new_milestone
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/runner.rb b/qa/qa/factory/resource/runner.rb
deleted file mode 100644
index 03b69eb1bdf..00000000000
--- a/qa/qa/factory/resource/runner.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require 'securerandom'
-
-module QA
- module Factory
- module Resource
- class Runner < Factory::Base
- attr_writer :name, :tags, :image
-
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'project-with-ci-cd'
- project.description = 'Project with CI/CD Pipelines'
- end
-
- def name
- @name || "qa-runner-#{SecureRandom.hex(4)}"
- end
-
- def tags
- @tags || %w[qa e2e]
- end
-
- def image
- @image || 'gitlab/gitlab-runner:alpine'
- end
-
- def fabricate!
- project.visit!
-
- Page::Menu::Side.act { click_ci_cd_settings }
-
- Service::Runner.new(name).tap do |runner|
- Page::Project::Settings::CICD.perform do |settings|
- settings.expand_runners_settings do |runners|
- runner.pull
- runner.token = runners.registration_token
- runner.address = runners.coordinator_address
- runner.tags = tags
- runner.image = image
- runner.register!
- end
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/sandbox.rb b/qa/qa/factory/resource/sandbox.rb
deleted file mode 100644
index 4f6039f300f..00000000000
--- a/qa/qa/factory/resource/sandbox.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module QA
- module Factory
- module Resource
- ##
- # Ensure we're in our sandbox namespace, either by navigating to it or by
- # creating it if it doesn't yet exist.
- #
- class Sandbox < Factory::Base
- def initialize
- @name = Runtime::Namespace.sandbox_name
- end
-
- def fabricate!
- Page::Menu::Main.act { go_to_groups }
-
- Page::Dashboard::Groups.perform do |page|
- if page.has_group?(@name)
- page.go_to_group(@name)
- else
- page.go_to_new_group
-
- Page::Group::New.perform do |group|
- group.set_path(@name)
- group.set_description('GitLab QA Sandbox Group')
- group.set_visibility('Public')
- group.create
- end
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/secret_variable.rb b/qa/qa/factory/resource/secret_variable.rb
deleted file mode 100644
index 12a830da116..00000000000
--- a/qa/qa/factory/resource/secret_variable.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-module QA
- module Factory
- module Resource
- class SecretVariable < Factory::Base
- attr_accessor :key, :value
-
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'project-with-secret-variables'
- project.description = 'project for adding secret variable test'
- end
-
- def fabricate!
- project.visit!
-
- Page::Menu::Side.act { click_ci_cd_settings }
-
- Page::Project::Settings::CICD.perform do |setting|
- setting.expand_secret_variables do |page|
- page.fill_variable(key, value)
-
- page.save_variables
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/user.rb b/qa/qa/factory/resource/user.rb
deleted file mode 100644
index e08df9e0cd0..00000000000
--- a/qa/qa/factory/resource/user.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'securerandom'
-
-module QA
- module Factory
- module Resource
- class User < Factory::Base
- attr_accessor :name, :username, :email, :password
-
- def initialize
- @name = "name-#{SecureRandom.hex(8)}"
- @username = "username-#{SecureRandom.hex(8)}"
- @email = "mail#{SecureRandom.hex(8)}@mail.com"
- @password = 'password'
- end
-
- product(:name) { |factory| factory.name }
-
- product(:username) { |factory| factory.username }
-
- product(:email) { |factory| factory.email }
-
- product(:password) { |factory| factory.password }
-
- def fabricate!
- Page::Menu::Main.act { sign_out }
- Page::Main::Login.act { switch_to_register_tab }
- Page::Main::SignUp.perform do |page|
- page.sign_up!(name: name, username: username, email: email, password: password)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/resource/wiki.rb b/qa/qa/factory/resource/wiki.rb
deleted file mode 100644
index cc200a512d5..00000000000
--- a/qa/qa/factory/resource/wiki.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module QA
- module Factory
- module Resource
- class Wiki < Factory::Base
- attr_accessor :title, :content, :message
-
- dependency Factory::Resource::Project, as: :project do |project|
- project.name = 'project-for-wikis'
- project.description = 'project for adding wikis'
- end
-
- def fabricate!
- Page::Menu::Side.act { click_wiki }
- Page::Project::Wiki::New.perform do |page|
- page.go_to_create_first_page
- page.set_title(@title)
- page.set_content(@content)
- page.set_message(@message)
- page.create_new_page
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/factory/settings/hashed_storage.rb b/qa/qa/factory/settings/hashed_storage.rb
deleted file mode 100644
index c69ebed3c6b..00000000000
--- a/qa/qa/factory/settings/hashed_storage.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module QA
- module Factory
- module Settings
- class HashedStorage < Factory::Base
- def fabricate!(*traits)
- raise ArgumentError unless traits.include?(:enabled)
-
- Page::Main::Login.act { sign_in_using_credentials }
- Page::Menu::Main.act { go_to_admin_area }
- Page::Menu::Admin.act { go_to_settings }
-
- Page::Admin::Settings::Main.perform do |setting|
- setting.expand_repository_storage do |page|
- page.enable_hashed_storage
- page.save_settings
- end
- end
-
- QA::Page::Menu::Main.act { sign_out }
- end
- end
- end
- end
-end
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 3df6db05970..7f959441dac 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -1,12 +1,26 @@
+# frozen_string_literal: true
+
require 'cgi'
require 'uri'
require 'open3'
+require 'fileutils'
+require 'tmpdir'
module QA
module Git
class Repository
include Scenario::Actable
+ attr_writer :password
+ attr_accessor :env_vars
+
+ def initialize
+ # We set HOME to the current working directory (which is a
+ # temporary directory created in .perform()) so the temporarily dropped
+ # .netrc can be utilised
+ self.env_vars = [%Q{HOME="#{File.dirname(netrc_file_path)}"}]
+ end
+
def self.perform(*args)
Dir.mktmpdir do |dir|
Dir.chdir(dir) { super }
@@ -17,31 +31,27 @@ module QA
@uri = URI(address)
end
- def username=(name)
- @username = name
- @uri.user = name
- end
-
- def password=(pass)
- @password = pass
- @uri.password = CGI.escape(pass).gsub('+', '%20')
+ def username=(username)
+ @username = username
+ @uri.user = username
end
def use_default_credentials
- self.username = Runtime::User.name
- self.password = Runtime::User.password
+ self.username, self.password = default_credentials
+
+ add_credentials_to_netrc unless ssh_key_set?
end
def clone(opts = '')
- run_and_redact_credentials("git clone #{opts} #{@uri} ./")
+ run("git clone #{opts} #{uri} ./")
end
def checkout(branch_name)
- `git checkout "#{branch_name}"`
+ run(%Q{git checkout "#{branch_name}"})
end
def checkout_new_branch(branch_name)
- `git checkout -b "#{branch_name}"`
+ run(%Q{git checkout -b "#{branch_name}"})
end
def shallow_clone
@@ -49,8 +59,10 @@ module QA
end
def configure_identity(name, email)
- `git config user.name #{name}`
- `git config user.email #{email}`
+ run(%Q{git config user.name #{name}})
+ run(%Q{git config user.email #{email}})
+
+ add_credentials_to_netrc
end
def commit_file(name, contents, message)
@@ -61,29 +73,121 @@ module QA
def add_file(name, contents)
::File.write(name, contents)
- `git add #{name}`
+ run(%Q{git add #{name}})
end
def commit(message)
- `git commit -m "#{message}"`
+ run(%Q{git commit -m "#{message}"})
end
def push_changes(branch = 'master')
- output, _ = run_and_redact_credentials("git push #{@uri} #{branch}")
-
- output
+ run("git push #{uri} #{branch}")
end
def commits
- `git log --oneline`.split("\n")
+ run('git log --oneline').split("\n")
+ end
+
+ def use_ssh_key(key)
+ @private_key_file = Tempfile.new("id_#{SecureRandom.hex(8)}")
+ File.binwrite(private_key_file, key.private_key)
+ File.chmod(0700, private_key_file)
+
+ @known_hosts_file = Tempfile.new("known_hosts_#{SecureRandom.hex(8)}")
+ keyscan_params = ['-H']
+ keyscan_params << "-p #{uri.port}" if uri.port
+ keyscan_params << uri.host
+ run("ssh-keyscan #{keyscan_params.join(' ')} >> #{known_hosts_file.path}")
+
+ self.env_vars << %Q{GIT_SSH_COMMAND="ssh -i #{private_key_file.path} -o UserKnownHostsFile=#{known_hosts_file.path}"}
+ end
+
+ def delete_ssh_key
+ return unless ssh_key_set?
+
+ private_key_file.close(true)
+ known_hosts_file.close(true)
+ end
+
+ def push_with_git_protocol(version, file_name, file_content, commit_message = 'Initial commit')
+ self.git_protocol = version
+ add_file(file_name, file_content)
+ commit(commit_message)
+ push_changes
+
+ fetch_supported_git_protocol
+ end
+
+ def git_protocol=(value)
+ raise ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2" unless %w[0 1 2].include?(value.to_s)
+
+ run("git config protocol.version #{value}")
+ end
+
+ def fetch_supported_git_protocol
+ # ls-remote is one command known to respond to Git protocol v2 so we use
+ # it to get output including the version reported via Git tracing
+ output = run("git ls-remote #{uri}", "GIT_TRACE_PACKET=1")
+ output[/git< version (\d+)/, 1] || 'unknown'
end
private
- # Since the remote URL contains the credentials, and git occasionally
- # outputs the URL. Note that stderr is redirected to stdout.
- def run_and_redact_credentials(command)
- Open3.capture2("#{command} 2>&1 | sed -E 's#://[^@]+@#://****@#g'")
+ attr_reader :uri, :username, :password, :known_hosts_file, :private_key_file
+
+ def ssh_key_set?
+ !private_key_file.nil?
+ end
+
+ def run(command_str, *extra_env)
+ command = [env_vars, *extra_env, command_str, '2>&1'].compact.join(' ')
+ Runtime::Logger.debug "Git: command=[#{command}]"
+
+ output, _ = Open3.capture2(command)
+ output = output.chomp.gsub(/\s+$/, '')
+ Runtime::Logger.debug "Git: output=[#{output}]"
+
+ output
+ end
+
+ def default_credentials
+ if ::QA::Runtime::User.ldap_user?
+ [Runtime::User.ldap_username, Runtime::User.ldap_password]
+ else
+ [Runtime::User.username, Runtime::User.password]
+ end
+ end
+
+ def tmp_netrc_directory
+ @tmp_netrc_directory ||= File.join(Dir.tmpdir, "qa-netrc-credentials", $$.to_s)
+ end
+
+ def netrc_file_path
+ @netrc_file_path ||= File.join(tmp_netrc_directory, '.netrc')
+ end
+
+ def netrc_content
+ "machine #{uri.host} login #{username} password #{password}"
+ end
+
+ def netrc_already_contains_content?
+ File.exist?(netrc_file_path) &&
+ File.readlines(netrc_file_path).grep(/^#{netrc_content}$/).any?
+ end
+
+ def add_credentials_to_netrc
+ # Despite libcurl supporting a custom .netrc location through the
+ # CURLOPT_NETRC_FILE environment variable, git does not support it :(
+ # Info: https://curl.haxx.se/libcurl/c/CURLOPT_NETRC_FILE.html
+ #
+ # This will create a .netrc in the correct working directory, which is
+ # a temporary directory created in .perform()
+ #
+ return if netrc_already_contains_content?
+
+ FileUtils.mkdir_p(tmp_netrc_directory)
+ File.open(netrc_file_path, 'a') { |file| file.puts(netrc_content) }
+ File.chmod(0600, netrc_file_path)
end
end
end
diff --git a/qa/qa/page/README.md b/qa/qa/page/README.md
index 2dbc59846e7..d0de33892c4 100644
--- a/qa/qa/page/README.md
+++ b/qa/qa/page/README.md
@@ -70,15 +70,15 @@ module Page
module Main
class Login < Page::Base
view 'app/views/devise/passwords/edit.html.haml' do
- element :password_field, 'password_field :password'
- element :password_confirmation, 'password_field :password_confirmation'
- element :change_password_button, 'submit "Change your password"'
+ element :password_field
+ element :password_confirmation
+ element :change_password_button
end
view 'app/views/devise/sessions/_new_base.html.haml' do
- element :login_field, 'text_field :login'
- element :password_field, 'password_field :password'
- element :sign_in_button, 'submit "Sign in"'
+ element :login_field
+ element :password_field
+ element :sign_in_button
end
# ...
@@ -86,20 +86,32 @@ module Page
end
```
-It is possible to use `element` DSL method without value, with a String value
-or with a Regexp.
+The `view` DSL method declares the filename of the view where an
+`element` is implemented.
+
+The `element` DSL method in turn declares an element for which a corresponding
+`qa-element-name-dasherized` CSS class need to be added to the view file.
+
+You can also define a value (String or Regexp) to match to the actual view
+code but **this is deprecated** in favor of the above method for two reasons:
+
+- Consistency: there is only one way to define an element
+- Separation of concerns: QA uses dedicated CSS classes instead of reusing code
+ or classes used by other components (e.g. `js-*` classes etc.)
```ruby
view 'app/views/my/view.html.haml' do
+ # Implicitly require `.qa-logout-button` CSS class to be present in the view
+ element :logout_button
+
+ ## This is deprecated and forbidden by the `QA/ElementWithPattern` RuboCop cop.
# Require `f.submit "Sign in"` to be present in `my/view.html.haml
- element :my_button, 'f.submit "Sign in"'
+ element :my_button, 'f.submit "Sign in"' # rubocop:disable QA/ElementWithPattern
+ ## This is deprecated and forbidden by the `QA/ElementWithPattern` RuboCop cop.
# Match every line in `my/view.html.haml` against
# `/link_to .* "My Profile"/` regexp.
- element :profile_link, /link_to .* "My Profile"/
-
- # Implicitly require `.qa-logout-button` CSS class to be present in the view
- element :logout_button
+ element :profile_link, /link_to .* "My Profile"/ # rubocop:disable QA/ElementWithPattern
end
```
@@ -119,4 +131,4 @@ If you need more information, ask for help on `#quality` channel on Slack
(internal, GitLab Team only).
If you are not a Team Member, and you still need help to contribute, please
-open an issue in GitLab QA issue tracker.
+open an issue in GitLab CE issue tracker with the `~QA` label.
diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb
new file mode 100644
index 00000000000..e8c7d274966
--- /dev/null
+++ b/qa/qa/page/admin/menu.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Admin
+ class Menu < Page::Base
+ view 'app/views/layouts/nav/sidebar/_admin.html.haml' do
+ element :admin_sidebar
+ element :admin_sidebar_submenu
+ element :admin_settings_item
+ element :admin_settings_repository_item
+ end
+
+ def go_to_repository_settings
+ hover_settings do
+ within_submenu do
+ click_element :admin_settings_repository_item
+ end
+ end
+ end
+
+ private
+
+ def hover_settings
+ within_sidebar do
+ scroll_to_element(:admin_settings_item)
+ find_element(:admin_settings_item).hover
+
+ yield
+ end
+ end
+
+ def within_sidebar
+ within_element(:admin_sidebar) do
+ yield
+ end
+ end
+
+ def within_submenu
+ within_element(:admin_sidebar_submenu) do
+ yield
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/admin/settings/component/repository_storage.rb b/qa/qa/page/admin/settings/component/repository_storage.rb
new file mode 100644
index 00000000000..2ed0cd73aa3
--- /dev/null
+++ b/qa/qa/page/admin/settings/component/repository_storage.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Admin
+ module Settings
+ module Component
+ class RepositoryStorage < Page::Base
+ view 'app/views/admin/application_settings/_repository_storage.html.haml' do
+ element :hashed_storage_checkbox
+ element :save_changes_button
+ end
+
+ def enable_hashed_storage
+ check_element :hashed_storage_checkbox
+ end
+
+ def save_settings
+ click_element :save_changes_button
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/admin/settings/main.rb b/qa/qa/page/admin/settings/main.rb
deleted file mode 100644
index db3387b4557..00000000000
--- a/qa/qa/page/admin/settings/main.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module QA
- module Page
- module Admin
- module Settings
- class Main < Page::Base
- include QA::Page::Settings::Common
-
- view 'app/views/admin/application_settings/show.html.haml' do
- element :repository_storage_settings
- end
-
- def expand_repository_storage(&block)
- expand_section(:repository_storage_settings) do
- RepositoryStorage.perform(&block)
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/admin/settings/repository.rb b/qa/qa/page/admin/settings/repository.rb
new file mode 100644
index 00000000000..b7f1deb21bd
--- /dev/null
+++ b/qa/qa/page/admin/settings/repository.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Admin
+ module Settings
+ class Repository < Page::Base
+ include QA::Page::Settings::Common
+
+ view 'app/views/admin/application_settings/repository.html.haml' do
+ element :repository_storage_settings
+ end
+
+ def expand_repository_storage(&block)
+ expand_section(:repository_storage_settings) do
+ Component::RepositoryStorage.perform(&block)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/admin/settings/repository_storage.rb b/qa/qa/page/admin/settings/repository_storage.rb
deleted file mode 100644
index 68dd23a41e1..00000000000
--- a/qa/qa/page/admin/settings/repository_storage.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module QA
- module Page
- module Admin
- module Settings
- class RepositoryStorage < Page::Base
- view 'app/views/admin/application_settings/_repository_storage.html.haml' do
- element :submit, "submit 'Save changes'"
- element :hashed_storage,
- 'Use hashed storage paths for newly created and renamed projects'
- end
-
- def enable_hashed_storage
- check 'Use hashed storage paths for newly created and renamed projects'
- end
-
- def save_settings
- click_button 'Save changes'
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 30e35bf7abb..91e229c4c8c 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -1,12 +1,17 @@
+# frozen_string_literal: true
+
require 'capybara/dsl'
module QA
module Page
class Base
+ prepend Support::Page::Logging if Runtime::Env.debug?
include Capybara::DSL
include Scenario::Actable
extend SingleForwardable
+ ElementNotFound = Class.new(RuntimeError)
+
def_delegators :evaluator, :view, :views
def refresh
@@ -28,6 +33,21 @@ module QA
false
end
+ def with_retry(max_attempts: 3, reload: false)
+ attempts = 0
+
+ while attempts < max_attempts
+ result = yield
+ return result if result
+
+ refresh if reload
+
+ attempts += 1
+ end
+
+ false
+ end
+
def scroll_to(selector, text: nil)
page.execute_script <<~JS
var elements = Array.from(document.querySelectorAll('#{selector}'));
@@ -68,6 +88,10 @@ module QA
all(element_selector_css(name))
end
+ def check_element(name)
+ find_element(name).set(true)
+ end
+
def click_element(name)
find_element(name).click
end
@@ -76,12 +100,20 @@ module QA
find_element(name).set(content)
end
+ def has_element?(name)
+ has_css?(element_selector_css(name))
+ end
+
def within_element(name)
page.within(element_selector_css(name)) do
yield
end
end
+ def scroll_to_element(name, *args)
+ scroll_to(element_selector_css(name), *args)
+ end
+
def element_selector_css(name)
Page::Element.new(name).selector_css
end
diff --git a/qa/qa/page/component/clone_panel.rb b/qa/qa/page/component/clone_panel.rb
new file mode 100644
index 00000000000..94e761b0e0c
--- /dev/null
+++ b/qa/qa/page/component/clone_panel.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module ClonePanel
+ def self.included(base)
+ base.view 'app/views/shared/_clone_panel.html.haml' do
+ element :clone_dropdown
+ element :clone_options_dropdown, '.clone-options-dropdown' # rubocop:disable QA/ElementWithPattern
+ element :project_repository_location, 'text_field_tag :project_clone' # rubocop:disable QA/ElementWithPattern
+ end
+ end
+
+ def choose_repository_clone_http
+ choose_repository_clone('HTTP', 'http')
+ end
+
+ def choose_repository_clone_ssh
+ # It's not always beginning with ssh:// so detecting with @
+ # would be more reliable because ssh would always contain it.
+ # We can't use .git because HTTP also contain that part.
+ choose_repository_clone('SSH', '@')
+ end
+
+ def repository_location
+ Git::Location.new(find('#project_clone').value)
+ end
+
+ def wait_for_push
+ sleep 5
+ refresh
+ end
+
+ private
+
+ def choose_repository_clone(kind, detect_text)
+ wait(reload: false) do
+ click_element :clone_dropdown
+
+ page.within('.clone-options-dropdown') do
+ click_link(kind)
+ end
+
+ # Ensure git clone textbox was updated
+ repository_location.git_uri.include?(detect_text)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/dropdown_filter.rb b/qa/qa/page/component/dropdown_filter.rb
new file mode 100644
index 00000000000..e896c382779
--- /dev/null
+++ b/qa/qa/page/component/dropdown_filter.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module DropdownFilter
+ def filter_and_select(item)
+ wait(reload: false) do
+ page.has_css?('.dropdown-input-field')
+ end
+
+ find('.dropdown-input-field').set(item)
+ click_link item
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/groups_filter.rb b/qa/qa/page/component/groups_filter.rb
new file mode 100644
index 00000000000..cc50bb439b4
--- /dev/null
+++ b/qa/qa/page/component/groups_filter.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module GroupsFilter
+ def self.included(base)
+ base.view 'app/views/shared/groups/_search_form.html.haml' do
+ element :groups_filter
+ end
+
+ base.view 'app/assets/javascripts/groups/components/groups.vue' do
+ element :groups_list_tree_container
+ end
+ end
+
+ private
+
+ def has_filtered_group?(name)
+ # Filter and submit to reload the page and only retrieve the filtered results
+ find_element(:groups_filter).set(name).send_keys(:return)
+
+ # Since we submitted after filtering, the presence of
+ # groups_list_tree_container means we have the complete filtered list
+ # of groups
+ wait(reload: false) do
+ page.has_css?(element_selector_css(:groups_list_tree_container))
+ end
+
+ # If there are no groups we'll know immediately because we filtered the list
+ return false if page.has_text?('No groups or projects matched your search', wait: 0)
+
+ # The name will be present as filter input so we check for a link, not text
+ page.has_link?(name, wait: 0)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/issuable/common.rb b/qa/qa/page/component/issuable/common.rb
new file mode 100644
index 00000000000..cfd8ac1e7c8
--- /dev/null
+++ b/qa/qa/page/component/issuable/common.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Issuable
+ module Common
+ def self.included(base)
+ base.view 'app/assets/javascripts/issue_show/components/title.vue' do
+ element :edit_button
+ end
+
+ base.view 'app/assets/javascripts/issue_show/components/fields/title.vue' do
+ element :title_input
+ end
+
+ base.view 'app/assets/javascripts/issue_show/components/fields/description.vue' do
+ element :description_textarea
+ end
+
+ base.view 'app/assets/javascripts/issue_show/components/edit_actions.vue' do
+ element :save_button
+ element :delete_button
+ end
+
+ base.view 'app/assets/javascripts/issue_show/components/edit_actions.vue' do
+ element :save_button
+ element :delete_button
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/users_select.rb b/qa/qa/page/component/users_select.rb
new file mode 100644
index 00000000000..f88d6450a33
--- /dev/null
+++ b/qa/qa/page/component/users_select.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module UsersSelect
+ def select_user(element, username)
+ find("#{element_selector_css(element)} input").set(username)
+ find('.ajax-users-dropdown .user-username', text: "@#{username}").click
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/dashboard/groups.rb b/qa/qa/page/dashboard/groups.rb
index e853e0d85e0..7a07515de62 100644
--- a/qa/qa/page/dashboard/groups.rb
+++ b/qa/qa/page/dashboard/groups.rb
@@ -1,24 +1,22 @@
+# frozen_string_literal: true
+
module QA
module Page
module Dashboard
class Groups < Page::Base
+ include Page::Component::GroupsFilter
+
view 'app/views/shared/groups/_search_form.html.haml' do
- element :groups_filter, 'search_field_tag :filter'
- element :groups_filter_placeholder, 'Filter by name...'
+ element :groups_filter, 'search_field_tag :filter' # rubocop:disable QA/ElementWithPattern
+ element :groups_filter_placeholder, 'Search by name' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/dashboard/_groups_head.html.haml' do
- element :new_group_button, 'link_to _("New group")'
- end
-
- def filter_by_name(name)
- fill_in 'Filter by name...', with: name
+ element :new_group_button, 'link_to _("New group")' # rubocop:disable QA/ElementWithPattern
end
def has_group?(name)
- filter_by_name(name)
-
- page.has_link?(name)
+ has_filtered_group?(name)
end
def go_to_group(name)
diff --git a/qa/qa/page/dashboard/projects.rb b/qa/qa/page/dashboard/projects.rb
index 73942cb856a..0f434577b3b 100644
--- a/qa/qa/page/dashboard/projects.rb
+++ b/qa/qa/page/dashboard/projects.rb
@@ -3,8 +3,9 @@ module QA
module Dashboard
class Projects < Page::Base
view 'app/views/dashboard/projects/index.html.haml'
+
view 'app/views/shared/projects/_search_form.html.haml' do
- element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/
+ element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/ # rubocop:disable QA/ElementWithPattern
end
def go_to_project(name)
@@ -13,6 +14,8 @@ module QA
find_link(text: name).click
end
+ private
+
def filter_by_name(name)
page.within('form#project-filter-form') do
fill_in :name, with: name
diff --git a/qa/qa/page/file/form.rb b/qa/qa/page/file/form.rb
index f6e502f500b..a1534231691 100644
--- a/qa/qa/page/file/form.rb
+++ b/qa/qa/page/file/form.rb
@@ -3,14 +3,23 @@ module QA
module File
class Form < Page::Base
include Shared::CommitMessage
+ include Page::Component::DropdownFilter
view 'app/views/projects/blob/_editor.html.haml' do
- element :file_name, "text_field_tag 'file_name'"
- element :editor, '#editor'
+ element :file_name, "text_field_tag 'file_name'" # rubocop:disable QA/ElementWithPattern
+ element :editor, '#editor' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/_commit_button.html.haml' do
- element :commit_changes, "button_tag 'Commit changes'"
+ element :commit_changes, "button_tag 'Commit changes'" # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/views/projects/blob/_template_selectors.html.haml' do
+ element :template_type_dropdown
+ element :gitignore_dropdown
+ element :gitlab_ci_yml_dropdown
+ element :dockerfile_dropdown
+ element :license_dropdown
end
def add_name(name)
@@ -29,6 +38,25 @@ module QA
click_on 'Commit changes'
end
+ def select_template(template_type, template)
+ click_element :template_type_dropdown
+ click_link template_type
+
+ case template_type
+ when '.gitignore'
+ click_element :gitignore_dropdown
+ when '.gitlab-ci.yml'
+ click_element :gitlab_ci_yml_dropdown
+ when 'Dockerfile'
+ click_element :dockerfile_dropdown
+ when 'LICENSE'
+ click_element :license_dropdown
+ else
+ raise %Q(Unsupported template_type "#{template_type}". Please confirm that it is a valid option.)
+ end
+ filter_and_select template
+ end
+
private
def text_area
diff --git a/qa/qa/page/file/shared/commit_message.rb b/qa/qa/page/file/shared/commit_message.rb
index 5af1a55e2ef..aa1bb081cd3 100644
--- a/qa/qa/page/file/shared/commit_message.rb
+++ b/qa/qa/page/file/shared/commit_message.rb
@@ -5,7 +5,7 @@ module QA
module CommitMessage
def self.included(base)
base.view 'app/views/shared/_commit_message_container.html.haml' do
- element :commit_message, "text_area_tag 'commit_message'"
+ element :commit_message, "text_area_tag 'commit_message'" # rubocop:disable QA/ElementWithPattern
end
end
diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb
index 99f5924b67f..abd8ebb089f 100644
--- a/qa/qa/page/file/show.rb
+++ b/qa/qa/page/file/show.rb
@@ -5,12 +5,12 @@ module QA
include Shared::CommitMessage
view 'app/helpers/blob_helper.rb' do
- element :edit_button, "_('Edit')"
- element :delete_button, /label:\s+"Delete"/
+ element :edit_button, "_('Edit')" # rubocop:disable QA/ElementWithPattern
+ element :delete_button, /label:\s+"Delete"/ # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/blob/_remove.html.haml' do
- element :delete_file_button, "button_tag 'Delete file'"
+ element :delete_file_button, "button_tag 'Delete file'" # rubocop:disable QA/ElementWithPattern
end
def click_edit
diff --git a/qa/qa/page/group/new.rb b/qa/qa/page/group/new.rb
index 48b71a7c883..39584daf334 100644
--- a/qa/qa/page/group/new.rb
+++ b/qa/qa/page/group/new.rb
@@ -3,14 +3,14 @@ module QA
module Group
class New < Page::Base
view 'app/views/shared/_group_form.html.haml' do
- element :group_path_field, 'text_field :path'
- element :group_name_field, 'text_field :name'
- element :group_description_field, 'text_area :description'
+ element :group_path_field, 'text_field :path' # rubocop:disable QA/ElementWithPattern
+ element :group_name_field, 'text_field :name' # rubocop:disable QA/ElementWithPattern
+ element :group_description_field, 'text_area :description' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/groups/new.html.haml' do
- element :create_group_button, "submit 'Create group'"
- element :visibility_radios, 'visibility_level:'
+ element :create_group_button, "submit 'Create group'" # rubocop:disable QA/ElementWithPattern
+ element :visibility_radios, 'visibility_level:' # rubocop:disable QA/ElementWithPattern
end
def set_path(path)
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index 3e0eaa392f5..0f0ab81a4ef 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -1,64 +1,59 @@
+# frozen_string_literal: true
+
module QA
module Page
module Group
class Show < Page::Base
- view 'app/views/groups/show.html.haml' do
- element :new_project_or_subgroup_dropdown, '.new-project-subgroup'
- element :new_project_or_subgroup_dropdown_toggle, '.dropdown-toggle'
- element :new_project_option, /%li.*data:.*value: "new-project"/
- element :new_project_button, /%input.*data:.*action: "new-project"/
- element :new_subgroup_option, /%li.*data:.*value: "new-subgroup"/
+ include Page::Component::GroupsFilter
- # data-value and data-action get modified by JS for subgroup
- element :new_subgroup_button, /%input.*\.js-new-group-child/
+ view 'app/views/groups/show.html.haml' do
+ element :new_project_or_subgroup_dropdown
+ element :new_project_or_subgroup_dropdown_toggle
+ element :new_project_option
+ element :new_subgroup_option
+ element :new_in_group_button
end
view 'app/assets/javascripts/groups/constants.js' do
- element :no_result_text, 'Sorry, no groups or projects matched your search'
+ element :no_result_text, 'No groups or projects matched your search' # rubocop:disable QA/ElementWithPattern
end
def go_to_subgroup(name)
click_link name
end
- def filter_by_name(name)
- fill_in 'Filter by name...', with: name
+ def has_new_project_or_subgroup_dropdown?
+ has_element?(:new_project_or_subgroup_dropdown)
end
def has_subgroup?(name)
- filter_by_name(name)
-
- page.has_text?(/#{name}|Sorry, no groups or projects matched your search/, wait: 60)
-
- page.has_text?(name, wait: 0)
+ has_filtered_group?(name)
end
def go_to_new_subgroup
- click_new('subgroup')
+ select_kind :new_subgroup_option
- find("input[data-action='new-subgroup']").click
+ click_element :new_in_group_button
end
def go_to_new_project
- click_new('project')
+ select_kind :new_project_option
- find("input[data-action='new-project']").click
+ click_element :new_in_group_button
end
private
- def click_new(kind)
- within '.new-project-subgroup' do
- css = "li[data-value='new-#{kind}']"
-
+ def select_kind(kind)
+ within_element(:new_project_or_subgroup_dropdown) do
# May need to click again because it is possible to click the button quicker than the JS is bound
wait(reload: false) do
- find('.dropdown-toggle').click
+ click_element :new_project_or_subgroup_dropdown_toggle
- page.has_css?(css)
+ has_element?(kind)
end
- find(css).click
+ click_element kind
end
end
end
diff --git a/qa/qa/page/issuable/sidebar.rb b/qa/qa/page/issuable/sidebar.rb
index f207264e24f..d3751b712c9 100644
--- a/qa/qa/page/issuable/sidebar.rb
+++ b/qa/qa/page/issuable/sidebar.rb
@@ -3,8 +3,8 @@ module QA
module Issuable
class Sidebar < Page::Base
view 'app/views/shared/issuable/_sidebar.html.haml' do
- element :labels_block, ".issuable-show-labels"
- element :milestones_block, '.block.milestone'
+ element :labels_block, ".issuable-show-labels" # rubocop:disable QA/ElementWithPattern
+ element :milestones_block, '.block.milestone' # rubocop:disable QA/ElementWithPattern
end
def has_label?(label)
diff --git a/qa/qa/page/label/index.rb b/qa/qa/page/label/index.rb
new file mode 100644
index 00000000000..323acd57743
--- /dev/null
+++ b/qa/qa/page/label/index.rb
@@ -0,0 +1,15 @@
+module QA
+ module Page
+ module Label
+ class Index < Page::Base
+ view 'app/views/projects/labels/index.html.haml' do
+ element :label_create_new
+ end
+
+ def go_to_new_label
+ click_element :label_create_new
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/label/new.rb b/qa/qa/page/label/new.rb
new file mode 100644
index 00000000000..b5422dc9400
--- /dev/null
+++ b/qa/qa/page/label/new.rb
@@ -0,0 +1,30 @@
+module QA
+ module Page
+ module Label
+ class New < Page::Base
+ view 'app/views/shared/labels/_form.html.haml' do
+ element :label_title
+ element :label_description
+ element :label_color
+ element :label_create_button
+ end
+
+ def create_label
+ click_element :label_create_button
+ end
+
+ def fill_title(title)
+ fill_element :label_title, title
+ end
+
+ def fill_description(description)
+ fill_element :label_description, description
+ end
+
+ def fill_color(color)
+ fill_element :label_color, color
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/layout/banner.rb b/qa/qa/page/layout/banner.rb
index e7654bdafc9..2223f2adec8 100644
--- a/qa/qa/page/layout/banner.rb
+++ b/qa/qa/page/layout/banner.rb
@@ -3,7 +3,7 @@ module QA
module Layout
class Banner < Page::Base
view 'app/views/layouts/header/_read_only_banner.html.haml' do
- element :flash_notice, ".flash-notice"
+ element :flash_notice, ".flash-notice" # rubocop:disable QA/ElementWithPattern
end
def has_notice?(message)
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 6cdfbd1c125..97ffe0e5716 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -3,31 +3,36 @@ module QA
module Main
class Login < Page::Base
view 'app/views/devise/passwords/edit.html.haml' do
- element :password_field, 'password_field :password'
- element :password_confirmation, 'password_field :password_confirmation'
- element :change_password_button, 'submit "Change your password"'
+ element :password_field
+ element :password_confirmation
+ element :change_password_button
end
view 'app/views/devise/sessions/_new_base.html.haml' do
- element :login_field, 'text_field :login'
- element :password_field, 'password_field :password'
- element :sign_in_button, 'submit "Sign in"'
+ element :login_field
+ element :password_field
+ element :sign_in_button
end
view 'app/views/devise/sessions/_new_ldap.html.haml' do
- element :username_field, 'text_field_tag :username'
- element :password_field, 'password_field_tag :password'
- element :sign_in_button, 'submit_tag "Sign in"'
+ element :username_field
+ element :password_field
+ element :sign_in_button
end
view 'app/views/devise/shared/_tabs_ldap.html.haml' do
- element :ldap_tab, "link_to server['label']"
- element :standard_tab, "link_to 'Standard'"
+ element :ldap_tab
+ element :standard_tab
+ element :register_tab
end
view 'app/views/devise/shared/_tabs_normal.html.haml' do
- element :sign_in_tab, /nav-link.*login-pane.*Sign in/
- element :register_tab, /nav-link.*register-pane.*Register/
+ element :sign_in_tab
+ element :register_tab
+ end
+
+ view 'app/views/devise/shared/_omniauth_box.html.haml' do
+ element :saml_login_button
end
def initialize
@@ -35,64 +40,120 @@ module QA
# we need to wait for the instance to start. That said, in some cases
# we are already logged-in so we check both cases here.
wait(max: 500) do
- page.has_css?('.login-page') ||
- Page::Menu::Main.act { has_personal_area?(wait: 0) }
+ has_css?('.login-page') ||
+ Page::Main::Menu.act { has_personal_area?(wait: 0) }
end
end
- def sign_in_using_credentials
+ def sign_in_using_credentials(user = nil)
# Don't try to log-in if we're already logged-in
- return if Page::Menu::Main.act { has_personal_area?(wait: 0) }
+ return if Page::Main::Menu.act { has_personal_area?(wait: 0) }
using_wait_time 0 do
set_initial_password_if_present
+ raise NotImplementedError if Runtime::User.ldap_user? && user&.credentials_given?
+
if Runtime::User.ldap_user?
sign_in_using_ldap_credentials
else
- sign_in_using_gitlab_credentials
+ sign_in_using_gitlab_credentials(user || Runtime::User)
end
end
- Page::Menu::Main.act { has_personal_area? }
+ Page::Main::Menu.act { has_personal_area? }
+ end
+
+ def sign_in_using_admin_credentials
+ admin = QA::Resource::User.new.tap do |user|
+ user.username = QA::Runtime::User.admin_username
+ user.password = QA::Runtime::User.admin_password
+ end
+
+ using_wait_time 0 do
+ set_initial_password_if_present
+
+ sign_in_using_gitlab_credentials(admin)
+ end
+
+ Page::Main::Menu.act { has_personal_area? }
end
def self.path
'/users/sign_in'
end
+ def has_sign_in_tab?
+ has_element?(:sign_in_tab)
+ end
+
+ def has_ldap_tab?
+ has_element?(:ldap_tab)
+ end
+
+ def has_standard_tab?
+ has_element?(:standard_tab)
+ end
+
+ def sign_in_tab?
+ has_css?(".active", text: 'Sign in')
+ end
+
+ def ldap_tab?
+ has_css?(".active", text: 'LDAP')
+ end
+
+ def standard_tab?
+ has_css?(".active", text: 'Standard')
+ end
+
def switch_to_sign_in_tab
- click_on 'Sign in'
+ click_element :sign_in_tab
end
def switch_to_register_tab
- click_on 'Register'
+ set_initial_password_if_present
+ click_element :register_tab
+ end
+
+ def switch_to_ldap_tab
+ click_element :ldap_tab
+ end
+
+ def switch_to_standard_tab
+ click_element :standard_tab
end
private
def sign_in_using_ldap_credentials
- click_link 'LDAP'
+ switch_to_ldap_tab
+
+ fill_element :username_field, Runtime::User.ldap_username
+ fill_element :password_field, Runtime::User.ldap_password
+ click_element :sign_in_button
+ end
- fill_in :username, with: Runtime::User.ldap_username
- fill_in :password, with: Runtime::User.ldap_password
- click_button 'Sign in'
+ def sign_in_with_saml
+ set_initial_password_if_present
+ click_element :saml_login_button
end
- def sign_in_using_gitlab_credentials
- click_link 'Standard' if page.has_content?('LDAP')
+ def sign_in_using_gitlab_credentials(user)
+ switch_to_sign_in_tab if has_sign_in_tab?
+ switch_to_standard_tab if has_standard_tab?
- fill_in :user_login, with: Runtime::User.name
- fill_in :user_password, with: Runtime::User.password
- click_button 'Sign in'
+ fill_element :login_field, user.username
+ fill_element :password_field, user.password
+ click_element :sign_in_button
end
def set_initial_password_if_present
- return unless page.has_content?('Change your password')
+ return unless has_content?('Change your password')
- fill_in :user_password, with: Runtime::User.password
- fill_in :user_password_confirmation, with: Runtime::User.password
- click_button 'Change your password'
+ fill_element :password_field, Runtime::User.password
+ fill_element :password_confirmation, Runtime::User.password
+ click_element :change_password_button
end
end
end
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
new file mode 100644
index 00000000000..fb45ebef1b6
--- /dev/null
+++ b/qa/qa/page/main/menu.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Main
+ class Menu < Page::Base
+ view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
+ element :user_sign_out_link, 'link_to _("Sign out")' # rubocop:disable QA/ElementWithPattern
+ element :settings_link, 'link_to s_("CurrentUser|Settings")' # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/views/layouts/header/_default.html.haml' do
+ element :navbar
+ element :user_avatar
+ element :user_menu, '.dropdown-menu' # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/views/layouts/nav/_dashboard.html.haml' do
+ element :admin_area_link
+ element :projects_dropdown
+ element :groups_dropdown
+ end
+
+ view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do
+ element :projects_dropdown_sidebar
+ element :your_projects_link
+ end
+
+ def go_to_groups
+ within_top_menu do
+ click_element :groups_dropdown
+ end
+
+ page.within('.qa-groups-dropdown-sidebar') do
+ click_element :your_groups_link
+ end
+ end
+
+ def go_to_projects
+ within_top_menu do
+ click_element :projects_dropdown
+ end
+
+ page.within('.qa-projects-dropdown-sidebar') do
+ click_element :your_projects_link
+ end
+ end
+
+ def go_to_admin_area
+ within_top_menu { click_element :admin_area_link }
+ end
+
+ def sign_out
+ within_user_menu do
+ click_link 'Sign out'
+ end
+ end
+
+ def go_to_profile_settings
+ within_user_menu do
+ click_link 'Settings'
+ end
+ end
+
+ def has_personal_area?(wait: Capybara.default_max_wait_time)
+ using_wait_time(wait) do
+ page.has_selector?(element_selector_css(:user_avatar))
+ end
+ end
+
+ private
+
+ def within_top_menu
+ page.within('.qa-navbar') do
+ yield
+ end
+ end
+
+ def within_user_menu
+ within_top_menu do
+ click_element :user_avatar
+
+ page.within('.dropdown-menu') do
+ yield
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/main/oauth.rb b/qa/qa/page/main/oauth.rb
index 618f114e058..bc44d274314 100644
--- a/qa/qa/page/main/oauth.rb
+++ b/qa/qa/page/main/oauth.rb
@@ -3,7 +3,7 @@ module QA
module Main
class OAuth < Page::Base
view 'app/views/doorkeeper/authorizations/new.html.haml' do
- element :authorization_button, 'submit_tag _("Authorize")'
+ element :authorization_button, 'submit_tag _("Authorize")' # rubocop:disable QA/ElementWithPattern
end
def needs_authorization?
diff --git a/qa/qa/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb
index 9a834e94b81..9ca498012eb 100644
--- a/qa/qa/page/main/sign_up.rb
+++ b/qa/qa/page/main/sign_up.rb
@@ -1,25 +1,35 @@
+# frozen_string_literal: true
+
module QA
module Page
module Main
class SignUp < Page::Base
view 'app/views/devise/shared/_signup_box.html.haml' do
- element :name, 'text_field :name'
- element :username, 'text_field :username'
- element :email_field, 'email_field :email'
- element :email_confirmation, 'email_field :email_confirmation'
- element :password, 'password_field :password'
- element :register_button, 'submit "Register"'
+ element :new_user_name
+ element :new_user_username
+ element :new_user_email
+ element :new_user_email_confirmation
+ element :new_user_password
+ element :new_user_register_button
+ element :new_user_accept_terms
end
- def sign_up!(name:, username:, email:, password:)
- fill_in :new_user_name, with: name
- fill_in :new_user_username, with: username
- fill_in :new_user_email, with: email
- fill_in :new_user_email_confirmation, with: email
- fill_in :new_user_password, with: password
- click_button 'Register'
+ def sign_up!(user)
+ fill_element :new_user_name, user.name
+ fill_element :new_user_username, user.username
+ fill_element :new_user_email, user.email
+ fill_element :new_user_email_confirmation, user.email
+ fill_element :new_user_password, user.password
+
+ check_element :new_user_accept_terms if has_element?(:new_user_accept_terms)
+
+ signed_in = with_retry do
+ click_element :new_user_register_button
+
+ Page::Main::Menu.act { has_personal_area? }
+ end
- Page::Menu::Main.act { has_personal_area? }
+ raise "Failed to register and sign in" unless signed_in
end
end
end
diff --git a/qa/qa/page/menu/admin.rb b/qa/qa/page/menu/admin.rb
deleted file mode 100644
index 573b98f7386..00000000000
--- a/qa/qa/page/menu/admin.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module QA
- module Page
- module Menu
- class Admin < Page::Base
- view 'app/views/layouts/nav/sidebar/_admin.html.haml' do
- element :settings, "_('Settings')"
- end
-
- def go_to_settings
- click_link 'Settings'
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/menu/main.rb b/qa/qa/page/menu/main.rb
deleted file mode 100644
index 36e7285f7b7..00000000000
--- a/qa/qa/page/menu/main.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-module QA
- module Page
- module Menu
- class Main < Page::Base
- view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
- element :user_sign_out_link, 'link_to _("Sign out")'
- element :settings_link, 'link_to s_("CurrentUser|Settings")'
- end
-
- view 'app/views/layouts/header/_default.html.haml' do
- element :navbar
- element :user_avatar
- element :user_menu, '.dropdown-menu'
- end
-
- view 'app/views/layouts/nav/_dashboard.html.haml' do
- element :admin_area_link
- element :projects_dropdown
- element :groups_dropdown
- end
-
- view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do
- element :projects_dropdown_sidebar
- element :your_projects_link
- end
-
- def go_to_groups
- within_top_menu do
- click_element :groups_dropdown
- end
-
- page.within('.qa-groups-dropdown-sidebar') do
- click_element :your_groups_link
- end
- end
-
- def go_to_projects
- within_top_menu do
- click_element :projects_dropdown
- end
-
- page.within('.qa-projects-dropdown-sidebar') do
- click_element :your_projects_link
- end
- end
-
- def go_to_admin_area
- within_top_menu { click_element :admin_area_link }
- end
-
- def sign_out
- within_user_menu do
- click_link 'Sign out'
- end
- end
-
- def go_to_profile_settings
- within_user_menu do
- click_link 'Settings'
- end
- end
-
- def has_personal_area?(wait: Capybara.default_max_wait_time)
- # No need to wait, either we're logged-in, or not.
- using_wait_time(wait) { page.has_selector?('.qa-user-avatar') }
- end
-
- private
-
- def within_top_menu
- page.within('.qa-navbar') do
- yield
- end
- end
-
- def within_user_menu
- within_top_menu do
- click_element :user_avatar
-
- page.within('.dropdown-menu') do
- yield
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/menu/profile.rb b/qa/qa/page/menu/profile.rb
deleted file mode 100644
index 95e88d863e4..00000000000
--- a/qa/qa/page/menu/profile.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module QA
- module Page
- module Menu
- class Profile < Page::Base
- view 'app/views/layouts/nav/sidebar/_profile.html.haml' do
- element :access_token_link, 'link_to profile_personal_access_tokens_path'
- element :access_token_title, 'Access Tokens'
- element :top_level_items, '.sidebar-top-level-items'
- end
-
- def click_access_tokens
- within_sidebar do
- click_link('Access Tokens')
- end
- end
-
- private
-
- def within_sidebar
- page.within('.sidebar-top-level-items') do
- yield
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb
deleted file mode 100644
index 354ccec2a5a..00000000000
--- a/qa/qa/page/menu/side.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-module QA
- module Page
- module Menu
- class Side < Page::Base
- view 'app/views/layouts/nav/sidebar/_project.html.haml' do
- element :settings_item
- element :settings_link, 'link_to edit_project_path'
- element :repository_link, "title: _('Repository')"
- element :pipelines_settings_link, "title: _('CI / CD')"
- element :operations_kubernetes_link, "title: _('Kubernetes')"
- element :issues_link, /link_to.*shortcuts-issues/
- element :issues_link_text, "Issues"
- element :merge_requests_link, /link_to.*shortcuts-merge_requests/
- element :merge_requests_link_text, "Merge Requests"
- element :top_level_items, '.sidebar-top-level-items'
- element :operations_section, "class: 'shortcuts-operations'"
- element :activity_link, "title: _('Activity')"
- element :wiki_link_text, "Wiki"
- element :milestones_link
- end
-
- view 'app/assets/javascripts/fly_out_nav.js' do
- element :fly_out, "classList.add('fly-out-list')"
- end
-
- def click_repository_settings
- hover_settings do
- within_submenu do
- click_link('Repository')
- end
- end
- end
-
- def click_ci_cd_settings
- hover_settings do
- within_submenu do
- click_link('CI / CD')
- end
- end
- end
-
- def click_operations_kubernetes
- hover_operations do
- within_submenu do
- click_link('Kubernetes')
- end
- end
- end
-
- def click_ci_cd_pipelines
- within_sidebar do
- click_link('CI / CD')
- end
- end
-
- def go_to_settings
- within_sidebar do
- click_on 'Settings'
- end
- end
-
- def click_issues
- within_sidebar do
- click_link('Issues')
- end
- end
-
- def click_merge_requests
- within_sidebar do
- click_link('Merge Requests')
- end
- end
-
- def click_milestones
- within_sidebar do
- click_element :milestones_link
- end
- end
-
- def click_wiki
- within_sidebar do
- click_link('Wiki')
- end
- end
-
- private
-
- def hover_settings
- within_sidebar do
- find('.qa-settings-item').hover
-
- yield
- end
- end
-
- def hover_operations
- within_sidebar do
- find('.shortcuts-operations').hover
-
- yield
- end
- end
-
- def within_sidebar
- page.within('.sidebar-top-level-items') do
- yield
- end
- end
-
- def go_to_activity
- within_sidebar do
- click_on 'Activity'
- end
- end
-
- def within_submenu
- page.within('.fly-out-list') do
- yield
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index 83cc4bbbace..1f8f1fbca8e 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -22,6 +22,10 @@ module QA
element :issuable_dropdown_menu_milestone
end
+ view 'app/views/shared/issuable/_label_dropdown.html.haml' do
+ element :issuable_label
+ end
+
def create_merge_request
click_element :issuable_create_button
end
@@ -40,6 +44,12 @@ module QA
click_on milestone.title
end
end
+
+ def select_label(label)
+ click_element :issuable_label
+
+ click_link label.title
+ end
end
end
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index c200f14f4fb..2fd30e15ffb 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -1,25 +1,64 @@
+# frozen_string_literal: true
+
module QA
module Page
module MergeRequest
class Show < Page::Base
view 'app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue' do
element :merge_button
- element :fast_forward_message, 'Fast-forward merge without a merge commit'
+ element :fast_forward_message, 'Fast-forward merge without a merge commit' # rubocop:disable QA/ElementWithPattern
+ element :merge_moment_dropdown
+ element :merge_when_pipeline_succeeds_option
+ element :merge_immediately_option
end
view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue' do
- element :merged_status, 'The changes were merged into'
+ element :merged_status, 'The changes were merged into' # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue' do
element :mr_rebase_button
- element :no_fast_forward_message, 'Fast-forward merge is not possible'
+ element :no_fast_forward_message, 'Fast-forward merge is not possible' # rubocop:disable QA/ElementWithPattern
end
- view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue' do
+ view 'app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue' do
element :squash_checkbox
end
+ view 'app/views/projects/merge_requests/show.html.haml' do
+ element :notes_tab
+ element :diffs_tab
+ end
+
+ view 'app/assets/javascripts/diffs/components/diff_line_gutter_content.vue' do
+ element :diff_comment
+ end
+
+ view 'app/assets/javascripts/notes/components/comment_form.vue' do
+ element :note_dropdown
+ element :discussion_option
+ end
+
+ view 'app/assets/javascripts/notes/components/note_form.vue' do
+ element :reply_input
+ end
+
+ view 'app/assets/javascripts/notes/components/noteable_discussion.vue' do
+ element :discussion_reply
+ end
+
+ view 'app/assets/javascripts/diffs/components/inline_diff_table_row.vue' do
+ element :new_diff_line
+ end
+
+ view 'app/views/shared/issuable/_sidebar.html.haml' do
+ element :labels_block
+ end
+
+ view 'app/views/projects/merge_requests/_mr_title.html.haml' do
+ element :edit_button
+ end
+
def fast_forward_possible?
!has_text?('Fast-forward merge is not possible')
end
@@ -27,7 +66,20 @@ module QA
def has_merge_button?
refresh
- has_selector?('.accept-merge-request')
+ has_css?(element_selector_css(:merge_button))
+ end
+
+ def has_merge_options?
+ has_css?(element_selector_css(:merge_moment_dropdown))
+ end
+
+ def merge_immediately
+ if has_merge_options?
+ click_element :merge_moment_dropdown
+ click_element :merge_immediately_option
+ else
+ click_element :merge_button
+ end
end
def rebase!
@@ -48,6 +100,13 @@ module QA
end
end
+ def has_label?(label)
+ page.within(element_selector_css(:labels_block)) do
+ element = find('span', text: label)
+ !element.nil?
+ end
+ end
+
def merge!
# The merge button is disabled on load
wait do
@@ -59,7 +118,7 @@ module QA
!first(element_selector_css(:merge_button)).disabled?
end
- click_element :merge_button
+ merge_immediately
wait(reload: false) do
has_text?('The changes were merged into')
@@ -79,6 +138,39 @@ module QA
click_element :squash_checkbox
end
+
+ def go_to_discussions_tab
+ click_element :notes_tab
+ end
+
+ def go_to_diffs_tab
+ click_element :diffs_tab
+ end
+
+ def add_comment_to_diff(text)
+ wait(time: 5) do
+ page.has_text?("No newline at end of file")
+ end
+ all_elements(:new_diff_line).first.hover
+ click_element :diff_comment
+ fill_element :reply_input, text
+ end
+
+ def start_discussion(text)
+ fill_element :comment_input, text
+ click_element :note_dropdown
+ click_element :discussion_option
+ click_element :comment_button
+ end
+
+ def reply_to_discussion(reply_text)
+ all_elements(:discussion_reply).last.click
+ fill_element :reply_input, reply_text
+ end
+
+ def edit!
+ click_element :edit_button
+ end
end
end
end
diff --git a/qa/qa/page/profile/menu.rb b/qa/qa/page/profile/menu.rb
new file mode 100644
index 00000000000..2d503499e13
--- /dev/null
+++ b/qa/qa/page/profile/menu.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Profile
+ class Menu < Page::Base
+ view 'app/views/layouts/nav/sidebar/_profile.html.haml' do
+ element :access_token_link, 'link_to profile_personal_access_tokens_path' # rubocop:disable QA/ElementWithPattern
+ element :access_token_title, 'Access Tokens' # rubocop:disable QA/ElementWithPattern
+ element :top_level_items, '.sidebar-top-level-items' # rubocop:disable QA/ElementWithPattern
+ element :ssh_keys, 'SSH Keys' # rubocop:disable QA/ElementWithPattern
+ end
+
+ def click_access_tokens
+ within_sidebar do
+ click_link('Access Tokens')
+ end
+ end
+
+ def click_ssh_keys
+ within_sidebar do
+ click_link('SSH Keys')
+ end
+ end
+
+ private
+
+ def within_sidebar
+ page.within('.sidebar-top-level-items') do
+ yield
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/profile/personal_access_tokens.rb b/qa/qa/page/profile/personal_access_tokens.rb
index f5ae47dadd0..9191dbe9cf3 100644
--- a/qa/qa/page/profile/personal_access_tokens.rb
+++ b/qa/qa/page/profile/personal_access_tokens.rb
@@ -3,13 +3,13 @@ module QA
module Profile
class PersonalAccessTokens < Page::Base
view 'app/views/shared/_personal_access_tokens_form.html.haml' do
- element :personal_access_token_name_field, 'text_field :name'
- element :create_token_button, 'submit "Create #{type} token"' # rubocop:disable Lint/InterpolationCheck
- element :scopes_api_radios, "label :scopes"
+ element :personal_access_token_name_field, 'text_field :name' # rubocop:disable QA/ElementWithPattern
+ element :create_token_button, 'submit "Create #{type} token"' # rubocop:disable QA/ElementWithPattern, Lint/InterpolationCheck
+ element :scopes_api_radios, "label :scopes" # rubocop:disable QA/ElementWithPattern
end
- view 'app/views/profiles/personal_access_tokens/index.html.haml' do
- element :create_token_field, "text_field_tag 'created-personal-access-token'"
+ view 'app/views/shared/_personal_access_tokens_created_container.html.haml' do
+ element :create_token_field, "text_field_tag 'created-personal-access-token'" # rubocop:disable QA/ElementWithPattern
end
def fill_token_name(name)
diff --git a/qa/qa/page/profile/ssh_keys.rb b/qa/qa/page/profile/ssh_keys.rb
new file mode 100644
index 00000000000..ce1813b14d0
--- /dev/null
+++ b/qa/qa/page/profile/ssh_keys.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Profile
+ class SSHKeys < Page::Base
+ view 'app/views/profiles/keys/_form.html.haml' do
+ element :key_title_field
+ element :key_public_key_field
+ element :add_key_button
+ end
+
+ view 'app/views/profiles/keys/_key_details.html.haml' do
+ element :delete_key_button
+ end
+
+ def add_key(public_key, title)
+ fill_element :key_public_key_field, public_key
+ fill_element :key_title_field, title
+
+ click_element :add_key_button
+ end
+
+ def remove_key(title)
+ click_link(title)
+
+ accept_alert do
+ click_element :delete_key_button
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/activity.rb b/qa/qa/page/project/activity.rb
index 0196922c889..56fbaa90790 100644
--- a/qa/qa/page/project/activity.rb
+++ b/qa/qa/page/project/activity.rb
@@ -3,7 +3,7 @@ module QA
module Project
class Activity < Page::Base
view 'app/views/shared/_event_filter.html.haml' do
- element :push_events, "event_filter_link EventFilter.push, _('Push events')"
+ element :push_events, "event_filter_link EventFilter::PUSH, _('Push events')" # rubocop:disable QA/ElementWithPattern
end
def go_to_push_events
diff --git a/qa/qa/page/project/fork/new.rb b/qa/qa/page/project/fork/new.rb
index ed92df956bf..140c004b458 100644
--- a/qa/qa/page/project/fork/new.rb
+++ b/qa/qa/page/project/fork/new.rb
@@ -4,7 +4,7 @@ module QA
module Fork
class New < Page::Base
view 'app/views/projects/forks/_fork_button.html.haml' do
- element :namespace, 'link_to project_forks_path'
+ element :namespace, 'link_to project_forks_path' # rubocop:disable QA/ElementWithPattern
end
def choose_namespace(namespace = Runtime::Namespace.path)
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index 36567927194..a3cde73d3f2 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -6,16 +6,16 @@ module QA
include Page::Component::Select2
view 'app/views/import/github/new.html.haml' do
- element :personal_access_token_field, 'text_field_tag :personal_access_token'
- element :list_repos_button, "submit_tag _('List your GitHub repositories')"
+ element :personal_access_token_field, 'text_field_tag :personal_access_token' # rubocop:disable QA/ElementWithPattern
+ element :list_repos_button, "submit_tag _('List your GitHub repositories')" # rubocop:disable QA/ElementWithPattern
end
view 'app/views/import/_githubish_status.html.haml' do
- element :project_import_row, 'data: { qa: { repo_path: repo.full_name } }'
+ element :project_import_row, 'data: { qa: { repo_path: repo.full_name } }' # rubocop:disable QA/ElementWithPattern
element :project_namespace_select
- element :project_namespace_field, 'select_tag :namespace_id'
- element :project_path_field, 'text_field_tag :path, repo.name'
- element :import_button, "_('Import')"
+ element :project_namespace_field, 'select_tag :namespace_id' # rubocop:disable QA/ElementWithPattern
+ element :project_path_field, 'text_field_tag :path, sanitize_project_name(repo.name)' # rubocop:disable QA/ElementWithPattern
+ element :import_button, "_('Import')" # rubocop:disable QA/ElementWithPattern
end
def add_personal_access_token(personal_access_token)
diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb
index b5903f536a4..1035bf74a43 100644
--- a/qa/qa/page/project/issue/index.rb
+++ b/qa/qa/page/project/issue/index.rb
@@ -4,7 +4,7 @@ module QA
module Issue
class Index < Page::Base
view 'app/views/projects/issues/_issue.html.haml' do
- element :issue_link, 'link_to issue.title'
+ element :issue_link, 'link_to issue.title' # rubocop:disable QA/ElementWithPattern
end
def go_to_issue(title)
diff --git a/qa/qa/page/project/issue/new.rb b/qa/qa/page/project/issue/new.rb
index 7fc581da1ed..03b605ab24b 100644
--- a/qa/qa/page/project/issue/new.rb
+++ b/qa/qa/page/project/issue/new.rb
@@ -4,15 +4,15 @@ module QA
module Issue
class New < Page::Base
view 'app/views/shared/issuable/_form.html.haml' do
- element :submit_issue_button, 'form.submit "Submit'
+ element :submit_issue_button, 'form.submit "Submit' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/shared/issuable/form/_title.html.haml' do
- element :issue_title_textbox, 'form.text_field :title'
+ element :issue_title_textbox, 'form.text_field :title' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/shared/form_elements/_description.html.haml' do
- element :issue_description_textarea, "render 'projects/zen', f: form, attr: :description"
+ element :issue_description_textarea, "render 'projects/zen', f: form, attr: :description" # rubocop:disable QA/ElementWithPattern
end
def add_title(title)
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index 5bc0598a524..23def93c7dd 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -1,37 +1,53 @@
+# frozen_string_literal: true
+
module QA
module Page
module Project
module Issue
class Show < Page::Base
- view 'app/views/projects/issues/show.html.haml' do
- element :issue_details, '.issue-details'
- element :title, '.title'
- end
+ include Page::Component::Issuable::Common
view 'app/views/shared/notes/_form.html.haml' do
- element :new_note_form, 'new-note'
- element :new_note_form, 'attr: :note'
+ element :new_note_form, 'new-note' # rubocop:disable QA/ElementWithPattern
+ element :new_note_form, 'attr: :note' # rubocop:disable QA/ElementWithPattern
end
- view 'app/views/shared/notes/_comment_button.html.haml' do
- element :comment_button, '%strong Comment'
+ view 'app/assets/javascripts/notes/components/comment_form.vue' do
+ element :comment_button
+ element :comment_input
end
- def issue_title
- find('.issue-details .title').text
+ view 'app/assets/javascripts/notes/components/discussion_filter.vue' do
+ element :discussion_filter
+ element :filter_options
end
# Adds a comment to an issue
# attachment option should be an absolute path
def comment(text, attachment: nil)
- fill_in(with: text, name: 'note[note]')
+ fill_element :comment_input, text
unless attachment.nil?
QA::Page::Component::Dropzone.new(self, '.new-note')
.attach_file(attachment)
end
- click_on 'Comment'
+ click_element :comment_button
+ end
+
+ def select_comments_only_filter
+ click_element :discussion_filter
+ all_elements(:filter_options)[1].click
+ end
+
+ def select_history_only_filter
+ click_element :discussion_filter
+ all_elements(:filter_options).last.click
+ end
+
+ def select_all_activities_filter
+ click_element :discussion_filter
+ all_elements(:filter_options).first.click
end
end
end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 228ffd9d381..d688f15914c 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -4,30 +4,39 @@ module QA::Page
COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
PASSED_STATUS = 'passed'.freeze
- view 'app/views/shared/builds/_build_output.html.haml' do
- element :build_output, '.js-build-output'
- element :loading_animation, '.js-build-refresh'
+ view 'app/assets/javascripts/jobs/components/job_app.vue' do
+ element :loading_animation
+ end
+
+ view 'app/assets/javascripts/jobs/components/job_log.vue' do
+ element :build_trace
end
view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
- element :status_badge, 'ci-status'
+ element :status_badge
end
def completed?
- COMPLETED_STATUSES.include? find('.ci-status').text
+ COMPLETED_STATUSES.include?(status_badge)
end
def passed?
- find('.ci-status').text == PASSED_STATUS
+ status_badge == PASSED_STATUS
end
def trace_loading?
- has_css?('.js-build-refresh')
+ has_element?(:loading_animation)
end
# Reminder: You may wish to wait for a particular job status before checking output
def output
- find('.js-build-output').text
+ find_element(:build_trace).text
+ end
+
+ private
+
+ def status_badge
+ find_element(:status_badge).text
end
end
end
diff --git a/qa/qa/page/project/menu.rb b/qa/qa/page/project/menu.rb
new file mode 100644
index 00000000000..cb4a10e1b6a
--- /dev/null
+++ b/qa/qa/page/project/menu.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ class Menu < Page::Base
+ view 'app/views/layouts/nav/sidebar/_project.html.haml' do
+ element :settings_item
+ element :settings_link, 'link_to edit_project_path' # rubocop:disable QA/ElementWithPattern
+ element :repository_link, "title: _('Repository')" # rubocop:disable QA/ElementWithPattern
+ element :link_pipelines
+ element :link_members_settings
+ element :pipelines_settings_link, "title: _('CI / CD')" # rubocop:disable QA/ElementWithPattern
+ element :operations_kubernetes_link, "title: _('Kubernetes')" # rubocop:disable QA/ElementWithPattern
+ element :operations_environments_link
+ element :issues_link, /link_to.*shortcuts-issues/ # rubocop:disable QA/ElementWithPattern
+ element :issues_link_text, "Issues" # rubocop:disable QA/ElementWithPattern
+ element :merge_requests_link, /link_to.*shortcuts-merge_requests/ # rubocop:disable QA/ElementWithPattern
+ element :merge_requests_link_text, "Merge Requests" # rubocop:disable QA/ElementWithPattern
+ element :top_level_items, '.sidebar-top-level-items' # rubocop:disable QA/ElementWithPattern
+ element :operations_section, "class: 'shortcuts-operations'" # rubocop:disable QA/ElementWithPattern
+ element :activity_link, "title: _('Activity')" # rubocop:disable QA/ElementWithPattern
+ element :wiki_link_text, "Wiki" # rubocop:disable QA/ElementWithPattern
+ element :milestones_link
+ element :labels_link
+ end
+
+ view 'app/assets/javascripts/fly_out_nav.js' do
+ element :fly_out, "classList.add('fly-out-list')" # rubocop:disable QA/ElementWithPattern
+ end
+
+ def click_repository_settings
+ hover_settings do
+ within_submenu do
+ click_link('Repository')
+ end
+ end
+ end
+
+ def click_ci_cd_settings
+ hover_settings do
+ within_submenu do
+ click_link('CI / CD')
+ end
+ end
+ end
+
+ def click_operations_environments
+ hover_operations do
+ within_submenu do
+ click_element(:operations_environments_link)
+ end
+ end
+ end
+
+ def click_members_settings
+ hover_settings do
+ within_submenu do
+ click_element :link_members_settings
+ end
+ end
+ end
+
+ def click_operations_kubernetes
+ hover_operations do
+ within_submenu do
+ click_link('Kubernetes')
+ end
+ end
+ end
+
+ def click_ci_cd_pipelines
+ within_sidebar do
+ click_element :link_pipelines
+ end
+ end
+
+ def go_to_settings
+ within_sidebar do
+ click_on 'Settings'
+ end
+ end
+
+ def click_issues
+ within_sidebar do
+ click_link('Issues')
+ end
+ end
+
+ def go_to_labels
+ hover_issues do
+ within_submenu do
+ click_element(:labels_link)
+ end
+ end
+ end
+
+ def click_merge_requests
+ within_sidebar do
+ click_link('Merge Requests')
+ end
+ end
+
+ def click_milestones
+ within_sidebar do
+ click_element :milestones_link
+ end
+ end
+
+ def click_wiki
+ within_sidebar do
+ click_link('Wiki')
+ end
+ end
+
+ def click_repository
+ within_sidebar do
+ click_link('Repository')
+ end
+ end
+
+ private
+
+ def hover_issues
+ within_sidebar do
+ find_element(:issues_item).hover
+
+ yield
+ end
+ end
+
+ def hover_settings
+ within_sidebar do
+ find('.qa-settings-item').hover
+
+ yield
+ end
+ end
+
+ def hover_operations
+ within_sidebar do
+ find('.shortcuts-operations').hover
+
+ yield
+ end
+ end
+
+ def within_sidebar
+ page.within('.sidebar-top-level-items') do
+ yield
+ end
+ end
+
+ def go_to_activity
+ within_sidebar do
+ click_on 'Activity'
+ end
+ end
+
+ def within_submenu
+ page.within('.fly-out-list') do
+ yield
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index 1fb569b0f29..6acc413b586 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -5,20 +5,21 @@ module QA
include Page::Component::Select2
view 'app/views/projects/new.html.haml' do
- element :import_project_tab, "Import project"
+ element :import_project_tab, "Import project" # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/_new_project_fields.html.haml' do
element :project_namespace_select
- element :project_namespace_field, 'namespaces_options'
- element :project_path, 'text_field :path'
- element :project_description, 'text_area :description'
- element :project_create_button, "submit 'Create project'"
- element :visibility_radios, 'visibility_level:'
+ element :project_namespace_field, 'namespaces_options' # rubocop:disable QA/ElementWithPattern
+ element :project_name, 'text_field :name' # rubocop:disable QA/ElementWithPattern
+ element :project_path, 'text_field :path' # rubocop:disable QA/ElementWithPattern
+ element :project_description, 'text_area :description' # rubocop:disable QA/ElementWithPattern
+ element :project_create_button, "submit 'Create project'" # rubocop:disable QA/ElementWithPattern
+ element :visibility_radios, 'visibility_level:' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/_import_project_pane.html.haml' do
- element :import_github, "icon('github', text: 'GitHub')"
+ element :import_github, "icon('github', text: 'GitHub')" # rubocop:disable QA/ElementWithPattern
end
def choose_test_namespace
@@ -32,7 +33,7 @@ module QA
end
def choose_name(name)
- fill_in 'project_path', with: name
+ fill_in 'project_name', with: name
end
def add_description(description)
diff --git a/qa/qa/page/project/operations/environments/index.rb b/qa/qa/page/project/operations/environments/index.rb
new file mode 100644
index 00000000000..63965a57edd
--- /dev/null
+++ b/qa/qa/page/project/operations/environments/index.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Operations
+ module Environments
+ class Index < Page::Base
+ view 'app/assets/javascripts/environments/components/environment_item.vue' do
+ element :environment_link
+ end
+
+ def go_to_environment(environment_name)
+ wait(reload: false) do
+ find(element_selector_css(:environment_link), text: environment_name).click
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/operations/environments/show.rb b/qa/qa/page/project/operations/environments/show.rb
new file mode 100644
index 00000000000..aa88c218c89
--- /dev/null
+++ b/qa/qa/page/project/operations/environments/show.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Operations
+ module Environments
+ class Show < Page::Base
+ view 'app/views/projects/environments/_external_url.html.haml' do
+ element :view_deployment
+ end
+
+ def view_deployment(&block)
+ new_window = window_opened_by { click_element(:view_deployment) }
+
+ within_window(new_window, &block) if block
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/operations/kubernetes/add.rb b/qa/qa/page/project/operations/kubernetes/add.rb
index 11ebe10fb18..939f912ea85 100644
--- a/qa/qa/page/project/operations/kubernetes/add.rb
+++ b/qa/qa/page/project/operations/kubernetes/add.rb
@@ -4,8 +4,8 @@ module QA
module Operations
module Kubernetes
class Add < Page::Base
- view 'app/views/projects/clusters/new.html.haml' do
- element :add_existing_cluster_button, "Add existing cluster"
+ view 'app/views/clusters/clusters/new.html.haml' do
+ element :add_existing_cluster_button, "Add existing cluster" # rubocop:disable QA/ElementWithPattern
end
def add_existing_cluster
diff --git a/qa/qa/page/project/operations/kubernetes/add_existing.rb b/qa/qa/page/project/operations/kubernetes/add_existing.rb
index eef82b5f329..f3ab636ecc1 100644
--- a/qa/qa/page/project/operations/kubernetes/add_existing.rb
+++ b/qa/qa/page/project/operations/kubernetes/add_existing.rb
@@ -4,12 +4,13 @@ module QA
module Operations
module Kubernetes
class AddExisting < Page::Base
- view 'app/views/projects/clusters/user/_form.html.haml' do
- element :cluster_name, 'text_field :name'
- element :api_url, 'text_field :api_url'
- element :ca_certificate, 'text_area :ca_cert'
- element :token, 'text_field :token'
- element :add_cluster_button, "submit s_('ClusterIntegration|Add Kubernetes cluster')"
+ view 'app/views/clusters/clusters/user/_form.html.haml' do
+ element :cluster_name, 'text_field :name' # rubocop:disable QA/ElementWithPattern
+ element :api_url, 'text_field :api_url' # rubocop:disable QA/ElementWithPattern
+ element :ca_certificate, 'text_area :ca_cert' # rubocop:disable QA/ElementWithPattern
+ element :token, 'text_field :token' # rubocop:disable QA/ElementWithPattern
+ element :add_cluster_button, "submit s_('ClusterIntegration|Add Kubernetes cluster')" # rubocop:disable QA/ElementWithPattern
+ element :rbac_checkbox
end
def set_cluster_name(name)
@@ -31,6 +32,10 @@ module QA
def add_cluster!
click_on 'Add Kubernetes cluster'
end
+
+ def check_rbac!
+ check_element :rbac_checkbox
+ end
end
end
end
diff --git a/qa/qa/page/project/operations/kubernetes/index.rb b/qa/qa/page/project/operations/kubernetes/index.rb
index 7261b5645da..67a74af1cd2 100644
--- a/qa/qa/page/project/operations/kubernetes/index.rb
+++ b/qa/qa/page/project/operations/kubernetes/index.rb
@@ -4,8 +4,8 @@ module QA
module Operations
module Kubernetes
class Index < Page::Base
- view 'app/views/projects/clusters/_empty_state.html.haml' do
- element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Add Kubernetes cluster')"
+ view 'app/views/clusters/clusters/_empty_state.html.haml' do
+ element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Add Kubernetes cluster')" # rubocop:disable QA/ElementWithPattern
end
def add_kubernetes_cluster
diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb
index e831edeb89e..9e8f9ba79d7 100644
--- a/qa/qa/page/project/operations/kubernetes/show.rb
+++ b/qa/qa/page/project/operations/kubernetes/show.rb
@@ -5,13 +5,13 @@ module QA
module Kubernetes
class Show < Page::Base
view 'app/assets/javascripts/clusters/components/application_row.vue' do
- element :application_row, 'js-cluster-application-row-${this.id}'
- element :install_button, "s__('ClusterIntegration|Install')"
- element :installed_button, "s__('ClusterIntegration|Installed')"
+ element :application_row, 'js-cluster-application-row-${this.id}' # rubocop:disable QA/ElementWithPattern
+ element :install_button, "s__('ClusterIntegration|Install')" # rubocop:disable QA/ElementWithPattern
+ element :installed_button, "s__('ClusterIntegration|Installed')" # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/clusters/components/applications.vue' do
- element :ingress_ip_address, 'id="ingress-ip-address"'
+ element :ingress_ip_address, 'id="ingress-ip-address"' # rubocop:disable QA/ElementWithPattern
end
def install!(application_name)
diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb
index ce430a2a6ee..19d83ecc4f4 100644
--- a/qa/qa/page/project/pipeline/index.rb
+++ b/qa/qa/page/project/pipeline/index.rb
@@ -2,7 +2,7 @@ module QA::Page
module Project::Pipeline
class Index < QA::Page::Base
view 'app/assets/javascripts/pipelines/components/pipeline_url.vue' do
- element :pipeline_link, 'class="js-pipeline-url-link"'
+ element :pipeline_link, 'class="js-pipeline-url-link"' # rubocop:disable QA/ElementWithPattern
end
def go_to_latest_pipeline
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index babc0079f3f..b22396fd67a 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -2,20 +2,20 @@ module QA::Page
module Project::Pipeline
class Show < QA::Page::Base
view 'app/assets/javascripts/vue_shared/components/header_ci_component.vue' do
- element :pipeline_header, /header class.*ci-header-container.*/
+ element :pipeline_header, /header class.*ci-header-container.*/ # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/pipelines/components/graph/graph_component.vue' do
- element :pipeline_graph, /class.*pipeline-graph.*/
+ element :pipeline_graph, /class.*pipeline-graph.*/ # rubocop:disable QA/ElementWithPattern
end
- view 'app/assets/javascripts/pipelines/components/graph/job_component.vue' do
- element :job_component, /class.*ci-job-component.*/
- element :job_link, /class.*js-pipeline-graph-job-link.*/
+ view 'app/assets/javascripts/pipelines/components/graph/job_item.vue' do
+ element :job_component, /class.*ci-job-component.*/ # rubocop:disable QA/ElementWithPattern
+ element :job_link, /class.*js-pipeline-graph-job-link.*/ # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do
- element :status_icon, 'ci-status-icon-${status}'
+ element :status_icon, 'ci-status-icon-${status}' # rubocop:disable QA/ElementWithPattern
end
def running?
diff --git a/qa/qa/page/project/settings/advanced.rb b/qa/qa/page/project/settings/advanced.rb
index d7b2b66b587..578f097e2dc 100644
--- a/qa/qa/page/project/settings/advanced.rb
+++ b/qa/qa/page/project/settings/advanced.rb
@@ -4,9 +4,9 @@ module QA
module Settings
class Advanced < Page::Base
view 'app/views/projects/edit.html.haml' do
- element :project_path_field, 'text_field :path'
- element :project_name_field, 'text_field :name'
- element :rename_project_button, "submit 'Rename project'"
+ element :project_path_field, 'text_field :path' # rubocop:disable QA/ElementWithPattern
+ element :project_name_field, 'text_field :name' # rubocop:disable QA/ElementWithPattern
+ element :rename_project_button, "submit 'Rename project'" # rubocop:disable QA/ElementWithPattern
end
def rename_to(path)
diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb
index 752d3d93407..12c2409a5a7 100644
--- a/qa/qa/page/project/settings/ci_cd.rb
+++ b/qa/qa/page/project/settings/ci_cd.rb
@@ -12,11 +12,11 @@ module QA # rubocop:disable Naming/FileName
end
view 'app/views/projects/settings/ci_cd/_autodevops_form.html.haml' do
- element :enable_auto_devops_field, 'check_box :enabled'
- element :domain_field, 'text_field :domain'
- element :enable_auto_devops_button, "%strong= s_('CICD|Default to Auto DevOps pipeline')"
- element :domain_input, "%strong= _('Domain')"
- element :save_changes_button, "submit _('Save changes')"
+ element :enable_auto_devops_field, 'check_box :enabled' # rubocop:disable QA/ElementWithPattern
+ element :domain_field, 'text_field :domain' # rubocop:disable QA/ElementWithPattern
+ element :enable_auto_devops_button, "%strong= s_('CICD|Default to Auto DevOps pipeline')" # rubocop:disable QA/ElementWithPattern
+ element :domain_input, "%strong= _('Domain')" # rubocop:disable QA/ElementWithPattern
+ element :save_changes_button, "submit _('Save changes')" # rubocop:disable QA/ElementWithPattern
end
def expand_runners_settings(&block)
@@ -25,9 +25,9 @@ module QA # rubocop:disable Naming/FileName
end
end
- def expand_secret_variables(&block)
+ def expand_ci_variables(&block)
expand_section(:variables_settings) do
- Settings::SecretVariables.perform(&block)
+ Settings::CiVariables.perform(&block)
end
end
diff --git a/qa/qa/page/project/settings/ci_variables.rb b/qa/qa/page/project/settings/ci_variables.rb
new file mode 100644
index 00000000000..e7a6e4bf628
--- /dev/null
+++ b/qa/qa/page/project/settings/ci_variables.rb
@@ -0,0 +1,52 @@
+module QA
+ module Page
+ module Project
+ module Settings
+ class CiVariables < Page::Base
+ include Common
+
+ view 'app/views/ci/variables/_variable_row.html.haml' do
+ element :variable_row, '.ci-variable-row-body' # rubocop:disable QA/ElementWithPattern
+ element :variable_key, '.qa-ci-variable-input-key' # rubocop:disable QA/ElementWithPattern
+ element :variable_value, '.qa-ci-variable-input-value' # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/views/ci/variables/_index.html.haml' do
+ element :save_variables, '.js-ci-variables-save-button' # rubocop:disable QA/ElementWithPattern
+ element :reveal_values, '.js-secret-value-reveal-button' # rubocop:disable QA/ElementWithPattern
+ end
+
+ def fill_variable(key, value)
+ keys = all_elements(:ci_variable_input_key)
+ index = keys.size - 1
+
+ # After we fill the key, JS would generate another field so
+ # we need to use the same index to find the corresponding one.
+ keys[index].set(key)
+ node = all_elements(:ci_variable_input_value)[index]
+
+ # Simply run `node.set(value)` is too slow for long text here,
+ # so we need to run JavaScript directly to set the value.
+ # The code was inspired from:
+ # https://github.com/teamcapybara/capybara/blob/679548cea10773d45e32808f4d964377cfe5e892/lib/capybara/selenium/node.rb#L217
+ execute_script("arguments[0].value = #{value.to_json}", node)
+ end
+
+ def save_variables
+ find('.js-ci-variables-save-button').click
+ end
+
+ def reveal_variables
+ find('.js-secret-value-reveal-button').click
+ end
+
+ def variable_value(key)
+ within('.ci-variable-row-body', text: key) do
+ find('.qa-ci-variable-input-value').value
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/common.rb b/qa/qa/page/project/settings/common.rb
index 874fb381554..f3b217677f2 100644
--- a/qa/qa/page/project/settings/common.rb
+++ b/qa/qa/page/project/settings/common.rb
@@ -8,7 +8,7 @@ module QA
def self.included(base)
base.class_eval do
view 'app/views/projects/edit.html.haml' do
- element :advanced_settings_expand, "= expanded ? 'Collapse' : 'Expand'"
+ element :advanced_settings_expand, "= expanded ? 'Collapse' : 'Expand'" # rubocop:disable QA/ElementWithPattern
end
end
end
diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb
index 90a0e7092bd..3c8c0cbdf7c 100644
--- a/qa/qa/page/project/settings/deploy_keys.rb
+++ b/qa/qa/page/project/settings/deploy_keys.rb
@@ -4,18 +4,18 @@ module QA
module Settings
class DeployKeys < Page::Base
view 'app/views/projects/deploy_keys/_form.html.haml' do
- element :deploy_key_title, 'text_field :title'
- element :deploy_key_key, 'text_area :key'
+ element :deploy_key_title, 'text_field :title' # rubocop:disable QA/ElementWithPattern
+ element :deploy_key_key, 'text_area :key' # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/deploy_keys/components/app.vue' do
- element :deploy_keys_section, /class=".*deploy\-keys.*"/
- element :project_deploy_keys, 'class="qa-project-deploy-keys"'
+ element :deploy_keys_section, /class=".*deploy\-keys.*"/ # rubocop:disable QA/ElementWithPattern
+ element :project_deploy_keys, 'class="qa-project-deploy-keys"' # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/deploy_keys/components/key.vue' do
- element :key_title, /class=".*qa-key-title.*"/
- element :key_fingerprint, /class=".*qa-key-fingerprint.*"/
+ element :key_title, /class=".*qa-key-title.*"/ # rubocop:disable QA/ElementWithPattern
+ element :key_fingerprint, /class=".*qa-key-fingerprint.*"/ # rubocop:disable QA/ElementWithPattern
end
def fill_key_title(title)
diff --git a/qa/qa/page/project/settings/deploy_tokens.rb b/qa/qa/page/project/settings/deploy_tokens.rb
new file mode 100644
index 00000000000..2d42372cbc5
--- /dev/null
+++ b/qa/qa/page/project/settings/deploy_tokens.rb
@@ -0,0 +1,64 @@
+module QA
+ module Page
+ module Project
+ module Settings
+ class DeployTokens < Page::Base
+ view 'app/views/projects/deploy_tokens/_form.html.haml' do
+ element :deploy_token_name
+ element :deploy_token_expires_at
+ element :deploy_token_read_repository
+ element :deploy_token_read_registry
+ element :create_deploy_token
+ end
+
+ view 'app/views/projects/deploy_tokens/_new_deploy_token.html.haml' do
+ element :created_deploy_token_section
+ element :deploy_token_user
+ element :deploy_token
+ end
+
+ def fill_token_name(name)
+ fill_element :deploy_token_name, name
+ end
+
+ def fill_token_expires_at(expires_at)
+ fill_element :deploy_token_expires_at, expires_at.to_s + "\n"
+ end
+
+ def fill_scopes(read_repository:, read_registry:)
+ check_element :deploy_token_read_repository if read_repository
+ check_element :deploy_token_read_registry if read_registry
+ end
+
+ def add_token
+ click_element :create_deploy_token
+ end
+
+ def token_username
+ within_new_project_deploy_token do
+ find_element(:deploy_token_user).value
+ end
+ end
+
+ def token_password
+ within_new_project_deploy_token do
+ find_element(:deploy_token).value
+ end
+ end
+
+ private
+
+ def within_new_project_deploy_token
+ wait(reload: false) do
+ has_css?(element_selector_css(:created_deploy_token_section))
+ end
+
+ within_element(:created_deploy_token_section) do
+ yield
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/members.rb b/qa/qa/page/project/settings/members.rb
new file mode 100644
index 00000000000..7fed93ca83f
--- /dev/null
+++ b/qa/qa/page/project/settings/members.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Settings
+ class Members < Page::Base
+ include Page::Component::UsersSelect
+
+ view 'app/views/projects/project_members/_new_project_member.html.haml' do
+ element :member_select_input
+ element :add_member_button
+ end
+
+ view 'app/views/projects/project_members/_team.html.haml' do
+ element :members_list
+ end
+
+ def add_member(username)
+ select_user :member_select_input, username
+ click_element :add_member_button
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb
index 1ed5f455a85..53ebe28970b 100644
--- a/qa/qa/page/project/settings/repository.rb
+++ b/qa/qa/page/project/settings/repository.rb
@@ -24,6 +24,12 @@ module QA
ProtectedBranches.perform(&block)
end
end
+
+ def expand_deploy_tokens(&block)
+ expand_section(:deploy_tokens_settings) do
+ DeployTokens.perform(&block)
+ end
+ end
end
end
end
diff --git a/qa/qa/page/project/settings/runners.rb b/qa/qa/page/project/settings/runners.rb
index b41668c94cd..ac930f5385a 100644
--- a/qa/qa/page/project/settings/runners.rb
+++ b/qa/qa/page/project/settings/runners.rb
@@ -4,8 +4,8 @@ module QA
module Settings
class Runners < Page::Base
view 'app/views/ci/runner/_how_to_setup_runner.html.haml' do
- element :registration_token, '%code#registration_token'
- element :coordinator_address, '%code#coordinator_address'
+ element :registration_token, '%code#registration_token' # rubocop:disable QA/ElementWithPattern
+ element :coordinator_address, '%code#coordinator_address' # rubocop:disable QA/ElementWithPattern
end
##
@@ -13,7 +13,7 @@ module QA
#
view 'app/helpers/runners_helper.rb' do
# rubocop:disable Lint/InterpolationCheck
- element :runner_status, 'runner-status-#{status}'
+ element :runner_status, 'runner-status-#{status}' # rubocop:disable QA/ElementWithPattern
# rubocop:enable Lint/InterpolationCheck
end
diff --git a/qa/qa/page/project/settings/secret_variables.rb b/qa/qa/page/project/settings/secret_variables.rb
deleted file mode 100644
index d2f5d5a9060..00000000000
--- a/qa/qa/page/project/settings/secret_variables.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-module QA
- module Page
- module Project
- module Settings
- class SecretVariables < Page::Base
- include Common
-
- view 'app/views/ci/variables/_variable_row.html.haml' do
- element :variable_row, '.ci-variable-row-body'
- element :variable_key, '.qa-ci-variable-input-key'
- element :variable_value, '.qa-ci-variable-input-value'
- end
-
- view 'app/views/ci/variables/_index.html.haml' do
- element :save_variables, '.js-secret-variables-save-button'
- element :reveal_values, '.js-secret-value-reveal-button'
- end
-
- def fill_variable(key, value)
- keys = all_elements(:ci_variable_input_key)
- index = keys.size - 1
-
- # After we fill the key, JS would generate another field so
- # we need to use the same index to find the corresponding one.
- keys[index].set(key)
- all_elements(:ci_variable_input_value)[index].set(value)
- end
-
- def save_variables
- find('.js-secret-variables-save-button').click
- end
-
- def reveal_variables
- find('.js-secret-value-reveal-button').click
- end
-
- def variable_value(key)
- within('.ci-variable-row-body', text: key) do
- find('.qa-ci-variable-input-value').value
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index c751b472535..d6dddf03ffb 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
module QA
module Page
module Project
class Show < Page::Base
- include Page::Shared::ClonePanel
+ include Page::Component::ClonePanel
view 'app/views/projects/_last_push.html.haml' do
element :create_merge_request
@@ -14,7 +16,7 @@ module QA
view 'app/views/layouts/header/_new_dropdown.haml' do
element :new_menu_toggle
- element :new_issue_link, "link_to _('New issue'), new_project_issue_path(@project)"
+ element :new_issue_link, "link_to _('New issue'), new_project_issue_path(@project)" # rubocop:disable QA/ElementWithPattern
end
view 'app/views/shared/_ref_switcher.html.haml' do
@@ -23,24 +25,40 @@ module QA
end
view 'app/views/projects/buttons/_fork.html.haml' do
- element :fork_label, "%span= s_('GoToYourFork|Fork')"
- element :fork_link, "link_to new_project_fork_path(@project)"
+ element :fork_label, "%span= s_('ProjectOverview|Fork')" # rubocop:disable QA/ElementWithPattern
+ element :fork_link, "link_to new_project_fork_path(@project)" # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/_files.html.haml' do
- element :tree_holder, '.tree-holder'
+ element :tree_holder, '.tree-holder' # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/views/projects/buttons/_dropdown.html.haml' do
+ element :create_new_dropdown
+ element :new_file_option
+ end
+
+ view 'app/views/projects/tree/_tree_header.html.haml' do
+ element :web_ide_button
end
- view 'app/presenters/project_presenter.rb' do
- element :new_file_button, "label: _('New file'),"
+ view 'app/views/projects/tree/_tree_content.html.haml' do
+ element :file_tree
end
def project_name
find('.qa-project-name').text
end
- def go_to_new_file!
- click_on 'New file'
+ def create_new_file!
+ click_element :create_new_dropdown
+ click_element :new_file_option
+ end
+
+ def go_to_file(filename)
+ within_element(:file_tree) do
+ click_on filename
+ end
end
def switch_to_branch(branch_name)
@@ -78,6 +96,10 @@ module QA
def fork_project
click_on 'Fork'
end
+
+ def open_web_ide!
+ click_element :web_ide_button
+ end
end
end
end
diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb
new file mode 100644
index 00000000000..23e580b81b6
--- /dev/null
+++ b/qa/qa/page/project/web_ide/edit.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module WebIDE
+ class Edit < Page::Base
+ include Page::Component::DropdownFilter
+
+ view 'app/assets/javascripts/ide/components/ide_tree.vue' do
+ element :new_file
+ end
+
+ view 'app/assets/javascripts/ide/components/ide_tree_list.vue' do
+ element :file_list
+ end
+
+ view 'app/assets/javascripts/ide/components/new_dropdown/modal.vue' do
+ element :full_file_path
+ element :template_list
+ end
+
+ view 'app/assets/javascripts/ide/components/file_templates/bar.vue' do
+ element :file_templates_bar
+ element :file_template_dropdown
+ end
+
+ view 'app/assets/javascripts/ide/components/file_templates/dropdown.vue' do
+ element :dropdown_filter_input
+ end
+
+ view 'app/assets/javascripts/ide/components/commit_sidebar/form.vue' do
+ element :begin_commit_button
+ element :commit_button
+ end
+
+ def has_file?(file_name)
+ within_element(:file_list) do
+ page.has_content? file_name
+ end
+ end
+
+ def create_new_file_from_template(file_name, template)
+ click_element :new_file
+ within_element(:template_list) do
+ begin
+ click_on file_name
+ rescue Capybara::ElementNotFound
+ raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
+ end
+ end
+
+ wait(reload: false) do
+ within_element(:file_templates_bar) do
+ click_element :file_template_dropdown
+ fill_element :dropdown_filter_input, template
+
+ begin
+ click_on template
+ rescue Capybara::ElementNotFound
+ raise ElementNotFound, %Q(Couldn't find template "#{template}" for #{file_name}. Please confirm that it exists in the list of templates.)
+ end
+ end
+ end
+ end
+
+ def commit_changes
+ click_element :begin_commit_button
+ click_element :commit_button
+
+ wait(reload: false) do
+ page.has_content?('Your changes have been committed')
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/wiki/edit.rb b/qa/qa/page/project/wiki/edit.rb
index 6fa45569cc0..8d0eafa1818 100644
--- a/qa/qa/page/project/wiki/edit.rb
+++ b/qa/qa/page/project/wiki/edit.rb
@@ -4,9 +4,9 @@ module QA
module Wiki
class Edit < Page::Base
view 'app/views/projects/wikis/_main_links.html.haml' do
- element :new_page_link, 'New page'
- element :page_history_link, 'Page history'
- element :edit_page_link, 'Edit'
+ element :new_page_link, 'New page' # rubocop:disable QA/ElementWithPattern
+ element :page_history_link, 'Page history' # rubocop:disable QA/ElementWithPattern
+ element :edit_page_link, 'Edit' # rubocop:disable QA/ElementWithPattern
end
def go_to_new_page
diff --git a/qa/qa/page/project/wiki/new.rb b/qa/qa/page/project/wiki/new.rb
index 415b3835538..2498af8600c 100644
--- a/qa/qa/page/project/wiki/new.rb
+++ b/qa/qa/page/project/wiki/new.rb
@@ -4,15 +4,15 @@ module QA
module Wiki
class New < Page::Base
view 'app/views/projects/wikis/_form.html.haml' do
- element :wiki_title_textbox, 'text_field :title'
- element :wiki_content_textarea, "render 'projects/zen', f: f, attr: :content"
- element :wiki_message_textbox, 'text_field :message'
- element :save_changes_button, 'submit _("Save changes")'
- element :create_page_button, 'submit s_("Wiki|Create page")'
+ element :wiki_title_textbox, 'text_field :title' # rubocop:disable QA/ElementWithPattern
+ element :wiki_content_textarea, "render 'projects/zen', f: f, attr: :content" # rubocop:disable QA/ElementWithPattern
+ element :wiki_message_textbox, 'text_field :message' # rubocop:disable QA/ElementWithPattern
+ element :save_changes_button, 'submit _("Save changes")' # rubocop:disable QA/ElementWithPattern
+ element :create_page_button, 'submit s_("Wiki|Create page")' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/shared/empty_states/_wikis.html.haml' do
- element :create_link, 'Create your first page'
+ element :create_link, 'Create your first page' # rubocop:disable QA/ElementWithPattern
end
def go_to_create_first_page
diff --git a/qa/qa/page/project/wiki/show.rb b/qa/qa/page/project/wiki/show.rb
index 044e514bab3..a7c4455d080 100644
--- a/qa/qa/page/project/wiki/show.rb
+++ b/qa/qa/page/project/wiki/show.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
module QA
module Page
module Project
module Wiki
class Show < Page::Base
- include Page::Shared::ClonePanel
+ include Page::Component::ClonePanel
view 'app/views/projects/wikis/pages.html.haml' do
- element :clone_repository_link, 'Clone repository'
+ element :clone_repository_link, 'Clone repository' # rubocop:disable QA/ElementWithPattern
end
def go_to_clone_repository
diff --git a/qa/qa/page/shared/clone_panel.rb b/qa/qa/page/shared/clone_panel.rb
deleted file mode 100644
index 73e3dff956d..00000000000
--- a/qa/qa/page/shared/clone_panel.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-module QA
- module Page
- module Shared
- module ClonePanel
- def self.included(base)
- base.view 'app/views/shared/_clone_panel.html.haml' do
- element :clone_dropdown
- element :clone_options_dropdown, '.clone-options-dropdown'
- element :project_repository_location, 'text_field_tag :project_clone'
- end
- end
-
- def choose_repository_clone_http
- choose_repository_clone('HTTP', 'http')
- end
-
- def choose_repository_clone_ssh
- # It's not always beginning with ssh:// so detecting with @
- # would be more reliable because ssh would always contain it.
- # We can't use .git because HTTP also contain that part.
- choose_repository_clone('SSH', '@')
- end
-
- def repository_location
- Git::Location.new(find('#project_clone').value)
- end
-
- def wait_for_push
- sleep 5
- refresh
- end
-
- private
-
- def choose_repository_clone(kind, detect_text)
- wait(reload: false) do
- click_element :clone_dropdown
-
- page.within('.clone-options-dropdown') do
- click_link(kind)
- end
-
- # Ensure git clone textbox was updated
- repository_location.git_uri.include?(detect_text)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/view.rb b/qa/qa/page/view.rb
index b2a2da4dbf3..c59fad2e223 100644
--- a/qa/qa/page/view.rb
+++ b/qa/qa/page/view.rb
@@ -1,3 +1,5 @@
+require 'pathname'
+
module QA
module Page
class View
@@ -9,7 +11,7 @@ module QA
end
def pathname
- @pathname ||= Pathname.new(::File.join(__dir__, '../../../', @path))
+ @pathname ||= ::Pathname.new(::File.join(__dir__, '../../../', @path))
.cleanpath.expand_path
end
diff --git a/qa/qa/resource/README.md b/qa/qa/resource/README.md
new file mode 100644
index 00000000000..4cdeb3f42a2
--- /dev/null
+++ b/qa/qa/resource/README.md
@@ -0,0 +1,392 @@
+# Resource class in GitLab QA
+
+Resources are primarily created using Browser UI steps, but can also
+be created via the API.
+
+## How to properly implement a resource class?
+
+All resource classes should inherit from [`Resource::Base`](./base.rb).
+
+There is only one mandatory method to implement to define a resource class.
+This is the `#fabricate!` method, which is used to build the resource via the
+browser UI. Note that you should only use [Page objects](../page/README.md) to
+interact with a Web page in this method.
+
+Here is an imaginary example:
+
+```ruby
+module QA
+ module Resource
+ class Shirt < Base
+ attr_accessor :name
+
+ def fabricate!
+ Page::Dashboard::Index.perform do |dashboard_index|
+ dashboard_index.go_to_new_shirt
+ end
+
+ Page::Shirt::New.perform do |shirt_new|
+ shirt_new.set_name(name)
+ shirt_new.create_shirt!
+ end
+ end
+ end
+ end
+end
+```
+
+### Define API implementation
+
+A resource class may also implement the three following methods to be able to
+create the resource via the public GitLab API:
+
+- `#api_get_path`: The `GET` path to fetch an existing resource.
+- `#api_post_path`: The `POST` path to create a new resource.
+- `#api_post_body`: The `POST` body (as a Ruby hash) to create a new resource.
+
+Let's take the `Shirt` resource class, and add these three API methods:
+
+```ruby
+module QA
+ module Resource
+ class Shirt < Base
+ attr_accessor :name
+
+ def fabricate!
+ # ... same as before
+ end
+
+ def api_get_path
+ "/shirt/#{name}"
+ end
+
+ def api_post_path
+ "/shirts"
+ end
+
+ def api_post_body
+ {
+ name: name
+ }
+ end
+ end
+ end
+end
+```
+
+The [`Project` resource](./project.rb) is a good real example of Browser
+UI and API implementations.
+
+#### Resource attributes
+
+A resource may need another resource to exist first. For instance, a project
+needs a group to be created in.
+
+To define a resource attribute, you can use the `attribute` method with a
+block using the other resource class to fabricate the resource.
+
+That will allow access to the other resource from your resource object's
+methods. You would usually use it in `#fabricate!`, `#api_get_path`,
+`#api_post_path`, `#api_post_body`.
+
+Let's take the `Shirt` resource class, and add a `project` attribute to it:
+
+```ruby
+module QA
+ module Resource
+ class Shirt < Base
+ attr_accessor :name
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-to-create-a-shirt'
+ end
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.perform do |project_show|
+ project_show.go_to_new_shirt
+ end
+
+ Page::Shirt::New.perform do |shirt_new|
+ shirt_new.set_name(name)
+ shirt_new.create_shirt!
+ end
+ end
+
+ def api_get_path
+ "/project/#{project.path}/shirt/#{name}"
+ end
+
+ def api_post_path
+ "/project/#{project.path}/shirts"
+ end
+
+ def api_post_body
+ {
+ name: name
+ }
+ end
+ end
+ end
+end
+```
+
+**Note that all the attributes are lazily constructed. This means if you want
+a specific attribute to be fabricated first, you'll need to call the
+attribute method first even if you're not using it.**
+
+#### Product data attributes
+
+Once created, you may want to populate a resource with attributes that can be
+found in the Web page, or in the API response.
+For instance, once you create a project, you may want to store its repository
+SSH URL as an attribute.
+
+Again we could use the `attribute` method with a block, using a page object
+to retrieve the data on the page.
+
+Let's take the `Shirt` resource class, and define a `:brand` attribute:
+
+```ruby
+module QA
+ module Resource
+ class Shirt < Base
+ attr_accessor :name
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-to-create-a-shirt'
+ end
+ end
+
+ # Attribute populated from the Browser UI (using the block)
+ attribute :brand do
+ Page::Shirt::Show.perform do |shirt_show|
+ shirt_show.fetch_brand_from_page
+ end
+ end
+
+ # ... same as before
+ end
+ end
+end
+```
+
+**Note again that all the attributes are lazily constructed. This means if
+you call `shirt.brand` after moving to the other page, it'll not properly
+retrieve the data because we're no longer on the expected page.**
+
+Consider this:
+
+```ruby
+shirt =
+ QA::Resource::Shirt.fabricate! do |resource|
+ resource.name = "GitLab QA"
+ end
+
+shirt.project.visit!
+
+shirt.brand # => FAIL!
+```
+
+The above example will fail because now we're on the project page, trying to
+construct the brand data from the shirt page, however we moved to the project
+page already. There are two ways to solve this, one is that we could try to
+retrieve the brand before visiting the project again:
+
+```ruby
+shirt =
+ QA::Resource::Shirt.fabricate! do |resource|
+ resource.name = "GitLab QA"
+ end
+
+shirt.brand # => OK!
+
+shirt.project.visit!
+
+shirt.brand # => OK!
+```
+
+The attribute will be stored in the instance therefore all the following calls
+will be fine, using the data previously constructed. If we think that this
+might be too brittle, we could eagerly construct the data right before
+ending fabrication:
+
+```ruby
+module QA
+ module Resource
+ class Shirt < Base
+ # ... same as before
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.perform do |project_show|
+ project_show.go_to_new_shirt
+ end
+
+ Page::Shirt::New.perform do |shirt_new|
+ shirt_new.set_name(name)
+ shirt_new.create_shirt!
+ end
+
+ populate(:brand) # Eagerly construct the data
+ end
+ end
+ end
+end
+```
+
+The `populate` method will iterate through its arguments and call each
+attribute respectively. Here `populate(:brand)` has the same effect as
+just `brand`. Using the populate method makes the intention clearer.
+
+With this, it will make sure we construct the data right after we create the
+shirt. The drawback is that this will always construct the data when the
+resource is fabricated even if we don't need to use the data.
+
+Alternatively, we could just make sure we're on the right page before
+constructing the brand data:
+
+```ruby
+module QA
+ module Resource
+ class Shirt < Base
+ attr_accessor :name
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-to-create-a-shirt'
+ end
+ end
+
+ # Attribute populated from the Browser UI (using the block)
+ attribute :brand do
+ back_url = current_url
+ visit!
+
+ Page::Shirt::Show.perform do |shirt_show|
+ shirt_show.fetch_brand_from_page
+ end
+
+ visit(back_url)
+ end
+
+ # ... same as before
+ end
+ end
+end
+```
+
+This will make sure it's on the shirt page before constructing brand, and
+move back to the previous page to avoid breaking the state.
+
+#### Define an attribute based on an API response
+
+Sometimes, you want to define a resource attribute based on the API response
+from its `GET` or `POST` request. For instance, if the creation of a shirt via
+the API returns
+
+```ruby
+{
+ brand: 'a-brand-new-brand',
+ style: 't-shirt',
+ materials: [[:cotton, 80], [:polyamide, 20]]
+}
+```
+
+you may want to store `style` as-is in the resource, and fetch the first value
+of the first `materials` item in a `main_fabric` attribute.
+
+Let's take the `Shirt` resource class, and define a `:style` and a
+`:main_fabric` attributes:
+
+```ruby
+module QA
+ module Resource
+ class Shirt < Base
+ # ... same as before
+
+ # @style from the instance if present,
+ # or fetched from the API response if present,
+ # or a QA::Resource::Base::NoValueError is raised otherwise
+ attribute :style
+
+ # If @main_fabric is not present,
+ # and if the API does not contain this field, this block will be
+ # used to construct the value based on the API response, and
+ # store the result in @main_fabric
+ attribute :main_fabric do
+ api_response.&dig(:materials, 0, 0)
+ end
+
+ # ... same as before
+ end
+ end
+end
+```
+
+**Notes on attributes precedence:**
+
+- resource instance variables have the highest precedence
+- attributes from the API response take precedence over attributes from the
+ block (usually from Browser UI)
+- attributes without a value will raise a `QA::Resource::Base::NoValueError` error
+
+## Creating resources in your tests
+
+To create a resource in your tests, you can call the `.fabricate!` method on
+the resource class.
+Note that if the resource class supports API fabrication, this will use this
+fabrication by default.
+
+Here is an example that will use the API fabrication method under the hood
+since it's supported by the `Shirt` resource class:
+
+```ruby
+my_shirt = Resource::Shirt.fabricate! do |shirt|
+ shirt.name = 'my-shirt'
+end
+
+expect(page).to have_text(my_shirt.name) # => "my-shirt" from the resource's instance variable
+expect(page).to have_text(my_shirt.brand) # => "a-brand-new-brand" from the API response
+expect(page).to have_text(my_shirt.style) # => "t-shirt" from the API response
+expect(page).to have_text(my_shirt.main_fabric) # => "cotton" from the API response via the block
+```
+
+If you explicitly want to use the Browser UI fabrication method, you can call
+the `.fabricate_via_browser_ui!` method instead:
+
+```ruby
+my_shirt = Resource::Shirt.fabricate_via_browser_ui! do |shirt|
+ shirt.name = 'my-shirt'
+end
+
+expect(page).to have_text(my_shirt.name) # => "my-shirt" from the resource's instance variable
+expect(page).to have_text(my_shirt.brand) # => the brand name fetched from the `Page::Shirt::Show` page via the block
+expect(page).to have_text(my_shirt.style) # => QA::Resource::Base::NoValueError will be raised because no API response nor a block is provided
+expect(page).to have_text(my_shirt.main_fabric) # => QA::Resource::Base::NoValueError will be raised because no API response and the block didn't provide a value (because it's also based on the API response)
+```
+
+You can also explicitly use the API fabrication method, by calling the
+`.fabricate_via_api!` method:
+
+```ruby
+my_shirt = Resource::Shirt.fabricate_via_api! do |shirt|
+ shirt.name = 'my-shirt'
+end
+```
+
+In this case, the result will be similar to calling
+`Resource::Shirt.fabricate!`.
+
+## Where to ask for help?
+
+If you need more information, ask for help on `#quality` channel on Slack
+(internal, GitLab Team only).
+
+If you are not a Team Member, and you still need help to contribute, please
+open an issue in GitLab CE issue tracker with the `~QA` label.
diff --git a/qa/qa/resource/api_fabricator.rb b/qa/qa/resource/api_fabricator.rb
new file mode 100644
index 00000000000..3762a94f312
--- /dev/null
+++ b/qa/qa/resource/api_fabricator.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'airborne'
+require 'active_support/core_ext/object/deep_dup'
+require 'capybara/dsl'
+
+module QA
+ module Resource
+ module ApiFabricator
+ include Airborne
+ include Capybara::DSL
+
+ HTTP_STATUS_OK = 200
+ HTTP_STATUS_CREATED = 201
+
+ ResourceNotFoundError = Class.new(RuntimeError)
+ ResourceFabricationFailedError = Class.new(RuntimeError)
+ ResourceURLMissingError = Class.new(RuntimeError)
+
+ attr_reader :api_resource, :api_response
+
+ def api_support?
+ respond_to?(:api_get_path) &&
+ respond_to?(:api_post_path) &&
+ respond_to?(:api_post_body)
+ end
+
+ def fabricate_via_api!
+ unless api_support?
+ raise NotImplementedError, "Resource #{self.class.name} does not support fabrication via the API!"
+ end
+
+ resource_web_url(api_post)
+ end
+
+ def eager_load_api_client!
+ api_client.tap do |client|
+ # Eager-load the API client so that the personal token creation isn't
+ # taken in account in the actual resource creation timing.
+ client.personal_access_token
+ end
+ end
+
+ private
+
+ attr_writer :api_resource, :api_response
+
+ def resource_web_url(resource)
+ resource.fetch(:web_url) do
+ raise ResourceURLMissingError, "API resource for #{self.class.name} does not expose a `web_url` property: `#{resource}`."
+ end
+ end
+
+ def api_get
+ process_api_response(parse_body(api_get_from(api_get_path)))
+ end
+
+ def api_get_from(get_path)
+ url = Runtime::API::Request.new(api_client, get_path).url
+ response = get(url)
+
+ unless response.code == HTTP_STATUS_OK
+ raise ResourceNotFoundError, "Resource at #{url} could not be found (#{response.code}): `#{response}`."
+ end
+
+ response
+ end
+
+ def api_post
+ response = post(
+ Runtime::API::Request.new(api_client, api_post_path).url,
+ api_post_body)
+
+ unless response.code == HTTP_STATUS_CREATED
+ raise ResourceFabricationFailedError, "Fabrication of #{self.class.name} using the API failed (#{response.code}) with `#{response}`."
+ end
+
+ process_api_response(parse_body(response))
+ end
+
+ def api_client
+ @api_client ||= begin
+ Runtime::API::Client.new(:gitlab, is_new_session: !current_url.start_with?('http'))
+ end
+ end
+
+ def parse_body(response)
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ def process_api_response(parsed_response)
+ self.api_response = parsed_response
+ self.api_resource = transform_api_resource(parsed_response.deep_dup)
+ end
+
+ def transform_api_resource(api_resource)
+ api_resource
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb
new file mode 100644
index 00000000000..f3eefb70520
--- /dev/null
+++ b/qa/qa/resource/base.rb
@@ -0,0 +1,154 @@
+# frozen_string_literal: true
+
+require 'forwardable'
+require 'capybara/dsl'
+
+module QA
+ module Resource
+ class Base
+ extend SingleForwardable
+ include ApiFabricator
+ extend Capybara::DSL
+
+ NoValueError = Class.new(RuntimeError)
+
+ def_delegators :evaluator, :attribute
+
+ def fabricate!(*_args)
+ raise NotImplementedError
+ end
+
+ def visit!
+ visit(web_url)
+ end
+
+ def populate(*attributes)
+ attributes.each(&method(:public_send))
+ end
+
+ private
+
+ def populate_attribute(name, block)
+ value = attribute_value(name, block)
+
+ raise NoValueError, "No value was computed for #{name} of #{self.class.name}." unless value
+
+ value
+ end
+
+ def attribute_value(name, block)
+ api_value = api_resource&.dig(name)
+
+ if api_value && block
+ log_having_both_api_result_and_block(name, api_value)
+ end
+
+ api_value || (block && instance_exec(&block))
+ end
+
+ def log_having_both_api_result_and_block(name, api_value)
+ QA::Runtime::Logger.info "<#{self.class}> Attribute #{name.inspect} has both API response `#{api_value}` and a block. API response will be picked. Block will be ignored."
+ end
+
+ def self.fabricate!(*args, &prepare_block)
+ fabricate_via_api!(*args, &prepare_block)
+ rescue NotImplementedError
+ fabricate_via_browser_ui!(*args, &prepare_block)
+ end
+
+ def self.fabricate_via_browser_ui!(*args, &prepare_block)
+ options = args.extract_options!
+ resource = options.fetch(:resource) { new }
+ parents = options.fetch(:parents) { [] }
+
+ do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
+ log_fabrication(:browser_ui, resource, parents, args) { resource.fabricate!(*args) }
+
+ current_url
+ end
+ end
+
+ def self.fabricate_via_api!(*args, &prepare_block)
+ options = args.extract_options!
+ resource = options.fetch(:resource) { new }
+ parents = options.fetch(:parents) { [] }
+
+ raise NotImplementedError unless resource.api_support?
+
+ resource.eager_load_api_client!
+
+ do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
+ log_fabrication(:api, resource, parents, args) { resource.fabricate_via_api! }
+ end
+ end
+
+ def self.do_fabricate!(resource:, prepare_block:, parents: [])
+ prepare_block.call(resource) if prepare_block
+
+ resource_web_url = yield
+ resource.web_url = resource_web_url
+
+ resource
+ end
+ private_class_method :do_fabricate!
+
+ def self.log_fabrication(method, resource, parents, args)
+ return yield unless Runtime::Env.debug?
+
+ start = Time.now
+ prefix = "==#{'=' * parents.size}>"
+ msg = [prefix]
+ msg << "Built a #{name}"
+ msg << "as a dependency of #{parents.last}" if parents.any?
+ msg << "via #{method}"
+
+ yield.tap do
+ msg << "in #{Time.now - start} seconds"
+ puts msg.join(' ')
+ puts if parents.empty?
+ end
+ end
+ private_class_method :log_fabrication
+
+ def self.evaluator
+ @evaluator ||= Base::DSL.new(self)
+ end
+ private_class_method :evaluator
+
+ def self.dynamic_attributes
+ const_get(:DynamicAttributes)
+ rescue NameError
+ mod = const_set(:DynamicAttributes, Module.new)
+
+ include mod
+
+ mod
+ end
+
+ def self.attributes_names
+ dynamic_attributes.instance_methods(false).sort.grep_v(/=$/)
+ end
+
+ class DSL
+ def initialize(base)
+ @base = base
+ end
+
+ def attribute(name, &block)
+ @base.dynamic_attributes.module_eval do
+ attr_writer(name)
+
+ define_method(name) do
+ instance_variable_get("@#{name}") ||
+ instance_variable_set(
+ "@#{name}",
+ populate_attribute(name, block))
+ end
+ end
+ end
+ end
+
+ attribute :web_url
+ end
+ end
+end
diff --git a/qa/qa/resource/branch.rb b/qa/qa/resource/branch.rb
new file mode 100644
index 00000000000..bd52c4abe02
--- /dev/null
+++ b/qa/qa/resource/branch.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Branch < Base
+ attr_accessor :project, :branch_name,
+ :allow_to_push, :allow_to_merge, :protected
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'protected-branch-project'
+ end
+ end
+
+ def initialize
+ @branch_name = 'test/branch'
+ @allow_to_push = true
+ @allow_to_merge = true
+ @protected = false
+ end
+
+ def fabricate!
+ project.visit!
+
+ Repository::ProjectPush.fabricate! do |resource|
+ resource.project = project
+ resource.file_name = 'kick-off.txt'
+ resource.commit_message = 'First commit'
+ end
+
+ branch = Repository::ProjectPush.fabricate! do |resource|
+ resource.project = project
+ resource.file_name = 'README.md'
+ resource.commit_message = 'Add readme'
+ resource.branch_name = 'master'
+ resource.new_branch = false
+ resource.remote_branch = @branch_name
+ end
+
+ Page::Project::Show.perform do |page|
+ page.wait { page.has_content?(branch_name) }
+ end
+
+ # The upcoming process will make it access the Protected Branches page,
+ # select the already created branch and protect it according
+ # to `allow_to_push` variable.
+ return branch unless @protected
+
+ Page::Project::Menu.perform(&:click_repository_settings)
+
+ Page::Project::Settings::Repository.perform do |setting|
+ setting.expand_protected_branches do |page|
+ page.select_branch(branch_name)
+
+ if allow_to_push
+ page.allow_devs_and_maintainers_to_push
+ else
+ page.allow_no_one_to_push
+ end
+
+ if allow_to_merge
+ page.allow_devs_and_maintainers_to_merge
+ else
+ page.allow_no_one_to_merge
+ end
+
+ page.wait(reload: false) do
+ !page.first('.btn-success').disabled?
+ end
+
+ page.protect_branch
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/ci_variable.rb b/qa/qa/resource/ci_variable.rb
new file mode 100644
index 00000000000..0570c47d41c
--- /dev/null
+++ b/qa/qa/resource/ci_variable.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class CiVariable < Base
+ attr_accessor :key, :value
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-with-ci-variables'
+ resource.description = 'project for adding CI variable test'
+ end
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Menu.perform(&:click_ci_cd_settings)
+
+ Page::Project::Settings::CICD.perform do |setting|
+ setting.expand_ci_variables do |page|
+ page.fill_variable(key, value)
+
+ page.save_variables
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/deploy_key.rb b/qa/qa/resource/deploy_key.rb
new file mode 100644
index 00000000000..9ed8fb7726e
--- /dev/null
+++ b/qa/qa/resource/deploy_key.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class DeployKey < Base
+ attr_accessor :title, :key
+
+ attribute :fingerprint do
+ Page::Project::Settings::Repository.perform do |setting|
+ setting.expand_deploy_keys do |key|
+ key_offset = key.key_titles.index do |key_title|
+ key_title.text == title
+ end
+
+ key.key_fingerprints[key_offset].text
+ end
+ end
+ end
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-to-deploy'
+ resource.description = 'project for adding deploy key test'
+ end
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Menu.perform(&:click_repository_settings)
+
+ Page::Project::Settings::Repository.perform do |setting|
+ setting.expand_deploy_keys do |page|
+ page.fill_key_title(title)
+ page.fill_key_value(key)
+
+ page.add_key
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/deploy_token.rb b/qa/qa/resource/deploy_token.rb
new file mode 100644
index 00000000000..cee4422f6b4
--- /dev/null
+++ b/qa/qa/resource/deploy_token.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class DeployToken < Base
+ attr_accessor :name, :expires_at
+
+ attribute :username do
+ Page::Project::Settings::Repository.perform do |page|
+ page.expand_deploy_tokens do |token|
+ token.token_username
+ end
+ end
+ end
+
+ attribute :password do
+ Page::Project::Settings::Repository.perform do |page|
+ page.expand_deploy_tokens do |token|
+ token.token_password
+ end
+ end
+ end
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-to-deploy'
+ resource.description = 'project for adding deploy token test'
+ end
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Menu.act do
+ click_repository_settings
+ end
+
+ Page::Project::Settings::Repository.perform do |setting|
+ setting.expand_deploy_tokens do |page|
+ page.fill_token_name(name)
+ page.fill_token_expires_at(expires_at)
+ page.fill_scopes(read_repository: true, read_registry: false)
+
+ page.add_token
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/file.rb b/qa/qa/resource/file.rb
new file mode 100644
index 00000000000..effc5a7940b
--- /dev/null
+++ b/qa/qa/resource/file.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class File < Base
+ attr_accessor :name,
+ :content,
+ :commit_message
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-with-new-file'
+ end
+ end
+
+ def initialize
+ @name = 'QA Test - File name'
+ @content = 'QA Test - File content'
+ @commit_message = 'QA Test - Commit message'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.perform(&:create_new_file!)
+
+ Page::File::Form.perform do |page|
+ page.add_name(@name)
+ page.add_content(@content)
+ page.add_commit_message(@commit_message)
+ page.commit_changes
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/fork.rb b/qa/qa/resource/fork.rb
new file mode 100644
index 00000000000..9fd66f3a36a
--- /dev/null
+++ b/qa/qa/resource/fork.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Fork < Base
+ attribute :push do
+ Repository::ProjectPush.fabricate!
+ end
+
+ attribute :user do
+ User.fabricate! do |resource|
+ if Runtime::Env.forker?
+ resource.username = Runtime::Env.forker_username
+ resource.password = Runtime::Env.forker_password
+ end
+ end
+ end
+
+ def fabricate!
+ populate(:push, :user)
+
+ # Sign out as admin and sign is as the fork user
+ Page::Main::Menu.perform(&:sign_out)
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform do |login|
+ login.sign_in_using_credentials(user)
+ end
+
+ push.project.visit!
+
+ Page::Project::Show.perform(&:fork_project)
+
+ Page::Project::Fork::New.perform do |fork_new|
+ fork_new.choose_namespace(user.name)
+ end
+
+ Page::Layout::Banner.perform do |page|
+ page.has_notice?('The project was successfully forked.')
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
new file mode 100644
index 00000000000..dce15e4f10b
--- /dev/null
+++ b/qa/qa/resource/group.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Group < Base
+ attr_accessor :path, :description
+
+ attribute :sandbox do
+ Sandbox.fabricate!
+ end
+
+ attribute :id
+
+ def initialize
+ @path = Runtime::Namespace.name
+ @description = "QA test run at #{Runtime::Namespace.time}"
+ end
+
+ def fabricate!
+ sandbox.visit!
+
+ Page::Group::Show.perform do |group_show|
+ if group_show.has_subgroup?(path)
+ group_show.go_to_subgroup(path)
+ else
+ group_show.go_to_new_subgroup
+
+ Page::Group::New.perform do |group_new|
+ group_new.set_path(path)
+ group_new.set_description(description)
+ group_new.set_visibility('Public')
+ group_new.create
+ end
+
+ # Ensure that the group was actually created
+ group_show.wait(time: 1) do
+ group_show.has_text?(path) &&
+ group_show.has_new_project_or_subgroup_dropdown?
+ end
+ end
+ end
+ end
+
+ def fabricate_via_api!
+ resource_web_url(api_get)
+ rescue ResourceNotFoundError
+ super
+ end
+
+ def api_get_path
+ "/groups/#{CGI.escape("#{sandbox.path}/#{path}")}"
+ end
+
+ def api_post_path
+ '/groups'
+ end
+
+ def api_post_body
+ {
+ parent_id: sandbox.id,
+ path: path,
+ name: path,
+ visibility: 'public'
+ }
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb
new file mode 100644
index 00000000000..2c2f27fe231
--- /dev/null
+++ b/qa/qa/resource/issue.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Issue < Base
+ attr_writer :description
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-for-issues'
+ resource.description = 'project for adding issues'
+ end
+ end
+
+ attribute :title
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.perform(&:go_to_new_issue)
+
+ Page::Project::Issue::New.perform do |page|
+ page.add_title(@title)
+ page.add_description(@description)
+ page.create_new_issue
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/kubernetes_cluster.rb b/qa/qa/resource/kubernetes_cluster.rb
new file mode 100644
index 00000000000..96c8843fb99
--- /dev/null
+++ b/qa/qa/resource/kubernetes_cluster.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Resource
+ class KubernetesCluster < Base
+ attr_writer :project, :cluster,
+ :install_helm_tiller, :install_ingress, :install_prometheus, :install_runner
+
+ attribute :ingress_ip do
+ Page::Project::Operations::Kubernetes::Show.perform(&:ingress_ip)
+ end
+
+ def fabricate!
+ @project.visit!
+
+ Page::Project::Menu.perform(
+ &:click_operations_kubernetes)
+
+ Page::Project::Operations::Kubernetes::Index.perform(
+ &:add_kubernetes_cluster)
+
+ Page::Project::Operations::Kubernetes::Add.perform(
+ &:add_existing_cluster)
+
+ Page::Project::Operations::Kubernetes::AddExisting.perform do |page|
+ page.set_cluster_name(@cluster.cluster_name)
+ page.set_api_url(@cluster.api_url)
+ page.set_ca_certificate(@cluster.ca_certificate)
+ page.set_token(@cluster.token)
+ page.check_rbac! if @cluster.rbac
+ page.add_cluster!
+ end
+
+ if @install_helm_tiller
+ Page::Project::Operations::Kubernetes::Show.perform do |page|
+ # We must wait a few seconds for permissions to be set up correctly for new cluster
+ sleep 10
+
+ # Helm must be installed before everything else
+ page.install!(:helm)
+ page.await_installed(:helm)
+
+ page.install!(:ingress) if @install_ingress
+ page.install!(:prometheus) if @install_prometheus
+ page.install!(:runner) if @install_runner
+
+ page.await_installed(:ingress) if @install_ingress
+ page.await_installed(:prometheus) if @install_prometheus
+ page.await_installed(:runner) if @install_runner
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/label.rb b/qa/qa/resource/label.rb
new file mode 100644
index 00000000000..c0869cb1f2a
--- /dev/null
+++ b/qa/qa/resource/label.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Resource
+ class Label < Base
+ attr_accessor :description, :color
+
+ attribute :title
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-with-label'
+ end
+ end
+
+ def initialize
+ @title = "qa-test-#{SecureRandom.hex(8)}"
+ @description = 'This is a test label'
+ @color = '#0033CC'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Menu.perform(&:go_to_labels)
+ Page::Label::Index.perform(&:go_to_new_label)
+
+ Page::Label::New.perform do |page|
+ page.fill_title(@title)
+ page.fill_description(@description)
+ page.fill_color(@color)
+ page.create_label
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
new file mode 100644
index 00000000000..77afb3cfcba
--- /dev/null
+++ b/qa/qa/resource/merge_request.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Resource
+ class MergeRequest < Base
+ attr_accessor :title,
+ :description,
+ :source_branch,
+ :target_branch,
+ :assignee,
+ :milestone,
+ :labels,
+ :file_name,
+ :file_content
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-with-merge-request'
+ end
+ end
+
+ attribute :target do
+ project.visit!
+
+ Repository::ProjectPush.fabricate! do |resource|
+ resource.project = project
+ resource.branch_name = 'master'
+ resource.remote_branch = target_branch
+ end
+ end
+
+ attribute :source do
+ Repository::ProjectPush.fabricate! do |resource|
+ resource.project = project
+ resource.branch_name = target_branch
+ resource.remote_branch = source_branch
+ resource.new_branch = false
+ resource.file_name = file_name
+ resource.file_content = file_content
+ end
+ end
+
+ def initialize
+ @title = 'QA test - merge request'
+ @description = 'This is a test merge request'
+ @source_branch = "qa-test-feature-#{SecureRandom.hex(8)}"
+ @target_branch = "master"
+ @assignee = nil
+ @milestone = nil
+ @labels = []
+ @file_name = "added_file.txt"
+ @file_content = "File Added"
+ end
+
+ def fabricate!
+ populate(:target, :source)
+
+ project.visit!
+ Page::Project::Show.perform(&:new_merge_request)
+ Page::MergeRequest::New.perform do |page|
+ page.fill_title(@title)
+ page.fill_description(@description)
+ page.choose_milestone(@milestone) if @milestone
+ labels.each do |label|
+ page.select_label(label)
+ end
+
+ page.create_merge_request
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/merge_request_from_fork.rb b/qa/qa/resource/merge_request_from_fork.rb
new file mode 100644
index 00000000000..f91ae299d76
--- /dev/null
+++ b/qa/qa/resource/merge_request_from_fork.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class MergeRequestFromFork < MergeRequest
+ attr_accessor :fork_branch
+
+ attribute :fork do
+ Fork.fabricate!
+ end
+
+ attribute :push do
+ Repository::ProjectPush.fabricate! do |resource|
+ resource.project = fork
+ resource.branch_name = fork_branch
+ resource.file_name = 'file2.txt'
+ resource.user = fork.user
+ end
+ end
+
+ def fabricate!
+ populate(:push)
+
+ fork.visit!
+
+ Page::Project::Show.perform(&:new_merge_request)
+ Page::MergeRequest::New.perform(&:create_merge_request)
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/personal_access_token.rb b/qa/qa/resource/personal_access_token.rb
new file mode 100644
index 00000000000..b8dd0a3562f
--- /dev/null
+++ b/qa/qa/resource/personal_access_token.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ ##
+ # Create a personal access token that can be used by the api
+ #
+ class PersonalAccessToken < Base
+ attr_accessor :name
+
+ attribute :access_token do
+ Page::Profile::PersonalAccessTokens.perform(&:created_access_token)
+ end
+
+ def fabricate!
+ Page::Main::Menu.perform(&:go_to_profile_settings)
+ Page::Profile::Menu.perform(&:click_access_tokens)
+
+ Page::Profile::PersonalAccessTokens.perform do |page|
+ page.fill_token_name(name || 'api-test-token')
+ page.check_api
+ page.create_token
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
new file mode 100644
index 00000000000..7fdf69278f9
--- /dev/null
+++ b/qa/qa/resource/project.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Resource
+ class Project < Base
+ attribute :name
+ attribute :description
+
+ attribute :group do
+ Group.fabricate!
+ end
+
+ attribute :repository_ssh_location do
+ Page::Project::Show.perform do |page|
+ page.choose_repository_clone_ssh
+ page.repository_location
+ end
+ end
+
+ attribute :repository_http_location do
+ Page::Project::Show.perform do |page|
+ page.choose_repository_clone_http
+ page.repository_location
+ end
+ end
+
+ def initialize
+ @description = 'My awesome project'
+ end
+
+ def name=(raw_name)
+ @name = "#{raw_name}-#{SecureRandom.hex(8)}"
+ end
+
+ def fabricate!
+ group.visit!
+
+ Page::Group::Show.perform(&:go_to_new_project)
+
+ Page::Project::New.perform do |page|
+ page.choose_test_namespace
+ page.choose_name(@name)
+ page.add_description(@description)
+ page.set_visibility('Public')
+ page.create_new_project
+ end
+ end
+
+ def api_get_path
+ "/projects/#{name}"
+ end
+
+ def api_post_path
+ '/projects'
+ end
+
+ def api_post_body
+ {
+ namespace_id: group.id,
+ path: name,
+ name: name,
+ description: description,
+ visibility: 'public'
+ }
+ end
+
+ private
+
+ def transform_api_resource(api_resource)
+ api_resource[:repository_ssh_location] =
+ Git::Location.new(api_resource[:ssh_url_to_repo])
+ api_resource[:repository_http_location] =
+ Git::Location.new(api_resource[:http_url_to_repo])
+ api_resource
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb
new file mode 100644
index 00000000000..3f02fe885a9
--- /dev/null
+++ b/qa/qa/resource/project_imported_from_github.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Resource
+ class ProjectImportedFromGithub < Project
+ attr_accessor :name
+ attr_writer :personal_access_token, :github_repository_path
+
+ attribute :group do
+ Group.fabricate!
+ end
+
+ def fabricate!
+ group.visit!
+
+ Page::Group::Show.perform(&:go_to_new_project)
+
+ Page::Project::New.perform do |page|
+ page.go_to_import_project
+ end
+
+ Page::Project::New.perform do |page|
+ page.go_to_github_import
+ end
+
+ Page::Project::Import::Github.perform do |page|
+ page.add_personal_access_token(@personal_access_token)
+ page.list_repos
+ page.import!(@github_repository_path, @name)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/project_milestone.rb b/qa/qa/resource/project_milestone.rb
new file mode 100644
index 00000000000..a4d6657caff
--- /dev/null
+++ b/qa/qa/resource/project_milestone.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class ProjectMilestone < Base
+ attr_reader :title
+ attr_accessor :description
+
+ attribute :project do
+ Project.fabricate!
+ end
+
+ def title=(title)
+ @title = "#{title}-#{SecureRandom.hex(4)}"
+ @description = 'A milestone'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Menu.perform do |page|
+ page.click_issues
+ page.click_milestones
+ end
+
+ Page::Project::Milestone::Index.perform(&:click_new_milestone)
+
+ Page::Project::Milestone::New.perform do |milestone_new|
+ milestone_new.set_title(@title)
+ milestone_new.set_description(@description)
+ milestone_new.create_new_milestone
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/repository/project_push.rb b/qa/qa/resource/repository/project_push.rb
new file mode 100644
index 00000000000..c9fafe3419f
--- /dev/null
+++ b/qa/qa/resource/repository/project_push.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ module Repository
+ class ProjectPush < Repository::Push
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-with-code'
+ resource.description = 'Project with repository'
+ end
+ end
+
+ def initialize
+ @file_name = 'file.txt'
+ @file_content = '# This is test project'
+ @commit_message = "This is a test commit"
+ @branch_name = 'master'
+ @new_branch = true
+ end
+
+ def repository_http_uri
+ @repository_http_uri ||= begin
+ project.visit!
+ Page::Project::Show.act do
+ choose_repository_clone_http
+ repository_location.uri
+ end
+ end
+ end
+
+ def repository_ssh_uri
+ @repository_ssh_uri ||= begin
+ project.visit!
+ Page::Project::Show.act do
+ choose_repository_clone_ssh
+ repository_location.uri
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/repository/push.rb b/qa/qa/resource/repository/push.rb
new file mode 100644
index 00000000000..c14d97ff7fb
--- /dev/null
+++ b/qa/qa/resource/repository/push.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'pathname'
+
+module QA
+ module Resource
+ module Repository
+ class Push < Base
+ attr_accessor :file_name, :file_content, :commit_message,
+ :branch_name, :new_branch, :output, :repository_http_uri,
+ :repository_ssh_uri, :ssh_key, :user
+
+ attr_writer :remote_branch
+
+ def initialize
+ @file_name = 'file.txt'
+ @file_content = '# This is test file'
+ @commit_message = "This is a test commit"
+ @branch_name = 'master'
+ @new_branch = true
+ @repository_http_uri = ""
+ @ssh_key = nil
+ end
+
+ def remote_branch
+ @remote_branch ||= branch_name
+ end
+
+ def directory=(dir)
+ raise "Must set directory as a Pathname" unless dir.is_a?(Pathname)
+
+ @directory = dir
+ end
+
+ def files=(files)
+ if !files.is_a?(Array) || files.empty?
+ raise ArgumentError, "Please provide an array of hashes e.g.: [{name: 'file1', content: 'foo'}]"
+ end
+
+ @files = files
+ end
+
+ def fabricate!
+ Git::Repository.perform do |repository|
+ if ssh_key
+ repository.uri = repository_ssh_uri
+ repository.use_ssh_key(ssh_key)
+ else
+ repository.uri = repository_http_uri
+ repository.use_default_credentials unless user
+ end
+
+ username = 'GitLab QA'
+ email = 'root@gitlab.com'
+
+ if user
+ repository.username = user.username
+ repository.password = user.password
+ username = user.name
+ email = user.email
+ end
+
+ repository.clone
+ repository.configure_identity(username, email)
+
+ if new_branch
+ repository.checkout_new_branch(branch_name)
+ else
+ repository.checkout(branch_name)
+ end
+
+ if @directory
+ @directory.each_child do |f|
+ repository.add_file(f.basename, f.read) if f.file?
+ end
+ elsif @files
+ @files.each do |f|
+ repository.add_file(f[:name], f[:content])
+ end
+ else
+ repository.add_file(file_name, file_content)
+ end
+
+ repository.commit(commit_message)
+ @output = repository.push_changes("#{branch_name}:#{remote_branch}")
+
+ repository.delete_ssh_key
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/repository/wiki_push.rb b/qa/qa/resource/repository/wiki_push.rb
new file mode 100644
index 00000000000..f1c39d507fe
--- /dev/null
+++ b/qa/qa/resource/repository/wiki_push.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ module Repository
+ class WikiPush < Repository::Push
+ attribute :wiki do
+ Wiki.fabricate! do |resource|
+ resource.title = 'Home'
+ resource.content = '# My First Wiki Content'
+ resource.message = 'Update home'
+ end
+ end
+
+ def initialize
+ @file_name = 'Home.md'
+ @file_content = '# Welcome to My Wiki'
+ @commit_message = 'Updating Home Page'
+ @branch_name = 'master'
+ @new_branch = false
+ end
+
+ def repository_http_uri
+ @repository_http_uri ||= begin
+ wiki.visit!
+ Page::Project::Wiki::Show.act do
+ go_to_clone_repository
+ choose_repository_clone_http
+ repository_location.uri
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb
new file mode 100644
index 00000000000..08ae3f22117
--- /dev/null
+++ b/qa/qa/resource/runner.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Resource
+ class Runner < Base
+ attr_writer :name, :tags, :image
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-with-ci-cd'
+ resource.description = 'Project with CI/CD Pipelines'
+ end
+ end
+
+ def name
+ @name || "qa-runner-#{SecureRandom.hex(4)}"
+ end
+
+ def tags
+ @tags || %w[qa e2e]
+ end
+
+ def image
+ @image || 'gitlab/gitlab-runner:alpine'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Menu.perform(&:click_ci_cd_settings)
+
+ Service::Runner.new(name).tap do |runner|
+ Page::Project::Settings::CICD.perform do |settings|
+ settings.expand_runners_settings do |runners|
+ runner.pull
+ runner.token = runners.registration_token
+ runner.address = runners.coordinator_address
+ runner.tags = tags
+ runner.image = image
+ runner.register!
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb
new file mode 100644
index 00000000000..41ce857a8b8
--- /dev/null
+++ b/qa/qa/resource/sandbox.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ ##
+ # Ensure we're in our sandbox namespace, either by navigating to it or by
+ # creating it if it doesn't yet exist.
+ #
+ class Sandbox < Base
+ attr_reader :path
+
+ attribute :id
+
+ def initialize
+ @path = Runtime::Namespace.sandbox_name
+ end
+
+ def fabricate!
+ Page::Main::Menu.perform(&:go_to_groups)
+
+ Page::Dashboard::Groups.perform do |page|
+ if page.has_group?(path)
+ page.go_to_group(path)
+ else
+ page.go_to_new_group
+
+ Page::Group::New.perform do |group|
+ group.set_path(path)
+ group.set_description('GitLab QA Sandbox Group')
+ group.set_visibility('Public')
+ group.create
+ end
+ end
+ end
+ end
+
+ def fabricate_via_api!
+ resource_web_url(api_get)
+ rescue ResourceNotFoundError
+ super
+ end
+
+ def api_get_path
+ "/groups/#{path}"
+ end
+
+ def api_post_path
+ '/groups'
+ end
+
+ def api_post_body
+ {
+ path: path,
+ name: path,
+ visibility: 'public'
+ }
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/settings/hashed_storage.rb b/qa/qa/resource/settings/hashed_storage.rb
new file mode 100644
index 00000000000..40c06768ffe
--- /dev/null
+++ b/qa/qa/resource/settings/hashed_storage.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ module Settings
+ class HashedStorage < Base
+ def fabricate!(*traits)
+ raise ArgumentError unless traits.include?(:enabled)
+
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+ Page::Main::Menu.perform(&:go_to_admin_area)
+ Page::Admin::Menu.perform(&:go_to_repository_settings)
+
+ Page::Admin::Settings::Repository.perform do |setting|
+ setting.expand_repository_storage do |page|
+ page.enable_hashed_storage
+ page.save_settings
+ end
+ end
+
+ QA::Page::Main::Menu.perform(&:sign_out)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/ssh_key.rb b/qa/qa/resource/ssh_key.rb
new file mode 100644
index 00000000000..c6c97c8532f
--- /dev/null
+++ b/qa/qa/resource/ssh_key.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class SSHKey < Base
+ extend Forwardable
+
+ attr_accessor :title
+
+ def_delegators :key, :private_key, :public_key, :fingerprint
+
+ def key
+ @key ||= Runtime::Key::RSA.new
+ end
+
+ def fabricate!
+ Page::Main::Menu.perform(&:go_to_profile_settings)
+ Page::Profile::Menu.perform(&:click_ssh_keys)
+
+ Page::Profile::SSHKeys.perform do |page|
+ page.add_key(public_key, title)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
new file mode 100644
index 00000000000..9be88ba4211
--- /dev/null
+++ b/qa/qa/resource/user.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Resource
+ class User < Base
+ attr_reader :unique_id
+ attr_writer :username, :password
+
+ def initialize
+ @unique_id = SecureRandom.hex(8)
+ end
+
+ def username
+ @username ||= "qa-user-#{unique_id}"
+ end
+
+ def password
+ @password ||= 'password'
+ end
+
+ def name
+ @name ||= username
+ end
+
+ def email
+ @email ||= "#{username}@example.com"
+ end
+
+ def credentials_given?
+ defined?(@username) && defined?(@password)
+ end
+
+ def fabricate!
+ # Don't try to log-out if we're not logged-in
+ if Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) }
+ Page::Main::Menu.perform { |main| main.sign_out }
+ end
+
+ if credentials_given?
+ Page::Main::Login.perform do |login|
+ login.sign_in_using_credentials(self)
+ end
+ else
+ Page::Main::Login.perform do |login|
+ login.switch_to_register_tab
+ end
+ Page::Main::SignUp.perform do |signup|
+ signup.sign_up!(self)
+ end
+ end
+ end
+
+ def fabricate_via_api!
+ resource_web_url(api_get)
+ rescue ResourceNotFoundError
+ super
+ end
+
+ def api_get_path
+ "/users/#{fetch_id(username)}"
+ end
+
+ def api_post_path
+ '/users'
+ end
+
+ def api_post_body
+ {
+ email: email,
+ password: password,
+ username: username,
+ name: name,
+ skip_confirmation: true
+ }
+ end
+
+ def self.fabricate_or_use(username, password)
+ if Runtime::Env.signup_disabled?
+ self.new.tap do |user|
+ user.username = username
+ user.password = password
+ end
+ else
+ self.fabricate!
+ end
+ end
+
+ private
+
+ def fetch_id(username)
+ users = parse_body(api_get_from("/users?username=#{username}"))
+
+ unless users.size == 1 && users.first[:username] == username
+ raise ResourceNotFoundError, "Expected one user with username #{username} but found: `#{users}`."
+ end
+
+ users.first[:id]
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/wiki.rb b/qa/qa/resource/wiki.rb
new file mode 100644
index 00000000000..e942e9718a0
--- /dev/null
+++ b/qa/qa/resource/wiki.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Wiki < Base
+ attr_accessor :title, :content, :message
+
+ attribute :project do
+ Project.fabricate! do |resource|
+ resource.name = 'project-for-wikis'
+ resource.description = 'project for adding wikis'
+ end
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Menu.perform { |menu_side| menu_side.click_wiki }
+
+ Page::Project::Wiki::New.perform do |wiki_new|
+ wiki_new.go_to_create_first_page
+ wiki_new.set_title(@title)
+ wiki_new.set_content(@content)
+ wiki_new.set_message(@message)
+ wiki_new.create_new_page
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb
index 02015e23ad8..aff84c89f0e 100644
--- a/qa/qa/runtime/api/client.rb
+++ b/qa/qa/runtime/api/client.rb
@@ -6,33 +6,34 @@ module QA
class Client
attr_reader :address
- def initialize(address = :gitlab, personal_access_token: nil)
+ def initialize(address = :gitlab, personal_access_token: nil, is_new_session: true)
@address = address
@personal_access_token = personal_access_token
+ @is_new_session = is_new_session
end
def personal_access_token
- @personal_access_token ||= get_personal_access_token
- end
-
- def get_personal_access_token
- # you can set the environment variable PERSONAL_ACCESS_TOKEN
- # to use a specific access token rather than create one from the UI
- if Runtime::Env.personal_access_token
- Runtime::Env.personal_access_token
- else
- create_personal_access_token
+ @personal_access_token ||= begin
+ # you can set the environment variable PERSONAL_ACCESS_TOKEN
+ # to use a specific access token rather than create one from the UI
+ Runtime::Env.personal_access_token ||= create_personal_access_token
end
end
private
def create_personal_access_token
- Runtime::Browser.visit(@address, Page::Main::Login) do
- Page::Main::Login.act { sign_in_using_credentials }
- Factory::Resource::PersonalAccessToken.fabricate!.access_token
+ if @is_new_session
+ Runtime::Browser.visit(@address, Page::Main::Login) { do_create_personal_access_token }
+ else
+ do_create_personal_access_token
end
end
+
+ def do_create_personal_access_token
+ Page::Main::Login.act { sign_in_using_credentials }
+ Resource::PersonalAccessToken.fabricate!.access_token
+ end
end
end
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 4c64270ce92..9aaf57e8d83 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -51,6 +51,10 @@ module QA
}
)
+ if QA::Runtime::Env.accept_insecure_certs?
+ capabilities['acceptInsecureCerts'] = true
+ end
+
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("window-size=1240,1680")
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 5dc194e0aef..1154eaca6a9 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -1,34 +1,50 @@
+# frozen_string_literal: true
+
module QA
module Runtime
module Env
extend self
- attr_writer :user_type
+ attr_writer :personal_access_token
+
+ # The environment variables used to indicate if the environment under test
+ # supports the given feature
+ SUPPORTED_FEATURES = {
+ git_protocol_v2: 'QA_CAN_TEST_GIT_PROTOCOL_V2'
+ }.freeze
+
+ def supported_features
+ SUPPORTED_FEATURES
+ end
+
+ def debug?
+ enabled?(ENV['QA_DEBUG'], default: false)
+ end
+
+ def log_destination
+ ENV['QA_LOG_PATH'] || $stdout
+ end
# set to 'false' to have Chrome run visibly instead of headless
def chrome_headless?
- (ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0
+ enabled?(ENV['CHROME_HEADLESS'])
+ end
+
+ def accept_insecure_certs?
+ enabled?(ENV['ACCEPT_INSECURE_CERTS'])
end
def running_in_ci?
ENV['CI'] || ENV['CI_SERVER']
end
- # specifies token that can be used for the api
- def personal_access_token
- ENV['PERSONAL_ACCESS_TOKEN']
+ def signup_disabled?
+ enabled?(ENV['SIGNUP_DISABLED'], default: false)
end
- # By default, "standard" denotes a standard GitLab user login.
- # Set this to "ldap" if the user should be logged in via LDAP.
- def user_type
- return @user_type if defined?(@user_type) # rubocop:disable Gitlab/ModuleWithInstanceVariables
-
- ENV.fetch('GITLAB_USER_TYPE', 'standard').tap do |type|
- unless %w(ldap standard).include?(type)
- raise ArgumentError.new("Invalid user type '#{type}': must be 'ldap' or 'standard'")
- end
- end
+ # specifies token that can be used for the api
+ def personal_access_token
+ @personal_access_token ||= ENV['PERSONAL_ACCESS_TOKEN']
end
def user_username
@@ -39,6 +55,42 @@ module QA
ENV['GITLAB_PASSWORD']
end
+ def admin_username
+ ENV['GITLAB_ADMIN_USERNAME']
+ end
+
+ def admin_password
+ ENV['GITLAB_ADMIN_PASSWORD']
+ end
+
+ def forker?
+ !!(forker_username && forker_password)
+ end
+
+ def forker_username
+ ENV['GITLAB_FORKER_USERNAME']
+ end
+
+ def forker_password
+ ENV['GITLAB_FORKER_PASSWORD']
+ end
+
+ def gitlab_qa_username_1
+ ENV['GITLAB_QA_USERNAME_1'] || 'gitlab-qa-user1'
+ end
+
+ def gitlab_qa_password_1
+ ENV['GITLAB_QA_PASSWORD_1']
+ end
+
+ def gitlab_qa_username_2
+ ENV['GITLAB_QA_USERNAME_2'] || 'gitlab-qa-user2'
+ end
+
+ def gitlab_qa_password_2
+ ENV['GITLAB_QA_PASSWORD_2']
+ end
+
def ldap_username
ENV['GITLAB_LDAP_USERNAME']
end
@@ -77,6 +129,23 @@ module QA
raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
end
+
+ # Returns true if there is an environment variable that indicates that
+ # the feature is supported in the environment under test.
+ # All features are supported by default.
+ def can_test?(feature)
+ raise ArgumentError, %Q(Unknown feature "#{feature}") unless SUPPORTED_FEATURES.include? feature
+
+ enabled?(ENV[SUPPORTED_FEATURES[feature]], default: true)
+ end
+
+ private
+
+ def enabled?(value, default: true)
+ return default if value.nil?
+
+ (value =~ /^(false|no|0)$/i) != 0
+ end
end
end
end
diff --git a/qa/qa/runtime/fixtures.rb b/qa/qa/runtime/fixtures.rb
new file mode 100644
index 00000000000..72004d5b00a
--- /dev/null
+++ b/qa/qa/runtime/fixtures.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module QA
+ module Runtime
+ module Fixtures
+ def fetch_template_from_api(api_path, key)
+ request = Runtime::API::Request.new(api_client, "/templates/#{api_path}/#{key}")
+ get request.url
+ json_body[:content]
+ end
+
+ private
+
+ def api_client
+ @api_client ||= Runtime::API::Client.new(:gitlab)
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/logger.rb b/qa/qa/runtime/logger.rb
new file mode 100644
index 00000000000..bd5c4fe5bf5
--- /dev/null
+++ b/qa/qa/runtime/logger.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'logger'
+
+module QA
+ module Runtime
+ module Logger
+ extend SingleForwardable
+
+ def_delegators :logger, :debug, :info, :warn, :error, :fatal, :unknown
+
+ singleton_class.module_eval do
+ attr_writer :logger
+
+ def logger
+ return @logger if @logger
+
+ @logger = ::Logger.new Runtime::Env.log_destination
+ @logger.level = Runtime::Env.debug? ? ::Logger::DEBUG : ::Logger::ERROR
+ @logger
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/path.rb b/qa/qa/runtime/path.rb
new file mode 100644
index 00000000000..3169c5dd743
--- /dev/null
+++ b/qa/qa/runtime/path.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Runtime
+ module Path
+ extend self
+
+ def qa_root
+ ::File.expand_path('../../', __dir__)
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb
index c80ee6d4d96..5eb7a210fce 100644
--- a/qa/qa/runtime/user.rb
+++ b/qa/qa/runtime/user.rb
@@ -3,29 +3,41 @@ module QA
module User
extend self
- def default_name
+ def default_username
'root'
end
- def name
- Runtime::Env.user_username || default_name
+ def default_password
+ '5iveL!fe'
+ end
+
+ def username
+ Runtime::Env.user_username || default_username
end
def password
- Runtime::Env.user_password || '5iveL!fe'
+ Runtime::Env.user_password || default_password
end
def ldap_user?
- Runtime::Env.user_type == 'ldap'
+ Runtime::Env.ldap_username && Runtime::Env.ldap_password
end
def ldap_username
- Runtime::Env.ldap_username || name
+ Runtime::Env.ldap_username || username
end
def ldap_password
Runtime::Env.ldap_password || password
end
+
+ def admin_username
+ Runtime::Env.admin_username || default_username
+ end
+
+ def admin_password
+ Runtime::Env.admin_password || default_password
+ end
end
end
end
diff --git a/qa/qa/scenario/taggable.rb b/qa/qa/scenario/taggable.rb
deleted file mode 100644
index b1f24d742e0..00000000000
--- a/qa/qa/scenario/taggable.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module QA
- module Scenario
- module Taggable
- # rubocop:disable Gitlab/ModuleWithInstanceVariables
-
- def tags(*tags)
- @tags = tags
- end
-
- def focus
- @tags.to_a
- end
-
- # rubocop:enable Gitlab/ModuleWithInstanceVariables
- end
- end
-end
diff --git a/qa/qa/scenario/template.rb b/qa/qa/scenario/template.rb
index d21a9d52997..cb1a1de6b9a 100644
--- a/qa/qa/scenario/template.rb
+++ b/qa/qa/scenario/template.rb
@@ -1,15 +1,36 @@
module QA
module Scenario
class Template
- def self.perform(*args)
- new.tap do |scenario|
- yield scenario if block_given?
- break scenario.perform(*args)
+ class << self
+ def perform(*args)
+ new.tap do |scenario|
+ yield scenario if block_given?
+ break scenario.perform(*args)
+ end
+ end
+
+ def tags(*tags)
+ @tags = tags
+ end
+
+ def focus
+ @tags.to_a
end
end
- def perform(*_args)
- raise NotImplementedError
+ def perform(address, *rspec_options)
+ Runtime::Scenario.define(:gitlab_address, address)
+
+ ##
+ # Perform before hooks, which are different for CE and EE
+ #
+ Runtime::Release.perform_before_hooks
+
+ Specs::Runner.perform do |specs|
+ specs.tty = true
+ specs.tags = self.class.focus
+ specs.options = rspec_options if rspec_options.any?
+ end
end
end
end
diff --git a/qa/qa/scenario/test/instance.rb b/qa/qa/scenario/test/instance.rb
index 46eb2dabb11..b4098619e4e 100644
--- a/qa/qa/scenario/test/instance.rb
+++ b/qa/qa/scenario/test/instance.rb
@@ -1,17 +1,23 @@
+# frozen_string_literal: true
+
module QA
module Scenario
module Test
- ##
- # Base class for running the suite against any GitLab instance,
- # including staging and on-premises installation.
- #
- class Instance < Template
+ # This class exists for back-compatibility so that gitlab-qa can continue
+ # to call Test::Instance instead of Test::Instance::All until at least
+ # the current latest GitLab version has the Test::Instance::All class.
+ # As of Aug, 22nd 2018. Only GitLab >= 11.3 has this class.
+ module Instance
include Bootable
- extend Taggable
- tags :core
+ def self.perform(*args)
+ self.tap do |scenario|
+ yield scenario if block_given?
+ break scenario.do_perform(*args)
+ end
+ end
- def perform(address, *rspec_options)
+ def self.do_perform(address, *rspec_options)
Runtime::Scenario.define(:gitlab_address, address)
##
@@ -21,13 +27,7 @@ module QA
Specs::Runner.perform do |specs|
specs.tty = true
- specs.tags = self.class.focus
- specs.options =
- if rspec_options.any?
- rspec_options
- else
- ::File.expand_path('../../specs/features', __dir__)
- end
+ specs.options = rspec_options if rspec_options.any?
end
end
end
diff --git a/qa/qa/scenario/test/instance/all.rb b/qa/qa/scenario/test/instance/all.rb
new file mode 100644
index 00000000000..a07c26431bd
--- /dev/null
+++ b/qa/qa/scenario/test/instance/all.rb
@@ -0,0 +1,15 @@
+module QA
+ module Scenario
+ module Test
+ ##
+ # Base class for running the suite against any GitLab instance,
+ # including staging and on-premises installation.
+ #
+ module Instance
+ class All < Template
+ include Bootable
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/smoke.rb b/qa/qa/scenario/test/instance/smoke.rb
new file mode 100644
index 00000000000..a7d2cb27f27
--- /dev/null
+++ b/qa/qa/scenario/test/instance/smoke.rb
@@ -0,0 +1,17 @@
+module QA
+ module Scenario
+ module Test
+ module Instance
+ ##
+ # Base class for running the suite against any GitLab instance,
+ # including staging and on-premises installation.
+ #
+ class Smoke < Template
+ include Bootable
+
+ tags :smoke
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/integration/github.rb b/qa/qa/scenario/test/integration/github.rb
index 1d22b532aa5..76c3923d68a 100644
--- a/qa/qa/scenario/test/integration/github.rb
+++ b/qa/qa/scenario/test/integration/github.rb
@@ -2,7 +2,7 @@ module QA
module Scenario
module Test
module Integration
- class Github < Test::Instance
+ class Github < Test::Instance::All
tags :github
def perform(address, *rspec_options)
diff --git a/qa/qa/scenario/test/integration/instance_saml.rb b/qa/qa/scenario/test/integration/instance_saml.rb
new file mode 100644
index 00000000000..0697d0c2a0e
--- /dev/null
+++ b/qa/qa/scenario/test/integration/instance_saml.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class InstanceSAML < Test::Instance::All
+ tags :instance_saml
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/integration/kubernetes.rb b/qa/qa/scenario/test/integration/kubernetes.rb
index 7479073e979..405962caeed 100644
--- a/qa/qa/scenario/test/integration/kubernetes.rb
+++ b/qa/qa/scenario/test/integration/kubernetes.rb
@@ -2,7 +2,7 @@ module QA
module Scenario
module Test
module Integration
- class Kubernetes < Test::Instance
+ class Kubernetes < Test::Instance::All
tags :kubernetes
end
end
diff --git a/qa/qa/scenario/test/integration/ldap.rb b/qa/qa/scenario/test/integration/ldap.rb
deleted file mode 100644
index 257ed81d9e1..00000000000
--- a/qa/qa/scenario/test/integration/ldap.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module QA
- module Scenario
- module Test
- module Integration
- class LDAP < Test::Instance
- tags :ldap
- end
- end
- end
- end
-end
diff --git a/qa/qa/scenario/test/integration/ldap_no_tls.rb b/qa/qa/scenario/test/integration/ldap_no_tls.rb
new file mode 100644
index 00000000000..bbf4c847f33
--- /dev/null
+++ b/qa/qa/scenario/test/integration/ldap_no_tls.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class LDAPNoTLS < Test::Instance::All
+ tags :ldap_no_tls
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/integration/ldap_tls.rb b/qa/qa/scenario/test/integration/ldap_tls.rb
new file mode 100644
index 00000000000..2a767e57bc6
--- /dev/null
+++ b/qa/qa/scenario/test/integration/ldap_tls.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class LDAPTLS < Test::Instance::All
+ tags :ldap_tls
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/integration/mattermost.rb b/qa/qa/scenario/test/integration/mattermost.rb
index 13bfad28b0b..ece6fba75c9 100644
--- a/qa/qa/scenario/test/integration/mattermost.rb
+++ b/qa/qa/scenario/test/integration/mattermost.rb
@@ -6,8 +6,8 @@ module QA
# Run test suite against any GitLab instance where mattermost is enabled,
# including staging and on-premises installation.
#
- class Mattermost < Test::Instance
- tags :core, :mattermost
+ class Mattermost < Test::Instance::All
+ tags :mattermost
def perform(address, mattermost, *rspec_options)
Runtime::Scenario.define(:mattermost_address, mattermost)
diff --git a/qa/qa/scenario/test/integration/object_storage.rb b/qa/qa/scenario/test/integration/object_storage.rb
new file mode 100644
index 00000000000..2e028bbb5c6
--- /dev/null
+++ b/qa/qa/scenario/test/integration/object_storage.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class ObjectStorage < Test::Instance::All
+ tags :object_storage
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/sanity/framework.rb b/qa/qa/scenario/test/sanity/framework.rb
new file mode 100644
index 00000000000..7835d2564f0
--- /dev/null
+++ b/qa/qa/scenario/test/sanity/framework.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Sanity
+ ##
+ # This scenario runs 1 passing example, and 1 failing example, and exits
+ # with a 1 exit code.
+ #
+ class Framework < Template
+ include Bootable
+
+ tags :framework
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index abd9d53554f..c5f12255d72 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -1,12 +1,17 @@
require 'securerandom'
require 'mkmf'
+require 'pathname'
module QA
module Service
class KubernetesCluster
include Service::Shellout
- attr_reader :api_url, :ca_certificate, :token
+ attr_reader :api_url, :ca_certificate, :token, :rbac
+
+ def initialize(rbac: false)
+ @rbac = rbac
+ end
def cluster_name
@cluster_name ||= "qa-cluster-#{SecureRandom.hex(4)}-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}"
@@ -19,7 +24,8 @@ module QA
shell <<~CMD.tr("\n", ' ')
gcloud container clusters
create #{cluster_name}
- --enable-legacy-authorization
+ #{auth_options}
+ --enable-basic-auth
--zone #{Runtime::Env.gcloud_zone}
&& gcloud container clusters
get-credentials
@@ -28,8 +34,30 @@ module QA
CMD
@api_url = `kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}'`
- @ca_certificate = Base64.decode64(`kubectl get secrets -o jsonpath="{.items[0].data['ca\\.crt']}"`)
- @token = Base64.decode64(`kubectl get secrets -o jsonpath='{.items[0].data.token}'`)
+
+ @admin_user = "#{cluster_name}-admin"
+ master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --zone #{Runtime::Env.gcloud_zone} --format 'json(masterAuth.username, masterAuth.password)'`)
+ shell <<~CMD.tr("\n", ' ')
+ kubectl config set-credentials #{@admin_user}
+ --username #{master_auth['masterAuth']['username']}
+ --password #{master_auth['masterAuth']['password']}
+ CMD
+
+ if rbac
+ create_service_account
+
+ secrets = JSON.parse(`kubectl get secrets -o json`)
+ gitlab_account = secrets['items'].find do |item|
+ item['metadata']['annotations']['kubernetes.io/service-account.name'] == 'gitlab-account'
+ end
+
+ @ca_certificate = Base64.decode64(gitlab_account['data']['ca.crt'])
+ @token = Base64.decode64(gitlab_account['data']['token'])
+ else
+ @ca_certificate = Base64.decode64(`kubectl get secrets -o jsonpath="{.items[0].data['ca\\.crt']}"`)
+ @token = Base64.decode64(`kubectl get secrets -o jsonpath='{.items[0].data.token}'`)
+ end
+
self
end
@@ -44,6 +72,42 @@ module QA
private
+ def create_service_account
+ shell('kubectl create -f -', stdin_data: service_account)
+ shell("kubectl --user #{@admin_user} create -f -", stdin_data: service_account_role_binding)
+ end
+
+ def service_account
+ <<~YAML
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: gitlab-account
+ namespace: default
+ YAML
+ end
+
+ def service_account_role_binding
+ <<~YAML
+ kind: ClusterRoleBinding
+ apiVersion: rbac.authorization.k8s.io/v1
+ metadata:
+ name: gitlab-account-binding
+ subjects:
+ - kind: ServiceAccount
+ name: gitlab-account
+ namespace: default
+ roleRef:
+ kind: ClusterRole
+ name: cluster-admin
+ apiGroup: rbac.authorization.k8s.io
+ YAML
+ end
+
+ def auth_options
+ "--enable-legacy-authorization" unless rbac
+ end
+
def validate_dependencies
find_executable('gcloud') || raise("You must first install `gcloud` executable to run these tests.")
find_executable('kubectl') || raise("You must first install `kubectl` executable to run these tests.")
diff --git a/qa/qa/service/shellout.rb b/qa/qa/service/shellout.rb
index 1ca9504bb33..43dc0851571 100644
--- a/qa/qa/service/shellout.rb
+++ b/qa/qa/service/shellout.rb
@@ -11,10 +11,12 @@ module QA
# TODO, make it possible to use generic QA framework classes
# as a library - gitlab-org/gitlab-qa#94
#
- def shell(command)
+ def shell(command, stdin_data: nil)
puts "Executing `#{command}`"
- Open3.popen2e(*command) do |_in, out, wait|
+ Open3.popen2e(*command) do |stdin, out, wait|
+ stdin.puts(stdin_data) if stdin_data
+ stdin.close if stdin_data
out.each { |line| puts line }
if wait.value.exited? && wait.value.exitstatus.nonzero?
diff --git a/qa/spec/runtime/api_request_spec.rb b/qa/qa/specs/features/api/1_manage/.gitkeep
index e69de29bb2d..e69de29bb2d 100644
--- a/qa/spec/runtime/api_request_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/.gitkeep
diff --git a/qa/qa/specs/features/api/1_manage/users_spec.rb b/qa/qa/specs/features/api/1_manage/users_spec.rb
new file mode 100644
index 00000000000..ba1ba204d24
--- /dev/null
+++ b/qa/qa/specs/features/api/1_manage/users_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Manage' do
+ describe 'Users API' do
+ before(:context) do
+ @api_client = Runtime::API::Client.new(:gitlab)
+ end
+
+ let(:request) { Runtime::API::Request.new(@api_client, '/users') }
+
+ it 'GET /users' do
+ get request.url
+
+ expect_status(200)
+ end
+
+ it 'GET /users/:username with a valid username' do
+ get request.url, { params: { username: Runtime::User.username } }
+
+ expect_status(200)
+ expect(json_body).to contain_exactly(
+ a_hash_including(username: Runtime::User.username)
+ )
+ end
+
+ it 'GET /users/:username with an invalid username' do
+ get request.url, { params: { username: SecureRandom.hex(10) } }
+
+ expect_status(200)
+ expect(json_body).to eq([])
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/2_plan/.gitkeep b/qa/qa/specs/features/api/2_plan/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/qa/qa/specs/features/api/2_plan/.gitkeep
diff --git a/qa/qa/specs/features/api/3_create/repository/files_spec.rb b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
new file mode 100644
index 00000000000..bc0b5ebfe10
--- /dev/null
+++ b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
@@ -0,0 +1,61 @@
+require 'securerandom'
+
+module QA
+ describe 'API basics' do
+ before(:context) do
+ @api_client = Runtime::API::Client.new(:gitlab)
+ end
+
+ let(:project_name) { "api-basics-#{SecureRandom.hex(8)}" }
+ let(:sanitized_project_path) { CGI.escape("#{Runtime::User.username}/#{project_name}") }
+
+ it 'user creates a project with a file and deletes them afterwards' do
+ create_project_request = Runtime::API::Request.new(@api_client, '/projects')
+ post create_project_request.url, path: project_name, name: project_name
+
+ expect_status(201)
+ expect(json_body).to match(
+ a_hash_including(name: project_name, path: project_name)
+ )
+
+ create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md")
+ post create_file_request.url, branch: 'master', content: 'Hello world', commit_message: 'Add README.md'
+
+ expect_status(201)
+ expect(json_body).to match(
+ a_hash_including(branch: 'master', file_path: 'README.md')
+ )
+
+ get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", ref: 'master')
+ get get_file_request.url
+
+ expect_status(200)
+ expect(json_body).to match(
+ a_hash_including(
+ ref: 'master',
+ file_path: 'README.md', file_name: 'README.md',
+ encoding: 'base64', content: 'SGVsbG8gd29ybGQ='
+ )
+ )
+
+ delete_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", branch: 'master', commit_message: 'Remove README.md')
+ delete delete_file_request.url
+
+ expect_status(204)
+
+ get_tree_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/tree")
+ get get_tree_request.url
+
+ expect_status(200)
+ expect(json_body).to eq([])
+
+ delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
+ delete delete_project_request.url
+
+ expect_status(202)
+ expect(json_body).to match(
+ a_hash_including(message: '202 Accepted')
+ )
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/4_verify/.gitkeep b/qa/qa/specs/features/api/4_verify/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/qa/qa/specs/features/api/4_verify/.gitkeep
diff --git a/qa/qa/specs/features/api/5_package/.gitkeep b/qa/qa/specs/features/api/5_package/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/qa/qa/specs/features/api/5_package/.gitkeep
diff --git a/qa/qa/specs/features/api/6_release/.gitkeep b/qa/qa/specs/features/api/6_release/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/qa/qa/specs/features/api/6_release/.gitkeep
diff --git a/qa/qa/specs/features/api/7_configure/.gitkeep b/qa/qa/specs/features/api/7_configure/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/qa/qa/specs/features/api/7_configure/.gitkeep
diff --git a/qa/qa/specs/features/api/basics_spec.rb b/qa/qa/specs/features/api/basics_spec.rb
deleted file mode 100644
index 6563b56d1b4..00000000000
--- a/qa/qa/specs/features/api/basics_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-require 'securerandom'
-
-module QA
- describe 'API basics', :core do
- before(:context) do
- @api_client = Runtime::API::Client.new(:gitlab)
- end
-
- let(:project_name) { "api-basics-#{SecureRandom.hex(8)}" }
- let(:sanitized_project_path) { CGI.escape("#{Runtime::User.name}/#{project_name}") }
-
- it 'user creates a project with a file and deletes them afterwards' do
- create_project_request = Runtime::API::Request.new(@api_client, '/projects')
- post create_project_request.url, path: project_name, name: project_name
-
- expect_status(201)
- expect(json_body).to match(
- a_hash_including(name: project_name, path: project_name)
- )
-
- create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md")
- post create_file_request.url, branch: 'master', content: 'Hello world', commit_message: 'Add README.md'
-
- expect_status(201)
- expect(json_body).to match(
- a_hash_including(branch: 'master', file_path: 'README.md')
- )
-
- get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", ref: 'master')
- get get_file_request.url
-
- expect_status(200)
- expect(json_body).to match(
- a_hash_including(
- ref: 'master',
- file_path: 'README.md', file_name: 'README.md',
- encoding: 'base64', content: 'SGVsbG8gd29ybGQ='
- )
- )
-
- delete_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", branch: 'master', commit_message: 'Remove README.md')
- delete delete_file_request.url
-
- expect_status(204)
-
- get_tree_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/tree")
- get get_tree_request.url
-
- expect_status(200)
- expect(json_body).to eq([])
-
- delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
- delete delete_project_request.url
-
- expect_status(202)
- expect(json_body).to match(
- a_hash_including(message: '202 Accepted')
- )
- end
- end
-end
diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/users_spec.rb
deleted file mode 100644
index 8a63d8095c9..00000000000
--- a/qa/qa/specs/features/api/users_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-module QA
- describe 'API users', :core do
- before(:context) do
- @api_client = Runtime::API::Client.new(:gitlab)
- end
-
- context 'when authenticated' do
- let(:request) { Runtime::API::Request.new(@api_client, '/users') }
-
- it 'get list of users' do
- get request.url
-
- expect_status(200)
- end
-
- it 'submit request with a valid user name' do
- get request.url, { params: { username: Runtime::User.name } }
-
- expect_status(200)
- expect(json_body).to contain_exactly(
- a_hash_including(username: Runtime::User.name)
- )
- end
-
- it 'submit request with an invalid user name' do
- get request.url, { params: { username: SecureRandom.hex(10) } }
-
- expect_status(200)
- expect(json_body).to eq([])
- end
- end
-
- it 'submit request with an invalid token' do
- request = Runtime::API::Request.new(@api_client, '/users', private_token: 'invalid')
-
- get request.url
-
- expect_status(401)
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
new file mode 100644
index 00000000000..dae2a9e0236
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
@@ -0,0 +1,17 @@
+module QA
+ context 'Manage', :smoke do
+ describe 'basic user login' do
+ it 'user logs in using basic credentials' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ # TODO, since `Signed in successfully` message was removed
+ # this is the only way to tell if user is signed in correctly.
+ #
+ Page::Main::Menu.perform do |menu|
+ expect(menu).to have_personal_area
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
new file mode 100644
index 00000000000..a397df03bd2
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Manage', :orchestrated, :ldap_no_tls, :ldap_tls do
+ describe 'LDAP login' do
+ it 'user logs into GitLab using LDAP credentials' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ # TODO, since `Signed in successfully` message was removed
+ # this is the only way to tell if user is signed in correctly.
+ #
+ Page::Main::Menu.perform do |menu|
+ expect(menu).to have_personal_area
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb
new file mode 100644
index 00000000000..b1d641b507f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Manage', :orchestrated, :mattermost do
+ describe 'Mattermost login' do
+ it 'user logs into Mattermost using GitLab OAuth' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login) do
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) do
+ Page::Mattermost::Login.act { sign_in_using_oauth }
+
+ Page::Mattermost::Main.perform do |page|
+ expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
new file mode 100644
index 00000000000..87f0e9030d2
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Manage', :orchestrated, :instance_saml do
+ describe 'Instance wide SAML SSO' do
+ it 'User logs in to gitlab with SAML SSO' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+
+ Page::Main::Login.act { sign_in_with_saml }
+
+ Vendor::SAMLIdp::Page::Login.act { login }
+
+ expect(page).to have_content('Welcome to GitLab')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
new file mode 100644
index 00000000000..185837edacf
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QA
+ shared_examples 'registration and login' do
+ it 'user registers and logs in' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+
+ Resource::User.fabricate_via_browser_ui!
+
+ # TODO, since `Signed in successfully` message was removed
+ # this is the only way to tell if user is signed in correctly.
+ #
+ Page::Main::Menu.perform do |menu|
+ expect(menu).to have_personal_area
+ end
+ end
+ end
+
+ context 'Manage', :skip_signup_disabled do
+ describe 'standard' do
+ it_behaves_like 'registration and login'
+ end
+ end
+
+ context 'Manage', :orchestrated, :ldap_no_tls, :skip_signup_disabled do
+ describe 'while LDAP is enabled' do
+ it_behaves_like 'registration and login'
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
new file mode 100644
index 00000000000..4070a225260
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Manage' do
+ describe 'Add project member' do
+ it 'user adds project member' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+
+ user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
+
+ project = Resource::Project.fabricate! do |resource|
+ resource.name = 'add-member-project'
+ end
+ project.visit!
+
+ Page::Project::Menu.perform(&:click_members_settings)
+ Page::Project::Settings::Members.perform do |page|
+ page.add_member(user.username)
+ end
+
+ expect(page).to have_content("#{user.name} @#{user.username} Given access")
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
new file mode 100644
index 00000000000..6632c2977ef
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Manage', :smoke do
+ describe 'Project creation' do
+ it 'user creates a new project' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ created_project = Resource::Project.fabricate_via_browser_ui! do |project|
+ project.name = 'awesome-project'
+ project.description = 'create awesome project test'
+ end
+
+ expect(page).to have_content(created_project.name)
+ expect(page).to have_content(
+ /Project \S?awesome-project\S+ was successfully created/
+ )
+ expect(page).to have_content('create awesome project test')
+ expect(page).to have_content('The repository for this project is empty')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
new file mode 100644
index 00000000000..3ce48de2c25
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Manage', :orchestrated, :github do
+ describe 'Project import from GitHub' do
+ let(:imported_project) do
+ Resource::ProjectImportedFromGithub.fabricate! do |project|
+ project.name = 'imported-project'
+ project.personal_access_token = Runtime::Env.github_access_token
+ project.github_repository_path = 'gitlab-qa/test-project'
+ end
+ end
+
+ after do
+ # We need to delete the imported project because it's impossible to import
+ # the same GitHub project twice for a given user.
+ api_client = Runtime::API::Client.new(:gitlab)
+ delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}")
+ delete delete_project_request.url
+
+ expect_status(202)
+ end
+
+ it 'user imports a GitHub repo' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ imported_project # import the project
+
+ Page::Main::Menu.act { go_to_projects }
+ Page::Dashboard::Projects.perform do |dashboard|
+ dashboard.go_to_project(imported_project.name)
+ end
+
+ Page::Project::Show.act { wait_for_import }
+
+ verify_repository_import
+ verify_issues_import
+ verify_merge_requests_import
+ verify_labels_import
+ verify_milestones_import
+ verify_wiki_import
+ end
+
+ def verify_repository_import
+ expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.')
+ expect(page).to have_content(imported_project.name)
+ end
+
+ def verify_issues_import
+ Page::Project::Menu.act { click_issues }
+ expect(page).to have_content('This is a sample issue')
+
+ click_link 'This is a sample issue'
+
+ expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.')
+
+ # Comments
+ expect(page).to have_content('This is a comment from @rymai.')
+
+ Page::Issuable::Sidebar.perform do |issuable|
+ expect(issuable).to have_label('enhancement')
+ expect(issuable).to have_label('help wanted')
+ expect(issuable).to have_label('good first issue')
+ end
+ end
+
+ def verify_merge_requests_import
+ Page::Project::Menu.act { click_merge_requests }
+ expect(page).to have_content('Improve README.md')
+
+ click_link 'Improve README.md'
+
+ expect(page).to have_content('This improves the README file a bit.')
+
+ # Review comment are not supported yet
+ expect(page).not_to have_content('Really nice change.')
+
+ # Comments
+ expect(page).to have_content('Nice work! This is a comment from @rymai.')
+
+ # Diff comments
+ expect(page).to have_content('[Review comment] I like that!')
+ expect(page).to have_content('[Review comment] Nice blank line.')
+ expect(page).to have_content('[Single diff comment] Much better without this line!')
+
+ Page::Issuable::Sidebar.perform do |issuable|
+ expect(issuable).to have_label('bug')
+ expect(issuable).to have_label('enhancement')
+ end
+ end
+
+ def verify_labels_import
+ # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19228
+ # to build upon it.
+ end
+
+ def verify_milestones_import
+ # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18727
+ # to build upon it.
+ end
+
+ def verify_wiki_import
+ Page::Project::Menu.act { click_wiki }
+
+ expect(page).to have_content('Welcome to the test-project wiki!')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
new file mode 100644
index 00000000000..275de3d332c
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Manage' do
+ describe 'Project activity' do
+ it 'user creates an event in the activity page upon Git push' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.file_name = 'README.md'
+ push.file_content = '# This is a test project'
+ push.commit_message = 'Add README.md'
+ end
+
+ Page::Project::Menu.act { go_to_activity }
+
+ Page::Project::Activity.act { go_to_push_events }
+
+ expect(page).to have_content('pushed new branch master')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
new file mode 100644
index 00000000000..7145b950b6c
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Plan', :smoke do
+ describe 'Issue creation' do
+ let(:issue_title) { 'issue title' }
+
+ def create_issue
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Resource::Issue.fabricate! do |issue|
+ issue.title = issue_title
+ end
+ end
+
+ it 'user creates an issue' do
+ create_issue
+
+ Page::Project::Menu.act { click_issues }
+
+ expect(page).to have_content(issue_title)
+ end
+
+ context 'when using attachments in comments', :object_storage do
+ let(:file_to_attach) do
+ File.absolute_path(File.join('spec', 'fixtures', 'banana_sample.gif'))
+ end
+
+ it 'user comments on an issue with an attachment' do
+ create_issue
+
+ Page::Project::Issue::Show.perform do |show|
+ show.select_all_activities_filter
+ show.comment('See attached banana for scale', attachment: file_to_attach)
+
+ show.refresh
+
+ image_url = find('a[href$="banana_sample.gif"]')[:href]
+
+ found = show.wait(reload: false) do
+ show.asset_exists?(image_url)
+ end
+
+ expect(found).to be_truthy
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
new file mode 100644
index 00000000000..ac34f72bb8f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Plan' do
+ describe 'filter issue comments activities' do
+ let(:issue_title) { 'issue title' }
+
+ it 'user filters comments and activites in an issue' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Resource::Issue.fabricate! do |issue|
+ issue.title = issue_title
+ end
+
+ expect(page).to have_content(issue_title)
+
+ Page::Project::Issue::Show.perform do |show_page|
+ show_page.select_comments_only_filter
+ show_page.comment('/confidential')
+ show_page.comment('My own comment')
+
+ expect(show_page).not_to have_content("made the issue confidential")
+ expect(show_page).to have_content("My own comment")
+
+ show_page.select_all_activities_filter
+
+ expect(show_page).to have_content("made the issue confidential")
+ expect(show_page).to have_content("My own comment")
+
+ show_page.select_history_only_filter
+
+ expect(show_page).to have_content("made the issue confidential")
+ expect(show_page).not_to have_content("My own comment")
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
new file mode 100644
index 00000000000..d33947f41da
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Merge request creation' do
+ it 'user creates a new merge request' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ current_project = Resource::Project.fabricate! do |project|
+ project.name = 'project-with-merge-request-and-milestone'
+ end
+
+ current_milestone = Resource::ProjectMilestone.fabricate! do |milestone|
+ milestone.title = 'unique-milestone'
+ milestone.project = current_project
+ end
+
+ new_label = Resource::Label.fabricate! do |label|
+ label.project = current_project
+ label.title = 'qa-mr-test-label'
+ label.description = 'Merge Request label'
+ end
+
+ Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.title = 'This is a merge request with a milestone'
+ merge_request.description = 'Great feature with milestone'
+ merge_request.project = current_project
+ merge_request.milestone = current_milestone
+ merge_request.labels.push(new_label)
+ end
+
+ Page::MergeRequest::Show.perform do |merge_request|
+ expect(merge_request).to have_content('This is a merge request with a milestone')
+ expect(merge_request).to have_content('Great feature with milestone')
+ expect(merge_request).to have_content(/Opened [\w\s]+ ago/)
+ expect(merge_request).to have_label(new_label.title)
+ end
+
+ Page::Issuable::Sidebar.perform do |sidebar|
+ expect(sidebar).to have_milestone(current_milestone.title)
+ end
+ end
+ end
+ end
+
+ describe 'creates a merge request', :smoke do
+ it 'user creates a new merge request' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ current_project = Resource::Project.fabricate! do |project|
+ project.name = 'project-with-merge-request'
+ end
+
+ Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.title = 'This is a merge request'
+ merge_request.description = 'Great feature'
+ merge_request.project = current_project
+ end
+
+ expect(page).to have_content('This is a merge request')
+ expect(page).to have_content('Great feature')
+ expect(page).to have_content(/Opened [\w\s]+ ago/)
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
new file mode 100644
index 00000000000..6dcd74471fe
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Merge request creation from fork' do
+ it 'user forks a project, submits a merge request and maintainer merges it' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ merge_request = Resource::MergeRequestFromFork.fabricate! do |merge_request|
+ merge_request.fork_branch = 'feature-branch'
+ end
+
+ Page::Main::Menu.perform { |main| main.sign_out }
+ Page::Main::Login.perform { |login| login.sign_in_using_credentials }
+
+ merge_request.visit!
+
+ Page::MergeRequest::Show.perform { |show| show.merge! }
+
+ expect(page).to have_content('The changes were merged')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
new file mode 100644
index 00000000000..e2d639fd150
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Merge request rebasing' do
+ it 'user rebases source branch of merge request' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ project = Resource::Project.fabricate! do |project|
+ project.name = "only-fast-forward"
+ end
+ project.visit!
+
+ Page::Project::Menu.act { go_to_settings }
+ Page::Project::Settings::MergeRequest.act { enable_ff_only }
+
+ merge_request = Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.project = project
+ merge_request.title = 'Needs rebasing'
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.file_name = "other.txt"
+ push.file_content = "New file added!"
+ push.branch_name = "master"
+ push.new_branch = false
+ end
+
+ merge_request.visit!
+
+ Page::MergeRequest::Show.perform do |merge_request|
+ expect(merge_request).to have_content('Needs rebasing')
+ expect(merge_request).not_to be_fast_forward_possible
+ expect(merge_request).not_to have_merge_button
+
+ merge_request.rebase!
+
+ expect(merge_request).to have_merge_button
+ expect(merge_request.fast_forward_possible?).to be_truthy
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
new file mode 100644
index 00000000000..6ff7360c413
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Merge request squashing' do
+ it 'user squashes commits while merging' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ project = Resource::Project.fabricate! do |project|
+ project.name = "squash-before-merge"
+ end
+
+ merge_request = Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.project = project
+ merge_request.title = 'Squashing commits'
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.commit_message = 'to be squashed'
+ push.branch_name = merge_request.source_branch
+ push.new_branch = false
+ push.file_name = 'other.txt'
+ push.file_content = "Test with unicode characters â¤âœ“€â„"
+ end
+
+ Page::Project::Show.perform(&:wait_for_push)
+ merge_request.visit!
+
+ expect(page).to have_text('to be squashed')
+
+ Page::MergeRequest::Show.perform do |merge_request_page|
+ merge_request_page.mark_to_squash
+ merge_request_page.merge!
+
+ merge_request.project.visit!
+
+ Git::Repository.perform do |repository|
+ repository.uri = Page::Project::Show.act do
+ choose_repository_clone_http
+ repository_location.uri
+ end
+
+ repository.use_default_credentials
+
+ repository.act { clone }
+
+ expect(repository.commits.size).to eq 3
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
new file mode 100644
index 00000000000..297485dd81e
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'File templates' do
+ include Runtime::Fixtures
+
+ def login
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+ end
+
+ before(:all) do
+ login
+
+ @project = Resource::Project.fabricate! do |project|
+ project.name = 'file-template-project'
+ project.description = 'Add file templates via the Files view'
+ end
+
+ Page::Main::Menu.act { sign_out }
+ end
+
+ templates = [
+ {
+ file_name: '.gitignore',
+ name: 'Android',
+ api_path: 'gitignores',
+ api_key: 'Android'
+ },
+ {
+ file_name: '.gitlab-ci.yml',
+ name: 'Julia',
+ api_path: 'gitlab_ci_ymls',
+ api_key: 'Julia'
+ },
+ {
+ file_name: 'Dockerfile',
+ name: 'Python',
+ api_path: 'dockerfiles',
+ api_key: 'Python'
+ },
+ {
+ file_name: 'LICENSE',
+ name: 'Mozilla Public License 2.0',
+ api_path: 'licenses',
+ api_key: 'mpl-2.0'
+ }
+ ]
+
+ templates.each do |template|
+ it "user adds #{template[:file_name]} via file template #{template[:name]}" do
+ content = fetch_template_from_api(template[:api_path], template[:api_key])
+
+ login
+ @project.visit!
+
+ Page::Project::Show.act { create_new_file! }
+ Page::File::Form.perform do |page|
+ page.select_template template[:file_name], template[:name]
+ end
+
+ expect(page).to have_content('Template applied')
+ expect(page).to have_button('Undo')
+ expect(page).to have_content(content[0..100])
+
+ Page::File::Form.perform(&:commit_changes)
+
+ expect(page).to have_content('The file has been successfully created.')
+ expect(page).to have_content(template[:file_name])
+ expect(page).to have_content('Add new file')
+ expect(page).to have_content(content[0..100])
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb
new file mode 100644
index 00000000000..94be66782c6
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'SSH keys support' do
+ let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
+
+ it 'user adds and then removes an SSH key' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ key = Resource::SSHKey.fabricate! do |resource|
+ resource.title = key_title
+ end
+
+ expect(page).to have_content("Title: #{key_title}")
+ expect(page).to have_content(key.fingerprint)
+
+ Page::Main::Menu.act { go_to_profile_settings }
+ Page::Profile::Menu.act { click_ssh_keys }
+
+ Page::Profile::SSHKeys.perform do |ssh_keys|
+ ssh_keys.remove_key(key_title)
+ end
+
+ expect(page).not_to have_content("Title: #{key_title}")
+ expect(page).not_to have_content(key.fingerprint)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
new file mode 100644
index 00000000000..6a0add56fe0
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Git clone over HTTP', :ldap_no_tls do
+ let(:location) do
+ Page::Project::Show.act do
+ choose_repository_clone_http
+ repository_location
+ end
+ end
+
+ before do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ project = Resource::Project.fabricate! do |scenario|
+ scenario.name = 'project-with-code'
+ scenario.description = 'project for git clone tests'
+ end
+ project.visit!
+
+ Git::Repository.perform do |repository|
+ repository.uri = location.uri
+ repository.use_default_credentials
+
+ repository.act do
+ clone
+ configure_identity('GitLab QA', 'root@gitlab.com')
+ commit_file('test.rb', 'class Test; end', 'Add Test class')
+ commit_file('README.md', '# Test', 'Add Readme')
+ push_changes
+ end
+ end
+ end
+
+ it 'user performs a deep clone' do
+ Git::Repository.perform do |repository|
+ repository.uri = location.uri
+ repository.use_default_credentials
+
+ repository.act { clone }
+
+ expect(repository.commits.size).to eq 2
+ end
+ end
+
+ it 'user performs a shallow clone' do
+ Git::Repository.perform do |repository|
+ repository.uri = location.uri
+ repository.use_default_credentials
+
+ repository.act { shallow_clone }
+
+ expect(repository.commits.size).to eq 1
+ expect(repository.commits.first).to include 'Add Readme'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb
new file mode 100644
index 00000000000..46346d1b984
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Files management' do
+ it 'user creates, edits and deletes a file via the Web' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ # Create
+ file_name = 'QA Test - File name'
+ file_content = 'QA Test - File content'
+ commit_message_for_create = 'QA Test - Create new file'
+
+ Resource::File.fabricate! do |file|
+ file.name = file_name
+ file.content = file_content
+ file.commit_message = commit_message_for_create
+ end
+
+ expect(page).to have_content('The file has been successfully created.')
+ expect(page).to have_content(file_name)
+ expect(page).to have_content(file_content)
+ expect(page).to have_content(commit_message_for_create)
+
+ # Edit
+ updated_file_content = 'QA Test - Updated file content'
+ commit_message_for_update = 'QA Test - Update file'
+
+ Page::File::Show.act { click_edit }
+
+ Page::File::Form.act do
+ remove_content
+ add_content(updated_file_content)
+ add_commit_message(commit_message_for_update)
+ commit_changes
+ end
+
+ expect(page).to have_content('Your changes have been successfully committed.')
+ expect(page).to have_content(updated_file_content)
+ expect(page).to have_content(commit_message_for_update)
+
+ # Delete
+ commit_message_for_delete = 'QA Test - Delete file'
+
+ Page::File::Show.act do
+ click_delete
+ add_commit_message(commit_message_for_delete)
+ click_delete_file
+ end
+
+ expect(page).to have_content('The file has been successfully deleted.')
+ expect(page).to have_content(commit_message_for_delete)
+ expect(page).to have_no_content(file_name)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
new file mode 100644
index 00000000000..43894372cf5
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2 do
+ it 'user pushes to the repository' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+
+ # Create a project to push to
+ project = Resource::Project.fabricate! do |project|
+ project.name = 'git-protocol-project'
+ end
+
+ file_name = 'README.md'
+ file_content = 'Test Git protocol v2'
+ git_protocol = '2'
+ git_protocol_reported = nil
+
+ # Use Git to clone the project, push a file to it, and then check the
+ # supported Git protocol
+ Git::Repository.perform do |repository|
+ username = 'GitLab QA'
+ email = 'root@gitlab.com'
+
+ repository.uri = project.repository_http_location.uri
+ repository.use_default_credentials
+ repository.clone
+ repository.configure_identity(username, email)
+
+ git_protocol_reported = repository.push_with_git_protocol(
+ git_protocol,
+ file_name,
+ file_content)
+ end
+
+ project.visit!
+ Page::Project::Show.perform(&:wait_for_push)
+
+ # Check that the push worked
+ expect(page).to have_content(file_name)
+ expect(page).to have_content(file_content)
+
+ # And check that the correct Git protocol was used
+ expect(git_protocol_reported).to eq(git_protocol)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
new file mode 100644
index 00000000000..135925c007f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2 do
+ # Note: If you run this test against GDK make sure you've enabled sshd and
+ # enabled setting the Git protocol by adding `AcceptEnv GIT_PROTOCOL` to
+ # `sshd_config`
+ # See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md
+
+ let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
+ let(:ssh_key) do
+ Resource::SSHKey.fabricate! do |resource|
+ resource.title = key_title
+ end
+ end
+
+ def login
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+ end
+
+ around do |example|
+ # Create an SSH key to be used with Git
+ login
+ ssh_key
+
+ example.run
+
+ # Remove the SSH key
+ login
+ Page::Main::Menu.perform(&:go_to_profile_settings)
+ Page::Profile::Menu.perform(&:click_ssh_keys)
+ Page::Profile::SSHKeys.perform do |ssh_keys|
+ ssh_keys.remove_key(key_title)
+ end
+ end
+
+ it 'user pushes to the repository' do
+ # Create a project to push to
+ project = Resource::Project.fabricate! do |project|
+ project.name = 'git-protocol-project'
+ end
+
+ file_name = 'README.md'
+ file_content = 'Test Git protocol v2'
+ git_protocol = '2'
+ git_protocol_reported = nil
+
+ # Use Git to clone the project, push a file to it, and then check the
+ # supported Git protocol
+ Git::Repository.perform do |repository|
+ username = 'GitLab QA'
+ email = 'root@gitlab.com'
+
+ repository.uri = project.repository_ssh_location.uri
+
+ begin
+ repository.use_ssh_key(ssh_key)
+ repository.clone
+ repository.configure_identity(username, email)
+
+ git_protocol_reported = repository.push_with_git_protocol(
+ git_protocol,
+ file_name,
+ file_content)
+ ensure
+ repository.delete_ssh_key
+ end
+ end
+
+ project.visit!
+ Page::Project::Show.perform(&:wait_for_push)
+
+ # Check that the push worked
+ expect(page).to have_content(file_name)
+ expect(page).to have_content(file_content)
+
+ # And check that the correct Git protocol was used
+ expect(git_protocol_reported).to eq(git_protocol)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
new file mode 100644
index 00000000000..a63b7dce8d6
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Git push over HTTP', :ldap_no_tls do
+ it 'user using a personal access token pushes code to the repository' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+
+ access_token = Resource::PersonalAccessToken.fabricate!.access_token
+
+ user = Resource::User.new.tap do |user|
+ user.username = Runtime::User.username
+ user.password = access_token
+ end
+
+ push = Resource::Repository::ProjectPush.fabricate! do |push|
+ push.user = user
+ push.file_name = 'README.md'
+ push.file_content = '# This is a test project'
+ push.commit_message = 'Add README.md'
+ end
+
+ push.project.visit!
+ Page::Project::Show.perform(&:wait_for_push)
+
+ expect(page).to have_content('README.md')
+ expect(page).to have_content('This is a test project')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb
new file mode 100644
index 00000000000..92f596a44d9
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Git push over HTTP', :ldap_no_tls do
+ it 'user pushes code to the repository' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.file_name = 'README.md'
+ push.file_content = '# This is a test project'
+ push.commit_message = 'Add README.md'
+ end
+
+ Page::Project::Show.act { wait_for_push }
+
+ expect(page).to have_content('README.md')
+ expect(page).to have_content('This is a test project')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
new file mode 100644
index 00000000000..73a3dc14a65
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Protected branch support', :ldap_no_tls do
+ let(:branch_name) { 'protected-branch' }
+ let(:commit_message) { 'Protected push commit message' }
+ let(:project) do
+ Resource::Project.fabricate! do |resource|
+ resource.name = 'protected-branch-project'
+ end
+ end
+
+ before do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+ end
+
+ after do
+ # We need to clear localStorage because we're using it for the dropdown,
+ # and capybara doesn't do this for us.
+ # https://github.com/teamcapybara/capybara/issues/1702
+ Capybara.execute_script 'localStorage.clear()'
+ end
+
+ context 'when developers and maintainers are allowed to push to a protected branch' do
+ it 'user with push rights successfully pushes to the protected branch' do
+ create_protected_branch(allow_to_push: true)
+
+ push = push_new_file(branch_name)
+
+ expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
+ end
+ end
+
+ context 'when developers and maintainers are not allowed to push to a protected branch' do
+ it 'user without push rights fails to push to the protected branch' do
+ create_protected_branch(allow_to_push: false)
+
+ push = push_new_file(branch_name)
+
+ expect(push.output)
+ .to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
+ expect(push.output)
+ .to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
+ end
+ end
+
+ def create_protected_branch(allow_to_push:)
+ Resource::Branch.fabricate! do |resource|
+ resource.branch_name = branch_name
+ resource.project = project
+ resource.allow_to_push = allow_to_push
+ resource.protected = true
+ end
+ end
+
+ def push_new_file(branch)
+ Resource::Repository::ProjectPush.fabricate! do |resource|
+ resource.project = project
+ resource.file_name = 'new_file.md'
+ resource.file_content = '# This is a new file'
+ resource.commit_message = 'Add new_file.md'
+ resource.branch_name = branch_name
+ resource.new_branch = false
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb
new file mode 100644
index 00000000000..9c764424129
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'SSH key support' do
+ # Note: If you run this test against GDK make sure you've enabled sshd
+ # See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md
+
+ let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
+
+ it 'user adds an ssh key and pushes code to the repository' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ key = Resource::SSHKey.fabricate! do |resource|
+ resource.title = key_title
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.ssh_key = key
+ push.file_name = 'README.md'
+ push.file_content = '# Test Use SSH Key'
+ push.commit_message = 'Add README.md'
+ end
+
+ Page::Project::Show.act { wait_for_push }
+
+ expect(page).to have_content('README.md')
+ expect(page).to have_content('Test Use SSH Key')
+
+ Page::Main::Menu.act { go_to_profile_settings }
+ Page::Profile::Menu.act { click_ssh_keys }
+
+ Page::Profile::SSHKeys.perform do |ssh_keys|
+ ssh_keys.remove_key(key_title)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
new file mode 100644
index 00000000000..e7374377104
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Web IDE file templates' do
+ include Runtime::Fixtures
+
+ def login
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+ end
+
+ before(:all) do
+ login
+
+ @project = Resource::Project.fabricate! do |project|
+ project.name = 'file-template-project'
+ project.description = 'Add file templates via the Web IDE'
+ end
+ @project.visit!
+
+ # Add a file via the regular Files view because the Web IDE isn't
+ # available unless there is a file present
+ Page::Project::Show.act { create_new_file! }
+ Page::File::Form.perform do |page|
+ page.add_name('dummy')
+ page.add_content('Enable the Web IDE')
+ page.commit_changes
+ end
+
+ Page::Main::Menu.act { sign_out }
+ end
+
+ templates = [
+ {
+ file_name: '.gitignore',
+ name: 'Android',
+ api_path: 'gitignores',
+ api_key: 'Android'
+ },
+ {
+ file_name: '.gitlab-ci.yml',
+ name: 'Julia',
+ api_path: 'gitlab_ci_ymls',
+ api_key: 'Julia'
+ },
+ {
+ file_name: 'Dockerfile',
+ name: 'Python',
+ api_path: 'dockerfiles',
+ api_key: 'Python'
+ },
+ {
+ file_name: 'LICENSE',
+ name: 'Mozilla Public License 2.0',
+ api_path: 'licenses',
+ api_key: 'mpl-2.0'
+ }
+ ]
+
+ templates.each do |template|
+ it "user adds #{template[:file_name]} via file template #{template[:name]}" do
+ content = fetch_template_from_api(template[:api_path], template[:api_key])
+
+ login
+ @project.visit!
+
+ Page::Project::Show.act { open_web_ide! }
+ Page::Project::WebIDE::Edit.perform do |page|
+ page.create_new_file_from_template template[:file_name], template[:name]
+
+ expect(page.has_file?(template[:file_name])).to be_truthy
+ end
+
+ expect(page).to have_button('Undo')
+ expect(page).to have_content(content[0..100])
+
+ Page::Project::WebIDE::Edit.perform do |page|
+ page.commit_changes
+ end
+
+ expect(page).to have_content(template[:file_name])
+ expect(page).to have_content(content[0..100])
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb
new file mode 100644
index 00000000000..210271705d9
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Wiki management' do
+ def login
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+ end
+
+ def validate_content(content)
+ expect(page).to have_content('Wiki was successfully updated')
+ expect(page).to have_content(/#{content}/)
+ end
+
+ before do
+ login
+ end
+
+ it 'user creates, edits, clones, and pushes to the wiki' do
+ wiki = Resource::Wiki.fabricate! do |resource|
+ resource.title = 'Home'
+ resource.content = '# My First Wiki Content'
+ resource.message = 'Update home'
+ end
+
+ validate_content('My First Wiki Content')
+
+ Page::Project::Wiki::Edit.act { go_to_edit_page }
+ Page::Project::Wiki::New.perform do |page|
+ page.set_content("My Second Wiki Content")
+ page.save_changes
+ end
+
+ validate_content('My Second Wiki Content')
+
+ Resource::Repository::WikiPush.fabricate! do |push|
+ push.wiki = wiki
+ push.file_name = 'Home.md'
+ push.file_content = '# My Third Wiki Content'
+ push.commit_message = 'Update Home.md'
+ end
+ Page::Project::Menu.act { click_wiki }
+
+ expect(page).to have_content('My Third Wiki Content')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb
new file mode 100644
index 00000000000..0837b720df1
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Verify' do
+ describe 'CI variable support' do
+ it 'user adds a CI variable' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Resource::CiVariable.fabricate! do |resource|
+ resource.key = 'VARIABLE_KEY'
+ resource.value = 'some CI variable'
+ end
+
+ Page::Project::Settings::CICD.perform do |settings|
+ settings.expand_ci_variables do |page|
+ expect(page).to have_field(with: 'VARIABLE_KEY')
+ expect(page).not_to have_field(with: 'some CI variable')
+
+ page.reveal_variables
+
+ expect(page).to have_field(with: 'some CI variable')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
new file mode 100644
index 00000000000..25cbe41c684
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Verify', :orchestrated, :docker do
+ describe 'Pipeline creation and processing' do
+ let(:executor) { "qa-runner-#{Time.now.to_i}" }
+
+ after do
+ Service::Runner.new(executor).remove!
+ end
+
+ it 'users creates a pipeline which gets processed' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ project = Resource::Project.fabricate! do |project|
+ project.name = 'project-with-pipelines'
+ project.description = 'Project with CI/CD Pipelines.'
+ end
+
+ Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = executor
+ runner.tags = %w[qa test]
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.file_name = '.gitlab-ci.yml'
+ push.commit_message = 'Add .gitlab-ci.yml'
+ push.file_content = <<~EOF
+ test-success:
+ tags:
+ - qa
+ - test
+ script: echo 'OK'
+
+ test-failure:
+ tags:
+ - qa
+ - test
+ script:
+ - echo 'FAILURE'
+ - exit 1
+
+ test-tags:
+ tags:
+ - qa
+ - docker
+ script: echo 'NOOP'
+
+ test-artifacts:
+ tags:
+ - qa
+ - test
+ script: mkdir my-artifacts; echo "CONTENTS" > my-artifacts/artifact.txt
+ artifacts:
+ paths:
+ - my-artifacts/
+ EOF
+ end
+
+ Page::Project::Show.act { wait_for_push }
+
+ expect(page).to have_content('Add .gitlab-ci.yml')
+
+ Page::Project::Menu.act { click_ci_cd_pipelines }
+
+ expect(page).to have_content('All 1')
+ expect(page).to have_content('Add .gitlab-ci.yml')
+
+ puts 'Waiting for the runner to process the pipeline'
+ sleep 15 # Runner should process all jobs within 15 seconds.
+
+ Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ expect(pipeline).to be_running
+ expect(pipeline).to have_build('test-success', status: :success)
+ expect(pipeline).to have_build('test-failure', status: :failed)
+ expect(pipeline).to have_build('test-tags', status: :pending)
+ expect(pipeline).to have_build('test-artifacts', status: :success)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
new file mode 100644
index 00000000000..3af7db751e7
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Verify', :docker do
+ describe 'Runner registration' do
+ let(:executor) { "qa-runner-#{Time.now.to_i}" }
+
+ after do
+ Service::Runner.new(executor).remove!
+ end
+
+ it 'user registers a new specific runner' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Resource::Runner.fabricate! do |runner|
+ runner.name = executor
+ end
+
+ Page::Project::Settings::CICD.perform do |settings|
+ sleep 5 # Runner should register within 5 seconds
+ settings.refresh
+
+ settings.expand_runners_settings do |page|
+ expect(page).to have_content(executor)
+ expect(page).to have_online_runner
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/.gitkeep b/qa/qa/specs/features/browser_ui/5_package/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/.gitkeep
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb
new file mode 100644
index 00000000000..84757f25379
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Release' do
+ describe 'Deploy key creation' do
+ it 'user adds a deploy key' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ key = Runtime::Key::RSA.new
+ deploy_key_title = 'deploy key title'
+ deploy_key_value = key.public_key
+
+ deploy_key = Resource::DeployKey.fabricate! do |resource|
+ resource.title = deploy_key_title
+ resource.key = deploy_key_value
+ end
+
+ expect(deploy_key.fingerprint).to eq(key.fingerprint)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
new file mode 100644
index 00000000000..e2320c92343
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'digest/sha1'
+
+module QA
+ context 'Release', :docker do
+ describe 'Git clone using a deploy key' do
+ def login
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+ end
+
+ before(:all) do
+ login
+
+ @runner_name = "qa-runner-#{Time.now.to_i}"
+
+ @project = Resource::Project.fabricate! do |resource|
+ resource.name = 'deploy-key-clone-project'
+ end
+
+ @repository_location = @project.repository_ssh_location
+
+ Resource::Runner.fabricate! do |resource|
+ resource.project = @project
+ resource.name = @runner_name
+ resource.tags = %w[qa docker]
+ resource.image = 'gitlab/gitlab-runner:ubuntu'
+ end
+
+ Page::Main::Menu.act { sign_out }
+ end
+
+ after(:all) do
+ Service::Runner.new(@runner_name).remove!
+ end
+
+ keys = [
+ [Runtime::Key::RSA, 8192],
+ [Runtime::Key::ECDSA, 521],
+ [Runtime::Key::ED25519]
+ ]
+
+ keys.each do |(key_class, bits)|
+ it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do
+ key = key_class.new(*bits)
+
+ login
+
+ Resource::DeployKey.fabricate! do |resource|
+ resource.project = @project
+ resource.title = "deploy key #{key.name}(#{key.bits})"
+ resource.key = key.public_key
+ end
+
+ deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}"
+
+ Resource::CiVariable.fabricate! do |resource|
+ resource.project = @project
+ resource.key = deploy_key_name
+ resource.value = key.private_key
+ end
+
+ gitlab_ci = <<~YAML
+ cat-config:
+ script:
+ - mkdir -p ~/.ssh
+ - ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts
+ - eval $(ssh-agent -s)
+ - ssh-add -D
+ - echo "$#{deploy_key_name}" | ssh-add -
+ - git clone #{@repository_location.git_uri}
+ - cd #{@project.name}
+ - git checkout #{deploy_key_name}
+ - sha1sum .gitlab-ci.yml
+ tags:
+ - qa
+ - docker
+ YAML
+
+ Resource::Repository::ProjectPush.fabricate! do |resource|
+ resource.project = @project
+ resource.file_name = '.gitlab-ci.yml'
+ resource.commit_message = 'Add .gitlab-ci.yml'
+ resource.file_content = gitlab_ci
+ resource.branch_name = deploy_key_name
+ resource.new_branch = true
+ end
+
+ sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
+
+ Page::Project::Show.act { wait_for_push }
+ Page::Project::Menu.act { click_ci_cd_pipelines }
+ Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
+ Page::Project::Pipeline::Show.act { go_to_first_job }
+
+ Page::Project::Job::Show.perform do |job|
+ job.wait(reload: false) do
+ job.completed? && !job.trace_loading?
+ end
+
+ expect(job.passed?).to be_truthy, "Job status did not become \"passed\"."
+ expect(job.output).to include(sha1sum)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb
new file mode 100644
index 00000000000..9f34e4218c1
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Release' do
+ describe 'Deploy token creation' do
+ it 'user adds a deploy token' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ deploy_token_name = 'deploy token name'
+ deploy_token_expires_at = Date.today + 7 # 1 Week from now
+
+ deploy_token = Resource::DeployToken.fabricate! do |resource|
+ resource.name = deploy_token_name
+ resource.expires_at = deploy_token_expires_at
+ end
+
+ expect(deploy_token.username.length).to be > 0
+ expect(deploy_token.password.length).to be > 0
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
new file mode 100644
index 00000000000..30ec0665973
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'pathname'
+
+module QA
+ context 'Configure', :orchestrated, :kubernetes do
+ describe 'Auto DevOps support' do
+ after do
+ @cluster&.remove!
+ end
+
+ [true, false].each do |rbac|
+ context "when rbac is #{rbac ? 'enabled' : 'disabled'}" do
+ it 'user creates a new project and runs auto devops' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ project = Resource::Project.fabricate! do |p|
+ p.name = 'project-with-autodevops'
+ p.description = 'Project with Auto Devops'
+ end
+
+ # Disable code_quality check in Auto DevOps pipeline as it takes
+ # too long and times out the test
+ Resource::CiVariable.fabricate! do |resource|
+ resource.project = project
+ resource.key = 'CODE_QUALITY_DISABLED'
+ resource.value = '1'
+ end
+
+ # Create Auto Devops compatible repo
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.directory = Pathname
+ .new(__dir__)
+ .join('../../../../../fixtures/auto_devops_rack')
+ push.commit_message = 'Create Auto DevOps compatible rack application'
+ end
+
+ Page::Project::Show.act { wait_for_push }
+
+ # Create and connect K8s cluster
+ @cluster = Service::KubernetesCluster.new(rbac: rbac).create!
+ kubernetes_cluster = Resource::KubernetesCluster.fabricate! do |cluster|
+ cluster.project = project
+ cluster.cluster = @cluster
+ cluster.install_helm_tiller = true
+ cluster.install_ingress = true
+ cluster.install_prometheus = true
+ cluster.install_runner = true
+ end
+ kubernetes_cluster.populate(:ingress_ip)
+
+ project.visit!
+ Page::Project::Menu.act { click_ci_cd_settings }
+ Page::Project::Settings::CICD.perform do |p|
+ p.enable_auto_devops_with_domain(
+ "#{kubernetes_cluster.ingress_ip}.nip.io")
+ end
+
+ project.visit!
+ Page::Project::Menu.act { click_ci_cd_pipelines }
+ Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ expect(pipeline).to have_build('build', status: :success, wait: 600)
+ expect(pipeline).to have_build('test', status: :success, wait: 600)
+ expect(pipeline).to have_build('production', status: :success, wait: 1200)
+ end
+
+ Page::Project::Menu.act { click_operations_environments }
+ Page::Project::Operations::Environments::Index.perform do |index|
+ index.go_to_environment('production')
+ end
+ Page::Project::Operations::Environments::Show.perform do |show|
+ show.view_deployment do
+ expect(page).to have_content('Hello World!')
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb
new file mode 100644
index 00000000000..7096864e011
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Configure', :orchestrated, :mattermost do
+ describe 'Mattermost support' do
+ it 'user creates a group with a mattermost team' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Menu.act { go_to_groups }
+
+ Page::Dashboard::Groups.perform do |page|
+ page.go_to_new_group
+
+ expect(page).to have_content(
+ /Create a Mattermost team for this group/
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/login/ldap_spec.rb b/qa/qa/specs/features/login/ldap_spec.rb
deleted file mode 100644
index b7a284c584b..00000000000
--- a/qa/qa/specs/features/login/ldap_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module QA
- describe 'LDAP user login', :ldap do
- before do
- Runtime::Env.user_type = 'ldap'
- end
-
- it 'user logs in using LDAP credentials' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- # TODO, since `Signed in successfully` message was removed
- # this is the only way to tell if user is signed in correctly.
- #
- Page::Menu::Main.perform do |menu|
- expect(menu).to have_personal_area
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/mattermost/group_create_spec.rb b/qa/qa/specs/features/mattermost/group_create_spec.rb
deleted file mode 100644
index a59761da341..00000000000
--- a/qa/qa/specs/features/mattermost/group_create_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module QA
- describe 'create a new group', :mattermost do
- it 'creating a group with a mattermost team' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
- Page::Menu::Main.act { go_to_groups }
-
- Page::Dashboard::Groups.perform do |page|
- page.go_to_new_group
-
- expect(page).to have_content(
- /Create a Mattermost team for this group/
- )
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/mattermost/login_spec.rb b/qa/qa/specs/features/mattermost/login_spec.rb
deleted file mode 100644
index b140191e160..00000000000
--- a/qa/qa/specs/features/mattermost/login_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module QA
- describe 'logging in to Mattermost', :mattermost do
- it 'can use gitlab oauth' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login) do
- Page::Main::Login.act { sign_in_using_credentials }
-
- Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) do
- Page::Mattermost::Login.act { sign_in_using_oauth }
-
- Page::Mattermost::Main.perform do |page|
- expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb
deleted file mode 100644
index 36d7efb02e1..00000000000
--- a/qa/qa/specs/features/merge_request/create_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module QA
- describe 'creates a merge request', :core do
- it 'user creates a new merge request' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- current_project = Factory::Resource::Project.fabricate! do |project|
- project.name = 'project-with-merge-request-and-milestone'
- end
-
- current_milestone = Factory::Resource::ProjectMilestone.fabricate! do |milestone|
- milestone.title = 'unique-milestone'
- milestone.project = current_project
- end
-
- Factory::Resource::MergeRequest.fabricate! do |merge_request|
- merge_request.title = 'This is a merge request with a milestone'
- merge_request.description = 'Great feature with milestone'
- merge_request.project = current_project
- merge_request.milestone = current_milestone
- end
-
- expect(page).to have_content('This is a merge request with a milestone')
- expect(page).to have_content('Great feature with milestone')
- expect(page).to have_content(/Opened [\w\s]+ ago/)
-
- Page::Issuable::Sidebar.perform do |sidebar|
- expect(sidebar).to have_milestone(current_milestone.title)
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/merge_request/rebase_spec.rb b/qa/qa/specs/features/merge_request/rebase_spec.rb
deleted file mode 100644
index 163dcbe7963..00000000000
--- a/qa/qa/specs/features/merge_request/rebase_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module QA
- describe 'merge request rebase', :core do
- it 'rebases source branch of merge request' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- project = Factory::Resource::Project.fabricate! do |project|
- project.name = "only-fast-forward"
- end
-
- Page::Menu::Side.act { go_to_settings }
- Page::Project::Settings::MergeRequest.act { enable_ff_only }
-
- merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request|
- merge_request.project = project
- merge_request.title = 'Needs rebasing'
- end
-
- Factory::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.file_name = "other.txt"
- push.file_content = "New file added!"
- end
-
- merge_request.visit!
-
- Page::MergeRequest::Show.perform do |merge_request|
- expect(merge_request).to have_content('Needs rebasing')
- expect(merge_request).not_to be_fast_forward_possible
- expect(merge_request).not_to have_merge_button
-
- merge_request.rebase!
-
- expect(merge_request).to have_merge_button
- expect(merge_request.fast_forward_possible?).to be_truthy
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/merge_request/squash_spec.rb b/qa/qa/specs/features/merge_request/squash_spec.rb
deleted file mode 100644
index 4856bbe1a69..00000000000
--- a/qa/qa/specs/features/merge_request/squash_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-module QA
- describe 'merge request squash commits', :core do
- it 'when squash commits is marked before merge' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- project = Factory::Resource::Project.fabricate! do |project|
- project.name = "squash-before-merge"
- end
-
- merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request|
- merge_request.project = project
- merge_request.title = 'Squashing commits'
- end
-
- Factory::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.commit_message = 'to be squashed'
- push.branch_name = merge_request.source_branch
- push.new_branch = false
- push.file_name = 'other.txt'
- push.file_content = "Test with unicode characters â¤âœ“€â„"
- end
-
- merge_request.visit!
-
- expect(page).to have_text('to be squashed')
-
- Page::MergeRequest::Show.perform do |merge_request_page|
- merge_request_page.mark_to_squash
- merge_request_page.merge!
-
- merge_request.project.visit!
-
- Git::Repository.perform do |repository|
- repository.uri = Page::Project::Show.act do
- choose_repository_clone_http
- repository_location.uri
- end
-
- repository.use_default_credentials
-
- repository.act { clone }
-
- expect(repository.commits.size).to eq 3
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/project/activity_spec.rb b/qa/qa/specs/features/project/activity_spec.rb
deleted file mode 100644
index 02074e386b6..00000000000
--- a/qa/qa/specs/features/project/activity_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-module QA
- describe 'activity page', :core do
- it 'push creates an event in the activity page' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- Factory::Repository::ProjectPush.fabricate! do |push|
- push.file_name = 'README.md'
- push.file_content = '# This is a test project'
- push.commit_message = 'Add README.md'
- end
-
- Page::Menu::Side.act { go_to_activity }
-
- Page::Project::Activity.act { go_to_push_events }
-
- expect(page).to have_content('pushed new branch master')
- end
- end
-end
diff --git a/qa/qa/specs/features/project/add_deploy_key_spec.rb b/qa/qa/specs/features/project/add_deploy_key_spec.rb
deleted file mode 100644
index 14642af97ad..00000000000
--- a/qa/qa/specs/features/project/add_deploy_key_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module QA
- describe 'deploy keys support', :core do
- it 'user adds a deploy key' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- key = Runtime::Key::RSA.new
- deploy_key_title = 'deploy key title'
- deploy_key_value = key.public_key
-
- deploy_key = Factory::Resource::DeployKey.fabricate! do |resource|
- resource.title = deploy_key_title
- resource.key = deploy_key_value
- end
-
- expect(deploy_key.fingerprint).to eq(key.fingerprint)
- end
- end
-end
diff --git a/qa/qa/specs/features/project/add_secret_variable_spec.rb b/qa/qa/specs/features/project/add_secret_variable_spec.rb
deleted file mode 100644
index 32c91dd9d4d..00000000000
--- a/qa/qa/specs/features/project/add_secret_variable_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module QA
- describe 'secret variables support', :core do
- it 'user adds a secret variable' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- Factory::Resource::SecretVariable.fabricate! do |resource|
- resource.key = 'VARIABLE_KEY'
- resource.value = 'some secret variable'
- end
-
- Page::Project::Settings::CICD.perform do |settings|
- settings.expand_secret_variables do |page|
- expect(page).to have_field(with: 'VARIABLE_KEY')
- expect(page).not_to have_field(with: 'some secret variable')
-
- page.reveal_variables
-
- expect(page).to have_field(with: 'some secret variable')
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/project/auto_devops_spec.rb b/qa/qa/specs/features/project/auto_devops_spec.rb
deleted file mode 100644
index bc713b46d81..00000000000
--- a/qa/qa/specs/features/project/auto_devops_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require 'pathname'
-
-module QA
- describe 'Auto Devops', :kubernetes do
- after do
- @cluster&.remove!
- end
-
- it 'user creates a new project and runs auto devops' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- project = Factory::Resource::Project.fabricate! do |p|
- p.name = 'project-with-autodevops'
- p.description = 'Project with Auto Devops'
- end
-
- # Create Auto Devops compatible repo
- Factory::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.directory = Pathname
- .new(__dir__)
- .join('../../../fixtures/auto_devops_rack')
- push.commit_message = 'Create Auto DevOps compatible rack application'
- end
-
- Page::Project::Show.act { wait_for_push }
-
- # Create and connect K8s cluster
- @cluster = Service::KubernetesCluster.new.create!
- kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |cluster|
- cluster.project = project
- cluster.cluster = @cluster
- cluster.install_helm_tiller = true
- cluster.install_ingress = true
- cluster.install_prometheus = true
- cluster.install_runner = true
- end
-
- project.visit!
- Page::Menu::Side.act { click_ci_cd_settings }
- Page::Project::Settings::CICD.perform do |p|
- p.enable_auto_devops_with_domain("#{kubernetes_cluster.ingress_ip}.nip.io")
- end
-
- project.visit!
- Page::Menu::Side.act { click_ci_cd_pipelines }
- Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- expect(pipeline).to have_build('build', status: :success, wait: 600)
- expect(pipeline).to have_build('test', status: :success, wait: 600)
- expect(pipeline).to have_build('production', status: :success, wait: 600)
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/project/create_issue_spec.rb b/qa/qa/specs/features/project/create_issue_spec.rb
deleted file mode 100644
index ac2ed2ca2a1..00000000000
--- a/qa/qa/specs/features/project/create_issue_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module QA
- describe 'creates issue', :core do
- let(:issue_title) { 'issue title' }
-
- it 'user creates issue' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- Factory::Resource::Issue.fabricate! do |issue|
- issue.title = issue_title
- end
-
- Page::Menu::Side.act { click_issues }
-
- expect(page).to have_content(issue_title)
- end
- end
-end
diff --git a/qa/qa/specs/features/project/create_spec.rb b/qa/qa/specs/features/project/create_spec.rb
deleted file mode 100644
index 14ecd464685..00000000000
--- a/qa/qa/specs/features/project/create_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module QA
- describe 'create a new project', :core do
- it 'user creates a new project' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- created_project = Factory::Resource::Project.fabricate! do |project|
- project.name = 'awesome-project'
- project.description = 'create awesome project test'
- end
-
- expect(created_project.name).to match /^awesome-project-\h{16}$/
-
- expect(page).to have_content(
- /Project \S?awesome-project\S+ was successfully created/
- )
-
- expect(page).to have_content('create awesome project test')
- expect(page).to have_content('The repository for this project is empty')
- end
- end
-end
diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb
deleted file mode 100644
index 054f49b408a..00000000000
--- a/qa/qa/specs/features/project/deploy_key_clone_spec.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-require 'digest/sha1'
-
-module QA
- describe 'cloning code using a deploy key', :core, :docker do
- def login
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
- end
-
- before(:all) do
- login
-
- @runner_name = "qa-runner-#{Time.now.to_i}"
-
- @project = Factory::Resource::Project.fabricate! do |resource|
- resource.name = 'deploy-key-clone-project'
- end
-
- @repository_location = @project.repository_ssh_location
-
- Factory::Resource::Runner.fabricate! do |resource|
- resource.project = @project
- resource.name = @runner_name
- resource.tags = %w[qa docker]
- resource.image = 'gitlab/gitlab-runner:ubuntu'
- end
-
- Page::Menu::Main.act { sign_out }
- end
-
- after(:all) do
- Service::Runner.new(@runner_name).remove!
- end
-
- keys = [
- [Runtime::Key::RSA, 8192],
- [Runtime::Key::ECDSA, 521],
- [Runtime::Key::ED25519]
- ]
-
- keys.each do |(key_class, bits)|
- it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do
- key = key_class.new(*bits)
-
- login
-
- Factory::Resource::DeployKey.fabricate! do |resource|
- resource.project = @project
- resource.title = "deploy key #{key.name}(#{key.bits})"
- resource.key = key.public_key
- end
-
- deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}"
-
- Factory::Resource::SecretVariable.fabricate! do |resource|
- resource.project = @project
- resource.key = deploy_key_name
- resource.value = key.private_key
- end
-
- gitlab_ci = <<~YAML
- cat-config:
- script:
- - mkdir -p ~/.ssh
- - ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts
- - eval $(ssh-agent -s)
- - ssh-add -D
- - echo "$#{deploy_key_name}" | ssh-add -
- - git clone #{@repository_location.git_uri}
- - cd #{@project.name}
- - git checkout #{deploy_key_name}
- - sha1sum .gitlab-ci.yml
- tags:
- - qa
- - docker
- YAML
-
- Factory::Repository::ProjectPush.fabricate! do |resource|
- resource.project = @project
- resource.file_name = '.gitlab-ci.yml'
- resource.commit_message = 'Add .gitlab-ci.yml'
- resource.file_content = gitlab_ci
- resource.branch_name = deploy_key_name
- resource.new_branch = true
- end
-
- sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
-
- Page::Project::Show.act { wait_for_push }
- Page::Menu::Side.act { click_ci_cd_pipelines }
- Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
- Page::Project::Pipeline::Show.act { go_to_first_job }
-
- Page::Project::Job::Show.perform do |job|
- job.wait(reload: false) do
- job.completed? && !job.trace_loading?
- end
-
- expect(job.passed?).to be_truthy, "Job status did not become \"passed\"."
- expect(job.output).to include(sha1sum)
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/project/file_spec.rb b/qa/qa/specs/features/project/file_spec.rb
deleted file mode 100644
index 5659784cd5c..00000000000
--- a/qa/qa/specs/features/project/file_spec.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-module QA
- describe 'File Functionality', :core do
- it 'lets a user create, edit and delete a file via WebUI' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- # Create
- file_name = 'QA Test - File name'
- file_content = 'QA Test - File content'
- commit_message_for_create = 'QA Test - Create new file'
-
- Factory::Resource::File.fabricate! do |file|
- file.name = file_name
- file.content = file_content
- file.commit_message = commit_message_for_create
- end
-
- expect(page).to have_content('The file has been successfully created.')
- expect(page).to have_content(file_name)
- expect(page).to have_content(file_content)
- expect(page).to have_content(commit_message_for_create)
-
- # Edit
- updated_file_content = 'QA Test - Updated file content'
- commit_message_for_update = 'QA Test - Update file'
-
- Page::File::Show.act { click_edit }
-
- Page::File::Form.act do
- remove_content
- add_content(updated_file_content)
- add_commit_message(commit_message_for_update)
- commit_changes
- end
-
- expect(page).to have_content('Your changes have been successfully committed.')
- expect(page).to have_content(updated_file_content)
- expect(page).to have_content(commit_message_for_update)
-
- # Delete
- commit_message_for_delete = 'QA Test - Delete file'
-
- Page::File::Show.act do
- click_delete
- add_commit_message(commit_message_for_delete)
- click_delete_file
- end
-
- expect(page).to have_content('The file has been successfully deleted.')
- expect(page).to have_content(commit_message_for_delete)
- expect(page).to have_no_content(file_name)
- end
- end
-end
diff --git a/qa/qa/specs/features/project/fork_project_spec.rb b/qa/qa/specs/features/project/fork_project_spec.rb
deleted file mode 100644
index 8ad0120305a..00000000000
--- a/qa/qa/specs/features/project/fork_project_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module QA
- describe 'Project fork', :core do
- it 'can submit merge requests to upstream master' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- merge_request = Factory::Resource::MergeRequestFromFork.fabricate! do |merge_request|
- merge_request.fork_branch = 'feature-branch'
- end
-
- Page::Menu::Main.act { sign_out }
- Page::Main::Login.act do
- switch_to_sign_in_tab
- sign_in_using_credentials
- end
-
- merge_request.visit!
- Page::MergeRequest::Show.act { merge! }
-
- expect(page).to have_content('The changes were merged')
- end
- end
-end
diff --git a/qa/qa/specs/features/project/import_from_github_spec.rb b/qa/qa/specs/features/project/import_from_github_spec.rb
deleted file mode 100644
index 221b5c27fba..00000000000
--- a/qa/qa/specs/features/project/import_from_github_spec.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-module QA
- describe 'user imports a GitHub repo', :core, :github do
- let(:imported_project) do
- Factory::Resource::ProjectImportedFromGithub.fabricate! do |project|
- project.name = 'imported-project'
- project.personal_access_token = Runtime::Env.github_access_token
- project.github_repository_path = 'gitlab-qa/test-project'
- end
- end
-
- after do
- # We need to delete the imported project because it's impossible to import
- # the same GitHub project twice for a given user.
- api_client = Runtime::API::Client.new(:gitlab)
- delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}")
- delete delete_project_request.url
-
- expect_status(202)
- end
-
- it 'user imports a GitHub repo' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- imported_project # import the project
-
- Page::Menu::Main.act { go_to_projects }
- Page::Dashboard::Projects.perform do |dashboard|
- dashboard.go_to_project(imported_project.name)
- end
-
- Page::Project::Show.act { wait_for_import }
-
- verify_repository_import
- verify_issues_import
- verify_merge_requests_import
- verify_labels_import
- verify_milestones_import
- verify_wiki_import
- end
-
- def verify_repository_import
- expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.')
- expect(page).to have_content(imported_project.name)
- end
-
- def verify_issues_import
- Page::Menu::Side.act { click_issues }
- expect(page).to have_content('This is a sample issue')
-
- click_link 'This is a sample issue'
-
- expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.')
-
- # Comments
- expect(page).to have_content('This is a comment from @rymai.')
-
- Page::Issuable::Sidebar.perform do |issuable|
- expect(issuable).to have_label('enhancement')
- expect(issuable).to have_label('help wanted')
- expect(issuable).to have_label('good first issue')
- end
- end
-
- def verify_merge_requests_import
- Page::Menu::Side.act { click_merge_requests }
- expect(page).to have_content('Improve README.md')
-
- click_link 'Improve README.md'
-
- expect(page).to have_content('This improves the README file a bit.')
-
- # Review comment are not supported yet
- expect(page).not_to have_content('Really nice change.')
-
- # Comments
- expect(page).to have_content('Nice work! This is a comment from @rymai.')
-
- # Diff comments
- expect(page).to have_content('[Review comment] I like that!')
- expect(page).to have_content('[Review comment] Nice blank line.')
- expect(page).to have_content('[Single diff comment] Much better without this line!')
-
- Page::Issuable::Sidebar.perform do |issuable|
- expect(issuable).to have_label('bug')
- expect(issuable).to have_label('enhancement')
- end
- end
-
- def verify_labels_import
- # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19228
- # to build upon it.
- end
-
- def verify_milestones_import
- # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18727
- # to build upon it.
- end
-
- def verify_wiki_import
- Page::Menu::Side.act { click_wiki }
-
- expect(page).to have_content('Welcome to the test-project wiki!')
- end
- end
-end
diff --git a/qa/qa/specs/features/project/pipelines_spec.rb b/qa/qa/specs/features/project/pipelines_spec.rb
deleted file mode 100644
index ddedde7a8bc..00000000000
--- a/qa/qa/specs/features/project/pipelines_spec.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-module QA
- describe 'CI/CD Pipelines', :core, :docker do
- let(:executor) { "qa-runner-#{Time.now.to_i}" }
-
- after do
- Service::Runner.new(executor).remove!
- end
-
- it 'user registers a new specific runner' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- Factory::Resource::Runner.fabricate! do |runner|
- runner.name = executor
- end
-
- Page::Project::Settings::CICD.perform do |settings|
- sleep 5 # Runner should register within 5 seconds
- settings.refresh
-
- settings.expand_runners_settings do |page|
- expect(page).to have_content(executor)
- expect(page).to have_online_runner
- end
- end
- end
-
- it 'users creates a new pipeline' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- project = Factory::Resource::Project.fabricate! do |project|
- project.name = 'project-with-pipelines'
- project.description = 'Project with CI/CD Pipelines.'
- end
-
- Factory::Resource::Runner.fabricate! do |runner|
- runner.project = project
- runner.name = executor
- runner.tags = %w[qa test]
- end
-
- Factory::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.file_name = '.gitlab-ci.yml'
- push.commit_message = 'Add .gitlab-ci.yml'
- push.file_content = <<~EOF
- test-success:
- tags:
- - qa
- - test
- script: echo 'OK'
-
- test-failure:
- tags:
- - qa
- - test
- script:
- - echo 'FAILURE'
- - exit 1
-
- test-tags:
- tags:
- - qa
- - docker
- script: echo 'NOOP'
-
- test-artifacts:
- tags:
- - qa
- - test
- script: mkdir my-artifacts; echo "CONTENTS" > my-artifacts/artifact.txt
- artifacts:
- paths:
- - my-artifacts/
- EOF
- end
-
- Page::Project::Show.act { wait_for_push }
-
- expect(page).to have_content('Add .gitlab-ci.yml')
-
- Page::Menu::Side.act { click_ci_cd_pipelines }
-
- expect(page).to have_content('All 1')
- expect(page).to have_content('Add .gitlab-ci.yml')
-
- puts 'Waiting for the runner to process the pipeline'
- sleep 15 # Runner should process all jobs within 15 seconds.
-
- Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- expect(pipeline).to be_running
- expect(pipeline).to have_build('test-success', status: :success)
- expect(pipeline).to have_build('test-failure', status: :failed)
- expect(pipeline).to have_build('test-tags', status: :pending)
- expect(pipeline).to have_build('test-artifacts', status: :success)
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/project/wikis_spec.rb b/qa/qa/specs/features/project/wikis_spec.rb
deleted file mode 100644
index 59cc455fffc..00000000000
--- a/qa/qa/specs/features/project/wikis_spec.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-module QA
- describe 'Wiki Functionality', :core do
- def login
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
- end
-
- def validate_content(content)
- expect(page).to have_content('Wiki was successfully updated')
- expect(page).to have_content(/#{content}/)
- end
-
- before do
- login
- end
-
- it 'User creates, edits, clones, and pushes to the wiki' do
- wiki = Factory::Resource::Wiki.fabricate! do |resource|
- resource.title = 'Home'
- resource.content = '# My First Wiki Content'
- resource.message = 'Update home'
- end
-
- validate_content('My First Wiki Content')
-
- Page::Project::Wiki::Edit.act { go_to_edit_page }
- Page::Project::Wiki::New.perform do |page|
- page.set_content("My Second Wiki Content")
- page.save_changes
- end
-
- validate_content('My Second Wiki Content')
-
- Factory::Repository::WikiPush.fabricate! do |push|
- push.wiki = wiki
- push.file_name = 'Home.md'
- push.file_content = '# My Third Wiki Content'
- push.commit_message = 'Update Home.md'
- end
- Page::Menu::Side.act { click_wiki }
-
- expect(page).to have_content('My Third Wiki Content')
- end
- end
-end
diff --git a/qa/qa/specs/features/repository/clone_spec.rb b/qa/qa/specs/features/repository/clone_spec.rb
deleted file mode 100644
index a04ce4e44d9..00000000000
--- a/qa/qa/specs/features/repository/clone_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-module QA
- describe 'clone code from the repository', :core do
- context 'with regular account over http' do
- let(:location) do
- Page::Project::Show.act do
- choose_repository_clone_http
- repository_location
- end
- end
-
- before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- Factory::Resource::Project.fabricate! do |scenario|
- scenario.name = 'project-with-code'
- scenario.description = 'project for git clone tests'
- end
-
- Git::Repository.perform do |repository|
- repository.uri = location.uri
- repository.use_default_credentials
-
- repository.act do
- clone
- configure_identity('GitLab QA', 'root@gitlab.com')
- commit_file('test.rb', 'class Test; end', 'Add Test class')
- commit_file('README.md', '# Test', 'Add Readme')
- push_changes
- end
- end
- end
-
- it 'user performs a deep clone' do
- Git::Repository.perform do |repository|
- repository.uri = location.uri
- repository.use_default_credentials
-
- repository.act { clone }
-
- expect(repository.commits.size).to eq 2
- end
- end
-
- it 'user performs a shallow clone' do
- Git::Repository.perform do |repository|
- repository.uri = location.uri
- repository.use_default_credentials
-
- repository.act { shallow_clone }
-
- expect(repository.commits.size).to eq 1
- expect(repository.commits.first).to include 'Add Readme'
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb
deleted file mode 100644
index c2de94516d9..00000000000
--- a/qa/qa/specs/features/repository/protected_branches_spec.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-module QA
- describe 'branch protection support', :core do
- let(:branch_name) { 'protected-branch' }
- let(:commit_message) { 'Protected push commit message' }
- let(:project) do
- Factory::Resource::Project.fabricate! do |resource|
- resource.name = 'protected-branch-project'
- end
- end
-
- before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
- end
-
- after do
- # We need to clear localStorage because we're using it for the dropdown,
- # and capybara doesn't do this for us.
- # https://github.com/teamcapybara/capybara/issues/1702
- Capybara.execute_script 'localStorage.clear()'
- end
-
- context 'when developers and maintainers are allowed to push to a protected branch' do
- it 'user with push rights successfully pushes to the protected branch' do
- create_protected_branch(allow_to_push: true)
-
- push = push_new_file(branch_name)
-
- expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
- end
- end
-
- context 'when developers and maintainers are not allowed to push to a protected branch' do
- it 'user without push rights fails to push to the protected branch' do
- create_protected_branch(allow_to_push: false)
-
- push = push_new_file(branch_name)
-
- expect(push.output)
- .to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
- expect(push.output)
- .to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
- end
- end
-
- def create_protected_branch(allow_to_push:)
- Factory::Resource::Branch.fabricate! do |resource|
- resource.branch_name = branch_name
- resource.project = project
- resource.allow_to_push = allow_to_push
- resource.protected = true
- end
- end
-
- def push_new_file(branch)
- Factory::Repository::ProjectPush.fabricate! do |resource|
- resource.project = project
- resource.file_name = 'new_file.md'
- resource.file_content = '# This is a new file'
- resource.commit_message = 'Add new_file.md'
- resource.branch_name = branch_name
- resource.new_branch = false
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/repository/push_spec.rb b/qa/qa/specs/features/repository/push_spec.rb
deleted file mode 100644
index fc40b60d915..00000000000
--- a/qa/qa/specs/features/repository/push_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module QA
- describe 'push code to repository', :core do
- context 'with regular account over http' do
- it 'user pushes code to the repository' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- Factory::Repository::ProjectPush.fabricate! do |push|
- push.file_name = 'README.md'
- push.file_content = '# This is a test project'
- push.commit_message = 'Add README.md'
- end
-
- Page::Project::Show.act { wait_for_push }
-
- expect(page).to have_content('README.md')
- expect(page).to have_content('This is a test project')
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/sanity/framework_spec.rb b/qa/qa/specs/features/sanity/framework_spec.rb
new file mode 100644
index 00000000000..aae0f0ade71
--- /dev/null
+++ b/qa/qa/specs/features/sanity/framework_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Framework sanity checks', :orchestrated, :framework do
+ describe 'Passing orchestrated example' do
+ it 'succeeds' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+
+ expect(page).to have_text('Open source software to collaborate on code')
+ end
+ end
+
+ describe 'Failing orchestrated example' do
+ it 'fails' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+
+ expect(page).to have_text("These Aren't the Texts You're Looking For", wait: 1)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index f8f6fe65599..1bd8101c36d 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -5,17 +5,32 @@ module QA
class Runner < Scenario::Template
attr_accessor :tty, :tags, :options
+ DEFAULT_TEST_PATH_ARGS = ['--', File.expand_path('./features', __dir__)].freeze
+
def initialize
@tty = false
@tags = []
- @options = [File.expand_path('./features', __dir__)]
+ @options = []
end
def perform
args = []
args.push('--tty') if tty
- tags.to_a.each { |tag| args.push(['-t', tag.to_s]) }
+
+ if tags.any?
+ tags.each { |tag| args.push(['--tag', tag.to_s]) }
+ else
+ args.push(%w[--tag ~orchestrated]) unless (%w[-t --tag] & options).any?
+ end
+
+ args.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
+
+ QA::Runtime::Env.supported_features.each_key do |key|
+ args.push(["--tag", "~requires_#{key}"]) unless QA::Runtime::Env.can_test? key
+ end
+
args.push(options)
+ args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
Runtime::Browser.configure!
diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb
new file mode 100644
index 00000000000..cf5cd3a79f8
--- /dev/null
+++ b/qa/qa/support/page/logging.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Page
+ module Logging
+ def refresh
+ log("refreshing #{current_url}")
+
+ super
+ end
+
+ def wait(max: 60, time: 0.1, reload: true)
+ log("with wait: max #{max}; time #{time}; reload #{reload}")
+ now = Time.now
+
+ element = super
+
+ log("ended wait after #{Time.now - now} seconds")
+
+ element
+ end
+
+ def scroll_to(selector, text: nil)
+ msg = "scrolling to :#{selector}"
+ msg += " with text: #{text}" if text
+ log(msg)
+
+ super
+ end
+
+ def asset_exists?(url)
+ exists = super
+
+ log("asset_exists? #{url} returned #{exists}")
+
+ exists
+ end
+
+ def find_element(name)
+ log("finding :#{name}")
+
+ element = super
+
+ log("found :#{name}") if element
+
+ element
+ end
+
+ def all_elements(name)
+ log("finding all :#{name}")
+
+ elements = super
+
+ log("found #{elements.size} :#{name}") if elements
+
+ elements
+ end
+
+ def click_element(name)
+ log("clicking :#{name}")
+
+ super
+ end
+
+ def fill_element(name, content)
+ masked_content = name.to_s.include?('password') ? '*****' : content
+
+ log(%Q(filling :#{name} with "#{masked_content}"))
+
+ super
+ end
+
+ def has_element?(name)
+ found = super
+
+ log("has_element? :#{name} returned #{found}")
+
+ found
+ end
+
+ def within_element(name)
+ log("within element :#{name}")
+
+ element = super
+
+ log("end within element :#{name}")
+
+ element
+ end
+
+ private
+
+ def log(msg)
+ QA::Runtime::Logger.debug(msg)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/saml_idp/page/base.rb b/qa/qa/vendor/saml_idp/page/base.rb
new file mode 100644
index 00000000000..286cb0a8cd8
--- /dev/null
+++ b/qa/qa/vendor/saml_idp/page/base.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module QA
+ module Vendor
+ module SAMLIdp
+ module Page
+ class Base
+ include Capybara::DSL
+ include Scenario::Actable
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/saml_idp/page/login.rb b/qa/qa/vendor/saml_idp/page/login.rb
new file mode 100644
index 00000000000..9c1f9904a7a
--- /dev/null
+++ b/qa/qa/vendor/saml_idp/page/login.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'capybara/dsl'
+
+module QA
+ module Vendor
+ module SAMLIdp
+ module Page
+ class Login < Page::Base
+ def login
+ fill_in 'username', with: 'user1'
+ fill_in 'password', with: 'user1pass'
+ click_on 'Login'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/spec/factory/base_spec.rb b/qa/spec/factory/base_spec.rb
deleted file mode 100644
index 04e04886699..00000000000
--- a/qa/spec/factory/base_spec.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-describe QA::Factory::Base do
- let(:factory) { spy('factory') }
- let(:product) { spy('product') }
-
- describe '.fabricate!' do
- subject { Class.new(described_class) }
-
- before do
- allow(QA::Factory::Product).to receive(:new).and_return(product)
- allow(QA::Factory::Product).to receive(:populate!).and_return(product)
- end
-
- it 'instantiates the factory and calls factory method' do
- expect(subject).to receive(:new).and_return(factory)
-
- subject.fabricate!('something')
-
- expect(factory).to have_received(:fabricate!).with('something')
- end
-
- it 'returns fabrication product' do
- allow(subject).to receive(:new).and_return(factory)
-
- result = subject.fabricate!('something')
-
- expect(result).to eq product
- end
-
- it 'yields factory before calling factory method' do
- allow(subject).to receive(:new).and_return(factory)
-
- subject.fabricate! do |factory|
- factory.something!
- end
-
- expect(factory).to have_received(:something!).ordered
- expect(factory).to have_received(:fabricate!).ordered
- end
- end
-
- describe '.dependency' do
- let(:dependency) { spy('dependency') }
-
- before do
- stub_const('Some::MyDependency', dependency)
- end
-
- subject do
- Class.new(described_class) do
- dependency Some::MyDependency, as: :mydep do |factory|
- factory.something!
- end
- end
- end
-
- it 'appends a new dependency and accessors' do
- expect(subject.dependencies).to be_one
- end
-
- it 'defines dependency accessors' do
- expect(subject.new).to respond_to :mydep, :mydep=
- end
-
- describe 'dependencies fabrication' do
- let(:dependency) { double('dependency') }
- let(:instance) { spy('instance') }
-
- subject do
- Class.new(described_class) do
- dependency Some::MyDependency, as: :mydep
- end
- end
-
- before do
- stub_const('Some::MyDependency', dependency)
-
- allow(subject).to receive(:new).and_return(instance)
- allow(instance).to receive(:mydep).and_return(nil)
- allow(QA::Factory::Product).to receive(:new)
- allow(QA::Factory::Product).to receive(:populate!)
- end
-
- it 'builds all dependencies first' do
- expect(dependency).to receive(:fabricate!).once
-
- subject.fabricate!
- end
- end
- end
-
- describe '.product' do
- subject do
- Class.new(described_class) do
- def fabricate!
- "any"
- end
-
- # Defined only to be stubbed
- def self.find_page
- end
-
- product :token do
- find_page.do_something_on_page!
- 'resulting value'
- end
- end
- end
-
- it 'appends new product attribute' do
- expect(subject.attributes).to be_one
- expect(subject.attributes).to have_key(:token)
- end
-
- describe 'populating fabrication product with data' do
- let(:page) { spy('page') }
-
- before do
- allow(factory).to receive(:class).and_return(subject)
- allow(QA::Factory::Product).to receive(:new).and_return(product)
- allow(product).to receive(:page).and_return(page)
- allow(subject).to receive(:find_page).and_return(page)
- end
-
- it 'populates product after fabrication' do
- subject.fabricate!
-
- expect(product.token).to eq 'resulting value'
- expect(page).to have_received(:do_something_on_page!)
- end
- end
- end
-end
diff --git a/qa/spec/factory/dependency_spec.rb b/qa/spec/factory/dependency_spec.rb
deleted file mode 100644
index 8aaa6665a18..00000000000
--- a/qa/spec/factory/dependency_spec.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-describe QA::Factory::Dependency do
- let(:dependency) { spy('dependency' ) }
- let(:factory) { spy('factory') }
- let(:block) { spy('block') }
-
- let(:signature) do
- double('signature', factory: dependency, block: block)
- end
-
- subject do
- described_class.new(:mydep, factory, signature)
- end
-
- describe '#overridden?' do
- it 'returns true if factory has overridden dependency' do
- allow(factory).to receive(:mydep).and_return('something')
-
- expect(subject).to be_overridden
- end
-
- it 'returns false if dependency has not been overridden' do
- allow(factory).to receive(:mydep).and_return(nil)
-
- expect(subject).not_to be_overridden
- end
- end
-
- describe '#build!' do
- context 'when dependency has been overridden' do
- before do
- allow(subject).to receive(:overridden?).and_return(true)
- end
-
- it 'does not fabricate dependency' do
- subject.build!
-
- expect(dependency).not_to have_received(:fabricate!)
- end
- end
-
- context 'when dependency has not been overridden' do
- before do
- allow(subject).to receive(:overridden?).and_return(false)
- end
-
- it 'fabricates dependency' do
- subject.build!
-
- expect(dependency).to have_received(:fabricate!)
- end
-
- it 'sets product in the factory' do
- subject.build!
-
- expect(factory).to have_received(:mydep=).with(dependency)
- end
-
- context 'when receives a caller factory as block argument' do
- let(:dependency) { QA::Factory::Base }
-
- it 'calls given block with dependency factory and caller factory' do
- allow_any_instance_of(QA::Factory::Base).to receive(:fabricate!).and_return(factory)
- allow(QA::Factory::Product).to receive(:populate!).and_return(spy('any'))
-
- subject.build!
-
- expect(block).to have_received(:call).with(an_instance_of(QA::Factory::Base), factory)
- end
- end
- end
- end
-end
diff --git a/qa/spec/factory/product_spec.rb b/qa/spec/factory/product_spec.rb
deleted file mode 100644
index f245aabbf43..00000000000
--- a/qa/spec/factory/product_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-describe QA::Factory::Product do
- let(:factory) do
- QA::Factory::Base.new
- end
-
- let(:attributes) do
- { test: QA::Factory::Product::Attribute.new(:test, proc { 'returned' }) }
- end
-
- let(:product) { spy('product') }
-
- before do
- allow(QA::Factory::Base).to receive(:attributes).and_return(attributes)
- end
-
- describe '.populate!' do
- it 'returns a fabrication product and define factory attributes as its methods' do
- expect(described_class).to receive(:new).and_return(product)
-
- result = described_class.populate!(factory) do |instance|
- instance.something = 'string'
- end
-
- expect(result).to be product
- expect(result.test).to eq('returned')
- end
- end
-
- describe '.visit!' do
- it 'makes it possible to visit fabrication product' do
- allow_any_instance_of(described_class)
- .to receive(:current_url).and_return('some url')
- allow_any_instance_of(described_class)
- .to receive(:visit).and_return('visited some url')
-
- expect(subject.visit!).to eq 'visited some url'
- end
- end
-end
diff --git a/qa/spec/factory/resource/user_spec.rb b/qa/spec/factory/resource/user_spec.rb
new file mode 100644
index 00000000000..820c506b715
--- /dev/null
+++ b/qa/spec/factory/resource/user_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+describe QA::Resource::User do
+ describe "#fabricate_via_api!" do
+ Response = Struct.new(:code, :body)
+
+ it 'fetches an existing user' do
+ existing_users = [
+ {
+ id: '0',
+ name: 'name',
+ username: 'name',
+ web_url: ''
+ }
+ ]
+ users_response = Response.new('200', JSON.dump(existing_users))
+ single_user_response = Response.new('200', JSON.dump(existing_users.first))
+
+ expect(subject).to receive(:api_get_from).with("/users?username=name").and_return(users_response)
+ expect(subject).to receive(:api_get_from).with("/users/0").and_return(single_user_response)
+
+ subject.username = 'name'
+ subject.fabricate_via_api!
+
+ expect(subject.api_response).to eq(existing_users.first)
+ end
+
+ it 'tries to create a user if it does not exist' do
+ expect(subject).to receive(:api_get_from).with("/users?username=foo").and_return(Response.new('200', '[]'))
+ expect(subject).to receive(:api_post).and_return({ web_url: '' })
+
+ subject.username = 'foo'
+ subject.fabricate_via_api!
+ end
+ end
+end
diff --git a/qa/spec/git/repository_spec.rb b/qa/spec/git/repository_spec.rb
index 5c65128d10c..faa154c78da 100644
--- a/qa/spec/git/repository_spec.rb
+++ b/qa/spec/git/repository_spec.rb
@@ -1,17 +1,18 @@
describe QA::Git::Repository do
+ include Support::StubENV
+
let(:repository) { described_class.new }
before do
+ stub_env('GITLAB_USERNAME', 'root')
cd_empty_temp_directory
set_bad_uri
repository.use_default_credentials
end
describe '#clone' do
- it 'redacts credentials from the URI in output' do
- output, _ = repository.clone
-
- expect(output).to include("fatal: unable to access 'http://****@foo/bar.git/'")
+ it 'is unable to resolve host' do
+ expect(repository.clone).to include("fatal: unable to access 'http://root@foo/bar.git/'")
end
end
@@ -20,16 +21,44 @@ describe QA::Git::Repository do
`git init` # need a repo to push from
end
- it 'redacts credentials from the URI in output' do
- output, _ = repository.push_changes
+ it 'fails to push changes' do
+ expect(repository.push_changes).to include("error: failed to push some refs to 'http://root@foo/bar.git'")
+ end
+ end
+
+ describe '#git_protocol=' do
+ [0, 1, 2].each do |version|
+ it "configures git to use protocol version #{version}" do
+ expect(repository).to receive(:run).with("git config protocol.version #{version}")
+ repository.git_protocol = version
+ end
+ end
+
+ it 'raises an error if the version is unsupported' do
+ expect { repository.git_protocol = 'foo' }.to raise_error(ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2")
+ end
+ end
+
+ describe '#fetch_supported_git_protocol' do
+ it "reports the detected version" do
+ expect(repository).to receive(:run).and_return("packet: git< version 2")
+ expect(repository.fetch_supported_git_protocol).to eq('2')
+ end
+
+ it 'reports unknown if version is unknown' do
+ expect(repository).to receive(:run).and_return("packet: git< version -1")
+ expect(repository.fetch_supported_git_protocol).to eq('unknown')
+ end
- expect(output).to include("error: failed to push some refs to 'http://****@foo/bar.git'")
+ it 'reports unknown if content does not identify a version' do
+ expect(repository).to receive(:run).and_return("foo")
+ expect(repository.fetch_supported_git_protocol).to eq('unknown')
end
end
def cd_empty_temp_directory
tmp_dir = 'tmp/git-repository-spec/'
- FileUtils.rm_r(tmp_dir) if ::File.exist?(tmp_dir)
+ FileUtils.rm_rf(tmp_dir) if ::File.exist?(tmp_dir)
FileUtils.mkdir_p tmp_dir
FileUtils.cd tmp_dir
end
diff --git a/qa/spec/page/base_spec.rb b/qa/spec/page/base_spec.rb
index 52daa9697ee..076a8087db5 100644
--- a/qa/spec/page/base_spec.rb
+++ b/qa/spec/page/base_spec.rb
@@ -9,12 +9,12 @@ describe QA::Page::Base do
subject do
Class.new(described_class) do
view 'path/to/some/view.html.haml' do
- element :something, 'string pattern'
- element :something_else, /regexp pattern/
+ element :something, 'string pattern' # rubocop:disable QA/ElementWithPattern
+ element :something_else, /regexp pattern/ # rubocop:disable QA/ElementWithPattern
end
view 'path/to/some/_partial.html.haml' do
- element :another_element, 'string pattern'
+ element :another_element, 'string pattern' # rubocop:disable QA/ElementWithPattern
end
end
end
diff --git a/qa/spec/page/logging_spec.rb b/qa/spec/page/logging_spec.rb
new file mode 100644
index 00000000000..9d56353062b
--- /dev/null
+++ b/qa/spec/page/logging_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'capybara/dsl'
+
+describe QA::Support::Page::Logging do
+ include Support::StubENV
+
+ let(:page) { double().as_null_object }
+
+ before do
+ logger = Logger.new $stdout
+ logger.level = ::Logger::DEBUG
+ QA::Runtime::Logger.logger = logger
+
+ allow(Capybara).to receive(:current_session).and_return(page)
+ allow(page).to receive(:current_url).and_return('http://current-url')
+ allow(page).to receive(:has_css?).with(any_args).and_return(true)
+ end
+
+ subject do
+ Class.new(QA::Page::Base) do
+ prepend QA::Support::Page::Logging
+ end.new
+ end
+
+ it 'logs refresh' do
+ expect { subject.refresh }
+ .to output(%r{refreshing http://current-url}).to_stdout_from_any_process
+ end
+
+ it 'logs wait' do
+ expect { subject.wait(max: 0) {} }
+ .to output(/with wait/).to_stdout_from_any_process
+ expect { subject.wait(max: 0) {} }
+ .to output(/ended wait after .* seconds$/).to_stdout_from_any_process
+ end
+
+ it 'logs scroll_to' do
+ expect { subject.scroll_to(:element) }
+ .to output(/scrolling to :element/).to_stdout_from_any_process
+ end
+
+ it 'logs asset_exists?' do
+ expect { subject.asset_exists?('http://asset-url') }
+ .to output(%r{asset_exists\? http://asset-url returned false}).to_stdout_from_any_process
+ end
+
+ it 'logs find_element' do
+ expect { subject.find_element(:element) }
+ .to output(/found :element/).to_stdout_from_any_process
+ end
+
+ it 'logs click_element' do
+ expect { subject.click_element(:element) }
+ .to output(/clicking :element/).to_stdout_from_any_process
+ end
+
+ it 'logs fill_element' do
+ expect { subject.fill_element(:element, 'foo') }
+ .to output(/filling :element with "foo"/).to_stdout_from_any_process
+ end
+
+ it 'logs has_element?' do
+ expect { subject.has_element?(:element) }
+ .to output(/has_element\? :element returned true/).to_stdout_from_any_process
+ end
+
+ it 'logs within_element' do
+ expect { subject.within_element(:element) }
+ .to output(/within element :element/).to_stdout_from_any_process
+ expect { subject.within_element(:element) }
+ .to output(/end within element :element/).to_stdout_from_any_process
+ end
+
+ context 'all_elements' do
+ it 'logs the number of elements found' do
+ allow(page).to receive(:all).and_return([1, 2])
+
+ expect { subject.all_elements(:element) }
+ .to output(/finding all :element/).to_stdout_from_any_process
+ expect { subject.all_elements(:element) }
+ .to output(/found 2 :element/).to_stdout_from_any_process
+ end
+
+ it 'logs 0 if no elements are found' do
+ allow(page).to receive(:all).and_return([])
+
+ expect { subject.all_elements(:element) }
+ .to output(/finding all :element/).to_stdout_from_any_process
+ expect { subject.all_elements(:element) }
+ .not_to output(/found 0 :elements/).to_stdout_from_any_process
+ end
+ end
+end
diff --git a/qa/spec/page/validator_spec.rb b/qa/spec/page/validator_spec.rb
index 55957649904..0ae6e66d767 100644
--- a/qa/spec/page/validator_spec.rb
+++ b/qa/spec/page/validator_spec.rb
@@ -30,7 +30,7 @@ describe QA::Page::Validator do
let(:view) { spy('view') }
before do
- allow(QA::Page::Admin::Settings::Main)
+ allow(QA::Page::Admin::Settings::Repository)
.to receive(:views).and_return([view])
end
diff --git a/qa/spec/page/view_spec.rb b/qa/spec/page/view_spec.rb
index 34d2ff11447..d7b3ccd316d 100644
--- a/qa/spec/page/view_spec.rb
+++ b/qa/spec/page/view_spec.rb
@@ -8,8 +8,8 @@ describe QA::Page::View do
describe '.evaluate' do
it 'evaluates a block and returns a DSL object' do
results = described_class.evaluate do
- element :something, 'my pattern'
- element :something_else, /another pattern/
+ element :something
+ element :something_else
end
expect(results.elements.size).to eq 2
diff --git a/qa/spec/resource/api_fabricator_spec.rb b/qa/spec/resource/api_fabricator_spec.rb
new file mode 100644
index 00000000000..a5ed4422f6e
--- /dev/null
+++ b/qa/spec/resource/api_fabricator_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+describe QA::Resource::ApiFabricator do
+ let(:resource_without_api_support) do
+ Class.new do
+ def self.name
+ 'FooBarResource'
+ end
+ end
+ end
+
+ let(:resource_with_api_support) do
+ Class.new do
+ def self.name
+ 'FooBarResource'
+ end
+
+ def api_get_path
+ '/foo'
+ end
+
+ def api_post_path
+ '/bar'
+ end
+
+ def api_post_body
+ { name: 'John Doe' }
+ end
+ end
+ end
+
+ before do
+ allow(subject).to receive(:current_url).and_return('')
+ end
+
+ subject { resource.tap { |f| f.include(described_class) }.new }
+
+ describe '#api_support?' do
+ let(:api_client) { spy('Runtime::API::Client') }
+ let(:api_client_instance) { double('API Client') }
+
+ context 'when resource does not support fabrication via the API' do
+ let(:resource) { resource_without_api_support }
+
+ it 'returns false' do
+ expect(subject).not_to be_api_support
+ end
+ end
+
+ context 'when resource supports fabrication via the API' do
+ let(:resource) { resource_with_api_support }
+
+ it 'returns false' do
+ expect(subject).to be_api_support
+ end
+ end
+ end
+
+ describe '#fabricate_via_api!' do
+ let(:api_client) { spy('Runtime::API::Client') }
+ let(:api_client_instance) { double('API Client') }
+
+ before do
+ stub_const('QA::Runtime::API::Client', api_client)
+
+ allow(api_client).to receive(:new).and_return(api_client_instance)
+ allow(api_client_instance).to receive(:personal_access_token).and_return('foo')
+ end
+
+ context 'when resource does not support fabrication via the API' do
+ let(:resource) { resource_without_api_support }
+
+ it 'raises a NotImplementedError exception' do
+ expect { subject.fabricate_via_api! }.to raise_error(NotImplementedError, "Resource FooBarResource does not support fabrication via the API!")
+ end
+ end
+
+ context 'when resource supports fabrication via the API' do
+ let(:resource) { resource_with_api_support }
+ let(:api_request) { spy('Runtime::API::Request') }
+ let(:resource_web_url) { 'http://example.org/api/v4/foo' }
+ let(:response) { { id: 1, name: 'John Doe', web_url: resource_web_url } }
+ let(:raw_post) { double('Raw POST response', code: 201, body: response.to_json) }
+
+ before do
+ stub_const('QA::Runtime::API::Request', api_request)
+
+ allow(api_request).to receive(:new).and_return(double(url: resource_web_url))
+ end
+
+ context 'when creating a resource' do
+ before do
+ allow(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
+ end
+
+ it 'returns the resource URL' do
+ expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
+ expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
+
+ expect(subject.fabricate_via_api!).to eq(resource_web_url)
+ end
+
+ it 'populates api_resource with the resource' do
+ subject.fabricate_via_api!
+
+ expect(subject.api_resource).to eq(response)
+ end
+
+ context 'when the POST fails' do
+ let(:post_response) { { error: "Name already taken." } }
+ let(:raw_post) { double('Raw POST response', code: 400, body: post_response.to_json) }
+
+ it 'raises a ResourceFabricationFailedError exception' do
+ expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
+ expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
+
+ expect { subject.fabricate_via_api! }.to raise_error(described_class::ResourceFabricationFailedError, "Fabrication of FooBarResource using the API failed (400) with `#{raw_post}`.")
+ expect(subject.api_resource).to be_nil
+ end
+ end
+ end
+
+ context '#transform_api_resource' do
+ let(:resource) do
+ Class.new do
+ def self.name
+ 'FooBarResource'
+ end
+
+ def api_get_path
+ '/foo'
+ end
+
+ def api_post_path
+ '/bar'
+ end
+
+ def api_post_body
+ { name: 'John Doe' }
+ end
+
+ def transform_api_resource(resource)
+ resource[:new] = 'foobar'
+ resource
+ end
+ end
+ end
+
+ let(:response) { { existing: 'foo', web_url: resource_web_url } }
+ let(:transformed_resource) { { existing: 'foo', new: 'foobar', web_url: resource_web_url } }
+
+ it 'transforms the resource' do
+ expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
+ expect(subject).to receive(:transform_api_resource).with(response).and_return(transformed_resource)
+
+ subject.fabricate_via_api!
+ end
+ end
+ end
+ end
+end
diff --git a/qa/spec/resource/base_spec.rb b/qa/spec/resource/base_spec.rb
new file mode 100644
index 00000000000..dc9e16792d3
--- /dev/null
+++ b/qa/spec/resource/base_spec.rb
@@ -0,0 +1,246 @@
+# frozen_string_literal: true
+
+describe QA::Resource::Base do
+ include Support::StubENV
+
+ let(:resource) { spy('resource') }
+ let(:location) { 'http://location' }
+
+ shared_context 'fabrication context' do
+ subject do
+ Class.new(described_class) do
+ def self.name
+ 'MyResource'
+ end
+ end
+ end
+
+ before do
+ allow(subject).to receive(:current_url).and_return(location)
+ allow(subject).to receive(:new).and_return(resource)
+ end
+ end
+
+ shared_examples 'fabrication method' do |fabrication_method_called, actual_fabrication_method = nil|
+ let(:fabrication_method_used) { actual_fabrication_method || fabrication_method_called }
+
+ it 'yields resource before calling resource method' do
+ expect(resource).to receive(:something!).ordered
+ expect(resource).to receive(fabrication_method_used).ordered.and_return(location)
+
+ subject.public_send(fabrication_method_called, resource: resource) do |resource|
+ resource.something!
+ end
+ end
+
+ it 'does not log the resource and build method when QA_DEBUG=false' do
+ stub_env('QA_DEBUG', 'false')
+ expect(resource).to receive(fabrication_method_used).and_return(location)
+
+ expect { subject.public_send(fabrication_method_called, 'something', resource: resource) }
+ .not_to output.to_stdout
+ end
+ end
+
+ describe '.fabricate!' do
+ context 'when resource does not support fabrication via the API' do
+ before do
+ expect(described_class).to receive(:fabricate_via_api!).and_raise(NotImplementedError)
+ end
+
+ it 'calls .fabricate_via_browser_ui!' do
+ expect(described_class).to receive(:fabricate_via_browser_ui!)
+
+ described_class.fabricate!
+ end
+ end
+
+ context 'when resource supports fabrication via the API' do
+ it 'calls .fabricate_via_browser_ui!' do
+ expect(described_class).to receive(:fabricate_via_api!)
+
+ described_class.fabricate!
+ end
+ end
+ end
+
+ describe '.fabricate_via_api!' do
+ include_context 'fabrication context'
+
+ it_behaves_like 'fabrication method', :fabricate_via_api!
+
+ it 'instantiates the resource, calls resource method returns the resource' do
+ expect(resource).to receive(:fabricate_via_api!).and_return(location)
+
+ result = subject.fabricate_via_api!(resource: resource, parents: [])
+
+ expect(result).to eq(resource)
+ end
+
+ it 'logs the resource and build method when QA_DEBUG=true' do
+ stub_env('QA_DEBUG', 'true')
+ expect(resource).to receive(:fabricate_via_api!).and_return(location)
+
+ expect { subject.fabricate_via_api!('something', resource: resource, parents: []) }
+ .to output(/==> Built a MyResource via api in [\d\.\-e]+ seconds+/)
+ .to_stdout
+ end
+ end
+
+ describe '.fabricate_via_browser_ui!' do
+ include_context 'fabrication context'
+
+ it_behaves_like 'fabrication method', :fabricate_via_browser_ui!, :fabricate!
+
+ it 'instantiates the resource and calls resource method' do
+ subject.fabricate_via_browser_ui!('something', resource: resource, parents: [])
+
+ expect(resource).to have_received(:fabricate!).with('something')
+ end
+
+ it 'returns fabrication resource' do
+ result = subject.fabricate_via_browser_ui!('something', resource: resource, parents: [])
+
+ expect(result).to eq(resource)
+ end
+
+ it 'logs the resource and build method when QA_DEBUG=true' do
+ stub_env('QA_DEBUG', 'true')
+
+ expect { subject.fabricate_via_browser_ui!('something', resource: resource, parents: []) }
+ .to output(/==> Built a MyResource via browser_ui in [\d\.\-e]+ seconds+/)
+ .to_stdout
+ end
+ end
+
+ shared_context 'simple resource' do
+ subject do
+ Class.new(QA::Resource::Base) do
+ attribute :test do
+ 'block'
+ end
+
+ attribute :no_block
+
+ def fabricate!
+ 'any'
+ end
+
+ def self.current_url
+ 'http://stub'
+ end
+ end
+ end
+
+ let(:resource) { subject.new }
+ end
+
+ describe '.attribute' do
+ include_context 'simple resource'
+
+ it 'appends new attribute' do
+ expect(subject.attributes_names).to eq([:no_block, :test, :web_url])
+ end
+
+ context 'when the attribute is populated via a block' do
+ it 'returns value from the block' do
+ result = subject.fabricate!(resource: resource)
+
+ expect(result).to be_a(described_class)
+ expect(result.test).to eq('block')
+ end
+ end
+
+ context 'when the attribute is populated via the api' do
+ let(:api_resource) { { no_block: 'api' } }
+
+ before do
+ expect(resource).to receive(:api_resource).and_return(api_resource)
+ end
+
+ it 'returns value from api' do
+ result = subject.fabricate!(resource: resource)
+
+ expect(result).to be_a(described_class)
+ expect(result.no_block).to eq('api')
+ end
+
+ context 'when the attribute also has a block' do
+ let(:api_resource) { { test: 'api_with_block' } }
+
+ before do
+ allow(QA::Runtime::Logger).to receive(:info)
+ end
+
+ it 'returns value from api and emits an INFO log entry' do
+ result = subject.fabricate!(resource: resource)
+
+ expect(result).to be_a(described_class)
+ expect(result.test).to eq('api_with_block')
+ expect(QA::Runtime::Logger)
+ .to have_received(:info).with(/api_with_block/)
+ end
+ end
+ end
+
+ context 'when the attribute is populated via direct assignment' do
+ before do
+ resource.test = 'value'
+ end
+
+ it 'returns value from the assignment' do
+ result = subject.fabricate!(resource: resource)
+
+ expect(result).to be_a(described_class)
+ expect(result.test).to eq('value')
+ end
+
+ context 'when the api also has such response' do
+ before do
+ allow(resource).to receive(:api_resource).and_return({ test: 'api' })
+ end
+
+ it 'returns value from the assignment' do
+ result = subject.fabricate!(resource: resource)
+
+ expect(result).to be_a(described_class)
+ expect(result.test).to eq('value')
+ end
+ end
+ end
+
+ context 'when the attribute has no value' do
+ it 'raises an error because no values could be found' do
+ result = subject.fabricate!(resource: resource)
+
+ expect { result.no_block }
+ .to raise_error(described_class::NoValueError, "No value was computed for no_block of #{resource.class.name}.")
+ end
+ end
+ end
+
+ describe '#web_url' do
+ include_context 'simple resource'
+
+ it 'sets #web_url to #current_url after fabrication' do
+ subject.fabricate!(resource: resource)
+
+ expect(resource.web_url).to eq(subject.current_url)
+ end
+ end
+
+ describe '#visit!' do
+ include_context 'simple resource'
+
+ before do
+ allow(resource).to receive(:visit)
+ end
+
+ it 'calls #visit with the underlying #web_url' do
+ resource.web_url = subject.current_url
+ resource.visit!
+
+ expect(resource).to have_received(:visit).with(subject.current_url)
+ end
+ end
+end
diff --git a/qa/spec/resource/repository/push_spec.rb b/qa/spec/resource/repository/push_spec.rb
new file mode 100644
index 00000000000..bf3ebce0cfe
--- /dev/null
+++ b/qa/spec/resource/repository/push_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+describe QA::Resource::Repository::Push do
+ describe '.files=' do
+ let(:files) do
+ [
+ {
+ name: 'file.txt',
+ content: 'foo'
+ }
+ ]
+ end
+
+ it 'raises an error if files is not an array' do
+ expect { subject.files = '' }.to raise_error(ArgumentError)
+ end
+
+ it 'raises an error if files is an empty array' do
+ expect { subject.files = [] }.to raise_error(ArgumentError)
+ end
+
+ it 'does not raise if files is an array' do
+ expect { subject.files = files }.not_to raise_error
+ end
+ end
+end
diff --git a/qa/spec/runtime/api/client_spec.rb b/qa/spec/runtime/api/client_spec.rb
index d497d8839b8..975586b505f 100644
--- a/qa/spec/runtime/api/client_spec.rb
+++ b/qa/spec/runtime/api/client_spec.rb
@@ -13,18 +13,27 @@ describe QA::Runtime::API::Client do
end
end
- describe '#get_personal_access_token' do
- it 'returns specified token from env' do
- stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
+ describe '#personal_access_token' do
+ context 'when QA::Runtime::Env.personal_access_token is present' do
+ before do
+ allow(QA::Runtime::Env).to receive(:personal_access_token).and_return('a_token')
+ end
- expect(described_class.new.get_personal_access_token).to eq 'a_token'
+ it 'returns specified token from env' do
+ expect(described_class.new.personal_access_token).to eq 'a_token'
+ end
end
- it 'returns a created token' do
- allow_any_instance_of(described_class)
- .to receive(:create_personal_access_token).and_return('created_token')
+ context 'when QA::Runtime::Env.personal_access_token is nil' do
+ before do
+ allow(QA::Runtime::Env).to receive(:personal_access_token).and_return(nil)
+ end
- expect(described_class.new.get_personal_access_token).to eq 'created_token'
+ it 'returns a created token' do
+ expect(subject).to receive(:create_personal_access_token).and_return('created_token')
+
+ expect(subject.personal_access_token).to eq 'created_token'
+ end
end
end
end
diff --git a/qa/spec/runtime/api/request_spec.rb b/qa/spec/runtime/api/request_spec.rb
index 80e3149f32d..08233e3c1d6 100644
--- a/qa/spec/runtime/api/request_spec.rb
+++ b/qa/spec/runtime/api/request_spec.rb
@@ -1,17 +1,23 @@
describe QA::Runtime::API::Request do
- include Support::StubENV
+ let(:client) { QA::Runtime::API::Client.new('http://example.com') }
+ let(:request) { described_class.new(client, '/users') }
before do
- stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
+ allow(client).to receive(:personal_access_token).and_return('a_token')
end
- let(:client) { QA::Runtime::API::Client.new('http://example.com') }
- let(:request) { described_class.new(client, '/users') }
-
describe '#url' do
- it 'returns the full api request url' do
+ it 'returns the full API request url' do
expect(request.url).to eq 'http://example.com/api/v4/users?private_token=a_token'
end
+
+ context 'when oauth_access_token is passed in the query string' do
+ let(:request) { described_class.new(client, '/users', { oauth_access_token: 'foo' }) }
+
+ it 'does not adds a private_token query string' do
+ expect(request.url).to eq 'http://example.com/api/v4/users?oauth_access_token=foo'
+ end
+ end
end
describe '#request_path' do
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index 851026c71f0..ded51d5bb7c 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -1,39 +1,66 @@
+# frozen_string_literal: true
+
describe QA::Runtime::Env do
include Support::StubENV
- describe '.chrome_headless?' do
+ shared_examples 'boolean method' do |**kwargs|
+ it_behaves_like 'boolean method with parameter', kwargs
+ end
+
+ shared_examples 'boolean method with parameter' do |method:, param: nil, env_key:, default:|
context 'when there is an env variable set' do
it 'returns false when falsey values specified' do
- stub_env('CHROME_HEADLESS', 'false')
- expect(described_class.chrome_headless?).to be_falsey
+ stub_env(env_key, 'false')
+ expect(described_class.public_send(method, *param)).to be_falsey
- stub_env('CHROME_HEADLESS', 'no')
- expect(described_class.chrome_headless?).to be_falsey
+ stub_env(env_key, 'no')
+ expect(described_class.public_send(method, *param)).to be_falsey
- stub_env('CHROME_HEADLESS', '0')
- expect(described_class.chrome_headless?).to be_falsey
+ stub_env(env_key, '0')
+ expect(described_class.public_send(method, *param)).to be_falsey
end
it 'returns true when anything else specified' do
- stub_env('CHROME_HEADLESS', 'true')
- expect(described_class.chrome_headless?).to be_truthy
+ stub_env(env_key, 'true')
+ expect(described_class.public_send(method, *param)).to be_truthy
- stub_env('CHROME_HEADLESS', '1')
- expect(described_class.chrome_headless?).to be_truthy
+ stub_env(env_key, '1')
+ expect(described_class.public_send(method, *param)).to be_truthy
- stub_env('CHROME_HEADLESS', 'anything')
- expect(described_class.chrome_headless?).to be_truthy
+ stub_env(env_key, 'anything')
+ expect(described_class.public_send(method, *param)).to be_truthy
end
end
context 'when there is no env variable set' do
- it 'returns the default, true' do
- stub_env('CHROME_HEADLESS', nil)
- expect(described_class.chrome_headless?).to be_truthy
+ it "returns the default, #{default}" do
+ stub_env(env_key, nil)
+ expect(described_class.public_send(method, *param)).to be(default)
end
end
end
+ describe '.signup_disabled?' do
+ it_behaves_like 'boolean method',
+ method: :signup_disabled?,
+ env_key: 'SIGNUP_DISABLED',
+ default: false
+ end
+
+ describe '.debug?' do
+ it_behaves_like 'boolean method',
+ method: :debug?,
+ env_key: 'QA_DEBUG',
+ default: false
+ end
+
+ describe '.chrome_headless?' do
+ it_behaves_like 'boolean method',
+ method: :chrome_headless?,
+ env_key: 'CHROME_HEADLESS',
+ default: true
+ end
+
describe '.running_in_ci?' do
context 'when there is an env variable set' do
it 'returns true if CI' do
@@ -56,29 +83,82 @@ describe QA::Runtime::Env do
end
end
- describe '.user_type' do
- it 'returns standard if not defined' do
- expect(described_class.user_type).to eq('standard')
+ describe '.personal_access_token' do
+ around do |example|
+ described_class.instance_variable_set(:@personal_access_token, nil)
+ example.run
+ described_class.instance_variable_set(:@personal_access_token, nil)
+ end
+
+ context 'when PERSONAL_ACCESS_TOKEN is set' do
+ before do
+ stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
+ end
+
+ it 'returns specified token from env' do
+ expect(described_class.personal_access_token).to eq 'a_token'
+ end
+ end
+
+ context 'when @personal_access_token is set' do
+ before do
+ described_class.personal_access_token = 'another_token'
+ end
+
+ it 'returns the instance variable value' do
+ expect(described_class.personal_access_token).to eq 'another_token'
+ end
+ end
+ end
+
+ describe '.personal_access_token=' do
+ around do |example|
+ described_class.instance_variable_set(:@personal_access_token, nil)
+ example.run
+ described_class.instance_variable_set(:@personal_access_token, nil)
end
- it 'returns standard as defined' do
- stub_env('GITLAB_USER_TYPE', 'standard')
- expect(described_class.user_type).to eq('standard')
+ it 'saves the token' do
+ described_class.personal_access_token = 'a_token'
+
+ expect(described_class.personal_access_token).to eq 'a_token'
+ end
+ end
+
+ describe '.forker?' do
+ before do
+ stub_env('GITLAB_FORKER_USERNAME', nil)
+ stub_env('GITLAB_FORKER_PASSWORD', nil)
+ end
+
+ it 'returns false if no forker credentials are defined' do
+ expect(described_class).not_to be_forker
end
- it 'returns ldap as defined' do
- stub_env('GITLAB_USER_TYPE', 'ldap')
- expect(described_class.user_type).to eq('ldap')
+ it 'returns false if only forker username is defined' do
+ stub_env('GITLAB_FORKER_USERNAME', 'foo')
+
+ expect(described_class).not_to be_forker
+ end
+
+ it 'returns false if only forker password is defined' do
+ stub_env('GITLAB_FORKER_PASSWORD', 'bar')
+
+ expect(described_class).not_to be_forker
end
- it 'returns an error if invalid user type' do
- stub_env('GITLAB_USER_TYPE', 'foobar')
- expect { described_class.user_type }.to raise_error(ArgumentError)
+ it 'returns true if forker username and password are defined' do
+ stub_env('GITLAB_FORKER_USERNAME', 'foo')
+ stub_env('GITLAB_FORKER_PASSWORD', 'bar')
+
+ expect(described_class).to be_forker
end
end
describe '.github_access_token' do
it 'returns "" if GITHUB_ACCESS_TOKEN is not defined' do
+ stub_env('GITHUB_ACCESS_TOKEN', nil)
+
expect(described_class.github_access_token).to eq('')
end
@@ -90,6 +170,8 @@ describe QA::Runtime::Env do
describe '.require_github_access_token!' do
it 'raises ArgumentError if GITHUB_ACCESS_TOKEN is not defined' do
+ stub_env('GITHUB_ACCESS_TOKEN', nil)
+
expect { described_class.require_github_access_token! }.to raise_error(ArgumentError)
end
@@ -99,4 +181,30 @@ describe QA::Runtime::Env do
expect { described_class.require_github_access_token! }.not_to raise_error
end
end
+
+ describe '.log_destination' do
+ it 'returns $stdout if QA_LOG_PATH is not defined' do
+ stub_env('QA_LOG_PATH', nil)
+
+ expect(described_class.log_destination).to eq($stdout)
+ end
+
+ it 'returns the path if QA_LOG_PATH is defined' do
+ stub_env('QA_LOG_PATH', 'path/to_file')
+
+ expect(described_class.log_destination).to eq('path/to_file')
+ end
+ end
+
+ describe '.can_test?' do
+ it_behaves_like 'boolean method with parameter',
+ method: :can_test?,
+ param: :git_protocol_v2,
+ env_key: 'QA_CAN_TEST_GIT_PROTOCOL_V2',
+ default: true
+
+ it 'raises ArgumentError if feature is unknown' do
+ expect { described_class.can_test? :foo }.to raise_error(ArgumentError, 'Unknown feature "foo"')
+ end
+ end
end
diff --git a/qa/spec/runtime/logger_spec.rb b/qa/spec/runtime/logger_spec.rb
new file mode 100644
index 00000000000..44be3381bff
--- /dev/null
+++ b/qa/spec/runtime/logger_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+describe QA::Runtime::Logger do
+ before do
+ logger = Logger.new $stdout
+ logger.level = ::Logger::DEBUG
+ described_class.logger = logger
+ end
+
+ it 'logs debug' do
+ expect { described_class.debug('test') }.to output(/DEBUG -- : test/).to_stdout_from_any_process
+ end
+
+ it 'logs info' do
+ expect { described_class.info('test') }.to output(/INFO -- : test/).to_stdout_from_any_process
+ end
+
+ it 'logs warn' do
+ expect { described_class.warn('test') }.to output(/WARN -- : test/).to_stdout_from_any_process
+ end
+
+ it 'logs error' do
+ expect { described_class.error('test') }.to output(/ERROR -- : test/).to_stdout_from_any_process
+ end
+
+ it 'logs fatal' do
+ expect { described_class.fatal('test') }.to output(/FATAL -- : test/).to_stdout_from_any_process
+ end
+
+ it 'logs unknown' do
+ expect { described_class.unknown('test') }.to output(/ANY -- : test/).to_stdout_from_any_process
+ end
+end
diff --git a/qa/spec/scenario/test/instance/all_spec.rb b/qa/spec/scenario/test/instance/all_spec.rb
new file mode 100644
index 00000000000..9311d1d8199
--- /dev/null
+++ b/qa/spec/scenario/test/instance/all_spec.rb
@@ -0,0 +1,3 @@
+describe QA::Scenario::Test::Instance::All do
+ it_behaves_like 'a QA scenario class'
+end
diff --git a/qa/spec/scenario/test/instance/smoke_spec.rb b/qa/spec/scenario/test/instance/smoke_spec.rb
new file mode 100644
index 00000000000..b5db9783af3
--- /dev/null
+++ b/qa/spec/scenario/test/instance/smoke_spec.rb
@@ -0,0 +1,5 @@
+describe QA::Scenario::Test::Instance::Smoke do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:smoke] }
+ end
+end
diff --git a/qa/spec/scenario/test/instance_spec.rb b/qa/spec/scenario/test/instance_spec.rb
deleted file mode 100644
index 0d0b534911f..00000000000
--- a/qa/spec/scenario/test/instance_spec.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-describe QA::Scenario::Test::Instance do
- subject do
- Class.new(described_class) do
- tags :rspec
- end
- end
-
- context '#perform' do
- let(:arguments) { spy('Runtime::Scenario') }
- let(:release) { spy('Runtime::Release') }
- let(:runner) { spy('Specs::Runner') }
-
- before do
- stub_const('QA::Runtime::Release', release)
- stub_const('QA::Runtime::Scenario', arguments)
- stub_const('QA::Specs::Runner', runner)
-
- allow(runner).to receive(:perform).and_yield(runner)
- end
-
- it 'sets an address of the subject' do
- subject.perform("hello")
-
- expect(arguments).to have_received(:define)
- .with(:gitlab_address, "hello")
- end
-
- context 'no paths' do
- it 'should call runner with default arguments' do
- subject.perform("test")
-
- expect(runner).to have_received(:options=)
- .with(::File.expand_path('../../../qa/specs/features', __dir__))
- end
- end
-
- context 'specifying paths' do
- it 'should call runner with paths' do
- subject.perform('test', 'path1', 'path2')
-
- expect(runner).to have_received(:options=).with(%w[path1 path2])
- end
- end
- end
-end
diff --git a/qa/spec/scenario/test/integration/github_spec.rb b/qa/spec/scenario/test/integration/github_spec.rb
new file mode 100644
index 00000000000..c2aeb1ded1d
--- /dev/null
+++ b/qa/spec/scenario/test/integration/github_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+describe QA::Scenario::Test::Integration::Github do
+ context '#perform' do
+ let(:env) { spy('Runtime::Env') }
+
+ before do
+ stub_const('QA::Runtime::Env', env)
+ end
+
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:github] }
+
+ it 'requires a GitHub access token' do
+ subject.perform('gitlab_address')
+
+ expect(env).to have_received(:require_github_access_token!)
+ end
+ end
+ end
+end
diff --git a/qa/spec/scenario/test/integration/instance_saml_spec.rb b/qa/spec/scenario/test/integration/instance_saml_spec.rb
new file mode 100644
index 00000000000..cb8a6a630cc
--- /dev/null
+++ b/qa/spec/scenario/test/integration/instance_saml_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+describe QA::Scenario::Test::Integration::InstanceSAML do
+ context '#perform' do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:instance_saml] }
+ end
+ end
+end
diff --git a/qa/spec/scenario/test/integration/kubernetes_spec.rb b/qa/spec/scenario/test/integration/kubernetes_spec.rb
new file mode 100644
index 00000000000..cb43994b229
--- /dev/null
+++ b/qa/spec/scenario/test/integration/kubernetes_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+describe QA::Scenario::Test::Integration::Kubernetes do
+ context '#perform' do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:kubernetes] }
+ end
+ end
+end
diff --git a/qa/spec/scenario/test/integration/ldap_spec.rb b/qa/spec/scenario/test/integration/ldap_spec.rb
new file mode 100644
index 00000000000..b6d798bf504
--- /dev/null
+++ b/qa/spec/scenario/test/integration/ldap_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+describe QA::Scenario::Test::Integration::LDAPNoTLS do
+ context '#perform' do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:ldap_no_tls] }
+ end
+ end
+end
+
+describe QA::Scenario::Test::Integration::LDAPTLS do
+ context '#perform' do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:ldap_tls] }
+ end
+ end
+end
diff --git a/qa/spec/scenario/test/integration/mattermost_spec.rb b/qa/spec/scenario/test/integration/mattermost_spec.rb
new file mode 100644
index 00000000000..59caf2ba2cd
--- /dev/null
+++ b/qa/spec/scenario/test/integration/mattermost_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+describe QA::Scenario::Test::Integration::Mattermost do
+ context '#perform' do
+ it_behaves_like 'a QA scenario class' do
+ let(:args) { %w[gitlab_address mattermost_address] }
+ let(:tags) { [:mattermost] }
+ let(:options) { ['path1']}
+
+ it 'requires a GitHub access token' do
+ subject.perform(*args)
+
+ expect(attributes).to have_received(:define)
+ .with(:mattermost_address, 'mattermost_address')
+ end
+ end
+ end
+end
diff --git a/qa/spec/scenario/test/integration/object_storage_spec.rb b/qa/spec/scenario/test/integration/object_storage_spec.rb
new file mode 100644
index 00000000000..2b7188223e0
--- /dev/null
+++ b/qa/spec/scenario/test/integration/object_storage_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+describe QA::Scenario::Test::Integration::ObjectStorage do
+ context '#perform' do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:object_storage] }
+ end
+ end
+end
diff --git a/qa/spec/scenario/test/sanity/framework_spec.rb b/qa/spec/scenario/test/sanity/framework_spec.rb
new file mode 100644
index 00000000000..44ac780556e
--- /dev/null
+++ b/qa/spec/scenario/test/sanity/framework_spec.rb
@@ -0,0 +1,5 @@
+describe QA::Scenario::Test::Sanity::Framework do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:framework] }
+ end
+end
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index 8e6613cd688..8e01da01340 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -3,6 +3,10 @@ require_relative '../qa'
Dir[::File.join(__dir__, 'support', '**', '*.rb')].each { |f| require f }
RSpec.configure do |config|
+ config.before do |example|
+ QA::Runtime::Logger.debug("Starting test: #{example.full_description}") if QA::Runtime::Env.debug?
+ end
+
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
diff --git a/qa/spec/specs/runner_spec.rb b/qa/spec/specs/runner_spec.rb
new file mode 100644
index 00000000000..741821ddf8c
--- /dev/null
+++ b/qa/spec/specs/runner_spec.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+describe QA::Specs::Runner do
+ context '#perform' do
+ before do
+ allow(QA::Runtime::Browser).to receive(:configure!)
+ end
+
+ it 'excludes the orchestrated tag by default' do
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', *described_class::DEFAULT_TEST_PATH_ARGS])
+
+ subject.perform
+ end
+
+ context 'when tty is set' do
+ subject { described_class.new.tap { |runner| runner.tty = true } }
+
+ it 'sets the `--tty` flag' do
+ expect_rspec_runner_arguments(['--tty', '--tag', '~orchestrated', *described_class::DEFAULT_TEST_PATH_ARGS])
+
+ subject.perform
+ end
+ end
+
+ context 'when tags are set' do
+ subject { described_class.new.tap { |runner| runner.tags = %i[orchestrated github] } }
+
+ it 'focuses on the given tags' do
+ expect_rspec_runner_arguments(['--tag', 'orchestrated', '--tag', 'github', *described_class::DEFAULT_TEST_PATH_ARGS])
+
+ subject.perform
+ end
+ end
+
+ context 'when "--tag smoke" is set as options' do
+ subject { described_class.new.tap { |runner| runner.options = %w[--tag smoke] } }
+
+ it 'focuses on the given tag without excluded the orchestrated tag' do
+ expect_rspec_runner_arguments(['--tag', 'smoke', *described_class::DEFAULT_TEST_PATH_ARGS])
+
+ subject.perform
+ end
+ end
+
+ context 'when "qa/specs/features/foo" is set as options' do
+ subject { described_class.new.tap { |runner| runner.options = %w[qa/specs/features/foo] } }
+
+ it 'passes the given tests path and excludes the orchestrated tag' do
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', 'qa/specs/features/foo'])
+
+ subject.perform
+ end
+ end
+
+ context 'when "-- qa/specs/features/foo" is set as options' do
+ subject { described_class.new.tap { |runner| runner.options = %w[-- qa/specs/features/foo] } }
+
+ it 'passes the given tests path and excludes the orchestrated tag' do
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--', 'qa/specs/features/foo'])
+
+ subject.perform
+ end
+ end
+
+ context 'when SIGNUP_DISABLED is true' do
+ before do
+ allow(QA::Runtime::Env).to receive(:signup_disabled?).and_return(true)
+ end
+
+ subject { described_class.new }
+
+ it 'it includes default args and excludes the skip_signup_disabled tag' do
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~skip_signup_disabled', *described_class::DEFAULT_TEST_PATH_ARGS])
+
+ subject.perform
+ end
+ end
+
+ context 'when git protocol v2 is not supported' do
+ before do
+ allow(QA::Runtime::Env).to receive(:can_test?).with(:git_protocol_v2).and_return(false)
+ end
+
+ subject { described_class.new }
+
+ it 'it includes default args and excludes the requires_git_protocol_v2 tag' do
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~requires_git_protocol_v2', *described_class::DEFAULT_TEST_PATH_ARGS])
+
+ subject.perform
+ end
+ end
+
+ def expect_rspec_runner_arguments(arguments)
+ expect(RSpec::Core::Runner).to receive(:run)
+ .with(arguments, $stderr, $stdout)
+ .and_return(0)
+ end
+ end
+end
diff --git a/qa/spec/support/shared_examples/scenario_shared_examples.rb b/qa/spec/support/shared_examples/scenario_shared_examples.rb
new file mode 100644
index 00000000000..5fd55d7d96b
--- /dev/null
+++ b/qa/spec/support/shared_examples/scenario_shared_examples.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+shared_examples 'a QA scenario class' do
+ let(:attributes) { spy('Runtime::Scenario') }
+ let(:release) { spy('Runtime::Release') }
+ let(:runner) { spy('Specs::Runner') }
+
+ let(:args) { ['gitlab_address'] }
+ let(:tags) { [] }
+ let(:options) { %w[path1 path2] }
+
+ before do
+ stub_const('QA::Runtime::Release', release)
+ stub_const('QA::Runtime::Scenario', attributes)
+ stub_const('QA::Specs::Runner', runner)
+
+ allow(runner).to receive(:perform).and_yield(runner)
+ end
+
+ it 'responds to perform' do
+ expect(subject).to respond_to(:perform)
+ end
+
+ it 'sets an address of the subject' do
+ subject.perform(*args)
+
+ expect(attributes).to have_received(:define).with(:gitlab_address, 'gitlab_address')
+ end
+
+ it 'performs before hooks' do
+ subject.perform(*args)
+
+ expect(release).to have_received(:perform_before_hooks)
+ end
+
+ it 'sets tags on runner' do
+ subject.perform(*args)
+
+ expect(runner).to have_received(:tags=).with(tags)
+ end
+
+ context 'specifying RSpec options' do
+ it 'sets options on runner' do
+ subject.perform(*args, *options)
+
+ expect(runner).to have_received(:options=).with(options)
+ end
+ end
+end
diff --git a/rubocop/code_reuse_helpers.rb b/rubocop/code_reuse_helpers.rb
new file mode 100644
index 00000000000..0929a55d901
--- /dev/null
+++ b/rubocop/code_reuse_helpers.rb
@@ -0,0 +1,156 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module CodeReuseHelpers
+ # Returns true for a `(send const ...)` node.
+ def send_to_constant?(node)
+ node.type == :send && node.children&.first&.type == :const
+ end
+
+ # Returns `true` if the name of the receiving constant ends with a given
+ # `String`.
+ def send_receiver_name_ends_with?(node, suffix)
+ return false unless send_to_constant?(node)
+
+ receiver_name = name_of_receiver(node)
+
+ receiver_name != suffix &&
+ receiver_name.end_with?(suffix)
+ end
+
+ # Returns the file path (as a `String`) for an AST node.
+ def file_path_for_node(node)
+ node.location.expression.source_buffer.name
+ end
+
+ # Returns the name of a constant node.
+ #
+ # Given the AST node `(const nil :Foo)`, this method will return `:Foo`.
+ def name_of_constant(node)
+ node.children[1]
+ end
+
+ # Returns true if the given node resides in app/finders or ee/app/finders.
+ def in_finder?(node)
+ in_directory?(node, 'finders')
+ end
+
+ # Returns true if the given node resides in app/models or ee/app/models.
+ def in_model?(node)
+ in_directory?(node, 'models')
+ end
+
+ # Returns true if the given node resides in app/services or ee/app/services.
+ def in_service_class?(node)
+ in_directory?(node, 'services')
+ end
+
+ # Returns true if the given node resides in app/presenters or
+ # ee/app/presenters.
+ def in_presenter?(node)
+ in_directory?(node, 'presenters')
+ end
+
+ # Returns true if the given node resides in app/serializers or
+ # ee/app/serializers.
+ def in_serializer?(node)
+ in_directory?(node, 'serializers')
+ end
+
+ # Returns true if the given node resides in app/workers or ee/app/workers.
+ def in_worker?(node)
+ in_directory?(node, 'workers')
+ end
+
+ # Returns true if the given node resides in app/controllers or
+ # ee/app/controllers.
+ def in_controller?(node)
+ in_directory?(node, 'controllers')
+ end
+
+ # Returns true if the given node resides in lib/api or ee/lib/api.
+ def in_api?(node)
+ file_path_for_node(node).start_with?(
+ File.join(ce_lib_directory, 'api'),
+ File.join(ee_lib_directory, 'api')
+ )
+ end
+
+ # Returns `true` if the given AST node resides in the given directory,
+ # relative to app and/or ee/app.
+ def in_directory?(node, directory)
+ file_path_for_node(node).start_with?(
+ File.join(ce_app_directory, directory),
+ File.join(ee_app_directory, directory)
+ )
+ end
+
+ # Returns the receiver name of a send node.
+ #
+ # For the AST node `(send (const nil :Foo) ...)` this would return
+ # `'Foo'`.
+ def name_of_receiver(node)
+ name_of_constant(node.children.first).to_s
+ end
+
+ # Yields every defined class method in the given AST node.
+ def each_class_method(node)
+ return to_enum(__method__, node) unless block_given?
+
+ # class << self
+ # def foo
+ # end
+ # end
+ node.each_descendant(:sclass) do |sclass|
+ sclass.each_descendant(:def) do |def_node|
+ yield def_node
+ end
+ end
+
+ # def self.foo
+ # end
+ node.each_descendant(:defs) do |defs_node|
+ yield defs_node
+ end
+ end
+
+ # Yields every send node found in the given AST node.
+ def each_send_node(node, &block)
+ node.each_descendant(:send, &block)
+ end
+
+ # Registers a RuboCop offense for a `(send)` node with a receiver that ends
+ # with a given suffix.
+ #
+ # node - The AST node to check.
+ # suffix - The suffix of the receiver name, such as "Finder".
+ # message - The message to use for the offense.
+ def disallow_send_to(node, suffix, message)
+ each_send_node(node) do |send_node|
+ next unless send_receiver_name_ends_with?(send_node, suffix)
+
+ add_offense(send_node, location: :expression, message: message)
+ end
+ end
+
+ def ce_app_directory
+ File.join(rails_root, 'app')
+ end
+
+ def ee_app_directory
+ File.join(rails_root, 'ee', 'app')
+ end
+
+ def ce_lib_directory
+ File.join(rails_root, 'lib')
+ end
+
+ def ee_lib_directory
+ File.join(rails_root, 'ee', 'lib')
+ end
+
+ def rails_root
+ File.expand_path('..', __dir__)
+ end
+ end
+end
diff --git a/rubocop/cop/avoid_route_redirect_leading_slash.rb b/rubocop/cop/avoid_route_redirect_leading_slash.rb
new file mode 100644
index 00000000000..7ac1c881269
--- /dev/null
+++ b/rubocop/cop/avoid_route_redirect_leading_slash.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ # Checks for a leading '/' in route redirects
+ # For more information see: https://gitlab.com/gitlab-org/gitlab-ce/issues/50645
+ #
+ # @example
+ # # bad
+ # root to: redirect('/-/instance/statistics/conversational_development_index')
+ #
+ # # good
+ # root to: redirect('-/instance/statistics/conversational_development_index')
+ #
+
+ class AvoidRouteRedirectLeadingSlash < RuboCop::Cop::Cop
+ MSG = 'Do not use a leading "/" in route redirects'
+
+ def_node_matcher :leading_slash_in_redirect?, <<~PATTERN
+ (send nil? :redirect (str #has_leading_slash?))
+ PATTERN
+
+ def on_send(node)
+ return unless in_routes?(node)
+ return unless leading_slash_in_redirect?(node)
+
+ add_offense(node)
+ end
+
+ def has_leading_slash?(str)
+ str.start_with?("/")
+ end
+
+ def in_routes?(node)
+ path = node.location.expression.source_buffer.name
+ dirname = File.dirname(path)
+ filename = File.basename(path)
+ dirname.end_with?('config/routes') || filename.end_with?('routes.rb')
+ end
+
+ def autocorrect(node)
+ lambda do |corrector|
+ corrector.replace(node.loc.expression, remove_leading_slash(node))
+ end
+ end
+
+ def remove_leading_slash(node)
+ node.source.sub('/', '')
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/code_reuse/active_record.rb b/rubocop/cop/code_reuse/active_record.rb
new file mode 100644
index 00000000000..2be8f7c11aa
--- /dev/null
+++ b/rubocop/cop/code_reuse/active_record.rb
@@ -0,0 +1,169 @@
+# frozen_string_literal: true
+
+require_relative '../../code_reuse_helpers'
+
+module RuboCop
+ module Cop
+ module CodeReuse
+ # Cop that blacklists the use of ActiveRecord methods outside of models.
+ class ActiveRecord < RuboCop::Cop::Cop
+ include CodeReuseHelpers
+
+ MSG = 'This method can only be used inside an ActiveRecord model'
+
+ # Various methods from ActiveRecord::Querying that are blacklisted. We
+ # exclude some generic ones such as `any?` and `first`, as these may
+ # lead to too many false positives, since `Array` also supports these
+ # methods.
+ #
+ # The keys of this Hash are the blacklisted method names. The values are
+ # booleans that indicate if the method should only be blacklisted if any
+ # arguments are provided.
+ NOT_ALLOWED = {
+ average: true,
+ calculate: true,
+ count_by_sql: true,
+ create_with: true,
+ distinct: false,
+ eager_load: true,
+ except: true,
+ exists?: true,
+ find_by: true,
+ find_by!: true,
+ find_by_sql: true,
+ find_each: true,
+ find_in_batches: true,
+ find_or_create_by: true,
+ find_or_create_by!: true,
+ find_or_initialize_by: true,
+ first!: false,
+ first_or_create: true,
+ first_or_create!: true,
+ first_or_initialize: true,
+ from: true,
+ group: true,
+ having: true,
+ ids: false,
+ includes: true,
+ joins: true,
+ limit: true,
+ lock: false,
+ many?: false,
+ offset: true,
+ order: true,
+ pluck: true,
+ preload: true,
+ readonly: false,
+ references: true,
+ reorder: true,
+ rewhere: true,
+ sum: false,
+ take: false,
+ take!: false,
+ unscope: false,
+ where: false,
+ with: true
+ }.freeze
+
+ # Directories that allow the use of the blacklisted methods. These
+ # directories are checked relative to both . and ee/
+ WHITELISTED_DIRECTORIES = %w[
+ app/models
+ config
+ danger
+ db
+ lib/backup
+ lib/banzai
+ lib/gitlab/background_migration
+ lib/gitlab/cycle_analytics
+ lib/gitlab/database
+ lib/gitlab/import_export
+ lib/gitlab/project_authorizations
+ lib/gitlab/sql
+ lib/system_check
+ lib/tasks
+ qa
+ rubocop
+ spec
+ ].freeze
+
+ def on_send(node)
+ return if in_whitelisted_directory?(node)
+
+ receiver = node.children[0]
+ send_name = node.children[1]
+ first_arg = node.children[2]
+
+ if receiver && NOT_ALLOWED.key?(send_name)
+ # If the rule requires an argument to be given, but none are
+ # provided, we won't register an offense. This prevents us from
+ # adding offenses for `project.group`, while still covering
+ # `Project.group(:name)`.
+ return if NOT_ALLOWED[send_name] && !first_arg
+
+ add_offense(node, location: :selector)
+ end
+ end
+
+ # Returns true if the node resides in one of the whitelisted
+ # directories.
+ def in_whitelisted_directory?(node)
+ path = file_path_for_node(node)
+
+ WHITELISTED_DIRECTORIES.any? do |directory|
+ path.start_with?(
+ File.join(rails_root, directory),
+ File.join(rails_root, 'ee', directory)
+ )
+ end
+ end
+
+ # We can not auto correct code like this, as it requires manual
+ # refactoring. Instead, we'll just whitelist the surrounding scope.
+ #
+ # Despite this method's presence, you should not use it. This method
+ # exists to make it possible to whitelist large chunks of offenses we
+ # can't fix in the short term. If you are writing new code, follow the
+ # code reuse guidelines, instead of whitelisting any new offenses.
+ def autocorrect(node)
+ scope = surrounding_scope_of(node)
+ indent = indentation_of(scope)
+
+ lambda do |corrector|
+ # This prevents us from inserting the same enable/disable comment
+ # for a method or block that has multiple offenses.
+ next if whitelisted_scopes.include?(scope)
+
+ corrector.insert_before(
+ scope.source_range,
+ "# rubocop: disable #{cop_name}\n#{indent}"
+ )
+
+ corrector.insert_after(
+ scope.source_range,
+ "\n#{indent}# rubocop: enable #{cop_name}"
+ )
+
+ whitelisted_scopes << scope
+ end
+ end
+
+ def indentation_of(node)
+ ' ' * node.loc.expression.source_line[/\A */].length
+ end
+
+ def surrounding_scope_of(node)
+ %i[def defs block begin].each do |type|
+ if (found = node.each_ancestor(type).first)
+ return found
+ end
+ end
+ end
+
+ def whitelisted_scopes
+ @whitelisted_scopes ||= Set.new
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/code_reuse/finder.rb b/rubocop/cop/code_reuse/finder.rb
new file mode 100644
index 00000000000..1d70befe79b
--- /dev/null
+++ b/rubocop/cop/code_reuse/finder.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require_relative '../../code_reuse_helpers'
+
+module RuboCop
+ module Cop
+ module CodeReuse
+ # Cop that enforces various code reuse rules for Finders.
+ class Finder < RuboCop::Cop::Cop
+ include CodeReuseHelpers
+
+ IN_FINDER = 'Finders can not be used inside a Finder.'
+
+ IN_MODEL_CLASS_METHOD =
+ 'Finders can not be used inside model class methods.'
+
+ SUFFIX = 'Finder'
+
+ def on_class(node)
+ if in_finder?(node)
+ check_finder(node)
+ elsif in_model?(node)
+ check_model_class_methods(node)
+ end
+ end
+
+ def check_finder(node)
+ disallow_send_to(node, SUFFIX, IN_FINDER)
+ end
+
+ def check_model_class_methods(node)
+ each_class_method(node) do |def_node|
+ disallow_send_to(def_node, SUFFIX, IN_MODEL_CLASS_METHOD)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/code_reuse/presenter.rb b/rubocop/cop/code_reuse/presenter.rb
new file mode 100644
index 00000000000..5f8f2839ca6
--- /dev/null
+++ b/rubocop/cop/code_reuse/presenter.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require_relative '../../code_reuse_helpers.rb'
+
+module RuboCop
+ module Cop
+ module CodeReuse
+ # Cop that enforces various code reuse rules for Presenter classes.
+ class Presenter < RuboCop::Cop::Cop
+ include CodeReuseHelpers
+
+ IN_SERVICE = 'Presenters can not be used in a Service class.'
+ IN_FINDER = 'Presenters can not be used in a Finder.'
+ IN_PRESENTER = 'Presenters can not be used in a Presenter.'
+ IN_SERIALIZER = 'Presenters can not be used in a Serializer.'
+ IN_MODEL = 'Presenters can not be used in a model.'
+ IN_WORKER = 'Presenters can not be used in a worker.'
+ SUFFIX = 'Presenter'
+
+ def on_class(node)
+ message =
+ if in_service_class?(node)
+ IN_SERVICE
+ elsif in_finder?(node)
+ IN_FINDER
+ elsif in_presenter?(node)
+ IN_PRESENTER
+ elsif in_serializer?(node)
+ IN_SERIALIZER
+ elsif in_model?(node)
+ IN_MODEL
+ elsif in_worker?(node)
+ IN_WORKER
+ end
+
+ disallow_send_to(node, SUFFIX, message) if message
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/code_reuse/serializer.rb b/rubocop/cop/code_reuse/serializer.rb
new file mode 100644
index 00000000000..2212c50514e
--- /dev/null
+++ b/rubocop/cop/code_reuse/serializer.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require_relative '../../code_reuse_helpers.rb'
+
+module RuboCop
+ module Cop
+ module CodeReuse
+ # Cop that enforces various code reuse rules for Serializer classes.
+ class Serializer < RuboCop::Cop::Cop
+ include CodeReuseHelpers
+
+ IN_SERVICE = 'Serializers can not be used in a Service class.'
+ IN_FINDER = 'Serializers can not be used in a Finder.'
+ IN_PRESENTER = 'Serializers can not be used in a Presenter.'
+ IN_SERIALIZER = 'Serializers can not be used in a Serializer.'
+ IN_MODEL = 'Serializers can not be used in a model.'
+ IN_WORKER = 'Serializers can not be used in a worker.'
+ SUFFIX = 'Serializer'
+
+ def on_class(node)
+ message =
+ if in_service_class?(node)
+ IN_SERVICE
+ elsif in_finder?(node)
+ IN_FINDER
+ elsif in_presenter?(node)
+ IN_PRESENTER
+ elsif in_serializer?(node)
+ IN_SERIALIZER
+ elsif in_model?(node)
+ IN_MODEL
+ elsif in_worker?(node)
+ IN_WORKER
+ end
+
+ disallow_send_to(node, SUFFIX, message) if message
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/code_reuse/service_class.rb b/rubocop/cop/code_reuse/service_class.rb
new file mode 100644
index 00000000000..768b43fb684
--- /dev/null
+++ b/rubocop/cop/code_reuse/service_class.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require_relative '../../code_reuse_helpers.rb'
+
+module RuboCop
+ module Cop
+ module CodeReuse
+ # Cop that enforces various code reuse rules for Service classes.
+ class ServiceClass < RuboCop::Cop::Cop
+ include CodeReuseHelpers
+
+ IN_FINDER = 'Service classes can not be used in a Finder.'
+ IN_PRESENTER = 'Service classes can not be used in a Presenter.'
+ IN_SERIALIZER = 'Service classes can not be used in a Serializer.'
+ IN_MODEL = 'Service classes can not be used in a model.'
+ SUFFIX = 'Service'
+
+ def on_class(node)
+ check_all_send_nodes(node)
+ end
+
+ def check_all_send_nodes(node)
+ message =
+ if in_finder?(node)
+ IN_FINDER
+ elsif in_presenter?(node)
+ IN_PRESENTER
+ elsif in_serializer?(node)
+ IN_SERIALIZER
+ elsif in_model?(node)
+ IN_MODEL
+ end
+
+ disallow_send_to(node, SUFFIX, message) if message
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/code_reuse/worker.rb b/rubocop/cop/code_reuse/worker.rb
new file mode 100644
index 00000000000..e38d2783d0f
--- /dev/null
+++ b/rubocop/cop/code_reuse/worker.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require_relative '../../code_reuse_helpers.rb'
+
+module RuboCop
+ module Cop
+ module CodeReuse
+ # Cop that enforces various code reuse rules for workers.
+ class Worker < RuboCop::Cop::Cop
+ include CodeReuseHelpers
+
+ IN_CONTROLLER = 'Workers can not be used in a controller.'
+ IN_API = 'Workers can not be used in a Grape API.'
+ IN_FINDER = 'Workers can not be used in a Finder.'
+ IN_PRESENTER = 'Workers can not be used in a Presenter.'
+ IN_SERIALIZER = 'Workers can not be used in a Serializer.'
+
+ IN_MODEL_CLASS_METHOD =
+ 'Workers can not be used in model class methods.'
+
+ SUFFIX = 'Worker'
+
+ def on_class(node)
+ if in_model?(node)
+ check_model_class_methods(node)
+ else
+ check_all_send_nodes(node)
+ end
+ end
+
+ def check_all_send_nodes(node)
+ message =
+ if in_controller?(node)
+ IN_CONTROLLER
+ elsif in_api?(node)
+ IN_API
+ elsif in_finder?(node)
+ IN_FINDER
+ elsif in_presenter?(node)
+ IN_PRESENTER
+ elsif in_serializer?(node)
+ IN_SERIALIZER
+ end
+
+ disallow_send_to(node, SUFFIX, message) if message
+ end
+
+ def check_model_class_methods(node)
+ each_class_method(node) do |def_node|
+ disallow_send_to(def_node, SUFFIX, IN_MODEL_CLASS_METHOD)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/destroy_all.rb b/rubocop/cop/destroy_all.rb
new file mode 100644
index 00000000000..38b6cb40f91
--- /dev/null
+++ b/rubocop/cop/destroy_all.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ # Cop that blacklists the use of `destroy_all`.
+ class DestroyAll < RuboCop::Cop::Cop
+ MSG = 'Use `delete_all` instead of `destroy_all`. ' \
+ '`destroy_all` will load the rows into memory, then execute a ' \
+ '`DELETE` for every individual row.'
+
+ def_node_matcher :destroy_all?, <<~PATTERN
+ (send {send ivar lvar const} :destroy_all ...)
+ PATTERN
+
+ def on_send(node)
+ return unless destroy_all?(node)
+
+ add_offense(node, location: :expression)
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/gitlab/union.rb b/rubocop/cop/gitlab/union.rb
new file mode 100644
index 00000000000..09541d8af3b
--- /dev/null
+++ b/rubocop/cop/gitlab/union.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+require_relative '../../spec_helpers'
+
+module RuboCop
+ module Cop
+ module Gitlab
+ # Cop that disallows the use of `Gitlab::SQL::Union`, in favour of using
+ # the `FromUnion` module.
+ class Union < RuboCop::Cop::Cop
+ include SpecHelpers
+
+ MSG = 'Use the `FromUnion` concern, instead of using `Gitlab::SQL::Union` directly'
+
+ def_node_matcher :raw_union?, <<~PATTERN
+ (send (const (const (const nil? :Gitlab) :SQL) :Union) :new ...)
+ PATTERN
+
+ def on_send(node)
+ return unless raw_union?(node)
+ return if in_spec?(node)
+
+ add_offense(node, location: :expression)
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/group_public_or_visible_to_user.rb b/rubocop/cop/group_public_or_visible_to_user.rb
new file mode 100644
index 00000000000..beda0b7f8ba
--- /dev/null
+++ b/rubocop/cop/group_public_or_visible_to_user.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+#
+module RuboCop
+ module Cop
+ # Cop that blacklists the usage of Group.public_or_visible_to_user
+ class GroupPublicOrVisibleToUser < RuboCop::Cop::Cop
+ MSG = '`Group.public_or_visible_to_user` should be used with extreme care. ' \
+ 'Please ensure that you are not using it on its own and that the amount ' \
+ 'of rows being filtered is reasonable.'
+
+ def_node_matcher :public_or_visible_to_user?, <<~PATTERN
+ (send (const nil? :Group) :public_or_visible_to_user ...)
+ PATTERN
+
+ def on_send(node)
+ return unless public_or_visible_to_user?(node)
+
+ add_offense(node, location: :expression)
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/line_break_around_conditional_block.rb b/rubocop/cop/line_break_around_conditional_block.rb
index 011f2bcf8bf..8118b314b63 100644
--- a/rubocop/cop/line_break_around_conditional_block.rb
+++ b/rubocop/cop/line_break_around_conditional_block.rb
@@ -48,6 +48,8 @@ module RuboCop
MSG = 'Add a line break around conditional blocks'
def on_if(node)
+ # This cop causes errors in haml files, so let's skip those
+ return if in_haml?(node)
return if node.single_line?
return unless node.if? || node.unless?
@@ -75,7 +77,8 @@ module RuboCop
start_clause_line?(previous_line(node)) ||
block_start?(previous_line(node)) ||
begin_line?(previous_line(node)) ||
- assignment_line?(previous_line(node))
+ assignment_line?(previous_line(node)) ||
+ rescue_line?(previous_line(node))
end
def last_line_valid?(node)
@@ -109,6 +112,10 @@ module RuboCop
line =~ /^\s*.*=/
end
+ def rescue_line?(line)
+ line =~ /^\s*rescue/
+ end
+
def block_start?(line)
line.match(/ (do|{)( \|.*?\|)?\s?$/)
end
@@ -116,6 +123,10 @@ module RuboCop
def end_line?(line)
line =~ /^\s*(end|})/
end
+
+ def in_haml?(node)
+ node.location.expression.source_buffer.name.end_with?('.haml.rb')
+ end
end
end
end
diff --git a/rubocop/cop/prefer_class_methods_over_module.rb b/rubocop/cop/prefer_class_methods_over_module.rb
new file mode 100644
index 00000000000..0dfa80ccfab
--- /dev/null
+++ b/rubocop/cop/prefer_class_methods_over_module.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ # Enforces the use of 'class_methods' instead of 'module ClassMethods' for activesupport concerns.
+ # For more information see: https://gitlab.com/gitlab-org/gitlab-ce/issues/50414
+ #
+ # @example
+ # # bad
+ # module Foo
+ # extend ActiveSupport::Concern
+ #
+ # module ClassMethods
+ # def a_class_method
+ # end
+ # end
+ # end
+ #
+ # # good
+ # module Foo
+ # extend ActiveSupport::Concern
+ #
+ # class_methods do
+ # def a_class_method
+ # end
+ # end
+ # end
+ #
+ class PreferClassMethodsOverModule < RuboCop::Cop::Cop
+ include RangeHelp
+
+ MSG = 'Do not use module ClassMethods, use class_methods block instead.'
+
+ def_node_matcher :extend_activesupport_concern?, <<~PATTERN
+ (:send nil? :extend (:const (:const nil? :ActiveSupport) :Concern))
+ PATTERN
+
+ def on_module(node)
+ add_offense(node) if node.defined_module_name == 'ClassMethods' && module_extends_activesupport_concern?(node)
+ end
+
+ def autocorrect(node)
+ lambda do |corrector|
+ corrector.replace(module_range(node), 'class_methods do')
+ end
+ end
+
+ private
+
+ def module_extends_activesupport_concern?(node)
+ container_module = container_module_of(node)
+ return false unless container_module
+
+ container_module.descendants.any? do |descendant|
+ extend_activesupport_concern?(descendant)
+ end
+ end
+
+ def container_module_of(node)
+ while node = node.parent
+ break if node.type == :module
+ end
+
+ node
+ end
+
+ def module_range(node)
+ module_node, _ = *node
+ range_between(node.loc.keyword.begin_pos, module_node.source_range.end_pos)
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/qa/element_with_pattern.rb b/rubocop/cop/qa/element_with_pattern.rb
new file mode 100644
index 00000000000..9d80946f1ba
--- /dev/null
+++ b/rubocop/cop/qa/element_with_pattern.rb
@@ -0,0 +1,39 @@
+require_relative '../../qa_helpers'
+
+module RuboCop
+ module Cop
+ module QA
+ # This cop checks for the usage of factories in migration specs
+ #
+ # @example
+ #
+ # # bad
+ # let(:user) { create(:user) }
+ #
+ # # good
+ # let(:users) { table(:users) }
+ # let(:user) { users.create!(name: 'User 1', username: 'user1') }
+ class ElementWithPattern < RuboCop::Cop::Cop
+ include QAHelpers
+
+ MESSAGE = "Don't use a pattern for element, create a corresponding `%s` instead.".freeze
+
+ def on_send(node)
+ return unless in_qa_file?(node)
+ return unless method_name(node).to_s == 'element'
+
+ element_name, pattern = node.arguments
+ return unless pattern
+
+ add_offense(node, location: pattern.source_range, message: MESSAGE % "qa-#{element_name.value.to_s.tr('_', '-')}")
+ end
+
+ private
+
+ def method_name(node)
+ node.children[1]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/ruby_interpolation_in_translation.rb b/rubocop/cop/ruby_interpolation_in_translation.rb
new file mode 100644
index 00000000000..c431b4a1977
--- /dev/null
+++ b/rubocop/cop/ruby_interpolation_in_translation.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ class RubyInterpolationInTranslation < RuboCop::Cop::Cop
+ MSG = "Don't use ruby interpolation \#{} inside translated strings, instead use \%{}"
+
+ TRANSLATION_METHODS = ':_ :s_ :N_ :n_'
+
+ def_node_matcher :translation_method?, <<~PATTERN
+ (send nil? {#{TRANSLATION_METHODS}} $dstr ...)
+ PATTERN
+
+ def_node_matcher :plural_translation_method?, <<~PATTERN
+ (send nil? :n_ str $dstr ...)
+ PATTERN
+
+ def on_send(node)
+ interpolation = translation_method?(node) || plural_translation_method?(node)
+ return unless interpolation
+
+ interpolation.descendants.each do |possible_violation|
+ add_offense(possible_violation, message: MSG) if possible_violation.type != :str
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/qa_helpers.rb b/rubocop/qa_helpers.rb
new file mode 100644
index 00000000000..f4adf7f4e9f
--- /dev/null
+++ b/rubocop/qa_helpers.rb
@@ -0,0 +1,11 @@
+module RuboCop
+ # Module containing helper methods for writing QA cops.
+ module QAHelpers
+ # Returns true if the given node originated from the qa/ directory.
+ def in_qa_file?(node)
+ path = node.location.expression.source_buffer.name
+
+ path.start_with?(File.join(Dir.pwd, 'qa'))
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index a427208cdab..6c9b8753c1a 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -3,10 +3,13 @@ require_relative 'cop/gitlab/module_with_instance_variables'
require_relative 'cop/gitlab/predicate_memoization'
require_relative 'cop/gitlab/httparty'
require_relative 'cop/gitlab/finder_with_find_by'
+require_relative 'cop/gitlab/union'
require_relative 'cop/include_sidekiq_worker'
require_relative 'cop/avoid_return_from_blocks'
require_relative 'cop/avoid_break_from_strong_memoize'
+require_relative 'cop/avoid_route_redirect_leading_slash'
require_relative 'cop/line_break_around_conditional_block'
+require_relative 'cop/prefer_class_methods_over_module'
require_relative 'cop/migration/add_column'
require_relative 'cop/migration/add_concurrent_foreign_key'
require_relative 'cop/migration/add_concurrent_index'
@@ -26,4 +29,14 @@ require_relative 'cop/migration/update_large_table'
require_relative 'cop/project_path_helper'
require_relative 'cop/rspec/env_assignment'
require_relative 'cop/rspec/factories_in_migration_specs'
+require_relative 'cop/qa/element_with_pattern'
require_relative 'cop/sidekiq_options_queue'
+require_relative 'cop/destroy_all'
+require_relative 'cop/ruby_interpolation_in_translation'
+require_relative 'code_reuse_helpers'
+require_relative 'cop/code_reuse/finder'
+require_relative 'cop/code_reuse/service_class'
+require_relative 'cop/code_reuse/presenter'
+require_relative 'cop/code_reuse/serializer'
+require_relative 'cop/code_reuse/active_record'
+require_relative 'cop/group_public_or_visible_to_user'
diff --git a/scripts/build_assets_image b/scripts/build_assets_image
new file mode 100755
index 00000000000..4e5ef977161
--- /dev/null
+++ b/scripts/build_assets_image
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# Exit early if we don't want to build the image
+if [[ "${BUILD_ASSETS_IMAGE}" != "true" ]]
+then
+ exit 0
+fi
+
+# Generate the image name based on the project this is being run in
+ASSETS_IMAGE_NAME=$(echo ${CI_PROJECT_NAME} |
+ awk '{
+ split($1, p, "-");
+ interim = sprintf("%s-assets-%s", p[1], p[2]);
+ sub(/-$/, "", interim);
+ print interim
+ }'
+)
+
+ASSETS_IMAGE_PATH=${CI_REGISTRY}/${CI_PROJECT_PATH}/${ASSETS_IMAGE_NAME}
+
+mkdir -p assets_container.build/public
+cp -r public/assets assets_container.build/public/
+cp Dockerfile.assets assets_container.build/
+docker build -t ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG} -f assets_container.build/Dockerfile.assets assets_container.build/
+docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
+docker push ${ASSETS_IMAGE_PATH}
+
diff --git a/scripts/frontend/extract_gettext_all.js b/scripts/frontend/extract_gettext_all.js
new file mode 100644
index 00000000000..725522a3540
--- /dev/null
+++ b/scripts/frontend/extract_gettext_all.js
@@ -0,0 +1,72 @@
+const argumentsParser = require('commander');
+
+const { GettextExtractor, JsExtractors } = require('gettext-extractor');
+const {
+ decorateJSParserWithVueSupport,
+ decorateExtractorWithHelpers,
+} = require('gettext-extractor-vue');
+const ensureSingleLine = require('../../app/assets/javascripts/locale/ensure_single_line.js');
+
+const args = argumentsParser
+ .option('-f, --file <file>', 'Extract message from one single file')
+ .option('-a, --all', 'Extract message from all js/vue files')
+ .parse(process.argv);
+
+const extractor = decorateExtractorWithHelpers(new GettextExtractor());
+
+extractor.addMessageTransformFunction(ensureSingleLine);
+
+const jsParser = extractor.createJsParser([
+ // Place all the possible expressions to extract here:
+ JsExtractors.callExpression('__', {
+ arguments: {
+ text: 0,
+ },
+ }),
+ JsExtractors.callExpression('n__', {
+ arguments: {
+ text: 0,
+ textPlural: 1,
+ },
+ }),
+ JsExtractors.callExpression('s__', {
+ arguments: {
+ text: 0,
+ },
+ }),
+]);
+
+const vueParser = decorateJSParserWithVueSupport(jsParser);
+
+function printJson() {
+ const messages = extractor.getMessages().reduce((result, message) => {
+ let text = message.text;
+ if (message.textPlural) {
+ text += `\u0000${message.textPlural}`;
+ }
+
+ message.references.forEach(reference => {
+ const filename = reference.replace(/:\d+$/, '');
+
+ if (!Array.isArray(result[filename])) {
+ result[filename] = [];
+ }
+
+ result[filename].push([text, reference]);
+ });
+
+ return result;
+ }, {});
+
+ console.log(JSON.stringify(messages));
+}
+
+if (args.file) {
+ vueParser.parseFile(args.file).then(() => printJson());
+} else if (args.all) {
+ vueParser.parseFilesGlob('{ee/app,app}/assets/javascripts/**/*.{js,vue}').then(() => printJson());
+} else {
+ console.warn('ERROR: Please use the script correctly:');
+ args.outputHelp();
+ process.exit(1);
+}
diff --git a/scripts/frontend/frontend_script_utils.js b/scripts/frontend/frontend_script_utils.js
index e42b912d359..e3d357b4a40 100644
--- a/scripts/frontend/frontend_script_utils.js
+++ b/scripts/frontend/frontend_script_utils.js
@@ -13,7 +13,8 @@ const execGitCmd = args =>
exec('git', args)
.trim()
.toString()
- .split('\n');
+ .split('\n')
+ .filter(Boolean);
module.exports = {
getStagedFiles: fileExtensionFilter => {
diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js
index b66ba885701..ce86a9f4601 100644
--- a/scripts/frontend/prettier.js
+++ b/scripts/frontend/prettier.js
@@ -1,126 +1,116 @@
const glob = require('glob');
const prettier = require('prettier');
const fs = require('fs');
-const path = require('path');
-const prettierIgnore = require('ignore')();
+const { getStagedFiles } = require('./frontend_script_utils');
-const getStagedFiles = require('./frontend_script_utils').getStagedFiles;
+const matchExtensions = ['js', 'vue'];
+
+// This will improve glob performance by excluding certain directories.
+// The .prettierignore file will also be respected, but after the glob has executed.
+const globIgnore = ['**/node_modules/**', 'vendor/**', 'public/**'];
+
+const readFileAsync = (file, options) =>
+ new Promise((resolve, reject) => {
+ fs.readFile(file, options, function(err, data) {
+ if (err) reject(err);
+ else resolve(data);
+ });
+ });
+
+const writeFileAsync = (file, data, options) =>
+ new Promise((resolve, reject) => {
+ fs.writeFile(file, data, options, function(err) {
+ if (err) reject(err);
+ else resolve();
+ });
+ });
const mode = process.argv[2] || 'check';
const shouldSave = mode === 'save' || mode === 'save-all';
const allFiles = mode === 'check-all' || mode === 'save-all';
-let dirPath = process.argv[3] || '';
-if (dirPath && dirPath.charAt(dirPath.length - 1) !== '/') dirPath += '/';
-
-const config = {
- patterns: ['**/*.js', '**/*.vue', '**/*.scss'],
- /*
- * The ignore patterns below are just to reduce search time with glob, as it includes the
- * folders with the most ignored assets, the actual `.prettierignore` will be used later on
- */
- ignore: ['**/node_modules/**', '**/vendor/**', '**/public/**'],
- parsers: {
- js: 'babylon',
- vue: 'vue',
- scss: 'css',
- },
-};
+let globDir = process.argv[3] || '';
+if (globDir && globDir.charAt(globDir.length - 1) !== '/') globDir += '/';
-/*
- * Unfortunately the prettier API does not expose support for `.prettierignore` files, they however
- * use the ignore package, so we do the same. We simply cannot use the glob package, because
- * gitignore style is not compatible with globs ignore style.
- */
-prettierIgnore.add(
- fs
- .readFileSync(path.join(__dirname, '../../', '.prettierignore'))
- .toString()
- .trim()
- .split(/\r?\n/)
+console.log(
+ `Loading all ${allFiles ? '' : 'staged '}files ${globDir ? `within ${globDir} ` : ''}...`
);
-const availableExtensions = Object.keys(config.parsers);
-
-console.log(`Loading ${allFiles ? 'All' : 'Selected'} Files ...`);
+const globPatterns = matchExtensions.map(ext => `${globDir}**/*.${ext}`);
+const matchedFiles = allFiles
+ ? glob.sync(`{${globPatterns.join(',')}}`, { ignore: globIgnore })
+ : getStagedFiles(globPatterns);
+const matchedCount = matchedFiles.length;
-const stagedFiles =
- allFiles || dirPath ? null : getStagedFiles(availableExtensions.map(ext => `*.${ext}`));
-
-if (stagedFiles) {
- if (!stagedFiles.length || (stagedFiles.length === 1 && !stagedFiles[0])) {
- console.log('No matching staged files.');
- process.exit(1);
- }
- console.log(`Matching staged Files : ${stagedFiles.length}`);
+if (!matchedCount) {
+ console.log('No files found to process with prettier');
+ process.exit(0);
}
let didWarn = false;
-let didError = false;
-
-let files;
-if (allFiles) {
- const ignore = config.ignore;
- const patterns = config.patterns;
- const globPattern = patterns.length > 1 ? `{${patterns.join(',')}}` : `${patterns.join(',')}`;
- files = glob.sync(globPattern, { ignore }).filter(f => allFiles || stagedFiles.includes(f));
-} else if (dirPath) {
- const ignore = config.ignore;
- const patterns = config.patterns.map(item => {
- return dirPath + item;
- });
- const globPattern = patterns.length > 1 ? `{${patterns.join(',')}}` : `${patterns.join(',')}`;
- files = glob.sync(globPattern, { ignore });
-} else {
- files = stagedFiles.filter(f => availableExtensions.includes(f.split('.').pop()));
-}
-
-files = prettierIgnore.filter(files);
-
-if (!files.length) {
- console.log('No Files found to process with Prettier');
- process.exit(1);
-}
-
-console.log(`${shouldSave ? 'Updating' : 'Checking'} ${files.length} file(s)`);
-
-files.forEach(file => {
- try {
- prettier
- .resolveConfig(file)
- .then(options => {
- const fileExtension = file.split('.').pop();
- Object.assign(options, {
- parser: config.parsers[fileExtension],
+let passedCount = 0;
+let failedCount = 0;
+let ignoredCount = 0;
+
+console.log(`${shouldSave ? 'Updating' : 'Checking'} ${matchedCount} file(s)`);
+
+const fixCommand = `yarn prettier-${allFiles ? 'all' : 'staged'}-save`;
+const warningMessage = `
+===============================
+GitLab uses Prettier to format all JavaScript code.
+Please format each file listed below or run "${fixCommand}"
+===============================
+`;
+
+const checkFileWithOptions = (filePath, options) =>
+ readFileAsync(filePath, 'utf8').then(input => {
+ if (shouldSave) {
+ const output = prettier.format(input, options);
+ if (input === output) {
+ passedCount += 1;
+ } else {
+ return writeFileAsync(filePath, output, 'utf8').then(() => {
+ console.log(`Prettified : ${filePath}`);
+ failedCount += 1;
});
-
- const input = fs.readFileSync(file, 'utf8');
-
- if (shouldSave) {
- const output = prettier.format(input, options);
- if (output !== input) {
- fs.writeFileSync(file, output, 'utf8');
- console.log(`Prettified : ${file}`);
- }
- } else if (!prettier.check(input, options)) {
- if (!didWarn) {
- console.log(
- '\n===============================\nGitLab uses Prettier to format all JavaScript code.\nPlease format each file listed below or run "yarn prettier-staged-save"\n===============================\n'
- );
- didWarn = true;
- }
- console.log(`Prettify Manually : ${file}`);
+ }
+ } else {
+ if (prettier.check(input, options)) {
+ passedCount += 1;
+ } else {
+ if (!didWarn) {
+ console.log(warningMessage);
+ didWarn = true;
}
- })
- .catch(e => {
- console.log(`Error on loading the Config File: ${e.message}`);
- process.exit(1);
- });
- } catch (error) {
- didError = true;
- console.log(`\n\nError with ${file}: ${error.message}`);
- }
-});
+ console.log(`Prettify Manually : ${filePath}`);
+ failedCount += 1;
+ }
+ }
+ });
-if (didWarn || didError) {
- process.exit(1);
-}
+const checkFileWithPrettierConfig = filePath =>
+ prettier
+ .getFileInfo(filePath, { ignorePath: '.prettierignore' })
+ .then(({ ignored, inferredParser }) => {
+ if (ignored || !inferredParser) {
+ ignoredCount += 1;
+ return;
+ }
+ return prettier.resolveConfig(filePath).then(fileOptions => {
+ const options = { ...fileOptions, parser: inferredParser };
+ return checkFileWithOptions(filePath, options);
+ });
+ });
+
+Promise.all(matchedFiles.map(checkFileWithPrettierConfig))
+ .then(() => {
+ const failAction = shouldSave ? 'fixed' : 'failed';
+ console.log(
+ `\nSummary:\n ${matchedCount} files processed (${passedCount} passed, ${failedCount} ${failAction}, ${ignoredCount} ignored)\n`
+ );
+
+ if (didWarn) process.exit(1);
+ })
+ .catch(e => {
+ console.log(`\nAn error occured while processing files with prettier: ${e.message}\n`);
+ process.exit(1);
+ });
diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh
index 178b209aacf..848364b4a9b 100755
--- a/scripts/lint-doc.sh
+++ b/scripts/lint-doc.sh
@@ -5,7 +5,7 @@ cd "$(dirname "$0")/.."
# Use long options (e.g. --header instead of -H) for curl examples in documentation.
echo '=> Checking for cURL short options...'
grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ >/dev/null 2>&1
-if [ $? == 0 ]
+if [ $? -eq 0 ]
then
echo '✖ ERROR: Short options for curl should not be used in documentation!
Use long options (e.g., --header instead of -H):' >&2
diff --git a/scripts/lint-rugged b/scripts/lint-rugged
index d0c2c544c47..22e3e1f1505 100755
--- a/scripts/lint-rugged
+++ b/scripts/lint-rugged
@@ -1,21 +1,9 @@
#!/usr/bin/env ruby
ALLOWED = [
- # Can be fixed once Rugged is no longer used in production. Doesn't make Rugged calls.
- 'config/initializers/8_metrics.rb',
-
- # Can be deleted once wiki's are fully (mandatory) migrated
- 'config/initializers/gollum.rb',
-
- # Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/953
+ # Needed to handle repositories that are not in any storage
'lib/gitlab/bare_repository_import/repository.rb',
- # Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/954
- 'lib/tasks/gitlab/cleanup.rake',
-
- # The only place where Rugged code is still allowed in production
- 'lib/gitlab/git/',
-
# Needed to avoid using the git binary to validate a branch name
'lib/gitlab/git_ref_validator.rb'
].freeze
diff --git a/scripts/rails4-gemfile-lock-check b/scripts/rails4-gemfile-lock-check
new file mode 100755
index 00000000000..a74a49874e1
--- /dev/null
+++ b/scripts/rails4-gemfile-lock-check
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+echo -e "=> Checking if Gemfile.rails4.lock is up-to-date...\\n"
+
+cp Gemfile.rails4.lock Gemfile.rails4.lock.orig
+BUNDLE_GEMFILE=Gemfile.rails4 bundle install "$BUNDLE_INSTALL_FLAGS"
+diff -u Gemfile.rails4.lock.orig Gemfile.rails4.lock >/dev/null 2>&1
+
+if [ $? == 1 ]
+then
+ diff -u Gemfile.rails4.lock.orig Gemfile.rails4.lock
+
+ echo -e "\\n✖ ERROR: Gemfile.rails4.lock is not up-to-date!
+ Please run 'BUNDLE_GEMFILE=Gemfile.rails4 bundle install'\\n" >&2
+ exit 1
+fi
+
+echo "✔ Gemfile.rails4.lock is up-to-date"
+exit 0
diff --git a/scripts/rails5-gemfile-lock-check b/scripts/rails5-gemfile-lock-check
deleted file mode 100755
index da6f1b7145e..00000000000
--- a/scripts/rails5-gemfile-lock-check
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-echo -e "=> Checking if Gemfile.rails5.lock is up-to-date...\\n"
-
-cp Gemfile.rails5.lock Gemfile.rails5.lock.orig
-BUNDLE_GEMFILE=Gemfile.rails5 bundle install "$BUNDLE_INSTALL_FLAGS"
-diff -u Gemfile.rails5.lock.orig Gemfile.rails5.lock >/dev/null 2>&1
-
-if [ $? == 1 ]
-then
- diff -u Gemfile.rails5.lock.orig Gemfile.rails5.lock
-
- echo -e "\\n✖ ERROR: Gemfile.rails5.lock is not up-to-date!
- Please run 'BUNDLE_GEMFILE=Gemfile.rails5 bundle install'\\n" >&2
- exit 1
-fi
-
-echo "✔ Gemfile.rails5.lock is up-to-date"
-exit 0
diff --git a/scripts/review_apps/automated_cleanup.rb b/scripts/review_apps/automated_cleanup.rb
new file mode 100755
index 00000000000..4166070f7cd
--- /dev/null
+++ b/scripts/review_apps/automated_cleanup.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require 'gitlab'
+require_relative File.expand_path('../../lib/quality/helm_client.rb', __dir__)
+require_relative File.expand_path('../../lib/quality/kubernetes_client.rb', __dir__)
+
+class AutomatedCleanup
+ attr_reader :project_path, :gitlab_token
+
+ DEPLOYMENTS_PER_PAGE = 100
+ HELM_RELEASES_BATCH_SIZE = 5
+ IGNORED_HELM_ERRORS = [
+ 'transport is closing',
+ 'error upgrading connection'
+ ].freeze
+ IGNORED_KUBERNETES_ERRORS = [
+ 'NotFound'
+ ].freeze
+
+ def self.ee?
+ ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
+ end
+
+ def initialize(project_path: ENV['CI_PROJECT_PATH'], gitlab_token: ENV['GITLAB_BOT_REVIEW_APPS_CLEANUP_TOKEN'])
+ @project_path = project_path
+ @gitlab_token = gitlab_token
+ ENV['TILLER_NAMESPACE'] ||= review_apps_namespace
+ end
+
+ def gitlab
+ @gitlab ||= begin
+ Gitlab.configure do |config|
+ config.endpoint = 'https://gitlab.com/api/v4'
+ # gitlab-bot's token "GitLab review apps cleanup"
+ config.private_token = gitlab_token
+ end
+
+ Gitlab
+ end
+ end
+
+ def review_apps_namespace
+ self.class.ee? ? 'review-apps-ee' : 'review-apps-ce'
+ end
+
+ def helm
+ @helm ||= Quality::HelmClient.new(namespace: review_apps_namespace)
+ end
+
+ def kubernetes
+ @kubernetes ||= Quality::KubernetesClient.new(namespace: review_apps_namespace)
+ end
+
+ def perform_gitlab_environment_cleanup!(days_for_stop:, days_for_delete:)
+ puts "Checking for review apps not updated in the last #{days_for_stop} days..."
+
+ checked_environments = []
+ delete_threshold = threshold_time(days: days_for_delete)
+ stop_threshold = threshold_time(days: days_for_stop)
+
+ gitlab.deployments(project_path, per_page: DEPLOYMENTS_PER_PAGE).auto_paginate do |deployment|
+ environment = deployment.environment
+
+ next unless environment.name.start_with?('review/')
+ next if checked_environments.include?(environment.slug)
+
+ last_deploy = deployment.created_at
+ deployed_at = Time.parse(last_deploy)
+
+ if deployed_at < delete_threshold
+ delete_environment(environment, deployment)
+ release = Quality::HelmClient::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, review_apps_namespace)
+ delete_helm_release(release)
+ elsif deployed_at < stop_threshold
+ stop_environment(environment, deployment)
+ else
+ print_release_state(subject: 'Review app', release_name: environment.slug, release_date: last_deploy, action: 'leaving')
+ end
+
+ checked_environments << environment.slug
+ end
+ end
+
+ def perform_helm_releases_cleanup!(days:)
+ puts "Checking for Helm releases not updated in the last #{days} days..."
+
+ threshold_day = threshold_time(days: days)
+
+ helm_releases.each do |release|
+ if release.status == 'FAILED' || release.last_update < threshold_day
+ delete_helm_release(release)
+ else
+ print_release_state(subject: 'Release', release_name: release.name, release_date: release.last_update, action: 'leaving')
+ end
+ end
+ end
+
+ private
+
+ def delete_environment(environment, deployment)
+ print_release_state(subject: 'Review app', release_name: environment.slug, release_date: deployment.created_at, action: 'deleting')
+ gitlab.delete_environment(project_path, environment.id)
+ end
+
+ def stop_environment(environment, deployment)
+ print_release_state(subject: 'Review app', release_name: environment.slug, release_date: deployment.created_at, action: 'stopping')
+ gitlab.stop_environment(project_path, environment.id)
+ end
+
+ def helm_releases
+ args = ['--all', '--date', "--max #{HELM_RELEASES_BATCH_SIZE}"]
+
+ helm.releases(args: args)
+ end
+
+ def delete_helm_release(release)
+ print_release_state(subject: 'Release', release_name: release.name, release_status: release.status, release_date: release.last_update, action: 'cleaning')
+ helm.delete(release_name: release.name)
+ kubernetes.cleanup(release_name: release.name)
+ rescue Quality::HelmClient::CommandFailedError => ex
+ raise ex unless ignore_exception?(ex.message, IGNORED_HELM_ERRORS)
+
+ puts "Ignoring the following Helm error:\n#{ex}\n"
+ rescue Quality::KubernetesClient::CommandFailedError => ex
+ raise ex unless ignore_exception?(ex.message, IGNORED_KUBERNETES_ERRORS)
+
+ puts "Ignoring the following Kubernetes error:\n#{ex}\n"
+ end
+
+ def threshold_time(days:)
+ Time.now - days * 24 * 3600
+ end
+
+ def ignore_exception?(exception_message, exceptions_ignored)
+ exception_message.match?(/(#{exceptions_ignored})/)
+ end
+
+ def print_release_state(subject:, release_name:, release_date:, action:, release_status: nil)
+ puts "\n#{subject} '#{release_name}' #{"(#{release_status}) " if release_status}was last deployed on #{release_date}: #{action} it.\n"
+ end
+end
+
+def timed(task)
+ start = Time.now
+ yield(self)
+ puts "#{task} finished in #{Time.now - start} seconds.\n"
+end
+
+automated_cleanup = AutomatedCleanup.new
+
+timed('Review apps cleanup') do
+ automated_cleanup.perform_gitlab_environment_cleanup!(days_for_stop: 2, days_for_delete: 3)
+end
+
+puts
+
+timed('Helm releases cleanup') do
+ automated_cleanup.perform_helm_releases_cleanup!(days: 3)
+end
+
+exit(0)
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
new file mode 100755
index 00000000000..00e23f12bc0
--- /dev/null
+++ b/scripts/review_apps/review-apps.sh
@@ -0,0 +1,231 @@
+[[ "$TRACE" ]] && set -x
+export TILLER_NAMESPACE="$KUBE_NAMESPACE"
+
+function check_kube_domain() {
+ if [ -z ${REVIEW_APPS_DOMAIN+x} ]; then
+ echo "In order to deploy or use Review Apps, REVIEW_APPS_DOMAIN variable must be set"
+ echo "You can do it in Auto DevOps project settings or defining a variable at group or project level"
+ echo "You can also manually add it in .gitlab-ci.yml"
+ false
+ else
+ true
+ fi
+}
+
+function download_gitlab_chart() {
+ curl -o gitlab.tar.bz2 https://gitlab.com/charts/gitlab/-/archive/$GITLAB_HELM_CHART_REF/gitlab-$GITLAB_HELM_CHART_REF.tar.bz2
+ tar -xjf gitlab.tar.bz2
+ cd gitlab-$GITLAB_HELM_CHART_REF
+
+ helm init --client-only
+ helm repo add gitlab https://charts.gitlab.io
+ helm dependency update
+ helm dependency build
+}
+
+function ensure_namespace() {
+ kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
+}
+
+function install_tiller() {
+ echo "Checking Tiller..."
+ helm init --upgrade
+ kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy"
+ if ! helm version --debug; then
+ echo "Failed to init Tiller."
+ return 1
+ fi
+ echo ""
+}
+
+function create_secret() {
+ echo "Create secret..."
+
+ kubectl create secret generic -n "$KUBE_NAMESPACE" \
+ $CI_ENVIRONMENT_SLUG-gitlab-initial-root-password \
+ --from-literal=password=$REVIEW_APPS_ROOT_PASSWORD \
+ --dry-run -o json | kubectl apply -f -
+}
+
+function deployExists() {
+ local namespace="${1}"
+ local deploy="${2}"
+ helm status --tiller-namespace "${namespace}" "${deploy}" >/dev/null 2>&1
+ return $?
+}
+
+function previousDeployFailed() {
+ set +e
+ deploy="${1}"
+ echo "Checking for previous deployment of ${deploy}"
+ deployment_status=$(helm status ${deploy} >/dev/null 2>&1)
+ status=$?
+ # if `status` is `0`, deployment exists, has a status
+ if [ $status -eq 0 ]; then
+ echo "Previous deployment found, checking status"
+ deployment_status=$(helm status ${deploy} | grep ^STATUS | cut -d' ' -f2)
+ echo "Previous deployment state: $deployment_status"
+ if [[ "$deployment_status" == "FAILED" || "$deployment_status" == "PENDING_UPGRADE" || "$deployment_status" == "PENDING_INSTALL" ]]; then
+ status=0;
+ else
+ status=1;
+ fi
+ else
+ echo "Previous deployment NOT found."
+ fi
+ set -e
+ return $status
+}
+
+function deploy() {
+ track="${1-stable}"
+ name="$CI_ENVIRONMENT_SLUG"
+
+ if [[ "$track" != "stable" ]]; then
+ name="$name-$track"
+ fi
+
+ replicas="1"
+ service_enabled="false"
+ postgres_enabled="$POSTGRES_ENABLED"
+ gitlab_migrations_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-rails-ce"
+ gitlab_sidekiq_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-sidekiq-ce"
+ gitlab_unicorn_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-unicorn-ce"
+ gitlab_gitaly_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitaly"
+ gitlab_shell_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-shell"
+ gitlab_workhorse_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-workhorse-ce"
+
+ if [[ "$CI_PROJECT_NAME" == "gitlab-ee" ]]; then
+ gitlab_migrations_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-rails-ee"
+ gitlab_sidekiq_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-sidekiq-ee"
+ gitlab_unicorn_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-unicorn-ee"
+ gitlab_workhorse_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-workhorse-ee"
+ fi
+
+ # canary uses stable db
+ [[ "$track" == "canary" ]] && postgres_enabled="false"
+
+ env_track=$( echo $track | tr -s '[:lower:]' '[:upper:]' )
+ env_slug=$( echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]' )
+
+ if [[ "$track" == "stable" ]]; then
+ # for stable track get number of replicas from `PRODUCTION_REPLICAS`
+ eval new_replicas=\$${env_slug}_REPLICAS
+ service_enabled="true"
+ else
+ # for all tracks get number of replicas from `CANARY_PRODUCTION_REPLICAS`
+ eval new_replicas=\$${env_track}_${env_slug}_REPLICAS
+ fi
+ if [[ -n "$new_replicas" ]]; then
+ replicas="$new_replicas"
+ fi
+
+ # Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade`
+ if [ "$CI_ENVIRONMENT_SLUG" != "production" ] && previousDeployFailed "$CI_ENVIRONMENT_SLUG" ; then
+ echo "Deployment in bad state, cleaning up $CI_ENVIRONMENT_SLUG"
+ delete
+ cleanup
+ fi
+
+ create_secret
+
+ helm repo add gitlab https://charts.gitlab.io/
+ helm dep update .
+
+HELM_CMD=$(cat << EOF
+ helm upgrade --install \
+ --wait \
+ --timeout 600 \
+ --set global.appConfig.enableUsagePing=false \
+ --set releaseOverride="$CI_ENVIRONMENT_SLUG" \
+ --set global.hosts.hostSuffix="$HOST_SUFFIX" \
+ --set global.hosts.domain="$REVIEW_APPS_DOMAIN" \
+ --set certmanager.install=false \
+ --set global.ingress.configureCertmanager=false \
+ --set global.ingress.tls.secretName=tls-cert \
+ --set global.ingress.annotations."external-dns\.alpha\.kubernetes\.io/ttl"="10"
+ --set gitlab.unicorn.resources.requests.cpu=200m \
+ --set gitlab.sidekiq.resources.requests.cpu=100m \
+ --set gitlab.gitlab-shell.resources.requests.cpu=100m \
+ --set redis.resources.requests.cpu=100m \
+ --set minio.resources.requests.cpu=100m \
+ --set gitlab.migrations.image.repository="$gitlab_migrations_image_repository" \
+ --set gitlab.migrations.image.tag="$CI_COMMIT_REF_NAME" \
+ --set gitlab.sidekiq.image.repository="$gitlab_sidekiq_image_repository" \
+ --set gitlab.sidekiq.image.tag="$CI_COMMIT_REF_NAME" \
+ --set gitlab.unicorn.image.repository="$gitlab_unicorn_image_repository" \
+ --set gitlab.unicorn.image.tag="$CI_COMMIT_REF_NAME" \
+ --set gitlab.gitaly.image.repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitaly" \
+ --set gitlab.gitaly.image.tag="v$GITALY_VERSION" \
+ --set gitlab.gitlab-shell.image.repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-shell" \
+ --set gitlab.gitlab-shell.image.tag="v$GITLAB_SHELL_VERSION" \
+ --set gitlab.unicorn.workhorse.image="$gitlab_workhorse_image_repository" \
+ --set gitlab.unicorn.workhorse.tag="$CI_COMMIT_REF_NAME" \
+ --set nginx-ingress.controller.config.ssl-ciphers="ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4" \
+ --namespace="$KUBE_NAMESPACE" \
+ --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
+ "$name" \
+ .
+EOF
+)
+
+ echo "Deploying with:"
+ echo $HELM_CMD
+
+ eval $HELM_CMD
+}
+
+function delete() {
+ track="${1-stable}"
+ name="$CI_ENVIRONMENT_SLUG"
+
+ if [ -z "$CI_ENVIRONMENT_SLUG" ]; then
+ echo "No release given, aborting the delete!"
+ return
+ fi
+
+ if [[ "$track" != "stable" ]]; then
+ name="$name-$track"
+ fi
+
+ if ! deployExists "${KUBE_NAMESPACE}" "${name}"; then
+ echo "The release $name doesn't exist, aborting the cleanup!"
+ return
+ fi
+
+ echo "Deleting release '$name'..."
+ helm delete --purge "$name" || true
+}
+
+function cleanup() {
+ if [ -z "$CI_ENVIRONMENT_SLUG" ]; then
+ echo "No release given, aborting the delete!"
+ return
+ fi
+
+ echo "Cleaning up '$CI_ENVIRONMENT_SLUG'..."
+ kubectl -n "$KUBE_NAMESPACE" delete \
+ ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa \
+ -l release="$CI_ENVIRONMENT_SLUG" \
+ || true
+}
+
+function install_external_dns() {
+ local release_name="dns-gitlab-review-app"
+ local domain=$(echo "${REVIEW_APPS_DOMAIN}" | awk -F. '{printf "%s.%s", $(NF-1), $NF}')
+
+ if ! deployExists "${KUBE_NAMESPACE}" "${release_name}" || previousDeployFailed "${release_name}" ; then
+ echo "Installing external-dns helm chart"
+ helm repo update
+ helm install stable/external-dns \
+ -n "${release_name}" \
+ --namespace "${KUBE_NAMESPACE}" \
+ --set provider="aws" \
+ --set aws.secretKey="${REVIEW_APPS_AWS_SECRET_KEY}" \
+ --set aws.accessKey="${REVIEW_APPS_AWS_ACCESS_KEY}" \
+ --set aws.zoneType="public" \
+ --set domainFilters[0]="${domain}" \
+ --set txtOwnerId="${KUBE_NAMESPACE}" \
+ --set rbac.create="true"
+ fi
+}
diff --git a/scripts/schema_changed.sh b/scripts/schema_changed.sh
index 5de2b35571d..b5e510c2367 100644
--- a/scripts/schema_changed.sh
+++ b/scripts/schema_changed.sh
@@ -1,9 +1,14 @@
-function schema_changed() {
- if [[ ! -z `git diff --name-only -- db/schema.rb` ]]; then
- echo "db/schema.rb after rake db:migrate:reset is different from one in the repository"
+#!/bin/sh
+
+schema_changed() {
+ if [ ! -z "$(git diff --name-only -- db/schema.rb)" ]; then
+ printf "db/schema.rb after rake db:migrate:reset is different from one in the repository"
+ printf "The diff is as follows:\n"
+ diff=$(git diff -p --binary -- db/schema.rb)
+ printf "%s" "$diff"
exit 1
else
- echo "db/schema.rb after rake db:migrate:reset matches one in the repository"
+ printf "db/schema.rb after rake db:migrate:reset matches one in the repository"
fi
}
diff --git a/scripts/static-analysis b/scripts/static-analysis
index 0e67eabfec1..25ba7ec6c8e 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -29,6 +29,7 @@ tasks = [
%w[bin/rake lint:all],
%w[bundle exec license_finder],
%w[yarn run eslint],
+ %w[yarn run prettier-all],
%w[bundle exec rubocop --parallel],
%w[scripts/lint-conflicts.sh],
%w[scripts/lint-rugged]
diff --git a/scripts/trigger-build b/scripts/trigger-build
index 798bf1e82b7..873c41db456 100755
--- a/scripts/trigger-build
+++ b/scripts/trigger-build
@@ -1,122 +1,169 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
-require 'net/http'
-require 'json'
-require 'cgi'
+require 'gitlab'
-module Trigger
- OMNIBUS_PROJECT_PATH = 'gitlab-org/omnibus-gitlab'.freeze
- CNG_PROJECT_PATH = 'gitlab-org/build/CNG'.freeze
- TOKEN = ENV['BUILD_TRIGGER_TOKEN']
+#
+# Configure credentials to be used with gitlab gem
+#
+Gitlab.configure do |config|
+ config.endpoint = 'https://gitlab.com/api/v4'
+end
+module Trigger
def self.ee?
ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
end
- class Omnibus
- def initialize
- @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Trigger::OMNIBUS_PROJECT_PATH)}/trigger/pipeline")
- @params = env_params.merge(file_params).merge(token: Trigger::TOKEN)
+ class Base
+ def invoke!(post_comment: false)
+ pipeline = Gitlab.run_trigger(
+ downstream_project_path,
+ trigger_token,
+ ref,
+ variables)
+
+ puts "Triggered downstream pipeline: #{pipeline.web_url}\n"
+ puts "Waiting for downstream pipeline status"
+
+ Trigger::CommitComment.post!(pipeline, access_token) if post_comment
+ Trigger::Pipeline.new(downstream_project_path, pipeline.id, access_token)
end
- def invoke!
- res = Net::HTTP.post_form(@uri, @params)
- id = JSON.parse(res.body)['id']
- project = Trigger::OMNIBUS_PROJECT_PATH
+ private
- if id
- puts "Triggered https://gitlab.com/#{project}/pipelines/#{id}"
- puts "Waiting for downstream pipeline status"
- else
- raise "Trigger failed! The response from the trigger is: #{res.body}"
- end
+ # Must be overridden
+ def downstream_project_path
+ raise NotImplementedError
+ end
- Trigger::Pipeline.new(project, id)
+ # Must be overridden
+ def ref
+ raise NotImplementedError
end
- private
+ # Must be overridden
+ def trigger_token
+ raise NotImplementedError
+ end
+
+ # Must be overridden
+ def access_token
+ raise NotImplementedError
+ end
+
+ # Can be overridden
+ def extra_variables
+ {}
+ end
- def env_params
+ # Can be overridden
+ def version_param_value(version_file)
+ File.read(version_file).strip
+ end
+
+ def variables
+ base_variables.merge(extra_variables).merge(version_file_variables)
+ end
+
+ def base_variables
{
- "ref" => ENV["OMNIBUS_BRANCH"] || "master",
- "variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
- "variables[ALTERNATIVE_SOURCES]" => true,
- "variables[ee]" => Trigger.ee? ? 'true' : 'false',
- "variables[TRIGGERED_USER]" => ENV["GITLAB_USER_NAME"],
- "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
+ 'TRIGGERED_USER' => ENV['TRIGGERED_USER'] || ENV['GITLAB_USER_NAME'],
+ 'TRIGGER_SOURCE' => ENV['CI_JOB_URL'],
+ 'TOP_UPSTREAM_SOURCE_PROJECT' => ENV['CI_PROJECT_PATH'],
+ 'TOP_UPSTREAM_SOURCE_JOB' => ENV['CI_JOB_URL'],
+ 'TOP_UPSTREAM_SOURCE_SHA' => ENV['CI_COMMIT_SHA']
}
end
- def file_params
- Hash.new.tap do |params|
- Dir.glob("*_VERSION").each do |version_file|
- params["variables[#{version_file}]"] = File.read(version_file).strip
- end
+ # Read version files from all components
+ def version_file_variables
+ Dir.glob("*_VERSION").each_with_object({}) do |version_file, params|
+ params[version_file] = version_param_value(version_file)
end
end
end
- class CNG
- def initialize
- @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Trigger::CNG_PROJECT_PATH)}/trigger/pipeline")
- @ref_name = ENV['CI_COMMIT_REF_NAME']
- @username = ENV['GITLAB_USER_NAME']
- @project_name = ENV['CI_PROJECT_NAME']
- @job_id = ENV['CI_JOB_ID']
- @params = env_params.merge(file_params).merge(token: Trigger::TOKEN)
- end
-
- #
- # Trigger a pipeline
- #
- def invoke!
- res = Net::HTTP.post_form(@uri, @params)
- id = JSON.parse(res.body)['id']
- project = Trigger::CNG_PROJECT_PATH
-
- if id
- puts "Triggered https://gitlab.com/#{project}/pipelines/#{id}"
- puts "Waiting for downstream pipeline status"
- else
- raise "Trigger failed! The response from the trigger is: #{res.body}"
- end
+ class Omnibus < Base
+ private
+
+ def downstream_project_path
+ 'gitlab-org/omnibus-gitlab'
+ end
+
+ def ref
+ ENV['OMNIBUS_BRANCH'] || 'master'
+ end
+
+ def trigger_token
+ ENV['BUILD_TRIGGER_TOKEN']
+ end
+
+ def access_token
+ ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ end
- Trigger::Pipeline.new(project, id)
+ def extra_variables
+ {
+ 'GITLAB_VERSION' => ENV['CI_COMMIT_SHA'],
+ 'ALTERNATIVE_SOURCES' => 'true',
+ 'ee' => Trigger.ee? ? 'true' : 'false',
+ 'QA_BRANCH' => ENV['QA_BRANCH'] || 'master'
+ }
end
+ end
+ class CNG < Base
private
- def env_params
- params = {
- "ref" => ENV["CNG_BRANCH"] || "master",
- "variables[TRIGGERED_USER]" => @username,
- "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{@project_name}/-/jobs/#{@job_id}"
+ def downstream_project_path
+ ENV['CNG_PROJECT_PATH'] || 'gitlab-org/build/CNG-mirror'
+ end
+
+ def ref
+ ENV['CNG_BRANCH'] || 'master'
+ end
+
+ def trigger_token
+ ENV['BUILD_TRIGGER_TOKEN']
+ end
+
+ def access_token
+ ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ end
+
+ def extra_variables
+ edition = Trigger.ee? ? 'EE' : 'CE'
+
+ {
+ "GITLAB_#{edition}_VERSION" => ENV['CI_COMMIT_REF_NAME'],
+ "#{edition}_PIPELINE" => 'true'
}
+ end
- if Trigger.ee?
- params["variables[GITLAB_EE_VERSION]"] = @ref_name
- params["variables[EE_PIPELINE]"] = 'true'
+ def version_param_value(_version_file)
+ raw_version = super
+
+ # if the version matches semver format, treat it as a tag and prepend `v`
+ if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/)
+ "v#{raw_version}"
else
- params["variables[GITLAB_CE_VERSION]"] = @ref_name
- params["variables[CE_PIPELINE]"] = 'true'
+ raw_version
end
-
- params
end
+ end
- # Read version files from all components
- def file_params
- Dir.glob("*_VERSION").each_with_object({}) do |version_file, params|
- raw_version = File.read(version_file).strip
- # if the version matches semver format, treat it as a tag and prepend `v`
- version = if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/)
- "v#{raw_version}"
- else
- raw_version
- end
-
- params["variables[#{version_file}]"] = version
- end
+ class CommitComment
+ def self.post!(downstream_pipeline, access_token)
+ Gitlab.private_token = access_token
+
+ Gitlab.create_commit_comment(
+ ENV['CI_PROJECT_PATH'],
+ ENV['CI_COMMIT_SHA'],
+ "The [`#{ENV['CI_JOB_NAME']}`](#{ENV['CI_JOB_URL']}) job from pipeline #{ENV['CI_PIPELINE_URL']} triggered #{downstream_pipeline.web_url} downstream.")
+
+ rescue Gitlab::Error::Error => error
+ puts "Ignoring the following error: #{error}"
end
end
@@ -124,9 +171,16 @@ module Trigger
INTERVAL = 60 # seconds
MAX_DURATION = 3600 * 3 # 3 hours
- def initialize(project, id)
+ attr_reader :project, :id, :api_token
+
+ def initialize(project, id, api_token)
+ @project = project
+ @id = id
+ @api_token = api_token
@start = Time.now.to_i
- @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(project)}/pipelines/#{id}")
+
+ # gitlab-bot's token "GitLab multi-project pipeline polling"
+ Gitlab.private_token = api_token
end
def wait!
@@ -157,15 +211,9 @@ module Trigger
end
def status
- req = Net::HTTP::Get.new(@uri)
- req['PRIVATE-TOKEN'] = ENV['GITLAB_QA_ACCESS_TOKEN']
-
- res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: true) do |http|
- http.request(req)
- end
-
- JSON.parse(res.body)['status'].to_s.to_sym
- rescue JSON::ParserError
+ Gitlab.pipeline(project, id).status.to_sym
+ rescue Gitlab::Error::Error => error
+ puts "Ignoring the following error: #{error}"
# Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job
# timeout anyway.
:running
@@ -175,7 +223,7 @@ end
case ARGV[0]
when 'omnibus'
- Trigger::Omnibus.new.invoke!.wait!
+ Trigger::Omnibus.new.invoke!(post_comment: true).wait!
when 'cng'
Trigger::CNG.new.invoke!.wait!
else
diff --git a/scripts/trigger-build-docs b/scripts/trigger-build-docs
index 9ee35684509..dfc8ee6050a 100755
--- a/scripts/trigger-build-docs
+++ b/scripts/trigger-build-docs
@@ -6,8 +6,8 @@ require 'gitlab'
# Configure credentials to be used with gitlab gem
#
Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
- config.private_token = ENV["DOCS_API_TOKEN"] # GitLab Docs bot access token with Developer access to gitlab-docs
+ config.endpoint = 'https://gitlab.com/api/v4'
+ config.private_token = ENV["DOCS_API_TOKEN"] # GitLab Docs bot access token with Developer access to gitlab-docs
end
#
@@ -99,7 +99,7 @@ def trigger_pipeline
puts "=> Follow the status of the triggered pipeline:"
puts ""
- puts "https://gitlab.com/gitlab-com/gitlab-docs/pipelines/#{pipeline.id}"
+ puts pipeline.web_url
puts ""
puts "=> In a few minutes, you will be able to preview your changes under the following URL:"
puts ""
diff --git a/spec/bin/changelog_spec.rb b/spec/bin/changelog_spec.rb
index 9dc4edf97d1..c59add88a82 100644
--- a/spec/bin/changelog_spec.rb
+++ b/spec/bin/changelog_spec.rb
@@ -95,6 +95,7 @@ describe 'bin/changelog' do
it 'shows error message and exits the program' do
allow($stdin).to receive(:getc).and_return(type)
+
expect do
expect { described_class.read_type }.to raise_error(
ChangelogHelpers::Abort,
diff --git a/spec/bin/storage_check_spec.rb b/spec/bin/storage_check_spec.rb
deleted file mode 100644
index 02f6fcb6e3a..00000000000
--- a/spec/bin/storage_check_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'spec_helper'
-
-describe 'bin/storage_check' do
- it 'is executable' do
- command = %w[bin/storage_check -t unix://the/path/to/a/unix-socket.sock -i 10 -d]
- expected_output = 'Checking unix://the/path/to/a/unix-socket.sock every 10 seconds'
-
- output, status = Gitlab::Popen.popen(command, Rails.root.to_s)
-
- expect(status).to eq(0)
- expect(output).to include(expected_output)
- end
-end
diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb
new file mode 100644
index 00000000000..83b2de47741
--- /dev/null
+++ b/spec/config/settings_spec.rb
@@ -0,0 +1,9 @@
+require 'spec_helper'
+
+describe Settings do
+ describe 'omniauth' do
+ it 'defaults to enabled' do
+ expect(described_class.omniauth.enabled).to be true
+ end
+ end
+end
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index 9d10d725ff3..2e0f79cd313 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -78,5 +78,30 @@ describe Admin::ApplicationSettingsController do
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.restricted_visibility_levels).to be_empty
end
+
+ it 'updates the receive_max_input_size setting' do
+ put :update, application_setting: { receive_max_input_size: "1024" }
+
+ expect(response).to redirect_to(admin_application_settings_path)
+ expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
+ end
+ end
+
+ describe 'PUT #reset_registration_token' do
+ before do
+ sign_in(admin)
+ end
+
+ subject { put :reset_registration_token }
+
+ it 'resets runner registration token' do
+ expect { subject }.to change { ApplicationSetting.current.runners_registration_token }
+ end
+
+ it 'redirects the user to admin runners page' do
+ subject
+
+ expect(response).to redirect_to(admin_runners_path)
+ end
end
end
diff --git a/spec/controllers/admin/health_check_controller_spec.rb b/spec/controllers/admin/health_check_controller_spec.rb
index d15ee0021d9..e13401fc06b 100644
--- a/spec/controllers/admin/health_check_controller_spec.rb
+++ b/spec/controllers/admin/health_check_controller_spec.rb
@@ -8,18 +8,10 @@ describe Admin::HealthCheckController do
end
describe 'GET show' do
- it 'loads the git storage health information' do
+ it 'loads the health information' do
get :show
- expect(assigns[:failing_storage_statuses]).not_to be_nil
- end
- end
-
- describe 'POST reset_storage_health' do
- it 'resets all storage health information' do
- expect(Gitlab::Git::Storage::FailureInfo).to receive(:reset_all!)
-
- post :reset_storage_health
+ expect(assigns[:errors]).not_to be_nil
end
end
end
diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb
index cc200b9fed9..ee1aff09bdf 100644
--- a/spec/controllers/admin/projects_controller_spec.rb
+++ b/spec/controllers/admin/projects_controller_spec.rb
@@ -42,4 +42,15 @@ describe Admin::ProjectsController do
expect { get :index }.not_to exceed_query_limit(control_count)
end
end
+
+ describe 'GET /projects/:id' do
+ render_views
+
+ it 'renders show page' do
+ get :show, namespace_id: project.namespace.path, id: project.path
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to match(project.name)
+ end
+ end
end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index fbf116e533b..efc3ce74627 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -190,30 +190,6 @@ describe ApplicationController do
end
end
- describe 'rescue from Gitlab::Git::Storage::Inaccessible' do
- controller(described_class) do
- def index
- raise Gitlab::Git::Storage::Inaccessible.new('broken', 100)
- end
- end
-
- it 'renders a 503 when storage is not available' do
- sign_in(create(:user))
-
- get :index
-
- expect(response.status).to eq(503)
- end
-
- it 'renders includes a Retry-After header' do
- sign_in(create(:user))
-
- get :index
-
- expect(response.headers['Retry-After']).to eq(100)
- end
- end
-
describe 'response format' do
controller(described_class) do
def index
@@ -674,7 +650,7 @@ describe ApplicationController do
describe '#access_denied' do
controller(described_class) do
def index
- access_denied!(params[:message])
+ access_denied!(params[:message], params[:status])
end
end
@@ -693,5 +669,159 @@ describe ApplicationController do
expect(response).to have_gitlab_http_status(403)
end
+
+ it 'renders a status passed to access denied' do
+ get :index, status: 401
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+
+ context 'when invalid UTF-8 parameters are received' do
+ controller(described_class) do
+ def index
+ params[:text].split(' ')
+
+ render json: :ok
+ end
+ end
+
+ before do
+ sign_in user
+ end
+
+ context 'html' do
+ subject { get :index, text: "hi \255" }
+
+ it 'renders 412' do
+ if Gitlab.rails5?
+ expect { subject }.to raise_error(ActionController::BadRequest)
+ else
+ subject
+
+ expect(response).to have_gitlab_http_status(412)
+ expect(response).to render_template :precondition_failed
+ end
+ end
+ end
+
+ context 'js' do
+ subject { get :index, text: "hi \255", format: :js }
+
+ it 'renders 412' do
+ if Gitlab.rails5?
+ expect { subject }.to raise_error(ActionController::BadRequest)
+ else
+ subject
+
+ json_response = JSON.parse(response.body)
+
+ expect(response).to have_gitlab_http_status(412)
+ expect(json_response['error']).to eq('Invalid UTF-8')
+ end
+ end
+ end
+ end
+
+ context 'X-GitLab-Custom-Error header' do
+ before do
+ sign_in user
+ end
+
+ context 'given a 422 error page' do
+ controller do
+ def index
+ render 'errors/omniauth_error', layout: 'errors', status: 422
+ end
+ end
+
+ it 'sets a custom header' do
+ get :index
+
+ expect(response.headers['X-GitLab-Custom-Error']).to eq '1'
+ end
+ end
+
+ context 'given a 500 error page' do
+ controller do
+ def index
+ render 'errors/omniauth_error', layout: 'errors', status: 500
+ end
+ end
+
+ it 'sets a custom header' do
+ get :index
+
+ expect(response.headers['X-GitLab-Custom-Error']).to eq '1'
+ end
+ end
+
+ context 'given a 200 success page' do
+ controller do
+ def index
+ render 'errors/omniauth_error', layout: 'errors', status: 200
+ end
+ end
+
+ it 'does not set a custom header' do
+ get :index
+
+ expect(response.headers['X-GitLab-Custom-Error']).to be_nil
+ end
+ end
+
+ context 'given a json response' do
+ controller do
+ def index
+ render json: {}, status: :unprocessable_entity
+ end
+ end
+
+ it 'does not set a custom header' do
+ get :index, format: :json
+
+ expect(response.headers['X-GitLab-Custom-Error']).to be_nil
+ end
+ end
+
+ context 'given a json response for an html request' do
+ controller do
+ def index
+ render json: {}, status: :unprocessable_entity
+ end
+ end
+
+ it 'does not set a custom header' do
+ get :index
+
+ expect(response.headers['X-GitLab-Custom-Error']).to be_nil
+ end
+ end
+ end
+
+ context 'control headers' do
+ controller(described_class) do
+ def index
+ render json: :ok
+ end
+ end
+
+ context 'user not logged in' do
+ it 'sets the default headers' do
+ get :index
+
+ expect(response.headers['Cache-Control']).to be_nil
+ end
+ end
+
+ context 'user logged in' do
+ it 'sets the default headers' do
+ sign_in(user)
+
+ get :index
+
+ expect(response.headers['Cache-Control']).to eq 'max-age=0, private, must-revalidate, no-store'
+ end
+ end
end
end
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 2c59d1929a1..883bb35f396 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -274,14 +274,11 @@ describe AutocompleteController do
context 'authorized projects apply limit' do
before do
- authorized_project2 = create(:project)
- authorized_project3 = create(:project)
-
- authorized_project.add_maintainer(user)
- authorized_project2.add_maintainer(user)
- authorized_project3.add_maintainer(user)
+ allow(Kaminari.config).to receive(:default_per_page).and_return(2)
- stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
+ create_list(:project, 2) do |project|
+ project.add_maintainer(user)
+ end
end
describe 'GET #projects with project ID' do
@@ -291,7 +288,7 @@ describe AutocompleteController do
it 'returns projects' do
expect(json_response).to be_kind_of(Array)
- expect(json_response.size).to eq 2 # Of a total of 3
+ expect(json_response.size).to eq(Kaminari.config.default_per_page)
end
end
end
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index d98e6ff0df8..98946e4287b 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -30,6 +30,15 @@ describe Boards::IssuesController do
context 'when list id is present' do
context 'with valid list id' do
+ let(:group) { create(:group, :private, projects: [project]) }
+ let(:group_board) { create(:board, group: group) }
+ let!(:list3) { create(:list, board: group_board, label: development, position: 2) }
+ let(:sub_group_1) { create(:group, :private, parent: group) }
+
+ before do
+ group.add_maintainer(user)
+ end
+
it 'returns issues that have the list label applied' do
issue = create(:labeled_issue, project: project, labels: [planning])
create(:labeled_issue, project: project, labels: [planning])
@@ -56,6 +65,39 @@ describe Boards::IssuesController do
expect { list_issues(user: user, board: board, list: list2) }.not_to exceed_query_limit(control_count)
end
+
+ it 'avoids N+1 database queries when adding a project', :request_store do
+ create(:labeled_issue, project: project, labels: [development])
+ control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count
+
+ 2.times do
+ p = create(:project, group: group)
+ create(:labeled_issue, project: p, labels: [development])
+ end
+
+ project_2 = create(:project, group: group)
+ create(:labeled_issue, project: project_2, labels: [development], assignees: [johndoe])
+
+ # because each issue without relative_position must be updated with
+ # a different value, we have 8 extra queries per issue
+ expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1))
+ end
+
+ it 'avoids N+1 database queries when adding a subgroup, project, and issue', :nested_groups do
+ create(:project, group: sub_group_1)
+ create(:labeled_issue, project: project, labels: [development])
+ control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count
+ project_2 = create(:project, group: group)
+
+ 2.times do
+ p = create(:project, group: sub_group_1)
+ create(:labeled_issue, project: p, labels: [development])
+ end
+
+ create(:labeled_issue, project: project_2, labels: [development], assignees: [johndoe])
+
+ expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1))
+ end
end
context 'with invalid list id' do
@@ -102,12 +144,15 @@ describe Boards::IssuesController do
sign_in(user)
params = {
- namespace_id: project.namespace.to_param,
- project_id: project,
board_id: board.to_param,
list_id: list.try(:to_param)
}
+ unless board.try(:parent)&.is_a?(Group)
+ params[:namespace_id] = project.namespace.to_param
+ params[:project_id] = project
+ end
+
get :index, params.compact
end
end
@@ -163,11 +208,22 @@ describe Boards::IssuesController do
end
end
- context 'with unauthorized user' do
- it 'returns a forbidden 403 response' do
- create_issue user: guest, board: board, list: list1, title: 'New issue'
+ context 'with guest user' do
+ context 'in open list' do
+ it 'returns a successful 200 response' do
+ open_list = board.lists.create(list_type: :backlog)
+ create_issue user: guest, board: board, list: open_list, title: 'New issue'
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ context 'in label list' do
+ it 'returns a forbidden 403 response' do
+ create_issue user: guest, board: board, list: list1, title: 'New issue'
+
+ expect(response).to have_gitlab_http_status(403)
+ end
end
end
diff --git a/spec/controllers/concerns/issuable_collections_spec.rb b/spec/controllers/concerns/issuable_collections_spec.rb
index c1f42bbb9d7..e93c923fd39 100644
--- a/spec/controllers/concerns/issuable_collections_spec.rb
+++ b/spec/controllers/concerns/issuable_collections_spec.rb
@@ -21,6 +21,34 @@ describe IssuableCollections do
controller
end
+ describe '#set_set_order_from_cookie' do
+ describe 'when sort param given' do
+ let(:cookies) { {} }
+ let(:params) { { sort: 'downvotes_asc' } }
+
+ it 'sets the cookie with the right values and flags' do
+ allow(controller).to receive(:cookies).and_return(cookies)
+
+ controller.send(:set_sort_order_from_cookie)
+
+ expect(cookies['issue_sort']).to eq({ value: 'popularity', secure: false, httponly: false })
+ end
+ end
+
+ describe 'when cookie exists' do
+ let(:cookies) { { 'issue_sort' => 'id_asc' } }
+ let(:params) { {} }
+
+ it 'sets the cookie with the right values and flags' do
+ allow(controller).to receive(:cookies).and_return(cookies)
+
+ controller.send(:set_sort_order_from_cookie)
+
+ expect(cookies['issue_sort']).to eq({ value: 'created_asc', secure: false, httponly: false })
+ end
+ end
+ end
+
describe '#page_count_for_relation' do
let(:params) { { state: 'opened' } }
@@ -32,7 +60,7 @@ describe IssuableCollections do
end
end
- describe '#filter_params' do
+ describe '#finder_options' do
let(:params) do
{
assignee_id: '1',
@@ -56,25 +84,20 @@ describe IssuableCollections do
}
end
- it 'filters params' do
+ it 'only allows whitelisted params' do
allow(controller).to receive(:cookies).and_return({})
- filtered_params = controller.send(:filter_params)
+ finder_options = controller.send(:finder_options)
- expect(filtered_params).to eq({
+ expect(finder_options).to eq({
'assignee_id' => '1',
'assignee_username' => 'user1',
'author_id' => '2',
'author_username' => 'user2',
- 'authorized_only' => 'true',
- 'due_date' => '2017-01-01',
- 'group_id' => '3',
- 'iids' => '4',
'label_name' => 'foo',
'milestone_title' => 'bar',
'my_reaction_emoji' => 'thumbsup',
- 'non_archived' => 'true',
- 'project_id' => '5',
+ 'due_date' => '2017-01-01',
'scope' => 'all',
'search' => 'baz',
'sort' => 'priority',
diff --git a/spec/controllers/concerns/send_file_upload_spec.rb b/spec/controllers/concerns/send_file_upload_spec.rb
index 58bb91a0c80..379b2d6b935 100644
--- a/spec/controllers/concerns/send_file_upload_spec.rb
+++ b/spec/controllers/concerns/send_file_upload_spec.rb
@@ -28,8 +28,9 @@ describe SendFileUpload do
describe '#send_upload' do
let(:controller) { controller_class.new }
let(:temp_file) { Tempfile.new('test') }
+ let(:params) { {} }
- subject { controller.send_upload(uploader) }
+ subject { controller.send_upload(uploader, **params) }
before do
FileUtils.touch(temp_file)
@@ -52,7 +53,7 @@ describe SendFileUpload do
end
context 'with attachment' do
- subject { controller.send_upload(uploader, attachment: 'test.js') }
+ let(:params) { { attachment: 'test.js' } }
it 'sends a file with content-type of text/plain' do
expected_params = {
@@ -64,6 +65,28 @@ describe SendFileUpload do
subject
end
+
+ context 'with a proxied file in object storage' do
+ before do
+ stub_uploads_object_storage(uploader: uploader_class)
+ uploader.object_store = ObjectStorage::Store::REMOTE
+ uploader.store!(temp_file)
+ allow(Gitlab.config.uploads.object_store).to receive(:proxy_download) { true }
+ end
+
+ it 'sends a file with a custom type' do
+ headers = double
+ expected_headers = %r(response-content-disposition=attachment%3Bfilename%3D%22test.js%22&response-content-type=application/ecmascript)
+ expect(Gitlab::Workhorse).to receive(:send_url).with(expected_headers).and_call_original
+ expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-url:/)
+
+ expect(controller).not_to receive(:send_file)
+ expect(controller).to receive(:headers) { headers }
+ expect(controller).to receive(:head).with(:ok)
+
+ subject
+ end
+ end
end
context 'when remote file is used' do
@@ -73,14 +96,15 @@ describe SendFileUpload do
uploader.store!(temp_file)
end
- context 'and proxying is enabled' do
- before do
- allow(Gitlab.config.uploads.object_store).to receive(:proxy_download) { true }
- end
-
+ shared_examples 'proxied file' do
it 'sends a file' do
headers = double
+ expect(Gitlab::Workhorse).not_to receive(:send_url).with(/response-content-disposition/)
+ expect(Gitlab::Workhorse).not_to receive(:send_url).with(/response-content-type/)
+ expect(Gitlab::Workhorse).to receive(:send_url).and_call_original
+
expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-url:/)
+ expect(controller).not_to receive(:send_file)
expect(controller).to receive(:headers) { headers }
expect(controller).to receive(:head).with(:ok)
@@ -88,6 +112,14 @@ describe SendFileUpload do
end
end
+ context 'and proxying is enabled' do
+ before do
+ allow(Gitlab.config.uploads.object_store).to receive(:proxy_download) { true }
+ end
+
+ it_behaves_like 'proxied file'
+ end
+
context 'and proxying is disabled' do
before do
allow(Gitlab.config.uploads.object_store).to receive(:proxy_download) { false }
@@ -98,6 +130,12 @@ describe SendFileUpload do
subject
end
+
+ context 'with proxy requested' do
+ let(:params) { { proxy: true } }
+
+ it_behaves_like 'proxied file'
+ end
end
end
end
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
index 505c040b5d5..278b980b6d8 100644
--- a/spec/controllers/dashboard/milestones_controller_spec.rb
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -3,9 +3,11 @@ require 'spec_helper'
describe Dashboard::MilestonesController do
let(:project) { create(:project) }
let(:group) { create(:group) }
+ let(:public_group) { create(:group, :public) }
let(:user) { create(:user) }
let(:project_milestone) { create(:milestone, project: project) }
let(:group_milestone) { create(:milestone, group: group) }
+ let!(:public_milestone) { create(:milestone, group: public_group) }
let(:milestone) do
DashboardMilestone.build(
[project],
@@ -43,13 +45,22 @@ describe Dashboard::MilestonesController do
end
describe "#index" do
- it 'should contain group and project milestones' do
+ render_views
+
+ it 'returns group and project milestones to which the user belongs' do
get :index, format: :json
expect(response).to have_gitlab_http_status(200)
expect(json_response.size).to eq(2)
- expect(json_response.map { |i| i["first_milestone"]["id"] }).to include(group_milestone.id, project_milestone.id)
- expect(json_response.map { |i| i["group_name"] }).to include(group.name)
+ expect(json_response.map { |i| i["first_milestone"]["id"] }).to match_array([group_milestone.id, project_milestone.id])
+ expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
+ end
+
+ it 'should contain group and project milestones to which the user belongs to' do
+ get :index
+
+ expect(response.body).to include("Open\n<span class=\"badge badge-pill\">3</span>")
+ expect(response.body).to include("Closed\n<span class=\"badge badge-pill\">0</span>")
end
end
end
diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb
index bf41aa0706f..99429c93b82 100644
--- a/spec/controllers/groups/boards_controller_spec.rb
+++ b/spec/controllers/groups/boards_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Groups::BoardsController do
let(:group) { create(:group) }
- let(:user) { create(:user) }
+ let(:user) { create(:user) }
before do
group.add_maintainer(user)
@@ -22,6 +22,28 @@ describe Groups::BoardsController do
expect(response.content_type).to eq 'text/html'
end
+ it 'redirects to latest visited board' do
+ board = create(:board, group: group)
+ create(:board_group_recent_visit, group: board.group, board: board, user: user)
+
+ list_boards
+
+ expect(response).to redirect_to(group_board_path(id: board.id))
+ end
+
+ it 'renders template if visited board is not found' do
+ temporary_board = create(:board, group: group)
+ visited = create(:board_group_recent_visit, group: temporary_board.group, board: temporary_board, user: user)
+ temporary_board.delete
+
+ allow_any_instance_of(Boards::Visits::LatestService).to receive(:execute).and_return(visited)
+
+ list_boards
+
+ expect(response).to render_template :index
+ expect(response.content_type).to eq 'text/html'
+ end
+
context 'with unauthorized user' do
before do
allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true)
@@ -35,12 +57,30 @@ describe Groups::BoardsController do
expect(response.content_type).to eq 'text/html'
end
end
+
+ context 'when user is signed out' do
+ let(:group) { create(:group, :public) }
+
+ it 'renders template' do
+ sign_out(user)
+
+ board = create(:board, group: group)
+ create(:board_group_recent_visit, group: board.group, board: board, user: user)
+
+ list_boards
+
+ expect(response).to render_template :index
+ expect(response.content_type).to eq 'text/html'
+ end
+ end
end
context 'when format is JSON' do
it 'return an array with one group board' do
create(:board, group: group)
+ expect(Boards::Visits::LatestService).not_to receive(:new)
+
list_boards format: :json
parsed_response = JSON.parse(response.body)
@@ -74,7 +114,7 @@ describe Groups::BoardsController do
context 'when format is HTML' do
it 'renders template' do
- read_board board: board
+ expect { read_board board: board }.to change(BoardGroupRecentVisit, :count).by(1)
expect(response).to render_template :show
expect(response.content_type).to eq 'text/html'
@@ -93,10 +133,25 @@ describe Groups::BoardsController do
expect(response.content_type).to eq 'text/html'
end
end
+
+ context 'when user is signed out' do
+ let(:group) { create(:group, :public) }
+
+ it 'does not save visit' do
+ sign_out(user)
+
+ expect { read_board board: board }.to change(BoardGroupRecentVisit, :count).by(0)
+
+ expect(response).to render_template :show
+ expect(response.content_type).to eq 'text/html'
+ end
+ end
end
context 'when format is JSON' do
it 'returns project board' do
+ expect(Boards::Visits::CreateService).not_to receive(:new)
+
read_board board: board, format: :json
expect(response).to match_response_schema('board')
diff --git a/spec/controllers/groups/clusters/applications_controller_spec.rb b/spec/controllers/groups/clusters/applications_controller_spec.rb
new file mode 100644
index 00000000000..68a798542b6
--- /dev/null
+++ b/spec/controllers/groups/clusters/applications_controller_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Groups::Clusters::ApplicationsController do
+ include AccessMatchersForController
+
+ def current_application
+ Clusters::Cluster::APPLICATIONS[application]
+ end
+
+ describe 'POST create' do
+ let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
+ let(:group) { cluster.group }
+ let(:application) { 'helm' }
+ let(:params) { { application: application, id: cluster.id } }
+
+ describe 'functionality' do
+ let(:user) { create(:user) }
+
+ before do
+ group.add_maintainer(user)
+ sign_in(user)
+ end
+
+ it 'schedule an application installation' do
+ expect(ClusterInstallAppWorker).to receive(:perform_async).with(application, anything).once
+
+ expect { go }.to change { current_application.count }
+ expect(response).to have_http_status(:no_content)
+ expect(cluster.application_helm).to be_scheduled
+ end
+
+ context 'when cluster do not exists' do
+ before do
+ cluster.destroy!
+ end
+
+ it 'return 404' do
+ expect { go }.not_to change { current_application.count }
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'when application is unknown' do
+ let(:application) { 'unkwnown-app' }
+
+ it 'return 404' do
+ go
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'when application is already installing' do
+ before do
+ create(:clusters_applications_helm, :installing, cluster: cluster)
+ end
+
+ it 'returns 400' do
+ go
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+ end
+
+ describe 'security' do
+ before do
+ allow(ClusterInstallAppWorker).to receive(:perform_async)
+ end
+
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+
+ def go
+ post :create, params.merge(group_id: group)
+ end
+ end
+end
diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb
new file mode 100644
index 00000000000..6e130f830a2
--- /dev/null
+++ b/spec/controllers/groups/clusters_controller_spec.rb
@@ -0,0 +1,574 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Groups::ClustersController do
+ include AccessMatchersForController
+ include GoogleApi::CloudPlatformHelpers
+
+ set(:group) { create(:group) }
+
+ let(:user) { create(:user) }
+
+ before do
+ group.add_maintainer(user)
+ sign_in(user)
+ end
+
+ describe 'GET index' do
+ def go(params = {})
+ get :index, params.reverse_merge(group_id: group)
+ end
+
+ context 'when feature flag is not enabled' do
+ before do
+ stub_feature_flags(group_clusters: false)
+ end
+
+ it 'renders 404' do
+ go
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when feature flag is enabled' do
+ before do
+ stub_feature_flags(group_clusters: true)
+ end
+
+ describe 'functionality' do
+ context 'when group has one or more clusters' do
+ let(:group) { create(:group) }
+
+ let!(:enabled_cluster) do
+ create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group])
+ end
+
+ let!(:disabled_cluster) do
+ create(:cluster, :disabled, :provided_by_gcp, :production_environment, cluster_type: :group_type, groups: [group])
+ end
+
+ it 'lists available clusters' do
+ go
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:index)
+ expect(assigns(:clusters)).to match_array([enabled_cluster, disabled_cluster])
+ end
+
+ context 'when page is specified' do
+ let(:last_page) { group.clusters.page.total_pages }
+
+ before do
+ allow(Clusters::Cluster).to receive(:paginates_per).and_return(1)
+ create_list(:cluster, 2, :provided_by_gcp, :production_environment, cluster_type: :group_type, groups: [group])
+ end
+
+ it 'redirects to the page' do
+ go(page: last_page)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(assigns(:clusters).current_page).to eq(last_page)
+ end
+ end
+ end
+
+ context 'when group does not have a cluster' do
+ let(:group) { create(:group) }
+
+ it 'returns an empty state page' do
+ go
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:index, partial: :empty_state)
+ expect(assigns(:clusters)).to eq([])
+ end
+ end
+ end
+ end
+
+ describe 'security' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group]) }
+
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+ end
+
+ describe 'GET new' do
+ def go
+ get :new, group_id: group
+ end
+
+ describe 'functionality for new cluster' do
+ context 'when omniauth has been configured' do
+ let(:key) { 'secret-key' }
+ let(:session_key_for_redirect_uri) do
+ GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key)
+ end
+
+ before do
+ allow(SecureRandom).to receive(:hex).and_return(key)
+ end
+
+ it 'has authorize_url' do
+ go
+
+ expect(assigns(:authorize_url)).to include(key)
+ expect(session[session_key_for_redirect_uri]).to eq(new_group_cluster_path(group))
+ end
+ end
+
+ context 'when omniauth has not configured' do
+ before do
+ stub_omniauth_setting(providers: [])
+ end
+
+ it 'does not have authorize_url' do
+ go
+
+ expect(assigns(:authorize_url)).to be_nil
+ end
+ end
+
+ context 'when access token is valid' do
+ before do
+ stub_google_api_validate_token
+ end
+
+ it 'has new object' do
+ go
+
+ expect(assigns(:gcp_cluster)).to be_an_instance_of(Clusters::ClusterPresenter)
+ end
+ end
+
+ context 'when access token is expired' do
+ before do
+ stub_google_api_expired_token
+ end
+
+ it { expect(@valid_gcp_token).to be_falsey }
+ end
+
+ context 'when access token is not stored in session' do
+ it { expect(@valid_gcp_token).to be_falsey }
+ end
+ end
+
+ describe 'functionality for existing cluster' do
+ it 'has new object' do
+ go
+
+ expect(assigns(:user_cluster)).to be_an_instance_of(Clusters::ClusterPresenter)
+ end
+ end
+
+ describe 'security' do
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+ end
+
+ describe 'POST create for new cluster' do
+ let(:legacy_abac_param) { 'true' }
+ let(:params) do
+ {
+ cluster: {
+ name: 'new-cluster',
+ provider_gcp_attributes: {
+ gcp_project_id: 'gcp-project-12345',
+ legacy_abac: legacy_abac_param
+ }
+ }
+ }
+ end
+
+ def go
+ post :create_gcp, params.merge(group_id: group)
+ end
+
+ describe 'functionality' do
+ context 'when access token is valid' do
+ before do
+ stub_google_api_validate_token
+ end
+
+ it 'creates a new cluster' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Providers::Gcp.count }
+
+ cluster = group.clusters.first
+
+ expect(response).to redirect_to(group_cluster_path(group, cluster))
+ expect(cluster).to be_gcp
+ expect(cluster).to be_kubernetes
+ expect(cluster.provider_gcp).to be_legacy_abac
+ end
+
+ context 'when legacy_abac param is false' do
+ let(:legacy_abac_param) { 'false' }
+
+ it 'creates a new cluster with legacy_abac_disabled' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Providers::Gcp.count }
+ expect(group.clusters.first.provider_gcp).not_to be_legacy_abac
+ end
+ end
+ end
+
+ context 'when access token is expired' do
+ before do
+ stub_google_api_expired_token
+ end
+
+ it { expect(@valid_gcp_token).to be_falsey }
+ end
+
+ context 'when access token is not stored in session' do
+ it { expect(@valid_gcp_token).to be_falsey }
+ end
+ end
+
+ describe 'security' do
+ before do
+ allow_any_instance_of(described_class)
+ .to receive(:token_in_session).and_return('token')
+ allow_any_instance_of(described_class)
+ .to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
+ allow_any_instance_of(GoogleApi::CloudPlatform::Client)
+ .to receive(:projects_zones_clusters_create) do
+ OpenStruct.new(
+ self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
+ status: 'RUNNING'
+ )
+ end
+
+ allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
+ end
+
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+ end
+
+ describe 'POST create for existing cluster' do
+ let(:params) do
+ {
+ cluster: {
+ name: 'new-cluster',
+ platform_kubernetes_attributes: {
+ api_url: 'http://my-url',
+ token: 'test'
+ }
+ }
+ }
+ end
+
+ def go
+ post :create_user, params.merge(group_id: group)
+ end
+
+ describe 'functionality' do
+ context 'when creates a cluster' do
+ it 'creates a new cluster' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Platforms::Kubernetes.count }
+
+ cluster = group.clusters.first
+
+ expect(response).to redirect_to(group_cluster_path(group, cluster))
+ expect(cluster).to be_user
+ expect(cluster).to be_kubernetes
+ end
+ end
+
+ context 'when creates a RBAC-enabled cluster' do
+ let(:params) do
+ {
+ cluster: {
+ name: 'new-cluster',
+ platform_kubernetes_attributes: {
+ api_url: 'http://my-url',
+ token: 'test',
+ authorization_type: 'rbac'
+ }
+ }
+ }
+ end
+
+ it 'creates a new cluster' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Platforms::Kubernetes.count }
+
+ cluster = group.clusters.first
+
+ expect(response).to redirect_to(group_cluster_path(group, cluster))
+ expect(cluster).to be_user
+ expect(cluster).to be_kubernetes
+ expect(cluster).to be_platform_kubernetes_rbac
+ end
+ end
+ end
+
+ describe 'security' do
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+ end
+
+ describe 'GET cluster_status' do
+ let(:cluster) { create(:cluster, :providing_by_gcp, cluster_type: :group_type, groups: [group]) }
+
+ def go
+ get :cluster_status,
+ group_id: group.to_param,
+ id: cluster,
+ format: :json
+ end
+
+ describe 'functionality' do
+ it 'responds with matching schema' do
+ go
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('cluster_status')
+ end
+
+ it 'invokes schedule_status_update on each application' do
+ expect_any_instance_of(Clusters::Applications::Ingress).to receive(:schedule_status_update)
+
+ go
+ end
+ end
+
+ describe 'security' do
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+ end
+
+ describe 'GET show' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group]) }
+
+ def go
+ get :show,
+ group_id: group,
+ id: cluster
+ end
+
+ describe 'functionality' do
+ it 'renders view' do
+ go
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(assigns(:cluster)).to eq(cluster)
+ end
+ end
+
+ describe 'security' do
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+ end
+
+ describe 'PUT update' do
+ def go(format: :html)
+ put :update, params.merge(
+ group_id: group.to_param,
+ id: cluster,
+ format: format
+ )
+ end
+
+ let(:cluster) { create(:cluster, :provided_by_user, cluster_type: :group_type, groups: [group]) }
+
+ let(:params) do
+ {
+ cluster: {
+ enabled: false,
+ name: 'my-new-cluster-name'
+ }
+ }
+ end
+
+ it 'updates and redirects back to show page' do
+ go
+
+ cluster.reload
+ expect(response).to redirect_to(group_cluster_path(group, cluster))
+ expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.')
+ expect(cluster.enabled).to be_falsey
+ expect(cluster.name).to eq('my-new-cluster-name')
+ end
+
+ context 'when format is json' do
+ context 'when changing parameters' do
+ context 'when valid parameters are used' do
+ let(:params) do
+ {
+ cluster: {
+ enabled: false,
+ name: 'my-new-cluster-name'
+ }
+ }
+ end
+
+ it 'updates and redirects back to show page' do
+ go(format: :json)
+
+ cluster.reload
+ expect(response).to have_http_status(:no_content)
+ expect(cluster.enabled).to be_falsey
+ expect(cluster.name).to eq('my-new-cluster-name')
+ end
+ end
+
+ context 'when invalid parameters are used' do
+ let(:params) do
+ {
+ cluster: {
+ enabled: false,
+ name: ''
+ }
+ }
+ end
+
+ it 'rejects changes' do
+ go(format: :json)
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+ end
+ end
+
+ describe 'security' do
+ set(:cluster) { create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group]) }
+
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+ end
+
+ describe 'DELETE destroy' do
+ let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, cluster_type: :group_type, groups: [group]) }
+
+ def go
+ delete :destroy,
+ group_id: group,
+ id: cluster
+ end
+
+ describe 'functionality' do
+ context 'when cluster is provided by GCP' do
+ context 'when cluster is created' do
+ it 'destroys and redirects back to clusters list' do
+ expect { go }
+ .to change { Clusters::Cluster.count }.by(-1)
+ .and change { Clusters::Platforms::Kubernetes.count }.by(-1)
+ .and change { Clusters::Providers::Gcp.count }.by(-1)
+
+ expect(response).to redirect_to(group_clusters_path(group))
+ expect(flash[:notice]).to eq('Kubernetes cluster integration was successfully removed.')
+ end
+ end
+
+ context 'when cluster is being created' do
+ let!(:cluster) { create(:cluster, :providing_by_gcp, :production_environment, cluster_type: :group_type, groups: [group]) }
+
+ it 'destroys and redirects back to clusters list' do
+ expect { go }
+ .to change { Clusters::Cluster.count }.by(-1)
+ .and change { Clusters::Providers::Gcp.count }.by(-1)
+
+ expect(response).to redirect_to(group_clusters_path(group))
+ expect(flash[:notice]).to eq('Kubernetes cluster integration was successfully removed.')
+ end
+ end
+ end
+
+ context 'when cluster is provided by user' do
+ let!(:cluster) { create(:cluster, :provided_by_user, :production_environment, cluster_type: :group_type, groups: [group]) }
+
+ it 'destroys and redirects back to clusters list' do
+ expect { go }
+ .to change { Clusters::Cluster.count }.by(-1)
+ .and change { Clusters::Platforms::Kubernetes.count }.by(-1)
+ .and change { Clusters::Providers::Gcp.count }.by(0)
+
+ expect(response).to redirect_to(group_clusters_path(group))
+ expect(flash[:notice]).to eq('Kubernetes cluster integration was successfully removed.')
+ end
+ end
+ end
+
+ describe 'security' do
+ set(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, cluster_type: :group_type, groups: [group]) }
+
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(group) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { go }.to be_denied_for(:developer).of(group) }
+ it { expect { go }.to be_denied_for(:reporter).of(group) }
+ it { expect { go }.to be_denied_for(:guest).of(group) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+ end
+
+ context 'no group_id param' do
+ it 'does not respond to any action without group_id param' do
+ expect { get :index }.to raise_error(ActionController::UrlGenerationError)
+ end
+ end
+end
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index f7068546093..42723bb3820 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -63,7 +63,7 @@ describe Groups::MilestonesController do
let(:group_milestone) { create(:milestone, group: group) }
context 'when there is a title parameter' do
- it 'searchs for a legacy group milestone' do
+ it 'searches for a legacy group milestone' do
expect(GlobalMilestone).to receive(:build)
expect(Milestone).not_to receive(:find_by_iid)
@@ -72,7 +72,7 @@ describe Groups::MilestonesController do
end
context 'when there is not a title parameter' do
- it 'searchs for a group milestone' do
+ it 'searches for a group milestone' do
expect(GlobalMilestone).not_to receive(:build)
expect(Milestone).to receive(:find_by_iid)
@@ -141,6 +141,17 @@ describe Groups::MilestonesController do
end
end
+ describe "#destroy" do
+ let(:milestone) { create(:milestone, group: group) }
+
+ it "removes milestone" do
+ delete :destroy, group_id: group.to_param, id: milestone.iid, format: :js
+
+ expect(response).to be_success
+ expect { Milestone.find(milestone.id) }.to raise_exception(ActiveRecord::RecordNotFound)
+ end
+ end
+
describe '#ensure_canonical_path' do
before do
sign_in(user)
diff --git a/spec/controllers/groups/settings/ci_cd_controller_spec.rb b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
index ea18122e0c3..06ccace8242 100644
--- a/spec/controllers/groups/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
@@ -17,4 +17,18 @@ describe Groups::Settings::CiCdController do
expect(response).to render_template(:show)
end
end
+
+ describe 'PUT #reset_registration_token' do
+ subject { put :reset_registration_token, group_id: group }
+
+ it 'resets runner registration token' do
+ expect { subject }.to change { group.reload.runners_token }
+ end
+
+ it 'redirects the user to admin runners page' do
+ subject
+
+ expect(response).to redirect_to(group_settings_ci_cd_path)
+ end
+ end
end
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 7a037828035..4de61b65f71 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -38,14 +38,6 @@ describe GroupsController do
project
end
- context 'as html' do
- it 'assigns whether or not a group has children' do
- get :show, id: group.to_param
-
- expect(assigns(:has_children)).to be_truthy
- end
- end
-
context 'as atom' do
it 'assigns events for all the projects in the group' do
create(:event, project: project)
@@ -57,6 +49,16 @@ describe GroupsController do
end
end
+ describe 'GET edit' do
+ it 'sets the badge API endpoint' do
+ sign_in(owner)
+
+ get :edit, id: group.to_param
+
+ expect(assigns(:badge_api_endpoint)).not_to be_nil
+ end
+ end
+
describe 'GET #new' do
context 'when creating subgroups', :nested_groups do
[true, false].each do |can_create_group_status|
@@ -200,8 +202,8 @@ describe GroupsController do
end
describe 'GET #issues' do
- let(:issue_1) { create(:issue, project: project) }
- let(:issue_2) { create(:issue, project: project) }
+ let(:issue_1) { create(:issue, project: project, title: 'foo') }
+ let(:issue_2) { create(:issue, project: project, title: 'bar') }
before do
create_list(:award_emoji, 3, awardable: issue_2)
@@ -222,6 +224,31 @@ describe GroupsController do
expect(assigns(:issues)).to eq [issue_2, issue_1]
end
end
+
+ context 'searching' do
+ # Remove as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/52271
+ before do
+ stub_feature_flags(use_cte_for_group_issues_search: false)
+ end
+
+ it 'works with popularity sort' do
+ get :issues, id: group.to_param, search: 'foo', sort: 'popularity'
+
+ expect(assigns(:issues)).to eq([issue_1])
+ end
+
+ it 'works with priority sort' do
+ get :issues, id: group.to_param, search: 'foo', sort: 'priority'
+
+ expect(assigns(:issues)).to eq([issue_1])
+ end
+
+ it 'works with label priority sort' do
+ get :issues, id: group.to_param, search: 'foo', sort: 'label_priority'
+
+ expect(assigns(:issues)).to eq([issue_1])
+ end
+ end
end
describe 'GET #merge_requests' do
@@ -502,7 +529,7 @@ describe GroupsController do
sign_in(user)
end
- context 'when transfering to a subgroup goes right' do
+ context 'when transferring to a subgroup goes right' do
let(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let!(:new_parent_group_member) { create(:group_member, :owner, group: new_parent_group, user: user) }
diff --git a/spec/controllers/health_controller_spec.rb b/spec/controllers/health_controller_spec.rb
index d800ad7c187..ec73c89cb11 100644
--- a/spec/controllers/health_controller_spec.rb
+++ b/spec/controllers/health_controller_spec.rb
@@ -14,48 +14,6 @@ describe HealthController do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
- describe '#storage_check' do
- before do
- allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip)
- end
-
- subject { post :storage_check }
-
- it 'checks all the configured storages' do
- expect(Gitlab::Git::Storage::Checker).to receive(:check_all).and_call_original
-
- subject
- end
-
- it 'returns the check interval' do
- stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'true')
- stub_application_setting(circuitbreaker_check_interval: 10)
-
- subject
-
- expect(json_response['check_interval']).to eq(10)
- end
-
- context 'with failing storages', :broken_storage do
- before do
- stub_storage_settings(
- broken: { path: 'tmp/tests/non-existent-repositories' }
- )
- end
-
- it 'includes the failure information' do
- subject
-
- expected_results = [
- { 'storage' => 'broken', 'success' => false },
- { 'storage' => 'default', 'success' => true }
- ]
-
- expect(json_response['results']).to eq(expected_results)
- end
- end
- end
-
describe '#readiness' do
shared_context 'endpoint responding with readiness data' do
let(:request_params) { {} }
diff --git a/spec/controllers/import/bitbucket_server_controller_spec.rb b/spec/controllers/import/bitbucket_server_controller_spec.rb
index 5024ef71771..77060fdc3be 100644
--- a/spec/controllers/import/bitbucket_server_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_server_controller_spec.rb
@@ -121,12 +121,19 @@ describe Import::BitbucketServerController do
@repo = double(slug: 'vim', project_key: 'asd', full_name: 'asd/vim', "valid?" => true, project_name: 'asd', browse_url: 'http://test', name: 'vim')
@invalid_repo = double(slug: 'invalid', project_key: 'foobar', full_name: 'asd/foobar', "valid?" => false, browse_url: 'http://bad-repo')
+ @created_repo = double(slug: 'created', project_key: 'existing', full_name: 'group/created', "valid?" => true, browse_url: 'http://existing')
assign_session_tokens
end
it 'assigns repository categories' do
- created_project = create(:project, import_type: 'bitbucket_server', creator_id: user.id, import_source: 'foo/bar', import_status: 'finished')
- expect(client).to receive(:repos).and_return([@repo, @invalid_repo])
+ created_project = create(:project, import_type: 'bitbucket_server', creator_id: user.id, import_status: 'finished', import_source: @created_repo.browse_url)
+ repos = instance_double(BitbucketServer::Collection)
+
+ expect(repos).to receive(:partition).and_return([[@repo, @created_repo], [@invalid_repo]])
+ expect(repos).to receive(:current_page).and_return(1)
+ expect(repos).to receive(:next_page).and_return(2)
+ expect(repos).to receive(:prev_page).and_return(nil)
+ expect(client).to receive(:repos).and_return(repos)
get :status
diff --git a/spec/controllers/import/gitlab_projects_controller_spec.rb b/spec/controllers/import/gitlab_projects_controller_spec.rb
index d624659bce9..cbd1a112602 100644
--- a/spec/controllers/import/gitlab_projects_controller_spec.rb
+++ b/spec/controllers/import/gitlab_projects_controller_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Import::GitlabProjectsController do
set(:namespace) { create(:namespace) }
set(:user) { namespace.owner }
- let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') }
+ let(:file) { fixture_file_upload('spec/fixtures/project_export.tar.gz', 'text/plain') }
before do
sign_in(user)
diff --git a/spec/controllers/instance_statistics/cohorts_controller_spec.rb b/spec/controllers/instance_statistics/cohorts_controller_spec.rb
index e4eedede93a..596d3c7abe5 100644
--- a/spec/controllers/instance_statistics/cohorts_controller_spec.rb
+++ b/spec/controllers/instance_statistics/cohorts_controller_spec.rb
@@ -3,5 +3,19 @@
require 'spec_helper'
describe InstanceStatistics::CohortsController do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
it_behaves_like 'instance statistics availability'
+
+ it 'renders a 404 when the usage ping is disabled' do
+ stub_application_setting(usage_ping_enabled: false)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
end
diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb
index e133950e684..a3356a86d4b 100644
--- a/spec/controllers/notification_settings_controller_spec.rb
+++ b/spec/controllers/notification_settings_controller_spec.rb
@@ -21,10 +21,11 @@ describe NotificationSettingsController do
end
context 'when authorized' do
+ let(:notification_setting) { user.notification_settings_for(source) }
let(:custom_events) do
events = {}
- NotificationSetting::EMAIL_EVENTS.each do |event|
+ NotificationSetting.email_events(source).each do |event|
events[event.to_s] = true
end
@@ -36,7 +37,7 @@ describe NotificationSettingsController do
end
context 'for projects' do
- let(:notification_setting) { user.notification_settings_for(project) }
+ let(:source) { project }
it 'creates notification setting' do
post :create,
@@ -67,7 +68,7 @@ describe NotificationSettingsController do
end
context 'for groups' do
- let(:notification_setting) { user.notification_settings_for(group) }
+ let(:source) { group }
it 'creates notification setting' do
post :create,
@@ -145,7 +146,7 @@ describe NotificationSettingsController do
let(:custom_events) do
events = {}
- NotificationSetting::EMAIL_EVENTS.each do |event|
+ notification_setting.email_events.each do |event|
events[event] = "true"
end
end
diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb
index 1195f44f37d..ace8a954e92 100644
--- a/spec/controllers/oauth/applications_controller_spec.rb
+++ b/spec/controllers/oauth/applications_controller_spec.rb
@@ -15,14 +15,44 @@ describe Oauth::ApplicationsController do
expect(response).to have_gitlab_http_status(200)
end
- it 'redirects back to profile page if OAuth applications are disabled' do
- allow(Gitlab::CurrentSettings.current_application_settings).to receive(:user_oauth_applications?).and_return(false)
+ it 'shows list of applications' do
+ disable_user_oauth
get :index
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe 'POST #create' do
+ it 'creates an application' do
+ post :create, oauth_params
+
+ expect(response).to have_gitlab_http_status(302)
+ expect(response).to redirect_to(oauth_application_path(Doorkeeper::Application.last))
+ end
+
+ it 'redirects back to profile page if OAuth applications are disabled' do
+ disable_user_oauth
+
+ post :create, oauth_params
+
expect(response).to have_gitlab_http_status(302)
expect(response).to redirect_to(profile_path)
end
end
end
+
+ def disable_user_oauth
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:user_oauth_applications?).and_return(false)
+ end
+
+ def oauth_params
+ {
+ doorkeeper_application: {
+ name: 'foo',
+ redirect_uri: 'http://example.org'
+ }
+ }
+ end
end
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index b23f183fec8..d377d69457f 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -95,7 +95,7 @@ describe OmniauthCallbacksController, type: :controller do
end
it 'allows linking the disabled provider' do
- user.identities.destroy_all
+ user.identities.destroy_all # rubocop: disable DestroyAll
sign_in(user)
expect { post provider }.to change { user.reload.identities.count }.by(1)
diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb
index 363ed410bc0..ea26bc83353 100644
--- a/spec/controllers/profiles/keys_controller_spec.rb
+++ b/spec/controllers/profiles/keys_controller_spec.rb
@@ -4,7 +4,7 @@ describe Profiles::KeysController do
let(:user) { create(:user) }
describe "#get_keys" do
- describe "non existant user" do
+ describe "non existent user" do
it "does not generally work" do
get :get_keys, username: 'not-existent'
diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
index ed08a4c1bf2..f5860d4296b 100644
--- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
+++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
@@ -39,8 +39,10 @@ describe Profiles::PersonalAccessTokensController do
let!(:active_personal_access_token) { create(:personal_access_token, user: user) }
let!(:inactive_personal_access_token) { create(:personal_access_token, :revoked, user: user) }
let!(:impersonation_personal_access_token) { create(:personal_access_token, :impersonation, user: user) }
+ let(:token_value) { 's3cr3t' }
before do
+ PersonalAccessToken.redis_store!(user.id, token_value)
get :index
end
@@ -56,5 +58,9 @@ describe Profiles::PersonalAccessTokensController do
expect(assigns(:active_personal_access_tokens)).not_to include(impersonation_personal_access_token)
expect(assigns(:inactive_personal_access_tokens)).not_to include(impersonation_personal_access_token)
end
+
+ it "retrieves newly created personal access token value" do
+ expect(assigns(:new_personal_access_token)).to eql(token_value)
+ end
end
end
diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb
index 4ea6f869aa3..b3c8d6a954e 100644
--- a/spec/controllers/projects/artifacts_controller_spec.rb
+++ b/spec/controllers/projects/artifacts_controller_spec.rb
@@ -19,10 +19,67 @@ describe Projects::ArtifactsController do
end
describe 'GET download' do
- it 'sends the artifacts file' do
- expect(controller).to receive(:send_file).with(job.artifacts_file.path, hash_including(disposition: 'attachment')).and_call_original
+ def download_artifact(extra_params = {})
+ params = { namespace_id: project.namespace, project_id: project, job_id: job }.merge(extra_params)
- get :download, namespace_id: project.namespace, project_id: project, job_id: job
+ get :download, params
+ end
+
+ context 'when no file type is supplied' do
+ it 'sends the artifacts file' do
+ expect(controller).to receive(:send_file).with(job.artifacts_file.path, hash_including(disposition: 'attachment')).and_call_original
+
+ download_artifact
+ end
+ end
+
+ context 'when a file type is supplied' do
+ context 'when an invalid file type is supplied' do
+ let(:file_type) { 'invalid' }
+
+ it 'returns 404' do
+ download_artifact(file_type: file_type)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when codequality file type is supplied' do
+ let(:file_type) { 'codequality' }
+
+ context 'when file is stored locally' do
+ before do
+ create(:ci_job_artifact, :codequality, job: job)
+ end
+
+ it 'sends the codequality report' do
+ expect(controller).to receive(:send_file).with(job.job_artifacts_codequality.file.path, hash_including(disposition: 'attachment')).and_call_original
+
+ download_artifact(file_type: file_type)
+ end
+ end
+
+ context 'when file is stored remotely' do
+ before do
+ stub_artifacts_object_storage
+ create(:ci_job_artifact, :remote_store, :codequality, job: job)
+ end
+
+ it 'sends the codequality report' do
+ expect(controller).to receive(:redirect_to).and_call_original
+
+ download_artifact(file_type: file_type)
+ end
+
+ context 'when proxied' do
+ it 'sends the codequality report' do
+ expect(Gitlab::Workhorse).to receive(:send_url).and_call_original
+
+ download_artifact(file_type: file_type, proxy: true)
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index 17c9a61f339..14059cff74c 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -1,24 +1,55 @@
require 'spec_helper'
describe Projects::AvatarsController do
- let(:project) { create(:project, :repository, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
- let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
before do
- sign_in(user)
- project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
- it 'GET #show' do
- get :show, namespace_id: project.namespace.id, project_id: project.id
+ describe 'GET #show' do
+ subject { get :show, namespace_id: project.namespace, project_id: project }
- expect(response).to have_gitlab_http_status(404)
+ context 'when repository has no avatar' do
+ it 'shows 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when repository has an avatar' do
+ before do
+ allow(project).to receive(:avatar_in_git).and_return(filepath)
+ end
+
+ context 'when the avatar is stored in the repository' do
+ let(:filepath) { 'files/images/logo-white.png' }
+
+ it 'sends the avatar' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Type']).to eq('image/png')
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
+ end
+ end
+
+ context 'when the avatar is stored in lfs' do
+ it_behaves_like 'repository lfs file load' do
+ let(:filename) { 'lfs_object.iso' }
+ let(:filepath) { "files/lfs/#{filename}" }
+ end
+ end
+ end
end
- it 'removes avatar from DB by calling destroy' do
- delete :destroy, namespace_id: project.namespace.id, project_id: project.id
- expect(project.avatar.present?).to be_falsey
- expect(project).to be_valid
+ describe 'DELETE #destroy' do
+ it 'removes avatar from DB by calling destroy' do
+ delete :destroy, namespace_id: project.namespace.id, project_id: project.id
+
+ expect(project.avatar.present?).to be_falsey
+ expect(project).to be_valid
+ end
end
end
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 32cd7c6e70a..5fdf7f1229d 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -157,7 +157,7 @@ describe Projects::BlobController do
match_line = JSON.parse(response.body).first
- expect(match_line['type']).to eq('context')
+ expect(match_line['type']).to be_nil
end
it 'adds bottom match line when "t"o is less than blob size' do
@@ -177,7 +177,7 @@ describe Projects::BlobController do
match_line = JSON.parse(response.body).last
- expect(match_line['type']).to eq('context')
+ expect(match_line['type']).to be_nil
end
end
end
@@ -343,4 +343,76 @@ describe Projects::BlobController do
end
end
end
+
+ describe 'DELETE destroy' do
+ let(:user) { create(:user) }
+ let(:project_root_path) { project_tree_path(project, 'master') }
+
+ before do
+ project.add_maintainer(user)
+
+ sign_in(user)
+ end
+
+ context 'for a file in a subdirectory' do
+ let(:default_params) do
+ {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: 'master/files/whitespace',
+ original_branch: 'master',
+ branch_name: 'master',
+ commit_message: 'Delete whitespace'
+ }
+ end
+
+ let(:after_delete_path) { project_tree_path(project, 'master/files') }
+
+ it 'redirects to the sub directory' do
+ delete :destroy, default_params
+
+ expect(response).to redirect_to(after_delete_path)
+ end
+ end
+
+ context 'if deleted file is the last one in a subdirectory' do
+ let(:default_params) do
+ {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: 'master/bar/branch-test.txt',
+ original_branch: 'master',
+ branch_name: 'master',
+ commit_message: 'Delete whitespace'
+ }
+ end
+
+ it 'redirects to the project root' do
+ delete :destroy, default_params
+
+ expect(response).to redirect_to(project_root_path)
+ end
+
+ context 'when deleting a file in a branch other than master' do
+ let(:default_params) do
+ {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: 'binary-encoding/foo/bar/.gitkeep',
+ original_branch: 'binary-encoding',
+ branch_name: 'binary-encoding',
+ commit_message: 'Delete whitespace'
+ }
+ end
+
+ let(:after_delete_path) { project_tree_path(project, 'binary-encoding') }
+
+ it 'redirects to the project root of the branch' do
+ delete :destroy, default_params
+
+ expect(response).to redirect_to(after_delete_path)
+ end
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb
index 096efc1c7b2..8d503f6ad32 100644
--- a/spec/controllers/projects/boards_controller_spec.rb
+++ b/spec/controllers/projects/boards_controller_spec.rb
@@ -28,6 +28,28 @@ describe Projects::BoardsController do
expect(response.content_type).to eq 'text/html'
end
+ it 'redirects to latest visited board' do
+ board = create(:board, project: project)
+ create(:board_project_recent_visit, project: board.project, board: board, user: user)
+
+ list_boards
+
+ expect(response).to redirect_to(namespace_project_board_path(id: board.id))
+ end
+
+ it 'renders template if visited board is not found' do
+ temporary_board = create(:board, project: project)
+ visited = create(:board_project_recent_visit, project: temporary_board.project, board: temporary_board, user: user)
+ temporary_board.delete
+
+ allow_any_instance_of(Boards::Visits::LatestService).to receive(:execute).and_return(visited)
+
+ list_boards
+
+ expect(response).to render_template :index
+ expect(response.content_type).to eq 'text/html'
+ end
+
context 'with unauthorized user' do
before do
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
@@ -41,12 +63,30 @@ describe Projects::BoardsController do
expect(response.content_type).to eq 'text/html'
end
end
+
+ context 'when user is signed out' do
+ let(:project) { create(:project, :public) }
+
+ it 'renders template' do
+ sign_out(user)
+
+ board = create(:board, project: project)
+ create(:board_project_recent_visit, project: board.project, board: board, user: user)
+
+ list_boards
+
+ expect(response).to render_template :index
+ expect(response.content_type).to eq 'text/html'
+ end
+ end
end
context 'when format is JSON' do
it 'returns a list of project boards' do
create_list(:board, 2, project: project)
+ expect(Boards::Visits::LatestService).not_to receive(:new)
+
list_boards format: :json
parsed_response = JSON.parse(response.body)
@@ -98,7 +138,7 @@ describe Projects::BoardsController do
context 'when format is HTML' do
it 'renders template' do
- read_board board: board
+ expect { read_board board: board }.to change(BoardProjectRecentVisit, :count).by(1)
expect(response).to render_template :show
expect(response.content_type).to eq 'text/html'
@@ -117,10 +157,25 @@ describe Projects::BoardsController do
expect(response.content_type).to eq 'text/html'
end
end
+
+ context 'when user is signed out' do
+ let(:project) { create(:project, :public) }
+
+ it 'does not save visit' do
+ sign_out(user)
+
+ expect { read_board board: board }.to change(BoardProjectRecentVisit, :count).by(0)
+
+ expect(response).to render_template :show
+ expect(response.content_type).to eq 'text/html'
+ end
+ end
end
context 'when format is JSON' do
it 'returns project board' do
+ expect(Boards::Visits::CreateService).not_to receive(:new)
+
read_board board: board, format: :json
expect(response).to match_response_schema('board')
diff --git a/spec/controllers/projects/clusters/applications_controller_spec.rb b/spec/controllers/projects/clusters/applications_controller_spec.rb
index 9e17e392d3d..8106453a775 100644
--- a/spec/controllers/projects/clusters/applications_controller_spec.rb
+++ b/spec/controllers/projects/clusters/applications_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Projects::Clusters::ApplicationsController do
diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb
index 42917d0d505..483222363bb 100644
--- a/spec/controllers/projects/clusters_controller_spec.rb
+++ b/spec/controllers/projects/clusters_controller_spec.rb
@@ -1,20 +1,27 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Projects::ClustersController do
include AccessMatchersForController
include GoogleApi::CloudPlatformHelpers
+ include KubernetesHelpers
set(:project) { create(:project) }
- describe 'GET index' do
- describe 'functionality' do
- let(:user) { create(:user) }
+ let(:user) { create(:user) }
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+ describe 'GET index' do
+ def go(params = {})
+ get :index, params.reverse_merge(namespace_id: project.namespace.to_param, project_id: project)
+ end
+
+ describe 'functionality' do
context 'when project has one or more clusters' do
let(:project) { create(:project) }
let!(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
@@ -33,10 +40,11 @@ describe Projects::ClustersController do
before do
allow(Clusters::Cluster).to receive(:paginates_per).and_return(1)
create_list(:cluster, 2, :provided_by_gcp, :production_environment, projects: [project])
- get :index, namespace_id: project.namespace, project_id: project, page: last_page
end
it 'redirects to the page' do
+ go(page: last_page)
+
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:clusters).current_page).to eq(last_page)
end
@@ -68,21 +76,14 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
+ end
+ describe 'GET new' do
def go
- get :index, namespace_id: project.namespace.to_param, project_id: project
+ get :new, namespace_id: project.namespace, project_id: project
end
- end
- describe 'GET new' do
describe 'functionality for new cluster' do
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
context 'when omniauth has been configured' do
let(:key) { 'secret-key' }
let(:session_key_for_redirect_uri) do
@@ -121,7 +122,7 @@ describe Projects::ClustersController do
it 'has new object' do
go
- expect(assigns(:gcp_cluster)).to be_an_instance_of(Clusters::Cluster)
+ expect(assigns(:gcp_cluster)).to be_an_instance_of(Clusters::ClusterPresenter)
end
end
@@ -139,17 +140,10 @@ describe Projects::ClustersController do
end
describe 'functionality for existing cluster' do
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
it 'has new object' do
go
- expect(assigns(:user_cluster)).to be_an_instance_of(Clusters::Cluster)
+ expect(assigns(:user_cluster)).to be_an_instance_of(Clusters::ClusterPresenter)
end
end
@@ -163,32 +157,27 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
-
- def go
- get :new, namespace_id: project.namespace, project_id: project
- end
end
describe 'POST create for new cluster' do
+ let(:legacy_abac_param) { 'true' }
let(:params) do
{
cluster: {
name: 'new-cluster',
provider_gcp_attributes: {
- gcp_project_id: 'gcp-project-12345'
+ gcp_project_id: 'gcp-project-12345',
+ legacy_abac: legacy_abac_param
}
}
}
end
- describe 'functionality' do
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
+ def go
+ post :create_gcp, params.merge(namespace_id: project.namespace, project_id: project)
+ end
+ describe 'functionality' do
context 'when access token is valid' do
before do
stub_google_api_validate_token
@@ -201,6 +190,18 @@ describe Projects::ClustersController do
expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
expect(project.clusters.first).to be_gcp
expect(project.clusters.first).to be_kubernetes
+ expect(project.clusters.first.provider_gcp).to be_legacy_abac
+ end
+
+ context 'when legacy_abac param is false' do
+ let(:legacy_abac_param) { 'false' }
+
+ it 'creates a new cluster with legacy_abac_disabled' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Providers::Gcp.count }
+ expect(project.clusters.first.provider_gcp).not_to be_legacy_abac
+ end
end
end
@@ -220,9 +221,9 @@ describe Projects::ClustersController do
describe 'security' do
before do
allow_any_instance_of(described_class)
- .to receive(:token_in_session).and_return('token')
+ .to receive(:token_in_session).and_return('token')
allow_any_instance_of(described_class)
- .to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
+ .to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
.to receive(:projects_zones_clusters_create) do
OpenStruct.new(
@@ -243,10 +244,6 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
-
- def go
- post :create_gcp, params.merge(namespace_id: project.namespace, project_id: project)
- end
end
describe 'POST create for existing cluster' do
@@ -263,27 +260,61 @@ describe Projects::ClustersController do
}
end
+ def go
+ post :create_user, params.merge(namespace_id: project.namespace, project_id: project)
+ end
+
describe 'functionality' do
- let(:user) { create(:user) }
+ context 'when creates a cluster' do
+ it 'creates a new cluster' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
- before do
- project.add_maintainer(user)
- sign_in(user)
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Platforms::Kubernetes.count }
+
+ expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
+
+ expect(project.clusters.first).to be_user
+ expect(project.clusters.first).to be_kubernetes
+ end
end
- context 'when creates a cluster' do
+ context 'when creates a RBAC-enabled cluster' do
+ let(:params) do
+ {
+ cluster: {
+ name: 'new-cluster',
+ platform_kubernetes_attributes: {
+ api_url: 'http://my-url',
+ token: 'test',
+ namespace: 'aaa',
+ authorization_type: 'rbac'
+ }
+ }
+ }
+ end
+
it 'creates a new cluster' do
expect(ClusterProvisionWorker).to receive(:perform_async)
+
expect { go }.to change { Clusters::Cluster.count }
.and change { Clusters::Platforms::Kubernetes.count }
+
expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
+
expect(project.clusters.first).to be_user
expect(project.clusters.first).to be_kubernetes
+ expect(project.clusters.first).to be_platform_kubernetes_rbac
end
end
end
describe 'security' do
+ before do
+ allow(ClusterPlatformConfigureWorker).to receive(:perform_async)
+ stub_kubeclient_get_namespace('https://kubernetes.example.com', namespace: 'my-namespace')
+ end
+
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
it { expect { go }.to be_allowed_for(:maintainer).of(project) }
@@ -293,23 +324,20 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
-
- def go
- post :create_user, params.merge(namespace_id: project.namespace, project_id: project)
- end
end
- describe 'GET status' do
+ describe 'GET cluster_status' do
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
- describe 'functionality' do
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
+ def go
+ get :cluster_status,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: cluster,
+ format: :json
+ end
+ describe 'functionality' do
it "responds with matching schema" do
go
@@ -334,26 +362,19 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
-
- def go
- get :status, namespace_id: project.namespace,
- project_id: project,
- id: cluster,
- format: :json
- end
end
describe 'GET show' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
- describe 'functionality' do
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
+ def go
+ get :show,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: cluster
+ end
+ describe 'functionality' do
it "renders view" do
go
@@ -372,145 +393,89 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
-
- def go
- get :show, namespace_id: project.namespace,
- project_id: project,
- id: cluster
- end
end
describe 'PUT update' do
- context 'when cluster is provided by GCP' do
- let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
- context 'when changing parameters' do
- let(:params) do
- {
- cluster: {
- enabled: false,
- name: 'my-new-cluster-name',
- platform_kubernetes_attributes: {
- namespace: 'my-namespace'
- }
- }
- }
- end
-
- it "updates and redirects back to show page" do
- go
-
- cluster.reload
- expect(response).to redirect_to(project_cluster_path(project, cluster))
- expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.')
- expect(cluster.enabled).to be_falsey
- end
-
- it "does not change cluster name" do
- go
-
- expect(cluster.name).to eq('test-cluster')
- end
+ def go(format: :html)
+ put :update, params.merge(namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: cluster,
+ format: format
+ )
+ end
- context 'when cluster is being created' do
- let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
+ before do
+ allow(ClusterPlatformConfigureWorker).to receive(:perform_async)
+ stub_kubeclient_get_namespace('https://kubernetes.example.com', namespace: 'my-namespace')
+ end
- it "rejects changes" do
- go
+ let(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template(:show)
- expect(cluster.enabled).to be_truthy
- end
- end
- end
+ let(:params) do
+ {
+ cluster: {
+ enabled: false,
+ name: 'my-new-cluster-name',
+ platform_kubernetes_attributes: {
+ namespace: 'my-namespace'
+ }
+ }
+ }
end
- context 'when cluster is provided by user' do
- let(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
- let(:user) { create(:user) }
+ it "updates and redirects back to show page" do
+ go
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
+ cluster.reload
+ expect(response).to redirect_to(project_cluster_path(project, cluster))
+ expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.')
+ expect(cluster.enabled).to be_falsey
+ expect(cluster.name).to eq('my-new-cluster-name')
+ expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
+ end
- context 'when format is json' do
- context 'when changing parameters' do
- context 'when valid parameters are used' do
- let(:params) do
- {
- cluster: {
- enabled: false,
- name: 'my-new-cluster-name',
- platform_kubernetes_attributes: {
- namespace: 'my-namespace'
- }
+ context 'when format is json' do
+ context 'when changing parameters' do
+ context 'when valid parameters are used' do
+ let(:params) do
+ {
+ cluster: {
+ enabled: false,
+ name: 'my-new-cluster-name',
+ platform_kubernetes_attributes: {
+ namespace: 'my-namespace'
}
}
- end
-
- it "updates and redirects back to show page" do
- go_json
-
- cluster.reload
- expect(response).to have_http_status(:no_content)
- expect(cluster.enabled).to be_falsey
- expect(cluster.name).to eq('my-new-cluster-name')
- expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
- end
+ }
end
- context 'when invalid parameters are used' do
- let(:params) do
- {
- cluster: {
- enabled: false,
- platform_kubernetes_attributes: {
- namespace: 'my invalid namespace #@'
- }
- }
- }
- end
-
- it "rejects changes" do
- go_json
+ it "updates and redirects back to show page" do
+ go(format: :json)
- expect(response).to have_http_status(:bad_request)
- end
+ cluster.reload
+ expect(response).to have_http_status(:no_content)
+ expect(cluster.enabled).to be_falsey
+ expect(cluster.name).to eq('my-new-cluster-name')
+ expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
end
end
- end
- context 'when format is html' do
- context 'when update enabled' do
+ context 'when invalid parameters are used' do
let(:params) do
{
cluster: {
enabled: false,
- name: 'my-new-cluster-name',
platform_kubernetes_attributes: {
- namespace: 'my-namespace'
+ namespace: 'my invalid namespace #@'
}
}
}
end
- it "updates and redirects back to show page" do
- go
+ it "rejects changes" do
+ go(format: :json)
- cluster.reload
- expect(response).to redirect_to(project_cluster_path(project, cluster))
- expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.')
- expect(cluster.enabled).to be_falsey
- expect(cluster.name).to eq('my-new-cluster-name')
- expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
+ expect(response).to have_http_status(:bad_request)
end
end
end
@@ -519,10 +484,6 @@ describe Projects::ClustersController do
describe 'security' do
set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
- let(:params) do
- { cluster: { enabled: false } }
- end
-
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
it { expect { go }.to be_allowed_for(:maintainer).of(project) }
@@ -532,34 +493,21 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
-
- def go
- put :update, params.merge(namespace_id: project.namespace,
- project_id: project,
- id: cluster)
- end
-
- def go_json
- put :update, params.merge(namespace_id: project.namespace,
- project_id: project,
- id: cluster,
- format: :json)
- end
end
describe 'DELETE destroy' do
- describe 'functionality' do
- let(:user) { create(:user) }
+ let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
+ def go
+ delete :destroy,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: cluster
+ end
+ describe 'functionality' do
context 'when cluster is provided by GCP' do
context 'when cluster is created' do
- let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
-
it "destroys and redirects back to clusters list" do
expect { go }
.to change { Clusters::Cluster.count }.by(-1)
@@ -612,11 +560,11 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
+ end
- def go
- delete :destroy, namespace_id: project.namespace,
- project_id: project,
- id: cluster
+ context 'no project_id param' do
+ it 'does not respond to any action without project_id param' do
+ expect { get :index }.to raise_error(ActionController::UrlGenerationError)
end
end
end
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 916a4be2567..e34fdee62d6 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -230,7 +230,7 @@ describe Projects::CommitController do
id: master_pickable_commit.id)
expect(response).to redirect_to project_commits_path(project, 'master')
- expect(flash[:notice]).to eq('The commit has been successfully cherry-picked.')
+ expect(flash[:notice]).to eq('The commit has been successfully cherry-picked into master.')
end
end
@@ -356,6 +356,7 @@ describe Projects::CommitController do
expect(response).to be_ok
expect(JSON.parse(response.body)['pipelines']).not_to be_empty
expect(JSON.parse(response.body)['count']['all']).to eq 1
+ expect(response).to include_pagination_headers
end
end
end
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index 8695aa826bb..17883d0fadd 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -97,6 +97,30 @@ describe Projects::CompareController do
expect(assigns(:commits)).to eq([])
end
end
+
+ context 'when the target ref is invalid' do
+ let(:target_ref) { "master%' AND 2554=4423 AND '%'='" }
+ let(:source_ref) { "improve%2Fawesome" }
+
+ it 'shows a flash message and redirects' do
+ show_request
+
+ expect(flash[:alert]).to eq('Invalid branch name')
+ expect(response).to have_http_status(302)
+ end
+ end
+
+ context 'when the source ref is invalid' do
+ let(:source_ref) { "master%' AND 2554=4423 AND '%'='" }
+ let(:target_ref) { "improve%2Fawesome" }
+
+ it 'shows a flash message and redirects' do
+ show_request
+
+ expect(flash[:alert]).to eq('Invalid branch name')
+ expect(response).to have_http_status(302)
+ end
+ end
end
describe 'GET diff_for_path' do
diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb
index 73bf169085f..4567a51b88e 100644
--- a/spec/controllers/projects/deploy_keys_controller_spec.rb
+++ b/spec/controllers/projects/deploy_keys_controller_spec.rb
@@ -27,12 +27,8 @@ describe Projects::DeployKeysController do
let(:project2) { create(:project, :internal)}
let(:project_private) { create(:project, :private)}
- let(:deploy_key_internal) do
- create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com')
- end
- let(:deploy_key_actual) do
- create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com')
- end
+ let(:deploy_key_internal) { create(:deploy_key) }
+ let(:deploy_key_actual) { create(:deploy_key) }
let!(:deploy_key_public) { create(:deploy_key, public: true) }
let!(:deploy_keys_project_internal) do
@@ -63,4 +59,145 @@ describe Projects::DeployKeysController do
end
end
end
+
+ describe '/enable/:id' do
+ let(:deploy_key) { create(:deploy_key) }
+ let(:project2) { create(:project) }
+ let!(:deploy_keys_project_internal) do
+ create(:deploy_keys_project, project: project2, deploy_key: deploy_key)
+ end
+
+ context 'with anonymous user' do
+ before do
+ sign_out(:user)
+ end
+
+ it 'redirects to login' do
+ expect do
+ put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
+ end.not_to change { DeployKeysProject.count }
+
+ expect(response).to have_http_status(302)
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context 'with user with no permission' do
+ before do
+ sign_in(create(:user))
+ end
+
+ it 'returns 404' do
+ expect do
+ put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
+ end.not_to change { DeployKeysProject.count }
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'with user with permission' do
+ before do
+ project2.add_maintainer(user)
+ end
+
+ it 'returns 302' do
+ expect do
+ put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
+ end.to change { DeployKeysProject.count }.by(1)
+
+ expect(DeployKeysProject.where(project_id: project.id, deploy_key_id: deploy_key.id).count).to eq(1)
+ expect(response).to have_http_status(302)
+ expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
+ end
+
+ it 'returns 404' do
+ put :enable, id: 0, namespace_id: project.namespace, project_id: project
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'with admin' do
+ before do
+ sign_in(create(:admin))
+ end
+
+ it 'returns 302' do
+ expect do
+ put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
+ end.to change { DeployKeysProject.count }.by(1)
+
+ expect(DeployKeysProject.where(project_id: project.id, deploy_key_id: deploy_key.id).count).to eq(1)
+ expect(response).to have_http_status(302)
+ expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
+ end
+ end
+ end
+
+ describe '/disable/:id' do
+ let(:deploy_key) { create(:deploy_key) }
+ let!(:deploy_key_project) { create(:deploy_keys_project, project: project, deploy_key: deploy_key) }
+
+ context 'with anonymous user' do
+ before do
+ sign_out(:user)
+ end
+
+ it 'redirects to login' do
+ put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
+
+ expect(response).to have_http_status(302)
+ expect(response).to redirect_to(new_user_session_path)
+ expect(DeployKey.find(deploy_key.id)).to eq(deploy_key)
+ end
+ end
+
+ context 'with user with no permission' do
+ before do
+ sign_in(create(:user))
+ end
+
+ it 'returns 404' do
+ put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
+
+ expect(response).to have_http_status(404)
+ expect(DeployKey.find(deploy_key.id)).to eq(deploy_key)
+ end
+ end
+
+ context 'with user with permission' do
+ it 'returns 302' do
+ put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
+
+ expect(response).to have_http_status(302)
+ expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
+
+ expect { DeployKey.find(deploy_key.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'returns 404' do
+ put :disable, id: 0, namespace_id: project.namespace, project_id: project
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'with admin' do
+ before do
+ sign_in(create(:admin))
+ end
+
+ it 'returns 302' do
+ expect do
+ put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
+ end.to change { DeployKey.count }.by(-1)
+
+ expect(response).to have_http_status(302)
+ expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
+
+ expect { DeployKey.find(deploy_key.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb
index d1c960e895d..5b7da81b6a1 100644
--- a/spec/controllers/projects/deployments_controller_spec.rb
+++ b/spec/controllers/projects/deployments_controller_spec.rb
@@ -15,9 +15,9 @@ describe Projects::DeploymentsController do
describe 'GET #index' do
it 'returns list of deployments from last 8 hours' do
- create(:deployment, environment: environment, created_at: 9.hours.ago)
- create(:deployment, environment: environment, created_at: 7.hours.ago)
- create(:deployment, environment: environment)
+ create(:deployment, :success, environment: environment, created_at: 9.hours.ago)
+ create(:deployment, :success, environment: environment, created_at: 7.hours.ago)
+ create(:deployment, :success, environment: environment)
get :index, deployment_params(after: 8.hours.ago)
@@ -27,7 +27,7 @@ describe Projects::DeploymentsController do
end
it 'returns a list with deployments information' do
- create(:deployment, environment: environment)
+ create(:deployment, :success, environment: environment)
get :index, deployment_params
@@ -37,7 +37,7 @@ describe Projects::DeploymentsController do
end
describe 'GET #metrics' do
- let(:deployment) { create(:deployment, project: project, environment: environment) }
+ let(:deployment) { create(:deployment, :success, project: project, environment: environment) }
before do
allow(controller).to receive(:deployment).and_return(deployment)
@@ -110,7 +110,7 @@ describe Projects::DeploymentsController do
end
describe 'GET #additional_metrics' do
- let(:deployment) { create(:deployment, project: project, environment: environment) }
+ let(:deployment) { create(:deployment, :success, project: project, environment: environment) }
before do
allow(controller).to receive(:deployment).and_return(deployment)
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index b86029a4baf..bc17331f531 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -216,7 +216,7 @@ describe Projects::EnvironmentsController do
expect(response).to have_gitlab_http_status(200)
end
- it 'loads the terminals for the enviroment' do
+ it 'loads the terminals for the environment' do
expect_any_instance_of(Environment).to receive(:terminals)
get :terminal, environment_params
diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb
index 0f3033b0933..7d3a8c3d0d3 100644
--- a/spec/controllers/projects/hooks_controller_spec.rb
+++ b/spec/controllers/projects/hooks_controller_spec.rb
@@ -30,6 +30,7 @@ describe Projects::HooksController do
tag_push_events: true,
merge_requests_events: true,
issues_events: true,
+ confidential_note_events: true,
confidential_issues_events: true,
note_events: true,
job_events: true,
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 5b347b1109a..80138183c07 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -637,6 +637,18 @@ describe Projects::IssuesController do
project_id: project,
id: id
end
+
+ it 'avoids (most) N+1s loading labels', :request_store do
+ label = create(:label, project: project).to_reference
+ labels = create_list(:label, 10, project: project).map(&:to_reference)
+ issue = create(:issue, project: project, description: 'Test issue')
+
+ control_count = ActiveRecord::QueryRecorder.new { issue.update(description: [issue.description, label].join(' ')) }.count
+
+ # Follow-up to get rid of this `2 * label.count` requirement: https://gitlab.com/gitlab-org/gitlab-ce/issues/52230
+ expect { issue.update(description: [issue.description, labels].join(' ')) }
+ .not_to exceed_query_limit(control_count + 2 * labels.count)
+ end
end
describe 'GET #realtime_changes' do
@@ -1016,6 +1028,13 @@ describe Projects::IssuesController do
.not_to exceed_query_limit(control)
end
+ context 'when user is setting notes filters' do
+ let(:issuable) { issue }
+ let!(:discussion_note) { create(:discussion_note_on_issue, :system, noteable: issuable, project: project) }
+
+ it_behaves_like 'issuable notes filter'
+ end
+
context 'with cross-reference system note', :request_store do
let(:new_issue) { create(:issue) }
let(:cross_reference) { "mentioned in #{new_issue.to_reference(issue.project)}" }
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 1aca44c6e74..da3d658d061 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -86,7 +86,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
def create_job(name, status)
pipeline = create(:ci_pipeline, project: project)
create(:ci_build, :tags, :triggered, :artifacts,
- pipeline: pipeline, name: name, status: status)
+ pipeline: pipeline, name: name, status: status)
end
end
@@ -147,12 +147,273 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
get_show(id: job.id, format: :json)
end
- it 'exposes needed information' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['raw_path']).to match(%r{jobs/\d+/raw\z})
- expect(json_response.dig('merge_request', 'path')).to match(%r{merge_requests/\d+\z})
- expect(json_response['new_issue_path'])
- .to include('/issues/new')
+ context 'when job failed' do
+ it 'exposes needed information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['raw_path']).to match(%r{jobs/\d+/raw\z})
+ expect(json_response['merge_request']['path']).to match(%r{merge_requests/\d+\z})
+ expect(json_response['new_issue_path']).to include('/issues/new')
+ end
+ end
+
+ context 'when job is running' do
+ context 'job is cancelable' do
+ let(:job) { create(:ci_build, :running, pipeline: pipeline) }
+
+ it 'cancel_path is present with correct redirect' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['cancel_path']).to include(CGI.escape(json_response['build_path']))
+ end
+ end
+
+ context 'with web terminal' do
+ let(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }
+
+ it 'exposes the terminal path' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['terminal_path']).to match(%r{/terminal})
+ end
+ end
+ end
+
+ context 'when job has artifacts' do
+ context 'with not expiry date' do
+ let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
+
+ it 'exposes needed information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['artifact']['download_path']).to match(%r{artifacts/download})
+ expect(json_response['artifact']['browse_path']).to match(%r{artifacts/browse})
+ expect(json_response['artifact']).not_to have_key('expired')
+ expect(json_response['artifact']).not_to have_key('expired_at')
+ end
+ end
+
+ context 'with expiry date' do
+ let(:job) { create(:ci_build, :success, :artifacts, :expired, pipeline: pipeline) }
+
+ it 'exposes needed information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['artifact']).not_to have_key('download_path')
+ expect(json_response['artifact']).not_to have_key('browse_path')
+ expect(json_response['artifact']['expired']).to eq(true)
+ expect(json_response['artifact']['expire_at']).not_to be_empty
+ end
+ end
+ end
+
+ context 'when job passed with no trace' do
+ let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
+
+ it 'exposes empty state illustrations' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['status']['illustration']).to have_key('image')
+ expect(json_response['status']['illustration']).to have_key('size')
+ expect(json_response['status']['illustration']).to have_key('title')
+ end
+ end
+
+ context 'with no deployment' do
+ let(:job) { create(:ci_build, :success, pipeline: pipeline) }
+
+ it 'does not exposes the deployment information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['deployment_status']).to be_nil
+ end
+ end
+
+ context 'with deployment' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:environment) { create(:environment, project: project, name: 'staging', state: :available) }
+ let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
+
+ it 'exposes the deployment information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to match_schema('job/job_details')
+ expect(json_response['deployment_status']["status"]).to eq 'creating'
+ expect(json_response['deployment_status']["environment"]).not_to be_nil
+ end
+ end
+
+ context 'when user can edit runner' do
+ context 'that belongs to the project' do
+ let(:runner) { create(:ci_runner, :project, projects: [project]) }
+ let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ get_show(id: job.id, format: :json)
+ end
+
+ it 'user can edit runner' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['runner']).to have_key('edit_path')
+ end
+ end
+
+ context 'that belongs to group' do
+ let(:group) { create(:group) }
+ let(:runner) { create(:ci_runner, :group, groups: [group]) }
+ let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }
+ let(:user) { create(:user, :admin) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ get_show(id: job.id, format: :json)
+ end
+
+ it 'user can not edit runner' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['runner']).not_to have_key('edit_path')
+ end
+ end
+
+ context 'that belongs to instance' do
+ let(:runner) { create(:ci_runner, :instance) }
+ let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }
+ let(:user) { create(:user, :admin) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ get_show(id: job.id, format: :json)
+ end
+
+ it 'user can not edit runner' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['runner']).not_to have_key('edit_path')
+ end
+ end
+ end
+
+ context 'when no runners are available' do
+ let(:runner) { create(:ci_runner, :instance, active: false) }
+ let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner) }
+
+ it 'exposes needed information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['runners']['online']).to be false
+ expect(json_response['runners']['available']).to be false
+ expect(json_response['stuck']).to be true
+ end
+ end
+
+ context 'when no runner is online' do
+ let(:runner) { create(:ci_runner, :instance) }
+ let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner) }
+
+ it 'exposes needed information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['runners']['online']).to be false
+ expect(json_response['runners']['available']).to be true
+ expect(json_response['stuck']).to be true
+ end
+ end
+
+ context 'settings_path' do
+ context 'when user is developer' do
+ it 'settings_path is not available' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['runners']).not_to have_key('settings_path')
+ end
+ end
+
+ context 'when user is maintainer' do
+ let(:user) { create(:user, :admin) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ it 'settings_path is available' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['runners']['settings_path']).to match(/runners/)
+ end
+ end
+ end
+
+ context 'when no trace is available' do
+ it 'has_trace is false' do
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['has_trace']).to be false
+ end
+ end
+
+ context 'when job has trace' do
+ let(:job) { create(:ci_build, :running, :trace_live, pipeline: pipeline) }
+
+ it "has_trace is true" do
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['has_trace']).to be true
+ end
+ end
+
+ it 'exposes the stage the job belongs to' do
+ expect(json_response['stage']).to eq('test')
+ end
+ end
+
+ context 'when requesting JSON job is triggered' do
+ let!(:merge_request) { create(:merge_request, source_project: project) }
+ let(:trigger) { create(:ci_trigger, project: project) }
+ let(:trigger_request) { create(:ci_trigger_request, pipeline: pipeline, trigger: trigger) }
+ let(:job) { create(:ci_build, pipeline: pipeline, trigger_request: trigger_request) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+
+ allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request)
+ end
+
+ context 'with no variables' do
+ before do
+ get_show(id: job.id, format: :json)
+ end
+
+ it 'exposes trigger information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['trigger']['short_token']).to eq 'toke'
+ expect(json_response['trigger']['variables'].length).to eq 0
+ end
+ end
+
+ context 'with variables' do
+ before do
+ create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1')
+
+ get_show(id: job.id, format: :json)
+ end
+
+ it 'exposes trigger information and variables' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['trigger']['short_token']).to eq 'toke'
+ expect(json_response['trigger']['variables'].length).to eq 1
+ expect(json_response['trigger']['variables'].first['key']).to eq "TRIGGER_KEY_1"
+ expect(json_response['trigger']['variables'].first['value']).to eq "TRIGGER_VALUE_1"
+ expect(json_response['trigger']['variables'].first['public']).to eq false
+ end
end
end
@@ -356,35 +617,108 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
before do
project.add_developer(user)
sign_in(user)
-
- post_cancel
end
- context 'when job is cancelable' do
+ context 'when continue url is present' do
let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
- it 'redirects to the canceled job page' do
+ context 'when continue to is a safe url' do
+ let(:url) { '/test' }
+
+ before do
+ post_cancel(continue: { to: url })
+ end
+
+ it 'redirects to the continue url' do
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to(url)
+ end
+
+ it 'transits to canceled' do
+ expect(job.reload).to be_canceled
+ end
+ end
+
+ context 'when continue to is not a safe url' do
+ let(:url) { 'http://example.com' }
+
+ it 'raises an error' do
+ expect { cancel_with_redirect(url) }.to raise_error
+ end
+ end
+ end
+
+ context 'when continue url is not present' do
+ before do
+ post_cancel
+ end
+
+ context 'when job is cancelable' do
+ let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
+
+ it 'redirects to the builds page' do
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to(builds_namespace_project_pipeline_path(id: pipeline.id))
+ end
+
+ it 'transits to canceled' do
+ expect(job.reload).to be_canceled
+ end
+ end
+
+ context 'when job is not cancelable' do
+ let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
+
+ it 'returns unprocessable_entity' do
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
+ end
+ end
+
+ def post_cancel(additional_params = {})
+ post :cancel, { namespace_id: project.namespace,
+ project_id: project,
+ id: job.id }.merge(additional_params)
+ end
+ end
+
+ describe 'POST unschedule' do
+ before do
+ project.add_developer(user)
+
+ create(:protected_branch, :developers_can_merge,
+ name: 'master', project: project)
+
+ sign_in(user)
+
+ post_unschedule
+ end
+
+ context 'when job is scheduled' do
+ let(:job) { create(:ci_build, :scheduled, pipeline: pipeline) }
+
+ it 'redirects to the unscheduled job page' do
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(namespace_project_job_path(id: job.id))
end
- it 'transits to canceled' do
- expect(job.reload).to be_canceled
+ it 'transits to manual' do
+ expect(job.reload).to be_manual
end
end
- context 'when job is not cancelable' do
- let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
+ context 'when job is not scheduled' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
- it 'returns unprocessable_entity' do
+ it 'renders unprocessable_entity' do
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
- def post_cancel
- post :cancel, namespace_id: project.namespace,
- project_id: project,
- id: job.id
+ def post_unschedule
+ post :unschedule, namespace_id: project.namespace,
+ project_id: project,
+ id: job.id
end
end
diff --git a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
index 397cc79bde4..1e1ea9a7144 100644
--- a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
@@ -150,7 +150,6 @@ describe Projects::MergeRequests::ConflictsController do
'new_path' => path,
'blob_icon' => 'file-text-o',
'blob_path' => a_string_ending_with(path),
- 'blob_ace_mode' => 'ruby',
'content' => content)
end
end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index d8995f98575..f8c37c0a676 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -29,6 +29,55 @@ describe Projects::MergeRequests::CreationsController do
expect(response).to be_success
end
end
+
+ context 'merge request with some commits' do
+ render_views
+
+ let(:large_diff_params) do
+ {
+ namespace_id: fork_project.namespace.to_param,
+ project_id: fork_project,
+ merge_request: {
+ source_branch: 'master',
+ target_branch: 'fix'
+ }
+ }
+ end
+
+ describe 'with artificial limits' do
+ before do
+ # Load MergeRequestdiff so stub_const won't override it with its own definition
+ # See https://github.com/rspec/rspec-mocks/issues/1079
+ stub_const("#{MergeRequestDiff}::COMMITS_SAFE_SIZE", 2)
+ end
+
+ it 'limits total commits' do
+ get :new, large_diff_params
+
+ expect(response).to be_success
+
+ total = assigns(:total_commit_count)
+ expect(assigns(:commits)).to be_an Array
+ expect(total).to be > 0
+ expect(assigns(:hidden_commit_count)).to be > 0
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to match %r(<span class="commits-count">2 commits</span>)
+ end
+ end
+
+ it 'shows total commits' do
+ get :new, large_diff_params
+
+ expect(response).to be_success
+
+ total = assigns(:total_commit_count)
+ expect(assigns(:commits)).to be_an Array
+ expect(total).to be > 0
+ expect(assigns(:hidden_commit_count)).to eq(0)
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to match %r(<span class="commits-count">#{total} commits</span>)
+ end
+ end
end
describe 'GET diffs' do
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index d9bb3981539..e62523c65c9 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -76,28 +76,6 @@ describe Projects::MergeRequestsController do
expect(response).to be_success
end
- context "loads notes" do
- let(:first_contributor) { create(:user) }
- let(:contributor) { create(:user) }
- let(:merge_request) { create(:merge_request, author: first_contributor, target_project: project, source_project: project) }
- let(:contributor_merge_request) { create(:merge_request, :merged, author: contributor, target_project: project, source_project: project) }
- # the order here is important
- # as the controller reloads these from DB, references doesn't correspond after
- let!(:first_contributor_note) { create(:note, author: first_contributor, noteable: merge_request, project: project) }
- let!(:contributor_note) { create(:note, author: contributor, noteable: merge_request, project: project) }
- let!(:owner_note) { create(:note, author: user, noteable: merge_request, project: project) }
-
- it "with special_role FIRST_TIME_CONTRIBUTOR" do
- go(format: :html)
-
- notes = assigns(:notes)
- expect(notes).to match(a_collection_containing_exactly(an_object_having_attributes(special_role: Note::SpecialRole::FIRST_TIME_CONTRIBUTOR),
- an_object_having_attributes(special_role: nil),
- an_object_having_attributes(special_role: nil)
- ))
- end
- end
-
context "that is invalid" do
let(:merge_request) { create(:invalid_merge_request, target_project: project, source_project: project) }
@@ -109,6 +87,14 @@ describe Projects::MergeRequestsController do
end
end
+ context 'when user is setting notes filters' do
+ let(:issuable) { merge_request }
+ let!(:discussion_note) { create(:discussion_note_on_merge_request, :system, noteable: issuable, project: project) }
+ let!(:discussion_comment) { create(:discussion_note_on_merge_request, noteable: issuable, project: project) }
+
+ it_behaves_like 'issuable notes filter'
+ end
+
describe 'as json' do
context 'with basic serializer param' do
it 'renders basic MR entity as json' do
@@ -577,6 +563,7 @@ describe Projects::MergeRequestsController do
it 'responds with serialized pipelines' do
expect(json_response['pipelines']).not_to be_empty
expect(json_response['count']['all']).to eq 1
+ expect(response).to include_pagination_headers
end
end
@@ -763,24 +750,69 @@ describe Projects::MergeRequestsController do
describe 'GET ci_environments_status' do
context 'the environment is from a forked project' do
- let!(:forked) { fork_project(project, user, repository: true) }
- let!(:environment) { create(:environment, project: forked) }
- let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') }
- let(:admin) { create(:admin) }
+ let(:forked) { fork_project(project, user, repository: true) }
+ let(:sha) { forked.commit.sha }
+ let(:environment) { create(:environment, project: forked) }
+ let(:pipeline) { create(:ci_pipeline, sha: sha, project: forked) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let!(:deployment) { create(:deployment, :succeed, environment: environment, sha: sha, ref: 'master', deployable: build) }
let(:merge_request) do
- create(:merge_request, source_project: forked, target_project: project)
+ create(:merge_request, source_project: forked, target_project: project, target_branch: 'master', head_pipeline: pipeline)
end
- before do
- get :ci_environments_status,
+ it 'links to the environment on that project' do
+ get_ci_environments_status
+
+ expect(json_response.first['url']).to match /#{forked.full_path}/
+ end
+
+ context "when environment_target is 'merge_commit'" do
+ it 'returns nothing' do
+ get_ci_environments_status(environment_target: 'merge_commit')
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to be_empty
+ end
+
+ context 'when is merged' do
+ let(:source_environment) { create(:environment, project: project) }
+ let(:merge_commit_sha) { project.repository.merge(user, forked.commit.id, merge_request, "merged in test") }
+ let(:post_merge_pipeline) { create(:ci_pipeline, sha: merge_commit_sha, project: project) }
+ let(:post_merge_build) { create(:ci_build, pipeline: post_merge_pipeline) }
+ let!(:source_deployment) { create(:deployment, :succeed, environment: source_environment, sha: merge_commit_sha, ref: 'master', deployable: post_merge_build) }
+
+ before do
+ merge_request.update!(merge_commit_sha: merge_commit_sha)
+ merge_request.mark_as_merged!
+ end
+
+ it 'returns the environment on the source project' do
+ get_ci_environments_status(environment_target: 'merge_commit')
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.first['url']).to match /#{project.full_path}/
+ end
+ end
+ end
+
+ # we're trying to reduce the overall number of queries for this method.
+ # set a hard limit for now. https://gitlab.com/gitlab-org/gitlab-ce/issues/52287
+ it 'keeps queries in check' do
+ control_count = ActiveRecord::QueryRecorder.new { get_ci_environments_status }.count
+
+ expect(control_count).to be <= 137
+ end
+
+ def get_ci_environments_status(extra_params = {})
+ params = {
namespace_id: merge_request.project.namespace.to_param,
project_id: merge_request.project,
- id: merge_request.iid, format: 'json'
- end
+ id: merge_request.iid,
+ format: 'json'
+ }
- it 'links to the environment on that project' do
- expect(json_response.first['url']).to match /#{forked.full_path}/
+ get :ci_environments_status, params.merge(extra_params)
end
end
end
@@ -850,12 +882,14 @@ describe Projects::MergeRequestsController do
end
context 'with a forked project' do
- let(:fork_project) { create(:project, :repository, forked_from_project: project) }
- let(:fork_owner) { fork_project.owner }
+ let(:forked_project) { fork_project(project, fork_owner, repository: true) }
+ let(:fork_owner) { create(:user) }
before do
- merge_request.update!(source_project: fork_project)
- fork_project.add_reporter(user)
+ project.add_developer(fork_owner)
+
+ merge_request.update!(source_project: forked_project)
+ forked_project.add_reporter(user)
end
context 'user cannot push to source branch' do
@@ -885,4 +919,18 @@ describe Projects::MergeRequestsController do
end
end
end
+
+ describe 'GET edit' do
+ it 'responds successfully' do
+ get :edit, namespace_id: project.namespace, project_id: project, id: merge_request
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ it 'assigns the noteable to make sure autocompletes work' do
+ get :edit, namespace_id: project.namespace, project_id: project, id: merge_request
+
+ expect(assigns(:noteable)).not_to be_nil
+ end
+ end
end
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index 3190f1ce9d4..ccd4fc4db3a 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::MilestonesController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:milestone) { create(:milestone, project: project) }
let(:issue) { create(:issue, project: project, milestone: milestone) }
diff --git a/spec/controllers/projects/mirrors_controller_spec.rb b/spec/controllers/projects/mirrors_controller_spec.rb
index 6114eef7003..00c1e617e3a 100644
--- a/spec/controllers/projects/mirrors_controller_spec.rb
+++ b/spec/controllers/projects/mirrors_controller_spec.rb
@@ -63,6 +63,69 @@ describe Projects::MirrorsController do
end
end
+ describe '#ssh_host_keys', :use_clean_rails_memory_store_caching do
+ let(:project) { create(:project) }
+ let(:cache) { SshHostKey.new(project: project, url: "ssh://example.com:22") }
+
+ before do
+ sign_in(project.owner)
+ end
+
+ context 'invalid URLs' do
+ %w[
+ INVALID
+ git@example.com:foo/bar.git
+ ssh://git@example.com:foo/bar.git
+ ].each do |url|
+ it "returns an error with a 400 response for URL #{url.inspect}" do
+ do_get(project, url)
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response).to eq('message' => 'Invalid URL')
+ end
+ end
+ end
+
+ context 'no data in cache' do
+ it 'requests the cache to be filled and returns a 204 response' do
+ expect(ReactiveCachingWorker).to receive(:perform_async).with(cache.class, cache.id).at_least(:once)
+
+ do_get(project)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+ end
+
+ context 'error in the cache' do
+ it 'returns the error with a 400 response' do
+ stub_reactive_cache(cache, error: 'An error')
+
+ do_get(project)
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response).to eq('message' => 'An error')
+ end
+ end
+
+ context 'data in the cache' do
+ let(:ssh_key) { 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf' }
+ let(:ssh_fp) { { type: 'ed25519', bits: 256, fingerprint: '2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16', index: 0 } }
+
+ it 'returns the data with a 200 response' do
+ stub_reactive_cache(cache, known_hosts: ssh_key)
+
+ do_get(project)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to eq('known_hosts' => ssh_key, 'fingerprints' => [ssh_fp.stringify_keys], 'host_keys_changed' => true)
+ end
+ end
+
+ def do_get(project, url = 'ssh://example.com')
+ get :ssh_host_keys, namespace_id: project.namespace, project_id: project, ssh_url: url
+ end
+ end
+
def do_put(project, options, extra_attrs = {})
attrs = extra_attrs.merge(namespace_id: project.namespace.to_param, project_id: project.to_param)
attrs[:project] = options
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 1458113b90c..9ac7b8ee8a8 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -47,6 +47,37 @@ describe Projects::NotesController do
get :index, request_params
end
+ context 'when user notes_filter is present' do
+ let(:notes_json) { parsed_response[:notes] }
+ let!(:comment) { create(:note, noteable: issue, project: project) }
+ let!(:system_note) { create(:note, noteable: issue, project: project, system: true) }
+
+ it 'filters system notes by comments' do
+ user.set_notes_filter(UserPreference::NOTES_FILTERS[:only_comments], issue)
+
+ get :index, request_params
+
+ expect(notes_json.count).to eq(1)
+ expect(notes_json.first[:id].to_i).to eq(comment.id)
+ end
+
+ it 'returns all notes' do
+ user.set_notes_filter(UserPreference::NOTES_FILTERS[:all_notes], issue)
+
+ get :index, request_params
+
+ expect(notes_json.map { |note| note[:id].to_i }).to contain_exactly(comment.id, system_note.id)
+ end
+
+ it 'does not merge label event notes' do
+ user.set_notes_filter(UserPreference::NOTES_FILTERS[:only_comments], issue)
+
+ expect(ResourceEvents::MergeIntoNotesService).not_to receive(:new)
+
+ get :index, request_params
+ end
+ end
+
context 'for a discussion note' do
let(:project) { create(:project, :repository) }
let!(:note) { create(:discussion_note_on_merge_request, project: project) }
@@ -154,7 +185,7 @@ describe Projects::NotesController do
get :index, request_params
expect(parsed_response[:notes].count).to eq(1)
- expect(note_json[:id]).to eq(note.id)
+ expect(note_json[:id]).to eq(note.id.to_s)
end
it 'does not result in N+1 queries' do
@@ -207,6 +238,14 @@ describe Projects::NotesController do
expect(response).to have_gitlab_http_status(200)
end
+ it 'returns discussion JSON when the return_discussion param is set' do
+ post :create, request_params.merge(format: :json, return_discussion: 'true')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to have_key 'discussion'
+ expect(json_response['discussion']['notes'][0]['note']).to eq(request_params[:note][:note])
+ end
+
context 'when merge_request_diff_head_sha present' do
before do
service_params = {
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index d89716b1b50..5c7415a318d 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -90,6 +90,11 @@ describe Projects::PipelinesController do
context 'when performing gitaly calls', :request_store do
it 'limits the Gitaly requests' do
+ # Isolate from test preparation (Repository#exists? is also cached in RequestStore)
+ RequestStore.end!
+ RequestStore.clear!
+ RequestStore.begin!
+
expect { get_pipelines_index_json }
.to change { Gitlab::GitalyClient.get_request_count }.by(2)
end
@@ -193,14 +198,34 @@ describe Projects::PipelinesController do
context 'when accessing existing stage' do
before do
+ create(:ci_build, :retried, :failed, pipeline: pipeline, stage: 'build')
create(:ci_build, pipeline: pipeline, stage: 'build')
+ end
+
+ context 'without retried' do
+ before do
+ get_stage('build')
+ end
- get_stage('build')
+ it 'returns pipeline jobs without the retried builds' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('pipeline_stage')
+ expect(json_response['latest_statuses'].length).to eq 1
+ expect(json_response).not_to have_key('retried')
+ end
end
- it 'returns html source for stage dropdown' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('pipeline_stage')
+ context 'with retried' do
+ before do
+ get_stage('build', retried: true)
+ end
+
+ it 'returns pipelines jobs with the retried builds' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('pipeline_stage')
+ expect(json_response['latest_statuses'].length).to eq 1
+ expect(json_response['retried'].length).to eq 1
+ end
end
end
@@ -214,12 +239,13 @@ describe Projects::PipelinesController do
end
end
- def get_stage(name)
- get :stage, namespace_id: project.namespace,
- project_id: project,
- id: pipeline.id,
- stage: name,
- format: :json
+ def get_stage(name, params = {})
+ get :stage, **params.merge(
+ namespace_id: project.namespace,
+ project_id: project,
+ id: pipeline.id,
+ stage: name,
+ format: :json)
end
end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index c3468536ae1..6b658bf5295 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -1,14 +1,21 @@
require 'spec_helper'
describe Projects::RawController do
- let(:public_project) { create(:project, :public, :repository) }
+ let(:project) { create(:project, :public, :repository) }
+
+ describe 'GET #show' do
+ subject do
+ get(:show,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: filepath)
+ end
- describe '#show' do
context 'regular filename' do
- let(:id) { 'master/README.md' }
+ let(:filepath) { 'master/README.md' }
it 'delivers ASCII file' do
- get_show(public_project, id)
+ subject
expect(response).to have_gitlab_http_status(200)
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
@@ -19,10 +26,10 @@ describe Projects::RawController do
end
context 'image header' do
- let(:id) { 'master/files/images/6049019_460s.jpg' }
+ let(:filepath) { 'master/files/images/6049019_460s.jpg' }
it 'sets image content type header' do
- get_show(public_project, id)
+ subject
expect(response).to have_gitlab_http_status(200)
expect(response.header['Content-Type']).to eq('image/jpeg')
@@ -30,85 +37,9 @@ describe Projects::RawController do
end
end
- context 'lfs object' do
- let(:id) { 'be93687/files/lfs/lfs_object.iso' }
- let!(:lfs_object) { create(:lfs_object, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
-
- context 'when lfs is enabled' do
- before do
- allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(true)
- end
-
- context 'when project has access' do
- before do
- public_project.lfs_objects << lfs_object
- allow_any_instance_of(LfsObjectUploader).to receive(:exists?).and_return(true)
- allow(controller).to receive(:send_file) { controller.head :ok }
- end
-
- it 'serves the file' do
- expect(controller).to receive(:send_file).with("#{LfsObjectUploader.root}/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", filename: 'lfs_object.iso', disposition: 'attachment')
- get_show(public_project, id)
-
- expect(response).to have_gitlab_http_status(200)
- end
-
- context 'and lfs uses object storage' do
- let(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
-
- before do
- stub_lfs_object_storage
- lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
- end
-
- it 'responds with redirect to file' do
- get_show(public_project, id)
-
- expect(response).to have_gitlab_http_status(302)
- expect(response.location).to include(lfs_object.reload.file.path)
- end
-
- it 'sets content disposition' do
- get_show(public_project, id)
-
- file_uri = URI.parse(response.location)
- params = CGI.parse(file_uri.query)
-
- expect(params["response-content-disposition"].first).to eq 'attachment;filename="lfs_object.iso"'
- end
- end
- end
-
- context 'when project does not have access' do
- it 'does not serve the file' do
- get_show(public_project, id)
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
- end
-
- context 'when lfs is not enabled' do
- before do
- allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(false)
- end
-
- it 'delivers ASCII file' do
- get_show(public_project, id)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.header['Content-Disposition'])
- .to eq('inline')
- expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- end
- end
+ it_behaves_like 'repository lfs file load' do
+ let(:filename) { 'lfs_object.iso' }
+ let(:filepath) { "be93687/files/lfs/#{filename}" }
end
end
-
- def get_show(project, id)
- get(:show, namespace_id: project.namespace.to_param,
- project_id: project,
- id: id)
- end
end
diff --git a/spec/controllers/projects/registry/repositories_controller_spec.rb b/spec/controllers/projects/registry/repositories_controller_spec.rb
index 17769a14def..d11e42b411b 100644
--- a/spec/controllers/projects/registry/repositories_controller_spec.rb
+++ b/spec/controllers/projects/registry/repositories_controller_spec.rb
@@ -86,9 +86,10 @@ describe Projects::Registry::RepositoriesController do
stub_container_registry_tags(repository: :any, tags: [])
end
- it 'deletes a repository' do
- expect { delete_repository(repository) }.to change { ContainerRepository.all.count }.by(-1)
+ it 'schedules a job to delete a repository' do
+ expect(DeleteContainerRepositoryWorker).to receive(:perform_async).with(user.id, repository.id)
+ delete_repository(repository)
expect(response).to have_gitlab_http_status(:no_content)
end
end
diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb
index fc1619acec6..20a6beb3df8 100644
--- a/spec/controllers/projects/releases_controller_spec.rb
+++ b/spec/controllers/projects/releases_controller_spec.rb
@@ -14,7 +14,7 @@ describe Projects::ReleasesController do
describe 'GET #edit' do
it 'initializes a new release' do
tag_id = release.tag
- project.releases.destroy_all
+ project.releases.destroy_all # rubocop: disable DestroyAll
get :edit, namespace_id: project.namespace, project_id: project, tag_id: tag_id
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index 1f14a0cc381..4629929f9af 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -74,6 +74,19 @@ describe Projects::Settings::CiCdController do
end
end
+ describe 'PUT #reset_registration_token' do
+ subject { put :reset_registration_token, namespace_id: project.namespace, project_id: project }
+ it 'resets runner registration token' do
+ expect { subject }.to change { project.reload.runners_token }
+ end
+
+ it 'redirects the user to admin runners page' do
+ subject
+
+ expect(response).to redirect_to(namespace_project_settings_ci_cd_path)
+ end
+ end
+
describe 'PATCH update' do
let(:params) { { ci_config_path: '' } }
diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb
index 325ee53aafb..9802e4d5b1e 100644
--- a/spec/controllers/projects/uploads_controller_spec.rb
+++ b/spec/controllers/projects/uploads_controller_spec.rb
@@ -18,6 +18,20 @@ describe Projects::UploadsController do
end
end
+ context "when exception occurs" do
+ before do
+ allow(FileUploader).to receive(:workhorse_authorize).and_raise(SocketError.new)
+ sign_in(create(:user))
+ end
+
+ it "responds with status internal_server_error" do
+ post_authorize
+
+ expect(response).to have_gitlab_http_status(500)
+ expect(response.body).to eq('Error uploading file')
+ end
+ end
+
def post_authorize(verified: true)
request.headers.merge!(workhorse_internal_api_request_header) if verified
diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb
index 30623b6cb3d..6d75152857b 100644
--- a/spec/controllers/projects/wikis_controller_spec.rb
+++ b/spec/controllers/projects/wikis_controller_spec.rb
@@ -22,20 +22,18 @@ describe Projects::WikisController do
subject { get :show, namespace_id: project.namespace, project_id: project, id: wiki_title }
- context 'when page content encoding is invalid' do
- it 'limits the retrieved pages for the sidebar' do
- expect(controller).to receive(:load_wiki).and_return(project_wiki)
+ it 'limits the retrieved pages for the sidebar' do
+ expect(controller).to receive(:load_wiki).and_return(project_wiki)
- # empty? call
- expect(project_wiki).to receive(:pages).with(limit: 1).and_call_original
- # Sidebar entries
- expect(project_wiki).to receive(:pages).with(limit: 15).and_call_original
+ # empty? call
+ expect(project_wiki).to receive(:pages).with(limit: 1).and_call_original
+ # Sidebar entries
+ expect(project_wiki).to receive(:pages).with(limit: 15).and_call_original
- subject
+ subject
- expect(response).to have_http_status(:ok)
- expect(response.body).to include(wiki_title)
- end
+ expect(response).to have_http_status(:ok)
+ expect(response.body).to include(wiki_title)
end
context 'when page content encoding is invalid' do
@@ -48,6 +46,42 @@ describe Projects::WikisController do
expect(flash[:notice]).to eq 'The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.'
end
end
+
+ context 'when page is a file' do
+ include WikiHelpers
+
+ let(:path) { upload_file_to_wiki(project, user, file_name) }
+
+ before do
+ subject
+ end
+
+ subject { get :show, namespace_id: project.namespace, project_id: project, id: path }
+
+ context 'when file is an image' do
+ let(:file_name) { 'dk.png' }
+
+ it 'renders the content inline' do
+ expect(response.headers['Content-Disposition']).to match(/^inline/)
+ end
+
+ context 'when file is a svg' do
+ let(:file_name) { 'unsanitized.svg' }
+
+ it 'renders the content as an attachment' do
+ expect(response.headers['Content-Disposition']).to match(/^attachment/)
+ end
+ end
+ end
+
+ context 'when file is a pdf' do
+ let(:file_name) { 'git-cheat-sheet.pdf' }
+
+ it 'sets the content type to application/octet-stream' do
+ expect(response.headers['Content-Type']).to eq 'application/octet-stream'
+ end
+ end
+ end
end
describe 'POST #preview_markdown' do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 94644b1f9fd..3bc9cbe64c5 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -284,6 +284,19 @@ describe ProjectsController do
end
end
+ describe 'GET edit' do
+ it 'sets the badge API endpoint' do
+ sign_in(user)
+ project.add_maintainer(user)
+
+ get :edit,
+ namespace_id: project.namespace.path,
+ id: project.path
+
+ expect(assigns(:badge_api_endpoint)).not_to be_nil
+ end
+ end
+
describe "#update" do
render_views
@@ -790,37 +803,7 @@ describe ProjectsController do
project.add_maintainer(user)
end
- context 'object storage disabled' do
- before do
- stub_feature_flags(import_export_object_storage: false)
- end
-
- context 'when project export is enabled' do
- it 'returns 302' do
- get :download_export, namespace_id: project.namespace, id: project
-
- expect(response).to have_gitlab_http_status(302)
- end
- end
-
- context 'when project export is disabled' do
- before do
- stub_application_setting(project_export_enabled?: false)
- end
-
- it 'returns 404' do
- get :download_export, namespace_id: project.namespace, id: project
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
- end
-
context 'object storage enabled' do
- before do
- stub_feature_flags(import_export_object_storage: true)
- end
-
context 'when project export is enabled' do
it 'returns 302' do
get :download_export, namespace_id: project.namespace, id: project
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 898f3863008..d334a2ff566 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -49,7 +49,7 @@ describe RegistrationsController do
end
it 'displays an error when the reCAPTCHA is not solved' do
- # Without this, `verify_recaptcha` arbitraily returns true in test env
+ # Without this, `verify_recaptcha` arbitrarily returns true in test env
Recaptcha.configuration.skip_verify_env.delete('test')
post(:create, user_params)
diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb
index 7688538a468..995f803d757 100644
--- a/spec/controllers/root_controller_spec.rb
+++ b/spec/controllers/root_controller_spec.rb
@@ -98,7 +98,7 @@ describe RootController do
it 'redirects to their assigned issues' do
get :index
- expect(response).to redirect_to issues_dashboard_path(assignee_id: user.id)
+ expect(response).to redirect_to issues_dashboard_path(assignee_username: user.username)
end
end
@@ -110,7 +110,7 @@ describe RootController do
it 'redirects to their assigned merge requests' do
get :index
- expect(response).to redirect_to merge_requests_dashboard_path(assignee_id: user.id)
+ expect(response).to redirect_to merge_requests_dashboard_path(assignee_username: user.username)
end
end
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 8e25b61e2f1..c691b3f478b 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -89,7 +89,7 @@ describe SessionsController do
end
it 'displays an error when the reCAPTCHA is not solved' do
- # Without this, `verify_recaptcha` arbitraily returns true in test env
+ # Without this, `verify_recaptcha` arbitrarily returns true in test env
Recaptcha.configuration.skip_verify_env.delete('test')
counter = double(:counter)
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index bcf289f36a9..6420b70a54f 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -5,6 +5,17 @@ shared_examples 'content not cached without revalidation' do
end
end
+shared_examples 'content not cached without revalidation and no-store' do
+ it 'ensures content will not be cached without revalidation' do
+ # Fixed in newer versions of ActivePack, it will only output a single `private`.
+ if Gitlab.rails5?
+ expect(subject['Cache-Control']).to eq('max-age=0, private, must-revalidate, no-store')
+ else
+ expect(subject['Cache-Control']).to eq('max-age=0, private, must-revalidate, private, no-store')
+ end
+ end
+end
+
describe UploadsController do
let!(:user) { create(:user, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
@@ -177,7 +188,7 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(200)
end
- it_behaves_like 'content not cached without revalidation' do
+ it_behaves_like 'content not cached without revalidation and no-store' do
subject do
get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png'
@@ -239,7 +250,7 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(200)
end
- it_behaves_like 'content not cached without revalidation' do
+ it_behaves_like 'content not cached without revalidation and no-store' do
subject do
get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png'
@@ -292,7 +303,7 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(200)
end
- it_behaves_like 'content not cached without revalidation' do
+ it_behaves_like 'content not cached without revalidation and no-store' do
subject do
get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png'
@@ -344,7 +355,7 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(200)
end
- it_behaves_like 'content not cached without revalidation' do
+ it_behaves_like 'content not cached without revalidation and no-store' do
subject do
get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png'
@@ -388,7 +399,7 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(200)
end
- it_behaves_like 'content not cached without revalidation' do
+ it_behaves_like 'content not cached without revalidation and no-store' do
subject do
get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png'
@@ -445,7 +456,7 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(200)
end
- it_behaves_like 'content not cached without revalidation' do
+ it_behaves_like 'content not cached without revalidation and no-store' do
subject do
get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png'
@@ -498,7 +509,7 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(200)
end
- it_behaves_like 'content not cached without revalidation' do
+ it_behaves_like 'content not cached without revalidation and no-store' do
subject do
get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png'
diff --git a/spec/db/development/import_common_metrics_spec.rb b/spec/db/development/import_common_metrics_spec.rb
new file mode 100644
index 00000000000..25061ef0887
--- /dev/null
+++ b/spec/db/development/import_common_metrics_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Import metrics on development seed' do
+ subject { load Rails.root.join('db', 'fixtures', 'development', '99_common_metrics.rb') }
+
+ it "imports all prometheus metrics" do
+ expect(PrometheusMetric.common).to be_empty
+
+ subject
+
+ expect(PrometheusMetric.common).not_to be_empty
+ end
+end
diff --git a/spec/db/importers/common_metrics_importer_spec.rb b/spec/db/importers/common_metrics_importer_spec.rb
new file mode 100644
index 00000000000..68260820958
--- /dev/null
+++ b/spec/db/importers/common_metrics_importer_spec.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require Rails.root.join("db", "importers", "common_metrics_importer.rb")
+
+describe Importers::PrometheusMetric do
+ it 'group enum equals ::PrometheusMetric' do
+ expect(described_class.groups).to eq(::PrometheusMetric.groups)
+ end
+
+ it 'GROUP_TITLES equals ::PrometheusMetric' do
+ expect(described_class::GROUP_TITLES).to eq(::PrometheusMetric::GROUP_TITLES)
+ end
+end
+
+describe Importers::CommonMetricsImporter do
+ subject { described_class.new }
+
+ context "does import common_metrics.yml" do
+ let(:groups) { subject.content }
+ let(:metrics) { groups.map { |group| group['metrics'] }.flatten }
+ let(:queries) { metrics.map { |group| group['queries'] }.flatten }
+ let(:query_ids) { queries.map { |query| query['id'] } }
+
+ before do
+ subject.execute
+ end
+
+ it "has the same amount of groups" do
+ expect(PrometheusMetric.common.group(:group).count.count).to eq(groups.count)
+ end
+
+ it "has the same amount of metrics" do
+ expect(PrometheusMetric.common.group(:group, :title).count.count).to eq(metrics.count)
+ end
+
+ it "has the same amount of queries" do
+ expect(PrometheusMetric.common.count).to eq(queries.count)
+ end
+
+ it "does not have duplicate IDs" do
+ expect(query_ids).to eq(query_ids.uniq)
+ end
+
+ it "imports all IDs" do
+ expect(PrometheusMetric.common.pluck(:identifier)).to contain_exactly(*query_ids)
+ end
+ end
+
+ context "does import common_metrics.yml" do
+ it "when executed from outside of the Rails.root" do
+ Dir.chdir(Dir.tmpdir) do
+ expect { subject.execute }.not_to raise_error
+ end
+
+ expect(PrometheusMetric.common).not_to be_empty
+ end
+ end
+
+ context 'does import properly all fields' do
+ let(:query_identifier) { 'response-metric' }
+ let(:group) do
+ {
+ group: 'Response metrics (NGINX Ingress)',
+ metrics: [{
+ title: "Throughput",
+ y_label: "Requests / Sec",
+ queries: [{
+ id: query_identifier,
+ query_range: 'my-query',
+ unit: 'my-unit',
+ label: 'status code'
+ }]
+ }]
+ }
+ end
+
+ before do
+ expect(subject).to receive(:content) { [group.deep_stringify_keys] }
+ end
+
+ shared_examples 'stores metric' do
+ let(:metric) { PrometheusMetric.find_by(identifier: query_identifier) }
+
+ it 'with all data' do
+ expect(metric.group).to eq('nginx_ingress')
+ expect(metric.title).to eq('Throughput')
+ expect(metric.y_label).to eq('Requests / Sec')
+ expect(metric.unit).to eq('my-unit')
+ expect(metric.legend).to eq('status code')
+ expect(metric.query).to eq('my-query')
+ end
+ end
+
+ context 'if ID is missing' do
+ let(:query_identifier) { }
+
+ it 'raises exception' do
+ expect { subject.execute }.to raise_error(described_class::MissingQueryId)
+ end
+ end
+
+ context 'for existing common metric with different ID' do
+ let!(:existing_metric) { create(:prometheus_metric, :common, identifier: 'my-existing-metric') }
+
+ before do
+ subject.execute
+ end
+
+ it_behaves_like 'stores metric' do
+ it 'and existing metric is not changed' do
+ expect(metric).not_to eq(existing_metric)
+ end
+ end
+ end
+
+ context 'when metric with ID exists ' do
+ let!(:existing_metric) { create(:prometheus_metric, :common, identifier: 'response-metric') }
+
+ before do
+ subject.execute
+ end
+
+ it_behaves_like 'stores metric' do
+ it 'and existing metric is changed' do
+ expect(metric).to eq(existing_metric)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/db/production/import_common_metrics_spec.rb b/spec/db/production/import_common_metrics_spec.rb
new file mode 100644
index 00000000000..1e4ff818a86
--- /dev/null
+++ b/spec/db/production/import_common_metrics_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Import metrics on production seed' do
+ subject { load Rails.root.join('db', 'fixtures', 'production', '999_common_metrics.rb') }
+
+ it "imports all prometheus metrics" do
+ expect(PrometheusMetric.common).to be_empty
+
+ subject
+
+ expect(PrometheusMetric.common).not_to be_empty
+ end
+end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
new file mode 100644
index 00000000000..e8584846b56
--- /dev/null
+++ b/spec/db/schema_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Database schema' do
+ let(:connection) { ActiveRecord::Base.connection }
+ let(:tables) { connection.tables }
+
+ # Use if you are certain that this column should not have a foreign key
+ IGNORED_FK_COLUMNS = {
+ abuse_reports: %w[reporter_id user_id],
+ application_settings: %w[performance_bar_allowed_group_id],
+ audit_events: %w[author_id entity_id],
+ award_emoji: %w[awardable_id user_id],
+ chat_names: %w[chat_id service_id team_id user_id],
+ chat_teams: %w[team_id],
+ ci_builds: %w[erased_by_id runner_id trigger_request_id user_id],
+ ci_pipelines: %w[user_id],
+ ci_runner_projects: %w[runner_id],
+ ci_trigger_requests: %w[commit_id],
+ cluster_providers_gcp: %w[gcp_project_id operation_id],
+ deploy_keys_projects: %w[deploy_key_id],
+ deployments: %w[deployable_id environment_id user_id],
+ emails: %w[user_id],
+ events: %w[target_id],
+ forked_project_links: %w[forked_from_project_id],
+ identities: %w[user_id],
+ issues: %w[last_edited_by_id],
+ keys: %w[user_id],
+ label_links: %w[target_id],
+ lfs_objects_projects: %w[lfs_object_id project_id],
+ members: %w[source_id created_by_id],
+ merge_requests: %w[last_edited_by_id],
+ namespaces: %w[owner_id parent_id],
+ notes: %w[author_id commit_id noteable_id updated_by_id resolved_by_id discussion_id],
+ notification_settings: %w[source_id],
+ oauth_access_grants: %w[resource_owner_id application_id],
+ oauth_access_tokens: %w[resource_owner_id application_id],
+ oauth_applications: %w[owner_id],
+ project_group_links: %w[group_id],
+ project_statistics: %w[namespace_id],
+ projects: %w[creator_id namespace_id ci_id],
+ redirect_routes: %w[source_id],
+ repository_languages: %w[programming_language_id],
+ routes: %w[source_id],
+ sent_notifications: %w[project_id noteable_id recipient_id commit_id in_reply_to_discussion_id],
+ snippets: %w[author_id],
+ spam_logs: %w[user_id],
+ subscriptions: %w[user_id subscribable_id],
+ taggings: %w[tag_id taggable_id tagger_id],
+ timelogs: %w[user_id],
+ todos: %w[target_id commit_id],
+ uploads: %w[model_id],
+ user_agent_details: %w[subject_id],
+ users: %w[color_scheme_id created_by_id theme_id],
+ users_star_projects: %w[user_id],
+ web_hooks: %w[service_id]
+ }.with_indifferent_access.freeze
+
+ context 'for table' do
+ ActiveRecord::Base.connection.tables.sort.each do |table|
+ describe table do
+ let(:indexes) { connection.indexes(table) }
+ let(:columns) { connection.columns(table) }
+ let(:foreign_keys) { connection.foreign_keys(table) }
+
+ context 'all foreign keys' do
+ # for index to be effective, the FK constraint has to be at first place
+ it 'are indexed' do
+ first_indexed_column = indexes.map(&:columns).map(&:first)
+ foreign_keys_columns = foreign_keys.map(&:column)
+
+ expect(first_indexed_column.uniq).to include(*foreign_keys_columns)
+ end
+ end
+
+ context 'columns ending with _id' do
+ let(:column_names) { columns.map(&:name) }
+ let(:column_names_with_id) { column_names.select { |column_name| column_name.ends_with?('_id') } }
+ let(:foreign_keys_columns) { foreign_keys.map(&:column) }
+ let(:ignored_columns) { ignored_fk_columns(table) }
+
+ it 'do have the foreign keys' do
+ expect(column_names_with_id - ignored_columns).to contain_exactly(*foreign_keys_columns)
+ end
+ end
+ end
+ end
+ end
+
+ private
+
+ def ignored_fk_columns(column)
+ IGNORED_FK_COLUMNS.fetch(column, [])
+ end
+end
diff --git a/spec/factories/board_group_recent_visit.rb b/spec/factories/board_group_recent_visit.rb
new file mode 100644
index 00000000000..97ad5d6d068
--- /dev/null
+++ b/spec/factories/board_group_recent_visit.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :board_group_recent_visit do
+ user
+ group
+ board
+ end
+end
diff --git a/spec/factories/board_project_recent_visit.rb b/spec/factories/board_project_recent_visit.rb
new file mode 100644
index 00000000000..49ae4d7b391
--- /dev/null
+++ b/spec/factories/board_project_recent_visit.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :board_project_recent_visit do
+ user
+ project
+ board
+ end
+end
diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb
index 9a65e7f8a3f..1a2be5e9552 100644
--- a/spec/factories/broadcast_messages.rb
+++ b/spec/factories/broadcast_messages.rb
@@ -1,17 +1,17 @@
FactoryBot.define do
factory :broadcast_message do
message "MyText"
- starts_at 1.day.ago
- ends_at 1.day.from_now
+ starts_at { 1.day.ago }
+ ends_at { 1.day.from_now }
trait :expired do
- starts_at 5.days.ago
- ends_at 3.days.ago
+ starts_at { 5.days.ago }
+ ends_at { 3.days.ago }
end
trait :future do
- starts_at 5.days.from_now
- ends_at 6.days.from_now
+ starts_at { 5.days.from_now }
+ ends_at { 6.days.from_now }
end
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 9813190925b..90754319f05 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -27,6 +27,12 @@ FactoryBot.define do
pipeline factory: :ci_pipeline
+ trait :degenerated do
+ commands nil
+ options nil
+ yaml_variables nil
+ end
+
trait :started do
started_at 'Di 29. Okt 09:51:28 CET 2013'
end
@@ -70,6 +76,18 @@ FactoryBot.define do
status 'created'
end
+ trait :scheduled do
+ schedulable
+ status 'scheduled'
+ scheduled_at { 1.minute.since }
+ end
+
+ trait :expired_scheduled do
+ schedulable
+ status 'scheduled'
+ scheduled_at { 1.minute.ago }
+ end
+
trait :manual do
status 'manual'
self.when 'manual'
@@ -82,6 +100,30 @@ FactoryBot.define do
url: 'http://staging.example.com/$CI_JOB_NAME' }
end
+ trait :deploy_to_production do
+ environment 'production'
+
+ options environment: { name: 'production',
+ url: 'http://prd.example.com/$CI_JOB_NAME' }
+ end
+
+ trait :start_review_app do
+ environment 'review/$CI_COMMIT_REF_NAME'
+
+ options environment: { name: 'review/$CI_COMMIT_REF_NAME',
+ url: 'http://staging.example.com/$CI_JOB_NAME',
+ on_stop: 'stop_review_app' }
+ end
+
+ trait :stop_review_app do
+ name 'stop_review_app'
+ environment 'review/$CI_COMMIT_REF_NAME'
+
+ options environment: { name: 'review/$CI_COMMIT_REF_NAME',
+ url: 'http://staging.example.com/$CI_JOB_NAME',
+ action: 'stop' }
+ end
+
trait :allowed_to_fail do
allow_failure true
end
@@ -98,6 +140,15 @@ FactoryBot.define do
success
end
+ trait :schedulable do
+ self.when 'delayed'
+ options start_in: '1 minute'
+ end
+
+ trait :actionable do
+ self.when 'manual'
+ end
+
trait :retried do
retried true
end
@@ -159,12 +210,12 @@ FactoryBot.define do
end
trait :erased do
- erased_at Time.now
+ erased_at { Time.now }
erased_by factory: :user
end
trait :queued do
- queued_at Time.now
+ queued_at { Time.now }
runner factory: :ci_runner
end
@@ -194,7 +245,7 @@ FactoryBot.define do
end
trait :expired do
- artifacts_expire_at 1.minute.ago
+ artifacts_expire_at { 1.minute.ago }
end
trait :with_commit do
diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb
index 46aaaf6aa5d..2c76c22ba69 100644
--- a/spec/factories/ci/job_artifacts.rb
+++ b/spec/factories/ci/job_artifacts.rb
@@ -14,6 +14,33 @@ FactoryBot.define do
artifact.project ||= artifact.job.project
end
+ trait :raw do
+ file_format :raw
+
+ after(:build) do |artifact, _|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/trace/sample_trace'), 'text/plain')
+ end
+ end
+
+ trait :zip do
+ file_format :zip
+
+ after(:build) do |artifact, _|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip')
+ end
+ end
+
+ trait :gzip do
+ file_format :gzip
+
+ after(:build) do |artifact, _|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/ci_build_artifacts_metadata.gz'), 'application/x-gzip')
+ end
+ end
+
trait :archive do
file_type :archive
file_format :zip
@@ -24,6 +51,12 @@ FactoryBot.define do
end
end
+ trait :legacy_archive do
+ archive
+
+ file_location :legacy_path
+ end
+
trait :metadata do
file_type :metadata
file_format :gzip
@@ -84,6 +117,16 @@ FactoryBot.define do
end
end
+ trait :codequality do
+ file_type :codequality
+ file_format :raw
+
+ after(:build) do |artifact, evaluator|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/codequality/codequality.json'), 'application/json')
+ end
+ end
+
trait :correct_checksum do
after(:build) do |artifact, evaluator|
artifact.file_sha256 = Digest::SHA256.file(artifact.file.path).hexdigest
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index a6ff226fa75..8a44ce52849 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -54,6 +54,10 @@ FactoryBot.define do
status :manual
end
+ trait :scheduled do
+ status :scheduled
+ end
+
trait :success do
status :success
end
@@ -77,6 +81,14 @@ FactoryBot.define do
pipeline.builds << build(:ci_build, :test_reports, pipeline: pipeline, project: pipeline.project)
end
end
+
+ trait :auto_devops_source do
+ config_source { Ci::Pipeline.config_sources[:auto_devops_source] }
+ end
+
+ trait :repository_source do
+ config_source { Ci::Pipeline.config_sources[:repository_source] }
+ end
end
end
end
diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb
index 347e4f433e2..24e70913b87 100644
--- a/spec/factories/ci/runners.rb
+++ b/spec/factories/ci/runners.rb
@@ -9,7 +9,7 @@ FactoryBot.define do
runner_type :instance_type
trait :online do
- contacted_at Time.now
+ contacted_at { Time.now }
end
trait :instance do
@@ -47,5 +47,15 @@ FactoryBot.define do
trait :ref_protected do
access_level :ref_protected
end
+
+ trait :tagged_only do
+ run_untagged false
+
+ tag_list %w(tag1 tag2)
+ end
+
+ trait :locked do
+ locked true
+ end
end
end
diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb
index 7c4a440b9a9..ff65c76cf26 100644
--- a/spec/factories/clusters/applications/helm.rb
+++ b/spec/factories/clusters/applications/helm.rb
@@ -22,14 +22,27 @@ FactoryBot.define do
status 3
end
+ trait :updating do
+ status 4
+ end
+
+ trait :updated do
+ status 5
+ end
+
trait :errored do
status(-1)
status_reason 'something went wrong'
end
+ trait :update_errored do
+ status(6)
+ status_reason 'something went wrong'
+ end
+
trait :timeouted do
installing
- updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago
+ updated_at { ClusterWaitForAppInstallationWorker::TIMEOUT.ago }
end
factory :clusters_applications_ingress, class: Clusters::Applications::Ingress do
@@ -44,9 +57,14 @@ FactoryBot.define do
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
+ factory :clusters_applications_knative, class: Clusters::Applications::Knative do
+ hostname 'example.com'
+ cluster factory: %i(cluster with_installed_helm provided_by_gcp)
+ end
+
factory :clusters_applications_jupyter, class: Clusters::Applications::Jupyter do
oauth_application factory: :oauth_application
- cluster factory: %i(cluster with_installed_helm provided_by_gcp)
+ cluster factory: %i(cluster with_installed_helm provided_by_gcp project)
end
end
end
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index bbeba8ce8b9..c9f5d0a813e 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -2,13 +2,28 @@ FactoryBot.define do
factory :cluster, class: Clusters::Cluster do
user
name 'test-cluster'
+ cluster_type :project_type
+
+ trait :instance do
+ cluster_type { Clusters::Cluster.cluster_types[:instance_type] }
+ end
trait :project do
+ cluster_type { Clusters::Cluster.cluster_types[:project_type] }
+
before(:create) do |cluster, evaluator|
cluster.projects << create(:project, :repository)
end
end
+ trait :group do
+ cluster_type { Clusters::Cluster.cluster_types[:group_type] }
+
+ before(:create) do |cluster, evalutor|
+ cluster.groups << create(:group)
+ end
+ end
+
trait :provided_by_user do
provider_type :user
platform_type :kubernetes
diff --git a/spec/factories/clusters/kubernetes_namespaces.rb b/spec/factories/clusters/kubernetes_namespaces.rb
new file mode 100644
index 00000000000..6ad93fb0f45
--- /dev/null
+++ b/spec/factories/clusters/kubernetes_namespaces.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :cluster_kubernetes_namespace, class: Clusters::KubernetesNamespace do
+ association :cluster, :project, :provided_by_gcp
+
+ after(:build) do |kubernetes_namespace|
+ cluster_project = kubernetes_namespace.cluster.cluster_project
+
+ kubernetes_namespace.project = cluster_project.project
+ kubernetes_namespace.cluster_project = cluster_project
+ end
+
+ trait :with_token do
+ service_account_token { FFaker::Lorem.characters(10) }
+ end
+ end
+end
diff --git a/spec/factories/clusters/platforms/kubernetes.rb b/spec/factories/clusters/platforms/kubernetes.rb
index 89f6ddebf6a..8169c457ab7 100644
--- a/spec/factories/clusters/platforms/kubernetes.rb
+++ b/spec/factories/clusters/platforms/kubernetes.rb
@@ -3,18 +3,21 @@ FactoryBot.define do
cluster
namespace nil
api_url 'https://kubernetes.example.com'
- token 'a' * 40
+ token { 'a' * 40 }
trait :configured do
api_url 'https://kubernetes.example.com'
- token 'a' * 40
username 'xxxxxx'
password 'xxxxxx'
- after(:create) do |platform_kubernetes, evaluator|
+ before(:create) do |platform_kubernetes, evaluator|
pem_file = File.expand_path(Rails.root.join('spec/fixtures/clusters/sample_cert.pem'))
platform_kubernetes.ca_cert = File.read(pem_file)
end
end
+
+ trait :rbac_enabled do
+ authorization_type :rbac
+ end
end
end
diff --git a/spec/factories/clusters/projects.rb b/spec/factories/clusters/projects.rb
new file mode 100644
index 00000000000..6cda77c6f85
--- /dev/null
+++ b/spec/factories/clusters/projects.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :cluster_project, class: Clusters::Project do
+ cluster
+ project
+ end
+end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index 53368c64e10..381bf07f6a0 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -41,6 +41,10 @@ FactoryBot.define do
status 'manual'
end
+ trait :scheduled do
+ status 'scheduled'
+ end
+
after(:build) do |build, evaluator|
build.project = build.pipeline.project
end
diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb
index cac56695319..011c98599a3 100644
--- a/spec/factories/deployments.rb
+++ b/spec/factories/deployments.rb
@@ -16,5 +16,36 @@ FactoryBot.define do
allow(deployment.project.repository).to receive(:create_ref)
end
end
+
+ trait :review_app do
+ sha { TestEnv::BRANCH_SHA['pages-deploy'] }
+ ref 'pages-deploy'
+ end
+
+ trait :running do
+ status :running
+ end
+
+ trait :success do
+ status :success
+ finished_at { Time.now }
+ end
+
+ trait :failed do
+ status :failed
+ finished_at { Time.now }
+ end
+
+ trait :canceled do
+ status :canceled
+ finished_at { Time.now }
+ end
+
+ # This trait hooks the state maechine's events
+ trait :succeed do
+ after(:create) do |deployment, evaluator|
+ deployment.succeed!
+ end
+ end
end
end
diff --git a/spec/factories/emails.rb b/spec/factories/emails.rb
index 4dc7961060a..feacd5ccf15 100644
--- a/spec/factories/emails.rb
+++ b/spec/factories/emails.rb
@@ -3,6 +3,7 @@ FactoryBot.define do
user
email { generate(:email_alias) }
- trait(:confirmed) { confirmed_at Time.now }
+ trait(:confirmed) { confirmed_at { Time.now } }
+ trait(:skip_validate) { to_create {|instance| instance.save(validate: false) } }
end
end
diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb
index b5db57d5148..9d9e3d693b8 100644
--- a/spec/factories/environments.rb
+++ b/spec/factories/environments.rb
@@ -22,6 +22,7 @@ FactoryBot.define do
pipeline: pipeline)
deployment = create(:deployment,
+ :success,
environment: environment,
project: environment.project,
deployable: deployable,
diff --git a/spec/factories/events.rb b/spec/factories/events.rb
index 5798b81ecad..bf8411b1894 100644
--- a/spec/factories/events.rb
+++ b/spec/factories/events.rb
@@ -24,7 +24,7 @@ FactoryBot.define do
factory :push_event, class: PushEvent do
project factory: :project_empty_repo
- author factory: :user
+ author(factory: :user) { project.creator }
action Event::PUSHED
end
diff --git a/spec/factories/forked_project_links.rb b/spec/factories/forked_project_links.rb
deleted file mode 100644
index bc59fea81ec..00000000000
--- a/spec/factories/forked_project_links.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-FactoryBot.define do
- factory :forked_project_link do
- association :forked_to_project, factory: [:project, :repository]
- association :forked_from_project, factory: [:project, :repository]
-
- after(:create) do |link|
- link.forked_from_project.reload
- link.forked_to_project.reload
- end
-
- trait :forked_to_empty_project do
- association :forked_to_project, factory: [:project, :repository]
- end
- end
-end
diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb
index 51b8ddc9934..3c0f43cc1b6 100644
--- a/spec/factories/gpg_keys.rb
+++ b/spec/factories/gpg_keys.rb
@@ -2,11 +2,11 @@ require_relative '../support/helpers/gpg_helpers'
FactoryBot.define do
factory :gpg_key do
- key GpgHelpers::User1.public_key
+ key { GpgHelpers::User1.public_key }
user
factory :gpg_key_with_subkeys do
- key GpgHelpers::User1.public_key_with_extra_signing_key
+ key { GpgHelpers::User1.public_key_with_extra_signing_key }
end
end
end
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 47036560b9d..12be63e5d92 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -9,7 +9,7 @@ FactoryBot.define do
trait(:developer) { access_level GroupMember::DEVELOPER }
trait(:maintainer) { access_level GroupMember::MAINTAINER }
trait(:owner) { access_level GroupMember::OWNER }
- trait(:access_request) { requested_at Time.now }
+ trait(:access_request) { requested_at { Time.now } }
trait(:invited) do
user_id nil
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 4d4f74e101e..ab27fee33dc 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -13,6 +13,10 @@ FactoryBot.define do
state :opened
end
+ trait :locked do
+ discussion_locked true
+ end
+
trait :closed do
state :closed
closed_at { Time.now }
diff --git a/spec/factories/merge_request_diff_files.rb b/spec/factories/merge_request_diff_files.rb
new file mode 100644
index 00000000000..469a7a0ac8d
--- /dev/null
+++ b/spec/factories/merge_request_diff_files.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :merge_request_diff_file do
+ association :merge_request_diff
+
+ relative_order 0
+ new_file true
+ renamed_file false
+ deleted_file false
+ too_large false
+ a_mode 0
+ b_mode 100644
+ new_path 'foo'
+ old_path 'foo'
+ diff ''
+ binary false
+
+ trait :new_file do
+ relative_order 0
+ new_file true
+ renamed_file false
+ deleted_file false
+ too_large false
+ a_mode 0
+ b_mode 100644
+ new_path 'foo'
+ old_path 'foo'
+ diff ''
+ binary false
+ end
+
+ trait :renamed_file do
+ relative_order 662
+ new_file false
+ renamed_file true
+ deleted_file false
+ too_large false
+ a_mode 100644
+ b_mode 100644
+ new_path 'bar'
+ old_path 'baz'
+ diff ''
+ binary false
+ end
+ end
+end
diff --git a/spec/factories/merge_request_diffs.rb b/spec/factories/merge_request_diffs.rb
new file mode 100644
index 00000000000..e7b51189538
--- /dev/null
+++ b/spec/factories/merge_request_diffs.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :merge_request_diff do
+ association :merge_request
+ state :collected
+ commits_count 1
+
+ base_commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) }
+ head_commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) }
+ start_commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) }
+ end
+end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index b8b089b069b..2392bfc4a53 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -80,7 +80,7 @@ FactoryBot.define do
trait :merge_when_pipeline_succeeds do
merge_when_pipeline_succeeds true
- merge_user author
+ merge_user { author }
end
trait :remove_source_branch do
@@ -101,6 +101,20 @@ FactoryBot.define do
end
end
+ trait :deployed_review_app do
+ target_branch 'pages-deploy-target'
+
+ transient do
+ deployment { create(:deployment, :review_app) }
+ end
+
+ after(:build) do |merge_request, evaluator|
+ merge_request.source_branch = evaluator.deployment.ref
+ merge_request.source_project = evaluator.deployment.project
+ merge_request.target_project = evaluator.deployment.project
+ end
+ end
+
after(:build) do |merge_request|
target_project = merge_request.target_project
source_project = merge_request.source_project
diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb
index 019e4420212..027349b2f8e 100644
--- a/spec/factories/milestones.rb
+++ b/spec/factories/milestones.rb
@@ -34,7 +34,7 @@ FactoryBot.define do
milestone.project_id = evaluator.project_id
elsif evaluator.parent
id = evaluator.parent.id
- evaluator.parent.is_a?(Group) ? board.group_id = id : evaluator.project_id = id
+ evaluator.parent.is_a?(Group) ? evaluator.group_id = id : evaluator.project_id = id
else
milestone.project = create(:project)
end
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 6844ed8aa4a..2d1f48bf249 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -90,7 +90,7 @@ FactoryBot.define do
noteable nil
noteable_type 'Commit'
noteable_id nil
- commit_id RepoHelpers.sample_commit.id
+ commit_id { RepoHelpers.sample_commit.id }
end
trait :legacy_diff_note do
diff --git a/spec/factories/oauth_access_grants.rb b/spec/factories/oauth_access_grants.rb
index 9e6af24c4eb..02c51cd9899 100644
--- a/spec/factories/oauth_access_grants.rb
+++ b/spec/factories/oauth_access_grants.rb
@@ -3,7 +3,7 @@ FactoryBot.define do
resource_owner_id { create(:user).id }
application
token { Doorkeeper::OAuth::Helpers::UniqueToken.generate }
- expires_in 2.hours
+ expires_in { 2.hours }
redirect_uri { application.redirect_uri }
scopes { application.scopes }
diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb
index b77f702f9e1..75ac7cc7687 100644
--- a/spec/factories/project_auto_devops.rb
+++ b/spec/factories/project_auto_devops.rb
@@ -5,8 +5,16 @@ FactoryBot.define do
domain "example.com"
deploy_strategy :continuous
- trait :manual do
- deploy_strategy :manual
+ trait :continuous_deployment do
+ deploy_strategy ProjectAutoDevops.deploy_strategies[:continuous] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
+ end
+
+ trait :manual_deployment do
+ deploy_strategy ProjectAutoDevops.deploy_strategies[:manual] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
+ end
+
+ trait :timed_incremental_deployment do
+ deploy_strategy ProjectAutoDevops.deploy_strategies[:timed_incremental] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
end
trait :disabled do
diff --git a/spec/factories/project_group_links.rb b/spec/factories/project_group_links.rb
index d5ace9425a0..59c77627ee5 100644
--- a/spec/factories/project_group_links.rb
+++ b/spec/factories/project_group_links.rb
@@ -2,5 +2,6 @@ FactoryBot.define do
factory :project_group_link do
project
group
+ expires_at nil
end
end
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index 22a8085ea45..c72e0487895 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -8,7 +8,7 @@ FactoryBot.define do
trait(:reporter) { access_level ProjectMember::REPORTER }
trait(:developer) { access_level ProjectMember::DEVELOPER }
trait(:maintainer) { access_level ProjectMember::MAINTAINER }
- trait(:access_request) { requested_at Time.now }
+ trait(:access_request) { requested_at { Time.now } }
trait(:invited) do
user_id nil
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 1215b04913e..e4823a5adf1 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -1,6 +1,8 @@
require_relative '../support/helpers/test_env'
FactoryBot.define do
+ PAGES_ACCESS_LEVEL_SCHEMA_VERSION = 20180423204600
+
# Project without repository
#
# Project does not have bare repository.
@@ -23,6 +25,7 @@ FactoryBot.define do
issues_access_level ProjectFeature::ENABLED
merge_requests_access_level ProjectFeature::ENABLED
repository_access_level ProjectFeature::ENABLED
+ pages_access_level ProjectFeature::ENABLED
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first
@@ -34,13 +37,20 @@ FactoryBot.define do
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
- project.project_feature.update(
+ hash = {
wiki_access_level: evaluator.wiki_access_level,
builds_access_level: builds_access_level,
snippets_access_level: evaluator.snippets_access_level,
issues_access_level: evaluator.issues_access_level,
merge_requests_access_level: merge_requests_access_level,
- repository_access_level: evaluator.repository_access_level)
+ repository_access_level: evaluator.repository_access_level
+ }
+
+ if ActiveRecord::Migrator.current_version >= PAGES_ACCESS_LEVEL_SCHEMA_VERSION
+ hash.store("pages_access_level", evaluator.pages_access_level)
+ end
+
+ project.project_feature.update(hash)
# Normally the class Projects::CreateService is used for creating
# projects, and this class takes care of making sure the owner and current
@@ -103,30 +113,41 @@ FactoryBot.define do
end
trait :with_export do
- before(:create) do |_project, _evaluator|
- allow(Feature).to receive(:enabled?).with(:import_export_object_storage) { false }
- allow(Feature).to receive(:enabled?).with('import_export_object_storage') { false }
- end
-
after(:create) do |project, _evaluator|
ProjectExportWorker.new.perform(project.creator.id, project.id)
end
end
- trait :with_object_export do
- before(:create) do |_project, _evaluator|
- allow(Feature).to receive(:enabled?).with(:import_export_object_storage) { true }
- allow(Feature).to receive(:enabled?).with('import_export_object_storage') { true }
+ trait :broken_storage do
+ after(:create) do |project|
+ project.update_column(:repository_storage, 'broken')
end
+ end
- after(:create) do |project, evaluator|
- ProjectExportWorker.new.perform(project.creator.id, project.id)
+ # Build a custom repository by specifying a hash of `filename => content` in
+ # the transient `files` attribute. Each file will be created in its own
+ # commit, operating against the master branch. So, the following call:
+ #
+ # create(:project, :custom_repo, files: { 'foo/a.txt' => 'foo', 'b.txt' => bar' })
+ #
+ # will create a repository containing two files, and two commits, in master
+ trait :custom_repo do
+ transient do
+ files {}
end
- end
- trait :broken_storage do
- after(:create) do |project|
- project.update_column(:repository_storage, 'broken')
+ after :create do |project, evaluator|
+ raise "Failed to create repository!" unless project.create_repository
+
+ evaluator.files.each do |filename, content|
+ project.repository.create_file(
+ project.creator,
+ filename,
+ content,
+ message: "Automatically created file #{filename}",
+ branch_name: 'master'
+ )
+ end
end
end
@@ -233,6 +254,14 @@ FactoryBot.define do
trait(:repository_enabled) { repository_access_level ProjectFeature::ENABLED }
trait(:repository_disabled) { repository_access_level ProjectFeature::DISABLED }
trait(:repository_private) { repository_access_level ProjectFeature::PRIVATE }
+ trait(:pages_public) { pages_access_level ProjectFeature::PUBLIC }
+ trait(:pages_enabled) { pages_access_level ProjectFeature::ENABLED }
+ trait(:pages_disabled) { pages_access_level ProjectFeature::DISABLED }
+ trait(:pages_private) { pages_access_level ProjectFeature::PRIVATE }
+
+ trait :auto_devops do
+ association :auto_devops, factory: :project_auto_devops
+ end
end
# Project with empty repository
diff --git a/spec/factories/prometheus_metrics.rb b/spec/factories/prometheus_metrics.rb
new file mode 100644
index 00000000000..c56644bfb96
--- /dev/null
+++ b/spec/factories/prometheus_metrics.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :prometheus_metric, class: PrometheusMetric do
+ title 'title'
+ query 'avg(metric)'
+ y_label 'y_label'
+ unit 'm/s'
+ group :business
+ project
+ legend 'legend'
+
+ trait :common do
+ common true
+ project nil
+ end
+ end
+end
diff --git a/spec/factories/resource_label_events.rb b/spec/factories/resource_label_events.rb
index a67ad78c098..739ba901052 100644
--- a/spec/factories/resource_label_events.rb
+++ b/spec/factories/resource_label_events.rb
@@ -2,9 +2,12 @@
FactoryBot.define do
factory :resource_label_event do
- user { issue.project.creator }
action :add
label
- issue
+ user { issuable&.author || create(:user) }
+
+ after(:build) do |event, evaluator|
+ event.issue = create(:issue) unless event.issuable
+ end
end
end
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index 0d4fd49bf3a..5be56a49903 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -46,6 +46,17 @@ FactoryBot.define do
)
end
+ factory :jira_cloud_service, class: JiraService do
+ project
+ active true
+ properties(
+ url: 'https://mysite.atlassian.net',
+ username: 'jira_user',
+ password: 'my-secret-password',
+ project_key: 'jira-key'
+ )
+ end
+
factory :hipchat_service do
project
type 'HipchatService'
diff --git a/spec/factories/site_statistics.rb b/spec/factories/site_statistics.rb
index dd8c795515a..2533d0eecc2 100644
--- a/spec/factories/site_statistics.rb
+++ b/spec/factories/site_statistics.rb
@@ -2,6 +2,5 @@ FactoryBot.define do
factory :site_statistics, class: 'SiteStatistic' do
id 1
repositories_count 999
- wikis_count 555
end
end
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 14486c80341..ed3d87eb76b 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -49,7 +49,7 @@ FactoryBot.define do
author
user
action { Todo::ASSIGNED }
- commit_id RepoHelpers.sample_commit.id
+ commit_id { RepoHelpers.sample_commit.id }
target_type "Commit"
end
end
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index a81b2169b89..7256f785e1f 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -1,7 +1,7 @@
FactoryBot.define do
factory :upload do
model { build(:project) }
- size 100.kilobytes
+ size { 100.kilobytes }
uploader "AvatarUploader"
mount_point :avatar
secret nil
@@ -19,13 +19,13 @@ FactoryBot.define do
uploader "PersonalFileUploader"
path { File.join(secret, filename) }
model { build(:personal_snippet) }
- secret SecureRandom.hex
+ secret { SecureRandom.hex }
end
trait :issuable_upload do
uploader "FileUploader"
path { File.join(secret, filename) }
- secret SecureRandom.hex
+ secret { SecureRandom.hex }
end
trait :with_file do
@@ -43,7 +43,14 @@ FactoryBot.define do
model { build(:group) }
path { File.join(secret, filename) }
uploader "NamespaceFileUploader"
- secret SecureRandom.hex
+ secret { SecureRandom.hex }
+ end
+
+ trait :favicon_upload do
+ model { build(:appearance) }
+ path { File.join(secret, filename) }
+ uploader "FaviconUploader"
+ secret { SecureRandom.hex }
end
trait :attachment_upload do
diff --git a/spec/factories/user_preferences.rb b/spec/factories/user_preferences.rb
new file mode 100644
index 00000000000..19059a93625
--- /dev/null
+++ b/spec/factories/user_preferences.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :user_preference do
+ user
+
+ trait :only_comments do
+ issue_notes_filter { UserPreference::NOTES_FILTERS[:only_comments] }
+ merge_request_notes_filter { UserPreference::NOTE_FILTERS[:only_comments] }
+ end
+ end
+end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 59db8cdc34b..a47bd7cafca 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -58,6 +58,14 @@ FactoryBot.define do
project_view :readme
end
+ trait :commit_email do
+ after(:create) do |user, evaluator|
+ additional = create(:email, :confirmed, user: user, email: "commit-#{user.email}")
+
+ user.update!(commit_email: additional.email)
+ end
+ end
+
factory :omniauth_user do
transient do
extern_uid '123456'
diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb
index 134eb25e4b1..0b5ab16ad71 100644
--- a/spec/fast_spec_helper.rb
+++ b/spec/fast_spec_helper.rb
@@ -8,3 +8,5 @@ require_relative 'support/rspec'
require 'active_support/all'
ActiveSupport::Dependencies.autoload_paths << 'lib'
+ActiveSupport::Dependencies.autoload_paths << 'ee/lib'
+ActiveSupport::XmlMini.backend = 'Nokogiri'
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 96dfde2e08c..735ca60f7da 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -53,13 +53,33 @@ describe 'Admin Groups' do
expect_selected_visibility(internal)
end
- it 'when entered in group path, it auto filled the group name', :js do
+ it 'when entered in group name, it auto filled the group path', :js do
visit admin_groups_path
click_link "New group"
- group_path = 'gitlab'
+ group_name = 'gitlab'
+ fill_in 'group_name', with: group_name
+ path_field = find('input#group_path')
+ expect(path_field.value).to eq group_name
+ end
+
+ it 'auto populates the group path with the group name', :js do
+ visit admin_groups_path
+ click_link "New group"
+ group_name = 'my gitlab project'
+ fill_in 'group_name', with: group_name
+ path_field = find('input#group_path')
+ expect(path_field.value).to eq 'my-gitlab-project'
+ end
+
+ it 'when entering in group path, group name does not change anymore', :js do
+ visit admin_groups_path
+ click_link "New group"
+ group_path = 'my-gitlab-project'
+ group_name = 'My modified gitlab project'
fill_in 'group_path', with: group_path
- name_field = find('input#group_name')
- expect(name_field.value).to eq group_path
+ fill_in 'group_name', with: group_name
+ path_field = find('input#group_path')
+ expect(path_field.value).to eq 'my-gitlab-project'
end
end
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index aaa3e8dc821..790051dd933 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -2,10 +2,11 @@ require 'spec_helper'
describe "Admin Health Check", :feature do
include StubENV
+ set(:admin) { create(:admin) }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- sign_in(create(:admin))
+ sign_in(admin)
end
describe '#show' do
@@ -56,27 +57,4 @@ describe "Admin Health Check", :feature do
expect(page).to have_content('The server is on fire')
end
end
-
- context 'with repository storage failures', :broken_storage do
- before do
- visit admin_health_check_path
- end
-
- it 'shows storage failure information' do
- hostname = Gitlab::Environment.hostname
- maximum_failures = Gitlab::CurrentSettings.current_application_settings
- .circuitbreaker_failure_count_threshold
- number_of_failures = maximum_failures + 1
-
- expect(page).to have_content("broken: #{number_of_failures} failed storage access attempts:")
- expect(page).to have_content("#{hostname}: #{number_of_failures} of #{maximum_failures} failures.")
- end
-
- it 'allows resetting storage failures' do
- click_button 'Reset git storage health information'
-
- expect(page).to have_content('Git storage health information has been reset')
- expect(page).not_to have_content('failed storage access attempt')
- end
- end
end
diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb
index f979d2f6090..a4904272706 100644
--- a/spec/features/admin/admin_manage_applications_spec.rb
+++ b/spec/features/admin/admin_manage_applications_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'admin manage applications' do
check :doorkeeper_application_trusted
click_on 'Submit'
expect(page).to have_content('Application: test')
- expect(page).to have_content('Application Id')
+ expect(page).to have_content('Application ID')
expect(page).to have_content('Secret')
expect(page).to have_content('Trusted Y')
@@ -28,7 +28,7 @@ RSpec.describe 'admin manage applications' do
click_on 'Submit'
expect(page).to have_content('test_changed')
- expect(page).to have_content('Application Id')
+ expect(page).to have_content('Application ID')
expect(page).to have_content('Secret')
expect(page).to have_content('Trusted N')
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index be8754a5315..ed9c0ea9ac0 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
describe "Admin Runners" do
include StubENV
+ include FilteredSearchHelpers
+ include SortingHelper
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
@@ -12,39 +14,156 @@ describe "Admin Runners" do
let(:pipeline) { create(:ci_pipeline) }
context "when there are runners" do
- before do
- runner = FactoryBot.create(:ci_runner, contacted_at: Time.now)
- FactoryBot.create(:ci_build, pipeline: pipeline, runner_id: runner.id)
+ it 'has all necessary texts' do
+ runner = create(:ci_runner, contacted_at: Time.now)
+ create(:ci_build, pipeline: pipeline, runner_id: runner.id)
visit admin_runners_path
- end
- it 'has all necessary texts' do
- expect(page).to have_text "Setup a shared Runner manually"
- expect(page).to have_text "Runners with last contact more than a minute ago: 1"
+ expect(page).to have_text "Set up a shared Runner manually"
+ expect(page).to have_text "Runners currently online: 1"
end
- describe 'search' do
+ describe 'search', :js do
before do
- FactoryBot.create :ci_runner, description: 'runner-foo'
- FactoryBot.create :ci_runner, description: 'runner-bar'
+ create(:ci_runner, description: 'runner-foo')
+ create(:ci_runner, description: 'runner-bar')
+
+ visit admin_runners_path
end
it 'shows correct runner when description matches' do
- search_form = find('#runners-search')
- search_form.fill_in 'search', with: 'runner-foo'
- search_form.click_button 'Search'
+ input_filtered_search_keys('runner-foo')
expect(page).to have_content("runner-foo")
expect(page).not_to have_content("runner-bar")
end
it 'shows no runner when description does not match' do
- search_form = find('#runners-search')
- search_form.fill_in 'search', with: 'runner-baz'
- search_form.click_button 'Search'
+ input_filtered_search_keys('runner-baz')
+
+ expect(page).to have_text 'No runners found'
+ end
+ end
+
+ describe 'filter by status', :js do
+ it 'shows correct runner when status matches' do
+ create(:ci_runner, description: 'runner-active', active: true)
+ create(:ci_runner, description: 'runner-paused', active: false)
+
+ visit admin_runners_path
+
+ expect(page).to have_content 'runner-active'
+ expect(page).to have_content 'runner-paused'
+
+ input_filtered_search_keys('status:active')
+ expect(page).to have_content 'runner-active'
+ expect(page).not_to have_content 'runner-paused'
+ end
+
+ it 'shows no runner when status does not match' do
+ create(:ci_runner, :online, description: 'runner-active', active: true)
+ create(:ci_runner, :online, description: 'runner-paused', active: false)
+
+ visit admin_runners_path
+
+ input_filtered_search_keys('status:offline')
+
+ expect(page).not_to have_content 'runner-active'
+ expect(page).not_to have_content 'runner-paused'
+
+ expect(page).to have_text 'No runners found'
+ end
+
+ it 'shows correct runner when status is selected and search term is entered' do
+ create(:ci_runner, description: 'runner-a-1', active: true)
+ create(:ci_runner, description: 'runner-a-2', active: false)
+ create(:ci_runner, description: 'runner-b-1', active: true)
+
+ visit admin_runners_path
+
+ input_filtered_search_keys('status:active')
+ expect(page).to have_content 'runner-a-1'
+ expect(page).to have_content 'runner-b-1'
+ expect(page).not_to have_content 'runner-a-2'
+
+ input_filtered_search_keys('status:active runner-a')
+ expect(page).to have_content 'runner-a-1'
+ expect(page).not_to have_content 'runner-b-1'
+ expect(page).not_to have_content 'runner-a-2'
+ end
+ end
+
+ describe 'filter by type', :js do
+ it 'shows correct runner when type matches' do
+ create :ci_runner, :project, description: 'runner-project'
+ create :ci_runner, :group, description: 'runner-group'
+
+ visit admin_runners_path
+
+ expect(page).to have_content 'runner-project'
+ expect(page).to have_content 'runner-group'
+
+ input_filtered_search_keys('type:project_type')
+ expect(page).to have_content 'runner-project'
+ expect(page).not_to have_content 'runner-group'
+ end
+
+ it 'shows no runner when type does not match' do
+ create :ci_runner, :project, description: 'runner-project'
+ create :ci_runner, :group, description: 'runner-group'
+
+ visit admin_runners_path
+
+ input_filtered_search_keys('type:instance_type')
+
+ expect(page).not_to have_content 'runner-project'
+ expect(page).not_to have_content 'runner-group'
expect(page).to have_text 'No runners found'
end
+
+ it 'shows correct runner when type is selected and search term is entered' do
+ create :ci_runner, :project, description: 'runner-a-1'
+ create :ci_runner, :instance, description: 'runner-a-2'
+ create :ci_runner, :project, description: 'runner-b-1'
+
+ visit admin_runners_path
+
+ input_filtered_search_keys('type:project_type')
+ expect(page).to have_content 'runner-a-1'
+ expect(page).to have_content 'runner-b-1'
+ expect(page).not_to have_content 'runner-a-2'
+
+ input_filtered_search_keys('type:project_type runner-a')
+ expect(page).to have_content 'runner-a-1'
+ expect(page).not_to have_content 'runner-b-1'
+ expect(page).not_to have_content 'runner-a-2'
+ end
+ end
+
+ it 'sorts by last contact date', :js do
+ create(:ci_runner, description: 'runner-1', created_at: '2018-07-12 15:37', contacted_at: '2018-07-12 15:37')
+ create(:ci_runner, description: 'runner-2', created_at: '2018-07-12 16:37', contacted_at: '2018-07-12 16:37')
+
+ visit admin_runners_path
+
+ within '.runners-content .gl-responsive-table-row:nth-child(2)' do
+ expect(page).to have_content 'runner-2'
+ end
+
+ within '.runners-content .gl-responsive-table-row:nth-child(3)' do
+ expect(page).to have_content 'runner-1'
+ end
+
+ sorting_by 'Last Contact'
+
+ within '.runners-content .gl-responsive-table-row:nth-child(2)' do
+ expect(page).to have_content 'runner-1'
+ end
+
+ within '.runners-content .gl-responsive-table-row:nth-child(3)' do
+ expect(page).to have_content 'runner-2'
+ end
end
end
@@ -54,8 +173,8 @@ describe "Admin Runners" do
end
it 'has all necessary texts including no runner message' do
- expect(page).to have_text "Setup a shared Runner manually"
- expect(page).to have_text "Runners with last contact more than a minute ago: 0"
+ expect(page).to have_text "Set up a shared Runner manually"
+ expect(page).to have_text "Runners currently online: 0"
expect(page).to have_text 'No runners found'
end
end
@@ -76,7 +195,7 @@ describe "Admin Runners" do
context 'shared runner' do
it 'shows the label and does not show the project count' do
- runner = create :ci_runner, :instance
+ runner = create(:ci_runner, :instance)
visit admin_runners_path
@@ -89,8 +208,8 @@ describe "Admin Runners" do
context 'specific runner' do
it 'shows the label and the project count' do
- project = create :project
- runner = create :ci_runner, :project, projects: [project]
+ project = create(:project)
+ runner = create(:ci_runner, :project, projects: [project])
visit admin_runners_path
@@ -103,11 +222,11 @@ describe "Admin Runners" do
end
describe "Runner show page" do
- let(:runner) { FactoryBot.create :ci_runner }
+ let(:runner) { create(:ci_runner) }
before do
- @project1 = FactoryBot.create(:project)
- @project2 = FactoryBot.create(:project)
+ @project1 = create(:project)
+ @project2 = create(:project)
visit admin_runner_path(runner)
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index af1c153dec8..0a69a26eb3e 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -9,319 +9,365 @@ describe 'Admin updates settings' do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
- visit admin_application_settings_path
end
- it 'Change visibility settings' do
- page.within('.as-visibility-access') do
- choose "application_setting_default_project_visibility_20"
- click_button 'Save changes'
+ context 'General page' do
+ before do
+ visit admin_application_settings_path
end
- expect(page).to have_content "Application settings saved successfully"
- end
+ it 'Change visibility settings' do
+ page.within('.as-visibility-access') do
+ choose "application_setting_default_project_visibility_20"
+ click_button 'Save changes'
+ end
- it 'Uncheck all restricted visibility levels' do
- page.within('.as-visibility-access') do
- find('#application_setting_visibility_level_0').set(false)
- find('#application_setting_visibility_level_10').set(false)
- find('#application_setting_visibility_level_20').set(false)
- click_button 'Save changes'
+ expect(page).to have_content "Application settings saved successfully"
end
- expect(page).to have_content "Application settings saved successfully"
- expect(find('#application_setting_visibility_level_0')).not_to be_checked
- expect(find('#application_setting_visibility_level_10')).not_to be_checked
- expect(find('#application_setting_visibility_level_20')).not_to be_checked
- end
+ it 'Uncheck all restricted visibility levels' do
+ page.within('.as-visibility-access') do
+ find('#application_setting_visibility_level_0').set(false)
+ find('#application_setting_visibility_level_10').set(false)
+ find('#application_setting_visibility_level_20').set(false)
+ click_button 'Save changes'
+ end
- it 'Modify import sources' do
- expect(Gitlab::CurrentSettings.import_sources).not_to be_empty
+ expect(page).to have_content "Application settings saved successfully"
+ expect(find('#application_setting_visibility_level_0')).not_to be_checked
+ expect(find('#application_setting_visibility_level_10')).not_to be_checked
+ expect(find('#application_setting_visibility_level_20')).not_to be_checked
+ end
- page.within('.as-visibility-access') do
- Gitlab::ImportSources.options.map do |name, _|
- uncheck name
+ it 'Modify import sources' do
+ expect(Gitlab::CurrentSettings.import_sources).not_to be_empty
+
+ page.within('.as-visibility-access') do
+ Gitlab::ImportSources.options.map do |name, _|
+ uncheck name
+ end
+
+ click_button 'Save changes'
end
- click_button 'Save changes'
- end
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.import_sources).to be_empty
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.import_sources).to be_empty
+ page.within('.as-visibility-access') do
+ check "Repo by URL"
+ click_button 'Save changes'
+ end
- page.within('.as-visibility-access') do
- check "Repo by URL"
- click_button 'Save changes'
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.import_sources).to eq(['git'])
end
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.import_sources).to eq(['git'])
- end
+ it 'Change Visibility and Access Controls' do
+ page.within('.as-visibility-access') do
+ uncheck 'Project export enabled'
+ click_button 'Save changes'
+ end
- it 'Change Visibility and Access Controls' do
- page.within('.as-visibility-access') do
- uncheck 'Project export enabled'
- click_button 'Save changes'
+ expect(Gitlab::CurrentSettings.project_export_enabled).to be_falsey
+ expect(page).to have_content "Application settings saved successfully"
end
- expect(Gitlab::CurrentSettings.project_export_enabled).to be_falsey
- expect(page).to have_content "Application settings saved successfully"
- end
+ it 'Change Keys settings' do
+ page.within('.as-visibility-access') do
+ select 'Are forbidden', from: 'RSA SSH keys'
+ select 'Are allowed', from: 'DSA SSH keys'
+ select 'Must be at least 384 bits', from: 'ECDSA SSH keys'
+ select 'Are forbidden', from: 'ED25519 SSH keys'
+ click_on 'Save changes'
+ end
+
+ forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE.to_s
- it 'Change Account and Limit Settings' do
- page.within('.as-account-limit') do
- uncheck 'Gravatar enabled'
- click_button 'Save changes'
+ expect(page).to have_content 'Application settings saved successfully'
+ expect(find_field('RSA SSH keys').value).to eq(forbidden)
+ expect(find_field('DSA SSH keys').value).to eq('0')
+ expect(find_field('ECDSA SSH keys').value).to eq('384')
+ expect(find_field('ED25519 SSH keys').value).to eq(forbidden)
end
- expect(Gitlab::CurrentSettings.gravatar_enabled).to be_falsey
- expect(page).to have_content "Application settings saved successfully"
- end
+ it 'Change Account and Limit Settings' do
+ page.within('.as-account-limit') do
+ uncheck 'Gravatar enabled'
+ click_button 'Save changes'
+ end
- it 'Change Sign-in restrictions' do
- page.within('.as-signin') do
- fill_in 'Home page URL', with: 'https://about.gitlab.com/'
- click_button 'Save changes'
+ expect(Gitlab::CurrentSettings.gravatar_enabled).to be_falsey
+ expect(page).to have_content "Application settings saved successfully"
end
- expect(Gitlab::CurrentSettings.home_page_url).to eq "https://about.gitlab.com/"
- expect(page).to have_content "Application settings saved successfully"
- end
+ it 'Change New users set to external', :js do
+ user_internal_regex = find('#application_setting_user_default_internal_regex', visible: :all)
- it 'Terms of Service' do
- # Already have the admin accept terms, so they don't need to accept in this spec.
- _existing_terms = create(:term)
- accept_terms(admin)
+ expect(user_internal_regex).to be_readonly
+ expect(user_internal_regex['placeholder']).to eq 'To define internal users, first enable new users set to external'
- page.within('.as-terms') do
- check 'Require all users to accept Terms of Service and Privacy Policy when they access GitLab.'
- fill_in 'Terms of Service Agreement', with: 'Be nice!'
- click_button 'Save changes'
- end
+ check 'application_setting_user_default_external'
- expect(Gitlab::CurrentSettings.enforce_terms).to be(true)
- expect(Gitlab::CurrentSettings.terms).to eq 'Be nice!'
- expect(page).to have_content 'Application settings saved successfully'
- end
+ expect(user_internal_regex).not_to be_readonly
+ expect(user_internal_regex['placeholder']).to eq 'Regex pattern'
+ end
- it 'Modify oauth providers' do
- expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty
+ it 'Change Sign-in restrictions' do
+ page.within('.as-signin') do
+ fill_in 'Home page URL', with: 'https://about.gitlab.com/'
+ click_button 'Save changes'
+ end
- page.within('.as-signin') do
- uncheck 'Google'
- click_button 'Save changes'
+ expect(Gitlab::CurrentSettings.home_page_url).to eq "https://about.gitlab.com/"
+ expect(page).to have_content "Application settings saved successfully"
end
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
+ it 'Terms of Service' do
+ # Already have the admin accept terms, so they don't need to accept in this spec.
+ _existing_terms = create(:term)
+ accept_terms(admin)
+
+ page.within('.as-terms') do
+ check 'Require all users to accept Terms of Service and Privacy Policy when they access GitLab.'
+ fill_in 'Terms of Service Agreement', with: 'Be nice!'
+ click_button 'Save changes'
+ end
- page.within('.as-signin') do
- check "Google"
- click_button 'Save changes'
+ expect(Gitlab::CurrentSettings.enforce_terms).to be(true)
+ expect(Gitlab::CurrentSettings.terms).to eq 'Be nice!'
+ expect(page).to have_content 'Application settings saved successfully'
end
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).not_to include('google_oauth2')
- end
+ it 'Modify oauth providers' do
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty
+
+ page.within('.as-signin') do
+ uncheck 'Google'
+ click_button 'Save changes'
+ end
- it 'Oauth providers do not raise validation errors when saving unrelated changes' do
- expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
- page.within('.as-signin') do
- uncheck 'Google'
- click_button 'Save changes'
+ page.within('.as-signin') do
+ check "Google"
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).not_to include('google_oauth2')
end
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
+ it 'Oauth providers do not raise validation errors when saving unrelated changes' do
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty
- # Remove google_oauth2 from the Omniauth strategies
- allow(Devise).to receive(:omniauth_providers).and_return([])
+ page.within('.as-signin') do
+ uncheck 'Google'
+ click_button 'Save changes'
+ end
- # Save an unrelated setting
- page.within('.as-ci-cd') do
- click_button 'Save changes'
- end
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
- end
+ # Remove google_oauth2 from the Omniauth strategies
+ allow(Devise).to receive(:omniauth_providers).and_return([])
- it 'Change Help page' do
- page.within('.as-help-page') do
- fill_in 'Help page text', with: 'Example text'
- check 'Hide marketing-related entries from help'
- fill_in 'Support page URL', with: 'http://example.com/help'
- click_button 'Save changes'
+ # Save an unrelated setting
+ page.within('.as-terms') do
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
end
- expect(Gitlab::CurrentSettings.help_page_text).to eq "Example text"
- expect(Gitlab::CurrentSettings.help_page_hide_commercial_content).to be_truthy
- expect(Gitlab::CurrentSettings.help_page_support_url).to eq "http://example.com/help"
- expect(page).to have_content "Application settings saved successfully"
- end
+ it 'Configure web terminal' do
+ page.within('.as-terminal') do
+ fill_in 'Max session time', with: 15
+ click_button 'Save changes'
+ end
- it 'Change Pages settings' do
- page.within('.as-pages') do
- fill_in 'Maximum size of pages (MB)', with: 15
- check 'Require users to prove ownership of custom domains'
- click_button 'Save changes'
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.terminal_max_session_time).to eq(15)
end
-
- expect(Gitlab::CurrentSettings.max_pages_size).to eq 15
- expect(Gitlab::CurrentSettings.pages_domain_verification_enabled?).to be_truthy
- expect(page).to have_content "Application settings saved successfully"
end
- it 'Change CI/CD settings' do
- page.within('.as-ci-cd') do
- check 'Default to Auto DevOps pipeline for all projects'
- fill_in 'Auto devops domain', with: 'domain.com'
- click_button 'Save changes'
+ context 'Integrations page' do
+ before do
+ visit integrations_admin_application_settings_path
end
- expect(Gitlab::CurrentSettings.auto_devops_enabled?).to be true
- expect(Gitlab::CurrentSettings.auto_devops_domain).to eq('domain.com')
- expect(page).to have_content "Application settings saved successfully"
- end
+ it 'Enable hiding third party offers' do
+ page.within('.as-third-party-offers') do
+ check 'Do not display offers from third parties within GitLab'
+ click_button 'Save changes'
+ end
- it 'Change Influx settings' do
- page.within('.as-influx') do
- check 'Enable InfluxDB Metrics'
- click_button 'Save changes'
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.hide_third_party_offers).to be true
end
- expect(Gitlab::CurrentSettings.metrics_enabled?).to be true
- expect(page).to have_content "Application settings saved successfully"
- end
+ it 'Change Slack Notifications Service template settings' do
+ first(:link, 'Service Templates').click
+ click_link 'Slack notifications'
+ fill_in 'Webhook', with: 'http://localhost'
+ fill_in 'Username', with: 'test_user'
+ fill_in 'service_push_channel', with: '#test_channel'
+ page.check('Notify only broken pipelines')
+ page.check('Notify only default branch')
- it 'Change Prometheus settings' do
- page.within('.as-prometheus') do
- check 'Enable Prometheus Metrics'
- click_button 'Save changes'
- end
+ check_all_events
+ click_on 'Save'
- expect(Gitlab::CurrentSettings.prometheus_metrics_enabled?).to be true
- expect(page).to have_content "Application settings saved successfully"
- end
+ expect(page).to have_content 'Application settings saved successfully'
- it 'Change Performance bar settings' do
- group = create(:group)
+ click_link 'Slack notifications'
- page.within('.as-performance-bar') do
- check 'Enable the Performance Bar'
- fill_in 'Allowed group', with: group.path
- click_on 'Save changes'
+ page.all('input[type=checkbox]').each do |checkbox|
+ expect(checkbox).to be_checked
+ end
+ expect(find_field('Webhook').value).to eq 'http://localhost'
+ expect(find_field('Username').value).to eq 'test_user'
+ expect(find('#service_push_channel').value).to eq '#test_channel'
end
+ end
- expect(page).to have_content "Application settings saved successfully"
- expect(find_field('Enable the Performance Bar')).to be_checked
- expect(find_field('Allowed group').value).to eq group.path
+ context 'CI/CD page' do
+ it 'Change CI/CD settings' do
+ visit ci_cd_admin_application_settings_path
- page.within('.as-performance-bar') do
- uncheck 'Enable the Performance Bar'
- click_on 'Save changes'
- end
+ page.within('.as-ci-cd') do
+ check 'Default to Auto DevOps pipeline for all projects'
+ fill_in 'Auto devops domain', with: 'domain.com'
+ click_button 'Save changes'
+ end
- expect(page).to have_content 'Application settings saved successfully'
- expect(find_field('Enable the Performance Bar')).not_to be_checked
- expect(find_field('Allowed group').value).to be_nil
+ expect(Gitlab::CurrentSettings.auto_devops_enabled?).to be true
+ expect(Gitlab::CurrentSettings.auto_devops_domain).to eq('domain.com')
+ expect(page).to have_content "Application settings saved successfully"
+ end
end
- it 'Change Background jobs settings' do
- page.within('.as-background') do
- fill_in 'Throttling Factor', with: 1
- click_button 'Save changes'
- end
+ context 'Reporting page' do
+ it 'Change Spam settings' do
+ visit reporting_admin_application_settings_path
+
+ page.within('.as-spam') do
+ check 'Enable reCAPTCHA'
+ fill_in 'reCAPTCHA Site Key', with: 'key'
+ fill_in 'reCAPTCHA Private Key', with: 'key'
+ fill_in 'IPs per user', with: 15
+ click_button 'Save changes'
+ end
- expect(Gitlab::CurrentSettings.sidekiq_throttling_factor).to eq(1)
- expect(page).to have_content "Application settings saved successfully"
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.recaptcha_enabled).to be true
+ expect(Gitlab::CurrentSettings.unique_ips_limit_per_user).to eq(15)
+ end
end
- it 'Change Spam settings' do
- page.within('.as-spam') do
- check 'Enable reCAPTCHA'
- fill_in 'reCAPTCHA Site Key', with: 'key'
- fill_in 'reCAPTCHA Private Key', with: 'key'
- fill_in 'IPs per user', with: 15
- click_button 'Save changes'
+ context 'Metrics and profiling page' do
+ before do
+ visit metrics_and_profiling_admin_application_settings_path
end
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.recaptcha_enabled).to be true
- expect(Gitlab::CurrentSettings.unique_ips_limit_per_user).to eq(15)
- end
+ it 'Change Influx settings' do
+ page.within('.as-influx') do
+ check 'Enable InfluxDB Metrics'
+ click_button 'Save changes'
+ end
- it 'Configure web terminal' do
- page.within('.as-terminal') do
- fill_in 'Max session time', with: 15
- click_button 'Save changes'
+ expect(Gitlab::CurrentSettings.metrics_enabled?).to be true
+ expect(page).to have_content "Application settings saved successfully"
end
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.terminal_max_session_time).to eq(15)
- end
+ it 'Change Prometheus settings' do
+ page.within('.as-prometheus') do
+ check 'Enable Prometheus Metrics'
+ click_button 'Save changes'
+ end
- it 'Enable outbound requests' do
- page.within('.as-outbound') do
- check 'Allow requests to the local network from hooks and services'
- click_button 'Save changes'
+ expect(Gitlab::CurrentSettings.prometheus_metrics_enabled?).to be true
+ expect(page).to have_content "Application settings saved successfully"
end
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.allow_local_requests_from_hooks_and_services).to be true
- end
+ it 'Change Performance bar settings' do
+ group = create(:group)
+
+ page.within('.as-performance-bar') do
+ check 'Enable the Performance Bar'
+ fill_in 'Allowed group', with: group.path
+ click_on 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+ expect(find_field('Enable the Performance Bar')).to be_checked
+ expect(find_field('Allowed group').value).to eq group.path
- it 'Enable hiding third party offers' do
- page.within('.as-third-party-offers') do
- check 'Do not display offers from third parties within GitLab'
- click_button 'Save changes'
+ page.within('.as-performance-bar') do
+ uncheck 'Enable the Performance Bar'
+ click_on 'Save changes'
+ end
+
+ expect(page).to have_content 'Application settings saved successfully'
+ expect(find_field('Enable the Performance Bar')).not_to be_checked
+ expect(find_field('Allowed group').value).to be_nil
end
- expect(page).to have_content "Application settings saved successfully"
- expect(Gitlab::CurrentSettings.hide_third_party_offers).to be true
- end
+ it 'loads usage ping payload on click', :js do
+ expect(page).to have_button 'Preview payload'
- it 'Change Slack Notifications Service template settings' do
- first(:link, 'Service Templates').click
- click_link 'Slack notifications'
- fill_in 'Webhook', with: 'http://localhost'
- fill_in 'Username', with: 'test_user'
- fill_in 'service_push_channel', with: '#test_channel'
- page.check('Notify only broken pipelines')
- page.check('Notify only default branch')
+ find('.js-usage-ping-payload-trigger').click
- check_all_events
- click_on 'Save'
+ expect(page).to have_selector '.js-usage-ping-payload'
+ expect(page).to have_button 'Hide payload'
+ end
+ end
- expect(page).to have_content 'Application settings saved successfully'
+ context 'Network page' do
+ it 'Enable outbound requests' do
+ visit network_admin_application_settings_path
- click_link 'Slack notifications'
+ page.within('.as-outbound') do
+ check 'Allow requests to the local network from hooks and services'
+ click_button 'Save changes'
+ end
- page.all('input[type=checkbox]').each do |checkbox|
- expect(checkbox).to be_checked
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.allow_local_requests_from_hooks_and_services).to be true
end
- expect(find_field('Webhook').value).to eq 'http://localhost'
- expect(find_field('Username').value).to eq 'test_user'
- expect(find('#service_push_channel').value).to eq '#test_channel'
end
- it 'Change Keys settings' do
- page.within('.as-visibility-access') do
- select 'Are forbidden', from: 'RSA SSH keys'
- select 'Are allowed', from: 'DSA SSH keys'
- select 'Must be at least 384 bits', from: 'ECDSA SSH keys'
- select 'Are forbidden', from: 'ED25519 SSH keys'
- click_on 'Save changes'
+ context 'Preferences page' do
+ before do
+ visit preferences_admin_application_settings_path
+ end
+
+ it 'Change Help page' do
+ page.within('.as-help-page') do
+ fill_in 'Help page text', with: 'Example text'
+ check 'Hide marketing-related entries from help'
+ fill_in 'Support page URL', with: 'http://example.com/help'
+ click_button 'Save changes'
+ end
+
+ expect(Gitlab::CurrentSettings.help_page_text).to eq "Example text"
+ expect(Gitlab::CurrentSettings.help_page_hide_commercial_content).to be_truthy
+ expect(Gitlab::CurrentSettings.help_page_support_url).to eq "http://example.com/help"
+ expect(page).to have_content "Application settings saved successfully"
end
- forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE.to_s
+ it 'Change Pages settings' do
+ page.within('.as-pages') do
+ fill_in 'Maximum size of pages (MB)', with: 15
+ check 'Require users to prove ownership of custom domains'
+ click_button 'Save changes'
+ end
- expect(page).to have_content 'Application settings saved successfully'
- expect(find_field('RSA SSH keys').value).to eq(forbidden)
- expect(find_field('DSA SSH keys').value).to eq('0')
- expect(find_field('ECDSA SSH keys').value).to eq('384')
- expect(find_field('ED25519 SSH keys').value).to eq(forbidden)
+ expect(Gitlab::CurrentSettings.max_pages_size).to eq 15
+ expect(Gitlab::CurrentSettings.pages_domain_verification_enabled?).to be_truthy
+ expect(page).to have_content "Application settings saved successfully"
+ end
end
def check_all_events
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index e16eae219a4..c7860bebb06 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -12,6 +12,10 @@ describe 'Admin > Users > Impersonation Tokens', :js do
find(".settings-message")
end
+ def created_impersonation_token
+ find("#created-personal-access-token").value
+ end
+
before do
sign_in(admin)
end
@@ -39,6 +43,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do
expect(active_impersonation_tokens).to have_text('api')
expect(active_impersonation_tokens).to have_text('read_user')
expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1)
+ expect(created_impersonation_token).not_to be_empty
end
end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 1db6c75b85b..f7c7a257075 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -125,6 +125,68 @@ describe "Admin::Users" do
expect(page).to have_content('Username can contain only letters, digits')
end
end
+
+ context 'with new users set to external enabled' do
+ context 'with regex to match internal user email address set', :js do
+ before do
+ stub_application_setting(user_default_external: true)
+ stub_application_setting(user_default_internal_regex: '\.internal@')
+
+ visit new_admin_user_path
+ end
+
+ def expects_external_to_be_checked
+ expect(find('#user_external')).to be_checked
+ end
+
+ def expects_external_to_be_unchecked
+ expect(find('#user_external')).not_to be_checked
+ end
+
+ def expects_warning_to_be_hidden
+ expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden'
+ end
+
+ def expects_warning_to_be_shown
+ expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden'
+ end
+
+ it 'automatically unchecks external for matching email' do
+ expects_external_to_be_checked
+ expects_warning_to_be_hidden
+
+ fill_in 'user_email', with: 'test.internal@domain.ch'
+
+ expects_external_to_be_unchecked
+ expects_warning_to_be_shown
+
+ fill_in 'user_email', with: 'test@domain.ch'
+
+ expects_external_to_be_checked
+ expects_warning_to_be_hidden
+
+ uncheck 'user_external'
+
+ expects_warning_to_be_hidden
+ end
+
+ it 'creates an internal user' do
+ user_name = 'tester1'
+ fill_in 'user_email', with: 'test.internal@domain.ch'
+ fill_in 'user_name', with: 'tester1 name'
+ fill_in 'user_username', with: user_name
+
+ expects_external_to_be_unchecked
+ expects_warning_to_be_shown
+
+ click_button 'Create user'
+
+ new_user = User.find_by(username: user_name)
+
+ expect(new_user.external).to be_falsy
+ end
+ end
+ end
end
describe "GET /admin/users/:id" do
@@ -134,6 +196,7 @@ describe "Admin::Users" do
expect(page).to have_content(user.email)
expect(page).to have_content(user.name)
+ expect(page).to have_content(user.id)
expect(page).to have_link('Block user', href: block_admin_user_path(user))
expect(page).to have_button('Delete user')
expect(page).to have_button('Delete user and contributions')
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index e658f1b6738..d04bb9acd9e 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -33,7 +33,7 @@ describe 'Admin uses repository checks' do
end
it 'to clear all repository checks', :js do
- visit admin_application_settings_path
+ visit repository_admin_application_settings_path
expect(RepositoryCheck::ClearWorker).to receive(:perform_async)
diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb
new file mode 100644
index 00000000000..a6ca0803469
--- /dev/null
+++ b/spec/features/admin/dashboard_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe 'admin visits dashboard' do
+ include ProjectForksHelper
+
+ before do
+ sign_in(create(:admin))
+ end
+
+ context 'counting forks' do
+ it 'correctly counts 2 forks of a project' do
+ project = create(:project)
+ project_fork = fork_project(project)
+ fork_project(project_fork)
+
+ # Make sure the fork_networks & fork_networks reltuples have been updated
+ # to get a correct count on postgresql
+ if Gitlab::Database.postgresql?
+ ActiveRecord::Base.connection.execute('ANALYZE fork_networks')
+ ActiveRecord::Base.connection.execute('ANALYZE fork_network_members')
+ end
+
+ visit admin_root_path
+
+ expect(page).to have_content('Forks 2')
+ end
+ end
+end
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index bd4c00d97b1..5fa1a26f1a6 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -25,35 +25,35 @@ describe "Dashboard Issues Feed" do
it "renders atom feed via personal access token" do
personal_access_token = create(:personal_access_token, user: user)
- visit issues_dashboard_path(:atom, private_token: personal_access_token.token, assignee_id: user.id)
+ visit issues_dashboard_path(:atom, private_token: personal_access_token.token, assignee_username: user.username)
expect(response_headers['Content-Type']).to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{user.name} issues")
end
it "renders atom feed via feed token" do
- visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: user.id)
+ visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_username: user.username)
expect(response_headers['Content-Type']).to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{user.name} issues")
end
it "renders atom feed with url parameters" do
- visit issues_dashboard_path(:atom, feed_token: user.feed_token, state: 'opened', assignee_id: user.id)
+ visit issues_dashboard_path(:atom, feed_token: user.feed_token, state: 'opened', assignee_username: user.username)
link = find('link[type="application/atom+xml"]')
params = CGI.parse(URI.parse(link[:href]).query)
expect(params).to include('feed_token' => [user.feed_token])
expect(params).to include('state' => ['opened'])
- expect(params).to include('assignee_id' => [user.id.to_s])
+ expect(params).to include('assignee_username' => [user.username.to_s])
end
context "issue with basic fields" do
let!(:issue2) { create(:issue, author: user, assignees: [assignee], project: project2, description: 'test desc') }
it "renders issue fields" do
- visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: assignee.id)
+ visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_username: assignee.username)
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
@@ -76,7 +76,7 @@ describe "Dashboard Issues Feed" do
end
it "renders issue label and milestone info" do
- visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: assignee.id)
+ visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_username: assignee.username)
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index eebc987499d..030993462b5 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -160,7 +160,7 @@ describe 'Issue Boards add issue modal', :js do
it 'changes button text with plural' do
page.within('.add-issues-modal') do
- all('.board-card .board-card-number').each do |el|
+ all('.board-card .js-board-card-number-container').each do |el|
el.click
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index a0af2dea3ad..baa2b1d8af5 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -44,7 +44,7 @@ describe 'Issue Boards', :js do
end
it 'creates default lists' do
- lists = ['Backlog', 'To Do', 'Doing', 'Closed']
+ lists = ['Open', 'To Do', 'Doing', 'Closed']
page.within(find('.board-blank-state')) do
click_button('Add default lists')
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index ec0ca21450a..21779336559 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -78,7 +78,7 @@ describe 'Issue Boards', :js do
end
it 'moves from bottom to top' do
- drag(from_index: 2, to_index: 0)
+ drag(from_index: 2, to_index: 0, duration: 1020)
wait_for_requests
@@ -130,7 +130,7 @@ describe 'Issue Boards', :js do
end
it 'moves to bottom of another list' do
- drag(list_from_index: 1, list_to_index: 2, to_index: 2)
+ drag(list_from_index: 1, list_to_index: 2, to_index: 2, duration: 1020)
wait_for_requests
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index 615223a2a88..2cdd3f55b50 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -106,7 +106,7 @@ describe 'Issue Boards add issue modal filtering', :js do
it 'filters by unassigned' do
set_filter('assignee')
- click_filter_link('No Assignee')
+ click_filter_link('None')
submit_filter
page.within('.add-issues-modal') do
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 0bf1ecbc433..164442a47f5 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -94,8 +94,14 @@ describe 'Issue Boards new issue', :js do
wait_for_requests
end
- it 'does not display new issue button' do
- expect(page).to have_selector('.issue-count-badge-add-button', count: 0)
+ it 'displays new issue button in open list' do
+ expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ end
+
+ it 'does not display new issue button in label list' do
+ page.within('.board:nth-child(2)') do
+ expect(page).not_to have_selector('.issue-count-badge-add-button')
+ end
end
end
end
diff --git a/spec/features/boards/reload_boards_on_browser_back_spec.rb b/spec/features/boards/reload_boards_on_browser_back_spec.rb
new file mode 100644
index 00000000000..4b4bd705a77
--- /dev/null
+++ b/spec/features/boards/reload_boards_on_browser_back_spec.rb
@@ -0,0 +1,48 @@
+require 'rails_helper'
+
+describe 'Ensure Boards do not show stale data on browser back', :js do
+ let(:project) {create(:project, :public)}
+ let(:board) {create(:board, project: project)}
+ let(:user) {create(:user)}
+
+ context 'authorized user' do
+ before do
+ project.add_maintainer(user)
+
+ sign_in(user)
+
+ visit project_board_path(project, board)
+ wait_for_requests
+
+ page.within(first('.board .issue-count-badge-count')) do
+ expect(page).to have_content('0')
+ end
+ end
+
+ it 'created issue is listed on board' do
+ visit new_project_issue_path(project)
+ wait_for_requests
+
+ fill_in 'issue_title', with: 'issue should be shown'
+
+ click_button 'Submit issue'
+
+ page.go_back
+ wait_for_requests
+
+ page.go_back
+ wait_for_requests
+
+ page.within(first('.board .issue-count-badge-count')) do
+ expect(page).to have_content('1')
+ end
+
+ page.within(first('.board-card')) do
+ issue = project.issues.find_by_title('issue should be shown')
+
+ expect(page).to have_content(issue.to_reference)
+ expect(page).to have_link(issue.title, href: issue_path(issue))
+ end
+ end
+ end
+end
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index f08946b0593..8cb9b57a049 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -64,7 +64,7 @@ describe 'Contributions Calendar', :js do
end
def selected_day_activities(visible: true)
- find('.user-calendar-activities', visible: visible).text
+ find('#js-overview .user-calendar-activities', visible: visible).text
end
before do
@@ -74,15 +74,16 @@ describe 'Contributions Calendar', :js do
describe 'calendar day selection' do
before do
visit user.username
+ page.find('.js-overview-tab a').click
wait_for_requests
end
it 'displays calendar' do
- expect(page).to have_css('.js-contrib-calendar')
+ expect(find('#js-overview')).to have_css('.js-contrib-calendar')
end
describe 'select calendar day' do
- let(:cells) { page.all('.user-contrib-cell') }
+ let(:cells) { page.all('#js-overview .user-contrib-cell') }
before do
cells[0].click
@@ -108,6 +109,7 @@ describe 'Contributions Calendar', :js do
describe 'deselect calendar day' do
before do
cells[0].click
+ page.find('.js-overview-tab a').click
wait_for_requests
end
@@ -122,6 +124,7 @@ describe 'Contributions Calendar', :js do
shared_context 'visit user page' do
before do
visit user.username
+ page.find('.js-overview-tab a').click
wait_for_requests
end
end
@@ -130,12 +133,12 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page'
it 'displays calendar activity square color for 1 contribution' do
- expect(page).to have_selector(get_cell_color_selector(contribution_count), count: 1)
+ expect(find('#js-overview')).to have_selector(get_cell_color_selector(contribution_count), count: 1)
end
it 'displays calendar activity square on the correct date' do
today = Date.today.strftime(date_format)
- expect(page).to have_selector(get_cell_date_selector(contribution_count, today), count: 1)
+ expect(find('#js-overview')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1)
end
end
@@ -150,7 +153,7 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page'
it 'displays calendar activity log' do
- expect(find('.content_list .event-note')).to have_content issue_title
+ expect(find('#js-overview .overview-content-list .event-target-title')).to have_content issue_title
end
end
end
@@ -182,17 +185,17 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page'
it 'displays calendar activity squares for both days' do
- expect(page).to have_selector(get_cell_color_selector(1), count: 2)
+ expect(find('#js-overview')).to have_selector(get_cell_color_selector(1), count: 2)
end
it 'displays calendar activity square for yesterday' do
yesterday = Date.yesterday.strftime(date_format)
- expect(page).to have_selector(get_cell_date_selector(1, yesterday), count: 1)
+ expect(find('#js-overview')).to have_selector(get_cell_date_selector(1, yesterday), count: 1)
end
it 'displays calendar activity square for today' do
today = Date.today.strftime(date_format)
- expect(page).to have_selector(get_cell_date_selector(1, today), count: 1)
+ expect(find('#js-overview')).to have_selector(get_cell_date_selector(1, today), count: 1)
end
end
end
diff --git a/spec/features/commits/user_uses_slash_commands_spec.rb b/spec/features/commits/user_uses_quick_actions_spec.rb
index 9a4b7bd2444..9a4b7bd2444 100644
--- a/spec/features/commits/user_uses_slash_commands_spec.rb
+++ b/spec/features/commits/user_uses_quick_actions_spec.rb
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 8989b2051bb..5c6c1c4fd15 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -114,33 +114,6 @@ describe 'Commits' do
expect(page).to have_content 'canceled'
end
end
-
- describe '.gitlab-ci.yml not found warning' do
- context 'ci builds enabled' do
- it "does not show warning" do
- visit pipeline_path(pipeline)
- expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
- end
-
- it 'shows warning' do
- stub_ci_pipeline_yaml_file(nil)
- visit pipeline_path(pipeline)
- expect(page).to have_content '.gitlab-ci.yml not found in this commit'
- end
- end
-
- context 'ci builds disabled' do
- before do
- stub_ci_builds_disabled
- stub_ci_pipeline_yaml_file(nil)
- visit pipeline_path(pipeline)
- end
-
- it 'does not show warning' do
- expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
- end
- end
- end
end
context "when logged as reporter" do
@@ -182,6 +155,39 @@ describe 'Commits' do
end
end
end
+
+ describe '.gitlab-ci.yml not found warning' do
+ before do
+ project.add_reporter(user)
+ end
+
+ context 'ci builds enabled' do
+ it 'does not show warning' do
+ visit pipeline_path(pipeline)
+
+ expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
+ end
+
+ it 'shows warning' do
+ stub_ci_pipeline_yaml_file(nil)
+
+ visit pipeline_path(pipeline)
+
+ expect(page).to have_content '.gitlab-ci.yml not found in this commit'
+ end
+ end
+
+ context 'ci builds disabled' do
+ it 'does not show warning' do
+ stub_ci_builds_disabled
+ stub_ci_pipeline_yaml_file(nil)
+
+ visit pipeline_path(pipeline)
+
+ expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
+ end
+ end
+ end
end
context 'viewing commits for a branch' do
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index 6a0cd848345..d31df322d10 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe 'Dashboard Archived Project' do
expect(page).not_to have_content(project.name)
end
- it 'searchs archived projects', :js do
+ it 'searches archived projects', :js do
click_button 'Last updated'
click_link 'Show archived projects'
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index d7234158fa1..0db8093411b 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -14,7 +14,7 @@ describe 'Tooltips on .timeago dates', :js do
updated_at: created_date, created_at: created_date)
sign_in user
- visit user_path(user)
+ visit user_activity_path(user)
wait_for_requests()
page.find('.js-timeago').hover
diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb
index 1c7932e7964..259f220c68b 100644
--- a/spec/features/dashboard/group_spec.rb
+++ b/spec/features/dashboard/group_spec.rb
@@ -13,16 +13,16 @@ RSpec.describe 'Dashboard Group' do
it 'creates new group', :js do
visit dashboard_groups_path
- find('.btn-new').click
- new_path = 'Samurai'
+ find('.btn-success').click
+ new_name = 'Samurai'
new_description = 'Tokugawa Shogunate'
- fill_in 'group_path', with: new_path
+ fill_in 'group_name', with: new_name
fill_in 'group_description', with: new_description
click_button 'Create group'
- expect(current_path).to eq group_path(Group.find_by(name: new_path))
- expect(page).to have_content(new_path)
+ expect(current_path).to eq group_path(Group.find_by(name: new_name))
+ expect(page).to have_content(new_name)
expect(page).to have_content(new_description)
end
end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index eceb12e91cd..e75c43d5338 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -125,7 +125,7 @@ describe 'Dashboard Groups page', :js do
end
it 'loads results for next page' do
- expect(page).to have_selector('.gl-pagination .page', count: 2)
+ expect(page).to have_selector('.gl-pagination .page-item a[role=menuitemradio]', count: 2)
# Check first page
expect(page).to have_content(group2.full_name)
@@ -134,7 +134,7 @@ describe 'Dashboard Groups page', :js do
expect(page).not_to have_selector("#group-#{group.id}")
# Go to next page
- find(".gl-pagination .page:not(.active) a").click
+ find('.gl-pagination .page-item:not(.active) a[role=menuitemradio]').click
wait_for_requests
diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb
index b431f72fcc9..fbc2e5cc3d3 100644
--- a/spec/features/dashboard/issuables_counter_spec.rb
+++ b/spec/features/dashboard/issuables_counter_spec.rb
@@ -45,11 +45,11 @@ describe 'Navigation bar counter', :use_clean_rails_memory_store_caching do
end
def issues_path
- issues_dashboard_path(assignee_id: user.id)
+ issues_dashboard_path(assignee_username: user.username)
end
def merge_requests_path
- merge_requests_dashboard_path(assignee_id: user.id)
+ merge_requests_dashboard_path(assignee_username: user.username)
end
def expect_counters(issuable_type, count)
diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb
index 95e2610dd4a..c0434f767bb 100644
--- a/spec/features/dashboard/issues_filter_spec.rb
+++ b/spec/features/dashboard/issues_filter_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe 'Dashboard Issues filtering', :js do
include Spec::Support::Helpers::Features::SortingHelpers
+ include FilteredSearchHelpers
let(:user) { create(:user) }
let(:project) { create(:project) }
@@ -25,27 +26,21 @@ describe 'Dashboard Issues filtering', :js do
context 'filtering by milestone' do
it 'shows all issues with no milestone' do
- show_milestone_dropdown
-
- click_link 'No Milestone'
+ input_filtered_search("milestone:none")
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_selector('.issue', count: 1)
end
it 'shows all issues with the selected milestone' do
- show_milestone_dropdown
-
- page.within '.dropdown-content' do
- click_link milestone.title
- end
+ input_filtered_search("milestone:%\"#{milestone.title}\"")
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_selector('.issue', count: 1)
end
it 'updates atom feed link' do
- visit_issues(milestone_title: '', assignee_id: user.id)
+ visit_issues(milestone_title: '', assignee_username: user.username)
link = find('.nav-controls a[title="Subscribe to RSS feed"]')
params = CGI.parse(URI.parse(link[:href]).query)
@@ -54,10 +49,10 @@ describe 'Dashboard Issues filtering', :js do
expect(params).to include('feed_token' => [user.feed_token])
expect(params).to include('milestone_title' => [''])
- expect(params).to include('assignee_id' => [user.id.to_s])
+ expect(params).to include('assignee_username' => [user.username.to_s])
expect(auto_discovery_params).to include('feed_token' => [user.feed_token])
expect(auto_discovery_params).to include('milestone_title' => [''])
- expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
+ expect(auto_discovery_params).to include('assignee_username' => [user.username.to_s])
end
end
@@ -66,10 +61,7 @@ describe 'Dashboard Issues filtering', :js do
let!(:label_link) { create(:label_link, label: label, target: issue) }
it 'shows all issues with the selected label' do
- page.within '.labels-filter' do
- find('.dropdown').click
- click_link label.title
- end
+ input_filtered_search("label:~#{label.title}")
page.within 'ul.content-list' do
expect(page).to have_content issue.title
@@ -80,12 +72,12 @@ describe 'Dashboard Issues filtering', :js do
context 'sorting' do
before do
- visit_issues(assignee_id: user.id)
+ visit_issues(assignee_username: user.username)
end
it 'remembers last sorting value' do
sort_by('Created date')
- visit_issues(assignee_id: user.id)
+ visit_issues(assignee_username: user.username)
expect(find('.issues-filters')).to have_content('Created date')
end
@@ -98,11 +90,6 @@ describe 'Dashboard Issues filtering', :js do
end
end
- def show_milestone_dropdown
- click_button 'Milestone'
- expect(page).to have_selector('.dropdown-content', visible: true)
- end
-
def visit_issues(*args)
visit issues_dashboard_path(*args)
end
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 4ae062f242a..9957bec0f0b 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
RSpec.describe 'Dashboard Issues' do
+ include FilteredSearchHelpers
+
let(:current_user) { create :user }
let(:user) { current_user } # Shared examples depend on this being available
let!(:public_project) { create(:project, :public) }
@@ -14,7 +16,7 @@ RSpec.describe 'Dashboard Issues' do
before do
[project, project_with_issues_disabled].each { |project| project.add_maintainer(current_user) }
sign_in(current_user)
- visit issues_dashboard_path(assignee_id: current_user.id)
+ visit issues_dashboard_path(assignee_username: current_user.username)
end
describe 'issues' do
@@ -24,26 +26,9 @@ RSpec.describe 'Dashboard Issues' do
expect(page).not_to have_content(other_issue.title)
end
- it 'shows checkmark when unassigned is selected for assignee', :js do
- find('.js-assignee-search').click
- find('li', text: 'Unassigned').click
- find('.js-assignee-search').click
-
- expect(find('li[data-user-id="0"] a.is-active')).to be_visible
- end
-
it 'shows issues when current user is author', :js do
- execute_script("document.querySelector('#assignee_id').value=''")
- find('.js-author-search', match: :first).click
-
- expect(find('li[data-user-id="null"] a.is-active')).to be_visible
-
- find('.dropdown-menu-author li a', match: :first, text: current_user.to_reference).click
- find('.js-author-search', match: :first).click
-
- page.within '.dropdown-menu-user' do
- expect(find('.dropdown-menu-author li a.is-active', match: :first, text: current_user.to_reference)).to be_visible
- end
+ reset_filters
+ input_filtered_search("author:#{current_user.to_reference}")
expect(page).to have_content(authored_issue.title)
expect(page).to have_content(authored_issue_on_public_project.title)
@@ -53,7 +38,7 @@ RSpec.describe 'Dashboard Issues' do
it 'state filter tabs work' do
find('#state-closed').click
- expect(page).to have_current_path(issues_dashboard_url(assignee_id: current_user.id, state: 'closed'), url: true)
+ expect(page).to have_current_path(issues_dashboard_url(assignee_username: current_user.username, state: 'closed'), url: true)
end
it_behaves_like "it has an RSS button with current_user's feed token"
diff --git a/spec/features/dashboard/label_filter_spec.rb b/spec/features/dashboard/label_filter_spec.rb
index 6802974c2ee..2d4659d380f 100644
--- a/spec/features/dashboard/label_filter_spec.rb
+++ b/spec/features/dashboard/label_filter_spec.rb
@@ -1,6 +1,11 @@
require 'spec_helper'
describe 'Dashboard > label filter', :js do
+ include FilteredSearchHelpers
+
+ let(:filtered_search) { find('.filtered-search') }
+ let(:filter_dropdown) { find("#js-dropdown-label .filter-dropdown") }
+
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:project2) { create(:project, name: 'test2', path: 'test2', namespace: user.namespace) }
@@ -13,17 +18,15 @@ describe 'Dashboard > label filter', :js do
sign_in(user)
visit issues_dashboard_path
+
+ init_label_search
end
context 'duplicate labels' do
it 'removes duplicate labels' do
- page.within('.labels-filter') do
- click_button 'Label'
- end
+ filtered_search.send_keys('bu')
- page.within('.dropdown-menu-labels') do
- expect(page).to have_selector('.dropdown-content a', text: 'bug', count: 1)
- end
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', text: 'bug', count: 1)
end
end
end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index f51142f5790..282bf542e77 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'Dashboard Merge Requests' do
include Spec::Support::Helpers::Features::SortingHelpers
- include FilterItemSelectHelper
+ include FilteredSearchHelpers
include ProjectForksHelper
let(:current_user) { create :user }
@@ -36,7 +36,7 @@ describe 'Dashboard Merge Requests' do
context 'no merge requests exist' do
it 'shows an empty state' do
- visit merge_requests_dashboard_path(assignee_id: current_user.id)
+ visit merge_requests_dashboard_path(assignee_username: current_user.username)
expect(page).to have_selector('.empty-state')
end
@@ -79,7 +79,7 @@ describe 'Dashboard Merge Requests' do
end
before do
- visit merge_requests_dashboard_path(assignee_id: current_user.id)
+ visit merge_requests_dashboard_path(assignee_username: current_user.username)
end
it 'shows assigned merge requests' do
@@ -92,8 +92,8 @@ describe 'Dashboard Merge Requests' do
end
it 'shows authored merge requests', :js do
- filter_item_select('Any Assignee', '.js-assignee-search')
- filter_item_select(current_user.to_reference, '.js-author-search')
+ reset_filters
+ input_filtered_search("author:#{current_user.to_reference}")
expect(page).to have_content(authored_merge_request.title)
expect(page).to have_content(authored_merge_request_from_fork.title)
@@ -104,8 +104,7 @@ describe 'Dashboard Merge Requests' do
end
it 'shows error message without filter', :js do
- filter_item_select('Any Assignee', '.js-assignee-search')
- filter_item_select('Any Author', '.js-author-search')
+ reset_filters
expect(page).to have_content('Please select at least one filter to see results')
end
@@ -113,7 +112,7 @@ describe 'Dashboard Merge Requests' do
it 'shows sorted merge requests' do
sort_by('Created date')
- visit merge_requests_dashboard_path(assignee_id: current_user.id)
+ visit merge_requests_dashboard_path(assignee_username: current_user.username)
expect(find('.issues-filters')).to have_content('Created date')
end
diff --git a/spec/features/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb
deleted file mode 100644
index 00373050aeb..00000000000
--- a/spec/features/dashboard/milestone_filter_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'spec_helper'
-
-describe 'Dashboard > milestone filter', :js do
- include FilterItemSelectHelper
-
- let(:user) { create(:user) }
- let(:project) { create(:project, name: 'test', namespace: user.namespace) }
- let(:milestone) { create(:milestone, title: 'v1.0', project: project) }
- let(:milestone2) { create(:milestone, title: 'v2.0', project: project) }
- let!(:issue) { create :issue, author: user, project: project, milestone: milestone }
- let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 }
-
- dropdown_toggle_button = '.js-milestone-select'
-
- before do
- sign_in(user)
- end
-
- context 'default state' do
- it 'shows issues with Any Milestone' do
- visit issues_dashboard_path(author_id: user.id)
-
- page.all('.issue-info').each do |issue_info|
- expect(issue_info.text).to match(/v\d.0/)
- end
- end
- end
-
- context 'filtering by milestone' do
- before do
- visit issues_dashboard_path(author_id: user.id)
- filter_item_select('v1.0', dropdown_toggle_button)
- find(dropdown_toggle_button).click
- wait_for_requests
- end
-
- it 'shows issues with Milestone v1.0' do
- expect(find('.issues-list')).to have_selector('.issue', count: 1)
- expect(find('.milestone-filter .dropdown-content')).to have_selector('a.is-active', count: 1)
- end
-
- it 'should not change active Milestone unless clicked' do
- page.within '.milestone-filter' do
- expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
-
- find('.dropdown-menu-close').click
-
- expect(page).not_to have_selector('.dropdown.open')
-
- find(dropdown_toggle_button).click
-
- expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
- expect(find('.dropdown-content a.is-active')).to have_content('v1.0')
- end
- end
- end
-
- context 'with milestone filter in URL' do
- before do
- visit issues_dashboard_path(author_id: user.id, milestone_title: milestone.title)
- find(dropdown_toggle_button).click
- wait_for_requests
- end
-
- it 'has milestone selected' do
- expect(find('.milestone-filter .dropdown-content')).to have_css('.is-active', text: milestone.title)
- end
-
- it 'removes milestone filter from URL after clicking "Any Milestone"' do
- expect(current_url).to include("milestone_title=#{milestone.title}")
-
- find('.milestone-filter .dropdown-content li', text: 'Any Milestone').click
-
- expect(current_url).not_to include('milestone_title')
- end
- end
-end
diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb
index 0db69432702..8fb2e37e269 100644
--- a/spec/features/dashboard/milestones_spec.rb
+++ b/spec/features/dashboard/milestones_spec.rb
@@ -13,10 +13,13 @@ describe 'Dashboard > Milestones' do
describe 'as logged-in user' do
let(:user) { create(:user) }
+ let(:group) { create(:group) }
let(:project) { create(:project, namespace: user.namespace) }
let!(:milestone) { create(:milestone, project: project) }
+ let!(:milestone2) { create(:milestone, group: group) }
+
before do
- project.add_maintainer(user)
+ group.add_developer(user)
sign_in(user)
visit dashboard_milestones_path
end
@@ -24,6 +27,7 @@ describe 'Dashboard > Milestones' do
it 'sees milestones' do
expect(current_path).to eq dashboard_milestones_path
expect(page).to have_content(milestone.title)
+ expect(page).to have_content(group.name)
end
end
end
diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb
index 498775acff3..16919fe63ad 100644
--- a/spec/features/dashboard/project_member_activity_index_spec.rb
+++ b/spec/features/dashboard/project_member_activity_index_spec.rb
@@ -14,14 +14,15 @@ describe 'Project member activity', :js do
wait_for_requests
end
- subject { page.find(".event-title").text }
-
context 'when a user joins the project' do
before do
visit_activities_and_wait_with_event(Event::JOINED)
end
- it { is_expected.to eq("#{user.name} joined project") }
+ it "presents the correct message" do
+ expect(page.find('.event-user-info').text).to eq("#{user.name} #{user.to_reference}")
+ expect(page.find('.event-title').text).to eq("joined project")
+ end
end
context 'when a user leaves the project' do
@@ -29,7 +30,10 @@ describe 'Project member activity', :js do
visit_activities_and_wait_with_event(Event::LEFT)
end
- it { is_expected.to eq("#{user.name} left project") }
+ it "presents the correct message" do
+ expect(page.find('.event-user-info').text).to eq("#{user.name} #{user.to_reference}")
+ expect(page.find('.event-title').text).to eq("left project")
+ end
end
context 'when a users membership expires for the project' do
@@ -38,8 +42,8 @@ describe 'Project member activity', :js do
end
it "presents the correct message" do
- message = "#{user.name} removed due to membership expiration from project"
- is_expected.to eq(message)
+ expect(page.find('.event-user-info').text).to eq("#{user.name} #{user.to_reference}")
+ expect(page.find('.event-title').text).to eq("removed due to membership expiration from project")
end
end
end
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index 4daacc61d85..975b7944741 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -103,6 +103,14 @@ describe 'Dashboard Projects' do
expect(page).not_to have_content(project.name)
expect(page).to have_content(project3.name)
end
+
+ it 'sorts projects by most stars when sorting by most stars' do
+ project_with_most_stars = create(:project, namespace: user.namespace, star_count: 10)
+
+ visit dashboard_projects_path(sort: :stars_desc)
+
+ expect(first('.project-row')).to have_content(project_with_most_stars.title)
+ end
end
context 'when on Starred projects tab' do
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index e5c5ab9c039..31a1dcf826d 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -2,8 +2,12 @@ require 'spec_helper'
describe 'Dashboard shortcuts', :js do
context 'logged in' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
before do
- sign_in(create(:user))
+ project.add_developer(user)
+ sign_in(user)
visit root_dashboard_path
end
@@ -50,6 +54,6 @@ describe 'Dashboard shortcuts', :js do
end
def check_page_title(title)
- expect(find('.breadcrumbs-sub-title')).to have_content(title)
+ expect(find('.page-title')).to have_content(title)
end
end
diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb
index 0a88988ea09..259f22139ef 100644
--- a/spec/features/explore/new_menu_spec.rb
+++ b/spec/features/explore/new_menu_spec.rb
@@ -20,7 +20,7 @@ describe 'Top Plus Menu', :js do
click_topmenuitem("New project")
- expect(page).to have_content('Project path')
+ expect(page).to have_content('Project URL')
expect(page).to have_content('Project name')
end
@@ -29,7 +29,7 @@ describe 'Top Plus Menu', :js do
click_topmenuitem("New group")
- expect(page).to have_content('Group path')
+ expect(page).to have_content('Group URL')
expect(page).to have_content('Group name')
end
@@ -79,7 +79,7 @@ describe 'Top Plus Menu', :js do
click_topmenuitem("New subgroup")
- expect(page).to have_content('Group path')
+ expect(page).to have_content('Group URL')
expect(page).to have_content('Group name')
end
@@ -92,7 +92,7 @@ describe 'Top Plus Menu', :js do
find('.header-new-group-project a').click
end
- expect(page).to have_content('Project path')
+ expect(page).to have_content('Project URL')
expect(page).to have_content('Project name')
end
end
diff --git a/spec/features/groups/board_sidebar_spec.rb b/spec/features/groups/board_sidebar_spec.rb
new file mode 100644
index 00000000000..9f597efa7b7
--- /dev/null
+++ b/spec/features/groups/board_sidebar_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'Group Issue Boards', :js do
+ include BoardHelpers
+
+ let(:group) { create(:group) }
+ let(:user) { create(:group_member, user: create(:user), group: group ).user }
+ let!(:project_1) { create(:project, :public, group: group) }
+ let!(:project_2) { create(:project, :public, group: group) }
+ let!(:project_1_label) { create(:label, project: project_1, name: 'Development 1') }
+ let!(:project_2_label) { create(:label, project: project_2, name: 'Development 2') }
+ let!(:group_label) { create(:group_label, title: 'Bug', description: 'Fusce consequat', group: group) }
+ let!(:issue_1) { create(:labeled_issue, project: project_1, relative_position: 1) }
+ let!(:issue_2) { create(:labeled_issue, project: project_2, relative_position: 2) }
+ let(:board) { create(:board, group: group) }
+ let!(:list) { create(:list, board: board, label: project_1_label, position: 0) }
+ let(:card) { find('.board:nth-child(1)').first('.board-card') }
+
+ before do
+ sign_in(user)
+
+ visit group_board_path(group, board)
+ wait_for_requests
+ end
+
+ context 'labels' do
+ it 'only shows valid labels for the issue project and group' do
+ click_card(card)
+
+ page.within('.labels') do
+ click_link 'Edit'
+
+ wait_for_requests
+
+ page.within('.selectbox') do
+ expect(page).to have_content(project_1_label.title)
+ expect(page).to have_content(group_label.title)
+ expect(page).not_to have_content(project_2_label.title)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 59254ecc982..2cdbdcffbc3 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -98,6 +98,22 @@ describe 'Edit group settings' do
end
end
+ describe 'edit group path' do
+ it 'has a root URL label for top-level group' do
+ visit edit_group_path(group)
+
+ expect(find(:css, '.group-root-path').text).to eq(root_url)
+ end
+
+ it 'has a parent group URL label for a subgroup group', :postgresql do
+ subgroup = create(:group, parent: group)
+
+ visit edit_group_path(subgroup)
+
+ expect(find(:css, '.group-root-path').text).to eq(group_url(subgroup.parent) + '/')
+ end
+ end
+
def update_path(new_group_path)
visit edit_group_path(group)
@@ -109,7 +125,7 @@ describe 'Edit group settings' do
def save_group
page.within('.gs-general') do
- click_button 'Save group'
+ click_button 'Save changes'
end
end
end
diff --git a/spec/features/groups/labels/search_labels_spec.rb b/spec/features/groups/labels/search_labels_spec.rb
new file mode 100644
index 00000000000..14b88a561b1
--- /dev/null
+++ b/spec/features/groups/labels/search_labels_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Search for labels', :js do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let!(:label1) { create(:group_label, title: 'Foo', description: 'Lorem ipsum', group: group) }
+ let!(:label2) { create(:group_label, title: 'Bar', description: 'Fusce consequat', group: group) }
+
+ before do
+ group.add_maintainer(user)
+ sign_in(user)
+
+ visit group_labels_path(group)
+ end
+
+ it 'searches for label by title' do
+ fill_in 'label-search', with: 'Bar'
+ find('#label-search').native.send_keys(:enter)
+
+ expect(page).to have_content(label2.title)
+ expect(page).to have_content(label2.description)
+ expect(page).not_to have_content(label1.title)
+ expect(page).not_to have_content(label1.description)
+ end
+
+ it 'searches for label by description' do
+ fill_in 'label-search', with: 'Lorem'
+ find('#label-search').native.send_keys(:enter)
+
+ expect(page).to have_content(label1.title)
+ expect(page).to have_content(label1.description)
+ expect(page).not_to have_content(label2.title)
+ expect(page).not_to have_content(label2.description)
+ end
+
+ it 'shows nothing found message' do
+ fill_in 'label-search', with: 'nonexistent'
+ find('#label-search').native.send_keys(:enter)
+
+ expect(page).to have_content('No labels with such name or description')
+ expect(page).not_to have_content(label1.title)
+ expect(page).not_to have_content(label1.description)
+ expect(page).not_to have_content(label2.title)
+ expect(page).not_to have_content(label2.description)
+ end
+end
diff --git a/spec/features/groups/labels/sort_labels_spec.rb b/spec/features/groups/labels/sort_labels_spec.rb
new file mode 100644
index 00000000000..2aea4d77675
--- /dev/null
+++ b/spec/features/groups/labels/sort_labels_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Sort labels', :js do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let!(:label1) { create(:group_label, title: 'Foo', description: 'Lorem ipsum', group: group) }
+ let!(:label2) { create(:group_label, title: 'Bar', description: 'Fusce consequat', group: group) }
+
+ before do
+ group.add_maintainer(user)
+ sign_in(user)
+
+ visit group_labels_path(group)
+ end
+
+ it 'sorts by title by default' do
+ expect(page).to have_button('Name')
+
+ # assert default sorting
+ within '.other-labels' do
+ expect(page.all('.label-list-item').first.text).to include('Bar')
+ expect(page.all('.label-list-item').last.text).to include('Foo')
+ end
+ end
+
+ it 'sorts by date' do
+ click_button 'Name'
+
+ sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text)
+
+ expect(sort_options[0]).to eq('Name')
+ expect(sort_options[1]).to eq('Name, descending')
+ expect(sort_options[2]).to eq('Last created')
+ expect(sort_options[3]).to eq('Oldest created')
+ expect(sort_options[4]).to eq('Last updated')
+ expect(sort_options[5]).to eq('Oldest updated')
+
+ click_link 'Name, descending'
+
+ # assert default sorting
+ within '.other-labels' do
+ expect(page.all('.label-list-item').first.text).to include('Foo')
+ expect(page.all('.label-list-item').last.text).to include('Bar')
+ end
+ end
+end
diff --git a/spec/features/groups/labels/subscription_spec.rb b/spec/features/groups/labels/subscription_spec.rb
index d9543bfa97f..22b51b297a6 100644
--- a/spec/features/groups/labels/subscription_spec.rb
+++ b/spec/features/groups/labels/subscription_spec.rb
@@ -3,7 +3,8 @@ require 'spec_helper'
describe 'Labels subscription' do
let(:user) { create(:user) }
let(:group) { create(:group) }
- let!(:feature) { create(:group_label, group: group, title: 'feature') }
+ let!(:label1) { create(:group_label, group: group, title: 'foo') }
+ let!(:label2) { create(:group_label, group: group, title: 'bar') }
context 'when signed in' do
before do
@@ -14,9 +15,9 @@ describe 'Labels subscription' do
it 'users can subscribe/unsubscribe to group labels', :js do
visit group_labels_path(group)
- expect(page).to have_content('feature')
+ expect(page).to have_content(label1.title)
- within "#group_label_#{feature.id}" do
+ within "#group_label_#{label1.id}" do
expect(page).not_to have_button 'Unsubscribe'
click_button 'Subscribe'
@@ -30,15 +31,48 @@ describe 'Labels subscription' do
expect(page).not_to have_button 'Unsubscribe'
end
end
+
+ context 'subscription filter' do
+ before do
+ visit group_labels_path(group)
+ end
+
+ it 'shows only subscribed labels' do
+ label1.subscribe(user)
+
+ click_subscribed_tab
+
+ page.within('.labels-container') do
+ expect(page).to have_content label1.title
+ end
+ end
+
+ it 'shows no subscribed labels message' do
+ click_subscribed_tab
+
+ page.within('.labels-container') do
+ expect(page).not_to have_content label1.title
+ expect(page).to have_content('You do not have any subscriptions yet')
+ end
+ end
+ end
end
context 'when not signed in' do
- it 'users can not subscribe/unsubscribe to labels' do
+ before do
visit group_labels_path(group)
+ end
- expect(page).to have_content 'feature'
+ it 'users can not subscribe/unsubscribe to labels' do
+ expect(page).to have_content label1.title
expect(page).not_to have_button('Subscribe')
end
+
+ it 'does not show subscribed tab' do
+ page.within('.nav-tabs') do
+ expect(page).not_to have_link 'Subscribed'
+ end
+ end
end
def click_link_on_dropdown(text)
@@ -48,4 +82,10 @@ describe 'Labels subscription' do
find('a.js-subscribe-button', text: text).click
end
end
+
+ def click_subscribed_tab
+ page.within('.nav-tabs') do
+ click_link 'Subscribed'
+ end
+ end
end
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 80df0618a6a..174840794ed 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -23,17 +23,17 @@ describe 'Group milestones' do
description.native.send_keys('')
- click_link('Preview')
+ click_button('Preview')
preview = find('.js-md-preview')
expect(preview).to have_content('Nothing to preview.')
- click_link('Write')
+ click_button('Write')
description.native.send_keys(':+1: Nice')
- click_link('Preview')
+ click_button('Preview')
expect(preview).to have_css('gl-emoji')
expect(find('#milestone_description', visible: false)).not_to be_visible
@@ -95,9 +95,9 @@ describe 'Group milestones' do
end
it 'counts milestones correctly' do
- expect(find('.top-area .active .badge').text).to eq("2")
- expect(find('.top-area .closed .badge').text).to eq("2")
- expect(find('.top-area .all .badge').text).to eq("4")
+ expect(find('.top-area .active .badge').text).to eq("3")
+ expect(find('.top-area .closed .badge').text).to eq("3")
+ expect(find('.top-area .all .badge').text).to eq("6")
end
it 'lists legacy group milestones and group milestones' do
diff --git a/spec/features/groups/settings/ci_cd_spec.rb b/spec/features/groups/settings/ci_cd_spec.rb
new file mode 100644
index 00000000000..d422fd18346
--- /dev/null
+++ b/spec/features/groups/settings/ci_cd_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Group CI/CD settings' do
+ include WaitForRequests
+
+ let(:user) {create(:user)}
+ let(:group) {create(:group)}
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ end
+
+ describe 'runners registration token' do
+ let!(:token) { group.runners_token }
+
+ before do
+ visit group_settings_ci_cd_path(group)
+ end
+
+ it 'has a registration token' do
+ expect(page.find('#registration_token')).to have_content(token)
+ end
+
+ describe 'reload registration token' do
+ let(:page_token) { find('#registration_token').text }
+
+ before do
+ click_button 'Reset runners registration token'
+ end
+
+ it 'changes registration token' do
+ expect(page_token).not_to eq token
+ end
+ end
+ end
+end
diff --git a/spec/features/groups/settings/group_badges_spec.rb b/spec/features/groups/settings/group_badges_spec.rb
index 070a4a31ffa..a5c8dbf18d0 100644
--- a/spec/features/groups/settings/group_badges_spec.rb
+++ b/spec/features/groups/settings/group_badges_spec.rb
@@ -14,7 +14,7 @@ describe 'Group Badges' do
group.add_owner(user)
sign_in(user)
- visit(group_settings_badges_path(group))
+ visit(edit_group_path(group))
end
it 'shows a list of badges', :js do
diff --git a/spec/features/groups/share_lock_spec.rb b/spec/features/groups/share_lock_spec.rb
index 5bbe77019ca..704d9f12888 100644
--- a/spec/features/groups/share_lock_spec.rb
+++ b/spec/features/groups/share_lock_spec.rb
@@ -60,14 +60,14 @@ describe 'Group share with group lock' do
def enable_group_lock
page.within('.gs-permissions') do
check 'group_share_with_group_lock'
- click_on 'Save group'
+ click_on 'Save changes'
end
end
def disable_group_lock
page.within('.gs-permissions') do
uncheck 'group_share_with_group_lock'
- click_on 'Save group'
+ click_on 'Save changes'
end
end
end
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index ac961e98a61..4e6f73ef58a 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -101,4 +101,25 @@ describe 'Group show page' do
expect(page).to have_emoji('smile')
end
end
+
+ context 'where group has projects' do
+ let(:user) { create(:user) }
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ end
+
+ it 'allows users to sorts projects by most stars', :js do
+ project1 = create(:project, namespace: group, star_count: 2)
+ project2 = create(:project, namespace: group, star_count: 3)
+ project3 = create(:project, namespace: group, star_count: 0)
+
+ visit group_path(group, sort: :stars_desc)
+
+ expect(find('.group-row:nth-child(1) .namespace-title > a')).to have_content(project2.title)
+ expect(find('.group-row:nth-child(2) .namespace-title > a')).to have_content(project1.title)
+ expect(find('.group-row:nth-child(3) .namespace-title > a')).to have_content(project3.title)
+ end
+ end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index e62bd6f8187..d01fc04311a 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -1,13 +1,15 @@
require 'spec_helper'
describe 'Group' do
+ let(:user) { create(:admin) }
+
before do
- sign_in(create(:admin))
+ sign_in(user)
end
matcher :have_namespace_error_message do
match do |page|
- page.has_content?("Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-' or end in '.', '.git' or '.atom'.")
+ page.has_content?("Group URL can contain only letters, digits, '_', '-' and '.'. Cannot start with '-' or end in '.', '.git' or '.atom'.")
end
end
@@ -16,9 +18,27 @@ describe 'Group' do
visit new_group_path
end
+ describe 'as a non-admin' do
+ let(:user) { create(:user) }
+
+ it 'creates a group and persists visibility radio selection', :js do
+ stub_application_setting(default_group_visibility: :private)
+
+ fill_in 'Group name', with: 'test-group'
+ find("input[name='group[visibility_level]'][value='#{Gitlab::VisibilityLevel::PUBLIC}']").click
+ click_button 'Create group'
+
+ group = Group.find_by(name: 'test-group')
+
+ expect(group.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ expect(current_path).to eq(group_path(group))
+ expect(page).to have_selector '.visibility-icon .fa-globe'
+ end
+ end
+
describe 'with space in group path' do
it 'renders new group form with validation errors' do
- fill_in 'Group path', with: 'space group'
+ fill_in 'Group URL', with: 'space group'
click_button 'Create group'
expect(current_path).to eq(groups_path)
@@ -28,7 +48,7 @@ describe 'Group' do
describe 'with .atom at end of group path' do
it 'renders new group form with validation errors' do
- fill_in 'Group path', with: 'atom_group.atom'
+ fill_in 'Group URL', with: 'atom_group.atom'
click_button 'Create group'
expect(current_path).to eq(groups_path)
@@ -38,7 +58,7 @@ describe 'Group' do
describe 'with .git at end of group path' do
it 'renders new group form with validation errors' do
- fill_in 'Group path', with: 'git_group.git'
+ fill_in 'Group URL', with: 'git_group.git'
click_button 'Create group'
expect(current_path).to eq(groups_path)
@@ -94,7 +114,8 @@ describe 'Group' do
end
it 'creates a nested group' do
- fill_in 'Group path', with: 'bar'
+ fill_in 'Group name', with: 'bar'
+ fill_in 'Group URL', with: 'bar'
click_button 'Create group'
expect(current_path).to eq(group_path('foo/bar'))
@@ -112,7 +133,8 @@ describe 'Group' do
visit new_group_path(group, parent_id: group.id)
- fill_in 'Group path', with: 'bar'
+ fill_in 'Group name', with: 'bar'
+ fill_in 'Group URL', with: 'bar'
click_button 'Create group'
expect(current_path).to eq(group_path('foo/bar'))
@@ -140,10 +162,13 @@ describe 'Group' do
visit path
end
+ it_behaves_like 'dirty submit form', [{ form: '.js-general-settings-form', input: 'input[name="group[name]"]' },
+ { form: '.js-general-permissions-form', input: 'input[name="group[two_factor_grace_period]"]' }]
+
it 'saves new settings' do
page.within('.gs-general') do
fill_in 'group_name', with: new_name
- click_button 'Save group'
+ click_button 'Save changes'
end
expect(page).to have_content 'successfully updated'
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
index e381d073804..a90cdd8d920 100644
--- a/spec/features/import/manifest_import_spec.rb
+++ b/spec/features/import/manifest_import_spec.rb
@@ -22,7 +22,7 @@ describe 'Import multiple repositories by uploading a manifest file', :js, :post
expect(page).to have_content('https://android-review.googlesource.com/platform/build/blueprint')
end
- it 'imports succesfully imports a project' do
+ it 'imports successfully imports a project' do
visit new_import_manifest_path
attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
diff --git a/spec/features/instance_statistics/cohorts_spec.rb b/spec/features/instance_statistics/cohorts_spec.rb
index 81fc5eff980..40e65515ceb 100644
--- a/spec/features/instance_statistics/cohorts_spec.rb
+++ b/spec/features/instance_statistics/cohorts_spec.rb
@@ -3,6 +3,8 @@ require 'rails_helper'
describe 'Cohorts page' do
before do
sign_in(create(:admin))
+
+ stub_application_setting(usage_ping_enabled: true)
end
it 'See users count per month' do
diff --git a/spec/features/instance_statistics/conversational_development_index_spec.rb b/spec/features/instance_statistics/conversational_development_index_spec.rb
index d441a7a5af9..d8be554d734 100644
--- a/spec/features/instance_statistics/conversational_development_index_spec.rb
+++ b/spec/features/instance_statistics/conversational_development_index_spec.rb
@@ -5,14 +5,32 @@ describe 'Conversational Development Index' do
sign_in(create(:admin))
end
+ it 'has dismissable intro callout', :js do
+ visit instance_statistics_conversational_development_index_index_path
+
+ expect(page).to have_content 'Introducing Your Conversational Development Index'
+
+ find('.js-close-callout').click
+
+ expect(page).not_to have_content 'Introducing Your Conversational Development Index'
+ end
+
context 'when usage ping is disabled' do
- it 'shows empty state' do
+ before do
stub_application_setting(usage_ping_enabled: false)
+ end
+ it 'shows empty state' do
visit instance_statistics_conversational_development_index_index_path
expect(page).to have_content('Usage ping is not enabled')
end
+
+ it 'hides the intro callout' do
+ visit instance_statistics_conversational_development_index_index_path
+
+ expect(page).not_to have_content 'Introducing Your Conversational Development Index'
+ end
end
context 'when there is no data to display' do
diff --git a/spec/features/instance_statistics/instance_statistics.rb b/spec/features/instance_statistics/instance_statistics.rb
new file mode 100644
index 00000000000..d03e6e68075
--- /dev/null
+++ b/spec/features/instance_statistics/instance_statistics.rb
@@ -0,0 +1,23 @@
+require 'rails_helper'
+
+describe 'Cohorts page', :js do
+ before do
+ sign_in(create(:admin))
+ end
+
+ it 'hides cohorts nav button when usage ping is disabled' do
+ stub_application_setting(usage_ping_enabled: false)
+
+ visit instance_statistics_root_path
+
+ expect(find('.nav-sidebar')).not_to have_content('Cohorts')
+ end
+
+ it 'shows cohorts nav button when usage ping is enabled' do
+ stub_application_setting(usage_ping_enabled: true)
+
+ visit instance_statistics_root_path
+
+ expect(find('.nav-sidebar')).to have_content('Cohorts')
+ end
+end
diff --git a/spec/features/issuables/close_reopen_report_toggle_spec.rb b/spec/features/issuables/close_reopen_report_toggle_spec.rb
index de6f5fe1560..26c44baeb21 100644
--- a/spec/features/issuables/close_reopen_report_toggle_spec.rb
+++ b/spec/features/issuables/close_reopen_report_toggle_spec.rb
@@ -56,6 +56,24 @@ describe 'Issuables Close/Reopen/Report toggle' do
end
it_behaves_like 'an issuable close/reopen/report toggle'
+
+ context 'when the issue is closed and locked' do
+ let(:issuable) { create(:issue, :closed, :locked, project: project) }
+
+ it 'hides the reopen button' do
+ expect(page).not_to have_link('Reopen issue')
+ end
+
+ context 'when the issue author is the current user' do
+ before do
+ issuable.update(author: user)
+ end
+
+ it 'hides the reopen button' do
+ expect(page).not_to have_link('Reopen issue')
+ end
+ end
+ end
end
context 'when user doesnt have permission to update' do
@@ -93,6 +111,28 @@ describe 'Issuables Close/Reopen/Report toggle' do
end
it_behaves_like 'an issuable close/reopen/report toggle'
+
+ context 'when the merge request is merged' do
+ let(:issuable) { create(:merge_request, :merged, source_project: project) }
+
+ it 'shows only the `Report abuse` and `Edit` button' do
+ expect(page).to have_link('Report abuse')
+ expect(page).to have_link('Edit')
+ expect(page).not_to have_link('Close merge request')
+ expect(page).not_to have_link('Reopen merge request')
+ end
+
+ context 'when the merge request author is the current user' do
+ let(:issuable) { create(:merge_request, :merged, source_project: project, author: user) }
+
+ it 'shows only the `Edit` button' do
+ expect(page).to have_link('Edit')
+ expect(page).not_to have_link('Report abuse')
+ expect(page).not_to have_link('Close merge request')
+ expect(page).not_to have_link('Reopen merge request')
+ end
+ end
+ end
end
context 'when user doesnt have permission to update' do
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
deleted file mode 100644
index bf60b18873c..00000000000
--- a/spec/features/issues/award_emoji_spec.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-require 'rails_helper'
-
-describe 'Awards Emoji' do
- let!(:project) { create(:project, :public) }
- let!(:user) { create(:user) }
- let(:issue) do
- create(:issue,
- assignees: [user],
- project: project)
- end
-
- context 'authorized user' do
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
- describe 'visiting an issue with a legacy award emoji that is not valid anymore' do
- before do
- # The `heart_tip` emoji is not valid anymore so we need to skip validation
- issue.award_emoji.build(user: user, name: 'heart_tip').save!(validate: false)
- visit project_issue_path(project, issue)
- wait_for_requests
- end
-
- # Regression test: https://gitlab.com/gitlab-org/gitlab-ce/issues/29529
- it 'does not shows a 500 page', :js do
- expect(page).to have_text(issue.title)
- end
- end
-
- describe 'Click award emoji from issue#show' do
- let!(:note) { create(:note_on_issue, noteable: issue, project: issue.project, note: "Hello world") }
-
- before do
- visit project_issue_path(project, issue)
- wait_for_requests
- end
-
- it 'increments the thumbsdown emoji', :js do
- find('[data-name="thumbsdown"]').click
- wait_for_requests
- expect(thumbsdown_emoji).to have_text("1")
- end
-
- context 'click the thumbsup emoji' do
- it 'increments the thumbsup emoji', :js do
- find('[data-name="thumbsup"]').click
- wait_for_requests
- expect(thumbsup_emoji).to have_text("1")
- end
-
- it 'decrements the thumbsdown emoji', :js do
- expect(thumbsdown_emoji).to have_text("0")
- end
- end
-
- context 'click the thumbsdown emoji' do
- it 'increments the thumbsdown emoji', :js do
- find('[data-name="thumbsdown"]').click
- wait_for_requests
- expect(thumbsdown_emoji).to have_text("1")
- end
-
- it 'decrements the thumbsup emoji', :js do
- expect(thumbsup_emoji).to have_text("0")
- end
- end
-
- it 'toggles the smiley emoji on a note', :js do
- toggle_smiley_emoji(true)
-
- within('.note-body') do
- expect(find(emoji_counter)).to have_text("1")
- end
-
- toggle_smiley_emoji(false)
-
- within('.note-body') do
- expect(page).not_to have_selector(emoji_counter)
- end
- end
-
- context 'execute /award quick action' do
- it 'toggles the emoji award on noteable', :js do
- execute_quick_action('/award :100:')
-
- expect(find(noteable_award_counter)).to have_text("1")
-
- execute_quick_action('/award :100:')
-
- expect(page).not_to have_selector(noteable_award_counter)
- end
- end
- end
- end
-
- context 'unauthorized user', :js do
- before do
- visit project_issue_path(project, issue)
- end
-
- it 'has disabled emoji button' do
- expect(first('.award-control')[:class]).to have_text('disabled')
- end
- end
-
- def execute_quick_action(cmd)
- within('.js-main-target-form') do
- fill_in 'note[note]', with: cmd
- click_button 'Comment'
- end
-
- wait_for_requests
- end
-
- def thumbsup_emoji
- page.all(emoji_counter).first
- end
-
- def thumbsdown_emoji
- page.all(emoji_counter).last
- end
-
- def emoji_counter
- 'span.js-counter'
- end
-
- def noteable_award_counter
- ".awards .active"
- end
-
- def toggle_smiley_emoji(status)
- within('.note') do
- find('.note-emoji-button').click
- end
-
- unless status
- first('[data-name="smiley"]').click
- else
- find('[data-name="smiley"]').click
- end
-
- wait_for_requests
- end
-end
diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb
deleted file mode 100644
index e53a4ce49c7..00000000000
--- a/spec/features/issues/award_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'rails_helper'
-
-describe 'Issue awards', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public) }
- let(:issue) { create(:issue, project: project) }
-
- describe 'logged in' do
- before do
- sign_in(user)
- visit project_issue_path(project, issue)
- wait_for_requests
- end
-
- it 'adds award to issue' do
- first('.js-emoji-btn').click
- expect(page).to have_selector('.js-emoji-btn.active')
- expect(first('.js-emoji-btn')).to have_content '1'
-
- visit project_issue_path(project, issue)
- expect(first('.js-emoji-btn')).to have_content '1'
- end
-
- it 'removes award from issue' do
- first('.js-emoji-btn').click
- find('.js-emoji-btn.active').click
- expect(first('.js-emoji-btn')).to have_content '0'
-
- visit project_issue_path(project, issue)
- expect(first('.js-emoji-btn')).to have_content '0'
- end
-
- it 'only has one menu on the page' do
- first('.js-add-award').click
- expect(page).to have_selector('.emoji-menu')
-
- expect(page).to have_selector('.emoji-menu', count: 1)
- end
- end
-
- describe 'logged out' do
- before do
- visit project_issue_path(project, issue)
- wait_for_requests
- end
-
- it 'does not see award menu button' do
- expect(page).not_to have_selector('.js-award-holder')
- end
- end
-end
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index d011d2545bb..e910fb54d23 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -156,13 +156,21 @@ describe 'Dropdown assignee', :js do
expect_filtered_search_input_empty
end
- it 'selects `no assignee`' do
- find('#js-dropdown-assignee .filter-dropdown-item', text: 'No Assignee').click
+ it 'selects `None`' do
+ find('#js-dropdown-assignee .filter-dropdown-item', text: 'None').click
expect(page).to have_css(js_dropdown_assignee, visible: false)
expect_tokens([assignee_token('none')])
expect_filtered_search_input_empty
end
+
+ it 'selects `Any`' do
+ find('#js-dropdown-assignee .filter-dropdown-item', text: 'Any').click
+
+ expect(page).to have_css(js_dropdown_assignee, visible: false)
+ expect_tokens([assignee_token('any')])
+ expect_filtered_search_input_empty
+ end
end
describe 'selecting from dropdown without Ajax call' do
diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
index be229e8aa7d..97dd0afd002 100644
--- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
@@ -20,7 +20,7 @@ describe 'Dropdown emoji', :js do
end
def dropdown_emoji_size
- page.all('#js-dropdown-my-reaction .filter-dropdown .filter-dropdown-item').size
+ all('gl-emoji[data-name]').size
end
def click_emoji(text)
@@ -92,7 +92,7 @@ describe 'Dropdown emoji', :js do
it 'shows the most populated emoji at top of dropdown' do
send_keys_to_filtered_search('my-reaction:')
- expect(first('#js-dropdown-my-reaction li')).to have_content(award_emoji_star.name)
+ expect(first('#js-dropdown-my-reaction .filter-dropdown li')).to have_content(award_emoji_star.name)
end
end
@@ -121,13 +121,29 @@ describe 'Dropdown emoji', :js do
send_keys_to_filtered_search(':')
end
+ it 'selects `None`' do
+ find('#js-dropdown-my-reaction .filter-dropdown-item', text: 'None').click
+
+ expect(page).to have_css(js_dropdown_emoji, visible: false)
+ expect_tokens([reaction_token('none', false)])
+ expect_filtered_search_input_empty
+ end
+
+ it 'selects `Any`' do
+ find('#js-dropdown-my-reaction .filter-dropdown-item', text: 'Any').click
+
+ expect(page).to have_css(js_dropdown_emoji, visible: false)
+ expect_tokens([reaction_token('any', false)])
+ expect_filtered_search_input_empty
+ end
+
it 'fills in the my-reaction name' do
click_emoji('thumbsup')
wait_for_requests
expect(page).to have_css(js_dropdown_emoji, visible: false)
- expect_tokens([emoji_token('thumbsup')])
+ expect_tokens([reaction_token('thumbsup')])
expect_filtered_search_input_empty
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index b99c5a7f4e3..0e296ab2109 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -15,6 +15,7 @@ describe 'Dropdown hint', :js do
before do
project.add_maintainer(user)
create(:issue, project: project)
+ create(:merge_request, source_project: project, target_project: project)
end
context 'when user not logged in' do
@@ -224,4 +225,21 @@ describe 'Dropdown hint', :js do
end
end
end
+
+ context 'merge request page' do
+ before do
+ sign_in(user)
+ visit project_merge_requests_path(project)
+ filtered_search.click
+ end
+
+ it 'shows the WIP menu item and opens the WIP options dropdown' do
+ click_hint('wip')
+
+ expect(page).to have_css(js_dropdown_hint, visible: false)
+ expect(page).to have_css('#js-dropdown-wip', visible: true)
+ expect_tokens([{ name: 'wip' }])
+ expect_filtered_search_input_empty
+ end
+ end
end
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index f76d30056da..ef5801e61e8 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -189,13 +189,21 @@ describe 'Dropdown milestone', :js do
end
it 'selects `no milestone`' do
- click_static_milestone('No Milestone')
+ click_static_milestone('None')
expect(page).to have_css(js_dropdown_milestone, visible: false)
expect_tokens([milestone_token('none', false)])
expect_filtered_search_input_empty
end
+ it 'selects `any milestone`' do
+ click_static_milestone('Any')
+
+ expect(page).to have_css(js_dropdown_milestone, visible: false)
+ expect_tokens([milestone_token('any', false)])
+ expect_filtered_search_input_empty
+ end
+
it 'selects `upcoming milestone`' do
click_static_milestone('Upcoming')
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index d4949de3f27..35d57b3896d 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -265,7 +265,7 @@ describe 'Filter issues', :js do
context 'issue label clicked' do
it 'filters and displays in search bar' do
- find('.issues-list .issue .issue-main-info .issuable-info a .badge', text: multiple_words_label.title).click
+ find('.issues-list .issue .issuable-main-info .issuable-info a .badge', text: multiple_words_label.title).click
expect_issues_list_count(1)
expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index 6ac7ccd00f7..1e1dd5691ab 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -118,7 +118,7 @@ describe 'Visual tokens', :js do
describe 'selecting static option from dropdown' do
before do
- find("#js-dropdown-assignee").find('.filter-dropdown-item', text: 'No Assignee').click
+ find("#js-dropdown-assignee").find('.filter-dropdown-item', text: 'None').click
end
it 'changes value in visual token' do
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 1456a2f0375..f2e4c5779df 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -27,7 +27,7 @@ describe 'New/edit issue', :js do
before do
# Using `allow_any_instance_of`/`and_wrap_original`, `original` would
# somehow refer to the very block we defined to _wrap_ that method, instead of
- # the original method, resulting in infinite recurison when called.
+ # the original method, resulting in infinite recursion when called.
# This is likely a bug with helper modules included into dynamically generated view classes.
# To work around this, we have to hold on to and call to the original implementation manually.
original_issue_dropdown_options = FormHelper.instance_method(:issue_assignees_dropdown_options)
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 98e37d8011a..7c591dacce5 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -1,20 +1,27 @@
require 'rails_helper'
describe 'GFM autocomplete', :js do
+ let(:issue_xss_title) { 'This will execute alert<img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
+ let(:user_xss_title) { 'eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
+
+ let(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') }
let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let(:project) { create(:project) }
let(:label) { create(:label, project: project, title: 'special+') }
let(:issue) { create(:issue, project: project) }
+ let!(:project_snippet) { create(:project_snippet, project: project, title: 'code snippet') }
before do
project.add_maintainer(user)
+ project.add_maintainer(user_xss)
+
sign_in(user)
visit project_issue_path(project, issue)
wait_for_requests
end
- it 'updates issue descripton with GFM reference' do
+ it 'updates issue description with GFM reference' do
find('.js-issuable-edit').click
simulate_input('#issue-description', "@#{user.name[0...3]}")
@@ -34,6 +41,32 @@ describe 'GFM autocomplete', :js do
expect(page).to have_selector('.atwho-container')
end
+ it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do
+ create(:issue, project: project, title: issue_xss_title)
+
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('#')
+ end
+
+ expect(page).to have_selector('.atwho-container')
+
+ page.within '.atwho-container #at-view-issues' do
+ expect(page.all('li').first.text).to include(issue_xss_title)
+ end
+ end
+
+ it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@ev')
+ end
+
+ expect(page).to have_selector('.atwho-container')
+
+ page.within '.atwho-container #at-view-users' do
+ expect(find('li').text).to have_content(user_xss.username)
+ end
+ end
+
it 'doesnt open autocomplete menu character is prefixed with text' do
page.within '.timeline-content-form' do
find('#note-body').native.send_keys('testing')
@@ -91,7 +124,7 @@ describe 'GFM autocomplete', :js do
wait_for_requests
- expect(find('#at-view-64')).to have_selector('.cur:first-of-type')
+ expect(find('#at-view-users')).to have_selector('.cur:first-of-type')
end
it 'includes items for assignee dropdowns with non-ASCII characters in name' do
@@ -104,7 +137,7 @@ describe 'GFM autocomplete', :js do
wait_for_requests
- expect(find('#at-view-64')).to have_content(user.name)
+ expect(find('#at-view-users')).to have_content(user.name)
end
it 'selects the first item for non-assignee dropdowns if a query is entered' do
@@ -301,6 +334,16 @@ describe 'GFM autocomplete', :js do
end
end
+ it 'shows project snippets' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('$')
+ end
+
+ page.within '.atwho-container' do
+ expect(page).to have_content(project_snippet.title)
+ end
+ end
+
private
def expect_to_wrap(should_wrap, item, note, value)
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
index 088ab114df3..76bc93e9766 100644
--- a/spec/features/issues/issue_detail_spec.rb
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -18,6 +18,23 @@ describe 'Issue Detail', :js do
end
end
+ context 'when issue description has xss snippet' do
+ before do
+ issue.update!(description: '![xss" onload=alert(1);//](a)')
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'should encode the description to prevent xss issues' do
+ page.within('.issuable-details .detail-page-description') do
+ expect(page).to have_selector('img', count: 1)
+ expect(find('img')['onerror']).to be_nil
+ expect(find('img')['src']).to end_with('/a')
+ end
+ end
+ end
+
context 'when edited by a user who is later deleted' do
before do
sign_in(user)
diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb
index f08c73f947c..fed77453cbb 100644
--- a/spec/features/issues/notes_on_issues_spec.rb
+++ b/spec/features/issues/notes_on_issues_spec.rb
@@ -3,6 +3,12 @@ require 'spec_helper'
describe 'Create notes on issues', :js do
let(:user) { create(:user) }
+ def submit_comment(text)
+ fill_in 'note[note]', with: text
+ click_button 'Comment'
+ wait_for_requests
+ end
+
shared_examples 'notes with reference' do
let(:issue) { create(:issue, project: project) }
let(:note_text) { "Check #{mention.to_reference}" }
@@ -12,10 +18,7 @@ describe 'Create notes on issues', :js do
sign_in(user)
visit project_issue_path(project, issue)
- fill_in 'note[note]', with: note_text
- click_button 'Comment'
-
- wait_for_requests
+ submit_comment(note_text)
end
it 'creates a note with reference and cross references the issue' do
@@ -74,4 +77,16 @@ describe 'Create notes on issues', :js do
let(:mention) { create(:merge_request, source_project: project) }
end
end
+
+ it 'highlights the current user in a comment' do
+ project = create(:project)
+ issue = create(:issue, project: project)
+ project.add_developer(user)
+ sign_in(user)
+
+ visit project_issue_path(project, issue)
+ submit_comment("@#{user.username} note to self")
+
+ expect(page).to have_selector '.gfm-project_member.current-user', text: user.username
+ end
end
diff --git a/spec/features/issues/resource_label_events_spec.rb b/spec/features/issues/resource_label_events_spec.rb
new file mode 100644
index 00000000000..b0764db7751
--- /dev/null
+++ b/spec/features/issues/resource_label_events_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'List issue resource label events', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project, author: user) }
+ let!(:label) { create(:label, project: project, title: 'foo') }
+ let!(:user_status) { create(:user_status, user: user) }
+
+ context 'when user displays the issue' do
+ let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue, note: 'some note') }
+ let!(:event) { create(:resource_label_event, user: user, issue: issue, label: label) }
+
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'shows both notes and resource label events' do
+ page.within('#notes') do
+ expect(find("#note_#{note.id}")).to have_content 'some note'
+ expect(find("#note_#{event.discussion_id}")).to have_content 'added foo label'
+ end
+ end
+
+ it 'shows the user status on the system note for the label' do
+ page.within("#note_#{event.discussion_id}") do
+ expect(page).to show_user_status user_status
+ end
+ end
+ end
+
+ context 'when user adds label to the issue' do
+ def toggle_labels(labels)
+ page.within '.labels' do
+ click_link 'Edit'
+ wait_for_requests
+
+ labels.each { |label| click_link label }
+
+ click_link 'Edit'
+ wait_for_requests
+ end
+ end
+
+ before do
+ create(:label, project: project, title: 'bar')
+ project.add_developer(user)
+
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'shows add note for newly added labels' do
+ toggle_labels(%w(foo bar))
+ visit project_issue_path(project, issue)
+ wait_for_requests
+
+ page.within('#notes') do
+ expect(page).to have_content 'added bar foo labels'
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/issues/rss_spec.rb
index 0e1383cd607..0e1383cd607 100644
--- a/spec/features/projects/issues/rss_spec.rb
+++ b/spec/features/issues/rss_spec.rb
diff --git a/spec/features/projects/issues/user_comments_on_issue_spec.rb b/spec/features/issues/user_comments_on_issue_spec.rb
index ba5b80ed04b..ba5b80ed04b 100644
--- a/spec/features/projects/issues/user_comments_on_issue_spec.rb
+++ b/spec/features/issues/user_comments_on_issue_spec.rb
diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
index 3dfcbc2fcb8..32bc851f00f 100644
--- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
@@ -55,11 +55,11 @@ describe 'User creates branch and merge request on issue page', :js do
test_branch_name_checking(input_branch_name)
test_source_checking(input_source)
- # The button inside dropdown should be disabled if any errors occured.
+ # The button inside dropdown should be disabled if any errors occurred.
expect(page).to have_button('Create branch', disabled: true)
end
- # The top level button should be disabled if any errors occured.
+ # The top level button should be disabled if any errors occurred.
expect(page).to have_button('Create branch', disabled: true)
end
@@ -76,7 +76,7 @@ describe 'User creates branch and merge request on issue page', :js do
visit project_issue_path(project, issue)
- expect(page).to have_content('created branch 1-cherry-coloured-funk')
+ expect(page).to have_content("created merge request !1 to address this issue")
expect(page).to have_content('mentioned in merge request !1')
end
@@ -106,7 +106,7 @@ describe 'User creates branch and merge request on issue page', :js do
visit project_issue_path(project, issue)
- expect(page).to have_content('created branch custom-branch-name')
+ expect(page).to have_content("created merge request !1 to address this issue")
expect(page).to have_content('mentioned in merge request !1')
end
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
new file mode 100644
index 00000000000..687a6f1eafc
--- /dev/null
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -0,0 +1,90 @@
+require "spec_helper"
+
+describe "User creates issue" do
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:user) { create(:user) }
+
+ context "when signed in as guest" do
+ before do
+ project.add_guest(user)
+ sign_in(user)
+
+ visit(new_project_issue_path(project))
+ end
+
+ it "creates issue" do
+ page.within(".issue-form") do
+ expect(page).to have_no_content("Assign to")
+ .and have_no_content("Labels")
+ .and have_no_content("Milestone")
+
+ expect(page.find('#issue_title')['placeholder']).to eq 'Title'
+ expect(page.find('#issue_description')['placeholder']).to eq 'Write a comment or drag your files here…'
+ end
+
+ issue_title = "500 error on profile"
+
+ fill_in("Title", with: issue_title)
+ click_button("Submit issue")
+
+ expect(page).to have_content(issue_title)
+ .and have_content(user.name)
+ .and have_content(project.name)
+ end
+ end
+
+ context "when signed in as developer", :js do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+
+ visit(new_project_issue_path(project))
+ end
+
+ context "when previewing" do
+ it "previews content" do
+ form = first(".gfm-form")
+ textarea = first(".gfm-form textarea")
+
+ page.within(form) do
+ click_button("Preview")
+
+ preview = find(".js-md-preview") # this element is findable only when the "Preview" link is clicked.
+
+ expect(preview).to have_content("Nothing to preview.")
+
+ click_button("Write")
+ fill_in("Description", with: "Bug fixed :smile:")
+ click_button("Preview")
+
+ expect(preview).to have_css("gl-emoji")
+ expect(textarea).not_to be_visible
+ end
+ end
+ end
+
+ context "with labels" do
+ LABEL_TITLES = %w(bug feature enhancement).freeze
+
+ before do
+ LABEL_TITLES.each do |title|
+ create(:label, project: project, title: title)
+ end
+ end
+
+ it "creates issue" do
+ issue_title = "500 error on profile"
+
+ fill_in("Title", with: issue_title)
+ click_button("Label")
+ click_link(LABEL_TITLES.first)
+ click_button("Submit issue")
+
+ expect(page).to have_content(issue_title)
+ .and have_content(user.name)
+ .and have_content(project.name)
+ .and have_content(LABEL_TITLES.first)
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
new file mode 100644
index 00000000000..60b88ef4bdf
--- /dev/null
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -0,0 +1,25 @@
+require "spec_helper"
+
+describe "User edits issue", :js do
+ set(:project) { create(:project_empty_repo, :public) }
+ set(:user) { create(:user) }
+ set(:issue) { create(:issue, project: project, author: user) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+
+ visit(edit_project_issue_path(project, issue))
+ end
+
+ it "previews content" do
+ form = first(".gfm-form")
+
+ page.within(form) do
+ fill_in("Description", with: "Bug fixed :smile:")
+ click_button("Preview")
+ end
+
+ expect(form).to have_button("Write")
+ end
+end
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
new file mode 100644
index 00000000000..afa425c2cec
--- /dev/null
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -0,0 +1,347 @@
+require 'spec_helper'
+
+describe 'User interacts with awards' do
+ let(:user) { create(:user) }
+
+ describe 'User interacts with awards in an issue', :js do
+ let(:issue) { create(:issue, project: project)}
+ let(:project) { create(:project) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit(project_issue_path(project, issue))
+ end
+
+ it 'toggles the thumbsup award emoji' do
+ page.within('.awards') do
+ thumbsup = page.first('.award-control')
+ thumbsup.click
+ thumbsup.hover
+
+ expect(page).to have_selector('.js-emoji-btn')
+ expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
+ expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
+
+ thumbsup = page.first('.award-control')
+ thumbsup.click
+ thumbsup.hover
+
+ expect(page).to have_selector('.award-control.js-emoji-btn')
+ expect(page.all('.award-control.js-emoji-btn').size).to eq(2)
+
+ page.all('.award-control.js-emoji-btn').each do |element|
+ expect(element['title']).to eq('')
+ end
+
+ expect(page.all('.award-control .js-counter')).to all(have_content('0'))
+
+ thumbsup = page.first('.award-control')
+ thumbsup.click
+ thumbsup.hover
+
+ expect(page).to have_selector('.js-emoji-btn')
+ expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
+ expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
+ end
+ end
+
+ it 'toggles a custom award emoji' do
+ page.within('.awards') do
+ page.find('.js-add-award').click
+ end
+
+ page.find('.emoji-menu.is-visible')
+
+ expect(page).to have_selector('.js-emoji-menu-search')
+ expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
+
+ page.within('.emoji-menu-content') do
+ emoji_button = page.first('.js-emoji-btn')
+ emoji_button.hover
+ emoji_button.click
+ end
+
+ page.within('.awards') do
+ expect(page).to have_selector('.js-emoji-btn')
+ expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
+ expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
+
+ expect do
+ page.find('.js-emoji-btn.active').click
+ wait_for_requests
+ end.to change { page.all('.award-control.js-emoji-btn').size }.from(3).to(2)
+ end
+ end
+
+ it 'shows the list of award emoji categories' do
+ page.within('.awards') do
+ page.find('.js-add-award').click
+ end
+
+ page.find('.emoji-menu.is-visible')
+
+ expect(page).to have_selector('.js-emoji-menu-search')
+ expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
+
+ fill_in('emoji-menu-search', with: 'hand')
+
+ page.within('.emoji-menu-content') do
+ expect(page).to have_selector('[data-name="raised_hand"]')
+ end
+ end
+
+ it 'adds an award emoji by a comment' do
+ page.within('.js-main-target-form') do
+ fill_in('note[note]', with: ':smile:')
+
+ click_button('Comment')
+ end
+
+ expect(page).to have_emoji('smile')
+ end
+
+ context 'when a project is archived' do
+ let(:project) { create(:project, :archived) }
+
+ it 'hides the add award button' do
+ page.within('.awards') do
+ expect(page).not_to have_css('.js-add-award')
+ end
+ end
+ end
+
+ context 'User interacts with awards on a note' do
+ let!(:note) { create(:note, noteable: issue, project: issue.project) }
+ let!(:award_emoji) { create(:award_emoji, awardable: note, name: '100') }
+
+ it 'shows the award on the note' do
+ page.within('.note-awards') do
+ expect(page).to have_emoji('100')
+ end
+ end
+
+ it 'allows adding a vote to an award' do
+ page.within('.note-awards') do
+ find('gl-emoji[data-name="100"]').click
+ end
+ wait_for_requests
+
+ expect(note.reload.award_emoji.size).to eq(2)
+ end
+
+ it 'allows adding a new emoji' do
+ page.within('.note-actions') do
+ find('a.js-add-award').click
+ end
+ page.within('.emoji-menu-content') do
+ find('gl-emoji[data-name="8ball"]').click
+ end
+ wait_for_requests
+
+ page.within('.note-awards') do
+ expect(page).to have_emoji('8ball')
+ end
+ expect(note.reload.award_emoji.size).to eq(2)
+ end
+
+ context 'when the project is archived' do
+ let(:project) { create(:project, :archived) }
+
+ it 'hides the buttons for adding new emoji' do
+ page.within('.note-awards') do
+ expect(page).not_to have_css('.award-menu-holder')
+ end
+
+ page.within('.note-actions') do
+ expect(page).not_to have_css('a.js-add-award')
+ end
+ end
+
+ it 'does not allow toggling existing emoji' do
+ page.within('.note-awards') do
+ find('gl-emoji[data-name="100"]').click
+ end
+ wait_for_requests
+
+ expect(note.reload.award_emoji.size).to eq(1)
+ end
+ end
+ end
+ end
+
+ describe 'User interacts with awards on an issue', :js do
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+
+ describe 'logged in' do
+ before do
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'adds award to issue' do
+ first('.js-emoji-btn').click
+
+ expect(page).to have_selector('.js-emoji-btn.active')
+ expect(first('.js-emoji-btn')).to have_content '1'
+
+ visit project_issue_path(project, issue)
+
+ expect(first('.js-emoji-btn')).to have_content '1'
+ end
+
+ it 'removes award from issue' do
+ first('.js-emoji-btn').click
+ find('.js-emoji-btn.active').click
+
+ expect(first('.js-emoji-btn')).to have_content '0'
+
+ visit project_issue_path(project, issue)
+
+ expect(first('.js-emoji-btn')).to have_content '0'
+ end
+
+ it 'only has one menu on the page' do
+ first('.js-add-award').click
+
+ expect(page).to have_selector('.emoji-menu', count: 1)
+ end
+ end
+
+ describe 'logged out' do
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'does not see award menu button' do
+ expect(page).not_to have_selector('.js-award-holder')
+ end
+ end
+ end
+
+ describe 'Awards Emoji' do
+ let!(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, assignees: [user], project: project) }
+
+ context 'authorized user' do
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ describe 'visiting an issue with a legacy award emoji that is not valid anymore' do
+ before do
+ # The `heart_tip` emoji is not valid anymore so we need to skip validation
+ issue.award_emoji.build(user: user, name: 'heart_tip').save!(validate: false)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ # Regression test: https://gitlab.com/gitlab-org/gitlab-ce/issues/29529
+ it 'does not shows a 500 page', :js do
+ expect(page).to have_text(issue.title)
+ end
+ end
+
+ describe 'Click award emoji from issue#show' do
+ let!(:note) { create(:note_on_issue, noteable: issue, project: issue.project, note: "Hello world") }
+
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ context 'click the thumbsdown emoji' do
+ it 'increments the thumbsdown emoji', :js do
+ find('[data-name="thumbsdown"]').click
+ wait_for_requests
+ expect(thumbsdown_emoji).to have_text("1")
+ end
+
+ it 'decrements the thumbsup emoji', :js do
+ expect(thumbsup_emoji).to have_text("0")
+ end
+ end
+
+ it 'toggles the smiley emoji on a note', :js do
+ toggle_smiley_emoji(true)
+
+ within('.note-body') do
+ expect(find(emoji_counter)).to have_text("1")
+ end
+
+ toggle_smiley_emoji(false)
+
+ within('.note-body') do
+ expect(page).not_to have_selector(emoji_counter)
+ end
+ end
+
+ context 'execute /award quick action' do
+ it 'toggles the emoji award on noteable', :js do
+ execute_quick_action('/award :100:')
+
+ expect(find(noteable_award_counter)).to have_text("1")
+
+ execute_quick_action('/award :100:')
+
+ expect(page).not_to have_selector(noteable_award_counter)
+ end
+ end
+ end
+ end
+
+ context 'unauthorized user', :js do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'has disabled emoji button' do
+ expect(first('.award-control')[:class]).to have_text('disabled')
+ end
+ end
+
+ def execute_quick_action(cmd)
+ within('.js-main-target-form') do
+ fill_in 'note[note]', with: cmd
+ click_button 'Comment'
+ end
+
+ wait_for_requests
+ end
+
+ def thumbsup_emoji
+ page.all(emoji_counter).first
+ end
+
+ def thumbsdown_emoji
+ page.all(emoji_counter).last
+ end
+
+ def emoji_counter
+ 'span.js-counter'
+ end
+
+ def noteable_award_counter
+ ".awards .active"
+ end
+
+ def toggle_smiley_emoji(status)
+ within('.note') do
+ find('.note-emoji-button').click
+ end
+
+ if !status
+ first('[data-name="smiley"]').click
+ else
+ find('[data-name="smiley"]').click
+ end
+
+ wait_for_requests
+ end
+ end
+end
diff --git a/spec/features/issues/user_sees_breadcrumb_links_spec.rb b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
new file mode 100644
index 00000000000..ca234321235
--- /dev/null
+++ b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
@@ -0,0 +1,18 @@
+require 'rails_helper'
+
+describe 'New issue breadcrumbs' do
+ let(:project) { create(:project) }
+ let(:user) { project.creator }
+
+ before do
+ sign_in(user)
+ visit new_project_issue_path(project)
+ end
+
+ it 'display a link to project issues and new issue pages' do
+ page.within '.breadcrumbs' do
+ expect(find_link('Issues')[:href]).to end_with(project_issues_path(project))
+ expect(find_link('New')[:href]).to end_with(new_project_issue_path(project))
+ end
+ end
+end
diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb
new file mode 100644
index 00000000000..4771d2c6d28
--- /dev/null
+++ b/spec/features/issues/user_sorts_issues_spec.rb
@@ -0,0 +1,63 @@
+require "spec_helper"
+
+describe "User sorts issues" do
+ set(:user) { create(:user) }
+ set(:group) { create(:group) }
+ set(:project) { create(:project_empty_repo, :public, group: group) }
+ set(:issue1) { create(:issue, project: project) }
+ set(:issue2) { create(:issue, project: project) }
+ set(:issue3) { create(:issue, project: project) }
+
+ before do
+ create_list(:award_emoji, 2, :upvote, awardable: issue1)
+ create_list(:award_emoji, 2, :downvote, awardable: issue2)
+ create(:award_emoji, :downvote, awardable: issue1)
+ create(:award_emoji, :upvote, awardable: issue2)
+
+ sign_in(user)
+
+ visit(project_issues_path(project))
+ end
+
+ it 'keeps the sort option' do
+ find('button.dropdown-toggle').click
+
+ page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
+ click_link('Milestone')
+ end
+
+ visit(issues_dashboard_path(assignee_username: user.username))
+
+ expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+
+ visit(project_issues_path(project))
+
+ expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+
+ visit(issues_group_path(group))
+
+ expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ end
+
+ it "sorts by popularity" do
+ find("button.dropdown-toggle").click
+
+ page.within(".content ul.dropdown-menu.dropdown-menu-right li") do
+ click_link("Popularity")
+ end
+
+ page.within(".issues-list") do
+ page.within("li.issue:nth-child(1)") do
+ expect(page).to have_content(issue1.title)
+ end
+
+ page.within("li.issue:nth-child(2)") do
+ expect(page).to have_content(issue2.title)
+ end
+
+ page.within("li.issue:nth-child(3)") do
+ expect(page).to have_content(issue3.title)
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/issues/user_toggles_subscription_spec.rb b/spec/features/issues/user_toggles_subscription_spec.rb
index c2b2a193682..c2b2a193682 100644
--- a/spec/features/projects/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/issues/user_toggles_subscription_spec.rb
diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb
new file mode 100644
index 00000000000..27cffdc5f8b
--- /dev/null
+++ b/spec/features/issues/user_uses_quick_actions_spec.rb
@@ -0,0 +1,365 @@
+require 'rails_helper'
+
+describe 'Issues > User uses quick actions', :js do
+ include Spec::Support::Helpers::Features::NotesHelpers
+
+ it_behaves_like 'issuable record that supports quick actions in its description and notes', :issue do
+ let(:issuable) { create(:issue, project: project) }
+ end
+
+ describe 'issue-only commands' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ end
+
+ after do
+ wait_for_requests
+ end
+
+ describe 'time tracking' do
+ let(:issue) { create(:issue, project: project) }
+
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it_behaves_like 'issuable time tracker'
+ end
+
+ describe 'adding a due date from note' do
+ let(:issue) { create(:issue, project: project) }
+
+ context 'when the current user can update the due date' do
+ it 'does not create a note, and sets the due date accordingly' do
+ add_note("/due 2016-08-28")
+
+ expect(page).not_to have_content '/due 2016-08-28'
+ expect(page).to have_content 'Commands applied'
+
+ issue.reload
+
+ expect(issue.due_date).to eq Date.new(2016, 8, 28)
+ end
+ end
+
+ context 'when the current user cannot update the due date' do
+ let(:guest) { create(:user) }
+ before do
+ project.add_guest(guest)
+ gitlab_sign_out
+ sign_in(guest)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not create a note, and sets the due date accordingly' do
+ add_note("/due 2016-08-28")
+
+ expect(page).not_to have_content 'Commands applied'
+
+ issue.reload
+
+ expect(issue.due_date).to be_nil
+ end
+ end
+ end
+
+ describe 'removing a due date from note' do
+ let(:issue) { create(:issue, project: project, due_date: Date.new(2016, 8, 28)) }
+
+ context 'when the current user can update the due date' do
+ it 'does not create a note, and removes the due date accordingly' do
+ expect(issue.due_date).to eq Date.new(2016, 8, 28)
+
+ add_note("/remove_due_date")
+
+ expect(page).not_to have_content '/remove_due_date'
+ expect(page).to have_content 'Commands applied'
+
+ issue.reload
+
+ expect(issue.due_date).to be_nil
+ end
+ end
+
+ context 'when the current user cannot update the due date' do
+ let(:guest) { create(:user) }
+ before do
+ project.add_guest(guest)
+ gitlab_sign_out
+ sign_in(guest)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not create a note, and sets the due date accordingly' do
+ add_note("/remove_due_date")
+
+ expect(page).not_to have_content 'Commands applied'
+
+ issue.reload
+
+ expect(issue.due_date).to eq Date.new(2016, 8, 28)
+ end
+ end
+ end
+
+ describe 'toggling the WIP prefix from the title from note' do
+ let(:issue) { create(:issue, project: project) }
+
+ it 'does not recognize the command nor create a note' do
+ add_note("/wip")
+
+ expect(page).not_to have_content '/wip'
+ end
+ end
+
+ describe 'mark issue as duplicate' do
+ let(:issue) { create(:issue, project: project) }
+ let(:original_issue) { create(:issue, project: project) }
+
+ context 'when the current user can update issues' do
+ it 'does not create a note, and marks the issue as a duplicate' do
+ add_note("/duplicate ##{original_issue.to_reference}")
+
+ expect(page).not_to have_content "/duplicate #{original_issue.to_reference}"
+ expect(page).to have_content 'Commands applied'
+ expect(page).to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
+
+ expect(issue.reload).to be_closed
+ end
+ end
+
+ context 'when the current user cannot update the issue' do
+ let(:guest) { create(:user) }
+ before do
+ project.add_guest(guest)
+ gitlab_sign_out
+ sign_in(guest)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not create a note, and does not mark the issue as a duplicate' do
+ add_note("/duplicate ##{original_issue.to_reference}")
+
+ expect(page).not_to have_content 'Commands applied'
+ expect(page).not_to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
+
+ expect(issue.reload).to be_open
+ end
+ end
+ end
+
+ describe 'make issue confidential' do
+ let(:issue) { create(:issue, project: project) }
+ let(:original_issue) { create(:issue, project: project) }
+
+ context 'when the current user can update issues' do
+ it 'does not create a note, and marks the issue as confidential' do
+ add_note("/confidential")
+
+ expect(page).not_to have_content "/confidential"
+ expect(page).to have_content 'Commands applied'
+ expect(page).to have_content "made the issue confidential"
+
+ expect(issue.reload).to be_confidential
+ end
+ end
+
+ context 'when the current user cannot update the issue' do
+ let(:guest) { create(:user) }
+ before do
+ project.add_guest(guest)
+ gitlab_sign_out
+ sign_in(guest)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not create a note, and does not mark the issue as confidential' do
+ add_note("/confidential")
+
+ expect(page).not_to have_content 'Commands applied'
+ expect(page).not_to have_content "made the issue confidential"
+
+ expect(issue.reload).not_to be_confidential
+ end
+ end
+ end
+
+ describe 'move the issue to another project' do
+ let(:issue) { create(:issue, project: project) }
+
+ context 'when the project is valid' do
+ let(:target_project) { create(:project, :public) }
+
+ before do
+ target_project.add_maintainer(user)
+ gitlab_sign_out
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'moves the issue' do
+ add_note("/move #{target_project.full_path}")
+
+ expect(page).to have_content 'Commands applied'
+ expect(issue.reload).to be_closed
+
+ visit project_issue_path(target_project, issue)
+
+ expect(page).to have_content 'Issues 1'
+ end
+ end
+
+ context 'when the project is valid but the user not authorized' do
+ let(:project_unauthorized) { create(:project, :public) }
+
+ before do
+ gitlab_sign_out
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not move the issue' do
+ add_note("/move #{project_unauthorized.full_path}")
+
+ wait_for_requests
+
+ expect(page).to have_content 'Commands applied'
+ expect(issue.reload).to be_open
+ end
+ end
+
+ context 'when the project is invalid' do
+ before do
+ gitlab_sign_out
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not move the issue' do
+ add_note("/move not/valid")
+
+ expect(page).not_to have_content 'Commands applied'
+ expect(issue.reload).to be_open
+ end
+ end
+
+ context 'when the user issues multiple commands' do
+ let(:target_project) { create(:project, :public) }
+ let(:milestone) { create(:milestone, title: '1.0', project: project) }
+ let(:target_milestone) { create(:milestone, title: '1.0', project: target_project) }
+ let(:bug) { create(:label, project: project, title: 'bug') }
+ let(:wontfix) { create(:label, project: project, title: 'wontfix') }
+ let(:bug_target) { create(:label, project: target_project, title: 'bug') }
+ let(:wontfix_target) { create(:label, project: target_project, title: 'wontfix') }
+
+ before do
+ target_project.add_maintainer(user)
+ gitlab_sign_out
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'applies the commands to both issues and moves the issue' do
+ add_note("/label ~#{bug.title} ~#{wontfix.title}\n\n/milestone %\"#{milestone.title}\"\n\n/move #{target_project.full_path}")
+
+ expect(page).to have_content 'Commands applied'
+ expect(issue.reload).to be_closed
+
+ visit project_issue_path(target_project, issue)
+
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'wontfix'
+ expect(page).to have_content '1.0'
+
+ visit project_issue_path(project, issue)
+ expect(page).to have_content 'Closed'
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'wontfix'
+ expect(page).to have_content '1.0'
+ end
+
+ it 'moves the issue and applies the commands to both issues' do
+ add_note("/move #{target_project.full_path}\n\n/label ~#{bug.title} ~#{wontfix.title}\n\n/milestone %\"#{milestone.title}\"")
+
+ expect(page).to have_content 'Commands applied'
+ expect(issue.reload).to be_closed
+
+ visit project_issue_path(target_project, issue)
+
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'wontfix'
+ expect(page).to have_content '1.0'
+
+ visit project_issue_path(project, issue)
+ expect(page).to have_content 'Closed'
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'wontfix'
+ expect(page).to have_content '1.0'
+ end
+ end
+ end
+
+ describe 'create a merge request starting from an issue' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:issue) { create(:issue, project: project) }
+
+ def expect_mr_quickaction(success)
+ expect(page).to have_content 'Commands applied'
+
+ if success
+ expect(page).to have_content 'created merge request'
+ else
+ expect(page).not_to have_content 'created merge request'
+ end
+ end
+
+ it "doesn't create a merge request when the branch name is invalid" do
+ add_note("/create_merge_request invalid branch name")
+
+ wait_for_requests
+
+ expect_mr_quickaction(false)
+ end
+
+ it "doesn't create a merge request when a branch with that name already exists" do
+ add_note("/create_merge_request feature")
+
+ wait_for_requests
+
+ expect_mr_quickaction(false)
+ end
+
+ it 'creates a new merge request using issue iid and title as branch name when the branch name is empty' do
+ add_note("/create_merge_request")
+
+ wait_for_requests
+
+ expect_mr_quickaction(true)
+
+ created_mr = project.merge_requests.last
+ expect(created_mr.source_branch).to eq(issue.to_branch_name)
+
+ visit project_merge_request_path(project, created_mr)
+ expect(page).to have_content %{WIP: Resolve "#{issue.title}"}
+ end
+
+ it 'creates a merge request using the given branch name' do
+ branch_name = '1-feature'
+ add_note("/create_merge_request #{branch_name}")
+
+ expect_mr_quickaction(true)
+
+ created_mr = project.merge_requests.last
+ expect(created_mr.source_branch).to eq(branch_name)
+
+ visit project_merge_request_path(project, created_mr)
+ expect(page).to have_content %{WIP: Resolve "#{issue.title}"}
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
deleted file mode 100644
index 5926e442f24..00000000000
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ /dev/null
@@ -1,307 +0,0 @@
-require 'rails_helper'
-
-describe 'Issues > User uses quick actions', :js do
- include Spec::Support::Helpers::Features::NotesHelpers
-
- it_behaves_like 'issuable record that supports quick actions in its description and notes', :issue do
- let(:issuable) { create(:issue, project: project) }
- end
-
- describe 'issue-only commands' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- visit project_issue_path(project, issue)
- end
-
- after do
- wait_for_requests
- end
-
- describe 'time tracking' do
- let(:issue) { create(:issue, project: project) }
-
- before do
- visit project_issue_path(project, issue)
- end
-
- it_behaves_like 'issuable time tracker'
- end
-
- describe 'adding a due date from note' do
- let(:issue) { create(:issue, project: project) }
-
- context 'when the current user can update the due date' do
- it 'does not create a note, and sets the due date accordingly' do
- add_note("/due 2016-08-28")
-
- expect(page).not_to have_content '/due 2016-08-28'
- expect(page).to have_content 'Commands applied'
-
- issue.reload
-
- expect(issue.due_date).to eq Date.new(2016, 8, 28)
- end
- end
-
- context 'when the current user cannot update the due date' do
- let(:guest) { create(:user) }
- before do
- project.add_guest(guest)
- gitlab_sign_out
- sign_in(guest)
- visit project_issue_path(project, issue)
- end
-
- it 'does not create a note, and sets the due date accordingly' do
- add_note("/due 2016-08-28")
-
- expect(page).not_to have_content 'Commands applied'
-
- issue.reload
-
- expect(issue.due_date).to be_nil
- end
- end
- end
-
- describe 'removing a due date from note' do
- let(:issue) { create(:issue, project: project, due_date: Date.new(2016, 8, 28)) }
-
- context 'when the current user can update the due date' do
- it 'does not create a note, and removes the due date accordingly' do
- expect(issue.due_date).to eq Date.new(2016, 8, 28)
-
- add_note("/remove_due_date")
-
- expect(page).not_to have_content '/remove_due_date'
- expect(page).to have_content 'Commands applied'
-
- issue.reload
-
- expect(issue.due_date).to be_nil
- end
- end
-
- context 'when the current user cannot update the due date' do
- let(:guest) { create(:user) }
- before do
- project.add_guest(guest)
- gitlab_sign_out
- sign_in(guest)
- visit project_issue_path(project, issue)
- end
-
- it 'does not create a note, and sets the due date accordingly' do
- add_note("/remove_due_date")
-
- expect(page).not_to have_content 'Commands applied'
-
- issue.reload
-
- expect(issue.due_date).to eq Date.new(2016, 8, 28)
- end
- end
- end
-
- describe 'toggling the WIP prefix from the title from note' do
- let(:issue) { create(:issue, project: project) }
-
- it 'does not recognize the command nor create a note' do
- add_note("/wip")
-
- expect(page).not_to have_content '/wip'
- end
- end
-
- describe 'mark issue as duplicate' do
- let(:issue) { create(:issue, project: project) }
- let(:original_issue) { create(:issue, project: project) }
-
- context 'when the current user can update issues' do
- it 'does not create a note, and marks the issue as a duplicate' do
- add_note("/duplicate ##{original_issue.to_reference}")
-
- expect(page).not_to have_content "/duplicate #{original_issue.to_reference}"
- expect(page).to have_content 'Commands applied'
- expect(page).to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
-
- expect(issue.reload).to be_closed
- end
- end
-
- context 'when the current user cannot update the issue' do
- let(:guest) { create(:user) }
- before do
- project.add_guest(guest)
- gitlab_sign_out
- sign_in(guest)
- visit project_issue_path(project, issue)
- end
-
- it 'does not create a note, and does not mark the issue as a duplicate' do
- add_note("/duplicate ##{original_issue.to_reference}")
-
- expect(page).not_to have_content 'Commands applied'
- expect(page).not_to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
-
- expect(issue.reload).to be_open
- end
- end
- end
-
- describe 'make issue confidential' do
- let(:issue) { create(:issue, project: project) }
- let(:original_issue) { create(:issue, project: project) }
-
- context 'when the current user can update issues' do
- it 'does not create a note, and marks the issue as confidential' do
- add_note("/confidential")
-
- expect(page).not_to have_content "/confidential"
- expect(page).to have_content 'Commands applied'
- expect(page).to have_content "made the issue confidential"
-
- expect(issue.reload).to be_confidential
- end
- end
-
- context 'when the current user cannot update the issue' do
- let(:guest) { create(:user) }
- before do
- project.add_guest(guest)
- gitlab_sign_out
- sign_in(guest)
- visit project_issue_path(project, issue)
- end
-
- it 'does not create a note, and does not mark the issue as confidential' do
- add_note("/confidential")
-
- expect(page).not_to have_content 'Commands applied'
- expect(page).not_to have_content "made the issue confidential"
-
- expect(issue.reload).not_to be_confidential
- end
- end
- end
-
- describe 'move the issue to another project' do
- let(:issue) { create(:issue, project: project) }
-
- context 'when the project is valid' do
- let(:target_project) { create(:project, :public) }
-
- before do
- target_project.add_maintainer(user)
- gitlab_sign_out
- sign_in(user)
- visit project_issue_path(project, issue)
- end
-
- it 'moves the issue' do
- add_note("/move #{target_project.full_path}")
-
- expect(page).to have_content 'Commands applied'
- expect(issue.reload).to be_closed
-
- visit project_issue_path(target_project, issue)
-
- expect(page).to have_content 'Issues 1'
- end
- end
-
- context 'when the project is valid but the user not authorized' do
- let(:project_unauthorized) { create(:project, :public) }
-
- before do
- gitlab_sign_out
- sign_in(user)
- visit project_issue_path(project, issue)
- end
-
- it 'does not move the issue' do
- add_note("/move #{project_unauthorized.full_path}")
-
- wait_for_requests
-
- expect(page).to have_content 'Commands applied'
- expect(issue.reload).to be_open
- end
- end
-
- context 'when the project is invalid' do
- before do
- gitlab_sign_out
- sign_in(user)
- visit project_issue_path(project, issue)
- end
-
- it 'does not move the issue' do
- add_note("/move not/valid")
-
- expect(page).not_to have_content 'Commands applied'
- expect(issue.reload).to be_open
- end
- end
-
- context 'when the user issues multiple commands' do
- let(:target_project) { create(:project, :public) }
- let(:milestone) { create(:milestone, title: '1.0', project: project) }
- let(:target_milestone) { create(:milestone, title: '1.0', project: target_project) }
- let(:bug) { create(:label, project: project, title: 'bug') }
- let(:wontfix) { create(:label, project: project, title: 'wontfix') }
- let(:bug_target) { create(:label, project: target_project, title: 'bug') }
- let(:wontfix_target) { create(:label, project: target_project, title: 'wontfix') }
-
- before do
- target_project.add_maintainer(user)
- gitlab_sign_out
- sign_in(user)
- visit project_issue_path(project, issue)
- end
-
- it 'applies the commands to both issues and moves the issue' do
- add_note("/label ~#{bug.title} ~#{wontfix.title}\n\n/milestone %\"#{milestone.title}\"\n\n/move #{target_project.full_path}")
-
- expect(page).to have_content 'Commands applied'
- expect(issue.reload).to be_closed
-
- visit project_issue_path(target_project, issue)
-
- expect(page).to have_content 'bug'
- expect(page).to have_content 'wontfix'
- expect(page).to have_content '1.0'
-
- visit project_issue_path(project, issue)
- expect(page).to have_content 'Closed'
- expect(page).to have_content 'bug'
- expect(page).to have_content 'wontfix'
- expect(page).to have_content '1.0'
- end
-
- it 'moves the issue and applies the commands to both issues' do
- add_note("/move #{target_project.full_path}\n\n/label ~#{bug.title} ~#{wontfix.title}\n\n/milestone %\"#{milestone.title}\"")
-
- expect(page).to have_content 'Commands applied'
- expect(issue.reload).to be_closed
-
- visit project_issue_path(target_project, issue)
-
- expect(page).to have_content 'bug'
- expect(page).to have_content 'wontfix'
- expect(page).to have_content '1.0'
-
- visit project_issue_path(project, issue)
- expect(page).to have_content 'Closed'
- expect(page).to have_content 'bug'
- expect(page).to have_content 'wontfix'
- expect(page).to have_content '1.0'
- end
- end
- end
- end
-end
diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb
new file mode 100644
index 00000000000..330b6f0e77a
--- /dev/null
+++ b/spec/features/issues/user_views_issue_spec.rb
@@ -0,0 +1,50 @@
+require "spec_helper"
+
+describe "User views issue" do
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, project: project, description: "# Description header", author: user) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+
+ visit(project_issue_path(project, issue))
+ end
+
+ it { expect(page).to have_header_with_correct_id_and_link(1, "Description header", "description-header") }
+
+ it 'shows the merge request and issue actions', :aggregate_failures do
+ expect(page).to have_link('New issue')
+ expect(page).to have_button('Create merge request')
+ expect(page).to have_link('Close issue')
+ end
+
+ context 'when the project is archived' do
+ let(:project) { create(:project, :public, :archived) }
+
+ it 'hides the merge request and issue actions', :aggregate_failures do
+ expect(page).not_to have_link('New issue')
+ expect(page).not_to have_button('Create merge request')
+ expect(page).not_to have_link('Close issue')
+ end
+ end
+
+ describe 'user status' do
+ subject { visit(project_issue_path(project, issue)) }
+
+ describe 'showing status of the author of the issue' do
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { issue.author }
+ end
+ end
+
+ describe 'showing status of a user who commented on an issue', :js do
+ let!(:note) { create(:note, noteable: issue, project: project, author: user_with_status) }
+
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { create(:user) }
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/issues/user_views_issues_spec.rb b/spec/features/issues/user_views_issues_spec.rb
index 58afb4efb86..58afb4efb86 100644
--- a/spec/features/projects/issues/user_views_issues_spec.rb
+++ b/spec/features/issues/user_views_issues_spec.rb
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 6f917f522bc..09904cb907f 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -156,7 +156,7 @@ describe 'Labels Hierarchy', :js, :nested_groups do
find('a.label-item', text: parent_group_label.title).click
find('a.label-item', text: project_label_1.title).click
- find('.btn-create').click
+ find('.btn-success').click
expect(page.find('.issue-details h2.title')).to have_content('new created issue')
expect(page).to have_selector('span.badge', text: grandparent_group_label.title)
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index cac8a5068ec..3b37ede8579 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -264,9 +264,9 @@ describe 'GitLab Markdown', :aggregate_failures do
@project_wiki = @feat.project_wiki
@project_wiki_page = @feat.project_wiki_page
- file = Gollum::File.new(@project_wiki.wiki)
- expect(file).to receive(:path).and_return('images/example.jpg')
- expect(@project_wiki).to receive(:find_file).with('images/example.jpg').and_return(file)
+ path = 'images/example.jpg'
+ gitaly_wiki_file = Gitlab::GitalyClient::WikiFile.new(path: path)
+ expect(@project_wiki).to receive(:find_file).with(path).and_return(Gitlab::Git::WikiFile.new(gitaly_wiki_file))
allow(@project_wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' }
@html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki, page_slug: @project_wiki_page.slug })
diff --git a/spec/features/projects/merge_requests/user_accepts_merge_request_spec.rb b/spec/features/merge_request/user_accepts_merge_request_spec.rb
index 01aeed93947..01aeed93947 100644
--- a/spec/features/projects/merge_requests/user_accepts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_accepts_merge_request_spec.rb
diff --git a/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb b/spec/features/merge_request/user_closes_merge_request_spec.rb
index 2d12d690151..2d12d690151 100644
--- a/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb
+++ b/spec/features/merge_request/user_closes_merge_request_spec.rb
diff --git a/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb b/spec/features/merge_request/user_comments_on_commit_spec.rb
index 8ea358bcc70..8ea358bcc70 100644
--- a/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb
+++ b/spec/features/merge_request/user_comments_on_commit_spec.rb
diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb
new file mode 100644
index 00000000000..00cf368e8c9
--- /dev/null
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -0,0 +1,173 @@
+require 'spec_helper'
+
+describe 'User comments on a diff', :js do
+ include MergeRequestDiffHelpers
+ include RepoHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) do
+ create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
+ end
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit(diffs_project_merge_request_path(project, merge_request))
+ end
+
+ context 'when viewing comments' do
+ context 'when toggling inline comments' do
+ context 'in a single file' do
+ it 'hides a comment' do
+ click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in('note_note', with: 'Line is wrong')
+ click_button('Comment')
+ end
+
+ page.within('.diff-files-holder > div:nth-child(3)') do
+ expect(page).to have_content('Line is wrong')
+
+ find('.js-btn-vue-toggle-comments').click
+
+ expect(page).not_to have_content('Line is wrong')
+ end
+ end
+ end
+
+ context 'in multiple files' do
+ it 'toggles comments' do
+ click_diff_line(find("[id='#{sample_compare.changes[0][:line_code]}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in('note_note', with: 'Line is correct')
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do
+ expect(page).to have_content('Line is correct')
+ end
+
+ click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in('note_note', with: 'Line is wrong')
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ # Hide the comment.
+ page.within('.diff-files-holder > div:nth-child(3)') do
+ find('.js-btn-vue-toggle-comments').click
+
+ expect(page).not_to have_content('Line is wrong')
+ end
+
+ # At this moment a user should see only one comment.
+ # The other one should be hidden.
+ page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do
+ expect(page).to have_content('Line is correct')
+ end
+
+ # Show the comment.
+ page.within('.diff-files-holder > div:nth-child(3)') do
+ find('.js-btn-vue-toggle-comments').click
+ end
+
+ # Now both the comments should be shown.
+ page.within('.diff-files-holder > div:nth-child(3) .note-body > .note-text') do
+ expect(page).to have_content('Line is wrong')
+ end
+
+ page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do
+ expect(page).to have_content('Line is correct')
+ end
+
+ # Check the same comments in the side-by-side view.
+ execute_script("window.scrollTo(0,0);")
+ click_button 'Side-by-side'
+
+ wait_for_requests
+
+ page.within('.diff-files-holder > div:nth-child(3) .parallel .note-body > .note-text') do
+ expect(page).to have_content('Line is wrong')
+ end
+
+ page.within('.diff-files-holder > div:nth-child(2) .parallel .note-body > .note-text') do
+ expect(page).to have_content('Line is correct')
+ end
+ end
+ end
+ end
+ end
+
+ context 'when adding comments' do
+ include_examples 'comment on merge request file'
+ end
+
+ context 'when editing comments' do
+ it 'edits a comment' do
+ click_diff_line(find("[id='#{sample_commit.line_code}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in(:note_note, with: 'Line is wrong')
+ click_button('Comment')
+ end
+
+ page.within('.diff-file:nth-of-type(5) .discussion .note') do
+ find('.js-note-edit').click
+
+ page.within('.current-note-edit-form') do
+ fill_in('note_note', with: 'Typo, please fix')
+ click_button('Save comment')
+ end
+
+ expect(page).not_to have_button('Save comment', disabled: true)
+ end
+
+ page.within('.diff-file:nth-of-type(5) .discussion .note') do
+ expect(page).to have_content('Typo, please fix').and have_no_content('Line is wrong')
+ end
+ end
+ end
+
+ context 'when deleting comments' do
+ it 'deletes a comment' do
+ click_diff_line(find("[id='#{sample_commit.line_code}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in(:note_note, with: 'Line is wrong')
+ click_button('Comment')
+ end
+
+ page.within('.notes-tab .badge') do
+ expect(page).to have_content('1')
+ end
+
+ page.within('.diff-file:nth-of-type(5) .discussion .note') do
+ find('.more-actions').click
+ find('.more-actions .dropdown-menu li', match: :first)
+
+ accept_confirm { find('.js-note-delete').click }
+ end
+
+ page.within('.merge-request-tabs') do
+ find('.notes-tab').click
+ end
+
+ wait_for_requests
+
+ expect(page).not_to have_css('.notes .discussion')
+
+ page.within('.notes-tab .badge') do
+ expect(page).to have_content('0')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb b/spec/features/merge_request/user_comments_on_merge_request_spec.rb
index 69bdab85d81..69bdab85d81 100644
--- a/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb
+++ b/spec/features/merge_request/user_comments_on_merge_request_spec.rb
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index f0d38dc6a0c..d790bdc82ce 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -114,10 +114,9 @@ describe 'Merge request > User creates image diff notes', :js do
create_image_diff_note
end
- # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
- xit 'shows indicator and avatar badges, and allows collapsing/expanding the discussion notes' do
+ it 'shows indicator and avatar badges, and allows collapsing/expanding the discussion notes' do
indicator = find('.js-image-badge', match: :first)
- badge = find('.image-diff-avatar-link .badge', match: :first)
+ badge = find('.user-avatar-link .badge', match: :first)
expect(indicator).to have_content('1')
expect(badge).to have_content('1')
@@ -157,8 +156,7 @@ describe 'Merge request > User creates image diff notes', :js do
visit project_merge_request_path(project, merge_request)
end
- # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
- xit 'render diff indicators within the image frame' do
+ it 'render diff indicators within the image frame' do
diff_note = create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
wait_for_requests
@@ -200,7 +198,6 @@ describe 'Merge request > User creates image diff notes', :js do
def create_image_diff_note
find('.js-add-image-diff-note-button', match: :first).click
- page.all('.js-add-image-diff-note-button')[0].click
find('.diff-content .note-textarea').native.send_keys('image diff test comment')
click_button 'Comment'
wait_for_requests
diff --git a/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb b/spec/features/merge_request/user_creates_merge_request_spec.rb
index 38b4e4a6d1b..38b4e4a6d1b 100644
--- a/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb
+++ b/spec/features/merge_request/user_creates_merge_request_spec.rb
diff --git a/spec/features/merge_request/user_creates_mr_spec.rb b/spec/features/merge_request/user_creates_mr_spec.rb
index 1ac31de62cb..9d2a94a4a41 100644
--- a/spec/features/merge_request/user_creates_mr_spec.rb
+++ b/spec/features/merge_request/user_creates_mr_spec.rb
@@ -28,4 +28,29 @@ describe 'Merge request > User creates MR' do
it_behaves_like 'a creatable merge request'
end
end
+
+ context 'source project', :js do
+ let(:user) { create(:user) }
+ let(:target_project) { create(:project, :public, :repository) }
+ let(:source_project) { target_project }
+
+ before do
+ source_project.add_maintainer(user)
+
+ sign_in(user)
+ visit project_new_merge_request_path(
+ target_project,
+ merge_request: {
+ source_project_id: source_project.id,
+ target_project_id: target_project.id
+ })
+ end
+
+ it 'filters source project' do
+ find('.js-source-project').click
+ find('.dropdown-source-project input').set('source')
+
+ expect(find('.dropdown-source-project .dropdown-content')).not_to have_content(source_project.name)
+ end
+ end
end
diff --git a/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb b/spec/features/merge_request/user_edits_merge_request_spec.rb
index 7de0f9daac6..7de0f9daac6 100644
--- a/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb
+++ b/spec/features/merge_request/user_edits_merge_request_spec.rb
diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb
index 8c9e782aa76..3152707136c 100644
--- a/spec/features/merge_request/user_edits_mr_spec.rb
+++ b/spec/features/merge_request/user_edits_mr_spec.rb
@@ -1,11 +1,13 @@
require 'rails_helper'
describe 'Merge request > User edits MR' do
+ include ProjectForksHelper
+
it_behaves_like 'an editable merge request'
context 'for a forked project' do
it_behaves_like 'an editable merge request' do
- let(:source_project) { create(:project, :repository, forked_from_project: target_project) }
+ let(:source_project) { fork_project(target_project, nil, repository: true) }
end
end
end
diff --git a/spec/features/projects/merge_requests/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb
index 68a835e7f77..68a835e7f77 100644
--- a/spec/features/projects/merge_requests/user_manages_subscription_spec.rb
+++ b/spec/features/merge_request/user_manages_subscription_spec.rb
diff --git a/spec/features/projects/merge_requests/user_merges_merge_request_spec.rb b/spec/features/merge_request/user_merges_merge_request_spec.rb
index 6539e6e9208..6539e6e9208 100644
--- a/spec/features/projects/merge_requests/user_merges_merge_request_spec.rb
+++ b/spec/features/merge_request/user_merges_merge_request_spec.rb
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index 77261f9375c..51b78d3e7d1 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -85,12 +85,13 @@ describe 'Merge request > User posts diff notes', :js do
# `.line_holder` will be an unfolded line.
let(:line_holder) { first('#a5cc2925ca8258af241be7e5b0381edf30266302 .line_holder') }
- it 'does not allow commenting on the left side' do
- should_not_allow_commenting(line_holder, 'left')
+ it 'allows commenting on the left side' do
+ should_allow_commenting(line_holder, 'left')
end
- it 'does not allow commenting on the right side' do
- should_not_allow_commenting(line_holder, 'right')
+ it 'allows commenting on the right side' do
+ # Automatically shifts comment box to left side.
+ should_allow_commenting(line_holder, 'right')
end
end
end
@@ -147,8 +148,8 @@ describe 'Merge request > User posts diff notes', :js do
# `.line_holder` will be an unfolded line.
let(:line_holder) { first('.line_holder[id="a5cc2925ca8258af241be7e5b0381edf30266302_1_1"]') }
- it 'does not allow commenting' do
- should_not_allow_commenting line_holder
+ it 'allows commenting' do
+ should_allow_commenting line_holder
end
end
@@ -186,11 +187,8 @@ describe 'Merge request > User posts diff notes', :js do
describe 'posting a note' do
it 'adds as discussion' do
- expect(page).to have_css('.js-temp-notes-holder', count: 2)
-
should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'), asset_form_reset: false)
expect(page).to have_css('.notes_holder .note.note-discussion', count: 1)
- expect(page).to have_css('.js-temp-notes-holder', count: 1)
expect(page).to have_button('Reply...')
end
end
@@ -203,23 +201,20 @@ describe 'Merge request > User posts diff notes', :js do
end
context 'with a new line' do
- # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
- xit 'allows commenting' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]').find(:xpath, '..'))
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
end
end
context 'with an old line' do
- # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
- xit 'allows commenting' do
- should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]').find(:xpath, '..'))
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
end
end
context 'with an unchanged line' do
- # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
- xit 'allows commenting' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]').find(:xpath, '..'))
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
end
end
@@ -267,7 +262,7 @@ describe 'Merge request > User posts diff notes', :js do
def assert_comment_persistence(line_holder, asset_form_reset:)
notes_holder_saved = line_holder.find(:xpath, notes_holder_input_xpath)
- expect(notes_holder_saved[:class]).not_to include(notes_holder_input_class)
+ expect(notes_holder_saved[:class]).not_to include('note-edit-form')
expect(notes_holder_saved).to have_content test_note_comment
assert_form_is_reset if asset_form_reset
@@ -281,6 +276,6 @@ describe 'Merge request > User posts diff notes', :js do
end
def assert_form_is_reset
- expect(page).to have_no_css('.js-temp-notes-holder')
+ expect(page).to have_no_css('.note-edit-form')
end
end
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index 260c5f9c28b..ee5f5377ca6 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -111,7 +111,7 @@ describe 'Merge request > User posts notes', :js do
it 'allows using markdown buttons after saving a note and then trying to edit it again' do
page.within('.current-note-edit-form') do
fill_in 'note[note]', with: 'This is the new content'
- find('.btn-save').click
+ find('.btn-success').click
end
find('.note').hover
@@ -129,7 +129,7 @@ describe 'Merge request > User posts notes', :js do
it 'appends the edited at time to the note' do
page.within('.current-note-edit-form') do
fill_in 'note[note]', with: 'Some new content'
- find('.btn-save').click
+ find('.btn-success').click
end
page.within("#note_#{note.id}") do
diff --git a/spec/features/projects/merge_requests/user_rebases_merge_request_spec.rb b/spec/features/merge_request/user_rebases_merge_request_spec.rb
index 92e1c9942b1..92e1c9942b1 100644
--- a/spec/features/projects/merge_requests/user_rebases_merge_request_spec.rb
+++ b/spec/features/merge_request/user_rebases_merge_request_spec.rb
diff --git a/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb b/spec/features/merge_request/user_reopens_merge_request_spec.rb
index 745b4537e72..745b4537e72 100644
--- a/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb
+++ b/spec/features/merge_request/user_reopens_merge_request_spec.rb
diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb
index 629052442b4..50c723776a3 100644
--- a/spec/features/merge_request/user_resolves_conflicts_spec.rb
+++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb
@@ -44,9 +44,7 @@ describe 'Merge request > User resolves conflicts', :js do
within find('.diff-file', text: 'files/ruby/regex.rb') do
expect(page).to have_selector('.line_content.new', text: "def username_regexp")
- expect(page).not_to have_selector('.line_content.new', text: "def username_regex")
expect(page).to have_selector('.line_content.new', text: "def project_name_regexp")
- expect(page).not_to have_selector('.line_content.new', text: "def project_name_regex")
expect(page).to have_selector('.line_content.new', text: "def path_regexp")
expect(page).to have_selector('.line_content.new', text: "def archive_formats_regexp")
expect(page).to have_selector('.line_content.new', text: "def git_reference_regexp")
@@ -110,12 +108,8 @@ describe 'Merge request > User resolves conflicts', :js do
click_link('conflicts', href: %r{/conflicts\Z})
end
- # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
- # include_examples "conflicts are resolved in Interactive mode"
- # include_examples "conflicts are resolved in Edit inline mode"
-
- it 'prevents RSpec/EmptyExampleGroup' do
- end
+ include_examples "conflicts are resolved in Interactive mode"
+ include_examples "conflicts are resolved in Edit inline mode"
end
context 'in Parallel view mode' do
@@ -124,12 +118,8 @@ describe 'Merge request > User resolves conflicts', :js do
click_button 'Side-by-side'
end
- # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
- # include_examples "conflicts are resolved in Interactive mode"
- # include_examples "conflicts are resolved in Edit inline mode"
-
- it 'prevents RSpec/EmptyExampleGroup' do
- end
+ include_examples "conflicts are resolved in Interactive mode"
+ include_examples "conflicts are resolved in Edit inline mode"
end
end
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index 2d268ecab58..8a16c011067 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -139,44 +139,64 @@ describe 'Merge request > User resolves diff notes and discussions', :js do
expect(find('.diffs .diff-file .notes_holder')).to be_visible
end
end
- end
- it 'allows user to resolve from reply form without a comment' do
- page.within '.diff-content' do
- click_button 'Reply...'
+ describe 'reply form' do
+ before do
+ click_button 'Toggle discussion'
- click_button 'Resolve discussion'
- end
+ page.within '.diff-content' do
+ click_button 'Reply...'
+ end
+ end
- page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 discussion resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
- end
- end
+ it 'allows user to comment' do
+ page.within '.diff-content' do
+ find('.js-note-text').set 'testing'
- it 'allows user to unresolve from reply form without a comment' do
- page.within '.diff-content' do
- click_button 'Resolve discussion'
- sleep 1
+ click_button 'Comment'
- click_button 'Reply...'
+ wait_for_requests
+ end
- click_button 'Unresolve discussion'
- end
+ page.within '.line-resolve-all-container' do
+ expect(page).to have_content('1/1 discussion resolved')
+ end
+ end
- page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 discussion resolved')
- expect(page).not_to have_selector('.line-resolve-btn.is-active')
+ it 'allows user to unresolve from reply form without a comment' do
+ page.within '.diff-content' do
+ click_button 'Unresolve discussion'
+
+ wait_for_requests
+ end
+
+ page.within '.line-resolve-all-container' do
+ expect(page).to have_content('0/1 discussion resolved')
+ expect(page).not_to have_selector('.line-resolve-btn.is-active')
+ end
+ end
+
+ it 'allows user to comment & unresolve discussion' do
+ page.within '.diff-content' do
+ find('.js-note-text').set 'testing'
+
+ click_button 'Comment & unresolve discussion'
+
+ wait_for_requests
+ end
+
+ page.within '.line-resolve-all-container' do
+ expect(page).to have_content('0/1 discussion resolved')
+ end
+ end
end
end
- it 'allows user to comment & resolve discussion' do
+ it 'allows user to resolve from reply form without a comment' do
page.within '.diff-content' do
click_button 'Reply...'
- find('.js-note-text').set 'testing'
-
- click_button 'Comment & resolve discussion'
+ click_button 'Resolve discussion'
end
page.within '.line-resolve-all-container' do
@@ -185,19 +205,18 @@ describe 'Merge request > User resolves diff notes and discussions', :js do
end
end
- it 'allows user to comment & unresolve discussion' do
+ it 'allows user to comment & resolve discussion' do
page.within '.diff-content' do
- click_button 'Resolve discussion'
-
click_button 'Reply...'
find('.js-note-text').set 'testing'
- click_button 'Comment & unresolve discussion'
+ click_button 'Comment & resolve discussion'
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 discussion resolved')
+ expect(page).to have_content('1/1 discussion resolved')
+ expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
diff --git a/spec/features/projects/merge_requests/user_reverts_merge_request_spec.rb b/spec/features/merge_request/user_reverts_merge_request_spec.rb
index 67b6aefb2d8..67b6aefb2d8 100644
--- a/spec/features/projects/merge_requests/user_reverts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_reverts_merge_request_spec.rb
diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
index 428eb414274..b58c433bbfe 100644
--- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
@@ -81,22 +81,25 @@ describe 'Merge request > User sees avatars on diff notes', :js do
visit diffs_project_merge_request_path(project, merge_request, view: view)
wait_for_requests
+
+ find('.js-toggle-tree-list').click
end
it 'shows note avatar' do
page.within find_line(position.line_code(project.repository)) do
find('.diff-notes-collapse').send_keys(:return)
- expect(page).to have_selector('img.js-diff-comment-avatar', count: 1)
+ expect(page).to have_selector('.js-diff-comment-avatar img', count: 1)
end
end
it 'shows comment on note avatar' do
page.within find_line(position.line_code(project.repository)) do
find('.diff-notes-collapse').send_keys(:return)
-
- expect(first('img.js-diff-comment-avatar')["data-original-title"]).to eq("#{note.author.name}: #{note.note.truncate(17)}")
+ first('.js-diff-comment-avatar img').hover
end
+
+ expect(page).to have_content "#{note.author.name}: #{note.note.truncate(17)}"
end
it 'toggles comments when clicking avatar' do
@@ -107,7 +110,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do
expect(page).not_to have_selector('.notes_holder')
page.within find_line(position.line_code(project.repository)) do
- first('img.js-diff-comment-avatar').click
+ first('.js-diff-comment-avatar img').click
end
expect(page).to have_selector('.notes_holder')
@@ -123,7 +126,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do
wait_for_requests
page.within find_line(position.line_code(project.repository)) do
- expect(page).not_to have_selector('img.js-diff-comment-avatar')
+ expect(page).not_to have_selector('.js-diff-comment-avatar img')
end
end
@@ -141,7 +144,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do
page.within find_line(position.line_code(project.repository)) do
find('.diff-notes-collapse').send_keys(:return)
- expect(page).to have_selector('img.js-diff-comment-avatar', count: 2)
+ expect(page).to have_selector('.js-diff-comment-avatar img', count: 2)
end
end
@@ -160,7 +163,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do
page.within find_line(position.line_code(project.repository)) do
find('.diff-notes-collapse').send_keys(:return)
- expect(page).to have_selector('img.js-diff-comment-avatar', count: 3)
+ expect(page).to have_selector('.js-diff-comment-avatar img', count: 3)
expect(find('.diff-comments-more-count')).to have_content '+1'
end
end
diff --git a/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb b/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb
new file mode 100644
index 00000000000..f17acb35a5a
--- /dev/null
+++ b/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb
@@ -0,0 +1,18 @@
+require 'rails_helper'
+
+describe 'New merge request breadcrumbs' do
+ let(:project) { create(:project, :repository) }
+ let(:user) { project.creator }
+
+ before do
+ sign_in(user)
+ visit project_new_merge_request_path(project)
+ end
+
+ it 'display a link to project merge requests and new merge request pages' do
+ page.within '.breadcrumbs' do
+ expect(find_link('Merge Requests')[:href]).to end_with(project_merge_requests_path(project))
+ expect(find_link('New')[:href]).to end_with(project_new_merge_request_path(project))
+ end
+ end
+end
diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
index f744d7941f5..74290c0fff9 100644
--- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
@@ -1,45 +1,88 @@
require 'rails_helper'
describe 'Merge request > User sees deployment widget', :js do
- describe 'when deployed to an environment' do
+ describe 'when merge request has associated environments' do
let(:user) { create(:user) }
- let(:project) { merge_request.target_project }
- let(:merge_request) { create(:merge_request, :merged) }
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) { create(:merge_request, :merged, source_project: project) }
let(:environment) { create(:environment, project: project) }
let(:role) { :developer }
- let(:sha) { project.commit('master').id }
- let!(:deployment) { create(:deployment, environment: environment, sha: sha) }
+ let(:ref) { merge_request.target_branch }
+ let(:sha) { project.commit(ref).id }
+ let(:pipeline) { create(:ci_pipeline_without_jobs, sha: sha, project: project, ref: ref) }
let!(:manual) { }
before do
+ merge_request.update!(merge_commit_sha: sha)
project.add_user(user, role)
sign_in(user)
- visit project_merge_request_path(project, merge_request)
- wait_for_requests
end
- it 'displays that the environment is deployed' do
- wait_for_requests
+ context 'when deployment succeeded' do
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+ let!(:deployment) { create(:deployment, :succeed, environment: environment, sha: sha, ref: ref, deployable: build) }
- expect(page).to have_content("Deployed to #{environment.name}")
- expect(find('.js-deploy-time')['data-original-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
+ it 'displays that the environment is deployed' do
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
+ expect(page).to have_content("Deployed to #{environment.name}")
+ expect(find('.js-deploy-time')['data-original-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
+ end
+ end
+
+ context 'when deployment failed' do
+ let(:build) { create(:ci_build, :failed, pipeline: pipeline) }
+ let!(:deployment) { create(:deployment, :failed, environment: environment, sha: sha, ref: ref, deployable: build) }
+
+ it 'displays that the deployment failed' do
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
+ expect(page).to have_content("Failed to deploy to #{environment.name}")
+ expect(page).not_to have_css('.js-deploy-time')
+ end
+ end
+
+ context 'when deployment running' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+ let!(:deployment) { create(:deployment, :running, environment: environment, sha: sha, ref: ref, deployable: build) }
+
+ it 'displays that the running deployment' do
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
+ expect(page).to have_content("Deploying to #{environment.name}")
+ expect(page).not_to have_css('.js-deploy-time')
+ end
+ end
+
+ context 'when deployment will happen' do
+ let(:build) { create(:ci_build, :created, pipeline: pipeline) }
+ let!(:deployment) { create(:deployment, environment: environment, sha: sha, ref: ref, deployable: build) }
+
+ it 'displays that the environment name' do
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
+ expect(page).to have_content("Deploying to #{environment.name}")
+ expect(page).not_to have_css('.js-deploy-time')
+ end
end
context 'with stop action' do
- let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+ let!(:deployment) { create(:deployment, :succeed, environment: environment, sha: sha, ref: ref, deployable: build) }
let(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
- let(:deployment) do
- create(:deployment, environment: environment, ref: merge_request.target_branch,
- sha: sha, deployable: build, on_stop: 'close_app')
- end
before do
+ deployment.update!(on_stop: manual.name)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
end
it 'does start build when stop button clicked' do
- accept_confirm { click_button('Stop environment') }
+ accept_confirm { find('.js-stop-env').click }
expect(page).to have_content('close_app')
end
@@ -48,7 +91,7 @@ describe 'Merge request > User sees deployment widget', :js do
let(:role) { :reporter }
it 'does not show stop button' do
- expect(page).not_to have_button('Stop environment')
+ expect(page).not_to have_selector('.js-stop-env')
end
end
end
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index d6e7ff33d5d..0df9e4bbc1a 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -2,6 +2,7 @@ require 'rails_helper'
describe 'Merge request > User sees diff', :js do
include ProjectForksHelper
+ include RepoHelpers
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -81,5 +82,59 @@ describe 'Merge request > User sees diff', :js do
expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1)
end
end
+
+ context 'when file contains html' do
+ let(:current_user) { project.owner }
+ let(:branch_name) {"test_branch"}
+
+ def create_file(branch_name, file_name, content)
+ Files::CreateService.new(
+ project,
+ current_user,
+ start_branch: branch_name,
+ branch_name: branch_name,
+ commit_message: "Create file",
+ file_path: file_name,
+ file_content: content
+ ).execute
+
+ project.commit(branch_name)
+ end
+
+ it 'escapes any HTML special characters in the diff chunk header' do
+ file_content =
+ <<~CONTENT
+ function foo<input> {
+ let a = 1;
+ let b = 2;
+ let c = 3;
+ let d = 3;
+ }
+ CONTENT
+
+ new_file_content =
+ <<~CONTENT
+ function foo<input> {
+ let a = 1;
+ let b = 2;
+ let c = 3;
+ let x = 3;
+ }
+ CONTENT
+
+ file_name = 'xss_file.rs'
+
+ create_file('master', file_name, file_content)
+ merge_request = create(:merge_request, source_project: project)
+ create_file(merge_request.source_branch, file_name, new_file_content)
+
+ project.commit(merge_request.source_branch)
+
+ visit diffs_project_merge_request_path(project, merge_request)
+
+ expect(page).to have_text("function foo<input> {")
+ expect(page).to have_css(".line[lang='rust'] .k")
+ end
+ end
end
end
diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index b285cd7a7ac..582be101399 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -40,21 +40,27 @@ describe 'Merge request > User sees merge widget', :js do
context 'view merge request' do
let!(:environment) { create(:environment, project: project) }
+ let(:sha) { project.commit(merge_request.source_branch).sha }
+ let(:pipeline) { create(:ci_pipeline_without_jobs, status: 'success', sha: sha, project: project, ref: merge_request.source_branch) }
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
let!(:deployment) do
- create(:deployment, environment: environment,
- ref: 'feature',
- sha: merge_request.diff_head_sha)
+ create(:deployment, :succeed,
+ environment: environment,
+ ref: merge_request.source_branch,
+ deployable: build,
+ sha: sha)
end
before do
+ merge_request.update!(head_pipeline: pipeline)
visit project_merge_request_path(project, merge_request)
end
it 'shows environments link' do
wait_for_requests
- page.within('.mr-widget-heading') do
+ page.within('.js-pre-merge-deploy') do
expect(page).to have_content("Deployed to #{environment.name}")
expect(find('.js-deploy-url')[:href]).to include(environment.formatted_external_url)
end
@@ -160,7 +166,7 @@ describe 'Merge request > User sees merge widget', :js do
end
end
- context 'view merge request where project has CI setup but no CI status' do
+ context 'view merge request where project has CI set up but no CI status' do
before do
pipeline = create(:ci_pipeline, project: project,
sha: merge_request.diff_head_sha,
@@ -174,11 +180,11 @@ describe 'Merge request > User sees merge widget', :js do
# Wait for the `ci_status` and `merge_check` requests
wait_for_requests
- expect(page).to have_text('Could not connect to the CI server. Please check your settings and try again')
+ expect(page).to have_text("Could not retrieve the pipeline status. For troubleshooting steps, read the documentation.")
end
end
- context 'view merge request in project with only-mwps setting enabled but no CI is setup' do
+ context 'view merge request in project with only-mwps setting enabled but no CI is set up' do
before do
visit project_merge_request_path(project_only_mwps, merge_request_in_only_mwps_project)
end
@@ -423,7 +429,7 @@ describe 'Merge request > User sees merge widget', :js do
end
it 'shows test reports summary which includes the new failure' do
- within(".mr-section-container") do
+ within(".js-reports-container") do
click_button 'Expand'
expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests')
@@ -438,7 +444,7 @@ describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the new failure' do
it 'shows the test report detail' do
- within(".mr-section-container") do
+ within(".js-reports-container") do
click_button 'Expand'
within(".js-report-section-container") do
@@ -468,7 +474,7 @@ describe 'Merge request > User sees merge widget', :js do
end
it 'shows test reports summary which includes the existing failure' do
- within(".mr-section-container") do
+ within(".js-reports-container") do
click_button 'Expand'
expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests')
@@ -483,7 +489,7 @@ describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the existing failure' do
it 'shows test report detail of it' do
- within(".mr-section-container") do
+ within(".js-reports-container") do
click_button 'Expand'
within(".js-report-section-container") do
@@ -519,7 +525,7 @@ describe 'Merge request > User sees merge widget', :js do
end
it 'shows test reports summary which includes the resolved failure' do
- within(".mr-section-container") do
+ within(".js-reports-container") do
click_button 'Expand'
expect(page).to have_content('Test summary contained 1 fixed test result out of 2 total tests')
@@ -533,7 +539,7 @@ describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the resolved failure' do
it 'shows test report detail of it' do
- within(".mr-section-container") do
+ within(".js-reports-container") do
click_button 'Expand'
within(".js-report-section-container") do
diff --git a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
index 029b66b5e8e..fc4a188d4a7 100644
--- a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
+++ b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
@@ -1,18 +1,20 @@
require 'rails_helper'
describe 'Merge request > User sees MR from deleted forked project', :js do
+ include ProjectForksHelper
+
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
- let(:fork_project) { create(:project, :public, :repository, forked_from_project: project) }
+ let(:forked_project) { fork_project(project, nil, repository: true) }
let!(:merge_request) do
- create(:merge_request_with_diffs, source_project: fork_project,
+ create(:merge_request_with_diffs, source_project: forked_project,
target_project: project,
description: 'Test merge request')
end
before do
MergeRequests::MergeService.new(project, user).execute(merge_request)
- fork_project.destroy!
+ forked_project.destroy!
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
index c1608be402a..fd4175d5227 100644
--- a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
+++ b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
@@ -28,7 +28,7 @@ describe 'Merge request > User sees MR with deleted source branch', :js do
click_on 'Changes'
wait_for_requests
- expect(page).to have_selector('.diffs.tab-pane .nothing-here-block')
+ expect(page).to have_selector('.diffs.tab-pane .file-holder')
expect(page).to have_content('Source branch does not exist.')
end
end
diff --git a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
index d4ad0b0a377..a6118453540 100644
--- a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
+++ b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
@@ -1,18 +1,20 @@
require 'rails_helper'
describe 'Merge request > User sees notes from forked project', :js do
+ include ProjectForksHelper
+
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
- let(:fork_project) { create(:project, :public, :repository, forked_from_project: project) }
+ let(:forked_project) { fork_project(project, nil, repository: true) }
let!(:merge_request) do
- create(:merge_request_with_diffs, source_project: fork_project,
+ create(:merge_request_with_diffs, source_project: forked_project,
target_project: project,
description: 'Test merge request')
end
before do
create(:note_on_commit, note: 'A commit comment',
- project: fork_project,
+ project: forked_project,
commit_id: merge_request.commit_shas.first)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb
index d30dcefc6aa..97093bbc2f6 100644
--- a/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb
@@ -1,17 +1,19 @@
require 'rails_helper'
describe 'Merge request > User sees pipelines from forked project', :js do
+ include ProjectForksHelper
+
let(:target_project) { create(:project, :public, :repository) }
let(:user) { target_project.creator }
- let(:fork_project) { create(:project, :repository, forked_from_project: target_project) }
+ let(:forked_project) { fork_project(target_project, nil, repository: true) }
let!(:merge_request) do
- create(:merge_request_with_diffs, source_project: fork_project,
+ create(:merge_request_with_diffs, source_project: forked_project,
target_project: target_project,
description: 'Test merge request')
end
let(:pipeline) do
create(:ci_pipeline,
- project: fork_project,
+ project: forked_project,
sha: merge_request.diff_head_sha,
ref: merge_request.source_branch)
end
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index 45cccbee63e..8faddee4daa 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -41,8 +41,7 @@ describe 'Merge request > User sees pipelines', :js do
visit project_merge_request_path(project, merge_request)
wait_for_requests
- expect(page.find('.ci-widget')).to have_content(
- 'Could not connect to the CI server. Please check your settings and try again')
+ expect(page.find('.ci-widget')).to have_text("Could not retrieve the pipeline status. For troubleshooting steps, read the documentation.")
end
end
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index f42b4dcbb47..92db4f44098 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -110,7 +110,8 @@ describe 'Merge request > User sees versions', :js do
diff_id: merge_request_diff3.id,
start_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
)
- expect(page).to have_content '4 changed files with 15 additions and 6 deletions'
+ expect(page).to have_content '4 changed files'
+ expect(page).to have_content '15 additions 6 deletions'
expect(page).to have_content 'Not all comments are displayed'
position = Gitlab::Diff::Position.new(
@@ -131,7 +132,8 @@ describe 'Merge request > User sees versions', :js do
end
it 'show diff between new and old version' do
- expect(page).to have_content '4 changed files with 15 additions and 6 deletions'
+ expect(page).to have_content '4 changed files'
+ expect(page).to have_content '15 additions 6 deletions'
end
it 'returns to latest version when "Show latest version" button is clicked' do
@@ -158,7 +160,7 @@ describe 'Merge request > User sees versions', :js do
it 'has 0 chages between versions' do
page.within '.mr-version-compare-dropdown' do
- expect(find('.dropdown-toggle')).to have_content 'version 1'
+ expect(find('.dropdown-menu-toggle')).to have_content 'version 1'
end
page.within '.mr-version-dropdown' do
@@ -179,7 +181,7 @@ describe 'Merge request > User sees versions', :js do
it 'sets the compared versions to be the same' do
page.within '.mr-version-compare-dropdown' do
- expect(find('.dropdown-toggle')).to have_content 'version 2'
+ expect(find('.dropdown-menu-toggle')).to have_content 'version 2'
end
page.within '.mr-version-dropdown' do
diff --git a/spec/features/merge_request/user_uses_slash_commands_spec.rb b/spec/features/merge_request/user_uses_quick_actions_spec.rb
index b81478a481f..b81478a481f 100644
--- a/spec/features/merge_request/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_request/user_uses_quick_actions_spec.rb
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
new file mode 100644
index 00000000000..7f95a1282f9
--- /dev/null
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -0,0 +1,60 @@
+require 'spec_helper'
+
+describe 'User views diffs', :js do
+ let(:merge_request) do
+ create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
+ end
+ let(:project) { create(:project, :public, :repository) }
+
+ before do
+ visit(diffs_project_merge_request_path(project, merge_request))
+
+ wait_for_requests
+
+ find('.js-toggle-tree-list').click
+ end
+
+ shared_examples 'unfold diffs' do
+ it 'unfolds diffs' do
+ first('.js-unfold').click
+
+ expect(find('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"] .text-file')).to have_content('.bundle')
+ end
+ end
+
+ it 'shows diffs' do
+ expect(page).to have_css('.tab-content #diffs.active')
+ expect(page).to have_css('#parallel-diff-btn', count: 1)
+ expect(page).to have_css('#inline-diff-btn', count: 1)
+ end
+
+ it 'hides loading spinner after load' do
+ expect(page).not_to have_selector('.mr-loading-status .loading', visible: true)
+ end
+
+ context 'when in the inline view' do
+ include_examples 'unfold diffs'
+ end
+
+ context 'when in the side-by-side view' do
+ before do
+ click_button 'Side-by-side'
+
+ wait_for_requests
+ end
+
+ it 'shows diffs in parallel' do
+ expect(page).to have_css('.parallel')
+ end
+
+ it 'toggles container class' do
+ expect(page).not_to have_css('.content-wrapper > .container-fluid.container-limited')
+
+ click_link 'Commits'
+
+ expect(page).to have_css('.content-wrapper > .container-fluid.container-limited')
+ end
+
+ include_examples 'unfold diffs'
+ end
+end
diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb
new file mode 100644
index 00000000000..71022c6bb08
--- /dev/null
+++ b/spec/features/merge_request/user_views_open_merge_request_spec.rb
@@ -0,0 +1,92 @@
+require 'spec_helper'
+
+describe 'User views an open merge request' do
+ let(:merge_request) do
+ create(:merge_request, source_project: project, target_project: project, description: '# Description header')
+ end
+
+ context 'when a merge request does not have repository' do
+ let(:project) { create(:project, :public, :repository) }
+
+ before do
+ visit(merge_request_path(merge_request))
+ end
+
+ it 'renders both the title and the description' do
+ node = find('.wiki h1 a#user-content-description-header')
+ expect(node[:href]).to end_with('#description-header')
+
+ # Work around a weird Capybara behavior where calling `parent` on a node
+ # returns the whole document, not the node's actual parent element
+ expect(find(:xpath, "#{node.path}/..").text).to eq(merge_request.description[2..-1])
+
+ expect(page).to have_content(merge_request.title).and have_content(merge_request.description)
+ end
+ end
+
+ context 'when a merge request has repository', :js do
+ let(:project) { create(:project, :public, :repository) }
+
+ context 'when rendering description preview' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit(edit_project_merge_request_path(project, merge_request))
+ end
+
+ it 'renders empty description preview' do
+ find('.gfm-form').fill_in(:merge_request_description, with: '')
+
+ page.within('.gfm-form') do
+ click_button('Preview')
+
+ expect(find('.js-md-preview')).to have_content('Nothing to preview.')
+ end
+ end
+
+ it 'renders description preview' do
+ find('.gfm-form').fill_in(:merge_request_description, with: ':+1: Nice')
+
+ page.within('.gfm-form') do
+ click_button('Preview')
+
+ expect(find('.js-md-preview')).to have_css('gl-emoji')
+ end
+
+ expect(find('.gfm-form')).to have_css('.js-md-preview').and have_button('Write')
+ expect(find('#merge_request_description', visible: false)).not_to be_visible
+ end
+ end
+
+ context 'when the branch is rebased on the target' do
+ let(:merge_request) { create(:merge_request, :rebased, source_project: project, target_project: project) }
+
+ before do
+ visit(merge_request_path(merge_request))
+ end
+
+ it 'does not show diverged commits count' do
+ page.within('.mr-source-target') do
+ expect(page).not_to have_content(/([0-9]+ commit[s]? behind)/)
+ end
+ end
+ end
+
+ context 'when the branch is diverged on the target' do
+ let(:merge_request) { create(:merge_request, :diverged, source_project: project, target_project: project) }
+
+ before do
+ visit(merge_request_path(merge_request))
+ end
+
+ it 'shows diverged commits count' do
+ page.within('.mr-source-target') do
+ expect(page).to have_content(/([0-9]+ commits behind)/)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/merge_requests/user_views_user_status_on_merge_request_spec.rb b/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
index 78d9c6c6db1..78d9c6c6db1 100644
--- a/spec/features/projects/merge_requests/user_views_user_status_on_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
diff --git a/spec/features/merge_requests/user_sorts_merge_requests_spec.rb b/spec/features/merge_requests/user_sorts_merge_requests_spec.rb
new file mode 100644
index 00000000000..e163868e8e7
--- /dev/null
+++ b/spec/features/merge_requests/user_sorts_merge_requests_spec.rb
@@ -0,0 +1,92 @@
+require 'spec_helper'
+
+describe 'User sorts merge requests' do
+ include CookieHelper
+
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ let!(:merge_request2) do
+ create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
+ end
+ set(:user) { create(:user) }
+ set(:group) { create(:group) }
+ set(:group_member) { create(:group_member, :maintainer, user: user, group: group) }
+ set(:project) { create(:project, :public, group: group) }
+
+ before do
+ sign_in(user)
+
+ visit(project_merge_requests_path(project))
+ end
+
+ it 'keeps the sort option' do
+ find('button.dropdown-toggle').click
+
+ page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
+ click_link('Milestone')
+ end
+
+ visit(merge_requests_dashboard_path(assignee_username: user.username))
+
+ expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+
+ visit(project_merge_requests_path(project))
+
+ expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+
+ visit(merge_requests_group_path(group))
+
+ expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ end
+
+ it 'fallbacks to issuable_sort cookie key when remembering the sorting option' do
+ set_cookie('issuable_sort', 'milestone')
+
+ visit(merge_requests_dashboard_path(assignee_username: user.username))
+
+ expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ end
+
+ it 'separates remember sorting with issues' do
+ create(:issue, project: project)
+
+ find('button.dropdown-toggle').click
+
+ page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
+ click_link('Milestone')
+ end
+
+ visit(project_issues_path(project))
+
+ expect(find('.issues-filters a.is-active')).not_to have_content('Milestone')
+ end
+
+ context 'when merge requests have awards' do
+ before do
+ create_list(:award_emoji, 2, awardable: merge_request)
+ create(:award_emoji, :downvote, awardable: merge_request)
+
+ create(:award_emoji, awardable: merge_request2)
+ create_list(:award_emoji, 2, :downvote, awardable: merge_request2)
+ end
+
+ it 'sorts by popularity' do
+ find('button.dropdown-toggle').click
+
+ page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
+ click_link('Popularity')
+ end
+
+ page.within('.mr-list') do
+ page.within('li.merge-request:nth-child(1)') do
+ expect(page).to have_content(merge_request.title)
+ expect(page).to have_content('2 1')
+ end
+
+ page.within('li.merge-request:nth-child(2)') do
+ expect(page).to have_content(merge_request2.title)
+ expect(page).to have_content('1 2')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/merge_requests/user_views_all_merge_requests_spec.rb b/spec/features/merge_requests/user_views_all_merge_requests_spec.rb
index 6c695bd7aa9..6c695bd7aa9 100644
--- a/spec/features/projects/merge_requests/user_views_all_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_views_all_merge_requests_spec.rb
diff --git a/spec/features/projects/merge_requests/user_views_closed_merge_requests_spec.rb b/spec/features/merge_requests/user_views_closed_merge_requests_spec.rb
index 853809fe87a..853809fe87a 100644
--- a/spec/features/projects/merge_requests/user_views_closed_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_views_closed_merge_requests_spec.rb
diff --git a/spec/features/projects/merge_requests/user_views_merged_merge_requests_spec.rb b/spec/features/merge_requests/user_views_merged_merge_requests_spec.rb
index eb012694f1e..eb012694f1e 100644
--- a/spec/features/projects/merge_requests/user_views_merged_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_views_merged_merge_requests_spec.rb
diff --git a/spec/features/projects/merge_requests/user_views_open_merge_requests_spec.rb b/spec/features/merge_requests/user_views_open_merge_requests_spec.rb
index 115e548b691..115e548b691 100644
--- a/spec/features/projects/merge_requests/user_views_open_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_views_open_merge_requests_spec.rb
diff --git a/spec/features/milestones/user_creates_milestone_spec.rb b/spec/features/milestones/user_creates_milestone_spec.rb
index 8fd057d587c..5de0c381cdf 100644
--- a/spec/features/milestones/user_creates_milestone_spec.rb
+++ b/spec/features/milestones/user_creates_milestone_spec.rb
@@ -24,6 +24,6 @@ describe "User creates milestone", :js do
visit(activity_project_path(project))
- expect(page).to have_content("#{user.name} opened milestone")
+ expect(page).to have_content("#{user.name} #{user.to_reference} opened milestone")
end
end
diff --git a/spec/features/milestones/user_deletes_milestone_spec.rb b/spec/features/milestones/user_deletes_milestone_spec.rb
index 9d4a68239d3..f68ed1cde07 100644
--- a/spec/features/milestones/user_deletes_milestone_spec.rb
+++ b/spec/features/milestones/user_deletes_milestone_spec.rb
@@ -1,26 +1,46 @@
require "rails_helper"
describe "User deletes milestone", :js do
- set(:user) { create(:user) }
- set(:project) { create(:project) }
- set(:milestone) { create(:milestone, project: project) }
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
before do
- project.add_developer(user)
sign_in(user)
+ end
+
+ context "when milestone belongs to project" do
+ let!(:milestone) { create(:milestone, parent: project, title: "project milestone") }
+
+ it "deletes milestone" do
+ project.add_developer(user)
+ visit(project_milestones_path(project))
+ click_link(milestone.title)
+ click_button("Delete")
+ click_button("Delete milestone")
+
+ expect(page).to have_content("No milestones to show")
+
+ visit(activity_project_path(project))
- visit(project_milestones_path(project))
+ expect(page).to have_content("#{user.name} #{user.to_reference} destroyed milestone")
+ end
end
- it "deletes milestone" do
- click_link(milestone.title)
- click_button("Delete")
- click_button("Delete milestone")
+ context "when milestone belongs to group" do
+ let!(:milestone_to_be_deleted) { create(:milestone, parent: group, title: "group milestone 1") }
+ let!(:milestone) { create(:milestone, parent: group, title: "group milestone 2") }
- expect(page).to have_content("No milestones to show")
+ it "deletes milestone" do
+ group.add_developer(user)
+ visit(group_milestones_path(group))
- visit(activity_project_path(project))
+ click_link(milestone_to_be_deleted.title)
+ click_button("Delete")
+ click_button("Delete milestone")
- expect(page).to have_content("#{user.name} destroyed milestone")
+ expect(page).to have_content(milestone.title)
+ expect(page).not_to have_content(milestone_to_be_deleted)
+ end
end
end
diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb
index 8461cd0027c..dee213a11d4 100644
--- a/spec/features/profiles/personal_access_tokens_spec.rb
+++ b/spec/features/profiles/personal_access_tokens_spec.rb
@@ -43,10 +43,12 @@ describe 'Profile > Personal Access Tokens', :js do
check "read_user"
click_on "Create personal access token"
+
expect(active_personal_access_tokens).to have_text(name)
expect(active_personal_access_tokens).to have_text('In')
expect(active_personal_access_tokens).to have_text('api')
expect(active_personal_access_tokens).to have_text('read_user')
+ expect(created_personal_access_token).not_to be_empty
end
context "when creation fails" do
@@ -57,6 +59,7 @@ describe 'Profile > Personal Access Tokens', :js do
expect { click_on "Create personal access token" }.not_to change { PersonalAccessToken.count }
expect(page).to have_content("Name cannot be nil")
+ expect(page).not_to have_selector("#created-personal-access-token")
end
end
end
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 9e60b4995bd..5e0434c1c2c 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -61,74 +61,232 @@ describe 'User edit profile' do
end
context 'user status', :js do
- def select_emoji(emoji_name)
+ def select_emoji(emoji_name, is_modal = false)
+ emoji_menu_class = is_modal ? '.js-modal-status-emoji-menu' : '.js-status-emoji-menu'
toggle_button = find('.js-toggle-emoji-menu')
toggle_button.click
- emoji_button = find(%Q{.js-status-emoji-menu .js-emoji-btn gl-emoji[data-name="#{emoji_name}"]})
+ emoji_button = find(%Q{#{emoji_menu_class} .js-emoji-btn gl-emoji[data-name="#{emoji_name}"]})
emoji_button.click
end
- it 'shows the user status form' do
- visit(profile_path)
+ context 'profile edit form' do
+ it 'shows the user status form' do
+ visit(profile_path)
- expect(page).to have_content('Current status')
- end
+ expect(page).to have_content('Current status')
+ end
- it 'adds emoji to user status' do
- emoji = 'biohazard'
- visit(profile_path)
- select_emoji(emoji)
- submit_settings
+ it 'adds emoji to user status' do
+ emoji = 'biohazard'
+ visit(profile_path)
+ select_emoji(emoji)
+ submit_settings
- visit user_path(user)
- within('.cover-status') do
- expect(page).to have_emoji(emoji)
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ end
end
- end
- it 'adds message to user status' do
- message = 'I have something to say'
- visit(profile_path)
- fill_in 'js-status-message-field', with: message
- submit_settings
+ it 'adds message to user status' do
+ message = 'I have something to say'
+ visit(profile_path)
+ fill_in 'js-status-message-field', with: message
+ submit_settings
- visit user_path(user)
- within('.cover-status') do
- expect(page).to have_emoji('speech_balloon')
- expect(page).to have_content message
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji('speech_balloon')
+ expect(page).to have_content message
+ end
end
- end
- it 'adds message and emoji to user status' do
- emoji = 'tanabata_tree'
- message = 'Playing outside'
- visit(profile_path)
- select_emoji(emoji)
- fill_in 'js-status-message-field', with: message
- submit_settings
+ it 'adds message and emoji to user status' do
+ emoji = 'tanabata_tree'
+ message = 'Playing outside'
+ visit(profile_path)
+ select_emoji(emoji)
+ fill_in 'js-status-message-field', with: message
+ submit_settings
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ expect(page).to have_content message
+ end
+ end
+
+ it 'clears the user status' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ end
+
+ visit(profile_path)
+ click_button 'js-clear-user-status-button'
+ submit_settings
+
+ wait_for_requests
+
+ visit user_path(user)
+ expect(page).not_to have_selector '.cover-status'
+ end
+
+ it 'displays a default emoji if only message is entered' do
+ message = 'a status without emoji'
+ visit(profile_path)
+ fill_in 'js-status-message-field', with: message
- visit user_path(user)
- within('.cover-status') do
- expect(page).to have_emoji(emoji)
- expect(page).to have_content message
+ within('.js-toggle-emoji-menu') do
+ expect(page).to have_emoji('speech_balloon')
+ end
end
end
- it 'clears the user status' do
- user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+ context 'user menu' do
+ def open_user_status_modal
+ find('.header-user-dropdown-toggle').click
- visit user_path(user)
- within('.cover-status') do
- expect(page).to have_emoji(user_status.emoji)
- expect(page).to have_content user_status.message
+ page.within ".header-user" do
+ click_button 'Set status'
+ end
end
- visit(profile_path)
- click_button 'js-clear-user-status-button'
- submit_settings
+ def set_user_status_in_modal
+ page.within "#set-user-status-modal" do
+ click_button 'Set status'
+ end
+ end
+
+ before do
+ visit root_path(user)
+ end
+
+ it 'shows the "Set status" menu item in the user menu' do
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ expect(page).to have_content('Set status')
+ end
+ end
+
+ it 'shows the "Edit status" menu item in the user menu' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+ visit root_path(user)
+
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ expect(page).to have_content('Edit status')
+ end
+ end
+
+ it 'shows user status modal' do
+ open_user_status_modal
- visit user_path(user)
- expect(page).not_to have_selector '.cover-status'
+ expect(page.find('#set-user-status-modal')).to be_visible
+ expect(page).to have_content('Set a status')
+ end
+
+ it 'adds emoji to user status' do
+ emoji = 'biohazard'
+ open_user_status_modal
+ select_emoji(emoji, true)
+ set_user_status_in_modal
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ end
+ end
+
+ it 'adds message to user status' do
+ message = 'I have something to say'
+ open_user_status_modal
+ find('.js-status-message-field').native.send_keys(message)
+ set_user_status_in_modal
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji('speech_balloon')
+ expect(page).to have_content message
+ end
+ end
+
+ it 'adds message and emoji to user status' do
+ emoji = 'tanabata_tree'
+ message = 'Playing outside'
+ open_user_status_modal
+ select_emoji(emoji, true)
+ find('.js-status-message-field').native.send_keys(message)
+ set_user_status_in_modal
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ expect(page).to have_content message
+ end
+ end
+
+ it 'clears the user status with the "X" button' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ end
+
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ click_button 'Edit status'
+ end
+
+ find('.js-clear-user-status-button').click
+ set_user_status_in_modal
+
+ visit user_path(user)
+ expect(page).not_to have_selector '.cover-status'
+ end
+
+ it 'clears the user status with the "Remove status" button' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ end
+
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ click_button 'Edit status'
+ end
+
+ page.within "#set-user-status-modal" do
+ click_button 'Remove status'
+ end
+
+ visit user_path(user)
+ expect(page).not_to have_selector '.cover-status'
+ end
+
+ it 'displays a default emoji if only message is entered' do
+ message = 'a status without emoji'
+ open_user_status_modal
+ find('.js-status-message-field').native.send_keys(message)
+
+ within('.js-toggle-emoji-menu') do
+ expect(page).to have_emoji('speech_balloon')
+ end
+ end
end
end
end
diff --git a/spec/features/profiles/user_manages_applications_spec.rb b/spec/features/profiles/user_manages_applications_spec.rb
index 387584fef62..34aaab240cc 100644
--- a/spec/features/profiles/user_manages_applications_spec.rb
+++ b/spec/features/profiles/user_manages_applications_spec.rb
@@ -16,7 +16,7 @@ describe 'User manages applications' do
click_on 'Save application'
expect(page).to have_content 'Application: test'
- expect(page).to have_content 'Application Id'
+ expect(page).to have_content 'Application ID'
expect(page).to have_content 'Secret'
click_on 'Edit'
@@ -26,7 +26,7 @@ describe 'User manages applications' do
click_on 'Save application'
expect(page).to have_content 'test_changed'
- expect(page).to have_content 'Application Id'
+ expect(page).to have_content 'Application ID'
expect(page).to have_content 'Secret'
visit applications_profile_path
diff --git a/spec/features/projects/activity/user_sees_activity_spec.rb b/spec/features/projects/activity/user_sees_activity_spec.rb
index e0248911b5f..bb4b2abc3c7 100644
--- a/spec/features/projects/activity/user_sees_activity_spec.rb
+++ b/spec/features/projects/activity/user_sees_activity_spec.rb
@@ -3,8 +3,10 @@ require 'spec_helper'
describe 'Projects > Activity > User sees activity' do
let(:project) { create(:project, :repository, :public) }
let(:user) { project.creator }
+ let(:issue) { create(:issue, project: project) }
before do
+ create(:event, :created, project: project, target: issue, author: user)
event = create(:push_event, project: project, author: user)
create(:push_event_payload,
event: event,
@@ -12,10 +14,18 @@ describe 'Projects > Activity > User sees activity' do
commit_to: '6d394385cf567f80a8fd85055db1ab4c5295806f',
ref: 'fix',
commit_count: 1)
- visit activity_project_path(project)
end
it 'shows the last push in the activity page', :js do
- expect(page).to have_content "#{user.name} pushed new branch fix"
+ visit activity_project_path(project)
+
+ expect(page).to have_content "#{user.name} #{user.to_reference} pushed new branch fix"
+ end
+
+ it 'allows to filter event with the "event_filter=issue" URL param', :js do
+ visit activity_project_path(project, event_filter: 'issue')
+
+ expect(page).not_to have_content "#{user.name} #{user.to_reference} pushed new branch fix"
+ expect(page).to have_content "#{user.name} #{user.to_reference} opened issue #{issue.to_reference}"
end
end
diff --git a/spec/features/projects/activity/user_sees_private_activity_spec.rb b/spec/features/projects/activity/user_sees_private_activity_spec.rb
new file mode 100644
index 00000000000..61ec2ce9d29
--- /dev/null
+++ b/spec/features/projects/activity/user_sees_private_activity_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe 'Project > Activity > User sees private activity', :js do
+ let(:project) { create(:project, :public) }
+ let(:author) { create(:user) }
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, :confidential, project: project, author: author) }
+ let(:message) { "#{author.name} #{author.to_reference} opened issue #{issue.to_reference}" }
+
+ before do
+ project.add_developer(author)
+
+ create(:event, :created, project: project, target: issue, author: author)
+ end
+
+ it 'shows the activity to a logged-in user with permissions' do
+ sign_in(author)
+ visit activity_project_path(project)
+
+ expect(page).to have_content(message)
+ end
+
+ it 'hides the activity from a logged-in user without permissions' do
+ sign_in(user)
+ visit activity_project_path(project)
+
+ expect(page).not_to have_content(message)
+ end
+
+ it 'hides the activity from an anonymous user' do
+ visit activity_project_path(project)
+
+ expect(page).not_to have_content(message)
+ end
+end
diff --git a/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb b/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
index 67ed2f18d76..554f0b49052 100644
--- a/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
+++ b/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
@@ -20,23 +20,13 @@ describe "User downloads artifacts" do
end
context "via job id" do
- set(:url) { download_project_job_artifacts_path(project, job) }
+ let(:url) { download_project_job_artifacts_path(project, job) }
it_behaves_like "downloading"
end
context "via branch name and job name" do
- set(:url) { latest_succeeded_project_artifacts_path(project, "#{pipeline.ref}/download", job: job.name) }
-
- it_behaves_like "downloading"
- end
-
- context "via clicking the `Download` button" do
- set(:url) { project_job_path(project, job) }
-
- before do
- click_link("Download")
- end
+ let(:url) { latest_succeeded_project_artifacts_path(project, "#{pipeline.ref}/download", job: job.name) }
it_behaves_like "downloading"
end
diff --git a/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb b/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
deleted file mode 100644
index 4d860893abe..00000000000
--- a/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
+++ /dev/null
@@ -1,172 +0,0 @@
-require 'spec_helper'
-
-describe 'User interacts with awards in an issue', :js do
- let(:issue) { create(:issue, project: project)}
- let(:project) { create(:project) }
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
-
- visit(project_issue_path(project, issue))
- end
-
- it 'toggles the thumbsup award emoji' do
- page.within('.awards') do
- thumbsup = page.first('.award-control')
- thumbsup.click
- thumbsup.hover
-
- expect(page).to have_selector('.js-emoji-btn')
- expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
- expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
-
- thumbsup = page.first('.award-control')
- thumbsup.click
- thumbsup.hover
-
- expect(page).to have_selector('.award-control.js-emoji-btn')
- expect(page.all('.award-control.js-emoji-btn').size).to eq(2)
-
- page.all('.award-control.js-emoji-btn').each do |element|
- expect(element['title']).to eq('')
- end
-
- page.all('.award-control .js-counter').each do |element|
- expect(element).to have_content('0')
- end
-
- thumbsup = page.first('.award-control')
- thumbsup.click
- thumbsup.hover
-
- expect(page).to have_selector('.js-emoji-btn')
- expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
- expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
- end
- end
-
- it 'toggles a custom award emoji' do
- page.within('.awards') do
- page.find('.js-add-award').click
- end
-
- page.find('.emoji-menu.is-visible')
-
- expect(page).to have_selector('.js-emoji-menu-search')
- expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
-
- page.within('.emoji-menu-content') do
- emoji_button = page.first('.js-emoji-btn')
- emoji_button.hover
- emoji_button.click
- end
-
- page.within('.awards') do
- expect(page).to have_selector('.js-emoji-btn')
- expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
- expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
-
- expect do
- page.find('.js-emoji-btn.active').click
- wait_for_requests
- end.to change { page.all('.award-control.js-emoji-btn').size }.from(3).to(2)
- end
- end
-
- it 'shows the list of award emoji categories' do
- page.within('.awards') do
- page.find('.js-add-award').click
- end
-
- page.find('.emoji-menu.is-visible')
-
- expect(page).to have_selector('.js-emoji-menu-search')
- expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
-
- fill_in('emoji-menu-search', with: 'hand')
-
- page.within('.emoji-menu-content') do
- expect(page).to have_selector('[data-name="raised_hand"]')
- end
- end
-
- it 'adds an award emoji by a comment' do
- page.within('.js-main-target-form') do
- fill_in('note[note]', with: ':smile:')
-
- click_button('Comment')
- end
-
- expect(page).to have_emoji('smile')
- end
-
- context 'when a project is archived' do
- let(:project) { create(:project, :archived) }
-
- it 'hides the add award button' do
- page.within('.awards') do
- expect(page).not_to have_css('.js-add-award')
- end
- end
- end
-
- context 'awards on a note' do
- let!(:note) { create(:note, noteable: issue, project: issue.project) }
- let!(:award_emoji) { create(:award_emoji, awardable: note, name: '100') }
-
- it 'shows the award on the note' do
- page.within('.note-awards') do
- expect(page).to have_emoji('100')
- end
- end
-
- it 'allows adding a vote to an award' do
- page.within('.note-awards') do
- find('gl-emoji[data-name="100"]').click
- end
- wait_for_requests
-
- expect(note.reload.award_emoji.size).to eq(2)
- end
-
- it 'allows adding a new emoji' do
- page.within('.note-actions') do
- find('a.js-add-award').click
- end
- page.within('.emoji-menu-content') do
- find('gl-emoji[data-name="8ball"]').click
- end
- wait_for_requests
-
- page.within('.note-awards') do
- expect(page).to have_emoji('8ball')
- end
- expect(note.reload.award_emoji.size).to eq(2)
- end
-
- context 'when the project is archived' do
- let(:project) { create(:project, :archived) }
-
- it 'hides the buttons for adding new emoji' do
- page.within('.note-awards') do
- expect(page).not_to have_css('.award-menu-holder')
- end
-
- page.within('.note-actions') do
- expect(page).not_to have_css('a.js-add-award')
- end
- end
-
- it 'does not allow toggling existing emoji' do
- page.within('.note-awards') do
- find('gl-emoji[data-name="100"]').click
- end
- wait_for_requests
-
- expect(note.reload.award_emoji.size).to eq(1)
- end
- end
- end
-end
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
index 8c4488b2ca6..dee81898928 100644
--- a/spec/features/projects/badges/pipeline_badge_spec.rb
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -19,7 +19,7 @@ describe 'Pipeline Badge' do
let!(:pipeline) { create(:ci_empty_pipeline, project: project, ref: ref, sha: project.commit(ref).sha) }
let!(:job) { create(:ci_build, pipeline: pipeline) }
- context 'when the pipeline was successfull' do
+ context 'when the pipeline was successful' do
it 'displays so on the badge' do
job.success
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 1064f72c271..e2f9e7e9cc5 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -5,8 +5,8 @@ describe 'File blob', :js do
let(:project) { create(:project, :public, :repository) }
- def visit_blob(path, anchor: nil, ref: 'master')
- visit project_blob_path(project, File.join(ref, path), anchor: anchor)
+ def visit_blob(path, anchor: nil, ref: 'master', legacy_render: nil)
+ visit project_blob_path(project, File.join(ref, path), anchor: anchor, legacy_render: legacy_render)
wait_for_requests
end
@@ -142,6 +142,52 @@ describe 'File blob', :js do
end
end
+ context 'Markdown rendering' do
+ before do
+ project.add_maintainer(project.creator)
+
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add RedCarpet and CommonMark Markdown ",
+ file_path: 'files/commonmark/file.md',
+ file_content: "1. one\n - sublist\n"
+ ).execute
+ end
+
+ context 'when rendering default markdown' do
+ before do
+ visit_blob('files/commonmark/file.md')
+
+ wait_for_requests
+ end
+
+ it 'renders using CommonMark' do
+ aggregate_failures do
+ expect(page).to have_content("sublist")
+ expect(page).not_to have_xpath("//ol//li//ul")
+ end
+ end
+ end
+
+ context 'when rendering legacy markdown' do
+ before do
+ visit_blob('files/commonmark/file.md', legacy_render: 1)
+
+ wait_for_requests
+ end
+
+ it 'renders using RedCarpet' do
+ aggregate_failures do
+ expect(page).to have_content("sublist")
+ expect(page).to have_xpath("//ol//li//ul")
+ end
+ end
+ end
+ end
+
context 'Markdown file (stored in LFS)' do
before do
project.add_maintainer(project.creator)
@@ -487,7 +533,7 @@ describe 'File blob', :js do
expect(page).to have_content('This project is licensed under the MIT License.')
# shows a learn more link
- expect(page).to have_link('Learn more', 'http://choosealicense.com/licenses/mit/')
+ expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/')
end
end
end
@@ -520,10 +566,10 @@ describe 'File blob', :js do
expect(page).to have_content('This project manages its dependencies using RubyGems and defines a gem named activerecord.')
# shows a link to the gem
- expect(page).to have_link('activerecord', 'https://rubygems.org/gems/activerecord')
+ expect(page).to have_link('activerecord', href: 'https://rubygems.org/gems/activerecord')
# shows a learn more link
- expect(page).to have_link('Learn more', 'http://choosealicense.com/licenses/mit/')
+ expect(page).to have_link('Learn more', href: 'https://rubygems.org/')
end
end
end
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 0e036b4ea68..d5b20605860 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -7,6 +7,7 @@ describe 'Editing file blob', :js do
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') }
let(:branch) { 'master' }
let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] }
+ let(:readme_file_path) { 'README.md' }
context 'as a developer' do
let(:user) { create(:user) }
@@ -20,14 +21,19 @@ describe 'Editing file blob', :js do
def edit_and_commit(commit_changes: true)
wait_for_requests
find('.js-edit-blob').click
- find('#editor')
- execute_script('ace.edit("editor").setValue("class NextFeature\nend\n")')
+ fill_editor(content: "class NextFeature\\nend\\n")
if commit_changes
click_button 'Commit changes'
end
end
+ def fill_editor(content: "class NextFeature\\nend\\n")
+ wait_for_requests
+ find('#editor')
+ execute_script("ace.edit('editor').setValue('#{content}')")
+ end
+
context 'from MR diff' do
before do
visit diffs_project_merge_request_path(project, merge_request)
@@ -63,6 +69,30 @@ describe 'Editing file blob', :js do
expect(new_line_count).to be > 0
end
end
+
+ context 'when rendering the preview' do
+ it 'renders content with CommonMark' do
+ visit project_edit_blob_path(project, tree_join(branch, readme_file_path))
+ fill_editor(content: "1. one\\n - sublist\\n")
+ click_link 'Preview'
+ wait_for_requests
+
+ # the above generates two seperate lists (not embedded) in CommonMark
+ expect(page).to have_content("sublist")
+ expect(page).not_to have_xpath("//ol//li//ul")
+ end
+
+ it 'renders content with RedCarpet when legacy_render is set' do
+ visit project_edit_blob_path(project, tree_join(branch, readme_file_path), legacy_render: 1)
+ fill_editor(content: "1. one\\n - sublist\\n")
+ click_link 'Preview'
+ wait_for_requests
+
+ # the above generates a sublist list in RedCarpet
+ expect(page).to have_content("sublist")
+ expect(page).to have_xpath("//ol//li//ul")
+ end
+ end
end
context 'visit blob edit' do
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 31e3ebf675d..3d17eb3a73a 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -33,6 +33,32 @@ describe 'Gcp Cluster', :js do
context 'when user filled form with valid parameters' do
subject { click_button 'Create Kubernetes cluster' }
+ shared_examples 'valid cluster gcp form' do
+ it 'users sees a form with the GCP token' do
+ expect(page).to have_selector(:css, 'form[data-token="token"]')
+ end
+
+ it 'user sees a cluster details page and creation status' do
+ subject
+
+ expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+
+ Clusters::Cluster.last.provider.make_created!
+
+ expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
+ end
+
+ it 'user sees a error if something wrong during creation' do
+ subject
+
+ expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+
+ Clusters::Cluster.last.provider.make_errored!('Something wrong!')
+
+ expect(page).to have_content('Something wrong!')
+ end
+ end
+
before do
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
.to receive(:projects_zones_clusters_create) do
@@ -56,28 +82,14 @@ describe 'Gcp Cluster', :js do
fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
end
- it 'users sees a form with the GCP token' do
- expect(page).to have_selector(:css, 'form[data-token="token"]')
- end
-
- it 'user sees a cluster details page and creation status' do
- subject
-
- expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
-
- Clusters::Cluster.last.provider.make_created!
+ it_behaves_like 'valid cluster gcp form'
- expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
- end
-
- it 'user sees a error if something wrong during creation' do
- subject
-
- expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
-
- Clusters::Cluster.last.provider.make_errored!('Something wrong!')
+ context 'RBAC is enabled for the cluster' do
+ before do
+ check 'cluster_provider_gcp_attributes_legacy_abac'
+ end
- expect(page).to have_content('Something wrong!')
+ it_behaves_like 'valid cluster gcp form'
end
end
@@ -118,6 +130,7 @@ describe 'Gcp Cluster', :js do
context 'when user changes cluster parameters' do
before do
+ allow(ClusterPlatformConfigureWorker).to receive(:perform_async)
fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
page.within('#js-cluster-details') { click_button 'Save changes' }
end
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index babf47cc341..250c964cc32 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -9,7 +9,9 @@ describe 'User Cluster', :js do
before do
project.add_maintainer(user)
gitlab_sign_in(user)
+
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
+ allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
end
context 'when user does not have a cluster and visits cluster index page' do
@@ -21,20 +23,41 @@ describe 'User Cluster', :js do
end
context 'when user filled form with valid parameters' do
+ shared_examples 'valid cluster user form' do
+ it 'user sees a cluster details page' do
+ subject
+
+ expect(page).to have_content('Kubernetes cluster integration')
+ expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
+ expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
+ .to have_content('http://example.com')
+ expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
+ .to have_content('my-token')
+ end
+ end
+
before do
fill_in 'cluster_name', with: 'dev-cluster'
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com'
fill_in 'cluster_platform_kubernetes_attributes_token', with: 'my-token'
- click_button 'Add Kubernetes cluster'
end
- it 'user sees a cluster details page' do
- expect(page).to have_content('Kubernetes cluster integration')
- expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
- expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
- .to have_content('http://example.com')
- expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
- .to have_content('my-token')
+ subject { click_button 'Add Kubernetes cluster' }
+
+ it_behaves_like 'valid cluster user form'
+
+ context 'RBAC is enabled for the cluster' do
+ before do
+ check 'cluster_platform_kubernetes_attributes_authorization_type'
+ end
+
+ it_behaves_like 'valid cluster user form'
+
+ it 'user sees a cluster details page with RBAC enabled' do
+ subject
+
+ expect(page.find_field('cluster[platform_kubernetes_attributes][authorization_type]', disabled: true)).to be_checked
+ end
end
end
@@ -63,7 +86,6 @@ describe 'User Cluster', :js do
context 'when user disables the cluster' do
before do
page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click
- fill_in 'cluster_name', with: 'dev-cluster'
page.within('#cluster-integration') { click_button 'Save changes' }
end
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index 91eac9c8278..a85e7333ba8 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -35,37 +35,6 @@ describe 'Clusters', :js do
expect(page).to have_selector('.gl-responsive-table-row', count: 2)
end
- context 'inline update of cluster' do
- it 'user can update cluster' do
- expect(page).to have_selector('.js-project-feature-toggle')
- end
-
- context 'with sucessfull request' do
- it 'user sees updated cluster' do
- expect do
- page.find('.js-project-feature-toggle').click
- wait_for_requests
- end.to change { cluster.reload.enabled }
-
- expect(page).not_to have_selector('.is-checked')
- expect(cluster.reload).not_to be_enabled
- end
- end
-
- context 'with failed request' do
- it 'user sees not update cluster and error message' do
- expect_any_instance_of(Clusters::UpdateService).to receive(:execute).and_call_original
- allow_any_instance_of(Clusters::Cluster).to receive(:valid?) { false }
-
- page.find('.js-project-feature-toggle').click
-
- expect(page).to have_content('Something went wrong on our end.')
- expect(page).to have_selector('.is-checked')
- expect(cluster.reload).to be_enabled
- end
- end
- end
-
context 'when user clicks on a cluster' do
before do
click_link cluster.name
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index bc3c00dafe2..a61b614dbc8 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -21,7 +21,7 @@ describe 'Cherry-pick Commits' do
uncheck 'create_merge_request'
click_button 'Cherry-pick'
end
- expect(page).to have_content('The commit has been successfully cherry-picked.')
+ expect(page).to have_content('The commit has been successfully cherry-picked into master.')
end
end
@@ -32,7 +32,7 @@ describe 'Cherry-pick Commits' do
uncheck 'create_merge_request'
click_button 'Cherry-pick'
end
- expect(page).to have_content('The commit has been successfully cherry-picked.')
+ expect(page).to have_content('The commit has been successfully cherry-picked into master.')
end
end
@@ -59,7 +59,7 @@ describe 'Cherry-pick Commits' do
page.within('#modal-cherry-pick-commit') do
click_button 'Cherry-pick'
end
- expect(page).to have_content('The commit has been successfully cherry-picked. You can now submit a merge request to get this change into the original branch.')
+ expect(page).to have_content("The commit has been successfully cherry-picked into cherry-pick-#{master_pickable_commit.short_id}. You can now submit a merge request to get this change into the original branch.")
expect(page).to have_content("From cherry-pick-#{master_pickable_commit.short_id} into master")
end
end
@@ -86,7 +86,7 @@ describe 'Cherry-pick Commits' do
click_button 'Cherry-pick'
end
- expect(page).to have_content('The commit has been successfully cherry-picked.')
+ expect(page).to have_content('The commit has been successfully cherry-picked into feature.')
end
end
diff --git a/spec/features/projects/commit/comments/user_adds_comment_spec.rb b/spec/features/projects/commit/comments/user_adds_comment_spec.rb
index 6397df086a7..29442a58ea4 100644
--- a/spec/features/projects/commit/comments/user_adds_comment_spec.rb
+++ b/spec/features/projects/commit/comments/user_adds_comment_spec.rb
@@ -28,19 +28,19 @@ describe "User adds a comment on a commit", :js do
fill_in("note[note]", with: "#{comment_text} #{emoji}")
# Check on `Preview` tab
- click_link("Preview")
+ click_button("Preview")
expect(find(".js-md-preview")).to have_content(comment_text).and have_css("gl-emoji")
expect(page).not_to have_css(".js-note-text")
# Check on the `Write` tab
- click_link("Write")
+ click_button("Write")
expect(page).to have_field("note[note]", with: "#{comment_text} #{emoji}")
# Submit comment from the `Preview` tab to get rid of a separate `it` block
# which would specially tests if everything gets cleared from the note form.
- click_link("Preview")
+ click_button("Preview")
click_button("Comment")
end
@@ -88,13 +88,13 @@ describe "User adds a comment on a commit", :js do
# Test Preview feature for both forms.
page.within("form[data-line-code='#{sample_commit.line_code}']") do
- click_link("Preview")
+ click_button("Preview")
end
page.within("form[data-line-code='#{sample_commit.del_line_code}']") do
fill_in("note[note]", with: another_comment_text)
- click_link("Preview")
+ click_button("Preview")
end
expect(page).to have_css(".js-md-preview", visible: true, count: 2)
diff --git a/spec/features/projects/commit/user_comments_on_commit_spec.rb b/spec/features/projects/commit/user_comments_on_commit_spec.rb
index 6397a8ad845..73ce8d2b996 100644
--- a/spec/features/projects/commit/user_comments_on_commit_spec.rb
+++ b/spec/features/projects/commit/user_comments_on_commit_spec.rb
@@ -25,19 +25,19 @@ describe "User comments on commit", :js do
fill_in("note[note]", with: "#{comment_text} #{emoji_code}")
# Check on `Preview` tab
- click_link("Preview")
+ click_button("Preview")
expect(find(".js-md-preview")).to have_content(comment_text).and have_css("gl-emoji")
expect(page).not_to have_css(".js-note-text")
# Check on `Write` tab
- click_link("Write")
+ click_button("Write")
expect(page).to have_field("note[note]", with: "#{comment_text} #{emoji_code}")
# Submit comment from the `Preview` tab to get rid of a separate `it` block
# which would specially tests if everything gets cleared from the note form.
- click_link("Preview")
+ click_button("Preview")
click_button("Comment")
end
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index 4c5dda29fee..9772a7bacac 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -25,7 +25,7 @@ describe 'Environment' do
end
context 'without deployments' do
- it 'does show no deployments' do
+ it 'does not show deployments' do
expect(page).to have_content('You don\'t have any deployments right now.')
end
end
@@ -33,7 +33,7 @@ describe 'Environment' do
context 'with deployments' do
context 'when there is no related deployable' do
let(:deployment) do
- create(:deployment, environment: environment, deployable: nil)
+ create(:deployment, :success, environment: environment, deployable: nil)
end
it 'does show deployment SHA' do
@@ -43,24 +43,74 @@ describe 'Environment' do
end
end
+ context 'when there is a successful deployment' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+
+ let(:deployment) do
+ create(:deployment, :success, environment: environment, deployable: build)
+ end
+
+ it 'does show deployments' do
+ expect(page).to have_link("#{build.name} (##{build.id})")
+ end
+ end
+
+ context 'when there is a running deployment' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
+ let(:deployment) do
+ create(:deployment, :running, environment: environment, deployable: build)
+ end
+
+ it 'does not show deployments' do
+ expect(page).to have_content('You don\'t have any deployments right now.')
+ end
+ end
+
+ context 'when there is a failed deployment' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
+ let(:deployment) do
+ create(:deployment, :failed, environment: environment, deployable: build)
+ end
+
+ it 'does not show deployments' do
+ expect(page).to have_content('You don\'t have any deployments right now.')
+ end
+ end
+
context 'with related deployable present' do
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:deployment) do
- create(:deployment, environment: environment, deployable: build)
+ create(:deployment, :success, environment: environment, deployable: build)
end
it 'does show build name' do
expect(page).to have_link("#{build.name} (##{build.id})")
- expect(page).to have_link('Re-deploy')
+ expect(page).not_to have_link('Re-deploy')
expect(page).not_to have_terminal_button
end
+ context 'when user has ability to re-deploy' do
+ let(:permissions) do
+ create(:protected_branch, :developers_can_merge,
+ name: build.ref, project: project)
+ end
+
+ it 'does show re-deploy' do
+ expect(page).to have_link('Re-deploy')
+ end
+ end
+
context 'with manual action' do
let(:action) do
create(:ci_build, :manual, pipeline: pipeline,
- name: 'deploy to production')
+ name: 'deploy to production', environment: environment.name)
end
context 'when user has ability to trigger deployment' do
@@ -73,12 +123,16 @@ describe 'Environment' do
expect(page).to have_link(action.name.humanize)
end
- it 'does allow to play manual action' do
+ it 'does allow to play manual action', :js do
expect(action).to be_manual
+ find('button.dropdown').click
+
expect { click_link(action.name.humanize) }
.not_to change { Ci::Pipeline.count }
+ wait_for_all_requests
+
expect(page).to have_content(action.name)
expect(action.reload).to be_pending
end
@@ -93,7 +147,7 @@ describe 'Environment' do
context 'with external_url' do
let(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
let(:build) { create(:ci_build, pipeline: pipeline) }
- let(:deployment) { create(:deployment, environment: environment, deployable: build) }
+ let(:deployment) { create(:deployment, :success, environment: environment, deployable: build) }
it 'does show an external link button' do
expect(page).to have_link(nil, href: environment.external_url)
@@ -154,7 +208,8 @@ describe 'Environment' do
end
let(:deployment) do
- create(:deployment, environment: environment,
+ create(:deployment, :success,
+ environment: environment,
deployable: build,
on_stop: 'close_app')
end
@@ -165,10 +220,10 @@ describe 'Environment' do
name: action.ref, project: project)
end
- it 'allows to stop environment' do
+ it 'allows to stop environment', :js do
click_button('Stop')
click_button('Stop environment') # confirm modal
-
+ wait_for_all_requests
expect(page).to have_content('close_app')
end
end
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index f0890018286..89954d35f91 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -95,7 +95,7 @@ describe 'Environments page', :js do
end
it 'does not show environments and counters are set to zero' do
- expect(page).to have_content('You don\'t have any environments right now.')
+ expect(page).to have_content('You don\'t have any environments right now')
expect(page.find('.js-environments-tab-available .badge').text).to eq('0')
expect(page.find('.js-environments-tab-stopped .badge').text).to eq('0')
@@ -128,11 +128,12 @@ describe 'Environments page', :js do
end
end
- context 'when there are deployments' do
+ context 'when there are successful deployments' do
let(:project) { create(:project, :repository) }
let!(:deployment) do
- create(:deployment, environment: environment,
+ create(:deployment, :success,
+ environment: environment,
sha: project.commit.id)
end
@@ -152,7 +153,8 @@ describe 'Environments page', :js do
end
let!(:deployment) do
- create(:deployment, environment: environment,
+ create(:deployment, :success,
+ environment: environment,
deployable: build,
sha: project.commit.id)
end
@@ -162,7 +164,7 @@ describe 'Environments page', :js do
end
it 'shows a play button' do
- find('.js-dropdown-play-icon-container').click
+ find('.js-environment-actions-dropdown').click
expect(page).to have_content(action.name.humanize)
end
@@ -170,7 +172,7 @@ describe 'Environments page', :js do
it 'allows to play a manual action', :js do
expect(action).to be_manual
- find('.js-dropdown-play-icon-container').click
+ find('.js-environment-actions-dropdown').click
expect(page).to have_content(action.name.humanize)
expect { find('.js-manual-action-link').click }
@@ -196,7 +198,7 @@ describe 'Environments page', :js do
context 'with external_url' do
let(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
let(:build) { create(:ci_build, pipeline: pipeline) }
- let(:deployment) { create(:deployment, environment: environment, deployable: build) }
+ let(:deployment) { create(:deployment, :success, environment: environment, deployable: build) }
it 'shows an external link button' do
expect(page).to have_link(nil, href: environment.external_url)
@@ -209,7 +211,8 @@ describe 'Environments page', :js do
end
let(:deployment) do
- create(:deployment, environment: environment,
+ create(:deployment, :success,
+ environment: environment,
deployable: build,
on_stop: 'close_app')
end
@@ -260,6 +263,86 @@ describe 'Environments page', :js do
end
end
end
+
+ context 'when there is a delayed job' do
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:build) { create(:ci_build, pipeline: pipeline) }
+
+ let!(:delayed_job) do
+ create(:ci_build, :scheduled,
+ pipeline: pipeline,
+ name: 'delayed job',
+ stage: 'test',
+ commands: 'test')
+ end
+
+ let!(:deployment) do
+ create(:deployment,
+ :success,
+ environment: environment,
+ deployable: build,
+ sha: project.commit.id)
+ end
+
+ before do
+ visit_environments(project)
+ end
+
+ it 'has a dropdown for actionable jobs' do
+ expect(page).to have_selector('.dropdown-new.btn.btn-default .ic-play')
+ end
+
+ it "has link to the delayed job's action" do
+ find('.js-environment-actions-dropdown').click
+
+ expect(page).to have_button('Delayed job')
+ expect(page).to have_content(/\d{2}:\d{2}:\d{2}/)
+ end
+
+ context 'when delayed job is expired already' do
+ let!(:delayed_job) do
+ create(:ci_build, :expired_scheduled,
+ pipeline: pipeline,
+ name: 'delayed job',
+ stage: 'test',
+ commands: 'test')
+ end
+
+ it "shows 00:00:00 as the remaining time" do
+ find('.js-environment-actions-dropdown').click
+
+ expect(page).to have_content("00:00:00")
+ end
+ end
+
+ context 'when user played a delayed job immediately' do
+ before do
+ find('.js-environment-actions-dropdown').click
+ page.accept_confirm { click_button('Delayed job') }
+ wait_for_requests
+ end
+
+ it 'enqueues the delayed job', :js do
+ expect(delayed_job.reload).to be_pending
+ end
+ end
+ end
+ end
+
+ context 'when there is a failed deployment' do
+ let(:project) { create(:project, :repository) }
+
+ let!(:deployment) do
+ create(:deployment, :failed,
+ environment: environment,
+ sha: project.commit.id)
+ end
+
+ it 'does not show deployments' do
+ visit_environments(project)
+
+ expect(page).to have_content('No deployments yet')
+ end
end
end
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index ac6c8c337fa..6762460971f 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -36,7 +36,7 @@ describe 'Projects > Files > Project owner creates a license file', :js do
end
it 'project maintainer creates a license file from the "Add license" link' do
- click_link 'Add License'
+ click_link 'Add license'
expect(page).to have_content('New file')
expect(current_path).to eq(
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 801291c1f77..0b8474fb87a 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -10,7 +10,7 @@ describe 'Projects > Files > Project owner sees a link to create a license file
it 'project maintainer creates a license file from a template' do
visit project_path(project)
- click_on 'Add License'
+ click_on 'Add license'
expect(page).to have_content('New file')
expect(current_path).to eq(
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index d4dda43c823..a4f94b7a76d 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -16,7 +16,7 @@ describe 'Projects > Files > User creates files' do
sign_in(user)
end
- context 'without commiting a new file' do
+ context 'without committing a new file' do
context 'when an user has write access' do
before do
visit(project_tree_path_root_ref)
@@ -49,7 +49,7 @@ describe 'Projects > Files > User creates files' do
end
end
- context 'with commiting a new file' do
+ context 'with committing a new file' do
context 'when an user has write access' do
before do
visit(project_tree_path_root_ref)
diff --git a/spec/features/projects/files/user_deletes_files_spec.rb b/spec/features/projects/files/user_deletes_files_spec.rb
index dcb7b947c61..614b11fa5c8 100644
--- a/spec/features/projects/files/user_deletes_files_spec.rb
+++ b/spec/features/projects/files/user_deletes_files_spec.rb
@@ -31,7 +31,7 @@ describe 'Projects > Files > User deletes files', :js do
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Delete file')
- expect(current_path).to eq(project_tree_path(project, 'master'))
+ expect(current_path).to eq(project_tree_path(project, 'master/'))
expect(page).not_to have_content('.gitignore')
end
end
diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb
index cd5fef8238e..7c71b4c52e0 100644
--- a/spec/features/projects/fork_spec.rb
+++ b/spec/features/projects/fork_spec.rb
@@ -53,6 +53,18 @@ describe 'Project fork' do
expect(current_path).to have_content(/#{user.namespace.name}/i)
end
+ it 'shows avatars when Gravatar is disabled' do
+ stub_application_setting(gravatar_enabled: false)
+
+ visit project_path(project)
+
+ click_link 'Fork'
+
+ page.within('.fork-thumbnail-container') do
+ expect(page).to have_css('div.identicon')
+ end
+ end
+
it 'shows the forked project on the list' do
visit project_path(project)
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index eb281cd2122..f76f9ba7577 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -12,7 +12,7 @@ describe 'Import/Export - project export integration test', :js do
let(:export_path) { "#{Dir.tmpdir}/import_file_spec" }
let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys }
- let(:sensitive_words) { %w[pass secret token key] }
+ let(:sensitive_words) { %w[pass secret token key encrypted] }
let(:safe_list) do
{
token: [ProjectHook, Ci::Trigger, CommitStatus],
@@ -25,7 +25,6 @@ describe 'Import/Export - project export integration test', :js do
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
- stub_feature_flags(import_export_object_storage: false)
end
after do
@@ -50,6 +49,8 @@ describe 'Import/Export - project export integration test', :js do
expect(file_permissions(project.export_path)).to eq(0700)
+ expect(project.export_file.path).to include('tar.gz')
+
in_directory_with_expanded_export(project) do |exit_status, tmpdir|
expect(exit_status).to eq(0)
diff --git a/spec/features/projects/import_export/import_file_object_storage_spec.rb b/spec/features/projects/import_export/import_file_object_storage_spec.rb
deleted file mode 100644
index 0d364543916..00000000000
--- a/spec/features/projects/import_export/import_file_object_storage_spec.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-require 'spec_helper'
-
-describe 'Import/Export - project import integration test', :js do
- include Select2Helper
-
- let(:user) { create(:user) }
- let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
- let(:export_path) { "#{Dir.tmpdir}/import_file_spec" }
-
- before do
- stub_feature_flags(import_export_object_storage: true)
- stub_uploads_object_storage(FileUploader)
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
- gitlab_sign_in(user)
- end
-
- after do
- FileUtils.rm_rf(export_path, secure: true)
- end
-
- context 'when selecting the namespace' do
- let(:user) { create(:admin) }
- let!(:namespace) { user.namespace }
- let(:project_path) { 'test-project-path' + SecureRandom.hex }
-
- context 'prefilled the path' do
- it 'user imports an exported project successfully' do
- visit new_project_path
-
- select2(namespace.id, from: '#project_namespace_id')
- fill_in :project_path, with: project_path, visible: true
- click_import_project_tab
- click_link 'GitLab export'
-
- expect(page).to have_content('Import an exported GitLab project')
- expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=#{project_path}")
-
- attach_file('file', file)
- click_on 'Import project'
-
- expect(Project.count).to eq(1)
-
- project = Project.last
- expect(project).not_to be_nil
- expect(project.description).to eq("Foo Bar")
- expect(project.issues).not_to be_empty
- expect(project.merge_requests).not_to be_empty
- expect(project_hook_exists?(project)).to be true
- expect(wiki_exists?(project)).to be true
- expect(project.import_state.status).to eq('finished')
- end
- end
-
- context 'path is not prefilled' do
- it 'user imports an exported project successfully' do
- visit new_project_path
- click_import_project_tab
- click_link 'GitLab export'
-
- fill_in :path, with: 'test-project-path', visible: true
- attach_file('file', file)
-
- expect { click_on 'Import project' }.to change { Project.count }.by(1)
-
- project = Project.last
- expect(project).not_to be_nil
- expect(page).to have_content("Project 'test-project-path' is being imported")
- end
- end
- end
-
- it 'invalid project' do
- project = create(:project, namespace: user.namespace)
-
- visit new_project_path
-
- select2(user.namespace.id, from: '#project_namespace_id')
- fill_in :project_path, with: project.name, visible: true
- click_import_project_tab
- click_link 'GitLab export'
- attach_file('file', file)
- click_on 'Import project'
-
- page.within('.flash-container') do
- expect(page).to have_content('Project could not be imported')
- end
- end
-
- def wiki_exists?(project)
- wiki = ProjectWiki.new(project)
- wiki.repository.exists? && !wiki.repository.empty?
- end
-
- def project_hook_exists?(project)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository).exists?
- end
- end
-
- def click_import_project_tab
- find('#import-project-tab').click
- end
-end
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index 2d86115de12..28ae90bc0de 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -2,13 +2,14 @@ require 'spec_helper'
describe 'Import/Export - project import integration test', :js do
include Select2Helper
+ include GitHelpers
let(:user) { create(:user) }
let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
let(:export_path) { "#{Dir.tmpdir}/import_file_spec" }
before do
- stub_feature_flags(import_export_object_storage: false)
+ stub_uploads_object_storage(FileUploader)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
gitlab_sign_in(user)
end
@@ -20,20 +21,21 @@ describe 'Import/Export - project import integration test', :js do
context 'when selecting the namespace' do
let(:user) { create(:admin) }
let!(:namespace) { user.namespace }
- let(:project_path) { 'test-project-path' + SecureRandom.hex }
+ let(:randomHex) { SecureRandom.hex }
+ let(:project_name) { 'Test Project Name' + randomHex }
+ let(:project_path) { 'test-project-name' + randomHex }
context 'prefilled the path' do
it 'user imports an exported project successfully' do
visit new_project_path
select2(namespace.id, from: '#project_namespace_id')
- fill_in :project_path, with: project_path, visible: true
+ fill_in :project_name, with: project_name, visible: true
click_import_project_tab
click_link 'GitLab export'
expect(page).to have_content('Import an exported GitLab project')
- expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=#{project_path}")
- expect(Gitlab::ImportExport).to receive(:import_upload_path).with(filename: /\A\h{32}\z/).and_call_original
+ expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&name=#{ERB::Util.url_encode(project_name)}&path=#{project_path}")
attach_file('file', file)
click_on 'Import project'
@@ -57,6 +59,7 @@ describe 'Import/Export - project import integration test', :js do
click_import_project_tab
click_link 'GitLab export'
+ fill_in :name, with: 'Test Project Name', visible: true
fill_in :path, with: 'test-project-path', visible: true
attach_file('file', file)
@@ -75,7 +78,7 @@ describe 'Import/Export - project import integration test', :js do
visit new_project_path
select2(user.namespace.id, from: '#project_namespace_id')
- fill_in :project_path, with: project.name, visible: true
+ fill_in :project_name, with: project.name, visible: true
click_import_project_tab
click_link 'GitLab export'
attach_file('file', file)
@@ -91,12 +94,6 @@ describe 'Import/Export - project import integration test', :js do
wiki.repository.exists? && !wiki.repository.empty?
end
- def project_hook_exists?(project)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository).exists?
- end
- end
-
def click_import_project_tab
find('#import-project-tab').click
end
diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb
deleted file mode 100644
index 9bb8a2063b5..00000000000
--- a/spec/features/projects/import_export/namespace_export_file_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'spec_helper'
-
-describe 'Import/Export - Namespace export file cleanup', :js do
- let(:export_path) { Dir.mktmpdir('namespace_export_file_spec') }
-
- before do
- allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
- stub_feature_flags(import_export_object_storage: false)
- end
-
- after do
- FileUtils.rm_rf(export_path, secure: true)
- end
-
- shared_examples_for 'handling project exports on namespace change' do
- let!(:old_export_path) { project.export_path }
-
- before do
- sign_in(create(:admin))
-
- setup_export_project
- end
-
- context 'moving the namespace' do
- it 'removes the export file' do
- expect(File).to exist(old_export_path)
-
- project.namespace.update!(path: build(:namespace).path)
-
- expect(File).not_to exist(old_export_path)
- end
- end
-
- context 'deleting the namespace' do
- it 'removes the export file' do
- expect(File).to exist(old_export_path)
-
- project.namespace.destroy
-
- expect(File).not_to exist(old_export_path)
- end
- end
- end
-
- describe 'legacy storage' do
- let(:project) { create(:project, :legacy_storage) }
-
- it_behaves_like 'handling project exports on namespace change'
- end
-
- describe 'hashed storage' do
- let(:project) { create(:project) }
-
- it_behaves_like 'handling project exports on namespace change'
- end
-
- def setup_export_project
- visit edit_project_path(project)
-
- expect(page).to have_content('Export project')
-
- find(:link, 'Export project').send_keys(:return)
-
- visit edit_project_path(project)
-
- expect(page).to have_content('Download export')
- end
-end
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
index 3b5df47e0b6..730e586b278 100644
--- a/spec/features/projects/import_export/test_project_export.tar.gz
+++ b/spec/features/projects/import_export/test_project_export.tar.gz
Binary files differ
diff --git a/spec/features/projects/issues/user_creates_issue_spec.rb b/spec/features/projects/issues/user_creates_issue_spec.rb
deleted file mode 100644
index 5e8662100c5..00000000000
--- a/spec/features/projects/issues/user_creates_issue_spec.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-require "spec_helper"
-
-describe "User creates issue" do
- let(:project) { create(:project_empty_repo, :public) }
- let(:user) { create(:user) }
-
- context "when signed in as guest" do
- before do
- project.add_guest(user)
- sign_in(user)
-
- visit(new_project_issue_path(project))
- end
-
- it "creates issue" do
- page.within(".issue-form") do
- expect(page).to have_no_content("Assign to")
- .and have_no_content("Labels")
- .and have_no_content("Milestone")
-
- expect(page.find('#issue_title')['placeholder']).to eq 'Title'
- expect(page.find('#issue_description')['placeholder']).to eq 'Write a comment or drag your files here…'
- end
-
- issue_title = "500 error on profile"
-
- fill_in("Title", with: issue_title)
- click_button("Submit issue")
-
- expect(page).to have_content(issue_title)
- .and have_content(user.name)
- .and have_content(project.name)
- end
- end
-
- context "when signed in as developer", :js do
- before do
- project.add_developer(user)
- sign_in(user)
-
- visit(new_project_issue_path(project))
- end
-
- context "when previewing" do
- it "previews content" do
- form = first(".gfm-form")
- textarea = first(".gfm-form textarea")
-
- page.within(form) do
- click_link("Preview")
-
- preview = find(".js-md-preview") # this element is findable only when the "Preview" link is clicked.
-
- expect(preview).to have_content("Nothing to preview.")
-
- click_link("Write")
- fill_in("Description", with: "Bug fixed :smile:")
- click_link("Preview")
-
- expect(preview).to have_css("gl-emoji")
- expect(textarea).not_to be_visible
- end
- end
- end
-
- context "with labels" do
- LABEL_TITLES = %w(bug feature enhancement).freeze
-
- before do
- LABEL_TITLES.each do |title|
- create(:label, project: project, title: title)
- end
- end
-
- it "creates issue" do
- issue_title = "500 error on profile"
-
- fill_in("Title", with: issue_title)
- click_button("Label")
- click_link(LABEL_TITLES.first)
- click_button("Submit issue")
-
- expect(page).to have_content(issue_title)
- .and have_content(user.name)
- .and have_content(project.name)
- .and have_content(LABEL_TITLES.first)
- end
- end
- end
-end
diff --git a/spec/features/projects/issues/user_edits_issue_spec.rb b/spec/features/projects/issues/user_edits_issue_spec.rb
deleted file mode 100644
index 1d9c3abc20f..00000000000
--- a/spec/features/projects/issues/user_edits_issue_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require "spec_helper"
-
-describe "User edits issue", :js do
- set(:project) { create(:project_empty_repo, :public) }
- set(:user) { create(:user) }
- set(:issue) { create(:issue, project: project, author: user) }
-
- before do
- project.add_developer(user)
- sign_in(user)
-
- visit(edit_project_issue_path(project, issue))
- end
-
- it "previews content" do
- form = first(".gfm-form")
-
- page.within(form) do
- fill_in("Description", with: "Bug fixed :smile:")
- click_link("Preview")
- end
-
- expect(form).to have_link("Write")
- end
-end
diff --git a/spec/features/projects/issues/user_sorts_issues_spec.rb b/spec/features/projects/issues/user_sorts_issues_spec.rb
deleted file mode 100644
index db5936a30cb..00000000000
--- a/spec/features/projects/issues/user_sorts_issues_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require "spec_helper"
-
-describe "User sorts issues" do
- set(:project) { create(:project_empty_repo, :public) }
- set(:issue1) { create(:issue, project: project) }
- set(:issue2) { create(:issue, project: project) }
- set(:issue3) { create(:issue, project: project) }
-
- before do
- create_list(:award_emoji, 2, :upvote, awardable: issue1)
- create_list(:award_emoji, 2, :downvote, awardable: issue2)
- create(:award_emoji, :downvote, awardable: issue1)
- create(:award_emoji, :upvote, awardable: issue2)
-
- visit(project_issues_path(project))
- end
-
- it "sorts by popularity" do
- find("button.dropdown-toggle").click
-
- page.within(".content ul.dropdown-menu.dropdown-menu-right li") do
- click_link("Popularity")
- end
-
- page.within(".issues-list") do
- page.within("li.issue:nth-child(1)") do
- expect(page).to have_content(issue1.title)
- end
-
- page.within("li.issue:nth-child(2)") do
- expect(page).to have_content(issue2.title)
- end
-
- page.within("li.issue:nth-child(3)") do
- expect(page).to have_content(issue3.title)
- end
- end
- end
-end
diff --git a/spec/features/projects/issues/user_views_issue_spec.rb b/spec/features/projects/issues/user_views_issue_spec.rb
deleted file mode 100644
index 117e5986f29..00000000000
--- a/spec/features/projects/issues/user_views_issue_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require "spec_helper"
-
-describe "User views issue" do
- set(:project) { create(:project_empty_repo, :public) }
- set(:user) { create(:user) }
- set(:issue) { create(:issue, project: project, description: "# Description header", author: user) }
-
- before do
- project.add_developer(user)
- sign_in(user)
-
- visit(project_issue_path(project, issue))
- end
-
- it { expect(page).to have_header_with_correct_id_and_link(1, "Description header", "description-header") }
-
- it 'shows the merge request and issue actions', :aggregate_failures do
- expect(page).to have_link('New issue')
- expect(page).to have_button('Create merge request')
- expect(page).to have_link('Close issue')
- end
-
- context 'when the project is archived' do
- let(:project) { create(:project, :public, :archived) }
-
- it 'hides the merge request and issue actions', :aggregate_failures do
- expect(page).not_to have_link('New issue')
- expect(page).not_to have_button('Create merge request')
- expect(page).not_to have_link('Close issue')
- end
- end
-
- describe 'user status' do
- subject { visit(project_issue_path(project, issue)) }
-
- describe 'showing status of the author of the issue' do
- it_behaves_like 'showing user status' do
- let(:user_with_status) { issue.author }
- end
- end
-
- describe 'showing status of a user who commented on an issue', :js do
- let!(:note) { create(:note, noteable: issue, project: project, author: user_with_status) }
-
- it_behaves_like 'showing user status' do
- let(:user_with_status) { create(:user) }
- end
- end
- end
-end
diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb
index e639f0cf82e..6ce37297a7e 100644
--- a/spec/features/projects/jobs/permissions_spec.rb
+++ b/spec/features/projects/jobs/permissions_spec.rb
@@ -67,7 +67,7 @@ describe 'Project Jobs Permissions' do
it_behaves_like 'recent job page details responds with status', 200 do
it 'renders job details', :js do
expect(page).to have_content "Job ##{job.id}"
- expect(page).to have_css '#build-trace'
+ expect(page).to have_css '.js-build-trace'
end
end
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index 50e957bf12b..908c616f2fc 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -16,9 +16,11 @@ describe 'User browses a job', :js do
visit(project_job_path(project, build))
end
- it 'erases the job log' do
+ it 'erases the job log', :js do
+ wait_for_requests
+
expect(page).to have_content("Job ##{build.id}")
- expect(page).to have_css('#build-trace')
+ expect(page).to have_css('.js-build-trace')
# scroll to the top of the page first
execute_script "window.scrollTo(0,0)"
@@ -29,18 +31,17 @@ describe 'User browses a job', :js do
expect(build.artifacts_file.exists?).to be_falsy
expect(build.artifacts_metadata.exists?).to be_falsy
- page.within('.erased') do
- expect(page).to have_content('Job has been erased')
- end
+ expect(page).to have_content('Job has been erased')
end
context 'with a failed job' do
let!(:build) { create(:ci_build, :failed, :trace_artifact, pipeline: pipeline) }
it 'displays the failure reason' do
+ wait_for_all_requests
within('.builds-container') do
build_link = first('.build-job > a')
- expect(build_link['data-title']).to eq('test - failed <br> (unknown failure)')
+ expect(build_link['data-original-title']).to eq('test - failed - (unknown failure)')
end
end
end
@@ -49,9 +50,10 @@ describe 'User browses a job', :js do
let!(:build) { create(:ci_build, :failed, :retried, :trace_artifact, pipeline: pipeline) }
it 'displays the failure reason and retried label' do
+ wait_for_all_requests
within('.builds-container') do
build_link = first('.build-job > a')
- expect(build_link['data-title']).to eq('test - failed <br> (unknown failure) (retried)')
+ expect(build_link['data-original-title']).to eq('test - failed - (unknown failure) (retried)')
end
end
end
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index 08786fe1630..ebc20d15d67 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -36,7 +36,7 @@ describe 'User browses jobs' do
it 'displays a tooltip with the failure reason' do
page.within('.ci-table') do
failed_job_link = page.find('.ci-failed')
- expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)')
+ expect(failed_job_link[:title]).to eq('Failed - (unknown failure)')
end
end
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 83293c0ca7d..99a7fbb63bd 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -5,7 +5,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
let(:user) { create(:user) }
let(:user_access_level) { :developer }
let(:project) { create(:project, :repository) }
- let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) }
let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
let(:job2) { create(:ci_build) }
@@ -20,7 +20,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
describe "GET /:project/jobs" do
- let!(:job) { create(:ci_build, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, pipeline: pipeline) }
context "Pending scope" do
before do
@@ -115,37 +115,44 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
context "Job from project" do
let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline) }
- before do
+ it 'shows status name', :js do
visit project_job_path(project, job)
- end
- it 'shows status name', :js do
+ wait_for_requests
+
expect(page).to have_css('.ci-status.ci-success', text: 'passed')
end
- it 'shows commit`s data' do
- expect(page.status_code).to eq(200)
+ it 'shows commit`s data', :js do
+ requests = inspect_requests() do
+ visit project_job_path(project, job)
+ end
+
+ wait_for_requests
+ expect(requests.first.status_code).to eq(200)
expect(page).to have_content pipeline.sha[0..7]
- expect(page).to have_content pipeline.git_commit_message
- expect(page).to have_content pipeline.git_author_name
+ expect(page).to have_content pipeline.commit.title
end
- it 'shows active job' do
+ it 'shows active job', :js do
+ visit project_job_path(project, job)
+
+ wait_for_requests
expect(page).to have_selector('.build-job.active')
end
end
- context 'sidebar' do
+ context 'sidebar', :js do
let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') }
before do
visit project_job_path(project, job)
+ wait_for_requests
end
it 'renders escaped tooltip name' do
- page.within('aside.right-sidebar') do
- expect(find('.active.build-job a')['data-title']).to eq('<img src="x"> - passed')
- end
+ page.find('.active.build-job a').hover
+ expect(page).to have_content('<img src=x onerror=alert(document.domain)> - passed')
end
end
@@ -191,6 +198,24 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
+ context 'when job is running', :js do
+ let(:job) { create(:ci_build, :running, pipeline: pipeline) }
+ let(:job_url) { project_job_path(project, job) }
+
+ before do
+ visit job_url
+ wait_for_requests
+ end
+
+ context 'job is cancelable' do
+ it 'shows cancel button' do
+ click_link 'Cancel'
+
+ expect(page.current_path).to eq(job_url)
+ end
+ end
+ end
+
context "Job from other project" do
before do
visit project_job_path(project, job2)
@@ -199,7 +224,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
it { expect(page.status_code).to eq(404) }
end
- context "Download artifacts" do
+ context "Download artifacts", :js do
before do
job.update(legacy_artifacts_file: artifacts_file)
visit project_job_path(project, job)
@@ -208,9 +233,22 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
it 'has button to download artifacts' do
expect(page).to have_content 'Download'
end
+
+ it 'downloads the zip file when user clicks the download button' do
+ requests = inspect_requests() do
+ click_link 'Download'
+ end
+
+ artifact_request = requests.find { |req| req.url.match(%r{artifacts/download}) }
+
+ expect(artifact_request.response_headers["Content-Disposition"]).to eq(%Q{attachment; filename="#{job.artifacts_file.filename}"})
+ expect(artifact_request.response_headers['Content-Transfer-Encoding']).to eq("binary")
+ expect(artifact_request.response_headers['Content-Type']).to eq("image/gif")
+ expect(artifact_request.body).to eq(job.artifacts_file.file.read.b)
+ end
end
- context 'Artifacts expire date' do
+ context 'Artifacts expire date', :js do
before do
job.update(legacy_artifacts_file: artifacts_file,
artifacts_expire_at: expire_at)
@@ -231,12 +269,12 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
context 'when user has ability to update job' do
it 'keeps artifacts when keep button is clicked' do
- expect(page).to have_content 'The artifacts will be removed'
+ expect(page).to have_content 'The artifacts will be removed in'
click_link 'Keep'
expect(page).to have_no_link 'Keep'
- expect(page).to have_no_content 'The artifacts will be removed'
+ expect(page).to have_no_content 'The artifacts will be removed in'
end
end
@@ -273,7 +311,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- describe 'Raw trace' do
+ describe 'Raw trace', :js do
before do
job.run!
@@ -281,7 +319,8 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
it do
- expect(page).to have_css('.js-raw-link')
+ wait_for_all_requests
+ expect(page).to have_css('.js-raw-link-controller')
end
end
@@ -314,6 +353,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
shared_examples 'expected variables behavior' do
it 'shows variable key and value after click', :js do
+ expect(page).to have_content('Token')
expect(page).to have_css('.js-reveal-variables')
expect(page).not_to have_css('.js-build-variable')
expect(page).not_to have_css('.js-build-value')
@@ -347,39 +387,176 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- context 'when job starts environment' do
- let(:environment) { create(:environment, project: project) }
- let(:pipeline) { create(:ci_pipeline, project: project) }
+ context 'when job starts environment', :js do
+ let(:environment) { create(:environment, name: 'production', project: project) }
+
+ before do
+ visit project_job_path(project, build)
+ wait_for_requests
+ end
- context 'job is successfull and has deployment' do
- let(:deployment) { create(:deployment) }
- let(:job) { create(:ci_build, :success, :trace_artifact, environment: environment.name, deployments: [deployment], pipeline: pipeline) }
+ context 'job is successful and has deployment' do
+ let(:build) { create(:ci_build, :success, :trace_live, environment: environment.name, pipeline: pipeline, deployment: deployment) }
+ let(:deployment) { create(:deployment, :success, environment: environment, project: environment.project) }
it 'shows a link for the job' do
- visit project_job_path(project, job)
-
expect(page).to have_link environment.name
end
+
+ it 'shows deployment message' do
+ expect(page).to have_content 'This job is the most recent deployment'
+ expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
+ end
end
context 'job is complete and not successful' do
- let(:job) { create(:ci_build, :failed, :trace_artifact, environment: environment.name, pipeline: pipeline) }
+ let(:build) { create(:ci_build, :failed, :trace_artifact, environment: environment.name, pipeline: pipeline) }
it 'shows a link for the job' do
- visit project_job_path(project, job)
-
expect(page).to have_link environment.name
+ expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
end
end
- context 'job creates a new deployment' do
- let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) }
- let(:job) { create(:ci_build, :success, :trace_artifact, environment: environment.name, pipeline: pipeline) }
+ context 'deployment still not finished' do
+ let(:build) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
it 'shows a link to latest deployment' do
- visit project_job_path(project, job)
+ expect(page).to have_link environment.name
+ expect(page).to have_content 'This job is creating a deployment'
+ expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
+ end
+ end
+ end
+
+ context 'when job stops environment', :js do
+ let(:environment) { create(:environment, name: 'production', project: project) }
+ let(:build) do
+ create(
+ :ci_build,
+ :success,
+ :trace_live,
+ environment: environment.name,
+ pipeline: pipeline,
+ options: { environment: { action: 'stop' } }
+ )
+ end
+
+ before do
+ visit project_job_path(project, build)
+ wait_for_requests
+ end
+
+ it 'does not show environment information banner' do
+ expect(page).not_to have_selector('.js-environment-container')
+ expect(page).not_to have_selector('.environment-information')
+ expect(page).not_to have_text(environment.name)
+ end
+ end
+
+ describe 'environment info in job view', :js do
+ before do
+ allow_any_instance_of(Ci::Build).to receive(:create_deployment)
+
+ visit project_job_path(project, job)
+ wait_for_requests
+ end
+
+ context 'job with outdated deployment' do
+ let(:job) { create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) }
+ let(:second_build) { create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) }
+ let(:environment) { create(:environment, name: 'staging', project: project) }
+ let!(:first_deployment) { create(:deployment, :success, environment: environment, deployable: job) }
+ let!(:second_deployment) { create(:deployment, :success, environment: environment, deployable: second_build) }
+
+ it 'shows deployment message' do
+ expected_text = 'This job is an out-of-date deployment ' \
+ "to staging. View the most recent deployment ##{second_deployment.iid}."
+
+ expect(page).to have_css('.environment-information', text: expected_text)
+ end
+
+ it 'renders a link to the most recent deployment' do
+ expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
+ expect(find('.js-job-deployment-link')['href']).to include(second_deployment.deployable.project.path, second_deployment.deployable_id.to_s)
+ end
+ end
+
+ context 'job failed to deploy' do
+ let(:job) { create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline) }
+ let!(:environment) { create(:environment, name: 'staging', project: project) }
+
+ it 'shows deployment message' do
+ expected_text = 'The deployment of this job to staging did not succeed.'
+
+ expect(page).to have_css('.environment-information', text: expected_text)
+ end
+ end
+
+ context 'job will deploy' do
+ let(:job) { create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline) }
+
+ context 'when environment exists' do
+ let!(:environment) { create(:environment, name: 'staging', project: project) }
+
+ it 'shows deployment message' do
+ expected_text = 'This job is creating a deployment to staging'
+
+ expect(page).to have_css('.environment-information', text: expected_text)
+ expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
+ end
+
+ context 'when it has deployment' do
+ let!(:deployment) { create(:deployment, :success, environment: environment) }
+
+ it 'shows that deployment will be overwritten' do
+ expected_text = 'This job is creating a deployment to staging'
- expect(page).to have_link('latest deployment')
+ expect(page).to have_css('.environment-information', text: expected_text)
+ expect(page).to have_css('.environment-information', text: 'latest deployment')
+ expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
+ end
+ end
+ end
+
+ context 'when environment does not exist' do
+ let!(:environment) { create(:environment, name: 'staging', project: project) }
+
+ it 'shows deployment message' do
+ expected_text = 'This job is creating a deployment to staging'
+
+ expect(page).to have_css(
+ '.environment-information', text: expected_text)
+ expect(page).not_to have_css(
+ '.environment-information', text: 'latest deployment')
+ expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
+ end
+ end
+ end
+
+ context 'job that failed to deploy and environment has not been created' do
+ let(:job) { create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline) }
+ let!(:environment) { create(:environment, name: 'staging', project: project) }
+
+ it 'shows deployment message' do
+ expected_text = 'The deployment of this job to staging did not succeed'
+
+ expect(page).to have_css(
+ '.environment-information', text: expected_text)
+ end
+ end
+
+ context 'job that will deploy and environment has not been created' do
+ let(:job) { create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline) }
+ let!(:environment) { create(:environment, name: 'staging', project: project) }
+
+ it 'shows deployment message' do
+ expected_text = 'This job is creating a deployment to staging'
+
+ expect(page).to have_css(
+ '.environment-information', text: expected_text)
+ expect(page).not_to have_css(
+ '.environment-information', text: 'latest deployment')
end
end
end
@@ -392,7 +569,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job)
end
- it 'shows manual action empty state' do
+ it 'shows manual action empty state', :js do
expect(page).to have_content(job.detailed_status(user).illustration[:title])
expect(page).to have_content('This job requires a manual action')
expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments')
@@ -409,6 +586,30 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
+ context 'Delayed job' do
+ let(:job) { create(:ci_build, :scheduled, pipeline: pipeline) }
+
+ before do
+ project.add_developer(user)
+ visit project_job_path(project, job)
+ end
+
+ it 'shows delayed job', :js do
+ expect(page).to have_content('This is a delayed job to run in')
+ expect(page).to have_content("This job will automatically run after it's timer finishes.")
+ expect(page).to have_link('Unschedule job')
+ end
+
+ it 'unschedules delayed job and shows manual action', :js do
+ click_link 'Unschedule job'
+
+ wait_for_requests
+ expect(page).to have_content('This job requires a manual action')
+ expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments')
+ expect(page).to have_link('Trigger this manual action')
+ end
+ end
+
context 'Non triggered job' do
let(:job) { create(:ci_build, :created, pipeline: pipeline) }
@@ -416,14 +617,14 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job)
end
- it 'shows empty state' do
+ it 'shows empty state', :js do
expect(page).to have_content(job.detailed_status(user).illustration[:title])
expect(page).to have_content('This job has not been triggered yet')
expect(page).to have_content('This job depends on upstream jobs that need to succeed in order for this job to be triggered')
end
end
- context 'Pending job' do
+ context 'Pending job', :js do
let(:job) { create(:ci_build, :pending, pipeline: pipeline) }
before do
@@ -437,7 +638,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- context 'Canceled job' do
+ context 'Canceled job', :js do
context 'with log' do
let(:job) { create(:ci_build, :canceled, :trace_artifact, pipeline: pipeline) }
@@ -446,11 +647,12 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
it 'renders job log' do
- expect(page).to have_selector('.js-build-output')
+ wait_for_all_requests
+ expect(page).to have_selector('.js-build-trace')
end
end
- context 'without log' do
+ context 'without log', :js do
let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
before do
@@ -459,13 +661,13 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
it 'renders empty state' do
expect(page).to have_content(job.detailed_status(user).illustration[:title])
- expect(page).not_to have_selector('.js-build-output')
+ expect(page).not_to have_selector('.js-build-trace')
expect(page).to have_content('This job has been canceled')
end
end
end
- context 'Skipped job' do
+ context 'Skipped job', :js do
let(:job) { create(:ci_build, :skipped, pipeline: pipeline) }
before do
@@ -474,12 +676,12 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
it 'renders empty state' do
expect(page).to have_content(job.detailed_status(user).illustration[:title])
- expect(page).not_to have_selector('.js-build-output')
+ expect(page).not_to have_selector('.js-build-trace')
expect(page).to have_content('This job has been skipped')
end
end
- context 'when job is failed but has no trace' do
+ context 'when job is failed but has no trace', :js do
let(:job) { create(:ci_build, :failed, pipeline: pipeline) }
it 'renders empty state' do
@@ -489,6 +691,112 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
expect(page).to have_content('This job does not have a trace.')
end
end
+
+ context 'with erased job', :js do
+ let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
+
+ it 'renders erased job warning' do
+ visit project_job_path(project, job)
+ wait_for_requests
+
+ page.within('.js-job-erased-block') do
+ expect(page).to have_content('Job has been erased')
+ end
+ end
+ end
+
+ context 'without erased job', :js do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'does not render erased job warning' do
+ visit project_job_path(project, job)
+ wait_for_requests
+
+ expect(page).not_to have_css('.js-job-erased-block')
+ end
+ end
+
+ context 'on mobile', :js do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'renders collapsed sidebar' do
+ page.current_window.resize_to(600, 800)
+
+ visit project_job_path(project, job)
+ wait_for_requests
+
+ expect(page).to have_css('.js-job-sidebar.right-sidebar-collapsed', visible: false)
+ expect(page).not_to have_css('.js-job-sidebar.right-sidebar-expanded', visible: false)
+ end
+ end
+
+ context 'on desktop', :js do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'renders expanded sidebar' do
+ visit project_job_path(project, job)
+ wait_for_requests
+
+ expect(page).to have_css('.js-job-sidebar.right-sidebar-expanded')
+ expect(page).not_to have_css('.js-job-sidebar.right-sidebar-collapsed')
+ end
+ end
+
+ context 'stuck', :js do
+ before do
+ visit project_job_path(project, job)
+ wait_for_requests
+ end
+
+ context 'without active runners available' do
+ let(:runner) { create(:ci_runner, :instance, active: false) }
+ let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner) }
+
+ it 'renders message about job being stuck because no runners are active' do
+ expect(page).to have_css('.js-stuck-no-active-runner')
+ expect(page).to have_content("This job is stuck, because you don't have any active runners that can run this job.")
+ end
+ end
+
+ context 'when available runners can not run specified tag' do
+ let(:runner) { create(:ci_runner, :instance, active: false) }
+ let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner, tag_list: %w(docker linux)) }
+
+ it 'renders message about job being stuck because of no runners with the specified tags' do
+ expect(page).to have_css('.js-stuck-with-tags')
+ expect(page).to have_content("This job is stuck, because you don't have any active runners online with any of these tags assigned to them:")
+ end
+ end
+
+ context 'when runners are offline and build has tags' do
+ let(:runner) { create(:ci_runner, :instance, active: true) }
+ let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner, tag_list: %w(docker linux)) }
+
+ it 'renders message about job being stuck because of no runners with the specified tags' do
+ expect(page).to have_css('.js-stuck-with-tags')
+ expect(page).to have_content("This job is stuck, because you don't have any active runners online with any of these tags assigned to them:")
+ end
+ end
+
+ context 'without any runners available' do
+ let(:job) { create(:ci_build, :pending, pipeline: pipeline) }
+
+ it 'renders message about job being stuck because not runners are available' do
+ expect(page).to have_css('.js-stuck-no-active-runner')
+ expect(page).to have_content("This job is stuck, because you don't have any active runners that can run this job.")
+ end
+ end
+
+ context 'without available runners online' do
+ let(:runner) { create(:ci_runner, :instance, active: true) }
+ let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner) }
+
+ it 'renders message about job being stuck because runners are offline' do
+ expect(page).to have_css('.js-stuck-no-runners')
+ expect(page).to have_content("This job is stuck, because the project doesn't have any runners online assigned to it.")
+ end
+ end
+ end
end
describe "POST /:project/jobs/:id/cancel", :js do
@@ -542,20 +850,26 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- describe "GET /:project/jobs/:id/download" do
+ describe "GET /:project/jobs/:id/download", :js do
before do
job.update(legacy_artifacts_file: artifacts_file)
visit project_job_path(project, job)
+
click_link 'Download'
end
context "Build from other project" do
before do
job2.update(legacy_artifacts_file: artifacts_file)
- visit download_project_job_artifacts_path(project, job2)
end
- it { expect(page.status_code).to eq(404) }
+ it do
+ requests = inspect_requests() do
+ visit download_project_job_artifacts_path(project, job2)
+ end
+
+ expect(requests.first.status_code).to eq(404)
+ end
end
end
diff --git a/spec/features/projects/labels/sort_labels_spec.rb b/spec/features/projects/labels/sort_labels_spec.rb
new file mode 100644
index 00000000000..01c3f251173
--- /dev/null
+++ b/spec/features/projects/labels/sort_labels_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Sort labels', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let!(:label1) { create(:label, title: 'Foo', description: 'Lorem ipsum', project: project) }
+ let!(:label2) { create(:label, title: 'Bar', description: 'Fusce consequat', project: project) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit project_labels_path(project)
+ end
+
+ it 'sorts by title by default' do
+ expect(page).to have_button('Name')
+
+ # assert default sorting
+ within '.other-labels' do
+ expect(page.all('.label-list-item').first.text).to include('Bar')
+ expect(page.all('.label-list-item').last.text).to include('Foo')
+ end
+ end
+
+ it 'sorts by date' do
+ click_button 'Name'
+
+ sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text)
+
+ expect(sort_options[0]).to eq('Name')
+ expect(sort_options[1]).to eq('Name, descending')
+ expect(sort_options[2]).to eq('Last created')
+ expect(sort_options[3]).to eq('Oldest created')
+ expect(sort_options[4]).to eq('Last updated')
+ expect(sort_options[5]).to eq('Oldest updated')
+
+ click_link 'Name, descending'
+
+ # assert default sorting
+ within '.other-labels' do
+ expect(page.all('.label-list-item').first.text).to include('Foo')
+ expect(page.all('.label-list-item').last.text).to include('Bar')
+ end
+ end
+end
diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb
new file mode 100644
index 00000000000..fceead0b45e
--- /dev/null
+++ b/spec/features/projects/members/invite_group_spec.rb
@@ -0,0 +1,195 @@
+require 'spec_helper'
+
+describe 'Project > Members > Invite group', :js do
+ include Select2Helper
+ include ActionView::Helpers::DateHelper
+
+ let(:maintainer) { create(:user) }
+
+ describe 'Share with group lock' do
+ shared_examples 'the project can be shared with groups' do
+ it 'the "Invite group" tab exists' do
+ visit project_settings_members_path(project)
+ expect(page).to have_selector('#invite-group-tab')
+ end
+ end
+
+ shared_examples 'the project cannot be shared with groups' do
+ it 'the "Invite group" tab does not exist' do
+ visit project_settings_members_path(project)
+ expect(page).not_to have_selector('#invite-group-tab')
+ end
+ end
+
+ context 'for a project in a root group' do
+ let!(:group_to_share_with) { create(:group) }
+ let(:project) { create(:project, namespace: create(:group)) }
+
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
+ end
+
+ context 'when the group has "Share with group lock" disabled' do
+ it_behaves_like 'the project can be shared with groups'
+
+ it 'the project can be shared with another group' do
+ visit project_settings_members_path(project)
+
+ click_on 'invite-group-tab'
+
+ select2 group_to_share_with.id, from: '#link_group_id'
+ page.find('body').click
+ find('.btn-success').click
+
+ page.within('.project-members-groups') do
+ expect(page).to have_content(group_to_share_with.name)
+ end
+ end
+ end
+
+ context 'when the group has "Share with group lock" enabled' do
+ before do
+ project.namespace.update_column(:share_with_group_lock, true)
+ end
+
+ it_behaves_like 'the project cannot be shared with groups'
+ end
+ end
+
+ context 'for a project in a subgroup', :nested_groups do
+ let!(:group_to_share_with) { create(:group) }
+ let(:root_group) { create(:group) }
+ let(:subgroup) { create(:group, parent: root_group) }
+ let(:project) { create(:project, namespace: subgroup) }
+
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
+ end
+
+ context 'when the root_group has "Share with group lock" disabled' do
+ context 'when the subgroup has "Share with group lock" disabled' do
+ it_behaves_like 'the project can be shared with groups'
+ end
+
+ context 'when the subgroup has "Share with group lock" enabled' do
+ before do
+ subgroup.update_column(:share_with_group_lock, true)
+ end
+
+ it_behaves_like 'the project cannot be shared with groups'
+ end
+ end
+
+ context 'when the root_group has "Share with group lock" enabled' do
+ before do
+ root_group.update_column(:share_with_group_lock, true)
+ end
+
+ context 'when the subgroup has "Share with group lock" disabled (parent overridden)' do
+ it_behaves_like 'the project can be shared with groups'
+ end
+
+ context 'when the subgroup has "Share with group lock" enabled' do
+ before do
+ subgroup.update_column(:share_with_group_lock, true)
+ end
+
+ it_behaves_like 'the project cannot be shared with groups'
+ end
+ end
+ end
+ end
+
+ describe 'setting an expiration date for a group link' do
+ let(:project) { create(:project) }
+ let!(:group) { create(:group) }
+
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
+
+ visit project_settings_members_path(project)
+
+ click_on 'invite-group-tab'
+
+ select2 group.id, from: '#link_group_id'
+
+ fill_in 'expires_at_groups', with: (Time.now + 4.5.days).strftime('%Y-%m-%d')
+ click_on 'invite-group-tab'
+ find('.btn-success').click
+ end
+
+ it 'the group link shows the expiration time with a warning class' do
+ page.within('.project-members-groups') do
+ # Using distance_of_time_in_words_to_now because it is not the same as
+ # subtraction, and this way avoids time zone issues as well
+ expires_in_text = distance_of_time_in_words_to_now(project.project_group_links.first.expires_at)
+ expect(page).to have_content(expires_in_text)
+ expect(page).to have_selector('.text-warning')
+ end
+ end
+ end
+
+ describe 'the groups dropdown' do
+ context 'with multiple groups to choose from' do
+ let(:project) { create(:project) }
+
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
+
+ create(:group).add_owner(maintainer)
+ create(:group).add_owner(maintainer)
+
+ visit project_settings_members_path(project)
+
+ click_link 'Invite group'
+
+ find('.ajax-groups-select.select2-container')
+
+ execute_script 'GROUP_SELECT_PER_PAGE = 1;'
+ open_select2 '#link_group_id'
+ end
+
+ it 'should infinitely scroll' do
+ expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 1)
+
+ scroll_select2_to_bottom('.select2-drop .select2-results:visible')
+
+ expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 2)
+ end
+ end
+
+ context 'for a project in a nested group' do
+ let(:group) { create(:group) }
+ let!(:nested_group) { create(:group, parent: group) }
+ let!(:group_to_share_with) { create(:group) }
+ let!(:project) { create(:project, namespace: nested_group) }
+
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
+ group.add_maintainer(maintainer)
+ group_to_share_with.add_maintainer(maintainer)
+ end
+
+ it 'the groups dropdown does not show ancestors', :nested_groups do
+ visit project_settings_members_path(project)
+
+ click_on 'invite-group-tab'
+ click_link 'Search for a group'
+
+ page.within '.select2-drop' do
+ expect(page).to have_content(group_to_share_with.name)
+ expect(page).not_to have_content(group.name)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/members/share_with_group_spec.rb b/spec/features/projects/members/share_with_group_spec.rb
deleted file mode 100644
index c6d85e5d22f..00000000000
--- a/spec/features/projects/members/share_with_group_spec.rb
+++ /dev/null
@@ -1,196 +0,0 @@
-require 'spec_helper'
-
-describe 'Project > Members > Share with Group', :js do
- include Select2Helper
- include ActionView::Helpers::DateHelper
-
- let(:maintainer) { create(:user) }
-
- describe 'Share with group lock' do
- shared_examples 'the project can be shared with groups' do
- it 'the "Share with group" tab exists' do
- visit project_settings_members_path(project)
- expect(page).to have_selector('#share-with-group-tab')
- end
- end
-
- shared_examples 'the project cannot be shared with groups' do
- it 'the "Share with group" tab does not exist' do
- visit project_settings_members_path(project)
- expect(page).to have_selector('#add-member-tab')
- expect(page).not_to have_selector('#share-with-group-tab')
- end
- end
-
- context 'for a project in a root group' do
- let!(:group_to_share_with) { create(:group) }
- let(:project) { create(:project, namespace: create(:group)) }
-
- before do
- project.add_maintainer(maintainer)
- sign_in(maintainer)
- end
-
- context 'when the group has "Share with group lock" disabled' do
- it_behaves_like 'the project can be shared with groups'
-
- it 'the project can be shared with another group' do
- visit project_settings_members_path(project)
-
- click_on 'share-with-group-tab'
-
- select2 group_to_share_with.id, from: '#link_group_id'
- page.find('body').click
- find('.btn-create').click
-
- page.within('.project-members-groups') do
- expect(page).to have_content(group_to_share_with.name)
- end
- end
- end
-
- context 'when the group has "Share with group lock" enabled' do
- before do
- project.namespace.update_column(:share_with_group_lock, true)
- end
-
- it_behaves_like 'the project cannot be shared with groups'
- end
- end
-
- context 'for a project in a subgroup', :nested_groups do
- let!(:group_to_share_with) { create(:group) }
- let(:root_group) { create(:group) }
- let(:subgroup) { create(:group, parent: root_group) }
- let(:project) { create(:project, namespace: subgroup) }
-
- before do
- project.add_maintainer(maintainer)
- sign_in(maintainer)
- end
-
- context 'when the root_group has "Share with group lock" disabled' do
- context 'when the subgroup has "Share with group lock" disabled' do
- it_behaves_like 'the project can be shared with groups'
- end
-
- context 'when the subgroup has "Share with group lock" enabled' do
- before do
- subgroup.update_column(:share_with_group_lock, true)
- end
-
- it_behaves_like 'the project cannot be shared with groups'
- end
- end
-
- context 'when the root_group has "Share with group lock" enabled' do
- before do
- root_group.update_column(:share_with_group_lock, true)
- end
-
- context 'when the subgroup has "Share with group lock" disabled (parent overridden)' do
- it_behaves_like 'the project can be shared with groups'
- end
-
- context 'when the subgroup has "Share with group lock" enabled' do
- before do
- subgroup.update_column(:share_with_group_lock, true)
- end
-
- it_behaves_like 'the project cannot be shared with groups'
- end
- end
- end
- end
-
- describe 'setting an expiration date for a group link' do
- let(:project) { create(:project) }
- let!(:group) { create(:group) }
-
- around do |example|
- Timecop.freeze { example.run }
- end
-
- before do
- project.add_maintainer(maintainer)
- sign_in(maintainer)
-
- visit project_settings_members_path(project)
-
- click_on 'share-with-group-tab'
-
- select2 group.id, from: '#link_group_id'
-
- fill_in 'expires_at_groups', with: (Time.now + 4.5.days).strftime('%Y-%m-%d')
- click_on 'share-with-group-tab'
- find('.btn-create').click
- end
-
- it 'the group link shows the expiration time with a warning class' do
- page.within('.project-members-groups') do
- # Using distance_of_time_in_words_to_now because it is not the same as
- # subtraction, and this way avoids time zone issues as well
- expires_in_text = distance_of_time_in_words_to_now(project.project_group_links.first.expires_at)
- expect(page).to have_content(expires_in_text)
- expect(page).to have_selector('.text-warning')
- end
- end
- end
-
- describe 'the groups dropdown' do
- context 'with multiple groups to choose from' do
- let(:project) { create(:project) }
-
- before do
- project.add_maintainer(maintainer)
- sign_in(maintainer)
-
- create(:group).add_owner(maintainer)
- create(:group).add_owner(maintainer)
-
- visit project_settings_members_path(project)
-
- click_link 'Share with group'
-
- find('.ajax-groups-select.select2-container')
-
- execute_script 'GROUP_SELECT_PER_PAGE = 1;'
- open_select2 '#link_group_id'
- end
-
- it 'should infinitely scroll' do
- expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 1)
-
- scroll_select2_to_bottom('.select2-drop .select2-results:visible')
-
- expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 2)
- end
- end
-
- context 'for a project in a nested group' do
- let(:group) { create(:group) }
- let!(:nested_group) { create(:group, parent: group) }
- let!(:group_to_share_with) { create(:group) }
- let!(:project) { create(:project, namespace: nested_group) }
-
- before do
- project.add_maintainer(maintainer)
- sign_in(maintainer)
- group.add_maintainer(maintainer)
- group_to_share_with.add_maintainer(maintainer)
- end
-
- it 'the groups dropdown does not show ancestors', :nested_groups do
- visit project_settings_members_path(project)
-
- click_on 'share-with-group-tab'
- click_link 'Search for a group'
-
- page.within '.select2-drop' do
- expect(page).to have_content(group_to_share_with.name)
- expect(page).not_to have_content(group.name)
- end
- end
- end
- end
-end
diff --git a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb
deleted file mode 100644
index 441b080bee5..00000000000
--- a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-require 'spec_helper'
-
-describe 'User comments on a diff', :js do
- include MergeRequestDiffHelpers
- include RepoHelpers
-
- let(:project) { create(:project, :repository) }
- let(:merge_request) do
- create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
- end
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
-
- visit(diffs_project_merge_request_path(project, merge_request))
- end
-
- context 'when viewing comments' do
- context 'when toggling inline comments' do
- context 'in a single file' do
- it 'hides a comment' do
- click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
-
- page.within('.js-discussion-note-form') do
- fill_in('note_note', with: 'Line is wrong')
- click_button('Comment')
- end
-
- page.within('.files > div:nth-child(3)') do
- expect(page).to have_content('Line is wrong')
-
- find('.js-btn-vue-toggle-comments').click
-
- expect(page).not_to have_content('Line is wrong')
- end
- end
- end
-
- context 'in multiple files' do
- it 'toggles comments' do
- click_diff_line(find("[id='#{sample_compare.changes[0][:line_code]}']"))
-
- page.within('.js-discussion-note-form') do
- fill_in('note_note', with: 'Line is correct')
- click_button('Comment')
- end
-
- wait_for_requests
-
- page.within('.files > div:nth-child(2) .note-body > .note-text') do
- expect(page).to have_content('Line is correct')
- end
-
- click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
-
- page.within('.js-discussion-note-form') do
- fill_in('note_note', with: 'Line is wrong')
- click_button('Comment')
- end
-
- wait_for_requests
-
- # Hide the comment.
- page.within('.files > div:nth-child(3)') do
- find('.js-btn-vue-toggle-comments').click
-
- expect(page).not_to have_content('Line is wrong')
- end
-
- # At this moment a user should see only one comment.
- # The other one should be hidden.
- page.within('.files > div:nth-child(2) .note-body > .note-text') do
- expect(page).to have_content('Line is correct')
- end
-
- # Show the comment.
- page.within('.files > div:nth-child(3)') do
- find('.js-btn-vue-toggle-comments').click
- end
-
- # Now both the comments should be shown.
- page.within('.files > div:nth-child(3) .note-body > .note-text') do
- expect(page).to have_content('Line is wrong')
- end
-
- page.within('.files > div:nth-child(2) .note-body > .note-text') do
- expect(page).to have_content('Line is correct')
- end
-
- # Check the same comments in the side-by-side view.
- execute_script("window.scrollTo(0,0);")
- click_button 'Side-by-side'
-
- wait_for_requests
-
- page.within('.files > div:nth-child(3) .parallel .note-body > .note-text') do
- expect(page).to have_content('Line is wrong')
- end
-
- page.within('.files > div:nth-child(2) .parallel .note-body > .note-text') do
- expect(page).to have_content('Line is correct')
- end
- end
- end
- end
- end
-
- context 'when adding comments' do
- include_examples 'comment on merge request file'
- end
-
- context 'when editing comments' do
- it 'edits a comment' do
- click_diff_line(find("[id='#{sample_commit.line_code}']"))
-
- page.within('.js-discussion-note-form') do
- fill_in(:note_note, with: 'Line is wrong')
- click_button('Comment')
- end
-
- page.within('.diff-file:nth-of-type(5) .discussion .note') do
- find('.js-note-edit').click
-
- page.within('.current-note-edit-form') do
- fill_in('note_note', with: 'Typo, please fix')
- click_button('Save comment')
- end
-
- expect(page).not_to have_button('Save comment', disabled: true)
- end
-
- page.within('.diff-file:nth-of-type(5) .discussion .note') do
- expect(page).to have_content('Typo, please fix').and have_no_content('Line is wrong')
- end
- end
- end
-
- context 'when deleting comments' do
- it 'deletes a comment' do
- click_diff_line(find("[id='#{sample_commit.line_code}']"))
-
- page.within('.js-discussion-note-form') do
- fill_in(:note_note, with: 'Line is wrong')
- click_button('Comment')
- end
-
- page.within('.notes-tab .badge') do
- expect(page).to have_content('1')
- end
-
- page.within('.diff-file:nth-of-type(5) .discussion .note') do
- find('.more-actions').click
- find('.more-actions .dropdown-menu li', match: :first)
-
- accept_confirm { find('.js-note-delete').click }
- end
-
- page.within('.merge-request-tabs') do
- find('.notes-tab').click
- end
-
- wait_for_requests
-
- expect(page).not_to have_css('.notes .discussion')
-
- page.within('.notes-tab .badge') do
- expect(page).to have_content('0')
- end
- end
- end
-end
diff --git a/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb b/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
deleted file mode 100644
index e401933aed2..00000000000
--- a/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-require 'spec_helper'
-
-describe 'User sorts merge requests' do
- let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- let!(:merge_request2) do
- create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
- end
- let(:project) { create(:project, :public, :repository) }
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
-
- visit(project_merge_requests_path(project))
- end
-
- it 'keeps the sort option' do
- find('button.dropdown-toggle').click
-
- page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
- click_link('Last updated')
- end
-
- visit(merge_requests_dashboard_path(assignee_id: user.id))
-
- expect(find('.issues-filters')).to have_content('Last updated')
-
- visit(project_merge_requests_path(project))
-
- expect(find('.issues-filters')).to have_content('Last updated')
- end
-
- context 'when merge requests have awards' do
- before do
- create_list(:award_emoji, 2, awardable: merge_request)
- create(:award_emoji, :downvote, awardable: merge_request)
-
- create(:award_emoji, awardable: merge_request2)
- create_list(:award_emoji, 2, :downvote, awardable: merge_request2)
- end
-
- it 'sorts by popularity' do
- find('button.dropdown-toggle').click
-
- page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
- click_link('Popularity')
- end
-
- page.within('.mr-list') do
- page.within('li.merge-request:nth-child(1)') do
- expect(page).to have_content(merge_request.title)
- expect(page).to have_content('2 1')
- end
-
- page.within('li.merge-request:nth-child(2)') do
- expect(page).to have_content(merge_request2.title)
- expect(page).to have_content('1 2')
- end
- end
- end
- end
-end
diff --git a/spec/features/projects/merge_requests/user_views_diffs_spec.rb b/spec/features/projects/merge_requests/user_views_diffs_spec.rb
deleted file mode 100644
index b1bfe9e5de3..00000000000
--- a/spec/features/projects/merge_requests/user_views_diffs_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require 'spec_helper'
-
-describe 'User views diffs', :js do
- let(:merge_request) do
- create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
- end
- let(:project) { create(:project, :public, :repository) }
-
- before do
- visit(diffs_project_merge_request_path(project, merge_request))
-
- wait_for_requests
- end
-
- shared_examples 'unfold diffs' do
- it 'unfolds diffs' do
- first('.js-unfold').click
-
- expect(find('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"] .text-file')).to have_content('.bundle')
- end
- end
-
- it 'shows diffs' do
- expect(page).to have_css('.tab-content #diffs.active')
- expect(page).to have_css('#parallel-diff-btn', count: 1)
- expect(page).to have_css('#inline-diff-btn', count: 1)
- end
-
- it 'hides loading spinner after load' do
- expect(page).not_to have_selector('.mr-loading-status .loading', visible: true)
- end
-
- context 'when in the inline view' do
- include_examples 'unfold diffs'
- end
-
- context 'when in the side-by-side view' do
- before do
- click_button 'Side-by-side'
-
- wait_for_requests
- end
-
- it 'shows diffs in parallel' do
- expect(page).to have_css('.parallel')
- end
-
- it 'toggles container class' do
- expect(page).not_to have_css('.content-wrapper > .container-fluid.container-limited')
-
- click_link 'Commits'
-
- expect(page).to have_css('.content-wrapper > .container-fluid.container-limited')
- end
-
- include_examples 'unfold diffs'
- end
-end
diff --git a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
deleted file mode 100644
index 6ac495aa03d..00000000000
--- a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-require 'spec_helper'
-
-describe 'User views an open merge request' do
- let(:merge_request) do
- create(:merge_request, source_project: project, target_project: project, description: '# Description header')
- end
-
- context 'when a merge request does not have repository' do
- let(:project) { create(:project, :public, :repository) }
-
- before do
- visit(merge_request_path(merge_request))
- end
-
- it 'renders both the title and the description' do
- node = find('.wiki h1 a#user-content-description-header')
- expect(node[:href]).to end_with('#description-header')
-
- # Work around a weird Capybara behavior where calling `parent` on a node
- # returns the whole document, not the node's actual parent element
- expect(find(:xpath, "#{node.path}/..").text).to eq(merge_request.description[2..-1])
-
- expect(page).to have_content(merge_request.title).and have_content(merge_request.description)
- end
- end
-
- context 'when a merge request has repository', :js do
- let(:project) { create(:project, :public, :repository) }
-
- context 'when rendering description preview' do
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
-
- visit(edit_project_merge_request_path(project, merge_request))
- end
-
- it 'renders empty description preview' do
- find('.gfm-form').fill_in(:merge_request_description, with: '')
-
- page.within('.gfm-form') do
- click_link('Preview')
-
- expect(find('.js-md-preview')).to have_content('Nothing to preview.')
- end
- end
-
- it 'renders description preview' do
- find('.gfm-form').fill_in(:merge_request_description, with: ':+1: Nice')
-
- page.within('.gfm-form') do
- click_link('Preview')
-
- expect(find('.js-md-preview')).to have_css('gl-emoji')
- end
-
- expect(find('.gfm-form')).to have_css('.js-md-preview').and have_link('Write')
- expect(find('#merge_request_description', visible: false)).not_to be_visible
- end
- end
-
- context 'when the branch is rebased on the target' do
- let(:merge_request) { create(:merge_request, :rebased, source_project: project, target_project: project) }
-
- before do
- visit(merge_request_path(merge_request))
- end
-
- it 'does not show diverged commits count' do
- page.within('.mr-source-target') do
- expect(page).not_to have_content(/([0-9]+ commit[s]? behind)/)
- end
- end
- end
-
- context 'when the branch is diverged on the target' do
- let(:merge_request) { create(:merge_request, :diverged, source_project: project, target_project: project) }
-
- before do
- visit(merge_request_path(merge_request))
- end
-
- it 'shows diverged commits count' do
- page.within('.mr-source-target') do
- expect(page).to have_content(/([0-9]+ commits behind)/)
- end
- end
- end
- end
-end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index bbe08ff83ff..75c72a68069 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -12,8 +12,9 @@ describe 'New project' do
it 'shows "New project" page', :js do
visit new_project_path
- expect(page).to have_content('Project path')
expect(page).to have_content('Project name')
+ expect(page).to have_content('Project URL')
+ expect(page).to have_content('Project slug')
find('#import-project-tab').click
@@ -65,12 +66,34 @@ describe 'New project' do
end
context 'Readme selector' do
- it 'shows the initialize with Readme checkbox' do
+ it 'shows the initialize with Readme checkbox on "Blank project" tab' do
visit new_project_path
expect(page).to have_css('input#project_initialize_with_readme')
expect(page).to have_content('Initialize repository with a README')
end
+
+ it 'does not show the initialize with Readme checkbox on "Create from template" tab' do
+ visit new_project_path
+ find('#create-from-template-pane').click
+ first('.choose-template').click
+
+ page.within '.project-fields-form' do
+ expect(page).not_to have_css('input#project_initialize_with_readme')
+ expect(page).not_to have_content('Initialize repository with a README')
+ end
+ end
+
+ it 'does not show the initialize with Readme checkbox on "Import project" tab' do
+ visit new_project_path
+ find('#import-project-tab').click
+ first('.js-import-git-toggle-button').click
+
+ page.within '.toggle-import-form' do
+ expect(page).not_to have_css('input#project_initialize_with_readme')
+ expect(page).not_to have_content('Initialize repository with a README')
+ end
+ end
end
context 'Namespace selector' do
@@ -187,7 +210,7 @@ describe 'New project' do
collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
fill_in 'project_import_url', with: collision_project.http_url_to_repo
- fill_in 'project_path', with: collision_project.path
+ fill_in 'project_name', with: collision_project.name
click_on 'Create project'
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index a84492ea5f1..049bbca958f 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -31,6 +31,11 @@ describe 'Pipeline', :js do
pipeline: pipeline, stage: 'deploy', name: 'manual-build')
end
+ let!(:build_scheduled) do
+ create(:ci_build, :scheduled,
+ pipeline: pipeline, stage: 'deploy', name: 'delayed-job')
+ end
+
let!(:build_external) do
create(:generic_commit_status, status: 'success',
pipeline: pipeline,
@@ -63,6 +68,10 @@ describe 'Pipeline', :js do
expect(page).to have_css('#js-tab-pipeline.active')
end
+ it 'shows link to the pipeline ref' do
+ expect(page).to have_link(pipeline.ref)
+ end
+
it_behaves_like 'showing user status' do
let(:user_with_status) { pipeline.user }
@@ -79,10 +88,12 @@ describe 'Pipeline', :js do
end
end
- it 'should be possible to cancel the running build' do
+ it 'cancels the running build and shows retry button' do
find('#ci-badge-deploy .ci-action-icon-container').click
- expect(page).not_to have_content('Cancel running')
+ page.within('#ci-badge-deploy') do
+ expect(page).to have_css('.js-icon-retry')
+ end
end
end
@@ -105,6 +116,27 @@ describe 'Pipeline', :js do
end
end
+ context 'when pipeline has a delayed job' do
+ it 'shows the scheduled icon and an unschedule action for the delayed job' do
+ page.within('#ci-badge-delayed-job') do
+ expect(page).to have_selector('.js-ci-status-icon-scheduled')
+ expect(page).to have_content('delayed-job')
+ end
+
+ page.within('#ci-badge-delayed-job .ci-action-icon-container.js-icon-time-out') do
+ expect(page).to have_selector('svg')
+ end
+ end
+
+ it 'unschedules the delayed job and shows play button as a manual job' do
+ find('#ci-badge-delayed-job .ci-action-icon-container').click
+
+ page.within('#ci-badge-delayed-job') do
+ expect(page).to have_css('.js-icon-play')
+ end
+ end
+ end
+
context 'when pipeline has failed builds' do
it 'shows the failed icon and a retry action for the failed build' do
page.within('#ci-badge-test') do
@@ -126,7 +158,7 @@ describe 'Pipeline', :js do
it 'should include the failure reason' do
page.within('#ci-badge-test') do
build_link = page.find('.js-pipeline-graph-job-link')
- expect(build_link['data-original-title']).to eq('test - failed <br> (unknown failure)')
+ expect(build_link['data-original-title']).to eq('test - failed - (unknown failure)')
end
end
end
@@ -208,6 +240,20 @@ describe 'Pipeline', :js do
it { expect(page).not_to have_content('Cancel running') }
end
end
+
+ context 'when pipeline ref does not exist in repository anymore' do
+ let(:pipeline) do
+ create(:ci_empty_pipeline, project: project,
+ ref: 'non-existent',
+ sha: project.commit.id,
+ user: user)
+ end
+
+ it 'does not render link to the pipeline ref' do
+ expect(page).not_to have_link(pipeline.ref)
+ expect(page).to have_content(pipeline.ref)
+ end
+ end
end
context 'when user does not have access to read jobs' do
@@ -315,11 +361,23 @@ describe 'Pipeline', :js do
it { expect(build_manual.reload).to be_pending }
end
+ context 'when user unschedules a delayed job' do
+ before do
+ within '.pipeline-holder' do
+ click_link('Unschedule')
+ end
+ end
+
+ it 'unschedules the delayed job and shows play button as a manual job' do
+ expect(page).to have_content('Trigger this manual action')
+ end
+ end
+
context 'failed jobs' do
it 'displays a tooltip with the failure reason' do
page.within('.ci-table') do
failed_job_link = page.find('.ci-failed')
- expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)')
+ expect(failed_job_link[:title]).to eq('Failed - (unknown failure)')
end
end
end
@@ -330,54 +388,83 @@ describe 'Pipeline', :js do
let(:pipeline_failures_page) { failures_project_pipeline_path(project, pipeline) }
let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) }
+ subject { visit pipeline_failures_page }
+
context 'with failed build' do
before do
failed_build.trace.set('4 examples, 1 failure')
-
- visit pipeline_failures_page
end
it 'shows jobs tab pane as active' do
+ subject
+
expect(page).to have_content('Failed Jobs')
expect(page).to have_css('#js-tab-failures.active')
end
it 'lists failed builds' do
+ subject
+
expect(page).to have_content(failed_build.name)
expect(page).to have_content(failed_build.stage)
end
it 'shows build failure logs' do
+ subject
+
expect(page).to have_content('4 examples, 1 failure')
end
it 'shows the failure reason' do
+ subject
+
expect(page).to have_content('There is an unknown failure, please try again')
end
- it 'shows retry button for failed build' do
- page.within(find('.build-failures', match: :first)) do
- expect(page).to have_link('Retry')
+ context 'when user does not have permission to retry build' do
+ it 'shows retry button for failed build' do
+ subject
+
+ page.within(find('.build-failures', match: :first)) do
+ expect(page).not_to have_link('Retry')
+ end
end
end
- end
- context 'when missing build logs' do
- before do
- visit pipeline_failures_page
+ context 'when user does have permission to retry build' do
+ before do
+ create(:protected_branch, :developers_can_merge,
+ name: pipeline.ref, project: project)
+ end
+
+ it 'shows retry button for failed build' do
+ subject
+
+ page.within(find('.build-failures', match: :first)) do
+ expect(page).to have_link('Retry')
+ end
+ end
end
+ end
+ context 'when missing build logs' do
it 'shows jobs tab pane as active' do
+ subject
+
expect(page).to have_content('Failed Jobs')
expect(page).to have_css('#js-tab-failures.active')
end
it 'lists failed builds' do
+ subject
+
expect(page).to have_content(failed_build.name)
expect(page).to have_content(failed_build.stage)
end
it 'does not show trace' do
+ subject
+
expect(page).to have_content('No job trace')
end
end
@@ -390,11 +477,9 @@ describe 'Pipeline', :js do
end
context 'when accessing failed jobs page' do
- before do
- visit pipeline_failures_page
- end
-
it 'fails to access the page' do
+ subject
+
expect(page).to have_title('Access Denied')
end
end
@@ -403,11 +488,11 @@ describe 'Pipeline', :js do
context 'without failures' do
before do
failed_build.update!(status: :success)
-
- visit pipeline_failures_page
end
it 'displays the pipeline graph' do
+ subject
+
expect(current_path).to eq(pipeline_path(pipeline))
expect(page).not_to have_content('Failed Jobs')
expect(page).to have_selector('.pipeline-visualization')
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 4a83bcc3efb..17772a35779 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -9,6 +9,7 @@ describe 'Pipelines', :js do
before do
sign_in(user)
project.add_developer(user)
+ project.update!(auto_devops_attributes: { enabled: false })
end
describe 'GET /:project/pipelines' do
@@ -231,6 +232,60 @@ describe 'Pipelines', :js do
end
end
+ context 'when there is a delayed job' do
+ let!(:delayed_job) do
+ create(:ci_build, :scheduled,
+ pipeline: pipeline,
+ name: 'delayed job',
+ stage: 'test',
+ commands: 'test')
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'has a dropdown for actionable jobs' do
+ expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
+ end
+
+ it "has link to the delayed job's action" do
+ find('.js-pipeline-dropdown-manual-actions').click
+
+ time_diff = [0, delayed_job.scheduled_at - Time.now].max
+ expect(page).to have_button('delayed job')
+ expect(page).to have_content(Time.at(time_diff).utc.strftime("%H:%M:%S"))
+ end
+
+ context 'when delayed job is expired already' do
+ let!(:delayed_job) do
+ create(:ci_build, :expired_scheduled,
+ pipeline: pipeline,
+ name: 'delayed job',
+ stage: 'test',
+ commands: 'test')
+ end
+
+ it "shows 00:00:00 as the remaining time" do
+ find('.js-pipeline-dropdown-manual-actions').click
+
+ expect(page).to have_content("00:00:00")
+ end
+ end
+
+ context 'when user played a delayed job immediately' do
+ before do
+ find('.js-pipeline-dropdown-manual-actions').click
+ page.accept_confirm { click_button('delayed job') }
+ wait_for_requests
+ end
+
+ it 'enqueues the delayed job', :js do
+ expect(delayed_job.reload).to be_pending
+ end
+ end
+ end
+
context 'for generic statuses' do
context 'when running' do
let!(:running) do
@@ -406,7 +461,7 @@ describe 'Pipelines', :js do
within('.js-builds-dropdown-list') do
build_element = page.find('.mini-pipeline-graph-dropdown-item')
- expect(build_element['data-original-title']).to eq('build - failed <br> (unknown failure)')
+ expect(build_element['data-original-title']).to eq('build - failed - (unknown failure)')
end
end
end
@@ -641,6 +696,7 @@ describe 'Pipelines', :js do
context 'when user is not logged in' do
before do
+ project.update!(auto_devops_attributes: { enabled: false })
visit project_pipelines_path(project)
end
diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
index 25b74cc481d..70f3a812ee9 100644
--- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Setup Mattermost slash commands', :js do
+describe 'Set up Mattermost slash commands', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:service) { project.create_mattermost_slash_commands_service }
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
index 8745ff72df0..32959969f54 100644
--- a/spec/features/projects/settings/integration_settings_spec.rb
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -51,6 +51,7 @@ describe 'Projects > Settings > Integration settings' do
fill_in 'hook_url', with: url
check 'Tag push events'
+ fill_in 'hook_push_events_branch_filter', with: 'master'
check 'Enable SSL verification'
check 'Job events'
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 30b0a5578ea..6f8ec0015ad 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -137,5 +137,29 @@ describe "Projects > Settings > Pipelines settings" do
end
end
end
+
+ describe 'runners registration token' do
+ let!(:token) { project.runners_token }
+
+ before do
+ visit project_settings_ci_cd_path(project)
+ end
+
+ it 'has a registration token' do
+ expect(page.find('#registration_token')).to have_content(token)
+ end
+
+ describe 'reload registration token' do
+ let(:page_token) { find('#registration_token').text }
+
+ before do
+ click_button 'Reset runners registration token'
+ end
+
+ it 'changes registration token' do
+ expect(page_token).not_to eq token
+ end
+ end
+ end
end
end
diff --git a/spec/features/projects/settings/project_badges_spec.rb b/spec/features/projects/settings/project_badges_spec.rb
index 2ec94274f80..42b5547d43b 100644
--- a/spec/features/projects/settings/project_badges_spec.rb
+++ b/spec/features/projects/settings/project_badges_spec.rb
@@ -15,7 +15,7 @@ describe 'Project Badges' do
group.add_maintainer(user)
sign_in(user)
- visit(project_settings_badges_path(project))
+ visit(edit_project_path(project))
end
it 'shows a list of badges', :js do
diff --git a/spec/features/projects/settings/user_changes_default_branch_spec.rb b/spec/features/projects/settings/user_changes_default_branch_spec.rb
index e925539351d..fcf05e04a5c 100644
--- a/spec/features/projects/settings/user_changes_default_branch_spec.rb
+++ b/spec/features/projects/settings/user_changes_default_branch_spec.rb
@@ -1,20 +1,35 @@
require 'spec_helper'
describe 'Projects > Settings > User changes default branch' do
+ include Select2Helper
+
let(:user) { create(:user) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
sign_in(user)
- visit edit_project_path(project)
+
+ visit project_settings_repository_path(project)
end
- it 'allows to change the default branch' do
- select 'fix', from: 'project_default_branch'
- page.within '.general-settings' do
- click_button 'Save changes'
+ context 'with normal project' do
+ let(:project) { create(:project, :repository, namespace: user.namespace) }
+
+ it 'allows to change the default branch', :js do
+ select2('fix', from: '#project_default_branch')
+
+ page.within '#default-branch-settings' do
+ click_button 'Save changes'
+ end
+
+ expect(find('#project_default_branch', visible: false).value).to eq 'fix'
end
+ end
- expect(find(:css, 'select#project_default_branch').value).to eq 'fix'
+ context 'with empty project' do
+ let(:project) { create(:project_empty_repo, namespace: user.namespace) }
+
+ it 'does not show default branch selector' do
+ expect(page).not_to have_selector('#project_default_branch')
+ end
end
end
diff --git a/spec/features/projects/settings/user_manages_group_links_spec.rb b/spec/features/projects/settings/user_manages_group_links_spec.rb
index 2f1824d7849..676659b90c3 100644
--- a/spec/features/projects/settings/user_manages_group_links_spec.rb
+++ b/spec/features/projects/settings/user_manages_group_links_spec.rb
@@ -26,13 +26,13 @@ describe 'Projects > Settings > User manages group links' do
end
end
- it 'shares a project with a group', :js do
- click_link('Share with group')
+ it 'invites a group to a project', :js do
+ click_link('Invite group')
select2(group_market.id, from: '#link_group_id')
select('Maintainer', from: 'link_group_access')
- click_button('Share')
+ click_button('Invite')
page.within('.project-members-groups') do
expect(page).to have_content('Market')
diff --git a/spec/features/projects/settings/user_tags_project_spec.rb b/spec/features/projects/settings/user_tags_project_spec.rb
index 57b4b1287fa..9357215ae6f 100644
--- a/spec/features/projects/settings/user_tags_project_spec.rb
+++ b/spec/features/projects/settings/user_tags_project_spec.rb
@@ -9,15 +9,13 @@ describe 'Projects > Settings > User tags a project' do
visit edit_project_path(project)
end
- context 'when a project is archived' do
- it 'unarchives a project' do
- fill_in 'Tags', with: 'tag1, tag2'
+ it 'sets project tags' do
+ fill_in 'Tags', with: 'tag1, tag2'
- page.within '.general-settings' do
- click_button 'Save changes'
- end
-
- expect(find_field('Tags').value).to eq 'tag1, tag2'
+ page.within '.general-settings' do
+ click_button 'Save changes'
end
+
+ expect(find_field('Tags').value).to eq 'tag1, tag2'
end
end
diff --git a/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb b/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb
new file mode 100644
index 00000000000..baf715000a3
--- /dev/null
+++ b/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Project > Show > User interacts with auto devops implicitly enabled banner' do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_user(user, role)
+ sign_in(user)
+ end
+
+ context 'when user does not have maintainer access' do
+ let(:role) { :developer }
+
+ context 'when AutoDevOps is implicitly enabled' do
+ it 'does not display AutoDevOps implicitly enabled banner' do
+ expect(page).not_to have_css('.auto-devops-implicitly-enabled-banner')
+ end
+ end
+ end
+
+ context 'when user has mantainer access' do
+ let(:role) { :maintainer }
+
+ context 'when AutoDevOps is implicitly enabled' do
+ before do
+ stub_application_setting(auto_devops_enabled: true)
+
+ visit project_path(project)
+ end
+
+ it 'display AutoDevOps implicitly enabled banner' do
+ expect(page).to have_css('.auto-devops-implicitly-enabled-banner')
+ end
+
+ context 'when user dismisses the banner', :js do
+ it 'does not display AutoDevOps implicitly enabled banner' do
+ find('.hide-auto-devops-implicitly-enabled-banner').click
+ wait_for_requests
+ visit project_path(project)
+
+ expect(page).not_to have_css('.auto-devops-implicitly-enabled-banner')
+ end
+ end
+ end
+
+ context 'when AutoDevOps is not implicitly enabled' do
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+
+ visit project_path(project)
+ end
+
+ it 'does not display AutoDevOps implicitly enabled banner' do
+ expect(page).not_to have_css('.auto-devops-implicitly-enabled-banner')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
index 0405e21a0d7..df2b492ae6b 100644
--- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
+++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'Projects > Show > User sees setup shortcut buttons' do
- # For "New file", "Add License" functionality,
+ # For "New file", "Add license" functionality,
# see spec/features/projects/files/project_owner_creates_license_file_spec.rb
# see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -28,8 +28,6 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
it '"Auto DevOps enabled" button not linked' do
- project.create_auto_devops!(enabled: true)
-
visit project_path(project)
page.within('.project-stats') do
@@ -58,26 +56,30 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
end
- it '"Add License" button linked to new file populated for a license' do
- page.within('.project-stats') do
- expect(page).to have_link('Add License', href: presenter.add_license_path)
+ it '"Add license" button linked to new file populated for a license' do
+ page.within('.project-metadata') do
+ expect(page).to have_link('Add license', href: presenter.add_license_path)
end
end
describe 'Auto DevOps button' do
- it '"Enable Auto DevOps" button linked to settings page' do
- page.within('.project-stats') do
- expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ context 'when Auto DevOps is enabled' do
+ it '"Auto DevOps enabled" anchor linked to settings page' do
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ end
end
end
- it '"Auto DevOps enabled" anchor linked to settings page' do
- project.create_auto_devops!(enabled: true)
+ context 'when Auto DevOps is not enabled' do
+ let(:project) { create(:project, :public, :empty_repo, auto_devops_attributes: { enabled: false }) }
- visit project_path(project)
-
- page.within('.project-stats') do
- expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ it '"Enable Auto DevOps" button linked to settings page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ end
end
end
end
@@ -113,27 +115,31 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
end
- it 'no Auto DevOps button if can not manage pipelines' do
- page.within('.project-stats') do
- expect(page).not_to have_link('Enable Auto DevOps')
- expect(page).not_to have_link('Auto DevOps enabled')
+ context 'when Auto DevOps is enabled' do
+ it '"Auto DevOps enabled" button not linked' do
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_text('Auto DevOps enabled')
+ end
end
end
- it '"Auto DevOps enabled" button not linked' do
- project.create_auto_devops!(enabled: true)
-
- visit project_path(project)
+ context 'when Auto DevOps is not enabled' do
+ let(:project) { create(:project, :public, :repository, auto_devops_attributes: { enabled: false }) }
- page.within('.project-stats') do
- expect(page).to have_text('Auto DevOps enabled')
+ it 'no Auto DevOps button if can not manage pipelines' do
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
end
- end
- it 'no Kubernetes cluster button if can not manage clusters' do
- page.within('.project-stats') do
- expect(page).not_to have_link('Add Kubernetes cluster')
- expect(page).not_to have_link('Kubernetes configured')
+ it 'no Kubernetes cluster button if can not manage clusters' do
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Kubernetes cluster')
+ expect(page).not_to have_link('Kubernetes configured')
+ end
end
end
end
@@ -201,13 +207,13 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
end
- it 'no "Add License" button if the project already has a license' do
+ it 'no "Add license" button if the project already has a license' do
visit project_path(project)
expect(project.repository.license_blob).not_to be_nil
page.within('.project-stats') do
- expect(page).not_to have_link('Add License')
+ expect(page).not_to have_link('Add license')
end
end
@@ -222,97 +228,105 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
describe 'GitLab CI configuration button' do
- it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do
- visit project_path(project)
-
- expect(project.repository.gitlab_ci_yml).to be_nil
+ context 'when Auto DevOps is enabled' do
+ it 'no "Set up CI/CD" button if the project has Auto DevOps enabled' do
+ visit project_path(project)
- page.within('.project-stats') do
- expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up CI/CD')
+ end
end
end
- it 'no "Set up CI/CD" button if the project already has a .gitlab-ci.yml' do
- Files::CreateService.new(
- project,
- project.creator,
- start_branch: 'master',
- branch_name: 'master',
- commit_message: "Add .gitlab-ci.yml",
- file_path: '.gitlab-ci.yml',
- file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
- ).execute
+ context 'when Auto DevOps is not enabled' do
+ let(:project) { create(:project, :public, :repository, auto_devops_attributes: { enabled: false }) }
- expect(project.repository.gitlab_ci_yml).not_to be_nil
+ it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do
+ visit project_path(project)
- visit project_path(project)
+ expect(project.repository.gitlab_ci_yml).to be_nil
- page.within('.project-stats') do
- expect(page).not_to have_link('Set up CI/CD')
+ page.within('.project-stats') do
+ expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
+ end
end
- end
- it 'no "Set up CI/CD" button if the project has Auto DevOps enabled' do
- project.create_auto_devops!(enabled: true)
+ it 'no "Set up CI/CD" button if the project already has a .gitlab-ci.yml' do
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add .gitlab-ci.yml",
+ file_path: '.gitlab-ci.yml',
+ file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ ).execute
- visit project_path(project)
+ expect(project.repository.gitlab_ci_yml).not_to be_nil
- page.within('.project-stats') do
- expect(page).not_to have_link('Set up CI/CD')
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up CI/CD')
+ end
end
end
end
describe 'Auto DevOps button' do
- it '"Enable Auto DevOps" button linked to settings page' do
- visit project_path(project)
+ context 'when Auto DevOps is enabled' do
+ it '"Auto DevOps enabled" anchor linked to settings page' do
+ visit project_path(project)
- page.within('.project-stats') do
- expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ page.within('.project-stats') do
+ expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ end
end
end
- it '"Enable Auto DevOps" button linked to settings page' do
- project.create_auto_devops!(enabled: true)
+ context 'when Auto DevOps is not enabled' do
+ let(:project) { create(:project, :public, :repository, auto_devops_attributes: { enabled: false }) }
- visit project_path(project)
+ it '"Enable Auto DevOps" button linked to settings page' do
+ visit project_path(project)
- page.within('.project-stats') do
- expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ page.within('.project-stats') do
+ expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
+ end
end
- end
- it 'no Auto DevOps button if Auto DevOps callout is shown' do
- allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(true)
+ it 'no Auto DevOps button if Auto DevOps callout is shown' do
+ allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(true)
- visit project_path(project)
+ visit project_path(project)
- expect(page).to have_selector('.js-autodevops-banner')
+ expect(page).to have_selector('.js-autodevops-banner')
- page.within('.project-stats') do
- expect(page).not_to have_link('Enable Auto DevOps')
- expect(page).not_to have_link('Auto DevOps enabled')
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
end
- end
- it 'no "Enable Auto DevOps" button when .gitlab-ci.yml already exists' do
- Files::CreateService.new(
- project,
- project.creator,
- start_branch: 'master',
- branch_name: 'master',
- commit_message: "Add .gitlab-ci.yml",
- file_path: '.gitlab-ci.yml',
- file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
- ).execute
+ it 'no "Enable Auto DevOps" button when .gitlab-ci.yml already exists' do
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add .gitlab-ci.yml",
+ file_path: '.gitlab-ci.yml',
+ file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ ).execute
- expect(project.repository.gitlab_ci_yml).not_to be_nil
+ expect(project.repository.gitlab_ci_yml).not_to be_nil
- visit project_path(project)
+ visit project_path(project)
- page.within('.project-stats') do
- expect(page).not_to have_link('Enable Auto DevOps')
- expect(page).not_to have_link('Auto DevOps enabled')
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
end
end
end
@@ -336,41 +350,6 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
end
end
-
- describe '"Set up Koding" button' do
- it 'no "Set up Koding" button if Koding disabled' do
- stub_application_setting(koding_enabled?: false)
-
- visit project_path(project)
-
- page.within('.project-stats') do
- expect(page).not_to have_link('Set up Koding')
- end
- end
-
- it 'no "Set up Koding" button if the project already has a .koding.yml' do
- stub_application_setting(koding_enabled?: true)
- allow(Gitlab::CurrentSettings.current_application_settings).to receive(:koding_url).and_return('http://koding.example.com')
- expect(project.repository.changelog).not_to be_nil
- allow_any_instance_of(Repository).to receive(:koding_yml).and_return(project.repository.changelog)
-
- visit project_path(project)
-
- page.within('.project-stats') do
- expect(page).not_to have_link('Set up Koding')
- end
- end
-
- it '"Set up Koding" button linked to new file populated for a .koding.yml' do
- stub_application_setting(koding_enabled?: true)
-
- visit project_path(project)
-
- page.within('.project-stats') do
- expect(page).to have_link('Set up Koding', href: presenter.add_koding_stack_path)
- end
- end
- end
end
end
end
diff --git a/spec/features/projects/tags/user_views_tags_spec.rb b/spec/features/projects/tags/user_views_tags_spec.rb
new file mode 100644
index 00000000000..f344b682715
--- /dev/null
+++ b/spec/features/projects/tags/user_views_tags_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe 'User views tags', :feature do
+ context 'rss' do
+ shared_examples 'has access to the tags RSS feed' do
+ it do
+ visit project_tags_path(project, format: :atom)
+
+ expect(page).to have_gitlab_http_status(200)
+ end
+ end
+
+ shared_examples 'does not have access to the tags RSS feed' do
+ it do
+ visit project_tags_path(project, format: :atom)
+
+ expect(page).to have_gitlab_http_status(401)
+ end
+ end
+
+ context 'when project public' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+
+ context 'when user signed in' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ visit project_tags_path(project)
+ end
+
+ it_behaves_like "it has an RSS button with current_user's feed token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's feed token"
+ it_behaves_like 'has access to the tags RSS feed'
+ end
+
+ context 'when user signed out' do
+ before do
+ visit project_tags_path(project)
+ end
+
+ it_behaves_like 'it has an RSS button without a feed token'
+ it_behaves_like 'an autodiscoverable RSS feed without a feed token'
+ it_behaves_like 'has access to the tags RSS feed'
+ end
+ end
+
+ context 'when project is not public' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
+
+ context 'when user signed in' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ it_behaves_like 'has access to the tags RSS feed'
+ end
+
+ context 'when user signed out' do
+ it_behaves_like 'does not have access to the tags RSS feed'
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 9e58280b868..2cb2a23b7be 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -43,7 +43,7 @@ describe 'Multi-file editor new directory', :js do
find('.js-ide-commit-mode').click
find('.multi-file-commit-list-item').hover
- first('.multi-file-discard-btn .btn').click
+ click_button 'Stage'
fill_in('commit-message', with: 'commit message ide')
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index a04d3566a7e..9f5524da8e9 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -35,7 +35,7 @@ describe 'Multi-file editor new file', :js do
find('.js-ide-commit-mode').click
find('.multi-file-commit-list-item').hover
- first('.multi-file-discard-btn .btn').click
+ click_button 'Stage'
fill_in('commit-message', with: 'commit message ide')
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index 8ae036cd29f..45e81e1c040 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -4,16 +4,30 @@ describe 'Projects tree', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
+ # This commit has a known state on the master branch of gitlab-test
+ let(:test_sha) { '7975be0116940bf2ad4321f79d02a55c5f7779aa' }
+
before do
project.add_maintainer(user)
sign_in(user)
end
it 'renders tree table without errors' do
- visit project_tree_path(project, 'master')
+ visit project_tree_path(project, test_sha)
+ wait_for_requests
+
+ expect(page).to have_selector('.tree-item')
+ expect(page).to have_content('add tests for .gitattributes custom highlighting')
+ expect(page).not_to have_selector('.flash-alert')
+ expect(page).not_to have_selector('.label-lfs', text: 'LFS')
+ end
+
+ it 'renders tree table for a subtree without errors' do
+ visit project_tree_path(project, File.join(test_sha, 'files'))
wait_for_requests
expect(page).to have_selector('.tree-item')
+ expect(page).to have_content('add spaces in whitespace file')
expect(page).not_to have_selector('.label-lfs', text: 'LFS')
expect(page).not_to have_selector('.flash-alert')
end
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
index 83d18996f4e..8d7e2883b2a 100644
--- a/spec/features/projects/user_creates_project_spec.rb
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -11,7 +11,7 @@ describe 'User creates a project', :js do
it 'creates a new project' do
visit(new_project_path)
- fill_in(:project_path, with: 'Empty')
+ fill_in(:project_name, with: 'Empty')
page.within('#content-body') do
click_button('Create project')
@@ -37,6 +37,7 @@ describe 'User creates a project', :js do
it 'creates a new project' do
visit(new_project_path)
+ fill_in :project_name, with: 'A Subgroup Project'
fill_in :project_path, with: 'a-subgroup-project'
page.find('.js-select-namespace').click
@@ -46,7 +47,7 @@ describe 'User creates a project', :js do
click_button('Create project')
end
- expect(page).to have_content("Project 'a-subgroup-project' was successfully created")
+ expect(page).to have_content("Project 'A Subgroup Project' was successfully created")
project = Project.last
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index a48ad94e9fa..7bfcd46713e 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -44,7 +44,7 @@ describe 'View on environment', :js do
context 'and an active deployment' do
let(:sha) { project.commit(branch_name).sha }
let(:environment) { create(:environment, project: project, name: 'review/feature', external_url: 'http://feature.review.example.com') }
- let!(:deployment) { create(:deployment, environment: environment, ref: branch_name, sha: sha) }
+ let!(:deployment) { create(:deployment, :success, environment: environment, ref: branch_name, sha: sha) }
context 'when visiting the diff of a merge request for the branch' do
let(:merge_request) { create(:merge_request, :simple, source_project: project, source_branch: branch_name) }
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index ed5f8105487..f505023d1d0 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -162,6 +162,34 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
+
+ context 'when rendering the preview' do
+ it 'renders content with CommonMark' do
+ create_wiki_page 'a-page/b-page/c-page/common-mark'
+ click_link 'Edit'
+
+ fill_in :wiki_content, with: "1. one\n - sublist\n"
+ click_on "Preview"
+
+ # the above generates two seperate lists (not embedded) in CommonMark
+ expect(page).to have_content("sublist")
+ expect(page).not_to have_xpath("//ol//li//ul")
+ end
+
+ it 'renders content with RedCarpet when legacy_render is set' do
+ wiki_page = create(:wiki_page,
+ wiki: project.wiki,
+ attrs: { title: 'home', content: "Empty content" })
+ visit(project_wiki_edit_path(project, wiki_page, legacy_render: 1))
+
+ fill_in :wiki_content, with: "1. one\n - sublist\n"
+ click_on "Preview"
+
+ # the above generates a sublist list in RedCarpet
+ expect(page).to have_content("sublist")
+ expect(page).to have_xpath("//ol//li//ul")
+ end
+ end
end
it "does not linkify double brackets inside code blocks as expected" do
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 149eeb4f9ba..b30286e4446 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -146,6 +146,8 @@ describe "User creates wiki page" do
expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4")
end
end
+
+ it_behaves_like 'wiki file attachments'
end
context "in a group namespace", :js do
diff --git a/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb b/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb
index 5007794cd77..18ccd31f3d0 100644
--- a/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb
@@ -13,7 +13,7 @@ describe 'User deletes wiki page', :js do
it 'deletes a page' do
click_on('Edit')
click_on('Delete')
- find('.js-modal-primary-action').click
+ find('.modal-footer .btn-danger').click
expect(page).to have_content('Page was successfully deleted')
end
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index 2840d28cf30..2ce5ee0e87d 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe 'User updates wiki page' do
shared_examples 'wiki page user update' do
let(:user) { create(:user) }
+
before do
project.add_maintainer(user)
sign_in(user)
@@ -55,6 +56,8 @@ describe 'User updates wiki page' do
expect(page).to have_content('Updated Wiki Content')
end
+
+ it_behaves_like 'wiki file attachments'
end
end
@@ -64,14 +67,14 @@ describe 'User updates wiki page' do
before do
visit(project_wikis_path(project))
+
+ click_link('Edit')
end
context 'in a user namespace' do
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
it 'updates a page' do
- click_link('Edit')
-
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Update home')
@@ -84,8 +87,6 @@ describe 'User updates wiki page' do
end
it 'shows a validation error message' do
- click_link('Edit')
-
fill_in(:wiki_content, with: '')
click_button('Save changes')
@@ -97,8 +98,6 @@ describe 'User updates wiki page' do
end
it 'shows the emoji autocompletion dropdown', :js do
- click_link('Edit')
-
find('#wiki_content').native.send_keys('')
fill_in(:wiki_content, with: ':')
@@ -106,8 +105,6 @@ describe 'User updates wiki page' do
end
it 'shows the error message' do
- click_link('Edit')
-
wiki_page.update(content: 'Update')
click_button('Save changes')
@@ -116,30 +113,27 @@ describe 'User updates wiki page' do
end
it 'updates a page' do
- click_on('Edit')
fill_in('Content', with: 'Updated Wiki Content')
click_on('Save changes')
expect(page).to have_content('Updated Wiki Content')
end
- it 'cancels edititng of a page' do
- click_on('Edit')
-
+ it 'cancels editing of a page' do
page.within(:css, '.wiki-form .form-actions') do
click_on('Cancel')
end
expect(current_path).to eq(project_wiki_path(project, wiki_page))
end
+
+ it_behaves_like 'wiki file attachments'
end
context 'in a group namespace' do
let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
it 'updates a page' do
- click_link('Edit')
-
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Update home')
@@ -151,6 +145,8 @@ describe 'User updates wiki page' do
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
+
+ it_behaves_like 'wiki file attachments'
end
end
@@ -222,6 +218,8 @@ describe 'User updates wiki page' do
expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}"))
end
+
+ it_behaves_like 'wiki file attachments'
end
end
diff --git a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
index 83ffbb4a94e..e94b3a9432b 100644
--- a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
@@ -54,7 +54,7 @@ describe 'User views empty wiki' do
it_behaves_like 'empty wiki and non-accessible issues'
end
- context 'when user is logged in and a memeber' do
+ context 'when user is logged in and a member' do
let(:project) { create(:project, :public, :wiki_repo) }
before do
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index 760324adacc..4b974a3ca10 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -2,12 +2,15 @@ require 'spec_helper'
describe 'User views a wiki page' do
shared_examples 'wiki page user view' do
+ include WikiHelpers
+
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
+ let(:path) { 'image.png' }
let(:wiki_page) do
create(:wiki_page,
wiki: project.wiki,
- attrs: { title: 'home', content: 'Look at this [image](image.jpg)\n\n ![alt text](image.jpg)' })
+ attrs: { title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})" })
end
before do
@@ -82,32 +85,26 @@ describe 'User views a wiki page' do
expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize)
end
- it 'shows a file stored in a page' do
- gollum_file_double = double('Gollum::File',
- mime_type: 'image/jpeg',
- name: 'images/image.jpg',
- path: 'images/image.jpg',
- raw_data: '')
- wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double)
-
- allow(wiki_file).to receive(:mime_type).and_return('image/jpeg')
- allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file)
+ context 'shows a file stored in a page' do
+ let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
- expect(page).to have_xpath('//img[@data-src="image.jpg"]')
- expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
+ it do
+ expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
+ expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
- click_on('image')
+ click_on('image')
- expect(current_path).to match('wikis/image.jpg')
- expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
+ expect(current_path).to match("wikis/#{path}")
+ expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
+ end
end
it 'shows the creation page if file does not exist' do
- expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
+ expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
click_on('image')
- expect(current_path).to match('wikis/image.jpg')
+ expect(current_path).to match("wikis/#{path}")
expect(page).to have_content('New Wiki Page')
expect(page).to have_content('Create page')
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 56ed0c936a6..0add129dde2 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe 'Project' do
include ProjectForksHelper
+ include MobileHelpers
describe 'creating from template' do
let(:user) { create(:user) }
@@ -15,7 +16,7 @@ describe 'Project' do
it "allows creation from templates", :js do
find('#create-from-template-tab').click
find("label[for=#{template.name}]").click
- fill_in("project_path", with: template.name)
+ fill_in("project_name", with: template.name)
page.within '#content-body' do
click_button "Create project"
@@ -54,25 +55,72 @@ describe 'Project' do
it 'parses Markdown' do
project.update_attribute(:description, 'This is **my** project')
visit path
- expect(page).to have_css('.project-home-desc > p > strong')
+ expect(page).to have_css('.project-description > .project-description-markdown > p > strong')
end
it 'passes through html-pipeline' do
project.update_attribute(:description, 'This project is the :poop:')
visit path
- expect(page).to have_css('.project-home-desc > p > gl-emoji')
+ expect(page).to have_css('.project-description > .project-description-markdown > p > gl-emoji')
end
it 'sanitizes unwanted tags' do
project.update_attribute(:description, "```\ncode\n```")
visit path
- expect(page).not_to have_css('.project-home-desc code')
+ expect(page).not_to have_css('.project-description code')
end
it 'permits `rel` attribute on links' do
project.update_attribute(:description, 'https://google.com/')
visit path
- expect(page).to have_css('.project-home-desc a[rel]')
+ expect(page).to have_css('.project-description a[rel]')
+ end
+
+ context 'read more', :js do
+ let(:read_more_selector) { '.read-more-container' }
+ let(:read_more_trigger_selector) { '.project-home-desc .js-read-more-trigger' }
+
+ it 'does not display "read more" link on desktop breakpoint' do
+ project.update_attribute(:description, 'This is **my** project')
+ visit path
+
+ expect(find(read_more_trigger_selector, visible: false)).not_to be_visible
+ end
+
+ it 'displays "read more" link on mobile breakpoint' do
+ project.update_attribute(:description, 'This is **my** project')
+ visit path
+ resize_screen_xs
+
+ find(read_more_trigger_selector).click
+
+ expect(page).to have_css('.project-description .is-expanded')
+ end
+ end
+ end
+
+ describe 'copy clone URL to clipboard', :js do
+ let(:project) { create(:project, :repository) }
+ let(:path) { project_path(project) }
+
+ before do
+ sign_in(create(:admin))
+ visit path
+ end
+
+ context 'desktop component' do
+ it 'shows on md and larger breakpoints' do
+ expect(find('.git-clone-holder')).to be_visible
+ expect(find('.mobile-git-clone', visible: false)).not_to be_visible
+ end
+ end
+
+ context 'mobile component' do
+ it 'shows mobile component on sm and smaller breakpoints' do
+ resize_screen_xs
+ expect(find('.mobile-git-clone')).to be_visible
+ expect(find('.git-clone-holder', visible: false)).not_to be_visible
+ end
end
end
@@ -145,6 +193,23 @@ describe 'Project' do
end
end
+ describe 'when the project repository is disabled', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository_disabled, :repository, namespace: user.namespace) }
+
+ before do
+ sign_in(user)
+ project.add_maintainer(user)
+ visit project_path(project)
+ end
+
+ it 'does not show an error' do
+ wait_for_requests
+
+ expect(page).not_to have_selector('.flash-alert')
+ end
+ end
+
describe 'removal', :js do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
@@ -257,6 +322,22 @@ describe 'Project' do
end
end
+ context 'content is not cached after signing out', :js do
+ let(:user) { create(:user, project_view: 'activity') }
+ let(:project) { create(:project, :repository) }
+
+ it 'does not load activity', :js do
+ project.add_maintainer(user)
+ sign_in(user)
+ visit project_path(project)
+ sign_out(user)
+
+ page.evaluate_script('window.history.back()')
+
+ expect(page).not_to have_selector('.event-item')
+ end
+ end
+
def remove_with_confirm(button_text, confirm_with)
click_button button_text
fill_in 'confirm_name_input', with: confirm_with
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 0c6cf3dc477..cb7a912946c 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -198,7 +198,7 @@ describe 'Runners' do
expect(page).to have_content 'This group does not provide any group Runners yet'
expect(page).to have_content 'Group maintainers can register group runners in the Group CI/CD settings'
- expect(page).not_to have_content 'Ask your group maintainer to setup a group Runner'
+ expect(page).not_to have_content 'Ask your group maintainer to set up a group Runner'
end
end
end
@@ -224,7 +224,7 @@ describe 'Runners' do
expect(page).to have_content 'This group does not provide any group Runners yet.'
expect(page).not_to have_content 'Group maintainers can register group runners in the Group CI/CD settings'
- expect(page).to have_content 'Ask your group maintainer to setup a group Runner.'
+ expect(page).to have_content 'Ask your group maintainer to set up a group Runner.'
end
end
diff --git a/spec/features/search/user_searches_for_wiki_pages_spec.rb b/spec/features/search/user_searches_for_wiki_pages_spec.rb
index 3ee753b7d23..7225ca65492 100644
--- a/spec/features/search/user_searches_for_wiki_pages_spec.rb
+++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'User searches for wiki pages', :js do
let(:user) { create(:user) }
- let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
+ let(:project) { create(:project, :repository, :wiki_repo, namespace: user.namespace) }
let!(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'test_wiki', content: 'Some Wiki content' }) }
before do
diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb
index af38f77c0c6..444de26733f 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -21,13 +21,17 @@ describe 'User uses header search field' do
it 'shows assigned issues' do
find('.search-input-container .dropdown-menu').click_link('Issues assigned to me')
- expect(find('.js-assignee-search')).to have_content(user.name)
+ expect(page).to have_selector('.filtered-search')
+ expect_tokens([assignee_token(user.name)])
+ expect_filtered_search_input_empty
end
it 'shows created issues' do
find('.search-input-container .dropdown-menu').click_link("Issues I've created")
- expect(find('.js-author-search')).to have_content(user.name)
+ expect(page).to have_selector('.filtered-search')
+ expect_tokens([author_token(user.name)])
+ expect_filtered_search_input_empty
end
end
@@ -37,13 +41,17 @@ describe 'User uses header search field' do
it 'shows assigned merge requests' do
find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me')
- expect(find('.js-assignee-search')).to have_content(user.name)
+ expect(page).to have_selector('.filtered-search')
+ expect_tokens([assignee_token(user.name)])
+ expect_filtered_search_input_empty
end
it 'shows created merge requests' do
find('.search-input-container .dropdown-menu').click_link("Merge requests I've created")
- expect(find('.js-author-search')).to have_content(user.name)
+ expect(page).to have_selector('.filtered-search')
+ expect_tokens([author_token(user.name)])
+ expect_filtered_search_input_empty
end
end
end
diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb
index 66afe163447..0725ff178ac 100644
--- a/spec/features/search/user_uses_search_filters_spec.rb
+++ b/spec/features/search/user_uses_search_filters_spec.rb
@@ -14,7 +14,7 @@ describe 'User uses search filters', :js do
visit(search_path)
end
- context' when filtering by group' do
+ context 'when filtering by group' do
it 'shows group projects' do
find('.js-search-group-dropdown').click
@@ -36,7 +36,7 @@ describe 'User uses search filters', :js do
end
end
- context' when filtering by project' do
+ context 'when filtering by project' do
it 'shows a project' do
page.within('.project-filter') do
find('.js-search-project-dropdown').click
diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb
index 149bd32e736..0c893e65d9c 100644
--- a/spec/features/security/dashboard_access_spec.rb
+++ b/spec/features/security/dashboard_access_spec.rb
@@ -43,20 +43,6 @@ describe "Dashboard access" do
it { is_expected.to be_allowed_for :visitor }
end
- describe "GET /koding" do
- subject { koding_path }
-
- context 'with Koding enabled' do
- before do
- stub_application_setting(koding_enabled?: true)
- end
-
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- end
- end
-
describe "GET /projects/new" do
it { expect(new_project_path).to be_allowed_for :admin }
it { expect(new_project_path).to be_allowed_for :user }
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 1442e011d52..eeacaf5f72a 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -103,7 +103,7 @@ describe 'Comments on personal snippets', :js do
page.within('.current-note-edit-form') do
fill_in 'note[note]', with: 'new content'
- find('.btn-save').click
+ find('.btn-success').click
end
page.within("#notes-list li#note_#{snippet_notes[0].id}") do
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
index 3fe0b60b18f..367a479f62a 100644
--- a/spec/features/snippets/show_spec.rb
+++ b/spec/features/snippets/show_spec.rb
@@ -68,23 +68,45 @@ describe 'Snippet', :js do
end
end
- context 'with cached Redcarpet html' do
- let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION) }
+ context 'Markdown rendering' do
+ let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content) }
let(:file_name) { 'test.md' }
let(:content) { "1. one\n - sublist\n" }
- it 'renders correctly' do
- expect(page).to have_xpath("//ol//li//ul")
+ context 'when rendering default markdown' do
+ it 'renders using CommonMark' do
+ expect(page).to have_content("sublist")
+ expect(page).not_to have_xpath("//ol//li//ul")
+ end
end
- end
- context 'with cached CommonMark html' do
- let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
- let(:file_name) { 'test.md' }
- let(:content) { "1. one\n - sublist\n" }
+ context 'when rendering legacy markdown' do
+ before do
+ visit snippet_path(snippet, legacy_render: 1)
- it 'renders correctly' do
- expect(page).not_to have_xpath("//ol//li//ul")
+ wait_for_requests
+ end
+
+ it 'renders using RedCarpet' do
+ expect(page).to have_content("sublist")
+ expect(page).to have_xpath("//ol//li//ul")
+ end
+ end
+
+ context 'with cached CommonMark html' do
+ let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
+
+ it 'renders correctly' do
+ expect(page).not_to have_xpath("//ol//li//ul")
+ end
+ end
+
+ context 'with cached Redcarpet html' do
+ let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION) }
+
+ it 'renders correctly' do
+ expect(page).to have_xpath("//ol//li//ul")
+ end
end
end
diff --git a/spec/features/snippets/user_sees_breadcrumb_links.rb b/spec/features/snippets/user_sees_breadcrumb_links.rb
new file mode 100644
index 00000000000..696f2b93390
--- /dev/null
+++ b/spec/features/snippets/user_sees_breadcrumb_links.rb
@@ -0,0 +1,17 @@
+require 'rails_helper'
+
+describe 'New user snippet breadcrumbs' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ visit new_snippet_path
+ end
+
+ it 'display a link to user snippets and new user snippet pages' do
+ page.within '.breadcrumbs' do
+ expect(find_link('Snippets')[:href]).to end_with(dashboard_snippets_path)
+ expect(find_link('New')[:href]).to end_with(new_snippet_path)
+ end
+ end
+end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index f245c1ebbd9..ae9b65d1a39 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -3,14 +3,14 @@ require 'spec_helper'
describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
def manage_two_factor_authentication
click_on 'Manage two-factor authentication'
- expect(page).to have_content("Setup new U2F device")
+ expect(page).to have_content("Set up new U2F device")
wait_for_requests
end
def register_u2f_device(u2f_device = nil, name: 'My device')
u2f_device ||= FakeU2fDevice.new(page, name)
u2f_device.respond_to_u2f_registration
- click_on 'Setup new U2F device'
+ click_on 'Set up new U2F device'
expect(page).to have_content('Your device was successfully set up')
fill_in "Pick a name", with: name
click_on 'Register U2F device'
@@ -34,7 +34,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
visit profile_account_path
click_on 'Enable two-factor authentication'
- expect(page).to have_button('Setup new U2F device', disabled: true)
+ expect(page).to have_button('Set up new U2F device', disabled: true)
end
end
@@ -42,7 +42,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
it 'allows registering a new device with a name' do
visit profile_account_path
manage_two_factor_authentication
- expect(page).to have_content("You've already enabled two-factor authentication using mobile")
+ expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
u2f_device = register_u2f_device
@@ -70,7 +70,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
it 'allows deleting a device' do
visit profile_account_path
manage_two_factor_authentication
- expect(page).to have_content("You've already enabled two-factor authentication using mobile")
+ expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
first_u2f_device = register_u2f_device
second_u2f_device = register_u2f_device(name: 'My other device')
@@ -109,7 +109,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
# Have the "u2f device" respond with bad data
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
- click_on 'Setup new U2F device'
+ click_on 'Set up new U2F device'
expect(page).to have_content('Your device was successfully set up')
click_on 'Register U2F device'
@@ -124,7 +124,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
# Failed registration
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
- click_on 'Setup new U2F device'
+ click_on 'Set up new U2F device'
expect(page).to have_content('Your device was successfully set up')
click_on 'Register U2F device'
expect(page).to have_content("The form contains the following error")
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index a07edc42eae..72b53bae46a 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -15,7 +15,7 @@ describe 'User uploads avatar to group' do
)
page.within('.gs-general') do
- click_button 'Save group'
+ click_button 'Save changes'
end
visit group_path(group)
diff --git a/spec/features/usage_stats_consent_spec.rb b/spec/features/usage_stats_consent_spec.rb
new file mode 100644
index 00000000000..dd8f3179895
--- /dev/null
+++ b/spec/features/usage_stats_consent_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Usage stats consent' do
+ context 'when signed in' do
+ let(:user) { create(:admin, created_at: 8.days.ago) }
+ let(:message) { 'To help improve GitLab, we would like to periodically collect usage information.' }
+
+ before do
+ allow(user).to receive(:has_current_license?).and_return false
+
+ gitlab_sign_in(user)
+ end
+
+ it 'hides the banner permanently when sets usage stats' do
+ visit root_dashboard_path
+
+ expect(page).to have_content(message)
+
+ click_link 'Send usage data'
+
+ expect(page).not_to have_content(message)
+ expect(page).to have_content('Application settings saved successfully')
+
+ gitlab_sign_out
+ gitlab_sign_in(user)
+ visit root_dashboard_path
+
+ expect(page).not_to have_content(message)
+ end
+
+ it 'shows banner on next session if user did not set usage stats' do
+ visit root_dashboard_path
+
+ expect(page).to have_content(message)
+
+ gitlab_sign_out
+ gitlab_sign_in(user)
+ visit root_dashboard_path
+
+ expect(page).to have_content(message)
+ end
+ end
+end
diff --git a/spec/features/user_sorts_things_spec.rb b/spec/features/user_sorts_things_spec.rb
index 69ebdddaeec..0295f588326 100644
--- a/spec/features/user_sorts_things_spec.rb
+++ b/spec/features/user_sorts_things_spec.rb
@@ -6,7 +6,7 @@ require "spec_helper"
# All those specs are moved out to this spec intentionally to keep them all in one place.
describe "User sorts things" do
include Spec::Support::Helpers::Features::SortingHelpers
- include Helpers::DashboardHelper
+ include DashboardHelper
set(:project) { create(:project_empty_repo, :public) }
set(:current_user) { create(:user) } # Using `current_user` instead of just `user` because of the hardoced call in `assigned_mrs_dashboard_path` which is used below.
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 44758f862a8..ad856bd062e 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe 'Login' do
include TermsHelper
+ include UserLoginHelper
before do
stub_authentication_activity_metrics(debug: true)
@@ -546,29 +547,6 @@ describe 'Login' do
ensure_tab_pane_correctness(false)
end
end
-
- def ensure_tab_pane_correctness(visit_path = true)
- if visit_path
- visit new_user_session_path
- end
-
- ensure_tab_pane_counts
- ensure_one_active_tab
- ensure_one_active_pane
- end
-
- def ensure_tab_pane_counts
- tabs_count = page.all('[role="tab"]').size
- expect(page).to have_selector('[role="tabpanel"]', count: tabs_count)
- end
-
- def ensure_one_active_tab
- expect(page).to have_selector('ul.new-session-tabs > li > a.active', count: 1)
- end
-
- def ensure_one_active_pane
- expect(page).to have_selector('.tab-pane.active', count: 1)
- end
end
context 'when terms are enforced' do
diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb
new file mode 100644
index 00000000000..34ed771340f
--- /dev/null
+++ b/spec/features/users/overview_spec.rb
@@ -0,0 +1,150 @@
+require 'spec_helper'
+
+describe 'Overview tab on a user profile', :js do
+ let(:user) { create(:user) }
+ let(:contributed_project) { create(:project, :public, :repository) }
+
+ def push_code_contribution
+ event = create(:push_event, project: contributed_project, author: user)
+
+ create(:push_event_payload,
+ event: event,
+ commit_from: '11f9ac0a48b62cef25eedede4c1819964f08d5ce',
+ commit_to: '1cf19a015df3523caf0a1f9d40c98a267d6a2fc2',
+ commit_count: 3,
+ ref: 'master')
+ end
+
+ before do
+ sign_in user
+ end
+
+ describe 'activities section' do
+ shared_context 'visit overview tab' do
+ before do
+ visit user.username
+ page.find('.js-overview-tab a').click
+ wait_for_requests
+ end
+ end
+
+ describe 'user has no activities' do
+ include_context 'visit overview tab'
+
+ it 'does not show any entries in the list of activities' do
+ page.within('.activities-block') do
+ expect(page).not_to have_selector('.event-item')
+ end
+ end
+
+ it 'does not show a link to the activity list' do
+ expect(find('#js-overview .activities-block')).to have_selector('.js-view-all', visible: false)
+ end
+ end
+
+ describe 'user has 3 activities' do
+ before do
+ 3.times { push_code_contribution }
+ end
+
+ include_context 'visit overview tab'
+
+ it 'display 3 entries in the list of activities' do
+ expect(find('#js-overview')).to have_selector('.event-item', count: 3)
+ end
+ end
+
+ describe 'user has 11 activities' do
+ before do
+ 11.times { push_code_contribution }
+ end
+
+ include_context 'visit overview tab'
+
+ it 'displays 10 entries in the list of activities' do
+ expect(find('#js-overview')).to have_selector('.event-item', count: 10)
+ end
+
+ it 'shows a link to the activity list' do
+ expect(find('#js-overview .activities-block')).to have_selector('.js-view-all', visible: true)
+ end
+
+ it 'links to the activity tab' do
+ page.within('.activities-block') do
+ find('.js-view-all').click
+ wait_for_requests
+ expect(URI.parse(current_url).path).to eq("/users/#{user.username}/activity")
+ end
+ end
+ end
+ end
+
+ describe 'projects section' do
+ shared_context 'visit overview tab' do
+ before do
+ visit user.username
+ page.find('.js-overview-tab a').click
+ wait_for_requests
+ end
+ end
+
+ describe 'user has no personal projects' do
+ include_context 'visit overview tab'
+
+ it 'it shows an empty project list with an info message' do
+ page.within('.projects-block') do
+ expect(page).to have_content('No projects found')
+ expect(page).not_to have_selector('.project-row')
+ end
+ end
+
+ it 'does not show a link to the project list' do
+ expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: false)
+ end
+ end
+
+ describe 'user has a personal project' do
+ before do
+ create(:project, :private, namespace: user.namespace, creator: user) { |p| p.add_maintainer(user) }
+ end
+
+ include_context 'visit overview tab'
+
+ it 'it shows one entry in the list of projects' do
+ page.within('.projects-block') do
+ expect(page).to have_selector('.project-row', count: 1)
+ end
+ end
+
+ it 'shows a link to the project list' do
+ expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: true)
+ end
+ end
+
+ describe 'user has more than ten personal projects' do
+ before do
+ create_list(:project, 11, :private, namespace: user.namespace, creator: user) do |project|
+ project.add_maintainer(user)
+ end
+ end
+
+ include_context 'visit overview tab'
+
+ it 'it shows max. ten entries in the list of projects' do
+ page.within('.projects-block') do
+ expect(page).to have_selector('.project-row', count: 10)
+ end
+ end
+
+ it 'shows a link to the project list' do
+ expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: true)
+ end
+
+ it 'does not show pagination' do
+ page.within('.projects-block') do
+ expect(page).not_to have_selector('.gl-pagination')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index bc07ab48c39..86379164cf0 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -8,6 +8,7 @@ describe 'User page' do
visit(user_path(user))
page.within '.nav-links' do
+ expect(page).to have_link('Overview')
expect(page).to have_link('Activity')
expect(page).to have_link('Groups')
expect(page).to have_link('Contributed projects')
@@ -44,6 +45,7 @@ describe 'User page' do
visit(user_path(user))
page.within '.nav-links' do
+ expect(page).to have_link('Overview')
expect(page).to have_link('Activity')
expect(page).to have_link('Groups')
expect(page).to have_link('Contributed projects')
diff --git a/spec/finders/admin/runners_finder_spec.rb b/spec/finders/admin/runners_finder_spec.rb
new file mode 100644
index 00000000000..0b2325cc7ca
--- /dev/null
+++ b/spec/finders/admin/runners_finder_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Admin::RunnersFinder do
+ describe '#execute' do
+ context 'with empty params' do
+ it 'returns all runners' do
+ runner1 = create :ci_runner, active: true
+ runner2 = create :ci_runner, active: false
+
+ expect(described_class.new(params: {}).execute).to match_array [runner1, runner2]
+ end
+ end
+
+ context 'filter by search term' do
+ it 'calls Ci::Runner.search' do
+ expect(Ci::Runner).to receive(:search).with('term').and_call_original
+
+ described_class.new(params: { search: 'term' }).execute
+ end
+ end
+
+ context 'filter by status' do
+ it 'calls the corresponding scope on Ci::Runner' do
+ expect(Ci::Runner).to receive(:paused).and_call_original
+
+ described_class.new(params: { status_status: 'paused' }).execute
+ end
+ end
+
+ context 'filter by runner type' do
+ it 'calls the corresponding scope on Ci::Runner' do
+ expect(Ci::Runner).to receive(:project_type).and_call_original
+
+ described_class.new(params: { type_type: 'project_type' }).execute
+ end
+ end
+
+ context 'sort' do
+ context 'without sort param' do
+ it 'sorts by created_at' do
+ runner1 = create :ci_runner, created_at: '2018-07-12 07:00'
+ runner2 = create :ci_runner, created_at: '2018-07-12 08:00'
+ runner3 = create :ci_runner, created_at: '2018-07-12 09:00'
+
+ expect(described_class.new(params: {}).execute).to eq [runner3, runner2, runner1]
+ end
+ end
+
+ context 'with sort param' do
+ it 'sorts by specified attribute' do
+ runner1 = create :ci_runner, contacted_at: 1.minute.ago
+ runner2 = create :ci_runner, contacted_at: 3.minutes.ago
+ runner3 = create :ci_runner, contacted_at: 2.minutes.ago
+
+ expect(described_class.new(params: { sort: 'contacted_asc' }).execute).to eq [runner2, runner3, runner1]
+ end
+ end
+ end
+
+ context 'paginate' do
+ it 'returns the runners for the specified page' do
+ stub_const('Admin::RunnersFinder::NUMBER_OF_RUNNERS_PER_PAGE', 1)
+ runner1 = create :ci_runner, created_at: '2018-07-12 07:00'
+ runner2 = create :ci_runner, created_at: '2018-07-12 08:00'
+
+ expect(described_class.new(params: { page: 1 }).execute).to eq [runner2]
+ expect(described_class.new(params: { page: 2 }).execute).to eq [runner1]
+ end
+ end
+ end
+end
diff --git a/spec/finders/applications_finder_spec.rb b/spec/finders/applications_finder_spec.rb
new file mode 100644
index 00000000000..14d6b35cc27
--- /dev/null
+++ b/spec/finders/applications_finder_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ApplicationsFinder do
+ let(:application1) { create(:application, name: 'some_application', owner: nil, redirect_uri: 'http://some_application.url', scopes: '') }
+ let(:application2) { create(:application, name: 'another_application', owner: nil, redirect_uri: 'http://other_application.url', scopes: '') }
+
+ describe '#execute' do
+ it 'returns an array of applications' do
+ found = described_class.new.execute
+
+ expect(found).to match_array([application1, application2])
+ end
+ it 'returns the application by id' do
+ params = { id: application1.id }
+ found = described_class.new(params).execute
+
+ expect(found).to match(application1)
+ end
+ end
+end
diff --git a/spec/finders/autocomplete/group_finder_spec.rb b/spec/finders/autocomplete/group_finder_spec.rb
new file mode 100644
index 00000000000..d7cb2c3bbe2
--- /dev/null
+++ b/spec/finders/autocomplete/group_finder_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Autocomplete::GroupFinder do
+ let(:user) { create(:user) }
+
+ describe '#execute' do
+ context 'with a project' do
+ it 'returns nil' do
+ project = create(:project)
+
+ expect(described_class.new(user, project).execute).to be_nil
+ end
+ end
+
+ context 'without a group ID' do
+ it 'returns nil' do
+ expect(described_class.new(user).execute).to be_nil
+ end
+ end
+
+ context 'with an empty String as the group ID' do
+ it 'returns nil' do
+ expect(described_class.new(user, nil, group_id: '').execute).to be_nil
+ end
+ end
+
+ context 'without a project and with a group ID' do
+ it 'raises ActiveRecord::RecordNotFound if the group does not exist' do
+ finder = described_class.new(user, nil, group_id: 1)
+
+ expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises ActiveRecord::RecordNotFound if the user can not read the group' do
+ group = create(:group, :private)
+ finder = described_class.new(user, nil, group_id: group.id)
+
+ expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises ActiveRecord::RecordNotFound if an anonymous user can not read the group' do
+ group = create(:group, :private)
+ finder = described_class.new(nil, nil, group_id: group.id)
+
+ expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'returns the group if it exists and is readable' do
+ group = create(:group)
+ finder = described_class.new(user, nil, group_id: group.id)
+
+ expect(finder.execute).to eq(group)
+ end
+ end
+ end
+end
diff --git a/spec/finders/autocomplete/move_to_project_finder_spec.rb b/spec/finders/autocomplete/move_to_project_finder_spec.rb
new file mode 100644
index 00000000000..c3bc410a7f6
--- /dev/null
+++ b/spec/finders/autocomplete/move_to_project_finder_spec.rb
@@ -0,0 +1,109 @@
+require 'spec_helper'
+
+describe Autocomplete::MoveToProjectFinder do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ let(:no_access_project) { create(:project) }
+ let(:guest_project) { create(:project) }
+ let(:reporter_project) { create(:project) }
+ let(:developer_project) { create(:project) }
+ let(:maintainer_project) { create(:project) }
+
+ describe '#execute' do
+ context 'filter' do
+ it 'does not return projects under Gitlab::Access::REPORTER' do
+ guest_project.add_guest(user)
+
+ finder = described_class.new(user, project_id: project.id)
+
+ expect(finder.execute).to be_empty
+ end
+
+ it 'returns projects equal or above Gitlab::Access::REPORTER ordered by id in descending order' do
+ reporter_project.add_reporter(user)
+ developer_project.add_developer(user)
+ maintainer_project.add_maintainer(user)
+
+ finder = described_class.new(user, project_id: project.id)
+
+ expect(finder.execute.to_a).to eq([maintainer_project, developer_project, reporter_project])
+ end
+
+ it 'does not include the source project' do
+ project.add_reporter(user)
+
+ finder = described_class.new(user, project_id: project.id)
+
+ expect(finder.execute.to_a).to be_empty
+ end
+
+ it 'does not return archived projects' do
+ reporter_project.add_reporter(user)
+ ::Projects::UpdateService.new(reporter_project, user, archived: true).execute
+ other_reporter_project = create(:project)
+ other_reporter_project.add_reporter(user)
+
+ finder = described_class.new(user, project_id: project.id)
+
+ expect(finder.execute.to_a).to eq([other_reporter_project])
+ end
+
+ it 'does not return projects for which issues are disabled' do
+ reporter_project.add_reporter(user)
+ reporter_project.update(issues_enabled: false)
+ other_reporter_project = create(:project)
+ other_reporter_project.add_reporter(user)
+
+ finder = described_class.new(user, project_id: project.id)
+
+ expect(finder.execute.to_a).to eq([other_reporter_project])
+ end
+
+ it 'returns a page of projects ordered by id in descending order' do
+ allow(Kaminari.config).to receive(:default_per_page).and_return(2)
+
+ projects = create_list(:project, 2) do |project|
+ project.add_developer(user)
+ end
+
+ finder = described_class.new(user, project_id: project.id)
+ page = finder.execute.to_a
+
+ expect(page.length).to eq(Kaminari.config.default_per_page)
+ expect(page[0]).to eq(projects.last)
+ end
+
+ it 'returns projects after the given offset id' do
+ reporter_project.add_reporter(user)
+ developer_project.add_developer(user)
+ maintainer_project.add_maintainer(user)
+
+ expect(described_class.new(user, project_id: project.id, offset_id: maintainer_project.id).execute.to_a)
+ .to eq([developer_project, reporter_project])
+
+ expect(described_class.new(user, project_id: project.id, offset_id: developer_project.id).execute.to_a)
+ .to eq([reporter_project])
+
+ expect(described_class.new(user, project_id: project.id, offset_id: reporter_project.id).execute.to_a)
+ .to be_empty
+ end
+ end
+
+ context 'search' do
+ it 'returns projects matching a search query' do
+ foo_project = create(:project)
+ foo_project.add_maintainer(user)
+
+ wadus_project = create(:project, name: 'wadus')
+ wadus_project.add_maintainer(user)
+
+ expect(described_class.new(user, project_id: project.id).execute.to_a)
+ .to eq([wadus_project, foo_project])
+
+ expect(described_class.new(user, project_id: project.id, search: 'wadus').execute.to_a)
+ .to eq([wadus_project])
+ end
+ end
+ end
+end
diff --git a/spec/finders/autocomplete/project_finder_spec.rb b/spec/finders/autocomplete/project_finder_spec.rb
new file mode 100644
index 00000000000..207d0598c28
--- /dev/null
+++ b/spec/finders/autocomplete/project_finder_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Autocomplete::ProjectFinder do
+ let(:user) { create(:user) }
+
+ describe '#execute' do
+ context 'without a project ID' do
+ it 'returns nil' do
+ expect(described_class.new(user).execute).to be_nil
+ end
+ end
+
+ context 'with an empty String as the project ID' do
+ it 'returns nil' do
+ expect(described_class.new(user, project_id: '').execute).to be_nil
+ end
+ end
+
+ context 'with a project ID' do
+ it 'raises ActiveRecord::RecordNotFound if the project does not exist' do
+ finder = described_class.new(user, project_id: 1)
+
+ expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises ActiveRecord::RecordNotFound if the user can not read the project' do
+ project = create(:project, :private)
+
+ finder = described_class.new(user, project_id: project.id)
+
+ expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises ActiveRecord::RecordNotFound if an anonymous user can not read the project' do
+ project = create(:project, :private)
+
+ finder = described_class.new(nil, project_id: project.id)
+
+ expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'returns the project if it exists and is readable' do
+ project = create(:project, :private)
+
+ project.add_maintainer(user)
+
+ finder = described_class.new(user, project_id: project.id)
+
+ expect(finder.execute).to eq(project)
+ end
+ end
+ end
+end
diff --git a/spec/finders/autocomplete/users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb
new file mode 100644
index 00000000000..abd0d6b5185
--- /dev/null
+++ b/spec/finders/autocomplete/users_finder_spec.rb
@@ -0,0 +1,112 @@
+require 'spec_helper'
+
+describe Autocomplete::UsersFinder do
+ describe '#execute' do
+ let!(:user1) { create(:user, username: 'johndoe') }
+ let!(:user2) { create(:user, :blocked, username: 'notsorandom') }
+ let!(:external_user) { create(:user, :external) }
+ let!(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
+ let(:current_user) { create(:user) }
+ let(:params) { {} }
+
+ let(:project) { nil }
+ let(:group) { nil }
+
+ subject { described_class.new(params: params, current_user: current_user, project: project, group: group).execute.to_a }
+
+ context 'when current_user not passed or nil' do
+ let(:current_user) { nil }
+
+ it { is_expected.to match_array([]) }
+ end
+
+ context 'when project passed' do
+ let(:project) { create(:project) }
+
+ it { is_expected.to match_array([project.owner]) }
+
+ context 'when author_id passed' do
+ let(:params) { { author_id: user2.id } }
+
+ it { is_expected.to match_array([project.owner, user2]) }
+ end
+ end
+
+ context 'when group passed and project not passed' do
+ let(:group) { create(:group, :public) }
+
+ before do
+ group.add_users([user1], GroupMember::DEVELOPER)
+ end
+
+ it { is_expected.to match_array([user1]) }
+ end
+
+ context 'when passed a subgroup', :nested_groups do
+ let(:grandparent) { create(:group, :public) }
+ let(:parent) { create(:group, :public, parent: grandparent) }
+ let(:child) { create(:group, :public, parent: parent) }
+ let(:group) { parent }
+
+ let!(:grandparent_user) { create(:group_member, :developer, group: grandparent).user }
+ let!(:parent_user) { create(:group_member, :developer, group: parent).user }
+ let!(:child_user) { create(:group_member, :developer, group: child).user }
+
+ it 'includes users from parent groups as well' do
+ expect(subject).to match_array([grandparent_user, parent_user])
+ end
+ end
+
+ it { is_expected.to match_array([user1, external_user, omniauth_user, current_user]) }
+
+ context 'when filtered by search' do
+ let(:params) { { search: 'johndoe' } }
+
+ it { is_expected.to match_array([user1]) }
+ end
+
+ context 'when filtered by skip_users' do
+ let(:params) { { skip_users: [omniauth_user.id, current_user.id] } }
+
+ it { is_expected.to match_array([user1, external_user]) }
+ end
+
+ context 'when todos exist' do
+ let!(:pending_todo1) { create(:todo, user: current_user, author: user1, state: :pending) }
+ let!(:pending_todo2) { create(:todo, user: external_user, author: omniauth_user, state: :pending) }
+ let!(:done_todo1) { create(:todo, user: current_user, author: external_user, state: :done) }
+ let!(:done_todo2) { create(:todo, user: user1, author: external_user, state: :done) }
+
+ context 'when filtered by todo_filter without todo_state_filter' do
+ let(:params) { { todo_filter: true } }
+
+ it { is_expected.to match_array([]) }
+ end
+
+ context 'when filtered by todo_filter with pending todo_state_filter' do
+ let(:params) { { todo_filter: true, todo_state_filter: 'pending' } }
+
+ it { is_expected.to match_array([user1]) }
+ end
+
+ context 'when filtered by todo_filter with done todo_state_filter' do
+ let(:params) { { todo_filter: true, todo_state_filter: 'done' } }
+
+ it { is_expected.to match_array([external_user]) }
+ end
+ end
+
+ context 'when filtered by current_user' do
+ let(:current_user) { user2 }
+ let(:params) { { current_user: true } }
+
+ it { is_expected.to match_array([user2, user1, external_user, omniauth_user]) }
+ end
+
+ context 'when filtered by author_id' do
+ let(:params) { { author_id: user2.id } }
+
+ it { is_expected.to match_array([user2, user1, external_user, omniauth_user, current_user]) }
+ end
+ end
+end
diff --git a/spec/finders/autocomplete_users_finder_spec.rb b/spec/finders/autocomplete_users_finder_spec.rb
deleted file mode 100644
index dcf9111776e..00000000000
--- a/spec/finders/autocomplete_users_finder_spec.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-require 'spec_helper'
-
-describe AutocompleteUsersFinder do
- describe '#execute' do
- let!(:user1) { create(:user, username: 'johndoe') }
- let!(:user2) { create(:user, :blocked, username: 'notsorandom') }
- let!(:external_user) { create(:user, :external) }
- let!(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
- let(:current_user) { create(:user) }
- let(:params) { {} }
-
- let(:project) { nil }
- let(:group) { nil }
-
- subject { described_class.new(params: params, current_user: current_user, project: project, group: group).execute.to_a }
-
- context 'when current_user not passed or nil' do
- let(:current_user) { nil }
-
- it { is_expected.to match_array([]) }
- end
-
- context 'when project passed' do
- let(:project) { create(:project) }
-
- it { is_expected.to match_array([project.owner]) }
-
- context 'when author_id passed' do
- let(:params) { { author_id: user2.id } }
-
- it { is_expected.to match_array([project.owner, user2]) }
- end
- end
-
- context 'when group passed and project not passed' do
- let(:group) { create(:group, :public) }
-
- before do
- group.add_users([user1], GroupMember::DEVELOPER)
- end
-
- it { is_expected.to match_array([user1]) }
- end
-
- context 'when passed a subgroup', :nested_groups do
- let(:grandparent) { create(:group, :public) }
- let(:parent) { create(:group, :public, parent: grandparent) }
- let(:child) { create(:group, :public, parent: parent) }
- let(:group) { parent }
-
- let!(:grandparent_user) { create(:group_member, :developer, group: grandparent).user }
- let!(:parent_user) { create(:group_member, :developer, group: parent).user }
- let!(:child_user) { create(:group_member, :developer, group: child).user }
-
- it 'includes users from parent groups as well' do
- expect(subject).to match_array([grandparent_user, parent_user])
- end
- end
-
- it { is_expected.to match_array([user1, external_user, omniauth_user, current_user]) }
-
- context 'when filtered by search' do
- let(:params) { { search: 'johndoe' } }
-
- it { is_expected.to match_array([user1]) }
- end
-
- context 'when filtered by skip_users' do
- let(:params) { { skip_users: [omniauth_user.id, current_user.id] } }
-
- it { is_expected.to match_array([user1, external_user]) }
- end
-
- context 'when todos exist' do
- let!(:pending_todo1) { create(:todo, user: current_user, author: user1, state: :pending) }
- let!(:pending_todo2) { create(:todo, user: external_user, author: omniauth_user, state: :pending) }
- let!(:done_todo1) { create(:todo, user: current_user, author: external_user, state: :done) }
- let!(:done_todo2) { create(:todo, user: user1, author: external_user, state: :done) }
-
- context 'when filtered by todo_filter without todo_state_filter' do
- let(:params) { { todo_filter: true } }
-
- it { is_expected.to match_array([]) }
- end
-
- context 'when filtered by todo_filter with pending todo_state_filter' do
- let(:params) { { todo_filter: true, todo_state_filter: 'pending' } }
-
- it { is_expected.to match_array([user1]) }
- end
-
- context 'when filtered by todo_filter with done todo_state_filter' do
- let(:params) { { todo_filter: true, todo_state_filter: 'done' } }
-
- it { is_expected.to match_array([external_user]) }
- end
- end
-
- context 'when filtered by current_user' do
- let(:current_user) { user2 }
- let(:params) { { current_user: true } }
-
- it { is_expected.to match_array([user2, user1, external_user, omniauth_user]) }
- end
-
- context 'when filtered by author_id' do
- let(:params) { { author_id: user2.id } }
-
- it { is_expected.to match_array([user2, user1, external_user, omniauth_user, current_user]) }
- end
- end
-end
diff --git a/spec/finders/awarded_emoji_finder_spec.rb b/spec/finders/awarded_emoji_finder_spec.rb
new file mode 100644
index 00000000000..d4479df7418
--- /dev/null
+++ b/spec/finders/awarded_emoji_finder_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe AwardedEmojiFinder do
+ describe '#execute' do
+ it 'returns an Array containing the awarded emoji names' do
+ user = create(:user)
+
+ create(:award_emoji, user: user, name: 'thumbsup')
+ create(:award_emoji, user: user, name: 'thumbsup')
+ create(:award_emoji, user: user, name: 'thumbsdown')
+
+ awarded = described_class.new(user).execute
+
+ expect(awarded).to eq([{ name: 'thumbsup' }, { name: 'thumbsdown' }])
+ end
+
+ it 'returns an empty Array when no user is given' do
+ awarded = described_class.new.execute
+
+ expect(awarded).to be_empty
+ end
+ end
+end
diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb
index 9e3f2c69606..7d164539d9a 100644
--- a/spec/finders/branches_finder_spec.rb
+++ b/spec/finders/branches_finder_spec.rb
@@ -66,7 +66,7 @@ describe BranchesFinder do
context 'filter and sort' do
it 'filters branches by name and sorts by recently_updated' do
- params = { sort: 'updated_desc', search: 'feature' }
+ params = { sort: 'updated_desc', search: 'feat' }
branches_finder = described_class.new(repository, params)
result = branches_finder.execute
@@ -75,6 +75,17 @@ describe BranchesFinder do
expect(result.count).to eq(2)
end
+ it 'filters branches by name and sorts by recently_updated, with exact matches first' do
+ params = { sort: 'updated_desc', search: 'feature' }
+ branches_finder = described_class.new(repository, params)
+
+ result = branches_finder.execute
+
+ expect(result.first.name).to eq('feature')
+ expect(result.second.name).to eq('feature_conflict')
+ expect(result.count).to eq(2)
+ end
+
it 'filters branches by name and sorts by last_updated' do
params = { sort: 'updated_asc', search: 'feature' }
branches_finder = described_class.new(repository, params)
@@ -84,6 +95,26 @@ describe BranchesFinder do
expect(result.first.name).to eq('feature')
expect(result.count).to eq(2)
end
+
+ it 'filters branches by name that begins with' do
+ params = { search: '^feature_' }
+ branches_finder = described_class.new(repository, params)
+
+ result = branches_finder.execute
+
+ expect(result.first.name).to eq('feature_conflict')
+ expect(result.count).to eq(1)
+ end
+
+ it 'filters branches by name that ends with' do
+ params = { search: 'feature$' }
+ branches_finder = described_class.new(repository, params)
+
+ result = branches_finder.execute
+
+ expect(result.first.name).to eq('feature')
+ expect(result.count).to eq(1)
+ end
end
end
end
diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb
index 9155a8d6fe9..81fb4e3561c 100644
--- a/spec/finders/contributed_projects_finder_spec.rb
+++ b/spec/finders/contributed_projects_finder_spec.rb
@@ -8,6 +8,7 @@ describe ContributedProjectsFinder do
let!(:public_project) { create(:project, :public) }
let!(:private_project) { create(:project, :private) }
+ let!(:internal_project) { create(:project, :internal) }
before do
private_project.add_maintainer(source_user)
@@ -16,17 +17,18 @@ describe ContributedProjectsFinder do
create(:push_event, project: public_project, author: source_user)
create(:push_event, project: private_project, author: source_user)
+ create(:push_event, project: internal_project, author: source_user)
end
- describe 'without a current user' do
+ describe 'activity without a current user' do
subject { finder.execute }
- it { is_expected.to eq([public_project]) }
+ it { is_expected.to match_array([public_project]) }
end
- describe 'with a current user' do
+ describe 'activity with a current user' do
subject { finder.execute(current_user) }
- it { is_expected.to eq([private_project, public_project]) }
+ it { is_expected.to match_array([private_project, internal_project, public_project]) }
end
end
diff --git a/spec/finders/environments_finder_spec.rb b/spec/finders/environments_finder_spec.rb
index 3cd421f22eb..25835bb4d94 100644
--- a/spec/finders/environments_finder_spec.rb
+++ b/spec/finders/environments_finder_spec.rb
@@ -12,7 +12,7 @@ describe EnvironmentsFinder do
context 'tagged deployment' do
before do
- create(:deployment, environment: environment, ref: 'v1.1.0', tag: true, sha: project.commit.id)
+ create(:deployment, :success, environment: environment, ref: 'v1.1.0', tag: true, sha: project.commit.id)
end
it 'returns environment when with_tags is set' do
@@ -33,7 +33,7 @@ describe EnvironmentsFinder do
context 'branch deployment' do
before do
- create(:deployment, environment: environment, ref: 'master', sha: project.commit.id)
+ create(:deployment, :success, environment: environment, ref: 'master', sha: project.commit.id)
end
it 'returns environment when ref is set' do
@@ -59,7 +59,7 @@ describe EnvironmentsFinder do
context 'commit deployment' do
before do
- create(:deployment, environment: environment, ref: 'master', sha: project.commit.id)
+ create(:deployment, :success, environment: environment, ref: 'master', sha: project.commit.id)
end
it 'returns environment' do
@@ -71,7 +71,7 @@ describe EnvironmentsFinder do
context 'recently updated' do
context 'when last deployment to environment is the most recent one' do
before do
- create(:deployment, environment: environment, ref: 'feature')
+ create(:deployment, :success, environment: environment, ref: 'feature')
end
it 'finds recently updated environment' do
@@ -82,8 +82,8 @@ describe EnvironmentsFinder do
context 'when last deployment to environment is not the most recent' do
before do
- create(:deployment, environment: environment, ref: 'feature')
- create(:deployment, environment: environment, ref: 'master')
+ create(:deployment, :success, environment: environment, ref: 'feature')
+ create(:deployment, :success, environment: environment, ref: 'master')
end
it 'does not find environment' do
@@ -96,8 +96,8 @@ describe EnvironmentsFinder do
let(:second_environment) { create(:environment, project: project) }
before do
- create(:deployment, environment: environment, ref: 'feature')
- create(:deployment, environment: second_environment, ref: 'feature')
+ create(:deployment, :success, environment: environment, ref: 'feature')
+ create(:deployment, :success, environment: second_environment, ref: 'feature')
end
it 'finds both environments' do
diff --git a/spec/finders/fork_projects_finder_spec.rb b/spec/finders/fork_projects_finder_spec.rb
index f0cef7ea406..b3fdffc3331 100644
--- a/spec/finders/fork_projects_finder_spec.rb
+++ b/spec/finders/fork_projects_finder_spec.rb
@@ -1,20 +1,21 @@
require 'spec_helper'
describe ForkProjectsFinder do
- let(:source_project) { create(:project, :empty_repo) }
- let(:private_fork) { create(:project, :private, :empty_repo, name: 'A') }
- let(:internal_fork) { create(:project, :internal, :empty_repo, name: 'B') }
- let(:public_fork) { create(:project, :public, :empty_repo, name: 'C') }
+ include ProjectForksHelper
+
+ let(:source_project) { create(:project, :public, :empty_repo) }
+ let(:private_fork) { fork_project(source_project, nil, name: 'A') }
+ let(:internal_fork) { fork_project(source_project, nil, name: 'B') }
+ let(:public_fork) { fork_project(source_project, nil, name: 'C') }
let(:non_member) { create(:user) }
let(:private_fork_member) { create(:user) }
before do
+ private_fork.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
private_fork.add_developer(private_fork_member)
- source_project.forks << private_fork
- source_project.forks << internal_fork
- source_project.forks << public_fork
+ internal_fork.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
describe '#execute' do
diff --git a/spec/finders/group_descendants_finder_spec.rb b/spec/finders/group_descendants_finder_spec.rb
index 796d40cb625..c28fd7cad11 100644
--- a/spec/finders/group_descendants_finder_spec.rb
+++ b/spec/finders/group_descendants_finder_spec.rb
@@ -74,6 +74,13 @@ describe GroupDescendantsFinder do
end
end
+ it 'sorts elements by latest created as default' do
+ project1 = create(:project, namespace: group, created_at: 1.hour.ago)
+ project2 = create(:project, namespace: group)
+
+ expect(subject.execute).to eq([project2, project1])
+ end
+
context 'sorting by name' do
let!(:project1) { create(:project, namespace: group, name: 'a', path: 'project-a') }
let!(:project2) { create(:project, namespace: group, name: 'z', path: 'project-z') }
@@ -108,6 +115,15 @@ describe GroupDescendantsFinder do
end
end
end
+
+ it 'does not include projects shared with the group' do
+ project = create(:project, namespace: group)
+ other_project = create(:project)
+ other_project.project_group_links.create(group: group,
+ group_access: ProjectGroupLink::MASTER)
+
+ expect(finder.execute).to contain_exactly(project)
+ end
end
context 'with nested groups', :nested_groups do
diff --git a/spec/finders/group_labels_finder_spec.rb b/spec/finders/group_labels_finder_spec.rb
new file mode 100644
index 00000000000..7bdd312eff0
--- /dev/null
+++ b/spec/finders/group_labels_finder_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GroupLabelsFinder, '#execute' do
+ let!(:group) { create(:group) }
+ let!(:user) { create(:user) }
+ let!(:label1) { create(:group_label, title: 'Foo', description: 'Lorem ipsum', group: group) }
+ let!(:label2) { create(:group_label, title: 'Bar', description: 'Fusce consequat', group: group) }
+
+ it 'returns all group labels sorted by name if no params' do
+ result = described_class.new(user, group).execute
+
+ expect(result.to_a).to match_array([label2, label1])
+ end
+
+ it 'returns all group labels sorted by name desc' do
+ result = described_class.new(user, group, sort: 'name_desc').execute
+
+ expect(result.to_a).to match_array([label2, label1])
+ end
+
+ it 'returns group labels that match search' do
+ result = described_class.new(user, group, search: 'Foo').execute
+
+ expect(result.to_a).to match_array([label1])
+ end
+
+ it 'returns group labels user subscribed to' do
+ label2.subscribe(user)
+
+ result = described_class.new(user, group, subscribed: 'true').execute
+
+ expect(result.to_a).to match_array([label2])
+ end
+
+ it 'returns second page of labels' do
+ result = described_class.new(user, group, page: '2').execute
+
+ expect(result.to_a).to match_array([])
+ end
+end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 07a2fa86dd7..c0488c83bd8 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -56,6 +56,40 @@ describe IssuesFinder do
end
end
+ context 'filtering by no assignee' do
+ let(:params) { { assignee_id: 'None' } }
+
+ it 'returns issues not assigned to any assignee' do
+ expect(issues).to contain_exactly(issue4)
+ end
+
+ it 'returns issues not assigned to any assignee' do
+ params[:assignee_id] = 0
+
+ expect(issues).to contain_exactly(issue4)
+ end
+
+ it 'returns issues not assigned to any assignee' do
+ params[:assignee_id] = 'none'
+
+ expect(issues).to contain_exactly(issue4)
+ end
+ end
+
+ context 'filtering by any assignee' do
+ let(:params) { { assignee_id: 'Any' } }
+
+ it 'returns issues assigned to any assignee' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3)
+ end
+
+ it 'returns issues assigned to any assignee' do
+ params[:assignee_id] = 'any'
+
+ expect(issues).to contain_exactly(issue1, issue2, issue3)
+ end
+ end
+
context 'filtering by group_id' do
let(:params) { { group_id: group.id } }
@@ -110,11 +144,31 @@ describe IssuesFinder do
end
context 'filtering by no milestone' do
- let(:params) { { milestone_title: Milestone::None.title } }
+ let(:params) { { milestone_title: 'None' } }
it 'returns issues with no milestone' do
expect(issues).to contain_exactly(issue2, issue3, issue4)
end
+
+ it 'returns issues with no milestone (deprecated)' do
+ params[:milestone_title] = Milestone::None.title
+
+ expect(issues).to contain_exactly(issue2, issue3, issue4)
+ end
+ end
+
+ context 'filtering by any milestone' do
+ let(:params) { { milestone_title: 'Any' } }
+
+ it 'returns issues with any assigned milestone' do
+ expect(issues).to contain_exactly(issue1)
+ end
+
+ it 'returns issues with any assigned milestone (deprecated)' do
+ params[:milestone_title] = Milestone::Any.title
+
+ expect(issues).to contain_exactly(issue1)
+ end
end
context 'filtering by upcoming milestone' do
@@ -318,6 +372,22 @@ describe IssuesFinder do
end
context 'filtering by reaction name' do
+ context 'user searches by no reaction' do
+ let(:params) { { my_reaction_emoji: 'None' } }
+
+ it 'returns issues that the user did not react to' do
+ expect(issues).to contain_exactly(issue2, issue4)
+ end
+ end
+
+ context 'user searches by any reaction' do
+ let(:params) { { my_reaction_emoji: 'Any' } }
+
+ it 'returns issues that the user reacted to' do
+ expect(issues).to contain_exactly(issue1, issue3)
+ end
+ end
+
context 'user searches by "thumbsup" reaction' do
let(:params) { { my_reaction_emoji: 'thumbsup' } }
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index f5cec8e349a..9abc52aa664 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -210,5 +210,15 @@ describe LabelsFinder do
expect(finder.execute).to eq [project_label_1]
end
end
+
+ context 'filter by subscription' do
+ it 'returns labels user subscribed to' do
+ project_label_1.subscribe(user)
+
+ finder = described_class.new(user, subscribed: 'true')
+
+ expect(finder.execute).to eq [project_label_1]
+ end
+ end
end
end
diff --git a/spec/finders/license_template_finder_spec.rb b/spec/finders/license_template_finder_spec.rb
new file mode 100644
index 00000000000..f6f40bf33cc
--- /dev/null
+++ b/spec/finders/license_template_finder_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe LicenseTemplateFinder do
+ describe '#execute' do
+ subject(:result) { described_class.new(nil, params).execute }
+
+ let(:categories) { categorised_licenses.keys }
+ let(:categorised_licenses) { result.group_by(&:category) }
+
+ context 'popular: true' do
+ let(:params) { { popular: true } }
+
+ it 'only returns popular licenses' do
+ expect(categories).to contain_exactly(:Popular)
+ expect(categorised_licenses[:Popular]).to be_present
+ end
+ end
+
+ context 'popular: false' do
+ let(:params) { { popular: false } }
+
+ it 'only returns unpopular licenses' do
+ expect(categories).to contain_exactly(:Other)
+ expect(categorised_licenses[:Other]).to be_present
+ end
+ end
+
+ context 'popular: nil' do
+ let(:params) { { popular: nil } }
+
+ it 'returns all licenses known by the Licensee gem' do
+ from_licensee = Licensee::License.all.map { |l| l.key }
+
+ expect(result.map(&:key)).to match_array(from_licensee)
+ end
+
+ it 'correctly copies all attributes' do
+ licensee = Licensee::License.all.first
+ found = result.find { |r| r.key == licensee.key }
+
+ aggregate_failures do
+ %i[key name content nickname url meta featured?].each do |k|
+ expect(found.public_send(k)).to eq(licensee.public_send(k))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 35d0eeda8f6..ff4c6b8dd42 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -3,25 +3,47 @@ require 'spec_helper'
describe MergeRequestsFinder do
include ProjectForksHelper
+ # We need to explicitly permit Gitaly N+1s because of the specs that use
+ # :request_store. Gitaly N+1 detection is only enabled when :request_store is,
+ # but we don't care about potential N+1s when we're just creating several
+ # projects in the setup phase.
+ def create_project_without_n_plus_1(*args)
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ create(:project, :public, *args)
+ end
+ end
+
let(:user) { create :user }
let(:user2) { create :user }
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
- let(:project1) { create(:project, :public, group: group) }
- let(:project2) { fork_project(project1, user) }
+ let(:project1) { create_project_without_n_plus_1(group: group) }
+ let(:project2) do
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ fork_project(project1, user)
+ end
+ end
let(:project3) do
- p = fork_project(project1, user)
- p.update!(archived: true)
- p
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ p = fork_project(project1, user)
+ p.update!(archived: true)
+ p
+ end
end
- let(:project4) { create(:project, :public, group: subgroup) }
+ let(:project4) { create_project_without_n_plus_1(group: subgroup) }
+ let(:project5) { create_project_without_n_plus_1(group: subgroup) }
+ let(:project6) { create_project_without_n_plus_1(group: subgroup) }
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
let!(:merge_request2) { create(:merge_request, :conflict, author: user, source_project: project2, target_project: project1, state: 'closed') }
- let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, state: 'locked') }
- let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) }
- let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4) }
+ let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, state: 'locked', title: 'thing WIP thing') }
+ let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3, title: 'WIP thing') }
+ let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4, title: '[WIP]') }
+ let!(:merge_request6) { create(:merge_request, :simple, author: user, source_project: project5, target_project: project5, title: 'WIP: thing') }
+ let!(:merge_request7) { create(:merge_request, :simple, author: user, source_project: project6, target_project: project6, title: 'wip thing') }
+ let!(:merge_request8) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project1, title: '[wip] thing') }
+ let!(:merge_request9) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2, title: 'wip: thing') }
before do
project1.add_maintainer(user)
@@ -29,19 +51,21 @@ describe MergeRequestsFinder do
project3.add_developer(user)
project2.add_developer(user2)
project4.add_developer(user)
+ project5.add_developer(user)
+ project6.add_developer(user)
end
describe "#execute" do
it 'filters by scope' do
params = { scope: 'authored', state: 'opened' }
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(3)
+ expect(merge_requests.size).to eq(7)
end
it 'filters by project' do
params = { project_id: project1.id, scope: 'authored', state: 'opened' }
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(1)
+ expect(merge_requests.size).to eq(2)
end
it 'filters by group' do
@@ -49,7 +73,7 @@ describe MergeRequestsFinder do
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(2)
+ expect(merge_requests.size).to eq(3)
end
it 'filters by group including subgroups', :nested_groups do
@@ -57,13 +81,13 @@ describe MergeRequestsFinder do
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(3)
+ expect(merge_requests.size).to eq(6)
end
it 'filters by non_archived' do
params = { non_archived: true }
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(4)
+ expect(merge_requests.size).to eq(8)
end
it 'filters by iid' do
@@ -98,6 +122,36 @@ describe MergeRequestsFinder do
expect(merge_requests).to contain_exactly(merge_request3)
end
+ it 'filters by wip' do
+ params = { wip: 'yes' }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request4, merge_request5, merge_request6, merge_request7, merge_request8, merge_request9)
+ end
+
+ it 'filters by not wip' do
+ params = { wip: 'no' }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3)
+ end
+
+ it 'returns all items if no valid wip param exists' do
+ params = { wip: '' }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3, merge_request4, merge_request5, merge_request6, merge_request7, merge_request8, merge_request9)
+ end
+
+ it 'adds wip to scalar params' do
+ scalar_params = described_class.scalar_params
+
+ expect(scalar_params).to include(:wip, :assignee_id)
+ end
+
context 'filtering by group milestone' do
let!(:group) { create(:group, :public) }
let(:group_milestone) { create(:milestone, group: group) }
@@ -207,7 +261,7 @@ describe MergeRequestsFinder do
it 'returns the number of rows for the default state' do
finder = described_class.new(user)
- expect(finder.row_count).to eq(3)
+ expect(finder.row_count).to eq(7)
end
it 'returns the number of rows for a given state' do
diff --git a/spec/finders/move_to_project_finder_spec.rb b/spec/finders/move_to_project_finder_spec.rb
deleted file mode 100644
index 1b3f44cced1..00000000000
--- a/spec/finders/move_to_project_finder_spec.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-require 'spec_helper'
-
-describe MoveToProjectFinder do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
-
- let(:no_access_project) { create(:project) }
- let(:guest_project) { create(:project) }
- let(:reporter_project) { create(:project) }
- let(:developer_project) { create(:project) }
- let(:maintainer_project) { create(:project) }
-
- subject { described_class.new(user) }
-
- describe '#execute' do
- context 'filter' do
- it 'does not return projects under Gitlab::Access::REPORTER' do
- guest_project.add_guest(user)
-
- expect(subject.execute(project)).to be_empty
- end
-
- it 'returns projects equal or above Gitlab::Access::REPORTER ordered by id in descending order' do
- reporter_project.add_reporter(user)
- developer_project.add_developer(user)
- maintainer_project.add_maintainer(user)
-
- expect(subject.execute(project).to_a).to eq([maintainer_project, developer_project, reporter_project])
- end
-
- it 'does not include the source project' do
- project.add_reporter(user)
-
- expect(subject.execute(project).to_a).to be_empty
- end
-
- it 'does not return archived projects' do
- reporter_project.add_reporter(user)
- ::Projects::UpdateService.new(reporter_project, user, archived: true).execute
- other_reporter_project = create(:project)
- other_reporter_project.add_reporter(user)
-
- expect(subject.execute(project).to_a).to eq([other_reporter_project])
- end
-
- it 'does not return projects for which issues are disabled' do
- reporter_project.add_reporter(user)
- reporter_project.update(issues_enabled: false)
- other_reporter_project = create(:project)
- other_reporter_project.add_reporter(user)
-
- expect(subject.execute(project).to_a).to eq([other_reporter_project])
- end
-
- it 'returns a page of projects ordered by id in descending order' do
- stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
-
- reporter_project.add_reporter(user)
- developer_project.add_developer(user)
- maintainer_project.add_maintainer(user)
-
- expect(subject.execute(project).to_a).to eq([maintainer_project, developer_project])
- end
-
- it 'returns projects after the given offset id' do
- stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
-
- reporter_project.add_reporter(user)
- developer_project.add_developer(user)
- maintainer_project.add_maintainer(user)
-
- expect(subject.execute(project, search: nil, offset_id: maintainer_project.id).to_a).to eq([developer_project, reporter_project])
- expect(subject.execute(project, search: nil, offset_id: developer_project.id).to_a).to eq([reporter_project])
- expect(subject.execute(project, search: nil, offset_id: reporter_project.id).to_a).to be_empty
- end
- end
-
- context 'search' do
- it 'uses Project#search' do
- expect(user).to receive_message_chain(:projects_where_can_admin_issues, :search) { Project.all }
-
- subject.execute(project, search: 'wadus')
- end
-
- it 'returns projects matching a search query' do
- foo_project = create(:project)
- foo_project.add_maintainer(user)
-
- wadus_project = create(:project, name: 'wadus')
- wadus_project.add_maintainer(user)
-
- expect(subject.execute(project).to_a).to eq([wadus_project, foo_project])
- expect(subject.execute(project, search: 'wadus').to_a).to eq([wadus_project])
- end
- end
- end
-end
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index b776e9d856a..b51f1955ac4 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -9,6 +9,35 @@ describe NotesFinder do
end
describe '#execute' do
+ context 'when notes filter is present' do
+ let!(:comment) { create(:note_on_issue, project: project) }
+ let!(:system_note) { create(:note_on_issue, project: project, system: true) }
+
+ it 'returns only user notes when using only_comments filter' do
+ finder = described_class.new(project, user, notes_filter: UserPreference::NOTES_FILTERS[:only_comments])
+
+ notes = finder.execute
+
+ expect(notes).to match_array(comment)
+ end
+
+ it 'returns only system notes when using only_activity filters' do
+ finder = described_class.new(project, user, notes_filter: UserPreference::NOTES_FILTERS[:only_activity])
+
+ notes = finder.execute
+
+ expect(notes).to match_array(system_note)
+ end
+
+ it 'gets all notes' do
+ finder = described_class.new(project, user, notes_filter: UserPreference::NOTES_FILTERS[:all_activity])
+
+ notes = finder.execute
+
+ expect(notes).to match_array([comment, system_note])
+ end
+ end
+
it 'finds notes on merge requests' do
create(:note_on_merge_request, project: project)
diff --git a/spec/finders/pending_todos_finder_spec.rb b/spec/finders/pending_todos_finder_spec.rb
new file mode 100644
index 00000000000..b41b1b46a93
--- /dev/null
+++ b/spec/finders/pending_todos_finder_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PendingTodosFinder do
+ let(:user) { create(:user) }
+
+ describe '#execute' do
+ it 'returns only pending todos' do
+ create(:todo, :done, user: user)
+
+ todo = create(:todo, :pending, user: user)
+ todos = described_class.new(user).execute
+
+ expect(todos).to eq([todo])
+ end
+
+ it 'supports retrieving of todos for a specific project' do
+ project1 = create(:project)
+ project2 = create(:project)
+
+ create(:todo, :pending, user: user, project: project2)
+
+ todo = create(:todo, :pending, user: user, project: project1)
+ todos = described_class.new(user, project_id: project1.id).execute
+
+ expect(todos).to eq([todo])
+ end
+
+ it 'supports retrieving of todos for a specific todo target' do
+ issue = create(:issue)
+ note = create(:note)
+ todo = create(:todo, :pending, user: user, target: issue)
+
+ create(:todo, :pending, user: user, target: note)
+
+ todos = described_class.new(user, target_id: issue.id).execute
+
+ expect(todos).to eq([todo])
+ end
+
+ it 'supports retrieving of todos for a specific target type' do
+ issue = create(:issue)
+ note = create(:note)
+ todo = create(:todo, :pending, user: user, target: issue)
+
+ create(:todo, :pending, user: user, target: note)
+
+ todos = described_class.new(user, target_type: issue.class.name).execute
+
+ expect(todos).to eq([todo])
+ end
+
+ it 'supports retrieving of todos for a specific commit ID' do
+ create(:todo, :pending, user: user, commit_id: '456')
+
+ todo = create(:todo, :pending, user: user, commit_id: '123')
+ todos = described_class.new(user, commit_id: '123').execute
+
+ expect(todos).to eq([todo])
+ end
+ end
+end
diff --git a/spec/finders/personal_access_tokens_finder_spec.rb b/spec/finders/personal_access_tokens_finder_spec.rb
index 3f22b3a253d..3e849c9a644 100644
--- a/spec/finders/personal_access_tokens_finder_spec.rb
+++ b/spec/finders/personal_access_tokens_finder_spec.rb
@@ -92,7 +92,7 @@ describe PersonalAccessTokensFinder do
end
describe 'with id' do
- subject { finder(params).find_by(id: active_personal_access_token.id) }
+ subject { finder(params).find_by_id(active_personal_access_token.id) }
it { is_expected.to eq(active_personal_access_token) }
@@ -106,7 +106,7 @@ describe PersonalAccessTokensFinder do
end
describe 'with token' do
- subject { finder(params).find_by(token: active_personal_access_token.token) }
+ subject { finder(params).find_by_token(active_personal_access_token.token) }
it { is_expected.to eq(active_personal_access_token) }
@@ -207,7 +207,7 @@ describe PersonalAccessTokensFinder do
end
describe 'with id' do
- subject { finder(params).find_by(id: active_personal_access_token.id) }
+ subject { finder(params).find_by_id(active_personal_access_token.id) }
it { is_expected.to eq(active_personal_access_token) }
@@ -221,7 +221,7 @@ describe PersonalAccessTokensFinder do
end
describe 'with token' do
- subject { finder(params).find_by(token: active_personal_access_token.token) }
+ subject { finder(params).find_by_token(active_personal_access_token.token) }
it { is_expected.to eq(active_personal_access_token) }
diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb
index c6e832ad69b..c2c304589c9 100644
--- a/spec/finders/pipelines_finder_spec.rb
+++ b/spec/finders/pipelines_finder_spec.rb
@@ -225,7 +225,7 @@ describe PipelinesFinder do
end
end
- context 'when the project has limited access to piplines' do
+ context 'when the project has limited access to pipelines' do
let(:project) { create(:project, :private, :repository) }
let(:current_user) { create(:user) }
let!(:pipelines) { create_list(:ci_pipeline, 2, project: project) }
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index 7931ad9b9f0..590e838f13e 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -174,6 +174,13 @@ describe ProjectsFinder do
end
end
+ describe 'filter by without_deleted' do
+ let(:params) { { without_deleted: true } }
+ let!(:pending_delete_project) { create(:project, :public, pending_delete: true) }
+
+ it { is_expected.to match_array([public_project, internal_project]) }
+ end
+
describe 'sorting' do
let(:params) { { sort: 'name_asc' } }
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 1ae0bd988f2..dfeeb3040c6 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -4,16 +4,13 @@ describe SnippetsFinder do
include Gitlab::Allowable
using RSpec::Parameterized::TableSyntax
- context 'filter by visibility' do
- let!(:snippet1) { create(:personal_snippet, :private) }
- let!(:snippet2) { create(:personal_snippet, :internal) }
- let!(:snippet3) { create(:personal_snippet, :public) }
+ describe '#initialize' do
+ it 'raises ArgumentError when a project and author are given' do
+ user = build(:user)
+ project = build(:project)
- it "returns public snippets when visibility is PUBLIC" do
- snippets = described_class.new(nil, visibility: Snippet::PUBLIC).execute
-
- expect(snippets).to include(snippet3)
- expect(snippets).not_to include(snippet1, snippet2)
+ expect { described_class.new(user, author: user, project: project) }
+ .to raise_error(ArgumentError)
end
end
@@ -66,21 +63,21 @@ describe SnippetsFinder do
end
it "returns internal snippets" do
- snippets = described_class.new(user, author: user, visibility: Snippet::INTERNAL).execute
+ snippets = described_class.new(user, author: user, scope: :are_internal).execute
expect(snippets).to include(snippet2)
expect(snippets).not_to include(snippet1, snippet3)
end
it "returns private snippets" do
- snippets = described_class.new(user, author: user, visibility: Snippet::PRIVATE).execute
+ snippets = described_class.new(user, author: user, scope: :are_private).execute
expect(snippets).to include(snippet1)
expect(snippets).not_to include(snippet2, snippet3)
end
it "returns public snippets" do
- snippets = described_class.new(user, author: user, visibility: Snippet::PUBLIC).execute
+ snippets = described_class.new(user, author: user, scope: :are_public).execute
expect(snippets).to include(snippet3)
expect(snippets).not_to include(snippet1, snippet2)
@@ -98,6 +95,13 @@ describe SnippetsFinder do
expect(snippets).to include(snippet3)
expect(snippets).not_to include(snippet2, snippet1)
end
+
+ it 'returns all snippets for an admin' do
+ admin = create(:user, :admin)
+ snippets = described_class.new(admin, author: user).execute
+
+ expect(snippets).to include(snippet1, snippet2, snippet3)
+ end
end
context 'filter by project' do
@@ -126,21 +130,21 @@ describe SnippetsFinder do
end
it "returns public snippets for non project members" do
- snippets = described_class.new(user, project: project1, visibility: Snippet::PUBLIC).execute
+ snippets = described_class.new(user, project: project1, scope: :are_public).execute
expect(snippets).to include(@snippet3)
expect(snippets).not_to include(@snippet1, @snippet2)
end
it "returns internal snippets for non project members" do
- snippets = described_class.new(user, project: project1, visibility: Snippet::INTERNAL).execute
+ snippets = described_class.new(user, project: project1, scope: :are_internal).execute
expect(snippets).to include(@snippet2)
expect(snippets).not_to include(@snippet1, @snippet3)
end
it "does not return private snippets for non project members" do
- snippets = described_class.new(user, project: project1, visibility: Snippet::PRIVATE).execute
+ snippets = described_class.new(user, project: project1, scope: :are_private).execute
expect(snippets).not_to include(@snippet1, @snippet2, @snippet3)
end
@@ -156,10 +160,17 @@ describe SnippetsFinder do
it "returns private snippets for project members" do
project1.add_developer(user)
- snippets = described_class.new(user, project: project1, visibility: Snippet::PRIVATE).execute
+ snippets = described_class.new(user, project: project1, scope: :are_private).execute
expect(snippets).to include(@snippet1)
end
+
+ it 'returns all snippets for an admin' do
+ admin = create(:user, :admin)
+ snippets = described_class.new(admin, project: project1).execute
+
+ expect(snippets).to include(@snippet1, @snippet2, @snippet3)
+ end
end
describe '#execute' do
@@ -184,4 +195,6 @@ describe SnippetsFinder do
end
end
end
+
+ it_behaves_like 'snippet visibility'
end
diff --git a/spec/finders/template_finder_spec.rb b/spec/finders/template_finder_spec.rb
new file mode 100644
index 00000000000..114af9461e0
--- /dev/null
+++ b/spec/finders/template_finder_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe TemplateFinder do
+ using RSpec::Parameterized::TableSyntax
+
+ describe '#build' do
+ let(:project) { build_stubbed(:project) }
+
+ where(:type, :expected_class) do
+ :dockerfiles | described_class
+ :gitignores | described_class
+ :gitlab_ci_ymls | described_class
+ :licenses | ::LicenseTemplateFinder
+ end
+
+ with_them do
+ subject(:finder) { described_class.build(type, project) }
+
+ it { is_expected.to be_a(expected_class) }
+ it { expect(finder.project).to eq(project) }
+ end
+ end
+
+ describe '#execute' do
+ where(:type, :vendored_name) do
+ :dockerfiles | 'Binary'
+ :gitignores | 'Actionscript'
+ :gitlab_ci_ymls | 'Android'
+ end
+
+ with_them do
+ it 'returns all vendored templates when no name is specified' do
+ result = described_class.new(type, nil).execute
+
+ expect(result).to include(have_attributes(name: vendored_name))
+ end
+
+ it 'returns only the specified vendored template when a name is specified' do
+ result = described_class.new(type, nil, name: vendored_name).execute
+
+ expect(result).to have_attributes(name: vendored_name)
+ end
+
+ it 'returns nil when an unknown name is specified' do
+ result = described_class.new(type, nil, name: 'unknown').execute
+
+ expect(result).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index 7f7cfb2cb98..d4ed41d54f0 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -105,9 +105,24 @@ describe TodosFinder do
todos = finder.new(user, { sort: 'priority' }).execute
- puts todos.to_sql
expect(todos).to eq([todo_3, todo_5, todo_4, todo_2, todo_1])
end
end
end
+
+ describe '#any_for_target?' do
+ it 'returns true if there are any todos for the given target' do
+ todo = create(:todo, :pending)
+ finder = described_class.new(todo.user)
+
+ expect(finder.any_for_target?(todo.target)).to eq(true)
+ end
+
+ it 'returns false if there are no todos for the given target' do
+ issue = create(:issue)
+ finder = described_class.new(issue.author)
+
+ expect(finder.any_for_target?(issue)).to eq(false)
+ end
+ end
end
diff --git a/spec/finders/user_finder_spec.rb b/spec/finders/user_finder_spec.rb
new file mode 100644
index 00000000000..4771b878b8e
--- /dev/null
+++ b/spec/finders/user_finder_spec.rb
@@ -0,0 +1,179 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe UserFinder do
+ set(:user) { create(:user) }
+
+ describe '#find_by_id' do
+ context 'when the user exists' do
+ it 'returns the user' do
+ found = described_class.new(user.id).find_by_id
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user exists (id as string)' do
+ it 'returns the user' do
+ found = described_class.new(user.id.to_s).find_by_id
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user does not exist' do
+ it 'returns nil' do
+ found = described_class.new(1).find_by_id
+
+ expect(found).to be_nil
+ end
+ end
+ end
+
+ describe '#find_by_username' do
+ context 'when the user exists' do
+ it 'returns the user' do
+ found = described_class.new(user.username).find_by_username
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user does not exist' do
+ it 'returns nil' do
+ found = described_class.new("non_existent_username").find_by_username
+
+ expect(found).to be_nil
+ end
+ end
+ end
+
+ describe '#find_by_id_or_username' do
+ context 'when the user exists (id)' do
+ it 'returns the user' do
+ found = described_class.new(user.id).find_by_id_or_username
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user exists (id as string)' do
+ it 'returns the user' do
+ found = described_class.new(user.id.to_s).find_by_id_or_username
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user exists (username)' do
+ it 'returns the user' do
+ found = described_class.new(user.username).find_by_id_or_username
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user does not exist (username)' do
+ it 'returns nil' do
+ found = described_class.new("non_existent_username").find_by_id_or_username
+
+ expect(found).to be_nil
+ end
+ end
+
+ context 'when the user does not exist' do
+ it 'returns nil' do
+ found = described_class.new(1).find_by_id_or_username
+
+ expect(found).to be_nil
+ end
+ end
+ end
+
+ describe '#find_by_id!' do
+ context 'when the user exists' do
+ it 'returns the user' do
+ found = described_class.new(user.id).find_by_id!
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user exists (id as string)' do
+ it 'returns the user' do
+ found = described_class.new(user.id.to_s).find_by_id!
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user does not exist' do
+ it 'raises ActiveRecord::RecordNotFound' do
+ finder = described_class.new(1)
+
+ expect { finder.find_by_id! }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
+
+ describe '#find_by_username!' do
+ context 'when the user exists' do
+ it 'returns the user' do
+ found = described_class.new(user.username).find_by_username!
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user does not exist' do
+ it 'raises ActiveRecord::RecordNotFound' do
+ finder = described_class.new("non_existent_username")
+
+ expect { finder.find_by_username! }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
+
+ describe '#find_by_id_or_username!' do
+ context 'when the user exists (id)' do
+ it 'returns the user' do
+ found = described_class.new(user.id).find_by_id_or_username!
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user exists (id as string)' do
+ it 'returns the user' do
+ found = described_class.new(user.id.to_s).find_by_id_or_username!
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user exists (username)' do
+ it 'returns the user' do
+ found = described_class.new(user.username).find_by_id_or_username!
+
+ expect(found).to eq(user)
+ end
+ end
+
+ context 'when the user does not exist (username)' do
+ it 'raises ActiveRecord::RecordNotFound' do
+ finder = described_class.new("non_existent_username")
+
+ expect { finder.find_by_id_or_username! }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context 'when the user does not exist' do
+ it 'raises ActiveRecord::RecordNotFound' do
+ finder = described_class.new(1)
+
+ expect { finder.find_by_id_or_username! }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
+end
diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb
index 58470f4c84d..c5fcd68eb4c 100644
--- a/spec/finders/user_recent_events_finder_spec.rb
+++ b/spec/finders/user_recent_events_finder_spec.rb
@@ -13,49 +13,25 @@ describe UserRecentEventsFinder do
subject(:finder) { described_class.new(current_user, project_owner) }
describe '#execute' do
- context 'current user does not have access to projects' do
- it 'returns public and internal events' do
- records = finder.execute
-
- expect(records).to include(public_event, internal_event)
- expect(records).not_to include(private_event)
+ context 'when profile is public' do
+ it 'returns all the events' do
+ expect(finder.execute).to include(private_event, internal_event, public_event)
end
end
- context 'when current user has access to the projects' do
- before do
- private_project.add_developer(current_user)
- internal_project.add_developer(current_user)
- public_project.add_developer(current_user)
- end
-
- context 'when profile is public' do
- it 'returns all the events' do
- expect(finder.execute).to include(private_event, internal_event, public_event)
- end
- end
-
- context 'when profile is private' do
- it 'returns no event' do
- allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false)
- expect(finder.execute).to be_empty
- end
- end
+ context 'when profile is private' do
+ it 'returns no event' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false)
- it 'does not include the events if the user cannot read cross project' do
- expect(Ability).to receive(:allowed?).and_call_original
- expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false }
expect(finder.execute).to be_empty
end
end
- context 'when current user is anonymous' do
- let(:current_user) { nil }
-
- it 'returns public events only' do
- expect(finder.execute).to eq([public_event])
- end
+ it 'does not include the events if the user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false }
+ expect(finder.execute).to be_empty
end
end
end
diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb
index 4249c52c481..fecf97dc641 100644
--- a/spec/finders/users_finder_spec.rb
+++ b/spec/finders/users_finder_spec.rb
@@ -22,6 +22,12 @@ describe UsersFinder do
expect(users).to contain_exactly(user1)
end
+ it 'filters by username (case insensitive)' do
+ users = described_class.new(user, username: 'joHNdoE').execute
+
+ expect(users).to contain_exactly(user1)
+ end
+
it 'filters by search' do
users = described_class.new(user, search: 'orando').execute
diff --git a/spec/finders/users_with_pending_todos_finder_spec.rb b/spec/finders/users_with_pending_todos_finder_spec.rb
new file mode 100644
index 00000000000..fa15355531c
--- /dev/null
+++ b/spec/finders/users_with_pending_todos_finder_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe UsersWithPendingTodosFinder do
+ describe '#execute' do
+ it 'returns the users for all pending todos of a target' do
+ issue = create(:issue)
+ note = create(:note)
+ todo = create(:todo, :pending, target: issue)
+
+ create(:todo, :pending, target: note)
+
+ users = described_class.new(issue).execute
+
+ expect(users).to eq([todo.user])
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/ci_detailed_status.json b/spec/fixtures/api/schemas/ci_detailed_status.json
deleted file mode 100644
index 01e34249bf1..00000000000
--- a/spec/fixtures/api/schemas/ci_detailed_status.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "type": "object",
- "required" : [
- "icon",
- "text",
- "label",
- "group",
- "tooltip",
- "has_details",
- "details_path",
- "favicon"
- ],
- "properties": {
- "icon": { "type": "string" },
- "text": { "type": "string" },
- "label": { "type": "string" },
- "group": { "type": "string" },
- "tooltip": { "type": "string" },
- "has_details": { "type": "boolean" },
- "details_path": { "type": "string" },
- "favicon": { "type": "string" }
- },
- "additionalProperties": false
-}
diff --git a/spec/fixtures/api/schemas/deployment.json b/spec/fixtures/api/schemas/deployment.json
index 536e6475c23..0828f113495 100644
--- a/spec/fixtures/api/schemas/deployment.json
+++ b/spec/fixtures/api/schemas/deployment.json
@@ -1,45 +1,58 @@
{
- "additionalProperties": false,
- "properties": {
- "created_at": {
- "type": "string"
- },
- "id": {
- "type": "integer"
- },
- "iid": {
- "type": "integer"
- },
- "last?": {
- "type": "boolean"
- },
- "ref": {
- "additionalProperties": false,
- "properties": {
- "name": {
- "type": "string"
- }
- },
- "required": [
- "name"
- ],
- "type": "object"
- },
- "sha": {
- "type": "string"
- },
- "tag": {
- "type": "boolean"
- }
+ "type": "object",
+ "required": [
+ "sha",
+ "created_at",
+ "iid",
+ "tag",
+ "last?",
+ "ref",
+ "id"
+ ],
+ "properties": {
+ "created_at": { "type": "string" },
+ "id": { "type": "integer" },
+ "iid": { "type": "integer" },
+ "last?": { "type": "boolean" },
+ "ref": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": { "type": "string" },
+ "ref_path": { "type": "string" }
+ },
+ "additionalProperties": false
},
- "required": [
- "sha",
- "created_at",
- "iid",
- "tag",
- "last?",
- "ref",
- "id"
- ],
- "type": "object"
+ "sha": { "type": "string" },
+ "tag": { "type": "boolean" },
+ "user": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "entities/user.json" }
+ ]
+ },
+ "commit": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "entities/commit.json" }
+ ]
+ },
+ "deployable": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "job/job.json" }
+ ]
+ },
+ "manual_actions": {
+ "type": "array",
+ "items": { "$ref": "job/job.json" }
+ },
+ "scheduled_actions": {
+ "type": "array",
+ "items": { "$ref": "job/job.json" }
+ }
+ },
+ "additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/entities/commit.json b/spec/fixtures/api/schemas/entities/commit.json
new file mode 100644
index 00000000000..324702e3f94
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/commit.json
@@ -0,0 +1,26 @@
+{
+ "type": "object",
+ "allOf": [
+ { "$ref": "../public_api/v4/commit/basic.json" },
+ {
+ "type": "object",
+ "required": [
+ "author_gravatar_url",
+ "commit_url",
+ "commit_path",
+ "author"
+ ],
+ "properties": {
+ "author_gravatar_url": { "type": "string" },
+ "commit_url": { "type": "string" },
+ "commit_path": { "type": "string" },
+ "author": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "user.json" }
+ ]
+ }
+ }
+ }
+ ]
+}
diff --git a/spec/fixtures/api/schemas/entities/diff_line.json b/spec/fixtures/api/schemas/entities/diff_line.json
new file mode 100644
index 00000000000..66e8b443e1b
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/diff_line.json
@@ -0,0 +1,14 @@
+{
+ "type": "object",
+ "required": ["type"],
+ "properties": {
+ "line_code": { "type": ["string", "null"] },
+ "type": { "type": ["string", "null"] },
+ "old_line": { "type": ["integer", "null"] },
+ "new_line": { "type": ["integer", "null"] },
+ "text": { "type": ["string"] },
+ "rich_text": { "type": ["string"] },
+ "meta_data": { "type": ["object", "null"] }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/diff_line_parallel.json b/spec/fixtures/api/schemas/entities/diff_line_parallel.json
new file mode 100644
index 00000000000..f924eb0c601
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/diff_line_parallel.json
@@ -0,0 +1,11 @@
+{
+ "required" : [
+ "left",
+ "right"
+ ],
+ "properties" : {
+ "left": { "$ref": "diff_line.json" },
+ "right": { "$ref": "diff_line.json" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/diff_viewer.json b/spec/fixtures/api/schemas/entities/diff_viewer.json
new file mode 100644
index 00000000000..19780f49a88
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/diff_viewer.json
@@ -0,0 +1,8 @@
+{
+ "type": "object",
+ "required": ["name"],
+ "properties": {
+ "name": { "type": ["string"] }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/issue_board.json b/spec/fixtures/api/schemas/entities/issue_board.json
new file mode 100644
index 00000000000..3e252ddd13c
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/issue_board.json
@@ -0,0 +1,39 @@
+{
+ "type": "object",
+ "properties" : {
+ "id": { "type": "integer" },
+ "iid": { "type": "integer" },
+ "title": { "type": "string" },
+ "confidential": { "type": "boolean" },
+ "due_date": { "type": "date" },
+ "project_id": { "type": "integer" },
+ "relative_position": { "type": ["integer", "null"] },
+ "time_estimate": { "type": "integer" },
+ "weight": { "type": "integer" },
+ "project": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "integer" },
+ "path": { "type": "string" }
+ }
+ },
+ "milestone": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "integer" },
+ "title": { "type": "string" }
+ }
+ },
+ "assignees": { "type": ["array", "null"] },
+ "labels": {
+ "type": "array",
+ "items": { "$ref": "label.json" }
+ },
+ "reference_path": { "type": "string" },
+ "real_path": { "type": "string" },
+ "issue_sidebar_endpoint": { "type": "string" },
+ "toggle_subscription_endpoint": { "type": "string" },
+ "assignable_labels_endpoint": { "type": "string" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index c40977bc4ee..35971d564d5 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -46,6 +46,7 @@
"diff_head_commit_short_id": { "type": ["string", "null"] },
"merge_commit_message": { "type": ["string", "null"] },
"pipeline": { "type": ["object", "null"] },
+ "merge_pipeline": { "type": ["object", "null"] },
"work_in_progress": { "type": "boolean" },
"source_branch_exists": { "type": "boolean" },
"mergeable_discussions_state": { "type": "boolean" },
diff --git a/spec/fixtures/api/schemas/entities/note_user_entity.json b/spec/fixtures/api/schemas/entities/note_user_entity.json
new file mode 100644
index 00000000000..9b838054563
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/note_user_entity.json
@@ -0,0 +1,21 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "state",
+ "avatar_url",
+ "path",
+ "name",
+ "username"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "string" },
+ "path": { "type": "string" },
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "status_tooltip_html": { "$ref": "../types/nullable_string.json" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/user.json b/spec/fixtures/api/schemas/entities/user.json
index 6482e0eedd2..82d80b75cef 100644
--- a/spec/fixtures/api/schemas/entities/user.json
+++ b/spec/fixtures/api/schemas/entities/user.json
@@ -5,13 +5,19 @@
"state",
"avatar_url",
"web_url",
- "path"
+ "path",
+ "name",
+ "username"
],
"properties": {
"id": { "type": "integer" },
"state": { "type": "string" },
"avatar_url": { "type": "string" },
"web_url": { "type": "string" },
- "path": { "type": "string" }
- }
+ "path": { "type": "string" },
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "status_tooltip_html": { "$ref": "../types/nullable_string.json" }
+ },
+ "additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/environment.json b/spec/fixtures/api/schemas/environment.json
new file mode 100644
index 00000000000..f1d33e3ce7b
--- /dev/null
+++ b/spec/fixtures/api/schemas/environment.json
@@ -0,0 +1,38 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "state",
+ "external_url",
+ "environment_type",
+ "has_stop_action",
+ "environment_path",
+ "stop_path",
+ "folder_path",
+ "created_at",
+ "updated_at",
+ "can_stop"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "name": { "type": "string" },
+ "state": { "type": "string" },
+ "external_url": { "$ref": "types/nullable_string.json" },
+ "environment_type": { "$ref": "types/nullable_string.json" },
+ "has_stop_action": { "type": "boolean" },
+ "environment_path": { "type": "string" },
+ "stop_path": { "type": "string" },
+ "folder_path": { "type": "string" },
+ "created_at": { "type": "string", "format": "date-time" },
+ "updated_at": { "type": "string", "format": "date-time" },
+ "can_stop": { "type": "boolean" },
+ "last_deployment": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "deployment.json" }
+ ]
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/http_method.json b/spec/fixtures/api/schemas/http_method.json
new file mode 100644
index 00000000000..c0f8acc5781
--- /dev/null
+++ b/spec/fixtures/api/schemas/http_method.json
@@ -0,0 +1,5 @@
+{
+ "type": "string",
+ "description": "HTTP methods that the API can specify to send.",
+ "enum": [ "post", "get", "put", "patch" ]
+}
diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json
index 8833825e3fb..a83ec55cede 100644
--- a/spec/fixtures/api/schemas/issue.json
+++ b/spec/fixtures/api/schemas/issue.json
@@ -13,8 +13,10 @@
"confidential": { "type": "boolean" },
"due_date": { "type": ["date", "null"] },
"relative_position": { "type": "integer" },
+ "time_estimate": { "type": "integer" },
"issue_sidebar_endpoint": { "type": "string" },
"toggle_subscription_endpoint": { "type": "string" },
+ "assignable_labels_endpoint": { "type": "string" },
"reference_path": { "type": "string" },
"real_path": { "type": "string" },
"project": {
diff --git a/spec/fixtures/api/schemas/job.json b/spec/fixtures/api/schemas/job.json
deleted file mode 100644
index 7b92ab25bc1..00000000000
--- a/spec/fixtures/api/schemas/job.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "type": "object",
- "required": [
- "id",
- "name",
- "started",
- "build_path",
- "playable",
- "created_at",
- "updated_at",
- "status"
- ],
- "properties": {
- "id": { "type": "integer" },
- "name": { "type": "string" },
- "started": { "type": "boolean" } ,
- "build_path": { "type": "string" },
- "playable": { "type": "boolean" },
- "created_at": { "type": "string" },
- "updated_at": { "type": "string" },
- "status": { "$ref": "ci_detailed_status.json" }
- },
- "additionalProperties": false
-}
diff --git a/spec/fixtures/api/schemas/job/artifact.json b/spec/fixtures/api/schemas/job/artifact.json
new file mode 100644
index 00000000000..1812e69fbd6
--- /dev/null
+++ b/spec/fixtures/api/schemas/job/artifact.json
@@ -0,0 +1,11 @@
+{
+ "type": "object",
+ "properties": {
+ "download_path": { "type": "string"},
+ "browse_path": { "type": "string"},
+ "keep_path": { "type": "string"},
+ "expired": { "type": "boolean" },
+ "expire_at": { "type": "string", "format": "date-time" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/job/deployment_status.json b/spec/fixtures/api/schemas/job/deployment_status.json
new file mode 100644
index 00000000000..83b1899fdf3
--- /dev/null
+++ b/spec/fixtures/api/schemas/job/deployment_status.json
@@ -0,0 +1,25 @@
+{
+ "type": "object",
+ "required": [
+ "status",
+ "environment"
+ ],
+ "properties": {
+ "status": {
+ "oneOf": [
+ {
+ "type": "string",
+ "enum": [
+ "last",
+ "creating",
+ "failed",
+ "out_of_date"
+ ]
+ },
+ { "type": "null" }
+ ]
+ },
+ "environment": { "$ref": "../environment.json" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/job/job.json b/spec/fixtures/api/schemas/job/job.json
new file mode 100644
index 00000000000..f3d5e9b038a
--- /dev/null
+++ b/spec/fixtures/api/schemas/job/job.json
@@ -0,0 +1,35 @@
+{
+ "description": "Basic job information",
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "started",
+ "build_path",
+ "playable",
+ "created_at",
+ "updated_at",
+ "status",
+ "archived"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "name": { "type": "string" },
+ "started": {
+ "oneOf": [
+ { "type": "string", "format": "date-time" },
+ { "type": "boolean" }
+ ]
+ },
+ "build_path": { "type": "string" },
+ "retry_path": { "type": "string" },
+ "playable": { "type": "boolean" },
+ "created_at": { "type": "string" },
+ "updated_at": { "type": "string" },
+ "status": { "$ref": "../status/ci_detailed_status.json" },
+ "callout_message": { "type": "string" },
+ "recoverable": { "type": "boolean" },
+ "archived": { "type": "boolean" }
+ },
+ "additionalProperties": true
+}
diff --git a/spec/fixtures/api/schemas/job/job_details.json b/spec/fixtures/api/schemas/job/job_details.json
new file mode 100644
index 00000000000..cdf7b049ab6
--- /dev/null
+++ b/spec/fixtures/api/schemas/job/job_details.json
@@ -0,0 +1,24 @@
+{
+ "allOf": [
+ { "$ref": "job.json" }
+ ],
+ "description": "An extension of job.json with more detailed information",
+ "required": [
+ "artifact",
+ "runner",
+ "runners",
+ "has_trace",
+ "stage"
+ ],
+ "properties": {
+ "artifact": { "$ref": "artifact.json" },
+ "terminal_path": { "type": "string" },
+ "trigger": { "$ref": "trigger.json" },
+ "deployment_status": { "$ref": "deployment_status.json" },
+ "runner": { "$ref": "runner.json" },
+ "runners": { "$ref": "runners.json" },
+ "has_trace": { "type": "boolean" },
+ "stage": { "type": "string" },
+ "stuck": { "type": "boolean" }
+ }
+}
diff --git a/spec/fixtures/api/schemas/job/runner.json b/spec/fixtures/api/schemas/job/runner.json
new file mode 100644
index 00000000000..acfeeeeb808
--- /dev/null
+++ b/spec/fixtures/api/schemas/job/runner.json
@@ -0,0 +1,17 @@
+{
+ "oneOf": [
+ { "type": "null" },
+ {
+ "type": "object",
+ "required": [
+ "id",
+ "description"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "description": { "type": "string" },
+ "edit_path": { "type": "string" }
+ }
+ }
+ ]
+}
diff --git a/spec/fixtures/api/schemas/job/runners.json b/spec/fixtures/api/schemas/job/runners.json
new file mode 100644
index 00000000000..646bfd3a82d
--- /dev/null
+++ b/spec/fixtures/api/schemas/job/runners.json
@@ -0,0 +1,12 @@
+{
+ "type": "object",
+ "required": [
+ "online",
+ "available"
+ ],
+ "properties": {
+ "online": { "type": "boolean" },
+ "available": { "type": "boolean" },
+ "settings_path": { "type": "string" }
+ }
+}
diff --git a/spec/fixtures/api/schemas/job/trigger.json b/spec/fixtures/api/schemas/job/trigger.json
new file mode 100644
index 00000000000..1c7e9cc7693
--- /dev/null
+++ b/spec/fixtures/api/schemas/job/trigger.json
@@ -0,0 +1,28 @@
+{
+ "type": "object",
+ "required": [
+ "short_token",
+ "variables"
+ ],
+ "properties": {
+ "short_token": { "type": "string" },
+ "variables": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key",
+ "value",
+ "public"
+ ],
+ "properties": {
+ "key": { "type": "string" },
+ "value": { "type": "string" },
+ "public": { "type": "boolean" }
+ },
+ "additionalProperties": false
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/pipeline_stage.json b/spec/fixtures/api/schemas/pipeline_stage.json
index 55454200bb3..c01a1946185 100644
--- a/spec/fixtures/api/schemas/pipeline_stage.json
+++ b/spec/fixtures/api/schemas/pipeline_stage.json
@@ -13,10 +13,15 @@
"groups": { "optional": true },
"latest_statuses": {
"type": "array",
- "items": { "$ref": "job.json" },
+ "items": { "$ref": "job/job.json" },
"optional": true
},
- "status": { "$ref": "ci_detailed_status.json" },
+ "retried": {
+ "type": "array",
+ "items": { "$ref": "job/job.json" },
+ "optional": true
+ },
+ "status": { "$ref": "status/ci_detailed_status.json" },
"path": { "type": "string" },
"dropdown_path": { "type": "string" }
},
diff --git a/spec/fixtures/api/schemas/public_api/v4/branch.json b/spec/fixtures/api/schemas/public_api/v4/branch.json
index a8891680d06..3b0f010bc4f 100644
--- a/spec/fixtures/api/schemas/public_api/v4/branch.json
+++ b/spec/fixtures/api/schemas/public_api/v4/branch.json
@@ -5,6 +5,7 @@
"commit",
"merged",
"protected",
+ "default",
"developers_can_push",
"developers_can_merge"
],
@@ -13,6 +14,7 @@
"commit": { "$ref": "commit/basic.json" },
"merged": { "type": "boolean" },
"protected": { "type": "boolean" },
+ "default": { "type": "boolean" },
"developers_can_push": { "type": "boolean" },
"developers_can_merge": { "type": "boolean" },
"can_push": { "type": "boolean" }
diff --git a/spec/fixtures/api/schemas/public_api/v4/license.json b/spec/fixtures/api/schemas/public_api/v4/license.json
new file mode 100644
index 00000000000..38c8c3e9192
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/license.json
@@ -0,0 +1,30 @@
+{
+ "type": "object",
+ "required": [
+ "key",
+ "name",
+ "nickname",
+ "popular",
+ "html_url",
+ "source_url",
+ "description",
+ "conditions",
+ "permissions",
+ "limitations",
+ "content"
+ ],
+ "properties": {
+ "key": { "type": "string" },
+ "name": { "type": "string" },
+ "nickname": { "type": ["null", "string"] },
+ "popular": { "type": "boolean" },
+ "html_url": { "type": ["null", "string"] },
+ "source_url": { "type": ["null", "string"] },
+ "description": { "type": ["null", "string"] },
+ "conditions": { "type": "array", "items": { "type": "string" } },
+ "permissions": { "type": "array", "items": { "type": "string" } },
+ "limitations": { "type": "array", "items": { "type": "string" } },
+ "content": { "type": "string" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
index f7adc4e0b91..6df27bf32b9 100644
--- a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
+++ b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
@@ -9,6 +9,32 @@
"title": { "type": "string" },
"description": { "type": ["string", "null"] },
"state": { "type": "string" },
+ "merged_by": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
+ },
+ "additionalProperties": false
+ },
+ "merged_at": { "type": ["date", "null"] },
+ "closed_by": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
+ },
+ "additionalProperties": false
+ },
+ "closed_at": { "type": ["date", "null"] },
"created_at": { "type": "date" },
"updated_at": { "type": "date" },
"target_branch": { "type": "string" },
diff --git a/spec/fixtures/api/schemas/public_api/v4/template.json b/spec/fixtures/api/schemas/public_api/v4/template.json
new file mode 100644
index 00000000000..38601aa6b45
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/template.json
@@ -0,0 +1,12 @@
+{
+ "type": "object",
+ "required": [
+ "name",
+ "content"
+ ],
+ "properties": {
+ "name": { "type": "string" },
+ "content": { "type": "string" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/template_list.json b/spec/fixtures/api/schemas/public_api/v4/template_list.json
new file mode 100644
index 00000000000..2336dafb17b
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/template_list.json
@@ -0,0 +1,15 @@
+{
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key",
+ "name"
+ ],
+ "properties": {
+ "key": { "type": "string" },
+ "name": { "type": "string" }
+ },
+ "additionalProperties": false
+ }
+}
diff --git a/spec/fixtures/api/schemas/status/action.json b/spec/fixtures/api/schemas/status/action.json
new file mode 100644
index 00000000000..99a576e6c5b
--- /dev/null
+++ b/spec/fixtures/api/schemas/status/action.json
@@ -0,0 +1,22 @@
+{
+ "type": "object",
+ "required": [
+ "icon",
+ "title",
+ "path",
+ "method"
+ ],
+ "properties": {
+ "icon": {
+ "type": "string",
+ "enum": [
+ "retry",
+ "play",
+ "cancel"
+ ]
+ },
+ "title": { "type": "string" },
+ "path": { "type": "string" },
+ "method": { "$ref": "../http_method.json" }
+ }
+}
diff --git a/spec/fixtures/api/schemas/status/ci_detailed_status.json b/spec/fixtures/api/schemas/status/ci_detailed_status.json
new file mode 100644
index 00000000000..8d0f1e4a6af
--- /dev/null
+++ b/spec/fixtures/api/schemas/status/ci_detailed_status.json
@@ -0,0 +1,26 @@
+{
+ "type": "object",
+ "required": [
+ "icon",
+ "text",
+ "label",
+ "group",
+ "tooltip",
+ "has_details",
+ "details_path",
+ "favicon"
+ ],
+ "properties": {
+ "icon": { "type": "string" },
+ "text": { "type": "string" },
+ "label": { "type": "string" },
+ "group": { "type": "string" },
+ "tooltip": { "type": "string" },
+ "has_details": { "type": "boolean" },
+ "details_path": { "type": "string" },
+ "favicon": { "type": "string" },
+ "illustration": { "$ref": "illustration.json" },
+ "action": { "$ref": "action.json" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/status/illustration.json b/spec/fixtures/api/schemas/status/illustration.json
new file mode 100644
index 00000000000..9a085f5f1ee
--- /dev/null
+++ b/spec/fixtures/api/schemas/status/illustration.json
@@ -0,0 +1,19 @@
+{
+ "oneOf": [
+ { "type": "null" },
+ {
+ "type": "object",
+ "required": [
+ "image",
+ "size",
+ "title"
+ ],
+ "properties": {
+ "image": { "type": "string" },
+ "size": { "type": "string" },
+ "title": { "type": "string" },
+ "content": { "type": "string" }
+ }
+ }
+ ]
+}
diff --git a/spec/fixtures/api/schemas/types/nullable_string.json b/spec/fixtures/api/schemas/types/nullable_string.json
new file mode 100644
index 00000000000..e3b0baef849
--- /dev/null
+++ b/spec/fixtures/api/schemas/types/nullable_string.json
@@ -0,0 +1,6 @@
+{
+ "oneOf": [
+ { "type": "null" },
+ { "type": "string" }
+ ]
+}
diff --git a/spec/fixtures/authentication/saml2_response.xml b/spec/fixtures/authentication/saml2_response.xml
new file mode 100644
index 00000000000..67dea7209e9
--- /dev/null
+++ b/spec/fixtures/authentication/saml2_response.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema" Destination="https://example.hello.com/access/saml" ID="jVFQbyEpSfUwqhZtJtarIaGoshwuAQMDwLoiMhzJXsv" InResponseTo="cfeooghajnhofcmogakmlhpkohnmikicnfhdnjlc" IssueInstant="2011-06-21T13:54:38.661Z" Version="2.0">
+ <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://idm.orademo.com</saml2:Issuer>
+ <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo>
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+ <ds:Reference URI="#jVFQbyEpSfUwqhZtJtarIaGoshwuAQMDwLoiMhzJXsv">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
+ <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/>
+ </ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>uHuSry39P16Yh7srS32xESmj4Lw=</ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue>fdghdfggfd=</ds:SignatureValue>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>dfghjkl</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </ds:Signature>
+ <saml2p:Status>
+ <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
+ </saml2p:Status>
+ <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="emmCjammnYdAbMWDuMAJeZvQIMBayeeYqqwvQoDclKE" IssueInstant="2011-06-21T13:54:38.676Z" Version="2.0">
+ <saml2:Issuer>https://idm.orademo.com</saml2:Issuer>
+ <saml2:Subject>
+ <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" NameQualifier="idp.example.org">someone@example.org</saml2:NameID>
+ <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
+ <saml2:SubjectConfirmationData InResponseTo="cfeooghajnhofcmogakmlhpkohnmikicnfhdnjlc" NotOnOrAfter="2011-06-21T14:09:38.676Z" Recipient="https://example.hello.com/access/saml"/>
+ </saml2:SubjectConfirmation>
+ </saml2:Subject>
+ <saml2:Conditions NotBefore="2011-06-21T13:54:38.683Z" NotOnOrAfter="2011-06-21T14:09:38.683Z">
+ <saml2:AudienceRestriction>
+ <saml2:Audience>hello.com</saml2:Audience>
+ </saml2:AudienceRestriction>
+ </saml2:Conditions>
+ <saml2:AuthnStatement AuthnInstant="2011-06-21T13:54:38.685Z" SessionIndex="perdkjfskdjfksdiertusfsdfsddeurtherukjdfgkdffg">
+ <saml2:AuthnContext>
+ <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
+ </saml2:AuthnContext>
+ </saml2:AuthnStatement>
+ <saml2:AttributeStatement>
+ <saml2:Attribute Name="FirstName">
+ <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Someone</saml2:AttributeValue>
+ </saml2:Attribute>
+ <saml2:Attribute Name="LastName">
+ <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Special</saml2:AttributeValue>
+ </saml2:Attribute>
+ </saml2:AttributeStatement>
+ </saml2:Assertion>
+</saml2p:Response>
diff --git a/spec/fixtures/codequality/codequality.json b/spec/fixtures/codequality/codequality.json
new file mode 100644
index 00000000000..89cc4b46521
--- /dev/null
+++ b/spec/fixtures/codequality/codequality.json
@@ -0,0 +1 @@
+[{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `simulateDrag` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a995a617b0ce0599b6d0a5649a308a8e","location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":75,"end":137}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `simulateEvent` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e50b54fce33185efbb17d1c0d210b439","location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":1,"end":37}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `simulateDrag` has 51 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"75ffb8892dac22a9d50822bc69339ab2","location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":75,"end":137}},"other_locations":[],"remediation_points":1224000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"40f188d222bd1f110e8c11f08ec77c3f","location":{"path":"app/assets/javascripts/blob/3d_viewer/index.js","lines":{"begin":10,"end":47}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 60 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"95b71f07d08eb9e2368568c6d872f677","location":{"path":"app/assets/javascripts/blob/blob_file_dropzone.js","lines":{"begin":21,"end":88}},"other_locations":[],"remediation_points":1440000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`FileTemplateMediator` has 29 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"02a64abfacf06440f6504888503fe571","location":{"path":"app/assets/javascripts/blob/file_template_mediator.js","lines":{"begin":11,"end":246}},"other_locations":[],"remediation_points":2100000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ede6d8c5878fbfdda2348d1c6a808f4b","location":{"path":"app/assets/javascripts/issuable_form.js","lines":{"begin":14,"end":57}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initTargetBranchDropdown` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"748b8a39304544f015c0abcbee9b929c","location":{"path":"app/assets/javascripts/issuable_form.js","lines":{"begin":116,"end":147}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `start` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b0272210e1d618fe95522ee47f652308","location":{"path":"app/assets/javascripts/smart_interval.js","lines":{"begin":44,"end":65}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `render` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ceb92ce0f43dbe13431f1f07d5787aff","location":{"path":"app/assets/javascripts/vue_shared/components/tabs/tabs.js","lines":{"begin":35,"end":75}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `file_icon_map.js` has 587 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"33d443c482c7a34a3a721ef8677a9b9b","location":{"path":"app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js","lines":{"begin":1,"end":590}},"other_locations":[],"remediation_points":6052800,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `gl_dropdown.js` has 935 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"144655d5e2aa528a6e4d63861c3bc2f1","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":1,"end":1111}},"other_locations":[],"remediation_points":11064000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `GitLabDropdownInput` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4679297a043d89d437946bd083443d13","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":14,"end":58}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `GitLabDropdownFilter` has 55 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0c2b6bfd5d744ae2f9724aae0a60a765","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":76,"end":136}},"other_locations":[],"remediation_points":1320000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `filter` has 53 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3be214723bb15219db661db421dccba1","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":142,"end":213}},"other_locations":[],"remediation_points":1272000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `GitLabDropdown` has 172 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"45e3d28c25ef43db19fcd1f8bf7a52dc","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":296,"end":481}},"other_locations":[],"remediation_points":4128000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `parseData` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5a6929b08a7efc020e932267316a1ce4","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":505,"end":538}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `opened` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b32257d5ff71fd022c46be4ff3f196b6","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":580,"end":627}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderItem` has 77 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"119c9b263aba3cc2173646b63b83d58d","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":698,"end":793}},"other_locations":[],"remediation_points":1848000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `rowClicked` has 75 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e37288d65d5701c4b7d06560d3eb6910","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":820,"end":904}},"other_locations":[],"remediation_points":1800000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addArrowKeyEvent` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4ccf81453af86fd4e7f521423adbf28e","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":971,"end":1015}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `highlightRowAtIndex` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a0b5b8a75d9f5726f8a559a3068bbfd9","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":1026,"end":1073}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":174,"end":184}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"603368a717652923897a9efd4f494784"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `onOptionClick` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a1ead8da9cc948075715df57538a53fb","location":{"path":"app/assets/javascripts/groups/groups_filterable_list.js","lines":{"begin":76,"end":122}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onOptionClick` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"aa9f5f9cb36f5307a7db44c29b5f3a4c","location":{"path":"app/assets/javascripts/groups/groups_filterable_list.js","lines":{"begin":76,"end":122}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setSearchedGroups` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4116f1bf48693b8dbc3a19233c167489","location":{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":19,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `formatGroupItem` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"97209b6e238a05d220f90d1e1fcf7c78","location":{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":63,"end":96}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `getGroups` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f9fc97d0941125a5eb91818b85ac955","location":{"path":"app/assets/javascripts/groups/service/groups_service.js","lines":{"begin":9,"end":34}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 47 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1cc8f54fcedff8d025db690e89c4d3bc","location":{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":24,"end":78}},"other_locations":[],"remediation_points":1128000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d34e04056341d2986626e42b9927e829","location":{"path":"app/assets/javascripts/clusters/stores/clusters_store.js","lines":{"begin":5,"end":51}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initLayoutNav` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"858063cf6c5a28edd5390360940941f1","location":{"path":"app/assets/javascripts/layout_nav.js","lines":{"begin":12,"end":52}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `sidebarToggleClicked` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e95063093263d654532b1bda2499ae09","location":{"path":"app/assets/javascripts/right_sidebar.js","lines":{"begin":44,"end":69}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleSidebar` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b4667aa48f617e84611c658bd9e6fd42","location":{"path":"app/assets/javascripts/right_sidebar.js","lines":{"begin":201,"end":218}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `message` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f561803cb2a47ffaeed70a1f967105da","location":{"path":"app/assets/javascripts/u2f/error.js","lines":{"begin":9,"end":21}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `issueTemplate` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"36dc94ada0224320b48f9dcddf971dd3","location":{"path":"app/assets/javascripts/api.js","lines":{"begin":223,"end":223}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`Api` has 24 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"62c9c1d73c6c2f6f294b2c13a5c92e49","location":{"path":"app/assets/javascripts/api.js","lines":{"begin":5,"end":282}},"other_locations":[],"remediation_points":1600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `validate` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8b07c343f7afe957592fac79043c9586","location":{"path":"app/assets/javascripts/new_branch_form.js","lines":{"begin":53,"end":94}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `labels_select.js` has 444 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"34830d2a928eacedb749d5c66c3637e9","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":1,"end":517}},"other_locations":[],"remediation_points":3993600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setDropdownData` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"99aee27c164d2c53ee3386f64239ebc9","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":466,"end":507}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 369 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ca542a5aefb3dbadb5e0a0100a94b863","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":16,"end":429}},"other_locations":[],"remediation_points":8856000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `data` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"00d06b2fdc398a9031e900f76184076f","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":133,"end":176}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderRow` has 50 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ef4d877e54496d7ad4da0e0a0b96d9aa","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":177,"end":235}},"other_locations":[],"remediation_points":1200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `toggleLabel` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"35ba5b5dab8a50aa713a5ba60e1db7a1","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":242,"end":278}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `hidden` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2dc1a005647c9e7e9f81e0eabeb9563d","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":294,"end":324}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `clicked` has 75 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f7fb23f1a88e5dc4c8430529946de3dc","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":327,"end":414}},"other_locations":[],"remediation_points":1800000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setDropdownData` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"955ed39a38f4f36c3ae0d23199b6b90d","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":466,"end":507}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":407,"end":412}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"cefdca8d12fc08aff6e7a861750e99d9"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":377,"end":377}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"1d311ac9ae722bf786749b2125e5cf47"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":411,"end":411}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"7d42a3bbce082d2bbdc96d5eaa747454"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `storeEnvironments` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"70cce4c4dfcfa9c7fb820fc6dea68e9c","location":{"path":"app/assets/javascripts/environments/stores/environments_store.js","lines":{"begin":36,"end":71}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `storeEnvironments` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f5b13b9d301832978862f37bcc22708f","location":{"path":"app/assets/javascripts/environments/stores/environments_store.js","lines":{"begin":36,"end":71}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `created` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"139fddb1aa522eb2c141871eab765fc3","location":{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":142,"end":175}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b4d0a598693b87337ee6b071a35c6da2","location":{"path":"app/assets/javascripts/issuable_context.js","lines":{"begin":7,"end":51}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `dropzoneInput` has 210 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"96984c8a7930ad755604f49c3edf6a2b","location":{"path":"app/assets/javascripts/dropzone_input.js","lines":{"begin":23,"end":290}},"other_locations":[],"remediation_points":5040000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1ec26d8ddc2c55de770d1f8038f590f4","location":{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":39,"end":67}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.ADD_NEW_NOTE` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"026945d2d3de0e3f478ec55a7bb820f5","location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":7,"end":31}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.REMOVE_PLACEHOLDER_NOTES` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"26efdceb93879d8478e3c8a4f549bc22","location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":66,"end":85}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`` has 22 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"75cd5c2d207814d4a7c7fbf69c709fd6","location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":6,"end":224}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `actions.js` has 271 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"9e2580af22da5d9875ac924fda6d0551","location":{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":1,"end":337}},"other_locations":[],"remediation_points":1502400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `saveNote` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9dbdac686defafb20b3397ccb7105ba3","location":{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":151,"end":226}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `getQuickActionText` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3019b0611c6febc7b696f0615d561d1b","location":{"path":"app/assets/javascripts/notes/stores/utils.js","lines":{"begin":7,"end":26}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `collapseSystemNotes` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"757d82e7c89224872a7bd7b269ed55c5","location":{"path":"app/assets/javascripts/notes/stores/collapse_utils.js","lines":{"begin":57,"end":105}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `unresolvedDiscussionsIdsByDiff` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"250bf3d3615586b7cdaecad2c187e0a0","location":{"path":"app/assets/javascripts/notes/stores/getters.js","lines":{"begin":117,"end":139}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `jumpToDiscussion` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ebbd3b5d75f27f2372eb1a56ca4d9206","location":{"path":"app/assets/javascripts/notes/mixins/discussion_navigation.js","lines":{"begin":5,"end":27}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `keydown` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1977a74c9fba9606480c93a63cb0d5c0","location":{"path":"app/assets/javascripts/droplab/plugins/filter.js","lines":{"begin":4,"end":47}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `keydown` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"6356352d7ef34cfddc4738ed0c5ebdbf","location":{"path":"app/assets/javascripts/droplab/plugins/filter.js","lines":{"begin":4,"end":47}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `trigger` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3ad4384530fc0032f4fafe9d12e7dc70","location":{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":35,"end":71}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `_loadData` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"357b7070736cef3f382a2c3439a40d89","location":{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":73,"end":92}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `trigger` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bc97a87fa8ec1b132731256b23616434","location":{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":35,"end":71}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `Keyboard` has a Cognitive Complexity of 49 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6b1d8f0930c09b5318307d0788ffca30","location":{"path":"app/assets/javascripts/droplab/keyboard.js","lines":{"begin":5,"end":111}},"other_locations":[],"remediation_points":4550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `Keyboard` has 96 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a812687f4ed1c53045b473f0d6aa5cb9","location":{"path":"app/assets/javascripts/droplab/keyboard.js","lines":{"begin":5,"end":111}},"other_locations":[],"remediation_points":2304000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `keydown` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e7cbff360ff21f798bf424a899291302","location":{"path":"app/assets/javascripts/droplab/keyboard.js","lines":{"begin":70,"end":107}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `highlightHash` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2bd33253a3ff30d380e94d478d49a6e7","location":{"path":"app/assets/javascripts/line_highlighter.js","lines":{"begin":59,"end":82}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `highlightRange` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4258948c78c182b6db020222636c12bb","location":{"path":"app/assets/javascripts/line_highlighter.js","lines":{"begin":144,"end":157}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `checkElementsInView` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"08e5457bd1bcf2aa36f67185fb5ab932","location":{"path":"app/assets/javascripts/lazy_loader.js","lines":{"begin":53,"end":75}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `loadImage` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e063810e532be97a06efabd3c56e4db","location":{"path":"app/assets/javascripts/lazy_loader.js","lines":{"begin":76,"end":94}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `constructor` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a3ef386977d1577238533b8b5352a499","location":{"path":"app/assets/javascripts/diff.js","lines":{"begin":14,"end":43}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `handleClickUnfold` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f82a7cc93be5e64d548469363a77a2fb","location":{"path":"app/assets/javascripts/diff.js","lines":{"begin":45,"end":79}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initMergeConflicts` has 81 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dd259a46f5e703e8f722acdac619ad79","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js","lines":{"begin":12,"end":100}},"other_locations":[],"remediation_points":1944000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_conflict_store.js` has 357 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"131a2e61cbfe7efc49cced2ffab3568b","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":1,"end":436}},"other_locations":[],"remediation_points":2740800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setParallelLine` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f005034ab86d552698a2211ae91ae99a","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":101,"end":139}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `isReadyToCommit` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"184e70a4c8de2226d5a4b197c147f2e8","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":315,"end":351}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":331,"end":333}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"78a238497c0c8acd6ddefbb67176f033"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `init` has 216 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0c8af9cd2b45c1c2e6e24c4678a9c6fc","location":{"path":"app/assets/javascripts/milestone_select.js","lines":{"begin":23,"end":253}},"other_locations":[],"remediation_points":5184000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `clicked` has 91 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c5e3838b8db3f11da2f72d9acaf52a17","location":{"path":"app/assets/javascripts/milestone_select.js","lines":{"begin":150,"end":250}},"other_locations":[],"remediation_points":2184000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/milestone_select.js","lines":{"begin":187,"end":187}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"9265cc21e5d1eb1bebd044e85db1bf79"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/milestone_select.js","lines":{"begin":220,"end":248}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"36ba0880a027f4448128be161cf33b26"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `createFlash` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"d8f8d96fc8578fb3f7f7bf875e0668a0","location":{"path":"app/assets/javascripts/flash.js","lines":{"begin":64,"end":69}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `textBuilder` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"877ac84192b385c963c08bb4ef960d3b","location":{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":10,"end":37}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e2f702de12038884b25d0ef63861bd2b","location":{"path":"app/assets/javascripts/ref_select_dropdown.js","lines":{"begin":4,"end":46}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setConfig` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"33b2d4c912bf8eb7a3c4b9c2d3302729","location":{"path":"app/assets/javascripts/close_reopen_report_toggle.js","lines":{"begin":57,"end":94}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `notes.js` has 1338 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"515dff155804bebfe20a78b3935600eb","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1,"end":1859}},"other_locations":[],"remediation_points":16867200,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `keydownNoteText` has a Cognitive Complexity of 24 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4a28bd8f572a9cb26ff49edf77e2d3a3","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":226,"end":278}},"other_locations":[],"remediation_points":2050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `renderNote` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc0dc1a78ad18f837b6cead5d11a5ac2","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":403,"end":462}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `renderDiscussionNote` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dbb0fd84250020e115dbb0554f3dfc2a","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":471,"end":538}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleDiffNote` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ddb3cba4008791217e07933b09a14951","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1054,"end":1124}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `updateTargetButtons` has a Cognitive Complexity of 22 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4ea9fe607edc9cf98ef50a17584952c2","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1196,"end":1241}},"other_locations":[],"remediation_points":1850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `postComment` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cc8dd67d9f1598e83dc132f50b7f0726","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1604,"end":1796}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`Notes` has 74 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"5363864915c583936ec6c63c5ed96689","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":48,"end":1856}},"other_locations":[],"remediation_points":6600000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 54 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"25812d53d4eb0f340c33a5a5ddca467a","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":59,"end":121}},"other_locations":[],"remediation_points":1296000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addBinding` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9b40ee2b94454885b3fd51a8bc139c1c","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":127,"end":180}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `keydownNoteText` has 48 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"85df701d158d32b25dca1c2039b6a3be","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":226,"end":278}},"other_locations":[],"remediation_points":1152000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderNote` has 50 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8dfb4b9b4d144d82422186651b46b3e4","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":403,"end":462}},"other_locations":[],"remediation_points":1200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderDiscussionNote` has 53 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"52b66ad02e73c9b1c8848bd1affad7fc","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":471,"end":538}},"other_locations":[],"remediation_points":1272000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `removeNote` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8c1e3976ef9bdf79365857e0b8bcb5d8","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":848,"end":911}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setupDiscussionNoteForm` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d1b7947a88bd1e4b3519e282320fbb38","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":956,"end":1009}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `toggleDiffNote` has 61 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"60ae737d4dc10fe0af6e670d2d4bc7e2","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1054,"end":1124}},"other_locations":[],"remediation_points":1464000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `updateTargetButtons` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8ccd0067a20bb8afcaf3317b434b1ece","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1196,"end":1241}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `createPlaceholderNote` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ddcd3bc4735c7d763b0fe5da55217278","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1526,"end":1566}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `postComment` has 143 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fcdd1766ceaaa90c770bddfe75d120cd","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1604,"end":1796}},"other_locations":[],"remediation_points":3432000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `updateComment` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7c508bf1571f9f4d795de4cb8ef1930a","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1811,"end":1855}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":264,"end":264}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"cd548cc7c1ebbb2263b1d2ecdb94a5dd"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":272,"end":272}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"cd548cc7c1ebbb2263b1d2ecdb94a5dd"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":275,"end":275}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"25fb735695be215146eaf2300412aae0"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `itemClicked` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"86a06125a02d35c7e121952daf7f0f08","location":{"path":"app/assets/javascripts/filtered_search/dropdown_hint.js","lines":{"begin":23,"end":60}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `itemClicked` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e7751795d821a7afa57ab25dbd658ee4","location":{"path":"app/assets/javascripts/filtered_search/dropdown_hint.js","lines":{"begin":23,"end":60}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `filterHint` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"22102abbd93e4d53a0798ecf7e058512","location":{"path":"app/assets/javascripts/filtered_search/dropdown_utils.js","lines":{"begin":117,"end":140}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getSearchQuery` has 40 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"26e123d9f4a95bfa34a59f42db0ae260","location":{"path":"app/assets/javascripts/filtered_search/dropdown_utils.js","lines":{"begin":164,"end":213}},"other_locations":[],"remediation_points":960000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `processTokens` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"28cb3a1b7fa1a51cd968e9016415efea","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_tokenizer.js","lines":{"begin":4,"end":51}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `processTokens` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"287e8c4963cc8869358280d31e4d4005","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_tokenizer.js","lines":{"begin":4,"end":51}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `loadDropdown` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"31af8cc264fa78512d238c04106208fe","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":202,"end":219}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setDropdown` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6dda613591636a58b06a298d0980498f","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":221,"end":243}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setupMapping` has 55 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f12b787fff78b0b8986506eb90b1997c","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":50,"end":108}},"other_locations":[],"remediation_points":1320000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `load` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"90c65f9cea258055f67defcb61f63abb","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":165,"end":200}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `filtered_search_manager.js` has 514 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"45105bf38088a343a5a9c0e1ca76840e","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":1,"end":645}},"other_locations":[],"remediation_points":5001600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setup` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c08d2326cb1eb811c4329b3ee92fc04","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":58,"end":106}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `checkForEnter` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2056e10cb965de92a331a6181618ff7f","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":236,"end":265}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `handleInputVisualToken` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3ef6573dc182d159eb13ddd70be42653","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":398,"end":438}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`FilteredSearchManager` has 32 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"dee04c9127382346c9edead8ceb49788","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":20,"end":644}},"other_locations":[],"remediation_points":2400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setup` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fc525f4373639fe1e5e079259d587918","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":58,"end":106}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `bindEvents` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0036874f7fdfa7b481bccec4adce2fd9","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":145,"end":182}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `handleInputVisualToken` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"105fd464c05f365ea496107488c4d048","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":398,"end":438}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `loadSearchParamsFromURL` has 63 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dc0a70cc2480e01d46d52df00f42980b","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":463,"end":543}},"other_locations":[],"remediation_points":1512000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `search` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8d31d46c10a7c7b27cba3e79c2c7e684","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":558,"end":607}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":524,"end":529}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"b4fcaa9a90b83251c167ce6bd363ead0"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":530,"end":533}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"b9acc2eaf5ae1dd8b6f24ac70e97ace6"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `users_select.js` has 594 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"9e6ca5bcc402f0dae9a9869dd46e4e77","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":1,"end":701}},"other_locations":[],"remediation_points":6153600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `UsersSelect` has 524 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b4438d98e020e34865089e812790a72e","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":14,"end":628}},"other_locations":[],"remediation_points":12576000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `processData` has 88 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9e7c9e224218cce1f434b0a0ff4616c4","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":221,"end":327}},"other_locations":[],"remediation_points":2112000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `clicked` has 64 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"19cb3539002fa7a4aa2abf730fc550bc","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":381,"end":468}},"other_locations":[],"remediation_points":1536000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderRow` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b67cb57917ef04a8b05e4be075bf6c0b","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":493,"end":531}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `query` has 48 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"447defa81cda25e0ae3f708d8cb7f927","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":553,"end":604}},"other_locations":[],"remediation_points":1152000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleCollapsed` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"01ddc746dbdfa4103552d4d09bda67bd","location":{"path":"app/assets/javascripts/image_diff/helpers/dom_helper.js","lines":{"begin":27,"end":45}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addBadge` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5cb8fbd4ee1e42eb778a29cd02efc8f5","location":{"path":"app/assets/javascripts/image_diff/image_diff.js","lines":{"begin":84,"end":116}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1579b1a9e97ea98e6348df70287ccd85","location":{"path":"app/assets/javascripts/job.js","lines":{"begin":13,"end":70}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getBuildTrace` has 55 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0990625843278c1e0d27866b6e51ec9b","location":{"path":"app/assets/javascripts/job.js","lines":{"begin":96,"end":163}},"other_locations":[],"remediation_points":1320000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setVisibilityOptions` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bd793c0b96469cb6904241d8beea60c0","location":{"path":"app/assets/javascripts/project_visibility.js","lines":{"begin":3,"end":35}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `restore` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ebd9e5c02ce67b5fecb0e9a610fa01a3","location":{"path":"app/assets/javascripts/autosave.js","lines":{"begin":19,"end":37}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `populateActiveMetrics` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0808d7392776f800f01eb00247cb49fc","location":{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":63,"end":100}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `loadActiveMetrics` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d2f56d70d330df4e004e4e730ec9bd7d","location":{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":102,"end":130}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `adminInit` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cb6b998e81923ba316eb986d14f16ef7","location":{"path":"app/assets/javascripts/pages/admin/admin.js","lines":{"begin":14,"end":60}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `renderState` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d23e829e807e7cfa7fa60a5bd8bd2a4f","location":{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":49,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":70,"end":70}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"59ee4d34d9113510c329a2b264aae308"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `showTab` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2db6bb70760f9a2493d374e830395ef3","location":{"path":"app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js","lines":{"begin":34,"end":47}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 52 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"364739acddb57495be3b94860555f2ae","location":{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":13,"end":72}},"other_locations":[],"remediation_points":1248000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initRefSwitcher` has 63 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e62a2b92cc28ef2e2c5cc5e391c1b3c1","location":{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":83,"end":155}},"other_locations":[],"remediation_points":1512000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initLabelIndex` has 72 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d6cbf4de48d122212aa6790bce385c52","location":{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":9,"end":91}},"other_locations":[],"remediation_points":1728000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `stat_graph_contributors_graph.js` has 260 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"d70980224b971643c65164d7599d8306","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":1,"end":315}},"other_locations":[],"remediation_points":1344000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `ContributorsGraph` has 68 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c5ffd1e9b7f7eb20d76540029db76ea5","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":19,"end":107}},"other_locations":[],"remediation_points":1632000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `ContributorsMasterGraph` has 98 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c90cea5a64860bd8ebc11adfe43f966f","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":109,"end":225}},"other_locations":[],"remediation_points":2352000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `ContributorsAuthorGraph` has 75 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cb2ba67ac3c7b0319408d414f7ab4a89","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":227,"end":314}},"other_locations":[],"remediation_points":1800000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `parse_log` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"54a68f13ca92482f197897caa4dde8c6","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":5,"end":31}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `renderSidebar` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a8652d503fb546ee9566f5bf3f473ac","location":{"path":"app/assets/javascripts/pages/projects/wikis/wikis.js","lines":{"begin":54,"end":66}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `activity_calendar.js` has 257 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"c8cf6370baf6d39528465c33a636bdaa","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":1,"end":300}},"other_locations":[],"remediation_points":1300800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `constructor` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"12e8c60549aefe50a5602474054d85e9","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":46,"end":115}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 49 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"70826affaa552c98140c396bd20b2321","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":46,"end":115}},"other_locations":[],"remediation_points":1176000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderDays` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b25511a5a67d72f5494bf518b57f8945","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":144,"end":182}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderKey` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f239dc6410f68cc561519c650b54fe79","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":226,"end":257}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 65 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"910e3f36c66a265e42a8eaf26a167c4b","location":{"path":"app/assets/javascripts/pages/search/show/search.js","lines":{"begin":6,"end":77}},"other_locations":[],"remediation_points":1560000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `showPreview` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1721f777313e23b7b202fa7491480968","location":{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":29,"end":64}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `copy_as_gfm.js` has 400 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"ac03b65c923edc62a2698fa8d47bed20","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":1,"end":510}},"other_locations":[],"remediation_points":3360000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `transformCodeSelection` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c2c7d04215c2bd2989380d4ec16d2fdd","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":392,"end":428}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `nodeToGFM` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"203624a0d90ac2910cd61598bd1ba9c4","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":430,"end":473}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`gfmRules` has 23 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"4cef7679aa1f25cb0fb0333b4dfea466","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":169,"end":306}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `transformCodeSelection` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cc79eaa8e95f40163769d11480f6ca8c","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":392,"end":428}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `nodeToGFM` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"abacf74acfb9ae1429676cb952d5250f","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":430,"end":473}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":472,"end":472}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"4dc32f63393b2dba3bb2b4186a5a9d3c"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initPageShortcuts` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7e91b97abbe33722ed743c916435f0ab","location":{"path":"app/assets/javascripts/behaviors/shortcuts.js","lines":{"begin":3,"end":35}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3992dd4b39964f74489f5037465f0968","location":{"path":"app/assets/javascripts/behaviors/shortcuts/shortcuts.js","lines":{"begin":18,"end":51}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `updateTopState` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4115079d252dabe584b1839baffa5696","location":{"path":"app/assets/javascripts/issue.js","lines":{"begin":46,"end":74}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `bindEvents` has 99 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d911289330106e823f8b66a27a6de9a8","location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":38,"end":154}},"other_locations":[],"remediation_points":2376000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `chooseTemplate` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"38d61f9d4b3748a9e5ab863d54f4ccf8","location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":98,"end":130}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `search_autocomplete.js` has 417 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"92cc9af55c3a4ab73da8e1f0d3edd029","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":1,"end":506}},"other_locations":[],"remediation_points":3604800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `getData` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fc8158cb371adf854571129bd363b7ef","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":149,"end":251}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `getCategoryContents` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d8c7fe125446a794240cdecc04543593","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":253,"end":305}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `onSearchInputKeyUp` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f647cc28b710d6de8a6b35f3f79f3ce","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":348,"end":382}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `onClick` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b118715fdcf7b3e7ac25f69bb1baa423","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":445,"end":458}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`SearchAutocomplete` has 28 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"4afabdab2d1c2b911d6a1c563638e8e6","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":71,"end":501}},"other_locations":[],"remediation_points":2000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setSearchOptions` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ba3145218ad79282dc1d42a52ae16802","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":32,"end":69}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"27f6673ed668afd5ac9f3fd3c02a5e40","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":72,"end":103}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getData` has 86 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a21aceb13c361e2e3e0299baa560bed5","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":149,"end":251}},"other_locations":[],"remediation_points":2064000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getCategoryContents` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"415e6e1a246f7b01199ed828bdebb278","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":253,"end":305}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onSearchInputKeyUp` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"438f5589be7d3c9865113320e8a85fc4","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":348,"end":382}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `highlighter` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"034f805c8a510c0c46e35c2e4fb8d329","location":{"path":"app/assets/javascripts/project_find_file.js","lines":{"begin":10,"end":32}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `selectRow` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b2917d9725004429f0a4319beac0822e","location":{"path":"app/assets/javascripts/project_find_file.js","lines":{"begin":123,"end":143}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `constructor` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b590709d469cb1c8c28f467e53d2154","location":{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":8,"end":58}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 48 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"af0c07eddd69b8e22c3e13c7474122a1","location":{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":8,"end":58}},"other_locations":[],"remediation_points":1152000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleDiff` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"098b6e689349244e7adddfcad90974a3","location":{"path":"app/assets/javascripts/single_file_diff.js","lines":{"begin":41,"end":62}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onSaveClicked` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"18922f177ff6315e508e19e5d7a6cd4d","location":{"path":"app/assets/javascripts/ci_variable_list/ajax_variable_list.js","lines":{"begin":53,"end":90}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cf8b6d0bf24fc15e2c0e87e1c6b6a636","location":{"path":"app/assets/javascripts/ci_variable_list/ci_variable_list.js","lines":{"begin":19,"end":62}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `jumpToNextUnresolvedDiscussion` has a Cognitive Complexity of 54 (exceeds 5 allowed). Consider refactoring.","fingerprint":"792eaacd2770c50c0a42bce535c7bee9","location":{"path":"app/assets/javascripts/diff_notes/components/jump_to_discussion.js","lines":{"begin":61,"end":208}},"other_locations":[],"remediation_points":5050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `jumpToNextUnresolvedDiscussion` has 102 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"784d361fd1ec4da2fc3be0e85d1e354e","location":{"path":"app/assets/javascripts/diff_notes/components/jump_to_discussion.js","lines":{"begin":61,"end":208}},"other_locations":[],"remediation_points":2448000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `buttonText` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5b0d7c1b1146fa7930c3c14fef72db27","location":{"path":"app/assets/javascripts/diff_notes/components/comment_resolve_btn.js","lines":{"begin":31,"end":45}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `diffNotesCompileComponents` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ec443ad0f520ec34248990713c0f0b5a","location":{"path":"app/assets/javascripts/diff_notes/diff_notes_bundle.js","lines":{"begin":30,"end":68}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `queryTimeSeries` has 7 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"01a425e5bd2ad64d006f35854f477ad0","location":{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":33,"end":33}},"other_locations":[],"remediation_points":525000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `queryTimeSeries` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f1015e96e281a9a77bdaeeba619aa36b","location":{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":33,"end":165}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `queryTimeSeries` has 110 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c99f8cbe0230e2f1d48ff4478f2117c4","location":{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":33,"end":165}},"other_locations":[],"remediation_points":2640000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `timeScaleFormat` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f52e997312984f599f152d3260bf8654","location":{"path":"app/assets/javascripts/monitoring/utils/date_time_formatters.js","lines":{"begin":22,"end":42}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"complex_logic","content":{"body":""},"description":"Consider simplifying this complex logical expression.","location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":95,"end":134}},"other_locations":[],"remediation_points":800000,"severity":"critical","type":"issue","engine_name":"structure","fingerprint":"531fa4647bfb14d1172613e86257dd28"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.SET_LINE_DISCUSSIONS_FOR_FILE` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f6d1e6ad89a6aec9b87ec051dfa0099d","location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":88,"end":135}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.REMOVE_LINE_DISCUSSIONS_FOR_FILE` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"967d987657c3659898f377b3d07be15b","location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":137,"end":165}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `types.SET_LINE_DISCUSSIONS_FOR_FILE` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"08263cf95339fef038c9b82b56e70844","location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":88,"end":135}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `startRenderDiffsQueue` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9d28a6db58513b7ea1157b081952903c","location":{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":58,"end":83}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `addLineReferences` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"782481c579d96128e5d0793c04d6113b","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":110,"end":145}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `prepareDiffData` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"69f60a55e02467d84573c99a4caa96e1","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":191,"end":224}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getNoteFormData` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2408381b663271af2ce36e4cfec0ef64","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":28,"end":77}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addLineReferences` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9e04f00e197ce4b77a3fa65c55e66cb1","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":110,"end":145}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `prepareDiffData` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4fa4b2e2a939b7a261a1e47aea3b124e","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":191,"end":224}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `shouldRenderParallelCommentRow` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cf200732380b3f0a2a2993c8cb3d3a71","location":{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":75,"end":97}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initDiffsApp` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"37689c9906180d81c8f636a1285361fa","location":{"path":"app/assets/javascripts/diffs/index.js","lines":{"begin":6,"end":41}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `create_merge_request_dropdown.js` has 391 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"efe0e6e60956f829c50aea15ff89a81c","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":1,"end":489}},"other_locations":[],"remediation_points":3230400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `onChangeInput` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"51e3d7e3f8979e2acacf219907cfae33","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":278,"end":322}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `updateInputState` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"15a5141561c69faaf772cc24838b9882","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":433,"end":472}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`CreateMergeRequestDropdown` has 32 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"6d33b610bd5704267ec05a9b7b5e44e0","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":15,"end":488}},"other_locations":[],"remediation_points":2400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9408c08016dc994d8ab6445d06b9e1ba","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":16,"end":61}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onChangeInput` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e5627e01c10e2d6e1299f91b38024a0a","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":278,"end":322}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `updateInputState` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5126386129e905b8737efa7b7aadb02e","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":433,"end":472}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":321,"end":321}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"8643526d248036a3735dc9a6e2e57920"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addToImport` has 48 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f5c4a1a17a8903f8c6dd885142d0e5a7","location":{"path":"app/assets/javascripts/importer_status.js","lines":{"begin":33,"end":92}},"other_locations":[],"remediation_points":1152000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `autoUpdate` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"86f5ee9b3a803d7476f4c94f06eeb20f","location":{"path":"app/assets/javascripts/importer_status.js","lines":{"begin":94,"end":123}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `deviseState` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cc96fa07083c1de787a6bff9f39300d8","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":3,"end":34}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `deviseState` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b9fdc968681b9a4d1a00bdaebcf5513e","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":3,"end":34}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":13,"end":13}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"7e01335e6a48fbcffe9b977a5893e0c1"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":15,"end":15}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"06b18956af744e4ba0818ef408ae519a"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":17,"end":17}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"b6db36d200c40a47864a6c82d74ecdd9"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":19,"end":19}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"94cbbf89dae775243077f30c7139777f"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":21,"end":21}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"407c4389388a40459b3a50e566ec4695"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":23,"end":23}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"c9317c489fdab4a76a50dbc5cebcfa17"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":25,"end":25}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"d69b63a794f4784245d6a949c148e1c8"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":27,"end":27}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"766dbcf000fa7d4e2636539267ea8cee"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":29,"end":29}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"9c3dd1c5c99c0b2e367eaf0f6c483688"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":31,"end":31}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"9735ac3532df3e31eea793eb471bcd3e"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":33,"end":33}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"6a3a4a85c8ee79ec2d8e4ea2cf165674"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setData` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c334ad6c327b60fc52e19d4b1a441be0","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js","lines":{"begin":14,"end":114}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setData` has 88 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"590c6f1bfea578d864bdc64a2fa9a1da","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js","lines":{"begin":14,"end":114}},"other_locations":[],"remediation_points":2112000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `groupsSelect` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6d30e631abef41c7858e8c2b472ab41b","location":{"path":"app/assets/javascripts/groups_select.js","lines":{"begin":6,"end":88}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `groupsSelect` has 74 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ced5ccd57fcd0e841cc27868cc850186","location":{"path":"app/assets/javascripts/groups_select.js","lines":{"begin":6,"end":88}},"other_locations":[],"remediation_points":1776000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setAjaxGroupsSelect2` has 71 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cb6c68e362058d81fc462714f431d13d","location":{"path":"app/assets/javascripts/groups_select.js","lines":{"begin":9,"end":87}},"other_locations":[],"remediation_points":1704000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setConfig` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"93dedf1db90bd5e014f609731004784e","location":{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":25,"end":60}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `memberExpirationDate` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"93c026069e6f3fae025e1bf3bf480e0b","location":{"path":"app/assets/javascripts/member_expiration_date.js","lines":{"begin":10,"end":54}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_request_tabs.js` has 311 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"91dcee71900ea33c229c8f115d145f57","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":1,"end":477}},"other_locations":[],"remediation_points":2078400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `constructor` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2c515fc51ceabbbd6bcacbadf7b409c8","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":71,"end":122}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `tabShown` has a Cognitive Complexity of 29 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ca9c966746914ecd639f907aed00306a","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":159,"end":223}},"other_locations":[],"remediation_points":2550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5e286159c60ca68824ec344d5dec0145","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":71,"end":122}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `tabShown` has 56 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5752347dcb1fd9940bd4bc3d21abd442","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":159,"end":223}},"other_locations":[],"remediation_points":1344000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `loadDiff` has 52 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3eedd69a3c93529e5939b19ecf1b3e32","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":331,"end":401}},"other_locations":[],"remediation_points":1248000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleLabelPriority` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"17bb8b4767aa6ee7a9db29cd4297dfd1","location":{"path":"app/assets/javascripts/label_manager.js","lines":{"begin":53,"end":94}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `toggleLabelPriority` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ac24663bc55eac3c9635bccf3a68dc5a","location":{"path":"app/assets/javascripts/label_manager.js","lines":{"begin":53,"end":94}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4cd0496900d30272b32ee2e3adf188ae","location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":11,"end":46}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `addIssue` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"70f0f8ea0547b7001e920932e4980ddc","location":{"path":"app/assets/javascripts/boards/models/list.js","lines":{"begin":164,"end":200}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addIssue` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7f1eced6b6dca9d960756de9b56ac3c4","location":{"path":"app/assets/javascripts/boards/models/list.js","lines":{"begin":164,"end":200}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `moveIssueInList` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"3726eea384e1d9e6d6d8eebb989b0a55","location":{"path":"app/assets/javascripts/boards/stores/boards_store.js","lines":{"begin":152,"end":152}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `moveIssueToList` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cc191b6dd17cde39e7dd162fd47e19c7","location":{"path":"app/assets/javascripts/boards/stores/boards_store.js","lines":{"begin":100,"end":144}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `moveIssueToList` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0501fa18f105e7c808db7348aee50cb4","location":{"path":"app/assets/javascripts/boards/stores/boards_store.js","lines":{"begin":100,"end":144}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `newListDropdownInit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8f897d86e7796b5b3d49f700124b5adb","location":{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":26,"end":82}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `newListDropdownInit` has 51 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f73b08dfbb16f388d03764aefc71b663","location":{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":26,"end":82}},"other_locations":[],"remediation_points":1224000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `projectSelect` has 74 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8ccd46aac4a6745374730b2c4f0f1a7f","location":{"path":"app/assets/javascripts/project_select.js","lines":{"begin":7,"end":87}},"other_locations":[],"remediation_points":1776000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `query` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8facf3a4b06c65c72b401a6ae241812e","location":{"path":"app/assets/javascripts/project_select.js","lines":{"begin":27,"end":64}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `swipe` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cd344682f57e11e94307c0ae71e31b21","location":{"path":"app/assets/javascripts/commit/image_file.js","lines":{"begin":118,"end":153}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onion-skin` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d98db2ac5d0ba5352c5501d8e616908a","location":{"path":"app/assets/javascripts/commit/image_file.js","lines":{"begin":154,"end":193}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `mergeUrlParams` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9586f62e5526e65ea5dea846d2d34814","location":{"path":"app/assets/javascripts/lib/utils/url_utility.js","lines":{"begin":19,"end":41}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `common_utils.js` has 421 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"5b2e1f175792cddc3122cb26f6f2901b","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":1,"end":647}},"other_locations":[],"remediation_points":3662400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `handleLocationHash` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"73cd83cbd5164515c6be6e51ced09b94","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":79,"end":112}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `urlParamsToObject` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"87d0167946e02e230645b1a8b4416926","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":152,"end":175}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `createOverlayIcon` has 40 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1195653ae898b98df0d276acb1429b55","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":458,"end":505}},"other_locations":[],"remediation_points":960000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onload` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d3e5a07a00d95bb2f853dee0c1c50e98","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":462,"end":502}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `isSticky` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b5704d88401ccd1910b07573165064f6","location":{"path":"app/assets/javascripts/lib/utils/sticky.js","lines":{"begin":10,"end":31}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `insertMarkdownText` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"1ff368be1929703177fe923eb38be8a2","location":{"path":"app/assets/javascripts/lib/utils/text_markdown.js","lines":{"begin":54,"end":54}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `moveCursor` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"78da0046365fa4a2a913735a7884c4fe","location":{"path":"app/assets/javascripts/lib/utils/text_markdown.js","lines":{"begin":34,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `insertMarkdownText` has a Cognitive Complexity of 18 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9c17d7ff9d0dfd043e0ac8b46e612742","location":{"path":"app/assets/javascripts/lib/utils/text_markdown.js","lines":{"begin":54,"end":111}},"other_locations":[],"remediation_points":1450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `insertMarkdownText` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f38a0d7dccba49d8195065ab71f8ef39","location":{"path":"app/assets/javascripts/lib/utils/text_markdown.js","lines":{"begin":54,"end":111}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleScroll` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a1904c8e94b053af5f2371dece57951f","location":{"path":"app/assets/javascripts/lib/utils/logoutput_behaviours.js","lines":{"begin":14,"end":41}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getMonthNames` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5630d70a3018ea651454e11d959c7e26","location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":16,"end":47}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getTimeago` has 42 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"915cddbb51190cedb7c5488e6d3f715a","location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":77,"end":122}},"other_locations":[],"remediation_points":1008000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `dateInWords` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"35c8c58c18a9af9c0ee997ae5d4b8e48","location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":201,"end":243}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initKeyNav` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"90bcaab65ebc8665cd94e8e6286ad7d3","location":{"path":"app/assets/javascripts/tree.js","lines":{"begin":28,"end":66}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `generateUnicodeSupportMap` has 47 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3235c3b88f6c553ff280eac9faec19fa","location":{"path":"app/assets/javascripts/emoji/support/unicode_support_map.js","lines":{"begin":77,"end":141}},"other_locations":[],"remediation_points":1128000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getUnicodeSupportMap` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d9342f9326c59d8e19277db8a2cb8500","location":{"path":"app/assets/javascripts/emoji/support/unicode_support_map.js","lines":{"begin":143,"end":178}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `glEmojiTag` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"580f88e3583b6c310ded6fbad9390305","location":{"path":"app/assets/javascripts/emoji/index.js","lines":{"begin":69,"end":102}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initDropdown` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"301e9b71ddb9969243fc57daa227b33b","location":{"path":"app/assets/javascripts/sidebar/lib/sidebar_move_issue.js","lines":{"begin":27,"end":59}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `branch_graph.js` has 307 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"8f961e52cddbca0cb2dd3290c7063853","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":1,"end":352}},"other_locations":[],"remediation_points":2020800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `buildGraph` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"18ed687a5ecc08e272ec3f5a1e45ba85","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":103,"end":139}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderPartialGraph` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"790548e7db64ebbadd07868d6c20245b","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":141,"end":171}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `appendLabel` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"918265d9fa262a2ea6ddda4b96f04407","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":211,"end":246}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `drawLines` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"df364db884043c3c81e753fb9a58ee90","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":286,"end":333}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `testWrap` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"65093271e08f9838db54de85e17d7c79","location":{"path":"app/assets/javascripts/network/raphael.js","lines":{"begin":37,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `commitTooltip` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c2753572072946d03cae221981b2a147","location":{"path":"app/assets/javascripts/network/raphael.js","lines":{"begin":3,"end":35}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `testWrap` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3ac8fefe6574f998844a591c4dff399c","location":{"path":"app/assets/javascripts/network/raphael.js","lines":{"begin":37,"end":72}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.TOGGLE_LOADING` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"41230e2a140c3c519f16d17067a948c0","location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":14,"end":24}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.RENAME_ENTRY` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"65c7f9e9aa2de7fa4e24860dbded9820","location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":214,"end":263}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`` has 26 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"ee3f6d8624f678d9741d386c4252f0fc","location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":10,"end":269}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `types.RENAME_ENTRY` has 40 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"98f116d65b4aa0e565234027efee827f","location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":214,"end":263}},"other_locations":[],"remediation_points":960000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.SET_FILE_RAW_DATA` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6460bb9792e8eedb0b1e558f229e6ffa","location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":53,"end":75}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.DISCARD_FILE_CHANGES` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"aa62953384fc873bd8bddabed1341a3a","location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":128,"end":154}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `closeFile` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ca626095e258a497e9cf9c1fa4b7537e","location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":10,"end":40}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `createCommitPayload` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ead12c6a7c7c105251c94c6af2fda7b0","location":{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":133,"end":145}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `decorateData` has 44 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f8abacda37ac495ce54d8ce69e488724","location":{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":55,"end":101}},"other_locations":[],"remediation_points":1056000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":158,"end":158}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"75e7e39f5b8ea0aadff2470a9b44ca68"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `entries` has 67 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1a9caa4d3fce207f88b2afafe14a1df0","location":{"path":"app/assets/javascripts/ide/stores/workers/files_decorator_worker.js","lines":{"begin":11,"end":91}},"other_locations":[],"remediation_points":1608000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.RECEIVE_LASTEST_PIPELINE_SUCCESS` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ac7ea082c786419244a234de0970ebc9","location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":11,"end":39}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `types.RECEIVE_LASTEST_PIPELINE_SUCCESS` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0883c6639137c6b51cad4b57c0dd1c38","location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":11,"end":39}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0d98be7e83cb9dbc1ca8775da0aa7d19","location":{"path":"app/assets/javascripts/ide/lib/common/model.js","lines":{"begin":6,"end":43}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initIde` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"34f27cc8f1c849369b0095674a48c531","location":{"path":"app/assets/javascripts/ide/index.js","lines":{"begin":11,"end":44}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `init` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"0c6f3babcfd2a42ae88ffe4cc05028dc","location":{"path":"app/assets/javascripts/pager.js","lines":{"begin":10,"end":10}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f19d7f17b74ab07e9651e12680b14b9d","location":{"path":"app/assets/javascripts/profile/gl_crop.js","lines":{"begin":12,"end":40}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onModalShow` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c0421daee7918d81456c8580b8878457","location":{"path":"app/assets/javascripts/profile/gl_crop.js","lines":{"begin":69,"end":101}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initMrNotes` has 74 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4dd3a8bed3022600ffaa03ba5cc77fe6","location":{"path":"app/assets/javascripts/mr_notes/index.js","lines":{"begin":10,"end":92}},"other_locations":[],"remediation_points":1776000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`` has 24 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"815b15d961f84ea742e6dce8eb3f8a4d","location":{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":14,"end":167}},"other_locations":[],"remediation_points":1600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cbc52cfd6436c771188ac8f4e355cb9b","location":{"path":"app/validators/branch_filter_validator.rb","lines":{"begin":16,"end":30}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a8179f4c2dfe625c27616c834d37a930","location":{"path":"app/validators/js_regex_validator.rb","lines":{"begin":4,"end":16}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d1ca6a4b7e3294fdc110a8e0ea47f92","location":{"path":"app/validators/cluster_name_validator.rb","lines":{"begin":7,"end":25}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d62539a99ae9fabba3cd2cc96713cf09","location":{"path":"app/validators/abstract_path_validator.rb","lines":{"begin":23,"end":35}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ProjectTeam` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"ce2cc10520ba3b4b7205e61120d67315","location":{"path":"app/models/project_team.rb","lines":{"begin":3,"end":194}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_kube_client!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e929847f530fa79a8a58054b9aa3fb8e","location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":125,"end":140}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `note.rb` has 325 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"5f0768574ccb05a5f08cf2e994799fa2","location":{"path":"app/models/note.rb","lines":{"begin":6,"end":474}},"other_locations":[],"remediation_points":2280000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `grouped_diff_discussions` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3e55577fec2bb70f0e880e0e8d0f42ec","location":{"path":"app/models/note.rb","lines":{"begin":150,"end":168}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `in_reply_to?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a17100ec3750af84df807aff92514aa5","location":{"path":"app/models/note.rb","lines":{"begin":367,"end":382}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Note` has 56 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"5d5c3b8714fe08a2c7df66bc4bfdc538","location":{"path":"app/models/note.rb","lines":{"begin":6,"end":474}},"other_locations":[],"remediation_points":4800000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `track_greatest` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"ee586dc1932745869e3f752d180242c9","location":{"path":"app/models/internal_id.rb","lines":{"begin":55,"end":55}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `rename_descendants` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"41d2108d52e96b7975591b300d3b2e60","location":{"path":"app/models/route.rb","lines":{"begin":23,"end":51}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"complex_logic","content":{"body":""},"description":"Consider simplifying this complex logical expression.","location":{"path":"app/models/issue.rb","lines":{"begin":306,"end":313}},"other_locations":[],"remediation_points":400000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"1cd63fdd17237f7c081abfa37690a7fe"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `as_json` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"430a48264a7379f4d248d672bb16fe44","location":{"path":"app/models/issue.rb","lines":{"begin":250,"end":273}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `readable_by?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"05cd07a39aaac900028cc3b49079c531","location":{"path":"app/models/issue.rb","lines":{"begin":301,"end":315}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Issue` has 30 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"9619613b02f7bb8841b9e19c688f5251","location":{"path":"app/models/issue.rb","lines":{"begin":5,"end":326}},"other_locations":[],"remediation_points":2200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cached_attr_reader` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a0b7bc4cebaa36fff63efc3ada11817","location":{"path":"app/models/concerns/redis_cacheable.rb","lines":{"begin":10,"end":20}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `move_dir` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a75dea312f0b07cc41974f687ba6fc64","location":{"path":"app/models/concerns/storage/legacy_namespace.rb","lines":{"begin":7,"end":41}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `expand_hierarchy_for_child` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f5222419d454a07f66c83180e4789d18","location":{"path":"app/models/concerns/group_descendant.rb","lines":{"begin":37,"end":70}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `expand_hierarchy_for_child` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5cd85b2e5055a268df55df99d4d5a94e","location":{"path":"app/models/concerns/group_descendant.rb","lines":{"begin":37,"end":70}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `avatar_path` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b6177dbe195b50f7a5b10734fbc4a8d2","location":{"path":"app/models/concerns/avatarable.rb","lines":{"begin":45,"end":72}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `with_reactive_cache` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a8e8f8a2e9ccd006ae8f61ca9db92d0c","location":{"path":"app/models/concerns/reactive_caching.rb","lines":{"begin":69,"end":84}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `exclusively_update_reactive_cache!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2811b2ef3d5196f0b89cec43f1f2b465","location":{"path":"app/models/concerns/reactive_caching.rb","lines":{"begin":91,"end":103}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_binary_column_exists!` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"23444612f22a67bddc69d4fb3d9ab40a","location":{"path":"app/models/concerns/sha_attribute.rb","lines":{"begin":18,"end":37}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `current` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"37517b27e64777d7506564ebffc2a9fa","location":{"path":"app/models/concerns/cacheable_attributes.rb","lines":{"begin":44,"end":57}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `chronic_duration_attr_writer` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8acb577138f44873e03ae5712c65f31d","location":{"path":"app/models/concerns/chronic_duration_attribute.rb","lines":{"begin":13,"end":28}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `truncated_diff_lines` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8bade7fdd3ab51990a34487352c56728","location":{"path":"app/models/concerns/discussion_on_diff.rb","lines":{"begin":41,"end":59}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `calculate_reactive_cache` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ff34ec3cc17426f2d12b615f23171aad","location":{"path":"app/models/concerns/prometheus_adapter.rb","lines":{"begin":36,"end":47}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `position_between` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0f9f931c8a1748472719ac1b03432d6c","location":{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":119,"end":139}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `status_sql` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"46850ccc347015917b7f7d5813dfbb20","location":{"path":"app/models/concerns/has_status.rb","lines":{"begin":19,"end":46}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `has_internal_id` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1753643cebf17fe9b88665c4895f0bc6","location":{"path":"app/models/concerns/atomic_internal_id.rb","lines":{"begin":30,"end":56}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_by_full_path` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9de369584b4bc5dcd50146b7967ef3b0","location":{"path":"app/models/concerns/routable.rb","lines":{"begin":35,"end":60}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `where_full_path_in` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1d7711a695fff8bdfaa49dc578b0132c","location":{"path":"app/models/concerns/routable.rb","lines":{"begin":69,"end":91}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `manual_inverse_association` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"53ad262470fd01998ef23cb50fac0495","location":{"path":"app/models/concerns/manual_inverse_association.rb","lines":{"begin":7,"end":17}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `raw_participants` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"014b735c6a47b089e8ef4112c427cdf6","location":{"path":"app/models/concerns/participable.rb","lines":{"begin":72,"end":104}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_batch` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5ca9b6fe345855869ed62b41036772f6","location":{"path":"app/models/concerns/each_batch.rb","lines":{"begin":42,"end":81}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `each_batch` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e56746932db5150f690993cd09ec2c5a","location":{"path":"app/models/concerns/each_batch.rb","lines":{"begin":42,"end":81}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_authentication_token_field` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9ffe8c5e6736ff7519d2b0ad210f55dd","location":{"path":"app/models/concerns/token_authenticatable.rb","lines":{"begin":31,"end":60}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `to_hook_data` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"651dd445a68aa882af831870aac09fe6","location":{"path":"app/models/concerns/issuable.rb","lines":{"begin":264,"end":290}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `notes_with_associations` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"edf734b8128847bcceb4300c584ab8e9","location":{"path":"app/models/concerns/issuable.rb","lines":{"begin":318,"end":334}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `max_member_access_for_resource_ids` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9a3525a1273d674a7a5dd1b4599a6b80","location":{"path":"app/models/concerns/bulk_member_access_load.rb","lines":{"begin":12,"end":40}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `enum_with_nil` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d195530dc1d564b6a5a2361310657dd7","location":{"path":"app/models/concerns/enum_with_nil.rb","lines":{"begin":7,"end":33}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `labels_str` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bf445c84a1f8ebfebacc811382517d81","location":{"path":"app/models/label_note.rb","lines":{"begin":77,"end":90}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `notifiable?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"76278bc24d3f0c41a172b68e2f632daa","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":31,"end":46}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `has_access?` has a Cognitive Complexity of 22 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4264d898878128898c4e12b32b905f8a","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":88,"end":101}},"other_locations":[],"remediation_points":1850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_notification_setting` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cf0f0817b180212c8e825cbbed40b752","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":144,"end":154}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `closest_non_global_group_notification_settting` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9f65343a59de0315d68462b18f90defe","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":157,"end":169}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":43,"end":43}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"d25d7135d797df677ee520a8a97ae9a6"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `find_commits_by_message` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"0c28ee46d93aa21d37eeb5b2a26d394f","location":{"path":"app/models/repository.rb","lines":{"begin":161,"end":161}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `repository.rb` has 758 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"849ce5e0e3a20d8ed78311435ce5fce9","location":{"path":"app/models/repository.rb","lines":{"begin":3,"end":1058}},"other_locations":[],"remediation_points":8515200,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `keep_around` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8ffb83b66ddf0a719303059ae40ce506","location":{"path":"app/models/repository.rb","lines":{"begin":249,"end":262}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tree` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"38d273d7d1372c09c7a5c165e170a642","location":{"path":"app/models/repository.rb","lines":{"begin":647,"end":659}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `next_branch` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"53a66b748f45002bcdd585986f4f828f","location":{"path":"app/models/repository.rb","lines":{"begin":684,"end":697}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Repository` has 128 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"1ffa67f7557cad6dc009a3331141e757","location":{"path":"app/models/repository.rb","lines":{"begin":5,"end":1058}},"other_locations":[],"remediation_points":12000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `enabled` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"38454ee90e05a227558719509fe5ec29","location":{"path":"app/models/remote_mirror.rb","lines":{"begin":104,"end":111}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `RemoteMirror` has 24 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"24334db25a09b73ccd711bb06c29a822","location":{"path":"app/models/remote_mirror.rb","lines":{"begin":3,"end":229}},"other_locations":[],"remediation_points":1600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9c206e4584e2321188004de82f54d910","location":{"path":"app/models/commit_range.rb","lines":{"begin":62,"end":82}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `user.rb` has 1050 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"11e77959335527c9cb28992c10d37866","location":{"path":"app/models/user.rb","lines":{"begin":3,"end":1502}},"other_locations":[],"remediation_points":12720000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `increment_failed_attempts!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"51accad62fa466380528844722430a98","location":{"path":"app/models/user.rb","lines":{"begin":1253,"end":1264}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_user_rights_and_limits` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c7a575c65331c7b917638baa8941d41c","location":{"path":"app/models/user.rb","lines":{"begin":1405,"end":1414}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `signup_domain_valid?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a9b5f7bc9d9c55cfe5d888bed2fb420a","location":{"path":"app/models/user.rb","lines":{"begin":1416,"end":1441}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `User` has 178 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"f685bcbb6f822884dec234767db1861a","location":{"path":"app/models/user.rb","lines":{"begin":5,"end":1502}},"other_locations":[],"remediation_points":17000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `project.rb` has 1664 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"531155f760c7355b54c66683855d1397","location":{"path":"app/models/project.rb","lines":{"begin":3,"end":2277}},"other_locations":[],"remediation_points":21561600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_or_update_import_data` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"669b48913bc191fe053ef3cf863c7b51","location":{"path":"app/models/project.rb","lines":{"begin":691,"end":706}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_import_state` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c647270223f46682704c8933072ea030","location":{"path":"app/models/project.rb","lines":{"begin":732,"end":745}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_limit` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"238a83e94e0d19d7e5bf774e737f1856","location":{"path":"app/models/project.rb","lines":{"begin":887,"end":899}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_or_initialize_services` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b5c439340a2145fd43e3c155a46b8084","location":{"path":"app/models/project.rb","lines":{"begin":1084,"end":1110}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_repository_path_availability` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"84ad9cf46f3288017a0f4b6db6438a6c","location":{"path":"app/models/project.rb","lines":{"begin":1302,"end":1316}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `after_create_default_branch` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a2d29b10f840adb402e27183b8afb0e","location":{"path":"app/models/project.rb","lines":{"begin":1685,"end":1704}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `container_registry_variables` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"22330faec24429812d37099efaf8566b","location":{"path":"app/models/project.rb","lines":{"begin":1811,"end":1821}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_update_attribute_error` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"103e8043443b236e57b2b71b4071ed2a","location":{"path":"app/models/project.rb","lines":{"begin":2240,"end":2250}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fetch_branch_allows_collaboration?` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0a44b195efa5fdd8f8a133a9382fe540","location":{"path":"app/models/project.rb","lines":{"begin":2252,"end":2276}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Project` has 258 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"64039fbe3749f7d87b4ba7e85868d7fc","location":{"path":"app/models/project.rb","lines":{"begin":5,"end":2277}},"other_locations":[],"remediation_points":25000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `service.rb` has 261 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"f45455ad20cdb5129a3bfb9eebb6498b","location":{"path":"app/models/service.rb","lines":{"begin":5,"end":346}},"other_locations":[],"remediation_points":1358400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Service` has 40 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"96ce9f4a9d59b553ec55116c8a3be7ae","location":{"path":"app/models/service.rb","lines":{"begin":5,"end":346}},"other_locations":[],"remediation_points":3200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `available_services_names` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f2ec9d5c5ae446fb75d746688a3a72fe","location":{"path":"app/models/service.rb","lines":{"begin":247,"end":284}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `raw_binary?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0ae3aeb7a760efb26099272499cfbade","location":{"path":"app/models/blob.rb","lines":{"begin":161,"end":175}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Blob` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"a44f4d9e927f1b91399b8e17fb943f6d","location":{"path":"app/models/blob.rb","lines":{"begin":4,"end":254}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `event.rb` has 309 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"6850b538f65ad91cf5c5ef2e7148924e","location":{"path":"app/models/event.rb","lines":{"begin":3,"end":410}},"other_locations":[],"remediation_points":2049600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `visible_to_user?` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"29269e08d03641e18bd2ba7cd22a0d3f","location":{"path":"app/models/event.rb","lines":{"begin":151,"end":167}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `action_name` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f28caae33540f5326e5ce2d58a03d771","location":{"path":"app/models/event.rb","lines":{"begin":265,"end":287}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Event` has 50 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"613c580b618dad28f34b389f766efe2e","location":{"path":"app/models/event.rb","lines":{"begin":3,"end":410}},"other_locations":[],"remediation_points":4200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `different_group` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2ac2c358921313bb5981e964133c5ecf","location":{"path":"app/models/project_group_link.rb","lines":{"begin":38,"end":49}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `merge_request_version_params` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8d1a8357abf058d725c59df9cca495bf","location":{"path":"app/models/diff_discussion.rb","lines":{"begin":25,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `active?` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"772f6211e69bcffb4fbc411c5d7d31c3","location":{"path":"app/models/legacy_diff_note.rb","lines":{"begin":57,"end":75}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/models/legacy_diff_note.rb","lines":{"begin":62,"end":62}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"4e0039e61f3fd67caeb1b978db868447"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `group.rb` has 290 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"d9e231fdbc15e514e21e1596b5e2d930","location":{"path":"app/models/group.rb","lines":{"begin":3,"end":403}},"other_locations":[],"remediation_points":1776000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Group` has 53 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"f82613684deaaf389431887bc5a62c74","location":{"path":"app/models/group.rb","lines":{"begin":5,"end":403}},"other_locations":[],"remediation_points":4500000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Label` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"9660fb59aa00ace0b608078c78c611a4","location":{"path":"app/models/label.rb","lines":{"begin":3,"end":244}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `has_intermediates?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fc033b957f3fcb1bd690b7edb50aa441","location":{"path":"app/models/pages_domain.rb","lines":{"begin":79,"end":98}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `PagesDomain` has 24 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"389a0c10cffbc7cd770d8d67a31e61ba","location":{"path":"app/models/pages_domain.rb","lines":{"begin":3,"end":201}},"other_locations":[],"remediation_points":1600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `generate_slug` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d507615693230a051c9d4ae397fe5d2","location":{"path":"app/models/environment.rb","lines":{"begin":178,"end":203}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Environment` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"4140683d4224f01a7fe2fad0da3d11c6","location":{"path":"app/models/environment.rb","lines":{"begin":3,"end":243}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ProjectWiki` has 29 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"a7ff4b093a326a93bf1e0887a36ba898","location":{"path":"app/models/project_wiki.rb","lines":{"begin":3,"end":203}},"other_locations":[],"remediation_points":2100000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `track` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d55ffd71690c0e606bcb1fa48074affd","location":{"path":"app/models/user_interacted_project.rb","lines":{"begin":16,"end":41}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8737f9bae4ea49696d911b5b162784ef","location":{"path":"app/models/wiki_page.rb","lines":{"begin":215,"end":244}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_title` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"43c91a5205fef35946442db85ab54267","location":{"path":"app/models/wiki_page.rb","lines":{"begin":283,"end":295}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `WikiPage` has 35 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"35e7d7ce82a5641563ba1029c54645bc","location":{"path":"app/models/wiki_page.rb","lines":{"begin":4,"end":326}},"other_locations":[],"remediation_points":2700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_position` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fa4f67c27ab14379e44af70d58264c1a","location":{"path":"app/models/diff_note.rb","lines":{"begin":152,"end":173}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `append` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d527adfd814d095e55079d7a898a3fc3","location":{"path":"app/models/ci/build_trace_chunk.rb","lines":{"begin":74,"end":84}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `pipeline.rb` has 488 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"cee8532683fbbb22a3b807c5714c49c7","location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":3,"end":671}},"other_locations":[],"remediation_points":4627200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ci_yaml_from_repo` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f19177ae4080cc2c9d81a39383b45922","location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":632,"end":639}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Pipeline` has 68 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"af1e8c39d8b166628ae043bce72269a5","location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":4,"end":670}},"other_locations":[],"remediation_points":6000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `build.rb` has 604 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"7967fd1fb861d27e33c208d88261cd76","location":{"path":"app/models/ci/build.rb","lines":{"begin":3,"end":809}},"other_locations":[],"remediation_points":6297600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `scoped_variables` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e310e4c57a2e9123f26a1cbcec9809eb","location":{"path":"app/models/ci/build.rb","lines":{"begin":312,"end":327}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `predefined_variables` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"13282d4d569e2daa02632e5249568733","location":{"path":"app/models/ci/build.rb","lines":{"begin":726,"end":745}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `legacy_variables` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"81e2fd25bb2dd91faed640216e965698","location":{"path":"app/models/ci/build.rb","lines":{"begin":747,"end":759}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `persisted_environment_variables` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"67327cbc303019830fbd1d6586dcde3e","location":{"path":"app/models/ci/build.rb","lines":{"begin":761,"end":772}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Build` has 100 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"736c24725c78f957ee4538a1ef73cab0","location":{"path":"app/models/ci/build.rb","lines":{"begin":4,"end":808}},"other_locations":[],"remediation_points":9200000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `status` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c700cdde3d0b6292dd18227b3e90cf8d","location":{"path":"app/models/ci/runner.rb","lines":{"begin":179,"end":187}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Runner` has 31 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"a9c3d8a2409fd9c5506a846374c16d42","location":{"path":"app/models/ci/runner.rb","lines":{"begin":4,"end":320}},"other_locations":[],"remediation_points":2300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Discussion` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"21189089f43b68ee94981dbac970d566","location":{"path":"app/models/discussion.rb","lines":{"begin":6,"end":138}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestone_format_reference` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0e735e47b6b8a533b9ff579e13b277c9","location":{"path":"app/models/milestone.rb","lines":{"begin":261,"end":273}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Milestone` has 26 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"283d777de6e0e7974ccbb1e316f5fd1a","location":{"path":"app/models/milestone.rb","lines":{"begin":3,"end":288}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d0fba50527ad864dc4649245f95f1bf","location":{"path":"app/models/project_services/drone_ci_service.rb","lines":{"begin":22,"end":31}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `calculate_reactive_cache` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"aa74471515edc43c1ab5339ddf68e505","location":{"path":"app/models/project_services/drone_ci_service.rb","lines":{"begin":53,"end":74}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0a0446e31eb4d865b34fe5938f53bbad","location":{"path":"app/models/project_services/chat_notification_service.rb","lines":{"begin":52,"end":79}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_message` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07a9999f0d84368d5983e08e2891f237","location":{"path":"app/models/project_services/chat_notification_service.rb","lines":{"begin":111,"end":126}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `notify_for_ref?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6a030ca161112b5f434986ab0ae88c0f","location":{"path":"app/models/project_services/chat_notification_service.rb","lines":{"begin":159,"end":171}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ChatNotificationService` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"5d159cd60a69a3f1a1b5beb530264825","location":{"path":"app/models/project_services/chat_notification_service.rb","lines":{"begin":5,"end":183}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read_commit_status` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"69dbb9ff98b20cc8db3a809b7e239e5f","location":{"path":"app/models/project_services/mock_ci_service.rb","lines":{"begin":70,"end":84}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `KubernetesService` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"2e6c7a0448e9bb4e799cb7efe0846fc7","location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":8,"end":251}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read_commit_status` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8fb7749a6cc98bccd8d5712c1d499494","location":{"path":"app/models/project_services/teamcity_service.rb","lines":{"begin":112,"end":132}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize_properties` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a965dd277aee3504a57790eb7271cadb","location":{"path":"app/models/project_services/issue_tracker_service.rb","lines":{"begin":52,"end":69}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `buildkite_endpoint` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"161d47385d79643f9414ed2ad67502a9","location":{"path":"app/models/project_services/buildkite_service.rb","lines":{"begin":106,"end":119}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"42914696303830e3d209925e13650d83","location":{"path":"app/models/project_services/pushover_service.rb","lines":{"begin":67,"end":104}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `fields` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"773d8cd6842c7b74f60e9ab8003ad977","location":{"path":"app/models/project_services/pushover_service.rb","lines":{"begin":21,"end":61}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7b760b5c22532a27dbd776328b7db9a6","location":{"path":"app/models/project_services/pushover_service.rb","lines":{"begin":67,"end":104}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"802d66c5bd557b6f1bdd32240846e4c2","location":{"path":"app/models/project_services/pipelines_email_service.rb","lines":{"begin":28,"end":38}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `jira_service.rb` has 261 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"c5f438f6b31722193eb4e10b389655b6","location":{"path":"app/models/project_services/jira_service.rb","lines":{"begin":3,"end":344}},"other_locations":[],"remediation_points":1358400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `close_issue` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f74475ea9bd04b9ed8ff7ddcc5c29633","location":{"path":"app/models/project_services/jira_service.rb","lines":{"begin":117,"end":135}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_cross_reference_note` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6c27b1c9b77bd4f2d2f11a294e90cf36","location":{"path":"app/models/project_services/jira_service.rb","lines":{"begin":137,"end":167}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `JiraService` has 35 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"2d0643b626f1ad53bf43aadedd954eae","location":{"path":"app/models/project_services/jira_service.rb","lines":{"begin":3,"end":344}},"other_locations":[],"remediation_points":2700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read_commit_status` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f2f5469088e256c3b81eccc8ecaf967b","location":{"path":"app/models/project_services/bamboo_service.rb","lines":{"begin":94,"end":112}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_message` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fcb9db20f4fe6adda5925e7da947a943","location":{"path":"app/models/project_services/hipchat_service.rb","lines":{"begin":85,"end":100}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_push_message` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7f972562d25c4198e75c8dac6931a928","location":{"path":"app/models/project_services/hipchat_service.rb","lines":{"begin":106,"end":138}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `HipchatService` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"bb12852ed6419e4dea808d1d873302a1","location":{"path":"app/models/project_services/hipchat_service.rb","lines":{"begin":3,"end":311}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `create_note_message` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"96500251c9498d9f1e9c7da0c8384528","location":{"path":"app/models/project_services/hipchat_service.rb","lines":{"begin":200,"end":244}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `format_channel` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e064d1923f5ef24f603e3c03af81644f","location":{"path":"app/models/project_services/irker_service.rb","lines":{"begin":93,"end":112}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_commit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d6ee148a09cf8c383948472d577bfb74","location":{"path":"app/models/project_services/asana_service.rb","lines":{"begin":81,"end":108}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Namespace` has 34 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"411d817f1a6db72f360bb2cb93e055ec","location":{"path":"app/models/namespace.rb","lines":{"begin":3,"end":302}},"other_locations":[],"remediation_points":2600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `application_setting.rb` has 387 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"50270695e6d7c5163050078e7cf65e18","location":{"path":"app/models/application_setting.rb","lines":{"begin":3,"end":489}},"other_locations":[],"remediation_points":3172800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ApplicationSetting` has 38 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"02be76880912851331582ab14ef56dd4","location":{"path":"app/models/application_setting.rb","lines":{"begin":3,"end":489}},"other_locations":[],"remediation_points":3000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `defaults` has 76 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4a5e7733302a5f59e7878ab40f6b9f82","location":{"path":"app/models/application_setting.rb","lines":{"begin":232,"end":309}},"other_locations":[],"remediation_points":1824000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `base_commit_sha` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"70d6880ba8e52f184e9eaf896119e352","location":{"path":"app/models/compare.rb","lines":{"begin":50,"end":56}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `commit.rb` has 355 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"c361e4106ded93f757f69964f420b067","location":{"path":"app/models/commit.rb","lines":{"begin":4,"end":508}},"other_locations":[],"remediation_points":2712000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `order_by` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c4e54c836dbfddf9329a85f83378cb5","location":{"path":"app/models/commit.rb","lines":{"begin":63,"end":75}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `uri_type` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ac39422eaa05c0e3880afe76c6341f18","location":{"path":"app/models/commit.rb","lines":{"begin":425,"end":435}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Commit` has 68 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"e2175bec55ffd09f5d7b2352b5c5dcad","location":{"path":"app/models/commit.rb","lines":{"begin":4,"end":508}},"other_locations":[],"remediation_points":6000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_request.rb` has 897 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"59d143493fc6a1f49dd6ff46bba4459f","location":{"path":"app/models/merge_request.rb","lines":{"begin":3,"end":1238}},"other_locations":[],"remediation_points":10516800,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_branches` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"95d0d3fa4206c77bab101daf5035ee18","location":{"path":"app/models/merge_request.rb","lines":{"begin":511,"end":524}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_fork` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"68cf8c7f9bbbebf2133c22434d2bcae9","location":{"path":"app/models/merge_request.rb","lines":{"begin":532,"end":539}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `mergeable_state?` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bdc249b8456dc07607ba4fe001ff1261","location":{"path":"app/models/merge_request.rb","lines":{"begin":677,"end":685}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cache_merge_request_closes_issues!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bcd9dc4beba740e5c019b9c760cacdd3","location":{"path":"app/models/merge_request.rb","lines":{"begin":769,"end":782}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `compare_test_reports` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b662bb8a5b01a77feb8b7b0a9ffefdb2","location":{"path":"app/models/merge_request.rb","lines":{"begin":1041,"end":1054}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `mergeable_with_quick_action?` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"74fd9deeffa5589190ba744e4af78384","location":{"path":"app/models/merge_request.rb","lines":{"begin":1177,"end":1187}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `MergeRequest` has 137 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"417a10e5eb9495ae2d250b8c195971f7","location":{"path":"app/models/merge_request.rb","lines":{"begin":3,"end":1238}},"other_locations":[],"remediation_points":12900000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/models/merge_request.rb","lines":{"begin":682,"end":682}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"ac1600795e5aff84e3d4e9243b88bdbc"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/models/merge_request.rb","lines":{"begin":1184,"end":1184}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"4cc86fde220f02f2e1c1443c9c96fe93"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `member.rb` has 297 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"ead9a1c0eaecbe741f755dd85ef04b38","location":{"path":"app/models/member.rb","lines":{"begin":3,"end":419}},"other_locations":[],"remediation_points":1876800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `retrieve_member` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9cf98664280114d55fe07fc00a2931f4","location":{"path":"app/models/member.rb","lines":{"begin":236,"end":248}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Member` has 41 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"5c62b78b2bb4b49f04e64fa24a5e7525","location":{"path":"app/models/member.rb","lines":{"begin":3,"end":419}},"other_locations":[],"remediation_points":3300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `set` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8869fa571b5fb7a617adc0197c7ac362","location":{"path":"app/models/active_session.rb","lines":{"begin":20,"end":50}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `count_to_display_commit_in_center` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b411aa7c155fd11004316e7fd39fd0f0","location":{"path":"app/models/network/graph.rb","lines":{"begin":79,"end":107}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `place_chain` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"177c4e2e8eb1351747941148b8c5841f","location":{"path":"app/models/network/graph.rb","lines":{"begin":180,"end":216}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `take_left_leaves` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07eb1c8a024e1084d5249e442c56ead6","location":{"path":"app/models/network/graph.rb","lines":{"begin":263,"end":277}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `place_chain` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d89c4cb61eca0dac9322942ca4b87aff","location":{"path":"app/models/network/graph.rb","lines":{"begin":180,"end":216}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `save_diffs` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c6b7ee6cba58c9eb3d9ad5eed75d6ec3","location":{"path":"app/models/merge_request_diff.rb","lines":{"begin":272,"end":296}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `MergeRequestDiff` has 33 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d7a32bf8ffc7cc2cb0b417114258fc44","location":{"path":"app/models/merge_request_diff.rb","lines":{"begin":3,"end":322}},"other_locations":[],"remediation_points":2500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `object_storage.rb` has 315 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"86494fa0d557a4aa389e5171896783dc","location":{"path":"app/uploaders/object_storage.rb","lines":{"begin":3,"end":458}},"other_locations":[],"remediation_points":2136000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `changed_mounts` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fd0a4f3d9448639f581ace634e78fb9f","location":{"path":"app/uploaders/object_storage.rb","lines":{"begin":113,"end":124}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `workhorse_remote_upload_options` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e022c746daf5a42b94b0696cad68db33","location":{"path":"app/uploaders/object_storage.rb","lines":{"begin":191,"end":201}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unsafe_migrate!` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1684da513e48bc7fed9d7d05f006f4f3","location":{"path":"app/uploaders/object_storage.rb","lines":{"begin":415,"end":442}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `record_upload` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7714f01e58a6f57d5612758293fdcd98","location":{"path":"app/uploaders/records_uploads.rb","lines":{"begin":22,"end":32}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `open` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e3420c6ebb4c5709171dfe0ad626a693","location":{"path":"app/uploaders/gitlab_uploader.rb","lines":{"begin":78,"end":94}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `FileUploader` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"377d9736f93da216ab2620478b97b6a7","location":{"path":"app/uploaders/file_uploader.rb","lines":{"begin":11,"end":200}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ci_status` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0ecf5e5baa621e39be46cda7a6b7e79a","location":{"path":"app/presenters/merge_request_presenter.rb","lines":{"begin":13,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `MergeRequestPresenter` has 29 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"8382c7b1054d55cea9b796fc549636ad","location":{"path":"app/presenters/merge_request_presenter.rb","lines":{"begin":3,"end":231}},"other_locations":[],"remediation_points":2100000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `cards` has 79 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"56c716082acebaebac8f5817c4770ff3","location":{"path":"app/presenters/conversational_development_index/metric_presenter.rb","lines":{"begin":5,"end":85}},"other_locations":[],"remediation_points":1896000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `idea_to_production_steps` has 52 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3ec38351e7aae7c1eb1bbf782da34f4f","location":{"path":"app/presenters/conversational_development_index/metric_presenter.rb","lines":{"begin":87,"end":140}},"other_locations":[],"remediation_points":1248000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `project_presenter.rb` has 312 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"d892daae824d8e78fe2a0b68c288acda","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":3,"end":370}},"other_locations":[],"remediation_points":2092800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `default_view` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d66dbcb27efcacaf84388d8cfc5f690f","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":64,"end":80}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `readme_anchor_data` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c40df23b551a8abe9f2d5830edfbf472","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":221,"end":231}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `autodevops_anchor_data` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3e4902e11edd6de01b81f9a61f5d9d7b","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":275,"end":285}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `kubernetes_cluster_anchor_data` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"119e536f7905363201fb67bbdcd65824","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":287,"end":299}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ProjectPresenter` has 40 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"174173017516e33835d52eb7c5513209","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":3,"end":370}},"other_locations":[],"remediation_points":3200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `notification_service.rb` has 356 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"69e4d6c40c4449fefd31aa10ca6d0d74","location":{"path":"app/services/notification_service.rb","lines":{"begin":18,"end":550}},"other_locations":[],"remediation_points":2726400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `NotificationService` has 57 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"f69a1b203ea4d5900672119cebb81b66","location":{"path":"app/services/notification_service.rb","lines":{"begin":18,"end":550}},"other_locations":[],"remediation_points":4900000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"40698d762fba8b2aa8a60c141d6d8790","location":{"path":"app/services/groups/create_service.rb","lines":{"begin":10,"end":29}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `can_create_group?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b75b73bf2d690e5a17c2abe284491440","location":{"path":"app/services/groups/create_service.rb","lines":{"begin":37,"end":54}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_allowed_transfer` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b1a41885cc0a6ac544248d54c07e1636","location":{"path":"app/services/groups/transfer_service.rb","lines":{"begin":41,"end":47}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4bb4279e0d059ae00152e8bdf1ea798d","location":{"path":"app/services/groups/update_service.rb","lines":{"begin":7,"end":25}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_operation_id` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e02445fcf7469e25e1790f6de102578e","location":{"path":"app/services/clusters/gcp/provision_service.rb","lines":{"begin":24,"end":48}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ee86b571c5fd71a16cf372ae35959a1d","location":{"path":"app/services/clusters/applications/check_installation_progress_service.rb","lines":{"begin":6,"end":19}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c7bf557578299a5ba915c32a04c77757","location":{"path":"app/services/gravatar_service.rb","lines":{"begin":4,"end":18}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `valid_visibility_level_change?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5663649284b422de3ee165079f02580a","location":{"path":"app/services/concerns/update_visibility_level.rb","lines":{"begin":4,"end":16}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e2214a2c7403de3b6b130ef6a8ae0e7","location":{"path":"app/services/labels/transfer_service.rb","lines":{"begin":15,"end":29}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"889d22762e16d3366a67e51333a2ca58","location":{"path":"app/services/labels/promote_service.rb","lines":{"begin":8,"end":30}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"83f2800751c8e1fc38441feba44838e9","location":{"path":"app/services/notes/create_service.rb","lines":{"begin":5,"end":53}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"64400ce10f6b24c336dc2c14dc7631e5","location":{"path":"app/services/notes/create_service.rb","lines":{"begin":5,"end":53}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"55c1155f35f19aeab86111aa747bcdef","location":{"path":"app/services/search_service.rb","lines":{"begin":12,"end":22}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `group` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9350f7f8937ed59a3c058c18876a72fc","location":{"path":"app/services/search_service.rb","lines":{"begin":26,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `add_commits` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"eb5157bfd09a812f4b9e9e59faa9bd43","location":{"path":"app/services/system_note_service.rb","lines":{"begin":22,"end":22}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `change_status` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"1b2b9c2adcef9eef8232ca3fabba5213","location":{"path":"app/services/system_note_service.rb","lines":{"begin":214,"end":214}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `change_branch` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"d01b8c3144cf8ecd33ca899268167ba5","location":{"path":"app/services/system_note_service.rb","lines":{"begin":370,"end":370}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `change_branch_presence` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"dcdc7164fbd2b97a6ffca4f6dc2e0e44","location":{"path":"app/services/system_note_service.rb","lines":{"begin":390,"end":390}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `system_note_service.rb` has 269 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"9d114264549f2dceede46ea914a3550c","location":{"path":"app/services/system_note_service.rb","lines":{"begin":7,"end":679}},"other_locations":[],"remediation_points":1473600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `change_time_spent` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5c81cb9f69e1de7b23d6f18ba82533d3","location":{"path":"app/services/system_note_service.rb","lines":{"begin":181,"end":197}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cross_reference_disallowed?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cf012be1a6a2e48dd333008fe9d0d152","location":{"path":"app/services/system_note_service.rb","lines":{"begin":455,"end":461}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `existing_commit_summary` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9e510f603309baf5e0cb54ad77970a75","location":{"path":"app/services/system_note_service.rb","lines":{"begin":637,"end":659}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `create_mention_todos` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"8a610af9d7608ea64f58ba654367d763","location":{"path":"app/services/todo_service.rb","lines":{"begin":273,"end":273}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `attributes_for_todo` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"22c5b886bc2bcef31fd1446dabf42446","location":{"path":"app/services/todo_service.rb","lines":{"begin":310,"end":310}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `TodoService` has 43 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"888b933ca386de89d1c664deed0728bd","location":{"path":"app/services/todo_service.rb","lines":{"begin":10,"end":357}},"other_locations":[],"remediation_points":3500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bcfd0e3ea01c9694d57bfccaa03838e6","location":{"path":"app/services/verify_pages_domain_service.rb","lines":{"begin":18,"end":28}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d473aafcb22747b52b1f62e820f2074d","location":{"path":"app/services/create_branch_service.rb","lines":{"begin":4,"end":21}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9a39c0c805730f76077d4c41f5a49ab0","location":{"path":"app/services/todos/destroy/entity_leave_service.rb","lines":{"begin":21,"end":35}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dd806bba9fb1ffe1bffc98d5b313f431","location":{"path":"app/services/todos/destroy/private_features_service.rb","lines":{"begin":14,"end":25}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"669e705b9b727535b65aa70c23908b8a","location":{"path":"app/services/issuable/bulk_update_service.rb","lines":{"begin":6,"end":31}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fa059d9d5e3d40934b6d454456a29e17","location":{"path":"app/services/issuable/common_system_notes_service.rb","lines":{"begin":7,"end":21}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_registry_access` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0a9f93af474354f18c131c4a6f33ce74","location":{"path":"app/services/auth/container_registry_authentication_service.rb","lines":{"begin":71,"end":77}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_repository_access` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d2612abf7127bdfa9f619c175cf5d2cc","location":{"path":"app/services/auth/container_registry_authentication_service.rb","lines":{"begin":79,"end":97}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7189b60fb2402fbb02818934986a95e8","location":{"path":"app/services/projects/overwrite_project_service.rb","lines":{"begin":5,"end":26}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 26 (exceeds 5 allowed). Consider refactoring.","fingerprint":"961ae326b78bf20ee97acef0cec52327","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":11,"end":74}},"other_locations":[],"remediation_points":2250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `after_create_actions` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e38e4c0872ce60d2ba85c11448b4522b","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":98,"end":114}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `save_project_and_import_data` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"49a616d751623502896fe1bf01ddc263","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":143,"end":158}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"175fda5eebf084d959ea97dbc7ddba89","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":11,"end":74}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":53,"end":53}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"4406c9c51c68a677aea6fc1c46031379"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `propagate_projects_with_template` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"293ec445efe1987a7ed4d749d241b1c9","location":{"path":"app/services/projects/propagate_service_template.rb","lines":{"begin":25,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a2218c0712e897508cb29f687ddbb15","location":{"path":"app/services/projects/destroy_service.rb","lines":{"begin":17,"end":49}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `attempt_repositories_rollback` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"06f3aaf8a1f066c4e7933483e129d710","location":{"path":"app/services/projects/destroy_service.rb","lines":{"begin":51,"end":63}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_repository_to_project` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0129f188de75c26a852812d70994f671","location":{"path":"app/services/projects/import_service.rb","lines":{"begin":33,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_repository` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a7dc266875c644d2161df4f52854509a","location":{"path":"app/services/projects/import_service.rb","lines":{"begin":60,"end":78}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `download_lfs_objects` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e22b343575e9881c97f2271bcd97f948","location":{"path":"app/services/projects/import_service.rb","lines":{"begin":80,"end":99}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_data` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7bc52f032ae36171f9038ca564b8725f","location":{"path":"app/services/projects/import_service.rb","lines":{"begin":101,"end":109}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d3753f60f8dc5d9fdb996971cbdec0ee","location":{"path":"app/services/projects/update_pages_service.rb","lines":{"begin":18,"end":47}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extract_zip_archive!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fe7709a469edc64e95ec098c80aa4d9e","location":{"path":"app/services/projects/update_pages_service.rb","lines":{"begin":84,"end":104}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `UpdatePagesService` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d7d865dc59fc4a03d279c1c19c89a168","location":{"path":"app/services/projects/update_pages_service.rb","lines":{"begin":4,"end":191}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `labels_as_hash` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"78f7382b2da989274c44cca4dd67b726","location":{"path":"app/services/projects/autocomplete_service.rb","lines":{"begin":25,"end":47}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ecc06d47c5ab14de0497085a390276e9","location":{"path":"app/services/projects/update_remote_mirror_service.rb","lines":{"begin":7,"end":31}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f166c36283c89cda2c2d1b400fc83d9c","location":{"path":"app/services/projects/transfer_service.rb","lines":{"begin":16,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8619477bd476b8e6c20c420edaafa603","location":{"path":"app/services/projects/update_service.rb","lines":{"begin":10,"end":29}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"eab5a0a1e19f69711efa955efe37831a","location":{"path":"app/services/projects/update_service.rb","lines":{"begin":40,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `after_update` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a1014f2a5df19a7195983026335ef76c","location":{"path":"app/services/projects/update_service.rb","lines":{"begin":54,"end":76}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"23b22fd0746b04d1cdb84c038d084b5b","location":{"path":"app/services/projects/hashed_storage/migrate_repository_service.rb","lines":{"begin":18,"end":46}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0543ffb09bae340f0c5077ae46b7cdcc","location":{"path":"app/services/projects/lfs_pointers/lfs_download_service.rb","lines":{"begin":8,"end":23}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"896cabbb6ed77bf3c709bb312f23a6fb","location":{"path":"app/services/projects/lfs_pointers/lfs_import_service.rb","lines":{"begin":17,"end":32}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `lfsconfig_endpoint_uri` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ab48443c563d169a420e80faa5c91bf8","location":{"path":"app/services/projects/lfs_pointers/lfs_import_service.rb","lines":{"begin":56,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d7377f3f82a92babb9d1d653fb3a6c0","location":{"path":"app/services/projects/unlink_fork_service.rb","lines":{"begin":6,"end":29}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `notification_recipient_service.rb` has 261 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"04116f97d81f75dc8b45ce0a5811fa81","location":{"path":"app/services/notification_recipient_service.rb","lines":{"begin":6,"end":384}},"other_locations":[],"remediation_points":1358400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc4e88abd84c5b4832ac272b050f5372","location":{"path":"app/services/notification_recipient_service.rb","lines":{"begin":257,"end":295}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Base` has 28 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"27695a9b55119263d5fc023bfb91d6e3","location":{"path":"app/services/notification_recipient_service.rb","lines":{"begin":28,"end":238}},"other_locations":[],"remediation_points":2000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6cc1d79d478940cb6d6b9ceed02cb6e6","location":{"path":"app/services/users/destroy_service.rb","lines":{"begin":26,"end":64}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute_without_lease` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"67cb8ef6b0457ad00d8347bcbe3ff3f7","location":{"path":"app/services/users/refresh_authorized_projects_service.rb","lines":{"begin":50,"end":71}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_authorizations` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6e70e1ba1ea2f084af1fff74047fe8d9","location":{"path":"app/services/users/refresh_authorized_projects_service.rb","lines":{"begin":77,"end":88}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d9165689bde9f912aa84f6f518e598da","location":{"path":"app/services/users/build_service.rb","lines":{"begin":14,"end":36}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_user_params` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"830b5807353157bd5e183aad25f2b282","location":{"path":"app/services/users/build_service.rb","lines":{"begin":88,"end":111}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `admin_create_params` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fe5ddceb25aafe04827303d1f6d564b4","location":{"path":"app/services/users/build_service.rb","lines":{"begin":45,"end":74}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5bc79f55d7e462657e85385bf64c461b","location":{"path":"app/services/submit_usage_ping_service.rb","lines":{"begin":16,"end":34}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"eff6af7a39548e896b0c36625e41edfa","location":{"path":"app/services/validate_new_branch_service.rb","lines":{"begin":6,"end":20}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f26b8395f9bebcbbe601568ab87f1dac","location":{"path":"app/services/tags/create_service.rb","lines":{"begin":5,"end":32}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"009acc0cc8f2cad1eb48f996869cd8b3","location":{"path":"app/services/tags/destroy_service.rb","lines":{"begin":6,"end":29}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `close_issue` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"268f126190d57eeaaded69cfb25897c3","location":{"path":"app/services/issues/close_service.rb","lines":{"begin":20,"end":39}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_changes` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ff12a5753d6bee667c95cfbc92416e03","location":{"path":"app/services/issues/update_service.rb","lines":{"begin":18,"end":56}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `handle_changes` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8b14e45d1c64f39d6eedb20d84843741","location":{"path":"app/services/issues/update_service.rb","lines":{"begin":18,"end":56}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `filter_assignee` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5691687cb00f6fd194e603429807185a","location":{"path":"app/services/issues/base_service.rb","lines":{"begin":35,"end":51}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1a9702d3dc815e8d64756d8fb212f9b3","location":{"path":"app/services/web_hook_service.rb","lines":{"begin":24,"end":62}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4d9ca5643eb0d67f71faf5371f5ae764","location":{"path":"app/services/ci/create_pipeline_service.rb","lines":{"begin":15,"end":48}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b8c02fe4ca7afa344ed38920cbc647e3","location":{"path":"app/services/ci/retry_pipeline_service.rb","lines":{"begin":7,"end":28}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc2e58626fb38ae2a635ad7504d1369f","location":{"path":"app/services/ci/stop_environments_service.rb","lines":{"begin":7,"end":18}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"617800a52fa424a1bcea1ed6a62b6793","location":{"path":"app/services/ci/register_job_service.rb","lines":{"begin":19,"end":66}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3c427dfa0dd24ca8d0170b6247260b87","location":{"path":"app/services/ci/register_job_service.rb","lines":{"begin":19,"end":66}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_label_ids` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d322faf3023b3e313c8abe2020cf59af","location":{"path":"app/services/issuable_base_service.rb","lines":{"begin":96,"end":111}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"139f887d96d46ca70a22e378422f8bc7","location":{"path":"app/services/issuable_base_service.rb","lines":{"begin":172,"end":224}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `IssuableBaseService` has 30 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"906133099e33d0679f4b8c65cc1eef9b","location":{"path":"app/services/issuable_base_service.rb","lines":{"begin":3,"end":319}},"other_locations":[],"remediation_points":2200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `update` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"009042d6d3df2763254c61f2b3d8536c","location":{"path":"app/services/issuable_base_service.rb","lines":{"begin":172,"end":224}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_event_data` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"181f03475858936500cb1903a2981922","location":{"path":"app/services/system_hooks_service.rb","lines":{"begin":22,"end":70}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_event_name` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"58fb30a590bb73d7a7c014c15cff2f61","location":{"path":"app/services/system_hooks_service.rb","lines":{"begin":72,"end":83}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `build_event_data` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cb1f235466d69ba0f51ae5082e6ccd1b","location":{"path":"app/services/system_hooks_service.rb","lines":{"begin":22,"end":70}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `include_any_scope?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c3b269346955ff77f7af249761778c5f","location":{"path":"app/services/access_token_validation_service.rb","lines":{"begin":33,"end":48}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b944cb228f06d17cfab396e042f46404","location":{"path":"app/services/create_deployment_service.rb","lines":{"begin":15,"end":29}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a911c4e00c978f2c3dee8a80d2b123c","location":{"path":"app/services/members/destroy_service.rb","lines":{"begin":5,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3e17107f5166483e06d9a3aa43ce4057","location":{"path":"app/services/delete_branch_service.rb","lines":{"begin":4,"end":23}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `can_be_resolved_in_ui?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"303b72ed1619302f77225ae41471638d","location":{"path":"app/services/merge_requests/conflicts/list_service.rb","lines":{"begin":15,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `reload_merge_requests` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a105139484033c8e7ced10c6ce36e8db","location":{"path":"app/services/merge_requests/refresh_service.rb","lines":{"begin":80,"end":106}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_new_commits` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"851ed172a4510d8303c8caf10c7c09e1","location":{"path":"app/services/merge_requests/refresh_service.rb","lines":{"begin":119,"end":141}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trigger` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8627834675a31ad536bf412edaafd7e7","location":{"path":"app/services/merge_requests/merge_when_pipeline_succeeds_service.rb","lines":{"begin":24,"end":33}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4cd7f00168c6c916f0dc7075f0ca3349","location":{"path":"app/services/merge_requests/merge_service.rb","lines":{"begin":17,"end":37}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"35e1a81d921bd211a80412b3021d8f1f","location":{"path":"app/services/merge_requests/create_from_issue_service.rb","lines":{"begin":16,"end":31}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_branches` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"629d86725d56fa929caf501972ac7cf2","location":{"path":"app/services/merge_requests/build_service.rb","lines":{"begin":92,"end":97}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `BuildService` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"0955c9bbce7bc1720631a8dc579bbf5f","location":{"path":"app/services/merge_requests/build_service.rb","lines":{"begin":4,"end":208}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_branches` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7e605b474bb1fb7b66f5bfbaf51d3c6d","location":{"path":"app/services/merge_requests/get_urls_service.rb","lines":{"begin":33,"end":50}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_changes` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cefad5ecb48fbecc8f4fa6ba026659f8","location":{"path":"app/services/merge_requests/update_service.rb","lines":{"begin":27,"end":78}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `handle_changes` has 42 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"db391ef0b97408393fd716be0c7356dd","location":{"path":"app/services/merge_requests/update_service.rb","lines":{"begin":27,"end":78}},"other_locations":[],"remediation_points":1008000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `commit_status_merge_requests` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"784a58094574fa35608fca082c2548e7","location":{"path":"app/services/merge_requests/base_service.rb","lines":{"begin":76,"end":85}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `interpret_service.rb` has 596 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"83735b0b7c1f8d22c40f49e709c57e70","location":{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":3,"end":700}},"other_locations":[],"remediation_points":6182400,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b08fd2ee935cfed15e0505bb9af417b4","location":{"path":"app/services/discussions/update_diff_position_service.rb","lines":{"begin":5,"end":34}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dcb90a6db168562206b2356716e752b6","location":{"path":"app/services/git_push_service.rb","lines":{"begin":24,"end":65}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `index` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8b2781e6841d5d63745094c685fcd1e8","location":{"path":"app/controllers/groups/children_controller.rb","lines":{"begin":6,"end":29}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `index` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3169a6c4ee72f03a1318dedbed6b40ff","location":{"path":"app/controllers/groups/group_members_controller.rb","lines":{"begin":17,"end":39}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `failure_message` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e3605869f669f4cb85ba66bdd5b04c7a","location":{"path":"app/controllers/omniauth_callbacks_controller.rb","lines":{"begin":30,"end":38}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `omniauth_flow` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8cf6a8a0fd40eb5ff273cc6dba558403","location":{"path":"app/controllers/omniauth_callbacks_controller.rb","lines":{"begin":77,"end":95}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sign_in_user_flow` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e780968a46460e3b3fe11a34ddfc0c00","location":{"path":"app/controllers/omniauth_callbacks_controller.rb","lines":{"begin":115,"end":136}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `OmniauthCallbacksController` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"af6b190bf330da08acbe854817a6812f","location":{"path":"app/controllers/omniauth_callbacks_controller.rb","lines":{"begin":3,"end":192}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `impersonate` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f01a1ec9614ccf1b226a3e42206b203b","location":{"path":"app/controllers/admin/users_controller.rb","lines":{"begin":33,"end":56}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"af47e69f6ee053a6f226de336934f214","location":{"path":"app/controllers/admin/users_controller.rb","lines":{"begin":118,"end":147}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `UsersController` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"bb3ecebd549737d88ffb4ea7b2d02d16","location":{"path":"app/controllers/admin/users_controller.rb","lines":{"begin":3,"end":230}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4d5e93c6a88076ccc249e69590f05f53","location":{"path":"app/controllers/admin/system_info_controller.rb","lines":{"begin":34,"end":57}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6c80ea5d1d0a024abdc38fba56cb8435","location":{"path":"app/controllers/concerns/uploads_actions.rb","lines":{"begin":35,"end":48}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `send_blob` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cfa9da0619389a51f42df3b51bf5c65c","location":{"path":"app/controllers/concerns/sends_blob.rb","lines":{"begin":11,"end":25}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `leave` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1f6860cf601d992df6f0af95d95748a0","location":{"path":"app/controllers/concerns/membership_actions.rb","lines":{"begin":63,"end":82}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `set_issuables_index` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f415a6c750063cabd8f90d48e98e5eb0","location":{"path":"app/controllers/concerns/issuable_collections.rb","lines":{"begin":17,"end":38}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_with_two_factor` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"36f3a297fab9a3ef0e526b7f851741b2","location":{"path":"app/controllers/concerns/authenticates_with_two_factor.rb","lines":{"begin":43,"end":54}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `safe_redirect_path` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"36420be74fc50045ef291eb40a71bb77","location":{"path":"app/controllers/concerns/internal_redirect.rb","lines":{"begin":6,"end":17}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `safe_redirect_path_for_url` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d5b751dbd9dd362815b574f1cb52225a","location":{"path":"app/controllers/concerns/internal_redirect.rb","lines":{"begin":19,"end":26}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `note_json` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"273ea02f6c72cced154034c12207d3f1","location":{"path":"app/controllers/concerns/notes_actions.rb","lines":{"begin":104,"end":146}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `diff_discussion_html` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"62bc37c3eb9f037889edcbe39ef53aa6","location":{"path":"app/controllers/concerns/notes_actions.rb","lines":{"begin":148,"end":174}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `note_project` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"573f594e00ed6b29987ce9e763457833","location":{"path":"app/controllers/concerns/notes_actions.rb","lines":{"begin":239,"end":256}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `note_json` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2572ca1fa7eb27c9668322b7a098f02a","location":{"path":"app/controllers/concerns/notes_actions.rb","lines":{"begin":104,"end":146}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `recaptcha_check_with_fallback` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f5ab9c6deb983a187870f912e14afaea","location":{"path":"app/controllers/concerns/spammable_actions.rb","lines":{"begin":29,"end":54}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_commit` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"832aa36f617c6cce4927e6c8b561ce98","location":{"path":"app/controllers/concerns/creates_commit.rb","lines":{"begin":8,"end":51}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_flash_notice` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"89030f5b74776bb2425f95279fdd5b7d","location":{"path":"app/controllers/concerns/creates_commit.rb","lines":{"begin":62,"end":73}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `final_success_path` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"afb1305c0c32730e0b027be235b174a0","location":{"path":"app/controllers/concerns/creates_commit.rb","lines":{"begin":75,"end":83}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `create_commit` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"eaa2bdf0bf7bb61da1f37f4031392e35","location":{"path":"app/controllers/concerns/creates_commit.rb","lines":{"begin":8,"end":51}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `send_upload` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cfa021a786ee32f97584fecedce60cde","location":{"path":"app/controllers/concerns/send_file_upload.rb","lines":{"begin":4,"end":26}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `lfs_check_access!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"46d5167d71a5e6a69a9a236086347aed","location":{"path":"app/controllers/concerns/lfs_request.rb","lines":{"begin":36,"end":45}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"905d81937e68b3931dad7416dc6ef9a6","location":{"path":"app/controllers/import/bitbucket_controller.rb","lines":{"begin":37,"end":65}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"20b781fcddc55e961f42441c445d70d8","location":{"path":"app/controllers/import/github_controller.rb","lines":{"begin":39,"end":58}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `GithubController` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"ab20d51ced6ac78aa49e0f6de0af0ac0","location":{"path":"app/controllers/import/github_controller.rb","lines":{"begin":1,"end":132}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_or_create_namespace` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2f80525d40e648a6d7ea2229b2cb74ae","location":{"path":"app/controllers/import/base_controller.rb","lines":{"begin":19,"end":31}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4d4ab07c39a5534d15b7443e37a7a235","location":{"path":"app/controllers/import/bitbucket_server_controller.rb","lines":{"begin":21,"end":45}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_import_params` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"da2d51f606c44590f847c4640444ac5b","location":{"path":"app/controllers/import/bitbucket_server_controller.rb","lines":{"begin":82,"end":90}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `callback` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3c9e710a6531242212414756cccfe915","location":{"path":"app/controllers/import/google_code_controller.rb","lines":{"begin":8,"end":33}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_captcha` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"de5a9e305854d86a525c3be0d27eaf6d","location":{"path":"app/controllers/sessions_controller.rb","lines":{"begin":62,"end":78}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `auto_sign_in_with_provider` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f619928e9105c1311e52540b45817001","location":{"path":"app/controllers/sessions_controller.rb","lines":{"begin":167,"end":186}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `SessionsController` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"ca547cff8dfcb4f2bb32febd07c3a8b9","location":{"path":"app/controllers/sessions_controller.rb","lines":{"begin":3,"end":221}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"44af899cceadececa5fb2d6343c42fb8","location":{"path":"app/controllers/registrations_controller.rb","lines":{"begin":16,"end":36}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `index` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07120d763a5a37d3563d108da7618fc2","location":{"path":"app/controllers/snippets_controller.rb","lines":{"begin":30,"end":43}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"19fe68118b5b1cf2a9c1bfef20064546","location":{"path":"app/controllers/projects/wikis_controller.rb","lines":{"begin":20,"end":44}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4754ebccd6eb3ba307dee978dd12dde8","location":{"path":"app/controllers/projects/wikis_controller.rb","lines":{"begin":49,"end":65}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_requests_controller.rb` has 265 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"ed0fbac1699084d99f2798b9ff54b642","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":1,"end":354}},"other_locations":[],"remediation_points":1416000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f6d989eecff0a311d8a4126b524b3d54","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":30,"end":76}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"043aac3b7446fd75e17405d975137865","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":123,"end":145}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ci_environments_status` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"46e544b6af20509fb22aa245f36e853b","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":204,"end":242}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `merge!` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5c7d0579b3b6ee7f277ab6932678009d","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":285,"end":319}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `MergeRequestsController` has 28 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"b062a438177acc72625495d3cbfaa77c","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":1,"end":354}},"other_locations":[],"remediation_points":2000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `show` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e3c966096d2b12e85a670db7911707b8","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":30,"end":76}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `ci_environments_status` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e5b7f8ffd1531062cbb97dd163687759","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":204,"end":242}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `set_commits` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e6d95cec8ea5cbbfb499acf2d8c47bb9","location":{"path":"app/controllers/projects/commits_controller.rb","lines":{"begin":55,"end":69}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `archive` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"24b42f263d62dd73163666f08a5dab84","location":{"path":"app/controllers/projects/repositories_controller.rb","lines":{"begin":16,"end":28}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"48f2140ee4baa6d602990f3cd3a4e3bd","location":{"path":"app/controllers/projects/branches_controller.rb","lines":{"begin":52,"end":91}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `create` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7b53414d925313d554a03dbde87a17b1","location":{"path":"app/controllers/projects/branches_controller.rb","lines":{"begin":52,"end":91}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_user` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"aeab90a0370c820e728a3abfbc25b413","location":{"path":"app/controllers/projects/git_http_client_controller.rb","lines":{"begin":30,"end":59}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"59245dcb34ab6bc8f9698502300494c2","location":{"path":"app/controllers/projects/mirrors_controller.rb","lines":{"begin":15,"end":34}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `resolve` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"43486b045d4b1186318cfc251852d98f","location":{"path":"app/controllers/projects/notes_controller.rb","lines":{"begin":21,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unresolve` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"687318ee4eb16a33452c1f37b2c881a0","location":{"path":"app/controllers/projects/notes_controller.rb","lines":{"begin":38,"end":52}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"31c48fb29d257a60073d2b38d6e1cb4d","location":{"path":"app/controllers/projects/forks_controller.rb","lines":{"begin":39,"end":60}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `index` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a118caadbf010e6edb81311d4382716d","location":{"path":"app/controllers/projects/pipelines_controller.rb","lines":{"begin":12,"end":44}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3abec97b27020fca915a84ac93bc67d6","location":{"path":"app/controllers/projects/imports_controller.rb","lines":{"begin":23,"end":39}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4da2f2dd27d4fd213b9f9f2f1a5cd41f","location":{"path":"app/controllers/projects/group_links_controller.rb","lines":{"begin":10,"end":22}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `service_test_response` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bca34a40fb26a815c60df6fd1e7dba51","location":{"path":"app/controllers/projects/services_controller.rb","lines":{"begin":36,"end":51}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `metrics` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"519adc463f2dd3de5cb94580d7f50383","location":{"path":"app/controllers/projects/deployments_controller.rb","lines":{"begin":15,"end":26}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `additional_metrics` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c4f6fc892643a150dc844973ad4d976","location":{"path":"app/controllers/projects/deployments_controller.rb","lines":{"begin":28,"end":42}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_match_line` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07c854b446b4087e5ee7892172f74ee7","location":{"path":"app/controllers/projects/blob_controller.rb","lines":{"begin":133,"end":149}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `blob` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b6f7a07f247048d784135038dce26244","location":{"path":"app/controllers/projects/blob_controller.rb","lines":{"begin":151,"end":165}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `editor_variables` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cfcc7b49429c64315cf6da835ec8bede","location":{"path":"app/controllers/projects/blob_controller.rb","lines":{"begin":192,"end":221}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `BlobController` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"c1e63be99f08c204815ffecf3dd4b66c","location":{"path":"app/controllers/projects/blob_controller.rb","lines":{"begin":2,"end":284}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"979438a3332d8f5f08717dc86472299b","location":{"path":"app/controllers/projects/tree_controller.rb","lines":{"begin":13,"end":47}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `show` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"806fa8153a8d1eb022e92afcd4601d91","location":{"path":"app/controllers/projects/tree_controller.rb","lines":{"begin":13,"end":47}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `raw` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e4f02dd301b3eeb842d85c37f20bae2e","location":{"path":"app/controllers/projects/jobs_controller.rb","lines":{"begin":126,"end":140}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `JobsController` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"6f969a368940183e4fce1acd28147f56","location":{"path":"app/controllers/projects/jobs_controller.rb","lines":{"begin":1,"end":189}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_merge_request_diff_compare` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"91ff93d1ec72c5ce3a260ca7bb0355a1","location":{"path":"app/controllers/projects/merge_requests/diffs_controller.rb","lines":{"begin":47,"end":73}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `download_objects!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b7b7331d3cec46b88359e5648ed2f9a2","location":{"path":"app/controllers/projects/lfs_api_controller.rb","lines":{"begin":52,"end":68}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ClustersController` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"964ab09026b9448f413a95a5f6094e09","location":{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":1,"end":222}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `move` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ae70ac550f5ace969dc34ad430e1a506","location":{"path":"app/controllers/projects/issues_controller.rb","lines":{"begin":95,"end":113}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `IssuesController` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"09358857b8d0a59b5b0f1efabe1eb312","location":{"path":"app/controllers/projects/issues_controller.rb","lines":{"begin":1,"end":251}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_root_container_repository!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a31d8c82827ad31ea06cbe929b5bcb9e","location":{"path":"app/controllers/projects/registry/repositories_controller.rb","lines":{"begin":39,"end":47}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_keys` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e98b2fc794e033c8a48c3ee0becb6ada","location":{"path":"app/controllers/profiles/keys_controller.rb","lines":{"begin":36,"end":51}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7fac5d70e69bf9664ceb8d865c808fa2","location":{"path":"app/controllers/profiles/two_factor_auths_controller.rb","lines":{"begin":4,"end":40}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `show` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"053f896df50c58d1c5a7f874e0cf72c3","location":{"path":"app/controllers/profiles/two_factor_auths_controller.rb","lines":{"begin":4,"end":40}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `new` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"17c6af74d7968b2ff43b8ab9795a3e8a","location":{"path":"app/controllers/oauth/authorizations_controller.rb","lines":{"begin":6,"end":18}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `projects_controller.rb` has 328 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"ed6bb55f0c75667bbb0ec51d3c2664cf","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":3,"end":436}},"other_locations":[],"remediation_points":2323200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `refs` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4477c3a607082b3d28aae895558786dd","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":241,"end":274}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `render_landing_page` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"278be4b0b74df103253b6e3205fb8a62","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":281,"end":298}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ProjectsController` has 37 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"8090aa2673614a81e71589f7753eff30","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":3,"end":436}},"other_locations":[],"remediation_points":2900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `project_params_attributes` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2fdc2a483f87578ca62a61a751d113d7","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":331,"end":370}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `render_check_results` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ba1563517e02434643d39267194f528e","location":{"path":"app/controllers/health_controller.rb","lines":{"begin":39,"end":56}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f7c994d83ef228aa163c05179f06d2fd","location":{"path":"app/controllers/help_controller.rb","lines":{"begin":23,"end":57}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `clean_path_info` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5db34152511808356524914b64cf554b","location":{"path":"app/controllers/help_controller.rb","lines":{"begin":82,"end":107}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `application_controller.rb` has 341 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"f029769395ddcac92c2b8d2ca3b05b28","location":{"path":"app/controllers/application_controller.rb","lines":{"begin":3,"end":465}},"other_locations":[],"remediation_points":2510400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ldap_security_check` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"523cc3bb278c788d5848949c8c3dead7","location":{"path":"app/controllers/application_controller.rb","lines":{"begin":262,"end":272}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `enforce_terms!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d14876e741b595110a644395f1230e14","location":{"path":"app/controllers/application_controller.rb","lines":{"begin":318,"end":339}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ApplicationController` has 53 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d63945970dce3ed1ef2b7634b214a4e9","location":{"path":"app/controllers/application_controller.rb","lines":{"begin":6,"end":465}},"other_locations":[],"remediation_points":4500000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `users_select_tag` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a108342e6dd48ba76731363b84661eaf","location":{"path":"app/helpers/selects_helper.rb","lines":{"begin":4,"end":26}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `users_select_data_attributes` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a8bad651f6dba343e0468fd0003dd784","location":{"path":"app/helpers/selects_helper.rb","lines":{"begin":74,"end":85}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show_last_push_widget?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2f5475afeb1122f3cfa25ac5955cbb27","location":{"path":"app/helpers/application_helper.rb","lines":{"begin":70,"end":86}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `visible_attributes` has 119 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"17f189b8fca7fc3b1f8a420a4bcbda19","location":{"path":"app/helpers/application_settings_helper.rb","lines":{"begin":146,"end":266}},"other_locations":[],"remediation_points":2856000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `toggle_award_url` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d69f1bff0994f6e3441f85f3f11d5bbe","location":{"path":"app/helpers/award_emoji_helper.rb","lines":{"begin":4,"end":17}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `page_gutter_class` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e65e525a4333beebdf83e031e767ef28","location":{"path":"app/helpers/nav_helper.rb","lines":{"begin":21,"end":39}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_header_links` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f16a544d112fad89ba71d21c6d5db6a3","location":{"path":"app/helpers/nav_helper.rb","lines":{"begin":55,"end":75}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `diff_match_line` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b869dd8c4eaef5740ed1f255a8f1fbf1","location":{"path":"app/helpers/diff_helper.rb","lines":{"begin":37,"end":57}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parallel_diff_discussions` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8ca093d3db0a23030593eb120a3b43fd","location":{"path":"app/helpers/diff_helper.rb","lines":{"begin":69,"end":85}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `active_nav_link?` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a16ad6a8f13c9288d42920124805df7f","location":{"path":"app/helpers/tab_helper.rb","lines":{"begin":75,"end":104}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `user_status` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1c1f76f6143eff8d1d22a334a235fd12","location":{"path":"app/helpers/users_helper.rb","lines":{"begin":55,"end":71}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `namespaces_options` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7abb679aa0b2ea21e01c24880d3bf77c","location":{"path":"app/helpers/namespaces_helper.rb","lines":{"begin":9,"end":45}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `options_for_group` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e8e66fbb82bdafdc25047aa1524869b","location":{"path":"app/helpers/namespaces_helper.rb","lines":{"begin":71,"end":86}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `namespaces_options` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d1da7571d4cd35e4edcc84ea6e21ec17","location":{"path":"app/helpers/namespaces_helper.rb","lines":{"begin":9,"end":45}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `submodule_links` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"592e98a3099ca8bf38ff5b45bcb0dffb","location":{"path":"app/helpers/submodule_helper.rb","lines":{"begin":9,"end":47}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `submodule_links` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c06e0c3ec596b1cdfd2de6f634cd6b86","location":{"path":"app/helpers/submodule_helper.rb","lines":{"begin":9,"end":47}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_member_message` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bba34164517c03c61579ce09b9d98bb9","location":{"path":"app/helpers/members_helper.rb","lines":{"begin":4,"end":22}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `chunk_snippet` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"de18ceb549965f7ea862a8c0dc960bea","location":{"path":"app/helpers/snippets_helper.rb","lines":{"begin":64,"end":105}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `labels_filter_path` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9d39f54cf4cda251c101014a6f4c7244","location":{"path":"app/helpers/labels_helper.rb","lines":{"begin":134,"end":149}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `label_status_tooltip` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"64c9182b39df4fa2de87d8d55c2d2cbc","location":{"path":"app/helpers/labels_helper.rb","lines":{"begin":216,"end":222}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `event_feed_title` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0ae609cca64a854e8b692dd72db01e13","location":{"path":"app/helpers/events_helper.rb","lines":{"begin":78,"end":102}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `event_feed_url` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f99a06ad5d757ef02870ca23d95676bc","location":{"path":"app/helpers/events_helper.rb","lines":{"begin":104,"end":122}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `push_event_feed_url` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0a4c5fb26657f91ff277b7ef0d13c32d","location":{"path":"app/helpers/events_helper.rb","lines":{"begin":124,"end":138}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `discussion_path` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f8fcc6a280f6df449c018435d3f92438","location":{"path":"app/helpers/notes_helper.rb","lines":{"begin":77,"end":92}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `clipboard_button` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c29991b281e121dc435a356a600e4e1e","location":{"path":"app/helpers/button_helper.rb","lines":{"begin":22,"end":59}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dropdown_item_with_description` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8d5076e20cc132338854d62653cd1df8","location":{"path":"app/helpers/button_helper.rb","lines":{"begin":88,"end":97}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `clipboard_button` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"97e66a0f68fa32b384f734ea55e5e044","location":{"path":"app/helpers/button_helper.rb","lines":{"begin":22,"end":59}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dropdown_tag` has a Cognitive Complexity of 26 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4267997c227f431d5c46888b32748d75","location":{"path":"app/helpers/dropdowns_helper.rb","lines":{"begin":4,"end":41}},"other_locations":[],"remediation_points":2250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dropdown_toggle` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a5dbb87125de6801bd8e3f421ea8d82","location":{"path":"app/helpers/dropdowns_helper.rb","lines":{"begin":43,"end":50}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `dropdown_tag` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5365287d8861b3ecac3eb84417018a2b","location":{"path":"app/helpers/dropdowns_helper.rb","lines":{"begin":4,"end":41}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sprite_icon` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b288e014419e55f1aa86631629c79889","location":{"path":"app/helpers/icons_helper.rb","lines":{"begin":44,"end":56}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `file_type_icon_class` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2ddea2c28588a85667f5b5399507ff46","location":{"path":"app/helpers/icons_helper.rb","lines":{"begin":109,"end":151}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `commit_action_link` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8657821ad7a6c0ec4e6398a6aee36f9c","location":{"path":"app/helpers/commits_helper.rb","lines":{"begin":161,"end":181}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `todo_target_path` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d03a4561eaff01e66db146018ad0c969","location":{"path":"app/helpers/todos_helper.rb","lines":{"begin":39,"end":54}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `todo_due_date` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"db4163fb6bc594c0c7f5188d10a94d61","location":{"path":"app/helpers/todos_helper.rb","lines":{"begin":141,"end":160}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `url_for_issue` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b77357907fc65f32d6adebdec5c99e74","location":{"path":"app/helpers/issues_helper.rb","lines":{"begin":18,"end":32}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `path_breadcrumbs` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"309df9fc4689b2f5fb71f6534a5efd03","location":{"path":"app/helpers/tree_helper.rb","lines":{"begin":105,"end":121}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `edit_fork_button_tag` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"2b6c57301b36358581b2491652866c44","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":302,"end":302}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `edit_button_tag` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"723d5e25e5499349ff424fa5046927f1","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":318,"end":318}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `modify_file_button` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4b6850840a5430937e584328e518311d","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":51,"end":69}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `blob_raw_url` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b8466fa1a816b27c99638fe228016597","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":119,"end":131}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `edit_button_tag` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"93c71a130633cfcebe91bcda32798413","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":318,"end":327}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `projects_helper.rb` has 438 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"a3d8d9970bc1e29fb6cf0845a8963a35","location":{"path":"app/helpers/projects_helper.rb","lines":{"begin":3,"end":555}},"other_locations":[],"remediation_points":3907200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `link_to_member` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"92e14b6f78cd2026911faaf78829ba53","location":{"path":"app/helpers/projects_helper.rb","lines":{"begin":49,"end":73}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project_title` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2ebe86c74e8aaea024bada46109f4e9a","location":{"path":"app/helpers/projects_helper.rb","lines":{"begin":75,"end":93}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_project_nav_tabs` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c4c2cc0402179e15ec7cff7acf71f67","location":{"path":"app/helpers/projects_helper.rb","lines":{"begin":266,"end":300}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `disallowed_project_visibility_level_description` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cde9c43b6e33582ebc01be9dcc6b0cc6","location":{"path":"app/helpers/visibility_level_helper.rb","lines":{"begin":84,"end":102}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `disallowed_group_visibility_level_description` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c964b53a81590235d500b10d64921ffc","location":{"path":"app/helpers/visibility_level_helper.rb","lines":{"begin":106,"end":128}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `webpack_controller_bundle_tags` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d1915400e4cdf8101f80cf09fcaa41a8","location":{"path":"app/helpers/webpack_helper.rb","lines":{"begin":8,"end":34}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `webpack_entrypoint_paths` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2059057b0a4c8c172104d3a69b6d33a1","location":{"path":"app/helpers/webpack_helper.rb","lines":{"begin":36,"end":57}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `webpack_public_host` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5187936b625c537cc83fe49f935245ac","location":{"path":"app/helpers/webpack_helper.rb","lines":{"begin":59,"end":68}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestone_class_for_state` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9f98077e679dcc8bdc4adbc9fc03e111","location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":71,"end":79}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestone_time_for` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7e9f0e967afa3a497b80340cf11b095e","location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":122,"end":144}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestone_date_range` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4843496f3de8ac4a890951c97d469064","location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":181,"end":197}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `share_with_group_lock_help_text` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ff00375205d66e384de0e3f33ec7d0d7","location":{"path":"app/helpers/groups_helper.rb","lines":{"begin":96,"end":108}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `group_title_link` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"98657f045e5336754e6eae56e789418f","location":{"path":"app/helpers/groups_helper.rb","lines":{"begin":141,"end":146}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `ci_icon_for_status` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7c3efeba2a30134025307449a05b2f77","location":{"path":"app/helpers/ci_status_helper.rb","lines":{"begin":61,"end":91}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `sorting_helper.rb` has 302 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"01923968066dde38b1a1f78557e3aa84","location":{"path":"app/helpers/sorting_helper.rb","lines":{"begin":3,"end":382}},"other_locations":[],"remediation_points":1948800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `link_to_html` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a4d4827db929df3ce6e16fb4d0b9522","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":45,"end":69}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `first_line_in_markdown` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a45a636cd6cfd627a9b9cb575482d665","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":75,"end":92}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `render_wiki_content` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f59f576a0875d3e1b3150c35d4244f1f","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":124,"end":148}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `markup_unsafe` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0f734634b0f84e17e369619da7591cc9","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":150,"end":166}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `truncate_visible` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7de1befc5006684b8aac21b36b5a2eee","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":198,"end":229}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `truncate_if_block` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c65f7114f8bc0ff0c40ba17a721dc4e7","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":234,"end":243}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `issuables_helper.rb` has 354 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"b0534175d956cb265e089add14a703e9","location":{"path":"app/helpers/issuables_helper.rb","lines":{"begin":3,"end":436}},"other_locations":[],"remediation_points":2697600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `multi_label_name` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f6f2aa839b3380e86377b58408fcfa15","location":{"path":"app/helpers/issuables_helper.rb","lines":{"begin":40,"end":51}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `issuable_labels_tooltip` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6ed7317a98ede04c4a1cde7f6af1871c","location":{"path":"app/helpers/issuables_helper.rb","lines":{"begin":208,"end":219}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `issuable_todo_button_data` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"382bc77616cd06810f3d2112c8460240","location":{"path":"app/helpers/issuables_helper.rb","lines":{"begin":381,"end":395}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `project_policy.rb` has 333 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"61f7a144aa6cdf7e965d3130c52aacc3","location":{"path":"app/policies/project_policy.rb","lines":{"begin":3,"end":434}},"other_locations":[],"remediation_points":2395200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remaining_days_in_words` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"db6e21e4091f24644db9efa04f03f5f2","location":{"path":"app/serializers/entity_date_helper.rb","lines":{"begin":47,"end":70}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_metrics` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fe3dd187f741789f765000b6e08c77bf","location":{"path":"app/serializers/merge_request_widget_entity.rb","lines":{"begin":252,"end":260}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `reassigned_issue_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"3e2a4c8e9b4b5584a258cdc21bf1779b","location":{"path":"app/mailers/emails/issues.rb","lines":{"begin":23,"end":23}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `relabeled_issue_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"eccd65b7da076099478491ce5ffd1bcd","location":{"path":"app/mailers/emails/issues.rb","lines":{"begin":40,"end":40}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `issue_status_changed_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"8d347450b4f4fa27b8d847a4e282b8f2","location":{"path":"app/mailers/emails/issues.rb","lines":{"begin":48,"end":48}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `issue_moved_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"a1fb6126b5a4051ae7af19c5fd1ea1ef","location":{"path":"app/mailers/emails/issues.rb","lines":{"begin":56,"end":56}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `reassigned_merge_request_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"8bd5f4e62d9db3075aa99840c56f1dc3","location":{"path":"app/mailers/emails/merge_requests.rb","lines":{"begin":26,"end":26}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `relabeled_merge_request_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"e4fbe646413819b74d51d11132d24f75","location":{"path":"app/mailers/emails/merge_requests.rb","lines":{"begin":34,"end":34}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `merge_request_status_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"c39ada600f20e34ac7bc271aaa17a489","location":{"path":"app/mailers/emails/merge_requests.rb","lines":{"begin":55,"end":55}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `NotifyPreview` has 26 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"0ad28a90a453e7f0892e2fa27ca7dd74","location":{"path":"app/mailers/previews/notify_preview.rb","lines":{"begin":3,"end":176}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `perform` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"ae09aa8f81cc51243569392f706dea8b","location":{"path":"app/workers/irker_worker.rb","lines":{"begin":9,"end":9}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `send_branch_updates` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"2efa6a64aab186fa6e22c887b82c2da8","location":{"path":"app/workers/irker_worker.rb","lines":{"begin":61,"end":61}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `send_commits` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"bda830a07ed575bd9c98deb67e6efa84","location":{"path":"app/workers/irker_worker.rb","lines":{"begin":84,"end":84}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `send_commits_count` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"fe8fcb808f2da3f31174330b3b8e8d2f","location":{"path":"app/workers/irker_worker.rb","lines":{"begin":113,"end":113}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `process_commit_message` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"0cbd58f4d824762ee08cbe8394a955be","location":{"path":"app/workers/process_commit_worker.rb","lines":{"begin":36,"end":36}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `close_issues` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"0ddd5587d752e08728eca56e8b842057","location":{"path":"app/workers/process_commit_worker.rb","lines":{"begin":45,"end":45}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"00fda5cf89ca2d06f32e73e5948d2d96","location":{"path":"app/workers/process_commit_worker.rb","lines":{"begin":18,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b2d21175bd49ce0560d00b41a5d4b105","location":{"path":"app/workers/emails_on_push_worker.rb","lines":{"begin":8,"end":78}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `perform` has 58 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"21b177b9c8aab67c1dd18ac4736d07e0","location":{"path":"app/workers/emails_on_push_worker.rb","lines":{"begin":8,"end":78}},"other_locations":[],"remediation_points":1392000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc6bfd63748977ec6b13ad1bcd32f3e0","location":{"path":"app/workers/repository_import_worker.rb","lines":{"begin":9,"end":30}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `perform` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"ac08a8c45d58e80f3f9f0e821af8c678","location":{"path":"app/workers/create_pipeline_worker.rb","lines":{"begin":9,"end":9}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sanity_check!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e3c40adf8befa842892c33a2ef33ef6","location":{"path":"app/workers/object_storage/migrate_uploads_worker.rb","lines":{"begin":71,"end":81}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5767377fdd633dc52d45bb5edaf792ad","location":{"path":"app/workers/object_storage/background_move_worker.rb","lines":{"begin":10,"end":21}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"873b9f464ca14204612b7168bd6cd51a","location":{"path":"app/workers/repository_update_remote_mirror_worker.rb","lines":{"begin":18,"end":42}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform_repository_checks` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c569f618db6dab9061d1c9a4fbd00161","location":{"path":"app/workers/repository_check/batch_worker.rb","lines":{"begin":34,"end":49}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `perform` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"fb73a85eb279eea1e916d045d1752c0b","location":{"path":"app/workers/update_merge_requests_worker.rb","lines":{"begin":9,"end":9}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9c731770fbe34f3e5817178bf1c1c9c2","location":{"path":"app/workers/post_receive.rb","lines":{"begin":6,"end":25}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_project_changes` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dcfa21f106c7ddc7d59ef6a69ed225a5","location":{"path":"app/workers/post_receive.rb","lines":{"begin":29,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"90ee5638594e38ee879df3d7b6670837","location":{"path":"app/workers/git_garbage_collect_worker.rb","lines":{"begin":11,"end":36}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c91f311ed659a5cf472f55294f93721e","location":{"path":"app/workers/project_migrate_hashed_storage_worker.rb","lines":{"begin":9,"end":22}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `handle_failure` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ea43d53b1207bf05223f6e3378b7622f","location":{"path":"app/workers/email_receiver_worker.rb","lines":{"begin":18,"end":51}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fb063b327d5645323a0adeb4a475ab88","location":{"path":"app/workers/create_gpg_signature_worker.rb","lines":{"begin":7,"end":29}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"90474c7bc130abea03bc8b0c1a6d72e3","location":{"path":"app/finders/concerns/finder_with_cross_project_access.rb","lines":{"begin":20,"end":32}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `by_due_date` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a7cd7e8d0b5f825ced9cce3fd0921f8b","location":{"path":"app/finders/issues_finder.rb","lines":{"begin":74,"end":90}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `all_groups` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9450c1e48d60393e153d88597dff24f6","location":{"path":"app/finders/groups_finder.rb","lines":{"begin":44,"end":54}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"be7b1d323697075c6c41a353ae096f3b","location":{"path":"app/finders/environments_finder.rb","lines":{"begin":11,"end":47}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"caa84d8a4abdc89470c9734cc20f668b","location":{"path":"app/finders/environments_finder.rb","lines":{"begin":11,"end":47}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `issuable_finder.rb` has 326 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"a0da380c5a294186d2f8b47c7dbbefc2","location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":30,"end":487}},"other_locations":[],"remediation_points":2294400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestones` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"10f2a889ce05a62411b22bd93f5cc5fb","location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":195,"end":214}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `by_milestone` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4b5705f5f9e383902a98d61545a61a9f","location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":432,"end":447}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `IssuableFinder` has 47 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"863953770234ecee39c26f4a376fd96f","location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":30,"end":487}},"other_locations":[],"remediation_points":3900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `target` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"10931dd4f683d88c8163ae49517c9ab8","location":{"path":"app/finders/notes_finder.rb","lines":{"begin":30,"end":46}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `by_archived` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8a0163155f7d44abda52664f1aeec277","location":{"path":"app/finders/projects_finder.rb","lines":{"begin":153,"end":167}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `label_ids` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8efea533e852a1ae66e134916ddee3ed","location":{"path":"app/finders/labels_finder.rb","lines":{"begin":30,"end":59}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dcae9d4bea82b824bf3281aee3585c28","location":{"path":"app/finders/labels_finder.rb","lines":{"begin":138,"end":149}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b10747a7f6c51031eb2aec28a84ab64","location":{"path":"app/finders/autocomplete/users_finder.rb","lines":{"begin":27,"end":40}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `distinct_on` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"684bb4005fc7ad31c75a6725f7cc9bb4","location":{"path":"app/finders/members_finder.rb","lines":{"begin":38,"end":71}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `TodosFinder` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"bda816f835a5b1d9557e5018542c8d40","location":{"path":"app/finders/todos_finder.rb","lines":{"begin":17,"end":195}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_block` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2764263922dc65629ac261e7b22a4aaa","location":{"path":"rubocop/cop/avoid_break_from_strong_memoize.rb","lines":{"begin":26,"end":37}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d3d826dd296b41ed52a3d279711f08c1","location":{"path":"rubocop/cop/migration/reversible_add_column_with_default.rb","lines":{"begin":22,"end":31}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7c2b352ab3b2b95bfd273eed48b0abde","location":{"path":"rubocop/cop/migration/update_column_in_batches.rb","lines":{"begin":14,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1ca44c40f4350122f1d014c4832b0903","location":{"path":"rubocop/cop/migration/update_large_table.rb","lines":{"begin":52,"end":64}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"259f2282957271f68f06320d6e9c9304","location":{"path":"rubocop/cop/migration/add_reference.rb","lines":{"begin":13,"end":31}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_def` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1eade1345b7999362c94ffdd69c9abe5","location":{"path":"rubocop/cop/migration/add_index.rb","lines":{"begin":12,"end":32}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2957b0117eb58305df4d1c4f628f6e22","location":{"path":"rubocop/cop/migration/add_concurrent_index.rb","lines":{"begin":14,"end":26}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a265e4d95fda1c51004e6b280d4faa62","location":{"path":"rubocop/cop/migration/hash_index.rb","lines":{"begin":16,"end":35}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4642fe2fb8caa127e076b834679b38c7","location":{"path":"rubocop/cop/migration/add_column.rb","lines":{"begin":16,"end":35}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"53f08cb9ea1aded1c4867d5ec20513e1","location":{"path":"rubocop/cop/migration/datetime.rb","lines":{"begin":26,"end":38}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_def` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"33c826c076b3d604191dc285ae1c197f","location":{"path":"rubocop/cop/migration/remove_column.rb","lines":{"begin":13,"end":26}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5f6ab7199493398668d2f7fb4ae05a45","location":{"path":"rubocop/cop/migration/safer_boolean_column.rb","lines":{"begin":34,"end":58}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5d3a133fdd5733e12d588c313215673c","location":{"path":"rubocop/cop/migration/remove_concurrent_index.rb","lines":{"begin":14,"end":21}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_block` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"60e29c1a431f0815adefc57a3daa815a","location":{"path":"rubocop/cop/avoid_return_from_blocks.rb","lines":{"begin":28,"end":39}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parent_blocks` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bf710c1583fafaba0fb4a8d7507199ca","location":{"path":"rubocop/cop/avoid_return_from_blocks.rb","lines":{"begin":55,"end":65}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_class` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ac6a847fc937c343670c7deedadaa245","location":{"path":"rubocop/cop/code_reuse/presenter.rb","lines":{"begin":20,"end":37}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_all_send_nodes` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"86036271965f234231993a45bdebb14d","location":{"path":"rubocop/cop/code_reuse/worker.rb","lines":{"begin":31,"end":46}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_class` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3468ff5bbfe02d60179b461cee64dcd2","location":{"path":"rubocop/cop/code_reuse/serializer.rb","lines":{"begin":20,"end":37}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f6cd42fcfb267a05384d5c43432583d9","location":{"path":"rubocop/cop/code_reuse/active_record.rb","lines":{"begin":91,"end":107}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_if` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c31d398a0e65e6ad5bf772ffbe612d27","location":{"path":"rubocop/cop/line_break_around_conditional_block.rb","lines":{"begin":50,"end":58}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `autocorrect` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6790fd3dafb4732df58459faaaee4a86","location":{"path":"rubocop/cop/line_break_around_conditional_block.rb","lines":{"begin":60,"end":71}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"83f3ce0952418f2d18756e7f01d3f37e","location":{"path":"rubocop/cop/project_path_helper.rb","lines":{"begin":10,"end":21}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"rubocop/cop/project_path_helper.rb","lines":{"begin":18,"end":18}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"16132947f8a9d308be379b39b9a5d3e6"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `perform` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"00f629cf63f892026e01afc3ea1f7651","location":{"path":"qa/qa/scenario/test/sanity/selectors.rb","lines":{"begin":10,"end":49}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `configure!` has 44 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"024a3eec035772b027f91725d6217728","location":{"path":"qa/qa/runtime/browser.rb","lines":{"begin":34,"end":100}},"other_locations":[],"remediation_points":1056000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b5bc236abe446161666817def77f8ddf","location":{"path":"qa/qa/specs/runner.rb","lines":{"begin":16,"end":34}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fabricate!` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d6f04a78d84c195424103bc166feb302","location":{"path":"qa/qa/factory/repository/push.rb","lines":{"begin":33,"end":75}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `fabricate!` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0619d32fe486fa8cd5d32a978dbba114","location":{"path":"qa/qa/factory/repository/push.rb","lines":{"begin":33,"end":75}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fabricate!` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3806fbbd18c151ecd8c0b6a777c6eafd","location":{"path":"qa/qa/factory/resource/kubernetes_cluster.rb","lines":{"begin":16,"end":55}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `fabricate!` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9a68225ad27f7d73dc48ba5138c97cc4","location":{"path":"qa/qa/factory/resource/kubernetes_cluster.rb","lines":{"begin":16,"end":55}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fabricate!` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"455a528019b993508c00252ac2356d13","location":{"path":"qa/qa/factory/resource/branch.rb","lines":{"begin":19,"end":73}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `fabricate!` has 40 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8c20ae555f1495416940ef8126556a4b","location":{"path":"qa/qa/factory/resource/branch.rb","lines":{"begin":19,"end":73}},"other_locations":[],"remediation_points":960000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sign_in_using_credentials` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cf2fda789a392298297395e24cd69833","location":{"path":"qa/qa/page/main/login.rb","lines":{"begin":43,"end":60}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `expand_section` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"403c8764d8456908ed9eeef4e0044633","location":{"path":"qa/qa/page/settings/common.rb","lines":{"begin":8,"end":19}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Repository` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"1f47da27b9c8ebadc35daab5ecdfacbd","location":{"path":"qa/qa/git/repository.rb","lines":{"begin":7,"end":126}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Base` has 36 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"e990b96533b4c82f0e6c7290f79ea42e","location":{"path":"lib/declarative_policy/base.rb","lines":{"begin":2,"end":330}},"other_locations":[],"remediation_points":2800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"lib/declarative_policy/condition.rb","lines":{"begin":72,"end":72}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"453a21507c9d4e6b09338d4ea74a2cb6"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `run` has a Cognitive Complexity of 29 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9109da1712e5180769d6dee88e01ab4b","location":{"path":"lib/declarative_policy/runner.rb","lines":{"begin":76,"end":108}},"other_locations":[],"remediation_points":2550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `steps_by_score` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c728761c8bd550bb676d5ac29cc54f2c","location":{"path":"lib/declarative_policy/runner.rb","lines":{"begin":130,"end":179}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `steps_by_score` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fda7518f9492e598aeaa14084a16fcad","location":{"path":"lib/declarative_policy/runner.rb","lines":{"begin":130,"end":179}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `flattened` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"938e199f4acb9d23e67b122acf7df125","location":{"path":"lib/declarative_policy/step.rb","lines":{"begin":51,"end":76}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e8ff80c1013b45a887d5b05aebda0a6d","location":{"path":"lib/mattermost/session.rb","lines":{"begin":100,"end":112}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Session` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"b6bc576e4f2b4e33eeb24fbd9a4756e0","location":{"path":"lib/mattermost/session.rb","lines":{"begin":23,"end":185}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `from_params` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8708d87b45e03dda5cf801ba3c8b4ae8","location":{"path":"lib/uploaded_file.rb","lines":{"begin":31,"end":50}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `object_link_commit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4d75625eef2a0bac64c81e6a99b470c7","location":{"path":"lib/banzai/filter/merge_request_reference_filter.rb","lines":{"begin":66,"end":77}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e4a7914b17bf2c4568c886ae0510580a","location":{"path":"lib/banzai/filter/issuable_state_filter.rb","lines":{"begin":13,"end":29}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c21517ccaabda5807f34857c92c02c20","location":{"path":"lib/banzai/filter/emoji_filter.rb","lines":{"begin":11,"end":26}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5b2ff3f6e59ccd27a3e8df6e92792289","location":{"path":"lib/banzai/filter/gollum_tags_filter.rb","lines":{"begin":64,"end":87}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_relative_path` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1fc007ca58af10483040ad6c3acafc7b","location":{"path":"lib/banzai/filter/relative_link_filter.rb","lines":{"begin":124,"end":140}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d11bc0fb07cb93de54e50ca187a470c","location":{"path":"lib/banzai/filter/inline_diff_filter.rb","lines":{"begin":8,"end":20}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"229de514760543ff1eae907b8931ae4a","location":{"path":"lib/banzai/filter/spaced_link_filter.rb","lines":{"begin":44,"end":60}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 28 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f2adb2780e68c3edecbf3243959331bc","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":99,"end":148}},"other_locations":[],"remediation_points":2450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `object_link_filter` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"756d39528c96fdea1b53fc02cd2f1b54","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":161,"end":204}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `AbstractReferenceFilter` has 31 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"bf8591fc0f7db3df80a7dd6204670584","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":7,"end":359}},"other_locations":[],"remediation_points":2300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `call` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"158c3e7c7c151f7461c460b58c8eb7fb","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":99,"end":148}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `object_link_filter` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cce139d64bf42a05629c35156d98c24b","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":161,"end":204}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f009def1da08a1c0910203aacb888646","location":{"path":"lib/banzai/filter/external_issue_reference_filter.rb","lines":{"begin":30,"end":55}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e19e0b6d7127e7778817e23a5c2202a6","location":{"path":"lib/banzai/filter/external_link_filter.rb","lines":{"begin":9,"end":23}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `highlight_node` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9dc3f1809c63dc39b3c7b301b2c8b0a8","location":{"path":"lib/banzai/filter/syntax_highlight_filter.rb","lines":{"begin":19,"end":53}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0228be52a5d55a59f39144f901ba3de8","location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":28,"end":51}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `user_link_filter` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e1a29dd5f3f29173136fead3b7feacff","location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":61,"end":75}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c56c41f7c09938431d527c9d84ee464c","location":{"path":"lib/banzai/filter/project_reference_filter.rb","lines":{"begin":26,"end":47}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"23c5be476e6cfabb3f50b8995a21481e","location":{"path":"lib/banzai/filter/autolink_filter.rb","lines":{"begin":51,"end":67}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `autolink_match` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"63ef57bb6d5d4556a6d292a19e1eb7aa","location":{"path":"lib/banzai/filter/autolink_filter.rb","lines":{"begin":79,"end":116}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_unsafe_links` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"578c89c0c495a9f9912ae5425a9baf35","location":{"path":"lib/banzai/filter/sanitization_filter.rb","lines":{"begin":64,"end":91}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_unsafe_table_style` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"66ae1f700aade40e1b93928de1809522","location":{"path":"lib/banzai/filter/sanitization_filter.rb","lines":{"begin":101,"end":114}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_link_attr` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fe0e7a59ae612e2391ff8956ece5a37e","location":{"path":"lib/banzai/filter/absolute_link_filter.rb","lines":{"begin":21,"end":29}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"91ceeacf382258ddf799f7e8df323086","location":{"path":"lib/banzai/filter/commit_trailers_filter.rb","lines":{"begin":29,"end":43}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `link_to_user` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2cae91969679378626a39ee56d06c378","location":{"path":"lib/banzai/filter/commit_trailers_filter.rb","lines":{"begin":81,"end":115}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"complex_logic","content":{"body":""},"description":"Consider simplifying this complex logical expression.","location":{"path":"lib/banzai/filter/math_filter.rb","lines":{"begin":27,"end":36}},"other_locations":[],"remediation_points":400000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"c132e79b1733f1ef2e4fba2f07f29935"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"88347949c1ff362bde8f0c3f4fe03dc2","location":{"path":"lib/banzai/filter/table_of_contents_filter.rb","lines":{"begin":21,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_parent` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b3d0438ee7565d4ad0316ba6f05a9baf","location":{"path":"lib/banzai/filter/table_of_contents_filter.rb","lines":{"begin":102,"end":115}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `nodes_visible_to_user` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c851d1fc927d9308df218d02f42fde99","location":{"path":"lib/banzai/reference_parser/user_parser.rb","lines":{"begin":25,"end":48}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `nodes_user_can_reference` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bfa5e9970213f7ef43f0e22780710fb3","location":{"path":"lib/banzai/reference_parser/user_parser.rb","lines":{"begin":64,"end":87}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `grouped_objects_for_nodes` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"56d2576cd012b6e37a94c3cac839a5fd","location":{"path":"lib/banzai/reference_parser/base_parser.rb","lines":{"begin":134,"end":147}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `BaseParser` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"3b65d4312cddf7542466b907246e6568","location":{"path":"lib/banzai/reference_parser/base_parser.rb","lines":{"begin":34,"end":255}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cache_collection_render` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"86955f7d246e45efcedaab9abac1b39e","location":{"path":"lib/banzai/renderer.rb","lines":{"begin":76,"end":103}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `filter_nodes_at_beginning` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"575e435b81c0cb589731931503f3f7ef","location":{"path":"lib/banzai/querying.rb","lines":{"begin":45,"end":64}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a1435f7fb2485ddc0076596fa851b5e1","location":{"path":"lib/file_size_validator.rb","lines":{"begin":8,"end":17}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_validity!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4c0eea681bcd3ea73ab99bdeb9992c52","location":{"path":"lib/file_size_validator.rb","lines":{"begin":19,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4ad97d789c0a7bb2c49b85af370fa1cb","location":{"path":"lib/file_size_validator.rb","lines":{"begin":36,"end":65}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `projects.rb` has 419 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"de2195ccf1e60f3a00e250b69809f314","location":{"path":"lib/api/projects.rb","lines":{"begin":1,"end":513}},"other_locations":[],"remediation_points":3633600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `apply_filters` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3d37733ec13b40a7294cb95cca30d7c0","location":{"path":"lib/api/projects.rb","lines":{"begin":21,"end":27}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `entities.rb` has 1174 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"14ca44a7292f309674c3e6bf5668d68a","location":{"path":"lib/api/entities.rb","lines":{"begin":1,"end":1469}},"other_locations":[],"remediation_points":14505600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_note` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5bdd6d096b03346b779bae1a47c5df19","location":{"path":"lib/api/helpers/notes_helpers.rb","lines":{"begin":89,"end":101}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_job!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9c2df515fd3e4e567e5c43ce34122ebc","location":{"path":"lib/api/helpers/runner.rb","lines":{"begin":34,"end":42}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `filter_runners` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8fe97bd7f7ff40f6f347505c5401de55","location":{"path":"lib/api/runners.rb","lines":{"begin":163,"end":176}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_show_runner!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"678c4afecdf242daef2608fb52f16121","location":{"path":"lib/api/runners.rb","lines":{"begin":184,"end":188}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_delete_runner!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"229dc324c7b4cd0754d3f429d0efa489","location":{"path":"lib/api/runners.rb","lines":{"begin":196,"end":201}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_enable_runner!` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fdde7223cb136c51a33619a48b7e10e9","location":{"path":"lib/api/runners.rb","lines":{"begin":203,"end":210}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `issues.rb` has 266 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"49d5eae8f5a9b7ef20af4a226580c9fb","location":{"path":"lib/api/issues.rb","lines":{"begin":1,"end":332}},"other_locations":[],"remediation_points":1430400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `filter_builds` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b03e3d209e73511af90d54684b559d3a","location":{"path":"lib/api/jobs.rb","lines":{"begin":176,"end":185}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `list_milestones_for` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8e051bd18be2af43510bfc934382e630","location":{"path":"lib/api/milestone_responses.rb","lines":{"begin":30,"end":37}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `assign_file_vars!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4be22423f2ecad963ae6b37b42f43510","location":{"path":"lib/api/files.rb","lines":{"begin":25,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `assign_blob_vars!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e73cbaada1130cabaf0dfa4c08f70656","location":{"path":"lib/api/repositories.rb","lines":{"begin":22,"end":35}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `services.rb` has 836 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"68db3252fd8eaa9d286783dcd5099377","location":{"path":"lib/api/services.rb","lines":{"begin":2,"end":864}},"other_locations":[],"remediation_points":9638400,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `runner.rb` has 266 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"1e11149dffed6dc2e322c584638327bf","location":{"path":"lib/api/runner.rb","lines":{"begin":1,"end":316}},"other_locations":[],"remediation_points":1430400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_requests.rb` has 322 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"3de9fe3d6f81552276ac816a0f052fb7","location":{"path":"lib/api/merge_requests.rb","lines":{"begin":1,"end":398}},"other_locations":[],"remediation_points":2236800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_merge_request_errors!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4b0820e7ae582cfc463b18401dc7dfd6","location":{"path":"lib/api/merge_requests.rb","lines":{"begin":145,"end":159}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `helpers.rb` has 381 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"5db68c679d275d3963e0f9660988e103","location":{"path":"lib/api/helpers.rb","lines":{"begin":1,"end":533}},"other_locations":[],"remediation_points":3086400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `attributes_for_keys` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"63712b203f7a63b38bcb9eea53c3001d","location":{"path":"lib/api/helpers.rb","lines":{"begin":289,"end":299}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project_finder_params` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"268f87eb93bd855d1807610fcddd6efe","location":{"path":"lib/api/helpers.rb","lines":{"begin":408,"end":420}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `present_carrierwave_file!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"360f85bdb8a06a280a62224143731f15","location":{"path":"lib/api/helpers.rb","lines":{"begin":440,"end":452}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sudo!` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ddabeb34a3b3e3067c6b88323ce10dcc","location":{"path":"lib/api/helpers.rb","lines":{"begin":468,"end":487}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `users.rb` has 652 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"da0cb2b4e7ad1056fa1e4f7a0fc774d7","location":{"path":"lib/api/users.rb","lines":{"begin":1,"end":837}},"other_locations":[],"remediation_points":6988800,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_groups` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"eea17dfbc1a8fa0837c83d51e2708400","location":{"path":"lib/api/groups.rb","lines":{"begin":42,"end":56}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `inject_rblineprof` has a Cognitive Complexity of 49 (exceeds 5 allowed). Consider refactoring.","fingerprint":"16157b2c336822ada397c30b37d0e63b","location":{"path":"lib/peek/rblineprof/custom_controller_helpers.rb","lines":{"begin":17,"end":100}},"other_locations":[],"remediation_points":4550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `inject_rblineprof` has 62 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9665938c091b2575d937990fbc609dac","location":{"path":"lib/peek/rblineprof/custom_controller_helpers.rb","lines":{"begin":17,"end":100}},"other_locations":[],"remediation_points":1488000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `flatten_comments` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3acab7e81bc6f21925b3944dc13c9d25","location":{"path":"lib/bitbucket_server/representation/comment.rb","lines":{"begin":87,"end":111}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_errors!` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5656af648a8148a4747d9750b93c8a0c","location":{"path":"lib/bitbucket_server/connection.rb","lines":{"begin":62,"end":74}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extract_ref` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8b8bde6183c235753993662c791c5f50","location":{"path":"lib/extracts_path.rb","lines":{"begin":40,"end":74}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `assign_ref_vars` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"994b000ce7697f00c9d2fdef6d5f219c","location":{"path":"lib/extracts_path.rb","lines":{"begin":108,"end":133}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `gitaly_configuration_toml` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7ce1b00e1e01659ed25ab9357f9d47d7","location":{"path":"lib/gitlab/setup_helper.rb","lines":{"begin":14,"end":43}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_with_user_password` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0212b6bdb5ef9f95aaaad8df3d25a15a","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":47,"end":83}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `rate_limit!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"18c084f7a7640dd9934777afe795d6e1","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":85,"end":104}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `service_request_check` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fcdcb167117e9fbdc60bcccfa8c52ce9","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":112,"end":128}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `deploy_token_check` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3349988ec83e4933b92c3a7e3824cc58","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":185,"end":199}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `lfs_token_check` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f1a1535569de597558d429c01457d5e","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":202,"end":228}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_access_token_check` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e8fcdca34d7247788de08e6051143d2","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":230,"end":245}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `setup_subscribers` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"85ca5972d830d83ef927532dcf66122f","location":{"path":"lib/gitlab/performance_bar/peek_query_tracker.rb","lines":{"begin":17,"end":36}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fields` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dbfd2a1c630915fc2b4befaca081c456","location":{"path":"lib/gitlab/slash_commands/presenters/issue_base.rb","lines":{"begin":21,"end":39}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `format_response` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"57bd6ed972d21a41e82e376f51d7a76e","location":{"path":"lib/gitlab/slash_commands/presenters/base.rb","lines":{"begin":47,"end":58}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `text` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0fea0819cd1d628d704dab5f9f56ae5c","location":{"path":"lib/gitlab/slash_commands/presenters/issue_show.rb","lines":{"begin":40,"end":53}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `file.rb` has 255 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"bcd7afc2511311acf93e48f89b257ee8","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":1,"end":356}},"other_locations":[],"remediation_points":1272000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `simple_viewer_class` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"455a703cf96823be99c475db08de0c9f","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":311,"end":339}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `viewer_class_from` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cae1cebb92d35a5859ef229108d1e4c1","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":345,"end":353}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `File` has 54 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"4872155d7a8ab149e4dbfdd0a34d314f","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":3,"end":354}},"other_locations":[],"remediation_points":4600000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `simple_viewer_class` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c663249a7eb69d79d53815c02da1287e","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":311,"end":339}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"62d56326c2f46a050e8a9f1219488e25","location":{"path":"lib/gitlab/diff/line.rb","lines":{"begin":10,"end":10}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Position` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"44aef8e9a5055d83b7605ef01a8ffcd5","location":{"path":"lib/gitlab/diff/position.rb","lines":{"begin":5,"end":150}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `highlight` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"922e6aee35a311a79f1043b07ee7064d","location":{"path":"lib/gitlab/diff/highlight.rb","lines":{"begin":21,"end":44}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `highlight_line` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b41f2627f4109bdc5b74287cf3ba0d21","location":{"path":"lib/gitlab/diff/highlight.rb","lines":{"begin":48,"end":64}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parallelize` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"631e96ad1ef01b07245ee2a542d76275","location":{"path":"lib/gitlab/diff/parallel_diff.rb","lines":{"begin":10,"end":58}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `parallelize` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c801b9d4ce1a0b15a92b13570e4b4a73","location":{"path":"lib/gitlab/diff/parallel_diff.rb","lines":{"begin":10,"end":58}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `diff_stats_collection` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d1edfc550eecbd5daa471adab0ab3385","location":{"path":"lib/gitlab/diff/file_collection/base.rb","lines":{"begin":50,"end":59}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse` has a Cognitive Complexity of 29 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bfc88a6b4fdafecdb4881ebd0a709e90","location":{"path":"lib/gitlab/diff/parser.rb","lines":{"begin":6,"end":63}},"other_locations":[],"remediation_points":2550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `parse` has 42 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"6d81f9668924e2328ae1d3b978012b2e","location":{"path":"lib/gitlab/diff/parser.rb","lines":{"begin":6,"end":63}},"other_locations":[],"remediation_points":1008000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trace` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"678e46609976cce578070c879cdeaa09","location":{"path":"lib/gitlab/diff/position_tracer.rb","lines":{"begin":18,"end":86}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trace_added_line` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3d3253565d5f52fdc8e227a610a4ba02","location":{"path":"lib/gitlab/diff/position_tracer.rb","lines":{"begin":90,"end":127}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trace_removed_line` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a2345cd4720ceb59ce0efdf2c931d70","location":{"path":"lib/gitlab/diff/position_tracer.rb","lines":{"begin":129,"end":158}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trace_unchanged_line` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"68d033636c672cf93801ba4a440a9b10","location":{"path":"lib/gitlab/diff/position_tracer.rb","lines":{"begin":160,"end":199}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `map_line_number` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dc329e8839fe1c0b24ddd3dbaa5b8485","location":{"path":"lib/gitlab/diff/line_mapper.rb","lines":{"begin":30,"end":61}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `popen_with_detail` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1f3016518aa9a963248a02cac889fd75","location":{"path":"lib/gitlab/popen.rb","lines":{"begin":18,"end":51}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `request_cache` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c7f577fa9ce2f42e5f27aac8db514fe3","location":{"path":"lib/gitlab/cache/request_cache.rb","lines":{"begin":23,"end":58}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `store_in_cache_if_needed` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"baa77c71d04626f49bd5c1e57a0e2ca9","location":{"path":"lib/gitlab/cache/ci/project_pipeline_status.rb","lines":{"begin":92,"end":100}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `aggregate_rblineprof` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5c969b511dec60cc5ae82a493581e987","location":{"path":"lib/gitlab/sherlock/line_profiler.rb","lines":{"begin":59,"end":85}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `subscribe_to_active_record` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cb47c9a23a0a444f0c02f2ce82101c23","location":{"path":"lib/gitlab/sherlock/transaction.rb","lines":{"begin":88,"end":96}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_keys_not_found_in_db` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"110e16cf8bc4b13203ae16707be71f9e","location":{"path":"lib/gitlab/shell.rb","lines":{"begin":229,"end":246}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Shell` has 37 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"9bb914cee67608b26add7bcc4f81b00b","location":{"path":"lib/gitlab/shell.rb","lines":{"begin":6,"end":445}},"other_locations":[],"remediation_points":2900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `bulk_insert` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"46738c8e24da7d644d4613d9605d7262","location":{"path":"lib/gitlab/database.rb","lines":{"begin":167,"end":197}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f71d3611436ba9323475933db3b224e0","location":{"path":"lib/gitlab/gitaly_client/conflict_files_stitcher.rb","lines":{"begin":10,"end":26}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a4899ef1be9285a53b4a3412a311ee1f","location":{"path":"lib/gitlab/gitaly_client/blobs_stitcher.rb","lines":{"begin":10,"end":29}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `update_page` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"5e1fe756db8adf0384ead3d7ca4d40bb","location":{"path":"lib/gitlab/gitaly_client/wiki_service.rb","lines":{"begin":41,"end":41}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_file` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f2ac95cd9282cf75ce51181ba7bcf087","location":{"path":"lib/gitlab/gitaly_client/wiki_service.rb","lines":{"begin":128,"end":152}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `wiki_page_from_iterator` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8d18749d8e89aa853f84d5b8ab2fa1e0","location":{"path":"lib/gitlab/gitaly_client/wiki_service.rb","lines":{"begin":171,"end":192}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `commit_service.rb` has 334 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"459baf31c3d4b57e8c9df845211d85c9","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":1,"end":431}},"other_locations":[],"remediation_points":2409600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `diff` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6776fdb83e8edfae5378441fd52a4925","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":33,"end":63}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tree_entry` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"00baf604adba61e863f7d154a8dee72b","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":78,"end":109}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_commit` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"186f0eba8b7425edc8cc3c73c38cd40a","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":242,"end":262}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `CommitService` has 31 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"49c846fd451fa9152c64a84b18cea747","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":3,"end":429}},"other_locations":[],"remediation_points":2300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `ref_service.rb` has 252 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"eb1efcba78462f496979baf4cb6cd2ad","location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":1,"end":327}},"other_locations":[],"remediation_points":1228800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `RefService` has 31 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"00c5cfab9191c2db4747039325616652","location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":3,"end":325}},"other_locations":[],"remediation_points":2300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `repository_service.rb` has 323 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"493989beb71c8d830c2b79d5553c6147","location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":1,"end":396}},"other_locations":[],"remediation_points":2251200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fetch_remote` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f3aee0aba331f59ab18ffef3792b052a","location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":64,"end":81}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `RepositoryService` has 35 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"6ca9b3950e1ce99211058bd24b696cd9","location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":3,"end":394}},"other_locations":[],"remediation_points":2700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `user_squash` has 7 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"3244c060b29a67dfaca14f5fd90c0c69","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":207,"end":207}},"other_locations":[],"remediation_points":525000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `user_commit_files` has 8 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"e11b9bf00977dfd40d466849a02aad7e","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":234,"end":235}},"other_locations":[],"remediation_points":600000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `user_commit_files_request_header` has 8 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"74f8cedbd2e30f093d96c68640b17e2f","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":316,"end":317}},"other_locations":[],"remediation_points":600000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `operation_service.rb` has 284 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"e9b10e91d8aa4aecb2dd40e2ff73151e","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":1,"end":343}},"other_locations":[],"remediation_points":1689600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `user_create_branch` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f83bdf53147b9d2930979ca6a622f44f","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":48,"end":69}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `user_merge_branch` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"96dce6cec3c6d464fe3b164bf05adf61","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":101,"end":137}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `user_commit_files` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b324bd9e25781ee4f45ebe63530654b5","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":233,"end":274}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `define_predicate_methods` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1748406e1b1a3efed8bbdbc2f7310efb","location":{"path":"lib/gitlab/fake_application_settings.rb","lines":{"begin":9,"end":19}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"306bfd1a5668c64b46f9a50ffcb84761","location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":76,"end":100}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_chunk` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"718a1c6197d9172beada51e8fb725aa8","location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":147,"end":173}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `HttpIO` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"f90f3bbe0002c0551d2c102e8d6cc2bb","location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":5,"end":192}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `system_usage_data` has 49 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e44de166e4765b13674da7e6f1adbb03","location":{"path":"lib/gitlab/usage_data.rb","lines":{"begin":35,"end":85}},"other_locations":[],"remediation_points":1176000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `print_methods` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"74e543934a1cb0119b1cb9bc2bef6f4a","location":{"path":"lib/gitlab/profiler/total_time_flat_printer.rb","lines":{"begin":13,"end":36}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `format_issue_comment_body` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"4b9885935625732368d27f4d57c09a6b","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":327,"end":327}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `importer.rb` has 283 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"1456110a9f0cea86dbda2fdc6ff8b006","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":1,"end":371}},"other_locations":[],"remediation_points":1675200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issues` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fda606a33fed0d051037b3e7ec312764","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":82,"end":126}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issue_comments` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e40d64a7ad3fca74d9f0603fcdfde073","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":146,"end":179}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `format_updates` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"627781504f289b64edb3cae5ded46686","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":237,"end":293}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `format_attachments` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3eae70f0804e77646cf183b658337852","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":312,"end":325}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_issues` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"140622e56443443be6d89cf7f8134759","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":82,"end":126}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_issue_comments` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"81d77c1a0b5ebc4248e39438a9b784c1","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":146,"end":179}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `format_updates` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9b843b25138348d8d9e7cf6df7a2f0ed","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":237,"end":293}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `full_path` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"200d2aea21e0c3f34efb9b3a8051a82f","location":{"path":"lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb","lines":{"begin":7,"end":15}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `rank_rows` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dc13ef2bef6a33cc8bc4e67b71564878","location":{"path":"lib/gitlab/database/median.rb","lines":{"begin":140,"end":174}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `migration_helpers.rb` has 543 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"01e21ae134d30a14bd512652b7a940ab","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":1,"end":1078}},"other_locations":[],"remediation_points":5419200,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_timestamps_with_timezone` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c59bbcc4bf6935d5c169f128844c4c91","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":17,"end":40}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_concurrent_foreign_key` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ce151ede627cdfdf31c565ade50905ea","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":152,"end":206}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `disable_statement_timeout` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"31b96e31df6242d18aba1d4abf07f1f9","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":240,"end":272}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_column_in_batches` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"21d13cfd466605197c31d8958201310c","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":323,"end":380}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_column_with_default` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"009915036d7d7932903bc6f766511a5e","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":406,"end":438}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `copy_indexes` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"60d019b8234aa4a0d7d445a95ee1e8ae","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":817,"end":859}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `bulk_queue_background_migration_jobs_by_range` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"128bc9f2059885db7d254bdb6a31d4b3","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":972,"end":993}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `add_concurrent_foreign_key` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f6243b1ac45a70775392f6137b36e35f","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":152,"end":206}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `update_column_in_batches` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"6dc99342ecbf79855eb8044b9e00cbdd","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":323,"end":380}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `change_column_type_using_background_migration` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"39133c1e5a2502598dbf0ec8dc0af798","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":593,"end":640}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `rename_column_using_background_migration` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fc89feb63b650bd5547ac2ba44210012","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":670,"end":731}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `copy_indexes` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8b98ee08501009b6bf421b8064689b30","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":817,"end":859}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fetch_config` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4b3b0f6bd727a62eae22091574b81015","location":{"path":"lib/gitlab/mail_room.rb","lines":{"begin":31,"end":49}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `note_url` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"59ca4f1b16f978d96a1c7e36f7e69d62","location":{"path":"lib/gitlab/url_builder.rb","lines":{"begin":52,"end":71}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `rewrite` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f45f6ba5c50a0ab576b49cf1b282a615","location":{"path":"lib/gitlab/gfm/uploads_rewriter.rb","lines":{"begin":19,"end":29}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unfold_reference` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"be381321436537b6ec6941dcff14d0b9","location":{"path":"lib/gitlab/gfm/reference_rewriter.rb","lines":{"begin":56,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sections` has a Cognitive Complexity of 24 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0f6b50ef494458ae4f54e52a28568869","location":{"path":"lib/gitlab/conflict/file.rb","lines":{"begin":49,"end":110}},"other_locations":[],"remediation_points":2050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_match_line_header` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4fef2b9774e94d0bd02f1158378f701c","location":{"path":"lib/gitlab/conflict/file.rb","lines":{"begin":123,"end":137}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `as_json` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ed7b03c5a2c2d110a9a9db9e00f3d8d0","location":{"path":"lib/gitlab/conflict/file.rb","lines":{"begin":150,"end":168}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `sections` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"629b80d357b767eb9b1f37185affacd4","location":{"path":"lib/gitlab/conflict/file.rb","lines":{"begin":49,"end":110}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `which` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"565d3b45775f71100b9c7277076be0f6","location":{"path":"lib/gitlab/utils.rb","lines":{"begin":63,"end":74}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `render_go_doc` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e0fac0dd89237b115b26def1d32171a6","location":{"path":"lib/gitlab/middleware/go.rb","lines":{"begin":22,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `with_open_files` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"83c9d42c4a44fe57eeee2a2b269ddb60","location":{"path":"lib/gitlab/middleware/multipart.rb","lines":{"begin":38,"end":57}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `decorate_params_value` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"32d0634549f8a039069852514c0c4e20","location":{"path":"lib/gitlab/middleware/multipart.rb","lines":{"begin":60,"end":82}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"100b77808e789566083b5722f853d491","location":{"path":"lib/gitlab/legacy_github_import/project_creator.rb","lines":{"begin":6,"end":6}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issues` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3bfd1976bd1c3f5b63ad73183773103c","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":117,"end":136}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_pull_requests` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d3b76e6c4972600af102835c1739459a","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":139,"end":165}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_comments` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8131d9fa54052fb8688da41fca5b29ff","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":222,"end":245}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_wiki` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c9017489731af795ba61fdc442e91922","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":266,"end":278}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_releases` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6cf70c97f808766cb57657e58420cb89","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":280,"end":291}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Importer` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"564493c4d63110500702ce8552822c02","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":3,"end":339}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_object_to_import` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1c46822af079d421dee8173b07f3b80a","location":{"path":"lib/gitlab/github_import/parallel_scheduling.rb","lines":{"begin":78,"end":106}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_page` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d8e73017cc50cf5ae0ff94ea436d3ced","location":{"path":"lib/gitlab/github_import/client.rb","lines":{"begin":91,"end":112}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `with_rate_limit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f8e67dd27500a710187cc201ddc1dded","location":{"path":"lib/gitlab/github_import/client.rb","lines":{"begin":133,"end":149}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Client` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"8c409ff9cd79515f70c131a687a58c2d","location":{"path":"lib/gitlab/github_import/client.rb","lines":{"begin":16,"end":216}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_sub_relations` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"de78150728d4deb7bbd23ae848875928","location":{"path":"lib/gitlab/import_export/project_tree_restorer.rb","lines":{"begin":134,"end":159}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_sub_relation` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"357889b554818506acea0dfc8f000afc","location":{"path":"lib/gitlab/import_export/project_tree_restorer.rb","lines":{"begin":161,"end":171}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e960e3648215aee14c4e5443b5f897b0","location":{"path":"lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb","lines":{"begin":27,"end":49}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1dd9f4457d861e5b12020c99ef408fe4","location":{"path":"lib/gitlab/import_export/after_export_strategy_builder.rb","lines":{"begin":6,"end":17}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"343742e2d2dc60bd360734eccf276cc8","location":{"path":"lib/gitlab/import_export/merge_request_parser.rb","lines":{"begin":13,"end":22}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `copy_project_uploads` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d141822a7e49c9987620bcbcaf7749c0","location":{"path":"lib/gitlab/import_export/uploads_manager.rb","lines":{"begin":44,"end":56}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Importer` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"9b57d9528846dda6448ed9d3a49747c3","location":{"path":"lib/gitlab/import_export/importer.rb","lines":{"begin":3,"end":129}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `imported_object` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7150a66352dfbd84b82e4bfa60942b90","location":{"path":"lib/gitlab/import_export/relation_factory.rb","lines":{"begin":198,"end":209}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `RelationFactory` has 30 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"4f17a460cbd29b77fbb38f05dd3866a4","location":{"path":"lib/gitlab/import_export/relation_factory.rb","lines":{"begin":3,"end":281}},"other_locations":[],"remediation_points":2200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project_attributes` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"80346e71805b8ddd466c6b77a8f2e1e6","location":{"path":"lib/gitlab/import_export/group_project_object_builder.rb","lines":{"begin":49,"end":62}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `build` has 7 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"34c8251c050af78b0e432e62b3bf6eaf","location":{"path":"lib/gitlab/data_builder/push.rb","lines":{"begin":56,"end":56}},"other_locations":[],"remediation_points":525000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `build` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e5d84ee4338c67ad951b6130bf67d21a","location":{"path":"lib/gitlab/data_builder/push.rb","lines":{"begin":56,"end":98}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `build` has 49 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ac9809b4ea4dd090261cab4784070118","location":{"path":"lib/gitlab/data_builder/build.rb","lines":{"begin":6,"end":68}},"other_locations":[],"remediation_points":1176000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `absolute_image_urls` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b291bb28522abd9dde68b0387000c263","location":{"path":"lib/gitlab/hook_data/base_builder.rb","lines":{"begin":22,"end":35}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `safe_hook_attributes` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"682e99b5b83244042ccc251929255272","location":{"path":"lib/gitlab/hook_data/merge_request_builder.rb","lines":{"begin":4,"end":32}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `gitaly_client.rb` has 321 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"aa680293a25a35a2aacd0db231b38fc3","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":1,"end":484}},"other_locations":[],"remediation_points":2222400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"96a0315d42972363e3be0faaf33fcff8","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":124,"end":149}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `request_kwargs` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"62070bc24b2fd5133e0a7beab00d9b77","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":197,"end":221}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `feature_enabled?` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"731633ec642f522eb73bf54d30cc752d","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":239,"end":269}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `enforce_gitaly_request_limits` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a75016ab6541606469d82939c599b4ad","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":317,"end":340}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `issues` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"625fb557175f1173f5275a5c774ad3d6","location":{"path":"lib/gitlab/reference_extractor.rb","lines":{"begin":34,"end":45}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `metrics` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"77acda463966e33d89aa2e8120528ca8","location":{"path":"lib/gitlab/health_checks/simple_abstract_check.rb","lines":{"begin":17,"end":25}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `profile` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fee59aefc8f7079890f207651b7981f0","location":{"path":"lib/gitlab/profiler.rb","lines":{"begin":38,"end":79}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `debug` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fdc82615456346012b61b614b718e980","location":{"path":"lib/gitlab/profiler.rb","lines":{"begin":91,"end":109}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `protected_branch_push_checks` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"32eb78f0c97e724ad8a8d5553b542d3f","location":{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":89,"end":99}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `protected_tag_checks` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"06f719f8ef5751a719db79c5e8c24be7","location":{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":111,"end":120}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ChangeAccess` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"c155eb2e64ad1ed777bca59ce76dbaef","location":{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":3,"end":214}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8ee7f543cac7d3fb2d03c2cf82f45f09","location":{"path":"lib/gitlab/checks/commit_check.rb","lines":{"begin":16,"end":28}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `stop` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0578d528100ce788878962a15cbeec2a","location":{"path":"lib/gitlab/daemon.rb","lines":{"begin":39,"end":51}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `verification_status` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"278e4598e8040e0a02ba62435277a8b2","location":{"path":"lib/gitlab/gpg/commit.rb","lines":{"begin":100,"end":112}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 7 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"26ed72f706cbd880946abd65775661b1","location":{"path":"lib/gitlab/bitbucket_server_import/project_creator.rb","lines":{"begin":6,"end":6}},"other_locations":[],"remediation_points":525000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `importer.rb` has 275 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"144e11b64a9050685cd582324f4c5650","location":{"path":"lib/gitlab/bitbucket_server_import/importer.rb","lines":{"begin":3,"end":388}},"other_locations":[],"remediation_points":1560000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Importer` has 28 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"fe99b8de0e4e755d01efc8644f28aa32","location":{"path":"lib/gitlab/bitbucket_server_import/importer.rb","lines":{"begin":5,"end":386}},"other_locations":[],"remediation_points":2000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_hash` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"32c1c4d58157c12dafbd9c492c51a5aa","location":{"path":"lib/gitlab/graphql/variables.rb","lines":{"begin":17,"end":34}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d95b586a43aa6e99b3f767263ce65d16","location":{"path":"lib/gitlab/graphql/present/instrumentation.rb","lines":{"begin":5,"end":32}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7b1d8edac9e190461296777cc29e22e5","location":{"path":"lib/gitlab/graphql/authorize/instrumentation.rb","lines":{"begin":10,"end":31}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument_methods` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d1438aadd557c25b108eea0106855664","location":{"path":"lib/gitlab/metrics/instrumentation.rb","lines":{"begin":70,"end":81}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument_instance_methods` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"eb2114fa42977c795cf347169a0f542c","location":{"path":"lib/gitlab/metrics/instrumentation.rb","lines":{"begin":88,"end":99}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bca595f2e9128a833295cec69e8c1e4e","location":{"path":"lib/gitlab/metrics/instrumentation.rb","lines":{"begin":118,"end":167}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `instrument` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"46dbb1e9f69c06f7d078548fc6061570","location":{"path":"lib/gitlab/metrics/instrumentation.rb","lines":{"begin":118,"end":167}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `submit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b4ef0ec3fde55c4188a9f170e0f50760","location":{"path":"lib/gitlab/metrics/transaction.rb","lines":{"begin":114,"end":129}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `submit_metrics` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9b54961b8f59db3e5122c4208ded0a04","location":{"path":"lib/gitlab/metrics/influx_db.rb","lines":{"begin":48,"end":62}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `pool` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"85d4413ec95715375e2c5ed3923bf252","location":{"path":"lib/gitlab/metrics/influx_db.rb","lines":{"begin":165,"end":181}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `measure` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9ce1cb49fa0e90749c14924f01742eab","location":{"path":"lib/gitlab/metrics/influx_db.rb","lines":{"begin":95,"end":133}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"04b6cf203bf783b43e1ac1b54b888441","location":{"path":"lib/gitlab/fogbugz_import/project_creator.rb","lines":{"begin":6,"end":6}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `format_issue_comment_body` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"1ec61e94902238cc9befa5a1daf367e9","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":279,"end":279}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_cases` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"deff1dce2bf92e8e8a73a7dad5c4c5c9","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":102,"end":146}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issue_comments` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3399ad65a3497aa307eaeb32cb5e83e6","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":158,"end":196}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_cases` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cdbe17ce063af3997de1ea4bb7b62f8b","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":102,"end":146}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_issue_comments` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a350dd17c12b659d965d2849ad05d077","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":158,"end":196}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `git_access.rb` has 288 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"217b36994b7bc085e2426b68d5ae12da","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":3,"end":372}},"other_locations":[],"remediation_points":1747200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_authentication_abilities!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"12742436ece459400a3eaf9d0a1bb60c","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":128,"end":139}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_project_on_push!` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"96a9f1789e7e7f1f1ba2da1e54b194c6","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":189,"end":213}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_push_access!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3ea1e3ce0a376bfea8bcf0212ff90809","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":233,"end":251}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `GitAccess` has 41 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"8b604fd91125836b6b7febd9f9706b53","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":4,"end":371}},"other_locations":[],"remediation_points":3300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_service_account` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ede2112edc62f1c1aaab207b4d2d3bb8","location":{"path":"lib/gitlab/kubernetes/helm/api.rb","lines":{"begin":49,"end":59}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_cluster_role_binding` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"35fbb979c3d0eb66f9ca187bb5295521","location":{"path":"lib/gitlab/kubernetes/helm/api.rb","lines":{"begin":61,"end":71}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"a6788548613abbede0f945c93f499e01","location":{"path":"lib/gitlab/bitbucket_import/project_creator.rb","lines":{"begin":6,"end":6}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issues` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dc7e6d5a30b4b296140a3b3ef8f0e44a","location":{"path":"lib/gitlab/bitbucket_import/importer.rb","lines":{"begin":74,"end":106}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issue_comments` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a48dbffa8fed1a096624520d3125343e","location":{"path":"lib/gitlab/bitbucket_import/importer.rb","lines":{"begin":109,"end":134}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_pull_requests` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5a484ad1bd84dce7a3bef35405340b71","location":{"path":"lib/gitlab/bitbucket_import/importer.rb","lines":{"begin":147,"end":183}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_pull_requests` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bb107ec4ecd65c38b8c57821b432e1bd","location":{"path":"lib/gitlab/bitbucket_import/importer.rb","lines":{"begin":147,"end":183}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_info` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9ccb0c18a6f37f110aadd6045933f910","location":{"path":"lib/gitlab/auth/ldap/auth_hash.rb","lines":{"begin":19,"end":36}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tls_options` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f8cf4abbbf7c74ebb10433a1b19d6e25","location":{"path":"lib/gitlab/auth/ldap/config.rb","lines":{"begin":210,"end":227}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Config` has 38 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d8bf80e499225a5522c5bf02c2b5de50","location":{"path":"lib/gitlab/auth/ldap/config.rb","lines":{"begin":5,"end":248}},"other_locations":[],"remediation_points":3000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `allowed?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b6717c2575d1b9634403637d926a53e","location":{"path":"lib/gitlab/auth/ldap/access.rb","lines":{"begin":17,"end":32}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `allowed?` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b6717c2575d1b9634403637d926a53e","location":{"path":"lib/gitlab/auth/ldap/access.rb","lines":{"begin":41,"end":63}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ldap_search` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5d595594b703b1d9783bd955a198807f","location":{"path":"lib/gitlab/auth/ldap/adapter.rb","lines":{"begin":52,"end":83}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_pair` has a Cognitive Complexity of 43 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0b175175ba20f21bb15cdf8c88896cd1","location":{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":58,"end":194}},"other_locations":[],"remediation_points":3950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize_array` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d6d33eb81193471d4408113ca113deac","location":{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":267,"end":281}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `each_pair` has 131 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"648c4eb09b87d49a1d79f5d0a77de3ea","location":{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":58,"end":194}},"other_locations":[],"remediation_points":3144000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `login` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c350e7d285ce68091161b324ed9ca293","location":{"path":"lib/gitlab/auth/ldap/authentication.rb","lines":{"begin":11,"end":20}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `save` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5b220e2f520a6fff2f90c05ba11999ba","location":{"path":"lib/gitlab/auth/o_auth/user.rb","lines":{"begin":37,"end":52}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_user` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a4874f3ff061cb0b1aeef04800ada15","location":{"path":"lib/gitlab/auth/o_auth/user.rb","lines":{"begin":60,"end":69}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_profile` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"38ded984fa19d27580db26e0540ac53f","location":{"path":"lib/gitlab/auth/o_auth/user.rb","lines":{"begin":229,"end":253}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `User` has 32 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"e416592e77f689818995fceca5f571bc","location":{"path":"lib/gitlab/auth/o_auth/user.rb","lines":{"begin":9,"end":268}},"other_locations":[],"remediation_points":2400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_user` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ba2b8382483d9be5260822909fe26d56","location":{"path":"lib/gitlab/auth/saml/user.rb","lines":{"begin":16,"end":28}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cache_method_output` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"173a260b95048ce7cfc5164ebeeaa45c","location":{"path":"lib/gitlab/repository_cache_adapter.rb","lines":{"begin":44,"end":70}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `expire_method_caches` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6938667a593219bc8d59f8046973c533","location":{"path":"lib/gitlab/repository_cache_adapter.rb","lines":{"begin":73,"end":86}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `mark` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7f23977dd6af02c109c5ecaf6ec88e79","location":{"path":"lib/gitlab/string_range_marker.rb","lines":{"begin":16,"end":45}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `position_mapping` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"75e44855c1190acfa386bf060fe0c79c","location":{"path":"lib/gitlab/string_range_marker.rb","lines":{"begin":50,"end":88}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `log_response` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7bd641d0b563cc47dc64d5b378494dd5","location":{"path":"lib/gitlab/storage_check/cli.rb","lines":{"begin":49,"end":68}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_permission!` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"596d8f0ed603043a25830a78af0b7870","location":{"path":"lib/gitlab/email/handler/reply_processing.rb","lines":{"begin":38,"end":47}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6fac16f8fc80d183ffc78d03d2d0245d","location":{"path":"lib/gitlab/email/handler/unsubscribe_handler.rb","lines":{"begin":15,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `select_body` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"74ea18f19435d9abee264cd1facce76a","location":{"path":"lib/gitlab/email/reply_parser.rb","lines":{"begin":36,"end":58}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fix_charset` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bbfbab207f44913ec99bdb0f738ef744","location":{"path":"lib/gitlab/email/reply_parser.rb","lines":{"begin":61,"end":71}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `target_url` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"587a65f54b3a9d4006c9fb8e97881295","location":{"path":"lib/gitlab/email/message/repository_push.rb","lines":{"begin":96,"end":108}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `subject` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"304068a60d47ba6b4d63d0770a3e66e0","location":{"path":"lib/gitlab/email/message/repository_push.rb","lines":{"begin":118,"end":137}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `run!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"06bf786acefc4467f3887ce0b97790b0","location":{"path":"lib/gitlab/cleanup/remote_uploads.rb","lines":{"begin":13,"end":32}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `steal` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bcfc4aae2401e33076da306af1be4a91","location":{"path":"lib/gitlab/background_migration.rb","lines":{"begin":17,"end":38}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate!` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7f4a6a0b5a98750c352c1646b1b3f685","location":{"path":"lib/gitlab/url_blocker.rb","lines":{"begin":8,"end":37}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `redis_store_options` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8e3b7658d5f2da0b6cfe80e375fba17a","location":{"path":"lib/gitlab/redis/wrapper.rb","lines":{"begin":94,"end":116}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `calculate` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"027f65d9b15a5a20ac32b7746317815a","location":{"path":"lib/gitlab/project_authorizations/with_nested_groups.rb","lines":{"begin":16,"end":57}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `allowed?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5ffe0c3e3d42265eae2251a019292113","location":{"path":"lib/gitlab/user_access.rb","lines":{"begin":30,"end":38}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `can_push_to_branch?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"303070bb63eb58bcb7a05b3b95932900","location":{"path":"lib/gitlab/user_access.rb","lines":{"begin":64,"end":75}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sanitize` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ce964ef0b84100c7c6ad7cb863485dc1","location":{"path":"lib/gitlab/ssh_public_key.rb","lines":{"begin":24,"end":38}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8bb3319e6987280746e3bc73708c56f0","location":{"path":"lib/gitlab/bare_repository_import/importer.rb","lines":{"begin":6,"end":27}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `to_kubeconfig` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b447110ec4376566b0f143ec7955f597","location":{"path":"lib/gitlab/kubernetes.rb","lines":{"begin":85,"end":115}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extract_filters` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"52f2917ff84bcee853228888bc9d4f20","location":{"path":"lib/gitlab/search/query.rb","lines":{"begin":28,"end":48}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `encode!` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5023e6a2cc1fe373219230a4ada29baa","location":{"path":"lib/gitlab/encoding_helper.rb","lines":{"begin":16,"end":36}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `encode_utf8` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c7dee9a09179a88f06945225a2a221af","location":{"path":"lib/gitlab/encoding_helper.rb","lines":{"begin":51,"end":69}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_exception_response` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0c36580ef2976c77eabf04d27eda6e0b","location":{"path":"lib/gitlab/prometheus_client.rb","lines":{"begin":78,"end":87}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `generate_full_url` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6347a85ed559890640d3146272847334","location":{"path":"lib/gitlab/url_sanitizer.rb","lines":{"begin":71,"end":78}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `convert` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"427e98fa770dd0ce1ed98ef15ec75fd5","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":136,"end":189}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `open_new_tag` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3d7e5bd9e7a6c35c9d687fc490695bed","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":232,"end":256}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_xterm_color_class` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4936b554b5287229c74930b44325b20c","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":329,"end":341}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Converter` has 69 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"61a0cb2a3f0303ed57f185452426b79a","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":31,"end":346}},"other_locations":[],"remediation_points":6100000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `convert` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dd13e7297553d67e7801e8ccc8eed932","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":136,"end":189}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `matches_pattern?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ebde1954a4f6fc95ac088dde003d1f7c","location":{"path":"lib/gitlab/ci/build/policy/refs.rb","lines":{"begin":27,"end":38}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `match_entries` has a Cognitive Complexity of 18 (exceeds 5 allowed). Consider refactoring.","fingerprint":"12277d192b4e56c2980eae35423fc226","location":{"path":"lib/gitlab/ci/build/artifacts/metadata.rb","lines":{"begin":54,"end":76}},"other_locations":[],"remediation_points":1450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read_version` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c8c5a0217b6a28198bd8cb92d0e82b00","location":{"path":"lib/gitlab/ci/build/artifacts/metadata.rb","lines":{"begin":78,"end":92}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Entry` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"a996c8f128cfc7543769dd21dd5de815","location":{"path":"lib/gitlab/ci/build/artifacts/metadata/entry.rb","lines":{"begin":15,"end":130}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `to_s` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6710c7eee920701d15361f52dbe4bd3d","location":{"path":"lib/gitlab/ci/build/artifacts/path.rb","lines":{"begin":26,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"481c8d08e5e47d4a4d4be68d0d06fe4b","location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":69,"end":93}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `chunk_slice_from_offset` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"60dbbad2af42b5e9d17acf1180b8e0d6","location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":178,"end":189}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ChunkedIO` has 26 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"3f6175d5d4d29675f68a7afb44b51fb3","location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":7,"end":236}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extract_coverage` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a126d9b81c791cb0bd1b8cd455422f3b","location":{"path":"lib/gitlab/ci/trace/stream.rb","lines":{"begin":79,"end":102}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `handle_line` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"6e13932e33f438288cd2d7a23cca7b04","location":{"path":"lib/gitlab/ci/trace/section_parser.rb","lines":{"begin":56,"end":56}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_next_marker` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7b78b80d610593251ebbeb9bb18f6246","location":{"path":"lib/gitlab/ci/trace/section_parser.rb","lines":{"begin":78,"end":93}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_string_or_regexp` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a99d0cd045678d388fd755c238f7757f","location":{"path":"lib/gitlab/ci/config/entry/legacy_validation_helpers.rb","lines":{"begin":43,"end":52}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `to_hash` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc0685829f332ba16f511159006c4cf9","location":{"path":"lib/gitlab/ci/config/entry/job.rb","lines":{"begin":136,"end":155}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `variables_expressions_syntax` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"736e4fd1de9745ba1bb433fcb5443516","location":{"path":"lib/gitlab/ci/config/entry/policy.rb","lines":{"begin":41,"end":53}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `attributes` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"69c16e006586bdd7ac4cb17f7ede9848","location":{"path":"lib/gitlab/ci/config/entry/attributable.rb","lines":{"begin":9,"end":21}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `helpers` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2d39bb63c3e71381a6cfd8d51654bee4","location":{"path":"lib/gitlab/ci/config/entry/configurable.rb","lines":{"begin":63,"end":75}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extend!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4fa7fe12ebb0fe9c7eed89e17ece5daf","location":{"path":"lib/gitlab/ci/config/extendable/entry.rb","lines":{"begin":48,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `write` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4254fbcc9b1008e1fd6a644e17f14b44","location":{"path":"lib/gitlab/ci/trace.rb","lines":{"begin":83,"end":101}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unsafe_archive!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b90c223da9c451bd90087645954fe5ee","location":{"path":"lib/gitlab/ci/trace.rb","lines":{"begin":125,"end":145}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Trace` has 26 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d9e217fe03ddebc9f270b441d4194d76","location":{"path":"lib/gitlab/ci/trace.rb","lines":{"begin":3,"end":237}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"56ff592ebb6677c3a386b683db2b6821","location":{"path":"lib/gitlab/ci/pipeline/chain/validate/config.rb","lines":{"begin":9,"end":21}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tokenize` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"60a6e3ebf3ba798ac11eca1daba671b8","location":{"path":"lib/gitlab/ci/pipeline/expression/lexer.rb","lines":{"begin":36,"end":56}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tree` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"494fb3f15182110d4c2051f9a07b9ddc","location":{"path":"lib/gitlab/ci/pipeline/expression/parser.rb","lines":{"begin":16,"end":31}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `all_cases` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f41f387d6f9c748719d97b454bbe92f","location":{"path":"lib/gitlab/ci/parsers/junit.rb","lines":{"begin":22,"end":37}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_job_dependencies!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"97a1046ed696c51fb2ce1bf34f0b7293","location":{"path":"lib/gitlab/ci/yaml_processor.rb","lines":{"begin":139,"end":151}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_on_stop_job!` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"06fcc3fed9dc213e5669186830670f59","location":{"path":"lib/gitlab/ci/yaml_processor.rb","lines":{"begin":161,"end":180}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5f09e73f4a1aa8458ce984db4f5fce37","location":{"path":"lib/gitlab/downtime_check.rb","lines":{"begin":16,"end":40}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse_entry` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f46e2835f32393dfdf07c66c0f704d0","location":{"path":"lib/gitlab/route_map.rb","lines":{"begin":30,"end":52}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e2058f84cad6a346389ce944d7c1c76e","location":{"path":"lib/gitlab/upgrader.rb","lines":{"begin":3,"end":25}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fuzzy_arel_match` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"44164fac144fbfeea45df1508dfe02db","location":{"path":"lib/gitlab/sql/pattern.rb","lines":{"begin":32,"end":51}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `lazy_page_iterator` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6ef599b3dc4f1b639d3197c028e40b71","location":{"path":"lib/gitlab/gitlab_import/client.rb","lines":{"begin":59,"end":76}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `merge_hash_tree` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e6ac0be8cece82ccc2e82ff8dd59e20e","location":{"path":"lib/gitlab/utils/merge_hash.rb","lines":{"begin":58,"end":97}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `listing` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7b99984bc28e1e4cbc4903a72d7ac663","location":{"path":"lib/gitlab/hashed_storage/rake_helper.rb","lines":{"begin":72,"end":87}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `ee_compat_check.rb` has 312 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"2103307f841e293ce32cd4161da95965","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":2,"end":440}},"other_locations":[],"remediation_points":2092800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `generate_patch` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d35820a437b86e532f50e32d127b5ab0","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":115,"end":130}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_patch` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2b66236ad0f63db5144a0820b94228b9","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":178,"end":211}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `EeCompatCheck` has 33 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"36de6fe216cdb6efd396cb08cb43cee5","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":4,"end":439}},"other_locations":[],"remediation_points":2500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `ce_branch_doesnt_apply_cleanly_and_no_ee_branch_msg` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e819c9434d1b44e764eb37a75eb1f24c","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":336,"end":412}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `scrub` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7d4324ffa269ec245847b9dd62388202","location":{"path":"lib/gitlab/sanitizers/svg.rb","lines":{"begin":12,"end":33}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `<=>` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07c5403aa1fb1c5999cd6138fc8be4c8","location":{"path":"lib/gitlab/version_info.rb","lines":{"begin":21,"end":40}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform_substitutions` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d72ee6aae32c9c1901f52bf269676a8b","location":{"path":"lib/gitlab/quick_actions/extractor.rb","lines":{"begin":115,"end":134}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `commands_regex` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1bf5c2a5cbd5a2d9489e4fefc15a284e","location":{"path":"lib/gitlab/quick_actions/extractor.rb","lines":{"begin":63,"end":113}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `to_h` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a75d7faca54c18a6777848a3850d38cc","location":{"path":"lib/gitlab/quick_actions/command_definition.rb","lines":{"begin":49,"end":66}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `wait` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bf93e6de620ccd0324ee0deae40e9661","location":{"path":"lib/gitlab/job_waiter.rb","lines":{"begin":44,"end":72}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_number_of_plurals` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ca29b997f9707951ab1c16832eac75dd","location":{"path":"lib/gitlab/i18n/po_linter.rb","lines":{"begin":86,"end":94}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fill_in_variables` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7baf89f0f9dd60c092411c5d3c85f5c4","location":{"path":"lib/gitlab/i18n/po_linter.rb","lines":{"begin":222,"end":236}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `PoLinter` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"befcb8a3c9cdc94613a938be76c2abf4","location":{"path":"lib/gitlab/i18n/po_linter.rb","lines":{"begin":3,"end":276}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `missing_members?` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a92dd0b78adf6408e1bb1362dfd52ac0","location":{"path":"lib/gitlab/background_migration/create_fork_network_memberships_range.rb","lines":{"begin":48,"end":78}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `migrate_stage_index_sql` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"17594a98741c619d81f5982d56852229","location":{"path":"lib/gitlab/background_migration/migrate_stage_index.rb","lines":{"begin":15,"end":44}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_restricted_features_todos` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2f6311086002a50283e8edb15d2db097","location":{"path":"lib/gitlab/background_migration/remove_restricted_todos.rb","lines":{"begin":80,"end":94}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `perform` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"3b81cbbe6df91a033cc8895bc63aa0e7","location":{"path":"lib/gitlab/background_migration/copy_column.rb","lines":{"begin":17,"end":17}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_pair` has a Cognitive Complexity of 43 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d8eeaa32395230adf97a240f222c4203","location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":53,"end":189}},"other_locations":[],"remediation_points":3950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize_array` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7098a1e02746bd3280879e695e913e37","location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":262,"end":276}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bb5327ee9231b73596539ad7b2c167fa","location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":300,"end":314}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `each_pair` has 131 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d03740b18e02c4d6c12a846688367eb2","location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":53,"end":189}},"other_locations":[],"remediation_points":3144000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `single_diff_rows` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c44e90db62947f73461b513d8b2bf71","location":{"path":"lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb","lines":{"begin":86,"end":129}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `single_diff_rows` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bc4293178ed4b9812cc3752d52c4c2ae","location":{"path":"lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb","lines":{"begin":86,"end":129}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `median` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8131f9850b467befa49f83c8893e074e","location":{"path":"lib/gitlab/cycle_analytics/base_stage.rb","lines":{"begin":23,"end":46}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `first_time_reference_commit` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"57863573b761b600f6f37177c4bdcc2c","location":{"path":"lib/gitlab/cycle_analytics/plan_event_fetcher.rb","lines":{"begin":40,"end":52}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `groups` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bb94f91ea8ea9bfebe88077bde7c9c70","location":{"path":"lib/gitlab/blame.rb","lines":{"begin":10,"end":34}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `issuable_meta_data` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a722790729116e77942fe04c5ea5c2fe","location":{"path":"lib/gitlab/issuable_metadata.rb","lines":{"begin":3,"end":42}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `issuable_meta_data` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"654ad0544dd20bd2a0fa2b2222724814","location":{"path":"lib/gitlab/issuable_metadata.rb","lines":{"begin":3,"end":42}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_id_by_path` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"249edd9d3a49a4634c75d7f66eddf0d3","location":{"path":"lib/gitlab/git/tree.rb","lines":{"begin":36,"end":52}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_submodules_by_name` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1779cdd505003a594a0ec1e401b49750","location":{"path":"lib/gitlab/git/gitmodules_parser.rb","lines":{"begin":45,"end":66}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `merge` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"14d8d23268624da10eee3aa315d71171","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":552,"end":552}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `find_commits_by_message` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"03db09f73721e8dc853a0e8d5c5b586a","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":948,"end":948}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `repository.rb` has 724 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"4fd0011d1b240dc0bd3776dada53d992","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":1,"end":1045}},"other_locations":[],"remediation_points":8025600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `refs_hash` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b3b8f358aa6754b1127fcaf1fc63eab","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":461,"end":473}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_count_commits_options` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d7dfc3dabbbd3403a850821e31ef9869","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":987,"end":1005}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Repository` has 117 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"7e5d4a5ee3414c57e7d1ae875324fab4","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":7,"end":1043}},"other_locations":[],"remediation_points":10900000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `resolve_lines` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a9fd4db8c8cf162f378aa963d6765b0","location":{"path":"lib/gitlab/git/conflict/file.rb","lines":{"begin":64,"end":86}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"244c77bd66325eb47245517103368219","location":{"path":"lib/gitlab/git/conflict/parser.rb","lines":{"begin":15,"end":70}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `parse` has 44 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bf9628db17f21b3c6afe90328a37f6d0","location":{"path":"lib/gitlab/git/conflict/parser.rb","lines":{"begin":15,"end":70}},"other_locations":[],"remediation_points":1056000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c678916b00a357376bbd89e7d450a28","location":{"path":"lib/gitlab/git/blob.rb","lines":{"begin":24,"end":50}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse_attributes` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4edbf1e31d321135c7006513a4035f15","location":{"path":"lib/gitlab/git/attributes_parser.rb","lines":{"begin":41,"end":77}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse_data` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"19a8edffd98f8988b199c80f6919a68a","location":{"path":"lib/gitlab/git/attributes_parser.rb","lines":{"begin":91,"end":108}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `update_page` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"cc6e53b3d35661118499e22b31c33048","location":{"path":"lib/gitlab/git/wiki.rb","lines":{"begin":41,"end":41}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `gitaly_update_page` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"29ae202c7d73dff34ed176c4c489e03c","location":{"path":"lib/gitlab/git/wiki.rb","lines":{"begin":139,"end":139}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Wiki` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"16108ddc899f6bed5535bed2d4dbd403","location":{"path":"lib/gitlab/git/wiki.rb","lines":{"begin":3,"end":166}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"992ce430fa1cfa75fffb04b791835530","location":{"path":"lib/gitlab/git/compare.rb","lines":{"begin":8,"end":22}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `commit.rb` has 261 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"b0e8a3e8bec1d1e4d61a4ca6961eee37","location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":2,"end":421}},"other_locations":[],"remediation_points":1358400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d2501d4f37da4c3f3bfc13d8ddefb8b1","location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":52,"end":72}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Commit` has 48 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"5c05f17b105a544fbb7b1e653e4e9622","location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":4,"end":419}},"other_locations":[],"remediation_points":4000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_serialized_patch` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"489c6b7588d70b32aa203aa6c41632df","location":{"path":"lib/gitlab/git/diff_collection.rb","lines":{"begin":120,"end":152}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `between` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"4c22ec0e0d894fff0c7dd7383e7e90f2","location":{"path":"lib/gitlab/git/diff.rb","lines":{"begin":31,"end":31}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_raw_blame` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c5cb9f5c58bb1d9925a29d6754967c20","location":{"path":"lib/gitlab/git/blame.rb","lines":{"begin":30,"end":56}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `in_lock` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"68b34982cd05d58d79738b15c96f20f7","location":{"path":"lib/gitlab/exclusive_lease_helpers.rb","lines":{"begin":12,"end":27}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `run_check` has a Cognitive Complexity of 18 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d86f72edc80623f2c31df045a73c54a3","location":{"path":"lib/system_check/simple_executor.rb","lines":{"begin":45,"end":82}},"other_locations":[],"remediation_points":1450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `run_check` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"32ee25468fc9ba9255e373bf8565a409","location":{"path":"lib/system_check/simple_executor.rb","lines":{"begin":45,"end":82}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dump` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"90c4b195bc0f9e6e440c936a49f66db4","location":{"path":"lib/backup/database.rb","lines":{"begin":14,"end":48}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `dump` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e224fd061ebe87897322d771a18ed4f9","location":{"path":"lib/backup/database.rb","lines":{"begin":14,"end":48}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dump` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7e1d28a077c128dd08f453c105fdd9df","location":{"path":"lib/backup/repository.rb","lines":{"begin":11,"end":39}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `restore` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c618e2d28195eb4c364400e1b377f3f","location":{"path":"lib/backup/repository.rb","lines":{"begin":74,"end":119}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `restore` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dce1e21850a52796bdc54dd0352fef39","location":{"path":"lib/backup/repository.rb","lines":{"begin":74,"end":119}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cleanup` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"df1565e2084a026c19a57df20f8c1dab","location":{"path":"lib/backup/manager.rb","lines":{"begin":59,"end":72}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_old` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"25cd2cf6fbb64afd366f3be6ab9d89fd","location":{"path":"lib/backup/manager.rb","lines":{"begin":74,"end":107}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unpack` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"67d7ec9e6873944629ee60ae0c82b98f","location":{"path":"lib/backup/manager.rb","lines":{"begin":110,"end":161}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Manager` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"c011f3bde58a57c5c1b310f99fdde1e8","location":{"path":"lib/backup/manager.rb","lines":{"begin":2,"end":247}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `unpack` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0f4697ee5870ab55f0f0482a90f0aafe","location":{"path":"lib/backup/manager.rb","lines":{"begin":110,"end":161}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `decoded` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9d399e9cd55ad1c8999c18172266e16d","location":{"path":"lib/omni_auth/strategies/jwt.rb","lines":{"begin":37,"end":51}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `class_for_class` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"44146c776b1d2705ab1e91d14f931ca6","location":{"path":"lib/declarative_policy.rb","lines":{"begin":62,"end":74}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `compute_class_for_class` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"504ef7ee6e8fa9917bb219a3ce5a6531","location":{"path":"lib/declarative_policy.rb","lines":{"begin":76,"end":93}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":23,"end":298}},"remediation_points":14090000,"other_locations":[{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":18,"end":293}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 722**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1672855f6e5f2e5e61f5f47620f50c44","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":18,"end":293}},"remediation_points":14090000,"other_locations":[{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":23,"end":298}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 722**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"08b04488cb6c1d433518d22f9fd7ac22","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":87,"end":87}},"remediation_points":1290000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":88,"end":88}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 82**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3bfe7daee55e5a8099dd7a73db3a63a6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":88,"end":88}},"remediation_points":1290000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":87,"end":87}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 82**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3bfe7daee55e5a8099dd7a73db3a63a6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":41,"end":41}},"remediation_points":1130000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":42,"end":42}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":58,"end":58}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":59,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8e7cbf1a02ae8fa5ae5397530797b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":42,"end":42}},"remediation_points":1130000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":41,"end":41}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":58,"end":58}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":59,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8e7cbf1a02ae8fa5ae5397530797b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":58,"end":58}},"remediation_points":1130000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":41,"end":41}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":42,"end":42}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":59,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8e7cbf1a02ae8fa5ae5397530797b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":59,"end":59}},"remediation_points":1130000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":41,"end":41}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":42,"end":42}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":58,"end":58}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8e7cbf1a02ae8fa5ae5397530797b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":30,"end":30}},"remediation_points":1610000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":31,"end":31}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":95,"end":95}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"22a2073b162b635650c424f759625ae1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":31,"end":31}},"remediation_points":1610000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":30,"end":30}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":95,"end":95}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"22a2073b162b635650c424f759625ae1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":95,"end":95}},"remediation_points":1610000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":30,"end":30}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":31,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"22a2073b162b635650c424f759625ae1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":65,"end":78}},"remediation_points":930000,"other_locations":[{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":107,"end":120}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4b2ec9ed4e92599e6bbba9a4a97ebeb0","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":107,"end":120}},"remediation_points":930000,"other_locations":[{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":65,"end":78}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0c7ce0e1a2c6b9abe1956a85bf8e6e52","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":40,"end":40}},"remediation_points":1350000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":72,"end":72}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":78,"end":78}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f55aade2bccc69344fa093ec61a30db","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":72,"end":72}},"remediation_points":1350000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":40,"end":40}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":78,"end":78}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f55aade2bccc69344fa093ec61a30db","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":78,"end":78}},"remediation_points":1350000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":40,"end":40}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":72,"end":72}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f55aade2bccc69344fa093ec61a30db","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":75,"end":75}},"remediation_points":1230000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":79,"end":79}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":97,"end":97}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9473b40c2cb0f7afc9308169cf2dced6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":79,"end":79}},"remediation_points":1230000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":75,"end":75}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":97,"end":97}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9473b40c2cb0f7afc9308169cf2dced6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":97,"end":97}},"remediation_points":1230000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":75,"end":75}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":79,"end":79}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9473b40c2cb0f7afc9308169cf2dced6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":35,"end":35}},"remediation_points":1090000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":43,"end":43}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":63,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6149a95705035fa65fb27a491aa16e7d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":43,"end":43}},"remediation_points":1090000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":35,"end":35}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":63,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6149a95705035fa65fb27a491aa16e7d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":63,"end":63}},"remediation_points":1090000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":35,"end":35}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":43,"end":43}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6149a95705035fa65fb27a491aa16e7d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":49,"end":49}},"remediation_points":1070000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":56,"end":56}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":83,"end":83}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"123ff3d56c152101a1c911f1a95326be","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":56,"end":56}},"remediation_points":1070000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":49,"end":49}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":83,"end":83}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"123ff3d56c152101a1c911f1a95326be","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":83,"end":83}},"remediation_points":1070000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":49,"end":49}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":56,"end":56}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"123ff3d56c152101a1c911f1a95326be","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":198,"end":227}},"remediation_points":570000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":270,"end":299}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"672d4e588f6155fa74bf7ca34fedabb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":270,"end":299}},"remediation_points":570000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":198,"end":227}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"672d4e588f6155fa74bf7ca34fedabb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":101,"end":101}},"remediation_points":1490000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":103,"end":103}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 92**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"633c7fa21c24339ba725403293231a75","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":103,"end":103}},"remediation_points":1490000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":101,"end":101}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 92**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"633c7fa21c24339ba725403293231a75","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":90,"end":90}},"remediation_points":1390000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":102,"end":102}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 87**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"925e4b3663b9cd9937ee7db7f882dfbc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":102,"end":102}},"remediation_points":1390000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":90,"end":90}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 87**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"925e4b3663b9cd9937ee7db7f882dfbc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":55,"end":55}},"remediation_points":1250000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":89,"end":89}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b931145ce6d6777daae248a043f04d6d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":89,"end":89}},"remediation_points":1250000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":55,"end":55}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b931145ce6d6777daae248a043f04d6d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/bitbucket/collection.rb","lines":{"begin":2,"end":18}},"remediation_points":410000,"other_locations":[{"path":"lib/bitbucket_server/collection.rb","lines":{"begin":4,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 38**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9daf3a24f43b64dd40b3841c26c29df5","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/bitbucket_server/collection.rb","lines":{"begin":4,"end":20}},"remediation_points":410000,"other_locations":[{"path":"lib/bitbucket/collection.rb","lines":{"begin":2,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 38**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ebc02a5c26ee206388b48b53d93e24a1","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":40,"end":89}},"remediation_points":1150000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":91,"end":140}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6778d05c8ddc9f6653765ac7ae623751","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":91,"end":140}},"remediation_points":1150000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":40,"end":89}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6778d05c8ddc9f6653765ac7ae623751","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/tag.rb","lines":{"begin":14,"end":24}},"remediation_points":650000,"other_locations":[{"path":"lib/gitlab/git/commit.rb","lines":{"begin":159,"end":169}},{"path":"lib/gitlab/git/commit.rb","lines":{"begin":179,"end":189}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d27c41bdaed8bbbc1c91788baad098b2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":159,"end":169}},"remediation_points":650000,"other_locations":[{"path":"lib/gitlab/git/commit.rb","lines":{"begin":179,"end":189}},{"path":"lib/gitlab/git/tag.rb","lines":{"begin":14,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e87685897f699d56f0c9bba98c92c792","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":179,"end":189}},"remediation_points":650000,"other_locations":[{"path":"lib/gitlab/git/commit.rb","lines":{"begin":159,"end":169}},{"path":"lib/gitlab/git/tag.rb","lines":{"begin":14,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e87685897f699d56f0c9bba98c92c792","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":46,"end":46}},"remediation_points":1150000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":80,"end":80}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"482f209ad35861d8a59e5e60275568d2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":80,"end":80}},"remediation_points":1150000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":46,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"482f209ad35861d8a59e5e60275568d2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/spec/git/location_spec.rb","lines":{"begin":3,"end":24}},"remediation_points":1130000,"other_locations":[{"path":"qa/spec/git/location_spec.rb","lines":{"begin":29,"end":50}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"dd9851dda741bf04f8f991425e6b4fa8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/spec/git/location_spec.rb","lines":{"begin":29,"end":50}},"remediation_points":1130000,"other_locations":[{"path":"qa/spec/git/location_spec.rb","lines":{"begin":3,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"dd9851dda741bf04f8f991425e6b4fa8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":54,"end":54}},"remediation_points":1110000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":66,"end":66}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"62cf424fda0f778b3dcc5914fc2096b6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":66,"end":66}},"remediation_points":1110000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":54,"end":54}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"62cf424fda0f778b3dcc5914fc2096b6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/system_hooks.rb","lines":{"begin":24,"end":31}},"remediation_points":550000,"other_locations":[{"path":"lib/api/broadcast_messages.rb","lines":{"begin":66,"end":73}},{"path":"lib/api/pipeline_schedules.rb","lines":{"begin":66,"end":73}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"dc8d377ac78b1e411b24adff2bb25ac8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/broadcast_messages.rb","lines":{"begin":66,"end":73}},"remediation_points":550000,"other_locations":[{"path":"lib/api/pipeline_schedules.rb","lines":{"begin":66,"end":73}},{"path":"lib/api/system_hooks.rb","lines":{"begin":24,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d6ed9adeccc2432c442bb7e3b2de77ca","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/pipeline_schedules.rb","lines":{"begin":66,"end":73}},"remediation_points":550000,"other_locations":[{"path":"lib/api/broadcast_messages.rb","lines":{"begin":66,"end":73}},{"path":"lib/api/system_hooks.rb","lines":{"begin":24,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e53239021b533bc772fd7c38fd9fbf32","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":76,"end":99}},"remediation_points":930000,"other_locations":[{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":69,"end":92}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"06b83fddd1854f4cbb5ee692f2b3c479","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":69,"end":92}},"remediation_points":930000,"other_locations":[{"path":"lib/gitlab/http_io.rb","lines":{"begin":76,"end":99}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fa155c048bfa229583a4b14cba8ef368","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/projects/commits_controller.rb","lines":{"begin":38,"end":49}},"remediation_points":270000,"other_locations":[{"path":"app/controllers/projects/compare_controller.rb","lines":{"begin":47,"end":58}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"25a210eb00ee75fb3a27aef364105444","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/projects/compare_controller.rb","lines":{"begin":47,"end":58}},"remediation_points":270000,"other_locations":[{"path":"app/controllers/projects/commits_controller.rb","lines":{"begin":38,"end":49}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3331c85dad0745b1770318b847052656","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":87,"end":91}},"remediation_points":270000,"other_locations":[{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":129,"end":133}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a485030c77dac9d4720e17783248f6f2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":129,"end":133}},"remediation_points":270000,"other_locations":[{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":87,"end":91}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"70c3aa57163e28d9e2b8cf58b891e37a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":153,"end":161}},"remediation_points":230000,"other_locations":[{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":209,"end":217}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c29d81ebf7e5c4008896a514ac05cd90","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":209,"end":217}},"remediation_points":230000,"other_locations":[{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":153,"end":161}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"562f28dc8f9527c3acb9451bb9905421","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"rubocop/cop/code_reuse/presenter.rb","lines":{"begin":5,"end":36}},"remediation_points":770000,"other_locations":[{"path":"rubocop/cop/code_reuse/serializer.rb","lines":{"begin":5,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ee8707c802bad394fc157d1bd785ea38","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"rubocop/cop/code_reuse/serializer.rb","lines":{"begin":5,"end":36}},"remediation_points":770000,"other_locations":[{"path":"rubocop/cop/code_reuse/presenter.rb","lines":{"begin":5,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"999d054331f9bf60e9fac34f362dfa33","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":363,"end":376}},"remediation_points":730000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":217,"end":230}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6d5fdcb9f4e92bdc8de77ce0d327e38d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":217,"end":230}},"remediation_points":730000,"other_locations":[{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":363,"end":376}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b9094b2f402bdf758fbd32cced243c48","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":143,"end":151}},"remediation_points":170000,"other_locations":[{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":199,"end":207}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9cdba14c6ea64993cb19a2d65e8cf92c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":199,"end":207}},"remediation_points":170000,"other_locations":[{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":143,"end":151}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a53ce766f9ff3869e29a216c2dd49425","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/users_controller.rb","lines":{"begin":37,"end":46}},"remediation_points":150000,"other_locations":[{"path":"app/controllers/users_controller.rb","lines":{"begin":50,"end":59}},{"path":"app/controllers/users_controller.rb","lines":{"begin":63,"end":72}},{"path":"app/controllers/users_controller.rb","lines":{"begin":76,"end":85}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7de6970da5143bb219d1d73c65245a18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/users_controller.rb","lines":{"begin":50,"end":59}},"remediation_points":150000,"other_locations":[{"path":"app/controllers/users_controller.rb","lines":{"begin":37,"end":46}},{"path":"app/controllers/users_controller.rb","lines":{"begin":63,"end":72}},{"path":"app/controllers/users_controller.rb","lines":{"begin":76,"end":85}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7de6970da5143bb219d1d73c65245a18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/users_controller.rb","lines":{"begin":63,"end":72}},"remediation_points":150000,"other_locations":[{"path":"app/controllers/users_controller.rb","lines":{"begin":37,"end":46}},{"path":"app/controllers/users_controller.rb","lines":{"begin":50,"end":59}},{"path":"app/controllers/users_controller.rb","lines":{"begin":76,"end":85}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7de6970da5143bb219d1d73c65245a18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/users_controller.rb","lines":{"begin":76,"end":85}},"remediation_points":150000,"other_locations":[{"path":"app/controllers/users_controller.rb","lines":{"begin":37,"end":46}},{"path":"app/controllers/users_controller.rb","lines":{"begin":50,"end":59}},{"path":"app/controllers/users_controller.rb","lines":{"begin":63,"end":72}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7de6970da5143bb219d1d73c65245a18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/serializers/note_entity.rb","lines":{"begin":67,"end":68}},"remediation_points":150000,"other_locations":[{"path":"app/serializers/project_note_entity.rb","lines":{"begin":20,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3177b0cd54cd809575f1d61e0a65ac25","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/serializers/project_note_entity.rb","lines":{"begin":20,"end":21}},"remediation_points":150000,"other_locations":[{"path":"app/serializers/note_entity.rb","lines":{"begin":67,"end":68}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc28db1513fbf8f22f0e63c12ccb87bb","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/project_hooks.rb","lines":{"begin":66,"end":69}},"remediation_points":150000,"other_locations":[{"path":"lib/api/project_hooks.rb","lines":{"begin":88,"end":91}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cd274943b0a85d56266ca30e0dc9e6ce","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/project_hooks.rb","lines":{"begin":88,"end":91}},"remediation_points":150000,"other_locations":[{"path":"lib/api/project_hooks.rb","lines":{"begin":66,"end":69}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cd274943b0a85d56266ca30e0dc9e6ce","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/settings.rb","lines":{"begin":76,"end":83}},"remediation_points":650000,"other_locations":[{"path":"lib/api/runner.rb","lines":{"begin":86,"end":93}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ad9527e40decd629ae978623c4e6dbb3","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/runner.rb","lines":{"begin":86,"end":93}},"remediation_points":650000,"other_locations":[{"path":"lib/api/settings.rb","lines":{"begin":76,"end":83}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5a97fc139bd16e6c2c1ebb387c258035","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/commits.rb","lines":{"begin":28,"end":36}},"remediation_points":590000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":182,"end":190}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4a0f4dd71fe015ff3e29d7ce0c5f5aa0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":182,"end":190}},"remediation_points":590000,"other_locations":[{"path":"lib/api/commits.rb","lines":{"begin":28,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"656c922dc9f4dab2a9d07fc10e49f190","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/health_checks/redis/queues_check.rb","lines":{"begin":2,"end":25}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/health_checks/redis/cache_check.rb","lines":{"begin":2,"end":25}},{"path":"lib/gitlab/health_checks/redis/shared_state_check.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a52f4db116344fb82ae762dfb265af18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/health_checks/redis/cache_check.rb","lines":{"begin":2,"end":25}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/health_checks/redis/queues_check.rb","lines":{"begin":2,"end":25}},{"path":"lib/gitlab/health_checks/redis/shared_state_check.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"31c92fe357e3684090c3440aef2d0b29","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/health_checks/redis/shared_state_check.rb","lines":{"begin":2,"end":25}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/health_checks/redis/cache_check.rb","lines":{"begin":2,"end":25}},{"path":"lib/gitlab/health_checks/redis/queues_check.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fed5fac4efc814d8bd6d479976af387a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":199,"end":206}},"remediation_points":250000,"other_locations":[{"path":"app/helpers/milestones_helper.rb","lines":{"begin":209,"end":216}},{"path":"app/helpers/milestones_helper.rb","lines":{"begin":219,"end":226}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9bdbae2675861886dd810a67f252ae76","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":209,"end":216}},"remediation_points":250000,"other_locations":[{"path":"app/helpers/milestones_helper.rb","lines":{"begin":199,"end":206}},{"path":"app/helpers/milestones_helper.rb","lines":{"begin":219,"end":226}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9bdbae2675861886dd810a67f252ae76","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":219,"end":226}},"remediation_points":250000,"other_locations":[{"path":"app/helpers/milestones_helper.rb","lines":{"begin":199,"end":206}},{"path":"app/helpers/milestones_helper.rb","lines":{"begin":209,"end":216}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9bdbae2675861886dd810a67f252ae76","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/policies/project_policy.rb","lines":{"begin":217,"end":236}},"remediation_points":510000,"other_locations":[{"path":"app/policies/project_policy.rb","lines":{"begin":330,"end":351}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 43**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6f53dee2e0dbe6e42301dfabd181bed2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/policies/project_policy.rb","lines":{"begin":330,"end":351}},"remediation_points":510000,"other_locations":[{"path":"app/policies/project_policy.rb","lines":{"begin":217,"end":236}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 43**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6f53dee2e0dbe6e42301dfabd181bed2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":5,"end":22}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":250,"end":267}},{"path":"lib/api/services.rb","lines":{"begin":322,"end":339}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":250,"end":267}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":5,"end":22}},{"path":"lib/api/services.rb","lines":{"begin":322,"end":339}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":322,"end":339}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":5,"end":22}},{"path":"lib/api/services.rb","lines":{"begin":250,"end":267}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":230,"end":247}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":302,"end":319}},{"path":"lib/api/services.rb","lines":{"begin":511,"end":528}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":302,"end":319}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":230,"end":247}},{"path":"lib/api/services.rb","lines":{"begin":511,"end":528}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":511,"end":528}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":230,"end":247}},{"path":"lib/api/services.rb","lines":{"begin":302,"end":319}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":102,"end":119}},"remediation_points":490000,"other_locations":[{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":95,"end":112}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 42**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9fcdc086aaa42b293828e5799976b36c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":95,"end":112}},"remediation_points":490000,"other_locations":[{"path":"lib/gitlab/http_io.rb","lines":{"begin":102,"end":119}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 42**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0aa489542536e966d0d4ee9d92b86de3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/policies/project_policy.rb","lines":{"begin":152,"end":170}},"remediation_points":470000,"other_locations":[{"path":"app/policies/project_policy.rb","lines":{"begin":177,"end":195}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 41**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3960d89aa99311987411af9a2ce4316a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/policies/project_policy.rb","lines":{"begin":177,"end":195}},"remediation_points":470000,"other_locations":[{"path":"app/policies/project_policy.rb","lines":{"begin":152,"end":170}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 41**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3960d89aa99311987411af9a2ce4316a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/tags.rb","lines":{"begin":95,"end":104}},"remediation_points":470000,"other_locations":[{"path":"lib/api/tags.rb","lines":{"begin":115,"end":124}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 41**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"04627bf7d430fe80b4f7d86af94fb085","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/tags.rb","lines":{"begin":115,"end":124}},"remediation_points":470000,"other_locations":[{"path":"lib/api/tags.rb","lines":{"begin":95,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 41**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"04627bf7d430fe80b4f7d86af94fb085","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/issues.rb","lines":{"begin":159,"end":171}},"remediation_points":450000,"other_locations":[{"path":"lib/api/commit_statuses.rb","lines":{"begin":16,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 40**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"72cad3326805b4987fbe6343ebb3d0fd","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/commit_statuses.rb","lines":{"begin":16,"end":23}},"remediation_points":450000,"other_locations":[{"path":"lib/api/issues.rb","lines":{"begin":159,"end":171}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 40**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"69ab1e3ccd645591451e705830d55186","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/discussions.rb","lines":{"begin":159,"end":163}},"remediation_points":170000,"other_locations":[{"path":"lib/api/discussions.rb","lines":{"begin":194,"end":198}},{"path":"lib/api/discussions.rb","lines":{"begin":209,"end":213}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"94185a2a13c6fd98f5095f170db11d97","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/discussions.rb","lines":{"begin":194,"end":198}},"remediation_points":170000,"other_locations":[{"path":"lib/api/discussions.rb","lines":{"begin":159,"end":163}},{"path":"lib/api/discussions.rb","lines":{"begin":209,"end":213}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"94185a2a13c6fd98f5095f170db11d97","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/discussions.rb","lines":{"begin":209,"end":213}},"remediation_points":170000,"other_locations":[{"path":"lib/api/discussions.rb","lines":{"begin":159,"end":163}},{"path":"lib/api/discussions.rb","lines":{"begin":194,"end":198}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"94185a2a13c6fd98f5095f170db11d97","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"rubocop/cop/migration/timestamps.rb","lines":{"begin":3,"end":22}},"remediation_points":430000,"other_locations":[{"path":"rubocop/cop/migration/remove_index.rb","lines":{"begin":3,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 39**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c7a34cb081b464faf300442c9a9c35d6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"rubocop/cop/migration/remove_index.rb","lines":{"begin":3,"end":21}},"remediation_points":430000,"other_locations":[{"path":"rubocop/cop/migration/timestamps.rb","lines":{"begin":3,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 39**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1c3d38d7fee5df958c3e432d1dd17b38","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":265,"end":272}},"remediation_points":150000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":330,"end":337}},{"path":"lib/api/users.rb","lines":{"begin":417,"end":423}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b090709c230c91d97c8733ac43a69eb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":330,"end":337}},"remediation_points":150000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":265,"end":272}},{"path":"lib/api/users.rb","lines":{"begin":417,"end":423}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b090709c230c91d97c8733ac43a69eb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":417,"end":423}},"remediation_points":150000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":265,"end":272}},{"path":"lib/api/users.rb","lines":{"begin":330,"end":337}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b090709c230c91d97c8733ac43a69eb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":172,"end":195}},"remediation_points":390000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":645,"end":668}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef781c60f85295b7fcab51d2c5fa6dbf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":645,"end":668}},"remediation_points":390000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":172,"end":195}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef781c60f85295b7fcab51d2c5fa6dbf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":469,"end":492}},"remediation_points":390000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":599,"end":622}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef781c60f85295b7fcab51d2c5fa6dbf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":599,"end":622}},"remediation_points":390000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":469,"end":492}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef781c60f85295b7fcab51d2c5fa6dbf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/variables.rb","lines":{"begin":70,"end":80}},"remediation_points":390000,"other_locations":[{"path":"lib/api/group_variables.rb","lines":{"begin":70,"end":80}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b710502de22d6ac75887584b4089f55d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/group_variables.rb","lines":{"begin":70,"end":80}},"remediation_points":390000,"other_locations":[{"path":"lib/api/variables.rb","lines":{"begin":70,"end":80}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5ed54d7e9dec3ef5264c44b21ab42f3b","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":63,"end":75}},"remediation_points":370000,"other_locations":[{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":78,"end":90}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6a6be26fc5b134ababae60c5b17185e7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":78,"end":90}},"remediation_points":370000,"other_locations":[{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":63,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6a6be26fc5b134ababae60c5b17185e7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":241,"end":252}},"remediation_points":370000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":305,"end":316}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b03582b4029701d34f64ca56f039ff3c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":305,"end":316}},"remediation_points":370000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":241,"end":252}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b03582b4029701d34f64ca56f039ff3c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":615,"end":622}},"remediation_points":370000,"other_locations":[{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":706,"end":713}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6613d45047514c566568cdc6b20a1433","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":706,"end":713}},"remediation_points":370000,"other_locations":[{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":615,"end":622}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6613d45047514c566568cdc6b20a1433","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":34,"end":47}},"remediation_points":330000,"other_locations":[{"path":"lib/banzai/filter/project_reference_filter.rb","lines":{"begin":30,"end":43}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 34**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e03c415d22f94144c8a11a51bf75449a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/project_reference_filter.rb","lines":{"begin":30,"end":43}},"remediation_points":330000,"other_locations":[{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":34,"end":47}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 34**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8a3a69b8619d932aa827e537f3525e47","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/cycle_analytics/code_stage.rb","lines":{"begin":2,"end":25}},"remediation_points":330000,"other_locations":[{"path":"lib/gitlab/cycle_analytics/review_stage.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 34**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9c27f17bb5883c88a2dcf096a1e3eb10","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/cycle_analytics/review_stage.rb","lines":{"begin":2,"end":25}},"remediation_points":330000,"other_locations":[{"path":"lib/gitlab/cycle_analytics/code_stage.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 34**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1a85a78111fd009c906af5e3e2e5c1b3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/teamcity_service.rb","lines":{"begin":52,"end":61}},"remediation_points":310000,"other_locations":[{"path":"app/models/project_services/bamboo_service.rb","lines":{"begin":49,"end":58}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 33**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"043f75067c16ff3e5d772bc2d1ae6742","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/bamboo_service.rb","lines":{"begin":49,"end":58}},"remediation_points":310000,"other_locations":[{"path":"app/models/project_services/teamcity_service.rb","lines":{"begin":52,"end":61}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 33**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3bc437123bd781d63ac2e5f1e2ee6eb3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb","lines":{"begin":5,"end":22}},"remediation_points":310000,"other_locations":[{"path":"lib/gitlab/background_migration/set_confidential_note_events_on_services.rb","lines":{"begin":5,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 33**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3ed85e9f30996e6d2ccc2e0700c1902e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/set_confidential_note_events_on_services.rb","lines":{"begin":5,"end":22}},"remediation_points":310000,"other_locations":[{"path":"lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb","lines":{"begin":5,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 33**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7ea2c375fc2078d33f6958933228ed7c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/backup/database.rb","lines":{"begin":23,"end":27}},"remediation_points":290000,"other_locations":[{"path":"lib/backup/database.rb","lines":{"begin":57,"end":61}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 32**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"eb17727d02a7c074c3154590d6ac3890","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/backup/database.rb","lines":{"begin":57,"end":61}},"remediation_points":290000,"other_locations":[{"path":"lib/backup/database.rb","lines":{"begin":23,"end":27}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 32**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"eb17727d02a7c074c3154590d6ac3890","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":130,"end":135}},"remediation_points":290000,"other_locations":[{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":138,"end":143}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 32**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c728f05ae3efb5098c029de0b3280f52","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":138,"end":143}},"remediation_points":290000,"other_locations":[{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":130,"end":135}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 32**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c728f05ae3efb5098c029de0b3280f52","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/concerns/updated_at_filterable.rb","lines":{"begin":6,"end":11}},"remediation_points":270000,"other_locations":[{"path":"app/models/concerns/created_at_filterable.rb","lines":{"begin":6,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cf343522a426e7b5b70965cafd123e2d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/concerns/created_at_filterable.rb","lines":{"begin":6,"end":11}},"remediation_points":270000,"other_locations":[{"path":"app/models/concerns/updated_at_filterable.rb","lines":{"begin":6,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"83ce9e73df4d8e3e8128ba7268e47c86","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/user.rb","lines":{"begin":1183,"end":1185}},"remediation_points":270000,"other_locations":[{"path":"app/models/user.rb","lines":{"begin":1189,"end":1191}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5c5665e3faea9b14f82119a1c414984d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/user.rb","lines":{"begin":1189,"end":1191}},"remediation_points":270000,"other_locations":[{"path":"app/models/user.rb","lines":{"begin":1183,"end":1185}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5c5665e3faea9b14f82119a1c414984d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":90,"end":96}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":105,"end":111}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fb529d437944a41ecb389c07d4f4b95c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":105,"end":111}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":90,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fb529d437944a41ecb389c07d4f4b95c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/system_check/app/tmp_writable_check.rb","lines":{"begin":2,"end":24}},"remediation_points":270000,"other_locations":[{"path":"lib/system_check/app/log_writable_check.rb","lines":{"begin":2,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5873e0223d34261535ea9380a655f530","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/system_check/app/log_writable_check.rb","lines":{"begin":2,"end":24}},"remediation_points":270000,"other_locations":[{"path":"lib/system_check/app/tmp_writable_check.rb","lines":{"begin":2,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5f6516f48de36202ed1fb08b864850e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/search_service.rb","lines":{"begin":12,"end":20}},"remediation_points":250000,"other_locations":[{"path":"app/services/search_service.rb","lines":{"begin":26,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"71865084b25bec6d7d5ead8d752fb091","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/search_service.rb","lines":{"begin":26,"end":34}},"remediation_points":250000,"other_locations":[{"path":"app/services/search_service.rb","lines":{"begin":12,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"71865084b25bec6d7d5ead8d752fb091","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":192,"end":201}},"remediation_points":250000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":205,"end":214}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"87efcabb3c142197252756a6564676fa","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":205,"end":214}},"remediation_points":250000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":192,"end":201}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"87efcabb3c142197252756a6564676fa","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb","lines":{"begin":3,"end":12}},"remediation_points":250000,"other_locations":[{"path":"qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb","lines":{"begin":5,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0022a6eabe051cdbca1e39ad3d0616a0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb","lines":{"begin":5,"end":14}},"remediation_points":250000,"other_locations":[{"path":"qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb","lines":{"begin":3,"end":12}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b769bd2ad86996248bc0cb9e9909ad48","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/admin/impersonation_tokens_controller.rb","lines":{"begin":21,"end":30}},"remediation_points":230000,"other_locations":[{"path":"app/controllers/profiles/personal_access_tokens_controller.rb","lines":{"begin":19,"end":28}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d66178654f9f4136765d0ea5d8b3e694","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/profiles/personal_access_tokens_controller.rb","lines":{"begin":19,"end":28}},"remediation_points":230000,"other_locations":[{"path":"app/controllers/admin/impersonation_tokens_controller.rb","lines":{"begin":21,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7450a990959a14aea730f2b6be2c03b2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":72,"end":85}},"remediation_points":230000,"other_locations":[{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":88,"end":101}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9983d989b521b3d7814b329e7276929a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":88,"end":101}},"remediation_points":230000,"other_locations":[{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":72,"end":85}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9983d989b521b3d7814b329e7276929a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/blob_viewer/podspec.rb","lines":{"begin":4,"end":26}},"remediation_points":230000,"other_locations":[{"path":"app/models/blob_viewer/gemspec.rb","lines":{"begin":4,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bff03fdced6cbae88ff2946f20dc10e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/blob_viewer/gemspec.rb","lines":{"begin":4,"end":26}},"remediation_points":230000,"other_locations":[{"path":"app/models/blob_viewer/podspec.rb","lines":{"begin":4,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"51fdccc6768efcdd38f19730ffe72a7c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/ci/stage.rb","lines":{"begin":96,"end":103}},"remediation_points":230000,"other_locations":[{"path":"app/models/ci/pipeline.rb","lines":{"begin":442,"end":449}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"25c1e443e5b07fc0b45f399a084137f7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":442,"end":449}},"remediation_points":230000,"other_locations":[{"path":"app/models/ci/stage.rb","lines":{"begin":96,"end":103}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"577c618e98095b86078d36b2ac019b73","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/settings.rb","lines":{"begin":59,"end":63}},"remediation_points":230000,"other_locations":[{"path":"lib/api/tags.rb","lines":{"begin":46,"end":50}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"34d52e18accbf89e1d40ad4efe71140e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/tags.rb","lines":{"begin":46,"end":50}},"remediation_points":230000,"other_locations":[{"path":"lib/api/settings.rb","lines":{"begin":59,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7249b946ecaa77a923de68a038829979","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/group_members_finder.rb","lines":{"begin":18,"end":22}},"remediation_points":210000,"other_locations":[{"path":"app/finders/group_members_finder.rb","lines":{"begin":26,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b855af99fe41d98222cc7d488606c72e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/group_members_finder.rb","lines":{"begin":26,"end":30}},"remediation_points":210000,"other_locations":[{"path":"app/finders/group_members_finder.rb","lines":{"begin":18,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b855af99fe41d98222cc7d488606c72e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/members.rb","lines":{"begin":132,"end":137}},"remediation_points":210000,"other_locations":[{"path":"lib/api/access_requests.rb","lines":{"begin":76,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"74eeab655880b068d73bfa1508e15e08","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/access_requests.rb","lines":{"begin":76,"end":81}},"remediation_points":210000,"other_locations":[{"path":"lib/api/members.rb","lines":{"begin":132,"end":137}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"89dd703acaf685f87aef279f54d3c31a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/database.rb","lines":{"begin":98,"end":109}},"remediation_points":210000,"other_locations":[{"path":"lib/gitlab/database.rb","lines":{"begin":112,"end":123}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2b8f3ea2a51c54532c43041c348ab5c3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/database.rb","lines":{"begin":112,"end":123}},"remediation_points":210000,"other_locations":[{"path":"lib/gitlab/database.rb","lines":{"begin":98,"end":109}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2b8f3ea2a51c54532c43041c348ab5c3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":44,"end":47}},"remediation_points":210000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":50,"end":53}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e02e496b4b6107134a36720421ac55a9","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":50,"end":53}},"remediation_points":210000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":44,"end":47}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e02e496b4b6107134a36720421ac55a9","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/spec/runtime/env_spec.rb","lines":{"begin":6,"end":14}},"remediation_points":210000,"other_locations":[{"path":"qa/spec/runtime/env_spec.rb","lines":{"begin":17,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d4456b6f43cef6bfbf9b6d039f749cdc","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/spec/runtime/env_spec.rb","lines":{"begin":17,"end":25}},"remediation_points":210000,"other_locations":[{"path":"qa/spec/runtime/env_spec.rb","lines":{"begin":6,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d4456b6f43cef6bfbf9b6d039f749cdc","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":252,"end":262}},"remediation_points":190000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":280,"end":290}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d67c2bf9c225577fade90666e5dfc6e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":280,"end":290}},"remediation_points":190000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":252,"end":262}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d67c2bf9c225577fade90666e5dfc6e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":396,"end":405}},"remediation_points":190000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":410,"end":419}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d67c2bf9c225577fade90666e5dfc6e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":410,"end":419}},"remediation_points":190000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":396,"end":405}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d67c2bf9c225577fade90666e5dfc6e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/migrate_legacy_artifacts.rb","lines":{"begin":24,"end":61}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/background_migration/migrate_legacy_artifacts.rb","lines":{"begin":66,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f75f6fbbc7064f4fdca08c087adf39c3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/migrate_legacy_artifacts.rb","lines":{"begin":66,"end":104}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/background_migration/migrate_legacy_artifacts.rb","lines":{"begin":24,"end":61}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f75f6fbbc7064f4fdca08c087adf39c3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":4,"end":17}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/git_access.rb","lines":{"begin":12,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef81133143dac9759b71c4ff2fbf45ed","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":12,"end":25}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":4,"end":17}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"26fd0da9b8c1ef08c93581298c4ae63a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":564,"end":575}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/git/repository.rb","lines":{"begin":579,"end":590}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7853b1dd44bd6645b11dfd03b93082d6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":579,"end":590}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/git/repository.rb","lines":{"begin":564,"end":575}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7853b1dd44bd6645b11dfd03b93082d6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":211,"end":225}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":204,"end":210}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"85278ceed072f59d95f4d59bb38349cd","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":204,"end":210}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":211,"end":225}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"84267e4195b1ef161585bb368ab56cb4","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/mailers/emails/profile.rb","lines":{"begin":13,"end":20}},"remediation_points":170000,"other_locations":[{"path":"app/mailers/emails/profile.rb","lines":{"begin":25,"end":32}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"08b3c569353eb654d48c0f3d5dfd5507","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/mailers/emails/profile.rb","lines":{"begin":25,"end":32}},"remediation_points":170000,"other_locations":[{"path":"app/mailers/emails/profile.rb","lines":{"begin":13,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"08b3c569353eb654d48c0f3d5dfd5507","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/prometheus_metric.rb","lines":{"begin":31,"end":43}},"remediation_points":170000,"other_locations":[{"path":"app/helpers/preferences_helper.rb","lines":{"begin":13,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"84368938cf4e38b28be7f2494ab7dded","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/helpers/preferences_helper.rb","lines":{"begin":13,"end":22}},"remediation_points":170000,"other_locations":[{"path":"app/models/prometheus_metric.rb","lines":{"begin":31,"end":43}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"60bfb34cb2ceca3dea92da73de2720d1","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/repository.rb","lines":{"begin":826,"end":838}},"remediation_points":170000,"other_locations":[{"path":"app/models/repository.rb","lines":{"begin":842,"end":854}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f850bbad5c6c0dbf53dcda181b0fe2ef","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/repository.rb","lines":{"begin":842,"end":854}},"remediation_points":170000,"other_locations":[{"path":"app/models/repository.rb","lines":{"begin":826,"end":838}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f850bbad5c6c0dbf53dcda181b0fe2ef","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/user.rb","lines":{"begin":1195,"end":1197}},"remediation_points":170000,"other_locations":[{"path":"app/models/user.rb","lines":{"begin":1201,"end":1203}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"79536c6f949eba9317227c4e88951f1f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/user.rb","lines":{"begin":1201,"end":1203}},"remediation_points":170000,"other_locations":[{"path":"app/models/user.rb","lines":{"begin":1195,"end":1197}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"79536c6f949eba9317227c4e88951f1f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":233,"end":242}},"remediation_points":170000,"other_locations":[{"path":"app/presenters/project_presenter.rb","lines":{"begin":263,"end":272}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c2d74780d6448543b93af9ac07770eb6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":263,"end":272}},"remediation_points":170000,"other_locations":[{"path":"app/presenters/project_presenter.rb","lines":{"begin":233,"end":242}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c2d74780d6448543b93af9ac07770eb6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/protected_branches/legacy_api_update_service.rb","lines":{"begin":18,"end":22}},"remediation_points":170000,"other_locations":[{"path":"app/services/protected_branches/legacy_api_update_service.rb","lines":{"begin":25,"end":29}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2a21c2dc3f646e0e31fff25ee48b9b44","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/protected_branches/legacy_api_update_service.rb","lines":{"begin":25,"end":29}},"remediation_points":170000,"other_locations":[{"path":"app/services/protected_branches/legacy_api_update_service.rb","lines":{"begin":18,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2a21c2dc3f646e0e31fff25ee48b9b44","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":222,"end":229}},"remediation_points":170000,"other_locations":[{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":276,"end":283}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c5f1ca2a225a96cfa3b4fc88b12b03b0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":276,"end":283}},"remediation_points":170000,"other_locations":[{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":222,"end":229}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c5f1ca2a225a96cfa3b4fc88b12b03b0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/branches.rb","lines":{"begin":71,"end":75}},"remediation_points":170000,"other_locations":[{"path":"lib/api/triggers.rb","lines":{"begin":12,"end":16}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6da3752f53f9199ec80a8ad0d2b565c1","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/triggers.rb","lines":{"begin":12,"end":16}},"remediation_points":170000,"other_locations":[{"path":"lib/api/branches.rb","lines":{"begin":71,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2a596f82671816ebb68812ebe7bf8287","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/variables.rb","lines":{"begin":31,"end":38}},"remediation_points":170000,"other_locations":[{"path":"lib/api/group_variables.rb","lines":{"begin":31,"end":38}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9c68d19fac636a233c57119e7465aae0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/group_variables.rb","lines":{"begin":31,"end":38}},"remediation_points":170000,"other_locations":[{"path":"lib/api/variables.rb","lines":{"begin":31,"end":38}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fb90f649add7621cd688c8cf825bd6ac","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/spaced_link_filter.rb","lines":{"begin":47,"end":56}},"remediation_points":170000,"other_locations":[{"path":"lib/banzai/filter/autolink_filter.rb","lines":{"begin":54,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bde5f678a848776d31606c1f7b9474fa","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/autolink_filter.rb","lines":{"begin":54,"end":63}},"remediation_points":170000,"other_locations":[{"path":"lib/banzai/filter/spaced_link_filter.rb","lines":{"begin":47,"end":56}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37daabf17a192f4d7be1f8b27c2260a6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/fill_file_store_job_artifact.rb","lines":{"begin":5,"end":15}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/background_migration/fill_file_store_lfs_object.rb","lines":{"begin":5,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8744b4f4833ad7e9ac1e28093ac9f90e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/fill_file_store_lfs_object.rb","lines":{"begin":5,"end":15}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/background_migration/fill_file_store_job_artifact.rb","lines":{"begin":5,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7504dbd080f799a40cd6f4e9f9588e2e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":419,"end":427}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/gitaly_client/blob_service.rb","lines":{"begin":93,"end":101}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"63af7969c300adefbe9421c884f45ddf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/blob_service.rb","lines":{"begin":93,"end":101}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":419,"end":427}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"037891a3606e1c8a2fffd32f88e7cede","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/metrics/samplers/unicorn_sampler.rb","lines":{"begin":23,"end":25}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/metrics/samplers/unicorn_sampler.rb","lines":{"begin":28,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8a65a519016a876bba37ba5a3c7a819f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/metrics/samplers/unicorn_sampler.rb","lines":{"begin":28,"end":30}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/metrics/samplers/unicorn_sampler.rb","lines":{"begin":23,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8a65a519016a876bba37ba5a3c7a819f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/system_check/orphans/repository_check.rb","lines":{"begin":25,"end":32}},"remediation_points":170000,"other_locations":[{"path":"lib/system_check/orphans/namespace_check.rb","lines":{"begin":23,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6d29fff75b609f46ded82f4a44c12b3e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/system_check/orphans/namespace_check.rb","lines":{"begin":23,"end":30}},"remediation_points":170000,"other_locations":[{"path":"lib/system_check/orphans/repository_check.rb","lines":{"begin":25,"end":32}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5b93c21edd90205b8238e3ade31ae23","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/concerns/created_at_filter.rb","lines":{"begin":4,"end":8}},"remediation_points":150000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":315,"end":319}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4781409b61d0fe8557e646dde62af4ee","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":315,"end":319}},"remediation_points":150000,"other_locations":[{"path":"app/finders/concerns/created_at_filter.rb","lines":{"begin":4,"end":8}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"99f151d4d58ba006fbb17af802305432","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":572,"end":575}},"remediation_points":150000,"other_locations":[{"path":"app/models/ci/runner.rb","lines":{"begin":220,"end":223}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3c0fb0db7a1fa6852c67bd50857aa448","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/ci/runner.rb","lines":{"begin":220,"end":223}},"remediation_points":150000,"other_locations":[{"path":"app/models/ci/pipeline.rb","lines":{"begin":572,"end":575}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"44cfa492a10b5b72b394efe01cd9e29d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/variables.rb","lines":{"begin":49,"end":57}},"remediation_points":150000,"other_locations":[{"path":"lib/api/group_variables.rb","lines":{"begin":49,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"320c3b54951e69257faaca3316f72058","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/group_variables.rb","lines":{"begin":49,"end":57}},"remediation_points":150000,"other_locations":[{"path":"lib/api/variables.rb","lines":{"begin":49,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fa689b5b4bab39de500b86b3d6814963","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/fix_cross_project_label_links.rb","lines":{"begin":65,"end":75}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/background_migration/fix_cross_project_label_links.rb","lines":{"begin":81,"end":91}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"26a19237576df2b97ff121c3b0cd2342","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/fix_cross_project_label_links.rb","lines":{"begin":81,"end":91}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/background_migration/fix_cross_project_label_links.rb","lines":{"begin":65,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"26a19237576df2b97ff121c3b0cd2342","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":136,"end":150}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":153,"end":167}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fc526fdd6733948898ee897d1eef487e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":153,"end":167}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":136,"end":150}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fc526fdd6733948898ee897d1eef487e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/redis/shared_state.rb","lines":{"begin":13,"end":29}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/redis/queues.rb","lines":{"begin":12,"end":28}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"077a90f16d3842a49d8095f339e2187f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/redis/queues.rb","lines":{"begin":12,"end":28}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/redis/shared_state.rb","lines":{"begin":13,"end":29}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f48114ce0a7710485f176b1b75c08e79","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/template/issue_template.rb","lines":{"begin":2,"end":14}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/template/merge_request_template.rb","lines":{"begin":2,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"685a5435dac10b7fc45977c34f30aba3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/template/merge_request_template.rb","lines":{"begin":2,"end":14}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/template/issue_template.rb","lines":{"begin":2,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc65c6ec010926daa88fbdcb539ddeea","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":164,"end":170}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":41,"end":47}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":45,"end":51}},{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":33,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d6588749bb4b05d1e583e13b9a75e39","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":33,"end":39}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":164,"end":170}},{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":41,"end":47}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":45,"end":51}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e1b42460d85ed0074c2f1fbd38bf2631","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":45,"end":51}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":164,"end":170}},{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":41,"end":47}},{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":33,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f4d765094c9218d5390de58501e17b93","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":41,"end":47}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":164,"end":170}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":45,"end":51}},{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":33,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"caa789e408f667bb83169e9a6e4f54bb","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":198,"end":200}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":142,"end":144}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":148,"end":150}},{"path":"app/assets/javascripts/ide/stores/mutations/tree.js","lines":{"begin":42,"end":44}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7b7e24c67302a04433b92238befc3cf1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/tree.js","lines":{"begin":42,"end":44}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":198,"end":200}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":142,"end":144}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":148,"end":150}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"935ca74221c521771d96150adf2278fe","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":142,"end":144}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":198,"end":200}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":148,"end":150}},{"path":"app/assets/javascripts/ide/stores/mutations/tree.js","lines":{"begin":42,"end":44}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"db86386cff8c19e24cbadfb75750f1d9","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":148,"end":150}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":198,"end":200}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":142,"end":144}},{"path":"app/assets/javascripts/ide/stores/mutations/tree.js","lines":{"begin":42,"end":44}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"db86386cff8c19e24cbadfb75750f1d9","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":7,"end":41}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":43,"end":74}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":76,"end":108}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37429fdf41dd049c7cec6890f8e50c82","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":43,"end":74}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":7,"end":41}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":76,"end":108}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37429fdf41dd049c7cec6890f8e50c82","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":76,"end":108}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":7,"end":41}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":43,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37429fdf41dd049c7cec6890f8e50c82","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":79,"end":96}},"remediation_points":4710000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":97,"end":114}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 192**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"860f8a405f1b26af1f14049631bd7aa9","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":97,"end":114}},"remediation_points":4710000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":79,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 192**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"860f8a405f1b26af1f14049631bd7aa9","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/subscription_select.js","lines":{"begin":3,"end":26}},"remediation_points":4710000,"other_locations":[{"path":"app/assets/javascripts/issue_status_select.js","lines":{"begin":3,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 192**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d440b0e1b0cca3d485fda9e0c4aaee09","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issue_status_select.js","lines":{"begin":3,"end":25}},"remediation_points":4710000,"other_locations":[{"path":"app/assets/javascripts/subscription_select.js","lines":{"begin":3,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 192**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d85cea2030ab41431ddc90787eb11389","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js","lines":{"begin":5,"end":32}},"remediation_points":4620000,"other_locations":[{"path":"app/assets/javascripts/blob/template_selectors/dockerfile_selector.js","lines":{"begin":5,"end":32}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 189**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4a9d91d82457a2a845b5366d5f0ce5b4","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/blob/template_selectors/dockerfile_selector.js","lines":{"begin":5,"end":32}},"remediation_points":4620000,"other_locations":[{"path":"app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js","lines":{"begin":5,"end":32}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 189**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9a6d28971082883e9748dd3bac2a6695","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":316,"end":324}},"remediation_points":1470000,"other_locations":[{"path":"app/assets/javascripts/commit/pipelines/pipelines_bundle.js","lines":{"begin":34,"end":42}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 84**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5595f472c12029fb0da2e625d62d94d6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/commit/pipelines/pipelines_bundle.js","lines":{"begin":34,"end":42}},"remediation_points":1470000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":316,"end":324}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 84**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"30d9e8739fb74a1a2a7618b85b5e6f7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1204,"end":1222}},"remediation_points":3990000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1222,"end":1240}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 168**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4725f16bae793b141b64c0921a6539b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1222,"end":1240}},"remediation_points":3990000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1204,"end":1222}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 168**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4725f16bae793b141b64c0921a6539b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":13,"end":37}},"remediation_points":2280000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":49,"end":70}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":82,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 111**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc78d02c62558d8dc38d2567eaf302a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":49,"end":70}},"remediation_points":2280000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":13,"end":37}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":82,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 111**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc78d02c62558d8dc38d2567eaf302a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":82,"end":104}},"remediation_points":2280000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":13,"end":37}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":49,"end":70}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 111**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc78d02c62558d8dc38d2567eaf302a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/group_avatar.js","lines":{"begin":3,"end":14}},"remediation_points":3810000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/shared/project_avatar.js","lines":{"begin":3,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 162**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"90682f4af3abe93c6405cd6eb215e9c6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/shared/project_avatar.js","lines":{"begin":3,"end":15}},"remediation_points":3810000,"other_locations":[{"path":"app/assets/javascripts/group_avatar.js","lines":{"begin":3,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 162**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"67e63301cd433971695d7cc16ff752b2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":14,"end":23}},"remediation_points":1350000,"other_locations":[{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":66,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"272dee8d71a4ab9c8d815e23c04c7742","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":66,"end":75}},"remediation_points":1350000,"other_locations":[{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":14,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"272dee8d71a4ab9c8d815e23c04c7742","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":55,"end":59}},"remediation_points":1050000,"other_locations":[{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":70,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 70**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f1257b0729020dbf8c1e761c6b423db","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":70,"end":74}},"remediation_points":1050000,"other_locations":[{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":55,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 70**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"39bdab25a792c5662feb840979099a97","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/commons/polyfills/element.js","lines":{"begin":14,"end":19}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":278,"end":283}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b95252006271b6a23d2d59296a0e5778","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":278,"end":283}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/commons/polyfills/element.js","lines":{"begin":14,"end":19}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"67ee0da8e9d2d3b183449d9e8d462f44","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":37,"end":57}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":99,"end":135}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6e9b85292de8771601751d3ed0d9dc18","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":99,"end":135}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":37,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6e9b85292de8771601751d3ed0d9dc18","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":105,"end":115}},"remediation_points":2700000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":117,"end":127}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 125**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0fee1b42ff17f6df3a6f14e35f13cf18","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":117,"end":127}},"remediation_points":2700000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":105,"end":115}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 125**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0fee1b42ff17f6df3a6f14e35f13cf18","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":183,"end":186}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":188,"end":191}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":193,"end":196}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":198,"end":201}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8fc9b42e2195587e4e8d777e75ad89d8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":188,"end":191}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":183,"end":186}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":193,"end":196}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":198,"end":201}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8fc9b42e2195587e4e8d777e75ad89d8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":193,"end":196}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":183,"end":186}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":188,"end":191}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":198,"end":201}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8fc9b42e2195587e4e8d777e75ad89d8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":198,"end":201}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":183,"end":186}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":188,"end":191}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":193,"end":196}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8fc9b42e2195587e4e8d777e75ad89d8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/snippets/show/index.js","lines":{"begin":7,"end":13}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/snippets/show/index.js","lines":{"begin":7,"end":13}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"15c8237c5ef3dd70061ce1d44bd5d199","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/snippets/show/index.js","lines":{"begin":7,"end":13}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/pages/snippets/show/index.js","lines":{"begin":7,"end":13}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8ae282c95da75609356442ee0da6e6fc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/new_group_child.js","lines":{"begin":28,"end":36}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":253,"end":262}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"611a498e9c5e5d8f256a05631ef5ac51","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":253,"end":262}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/groups/new_group_child.js","lines":{"begin":28,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37087f35974839e5e79033d561c840a6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issuable_bulk_update_actions.js","lines":{"begin":110,"end":117}},"remediation_points":2310000,"other_locations":[{"path":"app/assets/javascripts/issuable_bulk_update_actions.js","lines":{"begin":120,"end":126}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 112**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0e6b703c746c94d22e3a02f1993665ce","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issuable_bulk_update_actions.js","lines":{"begin":120,"end":126}},"remediation_points":2310000,"other_locations":[{"path":"app/assets/javascripts/issuable_bulk_update_actions.js","lines":{"begin":110,"end":117}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 112**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0e6b703c746c94d22e3a02f1993665ce","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"scripts/frontend/prettier.js","lines":{"begin":64,"end":64}},"remediation_points":600000,"other_locations":[{"path":"scripts/frontend/prettier.js","lines":{"begin":71,"end":71}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef21ab3df8b01ca7e22d4d8804157d7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"scripts/frontend/prettier.js","lines":{"begin":71,"end":71}},"remediation_points":600000,"other_locations":[{"path":"scripts/frontend/prettier.js","lines":{"begin":64,"end":64}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef21ab3df8b01ca7e22d4d8804157d7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":208,"end":211}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":213,"end":216}},{"path":"app/assets/javascripts/api.js","lines":{"begin":218,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8774a69c35e7469e5831f013a372ab41","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":213,"end":216}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":208,"end":211}},{"path":"app/assets/javascripts/api.js","lines":{"begin":218,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8774a69c35e7469e5831f013a372ab41","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":218,"end":221}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":208,"end":211}},{"path":"app/assets/javascripts/api.js","lines":{"begin":213,"end":216}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8774a69c35e7469e5831f013a372ab41","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":58,"end":63}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/boards/components/board_sidebar.js","lines":{"begin":95,"end":100}},{"path":"app/assets/javascripts/boards/index.js","lines":{"begin":84,"end":89}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6b694c569da1307b1f359dff7c2ba96a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/components/board_sidebar.js","lines":{"begin":95,"end":100}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/boards/index.js","lines":{"begin":84,"end":89}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":58,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3334e14e9af8a36a250b2eeea4a85929","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/index.js","lines":{"begin":84,"end":89}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/boards/components/board_sidebar.js","lines":{"begin":95,"end":100}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":58,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7f09eaadb7107879ce06fbd895c6dfd2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issue.js","lines":{"begin":133,"end":142}},"remediation_points":2130000,"other_locations":[{"path":"app/assets/javascripts/issue.js","lines":{"begin":144,"end":153}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 106**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"316f57fc6559be3f06b67e902431d4f3","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issue.js","lines":{"begin":144,"end":153}},"remediation_points":2130000,"other_locations":[{"path":"app/assets/javascripts/issue.js","lines":{"begin":133,"end":142}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 106**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"316f57fc6559be3f06b67e902431d4f3","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js","lines":{"begin":10,"end":15}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js","lines":{"begin":19,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7d9d7ed069d90637ae3219e74dfc3a6a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js","lines":{"begin":19,"end":24}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js","lines":{"begin":10,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"de8d186c2d499f4864bd5be7db1ddad4","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":44,"end":53}},"remediation_points":2010000,"other_locations":[{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":59,"end":68}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 102**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c1e5733385780c688efc3db1ad11f746","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":59,"end":68}},"remediation_points":2010000,"other_locations":[{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":44,"end":53}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 102**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8de13799b826a6e485d4019569e6c278","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":112,"end":122}},"remediation_points":1980000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":124,"end":134}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 101**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"762a1afa922792a6981db079fca70fa7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":124,"end":134}},"remediation_points":1980000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":112,"end":122}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 101**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"762a1afa922792a6981db079fca70fa7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects_list.js","lines":{"begin":7,"end":18}},"remediation_points":1980000,"other_locations":[{"path":"app/assets/javascripts/groups_list.js","lines":{"begin":7,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 101**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"23431c5334e56a528fc4eb179e3607ec","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups_list.js","lines":{"begin":7,"end":18}},"remediation_points":1980000,"other_locations":[{"path":"app/assets/javascripts/projects_list.js","lines":{"begin":7,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 101**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1c7090c499a2a96d464475ad7f818f11","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":605,"end":609}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/users_select.js","lines":{"begin":610,"end":614}},{"path":"app/assets/javascripts/users_select.js","lines":{"begin":615,"end":619}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"448aadd60c1a014f7e3ec3eed8404a65","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":610,"end":614}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/users_select.js","lines":{"begin":605,"end":609}},{"path":"app/assets/javascripts/users_select.js","lines":{"begin":615,"end":619}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"448aadd60c1a014f7e3ec3eed8404a65","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":615,"end":619}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/users_select.js","lines":{"begin":605,"end":609}},{"path":"app/assets/javascripts/users_select.js","lines":{"begin":610,"end":614}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"448aadd60c1a014f7e3ec3eed8404a65","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/plugins/filter.js","lines":{"begin":84,"end":90}},"remediation_points":1890000,"other_locations":[{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":102,"end":108}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"38d9afeb8b320571be87977725fdcafe","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":102,"end":108}},"remediation_points":1890000,"other_locations":[{"path":"app/assets/javascripts/droplab/plugins/filter.js","lines":{"begin":84,"end":90}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8bc2a8aade90176f81a992ae00dd6e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js","lines":{"begin":1,"end":21}},"remediation_points":1860000,"other_locations":[{"path":"app/assets/javascripts/badges/store/mutation_types.js","lines":{"begin":1,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 97**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7a9bae0109305e0b1d43de56513651bb","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/mutation_types.js","lines":{"begin":1,"end":21}},"remediation_points":1860000,"other_locations":[{"path":"app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js","lines":{"begin":1,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 97**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"500922ffefee136d140bb5e95d7a3653","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/protected_tags/protected_tag_edit_list.js","lines":{"begin":6,"end":19}},"remediation_points":1830000,"other_locations":[{"path":"app/assets/javascripts/protected_branches/protected_branch_edit_list.js","lines":{"begin":6,"end":19}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 96**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"746b71495fe41db39299682caa93445c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/protected_branches/protected_branch_edit_list.js","lines":{"begin":6,"end":19}},"remediation_points":1830000,"other_locations":[{"path":"app/assets/javascripts/protected_tags/protected_tag_edit_list.js","lines":{"begin":6,"end":19}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 96**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5f6ce24443dd76a3e1d0f7521a36be5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/settings_panels.js","lines":{"begin":7,"end":10}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/settings_panels.js","lines":{"begin":17,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e788e65abb5a4f6fb80e4e6628fb6d10","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/settings_panels.js","lines":{"begin":17,"end":20}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/settings_panels.js","lines":{"begin":7,"end":10}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e788e65abb5a4f6fb80e4e6628fb6d10","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":40,"end":40}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":47,"end":47}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3f756c21d7aebceedb361afbef2d4a17","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":47,"end":47}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":40,"end":40}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3f756c21d7aebceedb361afbef2d4a17","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":32,"end":34}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":243,"end":245}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"50b54b3d38d73c256194eed3de425376","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":243,"end":245}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":32,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"50b54b3d38d73c256194eed3de425376","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request.js","lines":{"begin":96,"end":103}},"remediation_points":1680000,"other_locations":[{"path":"app/assets/javascripts/merge_request.js","lines":{"begin":105,"end":112}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 91**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3d4aa6b1ed1ed8586dbb1479749be92","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request.js","lines":{"begin":105,"end":112}},"remediation_points":1680000,"other_locations":[{"path":"app/assets/javascripts/merge_request.js","lines":{"begin":96,"end":103}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 91**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3d4aa6b1ed1ed8586dbb1479749be92","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/sidebar/mount_sidebar.js","lines":{"begin":75,"end":92}},"remediation_points":1680000,"other_locations":[{"path":"app/assets/javascripts/sidebar/mount_sidebar.js","lines":{"begin":94,"end":111}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 91**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"961bf4d63d82bd0547b50951a7db6c69","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/sidebar/mount_sidebar.js","lines":{"begin":94,"end":111}},"remediation_points":1680000,"other_locations":[{"path":"app/assets/javascripts/sidebar/mount_sidebar.js","lines":{"begin":75,"end":92}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 91**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"961bf4d63d82bd0547b50951a7db6c69","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":514,"end":533}},"remediation_points":1650000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":522,"end":533}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 90**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e69530c757a3f18db186fc48d529606d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":522,"end":533}},"remediation_points":1650000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":514,"end":533}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 90**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e69530c757a3f18db186fc48d529606d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/mutations.js","lines":{"begin":85,"end":99}},"remediation_points":1620000,"other_locations":[{"path":"app/assets/javascripts/badges/store/mutations.js","lines":{"begin":100,"end":114}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 89**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0407713555d3e0c75813d9f23089bcfd","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/mutations.js","lines":{"begin":100,"end":114}},"remediation_points":1620000,"other_locations":[{"path":"app/assets/javascripts/badges/store/mutations.js","lines":{"begin":85,"end":99}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 89**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0407713555d3e0c75813d9f23089bcfd","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/monitoring/services/monitoring_service.js","lines":{"begin":34,"end":41}},"remediation_points":1620000,"other_locations":[{"path":"app/assets/javascripts/monitoring/services/monitoring_service.js","lines":{"begin":48,"end":55}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 89**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d4a3e547360e709e6f65d844f47d2578","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/monitoring/services/monitoring_service.js","lines":{"begin":48,"end":55}},"remediation_points":1620000,"other_locations":[{"path":"app/assets/javascripts/monitoring/services/monitoring_service.js","lines":{"begin":34,"end":41}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 89**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d4a3e547360e709e6f65d844f47d2578","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":2,"end":14}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":27,"end":39}},{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":85,"end":97}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ac500b1ccac9075c6ed2fffbc9b3961c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":27,"end":39}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":2,"end":14}},{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":85,"end":97}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ac500b1ccac9075c6ed2fffbc9b3961c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":85,"end":97}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":2,"end":14}},{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":27,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ac500b1ccac9075c6ed2fffbc9b3961c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":46,"end":57}},"remediation_points":1530000,"other_locations":[{"path":"app/assets/javascripts/pipelines/stores/pipelines_store.js","lines":{"begin":20,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 86**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ac70092b1281d03613501e7bff10586e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/stores/pipelines_store.js","lines":{"begin":20,"end":31}},"remediation_points":1530000,"other_locations":[{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":46,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 86**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d99660a2eda83bcadef8b0ab894abe34","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":28,"end":39}},"remediation_points":1500000,"other_locations":[{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":142,"end":153}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d5915d3ead3c5d344920b58d2d29398","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":142,"end":153}},"remediation_points":1500000,"other_locations":[{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":28,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d5915d3ead3c5d344920b58d2d29398","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":58,"end":62}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":78,"end":82}},{"path":"app/assets/javascripts/sidebar/stores/sidebar_store.js","lines":{"begin":77,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6bca973f7e7c682a30bc1ec4bf9bd764","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":78,"end":82}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":58,"end":62}},{"path":"app/assets/javascripts/sidebar/stores/sidebar_store.js","lines":{"begin":77,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6bca973f7e7c682a30bc1ec4bf9bd764","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/sidebar/stores/sidebar_store.js","lines":{"begin":77,"end":81}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":58,"end":62}},{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":78,"end":82}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0eb61104cf3e71a06f399141f9b4fa0d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":50,"end":53}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":139,"end":142}},{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":162,"end":165}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ab644c43df256102910e1758c76cc0b5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":139,"end":142}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":50,"end":53}},{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":162,"end":165}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ab644c43df256102910e1758c76cc0b5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":162,"end":165}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":50,"end":53}},{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":139,"end":142}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ab644c43df256102910e1758c76cc0b5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":22,"end":35}},"remediation_points":1410000,"other_locations":[{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":83,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 82**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"25ff43fa592634a300a75d479e7131a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":83,"end":96}},"remediation_points":1410000,"other_locations":[{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":22,"end":35}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 82**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"25ff43fa592634a300a75d479e7131a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":85,"end":91}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":93,"end":99}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8761bbf2f2e7182d15c059e6eda61093","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":93,"end":99}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":85,"end":91}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8761bbf2f2e7182d15c059e6eda61093","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/blob/show/index.js","lines":{"begin":13,"end":29}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/tree/show/index.js","lines":{"begin":24,"end":40}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"10512ee694664bf38a3b98f1983d771f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/tree/show/index.js","lines":{"begin":24,"end":40}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/blob/show/index.js","lines":{"begin":13,"end":29}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cacb4070042747bc39f22b6ed696d288","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":45,"end":51}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":59,"end":65}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14f5cdeb3ea2bd6b45456111559d4372","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":59,"end":65}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":45,"end":51}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14f5cdeb3ea2bd6b45456111559d4372","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":150,"end":154}},"remediation_points":1350000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":156,"end":160}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c9b1f08a21d96ed70ae569a5f20147b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":156,"end":160}},"remediation_points":1350000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":150,"end":154}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c9b1f08a21d96ed70ae569a5f20147b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":377,"end":382}},"remediation_points":1320000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":382,"end":387}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f7da63bef0b10cc1b2852a187aa6da7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":382,"end":387}},"remediation_points":1320000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":377,"end":382}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f7da63bef0b10cc1b2852a187aa6da7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":14,"end":14}},"remediation_points":1290000,"other_locations":[{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":8,"end":19}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 78**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c151b6404d32d859c90476abc53a459a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":8,"end":19}},"remediation_points":1290000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":14,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 78**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2e1258cb46a94d486ff0c217b7ee506b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/tree.js","lines":{"begin":78,"end":84}},"remediation_points":1260000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":81,"end":87}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 77**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5801c2247d4ab393293432462fbf849b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":81,"end":87}},"remediation_points":1260000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/tree.js","lines":{"begin":78,"end":84}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 77**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0b9486994211d1c4a0385b3c9ac7339f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":4,"end":14}},"remediation_points":1230000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_button.js","lines":{"begin":4,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ce9535c497280ac0f885dd216908adc4","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_button.js","lines":{"begin":4,"end":14}},"remediation_points":1230000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":4,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5ee1687cc7901d1085a41d0b7d5b3730","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":84,"end":96}},"remediation_points":1230000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/file_templates/actions.js","lines":{"begin":53,"end":65}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0d9f79efa645b760bbea0b48922a8231","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/file_templates/actions.js","lines":{"begin":53,"end":65}},"remediation_points":1230000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":84,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9033fca3ba0465c4cc79203ac97a67c6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":197,"end":202}},"remediation_points":1200000,"other_locations":[{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":204,"end":209}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"46e2ef5ccb0e3676b035d790303474f6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":204,"end":209}},"remediation_points":1200000,"other_locations":[{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":197,"end":202}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"46e2ef5ccb0e3676b035d790303474f6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/reports/store/actions.js","lines":{"begin":61,"end":67}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":73,"end":79}},{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":56,"end":62}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f6738e3ca61bc9141c14aca35a4ece63","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":56,"end":62}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":73,"end":79}},{"path":"app/assets/javascripts/reports/store/actions.js","lines":{"begin":61,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b0ce2389e2ef7f95bdab6dd30392133a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":73,"end":79}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":56,"end":62}},{"path":"app/assets/javascripts/reports/store/actions.js","lines":{"begin":61,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e577605dea8912ffd5b82ee2e188ec76","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":28,"end":40}},"remediation_points":1170000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":123,"end":133}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1825ad84d47a08c46a9dd2a4386db970","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":123,"end":133}},"remediation_points":1170000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":28,"end":40}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1825ad84d47a08c46a9dd2a4386db970","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":61,"end":67}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":68,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"40924a71c4587ae28cb080829b65871c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":68,"end":74}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":61,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"40924a71c4587ae28cb080829b65871c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":7,"end":9}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":13,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ca3e7297c2c42380a6b34e26f5faa025","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":13,"end":15}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":7,"end":9}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ca3e7297c2c42380a6b34e26f5faa025","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/merge_request.js","lines":{"begin":22,"end":26}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/branch.js","lines":{"begin":26,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5b01e7cc1ccdb5b45e44117d01906827","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/branch.js","lines":{"begin":26,"end":30}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/merge_request.js","lines":{"begin":22,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e8265ff476562ddb4bd141410d88ad2f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":12,"end":18}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":9,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"33bcc15ccc0be1f4e4787d8882ad36e2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":9,"end":15}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":12,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"931ead2443e997ee26d698667339fb06","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":222,"end":229}},"remediation_points":1080000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":237,"end":244}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"36bae5f12de5a60023e0fd8fc19f1842","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":237,"end":244}},"remediation_points":1080000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":222,"end":229}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"36bae5f12de5a60023e0fd8fc19f1842","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":71,"end":77}},"remediation_points":1080000,"other_locations":[{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":81,"end":87}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"47474e924fc42a7e0f1319c7a8ae13d3","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":81,"end":87}},"remediation_points":1080000,"other_locations":[{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":71,"end":77}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"aade96d11a83331446e70683959d49ec","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":381,"end":385}},"remediation_points":1050000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":418,"end":422}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 70**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fdc3dab0afea85797bddfc16706a3869","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":418,"end":422}},"remediation_points":1050000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":381,"end":385}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 70**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fdc3dab0afea85797bddfc16706a3869","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":80,"end":83}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":84,"end":87}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"85e1a20133858d47a7f4a64e49080aef","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":84,"end":87}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":80,"end":83}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"85e1a20133858d47a7f4a64e49080aef","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/frequent_items/constants.js","lines":{"begin":18,"end":28}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/frequent_items/constants.js","lines":{"begin":29,"end":37}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c7c7576a99db4f904055a712b428b46","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/frequent_items/constants.js","lines":{"begin":29,"end":37}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/frequent_items/constants.js","lines":{"begin":18,"end":28}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c7c7576a99db4f904055a712b428b46","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/index.js","lines":{"begin":79,"end":89}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/environments/folder/environments_folder_bundle.js","lines":{"begin":24,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b80cd93182a7ed27e84c9e59840d8f01","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/environments/folder/environments_folder_bundle.js","lines":{"begin":24,"end":34}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/groups/index.js","lines":{"begin":79,"end":89}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2beaf3990d7c635f7339eb68a668c3a8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/file_templates/actions.js","lines":{"begin":9,"end":20}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":202,"end":213}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9441eb5a1202300a1a6bfdd0f0275a31","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":202,"end":213}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/file_templates/actions.js","lines":{"begin":9,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f657ac30a9ca0e466c504f33ae3363a1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":259,"end":264}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":264,"end":269}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"40a4102731fea710642a925d4403b398","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":264,"end":269}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":259,"end":264}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"40a4102731fea710642a925d4403b398","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diff_notes/services/resolve.js","lines":{"begin":56,"end":68}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/diff_notes/services/resolve.js","lines":{"begin":70,"end":82}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5c966c3b72bce8578a3e8c47c412d39","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diff_notes/services/resolve.js","lines":{"begin":70,"end":82}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/diff_notes/services/resolve.js","lines":{"begin":56,"end":68}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5c966c3b72bce8578a3e8c47c412d39","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":40,"end":45}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":46,"end":51}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5aa5f88f636432f3381edff9ce5eb33c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":46,"end":51}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":40,"end":45}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5aa5f88f636432f3381edff9ce5eb33c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":207,"end":220}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":221,"end":234}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c3008559d14ec2056a4514ffdfd5d04b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":221,"end":234}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":207,"end":220}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c3008559d14ec2056a4514ffdfd5d04b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/utils.js","lines":{"begin":16,"end":23}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1506,"end":1515}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37585e488e2d24ae8b8b99880ff76099","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1506,"end":1515}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/utils.js","lines":{"begin":16,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ea9f3aa39779bc4f5ead96ecdcd5e87c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1526,"end":1566}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":103,"end":219}},{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":3,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5306c1b3e6d8ac0ac9bed008b527b8a7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":3,"end":23}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":103,"end":219}},{"path":"app/assets/javascripts/notes.js","lines":{"begin":1526,"end":1566}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4dfd0fcaf710a6337c5924d7c4fa91ea","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":103,"end":219}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1526,"end":1566}},{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":3,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"71749d080c69b950423ce3e73b3671d6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":44,"end":48}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js","lines":{"begin":44,"end":48}},{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":39,"end":43}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"80bd962a66c5ba8f3bb261204e0e8404","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js","lines":{"begin":44,"end":48}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":39,"end":43}},{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":44,"end":48}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4dccfbb8118720c67894021eefaeb04f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":39,"end":43}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js","lines":{"begin":44,"end":48}},{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":44,"end":48}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"594be23bc97d64731abf2df62520ccd3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/clusters/services/clusters_service.js","lines":{"begin":6,"end":12}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":96,"end":102}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5b85961f2c362ae642ccf393b8352fcb","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":96,"end":102}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/clusters/services/clusters_service.js","lines":{"begin":6,"end":12}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cad81c7468728ba369f9f23c8a1271bc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":91,"end":93}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":172,"end":174}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"beda25571cf3ea070a68d9cdfae91c8d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":172,"end":174}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":91,"end":93}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"beda25571cf3ea070a68d9cdfae91c8d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js","lines":{"begin":48,"end":53}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js","lines":{"begin":63,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"575f1444e3c2fec5a4f47d049cbd87c0","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js","lines":{"begin":63,"end":67}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js","lines":{"begin":48,"end":53}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5e4303b5381a6be7bcaade373d766f34","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":35,"end":42}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":50,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1d8b266328571b1177e922384e474eb5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":50,"end":57}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":35,"end":42}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"334a900ffecf98fb6a951456c58236a1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":181,"end":183}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":290,"end":292}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"32fbc7cab878611e7f156798ae3d2bca","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":290,"end":292}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":181,"end":183}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"32fbc7cab878611e7f156798ae3d2bca","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":50,"end":56}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":57,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c14eefb5c2db69bb969956efd4384de","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":57,"end":63}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":50,"end":56}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c14eefb5c2db69bb969956efd4384de","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":277,"end":286}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":287,"end":296}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"923d82c4da880c23e929c2402aedd37e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":287,"end":296}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":277,"end":286}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"923d82c4da880c23e929c2402aedd37e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/blob/blob_fork_suggestion.js","lines":{"begin":31,"end":34}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/blob/blob_fork_suggestion.js","lines":{"begin":56,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"938856cdef6ef5aa2933db645ea96362","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/blob/blob_fork_suggestion.js","lines":{"begin":56,"end":59}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/blob/blob_fork_suggestion.js","lines":{"begin":31,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"938856cdef6ef5aa2933db645ea96362","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/index.js","lines":{"begin":30,"end":39}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/mr_notes/index.js","lines":{"begin":66,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b35237501aeed21f2d38aef31627b97e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/mr_notes/index.js","lines":{"begin":66,"end":75}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/diffs/index.js","lines":{"begin":30,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"17da47f3678555baddd89ae6622e441f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":18,"end":31}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":33,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a93d28498cd20f4d35a3f8dbbc0a7a35","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":33,"end":46}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":18,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a93d28498cd20f4d35a3f8dbbc0a7a35","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":237,"end":241}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":340,"end":344}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"785078efeabfa8c98aa5bf0804b2619d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":340,"end":344}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":237,"end":241}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"785078efeabfa8c98aa5bf0804b2619d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":112,"end":122}},"remediation_points":840000,"other_locations":[{"path":"app/assets/javascripts/ide/lib/diff/controller.js","lines":{"begin":6,"end":16}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 63**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"09d7d5a8cacd9d95c32bc8b4e6b53930","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/lib/diff/controller.js","lines":{"begin":6,"end":16}},"remediation_points":840000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":112,"end":122}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 63**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f94a7934b1c80152deb5b61a0c5db3d4","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":118,"end":124}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":126,"end":132}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"104c7ee7f9a45a32c42bacfaa5a5f1bc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":126,"end":132}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":118,"end":124}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"104c7ee7f9a45a32c42bacfaa5a5f1bc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":37,"end":46}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":48,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1f17e867f83499d67ff9ed50b8097db5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":48,"end":57}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":37,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1f17e867f83499d67ff9ed50b8097db5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/pipeline_details_bundle.js","lines":{"begin":77,"end":84}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/jobs/job_details_bundle.js","lines":{"begin":27,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c4a434df3787893bc1f6d897464a8227","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/job_details_bundle.js","lines":{"begin":27,"end":34}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/pipelines/pipeline_details_bundle.js","lines":{"begin":77,"end":84}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5c1f719b9039c86ffdc704317d783449","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":20,"end":24}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":17,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ab079e1e6bc28e1fc184b520beb34531","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":17,"end":21}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":20,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e7369e08c5971c04639cff93271173af","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":56,"end":59}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":61,"end":64}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1ad13a005d315ccf15cfe0584579158f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":61,"end":64}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":56,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1ad13a005d315ccf15cfe0584579158f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":107,"end":111}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":113,"end":117}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c9228663eecdf0fc1a8dbd811c863e38","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":113,"end":117}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":107,"end":111}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c9228663eecdf0fc1a8dbd811c863e38","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_button.js","lines":{"begin":33,"end":36}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/landing.js","lines":{"begin":18,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0cdcc38394199ec6567937ce812cb5a5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/landing.js","lines":{"begin":18,"end":21}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_button.js","lines":{"begin":33,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6b06657bea1bfe588af0efba4a2bf57e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":26,"end":31}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":34,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1f5e585cdfd268a3748dbdd584f69df8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":34,"end":39}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":26,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"67d77d7b888d52cac1d666c6a95ba502","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/tree.js","lines":{"begin":38,"end":46}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/tree.js","lines":{"begin":49,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"16eb34236a934fb101dbe1e8b6ab4af1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/tree.js","lines":{"begin":49,"end":57}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/tree.js","lines":{"begin":38,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"16eb34236a934fb101dbe1e8b6ab4af1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":191,"end":203}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":236,"end":248}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"13f117af1725bb929e7aa63491a25429","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":236,"end":248}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":191,"end":203}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"13f117af1725bb929e7aa63491a25429","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/groups_filterable_list.js","lines":{"begin":7,"end":16}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":238,"end":247}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2a553868b7fea8b08596f9a95a22d484","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":238,"end":247}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/groups/groups_filterable_list.js","lines":{"begin":7,"end":16}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"12bd1fc0fd53a6c025f6ed5d690be133","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js","lines":{"begin":45,"end":49}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js","lines":{"begin":51,"end":55}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e7e7f0899a668845138c08c503e337fe","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js","lines":{"begin":51,"end":55}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js","lines":{"begin":45,"end":49}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e7e7f0899a668845138c08c503e337fe","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js","lines":{"begin":8,"end":11}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js","lines":{"begin":13,"end":16}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"21bb124a570e4088d0684442dade767f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js","lines":{"begin":13,"end":16}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js","lines":{"begin":8,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"21bb124a570e4088d0684442dade767f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":20,"end":26}},"remediation_points":630000,"other_locations":[{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":40,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d22f4f9ddac4d361d9f6721a25ded899","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":40,"end":46}},"remediation_points":630000,"other_locations":[{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":20,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d22f4f9ddac4d361d9f6721a25ded899","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":103,"end":116}},"remediation_points":630000,"other_locations":[{"path":"app/assets/javascripts/ide/constants.js","lines":{"begin":43,"end":56}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d9ad4d62a2c7b3a5ee02495f81a78cd8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/constants.js","lines":{"begin":43,"end":56}},"remediation_points":630000,"other_locations":[{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":103,"end":116}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b9bd1f7a0fafe178e70f6a652d6fd8be","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/components/diff_file_editor.js","lines":{"begin":13,"end":26}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js","lines":{"begin":8,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"423d5a0cc874e941bd22657b325446b4","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js","lines":{"begin":8,"end":21}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/components/diff_file_editor.js","lines":{"begin":13,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"716df5a6c1cbc00338163aa638130bca","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":436,"end":441}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":452,"end":457}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5a2cd4548ee8c0362f69cc88ee5982c0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":452,"end":457}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":436,"end":441}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5a2cd4548ee8c0362f69cc88ee5982c0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":214,"end":221}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":13,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e71451918dac542ee62e859e4a694a23","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":13,"end":20}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":214,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6696916f5ca830f21733956d3dc83b22","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":45,"end":49}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":50,"end":54}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"723e924a871cea3a66a3ccd49f2f4407","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":50,"end":54}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":45,"end":49}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"723e924a871cea3a66a3ccd49f2f4407","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":97,"end":102}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":103,"end":108}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"38cb87a7a94d43c42903f71cfd248953","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":103,"end":108}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":97,"end":102}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"38cb87a7a94d43c42903f71cfd248953","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":125,"end":129}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":150,"end":153}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"77fe9230b5c1c681ca23dd48b5bde881","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":150,"end":153}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":125,"end":129}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"77fe9230b5c1c681ca23dd48b5bde881","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":39,"end":42}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":44,"end":47}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6369d3d651e38cbbac285afad5297e9d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":44,"end":47}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":39,"end":42}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6369d3d651e38cbbac285afad5297e9d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":108,"end":111}},"remediation_points":510000,"other_locations":[{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":113,"end":116}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 52**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2b8d07121aa08a0ef9af946ea4e5d60d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":113,"end":116}},"remediation_points":510000,"other_locations":[{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":108,"end":111}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 52**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2b8d07121aa08a0ef9af946ea4e5d60d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":42,"end":45}},"remediation_points":510000,"other_locations":[{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":46,"end":49}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 52**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1c224ff513833f281d7bb58fb1280314","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":46,"end":49}},"remediation_points":510000,"other_locations":[{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":42,"end":45}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 52**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1c224ff513833f281d7bb58fb1280314","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions.js","lines":{"begin":133,"end":136}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions.js","lines":{"begin":144,"end":147}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5e09ce34a63a712c502dfcaa775c5a8e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions.js","lines":{"begin":144,"end":147}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions.js","lines":{"begin":133,"end":136}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5e09ce34a63a712c502dfcaa775c5a8e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":120,"end":123}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":128,"end":131}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5dcf5b9ed09acddcf34d9b99f4f7d4e0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":128,"end":131}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":120,"end":123}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5dcf5b9ed09acddcf34d9b99f4f7d4e0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects/tree/services/commit_pipeline_service.js","lines":{"begin":3,"end":11}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/jobs/services/job_service.js","lines":{"begin":3,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"020d87eef79dcc6d4979a4877f795e88","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/services/job_service.js","lines":{"begin":3,"end":11}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/projects/tree/services/commit_pipeline_service.js","lines":{"begin":3,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7f3c54ba927268843f9fe397b30c4541","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/star.js","lines":{"begin":19,"end":23}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/star.js","lines":{"begin":23,"end":27}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cc2a5b7fe6f4a900c4690f4960b1d2ce","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/star.js","lines":{"begin":23,"end":27}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/star.js","lines":{"begin":19,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cc2a5b7fe6f4a900c4690f4960b1d2ce","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":102,"end":104}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/boards/stores/modal_store.js","lines":{"begin":71,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e9ffa7b55a2d3d697297bb9c77d9e0c8","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/stores/modal_store.js","lines":{"begin":71,"end":74}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":102,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cae962d929e29ddbbe69117801f17860","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/groups/issues/index.js","lines":{"begin":6,"end":13}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/pages/groups/merge_requests/index.js","lines":{"begin":6,"end":13}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"65a33b3fe4ae20436c9fb46d65d9db04","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/groups/merge_requests/index.js","lines":{"begin":6,"end":13}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/pages/groups/issues/index.js","lines":{"begin":6,"end":13}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1b0f3dfb819efd869e74a58517390763","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":55,"end":58}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":51,"end":54}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ed657cc5e6969316c8bd1d2be6204668","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":51,"end":54}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":55,"end":58}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3da1a06772dc32dbbb550900c97f6cbd","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":125,"end":130}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":154,"end":159}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f6c22427e43838beefc533b33cde9955","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":154,"end":159}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":125,"end":130}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f6c22427e43838beefc533b33cde9955","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/deploy_keys/service/index.js","lines":{"begin":15,"end":18}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/deploy_keys/service/index.js","lines":{"begin":20,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5eaf7d9cd8339dca959f74f384e02906","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/deploy_keys/service/index.js","lines":{"begin":20,"end":23}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/deploy_keys/service/index.js","lines":{"begin":15,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5eaf7d9cd8339dca959f74f384e02906","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":146,"end":148}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":250,"end":252}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"70155684217b5724087f40626f7766bc","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":250,"end":252}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":146,"end":148}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"70155684217b5724087f40626f7766bc","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":134,"end":137}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":138,"end":141}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"180e35eb524273ddfc92de366020a611","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":138,"end":141}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":134,"end":137}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"180e35eb524273ddfc92de366020a611","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issuable_bulk_update_sidebar.js","lines":{"begin":100,"end":106}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/issuable_bulk_update_sidebar.js","lines":{"begin":117,"end":123}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"672a79b04a463defdd46c0086474fd52","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issuable_bulk_update_sidebar.js","lines":{"begin":117,"end":123}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/issuable_bulk_update_sidebar.js","lines":{"begin":100,"end":106}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"672a79b04a463defdd46c0086474fd52","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":66,"end":69}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":146,"end":149}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4ebfab6abe3fb873067f7b37190c9136","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":146,"end":149}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":66,"end":69}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4ebfab6abe3fb873067f7b37190c9136","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":46,"end":53}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":60,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"10bdf4c525726570be1c10982443983e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":60,"end":67}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":46,"end":53}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"10bdf4c525726570be1c10982443983e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/dropdown_hint.js","lines":{"begin":75,"end":77}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/dropdown_user.js","lines":{"begin":78,"end":80}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"223d4160dbc2f173c7019bf4258bf71a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/dropdown_user.js","lines":{"begin":78,"end":80}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/dropdown_hint.js","lines":{"begin":75,"end":77}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f69c39865bb0c31ba27242592efb90a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1451,"end":1457}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1459,"end":1465}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"06432a7535afbcbc307fe2c2fad49f03","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1459,"end":1465}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1451,"end":1457}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"06432a7535afbcbc307fe2c2fad49f03","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":63,"end":66}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":423,"end":426}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3cf8b42abcd81551bbf1d4b6a86a23ec","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":423,"end":426}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":63,"end":66}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3cf8b42abcd81551bbf1d4b6a86a23ec","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":223,"end":224}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":564,"end":565}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9b59a169ec24710bb17e53176d9f6484","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":564,"end":565}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":223,"end":224}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"629f61554a8bb6a8eb033206d609a01e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_form.js","lines":{"begin":93,"end":95}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/gl_form.js","lines":{"begin":96,"end":98}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a37da012d8b9134f9c2c07cbe6e5df45","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_form.js","lines":{"begin":96,"end":98}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/gl_form.js","lines":{"begin":93,"end":95}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a37da012d8b9134f9c2c07cbe6e5df45","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":48,"end":52}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":68,"end":72}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ad4849cc4103d257a75a53f533f93f88","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":68,"end":72}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":48,"end":52}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ad4849cc4103d257a75a53f533f93f88","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/project.js","lines":{"begin":1,"end":6}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/boards/models/milestone.js","lines":{"begin":1,"end":6}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"63e808e699724c7876463b889a910d69","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/milestone.js","lines":{"begin":1,"end":6}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/boards/models/project.js","lines":{"begin":1,"end":6}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d888235869b541141524699d46a10fe","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":142,"end":143}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":139,"end":139}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a5d03fc520fddde3ecc4ff06b86c9a0d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":139,"end":139}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":142,"end":143}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5a0bd2adf62a695699f94e1cef624dd7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":73,"end":76}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":78,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1dc35d639071fed3c333a47b9c544d56","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":78,"end":81}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":73,"end":76}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1dc35d639071fed3c333a47b9c544d56","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":234,"end":240}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":260,"end":266}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c9f4b3f70b4ef899508948c84a381a89","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":260,"end":266}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":234,"end":240}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c9f4b3f70b4ef899508948c84a381a89","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/protected_tags/protected_tag_edit.js","lines":{"begin":16,"end":20}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/protected_branches/protected_branch_edit.js","lines":{"begin":24,"end":28}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d44f8e3653f3f06777c422f4151a7d3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/protected_branches/protected_branch_edit.js","lines":{"begin":24,"end":28}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/protected_tags/protected_tag_edit.js","lines":{"begin":16,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9ff0344a1b63248724d8413b03573b43","severity":"minor","engine_name":"duplication"},{"categories":["Security"],"check_name":"Insecure Dependency","content":{"body":"**Advisory**: CVE-2018-1000211\n\n**URL**: https://blog.justinbull.ca/cve-2018-1000211-public-apps-cant-revoke-tokens-in-doorkeeper/\n\n**Solution**: upgrade to >= 4.4.0, >= 5.0.0.rc2"},"description":"Doorkeeper gem does not revoke token for public clients","location":{"path":"Gemfile.lock","lines":{"begin":173,"end":173}},"remediation_points":5000000,"severity":"minor","type":"Issue","fingerprint":"13e50c564110ecb5eba441004fe52261","engine_name":"bundler-audit"},{"categories":["Security"],"check_name":"Insecure Dependency","content":{"body":"**Advisory**: CVE-2018-3769\n\n**URL**: https://github.com/ruby-grape/grape/issues/1762\n\n**Solution**: upgrade to >= 1.1.0"},"description":"ruby-grape Gem has XSS via \"format\" parameter","location":{"path":"Gemfile.lock","lines":{"begin":346,"end":346}},"remediation_points":5000000,"severity":"minor","type":"Issue","fingerprint":"ac6d981abbf80f299374d5a1081c7d66","engine_name":"bundler-audit"}]
diff --git a/spec/fixtures/codequality/codequality.json.gz b/spec/fixtures/codequality/codequality.json.gz
new file mode 100644
index 00000000000..f04ec0065b4
--- /dev/null
+++ b/spec/fixtures/codequality/codequality.json.gz
Binary files differ
diff --git a/spec/fixtures/emails/merge_request_multiple_patches.eml b/spec/fixtures/emails/merge_request_multiple_patches.eml
new file mode 100644
index 00000000000..311b99a525d
--- /dev/null
+++ b/spec/fixtures/emails/merge_request_multiple_patches.eml
@@ -0,0 +1,181 @@
+From: "Jake the Dog" <jake@adventuretime.ooo>
+To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+Subject: new-branch-with-a-patch
+Date: Wed, 31 Oct 2018 17:27:52 +0100
+X-Mailer: MailMate (1.12r5523)
+Message-ID: <7BE7C2E6-F0D9-4C85-9F55-B2A4BA01BFEC@gitlab.com>
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_="
+
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+
+This applies nicely to a branch freshly created from the root-ref
+
+The other attachments in this email are ignored
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+Content-Disposition: attachment;
+ filename=0002-This-does-not-apply-to-the-feature-branch.patch
+Content-Transfer-Encoding: quoted-printable
+
+=46rom 00c68c2b4f954370ce82a1162bc29c13f524897e Mon Sep 17 00:00:00 2001
+From: Patch user <patchuser@gitlab.org>
+Date: Mon, 22 Oct 2018 11:05:48 +0200
+Subject: [PATCH] This does not apply to the `feature` branch
+
+---
+ files/ruby/feature.rb | 5 +++++
+ 1 file changed, 5 insertions(+)
+ create mode 100644 files/ruby/feature.rb
+
+diff --git a/files/ruby/feature.rb b/files/ruby/feature.rb
+new file mode 100644
+index 0000000..fef26e4
+--- /dev/null
++++ b/files/ruby/feature.rb
+@@ -0,0 +1,5 @@
++class Feature
++ def bar
++ puts 'foo'
++ end
++end
+-- =
+
+2.19.1
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+Content-Disposition: attachment; filename=0001-A-commit-from-a-patch.patch
+Content-Transfer-Encoding: quoted-printable
+
+=46rom 3fee0042e610fb3563e4379e316704cb1210f3de Mon Sep 17 00:00:00 2001
+From: Patch user <patchuser@gitlab.org>
+Date: Thu, 18 Oct 2018 13:40:35 +0200
+Subject: [PATCH] A commit from a patch
+
+---
+ README | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/README b/README
+index 3742e48..e40a3b9 100644
+--- a/README
++++ b/README
+@@ -1 +1,3 @@
+ Sample repo for testing gitlab features
++
++This was applied in a patch!
+-- =
+
+2.19.1
+
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+Content-Disposition: attachment; filename=really-not-a-patch.png
+Content-Type: image/png
+Content-Transfer-Encoding: base64
+
+iVBORw0KGgoAAAANSUhEUgAAAjMAAAAfCAYAAAASo0ymAAABfGlDQ1BJQ0MgUHJvZmlsZQAAKJFj
+YGAqSSwoyGFhYGDIzSspCnJ3UoiIjFJgv8PAzcDDIMRgxSCemFxc4BgQ4MOAE3y7xsAIoi/rgsxK
+8/x506a1fP4WNq+ZclYlOrj1gQF3SmpxMgMDIweQnZxSnJwLZOcA2TrJBUUlQPYMIFu3vKQAxD4B
+ZIsUAR0IZN8BsdMh7A8gdhKYzcQCVhMS5AxkSwDZAkkQtgaInQ5hW4DYyRmJKUC2B8guiBvAgNPD
+RcHcwFLXkYC7SQa5OaUwO0ChxZOaFxoMcgcQyzB4MLgwKDCYMxgwWDLoMjiWpFaUgBQ65xdUFmWm
+Z5QoOAJDNlXBOT+3oLQktUhHwTMvWU9HwcjA0ACkDhRnEKM/B4FNZxQ7jxDLX8jAYKnMwMDcgxBL
+msbAsH0PA4PEKYSYyjwGBn5rBoZt5woSixLhDmf8xkKIX5xmbARh8zgxMLDe+///sxoDA/skBoa/
+E////73o//+/i4H2A+PsQA4AJHdp4IxrEg8AAAGcaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8
+eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQu
+MCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1y
+ZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAg
+ICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAg
+ICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjU2MzwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAg
+ICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4zMTwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAg
+IDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpZ5vHZAAAS+ElE
+QVR4Ae2cC1QUV5rH/xnA1uZli02MiEHQqBMdTGB8ZIniA5xgfCTRycb1rBmdTHJGJ+toPBtzzGg2
+Rs3LdVTOYhKMcDgmDNHVNXKiGKJINCoqiXpUEFdB0CDKCtrSYp/Zr6q7muqmu+pWd7XgnFtH6Xrc
++h6/+92qr+69VQ9dqrvxd/CFE+AEOAFOgBPgBDiBB5TALx5Qu7nZnAAnwAlwApwAJ8AJiAR4MsMD
+gRPgBDgBToAT4AQeaAI8mXmgq48bzwlwApwAJ8AJcAI8meExwAlwApwAJ8AJcAIPNAGezDzQ1ceN
+5wQ4AU6AE+AEOIFgTwiamls87eb7OAFOgBPgBDgBToAT0I1ARHhXXWTxnhldMHIhnAAnwAlwApwA
+J9BRBIL+vOjfl7srt969577L63Zj1VGU/liHHn1j0I0xNbI1X8TRo5V4yNQLEQbGk7xawH6go/Sy
+W8hLBoqAL3EaKFtY5doaz6PkwCk8FN0H3f1oJx0d93r5wcpN13K2RhzbX4KKn4FH+vSAx65sXRX+
+YwgTYq5k72FUXqzChaoq1DQGI7Z3dwT6ai/oPbj/OJoNDyM6IsRvmL74odTe9LbPbwc7gQCDQZ9W
+5VFK0co52FKp7OW0N9di6sAI1B//G7YU3kHvEb+Gyah8jnTUWncYWdm7kf7GWrz0eIS0O+C/HaVX
+P8dsqPphL07dicFvxg6BQT/BnVSSfv76EqcdDcVafww5X1A7iRmGR/1oJ+pxrx9nT8z08sOT7IDv
+s9YiPzcf10InImlkfy9tLrD8Au5jABRY6w8j56vdbZIFfslxCG/bE5A1Idazqc2EpUVj/cwhfuvw
+xQ+l9qa3fX47eN8EWFC4cj4KKgdjadZiJATg5uUxUQ6JiEGYOQpm8X9bhiLtC4MRIcH2PCg4RBjv
+Mml7YgkOFRFGhnrMpQKHt6P06ubRbZzIy8f2radxVzeZnVmQfv76FKcdjSa4i2hBiL/NRDXu9ePs
+EZlefngUHuCdQcF0daOlRxcEeVUVYH5e9XbeA8aEF/HZZ5vw2Zo/wSyYqchPRz8csdbD70Zjt8kn
+P5Tam8726Ugu4KJaxam4TWixBUaVx8tk6vx3kSrps57HqtdWosI8Ge9/8BylMV4WR0u32WwICvLe
+7IWzjQnP4vPPn/UiKHC7O0qvfh4ZYOxB0lpCYb/N6Se5c0oKgL8a4rRjmNhATUhsQwblZsRsnnrc
+B4Az9PeD2eH7XjAQ/O67E7orFG8Dxm5iMnhNd+n3T6BWP9Tb2/2zvTNpkgb9PCYdOhjKINcxf4Y6
+YJQSquoTxdiVl4eTtwWrYjDzzYVIGyg+0zjNrC3NQ27JFUREGGC1Av/04qsY0ad9f1PD6d3IzdmJ
+k9cs4rlh5niMmTIL01PinLK0rHSE3trSTfikBMhIj8P3m5W5NJwrxqfr81AhsgP6Jk3GnDnP4VEp
+c7ReRM7H2agjpytqBM+/wZqPz0CA2NJiQMYfX8eIXu05KjFqvnwCuwv34shPZ3DNofexpIl4/l+m
+Y6BJ+13U7u9djEg0YR91L4sXL/MwzF/4KpJktjHp1eyvFee+244tW3ejWvTFiMeSnsZvZ09HQrir
+LyxxqsRNOsbkBxXWEgdV1D7WZhfjlqAkNgWzUyRtvv2qxr1mzmx2sPqhGvcOdZarJ1CwaQv2VV4X
+94TFDsfLf/ydS1wJB1iuG1rqw6He+0+A+Kn5a71cgjXritB7yh8oRmJl9jWh6JN1KL07DAvmP2vv
+UaKjavIEAbpykVnEssoaB6zl3HU2nP4auTsO42ZECt6YPzFgQ12q7c3dMMe2N/tY6s2LSI+7Wfhp
+iQO97fNotIadHoeZvJ3veluQSgk30Vps2ZiHq/1SkJ4UY99enYlz9lxEKmj/peLW+gqcPFWOmtuU
+0bgttqvFWPxRPiUyQHLaRExKG47elgvYlV2KZreymjbvs96WhjpUV5YiK1OZS/PpfCxebU9kho4Z
+h6eGRKH62E4sn7cOl5x4gtDFEEFJYATCRKe7wWAwiNuRkeHahvgc0Kr3rseuQ5QQ9R6MVOKcOiQG
+Fcd2Y/XCNahy6mUnbPf3CAookemWNA6TxsQD18qxYckK/CirODa9Wvy1onTDIqzOFRKZKKRmTEb6
+mEdFX0ovOLI00Q2NcariOpsf1InGGAe1peuwQkxkYpA+dSKG3iil+TKlKlYwHFaMey2cGXRREVY/
+2OKebsRVuzFvyXoxkXlsFF0PxgzGrZojFFeLcLih7fGK9brBWh/tvPX4tQr9+bH4a+gRjZZrtdj3
+5X40ygy1XS7DlkMXUA26Vjj2s8gTivrMRabfl1XWOGAt525D7Q+b6H6yDScrDXj+n1MDlsg49Sq2
+N2cp54o3+1jrzSlIZYWVH2sc6G2fivlMhxl6ZpjkIPmltzAvvb9Y+Fdb3sZHRRdwsqYJA2mSsLTE
+pMzCEnratF38Gr9/Z5u02+XXevOmuJ3+xgc0OdjRNTHzFTQ3W30OxI7SKziizMWC4vzdor8zaEJ1
+hshqFp7c/DY27C/H1oM1WDiWnrwMsXhp0WIqZ0PhX15BQcsYzKMnL+H27OvSf+p7WPVCNHo5ey5e
+xIQ9H2LpF2dw4mITEmT1pkVH34w/4Z0ZT4in/Lr3Oiz/ohyFJeeROMkeG0x6NfhrOVeI7GOUNZvH
+YcWqWYhxZNy/faEGV++1J6RcH+yeMvkhE6es9woKs8updBTmf/guknrS6uSn8Om/LcNBeT4mk8ey
+qhr3Gjiz6ANY/WCMe+rf2/Gf+aQ6CnNXrUZKL3vlPjehGAvezkPWl98jef5ocS6L1uuGcn3IvDUM
+wpLPN8l2yFZ158for3EQpowyYsOhYpRdnoE0R+/22ZLvROOmpSU75vcwypO5xMxFdo7vq6xxwFqu
+zZKudOuoKs2iB4QjQOhwLP/gtbae7rZiuq6ptjeZNmX7tNebTLSHVe38lOPAP/vY35X24IrCLt2S
+mcRf9XOq6ZtIs8iLahHipc/Aamt1lm2/Yp/auicnE5HPP4PEuBiYzCaEh0tjLu3PYN3TEXoVuViq
+cbyGrDdPxNOy5CHp2QyE7f8UNxvc72RW2Mm1ihOA29+qWUlQfmR6BIaLJ1D4zQn878+30KVLF9xt
+qGcX0K6k0J1jRMYEeyIjHH40JR19KZmpOHwSFkpmhBrUplfd35qfjguqkD57ijOREbaDwmNpsLP9
+olgf7Yt73aPND0BRr+UmLguaaHhxmJDICEtQLMZNiMfBHRfs2378VY57QbA6Zyb1rH6wxr3lOs6K
+TeA6DnyTgzN37deGLtRHKw7FVV+B0Plrf0NG23VDsT6YnJUX0osfu79Dxv8GOLQNRSWVSBPe2rHV
+YB9dc4HhGDHQca3UxM/uj75c5Iw8rDPHgdbrJA3Hf/UBVogqB2P5XymR8Tys4MEo/3eptzcV+3yo
+N0WrWTnLhCjGgT/2hfbDw/7fymWWtq3qkMwIN7EYmGXzLIK62qentkJ7Dmbsn46Zo05Rd+kZFGyk
+/w5bU//1Lcwa21/hjYI2p3xZ018vAxdqYOK3D4mXS1sLN6M3OVFRXUe3mUEuPTDSJCqX8j44fLqA
+es8KhYsf5VKx8TCRIS3/d8cHSfJTTOguD1RbM3V5uy5a9ar5GxxiT+mMXdVCmaE+XE1V3GL3g0Gv
+ozLND0e7xMG9VuHc+7OocWaygtUPKscU91KQmyktratDg8yIoQPiYe1pdvJib78M9SHTw7qqJz8w
++GtIGIHU0G2UwBSjlpIZ0/mDKCNj+05NRS/JaA38hIRWz+u4ZILir4Y4YIoXj8rOYN/RK5g98hGP
+Rzt+pwf7NNUbgwesnEVRDHHgo33ig7jR5P0lIgZXlIqo3QGUznU91jZ87bpf61aQCWl/eBfjZjXh
+Wn0dzh7dg62F5diXuxHJIz6ENPKkVaxq+UDpVeJCx8ShePfx+LvN4li4uW9vl0SmzYe7NODkx0Jv
+qP2PkMiEplD36xxn96u16r/x2oqdfghuQYvwgCx1GXUJx2O0WUH+ifb6rNe7v9INv5U1b/YLnAON
+L34o6XUcu3NbNrmIVAUbJZAOvQH/8c6ZSTWrH1SOKe4lZt3HYslb45RN0Np+JdnKUjUe1YcfWPyl
+F55TnomnyfblKKtqRJ/9B8hWI9JGDmizWfKRSZ7jNOmcNin+rZE898ubU6B0zL2A+/WPtZxTMBA2
+6hW8P6Mr3l9I8602voeEuL86hyllxdhXJRvYz1AsqWgf6RIXLfWmpE2yXY2zXIZkg3yftC4d02Sf
+EVP/YxOmSjIC8KtpArBe+g1B9ueYkGAPF2ubVbzxBRkj0CtuEFJnvI7X0qJItZib+2VCR+n1arQx
+CoNC6ei1gzgpe+y8VLJXfBvIZHTPNe/Zh5munUKdkED7utju2S8w/eKciQxNtcS3+d/6KpHOE+ry
+OkrLapwyGs+WoYK2zMMG2IcCNOtV9ze6n314c9fXB8QhB6dyetK0WKVW17ZXlzXNfqhoNXRFNBW5
+tX+fbPJ1E04d9n+ISdCsGPeiaeqcxWJqf1j9YI17Q6jIBZXf4Uf5TFfRDrf6DeB1Q81tUA+0+NTp
+b7vU4i8ZlTBivPhCwPaNK7H5EA24xY7Hk455RaLNGuWp++lDCWNvDDXTeTdui30/LhJY44C1nEx4
+j2jqATA9gQULhCTYguwlmaj153Kg5IdMr7Cq3t7osztK9uldbz7wc3PJddMn+yy4dO4sTp87j0Z/
+7l2ulrhsud8tXQ66bFBW508s2JrP40BJFVqNIWi9Qm/R0HL820JEVhthae2G4eNHoyd1X53bthSr
+C7siffpY/DLOhObKE8gvEl7JHIwwD7mPKEjhT0fpVTBJdsiM0S8Mw57ccmQtXoHrr05ExNVjyN4h
+8IlCxuj+srLCagT6D6LErqYWGz7OwoRkupHTNWzI+AntXkF2O9F10xGM1afykEnjeKm/DMdPO3Kw
+p1KYgeDfUpa7DDmWlzHIeAVbc4tFYRmjHU+LmvWq+2tKfg6TzKXYdSof81bWYm76UIS0/Izvs7ch
+ir4wPduPL+d6JaHZD6+S7AeC4jBhagzKqN5XvJeFuVOScP2Hv2F7W16oIqD9Yda4t5+pzrm9Bg97
+mP1gjHuaN/Tiqyko21iKtQsXY9JLk9GPPlF/4+oZFO0oxZ0xC7H+ZZovQove1w0P3ins0osfu7+i
+MT0T8UwsUFBzXZxDlJw+0rULXwM/Bef8PBSKfv1p7JkmK/9l5S2MGRBGHUiPY/KkJ+jxhzEOmMvJ
+TBWzS/qca+IsLEg7h7VF5Vi6thhZi8Y5O45lpRlWlfygeyPj/c2pSMk+3euNlbPTOuUVX+yjN5Iz
+V68BvaQcsC//sycz3UOc49OungoZhudUSz4B2Fp/kj5v7TqEUX1oJ3IOCdKiaLIoJTMU85GxifS0
+UYw9X+Vhj6QoNB6z//x7nyZxdZRee0+FOpeYsfPwhiUTH1F3ccHG/3J4HI+5y15Hon1mo0RB/H38
+hdcx7cYn2H7sCLZXHrEfG/o0JTPyySoup7TfoGD83bKXUf/OZpQV5tF/oUg80tMM2FN0hiZu+76Y
+B0RR1/dm7HOImDTvPaRK3xLyQa+6vyZMX/U+IrPX0TyrUmTT6/D2JQZzI+XZL1ucOk5W/tHkB5ve
+gdMWY279h8g+dATZmUK9RuGpUTQBmF61lb62rWyU61HWuJfOUucslVT+ZfWDNe57jpyDVcEmrMnc
+iV1fbG5THhqDaU887Nxmv26w1YdTMOOKXvxY/bWbZcTwKSkoyBRiPh4TnnyknbXs8gLDhWayI2nu
+Msy4uw4FdM3aVUkmmkORQcmMsLDGAWu5IMfVq6usVztx5kJMOruIHnjy8GXZUMxOFrqKtC7KfrC2
+N1b72OuNzQ9Wfqz3Lc32Ob6kLSQzkarzG9l8ci/10KW6G39339nU7D645l4i0Ns2WCy36UuoNljv
+BcFkivCSSOltR0fppcze0kTdb9T3Rf/Ce5p8fHrQysOKxkZ7b4zR5J/Oqu1vY8UOA5Z+thRx5EvT
+PRvN+aC30IRrZLtFP71y0VZLI+gNfhho+NJIyR119AV40d8PS2Oj+GhgECbKeWQXYJd0Es/qB3vc
+E+uGJqpcA4KDDAj3CKfj2q9O2GRiWPyVFVdd1VueqkJNBVjjgLWcJuWdurC+9aY/P//tiwj3fwqJ
+UIWdNJnp1NHFjfNAwJ7MAG9mvgvpzVAPxfguToAT4AQ4AU7ASUCvZIZ9mMmpmq9wAu0J3Gu1z9Bk
+famovQS+hxPgBDgBToAT8I0A75nxjRs/y42ApaEGV24Go08CfYzP7Rjf5AQ4AU6AE+AEPBHQq2fG
+YzLjSSHfxwlwApwAJ8AJcAKcQGck8IvOaBS3iRPgBDgBToAT4AQ4AVYCPJlhJcXLcQKcACfACXAC
+nECnJMCTmU5ZLdwoToAT4AQ4AU6AE2AlwJMZVlK8HCfACXACnAAnwAl0SgI8memU1cKN4gQ4AU6A
+E+AEOAFWAv8PxvdR0yK8jugAAAAASUVORK5CYII=
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=--
diff --git a/spec/fixtures/emails/merge_request_with_conflicting_patch.eml b/spec/fixtures/emails/merge_request_with_conflicting_patch.eml
new file mode 100644
index 00000000000..ddfdfe9e24a
--- /dev/null
+++ b/spec/fixtures/emails/merge_request_with_conflicting_patch.eml
@@ -0,0 +1,45 @@
+From: "Jake the Dog" <jake@adventuretime.ooo>
+To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+Subject: feature
+Date: Wed, 31 Oct 2018 17:27:52 +0100
+X-Mailer: MailMate (1.12r5523)
+Message-ID: <7BE7C2E6-F0D9-4C85-9F55-B2A4BA01BFEC@gitlab.com>
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_="
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+
+This does not apply
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+Content-Disposition: attachment;
+ filename=0002-This-does-not-apply-to-the-feature-branch.patch
+Content-Transfer-Encoding: quoted-printable
+
+=46rom 00c68c2b4f954370ce82a1162bc29c13f524897e Mon Sep 17 00:00:00 2001
+From: Patch user <patchuser@gitlab.org>
+Date: Mon, 22 Oct 2018 11:05:48 +0200
+Subject: [PATCH] This does not apply to the `feature` branch
+
+---
+ files/ruby/feature.rb | 5 +++++
+ 1 file changed, 5 insertions(+)
+ create mode 100644 files/ruby/feature.rb
+
+diff --git a/files/ruby/feature.rb b/files/ruby/feature.rb
+new file mode 100644
+index 0000000..fef26e4
+--- /dev/null
++++ b/files/ruby/feature.rb
+@@ -0,0 +1,5 @@
++class Feature
++ def bar
++ puts 'foo'
++ end
++end
+-- =
+
+2.19.1
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=--
diff --git a/spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml b/spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml
new file mode 100644
index 00000000000..965658721cd
--- /dev/null
+++ b/spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml
@@ -0,0 +1,44 @@
+From: "Jake the Dog" <jake@adventuretime.ooo>
+To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+Subject: new-branch-with-a-patch
+Date: Wed, 24 Oct 2018 16:39:49 +0200
+X-Mailer: MailMate (1.12r5523)
+Message-ID: <F1F36291-728D-4E8F-AFEB-C398B8D9BB4E@gitlab.com>
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_="
+
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+
+This applies nicely to a branch freshly created from the root-ref
+
+The other attachments in this email are ignored
+
+/target_branch with-codeowners
+
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+Content-Disposition: attachment; filename=0001-A-commit-from-a-patch.patch
+Content-Transfer-Encoding: quoted-printable
+
+=46rom 3fee0042e610fb3563e4379e316704cb1210f3de Mon Sep 17 00:00:00 2001
+From: Patch user <patchuser@gitlab.org>
+Date: Thu, 18 Oct 2018 13:40:35 +0200
+Subject: [PATCH] A commit from a patch
+
+---
+ README | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/README b/README
+index 3742e48..e40a3b9 100644
+--- a/README
++++ b/README
+@@ -1 +1,3 @@
+ Sample repo for testing gitlab features
++
++This was applied in a patch!
+-- =
+
+2.19.1
diff --git a/spec/fixtures/emails/valid_merge_request_with_patch.eml b/spec/fixtures/emails/valid_merge_request_with_patch.eml
new file mode 100644
index 00000000000..143fa77d1fa
--- /dev/null
+++ b/spec/fixtures/emails/valid_merge_request_with_patch.eml
@@ -0,0 +1,151 @@
+From: "Jake the Dog" <jake@adventuretime.ooo>
+To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+Subject: new-branch-with-a-patch
+Date: Wed, 24 Oct 2018 16:39:49 +0200
+X-Mailer: MailMate (1.12r5523)
+Message-ID: <F1F36291-728D-4E8F-AFEB-C398B8D9BB4E@gitlab.com>
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_="
+
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+
+This applies nicely to a branch freshly created from the root-ref
+
+The other attachments in this email are ignored
+
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+Content-Disposition: attachment; filename=0001-A-commit-from-a-patch.patch
+Content-Transfer-Encoding: quoted-printable
+
+=46rom 3fee0042e610fb3563e4379e316704cb1210f3de Mon Sep 17 00:00:00 2001
+From: Patch user <patchuser@gitlab.org>
+Date: Thu, 18 Oct 2018 13:40:35 +0200
+Subject: [PATCH] A commit from a patch
+
+---
+ README | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/README b/README
+index 3742e48..e40a3b9 100644
+--- a/README
++++ b/README
+@@ -1 +1,3 @@
+ Sample repo for testing gitlab features
++
++This was applied in a patch!
+-- =
+
+2.19.1
+
+
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=
+Content-Disposition: attachment; filename=really-not-a-patch.png
+Content-Type: image/png
+Content-Transfer-Encoding: base64
+
+iVBORw0KGgoAAAANSUhEUgAAAjMAAAAfCAYAAAASo0ymAAABfGlDQ1BJQ0MgUHJvZmlsZQAAKJFj
+YGAqSSwoyGFhYGDIzSspCnJ3UoiIjFJgv8PAzcDDIMRgxSCemFxc4BgQ4MOAE3y7xsAIoi/rgsxK
+8/x506a1fP4WNq+ZclYlOrj1gQF3SmpxMgMDIweQnZxSnJwLZOcA2TrJBUUlQPYMIFu3vKQAxD4B
+ZIsUAR0IZN8BsdMh7A8gdhKYzcQCVhMS5AxkSwDZAkkQtgaInQ5hW4DYyRmJKUC2B8guiBvAgNPD
+RcHcwFLXkYC7SQa5OaUwO0ChxZOaFxoMcgcQyzB4MLgwKDCYMxgwWDLoMjiWpFaUgBQ65xdUFmWm
+Z5QoOAJDNlXBOT+3oLQktUhHwTMvWU9HwcjA0ACkDhRnEKM/B4FNZxQ7jxDLX8jAYKnMwMDcgxBL
+msbAsH0PA4PEKYSYyjwGBn5rBoZt5woSixLhDmf8xkKIX5xmbARh8zgxMLDe+///sxoDA/skBoa/
+E////73o//+/i4H2A+PsQA4AJHdp4IxrEg8AAAGcaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8
+eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQu
+MCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1y
+ZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAg
+ICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAg
+ICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjU2MzwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAg
+ICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4zMTwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAg
+IDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpZ5vHZAAAS+ElE
+QVR4Ae2cC1QUV5rH/xnA1uZli02MiEHQqBMdTGB8ZIniA5xgfCTRycb1rBmdTHJGJ+toPBtzzGg2
+Rs3LdVTOYhKMcDgmDNHVNXKiGKJINCoqiXpUEFdB0CDKCtrSYp/Zr6q7muqmu+pWd7XgnFtH6Xrc
++h6/+92qr+69VQ9dqrvxd/CFE+AEOAFOgBPgBDiBB5TALx5Qu7nZnAAnwAlwApwAJ8AJiAR4MsMD
+gRPgBDgBToAT4AQeaAI8mXmgq48bzwlwApwAJ8AJcAI8meExwAlwApwAJ8AJcAIPNAGezDzQ1ceN
+5wQ4AU6AE+AEOIFgTwiamls87eb7OAFOgBPgBDgBToAT0I1ARHhXXWTxnhldMHIhnAAnwAlwApwA
+J9BRBIL+vOjfl7srt969577L63Zj1VGU/liHHn1j0I0xNbI1X8TRo5V4yNQLEQbGk7xawH6go/Sy
+W8hLBoqAL3EaKFtY5doaz6PkwCk8FN0H3f1oJx0d93r5wcpN13K2RhzbX4KKn4FH+vSAx65sXRX+
+YwgTYq5k72FUXqzChaoq1DQGI7Z3dwT6ai/oPbj/OJoNDyM6IsRvmL74odTe9LbPbwc7gQCDQZ9W
+5VFK0co52FKp7OW0N9di6sAI1B//G7YU3kHvEb+Gyah8jnTUWncYWdm7kf7GWrz0eIS0O+C/HaVX
+P8dsqPphL07dicFvxg6BQT/BnVSSfv76EqcdDcVafww5X1A7iRmGR/1oJ+pxrx9nT8z08sOT7IDv
+s9YiPzcf10InImlkfy9tLrD8Au5jABRY6w8j56vdbZIFfslxCG/bE5A1Idazqc2EpUVj/cwhfuvw
+xQ+l9qa3fX47eN8EWFC4cj4KKgdjadZiJATg5uUxUQ6JiEGYOQpm8X9bhiLtC4MRIcH2PCg4RBjv
+Mml7YgkOFRFGhnrMpQKHt6P06ubRbZzIy8f2radxVzeZnVmQfv76FKcdjSa4i2hBiL/NRDXu9ePs
+EZlefngUHuCdQcF0daOlRxcEeVUVYH5e9XbeA8aEF/HZZ5vw2Zo/wSyYqchPRz8csdbD70Zjt8kn
+P5Tam8726Ugu4KJaxam4TWixBUaVx8tk6vx3kSrps57HqtdWosI8Ge9/8BylMV4WR0u32WwICvLe
+7IWzjQnP4vPPn/UiKHC7O0qvfh4ZYOxB0lpCYb/N6Se5c0oKgL8a4rRjmNhATUhsQwblZsRsnnrc
+B4Az9PeD2eH7XjAQ/O67E7orFG8Dxm5iMnhNd+n3T6BWP9Tb2/2zvTNpkgb9PCYdOhjKINcxf4Y6
+YJQSquoTxdiVl4eTtwWrYjDzzYVIGyg+0zjNrC3NQ27JFUREGGC1Av/04qsY0ad9f1PD6d3IzdmJ
+k9cs4rlh5niMmTIL01PinLK0rHSE3trSTfikBMhIj8P3m5W5NJwrxqfr81AhsgP6Jk3GnDnP4VEp
+c7ReRM7H2agjpytqBM+/wZqPz0CA2NJiQMYfX8eIXu05KjFqvnwCuwv34shPZ3DNofexpIl4/l+m
+Y6BJ+13U7u9djEg0YR91L4sXL/MwzF/4KpJktjHp1eyvFee+244tW3ejWvTFiMeSnsZvZ09HQrir
+LyxxqsRNOsbkBxXWEgdV1D7WZhfjlqAkNgWzUyRtvv2qxr1mzmx2sPqhGvcOdZarJ1CwaQv2VV4X
+94TFDsfLf/ydS1wJB1iuG1rqw6He+0+A+Kn5a71cgjXritB7yh8oRmJl9jWh6JN1KL07DAvmP2vv
+UaKjavIEAbpykVnEssoaB6zl3HU2nP4auTsO42ZECt6YPzFgQ12q7c3dMMe2N/tY6s2LSI+7Wfhp
+iQO97fNotIadHoeZvJ3veluQSgk30Vps2ZiHq/1SkJ4UY99enYlz9lxEKmj/peLW+gqcPFWOmtuU
+0bgttqvFWPxRPiUyQHLaRExKG47elgvYlV2KZreymjbvs96WhjpUV5YiK1OZS/PpfCxebU9kho4Z
+h6eGRKH62E4sn7cOl5x4gtDFEEFJYATCRKe7wWAwiNuRkeHahvgc0Kr3rseuQ5QQ9R6MVOKcOiQG
+Fcd2Y/XCNahy6mUnbPf3CAookemWNA6TxsQD18qxYckK/CirODa9Wvy1onTDIqzOFRKZKKRmTEb6
+mEdFX0ovOLI00Q2NcariOpsf1InGGAe1peuwQkxkYpA+dSKG3iil+TKlKlYwHFaMey2cGXRREVY/
+2OKebsRVuzFvyXoxkXlsFF0PxgzGrZojFFeLcLih7fGK9brBWh/tvPX4tQr9+bH4a+gRjZZrtdj3
+5X40ygy1XS7DlkMXUA26Vjj2s8gTivrMRabfl1XWOGAt525D7Q+b6H6yDScrDXj+n1MDlsg49Sq2
+N2cp54o3+1jrzSlIZYWVH2sc6G2fivlMhxl6ZpjkIPmltzAvvb9Y+Fdb3sZHRRdwsqYJA2mSsLTE
+pMzCEnratF38Gr9/Z5u02+XXevOmuJ3+xgc0OdjRNTHzFTQ3W30OxI7SKziizMWC4vzdor8zaEJ1
+hshqFp7c/DY27C/H1oM1WDiWnrwMsXhp0WIqZ0PhX15BQcsYzKMnL+H27OvSf+p7WPVCNHo5ey5e
+xIQ9H2LpF2dw4mITEmT1pkVH34w/4Z0ZT4in/Lr3Oiz/ohyFJeeROMkeG0x6NfhrOVeI7GOUNZvH
+YcWqWYhxZNy/faEGV++1J6RcH+yeMvkhE6es9woKs8updBTmf/guknrS6uSn8Om/LcNBeT4mk8ey
+qhr3Gjiz6ANY/WCMe+rf2/Gf+aQ6CnNXrUZKL3vlPjehGAvezkPWl98jef5ocS6L1uuGcn3IvDUM
+wpLPN8l2yFZ158for3EQpowyYsOhYpRdnoE0R+/22ZLvROOmpSU75vcwypO5xMxFdo7vq6xxwFqu
+zZKudOuoKs2iB4QjQOhwLP/gtbae7rZiuq6ptjeZNmX7tNebTLSHVe38lOPAP/vY35X24IrCLt2S
+mcRf9XOq6ZtIs8iLahHipc/Aamt1lm2/Yp/auicnE5HPP4PEuBiYzCaEh0tjLu3PYN3TEXoVuViq
+cbyGrDdPxNOy5CHp2QyE7f8UNxvc72RW2Mm1ihOA29+qWUlQfmR6BIaLJ1D4zQn878+30KVLF9xt
+qGcX0K6k0J1jRMYEeyIjHH40JR19KZmpOHwSFkpmhBrUplfd35qfjguqkD57ijOREbaDwmNpsLP9
+olgf7Yt73aPND0BRr+UmLguaaHhxmJDICEtQLMZNiMfBHRfs2378VY57QbA6Zyb1rH6wxr3lOs6K
+TeA6DnyTgzN37deGLtRHKw7FVV+B0Plrf0NG23VDsT6YnJUX0osfu79Dxv8GOLQNRSWVSBPe2rHV
+YB9dc4HhGDHQca3UxM/uj75c5Iw8rDPHgdbrJA3Hf/UBVogqB2P5XymR8Tys4MEo/3eptzcV+3yo
+N0WrWTnLhCjGgT/2hfbDw/7fymWWtq3qkMwIN7EYmGXzLIK62qentkJ7Dmbsn46Zo05Rd+kZFGyk
+/w5bU//1Lcwa21/hjYI2p3xZ018vAxdqYOK3D4mXS1sLN6M3OVFRXUe3mUEuPTDSJCqX8j44fLqA
+es8KhYsf5VKx8TCRIS3/d8cHSfJTTOguD1RbM3V5uy5a9ar5GxxiT+mMXdVCmaE+XE1V3GL3g0Gv
+ozLND0e7xMG9VuHc+7OocWaygtUPKscU91KQmyktratDg8yIoQPiYe1pdvJib78M9SHTw7qqJz8w
++GtIGIHU0G2UwBSjlpIZ0/mDKCNj+05NRS/JaA38hIRWz+u4ZILir4Y4YIoXj8rOYN/RK5g98hGP
+Rzt+pwf7NNUbgwesnEVRDHHgo33ig7jR5P0lIgZXlIqo3QGUznU91jZ87bpf61aQCWl/eBfjZjXh
+Wn0dzh7dg62F5diXuxHJIz6ENPKkVaxq+UDpVeJCx8ShePfx+LvN4li4uW9vl0SmzYe7NODkx0Jv
+qP2PkMiEplD36xxn96u16r/x2oqdfghuQYvwgCx1GXUJx2O0WUH+ifb6rNe7v9INv5U1b/YLnAON
+L34o6XUcu3NbNrmIVAUbJZAOvQH/8c6ZSTWrH1SOKe4lZt3HYslb45RN0Np+JdnKUjUe1YcfWPyl
+F55TnomnyfblKKtqRJ/9B8hWI9JGDmizWfKRSZ7jNOmcNin+rZE898ubU6B0zL2A+/WPtZxTMBA2
+6hW8P6Mr3l9I8602voeEuL86hyllxdhXJRvYz1AsqWgf6RIXLfWmpE2yXY2zXIZkg3yftC4d02Sf
+EVP/YxOmSjIC8KtpArBe+g1B9ueYkGAPF2ubVbzxBRkj0CtuEFJnvI7X0qJItZib+2VCR+n1arQx
+CoNC6ei1gzgpe+y8VLJXfBvIZHTPNe/Zh5munUKdkED7utju2S8w/eKciQxNtcS3+d/6KpHOE+ry
+OkrLapwyGs+WoYK2zMMG2IcCNOtV9ze6n314c9fXB8QhB6dyetK0WKVW17ZXlzXNfqhoNXRFNBW5
+tX+fbPJ1E04d9n+ISdCsGPeiaeqcxWJqf1j9YI17Q6jIBZXf4Uf5TFfRDrf6DeB1Q81tUA+0+NTp
+b7vU4i8ZlTBivPhCwPaNK7H5EA24xY7Hk455RaLNGuWp++lDCWNvDDXTeTdui30/LhJY44C1nEx4
+j2jqATA9gQULhCTYguwlmaj153Kg5IdMr7Cq3t7osztK9uldbz7wc3PJddMn+yy4dO4sTp87j0Z/
+7l2ulrhsud8tXQ66bFBW508s2JrP40BJFVqNIWi9Qm/R0HL820JEVhthae2G4eNHoyd1X53bthSr
+C7siffpY/DLOhObKE8gvEl7JHIwwD7mPKEjhT0fpVTBJdsiM0S8Mw57ccmQtXoHrr05ExNVjyN4h
+8IlCxuj+srLCagT6D6LErqYWGz7OwoRkupHTNWzI+AntXkF2O9F10xGM1afykEnjeKm/DMdPO3Kw
+p1KYgeDfUpa7DDmWlzHIeAVbc4tFYRmjHU+LmvWq+2tKfg6TzKXYdSof81bWYm76UIS0/Izvs7ch
+ir4wPduPL+d6JaHZD6+S7AeC4jBhagzKqN5XvJeFuVOScP2Hv2F7W16oIqD9Yda4t5+pzrm9Bg97
+mP1gjHuaN/Tiqyko21iKtQsXY9JLk9GPPlF/4+oZFO0oxZ0xC7H+ZZovQove1w0P3ins0osfu7+i
+MT0T8UwsUFBzXZxDlJw+0rULXwM/Bef8PBSKfv1p7JkmK/9l5S2MGRBGHUiPY/KkJ+jxhzEOmMvJ
+TBWzS/qca+IsLEg7h7VF5Vi6thhZi8Y5O45lpRlWlfygeyPj/c2pSMk+3euNlbPTOuUVX+yjN5Iz
+V68BvaQcsC//sycz3UOc49OungoZhudUSz4B2Fp/kj5v7TqEUX1oJ3IOCdKiaLIoJTMU85GxifS0
+UYw9X+Vhj6QoNB6z//x7nyZxdZRee0+FOpeYsfPwhiUTH1F3ccHG/3J4HI+5y15Hon1mo0RB/H38
+hdcx7cYn2H7sCLZXHrEfG/o0JTPyySoup7TfoGD83bKXUf/OZpQV5tF/oUg80tMM2FN0hiZu+76Y
+B0RR1/dm7HOImDTvPaRK3xLyQa+6vyZMX/U+IrPX0TyrUmTT6/D2JQZzI+XZL1ucOk5W/tHkB5ve
+gdMWY279h8g+dATZmUK9RuGpUTQBmF61lb62rWyU61HWuJfOUucslVT+ZfWDNe57jpyDVcEmrMnc
+iV1fbG5THhqDaU887Nxmv26w1YdTMOOKXvxY/bWbZcTwKSkoyBRiPh4TnnyknbXs8gLDhWayI2nu
+Msy4uw4FdM3aVUkmmkORQcmMsLDGAWu5IMfVq6usVztx5kJMOruIHnjy8GXZUMxOFrqKtC7KfrC2
+N1b72OuNzQ9Wfqz3Lc32Ob6kLSQzkarzG9l8ci/10KW6G39339nU7D645l4i0Ns2WCy36UuoNljv
+BcFkivCSSOltR0fppcze0kTdb9T3Rf/Ce5p8fHrQysOKxkZ7b4zR5J/Oqu1vY8UOA5Z+thRx5EvT
+PRvN+aC30IRrZLtFP71y0VZLI+gNfhho+NJIyR119AV40d8PS2Oj+GhgECbKeWQXYJd0Es/qB3vc
+E+uGJqpcA4KDDAj3CKfj2q9O2GRiWPyVFVdd1VueqkJNBVjjgLWcJuWdurC+9aY/P//tiwj3fwqJ
+UIWdNJnp1NHFjfNAwJ7MAG9mvgvpzVAPxfguToAT4AQ4AU7ASUCvZIZ9mMmpmq9wAu0J3Gu1z9Bk
+famovQS+hxPgBDgBToAT8I0A75nxjRs/y42ApaEGV24Go08CfYzP7Rjf5AQ4AU6AE+AEPBHQq2fG
+YzLjSSHfxwlwApwAJ8AJcAKcQGck8IvOaBS3iRPgBDgBToAT4AQ4AVYCPJlhJcXLcQKcACfACXAC
+nECnJMCTmU5ZLdwoToAT4AQ4AU6AE2AlwJMZVlK8HCfACXACnAAnwAl0SgI8memU1cKN4gQ4AU6A
+E+AEOAFWAv8PxvdR0yK8jugAAAAASUVORK5CYII=
+--=_MailMate_D2C4B06A-4F8D-4AAB-B247-C507E35AAED6_=--
diff --git a/spec/fixtures/git-cheat-sheet.pdf b/spec/fixtures/git-cheat-sheet.pdf
new file mode 100644
index 00000000000..da38c840fd3
--- /dev/null
+++ b/spec/fixtures/git-cheat-sheet.pdf
@@ -0,0 +1,130423 @@
+%PDF-1.5 %âãÏÓ
+1 0 obj <</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R]/Order 6 0 R/RBGroups[]>>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <</Length 52318/Subtype/XML/Type/Metadata>>stream
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40 ">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xmp="http://ns.adobe.com/xap/1.0/"
+ xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/"
+ xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
+ xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
+ xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
+ xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/"
+ xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+ xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+ xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/"
+ xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
+ <dc:format>application/pdf</dc:format>
+ <dc:title>
+ <rdf:Alt>
+ <rdf:li xml:lang="x-default">Untitled-2</rdf:li>
+ </rdf:Alt>
+ </dc:title>
+ <xmp:MetadataDate>2016-10-31T17:24:20Z</xmp:MetadataDate>
+ <xmp:ModifyDate>2016-10-31T17:24:20Z</xmp:ModifyDate>
+ <xmp:CreateDate>2016-10-31T17:24:20Z</xmp:CreateDate>
+ <xmp:CreatorTool>Adobe Illustrator CC 2015.3 (Macintosh)</xmp:CreatorTool>
+ <xmp:Thumbnails>
+ <rdf:Alt>
+ <rdf:li rdf:parseType="Resource">
+ <xmpGImg:width>256</xmpGImg:width>
+ <xmpGImg:height>92</xmpGImg:height>
+ <xmpGImg:format>JPEG</xmpGImg:format>
+ <xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAXAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8AgedEzdirsVdirsVdirsV&#xA;dirsVdirsVdirsVdir07/nHj/lPn/wCYGb/iceYmt+j4oL6XzUMXYq+aP+ch/wDlPk/5gYf+JyZt&#xA;9F9HxZB5jmWl2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kpr5b0qTU9RMMfAmONpeD9GpQUHv8Vc1H&#xA;berODTmQuyQLHT8U7PsnFCecce8RvSaab5Qnnu54AymgKVcfZB/arQ9Bml1XtF6ImIINgnz8vi7n&#xA;F2LDFxGZ4oyiRHvF9feFfSvy+F9ollqbah6P1u4a3ZBEHSPixTeQSCrkivAL0INd86o596p5M7FV&#xA;sPys1q9kISQRIxRIGljkQyNJbmdWC0P7v4Speu3U4y1ACsLYFWINKg02II+8ZerWKuxVug8cVb4j&#xA;FWuPviq6ib1Py/zpiq0ih9uxxVxBBoRQ+BxVxBHUUxV6b/zjx/ynz/8AMDN/xOPMTW/R8UF9L5qG&#xA;LsVfNH/OQ/8Aynyf8wMP/E5M2+i+j4sgxyXR/LqQS3q3gNm0PGJ6AsZKUCiP7XMbV+/Ocx6zXHIM&#xA;Rj6hKz/Vv+dy4e75eT088WjGI5R1jw150N658Silj5DeK3/3I3cUjLH9ZZ4wQrHj6nBVVuVPipVh&#xA;2zqbn3PMpJqSael9KunSPLZg/uZJQFcigrUDwO1e/gOmWRut1Qwwqu+Gm9MVbFO3H5HFW/TiqKyA&#xA;VPuaD6MValRUYBWDAiuxr/TFUXoTaYuqRHUwrWdJA3PnwDmNhEX9P4+Ik4luO9MjO62VP7688kPd&#xA;28NnbxparJKWndJxt9UjEPqDk7cPrRcuFq3Ed9q1gTrf8b/qVXSb8u5JJU4CGNjCDKwnPwCOL1vR&#xA;ULIQ/IScQ7Ur+0Bgqaof655Jk1tK2yR6SIpmoROHDyOzRo/FizGOOiihAJ/aH2ganXmqOt7r8ufR&#xA;taQRq4a8+si4NySeQk+q0McTjiDwr3G2zb5Eif3KxXXbnT7i9R7CGOCBYIFZYg4Uy+kpmPxkn+9L&#xA;U9sugCBuqBillhkWWJzHIhqjqSCCO4IxnASBjIWCmMiDY5ppfeaNVu7YwO4jEn+9Lxji0ppT46dq&#xA;dhtmr0vYunw5OOIs9L34fd+tzs/aWXLAQJ2H2pro3lbQrvSrW9utTSKSZ40lhEkauvqXQgPwsP2I&#xA;/wB6T4H9nqdhLIQaAcBrRvLGg6jbwTT6ktiJipZ5ZIyqcroQ+kyr+85rF+85cOO4rxG+MshHS1Vp&#xA;PKXl+KJ5W1NX4N8MKSoGaNYebSVZBT94yIFAP7XxGmDxJdyrbXyhoM2lWN3LrEdvPccfVgd0J3SR&#xA;zxC8uNCij4jT3DfDhOQ2RSqWq+W9CsbKSZLwXEot5ZUSOYH94l2LdEIaFK1jb1dt6A7AUOMZknl+&#xA;KVi1R4ZcrYYeH+f34q7kPD9eKt81I3Ar47n+OKr1liAoyBh/LuPxrirvrPx8zGvI03q1dvA1wUrc&#xA;0sTpsDz2oTXYb16s3iMVej/848f8p8//ADAzf8TjzF1v0fFBfS+ahi7FUDe6BoV/N699p1rdTUC+&#xA;rPDHI3EdByZSab5ITkORV4ALzzoQCLbyoQQSDXStwFVift/yup+RGbDxMX86X2skXpcXn6/umig0&#xA;3y5dLbsgvFtU0uWSNWJG6h9ieLU5eGJyYv50vtVlsOg6iLI+tocRvuDcSlpooiL78agyFuPSuVHJ&#xA;G+e3vktqiaFdFk56CiqVPOltohIaq8QPiFRx5V6dsHiD+d/ulSvzRZ6vpuiRT22m6Ja3El9JHG2p&#xA;RaZCWtvTT0wfi9LnzLbK3hkoZIXuTy81tKtJ0/8AMK9QXP6H0G4sXST0prSHTJAzqCFAblx48xRj&#xA;23yZyYukj9qspm0C4ED+joqtcUPp87PRFStDTlSVj1plQyDv+2S26bQbmi+joaE8zy52miD93xNK&#xA;Uc/Fyp9GIyDv+2S2irLy8X1G1Q6HCbdnT12uLTSFXjy+PaB3k+z0p3wHIK5/bJbYnN5Y/N/1X9Py&#xA;3ofp8jw/0aw+zXbvl/iYv50vtW0+g8seZKf6R5cgr6nWO10enp8W7FvtcuH0V9sqOSP87/dLarB5&#xA;X1v1AJ/L0fpfHVo7TRuX2/g2L/ydd+uJyR7/ALZLaAnXS47n0zb2USRLItwkkXl/1FlHHiKeuKAf&#xA;Fyrv0yQvz/2Sst8peV7Cexnl1bRbBg8wawkNrZcntzDGQx+r+pHvKXpRvs0yjLkIOxP2oJSb8wfJ&#xA;2m6To0Nz5X8oabqOoPcrHLBLbc1EJRyz0RojUMqjr3yeHKZH1SICh5/9X88f+W20f/pCf/sozKuH&#xA;88/P9iUXpun+Z57gJf8AkHSbODkoaVNNkmIUmjEKLpKkeFcjKUekz8/2KnMOgIyVl8s2MblmHEaA&#xA;7AKCeLE/pAdcgZ/0v9l+xWpNAYep6XlfT5WUViU6HJGHNDRSxvm4VNBWh648f9I/6b9ipf5m0vzH&#xA;Za9e2ukfl9pN1pkMpW1uHs2ZnTsSVmQfhkscomIJmb96pZ9X88f+W20f/pCf/soydw/nn5/sVOLL&#xA;RtSktfVvPJ+mW8/EEW8ejyy/ETQjkbyMbda5WZi9pH/TfsVEjQjzUf4WsQhZgzfoF6hQGIan6Q3q&#xA;QopXv7YOP+l/sv2Kp65pq6b5fuL6LyppE12t1FDb/WbA2avG0cjyH03uZCSvAb8/HbGMwTRka9/7&#xA;FYedf1QR+ofJnloR/wA/pLx6V6/WvAZbcP55+f7FRFnq2oTTIs/lDy5BFKsnpyi39QF0X4VAF1Ug&#xA;yFVYj7Neh6YCYfzz8/2Knkdz5WeBJ/qOh+jKwjhlGkgo8oJDorfpKhKkEZXx/wBI/wCm/Yqx5dJS&#xA;ykuG0fQqqjtHTSz6TFR8I9Rr9aVagJ47YeMfzj/pv2K9l0ryf5W0i6N3pelW1nclShmhjVG4mhIq&#xA;O22YUsspCiWKcZWrsVdirwjXNAul1e5FzaTiV5nLx2K601rG7woQ8DwWUyceZoQrUH30KUvt7bVb&#xA;azeW2h1WG+mZPVAi8wJVeG5Yrp55EMx/aoN+Na7lUfI2rvqKpO+txaYgUO8K+YGmKghSy8bFQZK7&#xA;0O3vT4iFTuKPQlLK195sJmcKeVrrJ4ANUUK2tAO1a1od/EKGQ39xd2nkvT7jTrA6vbpIPUj1a2vZ&#xA;rwKX2ZoEt5Lgup/mj6AGvfFUpg86+YYI0jh0CKGECfkiaTrqqJOTNGQFsPsvyVmNP5u+KoOT86NP&#xA;0rUI7fzK+n6cio0t4rWmqxzJGwb0nEc9pHszLT4qe2NKiX/5yD/KgMETVIpJCQBGtpdBtzQfaiX5&#xA;40qF8i+dPy71XzvFb6J5l1DUNRufVeOwne7Nsw4NI3FJ4lVeAU8dx9OKo/zBpVxZeckuW1PldSM1&#xA;/FblY4UEUbqqK0srxxBq+FSaE065mQlcOXkyZnovm6y1O8hsDE0F/La/XPT5Ryx8FZEcLLEzKSrS&#xA;LsaHvTMaWIgWilC/8+aDa313YSB5JbMhLgfulWrKG4r6rxl9m/ZB8MlHDIgFaebaF5D1DW3m+oam&#xA;sENtN9Xu0kDerFQ81UBB6cv7p1NQ4FajsDmVLMI8wm3r2gaSNI0e2071jcG3UhpmHEuxYsxpU03P&#xA;jmDOXEbYoPzdprahpscK6PY63SUMbTUnEcK/A6+oCYbj4qtx+z9kt/qmKvIPMcOm3bahbw+ULTT5&#xA;7W4YTT2GnzXAuHWQq/rPcaDdoVX7YeIPU0+LcVKpK+nB40ki0+QvGIx8OlAgFCQFc/4UYoEptsKe&#xA;2Kr7eKe0vFubGykVopne0uzpwhuatUNKgj8rfCSo5Eqx8K4VZr5Z1jUZL60j1XV9aju3uljWyk04&#xA;m2kBI4pJPJomnsgb9rcUH7XfAqP88eRNQ1vU5JbXQtIQ81Mt88tn61ynKtJhdaVqFOA3AU/tHfFU&#xA;j0u81yyefS9V1DWdNWz4wWn6OsEvYeK/DxEi6BBAoQBQOBdd+u2Ko+TV0NJh5i8zrD8VVXQq+4rX&#xA;RyQBy/V71VWpqqLGS3mbzS2+/LQ0DCgpTiNGB3+WKqPmvTYfMXkE2v6Uv7tl1OKRJtSs47SfkkLM&#xA;I442OgLQjdX5HfxA+FVhWh/lj5ZgnuJfMGk3moUVPQNm9nZRKIgrSQy+tq96snIso/Z35bnqDap/&#xA;pn5d/k/canwm0e905VgDstzqFm1uWJEKqyw3U5aQ8OVaUruTypgtUw/5UV+S6IFKXIWImgbUHIUk&#xA;VOxelaLX/M42qvB+Sf5PSTJbxxSzzN6hWL62HY8l9MlVrUce3HoaeAxtL2HAh2KuxV2KvHdd0nyH&#xA;Jcwg6Je+X+cUipBFpejUcJGtTS6guX+MBYuoX4QNhuSqNMUsnl9X0DzfdW10I1FpaXiaMsMVCP3U&#xA;gt7NuIRPh4r7YqkjW35ncyq+eLLlRgB/oH2qgrVfqJP2evxYpWJb/mkIiW882BdWpJtY0ANK7/Ut&#xA;iN6AjwxVl8cepN5O01dYudT1TUBcP6l55ecKXImNOZhW1T01X4DVex3J3xQhr63c6XCtvD5u9ZQ4&#xA;i/enmH9STi059SrKp9zVOPXFUiu/J35lW0T3VxrqQASIfXmvtSVFBYqF3dVHIuB89sbSm4178wVY&#xA;iXVPLqgDl/eXNQBQt1cdjWvyxVNPLer+cJ9es7fUL3RpbRg5nis5Z2uDRW4mNXJWlePKvauKGVar&#xA;omiaqySXtr6s0SlY5CsqkAnoSnAkV7VycZyjyVj2gaBdWDJepCtprJgmgiSVZJIQryIxDBZJOnpI&#xA;w4v/ADA9qWZJg+5JR19odnqFnKKSQ6veRuHvIVmiSOZAIzMIeciVRqEKeXIjc98iJke5VtnbapoC&#xA;tMFjvLaWQPdRWVlcfWGPEIGMk13KTxoo+ydh26gkiX9v7FT3SdT/AEjbNP8AVZ7Ti5T07lPTc0AP&#xA;IDfbfKpRooY/+Zmt+U9H0GC58z6c2p6e90sUcCRxykStFIQ3GRoxTgrKd++Tw4jM0FDzN/zI/Ih2&#xA;kZvJ7kzNzm/0GzAdg3Lk49ajGvc5kfkZ94TSIPmT8pdcsmj0jy+ml3Z4Rw6g1npCtEIHSQKFuZeF&#xA;CKBaj3G67RlpJjr9/wCpaVY9Rh0vT47bT9cnsrOKqR29pD5eVUPEyMRGs3EcjVthux8TkRpyfwf1&#xA;JpGReaTZ3Md5c69e3NpAY5ZrbhobLKnQopgn9T4qfskkVxGml+L/AFLSH/M/ynLHql5rM+n2slvd&#xA;TwrbN6FpcuXCsaFF0PU7hd058ndviOxGwzHDFh1zYaK9k1xHatNfMT+kA+kQbAtsoY+Wecpb4SQU&#xA;X59yVZpaazo8EEdpZeZ9fggtkWKGCPRYUiWNSI1VFGjqvFduPHantgVVHmLT/TD/AOLPMRUP9oaP&#xA;HU96UGj9Nuo+/FUJ5uuvLmo/l4zapLrvmOxTVYwD9USzvEcQMQvpmytVaLqORj3LU5dsQrzaO08k&#xA;RwzKfKHmQxxyEyoTBJIXBJDhfqxqaAitenjhSmehp+X+nXrahB5K13UWgVDNYX8EE0Mvq8Y6uhgo&#xA;xRpCakj4h74qn8nn78v1VWl/KUlpfiqNMt3AYV5F6Q8tj3oa9saQibX8yPKVleLcW35VywXFrIzw&#xA;3Ftp8QeOQBWd1cQrxJkk4hgd9ySMFK95wK7FXYq7FXz/AOYvMHlvVLyzWw8yPrvoBnYajCEkVUQc&#xA;lAGh33OPghZt13+LwIyfyuQdPu/Wmk5S5DFZG8oaU8n2kItNVGw+JyG/Qrd9/p9t4eDLy+Y/Wrf1&#xA;qMstfJulcAfSX/RtVJ5V3Qf7gx49MfBl5fMfrVtpQrIqeS9L/lCG11UGnGhVf9wZ6Db5D7h4R8vm&#xA;P1qiPMHmLXrDyhp50C90zyrcRXcsF9bTlLWBSQrokY1G1s2qwkVzSIfa6nrlmKMIn1/j5KxRvPP5&#xA;oojSP550FUVzGzm70oAOOqk8Ovtl96fu+9dkwtPOOp3tnPaeZPNmmahGq1ubeG+0L0gUk6yJNbzb&#xA;K3HqNmyuRxdB9/612RGm6ZZa2Gm0b6lqccLenPJbP5duOKcCY0LR2LcTyNd+1cF4/L/ZfrXZkPl/&#xA;yvqdnrVhdRWaQyxSr605j0gUjIKOFNtZwzAmM8fhcfdtkZHHR/b+tXouqTXUOnXEtqjSXCIWiRQG&#xA;YkeCkivyyiIF7oYNbz+cdfkbSdYtJ7Ozm3W9jg9MxslXWQMz/CwZRTr92ZE4wAsHdKLk8p+QNNu4&#xA;YLq/itbmPi8UJmt7Sb4vhVleFYZxUr1VhWmR8Schy+9bS6P8wZLU3t39QDTrK0dta3GrRhpI6rQ+&#xA;mxaKJh4PxYd9ya2HBdC/sTTP9Jvv0hpVnf8ApmH63BFP6RIYp6qB+PJdjStKjMWQokMWO/mbqdjp&#xA;nlsXt7PfW8Mc6qG01bZpmaRHQLS7DRU+Ku/cDAFeAj81vMckdVttRSd3L0W+0srQ0PFVbTHoqsvw&#xA;+zbk5KyqY6R+c/mGCWV20CbUfUarQXd9ZKIIkrvG0Nlb9eVKtyrQbim7Z71R5/PzUeE1fJ0cckXV&#xA;WvomHwmjD4IWJY0Ow6dyemNnvSm/lD839Q1vzDpul3PleO1iv5vSN2LqNuA6giP0uTHf8Ovg2e9W&#xA;U+dNM8r3GqPING1FNTgnhnn1C001roTBHLBAzKyN8W5I3GRQwXQdE/NPSWvNP0WWz0vT4bkusl7C&#xA;1sk4BK8t7DhydI1rxY7DbClP/wBG/ngyFk1XSynMEN6g6cR8Ff0fTf7Xjv4YNlbfSPzxiKt+l9OY&#xA;KfjErKoNAxP2dPBFVo3Xanhjsqtr2n+bY/y7Ca/qmkfpVdQ9Vb7UhavZiEQuKKZ7WKMPTlv6daV+&#xA;Km2W4ZRErkrA49O1oSGFPMHk0yqfTMfDSSwJ+HjT6tWvXbM38xj/AKXzP602zzy55W8/aRG7R3Vn&#xA;aXEyqsx06LTbVWKuxqSLBiw4EUB6Gu++1M8uOX7b/WhkCQee+fx6vdFO4Eumg0p4/o898r4sfd9/&#xA;61Up7f8AMU2kwt9YmW7Ik9B5JNOaMGp9LmosFZhSnKjD2xEsd8vv/Wr0PMZDsVdirsVeJalpug6b&#xA;DBc2N75av7l5PTS2sLO0juJAyIf3TXWp28dQGqfiPwuPh75d48+8ptT0fzNpduZZNW02ymjkI9EW&#xA;zaNZiMoP3jOz61c+pVZEHw0409xg8WXetskhntJ4Uli8kXcluwEsMqSaOyMGHIMhXUTWtdjg8WXe&#xA;tuX6uCgHkG9XckfFo9AevbUcPiy71tMNasJpPKCJY6HdwPPK01xBa366dcWzBOHqyy2twFk4rvwE&#xA;pBoPDaBkTzQ8Lk1a9IedPO96xqwEQ1lVBFD8dPru2/QbHptilM9NudNuYrgah+ZV3pjRSA25bVZZ&#xA;C8KrTkOF2djt9qreO++KExaPRFjEkn5szhCjK4N5cqXG7niBdhg3E7cN6Upilk35c2umR+bbR7b8&#xA;wJ/MDgNx0uS7mmVxJCzgsrzyqSq/EPh7eOAoZ9qXnDUodSaztNFupoNlGomC5MaMdiWjEIZgp3+A&#xA;/EOh8LY4gRdhNIGDytrlk1uLGeadBamGedp47P1TJGAeSi3lmSjKGDci6+P2uUjkBG/P8ea2g7Hz&#xA;Jrk5sbWGMypDfCGaLjLPzj9c27GS5kYl/S5eqaKPsUqBXJGEefkqdaz5e1mfy+tnatbi9W7+t/u0&#xA;9KLkZjcc+LGQFxN+8B/m3675XCUeLfkqd6HBqEGlW0Woyepeqv79+RcFq/zHK5kE7ckJN+YOuWOj&#xA;aLDdXusXOiRPcLEt3aQxzuzNHIeBWWKdaUUtXj1A7VBljxmZoK8gvvzY1MXc6WXmmV7IyMYZpZIk&#xA;m9PfiPTGhui9ehdv9brW/wDJ5O5NIjSfzp1a2Mhnv4NSeZIVihuruZfTdQwcq1tosNTIeJNaiv2e&#xA;I2I/J5O5aZda/mB54uU9SLy+rR8nQMs2pMOUZKkbaVT7S0/zplZwkdR8wtICPV7aTzNFrWveULO3&#xA;uLb45tdEFzPewelHswaXTYG4qXVamUUBr7Y+Ae8fNaRHnfzBqtnrt9Hp/mu0s7aKISXEFzqtvaSw&#xA;FS7PxgfStQPHiuxMldtgN8qQstrzzH5p0WKOTTfLfmPTrdkVJLrVp7oGaNeLO9dI4CXg5rRRSp27&#xA;YqsPkiZ6rJ5F8ntDQUT6wx7VpvpFPtE9sVQOqaBqlq0Yh/LPy1fuqmQSQXcXBWDEhQZdNiblWjV4&#xA;0xVO7K0EfkS+jmsdM/L4zXlXUTmOF1jQOkpmsX0t1duHxAPuilTVTiqR/o+wadoYvzF0lpIixeP9&#xA;JauXRIwSeQGuVPED4q07nFUXbNo02oSWVmdU12/tYoZbiXS/MTehIWRZfUjgl1hZkj5txAkUV/yl&#xA;3xVX/RxO58u+ahSo/wCO+f2jQ7DWd+lf1Yqr2OmNHe2oGgeZI1+sJIZ59cEsMZ5A85UOryl41pUo&#xA;Eav8prir1PArsVdirsVeA+avNOozXMdobaBPqlxJ6N1pUFxEJHEKiN0lhuEDIeQff9mnSnLJJSzS&#xA;9Y0VxN+nYdRf1aywmya4QBaK78/UuJO6pTj8j1xVk0d9prxqILHzc0Kf3TRyXQjKqoZShEwDKwPw&#xA;02OBVRr+0KN/oHnAAIAKPdVIBUbfvup7nr1OKsm/x9YeWtI0uKa0vZlvFkMP1uQLcrxZiRObhg1a&#xA;DarHbLsWEzWkfpX5k2+ppE8GnTLHLJLGJJGQIDC5RyziqgVHWuGWnrqtJnqV5bahaS2Go2Ftc2s6&#xA;/vraa4hZHVSD8StsR0yvg/FISNvKn5fgFW8n6EAoqQYrDYHf+TvjwKmeieWfLdtenU9L8t6ZYX0V&#xA;YhdQJDHJRlBNHhi7g06+2AgBWKW35xeZp7qSA+S7iBY+RE0006o3E0242jbntmUdLGvq/HzTSM17&#xA;zXrd/pvpnTUT4uYS0vb2OYlQaLybTuABPi1MjDEAef2D/ilpfovmfWNMsI7OHToZI6syme7vmdeX&#xA;xEEjTB+1+PtjPGCbv7B/xSohvzD18NGBoluecrRf703vwhQx9Rj+jqcDx277jbI+BHv+79a0yPyv&#xA;rt5rFtdy3Vl9Se2uTbpxeR0lURxyeojSxW78ayFfsdRlWSAieaGI/n3fyWHkiK7js1v3jvYwLZo4&#xA;pK8opVLD1op1XgG5149vCoIhIg7FWJ6D5KurrV4ZNS0U2tm9JJY/qWivaAU5emssUklyQfs8uHXf&#xA;YZPx595TbN/8EeTf+rHYf9I8X/NODx595VWHlPywOmk2g3r/AHKdfuweNPvK22vlPyvy/wCOTabk&#xA;V/cx7/hj40+8raU+er6zupL/AE27876XpEUZRzA4mt7uBgxK/v7W+s5d+NKDrvWvTIIVNP8AJfkX&#xA;VLWSSy1u8v4iTBcy2ut6s8ZkU1dW4XjUYFtwTXfAqhq/lDTLKaNLW28wapDMHMr2mu3yCNif2kud&#xA;St615E1WuKpauhwL6qJ5d81Kk1WNNeZakksems/DU/y+OFU3ZY9M8iau82mapDbrKzSJqGr+pchF&#xA;QcZY7ua6vPSVmAXj6g77b7qvPZ/PnlD64YDeaoRHIsfI+YkEZVeVHryIDKVGzEHeu5GGkplpnmX8&#xA;ub2znM3mO90iaItHCH1p5AUHCk37tgoBc7qGFab4FS9pNJc/vPzVkoTUcJp0pzAaikXPcL8INfbq&#xA;cKpp5auNFfXNNMP5iNqZ9eF4rAzzfvxI4KwvyuGBLl1WjL7UwK91wIdirsVdirzDVbDU9Ikt5rry&#xA;noUyv6p52Nhd3jqwiWm1vayFOT9z+yKCpwq87n8o6g7zyQ2EkIcFz/uJ1NqepIrHiraU/wAXEd69&#xA;BXClk+hR3dvpwg1C68xpJbsDFBYadqPo+kCAsYMunxkcaU4rRePTvgVNpfqcvOU6r5qACxq0a6Xq&#xA;CV4KE+EDTlNW4gsR332qaqGvM3krWPMlroTaXPPIun/vJ5r0yWN46OXX4leBGVjxIPKIbZlafKIA&#xA;31SCq6Z+XGtw2Ho3EcSyetcSji/qALNM0gHLlDUhSP2euSlnjabRn+AdVFSFQmROEpapNaEAqfWF&#xA;Ou+Dxwtpbcw6N5d1OH/EnqLEKMgEMrpKUowXmWkjYLQVXJAmY9Ksj0T8xvJMkrWljLIrSuzQWkNr&#xA;OzUSMV4xxRtX7BNFynJhmBZRSYaFrnmjU9EstRWwsXF1Cspb63NEPiFT8H1eXjvX9o5GcIiRFn5f&#xA;tVCXy+a79r11i+oMbZY7WeymacErIrlv3sdtuQGWgB2777GPCPmqEsdP/MCyE076jLdzLQrbSwxt&#xA;EyMaUDCTkrildgae+SkYGtqVkuq6tq0CRnStKbU3JImQzR2xjoKivq7mvsMqjEdTSozT9RhvoXki&#xA;WRDFI8MqSo0bB424sPiAqPBhsRuDkCKQwz86V8xv5Qji0C0lvLqS7iWaOGN5WEIR2LcEDVHIKNwc&#xA;QrzXSPL/AOf2r2Mtwtx9TLK8YjuDPaOHI5AiOQhlAErKCu2y/wAu52SmD+U/+chTcSulxaiFm5CB&#xA;rpyN52lAV1IZfh4oe1OgwbK6Tyh/zkGwHG9t1DxxRsBck8CjqWcV4lmKp4gGprjsqK0Hyl+ecfmf&#xA;TptUvUOkRXET3SrcK1YUcB0YGvqF4+9BvXpjsr0PzDoHnTU0vLa11ixtLK5BSMfU7tbhAT/y0W99&#xA;bOGp3Tif1YEMWl/Lq80xQLhNZ19pS/ptpur6jaiFI1HBZRe6wOTMT8LR7VFSF2OFUJc+XZFiUSeW&#xA;PODjkoTjr7u3KnIH4daNOJNKnb8MVXHy08UI4eWfNzx1UIo19yRtRSA2sgr9r2p1NKVxVkWnaNr/&#xA;APhnUItGj1TQ9Ua4MkSapfR3Ukh4AErPM2tpHFQ1VVX7S/ZArUKlf6J/OgCZRe1aRWEbtqNmRGxN&#xA;QwA0RSaDYcq+/jhVMdMX82rOFYrm1tNS5MGNxc6oElQMBVeNvpcEbBd6GlTiqtdan+aFvZzXP6As&#xA;pTCjuYItUleRuHI0RRYfEWoOI679MVYfpk91feadNmn/AC2t7W5nuEnn1t4JRLbyvJyeX1G09GLg&#xA;sW5cwCerDrir2fArsVdirsVS7VNNZ9OuF02G2jvyhFs8sYKB+xYAHJxlvvyV5RbXX5rCVo7nTnZe&#xA;QHqNZxKqb0r+5iLMN+wOZpGLv+1ls3cnWXsJ5b9bpEiDl2igvo02FOLN6UdCW/2I/DCKvb9CpJGn&#xA;p3kR0lLs3fJmVLGSO9LAUr6iJLP08CN8s6b/AKkvQfKtp+ZD3tvcXc0cGkSqDcwXEMMNwRuDRYEV&#xA;lbw5N/TMTIcdbc2JpNU0TztFcJEmqQyWS8VNxMZWuCoABZlUpHyPXamQE4Vy3VkkFoVhjWWR5JVU&#xA;CSQM6hmA3YLyNKntXKSUL/q0fi//ACMf/mrG1Y/rvl/V2vFv9EFgLugVmvUn9ToQXE8UnL7NAF4/&#xA;TlsMgqpWm0qS21rR7GNNRtrjUZioiSw0p5xaxRqSOQY0YuQR8LGm22TsSOxr30qiPNGpX7y2Gl6T&#xA;fJqDQOsD3lzNDFzRSalgG+Pw6AnaoG+E4wNyRS0ym70e7nsCLe8mtL1kX4vVkdA6g9ix2JO9D2yk&#xA;TAPLZDHGtvM9netdamNQubSAkhdN4lpafCqspnaQoeXKiqDUdRuDbcSKFfH+xLJfL0U6w3U8ttJa&#xA;/Wrhpo4ZuJlCFEQFyry7nhXc1HSmUzPJD490nWL/AEm4a4sjEJXQxN60MNwvFiCfgmSRe3Wlc3so&#xA;iXNmmI876+IDCfqbIS5+KwsWI9RmdqEw1A5OT7dsj4UfP5lWx568wiN4x9SCyVD006wBNdzuIK4P&#xA;Bj5/Mqtt/O2vW8EUEX1MJDGsSFtPsXcogoOTvCzMadyanvhOKJ7/AJlV0/nnzBMgRxZBQhjXjp9i&#xA;rKrCh4sIOSn3BriMUfP5lWP5YrsVdirsVdirsVdirsVRFlePaTiZI4pSBThPGsqf8C4IwEWr0b/n&#xA;Hj/lPn/5gZv+Jx5i636PigvpfNQxdir5o/5yH/5T5P8AmBh/4nJm30X0fFkGHv5212ScTSLYu3D0&#xA;iradYFChKndPQ4k/AtDSoy/wh5/MpWr5w1dZPUWDTg3Uf7i9OoOnQfV6fs4+EPP5n9auXzlrS3M9&#xA;xwsTLcBPV5adYMtYyxVghg4K3xmrAVPfoMfCHn8yqp/jnXeMg9PT/wB7/ef7jNO3+f7jB4MfP5n9&#xA;apRf39xfXBuJ1iVyAvGCGK3jAHhHCsaD6BlgFKhsKuxV2KuxV2KuxV2KuxV2Ksq/5Bb/ANrz/p0y&#xA;n95/R+1Xf8gt/wC15/06Y/vP6P2q7/kFv/a8/wCnTH95/R+1Xf8AILf+15/06Y/vP6P2q7/kFv8A&#xA;2vP+nTH95/R+1Xf8gt/7Xn/Tpj+8/o/arv8AkFv/AGvP+nTH95/R+1Xf8gt/7Xn/AE6Y/vP6P2q7&#xA;/kFv/a8/6dMf3n9H7Vd/yC3/ALXn/Tpj+8/o/arv+QW/9rz/AKdMf3n9H7Vd/wAgt/7Xn/Tpj+8/&#xA;o/arv+QW/wDa8/6dMf3n9H7Vd/yC3/tef9OmP7z+j9qvQPyR/wAE/wCM3/Qv6S+ufVJa/XPQ9Lhy&#xA;Sv8Ad/FXpmNq+Pg3pBe95rGLsVeHfnL/AMq1/wAXr/iP9M/pD6rFT9HfVfR9Pk/H+++LlWtc2Ol8&#xA;Tg9PDXnbIMF/5Af/AN/N/wBy/Mn99/R+1d3f8gP/AO/m/wC5fj++/o/au7v+QH/9/N/3L8f339H7&#xA;V3d/yA//AL+b/uX4/vv6P2ru7/kB/wD383/cvx/ff0ftXd3/ACA//v5v+5fj++/o/au7v+QH/wDf&#xA;zf8Acvx/ff0ftXd3/ID/APv5v+5fj++/o/au7v8AkB//AH83/cvx/ff0ftXd3/ID/wDv5v8AuX4/&#xA;vv6P2ru7/kB//fzf9y/H99/R+1d3f8gP/wC/m/7l+P77+j9q7u/5Af8A9/N/3L8f339H7V3d/wAg&#xA;P/7+b/uX4/vv6P2ru//Z</xmpGImg:image>
+ </rdf:li>
+ </rdf:Alt>
+ </xmp:Thumbnails>
+ <xmpMM:InstanceID>uuid:9ceda8ff-23f0-3d4d-bee2-d129ed5be150</xmpMM:InstanceID>
+ <xmpMM:DocumentID>xmp.did:6e0ad609-63ba-4487-8c26-1d3d45c1e92e</xmpMM:DocumentID>
+ <xmpMM:OriginalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</xmpMM:OriginalDocumentID>
+ <xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
+ <xmpMM:DerivedFrom rdf:parseType="Resource">
+ <stRef:instanceID>uuid:1429a63f-381b-4bbe-b610-dcd92ca51b5b</stRef:instanceID>
+ <stRef:documentID>xmp.did:d92220c8-81e6-9646-85ef-ad1bfcf188fa</stRef:documentID>
+ <stRef:originalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</stRef:originalDocumentID>
+ <stRef:renditionClass>proof:pdf</stRef:renditionClass>
+ </xmpMM:DerivedFrom>
+ <xmpMM:History>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:6e0ad609-63ba-4487-8c26-1d3d45c1e92e</stEvt:instanceID>
+ <stEvt:when>2016-10-31T17:23:56Z</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CC 2015.3 (Macintosh)</stEvt:softwareAgent>
+ <stEvt:changed>/</stEvt:changed>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpMM:History>
+ <illustrator:StartupProfile>Print</illustrator:StartupProfile>
+ <xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint>
+ <xmpTPg:HasVisibleTransparency>True</xmpTPg:HasVisibleTransparency>
+ <xmpTPg:NPages>1</xmpTPg:NPages>
+ <xmpTPg:MaxPageSize rdf:parseType="Resource">
+ <stDim:w>11.000000</stDim:w>
+ <stDim:h>8.500000</stDim:h>
+ <stDim:unit>Inches</stDim:unit>
+ </xmpTPg:MaxPageSize>
+ <xmpTPg:PlateNames>
+ <rdf:Seq>
+ <rdf:li>Cyan</rdf:li>
+ <rdf:li>Magenta</rdf:li>
+ <rdf:li>Yellow</rdf:li>
+ <rdf:li>Black</rdf:li>
+ </rdf:Seq>
+ </xmpTPg:PlateNames>
+ <xmpTPg:SwatchGroups>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Default Swatch Group</xmpG:groupName>
+ <xmpG:groupType>0</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>White</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Black</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>100.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Red</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Yellow</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Green</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Cyan</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Blue</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Magenta</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=15 M=100 Y=90 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>15.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>90.000000</xmpG:yellow>
+ <xmpG:black>10.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=90 Y=85 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>90.000000</xmpG:magenta>
+ <xmpG:yellow>85.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=80 Y=95 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>80.000000</xmpG:magenta>
+ <xmpG:yellow>95.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=50 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>50.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=35 Y=85 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>35.000000</xmpG:magenta>
+ <xmpG:yellow>85.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=5 M=0 Y=90 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>5.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>90.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=20 M=0 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>20.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=50 M=0 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>50.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=75 M=0 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>75.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=85 M=10 Y=100 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>85.000000</xmpG:cyan>
+ <xmpG:magenta>10.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>10.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=90 M=30 Y=95 K=30</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>90.000000</xmpG:cyan>
+ <xmpG:magenta>30.000000</xmpG:magenta>
+ <xmpG:yellow>95.000000</xmpG:yellow>
+ <xmpG:black>30.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=75 M=0 Y=75 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>75.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>75.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=80 M=10 Y=45 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>80.000000</xmpG:cyan>
+ <xmpG:magenta>10.000000</xmpG:magenta>
+ <xmpG:yellow>45.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=70 M=15 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>70.000000</xmpG:cyan>
+ <xmpG:magenta>15.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=85 M=50 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>85.000000</xmpG:cyan>
+ <xmpG:magenta>50.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=100 M=95 Y=5 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>95.000000</xmpG:magenta>
+ <xmpG:yellow>5.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=100 M=100 Y=25 K=25</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>25.000000</xmpG:yellow>
+ <xmpG:black>25.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=75 M=100 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>75.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=50 M=100 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>50.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=35 M=100 Y=35 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>35.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>35.000000</xmpG:yellow>
+ <xmpG:black>10.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=10 M=100 Y=50 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>10.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>50.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=95 Y=20 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>95.000000</xmpG:magenta>
+ <xmpG:yellow>20.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=25 M=25 Y=40 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>25.000000</xmpG:cyan>
+ <xmpG:magenta>25.000000</xmpG:magenta>
+ <xmpG:yellow>40.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=40 M=45 Y=50 K=5</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>40.000000</xmpG:cyan>
+ <xmpG:magenta>45.000000</xmpG:magenta>
+ <xmpG:yellow>50.000000</xmpG:yellow>
+ <xmpG:black>5.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=50 M=50 Y=60 K=25</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>50.000000</xmpG:cyan>
+ <xmpG:magenta>50.000000</xmpG:magenta>
+ <xmpG:yellow>60.000000</xmpG:yellow>
+ <xmpG:black>25.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=55 M=60 Y=65 K=40</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>55.000000</xmpG:cyan>
+ <xmpG:magenta>60.000000</xmpG:magenta>
+ <xmpG:yellow>65.000000</xmpG:yellow>
+ <xmpG:black>40.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=25 M=40 Y=65 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>25.000000</xmpG:cyan>
+ <xmpG:magenta>40.000000</xmpG:magenta>
+ <xmpG:yellow>65.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=30 M=50 Y=75 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>30.000000</xmpG:cyan>
+ <xmpG:magenta>50.000000</xmpG:magenta>
+ <xmpG:yellow>75.000000</xmpG:yellow>
+ <xmpG:black>10.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=35 M=60 Y=80 K=25</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>35.000000</xmpG:cyan>
+ <xmpG:magenta>60.000000</xmpG:magenta>
+ <xmpG:yellow>80.000000</xmpG:yellow>
+ <xmpG:black>25.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=40 M=65 Y=90 K=35</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>40.000000</xmpG:cyan>
+ <xmpG:magenta>65.000000</xmpG:magenta>
+ <xmpG:yellow>90.000000</xmpG:yellow>
+ <xmpG:black>35.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=40 M=70 Y=100 K=50</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>40.000000</xmpG:cyan>
+ <xmpG:magenta>70.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>50.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=50 M=70 Y=80 K=70</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>50.000000</xmpG:cyan>
+ <xmpG:magenta>70.000000</xmpG:magenta>
+ <xmpG:yellow>80.000000</xmpG:yellow>
+ <xmpG:black>70.000000</xmpG:black>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Grays</xmpG:groupName>
+ <xmpG:groupType>1</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=100</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>100.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=90</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>89.999400</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=80</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>79.998800</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=70</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>69.999700</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=60</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>59.999100</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=50</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>50.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=40</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>39.999400</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=30</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>29.998800</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=20</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>19.999700</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>9.999100</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=5</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>4.998800</xmpG:black>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Brights</xmpG:groupName>
+ <xmpG:groupType>1</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=100 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=75 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>75.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=10 Y=95 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>10.000000</xmpG:magenta>
+ <xmpG:yellow>95.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=85 M=10 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>85.000000</xmpG:cyan>
+ <xmpG:magenta>10.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=100 M=90 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>90.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=60 M=90 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>60.000000</xmpG:cyan>
+ <xmpG:magenta>90.000000</xmpG:magenta>
+ <xmpG:yellow>0.003100</xmpG:yellow>
+ <xmpG:black>0.003100</xmpG:black>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpTPg:SwatchGroups>
+ <pdf:Producer>Adobe PDF library 15.00</pdf:Producer>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end="w"?> endstream endobj 3 0 obj <</Count 2/Kids[7 0 R 8 0 R]/Type/Pages>> endobj 7 0 obj <</ArtBox[9.0 9.0 801.0 621.0]/BleedBox[0.0 0.0 810.0 630.0]/Contents 9 0 R/Group 10 0 R/LastModified(D:20161031172420Z)/MediaBox[0.0 0.0 810.0 630.0]/Parent 3 0 R/PieceInfo<</Illustrator 11 0 R>>/Resources<</ExtGState<</GS0 12 0 R>>/Properties<</MC0 5 0 R>>/XObject<</Fm0 13 0 R>>>>/Thumb 14 0 R/TrimBox[9.0 9.0 801.0 621.0]/Type/Page>> endobj 8 0 obj <</ArtBox[9.0 9.0 801.0 621.0]/BleedBox[0.0 0.0 810.0 630.0]/Contents 15 0 R/Group 16 0 R/LastModified(D:20161031172420Z)/MediaBox[0.0 0.0 810.0 630.0]/Parent 3 0 R/PieceInfo<</Illustrator 11 0 R>>/Resources<</ExtGState<</GS0 12 0 R>>/Properties<</MC0 5 0 R>>/XObject<</Fm0 17 0 R>>>>/Thumb 18 0 R/TrimBox[9.0 9.0 801.0 621.0]/Type/Page>> endobj 15 0 obj <</Filter/FlateDecode/Length 74>>stream
+H‰Ò÷wVÐ÷u6PprqVà*ä2P036P°04PÐ1ŠR¹Âò€âúîÁ
+éÅ@ù}·\—|®@.W_ ¦@.€
+8;Z,'9pXNV$mt_*&*&q$Q^LQqM6=FYXa>kGj/ck9Fc%GnKFj5?R_77h@jVCad\dU!
+!@o_jlu@L%GWqGb9DU0#r%F![m'CIIL+eng%_hma##WkB(D#8`gtokLpXBs3)T[]P
+aS.3->!%A_T&_MJ,.&D(*L5Edr1-s#*Od-"j0Mi3,pO$DiV>V\g<)I+Z1a:?3'!"[
+,E099(b'^6ZSm'GOo6<8kT`ZX*hT,FC^<44q*l4X_u'_.`Qg#05&SD*+".%fD61.H
+R3,5+c1C>:Q`[@B;L74hfr_AP(eMi`mgk?e"PoS0\N%H-D5T74Kt?Ne'YAX&%haHu
+P_QqYYRpg=_9lFk2c2P&U81d_M.nn`,7i-VSHhbY]-AD+,^:i2@Ps=TT$<4M+hb&0
+1ad[Z.E!U&()-Sl?c^U4S*59e(go7lo&#+bq6I3pA,6YCMQYQ$jp8M?hGn2Rf@qKE
+O;X+!e@p\Dq"3iaqO,*W76BMFBCbG<5>kBq[[**iM@5@RdPoGr2R<Ka1(MP&.5G@s
+CjsQb`[@+3'5Ma&:e/^ns)nF7iJOBbYS5QMN8`V<Z^8>.!%X(VM'%%CXt5D=K88^U
+A5PjHofbFW8*?AjCs7^-AA;:^@)W]T#"r6%;SJgZ^htOc6TX`OFo,Lq:^s9]_c?9m
+oR%+FcUm#3ChlpHATYp8hgauV=NRL]JA<tDnj)@OSSo.(PX$L'M54K;L%if51>$rp
+:=oT7;hT@-Q0a#TENIMp$k$ohE)'FaP;p*G$(`5WN]D-E9o,XIAQ!_t]*a?=$],97
+]8g$nlRo3>0LXRJoJ_.mZ(d?t'dU$&'Lsd\loEhPFS5T0'Aju;@L`r:P,Vt.q<\'n
+[<6!\'mG%DPnK(i3iT/pf(<W,DWNZ%IT0K.Oe@i$`[>"]bKqF2Djj_$I]22]I.HH;
+&gn4GDaA%SD4eK()fLpnaiTm!ol;7#Z8:7IVIpulM7Ol(0^A&$=<Tar\`CKE(3a5=
+Z^c<E>oQ?KL:YTRZFFH@4KR"af:8PTLqe7[Gq8(QF<=YlR)Fe:0XaS'<[RW*5LDJ,
+B>MXR==NO+:52b`%n08C`ot099)<O:-n3BMGWXDnl.K3%)bR?Rq\:ZiZq?8T/R1W]
+0*]a#[b`XKMUS3P#)$X9C[nFM-]M?si(b^ZWj]5LoGFjkq;rMWE*=36_=ES@Oqd&k
+bHXok9NT;OH">-<<4'5B+G:Mj-r6@8Bc#iW8s>7%Kc1'=)5=O9Q@fl5`ll*c&2>$C
+HCC:$.mFi2(9d>$,RgouR/$79a$FJ3]Jpki*3)-UbWOAs[IQL,Y8P[,o!c7qm]SnH
+&k7<\!;*UBg&~> endstream endobj 19 0 obj [/Indexed/DeviceRGB 255 20 0 R] endobj 20 0 obj <</Filter[/ASCII85Decode/FlateDecode]/Length 428>>stream
+8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
+b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
+E1r!/,*0[*9.aFIR2&b-C#s<Xl5FH@[<=!#6V)uDBXnIr.F>oRZ7Dl%MLY\.?d>Mn
+6%Q2oYfNRF$$+ON<+]RUJmC0I<jlL.oXisZ;SYU[/7#<&37rclQKqeJe#,UF7Rgb1
+VNWFKf>nDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j<etJICj7e7nPMb=O6S7UOH<
+PO7r\I.Hu&e0d&E<.')fERr/l+*W,)q^D*ai5<uuLX.7g/>$XKrcYp0n+Xl_nU*O(
+l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 17 0 obj <</BBox[0.0 630.0 810.0 0.332611]/Group 21 0 R/Length 1521589/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R/GS1 22 0 R/GS2 23 0 R/GS3 24 0 R/GS4 25 0 R/GS5 26 0 R/GS6 27 0 R>>/XObject<</Fm0 28 0 R/Fm1 29 0 R/Fm10 30 0 R/Fm11 31 0 R/Fm12 32 0 R/Fm13 33 0 R/Fm14 34 0 R/Fm15 35 0 R/Fm16 36 0 R/Fm17 37 0 R/Fm18 38 0 R/Fm19 39 0 R/Fm2 40 0 R/Fm20 41 0 R/Fm21 42 0 R/Fm22 43 0 R/Fm23 44 0 R/Fm24 45 0 R/Fm25 46 0 R/Fm26 47 0 R/Fm27 48 0 R/Fm28 49 0 R/Fm29 50 0 R/Fm3 51 0 R/Fm30 52 0 R/Fm31 53 0 R/Fm32 54 0 R/Fm33 55 0 R/Fm34 56 0 R/Fm4 57 0 R/Fm5 58 0 R/Fm6 59 0 R/Fm7 60 0 R/Fm8 61 0 R/Fm9 62 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+0 630 810 -114.999 re
+f
+0 75 810 -74.667 re
+f
+q
+273.583 568.502 m
+272.764 568.502 272.076 567.939 271.892 567.175 c
+245.324 567.175 l
+245.232 567.175 245.159 567.061 245.166 566.97 c
+245.174 566.877 245.265 566.796 245.361 566.796 c
+271.834 566.796 l
+271.834 566.763 l
+271.834 565.801 272.621 565.025 273.583 565.025 c
+274.516 565.025 275.266 565.761 275.306 566.683 c
+278.83 566.191 280.818 564.192 282.861 562.148 c
+284.872 560.138 286.941 558.087 290.472 557.532 c
+290.568 556.662 291.288 555.989 292.184 555.989 c
+293.03 555.989 293.728 556.585 293.886 557.381 c
+300.581 557.381 l
+300.739 556.585 301.441 555.989 302.287 555.989 c
+303.132 555.989 303.83 556.585 303.988 557.381 c
+311.257 557.381 l
+316.026 557.381 318.452 559.793 320.807 562.148 c
+322.916 564.257 324.971 566.311 328.715 566.719 c
+328.738 565.779 329.516 565.025 330.465 565.025 c
+331.423 565.025 332.199 565.801 332.199 566.763 c
+332.199 566.796 l
+347.328 566.796 l
+347.328 566.763 l
+347.328 565.801 348.104 565.025 349.066 565.025 c
+350.003 565.025 350.764 565.763 350.801 566.694 c
+354.542 566.293 357.463 564.695 359.977 562.434 c
+362.895 559.807 365.253 556.286 367.609 552.755 c
+369.965 549.223 372.32 545.678 375.289 543.01 c
+377.454 541.062 379.945 539.588 382.977 538.931 c
+369.38 538.931 l
+369.222 539.729 368.524 540.334 367.679 540.334 c
+366.834 540.334 366.121 539.729 365.963 538.931 c
+350.779 538.931 l
+350.621 539.729 349.912 540.334 349.066 540.334 c
+348.254 540.334 347.578 539.773 347.387 539.019 c
+343.745 539.471 341.717 541.503 339.637 543.579 c
+337.561 545.659 335.418 547.783 331.662 548.253 c
+331.612 549.169 330.854 549.9 329.925 549.9 c
+329.028 549.9 328.308 549.217 328.212 548.346 c
+322.611 548.346 l
+322.52 549.217 321.785 549.9 320.888 549.9 c
+319.992 549.9 319.267 549.217 319.172 548.346 c
+314.295 548.346 l
+317.177 549.11 319.014 550.94 320.807 552.733 c
+322.829 554.754 324.802 556.735 328.256 557.256 c
+328.462 556.522 329.127 555.989 329.925 555.989 c
+330.773 555.989 331.486 556.588 331.641 557.393 c
+347.361 557.393 l
+347.516 556.588 348.217 555.989 349.066 555.989 c
+350.026 555.989 350.812 556.764 350.812 557.724 c
+350.812 558.686 350.026 559.461 349.066 559.461 c
+348.115 559.461 347.346 558.705 347.328 557.761 c
+331.674 557.761 l
+331.655 558.705 330.876 559.461 329.925 559.461 c
+328.965 559.461 328.19 558.686 328.19 557.724 c
+328.19 557.69 328.198 557.657 328.201 557.624 c
+324.637 557.08 322.553 555.016 320.531 552.994 c
+318.253 550.712 316.049 548.481 311.69 548.346 c
+303.473 548.346 l
+303.463 548.456 303.436 548.558 303.403 548.666 c
+303.374 548.764 303.338 548.86 303.29 548.948 c
+303.242 549.047 303.187 549.14 303.12 549.223 c
+303.069 549.29 303.01 549.349 302.948 549.408 c
+302.933 549.418 302.929 549.441 302.915 549.452 c
+302.893 549.47 302.867 549.481 302.845 549.499 c
+302.786 549.547 302.727 549.595 302.661 549.635 c
+302.577 549.691 302.485 549.734 302.389 549.771 c
+302.191 549.848 301.974 549.9 301.75 549.9 c
+301.632 549.9 301.514 549.886 301.404 549.863 c
+301.291 549.841 301.18 549.805 301.074 549.761 c
+300.978 549.72 300.876 549.668 300.787 549.613 c
+300.783 549.61 300.78 549.603 300.776 549.603 c
+300.685 549.539 300.604 549.462 300.527 549.385 c
+300.456 549.312 300.39 549.238 300.332 549.154 c
+300.295 549.103 300.261 549.044 300.228 548.985 c
+300.207 548.945 300.178 548.911 300.159 548.871 c
+300.155 548.86 300.155 548.845 300.148 548.834 c
+300.107 548.735 300.082 548.635 300.056 548.525 c
+300.049 548.481 300.041 548.437 300.034 548.39 c
+300.026 548.334 300.012 548.276 300.012 548.217 c
+296.411 547.688 294.32 545.612 292.288 543.579 c
+291.983 543.274 291.681 542.977 291.372 542.675 c
+289.866 544.288 288.874 546.247 287.878 548.242 c
+286.698 550.594 285.504 552.98 283.409 554.777 c
+281.568 556.353 279.036 557.455 275.241 557.701 c
+275.019 558.414 274.366 558.937 273.583 558.937 c
+272.819 558.937 272.172 558.444 271.937 557.761 c
+226.7 557.761 l
+226.642 557.761 l
+226.546 557.746 226.469 557.639 226.484 557.543 c
+226.494 557.445 226.601 557.371 226.7 557.381 c
+271.856 557.381 l
+271.852 557.319 271.834 557.264 271.834 557.198 c
+271.834 556.239 272.621 555.453 273.583 555.453 c
+274.545 555.453 275.321 556.239 275.321 557.198 c
+275.321 557.242 275.31 557.283 275.306 557.327 c
+278.985 557.069 281.41 555.989 283.17 554.478 c
+285.195 552.745 286.356 550.426 287.532 548.07 c
+288.528 546.078 289.535 544.064 291.089 542.403 c
+289.429 540.831 287.602 539.478 284.813 539.067 c
+284.607 539.794 283.942 540.334 283.145 540.334 c
+282.3 540.334 281.601 539.729 281.443 538.927 c
+275.285 538.927 l
+275.127 539.729 274.428 540.334 273.583 540.334 c
+272.738 540.334 272.029 539.729 271.867 538.927 c
+226.484 538.927 l
+226.476 538.931 198.221 538.931 198.214 538.927 c
+198.114 538.923 198.027 538.824 198.033 538.725 c
+198.037 538.622 198.125 538.538 198.225 538.541 c
+271.834 538.541 l
+271.86 537.601 272.635 536.851 273.583 536.851 c
+274.527 536.851 275.295 537.601 275.321 538.541 c
+281.41 538.541 l
+281.432 537.601 282.2 536.851 283.145 536.851 c
+284.093 536.851 284.857 537.601 284.883 538.541 c
+300.012 538.541 l
+300.037 537.601 300.802 536.851 301.75 536.851 c
+302.694 536.851 303.473 537.601 303.496 538.541 c
+308.217 538.541 l
+305.337 537.77 303.496 535.947 301.703 534.154 c
+299.7 532.147 297.734 530.192 294.335 529.652 c
+294.081 530.299 293.449 530.759 292.71 530.759 c
+291.92 530.759 291.266 530.225 291.052 529.502 c
+275.241 529.502 l
+275.027 530.225 274.373 530.759 273.583 530.759 c
+272.793 530.759 272.128 530.225 271.915 529.502 c
+235.898 529.502 l
+235.792 529.516 235.693 529.425 235.693 529.317 c
+235.693 529.211 235.792 529.123 235.898 529.138 c
+271.844 529.138 l
+271.844 529.097 271.834 529.064 271.834 529.024 c
+271.834 528.061 272.621 527.274 273.583 527.274 c
+274.545 527.274 275.321 528.061 275.321 529.024 c
+275.321 529.064 275.31 529.097 275.306 529.138 c
+290.987 529.138 l
+290.983 529.097 290.972 529.064 290.972 529.024 c
+290.972 528.061 291.748 527.274 292.71 527.274 c
+293.672 527.274 294.459 528.061 294.459 529.024 c
+294.459 529.119 294.452 529.203 294.437 529.296 c
+297.917 529.869 299.972 531.894 301.967 533.889 c
+304.021 535.947 306.031 537.961 309.599 538.439 c
+309.676 537.545 310.411 536.851 311.323 536.851 c
+312.271 536.851 313.036 537.601 313.061 538.541 c
+319.15 538.541 l
+319.176 537.601 319.94 536.851 320.888 536.851 c
+321.833 536.851 322.611 537.601 322.638 538.541 c
+330.086 538.541 l
+334.731 538.541 337.021 536.245 339.376 533.889 c
+341.455 531.813 343.595 529.696 347.35 529.23 c
+347.343 529.159 347.328 529.093 347.328 529.024 c
+347.328 528.061 348.104 527.274 349.066 527.274 c
+350.026 527.274 350.812 528.061 350.812 529.024 c
+350.812 529.064 350.804 529.097 350.801 529.138 c
+356.904 529.138 l
+356.9 529.097 356.894 529.064 356.894 529.024 c
+356.894 528.061 357.68 527.274 358.639 527.274 c
+359.602 527.274 360.377 528.061 360.377 529.024 c
+360.377 529.064 360.37 529.097 360.366 529.138 c
+375.517 529.138 l
+375.514 529.097 375.506 529.064 375.506 529.024 c
+375.506 528.061 376.282 527.274 377.244 527.274 c
+378.204 527.274 378.979 528.061 378.979 529.024 c
+378.979 529.101 378.967 529.174 378.957 529.252 c
+382.595 529.766 384.697 531.846 386.74 533.889 c
+388.75 535.899 390.713 537.857 394.13 538.391 c
+394.229 537.523 394.95 536.851 395.846 536.851 c
+396.791 536.851 397.57 537.601 397.591 538.541 c
+414.859 538.541 l
+419.504 538.541 421.782 536.245 424.138 533.889 c
+426.17 531.856 428.265 529.788 431.874 529.263 c
+431.862 529.182 431.851 529.108 431.851 529.024 c
+431.851 528.061 432.634 527.274 433.597 527.274 c
+434.559 527.274 435.335 528.061 435.335 529.024 c
+435.335 529.064 435.324 529.097 435.324 529.138 c
+441.435 529.138 l
+441.435 529.097 441.424 529.064 441.424 529.024 c
+441.424 528.061 442.199 527.274 443.163 527.274 c
+444.125 527.274 444.911 528.061 444.911 529.024 c
+444.911 529.982 444.125 530.759 443.163 530.759 c
+442.369 530.759 441.718 530.225 441.505 529.502 c
+435.254 529.502 l
+435.041 530.225 434.391 530.759 433.597 530.759 c
+432.847 530.759 432.208 530.281 431.965 529.616 c
+428.441 530.111 426.453 532.11 424.41 534.154 c
+422.617 535.947 420.779 537.77 417.898 538.541 c
+431.851 538.541 l
+431.874 537.601 432.653 536.851 433.597 536.851 c
+434.559 536.851 435.335 537.622 435.335 538.586 c
+435.335 539.548 434.559 540.334 433.597 540.334 c
+432.751 540.334 432.042 539.729 431.884 538.927 c
+408.473 538.927 l
+411.353 539.698 413.191 541.51 414.985 543.303 c
+416.98 545.302 418.938 547.258 422.319 547.809 c
+422.48 547.016 423.182 546.412 424.024 546.412 c
+424.917 546.412 425.652 547.096 425.748 547.967 c
+431.874 547.967 l
+431.969 547.096 432.704 546.412 433.597 546.412 c
+434.559 546.412 435.335 547.199 435.335 548.162 c
+435.335 549.125 434.559 549.9 433.597 549.9 c
+432.701 549.9 431.969 549.217 431.874 548.346 c
+425.748 548.346 l
+425.652 549.217 424.921 549.9 424.024 549.9 c
+423.068 549.9 422.297 549.136 422.286 548.184 c
+418.784 547.618 416.722 545.582 414.723 543.579 c
+412.367 541.224 410.079 538.927 405.433 538.927 c
+397.558 538.927 l
+397.4 539.729 396.691 540.334 395.846 540.334 c
+395 540.334 394.302 539.729 394.141 538.927 c
+388.522 538.927 l
+388.515 538.96 388.507 538.99 388.496 539.019 c
+388.478 539.104 388.452 539.181 388.419 539.262 c
+388.386 539.339 388.349 539.416 388.305 539.49 c
+388.284 539.526 388.257 539.559 388.236 539.592 c
+388.176 539.673 388.11 539.75 388.041 539.82 c
+387.971 539.89 387.904 539.956 387.824 540.015 c
+387.813 540.022 387.802 540.03 387.791 540.037 c
+387.784 540.045 387.773 540.045 387.765 540.048 c
+387.681 540.107 387.589 540.155 387.493 540.199 c
+387.287 540.284 387.056 540.334 386.817 540.334 c
+386.696 540.334 386.582 540.313 386.464 540.286 c
+386.376 540.268 386.284 540.253 386.2 540.221 c
+386.141 540.199 386.086 540.169 386.03 540.14 c
+385.994 540.122 385.961 540.103 385.928 540.085 c
+385.876 540.051 385.824 540.026 385.777 539.993 c
+385.726 539.952 385.678 539.897 385.63 539.853 c
+385.612 539.839 385.589 539.827 385.571 539.809 c
+385.494 539.732 385.428 539.648 385.365 539.555 c
+385.307 539.467 385.252 539.372 385.207 539.272 c
+385.207 539.262 l
+385.171 539.174 385.138 539.089 385.116 538.997 c
+381.184 539.324 378.141 540.941 375.539 543.282 c
+372.621 545.909 370.273 549.429 367.917 552.961 c
+365.566 556.492 363.21 560.046 360.241 562.717 c
+357.673 565.033 354.633 566.683 350.779 567.083 c
+350.631 567.891 349.919 568.502 349.066 568.502 c
+348.247 568.502 347.57 567.936 347.387 567.175 c
+332.144 567.175 l
+331.957 567.936 331.284 568.502 330.465 568.502 c
+329.619 568.502 328.907 567.906 328.749 567.105 c
+324.846 566.701 322.656 564.533 320.535 562.409 c
+318.18 560.057 315.901 557.761 311.257 557.761 c
+304.024 557.761 l
+304.006 558.705 303.238 559.461 302.287 559.461 c
+301.335 559.461 300.567 558.705 300.548 557.761 c
+293.923 557.761 l
+293.905 558.705 293.136 559.461 292.184 559.461 c
+291.288 559.461 290.568 558.789 290.472 557.919 c
+287.084 558.466 285.122 560.41 283.123 562.409 c
+281.068 564.467 278.967 566.573 275.285 567.072 c
+275.141 567.888 274.439 568.502 273.583 568.502 c
+271.639 551.054 m
+271.639 545.329 l
+273.55 542.459 l
+275.457 545.329 l
+275.457 551.054 l
+h
+394.086 551.054 m
+394.086 545.329 l
+395.993 542.459 l
+397.9 545.329 l
+397.9 551.054 l
+h
+303.473 547.967 m
+319.172 547.967 l
+319.271 547.096 319.992 546.412 320.888 546.412 c
+321.785 546.412 322.516 547.096 322.611 547.967 c
+328.212 547.967 l
+328.308 547.096 329.031 546.412 329.925 546.412 c
+330.791 546.412 331.516 547.048 331.652 547.875 c
+335.275 547.419 337.303 545.38 339.376 543.303 c
+341.169 541.51 343.007 539.698 345.887 538.931 c
+341.202 538.931 l
+341.044 539.729 340.346 540.334 339.501 540.334 c
+338.656 540.334 337.946 539.729 337.788 538.931 c
+322.6 538.931 l
+322.443 539.729 321.734 540.334 320.888 540.334 c
+320.043 540.334 319.345 539.729 319.187 538.931 c
+313.028 538.931 l
+312.87 539.729 312.168 540.334 311.323 540.334 c
+310.478 540.334 309.78 539.729 309.622 538.931 c
+303.463 538.931 l
+303.305 539.729 302.595 540.334 301.75 540.334 c
+300.93 540.334 300.255 539.761 300.067 538.997 c
+296.411 539.254 293.996 540.316 292.243 541.819 c
+292.026 542.003 291.821 542.208 291.626 542.403 c
+291.939 542.705 292.243 542.999 292.552 543.303 c
+294.58 545.333 296.565 547.328 300.045 547.842 c
+300.063 547.754 300.085 547.662 300.115 547.577 c
+300.118 547.563 300.122 547.548 300.126 547.534 c
+300.155 547.457 300.199 547.386 300.24 547.317 c
+300.247 547.302 300.255 547.284 300.261 547.269 c
+300.295 547.214 300.328 547.162 300.365 547.111 c
+300.416 547.045 300.467 546.986 300.527 546.927 c
+300.589 546.865 300.651 546.81 300.721 546.754 c
+300.754 546.732 300.787 546.711 300.824 546.688 c
+300.872 546.659 300.92 546.634 300.971 546.607 c
+301.022 546.582 301.074 546.549 301.133 546.526 c
+301.217 546.493 301.313 546.468 301.404 546.449 c
+301.518 546.424 301.628 546.412 301.75 546.412 c
+301.974 546.412 302.18 546.46 302.378 546.538 c
+302.478 546.578 302.573 546.622 302.661 546.677 c
+302.727 546.717 302.786 546.765 302.845 546.813 c
+302.867 546.831 302.893 546.839 302.915 546.857 c
+302.937 546.879 302.959 546.905 302.981 546.927 c
+303.11 547.056 303.209 547.203 303.29 547.361 c
+303.338 547.453 303.374 547.548 303.403 547.648 c
+303.436 547.75 303.463 547.857 303.473 547.967 c
+291.351 542.139 m
+291.556 541.933 291.773 541.731 292.001 541.533 c
+293.397 540.338 295.195 539.416 297.634 538.931 c
+286.036 538.931 l
+288.286 539.53 289.899 540.768 291.351 542.139 c
+441.174 540.687 m
+441.174 534.966 l
+443.082 532.107 l
+444.988 534.966 l
+444.988 540.687 l
+h
+333.125 538.541 m
+337.752 538.541 l
+337.777 537.601 338.556 536.851 339.501 536.851 c
+340.449 536.851 341.214 537.601 341.239 538.541 c
+347.328 538.541 l
+347.354 537.601 348.118 536.851 349.066 536.851 c
+350.01 536.851 350.789 537.601 350.812 538.541 c
+365.93 538.541 l
+365.955 537.601 366.731 536.851 367.679 536.851 c
+368.623 536.851 369.392 537.601 369.417 538.541 c
+385.068 538.541 l
+385.072 538.497 385.076 538.457 385.079 538.416 c
+385.09 538.343 385.097 538.269 385.116 538.2 c
+385.138 538.096 385.167 538.005 385.207 537.913 c
+385.23 537.861 385.259 537.814 385.284 537.762 c
+385.365 537.616 385.465 537.472 385.583 537.35 c
+385.604 537.332 385.63 537.314 385.652 537.296 c
+385.795 537.167 385.953 537.064 386.134 536.987 c
+386.144 536.987 l
+386.244 536.943 386.354 536.906 386.464 536.884 c
+386.475 536.884 l
+386.586 536.862 386.699 536.851 386.817 536.851 c
+386.938 536.851 387.048 536.862 387.158 536.884 c
+387.332 536.917 387.49 536.987 387.64 537.068 c
+387.688 537.094 387.744 537.115 387.787 537.145 c
+387.875 537.204 387.952 537.269 388.03 537.34 c
+388.041 537.35 l
+388.052 537.365 388.062 537.383 388.074 537.398 c
+388.137 537.464 388.206 537.538 388.257 537.616 c
+388.313 537.699 388.364 537.784 388.405 537.876 c
+388.411 537.888 388.411 537.902 388.415 537.913 c
+388.456 538.005 388.486 538.1 388.507 538.2 c
+388.526 538.269 388.537 538.343 388.544 538.416 c
+388.548 538.457 388.552 538.497 388.555 538.541 c
+392.976 538.541 l
+390.095 537.77 388.257 535.947 386.464 534.154 c
+384.413 532.103 382.418 530.104 378.876 529.616 c
+378.63 530.281 377.994 530.759 377.241 530.759 c
+376.451 530.759 375.796 530.225 375.587 529.502 c
+360.297 529.502 l
+360.083 530.225 359.433 530.759 358.639 530.759 c
+357.849 530.759 357.184 530.225 356.971 529.502 c
+350.735 529.502 l
+350.521 530.225 349.856 530.759 349.066 530.759 c
+348.306 530.759 347.67 530.269 347.431 529.593 c
+343.764 530.03 341.724 532.066 339.637 534.154 c
+337.844 535.947 336.006 537.77 333.125 538.541 c
+320.866 535.079 m
+318.955 532.221 l
+318.955 526.485 l
+322.773 526.485 l
+322.773 532.221 l
+h
+W n
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+/GS1 gs
+0 TL/Fm0 Do
+Q
+Q
+0 0 0 0 k
+q 1 0 0 1 40.6324 64.7361 cm
+0 0 m
+-0.276 -0.306 -0.632 -0.541 -1.073 -0.706 c
+-1.507 -0.871 -1.97 -0.948 -2.469 -0.941 c
+-2.911 -0.941 -3.293 -0.846 -3.616 -0.662 c
+-3.939 -0.467 -4.189 -0.188 -4.366 0.176 c
+-4.542 0.536 -4.642 0.955 -4.659 1.425 c
+-4.671 1.679 -4.656 1.944 -4.615 2.219 c
+-4.439 3.424 l
+-4.285 4.453 -3.95 5.247 -3.439 5.805 c
+-2.932 6.364 -2.263 6.632 -1.44 6.614 c
+-0.735 6.603 -0.206 6.393 0.147 5.982 c
+0.5 5.57 0.68 4.968 0.69 4.174 c
+-0.721 4.174 l
+-0.735 4.586 l
+-0.775 5.104 -1.037 5.373 -1.514 5.394 c
+-2.213 5.423 -2.668 4.961 -2.881 4.012 c
+-2.955 3.63 l
+-3.16 2.219 l
+-3.2 1.896 -3.215 1.616 -3.204 1.381 c
+-3.197 1.007 -3.113 0.735 -2.955 0.558 c
+-2.801 0.389 -2.583 0.301 -2.308 0.294 c
+-2.043 0.294 -1.749 0.382 -1.426 0.558 c
+-1.176 1.911 l
+-2.19 1.911 l
+-1.984 3.027 l
+0.47 3.027 l
+h
+2.095 -0.838 m
+0.698 -0.838 l
+1.639 4.629 l
+3.05 4.629 l
+h
+1.83 6.011 m
+1.83 6.247 1.903 6.437 2.051 6.585 c
+2.198 6.732 2.373 6.805 2.579 6.805 c
+2.793 6.813 2.977 6.746 3.123 6.599 c
+3.271 6.46 3.344 6.283 3.344 6.071 c
+3.344 5.835 3.271 5.644 3.123 5.497 c
+2.977 5.35 2.793 5.277 2.579 5.277 c
+2.363 5.265 2.183 5.328 2.036 5.468 c
+1.897 5.614 1.83 5.795 1.83 6.011 c
+5.945 5.967 m
+5.71 4.629 l
+6.416 4.629 l
+6.24 3.557 l
+5.534 3.557 l
+5.064 0.793 l
+5.049 0.631 l
+5.038 0.404 5.13 0.294 5.328 0.294 c
+5.388 0.282 5.505 0.286 5.681 0.309 c
+5.548 -0.809 l
+5.343 -0.897 5.097 -0.941 4.814 -0.941 c
+4.409 -0.93 4.108 -0.786 3.902 -0.5 c
+3.697 -0.206 3.616 0.183 3.667 0.675 c
+4.137 3.557 l
+3.52 3.557 l
+3.697 4.629 l
+4.314 4.629 l
+4.549 5.967 l
+h
+7.915 0.396 m
+10.444 0.396 l
+10.238 -0.838 l
+6.254 -0.838 l
+7.533 6.526 l
+8.974 6.526 l
+h
+13.498 -0.838 m
+13.475 -0.721 13.468 -0.581 13.468 -0.412 c
+13.152 -0.765 12.796 -0.941 12.395 -0.941 c
+11.973 -0.941 11.634 -0.79 11.381 -0.485 c
+11.135 -0.184 11.021 0.195 11.042 0.646 c
+11.072 1.234 11.296 1.69 11.719 2.013 c
+12.137 2.344 12.704 2.513 13.409 2.513 c
+13.82 2.513 l
+13.88 2.954 l
+13.895 3.175 l
+13.895 3.469 13.777 3.615 13.542 3.615 c
+13.218 3.623 13.02 3.417 12.954 2.998 c
+11.557 2.983 l
+11.586 3.501 11.8 3.924 12.204 4.247 c
+12.604 4.579 13.093 4.74 13.674 4.733 c
+14.21 4.722 14.622 4.549 14.909 4.218 c
+15.202 3.895 15.324 3.461 15.276 2.925 c
+14.82 0.147 l
+14.805 -0.133 l
+14.795 -0.368 14.82 -0.574 14.879 -0.75 c
+14.865 -0.838 l
+h
+12.792 0.176 m
+13.034 0.165 13.263 0.272 13.468 0.5 c
+13.718 1.646 l
+13.409 1.646 l
+13.145 1.635 12.928 1.547 12.762 1.381 c
+12.594 1.213 12.498 1.003 12.469 0.749 c
+12.469 0.558 l
+12.469 0.448 12.49 0.357 12.542 0.278 c
+12.601 0.209 12.681 0.176 12.792 0.176 c
+18.057 -0.941 m
+17.595 -0.919 17.249 -0.706 17.014 -0.294 c
+16.837 -0.838 l
+15.53 -0.838 l
+16.882 6.923 l
+18.279 6.923 l
+17.749 4.174 l
+18.061 4.556 18.418 4.74 18.822 4.733 c
+19.3 4.722 19.66 4.542 19.895 4.189 c
+20.13 3.844 20.233 3.34 20.204 2.675 c
+20.182 1.899 20.087 1.227 19.91 0.661 c
+19.74 0.091 19.502 -0.324 19.189 -0.588 c
+18.874 -0.842 18.499 -0.96 18.057 -0.941 c
+18.836 2.66 m
+18.844 2.973 18.807 3.193 18.719 3.322 c
+18.639 3.457 18.506 3.528 18.323 3.528 c
+18.065 3.534 17.83 3.403 17.617 3.131 c
+17.19 0.617 l
+17.286 0.382 17.477 0.253 17.764 0.235 c
+18.282 0.205 18.591 0.577 18.69 1.352 c
+18.734 1.72 l
+h
+25.672 1.72 m
+23.262 1.72 l
+23.482 2.91 l
+25.878 2.91 l
+h
+32.933 2.352 m
+30.684 2.352 l
+30.346 0.396 l
+33.007 0.396 l
+32.787 -0.838 l
+28.685 -0.838 l
+29.964 6.526 l
+34.065 6.526 l
+33.845 5.277 l
+31.199 5.277 l
+30.89 3.542 l
+33.139 3.542 l
+h
+35.424 1.205 m
+36.63 4.629 l
+38.115 4.629 l
+35.807 -0.838 l
+34.484 -0.838 l
+33.867 4.629 l
+35.293 4.629 l
+h
+39.966 -0.941 m
+39.526 -0.941 39.147 -0.834 38.835 -0.618 c
+38.53 -0.405 38.31 -0.103 38.173 0.278 c
+38.034 0.661 37.99 1.095 38.041 1.587 c
+38.085 1.955 l
+38.202 2.865 38.474 3.561 38.908 4.041 c
+39.349 4.519 39.912 4.751 40.599 4.733 c
+41.098 4.71 41.488 4.556 41.774 4.262 c
+42.058 3.968 42.223 3.549 42.274 3.013 c
+42.293 2.69 42.282 2.373 42.245 2.072 c
+42.157 1.411 l
+39.423 1.411 l
+39.401 1.271 39.397 1.135 39.409 0.999 c
+39.438 0.507 39.677 0.257 40.128 0.249 c
+40.548 0.239 40.955 0.4 41.348 0.735 c
+41.819 -0.133 l
+41.631 -0.389 41.371 -0.588 41.04 -0.736 c
+40.706 -0.882 40.349 -0.948 39.966 -0.941 c
+40.467 3.557 m
+40.062 3.564 39.783 3.307 39.629 2.778 c
+39.526 2.41 l
+40.907 2.41 l
+40.944 2.605 40.966 2.763 40.966 2.881 c
+40.974 3.31 40.808 3.534 40.467 3.557 c
+45.799 3.248 m
+45.358 3.307 l
+44.982 3.314 44.681 3.138 44.446 2.778 c
+43.814 -0.838 l
+42.418 -0.838 l
+43.373 4.629 l
+44.681 4.629 l
+44.564 4.027 l
+44.729 4.292 44.891 4.475 45.049 4.586 c
+45.203 4.692 45.376 4.747 45.564 4.747 c
+45.67 4.747 45.813 4.722 45.99 4.674 c
+h
+47.897 1.572 m
+49.146 4.629 l
+50.661 4.629 l
+47.853 -1.661 l
+47.647 -2.151 47.405 -2.503 47.133 -2.72 c
+46.857 -2.944 46.537 -3.057 46.177 -3.057 c
+46.037 -3.057 45.832 -3.021 45.56 -2.955 c
+45.692 -1.838 l
+45.839 -1.852 l
+46.221 -1.852 46.501 -1.68 46.677 -1.338 c
+46.912 -0.823 l
+46.339 4.629 l
+47.808 4.629 l
+h
+52.997 4.733 m
+53.556 4.71 53.986 4.519 54.291 4.159 c
+54.603 3.806 54.776 3.326 54.806 2.719 c
+54.806 2.366 l
+54.754 1.308 54.501 0.484 54.041 -0.103 c
+53.578 -0.684 52.976 -0.96 52.233 -0.941 c
+51.8 -0.93 51.443 -0.817 51.16 -0.588 c
+50.873 -0.364 50.667 -0.056 50.543 0.338 c
+50.426 0.738 50.389 1.198 50.44 1.72 c
+50.469 1.984 l
+50.565 2.844 50.84 3.52 51.292 4.012 c
+51.741 4.512 52.31 4.751 52.997 4.733 c
+51.822 1.146 m
+51.822 0.558 51.997 0.253 52.35 0.235 c
+52.839 0.205 53.159 0.577 53.306 1.352 c
+53.35 1.72 l
+53.398 2.198 53.424 2.506 53.424 2.645 c
+53.424 3.222 53.24 3.528 52.88 3.557 c
+52.604 3.564 52.38 3.439 52.204 3.175 c
+52.035 2.917 51.928 2.547 51.88 2.057 c
+51.839 1.565 51.822 1.263 51.822 1.146 c
+57.246 4.629 m
+57.142 4.086 l
+57.532 4.534 57.958 4.751 58.422 4.733 c
+58.804 4.722 59.091 4.575 59.288 4.292 c
+59.494 4.016 59.598 3.619 59.598 3.101 c
+59.568 2.645 l
+58.98 -0.838 l
+57.584 -0.838 l
+58.157 2.645 l
+58.187 2.94 l
+58.204 3.34 58.058 3.542 57.745 3.542 c
+57.599 3.542 57.47 3.501 57.363 3.424 c
+57.264 3.343 57.175 3.252 57.098 3.145 c
+56.393 -0.838 l
+54.997 -0.838 l
+55.952 4.629 l
+h
+62.141 -0.941 m
+61.699 -0.941 61.321 -0.834 61.009 -0.618 c
+60.703 -0.405 60.483 -0.103 60.347 0.278 c
+60.207 0.661 60.163 1.095 60.215 1.587 c
+60.259 1.955 l
+60.377 2.865 60.649 3.561 61.082 4.041 c
+61.522 4.519 62.085 4.751 62.773 4.733 c
+63.272 4.71 63.662 4.556 63.949 4.262 c
+64.231 3.968 64.396 3.549 64.448 3.013 c
+64.466 2.69 64.456 2.373 64.419 2.072 c
+64.331 1.411 l
+61.597 1.411 l
+61.574 1.271 61.57 1.135 61.582 0.999 c
+61.611 0.507 61.85 0.257 62.302 0.249 c
+62.721 0.239 63.128 0.4 63.522 0.735 c
+63.992 -0.133 l
+63.805 -0.389 63.544 -0.588 63.213 -0.736 c
+62.879 -0.882 62.522 -0.948 62.141 -0.941 c
+62.64 3.557 m
+62.236 3.564 61.956 3.307 61.802 2.778 c
+61.699 2.41 l
+63.081 2.41 l
+63.118 2.605 63.14 2.763 63.14 2.881 c
+63.147 3.31 62.982 3.534 62.64 3.557 c
+69.034 0.235 m
+69.416 0.224 69.655 0.5 69.754 1.058 c
+71.063 1.058 l
+71.004 0.448 70.776 -0.04 70.386 -0.412 c
+69.993 -0.786 69.519 -0.96 68.961 -0.941 c
+68.45 -0.93 68.042 -0.78 67.741 -0.485 c
+67.436 -0.192 67.255 0.213 67.197 0.735 c
+67.145 1.153 67.171 1.664 67.27 2.263 c
+67.365 2.869 67.554 3.373 67.829 3.777 c
+68.269 4.432 68.894 4.751 69.711 4.733 c
+70.258 4.71 70.681 4.512 70.974 4.13 c
+71.269 3.748 71.4 3.233 71.371 2.587 c
+70.048 2.587 l
+70.048 2.896 l
+70.037 3.314 69.875 3.534 69.563 3.557 c
+69.063 3.575 68.77 3.189 68.682 2.395 c
+68.578 1.484 l
+68.538 1.209 68.523 0.984 68.535 0.808 c
+68.564 0.434 68.729 0.242 69.034 0.235 c
+74.028 -0.838 m
+74.006 -0.721 73.999 -0.581 73.999 -0.412 c
+73.682 -0.765 73.326 -0.941 72.926 -0.941 c
+72.503 -0.941 72.165 -0.79 71.911 -0.485 c
+71.666 -0.184 71.552 0.195 71.573 0.646 c
+71.602 1.234 71.827 1.69 72.25 2.013 c
+72.668 2.344 73.235 2.513 73.94 2.513 c
+74.351 2.513 l
+74.411 2.954 l
+74.425 3.175 l
+74.425 3.469 74.307 3.615 74.072 3.615 c
+73.749 3.623 73.551 3.417 73.484 2.998 c
+72.088 2.983 l
+72.117 3.501 72.331 3.924 72.734 4.247 c
+73.135 4.579 73.624 4.74 74.205 4.733 c
+74.741 4.722 75.153 4.549 75.439 4.218 c
+75.733 3.895 75.855 3.461 75.807 2.925 c
+75.351 0.147 l
+75.336 -0.133 l
+75.325 -0.368 75.351 -0.574 75.41 -0.75 c
+75.396 -0.838 l
+h
+73.322 0.176 m
+73.565 0.165 73.793 0.272 73.999 0.5 c
+74.249 1.646 l
+73.94 1.646 l
+73.675 1.635 73.459 1.547 73.293 1.381 c
+73.124 1.213 73.029 1.003 72.999 0.749 c
+72.999 0.558 l
+72.999 0.448 73.021 0.357 73.073 0.278 c
+73.131 0.209 73.212 0.176 73.322 0.176 c
+78.28 4.629 m
+78.177 4.086 l
+78.567 4.534 78.993 4.751 79.456 4.733 c
+79.838 4.722 80.125 4.575 80.323 4.292 c
+80.529 4.016 80.632 3.619 80.632 3.101 c
+80.603 2.645 l
+80.015 -0.838 l
+78.618 -0.838 l
+79.191 2.645 l
+79.221 2.94 l
+79.238 3.34 79.092 3.542 78.779 3.542 c
+78.633 3.542 78.504 3.501 78.397 3.424 c
+78.298 3.343 78.21 3.252 78.133 3.145 c
+77.428 -0.838 l
+76.031 -0.838 l
+76.986 4.629 l
+h
+85.357 0.235 m
+85.74 0.224 85.979 0.5 86.078 1.058 c
+87.386 1.058 l
+87.327 0.448 87.099 -0.04 86.71 -0.412 c
+86.317 -0.786 85.842 -0.96 85.284 -0.941 c
+84.773 -0.93 84.366 -0.78 84.064 -0.485 c
+83.759 -0.192 83.579 0.213 83.52 0.735 c
+83.469 1.153 83.494 1.664 83.593 2.263 c
+83.689 2.869 83.876 3.373 84.152 3.777 c
+84.593 4.432 85.218 4.751 86.033 4.733 c
+86.581 4.71 87.003 4.512 87.298 4.13 c
+87.591 3.748 87.724 3.233 87.695 2.587 c
+86.372 2.587 l
+86.372 2.896 l
+86.361 3.314 86.199 3.534 85.887 3.557 c
+85.387 3.575 85.093 3.189 85.004 2.395 c
+84.902 1.484 l
+84.861 1.209 84.846 0.984 84.858 0.808 c
+84.887 0.434 85.052 0.242 85.357 0.235 c
+90.627 4.733 m
+91.186 4.71 91.616 4.519 91.921 4.159 c
+92.233 3.806 92.406 3.326 92.435 2.719 c
+92.435 2.366 l
+92.383 1.308 92.13 0.484 91.67 -0.103 c
+91.208 -0.684 90.605 -0.96 89.862 -0.941 c
+89.43 -0.93 89.073 -0.817 88.79 -0.588 c
+88.503 -0.364 88.297 -0.056 88.173 0.338 c
+88.055 0.738 88.018 1.198 88.069 1.72 c
+88.098 1.984 l
+88.194 2.844 88.47 3.52 88.921 4.012 c
+89.37 4.512 89.939 4.751 90.627 4.733 c
+89.451 1.146 m
+89.451 0.558 89.627 0.253 89.98 0.235 c
+90.469 0.205 90.789 0.577 90.936 1.352 c
+90.98 1.72 l
+91.028 2.198 91.053 2.506 91.053 2.645 c
+91.053 3.222 90.87 3.528 90.509 3.557 c
+90.234 3.564 90.01 3.439 89.833 3.175 c
+89.665 2.917 89.557 2.547 89.509 2.057 c
+89.469 1.565 89.451 1.263 89.451 1.146 c
+94.878 4.629 m
+94.776 4.086 l
+95.165 4.534 95.591 4.751 96.054 4.733 c
+96.437 4.722 96.723 4.575 96.922 4.292 c
+97.128 4.016 97.23 3.619 97.23 3.101 c
+97.201 2.645 l
+96.613 -0.838 l
+95.217 -0.838 l
+95.79 2.645 l
+95.819 2.94 l
+95.838 3.34 95.691 3.542 95.379 3.542 c
+95.231 3.542 95.103 3.501 94.996 3.424 c
+94.897 3.343 94.809 3.252 94.732 3.145 c
+94.026 -0.838 l
+92.63 -0.838 l
+93.585 4.629 l
+h
+100.318 5.967 m
+100.082 4.629 l
+100.788 4.629 l
+100.611 3.557 l
+99.906 3.557 l
+99.435 0.793 l
+99.421 0.631 l
+99.41 0.404 99.502 0.294 99.7 0.294 c
+99.759 0.282 99.876 0.286 100.052 0.309 c
+99.921 -0.809 l
+99.715 -0.897 99.468 -0.941 99.186 -0.941 c
+98.781 -0.93 98.48 -0.786 98.274 -0.5 c
+98.068 -0.206 97.987 0.183 98.039 0.675 c
+98.51 3.557 l
+97.892 3.557 l
+98.068 4.629 l
+98.685 4.629 l
+98.921 5.967 l
+h
+103.933 3.248 m
+103.493 3.307 l
+103.117 3.314 102.816 3.138 102.581 2.778 c
+101.949 -0.838 l
+100.553 -0.838 l
+101.508 4.629 l
+102.816 4.629 l
+102.699 4.027 l
+102.864 4.292 103.026 4.475 103.184 4.586 c
+103.338 4.692 103.51 4.747 103.698 4.747 c
+103.805 4.747 103.948 4.722 104.125 4.674 c
+h
+105.304 -0.838 m
+103.907 -0.838 l
+104.848 4.629 l
+106.259 4.629 l
+h
+105.039 6.011 m
+105.039 6.247 105.113 6.437 105.26 6.585 c
+105.407 6.732 105.583 6.805 105.789 6.805 c
+106.003 6.813 106.186 6.746 106.333 6.599 c
+106.479 6.46 106.554 6.283 106.554 6.071 c
+106.554 5.835 106.479 5.644 106.333 5.497 c
+106.186 5.35 106.003 5.277 105.789 5.277 c
+105.573 5.265 105.392 5.328 105.245 5.468 c
+105.106 5.614 105.039 5.795 105.039 6.011 c
+108.876 -0.941 m
+108.413 -0.919 108.067 -0.706 107.832 -0.294 c
+107.655 -0.838 l
+106.348 -0.838 l
+107.7 6.923 l
+109.097 6.923 l
+108.567 4.174 l
+108.879 4.556 109.236 4.74 109.64 4.733 c
+110.118 4.722 110.478 4.542 110.713 4.189 c
+110.948 3.844 111.051 3.34 111.022 2.675 c
+111 1.899 110.905 1.227 110.728 0.661 c
+110.559 0.091 110.32 -0.324 110.007 -0.588 c
+109.692 -0.842 109.317 -0.96 108.876 -0.941 c
+109.655 2.66 m
+109.662 2.973 109.625 3.193 109.537 3.322 c
+109.457 3.457 109.324 3.528 109.141 3.528 c
+108.883 3.534 108.648 3.403 108.435 3.131 c
+108.009 0.617 l
+108.104 0.382 108.295 0.253 108.582 0.235 c
+109.1 0.205 109.409 0.577 109.508 1.352 c
+109.552 1.72 l
+h
+114.003 -0.339 m
+113.656 -0.75 113.257 -0.948 112.797 -0.941 c
+112.433 -0.941 112.143 -0.823 111.929 -0.588 c
+111.713 -0.346 111.591 -0.015 111.562 0.396 c
+111.54 0.631 111.54 0.86 111.562 1.087 c
+112.164 4.629 l
+113.546 4.629 l
+112.958 1.072 l
+112.944 0.823 l
+112.933 0.654 112.958 0.517 113.018 0.411 c
+113.076 0.312 113.168 0.257 113.297 0.249 c
+113.569 0.239 113.826 0.382 114.061 0.675 c
+114.751 4.629 l
+116.148 4.629 l
+115.208 -0.838 l
+113.899 -0.838 l
+h
+118.897 5.967 m
+118.662 4.629 l
+119.368 4.629 l
+119.191 3.557 l
+118.485 3.557 l
+118.015 0.793 l
+118.001 0.631 l
+117.989 0.404 118.082 0.294 118.279 0.294 c
+118.338 0.282 118.456 0.286 118.632 0.309 c
+118.5 -0.809 l
+118.294 -0.897 118.048 -0.941 117.766 -0.941 c
+117.361 -0.93 117.06 -0.786 116.854 -0.5 c
+116.648 -0.206 116.567 0.183 116.619 0.675 c
+117.089 3.557 l
+116.472 3.557 l
+116.648 4.629 l
+117.265 4.629 l
+117.5 5.967 l
+h
+121.396 -0.941 m
+120.955 -0.941 120.576 -0.834 120.264 -0.618 c
+119.959 -0.405 119.738 -0.103 119.603 0.278 c
+119.463 0.661 119.418 1.095 119.47 1.587 c
+119.514 1.955 l
+119.632 2.865 119.904 3.561 120.337 4.041 c
+120.779 4.519 121.34 4.751 122.028 4.733 c
+122.528 4.71 122.917 4.556 123.204 4.262 c
+123.487 3.968 123.652 3.549 123.703 3.013 c
+123.722 2.69 123.711 2.373 123.674 2.072 c
+123.586 1.411 l
+120.852 1.411 l
+120.829 1.271 120.826 1.135 120.837 0.999 c
+120.866 0.507 121.105 0.257 121.558 0.249 c
+121.976 0.239 122.385 0.4 122.778 0.735 c
+123.248 -0.133 l
+123.061 -0.389 122.799 -0.588 122.469 -0.736 c
+122.134 -0.882 121.778 -0.948 121.396 -0.941 c
+121.895 3.557 m
+121.492 3.564 121.212 3.307 121.058 2.778 c
+120.955 2.41 l
+122.337 2.41 l
+122.373 2.605 122.395 2.763 122.395 2.881 c
+122.402 3.31 122.237 3.534 121.895 3.557 c
+135.212 4.145 m
+135.565 4.546 135.948 4.74 136.359 4.733 c
+136.76 4.722 137.065 4.571 137.271 4.277 c
+137.484 3.991 137.594 3.594 137.594 3.087 c
+137.579 2.601 l
+136.991 -0.838 l
+135.595 -0.838 l
+136.168 2.616 l
+136.197 2.91 l
+136.205 3.322 136.058 3.528 135.757 3.528 c
+135.529 3.534 135.316 3.417 135.11 3.175 c
+134.404 -0.838 l
+133.008 -0.838 l
+134.36 6.923 l
+135.757 6.923 l
+h
+140.696 5.967 m
+140.461 4.629 l
+141.166 4.629 l
+140.99 3.557 l
+140.284 3.557 l
+139.814 0.793 l
+139.799 0.631 l
+139.788 0.404 139.879 0.294 140.078 0.294 c
+140.137 0.282 140.255 0.286 140.431 0.309 c
+140.299 -0.809 l
+140.093 -0.897 139.847 -0.941 139.563 -0.941 c
+139.16 -0.93 138.858 -0.786 138.653 -0.5 c
+138.447 -0.206 138.366 0.183 138.418 0.675 c
+138.888 3.557 l
+138.27 3.557 l
+138.447 4.629 l
+139.064 4.629 l
+139.299 5.967 l
+h
+143.742 5.967 m
+143.507 4.629 l
+144.213 4.629 l
+144.036 3.557 l
+143.33 3.557 l
+142.86 0.793 l
+142.846 0.631 l
+142.834 0.404 142.927 0.294 143.125 0.294 c
+143.183 0.282 143.301 0.286 143.478 0.309 c
+143.345 -0.809 l
+143.139 -0.897 142.894 -0.941 142.61 -0.941 c
+142.206 -0.93 141.905 -0.786 141.699 -0.5 c
+141.493 -0.206 141.412 0.183 141.464 0.675 c
+141.934 3.557 l
+141.317 3.557 l
+141.493 4.629 l
+142.111 4.629 l
+142.346 5.967 l
+h
+146.491 -0.941 m
+146.069 -0.93 145.734 -0.746 145.491 -0.383 c
+145.007 -2.94 l
+143.625 -2.94 l
+144.933 4.629 l
+146.212 4.629 l
+146.123 4.13 l
+146.454 4.542 146.836 4.74 147.27 4.733 c
+147.748 4.722 148.107 4.542 148.342 4.189 c
+148.578 3.836 148.681 3.326 148.652 2.66 c
+148.629 1.866 148.534 1.194 148.357 0.646 c
+148.182 0.106 147.931 -0.302 147.608 -0.574 c
+147.292 -0.838 146.921 -0.96 146.491 -0.941 c
+147.27 2.675 m
+147.27 2.987 147.226 3.208 147.137 3.336 c
+147.056 3.461 146.931 3.532 146.755 3.542 c
+146.509 3.542 146.285 3.417 146.079 3.175 c
+145.624 0.588 l
+145.73 0.371 145.921 0.257 146.197 0.249 c
+146.704 0.22 147.023 0.621 147.152 1.454 c
+147.255 2.484 l
+h
+151.499 0.661 m
+151.536 0.897 151.356 1.105 150.956 1.294 c
+150.551 1.477 150.25 1.65 150.044 1.807 c
+149.846 1.973 149.699 2.153 149.603 2.352 c
+149.515 2.557 149.475 2.786 149.485 3.042 c
+149.515 3.532 149.721 3.939 150.103 4.262 c
+150.485 4.586 150.948 4.74 151.499 4.733 c
+152.047 4.722 152.477 4.56 152.793 4.247 c
+153.105 3.931 153.256 3.52 153.248 3.013 c
+151.852 3.013 l
+151.86 3.248 151.831 3.414 151.764 3.513 c
+151.694 3.619 151.58 3.675 151.426 3.675 c
+151.257 3.675 151.118 3.615 151 3.498 c
+150.89 3.388 150.827 3.256 150.809 3.101 c
+150.757 2.884 150.927 2.69 151.309 2.513 c
+151.698 2.344 151.977 2.204 152.146 2.087 c
+152.646 1.753 152.874 1.297 152.837 0.72 c
+152.815 0.386 152.712 0.091 152.529 -0.162 c
+152.34 -0.42 152.087 -0.614 151.764 -0.75 c
+151.448 -0.886 151.106 -0.948 150.735 -0.941 c
+150.195 -0.93 149.757 -0.761 149.427 -0.427 c
+149.092 -0.085 148.928 0.345 148.928 0.866 c
+150.264 0.852 l
+150.254 0.588 150.294 0.396 150.382 0.278 c
+150.478 0.168 150.625 0.118 150.823 0.118 c
+151 0.118 151.147 0.161 151.264 0.249 c
+151.389 0.345 151.47 0.484 151.499 0.661 c
+153.595 -0.148 m
+153.595 0.095 153.668 0.294 153.815 0.44 c
+153.969 0.595 154.167 0.675 154.403 0.675 c
+154.638 0.675 154.829 0.602 154.976 0.455 c
+155.13 0.316 155.211 0.139 155.211 -0.074 c
+155.211 -0.32 155.126 -0.522 154.961 -0.676 c
+154.803 -0.823 154.612 -0.897 154.388 -0.897 c
+154.152 -0.897 153.961 -0.831 153.815 -0.691 c
+153.668 -0.545 153.595 -0.364 153.595 -0.148 c
+154.535 3.615 m
+154.535 3.858 154.609 4.056 154.755 4.203 c
+154.91 4.357 155.108 4.438 155.343 4.438 c
+155.579 4.438 155.77 4.365 155.916 4.218 c
+156.071 4.079 156.151 3.902 156.151 3.689 c
+156.151 3.443 156.067 3.241 155.902 3.087 c
+155.744 2.94 155.553 2.865 155.328 2.865 c
+155.093 2.865 154.902 2.932 154.755 3.072 c
+154.609 3.218 154.535 3.399 154.535 3.615 c
+156.5 -1.47 m
+155.442 -1.47 l
+158.97 6.526 l
+160.028 6.526 l
+h
+158.566 -1.47 m
+157.508 -1.47 l
+161.036 6.526 l
+162.094 6.526 l
+h
+164.02 -0.838 m
+163.997 -0.721 163.99 -0.581 163.99 -0.412 c
+163.674 -0.765 163.317 -0.941 162.917 -0.941 c
+162.494 -0.941 162.156 -0.79 161.903 -0.485 c
+161.657 -0.184 161.543 0.195 161.564 0.646 c
+161.594 1.234 161.818 1.69 162.241 2.013 c
+162.659 2.344 163.226 2.513 163.931 2.513 c
+164.342 2.513 l
+164.402 2.954 l
+164.416 3.175 l
+164.416 3.469 164.298 3.615 164.063 3.615 c
+163.74 3.623 163.542 3.417 163.475 2.998 c
+162.079 2.983 l
+162.108 3.501 162.322 3.924 162.726 4.247 c
+163.126 4.579 163.615 4.74 164.196 4.733 c
+164.732 4.722 165.144 4.549 165.431 4.218 c
+165.724 3.895 165.846 3.461 165.798 2.925 c
+165.342 0.147 l
+165.327 -0.133 l
+165.316 -0.368 165.342 -0.574 165.401 -0.75 c
+165.387 -0.838 l
+h
+163.314 0.176 m
+163.556 0.165 163.785 0.272 163.99 0.5 c
+164.24 1.646 l
+163.931 1.646 l
+163.667 1.635 163.45 1.547 163.284 1.381 c
+163.116 1.213 163.02 1.003 162.991 0.749 c
+162.991 0.558 l
+162.991 0.448 163.012 0.357 163.064 0.278 c
+163.122 0.209 163.203 0.176 163.314 0.176 c
+168.58 -0.941 m
+168.117 -0.919 167.771 -0.706 167.536 -0.294 c
+167.359 -0.838 l
+166.052 -0.838 l
+167.404 6.923 l
+168.801 6.923 l
+168.271 4.174 l
+168.583 4.556 168.94 4.74 169.344 4.733 c
+169.822 4.722 170.182 4.542 170.417 4.189 c
+170.652 3.844 170.755 3.34 170.725 2.675 c
+170.704 1.899 170.608 1.227 170.432 0.661 c
+170.263 0.091 170.024 -0.324 169.711 -0.588 c
+169.395 -0.842 169.021 -0.96 168.58 -0.941 c
+169.358 2.66 m
+169.366 2.973 169.329 3.193 169.241 3.322 c
+169.16 3.457 169.028 3.528 168.844 3.528 c
+168.587 3.534 168.352 3.403 168.138 3.131 c
+167.712 0.617 l
+167.808 0.382 167.999 0.253 168.286 0.235 c
+168.803 0.205 169.113 0.577 169.212 1.352 c
+169.256 1.72 l
+h
+173.823 4.733 m
+174.382 4.71 174.812 4.519 175.117 4.159 c
+175.429 3.806 175.602 3.326 175.631 2.719 c
+175.631 2.366 l
+175.581 1.308 175.327 0.484 174.867 -0.103 c
+174.405 -0.684 173.802 -0.96 173.059 -0.941 c
+172.626 -0.93 172.269 -0.817 171.986 -0.588 c
+171.7 -0.364 171.494 -0.056 171.369 0.338 c
+171.251 0.738 171.215 1.198 171.266 1.72 c
+171.295 1.984 l
+171.39 2.844 171.666 3.52 172.119 4.012 c
+172.566 4.512 173.136 4.751 173.823 4.733 c
+172.647 1.146 m
+172.647 0.558 172.824 0.253 173.177 0.235 c
+173.665 0.205 173.985 0.577 174.133 1.352 c
+174.176 1.72 l
+174.224 2.198 174.25 2.506 174.25 2.645 c
+174.25 3.222 174.066 3.528 173.706 3.557 c
+173.43 3.564 173.206 3.439 173.03 3.175 c
+172.861 2.917 172.755 2.547 172.707 2.057 c
+172.666 1.565 172.647 1.263 172.647 1.146 c
+178.615 -0.339 m
+178.27 -0.75 177.869 -0.948 177.41 -0.941 c
+177.046 -0.941 176.756 -0.823 176.543 -0.588 c
+176.327 -0.346 176.205 -0.015 176.175 0.396 c
+176.153 0.631 176.153 0.86 176.175 1.087 c
+176.778 4.629 l
+178.16 4.629 l
+177.572 1.072 l
+177.557 0.823 l
+177.546 0.654 177.572 0.517 177.63 0.411 c
+177.69 0.312 177.781 0.257 177.91 0.249 c
+178.182 0.239 178.439 0.382 178.675 0.675 c
+179.365 4.629 l
+180.761 4.629 l
+179.82 -0.838 l
+178.513 -0.838 l
+h
+183.514 5.967 m
+183.279 4.629 l
+183.984 4.629 l
+183.808 3.557 l
+183.103 3.557 l
+182.632 0.793 l
+182.617 0.631 l
+182.606 0.404 182.698 0.294 182.897 0.294 c
+182.955 0.282 183.073 0.286 183.249 0.309 c
+183.117 -0.809 l
+182.912 -0.897 182.665 -0.941 182.382 -0.941 c
+181.978 -0.93 181.677 -0.786 181.47 -0.5 c
+181.265 -0.206 181.185 0.183 181.235 0.675 c
+181.706 3.557 l
+181.089 3.557 l
+181.265 4.629 l
+181.883 4.629 l
+182.118 5.967 l
+h
+183.999 -0.148 m
+183.999 0.095 184.073 0.294 184.219 0.44 c
+184.374 0.595 184.572 0.675 184.807 0.675 c
+185.042 0.675 185.233 0.602 185.381 0.455 c
+185.535 0.316 185.616 0.139 185.616 -0.074 c
+185.616 -0.32 185.532 -0.522 185.366 -0.676 c
+185.208 -0.823 185.017 -0.897 184.793 -0.897 c
+184.558 -0.897 184.366 -0.831 184.219 -0.691 c
+184.073 -0.545 183.999 -0.364 183.999 -0.148 c
+189.137 4.733 m
+189.614 4.71 189.967 4.512 190.195 4.13 c
+190.371 4.629 l
+191.635 4.629 l
+190.71 -0.838 l
+190.61 -1.551 190.334 -2.095 189.886 -2.469 c
+189.445 -2.841 188.886 -3.017 188.21 -2.999 c
+187.916 -2.988 187.622 -2.926 187.328 -2.808 c
+187.034 -2.698 186.792 -2.544 186.608 -2.338 c
+187.167 -1.353 l
+187.45 -1.676 187.803 -1.841 188.225 -1.852 c
+188.82 -1.881 189.187 -1.548 189.328 -0.853 c
+189.43 -0.397 l
+189.107 -0.761 188.743 -0.941 188.343 -0.941 c
+187.872 -0.941 187.512 -0.761 187.269 -0.397 c
+187.024 -0.037 186.906 0.47 186.916 1.117 c
+186.916 1.506 186.953 1.929 187.034 2.381 c
+187.111 2.84 187.226 3.226 187.373 3.542 c
+187.755 4.355 188.343 4.751 189.137 4.733 c
+188.284 1.132 m
+188.284 0.551 188.468 0.257 188.842 0.249 c
+189.085 0.249 189.316 0.357 189.534 0.573 c
+190.004 3.189 l
+189.875 3.414 189.688 3.532 189.445 3.542 c
+188.857 3.571 188.497 3.09 188.372 2.102 c
+188.313 1.631 188.284 1.308 188.284 1.132 c
+193.054 -0.838 m
+191.657 -0.838 l
+192.598 4.629 l
+194.009 4.629 l
+h
+192.789 6.011 m
+192.789 6.247 192.863 6.437 193.01 6.585 c
+193.156 6.732 193.333 6.805 193.538 6.805 c
+193.752 6.813 193.935 6.746 194.083 6.599 c
+194.23 6.46 194.303 6.283 194.303 6.071 c
+194.303 5.835 194.23 5.644 194.083 5.497 c
+193.935 5.35 193.752 5.277 193.538 5.277 c
+193.322 5.265 193.142 5.328 192.995 5.468 c
+192.855 5.614 192.789 5.795 192.789 6.011 c
+196.909 5.967 m
+196.674 4.629 l
+197.379 4.629 l
+197.202 3.557 l
+196.497 3.557 l
+196.027 0.793 l
+196.012 0.631 l
+196.001 0.404 196.092 0.294 196.291 0.294 c
+196.35 0.282 196.468 0.286 196.644 0.309 c
+196.512 -0.809 l
+196.306 -0.897 196.059 -0.941 195.776 -0.941 c
+195.373 -0.93 195.071 -0.786 194.866 -0.5 c
+194.66 -0.206 194.579 0.183 194.631 0.675 c
+195.101 3.557 l
+194.483 3.557 l
+194.66 4.629 l
+195.277 4.629 l
+195.512 5.967 l
+h
+198.614 -0.838 m
+197.218 -0.838 l
+198.555 6.923 l
+199.966 6.923 l
+h
+202.259 -0.838 m
+202.237 -0.721 202.23 -0.581 202.23 -0.412 c
+201.914 -0.765 201.557 -0.941 201.156 -0.941 c
+200.734 -0.941 200.396 -0.79 200.142 -0.485 c
+199.896 -0.184 199.782 0.195 199.805 0.646 c
+199.834 1.234 200.058 1.69 200.48 2.013 c
+200.9 2.344 201.465 2.513 202.171 2.513 c
+202.583 2.513 l
+202.641 2.954 l
+202.656 3.175 l
+202.656 3.469 202.538 3.615 202.303 3.615 c
+201.98 3.623 201.781 3.417 201.715 2.998 c
+200.318 2.983 l
+200.348 3.501 200.561 3.924 200.965 4.247 c
+201.366 4.579 201.854 4.74 202.435 4.733 c
+202.972 4.722 203.383 4.549 203.67 4.218 c
+203.965 3.895 204.085 3.461 204.038 2.925 c
+203.582 0.147 l
+203.568 -0.133 l
+203.556 -0.368 203.582 -0.574 203.641 -0.75 c
+203.626 -0.838 l
+h
+201.553 0.176 m
+201.796 0.165 202.024 0.272 202.23 0.5 c
+202.479 1.646 l
+202.171 1.646 l
+201.906 1.635 201.69 1.547 201.524 1.381 c
+201.355 1.213 201.259 1.003 201.23 0.749 c
+201.23 0.558 l
+201.23 0.448 201.252 0.357 201.303 0.278 c
+201.362 0.209 201.443 0.176 201.553 0.176 c
+206.82 -0.941 m
+206.356 -0.919 206.011 -0.706 205.775 -0.294 c
+205.6 -0.838 l
+204.291 -0.838 l
+205.644 6.923 l
+207.04 6.923 l
+206.511 4.174 l
+206.823 4.556 207.179 4.74 207.584 4.733 c
+208.061 4.722 208.422 4.542 208.657 4.189 c
+208.892 3.844 208.995 3.34 208.965 2.675 c
+208.943 1.899 208.848 1.227 208.671 0.661 c
+208.503 0.091 208.264 -0.324 207.951 -0.588 c
+207.635 -0.842 207.26 -0.96 206.82 -0.941 c
+207.599 2.66 m
+207.606 2.973 207.569 3.193 207.481 3.322 c
+207.4 3.457 207.267 3.528 207.084 3.528 c
+206.826 3.534 206.591 3.403 206.378 3.131 c
+205.952 0.617 l
+206.047 0.382 206.238 0.253 206.525 0.235 c
+207.044 0.205 207.352 0.577 207.452 1.352 c
+207.495 1.72 l
+h
+209.432 -0.148 m
+209.432 0.095 209.505 0.294 209.652 0.44 c
+209.807 0.595 210.005 0.675 210.24 0.675 c
+210.475 0.675 210.666 0.602 210.814 0.455 c
+210.968 0.316 211.049 0.139 211.049 -0.074 c
+211.049 -0.32 210.965 -0.522 210.799 -0.676 c
+210.641 -0.823 210.45 -0.897 210.226 -0.897 c
+209.991 -0.897 209.8 -0.831 209.652 -0.691 c
+209.505 -0.545 209.432 -0.364 209.432 -0.148 c
+214.217 0.235 m
+214.599 0.224 214.838 0.5 214.937 1.058 c
+216.245 1.058 l
+216.187 0.448 215.959 -0.04 215.569 -0.412 c
+215.175 -0.786 214.702 -0.96 214.143 -0.941 c
+213.632 -0.93 213.224 -0.78 212.923 -0.485 c
+212.618 -0.192 212.438 0.213 212.379 0.735 c
+212.328 1.153 212.353 1.664 212.453 2.263 c
+212.548 2.869 212.736 3.373 213.012 3.777 c
+213.452 4.432 214.077 4.751 214.893 4.733 c
+215.441 4.71 215.863 4.512 216.157 4.13 c
+216.451 3.748 216.584 3.233 216.553 2.587 c
+215.231 2.587 l
+215.231 2.896 l
+215.22 3.314 215.058 3.534 214.745 3.557 c
+214.246 3.575 213.952 3.189 213.864 2.395 c
+213.761 1.484 l
+213.721 1.209 213.706 0.984 213.717 0.808 c
+213.746 0.434 213.912 0.242 214.217 0.235 c
+219.49 4.733 m
+220.048 4.71 220.478 4.519 220.783 4.159 c
+221.095 3.806 221.268 3.326 221.298 2.719 c
+221.298 2.366 l
+221.247 1.308 220.993 0.484 220.534 -0.103 c
+220.071 -0.684 219.468 -0.96 218.726 -0.941 c
+218.292 -0.93 217.935 -0.817 217.652 -0.588 c
+217.366 -0.364 217.16 -0.056 217.035 0.338 c
+216.918 0.738 216.881 1.198 216.933 1.72 c
+216.962 1.984 l
+217.057 2.844 217.332 3.52 217.785 4.012 c
+218.233 4.512 218.803 4.751 219.49 4.733 c
+218.314 1.146 m
+218.314 0.558 218.49 0.253 218.843 0.235 c
+219.331 0.205 219.651 0.577 219.799 1.352 c
+219.842 1.72 l
+219.89 2.198 219.917 2.506 219.917 2.645 c
+219.917 3.222 219.732 3.528 219.372 3.557 c
+219.096 3.564 218.872 3.439 218.696 3.175 c
+218.527 2.917 218.421 2.547 218.373 2.057 c
+218.332 1.565 218.314 1.263 218.314 1.146 c
+223.767 4.629 m
+223.68 4.116 l
+224.062 4.534 224.491 4.74 224.973 4.733 c
+225.491 4.71 225.829 4.469 225.987 3.998 c
+226.398 4.498 226.857 4.74 227.368 4.733 c
+227.758 4.722 228.052 4.575 228.251 4.292 c
+228.457 4.016 228.559 3.619 228.559 3.101 c
+228.53 2.645 l
+227.956 -0.838 l
+226.56 -0.838 l
+227.133 2.645 l
+227.163 2.925 l
+227.192 3.336 227.052 3.542 226.751 3.542 c
+226.516 3.542 226.3 3.373 226.105 3.042 c
+226.075 2.807 l
+225.443 -0.838 l
+224.046 -0.838 l
+224.62 2.645 l
+224.649 2.925 l
+224.679 3.336 224.546 3.542 224.252 3.542 c
+224.106 3.542 223.981 3.501 223.885 3.424 c
+223.786 3.343 223.697 3.252 223.62 3.145 c
+222.93 -0.838 l
+221.533 -0.838 l
+222.474 4.629 l
+h
+229.232 -1.47 m
+228.174 -1.47 l
+231.701 6.526 l
+232.76 6.526 l
+h
+f
+Q
+q 1 0 0 1 127.5482 553.4567 cm
+0 0 m
+-2.23 0 l
+-2.224 -16.602 l
+6.802 -16.602 l
+6.802 -14.548 l
+0.007 -14.548 l
+h
+f
+Q
+q 1 0 0 1 142.9453 539.769 cm
+0 0 m
+-0.559 -0.584 -1.496 -1.166 -2.764 -1.166 c
+-4.461 -1.166 -5.145 -0.331 -5.145 0.76 c
+-5.145 2.406 -4.006 3.193 -1.573 3.193 c
+-1.114 3.193 -0.379 3.141 0 3.068 c
+h
+-2.308 10.065 m
+-4.108 10.065 -5.762 9.425 -7.052 8.363 c
+-6.262 6.996 l
+-5.351 7.53 -4.233 8.062 -2.635 8.062 c
+-0.812 8.062 0 7.121 0 5.552 c
+0 4.74 l
+-0.357 4.818 -1.091 4.868 -1.548 4.868 c
+-5.45 4.868 -7.427 3.498 -7.427 0.635 c
+-7.427 -1.926 -5.858 -3.219 -3.473 -3.219 c
+-1.867 -3.219 -0.331 -2.484 0.202 -1.294 c
+0.61 -2.914 l
+2.179 -2.914 l
+2.179 5.578 l
+2.179 8.264 1.014 10.065 -2.308 10.065 c
+f
+Q
+q 1 0 0 1 152.3235 538.5263 cm
+0 0 m
+-0.834 0 -1.569 0.103 -2.128 0.357 c
+-2.128 8.037 l
+-1.368 8.669 -0.43 9.125 0.76 9.125 c
+2.917 9.125 3.752 7.603 3.752 5.145 c
+3.752 1.646 2.41 0 0 0 c
+0.937 11.308 m
+-1.056 11.308 -2.128 9.948 y
+-2.128 12.09 l
+-2.135 14.93 l
+-4.314 14.93 l
+-4.31 -1.267 l
+-3.219 -1.724 -1.723 -1.977 -0.1 -1.977 c
+4.056 -1.977 6.059 0.684 6.059 5.274 c
+6.059 8.896 4.207 11.308 0.937 11.308 c
+f
+Q
+q 1 0 0 1 103.2978 551.6561 cm
+0 0 m
+1.977 0 3.246 -0.658 4.083 -1.319 c
+5.042 0.342 l
+3.734 1.488 1.977 2.106 0.1 2.106 c
+-4.641 2.106 -7.96 -0.786 -7.96 -6.614 c
+-7.96 -12.725 -4.376 -15.106 -0.279 -15.106 c
+1.775 -15.106 3.524 -14.626 4.663 -14.144 c
+4.619 -7.617 l
+4.619 -5.564 l
+-1.466 -5.564 l
+-1.466 -7.617 l
+2.411 -7.617 l
+2.459 -12.571 l
+1.951 -12.824 1.066 -13.026 -0.128 -13.026 c
+-3.424 -13.026 -5.629 -10.954 -5.629 -6.589 c
+-5.629 -2.153 -3.347 0 0 0 c
+f
+Q
+q 1 0 0 1 118.9894 553.4567 cm
+0 0 m
+-2.183 0 l
+-2.176 -2.788 l
+-2.176 -12.421 l
+-2.176 -15.106 -1.01 -16.907 2.311 -16.907 c
+2.77 -16.907 3.219 -16.867 3.657 -16.786 c
+3.657 -14.827 l
+3.341 -14.875 3.002 -14.904 2.643 -14.904 c
+0.816 -14.904 0.004 -13.967 0.004 -12.394 c
+0.004 -5.755 l
+3.657 -5.755 l
+3.657 -3.928 l
+0.004 -3.928 l
+h
+f
+Q
+111.276 549.529 2.179 -12.675 re
+f
+111.276 553.457 2.179 -2.179 re
+f
+0 0.672 0.855 0 k
+q 1 0 0 1 85.9054 541.8301 cm
+0 0 m
+-2.815 8.665 l
+-8.397 25.841 l
+-8.683 26.723 -9.936 26.723 -10.223 25.841 c
+-15.805 8.665 l
+-34.341 8.665 l
+-39.923 25.841 l
+-40.209 26.723 -41.458 26.723 -41.745 25.841 c
+-47.328 8.665 l
+-50.146 0 l
+-50.403 -0.794 -50.12 -1.658 -49.447 -2.146 c
+-25.073 -19.854 l
+-0.698 -2.146 l
+-0.025 -1.658 0.257 -0.794 0 0 c
+f
+Q
+0.031 0.844 0.875 0.004 k
+q 1 0 0 1 60.8329 521.9758 cm
+0 0 m
+9.267 28.519 l
+-9.268 28.519 l
+h
+f
+Q
+0 0.672 0.855 0 k
+q 1 0 0 1 60.8329 521.9758 cm
+0 0 m
+-9.268 28.519 l
+-22.255 28.519 l
+h
+f
+Q
+0 0.43 0.871 0 k
+q 1 0 0 1 38.5778 550.4952 cm
+0 0 m
+-2.818 -8.665 l
+-3.075 -9.459 -2.792 -10.323 -2.119 -10.811 c
+22.255 -28.517 l
+h
+f
+Q
+0.031 0.844 0.875 0.004 k
+q 1 0 0 1 38.5778 550.4952 cm
+0 0 m
+12.987 0 l
+7.405 17.176 l
+7.119 18.058 5.869 18.058 5.582 17.176 c
+h
+f
+Q
+0 0.672 0.855 0 k
+q 1 0 0 1 60.8329 521.9758 cm
+0 0 m
+9.267 28.519 l
+22.258 28.519 l
+h
+f
+Q
+0 0.43 0.871 0 k
+q 1 0 0 1 83.0908 550.4952 cm
+0 0 m
+2.815 -8.665 l
+3.072 -9.459 2.789 -10.323 2.117 -10.811 c
+-22.258 -28.517 l
+h
+f
+Q
+0.031 0.844 0.875 0.004 k
+q 1 0 0 1 83.0908 550.4952 cm
+0 0 m
+-12.991 0 l
+-7.408 17.176 l
+-7.121 18.058 -5.868 18.058 -5.582 17.176 c
+h
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 537.4641 538.8828 cm
+0 0 m
+-0.559 -0.706 l
+-2.168 -2.58 -4.513 -3.513 -7.599 -3.513 c
+-10.356 -3.513 -12.517 -2.601 -14.082 -0.779 c
+-15.64 1.044 -16.441 3.62 -16.478 6.953 c
+-16.478 11.936 l
+-16.478 15.501 -15.769 18.172 -14.346 19.946 c
+-12.917 21.718 -10.782 22.607 -7.938 22.607 c
+-5.519 22.607 -3.628 21.931 -2.263 20.579 c
+-0.893 19.227 -0.14 17.301 0 14.802 c
+-3.19 14.802 l
+-3.318 16.378 -3.759 17.613 -4.513 18.506 c
+-5.259 19.395 -6.394 19.844 -7.923 19.844 c
+-9.745 19.844 -11.083 19.245 -11.936 18.051 c
+-12.788 16.864 -13.23 14.963 -13.259 12.347 c
+-13.259 7.159 l
+-13.259 4.601 -12.773 2.635 -11.803 1.264 c
+-10.826 -0.099 -9.419 -0.779 -7.585 -0.779 c
+-5.872 -0.779 -4.565 -0.363 -3.66 0.47 c
+-3.19 0.912 l
+-3.19 6.776 l
+-7.834 6.776 l
+-7.834 9.496 l
+0 9.496 l
+h
+8.673 -3.16 -3.19 25.414 re
+28.677 19.491 m
+21.99 19.491 l
+21.99 -3.16 l
+18.8 -3.16 l
+18.8 19.491 l
+12.127 19.491 l
+12.127 22.254 l
+28.677 22.254 l
+h
+55.982 4.91 m
+55.834 2.183 55.062 0.096 53.674 -1.352 c
+52.292 -2.793 50.33 -3.513 47.794 -3.513 c
+45.244 -3.513 43.215 -2.543 41.709 -0.602 c
+40.209 1.338 39.459 3.961 39.459 7.277 c
+39.459 11.892 l
+39.459 15.192 40.228 17.804 41.767 19.726 c
+43.314 21.644 45.42 22.607 48.088 22.607 c
+50.536 22.607 52.431 21.865 53.776 20.388 c
+55.128 18.918 55.864 16.816 55.982 14.082 c
+52.747 14.082 l
+52.608 16.158 52.174 17.639 51.44 18.521 c
+50.704 19.403 49.587 19.844 48.088 19.844 c
+46.353 19.844 45.019 19.16 44.09 17.801 c
+43.156 16.449 42.694 14.464 42.694 11.848 c
+42.694 7.188 l
+42.694 4.619 43.123 2.65 43.987 1.279 c
+44.858 -0.095 46.125 -0.779 47.794 -0.779 c
+49.458 -0.779 50.653 -0.367 51.38 0.456 c
+52.115 1.279 52.571 2.764 52.747 4.91 c
+h
+77 -3.16 m
+73.796 -3.16 l
+73.796 8.584 l
+63.566 8.584 l
+63.566 -3.16 l
+60.362 -3.16 l
+60.362 22.254 l
+63.566 22.254 l
+63.566 11.333 l
+73.796 11.333 l
+73.796 22.254 l
+77 22.254 l
+h
+94.838 8.584 m
+85.858 8.584 l
+85.858 -0.426 l
+96.323 -0.426 l
+96.323 -3.16 l
+82.652 -3.16 l
+82.652 22.254 l
+96.146 22.254 l
+96.146 19.491 l
+85.858 19.491 l
+85.858 11.333 l
+94.838 11.333 l
+h
+112.319 3.469 m
+103.734 3.469 l
+101.75 -3.16 l
+98.473 -3.16 l
+106.66 22.254 l
+109.394 22.254 l
+117.61 -3.16 l
+114.318 -3.16 l
+h
+104.573 6.232 m
+111.495 6.232 l
+108.027 17.786 l
+h
+133.754 19.491 m
+127.066 19.491 l
+127.066 -3.16 l
+123.877 -3.16 l
+123.877 19.491 l
+117.203 19.491 l
+117.203 22.254 l
+133.754 22.254 l
+h
+156.221 3.263 m
+156.221 4.528 155.876 5.498 155.193 6.174 c
+154.505 6.849 153.267 7.504 151.474 8.143 c
+149.68 8.79 148.306 9.467 147.358 10.172 c
+146.406 10.878 145.697 11.678 145.227 12.582 c
+144.756 13.483 144.521 14.512 144.521 15.669 c
+144.521 17.687 145.19 19.348 146.535 20.652 c
+147.876 21.953 149.636 22.607 151.812 22.607 c
+153.3 22.607 154.626 22.269 155.795 21.593 c
+156.96 20.924 157.857 19.998 158.485 18.815 c
+159.11 17.628 159.426 16.324 159.426 14.905 c
+156.221 14.905 l
+156.221 16.47 155.839 17.687 155.075 18.55 c
+154.318 19.41 153.23 19.844 151.812 19.844 c
+150.526 19.844 149.526 19.48 148.813 18.756 c
+148.107 18.04 147.754 17.029 147.754 15.728 c
+147.754 14.659 148.137 13.762 148.901 13.039 c
+149.673 12.31 150.86 11.664 152.458 11.098 c
+154.964 10.275 156.754 9.253 157.824 8.04 c
+158.9 6.835 159.44 5.251 159.44 3.293 c
+159.44 1.235 158.768 -0.419 157.427 -1.66 c
+156.093 -2.896 154.274 -3.513 151.974 -3.513 c
+150.493 -3.513 149.125 -3.189 147.872 -2.543 c
+146.626 -1.896 145.645 -0.992 144.932 0.177 c
+144.216 1.353 143.86 2.694 143.86 4.204 c
+147.064 4.204 l
+147.064 2.635 147.505 1.415 148.387 0.544 c
+149.269 -0.33 150.463 -0.764 151.974 -0.764 c
+153.385 -0.764 154.443 -0.407 155.149 0.309 c
+155.862 1.033 156.221 2.018 156.221 3.263 c
+180.325 -3.16 m
+177.12 -3.16 l
+177.12 8.584 l
+166.889 8.584 l
+166.889 -3.16 l
+163.685 -3.16 l
+163.685 22.254 l
+166.889 22.254 l
+166.889 11.333 l
+177.12 11.333 l
+177.12 22.254 l
+180.325 22.254 l
+h
+198.161 8.584 m
+189.181 8.584 l
+189.181 -0.426 l
+199.646 -0.426 l
+199.646 -3.16 l
+185.976 -3.16 l
+185.976 22.254 l
+199.47 22.254 l
+199.47 19.491 l
+189.181 19.491 l
+189.181 11.333 l
+198.161 11.333 l
+h
+215.969 8.584 m
+206.988 8.584 l
+206.988 -0.426 l
+217.454 -0.426 l
+217.454 -3.16 l
+203.784 -3.16 l
+203.784 22.254 l
+217.278 22.254 l
+217.278 19.491 l
+206.988 19.491 l
+206.988 11.333 l
+215.969 11.333 l
+h
+236.798 19.491 m
+230.11 19.491 l
+230.11 -3.16 l
+226.92 -3.16 l
+226.92 19.491 l
+220.247 19.491 l
+220.247 22.254 l
+236.798 22.254 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+35.668 501.669 238.665 -21.46 re
+f
+0 0 0 0 k
+q 1 0 0 1 142.3903 490.78 cm
+0 0 m
+0.47 4.763 l
+4.997 4.763 l
+4.997 3.205 l
+1.955 3.205 l
+1.793 1.206 l
+2.146 1.441 2.506 1.559 2.881 1.559 c
+3.653 1.559 4.248 1.283 4.659 0.736 c
+5.078 0.184 5.292 -0.595 5.292 -1.602 c
+5.292 -2.535 5.041 -3.289 4.542 -3.865 c
+4.042 -4.446 3.381 -4.733 2.558 -4.733 c
+2.057 -4.733 1.595 -4.615 1.176 -4.38 c
+0.754 -4.145 0.419 -3.821 0.176 -3.41 c
+-0.059 -2.991 -0.177 -2.513 -0.177 -1.984 c
+1.617 -1.984 l
+1.646 -2.377 1.738 -2.682 1.897 -2.896 c
+2.061 -3.112 2.278 -3.218 2.543 -3.218 c
+2.837 -3.218 3.065 -3.079 3.233 -2.792 c
+3.399 -2.499 3.484 -2.065 3.484 -1.484 c
+3.484 -0.947 3.385 -0.54 3.19 -0.264 c
+2.992 0.008 2.72 0.148 2.367 0.148 c
+2.021 0.148 1.756 0.03 1.573 -0.205 c
+1.455 -0.367 l
+h
+6.868 -3.674 m
+6.868 -3.38 6.963 -3.141 7.162 -2.954 c
+7.357 -2.77 7.611 -2.675 7.927 -2.675 c
+8.228 -2.675 8.478 -2.77 8.676 -2.954 c
+8.882 -3.141 8.985 -3.38 8.985 -3.674 c
+8.985 -3.979 8.882 -4.226 8.676 -4.409 c
+8.478 -4.586 8.228 -4.674 7.927 -4.674 c
+7.622 -4.674 7.368 -4.582 7.162 -4.394 c
+6.963 -4.211 6.868 -3.968 6.868 -3.674 c
+16.709 -1.176 m
+15.769 -1.176 l
+15.769 -4.6 l
+13.887 -4.6 l
+13.887 4.763 l
+16.9 4.763 l
+17.849 4.763 18.58 4.516 19.09 4.028 c
+19.609 3.535 19.869 2.841 19.869 1.941 c
+19.869 0.695 19.418 -0.176 18.518 -0.675 c
+20.149 -4.512 l
+20.149 -4.6 l
+18.121 -4.6 l
+h
+15.769 0.397 m
+16.841 0.397 l
+17.224 0.397 17.506 0.518 17.694 0.765 c
+17.878 1.018 17.973 1.357 17.973 1.779 c
+17.973 2.72 17.61 3.19 16.885 3.19 c
+15.769 3.19 l
+h
+26.223 -0.544 m
+23.283 -0.544 l
+23.283 -3.027 l
+26.767 -3.027 l
+26.767 -4.6 l
+21.387 -4.6 l
+21.387 4.763 l
+26.752 4.763 l
+26.752 3.19 l
+23.283 3.19 l
+23.283 0.971 l
+26.223 0.971 l
+h
+31.085 -2.028 m
+32.658 4.763 l
+34.759 4.763 l
+32.07 -4.6 l
+30.1 -4.6 l
+27.41 4.763 l
+29.512 4.763 l
+h
+37.846 -4.6 -1.896 9.363 re
+44.373 -0.544 m
+41.433 -0.544 l
+41.433 -3.027 l
+44.917 -3.027 l
+44.917 -4.6 l
+39.536 -4.6 l
+39.536 4.763 l
+44.901 4.763 l
+44.901 3.19 l
+41.433 3.19 l
+41.433 0.971 l
+44.373 0.971 l
+h
+52.267 -1.117 m
+53.148 4.763 l
+55.029 4.763 l
+53.354 -4.6 l
+51.458 -4.6 l
+50.37 0.912 l
+49.297 -4.6 l
+47.386 -4.6 l
+45.711 4.763 l
+47.592 4.763 l
+48.473 -1.117 l
+49.576 4.763 l
+51.164 4.763 l
+h
+62.284 0.559 m
+63.695 4.763 l
+65.753 4.763 l
+63.239 -1.205 l
+63.239 -4.6 l
+61.328 -4.6 l
+61.328 -1.205 l
+58.814 4.763 l
+60.872 4.763 l
+h
+73.231 -0.764 m
+73.231 -2.021 72.929 -2.994 72.334 -3.689 c
+71.735 -4.388 70.912 -4.733 69.865 -4.733 c
+68.813 -4.733 67.986 -4.391 67.38 -3.704 c
+66.781 -3.009 66.476 -2.042 66.469 -0.808 c
+66.469 0.794 l
+66.469 2.076 66.767 3.08 67.365 3.807 c
+67.961 4.532 68.792 4.896 69.85 4.896 c
+70.887 4.896 71.706 4.535 72.304 3.822 c
+72.911 3.117 73.22 2.12 73.231 0.838 c
+h
+71.334 0.809 m
+71.334 1.65 71.209 2.278 70.967 2.691 c
+70.731 3.102 70.357 3.308 69.85 3.308 c
+69.35 3.308 68.975 3.105 68.732 2.705 c
+68.497 2.311 68.373 1.713 68.365 0.912 c
+68.365 -0.764 l
+68.365 -1.579 68.487 -2.182 68.732 -2.572 c
+68.975 -2.965 69.354 -3.16 69.865 -3.16 c
+70.353 -3.16 70.717 -2.969 70.952 -2.587 c
+71.195 -2.204 71.323 -1.616 71.334 -0.823 c
+h
+80.907 4.763 m
+80.907 -1.72 l
+80.896 -2.69 80.628 -3.436 80.099 -3.954 c
+79.57 -4.475 78.809 -4.733 77.821 -4.733 c
+76.821 -4.733 76.057 -4.475 75.527 -3.954 c
+74.999 -3.424 74.733 -2.668 74.733 -1.675 c
+74.733 4.763 l
+76.63 4.763 l
+76.63 -1.675 l
+76.63 -2.215 76.711 -2.597 76.88 -2.822 c
+77.056 -3.05 77.368 -3.16 77.821 -3.16 c
+78.269 -3.16 78.577 -3.05 78.746 -2.822 c
+78.912 -2.597 79 -2.23 79.011 -1.72 c
+79.011 4.763 l
+h
+85.398 -1.176 m
+84.457 -1.176 l
+84.457 -4.6 l
+82.575 -4.6 l
+82.575 4.763 l
+85.588 4.763 l
+86.537 4.763 87.269 4.516 87.779 4.028 c
+88.297 3.535 88.558 2.841 88.558 1.941 c
+88.558 0.695 88.106 -0.176 87.206 -0.675 c
+88.838 -4.512 l
+88.838 -4.6 l
+86.809 -4.6 l
+h
+84.457 0.397 m
+85.53 0.397 l
+85.912 0.397 86.195 0.518 86.382 0.765 c
+86.566 1.018 86.662 1.357 86.662 1.779 c
+86.662 2.72 86.298 3.19 85.574 3.19 c
+84.457 3.19 l
+h
+99.281 -1.117 m
+100.163 4.763 l
+102.045 4.763 l
+100.368 -4.6 l
+98.473 -4.6 l
+97.384 0.912 l
+96.312 -4.6 l
+94.401 -4.6 l
+92.725 4.763 l
+94.606 4.763 l
+95.489 -1.117 l
+96.591 4.763 l
+98.178 4.763 l
+h
+109.956 -0.764 m
+109.956 -2.021 109.654 -2.994 109.06 -3.689 c
+108.461 -4.388 107.638 -4.733 106.59 -4.733 c
+105.539 -4.733 104.713 -4.391 104.106 -3.704 c
+103.507 -3.009 103.202 -2.042 103.194 -0.808 c
+103.194 0.794 l
+103.194 2.076 103.493 3.08 104.091 3.807 c
+104.686 4.532 105.517 4.896 106.575 4.896 c
+107.612 4.896 108.431 4.535 109.03 3.822 c
+109.637 3.117 109.945 2.12 109.956 0.838 c
+h
+108.06 0.809 m
+108.06 1.65 107.935 2.278 107.693 2.691 c
+107.458 3.102 107.082 3.308 106.575 3.308 c
+106.076 3.308 105.701 3.105 105.459 2.705 c
+105.224 2.311 105.098 1.713 105.091 0.912 c
+105.091 -0.764 l
+105.091 -1.579 105.212 -2.182 105.459 -2.572 c
+105.701 -2.965 106.08 -3.16 106.59 -3.16 c
+107.079 -3.16 107.443 -2.969 107.678 -2.587 c
+107.921 -2.204 108.049 -1.616 108.06 -0.823 c
+h
+114.369 -1.176 m
+113.429 -1.176 l
+113.429 -4.6 l
+111.547 -4.6 l
+111.547 4.763 l
+114.56 4.763 l
+115.509 4.763 116.24 4.516 116.75 4.028 c
+117.269 3.535 117.53 2.841 117.53 1.941 c
+117.53 0.695 117.078 -0.176 116.178 -0.675 c
+117.809 -4.512 l
+117.809 -4.6 l
+115.781 -4.6 l
+h
+113.429 0.397 m
+114.502 0.397 l
+114.884 0.397 115.167 0.518 115.354 0.765 c
+115.538 1.018 115.634 1.357 115.634 1.779 c
+115.634 2.72 115.27 3.19 114.546 3.19 c
+113.429 3.19 l
+h
+121.689 -0.984 m
+120.94 -1.925 l
+120.94 -4.6 l
+119.044 -4.6 l
+119.044 4.763 l
+120.94 4.763 l
+120.94 0.676 l
+121.543 1.69 l
+123.277 4.763 l
+125.6 4.763 l
+122.909 0.647 l
+125.644 -4.6 l
+123.395 -4.6 l
+h
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 471.367 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 464.5277 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.596 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.279 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+23.221 -0.646 m
+24.353 -0.646 l
+24.353 -1.264 l
+21.045 -1.264 l
+21.045 -0.646 l
+22.31 -0.646 l
+22.31 2.896 l
+21.384 2.896 l
+21.384 3.514 l
+23.221 3.514 l
+h
+28.384 0.485 m
+28.384 0.21 28.347 -0.04 28.281 -0.264 c
+28.212 -0.482 28.108 -0.669 27.973 -0.823 c
+27.833 -0.981 27.657 -1.103 27.443 -1.19 c
+27.227 -1.278 26.973 -1.323 26.679 -1.323 c
+26.404 -1.323 26.157 -1.278 25.944 -1.19 c
+25.739 -1.103 25.566 -0.981 25.43 -0.823 c
+25.29 -0.669 25.187 -0.482 25.121 -0.264 c
+25.051 -0.04 25.018 0.21 25.018 0.485 c
+25.018 0.738 25.047 0.974 25.106 1.191 c
+25.172 1.415 25.275 1.606 25.415 1.764 c
+25.55 1.929 25.727 2.058 25.944 2.146 c
+26.157 2.234 26.414 2.278 26.709 2.278 c
+27.021 2.278 27.281 2.234 27.487 2.146 c
+27.701 2.058 27.873 1.929 28.002 1.764 c
+28.138 1.606 28.237 1.415 28.296 1.191 c
+28.355 0.974 28.384 0.738 28.384 0.485 c
+27.429 0.485 m
+27.429 0.691 27.414 0.867 27.385 1.014 c
+27.362 1.162 27.326 1.282 27.267 1.382 c
+27.208 1.477 27.135 1.548 27.046 1.588 c
+26.959 1.635 26.848 1.661 26.724 1.661 c
+26.458 1.661 26.267 1.562 26.15 1.367 c
+26.032 1.18 25.974 0.886 25.974 0.485 c
+25.974 0.063 26.032 -0.243 26.15 -0.426 c
+26.267 -0.613 26.444 -0.706 26.679 -0.706 c
+26.804 -0.706 26.918 -0.687 27.017 -0.646 c
+27.113 -0.599 27.194 -0.525 27.252 -0.426 c
+27.318 -0.33 27.362 -0.206 27.385 -0.058 c
+27.414 0.088 27.429 0.268 27.429 0.485 c
+30.769 -2.66 m
+30.552 -2.66 30.361 -2.635 30.196 -2.587 c
+30.027 -2.547 29.887 -2.484 29.77 -2.396 c
+29.652 -2.315 29.552 -2.219 29.475 -2.102 c
+29.406 -1.984 29.358 -1.855 29.328 -1.72 c
+30.225 -1.617 l
+30.262 -1.753 30.331 -1.859 30.431 -1.94 c
+30.526 -2.028 30.651 -2.072 30.799 -2.072 c
+30.886 -2.072 30.967 -2.057 31.048 -2.028 c
+31.125 -1.999 31.195 -1.944 31.254 -1.866 c
+31.313 -1.797 31.357 -1.705 31.387 -1.587 c
+31.424 -1.469 31.445 -1.323 31.445 -1.147 c
+31.445 -0.955 l
+31.445 -0.889 31.449 -0.831 31.46 -0.779 c
+31.46 -0.588 l
+31.445 -0.588 l
+31.346 -0.816 31.202 -0.977 31.019 -1.072 c
+30.832 -1.172 30.626 -1.22 30.402 -1.22 c
+30.196 -1.22 30.012 -1.183 29.858 -1.103 c
+29.71 -1.014 29.582 -0.897 29.475 -0.75 c
+29.376 -0.596 29.303 -0.411 29.255 -0.206 c
+29.203 0.008 29.182 0.243 29.182 0.5 c
+29.182 0.771 29.203 1.018 29.255 1.235 c
+29.313 1.448 29.394 1.632 29.505 1.779 c
+29.612 1.933 29.743 2.051 29.901 2.132 c
+30.056 2.22 30.244 2.263 30.46 2.263 c
+30.556 2.263 30.655 2.253 30.755 2.234 c
+30.85 2.213 30.938 2.18 31.019 2.132 c
+31.107 2.08 31.185 2.018 31.254 1.941 c
+31.331 1.86 31.393 1.768 31.445 1.661 c
+31.46 1.661 l
+31.46 1.808 l
+31.467 1.867 31.474 1.918 31.474 1.97 c
+31.482 2.028 31.489 2.076 31.489 2.117 c
+31.497 2.165 31.507 2.198 31.519 2.22 c
+32.371 2.22 l
+32.36 2.139 32.349 2.028 32.342 1.881 c
+32.342 1.411 l
+32.342 -1.161 l
+32.342 -1.415 32.305 -1.635 32.239 -1.822 c
+32.17 -2.007 32.066 -2.161 31.931 -2.279 c
+31.79 -2.404 31.625 -2.499 31.43 -2.558 c
+31.232 -2.624 31.011 -2.66 30.769 -2.66 c
+31.46 0.53 m
+31.46 0.742 31.434 0.919 31.387 1.058 c
+31.346 1.205 31.291 1.323 31.225 1.411 c
+31.166 1.5 31.096 1.558 31.019 1.588 c
+30.938 1.625 30.861 1.646 30.784 1.646 c
+30.684 1.646 30.593 1.621 30.504 1.573 c
+30.424 1.532 30.358 1.463 30.298 1.367 c
+30.248 1.279 30.203 1.162 30.167 1.014 c
+30.137 0.875 30.122 0.706 30.122 0.5 c
+30.122 0.125 30.181 -0.154 30.298 -0.338 c
+30.416 -0.515 30.578 -0.602 30.784 -0.602 c
+30.85 -0.602 30.923 -0.588 31.004 -0.559 c
+31.092 -0.522 31.166 -0.463 31.225 -0.382 c
+31.291 -0.294 31.346 -0.176 31.387 -0.029 c
+31.434 0.118 31.46 0.301 31.46 0.53 c
+38.115 -2.631 m
+38.115 3.514 l
+40.041 3.514 l
+40.041 2.896 l
+38.967 2.896 l
+38.967 -2.013 l
+40.041 -2.013 l
+40.041 -2.631 l
+h
+42.013 0.838 1.867 -0.794 re
+42.013 0.044 m
+47.677 -1.264 m
+47.677 0.721 l
+47.677 1.022 47.632 1.242 47.544 1.382 c
+47.463 1.529 47.328 1.602 47.133 1.602 c
+47.023 1.602 46.919 1.577 46.824 1.529 c
+46.736 1.477 46.655 1.411 46.589 1.323 c
+46.53 1.235 46.479 1.124 46.441 1 c
+46.412 0.882 46.398 0.75 46.398 0.603 c
+46.398 -1.264 l
+45.487 -1.264 l
+45.487 1.44 l
+45.487 1.661 l
+45.487 1.75 45.479 1.827 45.472 1.897 c
+45.472 2.087 l
+45.472 2.22 l
+46.324 2.22 l
+46.331 2.19 46.339 2.146 46.339 2.087 c
+46.339 1.897 l
+46.346 1.827 46.354 1.756 46.354 1.691 c
+46.362 1.621 46.368 1.565 46.368 1.529 c
+46.383 1.529 l
+46.501 1.793 46.651 1.985 46.838 2.103 c
+47.023 2.22 47.243 2.278 47.5 2.278 c
+47.684 2.278 47.846 2.249 47.985 2.19 c
+48.122 2.132 48.235 2.043 48.323 1.926 c
+48.411 1.808 48.474 1.665 48.515 1.5 c
+48.562 1.341 48.588 1.154 48.588 0.941 c
+48.588 -1.264 l
+h
+55.17 -1.323 m
+54.883 -1.323 54.64 -1.282 54.435 -1.205 c
+54.229 -1.117 54.056 -0.995 53.92 -0.837 c
+53.78 -0.683 53.678 -0.496 53.612 -0.279 c
+53.541 -0.055 53.508 0.191 53.508 0.456 c
+53.508 0.75 53.541 1.007 53.612 1.235 c
+53.689 1.459 53.795 1.646 53.934 1.793 c
+54.082 1.947 54.258 2.065 54.464 2.146 c
+54.67 2.234 54.905 2.278 55.17 2.278 c
+55.394 2.278 55.596 2.249 55.772 2.19 c
+55.949 2.132 56.099 2.047 56.228 1.941 c
+56.353 1.841 56.456 1.72 56.537 1.573 c
+56.614 1.434 56.669 1.282 56.698 1.118 c
+55.787 1.073 l
+55.758 1.249 55.688 1.389 55.581 1.5 c
+55.482 1.606 55.339 1.661 55.155 1.661 c
+54.908 1.661 54.732 1.558 54.626 1.353 c
+54.515 1.154 54.464 0.867 54.464 0.485 c
+54.464 -0.309 54.699 -0.706 55.17 -0.706 c
+55.335 -0.706 55.478 -0.654 55.596 -0.544 c
+55.713 -0.437 55.787 -0.276 55.816 -0.058 c
+56.728 -0.103 l
+56.698 -0.272 56.643 -0.426 56.566 -0.573 c
+56.496 -0.72 56.394 -0.852 56.257 -0.97 c
+56.128 -1.08 55.97 -1.168 55.787 -1.234 c
+55.611 -1.294 55.405 -1.323 55.17 -1.323 c
+60.876 0.485 m
+60.876 0.21 60.839 -0.04 60.774 -0.264 c
+60.704 -0.482 60.601 -0.669 60.465 -0.823 c
+60.325 -0.981 60.149 -1.103 59.935 -1.19 c
+59.719 -1.278 59.465 -1.323 59.172 -1.323 c
+58.896 -1.323 58.649 -1.278 58.436 -1.19 c
+58.231 -1.103 58.058 -0.981 57.922 -0.823 c
+57.782 -0.669 57.68 -0.482 57.613 -0.264 c
+57.543 -0.04 57.51 0.21 57.51 0.485 c
+57.51 0.738 57.539 0.974 57.599 1.191 c
+57.665 1.415 57.768 1.606 57.907 1.764 c
+58.043 1.929 58.22 2.058 58.436 2.146 c
+58.649 2.234 58.906 2.278 59.201 2.278 c
+59.513 2.278 59.774 2.234 59.98 2.146 c
+60.193 2.058 60.365 1.929 60.494 1.764 c
+60.63 1.606 60.729 1.415 60.788 1.191 c
+60.847 0.974 60.876 0.738 60.876 0.485 c
+59.921 0.485 m
+59.921 0.691 59.906 0.867 59.877 1.014 c
+59.855 1.162 59.818 1.282 59.76 1.382 c
+59.7 1.477 59.627 1.548 59.538 1.588 c
+59.451 1.635 59.34 1.661 59.216 1.661 c
+58.95 1.661 58.76 1.562 58.642 1.367 c
+58.524 1.18 58.466 0.886 58.466 0.485 c
+58.466 0.063 58.524 -0.243 58.642 -0.426 c
+58.76 -0.613 58.936 -0.706 59.172 -0.706 c
+59.297 -0.706 59.411 -0.687 59.509 -0.646 c
+59.605 -0.599 59.686 -0.525 59.744 -0.426 c
+59.81 -0.33 59.855 -0.206 59.877 -0.058 c
+59.906 0.088 59.921 0.268 59.921 0.485 c
+62.615 2.22 m
+62.615 0.264 l
+62.615 0.125 62.622 0 62.644 -0.118 c
+62.663 -0.228 62.696 -0.32 62.746 -0.397 c
+62.794 -0.478 62.854 -0.54 62.923 -0.588 c
+62.989 -0.628 63.074 -0.646 63.173 -0.646 c
+63.261 -0.646 63.342 -0.628 63.423 -0.588 c
+63.511 -0.54 63.585 -0.47 63.644 -0.382 c
+63.702 -0.287 63.746 -0.176 63.776 -0.058 c
+63.812 0.066 63.835 0.206 63.835 0.353 c
+63.835 2.22 l
+64.731 2.22 l
+64.731 -0.484 l
+64.731 -0.72 l
+64.739 -0.801 64.746 -0.878 64.746 -0.955 c
+64.746 -1.147 l
+64.753 -1.198 64.76 -1.234 64.76 -1.264 c
+63.908 -1.264 l
+63.897 -1.234 63.886 -1.198 63.879 -1.147 c
+63.879 -0.955 l
+63.879 -0.889 63.872 -0.819 63.864 -0.75 c
+63.864 -0.573 l
+63.849 -0.573 l
+63.731 -0.837 63.577 -1.029 63.394 -1.147 c
+63.217 -1.264 63.015 -1.323 62.791 -1.323 c
+62.586 -1.323 62.412 -1.286 62.276 -1.22 c
+62.137 -1.153 62.027 -1.058 61.938 -0.941 c
+61.857 -0.823 61.799 -0.687 61.762 -0.529 c
+61.732 -0.364 61.718 -0.187 61.718 0 c
+61.718 2.22 l
+h
+67.983 -1.264 m
+67.983 0.721 l
+67.983 1.022 67.939 1.242 67.851 1.382 c
+67.77 1.529 67.634 1.602 67.44 1.602 c
+67.33 1.602 67.226 1.577 67.131 1.529 c
+67.043 1.477 66.962 1.411 66.896 1.323 c
+66.837 1.235 66.785 1.124 66.748 1 c
+66.719 0.882 66.705 0.75 66.705 0.603 c
+66.705 -1.264 l
+65.793 -1.264 l
+65.793 1.44 l
+65.793 1.661 l
+65.793 1.75 65.786 1.827 65.778 1.897 c
+65.778 2.087 l
+65.778 2.22 l
+66.631 2.22 l
+66.638 2.19 66.646 2.146 66.646 2.087 c
+66.646 1.897 l
+66.653 1.827 66.661 1.756 66.661 1.691 c
+66.667 1.621 66.675 1.565 66.675 1.529 c
+66.69 1.529 l
+66.808 1.793 66.958 1.985 67.145 2.103 c
+67.33 2.22 67.55 2.278 67.807 2.278 c
+67.991 2.278 68.153 2.249 68.292 2.19 c
+68.428 2.132 68.542 2.043 68.63 1.926 c
+68.718 1.808 68.781 1.665 68.822 1.5 c
+68.869 1.341 68.895 1.154 68.895 0.941 c
+68.895 -1.264 l
+h
+70.457 1.602 m
+69.913 1.602 l
+69.913 2.22 l
+70.501 2.22 l
+70.78 3.117 l
+71.353 3.117 l
+71.353 2.22 l
+72.587 2.22 l
+72.587 1.602 l
+71.353 1.602 l
+71.353 -0.103 l
+71.353 -0.324 l
+71.361 -0.393 71.382 -0.455 71.412 -0.515 c
+71.448 -0.565 71.504 -0.61 71.573 -0.646 c
+71.65 -0.676 71.764 -0.691 71.912 -0.691 c
+72.047 -0.691 72.184 -0.687 72.323 -0.676 c
+72.46 -0.658 72.591 -0.632 72.72 -0.602 c
+72.72 -1.205 l
+72.639 -1.216 72.562 -1.23 72.485 -1.249 c
+72.404 -1.261 72.327 -1.267 72.25 -1.278 c
+72.169 -1.286 72.08 -1.294 71.985 -1.294 c
+71.897 -1.301 71.798 -1.308 71.691 -1.308 c
+71.504 -1.308 71.342 -1.294 71.206 -1.264 c
+71.078 -1.228 70.964 -1.183 70.868 -1.132 c
+70.78 -1.084 70.706 -1.025 70.648 -0.955 c
+70.588 -0.878 70.545 -0.801 70.515 -0.72 c
+70.486 -0.632 70.464 -0.544 70.457 -0.455 c
+70.445 -0.36 70.442 -0.264 70.442 -0.176 c
+h
+74.285 -2.631 m
+74.285 -2.013 l
+75.359 -2.013 l
+75.359 2.896 l
+74.285 2.896 l
+74.285 3.514 l
+76.211 3.514 l
+76.211 -2.631 l
+h
+f
+Q
+q 1 0 0 1 47.3793 448.9472 cm
+0 0 m
+2.102 0 l
+2.102 -0.574 l
+-0.676 -0.574 l
+-0.676 4.777 l
+0 4.777 l
+h
+3.513 -0.574 -0.646 3.984 re
+3.557 4.453 m
+3.557 4.343 3.528 4.251 3.469 4.174 c
+3.41 4.104 3.314 4.072 3.19 4.072 c
+3.072 4.072 2.977 4.104 2.911 4.174 c
+2.851 4.251 2.822 4.343 2.822 4.453 c
+2.822 4.571 2.851 4.663 2.911 4.733 c
+2.977 4.81 3.072 4.85 3.19 4.85 c
+3.314 4.85 3.41 4.81 3.469 4.733 c
+3.528 4.652 3.557 4.56 3.557 4.453 c
+6.468 0.44 m
+6.468 0.588 6.412 0.709 6.306 0.808 c
+6.196 0.904 5.99 1.022 5.689 1.161 c
+5.343 1.308 5.101 1.429 4.953 1.529 c
+4.806 1.635 4.696 1.753 4.63 1.881 c
+4.561 2.006 4.527 2.164 4.527 2.352 c
+4.527 2.674 4.644 2.944 4.88 3.16 c
+5.115 3.374 5.417 3.484 5.791 3.484 c
+6.173 3.484 6.483 3.37 6.718 3.145 c
+6.953 2.917 7.07 2.631 7.07 2.278 c
+6.423 2.278 l
+6.423 2.454 6.364 2.605 6.247 2.734 c
+6.129 2.859 5.975 2.925 5.791 2.925 c
+5.593 2.925 5.442 2.869 5.336 2.763 c
+5.226 2.664 5.174 2.532 5.174 2.366 c
+5.174 2.238 5.211 2.131 5.292 2.043 c
+5.369 1.962 5.56 1.859 5.865 1.734 c
+6.342 1.547 6.674 1.359 6.85 1.176 c
+7.026 0.999 7.114 0.771 7.114 0.5 c
+7.114 0.147 6.99 -0.133 6.747 -0.339 c
+6.512 -0.544 6.196 -0.647 5.806 -0.647 c
+5.384 -0.647 5.045 -0.53 4.792 -0.294 c
+4.534 -0.052 4.409 0.253 4.409 0.617 c
+5.056 0.617 l
+5.064 0.389 5.134 0.213 5.262 0.087 c
+5.387 -0.03 5.571 -0.088 5.806 -0.088 c
+6.019 -0.088 6.181 -0.04 6.291 0.058 c
+6.408 0.154 6.468 0.282 6.468 0.44 c
+8.804 4.365 m
+8.804 3.41 l
+9.407 3.41 l
+9.407 2.881 l
+8.804 2.881 l
+8.804 0.411 l
+8.804 0.253 8.827 0.135 8.878 0.058 c
+8.937 -0.023 9.025 -0.059 9.143 -0.059 c
+9.231 -0.059 9.319 -0.044 9.407 -0.015 c
+9.407 -0.574 l
+9.261 -0.621 9.106 -0.647 8.951 -0.647 c
+8.694 -0.647 8.5 -0.555 8.364 -0.368 c
+8.224 -0.184 8.158 0.077 8.158 0.411 c
+8.158 2.881 l
+7.555 2.881 l
+7.555 3.41 l
+8.158 3.41 l
+8.158 4.365 l
+h
+13.273 -0.088 m
+13.486 -0.088 13.659 -0.026 13.787 0.103 c
+13.924 0.239 13.997 0.43 14.009 0.675 c
+14.626 0.675 l
+14.603 0.294 14.468 -0.026 14.214 -0.279 c
+13.957 -0.526 13.644 -0.647 13.273 -0.647 c
+12.781 -0.647 12.406 -0.497 12.141 -0.191 c
+11.884 0.121 11.759 0.588 11.759 1.205 c
+11.759 1.646 l
+11.759 2.241 11.884 2.697 12.141 3.013 c
+12.406 3.326 12.781 3.484 13.273 3.484 c
+13.674 3.484 13.993 3.351 14.229 3.087 c
+14.471 2.829 14.603 2.484 14.626 2.043 c
+14.009 2.043 l
+13.986 2.337 13.913 2.557 13.787 2.705 c
+13.67 2.851 13.498 2.925 13.273 2.925 c
+12.979 2.925 12.762 2.826 12.627 2.631 c
+12.486 2.443 12.413 2.135 12.406 1.705 c
+12.406 1.19 l
+12.406 0.72 12.472 0.386 12.612 0.191 c
+12.758 0.004 12.979 -0.088 13.273 -0.088 c
+15.243 1.602 m
+15.243 2.179 15.379 2.634 15.655 2.969 c
+15.937 3.31 16.309 3.484 16.771 3.484 c
+17.231 3.484 17.598 3.314 17.874 2.984 c
+18.157 2.66 18.304 2.212 18.315 1.646 c
+18.315 1.22 l
+18.315 0.65 18.171 0.195 17.888 -0.148 c
+17.613 -0.482 17.246 -0.647 16.787 -0.647 c
+16.323 -0.647 15.952 -0.485 15.669 -0.162 c
+15.393 0.168 15.25 0.61 15.243 1.161 c
+h
+15.89 1.22 m
+15.89 0.816 15.967 0.5 16.125 0.264 c
+16.29 0.029 16.511 -0.088 16.787 -0.088 c
+17.352 -0.088 17.646 0.323 17.668 1.146 c
+17.668 1.602 l
+17.668 2.003 17.583 2.322 17.418 2.557 c
+17.261 2.8 17.043 2.925 16.771 2.925 c
+16.507 2.925 16.29 2.8 16.125 2.557 c
+15.967 2.322 15.89 2.003 15.89 1.602 c
+h
+19.77 3.41 m
+19.785 3.042 l
+20.027 3.337 20.347 3.484 20.74 3.484 c
+21.181 3.484 21.49 3.285 21.666 2.896 c
+21.92 3.285 22.269 3.484 22.71 3.484 c
+23.445 3.484 23.82 3.021 23.842 2.102 c
+23.842 -0.574 l
+23.195 -0.574 l
+23.195 2.043 l
+23.195 2.337 23.14 2.55 23.033 2.69 c
+22.934 2.826 22.761 2.896 22.518 2.896 c
+22.32 2.896 22.159 2.815 22.034 2.66 c
+21.916 2.514 21.847 2.322 21.828 2.087 c
+21.828 -0.574 l
+21.167 -0.574 l
+21.167 2.072 l
+21.167 2.62 20.946 2.896 20.505 2.896 c
+20.17 2.896 19.935 2.734 19.8 2.41 c
+19.8 -0.574 l
+19.153 -0.574 l
+19.153 3.41 l
+h
+25.429 3.41 m
+25.444 3.042 l
+25.687 3.337 26.006 3.484 26.399 3.484 c
+26.84 3.484 27.149 3.285 27.326 2.896 c
+27.579 3.285 27.928 3.484 28.369 3.484 c
+29.104 3.484 29.479 3.021 29.501 2.102 c
+29.501 -0.574 l
+28.854 -0.574 l
+28.854 2.043 l
+28.854 2.337 28.799 2.55 28.693 2.69 c
+28.593 2.826 28.421 2.896 28.178 2.896 c
+27.979 2.896 27.818 2.815 27.693 2.66 c
+27.575 2.514 27.505 2.322 27.487 2.087 c
+27.487 -0.574 l
+26.825 -0.574 l
+26.825 2.072 l
+26.825 2.62 26.605 2.896 26.164 2.896 c
+25.83 2.896 25.595 2.734 25.458 2.41 c
+25.458 -0.574 l
+24.812 -0.574 l
+24.812 3.41 l
+h
+31.177 -0.574 -0.647 3.984 re
+31.22 4.453 m
+31.22 4.343 31.191 4.251 31.133 4.174 c
+31.074 4.104 30.979 4.072 30.853 4.072 c
+30.736 4.072 30.64 4.104 30.574 4.174 c
+30.515 4.251 30.486 4.343 30.486 4.453 c
+30.486 4.571 30.515 4.663 30.574 4.733 c
+30.64 4.81 30.736 4.85 30.853 4.85 c
+30.979 4.85 31.074 4.81 31.133 4.733 c
+31.191 4.652 31.22 4.56 31.22 4.453 c
+33.043 4.365 m
+33.043 3.41 l
+33.646 3.41 l
+33.646 2.881 l
+33.043 2.881 l
+33.043 0.411 l
+33.043 0.253 33.065 0.135 33.117 0.058 c
+33.175 -0.023 33.264 -0.059 33.381 -0.059 c
+33.47 -0.059 33.558 -0.044 33.646 -0.015 c
+33.646 -0.574 l
+33.499 -0.621 33.344 -0.647 33.19 -0.647 c
+32.933 -0.647 32.739 -0.555 32.602 -0.368 c
+32.463 -0.184 32.396 0.077 32.396 0.411 c
+32.396 2.881 l
+31.794 2.881 l
+31.794 3.41 l
+32.396 3.41 l
+32.396 4.365 l
+h
+36.777 2.998 m
+37.03 3.322 37.35 3.484 37.732 3.484 c
+38.438 3.484 38.794 3.013 38.805 2.072 c
+38.805 -0.574 l
+38.158 -0.574 l
+38.158 2.043 l
+38.158 2.356 38.104 2.576 37.997 2.705 c
+37.886 2.829 37.732 2.896 37.527 2.896 c
+37.369 2.896 37.221 2.84 37.086 2.734 c
+36.957 2.624 36.854 2.487 36.777 2.322 c
+36.777 -0.574 l
+36.13 -0.574 l
+36.13 5.071 l
+36.777 5.071 l
+h
+40.466 -0.574 -0.646 3.984 re
+40.51 4.453 m
+40.51 4.343 40.481 4.251 40.423 4.174 c
+40.363 4.104 40.268 4.072 40.143 4.072 c
+40.026 4.072 39.93 4.104 39.864 4.174 c
+39.805 4.251 39.775 4.343 39.775 4.453 c
+39.775 4.571 39.805 4.663 39.864 4.733 c
+39.93 4.81 40.026 4.85 40.143 4.85 c
+40.268 4.85 40.363 4.81 40.423 4.733 c
+40.481 4.652 40.51 4.56 40.51 4.453 c
+43.421 0.44 m
+43.421 0.588 43.366 0.709 43.259 0.808 c
+43.149 0.904 42.943 1.022 42.642 1.161 c
+42.297 1.308 42.054 1.429 41.907 1.529 c
+41.76 1.635 41.649 1.753 41.583 1.881 c
+41.514 2.006 41.481 2.164 41.481 2.352 c
+41.481 2.674 41.599 2.944 41.834 3.16 c
+42.069 3.374 42.37 3.484 42.744 3.484 c
+43.127 3.484 43.436 3.37 43.671 3.145 c
+43.906 2.917 44.024 2.631 44.024 2.278 c
+43.377 2.278 l
+43.377 2.454 43.318 2.605 43.201 2.734 c
+43.083 2.859 42.929 2.925 42.744 2.925 c
+42.546 2.925 42.395 2.869 42.289 2.763 c
+42.179 2.664 42.127 2.532 42.127 2.366 c
+42.127 2.238 42.164 2.131 42.245 2.043 c
+42.322 1.962 42.513 1.859 42.818 1.734 c
+43.296 1.547 43.627 1.359 43.803 1.176 c
+43.98 0.999 44.068 0.771 44.068 0.5 c
+44.068 0.147 43.943 -0.133 43.7 -0.339 c
+43.465 -0.544 43.149 -0.647 42.759 -0.647 c
+42.337 -0.647 41.998 -0.53 41.745 -0.294 c
+41.488 -0.052 41.363 0.253 41.363 0.617 c
+42.01 0.617 l
+42.017 0.389 42.087 0.213 42.216 0.087 c
+42.341 -0.03 42.524 -0.088 42.759 -0.088 c
+42.973 -0.088 43.134 -0.04 43.245 0.058 c
+43.362 0.154 43.421 0.282 43.421 0.44 c
+45.758 4.365 m
+45.758 3.41 l
+46.361 3.41 l
+46.361 2.881 l
+45.758 2.881 l
+45.758 0.411 l
+45.758 0.253 45.78 0.135 45.832 0.058 c
+45.89 -0.023 45.979 -0.059 46.096 -0.059 c
+46.185 -0.059 46.272 -0.044 46.361 -0.015 c
+46.361 -0.574 l
+46.214 -0.621 46.06 -0.647 45.905 -0.647 c
+45.647 -0.647 45.453 -0.555 45.317 -0.368 c
+45.177 -0.184 45.111 0.077 45.111 0.411 c
+45.111 2.881 l
+44.508 2.881 l
+44.508 3.41 l
+45.111 3.41 l
+45.111 4.365 l
+h
+46.919 1.602 m
+46.919 2.179 47.055 2.634 47.33 2.969 c
+47.614 3.31 47.985 3.484 48.448 3.484 c
+48.907 3.484 49.275 3.314 49.551 2.984 c
+49.834 2.66 49.981 2.212 49.992 1.646 c
+49.992 1.22 l
+49.992 0.65 49.848 0.195 49.565 -0.148 c
+49.289 -0.482 48.922 -0.647 48.462 -0.647 c
+47.999 -0.647 47.629 -0.485 47.346 -0.162 c
+47.07 0.168 46.927 0.61 46.919 1.161 c
+h
+47.566 1.22 m
+47.566 0.816 47.643 0.5 47.801 0.264 c
+47.966 0.029 48.187 -0.088 48.462 -0.088 c
+49.029 -0.088 49.323 0.323 49.345 1.146 c
+49.345 1.602 l
+49.345 2.003 49.26 2.322 49.094 2.557 c
+48.936 2.8 48.72 2.925 48.448 2.925 c
+48.184 2.925 47.966 2.8 47.801 2.557 c
+47.643 2.322 47.566 2.003 47.566 1.602 c
+h
+52.475 2.792 m
+52.387 2.811 52.288 2.822 52.182 2.822 c
+51.847 2.822 51.612 2.638 51.476 2.278 c
+51.476 -0.574 l
+50.829 -0.574 l
+50.829 3.41 l
+51.461 3.41 l
+51.476 2.998 l
+51.652 3.322 51.895 3.484 52.211 3.484 c
+52.317 3.484 52.406 3.461 52.475 3.424 c
+h
+54.283 0.515 m
+55.004 3.41 l
+55.695 3.41 l
+54.401 -1.132 l
+54.302 -1.474 54.158 -1.735 53.975 -1.912 c
+53.798 -2.087 53.597 -2.176 53.372 -2.176 c
+53.284 -2.176 53.17 -2.153 53.034 -2.117 c
+53.034 -1.573 l
+53.181 -1.588 l
+53.364 -1.588 53.512 -1.544 53.622 -1.455 c
+53.728 -1.367 53.817 -1.209 53.886 -0.985 c
+54.004 -0.544 l
+52.843 3.41 l
+53.549 3.41 l
+h
+57.914 1.602 m
+57.914 2.179 58.05 2.634 58.326 2.969 c
+58.609 3.31 58.98 3.484 59.443 3.484 c
+59.903 3.484 60.269 3.314 60.545 2.984 c
+60.828 2.66 60.975 2.212 60.986 1.646 c
+60.986 1.22 l
+60.986 0.65 60.843 0.195 60.56 -0.148 c
+60.284 -0.482 59.917 -0.647 59.458 -0.647 c
+58.995 -0.647 58.623 -0.485 58.34 -0.162 c
+58.065 0.168 57.921 0.61 57.914 1.161 c
+h
+58.561 1.22 m
+58.561 0.816 58.638 0.5 58.796 0.264 c
+58.962 0.029 59.182 -0.088 59.458 -0.088 c
+60.024 -0.088 60.317 0.323 60.34 1.146 c
+60.34 1.602 l
+60.34 2.003 60.255 2.322 60.09 2.557 c
+59.932 2.8 59.714 2.925 59.443 2.925 c
+59.178 2.925 58.962 2.8 58.796 2.557 c
+58.638 2.322 58.561 2.003 58.561 1.602 c
+h
+62.118 -0.574 m
+62.118 2.881 l
+61.589 2.881 l
+61.589 3.41 l
+62.118 3.41 l
+62.118 3.866 l
+62.118 4.266 62.214 4.579 62.412 4.806 c
+62.617 5.03 62.897 5.144 63.25 5.144 c
+63.386 5.144 63.518 5.122 63.647 5.086 c
+63.617 4.542 l
+63.518 4.56 63.419 4.571 63.323 4.571 c
+62.949 4.571 62.765 4.307 62.765 3.777 c
+62.765 3.41 l
+63.441 3.41 l
+63.441 2.881 l
+62.765 2.881 l
+62.765 -0.574 l
+h
+67.307 -0.088 m
+67.52 -0.088 67.693 -0.026 67.822 0.103 c
+67.957 0.239 68.031 0.43 68.042 0.675 c
+68.659 0.675 l
+68.637 0.294 68.501 -0.026 68.248 -0.279 c
+67.99 -0.526 67.678 -0.647 67.307 -0.647 c
+66.814 -0.647 66.44 -0.497 66.175 -0.191 c
+65.918 0.121 65.792 0.588 65.792 1.205 c
+65.792 1.646 l
+65.792 2.241 65.918 2.697 66.175 3.013 c
+66.44 3.326 66.814 3.484 67.307 3.484 c
+67.708 3.484 68.027 3.351 68.262 3.087 c
+68.505 2.829 68.637 2.484 68.659 2.043 c
+68.042 2.043 l
+68.02 2.337 67.946 2.557 67.822 2.705 c
+67.704 2.851 67.531 2.925 67.307 2.925 c
+67.013 2.925 66.796 2.826 66.66 2.631 c
+66.521 2.443 66.447 2.135 66.44 1.705 c
+66.44 1.19 l
+66.44 0.72 66.506 0.386 66.646 0.191 c
+66.792 0.004 67.013 -0.088 67.307 -0.088 c
+71.437 -0.221 m
+71.221 -0.507 70.908 -0.647 70.496 -0.647 c
+70.133 -0.647 69.857 -0.526 69.673 -0.279 c
+69.497 -0.026 69.401 0.338 69.394 0.808 c
+69.394 3.41 l
+70.041 3.41 l
+70.041 0.867 l
+70.041 0.239 70.224 -0.073 70.6 -0.073 c
+71 -0.073 71.275 0.103 71.423 0.455 c
+71.423 3.41 l
+72.069 3.41 l
+72.069 -0.574 l
+71.452 -0.574 l
+h
+74.7 2.792 m
+74.612 2.811 74.513 2.822 74.407 2.822 c
+74.072 2.822 73.837 2.638 73.701 2.278 c
+73.701 -0.574 l
+73.054 -0.574 l
+73.054 3.41 l
+73.686 3.41 l
+73.701 2.998 l
+73.877 3.322 74.12 3.484 74.436 3.484 c
+74.542 3.484 74.631 3.461 74.7 3.424 c
+h
+76.994 2.792 m
+76.905 2.811 76.806 2.822 76.699 2.822 c
+76.365 2.822 76.13 2.638 75.994 2.278 c
+75.994 -0.574 l
+75.348 -0.574 l
+75.348 3.41 l
+75.979 3.41 l
+75.994 2.998 l
+76.171 3.322 76.412 3.484 76.729 3.484 c
+76.836 3.484 76.923 3.461 76.994 3.424 c
+h
+78.993 -0.647 m
+78.492 -0.647 78.111 -0.5 77.846 -0.206 c
+77.582 0.087 77.449 0.521 77.449 1.103 c
+77.449 1.573 l
+77.449 2.167 77.574 2.634 77.831 2.969 c
+78.095 3.31 78.456 3.484 78.92 3.484 c
+79.379 3.484 79.72 3.329 79.948 3.027 c
+80.183 2.734 80.304 2.271 80.316 1.646 c
+80.316 1.22 l
+78.095 1.22 l
+78.095 1.132 l
+78.095 0.698 78.174 0.386 78.331 0.191 c
+78.496 0.004 78.728 -0.088 79.022 -0.088 c
+79.217 -0.088 79.39 -0.055 79.537 0.014 c
+79.683 0.091 79.82 0.209 79.948 0.367 c
+80.286 -0.044 l
+80 -0.449 79.57 -0.647 78.993 -0.647 c
+78.92 2.925 m
+78.644 2.925 78.442 2.829 78.317 2.645 c
+78.188 2.458 78.114 2.167 78.095 1.778 c
+79.668 1.778 l
+79.668 1.866 l
+79.647 2.248 79.581 2.516 79.463 2.674 c
+79.346 2.84 79.161 2.925 78.92 2.925 c
+81.712 3.41 m
+81.727 2.969 l
+81.98 3.31 82.303 3.484 82.697 3.484 c
+83.402 3.484 83.759 3.013 83.77 2.072 c
+83.77 -0.574 l
+83.123 -0.574 l
+83.123 2.043 l
+83.123 2.356 83.068 2.576 82.961 2.705 c
+82.851 2.829 82.697 2.896 82.491 2.896 c
+82.333 2.896 82.186 2.84 82.05 2.734 c
+81.921 2.624 81.819 2.487 81.742 2.322 c
+81.742 -0.574 l
+81.095 -0.574 l
+81.095 3.41 l
+h
+85.592 4.365 m
+85.592 3.41 l
+86.195 3.41 l
+86.195 2.881 l
+85.592 2.881 l
+85.592 0.411 l
+85.592 0.253 85.615 0.135 85.666 0.058 c
+85.725 -0.023 85.813 -0.059 85.931 -0.059 c
+86.018 -0.059 86.107 -0.044 86.195 -0.015 c
+86.195 -0.574 l
+86.048 -0.621 85.894 -0.647 85.74 -0.647 c
+85.482 -0.647 85.287 -0.555 85.151 -0.368 c
+85.012 -0.184 84.946 0.077 84.946 0.411 c
+84.946 2.881 l
+84.343 2.881 l
+84.343 3.41 l
+84.946 3.41 l
+84.946 4.365 l
+h
+91.56 1.22 m
+91.56 0.602 91.446 0.135 91.223 -0.177 c
+91.005 -0.493 90.682 -0.647 90.252 -0.647 c
+89.829 -0.647 89.517 -0.467 89.311 -0.103 c
+89.282 -0.574 l
+88.679 -0.574 l
+88.679 5.071 l
+89.326 5.071 l
+89.326 2.969 l
+89.54 3.31 89.848 3.484 90.252 3.484 c
+90.682 3.484 91.005 3.326 91.223 3.013 c
+91.446 2.708 91.56 2.241 91.56 1.616 c
+h
+90.914 1.602 m
+90.914 2.072 90.843 2.403 90.708 2.601 c
+90.579 2.796 90.369 2.896 90.076 2.896 c
+89.741 2.896 89.492 2.711 89.326 2.352 c
+89.326 0.47 l
+89.492 0.106 89.745 -0.073 90.09 -0.073 c
+90.384 -0.073 90.594 0.029 90.722 0.235 c
+90.847 0.44 90.914 0.756 90.914 1.19 c
+h
+94.045 2.792 m
+93.956 2.811 93.857 2.822 93.75 2.822 c
+93.416 2.822 93.181 2.638 93.045 2.278 c
+93.045 -0.574 l
+92.398 -0.574 l
+92.398 3.41 l
+93.031 3.41 l
+93.045 2.998 l
+93.222 3.322 93.463 3.484 93.779 3.484 c
+93.887 3.484 93.974 3.461 94.045 3.424 c
+h
+96.588 -0.574 m
+96.547 -0.485 96.521 -0.339 96.514 -0.133 c
+96.279 -0.478 95.985 -0.647 95.632 -0.647 c
+95.268 -0.647 94.986 -0.551 94.779 -0.353 c
+94.581 -0.148 94.485 0.139 94.485 0.515 c
+94.485 0.914 94.621 1.234 94.897 1.469 c
+95.169 1.712 95.543 1.837 96.014 1.837 c
+96.499 1.837 l
+96.499 2.263 l
+96.499 2.499 96.444 2.664 96.337 2.763 c
+96.227 2.869 96.065 2.925 95.853 2.925 c
+95.654 2.925 95.493 2.866 95.367 2.749 c
+95.25 2.631 95.191 2.484 95.191 2.308 c
+94.544 2.308 l
+94.544 2.502 94.603 2.693 94.72 2.881 c
+94.845 3.064 95.007 3.212 95.206 3.322 c
+95.412 3.428 95.639 3.484 95.896 3.484 c
+96.297 3.484 96.602 3.38 96.808 3.175 c
+97.021 2.969 97.135 2.674 97.145 2.293 c
+97.145 0.279 l
+97.145 -0.026 97.183 -0.291 97.264 -0.515 c
+97.264 -0.574 l
+h
+95.72 -0.059 m
+95.886 -0.059 96.036 -0.015 96.176 0.073 c
+96.323 0.162 96.43 0.272 96.499 0.411 c
+96.499 1.352 l
+96.131 1.352 l
+95.815 1.352 95.573 1.282 95.397 1.146 c
+95.221 1.018 95.132 0.83 95.132 0.588 c
+95.132 0.359 95.176 0.195 95.264 0.087 c
+95.352 -0.011 95.503 -0.059 95.72 -0.059 c
+98.763 3.41 m
+98.778 2.969 l
+99.031 3.31 99.354 3.484 99.748 3.484 c
+100.453 3.484 100.81 3.013 100.821 2.072 c
+100.821 -0.574 l
+100.174 -0.574 l
+100.174 2.043 l
+100.174 2.356 100.119 2.576 100.012 2.705 c
+99.902 2.829 99.748 2.896 99.542 2.896 c
+99.383 2.896 99.237 2.84 99.101 2.734 c
+98.972 2.624 98.87 2.487 98.792 2.322 c
+98.792 -0.574 l
+98.145 -0.574 l
+98.145 3.41 l
+h
+103.173 -0.088 m
+103.385 -0.088 103.558 -0.026 103.687 0.103 c
+103.823 0.239 103.896 0.43 103.907 0.675 c
+104.525 0.675 l
+104.503 0.294 104.367 -0.026 104.113 -0.279 c
+103.856 -0.526 103.543 -0.647 103.173 -0.647 c
+102.68 -0.647 102.305 -0.497 102.041 -0.191 c
+101.783 0.121 101.658 0.588 101.658 1.205 c
+101.658 1.646 l
+101.658 2.241 101.783 2.697 102.041 3.013 c
+102.305 3.326 102.68 3.484 103.173 3.484 c
+103.574 3.484 103.892 3.351 104.128 3.087 c
+104.37 2.829 104.503 2.484 104.525 2.043 c
+103.907 2.043 l
+103.886 2.337 103.812 2.557 103.687 2.705 c
+103.57 2.851 103.397 2.925 103.173 2.925 c
+102.878 2.925 102.662 2.826 102.526 2.631 c
+102.386 2.443 102.313 2.135 102.305 1.705 c
+102.305 1.19 l
+102.305 0.72 102.371 0.386 102.511 0.191 c
+102.658 0.004 102.878 -0.088 103.173 -0.088 c
+105.922 2.998 m
+106.175 3.322 106.495 3.484 106.876 3.484 c
+107.582 3.484 107.939 3.013 107.95 2.072 c
+107.95 -0.574 l
+107.303 -0.574 l
+107.303 2.043 l
+107.303 2.356 107.248 2.576 107.142 2.705 c
+107.031 2.829 106.876 2.896 106.671 2.896 c
+106.513 2.896 106.366 2.84 106.23 2.734 c
+106.101 2.624 105.999 2.487 105.922 2.322 c
+105.922 -0.574 l
+105.274 -0.574 l
+105.274 5.071 l
+105.922 5.071 l
+h
+108.949 -0.221 m
+108.949 -0.103 108.983 -0.008 109.052 0.073 c
+109.118 0.151 109.221 0.191 109.361 0.191 c
+109.508 0.191 109.614 0.151 109.685 0.073 c
+109.762 -0.008 109.802 -0.103 109.802 -0.221 c
+109.802 -0.331 109.762 -0.423 109.685 -0.5 c
+109.614 -0.578 109.508 -0.618 109.361 -0.618 c
+109.221 -0.618 109.118 -0.578 109.052 -0.5 c
+108.983 -0.423 108.949 -0.331 108.949 -0.221 c
+f
+Q
+161.753 450.241 -1.793 0.867 re
+163.575 452.357 m
+163.605 451.96 l
+163.84 452.273 164.141 452.431 164.516 452.431 c
+165.199 452.431 165.552 451.949 165.575 450.99 c
+165.575 448.373 l
+164.531 448.373 l
+164.531 450.916 l
+164.531 451.141 164.494 451.303 164.428 451.402 c
+164.358 451.498 164.241 451.548 164.075 451.548 c
+163.888 451.548 163.74 451.453 163.634 451.269 c
+163.634 448.373 l
+162.591 448.373 l
+162.591 452.357 l
+h
+169.499 449.167 m
+169.793 449.167 169.944 449.362 169.955 449.755 c
+170.925 449.755 l
+170.925 449.322 170.792 448.969 170.528 448.697 c
+170.263 448.433 169.925 448.3 169.514 448.3 c
+169.003 448.3 168.61 448.454 168.338 448.77 c
+168.073 449.094 167.933 449.564 167.927 450.181 c
+167.927 450.505 l
+167.927 451.13 168.058 451.608 168.323 451.931 c
+168.595 452.261 168.992 452.431 169.514 452.431 c
+169.944 452.431 170.285 452.291 170.543 452.019 c
+170.796 451.743 170.925 451.361 170.925 450.873 c
+169.955 450.873 l
+169.955 451.085 169.914 451.255 169.837 451.372 c
+169.768 451.498 169.65 451.563 169.484 451.563 c
+169.308 451.563 169.18 451.498 169.103 451.372 c
+169.022 451.244 168.977 450.993 168.97 450.623 c
+168.97 450.211 l
+168.97 449.888 168.984 449.659 169.014 449.535 c
+169.051 449.406 169.105 449.314 169.176 449.256 c
+169.253 449.196 169.359 449.167 169.499 449.167 c
+171.41 450.49 m
+171.41 451.097 171.549 451.571 171.836 451.916 c
+172.12 452.258 172.512 452.431 173.012 452.431 c
+173.519 452.431 173.916 452.258 174.202 451.916 c
+174.485 451.571 174.628 451.097 174.628 450.49 c
+174.628 450.226 l
+174.628 449.626 174.485 449.156 174.202 448.815 c
+173.916 448.469 173.519 448.3 173.012 448.3 c
+172.501 448.3 172.104 448.469 171.821 448.815 c
+171.546 449.156 171.41 449.63 171.41 450.241 c
+h
+172.453 450.226 m
+172.453 449.52 172.637 449.167 173.012 449.167 c
+173.365 449.167 173.556 449.462 173.585 450.05 c
+173.585 450.49 l
+173.585 450.85 173.533 451.122 173.438 451.299 c
+173.339 451.475 173.196 451.563 173.012 451.563 c
+172.835 451.563 172.696 451.475 172.6 451.299 c
+172.501 451.122 172.453 450.85 172.453 450.49 c
+h
+177.231 448.741 m
+177.014 448.447 176.723 448.3 176.363 448.3 c
+176 448.3 175.721 448.421 175.526 448.668 c
+175.338 448.921 175.247 449.289 175.247 449.77 c
+175.247 452.357 l
+176.29 452.357 l
+176.29 449.755 l
+176.29 449.362 176.415 449.167 176.672 449.167 c
+176.907 449.167 177.076 449.27 177.186 449.476 c
+177.186 452.357 l
+178.231 452.357 l
+178.231 448.373 l
+177.26 448.373 l
+h
+179.935 452.357 m
+179.964 451.96 l
+180.2 452.273 180.501 452.431 180.876 452.431 c
+181.56 452.431 181.913 451.949 181.934 450.99 c
+181.934 448.373 l
+180.891 448.373 l
+180.891 450.916 l
+180.891 451.141 180.854 451.303 180.787 451.402 c
+180.718 451.498 180.6 451.548 180.435 451.548 c
+180.247 451.548 180.101 451.453 179.994 451.269 c
+179.994 448.373 l
+178.95 448.373 l
+178.95 452.357 l
+h
+183.889 453.327 m
+183.889 452.357 l
+184.419 452.357 l
+184.419 451.563 l
+183.889 451.563 l
+183.889 449.593 l
+183.889 449.435 183.908 449.329 183.948 449.27 c
+183.996 449.212 184.08 449.182 184.198 449.182 c
+184.305 449.182 184.39 449.189 184.448 449.212 c
+184.448 448.403 l
+184.271 448.337 184.08 448.3 183.875 448.3 c
+183.199 448.3 182.853 448.686 182.846 449.462 c
+182.846 451.563 l
+182.39 451.563 l
+182.39 452.357 l
+182.846 452.357 l
+182.846 453.327 l
+h
+f
+187.52 448.373 -0.647 5.644 re
+189.24 448.373 -0.647 3.984 re
+189.284 453.401 m
+189.284 453.291 189.254 453.198 189.196 453.121 c
+189.137 453.052 189.042 453.019 188.916 453.019 c
+188.799 453.019 188.703 453.052 188.637 453.121 c
+188.579 453.198 188.549 453.291 188.549 453.401 c
+188.549 453.518 188.579 453.61 188.637 453.68 c
+188.703 453.757 188.799 453.798 188.916 453.798 c
+189.042 453.798 189.137 453.757 189.196 453.68 c
+189.254 453.599 189.284 453.507 189.284 453.401 c
+190.871 452.357 m
+190.886 451.99 l
+191.129 452.284 191.448 452.431 191.841 452.431 c
+192.282 452.431 192.591 452.232 192.768 451.843 c
+193.021 452.232 193.37 452.431 193.811 452.431 c
+194.546 452.431 194.921 451.968 194.943 451.049 c
+194.943 448.373 l
+194.296 448.373 l
+194.296 450.99 l
+194.296 451.284 194.241 451.498 194.135 451.637 c
+194.035 451.773 193.863 451.843 193.62 451.843 c
+193.422 451.843 193.26 451.762 193.135 451.608 c
+193.017 451.461 192.947 451.269 192.93 451.034 c
+192.93 448.373 l
+192.267 448.373 l
+192.267 451.02 l
+192.267 451.567 192.047 451.843 191.606 451.843 c
+191.272 451.843 191.037 451.681 190.9 451.357 c
+190.9 448.373 l
+190.254 448.373 l
+190.254 452.357 l
+h
+196.619 448.373 -0.647 3.984 re
+196.662 453.401 m
+196.662 453.291 196.633 453.198 196.575 453.121 c
+196.516 453.052 196.421 453.019 196.296 453.019 c
+196.178 453.019 196.082 453.052 196.016 453.121 c
+195.957 453.198 195.928 453.291 195.928 453.401 c
+195.928 453.518 195.957 453.61 196.016 453.68 c
+196.082 453.757 196.178 453.798 196.296 453.798 c
+196.421 453.798 196.516 453.757 196.575 453.68 c
+196.633 453.599 196.662 453.507 196.662 453.401 c
+198.486 453.312 m
+198.486 452.357 l
+199.088 452.357 l
+199.088 451.828 l
+198.486 451.828 l
+198.486 449.358 l
+198.486 449.2 198.507 449.083 198.559 449.006 c
+198.617 448.925 198.706 448.888 198.823 448.888 c
+198.912 448.888 199 448.903 199.088 448.932 c
+199.088 448.373 l
+198.941 448.326 198.787 448.3 198.632 448.3 c
+198.376 448.3 198.181 448.392 198.044 448.579 c
+197.905 448.763 197.838 449.024 197.838 449.358 c
+197.838 451.828 l
+197.236 451.828 l
+197.236 452.357 l
+197.838 452.357 l
+197.838 453.312 l
+h
+201.793 449.387 m
+201.793 449.535 201.738 449.656 201.631 449.755 c
+201.52 449.851 201.315 449.969 201.013 450.108 c
+200.668 450.255 200.425 450.376 200.279 450.476 c
+200.132 450.582 200.022 450.7 199.955 450.829 c
+199.885 450.953 199.852 451.111 199.852 451.299 c
+199.852 451.622 199.97 451.891 200.205 452.107 c
+200.44 452.321 200.742 452.431 201.117 452.431 c
+201.499 452.431 201.807 452.317 202.043 452.092 c
+202.278 451.864 202.395 451.578 202.395 451.226 c
+201.749 451.226 l
+201.749 451.402 201.69 451.552 201.572 451.681 c
+201.455 451.806 201.3 451.872 201.117 451.872 c
+200.918 451.872 200.768 451.816 200.661 451.71 c
+200.551 451.611 200.499 451.479 200.499 451.313 c
+200.499 451.185 200.536 451.078 200.617 450.99 c
+200.694 450.91 200.885 450.806 201.19 450.681 c
+201.668 450.494 201.998 450.307 202.175 450.123 c
+202.351 449.946 202.44 449.719 202.44 449.447 c
+202.44 449.094 202.314 448.815 202.072 448.608 c
+201.837 448.403 201.52 448.3 201.131 448.3 c
+200.709 448.3 200.371 448.418 200.117 448.653 c
+199.86 448.895 199.735 449.2 199.735 449.564 c
+200.382 449.564 l
+200.389 449.337 200.459 449.16 200.587 449.035 c
+200.712 448.917 200.896 448.859 201.131 448.859 c
+201.345 448.859 201.506 448.907 201.616 449.006 c
+201.734 449.101 201.793 449.229 201.793 449.387 c
+205.703 448.373 -0.647 5.644 re
+207.423 448.373 -0.647 3.984 re
+207.467 453.401 m
+207.467 453.291 207.438 453.198 207.378 453.121 c
+207.319 453.052 207.224 453.019 207.099 453.019 c
+206.981 453.019 206.886 453.052 206.82 453.121 c
+206.761 453.198 206.732 453.291 206.732 453.401 c
+206.732 453.518 206.761 453.61 206.82 453.68 c
+206.886 453.757 206.981 453.798 207.099 453.798 c
+207.224 453.798 207.319 453.757 207.378 453.68 c
+207.438 453.599 207.467 453.507 207.467 453.401 c
+210.377 449.387 m
+210.377 449.535 210.322 449.656 210.216 449.755 c
+210.105 449.851 209.9 449.969 209.598 450.108 c
+209.252 450.255 209.01 450.376 208.863 450.476 c
+208.716 450.582 208.606 450.7 208.539 450.829 c
+208.47 450.953 208.437 451.111 208.437 451.299 c
+208.437 451.622 208.554 451.891 208.79 452.107 c
+209.025 452.321 209.326 452.431 209.701 452.431 c
+210.083 452.431 210.392 452.317 210.627 452.092 c
+210.862 451.864 210.98 451.578 210.98 451.226 c
+210.333 451.226 l
+210.333 451.402 210.274 451.552 210.156 451.681 c
+210.039 451.806 209.885 451.872 209.701 451.872 c
+209.503 451.872 209.352 451.816 209.245 451.71 c
+209.135 451.611 209.084 451.479 209.084 451.313 c
+209.084 451.185 209.121 451.078 209.201 450.99 c
+209.279 450.91 209.47 450.806 209.774 450.681 c
+210.252 450.494 210.583 450.307 210.759 450.123 c
+210.935 449.946 211.024 449.719 211.024 449.447 c
+211.024 449.094 210.899 448.815 210.656 448.608 c
+210.421 448.403 210.105 448.3 209.715 448.3 c
+209.293 448.3 208.955 448.418 208.701 448.653 c
+208.444 448.895 208.319 449.2 208.319 449.564 c
+208.965 449.564 l
+208.973 449.337 209.044 449.16 209.171 449.035 c
+209.297 448.917 209.48 448.859 209.715 448.859 c
+209.929 448.859 210.091 448.907 210.201 449.006 c
+210.318 449.101 210.377 449.229 210.377 449.387 c
+212.714 453.312 m
+212.714 452.357 l
+213.317 452.357 l
+213.317 451.828 l
+212.714 451.828 l
+212.714 449.358 l
+212.714 449.2 212.736 449.083 212.788 449.006 c
+212.847 448.925 212.934 448.888 213.052 448.888 c
+213.14 448.888 213.229 448.903 213.317 448.932 c
+213.317 448.373 l
+213.17 448.326 213.015 448.3 212.861 448.3 c
+212.604 448.3 212.41 448.392 212.273 448.579 c
+212.134 448.763 212.067 449.024 212.067 449.358 c
+212.067 451.828 l
+211.465 451.828 l
+211.465 452.357 l
+212.067 452.357 l
+212.067 453.312 l
+h
+216.653 453.312 m
+216.653 452.357 l
+217.256 452.357 l
+217.256 451.828 l
+216.653 451.828 l
+216.653 449.358 l
+216.653 449.2 216.676 449.083 216.727 449.006 c
+216.786 448.925 216.874 448.888 216.992 448.888 c
+217.08 448.888 217.168 448.903 217.256 448.932 c
+217.256 448.373 l
+217.109 448.326 216.955 448.3 216.801 448.3 c
+216.543 448.3 216.348 448.392 216.213 448.579 c
+216.073 448.763 216.007 449.024 216.007 449.358 c
+216.007 451.828 l
+215.404 451.828 l
+215.404 452.357 l
+216.007 452.357 l
+216.007 453.312 l
+h
+217.815 450.549 m
+217.815 451.126 217.95 451.581 218.226 451.916 c
+218.509 452.258 218.881 452.431 219.344 452.431 c
+219.803 452.431 220.171 452.261 220.446 451.931 c
+220.728 451.608 220.876 451.159 220.886 450.593 c
+220.886 450.167 l
+220.886 449.597 220.743 449.142 220.46 448.799 c
+220.185 448.466 219.818 448.3 219.358 448.3 c
+218.895 448.3 218.524 448.462 218.241 448.785 c
+217.965 449.116 217.823 449.557 217.815 450.108 c
+h
+218.461 450.167 m
+218.461 449.763 218.538 449.447 218.696 449.212 c
+218.862 448.976 219.082 448.859 219.358 448.859 c
+219.924 448.859 220.218 449.27 220.24 450.093 c
+220.24 450.549 l
+220.24 450.95 220.156 451.269 219.99 451.504 c
+219.832 451.747 219.616 451.872 219.344 451.872 c
+219.078 451.872 218.862 451.747 218.696 451.504 c
+218.538 451.269 218.461 450.95 218.461 450.549 c
+h
+224.15 448.373 -0.646 5.644 re
+227.207 448.373 m
+227.167 448.462 227.141 448.608 227.134 448.815 c
+226.899 448.469 226.604 448.3 226.252 448.3 c
+225.888 448.3 225.605 448.396 225.399 448.594 c
+225.201 448.799 225.106 449.086 225.106 449.462 c
+225.106 449.862 225.241 450.181 225.517 450.417 c
+225.789 450.659 226.164 450.784 226.634 450.784 c
+227.119 450.784 l
+227.119 451.211 l
+227.119 451.446 227.064 451.611 226.958 451.71 c
+226.847 451.816 226.685 451.872 226.473 451.872 c
+226.274 451.872 226.112 451.814 225.987 451.696 c
+225.87 451.578 225.811 451.431 225.811 451.255 c
+225.164 451.255 l
+225.164 451.45 225.223 451.641 225.341 451.828 c
+225.466 452.011 225.627 452.159 225.825 452.269 c
+226.031 452.375 226.259 452.431 226.517 452.431 c
+226.918 452.431 227.222 452.327 227.428 452.122 c
+227.641 451.916 227.755 451.622 227.766 451.24 c
+227.766 449.227 l
+227.766 448.921 227.803 448.656 227.884 448.433 c
+227.884 448.373 l
+h
+226.34 448.888 m
+226.506 448.888 226.656 448.932 226.796 449.021 c
+226.943 449.109 227.049 449.219 227.119 449.358 c
+227.119 450.299 l
+226.752 450.299 l
+226.436 450.299 226.193 450.229 226.017 450.093 c
+225.84 449.965 225.752 449.777 225.752 449.535 c
+225.752 449.307 225.796 449.142 225.885 449.035 c
+225.973 448.936 226.124 448.888 226.34 448.888 c
+230.706 449.387 m
+230.706 449.535 230.65 449.656 230.544 449.755 c
+230.434 449.851 230.228 449.969 229.927 450.108 c
+229.582 450.255 229.339 450.376 229.191 450.476 c
+229.045 450.582 228.935 450.7 228.869 450.829 c
+228.799 450.953 228.765 451.111 228.765 451.299 c
+228.765 451.622 228.883 451.891 229.118 452.107 c
+229.353 452.321 229.655 452.431 230.03 452.431 c
+230.411 452.431 230.721 452.317 230.956 452.092 c
+231.191 451.864 231.308 451.578 231.308 451.226 c
+230.662 451.226 l
+230.662 451.402 230.602 451.552 230.486 451.681 c
+230.367 451.806 230.213 451.872 230.03 451.872 c
+229.831 451.872 229.681 451.816 229.574 451.71 c
+229.464 451.611 229.412 451.479 229.412 451.313 c
+229.412 451.185 229.449 451.078 229.53 450.99 c
+229.607 450.91 229.798 450.806 230.103 450.681 c
+230.581 450.494 230.912 450.307 231.088 450.123 c
+231.265 449.946 231.352 449.719 231.352 449.447 c
+231.352 449.094 231.228 448.815 230.985 448.608 c
+230.75 448.403 230.434 448.3 230.045 448.3 c
+229.622 448.3 229.284 448.418 229.03 448.653 c
+228.773 448.895 228.648 449.2 228.648 449.564 c
+229.295 449.564 l
+229.302 449.337 229.372 449.16 229.501 449.035 c
+229.625 448.917 229.809 448.859 230.045 448.859 c
+230.257 448.859 230.419 448.907 230.529 449.006 c
+230.647 449.101 230.706 449.229 230.706 449.387 c
+233.043 453.312 m
+233.043 452.357 l
+233.646 452.357 l
+233.646 451.828 l
+233.043 451.828 l
+233.043 449.358 l
+233.043 449.2 233.065 449.083 233.116 449.006 c
+233.175 448.925 233.264 448.888 233.381 448.888 c
+233.469 448.888 233.557 448.903 233.646 448.932 c
+233.646 448.373 l
+233.499 448.326 233.345 448.3 233.189 448.3 c
+232.933 448.3 232.738 448.392 232.602 448.579 c
+232.462 448.763 232.396 449.024 232.396 449.358 c
+232.396 451.828 l
+231.793 451.828 l
+231.793 452.357 l
+232.396 452.357 l
+232.396 453.312 l
+h
+f
+q 1 0 0 1 236.9825 452.3574 cm
+0 0 m
+0.029 -0.397 l
+0.264 -0.085 0.565 0.073 0.941 0.073 c
+1.624 0.073 1.977 -0.408 1.999 -1.367 c
+1.999 -3.984 l
+0.955 -3.984 l
+0.955 -1.441 l
+0.955 -1.216 0.918 -1.055 0.852 -0.956 c
+0.783 -0.86 0.665 -0.809 0.5 -0.809 c
+0.312 -0.809 0.166 -0.904 0.058 -1.088 c
+0.058 -3.984 l
+-0.985 -3.984 l
+-0.985 0 l
+h
+f
+Q
+q 1 0 0 1 242.9355 448.8588 cm
+0 0 m
+0.213 0 0.386 0.062 0.515 0.191 c
+0.65 0.327 0.723 0.518 0.735 0.764 c
+1.352 0.764 l
+1.33 0.382 1.194 0.062 0.941 -0.191 c
+0.683 -0.437 0.371 -0.559 0 -0.559 c
+-0.493 -0.559 -0.867 -0.408 -1.132 -0.103 c
+-1.389 0.21 -1.515 0.676 -1.515 1.294 c
+-1.515 1.735 l
+-1.515 2.329 -1.389 2.786 -1.132 3.102 c
+-0.867 3.414 -0.493 3.572 0 3.572 c
+0.401 3.572 0.721 3.439 0.956 3.175 c
+1.198 2.917 1.33 2.572 1.352 2.132 c
+0.735 2.132 l
+0.713 2.425 0.64 2.645 0.515 2.793 c
+0.397 2.94 0.224 3.013 0 3.013 c
+-0.294 3.013 -0.511 2.914 -0.647 2.72 c
+-0.786 2.531 -0.86 2.223 -0.867 1.793 c
+-0.867 1.278 l
+-0.867 0.808 -0.801 0.474 -0.661 0.279 c
+-0.515 0.092 -0.294 0 0 0 c
+1.97 1.69 m
+1.97 2.267 2.105 2.722 2.381 3.057 c
+2.664 3.399 3.035 3.572 3.499 3.572 c
+3.958 3.572 4.326 3.403 4.6 3.072 c
+4.883 2.749 5.031 2.3 5.041 1.735 c
+5.041 1.309 l
+5.041 0.738 4.898 0.283 4.615 -0.059 c
+4.34 -0.393 3.973 -0.559 3.513 -0.559 c
+3.05 -0.559 2.679 -0.397 2.396 -0.073 c
+2.12 0.257 1.977 0.698 1.97 1.249 c
+h
+2.616 1.309 m
+2.616 0.904 2.693 0.588 2.851 0.353 c
+3.017 0.118 3.237 0 3.513 0 c
+4.079 0 4.373 0.411 4.395 1.234 c
+4.395 1.69 l
+4.395 2.091 4.31 2.41 4.145 2.645 c
+3.987 2.888 3.77 3.013 3.499 3.013 c
+3.233 3.013 3.017 2.888 2.851 2.645 c
+2.693 2.41 2.616 2.091 2.616 1.69 c
+h
+6.497 3.499 m
+6.512 3.131 l
+6.755 3.425 7.073 3.572 7.467 3.572 c
+7.908 3.572 8.216 3.373 8.393 2.984 c
+8.646 3.373 8.995 3.572 9.437 3.572 c
+10.171 3.572 10.547 3.109 10.568 2.19 c
+10.568 -0.485 l
+9.922 -0.485 l
+9.922 2.132 l
+9.922 2.425 9.866 2.639 9.76 2.778 c
+9.66 2.914 9.488 2.984 9.246 2.984 c
+9.047 2.984 8.885 2.903 8.76 2.749 c
+8.643 2.602 8.573 2.41 8.555 2.175 c
+8.555 -0.485 l
+7.893 -0.485 l
+7.893 2.161 l
+7.893 2.708 7.673 2.984 7.231 2.984 c
+6.898 2.984 6.662 2.822 6.526 2.499 c
+6.526 -0.485 l
+5.88 -0.485 l
+5.88 3.499 l
+h
+12.156 3.499 m
+12.17 3.131 l
+12.413 3.425 12.733 3.572 13.126 3.572 c
+13.567 3.572 13.876 3.373 14.052 2.984 c
+14.306 3.373 14.655 3.572 15.096 3.572 c
+15.831 3.572 16.205 3.109 16.228 2.19 c
+16.228 -0.485 l
+15.581 -0.485 l
+15.581 2.132 l
+15.581 2.425 15.526 2.639 15.419 2.778 c
+15.32 2.914 15.147 2.984 14.905 2.984 c
+14.707 2.984 14.545 2.903 14.42 2.749 c
+14.302 2.602 14.232 2.41 14.214 2.175 c
+14.214 -0.485 l
+13.552 -0.485 l
+13.552 2.161 l
+13.552 2.708 13.332 2.984 12.891 2.984 c
+12.557 2.984 12.322 2.822 12.185 2.499 c
+12.185 -0.485 l
+11.539 -0.485 l
+11.539 3.499 l
+h
+17.903 -0.485 -0.646 3.984 re
+17.947 4.542 m
+17.947 4.432 17.918 4.34 17.859 4.262 c
+17.801 4.193 17.705 4.16 17.58 4.16 c
+17.462 4.16 17.367 4.193 17.3 4.262 c
+17.242 4.34 17.213 4.432 17.213 4.542 c
+17.213 4.659 17.242 4.752 17.3 4.821 c
+17.367 4.898 17.462 4.939 17.58 4.939 c
+17.705 4.939 17.801 4.898 17.859 4.821 c
+17.918 4.74 17.947 4.648 17.947 4.542 c
+19.771 4.453 m
+19.771 3.499 l
+20.373 3.499 l
+20.373 2.969 l
+19.771 2.969 l
+19.771 0.5 l
+19.771 0.341 19.792 0.224 19.844 0.147 c
+19.902 0.066 19.991 0.029 20.108 0.029 c
+20.197 0.029 20.284 0.044 20.373 0.073 c
+20.373 -0.485 l
+20.226 -0.532 20.072 -0.559 19.917 -0.559 c
+19.66 -0.559 19.465 -0.467 19.329 -0.279 c
+19.189 -0.096 19.123 0.166 19.123 0.5 c
+19.123 2.969 l
+18.52 2.969 l
+18.52 3.499 l
+19.123 3.499 l
+19.123 4.453 l
+h
+23.077 0.529 m
+23.077 0.676 23.023 0.798 22.915 0.897 c
+22.805 0.992 22.599 1.11 22.298 1.249 c
+21.953 1.396 21.71 1.517 21.564 1.617 c
+21.417 1.723 21.306 1.841 21.24 1.97 c
+21.17 2.094 21.137 2.252 21.137 2.44 c
+21.137 2.763 21.255 3.032 21.49 3.248 c
+21.725 3.462 22.026 3.572 22.401 3.572 c
+22.784 3.572 23.092 3.458 23.328 3.233 c
+23.563 3.006 23.68 2.72 23.68 2.367 c
+23.033 2.367 l
+23.033 2.543 22.975 2.693 22.857 2.822 c
+22.74 2.947 22.585 3.013 22.401 3.013 c
+22.202 3.013 22.052 2.958 21.946 2.851 c
+21.835 2.753 21.784 2.62 21.784 2.454 c
+21.784 2.326 21.82 2.219 21.901 2.132 c
+21.979 2.051 22.169 1.947 22.474 1.822 c
+22.952 1.635 23.283 1.448 23.459 1.264 c
+23.636 1.087 23.725 0.86 23.725 0.588 c
+23.725 0.235 23.599 -0.044 23.357 -0.25 c
+23.122 -0.455 22.805 -0.559 22.416 -0.559 c
+21.994 -0.559 21.655 -0.441 21.402 -0.206 c
+21.144 0.037 21.02 0.341 21.02 0.706 c
+21.666 0.706 l
+21.674 0.478 21.743 0.301 21.872 0.176 c
+21.997 0.058 22.181 0 22.416 0 c
+22.629 0 22.79 0.048 22.901 0.147 c
+23.019 0.243 23.077 0.371 23.077 0.529 c
+24.606 -0.133 m
+24.606 -0.015 24.639 0.081 24.709 0.162 c
+24.775 0.239 24.878 0.279 25.018 0.279 c
+25.165 0.279 25.271 0.239 25.341 0.162 c
+25.418 0.081 25.458 -0.015 25.458 -0.133 c
+25.458 -0.243 25.418 -0.335 25.341 -0.412 c
+25.271 -0.489 25.165 -0.53 25.018 -0.53 c
+24.878 -0.53 24.775 -0.489 24.709 -0.412 c
+24.639 -0.335 24.606 -0.243 24.606 -0.133 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 441.263 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 434.424 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.882 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.882 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.69 15.056 -0.69 c
+15.192 -0.69 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+23.221 -0.646 m
+24.353 -0.646 l
+24.353 -1.264 l
+21.045 -1.264 l
+21.045 -0.646 l
+22.31 -0.646 l
+22.31 2.896 l
+21.384 2.896 l
+21.384 3.514 l
+23.221 3.514 l
+h
+28.384 0.485 m
+28.384 0.21 28.347 -0.04 28.281 -0.264 c
+28.212 -0.482 28.108 -0.669 27.973 -0.823 c
+27.833 -0.981 27.657 -1.103 27.443 -1.19 c
+27.227 -1.278 26.973 -1.323 26.679 -1.323 c
+26.404 -1.323 26.157 -1.278 25.944 -1.19 c
+25.739 -1.103 25.566 -0.981 25.43 -0.823 c
+25.29 -0.669 25.187 -0.482 25.121 -0.264 c
+25.051 -0.04 25.018 0.21 25.018 0.485 c
+25.018 0.738 25.047 0.974 25.106 1.191 c
+25.172 1.415 25.275 1.606 25.415 1.764 c
+25.55 1.929 25.727 2.058 25.944 2.146 c
+26.157 2.234 26.414 2.278 26.709 2.278 c
+27.021 2.278 27.281 2.234 27.487 2.146 c
+27.701 2.058 27.873 1.929 28.002 1.764 c
+28.138 1.606 28.237 1.415 28.296 1.191 c
+28.355 0.974 28.384 0.738 28.384 0.485 c
+27.429 0.485 m
+27.429 0.691 27.414 0.867 27.385 1.014 c
+27.362 1.162 27.326 1.282 27.267 1.382 c
+27.208 1.477 27.135 1.548 27.046 1.588 c
+26.959 1.635 26.848 1.661 26.724 1.661 c
+26.458 1.661 26.267 1.562 26.15 1.367 c
+26.032 1.18 25.974 0.886 25.974 0.485 c
+25.974 0.063 26.032 -0.243 26.15 -0.426 c
+26.267 -0.613 26.444 -0.706 26.679 -0.706 c
+26.804 -0.706 26.918 -0.687 27.017 -0.646 c
+27.113 -0.599 27.194 -0.525 27.252 -0.426 c
+27.318 -0.33 27.362 -0.206 27.385 -0.058 c
+27.414 0.088 27.429 0.268 27.429 0.485 c
+30.769 -2.66 m
+30.552 -2.66 30.361 -2.635 30.196 -2.587 c
+30.027 -2.547 29.887 -2.484 29.77 -2.396 c
+29.652 -2.315 29.552 -2.219 29.475 -2.102 c
+29.406 -1.984 29.358 -1.855 29.328 -1.72 c
+30.225 -1.617 l
+30.262 -1.753 30.331 -1.859 30.431 -1.94 c
+30.526 -2.028 30.651 -2.072 30.799 -2.072 c
+30.886 -2.072 30.967 -2.057 31.048 -2.028 c
+31.125 -1.999 31.195 -1.944 31.254 -1.866 c
+31.313 -1.797 31.357 -1.705 31.387 -1.587 c
+31.424 -1.469 31.445 -1.323 31.445 -1.147 c
+31.445 -0.955 l
+31.445 -0.889 31.449 -0.831 31.46 -0.779 c
+31.46 -0.588 l
+31.445 -0.588 l
+31.346 -0.816 31.202 -0.977 31.019 -1.072 c
+30.832 -1.172 30.626 -1.22 30.402 -1.22 c
+30.196 -1.22 30.012 -1.183 29.858 -1.103 c
+29.71 -1.014 29.582 -0.897 29.475 -0.75 c
+29.376 -0.595 29.303 -0.411 29.255 -0.206 c
+29.203 0.008 29.182 0.243 29.182 0.5 c
+29.182 0.771 29.203 1.018 29.255 1.235 c
+29.313 1.448 29.394 1.632 29.505 1.779 c
+29.612 1.933 29.743 2.051 29.901 2.132 c
+30.056 2.22 30.244 2.263 30.46 2.263 c
+30.556 2.263 30.655 2.253 30.755 2.234 c
+30.85 2.213 30.938 2.18 31.019 2.132 c
+31.107 2.08 31.185 2.018 31.254 1.941 c
+31.331 1.86 31.393 1.768 31.445 1.661 c
+31.46 1.661 l
+31.46 1.808 l
+31.467 1.867 31.474 1.918 31.474 1.97 c
+31.482 2.028 31.489 2.076 31.489 2.117 c
+31.497 2.165 31.507 2.198 31.519 2.22 c
+32.371 2.22 l
+32.36 2.139 32.349 2.028 32.342 1.882 c
+32.342 1.411 l
+32.342 -1.161 l
+32.342 -1.415 32.305 -1.635 32.239 -1.822 c
+32.17 -2.007 32.066 -2.161 31.931 -2.278 c
+31.79 -2.404 31.625 -2.499 31.43 -2.558 c
+31.232 -2.624 31.011 -2.66 30.769 -2.66 c
+31.46 0.53 m
+31.46 0.742 31.434 0.919 31.387 1.058 c
+31.346 1.205 31.291 1.323 31.225 1.411 c
+31.166 1.5 31.096 1.558 31.019 1.588 c
+30.938 1.625 30.861 1.646 30.784 1.646 c
+30.684 1.646 30.593 1.621 30.504 1.573 c
+30.424 1.532 30.358 1.463 30.298 1.367 c
+30.248 1.279 30.203 1.162 30.167 1.014 c
+30.137 0.875 30.122 0.706 30.122 0.5 c
+30.122 0.125 30.181 -0.154 30.298 -0.338 c
+30.416 -0.515 30.578 -0.602 30.784 -0.602 c
+30.85 -0.602 30.923 -0.588 31.004 -0.559 c
+31.092 -0.522 31.166 -0.463 31.225 -0.382 c
+31.291 -0.294 31.346 -0.176 31.387 -0.029 c
+31.434 0.118 31.46 0.301 31.46 0.53 c
+37.953 0.838 1.866 -0.794 re
+37.953 0.044 m
+42.013 0.838 1.867 -0.794 re
+42.013 0.044 m
+48.691 0.485 m
+48.691 0.21 48.654 -0.04 48.588 -0.264 c
+48.519 -0.482 48.415 -0.669 48.28 -0.823 c
+48.14 -0.981 47.964 -1.103 47.75 -1.19 c
+47.534 -1.278 47.28 -1.323 46.986 -1.323 c
+46.711 -1.323 46.464 -1.278 46.251 -1.19 c
+46.045 -1.103 45.872 -0.981 45.736 -0.823 c
+45.597 -0.669 45.494 -0.482 45.427 -0.264 c
+45.358 -0.04 45.325 0.21 45.325 0.485 c
+45.325 0.738 45.354 0.974 45.413 1.191 c
+45.479 1.415 45.582 1.606 45.722 1.764 c
+45.857 1.929 46.034 2.058 46.251 2.146 c
+46.464 2.234 46.721 2.278 47.015 2.278 c
+47.328 2.278 47.588 2.234 47.794 2.146 c
+48.008 2.058 48.18 1.929 48.309 1.764 c
+48.444 1.606 48.544 1.415 48.602 1.191 c
+48.662 0.974 48.691 0.738 48.691 0.485 c
+47.735 0.485 m
+47.735 0.691 47.721 0.867 47.692 1.014 c
+47.669 1.162 47.632 1.282 47.574 1.382 c
+47.515 1.477 47.441 1.548 47.353 1.588 c
+47.265 1.635 47.155 1.661 47.029 1.661 c
+46.765 1.661 46.574 1.562 46.457 1.367 c
+46.339 1.18 46.281 0.886 46.281 0.485 c
+46.281 0.063 46.339 -0.243 46.457 -0.426 c
+46.574 -0.613 46.751 -0.706 46.986 -0.706 c
+47.11 -0.706 47.224 -0.687 47.324 -0.646 c
+47.42 -0.599 47.5 -0.525 47.559 -0.426 c
+47.625 -0.33 47.669 -0.206 47.692 -0.058 c
+47.721 0.088 47.735 0.268 47.735 0.485 c
+51.737 -1.264 m
+51.737 0.721 l
+51.737 1.022 51.693 1.242 51.605 1.382 c
+51.524 1.529 51.388 1.602 51.193 1.602 c
+51.083 1.602 50.981 1.577 50.885 1.529 c
+50.796 1.477 50.715 1.411 50.649 1.323 c
+50.591 1.235 50.539 1.124 50.503 1 c
+50.473 0.882 50.458 0.75 50.458 0.603 c
+50.458 -1.264 l
+49.547 -1.264 l
+49.547 1.44 l
+49.547 1.661 l
+49.547 1.75 49.539 1.827 49.533 1.897 c
+49.533 2.087 l
+49.533 2.22 l
+50.385 2.22 l
+50.393 2.19 50.4 2.146 50.4 2.087 c
+50.4 1.897 l
+50.407 1.827 50.414 1.756 50.414 1.691 c
+50.422 1.621 50.429 1.565 50.429 1.529 c
+50.443 1.529 l
+50.561 1.793 50.712 1.985 50.9 2.103 c
+51.083 2.22 51.303 2.278 51.561 2.278 c
+51.744 2.278 51.906 2.249 52.046 2.19 c
+52.182 2.132 52.296 2.043 52.384 1.926 c
+52.473 1.808 52.535 1.665 52.575 1.5 c
+52.623 1.341 52.648 1.154 52.648 0.941 c
+52.648 -1.264 l
+h
+55.17 -1.323 m
+54.912 -1.323 54.684 -1.286 54.478 -1.22 c
+54.273 -1.143 54.096 -1.029 53.95 -0.881 c
+53.803 -0.727 53.685 -0.536 53.597 -0.309 c
+53.516 -0.085 53.479 0.181 53.479 0.485 c
+53.479 0.816 53.523 1.095 53.612 1.323 c
+53.707 1.558 53.836 1.742 53.994 1.882 c
+54.159 2.018 54.347 2.117 54.553 2.176 c
+54.758 2.242 54.967 2.278 55.184 2.278 c
+55.456 2.278 55.691 2.227 55.889 2.132 c
+56.095 2.043 56.261 1.911 56.39 1.735 c
+56.525 1.565 56.625 1.359 56.683 1.118 c
+56.75 0.882 56.787 0.617 56.787 0.324 c
+56.787 0.309 l
+54.42 0.309 l
+54.42 0.162 54.435 0.023 54.464 -0.103 c
+54.501 -0.231 54.555 -0.345 54.626 -0.441 c
+54.692 -0.529 54.776 -0.599 54.875 -0.646 c
+54.971 -0.698 55.085 -0.72 55.214 -0.72 c
+55.368 -0.72 55.507 -0.687 55.625 -0.617 c
+55.75 -0.551 55.839 -0.448 55.889 -0.309 c
+56.728 -0.382 l
+56.698 -0.482 56.643 -0.588 56.566 -0.706 c
+56.485 -0.816 56.382 -0.918 56.257 -1.014 c
+56.14 -1.103 55.985 -1.176 55.802 -1.234 c
+55.625 -1.294 55.412 -1.323 55.17 -1.323 c
+55.17 1.706 m
+55.081 1.706 54.993 1.691 54.905 1.661 c
+54.817 1.632 54.736 1.58 54.67 1.515 c
+54.6 1.444 54.541 1.357 54.493 1.249 c
+54.453 1.139 54.435 1.014 54.435 0.867 c
+55.904 0.867 l
+55.904 1.004 55.879 1.124 55.831 1.235 c
+55.791 1.341 55.735 1.43 55.669 1.5 c
+55.611 1.565 55.537 1.617 55.449 1.646 c
+55.361 1.683 55.265 1.706 55.17 1.706 c
+59.774 -0.646 m
+60.905 -0.646 l
+60.905 -1.264 l
+57.599 -1.264 l
+57.599 -0.646 l
+58.863 -0.646 l
+58.863 2.896 l
+57.936 2.896 l
+57.936 3.514 l
+59.774 3.514 l
+h
+63.835 -0.646 m
+64.967 -0.646 l
+64.967 -1.264 l
+61.659 -1.264 l
+61.659 -0.646 l
+62.923 -0.646 l
+62.923 1.602 l
+61.998 1.602 l
+61.998 2.22 l
+63.835 2.22 l
+h
+62.923 3.514 0.912 -0.676 re
+62.923 2.837 m
+67.983 -1.264 m
+67.983 0.721 l
+67.983 1.022 67.939 1.242 67.851 1.382 c
+67.77 1.529 67.634 1.602 67.44 1.602 c
+67.33 1.602 67.226 1.577 67.131 1.529 c
+67.043 1.477 66.962 1.411 66.896 1.323 c
+66.837 1.235 66.785 1.124 66.748 1 c
+66.719 0.882 66.705 0.75 66.705 0.603 c
+66.705 -1.264 l
+65.793 -1.264 l
+65.793 1.44 l
+65.793 1.661 l
+65.793 1.75 65.786 1.827 65.778 1.897 c
+65.778 2.087 l
+65.778 2.22 l
+66.631 2.22 l
+66.638 2.19 66.646 2.146 66.646 2.087 c
+66.646 1.897 l
+66.653 1.827 66.661 1.756 66.661 1.691 c
+66.667 1.621 66.675 1.565 66.675 1.529 c
+66.69 1.529 l
+66.808 1.793 66.958 1.985 67.145 2.103 c
+67.33 2.22 67.55 2.278 67.807 2.278 c
+67.991 2.278 68.153 2.249 68.292 2.19 c
+68.428 2.132 68.542 2.043 68.63 1.926 c
+68.718 1.808 68.781 1.665 68.822 1.5 c
+68.869 1.341 68.895 1.154 68.895 0.941 c
+68.895 -1.264 l
+h
+71.412 -1.323 m
+71.155 -1.323 70.927 -1.286 70.721 -1.22 c
+70.515 -1.143 70.339 -1.029 70.192 -0.881 c
+70.045 -0.727 69.927 -0.536 69.84 -0.309 c
+69.759 -0.085 69.722 0.181 69.722 0.485 c
+69.722 0.816 69.765 1.095 69.854 1.323 c
+69.95 1.558 70.078 1.742 70.236 1.882 c
+70.401 2.018 70.588 2.117 70.794 2.176 c
+71 2.242 71.21 2.278 71.427 2.278 c
+71.698 2.278 71.934 2.227 72.132 2.132 c
+72.338 2.043 72.504 1.911 72.632 1.735 c
+72.768 1.565 72.867 1.359 72.926 1.118 c
+72.992 0.882 73.029 0.617 73.029 0.324 c
+73.029 0.309 l
+70.663 0.309 l
+70.663 0.162 70.677 0.023 70.706 -0.103 c
+70.743 -0.231 70.798 -0.345 70.868 -0.441 c
+70.935 -0.529 71.018 -0.599 71.118 -0.646 c
+71.213 -0.698 71.328 -0.72 71.456 -0.72 c
+71.61 -0.72 71.75 -0.687 71.868 -0.617 c
+71.993 -0.551 72.08 -0.448 72.132 -0.309 c
+72.97 -0.382 l
+72.94 -0.482 72.886 -0.588 72.809 -0.706 c
+72.728 -0.816 72.624 -0.918 72.5 -1.014 c
+72.382 -1.103 72.228 -1.176 72.044 -1.234 c
+71.868 -1.294 71.654 -1.323 71.412 -1.323 c
+71.412 1.706 m
+71.324 1.706 71.236 1.691 71.147 1.661 c
+71.059 1.632 70.978 1.58 70.912 1.515 c
+70.842 1.444 70.783 1.357 70.736 1.249 c
+70.696 1.139 70.677 1.014 70.677 0.867 c
+72.147 0.867 l
+72.147 1.004 72.121 1.124 72.074 1.235 c
+72.033 1.341 71.978 1.43 71.912 1.5 c
+71.853 1.565 71.779 1.617 71.691 1.646 c
+71.603 1.683 71.507 1.706 71.412 1.706 c
+78.567 0.838 1.866 -0.794 re
+78.567 0.044 m
+82.627 0.838 1.867 -0.794 re
+82.627 0.044 m
+87.629 -2.66 m
+87.412 -2.66 87.221 -2.635 87.055 -2.587 c
+86.887 -2.547 86.747 -2.484 86.629 -2.396 c
+86.511 -2.315 86.413 -2.219 86.335 -2.102 c
+86.265 -1.984 86.218 -1.855 86.188 -1.72 c
+87.085 -1.617 l
+87.122 -1.753 87.191 -1.859 87.29 -1.94 c
+87.386 -2.028 87.511 -2.072 87.658 -2.072 c
+87.747 -2.072 87.827 -2.057 87.908 -2.028 c
+87.985 -1.999 88.055 -1.944 88.113 -1.866 c
+88.173 -1.797 88.217 -1.705 88.246 -1.587 c
+88.283 -1.469 88.304 -1.323 88.304 -1.147 c
+88.304 -0.955 l
+88.304 -0.889 88.308 -0.831 88.319 -0.779 c
+88.319 -0.588 l
+88.304 -0.588 l
+88.206 -0.816 88.063 -0.977 87.878 -1.072 c
+87.691 -1.172 87.485 -1.22 87.261 -1.22 c
+87.055 -1.22 86.872 -1.183 86.717 -1.103 c
+86.571 -1.014 86.442 -0.897 86.335 -0.75 c
+86.236 -0.595 86.162 -0.411 86.114 -0.206 c
+86.064 0.008 86.041 0.243 86.041 0.5 c
+86.041 0.771 86.064 1.018 86.114 1.235 c
+86.174 1.448 86.255 1.632 86.365 1.779 c
+86.471 1.933 86.604 2.051 86.762 2.132 c
+86.916 2.22 87.103 2.263 87.32 2.263 c
+87.415 2.263 87.515 2.253 87.614 2.234 c
+87.71 2.213 87.797 2.18 87.878 2.132 c
+87.967 2.08 88.044 2.018 88.113 1.941 c
+88.191 1.86 88.254 1.768 88.304 1.661 c
+88.319 1.661 l
+88.319 1.808 l
+88.327 1.867 88.334 1.918 88.334 1.97 c
+88.342 2.028 88.349 2.076 88.349 2.117 c
+88.356 2.165 88.367 2.198 88.379 2.22 c
+89.231 2.22 l
+89.22 2.139 89.208 2.028 89.202 1.882 c
+89.202 1.411 l
+89.202 -1.161 l
+89.202 -1.415 89.165 -1.635 89.098 -1.822 c
+89.029 -2.007 88.926 -2.161 88.79 -2.278 c
+88.651 -2.404 88.485 -2.499 88.29 -2.558 c
+88.092 -2.624 87.872 -2.66 87.629 -2.66 c
+88.319 0.53 m
+88.319 0.742 88.294 0.919 88.246 1.058 c
+88.206 1.205 88.15 1.323 88.084 1.411 c
+88.026 1.5 87.955 1.558 87.878 1.588 c
+87.797 1.625 87.72 1.646 87.643 1.646 c
+87.544 1.646 87.452 1.621 87.364 1.573 c
+87.284 1.532 87.217 1.463 87.159 1.367 c
+87.107 1.279 87.063 1.162 87.026 1.014 c
+86.997 0.875 86.982 0.706 86.982 0.5 c
+86.982 0.125 87.041 -0.154 87.159 -0.338 c
+87.276 -0.515 87.438 -0.602 87.643 -0.602 c
+87.71 -0.602 87.783 -0.588 87.864 -0.559 c
+87.952 -0.522 88.026 -0.463 88.084 -0.382 c
+88.15 -0.294 88.206 -0.176 88.246 -0.029 c
+88.294 0.118 88.319 0.301 88.319 0.53 c
+93.174 1.47 m
+93.075 1.477 92.971 1.488 92.865 1.5 c
+92.755 1.517 92.634 1.529 92.498 1.529 c
+92.321 1.529 92.163 1.488 92.028 1.411 c
+91.888 1.341 91.77 1.242 91.674 1.118 c
+91.587 0.989 91.517 0.842 91.469 0.676 c
+91.429 0.507 91.41 0.331 91.41 0.147 c
+91.41 -1.264 l
+90.513 -1.264 l
+90.513 0.985 l
+90.513 1.11 90.502 1.235 90.484 1.353 c
+90.473 1.477 90.459 1.595 90.44 1.706 c
+90.429 1.823 90.414 1.918 90.396 1.999 c
+90.374 2.087 90.355 2.161 90.337 2.22 c
+91.219 2.22 l
+91.226 2.168 91.238 2.117 91.248 2.058 c
+91.267 1.999 91.282 1.933 91.292 1.867 c
+91.311 1.808 91.325 1.742 91.336 1.675 c
+91.344 1.606 91.355 1.544 91.366 1.484 c
+91.381 1.484 l
+91.417 1.602 91.469 1.709 91.527 1.808 c
+91.594 1.904 91.674 1.988 91.763 2.058 c
+91.851 2.124 91.954 2.18 92.071 2.22 c
+92.196 2.257 92.343 2.278 92.512 2.278 c
+92.637 2.278 92.755 2.271 92.865 2.263 c
+92.983 2.253 93.085 2.238 93.174 2.22 c
+h
+95.18 -1.323 m
+95.011 -1.323 94.86 -1.301 94.725 -1.264 c
+94.596 -1.216 94.482 -1.147 94.386 -1.058 c
+94.299 -0.97 94.228 -0.864 94.181 -0.735 c
+94.129 -0.599 94.107 -0.448 94.107 -0.279 c
+94.107 -0.073 94.141 0.096 94.21 0.235 c
+94.276 0.382 94.372 0.493 94.49 0.574 c
+94.615 0.661 94.758 0.724 94.916 0.765 c
+95.081 0.802 95.257 0.827 95.445 0.838 c
+96.165 0.852 l
+96.165 1.029 l
+96.165 1.147 96.154 1.249 96.136 1.338 c
+96.113 1.426 96.081 1.492 96.033 1.544 c
+95.992 1.602 95.945 1.639 95.886 1.661 c
+95.827 1.679 95.761 1.691 95.695 1.691 c
+95.625 1.691 95.562 1.679 95.504 1.661 c
+95.452 1.65 95.404 1.625 95.357 1.588 c
+95.317 1.558 95.283 1.507 95.254 1.44 c
+95.232 1.382 95.217 1.301 95.209 1.205 c
+94.269 1.249 l
+94.299 1.397 94.342 1.532 94.401 1.661 c
+94.467 1.786 94.563 1.897 94.681 1.985 c
+94.798 2.08 94.938 2.153 95.107 2.205 c
+95.283 2.253 95.489 2.278 95.724 2.278 c
+96.165 2.278 96.496 2.168 96.724 1.955 c
+96.959 1.75 97.077 1.44 97.077 1.029 c
+97.077 -0.235 l
+97.077 -0.455 l
+97.083 -0.515 97.098 -0.569 97.121 -0.617 c
+97.139 -0.658 97.168 -0.69 97.208 -0.72 c
+97.245 -0.742 97.297 -0.75 97.356 -0.75 c
+97.422 -0.75 97.492 -0.746 97.561 -0.735 c
+97.561 -1.22 l
+97.503 -1.23 97.447 -1.242 97.399 -1.249 c
+97.359 -1.261 97.319 -1.267 97.282 -1.278 c
+97.241 -1.286 97.198 -1.294 97.15 -1.294 c
+97.098 -1.301 97.04 -1.308 96.973 -1.308 c
+96.746 -1.308 96.58 -1.257 96.474 -1.147 c
+96.364 -1.029 96.301 -0.864 96.283 -0.646 c
+96.268 -0.646 l
+96.198 -0.757 96.129 -0.852 96.063 -0.941 c
+95.992 -1.022 95.915 -1.087 95.827 -1.147 c
+95.739 -1.205 95.639 -1.249 95.533 -1.278 c
+95.434 -1.308 95.317 -1.323 95.18 -1.323 c
+96.165 0.353 m
+95.739 0.339 l
+95.639 0.339 95.548 0.331 95.46 0.324 c
+95.379 0.312 95.313 0.287 95.254 0.25 c
+95.195 0.21 95.144 0.151 95.107 0.073 c
+95.066 0.004 95.048 -0.088 95.048 -0.206 c
+95.048 -0.374 95.081 -0.496 95.151 -0.573 c
+95.217 -0.654 95.317 -0.69 95.445 -0.69 c
+95.552 -0.69 95.651 -0.669 95.739 -0.617 c
+95.834 -0.569 95.915 -0.507 95.974 -0.426 c
+96.04 -0.349 96.092 -0.261 96.121 -0.162 c
+96.15 -0.055 96.165 0.059 96.165 0.177 c
+h
+99.153 2.22 m
+99.161 2.198 99.167 2.165 99.167 2.117 c
+99.175 2.076 99.182 2.028 99.182 1.97 c
+99.19 1.918 99.197 1.867 99.197 1.808 c
+99.197 1.646 l
+99.211 1.646 l
+99.271 1.764 99.336 1.86 99.417 1.941 c
+99.494 2.018 99.579 2.08 99.668 2.132 c
+99.755 2.19 99.843 2.227 99.932 2.249 c
+100.027 2.267 100.127 2.278 100.225 2.278 c
+100.431 2.278 100.611 2.234 100.769 2.146 c
+100.924 2.058 101.052 1.929 101.152 1.764 c
+101.258 1.606 101.335 1.415 101.387 1.191 c
+101.446 0.974 101.475 0.738 101.475 0.485 c
+101.475 0.221 101.446 -0.025 101.387 -0.249 c
+101.335 -0.467 101.258 -0.658 101.152 -0.823 c
+101.052 -0.981 100.921 -1.103 100.755 -1.19 c
+100.597 -1.278 100.41 -1.323 100.196 -1.323 c
+100.097 -1.323 99.998 -1.311 99.903 -1.294 c
+99.803 -1.271 99.711 -1.242 99.623 -1.19 c
+99.542 -1.143 99.465 -1.08 99.388 -0.999 c
+99.319 -0.922 99.259 -0.831 99.211 -0.72 c
+99.197 -0.72 l
+99.197 -0.808 l
+99.204 -0.849 99.211 -0.897 99.211 -0.955 c
+99.211 -1.117 l
+99.211 -1.294 l
+99.211 -2.631 l
+98.3 -2.631 l
+98.3 1.455 l
+98.3 1.621 98.293 1.768 98.286 1.897 c
+98.286 2.22 l
+h
+99.197 0.456 m
+99.197 0.228 99.215 0.037 99.256 -0.118 c
+99.303 -0.264 99.358 -0.382 99.417 -0.47 c
+99.483 -0.559 99.557 -0.625 99.637 -0.661 c
+99.715 -0.702 99.792 -0.72 99.873 -0.72 c
+99.969 -0.72 100.057 -0.698 100.138 -0.646 c
+100.225 -0.599 100.292 -0.529 100.343 -0.441 c
+100.402 -0.345 100.446 -0.22 100.476 -0.073 c
+100.512 0.081 100.534 0.268 100.534 0.485 c
+100.534 0.875 100.476 1.168 100.358 1.367 c
+100.248 1.562 100.094 1.661 99.888 1.661 c
+99.807 1.661 99.73 1.639 99.653 1.602 c
+99.572 1.562 99.498 1.5 99.432 1.411 c
+99.362 1.323 99.303 1.198 99.256 1.044 c
+99.215 0.886 99.197 0.691 99.197 0.456 c
+103.242 1.515 m
+103.36 1.786 103.511 1.985 103.699 2.103 c
+103.882 2.22 104.102 2.278 104.36 2.278 c
+104.566 2.278 104.734 2.242 104.875 2.176 c
+105.021 2.105 105.131 2.014 105.212 1.897 c
+105.301 1.779 105.359 1.635 105.389 1.47 c
+105.426 1.301 105.447 1.124 105.447 0.941 c
+105.447 -1.264 l
+104.536 -1.264 l
+104.536 0.735 l
+104.536 0.871 104.526 0.992 104.507 1.103 c
+104.495 1.209 104.47 1.297 104.433 1.367 c
+104.393 1.444 104.335 1.503 104.257 1.544 c
+104.187 1.58 104.096 1.602 103.978 1.602 c
+103.867 1.602 103.772 1.577 103.684 1.529 c
+103.595 1.477 103.514 1.411 103.448 1.323 c
+103.39 1.235 103.338 1.124 103.302 1 c
+103.272 0.882 103.257 0.75 103.257 0.603 c
+103.257 -1.264 l
+102.346 -1.264 l
+102.346 3.514 l
+103.257 3.514 l
+103.257 2.205 l
+103.257 2.135 103.25 2.065 103.242 1.999 c
+103.242 1.793 l
+103.242 1.735 103.236 1.679 103.228 1.632 c
+103.228 1.515 l
+h
+111.055 0.838 1.866 -0.794 re
+111.055 0.044 m
+115.119 0.838 1.867 -0.794 re
+115.119 0.044 m
+120.767 -1.264 m
+120.757 -1.246 120.746 -1.216 120.738 -1.176 c
+120.738 -1.128 120.731 -1.08 120.723 -1.029 c
+120.723 -0.97 120.717 -0.912 120.709 -0.852 c
+120.709 -0.69 l
+120.591 -0.926 120.448 -1.095 120.283 -1.19 c
+120.114 -1.278 119.915 -1.323 119.68 -1.323 c
+119.481 -1.323 119.306 -1.278 119.151 -1.19 c
+118.993 -1.103 118.861 -0.981 118.754 -0.823 c
+118.655 -0.658 118.577 -0.467 118.519 -0.249 c
+118.467 -0.037 118.446 0.206 118.446 0.47 c
+118.446 0.735 118.467 0.974 118.519 1.191 c
+118.577 1.415 118.655 1.606 118.754 1.764 c
+118.861 1.918 118.993 2.043 119.151 2.132 c
+119.316 2.227 119.507 2.278 119.724 2.278 c
+119.819 2.278 119.915 2.263 120.004 2.234 c
+120.099 2.213 120.195 2.18 120.283 2.132 c
+120.37 2.08 120.448 2.018 120.518 1.941 c
+120.595 1.86 120.657 1.768 120.709 1.661 c
+120.709 1.75 l
+120.709 1.897 l
+120.709 2.058 l
+120.709 2.234 l
+120.709 3.514 l
+121.606 3.514 l
+121.606 -0.5 l
+121.606 -0.676 121.609 -0.834 121.621 -0.97 c
+121.627 -1.099 121.635 -1.198 121.635 -1.264 c
+h
+120.723 0.485 m
+120.723 0.721 120.698 0.912 120.65 1.058 c
+120.609 1.213 120.555 1.338 120.488 1.426 c
+120.43 1.515 120.36 1.573 120.283 1.602 c
+120.202 1.639 120.125 1.661 120.048 1.661 c
+119.948 1.661 119.857 1.635 119.768 1.588 c
+119.687 1.548 119.622 1.477 119.562 1.382 c
+119.511 1.282 119.466 1.162 119.43 1.014 c
+119.401 0.867 119.386 0.684 119.386 0.47 c
+119.386 0.077 119.437 -0.216 119.547 -0.411 c
+119.665 -0.61 119.827 -0.706 120.033 -0.706 c
+120.11 -0.706 120.187 -0.687 120.268 -0.646 c
+120.345 -0.61 120.418 -0.544 120.488 -0.455 c
+120.555 -0.367 120.609 -0.246 120.65 -0.088 c
+120.698 0.066 120.723 0.258 120.723 0.485 c
+124.211 -1.323 m
+123.954 -1.323 123.726 -1.286 123.52 -1.22 c
+123.314 -1.143 123.138 -1.029 122.991 -0.881 c
+122.844 -0.727 122.726 -0.536 122.639 -0.309 c
+122.558 -0.085 122.521 0.181 122.521 0.485 c
+122.521 0.816 122.564 1.095 122.653 1.323 c
+122.749 1.558 122.877 1.742 123.035 1.882 c
+123.2 2.018 123.387 2.117 123.593 2.176 c
+123.799 2.242 124.009 2.278 124.226 2.278 c
+124.497 2.278 124.733 2.227 124.931 2.132 c
+125.137 2.043 125.303 1.911 125.431 1.735 c
+125.567 1.565 125.666 1.359 125.725 1.118 c
+125.791 0.882 125.828 0.617 125.828 0.324 c
+125.828 0.309 l
+123.462 0.309 l
+123.462 0.162 123.476 0.023 123.505 -0.103 c
+123.543 -0.231 123.597 -0.345 123.667 -0.441 c
+123.734 -0.529 123.817 -0.599 123.917 -0.646 c
+124.013 -0.698 124.127 -0.72 124.255 -0.72 c
+124.409 -0.72 124.549 -0.687 124.667 -0.617 c
+124.792 -0.551 124.879 -0.448 124.931 -0.309 c
+125.769 -0.382 l
+125.739 -0.482 125.685 -0.588 125.608 -0.706 c
+125.527 -0.816 125.424 -0.918 125.299 -1.014 c
+125.181 -1.103 125.027 -1.176 124.843 -1.234 c
+124.667 -1.294 124.453 -1.323 124.211 -1.323 c
+124.211 1.706 m
+124.123 1.706 124.035 1.691 123.946 1.661 c
+123.858 1.632 123.778 1.58 123.711 1.515 c
+123.641 1.444 123.582 1.357 123.535 1.249 c
+123.495 1.139 123.476 1.014 123.476 0.867 c
+124.946 0.867 l
+124.946 1.004 124.92 1.124 124.873 1.235 c
+124.832 1.341 124.777 1.43 124.711 1.5 c
+124.652 1.565 124.578 1.617 124.49 1.646 c
+124.402 1.683 124.306 1.706 124.211 1.706 c
+128.272 -1.323 m
+127.985 -1.323 127.742 -1.282 127.537 -1.205 c
+127.331 -1.117 127.158 -0.995 127.022 -0.837 c
+126.882 -0.683 126.78 -0.496 126.714 -0.279 c
+126.643 -0.055 126.61 0.191 126.61 0.456 c
+126.61 0.75 126.643 1.007 126.714 1.235 c
+126.791 1.459 126.897 1.646 127.036 1.793 c
+127.184 1.947 127.36 2.065 127.566 2.146 c
+127.772 2.234 128.007 2.278 128.272 2.278 c
+128.496 2.278 128.698 2.249 128.874 2.19 c
+129.051 2.132 129.201 2.047 129.33 1.941 c
+129.455 1.841 129.558 1.72 129.639 1.573 c
+129.716 1.434 129.771 1.282 129.8 1.118 c
+128.889 1.073 l
+128.86 1.249 128.79 1.389 128.683 1.5 c
+128.584 1.606 128.441 1.661 128.257 1.661 c
+128.011 1.661 127.834 1.558 127.728 1.353 c
+127.618 1.154 127.566 0.867 127.566 0.485 c
+127.566 -0.309 127.801 -0.706 128.272 -0.706 c
+128.437 -0.706 128.58 -0.654 128.698 -0.544 c
+128.815 -0.437 128.889 -0.276 128.918 -0.058 c
+129.83 -0.103 l
+129.8 -0.272 129.745 -0.426 129.668 -0.573 c
+129.598 -0.72 129.496 -0.852 129.359 -0.97 c
+129.23 -1.08 129.072 -1.168 128.889 -1.234 c
+128.713 -1.294 128.507 -1.323 128.272 -1.323 c
+133.978 0.485 m
+133.978 0.21 133.942 -0.04 133.876 -0.264 c
+133.806 -0.482 133.703 -0.669 133.567 -0.823 c
+133.427 -0.981 133.251 -1.103 133.037 -1.19 c
+132.821 -1.278 132.567 -1.323 132.274 -1.323 c
+131.998 -1.323 131.751 -1.278 131.539 -1.19 c
+131.333 -1.103 131.16 -0.981 131.024 -0.823 c
+130.884 -0.669 130.782 -0.482 130.715 -0.264 c
+130.645 -0.04 130.612 0.21 130.612 0.485 c
+130.612 0.738 130.641 0.974 130.701 1.191 c
+130.767 1.415 130.87 1.606 131.009 1.764 c
+131.146 1.929 131.322 2.058 131.539 2.146 c
+131.751 2.234 132.009 2.278 132.303 2.278 c
+132.615 2.278 132.876 2.234 133.082 2.146 c
+133.295 2.058 133.467 1.929 133.596 1.764 c
+133.733 1.606 133.831 1.415 133.891 1.191 c
+133.949 0.974 133.978 0.738 133.978 0.485 c
+133.023 0.485 m
+133.023 0.691 133.008 0.867 132.979 1.014 c
+132.957 1.162 132.92 1.282 132.862 1.382 c
+132.802 1.477 132.729 1.548 132.64 1.588 c
+132.553 1.635 132.442 1.661 132.318 1.661 c
+132.053 1.661 131.862 1.562 131.744 1.367 c
+131.626 1.18 131.568 0.886 131.568 0.485 c
+131.568 0.063 131.626 -0.243 131.744 -0.426 c
+131.862 -0.613 132.038 -0.706 132.274 -0.706 c
+132.399 -0.706 132.513 -0.687 132.611 -0.646 c
+132.707 -0.599 132.788 -0.525 132.847 -0.426 c
+132.912 -0.33 132.957 -0.206 132.979 -0.058 c
+133.008 0.088 133.023 0.268 133.023 0.485 c
+137.851 1.47 m
+137.752 1.477 137.65 1.488 137.543 1.5 c
+137.433 1.517 137.311 1.529 137.176 1.529 c
+136.999 1.529 136.841 1.488 136.705 1.411 c
+136.565 1.341 136.448 1.242 136.353 1.118 c
+136.264 0.989 136.195 0.842 136.147 0.676 c
+136.106 0.507 136.087 0.331 136.087 0.147 c
+136.087 -1.264 l
+135.191 -1.264 l
+135.191 0.985 l
+135.191 1.11 135.18 1.235 135.162 1.353 c
+135.15 1.477 135.136 1.595 135.117 1.706 c
+135.106 1.823 135.092 1.918 135.073 1.999 c
+135.052 2.087 135.033 2.161 135.015 2.22 c
+135.896 2.22 l
+135.904 2.168 135.915 2.117 135.926 2.058 c
+135.944 1.999 135.959 1.933 135.97 1.867 c
+135.989 1.808 136.004 1.742 136.014 1.675 c
+136.021 1.606 136.033 1.544 136.044 1.484 c
+136.058 1.484 l
+136.095 1.602 136.147 1.709 136.205 1.808 c
+136.272 1.904 136.353 1.988 136.44 2.058 c
+136.529 2.124 136.632 2.18 136.75 2.22 c
+136.874 2.257 137.022 2.278 137.19 2.278 c
+137.315 2.278 137.433 2.271 137.543 2.263 c
+137.66 2.253 137.764 2.238 137.851 2.22 c
+h
+139.854 -1.323 m
+139.686 -1.323 139.535 -1.301 139.399 -1.264 c
+139.27 -1.216 139.156 -1.147 139.061 -1.058 c
+138.973 -0.97 138.903 -0.864 138.855 -0.735 c
+138.803 -0.599 138.782 -0.448 138.782 -0.279 c
+138.782 -0.073 138.815 0.096 138.884 0.235 c
+138.95 0.382 139.046 0.493 139.164 0.574 c
+139.289 0.661 139.432 0.724 139.59 0.765 c
+139.755 0.802 139.931 0.827 140.119 0.838 c
+140.839 0.852 l
+140.839 1.029 l
+140.839 1.147 140.828 1.249 140.81 1.338 c
+140.788 1.426 140.754 1.492 140.707 1.544 c
+140.667 1.602 140.619 1.639 140.56 1.661 c
+140.501 1.679 140.435 1.691 140.369 1.691 c
+140.299 1.691 140.237 1.679 140.178 1.661 c
+140.126 1.65 140.079 1.625 140.031 1.588 c
+139.99 1.558 139.958 1.507 139.927 1.44 c
+139.906 1.382 139.891 1.301 139.884 1.205 c
+138.943 1.249 l
+138.973 1.397 139.017 1.532 139.075 1.661 c
+139.142 1.786 139.237 1.897 139.355 1.985 c
+139.472 2.08 139.612 2.153 139.781 2.205 c
+139.958 2.253 140.163 2.278 140.398 2.278 c
+140.839 2.278 141.17 2.168 141.398 1.955 c
+141.633 1.75 141.751 1.44 141.751 1.029 c
+141.751 -0.235 l
+141.751 -0.455 l
+141.758 -0.515 141.772 -0.569 141.795 -0.617 c
+141.813 -0.658 141.843 -0.69 141.883 -0.72 c
+141.92 -0.742 141.971 -0.75 142.03 -0.75 c
+142.096 -0.75 142.165 -0.746 142.236 -0.735 c
+142.236 -1.22 l
+142.177 -1.23 142.122 -1.242 142.074 -1.249 c
+142.034 -1.261 141.993 -1.267 141.957 -1.278 c
+141.916 -1.286 141.872 -1.294 141.824 -1.294 c
+141.772 -1.301 141.714 -1.308 141.648 -1.308 c
+141.419 -1.308 141.255 -1.257 141.148 -1.147 c
+141.038 -1.029 140.975 -0.864 140.957 -0.646 c
+140.943 -0.646 l
+140.872 -0.757 140.802 -0.852 140.736 -0.941 c
+140.667 -1.022 140.59 -1.087 140.501 -1.147 c
+140.413 -1.205 140.314 -1.249 140.207 -1.278 c
+140.108 -1.308 139.99 -1.323 139.854 -1.323 c
+140.839 0.353 m
+140.413 0.339 l
+140.314 0.339 140.222 0.331 140.133 0.324 c
+140.053 0.312 139.987 0.287 139.927 0.25 c
+139.869 0.21 139.817 0.151 139.781 0.073 c
+139.74 0.004 139.722 -0.088 139.722 -0.206 c
+139.722 -0.374 139.755 -0.496 139.825 -0.573 c
+139.891 -0.654 139.99 -0.69 140.119 -0.69 c
+140.226 -0.69 140.324 -0.669 140.413 -0.617 c
+140.509 -0.569 140.59 -0.507 140.648 -0.426 c
+140.714 -0.349 140.766 -0.261 140.795 -0.162 c
+140.825 -0.055 140.839 0.059 140.839 0.177 c
+h
+143.562 1.602 m
+143.019 1.602 l
+143.019 2.22 l
+143.607 2.22 l
+143.885 3.117 l
+144.459 3.117 l
+144.459 2.22 l
+145.693 2.22 l
+145.693 1.602 l
+144.459 1.602 l
+144.459 -0.103 l
+144.459 -0.324 l
+144.467 -0.393 144.488 -0.455 144.517 -0.515 c
+144.554 -0.565 144.61 -0.61 144.679 -0.646 c
+144.756 -0.676 144.87 -0.69 145.018 -0.69 c
+145.153 -0.69 145.29 -0.687 145.429 -0.676 c
+145.565 -0.658 145.697 -0.632 145.826 -0.602 c
+145.826 -1.205 l
+145.745 -1.216 145.668 -1.23 145.591 -1.249 c
+145.51 -1.261 145.433 -1.267 145.356 -1.278 c
+145.275 -1.286 145.186 -1.294 145.091 -1.294 c
+145.003 -1.301 144.904 -1.308 144.797 -1.308 c
+144.61 -1.308 144.448 -1.294 144.312 -1.264 c
+144.183 -1.228 144.07 -1.183 143.974 -1.132 c
+143.885 -1.084 143.812 -1.025 143.753 -0.955 c
+143.694 -0.878 143.65 -0.801 143.621 -0.72 c
+143.592 -0.632 143.57 -0.544 143.562 -0.455 c
+143.551 -0.36 143.547 -0.264 143.547 -0.176 c
+h
+148.578 -1.323 m
+148.321 -1.323 148.093 -1.286 147.887 -1.22 c
+147.682 -1.143 147.505 -1.029 147.358 -0.881 c
+147.212 -0.727 147.094 -0.536 147.006 -0.309 c
+146.925 -0.085 146.888 0.181 146.888 0.485 c
+146.888 0.816 146.932 1.095 147.021 1.323 c
+147.116 1.558 147.245 1.742 147.403 1.882 c
+147.568 2.018 147.755 2.117 147.961 2.176 c
+148.166 2.242 148.376 2.278 148.593 2.278 c
+148.864 2.278 149.1 2.227 149.298 2.132 c
+149.504 2.043 149.67 1.911 149.799 1.735 c
+149.934 1.565 150.034 1.359 150.092 1.118 c
+150.158 0.882 150.196 0.617 150.196 0.324 c
+150.196 0.309 l
+147.829 0.309 l
+147.829 0.162 147.844 0.023 147.873 -0.103 c
+147.91 -0.231 147.964 -0.345 148.035 -0.441 c
+148.101 -0.529 148.185 -0.599 148.284 -0.646 c
+148.38 -0.698 148.494 -0.72 148.623 -0.72 c
+148.777 -0.72 148.916 -0.687 149.034 -0.617 c
+149.159 -0.551 149.247 -0.448 149.298 -0.309 c
+150.136 -0.382 l
+150.107 -0.482 150.052 -0.588 149.975 -0.706 c
+149.894 -0.816 149.791 -0.918 149.666 -1.014 c
+149.548 -1.103 149.394 -1.176 149.211 -1.234 c
+149.034 -1.294 148.821 -1.323 148.578 -1.323 c
+148.578 1.706 m
+148.49 1.706 148.402 1.691 148.314 1.661 c
+148.226 1.632 148.145 1.58 148.079 1.515 c
+148.008 1.444 147.95 1.357 147.902 1.249 c
+147.862 1.139 147.844 1.014 147.844 0.867 c
+149.313 0.867 l
+149.313 1.004 149.288 1.124 149.24 1.235 c
+149.199 1.341 149.144 1.43 149.078 1.5 c
+149.02 1.565 148.945 1.617 148.858 1.646 c
+148.769 1.683 148.673 1.706 148.578 1.706 c
+f
+Q
+q 1 0 0 1 51.1272 419.6667 cm
+0 0 m
+-1.808 0 l
+-2.219 -1.397 l
+-2.91 -1.397 l
+-1.19 3.954 l
+-0.617 3.954 l
+1.118 -1.397 l
+0.426 -1.397 l
+h
+-1.631 0.588 m
+-0.176 0.588 l
+-0.897 3.013 l
+h
+2.367 2.587 m
+2.381 2.146 l
+2.635 2.487 2.959 2.66 3.352 2.66 c
+4.057 2.66 4.414 2.19 4.424 1.249 c
+4.424 -1.397 l
+3.778 -1.397 l
+3.778 1.22 l
+3.778 1.532 3.723 1.753 3.616 1.882 c
+3.506 2.006 3.352 2.072 3.146 2.072 c
+2.988 2.072 2.841 2.017 2.705 1.911 c
+2.577 1.801 2.473 1.664 2.396 1.499 c
+2.396 -1.397 l
+1.75 -1.397 l
+1.75 2.587 l
+h
+6.982 0.779 m
+6.982 1.356 7.119 1.811 7.394 2.146 c
+7.676 2.487 8.048 2.66 8.511 2.66 c
+8.97 2.66 9.338 2.491 9.613 2.161 c
+9.897 1.837 10.043 1.389 10.055 0.823 c
+10.055 0.397 l
+10.055 -0.173 9.911 -0.628 9.628 -0.971 c
+9.353 -1.305 8.985 -1.47 8.526 -1.47 c
+8.063 -1.47 7.691 -1.309 7.408 -0.985 c
+7.133 -0.655 6.99 -0.214 6.982 0.338 c
+h
+7.629 0.397 m
+7.629 -0.008 7.707 -0.324 7.865 -0.559 c
+8.029 -0.794 8.25 -0.912 8.526 -0.912 c
+9.091 -0.912 9.386 -0.5 9.407 0.323 c
+9.407 0.779 l
+9.407 1.18 9.323 1.499 9.158 1.734 c
+9 1.976 8.783 2.102 8.511 2.102 c
+8.247 2.102 8.029 1.976 7.865 1.734 c
+7.707 1.499 7.629 1.18 7.629 0.779 c
+h
+11.965 -0.412 m
+12.715 2.587 l
+13.376 2.587 l
+12.2 -1.397 l
+11.715 -1.397 l
+10.525 2.587 l
+11.186 2.587 l
+h
+15.435 -1.47 m
+14.934 -1.47 14.552 -1.324 14.288 -1.029 c
+14.024 -0.736 13.891 -0.302 13.891 0.279 c
+13.891 0.75 l
+13.891 1.344 14.016 1.811 14.273 2.146 c
+14.537 2.487 14.898 2.66 15.36 2.66 c
+15.82 2.66 16.162 2.506 16.39 2.204 c
+16.625 1.911 16.746 1.448 16.757 0.823 c
+16.757 0.397 l
+14.537 0.397 l
+14.537 0.309 l
+14.537 -0.125 14.614 -0.437 14.772 -0.632 c
+14.938 -0.819 15.169 -0.912 15.464 -0.912 c
+15.659 -0.912 15.831 -0.879 15.978 -0.809 c
+16.125 -0.732 16.261 -0.614 16.39 -0.456 c
+16.727 -0.867 l
+16.441 -1.272 16.011 -1.47 15.435 -1.47 c
+15.36 2.102 m
+15.085 2.102 14.883 2.006 14.758 1.822 c
+14.629 1.635 14.556 1.344 14.537 0.955 c
+16.11 0.955 l
+16.11 1.043 l
+16.089 1.425 16.022 1.693 15.905 1.851 c
+15.787 2.017 15.603 2.102 15.36 2.102 c
+19.183 1.969 m
+19.094 1.988 18.995 1.999 18.888 1.999 c
+18.554 1.999 18.319 1.815 18.183 1.455 c
+18.183 -1.397 l
+17.536 -1.397 l
+17.536 2.587 l
+18.169 2.587 l
+18.183 2.175 l
+18.359 2.499 18.602 2.66 18.918 2.66 c
+19.025 2.66 19.113 2.638 19.183 2.601 c
+h
+21.035 -0.412 m
+21.785 2.587 l
+22.446 2.587 l
+21.27 -1.397 l
+20.785 -1.397 l
+19.594 2.587 l
+20.255 2.587 l
+h
+23.827 -1.397 -0.646 3.984 re
+23.871 3.63 m
+23.871 3.52 23.842 3.428 23.783 3.351 c
+23.725 3.281 23.629 3.248 23.504 3.248 c
+23.387 3.248 23.291 3.281 23.225 3.351 c
+23.166 3.428 23.137 3.52 23.137 3.63 c
+23.137 3.748 23.166 3.84 23.225 3.91 c
+23.291 3.987 23.387 4.027 23.504 4.027 c
+23.629 4.027 23.725 3.987 23.783 3.91 c
+23.842 3.829 23.871 3.737 23.871 3.63 c
+26.267 -1.47 m
+25.768 -1.47 25.386 -1.324 25.121 -1.029 c
+24.856 -0.736 24.724 -0.302 24.724 0.279 c
+24.724 0.75 l
+24.724 1.344 24.849 1.811 25.106 2.146 c
+25.371 2.487 25.731 2.66 26.194 2.66 c
+26.653 2.66 26.995 2.506 27.223 2.204 c
+27.458 1.911 27.58 1.448 27.59 0.823 c
+27.59 0.397 l
+25.371 0.397 l
+25.371 0.309 l
+25.371 -0.125 25.448 -0.437 25.606 -0.632 c
+25.772 -0.819 26.003 -0.912 26.296 -0.912 c
+26.491 -0.912 26.664 -0.879 26.811 -0.809 c
+26.959 -0.732 27.094 -0.614 27.223 -0.456 c
+27.561 -0.867 l
+27.274 -1.272 26.844 -1.47 26.267 -1.47 c
+26.194 2.102 m
+25.918 2.102 25.716 2.006 25.591 1.822 c
+25.463 1.635 25.39 1.344 25.371 0.955 c
+26.944 0.955 l
+26.944 1.043 l
+26.921 1.425 26.855 1.693 26.738 1.851 c
+26.62 2.017 26.437 2.102 26.194 2.102 c
+31.339 -0.25 m
+31.941 2.587 l
+32.588 2.587 l
+31.603 -1.397 l
+31.089 -1.397 l
+30.31 1.455 l
+29.56 -1.397 l
+29.031 -1.397 l
+28.075 2.587 l
+28.708 2.587 l
+29.325 -0.177 l
+30.059 2.587 l
+30.574 2.587 l
+h
+37.953 -0.25 m
+38.556 2.587 l
+39.202 2.587 l
+38.218 -1.397 l
+37.703 -1.397 l
+36.924 1.455 l
+36.174 -1.397 l
+35.646 -1.397 l
+34.69 2.587 l
+35.322 2.587 l
+35.939 -0.177 l
+36.675 2.587 l
+37.188 2.587 l
+h
+40.584 -1.397 -0.647 3.984 re
+40.629 3.63 m
+40.629 3.52 40.6 3.428 40.54 3.351 c
+40.481 3.281 40.386 3.248 40.261 3.248 c
+40.143 3.248 40.048 3.281 39.981 3.351 c
+39.923 3.428 39.894 3.52 39.894 3.63 c
+39.894 3.748 39.923 3.84 39.981 3.91 c
+40.048 3.987 40.143 4.027 40.261 4.027 c
+40.386 4.027 40.481 3.987 40.54 3.91 c
+40.6 3.829 40.629 3.737 40.629 3.63 c
+42.451 3.542 m
+42.451 2.587 l
+43.054 2.587 l
+43.054 2.057 l
+42.451 2.057 l
+42.451 -0.412 l
+42.451 -0.57 42.473 -0.688 42.524 -0.765 c
+42.584 -0.846 42.672 -0.882 42.79 -0.882 c
+42.877 -0.882 42.965 -0.867 43.054 -0.838 c
+43.054 -1.397 l
+42.907 -1.444 42.753 -1.47 42.598 -1.47 c
+42.341 -1.47 42.146 -1.378 42.011 -1.191 c
+41.87 -1.008 41.805 -0.746 41.805 -0.412 c
+41.805 2.057 l
+41.202 2.057 l
+41.202 2.587 l
+41.805 2.587 l
+41.805 3.542 l
+h
+44.465 2.175 m
+44.718 2.499 45.038 2.66 45.42 2.66 c
+46.125 2.66 46.482 2.19 46.493 1.249 c
+46.493 -1.397 l
+45.847 -1.397 l
+45.847 1.22 l
+45.847 1.532 45.791 1.753 45.685 1.882 c
+45.575 2.006 45.42 2.072 45.215 2.072 c
+45.057 2.072 44.91 2.017 44.774 1.911 c
+44.645 1.801 44.542 1.664 44.465 1.499 c
+44.465 -1.397 l
+43.818 -1.397 l
+43.818 4.247 l
+44.465 4.247 l
+h
+50.815 1.969 m
+50.727 1.988 50.628 1.999 50.521 1.999 c
+50.187 1.999 49.951 1.815 49.815 1.455 c
+49.815 -1.397 l
+49.169 -1.397 l
+49.169 2.587 l
+49.801 2.587 l
+49.815 2.175 l
+49.992 2.499 50.235 2.66 50.551 2.66 c
+50.657 2.66 50.745 2.638 50.815 2.601 c
+h
+52.814 -1.47 m
+52.315 -1.47 51.932 -1.324 51.667 -1.029 c
+51.403 -0.736 51.27 -0.302 51.27 0.279 c
+51.27 0.75 l
+51.27 1.344 51.395 1.811 51.653 2.146 c
+51.918 2.487 52.278 2.66 52.741 2.66 c
+53.2 2.66 53.541 2.506 53.77 2.204 c
+54.005 1.911 54.125 1.448 54.137 0.823 c
+54.137 0.397 l
+51.918 0.397 l
+51.918 0.309 l
+51.918 -0.125 51.995 -0.437 52.153 -0.632 c
+52.318 -0.819 52.55 -0.912 52.843 -0.912 c
+53.038 -0.912 53.211 -0.879 53.358 -0.809 c
+53.505 -0.732 53.641 -0.614 53.77 -0.456 c
+54.108 -0.867 l
+53.821 -1.272 53.391 -1.47 52.814 -1.47 c
+52.741 2.102 m
+52.465 2.102 52.263 2.006 52.138 1.822 c
+52.009 1.635 51.935 1.344 51.918 0.955 c
+53.49 0.955 l
+53.49 1.043 l
+53.468 1.425 53.402 1.693 53.284 1.851 c
+53.167 2.017 52.983 2.102 52.741 2.102 c
+55.21 -1.397 m
+55.21 2.057 l
+54.68 2.057 l
+54.68 2.587 l
+55.21 2.587 l
+55.21 3.042 l
+55.21 3.443 55.305 3.755 55.504 3.983 c
+55.71 4.207 55.989 4.321 56.342 4.321 c
+56.477 4.321 56.61 4.299 56.739 4.263 c
+56.709 3.719 l
+56.61 3.737 56.511 3.748 56.415 3.748 c
+56.041 3.748 55.856 3.484 55.856 2.954 c
+55.856 2.587 l
+56.533 2.587 l
+56.533 2.057 l
+55.856 2.057 l
+55.856 -1.397 l
+h
+58.634 -1.47 m
+58.135 -1.47 57.753 -1.324 57.489 -1.029 c
+57.223 -0.736 57.092 -0.302 57.092 0.279 c
+57.092 0.75 l
+57.092 1.344 57.217 1.811 57.474 2.146 c
+57.738 2.487 58.098 2.66 58.561 2.66 c
+59.021 2.66 59.363 2.506 59.59 2.204 c
+59.825 1.911 59.947 1.448 59.958 0.823 c
+59.958 0.397 l
+57.738 0.397 l
+57.738 0.309 l
+57.738 -0.125 57.815 -0.437 57.973 -0.632 c
+58.139 -0.819 58.37 -0.912 58.665 -0.912 c
+58.859 -0.912 59.031 -0.879 59.179 -0.809 c
+59.326 -0.732 59.461 -0.614 59.59 -0.456 c
+59.928 -0.867 l
+59.642 -1.272 59.212 -1.47 58.634 -1.47 c
+58.561 2.102 m
+58.285 2.102 58.083 2.006 57.959 1.822 c
+57.83 1.635 57.757 1.344 57.738 0.955 c
+59.311 0.955 l
+59.311 1.043 l
+59.289 1.425 59.222 1.693 59.105 1.851 c
+58.987 2.017 58.804 2.102 58.561 2.102 c
+62.383 1.969 m
+62.295 1.988 62.196 1.999 62.089 1.999 c
+61.755 1.999 61.52 1.815 61.383 1.455 c
+61.383 -1.397 l
+60.737 -1.397 l
+60.737 2.587 l
+61.369 2.587 l
+61.383 2.175 l
+61.56 2.499 61.803 2.66 62.118 2.66 c
+62.225 2.66 62.314 2.638 62.383 2.601 c
+h
+64.382 -1.47 m
+63.883 -1.47 63.5 -1.324 63.236 -1.029 c
+62.971 -0.736 62.839 -0.302 62.839 0.279 c
+62.839 0.75 l
+62.839 1.344 62.964 1.811 63.221 2.146 c
+63.486 2.487 63.845 2.66 64.309 2.66 c
+64.768 2.66 65.11 2.506 65.337 2.204 c
+65.572 1.911 65.694 1.448 65.705 0.823 c
+65.705 0.397 l
+63.486 0.397 l
+63.486 0.309 l
+63.486 -0.125 63.563 -0.437 63.721 -0.632 c
+63.886 -0.819 64.118 -0.912 64.411 -0.912 c
+64.606 -0.912 64.779 -0.879 64.926 -0.809 c
+65.073 -0.732 65.209 -0.614 65.337 -0.456 c
+65.676 -0.867 l
+65.389 -1.272 64.959 -1.47 64.382 -1.47 c
+64.309 2.102 m
+64.033 2.102 63.831 2.006 63.706 1.822 c
+63.577 1.635 63.504 1.344 63.486 0.955 c
+65.059 0.955 l
+65.059 1.043 l
+65.036 1.425 64.97 1.693 64.853 1.851 c
+64.735 2.017 64.551 2.102 64.309 2.102 c
+67.101 2.587 m
+67.116 2.146 l
+67.37 2.487 67.693 2.66 68.086 2.66 c
+68.792 2.66 69.148 2.19 69.159 1.249 c
+69.159 -1.397 l
+68.512 -1.397 l
+68.512 1.22 l
+68.512 1.532 68.458 1.753 68.351 1.882 c
+68.24 2.006 68.086 2.072 67.881 2.072 c
+67.723 2.072 67.575 2.017 67.44 1.911 c
+67.311 1.801 67.208 1.664 67.131 1.499 c
+67.131 -1.397 l
+66.484 -1.397 l
+66.484 2.587 l
+h
+71.511 -0.912 m
+71.724 -0.912 71.897 -0.849 72.026 -0.721 c
+72.161 -0.584 72.235 -0.393 72.246 -0.148 c
+72.863 -0.148 l
+72.841 -0.53 72.705 -0.849 72.452 -1.103 c
+72.194 -1.349 71.882 -1.47 71.511 -1.47 c
+71.018 -1.47 70.644 -1.32 70.38 -1.014 c
+70.122 -0.702 69.998 -0.235 69.998 0.382 c
+69.998 0.823 l
+69.998 1.418 70.122 1.874 70.38 2.19 c
+70.644 2.502 71.018 2.66 71.511 2.66 c
+71.912 2.66 72.231 2.528 72.466 2.263 c
+72.709 2.006 72.841 1.66 72.863 1.22 c
+72.246 1.22 l
+72.224 1.514 72.151 1.734 72.026 1.882 c
+71.908 2.028 71.735 2.102 71.511 2.102 c
+71.217 2.102 71 2.003 70.864 1.808 c
+70.725 1.62 70.651 1.311 70.644 0.882 c
+70.644 0.367 l
+70.644 -0.103 70.71 -0.437 70.85 -0.632 c
+70.997 -0.819 71.217 -0.912 71.511 -0.912 c
+75.039 -1.47 m
+74.539 -1.47 74.157 -1.324 73.892 -1.029 c
+73.628 -0.736 73.495 -0.302 73.495 0.279 c
+73.495 0.75 l
+73.495 1.344 73.62 1.811 73.877 2.146 c
+74.143 2.487 74.503 2.66 74.966 2.66 c
+75.425 2.66 75.766 2.506 75.994 2.204 c
+76.229 1.911 76.35 1.448 76.362 0.823 c
+76.362 0.397 l
+74.143 0.397 l
+74.143 0.309 l
+74.143 -0.125 74.22 -0.437 74.378 -0.632 c
+74.543 -0.819 74.775 -0.912 75.068 -0.912 c
+75.263 -0.912 75.436 -0.879 75.583 -0.809 c
+75.73 -0.732 75.866 -0.614 75.994 -0.456 c
+76.333 -0.867 l
+76.046 -1.272 75.616 -1.47 75.039 -1.47 c
+74.966 2.102 m
+74.69 2.102 74.488 2.006 74.363 1.822 c
+74.234 1.635 74.16 1.344 74.143 0.955 c
+75.715 0.955 l
+75.715 1.043 l
+75.693 1.425 75.627 1.693 75.509 1.851 c
+75.392 2.017 75.208 2.102 74.966 2.102 c
+79.081 -0.383 m
+79.081 -0.235 79.026 -0.114 78.92 -0.015 c
+78.809 0.081 78.603 0.199 78.302 0.338 c
+77.956 0.484 77.714 0.606 77.567 0.706 c
+77.42 0.812 77.31 0.929 77.244 1.058 c
+77.174 1.182 77.141 1.341 77.141 1.529 c
+77.141 1.851 77.258 2.12 77.493 2.337 c
+77.729 2.55 78.031 2.66 78.405 2.66 c
+78.787 2.66 79.095 2.547 79.331 2.322 c
+79.566 2.094 79.683 1.808 79.683 1.455 c
+79.037 1.455 l
+79.037 1.631 78.978 1.782 78.86 1.911 c
+78.743 2.036 78.588 2.102 78.405 2.102 c
+78.206 2.102 78.056 2.046 77.95 1.94 c
+77.839 1.841 77.788 1.708 77.788 1.543 c
+77.788 1.415 77.825 1.308 77.905 1.22 c
+77.983 1.139 78.174 1.036 78.478 0.911 c
+78.956 0.723 79.286 0.536 79.463 0.353 c
+79.64 0.176 79.728 -0.052 79.728 -0.324 c
+79.728 -0.676 79.603 -0.956 79.361 -1.162 c
+79.126 -1.367 78.809 -1.47 78.42 -1.47 c
+77.997 -1.47 77.659 -1.353 77.405 -1.118 c
+77.148 -0.875 77.023 -0.57 77.023 -0.206 c
+77.67 -0.206 l
+77.678 -0.434 77.747 -0.611 77.875 -0.736 c
+78 -0.853 78.185 -0.912 78.42 -0.912 c
+78.633 -0.912 78.794 -0.864 78.905 -0.765 c
+79.022 -0.669 79.081 -0.541 79.081 -0.383 c
+82.991 -1.397 -0.647 5.644 re
+86.048 -1.397 m
+86.008 -1.309 85.983 -1.162 85.975 -0.956 c
+85.74 -1.301 85.445 -1.47 85.093 -1.47 c
+84.73 -1.47 84.446 -1.374 84.24 -1.176 c
+84.042 -0.971 83.947 -0.684 83.947 -0.309 c
+83.947 0.091 84.082 0.411 84.358 0.646 c
+84.63 0.889 85.005 1.014 85.476 1.014 c
+85.96 1.014 l
+85.96 1.44 l
+85.96 1.675 85.906 1.841 85.798 1.94 c
+85.688 2.046 85.526 2.102 85.314 2.102 c
+85.115 2.102 84.953 2.043 84.828 1.926 c
+84.711 1.808 84.652 1.66 84.652 1.484 c
+84.005 1.484 l
+84.005 1.679 84.064 1.87 84.182 2.057 c
+84.306 2.241 84.468 2.389 84.667 2.499 c
+84.873 2.605 85.1 2.66 85.358 2.66 c
+85.758 2.66 86.064 2.557 86.269 2.352 c
+86.482 2.146 86.596 1.851 86.607 1.469 c
+86.607 -0.544 l
+86.607 -0.849 86.644 -1.114 86.725 -1.338 c
+86.725 -1.397 l
+h
+85.181 -0.882 m
+85.347 -0.882 85.497 -0.838 85.636 -0.75 c
+85.784 -0.661 85.89 -0.551 85.96 -0.412 c
+85.96 0.529 l
+85.593 0.529 l
+85.277 0.529 85.034 0.459 84.858 0.323 c
+84.682 0.195 84.593 0.007 84.593 -0.235 c
+84.593 -0.464 84.637 -0.628 84.726 -0.736 c
+84.814 -0.834 84.965 -0.882 85.181 -0.882 c
+90.488 0.397 m
+90.488 -0.221 90.374 -0.688 90.149 -1 c
+89.933 -1.316 89.609 -1.47 89.179 -1.47 c
+88.757 -1.47 88.445 -1.29 88.239 -0.927 c
+88.209 -1.397 l
+87.606 -1.397 l
+87.606 4.247 l
+88.254 4.247 l
+88.254 2.146 l
+88.466 2.487 88.775 2.66 89.179 2.66 c
+89.609 2.66 89.933 2.502 90.149 2.19 c
+90.374 1.885 90.488 1.418 90.488 0.793 c
+h
+89.841 0.779 m
+89.841 1.249 89.771 1.579 89.635 1.778 c
+89.507 1.973 89.297 2.072 89.003 2.072 c
+88.668 2.072 88.419 1.888 88.254 1.529 c
+88.254 -0.353 l
+88.419 -0.717 88.672 -0.897 89.017 -0.897 c
+89.312 -0.897 89.521 -0.794 89.65 -0.588 c
+89.775 -0.383 89.841 -0.067 89.841 0.367 c
+h
+92.751 -1.47 m
+92.252 -1.47 91.87 -1.324 91.605 -1.029 c
+91.34 -0.736 91.208 -0.302 91.208 0.279 c
+91.208 0.75 l
+91.208 1.344 91.333 1.811 91.59 2.146 c
+91.855 2.487 92.215 2.66 92.678 2.66 c
+93.137 2.66 93.478 2.506 93.707 2.204 c
+93.942 1.911 94.064 1.448 94.074 0.823 c
+94.074 0.397 l
+91.855 0.397 l
+91.855 0.309 l
+91.855 -0.125 91.932 -0.437 92.09 -0.632 c
+92.256 -0.819 92.487 -0.912 92.78 -0.912 c
+92.975 -0.912 93.148 -0.879 93.295 -0.809 c
+93.442 -0.732 93.578 -0.614 93.707 -0.456 c
+94.045 -0.867 l
+93.758 -1.272 93.328 -1.47 92.751 -1.47 c
+92.678 2.102 m
+92.402 2.102 92.2 2.006 92.075 1.822 c
+91.947 1.635 91.873 1.344 91.855 0.955 c
+93.428 0.955 l
+93.428 1.043 l
+93.405 1.425 93.339 1.693 93.222 1.851 c
+93.104 2.017 92.921 2.102 92.678 2.102 c
+95.558 -1.397 -0.646 5.644 re
+98.513 -0.383 m
+98.513 -0.235 98.458 -0.114 98.351 -0.015 c
+98.241 0.081 98.035 0.199 97.734 0.338 c
+97.389 0.484 97.146 0.606 97 0.706 c
+96.852 0.812 96.742 0.929 96.676 1.058 c
+96.606 1.182 96.573 1.341 96.573 1.529 c
+96.573 1.851 96.69 2.12 96.925 2.337 c
+97.161 2.55 97.462 2.66 97.837 2.66 c
+98.22 2.66 98.528 2.547 98.764 2.322 c
+98.999 2.094 99.116 1.808 99.116 1.455 c
+98.469 1.455 l
+98.469 1.631 98.411 1.782 98.293 1.911 c
+98.176 2.036 98.021 2.102 97.837 2.102 c
+97.638 2.102 97.488 2.046 97.382 1.94 c
+97.272 1.841 97.22 1.708 97.22 1.543 c
+97.22 1.415 97.256 1.308 97.337 1.22 c
+97.415 1.139 97.605 1.036 97.91 0.911 c
+98.388 0.723 98.719 0.536 98.895 0.353 c
+99.072 0.176 99.161 -0.052 99.161 -0.324 c
+99.161 -0.676 99.035 -0.956 98.793 -1.162 c
+98.558 -1.367 98.241 -1.47 97.852 -1.47 c
+97.43 -1.47 97.091 -1.353 96.838 -1.118 c
+96.58 -0.875 96.455 -0.57 96.455 -0.206 c
+97.102 -0.206 l
+97.11 -0.434 97.179 -0.611 97.308 -0.736 c
+97.433 -0.853 97.617 -0.912 97.852 -0.912 c
+98.065 -0.912 98.226 -0.864 98.336 -0.765 c
+98.455 -0.669 98.513 -0.541 98.513 -0.383 c
+103.761 -1.397 m
+103.72 -1.309 103.695 -1.162 103.687 -0.956 c
+103.452 -1.301 103.159 -1.47 102.805 -1.47 c
+102.442 -1.47 102.159 -1.374 101.953 -1.176 c
+101.754 -0.971 101.659 -0.684 101.659 -0.309 c
+101.659 0.091 101.795 0.411 102.07 0.646 c
+102.342 0.889 102.717 1.014 103.188 1.014 c
+103.672 1.014 l
+103.672 1.44 l
+103.672 1.675 103.618 1.841 103.51 1.94 c
+103.4 2.046 103.239 2.102 103.026 2.102 c
+102.828 2.102 102.666 2.043 102.541 1.926 c
+102.423 1.808 102.365 1.66 102.365 1.484 c
+101.717 1.484 l
+101.717 1.679 101.777 1.87 101.894 2.057 c
+102.019 2.241 102.18 2.389 102.379 2.499 c
+102.585 2.605 102.812 2.66 103.07 2.66 c
+103.471 2.66 103.776 2.557 103.981 2.352 c
+104.194 2.146 104.308 1.851 104.32 1.469 c
+104.32 -0.544 l
+104.32 -0.849 104.356 -1.114 104.437 -1.338 c
+104.437 -1.397 l
+h
+102.893 -0.882 m
+103.059 -0.882 103.209 -0.838 103.35 -0.75 c
+103.496 -0.661 103.603 -0.551 103.672 -0.412 c
+103.672 0.529 l
+103.305 0.529 l
+102.989 0.529 102.747 0.459 102.57 0.323 c
+102.394 0.195 102.305 0.007 102.305 -0.235 c
+102.305 -0.464 102.35 -0.628 102.438 -0.736 c
+102.526 -0.834 102.677 -0.882 102.893 -0.882 c
+105.937 2.587 m
+105.951 2.146 l
+106.205 2.487 106.528 2.66 106.922 2.66 c
+107.627 2.66 107.983 2.19 107.994 1.249 c
+107.994 -1.397 l
+107.348 -1.397 l
+107.348 1.22 l
+107.348 1.532 107.292 1.753 107.186 1.882 c
+107.076 2.006 106.922 2.072 106.716 2.072 c
+106.558 2.072 106.411 2.017 106.274 1.911 c
+106.146 1.801 106.043 1.664 105.966 1.499 c
+105.966 -1.397 l
+105.319 -1.397 l
+105.319 2.587 l
+h
+108.832 0.779 m
+108.832 1.385 108.942 1.851 109.17 2.175 c
+109.405 2.499 109.733 2.66 110.155 2.66 c
+110.537 2.66 110.835 2.502 111.051 2.19 c
+111.051 4.247 l
+111.699 4.247 l
+111.699 -1.397 l
+111.111 -1.397 l
+111.067 -0.971 l
+110.861 -1.305 110.556 -1.47 110.155 -1.47 c
+109.744 -1.47 109.42 -1.316 109.185 -1 c
+108.95 -0.676 108.832 -0.221 108.832 0.367 c
+h
+109.479 0.397 m
+109.479 -0.044 109.542 -0.375 109.67 -0.588 c
+109.806 -0.794 110.026 -0.897 110.331 -0.897 c
+110.654 -0.897 110.893 -0.736 111.051 -0.412 c
+111.051 1.602 l
+110.882 1.914 110.644 2.072 110.331 2.072 c
+110.026 2.072 109.806 1.969 109.67 1.764 c
+109.542 1.558 109.479 1.234 109.479 0.793 c
+h
+115.065 2.175 m
+115.318 2.499 115.638 2.66 116.02 2.66 c
+116.725 2.66 117.082 2.19 117.093 1.249 c
+117.093 -1.397 l
+116.446 -1.397 l
+116.446 1.22 l
+116.446 1.532 116.391 1.753 116.285 1.882 c
+116.174 2.006 116.02 2.072 115.814 2.072 c
+115.656 2.072 115.509 2.017 115.373 1.911 c
+115.244 1.801 115.142 1.664 115.065 1.499 c
+115.065 -1.397 l
+114.417 -1.397 l
+114.417 4.247 l
+115.065 4.247 l
+h
+118.754 -1.397 -0.647 3.984 re
+118.798 3.63 m
+118.798 3.52 118.768 3.428 118.71 3.351 c
+118.651 3.281 118.556 3.248 118.431 3.248 c
+118.313 3.248 118.217 3.281 118.151 3.351 c
+118.092 3.428 118.063 3.52 118.063 3.63 c
+118.063 3.748 118.092 3.84 118.151 3.91 c
+118.217 3.987 118.313 4.027 118.431 4.027 c
+118.556 4.027 118.651 3.987 118.71 3.91 c
+118.768 3.829 118.798 3.737 118.798 3.63 c
+121.708 -0.383 m
+121.708 -0.235 121.654 -0.114 121.546 -0.015 c
+121.436 0.081 121.23 0.199 120.929 0.338 c
+120.584 0.484 120.341 0.606 120.195 0.706 c
+120.048 0.812 119.937 0.929 119.871 1.058 c
+119.801 1.182 119.768 1.341 119.768 1.529 c
+119.768 1.851 119.886 2.12 120.121 2.337 c
+120.356 2.55 120.657 2.66 121.033 2.66 c
+121.415 2.66 121.723 2.547 121.958 2.322 c
+122.194 2.094 122.311 1.808 122.311 1.455 c
+121.664 1.455 l
+121.664 1.631 121.606 1.782 121.488 1.911 c
+121.37 2.036 121.216 2.102 121.033 2.102 c
+120.834 2.102 120.683 2.046 120.576 1.94 c
+120.466 1.841 120.415 1.708 120.415 1.543 c
+120.415 1.415 120.451 1.308 120.532 1.22 c
+120.609 1.139 120.8 1.036 121.106 0.911 c
+121.583 0.723 121.914 0.536 122.09 0.353 c
+122.267 0.176 122.355 -0.052 122.355 -0.324 c
+122.355 -0.676 122.23 -0.956 121.988 -1.162 c
+121.752 -1.367 121.436 -1.47 121.047 -1.47 c
+120.624 -1.47 120.287 -1.353 120.033 -1.118 c
+119.776 -0.875 119.651 -0.57 119.651 -0.206 c
+120.297 -0.206 l
+120.305 -0.434 120.374 -0.611 120.503 -0.736 c
+120.628 -0.853 120.812 -0.912 121.047 -0.912 c
+121.26 -0.912 121.422 -0.864 121.532 -0.765 c
+121.65 -0.669 121.708 -0.541 121.708 -0.383 c
+124.046 3.542 m
+124.046 2.587 l
+124.648 2.587 l
+124.648 2.057 l
+124.046 2.057 l
+124.046 -0.412 l
+124.046 -0.57 124.068 -0.688 124.119 -0.765 c
+124.178 -0.846 124.266 -0.882 124.384 -0.882 c
+124.472 -0.882 124.56 -0.867 124.648 -0.838 c
+124.648 -1.397 l
+124.501 -1.444 124.347 -1.47 124.193 -1.47 c
+123.935 -1.47 123.74 -1.378 123.605 -1.191 c
+123.465 -1.008 123.399 -0.746 123.399 -0.412 c
+123.399 2.057 l
+122.796 2.057 l
+122.796 2.587 l
+123.399 2.587 l
+123.399 3.542 l
+h
+125.207 0.779 m
+125.207 1.356 125.343 1.811 125.618 2.146 c
+125.901 2.487 126.273 2.66 126.735 2.66 c
+127.195 2.66 127.562 2.491 127.838 2.161 c
+128.121 1.837 128.268 1.389 128.279 0.823 c
+128.279 0.397 l
+128.279 -0.173 128.135 -0.628 127.853 -0.971 c
+127.577 -1.305 127.21 -1.47 126.751 -1.47 c
+126.287 -1.47 125.916 -1.309 125.633 -0.985 c
+125.357 -0.655 125.214 -0.214 125.207 0.338 c
+h
+125.853 0.397 m
+125.853 -0.008 125.931 -0.324 126.088 -0.559 c
+126.254 -0.794 126.475 -0.912 126.751 -0.912 c
+127.316 -0.912 127.61 -0.5 127.632 0.323 c
+127.632 0.779 l
+127.632 1.18 127.547 1.499 127.383 1.734 c
+127.225 1.976 127.007 2.102 126.735 2.102 c
+126.471 2.102 126.254 1.976 126.088 1.734 c
+125.931 1.499 125.853 1.18 125.853 0.779 c
+h
+130.763 1.969 m
+130.675 1.988 130.576 1.999 130.469 1.999 c
+130.134 1.999 129.899 1.815 129.764 1.455 c
+129.764 -1.397 l
+129.116 -1.397 l
+129.116 2.587 l
+129.749 2.587 l
+129.764 2.175 l
+129.94 2.499 130.182 2.66 130.498 2.66 c
+130.605 2.66 130.693 2.638 130.763 2.601 c
+h
+132.571 -0.309 m
+133.291 2.587 l
+133.982 2.587 l
+132.688 -1.955 l
+132.59 -2.297 132.446 -2.558 132.262 -2.735 c
+132.086 -2.911 131.884 -2.999 131.659 -2.999 c
+131.572 -2.999 131.458 -2.977 131.322 -2.94 c
+131.322 -2.396 l
+131.468 -2.411 l
+131.653 -2.411 131.799 -2.367 131.91 -2.278 c
+132.016 -2.19 132.104 -2.032 132.174 -1.808 c
+132.291 -1.367 l
+131.131 2.587 l
+131.836 2.587 l
+h
+136.201 0.779 m
+136.201 1.396 136.312 1.859 136.54 2.175 c
+136.764 2.499 137.099 2.66 137.539 2.66 c
+137.94 2.66 138.244 2.484 138.451 2.131 c
+138.495 2.587 l
+139.083 2.587 l
+139.083 -1.441 l
+139.083 -1.929 138.954 -2.308 138.701 -2.573 c
+138.443 -2.837 138.09 -2.97 137.642 -2.97 c
+137.444 -2.97 137.223 -2.918 136.981 -2.822 c
+136.735 -2.723 136.555 -2.602 136.436 -2.455 c
+136.702 -2.014 l
+136.966 -2.278 137.263 -2.411 137.598 -2.411 c
+138.134 -2.411 138.41 -2.117 138.421 -1.529 c
+138.421 -1 l
+138.215 -1.316 137.914 -1.47 137.525 -1.47 c
+137.113 -1.47 136.79 -1.32 136.555 -1.014 c
+136.326 -0.702 136.209 -0.25 136.201 0.338 c
+h
+136.863 0.397 m
+136.863 -0.044 136.926 -0.375 137.054 -0.588 c
+137.18 -0.794 137.396 -0.897 137.701 -0.897 c
+138.024 -0.897 138.263 -0.732 138.421 -0.397 c
+138.421 1.587 l
+138.252 1.911 138.013 2.072 137.701 2.072 c
+137.407 2.072 137.19 1.969 137.054 1.764 c
+136.926 1.558 136.863 1.234 136.863 0.793 c
+h
+141.685 1.969 m
+141.596 1.988 141.497 1.999 141.39 1.999 c
+141.056 1.999 140.821 1.815 140.685 1.455 c
+140.685 -1.397 l
+140.038 -1.397 l
+140.038 2.587 l
+140.67 2.587 l
+140.685 2.175 l
+140.862 2.499 141.103 2.66 141.419 2.66 c
+141.527 2.66 141.614 2.638 141.685 2.601 c
+h
+144.228 -1.397 m
+144.187 -1.309 144.161 -1.162 144.154 -0.956 c
+143.919 -1.301 143.625 -1.47 143.272 -1.47 c
+142.908 -1.47 142.626 -1.374 142.419 -1.176 c
+142.221 -0.971 142.125 -0.684 142.125 -0.309 c
+142.125 0.091 142.261 0.411 142.537 0.646 c
+142.809 0.889 143.183 1.014 143.654 1.014 c
+144.139 1.014 l
+144.139 1.44 l
+144.139 1.675 144.084 1.841 143.977 1.94 c
+143.867 2.046 143.705 2.102 143.492 2.102 c
+143.294 2.102 143.133 2.043 143.007 1.926 c
+142.89 1.808 142.831 1.66 142.831 1.484 c
+142.184 1.484 l
+142.184 1.679 142.243 1.87 142.36 2.057 c
+142.485 2.241 142.647 2.389 142.846 2.499 c
+143.052 2.605 143.279 2.66 143.536 2.66 c
+143.937 2.66 144.242 2.557 144.448 2.352 c
+144.661 2.146 144.775 1.851 144.786 1.469 c
+144.786 -0.544 l
+144.786 -0.849 144.823 -1.114 144.903 -1.338 c
+144.903 -1.397 l
+h
+143.36 -0.882 m
+143.526 -0.882 143.676 -0.838 143.816 -0.75 c
+143.962 -0.661 144.07 -0.551 144.139 -0.412 c
+144.139 0.529 l
+143.771 0.529 l
+143.455 0.529 143.213 0.459 143.037 0.323 c
+142.861 0.195 142.772 0.007 142.772 -0.235 c
+142.772 -0.464 142.816 -0.628 142.905 -0.736 c
+142.992 -0.834 143.143 -0.882 143.36 -0.882 c
+148.666 0.397 m
+148.666 -0.231 148.549 -0.702 148.314 -1.014 c
+148.086 -1.32 147.769 -1.47 147.358 -1.47 c
+146.954 -1.47 146.645 -1.32 146.433 -1.014 c
+146.433 -2.926 l
+145.785 -2.926 l
+145.785 2.587 l
+146.373 2.587 l
+146.418 2.146 l
+146.63 2.487 146.94 2.66 147.343 2.66 c
+147.785 2.66 148.112 2.506 148.328 2.204 c
+148.542 1.899 148.656 1.444 148.666 0.837 c
+h
+148.02 0.779 m
+148.02 1.22 147.95 1.543 147.814 1.749 c
+147.674 1.962 147.453 2.072 147.152 2.072 c
+146.836 2.072 146.597 1.918 146.433 1.616 c
+146.433 -0.456 l
+146.597 -0.761 146.836 -0.912 147.152 -0.912 c
+147.447 -0.912 147.659 -0.809 147.8 -0.603 c
+147.935 -0.389 148.008 -0.059 148.02 0.382 c
+h
+150.151 2.175 m
+150.404 2.499 150.724 2.66 151.106 2.66 c
+151.812 2.66 152.168 2.19 152.18 1.249 c
+152.18 -1.397 l
+151.532 -1.397 l
+151.532 1.22 l
+151.532 1.532 151.478 1.753 151.372 1.882 c
+151.261 2.006 151.106 2.072 150.901 2.072 c
+150.743 2.072 150.595 2.017 150.46 1.911 c
+150.331 1.801 150.229 1.664 150.151 1.499 c
+150.151 -1.397 l
+149.504 -1.397 l
+149.504 4.247 l
+150.151 4.247 l
+h
+153.179 -1.044 m
+153.179 -0.927 153.213 -0.831 153.282 -0.75 c
+153.348 -0.673 153.451 -0.632 153.591 -0.632 c
+153.738 -0.632 153.844 -0.673 153.914 -0.75 c
+153.992 -0.831 154.032 -0.927 154.032 -1.044 c
+154.032 -1.154 153.992 -1.246 153.914 -1.324 c
+153.844 -1.401 153.738 -1.441 153.591 -1.441 c
+153.451 -1.441 153.348 -1.401 153.282 -1.324 c
+153.213 -1.246 153.179 -1.154 153.179 -1.044 c
+160.411 0.808 m
+160.411 0.081 160.253 -0.482 159.941 -0.882 c
+159.636 -1.276 159.195 -1.47 158.618 -1.47 c
+158.067 -1.47 157.633 -1.279 157.31 -0.897 c
+156.993 -0.515 156.828 0.029 156.81 0.735 c
+156.81 1.734 l
+156.81 2.447 156.964 3.006 157.28 3.41 c
+157.603 3.821 158.048 4.027 158.618 4.027 c
+159.176 4.027 159.61 3.829 159.926 3.439 c
+160.238 3.057 160.4 2.502 160.411 1.778 c
+h
+159.735 1.749 m
+159.735 2.315 159.64 2.738 159.455 3.013 c
+159.279 3.285 159 3.424 158.618 3.424 c
+158.243 3.424 157.96 3.281 157.765 2.998 c
+157.578 2.723 157.486 2.311 157.486 1.764 c
+157.486 0.808 l
+157.486 0.257 157.578 -0.158 157.765 -0.441 c
+157.96 -0.728 158.243 -0.867 158.618 -0.867 c
+159 -0.867 159.279 -0.736 159.455 -0.47 c
+159.64 -0.206 159.735 0.199 159.735 0.75 c
+h
+161.984 2.587 m
+161.998 2.146 l
+162.252 2.487 162.576 2.66 162.969 2.66 c
+163.675 2.66 164.031 2.19 164.041 1.249 c
+164.041 -1.397 l
+163.395 -1.397 l
+163.395 1.22 l
+163.395 1.532 163.34 1.753 163.233 1.882 c
+163.123 2.006 162.969 2.072 162.763 2.072 c
+162.605 2.072 162.458 2.017 162.322 1.911 c
+162.193 1.801 162.09 1.664 162.013 1.499 c
+162.013 -1.397 l
+161.366 -1.397 l
+161.366 2.587 l
+h
+166.437 -1.47 m
+165.938 -1.47 165.556 -1.324 165.291 -1.029 c
+165.026 -0.736 164.894 -0.302 164.894 0.279 c
+164.894 0.75 l
+164.894 1.344 165.019 1.811 165.277 2.146 c
+165.541 2.487 165.901 2.66 166.364 2.66 c
+166.823 2.66 167.166 2.506 167.393 2.204 c
+167.628 1.911 167.75 1.448 167.76 0.823 c
+167.76 0.397 l
+165.541 0.397 l
+165.541 0.309 l
+165.541 -0.125 165.618 -0.437 165.776 -0.632 c
+165.942 -0.819 166.173 -0.912 166.467 -0.912 c
+166.661 -0.912 166.834 -0.879 166.981 -0.809 c
+167.129 -0.732 167.264 -0.614 167.393 -0.456 c
+167.731 -0.867 l
+167.444 -1.272 167.014 -1.47 166.437 -1.47 c
+166.364 2.102 m
+166.088 2.102 165.886 2.006 165.761 1.822 c
+165.633 1.635 165.56 1.344 165.541 0.955 c
+167.114 0.955 l
+167.114 1.043 l
+167.091 1.425 167.025 1.693 166.908 1.851 c
+166.79 2.017 166.607 2.102 166.364 2.102 c
+171.641 -0.912 m
+171.854 -0.912 172.027 -0.849 172.155 -0.721 c
+172.292 -0.584 172.365 -0.393 172.376 -0.148 c
+172.993 -0.148 l
+172.971 -0.53 172.835 -0.849 172.581 -1.103 c
+172.325 -1.349 172.012 -1.47 171.641 -1.47 c
+171.149 -1.47 170.773 -1.32 170.509 -1.014 c
+170.252 -0.702 170.127 -0.235 170.127 0.382 c
+170.127 0.823 l
+170.127 1.418 170.252 1.874 170.509 2.19 c
+170.773 2.502 171.149 2.66 171.641 2.66 c
+172.041 2.66 172.361 2.528 172.597 2.263 c
+172.839 2.006 172.971 1.66 172.993 1.22 c
+172.376 1.22 l
+172.354 1.514 172.28 1.734 172.155 1.882 c
+172.038 2.028 171.866 2.102 171.641 2.102 c
+171.347 2.102 171.13 2.003 170.994 1.808 c
+170.854 1.62 170.781 1.311 170.773 0.882 c
+170.773 0.367 l
+170.773 -0.103 170.84 -0.437 170.979 -0.632 c
+171.127 -0.819 171.347 -0.912 171.641 -0.912 c
+173.611 0.779 m
+173.611 1.356 173.747 1.811 174.023 2.146 c
+174.305 2.487 174.676 2.66 175.139 2.66 c
+175.598 2.66 175.966 2.491 176.242 2.161 c
+176.525 1.837 176.672 1.389 176.683 0.823 c
+176.683 0.397 l
+176.683 -0.173 176.539 -0.628 176.257 -0.971 c
+175.981 -1.305 175.613 -1.47 175.154 -1.47 c
+174.692 -1.47 174.32 -1.309 174.037 -0.985 c
+173.761 -0.655 173.618 -0.214 173.611 0.338 c
+h
+174.258 0.397 m
+174.258 -0.008 174.335 -0.324 174.493 -0.559 c
+174.658 -0.794 174.879 -0.912 175.154 -0.912 c
+175.72 -0.912 176.014 -0.5 176.036 0.323 c
+176.036 0.779 l
+176.036 1.18 175.951 1.499 175.787 1.734 c
+175.629 1.976 175.411 2.102 175.139 2.102 c
+174.875 2.102 174.658 1.976 174.493 1.734 c
+174.335 1.499 174.258 1.18 174.258 0.779 c
+h
+178.138 2.587 m
+178.153 2.219 l
+178.395 2.514 178.715 2.66 179.108 2.66 c
+179.55 2.66 179.858 2.462 180.034 2.072 c
+180.288 2.462 180.637 2.66 181.078 2.66 c
+181.813 2.66 182.187 2.198 182.21 1.278 c
+182.21 -1.397 l
+181.563 -1.397 l
+181.563 1.22 l
+181.563 1.514 181.508 1.727 181.401 1.866 c
+181.302 2.003 181.129 2.072 180.886 2.072 c
+180.689 2.072 180.527 1.992 180.402 1.837 c
+180.284 1.691 180.215 1.499 180.196 1.263 c
+180.196 -1.397 l
+179.534 -1.397 l
+179.534 1.249 l
+179.534 1.797 179.314 2.072 178.873 2.072 c
+178.538 2.072 178.303 1.911 178.168 1.587 c
+178.168 -1.397 l
+177.52 -1.397 l
+177.52 2.587 l
+h
+183.797 2.587 m
+183.812 2.219 l
+184.055 2.514 184.375 2.66 184.768 2.66 c
+185.208 2.66 185.517 2.462 185.693 2.072 c
+185.947 2.462 186.296 2.66 186.737 2.66 c
+187.471 2.66 187.847 2.198 187.868 1.278 c
+187.868 -1.397 l
+187.222 -1.397 l
+187.222 1.22 l
+187.222 1.514 187.167 1.727 187.06 1.866 c
+186.962 2.003 186.789 2.072 186.546 2.072 c
+186.347 2.072 186.186 1.992 186.06 1.837 c
+185.944 1.691 185.873 1.499 185.855 1.263 c
+185.855 -1.397 l
+185.194 -1.397 l
+185.194 1.249 l
+185.194 1.797 184.973 2.072 184.533 2.072 c
+184.198 2.072 183.963 1.911 183.827 1.587 c
+183.827 -1.397 l
+183.18 -1.397 l
+183.18 2.587 l
+h
+189.545 -1.397 -0.647 3.984 re
+189.588 3.63 m
+189.588 3.52 189.559 3.428 189.501 3.351 c
+189.441 3.281 189.346 3.248 189.221 3.248 c
+189.104 3.248 189.008 3.281 188.942 3.351 c
+188.883 3.428 188.853 3.52 188.853 3.63 c
+188.853 3.748 188.883 3.84 188.942 3.91 c
+189.008 3.987 189.104 4.027 189.221 4.027 c
+189.346 4.027 189.441 3.987 189.501 3.91 c
+189.559 3.829 189.588 3.737 189.588 3.63 c
+191.411 3.542 m
+191.411 2.587 l
+192.014 2.587 l
+192.014 2.057 l
+191.411 2.057 l
+191.411 -0.412 l
+191.411 -0.57 191.433 -0.688 191.485 -0.765 c
+191.544 -0.846 191.631 -0.882 191.749 -0.882 c
+191.837 -0.882 191.926 -0.867 192.014 -0.838 c
+192.014 -1.397 l
+191.867 -1.444 191.712 -1.47 191.558 -1.47 c
+191.301 -1.47 191.107 -1.378 190.97 -1.191 c
+190.831 -1.008 190.764 -0.746 190.764 -0.412 c
+190.764 2.057 l
+190.162 2.057 l
+190.162 2.587 l
+190.764 2.587 l
+190.764 3.542 l
+h
+197.379 0.397 m
+197.379 -0.231 197.262 -0.702 197.027 -1.014 c
+196.799 -1.32 196.483 -1.47 196.071 -1.47 c
+195.666 -1.47 195.358 -1.32 195.145 -1.014 c
+195.145 -2.926 l
+194.498 -2.926 l
+194.498 2.587 l
+195.086 2.587 l
+195.13 2.146 l
+195.344 2.487 195.652 2.66 196.057 2.66 c
+196.497 2.66 196.824 2.506 197.041 2.204 c
+197.254 1.899 197.368 1.444 197.379 0.837 c
+h
+196.732 0.779 m
+196.732 1.22 196.663 1.543 196.527 1.749 c
+196.387 1.962 196.167 2.072 195.865 2.072 c
+195.549 2.072 195.311 1.918 195.145 1.616 c
+195.145 -0.456 l
+195.311 -0.761 195.549 -0.912 195.865 -0.912 c
+196.159 -0.912 196.372 -0.809 196.512 -0.603 c
+196.647 -0.389 196.722 -0.059 196.732 0.382 c
+h
+199.643 -1.47 m
+199.143 -1.47 198.76 -1.324 198.496 -1.029 c
+198.232 -0.736 198.099 -0.302 198.099 0.279 c
+198.099 0.75 l
+198.099 1.344 198.224 1.811 198.482 2.146 c
+198.746 2.487 199.107 2.66 199.569 2.66 c
+200.028 2.66 200.37 2.506 200.599 2.204 c
+200.834 1.911 200.955 1.448 200.965 0.823 c
+200.965 0.397 l
+198.746 0.397 l
+198.746 0.309 l
+198.746 -0.125 198.823 -0.437 198.981 -0.632 c
+199.147 -0.819 199.378 -0.912 199.672 -0.912 c
+199.867 -0.912 200.04 -0.879 200.187 -0.809 c
+200.333 -0.732 200.47 -0.614 200.599 -0.456 c
+200.936 -0.867 l
+200.649 -1.272 200.219 -1.47 199.643 -1.47 c
+199.569 2.102 m
+199.294 2.102 199.092 2.006 198.966 1.822 c
+198.838 1.635 198.764 1.344 198.746 0.955 c
+200.319 0.955 l
+200.319 1.043 l
+200.297 1.425 200.231 1.693 200.113 1.851 c
+199.996 2.017 199.812 2.102 199.569 2.102 c
+203.391 1.969 m
+203.303 1.988 203.203 1.999 203.097 1.999 c
+202.762 1.999 202.527 1.815 202.392 1.455 c
+202.392 -1.397 l
+201.744 -1.397 l
+201.744 2.587 l
+202.377 2.587 l
+202.392 2.175 l
+202.568 2.499 202.81 2.66 203.126 2.66 c
+203.233 2.66 203.321 2.638 203.391 2.601 c
+h
+206.463 -1.397 -0.647 5.644 re
+208.183 -1.397 -0.646 3.984 re
+208.227 3.63 m
+208.227 3.52 208.198 3.428 208.139 3.351 c
+208.08 3.281 207.984 3.248 207.859 3.248 c
+207.742 3.248 207.647 3.281 207.581 3.351 c
+207.521 3.428 207.492 3.52 207.492 3.63 c
+207.492 3.748 207.521 3.84 207.581 3.91 c
+207.647 3.987 207.742 4.027 207.859 4.027 c
+207.984 4.027 208.08 3.987 208.139 3.91 c
+208.198 3.829 208.227 3.737 208.227 3.63 c
+209.814 2.587 m
+209.829 2.146 l
+210.083 2.487 210.406 2.66 210.799 2.66 c
+211.505 2.66 211.861 2.19 211.872 1.249 c
+211.872 -1.397 l
+211.225 -1.397 l
+211.225 1.22 l
+211.225 1.532 211.171 1.753 211.064 1.882 c
+210.953 2.006 210.799 2.072 210.594 2.072 c
+210.436 2.072 210.288 2.017 210.153 1.911 c
+210.024 1.801 209.921 1.664 209.844 1.499 c
+209.844 -1.397 l
+209.197 -1.397 l
+209.197 2.587 l
+h
+214.268 -1.47 m
+213.769 -1.47 213.386 -1.324 213.122 -1.029 c
+212.857 -0.736 212.725 -0.302 212.725 0.279 c
+212.725 0.75 l
+212.725 1.344 212.85 1.811 213.107 2.146 c
+213.372 2.487 213.732 2.66 214.195 2.66 c
+214.654 2.66 214.996 2.506 215.223 2.204 c
+215.459 1.911 215.58 1.448 215.591 0.823 c
+215.591 0.397 l
+213.372 0.397 l
+213.372 0.309 l
+213.372 -0.125 213.449 -0.437 213.607 -0.632 c
+213.773 -0.819 214.004 -0.912 214.298 -0.912 c
+214.492 -0.912 214.665 -0.879 214.812 -0.809 c
+214.959 -0.732 215.095 -0.614 215.223 -0.456 c
+215.562 -0.867 l
+215.275 -1.272 214.845 -1.47 214.268 -1.47 c
+214.195 2.102 m
+213.919 2.102 213.717 2.006 213.592 1.822 c
+213.463 1.635 213.39 1.344 213.372 0.955 c
+214.945 0.955 l
+214.945 1.043 l
+214.922 1.425 214.856 1.693 214.739 1.851 c
+214.621 2.017 214.438 2.102 214.195 2.102 c
+216.414 -1.044 m
+216.414 -0.927 216.447 -0.831 216.518 -0.75 c
+216.584 -0.673 216.686 -0.632 216.826 -0.632 c
+216.973 -0.632 217.079 -0.673 217.149 -0.75 c
+217.226 -0.831 217.267 -0.927 217.267 -1.044 c
+217.267 -1.154 217.226 -1.246 217.149 -1.324 c
+217.079 -1.401 216.973 -1.441 216.826 -1.441 c
+216.686 -1.441 216.584 -1.401 216.518 -1.324 c
+216.447 -1.246 216.414 -1.154 216.414 -1.044 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 411.16 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 404.3204 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.993 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.596 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.279 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+23.221 -0.646 m
+24.353 -0.646 l
+24.353 -1.264 l
+21.045 -1.264 l
+21.045 -0.646 l
+22.31 -0.646 l
+22.31 2.896 l
+21.384 2.896 l
+21.384 3.514 l
+23.221 3.514 l
+h
+28.384 0.485 m
+28.384 0.21 28.347 -0.04 28.281 -0.264 c
+28.212 -0.482 28.108 -0.669 27.973 -0.823 c
+27.833 -0.981 27.657 -1.103 27.443 -1.19 c
+27.227 -1.278 26.973 -1.323 26.679 -1.323 c
+26.404 -1.323 26.157 -1.278 25.944 -1.19 c
+25.739 -1.103 25.566 -0.981 25.43 -0.823 c
+25.29 -0.669 25.187 -0.482 25.121 -0.264 c
+25.051 -0.04 25.018 0.21 25.018 0.485 c
+25.018 0.738 25.047 0.974 25.106 1.191 c
+25.172 1.415 25.275 1.606 25.415 1.764 c
+25.55 1.929 25.727 2.058 25.944 2.146 c
+26.157 2.234 26.414 2.278 26.709 2.278 c
+27.021 2.278 27.281 2.234 27.487 2.146 c
+27.701 2.058 27.873 1.929 28.002 1.764 c
+28.138 1.606 28.237 1.415 28.296 1.191 c
+28.355 0.974 28.384 0.738 28.384 0.485 c
+27.429 0.485 m
+27.429 0.691 27.414 0.867 27.385 1.014 c
+27.362 1.162 27.326 1.282 27.267 1.382 c
+27.208 1.477 27.135 1.547 27.046 1.588 c
+26.959 1.635 26.848 1.661 26.724 1.661 c
+26.458 1.661 26.267 1.562 26.15 1.367 c
+26.032 1.18 25.974 0.886 25.974 0.485 c
+25.974 0.063 26.032 -0.243 26.15 -0.426 c
+26.267 -0.613 26.444 -0.706 26.679 -0.706 c
+26.804 -0.706 26.918 -0.687 27.017 -0.646 c
+27.113 -0.599 27.194 -0.525 27.252 -0.426 c
+27.318 -0.33 27.362 -0.206 27.385 -0.058 c
+27.414 0.088 27.429 0.268 27.429 0.485 c
+30.769 -2.66 m
+30.552 -2.66 30.361 -2.635 30.196 -2.587 c
+30.027 -2.547 29.887 -2.484 29.77 -2.396 c
+29.652 -2.315 29.552 -2.219 29.475 -2.102 c
+29.406 -1.984 29.358 -1.855 29.328 -1.72 c
+30.225 -1.617 l
+30.262 -1.753 30.331 -1.859 30.431 -1.94 c
+30.526 -2.028 30.651 -2.072 30.799 -2.072 c
+30.886 -2.072 30.967 -2.057 31.048 -2.028 c
+31.125 -1.999 31.195 -1.944 31.254 -1.866 c
+31.313 -1.797 31.357 -1.705 31.387 -1.587 c
+31.424 -1.469 31.445 -1.323 31.445 -1.147 c
+31.445 -0.955 l
+31.445 -0.889 31.449 -0.831 31.46 -0.779 c
+31.46 -0.588 l
+31.445 -0.588 l
+31.346 -0.816 31.202 -0.977 31.019 -1.072 c
+30.832 -1.172 30.626 -1.22 30.402 -1.22 c
+30.196 -1.22 30.012 -1.183 29.858 -1.103 c
+29.71 -1.014 29.582 -0.897 29.475 -0.75 c
+29.376 -0.596 29.303 -0.411 29.255 -0.206 c
+29.203 0.008 29.182 0.243 29.182 0.5 c
+29.182 0.771 29.203 1.018 29.255 1.235 c
+29.313 1.448 29.394 1.632 29.505 1.779 c
+29.612 1.933 29.743 2.051 29.901 2.132 c
+30.056 2.22 30.244 2.263 30.46 2.263 c
+30.556 2.263 30.655 2.253 30.755 2.234 c
+30.85 2.213 30.938 2.18 31.019 2.132 c
+31.107 2.08 31.185 2.018 31.254 1.941 c
+31.331 1.86 31.393 1.768 31.445 1.661 c
+31.46 1.661 l
+31.46 1.808 l
+31.467 1.867 31.474 1.918 31.474 1.97 c
+31.482 2.028 31.489 2.076 31.489 2.117 c
+31.497 2.165 31.507 2.198 31.519 2.22 c
+32.371 2.22 l
+32.36 2.139 32.349 2.028 32.342 1.881 c
+32.342 1.411 l
+32.342 -1.161 l
+32.342 -1.415 32.305 -1.635 32.239 -1.822 c
+32.17 -2.007 32.066 -2.161 31.931 -2.279 c
+31.79 -2.404 31.625 -2.499 31.43 -2.558 c
+31.232 -2.624 31.011 -2.66 30.769 -2.66 c
+31.46 0.53 m
+31.46 0.742 31.434 0.919 31.387 1.058 c
+31.346 1.205 31.291 1.323 31.225 1.411 c
+31.166 1.5 31.096 1.558 31.019 1.588 c
+30.938 1.625 30.861 1.646 30.784 1.646 c
+30.684 1.646 30.593 1.621 30.504 1.573 c
+30.424 1.532 30.358 1.463 30.298 1.367 c
+30.248 1.279 30.203 1.162 30.167 1.014 c
+30.137 0.875 30.122 0.706 30.122 0.5 c
+30.122 0.125 30.181 -0.154 30.298 -0.338 c
+30.416 -0.515 30.578 -0.602 30.784 -0.602 c
+30.85 -0.602 30.923 -0.588 31.004 -0.559 c
+31.092 -0.522 31.166 -0.463 31.225 -0.382 c
+31.291 -0.294 31.346 -0.176 31.387 -0.029 c
+31.434 0.118 31.46 0.301 31.46 0.53 c
+40.378 1.47 m
+40.28 1.477 40.176 1.488 40.07 1.5 c
+39.96 1.517 39.838 1.529 39.702 1.529 c
+39.526 1.529 39.368 1.488 39.232 1.411 c
+39.092 1.341 38.975 1.242 38.879 1.118 c
+38.791 0.989 38.721 0.842 38.674 0.676 c
+38.633 0.507 38.615 0.331 38.615 0.147 c
+38.615 -1.264 l
+37.718 -1.264 l
+37.718 0.985 l
+37.718 1.11 37.707 1.235 37.689 1.353 c
+37.678 1.477 37.663 1.595 37.645 1.706 c
+37.633 1.823 37.619 1.918 37.6 1.999 c
+37.579 2.087 37.56 2.161 37.542 2.22 c
+38.424 2.22 l
+38.431 2.168 38.442 2.117 38.453 2.058 c
+38.472 1.999 38.486 1.933 38.497 1.867 c
+38.516 1.808 38.53 1.742 38.541 1.675 c
+38.549 1.606 38.56 1.544 38.57 1.484 c
+38.585 1.484 l
+38.622 1.602 38.674 1.709 38.732 1.808 c
+38.798 1.904 38.879 1.988 38.967 2.058 c
+39.056 2.124 39.158 2.18 39.276 2.22 c
+39.401 2.257 39.548 2.278 39.717 2.278 c
+39.842 2.278 39.96 2.271 40.07 2.263 c
+40.187 2.253 40.29 2.238 40.378 2.22 c
+h
+42.984 -1.323 m
+42.727 -1.323 42.499 -1.286 42.293 -1.22 c
+42.088 -1.143 41.911 -1.029 41.764 -0.882 c
+41.617 -0.727 41.5 -0.536 41.411 -0.309 c
+41.33 -0.085 41.294 0.181 41.294 0.485 c
+41.294 0.816 41.338 1.095 41.425 1.323 c
+41.521 1.558 41.65 1.742 41.808 1.881 c
+41.973 2.018 42.161 2.117 42.366 2.176 c
+42.572 2.242 42.782 2.278 42.998 2.278 c
+43.27 2.278 43.505 2.227 43.704 2.132 c
+43.91 2.043 44.076 1.911 44.204 1.735 c
+44.34 1.565 44.44 1.359 44.498 1.118 c
+44.564 0.882 44.6 0.617 44.6 0.324 c
+44.6 0.309 l
+42.234 0.309 l
+42.234 0.162 42.249 0.023 42.279 -0.103 c
+42.315 -0.231 42.37 -0.345 42.441 -0.441 c
+42.506 -0.529 42.591 -0.599 42.69 -0.646 c
+42.786 -0.698 42.9 -0.72 43.028 -0.72 c
+43.183 -0.72 43.322 -0.687 43.44 -0.617 c
+43.565 -0.551 43.653 -0.448 43.704 -0.309 c
+44.542 -0.382 l
+44.513 -0.482 44.457 -0.588 44.38 -0.706 c
+44.299 -0.816 44.197 -0.918 44.072 -1.014 c
+43.954 -1.103 43.8 -1.176 43.616 -1.234 c
+43.44 -1.294 43.226 -1.323 42.984 -1.323 c
+42.984 1.706 m
+42.896 1.706 42.807 1.69 42.719 1.661 c
+42.631 1.632 42.551 1.58 42.484 1.515 c
+42.414 1.444 42.356 1.357 42.308 1.249 c
+42.267 1.139 42.249 1.014 42.249 0.867 c
+43.719 0.867 l
+43.719 1.004 43.694 1.124 43.646 1.235 c
+43.605 1.341 43.55 1.43 43.484 1.5 c
+43.425 1.565 43.351 1.617 43.264 1.646 c
+43.175 1.683 43.079 1.706 42.984 1.706 c
+47.133 1.602 m
+47.133 -1.264 l
+46.236 -1.264 l
+46.236 1.602 l
+45.413 1.602 l
+45.413 2.22 l
+46.236 2.22 l
+46.236 2.484 l
+46.236 2.61 46.251 2.741 46.281 2.881 c
+46.317 3.017 46.387 3.134 46.486 3.234 c
+46.593 3.341 46.736 3.429 46.912 3.499 c
+47.089 3.564 47.313 3.601 47.588 3.601 c
+47.802 3.601 48 3.591 48.176 3.572 c
+48.353 3.55 48.504 3.532 48.632 3.514 c
+48.632 2.926 l
+48.504 2.944 48.361 2.959 48.205 2.969 c
+48.048 2.976 47.897 2.984 47.75 2.984 c
+47.621 2.984 47.519 2.969 47.441 2.94 c
+47.361 2.911 47.298 2.87 47.251 2.822 c
+47.199 2.77 47.166 2.708 47.147 2.631 c
+47.137 2.562 47.133 2.484 47.133 2.396 c
+47.133 2.22 l
+48.558 2.22 l
+48.558 1.602 l
+h
+50.605 -0.279 0.927 -0.985 re
+50.605 -1.264 m
+54.67 -0.279 0.926 -0.985 re
+54.67 -1.264 m
+f
+Q
+q 1 0 0 1 58.4255 388.7399 cm
+0 0 m
+2.102 0 l
+2.102 -0.574 l
+-0.676 -0.574 l
+-0.676 4.777 l
+0 4.777 l
+h
+3.513 -0.574 -0.646 3.984 re
+3.557 4.453 m
+3.557 4.343 3.528 4.251 3.469 4.174 c
+3.41 4.104 3.314 4.072 3.19 4.072 c
+3.072 4.072 2.977 4.104 2.911 4.174 c
+2.851 4.251 2.822 4.343 2.822 4.453 c
+2.822 4.571 2.851 4.663 2.911 4.733 c
+2.977 4.81 3.072 4.85 3.19 4.85 c
+3.314 4.85 3.41 4.81 3.469 4.733 c
+3.528 4.652 3.557 4.56 3.557 4.453 c
+6.468 0.44 m
+6.468 0.588 6.412 0.709 6.306 0.808 c
+6.196 0.904 5.99 1.022 5.689 1.161 c
+5.343 1.308 5.101 1.429 4.954 1.529 c
+4.806 1.635 4.696 1.753 4.63 1.881 c
+4.561 2.006 4.528 2.164 4.528 2.352 c
+4.528 2.674 4.645 2.944 4.881 3.16 c
+5.116 3.373 5.417 3.484 5.791 3.484 c
+6.174 3.484 6.483 3.37 6.718 3.145 c
+6.953 2.917 7.071 2.631 7.071 2.278 c
+6.423 2.278 l
+6.423 2.454 6.365 2.605 6.247 2.734 c
+6.13 2.859 5.976 2.925 5.791 2.925 c
+5.593 2.925 5.442 2.869 5.336 2.763 c
+5.226 2.664 5.174 2.532 5.174 2.366 c
+5.174 2.238 5.211 2.131 5.292 2.043 c
+5.369 1.962 5.56 1.859 5.865 1.734 c
+6.342 1.547 6.674 1.359 6.85 1.176 c
+7.026 0.999 7.115 0.771 7.115 0.5 c
+7.115 0.147 6.99 -0.133 6.747 -0.339 c
+6.512 -0.544 6.196 -0.647 5.806 -0.647 c
+5.384 -0.647 5.045 -0.53 4.792 -0.294 c
+4.534 -0.052 4.41 0.253 4.41 0.617 c
+5.056 0.617 l
+5.064 0.389 5.134 0.213 5.262 0.087 c
+5.388 -0.03 5.571 -0.088 5.806 -0.088 c
+6.02 -0.088 6.181 -0.04 6.292 0.058 c
+6.409 0.154 6.468 0.282 6.468 0.44 c
+8.804 4.365 m
+8.804 3.41 l
+9.407 3.41 l
+9.407 2.881 l
+8.804 2.881 l
+8.804 0.411 l
+8.804 0.253 8.827 0.135 8.879 0.058 c
+8.937 -0.023 9.025 -0.059 9.143 -0.059 c
+9.231 -0.059 9.319 -0.044 9.407 -0.015 c
+9.407 -0.574 l
+9.261 -0.621 9.106 -0.647 8.952 -0.647 c
+8.694 -0.647 8.5 -0.555 8.364 -0.368 c
+8.224 -0.184 8.158 0.077 8.158 0.411 c
+8.158 2.881 l
+7.555 2.881 l
+7.555 3.41 l
+8.158 3.41 l
+8.158 4.365 l
+h
+13.273 -0.088 m
+13.486 -0.088 13.66 -0.026 13.787 0.103 c
+13.924 0.239 13.997 0.43 14.009 0.675 c
+14.626 0.675 l
+14.603 0.294 14.468 -0.026 14.214 -0.279 c
+13.957 -0.526 13.644 -0.647 13.273 -0.647 c
+12.781 -0.647 12.406 -0.497 12.141 -0.191 c
+11.884 0.121 11.759 0.588 11.759 1.205 c
+11.759 1.646 l
+11.759 2.241 11.884 2.697 12.141 3.013 c
+12.406 3.326 12.781 3.484 13.273 3.484 c
+13.674 3.484 13.993 3.351 14.229 3.087 c
+14.471 2.829 14.603 2.484 14.626 2.043 c
+14.009 2.043 l
+13.986 2.337 13.913 2.557 13.787 2.705 c
+13.67 2.851 13.498 2.925 13.273 2.925 c
+12.979 2.925 12.762 2.826 12.627 2.631 c
+12.486 2.443 12.413 2.135 12.406 1.705 c
+12.406 1.19 l
+12.406 0.72 12.472 0.386 12.612 0.191 c
+12.759 0.004 12.979 -0.088 13.273 -0.088 c
+15.243 1.602 m
+15.243 2.179 15.379 2.634 15.655 2.969 c
+15.937 3.31 16.309 3.484 16.771 3.484 c
+17.231 3.484 17.598 3.314 17.874 2.984 c
+18.157 2.66 18.304 2.212 18.315 1.646 c
+18.315 1.22 l
+18.315 0.65 18.171 0.195 17.889 -0.148 c
+17.613 -0.482 17.246 -0.647 16.787 -0.647 c
+16.324 -0.647 15.952 -0.485 15.669 -0.162 c
+15.393 0.168 15.25 0.61 15.243 1.161 c
+h
+15.89 1.22 m
+15.89 0.816 15.967 0.5 16.125 0.264 c
+16.29 0.029 16.511 -0.088 16.787 -0.088 c
+17.352 -0.088 17.646 0.323 17.668 1.146 c
+17.668 1.602 l
+17.668 2.003 17.583 2.322 17.419 2.557 c
+17.261 2.8 17.043 2.925 16.771 2.925 c
+16.507 2.925 16.29 2.8 16.125 2.557 c
+15.967 2.322 15.89 2.003 15.89 1.602 c
+h
+19.771 3.41 m
+19.785 3.042 l
+20.027 3.337 20.347 3.484 20.74 3.484 c
+21.182 3.484 21.49 3.285 21.666 2.896 c
+21.92 3.285 22.269 3.484 22.71 3.484 c
+23.445 3.484 23.82 3.021 23.842 2.102 c
+23.842 -0.574 l
+23.195 -0.574 l
+23.195 2.043 l
+23.195 2.337 23.14 2.55 23.033 2.69 c
+22.934 2.826 22.761 2.896 22.519 2.896 c
+22.321 2.896 22.159 2.815 22.034 2.66 c
+21.916 2.514 21.847 2.322 21.828 2.087 c
+21.828 -0.574 l
+21.167 -0.574 l
+21.167 2.072 l
+21.167 2.62 20.947 2.896 20.505 2.896 c
+20.17 2.896 19.935 2.734 19.8 2.41 c
+19.8 -0.574 l
+19.153 -0.574 l
+19.153 3.41 l
+h
+25.429 3.41 m
+25.444 3.042 l
+25.687 3.337 26.007 3.484 26.4 3.484 c
+26.84 3.484 27.149 3.285 27.326 2.896 c
+27.579 3.285 27.929 3.484 28.369 3.484 c
+29.105 3.484 29.479 3.021 29.501 2.102 c
+29.501 -0.574 l
+28.854 -0.574 l
+28.854 2.043 l
+28.854 2.337 28.799 2.55 28.693 2.69 c
+28.594 2.826 28.421 2.896 28.178 2.896 c
+27.979 2.896 27.818 2.815 27.693 2.66 c
+27.576 2.514 27.505 2.322 27.487 2.087 c
+27.487 -0.574 l
+26.826 -0.574 l
+26.826 2.072 l
+26.826 2.62 26.605 2.896 26.165 2.896 c
+25.83 2.896 25.595 2.734 25.459 2.41 c
+25.459 -0.574 l
+24.812 -0.574 l
+24.812 3.41 l
+h
+31.177 -0.574 -0.647 3.984 re
+31.221 4.453 m
+31.221 4.343 31.191 4.251 31.133 4.174 c
+31.074 4.104 30.979 4.072 30.853 4.072 c
+30.736 4.072 30.64 4.104 30.574 4.174 c
+30.516 4.251 30.486 4.343 30.486 4.453 c
+30.486 4.571 30.516 4.663 30.574 4.733 c
+30.64 4.81 30.736 4.85 30.853 4.85 c
+30.979 4.85 31.074 4.81 31.133 4.733 c
+31.191 4.652 31.221 4.56 31.221 4.453 c
+33.043 4.365 m
+33.043 3.41 l
+33.646 3.41 l
+33.646 2.881 l
+33.043 2.881 l
+33.043 0.411 l
+33.043 0.253 33.066 0.135 33.117 0.058 c
+33.176 -0.023 33.264 -0.059 33.381 -0.059 c
+33.47 -0.059 33.558 -0.044 33.646 -0.015 c
+33.646 -0.574 l
+33.499 -0.621 33.345 -0.647 33.19 -0.647 c
+32.933 -0.647 32.739 -0.555 32.602 -0.368 c
+32.463 -0.184 32.397 0.077 32.397 0.411 c
+32.397 2.881 l
+31.794 2.881 l
+31.794 3.41 l
+32.397 3.41 l
+32.397 4.365 l
+h
+36.351 0.44 m
+36.351 0.588 36.296 0.709 36.189 0.808 c
+36.079 0.904 35.873 1.022 35.572 1.161 c
+35.226 1.308 34.984 1.429 34.837 1.529 c
+34.69 1.635 34.58 1.753 34.514 1.881 c
+34.443 2.006 34.41 2.164 34.41 2.352 c
+34.41 2.674 34.528 2.944 34.763 3.16 c
+34.998 3.373 35.299 3.484 35.675 3.484 c
+36.057 3.484 36.365 3.37 36.6 3.145 c
+36.836 2.917 36.953 2.631 36.953 2.278 c
+36.307 2.278 l
+36.307 2.454 36.248 2.605 36.13 2.734 c
+36.012 2.859 35.858 2.925 35.675 2.925 c
+35.476 2.925 35.326 2.869 35.219 2.763 c
+35.108 2.664 35.058 2.532 35.058 2.366 c
+35.058 2.238 35.094 2.131 35.175 2.043 c
+35.252 1.962 35.443 1.859 35.748 1.734 c
+36.226 1.547 36.556 1.359 36.733 1.176 c
+36.909 0.999 36.997 0.771 36.997 0.5 c
+36.997 0.147 36.872 -0.133 36.631 -0.339 c
+36.395 -0.544 36.079 -0.647 35.69 -0.647 c
+35.266 -0.647 34.929 -0.53 34.675 -0.294 c
+34.418 -0.052 34.293 0.253 34.293 0.617 c
+34.94 0.617 l
+34.948 0.389 35.017 0.213 35.145 0.087 c
+35.27 -0.03 35.455 -0.088 35.69 -0.088 c
+35.902 -0.088 36.064 -0.04 36.174 0.058 c
+36.292 0.154 36.351 0.282 36.351 0.44 c
+40.408 4.365 m
+40.408 3.41 l
+41.011 3.41 l
+41.011 2.881 l
+40.408 2.881 l
+40.408 0.411 l
+40.408 0.253 40.43 0.135 40.481 0.058 c
+40.54 -0.023 40.629 -0.059 40.746 -0.059 c
+40.834 -0.059 40.922 -0.044 41.011 -0.015 c
+41.011 -0.574 l
+40.864 -0.621 40.709 -0.647 40.554 -0.647 c
+40.298 -0.647 40.103 -0.555 39.966 -0.368 c
+39.827 -0.184 39.761 0.077 39.761 0.411 c
+39.761 2.881 l
+39.158 2.881 l
+39.158 3.41 l
+39.761 3.41 l
+39.761 4.365 l
+h
+42.422 2.998 m
+42.675 3.322 42.995 3.484 43.377 3.484 c
+44.082 3.484 44.439 3.013 44.45 2.072 c
+44.45 -0.574 l
+43.804 -0.574 l
+43.804 2.043 l
+43.804 2.356 43.748 2.576 43.642 2.705 c
+43.531 2.829 43.377 2.896 43.171 2.896 c
+43.013 2.896 42.866 2.84 42.73 2.734 c
+42.601 2.624 42.499 2.487 42.422 2.322 c
+42.422 -0.574 l
+41.774 -0.574 l
+41.774 5.071 l
+42.422 5.071 l
+h
+47.449 -0.574 m
+47.409 -0.485 47.382 -0.339 47.375 -0.133 c
+47.14 -0.478 46.846 -0.647 46.493 -0.647 c
+46.129 -0.647 45.846 -0.551 45.641 -0.353 c
+45.442 -0.148 45.346 0.139 45.346 0.515 c
+45.346 0.914 45.483 1.234 45.759 1.469 c
+46.03 1.712 46.405 1.837 46.875 1.837 c
+47.361 1.837 l
+47.361 2.263 l
+47.361 2.499 47.305 2.664 47.199 2.763 c
+47.089 2.869 46.927 2.925 46.713 2.925 c
+46.515 2.925 46.353 2.866 46.229 2.749 c
+46.111 2.631 46.052 2.484 46.052 2.308 c
+45.406 2.308 l
+45.406 2.502 45.464 2.693 45.582 2.881 c
+45.707 3.064 45.869 3.212 46.067 3.322 c
+46.273 3.428 46.501 3.484 46.758 3.484 c
+47.158 3.484 47.463 3.38 47.669 3.175 c
+47.883 2.969 47.997 2.674 48.007 2.293 c
+48.007 0.279 l
+48.007 -0.026 48.044 -0.291 48.124 -0.515 c
+48.124 -0.574 l
+h
+46.582 -0.059 m
+46.746 -0.059 46.898 -0.015 47.037 0.073 c
+47.184 0.162 47.291 0.272 47.361 0.411 c
+47.361 1.352 l
+46.993 1.352 l
+46.677 1.352 46.434 1.282 46.258 1.146 c
+46.081 1.018 45.994 0.83 45.994 0.588 c
+45.994 0.359 46.038 0.195 46.125 0.087 c
+46.214 -0.011 46.364 -0.059 46.582 -0.059 c
+49.859 4.365 m
+49.859 3.41 l
+50.462 3.41 l
+50.462 2.881 l
+49.859 2.881 l
+49.859 0.411 l
+49.859 0.253 49.881 0.135 49.933 0.058 c
+49.992 -0.023 50.079 -0.059 50.197 -0.059 c
+50.285 -0.059 50.374 -0.044 50.462 -0.015 c
+50.462 -0.574 l
+50.315 -0.621 50.16 -0.647 50.006 -0.647 c
+49.749 -0.647 49.554 -0.555 49.418 -0.368 c
+49.279 -0.184 49.213 0.077 49.213 0.411 c
+49.213 2.881 l
+48.61 2.881 l
+48.61 3.41 l
+49.213 3.41 l
+49.213 4.365 l
+h
+54.989 -0.574 m
+54.949 -0.485 54.923 -0.339 54.916 -0.133 c
+54.68 -0.478 54.387 -0.647 54.034 -0.647 c
+53.67 -0.647 53.387 -0.551 53.181 -0.353 c
+52.982 -0.148 52.887 0.139 52.887 0.515 c
+52.887 0.914 53.023 1.234 53.299 1.469 c
+53.57 1.712 53.946 1.837 54.416 1.837 c
+54.901 1.837 l
+54.901 2.263 l
+54.901 2.499 54.846 2.664 54.74 2.763 c
+54.629 2.869 54.468 2.925 54.254 2.925 c
+54.056 2.925 53.894 2.866 53.769 2.749 c
+53.651 2.631 53.593 2.484 53.593 2.308 c
+52.946 2.308 l
+52.946 2.502 53.005 2.693 53.123 2.881 c
+53.248 3.064 53.409 3.212 53.607 3.322 c
+53.813 3.428 54.041 3.484 54.299 3.484 c
+54.699 3.484 55.004 3.38 55.21 3.175 c
+55.423 2.969 55.537 2.674 55.548 2.293 c
+55.548 0.279 l
+55.548 -0.026 55.585 -0.291 55.665 -0.515 c
+55.665 -0.574 l
+h
+54.122 -0.059 m
+54.287 -0.059 54.438 -0.015 54.578 0.073 c
+54.725 0.162 54.831 0.272 54.901 0.411 c
+54.901 1.352 l
+54.534 1.352 l
+54.218 1.352 53.975 1.282 53.799 1.146 c
+53.622 1.018 53.534 0.83 53.534 0.588 c
+53.534 0.359 53.578 0.195 53.666 0.087 c
+53.755 -0.011 53.905 -0.059 54.122 -0.059 c
+58.193 2.792 m
+58.106 2.811 58.006 2.822 57.9 2.822 c
+57.565 2.822 57.33 2.638 57.194 2.278 c
+57.194 -0.574 l
+56.547 -0.574 l
+56.547 3.41 l
+57.179 3.41 l
+57.194 2.998 l
+57.37 3.322 57.613 3.484 57.929 3.484 c
+58.035 3.484 58.124 3.461 58.193 3.424 c
+h
+60.192 -0.647 m
+59.693 -0.647 59.311 -0.5 59.046 -0.206 c
+58.781 0.087 58.649 0.521 58.649 1.103 c
+58.649 1.573 l
+58.649 2.167 58.775 2.634 59.031 2.969 c
+59.296 3.31 59.656 3.484 60.119 3.484 c
+60.579 3.484 60.92 3.329 61.148 3.027 c
+61.383 2.734 61.505 2.271 61.516 1.646 c
+61.516 1.22 l
+59.296 1.22 l
+59.296 1.132 l
+59.296 0.698 59.373 0.386 59.531 0.191 c
+59.697 0.004 59.928 -0.088 60.222 -0.088 c
+60.417 -0.088 60.589 -0.056 60.737 0.014 c
+60.884 0.091 61.019 0.209 61.148 0.367 c
+61.486 -0.044 l
+61.2 -0.449 60.77 -0.647 60.192 -0.647 c
+60.119 2.925 m
+59.843 2.925 59.642 2.829 59.517 2.645 c
+59.388 2.458 59.315 2.167 59.296 1.778 c
+60.869 1.778 l
+60.869 1.866 l
+60.847 2.248 60.78 2.516 60.663 2.674 c
+60.545 2.84 60.362 2.925 60.119 2.925 c
+66.895 1.22 m
+66.895 0.592 66.777 0.121 66.542 -0.191 c
+66.315 -0.497 65.999 -0.647 65.587 -0.647 c
+65.183 -0.647 64.874 -0.497 64.661 -0.191 c
+64.661 -2.103 l
+64.014 -2.103 l
+64.014 3.41 l
+64.602 3.41 l
+64.647 2.969 l
+64.859 3.31 65.168 3.484 65.572 3.484 c
+66.014 3.484 66.341 3.329 66.557 3.027 c
+66.771 2.722 66.885 2.267 66.895 1.66 c
+h
+66.249 1.602 m
+66.249 2.043 66.179 2.366 66.043 2.572 c
+65.904 2.786 65.682 2.896 65.381 2.896 c
+65.065 2.896 64.826 2.741 64.661 2.439 c
+64.661 0.367 l
+64.826 0.062 65.065 -0.088 65.381 -0.088 c
+65.676 -0.088 65.888 0.014 66.029 0.22 c
+66.164 0.434 66.237 0.764 66.249 1.205 c
+h
+69.38 2.792 m
+69.291 2.811 69.192 2.822 69.086 2.822 c
+68.751 2.822 68.516 2.638 68.38 2.278 c
+68.38 -0.574 l
+67.733 -0.574 l
+67.733 3.41 l
+68.365 3.41 l
+68.38 2.998 l
+68.556 3.322 68.799 3.484 69.115 3.484 c
+69.221 3.484 69.31 3.461 69.38 3.424 c
+h
+71.379 -0.647 m
+70.879 -0.647 70.497 -0.5 70.232 -0.206 c
+69.967 0.087 69.835 0.521 69.835 1.103 c
+69.835 1.573 l
+69.835 2.167 69.96 2.634 70.218 2.969 c
+70.482 3.31 70.842 3.484 71.305 3.484 c
+71.764 3.484 72.107 3.329 72.334 3.027 c
+72.57 2.734 72.691 2.271 72.701 1.646 c
+72.701 1.22 l
+70.482 1.22 l
+70.482 1.132 l
+70.482 0.698 70.559 0.386 70.717 0.191 c
+70.883 0.004 71.114 -0.088 71.408 -0.088 c
+71.602 -0.088 71.776 -0.056 71.922 0.014 c
+72.069 0.091 72.206 0.209 72.334 0.367 c
+72.672 -0.044 l
+72.385 -0.449 71.955 -0.647 71.379 -0.647 c
+71.305 2.925 m
+71.03 2.925 70.827 2.829 70.702 2.645 c
+70.574 2.458 70.5 2.167 70.482 1.778 c
+72.055 1.778 l
+72.055 1.866 l
+72.032 2.248 71.967 2.516 71.849 2.674 c
+71.731 2.84 71.548 2.925 71.305 2.925 c
+75.421 0.44 m
+75.421 0.588 75.365 0.709 75.259 0.808 c
+75.149 0.904 74.943 1.022 74.642 1.161 c
+74.297 1.308 74.054 1.429 73.907 1.529 c
+73.76 1.635 73.65 1.753 73.584 1.881 c
+73.514 2.006 73.48 2.164 73.48 2.352 c
+73.48 2.674 73.598 2.944 73.833 3.16 c
+74.068 3.373 74.37 3.484 74.745 3.484 c
+75.126 3.484 75.436 3.37 75.671 3.145 c
+75.906 2.917 76.024 2.631 76.024 2.278 c
+75.377 2.278 l
+75.377 2.454 75.318 2.605 75.201 2.734 c
+75.083 2.859 74.929 2.925 74.745 2.925 c
+74.546 2.925 74.396 2.869 74.289 2.763 c
+74.179 2.664 74.127 2.532 74.127 2.366 c
+74.127 2.238 74.164 2.131 74.245 2.043 c
+74.322 1.962 74.513 1.859 74.818 1.734 c
+75.296 1.547 75.627 1.359 75.803 1.176 c
+75.98 0.999 76.067 0.771 76.067 0.5 c
+76.067 0.147 75.943 -0.133 75.7 -0.339 c
+75.465 -0.544 75.149 -0.647 74.76 -0.647 c
+74.337 -0.647 73.999 -0.53 73.745 -0.294 c
+73.488 -0.052 73.363 0.253 73.363 0.617 c
+74.01 0.617 l
+74.017 0.389 74.087 0.213 74.216 0.087 c
+74.34 -0.03 74.524 -0.088 74.76 -0.088 c
+74.972 -0.088 75.134 -0.04 75.244 0.058 c
+75.362 0.154 75.421 0.282 75.421 0.44 c
+78.332 -0.647 m
+77.831 -0.647 77.449 -0.5 77.185 -0.206 c
+76.92 0.087 76.788 0.521 76.788 1.103 c
+76.788 1.573 l
+76.788 2.167 76.913 2.634 77.17 2.969 c
+77.435 3.31 77.794 3.484 78.258 3.484 c
+78.717 3.484 79.059 3.329 79.286 3.027 c
+79.522 2.734 79.643 2.271 79.654 1.646 c
+79.654 1.22 l
+77.435 1.22 l
+77.435 1.132 l
+77.435 0.698 77.512 0.386 77.67 0.191 c
+77.835 0.004 78.066 -0.088 78.361 -0.088 c
+78.555 -0.088 78.729 -0.056 78.875 0.014 c
+79.022 0.091 79.159 0.209 79.286 0.367 c
+79.625 -0.044 l
+79.338 -0.449 78.908 -0.647 78.332 -0.647 c
+78.258 2.925 m
+77.983 2.925 77.78 2.829 77.655 2.645 c
+77.526 2.458 77.453 2.167 77.435 1.778 c
+79.007 1.778 l
+79.007 1.866 l
+78.985 2.248 78.92 2.516 78.802 2.674 c
+78.684 2.84 78.5 2.925 78.258 2.925 c
+81.05 3.41 m
+81.065 2.969 l
+81.318 3.31 81.642 3.484 82.035 3.484 c
+82.741 3.484 83.097 3.013 83.109 2.072 c
+83.109 -0.574 l
+82.461 -0.574 l
+82.461 2.043 l
+82.461 2.356 82.407 2.576 82.3 2.705 c
+82.19 2.829 82.035 2.896 81.829 2.896 c
+81.671 2.896 81.524 2.84 81.389 2.734 c
+81.26 2.624 81.158 2.487 81.08 2.322 c
+81.08 -0.574 l
+80.433 -0.574 l
+80.433 3.41 l
+h
+84.931 4.365 m
+84.931 3.41 l
+85.534 3.41 l
+85.534 2.881 l
+84.931 2.881 l
+84.931 0.411 l
+84.931 0.253 84.953 0.135 85.004 0.058 c
+85.064 -0.023 85.152 -0.059 85.27 -0.059 c
+85.357 -0.059 85.445 -0.044 85.534 -0.015 c
+85.534 -0.574 l
+85.387 -0.621 85.233 -0.647 85.079 -0.647 c
+84.821 -0.647 84.626 -0.555 84.491 -0.368 c
+84.35 -0.184 84.285 0.077 84.285 0.411 c
+84.285 2.881 l
+83.682 2.881 l
+83.682 3.41 l
+84.285 3.41 l
+84.285 4.365 l
+h
+87.886 1.602 m
+87.886 2.179 88.021 2.634 88.297 2.969 c
+88.58 3.31 88.952 3.484 89.414 3.484 c
+89.874 3.484 90.241 3.314 90.517 2.984 c
+90.8 2.66 90.947 2.212 90.958 1.646 c
+90.958 1.22 l
+90.958 0.65 90.814 0.195 90.531 -0.148 c
+90.255 -0.482 89.889 -0.647 89.43 -0.647 c
+88.966 -0.647 88.595 -0.485 88.312 -0.162 c
+88.036 0.168 87.893 0.61 87.886 1.161 c
+h
+88.532 1.22 m
+88.532 0.816 88.609 0.5 88.767 0.264 c
+88.933 0.029 89.154 -0.088 89.43 -0.088 c
+89.995 -0.088 90.289 0.323 90.311 1.146 c
+90.311 1.602 l
+90.311 2.003 90.226 2.322 90.061 2.557 c
+89.903 2.8 89.686 2.925 89.414 2.925 c
+89.15 2.925 88.933 2.8 88.767 2.557 c
+88.609 2.322 88.532 2.003 88.532 1.602 c
+h
+92.413 3.41 m
+92.428 2.969 l
+92.682 3.31 93.004 3.484 93.398 3.484 c
+94.103 3.484 94.46 3.013 94.471 2.072 c
+94.471 -0.574 l
+93.824 -0.574 l
+93.824 2.043 l
+93.824 2.356 93.769 2.576 93.663 2.705 c
+93.552 2.829 93.398 2.896 93.192 2.896 c
+93.034 2.896 92.887 2.84 92.751 2.734 c
+92.622 2.624 92.52 2.487 92.443 2.322 c
+92.443 -0.574 l
+91.795 -0.574 l
+91.795 3.41 l
+h
+98.542 -0.088 m
+98.756 -0.088 98.928 -0.026 99.057 0.103 c
+99.192 0.239 99.267 0.43 99.277 0.675 c
+99.895 0.675 l
+99.873 0.294 99.737 -0.026 99.483 -0.279 c
+99.226 -0.526 98.914 -0.647 98.542 -0.647 c
+98.05 -0.647 97.675 -0.497 97.411 -0.191 c
+97.153 0.121 97.029 0.588 97.029 1.205 c
+97.029 1.646 l
+97.029 2.241 97.153 2.697 97.411 3.013 c
+97.675 3.326 98.05 3.484 98.542 3.484 c
+98.943 3.484 99.263 3.351 99.498 3.087 c
+99.74 2.829 99.873 2.484 99.895 2.043 c
+99.277 2.043 l
+99.256 2.337 99.182 2.557 99.057 2.705 c
+98.939 2.851 98.766 2.925 98.542 2.925 c
+98.249 2.925 98.032 2.826 97.896 2.631 c
+97.756 2.443 97.683 2.135 97.675 1.705 c
+97.675 1.19 l
+97.675 0.72 97.742 0.386 97.881 0.191 c
+98.028 0.004 98.249 -0.088 98.542 -0.088 c
+102.673 -0.221 m
+102.456 -0.507 102.144 -0.647 101.732 -0.647 c
+101.368 -0.647 101.093 -0.526 100.909 -0.279 c
+100.732 -0.026 100.637 0.338 100.63 0.808 c
+100.63 3.41 l
+101.276 3.41 l
+101.276 0.867 l
+101.276 0.239 101.461 -0.073 101.835 -0.073 c
+102.236 -0.073 102.511 0.103 102.658 0.455 c
+102.658 3.41 l
+103.305 3.41 l
+103.305 -0.574 l
+102.687 -0.574 l
+h
+105.936 2.792 m
+105.848 2.811 105.749 2.822 105.642 2.822 c
+105.307 2.822 105.072 2.638 104.937 2.278 c
+104.937 -0.574 l
+104.289 -0.574 l
+104.289 3.41 l
+104.922 3.41 l
+104.937 2.998 l
+105.113 3.322 105.355 3.484 105.671 3.484 c
+105.778 3.484 105.866 3.461 105.936 3.424 c
+h
+108.229 2.792 m
+108.141 2.811 108.042 2.822 107.935 2.822 c
+107.601 2.822 107.366 2.638 107.229 2.278 c
+107.229 -0.574 l
+106.583 -0.574 l
+106.583 3.41 l
+107.215 3.41 l
+107.229 2.998 l
+107.406 3.322 107.649 3.484 107.965 3.484 c
+108.071 3.484 108.16 3.461 108.229 3.424 c
+h
+110.228 -0.647 m
+109.729 -0.647 109.346 -0.5 109.082 -0.206 c
+108.817 0.087 108.685 0.521 108.685 1.103 c
+108.685 1.573 l
+108.685 2.167 108.81 2.634 109.067 2.969 c
+109.332 3.31 109.692 3.484 110.155 3.484 c
+110.614 3.484 110.956 3.329 111.184 3.027 c
+111.419 2.734 111.54 2.271 111.551 1.646 c
+111.551 1.22 l
+109.332 1.22 l
+109.332 1.132 l
+109.332 0.698 109.409 0.386 109.567 0.191 c
+109.733 0.004 109.964 -0.088 110.257 -0.088 c
+110.452 -0.088 110.625 -0.056 110.772 0.014 c
+110.919 0.091 111.055 0.209 111.184 0.367 c
+111.522 -0.044 l
+111.235 -0.449 110.805 -0.647 110.228 -0.647 c
+110.155 2.925 m
+109.879 2.925 109.677 2.829 109.552 2.645 c
+109.423 2.458 109.35 2.167 109.332 1.778 c
+110.905 1.778 l
+110.905 1.866 l
+110.882 2.248 110.816 2.516 110.699 2.674 c
+110.581 2.84 110.398 2.925 110.155 2.925 c
+112.948 3.41 m
+112.962 2.969 l
+113.216 3.31 113.539 3.484 113.932 3.484 c
+114.638 3.484 114.994 3.013 115.005 2.072 c
+115.005 -0.574 l
+114.359 -0.574 l
+114.359 2.043 l
+114.359 2.356 114.304 2.576 114.197 2.705 c
+114.086 2.829 113.932 2.896 113.727 2.896 c
+113.569 2.896 113.421 2.84 113.286 2.734 c
+113.157 2.624 113.054 2.487 112.977 2.322 c
+112.977 -0.574 l
+112.33 -0.574 l
+112.33 3.41 l
+h
+116.829 4.365 m
+116.829 3.41 l
+117.431 3.41 l
+117.431 2.881 l
+116.829 2.881 l
+116.829 0.411 l
+116.829 0.253 116.85 0.135 116.902 0.058 c
+116.96 -0.023 117.049 -0.059 117.166 -0.059 c
+117.255 -0.059 117.342 -0.044 117.431 -0.015 c
+117.431 -0.574 l
+117.284 -0.621 117.13 -0.647 116.975 -0.647 c
+116.718 -0.647 116.523 -0.555 116.387 -0.368 c
+116.247 -0.184 116.181 0.077 116.181 0.411 c
+116.181 2.881 l
+115.578 2.881 l
+115.578 3.41 l
+116.181 3.41 l
+116.181 4.365 l
+h
+122.796 1.22 m
+122.796 0.602 122.682 0.135 122.458 -0.177 c
+122.241 -0.493 121.918 -0.647 121.488 -0.647 c
+121.065 -0.647 120.752 -0.467 120.547 -0.104 c
+120.517 -0.574 l
+119.915 -0.574 l
+119.915 5.071 l
+120.561 5.071 l
+120.561 2.969 l
+120.775 3.31 121.084 3.484 121.488 3.484 c
+121.918 3.484 122.241 3.326 122.458 3.013 c
+122.682 2.708 122.796 2.241 122.796 1.616 c
+h
+122.149 1.602 m
+122.149 2.072 122.08 2.403 121.943 2.601 c
+121.814 2.796 121.606 2.896 121.311 2.896 c
+120.977 2.896 120.727 2.711 120.561 2.352 c
+120.561 0.47 l
+120.727 0.106 120.981 -0.073 121.326 -0.073 c
+121.62 -0.073 121.83 0.029 121.958 0.235 c
+122.083 0.44 122.149 0.756 122.149 1.19 c
+h
+125.28 2.792 m
+125.192 2.811 125.093 2.822 124.986 2.822 c
+124.652 2.822 124.417 2.638 124.28 2.278 c
+124.28 -0.574 l
+123.634 -0.574 l
+123.634 3.41 l
+124.266 3.41 l
+124.28 2.998 l
+124.457 3.322 124.7 3.484 125.016 3.484 c
+125.122 3.484 125.211 3.461 125.28 3.424 c
+h
+127.823 -0.574 m
+127.783 -0.485 127.757 -0.339 127.75 -0.133 c
+127.514 -0.478 127.22 -0.647 126.867 -0.647 c
+126.504 -0.647 126.221 -0.551 126.015 -0.353 c
+125.816 -0.148 125.721 0.139 125.721 0.515 c
+125.721 0.914 125.857 1.234 126.132 1.469 c
+126.404 1.712 126.78 1.837 127.25 1.837 c
+127.735 1.837 l
+127.735 2.263 l
+127.735 2.499 127.68 2.664 127.573 2.763 c
+127.463 2.869 127.301 2.925 127.088 2.925 c
+126.89 2.925 126.728 2.866 126.603 2.749 c
+126.485 2.631 126.427 2.484 126.427 2.308 c
+125.78 2.308 l
+125.78 2.502 125.839 2.693 125.957 2.881 c
+126.081 3.064 126.243 3.212 126.441 3.322 c
+126.647 3.428 126.875 3.484 127.132 3.484 c
+127.532 3.484 127.838 3.38 128.043 3.175 c
+128.257 2.969 128.37 2.674 128.382 2.293 c
+128.382 0.279 l
+128.382 -0.026 128.418 -0.291 128.499 -0.515 c
+128.499 -0.574 l
+h
+126.956 -0.059 m
+127.121 -0.059 127.272 -0.015 127.412 0.073 c
+127.559 0.162 127.665 0.272 127.735 0.411 c
+127.735 1.352 l
+127.368 1.352 l
+127.052 1.352 126.809 1.282 126.632 1.146 c
+126.456 1.018 126.368 0.83 126.368 0.588 c
+126.368 0.359 126.412 0.195 126.5 0.087 c
+126.589 -0.011 126.739 -0.059 126.956 -0.059 c
+129.999 3.41 m
+130.013 2.969 l
+130.267 3.31 130.59 3.484 130.983 3.484 c
+131.688 3.484 132.045 3.013 132.056 2.072 c
+132.056 -0.574 l
+131.41 -0.574 l
+131.41 2.043 l
+131.41 2.356 131.354 2.576 131.248 2.705 c
+131.138 2.829 130.983 2.896 130.778 2.896 c
+130.62 2.896 130.472 2.84 130.337 2.734 c
+130.208 2.624 130.105 2.487 130.028 2.322 c
+130.028 -0.574 l
+129.381 -0.574 l
+129.381 3.41 l
+h
+134.408 -0.088 m
+134.622 -0.088 134.794 -0.026 134.923 0.103 c
+135.058 0.239 135.133 0.43 135.143 0.675 c
+135.761 0.675 l
+135.738 0.294 135.603 -0.026 135.349 -0.279 c
+135.092 -0.526 134.78 -0.647 134.408 -0.647 c
+133.916 -0.647 133.541 -0.497 133.276 -0.191 c
+133.02 0.121 132.894 0.588 132.894 1.205 c
+132.894 1.646 l
+132.894 2.241 133.02 2.697 133.276 3.013 c
+133.541 3.326 133.916 3.484 134.408 3.484 c
+134.809 3.484 135.129 3.351 135.364 3.087 c
+135.607 2.829 135.738 2.484 135.761 2.043 c
+135.143 2.043 l
+135.121 2.337 135.048 2.557 134.923 2.705 c
+134.805 2.851 134.632 2.925 134.408 2.925 c
+134.115 2.925 133.897 2.826 133.762 2.631 c
+133.622 2.443 133.548 2.135 133.541 1.705 c
+133.541 1.19 l
+133.541 0.72 133.607 0.386 133.747 0.191 c
+133.894 0.004 134.115 -0.088 134.408 -0.088 c
+137.157 2.998 m
+137.41 3.322 137.73 3.484 138.113 3.484 c
+138.818 3.484 139.174 3.013 139.185 2.072 c
+139.185 -0.574 l
+138.539 -0.574 l
+138.539 2.043 l
+138.539 2.356 138.483 2.576 138.377 2.705 c
+138.267 2.829 138.113 2.896 137.907 2.896 c
+137.749 2.896 137.602 2.84 137.466 2.734 c
+137.337 2.624 137.234 2.487 137.157 2.322 c
+137.157 -0.574 l
+136.51 -0.574 l
+136.51 5.071 l
+137.157 5.071 l
+h
+143.904 -0.574 m
+143.864 -0.485 143.838 -0.339 143.831 -0.133 c
+143.595 -0.478 143.301 -0.647 142.948 -0.647 c
+142.584 -0.647 142.302 -0.551 142.096 -0.353 c
+141.897 -0.148 141.801 0.139 141.801 0.515 c
+141.801 0.914 141.938 1.234 142.213 1.469 c
+142.485 1.712 142.86 1.837 143.33 1.837 c
+143.816 1.837 l
+143.816 2.263 l
+143.816 2.499 143.76 2.664 143.654 2.763 c
+143.544 2.869 143.382 2.925 143.169 2.925 c
+142.971 2.925 142.809 2.866 142.684 2.749 c
+142.566 2.631 142.507 2.484 142.507 2.308 c
+141.861 2.308 l
+141.861 2.502 141.919 2.693 142.037 2.881 c
+142.162 3.064 142.324 3.212 142.522 3.322 c
+142.728 3.428 142.956 3.484 143.213 3.484 c
+143.613 3.484 143.918 3.38 144.124 3.175 c
+144.338 2.969 144.451 2.674 144.463 2.293 c
+144.463 0.279 l
+144.463 -0.026 144.499 -0.291 144.58 -0.515 c
+144.58 -0.574 l
+h
+143.037 -0.059 m
+143.202 -0.059 143.353 -0.015 143.492 0.073 c
+143.64 0.162 143.746 0.272 143.816 0.411 c
+143.816 1.352 l
+143.448 1.352 l
+143.133 1.352 142.89 1.282 142.713 1.146 c
+142.537 1.018 142.449 0.83 142.449 0.588 c
+142.449 0.359 142.493 0.195 142.581 0.087 c
+142.669 -0.011 142.819 -0.059 143.037 -0.059 c
+146.079 3.41 m
+146.094 2.969 l
+146.347 3.31 146.671 3.484 147.064 3.484 c
+147.769 3.484 148.126 3.013 148.137 2.072 c
+148.137 -0.574 l
+147.49 -0.574 l
+147.49 2.043 l
+147.49 2.356 147.435 2.576 147.328 2.705 c
+147.218 2.829 147.064 2.896 146.858 2.896 c
+146.7 2.896 146.553 2.84 146.418 2.734 c
+146.289 2.624 146.186 2.487 146.109 2.322 c
+146.109 -0.574 l
+145.462 -0.574 l
+145.462 3.41 l
+h
+148.975 1.602 m
+148.975 2.208 149.086 2.674 149.313 2.998 c
+149.548 3.322 149.875 3.484 150.298 3.484 c
+150.68 3.484 150.977 3.326 151.195 3.013 c
+151.195 5.071 l
+151.841 5.071 l
+151.841 -0.574 l
+151.253 -0.574 l
+151.209 -0.148 l
+151.004 -0.482 150.698 -0.647 150.298 -0.647 c
+149.886 -0.647 149.562 -0.493 149.327 -0.177 c
+149.092 0.147 148.975 0.602 148.975 1.19 c
+h
+149.622 1.22 m
+149.622 0.779 149.684 0.448 149.813 0.235 c
+149.949 0.029 150.169 -0.073 150.474 -0.073 c
+150.798 -0.073 151.037 0.087 151.195 0.411 c
+151.195 2.425 l
+151.025 2.738 150.786 2.896 150.474 2.896 c
+150.169 2.896 149.949 2.792 149.813 2.587 c
+149.684 2.381 149.622 2.057 149.622 1.616 c
+h
+155.178 3.41 m
+155.193 2.969 l
+155.446 3.31 155.77 3.484 156.163 3.484 c
+156.868 3.484 157.225 3.013 157.236 2.072 c
+157.236 -0.574 l
+156.589 -0.574 l
+156.589 2.043 l
+156.589 2.356 156.534 2.576 156.427 2.705 c
+156.317 2.829 156.163 2.896 155.957 2.896 c
+155.799 2.896 155.652 2.84 155.516 2.734 c
+155.388 2.624 155.284 2.487 155.207 2.322 c
+155.207 -0.574 l
+154.561 -0.574 l
+154.561 3.41 l
+h
+158.073 1.602 m
+158.073 2.179 158.21 2.634 158.485 2.969 c
+158.769 3.31 159.139 3.484 159.602 3.484 c
+160.062 3.484 160.429 3.314 160.705 2.984 c
+160.988 2.66 161.135 2.212 161.146 1.646 c
+161.146 1.22 l
+161.146 0.65 161.003 0.195 160.72 -0.148 c
+160.444 -0.482 160.076 -0.647 159.617 -0.647 c
+159.154 -0.647 158.783 -0.485 158.5 -0.162 c
+158.224 0.168 158.081 0.61 158.073 1.161 c
+h
+158.721 1.22 m
+158.721 0.816 158.798 0.5 158.956 0.264 c
+159.121 0.029 159.341 -0.088 159.617 -0.088 c
+160.183 -0.088 160.477 0.323 160.499 1.146 c
+160.499 1.602 l
+160.499 2.003 160.415 2.322 160.249 2.557 c
+160.091 2.8 159.874 2.925 159.602 2.925 c
+159.338 2.925 159.121 2.8 158.956 2.557 c
+158.798 2.322 158.721 2.003 158.721 1.602 c
+h
+162.836 4.365 m
+162.836 3.41 l
+163.438 3.41 l
+163.438 2.881 l
+162.836 2.881 l
+162.836 0.411 l
+162.836 0.253 162.858 0.135 162.91 0.058 c
+162.968 -0.023 163.056 -0.059 163.174 -0.059 c
+163.262 -0.059 163.351 -0.044 163.438 -0.015 c
+163.438 -0.574 l
+163.292 -0.621 163.137 -0.647 162.983 -0.647 c
+162.726 -0.647 162.532 -0.555 162.395 -0.368 c
+162.256 -0.184 162.189 0.077 162.189 0.411 c
+162.189 2.881 l
+161.587 2.881 l
+161.587 3.41 l
+162.189 3.41 l
+162.189 4.365 l
+h
+166.54 3.41 m
+166.555 3.042 l
+166.798 3.337 167.118 3.484 167.511 3.484 c
+167.951 3.484 168.26 3.285 168.437 2.896 c
+168.69 3.285 169.04 3.484 169.48 3.484 c
+170.215 3.484 170.59 3.021 170.612 2.102 c
+170.612 -0.574 l
+169.965 -0.574 l
+169.965 2.043 l
+169.965 2.337 169.91 2.55 169.803 2.69 c
+169.705 2.826 169.532 2.896 169.289 2.896 c
+169.09 2.896 168.929 2.815 168.804 2.66 c
+168.687 2.514 168.616 2.322 168.598 2.087 c
+168.598 -0.574 l
+167.937 -0.574 l
+167.937 2.072 l
+167.937 2.62 167.716 2.896 167.276 2.896 c
+166.941 2.896 166.706 2.734 166.57 2.41 c
+166.57 -0.574 l
+165.923 -0.574 l
+165.923 3.41 l
+h
+173.008 -0.647 m
+172.508 -0.647 172.126 -0.5 171.862 -0.206 c
+171.597 0.087 171.465 0.521 171.465 1.103 c
+171.465 1.573 l
+171.465 2.167 171.59 2.634 171.847 2.969 c
+172.111 3.31 172.471 3.484 172.934 3.484 c
+173.393 3.484 173.736 3.329 173.963 3.027 c
+174.199 2.734 174.32 2.271 174.331 1.646 c
+174.331 1.22 l
+172.111 1.22 l
+172.111 1.132 l
+172.111 0.698 172.188 0.386 172.346 0.191 c
+172.512 0.004 172.743 -0.088 173.038 -0.088 c
+173.232 -0.088 173.405 -0.056 173.551 0.014 c
+173.699 0.091 173.835 0.209 173.963 0.367 c
+174.301 -0.044 l
+174.015 -0.449 173.585 -0.647 173.008 -0.647 c
+172.934 2.925 m
+172.659 2.925 172.456 2.829 172.332 2.645 c
+172.203 2.458 172.13 2.167 172.111 1.778 c
+173.684 1.778 l
+173.684 1.866 l
+173.662 2.248 173.596 2.516 173.478 2.674 c
+173.36 2.84 173.177 2.925 172.934 2.925 c
+176.756 2.792 m
+176.668 2.811 176.568 2.822 176.462 2.822 c
+176.128 2.822 175.893 2.638 175.756 2.278 c
+175.756 -0.574 l
+175.11 -0.574 l
+175.11 3.41 l
+175.742 3.41 l
+175.756 2.998 l
+175.933 3.322 176.175 3.484 176.491 3.484 c
+176.598 3.484 176.686 3.461 176.756 3.424 c
+h
+177.197 1.602 m
+177.197 2.219 177.308 2.682 177.535 2.998 c
+177.759 3.322 178.094 3.484 178.534 3.484 c
+178.935 3.484 179.24 3.307 179.446 2.954 c
+179.49 3.41 l
+180.078 3.41 l
+180.078 -0.618 l
+180.078 -1.106 179.949 -1.484 179.696 -1.75 c
+179.439 -2.014 179.086 -2.147 178.638 -2.147 c
+178.439 -2.147 178.218 -2.095 177.977 -1.999 c
+177.73 -1.9 177.549 -1.779 177.432 -1.632 c
+177.697 -1.191 l
+177.962 -1.455 178.259 -1.588 178.594 -1.588 c
+179.13 -1.588 179.406 -1.294 179.417 -0.706 c
+179.417 -0.177 l
+179.211 -0.493 178.91 -0.647 178.52 -0.647 c
+178.108 -0.647 177.785 -0.497 177.549 -0.191 c
+177.322 0.121 177.204 0.573 177.197 1.161 c
+h
+177.859 1.22 m
+177.859 0.779 177.921 0.448 178.05 0.235 c
+178.175 0.029 178.391 -0.073 178.696 -0.073 c
+179.02 -0.073 179.259 0.091 179.417 0.426 c
+179.417 2.41 l
+179.248 2.734 179.009 2.896 178.696 2.896 c
+178.403 2.896 178.185 2.792 178.05 2.587 c
+177.921 2.381 177.859 2.057 177.859 1.616 c
+h
+182.459 -0.647 m
+181.96 -0.647 181.578 -0.5 181.312 -0.206 c
+181.048 0.087 180.916 0.521 180.916 1.103 c
+180.916 1.573 l
+180.916 2.167 181.041 2.634 181.298 2.969 c
+181.563 3.31 181.923 3.484 182.386 3.484 c
+182.845 3.484 183.187 3.329 183.415 3.027 c
+183.65 2.734 183.772 2.271 183.782 1.646 c
+183.782 1.22 l
+181.563 1.22 l
+181.563 1.132 l
+181.563 0.698 181.64 0.386 181.798 0.191 c
+181.963 0.004 182.195 -0.088 182.488 -0.088 c
+182.683 -0.088 182.856 -0.056 183.003 0.014 c
+183.151 0.091 183.286 0.209 183.415 0.367 c
+183.753 -0.044 l
+183.466 -0.449 183.036 -0.647 182.459 -0.647 c
+182.386 2.925 m
+182.11 2.925 181.908 2.829 181.783 2.645 c
+181.655 2.458 181.582 2.167 181.563 1.778 c
+183.136 1.778 l
+183.136 1.866 l
+183.114 2.248 183.047 2.516 182.93 2.674 c
+182.812 2.84 182.629 2.925 182.386 2.925 c
+184.429 1.602 m
+184.429 2.208 184.539 2.674 184.767 2.998 c
+185.002 3.322 185.329 3.484 185.752 3.484 c
+186.134 3.484 186.432 3.326 186.648 3.013 c
+186.648 5.071 l
+187.296 5.071 l
+187.296 -0.574 l
+186.708 -0.574 l
+186.663 -0.148 l
+186.457 -0.482 186.153 -0.647 185.752 -0.647 c
+185.341 -0.647 185.017 -0.493 184.782 -0.177 c
+184.547 0.147 184.429 0.602 184.429 1.19 c
+h
+185.075 1.22 m
+185.075 0.779 185.138 0.448 185.267 0.235 c
+185.403 0.029 185.623 -0.073 185.929 -0.073 c
+186.251 -0.073 186.49 0.087 186.648 0.411 c
+186.648 2.425 l
+186.48 2.738 186.241 2.896 185.929 2.896 c
+185.623 2.896 185.403 2.792 185.267 2.587 c
+185.138 2.381 185.075 2.057 185.075 1.616 c
+h
+190.72 -0.574 -0.646 3.984 re
+190.764 4.453 m
+190.764 4.343 190.735 4.251 190.677 4.174 c
+190.617 4.104 190.521 4.072 190.397 4.072 c
+190.28 4.072 190.184 4.104 190.118 4.174 c
+190.059 4.251 190.029 4.343 190.029 4.453 c
+190.029 4.571 190.059 4.663 190.118 4.733 c
+190.184 4.81 190.28 4.85 190.397 4.85 c
+190.521 4.85 190.617 4.81 190.677 4.733 c
+190.735 4.652 190.764 4.56 190.764 4.453 c
+192.352 3.41 m
+192.366 2.969 l
+192.62 3.31 192.944 3.484 193.337 3.484 c
+194.043 3.484 194.399 3.013 194.409 2.072 c
+194.409 -0.574 l
+193.763 -0.574 l
+193.763 2.043 l
+193.763 2.356 193.708 2.576 193.601 2.705 c
+193.491 2.829 193.337 2.896 193.131 2.896 c
+192.973 2.896 192.826 2.84 192.69 2.734 c
+192.561 2.624 192.458 2.487 192.381 2.322 c
+192.381 -0.574 l
+191.735 -0.574 l
+191.735 3.41 l
+h
+196.233 4.365 m
+196.233 3.41 l
+196.835 3.41 l
+196.835 2.881 l
+196.233 2.881 l
+196.233 0.411 l
+196.233 0.253 196.254 0.135 196.306 0.058 c
+196.364 -0.023 196.453 -0.059 196.57 -0.059 c
+196.659 -0.059 196.747 -0.044 196.835 -0.015 c
+196.835 -0.574 l
+196.688 -0.621 196.534 -0.647 196.379 -0.647 c
+196.123 -0.647 195.928 -0.555 195.791 -0.368 c
+195.652 -0.184 195.585 0.077 195.585 0.411 c
+195.585 2.881 l
+194.983 2.881 l
+194.983 3.41 l
+195.585 3.41 l
+195.585 4.365 l
+h
+197.393 1.602 m
+197.393 2.179 197.53 2.634 197.806 2.969 c
+198.088 3.31 198.459 3.484 198.922 3.484 c
+199.381 3.484 199.749 3.314 200.025 2.984 c
+200.308 2.66 200.455 2.212 200.466 1.646 c
+200.466 1.22 l
+200.466 0.65 200.322 0.195 200.04 -0.148 c
+199.764 -0.482 199.396 -0.647 198.937 -0.647 c
+198.474 -0.647 198.103 -0.485 197.82 -0.162 c
+197.544 0.168 197.401 0.61 197.393 1.161 c
+h
+198.041 1.22 m
+198.041 0.816 198.118 0.5 198.276 0.264 c
+198.441 0.029 198.662 -0.088 198.937 -0.088 c
+199.503 -0.088 199.797 0.323 199.819 1.146 c
+199.819 1.602 l
+199.819 2.003 199.734 2.322 199.569 2.557 c
+199.411 2.8 199.194 2.925 198.922 2.925 c
+198.658 2.925 198.441 2.8 198.276 2.557 c
+198.118 2.322 198.041 2.003 198.041 1.602 c
+h
+f
+Q
+q 1 0 0 1 263.3007 391.1359 cm
+0 0 m
+-0.338 0.029 l
+-0.625 0.029 -0.816 -0.096 -0.911 -0.339 c
+-0.911 -2.97 l
+-1.955 -2.97 l
+-1.955 1.014 l
+-0.985 1.014 l
+-0.955 0.573 l
+-0.79 0.914 -0.558 1.087 -0.264 1.087 c
+-0.147 1.087 -0.055 1.065 0.015 1.028 c
+h
+2.072 -3.043 m
+1.544 -3.043 1.125 -2.889 0.823 -2.573 c
+0.53 -2.249 0.383 -1.79 0.383 -1.191 c
+0.383 -0.882 l
+0.383 -0.258 0.518 0.228 0.794 0.573 c
+1.066 0.914 1.459 1.087 1.97 1.087 c
+2.469 1.087 2.841 0.926 3.087 0.602 c
+3.341 0.278 3.473 -0.199 3.484 -0.823 c
+3.484 -1.324 l
+1.411 -1.324 l
+1.43 -1.617 1.492 -1.834 1.602 -1.97 c
+1.72 -2.11 1.9 -2.176 2.147 -2.176 c
+2.488 -2.176 2.778 -2.058 3.013 -1.823 c
+3.425 -2.455 l
+3.296 -2.631 3.109 -2.775 2.866 -2.881 c
+2.62 -2.988 2.356 -3.043 2.072 -3.043 c
+1.426 -0.603 m
+2.455 -0.603 l
+2.455 -0.5 l
+2.455 -0.265 2.415 -0.088 2.338 0.029 c
+2.267 0.154 2.139 0.22 1.955 0.22 c
+1.779 0.22 1.646 0.151 1.559 0.014 c
+1.478 -0.115 1.434 -0.32 1.426 -0.603 c
+4.307 -2.97 m
+4.307 0.22 l
+3.822 0.22 l
+3.822 1.014 l
+4.307 1.014 l
+4.307 1.367 l
+4.307 1.807 4.418 2.146 4.645 2.381 c
+4.881 2.624 5.197 2.748 5.6 2.748 c
+5.725 2.748 5.883 2.723 6.071 2.674 c
+6.071 1.851 l
+6.001 1.87 5.916 1.881 5.821 1.881 c
+5.505 1.881 5.351 1.697 5.351 1.337 c
+5.351 1.014 l
+5.968 1.014 l
+5.968 0.22 l
+5.351 0.22 l
+5.351 -2.97 l
+h
+f
+Q
+q 1 0 0 1 269.9893 388.5188 cm
+0 0 m
+0 0.118 0.033 0.214 0.103 0.294 c
+0.168 0.372 0.272 0.412 0.411 0.412 c
+0.558 0.412 0.665 0.372 0.735 0.294 c
+0.812 0.214 0.852 0.118 0.852 0 c
+0.852 -0.11 0.812 -0.202 0.735 -0.279 c
+0.665 -0.357 0.558 -0.397 0.411 -0.397 c
+0.272 -0.397 0.168 -0.357 0.103 -0.279 c
+0.033 -0.202 0 -0.11 0 0 c
+f
+Q
+q 1 0 0 1 135.7129 380.1556 cm
+0 0 m
+-1.808 0 l
+-2.219 -1.397 l
+-2.91 -1.397 l
+-1.19 3.954 l
+-0.617 3.954 l
+1.118 -1.397 l
+0.426 -1.397 l
+h
+-1.631 0.588 m
+-0.176 0.588 l
+-0.897 3.013 l
+h
+f
+Q
+q 1 0 0 1 141.0347 381.7285 cm
+0 0 m
+-0.339 0.029 l
+-0.625 0.029 -0.817 -0.096 -0.912 -0.339 c
+-0.912 -2.97 l
+-1.956 -2.97 l
+-1.956 1.014 l
+-0.985 1.014 l
+-0.956 0.573 l
+-0.79 0.914 -0.559 1.087 -0.265 1.087 c
+-0.148 1.087 -0.056 1.065 0.014 1.028 c
+h
+2.072 -3.043 m
+1.543 -3.043 1.124 -2.889 0.823 -2.573 c
+0.529 -2.249 0.382 -1.79 0.382 -1.191 c
+0.382 -0.882 l
+0.382 -0.258 0.517 0.228 0.793 0.573 c
+1.065 0.914 1.458 1.087 1.969 1.087 c
+2.469 1.087 2.84 0.926 3.087 0.602 c
+3.34 0.278 3.472 -0.198 3.484 -0.823 c
+3.484 -1.324 l
+1.411 -1.324 l
+1.429 -1.617 1.492 -1.834 1.602 -1.97 c
+1.72 -2.11 1.899 -2.176 2.146 -2.176 c
+2.487 -2.176 2.778 -2.058 3.013 -1.823 c
+3.424 -2.455 l
+3.295 -2.631 3.108 -2.775 2.865 -2.881 c
+2.62 -2.988 2.355 -3.043 2.072 -3.043 c
+1.425 -0.603 m
+2.454 -0.603 l
+2.454 -0.5 l
+2.454 -0.265 2.414 -0.088 2.337 0.029 c
+2.267 0.154 2.138 0.22 1.955 0.22 c
+1.778 0.22 1.646 0.151 1.558 0.014 c
+1.477 -0.115 1.433 -0.32 1.425 -0.603 c
+4.307 -2.97 m
+4.307 0.22 l
+3.821 0.22 l
+3.821 1.014 l
+4.307 1.014 l
+4.307 1.367 l
+4.307 1.807 4.417 2.146 4.644 2.381 c
+4.88 2.624 5.196 2.748 5.6 2.748 c
+5.725 2.748 5.882 2.723 6.071 2.675 c
+6.071 1.851 l
+6 1.87 5.916 1.882 5.82 1.882 c
+5.504 1.882 5.35 1.697 5.35 1.337 c
+5.35 1.014 l
+5.967 1.014 l
+5.967 0.22 l
+5.35 0.22 l
+5.35 -2.97 l
+h
+f
+Q
+q 1 0 0 1 150.7798 379.244 cm
+0 0 m
+0.214 0 0.386 0.062 0.515 0.191 c
+0.65 0.327 0.723 0.518 0.735 0.764 c
+1.352 0.764 l
+1.33 0.382 1.194 0.062 0.941 -0.191 c
+0.684 -0.437 0.371 -0.559 0 -0.559 c
+-0.492 -0.559 -0.867 -0.408 -1.132 -0.103 c
+-1.389 0.21 -1.514 0.676 -1.514 1.294 c
+-1.514 1.735 l
+-1.514 2.329 -1.389 2.786 -1.132 3.102 c
+-0.867 3.414 -0.492 3.572 0 3.572 c
+0.401 3.572 0.721 3.439 0.956 3.175 c
+1.198 2.917 1.33 2.572 1.352 2.132 c
+0.735 2.132 l
+0.713 2.425 0.64 2.645 0.515 2.793 c
+0.397 2.94 0.224 3.013 0 3.013 c
+-0.294 3.013 -0.511 2.914 -0.647 2.72 c
+-0.786 2.531 -0.86 2.223 -0.867 1.793 c
+-0.867 1.278 l
+-0.867 0.808 -0.802 0.474 -0.661 0.279 c
+-0.515 0.092 -0.294 0 0 0 c
+4.145 -0.485 m
+4.104 -0.397 4.079 -0.25 4.072 -0.044 c
+3.836 -0.389 3.543 -0.559 3.19 -0.559 c
+2.826 -0.559 2.543 -0.463 2.337 -0.264 c
+2.138 -0.059 2.043 0.228 2.043 0.603 c
+2.043 1.003 2.179 1.323 2.454 1.558 c
+2.726 1.801 3.102 1.926 3.572 1.926 c
+4.056 1.926 l
+4.056 2.352 l
+4.056 2.587 4.002 2.753 3.896 2.851 c
+3.785 2.958 3.623 3.013 3.41 3.013 c
+3.212 3.013 3.05 2.955 2.925 2.837 c
+2.807 2.72 2.749 2.572 2.749 2.396 c
+2.102 2.396 l
+2.102 2.591 2.161 2.782 2.278 2.969 c
+2.404 3.152 2.565 3.3 2.763 3.41 c
+2.969 3.516 3.197 3.572 3.454 3.572 c
+3.855 3.572 4.16 3.468 4.366 3.263 c
+4.579 3.057 4.692 2.763 4.704 2.381 c
+4.704 0.368 l
+4.704 0.062 4.74 -0.202 4.821 -0.426 c
+4.821 -0.485 l
+h
+3.278 0.029 m
+3.443 0.029 3.594 0.073 3.734 0.162 c
+3.881 0.25 3.987 0.36 4.056 0.5 c
+4.056 1.44 l
+3.69 1.44 l
+3.373 1.44 3.131 1.371 2.955 1.234 c
+2.778 1.106 2.69 0.918 2.69 0.676 c
+2.69 0.448 2.734 0.283 2.822 0.176 c
+2.911 0.077 3.061 0.029 3.278 0.029 c
+6.321 3.499 m
+6.335 3.057 l
+6.589 3.399 6.913 3.572 7.306 3.572 c
+8.011 3.572 8.367 3.102 8.378 2.161 c
+8.378 -0.485 l
+7.732 -0.485 l
+7.732 2.132 l
+7.732 2.444 7.676 2.664 7.57 2.793 c
+7.46 2.917 7.306 2.984 7.1 2.984 c
+6.942 2.984 6.795 2.928 6.659 2.822 c
+6.53 2.712 6.427 2.576 6.35 2.41 c
+6.35 -0.485 l
+5.703 -0.485 l
+5.703 3.499 l
+h
+13.949 1.309 m
+13.949 0.69 13.835 0.224 13.612 -0.088 c
+13.394 -0.405 13.071 -0.559 12.641 -0.559 c
+12.218 -0.559 11.906 -0.378 11.7 -0.015 c
+11.671 -0.485 l
+11.069 -0.485 l
+11.069 5.159 l
+11.715 5.159 l
+11.715 3.057 l
+11.929 3.399 12.237 3.572 12.641 3.572 c
+13.071 3.572 13.394 3.414 13.612 3.102 c
+13.835 2.797 13.949 2.329 13.949 1.705 c
+h
+13.303 1.69 m
+13.303 2.161 13.232 2.491 13.097 2.69 c
+12.968 2.884 12.758 2.984 12.465 2.984 c
+12.13 2.984 11.881 2.8 11.715 2.44 c
+11.715 0.559 l
+11.881 0.195 12.134 0.015 12.48 0.015 c
+12.773 0.015 12.983 0.118 13.111 0.324 c
+13.236 0.529 13.303 0.845 13.303 1.278 c
+h
+16.213 -0.559 m
+15.713 -0.559 15.331 -0.412 15.067 -0.118 c
+14.802 0.176 14.67 0.61 14.67 1.191 c
+14.67 1.661 l
+14.67 2.256 14.795 2.722 15.052 3.057 c
+15.316 3.399 15.676 3.572 16.139 3.572 c
+16.598 3.572 16.941 3.418 17.168 3.116 c
+17.404 2.822 17.525 2.359 17.536 1.735 c
+17.536 1.309 l
+15.316 1.309 l
+15.316 1.22 l
+15.316 0.786 15.393 0.474 15.551 0.279 c
+15.717 0.092 15.948 0 16.243 0 c
+16.438 0 16.61 0.033 16.756 0.103 c
+16.904 0.18 17.04 0.297 17.168 0.455 c
+17.506 0.044 l
+17.22 -0.36 16.79 -0.559 16.213 -0.559 c
+16.139 3.013 m
+15.864 3.013 15.661 2.917 15.537 2.734 c
+15.408 2.547 15.335 2.256 15.316 1.866 c
+16.889 1.866 l
+16.889 1.955 l
+16.867 2.337 16.801 2.605 16.683 2.763 c
+16.565 2.928 16.382 3.013 16.139 3.013 c
+21.46 -0.559 m
+20.961 -0.559 20.579 -0.412 20.314 -0.118 c
+20.049 0.176 19.917 0.61 19.917 1.191 c
+19.917 1.661 l
+19.917 2.256 20.042 2.722 20.299 3.057 c
+20.564 3.399 20.924 3.572 21.387 3.572 c
+21.847 3.572 22.188 3.418 22.416 3.116 c
+22.651 2.822 22.772 2.359 22.784 1.735 c
+22.784 1.309 l
+20.564 1.309 l
+20.564 1.22 l
+20.564 0.786 20.641 0.474 20.799 0.279 c
+20.964 0.092 21.196 0 21.49 0 c
+21.685 0 21.857 0.033 22.005 0.103 c
+22.152 0.18 22.287 0.297 22.416 0.455 c
+22.754 0.044 l
+22.468 -0.36 22.038 -0.559 21.46 -0.559 c
+21.387 3.013 m
+21.111 3.013 20.91 2.917 20.785 2.734 c
+20.656 2.547 20.582 2.256 20.564 1.866 c
+22.137 1.866 l
+22.137 1.955 l
+22.115 2.337 22.048 2.605 21.931 2.763 c
+21.813 2.928 21.629 3.013 21.387 3.013 c
+23.607 -0.133 m
+23.607 -0.015 23.64 0.081 23.709 0.162 c
+23.775 0.239 23.879 0.279 24.018 0.279 c
+24.165 0.279 24.272 0.239 24.342 0.162 c
+24.419 0.081 24.459 -0.015 24.459 -0.133 c
+24.459 -0.243 24.419 -0.335 24.342 -0.412 c
+24.272 -0.489 24.165 -0.53 24.018 -0.53 c
+23.879 -0.53 23.775 -0.489 23.709 -0.412 c
+23.64 -0.335 23.607 -0.243 23.607 -0.133 c
+25.415 1.69 m
+25.415 2.308 25.525 2.77 25.753 3.087 c
+25.977 3.41 26.312 3.572 26.752 3.572 c
+27.153 3.572 27.457 3.395 27.663 3.042 c
+27.708 3.499 l
+28.296 3.499 l
+28.296 -0.53 l
+28.296 -1.018 28.167 -1.396 27.914 -1.661 c
+27.656 -1.926 27.303 -2.058 26.855 -2.058 c
+26.657 -2.058 26.436 -2.007 26.194 -1.911 c
+25.947 -1.812 25.768 -1.691 25.65 -1.544 c
+25.915 -1.103 l
+26.179 -1.367 26.476 -1.5 26.811 -1.5 c
+27.347 -1.5 27.623 -1.205 27.634 -0.617 c
+27.634 -0.088 l
+27.428 -0.405 27.127 -0.559 26.738 -0.559 c
+26.326 -0.559 26.003 -0.408 25.768 -0.103 c
+25.539 0.21 25.422 0.661 25.415 1.249 c
+h
+26.076 1.309 m
+26.076 0.867 26.138 0.536 26.267 0.324 c
+26.392 0.118 26.609 0.015 26.914 0.015 c
+27.237 0.015 27.476 0.18 27.634 0.515 c
+27.634 2.499 l
+27.465 2.822 27.226 2.984 26.914 2.984 c
+26.62 2.984 26.403 2.881 26.267 2.675 c
+26.138 2.469 26.076 2.146 26.076 1.705 c
+h
+29.296 -0.133 m
+29.296 -0.015 29.328 0.081 29.398 0.162 c
+29.464 0.239 29.567 0.279 29.707 0.279 c
+29.853 0.279 29.961 0.239 30.03 0.162 c
+30.107 0.081 30.148 -0.015 30.148 -0.133 c
+30.148 -0.243 30.107 -0.335 30.03 -0.412 c
+29.961 -0.489 29.853 -0.53 29.707 -0.53 c
+29.567 -0.53 29.464 -0.489 29.398 -0.412 c
+29.328 -0.335 29.296 -0.243 29.296 -0.133 c
+34.998 -0.485 m
+34.958 -0.397 34.932 -0.25 34.925 -0.044 c
+34.69 -0.389 34.395 -0.559 34.043 -0.559 c
+33.679 -0.559 33.396 -0.463 33.19 -0.264 c
+32.992 -0.059 32.897 0.228 32.897 0.603 c
+32.897 1.003 33.032 1.323 33.308 1.558 c
+33.58 1.801 33.955 1.926 34.425 1.926 c
+34.91 1.926 l
+34.91 2.352 l
+34.91 2.587 34.855 2.753 34.748 2.851 c
+34.638 2.958 34.476 3.013 34.264 3.013 c
+34.065 3.013 33.903 2.955 33.778 2.837 c
+33.661 2.72 33.602 2.572 33.602 2.396 c
+32.955 2.396 l
+32.955 2.591 33.014 2.782 33.132 2.969 c
+33.256 3.152 33.418 3.3 33.616 3.41 c
+33.822 3.516 34.05 3.572 34.308 3.572 c
+34.708 3.572 35.013 3.468 35.218 3.263 c
+35.432 3.057 35.546 2.763 35.557 2.381 c
+35.557 0.368 l
+35.557 0.062 35.594 -0.202 35.675 -0.426 c
+35.675 -0.485 l
+h
+34.131 0.029 m
+34.297 0.029 34.447 0.073 34.587 0.162 c
+34.734 0.25 34.84 0.36 34.91 0.5 c
+34.91 1.44 l
+34.543 1.44 l
+34.227 1.44 33.984 1.371 33.807 1.234 c
+33.631 1.106 33.543 0.918 33.543 0.676 c
+33.543 0.448 33.587 0.283 33.676 0.176 c
+33.764 0.077 33.915 0.029 34.131 0.029 c
+41.157 1.309 m
+41.157 0.69 41.044 0.224 40.82 -0.088 c
+40.602 -0.405 40.279 -0.559 39.849 -0.559 c
+39.426 -0.559 39.114 -0.378 38.908 -0.015 c
+38.879 -0.485 l
+38.276 -0.485 l
+38.276 5.159 l
+38.923 5.159 l
+38.923 3.057 l
+39.136 3.399 39.445 3.572 39.849 3.572 c
+40.279 3.572 40.602 3.414 40.82 3.102 c
+41.044 2.797 41.157 2.329 41.157 1.705 c
+h
+40.51 1.69 m
+40.51 2.161 40.441 2.491 40.305 2.69 c
+40.176 2.884 39.966 2.984 39.673 2.984 c
+39.338 2.984 39.089 2.8 38.923 2.44 c
+38.923 0.559 l
+39.089 0.195 39.342 0.015 39.687 0.015 c
+39.981 0.015 40.191 0.118 40.319 0.324 c
+40.444 0.529 40.51 0.845 40.51 1.278 c
+h
+43.642 2.881 m
+43.553 2.899 43.454 2.911 43.347 2.911 c
+43.013 2.911 42.778 2.726 42.642 2.367 c
+42.642 -0.485 l
+41.996 -0.485 l
+41.996 3.499 l
+42.627 3.499 l
+42.642 3.087 l
+42.819 3.41 43.06 3.572 43.377 3.572 c
+43.484 3.572 43.571 3.549 43.642 3.513 c
+h
+46.185 -0.485 m
+46.144 -0.397 46.118 -0.25 46.111 -0.044 c
+45.876 -0.389 45.582 -0.559 45.229 -0.559 c
+44.865 -0.559 44.583 -0.463 44.377 -0.264 c
+44.178 -0.059 44.082 0.228 44.082 0.603 c
+44.082 1.003 44.219 1.323 44.494 1.558 c
+44.766 1.801 45.14 1.926 45.611 1.926 c
+46.096 1.926 l
+46.096 2.352 l
+46.096 2.587 46.041 2.753 45.934 2.851 c
+45.824 2.958 45.663 3.013 45.449 3.013 c
+45.251 3.013 45.09 2.955 44.965 2.837 c
+44.847 2.72 44.788 2.572 44.788 2.396 c
+44.141 2.396 l
+44.141 2.591 44.2 2.782 44.317 2.969 c
+44.442 3.152 44.604 3.3 44.803 3.41 c
+45.009 3.516 45.236 3.572 45.493 3.572 c
+45.894 3.572 46.199 3.468 46.405 3.263 c
+46.618 3.057 46.732 2.763 46.743 2.381 c
+46.743 0.368 l
+46.743 0.062 46.78 -0.202 46.86 -0.426 c
+46.86 -0.485 l
+h
+45.317 0.029 m
+45.483 0.029 45.633 0.073 45.773 0.162 c
+45.919 0.25 46.027 0.36 46.096 0.5 c
+46.096 1.44 l
+45.728 1.44 l
+45.412 1.44 45.171 1.371 44.994 1.234 c
+44.818 1.106 44.729 0.918 44.729 0.676 c
+44.729 0.448 44.774 0.283 44.861 0.176 c
+44.949 0.077 45.1 0.029 45.317 0.029 c
+48.36 3.499 m
+48.375 3.057 l
+48.628 3.399 48.951 3.572 49.345 3.572 c
+50.05 3.572 50.407 3.102 50.418 2.161 c
+50.418 -0.485 l
+49.771 -0.485 l
+49.771 2.132 l
+49.771 2.444 49.716 2.664 49.609 2.793 c
+49.499 2.917 49.345 2.984 49.139 2.984 c
+48.981 2.984 48.834 2.928 48.698 2.822 c
+48.569 2.712 48.466 2.576 48.389 2.41 c
+48.389 -0.485 l
+47.743 -0.485 l
+47.743 3.499 l
+h
+52.77 0 m
+52.982 0 53.156 0.062 53.284 0.191 c
+53.42 0.327 53.493 0.518 53.505 0.764 c
+54.122 0.764 l
+54.1 0.382 53.964 0.062 53.711 -0.191 c
+53.453 -0.437 53.14 -0.559 52.77 -0.559 c
+52.277 -0.559 51.902 -0.408 51.638 -0.103 c
+51.38 0.21 51.255 0.676 51.255 1.294 c
+51.255 1.735 l
+51.255 2.329 51.38 2.786 51.638 3.102 c
+51.902 3.414 52.277 3.572 52.77 3.572 c
+53.17 3.572 53.49 3.439 53.725 3.175 c
+53.967 2.917 54.1 2.572 54.122 2.132 c
+53.505 2.132 l
+53.482 2.425 53.409 2.645 53.284 2.793 c
+53.167 2.94 52.994 3.013 52.77 3.013 c
+52.475 3.013 52.259 2.914 52.123 2.72 c
+51.983 2.531 51.91 2.223 51.902 1.793 c
+51.902 1.278 l
+51.902 0.808 51.968 0.474 52.109 0.279 c
+52.255 0.092 52.475 0 52.77 0 c
+55.519 3.087 m
+55.772 3.41 56.092 3.572 56.474 3.572 c
+57.179 3.572 57.536 3.102 57.547 2.161 c
+57.547 -0.485 l
+56.9 -0.485 l
+56.9 2.132 l
+56.9 2.444 56.845 2.664 56.738 2.793 c
+56.628 2.917 56.474 2.984 56.268 2.984 c
+56.109 2.984 55.963 2.928 55.827 2.822 c
+55.698 2.712 55.596 2.576 55.519 2.41 c
+55.519 -0.485 l
+54.871 -0.485 l
+54.871 5.159 l
+55.519 5.159 l
+h
+60.839 3.499 m
+60.854 3.057 l
+61.108 3.399 61.431 3.572 61.824 3.572 c
+62.53 3.572 62.887 3.102 62.897 2.161 c
+62.897 -0.485 l
+62.251 -0.485 l
+62.251 2.132 l
+62.251 2.444 62.195 2.664 62.089 2.793 c
+61.979 2.917 61.824 2.984 61.618 2.984 c
+61.46 2.984 61.314 2.928 61.177 2.822 c
+61.048 2.712 60.946 2.576 60.869 2.41 c
+60.869 -0.485 l
+60.222 -0.485 l
+60.222 3.499 l
+h
+65.91 -0.485 m
+65.87 -0.397 65.844 -0.25 65.837 -0.044 c
+65.602 -0.389 65.308 -0.559 64.955 -0.559 c
+64.591 -0.559 64.308 -0.463 64.103 -0.264 c
+63.904 -0.059 63.808 0.228 63.808 0.603 c
+63.808 1.003 63.945 1.323 64.221 1.558 c
+64.492 1.801 64.867 1.926 65.337 1.926 c
+65.823 1.926 l
+65.823 2.352 l
+65.823 2.587 65.767 2.753 65.661 2.851 c
+65.551 2.958 65.389 3.013 65.175 3.013 c
+64.977 3.013 64.815 2.955 64.691 2.837 c
+64.573 2.72 64.514 2.572 64.514 2.396 c
+63.868 2.396 l
+63.868 2.591 63.926 2.782 64.044 2.969 c
+64.169 3.152 64.331 3.3 64.529 3.41 c
+64.734 3.516 64.963 3.572 65.22 3.572 c
+65.62 3.572 65.925 3.468 66.131 3.263 c
+66.344 3.057 66.458 2.763 66.469 2.381 c
+66.469 0.368 l
+66.469 0.062 66.506 -0.202 66.586 -0.426 c
+66.586 -0.485 l
+h
+65.044 0.029 m
+65.208 0.029 65.36 0.073 65.499 0.162 c
+65.646 0.25 65.752 0.36 65.823 0.5 c
+65.823 1.44 l
+65.455 1.44 l
+65.139 1.44 64.896 1.371 64.72 1.234 c
+64.543 1.106 64.456 0.918 64.456 0.676 c
+64.456 0.448 64.499 0.283 64.587 0.176 c
+64.676 0.077 64.826 0.029 65.044 0.029 c
+68.086 3.499 m
+68.101 3.131 l
+68.343 3.425 68.663 3.572 69.056 3.572 c
+69.497 3.572 69.806 3.373 69.983 2.984 c
+70.236 3.373 70.585 3.572 71.026 3.572 c
+71.761 3.572 72.136 3.109 72.158 2.19 c
+72.158 -0.485 l
+71.511 -0.485 l
+71.511 2.132 l
+71.511 2.425 71.456 2.639 71.349 2.778 c
+71.25 2.914 71.078 2.984 70.835 2.984 c
+70.636 2.984 70.475 2.903 70.349 2.749 c
+70.232 2.602 70.162 2.41 70.143 2.175 c
+70.143 -0.485 l
+69.482 -0.485 l
+69.482 2.161 l
+69.482 2.708 69.262 2.984 68.821 2.984 c
+68.487 2.984 68.252 2.822 68.115 2.499 c
+68.115 -0.485 l
+67.469 -0.485 l
+67.469 3.499 l
+h
+74.554 -0.559 m
+74.054 -0.559 73.671 -0.412 73.407 -0.118 c
+73.142 0.176 73.01 0.61 73.01 1.191 c
+73.01 1.661 l
+73.01 2.256 73.135 2.722 73.393 3.057 c
+73.657 3.399 74.017 3.572 74.48 3.572 c
+74.939 3.572 75.281 3.418 75.509 3.116 c
+75.745 2.822 75.865 2.359 75.876 1.735 c
+75.876 1.309 l
+73.657 1.309 l
+73.657 1.22 l
+73.657 0.786 73.734 0.474 73.892 0.279 c
+74.058 0.092 74.289 0 74.583 0 c
+74.777 0 74.951 0.033 75.097 0.103 c
+75.244 0.18 75.381 0.297 75.509 0.455 c
+75.847 0.044 l
+75.56 -0.36 75.13 -0.559 74.554 -0.559 c
+74.48 3.013 m
+74.205 3.013 74.002 2.917 73.877 2.734 c
+73.748 2.547 73.675 2.256 73.657 1.866 c
+75.23 1.866 l
+75.23 1.955 l
+75.207 2.337 75.142 2.605 75.024 2.763 c
+74.906 2.928 74.723 3.013 74.48 3.013 c
+78.243 1.69 m
+78.243 2.267 78.379 2.722 78.654 3.057 c
+78.937 3.399 79.309 3.572 79.772 3.572 c
+80.231 3.572 80.599 3.403 80.874 3.072 c
+81.157 2.749 81.304 2.3 81.315 1.735 c
+81.315 1.309 l
+81.315 0.738 81.172 0.283 80.889 -0.059 c
+80.613 -0.393 80.246 -0.559 79.786 -0.559 c
+79.323 -0.559 78.952 -0.397 78.669 -0.073 c
+78.394 0.257 78.251 0.698 78.243 1.249 c
+h
+78.889 1.309 m
+78.889 0.904 78.967 0.588 79.125 0.353 c
+79.29 0.118 79.51 0 79.786 0 c
+80.352 0 80.647 0.411 80.668 1.234 c
+80.668 1.69 l
+80.668 2.091 80.584 2.41 80.418 2.645 c
+80.26 2.888 80.044 3.013 79.772 3.013 c
+79.508 3.013 79.29 2.888 79.125 2.645 c
+78.967 2.41 78.889 2.091 78.889 1.69 c
+h
+83.799 2.881 m
+83.711 2.899 83.612 2.911 83.506 2.911 c
+83.171 2.911 82.936 2.726 82.8 2.367 c
+82.8 -0.485 l
+82.153 -0.485 l
+82.153 3.499 l
+82.785 3.499 l
+82.8 3.087 l
+82.976 3.41 83.219 3.572 83.535 3.572 c
+83.641 3.572 83.73 3.549 83.799 3.513 c
+h
+88.209 -0.485 m
+88.169 -0.397 88.143 -0.25 88.135 -0.044 c
+87.9 -0.389 87.606 -0.559 87.254 -0.559 c
+86.89 -0.559 86.606 -0.463 86.401 -0.264 c
+86.203 -0.059 86.107 0.228 86.107 0.603 c
+86.107 1.003 86.243 1.323 86.519 1.558 c
+86.791 1.801 87.165 1.926 87.636 1.926 c
+88.121 1.926 l
+88.121 2.352 l
+88.121 2.587 88.065 2.753 87.959 2.851 c
+87.849 2.958 87.687 3.013 87.474 3.013 c
+87.275 3.013 87.113 2.955 86.989 2.837 c
+86.872 2.72 86.812 2.572 86.812 2.396 c
+86.166 2.396 l
+86.166 2.591 86.224 2.782 86.342 2.969 c
+86.467 3.152 86.629 3.3 86.827 3.41 c
+87.033 3.516 87.261 3.572 87.518 3.572 c
+87.919 3.572 88.223 3.468 88.43 3.263 c
+88.643 3.057 88.757 2.763 88.767 2.381 c
+88.767 0.368 l
+88.767 0.062 88.804 -0.202 88.885 -0.426 c
+88.885 -0.485 l
+h
+87.342 0.029 m
+87.507 0.029 87.658 0.073 87.797 0.162 c
+87.944 0.25 88.051 0.36 88.121 0.5 c
+88.121 1.44 l
+87.753 1.44 l
+87.437 1.44 87.194 1.371 87.018 1.234 c
+86.842 1.106 86.754 0.918 86.754 0.676 c
+86.754 0.448 86.798 0.283 86.886 0.176 c
+86.974 0.077 87.125 0.029 87.342 0.029 c
+92.339 4.453 m
+92.339 3.499 l
+92.942 3.499 l
+92.942 2.969 l
+92.339 2.969 l
+92.339 0.5 l
+92.339 0.341 92.362 0.224 92.413 0.147 c
+92.472 0.066 92.56 0.029 92.678 0.029 c
+92.765 0.029 92.854 0.044 92.942 0.073 c
+92.942 -0.485 l
+92.795 -0.532 92.641 -0.559 92.486 -0.559 c
+92.229 -0.559 92.034 -0.467 91.898 -0.279 c
+91.759 -0.096 91.693 0.166 91.693 0.5 c
+91.693 2.969 l
+91.09 2.969 l
+91.09 3.499 l
+91.693 3.499 l
+91.693 4.453 l
+h
+95.749 -0.485 m
+95.709 -0.397 95.684 -0.25 95.676 -0.044 c
+95.441 -0.389 95.147 -0.559 94.795 -0.559 c
+94.43 -0.559 94.147 -0.463 93.941 -0.264 c
+93.743 -0.059 93.648 0.228 93.648 0.603 c
+93.648 1.003 93.783 1.323 94.059 1.558 c
+94.331 1.801 94.706 1.926 95.176 1.926 c
+95.661 1.926 l
+95.661 2.352 l
+95.661 2.587 95.606 2.753 95.5 2.851 c
+95.389 2.958 95.228 3.013 95.015 3.013 c
+94.816 3.013 94.654 2.955 94.529 2.837 c
+94.412 2.72 94.353 2.572 94.353 2.396 c
+93.706 2.396 l
+93.706 2.591 93.765 2.782 93.883 2.969 c
+94.008 3.152 94.169 3.3 94.367 3.41 c
+94.573 3.516 94.801 3.572 95.059 3.572 c
+95.46 3.572 95.764 3.468 95.97 3.263 c
+96.183 3.057 96.297 2.763 96.308 2.381 c
+96.308 0.368 l
+96.308 0.062 96.345 -0.202 96.426 -0.426 c
+96.426 -0.485 l
+h
+94.882 0.029 m
+95.048 0.029 95.198 0.073 95.338 0.162 c
+95.485 0.25 95.591 0.36 95.661 0.5 c
+95.661 1.44 l
+95.294 1.44 l
+94.978 1.44 94.735 1.371 94.559 1.234 c
+94.383 1.106 94.294 0.918 94.294 0.676 c
+94.294 0.448 94.338 0.283 94.427 0.176 c
+94.515 0.077 94.666 0.029 94.882 0.029 c
+97.176 1.69 m
+97.176 2.308 97.286 2.77 97.513 3.087 c
+97.737 3.41 98.072 3.572 98.513 3.572 c
+98.913 3.572 99.219 3.395 99.424 3.042 c
+99.468 3.499 l
+100.056 3.499 l
+100.056 -0.53 l
+100.056 -1.018 99.928 -1.396 99.674 -1.661 c
+99.417 -1.926 99.065 -2.058 98.616 -2.058 c
+98.417 -2.058 98.197 -2.007 97.954 -1.911 c
+97.708 -1.812 97.528 -1.691 97.411 -1.544 c
+97.675 -1.103 l
+97.939 -1.367 98.238 -1.5 98.572 -1.5 c
+99.108 -1.5 99.383 -1.205 99.395 -0.617 c
+99.395 -0.088 l
+99.189 -0.405 98.888 -0.559 98.498 -0.559 c
+98.087 -0.559 97.764 -0.408 97.528 -0.103 c
+97.301 0.21 97.183 0.661 97.176 1.249 c
+h
+97.837 1.309 m
+97.837 0.867 97.899 0.536 98.028 0.324 c
+98.153 0.118 98.369 0.015 98.675 0.015 c
+98.998 0.015 99.237 0.18 99.395 0.515 c
+99.395 2.499 l
+99.226 2.822 98.987 2.984 98.675 2.984 c
+98.381 2.984 98.164 2.881 98.028 2.675 c
+97.899 2.469 97.837 2.146 97.837 1.705 c
+h
+103.349 3.499 m
+103.364 3.057 l
+103.617 3.399 103.94 3.572 104.334 3.572 c
+105.039 3.572 105.396 3.102 105.407 2.161 c
+105.407 -0.485 l
+104.76 -0.485 l
+104.76 2.132 l
+104.76 2.444 104.705 2.664 104.598 2.793 c
+104.488 2.917 104.334 2.984 104.128 2.984 c
+103.97 2.984 103.823 2.928 103.687 2.822 c
+103.558 2.712 103.456 2.576 103.379 2.41 c
+103.379 -0.485 l
+102.732 -0.485 l
+102.732 3.499 l
+h
+108.42 -0.485 m
+108.38 -0.397 108.354 -0.25 108.347 -0.044 c
+108.112 -0.389 107.817 -0.559 107.464 -0.559 c
+107.101 -0.559 106.818 -0.463 106.612 -0.264 c
+106.414 -0.059 106.319 0.228 106.319 0.603 c
+106.319 1.003 106.454 1.323 106.73 1.558 c
+107.002 1.801 107.377 1.926 107.847 1.926 c
+108.332 1.926 l
+108.332 2.352 l
+108.332 2.587 108.277 2.753 108.17 2.851 c
+108.06 2.958 107.898 3.013 107.686 3.013 c
+107.487 3.013 107.325 2.955 107.2 2.837 c
+107.082 2.72 107.024 2.572 107.024 2.396 c
+106.377 2.396 l
+106.377 2.591 106.436 2.782 106.554 2.969 c
+106.678 3.152 106.84 3.3 107.038 3.41 c
+107.244 3.516 107.472 3.572 107.73 3.572 c
+108.13 3.572 108.435 3.468 108.64 3.263 c
+108.854 3.057 108.968 2.763 108.979 2.381 c
+108.979 0.368 l
+108.979 0.062 109.016 -0.202 109.097 -0.426 c
+109.097 -0.485 l
+h
+107.553 0.029 m
+107.718 0.029 107.869 0.073 108.008 0.162 c
+108.156 0.25 108.262 0.36 108.332 0.5 c
+108.332 1.44 l
+107.965 1.44 l
+107.649 1.44 107.406 1.371 107.229 1.234 c
+107.053 1.106 106.965 0.918 106.965 0.676 c
+106.965 0.448 107.009 0.283 107.098 0.176 c
+107.185 0.077 107.336 0.029 107.553 0.029 c
+110.595 3.499 m
+110.61 3.131 l
+110.853 3.425 111.173 3.572 111.566 3.572 c
+112.006 3.572 112.316 3.373 112.492 2.984 c
+112.746 3.373 113.095 3.572 113.535 3.572 c
+114.271 3.572 114.645 3.109 114.668 2.19 c
+114.668 -0.485 l
+114.02 -0.485 l
+114.02 2.132 l
+114.02 2.425 113.965 2.639 113.859 2.778 c
+113.76 2.914 113.587 2.984 113.344 2.984 c
+113.146 2.984 112.984 2.903 112.86 2.749 c
+112.742 2.602 112.671 2.41 112.654 2.175 c
+112.654 -0.485 l
+111.992 -0.485 l
+111.992 2.161 l
+111.992 2.708 111.771 2.984 111.331 2.984 c
+110.996 2.984 110.761 2.822 110.625 2.499 c
+110.625 -0.485 l
+109.978 -0.485 l
+109.978 3.499 l
+h
+117.063 -0.559 m
+116.563 -0.559 116.181 -0.412 115.917 -0.118 c
+115.652 0.176 115.52 0.61 115.52 1.191 c
+115.52 1.661 l
+115.52 2.256 115.645 2.722 115.902 3.057 c
+116.166 3.399 116.527 3.572 116.989 3.572 c
+117.449 3.572 117.791 3.418 118.019 3.116 c
+118.254 2.822 118.375 2.359 118.386 1.735 c
+118.386 1.309 l
+116.166 1.309 l
+116.166 1.22 l
+116.166 0.786 116.243 0.474 116.401 0.279 c
+116.567 0.092 116.798 0 117.093 0 c
+117.288 0 117.46 0.033 117.607 0.103 c
+117.754 0.18 117.89 0.297 118.019 0.455 c
+118.356 0.044 l
+118.07 -0.36 117.64 -0.559 117.063 -0.559 c
+116.989 3.013 m
+116.714 3.013 116.512 2.917 116.387 2.734 c
+116.258 2.547 116.185 2.256 116.166 1.866 c
+117.739 1.866 l
+117.739 1.955 l
+117.718 2.337 117.651 2.605 117.533 2.763 c
+117.416 2.928 117.232 3.013 116.989 3.013 c
+119.21 -0.133 m
+119.21 -0.015 119.242 0.081 119.312 0.162 c
+119.378 0.239 119.481 0.279 119.621 0.279 c
+119.767 0.279 119.875 0.239 119.944 0.162 c
+120.021 0.081 120.062 -0.015 120.062 -0.133 c
+120.062 -0.243 120.021 -0.335 119.944 -0.412 c
+119.875 -0.489 119.767 -0.53 119.621 -0.53 c
+119.481 -0.53 119.378 -0.489 119.312 -0.412 c
+119.242 -0.335 119.21 -0.243 119.21 -0.133 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 373.53 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 366.6908 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.993 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.596 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.279 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+23.221 -0.646 m
+24.353 -0.646 l
+24.353 -1.264 l
+21.045 -1.264 l
+21.045 -0.646 l
+22.31 -0.646 l
+22.31 2.896 l
+21.384 2.896 l
+21.384 3.514 l
+23.221 3.514 l
+h
+28.384 0.485 m
+28.384 0.21 28.347 -0.04 28.281 -0.264 c
+28.212 -0.482 28.108 -0.669 27.973 -0.823 c
+27.833 -0.981 27.657 -1.103 27.443 -1.19 c
+27.227 -1.278 26.973 -1.323 26.679 -1.323 c
+26.404 -1.323 26.157 -1.278 25.944 -1.19 c
+25.739 -1.103 25.566 -0.981 25.43 -0.823 c
+25.29 -0.669 25.187 -0.482 25.121 -0.264 c
+25.051 -0.04 25.018 0.21 25.018 0.485 c
+25.018 0.738 25.047 0.974 25.106 1.191 c
+25.172 1.415 25.275 1.606 25.415 1.764 c
+25.55 1.929 25.727 2.058 25.944 2.146 c
+26.157 2.234 26.414 2.278 26.709 2.278 c
+27.021 2.278 27.281 2.234 27.487 2.146 c
+27.701 2.058 27.873 1.929 28.002 1.764 c
+28.138 1.606 28.237 1.415 28.296 1.191 c
+28.355 0.974 28.384 0.738 28.384 0.485 c
+27.429 0.485 m
+27.429 0.691 27.414 0.867 27.385 1.014 c
+27.362 1.162 27.326 1.282 27.267 1.382 c
+27.208 1.477 27.135 1.547 27.046 1.588 c
+26.959 1.635 26.848 1.661 26.724 1.661 c
+26.458 1.661 26.267 1.562 26.15 1.367 c
+26.032 1.18 25.974 0.886 25.974 0.485 c
+25.974 0.063 26.032 -0.243 26.15 -0.426 c
+26.267 -0.613 26.444 -0.706 26.679 -0.706 c
+26.804 -0.706 26.918 -0.687 27.017 -0.646 c
+27.113 -0.599 27.194 -0.525 27.252 -0.426 c
+27.318 -0.33 27.362 -0.206 27.385 -0.058 c
+27.414 0.088 27.429 0.268 27.429 0.485 c
+30.769 -2.66 m
+30.552 -2.66 30.361 -2.635 30.196 -2.587 c
+30.027 -2.547 29.887 -2.484 29.77 -2.396 c
+29.652 -2.315 29.552 -2.219 29.475 -2.102 c
+29.406 -1.984 29.358 -1.855 29.328 -1.72 c
+30.225 -1.617 l
+30.262 -1.753 30.331 -1.859 30.431 -1.94 c
+30.526 -2.028 30.651 -2.072 30.799 -2.072 c
+30.886 -2.072 30.967 -2.057 31.048 -2.028 c
+31.125 -1.999 31.195 -1.944 31.254 -1.866 c
+31.313 -1.797 31.357 -1.705 31.387 -1.587 c
+31.424 -1.469 31.445 -1.323 31.445 -1.147 c
+31.445 -0.955 l
+31.445 -0.889 31.449 -0.831 31.46 -0.779 c
+31.46 -0.588 l
+31.445 -0.588 l
+31.346 -0.816 31.202 -0.977 31.019 -1.072 c
+30.832 -1.172 30.626 -1.22 30.402 -1.22 c
+30.196 -1.22 30.012 -1.183 29.858 -1.103 c
+29.71 -1.014 29.582 -0.897 29.475 -0.75 c
+29.376 -0.596 29.303 -0.411 29.255 -0.206 c
+29.203 0.008 29.182 0.243 29.182 0.5 c
+29.182 0.771 29.203 1.018 29.255 1.235 c
+29.313 1.448 29.394 1.632 29.505 1.779 c
+29.612 1.933 29.743 2.051 29.901 2.132 c
+30.056 2.22 30.244 2.263 30.46 2.263 c
+30.556 2.263 30.655 2.253 30.755 2.234 c
+30.85 2.213 30.938 2.18 31.019 2.132 c
+31.107 2.08 31.185 2.018 31.254 1.941 c
+31.331 1.86 31.393 1.768 31.445 1.661 c
+31.46 1.661 l
+31.46 1.808 l
+31.467 1.867 31.474 1.918 31.474 1.97 c
+31.482 2.028 31.489 2.076 31.489 2.117 c
+31.497 2.165 31.507 2.198 31.519 2.22 c
+32.371 2.22 l
+32.36 2.139 32.349 2.028 32.342 1.881 c
+32.342 1.411 l
+32.342 -1.161 l
+32.342 -1.415 32.305 -1.635 32.239 -1.822 c
+32.17 -2.007 32.066 -2.161 31.931 -2.279 c
+31.79 -2.404 31.625 -2.499 31.43 -2.558 c
+31.232 -2.624 31.011 -2.66 30.769 -2.66 c
+31.46 0.53 m
+31.46 0.742 31.434 0.919 31.387 1.058 c
+31.346 1.205 31.291 1.323 31.225 1.411 c
+31.166 1.5 31.096 1.558 31.019 1.588 c
+30.938 1.625 30.861 1.646 30.784 1.646 c
+30.684 1.646 30.593 1.621 30.504 1.573 c
+30.424 1.532 30.358 1.463 30.298 1.367 c
+30.248 1.279 30.203 1.162 30.167 1.014 c
+30.137 0.875 30.122 0.706 30.122 0.5 c
+30.122 0.125 30.181 -0.154 30.298 -0.338 c
+30.416 -0.515 30.578 -0.602 30.784 -0.602 c
+30.85 -0.602 30.923 -0.588 31.004 -0.559 c
+31.092 -0.522 31.166 -0.463 31.225 -0.382 c
+31.291 -0.294 31.346 -0.176 31.387 -0.029 c
+31.434 0.118 31.46 0.301 31.46 0.53 c
+38.424 -0.279 0.926 -0.985 re
+38.424 -1.264 m
+42.484 -0.279 0.927 -0.985 re
+42.484 -1.264 m
+48.5 1.47 m
+48.4 1.477 48.298 1.488 48.191 1.5 c
+48.081 1.517 47.96 1.529 47.823 1.529 c
+47.648 1.529 47.489 1.488 47.353 1.411 c
+47.214 1.341 47.096 1.242 47 1.118 c
+46.912 0.989 46.842 0.842 46.794 0.676 c
+46.754 0.507 46.736 0.331 46.736 0.147 c
+46.736 -1.264 l
+45.839 -1.264 l
+45.839 0.985 l
+45.839 1.11 45.828 1.235 45.81 1.353 c
+45.799 1.477 45.784 1.595 45.766 1.706 c
+45.755 1.823 45.74 1.918 45.722 1.999 c
+45.699 2.087 45.681 2.161 45.663 2.22 c
+46.545 2.22 l
+46.552 2.168 46.563 2.117 46.574 2.058 c
+46.593 1.999 46.607 1.933 46.618 1.867 c
+46.637 1.808 46.651 1.742 46.663 1.675 c
+46.67 1.606 46.68 1.544 46.692 1.484 c
+46.707 1.484 l
+46.743 1.602 46.794 1.709 46.854 1.808 c
+46.919 1.904 47 1.988 47.089 2.058 c
+47.177 2.124 47.28 2.18 47.397 2.22 c
+47.522 2.257 47.669 2.278 47.838 2.278 c
+47.964 2.278 48.081 2.271 48.191 2.263 c
+48.309 2.253 48.411 2.238 48.5 2.22 c
+h
+51.106 -1.323 m
+50.848 -1.323 50.62 -1.286 50.414 -1.22 c
+50.208 -1.143 50.032 -1.029 49.886 -0.882 c
+49.738 -0.727 49.62 -0.536 49.533 -0.309 c
+49.452 -0.085 49.415 0.181 49.415 0.485 c
+49.415 0.816 49.459 1.095 49.547 1.323 c
+49.643 1.558 49.772 1.742 49.93 1.881 c
+50.094 2.018 50.282 2.117 50.488 2.176 c
+50.694 2.242 50.903 2.278 51.12 2.278 c
+51.392 2.278 51.627 2.227 51.825 2.132 c
+52.031 2.043 52.197 1.911 52.325 1.735 c
+52.461 1.565 52.56 1.359 52.619 1.118 c
+52.685 0.882 52.722 0.617 52.722 0.324 c
+52.722 0.309 l
+50.356 0.309 l
+50.356 0.162 50.37 0.023 50.4 -0.103 c
+50.437 -0.231 50.491 -0.345 50.561 -0.441 c
+50.628 -0.529 50.712 -0.599 50.811 -0.646 c
+50.907 -0.698 51.021 -0.72 51.149 -0.72 c
+51.303 -0.72 51.443 -0.687 51.561 -0.617 c
+51.686 -0.551 51.774 -0.448 51.825 -0.309 c
+52.664 -0.382 l
+52.633 -0.482 52.579 -0.588 52.502 -0.706 c
+52.421 -0.816 52.318 -0.918 52.193 -1.014 c
+52.076 -1.103 51.921 -1.176 51.737 -1.234 c
+51.561 -1.294 51.347 -1.323 51.106 -1.323 c
+51.106 1.706 m
+51.017 1.706 50.929 1.69 50.84 1.661 c
+50.752 1.632 50.672 1.58 50.605 1.515 c
+50.536 1.444 50.477 1.357 50.429 1.249 c
+50.389 1.139 50.37 1.014 50.37 0.867 c
+51.84 0.867 l
+51.84 1.004 51.814 1.124 51.767 1.235 c
+51.727 1.341 51.671 1.43 51.605 1.5 c
+51.546 1.565 51.473 1.617 51.384 1.646 c
+51.297 1.683 51.201 1.706 51.106 1.706 c
+55.258 1.602 m
+55.258 -1.264 l
+54.361 -1.264 l
+54.361 1.602 l
+53.537 1.602 l
+53.537 2.22 l
+54.361 2.22 l
+54.361 2.484 l
+54.361 2.61 54.376 2.741 54.405 2.881 c
+54.442 3.017 54.512 3.135 54.611 3.234 c
+54.717 3.341 54.861 3.429 55.037 3.499 c
+55.214 3.564 55.438 3.601 55.713 3.601 c
+55.926 3.601 56.125 3.591 56.301 3.572 c
+56.477 3.55 56.629 3.532 56.757 3.514 c
+56.757 2.926 l
+56.629 2.944 56.485 2.959 56.331 2.969 c
+56.172 2.976 56.022 2.984 55.875 2.984 c
+55.746 2.984 55.644 2.969 55.567 2.94 c
+55.486 2.911 55.423 2.87 55.376 2.822 c
+55.324 2.77 55.291 2.708 55.272 2.631 c
+55.261 2.562 55.258 2.484 55.258 2.396 c
+55.258 2.22 l
+56.683 2.22 l
+56.683 1.602 l
+h
+f
+Q
+q 1 0 0 1 60.3663 351.1103 cm
+0 0 m
+2.102 0 l
+2.102 -0.574 l
+-0.676 -0.574 l
+-0.676 4.777 l
+0 4.777 l
+h
+3.513 -0.574 -0.647 3.984 re
+3.557 4.453 m
+3.557 4.343 3.528 4.251 3.469 4.174 c
+3.41 4.104 3.314 4.072 3.189 4.072 c
+3.072 4.072 2.977 4.104 2.91 4.174 c
+2.851 4.251 2.822 4.343 2.822 4.453 c
+2.822 4.571 2.851 4.663 2.91 4.733 c
+2.977 4.81 3.072 4.85 3.189 4.85 c
+3.314 4.85 3.41 4.81 3.469 4.733 c
+3.528 4.652 3.557 4.56 3.557 4.453 c
+6.468 0.44 m
+6.468 0.588 6.412 0.709 6.306 0.808 c
+6.196 0.904 5.99 1.022 5.688 1.161 c
+5.342 1.308 5.1 1.429 4.953 1.529 c
+4.806 1.635 4.696 1.753 4.629 1.881 c
+4.56 2.006 4.527 2.164 4.527 2.352 c
+4.527 2.674 4.644 2.944 4.88 3.16 c
+5.115 3.373 5.416 3.484 5.791 3.484 c
+6.173 3.484 6.482 3.37 6.717 3.145 c
+6.952 2.917 7.07 2.631 7.07 2.278 c
+6.423 2.278 l
+6.423 2.454 6.364 2.605 6.247 2.734 c
+6.129 2.859 5.975 2.925 5.791 2.925 c
+5.593 2.925 5.442 2.869 5.335 2.763 c
+5.225 2.664 5.174 2.532 5.174 2.366 c
+5.174 2.238 5.211 2.131 5.292 2.043 c
+5.369 1.962 5.56 1.859 5.865 1.734 c
+6.342 1.547 6.673 1.359 6.85 1.176 c
+7.025 0.999 7.114 0.771 7.114 0.5 c
+7.114 0.147 6.989 -0.133 6.746 -0.339 c
+6.511 -0.544 6.196 -0.647 5.805 -0.647 c
+5.383 -0.647 5.045 -0.53 4.791 -0.294 c
+4.534 -0.052 4.409 0.253 4.409 0.617 c
+5.056 0.617 l
+5.063 0.389 5.134 0.213 5.262 0.087 c
+5.387 -0.03 5.57 -0.088 5.805 -0.088 c
+6.019 -0.088 6.181 -0.04 6.291 0.058 c
+6.408 0.154 6.468 0.282 6.468 0.44 c
+8.804 4.365 m
+8.804 3.41 l
+9.407 3.41 l
+9.407 2.881 l
+8.804 2.881 l
+8.804 0.411 l
+8.804 0.253 8.826 0.135 8.878 0.058 c
+8.937 -0.023 9.025 -0.059 9.142 -0.059 c
+9.231 -0.059 9.319 -0.044 9.407 -0.015 c
+9.407 -0.574 l
+9.26 -0.621 9.105 -0.647 8.951 -0.647 c
+8.694 -0.647 8.5 -0.555 8.363 -0.368 c
+8.224 -0.184 8.157 0.077 8.157 0.411 c
+8.157 2.881 l
+7.555 2.881 l
+7.555 3.41 l
+8.157 3.41 l
+8.157 4.365 l
+h
+13.273 -0.088 m
+13.486 -0.088 13.659 -0.026 13.787 0.103 c
+13.923 0.239 13.996 0.43 14.008 0.675 c
+14.625 0.675 l
+14.603 0.294 14.467 -0.026 14.214 -0.279 c
+13.957 -0.526 13.644 -0.647 13.273 -0.647 c
+12.781 -0.647 12.406 -0.497 12.141 -0.191 c
+11.883 0.121 11.759 0.588 11.759 1.205 c
+11.759 1.646 l
+11.759 2.241 11.883 2.697 12.141 3.013 c
+12.406 3.326 12.781 3.484 13.273 3.484 c
+13.674 3.484 13.993 3.351 14.229 3.087 c
+14.47 2.829 14.603 2.484 14.625 2.043 c
+14.008 2.043 l
+13.986 2.337 13.913 2.557 13.787 2.705 c
+13.67 2.851 13.497 2.925 13.273 2.925 c
+12.978 2.925 12.762 2.826 12.626 2.631 c
+12.486 2.443 12.413 2.135 12.406 1.705 c
+12.406 1.19 l
+12.406 0.72 12.471 0.386 12.612 0.191 c
+12.758 0.004 12.978 -0.088 13.273 -0.088 c
+15.243 1.602 m
+15.243 2.179 15.378 2.634 15.654 2.969 c
+15.937 3.31 16.309 3.484 16.771 3.484 c
+17.231 3.484 17.598 3.314 17.874 2.984 c
+18.156 2.66 18.304 2.212 18.314 1.646 c
+18.314 1.22 l
+18.314 0.65 18.171 0.195 17.888 -0.148 c
+17.613 -0.482 17.246 -0.647 16.786 -0.647 c
+16.323 -0.647 15.952 -0.485 15.669 -0.162 c
+15.393 0.168 15.249 0.61 15.243 1.161 c
+h
+15.889 1.22 m
+15.889 0.816 15.966 0.5 16.124 0.264 c
+16.29 0.029 16.51 -0.088 16.786 -0.088 c
+17.352 -0.088 17.645 0.323 17.668 1.146 c
+17.668 1.602 l
+17.668 2.003 17.583 2.322 17.418 2.557 c
+17.26 2.8 17.043 2.925 16.771 2.925 c
+16.506 2.925 16.29 2.8 16.124 2.557 c
+15.966 2.322 15.889 2.003 15.889 1.602 c
+h
+19.77 3.41 m
+19.785 3.042 l
+20.027 3.337 20.346 3.484 20.74 3.484 c
+21.181 3.484 21.489 3.285 21.666 2.896 c
+21.919 3.285 22.268 3.484 22.709 3.484 c
+23.444 3.484 23.82 3.021 23.841 2.102 c
+23.841 -0.574 l
+23.195 -0.574 l
+23.195 2.043 l
+23.195 2.337 23.139 2.55 23.033 2.69 c
+22.933 2.826 22.761 2.896 22.518 2.896 c
+22.32 2.896 22.158 2.815 22.033 2.66 c
+21.915 2.514 21.846 2.322 21.828 2.087 c
+21.828 -0.574 l
+21.167 -0.574 l
+21.167 2.072 l
+21.167 2.62 20.946 2.896 20.504 2.896 c
+20.17 2.896 19.935 2.734 19.799 2.41 c
+19.799 -0.574 l
+19.153 -0.574 l
+19.153 3.41 l
+h
+25.429 3.41 m
+25.443 3.042 l
+25.686 3.337 26.006 3.484 26.399 3.484 c
+26.84 3.484 27.149 3.285 27.325 2.896 c
+27.579 3.285 27.928 3.484 28.369 3.484 c
+29.104 3.484 29.479 3.021 29.501 2.102 c
+29.501 -0.574 l
+28.853 -0.574 l
+28.853 2.043 l
+28.853 2.337 28.799 2.55 28.693 2.69 c
+28.593 2.826 28.42 2.896 28.178 2.896 c
+27.979 2.896 27.818 2.815 27.693 2.66 c
+27.575 2.514 27.505 2.322 27.487 2.087 c
+27.487 -0.574 l
+26.825 -0.574 l
+26.825 2.072 l
+26.825 2.62 26.605 2.896 26.164 2.896 c
+25.83 2.896 25.595 2.734 25.458 2.41 c
+25.458 -0.574 l
+24.812 -0.574 l
+24.812 3.41 l
+h
+31.176 -0.574 -0.646 3.984 re
+31.22 4.453 m
+31.22 4.343 31.191 4.251 31.132 4.174 c
+31.074 4.104 30.978 4.072 30.852 4.072 c
+30.735 4.072 30.64 4.104 30.574 4.174 c
+30.515 4.251 30.486 4.343 30.486 4.453 c
+30.486 4.571 30.515 4.663 30.574 4.733 c
+30.64 4.81 30.735 4.85 30.852 4.85 c
+30.978 4.85 31.074 4.81 31.132 4.733 c
+31.191 4.652 31.22 4.56 31.22 4.453 c
+33.043 4.365 m
+33.043 3.41 l
+33.646 3.41 l
+33.646 2.881 l
+33.043 2.881 l
+33.043 0.411 l
+33.043 0.253 33.065 0.135 33.117 0.058 c
+33.175 -0.023 33.264 -0.059 33.381 -0.059 c
+33.47 -0.059 33.557 -0.044 33.646 -0.015 c
+33.646 -0.574 l
+33.499 -0.621 33.344 -0.647 33.19 -0.647 c
+32.932 -0.647 32.738 -0.555 32.602 -0.368 c
+32.462 -0.184 32.396 0.077 32.396 0.411 c
+32.396 2.881 l
+31.793 2.881 l
+31.793 3.41 l
+32.396 3.41 l
+32.396 4.365 l
+h
+34.424 -1.646 m
+34.027 -1.382 l
+34.263 -1.058 34.384 -0.724 34.395 -0.383 c
+34.395 0.235 l
+35.057 0.235 l
+35.057 -0.294 l
+35.057 -0.551 34.991 -0.798 34.866 -1.044 c
+34.748 -1.287 34.601 -1.488 34.424 -1.646 c
+38.467 4.365 m
+38.467 3.41 l
+39.07 3.41 l
+39.07 2.881 l
+38.467 2.881 l
+38.467 0.411 l
+38.467 0.253 38.489 0.135 38.54 0.058 c
+38.599 -0.023 38.688 -0.059 38.805 -0.059 c
+38.893 -0.059 38.981 -0.044 39.07 -0.015 c
+39.07 -0.574 l
+38.923 -0.621 38.768 -0.647 38.614 -0.647 c
+38.357 -0.647 38.162 -0.555 38.026 -0.368 c
+37.886 -0.184 37.821 0.077 37.821 0.411 c
+37.821 2.881 l
+37.218 2.881 l
+37.218 3.41 l
+37.821 3.41 l
+37.821 4.365 l
+h
+40.481 2.998 m
+40.734 3.322 41.054 3.484 41.436 3.484 c
+42.141 3.484 42.498 3.013 42.509 2.072 c
+42.509 -0.574 l
+41.863 -0.574 l
+41.863 2.043 l
+41.863 2.356 41.807 2.576 41.701 2.705 c
+41.59 2.829 41.436 2.896 41.231 2.896 c
+41.073 2.896 40.925 2.84 40.79 2.734 c
+40.661 2.624 40.558 2.487 40.481 2.322 c
+40.481 -0.574 l
+39.834 -0.574 l
+39.834 5.071 l
+40.481 5.071 l
+h
+45.508 -0.574 m
+45.468 -0.485 45.441 -0.339 45.434 -0.133 c
+45.199 -0.478 44.905 -0.647 44.552 -0.647 c
+44.188 -0.647 43.905 -0.551 43.7 -0.353 c
+43.502 -0.148 43.406 0.139 43.406 0.515 c
+43.406 0.914 43.542 1.234 43.818 1.469 c
+44.09 1.712 44.464 1.837 44.934 1.837 c
+45.42 1.837 l
+45.42 2.263 l
+45.42 2.499 45.364 2.664 45.258 2.763 c
+45.148 2.869 44.986 2.925 44.773 2.925 c
+44.574 2.925 44.412 2.866 44.288 2.749 c
+44.171 2.631 44.111 2.484 44.111 2.308 c
+43.465 2.308 l
+43.465 2.502 43.523 2.693 43.641 2.881 c
+43.766 3.064 43.928 3.212 44.126 3.322 c
+44.332 3.428 44.56 3.484 44.817 3.484 c
+45.218 3.484 45.522 3.38 45.728 3.175 c
+45.942 2.969 46.056 2.674 46.066 2.293 c
+46.066 0.279 l
+46.066 -0.026 46.103 -0.291 46.184 -0.515 c
+46.184 -0.574 l
+h
+44.641 -0.059 m
+44.806 -0.059 44.957 -0.015 45.096 0.073 c
+45.243 0.162 45.35 0.272 45.42 0.411 c
+45.42 1.352 l
+45.052 1.352 l
+44.736 1.352 44.493 1.282 44.317 1.146 c
+44.14 1.018 44.053 0.83 44.053 0.588 c
+44.053 0.359 44.097 0.195 44.185 0.087 c
+44.273 -0.011 44.424 -0.059 44.641 -0.059 c
+47.918 4.365 m
+47.918 3.41 l
+48.521 3.41 l
+48.521 2.881 l
+47.918 2.881 l
+47.918 0.411 l
+47.918 0.253 47.94 0.135 47.992 0.058 c
+48.051 -0.023 48.139 -0.059 48.256 -0.059 c
+48.345 -0.059 48.433 -0.044 48.521 -0.015 c
+48.521 -0.574 l
+48.374 -0.621 48.219 -0.647 48.065 -0.647 c
+47.808 -0.647 47.614 -0.555 47.477 -0.368 c
+47.338 -0.184 47.272 0.077 47.272 0.411 c
+47.272 2.881 l
+46.669 2.881 l
+46.669 3.41 l
+47.272 3.41 l
+47.272 4.365 l
+h
+53.048 -0.574 m
+53.008 -0.485 52.982 -0.339 52.975 -0.133 c
+52.74 -0.478 52.446 -0.647 52.093 -0.647 c
+51.729 -0.647 51.446 -0.551 51.24 -0.353 c
+51.042 -0.148 50.947 0.139 50.947 0.515 c
+50.947 0.914 51.082 1.234 51.358 1.469 c
+51.63 1.712 52.005 1.837 52.475 1.837 c
+52.96 1.837 l
+52.96 2.263 l
+52.96 2.499 52.905 2.664 52.799 2.763 c
+52.688 2.869 52.527 2.925 52.314 2.925 c
+52.115 2.925 51.953 2.866 51.828 2.749 c
+51.711 2.631 51.652 2.484 51.652 2.308 c
+51.005 2.308 l
+51.005 2.502 51.064 2.693 51.182 2.881 c
+51.307 3.064 51.468 3.212 51.666 3.322 c
+51.872 3.428 52.1 3.484 52.358 3.484 c
+52.759 3.484 53.063 3.38 53.269 3.175 c
+53.482 2.969 53.596 2.674 53.607 2.293 c
+53.607 0.279 l
+53.607 -0.026 53.644 -0.291 53.725 -0.515 c
+53.725 -0.574 l
+h
+52.181 -0.059 m
+52.346 -0.059 52.497 -0.015 52.637 0.073 c
+52.784 0.162 52.89 0.272 52.96 0.411 c
+52.96 1.352 l
+52.593 1.352 l
+52.277 1.352 52.034 1.282 51.858 1.146 c
+51.681 1.018 51.593 0.83 51.593 0.588 c
+51.593 0.359 51.637 0.195 51.726 0.087 c
+51.814 -0.011 51.965 -0.059 52.181 -0.059 c
+56.252 2.792 m
+56.165 2.811 56.065 2.822 55.959 2.822 c
+55.624 2.822 55.389 2.638 55.253 2.278 c
+55.253 -0.574 l
+54.606 -0.574 l
+54.606 3.41 l
+55.238 3.41 l
+55.253 2.998 l
+55.429 3.322 55.672 3.484 55.988 3.484 c
+56.094 3.484 56.183 3.461 56.252 3.424 c
+h
+58.252 -0.647 m
+57.752 -0.647 57.37 -0.5 57.106 -0.206 c
+56.84 0.087 56.709 0.521 56.709 1.103 c
+56.709 1.573 l
+56.709 2.167 56.834 2.634 57.091 2.969 c
+57.355 3.31 57.715 3.484 58.178 3.484 c
+58.638 3.484 58.98 3.329 59.207 3.027 c
+59.443 2.734 59.564 2.271 59.575 1.646 c
+59.575 1.22 l
+57.355 1.22 l
+57.355 1.132 l
+57.355 0.698 57.432 0.386 57.59 0.191 c
+57.756 0.004 57.987 -0.088 58.282 -0.088 c
+58.476 -0.088 58.649 -0.056 58.796 0.014 c
+58.943 0.091 59.078 0.209 59.207 0.367 c
+59.545 -0.044 l
+59.259 -0.449 58.829 -0.647 58.252 -0.647 c
+58.178 2.925 m
+57.903 2.925 57.701 2.829 57.576 2.645 c
+57.447 2.458 57.374 2.167 57.355 1.778 c
+58.928 1.778 l
+58.928 1.866 l
+58.906 2.248 58.839 2.516 58.722 2.674 c
+58.604 2.84 58.421 2.925 58.178 2.925 c
+64.954 1.22 m
+64.954 0.592 64.837 0.121 64.602 -0.191 c
+64.374 -0.497 64.058 -0.647 63.647 -0.647 c
+63.242 -0.647 62.934 -0.497 62.72 -0.191 c
+62.72 -2.103 l
+62.074 -2.103 l
+62.074 3.41 l
+62.662 3.41 l
+62.706 2.969 l
+62.919 3.31 63.227 3.484 63.632 3.484 c
+64.073 3.484 64.4 3.329 64.617 3.027 c
+64.83 2.722 64.944 2.267 64.954 1.66 c
+h
+64.308 1.602 m
+64.308 2.043 64.238 2.366 64.102 2.572 c
+63.963 2.786 63.742 2.896 63.441 2.896 c
+63.125 2.896 62.886 2.741 62.72 2.439 c
+62.72 0.367 l
+62.886 0.062 63.125 -0.088 63.441 -0.088 c
+63.735 -0.088 63.948 0.014 64.088 0.22 c
+64.223 0.434 64.297 0.764 64.308 1.205 c
+h
+67.439 2.792 m
+67.35 2.811 67.252 2.822 67.145 2.822 c
+66.81 2.822 66.575 2.638 66.44 2.278 c
+66.44 -0.574 l
+65.792 -0.574 l
+65.792 3.41 l
+66.425 3.41 l
+66.44 2.998 l
+66.616 3.322 66.858 3.484 67.174 3.484 c
+67.281 3.484 67.369 3.461 67.439 3.424 c
+h
+69.438 -0.647 m
+68.938 -0.647 68.556 -0.5 68.291 -0.206 c
+68.027 0.087 67.894 0.521 67.894 1.103 c
+67.894 1.573 l
+67.894 2.167 68.019 2.634 68.277 2.969 c
+68.541 3.31 68.902 3.484 69.364 3.484 c
+69.824 3.484 70.166 3.329 70.394 3.027 c
+70.629 2.734 70.75 2.271 70.761 1.646 c
+70.761 1.22 l
+68.541 1.22 l
+68.541 1.132 l
+68.541 0.698 68.618 0.386 68.777 0.191 c
+68.942 0.004 69.173 -0.088 69.467 -0.088 c
+69.662 -0.088 69.835 -0.056 69.982 0.014 c
+70.128 0.091 70.265 0.209 70.394 0.367 c
+70.731 -0.044 l
+70.444 -0.449 70.015 -0.647 69.438 -0.647 c
+69.364 2.925 m
+69.089 2.925 68.887 2.829 68.761 2.645 c
+68.634 2.458 68.559 2.167 68.541 1.778 c
+70.114 1.778 l
+70.114 1.866 l
+70.092 2.248 70.026 2.516 69.908 2.674 c
+69.791 2.84 69.607 2.925 69.364 2.925 c
+73.48 0.44 m
+73.48 0.588 73.425 0.709 73.318 0.808 c
+73.208 0.904 73.002 1.022 72.701 1.161 c
+72.356 1.308 72.113 1.429 71.967 1.529 c
+71.819 1.635 71.709 1.753 71.643 1.881 c
+71.573 2.006 71.539 2.164 71.539 2.352 c
+71.539 2.674 71.657 2.944 71.892 3.16 c
+72.127 3.373 72.429 3.484 72.804 3.484 c
+73.186 3.484 73.495 3.37 73.73 3.145 c
+73.966 2.917 74.083 2.631 74.083 2.278 c
+73.436 2.278 l
+73.436 2.454 73.378 2.605 73.26 2.734 c
+73.142 2.859 72.988 2.925 72.804 2.925 c
+72.605 2.925 72.455 2.869 72.348 2.763 c
+72.238 2.664 72.187 2.532 72.187 2.366 c
+72.187 2.238 72.223 2.131 72.304 2.043 c
+72.381 1.962 72.572 1.859 72.877 1.734 c
+73.355 1.547 73.686 1.359 73.862 1.176 c
+74.039 0.999 74.126 0.771 74.126 0.5 c
+74.126 0.147 74.002 -0.133 73.76 -0.339 c
+73.524 -0.544 73.208 -0.647 72.819 -0.647 c
+72.396 -0.647 72.058 -0.53 71.805 -0.294 c
+71.547 -0.052 71.422 0.253 71.422 0.617 c
+72.069 0.617 l
+72.077 0.389 72.146 0.213 72.275 0.087 c
+72.399 -0.03 72.584 -0.088 72.819 -0.088 c
+73.031 -0.088 73.193 -0.04 73.303 0.058 c
+73.421 0.154 73.48 0.282 73.48 0.44 c
+76.391 -0.647 m
+75.89 -0.647 75.508 -0.5 75.244 -0.206 c
+74.98 0.087 74.847 0.521 74.847 1.103 c
+74.847 1.573 l
+74.847 2.167 74.972 2.634 75.229 2.969 c
+75.494 3.31 75.854 3.484 76.317 3.484 c
+76.777 3.484 77.118 3.329 77.346 3.027 c
+77.581 2.734 77.702 2.271 77.714 1.646 c
+77.714 1.22 l
+75.494 1.22 l
+75.494 1.132 l
+75.494 0.698 75.571 0.386 75.73 0.191 c
+75.894 0.004 76.126 -0.088 76.42 -0.088 c
+76.615 -0.088 76.788 -0.056 76.935 0.014 c
+77.081 0.091 77.218 0.209 77.346 0.367 c
+77.684 -0.044 l
+77.397 -0.449 76.968 -0.647 76.391 -0.647 c
+76.317 2.925 m
+76.042 2.925 75.84 2.829 75.714 2.645 c
+75.586 2.458 75.512 2.167 75.494 1.778 c
+77.066 1.778 l
+77.066 1.866 l
+77.045 2.248 76.979 2.516 76.861 2.674 c
+76.744 2.84 76.559 2.925 76.317 2.925 c
+79.11 3.41 m
+79.125 2.969 l
+79.378 3.31 79.701 3.484 80.095 3.484 c
+80.8 3.484 81.157 3.013 81.168 2.072 c
+81.168 -0.574 l
+80.521 -0.574 l
+80.521 2.043 l
+80.521 2.356 80.466 2.576 80.359 2.705 c
+80.249 2.829 80.095 2.896 79.889 2.896 c
+79.73 2.896 79.584 2.84 79.448 2.734 c
+79.319 2.624 79.217 2.487 79.14 2.322 c
+79.14 -0.574 l
+78.492 -0.574 l
+78.492 3.41 l
+h
+82.99 4.365 m
+82.99 3.41 l
+83.593 3.41 l
+83.593 2.881 l
+82.99 2.881 l
+82.99 0.411 l
+82.99 0.253 83.013 0.135 83.064 0.058 c
+83.123 -0.023 83.211 -0.059 83.329 -0.059 c
+83.416 -0.059 83.505 -0.044 83.593 -0.015 c
+83.593 -0.574 l
+83.446 -0.621 83.292 -0.647 83.138 -0.647 c
+82.88 -0.647 82.685 -0.555 82.55 -0.368 c
+82.41 -0.184 82.344 0.077 82.344 0.411 c
+82.344 2.881 l
+81.741 2.881 l
+81.741 3.41 l
+82.344 3.41 l
+82.344 4.365 l
+h
+85.945 1.602 m
+85.945 2.179 86.08 2.634 86.356 2.969 c
+86.639 3.31 87.011 3.484 87.474 3.484 c
+87.933 3.484 88.301 3.314 88.576 2.984 c
+88.859 2.66 89.006 2.212 89.017 1.646 c
+89.017 1.22 l
+89.017 0.65 88.874 0.195 88.59 -0.148 c
+88.315 -0.482 87.948 -0.647 87.489 -0.647 c
+87.025 -0.647 86.654 -0.485 86.371 -0.162 c
+86.096 0.168 85.953 0.61 85.945 1.161 c
+h
+86.591 1.22 m
+86.591 0.816 86.668 0.5 86.826 0.264 c
+86.992 0.029 87.213 -0.088 87.489 -0.088 c
+88.054 -0.088 88.349 0.323 88.37 1.146 c
+88.37 1.602 l
+88.37 2.003 88.286 2.322 88.12 2.557 c
+87.962 2.8 87.746 2.925 87.474 2.925 c
+87.209 2.925 86.992 2.8 86.826 2.557 c
+86.668 2.322 86.591 2.003 86.591 1.602 c
+h
+90.472 3.41 m
+90.487 2.969 l
+90.741 3.31 91.064 3.484 91.457 3.484 c
+92.162 3.484 92.519 3.013 92.53 2.072 c
+92.53 -0.574 l
+91.883 -0.574 l
+91.883 2.043 l
+91.883 2.356 91.828 2.576 91.722 2.705 c
+91.611 2.829 91.457 2.896 91.252 2.896 c
+91.094 2.896 90.946 2.84 90.811 2.734 c
+90.682 2.624 90.579 2.487 90.502 2.322 c
+90.502 -0.574 l
+89.855 -0.574 l
+89.855 3.41 l
+h
+f
+Q
+q 1 0 0 1 157.4383 353.5063 cm
+0 0 m
+-0.338 0.029 l
+-0.625 0.029 -0.816 -0.096 -0.912 -0.339 c
+-0.912 -2.97 l
+-1.955 -2.97 l
+-1.955 1.014 l
+-0.985 1.014 l
+-0.956 0.573 l
+-0.79 0.914 -0.559 1.087 -0.264 1.087 c
+-0.147 1.087 -0.056 1.065 0.015 1.028 c
+h
+2.072 -3.043 m
+1.544 -3.043 1.124 -2.889 0.823 -2.573 c
+0.53 -2.249 0.382 -1.79 0.382 -1.191 c
+0.382 -0.882 l
+0.382 -0.258 0.518 0.228 0.794 0.573 c
+1.066 0.914 1.459 1.087 1.97 1.087 c
+2.469 1.087 2.841 0.926 3.087 0.602 c
+3.341 0.278 3.472 -0.198 3.484 -0.823 c
+3.484 -1.324 l
+1.411 -1.324 l
+1.43 -1.617 1.492 -1.834 1.602 -1.97 c
+1.72 -2.11 1.9 -2.176 2.146 -2.176 c
+2.488 -2.176 2.778 -2.058 3.013 -1.823 c
+3.425 -2.455 l
+3.296 -2.631 3.109 -2.775 2.866 -2.881 c
+2.62 -2.988 2.356 -3.043 2.072 -3.043 c
+1.426 -0.603 m
+2.454 -0.603 l
+2.454 -0.5 l
+2.454 -0.265 2.414 -0.088 2.337 0.029 c
+2.267 0.154 2.138 0.22 1.955 0.22 c
+1.779 0.22 1.646 0.151 1.558 0.014 c
+1.477 -0.115 1.433 -0.32 1.426 -0.603 c
+4.307 -2.97 m
+4.307 0.22 l
+3.822 0.22 l
+3.822 1.014 l
+4.307 1.014 l
+4.307 1.367 l
+4.307 1.807 4.417 2.146 4.645 2.381 c
+4.881 2.624 5.196 2.748 5.6 2.748 c
+5.725 2.748 5.883 2.723 6.071 2.674 c
+6.071 1.851 l
+6.001 1.87 5.916 1.881 5.821 1.881 c
+5.505 1.881 5.351 1.697 5.351 1.337 c
+5.351 1.014 l
+5.968 1.014 l
+5.968 0.22 l
+5.351 0.22 l
+5.351 -2.97 l
+h
+f
+Q
+q 1 0 0 1 167.8457 350.5364 cm
+0 0 m
+-0.04 0.088 -0.067 0.235 -0.074 0.441 c
+-0.31 0.096 -0.603 -0.073 -0.956 -0.073 c
+-1.32 -0.073 -1.603 0.023 -1.808 0.221 c
+-2.007 0.426 -2.103 0.713 -2.103 1.088 c
+-2.103 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.286 -1.044 2.411 -0.574 2.411 c
+-0.088 2.411 l
+-0.088 2.837 l
+-0.088 3.072 -0.144 3.238 -0.25 3.337 c
+-0.36 3.443 -0.522 3.499 -0.736 3.499 c
+-0.934 3.499 -1.095 3.44 -1.22 3.323 c
+-1.338 3.205 -1.397 3.057 -1.397 2.882 c
+-2.043 2.882 l
+-2.043 3.076 -1.985 3.267 -1.867 3.454 c
+-1.742 3.638 -1.58 3.786 -1.382 3.896 c
+-1.176 4.002 -0.948 4.057 -0.691 4.057 c
+-0.291 4.057 0.014 3.954 0.22 3.749 c
+0.433 3.543 0.548 3.248 0.558 2.866 c
+0.558 0.853 l
+0.558 0.548 0.595 0.283 0.675 0.059 c
+0.675 0 l
+h
+-0.867 0.515 m
+-0.703 0.515 -0.551 0.559 -0.412 0.647 c
+-0.265 0.736 -0.158 0.846 -0.088 0.985 c
+-0.088 1.926 l
+-0.456 1.926 l
+-0.772 1.926 -1.015 1.856 -1.191 1.72 c
+-1.368 1.592 -1.455 1.404 -1.455 1.162 c
+-1.455 0.933 -1.411 0.769 -1.324 0.661 c
+-1.235 0.563 -1.085 0.515 -0.867 0.515 c
+2.175 3.984 m
+2.19 3.543 l
+2.443 3.884 2.767 4.057 3.16 4.057 c
+3.865 4.057 4.222 3.587 4.233 2.646 c
+4.233 0 l
+3.586 0 l
+3.586 2.617 l
+3.586 2.929 3.531 3.15 3.424 3.279 c
+3.314 3.403 3.16 3.469 2.954 3.469 c
+2.796 3.469 2.649 3.414 2.513 3.308 c
+2.385 3.198 2.281 3.061 2.204 2.896 c
+2.204 0 l
+1.558 0 l
+1.558 3.984 l
+h
+5.071 2.176 m
+5.071 2.782 5.181 3.248 5.409 3.572 c
+5.644 3.896 5.971 4.057 6.393 4.057 c
+6.776 4.057 7.073 3.899 7.291 3.587 c
+7.291 5.644 l
+7.937 5.644 l
+7.937 0 l
+7.349 0 l
+7.305 0.426 l
+7.099 0.092 6.794 -0.073 6.393 -0.073 c
+5.982 -0.073 5.659 0.081 5.423 0.397 c
+5.188 0.721 5.071 1.176 5.071 1.764 c
+h
+5.718 1.794 m
+5.718 1.353 5.78 1.022 5.909 0.809 c
+6.044 0.603 6.265 0.5 6.57 0.5 c
+6.894 0.5 7.133 0.661 7.291 0.985 c
+7.291 2.999 l
+7.121 3.311 6.882 3.469 6.57 3.469 c
+6.265 3.469 6.044 3.366 5.909 3.161 c
+5.78 2.955 5.718 2.631 5.718 2.19 c
+h
+11.274 3.984 m
+11.289 3.543 l
+11.542 3.884 11.866 4.057 12.259 4.057 c
+12.964 4.057 13.321 3.587 13.331 2.646 c
+13.331 0 l
+12.685 0 l
+12.685 2.617 l
+12.685 2.929 12.629 3.15 12.523 3.279 c
+12.413 3.403 12.259 3.469 12.053 3.469 c
+11.895 3.469 11.748 3.414 11.612 3.308 c
+11.484 3.198 11.38 3.061 11.303 2.896 c
+11.303 0 l
+10.657 0 l
+10.657 3.984 l
+h
+14.169 2.176 m
+14.169 2.753 14.306 3.208 14.581 3.543 c
+14.864 3.884 15.235 4.057 15.698 4.057 c
+16.157 4.057 16.525 3.888 16.801 3.558 c
+17.084 3.234 17.231 2.786 17.242 2.22 c
+17.242 1.794 l
+17.242 1.224 17.098 0.769 16.816 0.426 c
+16.54 0.092 16.172 -0.073 15.713 -0.073 c
+15.249 -0.073 14.879 0.088 14.596 0.412 c
+14.32 0.742 14.177 1.183 14.169 1.735 c
+h
+14.817 1.794 m
+14.817 1.389 14.894 1.073 15.052 0.838 c
+15.216 0.603 15.438 0.485 15.713 0.485 c
+16.279 0.485 16.573 0.897 16.595 1.72 c
+16.595 2.176 l
+16.595 2.577 16.51 2.896 16.345 3.131 c
+16.187 3.374 15.97 3.499 15.698 3.499 c
+15.434 3.499 15.216 3.374 15.052 3.131 c
+14.894 2.896 14.817 2.577 14.817 2.176 c
+h
+18.932 4.939 m
+18.932 3.984 l
+19.534 3.984 l
+19.534 3.454 l
+18.932 3.454 l
+18.932 0.985 l
+18.932 0.827 18.954 0.709 19.006 0.632 c
+19.064 0.551 19.152 0.515 19.27 0.515 c
+19.359 0.515 19.446 0.53 19.534 0.559 c
+19.534 0 l
+19.388 -0.047 19.233 -0.073 19.079 -0.073 c
+18.821 -0.073 18.627 0.019 18.491 0.206 c
+18.351 0.389 18.285 0.651 18.285 0.985 c
+18.285 3.454 l
+17.682 3.454 l
+17.682 3.984 l
+18.285 3.984 l
+18.285 4.939 l
+h
+22.636 3.984 m
+22.651 3.616 l
+22.894 3.911 23.213 4.057 23.606 4.057 c
+24.047 4.057 24.356 3.859 24.533 3.469 c
+24.786 3.859 25.135 4.057 25.576 4.057 c
+26.311 4.057 26.686 3.595 26.708 2.676 c
+26.708 0 l
+26.061 0 l
+26.061 2.617 l
+26.061 2.911 26.006 3.124 25.899 3.263 c
+25.8 3.4 25.628 3.469 25.385 3.469 c
+25.186 3.469 25.025 3.389 24.899 3.234 c
+24.782 3.088 24.712 2.896 24.694 2.66 c
+24.694 0 l
+24.032 0 l
+24.032 2.646 l
+24.032 3.194 23.812 3.469 23.371 3.469 c
+23.037 3.469 22.802 3.308 22.665 2.984 c
+22.665 0 l
+22.019 0 l
+22.019 3.984 l
+h
+29.104 -0.073 m
+28.604 -0.073 28.222 0.073 27.957 0.368 c
+27.693 0.661 27.56 1.095 27.56 1.676 c
+27.56 2.147 l
+27.56 2.741 27.685 3.208 27.943 3.543 c
+28.207 3.884 28.567 4.057 29.03 4.057 c
+29.489 4.057 29.831 3.903 30.059 3.601 c
+30.295 3.308 30.416 2.845 30.426 2.22 c
+30.426 1.794 l
+28.207 1.794 l
+28.207 1.706 l
+28.207 1.272 28.284 0.96 28.442 0.765 c
+28.608 0.578 28.839 0.485 29.133 0.485 c
+29.328 0.485 29.501 0.518 29.647 0.588 c
+29.794 0.665 29.931 0.783 30.059 0.941 c
+30.397 0.53 l
+30.11 0.125 29.68 -0.073 29.104 -0.073 c
+29.03 3.499 m
+28.755 3.499 28.552 3.403 28.427 3.219 c
+28.299 3.032 28.225 2.741 28.207 2.352 c
+29.78 2.352 l
+29.78 2.44 l
+29.757 2.822 29.692 3.09 29.574 3.248 c
+29.456 3.414 29.273 3.499 29.03 3.499 c
+32.852 3.366 m
+32.764 3.385 32.664 3.396 32.558 3.396 c
+32.223 3.396 31.988 3.212 31.852 2.852 c
+31.852 0 l
+31.205 0 l
+31.205 3.984 l
+31.837 3.984 l
+31.852 3.572 l
+32.028 3.896 32.271 4.057 32.587 4.057 c
+32.693 4.057 32.782 4.035 32.852 3.998 c
+h
+33.293 2.176 m
+33.293 2.793 33.403 3.256 33.631 3.572 c
+33.855 3.896 34.189 4.057 34.63 4.057 c
+35.031 4.057 35.336 3.881 35.542 3.528 c
+35.586 3.984 l
+36.174 3.984 l
+36.174 -0.044 l
+36.174 -0.532 36.045 -0.911 35.791 -1.176 c
+35.535 -1.44 35.182 -1.573 34.733 -1.573 c
+34.535 -1.573 34.314 -1.521 34.072 -1.425 c
+33.826 -1.326 33.646 -1.205 33.528 -1.058 c
+33.792 -0.617 l
+34.058 -0.881 34.355 -1.014 34.69 -1.014 c
+35.226 -1.014 35.502 -0.72 35.513 -0.132 c
+35.513 0.397 l
+35.307 0.081 35.005 -0.073 34.615 -0.073 c
+34.204 -0.073 33.881 0.077 33.646 0.383 c
+33.418 0.695 33.3 1.147 33.293 1.735 c
+h
+33.954 1.794 m
+33.954 1.353 34.017 1.022 34.145 0.809 c
+34.27 0.603 34.487 0.5 34.792 0.5 c
+35.116 0.5 35.355 0.665 35.513 1 c
+35.513 2.984 l
+35.344 3.308 35.105 3.469 34.792 3.469 c
+34.498 3.469 34.281 3.366 34.145 3.161 c
+34.017 2.955 33.954 2.631 33.954 2.19 c
+h
+38.555 -0.073 m
+38.056 -0.073 37.673 0.073 37.408 0.368 c
+37.144 0.661 37.012 1.095 37.012 1.676 c
+37.012 2.147 l
+37.012 2.741 37.137 3.208 37.394 3.543 c
+37.659 3.884 38.019 4.057 38.482 4.057 c
+38.941 4.057 39.282 3.903 39.511 3.601 c
+39.746 3.308 39.867 2.845 39.878 2.22 c
+39.878 1.794 l
+37.659 1.794 l
+37.659 1.706 l
+37.659 1.272 37.736 0.96 37.894 0.765 c
+38.059 0.578 38.291 0.485 38.584 0.485 c
+38.779 0.485 38.952 0.518 39.099 0.588 c
+39.246 0.665 39.382 0.783 39.511 0.941 c
+39.849 0.53 l
+39.562 0.125 39.132 -0.073 38.555 -0.073 c
+38.482 3.499 m
+38.206 3.499 38.004 3.403 37.879 3.219 c
+37.75 3.032 37.677 2.741 37.659 2.352 c
+39.232 2.352 l
+39.232 2.44 l
+39.209 2.822 39.143 3.09 39.026 3.248 c
+38.908 3.414 38.725 3.499 38.482 3.499 c
+40.525 2.176 m
+40.525 2.782 40.635 3.248 40.863 3.572 c
+41.098 3.896 41.425 4.057 41.848 4.057 c
+42.23 4.057 42.528 3.899 42.744 3.587 c
+42.744 5.644 l
+43.392 5.644 l
+43.392 0 l
+42.804 0 l
+42.759 0.426 l
+42.553 0.092 42.249 -0.073 41.848 -0.073 c
+41.436 -0.073 41.113 0.081 40.878 0.397 c
+40.643 0.721 40.525 1.176 40.525 1.764 c
+h
+41.171 1.794 m
+41.171 1.353 41.234 1.022 41.362 0.809 c
+41.499 0.603 41.719 0.5 42.024 0.5 c
+42.347 0.5 42.586 0.661 42.744 0.985 c
+42.744 2.999 l
+42.575 3.311 42.336 3.469 42.024 3.469 c
+41.719 3.469 41.499 3.366 41.362 3.161 c
+41.234 2.955 41.171 2.631 41.171 2.19 c
+h
+46.816 0 -0.646 3.984 re
+46.86 5.027 m
+46.86 4.917 46.831 4.825 46.772 4.748 c
+46.713 4.678 46.617 4.645 46.492 4.645 c
+46.375 4.645 46.28 4.678 46.214 4.748 c
+46.155 4.825 46.125 4.917 46.125 5.027 c
+46.125 5.145 46.155 5.237 46.214 5.307 c
+46.28 5.384 46.375 5.424 46.492 5.424 c
+46.617 5.424 46.713 5.384 46.772 5.307 c
+46.831 5.226 46.86 5.134 46.86 5.027 c
+48.448 3.984 m
+48.462 3.543 l
+48.716 3.884 49.039 4.057 49.432 4.057 c
+50.138 4.057 50.494 3.587 50.505 2.646 c
+50.505 0 l
+49.859 0 l
+49.859 2.617 l
+49.859 2.929 49.804 3.15 49.697 3.279 c
+49.586 3.403 49.432 3.469 49.227 3.469 c
+49.069 3.469 48.921 3.414 48.786 3.308 c
+48.657 3.198 48.554 3.061 48.477 2.896 c
+48.477 0 l
+47.83 0 l
+47.83 3.984 l
+h
+52.329 4.939 m
+52.329 3.984 l
+52.931 3.984 l
+52.931 3.454 l
+52.329 3.454 l
+52.329 0.985 l
+52.329 0.827 52.35 0.709 52.402 0.632 c
+52.46 0.551 52.549 0.515 52.666 0.515 c
+52.755 0.515 52.842 0.53 52.931 0.559 c
+52.931 0 l
+52.784 -0.047 52.63 -0.073 52.475 -0.073 c
+52.218 -0.073 52.023 0.019 51.887 0.206 c
+51.747 0.389 51.681 0.651 51.681 0.985 c
+51.681 3.454 l
+51.078 3.454 l
+51.078 3.984 l
+51.681 3.984 l
+51.681 4.939 l
+h
+53.489 2.176 m
+53.489 2.753 53.625 3.208 53.901 3.543 c
+54.184 3.884 54.555 4.057 55.018 4.057 c
+55.477 4.057 55.845 3.888 56.121 3.558 c
+56.404 3.234 56.551 2.786 56.562 2.22 c
+56.562 1.794 l
+56.562 1.224 56.418 0.769 56.135 0.426 c
+55.859 0.092 55.492 -0.073 55.033 -0.073 c
+54.569 -0.073 54.199 0.088 53.916 0.412 c
+53.64 0.742 53.497 1.183 53.489 1.735 c
+h
+54.136 1.794 m
+54.136 1.389 54.213 1.073 54.371 0.838 c
+54.537 0.603 54.758 0.485 55.033 0.485 c
+55.599 0.485 55.893 0.897 55.915 1.72 c
+55.915 2.176 l
+55.915 2.577 55.83 2.896 55.664 3.131 c
+55.506 3.374 55.29 3.499 55.018 3.499 c
+54.754 3.499 54.537 3.374 54.371 3.131 c
+54.213 2.896 54.136 2.577 54.136 2.176 c
+h
+60.501 0.485 m
+60.714 0.485 60.887 0.548 61.015 0.676 c
+61.151 0.813 61.225 1.004 61.236 1.249 c
+61.854 1.249 l
+61.831 0.867 61.696 0.548 61.442 0.294 c
+61.185 0.048 60.872 -0.073 60.501 -0.073 c
+60.009 -0.073 59.633 0.077 59.369 0.383 c
+59.112 0.695 58.987 1.162 58.987 1.779 c
+58.987 2.22 l
+58.987 2.815 59.112 3.271 59.369 3.587 c
+59.633 3.899 60.009 4.057 60.501 4.057 c
+60.902 4.057 61.221 3.925 61.457 3.66 c
+61.699 3.403 61.831 3.057 61.854 2.617 c
+61.236 2.617 l
+61.214 2.911 61.141 3.131 61.015 3.279 c
+60.898 3.425 60.725 3.499 60.501 3.499 c
+60.207 3.499 59.99 3.4 59.855 3.205 c
+59.714 3.017 59.641 2.708 59.633 2.279 c
+59.633 1.764 l
+59.633 1.294 59.699 0.96 59.839 0.765 c
+59.986 0.578 60.207 0.485 60.501 0.485 c
+64.632 0.353 m
+64.414 0.067 64.102 -0.073 63.691 -0.073 c
+63.327 -0.073 63.051 0.048 62.868 0.294 c
+62.691 0.548 62.596 0.912 62.588 1.382 c
+62.588 3.984 l
+63.235 3.984 l
+63.235 1.441 l
+63.235 0.813 63.419 0.5 63.793 0.5 c
+64.194 0.5 64.47 0.676 64.617 1.029 c
+64.617 3.984 l
+65.264 3.984 l
+65.264 0 l
+64.646 0 l
+h
+67.894 3.366 m
+67.807 3.385 67.707 3.396 67.601 3.396 c
+67.266 3.396 67.03 3.212 66.895 2.852 c
+66.895 0 l
+66.248 0 l
+66.248 3.984 l
+66.88 3.984 l
+66.895 3.572 l
+67.071 3.896 67.314 4.057 67.63 4.057 c
+67.736 4.057 67.824 4.035 67.894 3.998 c
+h
+70.188 3.366 m
+70.099 3.385 70 3.396 69.893 3.396 c
+69.559 3.396 69.324 3.212 69.188 2.852 c
+69.188 0 l
+68.541 0 l
+68.541 3.984 l
+69.173 3.984 l
+69.188 3.572 l
+69.364 3.896 69.607 4.057 69.923 4.057 c
+70.03 4.057 70.118 4.035 70.188 3.998 c
+h
+72.187 -0.073 m
+71.687 -0.073 71.304 0.073 71.04 0.368 c
+70.776 0.661 70.643 1.095 70.643 1.676 c
+70.643 2.147 l
+70.643 2.741 70.768 3.208 71.026 3.543 c
+71.29 3.884 71.65 4.057 72.113 4.057 c
+72.572 4.057 72.914 3.903 73.142 3.601 c
+73.378 3.308 73.498 2.845 73.509 2.22 c
+73.509 1.794 l
+71.29 1.794 l
+71.29 1.706 l
+71.29 1.272 71.367 0.96 71.525 0.765 c
+71.691 0.578 71.922 0.485 72.216 0.485 c
+72.411 0.485 72.584 0.518 72.73 0.588 c
+72.877 0.665 73.014 0.783 73.142 0.941 c
+73.48 0.53 l
+73.193 0.125 72.763 -0.073 72.187 -0.073 c
+72.113 3.499 m
+71.838 3.499 71.635 3.403 71.51 3.219 c
+71.381 3.032 71.308 2.741 71.29 2.352 c
+72.863 2.352 l
+72.863 2.44 l
+72.84 2.822 72.775 3.09 72.657 3.248 c
+72.539 3.414 72.356 3.499 72.113 3.499 c
+74.906 3.984 m
+74.92 3.543 l
+75.174 3.884 75.497 4.057 75.89 4.057 c
+76.596 4.057 76.952 3.587 76.964 2.646 c
+76.964 0 l
+76.317 0 l
+76.317 2.617 l
+76.317 2.929 76.262 3.15 76.156 3.279 c
+76.046 3.403 75.89 3.469 75.685 3.469 c
+75.527 3.469 75.38 3.414 75.244 3.308 c
+75.115 3.198 75.013 3.061 74.936 2.896 c
+74.936 0 l
+74.288 0 l
+74.288 3.984 l
+h
+78.787 4.939 m
+78.787 3.984 l
+79.389 3.984 l
+79.389 3.454 l
+78.787 3.454 l
+78.787 0.985 l
+78.787 0.827 78.809 0.709 78.86 0.632 c
+78.919 0.551 79.007 0.515 79.125 0.515 c
+79.213 0.515 79.301 0.53 79.389 0.559 c
+79.389 0 l
+79.242 -0.047 79.088 -0.073 78.934 -0.073 c
+78.676 -0.073 78.481 0.019 78.346 0.206 c
+78.206 0.389 78.14 0.651 78.14 0.985 c
+78.14 3.454 l
+77.537 3.454 l
+77.537 3.984 l
+78.14 3.984 l
+78.14 4.939 l
+h
+84.754 1.794 m
+84.754 1.176 84.64 0.709 84.416 0.397 c
+84.199 0.081 83.876 -0.073 83.446 -0.073 c
+83.023 -0.073 82.711 0.107 82.506 0.47 c
+82.476 0 l
+81.873 0 l
+81.873 5.644 l
+82.52 5.644 l
+82.52 3.543 l
+82.733 3.884 83.042 4.057 83.446 4.057 c
+83.876 4.057 84.199 3.899 84.416 3.587 c
+84.64 3.282 84.754 2.815 84.754 2.19 c
+h
+84.108 2.176 m
+84.108 2.646 84.038 2.977 83.902 3.175 c
+83.773 3.37 83.564 3.469 83.27 3.469 c
+82.936 3.469 82.685 3.285 82.52 2.926 c
+82.52 1.044 l
+82.685 0.68 82.939 0.5 83.285 0.5 c
+83.578 0.5 83.788 0.603 83.917 0.809 c
+84.042 1.014 84.108 1.33 84.108 1.764 c
+h
+87.239 3.366 m
+87.15 3.385 87.051 3.396 86.944 3.396 c
+86.61 3.396 86.375 3.212 86.239 2.852 c
+86.239 0 l
+85.592 0 l
+85.592 3.984 l
+86.224 3.984 l
+86.239 3.572 l
+86.415 3.896 86.658 4.057 86.974 4.057 c
+87.08 4.057 87.169 4.035 87.239 3.998 c
+h
+89.781 0 m
+89.741 0.088 89.715 0.235 89.708 0.441 c
+89.473 0.096 89.178 -0.073 88.826 -0.073 c
+88.462 -0.073 88.179 0.023 87.973 0.221 c
+87.775 0.426 87.68 0.713 87.68 1.088 c
+87.68 1.488 87.815 1.808 88.091 2.043 c
+88.363 2.286 88.738 2.411 89.208 2.411 c
+89.693 2.411 l
+89.693 2.837 l
+89.693 3.072 89.638 3.238 89.531 3.337 c
+89.421 3.443 89.259 3.499 89.047 3.499 c
+88.848 3.499 88.686 3.44 88.561 3.323 c
+88.444 3.205 88.385 3.057 88.385 2.882 c
+87.738 2.882 l
+87.738 3.076 87.797 3.267 87.915 3.454 c
+88.04 3.638 88.201 3.786 88.399 3.896 c
+88.605 4.002 88.833 4.057 89.091 4.057 c
+89.491 4.057 89.796 3.954 90.001 3.749 c
+90.215 3.543 90.329 3.248 90.34 2.866 c
+90.34 0.853 l
+90.34 0.548 90.377 0.283 90.458 0.059 c
+90.458 0 l
+h
+88.914 0.515 m
+89.08 0.515 89.23 0.559 89.37 0.647 c
+89.517 0.736 89.623 0.846 89.693 0.985 c
+89.693 1.926 l
+89.326 1.926 l
+89.01 1.926 88.767 1.856 88.59 1.72 c
+88.414 1.592 88.326 1.404 88.326 1.162 c
+88.326 0.933 88.37 0.769 88.459 0.661 c
+88.547 0.563 88.698 0.515 88.914 0.515 c
+91.957 3.984 m
+91.971 3.543 l
+92.225 3.884 92.548 4.057 92.941 4.057 c
+93.647 4.057 94.003 3.587 94.015 2.646 c
+94.015 0 l
+93.368 0 l
+93.368 2.617 l
+93.368 2.929 93.313 3.15 93.207 3.279 c
+93.096 3.403 92.941 3.469 92.736 3.469 c
+92.578 3.469 92.43 3.414 92.295 3.308 c
+92.166 3.198 92.064 3.061 91.986 2.896 c
+91.986 0 l
+91.339 0 l
+91.339 3.984 l
+h
+96.367 0.485 m
+96.58 0.485 96.752 0.548 96.881 0.676 c
+97.017 0.813 97.091 1.004 97.101 1.249 c
+97.719 1.249 l
+97.697 0.867 97.561 0.548 97.307 0.294 c
+97.05 0.048 96.738 -0.073 96.367 -0.073 c
+95.874 -0.073 95.499 0.077 95.235 0.383 c
+94.977 0.695 94.853 1.162 94.853 1.779 c
+94.853 2.22 l
+94.853 2.815 94.977 3.271 95.235 3.587 c
+95.499 3.899 95.874 4.057 96.367 4.057 c
+96.767 4.057 97.087 3.925 97.322 3.66 c
+97.564 3.403 97.697 3.057 97.719 2.617 c
+97.101 2.617 l
+97.08 2.911 97.006 3.131 96.881 3.279 c
+96.763 3.425 96.59 3.499 96.367 3.499 c
+96.073 3.499 95.856 3.4 95.72 3.205 c
+95.58 3.017 95.507 2.708 95.499 2.279 c
+95.499 1.764 l
+95.499 1.294 95.566 0.96 95.705 0.765 c
+95.852 0.578 96.073 0.485 96.367 0.485 c
+99.115 3.572 m
+99.368 3.896 99.688 4.057 100.071 4.057 c
+100.777 4.057 101.132 3.587 101.144 2.646 c
+101.144 0 l
+100.497 0 l
+100.497 2.617 l
+100.497 2.929 100.442 3.15 100.335 3.279 c
+100.225 3.403 100.071 3.469 99.865 3.469 c
+99.707 3.469 99.56 3.414 99.424 3.308 c
+99.295 3.198 99.192 3.061 99.115 2.896 c
+99.115 0 l
+98.468 0 l
+98.468 5.644 l
+99.115 5.644 l
+h
+102.144 0.353 m
+102.144 0.47 102.177 0.566 102.246 0.647 c
+102.312 0.724 102.415 0.765 102.555 0.765 c
+102.701 0.765 102.809 0.724 102.878 0.647 c
+102.955 0.566 102.996 0.47 102.996 0.353 c
+102.996 0.243 102.955 0.151 102.878 0.073 c
+102.809 -0.004 102.701 -0.044 102.555 -0.044 c
+102.415 -0.044 102.312 -0.004 102.246 0.073 c
+102.177 0.151 102.144 0.243 102.144 0.353 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 343.426 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 336.5872 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.596 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.279 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+24.133 1.47 m
+24.033 1.477 23.931 1.488 23.824 1.5 c
+23.713 1.517 23.592 1.529 23.456 1.529 c
+23.28 1.529 23.121 1.488 22.986 1.411 c
+22.846 1.341 22.728 1.242 22.633 1.118 c
+22.545 0.989 22.475 0.842 22.427 0.676 c
+22.387 0.507 22.369 0.331 22.369 0.147 c
+22.369 -1.264 l
+21.472 -1.264 l
+21.472 0.985 l
+21.472 1.11 21.461 1.235 21.442 1.353 c
+21.432 1.477 21.417 1.595 21.398 1.706 c
+21.388 1.823 21.373 1.918 21.354 1.999 c
+21.332 2.087 21.314 2.161 21.296 2.22 c
+22.178 2.22 l
+22.185 2.168 22.196 2.117 22.207 2.058 c
+22.225 1.999 22.24 1.933 22.251 1.867 c
+22.269 1.808 22.284 1.742 22.295 1.675 c
+22.302 1.606 22.313 1.544 22.325 1.484 c
+22.339 1.484 l
+22.375 1.602 22.427 1.709 22.486 1.808 c
+22.552 1.904 22.633 1.988 22.722 2.058 c
+22.809 2.124 22.913 2.18 23.03 2.22 c
+23.155 2.257 23.302 2.278 23.471 2.278 c
+23.596 2.278 23.713 2.271 23.824 2.263 c
+23.941 2.253 24.044 2.238 24.133 2.22 c
+h
+26.738 -1.323 m
+26.481 -1.323 26.253 -1.286 26.047 -1.22 c
+25.841 -1.143 25.665 -1.029 25.518 -0.881 c
+25.371 -0.727 25.253 -0.536 25.165 -0.309 c
+25.084 -0.085 25.047 0.181 25.047 0.485 c
+25.047 0.816 25.091 1.095 25.18 1.323 c
+25.275 1.558 25.404 1.742 25.562 1.881 c
+25.727 2.018 25.915 2.117 26.121 2.176 c
+26.327 2.242 26.535 2.278 26.753 2.278 c
+27.025 2.278 27.26 2.227 27.458 2.132 c
+27.664 2.043 27.829 1.911 27.958 1.735 c
+28.094 1.565 28.193 1.359 28.252 1.118 c
+28.318 0.882 28.355 0.617 28.355 0.324 c
+28.355 0.309 l
+25.988 0.309 l
+25.988 0.162 26.003 0.023 26.032 -0.103 c
+26.069 -0.231 26.124 -0.345 26.194 -0.441 c
+26.26 -0.529 26.344 -0.599 26.444 -0.646 c
+26.539 -0.698 26.653 -0.72 26.782 -0.72 c
+26.936 -0.72 27.076 -0.687 27.194 -0.617 c
+27.318 -0.551 27.407 -0.448 27.458 -0.309 c
+28.296 -0.382 l
+28.266 -0.482 28.212 -0.588 28.135 -0.706 c
+28.054 -0.816 27.95 -0.918 27.825 -1.014 c
+27.708 -1.103 27.553 -1.176 27.37 -1.234 c
+27.194 -1.294 26.98 -1.323 26.738 -1.323 c
+26.738 1.706 m
+26.649 1.706 26.562 1.691 26.473 1.661 c
+26.385 1.632 26.304 1.58 26.238 1.515 c
+26.169 1.444 26.109 1.357 26.061 1.249 c
+26.021 1.139 26.003 1.014 26.003 0.867 c
+27.472 0.867 l
+27.472 1.004 27.447 1.124 27.399 1.235 c
+27.359 1.341 27.304 1.43 27.237 1.5 c
+27.179 1.565 27.106 1.617 27.017 1.646 c
+26.929 1.683 26.834 1.706 26.738 1.706 c
+30.886 1.602 m
+30.886 -1.264 l
+29.99 -1.264 l
+29.99 1.602 l
+29.167 1.602 l
+29.167 2.22 l
+29.99 2.22 l
+29.99 2.484 l
+29.99 2.61 30.005 2.741 30.034 2.881 c
+30.071 3.017 30.14 3.135 30.24 3.234 c
+30.346 3.341 30.489 3.429 30.666 3.499 c
+30.842 3.564 31.067 3.601 31.343 3.601 c
+31.555 3.601 31.754 3.591 31.931 3.572 c
+32.106 3.55 32.257 3.532 32.386 3.514 c
+32.386 2.926 l
+32.257 2.944 32.114 2.959 31.96 2.969 c
+31.802 2.976 31.651 2.984 31.504 2.984 c
+31.376 2.984 31.272 2.969 31.195 2.94 c
+31.115 2.911 31.052 2.87 31.004 2.822 c
+30.953 2.77 30.919 2.708 30.901 2.631 c
+30.89 2.562 30.886 2.484 30.886 2.396 c
+30.886 2.22 l
+32.313 2.22 l
+32.313 1.602 l
+h
+35.407 -0.646 m
+36.538 -0.646 l
+36.538 -1.264 l
+33.232 -1.264 l
+33.232 -0.646 l
+34.495 -0.646 l
+34.495 2.896 l
+33.569 2.896 l
+33.569 3.514 l
+35.407 3.514 l
+h
+40.569 0.485 m
+40.569 0.21 40.533 -0.04 40.467 -0.264 c
+40.397 -0.482 40.294 -0.669 40.158 -0.823 c
+40.018 -0.981 39.842 -1.103 39.629 -1.19 c
+39.412 -1.278 39.158 -1.323 38.865 -1.323 c
+38.589 -1.323 38.343 -1.278 38.13 -1.19 c
+37.924 -1.103 37.751 -0.981 37.615 -0.823 c
+37.475 -0.669 37.373 -0.482 37.307 -0.264 c
+37.236 -0.04 37.203 0.21 37.203 0.485 c
+37.203 0.738 37.233 0.974 37.292 1.191 c
+37.358 1.415 37.461 1.606 37.6 1.764 c
+37.737 1.929 37.913 2.058 38.13 2.146 c
+38.343 2.234 38.6 2.278 38.894 2.278 c
+39.206 2.278 39.467 2.234 39.673 2.146 c
+39.886 2.058 40.059 1.929 40.187 1.764 c
+40.324 1.606 40.423 1.415 40.482 1.191 c
+40.54 0.974 40.569 0.738 40.569 0.485 c
+39.615 0.485 m
+39.615 0.691 39.599 0.867 39.57 1.014 c
+39.548 1.162 39.511 1.282 39.453 1.382 c
+39.393 1.477 39.32 1.548 39.232 1.588 c
+39.144 1.635 39.033 1.661 38.909 1.661 c
+38.644 1.661 38.453 1.562 38.335 1.367 c
+38.218 1.18 38.159 0.886 38.159 0.485 c
+38.159 0.063 38.218 -0.243 38.335 -0.426 c
+38.453 -0.613 38.63 -0.706 38.865 -0.706 c
+38.99 -0.706 39.104 -0.687 39.202 -0.646 c
+39.298 -0.599 39.379 -0.525 39.438 -0.426 c
+39.504 -0.33 39.548 -0.206 39.57 -0.058 c
+39.599 0.088 39.615 0.268 39.615 0.485 c
+42.954 -2.66 m
+42.738 -2.66 42.547 -2.635 42.381 -2.587 c
+42.212 -2.547 42.073 -2.484 41.955 -2.396 c
+41.837 -2.315 41.738 -2.219 41.661 -2.102 c
+41.591 -1.984 41.543 -1.855 41.514 -1.72 c
+42.41 -1.617 l
+42.447 -1.753 42.518 -1.859 42.616 -1.94 c
+42.712 -2.028 42.837 -2.072 42.984 -2.072 c
+43.072 -2.072 43.153 -2.057 43.234 -2.028 c
+43.311 -1.999 43.381 -1.944 43.44 -1.866 c
+43.499 -1.797 43.542 -1.705 43.572 -1.587 c
+43.609 -1.469 43.631 -1.323 43.631 -1.147 c
+43.631 -0.955 l
+43.631 -0.889 43.634 -0.831 43.646 -0.779 c
+43.646 -0.588 l
+43.631 -0.588 l
+43.532 -0.816 43.388 -0.977 43.204 -1.072 c
+43.017 -1.172 42.811 -1.22 42.587 -1.22 c
+42.381 -1.22 42.198 -1.183 42.044 -1.103 c
+41.896 -1.014 41.768 -0.897 41.661 -0.75 c
+41.562 -0.596 41.488 -0.411 41.441 -0.206 c
+41.389 0.008 41.367 0.243 41.367 0.5 c
+41.367 0.771 41.389 1.018 41.441 1.235 c
+41.5 1.448 41.58 1.632 41.691 1.779 c
+41.797 1.933 41.93 2.051 42.088 2.132 c
+42.242 2.22 42.429 2.263 42.646 2.263 c
+42.742 2.263 42.84 2.253 42.94 2.234 c
+43.035 2.213 43.123 2.18 43.204 2.132 c
+43.293 2.08 43.37 2.018 43.44 1.941 c
+43.517 1.86 43.58 1.768 43.631 1.661 c
+43.646 1.661 l
+43.646 1.808 l
+43.653 1.867 43.66 1.918 43.66 1.97 c
+43.667 2.028 43.675 2.076 43.675 2.117 c
+43.682 2.165 43.694 2.198 43.704 2.22 c
+44.557 2.22 l
+44.546 2.139 44.535 2.028 44.527 1.881 c
+44.527 1.411 l
+44.527 -1.161 l
+44.527 -1.415 44.49 -1.635 44.425 -1.822 c
+44.355 -2.007 44.251 -2.161 44.116 -2.279 c
+43.976 -2.404 43.811 -2.499 43.616 -2.558 c
+43.418 -2.624 43.197 -2.66 42.954 -2.66 c
+43.646 0.53 m
+43.646 0.742 43.619 0.919 43.572 1.058 c
+43.532 1.205 43.476 1.323 43.41 1.411 c
+43.351 1.5 43.282 1.558 43.204 1.588 c
+43.123 1.625 43.046 1.646 42.969 1.646 c
+42.87 1.646 42.778 1.621 42.69 1.573 c
+42.609 1.532 42.543 1.463 42.484 1.367 c
+42.433 1.279 42.389 1.162 42.352 1.014 c
+42.323 0.875 42.308 0.706 42.308 0.5 c
+42.308 0.125 42.366 -0.154 42.484 -0.338 c
+42.601 -0.515 42.763 -0.602 42.969 -0.602 c
+43.035 -0.602 43.109 -0.588 43.189 -0.559 c
+43.278 -0.522 43.351 -0.463 43.41 -0.382 c
+43.476 -0.294 43.532 -0.176 43.572 -0.029 c
+43.619 0.118 43.646 0.301 43.646 0.53 c
+f
+Q
+q 1 0 0 1 60.7924 321.0066 cm
+0 0 m
+2.102 0 l
+2.102 -0.574 l
+-0.676 -0.574 l
+-0.676 4.777 l
+0 4.777 l
+h
+3.513 -0.574 -0.646 3.984 re
+3.557 4.453 m
+3.557 4.343 3.528 4.251 3.469 4.174 c
+3.41 4.104 3.314 4.072 3.189 4.072 c
+3.072 4.072 2.977 4.104 2.91 4.174 c
+2.851 4.251 2.822 4.343 2.822 4.453 c
+2.822 4.571 2.851 4.663 2.91 4.733 c
+2.977 4.81 3.072 4.85 3.189 4.85 c
+3.314 4.85 3.41 4.81 3.469 4.733 c
+3.528 4.652 3.557 4.56 3.557 4.453 c
+6.468 0.44 m
+6.468 0.588 6.412 0.709 6.306 0.808 c
+6.196 0.904 5.99 1.022 5.689 1.161 c
+5.342 1.308 5.101 1.429 4.953 1.529 c
+4.806 1.635 4.696 1.753 4.63 1.881 c
+4.56 2.006 4.527 2.164 4.527 2.352 c
+4.527 2.674 4.644 2.944 4.88 3.16 c
+5.115 3.374 5.416 3.484 5.791 3.484 c
+6.173 3.484 6.482 3.37 6.717 3.145 c
+6.952 2.917 7.07 2.631 7.07 2.278 c
+6.423 2.278 l
+6.423 2.454 6.364 2.605 6.247 2.734 c
+6.129 2.859 5.975 2.925 5.791 2.925 c
+5.593 2.925 5.442 2.869 5.336 2.763 c
+5.225 2.664 5.174 2.532 5.174 2.366 c
+5.174 2.238 5.211 2.131 5.292 2.043 c
+5.369 1.962 5.56 1.859 5.865 1.734 c
+6.342 1.547 6.673 1.359 6.85 1.176 c
+7.025 0.999 7.114 0.771 7.114 0.5 c
+7.114 0.147 6.989 -0.133 6.747 -0.339 c
+6.512 -0.544 6.196 -0.647 5.806 -0.647 c
+5.383 -0.647 5.045 -0.53 4.791 -0.294 c
+4.534 -0.052 4.409 0.253 4.409 0.617 c
+5.056 0.617 l
+5.064 0.389 5.134 0.213 5.262 0.087 c
+5.387 -0.03 5.571 -0.088 5.806 -0.088 c
+6.019 -0.088 6.181 -0.04 6.291 0.058 c
+6.408 0.154 6.468 0.282 6.468 0.44 c
+8.804 4.365 m
+8.804 3.41 l
+9.407 3.41 l
+9.407 2.881 l
+8.804 2.881 l
+8.804 0.411 l
+8.804 0.253 8.827 0.135 8.878 0.058 c
+8.937 -0.023 9.025 -0.059 9.142 -0.059 c
+9.231 -0.059 9.319 -0.044 9.407 -0.015 c
+9.407 -0.574 l
+9.26 -0.621 9.105 -0.647 8.951 -0.647 c
+8.694 -0.647 8.5 -0.555 8.363 -0.368 c
+8.224 -0.184 8.158 0.077 8.158 0.411 c
+8.158 2.881 l
+7.555 2.881 l
+7.555 3.41 l
+8.158 3.41 l
+8.158 4.365 l
+h
+11.759 1.602 m
+11.759 2.179 11.895 2.634 12.17 2.969 c
+12.454 3.31 12.825 3.484 13.288 3.484 c
+13.747 3.484 14.115 3.314 14.39 2.984 c
+14.673 2.66 14.82 2.212 14.831 1.646 c
+14.831 1.22 l
+14.831 0.65 14.688 0.195 14.405 -0.148 c
+14.129 -0.482 13.762 -0.647 13.302 -0.647 c
+12.839 -0.647 12.469 -0.485 12.185 -0.162 c
+11.91 0.168 11.766 0.61 11.759 1.161 c
+h
+12.406 1.22 m
+12.406 0.816 12.483 0.5 12.641 0.264 c
+12.806 0.029 13.026 -0.088 13.302 -0.088 c
+13.868 -0.088 14.162 0.323 14.184 1.146 c
+14.184 1.602 l
+14.184 2.003 14.1 2.322 13.934 2.557 c
+13.776 2.8 13.56 2.925 13.288 2.925 c
+13.023 2.925 12.806 2.8 12.641 2.557 c
+12.483 2.322 12.406 2.003 12.406 1.602 c
+h
+18.549 1.22 m
+18.549 0.592 18.432 0.121 18.197 -0.191 c
+17.969 -0.497 17.653 -0.647 17.242 -0.647 c
+16.837 -0.647 16.529 -0.497 16.315 -0.191 c
+16.315 -2.103 l
+15.669 -2.103 l
+15.669 3.41 l
+16.257 3.41 l
+16.301 2.969 l
+16.514 3.31 16.823 3.484 17.227 3.484 c
+17.668 3.484 17.995 3.329 18.212 3.027 c
+18.425 2.722 18.539 2.267 18.549 1.66 c
+h
+17.903 1.602 m
+17.903 2.043 17.834 2.366 17.697 2.572 c
+17.558 2.786 17.337 2.896 17.036 2.896 c
+16.72 2.896 16.481 2.741 16.315 2.439 c
+16.315 0.367 l
+16.481 0.062 16.72 -0.088 17.036 -0.088 c
+17.33 -0.088 17.543 0.014 17.683 0.22 c
+17.819 0.434 17.892 0.764 17.903 1.205 c
+h
+20.814 -0.647 m
+20.313 -0.647 19.931 -0.5 19.667 -0.206 c
+19.403 0.087 19.27 0.521 19.27 1.103 c
+19.27 1.573 l
+19.27 2.167 19.395 2.634 19.652 2.969 c
+19.917 3.31 20.277 3.484 20.74 3.484 c
+21.2 3.484 21.541 3.329 21.769 3.027 c
+22.004 2.734 22.125 2.271 22.137 1.646 c
+22.137 1.22 l
+19.917 1.22 l
+19.917 1.132 l
+19.917 0.698 19.994 0.386 20.153 0.191 c
+20.317 0.004 20.549 -0.088 20.843 -0.088 c
+21.038 -0.088 21.211 -0.055 21.358 0.014 c
+21.504 0.091 21.641 0.209 21.769 0.367 c
+22.107 -0.044 l
+21.82 -0.449 21.391 -0.647 20.814 -0.647 c
+20.74 2.925 m
+20.465 2.925 20.263 2.829 20.137 2.645 c
+20.009 2.458 19.935 2.167 19.917 1.778 c
+21.489 1.778 l
+21.489 1.866 l
+21.468 2.248 21.402 2.516 21.284 2.674 c
+21.167 2.84 20.982 2.925 20.74 2.925 c
+24.562 2.792 m
+24.473 2.811 24.375 2.822 24.268 2.822 c
+23.933 2.822 23.698 2.638 23.563 2.278 c
+23.563 -0.574 l
+22.915 -0.574 l
+22.915 3.41 l
+23.548 3.41 l
+23.563 2.998 l
+23.739 3.322 23.981 3.484 24.297 3.484 c
+24.404 3.484 24.492 3.461 24.562 3.424 c
+h
+27.105 -0.574 m
+27.064 -0.485 27.039 -0.339 27.031 -0.133 c
+26.796 -0.478 26.503 -0.647 26.15 -0.647 c
+25.786 -0.647 25.503 -0.551 25.296 -0.353 c
+25.098 -0.148 25.003 0.139 25.003 0.515 c
+25.003 0.914 25.138 1.234 25.414 1.469 c
+25.686 1.712 26.061 1.837 26.532 1.837 c
+27.016 1.837 l
+27.016 2.263 l
+27.016 2.499 26.962 2.664 26.855 2.763 c
+26.744 2.869 26.582 2.925 26.37 2.925 c
+26.171 2.925 26.01 2.866 25.884 2.749 c
+25.767 2.631 25.709 2.484 25.709 2.308 c
+25.061 2.308 l
+25.061 2.502 25.121 2.693 25.238 2.881 c
+25.363 3.064 25.525 3.212 25.723 3.322 c
+25.929 3.428 26.156 3.484 26.414 3.484 c
+26.815 3.484 27.12 3.38 27.326 3.175 c
+27.538 2.969 27.652 2.674 27.663 2.293 c
+27.663 0.279 l
+27.663 -0.026 27.7 -0.291 27.781 -0.515 c
+27.781 -0.574 l
+h
+26.237 -0.059 m
+26.403 -0.059 26.553 -0.015 26.693 0.073 c
+26.84 0.162 26.947 0.272 27.016 0.411 c
+27.016 1.352 l
+26.649 1.352 l
+26.333 1.352 26.09 1.282 25.915 1.146 c
+25.738 1.018 25.649 0.83 25.649 0.588 c
+25.649 0.359 25.693 0.195 25.782 0.087 c
+25.87 -0.011 26.021 -0.059 26.237 -0.059 c
+29.516 4.365 m
+29.516 3.41 l
+30.118 3.41 l
+30.118 2.881 l
+29.516 2.881 l
+29.516 0.411 l
+29.516 0.253 29.537 0.135 29.589 0.058 c
+29.647 -0.023 29.736 -0.059 29.853 -0.059 c
+29.942 -0.059 30.03 -0.044 30.118 -0.015 c
+30.118 -0.574 l
+29.971 -0.621 29.817 -0.647 29.662 -0.647 c
+29.405 -0.647 29.21 -0.555 29.074 -0.368 c
+28.934 -0.184 28.868 0.077 28.868 0.411 c
+28.868 2.881 l
+28.266 2.881 l
+28.266 3.41 l
+28.868 3.41 l
+28.868 4.365 l
+h
+31.588 -0.574 -0.647 3.984 re
+31.632 4.453 m
+31.632 4.343 31.602 4.251 31.544 4.174 c
+31.485 4.104 31.39 4.072 31.265 4.072 c
+31.147 4.072 31.051 4.104 30.985 4.174 c
+30.927 4.251 30.897 4.343 30.897 4.453 c
+30.897 4.571 30.927 4.663 30.985 4.733 c
+31.051 4.81 31.147 4.85 31.265 4.85 c
+31.39 4.85 31.485 4.81 31.544 4.733 c
+31.602 4.652 31.632 4.56 31.632 4.453 c
+32.47 1.602 m
+32.47 2.179 32.606 2.634 32.882 2.969 c
+33.165 3.31 33.535 3.484 33.998 3.484 c
+34.458 3.484 34.825 3.314 35.101 2.984 c
+35.384 2.66 35.531 2.212 35.542 1.646 c
+35.542 1.22 l
+35.542 0.65 35.399 0.195 35.116 -0.148 c
+34.84 -0.482 34.472 -0.647 34.013 -0.647 c
+33.551 -0.647 33.179 -0.485 32.896 -0.162 c
+32.62 0.168 32.477 0.61 32.47 1.161 c
+h
+33.117 1.22 m
+33.117 0.816 33.194 0.5 33.352 0.264 c
+33.518 0.029 33.738 -0.088 34.013 -0.088 c
+34.579 -0.088 34.873 0.323 34.895 1.146 c
+34.895 1.602 l
+34.895 2.003 34.811 2.322 34.646 2.557 c
+34.488 2.8 34.27 2.925 33.998 2.925 c
+33.734 2.925 33.518 2.8 33.352 2.557 c
+33.194 2.322 33.117 2.003 33.117 1.602 c
+h
+36.997 3.41 m
+37.012 2.969 l
+37.266 3.31 37.589 3.484 37.982 3.484 c
+38.688 3.484 39.044 3.013 39.055 2.072 c
+39.055 -0.574 l
+38.409 -0.574 l
+38.409 2.043 l
+38.409 2.356 38.353 2.576 38.247 2.705 c
+38.137 2.829 37.982 2.896 37.776 2.896 c
+37.618 2.896 37.472 2.84 37.335 2.734 c
+37.206 2.624 37.104 2.487 37.027 2.322 c
+37.027 -0.574 l
+36.38 -0.574 l
+36.38 3.41 l
+h
+41.965 0.44 m
+41.965 0.588 41.91 0.709 41.804 0.808 c
+41.694 0.904 41.488 1.022 41.187 1.161 c
+40.841 1.308 40.599 1.429 40.452 1.529 c
+40.304 1.635 40.194 1.753 40.128 1.881 c
+40.059 2.006 40.026 2.164 40.026 2.352 c
+40.026 2.674 40.143 2.944 40.378 3.16 c
+40.614 3.374 40.915 3.484 41.289 3.484 c
+41.672 3.484 41.98 3.37 42.216 3.145 c
+42.451 2.917 42.568 2.631 42.568 2.278 c
+41.921 2.278 l
+41.921 2.454 41.863 2.605 41.745 2.734 c
+41.628 2.859 41.473 2.925 41.289 2.925 c
+41.091 2.925 40.94 2.869 40.834 2.763 c
+40.724 2.664 40.672 2.532 40.672 2.366 c
+40.672 2.238 40.709 2.131 40.79 2.043 c
+40.867 1.962 41.058 1.859 41.362 1.734 c
+41.84 1.547 42.171 1.359 42.347 1.176 c
+42.524 0.999 42.613 0.771 42.613 0.5 c
+42.613 0.147 42.488 -0.133 42.245 -0.339 c
+42.01 -0.544 41.694 -0.647 41.304 -0.647 c
+40.882 -0.647 40.543 -0.53 40.29 -0.294 c
+40.032 -0.052 39.908 0.253 39.908 0.617 c
+40.554 0.617 l
+40.562 0.389 40.631 0.213 40.76 0.087 c
+40.885 -0.03 41.069 -0.088 41.304 -0.088 c
+41.518 -0.088 41.679 -0.04 41.79 0.058 c
+41.907 0.154 41.965 0.282 41.965 0.44 c
+45.156 1.602 m
+45.156 2.238 45.243 2.84 45.42 3.41 c
+45.596 3.976 45.838 4.472 46.155 4.895 c
+46.349 5.159 46.536 5.35 46.713 5.468 c
+46.846 5.012 l
+46.552 4.737 46.309 4.314 46.125 3.748 c
+45.938 3.179 45.835 2.547 45.817 1.851 c
+45.817 1.558 l
+45.817 0.694 45.934 -0.071 46.17 -0.736 c
+46.353 -1.235 46.581 -1.617 46.846 -1.881 c
+46.713 -2.308 l
+46.486 -2.15 46.261 -1.9 46.037 -1.559 c
+45.449 -0.676 45.156 0.374 45.156 1.602 c
+48.242 -0.574 -0.647 5.644 re
+49.962 -0.574 -0.646 3.984 re
+50.006 4.453 m
+50.006 4.343 49.977 4.251 49.918 4.174 c
+49.859 4.104 49.763 4.072 49.638 4.072 c
+49.521 4.072 49.426 4.104 49.359 4.174 c
+49.3 4.251 49.271 4.343 49.271 4.453 c
+49.271 4.571 49.3 4.663 49.359 4.733 c
+49.426 4.81 49.521 4.85 49.638 4.85 c
+49.763 4.85 49.859 4.81 49.918 4.733 c
+49.977 4.652 50.006 4.56 50.006 4.453 c
+51.976 1.249 m
+51.637 0.852 l
+51.637 -0.574 l
+50.976 -0.574 l
+50.976 5.071 l
+51.637 5.071 l
+51.637 1.691 l
+52.872 3.41 l
+53.651 3.41 l
+52.387 1.749 l
+53.813 -0.574 l
+53.063 -0.574 l
+h
+55.724 -0.647 m
+55.224 -0.647 54.842 -0.5 54.577 -0.206 c
+54.313 0.087 54.18 0.521 54.18 1.103 c
+54.18 1.573 l
+54.18 2.167 54.305 2.634 54.563 2.969 c
+54.827 3.31 55.188 3.484 55.65 3.484 c
+56.109 3.484 56.451 3.329 56.68 3.027 c
+56.915 2.734 57.036 2.271 57.046 1.646 c
+57.046 1.22 l
+54.827 1.22 l
+54.827 1.132 l
+54.827 0.698 54.904 0.386 55.062 0.191 c
+55.228 0.004 55.459 -0.088 55.753 -0.088 c
+55.948 -0.088 56.121 -0.055 56.268 0.014 c
+56.414 0.091 56.551 0.209 56.68 0.367 c
+57.017 -0.044 l
+56.73 -0.449 56.3 -0.647 55.724 -0.647 c
+55.65 2.925 m
+55.375 2.925 55.172 2.829 55.047 2.645 c
+54.919 2.458 54.845 2.167 54.827 1.778 c
+56.4 1.778 l
+56.4 1.866 l
+56.378 2.248 56.312 2.516 56.194 2.674 c
+56.077 2.84 55.893 2.925 55.65 2.925 c
+60.927 -0.088 m
+61.141 -0.088 61.313 -0.026 61.442 0.103 c
+61.578 0.239 61.651 0.43 61.663 0.675 c
+62.28 0.675 l
+62.257 0.294 62.122 -0.026 61.868 -0.279 c
+61.611 -0.526 61.299 -0.647 60.927 -0.647 c
+60.435 -0.647 60.06 -0.497 59.795 -0.191 c
+59.538 0.121 59.413 0.588 59.413 1.205 c
+59.413 1.646 l
+59.413 2.241 59.538 2.697 59.795 3.013 c
+60.06 3.326 60.435 3.484 60.927 3.484 c
+61.328 3.484 61.648 3.351 61.883 3.087 c
+62.125 2.829 62.257 2.484 62.28 2.043 c
+61.663 2.043 l
+61.64 2.337 61.567 2.557 61.442 2.705 c
+61.324 2.851 61.152 2.925 60.927 2.925 c
+60.633 2.925 60.416 2.826 60.281 2.631 c
+60.141 2.443 60.067 2.135 60.06 1.705 c
+60.06 1.19 l
+60.06 0.72 60.126 0.386 60.266 0.191 c
+60.412 0.004 60.633 -0.088 60.927 -0.088 c
+63.676 2.998 m
+63.93 3.322 64.25 3.484 64.632 3.484 c
+65.337 3.484 65.694 3.013 65.704 2.072 c
+65.704 -0.574 l
+65.058 -0.574 l
+65.058 2.043 l
+65.058 2.356 65.002 2.576 64.896 2.705 c
+64.786 2.829 64.632 2.896 64.426 2.896 c
+64.268 2.896 64.121 2.84 63.984 2.734 c
+63.856 2.624 63.753 2.487 63.676 2.322 c
+63.676 -0.574 l
+63.03 -0.574 l
+63.03 5.071 l
+63.676 5.071 l
+h
+68.086 -0.647 m
+67.586 -0.647 67.204 -0.5 66.939 -0.206 c
+66.675 0.087 66.542 0.521 66.542 1.103 c
+66.542 1.573 l
+66.542 2.167 66.667 2.634 66.924 2.969 c
+67.189 3.31 67.549 3.484 68.013 3.484 c
+68.472 3.484 68.813 3.329 69.041 3.027 c
+69.276 2.734 69.397 2.271 69.409 1.646 c
+69.409 1.22 l
+67.189 1.22 l
+67.189 1.132 l
+67.189 0.698 67.267 0.386 67.425 0.191 c
+67.589 0.004 67.821 -0.088 68.115 -0.088 c
+68.31 -0.088 68.483 -0.055 68.63 0.014 c
+68.777 0.091 68.913 0.209 69.041 0.367 c
+69.38 -0.044 l
+69.093 -0.449 68.663 -0.647 68.086 -0.647 c
+68.013 2.925 m
+67.737 2.925 67.535 2.829 67.41 2.645 c
+67.281 2.458 67.207 2.167 67.189 1.778 c
+68.761 1.778 l
+68.761 1.866 l
+68.74 2.248 68.674 2.516 68.556 2.674 c
+68.439 2.84 68.254 2.925 68.013 2.925 c
+71.57 -0.088 m
+71.782 -0.088 71.955 -0.026 72.084 0.103 c
+72.22 0.239 72.293 0.43 72.304 0.675 c
+72.921 0.675 l
+72.9 0.294 72.763 -0.026 72.51 -0.279 c
+72.253 -0.526 71.94 -0.647 71.57 -0.647 c
+71.077 -0.647 70.702 -0.497 70.438 -0.191 c
+70.18 0.121 70.055 0.588 70.055 1.205 c
+70.055 1.646 l
+70.055 2.241 70.18 2.697 70.438 3.013 c
+70.702 3.326 71.077 3.484 71.57 3.484 c
+71.97 3.484 72.289 3.351 72.524 3.087 c
+72.767 2.829 72.9 2.484 72.921 2.043 c
+72.304 2.043 l
+72.283 2.337 72.208 2.557 72.084 2.705 c
+71.967 2.851 71.793 2.925 71.57 2.925 c
+71.275 2.925 71.059 2.826 70.922 2.631 c
+70.783 2.443 70.71 2.135 70.702 1.705 c
+70.702 1.19 l
+70.702 0.72 70.768 0.386 70.908 0.191 c
+71.055 0.004 71.275 -0.088 71.57 -0.088 c
+74.671 1.249 m
+74.333 0.852 l
+74.333 -0.574 l
+73.671 -0.574 l
+73.671 5.071 l
+74.333 5.071 l
+74.333 1.691 l
+75.568 3.41 l
+76.347 3.41 l
+75.082 1.749 l
+76.508 -0.574 l
+75.759 -0.574 l
+h
+76.935 1.602 m
+76.935 2.179 77.07 2.634 77.346 2.969 c
+77.629 3.31 78 3.484 78.463 3.484 c
+78.922 3.484 79.29 3.314 79.566 2.984 c
+79.849 2.66 79.996 2.212 80.007 1.646 c
+80.007 1.22 l
+80.007 0.65 79.863 0.195 79.58 -0.148 c
+79.304 -0.482 78.937 -0.647 78.478 -0.647 c
+78.015 -0.647 77.644 -0.485 77.361 -0.162 c
+77.085 0.168 76.942 0.61 76.935 1.161 c
+h
+77.581 1.22 m
+77.581 0.816 77.658 0.5 77.816 0.264 c
+77.982 0.029 78.203 -0.088 78.478 -0.088 c
+79.044 -0.088 79.338 0.323 79.36 1.146 c
+79.36 1.602 l
+79.36 2.003 79.275 2.322 79.11 2.557 c
+78.952 2.8 78.735 2.925 78.463 2.925 c
+78.199 2.925 77.982 2.8 77.816 2.557 c
+77.658 2.322 77.581 2.003 77.581 1.602 c
+h
+82.873 -0.221 m
+82.656 -0.507 82.344 -0.647 81.932 -0.647 c
+81.569 -0.647 81.293 -0.526 81.109 -0.279 c
+80.933 -0.026 80.837 0.338 80.83 0.808 c
+80.83 3.41 l
+81.477 3.41 l
+81.477 0.867 l
+81.477 0.239 81.66 -0.073 82.035 -0.073 c
+82.436 -0.073 82.712 0.103 82.858 0.455 c
+82.858 3.41 l
+83.505 3.41 l
+83.505 -0.574 l
+82.888 -0.574 l
+h
+85.342 4.365 m
+85.342 3.41 l
+85.945 3.41 l
+85.945 2.881 l
+85.342 2.881 l
+85.342 0.411 l
+85.342 0.253 85.365 0.135 85.416 0.058 c
+85.475 -0.023 85.563 -0.059 85.681 -0.059 c
+85.769 -0.059 85.857 -0.044 85.945 -0.015 c
+85.945 -0.574 l
+85.798 -0.621 85.644 -0.647 85.49 -0.647 c
+85.232 -0.647 85.037 -0.555 84.902 -0.368 c
+84.762 -0.184 84.696 0.077 84.696 0.411 c
+84.696 2.881 l
+84.093 2.881 l
+84.093 3.41 l
+84.696 3.41 l
+84.696 4.365 l
+h
+88.65 0.44 m
+88.65 0.588 88.594 0.709 88.488 0.808 c
+88.378 0.904 88.172 1.022 87.871 1.161 c
+87.525 1.308 87.283 1.429 87.136 1.529 c
+86.988 1.635 86.878 1.753 86.812 1.881 c
+86.743 2.006 86.71 2.164 86.71 2.352 c
+86.71 2.674 86.827 2.944 87.063 3.16 c
+87.298 3.374 87.599 3.484 87.973 3.484 c
+88.356 3.484 88.665 3.37 88.9 3.145 c
+89.135 2.917 89.253 2.631 89.253 2.278 c
+88.605 2.278 l
+88.605 2.454 88.547 2.605 88.429 2.734 c
+88.312 2.859 88.158 2.925 87.973 2.925 c
+87.775 2.925 87.624 2.869 87.518 2.763 c
+87.408 2.664 87.356 2.532 87.356 2.366 c
+87.356 2.238 87.393 2.131 87.474 2.043 c
+87.551 1.962 87.742 1.859 88.047 1.734 c
+88.525 1.547 88.856 1.359 89.032 1.176 c
+89.208 0.999 89.297 0.771 89.297 0.5 c
+89.297 0.147 89.172 -0.133 88.929 -0.339 c
+88.694 -0.544 88.378 -0.647 87.988 -0.647 c
+87.566 -0.647 87.227 -0.53 86.974 -0.294 c
+86.716 -0.052 86.592 0.253 86.592 0.617 c
+87.239 0.617 l
+87.246 0.389 87.316 0.213 87.445 0.087 c
+87.57 -0.03 87.753 -0.088 87.988 -0.088 c
+88.201 -0.088 88.363 -0.04 88.474 0.058 c
+88.591 0.154 88.65 0.282 88.65 0.44 c
+90.149 -1.646 m
+89.752 -1.382 l
+89.987 -1.058 90.109 -0.724 90.12 -0.383 c
+90.12 0.235 l
+90.781 0.235 l
+90.781 -0.294 l
+90.781 -0.551 90.715 -0.798 90.59 -1.044 c
+90.473 -1.287 90.325 -1.488 90.149 -1.646 c
+94.72 -0.088 m
+94.934 -0.088 95.106 -0.026 95.235 0.103 c
+95.371 0.239 95.445 0.43 95.455 0.675 c
+96.073 0.675 l
+96.05 0.294 95.915 -0.026 95.661 -0.279 c
+95.404 -0.526 95.092 -0.647 94.72 -0.647 c
+94.228 -0.647 93.853 -0.497 93.589 -0.191 c
+93.332 0.121 93.207 0.588 93.207 1.205 c
+93.207 1.646 l
+93.207 2.241 93.332 2.697 93.589 3.013 c
+93.853 3.326 94.228 3.484 94.72 3.484 c
+95.121 3.484 95.441 3.351 95.676 3.087 c
+95.919 2.829 96.05 2.484 96.073 2.043 c
+95.455 2.043 l
+95.433 2.337 95.36 2.557 95.235 2.705 c
+95.117 2.851 94.944 2.925 94.72 2.925 c
+94.427 2.925 94.209 2.826 94.074 2.631 c
+93.934 2.443 93.86 2.135 93.853 1.705 c
+93.853 1.19 l
+93.853 0.72 93.92 0.386 94.059 0.191 c
+94.206 0.004 94.427 -0.088 94.72 -0.088 c
+96.69 1.602 m
+96.69 2.179 96.826 2.634 97.101 2.969 c
+97.384 3.31 97.756 3.484 98.219 3.484 c
+98.678 3.484 99.046 3.314 99.321 2.984 c
+99.605 2.66 99.751 2.212 99.763 1.646 c
+99.763 1.22 l
+99.763 0.65 99.619 0.195 99.335 -0.148 c
+99.06 -0.482 98.693 -0.647 98.234 -0.647 c
+97.77 -0.647 97.399 -0.485 97.116 -0.162 c
+96.841 0.168 96.698 0.61 96.69 1.161 c
+h
+97.336 1.22 m
+97.336 0.816 97.414 0.5 97.572 0.264 c
+97.737 0.029 97.958 -0.088 98.234 -0.088 c
+98.799 -0.088 99.094 0.323 99.115 1.146 c
+99.115 1.602 l
+99.115 2.003 99.031 2.322 98.865 2.557 c
+98.707 2.8 98.491 2.925 98.219 2.925 c
+97.954 2.925 97.737 2.8 97.572 2.557 c
+97.414 2.322 97.336 2.003 97.336 1.602 c
+h
+101.217 3.41 m
+101.232 3.042 l
+101.475 3.337 101.795 3.484 102.188 3.484 c
+102.628 3.484 102.938 3.285 103.114 2.896 c
+103.368 3.285 103.717 3.484 104.157 3.484 c
+104.892 3.484 105.267 3.021 105.289 2.102 c
+105.289 -0.574 l
+104.642 -0.574 l
+104.642 2.043 l
+104.642 2.337 104.587 2.55 104.48 2.69 c
+104.382 2.826 104.209 2.896 103.966 2.896 c
+103.767 2.896 103.606 2.815 103.481 2.66 c
+103.364 2.514 103.293 2.322 103.275 2.087 c
+103.275 -0.574 l
+102.614 -0.574 l
+102.614 2.072 l
+102.614 2.62 102.393 2.896 101.953 2.896 c
+101.618 2.896 101.383 2.734 101.247 2.41 c
+101.247 -0.574 l
+100.6 -0.574 l
+100.6 3.41 l
+h
+106.876 3.41 m
+106.892 3.042 l
+107.134 3.337 107.453 3.484 107.846 3.484 c
+108.288 3.484 108.596 3.285 108.773 2.896 c
+109.026 3.285 109.375 3.484 109.816 3.484 c
+110.551 3.484 110.926 3.021 110.948 2.102 c
+110.948 -0.574 l
+110.302 -0.574 l
+110.302 2.043 l
+110.302 2.337 110.246 2.55 110.14 2.69 c
+110.04 2.826 109.868 2.896 109.625 2.896 c
+109.427 2.896 109.265 2.815 109.14 2.66 c
+109.022 2.514 108.953 2.322 108.935 2.087 c
+108.935 -0.574 l
+108.273 -0.574 l
+108.273 2.072 l
+108.273 2.62 108.052 2.896 107.611 2.896 c
+107.277 2.896 107.042 2.734 106.906 2.41 c
+106.906 -0.574 l
+106.259 -0.574 l
+106.259 3.41 l
+h
+112.623 -0.574 -0.646 3.984 re
+112.668 4.453 m
+112.668 4.343 112.638 4.251 112.58 4.174 c
+112.521 4.104 112.425 4.072 112.301 4.072 c
+112.183 4.072 112.087 4.104 112.021 4.174 c
+111.962 4.251 111.933 4.343 111.933 4.453 c
+111.933 4.571 111.962 4.663 112.021 4.733 c
+112.087 4.81 112.183 4.85 112.301 4.85 c
+112.425 4.85 112.521 4.81 112.58 4.733 c
+112.638 4.652 112.668 4.56 112.668 4.453 c
+114.491 4.365 m
+114.491 3.41 l
+115.093 3.41 l
+115.093 2.881 l
+114.491 2.881 l
+114.491 0.411 l
+114.491 0.253 114.512 0.135 114.564 0.058 c
+114.622 -0.023 114.711 -0.059 114.829 -0.059 c
+114.917 -0.059 115.005 -0.044 115.093 -0.015 c
+115.093 -0.574 l
+114.946 -0.621 114.792 -0.647 114.638 -0.647 c
+114.381 -0.647 114.186 -0.555 114.05 -0.368 c
+113.91 -0.184 113.844 0.077 113.844 0.411 c
+113.844 2.881 l
+113.242 2.881 l
+113.242 3.41 l
+113.844 3.41 l
+113.844 4.365 l
+h
+117.798 0.44 m
+117.798 0.588 117.743 0.709 117.637 0.808 c
+117.526 0.904 117.321 1.022 117.019 1.161 c
+116.673 1.308 116.431 1.429 116.284 1.529 c
+116.137 1.635 116.027 1.753 115.96 1.881 c
+115.891 2.006 115.858 2.164 115.858 2.352 c
+115.858 2.674 115.975 2.944 116.21 3.16 c
+116.446 3.374 116.747 3.484 117.122 3.484 c
+117.504 3.484 117.813 3.37 118.048 3.145 c
+118.283 2.917 118.401 2.631 118.401 2.278 c
+117.754 2.278 l
+117.754 2.454 117.695 2.605 117.577 2.734 c
+117.46 2.859 117.305 2.925 117.122 2.925 c
+116.924 2.925 116.773 2.869 116.666 2.763 c
+116.556 2.664 116.504 2.532 116.504 2.366 c
+116.504 2.238 116.542 2.131 116.622 2.043 c
+116.699 1.962 116.891 1.859 117.195 1.734 c
+117.673 1.547 118.004 1.359 118.18 1.176 c
+118.356 0.999 118.445 0.771 118.445 0.5 c
+118.445 0.147 118.32 -0.133 118.077 -0.339 c
+117.842 -0.544 117.526 -0.647 117.136 -0.647 c
+116.714 -0.647 116.376 -0.53 116.122 -0.294 c
+115.865 -0.052 115.74 0.253 115.74 0.617 c
+116.386 0.617 l
+116.394 0.389 116.464 0.213 116.592 0.087 c
+116.718 -0.03 116.901 -0.088 117.136 -0.088 c
+117.35 -0.088 117.512 -0.04 117.622 0.058 c
+117.739 0.154 117.798 0.282 117.798 0.44 c
+122.428 -0.647 m
+121.928 -0.647 121.546 -0.5 121.282 -0.206 c
+121.017 0.087 120.885 0.521 120.885 1.103 c
+120.885 1.573 l
+120.885 2.167 121.01 2.634 121.267 2.969 c
+121.531 3.31 121.892 3.484 122.354 3.484 c
+122.814 3.484 123.156 3.329 123.384 3.027 c
+123.619 2.734 123.74 2.271 123.751 1.646 c
+123.751 1.22 l
+121.531 1.22 l
+121.531 1.132 l
+121.531 0.698 121.608 0.386 121.766 0.191 c
+121.932 0.004 122.163 -0.088 122.458 -0.088 c
+122.653 -0.088 122.825 -0.055 122.972 0.014 c
+123.119 0.091 123.255 0.209 123.384 0.367 c
+123.721 -0.044 l
+123.435 -0.449 123.005 -0.647 122.428 -0.647 c
+122.354 2.925 m
+122.079 2.925 121.877 2.829 121.752 2.645 c
+121.623 2.458 121.55 2.167 121.531 1.778 c
+123.104 1.778 l
+123.104 1.866 l
+123.083 2.248 123.016 2.516 122.898 2.674 c
+122.781 2.84 122.597 2.925 122.354 2.925 c
+125.383 4.365 m
+125.383 3.41 l
+125.986 3.41 l
+125.986 2.881 l
+125.383 2.881 l
+125.383 0.411 l
+125.383 0.253 125.404 0.135 125.456 0.058 c
+125.515 -0.023 125.603 -0.059 125.72 -0.059 c
+125.809 -0.059 125.897 -0.044 125.986 -0.015 c
+125.986 -0.574 l
+125.838 -0.621 125.684 -0.647 125.529 -0.647 c
+125.273 -0.647 125.078 -0.555 124.941 -0.368 c
+124.802 -0.184 124.735 0.077 124.735 0.411 c
+124.735 2.881 l
+124.133 2.881 l
+124.133 3.41 l
+124.735 3.41 l
+124.735 4.365 l
+h
+128.132 -0.088 m
+128.344 -0.088 128.517 -0.026 128.646 0.103 c
+128.782 0.239 128.855 0.43 128.866 0.675 c
+129.483 0.675 l
+129.462 0.294 129.325 -0.026 129.072 -0.279 c
+128.814 -0.526 128.502 -0.647 128.132 -0.647 c
+127.639 -0.647 127.264 -0.497 127 -0.191 c
+126.742 0.121 126.617 0.588 126.617 1.205 c
+126.617 1.646 l
+126.617 2.241 126.742 2.697 127 3.013 c
+127.264 3.326 127.639 3.484 128.132 3.484 c
+128.532 3.484 128.851 3.351 129.086 3.087 c
+129.329 2.829 129.462 2.484 129.483 2.043 c
+128.866 2.043 l
+128.845 2.337 128.771 2.557 128.646 2.705 c
+128.529 2.851 128.355 2.925 128.132 2.925 c
+127.837 2.925 127.621 2.826 127.484 2.631 c
+127.345 2.443 127.272 2.135 127.264 1.705 c
+127.264 1.19 l
+127.264 0.72 127.33 0.386 127.47 0.191 c
+127.617 0.004 127.837 -0.088 128.132 -0.088 c
+130.277 -0.221 m
+130.277 -0.103 130.31 -0.008 130.38 0.073 c
+130.447 0.151 130.549 0.191 130.689 0.191 c
+130.836 0.191 130.942 0.151 131.012 0.073 c
+131.089 -0.008 131.13 -0.103 131.13 -0.221 c
+131.13 -0.331 131.089 -0.423 131.012 -0.5 c
+130.942 -0.578 130.836 -0.618 130.689 -0.618 c
+130.549 -0.618 130.447 -0.578 130.38 -0.5 c
+130.31 -0.423 130.277 -0.331 130.277 -0.221 c
+133.614 1.558 m
+133.614 0.47 133.368 -0.497 132.879 -1.338 c
+132.614 -1.786 132.339 -2.11 132.056 -2.308 c
+131.939 -1.881 l
+132.24 -1.588 132.486 -1.135 132.673 -0.53 c
+132.868 0.077 132.967 0.742 132.967 1.469 c
+132.967 1.602 l
+132.967 2.532 132.813 3.366 132.512 4.101 c
+132.342 4.501 132.151 4.821 131.939 5.056 c
+132.056 5.468 l
+132.328 5.28 132.593 4.983 132.849 4.571 c
+133.356 3.719 133.614 2.711 133.614 1.558 c
+136.951 3.41 m
+136.965 3.042 l
+137.208 3.337 137.528 3.484 137.921 3.484 c
+138.362 3.484 138.671 3.285 138.847 2.896 c
+139.101 3.285 139.45 3.484 139.891 3.484 c
+140.625 3.484 141.001 3.021 141.022 2.102 c
+141.022 -0.574 l
+140.375 -0.574 l
+140.375 2.043 l
+140.375 2.337 140.321 2.55 140.214 2.69 c
+140.115 2.826 139.942 2.896 139.7 2.896 c
+139.501 2.896 139.339 2.815 139.214 2.66 c
+139.097 2.514 139.027 2.322 139.008 2.087 c
+139.008 -0.574 l
+138.347 -0.574 l
+138.347 2.072 l
+138.347 2.62 138.127 2.896 137.686 2.896 c
+137.352 2.896 137.117 2.734 136.98 2.41 c
+136.98 -0.574 l
+136.334 -0.574 l
+136.334 3.41 l
+h
+144.036 -0.574 m
+143.995 -0.485 143.97 -0.339 143.962 -0.133 c
+143.727 -0.478 143.433 -0.647 143.08 -0.647 c
+142.717 -0.647 142.434 -0.551 142.228 -0.353 c
+142.029 -0.148 141.934 0.139 141.934 0.515 c
+141.934 0.914 142.07 1.234 142.345 1.469 c
+142.617 1.712 142.992 1.837 143.463 1.837 c
+143.947 1.837 l
+143.947 2.263 l
+143.947 2.499 143.893 2.664 143.785 2.763 c
+143.675 2.869 143.514 2.925 143.301 2.925 c
+143.102 2.925 142.941 2.866 142.816 2.749 c
+142.698 2.631 142.64 2.484 142.64 2.308 c
+141.992 2.308 l
+141.992 2.502 142.052 2.693 142.169 2.881 c
+142.293 3.064 142.455 3.212 142.654 3.322 c
+142.86 3.428 143.087 3.484 143.345 3.484 c
+143.745 3.484 144.051 3.38 144.256 3.175 c
+144.469 2.969 144.583 2.674 144.594 2.293 c
+144.594 0.279 l
+144.594 -0.026 144.631 -0.291 144.712 -0.515 c
+144.712 -0.574 l
+h
+143.168 -0.059 m
+143.334 -0.059 143.484 -0.015 143.625 0.073 c
+143.771 0.162 143.878 0.272 143.947 0.411 c
+143.947 1.352 l
+143.58 1.352 l
+143.264 1.352 143.022 1.282 142.845 1.146 c
+142.669 1.018 142.58 0.83 142.58 0.588 c
+142.58 0.359 142.625 0.195 142.713 0.087 c
+142.801 -0.011 142.952 -0.059 143.168 -0.059 c
+145.462 1.602 m
+145.462 2.208 145.572 2.674 145.8 2.998 c
+146.035 3.322 146.362 3.484 146.785 3.484 c
+147.166 3.484 147.465 3.326 147.681 3.013 c
+147.681 5.071 l
+148.328 5.071 l
+148.328 -0.574 l
+147.74 -0.574 l
+147.696 -0.148 l
+147.49 -0.482 147.185 -0.647 146.785 -0.647 c
+146.372 -0.647 146.05 -0.493 145.815 -0.177 c
+145.579 0.147 145.462 0.602 145.462 1.19 c
+h
+146.108 1.22 m
+146.108 0.779 146.171 0.448 146.299 0.235 c
+146.435 0.029 146.656 -0.073 146.96 -0.073 c
+147.284 -0.073 147.523 0.087 147.681 0.411 c
+147.681 2.425 l
+147.512 2.738 147.273 2.896 146.96 2.896 c
+146.656 2.896 146.435 2.792 146.299 2.587 c
+146.171 2.381 146.108 2.057 146.108 1.616 c
+h
+150.753 -0.647 m
+150.253 -0.647 149.871 -0.5 149.607 -0.206 c
+149.342 0.087 149.21 0.521 149.21 1.103 c
+149.21 1.573 l
+149.21 2.167 149.335 2.634 149.592 2.969 c
+149.857 3.31 150.216 3.484 150.68 3.484 c
+151.139 3.484 151.481 3.329 151.708 3.027 c
+151.944 2.734 152.065 2.271 152.076 1.646 c
+152.076 1.22 l
+149.857 1.22 l
+149.857 1.132 l
+149.857 0.698 149.934 0.386 150.092 0.191 c
+150.257 0.004 150.488 -0.088 150.783 -0.088 c
+150.977 -0.088 151.15 -0.055 151.297 0.014 c
+151.444 0.091 151.58 0.209 151.708 0.367 c
+152.047 -0.044 l
+151.76 -0.449 151.33 -0.647 150.753 -0.647 c
+150.68 2.925 m
+150.404 2.925 150.202 2.829 150.077 2.645 c
+149.948 2.458 149.875 2.167 149.857 1.778 c
+151.429 1.778 l
+151.429 1.866 l
+151.407 2.248 151.341 2.516 151.224 2.674 c
+151.106 2.84 150.922 2.925 150.68 2.925 c
+154.443 1.602 m
+154.443 2.179 154.579 2.634 154.854 2.969 c
+155.137 3.31 155.508 3.484 155.972 3.484 c
+156.431 3.484 156.798 3.314 157.073 2.984 c
+157.357 2.66 157.503 2.212 157.515 1.646 c
+157.515 1.22 l
+157.515 0.65 157.372 0.195 157.088 -0.148 c
+156.813 -0.482 156.445 -0.647 155.986 -0.647 c
+155.523 -0.647 155.151 -0.485 154.869 -0.162 c
+154.594 0.168 154.45 0.61 154.443 1.161 c
+h
+155.089 1.22 m
+155.089 0.816 155.166 0.5 155.325 0.264 c
+155.49 0.029 155.71 -0.088 155.986 -0.088 c
+156.552 -0.088 156.846 0.323 156.868 1.146 c
+156.868 1.602 l
+156.868 2.003 156.784 2.322 156.618 2.557 c
+156.46 2.8 156.244 2.925 155.972 2.925 c
+155.706 2.925 155.49 2.8 155.325 2.557 c
+155.166 2.322 155.089 2.003 155.089 1.602 c
+h
+158.97 3.41 m
+158.985 2.969 l
+159.238 3.31 159.562 3.484 159.955 3.484 c
+160.66 3.484 161.017 3.013 161.028 2.072 c
+161.028 -0.574 l
+160.381 -0.574 l
+160.381 2.043 l
+160.381 2.356 160.326 2.576 160.219 2.705 c
+160.109 2.829 159.955 2.896 159.749 2.896 c
+159.591 2.896 159.444 2.84 159.308 2.734 c
+159.18 2.624 159.076 2.487 158.999 2.322 c
+158.999 -0.574 l
+158.353 -0.574 l
+158.353 3.41 l
+h
+164.423 -0.574 -0.647 5.644 re
+165.305 1.602 m
+165.305 2.179 165.441 2.634 165.717 2.969 c
+166 3.31 166.371 3.484 166.834 3.484 c
+167.294 3.484 167.66 3.314 167.936 2.984 c
+168.219 2.66 168.366 2.212 168.377 1.646 c
+168.377 1.22 l
+168.377 0.65 168.234 0.195 167.951 -0.148 c
+167.676 -0.482 167.308 -0.647 166.849 -0.647 c
+166.386 -0.647 166.014 -0.485 165.732 -0.162 c
+165.456 0.168 165.312 0.61 165.305 1.161 c
+h
+165.952 1.22 m
+165.952 0.816 166.029 0.5 166.187 0.264 c
+166.353 0.029 166.573 -0.088 166.849 -0.088 c
+167.415 -0.088 167.708 0.323 167.731 1.146 c
+167.731 1.602 l
+167.731 2.003 167.646 2.322 167.481 2.557 c
+167.323 2.8 167.106 2.925 166.834 2.925 c
+166.569 2.925 166.353 2.8 166.187 2.557 c
+166.029 2.322 165.952 2.003 165.952 1.602 c
+h
+170.596 -0.088 m
+170.81 -0.088 170.983 -0.026 171.111 0.103 c
+171.247 0.239 171.321 0.43 171.332 0.675 c
+171.949 0.675 l
+171.927 0.294 171.791 -0.026 171.537 -0.279 c
+171.28 -0.526 170.968 -0.647 170.596 -0.647 c
+170.104 -0.647 169.73 -0.497 169.465 -0.191 c
+169.208 0.121 169.083 0.588 169.083 1.205 c
+169.083 1.646 l
+169.083 2.241 169.208 2.697 169.465 3.013 c
+169.73 3.326 170.104 3.484 170.596 3.484 c
+170.997 3.484 171.317 3.351 171.552 3.087 c
+171.795 2.829 171.927 2.484 171.949 2.043 c
+171.332 2.043 l
+171.31 2.337 171.236 2.557 171.111 2.705 c
+170.993 2.851 170.821 2.925 170.596 2.925 c
+170.303 2.925 170.086 2.826 169.95 2.631 c
+169.81 2.443 169.737 2.135 169.73 1.705 c
+169.73 1.19 l
+169.73 0.72 169.796 0.386 169.935 0.191 c
+170.083 0.004 170.303 -0.088 170.596 -0.088 c
+174.742 -0.574 m
+174.702 -0.485 174.676 -0.339 174.669 -0.133 c
+174.434 -0.478 174.139 -0.647 173.787 -0.647 c
+173.422 -0.647 173.14 -0.551 172.934 -0.353 c
+172.736 -0.148 172.64 0.139 172.64 0.515 c
+172.64 0.914 172.776 1.234 173.052 1.469 c
+173.324 1.712 173.698 1.837 174.169 1.837 c
+174.654 1.837 l
+174.654 2.263 l
+174.654 2.499 174.598 2.664 174.492 2.763 c
+174.382 2.869 174.22 2.925 174.007 2.925 c
+173.808 2.925 173.647 2.866 173.522 2.749 c
+173.405 2.631 173.345 2.484 173.345 2.308 c
+172.699 2.308 l
+172.699 2.502 172.757 2.693 172.875 2.881 c
+173 3.064 173.162 3.212 173.36 3.322 c
+173.566 3.428 173.794 3.484 174.051 3.484 c
+174.452 3.484 174.756 3.38 174.962 3.175 c
+175.176 2.969 175.29 2.674 175.3 2.293 c
+175.3 0.279 l
+175.3 -0.026 175.338 -0.291 175.418 -0.515 c
+175.418 -0.574 l
+h
+173.875 -0.059 m
+174.041 -0.059 174.191 -0.015 174.33 0.073 c
+174.477 0.162 174.584 0.272 174.654 0.411 c
+174.654 1.352 l
+174.286 1.352 l
+173.97 1.352 173.728 1.282 173.551 1.146 c
+173.375 1.018 173.287 0.83 173.287 0.588 c
+173.287 0.359 173.331 0.195 173.419 0.087 c
+173.507 -0.011 173.658 -0.059 173.875 -0.059 c
+177.006 -0.574 -0.647 5.644 re
+181.386 2.792 m
+181.297 2.811 181.199 2.822 181.092 2.822 c
+180.757 2.822 180.522 2.638 180.387 2.278 c
+180.387 -0.574 l
+179.74 -0.574 l
+179.74 3.41 l
+180.372 3.41 l
+180.387 2.998 l
+180.563 3.322 180.805 3.484 181.121 3.484 c
+181.228 3.484 181.316 3.461 181.386 3.424 c
+h
+183.385 -0.647 m
+182.885 -0.647 182.503 -0.5 182.238 -0.206 c
+181.974 0.087 181.842 0.521 181.842 1.103 c
+181.842 1.573 l
+181.842 2.167 181.966 2.634 182.224 2.969 c
+182.488 3.31 182.849 3.484 183.312 3.484 c
+183.771 3.484 184.113 3.329 184.341 3.027 c
+184.576 2.734 184.697 2.271 184.708 1.646 c
+184.708 1.22 l
+182.488 1.22 l
+182.488 1.132 l
+182.488 0.698 182.566 0.386 182.724 0.191 c
+182.889 0.004 183.121 -0.088 183.414 -0.088 c
+183.609 -0.088 183.782 -0.055 183.929 0.014 c
+184.076 0.091 184.212 0.209 184.341 0.367 c
+184.678 -0.044 l
+184.391 -0.449 183.962 -0.647 183.385 -0.647 c
+183.312 2.925 m
+183.036 2.925 182.834 2.829 182.709 2.645 c
+182.581 2.458 182.507 2.167 182.488 1.778 c
+184.061 1.778 l
+184.061 1.866 l
+184.04 2.248 183.973 2.516 183.855 2.674 c
+183.738 2.84 183.554 2.925 183.312 2.925 c
+188.368 1.22 m
+188.368 0.592 188.25 0.121 188.015 -0.191 c
+187.788 -0.497 187.471 -0.647 187.059 -0.647 c
+186.656 -0.647 186.347 -0.497 186.134 -0.191 c
+186.134 -2.103 l
+185.487 -2.103 l
+185.487 3.41 l
+186.075 3.41 l
+186.119 2.969 l
+186.332 3.31 186.641 3.484 187.045 3.484 c
+187.487 3.484 187.813 3.329 188.03 3.027 c
+188.243 2.722 188.357 2.267 188.368 1.66 c
+h
+187.722 1.602 m
+187.722 2.043 187.651 2.366 187.516 2.572 c
+187.376 2.786 187.155 2.896 186.854 2.896 c
+186.538 2.896 186.299 2.741 186.134 2.439 c
+186.134 0.367 l
+186.299 0.062 186.538 -0.088 186.854 -0.088 c
+187.148 -0.088 187.361 0.014 187.501 0.22 c
+187.637 0.434 187.71 0.764 187.722 1.205 c
+h
+189.074 1.602 m
+189.074 2.179 189.21 2.634 189.486 2.969 c
+189.768 3.31 190.139 3.484 190.602 3.484 c
+191.061 3.484 191.429 3.314 191.705 2.984 c
+191.988 2.66 192.135 2.212 192.146 1.646 c
+192.146 1.22 l
+192.146 0.65 192.002 0.195 191.72 -0.148 c
+191.444 -0.482 191.076 -0.647 190.617 -0.647 c
+190.154 -0.647 189.783 -0.485 189.5 -0.162 c
+189.224 0.168 189.081 0.61 189.074 1.161 c
+h
+189.721 1.22 m
+189.721 0.816 189.798 0.5 189.956 0.264 c
+190.121 0.029 190.342 -0.088 190.617 -0.088 c
+191.183 -0.088 191.477 0.323 191.499 1.146 c
+191.499 1.602 l
+191.499 2.003 191.414 2.322 191.249 2.557 c
+191.091 2.8 190.874 2.925 190.602 2.925 c
+190.338 2.925 190.121 2.8 189.956 2.557 c
+189.798 2.322 189.721 2.003 189.721 1.602 c
+h
+194.924 0.44 m
+194.924 0.588 194.869 0.709 194.762 0.808 c
+194.652 0.904 194.446 1.022 194.145 1.161 c
+193.799 1.308 193.557 1.429 193.409 1.529 c
+193.263 1.635 193.153 1.753 193.087 1.881 c
+193.016 2.006 192.983 2.164 192.983 2.352 c
+192.983 2.674 193.101 2.944 193.336 3.16 c
+193.571 3.374 193.873 3.484 194.248 3.484 c
+194.63 3.484 194.938 3.37 195.173 3.145 c
+195.409 2.917 195.526 2.631 195.526 2.278 c
+194.88 2.278 l
+194.88 2.454 194.821 2.605 194.703 2.734 c
+194.585 2.859 194.431 2.925 194.248 2.925 c
+194.049 2.925 193.899 2.869 193.792 2.763 c
+193.682 2.664 193.63 2.532 193.63 2.366 c
+193.63 2.238 193.667 2.131 193.748 2.043 c
+193.825 1.962 194.016 1.859 194.321 1.734 c
+194.799 1.547 195.13 1.359 195.306 1.176 c
+195.483 0.999 195.57 0.771 195.57 0.5 c
+195.57 0.147 195.445 -0.133 195.203 -0.339 c
+194.968 -0.544 194.652 -0.647 194.263 -0.647 c
+193.839 -0.647 193.502 -0.53 193.249 -0.294 c
+192.991 -0.052 192.866 0.253 192.866 0.617 c
+193.513 0.617 l
+193.52 0.389 193.59 0.213 193.719 0.087 c
+193.843 -0.03 194.027 -0.088 194.263 -0.088 c
+194.475 -0.088 194.637 -0.04 194.747 0.058 c
+194.865 0.154 194.924 0.282 194.924 0.44 c
+197.114 -0.574 -0.647 3.984 re
+197.158 4.453 m
+197.158 4.343 197.129 4.251 197.07 4.174 c
+197.012 4.104 196.916 4.072 196.79 4.072 c
+196.673 4.072 196.578 4.104 196.511 4.174 c
+196.453 4.251 196.424 4.343 196.424 4.453 c
+196.424 4.571 196.453 4.663 196.511 4.733 c
+196.578 4.81 196.673 4.85 196.79 4.85 c
+196.916 4.85 197.012 4.81 197.07 4.733 c
+197.129 4.652 197.158 4.56 197.158 4.453 c
+198.98 4.365 m
+198.98 3.41 l
+199.584 3.41 l
+199.584 2.881 l
+198.98 2.881 l
+198.98 0.411 l
+198.98 0.253 199.003 0.135 199.054 0.058 c
+199.113 -0.023 199.202 -0.059 199.319 -0.059 c
+199.407 -0.059 199.495 -0.044 199.584 -0.015 c
+199.584 -0.574 l
+199.437 -0.621 199.282 -0.647 199.128 -0.647 c
+198.87 -0.647 198.676 -0.555 198.54 -0.368 c
+198.4 -0.184 198.334 0.077 198.334 0.411 c
+198.334 2.881 l
+197.731 2.881 l
+197.731 3.41 l
+198.334 3.41 l
+198.334 4.365 l
+h
+200.142 1.602 m
+200.142 2.179 200.278 2.634 200.553 2.969 c
+200.837 3.31 201.208 3.484 201.671 3.484 c
+202.13 3.484 202.498 3.314 202.774 2.984 c
+203.056 2.66 203.203 2.212 203.214 1.646 c
+203.214 1.22 l
+203.214 0.65 203.071 0.195 202.788 -0.148 c
+202.512 -0.482 202.145 -0.647 201.685 -0.647 c
+201.222 -0.647 200.852 -0.485 200.568 -0.162 c
+200.293 0.168 200.149 0.61 200.142 1.161 c
+h
+200.789 1.22 m
+200.789 0.816 200.866 0.5 201.024 0.264 c
+201.189 0.029 201.41 -0.088 201.685 -0.088 c
+202.251 -0.088 202.545 0.323 202.568 1.146 c
+202.568 1.602 l
+202.568 2.003 202.483 2.322 202.317 2.557 c
+202.159 2.8 201.943 2.925 201.671 2.925 c
+201.406 2.925 201.189 2.8 201.024 2.557 c
+200.866 2.322 200.789 2.003 200.789 1.602 c
+h
+205.698 2.792 m
+205.61 2.811 205.511 2.822 205.404 2.822 c
+205.07 2.822 204.835 2.638 204.698 2.278 c
+204.698 -0.574 l
+204.052 -0.574 l
+204.052 3.41 l
+204.684 3.41 l
+204.698 2.998 l
+204.875 3.322 205.118 3.484 205.434 3.484 c
+205.54 3.484 205.629 3.461 205.698 3.424 c
+h
+207.506 0.515 m
+208.226 3.41 l
+208.918 3.41 l
+207.624 -1.132 l
+207.524 -1.474 207.381 -1.735 207.198 -1.912 c
+207.021 -2.087 206.819 -2.176 206.595 -2.176 c
+206.506 -2.176 206.393 -2.153 206.257 -2.117 c
+206.257 -1.573 l
+206.404 -1.588 l
+206.587 -1.588 206.734 -1.544 206.845 -1.455 c
+206.951 -1.367 207.04 -1.209 207.109 -0.985 c
+207.227 -0.544 l
+206.066 3.41 l
+206.772 3.41 l
+h
+209.197 -0.221 m
+209.197 -0.103 209.23 -0.008 209.299 0.073 c
+209.365 0.151 209.469 0.191 209.608 0.191 c
+209.755 0.191 209.862 0.151 209.932 0.073 c
+210.009 -0.008 210.049 -0.103 210.049 -0.221 c
+210.049 -0.331 210.009 -0.423 209.932 -0.5 c
+209.862 -0.578 209.755 -0.618 209.608 -0.618 c
+209.469 -0.618 209.365 -0.578 209.299 -0.5 c
+209.23 -0.423 209.197 -0.331 209.197 -0.221 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+287.665 501.669 234.667 -21.46 re
+f
+0 0 0 0 k
+q 1 0 0 1 356.4047 495.6455 cm
+0 0 m
+0 -1.543 l
+-0.103 -1.543 l
+-0.79 -1.554 -1.334 -1.764 -1.735 -2.175 c
+-2.128 -2.579 -2.367 -3.142 -2.454 -3.865 c
+-2.065 -3.418 -1.573 -3.189 -0.985 -3.189 c
+-0.309 -3.189 0.228 -3.484 0.632 -4.072 c
+1.033 -4.652 1.235 -5.42 1.235 -6.379 c
+1.235 -6.996 1.118 -7.551 0.882 -8.04 c
+0.654 -8.533 0.331 -8.914 -0.088 -9.186 c
+-0.5 -9.462 -0.97 -9.598 -1.5 -9.598 c
+-2.352 -9.598 -3.036 -9.26 -3.543 -8.584 c
+-4.054 -7.9 -4.307 -6.978 -4.307 -5.82 c
+-4.307 -5.144 l
+-4.307 -4.116 -4.145 -3.215 -3.821 -2.439 c
+-3.499 -1.657 -3.036 -1.058 -2.425 -0.646 c
+-1.808 -0.228 -1.095 -0.01 -0.279 0 c
+h
+-1.558 -4.704 m
+-1.793 -4.704 -1.992 -4.777 -2.146 -4.924 c
+-2.304 -5.071 -2.421 -5.247 -2.499 -5.453 c
+-2.499 -6.115 l
+-2.499 -6.772 -2.41 -7.268 -2.234 -7.599 c
+-2.057 -7.923 -1.83 -8.084 -1.544 -8.084 c
+-1.249 -8.084 -1.014 -7.93 -0.838 -7.613 c
+-0.661 -7.301 -0.573 -6.894 -0.573 -6.394 c
+-0.573 -5.894 -0.669 -5.49 -0.852 -5.174 c
+-1.029 -4.862 -1.264 -4.704 -1.558 -4.704 c
+2.708 -8.54 m
+2.708 -8.246 2.804 -8.007 3.002 -7.819 c
+3.198 -7.636 3.451 -7.54 3.767 -7.54 c
+4.068 -7.54 4.318 -7.636 4.516 -7.819 c
+4.723 -8.007 4.825 -8.246 4.825 -8.54 c
+4.825 -8.845 4.723 -9.091 4.516 -9.275 c
+4.318 -9.452 4.068 -9.539 3.767 -9.539 c
+3.462 -9.539 3.208 -9.448 3.002 -9.26 c
+2.804 -9.076 2.708 -8.834 2.708 -8.54 c
+15.387 -1.675 m
+13.064 -1.675 l
+13.064 -9.466 l
+11.168 -9.466 l
+11.168 -1.675 l
+8.889 -1.675 l
+8.889 -0.103 l
+15.387 -0.103 l
+h
+20.347 -7.555 m
+17.775 -7.555 l
+17.275 -9.466 l
+15.276 -9.466 l
+18.201 -0.103 l
+19.921 -0.103 l
+22.875 -9.466 l
+20.847 -9.466 l
+h
+18.186 -5.967 m
+19.935 -5.967 l
+19.054 -2.631 l
+h
+30.206 -8.407 m
+29.843 -8.801 29.398 -9.099 28.869 -9.304 c
+28.34 -9.499 27.759 -9.598 27.135 -9.598 c
+26.054 -9.598 25.217 -9.267 24.621 -8.598 c
+24.022 -7.933 23.717 -6.963 23.709 -5.689 c
+23.709 -3.998 l
+23.709 -2.705 23.989 -1.712 24.548 -1.014 c
+25.113 -0.32 25.936 0.03 27.017 0.03 c
+28.035 0.03 28.799 -0.228 29.31 -0.735 c
+29.828 -1.234 30.125 -2.021 30.206 -3.087 c
+28.369 -3.087 l
+28.318 -2.491 28.197 -2.084 28.002 -1.866 c
+27.804 -1.654 27.495 -1.543 27.075 -1.543 c
+26.565 -1.543 26.194 -1.731 25.959 -2.102 c
+25.731 -2.477 25.613 -3.068 25.606 -3.881 c
+25.606 -5.585 l
+25.606 -6.437 25.731 -7.063 25.988 -7.453 c
+26.242 -7.834 26.657 -8.025 27.237 -8.025 c
+27.609 -8.025 27.914 -7.952 28.149 -7.805 c
+28.311 -7.688 l
+28.311 -5.967 l
+26.988 -5.967 l
+26.988 -4.542 l
+30.206 -4.542 l
+h
+38.144 -8.407 m
+37.78 -8.801 37.336 -9.099 36.806 -9.304 c
+36.278 -9.499 35.696 -9.598 35.072 -9.598 c
+33.992 -9.598 33.154 -9.267 32.558 -8.598 c
+31.96 -7.933 31.655 -6.963 31.647 -5.689 c
+31.647 -3.998 l
+31.647 -2.705 31.927 -1.712 32.485 -1.014 c
+33.051 -0.32 33.874 0.03 34.954 0.03 c
+35.972 0.03 36.737 -0.228 37.248 -0.735 c
+37.766 -1.234 38.063 -2.021 38.144 -3.087 c
+36.307 -3.087 l
+36.255 -2.491 36.134 -2.084 35.939 -1.866 c
+35.741 -1.654 35.432 -1.543 35.013 -1.543 c
+34.503 -1.543 34.131 -1.731 33.896 -2.102 c
+33.668 -2.477 33.551 -3.068 33.543 -3.881 c
+33.543 -5.585 l
+33.543 -6.437 33.668 -7.063 33.926 -7.453 c
+34.179 -7.834 34.594 -8.025 35.175 -8.025 c
+35.546 -8.025 35.851 -7.952 36.086 -7.805 c
+36.248 -7.688 l
+36.248 -5.967 l
+34.925 -5.967 l
+34.925 -4.542 l
+38.144 -4.542 l
+h
+41.701 -9.466 -1.896 9.363 re
+49.951 -9.466 m
+48.055 -9.466 l
+45.292 -3.322 l
+45.292 -9.466 l
+43.395 -9.466 l
+43.395 -0.103 l
+45.292 -0.103 l
+48.055 -6.247 l
+48.055 -0.103 l
+49.951 -0.103 l
+h
+58.069 -8.407 m
+57.705 -8.801 57.26 -9.099 56.731 -9.304 c
+56.202 -9.499 55.621 -9.598 54.997 -9.598 c
+53.917 -9.598 53.078 -9.267 52.483 -8.598 c
+51.884 -7.933 51.579 -6.963 51.571 -5.689 c
+51.571 -3.998 l
+51.571 -2.705 51.851 -1.712 52.41 -1.014 c
+52.976 -0.32 53.799 0.03 54.879 0.03 c
+55.897 0.03 56.662 -0.228 57.172 -0.735 c
+57.69 -1.234 57.988 -2.021 58.069 -3.087 c
+56.231 -3.087 l
+56.18 -2.491 56.059 -2.084 55.864 -1.866 c
+55.665 -1.654 55.357 -1.543 54.938 -1.543 c
+54.427 -1.543 54.056 -1.731 53.821 -2.102 c
+53.593 -2.477 53.475 -3.068 53.468 -3.881 c
+53.468 -5.585 l
+53.468 -6.437 53.593 -7.063 53.85 -7.453 c
+54.104 -7.834 54.519 -8.025 55.099 -8.025 c
+55.471 -8.025 55.776 -7.952 56.011 -7.805 c
+56.172 -7.688 l
+56.172 -5.967 l
+54.85 -5.967 l
+54.85 -4.542 l
+58.069 -4.542 l
+h
+65.407 -5.85 m
+64.658 -6.79 l
+64.658 -9.466 l
+62.761 -9.466 l
+62.761 -0.103 l
+64.658 -0.103 l
+64.658 -4.189 l
+65.26 -3.175 l
+66.995 -0.103 l
+69.317 -0.103 l
+66.627 -4.218 l
+69.362 -9.466 l
+67.112 -9.466 l
+h
+76.807 -9.466 m
+74.91 -9.466 l
+72.146 -3.322 l
+72.146 -9.466 l
+70.251 -9.466 l
+70.251 -0.103 l
+72.146 -0.103 l
+74.91 -6.247 l
+74.91 -0.103 l
+76.807 -0.103 l
+h
+85.159 -5.629 m
+85.159 -6.886 84.858 -7.86 84.262 -8.554 c
+83.664 -9.253 82.841 -9.598 81.793 -9.598 c
+80.742 -9.598 79.915 -9.257 79.309 -8.569 c
+78.71 -7.875 78.405 -6.908 78.397 -5.674 c
+78.397 -4.072 l
+78.397 -2.789 78.695 -1.786 79.294 -1.058 c
+79.889 -0.334 80.72 0.03 81.778 0.03 c
+82.814 0.03 83.634 -0.33 84.233 -1.043 c
+84.84 -1.749 85.148 -2.745 85.159 -4.027 c
+h
+83.263 -4.056 m
+83.263 -3.215 83.138 -2.587 82.895 -2.175 c
+82.66 -1.764 82.286 -1.558 81.778 -1.558 c
+81.279 -1.558 80.904 -1.76 80.661 -2.161 c
+80.426 -2.554 80.301 -3.152 80.294 -3.954 c
+80.294 -5.629 l
+80.294 -6.445 80.415 -7.048 80.661 -7.437 c
+80.904 -7.831 81.282 -8.025 81.793 -8.025 c
+82.282 -8.025 82.646 -7.834 82.881 -7.453 c
+83.123 -7.07 83.252 -6.482 83.263 -5.689 c
+h
+92.821 -5.982 m
+93.703 -0.103 l
+95.585 -0.103 l
+93.908 -9.466 l
+92.013 -9.466 l
+90.924 -3.954 l
+89.852 -9.466 l
+87.941 -9.466 l
+86.265 -0.103 l
+88.146 -0.103 l
+89.029 -5.982 l
+90.131 -0.103 l
+91.718 -0.103 l
+h
+103.448 -9.466 m
+101.552 -9.466 l
+98.789 -3.322 l
+98.789 -9.466 l
+96.892 -9.466 l
+96.892 -0.103 l
+98.789 -0.103 l
+101.552 -6.247 l
+101.552 -0.103 l
+103.448 -0.103 l
+h
+114.675 -6.35 m
+114.623 -7.419 114.322 -8.228 113.764 -8.775 c
+113.212 -9.327 112.44 -9.598 111.441 -9.598 c
+110.371 -9.598 109.552 -9.253 108.987 -8.554 c
+108.428 -7.86 108.148 -6.865 108.148 -5.571 c
+108.148 -3.998 l
+108.148 -2.705 108.435 -1.712 109.016 -1.014 c
+109.604 -0.32 110.416 0.03 111.456 0.03 c
+112.474 0.03 113.249 -0.261 113.778 -0.837 c
+114.307 -1.407 114.608 -2.227 114.689 -3.293 c
+112.794 -3.293 l
+112.771 -2.628 112.669 -2.171 112.484 -1.926 c
+112.297 -1.672 111.956 -1.543 111.456 -1.543 c
+110.956 -1.543 110.596 -1.72 110.383 -2.072 c
+110.177 -2.425 110.063 -3.009 110.045 -3.821 c
+110.045 -5.585 l
+110.045 -6.518 110.147 -7.158 110.353 -7.511 c
+110.566 -7.856 110.93 -8.025 111.441 -8.025 c
+111.93 -8.025 112.268 -7.908 112.455 -7.673 c
+112.65 -7.43 112.756 -6.989 112.779 -6.35 c
+h
+122.649 -5.629 m
+122.649 -6.886 122.348 -7.86 121.752 -8.554 c
+121.153 -9.253 120.33 -9.598 119.283 -9.598 c
+118.232 -9.598 117.405 -9.257 116.798 -8.569 c
+116.2 -7.875 115.894 -6.908 115.888 -5.674 c
+115.888 -4.072 l
+115.888 -2.789 116.185 -1.786 116.784 -1.058 c
+117.379 -0.334 118.21 0.03 119.268 0.03 c
+120.305 0.03 121.124 -0.33 121.723 -1.043 c
+122.329 -1.749 122.638 -2.745 122.649 -4.027 c
+h
+120.752 -4.056 m
+120.752 -3.215 120.628 -2.587 120.385 -2.175 c
+120.15 -1.764 119.775 -1.558 119.268 -1.558 c
+118.768 -1.558 118.394 -1.76 118.151 -2.161 c
+117.916 -2.554 117.791 -3.152 117.783 -3.954 c
+117.783 -5.629 l
+117.783 -6.445 117.905 -7.048 118.151 -7.437 c
+118.394 -7.831 118.772 -8.025 119.283 -8.025 c
+119.771 -8.025 120.135 -7.834 120.37 -7.453 c
+120.613 -7.07 120.742 -6.482 120.752 -5.689 c
+h
+126.695 -0.103 m
+128.473 -6.879 l
+130.223 -0.103 l
+132.692 -0.103 l
+132.692 -9.466 l
+130.796 -9.466 l
+130.796 -6.938 l
+130.973 -3.027 l
+129.105 -9.466 l
+127.812 -9.466 l
+125.945 -3.027 l
+126.121 -6.938 l
+126.121 -9.466 l
+124.226 -9.466 l
+124.226 -0.103 l
+h
+136.962 -0.103 m
+138.741 -6.879 l
+140.49 -0.103 l
+142.96 -0.103 l
+142.96 -9.466 l
+141.063 -9.466 l
+141.063 -6.938 l
+141.24 -3.027 l
+139.372 -9.466 l
+138.079 -9.466 l
+136.212 -3.027 l
+136.389 -6.938 l
+136.389 -9.466 l
+134.493 -9.466 l
+134.493 -0.103 l
+h
+146.74 -9.466 -1.896 9.363 re
+154.322 -1.675 m
+151.999 -1.675 l
+151.999 -9.466 l
+150.103 -9.466 l
+150.103 -1.675 l
+147.825 -1.675 l
+147.825 -0.103 l
+154.322 -0.103 l
+h
+159.452 -7.011 m
+159.452 -6.629 159.353 -6.342 159.158 -6.144 c
+158.96 -5.949 158.607 -5.747 158.1 -5.541 c
+157.159 -5.182 156.483 -4.762 156.071 -4.292 c
+155.659 -3.814 155.453 -3.245 155.453 -2.587 c
+155.453 -1.804 155.733 -1.172 156.292 -0.69 c
+156.85 -0.213 157.559 0.03 158.422 0.03 c
+159 0.03 159.515 -0.095 159.966 -0.338 c
+160.415 -0.584 160.76 -0.926 160.995 -1.367 c
+161.238 -1.808 161.362 -2.308 161.362 -2.866 c
+159.481 -2.866 l
+159.481 -2.425 159.386 -2.094 159.202 -1.866 c
+159.014 -1.643 158.746 -1.529 158.393 -1.529 c
+158.059 -1.529 157.798 -1.627 157.614 -1.822 c
+157.438 -2.009 157.35 -2.271 157.35 -2.601 c
+157.35 -2.859 157.452 -3.094 157.659 -3.307 c
+157.865 -3.513 158.224 -3.73 158.746 -3.954 c
+159.658 -4.278 160.319 -4.681 160.731 -5.159 c
+161.15 -5.641 161.362 -6.254 161.362 -6.996 c
+161.362 -7.812 161.102 -8.448 160.583 -8.907 c
+160.062 -9.371 159.357 -9.598 158.467 -9.598 c
+157.857 -9.598 157.306 -9.473 156.806 -9.231 c
+156.313 -8.977 155.928 -8.621 155.644 -8.158 c
+155.358 -7.688 155.218 -7.14 155.218 -6.512 c
+157.115 -6.512 l
+157.115 -7.052 157.217 -7.445 157.423 -7.688 c
+157.636 -7.933 157.986 -8.055 158.467 -8.055 c
+159.121 -8.055 159.452 -7.709 159.452 -7.011 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 471.367 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 464.5277 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.596 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.279 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.22 l
+13.641 2.22 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.22 l
+15.728 2.22 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.455 14.552 -0.515 c
+14.589 -0.565 14.645 -0.61 14.714 -0.646 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.687 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.602 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.23 15.626 -1.249 c
+15.545 -1.261 15.468 -1.267 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.308 14.832 -1.308 c
+14.645 -1.308 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.025 13.788 -0.955 c
+13.729 -0.878 13.685 -0.801 13.656 -0.72 c
+13.627 -0.632 13.604 -0.544 13.597 -0.455 c
+13.586 -0.36 13.582 -0.264 13.582 -0.176 c
+h
+21.722 1.602 m
+21.178 1.602 l
+21.178 2.22 l
+21.766 2.22 l
+22.045 3.117 l
+22.618 3.117 l
+22.618 2.22 l
+23.853 2.22 l
+23.853 1.602 l
+22.618 1.602 l
+22.618 -0.103 l
+22.618 -0.324 l
+22.626 -0.393 22.647 -0.455 22.677 -0.515 c
+22.714 -0.565 22.769 -0.61 22.839 -0.646 c
+22.916 -0.676 23.03 -0.691 23.177 -0.691 c
+23.313 -0.691 23.449 -0.687 23.588 -0.676 c
+23.725 -0.658 23.857 -0.632 23.985 -0.602 c
+23.985 -1.205 l
+23.904 -1.216 23.827 -1.23 23.75 -1.249 c
+23.669 -1.261 23.592 -1.267 23.515 -1.278 c
+23.434 -1.286 23.346 -1.294 23.25 -1.294 c
+23.162 -1.301 23.063 -1.308 22.957 -1.308 c
+22.769 -1.308 22.608 -1.294 22.471 -1.264 c
+22.343 -1.228 22.229 -1.183 22.134 -1.132 c
+22.045 -1.084 21.972 -1.025 21.913 -0.955 c
+21.854 -0.878 21.81 -0.801 21.781 -0.72 c
+21.751 -0.632 21.729 -0.544 21.722 -0.455 c
+21.71 -0.36 21.707 -0.264 21.707 -0.176 c
+h
+26.136 -1.323 m
+25.966 -1.323 25.816 -1.301 25.679 -1.264 c
+25.551 -1.216 25.437 -1.147 25.342 -1.058 c
+25.253 -0.97 25.184 -0.864 25.136 -0.735 c
+25.084 -0.599 25.062 -0.448 25.062 -0.279 c
+25.062 -0.073 25.095 0.096 25.165 0.235 c
+25.232 0.382 25.327 0.493 25.444 0.574 c
+25.569 0.661 25.712 0.724 25.87 0.765 c
+26.036 0.802 26.213 0.827 26.4 0.838 c
+27.12 0.852 l
+27.12 1.029 l
+27.12 1.147 27.109 1.249 27.09 1.338 c
+27.069 1.426 27.036 1.492 26.988 1.544 c
+26.947 1.602 26.9 1.639 26.841 1.661 c
+26.782 1.679 26.716 1.691 26.649 1.691 c
+26.58 1.691 26.518 1.679 26.458 1.661 c
+26.408 1.65 26.36 1.625 26.312 1.588 c
+26.271 1.558 26.238 1.507 26.209 1.44 c
+26.186 1.382 26.172 1.301 26.165 1.205 c
+25.224 1.249 l
+25.253 1.397 25.297 1.532 25.356 1.661 c
+25.423 1.786 25.518 1.897 25.635 1.985 c
+25.753 2.08 25.893 2.153 26.061 2.205 c
+26.238 2.253 26.444 2.278 26.679 2.278 c
+27.12 2.278 27.451 2.168 27.678 1.955 c
+27.914 1.75 28.031 1.44 28.031 1.029 c
+28.031 -0.235 l
+28.031 -0.455 l
+28.039 -0.515 28.054 -0.569 28.075 -0.617 c
+28.094 -0.658 28.123 -0.691 28.164 -0.72 c
+28.201 -0.742 28.252 -0.75 28.311 -0.75 c
+28.376 -0.75 28.447 -0.746 28.517 -0.735 c
+28.517 -1.22 l
+28.457 -1.23 28.403 -1.242 28.355 -1.249 c
+28.314 -1.261 28.274 -1.267 28.237 -1.278 c
+28.197 -1.286 28.153 -1.294 28.105 -1.294 c
+28.054 -1.301 27.995 -1.308 27.929 -1.308 c
+27.701 -1.308 27.535 -1.257 27.429 -1.147 c
+27.318 -1.029 27.256 -0.864 27.237 -0.646 c
+27.223 -0.646 l
+27.154 -0.757 27.083 -0.852 27.017 -0.941 c
+26.947 -1.022 26.87 -1.087 26.782 -1.147 c
+26.693 -1.205 26.595 -1.249 26.488 -1.278 c
+26.389 -1.308 26.271 -1.323 26.136 -1.323 c
+27.12 0.353 m
+26.693 0.339 l
+26.595 0.339 26.503 0.331 26.414 0.324 c
+26.333 0.312 26.267 0.287 26.209 0.25 c
+26.15 0.21 26.098 0.15 26.061 0.073 c
+26.021 0.004 26.003 -0.088 26.003 -0.206 c
+26.003 -0.374 26.036 -0.496 26.106 -0.573 c
+26.172 -0.654 26.271 -0.691 26.4 -0.691 c
+26.506 -0.691 26.606 -0.669 26.693 -0.617 c
+26.789 -0.569 26.87 -0.507 26.929 -0.426 c
+26.995 -0.349 27.046 -0.261 27.076 -0.162 c
+27.106 -0.055 27.12 0.059 27.12 0.177 c
+h
+30.769 -2.66 m
+30.552 -2.66 30.361 -2.635 30.196 -2.587 c
+30.027 -2.547 29.887 -2.484 29.77 -2.396 c
+29.652 -2.315 29.552 -2.219 29.475 -2.102 c
+29.406 -1.984 29.358 -1.855 29.328 -1.72 c
+30.225 -1.617 l
+30.262 -1.753 30.331 -1.859 30.431 -1.94 c
+30.527 -2.028 30.651 -2.072 30.799 -2.072 c
+30.886 -2.072 30.967 -2.057 31.048 -2.028 c
+31.125 -1.999 31.195 -1.944 31.254 -1.866 c
+31.313 -1.797 31.357 -1.705 31.387 -1.587 c
+31.424 -1.469 31.445 -1.323 31.445 -1.147 c
+31.445 -0.955 l
+31.445 -0.889 31.449 -0.831 31.46 -0.779 c
+31.46 -0.588 l
+31.445 -0.588 l
+31.346 -0.816 31.202 -0.977 31.019 -1.072 c
+30.832 -1.172 30.626 -1.22 30.402 -1.22 c
+30.196 -1.22 30.012 -1.183 29.858 -1.103 c
+29.71 -1.014 29.582 -0.897 29.475 -0.75 c
+29.376 -0.596 29.303 -0.411 29.255 -0.206 c
+29.203 0.008 29.182 0.243 29.182 0.5 c
+29.182 0.771 29.203 1.018 29.255 1.235 c
+29.313 1.448 29.394 1.632 29.505 1.779 c
+29.612 1.933 29.743 2.051 29.901 2.132 c
+30.057 2.22 30.244 2.263 30.46 2.263 c
+30.556 2.263 30.655 2.253 30.755 2.234 c
+30.85 2.213 30.938 2.18 31.019 2.132 c
+31.107 2.08 31.185 2.018 31.254 1.941 c
+31.331 1.86 31.393 1.768 31.445 1.661 c
+31.46 1.661 l
+31.46 1.808 l
+31.468 1.867 31.474 1.918 31.474 1.97 c
+31.482 2.028 31.489 2.076 31.489 2.117 c
+31.497 2.165 31.507 2.198 31.519 2.22 c
+32.371 2.22 l
+32.361 2.139 32.349 2.028 32.342 1.881 c
+32.342 1.411 l
+32.342 -1.161 l
+32.342 -1.415 32.305 -1.635 32.239 -1.822 c
+32.17 -2.007 32.066 -2.161 31.931 -2.279 c
+31.79 -2.404 31.625 -2.499 31.43 -2.558 c
+31.233 -2.624 31.011 -2.66 30.769 -2.66 c
+31.46 0.53 m
+31.46 0.742 31.434 0.919 31.387 1.058 c
+31.346 1.205 31.291 1.323 31.225 1.411 c
+31.166 1.5 31.096 1.558 31.019 1.588 c
+30.938 1.625 30.861 1.646 30.784 1.646 c
+30.684 1.646 30.593 1.621 30.504 1.573 c
+30.424 1.532 30.358 1.463 30.298 1.367 c
+30.248 1.279 30.203 1.162 30.167 1.014 c
+30.137 0.875 30.122 0.706 30.122 0.5 c
+30.122 0.125 30.181 -0.154 30.298 -0.338 c
+30.416 -0.515 30.578 -0.602 30.784 -0.602 c
+30.85 -0.602 30.923 -0.588 31.004 -0.559 c
+31.092 -0.522 31.166 -0.463 31.225 -0.382 c
+31.291 -0.294 31.346 -0.176 31.387 -0.029 c
+31.434 0.118 31.46 0.301 31.46 0.53 c
+f
+Q
+q 1 0 0 1 482.8862 448.9472 cm
+0 0 m
+2.103 0 l
+2.103 -0.574 l
+-0.675 -0.574 l
+-0.675 4.777 l
+0 4.777 l
+h
+3.514 -0.574 -0.647 3.984 re
+3.558 4.453 m
+3.558 4.343 3.528 4.251 3.469 4.174 c
+3.41 4.104 3.315 4.072 3.19 4.072 c
+3.072 4.072 2.977 4.104 2.911 4.174 c
+2.852 4.251 2.822 4.343 2.822 4.453 c
+2.822 4.571 2.852 4.663 2.911 4.733 c
+2.977 4.81 3.072 4.85 3.19 4.85 c
+3.315 4.85 3.41 4.81 3.469 4.733 c
+3.528 4.652 3.558 4.56 3.558 4.453 c
+6.468 0.44 m
+6.468 0.588 6.413 0.709 6.306 0.808 c
+6.196 0.904 5.99 1.022 5.689 1.161 c
+5.343 1.308 5.101 1.429 4.954 1.529 c
+4.807 1.635 4.696 1.753 4.63 1.881 c
+4.561 2.006 4.528 2.164 4.528 2.352 c
+4.528 2.674 4.645 2.944 4.881 3.16 c
+5.116 3.374 5.417 3.484 5.792 3.484 c
+6.174 3.484 6.483 3.37 6.718 3.145 c
+6.953 2.917 7.071 2.631 7.071 2.278 c
+6.423 2.278 l
+6.423 2.454 6.365 2.605 6.247 2.734 c
+6.13 2.859 5.976 2.925 5.792 2.925 c
+5.594 2.925 5.442 2.869 5.336 2.763 c
+5.226 2.664 5.174 2.532 5.174 2.366 c
+5.174 2.238 5.211 2.131 5.292 2.043 c
+5.369 1.962 5.56 1.859 5.866 1.734 c
+6.342 1.547 6.674 1.359 6.85 1.176 c
+7.026 0.999 7.115 0.771 7.115 0.5 c
+7.115 0.147 6.99 -0.133 6.747 -0.339 c
+6.512 -0.544 6.196 -0.647 5.806 -0.647 c
+5.384 -0.647 5.046 -0.53 4.792 -0.294 c
+4.535 -0.052 4.41 0.253 4.41 0.617 c
+5.056 0.617 l
+5.064 0.389 5.134 0.213 5.262 0.087 c
+5.388 -0.03 5.571 -0.088 5.806 -0.088 c
+6.02 -0.088 6.182 -0.04 6.292 0.058 c
+6.409 0.154 6.468 0.282 6.468 0.44 c
+8.805 4.365 m
+8.805 3.41 l
+9.407 3.41 l
+9.407 2.881 l
+8.805 2.881 l
+8.805 0.411 l
+8.805 0.253 8.827 0.135 8.879 0.058 c
+8.937 -0.023 9.025 -0.059 9.143 -0.059 c
+9.232 -0.059 9.32 -0.044 9.407 -0.015 c
+9.407 -0.574 l
+9.261 -0.621 9.106 -0.647 8.952 -0.647 c
+8.694 -0.647 8.5 -0.555 8.364 -0.368 c
+8.224 -0.184 8.158 0.077 8.158 0.411 c
+8.158 2.881 l
+7.556 2.881 l
+7.556 3.41 l
+8.158 3.41 l
+8.158 4.365 l
+h
+13.935 -0.574 m
+13.895 -0.485 13.868 -0.339 13.862 -0.133 c
+13.627 -0.478 13.332 -0.647 12.979 -0.647 c
+12.615 -0.647 12.333 -0.551 12.127 -0.353 c
+11.929 -0.148 11.833 0.139 11.833 0.515 c
+11.833 0.914 11.969 1.234 12.245 1.469 c
+12.517 1.712 12.891 1.837 13.361 1.837 c
+13.847 1.837 l
+13.847 2.263 l
+13.847 2.499 13.791 2.664 13.685 2.763 c
+13.575 2.869 13.413 2.925 13.2 2.925 c
+13.002 2.925 12.84 2.866 12.715 2.749 c
+12.597 2.631 12.538 2.484 12.538 2.308 c
+11.892 2.308 l
+11.892 2.502 11.95 2.693 12.068 2.881 c
+12.193 3.064 12.355 3.212 12.553 3.322 c
+12.759 3.428 12.987 3.484 13.244 3.484 c
+13.645 3.484 13.949 3.38 14.155 3.175 c
+14.369 2.969 14.483 2.674 14.494 2.293 c
+14.494 0.279 l
+14.494 -0.026 14.531 -0.291 14.612 -0.515 c
+14.612 -0.574 l
+h
+13.068 -0.059 m
+13.233 -0.059 13.384 -0.015 13.523 0.073 c
+13.671 0.162 13.777 0.272 13.847 0.411 c
+13.847 1.352 l
+13.479 1.352 l
+13.163 1.352 12.921 1.282 12.744 1.146 c
+12.568 1.018 12.48 0.83 12.48 0.588 c
+12.48 0.359 12.524 0.195 12.613 0.087 c
+12.7 -0.011 12.85 -0.059 13.068 -0.059 c
+16.199 -0.574 -0.646 5.644 re
+17.918 -0.574 -0.646 5.644 re
+21.505 4.365 m
+21.505 3.41 l
+22.107 3.41 l
+22.107 2.881 l
+21.505 2.881 l
+21.505 0.411 l
+21.505 0.253 21.527 0.135 21.579 0.058 c
+21.637 -0.023 21.725 -0.059 21.843 -0.059 c
+21.931 -0.059 22.02 -0.044 22.107 -0.015 c
+22.107 -0.574 l
+21.961 -0.621 21.806 -0.647 21.652 -0.647 c
+21.394 -0.647 21.2 -0.555 21.064 -0.368 c
+20.924 -0.184 20.858 0.077 20.858 0.411 c
+20.858 2.881 l
+20.256 2.881 l
+20.256 3.41 l
+20.858 3.41 l
+20.858 4.365 l
+h
+24.915 -0.574 m
+24.875 -0.485 24.849 -0.339 24.842 -0.133 c
+24.607 -0.478 24.312 -0.647 23.96 -0.647 c
+23.596 -0.647 23.313 -0.551 23.107 -0.353 c
+22.909 -0.148 22.813 0.139 22.813 0.515 c
+22.813 0.914 22.949 1.234 23.225 1.469 c
+23.497 1.712 23.871 1.837 24.342 1.837 c
+24.827 1.837 l
+24.827 2.263 l
+24.827 2.499 24.772 2.664 24.665 2.763 c
+24.555 2.869 24.393 2.925 24.18 2.925 c
+23.981 2.925 23.82 2.866 23.695 2.749 c
+23.578 2.631 23.518 2.484 23.518 2.308 c
+22.872 2.308 l
+22.872 2.502 22.931 2.693 23.048 2.881 c
+23.173 3.064 23.335 3.212 23.534 3.322 c
+23.74 3.428 23.967 3.484 24.224 3.484 c
+24.625 3.484 24.93 3.38 25.136 3.175 c
+25.349 2.969 25.463 2.674 25.473 2.293 c
+25.473 0.279 l
+25.473 -0.026 25.51 -0.291 25.591 -0.515 c
+25.591 -0.574 l
+h
+24.048 -0.059 m
+24.214 -0.059 24.364 -0.015 24.503 0.073 c
+24.65 0.162 24.757 0.272 24.827 0.411 c
+24.827 1.352 l
+24.459 1.352 l
+24.143 1.352 23.901 1.282 23.724 1.146 c
+23.549 1.018 23.46 0.83 23.46 0.588 c
+23.46 0.359 23.504 0.195 23.592 0.087 c
+23.68 -0.011 23.831 -0.059 24.048 -0.059 c
+26.341 1.602 m
+26.341 2.219 26.451 2.682 26.679 2.998 c
+26.903 3.322 27.237 3.484 27.678 3.484 c
+28.079 3.484 28.384 3.307 28.59 2.954 c
+28.634 3.41 l
+29.222 3.41 l
+29.222 -0.618 l
+29.222 -1.106 29.093 -1.484 28.84 -1.75 c
+28.582 -2.014 28.23 -2.147 27.782 -2.147 c
+27.583 -2.147 27.362 -2.095 27.12 -1.999 c
+26.874 -1.9 26.693 -1.779 26.576 -1.632 c
+26.841 -1.191 l
+27.106 -1.455 27.403 -1.588 27.738 -1.588 c
+28.274 -1.588 28.55 -1.294 28.561 -0.706 c
+28.561 -0.177 l
+28.355 -0.493 28.054 -0.647 27.664 -0.647 c
+27.252 -0.647 26.929 -0.497 26.693 -0.191 c
+26.466 0.121 26.348 0.573 26.341 1.161 c
+h
+27.002 1.22 m
+27.002 0.779 27.065 0.448 27.194 0.235 c
+27.318 0.029 27.535 -0.073 27.84 -0.073 c
+28.164 -0.073 28.403 0.091 28.561 0.426 c
+28.561 2.41 l
+28.392 2.734 28.153 2.896 27.84 2.896 c
+27.547 2.896 27.329 2.792 27.194 2.587 c
+27.065 2.381 27.002 2.057 27.002 1.616 c
+h
+32.118 0.44 m
+32.118 0.588 32.062 0.709 31.956 0.808 c
+31.846 0.904 31.64 1.022 31.339 1.161 c
+30.994 1.308 30.751 1.429 30.604 1.529 c
+30.456 1.635 30.346 1.753 30.281 1.881 c
+30.211 2.006 30.177 2.164 30.177 2.352 c
+30.177 2.674 30.295 2.944 30.53 3.16 c
+30.765 3.374 31.067 3.484 31.441 3.484 c
+31.823 3.484 32.133 3.37 32.368 3.145 c
+32.603 2.917 32.721 2.631 32.721 2.278 c
+32.074 2.278 l
+32.074 2.454 32.015 2.605 31.898 2.734 c
+31.78 2.859 31.625 2.925 31.441 2.925 c
+31.243 2.925 31.092 2.869 30.986 2.763 c
+30.876 2.664 30.824 2.532 30.824 2.366 c
+30.824 2.238 30.861 2.131 30.942 2.043 c
+31.019 1.962 31.21 1.859 31.515 1.734 c
+31.993 1.547 32.324 1.359 32.5 1.176 c
+32.677 0.999 32.764 0.771 32.764 0.5 c
+32.764 0.147 32.64 -0.133 32.397 -0.339 c
+32.162 -0.544 31.846 -0.647 31.456 -0.647 c
+31.034 -0.647 30.695 -0.53 30.442 -0.294 c
+30.185 -0.052 30.059 0.253 30.059 0.617 c
+30.707 0.617 l
+30.714 0.389 30.784 0.213 30.913 0.087 c
+31.037 -0.03 31.221 -0.088 31.456 -0.088 c
+31.669 -0.088 31.831 -0.04 31.941 0.058 c
+32.058 0.154 32.118 0.282 32.118 0.44 c
+33.646 -0.221 m
+33.646 -0.103 33.679 -0.008 33.749 0.073 c
+33.816 0.151 33.918 0.191 34.058 0.191 c
+34.205 0.191 34.312 0.151 34.381 0.073 c
+34.458 -0.008 34.499 -0.103 34.499 -0.221 c
+34.499 -0.331 34.458 -0.423 34.381 -0.5 c
+34.312 -0.578 34.205 -0.618 34.058 -0.618 c
+33.918 -0.618 33.816 -0.578 33.749 -0.5 c
+33.679 -0.423 33.646 -0.331 33.646 -0.221 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 441.263 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 434.424 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.882 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.882 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.22 l
+13.641 2.22 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.22 l
+15.728 2.22 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.455 14.552 -0.515 c
+14.589 -0.565 14.645 -0.61 14.714 -0.646 c
+14.791 -0.676 14.905 -0.69 15.052 -0.69 c
+15.188 -0.69 15.324 -0.687 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.602 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.23 15.626 -1.249 c
+15.545 -1.261 15.468 -1.267 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.308 14.832 -1.308 c
+14.645 -1.308 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.025 13.788 -0.955 c
+13.729 -0.878 13.685 -0.801 13.656 -0.72 c
+13.627 -0.632 13.604 -0.544 13.597 -0.455 c
+13.586 -0.36 13.582 -0.264 13.582 -0.176 c
+h
+21.722 1.602 m
+21.178 1.602 l
+21.178 2.22 l
+21.766 2.22 l
+22.045 3.117 l
+22.618 3.117 l
+22.618 2.22 l
+23.853 2.22 l
+23.853 1.602 l
+22.618 1.602 l
+22.618 -0.103 l
+22.618 -0.324 l
+22.626 -0.393 22.647 -0.455 22.677 -0.515 c
+22.714 -0.565 22.769 -0.61 22.839 -0.646 c
+22.916 -0.676 23.03 -0.69 23.177 -0.69 c
+23.313 -0.69 23.449 -0.687 23.588 -0.676 c
+23.725 -0.658 23.857 -0.632 23.985 -0.602 c
+23.985 -1.205 l
+23.904 -1.216 23.827 -1.23 23.75 -1.249 c
+23.669 -1.261 23.592 -1.267 23.515 -1.278 c
+23.434 -1.286 23.346 -1.294 23.25 -1.294 c
+23.162 -1.301 23.063 -1.308 22.957 -1.308 c
+22.769 -1.308 22.608 -1.294 22.471 -1.264 c
+22.343 -1.228 22.229 -1.183 22.134 -1.132 c
+22.045 -1.084 21.972 -1.025 21.913 -0.955 c
+21.854 -0.878 21.81 -0.801 21.781 -0.72 c
+21.751 -0.632 21.729 -0.544 21.722 -0.455 c
+21.71 -0.36 21.707 -0.264 21.707 -0.176 c
+h
+26.136 -1.323 m
+25.966 -1.323 25.816 -1.301 25.679 -1.264 c
+25.551 -1.216 25.437 -1.147 25.342 -1.058 c
+25.253 -0.97 25.184 -0.864 25.136 -0.735 c
+25.084 -0.599 25.062 -0.448 25.062 -0.279 c
+25.062 -0.073 25.095 0.096 25.165 0.235 c
+25.232 0.382 25.327 0.493 25.444 0.574 c
+25.569 0.661 25.712 0.724 25.87 0.765 c
+26.036 0.802 26.213 0.827 26.4 0.838 c
+27.12 0.852 l
+27.12 1.029 l
+27.12 1.147 27.109 1.249 27.09 1.338 c
+27.069 1.426 27.036 1.492 26.988 1.544 c
+26.947 1.602 26.9 1.639 26.841 1.661 c
+26.782 1.679 26.716 1.691 26.649 1.691 c
+26.58 1.691 26.518 1.679 26.458 1.661 c
+26.408 1.65 26.36 1.625 26.312 1.588 c
+26.271 1.558 26.238 1.507 26.209 1.44 c
+26.186 1.382 26.172 1.301 26.165 1.205 c
+25.224 1.249 l
+25.253 1.397 25.297 1.532 25.356 1.661 c
+25.423 1.786 25.518 1.897 25.635 1.985 c
+25.753 2.08 25.893 2.153 26.061 2.205 c
+26.238 2.253 26.444 2.278 26.679 2.278 c
+27.12 2.278 27.451 2.168 27.678 1.955 c
+27.914 1.75 28.031 1.44 28.031 1.029 c
+28.031 -0.235 l
+28.031 -0.455 l
+28.039 -0.515 28.054 -0.569 28.075 -0.617 c
+28.094 -0.658 28.123 -0.69 28.164 -0.72 c
+28.201 -0.742 28.252 -0.75 28.311 -0.75 c
+28.376 -0.75 28.447 -0.746 28.517 -0.735 c
+28.517 -1.22 l
+28.457 -1.23 28.403 -1.242 28.355 -1.249 c
+28.314 -1.261 28.274 -1.267 28.237 -1.278 c
+28.197 -1.286 28.153 -1.294 28.105 -1.294 c
+28.054 -1.301 27.995 -1.308 27.929 -1.308 c
+27.701 -1.308 27.535 -1.257 27.429 -1.147 c
+27.318 -1.029 27.256 -0.864 27.237 -0.646 c
+27.223 -0.646 l
+27.154 -0.757 27.083 -0.852 27.017 -0.941 c
+26.947 -1.022 26.87 -1.087 26.782 -1.147 c
+26.693 -1.205 26.595 -1.249 26.488 -1.278 c
+26.389 -1.308 26.271 -1.323 26.136 -1.323 c
+27.12 0.353 m
+26.693 0.339 l
+26.595 0.339 26.503 0.331 26.414 0.324 c
+26.333 0.312 26.267 0.287 26.209 0.25 c
+26.15 0.21 26.098 0.151 26.061 0.073 c
+26.021 0.004 26.003 -0.088 26.003 -0.206 c
+26.003 -0.374 26.036 -0.496 26.106 -0.573 c
+26.172 -0.654 26.271 -0.69 26.4 -0.69 c
+26.506 -0.69 26.606 -0.669 26.693 -0.617 c
+26.789 -0.569 26.87 -0.507 26.929 -0.426 c
+26.995 -0.349 27.046 -0.261 27.076 -0.162 c
+27.106 -0.055 27.12 0.059 27.12 0.177 c
+h
+30.769 -2.66 m
+30.552 -2.66 30.361 -2.635 30.196 -2.587 c
+30.027 -2.547 29.887 -2.484 29.77 -2.396 c
+29.652 -2.315 29.552 -2.219 29.475 -2.102 c
+29.406 -1.984 29.358 -1.855 29.328 -1.72 c
+30.225 -1.617 l
+30.262 -1.753 30.331 -1.859 30.431 -1.94 c
+30.527 -2.028 30.651 -2.072 30.799 -2.072 c
+30.886 -2.072 30.967 -2.057 31.048 -2.028 c
+31.125 -1.999 31.195 -1.944 31.254 -1.866 c
+31.313 -1.797 31.357 -1.705 31.387 -1.587 c
+31.424 -1.469 31.445 -1.323 31.445 -1.147 c
+31.445 -0.955 l
+31.445 -0.889 31.449 -0.831 31.46 -0.779 c
+31.46 -0.588 l
+31.445 -0.588 l
+31.346 -0.816 31.202 -0.977 31.019 -1.072 c
+30.832 -1.172 30.626 -1.22 30.402 -1.22 c
+30.196 -1.22 30.012 -1.183 29.858 -1.103 c
+29.71 -1.014 29.582 -0.897 29.475 -0.75 c
+29.376 -0.595 29.303 -0.411 29.255 -0.206 c
+29.203 0.008 29.182 0.243 29.182 0.5 c
+29.182 0.771 29.203 1.018 29.255 1.235 c
+29.313 1.448 29.394 1.632 29.505 1.779 c
+29.612 1.933 29.743 2.051 29.901 2.132 c
+30.057 2.22 30.244 2.263 30.46 2.263 c
+30.556 2.263 30.655 2.253 30.755 2.234 c
+30.85 2.213 30.938 2.18 31.019 2.132 c
+31.107 2.08 31.185 2.018 31.254 1.941 c
+31.331 1.86 31.393 1.768 31.445 1.661 c
+31.46 1.661 l
+31.46 1.808 l
+31.468 1.867 31.474 1.918 31.474 1.97 c
+31.482 2.028 31.489 2.076 31.489 2.117 c
+31.497 2.165 31.507 2.198 31.519 2.22 c
+32.371 2.22 l
+32.361 2.139 32.349 2.028 32.342 1.882 c
+32.342 1.411 l
+32.342 -1.161 l
+32.342 -1.415 32.305 -1.635 32.239 -1.822 c
+32.17 -2.007 32.066 -2.161 31.931 -2.278 c
+31.79 -2.404 31.625 -2.499 31.43 -2.558 c
+31.233 -2.624 31.011 -2.66 30.769 -2.66 c
+31.46 0.53 m
+31.46 0.742 31.434 0.919 31.387 1.058 c
+31.346 1.205 31.291 1.323 31.225 1.411 c
+31.166 1.5 31.096 1.558 31.019 1.588 c
+30.938 1.625 30.861 1.646 30.784 1.646 c
+30.684 1.646 30.593 1.621 30.504 1.573 c
+30.424 1.532 30.358 1.463 30.298 1.367 c
+30.248 1.279 30.203 1.162 30.167 1.014 c
+30.137 0.875 30.122 0.706 30.122 0.5 c
+30.122 0.125 30.181 -0.154 30.298 -0.338 c
+30.416 -0.515 30.578 -0.602 30.784 -0.602 c
+30.85 -0.602 30.923 -0.588 31.004 -0.559 c
+31.092 -0.522 31.166 -0.463 31.225 -0.382 c
+31.291 -0.294 31.346 -0.176 31.387 -0.029 c
+31.434 0.118 31.46 0.301 31.46 0.53 c
+38.115 -2.631 m
+38.115 3.514 l
+40.041 3.514 l
+40.041 2.896 l
+38.967 2.896 l
+38.967 -2.013 l
+40.041 -2.013 l
+40.041 -2.631 l
+h
+43.616 -1.264 m
+43.616 0.721 l
+43.616 1.022 43.572 1.242 43.484 1.382 c
+43.403 1.529 43.267 1.602 43.072 1.602 c
+42.962 1.602 42.859 1.577 42.763 1.529 c
+42.676 1.477 42.595 1.411 42.528 1.323 c
+42.47 1.235 42.418 1.124 42.381 1 c
+42.352 0.882 42.337 0.75 42.337 0.603 c
+42.337 -1.264 l
+41.425 -1.264 l
+41.425 1.44 l
+41.425 1.661 l
+41.425 1.75 41.419 1.827 41.411 1.897 c
+41.411 2.087 l
+41.411 2.22 l
+42.264 2.22 l
+42.271 2.19 42.279 2.146 42.279 2.087 c
+42.279 1.897 l
+42.286 1.827 42.293 1.756 42.293 1.691 c
+42.3 1.621 42.308 1.565 42.308 1.529 c
+42.323 1.529 l
+42.441 1.793 42.591 1.985 42.778 2.103 c
+42.962 2.22 43.183 2.278 43.44 2.278 c
+43.623 2.278 43.785 2.249 43.925 2.19 c
+44.06 2.132 44.174 2.043 44.263 1.926 c
+44.351 1.808 44.413 1.665 44.454 1.5 c
+44.502 1.341 44.527 1.154 44.527 0.941 c
+44.527 -1.264 l
+h
+46.441 -1.323 m
+46.273 -1.323 46.123 -1.301 45.986 -1.264 c
+45.857 -1.216 45.743 -1.147 45.648 -1.058 c
+45.56 -0.97 45.49 -0.864 45.442 -0.735 c
+45.391 -0.599 45.369 -0.448 45.369 -0.279 c
+45.369 -0.073 45.402 0.096 45.472 0.235 c
+45.538 0.382 45.633 0.493 45.751 0.574 c
+45.876 0.661 46.019 0.724 46.177 0.765 c
+46.343 0.802 46.52 0.827 46.707 0.838 c
+47.426 0.852 l
+47.426 1.029 l
+47.426 1.147 47.416 1.249 47.397 1.338 c
+47.376 1.426 47.342 1.492 47.295 1.544 c
+47.254 1.602 47.206 1.639 47.147 1.661 c
+47.089 1.679 47.023 1.691 46.956 1.691 c
+46.886 1.691 46.824 1.679 46.765 1.661 c
+46.714 1.65 46.666 1.625 46.618 1.588 c
+46.578 1.558 46.545 1.507 46.516 1.44 c
+46.493 1.382 46.479 1.301 46.472 1.205 c
+45.531 1.249 l
+45.56 1.397 45.604 1.532 45.663 1.661 c
+45.729 1.786 45.824 1.897 45.942 1.985 c
+46.06 2.08 46.2 2.153 46.368 2.205 c
+46.545 2.253 46.751 2.278 46.986 2.278 c
+47.426 2.278 47.758 2.168 47.985 1.955 c
+48.22 1.75 48.338 1.44 48.338 1.029 c
+48.338 -0.235 l
+48.338 -0.455 l
+48.346 -0.515 48.361 -0.569 48.382 -0.617 c
+48.401 -0.658 48.43 -0.69 48.471 -0.72 c
+48.507 -0.742 48.558 -0.75 48.617 -0.75 c
+48.683 -0.75 48.753 -0.746 48.823 -0.735 c
+48.823 -1.22 l
+48.764 -1.23 48.71 -1.242 48.662 -1.249 c
+48.621 -1.261 48.581 -1.267 48.544 -1.278 c
+48.504 -1.286 48.459 -1.294 48.411 -1.294 c
+48.361 -1.301 48.301 -1.308 48.235 -1.308 c
+48.008 -1.308 47.842 -1.257 47.735 -1.147 c
+47.625 -1.029 47.563 -0.864 47.544 -0.646 c
+47.53 -0.646 l
+47.46 -0.757 47.39 -0.852 47.324 -0.941 c
+47.254 -1.022 47.177 -1.087 47.089 -1.147 c
+47 -1.205 46.901 -1.249 46.794 -1.278 c
+46.695 -1.308 46.578 -1.323 46.441 -1.323 c
+47.426 0.353 m
+47 0.339 l
+46.901 0.339 46.809 0.331 46.721 0.324 c
+46.64 0.312 46.574 0.287 46.516 0.25 c
+46.457 0.21 46.405 0.151 46.368 0.073 c
+46.328 0.004 46.31 -0.088 46.31 -0.206 c
+46.31 -0.374 46.343 -0.496 46.412 -0.573 c
+46.479 -0.654 46.578 -0.69 46.707 -0.69 c
+46.813 -0.69 46.912 -0.669 47 -0.617 c
+47.096 -0.569 47.177 -0.507 47.235 -0.426 c
+47.302 -0.349 47.353 -0.261 47.382 -0.162 c
+47.412 -0.055 47.426 0.059 47.426 0.177 c
+h
+50.709 -1.264 m
+50.709 0.852 l
+50.709 1.018 50.701 1.154 50.694 1.264 c
+50.682 1.371 50.664 1.455 50.634 1.515 c
+50.613 1.58 50.584 1.632 50.547 1.661 c
+50.518 1.691 50.477 1.706 50.429 1.706 c
+50.37 1.706 50.315 1.675 50.267 1.617 c
+50.227 1.565 50.194 1.492 50.164 1.397 c
+50.135 1.309 50.109 1.195 50.091 1.058 c
+50.08 0.919 50.077 0.769 50.077 0.603 c
+50.077 -1.264 l
+49.327 -1.264 l
+49.327 1.47 l
+49.327 1.706 l
+49.327 1.926 l
+49.327 2.003 49.319 2.065 49.312 2.117 c
+49.312 2.22 l
+49.988 2.22 l
+49.988 2.132 l
+49.988 1.985 l
+49.996 1.926 50.003 1.867 50.003 1.808 c
+50.003 1.646 l
+50.017 1.646 l
+50.036 1.735 50.065 1.812 50.106 1.882 c
+50.142 1.959 50.187 2.028 50.238 2.087 c
+50.297 2.146 50.362 2.19 50.443 2.22 c
+50.521 2.257 50.609 2.278 50.709 2.278 c
+50.892 2.278 51.031 2.223 51.12 2.117 c
+51.216 2.018 51.285 1.86 51.326 1.646 c
+51.34 1.646 l
+51.378 1.742 51.418 1.83 51.458 1.911 c
+51.505 1.988 51.561 2.051 51.619 2.103 c
+51.679 2.161 51.744 2.205 51.825 2.234 c
+51.902 2.263 51.991 2.278 52.09 2.278 c
+52.226 2.278 52.34 2.253 52.428 2.205 c
+52.516 2.153 52.583 2.08 52.634 1.985 c
+52.693 1.885 52.729 1.756 52.751 1.602 c
+52.781 1.455 52.795 1.272 52.795 1.058 c
+52.795 -1.264 l
+52.076 -1.264 l
+52.076 0.852 l
+52.076 1.018 52.068 1.154 52.061 1.264 c
+52.049 1.371 52.031 1.455 52.002 1.515 c
+51.98 1.58 51.95 1.632 51.914 1.661 c
+51.885 1.691 51.844 1.706 51.796 1.706 c
+51.679 1.706 51.583 1.617 51.517 1.44 c
+51.458 1.272 51.428 1.014 51.428 0.661 c
+51.428 -1.264 l
+h
+55.166 -1.323 m
+54.908 -1.323 54.68 -1.286 54.475 -1.22 c
+54.269 -1.143 54.093 -1.029 53.946 -0.881 c
+53.799 -0.727 53.681 -0.536 53.593 -0.309 c
+53.512 -0.085 53.475 0.181 53.475 0.485 c
+53.475 0.816 53.52 1.095 53.608 1.323 c
+53.703 1.558 53.832 1.742 53.99 1.882 c
+54.156 2.018 54.343 2.117 54.549 2.176 c
+54.754 2.242 54.964 2.278 55.181 2.278 c
+55.453 2.278 55.688 2.227 55.887 2.132 c
+56.092 2.043 56.257 1.911 56.386 1.735 c
+56.522 1.565 56.621 1.359 56.68 1.118 c
+56.746 0.882 56.783 0.617 56.783 0.324 c
+56.783 0.309 l
+54.416 0.309 l
+54.416 0.162 54.431 0.023 54.46 -0.103 c
+54.497 -0.231 54.553 -0.345 54.622 -0.441 c
+54.688 -0.529 54.773 -0.599 54.871 -0.646 c
+54.967 -0.698 55.081 -0.72 55.21 -0.72 c
+55.364 -0.72 55.504 -0.687 55.621 -0.617 c
+55.746 -0.551 55.835 -0.448 55.887 -0.309 c
+56.724 -0.382 l
+56.695 -0.482 56.639 -0.588 56.562 -0.706 c
+56.481 -0.816 56.379 -0.918 56.253 -1.014 c
+56.136 -1.103 55.982 -1.176 55.798 -1.234 c
+55.621 -1.294 55.409 -1.323 55.166 -1.323 c
+55.166 1.706 m
+55.077 1.706 54.989 1.691 54.902 1.661 c
+54.813 1.632 54.732 1.58 54.666 1.515 c
+54.596 1.444 54.537 1.357 54.49 1.249 c
+54.449 1.139 54.431 1.014 54.431 0.867 c
+55.901 0.867 l
+55.901 1.004 55.875 1.124 55.827 1.235 c
+55.787 1.341 55.731 1.43 55.665 1.5 c
+55.607 1.565 55.534 1.617 55.445 1.646 c
+55.357 1.683 55.261 1.706 55.166 1.706 c
+58.04 -2.631 m
+58.04 -2.013 l
+59.112 -2.013 l
+59.112 2.896 l
+58.04 2.896 l
+58.04 3.514 l
+59.965 3.514 l
+59.965 -2.631 l
+h
+66.543 -2.631 m
+66.543 3.514 l
+68.469 3.514 l
+68.469 2.896 l
+67.396 2.896 l
+67.396 -2.013 l
+68.469 -2.013 l
+68.469 -2.631 l
+h
+71.412 -1.323 m
+71.126 -1.323 70.883 -1.282 70.677 -1.205 c
+70.471 -1.117 70.299 -0.995 70.162 -0.837 c
+70.023 -0.683 69.92 -0.496 69.854 -0.279 c
+69.784 -0.055 69.751 0.191 69.751 0.456 c
+69.751 0.75 69.784 1.007 69.854 1.235 c
+69.931 1.459 70.038 1.646 70.177 1.793 c
+70.324 1.947 70.501 2.065 70.706 2.146 c
+70.912 2.234 71.147 2.278 71.412 2.278 c
+71.636 2.278 71.839 2.249 72.015 2.19 c
+72.191 2.132 72.342 2.047 72.47 1.941 c
+72.595 1.841 72.698 1.72 72.779 1.573 c
+72.857 1.434 72.911 1.282 72.94 1.118 c
+72.03 1.073 l
+72 1.249 71.93 1.389 71.824 1.5 c
+71.724 1.606 71.581 1.661 71.397 1.661 c
+71.151 1.661 70.975 1.558 70.868 1.353 c
+70.758 1.154 70.706 0.867 70.706 0.485 c
+70.706 -0.309 70.941 -0.706 71.412 -0.706 c
+71.577 -0.706 71.721 -0.654 71.839 -0.544 c
+71.956 -0.437 72.03 -0.276 72.059 -0.058 c
+72.97 -0.103 l
+72.94 -0.272 72.886 -0.426 72.809 -0.573 c
+72.739 -0.72 72.635 -0.852 72.5 -0.97 c
+72.371 -1.08 72.213 -1.168 72.03 -1.234 c
+71.853 -1.294 71.647 -1.323 71.412 -1.323 c
+77.119 0.485 m
+77.119 0.21 77.082 -0.04 77.016 -0.264 c
+76.946 -0.482 76.843 -0.669 76.707 -0.823 c
+76.568 -0.981 76.391 -1.103 76.178 -1.19 c
+75.961 -1.278 75.708 -1.323 75.413 -1.323 c
+75.138 -1.323 74.892 -1.278 74.679 -1.19 c
+74.473 -1.103 74.301 -0.981 74.164 -0.823 c
+74.025 -0.669 73.921 -0.482 73.856 -0.264 c
+73.786 -0.04 73.753 0.21 73.753 0.485 c
+73.753 0.738 73.782 0.974 73.841 1.191 c
+73.907 1.415 74.01 1.606 74.15 1.764 c
+74.285 1.929 74.462 2.058 74.679 2.146 c
+74.892 2.234 75.149 2.278 75.444 2.278 c
+75.756 2.278 76.016 2.234 76.222 2.146 c
+76.435 2.058 76.608 1.929 76.737 1.764 c
+76.873 1.606 76.972 1.415 77.031 1.191 c
+77.09 0.974 77.119 0.738 77.119 0.485 c
+76.163 0.485 m
+76.163 0.691 76.149 0.867 76.119 1.014 c
+76.097 1.162 76.061 1.282 76.001 1.382 c
+75.943 1.477 75.87 1.548 75.781 1.588 c
+75.693 1.635 75.583 1.661 75.458 1.661 c
+75.193 1.661 75.002 1.562 74.885 1.367 c
+74.767 1.18 74.708 0.886 74.708 0.485 c
+74.708 0.063 74.767 -0.243 74.885 -0.426 c
+75.002 -0.613 75.178 -0.706 75.413 -0.706 c
+75.539 -0.706 75.652 -0.687 75.752 -0.646 c
+75.847 -0.599 75.928 -0.525 75.987 -0.426 c
+76.053 -0.33 76.097 -0.206 76.119 -0.058 c
+76.149 0.088 76.163 0.268 76.163 0.485 c
+79.14 -1.264 m
+79.14 0.852 l
+79.14 1.018 79.132 1.154 79.126 1.264 c
+79.114 1.371 79.095 1.455 79.066 1.515 c
+79.045 1.58 79.015 1.632 78.978 1.661 c
+78.949 1.691 78.908 1.706 78.86 1.706 c
+78.802 1.706 78.746 1.675 78.699 1.617 c
+78.659 1.565 78.625 1.492 78.596 1.397 c
+78.567 1.309 78.541 1.195 78.523 1.058 c
+78.511 0.919 78.508 0.769 78.508 0.603 c
+78.508 -1.264 l
+77.759 -1.264 l
+77.759 1.47 l
+77.759 1.706 l
+77.759 1.926 l
+77.759 2.003 77.751 2.065 77.744 2.117 c
+77.744 2.22 l
+78.42 2.22 l
+78.42 2.132 l
+78.42 1.985 l
+78.427 1.926 78.434 1.867 78.434 1.808 c
+78.434 1.646 l
+78.449 1.646 l
+78.467 1.735 78.497 1.812 78.538 1.882 c
+78.574 1.959 78.619 2.028 78.669 2.087 c
+78.729 2.146 78.794 2.19 78.875 2.22 c
+78.952 2.257 79.041 2.278 79.14 2.278 c
+79.324 2.278 79.463 2.223 79.552 2.117 c
+79.647 2.018 79.717 1.86 79.758 1.646 c
+79.772 1.646 l
+79.809 1.742 79.849 1.83 79.889 1.911 c
+79.937 1.988 79.993 2.051 80.051 2.103 c
+80.111 2.161 80.176 2.205 80.257 2.234 c
+80.334 2.263 80.423 2.278 80.522 2.278 c
+80.658 2.278 80.772 2.253 80.859 2.205 c
+80.948 2.153 81.014 2.08 81.065 1.985 c
+81.125 1.885 81.161 1.756 81.183 1.602 c
+81.212 1.455 81.227 1.272 81.227 1.058 c
+81.227 -1.264 l
+80.507 -1.264 l
+80.507 0.852 l
+80.507 1.018 80.5 1.154 80.492 1.264 c
+80.481 1.371 80.463 1.455 80.433 1.515 c
+80.412 1.58 80.382 1.632 80.346 1.661 c
+80.316 1.691 80.275 1.706 80.228 1.706 c
+80.111 1.706 80.015 1.617 79.949 1.44 c
+79.889 1.272 79.86 1.014 79.86 0.661 c
+79.86 -1.264 l
+h
+83.201 -1.264 m
+83.201 0.852 l
+83.201 1.018 83.193 1.154 83.186 1.264 c
+83.175 1.371 83.157 1.455 83.128 1.515 c
+83.105 1.58 83.076 1.632 83.039 1.661 c
+83.01 1.691 82.969 1.706 82.921 1.706 c
+82.862 1.706 82.808 1.675 82.76 1.617 c
+82.719 1.565 82.686 1.492 82.657 1.397 c
+82.627 1.309 82.602 1.195 82.583 1.058 c
+82.572 0.919 82.569 0.769 82.569 0.603 c
+82.569 -1.264 l
+81.819 -1.264 l
+81.819 1.47 l
+81.819 1.706 l
+81.819 1.926 l
+81.819 2.003 81.811 2.065 81.804 2.117 c
+81.804 2.22 l
+82.48 2.22 l
+82.48 2.132 l
+82.48 1.985 l
+82.488 1.926 82.495 1.867 82.495 1.808 c
+82.495 1.646 l
+82.509 1.646 l
+82.528 1.735 82.557 1.812 82.598 1.882 c
+82.635 1.959 82.679 2.028 82.73 2.087 c
+82.789 2.146 82.855 2.19 82.936 2.22 c
+83.013 2.257 83.101 2.278 83.201 2.278 c
+83.384 2.278 83.524 2.223 83.612 2.117 c
+83.708 2.018 83.778 1.86 83.818 1.646 c
+83.833 1.646 l
+83.87 1.742 83.91 1.83 83.951 1.911 c
+83.998 1.988 84.053 2.051 84.112 2.103 c
+84.171 2.161 84.237 2.205 84.318 2.234 c
+84.395 2.263 84.483 2.278 84.582 2.278 c
+84.718 2.278 84.832 2.253 84.921 2.205 c
+85.009 2.153 85.075 2.08 85.127 1.985 c
+85.185 1.885 85.222 1.756 85.244 1.602 c
+85.273 1.455 85.287 1.272 85.287 1.058 c
+85.287 -1.264 l
+84.568 -1.264 l
+84.568 0.852 l
+84.568 1.018 84.56 1.154 84.553 1.264 c
+84.541 1.371 84.524 1.455 84.494 1.515 c
+84.472 1.58 84.443 1.632 84.406 1.661 c
+84.377 1.691 84.336 1.706 84.288 1.706 c
+84.171 1.706 84.075 1.617 84.009 1.44 c
+83.951 1.272 83.921 1.014 83.921 0.661 c
+83.921 -1.264 l
+h
+88.202 -0.646 m
+89.334 -0.646 l
+89.334 -1.264 l
+86.027 -1.264 l
+86.027 -0.646 l
+87.29 -0.646 l
+87.29 1.602 l
+86.365 1.602 l
+86.365 2.22 l
+88.202 2.22 l
+h
+87.29 3.514 0.912 -0.676 re
+87.29 2.837 m
+90.763 1.602 m
+90.22 1.602 l
+90.22 2.22 l
+90.808 2.22 l
+91.086 3.117 l
+91.66 3.117 l
+91.66 2.22 l
+92.894 2.22 l
+92.894 1.602 l
+91.66 1.602 l
+91.66 -0.103 l
+91.66 -0.324 l
+91.668 -0.393 91.689 -0.455 91.718 -0.515 c
+91.755 -0.565 91.811 -0.61 91.88 -0.646 c
+91.957 -0.676 92.071 -0.69 92.219 -0.69 c
+92.354 -0.69 92.491 -0.687 92.63 -0.676 c
+92.766 -0.658 92.898 -0.632 93.027 -0.602 c
+93.027 -1.205 l
+92.946 -1.216 92.869 -1.23 92.792 -1.249 c
+92.711 -1.261 92.634 -1.267 92.557 -1.278 c
+92.476 -1.286 92.387 -1.294 92.292 -1.294 c
+92.204 -1.301 92.105 -1.308 91.998 -1.308 c
+91.811 -1.308 91.649 -1.294 91.513 -1.264 c
+91.384 -1.228 91.271 -1.183 91.175 -1.132 c
+91.086 -1.084 91.013 -1.025 90.954 -0.955 c
+90.895 -0.878 90.851 -0.801 90.822 -0.72 c
+90.793 -0.632 90.771 -0.544 90.763 -0.455 c
+90.752 -0.36 90.748 -0.264 90.748 -0.176 c
+h
+101.387 -0.249 m
+101.387 -0.419 101.347 -0.569 101.27 -0.706 c
+101.199 -0.834 101.097 -0.948 100.961 -1.043 c
+100.832 -1.132 100.67 -1.201 100.476 -1.249 c
+100.288 -1.297 100.071 -1.323 99.828 -1.323 c
+99.601 -1.323 99.402 -1.308 99.226 -1.278 c
+99.05 -1.249 98.891 -1.201 98.756 -1.132 c
+98.616 -1.055 98.506 -0.955 98.417 -0.837 c
+98.33 -0.72 98.26 -0.573 98.212 -0.397 c
+99.02 -0.279 l
+99.039 -0.378 99.068 -0.455 99.109 -0.515 c
+99.157 -0.573 99.215 -0.617 99.285 -0.646 c
+99.351 -0.676 99.432 -0.702 99.52 -0.72 c
+99.608 -0.731 99.711 -0.735 99.828 -0.735 c
+99.924 -0.735 100.02 -0.731 100.108 -0.72 c
+100.196 -0.702 100.273 -0.676 100.343 -0.646 c
+100.41 -0.617 100.461 -0.58 100.491 -0.529 c
+100.527 -0.482 100.549 -0.419 100.549 -0.338 c
+100.549 -0.243 100.52 -0.169 100.461 -0.118 c
+100.41 -0.07 100.343 -0.029 100.256 0 c
+100.167 0.037 100.057 0.066 99.932 0.088 c
+99.814 0.118 99.682 0.147 99.535 0.177 c
+99.396 0.214 99.256 0.254 99.109 0.294 c
+98.969 0.341 98.844 0.405 98.727 0.485 c
+98.616 0.563 98.528 0.661 98.462 0.779 c
+98.392 0.897 98.359 1.047 98.359 1.235 c
+98.359 1.389 98.388 1.532 98.447 1.661 c
+98.513 1.797 98.609 1.911 98.727 1.999 c
+98.851 2.087 99.009 2.153 99.197 2.205 c
+99.381 2.253 99.593 2.278 99.828 2.278 c
+100.013 2.278 100.189 2.257 100.358 2.22 c
+100.524 2.19 100.67 2.135 100.799 2.058 c
+100.924 1.988 101.034 1.889 101.122 1.764 c
+101.21 1.646 101.27 1.503 101.299 1.338 c
+100.505 1.264 l
+100.483 1.341 100.454 1.404 100.416 1.455 c
+100.377 1.515 100.329 1.558 100.27 1.588 c
+100.219 1.625 100.156 1.65 100.079 1.661 c
+99.998 1.669 99.917 1.675 99.828 1.675 c
+99.612 1.675 99.45 1.646 99.344 1.588 c
+99.234 1.536 99.182 1.448 99.182 1.323 c
+99.182 1.242 99.201 1.18 99.24 1.132 c
+99.288 1.081 99.351 1.044 99.432 1.014 c
+99.52 0.985 99.616 0.956 99.726 0.927 c
+99.832 0.904 99.954 0.882 100.094 0.852 c
+100.248 0.823 100.406 0.783 100.564 0.735 c
+100.718 0.684 100.858 0.621 100.975 0.544 c
+101.093 0.463 101.189 0.36 101.27 0.235 c
+101.347 0.106 101.387 -0.055 101.387 -0.249 c
+103.242 1.515 m
+103.36 1.786 103.511 1.985 103.699 2.103 c
+103.882 2.22 104.102 2.278 104.36 2.278 c
+104.566 2.278 104.734 2.242 104.875 2.176 c
+105.021 2.105 105.131 2.014 105.212 1.897 c
+105.301 1.779 105.359 1.635 105.389 1.47 c
+105.426 1.301 105.447 1.124 105.447 0.941 c
+105.447 -1.264 l
+104.536 -1.264 l
+104.536 0.735 l
+104.536 0.871 104.526 0.992 104.507 1.103 c
+104.495 1.209 104.47 1.297 104.433 1.367 c
+104.393 1.444 104.335 1.503 104.257 1.544 c
+104.187 1.58 104.096 1.602 103.978 1.602 c
+103.867 1.602 103.772 1.577 103.684 1.529 c
+103.595 1.477 103.514 1.411 103.448 1.323 c
+103.39 1.235 103.338 1.124 103.302 1 c
+103.272 0.882 103.257 0.75 103.257 0.603 c
+103.257 -1.264 l
+102.346 -1.264 l
+102.346 3.514 l
+103.257 3.514 l
+103.257 2.205 l
+103.257 2.135 103.25 2.065 103.242 1.999 c
+103.242 1.793 l
+103.242 1.735 103.236 1.679 103.228 1.632 c
+103.228 1.515 l
+h
+107.362 -1.323 m
+107.193 -1.323 107.042 -1.301 106.907 -1.264 c
+106.778 -1.216 106.664 -1.147 106.569 -1.058 c
+106.48 -0.97 106.411 -0.864 106.363 -0.735 c
+106.311 -0.599 106.289 -0.448 106.289 -0.279 c
+106.289 -0.073 106.322 0.096 106.392 0.235 c
+106.458 0.382 106.554 0.493 106.671 0.574 c
+106.797 0.661 106.939 0.724 107.098 0.765 c
+107.263 0.802 107.439 0.827 107.627 0.838 c
+108.347 0.852 l
+108.347 1.029 l
+108.347 1.147 108.336 1.249 108.318 1.338 c
+108.295 1.426 108.262 1.492 108.215 1.544 c
+108.175 1.602 108.127 1.639 108.067 1.661 c
+108.009 1.679 107.942 1.691 107.876 1.691 c
+107.807 1.691 107.745 1.679 107.685 1.661 c
+107.634 1.65 107.587 1.625 107.539 1.588 c
+107.498 1.558 107.465 1.507 107.435 1.44 c
+107.414 1.382 107.399 1.301 107.392 1.205 c
+106.451 1.249 l
+106.48 1.397 106.525 1.532 106.583 1.661 c
+106.649 1.786 106.745 1.897 106.862 1.985 c
+106.98 2.08 107.119 2.153 107.289 2.205 c
+107.465 2.253 107.67 2.278 107.906 2.278 c
+108.347 2.278 108.678 2.168 108.906 1.955 c
+109.141 1.75 109.258 1.44 109.258 1.029 c
+109.258 -0.235 l
+109.258 -0.455 l
+109.266 -0.515 109.28 -0.569 109.303 -0.617 c
+109.321 -0.658 109.351 -0.69 109.391 -0.72 c
+109.428 -0.742 109.479 -0.75 109.538 -0.75 c
+109.604 -0.75 109.673 -0.746 109.744 -0.735 c
+109.744 -1.22 l
+109.685 -1.23 109.629 -1.242 109.582 -1.249 c
+109.542 -1.261 109.501 -1.267 109.464 -1.278 c
+109.424 -1.286 109.38 -1.294 109.332 -1.294 c
+109.28 -1.301 109.222 -1.308 109.156 -1.308 c
+108.927 -1.308 108.763 -1.257 108.655 -1.147 c
+108.545 -1.029 108.483 -0.864 108.464 -0.646 c
+108.45 -0.646 l
+108.38 -0.757 108.31 -0.852 108.244 -0.941 c
+108.175 -1.022 108.098 -1.087 108.009 -1.147 c
+107.921 -1.205 107.822 -1.249 107.715 -1.278 c
+107.616 -1.308 107.498 -1.323 107.362 -1.323 c
+108.347 0.353 m
+107.921 0.339 l
+107.822 0.339 107.73 0.331 107.641 0.324 c
+107.56 0.312 107.495 0.287 107.435 0.25 c
+107.377 0.21 107.325 0.151 107.289 0.073 c
+107.248 0.004 107.23 -0.088 107.23 -0.206 c
+107.23 -0.374 107.263 -0.496 107.333 -0.573 c
+107.399 -0.654 107.498 -0.69 107.627 -0.69 c
+107.733 -0.69 107.832 -0.669 107.921 -0.617 c
+108.017 -0.569 108.098 -0.507 108.156 -0.426 c
+108.222 -0.349 108.273 -0.261 108.303 -0.162 c
+108.333 -0.055 108.347 0.059 108.347 0.177 c
+h
+110.835 -2.631 m
+110.835 -2.013 l
+111.908 -2.013 l
+111.908 2.896 l
+110.835 2.896 l
+110.835 3.514 l
+112.761 3.514 l
+112.761 -2.631 l
+h
+f
+Q
+q 1 0 0 1 300.6696 419.9752 cm
+0 0 m
+-0.029 -0.58 -0.191 -1.022 -0.485 -1.323 c
+-0.779 -1.628 -1.198 -1.779 -1.735 -1.779 c
+-2.263 -1.779 -2.69 -1.58 -3.013 -1.176 c
+-3.329 -0.765 -3.484 -0.206 -3.484 0.5 c
+-3.484 1.469 l
+-3.484 2.165 -3.322 2.712 -2.999 3.116 c
+-2.675 3.517 -2.23 3.719 -1.661 3.719 c
+-1.154 3.719 -0.757 3.561 -0.47 3.248 c
+-0.187 2.944 -0.029 2.502 0 1.926 c
+-0.69 1.926 l
+-0.721 2.366 -0.816 2.678 -0.97 2.866 c
+-1.118 3.05 -1.349 3.146 -1.661 3.146 c
+-2.036 3.146 -2.319 2.998 -2.514 2.705 c
+-2.712 2.418 -2.807 2.003 -2.807 1.455 c
+-2.807 0.47 l
+-2.807 -0.07 -2.716 -0.485 -2.529 -0.779 c
+-2.344 -1.066 -2.08 -1.205 -1.735 -1.205 c
+-1.382 -1.205 -1.128 -1.118 -0.97 -0.941 c
+-0.816 -0.765 -0.721 -0.453 -0.69 0 c
+h
+2.454 1.66 m
+2.367 1.679 2.267 1.691 2.161 1.691 c
+1.826 1.691 1.591 1.506 1.455 1.147 c
+1.455 -1.706 l
+0.808 -1.706 l
+0.808 2.278 l
+1.44 2.278 l
+1.455 1.866 l
+1.631 2.19 1.874 2.352 2.19 2.352 c
+2.296 2.352 2.385 2.329 2.454 2.293 c
+h
+4.453 -1.779 m
+3.954 -1.779 3.572 -1.632 3.308 -1.338 c
+3.042 -1.044 2.911 -0.611 2.911 -0.029 c
+2.911 0.441 l
+2.911 1.036 3.036 1.502 3.293 1.837 c
+3.557 2.179 3.917 2.352 4.38 2.352 c
+4.84 2.352 5.182 2.198 5.409 1.896 c
+5.644 1.602 5.766 1.139 5.777 0.515 c
+5.777 0.088 l
+3.557 0.088 l
+3.557 0 l
+3.557 -0.434 3.634 -0.746 3.792 -0.941 c
+3.958 -1.128 4.189 -1.22 4.484 -1.22 c
+4.678 -1.22 4.85 -1.187 4.997 -1.118 c
+5.145 -1.04 5.28 -0.923 5.409 -0.765 c
+5.747 -1.176 l
+5.461 -1.58 5.031 -1.779 4.453 -1.779 c
+4.38 1.793 m
+4.104 1.793 3.902 1.697 3.778 1.514 c
+3.649 1.326 3.576 1.036 3.557 0.646 c
+5.13 0.646 l
+5.13 0.735 l
+5.108 1.117 5.041 1.385 4.924 1.543 c
+4.806 1.708 4.623 1.793 4.38 1.793 c
+8.599 -1.706 m
+8.559 -1.617 8.533 -1.47 8.525 -1.264 c
+8.29 -1.61 7.996 -1.779 7.644 -1.779 c
+7.279 -1.779 6.996 -1.683 6.791 -1.484 c
+6.593 -1.279 6.497 -0.992 6.497 -0.617 c
+6.497 -0.217 6.633 0.103 6.909 0.338 c
+7.181 0.58 7.555 0.706 8.025 0.706 c
+8.511 0.706 l
+8.511 1.132 l
+8.511 1.367 8.455 1.532 8.349 1.631 c
+8.239 1.738 8.077 1.793 7.864 1.793 c
+7.665 1.793 7.504 1.735 7.379 1.617 c
+7.262 1.5 7.202 1.352 7.202 1.176 c
+6.556 1.176 l
+6.556 1.371 6.614 1.562 6.732 1.749 c
+6.857 1.932 7.019 2.08 7.217 2.19 c
+7.423 2.296 7.651 2.352 7.908 2.352 c
+8.309 2.352 8.613 2.248 8.819 2.043 c
+9.033 1.837 9.147 1.543 9.157 1.161 c
+9.157 -0.852 l
+9.157 -1.158 9.194 -1.422 9.275 -1.646 c
+9.275 -1.706 l
+h
+7.732 -1.191 m
+7.897 -1.191 8.048 -1.147 8.187 -1.058 c
+8.334 -0.97 8.441 -0.86 8.511 -0.721 c
+8.511 0.22 l
+8.143 0.22 l
+7.827 0.22 7.584 0.151 7.408 0.014 c
+7.231 -0.114 7.144 -0.302 7.144 -0.544 c
+7.144 -0.772 7.188 -0.937 7.276 -1.044 c
+7.364 -1.143 7.515 -1.191 7.732 -1.191 c
+11.009 3.233 m
+11.009 2.278 l
+11.612 2.278 l
+11.612 1.749 l
+11.009 1.749 l
+11.009 -0.721 l
+11.009 -0.879 11.032 -0.996 11.083 -1.073 c
+11.142 -1.154 11.23 -1.191 11.347 -1.191 c
+11.436 -1.191 11.524 -1.176 11.612 -1.147 c
+11.612 -1.706 l
+11.465 -1.753 11.311 -1.779 11.156 -1.779 c
+10.899 -1.779 10.705 -1.687 10.568 -1.5 c
+10.429 -1.316 10.363 -1.055 10.363 -0.721 c
+10.363 1.749 l
+9.76 1.749 l
+9.76 2.278 l
+10.363 2.278 l
+10.363 3.233 l
+h
+13.803 -1.779 m
+13.303 -1.779 12.92 -1.632 12.656 -1.338 c
+12.391 -1.044 12.259 -0.611 12.259 -0.029 c
+12.259 0.441 l
+12.259 1.036 12.384 1.502 12.641 1.837 c
+12.906 2.179 13.266 2.352 13.729 2.352 c
+14.188 2.352 14.53 2.198 14.757 1.896 c
+14.993 1.602 15.114 1.139 15.125 0.515 c
+15.125 0.088 l
+12.906 0.088 l
+12.906 0 l
+12.906 -0.434 12.983 -0.746 13.141 -0.941 c
+13.307 -1.128 13.538 -1.22 13.832 -1.22 c
+14.026 -1.22 14.2 -1.187 14.346 -1.118 c
+14.493 -1.04 14.629 -0.923 14.757 -0.765 c
+15.096 -1.176 l
+14.809 -1.58 14.379 -1.779 13.803 -1.779 c
+13.729 1.793 m
+13.454 1.793 13.251 1.697 13.126 1.514 c
+12.997 1.326 12.924 1.036 12.906 0.646 c
+14.479 0.646 l
+14.479 0.735 l
+14.456 1.117 14.391 1.385 14.273 1.543 c
+14.155 1.708 13.972 1.793 13.729 1.793 c
+19.667 -1.706 m
+19.627 -1.617 19.601 -1.47 19.594 -1.264 c
+19.359 -1.61 19.065 -1.779 18.712 -1.779 c
+18.348 -1.779 18.065 -1.683 17.859 -1.484 c
+17.661 -1.279 17.565 -0.992 17.565 -0.617 c
+17.565 -0.217 17.701 0.103 17.977 0.338 c
+18.248 0.58 18.624 0.706 19.094 0.706 c
+19.579 0.706 l
+19.579 1.132 l
+19.579 1.367 19.524 1.532 19.418 1.631 c
+19.307 1.738 19.146 1.793 18.932 1.793 c
+18.734 1.793 18.572 1.735 18.447 1.617 c
+18.329 1.5 18.271 1.352 18.271 1.176 c
+17.624 1.176 l
+17.624 1.371 17.683 1.562 17.801 1.749 c
+17.926 1.932 18.088 2.08 18.285 2.19 c
+18.491 2.296 18.719 2.352 18.977 2.352 c
+19.377 2.352 19.682 2.248 19.888 2.043 c
+20.101 1.837 20.215 1.543 20.226 1.161 c
+20.226 -0.852 l
+20.226 -1.158 20.263 -1.422 20.344 -1.646 c
+20.344 -1.706 l
+h
+18.8 -1.191 m
+18.965 -1.191 19.116 -1.147 19.256 -1.058 c
+19.403 -0.97 19.509 -0.86 19.579 -0.721 c
+19.579 0.22 l
+19.212 0.22 l
+18.896 0.22 18.653 0.151 18.477 0.014 c
+18.3 -0.114 18.212 -0.302 18.212 -0.544 c
+18.212 -0.772 18.256 -0.937 18.344 -1.044 c
+18.433 -1.143 18.583 -1.191 18.8 -1.191 c
+23.798 3.233 m
+23.798 2.278 l
+24.4 2.278 l
+24.4 1.749 l
+23.798 1.749 l
+23.798 -0.721 l
+23.798 -0.879 23.82 -0.996 23.871 -1.073 c
+23.93 -1.154 24.018 -1.191 24.136 -1.191 c
+24.224 -1.191 24.312 -1.176 24.4 -1.147 c
+24.4 -1.706 l
+24.253 -1.753 24.099 -1.779 23.945 -1.779 c
+23.688 -1.779 23.493 -1.687 23.357 -1.5 c
+23.217 -1.316 23.151 -1.055 23.151 -0.721 c
+23.151 1.749 l
+22.549 1.749 l
+22.549 2.278 l
+23.151 2.278 l
+23.151 3.233 l
+h
+27.208 -1.706 m
+27.168 -1.617 27.142 -1.47 27.135 -1.264 c
+26.9 -1.61 26.605 -1.779 26.252 -1.779 c
+25.888 -1.779 25.606 -1.683 25.4 -1.484 c
+25.201 -1.279 25.106 -0.992 25.106 -0.617 c
+25.106 -0.217 25.242 0.103 25.518 0.338 c
+25.789 0.58 26.164 0.706 26.634 0.706 c
+27.12 0.706 l
+27.12 1.132 l
+27.12 1.367 27.064 1.532 26.958 1.631 c
+26.848 1.738 26.686 1.793 26.473 1.793 c
+26.275 1.793 26.113 1.735 25.988 1.617 c
+25.87 1.5 25.811 1.352 25.811 1.176 c
+25.165 1.176 l
+25.165 1.371 25.223 1.562 25.341 1.749 c
+25.466 1.932 25.628 2.08 25.826 2.19 c
+26.032 2.296 26.26 2.352 26.517 2.352 c
+26.917 2.352 27.222 2.248 27.428 2.043 c
+27.642 1.837 27.756 1.543 27.767 1.161 c
+27.767 -0.852 l
+27.767 -1.158 27.804 -1.422 27.884 -1.646 c
+27.884 -1.706 l
+h
+26.341 -1.191 m
+26.506 -1.191 26.657 -1.147 26.796 -1.058 c
+26.944 -0.97 27.05 -0.86 27.12 -0.721 c
+27.12 0.22 l
+26.752 0.22 l
+26.437 0.22 26.194 0.151 26.017 0.014 c
+25.841 -0.114 25.753 -0.302 25.753 -0.544 c
+25.753 -0.772 25.797 -0.937 25.885 -1.044 c
+25.973 -1.143 26.123 -1.191 26.341 -1.191 c
+28.633 0.47 m
+28.633 1.088 28.744 1.55 28.972 1.866 c
+29.196 2.19 29.531 2.352 29.971 2.352 c
+30.372 2.352 30.677 2.175 30.883 1.822 c
+30.927 2.278 l
+31.515 2.278 l
+31.515 -1.75 l
+31.515 -2.238 31.386 -2.616 31.133 -2.882 c
+30.875 -3.146 30.522 -3.278 30.074 -3.278 c
+29.876 -3.278 29.655 -3.227 29.413 -3.131 c
+29.167 -3.032 28.986 -2.911 28.868 -2.764 c
+29.134 -2.323 l
+29.398 -2.587 29.695 -2.72 30.03 -2.72 c
+30.567 -2.72 30.842 -2.425 30.853 -1.837 c
+30.853 -1.309 l
+30.647 -1.625 30.346 -1.779 29.957 -1.779 c
+29.545 -1.779 29.221 -1.628 28.986 -1.323 c
+28.758 -1.01 28.641 -0.559 28.633 0.029 c
+h
+29.296 0.088 m
+29.296 -0.353 29.358 -0.684 29.487 -0.897 c
+29.612 -1.103 29.828 -1.205 30.133 -1.205 c
+30.456 -1.205 30.695 -1.04 30.853 -0.706 c
+30.853 1.278 l
+30.684 1.602 30.445 1.764 30.133 1.764 c
+29.839 1.764 29.622 1.66 29.487 1.455 c
+29.358 1.249 29.296 0.926 29.296 0.484 c
+h
+35.837 1.66 m
+35.748 1.679 35.649 1.691 35.542 1.691 c
+35.208 1.691 34.973 1.506 34.837 1.147 c
+34.837 -1.706 l
+34.19 -1.706 l
+34.19 2.278 l
+34.822 2.278 l
+34.837 1.866 l
+35.013 2.19 35.256 2.352 35.571 2.352 c
+35.678 2.352 35.766 2.329 35.837 2.293 c
+h
+37.836 -1.779 m
+37.335 -1.779 36.953 -1.632 36.689 -1.338 c
+36.424 -1.044 36.292 -0.611 36.292 -0.029 c
+36.292 0.441 l
+36.292 1.036 36.417 1.502 36.674 1.837 c
+36.939 2.179 37.298 2.352 37.762 2.352 c
+38.221 2.352 38.563 2.198 38.79 1.896 c
+39.026 1.602 39.147 1.139 39.158 0.515 c
+39.158 0.088 l
+36.939 0.088 l
+36.939 0 l
+36.939 -0.434 37.016 -0.746 37.174 -0.941 c
+37.339 -1.128 37.57 -1.22 37.865 -1.22 c
+38.06 -1.22 38.233 -1.187 38.379 -1.118 c
+38.526 -1.04 38.663 -0.923 38.79 -0.765 c
+39.129 -1.176 l
+38.842 -1.58 38.412 -1.779 37.836 -1.779 c
+37.762 1.793 m
+37.487 1.793 37.284 1.697 37.159 1.514 c
+37.03 1.326 36.957 1.036 36.939 0.646 c
+38.511 0.646 l
+38.511 0.735 l
+38.489 1.117 38.424 1.385 38.306 1.543 c
+38.188 1.708 38.004 1.793 37.762 1.793 c
+40.232 -1.706 m
+40.232 1.749 l
+39.702 1.749 l
+39.702 2.278 l
+40.232 2.278 l
+40.232 2.734 l
+40.232 3.135 40.327 3.447 40.525 3.675 c
+40.731 3.898 41.011 4.012 41.363 4.012 c
+41.499 4.012 41.632 3.991 41.76 3.954 c
+41.73 3.41 l
+41.632 3.428 41.532 3.439 41.437 3.439 c
+41.061 3.439 40.878 3.175 40.878 2.645 c
+40.878 2.278 l
+41.554 2.278 l
+41.554 1.749 l
+40.878 1.749 l
+40.878 -1.706 l
+h
+43.656 -1.779 m
+43.156 -1.779 42.774 -1.632 42.509 -1.338 c
+42.245 -1.044 42.113 -0.611 42.113 -0.029 c
+42.113 0.441 l
+42.113 1.036 42.237 1.502 42.495 1.837 c
+42.759 2.179 43.12 2.352 43.583 2.352 c
+44.042 2.352 44.384 2.198 44.612 1.896 c
+44.847 1.602 44.968 1.139 44.979 0.515 c
+44.979 0.088 l
+42.759 0.088 l
+42.759 0 l
+42.759 -0.434 42.837 -0.746 42.995 -0.941 c
+43.16 -1.128 43.392 -1.22 43.685 -1.22 c
+43.881 -1.22 44.053 -1.187 44.2 -1.118 c
+44.347 -1.04 44.483 -0.923 44.612 -0.765 c
+44.949 -1.176 l
+44.663 -1.58 44.233 -1.779 43.656 -1.779 c
+43.583 1.793 m
+43.307 1.793 43.105 1.697 42.98 1.514 c
+42.852 1.326 42.778 1.036 42.759 0.646 c
+44.332 0.646 l
+44.332 0.735 l
+44.311 1.117 44.244 1.385 44.126 1.543 c
+44.009 1.708 43.825 1.793 43.583 1.793 c
+47.405 1.66 m
+47.316 1.679 47.217 1.691 47.11 1.691 c
+46.776 1.691 46.541 1.506 46.405 1.147 c
+46.405 -1.706 l
+45.759 -1.706 l
+45.759 2.278 l
+46.39 2.278 l
+46.405 1.866 l
+46.582 2.19 46.823 2.352 47.14 2.352 c
+47.247 2.352 47.334 2.329 47.405 2.293 c
+h
+49.404 -1.779 m
+48.903 -1.779 48.521 -1.632 48.257 -1.338 c
+47.993 -1.044 47.86 -0.611 47.86 -0.029 c
+47.86 0.441 l
+47.86 1.036 47.985 1.502 48.242 1.837 c
+48.506 2.179 48.867 2.352 49.33 2.352 c
+49.79 2.352 50.131 2.198 50.359 1.896 c
+50.594 1.602 50.715 1.139 50.727 0.515 c
+50.727 0.088 l
+48.506 0.088 l
+48.506 0 l
+48.506 -0.434 48.584 -0.746 48.742 -0.941 c
+48.907 -1.128 49.139 -1.22 49.433 -1.22 c
+49.628 -1.22 49.8 -1.187 49.948 -1.118 c
+50.094 -1.04 50.231 -0.923 50.359 -0.765 c
+50.697 -1.176 l
+50.41 -1.58 49.981 -1.779 49.404 -1.779 c
+49.33 1.793 m
+49.055 1.793 48.853 1.697 48.727 1.514 c
+48.599 1.326 48.525 1.036 48.506 0.646 c
+50.079 0.646 l
+50.079 0.735 l
+50.058 1.117 49.992 1.385 49.874 1.543 c
+49.757 1.708 49.572 1.793 49.33 1.793 c
+52.123 2.278 m
+52.138 1.837 l
+52.391 2.179 52.714 2.352 53.108 2.352 c
+53.813 2.352 54.17 1.882 54.181 0.941 c
+54.181 -1.706 l
+53.534 -1.706 l
+53.534 0.912 l
+53.534 1.224 53.479 1.444 53.372 1.573 c
+53.262 1.697 53.108 1.764 52.902 1.764 c
+52.743 1.764 52.597 1.708 52.461 1.602 c
+52.332 1.492 52.229 1.356 52.152 1.19 c
+52.152 -1.706 l
+51.505 -1.706 l
+51.505 2.278 l
+h
+56.533 -1.22 m
+56.745 -1.22 56.918 -1.158 57.047 -1.029 c
+57.183 -0.893 57.256 -0.702 57.268 -0.456 c
+57.885 -0.456 l
+57.863 -0.838 57.727 -1.158 57.474 -1.411 c
+57.216 -1.658 56.903 -1.779 56.533 -1.779 c
+56.04 -1.779 55.665 -1.628 55.401 -1.323 c
+55.143 -1.01 55.018 -0.544 55.018 0.073 c
+55.018 0.515 l
+55.018 1.109 55.143 1.565 55.401 1.882 c
+55.665 2.194 56.04 2.352 56.533 2.352 c
+56.933 2.352 57.252 2.219 57.488 1.955 c
+57.73 1.697 57.863 1.352 57.885 0.912 c
+57.268 0.912 l
+57.246 1.205 57.172 1.425 57.047 1.573 c
+56.93 1.72 56.757 1.793 56.533 1.793 c
+56.238 1.793 56.022 1.694 55.886 1.5 c
+55.746 1.311 55.673 1.003 55.665 0.573 c
+55.665 0.058 l
+55.665 -0.412 55.731 -0.746 55.871 -0.941 c
+56.018 -1.128 56.238 -1.22 56.533 -1.22 c
+60.061 -1.779 m
+59.56 -1.779 59.178 -1.632 58.914 -1.338 c
+58.649 -1.044 58.517 -0.611 58.517 -0.029 c
+58.517 0.441 l
+58.517 1.036 58.642 1.502 58.899 1.837 c
+59.164 2.179 59.523 2.352 59.987 2.352 c
+60.446 2.352 60.788 2.198 61.015 1.896 c
+61.251 1.602 61.372 1.139 61.383 0.515 c
+61.383 0.088 l
+59.164 0.088 l
+59.164 0 l
+59.164 -0.434 59.241 -0.746 59.399 -0.941 c
+59.564 -1.128 59.795 -1.22 60.09 -1.22 c
+60.284 -1.22 60.458 -1.187 60.604 -1.118 c
+60.751 -1.04 60.887 -0.923 61.015 -0.765 c
+61.354 -1.176 l
+61.067 -1.58 60.637 -1.779 60.061 -1.779 c
+59.987 1.793 m
+59.712 1.793 59.509 1.697 59.384 1.514 c
+59.255 1.326 59.182 1.036 59.164 0.646 c
+60.736 0.646 l
+60.736 0.735 l
+60.714 1.117 60.649 1.385 60.531 1.543 c
+60.413 1.708 60.229 1.793 59.987 1.793 c
+64.499 2.278 m
+64.514 1.837 l
+64.768 2.179 65.091 2.352 65.484 2.352 c
+66.189 2.352 66.546 1.882 66.557 0.941 c
+66.557 -1.706 l
+65.91 -1.706 l
+65.91 0.912 l
+65.91 1.224 65.856 1.444 65.749 1.573 c
+65.638 1.697 65.484 1.764 65.279 1.764 c
+65.121 1.764 64.973 1.708 64.838 1.602 c
+64.709 1.492 64.606 1.356 64.529 1.19 c
+64.529 -1.706 l
+63.882 -1.706 l
+63.882 2.278 l
+h
+69.57 -1.706 m
+69.53 -1.617 69.505 -1.47 69.497 -1.264 c
+69.262 -1.61 68.967 -1.779 68.615 -1.779 c
+68.252 -1.779 67.968 -1.683 67.762 -1.484 c
+67.564 -1.279 67.469 -0.992 67.469 -0.617 c
+67.469 -0.217 67.604 0.103 67.88 0.338 c
+68.152 0.58 68.527 0.706 68.998 0.706 c
+69.482 0.706 l
+69.482 1.132 l
+69.482 1.367 69.428 1.532 69.32 1.631 c
+69.21 1.738 69.048 1.793 68.836 1.793 c
+68.637 1.793 68.475 1.735 68.35 1.617 c
+68.233 1.5 68.174 1.352 68.174 1.176 c
+67.527 1.176 l
+67.527 1.371 67.586 1.562 67.704 1.749 c
+67.829 1.932 67.99 2.08 68.189 2.19 c
+68.395 2.296 68.622 2.352 68.88 2.352 c
+69.281 2.352 69.586 2.248 69.791 2.043 c
+70.004 1.837 70.118 1.543 70.129 1.161 c
+70.129 -0.852 l
+70.129 -1.158 70.166 -1.422 70.247 -1.646 c
+70.247 -1.706 l
+h
+68.703 -1.191 m
+68.869 -1.191 69.019 -1.147 69.159 -1.058 c
+69.306 -0.97 69.412 -0.86 69.482 -0.721 c
+69.482 0.22 l
+69.115 0.22 l
+68.799 0.22 68.556 0.151 68.38 0.014 c
+68.204 -0.114 68.115 -0.302 68.115 -0.544 c
+68.115 -0.772 68.159 -0.937 68.248 -1.044 c
+68.336 -1.143 68.487 -1.191 68.703 -1.191 c
+71.746 2.278 m
+71.761 1.911 l
+72.003 2.205 72.323 2.352 72.716 2.352 c
+73.158 2.352 73.466 2.153 73.642 1.764 c
+73.896 2.153 74.245 2.352 74.686 2.352 c
+75.421 2.352 75.795 1.889 75.818 0.97 c
+75.818 -1.706 l
+75.171 -1.706 l
+75.171 0.912 l
+75.171 1.205 75.116 1.419 75.009 1.558 c
+74.91 1.694 74.737 1.764 74.494 1.764 c
+74.297 1.764 74.135 1.683 74.01 1.529 c
+73.892 1.382 73.823 1.19 73.804 0.955 c
+73.804 -1.706 l
+73.142 -1.706 l
+73.142 0.941 l
+73.142 1.488 72.922 1.764 72.481 1.764 c
+72.146 1.764 71.911 1.602 71.776 1.278 c
+71.776 -1.706 l
+71.128 -1.706 l
+71.128 2.278 l
+h
+78.214 -1.779 m
+77.714 -1.779 77.332 -1.632 77.067 -1.338 c
+76.803 -1.044 76.67 -0.611 76.67 -0.029 c
+76.67 0.441 l
+76.67 1.036 76.795 1.502 77.052 1.837 c
+77.317 2.179 77.677 2.352 78.14 2.352 c
+78.6 2.352 78.941 2.198 79.169 1.896 c
+79.404 1.602 79.525 1.139 79.537 0.515 c
+79.537 0.088 l
+77.317 0.088 l
+77.317 0 l
+77.317 -0.434 77.394 -0.746 77.552 -0.941 c
+77.717 -1.128 77.949 -1.22 78.243 -1.22 c
+78.438 -1.22 78.61 -1.187 78.758 -1.118 c
+78.905 -1.04 79.04 -0.923 79.169 -0.765 c
+79.508 -1.176 l
+79.221 -1.58 78.791 -1.779 78.214 -1.779 c
+78.14 1.793 m
+77.864 1.793 77.663 1.697 77.538 1.514 c
+77.409 1.326 77.335 1.036 77.317 0.646 c
+78.889 0.646 l
+78.889 0.735 l
+78.868 1.117 78.802 1.385 78.684 1.543 c
+78.567 1.708 78.382 1.793 78.14 1.793 c
+80.183 0.47 m
+80.183 1.076 80.294 1.543 80.522 1.866 c
+80.757 2.19 81.083 2.352 81.507 2.352 c
+81.889 2.352 82.186 2.194 82.403 1.882 c
+82.403 3.939 l
+83.049 3.939 l
+83.049 -1.706 l
+82.461 -1.706 l
+82.417 -1.279 l
+82.212 -1.613 81.906 -1.779 81.507 -1.779 c
+81.095 -1.779 80.771 -1.625 80.536 -1.309 c
+80.301 -0.985 80.183 -0.53 80.183 0.058 c
+h
+80.83 0.088 m
+80.83 -0.353 80.892 -0.684 81.021 -0.897 c
+81.157 -1.103 81.378 -1.205 81.683 -1.205 c
+82.006 -1.205 82.245 -1.044 82.403 -0.721 c
+82.403 1.294 l
+82.234 1.606 81.995 1.764 81.683 1.764 c
+81.378 1.764 81.157 1.66 81.021 1.455 c
+80.892 1.249 80.83 0.926 80.83 0.484 c
+h
+f
+Q
+q 1 0 0 1 387.291 422.2537 cm
+0 0 m
+0.029 -0.397 l
+0.264 -0.085 0.566 0.073 0.941 0.073 c
+1.625 0.073 1.977 -0.408 1.999 -1.367 c
+1.999 -3.984 l
+0.956 -3.984 l
+0.956 -1.441 l
+0.956 -1.216 0.919 -1.055 0.852 -0.956 c
+0.783 -0.86 0.665 -0.809 0.5 -0.809 c
+0.312 -0.809 0.166 -0.904 0.059 -1.088 c
+0.059 -3.984 l
+-0.985 -3.984 l
+-0.985 0 l
+h
+4.63 -3.984 m
+4.601 -3.925 4.571 -3.822 4.542 -3.675 c
+4.355 -3.932 4.104 -4.057 3.792 -4.057 c
+3.458 -4.057 3.179 -3.951 2.955 -3.734 c
+2.738 -3.51 2.631 -3.219 2.631 -2.866 c
+2.631 -2.455 2.764 -2.139 3.028 -1.912 c
+3.293 -1.676 3.675 -1.559 4.175 -1.559 c
+4.498 -1.559 l
+4.498 -1.235 l
+4.498 -1.058 4.461 -0.937 4.395 -0.867 c
+4.336 -0.79 4.248 -0.75 4.131 -0.75 c
+3.873 -0.75 3.748 -0.904 3.748 -1.206 c
+2.705 -1.206 l
+2.705 -0.834 2.841 -0.53 3.117 -0.294 c
+3.389 -0.052 3.738 0.073 4.16 0.073 c
+4.601 0.073 4.939 -0.044 5.174 -0.279 c
+5.417 -0.507 5.542 -0.831 5.542 -1.249 c
+5.542 -3.117 l
+5.542 -3.462 5.589 -3.73 5.689 -3.925 c
+5.689 -3.984 l
+h
+4.027 -3.234 m
+4.135 -3.234 4.226 -3.215 4.307 -3.175 c
+4.395 -3.128 4.457 -3.069 4.498 -2.999 c
+4.498 -2.176 l
+4.248 -2.176 l
+4.072 -2.176 3.929 -2.234 3.822 -2.352 c
+3.723 -2.463 3.675 -2.61 3.675 -2.793 c
+3.675 -3.088 3.792 -3.234 4.027 -3.234 c
+7.262 0 m
+7.291 -0.368 l
+7.526 -0.073 7.835 0.073 8.216 0.073 c
+8.617 0.073 8.897 -0.111 9.055 -0.47 c
+9.29 -0.111 9.617 0.073 10.04 0.073 c
+10.734 0.073 11.087 -0.412 11.098 -1.382 c
+11.098 -3.984 l
+10.069 -3.984 l
+10.069 -1.441 l
+10.069 -1.216 10.032 -1.055 9.966 -0.956 c
+9.907 -0.86 9.797 -0.809 9.643 -0.809 c
+9.444 -0.809 9.305 -0.927 9.216 -1.162 c
+9.216 -3.984 l
+8.173 -3.984 l
+8.173 -1.455 l
+8.173 -1.22 8.143 -1.055 8.085 -0.956 c
+8.025 -0.86 7.915 -0.809 7.761 -0.809 c
+7.585 -0.809 7.441 -0.904 7.335 -1.088 c
+7.335 -3.984 l
+6.292 -3.984 l
+6.292 0 l
+h
+13.479 -4.057 m
+12.95 -4.057 12.531 -3.903 12.23 -3.587 c
+11.936 -3.263 11.788 -2.804 11.788 -2.205 c
+11.788 -1.897 l
+11.788 -1.272 11.925 -0.786 12.2 -0.441 c
+12.472 -0.1 12.866 0.073 13.376 0.073 c
+13.876 0.073 14.247 -0.088 14.493 -0.412 c
+14.747 -0.736 14.88 -1.213 14.89 -1.837 c
+14.89 -2.338 l
+12.818 -2.338 l
+12.836 -2.631 12.898 -2.849 13.009 -2.984 c
+13.126 -3.124 13.307 -3.19 13.552 -3.19 c
+13.895 -3.19 14.184 -3.072 14.42 -2.837 c
+14.832 -3.469 l
+14.703 -3.645 14.516 -3.789 14.273 -3.896 c
+14.026 -4.002 13.762 -4.057 13.479 -4.057 c
+12.833 -1.617 m
+13.861 -1.617 l
+13.861 -1.515 l
+13.861 -1.279 13.82 -1.103 13.743 -0.985 c
+13.674 -0.86 13.546 -0.794 13.361 -0.794 c
+13.185 -0.794 13.053 -0.864 12.964 -1 c
+12.883 -1.129 12.84 -1.334 12.833 -1.617 c
+f
+Q
+q 1 0 0 1 404.8857 418.2697 cm
+0 0 m
+0 3.454 l
+-0.529 3.454 l
+-0.529 3.984 l
+0 3.984 l
+0 4.439 l
+0 4.84 0.096 5.152 0.294 5.38 c
+0.5 5.604 0.779 5.718 1.132 5.718 c
+1.268 5.718 1.4 5.696 1.529 5.66 c
+1.5 5.116 l
+1.4 5.134 1.301 5.145 1.205 5.145 c
+0.831 5.145 0.647 4.881 0.647 4.351 c
+0.647 3.984 l
+1.323 3.984 l
+1.323 3.454 l
+0.647 3.454 l
+0.647 0 l
+h
+1.955 2.176 m
+1.955 2.753 2.091 3.208 2.367 3.543 c
+2.65 3.884 3.021 4.057 3.484 4.057 c
+3.944 4.057 4.31 3.888 4.586 3.558 c
+4.869 3.234 5.016 2.786 5.027 2.22 c
+5.027 1.794 l
+5.027 1.224 4.884 0.769 4.601 0.426 c
+4.326 0.092 3.958 -0.073 3.499 -0.073 c
+3.036 -0.073 2.664 0.088 2.381 0.412 c
+2.105 0.742 1.962 1.183 1.955 1.735 c
+h
+2.602 1.794 m
+2.602 1.389 2.679 1.073 2.837 0.838 c
+3.003 0.603 3.223 0.485 3.499 0.485 c
+4.065 0.485 4.358 0.897 4.38 1.72 c
+4.38 2.176 l
+4.38 2.577 4.296 2.896 4.131 3.131 c
+3.973 3.373 3.755 3.499 3.484 3.499 c
+3.219 3.499 3.003 3.373 2.837 3.131 c
+2.679 2.896 2.602 2.577 2.602 2.176 c
+h
+7.512 3.366 m
+7.423 3.385 7.324 3.396 7.217 3.396 c
+6.883 3.396 6.648 3.212 6.512 2.852 c
+6.512 0 l
+5.865 0 l
+5.865 3.984 l
+6.497 3.984 l
+6.512 3.572 l
+6.689 3.896 6.93 4.057 7.247 4.057 c
+7.354 4.057 7.441 4.035 7.512 3.998 c
+h
+11.26 0.485 m
+11.473 0.485 11.645 0.548 11.774 0.676 c
+11.91 0.813 11.983 1.004 11.994 1.249 c
+12.612 1.249 l
+12.59 0.867 12.454 0.548 12.2 0.294 c
+11.944 0.048 11.631 -0.073 11.26 -0.073 c
+10.768 -0.073 10.392 0.077 10.128 0.383 c
+9.87 0.695 9.746 1.162 9.746 1.779 c
+9.746 2.22 l
+9.746 2.815 9.87 3.271 10.128 3.587 c
+10.392 3.899 10.768 4.057 11.26 4.057 c
+11.66 4.057 11.98 3.925 12.215 3.66 c
+12.457 3.403 12.59 3.057 12.612 2.617 c
+11.994 2.617 l
+11.973 2.911 11.899 3.131 11.774 3.279 c
+11.657 3.425 11.484 3.499 11.26 3.499 c
+10.965 3.499 10.749 3.4 10.612 3.205 c
+10.473 3.017 10.4 2.708 10.392 2.279 c
+10.392 1.764 l
+10.392 1.294 10.458 0.96 10.598 0.765 c
+10.745 0.578 10.965 0.485 11.26 0.485 c
+15.39 0.353 m
+15.173 0.067 14.861 -0.073 14.45 -0.073 c
+14.086 -0.073 13.81 0.048 13.627 0.294 c
+13.45 0.548 13.355 0.912 13.347 1.382 c
+13.347 3.984 l
+13.993 3.984 l
+13.993 1.441 l
+13.993 0.813 14.178 0.5 14.552 0.5 c
+14.953 0.5 15.229 0.676 15.375 1.029 c
+15.375 3.984 l
+16.022 3.984 l
+16.022 0 l
+15.405 0 l
+h
+18.653 3.366 m
+18.565 3.385 18.466 3.396 18.359 3.396 c
+18.025 3.396 17.789 3.212 17.654 2.852 c
+17.654 0 l
+17.007 0 l
+17.007 3.984 l
+17.639 3.984 l
+17.654 3.572 l
+17.83 3.896 18.073 4.057 18.389 4.057 c
+18.495 4.057 18.583 4.035 18.653 3.998 c
+h
+20.947 3.366 m
+20.858 3.385 20.759 3.396 20.652 3.396 c
+20.318 3.396 20.083 3.212 19.947 2.852 c
+19.947 0 l
+19.3 0 l
+19.3 3.984 l
+19.932 3.984 l
+19.947 3.572 l
+20.123 3.896 20.366 4.057 20.682 4.057 c
+20.788 4.057 20.877 4.035 20.947 3.998 c
+h
+22.946 -0.073 m
+22.446 -0.073 22.063 0.073 21.799 0.368 c
+21.534 0.661 21.402 1.095 21.402 1.676 c
+21.402 2.147 l
+21.402 2.741 21.527 3.208 21.784 3.543 c
+22.049 3.884 22.409 4.057 22.872 4.057 c
+23.331 4.057 23.673 3.903 23.9 3.601 c
+24.136 3.308 24.257 2.845 24.268 2.22 c
+24.268 1.794 l
+22.049 1.794 l
+22.049 1.706 l
+22.049 1.272 22.126 0.96 22.284 0.765 c
+22.45 0.578 22.681 0.485 22.975 0.485 c
+23.169 0.485 23.343 0.518 23.489 0.588 c
+23.636 0.665 23.772 0.783 23.9 0.941 c
+24.239 0.53 l
+23.952 0.125 23.522 -0.073 22.946 -0.073 c
+22.872 3.499 m
+22.597 3.499 22.394 3.403 22.269 3.219 c
+22.14 3.032 22.067 2.741 22.049 2.352 c
+23.622 2.352 l
+23.622 2.44 l
+23.599 2.822 23.534 3.09 23.416 3.248 c
+23.298 3.414 23.115 3.499 22.872 3.499 c
+25.664 3.984 m
+25.679 3.543 l
+25.933 3.884 26.256 4.057 26.649 4.057 c
+27.355 4.057 27.711 3.587 27.723 2.646 c
+27.723 0 l
+27.075 0 l
+27.075 2.617 l
+27.075 2.929 27.021 3.15 26.915 3.279 c
+26.804 3.403 26.649 3.469 26.444 3.469 c
+26.286 3.469 26.138 3.414 26.003 3.308 c
+25.874 3.198 25.772 3.061 25.694 2.896 c
+25.694 0 l
+25.047 0 l
+25.047 3.984 l
+h
+29.545 4.939 m
+29.545 3.984 l
+30.148 3.984 l
+30.148 3.454 l
+29.545 3.454 l
+29.545 0.985 l
+29.545 0.827 29.567 0.709 29.619 0.632 c
+29.677 0.551 29.766 0.515 29.884 0.515 c
+29.972 0.515 30.059 0.53 30.148 0.559 c
+30.148 0 l
+30.001 -0.047 29.847 -0.073 29.693 -0.073 c
+29.435 -0.073 29.24 0.019 29.105 0.206 c
+28.964 0.389 28.899 0.651 28.899 0.985 c
+28.899 3.454 l
+28.296 3.454 l
+28.296 3.984 l
+28.899 3.984 l
+28.899 4.939 l
+h
+34.013 0.485 m
+34.227 0.485 34.4 0.548 34.528 0.676 c
+34.664 0.813 34.738 1.004 34.749 1.249 c
+35.366 1.249 l
+35.345 0.867 35.208 0.548 34.954 0.294 c
+34.697 0.048 34.385 -0.073 34.013 -0.073 c
+33.521 -0.073 33.147 0.077 32.882 0.383 c
+32.625 0.695 32.5 1.162 32.5 1.779 c
+32.5 2.22 l
+32.5 2.815 32.625 3.271 32.882 3.587 c
+33.147 3.899 33.521 4.057 34.013 4.057 c
+34.414 4.057 34.734 3.925 34.969 3.66 c
+35.212 3.403 35.345 3.057 35.366 2.617 c
+34.749 2.617 l
+34.726 2.911 34.653 3.131 34.528 3.279 c
+34.41 3.425 34.238 3.499 34.013 3.499 c
+33.72 3.499 33.503 3.4 33.367 3.205 c
+33.228 3.017 33.154 2.708 33.147 2.279 c
+33.147 1.764 l
+33.147 1.294 33.213 0.96 33.352 0.765 c
+33.5 0.578 33.72 0.485 34.013 0.485 c
+35.983 2.176 m
+35.983 2.753 36.12 3.208 36.395 3.543 c
+36.678 3.884 37.049 4.057 37.512 4.057 c
+37.972 4.057 38.339 3.888 38.615 3.558 c
+38.898 3.234 39.044 2.786 39.056 2.22 c
+39.056 1.794 l
+39.056 1.224 38.913 0.769 38.63 0.426 c
+38.354 0.092 37.986 -0.073 37.527 -0.073 c
+37.064 -0.073 36.693 0.088 36.409 0.412 c
+36.134 0.742 35.991 1.183 35.983 1.735 c
+h
+36.63 1.794 m
+36.63 1.389 36.708 1.073 36.866 0.838 c
+37.031 0.603 37.251 0.485 37.527 0.485 c
+38.092 0.485 38.387 0.897 38.409 1.72 c
+38.409 2.176 l
+38.409 2.577 38.324 2.896 38.159 3.131 c
+38.001 3.373 37.784 3.499 37.512 3.499 c
+37.248 3.499 37.031 3.373 36.866 3.131 c
+36.708 2.896 36.63 2.577 36.63 2.176 c
+h
+40.511 3.984 m
+40.525 3.616 l
+40.768 3.911 41.088 4.057 41.481 4.057 c
+41.922 4.057 42.231 3.859 42.407 3.469 c
+42.661 3.859 43.01 4.057 43.451 4.057 c
+44.186 4.057 44.56 3.595 44.583 2.675 c
+44.583 0 l
+43.935 0 l
+43.935 2.617 l
+43.935 2.911 43.881 3.124 43.774 3.263 c
+43.675 3.4 43.502 3.469 43.26 3.469 c
+43.061 3.469 42.9 3.389 42.774 3.234 c
+42.657 3.088 42.587 2.896 42.568 2.66 c
+42.568 0 l
+41.907 0 l
+41.907 2.646 l
+41.907 3.194 41.687 3.469 41.246 3.469 c
+40.911 3.469 40.676 3.308 40.54 2.984 c
+40.54 0 l
+39.893 0 l
+39.893 3.984 l
+h
+46.17 3.984 m
+46.185 3.616 l
+46.427 3.911 46.747 4.057 47.14 4.057 c
+47.581 4.057 47.889 3.859 48.066 3.469 c
+48.32 3.859 48.669 4.057 49.109 4.057 c
+49.845 4.057 50.219 3.595 50.241 2.675 c
+50.241 0 l
+49.595 0 l
+49.595 2.617 l
+49.595 2.911 49.539 3.124 49.433 3.263 c
+49.334 3.4 49.161 3.469 48.918 3.469 c
+48.72 3.469 48.558 3.389 48.434 3.234 c
+48.316 3.088 48.246 2.896 48.228 2.66 c
+48.228 0 l
+47.567 0 l
+47.567 2.646 l
+47.567 3.194 47.346 3.469 46.905 3.469 c
+46.57 3.469 46.335 3.308 46.2 2.984 c
+46.2 0 l
+45.552 0 l
+45.552 3.984 l
+h
+51.918 0 -0.647 3.984 re
+51.962 5.027 m
+51.962 4.917 51.932 4.825 51.873 4.748 c
+51.814 4.678 51.719 4.645 51.594 4.645 c
+51.476 4.645 51.38 4.678 51.315 4.748 c
+51.256 4.825 51.226 4.917 51.226 5.027 c
+51.226 5.145 51.256 5.237 51.315 5.307 c
+51.38 5.384 51.476 5.424 51.594 5.424 c
+51.719 5.424 51.814 5.384 51.873 5.307 c
+51.932 5.226 51.962 5.134 51.962 5.027 c
+53.784 4.939 m
+53.784 3.984 l
+54.387 3.984 l
+54.387 3.454 l
+53.784 3.454 l
+53.784 0.985 l
+53.784 0.827 53.807 0.709 53.857 0.632 c
+53.917 0.551 54.004 0.515 54.122 0.515 c
+54.21 0.515 54.299 0.53 54.387 0.559 c
+54.387 0 l
+54.239 -0.047 54.085 -0.073 53.931 -0.073 c
+53.674 -0.073 53.479 0.019 53.343 0.206 c
+53.203 0.389 53.138 0.651 53.138 0.985 c
+53.138 3.454 l
+52.535 3.454 l
+52.535 3.984 l
+53.138 3.984 l
+53.138 4.939 l
+h
+55.195 0.353 m
+55.195 0.47 55.228 0.566 55.298 0.647 c
+55.364 0.724 55.467 0.765 55.607 0.765 c
+55.754 0.765 55.86 0.724 55.93 0.647 c
+56.007 0.566 56.047 0.47 56.047 0.353 c
+56.047 0.243 56.007 0.151 55.93 0.073 c
+55.86 -0.004 55.754 -0.044 55.607 -0.044 c
+55.467 -0.044 55.364 -0.004 55.298 0.073 c
+55.228 0.151 55.195 0.243 55.195 0.353 c
+61.443 1.397 m
+59.634 1.397 l
+59.222 0 l
+58.532 0 l
+60.252 5.351 l
+60.824 5.351 l
+62.559 0 l
+61.869 0 l
+h
+59.81 1.985 m
+61.266 1.985 l
+60.546 4.41 l
+h
+63.059 2.176 m
+63.059 2.782 63.17 3.248 63.397 3.572 c
+63.633 3.896 63.959 4.057 64.382 4.057 c
+64.764 4.057 65.062 3.899 65.279 3.587 c
+65.279 5.644 l
+65.925 5.644 l
+65.925 0 l
+65.337 0 l
+65.293 0.426 l
+65.088 0.092 64.783 -0.073 64.382 -0.073 c
+63.97 -0.073 63.647 0.081 63.411 0.397 c
+63.176 0.721 63.059 1.176 63.059 1.764 c
+h
+63.706 1.794 m
+63.706 1.353 63.768 1.022 63.897 0.809 c
+64.033 0.603 64.253 0.5 64.558 0.5 c
+64.882 0.5 65.121 0.661 65.279 0.985 c
+65.279 2.999 l
+65.109 3.311 64.871 3.469 64.558 3.469 c
+64.253 3.469 64.033 3.366 63.897 3.161 c
+63.768 2.955 63.706 2.631 63.706 2.19 c
+h
+66.792 2.176 m
+66.792 2.782 66.903 3.248 67.131 3.572 c
+67.366 3.896 67.693 4.057 68.115 4.057 c
+68.498 4.057 68.795 3.899 69.013 3.587 c
+69.013 5.644 l
+69.659 5.644 l
+69.659 0 l
+69.071 0 l
+69.027 0.426 l
+68.821 0.092 68.516 -0.073 68.115 -0.073 c
+67.704 -0.073 67.38 0.081 67.145 0.397 c
+66.91 0.721 66.792 1.176 66.792 1.764 c
+h
+67.44 1.794 m
+67.44 1.353 67.502 1.022 67.631 0.809 c
+67.766 0.603 67.987 0.5 68.292 0.5 c
+68.616 0.5 68.855 0.661 69.013 0.985 c
+69.013 2.999 l
+68.843 3.311 68.604 3.469 68.292 3.469 c
+67.987 3.469 67.766 3.366 67.631 3.161 c
+67.502 2.955 67.44 2.631 67.44 2.19 c
+h
+f
+Q
+q 1 0 0 1 478.602 419.0637 cm
+0 0 m
+0.294 0 0.444 0.195 0.455 0.588 c
+1.425 0.588 l
+1.425 0.154 1.294 -0.198 1.028 -0.47 c
+0.764 -0.735 0.426 -0.867 0.014 -0.867 c
+-0.497 -0.867 -0.89 -0.713 -1.162 -0.397 c
+-1.426 -0.073 -1.565 0.397 -1.573 1.014 c
+-1.573 1.338 l
+-1.573 1.962 -1.441 2.44 -1.176 2.764 c
+-0.904 3.094 -0.507 3.263 0.014 3.263 c
+0.444 3.263 0.786 3.123 1.043 2.851 c
+1.296 2.576 1.425 2.194 1.425 1.706 c
+0.455 1.706 l
+0.455 1.918 0.415 2.087 0.338 2.205 c
+0.268 2.33 0.151 2.396 -0.015 2.396 c
+-0.192 2.396 -0.32 2.33 -0.397 2.205 c
+-0.478 2.076 -0.522 1.826 -0.53 1.455 c
+-0.53 1.043 l
+-0.53 0.721 -0.515 0.492 -0.485 0.368 c
+-0.449 0.239 -0.393 0.147 -0.324 0.088 c
+-0.246 0.029 -0.14 0 0 0 c
+1.911 1.323 m
+1.911 1.929 2.05 2.404 2.337 2.749 c
+2.62 3.09 3.013 3.263 3.513 3.263 c
+4.02 3.263 4.417 3.09 4.704 2.749 c
+4.986 2.404 5.13 1.929 5.13 1.323 c
+5.13 1.058 l
+5.13 0.459 4.986 -0.011 4.704 -0.353 c
+4.417 -0.698 4.02 -0.867 3.513 -0.867 c
+3.002 -0.867 2.605 -0.698 2.322 -0.353 c
+2.046 -0.011 1.911 0.463 1.911 1.073 c
+h
+2.954 1.058 m
+2.954 0.353 3.138 0 3.513 0 c
+3.866 0 4.056 0.294 4.086 0.882 c
+4.086 1.323 l
+4.086 1.683 4.035 1.955 3.939 2.132 c
+3.84 2.308 3.696 2.396 3.513 2.396 c
+3.336 2.396 3.197 2.308 3.101 2.132 c
+3.002 1.955 2.954 1.683 2.954 1.323 c
+h
+6.746 3.19 m
+6.776 2.822 l
+7.011 3.117 7.32 3.263 7.702 3.263 c
+8.103 3.263 8.382 3.079 8.54 2.72 c
+8.775 3.079 9.102 3.263 9.525 3.263 c
+10.219 3.263 10.572 2.778 10.583 1.808 c
+10.583 -0.794 l
+9.554 -0.794 l
+9.554 1.749 l
+9.554 1.974 9.517 2.135 9.451 2.234 c
+9.392 2.33 9.282 2.381 9.128 2.381 c
+8.93 2.381 8.789 2.263 8.702 2.028 c
+8.702 -0.794 l
+7.658 -0.794 l
+7.658 1.735 l
+7.658 1.97 7.628 2.135 7.569 2.234 c
+7.511 2.33 7.401 2.381 7.247 2.381 c
+7.07 2.381 6.927 2.286 6.82 2.102 c
+6.82 -0.794 l
+5.776 -0.794 l
+5.776 3.19 l
+h
+12.332 3.19 m
+12.361 2.822 l
+12.596 3.117 12.905 3.263 13.288 3.263 c
+13.688 3.263 13.967 3.079 14.125 2.72 c
+14.36 3.079 14.688 3.263 15.11 3.263 c
+15.804 3.263 16.157 2.778 16.168 1.808 c
+16.168 -0.794 l
+15.139 -0.794 l
+15.139 1.749 l
+15.139 1.974 15.103 2.135 15.037 2.234 c
+14.978 2.33 14.867 2.381 14.713 2.381 c
+14.515 2.381 14.375 2.263 14.287 2.028 c
+14.287 -0.794 l
+13.244 -0.794 l
+13.244 1.735 l
+13.244 1.97 13.214 2.135 13.155 2.234 c
+13.096 2.33 12.986 2.381 12.832 2.381 c
+12.656 2.381 12.512 2.286 12.406 2.102 c
+12.406 -0.794 l
+11.362 -0.794 l
+11.362 3.19 l
+h
+18.035 -0.794 -1.043 3.984 re
+16.947 4.218 m
+16.947 4.373 16.995 4.501 17.094 4.601 c
+17.201 4.707 17.337 4.763 17.506 4.763 c
+17.682 4.763 17.819 4.707 17.917 4.601 c
+18.025 4.501 18.079 4.373 18.079 4.218 c
+18.079 4.05 18.025 3.913 17.917 3.807 c
+17.819 3.707 17.682 3.66 17.506 3.66 c
+17.337 3.66 17.201 3.707 17.094 3.807 c
+16.995 3.913 16.947 4.05 16.947 4.218 c
+20.049 4.16 m
+20.049 3.19 l
+20.579 3.19 l
+20.579 2.396 l
+20.049 2.396 l
+20.049 0.426 l
+20.049 0.268 20.068 0.162 20.108 0.103 c
+20.155 0.044 20.24 0.015 20.358 0.015 c
+20.465 0.015 20.549 0.022 20.608 0.044 c
+20.608 -0.764 l
+20.431 -0.831 20.24 -0.867 20.034 -0.867 c
+19.359 -0.867 19.013 -0.482 19.006 0.294 c
+19.006 2.396 l
+18.549 2.396 l
+18.549 3.19 l
+19.006 3.19 l
+19.006 4.16 l
+h
+24.65 0.279 m
+24.65 0.368 24.606 0.445 24.518 0.515 c
+24.429 0.592 24.242 0.694 23.959 0.823 c
+23.525 1 23.228 1.18 23.062 1.367 c
+22.904 1.55 22.827 1.783 22.827 2.058 c
+22.827 2.4 22.948 2.683 23.195 2.911 c
+23.448 3.146 23.787 3.263 24.209 3.263 c
+24.639 3.263 24.988 3.15 25.252 2.925 c
+25.518 2.697 25.649 2.396 25.649 2.014 c
+24.606 2.014 l
+24.606 2.337 24.466 2.499 24.194 2.499 c
+24.084 2.499 23.995 2.462 23.93 2.396 c
+23.86 2.326 23.827 2.227 23.827 2.102 c
+23.827 2.014 23.864 1.933 23.945 1.866 c
+24.022 1.808 24.201 1.712 24.488 1.588 c
+24.918 1.43 25.216 1.253 25.385 1.058 c
+25.561 0.871 25.649 0.621 25.649 0.309 c
+25.649 -0.044 25.518 -0.331 25.252 -0.544 c
+24.988 -0.761 24.639 -0.867 24.209 -0.867 c
+23.915 -0.867 23.654 -0.812 23.43 -0.706 c
+23.202 -0.588 23.026 -0.426 22.9 -0.22 c
+22.783 -0.015 22.725 0.206 22.725 0.441 c
+23.71 0.441 l
+23.71 0.254 23.746 0.118 23.827 0.029 c
+23.915 -0.058 24.047 -0.103 24.224 -0.103 c
+24.506 -0.103 24.65 0.022 24.65 0.279 c
+27.325 2.837 m
+27.55 3.119 27.825 3.263 28.148 3.263 c
+28.508 3.263 28.784 3.135 28.971 2.881 c
+29.167 2.624 29.266 2.242 29.266 1.735 c
+29.266 -0.794 l
+28.222 -0.794 l
+28.222 1.72 l
+28.222 1.955 28.182 2.12 28.105 2.219 c
+28.034 2.326 27.92 2.381 27.766 2.381 c
+27.579 2.381 27.432 2.296 27.325 2.132 c
+27.325 -0.794 l
+26.281 -0.794 l
+26.281 4.85 l
+27.325 4.85 l
+h
+31.897 -0.794 m
+31.868 -0.735 31.837 -0.632 31.808 -0.485 c
+31.621 -0.742 31.371 -0.867 31.059 -0.867 c
+30.725 -0.867 30.445 -0.761 30.221 -0.544 c
+30.004 -0.32 29.898 -0.029 29.898 0.324 c
+29.898 0.735 30.029 1.051 30.295 1.278 c
+30.559 1.514 30.941 1.631 31.44 1.631 c
+31.764 1.631 l
+31.764 1.955 l
+31.764 2.132 31.727 2.253 31.662 2.323 c
+31.602 2.4 31.515 2.44 31.397 2.44 c
+31.139 2.44 31.014 2.286 31.014 1.984 c
+29.971 1.984 l
+29.971 2.356 30.107 2.66 30.382 2.896 c
+30.655 3.138 31.004 3.263 31.426 3.263 c
+31.868 3.263 32.205 3.146 32.44 2.911 c
+32.683 2.683 32.808 2.359 32.808 1.941 c
+32.808 0.073 l
+32.808 -0.272 32.855 -0.54 32.955 -0.735 c
+32.955 -0.794 l
+h
+31.294 -0.044 m
+31.4 -0.044 31.492 -0.025 31.573 0.015 c
+31.662 0.062 31.724 0.121 31.764 0.191 c
+31.764 1.014 l
+31.515 1.014 l
+31.338 1.014 31.195 0.956 31.088 0.838 c
+30.989 0.727 30.941 0.58 30.941 0.397 c
+30.941 0.103 31.059 -0.044 31.294 -0.044 c
+f
+Q
+q 1 0 0 1 514.8346 423.2085 cm
+0 0 m
+0 -0.955 l
+0.603 -0.955 l
+0.603 -1.484 l
+0 -1.484 l
+0 -3.954 l
+0 -4.112 0.023 -4.23 0.073 -4.307 c
+0.133 -4.388 0.221 -4.424 0.339 -4.424 c
+0.426 -4.424 0.515 -4.409 0.603 -4.38 c
+0.603 -4.939 l
+0.456 -4.986 0.302 -5.012 0.147 -5.012 c
+-0.11 -5.012 -0.305 -4.92 -0.441 -4.733 c
+-0.58 -4.549 -0.646 -4.288 -0.646 -3.954 c
+-0.646 -1.484 l
+-1.249 -1.484 l
+-1.249 -0.955 l
+-0.646 -0.955 l
+-0.646 0 l
+h
+1.162 -2.763 m
+1.162 -2.186 1.297 -1.731 1.573 -1.396 c
+1.856 -1.055 2.227 -0.881 2.691 -0.881 c
+3.15 -0.881 3.517 -1.051 3.792 -1.381 c
+4.075 -1.705 4.223 -2.153 4.233 -2.719 c
+4.233 -3.145 l
+4.233 -3.715 4.09 -4.17 3.807 -4.513 c
+3.532 -4.847 3.164 -5.012 2.705 -5.012 c
+2.242 -5.012 1.871 -4.85 1.588 -4.527 c
+1.312 -4.197 1.168 -3.755 1.162 -3.204 c
+h
+1.808 -3.145 m
+1.808 -3.549 1.885 -3.865 2.043 -4.101 c
+2.209 -4.336 2.429 -4.453 2.705 -4.453 c
+3.271 -4.453 3.564 -4.042 3.587 -3.219 c
+3.587 -2.763 l
+3.587 -2.362 3.502 -2.043 3.337 -1.808 c
+3.179 -1.565 2.962 -1.44 2.691 -1.44 c
+2.425 -1.44 2.209 -1.565 2.043 -1.808 c
+1.885 -2.043 1.808 -2.362 1.808 -2.763 c
+h
+f
+Q
+q 1 0 0 1 388.0258 413.8011 cm
+0 0 m
+0 -0.955 l
+0.603 -0.955 l
+0.603 -1.484 l
+0 -1.484 l
+0 -3.954 l
+0 -4.112 0.023 -4.23 0.073 -4.307 c
+0.133 -4.388 0.221 -4.424 0.339 -4.424 c
+0.426 -4.424 0.515 -4.409 0.603 -4.38 c
+0.603 -4.939 l
+0.456 -4.986 0.302 -5.012 0.148 -5.012 c
+-0.11 -5.012 -0.305 -4.92 -0.44 -4.733 c
+-0.58 -4.549 -0.646 -4.288 -0.646 -3.954 c
+-0.646 -1.484 l
+-1.249 -1.484 l
+-1.249 -0.955 l
+-0.646 -0.955 l
+-0.646 0 l
+h
+3.41 -4.939 m
+3.37 -4.85 3.344 -4.704 3.337 -4.498 c
+3.102 -4.843 2.808 -5.012 2.455 -5.012 c
+2.091 -5.012 1.808 -4.916 1.602 -4.718 c
+1.405 -4.513 1.309 -4.226 1.309 -3.85 c
+1.309 -3.451 1.445 -3.131 1.72 -2.896 c
+1.992 -2.653 2.367 -2.528 2.837 -2.528 c
+3.323 -2.528 l
+3.323 -2.102 l
+3.323 -1.866 3.267 -1.701 3.161 -1.602 c
+3.051 -1.496 2.889 -1.44 2.675 -1.44 c
+2.477 -1.44 2.315 -1.499 2.19 -1.616 c
+2.072 -1.734 2.014 -1.882 2.014 -2.057 c
+1.367 -2.057 l
+1.367 -1.863 1.426 -1.672 1.544 -1.484 c
+1.669 -1.301 1.831 -1.153 2.029 -1.043 c
+2.234 -0.937 2.463 -0.881 2.72 -0.881 c
+3.12 -0.881 3.425 -0.985 3.631 -1.19 c
+3.844 -1.396 3.958 -1.691 3.969 -2.072 c
+3.969 -4.086 l
+3.969 -4.391 4.006 -4.656 4.087 -4.88 c
+4.087 -4.939 l
+h
+2.543 -4.424 m
+2.708 -4.424 2.859 -4.38 2.999 -4.292 c
+3.146 -4.203 3.252 -4.093 3.323 -3.954 c
+3.323 -3.013 l
+2.955 -3.013 l
+2.639 -3.013 2.396 -3.083 2.22 -3.219 c
+2.043 -3.347 1.955 -3.535 1.955 -3.777 c
+1.955 -4.006 1.999 -4.17 2.087 -4.278 c
+2.176 -4.376 2.326 -4.424 2.543 -4.424 c
+4.836 -2.763 m
+4.836 -2.146 4.946 -1.683 5.174 -1.367 c
+5.399 -1.043 5.733 -0.881 6.174 -0.881 c
+6.575 -0.881 6.88 -1.058 7.086 -1.411 c
+7.129 -0.955 l
+7.717 -0.955 l
+7.717 -4.983 l
+7.717 -5.471 7.589 -5.85 7.335 -6.115 c
+7.078 -6.379 6.725 -6.512 6.277 -6.512 c
+6.078 -6.512 5.858 -6.46 5.615 -6.364 c
+5.369 -6.265 5.189 -6.144 5.072 -5.997 c
+5.336 -5.556 l
+5.6 -5.82 5.898 -5.953 6.232 -5.953 c
+6.77 -5.953 7.045 -5.659 7.056 -5.071 c
+7.056 -4.542 l
+6.85 -4.858 6.548 -5.012 6.159 -5.012 c
+5.748 -5.012 5.424 -4.862 5.189 -4.556 c
+4.961 -4.244 4.844 -3.792 4.836 -3.204 c
+h
+5.498 -3.145 m
+5.498 -3.586 5.56 -3.917 5.689 -4.13 c
+5.814 -4.336 6.03 -4.438 6.336 -4.438 c
+6.659 -4.438 6.897 -4.274 7.056 -3.939 c
+7.056 -1.955 l
+6.887 -1.631 6.648 -1.469 6.336 -1.469 c
+6.041 -1.469 5.825 -1.573 5.689 -1.778 c
+5.56 -1.984 5.498 -2.308 5.498 -2.749 c
+h
+12.436 -4.939 m
+12.395 -4.85 12.37 -4.704 12.362 -4.498 c
+12.127 -4.843 11.833 -5.012 11.48 -5.012 c
+11.117 -5.012 10.834 -4.916 10.628 -4.718 c
+10.429 -4.513 10.334 -4.226 10.334 -3.85 c
+10.334 -3.451 10.469 -3.131 10.745 -2.896 c
+11.017 -2.653 11.392 -2.528 11.863 -2.528 c
+12.347 -2.528 l
+12.347 -2.102 l
+12.347 -1.866 12.293 -1.701 12.185 -1.602 c
+12.075 -1.496 11.914 -1.44 11.701 -1.44 c
+11.502 -1.44 11.341 -1.499 11.215 -1.616 c
+11.098 -1.734 11.04 -1.882 11.04 -2.057 c
+10.392 -2.057 l
+10.392 -1.863 10.452 -1.672 10.569 -1.484 c
+10.694 -1.301 10.855 -1.153 11.054 -1.043 c
+11.26 -0.937 11.487 -0.881 11.745 -0.881 c
+12.145 -0.881 12.451 -0.985 12.656 -1.19 c
+12.869 -1.396 12.983 -1.691 12.994 -2.072 c
+12.994 -4.086 l
+12.994 -4.391 13.031 -4.656 13.112 -4.88 c
+13.112 -4.939 l
+h
+11.568 -4.424 m
+11.734 -4.424 11.884 -4.38 12.025 -4.292 c
+12.171 -4.203 12.278 -4.093 12.347 -3.954 c
+12.347 -3.013 l
+11.98 -3.013 l
+11.664 -3.013 11.422 -3.083 11.245 -3.219 c
+11.069 -3.347 10.98 -3.535 10.98 -3.777 c
+10.98 -4.006 11.025 -4.17 11.113 -4.278 c
+11.201 -4.376 11.352 -4.424 11.568 -4.424 c
+17.654 -3.925 m
+17.654 -3.777 17.598 -3.656 17.492 -3.557 c
+17.382 -3.461 17.176 -3.343 16.875 -3.204 c
+16.53 -3.057 16.287 -2.936 16.14 -2.836 c
+15.993 -2.73 15.883 -2.612 15.817 -2.484 c
+15.747 -2.359 15.713 -2.201 15.713 -2.013 c
+15.713 -1.691 15.831 -1.421 16.066 -1.205 c
+16.301 -0.992 16.603 -0.881 16.978 -0.881 c
+17.359 -0.881 17.669 -0.995 17.904 -1.22 c
+18.139 -1.448 18.257 -1.734 18.257 -2.087 c
+17.61 -2.087 l
+17.61 -1.911 17.551 -1.76 17.434 -1.631 c
+17.316 -1.506 17.162 -1.44 16.978 -1.44 c
+16.779 -1.44 16.629 -1.496 16.522 -1.602 c
+16.412 -1.701 16.36 -1.833 16.36 -1.999 c
+16.36 -2.127 16.397 -2.234 16.478 -2.322 c
+16.555 -2.403 16.746 -2.506 17.051 -2.631 c
+17.529 -2.818 17.86 -3.006 18.036 -3.189 c
+18.213 -3.366 18.3 -3.594 18.3 -3.865 c
+18.3 -4.218 18.176 -4.498 17.933 -4.704 c
+17.698 -4.909 17.382 -5.012 16.993 -5.012 c
+16.57 -5.012 16.232 -4.895 15.978 -4.659 c
+15.721 -4.417 15.596 -4.112 15.596 -3.748 c
+16.243 -3.748 l
+16.25 -3.976 16.32 -4.152 16.449 -4.278 c
+16.574 -4.395 16.757 -4.453 16.993 -4.453 c
+17.205 -4.453 17.367 -4.405 17.477 -4.307 c
+17.595 -4.211 17.654 -4.083 17.654 -3.925 c
+22.02 -3.145 m
+22.02 -3.773 21.902 -4.244 21.667 -4.556 c
+21.439 -4.862 21.122 -5.012 20.711 -5.012 c
+20.307 -5.012 19.998 -4.862 19.786 -4.556 c
+19.786 -6.468 l
+19.138 -6.468 l
+19.138 -0.955 l
+19.726 -0.955 l
+19.771 -1.396 l
+19.984 -1.055 20.293 -0.881 20.696 -0.881 c
+21.137 -0.881 21.465 -1.036 21.681 -1.338 c
+21.895 -1.643 22.009 -2.098 22.02 -2.705 c
+h
+21.373 -2.763 m
+21.373 -2.322 21.303 -1.999 21.167 -1.793 c
+21.027 -1.579 20.807 -1.469 20.505 -1.469 c
+20.189 -1.469 19.95 -1.624 19.786 -1.926 c
+19.786 -3.998 l
+19.95 -4.303 20.189 -4.453 20.505 -4.453 c
+20.8 -4.453 21.012 -4.351 21.152 -4.145 c
+21.288 -3.931 21.361 -3.601 21.373 -3.16 c
+h
+24.283 -5.012 m
+23.784 -5.012 23.401 -4.866 23.137 -4.571 c
+22.872 -4.278 22.74 -3.844 22.74 -3.262 c
+22.74 -2.792 l
+22.74 -2.198 22.865 -1.731 23.121 -1.396 c
+23.387 -1.055 23.747 -0.881 24.21 -0.881 c
+24.669 -0.881 25.01 -1.036 25.238 -1.338 c
+25.473 -1.631 25.595 -2.094 25.606 -2.719 c
+25.606 -3.145 l
+23.387 -3.145 l
+23.387 -3.233 l
+23.387 -3.667 23.464 -3.979 23.622 -4.174 c
+23.788 -4.361 24.019 -4.453 24.312 -4.453 c
+24.507 -4.453 24.68 -4.421 24.827 -4.351 c
+24.974 -4.274 25.11 -4.156 25.238 -3.998 c
+25.577 -4.409 l
+25.29 -4.814 24.86 -5.012 24.283 -5.012 c
+24.21 -1.44 m
+23.934 -1.44 23.732 -1.536 23.607 -1.72 c
+23.478 -1.907 23.405 -2.198 23.387 -2.587 c
+24.96 -2.587 l
+24.96 -2.499 l
+24.937 -2.117 24.871 -1.849 24.754 -1.691 c
+24.636 -1.525 24.453 -1.44 24.21 -1.44 c
+27.767 -4.453 m
+27.98 -4.453 28.153 -4.391 28.281 -4.262 c
+28.417 -4.126 28.491 -3.935 28.502 -3.69 c
+29.119 -3.69 l
+29.097 -4.072 28.962 -4.391 28.708 -4.644 c
+28.451 -4.891 28.138 -5.012 27.767 -5.012 c
+27.275 -5.012 26.899 -4.862 26.635 -4.556 c
+26.377 -4.244 26.253 -3.777 26.253 -3.16 c
+26.253 -2.719 l
+26.253 -2.124 26.377 -1.668 26.635 -1.352 c
+26.899 -1.04 27.275 -0.881 27.767 -0.881 c
+28.168 -0.881 28.487 -1.014 28.723 -1.278 c
+28.964 -1.536 29.097 -1.882 29.119 -2.322 c
+28.502 -2.322 l
+28.48 -2.028 28.407 -1.808 28.281 -1.66 c
+28.164 -1.514 27.991 -1.44 27.767 -1.44 c
+27.472 -1.44 27.256 -1.539 27.12 -1.734 c
+26.98 -1.922 26.907 -2.23 26.899 -2.66 c
+26.899 -3.175 l
+26.899 -3.645 26.965 -3.979 27.105 -4.174 c
+27.252 -4.361 27.472 -4.453 27.767 -4.453 c
+30.574 -4.939 -0.646 3.984 re
+30.618 0.088 m
+30.618 -0.022 30.589 -0.114 30.53 -0.191 c
+30.471 -0.261 30.376 -0.293 30.251 -0.293 c
+30.134 -0.293 30.038 -0.261 29.972 -0.191 c
+29.913 -0.114 29.884 -0.022 29.884 0.088 c
+29.884 0.206 29.913 0.298 29.972 0.368 c
+30.038 0.445 30.134 0.485 30.251 0.485 c
+30.376 0.485 30.471 0.445 30.53 0.368 c
+30.589 0.287 30.618 0.195 30.618 0.088 c
+31.765 -4.939 m
+31.765 -1.484 l
+31.25 -1.484 l
+31.25 -0.955 l
+31.765 -0.955 l
+31.765 -0.588 l
+31.773 -0.158 31.886 0.177 32.103 0.412 c
+32.328 0.655 32.64 0.779 33.043 0.779 c
+33.191 0.779 33.33 0.757 33.47 0.721 c
+33.617 0.68 33.768 0.625 33.926 0.559 c
+33.808 -0.014 l
+33.573 0.11 33.33 0.177 33.088 0.177 c
+32.841 0.177 32.669 0.107 32.573 -0.029 c
+32.474 -0.158 32.426 -0.353 32.426 -0.617 c
+32.426 -0.955 l
+33.074 -0.955 l
+33.074 -1.484 l
+32.426 -1.484 l
+32.426 -4.939 l
+h
+34.234 -4.939 -0.647 3.984 re
+36.66 -4.453 m
+36.873 -4.453 37.045 -4.391 37.174 -4.262 c
+37.31 -4.126 37.384 -3.935 37.394 -3.69 c
+38.012 -3.69 l
+37.99 -4.072 37.854 -4.391 37.6 -4.644 c
+37.344 -4.891 37.031 -5.012 36.66 -5.012 c
+36.168 -5.012 35.792 -4.862 35.528 -4.556 c
+35.271 -4.244 35.146 -3.777 35.146 -3.16 c
+35.146 -2.719 l
+35.146 -2.124 35.271 -1.668 35.528 -1.352 c
+35.792 -1.04 36.168 -0.881 36.66 -0.881 c
+37.06 -0.881 37.38 -1.014 37.615 -1.278 c
+37.858 -1.536 37.99 -1.882 38.012 -2.322 c
+37.394 -2.322 l
+37.373 -2.028 37.299 -1.808 37.174 -1.66 c
+37.057 -1.514 36.884 -1.44 36.66 -1.44 c
+36.366 -1.44 36.149 -1.539 36.013 -1.734 c
+35.873 -1.922 35.8 -2.23 35.792 -2.66 c
+35.792 -3.175 l
+35.792 -3.645 35.859 -3.979 35.998 -4.174 c
+36.145 -4.361 36.366 -4.453 36.66 -4.453 c
+41.863 -4.453 m
+42.076 -4.453 42.249 -4.391 42.377 -4.262 c
+42.514 -4.126 42.587 -3.935 42.599 -3.69 c
+43.216 -3.69 l
+43.193 -4.072 43.058 -4.391 42.804 -4.644 c
+42.547 -4.891 42.234 -5.012 41.863 -5.012 c
+41.371 -5.012 40.996 -4.862 40.731 -4.556 c
+40.474 -4.244 40.349 -3.777 40.349 -3.16 c
+40.349 -2.719 l
+40.349 -2.124 40.474 -1.668 40.731 -1.352 c
+40.996 -1.04 41.371 -0.881 41.863 -0.881 c
+42.264 -0.881 42.583 -1.014 42.819 -1.278 c
+43.061 -1.536 43.193 -1.882 43.216 -2.322 c
+42.599 -2.322 l
+42.576 -2.028 42.503 -1.808 42.377 -1.66 c
+42.26 -1.514 42.088 -1.44 41.863 -1.44 c
+41.569 -1.44 41.352 -1.539 41.217 -1.734 c
+41.076 -1.922 41.003 -2.23 40.996 -2.66 c
+40.996 -3.175 l
+40.996 -3.645 41.062 -3.979 41.202 -4.174 c
+41.348 -4.361 41.569 -4.453 41.863 -4.453 c
+43.833 -2.763 m
+43.833 -2.186 43.969 -1.731 44.245 -1.396 c
+44.527 -1.055 44.899 -0.881 45.362 -0.881 c
+45.821 -0.881 46.188 -1.051 46.464 -1.381 c
+46.747 -1.705 46.894 -2.153 46.905 -2.719 c
+46.905 -3.145 l
+46.905 -3.715 46.761 -4.17 46.479 -4.513 c
+46.203 -4.847 45.836 -5.012 45.377 -5.012 c
+44.914 -5.012 44.542 -4.85 44.259 -4.527 c
+43.983 -4.197 43.84 -3.755 43.833 -3.204 c
+h
+44.48 -3.145 m
+44.48 -3.549 44.557 -3.865 44.715 -4.101 c
+44.88 -4.336 45.101 -4.453 45.377 -4.453 c
+45.942 -4.453 46.236 -4.042 46.258 -3.219 c
+46.258 -2.763 l
+46.258 -2.362 46.173 -2.043 46.009 -1.808 c
+45.851 -1.565 45.633 -1.44 45.362 -1.44 c
+45.097 -1.44 44.88 -1.565 44.715 -1.808 c
+44.557 -2.043 44.48 -2.362 44.48 -2.763 c
+h
+48.361 -0.955 m
+48.375 -1.323 l
+48.617 -1.028 48.937 -0.881 49.33 -0.881 c
+49.772 -0.881 50.08 -1.08 50.256 -1.469 c
+50.51 -1.08 50.859 -0.881 51.3 -0.881 c
+52.035 -0.881 52.41 -1.344 52.432 -2.263 c
+52.432 -4.939 l
+51.785 -4.939 l
+51.785 -2.322 l
+51.785 -2.028 51.73 -1.815 51.623 -1.675 c
+51.524 -1.539 51.351 -1.469 51.108 -1.469 c
+50.911 -1.469 50.749 -1.55 50.624 -1.705 c
+50.506 -1.851 50.437 -2.043 50.418 -2.278 c
+50.418 -4.939 l
+49.757 -4.939 l
+49.757 -2.293 l
+49.757 -1.745 49.536 -1.469 49.095 -1.469 c
+48.76 -1.469 48.525 -1.631 48.39 -1.955 c
+48.39 -4.939 l
+47.743 -4.939 l
+47.743 -0.955 l
+h
+54.019 -0.955 m
+54.034 -1.323 l
+54.277 -1.028 54.597 -0.881 54.989 -0.881 c
+55.43 -0.881 55.739 -1.08 55.916 -1.469 c
+56.169 -1.08 56.518 -0.881 56.959 -0.881 c
+57.694 -0.881 58.069 -1.344 58.091 -2.263 c
+58.091 -4.939 l
+57.444 -4.939 l
+57.444 -2.322 l
+57.444 -2.028 57.389 -1.815 57.283 -1.675 c
+57.184 -1.539 57.011 -1.469 56.768 -1.469 c
+56.569 -1.469 56.408 -1.55 56.283 -1.705 c
+56.165 -1.851 56.095 -2.043 56.077 -2.278 c
+56.077 -4.939 l
+55.416 -4.939 l
+55.416 -2.293 l
+55.416 -1.745 55.195 -1.469 54.754 -1.469 c
+54.42 -1.469 54.185 -1.631 54.048 -1.955 c
+54.048 -4.939 l
+53.402 -4.939 l
+53.402 -0.955 l
+h
+59.767 -4.939 -0.647 3.984 re
+59.81 0.088 m
+59.81 -0.022 59.781 -0.114 59.723 -0.191 c
+59.664 -0.261 59.569 -0.293 59.443 -0.293 c
+59.326 -0.293 59.23 -0.261 59.164 -0.191 c
+59.105 -0.114 59.076 -0.022 59.076 0.088 c
+59.076 0.206 59.105 0.298 59.164 0.368 c
+59.23 0.445 59.326 0.485 59.443 0.485 c
+59.569 0.485 59.664 0.445 59.723 0.368 c
+59.781 0.287 59.81 0.195 59.81 0.088 c
+61.633 0 m
+61.633 -0.955 l
+62.236 -0.955 l
+62.236 -1.484 l
+61.633 -1.484 l
+61.633 -3.954 l
+61.633 -4.112 61.655 -4.23 61.707 -4.307 c
+61.766 -4.388 61.854 -4.424 61.971 -4.424 c
+62.06 -4.424 62.148 -4.409 62.236 -4.38 c
+62.236 -4.939 l
+62.089 -4.986 61.935 -5.012 61.78 -5.012 c
+61.523 -5.012 61.329 -4.92 61.192 -4.733 c
+61.053 -4.549 60.986 -4.288 60.986 -3.954 c
+60.986 -1.484 l
+60.384 -1.484 l
+60.384 -0.955 l
+60.986 -0.955 l
+60.986 0 l
+h
+65.426 -4.939 -0.646 3.984 re
+65.47 0.088 m
+65.47 -0.022 65.441 -0.114 65.381 -0.191 c
+65.323 -0.261 65.227 -0.293 65.102 -0.293 c
+64.984 -0.293 64.889 -0.261 64.824 -0.191 c
+64.764 -0.114 64.735 -0.022 64.735 0.088 c
+64.735 0.206 64.764 0.298 64.824 0.368 c
+64.889 0.445 64.984 0.485 65.102 0.485 c
+65.227 0.485 65.323 0.445 65.381 0.368 c
+65.441 0.287 65.47 0.195 65.47 0.088 c
+67.058 -0.955 m
+67.072 -1.396 l
+67.326 -1.055 67.649 -0.881 68.042 -0.881 c
+68.747 -0.881 69.104 -1.352 69.115 -2.293 c
+69.115 -4.939 l
+68.469 -4.939 l
+68.469 -2.322 l
+68.469 -2.009 68.413 -1.789 68.307 -1.66 c
+68.197 -1.536 68.042 -1.469 67.837 -1.469 c
+67.679 -1.469 67.532 -1.525 67.396 -1.631 c
+67.267 -1.741 67.164 -1.878 67.087 -2.043 c
+67.087 -4.939 l
+66.44 -4.939 l
+66.44 -0.955 l
+h
+72.026 -3.925 m
+72.026 -3.777 71.97 -3.656 71.864 -3.557 c
+71.754 -3.461 71.548 -3.343 71.247 -3.204 c
+70.902 -3.057 70.659 -2.936 70.511 -2.836 c
+70.364 -2.73 70.254 -2.612 70.189 -2.484 c
+70.119 -2.359 70.085 -2.201 70.085 -2.013 c
+70.085 -1.691 70.203 -1.421 70.438 -1.205 c
+70.673 -0.992 70.975 -0.881 71.349 -0.881 c
+71.731 -0.881 72.04 -0.995 72.275 -1.22 c
+72.51 -1.448 72.628 -1.734 72.628 -2.087 c
+71.982 -2.087 l
+71.982 -1.911 71.922 -1.76 71.805 -1.631 c
+71.687 -1.506 71.533 -1.44 71.349 -1.44 c
+71.151 -1.44 71 -1.496 70.894 -1.602 c
+70.784 -1.701 70.732 -1.833 70.732 -1.999 c
+70.732 -2.127 70.769 -2.234 70.85 -2.322 c
+70.927 -2.403 71.118 -2.506 71.423 -2.631 c
+71.901 -2.818 72.232 -3.006 72.408 -3.189 c
+72.585 -3.366 72.672 -3.594 72.672 -3.865 c
+72.672 -4.218 72.548 -4.498 72.305 -4.704 c
+72.07 -4.909 71.754 -5.012 71.364 -5.012 c
+70.941 -5.012 70.603 -4.895 70.35 -4.659 c
+70.093 -4.417 69.967 -4.112 69.967 -3.748 c
+70.615 -3.748 l
+70.622 -3.976 70.692 -4.152 70.821 -4.278 c
+70.945 -4.395 71.129 -4.453 71.364 -4.453 c
+71.577 -4.453 71.739 -4.405 71.849 -4.307 c
+71.967 -4.211 72.026 -4.083 72.026 -3.925 c
+74.363 0 m
+74.363 -0.955 l
+74.966 -0.955 l
+74.966 -1.484 l
+74.363 -1.484 l
+74.363 -3.954 l
+74.363 -4.112 74.385 -4.23 74.436 -4.307 c
+74.495 -4.388 74.584 -4.424 74.701 -4.424 c
+74.789 -4.424 74.877 -4.409 74.966 -4.38 c
+74.966 -4.939 l
+74.819 -4.986 74.665 -5.012 74.509 -5.012 c
+74.253 -5.012 74.058 -4.92 73.921 -4.733 c
+73.782 -4.549 73.716 -4.288 73.716 -3.954 c
+73.716 -1.484 l
+73.113 -1.484 l
+73.113 -0.955 l
+73.716 -0.955 l
+73.716 0 l
+h
+77.156 -5.012 m
+76.656 -5.012 76.273 -4.866 76.009 -4.571 c
+75.745 -4.278 75.612 -3.844 75.612 -3.262 c
+75.612 -2.792 l
+75.612 -2.198 75.737 -1.731 75.995 -1.396 c
+76.259 -1.055 76.619 -0.881 77.082 -0.881 c
+77.541 -0.881 77.883 -1.036 78.111 -1.338 c
+78.347 -1.631 78.467 -2.094 78.478 -2.719 c
+78.478 -3.145 l
+76.259 -3.145 l
+76.259 -3.233 l
+76.259 -3.667 76.336 -3.979 76.494 -4.174 c
+76.66 -4.361 76.891 -4.453 77.185 -4.453 c
+77.38 -4.453 77.553 -4.421 77.699 -4.351 c
+77.846 -4.274 77.983 -4.156 78.111 -3.998 c
+78.449 -4.409 l
+78.162 -4.814 77.732 -5.012 77.156 -5.012 c
+77.082 -1.44 m
+76.807 -1.44 76.604 -1.536 76.479 -1.72 c
+76.35 -1.907 76.277 -2.198 76.259 -2.587 c
+77.832 -2.587 l
+77.832 -2.499 l
+77.809 -2.117 77.744 -1.849 77.626 -1.691 c
+77.508 -1.525 77.325 -1.44 77.082 -1.44 c
+81.301 -4.939 m
+81.26 -4.85 81.235 -4.704 81.227 -4.498 c
+80.992 -4.843 80.698 -5.012 80.346 -5.012 c
+79.982 -5.012 79.698 -4.916 79.492 -4.718 c
+79.294 -4.513 79.199 -4.226 79.199 -3.85 c
+79.199 -3.451 79.334 -3.131 79.61 -2.896 c
+79.882 -2.653 80.257 -2.528 80.728 -2.528 c
+81.212 -2.528 l
+81.212 -2.102 l
+81.212 -1.866 81.158 -1.701 81.051 -1.602 c
+80.94 -1.496 80.779 -1.44 80.566 -1.44 c
+80.367 -1.44 80.206 -1.499 80.08 -1.616 c
+79.963 -1.734 79.905 -1.882 79.905 -2.057 c
+79.257 -2.057 l
+79.257 -1.863 79.317 -1.672 79.434 -1.484 c
+79.559 -1.301 79.721 -1.153 79.919 -1.043 c
+80.125 -0.937 80.352 -0.881 80.61 -0.881 c
+81.011 -0.881 81.316 -0.985 81.522 -1.19 c
+81.734 -1.396 81.848 -1.691 81.859 -2.072 c
+81.859 -4.086 l
+81.859 -4.391 81.896 -4.656 81.977 -4.88 c
+81.977 -4.939 l
+h
+80.433 -4.424 m
+80.599 -4.424 80.749 -4.38 80.889 -4.292 c
+81.036 -4.203 81.143 -4.093 81.212 -3.954 c
+81.212 -3.013 l
+80.845 -3.013 l
+80.529 -3.013 80.286 -3.083 80.111 -3.219 c
+79.934 -3.347 79.845 -3.535 79.845 -3.777 c
+79.845 -4.006 79.889 -4.17 79.978 -4.278 c
+80.066 -4.376 80.217 -4.424 80.433 -4.424 c
+82.727 -2.763 m
+82.727 -2.157 82.837 -1.691 83.064 -1.367 c
+83.3 -1.043 83.627 -0.881 84.049 -0.881 c
+84.431 -0.881 84.73 -1.04 84.946 -1.352 c
+84.946 0.706 l
+85.593 0.706 l
+85.593 -4.939 l
+85.005 -4.939 l
+84.961 -4.513 l
+84.755 -4.847 84.45 -5.012 84.049 -5.012 c
+83.638 -5.012 83.315 -4.858 83.079 -4.542 c
+82.844 -4.218 82.727 -3.763 82.727 -3.175 c
+h
+83.373 -3.145 m
+83.373 -3.586 83.436 -3.917 83.564 -4.13 c
+83.7 -4.336 83.921 -4.438 84.226 -4.438 c
+84.549 -4.438 84.788 -4.278 84.946 -3.954 c
+84.946 -1.94 l
+84.778 -1.627 84.539 -1.469 84.226 -1.469 c
+83.921 -1.469 83.7 -1.573 83.564 -1.778 c
+83.436 -1.984 83.373 -2.308 83.373 -2.749 c
+h
+88.18 -2.763 m
+88.18 -2.186 88.316 -1.731 88.591 -1.396 c
+88.874 -1.055 89.246 -0.881 89.709 -0.881 c
+90.168 -0.881 90.536 -1.051 90.811 -1.381 c
+91.094 -1.705 91.241 -2.153 91.252 -2.719 c
+91.252 -3.145 l
+91.252 -3.715 91.109 -4.17 90.826 -4.513 c
+90.55 -4.847 90.183 -5.012 89.723 -5.012 c
+89.26 -5.012 88.889 -4.85 88.606 -4.527 c
+88.331 -4.197 88.188 -3.755 88.18 -3.204 c
+h
+88.826 -3.145 m
+88.826 -3.549 88.904 -3.865 89.062 -4.101 c
+89.227 -4.336 89.447 -4.453 89.723 -4.453 c
+90.289 -4.453 90.583 -4.042 90.605 -3.219 c
+90.605 -2.763 l
+90.605 -2.362 90.521 -2.043 90.355 -1.808 c
+90.197 -1.565 89.981 -1.44 89.709 -1.44 c
+89.444 -1.44 89.227 -1.565 89.062 -1.808 c
+88.904 -2.043 88.826 -2.362 88.826 -2.763 c
+h
+92.383 -4.939 m
+92.383 -1.484 l
+91.855 -1.484 l
+91.855 -0.955 l
+92.383 -0.955 l
+92.383 -0.5 l
+92.383 -0.099 92.479 0.214 92.678 0.441 c
+92.884 0.665 93.163 0.779 93.516 0.779 c
+93.652 0.779 93.784 0.757 93.912 0.721 c
+93.883 0.177 l
+93.784 0.195 93.684 0.206 93.589 0.206 c
+93.214 0.206 93.031 -0.058 93.031 -0.588 c
+93.031 -0.955 l
+93.707 -0.955 l
+93.707 -1.484 l
+93.031 -1.484 l
+93.031 -4.939 l
+h
+97.573 -4.453 m
+97.786 -4.453 97.958 -4.391 98.087 -4.262 c
+98.223 -4.126 98.297 -3.935 98.307 -3.69 c
+98.925 -3.69 l
+98.903 -4.072 98.767 -4.391 98.513 -4.644 c
+98.256 -4.891 97.944 -5.012 97.573 -5.012 c
+97.081 -5.012 96.705 -4.862 96.441 -4.556 c
+96.184 -4.244 96.059 -3.777 96.059 -3.16 c
+96.059 -2.719 l
+96.059 -2.124 96.184 -1.668 96.441 -1.352 c
+96.705 -1.04 97.081 -0.881 97.573 -0.881 c
+97.973 -0.881 98.293 -1.014 98.528 -1.278 c
+98.771 -1.536 98.903 -1.882 98.925 -2.322 c
+98.307 -2.322 l
+98.286 -2.028 98.212 -1.808 98.087 -1.66 c
+97.97 -1.514 97.796 -1.44 97.573 -1.44 c
+97.279 -1.44 97.062 -1.539 96.926 -1.734 c
+96.786 -1.922 96.713 -2.23 96.705 -2.66 c
+96.705 -3.175 l
+96.705 -3.645 96.772 -3.979 96.911 -4.174 c
+97.058 -4.361 97.279 -4.453 97.573 -4.453 c
+101.703 -4.586 m
+101.486 -4.872 101.174 -5.012 100.763 -5.012 c
+100.398 -5.012 100.123 -4.891 99.939 -4.644 c
+99.763 -4.391 99.668 -4.027 99.66 -3.557 c
+99.66 -0.955 l
+100.306 -0.955 l
+100.306 -3.498 l
+100.306 -4.126 100.491 -4.438 100.865 -4.438 c
+101.266 -4.438 101.542 -4.262 101.688 -3.91 c
+101.688 -0.955 l
+102.335 -0.955 l
+102.335 -4.939 l
+101.717 -4.939 l
+h
+104.966 -1.573 m
+104.878 -1.554 104.779 -1.543 104.672 -1.543 c
+104.337 -1.543 104.102 -1.727 103.967 -2.087 c
+103.967 -4.939 l
+103.319 -4.939 l
+103.319 -0.955 l
+103.952 -0.955 l
+103.967 -1.367 l
+104.143 -1.043 104.385 -0.881 104.701 -0.881 c
+104.808 -0.881 104.896 -0.904 104.966 -0.941 c
+h
+107.259 -1.573 m
+107.171 -1.554 107.072 -1.543 106.965 -1.543 c
+106.631 -1.543 106.396 -1.727 106.259 -2.087 c
+106.259 -4.939 l
+105.613 -4.939 l
+105.613 -0.955 l
+106.245 -0.955 l
+106.259 -1.367 l
+106.436 -1.043 106.679 -0.881 106.995 -0.881 c
+107.101 -0.881 107.19 -0.904 107.259 -0.941 c
+h
+109.258 -5.012 m
+108.759 -5.012 108.376 -4.866 108.112 -4.571 c
+107.847 -4.278 107.715 -3.844 107.715 -3.262 c
+107.715 -2.792 l
+107.715 -2.198 107.84 -1.731 108.098 -1.396 c
+108.362 -1.055 108.722 -0.881 109.185 -0.881 c
+109.644 -0.881 109.986 -1.036 110.214 -1.338 c
+110.449 -1.631 110.571 -2.094 110.581 -2.719 c
+110.581 -3.145 l
+108.362 -3.145 l
+108.362 -3.233 l
+108.362 -3.667 108.439 -3.979 108.597 -4.174 c
+108.763 -4.361 108.994 -4.453 109.288 -4.453 c
+109.482 -4.453 109.655 -4.421 109.802 -4.351 c
+109.949 -4.274 110.085 -4.156 110.214 -3.998 c
+110.552 -4.409 l
+110.265 -4.814 109.835 -5.012 109.258 -5.012 c
+109.185 -1.44 m
+108.909 -1.44 108.707 -1.536 108.582 -1.72 c
+108.454 -1.907 108.38 -2.198 108.362 -2.587 c
+109.935 -2.587 l
+109.935 -2.499 l
+109.912 -2.117 109.846 -1.849 109.729 -1.691 c
+109.611 -1.525 109.428 -1.44 109.185 -1.44 c
+111.978 -0.955 m
+111.992 -1.396 l
+112.246 -1.055 112.569 -0.881 112.962 -0.881 c
+113.668 -0.881 114.024 -1.352 114.035 -2.293 c
+114.035 -4.939 l
+113.389 -4.939 l
+113.389 -2.322 l
+113.389 -2.009 113.334 -1.789 113.227 -1.66 c
+113.117 -1.536 112.962 -1.469 112.757 -1.469 c
+112.599 -1.469 112.452 -1.525 112.316 -1.631 c
+112.187 -1.741 112.084 -1.878 112.007 -2.043 c
+112.007 -4.939 l
+111.36 -4.939 l
+111.36 -0.955 l
+h
+115.859 0 m
+115.859 -0.955 l
+116.461 -0.955 l
+116.461 -1.484 l
+115.859 -1.484 l
+115.859 -3.954 l
+115.859 -4.112 115.88 -4.23 115.932 -4.307 c
+115.99 -4.388 116.079 -4.424 116.196 -4.424 c
+116.285 -4.424 116.372 -4.409 116.461 -4.38 c
+116.461 -4.939 l
+116.314 -4.986 116.16 -5.012 116.005 -5.012 c
+115.748 -5.012 115.553 -4.92 115.417 -4.733 c
+115.277 -4.549 115.211 -4.288 115.211 -3.954 c
+115.211 -1.484 l
+114.608 -1.484 l
+114.608 -0.955 l
+115.211 -0.955 l
+115.211 0 l
+h
+118.813 -2.763 m
+118.813 -2.186 118.949 -1.731 119.225 -1.396 c
+119.508 -1.055 119.878 -0.881 120.341 -0.881 c
+120.8 -0.881 121.168 -1.051 121.444 -1.381 c
+121.727 -1.705 121.874 -2.153 121.885 -2.719 c
+121.885 -3.145 l
+121.885 -3.715 121.741 -4.17 121.459 -4.513 c
+121.183 -4.847 120.815 -5.012 120.356 -5.012 c
+119.893 -5.012 119.522 -4.85 119.239 -4.527 c
+118.963 -4.197 118.82 -3.755 118.813 -3.204 c
+h
+119.46 -3.145 m
+119.46 -3.549 119.537 -3.865 119.695 -4.101 c
+119.86 -4.336 120.081 -4.453 120.356 -4.453 c
+120.922 -4.453 121.216 -4.042 121.238 -3.219 c
+121.238 -2.763 l
+121.238 -2.362 121.154 -2.043 120.988 -1.808 c
+120.83 -1.565 120.613 -1.44 120.341 -1.44 c
+120.077 -1.44 119.86 -1.565 119.695 -1.808 c
+119.537 -2.043 119.46 -2.362 119.46 -2.763 c
+h
+123.34 -0.955 m
+123.354 -1.396 l
+123.608 -1.055 123.932 -0.881 124.325 -0.881 c
+125.031 -0.881 125.387 -1.352 125.398 -2.293 c
+125.398 -4.939 l
+124.751 -4.939 l
+124.751 -2.322 l
+124.751 -2.009 124.696 -1.789 124.59 -1.66 c
+124.48 -1.536 124.325 -1.469 124.119 -1.469 c
+123.961 -1.469 123.814 -1.525 123.678 -1.631 c
+123.549 -1.741 123.447 -1.878 123.37 -2.043 c
+123.37 -4.939 l
+122.722 -4.939 l
+122.722 -0.955 l
+h
+127.794 -5.012 m
+127.294 -5.012 126.912 -4.866 126.647 -4.571 c
+126.383 -4.278 126.25 -3.844 126.25 -3.262 c
+126.25 -2.792 l
+126.25 -2.198 126.375 -1.731 126.633 -1.396 c
+126.897 -1.055 127.258 -0.881 127.72 -0.881 c
+128.179 -0.881 128.522 -1.036 128.75 -1.338 c
+128.985 -1.631 129.106 -2.094 129.116 -2.719 c
+129.116 -3.145 l
+126.897 -3.145 l
+126.897 -3.233 l
+126.897 -3.667 126.974 -3.979 127.132 -4.174 c
+127.298 -4.361 127.529 -4.453 127.823 -4.453 c
+128.018 -4.453 128.191 -4.421 128.337 -4.351 c
+128.484 -4.274 128.621 -4.156 128.75 -3.998 c
+129.087 -4.409 l
+128.8 -4.814 128.37 -5.012 127.794 -5.012 c
+127.72 -1.44 m
+127.445 -1.44 127.242 -1.536 127.117 -1.72 c
+126.989 -1.907 126.915 -2.198 126.897 -2.587 c
+128.47 -2.587 l
+128.47 -2.499 l
+128.448 -2.117 128.382 -1.849 128.264 -1.691 c
+128.147 -1.525 127.963 -1.44 127.72 -1.44 c
+129.94 -4.586 m
+129.94 -4.469 129.973 -4.373 130.043 -4.292 c
+130.109 -4.215 130.211 -4.174 130.352 -4.174 c
+130.498 -4.174 130.605 -4.215 130.675 -4.292 c
+130.752 -4.373 130.793 -4.469 130.793 -4.586 c
+130.793 -4.696 130.752 -4.788 130.675 -4.866 c
+130.605 -4.943 130.498 -4.983 130.352 -4.983 c
+130.211 -4.983 130.109 -4.943 130.043 -4.866 c
+129.973 -4.788 129.94 -4.696 129.94 -4.586 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 402.127 234.667 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 395.2921 cm
+0 0 m
+0 -0.188 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.776 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.206 -1.278 -1.243 -1.543 -1.25 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.82 -3.23 -0.691 -3.307 -0.545 c
+-3.388 -0.39 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.148 c
+-2.55 -0.229 -2.506 -0.302 -2.454 -0.368 c
+-2.396 -0.427 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.581 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.675 c
+-1.977 0.683 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.572 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.63 -3.054 2.748 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.991 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.263 -0.941 1.205 c
+-0.756 1.146 -0.595 1.065 -0.455 0.97 c
+-0.32 0.87 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.513 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.499 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.356 -1.043 0.396 c
+-1.103 0.433 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.558 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.581 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.346 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.661 m
+6.184 -2.661 5.993 -2.635 5.829 -2.587 c
+5.659 -2.548 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.721 c
+5.858 -1.617 l
+5.895 -1.754 5.964 -1.86 6.064 -1.941 c
+6.159 -2.029 6.284 -2.073 6.431 -2.073 c
+6.519 -2.073 6.6 -2.058 6.681 -2.029 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.798 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.324 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.89 7.082 -0.831 7.092 -0.78 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.817 6.835 -0.978 6.652 -1.073 c
+6.464 -1.173 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.015 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.007 4.814 0.242 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.244 1.932 5.376 2.05 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.079 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.767 7.078 1.66 c
+7.092 1.66 l
+7.092 1.807 l
+7.1 1.866 7.107 1.918 7.107 1.969 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.164 7.14 2.197 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.636 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.5 7.063 -2.558 c
+6.865 -2.625 6.644 -2.661 6.402 -2.661 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.499 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.572 c
+6.056 1.531 5.99 1.462 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.874 5.755 0.706 5.755 0.5 c
+5.755 0.124 5.814 -0.155 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.464 6.857 -0.383 c
+6.924 -0.294 6.978 -0.177 7.019 -0.03 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.116 l
+14.494 3.116 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.456 14.552 -0.515 c
+14.589 -0.566 14.645 -0.611 14.714 -0.647 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.688 15.464 -0.676 c
+15.599 -0.659 15.732 -0.632 15.861 -0.603 c
+15.861 -1.206 l
+15.78 -1.216 15.703 -1.231 15.626 -1.25 c
+15.545 -1.261 15.468 -1.268 15.391 -1.279 c
+15.31 -1.287 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.133 c
+13.92 -1.085 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.545 13.597 -0.456 c
+13.586 -0.36 13.582 -0.265 13.582 -0.177 c
+h
+21.722 1.602 m
+21.178 1.602 l
+21.178 2.219 l
+21.766 2.219 l
+22.045 3.116 l
+22.618 3.116 l
+22.618 2.219 l
+23.853 2.219 l
+23.853 1.602 l
+22.618 1.602 l
+22.618 -0.103 l
+22.618 -0.324 l
+22.626 -0.393 22.647 -0.456 22.677 -0.515 c
+22.714 -0.566 22.769 -0.611 22.839 -0.647 c
+22.916 -0.676 23.03 -0.691 23.177 -0.691 c
+23.313 -0.691 23.449 -0.688 23.588 -0.676 c
+23.725 -0.659 23.857 -0.632 23.985 -0.603 c
+23.985 -1.206 l
+23.904 -1.216 23.827 -1.231 23.75 -1.25 c
+23.669 -1.261 23.592 -1.268 23.515 -1.279 c
+23.434 -1.287 23.346 -1.294 23.25 -1.294 c
+23.162 -1.301 23.063 -1.309 22.957 -1.309 c
+22.769 -1.309 22.608 -1.294 22.471 -1.264 c
+22.343 -1.228 22.229 -1.183 22.134 -1.133 c
+22.045 -1.085 21.972 -1.025 21.913 -0.956 c
+21.854 -0.879 21.81 -0.802 21.781 -0.721 c
+21.751 -0.632 21.729 -0.545 21.722 -0.456 c
+21.71 -0.36 21.707 -0.265 21.707 -0.177 c
+h
+26.136 -1.324 m
+25.966 -1.324 25.816 -1.301 25.679 -1.264 c
+25.551 -1.216 25.437 -1.147 25.342 -1.058 c
+25.253 -0.971 25.184 -0.864 25.136 -0.736 c
+25.084 -0.599 25.062 -0.449 25.062 -0.279 c
+25.062 -0.074 25.095 0.095 25.165 0.235 c
+25.232 0.382 25.327 0.492 25.444 0.573 c
+25.569 0.661 25.712 0.723 25.87 0.764 c
+26.036 0.801 26.213 0.826 26.4 0.837 c
+27.12 0.852 l
+27.12 1.028 l
+27.12 1.146 27.109 1.249 27.09 1.337 c
+27.069 1.425 27.036 1.491 26.988 1.543 c
+26.947 1.602 26.9 1.639 26.841 1.66 c
+26.782 1.679 26.716 1.69 26.649 1.69 c
+26.58 1.69 26.518 1.679 26.458 1.66 c
+26.408 1.649 26.36 1.624 26.312 1.587 c
+26.271 1.558 26.238 1.506 26.209 1.44 c
+26.186 1.381 26.172 1.3 26.165 1.205 c
+25.224 1.249 l
+25.253 1.396 25.297 1.531 25.356 1.66 c
+25.423 1.786 25.518 1.896 25.635 1.984 c
+25.753 2.079 25.893 2.153 26.061 2.204 c
+26.238 2.252 26.444 2.277 26.679 2.277 c
+27.12 2.277 27.451 2.167 27.678 1.955 c
+27.914 1.749 28.031 1.44 28.031 1.028 c
+28.031 -0.235 l
+28.031 -0.456 l
+28.039 -0.515 28.054 -0.57 28.075 -0.618 c
+28.094 -0.659 28.123 -0.691 28.164 -0.721 c
+28.201 -0.743 28.252 -0.75 28.311 -0.75 c
+28.376 -0.75 28.447 -0.746 28.517 -0.736 c
+28.517 -1.22 l
+28.457 -1.231 28.403 -1.243 28.355 -1.25 c
+28.314 -1.261 28.274 -1.268 28.237 -1.279 c
+28.197 -1.287 28.153 -1.294 28.105 -1.294 c
+28.054 -1.301 27.995 -1.309 27.929 -1.309 c
+27.701 -1.309 27.535 -1.257 27.429 -1.147 c
+27.318 -1.029 27.256 -0.864 27.237 -0.647 c
+27.223 -0.647 l
+27.154 -0.757 27.083 -0.853 27.017 -0.941 c
+26.947 -1.022 26.87 -1.088 26.782 -1.147 c
+26.693 -1.206 26.595 -1.25 26.488 -1.279 c
+26.389 -1.309 26.271 -1.324 26.136 -1.324 c
+27.12 0.353 m
+26.693 0.338 l
+26.595 0.338 26.503 0.33 26.414 0.323 c
+26.333 0.312 26.267 0.286 26.209 0.249 c
+26.15 0.209 26.098 0.151 26.061 0.073 c
+26.021 0.003 26.003 -0.088 26.003 -0.206 c
+26.003 -0.375 26.036 -0.497 26.106 -0.574 c
+26.172 -0.655 26.271 -0.691 26.4 -0.691 c
+26.506 -0.691 26.606 -0.669 26.693 -0.618 c
+26.789 -0.57 26.87 -0.508 26.929 -0.427 c
+26.995 -0.35 27.046 -0.262 27.076 -0.162 c
+27.106 -0.056 27.12 0.058 27.12 0.176 c
+h
+30.769 -2.661 m
+30.552 -2.661 30.361 -2.635 30.196 -2.587 c
+30.027 -2.548 29.887 -2.484 29.77 -2.396 c
+29.652 -2.315 29.552 -2.22 29.475 -2.103 c
+29.406 -1.985 29.358 -1.856 29.328 -1.721 c
+30.225 -1.617 l
+30.262 -1.754 30.331 -1.86 30.431 -1.941 c
+30.527 -2.029 30.651 -2.073 30.799 -2.073 c
+30.886 -2.073 30.967 -2.058 31.048 -2.029 c
+31.125 -1.999 31.195 -1.945 31.254 -1.867 c
+31.313 -1.798 31.357 -1.706 31.387 -1.588 c
+31.424 -1.47 31.445 -1.324 31.445 -1.147 c
+31.445 -0.956 l
+31.445 -0.89 31.449 -0.831 31.46 -0.78 c
+31.46 -0.588 l
+31.445 -0.588 l
+31.346 -0.817 31.202 -0.978 31.019 -1.073 c
+30.832 -1.173 30.626 -1.22 30.402 -1.22 c
+30.196 -1.22 30.012 -1.183 29.858 -1.103 c
+29.71 -1.015 29.582 -0.897 29.475 -0.75 c
+29.376 -0.595 29.303 -0.412 29.255 -0.206 c
+29.203 0.007 29.182 0.242 29.182 0.5 c
+29.182 0.771 29.203 1.018 29.255 1.234 c
+29.313 1.448 29.394 1.631 29.505 1.778 c
+29.612 1.932 29.743 2.05 29.901 2.131 c
+30.057 2.219 30.244 2.263 30.46 2.263 c
+30.556 2.263 30.655 2.252 30.755 2.234 c
+30.85 2.212 30.938 2.179 31.019 2.131 c
+31.107 2.079 31.185 2.017 31.254 1.94 c
+31.331 1.859 31.393 1.767 31.445 1.66 c
+31.46 1.66 l
+31.46 1.807 l
+31.468 1.866 31.474 1.918 31.474 1.969 c
+31.482 2.028 31.489 2.076 31.489 2.117 c
+31.497 2.164 31.507 2.197 31.519 2.219 c
+32.371 2.219 l
+32.361 2.138 32.349 2.028 32.342 1.881 c
+32.342 1.411 l
+32.342 -1.162 l
+32.342 -1.415 32.305 -1.636 32.239 -1.823 c
+32.17 -2.007 32.066 -2.161 31.931 -2.278 c
+31.79 -2.404 31.625 -2.5 31.43 -2.558 c
+31.233 -2.625 31.011 -2.661 30.769 -2.661 c
+31.46 0.529 m
+31.46 0.742 31.434 0.918 31.387 1.058 c
+31.346 1.205 31.291 1.323 31.225 1.411 c
+31.166 1.499 31.096 1.558 31.019 1.587 c
+30.938 1.624 30.861 1.646 30.784 1.646 c
+30.684 1.646 30.593 1.62 30.504 1.572 c
+30.424 1.531 30.358 1.462 30.298 1.367 c
+30.248 1.278 30.203 1.161 30.167 1.014 c
+30.137 0.874 30.122 0.706 30.122 0.5 c
+30.122 0.124 30.181 -0.155 30.298 -0.339 c
+30.416 -0.515 30.578 -0.603 30.784 -0.603 c
+30.85 -0.603 30.923 -0.588 31.004 -0.559 c
+31.092 -0.522 31.166 -0.464 31.225 -0.383 c
+31.291 -0.294 31.346 -0.177 31.387 -0.03 c
+31.434 0.118 31.46 0.301 31.46 0.529 c
+37.953 0.837 1.866 -0.794 re
+37.953 0.043 m
+42.381 -1.324 m
+42.212 -1.324 42.061 -1.301 41.926 -1.264 c
+41.797 -1.216 41.683 -1.147 41.587 -1.058 c
+41.5 -0.971 41.429 -0.864 41.382 -0.736 c
+41.33 -0.599 41.308 -0.449 41.308 -0.279 c
+41.308 -0.074 41.342 0.095 41.411 0.235 c
+41.477 0.382 41.573 0.492 41.691 0.573 c
+41.816 0.661 41.959 0.723 42.117 0.764 c
+42.282 0.801 42.458 0.826 42.646 0.837 c
+43.366 0.852 l
+43.366 1.028 l
+43.366 1.146 43.355 1.249 43.337 1.337 c
+43.314 1.425 43.282 1.491 43.234 1.543 c
+43.193 1.602 43.146 1.639 43.087 1.66 c
+43.028 1.679 42.962 1.69 42.896 1.69 c
+42.826 1.69 42.763 1.679 42.705 1.66 c
+42.653 1.649 42.605 1.624 42.558 1.587 c
+42.518 1.558 42.484 1.506 42.455 1.44 c
+42.433 1.381 42.418 1.3 42.41 1.205 c
+41.47 1.249 l
+41.5 1.396 41.543 1.531 41.602 1.66 c
+41.668 1.786 41.764 1.896 41.882 1.984 c
+41.999 2.079 42.139 2.153 42.308 2.204 c
+42.484 2.252 42.69 2.277 42.925 2.277 c
+43.366 2.277 43.697 2.167 43.925 1.955 c
+44.16 1.749 44.278 1.44 44.278 1.028 c
+44.278 -0.235 l
+44.278 -0.456 l
+44.284 -0.515 44.299 -0.57 44.322 -0.618 c
+44.34 -0.659 44.369 -0.691 44.409 -0.721 c
+44.446 -0.743 44.498 -0.75 44.557 -0.75 c
+44.623 -0.75 44.693 -0.746 44.762 -0.736 c
+44.762 -1.22 l
+44.704 -1.231 44.648 -1.243 44.6 -1.25 c
+44.56 -1.261 44.52 -1.268 44.483 -1.279 c
+44.442 -1.287 44.399 -1.294 44.351 -1.294 c
+44.299 -1.301 44.241 -1.309 44.174 -1.309 c
+43.947 -1.309 43.781 -1.257 43.675 -1.147 c
+43.565 -1.029 43.502 -0.864 43.484 -0.647 c
+43.469 -0.647 l
+43.399 -0.757 43.33 -0.853 43.264 -0.941 c
+43.193 -1.022 43.116 -1.088 43.028 -1.147 c
+42.94 -1.206 42.84 -1.25 42.734 -1.279 c
+42.635 -1.309 42.518 -1.324 42.381 -1.324 c
+43.366 0.353 m
+42.94 0.338 l
+42.84 0.338 42.749 0.33 42.661 0.323 c
+42.58 0.312 42.514 0.286 42.455 0.249 c
+42.396 0.209 42.345 0.151 42.308 0.073 c
+42.267 0.003 42.249 -0.088 42.249 -0.206 c
+42.249 -0.375 42.282 -0.497 42.352 -0.574 c
+42.418 -0.655 42.518 -0.691 42.646 -0.691 c
+42.753 -0.691 42.852 -0.669 42.94 -0.618 c
+43.035 -0.57 43.116 -0.508 43.175 -0.427 c
+43.241 -0.35 43.293 -0.262 43.322 -0.162 c
+43.351 -0.056 43.366 0.058 43.366 0.176 c
+h
+50.297 -2.631 m
+50.297 3.513 l
+52.222 3.513 l
+52.222 2.896 l
+51.149 2.896 l
+51.149 -2.014 l
+52.222 -2.014 l
+52.222 -2.631 l
+h
+55.798 -1.264 m
+55.798 0.72 l
+55.798 1.021 55.754 1.242 55.665 1.381 c
+55.585 1.529 55.449 1.602 55.254 1.602 c
+55.143 1.602 55.041 1.576 54.946 1.529 c
+54.857 1.477 54.776 1.411 54.711 1.323 c
+54.651 1.234 54.6 1.124 54.563 0.999 c
+54.534 0.881 54.519 0.749 54.519 0.602 c
+54.519 -1.264 l
+53.608 -1.264 l
+53.608 1.44 l
+53.608 1.66 l
+53.608 1.749 53.6 1.826 53.593 1.896 c
+53.593 2.087 l
+53.593 2.219 l
+54.445 2.219 l
+54.453 2.19 54.46 2.146 54.46 2.087 c
+54.46 1.896 l
+54.468 1.826 54.475 1.756 54.475 1.69 c
+54.482 1.62 54.49 1.565 54.49 1.529 c
+54.505 1.529 l
+54.622 1.793 54.773 1.984 54.96 2.102 c
+55.143 2.219 55.364 2.277 55.621 2.277 c
+55.806 2.277 55.966 2.248 56.107 2.19 c
+56.242 2.131 56.357 2.042 56.444 1.925 c
+56.533 1.807 56.595 1.664 56.635 1.499 c
+56.683 1.341 56.71 1.153 56.71 0.941 c
+56.71 -1.264 l
+h
+58.628 -1.324 m
+58.458 -1.324 58.308 -1.301 58.172 -1.264 c
+58.044 -1.216 57.929 -1.147 57.834 -1.058 c
+57.745 -0.971 57.676 -0.864 57.628 -0.736 c
+57.576 -0.599 57.554 -0.449 57.554 -0.279 c
+57.554 -0.074 57.587 0.095 57.657 0.235 c
+57.724 0.382 57.819 0.492 57.936 0.573 c
+58.061 0.661 58.204 0.723 58.363 0.764 c
+58.528 0.801 58.705 0.826 58.892 0.837 c
+59.612 0.852 l
+59.612 1.028 l
+59.612 1.146 59.601 1.249 59.583 1.337 c
+59.561 1.425 59.528 1.491 59.48 1.543 c
+59.44 1.602 59.392 1.639 59.333 1.66 c
+59.274 1.679 59.208 1.69 59.141 1.69 c
+59.072 1.69 59.01 1.679 58.951 1.66 c
+58.9 1.649 58.852 1.624 58.804 1.587 c
+58.763 1.558 58.73 1.506 58.701 1.44 c
+58.679 1.381 58.664 1.3 58.657 1.205 c
+57.716 1.249 l
+57.745 1.396 57.79 1.531 57.848 1.66 c
+57.915 1.786 58.01 1.896 58.127 1.984 c
+58.245 2.079 58.385 2.153 58.554 2.204 c
+58.73 2.252 58.936 2.277 59.172 2.277 c
+59.612 2.277 59.943 2.167 60.171 1.955 c
+60.406 1.749 60.523 1.44 60.523 1.028 c
+60.523 -0.235 l
+60.523 -0.456 l
+60.531 -0.515 60.546 -0.57 60.568 -0.618 c
+60.586 -0.659 60.616 -0.691 60.656 -0.721 c
+60.693 -0.743 60.744 -0.75 60.803 -0.75 c
+60.869 -0.75 60.939 -0.746 61.009 -0.736 c
+61.009 -1.22 l
+60.95 -1.231 60.895 -1.243 60.847 -1.25 c
+60.807 -1.261 60.766 -1.268 60.729 -1.279 c
+60.689 -1.287 60.645 -1.294 60.597 -1.294 c
+60.546 -1.301 60.487 -1.309 60.421 -1.309 c
+60.193 -1.309 60.028 -1.257 59.921 -1.147 c
+59.81 -1.029 59.748 -0.864 59.729 -0.647 c
+59.715 -0.647 l
+59.646 -0.757 59.575 -0.853 59.509 -0.941 c
+59.44 -1.022 59.363 -1.088 59.274 -1.147 c
+59.186 -1.206 59.087 -1.25 58.981 -1.279 c
+58.881 -1.309 58.763 -1.324 58.628 -1.324 c
+59.612 0.353 m
+59.186 0.338 l
+59.087 0.338 58.995 0.33 58.906 0.323 c
+58.826 0.312 58.76 0.286 58.701 0.249 c
+58.642 0.209 58.591 0.151 58.554 0.073 c
+58.514 0.003 58.495 -0.088 58.495 -0.206 c
+58.495 -0.375 58.528 -0.497 58.598 -0.574 c
+58.664 -0.655 58.763 -0.691 58.892 -0.691 c
+58.998 -0.691 59.098 -0.669 59.186 -0.618 c
+59.282 -0.57 59.363 -0.508 59.421 -0.427 c
+59.488 -0.35 59.538 -0.262 59.569 -0.162 c
+59.598 -0.056 59.612 0.058 59.612 0.176 c
+h
+62.894 -1.264 m
+62.894 0.852 l
+62.894 1.018 62.887 1.153 62.879 1.263 c
+62.868 1.371 62.85 1.454 62.821 1.514 c
+62.798 1.579 62.769 1.631 62.732 1.66 c
+62.703 1.69 62.663 1.705 62.615 1.705 c
+62.555 1.705 62.501 1.675 62.453 1.616 c
+62.412 1.565 62.379 1.491 62.35 1.396 c
+62.32 1.308 62.295 1.194 62.277 1.058 c
+62.266 0.918 62.262 0.768 62.262 0.602 c
+62.262 -1.264 l
+61.512 -1.264 l
+61.512 1.469 l
+61.512 1.705 l
+61.512 1.925 l
+61.512 2.002 61.505 2.065 61.497 2.117 c
+61.497 2.219 l
+62.173 2.219 l
+62.173 2.131 l
+62.173 1.984 l
+62.181 1.925 62.189 1.866 62.189 1.807 c
+62.189 1.646 l
+62.203 1.646 l
+62.221 1.734 62.251 1.811 62.291 1.881 c
+62.328 1.959 62.372 2.028 62.424 2.087 c
+62.482 2.146 62.549 2.19 62.629 2.219 c
+62.707 2.256 62.794 2.277 62.894 2.277 c
+63.078 2.277 63.218 2.223 63.305 2.117 c
+63.401 2.017 63.471 1.859 63.511 1.646 c
+63.526 1.646 l
+63.563 1.741 63.603 1.83 63.644 1.911 c
+63.691 1.988 63.746 2.05 63.805 2.102 c
+63.864 2.16 63.93 2.204 64.011 2.234 c
+64.088 2.263 64.176 2.277 64.275 2.277 c
+64.411 2.277 64.525 2.252 64.614 2.204 c
+64.702 2.153 64.768 2.079 64.82 1.984 c
+64.878 1.884 64.915 1.756 64.937 1.602 c
+64.967 1.454 64.981 1.271 64.981 1.058 c
+64.981 -1.264 l
+64.261 -1.264 l
+64.261 0.852 l
+64.261 1.018 64.253 1.153 64.246 1.263 c
+64.236 1.371 64.217 1.454 64.188 1.514 c
+64.165 1.579 64.136 1.631 64.099 1.66 c
+64.07 1.69 64.03 1.705 63.982 1.705 c
+63.864 1.705 63.768 1.616 63.702 1.44 c
+63.644 1.271 63.614 1.014 63.614 0.661 c
+63.614 -1.264 l
+h
+67.351 -1.324 m
+67.094 -1.324 66.866 -1.287 66.661 -1.22 c
+66.455 -1.143 66.278 -1.029 66.131 -0.882 c
+65.984 -0.728 65.867 -0.537 65.778 -0.31 c
+65.697 -0.085 65.661 0.18 65.661 0.484 c
+65.661 0.816 65.705 1.095 65.793 1.323 c
+65.888 1.558 66.017 1.741 66.175 1.881 c
+66.341 2.017 66.528 2.117 66.734 2.175 c
+66.94 2.241 67.149 2.277 67.366 2.277 c
+67.638 2.277 67.873 2.227 68.072 2.131 c
+68.277 2.042 68.443 1.911 68.571 1.734 c
+68.707 1.565 68.807 1.359 68.865 1.117 c
+68.932 0.881 68.968 0.617 68.968 0.323 c
+68.968 0.309 l
+66.602 0.309 l
+66.602 0.161 66.617 0.022 66.646 -0.103 c
+66.682 -0.232 66.738 -0.346 66.808 -0.441 c
+66.873 -0.53 66.958 -0.599 67.058 -0.647 c
+67.153 -0.699 67.267 -0.721 67.396 -0.721 c
+67.55 -0.721 67.689 -0.688 67.807 -0.618 c
+67.932 -0.551 68.02 -0.449 68.072 -0.31 c
+68.909 -0.383 l
+68.88 -0.482 68.824 -0.588 68.747 -0.706 c
+68.666 -0.817 68.564 -0.919 68.439 -1.015 c
+68.321 -1.103 68.167 -1.176 67.983 -1.235 c
+67.807 -1.294 67.594 -1.324 67.351 -1.324 c
+67.351 1.705 m
+67.263 1.705 67.175 1.69 67.087 1.66 c
+66.999 1.631 66.918 1.579 66.852 1.514 c
+66.782 1.444 66.723 1.356 66.675 1.249 c
+66.634 1.138 66.617 1.014 66.617 0.866 c
+68.086 0.866 l
+68.086 1.003 68.061 1.124 68.013 1.234 c
+67.972 1.341 67.918 1.429 67.851 1.499 c
+67.793 1.565 67.719 1.616 67.631 1.646 c
+67.542 1.683 67.447 1.705 67.351 1.705 c
+70.221 -2.631 m
+70.221 -2.014 l
+71.294 -2.014 l
+71.294 2.896 l
+70.221 2.896 l
+70.221 3.513 l
+72.147 3.513 l
+72.147 -2.631 l
+h
+78.729 -2.631 m
+78.729 3.513 l
+80.654 3.513 l
+80.654 2.896 l
+79.581 2.896 l
+79.581 -2.014 l
+80.654 -2.014 l
+80.654 -2.631 l
+h
+83.598 -1.324 m
+83.311 -1.324 83.068 -1.283 82.862 -1.206 c
+82.657 -1.118 82.484 -0.996 82.348 -0.838 c
+82.208 -0.684 82.106 -0.497 82.039 -0.279 c
+81.969 -0.056 81.937 0.191 81.937 0.455 c
+81.937 0.749 81.969 1.007 82.039 1.234 c
+82.116 1.458 82.223 1.646 82.363 1.793 c
+82.509 1.947 82.686 2.065 82.892 2.146 c
+83.097 2.234 83.333 2.277 83.598 2.277 c
+83.822 2.277 84.024 2.248 84.2 2.19 c
+84.377 2.131 84.527 2.046 84.656 1.94 c
+84.78 1.841 84.884 1.72 84.965 1.572 c
+85.042 1.433 85.096 1.282 85.127 1.117 c
+84.215 1.072 l
+84.186 1.249 84.115 1.389 84.009 1.499 c
+83.91 1.606 83.766 1.66 83.583 1.66 c
+83.336 1.66 83.16 1.558 83.053 1.352 c
+82.943 1.153 82.892 0.866 82.892 0.484 c
+82.892 -0.31 83.128 -0.706 83.598 -0.706 c
+83.763 -0.706 83.906 -0.655 84.024 -0.545 c
+84.142 -0.437 84.215 -0.276 84.244 -0.059 c
+85.156 -0.103 l
+85.127 -0.273 85.071 -0.427 84.994 -0.574 c
+84.924 -0.721 84.821 -0.853 84.685 -0.971 c
+84.556 -1.081 84.398 -1.169 84.215 -1.235 c
+84.038 -1.294 83.833 -1.324 83.598 -1.324 c
+89.304 0.484 m
+89.304 0.209 89.268 -0.04 89.202 -0.265 c
+89.131 -0.482 89.029 -0.669 88.892 -0.823 c
+88.753 -0.981 88.577 -1.103 88.364 -1.191 c
+88.147 -1.279 87.893 -1.324 87.599 -1.324 c
+87.323 -1.324 87.078 -1.279 86.864 -1.191 c
+86.658 -1.103 86.486 -0.981 86.35 -0.823 c
+86.21 -0.669 86.108 -0.482 86.041 -0.265 c
+85.971 -0.04 85.938 0.209 85.938 0.484 c
+85.938 0.738 85.968 0.974 86.027 1.19 c
+86.093 1.414 86.195 1.606 86.335 1.764 c
+86.471 1.928 86.648 2.057 86.864 2.146 c
+87.078 2.234 87.335 2.277 87.629 2.277 c
+87.941 2.277 88.202 2.234 88.408 2.146 c
+88.62 2.057 88.794 1.928 88.922 1.764 c
+89.058 1.606 89.158 1.414 89.216 1.19 c
+89.275 0.974 89.304 0.738 89.304 0.484 c
+88.349 0.484 m
+88.349 0.69 88.334 0.866 88.304 1.014 c
+88.283 1.161 88.246 1.282 88.187 1.381 c
+88.128 1.477 88.055 1.547 87.967 1.587 c
+87.878 1.635 87.768 1.66 87.643 1.66 c
+87.379 1.66 87.188 1.562 87.07 1.367 c
+86.953 1.179 86.893 0.885 86.893 0.484 c
+86.893 0.062 86.953 -0.243 87.07 -0.427 c
+87.188 -0.614 87.364 -0.706 87.599 -0.706 c
+87.724 -0.706 87.838 -0.688 87.938 -0.647 c
+88.033 -0.599 88.113 -0.526 88.173 -0.427 c
+88.239 -0.331 88.283 -0.206 88.304 -0.059 c
+88.334 0.087 88.349 0.268 88.349 0.484 c
+91.321 -1.264 m
+91.321 0.852 l
+91.321 1.018 91.315 1.153 91.307 1.263 c
+91.296 1.371 91.278 1.454 91.248 1.514 c
+91.226 1.579 91.197 1.631 91.16 1.66 c
+91.13 1.69 91.09 1.705 91.043 1.705 c
+90.984 1.705 90.929 1.675 90.881 1.616 c
+90.841 1.565 90.808 1.491 90.778 1.396 c
+90.748 1.308 90.723 1.194 90.704 1.058 c
+90.694 0.918 90.69 0.768 90.69 0.602 c
+90.69 -1.264 l
+89.94 -1.264 l
+89.94 1.469 l
+89.94 1.705 l
+89.94 1.925 l
+89.94 2.002 89.933 2.065 89.925 2.117 c
+89.925 2.219 l
+90.602 2.219 l
+90.602 2.131 l
+90.602 1.984 l
+90.609 1.925 90.616 1.866 90.616 1.807 c
+90.616 1.646 l
+90.631 1.646 l
+90.65 1.734 90.679 1.811 90.719 1.881 c
+90.756 1.959 90.8 2.028 90.851 2.087 c
+90.91 2.146 90.976 2.19 91.057 2.219 c
+91.134 2.256 91.223 2.277 91.321 2.277 c
+91.506 2.277 91.645 2.223 91.733 2.117 c
+91.829 2.017 91.899 1.859 91.939 1.646 c
+91.954 1.646 l
+91.99 1.741 92.031 1.83 92.071 1.911 c
+92.119 1.988 92.175 2.05 92.233 2.102 c
+92.292 2.16 92.358 2.204 92.439 2.234 c
+92.516 2.263 92.604 2.277 92.703 2.277 c
+92.84 2.277 92.954 2.252 93.042 2.204 c
+93.129 2.153 93.196 2.079 93.247 1.984 c
+93.306 1.884 93.343 1.756 93.365 1.602 c
+93.395 1.454 93.409 1.271 93.409 1.058 c
+93.409 -1.264 l
+92.689 -1.264 l
+92.689 0.852 l
+92.689 1.018 92.682 1.153 92.674 1.263 c
+92.663 1.371 92.645 1.454 92.616 1.514 c
+92.593 1.579 92.564 1.631 92.527 1.66 c
+92.497 1.69 92.458 1.705 92.41 1.705 c
+92.292 1.705 92.196 1.616 92.13 1.44 c
+92.071 1.271 92.042 1.014 92.042 0.661 c
+92.042 -1.264 l
+h
+95.383 -1.264 m
+95.383 0.852 l
+95.383 1.018 95.375 1.153 95.367 1.263 c
+95.357 1.371 95.338 1.454 95.309 1.514 c
+95.287 1.579 95.257 1.631 95.221 1.66 c
+95.192 1.69 95.151 1.705 95.103 1.705 c
+95.045 1.705 94.989 1.675 94.941 1.616 c
+94.901 1.565 94.868 1.491 94.839 1.396 c
+94.81 1.308 94.783 1.194 94.765 1.058 c
+94.754 0.918 94.75 0.768 94.75 0.602 c
+94.75 -1.264 l
+94.001 -1.264 l
+94.001 1.469 l
+94.001 1.705 l
+94.001 1.925 l
+94.001 2.002 93.993 2.065 93.986 2.117 c
+93.986 2.219 l
+94.662 2.219 l
+94.662 2.131 l
+94.662 1.984 l
+94.669 1.925 94.677 1.866 94.677 1.807 c
+94.677 1.646 l
+94.692 1.646 l
+94.71 1.734 94.739 1.811 94.78 1.881 c
+94.816 1.959 94.86 2.028 94.912 2.087 c
+94.97 2.146 95.037 2.19 95.118 2.219 c
+95.195 2.256 95.283 2.277 95.383 2.277 c
+95.566 2.277 95.706 2.223 95.794 2.117 c
+95.89 2.017 95.959 1.859 96 1.646 c
+96.015 1.646 l
+96.051 1.741 96.092 1.83 96.132 1.911 c
+96.18 1.988 96.235 2.05 96.294 2.102 c
+96.352 2.16 96.418 2.204 96.499 2.234 c
+96.576 2.263 96.665 2.277 96.764 2.277 c
+96.9 2.277 97.014 2.252 97.102 2.204 c
+97.191 2.153 97.256 2.079 97.308 1.984 c
+97.367 1.884 97.403 1.756 97.426 1.602 c
+97.455 1.454 97.47 1.271 97.47 1.058 c
+97.47 -1.264 l
+96.749 -1.264 l
+96.749 0.852 l
+96.749 1.018 96.742 1.153 96.734 1.263 c
+96.724 1.371 96.705 1.454 96.676 1.514 c
+96.653 1.579 96.624 1.631 96.588 1.66 c
+96.558 1.69 96.518 1.705 96.47 1.705 c
+96.352 1.705 96.257 1.616 96.191 1.44 c
+96.132 1.271 96.103 1.014 96.103 0.661 c
+96.103 -1.264 l
+h
+100.387 -0.647 m
+101.519 -0.647 l
+101.519 -1.264 l
+98.212 -1.264 l
+98.212 -0.647 l
+99.476 -0.647 l
+99.476 1.602 l
+98.55 1.602 l
+98.55 2.219 l
+100.387 2.219 l
+h
+99.476 3.513 0.912 -0.676 re
+99.476 2.836 m
+102.949 1.602 m
+102.405 1.602 l
+102.405 2.219 l
+102.993 2.219 l
+103.272 3.116 l
+103.845 3.116 l
+103.845 2.219 l
+105.081 2.219 l
+105.081 1.602 l
+103.845 1.602 l
+103.845 -0.103 l
+103.845 -0.324 l
+103.853 -0.393 103.875 -0.456 103.905 -0.515 c
+103.941 -0.566 103.996 -0.611 104.066 -0.647 c
+104.143 -0.676 104.257 -0.691 104.404 -0.691 c
+104.54 -0.691 104.676 -0.688 104.815 -0.676 c
+104.952 -0.659 105.083 -0.632 105.212 -0.603 c
+105.212 -1.206 l
+105.131 -1.216 105.054 -1.231 104.977 -1.25 c
+104.896 -1.261 104.819 -1.268 104.742 -1.279 c
+104.661 -1.287 104.573 -1.294 104.478 -1.294 c
+104.389 -1.301 104.29 -1.309 104.183 -1.309 c
+103.996 -1.309 103.834 -1.294 103.699 -1.264 c
+103.57 -1.228 103.456 -1.183 103.36 -1.133 c
+103.272 -1.085 103.199 -1.025 103.14 -0.956 c
+103.081 -0.879 103.037 -0.802 103.007 -0.721 c
+102.978 -0.632 102.956 -0.545 102.949 -0.456 c
+102.938 -0.36 102.934 -0.265 102.934 -0.177 c
+h
+113.569 -0.25 m
+113.569 -0.42 113.528 -0.57 113.451 -0.706 c
+113.382 -0.834 113.278 -0.948 113.143 -1.044 c
+113.014 -1.133 112.852 -1.202 112.657 -1.25 c
+112.47 -1.297 112.253 -1.324 112.011 -1.324 c
+111.782 -1.324 111.585 -1.309 111.408 -1.279 c
+111.232 -1.25 111.074 -1.202 110.938 -1.133 c
+110.798 -1.056 110.687 -0.956 110.6 -0.838 c
+110.511 -0.721 110.442 -0.574 110.394 -0.397 c
+111.202 -0.279 l
+111.221 -0.379 111.25 -0.456 111.29 -0.515 c
+111.338 -0.574 111.397 -0.618 111.467 -0.647 c
+111.533 -0.676 111.614 -0.703 111.702 -0.721 c
+111.79 -0.732 111.893 -0.736 112.011 -0.736 c
+112.106 -0.736 112.202 -0.732 112.29 -0.721 c
+112.378 -0.703 112.455 -0.676 112.526 -0.647 c
+112.591 -0.618 112.643 -0.581 112.672 -0.53 c
+112.709 -0.482 112.731 -0.42 112.731 -0.339 c
+112.731 -0.243 112.702 -0.169 112.643 -0.118 c
+112.591 -0.071 112.526 -0.03 112.437 0 c
+112.349 0.037 112.239 0.066 112.114 0.087 c
+111.996 0.118 111.863 0.147 111.717 0.176 c
+111.577 0.213 111.437 0.253 111.29 0.294 c
+111.151 0.341 111.026 0.404 110.908 0.484 c
+110.798 0.562 110.71 0.661 110.644 0.779 c
+110.574 0.897 110.541 1.047 110.541 1.234 c
+110.541 1.389 110.57 1.531 110.629 1.66 c
+110.695 1.797 110.791 1.911 110.908 1.999 c
+111.034 2.087 111.192 2.153 111.379 2.204 c
+111.562 2.252 111.776 2.277 112.011 2.277 c
+112.194 2.277 112.371 2.256 112.54 2.219 c
+112.705 2.19 112.852 2.135 112.981 2.057 c
+113.106 1.988 113.216 1.888 113.305 1.764 c
+113.392 1.646 113.451 1.502 113.48 1.337 c
+112.686 1.263 l
+112.665 1.341 112.636 1.404 112.599 1.454 c
+112.559 1.514 112.511 1.558 112.451 1.587 c
+112.4 1.624 112.337 1.649 112.26 1.66 c
+112.179 1.668 112.099 1.675 112.011 1.675 c
+111.794 1.675 111.632 1.646 111.526 1.587 c
+111.416 1.535 111.364 1.448 111.364 1.323 c
+111.364 1.242 111.383 1.179 111.423 1.132 c
+111.47 1.08 111.533 1.043 111.614 1.014 c
+111.702 0.984 111.797 0.955 111.908 0.926 c
+112.015 0.903 112.136 0.881 112.275 0.852 c
+112.43 0.823 112.588 0.783 112.746 0.735 c
+112.9 0.683 113.039 0.621 113.157 0.544 c
+113.274 0.463 113.37 0.359 113.451 0.235 c
+113.528 0.106 113.569 -0.056 113.569 -0.25 c
+115.425 1.514 m
+115.543 1.786 115.693 1.984 115.88 2.102 c
+116.064 2.219 116.285 2.277 116.542 2.277 c
+116.748 2.277 116.917 2.241 117.056 2.175 c
+117.203 2.105 117.313 2.013 117.394 1.896 c
+117.482 1.778 117.542 1.635 117.571 1.469 c
+117.607 1.3 117.629 1.124 117.629 0.941 c
+117.629 -1.264 l
+116.718 -1.264 l
+116.718 0.735 l
+116.718 0.87 116.707 0.992 116.688 1.102 c
+116.678 1.209 116.652 1.296 116.615 1.367 c
+116.575 1.444 116.516 1.502 116.439 1.543 c
+116.369 1.579 116.277 1.602 116.16 1.602 c
+116.05 1.602 115.954 1.576 115.865 1.529 c
+115.778 1.477 115.697 1.411 115.63 1.323 c
+115.572 1.234 115.52 1.124 115.483 0.999 c
+115.454 0.881 115.439 0.749 115.439 0.602 c
+115.439 -1.264 l
+114.527 -1.264 l
+114.527 3.513 l
+115.439 3.513 l
+115.439 2.204 l
+115.439 2.135 115.432 2.065 115.425 1.999 c
+115.425 1.793 l
+115.425 1.734 115.417 1.679 115.41 1.631 c
+115.41 1.514 l
+h
+119.547 -1.324 m
+119.379 -1.324 119.228 -1.301 119.092 -1.264 c
+118.963 -1.216 118.849 -1.147 118.754 -1.058 c
+118.666 -0.971 118.596 -0.864 118.548 -0.736 c
+118.496 -0.599 118.475 -0.449 118.475 -0.279 c
+118.475 -0.074 118.508 0.095 118.577 0.235 c
+118.643 0.382 118.739 0.492 118.857 0.573 c
+118.982 0.661 119.125 0.723 119.283 0.764 c
+119.449 0.801 119.624 0.826 119.813 0.837 c
+120.532 0.852 l
+120.532 1.028 l
+120.532 1.146 120.522 1.249 120.503 1.337 c
+120.481 1.425 120.448 1.491 120.401 1.543 c
+120.36 1.602 120.312 1.639 120.253 1.66 c
+120.195 1.679 120.129 1.69 120.062 1.69 c
+119.992 1.69 119.93 1.679 119.871 1.66 c
+119.819 1.649 119.772 1.624 119.724 1.587 c
+119.684 1.558 119.651 1.506 119.622 1.44 c
+119.599 1.381 119.584 1.3 119.577 1.205 c
+118.637 1.249 l
+118.666 1.396 118.71 1.531 118.768 1.66 c
+118.835 1.786 118.93 1.896 119.048 1.984 c
+119.165 2.079 119.306 2.153 119.474 2.204 c
+119.651 2.252 119.857 2.277 120.092 2.277 c
+120.532 2.277 120.863 2.167 121.091 1.955 c
+121.326 1.749 121.444 1.44 121.444 1.028 c
+121.444 -0.235 l
+121.444 -0.456 l
+121.451 -0.515 121.465 -0.57 121.488 -0.618 c
+121.506 -0.659 121.536 -0.691 121.576 -0.721 c
+121.613 -0.743 121.664 -0.75 121.723 -0.75 c
+121.789 -0.75 121.859 -0.746 121.929 -0.736 c
+121.929 -1.22 l
+121.87 -1.231 121.815 -1.243 121.767 -1.25 c
+121.727 -1.261 121.687 -1.268 121.65 -1.279 c
+121.609 -1.287 121.565 -1.294 121.517 -1.294 c
+121.465 -1.301 121.407 -1.309 121.341 -1.309 c
+121.113 -1.309 120.948 -1.257 120.841 -1.147 c
+120.731 -1.029 120.669 -0.864 120.65 -0.647 c
+120.636 -0.647 l
+120.565 -0.757 120.496 -0.853 120.43 -0.941 c
+120.36 -1.022 120.283 -1.088 120.195 -1.147 c
+120.106 -1.206 120.007 -1.25 119.9 -1.279 c
+119.801 -1.309 119.684 -1.324 119.547 -1.324 c
+120.532 0.353 m
+120.106 0.338 l
+120.007 0.338 119.915 0.33 119.827 0.323 c
+119.746 0.312 119.68 0.286 119.622 0.249 c
+119.562 0.209 119.511 0.151 119.474 0.073 c
+119.433 0.003 119.416 -0.088 119.416 -0.206 c
+119.416 -0.375 119.449 -0.497 119.518 -0.574 c
+119.584 -0.655 119.684 -0.691 119.813 -0.691 c
+119.919 -0.691 120.018 -0.669 120.106 -0.618 c
+120.202 -0.57 120.283 -0.508 120.341 -0.427 c
+120.407 -0.35 120.459 -0.262 120.488 -0.162 c
+120.518 -0.056 120.532 0.058 120.532 0.176 c
+h
+123.02 -2.631 m
+123.02 -2.014 l
+124.093 -2.014 l
+124.093 2.896 l
+123.02 2.896 l
+123.02 3.513 l
+124.946 3.513 l
+124.946 -2.631 l
+h
+f
+Q
+q 1 0 0 1 368.1085 380.8423 cm
+0 0 m
+-0.029 -0.58 -0.191 -1.022 -0.484 -1.323 c
+-0.779 -1.627 -1.198 -1.778 -1.734 -1.778 c
+-2.263 -1.778 -2.69 -1.58 -3.013 -1.176 c
+-3.329 -0.764 -3.484 -0.206 -3.484 0.5 c
+-3.484 1.47 l
+-3.484 2.165 -3.322 2.712 -2.998 3.117 c
+-2.675 3.516 -2.23 3.719 -1.66 3.719 c
+-1.153 3.719 -0.756 3.561 -0.47 3.248 c
+-0.187 2.944 -0.029 2.502 0 1.926 c
+-0.691 1.926 l
+-0.72 2.367 -0.816 2.679 -0.97 2.866 c
+-1.117 3.05 -1.348 3.146 -1.66 3.146 c
+-2.036 3.146 -2.318 2.999 -2.513 2.705 c
+-2.711 2.418 -2.807 2.003 -2.807 1.455 c
+-2.807 0.47 l
+-2.807 -0.07 -2.715 -0.484 -2.528 -0.779 c
+-2.344 -1.066 -2.079 -1.205 -1.734 -1.205 c
+-1.381 -1.205 -1.128 -1.117 -0.97 -0.941 c
+-0.816 -0.764 -0.72 -0.452 -0.691 0 c
+h
+2.455 1.661 m
+2.367 1.679 2.268 1.691 2.161 1.691 c
+1.827 1.691 1.592 1.507 1.455 1.147 c
+1.455 -1.705 l
+0.809 -1.705 l
+0.809 2.278 l
+1.441 2.278 l
+1.455 1.867 l
+1.632 2.19 1.875 2.352 2.191 2.352 c
+2.297 2.352 2.386 2.33 2.455 2.294 c
+h
+4.454 -1.778 m
+3.955 -1.778 3.572 -1.631 3.308 -1.338 c
+3.043 -1.043 2.911 -0.61 2.911 -0.029 c
+2.911 0.441 l
+2.911 1.037 3.036 1.503 3.293 1.837 c
+3.558 2.18 3.918 2.352 4.381 2.352 c
+4.84 2.352 5.182 2.198 5.409 1.897 c
+5.644 1.602 5.766 1.139 5.777 0.515 c
+5.777 0.088 l
+3.558 0.088 l
+3.558 0 l
+3.558 -0.434 3.635 -0.746 3.793 -0.941 c
+3.959 -1.128 4.19 -1.22 4.484 -1.22 c
+4.678 -1.22 4.851 -1.187 4.998 -1.117 c
+5.145 -1.04 5.281 -0.922 5.409 -0.764 c
+5.748 -1.176 l
+5.461 -1.58 5.031 -1.778 4.454 -1.778 c
+4.381 1.793 m
+4.105 1.793 3.903 1.698 3.778 1.515 c
+3.649 1.326 3.576 1.037 3.558 0.647 c
+5.131 0.647 l
+5.131 0.735 l
+5.108 1.118 5.042 1.386 4.925 1.544 c
+4.807 1.709 4.624 1.793 4.381 1.793 c
+8.599 -1.705 m
+8.559 -1.617 8.533 -1.469 8.526 -1.264 c
+8.291 -1.61 7.996 -1.778 7.643 -1.778 c
+7.28 -1.778 6.997 -1.683 6.791 -1.484 c
+6.593 -1.278 6.498 -0.992 6.498 -0.617 c
+6.498 -0.216 6.633 0.103 6.909 0.339 c
+7.181 0.58 7.556 0.706 8.026 0.706 c
+8.511 0.706 l
+8.511 1.132 l
+8.511 1.367 8.456 1.532 8.349 1.632 c
+8.239 1.738 8.077 1.793 7.865 1.793 c
+7.666 1.793 7.504 1.735 7.379 1.617 c
+7.262 1.5 7.203 1.353 7.203 1.176 c
+6.556 1.176 l
+6.556 1.371 6.615 1.562 6.733 1.75 c
+6.858 1.933 7.019 2.08 7.217 2.19 c
+7.423 2.297 7.651 2.352 7.909 2.352 c
+8.309 2.352 8.614 2.249 8.819 2.043 c
+9.033 1.837 9.147 1.544 9.158 1.162 c
+9.158 -0.852 l
+9.158 -1.157 9.195 -1.422 9.276 -1.646 c
+9.276 -1.705 l
+h
+7.732 -1.19 m
+7.897 -1.19 8.048 -1.147 8.188 -1.058 c
+8.335 -0.97 8.441 -0.86 8.511 -0.72 c
+8.511 0.221 l
+8.144 0.221 l
+7.828 0.221 7.585 0.151 7.408 0.015 c
+7.232 -0.114 7.144 -0.301 7.144 -0.544 c
+7.144 -0.771 7.188 -0.937 7.277 -1.043 c
+7.365 -1.143 7.516 -1.19 7.732 -1.19 c
+11.01 3.234 m
+11.01 2.278 l
+11.612 2.278 l
+11.612 1.75 l
+11.01 1.75 l
+11.01 -0.72 l
+11.01 -0.878 11.032 -0.995 11.084 -1.072 c
+11.142 -1.153 11.231 -1.19 11.348 -1.19 c
+11.437 -1.19 11.524 -1.176 11.612 -1.147 c
+11.612 -1.705 l
+11.466 -1.753 11.311 -1.778 11.157 -1.778 c
+10.899 -1.778 10.705 -1.687 10.569 -1.5 c
+10.429 -1.315 10.363 -1.055 10.363 -0.72 c
+10.363 1.75 l
+9.76 1.75 l
+9.76 2.278 l
+10.363 2.278 l
+10.363 3.234 l
+h
+13.803 -1.778 m
+13.303 -1.778 12.921 -1.631 12.657 -1.338 c
+12.391 -1.043 12.26 -0.61 12.26 -0.029 c
+12.26 0.441 l
+12.26 1.037 12.385 1.503 12.642 1.837 c
+12.906 2.18 13.266 2.352 13.729 2.352 c
+14.188 2.352 14.531 2.198 14.758 1.897 c
+14.993 1.602 15.115 1.139 15.126 0.515 c
+15.126 0.088 l
+12.906 0.088 l
+12.906 0 l
+12.906 -0.434 12.983 -0.746 13.141 -0.941 c
+13.307 -1.128 13.538 -1.22 13.833 -1.22 c
+14.027 -1.22 14.199 -1.187 14.346 -1.117 c
+14.494 -1.04 14.629 -0.922 14.758 -0.764 c
+15.096 -1.176 l
+14.81 -1.58 14.38 -1.778 13.803 -1.778 c
+13.729 1.793 m
+13.453 1.793 13.251 1.698 13.127 1.515 c
+12.998 1.326 12.925 1.037 12.906 0.647 c
+14.479 0.647 l
+14.479 0.735 l
+14.457 1.118 14.39 1.386 14.273 1.544 c
+14.155 1.709 13.972 1.793 13.729 1.793 c
+19.668 -1.705 m
+19.628 -1.617 19.601 -1.469 19.595 -1.264 c
+19.359 -1.61 19.065 -1.778 18.712 -1.778 c
+18.348 -1.778 18.066 -1.683 17.86 -1.484 c
+17.661 -1.278 17.565 -0.992 17.565 -0.617 c
+17.565 -0.216 17.702 0.103 17.978 0.339 c
+18.249 0.58 18.624 0.706 19.094 0.706 c
+19.58 0.706 l
+19.58 1.132 l
+19.58 1.367 19.524 1.532 19.418 1.632 c
+19.308 1.738 19.146 1.793 18.932 1.793 c
+18.735 1.793 18.573 1.735 18.448 1.617 c
+18.33 1.5 18.271 1.353 18.271 1.176 c
+17.625 1.176 l
+17.625 1.371 17.683 1.562 17.801 1.75 c
+17.926 1.933 18.088 2.08 18.286 2.19 c
+18.492 2.297 18.72 2.352 18.977 2.352 c
+19.377 2.352 19.682 2.249 19.888 2.043 c
+20.102 1.837 20.216 1.544 20.226 1.162 c
+20.226 -0.852 l
+20.226 -1.157 20.263 -1.422 20.343 -1.646 c
+20.343 -1.705 l
+h
+18.801 -1.19 m
+18.966 -1.19 19.117 -1.147 19.256 -1.058 c
+19.403 -0.97 19.51 -0.86 19.58 -0.72 c
+19.58 0.221 l
+19.212 0.221 l
+18.896 0.221 18.654 0.151 18.477 0.015 c
+18.301 -0.114 18.213 -0.301 18.213 -0.544 c
+18.213 -0.771 18.257 -0.937 18.344 -1.043 c
+18.433 -1.143 18.583 -1.19 18.801 -1.19 c
+23.798 3.234 m
+23.798 2.278 l
+24.401 2.278 l
+24.401 1.75 l
+23.798 1.75 l
+23.798 -0.72 l
+23.798 -0.878 23.82 -0.995 23.871 -1.072 c
+23.931 -1.153 24.019 -1.19 24.137 -1.19 c
+24.224 -1.19 24.312 -1.176 24.401 -1.147 c
+24.401 -1.705 l
+24.254 -1.753 24.1 -1.778 23.946 -1.778 c
+23.688 -1.778 23.493 -1.687 23.358 -1.5 c
+23.217 -1.315 23.152 -1.055 23.152 -0.72 c
+23.152 1.75 l
+22.549 1.75 l
+22.549 2.278 l
+23.152 2.278 l
+23.152 3.234 l
+h
+27.208 -1.705 m
+27.168 -1.617 27.142 -1.469 27.135 -1.264 c
+26.899 -1.61 26.606 -1.778 26.253 -1.778 c
+25.889 -1.778 25.606 -1.683 25.4 -1.484 c
+25.202 -1.278 25.106 -0.992 25.106 -0.617 c
+25.106 -0.216 25.242 0.103 25.518 0.339 c
+25.79 0.58 26.165 0.706 26.635 0.706 c
+27.121 0.706 l
+27.121 1.132 l
+27.121 1.367 27.065 1.532 26.959 1.632 c
+26.849 1.738 26.687 1.793 26.473 1.793 c
+26.275 1.793 26.113 1.735 25.988 1.617 c
+25.87 1.5 25.812 1.353 25.812 1.176 c
+25.165 1.176 l
+25.165 1.371 25.224 1.562 25.342 1.75 c
+25.467 1.933 25.629 2.08 25.827 2.19 c
+26.032 2.297 26.261 2.352 26.518 2.352 c
+26.918 2.352 27.223 2.249 27.429 2.043 c
+27.642 1.837 27.756 1.544 27.767 1.162 c
+27.767 -0.852 l
+27.767 -1.157 27.804 -1.422 27.884 -1.646 c
+27.884 -1.705 l
+h
+26.341 -1.19 m
+26.506 -1.19 26.657 -1.147 26.797 -1.058 c
+26.944 -0.97 27.05 -0.86 27.121 -0.72 c
+27.121 0.221 l
+26.753 0.221 l
+26.437 0.221 26.194 0.151 26.018 0.015 c
+25.841 -0.114 25.753 -0.301 25.753 -0.544 c
+25.753 -0.771 25.797 -0.937 25.885 -1.043 c
+25.974 -1.143 26.124 -1.19 26.341 -1.19 c
+28.634 0.47 m
+28.634 1.087 28.744 1.551 28.972 1.867 c
+29.197 2.19 29.531 2.352 29.972 2.352 c
+30.373 2.352 30.677 2.176 30.884 1.823 c
+30.927 2.278 l
+31.515 2.278 l
+31.515 -1.749 l
+31.515 -2.238 31.387 -2.616 31.133 -2.881 c
+30.876 -3.146 30.523 -3.278 30.074 -3.278 c
+29.876 -3.278 29.656 -3.227 29.413 -3.131 c
+29.167 -3.032 28.987 -2.911 28.869 -2.763 c
+29.134 -2.323 l
+29.398 -2.587 29.696 -2.719 30.03 -2.719 c
+30.567 -2.719 30.843 -2.425 30.853 -1.837 c
+30.853 -1.308 l
+30.648 -1.624 30.346 -1.778 29.957 -1.778 c
+29.546 -1.778 29.222 -1.627 28.987 -1.323 c
+28.759 -1.01 28.642 -0.559 28.634 0.029 c
+h
+29.296 0.088 m
+29.296 -0.353 29.359 -0.683 29.487 -0.897 c
+29.612 -1.103 29.829 -1.205 30.134 -1.205 c
+30.456 -1.205 30.695 -1.04 30.853 -0.706 c
+30.853 1.279 l
+30.685 1.602 30.446 1.764 30.134 1.764 c
+29.839 1.764 29.623 1.661 29.487 1.455 c
+29.359 1.249 29.296 0.927 29.296 0.485 c
+h
+34.059 0.47 m
+34.059 1.047 34.194 1.503 34.47 1.837 c
+34.753 2.18 35.123 2.352 35.587 2.352 c
+36.046 2.352 36.414 2.182 36.689 1.852 c
+36.972 1.529 37.12 1.081 37.13 0.515 c
+37.13 0.088 l
+37.13 -0.482 36.987 -0.937 36.704 -1.278 c
+36.428 -1.613 36.06 -1.778 35.601 -1.778 c
+35.138 -1.778 34.768 -1.617 34.485 -1.293 c
+34.209 -0.962 34.065 -0.522 34.059 0.029 c
+h
+34.705 0.088 m
+34.705 -0.316 34.782 -0.632 34.94 -0.867 c
+35.106 -1.103 35.326 -1.22 35.601 -1.22 c
+36.168 -1.22 36.461 -0.808 36.484 0.015 c
+36.484 0.47 l
+36.484 0.871 36.399 1.191 36.233 1.426 c
+36.075 1.669 35.859 1.793 35.587 1.793 c
+35.322 1.793 35.106 1.669 34.94 1.426 c
+34.782 1.191 34.705 0.871 34.705 0.47 c
+h
+40.849 0.088 m
+40.849 -0.529 40.735 -0.995 40.511 -1.308 c
+40.294 -1.624 39.971 -1.778 39.541 -1.778 c
+39.118 -1.778 38.805 -1.598 38.6 -1.234 c
+38.57 -1.705 l
+37.968 -1.705 l
+37.968 3.94 l
+38.615 3.94 l
+38.615 1.837 l
+38.828 2.18 39.137 2.352 39.541 2.352 c
+39.971 2.352 40.294 2.194 40.511 1.882 c
+40.735 1.577 40.849 1.11 40.849 0.485 c
+h
+40.202 0.47 m
+40.202 0.941 40.133 1.272 39.996 1.47 c
+39.868 1.665 39.659 1.764 39.364 1.764 c
+39.03 1.764 38.78 1.58 38.615 1.22 c
+38.615 -0.661 l
+38.78 -1.025 39.034 -1.205 39.379 -1.205 c
+39.673 -1.205 39.883 -1.103 40.012 -0.897 c
+40.137 -0.69 40.202 -0.374 40.202 0.059 c
+h
+42.348 2.278 m
+42.348 -2.205 l
+42.348 -2.94 42.059 -3.307 41.481 -3.307 c
+41.342 -3.307 41.22 -3.285 41.114 -3.248 c
+41.114 -2.705 l
+41.184 -2.723 41.268 -2.734 41.378 -2.734 c
+41.485 -2.734 41.562 -2.69 41.614 -2.601 c
+41.672 -2.521 41.702 -2.381 41.702 -2.175 c
+41.702 2.278 l
+h
+42.377 3.322 m
+42.377 3.212 42.348 3.12 42.29 3.043 c
+42.231 2.973 42.136 2.94 42.011 2.94 c
+41.893 2.94 41.797 2.973 41.731 3.043 c
+41.672 3.12 41.643 3.212 41.643 3.322 c
+41.643 3.439 41.672 3.532 41.731 3.601 c
+41.797 3.678 41.893 3.719 42.011 3.719 c
+42.136 3.719 42.231 3.678 42.29 3.601 c
+42.348 3.52 42.377 3.429 42.377 3.322 c
+44.803 -1.778 m
+44.303 -1.778 43.921 -1.631 43.657 -1.338 c
+43.392 -1.043 43.26 -0.61 43.26 -0.029 c
+43.26 0.441 l
+43.26 1.037 43.385 1.503 43.642 1.837 c
+43.906 2.18 44.266 2.352 44.729 2.352 c
+45.189 2.352 45.531 2.198 45.758 1.897 c
+45.994 1.602 46.115 1.139 46.126 0.515 c
+46.126 0.088 l
+43.906 0.088 l
+43.906 0 l
+43.906 -0.434 43.983 -0.746 44.141 -0.941 c
+44.307 -1.128 44.538 -1.22 44.833 -1.22 c
+45.028 -1.22 45.2 -1.187 45.347 -1.117 c
+45.494 -1.04 45.63 -0.922 45.758 -0.764 c
+46.096 -1.176 l
+45.81 -1.58 45.38 -1.778 44.803 -1.778 c
+44.729 1.793 m
+44.454 1.793 44.252 1.698 44.127 1.515 c
+43.998 1.326 43.925 1.037 43.906 0.647 c
+45.479 0.647 l
+45.479 0.735 l
+45.457 1.118 45.391 1.386 45.273 1.544 c
+45.155 1.709 44.972 1.793 44.729 1.793 c
+48.287 -1.22 m
+48.5 -1.22 48.673 -1.157 48.801 -1.029 c
+48.937 -0.893 49.011 -0.702 49.022 -0.455 c
+49.639 -0.455 l
+49.617 -0.837 49.481 -1.157 49.228 -1.411 c
+48.97 -1.658 48.658 -1.778 48.287 -1.778 c
+47.794 -1.778 47.42 -1.627 47.155 -1.323 c
+46.898 -1.01 46.773 -0.544 46.773 0.073 c
+46.773 0.515 l
+46.773 1.11 46.898 1.565 47.155 1.882 c
+47.42 2.194 47.794 2.352 48.287 2.352 c
+48.687 2.352 49.007 2.22 49.242 1.955 c
+49.485 1.698 49.617 1.353 49.639 0.912 c
+49.022 0.912 l
+48.999 1.205 48.926 1.426 48.801 1.573 c
+48.683 1.72 48.511 1.793 48.287 1.793 c
+47.993 1.793 47.776 1.694 47.64 1.5 c
+47.501 1.312 47.427 1.004 47.42 0.574 c
+47.42 0.059 l
+47.42 -0.411 47.486 -0.746 47.625 -0.941 c
+47.773 -1.128 47.993 -1.22 48.287 -1.22 c
+51.241 3.234 m
+51.241 2.278 l
+51.844 2.278 l
+51.844 1.75 l
+51.241 1.75 l
+51.241 -0.72 l
+51.241 -0.878 51.264 -0.995 51.314 -1.072 c
+51.374 -1.153 51.462 -1.19 51.58 -1.19 c
+51.667 -1.19 51.756 -1.176 51.844 -1.147 c
+51.844 -1.705 l
+51.697 -1.753 51.543 -1.778 51.388 -1.778 c
+51.131 -1.778 50.936 -1.687 50.8 -1.5 c
+50.661 -1.315 50.595 -1.055 50.595 -0.72 c
+50.595 1.75 l
+49.992 1.75 l
+49.992 2.278 l
+50.595 2.278 l
+50.595 3.234 l
+h
+54.946 2.278 m
+54.96 1.837 l
+55.214 2.18 55.537 2.352 55.931 2.352 c
+56.636 2.352 56.992 1.882 57.003 0.941 c
+57.003 -1.705 l
+56.357 -1.705 l
+56.357 0.912 l
+56.357 1.224 56.301 1.444 56.195 1.573 c
+56.085 1.698 55.931 1.764 55.725 1.764 c
+55.567 1.764 55.42 1.709 55.283 1.602 c
+55.155 1.492 55.052 1.357 54.975 1.191 c
+54.975 -1.705 l
+54.328 -1.705 l
+54.328 2.278 l
+h
+60.016 -1.705 m
+59.976 -1.617 59.95 -1.469 59.943 -1.264 c
+59.708 -1.61 59.414 -1.778 59.061 -1.778 c
+58.697 -1.778 58.414 -1.683 58.208 -1.484 c
+58.011 -1.278 57.915 -0.992 57.915 -0.617 c
+57.915 -0.216 58.051 0.103 58.326 0.339 c
+58.599 0.58 58.973 0.706 59.443 0.706 c
+59.929 0.706 l
+59.929 1.132 l
+59.929 1.367 59.873 1.532 59.767 1.632 c
+59.657 1.738 59.495 1.793 59.282 1.793 c
+59.083 1.793 58.921 1.735 58.796 1.617 c
+58.679 1.5 58.62 1.353 58.62 1.176 c
+57.973 1.176 l
+57.973 1.371 58.032 1.562 58.15 1.75 c
+58.275 1.933 58.437 2.08 58.635 2.19 c
+58.84 2.297 59.069 2.352 59.326 2.352 c
+59.727 2.352 60.031 2.249 60.237 2.043 c
+60.45 1.837 60.564 1.544 60.575 1.162 c
+60.575 -0.852 l
+60.575 -1.157 60.612 -1.422 60.693 -1.646 c
+60.693 -1.705 l
+h
+59.149 -1.19 m
+59.315 -1.19 59.465 -1.147 59.605 -1.058 c
+59.752 -0.97 59.858 -0.86 59.929 -0.72 c
+59.929 0.221 l
+59.561 0.221 l
+59.245 0.221 59.002 0.151 58.826 0.015 c
+58.649 -0.114 58.561 -0.301 58.561 -0.544 c
+58.561 -0.771 58.605 -0.937 58.694 -1.043 c
+58.782 -1.143 58.933 -1.19 59.149 -1.19 c
+62.192 2.278 m
+62.206 1.911 l
+62.449 2.205 62.769 2.352 63.162 2.352 c
+63.603 2.352 63.912 2.153 64.088 1.764 c
+64.342 2.153 64.691 2.352 65.132 2.352 c
+65.867 2.352 66.242 1.889 66.264 0.97 c
+66.264 -1.705 l
+65.617 -1.705 l
+65.617 0.912 l
+65.617 1.205 65.562 1.419 65.456 1.558 c
+65.356 1.694 65.184 1.764 64.941 1.764 c
+64.743 1.764 64.581 1.683 64.456 1.529 c
+64.338 1.382 64.268 1.191 64.25 0.956 c
+64.25 -1.705 l
+63.588 -1.705 l
+63.588 0.941 l
+63.588 1.488 63.368 1.764 62.927 1.764 c
+62.593 1.764 62.358 1.602 62.221 1.279 c
+62.221 -1.705 l
+61.575 -1.705 l
+61.575 2.278 l
+h
+68.66 -1.778 m
+68.16 -1.778 67.777 -1.631 67.513 -1.338 c
+67.249 -1.043 67.116 -0.61 67.116 -0.029 c
+67.116 0.441 l
+67.116 1.037 67.241 1.503 67.498 1.837 c
+67.763 2.18 68.124 2.352 68.586 2.352 c
+69.046 2.352 69.387 2.198 69.615 1.897 c
+69.85 1.602 69.971 1.139 69.983 0.515 c
+69.983 0.088 l
+67.763 0.088 l
+67.763 0 l
+67.763 -0.434 67.84 -0.746 67.999 -0.941 c
+68.164 -1.128 68.395 -1.22 68.689 -1.22 c
+68.884 -1.22 69.057 -1.187 69.204 -1.117 c
+69.35 -1.04 69.487 -0.922 69.615 -0.764 c
+69.953 -1.176 l
+69.666 -1.58 69.237 -1.778 68.66 -1.778 c
+68.586 1.793 m
+68.311 1.793 68.109 1.698 67.983 1.515 c
+67.855 1.326 67.781 1.037 67.763 0.647 c
+69.336 0.647 l
+69.336 0.735 l
+69.314 1.118 69.248 1.386 69.13 1.544 c
+69.013 1.709 68.829 1.793 68.586 1.793 c
+70.63 0.47 m
+70.63 1.077 70.74 1.544 70.967 1.867 c
+71.203 2.19 71.53 2.352 71.952 2.352 c
+72.334 2.352 72.633 2.194 72.849 1.882 c
+72.849 3.94 l
+73.495 3.94 l
+73.495 -1.705 l
+72.907 -1.705 l
+72.864 -1.278 l
+72.658 -1.613 72.353 -1.778 71.952 -1.778 c
+71.54 -1.778 71.218 -1.624 70.983 -1.308 c
+70.747 -0.985 70.63 -0.529 70.63 0.059 c
+h
+71.276 0.088 m
+71.276 -0.353 71.338 -0.683 71.467 -0.897 c
+71.603 -1.103 71.824 -1.205 72.128 -1.205 c
+72.452 -1.205 72.691 -1.043 72.849 -0.72 c
+72.849 1.294 l
+72.68 1.606 72.441 1.764 72.128 1.764 c
+71.824 1.764 71.603 1.661 71.467 1.455 c
+71.338 1.249 71.276 0.927 71.276 0.485 c
+h
+f
+Q
+q 1 0 0 1 445.1757 383.1208 cm
+0 0 m
+0.03 -0.397 l
+0.265 -0.085 0.566 0.073 0.941 0.073 c
+1.625 0.073 1.977 -0.408 1.999 -1.367 c
+1.999 -3.983 l
+0.956 -3.983 l
+0.956 -1.44 l
+0.956 -1.216 0.919 -1.055 0.853 -0.956 c
+0.783 -0.86 0.665 -0.808 0.5 -0.808 c
+0.312 -0.808 0.166 -0.904 0.059 -1.087 c
+0.059 -3.983 l
+-0.985 -3.983 l
+-0.985 0 l
+h
+4.63 -3.983 m
+4.601 -3.925 4.572 -3.821 4.542 -3.675 c
+4.355 -3.932 4.105 -4.056 3.793 -4.056 c
+3.458 -4.056 3.179 -3.95 2.955 -3.734 c
+2.738 -3.509 2.631 -3.219 2.631 -2.866 c
+2.631 -2.454 2.764 -2.138 3.028 -1.911 c
+3.293 -1.675 3.675 -1.558 4.175 -1.558 c
+4.499 -1.558 l
+4.499 -1.234 l
+4.499 -1.058 4.462 -0.937 4.395 -0.867 c
+4.337 -0.79 4.248 -0.75 4.131 -0.75 c
+3.874 -0.75 3.749 -0.904 3.749 -1.205 c
+2.705 -1.205 l
+2.705 -0.834 2.841 -0.529 3.117 -0.294 c
+3.389 -0.052 3.738 0.073 4.16 0.073 c
+4.601 0.073 4.939 -0.044 5.174 -0.279 c
+5.417 -0.507 5.542 -0.831 5.542 -1.249 c
+5.542 -3.116 l
+5.542 -3.462 5.59 -3.73 5.689 -3.925 c
+5.689 -3.983 l
+h
+4.028 -3.233 m
+4.135 -3.233 4.227 -3.215 4.307 -3.175 c
+4.395 -3.127 4.458 -3.069 4.499 -2.998 c
+4.499 -2.175 l
+4.248 -2.175 l
+4.072 -2.175 3.929 -2.234 3.822 -2.352 c
+3.723 -2.462 3.675 -2.609 3.675 -2.793 c
+3.675 -3.087 3.793 -3.233 4.028 -3.233 c
+7.262 0 m
+7.291 -0.368 l
+7.526 -0.073 7.835 0.073 8.217 0.073 c
+8.617 0.073 8.897 -0.11 9.055 -0.47 c
+9.29 -0.11 9.617 0.073 10.04 0.073 c
+10.734 0.073 11.088 -0.411 11.098 -1.382 c
+11.098 -3.983 l
+10.069 -3.983 l
+10.069 -1.44 l
+10.069 -1.216 10.032 -1.055 9.966 -0.956 c
+9.908 -0.86 9.797 -0.808 9.643 -0.808 c
+9.444 -0.808 9.305 -0.926 9.216 -1.161 c
+9.216 -3.983 l
+8.173 -3.983 l
+8.173 -1.455 l
+8.173 -1.22 8.144 -1.055 8.085 -0.956 c
+8.026 -0.86 7.915 -0.808 7.761 -0.808 c
+7.585 -0.808 7.441 -0.904 7.335 -1.087 c
+7.335 -3.983 l
+6.292 -3.983 l
+6.292 0 l
+h
+13.479 -4.056 m
+12.95 -4.056 12.532 -3.902 12.23 -3.586 c
+11.936 -3.263 11.789 -2.803 11.789 -2.205 c
+11.789 -1.897 l
+11.789 -1.272 11.925 -0.786 12.2 -0.441 c
+12.472 -0.099 12.866 0.073 13.376 0.073 c
+13.876 0.073 14.247 -0.088 14.494 -0.411 c
+14.747 -0.735 14.88 -1.213 14.89 -1.837 c
+14.89 -2.337 l
+12.818 -2.337 l
+12.836 -2.631 12.898 -2.848 13.009 -2.984 c
+13.126 -3.123 13.307 -3.19 13.553 -3.19 c
+13.895 -3.19 14.184 -3.072 14.42 -2.837 c
+14.832 -3.469 l
+14.703 -3.645 14.516 -3.788 14.273 -3.896 c
+14.026 -4.002 13.762 -4.056 13.479 -4.056 c
+12.833 -1.617 m
+13.862 -1.617 l
+13.862 -1.514 l
+13.862 -1.278 13.821 -1.103 13.744 -0.985 c
+13.675 -0.86 13.546 -0.794 13.361 -0.794 c
+13.185 -0.794 13.053 -0.864 12.965 -0.999 c
+12.884 -1.128 12.84 -1.334 12.833 -1.617 c
+f
+Q
+q 1 0 0 1 462.7704 379.1377 cm
+0 0 m
+0 3.454 l
+-0.529 3.454 l
+-0.529 3.983 l
+0 3.983 l
+0 4.438 l
+0 4.839 0.096 5.151 0.294 5.379 c
+0.5 5.604 0.78 5.718 1.133 5.718 c
+1.268 5.718 1.401 5.695 1.529 5.659 c
+1.5 5.115 l
+1.401 5.134 1.301 5.144 1.206 5.144 c
+0.831 5.144 0.647 4.88 0.647 4.351 c
+0.647 3.983 l
+1.324 3.983 l
+1.324 3.454 l
+0.647 3.454 l
+0.647 0 l
+h
+1.956 2.175 m
+1.956 2.752 2.091 3.208 2.367 3.542 c
+2.65 3.884 3.021 4.056 3.484 4.056 c
+3.944 4.056 4.311 3.887 4.586 3.557 c
+4.869 3.233 5.017 2.786 5.027 2.219 c
+5.027 1.793 l
+5.027 1.223 4.884 0.768 4.601 0.426 c
+4.326 0.091 3.958 -0.073 3.499 -0.073 c
+3.036 -0.073 2.665 0.087 2.382 0.411 c
+2.106 0.742 1.962 1.182 1.956 1.734 c
+h
+2.602 1.793 m
+2.602 1.389 2.679 1.072 2.837 0.837 c
+3.003 0.602 3.223 0.484 3.499 0.484 c
+4.065 0.484 4.358 0.897 4.381 1.72 c
+4.381 2.175 l
+4.381 2.576 4.296 2.896 4.131 3.131 c
+3.973 3.374 3.756 3.498 3.484 3.498 c
+3.219 3.498 3.003 3.374 2.837 3.131 c
+2.679 2.896 2.602 2.576 2.602 2.175 c
+h
+7.512 3.366 m
+7.423 3.384 7.325 3.395 7.217 3.395 c
+6.883 3.395 6.648 3.212 6.512 2.851 c
+6.512 0 l
+5.865 0 l
+5.865 3.983 l
+6.498 3.983 l
+6.512 3.572 l
+6.689 3.895 6.931 4.056 7.247 4.056 c
+7.354 4.056 7.442 4.035 7.512 3.998 c
+h
+11.26 0.484 m
+11.473 0.484 11.645 0.548 11.774 0.675 c
+11.911 0.812 11.984 1.003 11.994 1.249 c
+12.612 1.249 l
+12.59 0.867 12.454 0.548 12.2 0.294 c
+11.944 0.047 11.631 -0.073 11.26 -0.073 c
+10.768 -0.073 10.392 0.077 10.128 0.382 c
+9.871 0.694 9.746 1.161 9.746 1.778 c
+9.746 2.219 l
+9.746 2.815 9.871 3.27 10.128 3.586 c
+10.392 3.898 10.768 4.056 11.26 4.056 c
+11.66 4.056 11.98 3.925 12.215 3.659 c
+12.458 3.403 12.59 3.057 12.612 2.616 c
+11.994 2.616 l
+11.973 2.91 11.899 3.131 11.774 3.278 c
+11.657 3.424 11.484 3.498 11.26 3.498 c
+10.966 3.498 10.749 3.399 10.613 3.204 c
+10.473 3.017 10.4 2.708 10.392 2.278 c
+10.392 1.764 l
+10.392 1.294 10.459 0.959 10.598 0.764 c
+10.745 0.577 10.966 0.484 11.26 0.484 c
+15.39 0.353 m
+15.173 0.066 14.861 -0.073 14.45 -0.073 c
+14.086 -0.073 13.81 0.047 13.627 0.294 c
+13.45 0.548 13.355 0.911 13.347 1.381 c
+13.347 3.983 l
+13.993 3.983 l
+13.993 1.44 l
+13.993 0.812 14.178 0.5 14.552 0.5 c
+14.953 0.5 15.229 0.675 15.375 1.028 c
+15.375 3.983 l
+16.023 3.983 l
+16.023 0 l
+15.405 0 l
+h
+18.654 3.366 m
+18.565 3.384 18.466 3.395 18.359 3.395 c
+18.025 3.395 17.79 3.212 17.654 2.851 c
+17.654 0 l
+17.008 0 l
+17.008 3.983 l
+17.639 3.983 l
+17.654 3.572 l
+17.831 3.895 18.073 4.056 18.389 4.056 c
+18.496 4.056 18.583 4.035 18.654 3.998 c
+h
+20.947 3.366 m
+20.858 3.384 20.759 3.395 20.653 3.395 c
+20.318 3.395 20.083 3.212 19.947 2.851 c
+19.947 0 l
+19.3 0 l
+19.3 3.983 l
+19.932 3.983 l
+19.947 3.572 l
+20.123 3.895 20.366 4.056 20.682 4.056 c
+20.788 4.056 20.877 4.035 20.947 3.998 c
+h
+22.946 -0.073 m
+22.446 -0.073 22.064 0.073 21.799 0.367 c
+21.534 0.661 21.402 1.095 21.402 1.675 c
+21.402 2.146 l
+21.402 2.741 21.527 3.208 21.785 3.542 c
+22.049 3.884 22.409 4.056 22.872 4.056 c
+23.331 4.056 23.674 3.902 23.901 3.601 c
+24.137 3.307 24.258 2.844 24.268 2.219 c
+24.268 1.793 l
+22.049 1.793 l
+22.049 1.705 l
+22.049 1.271 22.126 0.959 22.284 0.764 c
+22.45 0.577 22.681 0.484 22.975 0.484 c
+23.169 0.484 23.343 0.517 23.489 0.588 c
+23.636 0.665 23.772 0.783 23.901 0.941 c
+24.239 0.529 l
+23.952 0.124 23.522 -0.073 22.946 -0.073 c
+22.872 3.498 m
+22.597 3.498 22.394 3.403 22.269 3.219 c
+22.141 3.031 22.067 2.741 22.049 2.352 c
+23.622 2.352 l
+23.622 2.439 l
+23.599 2.822 23.534 3.09 23.416 3.248 c
+23.298 3.414 23.115 3.498 22.872 3.498 c
+25.665 3.983 m
+25.679 3.542 l
+25.933 3.884 26.257 4.056 26.65 4.056 c
+27.356 4.056 27.711 3.586 27.723 2.645 c
+27.723 0 l
+27.076 0 l
+27.076 2.616 l
+27.076 2.929 27.021 3.149 26.915 3.278 c
+26.804 3.403 26.65 3.469 26.444 3.469 c
+26.286 3.469 26.139 3.414 26.003 3.307 c
+25.874 3.197 25.772 3.061 25.694 2.896 c
+25.694 0 l
+25.047 0 l
+25.047 3.983 l
+h
+29.546 4.939 m
+29.546 3.983 l
+30.148 3.983 l
+30.148 3.454 l
+29.546 3.454 l
+29.546 0.985 l
+29.546 0.827 29.567 0.709 29.619 0.632 c
+29.677 0.551 29.766 0.515 29.883 0.515 c
+29.972 0.515 30.06 0.529 30.148 0.558 c
+30.148 0 l
+30.001 -0.048 29.847 -0.073 29.693 -0.073 c
+29.436 -0.073 29.241 0.018 29.105 0.205 c
+28.965 0.389 28.899 0.65 28.899 0.985 c
+28.899 3.454 l
+28.297 3.454 l
+28.297 3.983 l
+28.899 3.983 l
+28.899 4.939 l
+h
+34.014 0.484 m
+34.227 0.484 34.4 0.548 34.529 0.675 c
+34.664 0.812 34.738 1.003 34.749 1.249 c
+35.366 1.249 l
+35.345 0.867 35.208 0.548 34.955 0.294 c
+34.697 0.047 34.385 -0.073 34.014 -0.073 c
+33.521 -0.073 33.147 0.077 32.883 0.382 c
+32.625 0.694 32.5 1.161 32.5 1.778 c
+32.5 2.219 l
+32.5 2.815 32.625 3.27 32.883 3.586 c
+33.147 3.898 33.521 4.056 34.014 4.056 c
+34.415 4.056 34.734 3.925 34.969 3.659 c
+35.212 3.403 35.345 3.057 35.366 2.616 c
+34.749 2.616 l
+34.727 2.91 34.653 3.131 34.529 3.278 c
+34.411 3.424 34.238 3.498 34.014 3.498 c
+33.72 3.498 33.503 3.399 33.367 3.204 c
+33.228 3.017 33.154 2.708 33.147 2.278 c
+33.147 1.764 l
+33.147 1.294 33.213 0.959 33.353 0.764 c
+33.5 0.577 33.72 0.484 34.014 0.484 c
+35.983 2.175 m
+35.983 2.752 36.12 3.208 36.395 3.542 c
+36.678 3.884 37.049 4.056 37.512 4.056 c
+37.972 4.056 38.339 3.887 38.615 3.557 c
+38.898 3.233 39.044 2.786 39.056 2.219 c
+39.056 1.793 l
+39.056 1.223 38.913 0.768 38.63 0.426 c
+38.354 0.091 37.986 -0.073 37.527 -0.073 c
+37.064 -0.073 36.693 0.087 36.41 0.411 c
+36.135 0.742 35.991 1.182 35.983 1.734 c
+h
+36.631 1.793 m
+36.631 1.389 36.708 1.072 36.866 0.837 c
+37.031 0.602 37.251 0.484 37.527 0.484 c
+38.093 0.484 38.387 0.897 38.409 1.72 c
+38.409 2.175 l
+38.409 2.576 38.325 2.896 38.159 3.131 c
+38.001 3.374 37.785 3.498 37.512 3.498 c
+37.248 3.498 37.031 3.374 36.866 3.131 c
+36.708 2.896 36.631 2.576 36.631 2.175 c
+h
+40.511 3.983 m
+40.526 3.615 l
+40.768 3.91 41.088 4.056 41.481 4.056 c
+41.922 4.056 42.231 3.858 42.408 3.469 c
+42.661 3.858 43.01 4.056 43.451 4.056 c
+44.186 4.056 44.561 3.594 44.583 2.674 c
+44.583 0 l
+43.936 0 l
+43.936 2.616 l
+43.936 2.91 43.881 3.123 43.774 3.262 c
+43.675 3.399 43.503 3.469 43.26 3.469 c
+43.061 3.469 42.9 3.388 42.774 3.233 c
+42.657 3.087 42.587 2.896 42.568 2.66 c
+42.568 0 l
+41.907 0 l
+41.907 2.645 l
+41.907 3.193 41.687 3.469 41.246 3.469 c
+40.912 3.469 40.677 3.307 40.54 2.984 c
+40.54 0 l
+39.894 0 l
+39.894 3.983 l
+h
+46.171 3.983 m
+46.185 3.615 l
+46.427 3.91 46.747 4.056 47.14 4.056 c
+47.582 4.056 47.89 3.858 48.066 3.469 c
+48.32 3.858 48.669 4.056 49.11 4.056 c
+49.845 4.056 50.219 3.594 50.242 2.674 c
+50.242 0 l
+49.595 0 l
+49.595 2.616 l
+49.595 2.91 49.54 3.123 49.433 3.262 c
+49.334 3.399 49.161 3.469 48.918 3.469 c
+48.721 3.469 48.559 3.388 48.434 3.233 c
+48.316 3.087 48.247 2.896 48.228 2.66 c
+48.228 0 l
+47.567 0 l
+47.567 2.645 l
+47.567 3.193 47.346 3.469 46.905 3.469 c
+46.57 3.469 46.335 3.307 46.2 2.984 c
+46.2 0 l
+45.552 0 l
+45.552 3.983 l
+h
+51.918 0 -0.647 3.983 re
+51.962 5.026 m
+51.962 4.916 51.933 4.825 51.873 4.748 c
+51.815 4.677 51.719 4.644 51.594 4.644 c
+51.476 4.644 51.381 4.677 51.314 4.748 c
+51.256 4.825 51.227 4.916 51.227 5.026 c
+51.227 5.144 51.256 5.236 51.314 5.306 c
+51.381 5.383 51.476 5.423 51.594 5.423 c
+51.719 5.423 51.815 5.383 51.873 5.306 c
+51.933 5.225 51.962 5.134 51.962 5.026 c
+53.784 4.939 m
+53.784 3.983 l
+54.387 3.983 l
+54.387 3.454 l
+53.784 3.454 l
+53.784 0.985 l
+53.784 0.827 53.807 0.709 53.857 0.632 c
+53.917 0.551 54.005 0.515 54.123 0.515 c
+54.21 0.515 54.298 0.529 54.387 0.558 c
+54.387 0 l
+54.24 -0.048 54.086 -0.073 53.932 -0.073 c
+53.674 -0.073 53.479 0.018 53.344 0.205 c
+53.203 0.389 53.138 0.65 53.138 0.985 c
+53.138 3.454 l
+52.535 3.454 l
+52.535 3.983 l
+53.138 3.983 l
+53.138 4.939 l
+h
+55.195 0.353 m
+55.195 0.47 55.228 0.565 55.299 0.646 c
+55.364 0.723 55.467 0.764 55.607 0.764 c
+55.754 0.764 55.86 0.723 55.931 0.646 c
+56.008 0.565 56.048 0.47 56.048 0.353 c
+56.048 0.242 56.008 0.151 55.931 0.073 c
+55.86 -0.004 55.754 -0.044 55.607 -0.044 c
+55.467 -0.044 55.364 -0.004 55.299 0.073 c
+55.228 0.151 55.195 0.242 55.195 0.353 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 373.53 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 366.6908 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.993 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.596 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.279 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.22 l
+13.641 2.22 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.22 l
+15.728 2.22 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.455 14.552 -0.515 c
+14.589 -0.565 14.645 -0.61 14.714 -0.646 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.687 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.602 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.23 15.626 -1.249 c
+15.545 -1.261 15.468 -1.267 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.308 14.832 -1.308 c
+14.645 -1.308 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.025 13.788 -0.955 c
+13.729 -0.878 13.685 -0.801 13.656 -0.72 c
+13.627 -0.632 13.604 -0.544 13.597 -0.455 c
+13.586 -0.36 13.582 -0.264 13.582 -0.176 c
+h
+21.722 1.602 m
+21.178 1.602 l
+21.178 2.22 l
+21.766 2.22 l
+22.045 3.117 l
+22.618 3.117 l
+22.618 2.22 l
+23.853 2.22 l
+23.853 1.602 l
+22.618 1.602 l
+22.618 -0.103 l
+22.618 -0.324 l
+22.626 -0.393 22.647 -0.455 22.677 -0.515 c
+22.714 -0.565 22.769 -0.61 22.839 -0.646 c
+22.916 -0.676 23.03 -0.691 23.177 -0.691 c
+23.313 -0.691 23.449 -0.687 23.588 -0.676 c
+23.725 -0.658 23.857 -0.632 23.985 -0.602 c
+23.985 -1.205 l
+23.904 -1.216 23.827 -1.23 23.75 -1.249 c
+23.669 -1.261 23.592 -1.267 23.515 -1.278 c
+23.434 -1.286 23.346 -1.294 23.25 -1.294 c
+23.162 -1.301 23.063 -1.308 22.957 -1.308 c
+22.769 -1.308 22.608 -1.294 22.471 -1.264 c
+22.343 -1.228 22.229 -1.183 22.134 -1.132 c
+22.045 -1.084 21.972 -1.025 21.913 -0.955 c
+21.854 -0.878 21.81 -0.801 21.781 -0.72 c
+21.751 -0.632 21.729 -0.544 21.722 -0.455 c
+21.71 -0.36 21.707 -0.264 21.707 -0.176 c
+h
+26.136 -1.323 m
+25.966 -1.323 25.816 -1.301 25.679 -1.264 c
+25.551 -1.216 25.437 -1.147 25.342 -1.058 c
+25.253 -0.97 25.184 -0.864 25.136 -0.735 c
+25.084 -0.599 25.062 -0.448 25.062 -0.279 c
+25.062 -0.073 25.095 0.096 25.165 0.235 c
+25.232 0.382 25.327 0.493 25.444 0.574 c
+25.569 0.661 25.712 0.724 25.87 0.765 c
+26.036 0.801 26.213 0.827 26.4 0.838 c
+27.12 0.852 l
+27.12 1.029 l
+27.12 1.147 27.109 1.249 27.09 1.338 c
+27.069 1.426 27.036 1.492 26.988 1.544 c
+26.947 1.602 26.9 1.639 26.841 1.661 c
+26.782 1.679 26.716 1.69 26.649 1.69 c
+26.58 1.69 26.518 1.679 26.458 1.661 c
+26.408 1.65 26.36 1.625 26.312 1.588 c
+26.271 1.558 26.238 1.507 26.209 1.44 c
+26.186 1.382 26.172 1.301 26.165 1.205 c
+25.224 1.249 l
+25.253 1.397 25.297 1.532 25.356 1.661 c
+25.423 1.786 25.518 1.897 25.635 1.985 c
+25.753 2.08 25.893 2.153 26.061 2.205 c
+26.238 2.253 26.444 2.278 26.679 2.278 c
+27.12 2.278 27.451 2.168 27.678 1.955 c
+27.914 1.75 28.031 1.44 28.031 1.029 c
+28.031 -0.235 l
+28.031 -0.455 l
+28.039 -0.515 28.054 -0.569 28.075 -0.617 c
+28.094 -0.658 28.123 -0.691 28.164 -0.72 c
+28.201 -0.742 28.252 -0.75 28.311 -0.75 c
+28.376 -0.75 28.447 -0.746 28.517 -0.735 c
+28.517 -1.22 l
+28.457 -1.23 28.403 -1.242 28.355 -1.249 c
+28.314 -1.261 28.274 -1.267 28.237 -1.278 c
+28.197 -1.286 28.153 -1.294 28.105 -1.294 c
+28.054 -1.301 27.995 -1.308 27.929 -1.308 c
+27.701 -1.308 27.535 -1.257 27.429 -1.147 c
+27.318 -1.029 27.256 -0.864 27.237 -0.646 c
+27.223 -0.646 l
+27.154 -0.757 27.083 -0.852 27.017 -0.941 c
+26.947 -1.022 26.87 -1.087 26.782 -1.147 c
+26.693 -1.205 26.595 -1.249 26.488 -1.278 c
+26.389 -1.308 26.271 -1.323 26.136 -1.323 c
+27.12 0.353 m
+26.693 0.339 l
+26.595 0.339 26.503 0.331 26.414 0.324 c
+26.333 0.312 26.267 0.287 26.209 0.25 c
+26.15 0.21 26.098 0.15 26.061 0.073 c
+26.021 0.004 26.003 -0.088 26.003 -0.206 c
+26.003 -0.374 26.036 -0.496 26.106 -0.573 c
+26.172 -0.654 26.271 -0.691 26.4 -0.691 c
+26.506 -0.691 26.606 -0.669 26.693 -0.617 c
+26.789 -0.569 26.87 -0.507 26.929 -0.426 c
+26.995 -0.349 27.046 -0.261 27.076 -0.162 c
+27.106 -0.055 27.12 0.059 27.12 0.177 c
+h
+30.769 -2.66 m
+30.552 -2.66 30.361 -2.635 30.196 -2.587 c
+30.027 -2.547 29.887 -2.484 29.77 -2.396 c
+29.652 -2.315 29.552 -2.219 29.475 -2.102 c
+29.406 -1.984 29.358 -1.855 29.328 -1.72 c
+30.225 -1.617 l
+30.262 -1.753 30.331 -1.859 30.431 -1.94 c
+30.527 -2.028 30.651 -2.072 30.799 -2.072 c
+30.886 -2.072 30.967 -2.057 31.048 -2.028 c
+31.125 -1.999 31.195 -1.944 31.254 -1.866 c
+31.313 -1.797 31.357 -1.705 31.387 -1.587 c
+31.424 -1.469 31.445 -1.323 31.445 -1.147 c
+31.445 -0.955 l
+31.445 -0.889 31.449 -0.831 31.46 -0.779 c
+31.46 -0.588 l
+31.445 -0.588 l
+31.346 -0.816 31.202 -0.977 31.019 -1.072 c
+30.832 -1.172 30.626 -1.22 30.402 -1.22 c
+30.196 -1.22 30.012 -1.183 29.858 -1.103 c
+29.71 -1.014 29.582 -0.897 29.475 -0.75 c
+29.376 -0.596 29.303 -0.411 29.255 -0.206 c
+29.203 0.008 29.182 0.243 29.182 0.5 c
+29.182 0.771 29.203 1.018 29.255 1.235 c
+29.313 1.448 29.394 1.632 29.505 1.779 c
+29.612 1.933 29.743 2.051 29.901 2.132 c
+30.057 2.22 30.244 2.263 30.46 2.263 c
+30.556 2.263 30.655 2.253 30.755 2.234 c
+30.85 2.213 30.938 2.18 31.019 2.132 c
+31.107 2.08 31.185 2.018 31.254 1.941 c
+31.331 1.86 31.393 1.768 31.445 1.661 c
+31.46 1.661 l
+31.46 1.808 l
+31.468 1.867 31.474 1.918 31.474 1.97 c
+31.482 2.028 31.489 2.076 31.489 2.117 c
+31.497 2.165 31.507 2.198 31.519 2.22 c
+32.371 2.22 l
+32.361 2.139 32.349 2.028 32.342 1.881 c
+32.342 1.411 l
+32.342 -1.161 l
+32.342 -1.415 32.305 -1.635 32.239 -1.822 c
+32.17 -2.007 32.066 -2.161 31.931 -2.279 c
+31.79 -2.404 31.625 -2.499 31.43 -2.558 c
+31.233 -2.624 31.011 -2.66 30.769 -2.66 c
+31.46 0.53 m
+31.46 0.742 31.434 0.919 31.387 1.058 c
+31.346 1.205 31.291 1.323 31.225 1.411 c
+31.166 1.5 31.096 1.558 31.019 1.588 c
+30.938 1.625 30.861 1.646 30.784 1.646 c
+30.684 1.646 30.593 1.621 30.504 1.573 c
+30.424 1.532 30.358 1.463 30.298 1.367 c
+30.248 1.279 30.203 1.162 30.167 1.014 c
+30.137 0.875 30.122 0.706 30.122 0.5 c
+30.122 0.125 30.181 -0.154 30.298 -0.338 c
+30.416 -0.515 30.578 -0.602 30.784 -0.602 c
+30.85 -0.602 30.923 -0.588 31.004 -0.559 c
+31.092 -0.522 31.166 -0.463 31.225 -0.382 c
+31.291 -0.294 31.346 -0.176 31.387 -0.029 c
+31.434 0.118 31.46 0.301 31.46 0.53 c
+37.953 0.838 1.866 -0.794 re
+37.953 0.044 m
+43.601 -1.264 m
+43.59 -1.246 43.579 -1.216 43.572 -1.176 c
+43.572 -1.128 43.565 -1.08 43.557 -1.029 c
+43.557 -0.97 43.55 -0.912 43.542 -0.852 c
+43.542 -0.691 l
+43.425 -0.926 43.282 -1.095 43.116 -1.19 c
+42.948 -1.278 42.749 -1.323 42.514 -1.323 c
+42.315 -1.323 42.139 -1.278 41.984 -1.19 c
+41.826 -1.103 41.695 -0.981 41.587 -0.823 c
+41.488 -0.658 41.411 -0.467 41.352 -0.249 c
+41.301 -0.037 41.279 0.206 41.279 0.47 c
+41.279 0.735 41.301 0.974 41.352 1.191 c
+41.411 1.415 41.488 1.606 41.587 1.764 c
+41.695 1.918 41.826 2.043 41.984 2.132 c
+42.15 2.227 42.341 2.278 42.558 2.278 c
+42.653 2.278 42.749 2.263 42.837 2.234 c
+42.933 2.213 43.028 2.18 43.116 2.132 c
+43.204 2.08 43.282 2.018 43.351 1.941 c
+43.428 1.86 43.491 1.768 43.542 1.661 c
+43.542 1.75 l
+43.542 1.897 l
+43.542 2.058 l
+43.542 2.234 l
+43.542 3.514 l
+44.44 3.514 l
+44.44 -0.5 l
+44.44 -0.676 44.442 -0.834 44.454 -0.97 c
+44.461 -1.099 44.469 -1.198 44.469 -1.264 c
+h
+43.557 0.485 m
+43.557 0.721 43.532 0.912 43.484 1.058 c
+43.443 1.213 43.388 1.338 43.322 1.426 c
+43.264 1.515 43.193 1.573 43.116 1.602 c
+43.035 1.639 42.958 1.661 42.881 1.661 c
+42.782 1.661 42.69 1.635 42.601 1.588 c
+42.521 1.547 42.455 1.477 42.396 1.382 c
+42.345 1.282 42.3 1.162 42.264 1.014 c
+42.234 0.867 42.219 0.684 42.219 0.47 c
+42.219 0.077 42.271 -0.216 42.381 -0.411 c
+42.499 -0.61 42.661 -0.706 42.867 -0.706 c
+42.944 -0.706 43.021 -0.687 43.102 -0.646 c
+43.179 -0.61 43.252 -0.544 43.322 -0.455 c
+43.388 -0.367 43.443 -0.246 43.484 -0.088 c
+43.532 0.066 43.557 0.258 43.557 0.485 c
+50.297 -2.631 m
+50.297 3.514 l
+52.222 3.514 l
+52.222 2.896 l
+51.149 2.896 l
+51.149 -2.013 l
+52.222 -2.013 l
+52.222 -2.631 l
+h
+55.798 -1.264 m
+55.798 0.721 l
+55.798 1.022 55.754 1.242 55.665 1.382 c
+55.585 1.529 55.449 1.602 55.254 1.602 c
+55.143 1.602 55.041 1.577 54.946 1.529 c
+54.857 1.477 54.776 1.411 54.711 1.323 c
+54.651 1.235 54.6 1.124 54.563 1 c
+54.534 0.882 54.519 0.75 54.519 0.603 c
+54.519 -1.264 l
+53.608 -1.264 l
+53.608 1.44 l
+53.608 1.661 l
+53.608 1.75 53.6 1.827 53.593 1.897 c
+53.593 2.087 l
+53.593 2.22 l
+54.445 2.22 l
+54.453 2.19 54.46 2.146 54.46 2.087 c
+54.46 1.897 l
+54.468 1.827 54.475 1.756 54.475 1.69 c
+54.482 1.621 54.49 1.565 54.49 1.529 c
+54.505 1.529 l
+54.622 1.793 54.773 1.985 54.96 2.103 c
+55.143 2.22 55.364 2.278 55.621 2.278 c
+55.806 2.278 55.966 2.249 56.107 2.19 c
+56.242 2.132 56.357 2.043 56.444 1.926 c
+56.533 1.808 56.595 1.665 56.635 1.5 c
+56.683 1.341 56.71 1.154 56.71 0.941 c
+56.71 -1.264 l
+h
+58.628 -1.323 m
+58.458 -1.323 58.308 -1.301 58.172 -1.264 c
+58.044 -1.216 57.929 -1.147 57.834 -1.058 c
+57.745 -0.97 57.676 -0.864 57.628 -0.735 c
+57.576 -0.599 57.554 -0.448 57.554 -0.279 c
+57.554 -0.073 57.587 0.096 57.657 0.235 c
+57.724 0.382 57.819 0.493 57.936 0.574 c
+58.061 0.661 58.204 0.724 58.363 0.765 c
+58.528 0.801 58.705 0.827 58.892 0.838 c
+59.612 0.852 l
+59.612 1.029 l
+59.612 1.147 59.601 1.249 59.583 1.338 c
+59.561 1.426 59.528 1.492 59.48 1.544 c
+59.44 1.602 59.392 1.639 59.333 1.661 c
+59.274 1.679 59.208 1.69 59.141 1.69 c
+59.072 1.69 59.01 1.679 58.951 1.661 c
+58.9 1.65 58.852 1.625 58.804 1.588 c
+58.763 1.558 58.73 1.507 58.701 1.44 c
+58.679 1.382 58.664 1.301 58.657 1.205 c
+57.716 1.249 l
+57.745 1.397 57.79 1.532 57.848 1.661 c
+57.915 1.786 58.01 1.897 58.127 1.985 c
+58.245 2.08 58.385 2.153 58.554 2.205 c
+58.73 2.253 58.936 2.278 59.172 2.278 c
+59.612 2.278 59.943 2.168 60.171 1.955 c
+60.406 1.75 60.523 1.44 60.523 1.029 c
+60.523 -0.235 l
+60.523 -0.455 l
+60.531 -0.515 60.546 -0.569 60.568 -0.617 c
+60.586 -0.658 60.616 -0.691 60.656 -0.72 c
+60.693 -0.742 60.744 -0.75 60.803 -0.75 c
+60.869 -0.75 60.939 -0.746 61.009 -0.735 c
+61.009 -1.22 l
+60.95 -1.23 60.895 -1.242 60.847 -1.249 c
+60.807 -1.261 60.766 -1.267 60.729 -1.278 c
+60.689 -1.286 60.645 -1.294 60.597 -1.294 c
+60.546 -1.301 60.487 -1.308 60.421 -1.308 c
+60.193 -1.308 60.028 -1.257 59.921 -1.147 c
+59.81 -1.029 59.748 -0.864 59.729 -0.646 c
+59.715 -0.646 l
+59.646 -0.757 59.575 -0.852 59.509 -0.941 c
+59.44 -1.022 59.363 -1.087 59.274 -1.147 c
+59.186 -1.205 59.087 -1.249 58.981 -1.278 c
+58.881 -1.308 58.763 -1.323 58.628 -1.323 c
+59.612 0.353 m
+59.186 0.339 l
+59.087 0.339 58.995 0.331 58.906 0.324 c
+58.826 0.312 58.76 0.287 58.701 0.25 c
+58.642 0.21 58.591 0.15 58.554 0.073 c
+58.514 0.004 58.495 -0.088 58.495 -0.206 c
+58.495 -0.374 58.528 -0.496 58.598 -0.573 c
+58.664 -0.654 58.763 -0.691 58.892 -0.691 c
+58.998 -0.691 59.098 -0.669 59.186 -0.617 c
+59.282 -0.569 59.363 -0.507 59.421 -0.426 c
+59.488 -0.349 59.538 -0.261 59.569 -0.162 c
+59.598 -0.055 59.612 0.059 59.612 0.177 c
+h
+62.894 -1.264 m
+62.894 0.852 l
+62.894 1.018 62.887 1.154 62.879 1.264 c
+62.868 1.371 62.85 1.455 62.821 1.515 c
+62.798 1.58 62.769 1.632 62.732 1.661 c
+62.703 1.69 62.663 1.706 62.615 1.706 c
+62.555 1.706 62.501 1.675 62.453 1.617 c
+62.412 1.565 62.379 1.492 62.35 1.397 c
+62.32 1.309 62.295 1.195 62.277 1.058 c
+62.266 0.919 62.262 0.769 62.262 0.603 c
+62.262 -1.264 l
+61.512 -1.264 l
+61.512 1.47 l
+61.512 1.706 l
+61.512 1.926 l
+61.512 2.003 61.505 2.065 61.497 2.117 c
+61.497 2.22 l
+62.173 2.22 l
+62.173 2.132 l
+62.173 1.985 l
+62.181 1.926 62.189 1.867 62.189 1.808 c
+62.189 1.646 l
+62.203 1.646 l
+62.221 1.735 62.251 1.812 62.291 1.881 c
+62.328 1.959 62.372 2.028 62.424 2.087 c
+62.482 2.146 62.549 2.19 62.629 2.22 c
+62.707 2.257 62.794 2.278 62.894 2.278 c
+63.078 2.278 63.218 2.223 63.305 2.117 c
+63.401 2.018 63.471 1.86 63.511 1.646 c
+63.526 1.646 l
+63.563 1.742 63.603 1.83 63.644 1.911 c
+63.691 1.988 63.746 2.051 63.805 2.103 c
+63.864 2.161 63.93 2.205 64.011 2.234 c
+64.088 2.263 64.176 2.278 64.275 2.278 c
+64.411 2.278 64.525 2.253 64.614 2.205 c
+64.702 2.153 64.768 2.08 64.82 1.985 c
+64.878 1.885 64.915 1.756 64.937 1.602 c
+64.967 1.455 64.981 1.272 64.981 1.058 c
+64.981 -1.264 l
+64.261 -1.264 l
+64.261 0.852 l
+64.261 1.018 64.253 1.154 64.246 1.264 c
+64.236 1.371 64.217 1.455 64.188 1.515 c
+64.165 1.58 64.136 1.632 64.099 1.661 c
+64.07 1.69 64.03 1.706 63.982 1.706 c
+63.864 1.706 63.768 1.617 63.702 1.44 c
+63.644 1.272 63.614 1.014 63.614 0.661 c
+63.614 -1.264 l
+h
+67.351 -1.323 m
+67.094 -1.323 66.866 -1.286 66.661 -1.22 c
+66.455 -1.143 66.278 -1.029 66.131 -0.882 c
+65.984 -0.727 65.867 -0.536 65.778 -0.309 c
+65.697 -0.085 65.661 0.181 65.661 0.485 c
+65.661 0.816 65.705 1.095 65.793 1.323 c
+65.888 1.558 66.017 1.742 66.175 1.881 c
+66.341 2.018 66.528 2.117 66.734 2.176 c
+66.94 2.242 67.149 2.278 67.366 2.278 c
+67.638 2.278 67.873 2.227 68.072 2.132 c
+68.277 2.043 68.443 1.911 68.571 1.735 c
+68.707 1.565 68.807 1.359 68.865 1.118 c
+68.932 0.882 68.968 0.617 68.968 0.324 c
+68.968 0.309 l
+66.602 0.309 l
+66.602 0.162 66.617 0.023 66.646 -0.103 c
+66.682 -0.231 66.738 -0.345 66.808 -0.441 c
+66.873 -0.529 66.958 -0.599 67.058 -0.646 c
+67.153 -0.698 67.267 -0.72 67.396 -0.72 c
+67.55 -0.72 67.689 -0.687 67.807 -0.617 c
+67.932 -0.551 68.02 -0.448 68.072 -0.309 c
+68.909 -0.382 l
+68.88 -0.482 68.824 -0.588 68.747 -0.706 c
+68.666 -0.816 68.564 -0.918 68.439 -1.014 c
+68.321 -1.103 68.167 -1.176 67.983 -1.234 c
+67.807 -1.294 67.594 -1.323 67.351 -1.323 c
+67.351 1.706 m
+67.263 1.706 67.175 1.69 67.087 1.661 c
+66.999 1.632 66.918 1.58 66.852 1.515 c
+66.782 1.444 66.723 1.357 66.675 1.249 c
+66.634 1.139 66.617 1.014 66.617 0.867 c
+68.086 0.867 l
+68.086 1.004 68.061 1.124 68.013 1.235 c
+67.972 1.341 67.918 1.43 67.851 1.5 c
+67.793 1.565 67.719 1.617 67.631 1.646 c
+67.542 1.683 67.447 1.706 67.351 1.706 c
+70.221 -2.631 m
+70.221 -2.013 l
+71.294 -2.013 l
+71.294 2.896 l
+70.221 2.896 l
+70.221 3.514 l
+72.147 3.514 l
+72.147 -2.631 l
+h
+f
+Q
+q 1 0 0 1 411.2216 352.6973 cm
+0 0 m
+-0.941 0 l
+-0.941 -2.161 l
+-1.617 -2.161 l
+-1.617 3.19 l
+-0.118 3.19 l
+0.411 3.19 0.808 3.05 1.072 2.778 c
+1.344 2.502 1.484 2.105 1.484 1.588 c
+1.484 1.253 1.411 0.963 1.263 0.721 c
+1.117 0.474 0.911 0.287 0.646 0.162 c
+1.675 -2.117 l
+1.675 -2.161 l
+0.955 -2.161 l
+h
+-0.941 0.588 m
+-0.118 0.588 l
+0.165 0.588 0.389 0.676 0.558 0.852 c
+0.723 1.037 0.808 1.282 0.808 1.588 c
+0.808 2.271 0.492 2.616 -0.133 2.616 c
+-0.941 2.616 l
+h
+3.719 -2.234 m
+3.218 -2.234 2.836 -2.088 2.572 -1.793 c
+2.308 -1.5 2.175 -1.066 2.175 -0.484 c
+2.175 -0.014 l
+2.175 0.58 2.3 1.047 2.557 1.382 c
+2.822 1.723 3.182 1.897 3.645 1.897 c
+4.104 1.897 4.446 1.742 4.674 1.44 c
+4.909 1.147 5.03 0.684 5.041 0.059 c
+5.041 -0.367 l
+2.822 -0.367 l
+2.822 -0.455 l
+2.822 -0.889 2.899 -1.201 3.057 -1.396 c
+3.222 -1.583 3.453 -1.675 3.748 -1.675 c
+3.943 -1.675 4.116 -1.643 4.262 -1.573 c
+4.409 -1.496 4.546 -1.378 4.674 -1.22 c
+5.012 -1.631 l
+4.725 -2.036 4.295 -2.234 3.719 -2.234 c
+3.645 1.338 m
+3.37 1.338 3.167 1.242 3.042 1.058 c
+2.913 0.871 2.84 0.58 2.822 0.191 c
+4.394 0.191 l
+4.394 0.279 l
+4.373 0.661 4.307 0.929 4.189 1.087 c
+4.072 1.253 3.887 1.338 3.645 1.338 c
+6.437 1.823 m
+6.453 1.455 l
+6.695 1.75 7.015 1.897 7.408 1.897 c
+7.849 1.897 8.157 1.698 8.334 1.309 c
+8.588 1.698 8.937 1.897 9.377 1.897 c
+10.113 1.897 10.487 1.434 10.509 0.515 c
+10.509 -2.161 l
+9.863 -2.161 l
+9.863 0.456 l
+9.863 0.75 9.807 0.963 9.701 1.103 c
+9.602 1.239 9.429 1.309 9.186 1.309 c
+8.988 1.309 8.826 1.228 8.702 1.073 c
+8.584 0.927 8.514 0.735 8.496 0.5 c
+8.496 -2.161 l
+7.834 -2.161 l
+7.834 0.485 l
+7.834 1.033 7.613 1.309 7.173 1.309 c
+6.838 1.309 6.603 1.147 6.468 0.823 c
+6.468 -2.161 l
+5.82 -2.161 l
+5.82 1.823 l
+h
+11.347 0.015 m
+11.347 0.592 11.484 1.047 11.759 1.382 c
+12.041 1.723 12.413 1.897 12.876 1.897 c
+13.335 1.897 13.703 1.727 13.978 1.397 c
+14.262 1.073 14.408 0.625 14.42 0.059 c
+14.42 -0.367 l
+14.42 -0.937 14.276 -1.392 13.993 -1.735 c
+13.718 -2.069 13.35 -2.234 12.891 -2.234 c
+12.428 -2.234 12.057 -2.072 11.773 -1.749 c
+11.498 -1.419 11.355 -0.977 11.347 -0.426 c
+h
+11.994 -0.367 m
+11.994 -0.771 12.072 -1.087 12.23 -1.323 c
+12.394 -1.558 12.615 -1.675 12.891 -1.675 c
+13.456 -1.675 13.751 -1.264 13.772 -0.441 c
+13.772 0.015 l
+13.772 0.416 13.688 0.735 13.523 0.97 c
+13.365 1.213 13.148 1.338 12.876 1.338 c
+12.612 1.338 12.394 1.213 12.23 0.97 c
+12.072 0.735 11.994 0.416 11.994 0.015 c
+h
+16.33 -1.176 m
+17.08 1.823 l
+17.741 1.823 l
+16.565 -2.161 l
+16.08 -2.161 l
+14.89 1.823 l
+15.551 1.823 l
+h
+19.799 -2.234 m
+19.299 -2.234 18.917 -2.088 18.653 -1.793 c
+18.388 -1.5 18.256 -1.066 18.256 -0.484 c
+18.256 -0.014 l
+18.256 0.58 18.381 1.047 18.638 1.382 c
+18.902 1.723 19.263 1.897 19.725 1.897 c
+20.185 1.897 20.527 1.742 20.755 1.44 c
+20.99 1.147 21.111 0.684 21.122 0.059 c
+21.122 -0.367 l
+18.902 -0.367 l
+18.902 -0.455 l
+18.902 -0.889 18.979 -1.201 19.137 -1.396 c
+19.303 -1.583 19.534 -1.675 19.829 -1.675 c
+20.024 -1.675 20.196 -1.643 20.343 -1.573 c
+20.49 -1.496 20.626 -1.378 20.755 -1.22 c
+21.092 -1.631 l
+20.806 -2.036 20.376 -2.234 19.799 -2.234 c
+19.725 1.338 m
+19.45 1.338 19.248 1.242 19.123 1.058 c
+18.994 0.871 18.921 0.58 18.902 0.191 c
+20.475 0.191 l
+20.475 0.279 l
+20.454 0.661 20.387 0.929 20.269 1.087 c
+20.152 1.253 19.968 1.338 19.725 1.338 c
+25.664 -2.161 m
+25.624 -2.072 25.598 -1.926 25.591 -1.72 c
+25.356 -2.065 25.061 -2.234 24.709 -2.234 c
+24.345 -2.234 24.062 -2.138 23.856 -1.94 c
+23.658 -1.735 23.562 -1.448 23.562 -1.072 c
+23.562 -0.673 23.698 -0.353 23.974 -0.118 c
+24.246 0.125 24.62 0.25 25.09 0.25 c
+25.576 0.25 l
+25.576 0.676 l
+25.576 0.912 25.521 1.077 25.414 1.176 c
+25.304 1.282 25.142 1.338 24.93 1.338 c
+24.731 1.338 24.569 1.279 24.444 1.162 c
+24.327 1.044 24.267 0.896 24.267 0.721 c
+23.621 0.721 l
+23.621 0.915 23.679 1.106 23.797 1.294 c
+23.922 1.477 24.084 1.625 24.282 1.735 c
+24.488 1.841 24.716 1.897 24.973 1.897 c
+25.374 1.897 25.678 1.793 25.884 1.588 c
+26.098 1.382 26.212 1.087 26.223 0.706 c
+26.223 -1.308 l
+26.223 -1.613 26.26 -1.878 26.341 -2.102 c
+26.341 -2.161 l
+h
+24.797 -1.646 m
+24.963 -1.646 25.113 -1.602 25.252 -1.514 c
+25.4 -1.425 25.506 -1.315 25.576 -1.176 c
+25.576 -0.235 l
+25.208 -0.235 l
+24.893 -0.235 24.65 -0.305 24.473 -0.441 c
+24.297 -0.569 24.209 -0.757 24.209 -0.999 c
+24.209 -1.228 24.253 -1.392 24.342 -1.5 c
+24.429 -1.598 24.581 -1.646 24.797 -1.646 c
+29.794 2.778 m
+29.794 1.823 l
+30.397 1.823 l
+30.397 1.294 l
+29.794 1.294 l
+29.794 -1.176 l
+29.794 -1.334 29.817 -1.452 29.868 -1.529 c
+29.927 -1.61 30.015 -1.646 30.133 -1.646 c
+30.221 -1.646 30.309 -1.631 30.397 -1.602 c
+30.397 -2.161 l
+30.25 -2.208 30.096 -2.234 29.942 -2.234 c
+29.684 -2.234 29.489 -2.142 29.354 -1.955 c
+29.214 -1.771 29.148 -1.51 29.148 -1.176 c
+29.148 1.294 l
+28.545 1.294 l
+28.545 1.823 l
+29.148 1.823 l
+29.148 2.778 l
+h
+33.204 -2.161 m
+33.164 -2.072 33.138 -1.926 33.131 -1.72 c
+32.896 -2.065 32.602 -2.234 32.249 -2.234 c
+31.885 -2.234 31.602 -2.138 31.397 -1.94 c
+31.199 -1.735 31.103 -1.448 31.103 -1.072 c
+31.103 -0.673 31.239 -0.353 31.515 -0.118 c
+31.787 0.125 32.161 0.25 32.631 0.25 c
+33.117 0.25 l
+33.117 0.676 l
+33.117 0.912 33.061 1.077 32.955 1.176 c
+32.845 1.282 32.683 1.338 32.47 1.338 c
+32.271 1.338 32.109 1.279 31.985 1.162 c
+31.868 1.044 31.808 0.896 31.808 0.721 c
+31.162 0.721 l
+31.162 0.915 31.22 1.106 31.338 1.294 c
+31.463 1.477 31.625 1.625 31.823 1.735 c
+32.028 1.841 32.257 1.897 32.514 1.897 c
+32.915 1.897 33.219 1.793 33.425 1.588 c
+33.638 1.382 33.753 1.087 33.763 0.706 c
+33.763 -1.308 l
+33.763 -1.613 33.8 -1.878 33.881 -2.102 c
+33.881 -2.161 l
+h
+32.338 -1.646 m
+32.503 -1.646 32.654 -1.602 32.793 -1.514 c
+32.94 -1.425 33.047 -1.315 33.117 -1.176 c
+33.117 -0.235 l
+32.749 -0.235 l
+32.433 -0.235 32.19 -0.305 32.014 -0.441 c
+31.837 -0.569 31.75 -0.757 31.75 -0.999 c
+31.75 -1.228 31.793 -1.392 31.882 -1.5 c
+31.97 -1.598 32.121 -1.646 32.338 -1.646 c
+34.63 0.015 m
+34.63 0.632 34.741 1.095 34.968 1.411 c
+35.193 1.735 35.527 1.897 35.968 1.897 c
+36.369 1.897 36.674 1.72 36.88 1.367 c
+36.924 1.823 l
+37.512 1.823 l
+37.512 -2.205 l
+37.512 -2.693 37.383 -3.072 37.129 -3.337 c
+36.872 -3.601 36.52 -3.734 36.071 -3.734 c
+35.872 -3.734 35.652 -3.682 35.409 -3.586 c
+35.164 -3.487 34.983 -3.366 34.866 -3.219 c
+35.13 -2.778 l
+35.395 -3.042 35.693 -3.175 36.027 -3.175 c
+36.564 -3.175 36.839 -2.881 36.85 -2.293 c
+36.85 -1.764 l
+36.645 -2.08 36.343 -2.234 35.953 -2.234 c
+35.542 -2.234 35.218 -2.084 34.983 -1.778 c
+34.756 -1.466 34.638 -1.014 34.63 -0.426 c
+h
+35.292 -0.367 m
+35.292 -0.808 35.355 -1.139 35.483 -1.352 c
+35.608 -1.558 35.825 -1.66 36.13 -1.66 c
+36.454 -1.66 36.692 -1.496 36.85 -1.161 c
+36.85 0.823 l
+36.681 1.147 36.442 1.309 36.13 1.309 c
+35.836 1.309 35.619 1.205 35.483 1 c
+35.355 0.794 35.292 0.47 35.292 0.029 c
+h
+40.481 -2.161 m
+40.481 1.294 l
+39.951 1.294 l
+39.951 1.823 l
+40.481 1.823 l
+40.481 2.278 l
+40.481 2.679 40.576 2.992 40.774 3.219 c
+40.98 3.443 41.26 3.557 41.613 3.557 c
+41.749 3.557 41.881 3.535 42.01 3.499 c
+41.98 2.955 l
+41.881 2.973 41.782 2.984 41.686 2.984 c
+41.312 2.984 41.127 2.72 41.127 2.19 c
+41.127 1.823 l
+41.804 1.823 l
+41.804 1.294 l
+41.127 1.294 l
+41.127 -2.161 l
+h
+44.215 1.205 m
+44.126 1.224 44.027 1.235 43.92 1.235 c
+43.586 1.235 43.351 1.051 43.215 0.691 c
+43.215 -2.161 l
+42.568 -2.161 l
+42.568 1.823 l
+43.2 1.823 l
+43.215 1.411 l
+43.392 1.735 43.633 1.897 43.949 1.897 c
+44.057 1.897 44.144 1.874 44.215 1.837 c
+h
+44.655 0.015 m
+44.655 0.592 44.791 1.047 45.067 1.382 c
+45.35 1.723 45.721 1.897 46.184 1.897 c
+46.644 1.897 47.011 1.727 47.286 1.397 c
+47.569 1.073 47.716 0.625 47.727 0.059 c
+47.727 -0.367 l
+47.727 -0.937 47.584 -1.392 47.301 -1.735 c
+47.026 -2.069 46.658 -2.234 46.199 -2.234 c
+45.736 -2.234 45.364 -2.072 45.081 -1.749 c
+44.806 -1.419 44.663 -0.977 44.655 -0.426 c
+h
+45.302 -0.367 m
+45.302 -0.771 45.379 -1.087 45.537 -1.323 c
+45.703 -1.558 45.923 -1.675 46.199 -1.675 c
+46.765 -1.675 47.059 -1.264 47.08 -0.441 c
+47.08 0.015 l
+47.08 0.416 46.996 0.735 46.831 0.97 c
+46.673 1.213 46.456 1.338 46.184 1.338 c
+45.919 1.338 45.703 1.213 45.537 0.97 c
+45.379 0.735 45.302 0.416 45.302 0.015 c
+h
+49.183 1.823 m
+49.197 1.455 l
+49.44 1.75 49.759 1.897 50.153 1.897 c
+50.594 1.897 50.902 1.698 51.078 1.309 c
+51.332 1.698 51.681 1.897 52.123 1.897 c
+52.857 1.897 53.233 1.434 53.254 0.515 c
+53.254 -2.161 l
+52.607 -2.161 l
+52.607 0.456 l
+52.607 0.75 52.553 0.963 52.446 1.103 c
+52.346 1.239 52.174 1.309 51.932 1.309 c
+51.733 1.309 51.571 1.228 51.446 1.073 c
+51.329 0.927 51.259 0.735 51.24 0.5 c
+51.24 -2.161 l
+50.579 -2.161 l
+50.579 0.485 l
+50.579 1.033 50.359 1.309 49.918 1.309 c
+49.584 1.309 49.348 1.147 49.212 0.823 c
+49.212 -2.161 l
+48.566 -2.161 l
+48.566 1.823 l
+h
+57.987 -2.161 m
+57.947 -2.072 57.921 -1.926 57.914 -1.72 c
+57.679 -2.065 57.385 -2.234 57.032 -2.234 c
+56.668 -2.234 56.385 -2.138 56.179 -1.94 c
+55.981 -1.735 55.886 -1.448 55.886 -1.072 c
+55.886 -0.673 56.021 -0.353 56.297 -0.118 c
+56.569 0.125 56.944 0.25 57.414 0.25 c
+57.899 0.25 l
+57.899 0.676 l
+57.899 0.912 57.844 1.077 57.738 1.176 c
+57.627 1.282 57.466 1.338 57.252 1.338 c
+57.054 1.338 56.892 1.279 56.767 1.162 c
+56.649 1.044 56.591 0.896 56.591 0.721 c
+55.944 0.721 l
+55.944 0.915 56.003 1.106 56.121 1.294 c
+56.246 1.477 56.408 1.625 56.605 1.735 c
+56.811 1.841 57.039 1.897 57.297 1.897 c
+57.697 1.897 58.002 1.793 58.208 1.588 c
+58.421 1.382 58.535 1.087 58.546 0.706 c
+58.546 -1.308 l
+58.546 -1.613 58.583 -1.878 58.664 -2.102 c
+58.664 -2.161 l
+h
+57.12 -1.646 m
+57.285 -1.646 57.436 -1.602 57.576 -1.514 c
+57.723 -1.425 57.829 -1.315 57.899 -1.176 c
+57.899 -0.235 l
+57.532 -0.235 l
+57.216 -0.235 56.973 -0.305 56.797 -0.441 c
+56.62 -0.569 56.532 -0.757 56.532 -0.999 c
+56.532 -1.228 56.576 -1.392 56.664 -1.5 c
+56.753 -1.598 56.903 -1.646 57.12 -1.646 c
+61.971 -2.161 -0.647 5.644 re
+62.853 0.015 m
+62.853 0.592 62.989 1.047 63.265 1.382 c
+63.547 1.723 63.919 1.897 64.381 1.897 c
+64.84 1.897 65.208 1.727 65.484 1.397 c
+65.767 1.073 65.914 0.625 65.925 0.059 c
+65.925 -0.367 l
+65.925 -0.937 65.781 -1.392 65.499 -1.735 c
+65.223 -2.069 64.855 -2.234 64.396 -2.234 c
+63.934 -2.234 63.562 -2.072 63.279 -1.749 c
+63.003 -1.419 62.86 -0.977 62.853 -0.426 c
+h
+63.5 -0.367 m
+63.5 -0.771 63.577 -1.087 63.735 -1.323 c
+63.9 -1.558 64.121 -1.675 64.396 -1.675 c
+64.962 -1.675 65.256 -1.264 65.278 -0.441 c
+65.278 0.015 l
+65.278 0.416 65.193 0.735 65.029 0.97 c
+64.871 1.213 64.653 1.338 64.381 1.338 c
+64.117 1.338 63.9 1.213 63.735 0.97 c
+63.577 0.735 63.5 0.416 63.5 0.015 c
+h
+68.144 -1.675 m
+68.358 -1.675 68.53 -1.613 68.659 -1.484 c
+68.795 -1.348 68.869 -1.157 68.879 -0.912 c
+69.497 -0.912 l
+69.475 -1.294 69.339 -1.613 69.085 -1.866 c
+68.828 -2.113 68.516 -2.234 68.144 -2.234 c
+67.652 -2.234 67.277 -2.084 67.013 -1.778 c
+66.756 -1.466 66.631 -0.999 66.631 -0.382 c
+66.631 0.059 l
+66.631 0.654 66.756 1.11 67.013 1.426 c
+67.277 1.738 67.652 1.897 68.144 1.897 c
+68.545 1.897 68.865 1.764 69.1 1.5 c
+69.343 1.242 69.475 0.896 69.497 0.456 c
+68.879 0.456 l
+68.857 0.75 68.784 0.97 68.659 1.118 c
+68.541 1.264 68.368 1.338 68.144 1.338 c
+67.851 1.338 67.634 1.239 67.498 1.044 c
+67.358 0.856 67.284 0.547 67.277 0.118 c
+67.277 -0.397 l
+67.277 -0.867 67.344 -1.201 67.483 -1.396 c
+67.63 -1.583 67.851 -1.675 68.144 -1.675 c
+72.289 -2.161 m
+72.249 -2.072 72.223 -1.926 72.216 -1.72 c
+71.981 -2.065 71.687 -2.234 71.334 -2.234 c
+70.97 -2.234 70.687 -2.138 70.481 -1.94 c
+70.284 -1.735 70.188 -1.448 70.188 -1.072 c
+70.188 -0.673 70.323 -0.353 70.599 -0.118 c
+70.872 0.125 71.246 0.25 71.716 0.25 c
+72.202 0.25 l
+72.202 0.676 l
+72.202 0.912 72.146 1.077 72.04 1.176 c
+71.93 1.282 71.768 1.338 71.555 1.338 c
+71.356 1.338 71.194 1.279 71.069 1.162 c
+70.952 1.044 70.893 0.896 70.893 0.721 c
+70.246 0.721 l
+70.246 0.915 70.305 1.106 70.423 1.294 c
+70.548 1.477 70.71 1.625 70.908 1.735 c
+71.113 1.841 71.342 1.897 71.599 1.897 c
+71.999 1.897 72.304 1.793 72.51 1.588 c
+72.723 1.382 72.837 1.087 72.848 0.706 c
+72.848 -1.308 l
+72.848 -1.613 72.885 -1.878 72.966 -2.102 c
+72.966 -2.161 l
+h
+71.422 -1.646 m
+71.587 -1.646 71.738 -1.602 71.878 -1.514 c
+72.025 -1.425 72.131 -1.315 72.202 -1.176 c
+72.202 -0.235 l
+71.834 -0.235 l
+71.518 -0.235 71.275 -0.305 71.099 -0.441 c
+70.922 -0.569 70.834 -0.757 70.834 -0.999 c
+70.834 -1.228 70.878 -1.392 70.967 -1.5 c
+71.055 -1.598 71.206 -1.646 71.422 -1.646 c
+74.554 -2.161 -0.647 5.644 re
+78.934 1.205 m
+78.845 1.224 78.746 1.235 78.639 1.235 c
+78.305 1.235 78.07 1.051 77.934 0.691 c
+77.934 -2.161 l
+77.287 -2.161 l
+77.287 1.823 l
+77.92 1.823 l
+77.934 1.411 l
+78.111 1.735 78.353 1.897 78.669 1.897 c
+78.776 1.897 78.864 1.874 78.934 1.837 c
+h
+80.933 -2.234 m
+80.433 -2.234 80.05 -2.088 79.786 -1.793 c
+79.522 -1.5 79.389 -1.066 79.389 -0.484 c
+79.389 -0.014 l
+79.389 0.58 79.514 1.047 79.771 1.382 c
+80.036 1.723 80.397 1.897 80.859 1.897 c
+81.318 1.897 81.66 1.742 81.888 1.44 c
+82.123 1.147 82.244 0.684 82.255 0.059 c
+82.255 -0.367 l
+80.036 -0.367 l
+80.036 -0.455 l
+80.036 -0.889 80.113 -1.201 80.271 -1.396 c
+80.436 -1.583 80.668 -1.675 80.962 -1.675 c
+81.157 -1.675 81.33 -1.643 81.477 -1.573 c
+81.623 -1.496 81.76 -1.378 81.888 -1.22 c
+82.226 -1.631 l
+81.939 -2.036 81.509 -2.234 80.933 -2.234 c
+80.859 1.338 m
+80.584 1.338 80.382 1.242 80.256 1.058 c
+80.127 0.871 80.054 0.58 80.036 0.191 c
+81.609 0.191 l
+81.609 0.279 l
+81.587 0.661 81.521 0.929 81.403 1.087 c
+81.286 1.253 81.102 1.338 80.859 1.338 c
+85.916 -0.367 m
+85.916 -0.995 85.798 -1.466 85.563 -1.778 c
+85.335 -2.084 85.019 -2.234 84.607 -2.234 c
+84.203 -2.234 83.894 -2.084 83.682 -1.778 c
+83.682 -3.69 l
+83.034 -3.69 l
+83.034 1.823 l
+83.622 1.823 l
+83.667 1.382 l
+83.88 1.723 84.189 1.897 84.592 1.897 c
+85.033 1.897 85.361 1.742 85.577 1.44 c
+85.791 1.135 85.905 0.68 85.916 0.073 c
+h
+85.269 0.015 m
+85.269 0.456 85.199 0.779 85.063 0.985 c
+84.923 1.198 84.703 1.309 84.401 1.309 c
+84.085 1.309 83.847 1.154 83.682 0.852 c
+83.682 -1.22 l
+83.847 -1.525 84.085 -1.675 84.401 -1.675 c
+84.696 -1.675 84.908 -1.573 85.048 -1.367 c
+85.184 -1.153 85.258 -0.823 85.269 -0.382 c
+h
+86.621 0.015 m
+86.621 0.592 86.757 1.047 87.032 1.382 c
+87.316 1.723 87.687 1.897 88.15 1.897 c
+88.609 1.897 88.977 1.727 89.253 1.397 c
+89.535 1.073 89.683 0.625 89.693 0.059 c
+89.693 -0.367 l
+89.693 -0.937 89.55 -1.392 89.267 -1.735 c
+88.991 -2.069 88.624 -2.234 88.164 -2.234 c
+87.701 -2.234 87.331 -2.072 87.048 -1.749 c
+86.772 -1.419 86.629 -0.977 86.621 -0.426 c
+h
+87.268 -0.367 m
+87.268 -0.771 87.345 -1.087 87.503 -1.323 c
+87.668 -1.558 87.889 -1.675 88.164 -1.675 c
+88.731 -1.675 89.024 -1.264 89.047 -0.441 c
+89.047 0.015 l
+89.047 0.416 88.962 0.735 88.796 0.97 c
+88.638 1.213 88.422 1.338 88.15 1.338 c
+87.885 1.338 87.668 1.213 87.503 0.97 c
+87.345 0.735 87.268 0.416 87.268 0.015 c
+h
+92.471 -1.147 m
+92.471 -0.999 92.416 -0.878 92.31 -0.779 c
+92.199 -0.683 91.994 -0.565 91.692 -0.426 c
+91.347 -0.279 91.104 -0.158 90.957 -0.058 c
+90.811 0.048 90.7 0.166 90.634 0.294 c
+90.564 0.419 90.531 0.577 90.531 0.765 c
+90.531 1.087 90.649 1.357 90.884 1.573 c
+91.119 1.786 91.42 1.897 91.795 1.897 c
+92.177 1.897 92.486 1.783 92.721 1.558 c
+92.956 1.33 93.074 1.044 93.074 0.691 c
+92.428 0.691 l
+92.428 0.867 92.368 1.018 92.251 1.147 c
+92.133 1.272 91.979 1.338 91.795 1.338 c
+91.597 1.338 91.446 1.282 91.339 1.176 c
+91.229 1.077 91.177 0.944 91.177 0.779 c
+91.177 0.651 91.215 0.544 91.295 0.456 c
+91.373 0.375 91.564 0.272 91.869 0.147 c
+92.347 -0.04 92.677 -0.228 92.854 -0.411 c
+93.03 -0.588 93.118 -0.816 93.118 -1.087 c
+93.118 -1.44 92.993 -1.72 92.75 -1.926 c
+92.515 -2.131 92.199 -2.234 91.81 -2.234 c
+91.387 -2.234 91.049 -2.117 90.795 -1.881 c
+90.539 -1.639 90.414 -1.334 90.414 -0.97 c
+91.06 -0.97 l
+91.067 -1.198 91.138 -1.374 91.266 -1.5 c
+91.391 -1.617 91.574 -1.675 91.81 -1.675 c
+92.023 -1.675 92.185 -1.628 92.295 -1.529 c
+92.413 -1.433 92.471 -1.305 92.471 -1.147 c
+94.662 -2.161 -0.647 3.984 re
+94.705 2.866 m
+94.705 2.756 94.676 2.664 94.618 2.587 c
+94.558 2.517 94.463 2.484 94.338 2.484 c
+94.221 2.484 94.125 2.517 94.059 2.587 c
+94 2.664 93.97 2.756 93.97 2.866 c
+93.97 2.984 94 3.076 94.059 3.146 c
+94.125 3.223 94.221 3.263 94.338 3.263 c
+94.463 3.263 94.558 3.223 94.618 3.146 c
+94.676 3.065 94.705 2.973 94.705 2.866 c
+96.528 2.778 m
+96.528 1.823 l
+97.131 1.823 l
+97.131 1.294 l
+96.528 1.294 l
+96.528 -1.176 l
+96.528 -1.334 96.55 -1.452 96.602 -1.529 c
+96.661 -1.61 96.748 -1.646 96.866 -1.646 c
+96.954 -1.646 97.043 -1.631 97.131 -1.602 c
+97.131 -2.161 l
+96.984 -2.208 96.829 -2.234 96.675 -2.234 c
+96.418 -2.234 96.224 -2.142 96.087 -1.955 c
+95.948 -1.771 95.881 -1.51 95.881 -1.176 c
+95.881 1.294 l
+95.279 1.294 l
+95.279 1.823 l
+95.881 1.823 l
+95.881 2.778 l
+h
+97.689 0.015 m
+97.689 0.592 97.826 1.047 98.101 1.382 c
+98.384 1.723 98.755 1.897 99.218 1.897 c
+99.678 1.897 100.045 1.727 100.32 1.397 c
+100.604 1.073 100.75 0.625 100.762 0.059 c
+100.762 -0.367 l
+100.762 -0.937 100.619 -1.392 100.336 -1.735 c
+100.06 -2.069 99.692 -2.234 99.233 -2.234 c
+98.77 -2.234 98.399 -2.072 98.115 -1.749 c
+97.84 -1.419 97.697 -0.977 97.689 -0.426 c
+h
+98.336 -0.367 m
+98.336 -0.771 98.414 -1.087 98.572 -1.323 c
+98.737 -1.558 98.957 -1.675 99.233 -1.675 c
+99.799 -1.675 100.093 -1.264 100.114 -0.441 c
+100.114 0.015 l
+100.114 0.416 100.031 0.735 99.865 0.97 c
+99.707 1.213 99.49 1.338 99.218 1.338 c
+98.954 1.338 98.737 1.213 98.572 0.97 c
+98.414 0.735 98.336 0.416 98.336 0.015 c
+h
+103.246 1.205 m
+103.158 1.224 103.058 1.235 102.952 1.235 c
+102.618 1.235 102.383 1.051 102.246 0.691 c
+102.246 -2.161 l
+101.6 -2.161 l
+101.6 1.823 l
+102.231 1.823 l
+102.246 1.411 l
+102.423 1.735 102.665 1.897 102.981 1.897 c
+103.088 1.897 103.176 1.874 103.246 1.837 c
+h
+105.053 -1.072 m
+105.774 1.823 l
+106.464 1.823 l
+105.171 -2.719 l
+105.072 -3.061 104.929 -3.322 104.745 -3.499 c
+104.569 -3.675 104.367 -3.763 104.143 -3.763 c
+104.054 -3.763 103.94 -3.74 103.804 -3.704 c
+103.804 -3.16 l
+103.952 -3.175 l
+104.135 -3.175 104.282 -3.131 104.392 -3.042 c
+104.499 -2.954 104.587 -2.796 104.657 -2.572 c
+104.775 -2.131 l
+103.613 1.823 l
+104.319 1.823 l
+h
+106.744 -1.808 m
+106.744 -1.691 106.777 -1.595 106.847 -1.514 c
+106.913 -1.437 107.016 -1.396 107.156 -1.396 c
+107.303 -1.396 107.409 -1.437 107.479 -1.514 c
+107.557 -1.595 107.597 -1.691 107.597 -1.808 c
+107.597 -1.918 107.557 -2.01 107.479 -2.088 c
+107.409 -2.165 107.303 -2.205 107.156 -2.205 c
+107.016 -2.205 106.913 -2.165 106.847 -2.088 c
+106.777 -2.01 106.744 -1.918 106.744 -1.808 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+287.665 337.903 234.667 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 389.6758 330.7339 cm
+0 0 m
+-2.954 -8.32 l
+-4.88 -8.32 l
+-1.911 -0.47 l
+-5.659 -0.47 l
+-5.659 1.043 l
+0 1.043 l
+h
+1.562 -7.394 m
+1.562 -7.1 1.658 -6.861 1.856 -6.674 c
+2.051 -6.49 2.304 -6.394 2.62 -6.394 c
+2.922 -6.394 3.171 -6.49 3.37 -6.674 c
+3.576 -6.861 3.678 -7.1 3.678 -7.394 c
+3.678 -7.699 3.576 -7.945 3.37 -8.129 c
+3.171 -8.306 2.922 -8.393 2.62 -8.393 c
+2.315 -8.393 2.062 -8.302 1.856 -8.114 c
+1.658 -7.93 1.562 -7.688 1.562 -7.394 c
+11.403 -4.896 m
+10.462 -4.896 l
+10.462 -8.32 l
+8.58 -8.32 l
+8.58 1.043 l
+11.594 1.043 l
+12.542 1.043 13.274 0.797 13.785 0.309 c
+14.302 -0.184 14.564 -0.879 14.564 -1.779 c
+14.564 -3.024 14.111 -3.896 13.211 -4.395 c
+14.843 -8.231 l
+14.843 -8.32 l
+12.814 -8.32 l
+h
+10.462 -3.323 m
+11.535 -3.323 l
+11.917 -3.323 12.2 -3.201 12.388 -2.955 c
+12.572 -2.702 12.667 -2.363 12.667 -1.941 c
+12.667 -1 12.303 -0.53 11.58 -0.53 c
+10.462 -0.53 l
+h
+20.917 -4.263 m
+17.978 -4.263 l
+17.978 -6.747 l
+21.461 -6.747 l
+21.461 -8.32 l
+16.081 -8.32 l
+16.081 1.043 l
+21.446 1.043 l
+21.446 -0.53 l
+17.978 -0.53 l
+17.978 -2.749 l
+20.917 -2.749 l
+h
+25.779 -5.748 m
+27.351 1.043 l
+29.454 1.043 l
+26.763 -8.32 l
+24.794 -8.32 l
+22.104 1.043 l
+24.206 1.043 l
+h
+35.388 -4.263 m
+32.448 -4.263 l
+32.448 -6.747 l
+35.932 -6.747 l
+35.932 -8.32 l
+30.552 -8.32 l
+30.552 1.043 l
+35.917 1.043 l
+35.917 -0.53 l
+32.448 -0.53 l
+32.448 -2.749 l
+35.388 -2.749 l
+h
+40.033 -4.896 m
+39.092 -4.896 l
+39.092 -8.32 l
+37.211 -8.32 l
+37.211 1.043 l
+40.224 1.043 l
+41.172 1.043 41.903 0.797 42.414 0.309 c
+42.932 -0.184 43.193 -0.879 43.193 -1.779 c
+43.193 -3.024 42.742 -3.896 41.841 -4.395 c
+43.473 -8.231 l
+43.473 -8.32 l
+41.444 -8.32 l
+h
+39.092 -3.323 m
+40.166 -3.323 l
+40.548 -3.323 40.831 -3.201 41.018 -2.955 c
+41.202 -2.702 41.297 -2.363 41.297 -1.941 c
+41.297 -1 40.933 -0.53 40.209 -0.53 c
+39.092 -0.53 l
+h
+50.462 -0.53 m
+48.139 -0.53 l
+48.139 -8.32 l
+46.243 -8.32 l
+46.243 -0.53 l
+43.965 -0.53 l
+43.965 1.043 l
+50.462 1.043 l
+h
+53.732 -8.32 -1.896 9.363 re
+61.979 -8.32 m
+60.082 -8.32 l
+57.319 -2.176 l
+57.319 -8.32 l
+55.423 -8.32 l
+55.423 1.043 l
+57.319 1.043 l
+60.082 -5.101 l
+60.082 1.043 l
+61.979 1.043 l
+h
+70.096 -7.262 m
+69.732 -7.655 69.288 -7.953 68.759 -8.158 c
+68.23 -8.353 67.649 -8.453 67.024 -8.453 c
+65.944 -8.453 65.106 -8.121 64.51 -7.453 c
+63.912 -6.787 63.607 -5.818 63.6 -4.543 c
+63.6 -2.852 l
+63.6 -1.559 63.879 -0.566 64.437 0.132 c
+65.003 0.826 65.826 1.176 66.906 1.176 c
+67.924 1.176 68.689 0.918 69.2 0.411 c
+69.718 -0.088 70.015 -0.875 70.096 -1.941 c
+68.259 -1.941 l
+68.207 -1.345 68.086 -0.938 67.891 -0.721 c
+67.693 -0.508 67.384 -0.397 66.966 -0.397 c
+66.455 -0.397 66.083 -0.585 65.848 -0.956 c
+65.62 -1.331 65.503 -1.922 65.495 -2.735 c
+65.495 -4.439 l
+65.495 -5.292 65.62 -5.917 65.878 -6.307 c
+66.131 -6.689 66.547 -6.88 67.127 -6.88 c
+67.498 -6.88 67.803 -6.806 68.038 -6.659 c
+68.2 -6.542 l
+68.2 -4.821 l
+66.877 -4.821 l
+66.877 -3.396 l
+70.096 -3.396 l
+h
+81.143 -5.204 m
+81.091 -6.273 80.79 -7.082 80.231 -7.629 c
+79.68 -8.181 78.908 -8.453 77.909 -8.453 c
+76.84 -8.453 76.02 -8.107 75.454 -7.408 c
+74.896 -6.714 74.617 -5.719 74.617 -4.425 c
+74.617 -2.852 l
+74.617 -1.559 74.903 -0.566 75.484 0.132 c
+76.072 0.826 76.884 1.176 77.923 1.176 c
+78.941 1.176 79.717 0.885 80.246 0.309 c
+80.776 -0.262 81.077 -1.081 81.158 -2.147 c
+79.261 -2.147 l
+79.239 -1.482 79.136 -1.025 78.952 -0.78 c
+78.765 -0.526 78.424 -0.397 77.923 -0.397 c
+77.424 -0.397 77.063 -0.574 76.851 -0.927 c
+76.645 -1.279 76.531 -1.864 76.512 -2.675 c
+76.512 -4.439 l
+76.512 -5.373 76.616 -6.012 76.821 -6.365 c
+77.034 -6.71 77.398 -6.88 77.909 -6.88 c
+78.397 -6.88 78.736 -6.762 78.923 -6.527 c
+79.118 -6.284 79.224 -5.843 79.247 -5.204 c
+h
+89.087 -8.32 m
+87.206 -8.32 l
+87.206 -4.308 l
+84.413 -4.308 l
+84.413 -8.32 l
+82.517 -8.32 l
+82.517 1.043 l
+84.413 1.043 l
+84.413 -2.749 l
+87.206 -2.749 l
+87.206 1.043 l
+89.087 1.043 l
+h
+95.427 -6.409 m
+92.854 -6.409 l
+92.354 -8.32 l
+90.355 -8.32 l
+93.28 1.043 l
+95.001 1.043 l
+97.954 -8.32 l
+95.926 -8.32 l
+h
+93.266 -4.821 m
+95.015 -4.821 l
+94.133 -1.485 l
+h
+105.609 -8.32 m
+103.713 -8.32 l
+100.95 -2.176 l
+100.95 -8.32 l
+99.053 -8.32 l
+99.053 1.043 l
+100.95 1.043 l
+103.713 -5.101 l
+103.713 1.043 l
+105.609 1.043 l
+h
+113.727 -7.262 m
+113.363 -7.655 112.919 -7.953 112.389 -8.158 c
+111.86 -8.353 111.279 -8.453 110.654 -8.453 c
+109.574 -8.453 108.736 -8.121 108.141 -7.453 c
+107.543 -6.787 107.237 -5.818 107.23 -4.543 c
+107.23 -2.852 l
+107.23 -1.559 107.51 -0.566 108.067 0.132 c
+108.634 0.826 109.457 1.176 110.537 1.176 c
+111.555 1.176 112.32 0.918 112.83 0.411 c
+113.349 -0.088 113.646 -0.875 113.727 -1.941 c
+111.89 -1.941 l
+111.838 -1.345 111.717 -0.938 111.522 -0.721 c
+111.323 -0.508 111.015 -0.397 110.596 -0.397 c
+110.085 -0.397 109.714 -0.585 109.479 -0.956 c
+109.251 -1.331 109.133 -1.922 109.126 -2.735 c
+109.126 -4.439 l
+109.126 -5.292 109.251 -5.917 109.508 -6.307 c
+109.762 -6.689 110.177 -6.88 110.758 -6.88 c
+111.129 -6.88 111.433 -6.806 111.669 -6.659 c
+111.83 -6.542 l
+111.83 -4.821 l
+110.508 -4.821 l
+110.508 -3.396 l
+113.727 -3.396 l
+h
+120.135 -4.263 m
+117.195 -4.263 l
+117.195 -6.747 l
+120.68 -6.747 l
+120.68 -8.32 l
+115.3 -8.32 l
+115.3 1.043 l
+120.665 1.043 l
+120.665 -0.53 l
+117.195 -0.53 l
+117.195 -2.749 l
+120.135 -2.749 l
+h
+125.893 -5.865 m
+125.893 -5.484 125.795 -5.197 125.6 -4.998 c
+125.401 -4.803 125.049 -4.601 124.542 -4.395 c
+123.601 -4.036 122.924 -3.616 122.513 -3.146 c
+122.101 -2.668 121.895 -2.099 121.895 -1.441 c
+121.895 -0.659 122.175 -0.026 122.734 0.455 c
+123.292 0.933 124.002 1.176 124.865 1.176 c
+125.442 1.176 125.957 1.051 126.408 0.808 c
+126.857 0.562 127.202 0.22 127.437 -0.221 c
+127.68 -0.662 127.805 -1.162 127.805 -1.721 c
+125.924 -1.721 l
+125.924 -1.279 125.828 -0.948 125.644 -0.721 c
+125.457 -0.497 125.188 -0.383 124.835 -0.383 c
+124.501 -0.383 124.241 -0.482 124.056 -0.676 c
+123.88 -0.864 123.792 -1.125 123.792 -1.455 c
+123.792 -1.713 123.894 -1.948 124.1 -2.161 c
+124.307 -2.367 124.667 -2.584 125.188 -2.808 c
+126.099 -3.132 126.761 -3.535 127.173 -4.013 c
+127.591 -4.495 127.805 -5.108 127.805 -5.85 c
+127.805 -6.666 127.544 -7.302 127.026 -7.761 c
+126.504 -8.225 125.799 -8.453 124.909 -8.453 c
+124.299 -8.453 123.748 -8.327 123.248 -8.085 c
+122.756 -7.832 122.37 -7.475 122.087 -7.012 c
+121.8 -6.542 121.66 -5.994 121.66 -5.366 c
+123.557 -5.366 l
+123.557 -5.906 123.659 -6.299 123.865 -6.542 c
+124.079 -6.787 124.428 -6.909 124.909 -6.909 c
+125.563 -6.909 125.893 -6.564 125.893 -5.865 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 307.601 234.667 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 300.7657 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.278 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.485 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.596 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.631 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.179 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.675 re
+10.124 2.837 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.455 14.552 -0.515 c
+14.589 -0.566 14.645 -0.61 14.714 -0.646 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.687 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.603 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.455 c
+13.586 -0.36 13.582 -0.264 13.582 -0.176 c
+h
+24.133 1.47 m
+24.033 1.477 23.931 1.488 23.823 1.5 c
+23.713 1.517 23.592 1.529 23.456 1.529 c
+23.28 1.529 23.121 1.488 22.986 1.411 c
+22.846 1.341 22.728 1.242 22.633 1.118 c
+22.545 0.989 22.475 0.842 22.427 0.676 c
+22.387 0.507 22.369 0.331 22.369 0.147 c
+22.369 -1.264 l
+21.471 -1.264 l
+21.471 0.985 l
+21.471 1.11 21.461 1.235 21.442 1.353 c
+21.432 1.477 21.417 1.595 21.398 1.706 c
+21.388 1.823 21.373 1.918 21.355 1.999 c
+21.332 2.087 21.314 2.161 21.296 2.219 c
+22.177 2.219 l
+22.185 2.168 22.196 2.117 22.207 2.058 c
+22.225 1.999 22.24 1.933 22.251 1.866 c
+22.269 1.808 22.284 1.742 22.296 1.675 c
+22.302 1.606 22.313 1.544 22.325 1.484 c
+22.339 1.484 l
+22.375 1.602 22.427 1.708 22.486 1.808 c
+22.552 1.903 22.633 1.988 22.722 2.058 c
+22.809 2.124 22.913 2.179 23.03 2.219 c
+23.155 2.256 23.302 2.278 23.471 2.278 c
+23.596 2.278 23.713 2.271 23.823 2.263 c
+23.942 2.253 24.044 2.238 24.133 2.219 c
+h
+26.738 -1.323 m
+26.481 -1.323 26.253 -1.286 26.047 -1.22 c
+25.841 -1.143 25.665 -1.029 25.518 -0.882 c
+25.371 -0.727 25.253 -0.536 25.165 -0.309 c
+25.084 -0.085 25.047 0.18 25.047 0.485 c
+25.047 0.816 25.091 1.095 25.18 1.323 c
+25.275 1.558 25.404 1.742 25.562 1.881 c
+25.727 2.018 25.915 2.117 26.121 2.176 c
+26.327 2.242 26.535 2.278 26.753 2.278 c
+27.025 2.278 27.26 2.227 27.458 2.132 c
+27.664 2.043 27.829 1.911 27.958 1.735 c
+28.094 1.565 28.193 1.359 28.252 1.118 c
+28.318 0.882 28.355 0.617 28.355 0.324 c
+28.355 0.309 l
+25.988 0.309 l
+25.988 0.162 26.003 0.022 26.032 -0.103 c
+26.069 -0.231 26.124 -0.345 26.194 -0.441 c
+26.26 -0.529 26.344 -0.599 26.444 -0.646 c
+26.539 -0.698 26.653 -0.721 26.782 -0.721 c
+26.936 -0.721 27.076 -0.687 27.194 -0.617 c
+27.318 -0.551 27.407 -0.449 27.458 -0.309 c
+28.296 -0.382 l
+28.266 -0.482 28.212 -0.588 28.135 -0.706 c
+28.054 -0.816 27.95 -0.919 27.825 -1.014 c
+27.708 -1.103 27.553 -1.176 27.37 -1.234 c
+27.194 -1.294 26.98 -1.323 26.738 -1.323 c
+26.738 1.706 m
+26.649 1.706 26.562 1.69 26.473 1.661 c
+26.385 1.631 26.304 1.58 26.238 1.514 c
+26.169 1.444 26.109 1.356 26.061 1.249 c
+26.021 1.139 26.003 1.014 26.003 0.867 c
+27.472 0.867 l
+27.472 1.003 27.447 1.124 27.399 1.235 c
+27.359 1.341 27.304 1.43 27.237 1.5 c
+27.179 1.565 27.106 1.617 27.017 1.646 c
+26.929 1.683 26.834 1.706 26.738 1.706 c
+32.342 -0.25 m
+32.342 -0.419 32.301 -0.569 32.224 -0.706 c
+32.154 -0.834 32.052 -0.948 31.916 -1.043 c
+31.787 -1.132 31.625 -1.201 31.43 -1.249 c
+31.243 -1.297 31.027 -1.323 30.784 -1.323 c
+30.556 -1.323 30.358 -1.309 30.181 -1.278 c
+30.005 -1.249 29.847 -1.201 29.71 -1.132 c
+29.571 -1.055 29.461 -0.956 29.373 -0.838 c
+29.284 -0.721 29.215 -0.573 29.167 -0.397 c
+29.976 -0.279 l
+29.994 -0.378 30.023 -0.455 30.063 -0.515 c
+30.111 -0.573 30.17 -0.617 30.24 -0.646 c
+30.306 -0.676 30.387 -0.702 30.475 -0.721 c
+30.564 -0.731 30.666 -0.735 30.784 -0.735 c
+30.879 -0.735 30.975 -0.731 31.063 -0.721 c
+31.152 -0.702 31.229 -0.676 31.298 -0.646 c
+31.364 -0.617 31.416 -0.58 31.445 -0.529 c
+31.482 -0.482 31.504 -0.419 31.504 -0.338 c
+31.504 -0.243 31.474 -0.169 31.416 -0.118 c
+31.364 -0.07 31.298 -0.029 31.21 0 c
+31.122 0.037 31.011 0.066 30.886 0.088 c
+30.769 0.118 30.637 0.147 30.489 0.177 c
+30.35 0.214 30.211 0.254 30.063 0.294 c
+29.924 0.341 29.799 0.405 29.681 0.485 c
+29.571 0.563 29.483 0.661 29.417 0.779 c
+29.347 0.897 29.313 1.047 29.313 1.235 c
+29.313 1.389 29.344 1.532 29.402 1.661 c
+29.468 1.797 29.564 1.911 29.681 1.999 c
+29.806 2.087 29.964 2.153 30.152 2.205 c
+30.335 2.253 30.549 2.278 30.784 2.278 c
+30.967 2.278 31.144 2.256 31.313 2.219 c
+31.478 2.19 31.625 2.135 31.754 2.058 c
+31.879 1.988 31.989 1.889 32.077 1.764 c
+32.166 1.646 32.224 1.503 32.253 1.338 c
+31.46 1.264 l
+31.438 1.341 31.408 1.404 31.372 1.455 c
+31.331 1.514 31.283 1.558 31.225 1.588 c
+31.173 1.625 31.111 1.65 31.034 1.661 c
+30.953 1.668 30.872 1.675 30.784 1.675 c
+30.567 1.675 30.406 1.646 30.298 1.588 c
+30.188 1.536 30.137 1.448 30.137 1.323 c
+30.137 1.242 30.155 1.18 30.196 1.132 c
+30.244 1.08 30.306 1.043 30.387 1.014 c
+30.475 0.985 30.57 0.956 30.681 0.926 c
+30.788 0.904 30.909 0.882 31.048 0.852 c
+31.202 0.823 31.361 0.783 31.519 0.735 c
+31.673 0.684 31.813 0.621 31.931 0.544 c
+32.048 0.463 32.143 0.36 32.224 0.235 c
+32.301 0.106 32.342 -0.056 32.342 -0.25 c
+34.859 -1.323 m
+34.602 -1.323 34.374 -1.286 34.169 -1.22 c
+33.963 -1.143 33.786 -1.029 33.639 -0.882 c
+33.492 -0.727 33.375 -0.536 33.286 -0.309 c
+33.205 -0.085 33.169 0.18 33.169 0.485 c
+33.169 0.816 33.213 1.095 33.301 1.323 c
+33.396 1.558 33.525 1.742 33.683 1.881 c
+33.849 2.018 34.036 2.117 34.242 2.176 c
+34.447 2.242 34.657 2.278 34.874 2.278 c
+35.146 2.278 35.381 2.227 35.58 2.132 c
+35.785 2.043 35.95 1.911 36.079 1.735 c
+36.215 1.565 36.314 1.359 36.373 1.118 c
+36.44 0.882 36.476 0.617 36.476 0.324 c
+36.476 0.309 l
+34.109 0.309 l
+34.109 0.162 34.124 0.022 34.154 -0.103 c
+34.19 -0.231 34.246 -0.345 34.315 -0.441 c
+34.381 -0.529 34.466 -0.599 34.565 -0.646 c
+34.661 -0.698 34.774 -0.721 34.903 -0.721 c
+35.058 -0.721 35.197 -0.687 35.314 -0.617 c
+35.44 -0.551 35.528 -0.449 35.58 -0.309 c
+36.417 -0.382 l
+36.388 -0.482 36.332 -0.588 36.255 -0.706 c
+36.174 -0.816 36.072 -0.919 35.947 -1.014 c
+35.829 -1.103 35.675 -1.176 35.491 -1.234 c
+35.314 -1.294 35.102 -1.323 34.859 -1.323 c
+34.859 1.706 m
+34.771 1.706 34.682 1.69 34.595 1.661 c
+34.506 1.631 34.425 1.58 34.36 1.514 c
+34.29 1.444 34.231 1.356 34.183 1.249 c
+34.142 1.139 34.124 1.014 34.124 0.867 c
+35.594 0.867 l
+35.594 1.003 35.568 1.124 35.52 1.235 c
+35.48 1.341 35.425 1.43 35.359 1.5 c
+35.3 1.565 35.227 1.617 35.138 1.646 c
+35.05 1.683 34.955 1.706 34.859 1.706 c
+37.968 1.602 m
+37.424 1.602 l
+37.424 2.219 l
+38.012 2.219 l
+38.291 3.117 l
+38.865 3.117 l
+38.865 2.219 l
+40.099 2.219 l
+40.099 1.602 l
+38.865 1.602 l
+38.865 -0.103 l
+38.865 -0.324 l
+38.872 -0.393 38.894 -0.455 38.923 -0.515 c
+38.96 -0.566 39.015 -0.61 39.085 -0.646 c
+39.162 -0.676 39.276 -0.691 39.424 -0.691 c
+39.559 -0.691 39.695 -0.687 39.835 -0.676 c
+39.971 -0.658 40.103 -0.632 40.232 -0.603 c
+40.232 -1.205 l
+40.151 -1.216 40.074 -1.231 39.996 -1.249 c
+39.916 -1.261 39.838 -1.268 39.761 -1.278 c
+39.68 -1.286 39.592 -1.294 39.497 -1.294 c
+39.409 -1.301 39.309 -1.309 39.202 -1.309 c
+39.015 -1.309 38.853 -1.294 38.718 -1.264 c
+38.589 -1.228 38.475 -1.183 38.379 -1.132 c
+38.291 -1.084 38.218 -1.025 38.159 -0.956 c
+38.1 -0.879 38.056 -0.802 38.027 -0.721 c
+37.997 -0.632 37.975 -0.544 37.968 -0.455 c
+37.957 -0.36 37.953 -0.264 37.953 -0.176 c
+h
+46.236 -2.631 m
+46.236 3.513 l
+48.162 3.513 l
+48.162 2.896 l
+47.089 2.896 l
+47.089 -2.014 l
+48.162 -2.014 l
+48.162 -2.631 l
+h
+50.135 0.838 1.867 -0.794 re
+50.135 0.044 m
+54.196 0.838 1.866 -0.794 re
+54.196 0.044 m
+58.569 1.514 m
+58.686 1.785 58.837 1.984 59.024 2.102 c
+59.208 2.219 59.428 2.278 59.686 2.278 c
+59.891 2.278 60.061 2.242 60.2 2.176 c
+60.347 2.105 60.458 2.014 60.538 1.897 c
+60.627 1.779 60.685 1.635 60.714 1.47 c
+60.751 1.301 60.774 1.124 60.774 0.941 c
+60.774 -1.264 l
+59.862 -1.264 l
+59.862 0.735 l
+59.862 0.871 59.851 0.992 59.833 1.103 c
+59.822 1.209 59.796 1.297 59.76 1.367 c
+59.719 1.444 59.66 1.503 59.583 1.544 c
+59.513 1.58 59.421 1.602 59.303 1.602 c
+59.193 1.602 59.098 1.577 59.01 1.529 c
+58.921 1.477 58.84 1.411 58.775 1.323 c
+58.715 1.235 58.664 1.124 58.628 1 c
+58.598 0.882 58.584 0.75 58.584 0.603 c
+58.584 -1.264 l
+57.672 -1.264 l
+57.672 3.513 l
+58.584 3.513 l
+58.584 2.205 l
+58.584 2.135 58.576 2.065 58.569 1.999 c
+58.569 1.793 l
+58.569 1.735 58.561 1.679 58.554 1.631 c
+58.554 1.514 l
+h
+62.688 -1.323 m
+62.519 -1.323 62.368 -1.301 62.233 -1.264 c
+62.104 -1.216 61.99 -1.147 61.894 -1.058 c
+61.807 -0.97 61.736 -0.864 61.688 -0.735 c
+61.637 -0.599 61.615 -0.449 61.615 -0.279 c
+61.615 -0.073 61.649 0.096 61.718 0.235 c
+61.784 0.382 61.88 0.492 61.998 0.573 c
+62.122 0.661 62.266 0.724 62.424 0.765 c
+62.589 0.801 62.765 0.827 62.952 0.838 c
+63.673 0.852 l
+63.673 1.029 l
+63.673 1.147 63.662 1.249 63.644 1.338 c
+63.621 1.426 63.588 1.492 63.54 1.544 c
+63.5 1.602 63.453 1.639 63.394 1.661 c
+63.334 1.679 63.268 1.69 63.203 1.69 c
+63.133 1.69 63.07 1.679 63.012 1.661 c
+62.96 1.65 62.912 1.625 62.864 1.588 c
+62.824 1.558 62.791 1.507 62.761 1.44 c
+62.74 1.382 62.725 1.301 62.717 1.205 c
+61.776 1.249 l
+61.807 1.396 61.85 1.532 61.909 1.661 c
+61.975 1.785 62.071 1.897 62.189 1.984 c
+62.306 2.08 62.445 2.153 62.615 2.205 c
+62.791 2.253 62.997 2.278 63.232 2.278 c
+63.673 2.278 64.003 2.168 64.232 1.955 c
+64.467 1.749 64.585 1.44 64.585 1.029 c
+64.585 -0.235 l
+64.585 -0.455 l
+64.591 -0.515 64.606 -0.569 64.629 -0.617 c
+64.647 -0.658 64.676 -0.691 64.716 -0.721 c
+64.753 -0.742 64.805 -0.75 64.864 -0.75 c
+64.93 -0.75 64.999 -0.746 65.069 -0.735 c
+65.069 -1.22 l
+65.011 -1.231 64.955 -1.242 64.907 -1.249 c
+64.867 -1.261 64.826 -1.268 64.79 -1.278 c
+64.749 -1.286 64.706 -1.294 64.658 -1.294 c
+64.606 -1.301 64.548 -1.309 64.481 -1.309 c
+64.253 -1.309 64.088 -1.257 63.982 -1.147 c
+63.871 -1.029 63.808 -0.864 63.791 -0.646 c
+63.776 -0.646 l
+63.706 -0.757 63.636 -0.852 63.57 -0.941 c
+63.5 -1.022 63.423 -1.087 63.334 -1.147 c
+63.247 -1.205 63.147 -1.249 63.041 -1.278 c
+62.942 -1.309 62.824 -1.323 62.688 -1.323 c
+63.673 0.353 m
+63.247 0.338 l
+63.147 0.338 63.056 0.331 62.967 0.324 c
+62.887 0.312 62.821 0.287 62.761 0.25 c
+62.703 0.21 62.651 0.15 62.615 0.073 c
+62.574 0.004 62.555 -0.088 62.555 -0.206 c
+62.555 -0.374 62.589 -0.496 62.659 -0.573 c
+62.725 -0.654 62.824 -0.691 62.952 -0.691 c
+63.06 -0.691 63.158 -0.669 63.247 -0.617 c
+63.342 -0.569 63.423 -0.507 63.482 -0.426 c
+63.548 -0.349 63.6 -0.261 63.629 -0.162 c
+63.658 -0.056 63.673 0.059 63.673 0.177 c
+h
+68.807 1.47 m
+68.707 1.477 68.604 1.488 68.498 1.5 c
+68.388 1.517 68.267 1.529 68.13 1.529 c
+67.954 1.529 67.796 1.488 67.66 1.411 c
+67.521 1.341 67.403 1.242 67.307 1.118 c
+67.219 0.989 67.149 0.842 67.101 0.676 c
+67.061 0.507 67.043 0.331 67.043 0.147 c
+67.043 -1.264 l
+66.146 -1.264 l
+66.146 0.985 l
+66.146 1.11 66.135 1.235 66.117 1.353 c
+66.106 1.477 66.091 1.595 66.073 1.706 c
+66.062 1.823 66.047 1.918 66.029 1.999 c
+66.006 2.087 65.988 2.161 65.969 2.219 c
+66.852 2.219 l
+66.859 2.168 66.87 2.117 66.881 2.058 c
+66.9 1.999 66.914 1.933 66.925 1.866 c
+66.943 1.808 66.958 1.742 66.969 1.675 c
+66.977 1.606 66.987 1.544 66.999 1.484 c
+67.014 1.484 l
+67.05 1.602 67.101 1.708 67.16 1.808 c
+67.226 1.903 67.307 1.988 67.396 2.058 c
+67.484 2.124 67.586 2.179 67.704 2.219 c
+67.829 2.256 67.976 2.278 68.145 2.278 c
+68.27 2.278 68.388 2.271 68.498 2.263 c
+68.616 2.253 68.718 2.238 68.807 2.219 c
+h
+72.03 -1.264 m
+72.018 -1.246 72.007 -1.216 72 -1.176 c
+72 -1.128 71.993 -1.08 71.985 -1.029 c
+71.985 -0.97 71.978 -0.912 71.97 -0.852 c
+71.97 -0.691 l
+71.853 -0.926 71.71 -1.095 71.544 -1.191 c
+71.375 -1.278 71.176 -1.323 70.941 -1.323 c
+70.744 -1.323 70.567 -1.278 70.412 -1.191 c
+70.254 -1.103 70.122 -0.981 70.015 -0.823 c
+69.917 -0.658 69.84 -0.467 69.78 -0.25 c
+69.729 -0.037 69.707 0.206 69.707 0.47 c
+69.707 0.735 69.729 0.974 69.78 1.191 c
+69.84 1.415 69.917 1.606 70.015 1.764 c
+70.122 1.918 70.254 2.043 70.412 2.132 c
+70.578 2.227 70.769 2.278 70.985 2.278 c
+71.081 2.278 71.176 2.263 71.265 2.234 c
+71.361 2.213 71.456 2.179 71.544 2.132 c
+71.633 2.08 71.71 2.018 71.779 1.941 c
+71.857 1.86 71.92 1.768 71.97 1.661 c
+71.97 1.749 l
+71.97 1.897 l
+71.97 2.058 l
+71.97 2.234 l
+71.97 3.513 l
+72.867 3.513 l
+72.867 -0.5 l
+72.867 -0.676 72.871 -0.834 72.882 -0.97 c
+72.889 -1.099 72.897 -1.198 72.897 -1.264 c
+h
+71.985 0.485 m
+71.985 0.721 71.959 0.912 71.912 1.058 c
+71.872 1.213 71.816 1.338 71.75 1.426 c
+71.691 1.514 71.621 1.573 71.544 1.602 c
+71.463 1.639 71.386 1.661 71.309 1.661 c
+71.21 1.661 71.118 1.635 71.03 1.588 c
+70.949 1.548 70.883 1.477 70.824 1.382 c
+70.773 1.282 70.729 1.161 70.692 1.014 c
+70.663 0.867 70.648 0.684 70.648 0.47 c
+70.648 0.077 70.699 -0.216 70.809 -0.411 c
+70.927 -0.61 71.089 -0.706 71.294 -0.706 c
+71.371 -0.706 71.449 -0.687 71.529 -0.646 c
+71.606 -0.61 71.681 -0.544 71.75 -0.455 c
+71.816 -0.368 71.872 -0.246 71.912 -0.088 c
+71.959 0.066 71.985 0.257 71.985 0.485 c
+74.282 -2.631 m
+74.282 -2.014 l
+75.355 -2.014 l
+75.355 2.896 l
+74.282 2.896 l
+74.282 3.513 l
+76.207 3.513 l
+76.207 -2.631 l
+h
+82.789 -2.631 m
+82.789 3.513 l
+84.715 3.513 l
+84.715 2.896 l
+83.641 2.896 l
+83.641 -2.014 l
+84.715 -2.014 l
+84.715 -2.631 l
+h
+86.702 1.602 m
+86.159 1.602 l
+86.159 2.219 l
+86.747 2.219 l
+87.026 3.117 l
+87.599 3.117 l
+87.599 2.219 l
+88.834 2.219 l
+88.834 1.602 l
+87.599 1.602 l
+87.599 -0.103 l
+87.599 -0.324 l
+87.606 -0.393 87.629 -0.455 87.658 -0.515 c
+87.695 -0.566 87.75 -0.61 87.82 -0.646 c
+87.897 -0.676 88.011 -0.691 88.158 -0.691 c
+88.294 -0.691 88.43 -0.687 88.57 -0.676 c
+88.705 -0.658 88.838 -0.632 88.967 -0.603 c
+88.967 -1.205 l
+88.886 -1.216 88.809 -1.231 88.731 -1.249 c
+88.651 -1.261 88.573 -1.268 88.496 -1.278 c
+88.415 -1.286 88.327 -1.294 88.231 -1.294 c
+88.144 -1.301 88.044 -1.309 87.938 -1.309 c
+87.75 -1.309 87.588 -1.294 87.452 -1.264 c
+87.323 -1.228 87.209 -1.183 87.114 -1.132 c
+87.026 -1.084 86.953 -1.025 86.893 -0.956 c
+86.835 -0.879 86.791 -0.802 86.762 -0.721 c
+86.732 -0.632 86.71 -0.544 86.702 -0.455 c
+86.692 -0.36 86.688 -0.264 86.688 -0.176 c
+h
+91.116 -1.323 m
+90.947 -1.323 90.796 -1.301 90.66 -1.264 c
+90.532 -1.216 90.418 -1.147 90.322 -1.058 c
+90.234 -0.97 90.164 -0.864 90.116 -0.735 c
+90.065 -0.599 90.043 -0.449 90.043 -0.279 c
+90.043 -0.073 90.076 0.096 90.145 0.235 c
+90.212 0.382 90.307 0.492 90.425 0.573 c
+90.55 0.661 90.694 0.724 90.851 0.765 c
+91.017 0.801 91.193 0.827 91.381 0.838 c
+92.101 0.852 l
+92.101 1.029 l
+92.101 1.147 92.09 1.249 92.071 1.338 c
+92.049 1.426 92.017 1.492 91.969 1.544 c
+91.928 1.602 91.88 1.639 91.822 1.661 c
+91.763 1.679 91.697 1.69 91.631 1.69 c
+91.56 1.69 91.498 1.679 91.44 1.661 c
+91.388 1.65 91.34 1.625 91.292 1.588 c
+91.252 1.558 91.219 1.507 91.19 1.44 c
+91.167 1.382 91.153 1.301 91.145 1.205 c
+90.205 1.249 l
+90.234 1.396 90.278 1.532 90.337 1.661 c
+90.403 1.785 90.499 1.897 90.616 1.984 c
+90.734 2.08 90.874 2.153 91.043 2.205 c
+91.219 2.253 91.425 2.278 91.66 2.278 c
+92.101 2.278 92.431 2.168 92.659 1.955 c
+92.894 1.749 93.012 1.44 93.012 1.029 c
+93.012 -0.235 l
+93.012 -0.455 l
+93.019 -0.515 93.034 -0.569 93.056 -0.617 c
+93.075 -0.658 93.104 -0.691 93.145 -0.721 c
+93.181 -0.742 93.233 -0.75 93.291 -0.75 c
+93.358 -0.75 93.428 -0.746 93.497 -0.735 c
+93.497 -1.22 l
+93.438 -1.231 93.383 -1.242 93.335 -1.249 c
+93.295 -1.261 93.255 -1.268 93.218 -1.278 c
+93.177 -1.286 93.133 -1.294 93.086 -1.294 c
+93.034 -1.301 92.975 -1.309 92.909 -1.309 c
+92.682 -1.309 92.516 -1.257 92.41 -1.147 c
+92.3 -1.029 92.237 -0.864 92.219 -0.646 c
+92.204 -0.646 l
+92.134 -0.757 92.065 -0.852 91.998 -0.941 c
+91.928 -1.022 91.851 -1.087 91.763 -1.147 c
+91.675 -1.205 91.575 -1.249 91.469 -1.278 c
+91.369 -1.309 91.252 -1.323 91.116 -1.323 c
+92.101 0.353 m
+91.675 0.338 l
+91.575 0.338 91.483 0.331 91.396 0.324 c
+91.315 0.312 91.248 0.287 91.19 0.25 c
+91.13 0.21 91.08 0.15 91.043 0.073 c
+91.002 0.004 90.984 -0.088 90.984 -0.206 c
+90.984 -0.374 91.017 -0.496 91.086 -0.573 c
+91.153 -0.654 91.252 -0.691 91.381 -0.691 c
+91.487 -0.691 91.587 -0.669 91.675 -0.617 c
+91.77 -0.569 91.851 -0.507 91.91 -0.426 c
+91.976 -0.349 92.027 -0.261 92.057 -0.162 c
+92.086 -0.056 92.101 0.059 92.101 0.177 c
+h
+97.235 1.47 m
+97.135 1.477 97.033 1.488 96.926 1.5 c
+96.815 1.517 96.694 1.529 96.558 1.529 c
+96.382 1.529 96.224 1.488 96.088 1.411 c
+95.948 1.341 95.83 1.242 95.735 1.118 c
+95.647 0.989 95.577 0.842 95.529 0.676 c
+95.489 0.507 95.471 0.331 95.471 0.147 c
+95.471 -1.264 l
+94.574 -1.264 l
+94.574 0.985 l
+94.574 1.11 94.563 1.235 94.544 1.353 c
+94.534 1.477 94.519 1.595 94.5 1.706 c
+94.49 1.823 94.475 1.918 94.457 1.999 c
+94.434 2.087 94.416 2.161 94.398 2.219 c
+95.28 2.219 l
+95.287 2.168 95.298 2.117 95.309 2.058 c
+95.327 1.999 95.342 1.933 95.353 1.866 c
+95.371 1.808 95.386 1.742 95.398 1.675 c
+95.404 1.606 95.415 1.544 95.427 1.484 c
+95.441 1.484 l
+95.478 1.602 95.529 1.708 95.589 1.808 c
+95.654 1.903 95.735 1.988 95.824 2.058 c
+95.911 2.124 96.015 2.179 96.132 2.219 c
+96.257 2.256 96.404 2.278 96.573 2.278 c
+96.698 2.278 96.815 2.271 96.926 2.263 c
+97.044 2.253 97.146 2.238 97.235 2.219 c
+h
+99.814 -2.66 m
+99.597 -2.66 99.406 -2.635 99.24 -2.587 c
+99.072 -2.547 98.932 -2.485 98.814 -2.396 c
+98.697 -2.315 98.598 -2.219 98.521 -2.102 c
+98.451 -1.984 98.403 -1.856 98.374 -1.72 c
+99.271 -1.617 l
+99.307 -1.753 99.377 -1.86 99.476 -1.94 c
+99.572 -2.028 99.697 -2.072 99.844 -2.072 c
+99.932 -2.072 100.013 -2.057 100.094 -2.028 c
+100.171 -1.999 100.24 -1.944 100.299 -1.866 c
+100.358 -1.797 100.402 -1.705 100.431 -1.587 c
+100.468 -1.469 100.491 -1.323 100.491 -1.147 c
+100.491 -0.956 l
+100.491 -0.889 100.494 -0.831 100.505 -0.779 c
+100.505 -0.588 l
+100.491 -0.588 l
+100.391 -0.816 100.248 -0.977 100.064 -1.073 c
+99.876 -1.172 99.671 -1.22 99.447 -1.22 c
+99.24 -1.22 99.057 -1.183 98.903 -1.103 c
+98.756 -1.014 98.627 -0.897 98.521 -0.75 c
+98.421 -0.596 98.348 -0.411 98.3 -0.206 c
+98.249 0.008 98.226 0.243 98.226 0.5 c
+98.226 0.771 98.249 1.018 98.3 1.235 c
+98.359 1.448 98.44 1.631 98.55 1.779 c
+98.656 1.933 98.789 2.051 98.947 2.132 c
+99.101 2.219 99.288 2.263 99.506 2.263 c
+99.601 2.263 99.701 2.253 99.799 2.234 c
+99.895 2.213 99.984 2.179 100.064 2.132 c
+100.152 2.08 100.229 2.018 100.299 1.941 c
+100.377 1.86 100.439 1.768 100.491 1.661 c
+100.505 1.661 l
+100.505 1.808 l
+100.512 1.866 100.52 1.918 100.52 1.97 c
+100.527 2.028 100.534 2.076 100.534 2.117 c
+100.542 2.165 100.553 2.198 100.564 2.219 c
+101.416 2.219 l
+101.405 2.138 101.395 2.028 101.387 1.881 c
+101.387 1.411 l
+101.387 -1.161 l
+101.387 -1.415 101.351 -1.635 101.284 -1.822 c
+101.214 -2.007 101.112 -2.161 100.975 -2.278 c
+100.836 -2.404 100.67 -2.499 100.476 -2.558 c
+100.277 -2.624 100.057 -2.66 99.814 -2.66 c
+100.505 0.53 m
+100.505 0.742 100.479 0.919 100.431 1.058 c
+100.391 1.205 100.336 1.323 100.27 1.411 c
+100.211 1.5 100.142 1.558 100.064 1.588 c
+99.984 1.625 99.907 1.646 99.828 1.646 c
+99.73 1.646 99.637 1.621 99.55 1.573 c
+99.469 1.532 99.402 1.463 99.344 1.367 c
+99.292 1.278 99.248 1.161 99.211 1.014 c
+99.182 0.875 99.167 0.706 99.167 0.5 c
+99.167 0.125 99.226 -0.154 99.344 -0.338 c
+99.462 -0.515 99.623 -0.603 99.828 -0.603 c
+99.895 -0.603 99.969 -0.588 100.049 -0.559 c
+100.138 -0.522 100.211 -0.463 100.27 -0.382 c
+100.336 -0.294 100.391 -0.176 100.431 -0.029 c
+100.479 0.118 100.505 0.301 100.505 0.53 c
+103.905 -1.323 m
+103.647 -1.323 103.419 -1.286 103.213 -1.22 c
+103.007 -1.143 102.831 -1.029 102.685 -0.882 c
+102.537 -0.727 102.419 -0.536 102.332 -0.309 c
+102.251 -0.085 102.214 0.18 102.214 0.485 c
+102.214 0.816 102.258 1.095 102.346 1.323 c
+102.442 1.558 102.571 1.742 102.729 1.881 c
+102.893 2.018 103.081 2.117 103.287 2.176 c
+103.493 2.242 103.702 2.278 103.919 2.278 c
+104.191 2.278 104.426 2.227 104.624 2.132 c
+104.83 2.043 104.996 1.911 105.124 1.735 c
+105.26 1.565 105.359 1.359 105.418 1.118 c
+105.484 0.882 105.521 0.617 105.521 0.324 c
+105.521 0.309 l
+103.155 0.309 l
+103.155 0.162 103.169 0.022 103.199 -0.103 c
+103.236 -0.231 103.29 -0.345 103.36 -0.441 c
+103.427 -0.529 103.511 -0.599 103.61 -0.646 c
+103.706 -0.698 103.82 -0.721 103.948 -0.721 c
+104.102 -0.721 104.242 -0.687 104.36 -0.617 c
+104.485 -0.551 104.573 -0.449 104.624 -0.309 c
+105.463 -0.382 l
+105.432 -0.482 105.378 -0.588 105.301 -0.706 c
+105.22 -0.816 105.117 -0.919 104.992 -1.014 c
+104.875 -1.103 104.72 -1.176 104.536 -1.234 c
+104.36 -1.294 104.146 -1.323 103.905 -1.323 c
+103.905 1.706 m
+103.816 1.706 103.728 1.69 103.639 1.661 c
+103.551 1.631 103.471 1.58 103.404 1.514 c
+103.335 1.444 103.276 1.356 103.228 1.249 c
+103.188 1.139 103.169 1.014 103.169 0.867 c
+104.639 0.867 l
+104.639 1.003 104.613 1.124 104.566 1.235 c
+104.526 1.341 104.47 1.43 104.404 1.5 c
+104.345 1.565 104.272 1.617 104.183 1.646 c
+104.096 1.683 104 1.706 103.905 1.706 c
+107.009 1.602 m
+106.465 1.602 l
+106.465 2.219 l
+107.053 2.219 l
+107.333 3.117 l
+107.906 3.117 l
+107.906 2.219 l
+109.141 2.219 l
+109.141 1.602 l
+107.906 1.602 l
+107.906 -0.103 l
+107.906 -0.324 l
+107.913 -0.393 107.936 -0.455 107.965 -0.515 c
+108.002 -0.566 108.057 -0.61 108.127 -0.646 c
+108.204 -0.676 108.318 -0.691 108.464 -0.691 c
+108.601 -0.691 108.736 -0.687 108.876 -0.676 c
+109.012 -0.658 109.145 -0.632 109.273 -0.603 c
+109.273 -1.205 l
+109.193 -1.216 109.115 -1.231 109.038 -1.249 c
+108.957 -1.261 108.88 -1.268 108.803 -1.278 c
+108.722 -1.286 108.634 -1.294 108.538 -1.294 c
+108.45 -1.301 108.351 -1.309 108.244 -1.309 c
+108.057 -1.309 107.895 -1.294 107.759 -1.264 c
+107.63 -1.228 107.516 -1.183 107.421 -1.132 c
+107.333 -1.084 107.259 -1.025 107.2 -0.956 c
+107.142 -0.879 107.098 -0.802 107.068 -0.721 c
+107.039 -0.632 107.017 -0.544 107.009 -0.455 c
+106.999 -0.36 106.995 -0.264 106.995 -0.176 c
+h
+117.542 1.47 m
+117.442 1.477 117.339 1.488 117.232 1.5 c
+117.122 1.517 117.001 1.529 116.865 1.529 c
+116.688 1.529 116.53 1.488 116.395 1.411 c
+116.255 1.341 116.137 1.242 116.042 1.118 c
+115.954 0.989 115.884 0.842 115.836 0.676 c
+115.796 0.507 115.778 0.331 115.778 0.147 c
+115.778 -1.264 l
+114.88 -1.264 l
+114.88 0.985 l
+114.88 1.11 114.87 1.235 114.851 1.353 c
+114.84 1.477 114.826 1.595 114.807 1.706 c
+114.797 1.823 114.781 1.918 114.763 1.999 c
+114.741 2.087 114.723 2.161 114.704 2.219 c
+115.586 2.219 l
+115.593 2.168 115.605 2.117 115.616 2.058 c
+115.634 1.999 115.649 1.933 115.66 1.866 c
+115.678 1.808 115.693 1.742 115.703 1.675 c
+115.711 1.606 115.722 1.544 115.734 1.484 c
+115.748 1.484 l
+115.784 1.602 115.836 1.708 115.895 1.808 c
+115.961 1.903 116.042 1.988 116.13 2.058 c
+116.218 2.124 116.321 2.179 116.439 2.219 c
+116.564 2.256 116.711 2.278 116.879 2.278 c
+117.004 2.278 117.122 2.271 117.232 2.263 c
+117.35 2.253 117.453 2.238 117.542 2.219 c
+h
+120.15 -1.323 m
+119.893 -1.323 119.665 -1.286 119.46 -1.22 c
+119.254 -1.143 119.077 -1.029 118.93 -0.882 c
+118.783 -0.727 118.666 -0.536 118.577 -0.309 c
+118.496 -0.085 118.46 0.18 118.46 0.485 c
+118.46 0.816 118.504 1.095 118.592 1.323 c
+118.687 1.558 118.816 1.742 118.974 1.881 c
+119.14 2.018 119.327 2.117 119.533 2.176 c
+119.739 2.242 119.948 2.278 120.165 2.278 c
+120.437 2.278 120.672 2.227 120.871 2.132 c
+121.076 2.043 121.242 1.911 121.37 1.735 c
+121.506 1.565 121.606 1.359 121.664 1.118 c
+121.731 0.882 121.767 0.617 121.767 0.324 c
+121.767 0.309 l
+119.401 0.309 l
+119.401 0.162 119.416 0.022 119.445 -0.103 c
+119.481 -0.231 119.537 -0.345 119.607 -0.441 c
+119.672 -0.529 119.757 -0.599 119.857 -0.646 c
+119.952 -0.698 120.066 -0.721 120.195 -0.721 c
+120.349 -0.721 120.488 -0.687 120.606 -0.617 c
+120.731 -0.551 120.819 -0.449 120.871 -0.309 c
+121.708 -0.382 l
+121.679 -0.482 121.624 -0.588 121.546 -0.706 c
+121.465 -0.816 121.363 -0.919 121.238 -1.014 c
+121.12 -1.103 120.966 -1.176 120.782 -1.234 c
+120.606 -1.294 120.393 -1.323 120.15 -1.323 c
+120.15 1.706 m
+120.062 1.706 119.974 1.69 119.886 1.661 c
+119.798 1.631 119.717 1.58 119.651 1.514 c
+119.581 1.444 119.522 1.356 119.474 1.249 c
+119.433 1.139 119.416 1.014 119.416 0.867 c
+120.885 0.867 l
+120.885 1.003 120.86 1.124 120.812 1.235 c
+120.771 1.341 120.717 1.43 120.65 1.5 c
+120.591 1.565 120.518 1.617 120.43 1.646 c
+120.341 1.683 120.246 1.706 120.15 1.706 c
+124.299 1.602 m
+124.299 -1.264 l
+123.402 -1.264 l
+123.402 1.602 l
+122.579 1.602 l
+122.579 2.219 l
+123.402 2.219 l
+123.402 2.484 l
+123.402 2.609 123.417 2.741 123.447 2.881 c
+123.483 3.017 123.553 3.135 123.653 3.234 c
+123.759 3.341 123.902 3.429 124.079 3.499 c
+124.255 3.564 124.48 3.601 124.755 3.601 c
+124.968 3.601 125.166 3.59 125.343 3.572 c
+125.519 3.549 125.67 3.532 125.798 3.513 c
+125.798 2.925 l
+125.67 2.944 125.527 2.959 125.372 2.969 c
+125.214 2.977 125.064 2.984 124.916 2.984 c
+124.788 2.984 124.685 2.969 124.608 2.94 c
+124.527 2.911 124.465 2.87 124.417 2.822 c
+124.366 2.77 124.332 2.708 124.314 2.631 c
+124.303 2.562 124.299 2.484 124.299 2.396 c
+124.299 2.219 l
+125.725 2.219 l
+125.725 1.602 l
+h
+128.272 -1.323 m
+128.014 -1.323 127.786 -1.286 127.58 -1.22 c
+127.375 -1.143 127.198 -1.029 127.052 -0.882 c
+126.905 -0.727 126.787 -0.536 126.699 -0.309 c
+126.618 -0.085 126.581 0.18 126.581 0.485 c
+126.581 0.816 126.625 1.095 126.714 1.323 c
+126.809 1.558 126.938 1.742 127.096 1.881 c
+127.261 2.018 127.449 2.117 127.655 2.176 c
+127.86 2.242 128.069 2.278 128.286 2.278 c
+128.559 2.278 128.794 2.227 128.991 2.132 c
+129.197 2.043 129.363 1.911 129.492 1.735 c
+129.627 1.565 129.727 1.359 129.785 1.118 c
+129.852 0.882 129.889 0.617 129.889 0.324 c
+129.889 0.309 l
+127.522 0.309 l
+127.522 0.162 127.537 0.022 127.566 -0.103 c
+127.603 -0.231 127.658 -0.345 127.728 -0.441 c
+127.794 -0.529 127.878 -0.599 127.977 -0.646 c
+128.073 -0.698 128.187 -0.721 128.316 -0.721 c
+128.47 -0.721 128.609 -0.687 128.727 -0.617 c
+128.852 -0.551 128.941 -0.449 128.991 -0.309 c
+129.83 -0.382 l
+129.8 -0.482 129.745 -0.588 129.668 -0.706 c
+129.587 -0.816 129.484 -0.919 129.359 -1.014 c
+129.242 -1.103 129.087 -1.176 128.904 -1.234 c
+128.727 -1.294 128.514 -1.323 128.272 -1.323 c
+128.272 1.706 m
+128.183 1.706 128.095 1.69 128.007 1.661 c
+127.919 1.631 127.838 1.58 127.772 1.514 c
+127.702 1.444 127.643 1.356 127.595 1.249 c
+127.555 1.139 127.537 1.014 127.537 0.867 c
+129.006 0.867 l
+129.006 1.003 128.981 1.124 128.933 1.235 c
+128.893 1.341 128.837 1.43 128.771 1.5 c
+128.713 1.565 128.639 1.617 128.551 1.646 c
+128.463 1.683 128.367 1.706 128.272 1.706 c
+133.787 1.47 m
+133.688 1.477 133.585 1.488 133.479 1.5 c
+133.369 1.517 133.247 1.529 133.111 1.529 c
+132.935 1.529 132.777 1.488 132.641 1.411 c
+132.501 1.341 132.384 1.242 132.288 1.118 c
+132.2 0.989 132.13 0.842 132.083 0.676 c
+132.042 0.507 132.023 0.331 132.023 0.147 c
+132.023 -1.264 l
+131.127 -1.264 l
+131.127 0.985 l
+131.127 1.11 131.116 1.235 131.098 1.353 c
+131.086 1.477 131.071 1.595 131.053 1.706 c
+131.042 1.823 131.028 1.918 131.009 1.999 c
+130.988 2.087 130.969 2.161 130.951 2.219 c
+131.832 2.219 l
+131.84 2.168 131.851 2.117 131.862 2.058 c
+131.88 1.999 131.895 1.933 131.906 1.866 c
+131.925 1.808 131.939 1.742 131.95 1.675 c
+131.958 1.606 131.969 1.544 131.979 1.484 c
+131.994 1.484 l
+132.031 1.602 132.083 1.708 132.141 1.808 c
+132.207 1.903 132.288 1.988 132.376 2.058 c
+132.465 2.124 132.567 2.179 132.685 2.219 c
+132.81 2.256 132.957 2.278 133.126 2.278 c
+133.251 2.278 133.369 2.271 133.479 2.263 c
+133.596 2.253 133.699 2.238 133.787 2.219 c
+h
+136.393 -1.323 m
+136.135 -1.323 135.908 -1.286 135.702 -1.22 c
+135.497 -1.143 135.32 -1.029 135.173 -0.882 c
+135.026 -0.727 134.909 -0.536 134.82 -0.309 c
+134.739 -0.085 134.703 0.18 134.703 0.485 c
+134.703 0.816 134.747 1.095 134.834 1.323 c
+134.93 1.558 135.059 1.742 135.217 1.881 c
+135.382 2.018 135.57 2.117 135.775 2.176 c
+135.981 2.242 136.191 2.278 136.407 2.278 c
+136.679 2.278 136.914 2.227 137.113 2.132 c
+137.319 2.043 137.484 1.911 137.613 1.735 c
+137.749 1.565 137.848 1.359 137.907 1.118 c
+137.973 0.882 138.009 0.617 138.009 0.324 c
+138.009 0.309 l
+135.643 0.309 l
+135.643 0.162 135.657 0.022 135.688 -0.103 c
+135.724 -0.231 135.779 -0.345 135.849 -0.441 c
+135.915 -0.529 136 -0.599 136.099 -0.646 c
+136.195 -0.698 136.308 -0.721 136.437 -0.721 c
+136.592 -0.721 136.731 -0.687 136.848 -0.617 c
+136.974 -0.551 137.062 -0.449 137.113 -0.309 c
+137.951 -0.382 l
+137.922 -0.482 137.866 -0.588 137.789 -0.706 c
+137.708 -0.816 137.606 -0.919 137.481 -1.014 c
+137.363 -1.103 137.209 -1.176 137.025 -1.234 c
+136.848 -1.294 136.635 -1.323 136.393 -1.323 c
+136.393 1.706 m
+136.305 1.706 136.216 1.69 136.128 1.661 c
+136.04 1.631 135.959 1.58 135.893 1.514 c
+135.823 1.444 135.765 1.356 135.717 1.249 c
+135.676 1.139 135.657 1.014 135.657 0.867 c
+137.128 0.867 l
+137.128 1.003 137.102 1.124 137.054 1.235 c
+137.014 1.341 136.958 1.43 136.893 1.5 c
+136.833 1.565 136.76 1.617 136.672 1.646 c
+136.584 1.683 136.488 1.706 136.393 1.706 c
+141.089 -1.264 m
+141.089 0.721 l
+141.089 1.022 141.045 1.242 140.957 1.382 c
+140.876 1.529 140.74 1.602 140.546 1.602 c
+140.435 1.602 140.332 1.577 140.237 1.529 c
+140.149 1.477 140.068 1.411 140.002 1.323 c
+139.943 1.235 139.891 1.124 139.854 1 c
+139.825 0.882 139.81 0.75 139.81 0.603 c
+139.81 -1.264 l
+138.899 -1.264 l
+138.899 1.44 l
+138.899 1.661 l
+138.899 1.749 138.892 1.826 138.884 1.897 c
+138.884 2.087 l
+138.884 2.219 l
+139.737 2.219 l
+139.744 2.19 139.752 2.146 139.752 2.087 c
+139.752 1.897 l
+139.759 1.826 139.767 1.756 139.767 1.69 c
+139.773 1.621 139.781 1.565 139.781 1.529 c
+139.796 1.529 l
+139.913 1.793 140.064 1.984 140.251 2.102 c
+140.435 2.219 140.656 2.278 140.912 2.278 c
+141.097 2.278 141.259 2.249 141.398 2.19 c
+141.534 2.132 141.648 2.043 141.736 1.926 c
+141.824 1.808 141.886 1.665 141.927 1.5 c
+141.975 1.341 142.001 1.154 142.001 0.941 c
+142.001 -1.264 l
+h
+144.517 -1.323 m
+144.231 -1.323 143.989 -1.282 143.783 -1.205 c
+143.577 -1.117 143.404 -0.996 143.268 -0.838 c
+143.129 -0.684 143.025 -0.496 142.959 -0.279 c
+142.89 -0.056 142.857 0.191 142.857 0.455 c
+142.857 0.75 142.89 1.007 142.959 1.235 c
+143.037 1.459 143.143 1.646 143.283 1.793 c
+143.43 1.947 143.607 2.065 143.812 2.146 c
+144.018 2.234 144.253 2.278 144.517 2.278 c
+144.742 2.278 144.943 2.249 145.12 2.19 c
+145.297 2.132 145.448 2.047 145.576 1.941 c
+145.701 1.841 145.804 1.72 145.884 1.573 c
+145.962 1.433 146.017 1.282 146.046 1.118 c
+145.135 1.073 l
+145.105 1.249 145.036 1.389 144.929 1.5 c
+144.83 1.606 144.687 1.661 144.503 1.661 c
+144.257 1.661 144.081 1.558 143.974 1.353 c
+143.864 1.154 143.812 0.867 143.812 0.485 c
+143.812 -0.309 144.047 -0.706 144.517 -0.706 c
+144.683 -0.706 144.827 -0.654 144.943 -0.544 c
+145.062 -0.437 145.135 -0.276 145.165 -0.058 c
+146.076 -0.103 l
+146.046 -0.272 145.991 -0.426 145.914 -0.573 c
+145.845 -0.721 145.741 -0.852 145.606 -0.97 c
+145.477 -1.08 145.319 -1.168 145.135 -1.234 c
+144.959 -1.294 144.753 -1.323 144.517 -1.323 c
+148.578 -1.323 m
+148.321 -1.323 148.093 -1.286 147.887 -1.22 c
+147.682 -1.143 147.505 -1.029 147.358 -0.882 c
+147.212 -0.727 147.094 -0.536 147.006 -0.309 c
+146.925 -0.085 146.888 0.18 146.888 0.485 c
+146.888 0.816 146.932 1.095 147.021 1.323 c
+147.116 1.558 147.245 1.742 147.403 1.881 c
+147.568 2.018 147.755 2.117 147.961 2.176 c
+148.166 2.242 148.376 2.278 148.593 2.278 c
+148.865 2.278 149.1 2.227 149.298 2.132 c
+149.504 2.043 149.67 1.911 149.799 1.735 c
+149.934 1.565 150.034 1.359 150.092 1.118 c
+150.158 0.882 150.196 0.617 150.196 0.324 c
+150.196 0.309 l
+147.829 0.309 l
+147.829 0.162 147.844 0.022 147.873 -0.103 c
+147.91 -0.231 147.964 -0.345 148.035 -0.441 c
+148.101 -0.529 148.185 -0.599 148.284 -0.646 c
+148.38 -0.698 148.494 -0.721 148.623 -0.721 c
+148.777 -0.721 148.916 -0.687 149.034 -0.617 c
+149.159 -0.551 149.247 -0.449 149.298 -0.309 c
+150.136 -0.382 l
+150.107 -0.482 150.052 -0.588 149.975 -0.706 c
+149.894 -0.816 149.791 -0.919 149.666 -1.014 c
+149.548 -1.103 149.394 -1.176 149.211 -1.234 c
+149.034 -1.294 148.821 -1.323 148.578 -1.323 c
+148.578 1.706 m
+148.49 1.706 148.402 1.69 148.314 1.661 c
+148.226 1.631 148.145 1.58 148.079 1.514 c
+148.008 1.444 147.95 1.356 147.902 1.249 c
+147.862 1.139 147.844 1.014 147.844 0.867 c
+149.313 0.867 l
+149.313 1.003 149.288 1.124 149.24 1.235 c
+149.199 1.341 149.144 1.43 149.078 1.5 c
+149.02 1.565 148.945 1.617 148.858 1.646 c
+148.769 1.683 148.674 1.706 148.578 1.706 c
+151.449 -2.631 m
+151.449 -2.014 l
+152.521 -2.014 l
+152.521 2.896 l
+151.449 2.896 l
+151.449 3.513 l
+153.373 3.513 l
+153.373 -2.631 l
+h
+f
+Q
+q 1 0 0 1 296.7449 285.9641 cm
+0 0 m
+0 0.264 -0.073 0.463 -0.22 0.602 c
+-0.36 0.75 -0.617 0.889 -0.999 1.028 c
+-1.374 1.165 -1.661 1.308 -1.866 1.455 c
+-2.065 1.602 -2.212 1.768 -2.308 1.955 c
+-2.407 2.15 -2.454 2.37 -2.454 2.616 c
+-2.454 3.035 -2.315 3.384 -2.028 3.659 c
+-1.745 3.931 -1.378 4.072 -0.926 4.072 c
+-0.613 4.072 -0.334 3.998 -0.088 3.85 c
+0.154 3.711 0.341 3.516 0.47 3.262 c
+0.607 3.017 0.676 2.749 0.676 2.454 c
+0 2.454 l
+0 2.786 -0.081 3.042 -0.235 3.219 c
+-0.393 3.403 -0.625 3.498 -0.926 3.498 c
+-1.191 3.498 -1.404 3.417 -1.558 3.262 c
+-1.705 3.116 -1.779 2.903 -1.779 2.631 c
+-1.779 2.403 -1.702 2.212 -1.544 2.057 c
+-1.378 1.911 -1.124 1.77 -0.779 1.646 c
+-0.261 1.477 0.11 1.267 0.338 1.014 c
+0.573 0.756 0.69 0.426 0.69 0.014 c
+0.69 -0.426 0.548 -0.779 0.264 -1.044 c
+-0.022 -1.301 -0.405 -1.426 -0.882 -1.426 c
+-1.198 -1.426 -1.484 -1.357 -1.749 -1.22 c
+-2.014 -1.085 -2.227 -0.894 -2.381 -0.647 c
+-2.528 -0.405 -2.602 -0.118 -2.602 0.205 c
+-1.926 0.205 l
+-1.926 -0.129 -1.834 -0.389 -1.646 -0.574 c
+-1.463 -0.761 -1.205 -0.853 -0.882 -0.853 c
+-0.588 -0.853 -0.368 -0.779 -0.22 -0.632 c
+-0.073 -0.478 0 -0.265 0 0 c
+4.439 -0.206 m
+5.041 2.631 l
+5.689 2.631 l
+4.704 -1.353 l
+4.189 -1.353 l
+3.41 1.499 l
+2.66 -1.353 l
+2.132 -1.353 l
+1.176 2.631 l
+1.808 2.631 l
+2.425 -0.133 l
+3.16 2.631 l
+3.675 2.631 l
+h
+7.071 -1.353 -0.647 3.984 re
+7.115 3.675 m
+7.115 3.564 7.085 3.472 7.026 3.395 c
+6.967 3.326 6.872 3.293 6.747 3.293 c
+6.629 3.293 6.533 3.326 6.468 3.395 c
+6.409 3.472 6.379 3.564 6.379 3.675 c
+6.379 3.792 6.409 3.884 6.468 3.954 c
+6.533 4.031 6.629 4.072 6.747 4.072 c
+6.872 4.072 6.967 4.031 7.026 3.954 c
+7.085 3.873 7.115 3.781 7.115 3.675 c
+8.937 3.586 m
+8.937 2.631 l
+9.54 2.631 l
+9.54 2.102 l
+8.937 2.102 l
+8.937 -0.368 l
+8.937 -0.526 8.96 -0.643 9.01 -0.721 c
+9.07 -0.802 9.157 -0.838 9.275 -0.838 c
+9.363 -0.838 9.452 -0.823 9.54 -0.794 c
+9.54 -1.353 l
+9.392 -1.401 9.238 -1.426 9.084 -1.426 c
+8.827 -1.426 8.632 -1.334 8.496 -1.147 c
+8.357 -0.963 8.291 -0.702 8.291 -0.368 c
+8.291 2.102 l
+7.688 2.102 l
+7.688 2.631 l
+8.291 2.631 l
+8.291 3.586 l
+h
+11.686 -0.867 m
+11.899 -0.867 12.072 -0.805 12.2 -0.676 c
+12.336 -0.541 12.409 -0.349 12.421 -0.103 c
+13.038 -0.103 l
+13.016 -0.485 12.88 -0.805 12.627 -1.058 c
+12.37 -1.305 12.057 -1.426 11.686 -1.426 c
+11.194 -1.426 10.818 -1.276 10.554 -0.971 c
+10.296 -0.659 10.172 -0.191 10.172 0.426 c
+10.172 0.867 l
+10.172 1.462 10.296 1.918 10.554 2.234 c
+10.818 2.547 11.194 2.705 11.686 2.705 c
+12.087 2.705 12.406 2.572 12.642 2.308 c
+12.883 2.05 13.016 1.705 13.038 1.263 c
+12.421 1.263 l
+12.399 1.558 12.326 1.778 12.2 1.926 c
+12.083 2.072 11.91 2.146 11.686 2.146 c
+11.391 2.146 11.175 2.046 11.039 1.851 c
+10.899 1.664 10.826 1.356 10.818 0.926 c
+10.818 0.411 l
+10.818 -0.059 10.884 -0.393 11.025 -0.588 c
+11.171 -0.776 11.391 -0.867 11.686 -0.867 c
+14.435 2.219 m
+14.688 2.543 15.008 2.705 15.39 2.705 c
+16.095 2.705 16.452 2.234 16.463 1.294 c
+16.463 -1.353 l
+15.817 -1.353 l
+15.817 1.263 l
+15.817 1.576 15.761 1.797 15.655 1.926 c
+15.545 2.05 15.39 2.117 15.184 2.117 c
+15.026 2.117 14.88 2.061 14.743 1.955 c
+14.614 1.845 14.512 1.708 14.435 1.543 c
+14.435 -1.353 l
+13.787 -1.353 l
+13.787 4.292 l
+14.435 4.292 l
+h
+20.52 -0.867 m
+20.733 -0.867 20.906 -0.805 21.034 -0.676 c
+21.17 -0.541 21.244 -0.349 21.255 -0.103 c
+21.872 -0.103 l
+21.851 -0.485 21.714 -0.805 21.461 -1.058 c
+21.203 -1.305 20.891 -1.426 20.52 -1.426 c
+20.027 -1.426 19.653 -1.276 19.388 -0.971 c
+19.131 -0.659 19.006 -0.191 19.006 0.426 c
+19.006 0.867 l
+19.006 1.462 19.131 1.918 19.388 2.234 c
+19.653 2.547 20.027 2.705 20.52 2.705 c
+20.92 2.705 21.24 2.572 21.475 2.308 c
+21.718 2.05 21.851 1.705 21.872 1.263 c
+21.255 1.263 l
+21.232 1.558 21.159 1.778 21.034 1.926 c
+20.916 2.072 20.744 2.146 20.52 2.146 c
+20.226 2.146 20.009 2.046 19.873 1.851 c
+19.734 1.664 19.66 1.356 19.653 0.926 c
+19.653 0.411 l
+19.653 -0.059 19.719 -0.393 19.858 -0.588 c
+20.006 -0.776 20.226 -0.867 20.52 -0.867 c
+24.65 -1 m
+24.434 -1.287 24.121 -1.426 23.709 -1.426 c
+23.346 -1.426 23.071 -1.305 22.886 -1.058 c
+22.71 -0.805 22.614 -0.441 22.607 0.029 c
+22.607 2.631 l
+23.254 2.631 l
+23.254 0.087 l
+23.254 -0.541 23.438 -0.853 23.813 -0.853 c
+24.213 -0.853 24.488 -0.676 24.636 -0.324 c
+24.636 2.631 l
+25.282 2.631 l
+25.282 -1.353 l
+24.665 -1.353 l
+h
+27.914 2.013 m
+27.825 2.032 27.726 2.043 27.619 2.043 c
+27.285 2.043 27.05 1.859 26.914 1.499 c
+26.914 -1.353 l
+26.267 -1.353 l
+26.267 2.631 l
+26.899 2.631 l
+26.914 2.219 l
+27.09 2.543 27.333 2.705 27.649 2.705 c
+27.756 2.705 27.844 2.682 27.914 2.645 c
+h
+30.206 2.013 m
+30.119 2.032 30.019 2.043 29.913 2.043 c
+29.578 2.043 29.343 1.859 29.207 1.499 c
+29.207 -1.353 l
+28.56 -1.353 l
+28.56 2.631 l
+29.192 2.631 l
+29.207 2.219 l
+29.383 2.543 29.626 2.705 29.942 2.705 c
+30.048 2.705 30.137 2.682 30.206 2.645 c
+h
+32.205 -1.426 m
+31.706 -1.426 31.324 -1.279 31.059 -0.985 c
+30.794 -0.691 30.662 -0.258 30.662 0.323 c
+30.662 0.793 l
+30.662 1.389 30.788 1.855 31.044 2.19 c
+31.309 2.532 31.669 2.705 32.132 2.705 c
+32.592 2.705 32.933 2.55 33.161 2.248 c
+33.396 1.955 33.518 1.492 33.529 0.867 c
+33.529 0.44 l
+31.309 0.44 l
+31.309 0.353 l
+31.309 -0.081 31.386 -0.393 31.544 -0.588 c
+31.71 -0.776 31.941 -0.867 32.235 -0.867 c
+32.43 -0.867 32.602 -0.834 32.75 -0.765 c
+32.897 -0.688 33.032 -0.57 33.161 -0.412 c
+33.499 -0.823 l
+33.213 -1.228 32.783 -1.426 32.205 -1.426 c
+32.132 2.146 m
+31.856 2.146 31.655 2.05 31.53 1.866 c
+31.401 1.679 31.328 1.389 31.309 0.999 c
+32.882 0.999 l
+32.882 1.087 l
+32.86 1.469 32.793 1.738 32.676 1.896 c
+32.558 2.061 32.375 2.146 32.132 2.146 c
+34.925 2.631 m
+34.94 2.19 l
+35.193 2.532 35.517 2.705 35.91 2.705 c
+36.615 2.705 36.972 2.234 36.983 1.294 c
+36.983 -1.353 l
+36.336 -1.353 l
+36.336 1.263 l
+36.336 1.576 36.281 1.797 36.174 1.926 c
+36.064 2.05 35.91 2.117 35.704 2.117 c
+35.546 2.117 35.399 2.061 35.263 1.955 c
+35.135 1.845 35.031 1.708 34.954 1.543 c
+34.954 -1.353 l
+34.308 -1.353 l
+34.308 2.631 l
+h
+38.805 3.586 m
+38.805 2.631 l
+39.409 2.631 l
+39.409 2.102 l
+38.805 2.102 l
+38.805 -0.368 l
+38.805 -0.526 38.828 -0.643 38.879 -0.721 c
+38.938 -0.802 39.026 -0.838 39.143 -0.838 c
+39.232 -0.838 39.32 -0.823 39.409 -0.794 c
+39.409 -1.353 l
+39.261 -1.401 39.107 -1.426 38.952 -1.426 c
+38.695 -1.426 38.501 -1.334 38.364 -1.147 c
+38.225 -0.963 38.159 -0.702 38.159 -0.368 c
+38.159 2.102 l
+37.556 2.102 l
+37.556 2.631 l
+38.159 2.631 l
+38.159 3.586 l
+h
+44.774 0.44 m
+44.774 -0.177 44.66 -0.643 44.435 -0.956 c
+44.219 -1.272 43.895 -1.426 43.465 -1.426 c
+43.043 -1.426 42.73 -1.246 42.524 -0.882 c
+42.495 -1.353 l
+41.892 -1.353 l
+41.892 4.292 l
+42.539 4.292 l
+42.539 2.19 l
+42.752 2.532 43.061 2.705 43.465 2.705 c
+43.895 2.705 44.219 2.547 44.435 2.234 c
+44.66 1.928 44.774 1.462 44.774 0.837 c
+h
+44.126 0.823 m
+44.126 1.294 44.057 1.624 43.921 1.822 c
+43.792 2.017 43.583 2.117 43.289 2.117 c
+42.954 2.117 42.705 1.932 42.539 1.573 c
+42.539 -0.309 l
+42.705 -0.673 42.958 -0.853 43.303 -0.853 c
+43.598 -0.853 43.807 -0.75 43.935 -0.544 c
+44.06 -0.339 44.126 -0.023 44.126 0.411 c
+h
+47.257 2.013 m
+47.17 2.032 47.07 2.043 46.964 2.043 c
+46.63 2.043 46.394 1.859 46.258 1.499 c
+46.258 -1.353 l
+45.611 -1.353 l
+45.611 2.631 l
+46.243 2.631 l
+46.258 2.219 l
+46.434 2.543 46.677 2.705 46.993 2.705 c
+47.1 2.705 47.187 2.682 47.257 2.645 c
+h
+49.801 -1.353 m
+49.76 -1.264 49.734 -1.118 49.727 -0.912 c
+49.491 -1.257 49.198 -1.426 48.845 -1.426 c
+48.481 -1.426 48.198 -1.33 47.993 -1.132 c
+47.794 -0.927 47.698 -0.64 47.698 -0.265 c
+47.698 0.135 47.835 0.455 48.11 0.691 c
+48.382 0.933 48.757 1.058 49.227 1.058 c
+49.712 1.058 l
+49.712 1.484 l
+49.712 1.72 49.657 1.885 49.551 1.984 c
+49.441 2.09 49.279 2.146 49.065 2.146 c
+48.867 2.146 48.706 2.087 48.581 1.969 c
+48.463 1.851 48.404 1.705 48.404 1.529 c
+47.758 1.529 l
+47.758 1.723 47.816 1.914 47.933 2.102 c
+48.059 2.285 48.22 2.433 48.419 2.543 c
+48.625 2.649 48.853 2.705 49.109 2.705 c
+49.51 2.705 49.815 2.601 50.021 2.396 c
+50.235 2.19 50.348 1.896 50.359 1.514 c
+50.359 -0.5 l
+50.359 -0.805 50.395 -1.07 50.476 -1.294 c
+50.476 -1.353 l
+h
+48.933 -0.838 m
+49.098 -0.838 49.25 -0.794 49.389 -0.706 c
+49.536 -0.618 49.643 -0.507 49.712 -0.368 c
+49.712 0.573 l
+49.345 0.573 l
+49.029 0.573 48.786 0.503 48.61 0.367 c
+48.433 0.239 48.346 0.051 48.346 -0.191 c
+48.346 -0.42 48.39 -0.584 48.477 -0.691 c
+48.566 -0.79 48.716 -0.838 48.933 -0.838 c
+51.976 2.631 m
+51.991 2.19 l
+52.244 2.532 52.568 2.705 52.961 2.705 c
+53.666 2.705 54.023 2.234 54.034 1.294 c
+54.034 -1.353 l
+53.387 -1.353 l
+53.387 1.263 l
+53.387 1.576 53.331 1.797 53.225 1.926 c
+53.115 2.05 52.961 2.117 52.755 2.117 c
+52.597 2.117 52.45 2.061 52.314 1.955 c
+52.186 1.845 52.082 1.708 52.005 1.543 c
+52.005 -1.353 l
+51.359 -1.353 l
+51.359 2.631 l
+h
+56.386 -0.867 m
+56.599 -0.867 56.772 -0.805 56.9 -0.676 c
+57.036 -0.541 57.109 -0.349 57.121 -0.103 c
+57.738 -0.103 l
+57.716 -0.485 57.58 -0.805 57.327 -1.058 c
+57.069 -1.305 56.757 -1.426 56.386 -1.426 c
+55.893 -1.426 55.519 -1.276 55.253 -0.971 c
+54.997 -0.659 54.871 -0.191 54.871 0.426 c
+54.871 0.867 l
+54.871 1.462 54.997 1.918 55.253 2.234 c
+55.519 2.547 55.893 2.705 56.386 2.705 c
+56.786 2.705 57.106 2.572 57.341 2.308 c
+57.584 2.05 57.716 1.705 57.738 1.263 c
+57.121 1.263 l
+57.098 1.558 57.025 1.778 56.9 1.926 c
+56.782 2.072 56.61 2.146 56.386 2.146 c
+56.092 2.146 55.875 2.046 55.739 1.851 c
+55.6 1.664 55.526 1.356 55.519 0.926 c
+55.519 0.411 l
+55.519 -0.059 55.585 -0.393 55.724 -0.588 c
+55.871 -0.776 56.092 -0.867 56.386 -0.867 c
+59.134 2.219 m
+59.388 2.543 59.708 2.705 60.09 2.705 c
+60.795 2.705 61.152 2.234 61.163 1.294 c
+61.163 -1.353 l
+60.516 -1.353 l
+60.516 1.263 l
+60.516 1.576 60.461 1.797 60.354 1.926 c
+60.244 2.05 60.09 2.117 59.884 2.117 c
+59.726 2.117 59.579 2.061 59.443 1.955 c
+59.315 1.845 59.211 1.708 59.134 1.543 c
+59.134 -1.353 l
+58.488 -1.353 l
+58.488 4.292 l
+59.134 4.292 l
+h
+64.691 3.586 m
+64.691 2.631 l
+65.293 2.631 l
+65.293 2.102 l
+64.691 2.102 l
+64.691 -0.368 l
+64.691 -0.526 64.713 -0.643 64.764 -0.721 c
+64.823 -0.802 64.911 -0.838 65.029 -0.838 c
+65.117 -0.838 65.205 -0.823 65.293 -0.794 c
+65.293 -1.353 l
+65.146 -1.401 64.992 -1.426 64.838 -1.426 c
+64.581 -1.426 64.385 -1.334 64.25 -1.147 c
+64.11 -0.963 64.044 -0.702 64.044 -0.368 c
+64.044 2.102 l
+63.442 2.102 l
+63.442 2.631 l
+64.044 2.631 l
+64.044 3.586 l
+h
+65.852 0.823 m
+65.852 1.4 65.988 1.855 66.264 2.19 c
+66.546 2.532 66.918 2.705 67.38 2.705 c
+67.84 2.705 68.207 2.535 68.483 2.204 c
+68.766 1.881 68.913 1.433 68.924 0.867 c
+68.924 0.44 l
+68.924 -0.129 68.78 -0.584 68.498 -0.927 c
+68.222 -1.261 67.855 -1.426 67.395 -1.426 c
+66.933 -1.426 66.561 -1.264 66.278 -0.941 c
+66.002 -0.611 65.859 -0.169 65.852 0.382 c
+h
+66.499 0.44 m
+66.499 0.037 66.576 -0.279 66.734 -0.515 c
+66.899 -0.75 67.12 -0.867 67.395 -0.867 c
+67.961 -0.867 68.255 -0.456 68.277 0.367 c
+68.277 0.823 l
+68.277 1.223 68.192 1.543 68.028 1.778 c
+67.87 2.021 67.652 2.146 67.38 2.146 c
+67.116 2.146 66.899 2.021 66.734 1.778 c
+66.576 1.543 66.499 1.223 66.499 0.823 c
+h
+72.334 3.586 m
+72.334 2.631 l
+72.936 2.631 l
+72.936 2.102 l
+72.334 2.102 l
+72.334 -0.368 l
+72.334 -0.526 72.356 -0.643 72.408 -0.721 c
+72.466 -0.802 72.555 -0.838 72.672 -0.838 c
+72.761 -0.838 72.849 -0.823 72.936 -0.794 c
+72.936 -1.353 l
+72.79 -1.401 72.635 -1.426 72.481 -1.426 c
+72.224 -1.426 72.03 -1.334 71.893 -1.147 c
+71.754 -0.963 71.687 -0.702 71.687 -0.368 c
+71.687 2.102 l
+71.085 2.102 l
+71.085 2.631 l
+71.687 2.631 l
+71.687 3.586 l
+h
+74.348 2.219 m
+74.602 2.543 74.921 2.705 75.303 2.705 c
+76.009 2.705 76.365 2.234 76.377 1.294 c
+76.377 -1.353 l
+75.73 -1.353 l
+75.73 1.263 l
+75.73 1.576 75.674 1.797 75.568 1.926 c
+75.458 2.05 75.303 2.117 75.097 2.117 c
+74.939 2.117 74.793 2.061 74.656 1.955 c
+74.528 1.845 74.425 1.708 74.348 1.543 c
+74.348 -1.353 l
+73.701 -1.353 l
+73.701 4.292 l
+74.348 4.292 l
+h
+78.758 -1.426 m
+78.258 -1.426 77.875 -1.279 77.611 -0.985 c
+77.347 -0.691 77.214 -0.258 77.214 0.323 c
+77.214 0.793 l
+77.214 1.389 77.339 1.855 77.596 2.19 c
+77.861 2.532 78.222 2.705 78.684 2.705 c
+79.143 2.705 79.485 2.55 79.713 2.248 c
+79.948 1.955 80.069 1.492 80.08 0.867 c
+80.08 0.44 l
+77.861 0.44 l
+77.861 0.353 l
+77.861 -0.081 77.938 -0.393 78.096 -0.588 c
+78.261 -0.776 78.493 -0.867 78.787 -0.867 c
+78.982 -0.867 79.155 -0.834 79.301 -0.765 c
+79.448 -0.688 79.585 -0.57 79.713 -0.412 c
+80.051 -0.823 l
+79.764 -1.228 79.334 -1.426 78.758 -1.426 c
+78.684 2.146 m
+78.409 2.146 78.206 2.05 78.081 1.866 c
+77.952 1.679 77.879 1.389 77.861 0.999 c
+79.434 0.999 l
+79.434 1.087 l
+79.412 1.469 79.346 1.738 79.228 1.896 c
+79.111 2.061 78.927 2.146 78.684 2.146 c
+f
+Q
+q 1 0 0 1 380.4265 289.5652 cm
+0 0 m
+0 -0.97 l
+0.53 -0.97 l
+0.53 -1.764 l
+0 -1.764 l
+0 -3.734 l
+0 -3.892 0.019 -3.998 0.059 -4.057 c
+0.107 -4.116 0.191 -4.145 0.309 -4.145 c
+0.416 -4.145 0.5 -4.138 0.559 -4.116 c
+0.559 -4.925 l
+0.382 -4.991 0.191 -5.027 -0.014 -5.027 c
+-0.691 -5.027 -1.036 -4.642 -1.043 -3.866 c
+-1.043 -1.764 l
+-1.5 -1.764 l
+-1.5 -0.97 l
+-1.043 -0.97 l
+-1.043 0 l
+h
+2.984 -4.954 m
+2.955 -4.895 2.926 -4.792 2.896 -4.645 c
+2.708 -4.902 2.459 -5.027 2.146 -5.027 c
+1.812 -5.027 1.532 -4.921 1.309 -4.704 c
+1.091 -4.48 0.985 -4.189 0.985 -3.836 c
+0.985 -3.425 1.118 -3.109 1.382 -2.882 c
+1.646 -2.646 2.028 -2.529 2.529 -2.529 c
+2.851 -2.529 l
+2.851 -2.205 l
+2.851 -2.028 2.815 -1.908 2.749 -1.837 c
+2.69 -1.76 2.602 -1.72 2.484 -1.72 c
+2.227 -1.72 2.103 -1.874 2.103 -2.176 c
+1.058 -2.176 l
+1.058 -1.804 1.195 -1.5 1.47 -1.264 c
+1.742 -1.022 2.091 -0.897 2.514 -0.897 c
+2.955 -0.897 3.293 -1.014 3.528 -1.249 c
+3.77 -1.478 3.896 -1.801 3.896 -2.22 c
+3.896 -4.087 l
+3.896 -4.432 3.944 -4.7 4.042 -4.895 c
+4.042 -4.954 l
+h
+2.381 -4.204 m
+2.488 -4.204 2.58 -4.185 2.66 -4.145 c
+2.749 -4.098 2.811 -4.039 2.851 -3.969 c
+2.851 -3.146 l
+2.602 -3.146 l
+2.425 -3.146 2.282 -3.204 2.176 -3.322 c
+2.076 -3.433 2.028 -3.58 2.028 -3.763 c
+2.028 -4.057 2.146 -4.204 2.381 -4.204 c
+6.6 -1.985 m
+6.262 -1.955 l
+5.976 -1.955 5.785 -2.08 5.689 -2.323 c
+5.689 -4.954 l
+4.645 -4.954 l
+4.645 -0.97 l
+5.615 -0.97 l
+5.644 -1.411 l
+5.81 -1.07 6.041 -0.897 6.336 -0.897 c
+6.453 -0.897 6.545 -0.919 6.614 -0.956 c
+h
+6.938 -2.837 m
+6.938 -2.19 7.056 -1.706 7.291 -1.382 c
+7.526 -1.058 7.857 -0.897 8.291 -0.897 c
+8.643 -0.897 8.915 -1.04 9.114 -1.323 c
+9.158 -0.97 l
+10.099 -0.97 l
+10.099 -4.954 l
+10.099 -5.461 9.955 -5.85 9.672 -6.115 c
+9.386 -6.387 8.981 -6.527 8.453 -6.527 c
+8.224 -6.527 7.989 -6.483 7.747 -6.394 c
+7.512 -6.306 7.335 -6.192 7.217 -6.057 c
+7.57 -5.336 l
+7.666 -5.442 7.794 -5.527 7.952 -5.586 c
+8.106 -5.652 8.254 -5.689 8.393 -5.689 c
+8.628 -5.689 8.794 -5.63 8.893 -5.513 c
+9 -5.402 9.055 -5.226 9.055 -4.983 c
+9.055 -4.63 l
+8.856 -4.895 8.599 -5.027 8.276 -5.027 c
+7.853 -5.027 7.526 -4.866 7.291 -4.542 c
+7.063 -4.212 6.945 -3.741 6.938 -3.131 c
+h
+7.982 -3.102 m
+7.982 -3.477 8.029 -3.745 8.129 -3.91 c
+8.224 -4.079 8.378 -4.16 8.584 -4.16 c
+8.798 -4.16 8.956 -4.083 9.055 -3.925 c
+9.055 -2.028 l
+8.945 -1.864 8.79 -1.779 8.584 -1.779 c
+8.378 -1.779 8.224 -1.864 8.129 -2.028 c
+8.029 -2.198 7.982 -2.466 7.982 -2.837 c
+h
+12.465 -5.027 m
+11.936 -5.027 11.517 -4.873 11.215 -4.557 c
+10.922 -4.233 10.774 -3.774 10.774 -3.175 c
+10.774 -2.866 l
+10.774 -2.242 10.911 -1.756 11.186 -1.411 c
+11.458 -1.07 11.851 -0.897 12.362 -0.897 c
+12.862 -0.897 13.233 -1.058 13.479 -1.382 c
+13.733 -1.706 13.865 -2.183 13.876 -2.808 c
+13.876 -3.308 l
+11.803 -3.308 l
+11.822 -3.601 11.884 -3.818 11.994 -3.954 c
+12.112 -4.094 12.293 -4.16 12.538 -4.16 c
+12.881 -4.16 13.17 -4.042 13.406 -3.807 c
+13.818 -4.439 l
+13.689 -4.615 13.501 -4.759 13.259 -4.866 c
+13.012 -4.972 12.748 -5.027 12.465 -5.027 c
+11.819 -2.587 m
+12.847 -2.587 l
+12.847 -2.484 l
+12.847 -2.249 12.807 -2.072 12.729 -1.955 c
+12.66 -1.831 12.532 -1.764 12.347 -1.764 c
+12.171 -1.764 12.039 -1.834 11.95 -1.97 c
+11.869 -2.099 11.826 -2.304 11.819 -2.587 c
+15.669 0 m
+15.669 -0.97 l
+16.199 -0.97 l
+16.199 -1.764 l
+15.669 -1.764 l
+15.669 -3.734 l
+15.669 -3.892 15.688 -3.998 15.728 -4.057 c
+15.776 -4.116 15.861 -4.145 15.978 -4.145 c
+16.085 -4.145 16.169 -4.138 16.228 -4.116 c
+16.228 -4.925 l
+16.052 -4.991 15.861 -5.027 15.655 -5.027 c
+14.978 -5.027 14.633 -4.642 14.626 -3.866 c
+14.626 -1.764 l
+14.17 -1.764 l
+14.17 -0.97 l
+14.626 -0.97 l
+14.626 0 l
+h
+20.461 -1.985 m
+20.123 -1.955 l
+19.836 -1.955 19.645 -2.08 19.549 -2.323 c
+19.549 -4.954 l
+18.506 -4.954 l
+18.506 -0.97 l
+19.476 -0.97 l
+19.506 -1.411 l
+19.671 -1.07 19.902 -0.897 20.197 -0.897 c
+20.314 -0.897 20.406 -0.919 20.476 -0.956 c
+h
+22.534 -5.027 m
+22.005 -5.027 21.586 -4.873 21.284 -4.557 c
+20.991 -4.233 20.843 -3.774 20.843 -3.175 c
+20.843 -2.866 l
+20.843 -2.242 20.979 -1.756 21.255 -1.411 c
+21.527 -1.07 21.92 -0.897 22.431 -0.897 c
+22.931 -0.897 23.302 -1.058 23.548 -1.382 c
+23.802 -1.706 23.934 -2.183 23.945 -2.808 c
+23.945 -3.308 l
+21.872 -3.308 l
+21.891 -3.601 21.953 -3.818 22.063 -3.954 c
+22.181 -4.094 22.361 -4.16 22.607 -4.16 c
+22.949 -4.16 23.239 -4.042 23.474 -3.807 c
+23.886 -4.439 l
+23.757 -4.615 23.57 -4.759 23.328 -4.866 c
+23.081 -4.972 22.817 -5.027 22.534 -5.027 c
+21.887 -2.587 m
+22.916 -2.587 l
+22.916 -2.484 l
+22.916 -2.249 22.876 -2.072 22.799 -1.955 c
+22.728 -1.831 22.6 -1.764 22.416 -1.764 c
+22.24 -1.764 22.107 -1.834 22.019 -1.97 c
+21.939 -2.099 21.895 -2.304 21.887 -2.587 c
+24.768 -4.954 m
+24.768 -1.764 l
+24.283 -1.764 l
+24.283 -0.97 l
+24.768 -0.97 l
+24.768 -0.617 l
+24.768 -0.177 24.879 0.162 25.106 0.397 c
+25.342 0.639 25.658 0.764 26.061 0.764 c
+26.186 0.764 26.344 0.738 26.532 0.69 c
+26.532 -0.133 l
+26.462 -0.114 26.377 -0.104 26.282 -0.104 c
+25.966 -0.104 25.812 -0.287 25.812 -0.647 c
+25.812 -0.97 l
+26.429 -0.97 l
+26.429 -1.764 l
+25.812 -1.764 l
+25.812 -4.954 l
+h
+28.517 -5.027 m
+27.987 -5.027 27.568 -4.873 27.267 -4.557 c
+26.973 -4.233 26.826 -3.774 26.826 -3.175 c
+26.826 -2.866 l
+26.826 -2.242 26.962 -1.756 27.237 -1.411 c
+27.509 -1.07 27.902 -0.897 28.413 -0.897 c
+28.914 -0.897 29.284 -1.058 29.531 -1.382 c
+29.784 -1.706 29.916 -2.183 29.928 -2.808 c
+29.928 -3.308 l
+27.855 -3.308 l
+27.873 -3.601 27.936 -3.818 28.046 -3.954 c
+28.164 -4.094 28.344 -4.16 28.59 -4.16 c
+28.931 -4.16 29.222 -4.042 29.457 -3.807 c
+29.868 -4.439 l
+29.741 -4.615 29.552 -4.759 29.31 -4.866 c
+29.064 -4.972 28.8 -5.027 28.517 -5.027 c
+27.869 -2.587 m
+28.899 -2.587 l
+28.899 -2.484 l
+28.899 -2.249 28.858 -2.072 28.781 -1.955 c
+28.711 -1.831 28.582 -1.764 28.399 -1.764 c
+28.222 -1.764 28.09 -1.834 28.002 -1.97 c
+27.921 -2.099 27.877 -2.304 27.869 -2.587 c
+32.471 -1.985 m
+32.132 -1.955 l
+31.846 -1.955 31.655 -2.08 31.559 -2.323 c
+31.559 -4.954 l
+30.516 -4.954 l
+30.516 -0.97 l
+31.486 -0.97 l
+31.515 -1.411 l
+31.68 -1.07 31.912 -0.897 32.206 -0.897 c
+32.324 -0.897 32.415 -0.919 32.485 -0.956 c
+h
+34.543 -5.027 m
+34.013 -5.027 33.595 -4.873 33.294 -4.557 c
+32.999 -4.233 32.853 -3.774 32.853 -3.175 c
+32.853 -2.866 l
+32.853 -2.242 32.989 -1.756 33.265 -1.411 c
+33.536 -1.07 33.93 -0.897 34.44 -0.897 c
+34.94 -0.897 35.311 -1.058 35.557 -1.382 c
+35.811 -1.706 35.943 -2.183 35.954 -2.808 c
+35.954 -3.308 l
+33.882 -3.308 l
+33.9 -3.601 33.963 -3.818 34.073 -3.954 c
+34.19 -4.094 34.37 -4.16 34.616 -4.16 c
+34.958 -4.16 35.249 -4.042 35.484 -3.807 c
+35.895 -4.439 l
+35.767 -4.615 35.58 -4.759 35.337 -4.866 c
+35.091 -4.972 34.826 -5.027 34.543 -5.027 c
+33.896 -2.587 m
+34.925 -2.587 l
+34.925 -2.484 l
+34.925 -2.249 34.884 -2.072 34.807 -1.955 c
+34.738 -1.831 34.609 -1.764 34.425 -1.764 c
+34.249 -1.764 34.117 -1.834 34.028 -1.97 c
+33.948 -2.099 33.903 -2.304 33.896 -2.587 c
+37.498 -0.97 m
+37.527 -1.367 l
+37.762 -1.055 38.063 -0.897 38.439 -0.897 c
+39.122 -0.897 39.474 -1.378 39.497 -2.338 c
+39.497 -4.954 l
+38.453 -4.954 l
+38.453 -2.411 l
+38.453 -2.186 38.416 -2.025 38.35 -1.926 c
+38.281 -1.831 38.163 -1.779 37.997 -1.779 c
+37.81 -1.779 37.663 -1.874 37.556 -2.058 c
+37.556 -4.954 l
+36.513 -4.954 l
+36.513 -0.97 l
+h
+41.687 -4.16 m
+41.98 -4.16 42.131 -3.965 42.142 -3.572 c
+43.112 -3.572 l
+43.112 -4.006 42.98 -4.358 42.715 -4.63 c
+42.451 -4.895 42.113 -5.027 41.701 -5.027 c
+41.19 -5.027 40.797 -4.873 40.525 -4.557 c
+40.261 -4.233 40.122 -3.763 40.114 -3.146 c
+40.114 -2.822 l
+40.114 -2.198 40.246 -1.72 40.511 -1.397 c
+40.783 -1.066 41.18 -0.897 41.701 -0.897 c
+42.131 -0.897 42.473 -1.037 42.73 -1.309 c
+42.984 -1.584 43.112 -1.966 43.112 -2.455 c
+42.142 -2.455 l
+42.142 -2.242 42.102 -2.072 42.025 -1.955 c
+41.955 -1.831 41.837 -1.764 41.672 -1.764 c
+41.496 -1.764 41.367 -1.831 41.29 -1.955 c
+41.209 -2.084 41.165 -2.334 41.157 -2.705 c
+41.157 -3.117 l
+41.157 -3.439 41.172 -3.668 41.202 -3.792 c
+41.238 -3.921 41.294 -4.013 41.363 -4.072 c
+41.441 -4.131 41.547 -4.16 41.687 -4.16 c
+45.317 -5.027 m
+44.789 -5.027 44.369 -4.873 44.068 -4.557 c
+43.774 -4.233 43.627 -3.774 43.627 -3.175 c
+43.627 -2.866 l
+43.627 -2.242 43.763 -1.756 44.039 -1.411 c
+44.311 -1.07 44.704 -0.897 45.215 -0.897 c
+45.714 -0.897 46.085 -1.058 46.331 -1.382 c
+46.585 -1.706 46.717 -2.183 46.728 -2.808 c
+46.728 -3.308 l
+44.656 -3.308 l
+44.674 -3.601 44.737 -3.818 44.847 -3.954 c
+44.965 -4.094 45.144 -4.16 45.391 -4.16 c
+45.733 -4.16 46.023 -4.042 46.258 -3.807 c
+46.67 -4.439 l
+46.541 -4.615 46.354 -4.759 46.111 -4.866 c
+45.865 -4.972 45.6 -5.027 45.317 -5.027 c
+44.671 -2.587 m
+45.699 -2.587 l
+45.699 -2.484 l
+45.699 -2.249 45.659 -2.072 45.582 -1.955 c
+45.512 -1.831 45.383 -1.764 45.2 -1.764 c
+45.024 -1.764 44.891 -1.834 44.803 -1.97 c
+44.722 -2.099 44.678 -2.304 44.671 -2.587 c
+f
+Q
+q 1 0 0 1 427.8604 283.5388 cm
+0 0 m
+-0.397 0.264 l
+-0.162 0.588 -0.04 0.922 -0.029 1.263 c
+-0.029 1.881 l
+0.632 1.881 l
+0.632 1.352 l
+0.632 1.095 0.566 0.849 0.441 0.602 c
+0.324 0.359 0.177 0.158 0 0 c
+5.233 1.072 m
+5.193 1.161 5.167 1.308 5.159 1.514 c
+4.924 1.168 4.63 0.999 4.278 0.999 c
+3.913 0.999 3.63 1.095 3.425 1.294 c
+3.227 1.499 3.131 1.786 3.131 2.16 c
+3.131 2.561 3.267 2.881 3.543 3.116 c
+3.815 3.358 4.189 3.484 4.659 3.484 c
+5.145 3.484 l
+5.145 3.91 l
+5.145 4.145 5.089 4.31 4.983 4.409 c
+4.873 4.516 4.711 4.571 4.498 4.571 c
+4.299 4.571 4.138 4.512 4.013 4.394 c
+3.896 4.277 3.836 4.13 3.836 3.954 c
+3.19 3.954 l
+3.19 4.149 3.248 4.34 3.366 4.527 c
+3.491 4.71 3.653 4.858 3.851 4.968 c
+4.057 5.074 4.285 5.13 4.542 5.13 c
+4.943 5.13 5.247 5.026 5.453 4.821 c
+5.667 4.615 5.781 4.321 5.791 3.939 c
+5.791 1.925 l
+5.791 1.62 5.828 1.356 5.909 1.132 c
+5.909 1.072 l
+h
+4.366 1.587 m
+4.531 1.587 4.682 1.631 4.821 1.72 c
+4.968 1.807 5.075 1.918 5.145 2.057 c
+5.145 2.998 l
+4.777 2.998 l
+4.461 2.998 4.218 2.929 4.042 2.792 c
+3.866 2.664 3.778 2.476 3.778 2.234 c
+3.778 2.006 3.822 1.841 3.91 1.734 c
+3.998 1.635 4.149 1.587 4.366 1.587 c
+7.408 5.056 m
+7.423 4.615 l
+7.676 4.957 8 5.13 8.393 5.13 c
+9.099 5.13 9.455 4.659 9.467 3.719 c
+9.467 1.072 l
+8.819 1.072 l
+8.819 3.689 l
+8.819 4.001 8.764 4.222 8.658 4.351 c
+8.548 4.475 8.393 4.542 8.187 4.542 c
+8.029 4.542 7.882 4.486 7.746 4.38 c
+7.618 4.27 7.515 4.134 7.437 3.968 c
+7.437 1.072 l
+6.791 1.072 l
+6.791 5.056 l
+h
+10.304 3.248 m
+10.304 3.854 10.414 4.321 10.643 4.644 c
+10.878 4.968 11.204 5.13 11.627 5.13 c
+12.009 5.13 12.307 4.972 12.524 4.659 c
+12.524 6.717 l
+13.17 6.717 l
+13.17 1.072 l
+12.582 1.072 l
+12.538 1.499 l
+12.332 1.165 12.027 0.999 11.627 0.999 c
+11.215 0.999 10.892 1.153 10.657 1.469 c
+10.422 1.793 10.304 2.248 10.304 2.836 c
+h
+10.951 2.865 m
+10.951 2.425 11.013 2.094 11.142 1.881 c
+11.278 1.675 11.499 1.572 11.803 1.572 c
+12.127 1.572 12.366 1.734 12.524 2.057 c
+12.524 4.072 l
+12.355 4.384 12.116 4.542 11.803 4.542 c
+11.499 4.542 11.278 4.438 11.142 4.233 c
+11.013 4.027 10.951 3.704 10.951 3.262 c
+h
+16.596 1.072 -0.647 5.644 re
+19.035 0.999 m
+18.535 0.999 18.153 1.146 17.889 1.44 c
+17.624 1.734 17.492 2.167 17.492 2.748 c
+17.492 3.218 l
+17.492 3.814 17.617 4.28 17.874 4.615 c
+18.138 4.957 18.499 5.13 18.962 5.13 c
+19.422 5.13 19.763 4.976 19.991 4.674 c
+20.226 4.38 20.347 3.917 20.359 3.293 c
+20.359 2.865 l
+18.138 2.865 l
+18.138 2.778 l
+18.138 2.344 18.216 2.032 18.374 1.837 c
+18.539 1.649 18.771 1.558 19.065 1.558 c
+19.26 1.558 19.432 1.591 19.58 1.66 c
+19.726 1.738 19.862 1.855 19.991 2.013 c
+20.328 1.602 l
+20.042 1.198 19.613 0.999 19.035 0.999 c
+18.962 4.571 m
+18.686 4.571 18.485 4.475 18.359 4.292 c
+18.231 4.104 18.157 3.814 18.138 3.424 c
+19.711 3.424 l
+19.711 3.513 l
+19.69 3.895 19.623 4.163 19.505 4.321 c
+19.388 4.486 19.204 4.571 18.962 4.571 c
+23.181 1.072 m
+23.14 1.161 23.114 1.308 23.107 1.514 c
+22.872 1.168 22.578 0.999 22.225 0.999 c
+21.861 0.999 21.579 1.095 21.373 1.294 c
+21.174 1.499 21.078 1.786 21.078 2.16 c
+21.078 2.561 21.215 2.881 21.49 3.116 c
+21.762 3.358 22.137 3.484 22.607 3.484 c
+23.092 3.484 l
+23.092 3.91 l
+23.092 4.145 23.037 4.31 22.931 4.409 c
+22.82 4.516 22.659 4.571 22.445 4.571 c
+22.248 4.571 22.086 4.512 21.961 4.394 c
+21.843 4.277 21.784 4.13 21.784 3.954 c
+21.137 3.954 l
+21.137 4.149 21.196 4.34 21.313 4.527 c
+21.439 4.71 21.6 4.858 21.799 4.968 c
+22.005 5.074 22.232 5.13 22.489 5.13 c
+22.89 5.13 23.195 5.026 23.401 4.821 c
+23.614 4.615 23.728 4.321 23.739 3.939 c
+23.739 1.925 l
+23.739 1.62 23.776 1.356 23.856 1.132 c
+23.856 1.072 l
+h
+22.313 1.587 m
+22.479 1.587 22.629 1.631 22.769 1.72 c
+22.915 1.807 23.023 1.918 23.092 2.057 c
+23.092 2.998 l
+22.725 2.998 l
+22.408 2.998 22.167 2.929 21.99 2.792 c
+21.814 2.664 21.725 2.476 21.725 2.234 c
+21.725 2.006 21.77 1.841 21.857 1.734 c
+21.946 1.635 22.096 1.587 22.313 1.587 c
+25.812 2.057 m
+26.561 5.056 l
+27.223 5.056 l
+26.047 1.072 l
+25.562 1.072 l
+24.371 5.056 l
+25.032 5.056 l
+h
+29.281 0.999 m
+28.781 0.999 28.399 1.146 28.134 1.44 c
+27.869 1.734 27.737 2.167 27.737 2.748 c
+27.737 3.218 l
+27.737 3.814 27.862 4.28 28.12 4.615 c
+28.384 4.957 28.744 5.13 29.207 5.13 c
+29.666 5.13 30.009 4.976 30.236 4.674 c
+30.471 4.38 30.593 3.917 30.603 3.293 c
+30.603 2.865 l
+28.384 2.865 l
+28.384 2.778 l
+28.384 2.344 28.461 2.032 28.619 1.837 c
+28.785 1.649 29.016 1.558 29.31 1.558 c
+29.504 1.558 29.677 1.591 29.824 1.66 c
+29.971 1.738 30.107 1.855 30.236 2.013 c
+30.574 1.602 l
+30.287 1.198 29.857 0.999 29.281 0.999 c
+29.207 4.571 m
+28.931 4.571 28.729 4.475 28.604 4.292 c
+28.476 4.104 28.403 3.814 28.384 3.424 c
+29.957 3.424 l
+29.957 3.513 l
+29.934 3.895 29.868 4.163 29.751 4.321 c
+29.633 4.486 29.45 4.571 29.207 4.571 c
+33.323 2.087 m
+33.323 2.234 33.267 2.355 33.161 2.454 c
+33.051 2.549 32.845 2.667 32.544 2.807 c
+32.199 2.954 31.956 3.075 31.809 3.175 c
+31.662 3.281 31.551 3.399 31.486 3.528 c
+31.416 3.652 31.382 3.81 31.382 3.998 c
+31.382 4.321 31.5 4.589 31.735 4.806 c
+31.97 5.019 32.272 5.13 32.646 5.13 c
+33.028 5.13 33.338 5.016 33.573 4.791 c
+33.808 4.564 33.926 4.277 33.926 3.924 c
+33.279 3.924 l
+33.279 4.101 33.22 4.251 33.103 4.38 c
+32.985 4.505 32.831 4.571 32.646 4.571 c
+32.448 4.571 32.297 4.516 32.191 4.409 c
+32.081 4.31 32.029 4.178 32.029 4.012 c
+32.029 3.883 32.066 3.777 32.147 3.689 c
+32.224 3.608 32.415 3.505 32.72 3.38 c
+33.198 3.193 33.529 3.006 33.705 2.822 c
+33.882 2.645 33.969 2.418 33.969 2.146 c
+33.969 1.793 33.845 1.514 33.602 1.308 c
+33.367 1.102 33.051 0.999 32.662 0.999 c
+32.239 0.999 31.9 1.117 31.647 1.352 c
+31.39 1.595 31.265 1.899 31.265 2.263 c
+31.912 2.263 l
+31.919 2.036 31.989 1.859 32.118 1.734 c
+32.243 1.616 32.426 1.558 32.662 1.558 c
+32.874 1.558 33.036 1.606 33.146 1.705 c
+33.264 1.801 33.323 1.928 33.323 2.087 c
+38.57 1.072 m
+38.53 1.161 38.504 1.308 38.497 1.514 c
+38.262 1.168 37.967 0.999 37.615 0.999 c
+37.251 0.999 36.968 1.095 36.762 1.294 c
+36.564 1.499 36.469 1.786 36.469 2.16 c
+36.469 2.561 36.604 2.881 36.88 3.116 c
+37.152 3.358 37.527 3.484 37.997 3.484 c
+38.482 3.484 l
+38.482 3.91 l
+38.482 4.145 38.427 4.31 38.32 4.409 c
+38.21 4.516 38.048 4.571 37.836 4.571 c
+37.637 4.571 37.475 4.512 37.35 4.394 c
+37.233 4.277 37.174 4.13 37.174 3.954 c
+36.527 3.954 l
+36.527 4.149 36.586 4.34 36.704 4.527 c
+36.829 4.71 36.99 4.858 37.188 4.968 c
+37.394 5.074 37.622 5.13 37.88 5.13 c
+38.281 5.13 38.585 5.026 38.79 4.821 c
+39.004 4.615 39.118 4.321 39.129 3.939 c
+39.129 1.925 l
+39.129 1.62 39.166 1.356 39.247 1.132 c
+39.247 1.072 l
+h
+37.703 1.587 m
+37.869 1.587 38.019 1.631 38.159 1.72 c
+38.306 1.807 38.412 1.918 38.482 2.057 c
+38.482 2.998 l
+38.115 2.998 l
+37.799 2.998 37.556 2.929 37.379 2.792 c
+37.203 2.664 37.115 2.476 37.115 2.234 c
+37.115 2.006 37.159 1.841 37.248 1.734 c
+37.336 1.635 37.487 1.587 37.703 1.587 c
+41.716 3.248 m
+41.716 3.854 41.826 4.321 42.054 4.644 c
+42.289 4.968 42.616 5.13 43.039 5.13 c
+43.421 5.13 43.719 4.972 43.935 4.659 c
+43.935 6.717 l
+44.583 6.717 l
+44.583 1.072 l
+43.995 1.072 l
+43.95 1.499 l
+43.744 1.165 43.44 0.999 43.039 0.999 c
+42.628 0.999 42.304 1.153 42.069 1.469 c
+41.834 1.793 41.716 2.248 41.716 2.836 c
+h
+42.362 2.865 m
+42.362 2.425 42.425 2.094 42.553 1.881 c
+42.69 1.675 42.91 1.572 43.216 1.572 c
+43.538 1.572 43.777 1.734 43.935 2.057 c
+43.935 4.072 l
+43.767 4.384 43.528 4.542 43.216 4.542 c
+42.91 4.542 42.69 4.438 42.553 4.233 c
+42.425 4.027 42.362 3.704 42.362 3.262 c
+h
+46.287 1.072 -0.646 3.984 re
+46.331 6.1 m
+46.331 5.99 46.302 5.898 46.243 5.82 c
+46.185 5.751 46.089 5.718 45.964 5.718 c
+45.846 5.718 45.751 5.751 45.685 5.82 c
+45.626 5.898 45.597 5.99 45.597 6.1 c
+45.597 6.217 45.626 6.31 45.685 6.379 c
+45.751 6.456 45.846 6.497 45.964 6.497 c
+46.089 6.497 46.185 6.456 46.243 6.379 c
+46.302 6.298 46.331 6.206 46.331 6.1 c
+47.596 1.072 m
+47.596 4.527 l
+47.066 4.527 l
+47.066 5.056 l
+47.596 5.056 l
+47.596 5.512 l
+47.596 5.913 47.691 6.225 47.889 6.453 c
+48.095 6.676 48.375 6.79 48.727 6.79 c
+48.864 6.79 48.996 6.769 49.124 6.732 c
+49.095 6.188 l
+48.996 6.206 48.897 6.217 48.801 6.217 c
+48.426 6.217 48.242 5.953 48.242 5.423 c
+48.242 5.056 l
+48.918 5.056 l
+48.918 4.527 l
+48.242 4.527 l
+48.242 1.072 l
+h
+49.977 1.072 m
+49.977 4.527 l
+49.448 4.527 l
+49.448 5.056 l
+49.977 5.056 l
+49.977 5.512 l
+49.977 5.913 50.073 6.225 50.271 6.453 c
+50.476 6.676 50.756 6.79 51.108 6.79 c
+51.245 6.79 51.377 6.769 51.505 6.732 c
+51.476 6.188 l
+51.377 6.206 51.278 6.217 51.182 6.217 c
+50.807 6.217 50.624 5.953 50.624 5.423 c
+50.624 5.056 l
+51.299 5.056 l
+51.299 4.527 l
+50.624 4.527 l
+50.624 1.072 l
+h
+53.402 0.999 m
+52.902 0.999 52.52 1.146 52.255 1.44 c
+51.991 1.734 51.858 2.167 51.858 2.748 c
+51.858 3.218 l
+51.858 3.814 51.983 4.28 52.24 4.615 c
+52.505 4.957 52.865 5.13 53.329 5.13 c
+53.788 5.13 54.129 4.976 54.357 4.674 c
+54.592 4.38 54.713 3.917 54.725 3.293 c
+54.725 2.865 l
+52.505 2.865 l
+52.505 2.778 l
+52.505 2.344 52.583 2.032 52.741 1.837 c
+52.905 1.649 53.137 1.558 53.431 1.558 c
+53.626 1.558 53.799 1.591 53.946 1.66 c
+54.092 1.738 54.229 1.855 54.357 2.013 c
+54.696 1.602 l
+54.409 1.198 53.979 0.999 53.402 0.999 c
+53.329 4.571 m
+53.053 4.571 52.851 4.475 52.726 4.292 c
+52.597 4.104 52.523 3.814 52.505 3.424 c
+54.077 3.424 l
+54.077 3.513 l
+54.056 3.895 53.99 4.163 53.872 4.321 c
+53.755 4.486 53.57 4.571 53.329 4.571 c
+57.15 4.438 m
+57.061 4.457 56.963 4.469 56.856 4.469 c
+56.522 4.469 56.286 4.284 56.151 3.924 c
+56.151 1.072 l
+55.504 1.072 l
+55.504 5.056 l
+56.136 5.056 l
+56.151 4.644 l
+56.327 4.968 56.569 5.13 56.886 5.13 c
+56.992 5.13 57.08 5.107 57.15 5.071 c
+h
+59.149 0.999 m
+58.649 0.999 58.268 1.146 58.002 1.44 c
+57.738 1.734 57.605 2.167 57.605 2.748 c
+57.605 3.218 l
+57.605 3.814 57.73 4.28 57.988 4.615 c
+58.252 4.957 58.613 5.13 59.076 5.13 c
+59.535 5.13 59.877 4.976 60.105 4.674 c
+60.34 4.38 60.461 3.917 60.472 3.293 c
+60.472 2.865 l
+58.252 2.865 l
+58.252 2.778 l
+58.252 2.344 58.33 2.032 58.488 1.837 c
+58.653 1.649 58.885 1.558 59.178 1.558 c
+59.373 1.558 59.546 1.591 59.693 1.66 c
+59.84 1.738 59.976 1.855 60.105 2.013 c
+60.443 1.602 l
+60.156 1.198 59.726 0.999 59.149 0.999 c
+59.076 4.571 m
+58.8 4.571 58.598 4.475 58.473 4.292 c
+58.345 4.104 58.271 3.814 58.252 3.424 c
+59.825 3.424 l
+59.825 3.513 l
+59.803 3.895 59.737 4.163 59.619 4.321 c
+59.502 4.486 59.318 4.571 59.076 4.571 c
+61.869 5.056 m
+61.883 4.615 l
+62.137 4.957 62.46 5.13 62.854 5.13 c
+63.559 5.13 63.916 4.659 63.926 3.719 c
+63.926 1.072 l
+63.28 1.072 l
+63.28 3.689 l
+63.28 4.001 63.224 4.222 63.118 4.351 c
+63.008 4.475 62.854 4.542 62.648 4.542 c
+62.49 4.542 62.343 4.486 62.206 4.38 c
+62.078 4.27 61.975 4.134 61.898 3.968 c
+61.898 1.072 l
+61.251 1.072 l
+61.251 5.056 l
+h
+66.278 1.558 m
+66.491 1.558 66.664 1.62 66.792 1.749 c
+66.929 1.884 67.002 2.076 67.013 2.322 c
+67.631 2.322 l
+67.608 1.94 67.473 1.62 67.219 1.367 c
+66.962 1.12 66.65 0.999 66.278 0.999 c
+65.786 0.999 65.411 1.15 65.146 1.454 c
+64.889 1.767 64.764 2.234 64.764 2.851 c
+64.764 3.293 l
+64.764 3.887 64.889 4.343 65.146 4.659 c
+65.411 4.972 65.786 5.13 66.278 5.13 c
+66.679 5.13 66.999 4.997 67.234 4.733 c
+67.476 4.475 67.608 4.13 67.631 3.689 c
+67.013 3.689 l
+66.991 3.983 66.918 4.203 66.792 4.351 c
+66.675 4.498 66.503 4.571 66.278 4.571 c
+65.984 4.571 65.767 4.471 65.632 4.277 c
+65.491 4.089 65.418 3.781 65.411 3.351 c
+65.411 2.836 l
+65.411 2.366 65.477 2.032 65.617 1.837 c
+65.763 1.649 65.984 1.558 66.278 1.558 c
+69.806 0.999 m
+69.306 0.999 68.924 1.146 68.659 1.44 c
+68.395 1.734 68.263 2.167 68.263 2.748 c
+68.263 3.218 l
+68.263 3.814 68.387 4.28 68.645 4.615 c
+68.909 4.957 69.27 5.13 69.732 5.13 c
+70.191 5.13 70.534 4.976 70.762 4.674 c
+70.997 4.38 71.118 3.917 71.128 3.293 c
+71.128 2.865 l
+68.909 2.865 l
+68.909 2.778 l
+68.909 2.344 68.986 2.032 69.144 1.837 c
+69.31 1.649 69.541 1.558 69.835 1.558 c
+70.031 1.558 70.203 1.591 70.349 1.66 c
+70.497 1.738 70.633 1.855 70.762 2.013 c
+71.099 1.602 l
+70.813 1.198 70.382 0.999 69.806 0.999 c
+69.732 4.571 m
+69.457 4.571 69.254 4.475 69.129 4.292 c
+69.001 4.104 68.928 3.814 68.909 3.424 c
+70.482 3.424 l
+70.482 3.513 l
+70.46 3.895 70.394 4.163 70.276 4.321 c
+70.158 4.486 69.975 4.571 69.732 4.571 c
+75.671 1.072 m
+75.631 1.161 75.604 1.308 75.597 1.514 c
+75.362 1.168 75.068 0.999 74.715 0.999 c
+74.351 0.999 74.068 1.095 73.863 1.294 c
+73.665 1.499 73.569 1.786 73.569 2.16 c
+73.569 2.561 73.705 2.881 73.981 3.116 c
+74.253 3.358 74.627 3.484 75.097 3.484 c
+75.583 3.484 l
+75.583 3.91 l
+75.583 4.145 75.527 4.31 75.421 4.409 c
+75.311 4.516 75.149 4.571 74.936 4.571 c
+74.737 4.571 74.576 4.512 74.451 4.394 c
+74.333 4.277 74.274 4.13 74.274 3.954 c
+73.628 3.954 l
+73.628 4.149 73.686 4.34 73.804 4.527 c
+73.929 4.71 74.091 4.858 74.289 4.968 c
+74.495 5.074 74.723 5.13 74.98 5.13 c
+75.38 5.13 75.685 5.026 75.891 4.821 c
+76.105 4.615 76.219 4.321 76.229 3.939 c
+76.229 1.925 l
+76.229 1.62 76.266 1.356 76.347 1.132 c
+76.347 1.072 l
+h
+74.804 1.587 m
+74.969 1.587 75.12 1.631 75.259 1.72 c
+75.406 1.807 75.513 1.918 75.583 2.057 c
+75.583 2.998 l
+75.215 2.998 l
+74.899 2.998 74.656 2.929 74.48 2.792 c
+74.303 2.664 74.216 2.476 74.216 2.234 c
+74.216 2.006 74.26 1.841 74.348 1.734 c
+74.436 1.635 74.587 1.587 74.804 1.587 c
+79.169 2.087 m
+79.169 2.234 79.114 2.355 79.007 2.454 c
+78.897 2.549 78.692 2.667 78.39 2.807 c
+78.045 2.954 77.802 3.075 77.655 3.175 c
+77.508 3.281 77.398 3.399 77.332 3.528 c
+77.262 3.652 77.229 3.81 77.229 3.998 c
+77.229 4.321 77.347 4.589 77.582 4.806 c
+77.817 5.019 78.118 5.13 78.493 5.13 c
+78.875 5.13 79.184 5.016 79.419 4.791 c
+79.654 4.564 79.772 4.277 79.772 3.924 c
+79.125 3.924 l
+79.125 4.101 79.066 4.251 78.949 4.38 c
+78.831 4.505 78.677 4.571 78.493 4.571 c
+78.295 4.571 78.143 4.516 78.037 4.409 c
+77.927 4.31 77.875 4.178 77.875 4.012 c
+77.875 3.883 77.912 3.777 77.993 3.689 c
+78.07 3.608 78.262 3.505 78.567 3.38 c
+79.045 3.193 79.375 3.006 79.552 2.822 c
+79.728 2.645 79.816 2.418 79.816 2.146 c
+79.816 1.793 79.691 1.514 79.448 1.308 c
+79.213 1.102 78.897 0.999 78.508 0.999 c
+78.085 0.999 77.747 1.117 77.493 1.352 c
+77.237 1.595 77.111 1.899 77.111 2.263 c
+77.758 2.263 l
+77.765 2.036 77.835 1.859 77.964 1.734 c
+78.089 1.616 78.272 1.558 78.508 1.558 c
+78.721 1.558 78.883 1.606 78.993 1.705 c
+79.111 1.801 79.169 1.928 79.169 2.087 c
+84.416 1.072 m
+84.377 1.161 84.35 1.308 84.343 1.514 c
+84.108 1.168 83.814 0.999 83.461 0.999 c
+83.097 0.999 82.814 1.095 82.609 1.294 c
+82.411 1.499 82.315 1.786 82.315 2.16 c
+82.315 2.561 82.451 2.881 82.727 3.116 c
+82.999 3.358 83.373 3.484 83.843 3.484 c
+84.329 3.484 l
+84.329 3.91 l
+84.329 4.145 84.273 4.31 84.167 4.409 c
+84.057 4.516 83.895 4.571 83.682 4.571 c
+83.483 4.571 83.321 4.512 83.197 4.394 c
+83.08 4.277 83.02 4.13 83.02 3.954 c
+82.374 3.954 l
+82.374 4.149 82.432 4.34 82.55 4.527 c
+82.675 4.71 82.837 4.858 83.035 4.968 c
+83.24 5.074 83.469 5.13 83.726 5.13 c
+84.127 5.13 84.431 5.026 84.637 4.821 c
+84.85 4.615 84.964 4.321 84.975 3.939 c
+84.975 1.925 l
+84.975 1.62 85.012 1.356 85.093 1.132 c
+85.093 1.072 l
+h
+83.55 1.587 m
+83.715 1.587 83.866 1.631 84.005 1.72 c
+84.152 1.807 84.258 1.918 84.329 2.057 c
+84.329 2.998 l
+83.961 2.998 l
+83.645 2.998 83.402 2.929 83.226 2.792 c
+83.049 2.664 82.962 2.476 82.962 2.234 c
+82.962 2.006 83.005 1.841 83.094 1.734 c
+83.182 1.635 83.333 1.587 83.55 1.587 c
+86.592 5.056 m
+86.607 4.615 l
+86.86 4.957 87.184 5.13 87.577 5.13 c
+88.283 5.13 88.639 4.659 88.65 3.719 c
+88.65 1.072 l
+88.003 1.072 l
+88.003 3.689 l
+88.003 4.001 87.948 4.222 87.842 4.351 c
+87.731 4.475 87.577 4.542 87.371 4.542 c
+87.213 4.542 87.066 4.486 86.93 4.38 c
+86.802 4.27 86.699 4.134 86.621 3.968 c
+86.621 1.072 l
+85.975 1.072 l
+85.975 5.056 l
+h
+f
+Q
+q 1 0 0 1 311.0178 275.5567 cm
+0 0 m
+-0.217 -0.287 -0.53 -0.426 -0.941 -0.426 c
+-1.305 -0.426 -1.58 -0.305 -1.764 -0.058 c
+-1.941 0.195 -2.036 0.559 -2.043 1.029 c
+-2.043 3.631 l
+-1.396 3.631 l
+-1.396 1.087 l
+-1.396 0.459 -1.213 0.147 -0.838 0.147 c
+-0.437 0.147 -0.162 0.324 -0.015 0.676 c
+-0.015 3.631 l
+0.632 3.631 l
+0.632 -0.353 l
+0.015 -0.353 l
+h
+2.234 3.631 m
+2.249 3.19 l
+2.502 3.532 2.826 3.705 3.219 3.705 c
+3.925 3.705 4.281 3.234 4.292 2.294 c
+4.292 -0.353 l
+3.645 -0.353 l
+3.645 2.263 l
+3.645 2.576 3.59 2.797 3.484 2.926 c
+3.373 3.05 3.219 3.117 3.013 3.117 c
+2.855 3.117 2.708 3.061 2.572 2.955 c
+2.444 2.845 2.341 2.708 2.263 2.543 c
+2.263 -0.353 l
+1.617 -0.353 l
+1.617 3.631 l
+h
+6.644 0.133 m
+6.857 0.133 7.03 0.195 7.158 0.324 c
+7.294 0.459 7.368 0.651 7.379 0.897 c
+7.996 0.897 l
+7.975 0.515 7.838 0.195 7.584 -0.058 c
+7.327 -0.305 7.015 -0.426 6.644 -0.426 c
+6.151 -0.426 5.777 -0.276 5.512 0.029 c
+5.255 0.341 5.13 0.809 5.13 1.426 c
+5.13 1.867 l
+5.13 2.462 5.255 2.918 5.512 3.234 c
+5.777 3.547 6.151 3.705 6.644 3.705 c
+7.044 3.705 7.364 3.572 7.599 3.308 c
+7.842 3.05 7.975 2.705 7.996 2.263 c
+7.379 2.263 l
+7.357 2.558 7.283 2.778 7.158 2.926 c
+7.04 3.072 6.868 3.146 6.644 3.146 c
+6.35 3.146 6.133 3.046 5.997 2.851 c
+5.858 2.664 5.784 2.356 5.777 1.926 c
+5.777 1.411 l
+5.777 0.941 5.843 0.607 5.982 0.412 c
+6.13 0.224 6.35 0.133 6.644 0.133 c
+8.613 1.823 m
+8.613 2.4 8.75 2.855 9.025 3.19 c
+9.309 3.532 9.679 3.705 10.142 3.705 c
+10.601 3.705 10.969 3.535 11.245 3.204 c
+11.528 2.882 11.675 2.433 11.686 1.867 c
+11.686 1.44 l
+11.686 0.871 11.542 0.416 11.26 0.073 c
+10.984 -0.261 10.616 -0.426 10.157 -0.426 c
+9.694 -0.426 9.323 -0.264 9.04 0.059 c
+8.764 0.389 8.621 0.831 8.613 1.382 c
+h
+9.261 1.44 m
+9.261 1.037 9.338 0.721 9.496 0.485 c
+9.66 0.25 9.882 0.133 10.157 0.133 c
+10.723 0.133 11.017 0.544 11.039 1.367 c
+11.039 1.823 l
+11.039 2.223 10.955 2.543 10.789 2.778 c
+10.631 3.021 10.414 3.146 10.142 3.146 c
+9.878 3.146 9.66 3.021 9.496 2.778 c
+9.338 2.543 9.261 2.223 9.261 1.823 c
+h
+13.141 3.631 m
+13.155 3.263 l
+13.398 3.557 13.718 3.705 14.111 3.705 c
+14.552 3.705 14.861 3.506 15.037 3.117 c
+15.291 3.506 15.64 3.705 16.081 3.705 c
+16.816 3.705 17.19 3.241 17.213 2.323 c
+17.213 -0.353 l
+16.565 -0.353 l
+16.565 2.263 l
+16.565 2.558 16.511 2.77 16.404 2.911 c
+16.305 3.046 16.132 3.117 15.89 3.117 c
+15.692 3.117 15.53 3.036 15.405 2.882 c
+15.287 2.734 15.217 2.543 15.199 2.308 c
+15.199 -0.353 l
+14.537 -0.353 l
+14.537 2.294 l
+14.537 2.841 14.317 3.117 13.876 3.117 c
+13.542 3.117 13.307 2.955 13.17 2.631 c
+13.17 -0.353 l
+12.523 -0.353 l
+12.523 3.631 l
+h
+18.8 3.631 m
+18.815 3.263 l
+19.057 3.557 19.377 3.705 19.771 3.705 c
+20.211 3.705 20.519 3.506 20.696 3.117 c
+20.949 3.506 21.299 3.705 21.74 3.705 c
+22.475 3.705 22.85 3.241 22.871 2.323 c
+22.871 -0.353 l
+22.225 -0.353 l
+22.225 2.263 l
+22.225 2.558 22.169 2.77 22.063 2.911 c
+21.964 3.046 21.791 3.117 21.549 3.117 c
+21.35 3.117 21.188 3.036 21.064 2.882 c
+20.947 2.734 20.876 2.543 20.858 2.308 c
+20.858 -0.353 l
+20.197 -0.353 l
+20.197 2.294 l
+20.197 2.841 19.976 3.117 19.535 3.117 c
+19.201 3.117 18.965 2.955 18.83 2.631 c
+18.83 -0.353 l
+18.183 -0.353 l
+18.183 3.631 l
+h
+24.548 -0.353 -0.647 3.984 re
+24.592 4.675 m
+24.592 4.564 24.562 4.472 24.503 4.395 c
+24.444 4.326 24.349 4.293 24.224 4.293 c
+24.106 4.293 24.01 4.326 23.945 4.395 c
+23.886 4.472 23.856 4.564 23.856 4.675 c
+23.856 4.792 23.886 4.884 23.945 4.954 c
+24.01 5.031 24.106 5.072 24.224 5.072 c
+24.349 5.072 24.444 5.031 24.503 4.954 c
+24.562 4.873 24.592 4.781 24.592 4.675 c
+26.414 4.586 m
+26.414 3.631 l
+27.017 3.631 l
+27.017 3.102 l
+26.414 3.102 l
+26.414 0.632 l
+26.414 0.474 26.437 0.357 26.487 0.279 c
+26.547 0.199 26.634 0.162 26.752 0.162 c
+26.84 0.162 26.929 0.177 27.017 0.206 c
+27.017 -0.353 l
+26.869 -0.401 26.715 -0.426 26.561 -0.426 c
+26.304 -0.426 26.109 -0.334 25.973 -0.147 c
+25.834 0.037 25.768 0.298 25.768 0.632 c
+25.768 3.102 l
+25.165 3.102 l
+25.165 3.631 l
+25.768 3.631 l
+25.768 4.586 l
+h
+29.207 -0.426 m
+28.708 -0.426 28.325 -0.279 28.06 0.015 c
+27.796 0.309 27.663 0.742 27.663 1.323 c
+27.663 1.793 l
+27.663 2.389 27.789 2.855 28.045 3.19 c
+28.311 3.532 28.671 3.705 29.134 3.705 c
+29.593 3.705 29.934 3.55 30.162 3.248 c
+30.397 2.955 30.519 2.492 30.53 1.867 c
+30.53 1.44 l
+28.311 1.44 l
+28.311 1.353 l
+28.311 0.919 28.388 0.607 28.546 0.412 c
+28.71 0.224 28.943 0.133 29.236 0.133 c
+29.431 0.133 29.604 0.166 29.751 0.235 c
+29.898 0.312 30.034 0.43 30.162 0.588 c
+30.501 0.177 l
+30.214 -0.228 29.784 -0.426 29.207 -0.426 c
+29.134 3.146 m
+28.858 3.146 28.656 3.05 28.531 2.866 c
+28.402 2.679 28.328 2.389 28.311 1.999 c
+29.884 1.999 l
+29.884 2.088 l
+29.861 2.469 29.795 2.738 29.677 2.896 c
+29.56 3.061 29.376 3.146 29.134 3.146 c
+31.177 1.823 m
+31.177 2.429 31.287 2.896 31.515 3.219 c
+31.75 3.543 32.077 3.705 32.5 3.705 c
+32.882 3.705 33.179 3.547 33.396 3.234 c
+33.396 5.292 l
+34.043 5.292 l
+34.043 -0.353 l
+33.455 -0.353 l
+33.411 0.073 l
+33.205 -0.261 32.9 -0.426 32.5 -0.426 c
+32.088 -0.426 31.765 -0.272 31.53 0.044 c
+31.295 0.368 31.177 0.823 31.177 1.411 c
+h
+31.823 1.44 m
+31.823 1 31.886 0.669 32.014 0.456 c
+32.151 0.25 32.371 0.147 32.676 0.147 c
+32.999 0.147 33.238 0.309 33.396 0.632 c
+33.396 2.646 l
+33.227 2.959 32.988 3.117 32.676 3.117 c
+32.371 3.117 32.151 3.013 32.014 2.808 c
+31.886 2.602 31.823 2.279 31.823 1.837 c
+h
+38.144 0.133 m
+38.357 0.133 38.53 0.195 38.659 0.324 c
+38.794 0.459 38.868 0.651 38.879 0.897 c
+39.496 0.897 l
+39.474 0.515 39.338 0.195 39.085 -0.058 c
+38.827 -0.305 38.515 -0.426 38.144 -0.426 c
+37.651 -0.426 37.277 -0.276 37.012 0.029 c
+36.755 0.341 36.63 0.809 36.63 1.426 c
+36.63 1.867 l
+36.63 2.462 36.755 2.918 37.012 3.234 c
+37.277 3.547 37.651 3.705 38.144 3.705 c
+38.545 3.705 38.864 3.572 39.099 3.308 c
+39.342 3.05 39.474 2.705 39.496 2.263 c
+38.879 2.263 l
+38.857 2.558 38.784 2.778 38.659 2.926 c
+38.541 3.072 38.368 3.146 38.144 3.146 c
+37.85 3.146 37.633 3.046 37.497 2.851 c
+37.358 2.664 37.284 2.356 37.277 1.926 c
+37.277 1.411 l
+37.277 0.941 37.343 0.607 37.483 0.412 c
+37.63 0.224 37.85 0.133 38.144 0.133 c
+40.893 3.219 m
+41.146 3.543 41.466 3.705 41.848 3.705 c
+42.553 3.705 42.91 3.234 42.921 2.294 c
+42.921 -0.353 l
+42.274 -0.353 l
+42.274 2.263 l
+42.274 2.576 42.219 2.797 42.113 2.926 c
+42.002 3.05 41.848 3.117 41.643 3.117 c
+41.485 3.117 41.337 3.061 41.202 2.955 c
+41.073 2.845 40.97 2.708 40.893 2.543 c
+40.893 -0.353 l
+40.246 -0.353 l
+40.246 5.292 l
+40.893 5.292 l
+h
+45.919 -0.353 m
+45.879 -0.264 45.854 -0.118 45.846 0.088 c
+45.611 -0.257 45.317 -0.426 44.965 -0.426 c
+44.6 -0.426 44.317 -0.33 44.112 -0.132 c
+43.914 0.073 43.818 0.36 43.818 0.735 c
+43.818 1.135 43.954 1.455 44.23 1.691 c
+44.502 1.933 44.876 2.058 45.346 2.058 c
+45.832 2.058 l
+45.832 2.484 l
+45.832 2.72 45.776 2.885 45.67 2.984 c
+45.56 3.09 45.398 3.146 45.185 3.146 c
+44.986 3.146 44.824 3.087 44.7 2.969 c
+44.583 2.851 44.523 2.705 44.523 2.529 c
+43.877 2.529 l
+43.877 2.723 43.935 2.914 44.053 3.102 c
+44.178 3.285 44.34 3.433 44.538 3.543 c
+44.743 3.649 44.972 3.705 45.229 3.705 c
+45.63 3.705 45.934 3.601 46.14 3.396 c
+46.353 3.19 46.468 2.896 46.478 2.514 c
+46.478 0.5 l
+46.478 0.195 46.515 -0.07 46.596 -0.293 c
+46.596 -0.353 l
+h
+45.053 0.162 m
+45.218 0.162 45.369 0.206 45.508 0.294 c
+45.655 0.382 45.762 0.493 45.832 0.632 c
+45.832 1.573 l
+45.464 1.573 l
+45.148 1.573 44.905 1.503 44.729 1.367 c
+44.552 1.239 44.465 1.051 44.465 0.809 c
+44.465 0.58 44.508 0.416 44.597 0.309 c
+44.685 0.21 44.836 0.162 45.053 0.162 c
+48.095 3.631 m
+48.11 3.19 l
+48.363 3.532 48.687 3.705 49.08 3.705 c
+49.786 3.705 50.142 3.234 50.153 2.294 c
+50.153 -0.353 l
+49.506 -0.353 l
+49.506 2.263 l
+49.506 2.576 49.451 2.797 49.345 2.926 c
+49.235 3.05 49.08 3.117 48.874 3.117 c
+48.716 3.117 48.569 3.061 48.433 2.955 c
+48.305 2.845 48.202 2.708 48.124 2.543 c
+48.124 -0.353 l
+47.478 -0.353 l
+47.478 3.631 l
+h
+50.991 1.823 m
+50.991 2.44 51.101 2.903 51.329 3.219 c
+51.553 3.543 51.887 3.705 52.329 3.705 c
+52.729 3.705 53.034 3.528 53.24 3.175 c
+53.284 3.631 l
+53.872 3.631 l
+53.872 -0.397 l
+53.872 -0.885 53.743 -1.264 53.49 -1.529 c
+53.233 -1.793 52.88 -1.926 52.431 -1.926 c
+52.233 -1.926 52.013 -1.874 51.77 -1.778 c
+51.523 -1.679 51.344 -1.558 51.226 -1.411 c
+51.49 -0.97 l
+51.756 -1.234 52.053 -1.367 52.387 -1.367 c
+52.924 -1.367 53.2 -1.072 53.21 -0.484 c
+53.21 0.044 l
+53.005 -0.272 52.703 -0.426 52.314 -0.426 c
+51.902 -0.426 51.579 -0.276 51.344 0.029 c
+51.116 0.341 50.998 0.794 50.991 1.382 c
+h
+51.652 1.44 m
+51.652 1 51.715 0.669 51.843 0.456 c
+51.968 0.25 52.186 0.147 52.49 0.147 c
+52.814 0.147 53.053 0.312 53.21 0.647 c
+53.21 2.631 l
+53.042 2.955 52.803 3.117 52.49 3.117 c
+52.196 3.117 51.98 3.013 51.843 2.808 c
+51.715 2.602 51.652 2.279 51.652 1.837 c
+h
+56.253 -0.426 m
+55.754 -0.426 55.371 -0.279 55.107 0.015 c
+54.842 0.309 54.71 0.742 54.71 1.323 c
+54.71 1.793 l
+54.71 2.389 54.835 2.855 55.092 3.19 c
+55.357 3.532 55.717 3.705 56.18 3.705 c
+56.639 3.705 56.981 3.55 57.208 3.248 c
+57.443 2.955 57.565 2.492 57.576 1.867 c
+57.576 1.44 l
+55.357 1.44 l
+55.357 1.353 l
+55.357 0.919 55.434 0.607 55.592 0.412 c
+55.757 0.224 55.989 0.133 56.283 0.133 c
+56.477 0.133 56.65 0.166 56.797 0.235 c
+56.944 0.312 57.08 0.43 57.208 0.588 c
+57.547 0.177 l
+57.26 -0.228 56.83 -0.426 56.253 -0.426 c
+56.18 3.146 m
+55.904 3.146 55.702 3.05 55.577 2.866 c
+55.448 2.679 55.375 2.389 55.357 1.999 c
+56.93 1.999 l
+56.93 2.088 l
+56.907 2.469 56.841 2.738 56.724 2.896 c
+56.606 3.061 56.423 3.146 56.18 3.146 c
+60.296 0.661 m
+60.296 0.809 60.24 0.929 60.134 1.029 c
+60.024 1.124 59.818 1.242 59.517 1.382 c
+59.171 1.529 58.929 1.65 58.781 1.75 c
+58.634 1.856 58.524 1.974 58.458 2.103 c
+58.388 2.227 58.355 2.385 58.355 2.573 c
+58.355 2.896 58.473 3.164 58.708 3.381 c
+58.943 3.594 59.245 3.705 59.619 3.705 c
+60.001 3.705 60.31 3.591 60.545 3.366 c
+60.78 3.138 60.898 2.851 60.898 2.499 c
+60.252 2.499 l
+60.252 2.675 60.192 2.826 60.075 2.955 c
+59.957 3.08 59.803 3.146 59.619 3.146 c
+59.421 3.146 59.27 3.09 59.164 2.984 c
+59.053 2.885 59.002 2.753 59.002 2.587 c
+59.002 2.458 59.039 2.352 59.12 2.263 c
+59.197 2.183 59.388 2.08 59.693 1.955 c
+60.171 1.768 60.501 1.58 60.678 1.397 c
+60.854 1.22 60.942 0.992 60.942 0.721 c
+60.942 0.368 60.817 0.088 60.575 -0.118 c
+60.34 -0.324 60.024 -0.426 59.634 -0.426 c
+59.211 -0.426 58.873 -0.309 58.619 -0.073 c
+58.363 0.169 58.237 0.474 58.237 0.838 c
+58.885 0.838 l
+58.892 0.611 58.962 0.434 59.09 0.309 c
+59.215 0.191 59.399 0.133 59.634 0.133 c
+59.847 0.133 60.009 0.181 60.119 0.279 c
+60.236 0.375 60.296 0.503 60.296 0.661 c
+61.824 0 m
+61.824 0.118 61.857 0.214 61.927 0.294 c
+61.993 0.372 62.096 0.412 62.236 0.412 c
+62.382 0.412 62.49 0.372 62.559 0.294 c
+62.636 0.214 62.677 0.118 62.677 0 c
+62.677 -0.11 62.636 -0.202 62.559 -0.279 c
+62.49 -0.357 62.382 -0.397 62.236 -0.397 c
+62.096 -0.397 61.993 -0.357 61.927 -0.279 c
+61.857 -0.202 61.824 -0.11 61.824 0 c
+66.602 1.338 m
+66.675 0.735 l
+66.777 1.264 l
+67.631 4.998 l
+68.189 4.998 l
+69.027 1.264 l
+69.129 0.721 l
+69.203 1.338 l
+69.865 4.998 l
+70.54 4.998 l
+69.468 -0.353 l
+68.865 -0.353 l
+67.968 3.543 l
+67.909 3.866 l
+67.866 3.543 l
+66.939 -0.353 l
+66.322 -0.353 l
+65.264 4.998 l
+65.94 4.998 l
+h
+71.864 3.219 m
+72.117 3.543 72.437 3.705 72.819 3.705 c
+73.524 3.705 73.881 3.234 73.892 2.294 c
+73.892 -0.353 l
+73.245 -0.353 l
+73.245 2.263 l
+73.245 2.576 73.19 2.797 73.083 2.926 c
+72.973 3.05 72.819 3.117 72.613 3.117 c
+72.455 3.117 72.308 3.061 72.173 2.955 c
+72.044 2.845 71.941 2.708 71.864 2.543 c
+71.864 -0.353 l
+71.217 -0.353 l
+71.217 5.292 l
+71.864 5.292 l
+h
+76.273 -0.426 m
+75.774 -0.426 75.392 -0.279 75.126 0.015 c
+74.862 0.309 74.73 0.742 74.73 1.323 c
+74.73 1.793 l
+74.73 2.389 74.855 2.855 75.112 3.19 c
+75.377 3.532 75.737 3.705 76.2 3.705 c
+76.659 3.705 77.001 3.55 77.229 3.248 c
+77.464 2.955 77.586 2.492 77.596 1.867 c
+77.596 1.44 l
+75.377 1.44 l
+75.377 1.353 l
+75.377 0.919 75.454 0.607 75.612 0.412 c
+75.777 0.224 76.009 0.133 76.302 0.133 c
+76.497 0.133 76.67 0.166 76.817 0.235 c
+76.964 0.312 77.1 0.43 77.229 0.588 c
+77.567 0.177 l
+77.28 -0.228 76.85 -0.426 76.273 -0.426 c
+76.2 3.146 m
+75.924 3.146 75.722 3.05 75.597 2.866 c
+75.469 2.679 75.396 2.389 75.377 1.999 c
+76.95 1.999 l
+76.95 2.088 l
+76.927 2.469 76.861 2.738 76.744 2.896 c
+76.626 3.061 76.443 3.146 76.2 3.146 c
+78.993 3.631 m
+79.007 3.19 l
+79.261 3.532 79.585 3.705 79.978 3.705 c
+80.683 3.705 81.04 3.234 81.05 2.294 c
+81.05 -0.353 l
+80.404 -0.353 l
+80.404 2.263 l
+80.404 2.576 80.349 2.797 80.242 2.926 c
+80.132 3.05 79.978 3.117 79.772 3.117 c
+79.614 3.117 79.467 3.061 79.331 2.955 c
+79.202 2.845 79.099 2.708 79.022 2.543 c
+79.022 -0.353 l
+78.375 -0.353 l
+78.375 3.631 l
+h
+f
+Q
+396.523 277.071 -1.794 0.866 re
+399.256 277.071 -1.793 0.866 re
+401.138 278.835 m
+401.362 279.117 401.637 279.261 401.961 279.261 c
+402.321 279.261 402.597 279.132 402.784 278.878 c
+402.979 278.622 403.078 278.24 403.078 277.733 c
+403.078 275.204 l
+402.034 275.204 l
+402.034 277.718 l
+402.034 277.953 401.994 278.118 401.917 278.217 c
+401.847 278.324 401.733 278.379 401.579 278.379 c
+401.391 278.379 401.244 278.294 401.138 278.13 c
+401.138 275.204 l
+400.094 275.204 l
+400.094 280.848 l
+401.138 280.848 l
+h
+405.709 275.204 m
+405.68 275.263 405.651 275.366 405.62 275.512 c
+405.433 275.256 405.184 275.131 404.872 275.131 c
+404.537 275.131 404.257 275.237 404.033 275.454 c
+403.817 275.678 403.71 275.969 403.71 276.322 c
+403.71 276.733 403.842 277.049 404.107 277.276 c
+404.371 277.512 404.754 277.629 405.254 277.629 c
+405.577 277.629 l
+405.577 277.953 l
+405.577 278.13 405.54 278.25 405.474 278.321 c
+405.415 278.398 405.327 278.438 405.209 278.438 c
+404.952 278.438 404.827 278.284 404.827 277.982 c
+403.783 277.982 l
+403.783 278.354 403.92 278.658 404.195 278.893 c
+404.467 279.136 404.816 279.261 405.238 279.261 c
+405.68 279.261 406.017 279.144 406.253 278.909 c
+406.495 278.68 406.62 278.357 406.62 277.938 c
+406.62 276.071 l
+406.62 275.726 406.668 275.458 406.767 275.263 c
+406.767 275.204 l
+h
+405.107 275.954 m
+405.213 275.954 405.305 275.972 405.385 276.013 c
+405.474 276.06 405.537 276.119 405.577 276.189 c
+405.577 277.012 l
+405.327 277.012 l
+405.15 277.012 405.007 276.954 404.901 276.836 c
+404.801 276.725 404.754 276.578 404.754 276.395 c
+404.754 276.1 404.872 275.954 405.107 275.954 c
+409.325 278.173 m
+408.987 278.203 l
+408.7 278.203 408.509 278.078 408.413 277.835 c
+408.413 275.204 l
+407.37 275.204 l
+407.37 279.188 l
+408.34 279.188 l
+408.369 278.747 l
+408.535 279.088 408.766 279.261 409.061 279.261 c
+409.178 279.261 409.27 279.239 409.34 279.202 c
+h
+409.649 277.321 m
+409.649 277.968 409.755 278.452 409.971 278.776 c
+410.196 279.1 410.519 279.261 410.942 279.261 c
+411.255 279.261 411.508 279.129 411.706 278.864 c
+411.706 280.848 l
+412.764 280.848 l
+412.764 275.204 l
+411.81 275.204 l
+411.765 275.616 l
+411.548 275.292 411.272 275.131 410.942 275.131 c
+410.53 275.131 410.21 275.285 409.986 275.601 c
+409.77 275.924 409.656 276.395 409.649 277.012 c
+h
+410.692 277.056 m
+410.692 276.663 410.729 276.387 410.81 276.233 c
+410.898 276.075 411.045 275.998 411.251 275.998 c
+411.457 275.998 411.607 276.09 411.706 276.277 c
+411.706 278.085 l
+411.607 278.28 411.457 278.379 411.251 278.379 c
+411.052 278.379 410.912 278.298 410.825 278.144 c
+410.736 277.997 410.692 277.725 410.692 277.336 c
+h
+f
+416.057 275.204 -0.646 3.984 re
+416.101 280.231 m
+416.101 280.121 416.072 280.029 416.013 279.952 c
+415.954 279.882 415.858 279.849 415.733 279.849 c
+415.616 279.849 415.521 279.882 415.455 279.952 c
+415.396 280.029 415.366 280.121 415.366 280.231 c
+415.366 280.349 415.396 280.441 415.455 280.511 c
+415.521 280.588 415.616 280.628 415.733 280.628 c
+415.858 280.628 415.954 280.588 416.013 280.511 c
+416.072 280.43 416.101 280.337 416.101 280.231 c
+419.012 276.218 m
+419.012 276.366 418.956 276.486 418.85 276.586 c
+418.74 276.681 418.534 276.798 418.233 276.939 c
+417.888 277.085 417.645 277.207 417.497 277.306 c
+417.35 277.413 417.24 277.53 417.175 277.659 c
+417.104 277.783 417.071 277.941 417.071 278.13 c
+417.071 278.452 417.189 278.72 417.424 278.938 c
+417.659 279.15 417.961 279.261 418.335 279.261 c
+418.717 279.261 419.026 279.147 419.261 278.923 c
+419.496 278.695 419.614 278.408 419.614 278.055 c
+418.968 278.055 l
+418.968 278.232 418.908 278.383 418.791 278.512 c
+418.673 278.637 418.519 278.703 418.335 278.703 c
+418.137 278.703 417.986 278.647 417.88 278.541 c
+417.77 278.442 417.718 278.309 417.718 278.144 c
+417.718 278.015 417.755 277.909 417.836 277.82 c
+417.913 277.739 418.104 277.637 418.409 277.512 c
+418.887 277.324 419.218 277.137 419.394 276.954 c
+419.571 276.777 419.658 276.549 419.658 276.277 c
+419.658 275.924 419.534 275.645 419.291 275.439 c
+419.056 275.233 418.74 275.131 418.35 275.131 c
+417.928 275.131 417.589 275.248 417.336 275.483 c
+417.079 275.726 416.954 276.031 416.954 276.395 c
+417.601 276.395 l
+417.608 276.167 417.678 275.99 417.807 275.865 c
+417.931 275.748 418.115 275.689 418.35 275.689 c
+418.563 275.689 418.725 275.737 418.835 275.836 c
+418.953 275.932 419.012 276.06 419.012 276.218 c
+424.244 275.557 m
+424.028 275.27 423.716 275.131 423.303 275.131 c
+422.94 275.131 422.665 275.252 422.48 275.498 c
+422.304 275.751 422.208 276.115 422.201 276.586 c
+422.201 279.188 l
+422.848 279.188 l
+422.848 276.644 l
+422.848 276.016 423.032 275.703 423.407 275.703 c
+423.808 275.703 424.082 275.88 424.23 276.233 c
+424.23 279.188 l
+424.876 279.188 l
+424.876 275.204 l
+424.259 275.204 l
+h
+427.802 276.218 m
+427.802 276.366 427.747 276.486 427.64 276.586 c
+427.53 276.681 427.324 276.798 427.022 276.939 c
+426.677 277.085 426.434 277.207 426.288 277.306 c
+426.141 277.413 426.031 277.53 425.964 277.659 c
+425.894 277.783 425.861 277.941 425.861 278.13 c
+425.861 278.452 425.979 278.72 426.214 278.938 c
+426.449 279.15 426.75 279.261 427.126 279.261 c
+427.508 279.261 427.816 279.147 428.051 278.923 c
+428.287 278.695 428.404 278.408 428.404 278.055 c
+427.758 278.055 l
+427.758 278.232 427.699 278.383 427.581 278.512 c
+427.463 278.637 427.309 278.703 427.126 278.703 c
+426.927 278.703 426.777 278.647 426.669 278.541 c
+426.559 278.442 426.509 278.309 426.509 278.144 c
+426.509 278.015 426.545 277.909 426.626 277.82 c
+426.703 277.739 426.894 277.637 427.199 277.512 c
+427.677 277.324 428.007 277.137 428.184 276.954 c
+428.36 276.777 428.448 276.549 428.448 276.277 c
+428.448 275.924 428.323 275.645 428.081 275.439 c
+427.845 275.233 427.53 275.131 427.14 275.131 c
+426.717 275.131 426.38 275.248 426.126 275.483 c
+425.869 275.726 425.744 276.031 425.744 276.395 c
+426.391 276.395 l
+426.398 276.167 426.468 275.99 426.596 275.865 c
+426.721 275.748 426.905 275.689 427.14 275.689 c
+427.353 275.689 427.515 275.737 427.625 275.836 c
+427.743 275.932 427.802 276.06 427.802 276.218 c
+430.712 275.131 m
+430.212 275.131 429.83 275.277 429.566 275.572 c
+429.301 275.865 429.169 276.299 429.169 276.879 c
+429.169 277.35 l
+429.169 277.945 429.294 278.412 429.551 278.747 c
+429.815 279.088 430.176 279.261 430.638 279.261 c
+431.098 279.261 431.44 279.107 431.668 278.805 c
+431.903 278.512 432.024 278.049 432.035 277.424 c
+432.035 276.997 l
+429.815 276.997 l
+429.815 276.909 l
+429.815 276.476 429.892 276.163 430.05 275.969 c
+430.216 275.781 430.447 275.689 430.742 275.689 c
+430.937 275.689 431.109 275.722 431.256 275.792 c
+431.403 275.869 431.539 275.987 431.668 276.145 c
+432.005 275.734 l
+431.719 275.329 431.289 275.131 430.712 275.131 c
+430.638 278.703 m
+430.363 278.703 430.161 278.607 430.036 278.423 c
+429.908 278.236 429.834 277.945 429.815 277.556 c
+431.388 277.556 l
+431.388 277.644 l
+431.367 278.026 431.3 278.294 431.182 278.452 c
+431.065 278.618 430.881 278.703 430.638 278.703 c
+432.682 277.38 m
+432.682 277.986 432.792 278.452 433.019 278.776 c
+433.255 279.1 433.582 279.261 434.004 279.261 c
+434.387 279.261 434.685 279.103 434.901 278.791 c
+434.901 280.848 l
+435.548 280.848 l
+435.548 275.204 l
+434.96 275.204 l
+434.916 275.63 l
+434.71 275.296 434.405 275.131 434.004 275.131 c
+433.593 275.131 433.27 275.285 433.035 275.601 c
+432.799 275.924 432.682 276.38 432.682 276.968 c
+h
+433.329 276.997 m
+433.329 276.557 433.391 276.226 433.52 276.013 c
+433.655 275.807 433.876 275.703 434.181 275.703 c
+434.505 275.703 434.743 275.865 434.901 276.189 c
+434.901 278.203 l
+434.733 278.515 434.494 278.673 434.181 278.673 c
+433.876 278.673 433.655 278.57 433.52 278.365 c
+433.391 278.159 433.329 277.835 433.329 277.394 c
+h
+436.562 274.131 m
+436.165 274.396 l
+436.4 274.719 436.522 275.053 436.533 275.395 c
+436.533 276.013 l
+437.194 276.013 l
+437.194 275.483 l
+437.194 275.226 437.129 274.98 437.003 274.734 c
+436.886 274.491 436.739 274.289 436.562 274.131 c
+441.796 275.204 m
+441.755 275.292 441.729 275.439 441.721 275.645 c
+441.486 275.3 441.193 275.131 440.84 275.131 c
+440.476 275.131 440.193 275.226 439.988 275.425 c
+439.789 275.63 439.693 275.917 439.693 276.291 c
+439.693 276.692 439.829 277.012 440.105 277.247 c
+440.377 277.49 440.751 277.615 441.222 277.615 c
+441.707 277.615 l
+441.707 278.041 l
+441.707 278.276 441.652 278.442 441.545 278.541 c
+441.435 278.647 441.273 278.703 441.06 278.703 c
+440.861 278.703 440.701 278.643 440.575 278.526 c
+440.458 278.408 440.399 278.261 440.399 278.085 c
+439.752 278.085 l
+439.752 278.28 439.811 278.471 439.928 278.658 c
+440.053 278.842 440.215 278.989 440.414 279.1 c
+440.62 279.206 440.847 279.261 441.104 279.261 c
+441.505 279.261 441.81 279.158 442.016 278.953 c
+442.229 278.747 442.343 278.452 442.353 278.07 c
+442.353 276.056 l
+442.353 275.751 442.39 275.487 442.471 275.263 c
+442.471 275.204 l
+h
+440.928 275.718 m
+441.093 275.718 441.244 275.763 441.384 275.851 c
+441.53 275.939 441.638 276.05 441.707 276.189 c
+441.707 277.13 l
+441.339 277.13 l
+441.023 277.13 440.781 277.06 440.605 276.924 c
+440.428 276.796 440.34 276.607 440.34 276.366 c
+440.34 276.137 440.384 275.972 440.472 275.865 c
+440.56 275.766 440.711 275.718 440.928 275.718 c
+444.059 275.204 -0.647 5.644 re
+445.779 275.204 -0.647 5.644 re
+449.894 275.689 m
+450.108 275.689 450.28 275.751 450.409 275.88 c
+450.544 276.016 450.619 276.208 450.629 276.453 c
+451.246 276.453 l
+451.225 276.071 451.089 275.751 450.835 275.498 c
+450.578 275.252 450.265 275.131 449.894 275.131 c
+449.402 275.131 449.027 275.281 448.763 275.586 c
+448.505 275.898 448.381 276.366 448.381 276.983 c
+448.381 277.424 l
+448.381 278.019 448.505 278.475 448.763 278.791 c
+449.027 279.103 449.402 279.261 449.894 279.261 c
+450.295 279.261 450.615 279.129 450.85 278.864 c
+451.092 278.607 451.225 278.261 451.246 277.82 c
+450.629 277.82 l
+450.608 278.115 450.534 278.335 450.409 278.482 c
+450.291 278.629 450.118 278.703 449.894 278.703 c
+449.6 278.703 449.384 278.603 449.247 278.408 c
+449.108 278.221 449.035 277.912 449.027 277.482 c
+449.027 276.968 l
+449.027 276.497 449.093 276.163 449.233 275.969 c
+449.38 275.781 449.6 275.689 449.894 275.689 c
+452.643 278.776 m
+452.896 279.1 453.216 279.261 453.598 279.261 c
+454.304 279.261 454.66 278.791 454.672 277.85 c
+454.672 275.204 l
+454.025 275.204 l
+454.025 277.82 l
+454.025 278.132 453.97 278.354 453.863 278.482 c
+453.753 278.607 453.598 278.673 453.393 278.673 c
+453.235 278.673 453.087 278.618 452.952 278.512 c
+452.823 278.401 452.72 278.265 452.643 278.099 c
+452.643 275.204 l
+451.996 275.204 l
+451.996 280.848 l
+452.643 280.848 l
+h
+457.671 275.204 m
+457.63 275.292 457.604 275.439 457.596 275.645 c
+457.361 275.3 457.068 275.131 456.715 275.131 c
+456.351 275.131 456.068 275.226 455.862 275.425 c
+455.664 275.63 455.568 275.917 455.568 276.291 c
+455.568 276.692 455.704 277.012 455.98 277.247 c
+456.252 277.49 456.626 277.615 457.097 277.615 c
+457.582 277.615 l
+457.582 278.041 l
+457.582 278.276 457.527 278.442 457.42 278.541 c
+457.31 278.647 457.148 278.703 456.935 278.703 c
+456.736 278.703 456.576 278.643 456.45 278.526 c
+456.333 278.408 456.274 278.261 456.274 278.085 c
+455.627 278.085 l
+455.627 278.28 455.686 278.471 455.803 278.658 c
+455.928 278.842 456.09 278.989 456.289 279.1 c
+456.495 279.206 456.722 279.261 456.979 279.261 c
+457.38 279.261 457.685 279.158 457.891 278.953 c
+458.104 278.747 458.218 278.452 458.228 278.07 c
+458.228 276.056 l
+458.228 275.751 458.265 275.487 458.346 275.263 c
+458.346 275.204 l
+h
+456.803 275.718 m
+456.969 275.718 457.119 275.763 457.259 275.851 c
+457.405 275.939 457.513 276.05 457.582 276.189 c
+457.582 277.13 l
+457.214 277.13 l
+456.898 277.13 456.656 277.06 456.48 276.924 c
+456.303 276.796 456.215 276.607 456.215 276.366 c
+456.215 276.137 456.259 275.972 456.347 275.865 c
+456.435 275.766 456.586 275.718 456.803 275.718 c
+459.846 279.188 m
+459.861 278.747 l
+460.114 279.088 460.437 279.261 460.831 279.261 c
+461.536 279.261 461.893 278.791 461.904 277.85 c
+461.904 275.204 l
+461.257 275.204 l
+461.257 277.82 l
+461.257 278.132 461.202 278.354 461.095 278.482 c
+460.985 278.607 460.831 278.673 460.625 278.673 c
+460.466 278.673 460.32 278.618 460.183 278.512 c
+460.055 278.401 459.952 278.265 459.875 278.099 c
+459.875 275.204 l
+459.228 275.204 l
+459.228 279.188 l
+h
+462.741 277.38 m
+462.741 277.997 462.851 278.46 463.08 278.776 c
+463.304 279.1 463.638 279.261 464.079 279.261 c
+464.48 279.261 464.784 279.084 464.99 278.732 c
+465.035 279.188 l
+465.623 279.188 l
+465.623 275.16 l
+465.623 274.671 465.494 274.292 465.24 274.028 c
+464.983 273.764 464.63 273.631 464.181 273.631 c
+463.984 273.631 463.763 273.683 463.52 273.779 c
+463.275 273.877 463.094 273.999 462.976 274.146 c
+463.241 274.587 l
+463.506 274.322 463.803 274.19 464.138 274.19 c
+464.674 274.19 464.95 274.484 464.961 275.072 c
+464.961 275.601 l
+464.755 275.285 464.454 275.131 464.064 275.131 c
+463.653 275.131 463.329 275.281 463.094 275.586 c
+462.866 275.898 462.749 276.351 462.741 276.939 c
+h
+463.403 276.997 m
+463.403 276.557 463.466 276.226 463.594 276.013 c
+463.719 275.807 463.936 275.703 464.241 275.703 c
+464.564 275.703 464.803 275.869 464.961 276.204 c
+464.961 278.188 l
+464.792 278.512 464.553 278.673 464.241 278.673 c
+463.946 278.673 463.73 278.57 463.594 278.365 c
+463.466 278.159 463.403 277.835 463.403 277.394 c
+h
+468.004 275.131 m
+467.504 275.131 467.121 275.277 466.857 275.572 c
+466.593 275.865 466.46 276.299 466.46 276.879 c
+466.46 277.35 l
+466.46 277.945 466.585 278.412 466.843 278.747 c
+467.107 279.088 467.467 279.261 467.93 279.261 c
+468.389 279.261 468.731 279.107 468.959 278.805 c
+469.195 278.512 469.315 278.049 469.326 277.424 c
+469.326 276.997 l
+467.107 276.997 l
+467.107 276.909 l
+467.107 276.476 467.184 276.163 467.342 275.969 c
+467.508 275.781 467.739 275.689 468.033 275.689 c
+468.228 275.689 468.401 275.722 468.547 275.792 c
+468.694 275.869 468.831 275.987 468.959 276.145 c
+469.297 275.734 l
+469.01 275.329 468.58 275.131 468.004 275.131 c
+467.93 278.703 m
+467.655 278.703 467.452 278.607 467.327 278.423 c
+467.198 278.236 467.125 277.945 467.107 277.556 c
+468.68 277.556 l
+468.68 277.644 l
+468.658 278.026 468.592 278.294 468.474 278.452 c
+468.356 278.618 468.173 278.703 467.93 278.703 c
+472.046 276.218 m
+472.046 276.366 471.991 276.486 471.884 276.586 c
+471.774 276.681 471.568 276.798 471.267 276.939 c
+470.922 277.085 470.679 277.207 470.531 277.306 c
+470.385 277.413 470.275 277.53 470.209 277.659 c
+470.139 277.783 470.105 277.941 470.105 278.13 c
+470.105 278.452 470.223 278.72 470.458 278.938 c
+470.693 279.15 470.995 279.261 471.37 279.261 c
+471.752 279.261 472.06 279.147 472.295 278.923 c
+472.531 278.695 472.648 278.408 472.648 278.055 c
+472.002 278.055 l
+472.002 278.232 471.943 278.383 471.825 278.512 c
+471.707 278.637 471.553 278.703 471.37 278.703 c
+471.171 278.703 471.021 278.647 470.914 278.541 c
+470.804 278.442 470.753 278.309 470.753 278.144 c
+470.753 278.015 470.789 277.909 470.87 277.82 c
+470.947 277.739 471.138 277.637 471.443 277.512 c
+471.921 277.324 472.252 277.137 472.428 276.954 c
+472.605 276.777 472.692 276.549 472.692 276.277 c
+472.692 275.924 472.568 275.645 472.325 275.439 c
+472.09 275.233 471.774 275.131 471.385 275.131 c
+470.961 275.131 470.624 275.248 470.371 275.483 c
+470.113 275.726 469.988 276.031 469.988 276.395 c
+470.635 276.395 l
+470.642 276.167 470.712 275.99 470.841 275.865 c
+470.965 275.748 471.15 275.689 471.385 275.689 c
+471.597 275.689 471.759 275.737 471.869 275.836 c
+471.987 275.932 472.046 276.06 472.046 276.218 c
+477.293 275.204 m
+477.253 275.292 477.228 275.439 477.22 275.645 c
+476.985 275.3 476.69 275.131 476.338 275.131 c
+475.975 275.131 475.691 275.226 475.485 275.425 c
+475.287 275.63 475.192 275.917 475.192 276.291 c
+475.192 276.692 475.327 277.012 475.603 277.247 c
+475.875 277.49 476.25 277.615 476.721 277.615 c
+477.205 277.615 l
+477.205 278.041 l
+477.205 278.276 477.15 278.442 477.043 278.541 c
+476.933 278.647 476.771 278.703 476.559 278.703 c
+476.36 278.703 476.198 278.643 476.073 278.526 c
+475.956 278.408 475.897 278.261 475.897 278.085 c
+475.25 278.085 l
+475.25 278.28 475.309 278.471 475.427 278.658 c
+475.551 278.842 475.713 278.989 475.912 279.1 c
+476.118 279.206 476.345 279.261 476.603 279.261 c
+477.003 279.261 477.309 279.158 477.514 278.953 c
+477.727 278.747 477.841 278.452 477.852 278.07 c
+477.852 276.056 l
+477.852 275.751 477.889 275.487 477.97 275.263 c
+477.97 275.204 l
+h
+476.426 275.718 m
+476.592 275.718 476.742 275.763 476.881 275.851 c
+477.029 275.939 477.135 276.05 477.205 276.189 c
+477.205 277.13 l
+476.838 277.13 l
+476.522 277.13 476.279 277.06 476.103 276.924 c
+475.927 276.796 475.838 276.607 475.838 276.366 c
+475.838 276.137 475.882 275.972 475.971 275.865 c
+476.058 275.766 476.21 275.718 476.426 275.718 c
+480.498 278.57 m
+480.409 278.589 480.31 278.6 480.204 278.6 c
+479.869 278.6 479.634 278.416 479.499 278.055 c
+479.499 275.204 l
+478.851 275.204 l
+478.851 279.188 l
+479.484 279.188 l
+479.499 278.776 l
+479.674 279.1 479.917 279.261 480.233 279.261 c
+480.34 279.261 480.428 279.239 480.498 279.202 c
+h
+482.497 275.131 m
+481.997 275.131 481.615 275.277 481.35 275.572 c
+481.086 275.865 480.954 276.299 480.954 276.879 c
+480.954 277.35 l
+480.954 277.945 481.078 278.412 481.336 278.747 c
+481.6 279.088 481.96 279.261 482.423 279.261 c
+482.882 279.261 483.225 279.107 483.452 278.805 c
+483.688 278.512 483.809 278.049 483.819 277.424 c
+483.819 276.997 l
+481.6 276.997 l
+481.6 276.909 l
+481.6 276.476 481.677 276.163 481.835 275.969 c
+482.001 275.781 482.232 275.689 482.526 275.689 c
+482.721 275.689 482.894 275.722 483.04 275.792 c
+483.188 275.869 483.324 275.987 483.452 276.145 c
+483.79 275.734 l
+483.504 275.329 483.074 275.131 482.497 275.131 c
+482.423 278.703 m
+482.148 278.703 481.945 278.607 481.82 278.423 c
+481.692 278.236 481.619 277.945 481.6 277.556 c
+483.173 277.556 l
+483.173 277.644 l
+483.151 278.026 483.085 278.294 482.967 278.452 c
+482.849 278.618 482.666 278.703 482.423 278.703 c
+486.186 277.38 m
+486.186 277.986 486.296 278.452 486.524 278.776 c
+486.759 279.1 487.087 279.261 487.509 279.261 c
+487.892 279.261 488.189 279.103 488.405 278.791 c
+488.405 280.848 l
+489.053 280.848 l
+489.053 275.204 l
+488.465 275.204 l
+488.421 275.63 l
+488.215 275.296 487.91 275.131 487.509 275.131 c
+487.098 275.131 486.774 275.285 486.539 275.601 c
+486.304 275.924 486.186 276.38 486.186 276.968 c
+h
+486.833 276.997 m
+486.833 276.557 486.896 276.226 487.024 276.013 c
+487.16 275.807 487.381 275.703 487.686 275.703 c
+488.009 275.703 488.248 275.865 488.405 276.189 c
+488.405 278.203 l
+488.237 278.515 487.998 278.673 487.686 278.673 c
+487.381 278.673 487.16 278.57 487.024 278.365 c
+486.896 278.159 486.833 277.835 486.833 277.394 c
+h
+490.757 275.204 -0.646 3.984 re
+490.802 280.231 m
+490.802 280.121 490.772 280.029 490.714 279.952 c
+490.655 279.882 490.56 279.849 490.435 279.849 c
+490.317 279.849 490.221 279.882 490.155 279.952 c
+490.096 280.029 490.067 280.121 490.067 280.231 c
+490.067 280.349 490.096 280.441 490.155 280.511 c
+490.221 280.588 490.317 280.628 490.435 280.628 c
+490.56 280.628 490.655 280.588 490.714 280.511 c
+490.772 280.43 490.802 280.337 490.802 280.231 c
+493.712 276.218 m
+493.712 276.366 493.658 276.486 493.55 276.586 c
+493.44 276.681 493.234 276.798 492.933 276.939 c
+492.588 277.085 492.345 277.207 492.199 277.306 c
+492.051 277.413 491.941 277.53 491.875 277.659 c
+491.805 277.783 491.772 277.941 491.772 278.13 c
+491.772 278.452 491.89 278.72 492.125 278.938 c
+492.36 279.15 492.661 279.261 493.036 279.261 c
+493.419 279.261 493.727 279.147 493.962 278.923 c
+494.198 278.695 494.315 278.408 494.315 278.055 c
+493.668 278.055 l
+493.668 278.232 493.61 278.383 493.492 278.512 c
+493.374 278.637 493.22 278.703 493.036 278.703 c
+492.837 278.703 492.687 278.647 492.581 278.541 c
+492.47 278.442 492.419 278.309 492.419 278.144 c
+492.419 278.015 492.455 277.909 492.536 277.82 c
+492.613 277.739 492.804 277.637 493.109 277.512 c
+493.587 277.324 493.918 277.137 494.094 276.954 c
+494.271 276.777 494.359 276.549 494.359 276.277 c
+494.359 275.924 494.234 275.645 493.992 275.439 c
+493.756 275.233 493.44 275.131 493.051 275.131 c
+492.628 275.131 492.29 275.248 492.037 275.483 c
+491.779 275.726 491.655 276.031 491.655 276.395 c
+492.301 276.395 l
+492.309 276.167 492.378 275.99 492.507 275.865 c
+492.632 275.748 492.816 275.689 493.051 275.689 c
+493.264 275.689 493.425 275.737 493.536 275.836 c
+493.654 275.932 493.712 276.06 493.712 276.218 c
+496.579 275.689 m
+496.792 275.689 496.964 275.751 497.093 275.88 c
+497.229 276.016 497.302 276.208 497.313 276.453 c
+497.93 276.453 l
+497.909 276.071 497.772 275.751 497.519 275.498 c
+497.263 275.252 496.95 275.131 496.579 275.131 c
+496.087 275.131 495.711 275.281 495.447 275.586 c
+495.189 275.898 495.065 276.366 495.065 276.983 c
+495.065 277.424 l
+495.065 278.019 495.189 278.475 495.447 278.791 c
+495.711 279.103 496.087 279.261 496.579 279.261 c
+496.979 279.261 497.299 279.129 497.534 278.864 c
+497.776 278.607 497.909 278.261 497.93 277.82 c
+497.313 277.82 l
+497.292 278.115 497.218 278.335 497.093 278.482 c
+496.976 278.629 496.803 278.703 496.579 278.703 c
+496.284 278.703 496.068 278.603 495.931 278.408 c
+495.792 278.221 495.719 277.912 495.711 277.482 c
+495.711 276.968 l
+495.711 276.497 495.777 276.163 495.917 275.969 c
+496.064 275.781 496.284 275.689 496.579 275.689 c
+500.724 275.204 m
+500.683 275.292 500.658 275.439 500.65 275.645 c
+500.415 275.3 500.121 275.131 499.769 275.131 c
+499.405 275.131 499.121 275.226 498.915 275.425 c
+498.717 275.63 498.622 275.917 498.622 276.291 c
+498.622 276.692 498.757 277.012 499.033 277.247 c
+499.305 277.49 499.68 277.615 500.151 277.615 c
+500.635 277.615 l
+500.635 278.041 l
+500.635 278.276 500.581 278.442 500.474 278.541 c
+500.363 278.647 500.202 278.703 499.989 278.703 c
+499.79 278.703 499.628 278.643 499.503 278.526 c
+499.386 278.408 499.327 278.261 499.327 278.085 c
+498.68 278.085 l
+498.68 278.28 498.74 278.471 498.857 278.658 c
+498.982 278.842 499.144 278.989 499.342 279.1 c
+499.548 279.206 499.775 279.261 500.033 279.261 c
+500.434 279.261 500.739 279.158 500.945 278.953 c
+501.157 278.747 501.271 278.452 501.282 278.07 c
+501.282 276.056 l
+501.282 275.751 501.319 275.487 501.4 275.263 c
+501.4 275.204 l
+h
+499.856 275.718 m
+500.022 275.718 500.172 275.763 500.312 275.851 c
+500.459 275.939 500.565 276.05 500.635 276.189 c
+500.635 277.13 l
+500.268 277.13 l
+499.952 277.13 499.709 277.06 499.534 276.924 c
+499.357 276.796 499.268 276.607 499.268 276.366 c
+499.268 276.137 499.312 275.972 499.401 275.865 c
+499.489 275.766 499.64 275.718 499.856 275.718 c
+503.928 278.57 m
+503.84 278.589 503.74 278.6 503.634 278.6 c
+503.3 278.6 503.065 278.416 502.929 278.055 c
+502.929 275.204 l
+502.281 275.204 l
+502.281 279.188 l
+502.914 279.188 l
+502.929 278.776 l
+503.105 279.1 503.347 279.261 503.663 279.261 c
+503.771 279.261 503.858 279.239 503.928 279.202 c
+h
+504.369 277.38 m
+504.369 277.986 504.479 278.452 504.708 278.776 c
+504.943 279.1 505.269 279.261 505.692 279.261 c
+506.074 279.261 506.372 279.103 506.589 278.791 c
+506.589 280.848 l
+507.235 280.848 l
+507.235 275.204 l
+506.647 275.204 l
+506.603 275.63 l
+506.397 275.296 506.092 275.131 505.692 275.131 c
+505.28 275.131 504.957 275.285 504.722 275.601 c
+504.486 275.924 504.369 276.38 504.369 276.968 c
+h
+505.016 276.997 m
+505.016 276.557 505.078 276.226 505.207 276.013 c
+505.343 275.807 505.564 275.703 505.868 275.703 c
+506.192 275.703 506.431 275.865 506.589 276.189 c
+506.589 278.203 l
+506.42 278.515 506.181 278.673 505.868 278.673 c
+505.564 278.673 505.343 278.57 505.207 278.365 c
+505.078 278.159 505.016 277.835 505.016 277.394 c
+h
+509.661 275.131 m
+509.161 275.131 508.779 275.277 508.514 275.572 c
+508.249 275.865 508.118 276.299 508.118 276.879 c
+508.118 277.35 l
+508.118 277.945 508.242 278.412 508.5 278.747 c
+508.764 279.088 509.124 279.261 509.587 279.261 c
+510.046 279.261 510.389 279.107 510.616 278.805 c
+510.852 278.512 510.973 278.049 510.983 277.424 c
+510.983 276.997 l
+508.764 276.997 l
+508.764 276.909 l
+508.764 276.476 508.841 276.163 508.999 275.969 c
+509.165 275.781 509.396 275.689 509.69 275.689 c
+509.885 275.689 510.058 275.722 510.204 275.792 c
+510.352 275.869 510.487 275.987 510.616 276.145 c
+510.954 275.734 l
+510.668 275.329 510.237 275.131 509.661 275.131 c
+509.587 278.703 m
+509.312 278.703 509.109 278.607 508.984 278.423 c
+508.856 278.236 508.783 277.945 508.764 277.556 c
+510.337 277.556 l
+510.337 277.644 l
+510.315 278.026 510.249 278.294 510.131 278.452 c
+510.013 278.618 509.83 278.703 509.587 278.703 c
+511.63 277.38 m
+511.63 277.986 511.741 278.452 511.968 278.776 c
+512.203 279.1 512.531 279.261 512.953 279.261 c
+513.335 279.261 513.633 279.103 513.85 278.791 c
+513.85 280.848 l
+514.497 280.848 l
+514.497 275.204 l
+513.909 275.204 l
+513.865 275.63 l
+513.659 275.296 513.354 275.131 512.953 275.131 c
+512.542 275.131 512.218 275.285 511.983 275.601 c
+511.748 275.924 511.63 276.38 511.63 276.968 c
+h
+512.277 276.997 m
+512.277 276.557 512.34 276.226 512.469 276.013 c
+512.604 275.807 512.824 275.703 513.13 275.703 c
+513.453 275.703 513.692 275.865 513.85 276.189 c
+513.85 278.203 l
+513.681 278.515 513.442 278.673 513.13 278.673 c
+512.824 278.673 512.604 278.57 512.469 278.365 c
+512.34 278.159 512.277 277.835 512.277 277.394 c
+h
+515.54 275.557 m
+515.54 275.674 515.573 275.77 515.644 275.851 c
+515.71 275.928 515.812 275.969 515.952 275.969 c
+516.099 275.969 516.205 275.928 516.275 275.851 c
+516.352 275.77 516.392 275.674 516.392 275.557 c
+516.392 275.447 516.352 275.354 516.275 275.277 c
+516.205 275.2 516.099 275.16 515.952 275.16 c
+515.812 275.16 515.71 275.2 515.644 275.277 c
+515.573 275.354 515.54 275.447 515.54 275.557 c
+f
+0.113 0.082 0.09 0 k
+287.665 268.468 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 261.629 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.807 l
+-1.896 -1.807 l
+-1.896 -1.263 l
+-2.142 -1.256 -2.359 -1.219 -2.543 -1.161 c
+-2.719 -1.102 -2.873 -1.024 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.034 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.477 -2.234 -0.514 c
+-2.138 -0.554 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.662 l
+-1.907 0.662 -1.926 0.666 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.757 -3.322 1.985 c
+-3.322 2.18 -3.289 2.345 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.933 -2.645 3.003 -2.469 3.043 c
+-2.293 3.091 -2.102 3.12 -1.896 3.132 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.132 l
+-1.319 3.12 -1.128 3.091 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.621 -0.279 2.484 c
+-0.202 2.345 -0.143 2.183 -0.103 1.999 c
+-0.941 1.882 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.368 l
+-1.514 1.368 l
+-1.506 1.368 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.32 0.871 -0.213 0.743 -0.132 0.588 c
+-0.044 0.431 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.503 -2.113 2.484 -2.19 2.455 c
+-2.26 2.426 -2.319 2.386 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.191 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.838 -2.439 1.779 c
+-2.41 1.721 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.201 -0.837 -0.014 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.634 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.395 c
+5.284 -2.314 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.83 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.219 6.034 -1.219 c
+5.829 -1.219 5.644 -1.182 5.49 -1.102 c
+5.343 -1.014 5.215 -0.897 5.108 -0.749 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.019 4.888 1.235 c
+4.946 1.449 5.027 1.632 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.188 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.919 7.107 1.97 c
+7.115 2.029 7.122 2.077 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.029 7.975 1.882 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.414 7.938 -1.635 7.871 -1.822 c
+7.802 -2.006 7.699 -2.16 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.743 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.368 c
+5.88 1.279 5.835 1.162 5.799 1.015 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.514 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.603 l
+9.199 1.603 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.597 1.603 m
+13.053 1.603 l
+13.053 2.22 l
+13.641 2.22 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.22 l
+15.728 2.22 l
+15.728 1.603 l
+14.494 1.603 l
+14.494 -0.103 l
+14.494 -0.323 l
+14.501 -0.392 14.523 -0.455 14.552 -0.514 c
+14.589 -0.565 14.645 -0.61 14.714 -0.646 c
+14.791 -0.675 14.905 -0.69 15.052 -0.69 c
+15.188 -0.69 15.324 -0.687 15.464 -0.675 c
+15.599 -0.658 15.732 -0.631 15.861 -0.602 c
+15.861 -1.205 l
+15.78 -1.215 15.703 -1.23 15.626 -1.249 c
+15.545 -1.26 15.468 -1.267 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.3 14.938 -1.308 14.832 -1.308 c
+14.645 -1.308 14.483 -1.294 14.346 -1.263 c
+14.218 -1.227 14.104 -1.182 14.009 -1.132 c
+13.92 -1.084 13.847 -1.024 13.788 -0.955 c
+13.729 -0.878 13.685 -0.801 13.656 -0.72 c
+13.627 -0.631 13.604 -0.544 13.597 -0.455 c
+13.586 -0.359 13.582 -0.264 13.582 -0.176 c
+h
+24.133 1.47 m
+24.033 1.478 23.931 1.489 23.823 1.5 c
+23.713 1.518 23.592 1.529 23.456 1.529 c
+23.28 1.529 23.121 1.489 22.986 1.411 c
+22.846 1.341 22.728 1.243 22.633 1.118 c
+22.545 0.989 22.475 0.842 22.427 0.676 c
+22.387 0.508 22.369 0.331 22.369 0.148 c
+22.369 -1.263 l
+21.471 -1.263 l
+21.471 0.985 l
+21.471 1.11 21.461 1.235 21.442 1.353 c
+21.432 1.478 21.417 1.595 21.398 1.706 c
+21.388 1.823 21.373 1.919 21.355 1.999 c
+21.332 2.087 21.314 2.161 21.296 2.22 c
+22.177 2.22 l
+22.185 2.168 22.196 2.117 22.207 2.058 c
+22.225 1.999 22.24 1.933 22.251 1.867 c
+22.269 1.808 22.284 1.742 22.296 1.676 c
+22.302 1.607 22.313 1.544 22.325 1.485 c
+22.339 1.485 l
+22.375 1.603 22.427 1.709 22.486 1.808 c
+22.552 1.904 22.633 1.989 22.722 2.058 c
+22.809 2.124 22.913 2.18 23.03 2.22 c
+23.155 2.257 23.302 2.278 23.471 2.278 c
+23.596 2.278 23.713 2.272 23.823 2.264 c
+23.942 2.253 24.044 2.238 24.133 2.22 c
+h
+26.738 -1.323 m
+26.481 -1.323 26.253 -1.286 26.047 -1.219 c
+25.841 -1.142 25.665 -1.028 25.518 -0.881 c
+25.371 -0.727 25.253 -0.536 25.165 -0.309 c
+25.084 -0.084 25.047 0.181 25.047 0.485 c
+25.047 0.817 25.091 1.096 25.18 1.324 c
+25.275 1.559 25.404 1.742 25.562 1.882 c
+25.727 2.018 25.915 2.117 26.121 2.176 c
+26.327 2.242 26.535 2.278 26.753 2.278 c
+27.025 2.278 27.26 2.228 27.458 2.132 c
+27.664 2.043 27.829 1.912 27.958 1.735 c
+28.094 1.566 28.193 1.36 28.252 1.118 c
+28.318 0.882 28.355 0.618 28.355 0.324 c
+28.355 0.31 l
+25.988 0.31 l
+25.988 0.162 26.003 0.023 26.032 -0.103 c
+26.069 -0.231 26.124 -0.345 26.194 -0.44 c
+26.26 -0.529 26.344 -0.598 26.444 -0.646 c
+26.539 -0.698 26.653 -0.72 26.782 -0.72 c
+26.936 -0.72 27.076 -0.687 27.194 -0.617 c
+27.318 -0.55 27.407 -0.448 27.458 -0.309 c
+28.296 -0.382 l
+28.266 -0.481 28.212 -0.588 28.135 -0.706 c
+28.054 -0.816 27.95 -0.918 27.825 -1.014 c
+27.708 -1.102 27.553 -1.176 27.37 -1.234 c
+27.194 -1.294 26.98 -1.323 26.738 -1.323 c
+26.738 1.706 m
+26.649 1.706 26.562 1.691 26.473 1.661 c
+26.385 1.632 26.304 1.58 26.238 1.515 c
+26.169 1.445 26.109 1.357 26.061 1.25 c
+26.021 1.139 26.003 1.015 26.003 0.867 c
+27.472 0.867 l
+27.472 1.004 27.447 1.125 27.399 1.235 c
+27.359 1.341 27.304 1.43 27.237 1.5 c
+27.179 1.566 27.106 1.617 27.017 1.646 c
+26.929 1.684 26.834 1.706 26.738 1.706 c
+31.269 -1.263 m
+30.196 -1.263 l
+28.887 2.22 l
+29.843 2.22 l
+30.504 0.221 l
+30.523 0.151 30.541 0.078 30.564 0 c
+30.593 -0.07 30.615 -0.139 30.637 -0.205 c
+30.655 -0.275 30.67 -0.345 30.681 -0.411 c
+30.699 -0.481 30.718 -0.54 30.74 -0.588 c
+30.747 -0.54 30.762 -0.481 30.784 -0.411 c
+30.802 -0.345 30.82 -0.275 30.842 -0.205 c
+30.861 -0.139 30.879 -0.07 30.901 0 c
+30.931 0.067 30.953 0.136 30.975 0.206 c
+31.68 2.22 l
+32.621 2.22 l
+h
+34.859 -1.323 m
+34.602 -1.323 34.374 -1.286 34.169 -1.219 c
+33.963 -1.142 33.786 -1.028 33.639 -0.881 c
+33.492 -0.727 33.375 -0.536 33.286 -0.309 c
+33.205 -0.084 33.169 0.181 33.169 0.485 c
+33.169 0.817 33.213 1.096 33.301 1.324 c
+33.396 1.559 33.525 1.742 33.683 1.882 c
+33.849 2.018 34.036 2.117 34.242 2.176 c
+34.447 2.242 34.657 2.278 34.874 2.278 c
+35.146 2.278 35.381 2.228 35.58 2.132 c
+35.785 2.043 35.95 1.912 36.079 1.735 c
+36.215 1.566 36.314 1.36 36.373 1.118 c
+36.44 0.882 36.476 0.618 36.476 0.324 c
+36.476 0.31 l
+34.109 0.31 l
+34.109 0.162 34.124 0.023 34.154 -0.103 c
+34.19 -0.231 34.246 -0.345 34.315 -0.44 c
+34.381 -0.529 34.466 -0.598 34.565 -0.646 c
+34.661 -0.698 34.774 -0.72 34.903 -0.72 c
+35.058 -0.72 35.197 -0.687 35.314 -0.617 c
+35.44 -0.55 35.528 -0.448 35.58 -0.309 c
+36.417 -0.382 l
+36.388 -0.481 36.332 -0.588 36.255 -0.706 c
+36.174 -0.816 36.072 -0.918 35.947 -1.014 c
+35.829 -1.102 35.675 -1.176 35.491 -1.234 c
+35.314 -1.294 35.102 -1.323 34.859 -1.323 c
+34.859 1.706 m
+34.771 1.706 34.682 1.691 34.595 1.661 c
+34.506 1.632 34.425 1.58 34.36 1.515 c
+34.29 1.445 34.231 1.357 34.183 1.25 c
+34.142 1.139 34.124 1.015 34.124 0.867 c
+35.594 0.867 l
+35.594 1.004 35.568 1.125 35.52 1.235 c
+35.48 1.341 35.425 1.43 35.359 1.5 c
+35.3 1.566 35.227 1.617 35.138 1.646 c
+35.05 1.684 34.955 1.706 34.859 1.706 c
+40.378 1.47 m
+40.28 1.478 40.176 1.489 40.07 1.5 c
+39.96 1.518 39.838 1.529 39.702 1.529 c
+39.526 1.529 39.368 1.489 39.232 1.411 c
+39.092 1.341 38.975 1.243 38.879 1.118 c
+38.791 0.989 38.721 0.842 38.674 0.676 c
+38.633 0.508 38.615 0.331 38.615 0.148 c
+38.615 -1.263 l
+37.718 -1.263 l
+37.718 0.985 l
+37.718 1.11 37.707 1.235 37.689 1.353 c
+37.678 1.478 37.663 1.595 37.645 1.706 c
+37.633 1.823 37.619 1.919 37.6 1.999 c
+37.579 2.087 37.56 2.161 37.542 2.22 c
+38.424 2.22 l
+38.431 2.168 38.442 2.117 38.453 2.058 c
+38.472 1.999 38.486 1.933 38.497 1.867 c
+38.516 1.808 38.53 1.742 38.541 1.676 c
+38.549 1.607 38.56 1.544 38.57 1.485 c
+38.585 1.485 l
+38.622 1.603 38.674 1.709 38.732 1.808 c
+38.798 1.904 38.879 1.989 38.967 2.058 c
+39.056 2.124 39.158 2.18 39.276 2.22 c
+39.401 2.257 39.548 2.278 39.717 2.278 c
+39.842 2.278 39.96 2.272 40.07 2.264 c
+40.187 2.253 40.29 2.238 40.378 2.22 c
+h
+42.028 1.603 m
+41.485 1.603 l
+41.485 2.22 l
+42.073 2.22 l
+42.352 3.117 l
+42.925 3.117 l
+42.925 2.22 l
+44.16 2.22 l
+44.16 1.603 l
+42.925 1.603 l
+42.925 -0.103 l
+42.925 -0.323 l
+42.933 -0.392 42.954 -0.455 42.984 -0.514 c
+43.021 -0.565 43.076 -0.61 43.146 -0.646 c
+43.223 -0.675 43.337 -0.69 43.484 -0.69 c
+43.619 -0.69 43.756 -0.687 43.895 -0.675 c
+44.031 -0.658 44.164 -0.631 44.292 -0.602 c
+44.292 -1.205 l
+44.211 -1.215 44.134 -1.23 44.057 -1.249 c
+43.976 -1.26 43.899 -1.267 43.822 -1.278 c
+43.741 -1.286 43.653 -1.294 43.557 -1.294 c
+43.469 -1.3 43.37 -1.308 43.264 -1.308 c
+43.076 -1.308 42.914 -1.294 42.778 -1.263 c
+42.649 -1.227 42.536 -1.182 42.441 -1.132 c
+42.352 -1.084 42.279 -1.024 42.219 -0.955 c
+42.161 -0.878 42.117 -0.801 42.088 -0.72 c
+42.058 -0.631 42.036 -0.544 42.028 -0.455 c
+42.017 -0.359 42.013 -0.264 42.013 -0.176 c
+h
+50.297 -2.63 m
+50.297 3.514 l
+52.222 3.514 l
+52.222 2.897 l
+51.149 2.897 l
+51.149 -2.013 l
+52.222 -2.013 l
+52.222 -2.63 l
+h
+55.166 -1.323 m
+54.879 -1.323 54.636 -1.282 54.431 -1.205 c
+54.225 -1.117 54.052 -0.995 53.917 -0.837 c
+53.776 -0.683 53.674 -0.496 53.608 -0.278 c
+53.538 -0.055 53.505 0.192 53.505 0.456 c
+53.505 0.75 53.538 1.008 53.608 1.235 c
+53.685 1.459 53.791 1.646 53.931 1.794 c
+54.078 1.948 54.254 2.066 54.46 2.147 c
+54.666 2.234 54.902 2.278 55.166 2.278 c
+55.39 2.278 55.592 2.249 55.769 2.191 c
+55.945 2.132 56.095 2.047 56.224 1.941 c
+56.349 1.842 56.452 1.721 56.533 1.573 c
+56.61 1.434 56.665 1.283 56.695 1.118 c
+55.783 1.073 l
+55.754 1.25 55.684 1.389 55.577 1.5 c
+55.478 1.607 55.335 1.661 55.151 1.661 c
+54.905 1.661 54.728 1.559 54.622 1.353 c
+54.512 1.154 54.46 0.867 54.46 0.485 c
+54.46 -0.309 54.696 -0.706 55.166 -0.706 c
+55.331 -0.706 55.474 -0.654 55.592 -0.544 c
+55.71 -0.436 55.783 -0.275 55.812 -0.058 c
+56.724 -0.103 l
+56.695 -0.272 56.639 -0.426 56.562 -0.573 c
+56.492 -0.72 56.39 -0.852 56.253 -0.97 c
+56.125 -1.08 55.966 -1.168 55.783 -1.234 c
+55.607 -1.294 55.401 -1.323 55.166 -1.323 c
+60.876 0.485 m
+60.876 0.21 60.84 -0.04 60.774 -0.264 c
+60.704 -0.481 60.601 -0.668 60.465 -0.823 c
+60.325 -0.98 60.149 -1.102 59.935 -1.19 c
+59.719 -1.278 59.465 -1.323 59.172 -1.323 c
+58.896 -1.323 58.649 -1.278 58.436 -1.19 c
+58.231 -1.102 58.058 -0.98 57.922 -0.823 c
+57.782 -0.668 57.68 -0.481 57.613 -0.264 c
+57.543 -0.04 57.51 0.21 57.51 0.485 c
+57.51 0.739 57.539 0.975 57.599 1.191 c
+57.665 1.415 57.768 1.607 57.907 1.764 c
+58.044 1.929 58.22 2.058 58.436 2.147 c
+58.649 2.234 58.906 2.278 59.201 2.278 c
+59.513 2.278 59.774 2.234 59.98 2.147 c
+60.193 2.058 60.365 1.929 60.494 1.764 c
+60.631 1.607 60.729 1.415 60.788 1.191 c
+60.847 0.975 60.876 0.739 60.876 0.485 c
+59.921 0.485 m
+59.921 0.691 59.906 0.867 59.877 1.015 c
+59.855 1.162 59.818 1.283 59.76 1.382 c
+59.7 1.478 59.627 1.548 59.538 1.588 c
+59.451 1.636 59.34 1.661 59.216 1.661 c
+58.951 1.661 58.76 1.563 58.642 1.368 c
+58.524 1.18 58.466 0.886 58.466 0.485 c
+58.466 0.063 58.524 -0.242 58.642 -0.426 c
+58.76 -0.613 58.936 -0.706 59.172 -0.706 c
+59.297 -0.706 59.411 -0.687 59.509 -0.646 c
+59.605 -0.598 59.686 -0.525 59.744 -0.426 c
+59.81 -0.33 59.855 -0.205 59.877 -0.058 c
+59.906 0.088 59.921 0.269 59.921 0.485 c
+62.894 -1.263 m
+62.894 0.853 l
+62.894 1.019 62.887 1.154 62.879 1.264 c
+62.868 1.372 62.85 1.455 62.821 1.515 c
+62.798 1.58 62.769 1.632 62.732 1.661 c
+62.703 1.691 62.663 1.706 62.615 1.706 c
+62.555 1.706 62.501 1.676 62.453 1.617 c
+62.412 1.566 62.379 1.492 62.35 1.397 c
+62.32 1.309 62.295 1.195 62.277 1.058 c
+62.266 0.919 62.262 0.769 62.262 0.603 c
+62.262 -1.263 l
+61.512 -1.263 l
+61.512 1.47 l
+61.512 1.706 l
+61.512 1.926 l
+61.512 2.003 61.505 2.066 61.497 2.117 c
+61.497 2.22 l
+62.173 2.22 l
+62.173 2.132 l
+62.173 1.985 l
+62.181 1.926 62.189 1.867 62.189 1.808 c
+62.189 1.646 l
+62.203 1.646 l
+62.221 1.735 62.251 1.812 62.291 1.882 c
+62.328 1.96 62.372 2.029 62.424 2.087 c
+62.482 2.147 62.549 2.191 62.629 2.22 c
+62.707 2.257 62.794 2.278 62.894 2.278 c
+63.078 2.278 63.218 2.224 63.305 2.117 c
+63.401 2.018 63.471 1.86 63.511 1.646 c
+63.526 1.646 l
+63.563 1.742 63.603 1.831 63.644 1.912 c
+63.691 1.989 63.746 2.051 63.805 2.103 c
+63.864 2.161 63.93 2.205 64.011 2.234 c
+64.088 2.264 64.176 2.278 64.275 2.278 c
+64.411 2.278 64.525 2.253 64.614 2.205 c
+64.702 2.154 64.768 2.08 64.82 1.985 c
+64.878 1.885 64.915 1.757 64.937 1.603 c
+64.967 1.455 64.981 1.272 64.981 1.058 c
+64.981 -1.263 l
+64.261 -1.263 l
+64.261 0.853 l
+64.261 1.019 64.253 1.154 64.246 1.264 c
+64.236 1.372 64.217 1.455 64.188 1.515 c
+64.165 1.58 64.136 1.632 64.099 1.661 c
+64.07 1.691 64.03 1.706 63.982 1.706 c
+63.864 1.706 63.768 1.617 63.702 1.441 c
+63.644 1.272 63.614 1.015 63.614 0.662 c
+63.614 -1.263 l
+h
+66.954 -1.263 m
+66.954 0.853 l
+66.954 1.019 66.947 1.154 66.94 1.264 c
+66.929 1.372 66.91 1.455 66.881 1.515 c
+66.859 1.58 66.829 1.632 66.793 1.661 c
+66.763 1.691 66.723 1.706 66.675 1.706 c
+66.617 1.706 66.561 1.676 66.513 1.617 c
+66.473 1.566 66.44 1.492 66.411 1.397 c
+66.381 1.309 66.355 1.195 66.337 1.058 c
+66.326 0.919 66.322 0.769 66.322 0.603 c
+66.322 -1.263 l
+65.572 -1.263 l
+65.572 1.47 l
+65.572 1.706 l
+65.572 1.926 l
+65.572 2.003 65.566 2.066 65.558 2.117 c
+65.558 2.22 l
+66.235 2.22 l
+66.235 2.132 l
+66.235 1.985 l
+66.241 1.926 66.249 1.867 66.249 1.808 c
+66.249 1.646 l
+66.264 1.646 l
+66.282 1.735 66.312 1.812 66.352 1.882 c
+66.389 1.96 66.432 2.029 66.484 2.087 c
+66.543 2.147 66.609 2.191 66.69 2.22 c
+66.767 2.257 66.856 2.278 66.954 2.278 c
+67.138 2.278 67.278 2.224 67.366 2.117 c
+67.461 2.018 67.531 1.86 67.571 1.646 c
+67.586 1.646 l
+67.623 1.742 67.664 1.831 67.704 1.912 c
+67.752 1.989 67.807 2.051 67.866 2.103 c
+67.924 2.161 67.991 2.205 68.072 2.234 c
+68.149 2.264 68.237 2.278 68.336 2.278 c
+68.472 2.278 68.586 2.253 68.674 2.205 c
+68.762 2.154 68.828 2.08 68.88 1.985 c
+68.939 1.885 68.976 1.757 68.998 1.603 c
+69.027 1.455 69.042 1.272 69.042 1.058 c
+69.042 -1.263 l
+68.321 -1.263 l
+68.321 0.853 l
+68.321 1.019 68.314 1.154 68.307 1.264 c
+68.296 1.372 68.277 1.455 68.248 1.515 c
+68.226 1.58 68.196 1.632 68.159 1.661 c
+68.13 1.691 68.09 1.706 68.042 1.706 c
+67.924 1.706 67.829 1.617 67.763 1.441 c
+67.704 1.272 67.675 1.015 67.675 0.662 c
+67.675 -1.263 l
+h
+71.956 -0.646 m
+73.088 -0.646 l
+73.088 -1.263 l
+69.78 -1.263 l
+69.78 -0.646 l
+71.045 -0.646 l
+71.045 1.603 l
+70.118 1.603 l
+70.118 2.22 l
+71.956 2.22 l
+h
+71.045 3.514 0.912 -0.676 re
+71.045 2.837 m
+74.517 1.603 m
+73.973 1.603 l
+73.973 2.22 l
+74.561 2.22 l
+74.841 3.117 l
+75.413 3.117 l
+75.413 2.22 l
+76.649 2.22 l
+76.649 1.603 l
+75.413 1.603 l
+75.413 -0.103 l
+75.413 -0.323 l
+75.421 -0.392 75.444 -0.455 75.473 -0.514 c
+75.509 -0.565 75.564 -0.61 75.634 -0.646 c
+75.712 -0.675 75.825 -0.69 75.972 -0.69 c
+76.109 -0.69 76.244 -0.687 76.384 -0.675 c
+76.52 -0.658 76.652 -0.631 76.78 -0.602 c
+76.78 -1.205 l
+76.7 -1.215 76.623 -1.23 76.545 -1.249 c
+76.465 -1.26 76.388 -1.267 76.31 -1.278 c
+76.23 -1.286 76.142 -1.294 76.046 -1.294 c
+75.957 -1.3 75.858 -1.308 75.752 -1.308 c
+75.564 -1.308 75.403 -1.294 75.267 -1.263 c
+75.138 -1.227 75.024 -1.182 74.929 -1.132 c
+74.841 -1.084 74.767 -1.024 74.708 -0.955 c
+74.65 -0.878 74.605 -0.801 74.576 -0.72 c
+74.546 -0.631 74.524 -0.544 74.517 -0.455 c
+74.507 -0.359 74.503 -0.264 74.503 -0.176 c
+h
+85.141 -0.249 m
+85.141 -0.419 85.1 -0.569 85.023 -0.706 c
+84.953 -0.833 84.851 -0.947 84.715 -1.043 c
+84.586 -1.132 84.424 -1.201 84.229 -1.249 c
+84.042 -1.296 83.826 -1.323 83.583 -1.323 c
+83.355 -1.323 83.157 -1.308 82.98 -1.278 c
+82.804 -1.249 82.646 -1.201 82.509 -1.132 c
+82.37 -1.055 82.26 -0.955 82.172 -0.837 c
+82.083 -0.72 82.014 -0.573 81.966 -0.396 c
+82.775 -0.278 l
+82.793 -0.378 82.822 -0.455 82.862 -0.514 c
+82.91 -0.573 82.969 -0.617 83.039 -0.646 c
+83.105 -0.675 83.186 -0.702 83.274 -0.72 c
+83.363 -0.731 83.465 -0.735 83.583 -0.735 c
+83.679 -0.735 83.774 -0.731 83.862 -0.72 c
+83.951 -0.702 84.028 -0.675 84.097 -0.646 c
+84.163 -0.617 84.215 -0.58 84.244 -0.529 c
+84.281 -0.481 84.303 -0.419 84.303 -0.338 c
+84.303 -0.242 84.273 -0.168 84.215 -0.118 c
+84.163 -0.07 84.097 -0.029 84.009 0 c
+83.921 0.038 83.81 0.067 83.685 0.088 c
+83.568 0.118 83.436 0.148 83.288 0.177 c
+83.149 0.214 83.01 0.254 82.862 0.294 c
+82.723 0.342 82.598 0.405 82.48 0.485 c
+82.37 0.563 82.282 0.662 82.216 0.78 c
+82.146 0.897 82.112 1.048 82.112 1.235 c
+82.112 1.389 82.143 1.532 82.201 1.661 c
+82.268 1.798 82.363 1.912 82.48 1.999 c
+82.605 2.087 82.763 2.154 82.951 2.205 c
+83.134 2.253 83.348 2.278 83.583 2.278 c
+83.766 2.278 83.943 2.257 84.112 2.22 c
+84.277 2.191 84.424 2.135 84.553 2.058 c
+84.678 1.989 84.788 1.889 84.876 1.764 c
+84.965 1.646 85.023 1.503 85.052 1.338 c
+84.259 1.264 l
+84.237 1.341 84.207 1.405 84.171 1.455 c
+84.13 1.515 84.082 1.559 84.024 1.588 c
+83.972 1.625 83.91 1.65 83.833 1.661 c
+83.752 1.669 83.671 1.676 83.583 1.676 c
+83.366 1.676 83.205 1.646 83.097 1.588 c
+82.987 1.536 82.936 1.449 82.936 1.324 c
+82.936 1.243 82.954 1.18 82.995 1.133 c
+83.043 1.081 83.105 1.044 83.186 1.015 c
+83.274 0.985 83.369 0.956 83.48 0.927 c
+83.587 0.904 83.708 0.882 83.847 0.853 c
+84.001 0.823 84.159 0.784 84.318 0.736 c
+84.472 0.684 84.612 0.622 84.73 0.545 c
+84.847 0.464 84.942 0.36 85.023 0.235 c
+85.1 0.107 85.141 -0.055 85.141 -0.249 c
+86.997 1.515 m
+87.114 1.786 87.265 1.985 87.452 2.103 c
+87.636 2.22 87.857 2.278 88.113 2.278 c
+88.319 2.278 88.489 2.242 88.628 2.176 c
+88.775 2.106 88.886 2.014 88.967 1.897 c
+89.054 1.779 89.113 1.636 89.143 1.47 c
+89.179 1.301 89.202 1.125 89.202 0.941 c
+89.202 -1.263 l
+88.29 -1.263 l
+88.29 0.736 l
+88.29 0.871 88.279 0.992 88.261 1.103 c
+88.25 1.21 88.224 1.297 88.187 1.368 c
+88.147 1.445 88.088 1.503 88.011 1.544 c
+87.941 1.58 87.849 1.603 87.731 1.603 c
+87.621 1.603 87.525 1.577 87.438 1.529 c
+87.35 1.478 87.269 1.411 87.203 1.324 c
+87.144 1.235 87.092 1.125 87.055 1 c
+87.026 0.882 87.011 0.75 87.011 0.603 c
+87.011 -1.263 l
+86.1 -1.263 l
+86.1 3.514 l
+87.011 3.514 l
+87.011 2.205 l
+87.011 2.135 87.004 2.066 86.997 1.999 c
+86.997 1.794 l
+86.997 1.735 86.989 1.68 86.982 1.632 c
+86.982 1.515 l
+h
+91.116 -1.323 m
+90.947 -1.323 90.796 -1.3 90.66 -1.263 c
+90.532 -1.215 90.418 -1.146 90.322 -1.058 c
+90.234 -0.97 90.164 -0.863 90.116 -0.735 c
+90.065 -0.598 90.043 -0.448 90.043 -0.278 c
+90.043 -0.073 90.076 0.096 90.145 0.235 c
+90.212 0.383 90.307 0.493 90.425 0.574 c
+90.55 0.662 90.694 0.724 90.851 0.765 c
+91.017 0.802 91.193 0.827 91.381 0.838 c
+92.101 0.853 l
+92.101 1.029 l
+92.101 1.147 92.09 1.25 92.071 1.338 c
+92.049 1.426 92.017 1.492 91.969 1.544 c
+91.928 1.603 91.88 1.64 91.822 1.661 c
+91.763 1.68 91.697 1.691 91.631 1.691 c
+91.56 1.691 91.498 1.68 91.44 1.661 c
+91.388 1.65 91.34 1.625 91.292 1.588 c
+91.252 1.559 91.219 1.507 91.19 1.441 c
+91.167 1.382 91.153 1.301 91.145 1.206 c
+90.205 1.25 l
+90.234 1.397 90.278 1.532 90.337 1.661 c
+90.403 1.786 90.499 1.897 90.616 1.985 c
+90.734 2.08 90.874 2.154 91.043 2.205 c
+91.219 2.253 91.425 2.278 91.66 2.278 c
+92.101 2.278 92.431 2.168 92.659 1.956 c
+92.894 1.75 93.012 1.441 93.012 1.029 c
+93.012 -0.235 l
+93.012 -0.455 l
+93.019 -0.514 93.034 -0.569 93.056 -0.617 c
+93.075 -0.658 93.104 -0.69 93.145 -0.72 c
+93.181 -0.742 93.233 -0.749 93.291 -0.749 c
+93.358 -0.749 93.428 -0.745 93.497 -0.735 c
+93.497 -1.219 l
+93.438 -1.23 93.383 -1.242 93.335 -1.249 c
+93.295 -1.26 93.255 -1.267 93.218 -1.278 c
+93.177 -1.286 93.133 -1.294 93.086 -1.294 c
+93.034 -1.3 92.975 -1.308 92.909 -1.308 c
+92.682 -1.308 92.516 -1.256 92.41 -1.146 c
+92.3 -1.028 92.237 -0.863 92.219 -0.646 c
+92.204 -0.646 l
+92.134 -0.756 92.065 -0.852 91.998 -0.941 c
+91.928 -1.021 91.851 -1.087 91.763 -1.146 c
+91.675 -1.205 91.575 -1.249 91.469 -1.278 c
+91.369 -1.308 91.252 -1.323 91.116 -1.323 c
+92.101 0.353 m
+91.675 0.339 l
+91.575 0.339 91.483 0.331 91.396 0.324 c
+91.315 0.313 91.248 0.287 91.19 0.25 c
+91.13 0.21 91.08 0.151 91.043 0.074 c
+91.002 0.004 90.984 -0.087 90.984 -0.205 c
+90.984 -0.374 91.017 -0.496 91.086 -0.573 c
+91.153 -0.654 91.252 -0.69 91.381 -0.69 c
+91.487 -0.69 91.587 -0.668 91.675 -0.617 c
+91.77 -0.569 91.851 -0.507 91.91 -0.426 c
+91.976 -0.349 92.027 -0.261 92.057 -0.161 c
+92.086 -0.055 92.101 0.059 92.101 0.177 c
+h
+94.589 -2.63 m
+94.589 -2.013 l
+95.662 -2.013 l
+95.662 2.897 l
+94.589 2.897 l
+94.589 3.514 l
+96.514 3.514 l
+96.514 -2.63 l
+h
+f
+Q
+q 1 0 0 1 314.6631 247.1802 cm
+0 0 m
+-0.029 -0.58 -0.191 -1.022 -0.485 -1.323 c
+-0.779 -1.627 -1.198 -1.779 -1.735 -1.779 c
+-2.263 -1.779 -2.69 -1.58 -3.013 -1.176 c
+-3.329 -0.764 -3.484 -0.206 -3.484 0.5 c
+-3.484 1.47 l
+-3.484 2.165 -3.322 2.712 -2.999 3.117 c
+-2.675 3.516 -2.23 3.719 -1.661 3.719 c
+-1.154 3.719 -0.757 3.561 -0.47 3.248 c
+-0.187 2.944 -0.029 2.502 0 1.926 c
+-0.69 1.926 l
+-0.721 2.367 -0.816 2.679 -0.97 2.866 c
+-1.118 3.05 -1.349 3.146 -1.661 3.146 c
+-2.036 3.146 -2.319 2.999 -2.514 2.705 c
+-2.712 2.418 -2.807 2.003 -2.807 1.455 c
+-2.807 0.47 l
+-2.807 -0.07 -2.716 -0.485 -2.529 -0.779 c
+-2.344 -1.066 -2.08 -1.205 -1.735 -1.205 c
+-1.382 -1.205 -1.128 -1.117 -0.97 -0.941 c
+-0.816 -0.764 -0.721 -0.452 -0.69 0 c
+h
+2.454 1.661 m
+2.367 1.679 2.267 1.691 2.161 1.691 c
+1.826 1.691 1.591 1.507 1.455 1.147 c
+1.455 -1.705 l
+0.808 -1.705 l
+0.808 2.278 l
+1.44 2.278 l
+1.455 1.866 l
+1.631 2.19 1.874 2.352 2.19 2.352 c
+2.296 2.352 2.385 2.33 2.454 2.294 c
+h
+4.453 -1.779 m
+3.954 -1.779 3.572 -1.631 3.308 -1.338 c
+3.042 -1.043 2.911 -0.61 2.911 -0.029 c
+2.911 0.441 l
+2.911 1.037 3.036 1.503 3.293 1.837 c
+3.557 2.18 3.917 2.352 4.38 2.352 c
+4.839 2.352 5.182 2.198 5.409 1.897 c
+5.644 1.602 5.766 1.139 5.777 0.515 c
+5.777 0.088 l
+3.557 0.088 l
+3.557 0 l
+3.557 -0.434 3.634 -0.746 3.792 -0.941 c
+3.958 -1.128 4.189 -1.22 4.484 -1.22 c
+4.678 -1.22 4.85 -1.187 4.997 -1.117 c
+5.145 -1.04 5.28 -0.922 5.409 -0.764 c
+5.747 -1.176 l
+5.461 -1.58 5.031 -1.779 4.453 -1.779 c
+4.38 1.793 m
+4.104 1.793 3.902 1.698 3.778 1.514 c
+3.649 1.326 3.576 1.037 3.557 0.647 c
+5.13 0.647 l
+5.13 0.735 l
+5.108 1.118 5.041 1.386 4.924 1.544 c
+4.806 1.709 4.623 1.793 4.38 1.793 c
+8.599 -1.705 m
+8.559 -1.617 8.533 -1.469 8.525 -1.264 c
+8.29 -1.61 7.996 -1.779 7.644 -1.779 c
+7.279 -1.779 6.996 -1.683 6.791 -1.484 c
+6.593 -1.278 6.497 -0.992 6.497 -0.617 c
+6.497 -0.216 6.633 0.103 6.909 0.338 c
+7.181 0.58 7.555 0.706 8.025 0.706 c
+8.511 0.706 l
+8.511 1.132 l
+8.511 1.367 8.455 1.532 8.349 1.631 c
+8.239 1.738 8.077 1.793 7.864 1.793 c
+7.665 1.793 7.504 1.735 7.379 1.617 c
+7.262 1.5 7.202 1.353 7.202 1.176 c
+6.556 1.176 l
+6.556 1.371 6.614 1.562 6.732 1.749 c
+6.857 1.933 7.019 2.08 7.217 2.19 c
+7.423 2.297 7.651 2.352 7.908 2.352 c
+8.309 2.352 8.613 2.249 8.819 2.043 c
+9.033 1.837 9.147 1.544 9.157 1.161 c
+9.157 -0.852 l
+9.157 -1.157 9.194 -1.422 9.275 -1.646 c
+9.275 -1.705 l
+h
+7.732 -1.191 m
+7.897 -1.191 8.048 -1.147 8.187 -1.058 c
+8.334 -0.97 8.441 -0.86 8.511 -0.721 c
+8.511 0.22 l
+8.143 0.22 l
+7.827 0.22 7.584 0.151 7.408 0.015 c
+7.231 -0.114 7.144 -0.301 7.144 -0.544 c
+7.144 -0.771 7.188 -0.937 7.276 -1.043 c
+7.364 -1.143 7.515 -1.191 7.732 -1.191 c
+11.009 3.234 m
+11.009 2.278 l
+11.612 2.278 l
+11.612 1.749 l
+11.009 1.749 l
+11.009 -0.721 l
+11.009 -0.878 11.032 -0.995 11.083 -1.073 c
+11.142 -1.153 11.23 -1.191 11.347 -1.191 c
+11.436 -1.191 11.524 -1.176 11.612 -1.147 c
+11.612 -1.705 l
+11.465 -1.753 11.311 -1.779 11.156 -1.779 c
+10.899 -1.779 10.705 -1.687 10.568 -1.5 c
+10.429 -1.315 10.363 -1.055 10.363 -0.721 c
+10.363 1.749 l
+9.76 1.749 l
+9.76 2.278 l
+10.363 2.278 l
+10.363 3.234 l
+h
+13.803 -1.779 m
+13.303 -1.779 12.92 -1.631 12.656 -1.338 c
+12.391 -1.043 12.259 -0.61 12.259 -0.029 c
+12.259 0.441 l
+12.259 1.037 12.384 1.503 12.641 1.837 c
+12.906 2.18 13.266 2.352 13.729 2.352 c
+14.188 2.352 14.53 2.198 14.757 1.897 c
+14.993 1.602 15.114 1.139 15.125 0.515 c
+15.125 0.088 l
+12.906 0.088 l
+12.906 0 l
+12.906 -0.434 12.983 -0.746 13.141 -0.941 c
+13.306 -1.128 13.538 -1.22 13.832 -1.22 c
+14.026 -1.22 14.2 -1.187 14.346 -1.117 c
+14.493 -1.04 14.629 -0.922 14.757 -0.764 c
+15.096 -1.176 l
+14.809 -1.58 14.379 -1.779 13.803 -1.779 c
+13.729 1.793 m
+13.454 1.793 13.251 1.698 13.126 1.514 c
+12.997 1.326 12.924 1.037 12.906 0.647 c
+14.479 0.647 l
+14.479 0.735 l
+14.456 1.118 14.391 1.386 14.273 1.544 c
+14.155 1.709 13.972 1.793 13.729 1.793 c
+19.667 -1.705 m
+19.627 -1.617 19.601 -1.469 19.594 -1.264 c
+19.359 -1.61 19.065 -1.779 18.712 -1.779 c
+18.348 -1.779 18.065 -1.683 17.859 -1.484 c
+17.661 -1.278 17.565 -0.992 17.565 -0.617 c
+17.565 -0.216 17.701 0.103 17.977 0.338 c
+18.248 0.58 18.624 0.706 19.094 0.706 c
+19.579 0.706 l
+19.579 1.132 l
+19.579 1.367 19.524 1.532 19.418 1.631 c
+19.307 1.738 19.146 1.793 18.932 1.793 c
+18.734 1.793 18.572 1.735 18.447 1.617 c
+18.329 1.5 18.271 1.353 18.271 1.176 c
+17.624 1.176 l
+17.624 1.371 17.683 1.562 17.801 1.749 c
+17.926 1.933 18.088 2.08 18.285 2.19 c
+18.491 2.297 18.719 2.352 18.977 2.352 c
+19.377 2.352 19.682 2.249 19.888 2.043 c
+20.101 1.837 20.215 1.544 20.226 1.161 c
+20.226 -0.852 l
+20.226 -1.157 20.263 -1.422 20.344 -1.646 c
+20.344 -1.705 l
+h
+18.8 -1.191 m
+18.965 -1.191 19.116 -1.147 19.256 -1.058 c
+19.403 -0.97 19.509 -0.86 19.579 -0.721 c
+19.579 0.22 l
+19.212 0.22 l
+18.896 0.22 18.653 0.151 18.477 0.015 c
+18.3 -0.114 18.212 -0.301 18.212 -0.544 c
+18.212 -0.771 18.256 -0.937 18.344 -1.043 c
+18.433 -1.143 18.583 -1.191 18.8 -1.191 c
+23.563 2.278 m
+23.577 1.837 l
+23.831 2.18 24.154 2.352 24.548 2.352 c
+25.253 2.352 25.61 1.881 25.62 0.941 c
+25.62 -1.705 l
+24.974 -1.705 l
+24.974 0.912 l
+24.974 1.224 24.918 1.444 24.812 1.573 c
+24.702 1.698 24.548 1.764 24.342 1.764 c
+24.184 1.764 24.037 1.709 23.9 1.602 c
+23.772 1.492 23.669 1.357 23.592 1.191 c
+23.592 -1.705 l
+22.946 -1.705 l
+22.946 2.278 l
+h
+28.016 -1.779 m
+27.517 -1.779 27.135 -1.631 26.869 -1.338 c
+26.605 -1.043 26.473 -0.61 26.473 -0.029 c
+26.473 0.441 l
+26.473 1.037 26.598 1.503 26.855 1.837 c
+27.12 2.18 27.48 2.352 27.943 2.352 c
+28.402 2.352 28.744 2.198 28.972 1.897 c
+29.207 1.602 29.328 1.139 29.339 0.515 c
+29.339 0.088 l
+27.12 0.088 l
+27.12 0 l
+27.12 -0.434 27.197 -0.746 27.355 -0.941 c
+27.52 -1.128 27.752 -1.22 28.045 -1.22 c
+28.241 -1.22 28.413 -1.187 28.56 -1.117 c
+28.708 -1.04 28.843 -0.922 28.972 -0.764 c
+29.31 -1.176 l
+29.024 -1.58 28.593 -1.779 28.016 -1.779 c
+27.943 1.793 m
+27.667 1.793 27.465 1.698 27.34 1.514 c
+27.212 1.326 27.138 1.037 27.12 0.647 c
+28.693 0.647 l
+28.693 0.735 l
+28.671 1.118 28.604 1.386 28.487 1.544 c
+28.369 1.709 28.185 1.793 27.943 1.793 c
+33.088 -0.559 m
+33.69 2.278 l
+34.337 2.278 l
+33.352 -1.705 l
+32.837 -1.705 l
+32.059 1.147 l
+31.309 -1.705 l
+30.78 -1.705 l
+29.824 2.278 l
+30.456 2.278 l
+31.074 -0.485 l
+31.808 2.278 l
+32.323 2.278 l
+h
+38.115 -1.22 m
+38.328 -1.22 38.501 -1.157 38.629 -1.029 c
+38.765 -0.893 38.838 -0.702 38.85 -0.455 c
+39.467 -0.455 l
+39.445 -0.838 39.309 -1.157 39.056 -1.411 c
+38.798 -1.658 38.486 -1.779 38.115 -1.779 c
+37.622 -1.779 37.248 -1.627 36.982 -1.323 c
+36.726 -1.01 36.6 -0.544 36.6 0.073 c
+36.6 0.515 l
+36.6 1.11 36.726 1.565 36.982 1.881 c
+37.248 2.194 37.622 2.352 38.115 2.352 c
+38.515 2.352 38.835 2.219 39.07 1.955 c
+39.313 1.698 39.445 1.353 39.467 0.912 c
+38.85 0.912 l
+38.827 1.205 38.754 1.426 38.629 1.573 c
+38.511 1.72 38.339 1.793 38.115 1.793 c
+37.821 1.793 37.604 1.694 37.468 1.5 c
+37.329 1.312 37.255 1.004 37.248 0.573 c
+37.248 0.059 l
+37.248 -0.411 37.314 -0.746 37.453 -0.941 c
+37.6 -1.128 37.821 -1.22 38.115 -1.22 c
+40.084 0.47 m
+40.084 1.047 40.22 1.503 40.496 1.837 c
+40.779 2.18 41.15 2.352 41.613 2.352 c
+42.073 2.352 42.44 2.183 42.715 1.852 c
+42.998 1.529 43.145 1.081 43.156 0.515 c
+43.156 0.088 l
+43.156 -0.482 43.013 -0.937 42.73 -1.278 c
+42.455 -1.613 42.087 -1.779 41.628 -1.779 c
+41.165 -1.779 40.793 -1.617 40.51 -1.294 c
+40.235 -0.962 40.091 -0.522 40.084 0.029 c
+h
+40.731 0.088 m
+40.731 -0.316 40.808 -0.632 40.966 -0.867 c
+41.132 -1.103 41.352 -1.22 41.628 -1.22 c
+42.194 -1.22 42.488 -0.808 42.509 0.015 c
+42.509 0.47 l
+42.509 0.871 42.425 1.191 42.26 1.426 c
+42.102 1.669 41.885 1.793 41.613 1.793 c
+41.348 1.793 41.132 1.669 40.966 1.426 c
+40.808 1.191 40.731 0.871 40.731 0.47 c
+h
+44.612 2.278 m
+44.626 1.911 l
+44.869 2.205 45.188 2.352 45.582 2.352 c
+46.023 2.352 46.331 2.153 46.507 1.764 c
+46.761 2.153 47.11 2.352 47.552 2.352 c
+48.286 2.352 48.662 1.889 48.683 0.97 c
+48.683 -1.705 l
+48.036 -1.705 l
+48.036 0.912 l
+48.036 1.205 47.981 1.419 47.875 1.558 c
+47.775 1.694 47.603 1.764 47.361 1.764 c
+47.162 1.764 47 1.683 46.875 1.529 c
+46.758 1.382 46.688 1.191 46.669 0.956 c
+46.669 -1.705 l
+46.008 -1.705 l
+46.008 0.941 l
+46.008 1.488 45.788 1.764 45.346 1.764 c
+45.012 1.764 44.777 1.602 44.641 1.278 c
+44.641 -1.705 l
+43.995 -1.705 l
+43.995 2.278 l
+h
+50.27 2.278 m
+50.285 1.911 l
+50.528 2.205 50.848 2.352 51.241 2.352 c
+51.681 2.352 51.991 2.153 52.167 1.764 c
+52.421 2.153 52.77 2.352 53.21 2.352 c
+53.946 2.352 54.32 1.889 54.343 0.97 c
+54.343 -1.705 l
+53.696 -1.705 l
+53.696 0.912 l
+53.696 1.205 53.641 1.419 53.534 1.558 c
+53.435 1.694 53.262 1.764 53.019 1.764 c
+52.821 1.764 52.66 1.683 52.535 1.529 c
+52.417 1.382 52.347 1.191 52.329 0.956 c
+52.329 -1.705 l
+51.667 -1.705 l
+51.667 0.941 l
+51.667 1.488 51.446 1.764 51.006 1.764 c
+50.671 1.764 50.436 1.602 50.3 1.278 c
+50.3 -1.705 l
+49.653 -1.705 l
+49.653 2.278 l
+h
+56.018 -1.705 -0.647 3.983 re
+56.062 3.322 m
+56.062 3.212 56.032 3.12 55.974 3.042 c
+55.915 2.973 55.82 2.94 55.695 2.94 c
+55.577 2.94 55.482 2.973 55.415 3.042 c
+55.357 3.12 55.327 3.212 55.327 3.322 c
+55.327 3.439 55.357 3.532 55.415 3.601 c
+55.482 3.678 55.577 3.719 55.695 3.719 c
+55.82 3.719 55.915 3.678 55.974 3.601 c
+56.032 3.52 56.062 3.429 56.062 3.322 c
+57.885 3.234 m
+57.885 2.278 l
+58.488 2.278 l
+58.488 1.749 l
+57.885 1.749 l
+57.885 -0.721 l
+57.885 -0.878 57.907 -0.995 57.958 -1.073 c
+58.017 -1.153 58.106 -1.191 58.223 -1.191 c
+58.311 -1.191 58.399 -1.176 58.488 -1.147 c
+58.488 -1.705 l
+58.341 -1.753 58.187 -1.779 58.031 -1.779 c
+57.775 -1.779 57.58 -1.687 57.443 -1.5 c
+57.304 -1.315 57.238 -1.055 57.238 -0.721 c
+57.238 1.749 l
+56.635 1.749 l
+56.635 2.278 l
+57.238 2.278 l
+57.238 3.234 l
+h
+59.267 -2.778 m
+58.87 -2.514 l
+59.105 -2.19 59.226 -1.856 59.237 -1.514 c
+59.237 -0.897 l
+59.899 -0.897 l
+59.899 -1.426 l
+59.899 -1.683 59.833 -1.929 59.708 -2.175 c
+59.59 -2.418 59.443 -2.62 59.267 -2.778 c
+64.103 1.661 m
+64.014 1.679 63.915 1.691 63.808 1.691 c
+63.475 1.691 63.239 1.507 63.103 1.147 c
+63.103 -1.705 l
+62.457 -1.705 l
+62.457 2.278 l
+63.088 2.278 l
+63.103 1.866 l
+63.28 2.19 63.522 2.352 63.838 2.352 c
+63.945 2.352 64.032 2.33 64.103 2.294 c
+h
+66.102 -1.779 m
+65.602 -1.779 65.22 -1.631 64.955 -1.338 c
+64.691 -1.043 64.558 -0.61 64.558 -0.029 c
+64.558 0.441 l
+64.558 1.037 64.683 1.503 64.94 1.837 c
+65.205 2.18 65.565 2.352 66.028 2.352 c
+66.488 2.352 66.829 2.198 67.057 1.897 c
+67.292 1.602 67.413 1.139 67.425 0.515 c
+67.425 0.088 l
+65.205 0.088 l
+65.205 0 l
+65.205 -0.434 65.282 -0.746 65.44 -0.941 c
+65.605 -1.128 65.837 -1.22 66.131 -1.22 c
+66.326 -1.22 66.498 -1.187 66.646 -1.117 c
+66.793 -1.04 66.928 -0.922 67.057 -0.764 c
+67.396 -1.176 l
+67.109 -1.58 66.679 -1.779 66.102 -1.779 c
+66.028 1.793 m
+65.752 1.793 65.551 1.698 65.426 1.514 c
+65.297 1.326 65.223 1.037 65.205 0.647 c
+66.777 0.647 l
+66.777 0.735 l
+66.756 1.118 66.69 1.386 66.572 1.544 c
+66.455 1.709 66.27 1.793 66.028 1.793 c
+69.291 -0.721 m
+70.041 2.278 l
+70.702 2.278 l
+69.526 -1.705 l
+69.042 -1.705 l
+67.851 2.278 l
+68.512 2.278 l
+h
+72.761 -1.779 m
+72.26 -1.779 71.878 -1.631 71.614 -1.338 c
+71.349 -1.043 71.217 -0.61 71.217 -0.029 c
+71.217 0.441 l
+71.217 1.037 71.342 1.503 71.599 1.837 c
+71.864 2.18 72.223 2.352 72.687 2.352 c
+73.146 2.352 73.488 2.198 73.715 1.897 c
+73.951 1.602 74.072 1.139 74.083 0.515 c
+74.083 0.088 l
+71.864 0.088 l
+71.864 0 l
+71.864 -0.434 71.941 -0.746 72.099 -0.941 c
+72.264 -1.128 72.495 -1.22 72.79 -1.22 c
+72.984 -1.22 73.158 -1.187 73.304 -1.117 c
+73.451 -1.04 73.587 -0.922 73.715 -0.764 c
+74.054 -1.176 l
+73.767 -1.58 73.337 -1.779 72.761 -1.779 c
+72.687 1.793 m
+72.412 1.793 72.209 1.698 72.084 1.514 c
+71.955 1.326 71.882 1.037 71.864 0.647 c
+73.436 0.647 l
+73.436 0.735 l
+73.414 1.118 73.349 1.386 73.231 1.544 c
+73.113 1.709 72.929 1.793 72.687 1.793 c
+76.508 1.661 m
+76.42 1.679 76.321 1.691 76.215 1.691 c
+75.88 1.691 75.645 1.507 75.509 1.147 c
+75.509 -1.705 l
+74.862 -1.705 l
+74.862 2.278 l
+75.494 2.278 l
+75.509 1.866 l
+75.685 2.19 75.928 2.352 76.244 2.352 c
+76.35 2.352 76.439 2.33 76.508 2.294 c
+h
+78.199 3.234 m
+78.199 2.278 l
+78.802 2.278 l
+78.802 1.749 l
+78.199 1.749 l
+78.199 -0.721 l
+78.199 -0.878 78.221 -0.995 78.272 -1.073 c
+78.332 -1.153 78.419 -1.191 78.537 -1.191 c
+78.625 -1.191 78.714 -1.176 78.802 -1.147 c
+78.802 -1.705 l
+78.654 -1.753 78.5 -1.779 78.346 -1.779 c
+78.089 -1.779 77.894 -1.687 77.758 -1.5 c
+77.619 -1.315 77.552 -1.055 77.552 -0.721 c
+77.552 1.749 l
+76.95 1.749 l
+76.95 2.278 l
+77.552 2.278 l
+77.552 3.234 l
+h
+80.271 -1.705 -0.646 3.983 re
+80.316 3.322 m
+80.316 3.212 80.286 3.12 80.227 3.042 c
+80.169 2.973 80.073 2.94 79.948 2.94 c
+79.83 2.94 79.735 2.973 79.668 3.042 c
+79.61 3.12 79.581 3.212 79.581 3.322 c
+79.581 3.439 79.61 3.532 79.668 3.601 c
+79.735 3.678 79.83 3.719 79.948 3.719 c
+80.073 3.719 80.169 3.678 80.227 3.601 c
+80.286 3.52 80.316 3.429 80.316 3.322 c
+81.903 2.278 m
+81.918 1.837 l
+82.172 2.18 82.494 2.352 82.888 2.352 c
+83.593 2.352 83.95 1.881 83.961 0.941 c
+83.961 -1.705 l
+83.314 -1.705 l
+83.314 0.912 l
+83.314 1.224 83.259 1.444 83.153 1.573 c
+83.042 1.698 82.888 1.764 82.683 1.764 c
+82.524 1.764 82.377 1.709 82.241 1.602 c
+82.112 1.492 82.01 1.357 81.933 1.191 c
+81.933 -1.705 l
+81.286 -1.705 l
+81.286 2.278 l
+h
+84.799 0.47 m
+84.799 1.087 84.909 1.551 85.137 1.866 c
+85.361 2.19 85.696 2.352 86.136 2.352 c
+86.537 2.352 86.842 2.176 87.048 1.823 c
+87.092 2.278 l
+87.68 2.278 l
+87.68 -1.749 l
+87.68 -2.238 87.551 -2.616 87.298 -2.881 c
+87.04 -3.146 86.687 -3.278 86.239 -3.278 c
+86.041 -3.278 85.821 -3.227 85.578 -3.131 c
+85.332 -3.032 85.152 -2.911 85.034 -2.763 c
+85.299 -2.323 l
+85.563 -2.587 85.861 -2.72 86.195 -2.72 c
+86.731 -2.72 87.007 -2.425 87.018 -1.837 c
+87.018 -1.309 l
+86.812 -1.624 86.511 -1.779 86.122 -1.779 c
+85.71 -1.779 85.387 -1.627 85.152 -1.323 c
+84.923 -1.01 84.806 -0.559 84.799 0.029 c
+h
+85.461 0.088 m
+85.461 -0.353 85.523 -0.683 85.651 -0.897 c
+85.777 -1.103 85.993 -1.205 86.298 -1.205 c
+86.621 -1.205 86.86 -1.04 87.018 -0.706 c
+87.018 1.278 l
+86.849 1.602 86.61 1.764 86.298 1.764 c
+86.004 1.764 85.787 1.661 85.651 1.455 c
+85.523 1.249 85.461 0.926 85.461 0.485 c
+h
+91.737 -1.22 m
+91.95 -1.22 92.123 -1.157 92.251 -1.029 c
+92.387 -0.893 92.461 -0.702 92.472 -0.455 c
+93.089 -0.455 l
+93.067 -0.838 92.931 -1.157 92.678 -1.411 c
+92.42 -1.658 92.108 -1.779 91.737 -1.779 c
+91.244 -1.779 90.87 -1.627 90.605 -1.323 c
+90.348 -1.01 90.223 -0.544 90.223 0.073 c
+90.223 0.515 l
+90.223 1.11 90.348 1.565 90.605 1.881 c
+90.87 2.194 91.244 2.352 91.737 2.352 c
+92.137 2.352 92.457 2.219 92.692 1.955 c
+92.935 1.698 93.067 1.353 93.089 0.912 c
+92.472 0.912 l
+92.449 1.205 92.376 1.426 92.251 1.573 c
+92.133 1.72 91.961 1.793 91.737 1.793 c
+91.443 1.793 91.226 1.694 91.09 1.5 c
+90.951 1.312 90.877 1.004 90.87 0.573 c
+90.87 0.059 l
+90.87 -0.411 90.936 -0.746 91.075 -0.941 c
+91.223 -1.128 91.443 -1.22 91.737 -1.22 c
+94.485 1.866 m
+94.739 2.19 95.059 2.352 95.441 2.352 c
+96.146 2.352 96.503 1.881 96.514 0.941 c
+96.514 -1.705 l
+95.867 -1.705 l
+95.867 0.912 l
+95.867 1.224 95.812 1.444 95.705 1.573 c
+95.595 1.698 95.441 1.764 95.235 1.764 c
+95.077 1.764 94.93 1.709 94.795 1.602 c
+94.666 1.492 94.563 1.357 94.485 1.191 c
+94.485 -1.705 l
+93.839 -1.705 l
+93.839 3.94 l
+94.485 3.94 l
+h
+99.512 -1.705 m
+99.472 -1.617 99.447 -1.469 99.439 -1.264 c
+99.204 -1.61 98.91 -1.779 98.557 -1.779 c
+98.193 -1.779 97.91 -1.683 97.704 -1.484 c
+97.506 -1.278 97.411 -0.992 97.411 -0.617 c
+97.411 -0.216 97.546 0.103 97.822 0.338 c
+98.094 0.58 98.469 0.706 98.939 0.706 c
+99.424 0.706 l
+99.424 1.132 l
+99.424 1.367 99.369 1.532 99.263 1.631 c
+99.152 1.738 98.991 1.793 98.778 1.793 c
+98.579 1.793 98.417 1.735 98.292 1.617 c
+98.175 1.5 98.116 1.353 98.116 1.176 c
+97.469 1.176 l
+97.469 1.371 97.528 1.562 97.646 1.749 c
+97.771 1.933 97.933 2.08 98.13 2.19 c
+98.336 2.297 98.564 2.352 98.822 2.352 c
+99.223 2.352 99.527 2.249 99.733 2.043 c
+99.946 1.837 100.06 1.544 100.071 1.161 c
+100.071 -0.852 l
+100.071 -1.157 100.108 -1.422 100.189 -1.646 c
+100.189 -1.705 l
+h
+98.645 -1.191 m
+98.811 -1.191 98.961 -1.147 99.101 -1.058 c
+99.248 -0.97 99.354 -0.86 99.424 -0.721 c
+99.424 0.22 l
+99.057 0.22 l
+98.741 0.22 98.498 0.151 98.322 0.015 c
+98.145 -0.114 98.057 -0.301 98.057 -0.544 c
+98.057 -0.771 98.101 -0.937 98.19 -1.043 c
+98.278 -1.143 98.429 -1.191 98.645 -1.191 c
+101.688 2.278 m
+101.702 1.837 l
+101.956 2.18 102.28 2.352 102.673 2.352 c
+103.379 2.352 103.735 1.881 103.746 0.941 c
+103.746 -1.705 l
+103.099 -1.705 l
+103.099 0.912 l
+103.099 1.224 103.044 1.444 102.938 1.573 c
+102.828 1.698 102.673 1.764 102.467 1.764 c
+102.309 1.764 102.162 1.709 102.026 1.602 c
+101.897 1.492 101.795 1.357 101.717 1.191 c
+101.717 -1.705 l
+101.07 -1.705 l
+101.07 2.278 l
+h
+104.584 0.47 m
+104.584 1.087 104.694 1.551 104.922 1.866 c
+105.146 2.19 105.48 2.352 105.922 2.352 c
+106.322 2.352 106.627 2.176 106.832 1.823 c
+106.876 2.278 l
+107.464 2.278 l
+107.464 -1.749 l
+107.464 -2.238 107.336 -2.616 107.082 -2.881 c
+106.826 -3.146 106.473 -3.278 106.024 -3.278 c
+105.826 -3.278 105.606 -3.227 105.363 -3.131 c
+105.116 -3.032 104.937 -2.911 104.819 -2.763 c
+105.083 -2.323 l
+105.348 -2.587 105.646 -2.72 105.98 -2.72 c
+106.517 -2.72 106.793 -2.425 106.803 -1.837 c
+106.803 -1.309 l
+106.597 -1.624 106.296 -1.779 105.907 -1.779 c
+105.495 -1.779 105.172 -1.627 104.937 -1.323 c
+104.709 -1.01 104.591 -0.559 104.584 0.029 c
+h
+105.245 0.088 m
+105.245 -0.353 105.307 -0.683 105.436 -0.897 c
+105.561 -1.103 105.778 -1.205 106.083 -1.205 c
+106.406 -1.205 106.645 -1.04 106.803 -0.706 c
+106.803 1.278 l
+106.635 1.602 106.396 1.764 106.083 1.764 c
+105.789 1.764 105.573 1.661 105.436 1.455 c
+105.307 1.249 105.245 0.926 105.245 0.485 c
+h
+109.846 -1.779 m
+109.346 -1.779 108.964 -1.631 108.7 -1.338 c
+108.435 -1.043 108.303 -0.61 108.303 -0.029 c
+108.303 0.441 l
+108.303 1.037 108.428 1.503 108.685 1.837 c
+108.949 2.18 109.309 2.352 109.772 2.352 c
+110.232 2.352 110.574 2.198 110.801 1.897 c
+111.036 1.602 111.158 1.139 111.169 0.515 c
+111.169 0.088 l
+108.949 0.088 l
+108.949 0 l
+108.949 -0.434 109.026 -0.746 109.184 -0.941 c
+109.35 -1.128 109.581 -1.22 109.876 -1.22 c
+110.07 -1.22 110.242 -1.187 110.39 -1.117 c
+110.537 -1.04 110.672 -0.922 110.801 -0.764 c
+111.14 -1.176 l
+110.853 -1.58 110.423 -1.779 109.846 -1.779 c
+109.772 1.793 m
+109.496 1.793 109.295 1.698 109.17 1.514 c
+109.041 1.326 108.968 1.037 108.949 0.647 c
+110.522 0.647 l
+110.522 0.735 l
+110.5 1.118 110.434 1.386 110.317 1.544 c
+110.199 1.709 110.015 1.793 109.772 1.793 c
+113.888 -0.691 m
+113.888 -0.544 113.833 -0.422 113.727 -0.324 c
+113.616 -0.228 113.411 -0.11 113.109 0.029 c
+112.764 0.177 112.521 0.298 112.374 0.397 c
+112.227 0.503 112.117 0.621 112.051 0.75 c
+111.981 0.875 111.948 1.033 111.948 1.22 c
+111.948 1.544 112.066 1.812 112.301 2.028 c
+112.536 2.242 112.837 2.352 113.212 2.352 c
+113.594 2.352 113.903 2.238 114.138 2.014 c
+114.373 1.786 114.491 1.5 114.491 1.147 c
+113.845 1.147 l
+113.845 1.323 113.785 1.474 113.668 1.602 c
+113.55 1.727 113.396 1.793 113.212 1.793 c
+113.014 1.793 112.863 1.738 112.756 1.631 c
+112.646 1.532 112.594 1.4 112.594 1.235 c
+112.594 1.106 112.632 1 112.712 0.912 c
+112.789 0.831 112.981 0.728 113.286 0.603 c
+113.764 0.416 114.094 0.228 114.271 0.044 c
+114.447 -0.133 114.535 -0.36 114.535 -0.632 c
+114.535 -0.985 114.41 -1.264 114.167 -1.469 c
+113.932 -1.675 113.616 -1.779 113.226 -1.779 c
+112.804 -1.779 112.466 -1.661 112.212 -1.426 c
+111.956 -1.183 111.83 -0.878 111.83 -0.515 c
+112.477 -0.515 l
+112.484 -0.742 112.554 -0.918 112.683 -1.043 c
+112.808 -1.161 112.991 -1.22 113.226 -1.22 c
+113.44 -1.22 113.602 -1.172 113.712 -1.073 c
+113.829 -0.977 113.888 -0.849 113.888 -0.691 c
+117.386 -1.705 m
+117.386 1.749 l
+116.858 1.749 l
+116.858 2.278 l
+117.386 2.278 l
+117.386 2.734 l
+117.386 3.135 117.482 3.447 117.681 3.675 c
+117.886 3.899 118.165 4.013 118.518 4.013 c
+118.655 4.013 118.787 3.991 118.915 3.954 c
+118.886 3.41 l
+118.787 3.429 118.687 3.439 118.591 3.439 c
+118.217 3.439 118.034 3.175 118.034 2.646 c
+118.034 2.278 l
+118.709 2.278 l
+118.709 1.749 l
+118.034 1.749 l
+118.034 -1.705 l
+h
+121.12 1.661 m
+121.032 1.679 120.933 1.691 120.826 1.691 c
+120.492 1.691 120.257 1.507 120.12 1.147 c
+120.12 -1.705 l
+119.474 -1.705 l
+119.474 2.278 l
+120.106 2.278 l
+120.12 1.866 l
+120.297 2.19 120.54 2.352 120.856 2.352 c
+120.962 2.352 121.051 2.33 121.12 2.294 c
+h
+121.561 0.47 m
+121.561 1.047 121.697 1.503 121.973 1.837 c
+122.256 2.18 122.627 2.352 123.09 2.352 c
+123.549 2.352 123.917 2.183 124.193 1.852 c
+124.475 1.529 124.623 1.081 124.633 0.515 c
+124.633 0.088 l
+124.633 -0.482 124.49 -0.937 124.207 -1.278 c
+123.931 -1.613 123.564 -1.779 123.104 -1.779 c
+122.641 -1.779 122.271 -1.617 121.988 -1.294 c
+121.712 -0.962 121.569 -0.522 121.561 0.029 c
+h
+122.208 0.088 m
+122.208 -0.316 122.285 -0.632 122.443 -0.867 c
+122.608 -1.103 122.829 -1.22 123.104 -1.22 c
+123.671 -1.22 123.964 -0.808 123.987 0.015 c
+123.987 0.47 l
+123.987 0.871 123.902 1.191 123.736 1.426 c
+123.578 1.669 123.362 1.793 123.09 1.793 c
+122.825 1.793 122.608 1.669 122.443 1.426 c
+122.285 1.191 122.208 0.871 122.208 0.47 c
+h
+126.088 2.278 m
+126.103 1.911 l
+126.346 2.205 126.665 2.352 127.058 2.352 c
+127.499 2.352 127.808 2.153 127.985 1.764 c
+128.238 2.153 128.587 2.352 129.028 2.352 c
+129.763 2.352 130.138 1.889 130.16 0.97 c
+130.16 -1.705 l
+129.513 -1.705 l
+129.513 0.912 l
+129.513 1.205 129.458 1.419 129.352 1.558 c
+129.252 1.694 129.08 1.764 128.837 1.764 c
+128.639 1.764 128.477 1.683 128.352 1.529 c
+128.234 1.382 128.164 1.191 128.147 0.956 c
+128.147 -1.705 l
+127.485 -1.705 l
+127.485 0.941 l
+127.485 1.488 127.264 1.764 126.823 1.764 c
+126.489 1.764 126.254 1.602 126.117 1.278 c
+126.117 -1.705 l
+125.471 -1.705 l
+125.471 2.278 l
+h
+133.703 3.234 m
+133.703 2.278 l
+134.306 2.278 l
+134.306 1.749 l
+133.703 1.749 l
+133.703 -0.721 l
+133.703 -0.878 133.724 -0.995 133.776 -1.073 c
+133.835 -1.153 133.923 -1.191 134.04 -1.191 c
+134.129 -1.191 134.217 -1.176 134.306 -1.147 c
+134.306 -1.705 l
+134.158 -1.753 134.004 -1.779 133.849 -1.779 c
+133.593 -1.779 133.398 -1.687 133.261 -1.5 c
+133.122 -1.315 133.055 -1.055 133.055 -0.721 c
+133.055 1.749 l
+132.453 1.749 l
+132.453 2.278 l
+133.055 2.278 l
+133.055 3.234 l
+h
+135.717 1.866 m
+135.97 2.19 136.29 2.352 136.672 2.352 c
+137.377 2.352 137.734 1.881 137.745 0.941 c
+137.745 -1.705 l
+137.098 -1.705 l
+137.098 0.912 l
+137.098 1.224 137.043 1.444 136.936 1.573 c
+136.826 1.698 136.672 1.764 136.466 1.764 c
+136.307 1.764 136.161 1.709 136.025 1.602 c
+135.896 1.492 135.794 1.357 135.717 1.191 c
+135.717 -1.705 l
+135.069 -1.705 l
+135.069 3.94 l
+135.717 3.94 l
+h
+140.126 -1.779 m
+139.626 -1.779 139.244 -1.631 138.979 -1.338 c
+138.715 -1.043 138.582 -0.61 138.582 -0.029 c
+138.582 0.441 l
+138.582 1.037 138.707 1.503 138.965 1.837 c
+139.229 2.18 139.59 2.352 140.053 2.352 c
+140.512 2.352 140.854 2.198 141.082 1.897 c
+141.317 1.602 141.438 1.139 141.449 0.515 c
+141.449 0.088 l
+139.229 0.088 l
+139.229 0 l
+139.229 -0.434 139.307 -0.746 139.465 -0.941 c
+139.63 -1.128 139.862 -1.22 140.155 -1.22 c
+140.35 -1.22 140.523 -1.187 140.67 -1.117 c
+140.816 -1.04 140.953 -0.922 141.082 -0.764 c
+141.419 -1.176 l
+141.133 -1.58 140.703 -1.779 140.126 -1.779 c
+140.053 1.793 m
+139.777 1.793 139.575 1.698 139.45 1.514 c
+139.322 1.326 139.248 1.037 139.229 0.647 c
+140.802 0.647 l
+140.802 0.735 l
+140.78 1.118 140.714 1.386 140.596 1.544 c
+140.479 1.709 140.295 1.793 140.053 1.793 c
+145.888 -0.691 m
+145.888 -0.544 145.833 -0.422 145.726 -0.324 c
+145.616 -0.228 145.41 -0.11 145.109 0.029 c
+144.764 0.177 144.521 0.298 144.374 0.397 c
+144.227 0.503 144.117 0.621 144.051 0.75 c
+143.981 0.875 143.948 1.033 143.948 1.22 c
+143.948 1.544 144.066 1.812 144.301 2.028 c
+144.536 2.242 144.837 2.352 145.212 2.352 c
+145.594 2.352 145.903 2.238 146.138 2.014 c
+146.373 1.786 146.491 1.5 146.491 1.147 c
+145.844 1.147 l
+145.844 1.323 145.785 1.474 145.668 1.602 c
+145.55 1.727 145.396 1.793 145.212 1.793 c
+145.013 1.793 144.863 1.738 144.756 1.631 c
+144.646 1.532 144.594 1.4 144.594 1.235 c
+144.594 1.106 144.631 1 144.712 0.912 c
+144.789 0.831 144.98 0.728 145.285 0.603 c
+145.763 0.416 146.094 0.228 146.27 0.044 c
+146.447 -0.133 146.535 -0.36 146.535 -0.632 c
+146.535 -0.985 146.41 -1.264 146.167 -1.469 c
+145.932 -1.675 145.616 -1.779 145.227 -1.779 c
+144.804 -1.779 144.466 -1.661 144.213 -1.426 c
+143.955 -1.183 143.831 -0.878 143.831 -0.515 c
+144.477 -0.515 l
+144.484 -0.742 144.554 -0.918 144.683 -1.043 c
+144.808 -1.161 144.991 -1.22 145.227 -1.22 c
+145.44 -1.22 145.601 -1.172 145.712 -1.073 c
+145.83 -0.977 145.888 -0.849 145.888 -0.691 c
+150.254 0.088 m
+150.254 -0.54 150.136 -1.01 149.901 -1.323 c
+149.674 -1.627 149.357 -1.779 148.945 -1.779 c
+148.541 -1.779 148.232 -1.627 148.02 -1.323 c
+148.02 -3.233 l
+147.372 -3.233 l
+147.372 2.278 l
+147.96 2.278 l
+148.005 1.837 l
+148.218 2.18 148.527 2.352 148.93 2.352 c
+149.372 2.352 149.699 2.198 149.915 1.897 c
+150.129 1.592 150.243 1.135 150.254 0.53 c
+h
+149.607 0.47 m
+149.607 0.912 149.537 1.235 149.401 1.44 c
+149.261 1.654 149.041 1.764 148.739 1.764 c
+148.423 1.764 148.185 1.61 148.02 1.309 c
+148.02 -0.764 l
+148.185 -1.07 148.423 -1.22 148.739 -1.22 c
+149.034 -1.22 149.246 -1.117 149.387 -0.912 c
+149.522 -0.698 149.596 -0.368 149.607 0.073 c
+h
+152.517 -1.779 m
+152.018 -1.779 151.635 -1.631 151.371 -1.338 c
+151.106 -1.043 150.974 -0.61 150.974 -0.029 c
+150.974 0.441 l
+150.974 1.037 151.099 1.503 151.357 1.837 c
+151.621 2.18 151.981 2.352 152.444 2.352 c
+152.903 2.352 153.245 2.198 153.473 1.897 c
+153.708 1.602 153.83 1.139 153.84 0.515 c
+153.84 0.088 l
+151.621 0.088 l
+151.621 0 l
+151.621 -0.434 151.698 -0.746 151.856 -0.941 c
+152.022 -1.128 152.253 -1.22 152.547 -1.22 c
+152.741 -1.22 152.914 -1.187 153.061 -1.117 c
+153.208 -1.04 153.344 -0.922 153.473 -0.764 c
+153.811 -1.176 l
+153.524 -1.58 153.094 -1.779 152.517 -1.779 c
+152.444 1.793 m
+152.168 1.793 151.966 1.698 151.841 1.514 c
+151.713 1.326 151.639 1.037 151.621 0.647 c
+153.194 0.647 l
+153.194 0.735 l
+153.171 1.118 153.105 1.386 152.988 1.544 c
+152.87 1.709 152.687 1.793 152.444 1.793 c
+156.001 -1.22 m
+156.214 -1.22 156.387 -1.157 156.516 -1.029 c
+156.652 -0.893 156.725 -0.702 156.736 -0.455 c
+157.354 -0.455 l
+157.331 -0.838 157.196 -1.157 156.942 -1.411 c
+156.685 -1.658 156.373 -1.779 156.001 -1.779 c
+155.509 -1.779 155.134 -1.627 154.869 -1.323 c
+154.612 -1.01 154.487 -0.544 154.487 0.073 c
+154.487 0.515 l
+154.487 1.11 154.612 1.565 154.869 1.881 c
+155.134 2.194 155.509 2.352 156.001 2.352 c
+156.402 2.352 156.722 2.219 156.957 1.955 c
+157.199 1.698 157.331 1.353 157.354 0.912 c
+156.736 0.912 l
+156.714 1.205 156.641 1.426 156.516 1.573 c
+156.398 1.72 156.225 1.793 156.001 1.793 c
+155.707 1.793 155.49 1.694 155.355 1.5 c
+155.214 1.312 155.141 1.004 155.134 0.573 c
+155.134 0.059 l
+155.134 -0.411 155.2 -0.746 155.34 -0.941 c
+155.486 -1.128 155.707 -1.22 156.001 -1.22 c
+158.808 -1.705 -0.646 3.983 re
+158.852 3.322 m
+158.852 3.212 158.823 3.12 158.765 3.042 c
+158.706 2.973 158.611 2.94 158.485 2.94 c
+158.368 2.94 158.272 2.973 158.206 3.042 c
+158.147 3.12 158.118 3.212 158.118 3.322 c
+158.118 3.439 158.147 3.532 158.206 3.601 c
+158.272 3.678 158.368 3.719 158.485 3.719 c
+158.611 3.719 158.706 3.678 158.765 3.601 c
+158.823 3.52 158.852 3.429 158.852 3.322 c
+159.999 -1.705 m
+159.999 1.749 l
+159.484 1.749 l
+159.484 2.278 l
+159.999 2.278 l
+159.999 2.646 l
+160.007 3.076 160.12 3.41 160.337 3.645 c
+160.562 3.888 160.874 4.013 161.278 4.013 c
+161.425 4.013 161.564 3.991 161.705 3.954 c
+161.851 3.913 162.002 3.859 162.16 3.792 c
+162.042 3.219 l
+161.807 3.344 161.564 3.41 161.322 3.41 c
+161.076 3.41 160.903 3.341 160.807 3.204 c
+160.708 3.076 160.66 2.881 160.66 2.616 c
+160.66 2.278 l
+161.308 2.278 l
+161.308 1.749 l
+160.66 1.749 l
+160.66 -1.705 l
+h
+162.469 -1.705 -0.646 3.983 re
+164.938 -1.779 m
+164.438 -1.779 164.056 -1.631 163.791 -1.338 c
+163.527 -1.043 163.394 -0.61 163.394 -0.029 c
+163.394 0.441 l
+163.394 1.037 163.519 1.503 163.777 1.837 c
+164.041 2.18 164.402 2.352 164.865 2.352 c
+165.324 2.352 165.666 2.198 165.894 1.897 c
+166.129 1.602 166.25 1.139 166.261 0.515 c
+166.261 0.088 l
+164.041 0.088 l
+164.041 0 l
+164.041 -0.434 164.119 -0.746 164.277 -0.941 c
+164.442 -1.128 164.674 -1.22 164.967 -1.22 c
+165.162 -1.22 165.335 -1.187 165.482 -1.117 c
+165.628 -1.04 165.765 -0.922 165.894 -0.764 c
+166.231 -1.176 l
+165.945 -1.58 165.515 -1.779 164.938 -1.779 c
+164.865 1.793 m
+164.589 1.793 164.387 1.698 164.262 1.514 c
+164.134 1.326 164.059 1.037 164.041 0.647 c
+165.614 0.647 l
+165.614 0.735 l
+165.592 1.118 165.526 1.386 165.408 1.544 c
+165.291 1.709 165.107 1.793 164.865 1.793 c
+166.908 0.47 m
+166.908 1.077 167.018 1.544 167.246 1.866 c
+167.481 2.19 167.808 2.352 168.231 2.352 c
+168.612 2.352 168.911 2.194 169.127 1.881 c
+169.127 3.94 l
+169.774 3.94 l
+169.774 -1.705 l
+169.186 -1.705 l
+169.142 -1.278 l
+168.936 -1.613 168.631 -1.779 168.231 -1.779 c
+167.819 -1.779 167.496 -1.624 167.261 -1.309 c
+167.025 -0.985 166.908 -0.529 166.908 0.059 c
+h
+167.554 0.088 m
+167.554 -0.353 167.617 -0.683 167.745 -0.897 c
+167.882 -1.103 168.102 -1.205 168.407 -1.205 c
+168.73 -1.205 168.969 -1.043 169.127 -0.721 c
+169.127 1.294 l
+168.959 1.606 168.72 1.764 168.407 1.764 c
+168.102 1.764 167.882 1.661 167.745 1.455 c
+167.617 1.249 167.554 0.926 167.554 0.485 c
+h
+173.875 -1.22 m
+174.089 -1.22 174.261 -1.157 174.39 -1.029 c
+174.526 -0.893 174.599 -0.702 174.61 -0.455 c
+175.228 -0.455 l
+175.205 -0.838 175.07 -1.157 174.816 -1.411 c
+174.559 -1.658 174.247 -1.779 173.875 -1.779 c
+173.383 -1.779 173.008 -1.627 172.743 -1.323 c
+172.486 -1.01 172.361 -0.544 172.361 0.073 c
+172.361 0.515 l
+172.361 1.11 172.486 1.565 172.743 1.881 c
+173.008 2.194 173.383 2.352 173.875 2.352 c
+174.276 2.352 174.596 2.219 174.831 1.955 c
+175.073 1.698 175.205 1.353 175.228 0.912 c
+174.61 0.912 l
+174.588 1.205 174.515 1.426 174.39 1.573 c
+174.272 1.72 174.099 1.793 173.875 1.793 c
+173.581 1.793 173.364 1.694 173.229 1.5 c
+173.089 1.312 173.015 1.004 173.008 0.573 c
+173.008 0.059 l
+173.008 -0.411 173.074 -0.746 173.214 -0.941 c
+173.36 -1.128 173.581 -1.22 173.875 -1.22 c
+175.845 0.47 m
+175.845 1.047 175.98 1.503 176.256 1.837 c
+176.539 2.18 176.911 2.352 177.374 2.352 c
+177.833 2.352 178.201 2.183 178.476 1.852 c
+178.759 1.529 178.906 1.081 178.916 0.515 c
+178.916 0.088 l
+178.916 -0.482 178.773 -0.937 178.49 -1.278 c
+178.215 -1.613 177.848 -1.779 177.388 -1.779 c
+176.925 -1.779 176.554 -1.617 176.271 -1.294 c
+175.995 -0.962 175.852 -0.522 175.845 0.029 c
+h
+176.491 0.088 m
+176.491 -0.316 176.568 -0.632 176.726 -0.867 c
+176.892 -1.103 177.113 -1.22 177.388 -1.22 c
+177.954 -1.22 178.249 -0.808 178.27 0.015 c
+178.27 0.47 l
+178.27 0.871 178.185 1.191 178.02 1.426 c
+177.862 1.669 177.646 1.793 177.374 1.793 c
+177.109 1.793 176.892 1.669 176.726 1.426 c
+176.568 1.191 176.491 0.871 176.491 0.47 c
+h
+180.372 2.278 m
+180.387 1.911 l
+180.63 2.205 180.949 2.352 181.343 2.352 c
+181.783 2.352 182.091 2.153 182.268 1.764 c
+182.522 2.153 182.871 2.352 183.312 2.352 c
+184.047 2.352 184.422 1.889 184.443 0.97 c
+184.443 -1.705 l
+183.797 -1.705 l
+183.797 0.912 l
+183.797 1.205 183.741 1.419 183.635 1.558 c
+183.536 1.694 183.363 1.764 183.121 1.764 c
+182.922 1.764 182.76 1.683 182.636 1.529 c
+182.519 1.382 182.448 1.191 182.43 0.956 c
+182.43 -1.705 l
+181.769 -1.705 l
+181.769 0.941 l
+181.769 1.488 181.548 1.764 181.107 1.764 c
+180.772 1.764 180.537 1.602 180.402 1.278 c
+180.402 -1.705 l
+179.755 -1.705 l
+179.755 2.278 l
+h
+186.031 2.278 m
+186.046 1.911 l
+186.288 2.205 186.608 2.352 187.001 2.352 c
+187.442 2.352 187.751 2.153 187.928 1.764 c
+188.181 2.153 188.53 2.352 188.971 2.352 c
+189.706 2.352 190.081 1.889 190.103 0.97 c
+190.103 -1.705 l
+189.456 -1.705 l
+189.456 0.912 l
+189.456 1.205 189.401 1.419 189.295 1.558 c
+189.195 1.694 189.023 1.764 188.78 1.764 c
+188.582 1.764 188.42 1.683 188.295 1.529 c
+188.177 1.382 188.107 1.191 188.089 0.956 c
+188.089 -1.705 l
+187.427 -1.705 l
+187.427 0.941 l
+187.427 1.488 187.207 1.764 186.766 1.764 c
+186.432 1.764 186.197 1.602 186.06 1.278 c
+186.06 -1.705 l
+185.414 -1.705 l
+185.414 2.278 l
+h
+191.778 -1.705 -0.646 3.983 re
+191.822 3.322 m
+191.822 3.212 191.793 3.12 191.734 3.042 c
+191.676 2.973 191.58 2.94 191.456 2.94 c
+191.338 2.94 191.242 2.973 191.176 3.042 c
+191.117 3.12 191.088 3.212 191.088 3.322 c
+191.088 3.439 191.117 3.532 191.176 3.601 c
+191.242 3.678 191.338 3.719 191.456 3.719 c
+191.58 3.719 191.676 3.678 191.734 3.601 c
+191.793 3.52 191.822 3.429 191.822 3.322 c
+193.646 3.234 m
+193.646 2.278 l
+194.248 2.278 l
+194.248 1.749 l
+193.646 1.749 l
+193.646 -0.721 l
+193.646 -0.878 193.667 -0.995 193.719 -1.073 c
+193.777 -1.153 193.866 -1.191 193.983 -1.191 c
+194.072 -1.191 194.16 -1.176 194.248 -1.147 c
+194.248 -1.705 l
+194.101 -1.753 193.947 -1.779 193.792 -1.779 c
+193.536 -1.779 193.341 -1.687 193.204 -1.5 c
+193.065 -1.315 192.998 -1.055 192.998 -0.721 c
+192.998 1.749 l
+192.396 1.749 l
+192.396 2.278 l
+192.998 2.278 l
+192.998 3.234 l
+h
+195.057 -1.352 m
+195.057 -1.234 195.09 -1.139 195.159 -1.058 c
+195.225 -0.981 195.329 -0.941 195.468 -0.941 c
+195.615 -0.941 195.722 -0.981 195.791 -1.058 c
+195.869 -1.139 195.909 -1.234 195.909 -1.352 c
+195.909 -1.463 195.869 -1.554 195.791 -1.631 c
+195.722 -1.708 195.615 -1.749 195.468 -1.749 c
+195.329 -1.749 195.225 -1.708 195.159 -1.631 c
+195.09 -1.554 195.057 -1.463 195.057 -1.352 c
+199.51 -1.705 -0.676 5.35 re
+201.451 3.234 m
+201.451 2.278 l
+202.053 2.278 l
+202.053 1.749 l
+201.451 1.749 l
+201.451 -0.721 l
+201.451 -0.878 201.472 -0.995 201.524 -1.073 c
+201.583 -1.153 201.671 -1.191 201.789 -1.191 c
+201.877 -1.191 201.964 -1.176 202.053 -1.147 c
+202.053 -1.705 l
+201.906 -1.753 201.752 -1.779 201.598 -1.779 c
+201.34 -1.779 201.145 -1.687 201.01 -1.5 c
+200.87 -1.315 200.804 -1.055 200.804 -0.721 c
+200.804 1.749 l
+200.201 1.749 l
+200.201 2.278 l
+200.804 2.278 l
+200.804 3.234 l
+h
+f
+Q
+q 1 0 0 1 412.9262 238.2432 cm
+0 0 m
+0 0.617 0.11 1.081 0.338 1.396 c
+0.563 1.72 0.897 1.881 1.338 1.881 c
+1.738 1.881 2.043 1.706 2.249 1.353 c
+2.294 1.808 l
+2.881 1.808 l
+2.881 -2.219 l
+2.881 -2.708 2.753 -3.087 2.499 -3.351 c
+2.242 -3.616 1.889 -3.748 1.44 -3.748 c
+1.242 -3.748 1.022 -3.697 0.779 -3.601 c
+0.533 -3.502 0.353 -3.381 0.235 -3.233 c
+0.5 -2.793 l
+0.765 -3.057 1.062 -3.19 1.396 -3.19 c
+1.933 -3.19 2.209 -2.896 2.219 -2.308 c
+2.219 -1.779 l
+2.014 -2.094 1.712 -2.249 1.323 -2.249 c
+0.912 -2.249 0.588 -2.098 0.353 -1.793 c
+0.125 -1.481 0.008 -1.029 0 -0.441 c
+h
+0.661 -0.382 m
+0.661 -0.823 0.724 -1.153 0.852 -1.367 c
+0.977 -1.573 1.195 -1.675 1.5 -1.675 c
+1.823 -1.675 2.061 -1.51 2.219 -1.176 c
+2.219 0.808 l
+2.051 1.132 1.812 1.294 1.5 1.294 c
+1.205 1.294 0.989 1.191 0.852 0.985 c
+0.724 0.779 0.661 0.455 0.661 0.015 c
+h
+5.262 -2.249 m
+4.763 -2.249 4.38 -2.102 4.116 -1.808 c
+3.851 -1.514 3.719 -1.08 3.719 -0.5 c
+3.719 -0.029 l
+3.719 0.566 3.844 1.033 4.101 1.367 c
+4.366 1.709 4.726 1.881 5.189 1.881 c
+5.648 1.881 5.99 1.727 6.217 1.426 c
+6.453 1.132 6.574 0.669 6.585 0.044 c
+6.585 -0.382 l
+4.366 -0.382 l
+4.366 -0.47 l
+4.366 -0.904 4.443 -1.216 4.601 -1.411 c
+4.767 -1.598 4.998 -1.691 5.292 -1.691 c
+5.486 -1.691 5.659 -1.658 5.806 -1.587 c
+5.953 -1.51 6.089 -1.392 6.217 -1.234 c
+6.556 -1.646 l
+6.269 -2.051 5.839 -2.249 5.262 -2.249 c
+5.189 1.323 m
+4.913 1.323 4.711 1.228 4.586 1.043 c
+4.457 0.856 4.384 0.566 4.366 0.177 c
+5.939 0.177 l
+5.939 0.264 l
+5.916 0.647 5.85 0.915 5.733 1.073 c
+5.615 1.239 5.432 1.323 5.189 1.323 c
+7.981 1.808 m
+7.996 1.367 l
+8.25 1.709 8.573 1.881 8.966 1.881 c
+9.672 1.881 10.028 1.411 10.04 0.47 c
+10.04 -2.175 l
+9.392 -2.175 l
+9.392 0.441 l
+9.392 0.754 9.338 0.974 9.231 1.103 c
+9.12 1.228 8.966 1.294 8.761 1.294 c
+8.603 1.294 8.455 1.239 8.32 1.132 c
+8.191 1.022 8.088 0.886 8.011 0.721 c
+8.011 -2.175 l
+7.364 -2.175 l
+7.364 1.808 l
+h
+12.436 -2.249 m
+11.936 -2.249 11.553 -2.102 11.289 -1.808 c
+11.025 -1.514 10.892 -1.08 10.892 -0.5 c
+10.892 -0.029 l
+10.892 0.566 11.017 1.033 11.274 1.367 c
+11.539 1.709 11.899 1.881 12.362 1.881 c
+12.821 1.881 13.163 1.727 13.391 1.426 c
+13.626 1.132 13.747 0.669 13.758 0.044 c
+13.758 -0.382 l
+11.539 -0.382 l
+11.539 -0.47 l
+11.539 -0.904 11.616 -1.216 11.774 -1.411 c
+11.939 -1.598 12.171 -1.691 12.465 -1.691 c
+12.66 -1.691 12.833 -1.658 12.979 -1.587 c
+13.126 -1.51 13.263 -1.392 13.391 -1.234 c
+13.729 -1.646 l
+13.442 -2.051 13.012 -2.249 12.436 -2.249 c
+12.362 1.323 m
+12.087 1.323 11.884 1.228 11.759 1.043 c
+11.63 0.856 11.557 0.566 11.539 0.177 c
+13.112 0.177 l
+13.112 0.264 l
+13.089 0.647 13.024 0.915 12.906 1.073 c
+12.788 1.239 12.605 1.323 12.362 1.323 c
+16.184 1.191 m
+16.095 1.209 15.996 1.22 15.89 1.22 c
+15.555 1.22 15.32 1.037 15.184 0.676 c
+15.184 -2.175 l
+14.537 -2.175 l
+14.537 1.808 l
+15.169 1.808 l
+15.184 1.396 l
+15.36 1.72 15.603 1.881 15.919 1.881 c
+16.025 1.881 16.114 1.86 16.184 1.823 c
+h
+18.726 -2.175 m
+18.686 -2.087 18.66 -1.94 18.653 -1.735 c
+18.418 -2.08 18.124 -2.249 17.771 -2.249 c
+17.407 -2.249 17.124 -2.153 16.918 -1.955 c
+16.721 -1.749 16.625 -1.463 16.625 -1.087 c
+16.625 -0.687 16.761 -0.368 17.036 -0.133 c
+17.309 0.11 17.683 0.235 18.153 0.235 c
+18.639 0.235 l
+18.639 0.661 l
+18.639 0.897 18.583 1.062 18.477 1.161 c
+18.367 1.268 18.205 1.323 17.992 1.323 c
+17.793 1.323 17.631 1.264 17.506 1.147 c
+17.389 1.029 17.33 0.882 17.33 0.706 c
+16.683 0.706 l
+16.683 0.9 16.742 1.091 16.86 1.278 c
+16.985 1.463 17.147 1.61 17.345 1.72 c
+17.55 1.827 17.779 1.881 18.036 1.881 c
+18.437 1.881 18.741 1.779 18.947 1.573 c
+19.16 1.367 19.274 1.073 19.285 0.691 c
+19.285 -1.323 l
+19.285 -1.627 19.322 -1.893 19.403 -2.117 c
+19.403 -2.175 l
+h
+17.859 -1.661 m
+18.025 -1.661 18.175 -1.617 18.315 -1.529 c
+18.462 -1.44 18.568 -1.33 18.639 -1.191 c
+18.639 -0.25 l
+18.271 -0.25 l
+17.955 -0.25 17.712 -0.32 17.536 -0.455 c
+17.359 -0.584 17.271 -0.771 17.271 -1.014 c
+17.271 -1.242 17.315 -1.407 17.404 -1.514 c
+17.492 -1.613 17.643 -1.661 17.859 -1.661 c
+21.137 2.764 m
+21.137 1.808 l
+21.74 1.808 l
+21.74 1.278 l
+21.137 1.278 l
+21.137 -1.191 l
+21.137 -1.348 21.159 -1.466 21.211 -1.544 c
+21.269 -1.624 21.358 -1.661 21.475 -1.661 c
+21.564 -1.661 21.652 -1.646 21.74 -1.617 c
+21.74 -2.175 l
+21.593 -2.223 21.439 -2.249 21.284 -2.249 c
+21.027 -2.249 20.833 -2.157 20.696 -1.97 c
+20.557 -1.786 20.49 -1.525 20.49 -1.191 c
+20.49 1.278 l
+19.888 1.278 l
+19.888 1.808 l
+20.49 1.808 l
+20.49 2.764 l
+h
+23.931 -2.249 m
+23.43 -2.249 23.048 -2.102 22.784 -1.808 c
+22.519 -1.514 22.387 -1.08 22.387 -0.5 c
+22.387 -0.029 l
+22.387 0.566 22.512 1.033 22.769 1.367 c
+23.033 1.709 23.393 1.881 23.856 1.881 c
+24.316 1.881 24.658 1.727 24.885 1.426 c
+25.121 1.132 25.242 0.669 25.253 0.044 c
+25.253 -0.382 l
+23.033 -0.382 l
+23.033 -0.47 l
+23.033 -0.904 23.111 -1.216 23.268 -1.411 c
+23.434 -1.598 23.665 -1.691 23.96 -1.691 c
+24.154 -1.691 24.327 -1.658 24.474 -1.587 c
+24.621 -1.51 24.757 -1.392 24.885 -1.234 c
+25.224 -1.646 l
+24.937 -2.051 24.507 -2.249 23.931 -2.249 c
+23.856 1.323 m
+23.582 1.323 23.379 1.228 23.254 1.043 c
+23.125 0.856 23.052 0.566 23.033 0.177 c
+24.606 0.177 l
+24.606 0.264 l
+24.584 0.647 24.518 0.915 24.401 1.073 c
+24.283 1.239 24.099 1.323 23.856 1.323 c
+27.972 -1.161 m
+27.972 -1.014 27.917 -0.893 27.811 -0.794 c
+27.7 -0.698 27.495 -0.58 27.193 -0.441 c
+26.848 -0.294 26.605 -0.172 26.458 -0.073 c
+26.312 0.033 26.201 0.151 26.135 0.279 c
+26.065 0.405 26.032 0.563 26.032 0.75 c
+26.032 1.073 26.15 1.341 26.385 1.558 c
+26.62 1.771 26.921 1.881 27.296 1.881 c
+27.678 1.881 27.987 1.768 28.222 1.544 c
+28.457 1.316 28.575 1.029 28.575 0.676 c
+27.929 0.676 l
+27.929 0.852 27.869 1.004 27.752 1.132 c
+27.634 1.257 27.48 1.323 27.296 1.323 c
+27.098 1.323 26.947 1.268 26.84 1.161 c
+26.73 1.062 26.678 0.929 26.678 0.765 c
+26.678 0.636 26.716 0.53 26.796 0.441 c
+26.874 0.36 27.065 0.258 27.37 0.133 c
+27.848 -0.055 28.178 -0.243 28.355 -0.426 c
+28.531 -0.603 28.619 -0.831 28.619 -1.103 c
+28.619 -1.455 28.494 -1.735 28.251 -1.94 c
+28.016 -2.146 27.7 -2.249 27.311 -2.249 c
+26.888 -2.249 26.55 -2.132 26.296 -1.897 c
+26.04 -1.654 25.915 -1.348 25.915 -0.985 c
+26.561 -0.985 l
+26.568 -1.213 26.639 -1.389 26.767 -1.514 c
+26.892 -1.631 27.075 -1.691 27.311 -1.691 c
+27.524 -1.691 27.686 -1.643 27.796 -1.544 c
+27.914 -1.448 27.972 -1.319 27.972 -1.161 c
+33.22 -2.175 m
+33.18 -2.087 33.154 -1.94 33.146 -1.735 c
+32.911 -2.08 32.617 -2.249 32.265 -2.249 c
+31.9 -2.249 31.617 -2.153 31.412 -1.955 c
+31.214 -1.749 31.118 -1.463 31.118 -1.087 c
+31.118 -0.687 31.254 -0.368 31.53 -0.133 c
+31.802 0.11 32.176 0.235 32.646 0.235 c
+33.132 0.235 l
+33.132 0.661 l
+33.132 0.897 33.076 1.062 32.97 1.161 c
+32.86 1.268 32.698 1.323 32.485 1.323 c
+32.286 1.323 32.125 1.264 32 1.147 c
+31.883 1.029 31.823 0.882 31.823 0.706 c
+31.177 0.706 l
+31.177 0.9 31.235 1.091 31.353 1.278 c
+31.478 1.463 31.64 1.61 31.838 1.72 c
+32.044 1.827 32.272 1.881 32.529 1.881 c
+32.93 1.881 33.234 1.779 33.44 1.573 c
+33.654 1.367 33.768 1.073 33.778 0.691 c
+33.778 -1.323 l
+33.778 -1.627 33.815 -1.893 33.896 -2.117 c
+33.896 -2.175 l
+h
+32.353 -1.661 m
+32.519 -1.661 32.669 -1.617 32.808 -1.529 c
+32.955 -1.44 33.062 -1.33 33.132 -1.191 c
+33.132 -0.25 l
+32.764 -0.25 l
+32.448 -0.25 32.205 -0.32 32.029 -0.455 c
+31.853 -0.584 31.765 -0.771 31.765 -1.014 c
+31.765 -1.242 31.809 -1.407 31.897 -1.514 c
+31.985 -1.613 32.136 -1.661 32.353 -1.661 c
+35.395 1.808 m
+35.41 1.367 l
+35.663 1.709 35.987 1.881 36.38 1.881 c
+37.086 1.881 37.442 1.411 37.454 0.47 c
+37.454 -2.175 l
+36.806 -2.175 l
+36.806 0.441 l
+36.806 0.754 36.752 0.974 36.645 1.103 c
+36.535 1.228 36.38 1.294 36.174 1.294 c
+36.016 1.294 35.869 1.239 35.733 1.132 c
+35.605 1.022 35.502 0.886 35.424 0.721 c
+35.424 -2.175 l
+34.778 -2.175 l
+34.778 1.808 l
+h
+f
+Q
+454.054 236.068 -1.044 3.983 re
+452.967 241.08 m
+452.967 241.235 453.014 241.364 453.114 241.462 c
+453.22 241.569 453.356 241.624 453.525 241.624 c
+453.702 241.624 453.837 241.569 453.937 241.462 c
+454.043 241.364 454.099 241.235 454.099 241.08 c
+454.099 240.911 454.043 240.776 453.937 240.668 c
+453.837 240.57 453.702 240.522 453.525 240.522 c
+453.356 240.522 453.22 240.57 453.114 240.668 c
+453.014 240.776 452.967 240.911 452.967 241.08 c
+455.818 240.051 m
+455.847 239.654 l
+456.083 239.967 456.384 240.125 456.759 240.125 c
+457.442 240.125 457.795 239.643 457.817 238.684 c
+457.817 236.068 l
+456.773 236.068 l
+456.773 238.611 l
+456.773 238.835 456.736 238.997 456.671 239.096 c
+456.601 239.191 456.483 239.243 456.318 239.243 c
+456.131 239.243 455.984 239.147 455.877 238.964 c
+455.877 236.068 l
+454.833 236.068 l
+454.833 240.051 l
+h
+459.875 237.538 m
+460.375 240.051 l
+461.463 240.051 l
+460.36 236.068 l
+459.375 236.068 l
+458.273 240.051 l
+459.36 240.051 l
+h
+463.506 235.994 m
+462.976 235.994 462.558 236.149 462.257 236.464 c
+461.962 236.788 461.815 237.248 461.815 237.846 c
+461.815 238.155 l
+461.815 238.779 461.951 239.265 462.227 239.61 c
+462.498 239.953 462.892 240.125 463.403 240.125 c
+463.903 240.125 464.274 239.963 464.52 239.639 c
+464.773 239.317 464.906 238.839 464.917 238.214 c
+464.917 237.714 l
+462.845 237.714 l
+462.863 237.42 462.926 237.204 463.036 237.067 c
+463.153 236.928 463.333 236.861 463.579 236.861 c
+463.921 236.861 464.212 236.979 464.447 237.214 c
+464.858 236.582 l
+464.73 236.406 464.542 236.263 464.299 236.156 c
+464.053 236.049 463.789 235.994 463.506 235.994 c
+462.859 238.434 m
+463.888 238.434 l
+463.888 238.538 l
+463.888 238.773 463.847 238.949 463.77 239.066 c
+463.701 239.191 463.572 239.257 463.388 239.257 c
+463.212 239.257 463.08 239.188 462.991 239.051 c
+462.91 238.923 462.866 238.717 462.859 238.434 c
+467.46 239.037 m
+467.121 239.066 l
+466.835 239.066 466.644 238.941 466.548 238.699 c
+466.548 236.068 l
+465.505 236.068 l
+465.505 240.051 l
+466.475 240.051 l
+466.504 239.61 l
+466.67 239.953 466.901 240.125 467.196 240.125 c
+467.313 240.125 467.404 240.103 467.474 240.066 c
+h
+469.768 237.141 m
+469.768 237.229 469.723 237.306 469.635 237.376 c
+469.547 237.453 469.359 237.556 469.077 237.684 c
+468.643 237.861 468.345 238.041 468.18 238.228 c
+468.022 238.413 467.944 238.644 467.944 238.92 c
+467.944 239.261 468.066 239.544 468.312 239.772 c
+468.566 240.007 468.904 240.125 469.326 240.125 c
+469.756 240.125 470.105 240.011 470.371 239.787 c
+470.635 239.559 470.767 239.257 470.767 238.875 c
+469.723 238.875 l
+469.723 239.199 469.584 239.361 469.312 239.361 c
+469.201 239.361 469.114 239.324 469.047 239.257 c
+468.977 239.188 468.944 239.089 468.944 238.964 c
+468.944 238.875 468.981 238.794 469.062 238.729 c
+469.139 238.669 469.319 238.574 469.606 238.449 c
+470.036 238.291 470.334 238.114 470.502 237.92 c
+470.679 237.732 470.767 237.483 470.767 237.17 c
+470.767 236.817 470.635 236.531 470.371 236.318 c
+470.105 236.101 469.756 235.994 469.326 235.994 c
+469.033 235.994 468.771 236.049 468.547 236.156 c
+468.32 236.273 468.143 236.435 468.019 236.641 c
+467.901 236.847 467.842 237.067 467.842 237.302 c
+468.827 237.302 l
+468.827 237.115 468.863 236.979 468.944 236.891 c
+469.033 236.803 469.165 236.759 469.341 236.759 c
+469.625 236.759 469.768 236.884 469.768 237.141 c
+472.516 236.068 -1.044 3.983 re
+471.429 241.08 m
+471.429 241.235 471.476 241.364 471.576 241.462 c
+471.682 241.569 471.818 241.624 471.987 241.624 c
+472.164 241.624 472.299 241.569 472.399 241.462 c
+472.505 241.364 472.561 241.235 472.561 241.08 c
+472.561 240.911 472.505 240.776 472.399 240.668 c
+472.299 240.57 472.164 240.522 471.987 240.522 c
+471.818 240.522 471.682 240.57 471.576 240.668 c
+471.476 240.776 471.429 240.911 471.429 241.08 c
+473.207 238.185 m
+473.207 238.791 473.347 239.265 473.633 239.61 c
+473.916 239.953 474.309 240.125 474.809 240.125 c
+475.316 240.125 475.713 239.953 476 239.61 c
+476.283 239.265 476.426 238.791 476.426 238.185 c
+476.426 237.92 l
+476.426 237.321 476.283 236.851 476 236.508 c
+475.713 236.163 475.316 235.994 474.809 235.994 c
+474.298 235.994 473.902 236.163 473.619 236.508 c
+473.343 236.851 473.207 237.325 473.207 237.935 c
+h
+474.251 237.92 m
+474.251 237.214 474.435 236.861 474.809 236.861 c
+475.162 236.861 475.353 237.156 475.383 237.744 c
+475.383 238.185 l
+475.383 238.544 475.331 238.816 475.235 238.993 c
+475.136 239.169 474.993 239.257 474.809 239.257 c
+474.633 239.257 474.493 239.169 474.398 238.993 c
+474.298 238.816 474.251 238.544 474.251 238.185 c
+h
+478.028 240.051 m
+478.057 239.654 l
+478.293 239.967 478.595 240.125 478.969 240.125 c
+479.653 240.125 480.006 239.643 480.027 238.684 c
+480.027 236.068 l
+478.984 236.068 l
+478.984 238.611 l
+478.984 238.835 478.947 238.997 478.881 239.096 c
+478.811 239.191 478.693 239.243 478.528 239.243 c
+478.341 239.243 478.194 239.147 478.087 238.964 c
+478.087 236.068 l
+477.043 236.068 l
+477.043 240.051 l
+h
+f
+q 1 0 0 1 482.4675 238.2432 cm
+0 0 m
+0 0.577 0.136 1.033 0.411 1.367 c
+0.694 1.709 1.066 1.881 1.529 1.881 c
+1.988 1.881 2.356 1.712 2.631 1.382 c
+2.914 1.058 3.061 0.611 3.072 0.044 c
+3.072 -0.382 l
+3.072 -0.952 2.929 -1.407 2.645 -1.749 c
+2.37 -2.084 2.003 -2.249 1.544 -2.249 c
+1.08 -2.249 0.709 -2.087 0.426 -1.764 c
+0.151 -1.433 0.008 -0.992 0 -0.441 c
+h
+0.646 -0.382 m
+0.646 -0.786 0.723 -1.103 0.882 -1.338 c
+1.047 -1.573 1.268 -1.691 1.544 -1.691 c
+2.109 -1.691 2.404 -1.278 2.425 -0.455 c
+2.425 0 l
+2.425 0.401 2.341 0.721 2.175 0.956 c
+2.018 1.199 1.801 1.323 1.529 1.323 c
+1.264 1.323 1.047 1.199 0.882 0.956 c
+0.723 0.721 0.646 0.401 0.646 0 c
+h
+4.204 -2.175 m
+4.204 1.278 l
+3.675 1.278 l
+3.675 1.808 l
+4.204 1.808 l
+4.204 2.263 l
+4.204 2.664 4.299 2.977 4.498 3.204 c
+4.704 3.429 4.983 3.543 5.336 3.543 c
+5.471 3.543 5.604 3.52 5.733 3.484 c
+5.703 2.94 l
+5.604 2.959 5.505 2.969 5.409 2.969 c
+5.035 2.969 4.85 2.705 4.85 2.176 c
+4.85 1.808 l
+5.527 1.808 l
+5.527 1.278 l
+4.85 1.278 l
+4.85 -2.175 l
+h
+9.392 -1.691 m
+9.606 -1.691 9.779 -1.627 9.907 -1.5 c
+10.043 -1.363 10.117 -1.172 10.128 -0.926 c
+10.745 -0.926 l
+10.723 -1.309 10.587 -1.627 10.333 -1.881 c
+10.076 -2.128 9.764 -2.249 9.392 -2.249 c
+8.9 -2.249 8.525 -2.098 8.261 -1.793 c
+8.004 -1.481 7.879 -1.014 7.879 -0.397 c
+7.879 0.044 l
+7.879 0.64 8.004 1.095 8.261 1.411 c
+8.525 1.723 8.9 1.881 9.392 1.881 c
+9.793 1.881 10.113 1.749 10.348 1.484 c
+10.591 1.228 10.723 0.882 10.745 0.441 c
+10.128 0.441 l
+10.105 0.735 10.032 0.956 9.907 1.103 c
+9.789 1.249 9.617 1.323 9.392 1.323 c
+9.099 1.323 8.882 1.224 8.746 1.029 c
+8.606 0.842 8.533 0.533 8.525 0.103 c
+8.525 -0.411 l
+8.525 -0.881 8.592 -1.216 8.731 -1.411 c
+8.878 -1.598 9.099 -1.691 9.392 -1.691 c
+12.141 1.396 m
+12.395 1.72 12.715 1.881 13.097 1.881 c
+13.803 1.881 14.159 1.411 14.169 0.47 c
+14.169 -2.175 l
+13.523 -2.175 l
+13.523 0.441 l
+13.523 0.754 13.468 0.974 13.361 1.103 c
+13.251 1.228 13.097 1.294 12.891 1.294 c
+12.733 1.294 12.586 1.239 12.45 1.132 c
+12.322 1.022 12.218 0.886 12.141 0.721 c
+12.141 -2.175 l
+11.495 -2.175 l
+11.495 3.469 l
+12.141 3.469 l
+h
+17.169 -2.175 m
+17.128 -2.087 17.103 -1.94 17.095 -1.735 c
+16.86 -2.08 16.565 -2.249 16.213 -2.249 c
+15.85 -2.249 15.566 -2.153 15.36 -1.955 c
+15.162 -1.749 15.067 -1.463 15.067 -1.087 c
+15.067 -0.687 15.202 -0.368 15.478 -0.133 c
+15.75 0.11 16.125 0.235 16.596 0.235 c
+17.08 0.235 l
+17.08 0.661 l
+17.08 0.897 17.026 1.062 16.918 1.161 c
+16.808 1.268 16.646 1.323 16.434 1.323 c
+16.235 1.323 16.074 1.264 15.948 1.147 c
+15.831 1.029 15.772 0.882 15.772 0.706 c
+15.125 0.706 l
+15.125 0.9 15.185 1.091 15.302 1.278 c
+15.426 1.463 15.588 1.61 15.787 1.72 c
+15.993 1.827 16.22 1.881 16.478 1.881 c
+16.878 1.881 17.184 1.779 17.389 1.573 c
+17.602 1.367 17.716 1.073 17.727 0.691 c
+17.727 -1.323 l
+17.727 -1.627 17.764 -1.893 17.845 -2.117 c
+17.845 -2.175 l
+h
+16.301 -1.661 m
+16.467 -1.661 16.617 -1.617 16.756 -1.529 c
+16.904 -1.44 17.01 -1.33 17.08 -1.191 c
+17.08 -0.25 l
+16.713 -0.25 l
+16.397 -0.25 16.154 -0.32 15.978 -0.455 c
+15.802 -0.584 15.713 -0.771 15.713 -1.014 c
+15.713 -1.242 15.757 -1.407 15.846 -1.514 c
+15.933 -1.613 16.085 -1.661 16.301 -1.661 c
+19.343 1.808 m
+19.359 1.367 l
+19.613 1.709 19.935 1.881 20.328 1.881 c
+21.034 1.881 21.391 1.411 21.402 0.47 c
+21.402 -2.175 l
+20.755 -2.175 l
+20.755 0.441 l
+20.755 0.754 20.7 0.974 20.594 1.103 c
+20.483 1.228 20.328 1.294 20.123 1.294 c
+19.965 1.294 19.818 1.239 19.682 1.132 c
+19.553 1.022 19.451 0.886 19.374 0.721 c
+19.374 -2.175 l
+18.726 -2.175 l
+18.726 1.808 l
+h
+22.24 0 m
+22.24 0.617 22.35 1.081 22.578 1.396 c
+22.802 1.72 23.137 1.881 23.577 1.881 c
+23.978 1.881 24.282 1.706 24.488 1.353 c
+24.533 1.808 l
+25.121 1.808 l
+25.121 -2.219 l
+25.121 -2.708 24.992 -3.087 24.739 -3.351 c
+24.481 -3.616 24.128 -3.748 23.68 -3.748 c
+23.482 -3.748 23.262 -3.697 23.019 -3.601 c
+22.772 -3.502 22.593 -3.381 22.475 -3.233 c
+22.74 -2.793 l
+23.004 -3.057 23.301 -3.19 23.636 -3.19 c
+24.172 -3.19 24.448 -2.896 24.459 -2.308 c
+24.459 -1.779 l
+24.253 -2.094 23.952 -2.249 23.563 -2.249 c
+23.151 -2.249 22.828 -2.098 22.593 -1.793 c
+22.364 -1.481 22.247 -1.029 22.24 -0.441 c
+h
+22.901 -0.382 m
+22.901 -0.823 22.963 -1.153 23.092 -1.367 c
+23.217 -1.573 23.434 -1.675 23.739 -1.675 c
+24.062 -1.675 24.301 -1.51 24.459 -1.176 c
+24.459 0.808 l
+24.29 1.132 24.051 1.294 23.739 1.294 c
+23.445 1.294 23.228 1.191 23.092 0.985 c
+22.963 0.779 22.901 0.455 22.901 0.015 c
+h
+27.502 -2.249 m
+27.002 -2.249 26.62 -2.102 26.356 -1.808 c
+26.09 -1.514 25.959 -1.08 25.959 -0.5 c
+25.959 -0.029 l
+25.959 0.566 26.084 1.033 26.341 1.367 c
+26.605 1.709 26.965 1.881 27.428 1.881 c
+27.888 1.881 28.23 1.727 28.457 1.426 c
+28.693 1.132 28.814 0.669 28.825 0.044 c
+28.825 -0.382 l
+26.605 -0.382 l
+26.605 -0.47 l
+26.605 -0.904 26.682 -1.216 26.84 -1.411 c
+27.006 -1.598 27.237 -1.691 27.532 -1.691 c
+27.726 -1.691 27.899 -1.658 28.045 -1.587 c
+28.193 -1.51 28.328 -1.392 28.457 -1.234 c
+28.795 -1.646 l
+28.509 -2.051 28.079 -2.249 27.502 -2.249 c
+27.428 1.323 m
+27.153 1.323 26.95 1.228 26.826 1.043 c
+26.697 0.856 26.624 0.566 26.605 0.177 c
+28.178 0.177 l
+28.178 0.264 l
+28.156 0.647 28.09 0.915 27.972 1.073 c
+27.854 1.239 27.671 1.323 27.428 1.323 c
+31.544 -1.161 m
+31.544 -1.014 31.489 -0.893 31.382 -0.794 c
+31.272 -0.698 31.066 -0.58 30.765 -0.441 c
+30.42 -0.294 30.177 -0.172 30.03 -0.073 c
+29.884 0.033 29.773 0.151 29.707 0.279 c
+29.637 0.405 29.604 0.563 29.604 0.75 c
+29.604 1.073 29.722 1.341 29.957 1.558 c
+30.192 1.771 30.493 1.881 30.868 1.881 c
+31.25 1.881 31.559 1.768 31.794 1.544 c
+32.029 1.316 32.147 1.029 32.147 0.676 c
+31.5 0.676 l
+31.5 0.852 31.441 1.004 31.324 1.132 c
+31.206 1.257 31.052 1.323 30.868 1.323 c
+30.67 1.323 30.519 1.268 30.412 1.161 c
+30.302 1.062 30.25 0.929 30.25 0.765 c
+30.25 0.636 30.287 0.53 30.368 0.441 c
+30.445 0.36 30.636 0.258 30.942 0.133 c
+31.419 -0.055 31.75 -0.243 31.926 -0.426 c
+32.103 -0.603 32.191 -0.831 32.191 -1.103 c
+32.191 -1.455 32.066 -1.735 31.823 -1.94 c
+31.588 -2.146 31.272 -2.249 30.883 -2.249 c
+30.46 -2.249 30.122 -2.132 29.869 -1.897 c
+29.612 -1.654 29.487 -1.348 29.487 -0.985 c
+30.133 -0.985 l
+30.14 -1.213 30.21 -1.389 30.339 -1.514 c
+30.464 -1.631 30.647 -1.691 30.883 -1.691 c
+31.096 -1.691 31.258 -1.643 31.368 -1.544 c
+31.486 -1.448 31.544 -1.319 31.544 -1.161 c
+33.073 -1.822 m
+33.073 -1.705 33.106 -1.61 33.176 -1.529 c
+33.242 -1.452 33.344 -1.411 33.485 -1.411 c
+33.631 -1.411 33.738 -1.452 33.807 -1.529 c
+33.885 -1.61 33.925 -1.705 33.925 -1.822 c
+33.925 -1.933 33.885 -2.024 33.807 -2.102 c
+33.738 -2.179 33.631 -2.219 33.485 -2.219 c
+33.344 -2.219 33.242 -2.179 33.176 -2.102 c
+33.106 -2.024 33.073 -1.933 33.073 -1.822 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+35.668 310.736 238.665 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 84.2441 302.125 cm
+0 0 m
+0 -0.463 -0.099 -0.867 -0.294 -1.22 c
+-0.492 -1.565 -0.756 -1.845 -1.087 -2.057 c
+-0.706 -2.275 -0.404 -2.572 -0.176 -2.954 c
+0.048 -3.337 0.162 -3.788 0.162 -4.307 c
+0.162 -5.14 -0.081 -5.802 -0.558 -6.291 c
+-1.04 -6.772 -1.697 -7.011 -2.528 -7.011 c
+-3.351 -7.011 -4.008 -6.772 -4.498 -6.291 c
+-4.99 -5.802 -5.232 -5.14 -5.232 -4.307 c
+-5.232 -3.788 -5.126 -3.337 -4.909 -2.954 c
+-4.685 -2.572 -4.376 -2.275 -3.983 -2.057 c
+-4.328 -1.845 -4.596 -1.565 -4.791 -1.22 c
+-4.979 -0.867 -5.071 -0.463 -5.071 0 c
+-5.071 0.813 -4.847 1.448 -4.395 1.912 c
+-3.935 2.382 -3.318 2.617 -2.543 2.617 c
+-1.76 2.617 -1.142 2.382 -0.691 1.912 c
+-0.231 1.441 0 0.802 0 0 c
+-2.528 -5.497 m
+-2.256 -5.497 -2.043 -5.379 -1.896 -5.144 c
+-1.749 -4.909 -1.675 -4.586 -1.675 -4.174 c
+-1.675 -3.755 -1.756 -3.424 -1.911 -3.189 c
+-2.057 -2.954 -2.271 -2.836 -2.543 -2.836 c
+-2.807 -2.836 -3.021 -2.954 -3.175 -3.189 c
+-3.333 -3.424 -3.41 -3.755 -3.41 -4.174 c
+-3.41 -4.586 -3.333 -4.909 -3.175 -5.144 c
+-3.021 -5.379 -2.803 -5.497 -2.528 -5.497 c
+-1.822 -0.103 m
+-1.822 0.258 -1.881 0.548 -1.999 0.765 c
+-2.117 0.989 -2.3 1.103 -2.543 1.103 c
+-2.77 1.103 -2.946 0.992 -3.072 0.779 c
+-3.189 0.574 -3.248 0.279 -3.248 -0.103 c
+-3.248 -0.467 -3.189 -0.76 -3.072 -0.985 c
+-2.946 -1.213 -2.763 -1.323 -2.528 -1.323 c
+-2.285 -1.323 -2.109 -1.213 -1.999 -0.985 c
+-1.881 -0.76 -1.822 -0.467 -1.822 -0.103 c
+1.798 -5.953 m
+1.798 -5.659 1.893 -5.42 2.091 -5.232 c
+2.286 -5.049 2.54 -4.953 2.856 -4.953 c
+3.157 -4.953 3.406 -5.049 3.605 -5.232 c
+3.811 -5.42 3.914 -5.659 3.914 -5.953 c
+3.914 -6.258 3.811 -6.504 3.605 -6.688 c
+3.406 -6.865 3.157 -6.952 2.856 -6.952 c
+2.55 -6.952 2.297 -6.861 2.091 -6.673 c
+1.893 -6.489 1.798 -6.246 1.798 -5.953 c
+12.755 -4.424 m
+12.755 -4.042 12.657 -3.755 12.461 -3.557 c
+12.263 -3.362 11.911 -3.16 11.403 -2.954 c
+10.462 -2.594 9.786 -2.175 9.374 -1.705 c
+8.963 -1.227 8.757 -0.658 8.757 0 c
+8.757 0.783 9.037 1.415 9.596 1.897 c
+10.153 2.374 10.863 2.617 11.726 2.617 c
+12.303 2.617 12.818 2.492 13.27 2.249 c
+13.718 2.003 14.064 1.661 14.299 1.22 c
+14.541 0.779 14.666 0.279 14.666 -0.279 c
+12.785 -0.279 l
+12.785 0.162 12.69 0.493 12.505 0.721 c
+12.318 0.944 12.05 1.058 11.697 1.058 c
+11.362 1.058 11.102 0.96 10.918 0.765 c
+10.741 0.578 10.654 0.316 10.654 -0.014 c
+10.654 -0.272 10.756 -0.507 10.962 -0.72 c
+11.168 -0.926 11.528 -1.142 12.05 -1.367 c
+12.962 -1.691 13.623 -2.094 14.034 -2.572 c
+14.454 -3.054 14.666 -3.667 14.666 -4.409 c
+14.666 -5.225 14.406 -5.861 13.887 -6.32 c
+13.365 -6.784 12.66 -7.011 11.771 -7.011 c
+11.161 -7.011 10.61 -6.886 10.109 -6.643 c
+9.617 -6.39 9.231 -6.034 8.948 -5.571 c
+8.661 -5.101 8.522 -4.552 8.522 -3.925 c
+10.419 -3.925 l
+10.419 -4.465 10.521 -4.858 10.727 -5.101 c
+10.94 -5.346 11.289 -5.468 11.771 -5.468 c
+12.424 -5.468 12.755 -5.122 12.755 -4.424 c
+18.955 -1.72 m
+20.366 2.484 l
+22.423 2.484 l
+19.91 -3.484 l
+19.91 -6.879 l
+17.999 -6.879 l
+17.999 -3.484 l
+15.486 2.484 l
+17.544 2.484 l
+h
+30.042 -6.879 m
+28.145 -6.879 l
+25.382 -0.735 l
+25.382 -6.879 l
+23.486 -6.879 l
+23.486 2.484 l
+25.382 2.484 l
+28.145 -3.659 l
+28.145 2.484 l
+30.042 2.484 l
+h
+38.144 -3.763 m
+38.093 -4.832 37.791 -5.641 37.233 -6.188 c
+36.682 -6.739 35.91 -7.011 34.911 -7.011 c
+33.841 -7.011 33.022 -6.666 32.456 -5.967 c
+31.898 -5.273 31.618 -4.278 31.618 -2.984 c
+31.618 -1.411 l
+31.618 -0.118 31.904 0.875 32.486 1.573 c
+33.074 2.268 33.885 2.617 34.925 2.617 c
+35.943 2.617 36.719 2.326 37.248 1.75 c
+37.777 1.18 38.078 0.36 38.159 -0.706 c
+36.263 -0.706 l
+36.241 -0.04 36.138 0.416 35.954 0.661 c
+35.767 0.915 35.425 1.044 34.925 1.044 c
+34.425 1.044 34.065 0.867 33.852 0.515 c
+33.646 0.162 33.533 -0.422 33.514 -1.234 c
+33.514 -2.998 l
+33.514 -3.931 33.617 -4.571 33.823 -4.924 c
+34.036 -5.269 34.4 -5.438 34.911 -5.438 c
+35.399 -5.438 35.738 -5.321 35.925 -5.086 c
+36.12 -4.843 36.226 -4.402 36.249 -3.763 c
+h
+46.09 -6.879 m
+44.208 -6.879 l
+44.208 -2.866 l
+41.415 -2.866 l
+41.415 -6.879 l
+39.519 -6.879 l
+39.519 2.484 l
+41.415 2.484 l
+41.415 -1.308 l
+44.208 -1.308 l
+44.208 2.484 l
+46.09 2.484 l
+h
+50.664 -3.454 m
+49.724 -3.454 l
+49.724 -6.879 l
+47.842 -6.879 l
+47.842 2.484 l
+50.855 2.484 l
+51.804 2.484 52.535 2.238 53.045 1.75 c
+53.564 1.257 53.824 0.563 53.824 -0.338 c
+53.824 -1.583 53.373 -2.454 52.473 -2.954 c
+54.104 -6.79 l
+54.104 -6.879 l
+52.076 -6.879 l
+h
+49.724 -1.881 m
+50.796 -1.881 l
+51.179 -1.881 51.462 -1.76 51.649 -1.514 c
+51.833 -1.26 51.929 -0.922 51.929 -0.5 c
+51.929 0.441 51.565 0.912 50.84 0.912 c
+49.724 0.912 l
+h
+61.942 -3.042 m
+61.942 -4.299 61.641 -5.273 61.046 -5.967 c
+60.446 -6.666 59.623 -7.011 58.576 -7.011 c
+57.525 -7.011 56.698 -6.669 56.092 -5.982 c
+55.493 -5.288 55.188 -4.321 55.181 -3.087 c
+55.181 -1.484 l
+55.181 -0.201 55.478 0.802 56.077 1.529 c
+56.673 2.253 57.503 2.617 58.561 2.617 c
+59.598 2.617 60.417 2.257 61.016 1.544 c
+61.622 0.838 61.931 -0.158 61.942 -1.44 c
+h
+60.046 -1.469 m
+60.046 -0.628 59.921 0 59.679 0.412 c
+59.443 0.823 59.068 1.029 58.561 1.029 c
+58.061 1.029 57.687 0.827 57.444 0.426 c
+57.209 0.034 57.084 -0.565 57.077 -1.367 c
+57.077 -3.042 l
+57.077 -3.858 57.198 -4.461 57.444 -4.85 c
+57.687 -5.244 58.065 -5.438 58.576 -5.438 c
+59.065 -5.438 59.428 -5.247 59.664 -4.866 c
+59.906 -4.483 60.035 -3.895 60.046 -3.102 c
+h
+70.075 -6.879 m
+68.178 -6.879 l
+65.415 -0.735 l
+65.415 -6.879 l
+63.519 -6.879 l
+63.519 2.484 l
+65.415 2.484 l
+68.178 -3.659 l
+68.178 2.484 l
+70.075 2.484 l
+h
+73.811 -6.879 -1.896 9.363 re
+77.472 -5.306 m
+81.323 -5.306 l
+81.323 -6.879 l
+75.253 -6.879 l
+75.253 -5.747 l
+79.074 0.912 l
+75.222 0.912 l
+75.222 2.484 l
+81.25 2.484 l
+81.25 1.367 l
+h
+84.685 -6.879 -1.897 9.363 re
+92.935 -6.879 m
+91.039 -6.879 l
+88.275 -0.735 l
+88.275 -6.879 l
+86.38 -6.879 l
+86.38 2.484 l
+88.275 2.484 l
+91.039 -3.659 l
+91.039 2.484 l
+92.935 2.484 l
+h
+101.049 -5.82 m
+100.685 -6.214 100.24 -6.512 99.712 -6.717 c
+99.182 -6.912 98.602 -7.011 97.977 -7.011 c
+96.896 -7.011 96.059 -6.68 95.463 -6.011 c
+94.864 -5.346 94.559 -4.376 94.552 -3.102 c
+94.552 -1.411 l
+94.552 -0.118 94.831 0.875 95.39 1.573 c
+95.955 2.268 96.779 2.617 97.859 2.617 c
+98.877 2.617 99.641 2.359 100.152 1.852 c
+100.67 1.353 100.968 0.566 101.049 -0.5 c
+99.211 -0.5 l
+99.161 0.096 99.039 0.504 98.844 0.721 c
+98.646 0.934 98.337 1.044 97.918 1.044 c
+97.407 1.044 97.036 0.857 96.801 0.485 c
+96.573 0.111 96.456 -0.481 96.448 -1.294 c
+96.448 -2.998 l
+96.448 -3.85 96.573 -4.475 96.83 -4.866 c
+97.084 -5.247 97.499 -5.438 98.08 -5.438 c
+98.451 -5.438 98.756 -5.365 98.991 -5.218 c
+99.153 -5.101 l
+99.153 -3.38 l
+97.83 -3.38 l
+97.83 -1.955 l
+101.049 -1.955 l
+h
+108.582 -3.454 m
+107.641 -3.454 l
+107.641 -6.879 l
+105.76 -6.879 l
+105.76 2.484 l
+108.773 2.484 l
+109.721 2.484 110.453 2.238 110.964 1.75 c
+111.481 1.257 111.743 0.563 111.743 -0.338 c
+111.743 -1.583 111.29 -2.454 110.39 -2.954 c
+112.021 -6.79 l
+112.021 -6.879 l
+109.993 -6.879 l
+h
+107.641 -1.881 m
+108.715 -1.881 l
+109.097 -1.881 109.38 -1.76 109.567 -1.514 c
+109.75 -1.26 109.846 -0.922 109.846 -0.5 c
+109.846 0.441 109.482 0.912 108.759 0.912 c
+107.641 0.912 l
+h
+118.097 -2.822 m
+115.157 -2.822 l
+115.157 -5.306 l
+118.64 -5.306 l
+118.64 -6.879 l
+113.26 -6.879 l
+113.26 2.484 l
+118.625 2.484 l
+118.625 0.912 l
+115.157 0.912 l
+115.157 -1.308 l
+118.097 -1.308 l
+h
+121.797 -3.586 m
+121.797 -6.879 l
+119.901 -6.879 l
+119.901 2.484 l
+123.09 2.484 l
+124.02 2.484 124.759 2.194 125.31 1.617 c
+125.858 1.048 126.132 0.302 126.132 -0.617 c
+126.132 -1.529 125.858 -2.256 125.31 -2.792 c
+124.769 -3.322 124.02 -3.586 123.061 -3.586 c
+h
+121.797 -2.013 m
+123.09 -2.013 l
+123.45 -2.013 123.73 -1.896 123.928 -1.66 c
+124.123 -1.425 124.222 -1.084 124.222 -0.632 c
+124.222 -0.162 124.119 0.21 123.913 0.485 c
+123.715 0.757 123.45 0.9 123.119 0.912 c
+121.797 0.912 l
+h
+134.199 -3.042 m
+134.199 -4.299 133.897 -5.273 133.303 -5.967 c
+132.704 -6.666 131.88 -7.011 130.833 -7.011 c
+129.782 -7.011 128.955 -6.669 128.349 -5.982 c
+127.75 -5.288 127.445 -4.321 127.437 -3.087 c
+127.437 -1.484 l
+127.437 -0.201 127.735 0.802 128.334 1.529 c
+128.929 2.253 129.76 2.617 130.818 2.617 c
+131.855 2.617 132.674 2.257 133.273 1.544 c
+133.879 0.838 134.188 -0.158 134.199 -1.44 c
+h
+132.303 -1.469 m
+132.303 -0.628 132.178 0 131.935 0.412 c
+131.7 0.823 131.325 1.029 130.818 1.029 c
+130.319 1.029 129.943 0.827 129.701 0.426 c
+129.465 0.034 129.341 -0.565 129.334 -1.367 c
+129.334 -3.042 l
+129.334 -3.858 129.455 -4.461 129.701 -4.85 c
+129.943 -5.244 130.322 -5.438 130.833 -5.438 c
+131.322 -5.438 131.686 -5.247 131.921 -4.866 c
+132.164 -4.483 132.291 -3.895 132.303 -3.102 c
+h
+139.73 -4.424 m
+139.73 -4.042 139.63 -3.755 139.435 -3.557 c
+139.237 -3.362 138.884 -3.16 138.377 -2.954 c
+137.436 -2.594 136.76 -2.175 136.349 -1.705 c
+135.937 -1.227 135.732 -0.658 135.732 0 c
+135.732 0.783 136.01 1.415 136.569 1.897 c
+137.128 2.374 137.837 2.617 138.701 2.617 c
+139.277 2.617 139.792 2.492 140.244 2.249 c
+140.692 2.003 141.038 1.661 141.273 1.22 c
+141.515 0.779 141.641 0.279 141.641 -0.279 c
+139.759 -0.279 l
+139.759 0.162 139.663 0.493 139.48 0.721 c
+139.292 0.944 139.024 1.058 138.672 1.058 c
+138.337 1.058 138.076 0.96 137.892 0.765 c
+137.716 0.578 137.627 0.316 137.627 -0.014 c
+137.627 -0.272 137.731 -0.507 137.936 -0.72 c
+138.142 -0.926 138.502 -1.142 139.024 -1.367 c
+139.935 -1.691 140.597 -2.094 141.008 -2.572 c
+141.427 -3.054 141.641 -3.667 141.641 -4.409 c
+141.641 -5.225 141.38 -5.861 140.862 -6.32 c
+140.339 -6.784 139.634 -7.011 138.745 -7.011 c
+138.134 -7.011 137.583 -6.886 137.084 -6.643 c
+136.592 -6.39 136.206 -6.034 135.923 -5.571 c
+135.636 -5.101 135.497 -4.552 135.497 -3.925 c
+137.392 -3.925 l
+137.392 -4.465 137.496 -4.858 137.701 -5.101 c
+137.914 -5.346 138.263 -5.468 138.745 -5.468 c
+139.399 -5.468 139.73 -5.122 139.73 -4.424 c
+145.087 -6.879 -1.897 9.363 re
+152.669 0.912 m
+150.346 0.912 l
+150.346 -6.879 l
+148.45 -6.879 l
+148.45 0.912 l
+146.171 0.912 l
+146.171 2.484 l
+152.669 2.484 l
+h
+160.367 -3.042 m
+160.367 -4.299 160.066 -5.273 159.47 -5.967 c
+158.871 -6.666 158.048 -7.011 157.001 -7.011 c
+155.95 -7.011 155.123 -6.669 154.516 -5.982 c
+153.918 -5.288 153.612 -4.321 153.606 -3.087 c
+153.606 -1.484 l
+153.606 -0.201 153.903 0.802 154.502 1.529 c
+155.098 2.253 155.928 2.617 156.986 2.617 c
+158.023 2.617 158.842 2.257 159.441 1.544 c
+160.047 0.838 160.356 -0.158 160.367 -1.44 c
+h
+158.471 -1.469 m
+158.471 -0.628 158.346 0 158.103 0.412 c
+157.868 0.823 157.493 1.029 156.986 1.029 c
+156.486 1.029 156.112 0.827 155.869 0.426 c
+155.634 0.034 155.509 -0.565 155.501 -1.367 c
+155.501 -3.042 l
+155.501 -3.858 155.623 -4.461 155.869 -4.85 c
+156.112 -5.244 156.49 -5.438 157.001 -5.438 c
+157.49 -5.438 157.853 -5.247 158.088 -4.866 c
+158.331 -4.483 158.46 -3.895 158.471 -3.102 c
+h
+164.781 -3.454 m
+163.84 -3.454 l
+163.84 -6.879 l
+161.959 -6.879 l
+161.959 2.484 l
+164.972 2.484 l
+165.919 2.484 166.651 2.238 167.162 1.75 c
+167.68 1.257 167.941 0.563 167.941 -0.338 c
+167.941 -1.583 167.488 -2.454 166.588 -2.954 c
+168.22 -6.79 l
+168.22 -6.879 l
+166.192 -6.879 l
+h
+163.84 -1.881 m
+164.913 -1.881 l
+165.294 -1.881 165.578 -1.76 165.765 -1.514 c
+165.949 -1.26 166.044 -0.922 166.044 -0.5 c
+166.044 0.441 165.681 0.912 164.957 0.912 c
+163.84 0.912 l
+h
+171.443 -6.879 -1.897 9.363 re
+177.969 -2.822 m
+175.029 -2.822 l
+175.029 -5.306 l
+178.513 -5.306 l
+178.513 -6.879 l
+173.133 -6.879 l
+173.133 2.484 l
+178.499 2.484 l
+178.499 0.912 l
+175.029 0.912 l
+175.029 -1.308 l
+177.969 -1.308 l
+h
+183.731 -4.424 m
+183.731 -4.042 183.632 -3.755 183.438 -3.557 c
+183.239 -3.362 182.886 -3.16 182.379 -2.954 c
+181.439 -2.594 180.762 -2.175 180.35 -1.705 c
+179.939 -1.227 179.733 -0.658 179.733 0 c
+179.733 0.783 180.012 1.415 180.571 1.897 c
+181.129 2.374 181.838 2.617 182.702 2.617 c
+183.28 2.617 183.793 2.492 184.246 2.249 c
+184.693 2.003 185.04 1.661 185.275 1.22 c
+185.518 0.779 185.642 0.279 185.642 -0.279 c
+183.76 -0.279 l
+183.76 0.162 183.665 0.493 183.481 0.721 c
+183.294 0.944 183.026 1.058 182.673 1.058 c
+182.339 1.058 182.077 0.96 181.894 0.765 c
+181.717 0.578 181.629 0.316 181.629 -0.014 c
+181.629 -0.272 181.732 -0.507 181.938 -0.72 c
+182.144 -0.926 182.503 -1.142 183.026 -1.367 c
+183.937 -1.691 184.598 -2.094 185.01 -2.572 c
+185.429 -3.054 185.642 -3.667 185.642 -4.409 c
+185.642 -5.225 185.381 -5.861 184.863 -6.32 c
+184.342 -6.784 183.636 -7.011 182.746 -7.011 c
+182.137 -7.011 181.585 -6.886 181.086 -6.643 c
+180.593 -6.39 180.207 -6.034 179.924 -5.571 c
+179.638 -5.101 179.498 -4.552 179.498 -3.925 c
+181.394 -3.925 l
+181.394 -4.465 181.497 -4.858 181.703 -5.101 c
+181.915 -5.346 182.264 -5.468 182.746 -5.468 c
+183.401 -5.468 183.731 -5.122 183.731 -4.424 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 280.433 238.665 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 273.5943 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.69 15.056 -0.69 c
+15.192 -0.69 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.264 c
+14.221 -1.227 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+22.765 1.602 m
+22.765 -1.264 l
+21.868 -1.264 l
+21.868 1.602 l
+21.045 1.602 l
+21.045 2.22 l
+21.868 2.22 l
+21.868 2.484 l
+21.868 2.61 21.883 2.741 21.913 2.882 c
+21.949 3.017 22.02 3.135 22.119 3.234 c
+22.225 3.341 22.369 3.429 22.545 3.499 c
+22.722 3.564 22.946 3.601 23.221 3.601 c
+23.434 3.601 23.632 3.591 23.809 3.572 c
+23.985 3.55 24.137 3.532 24.264 3.514 c
+24.264 2.926 l
+24.137 2.944 23.993 2.959 23.838 2.969 c
+23.68 2.976 23.53 2.984 23.383 2.984 c
+23.254 2.984 23.152 2.969 23.074 2.94 c
+22.994 2.911 22.931 2.87 22.883 2.822 c
+22.832 2.771 22.799 2.708 22.78 2.631 c
+22.769 2.562 22.765 2.484 22.765 2.396 c
+22.765 2.22 l
+24.191 2.22 l
+24.191 1.602 l
+h
+26.738 -1.323 m
+26.481 -1.323 26.253 -1.286 26.047 -1.22 c
+25.841 -1.143 25.665 -1.029 25.518 -0.881 c
+25.371 -0.727 25.253 -0.536 25.165 -0.309 c
+25.084 -0.085 25.047 0.181 25.047 0.485 c
+25.047 0.816 25.091 1.095 25.18 1.323 c
+25.275 1.558 25.404 1.742 25.562 1.881 c
+25.727 2.018 25.915 2.117 26.121 2.176 c
+26.327 2.242 26.535 2.278 26.753 2.278 c
+27.025 2.278 27.26 2.227 27.458 2.132 c
+27.664 2.043 27.829 1.911 27.958 1.735 c
+28.094 1.565 28.193 1.36 28.252 1.118 c
+28.318 0.882 28.355 0.617 28.355 0.324 c
+28.355 0.309 l
+25.988 0.309 l
+25.988 0.162 26.003 0.023 26.032 -0.103 c
+26.069 -0.231 26.124 -0.345 26.194 -0.441 c
+26.26 -0.529 26.344 -0.598 26.444 -0.646 c
+26.539 -0.698 26.653 -0.72 26.782 -0.72 c
+26.936 -0.72 27.076 -0.687 27.194 -0.617 c
+27.318 -0.551 27.407 -0.448 27.458 -0.309 c
+28.296 -0.382 l
+28.266 -0.481 28.212 -0.588 28.135 -0.706 c
+28.054 -0.816 27.95 -0.918 27.825 -1.014 c
+27.708 -1.103 27.553 -1.176 27.37 -1.234 c
+27.194 -1.294 26.98 -1.323 26.738 -1.323 c
+26.738 1.706 m
+26.649 1.706 26.562 1.69 26.473 1.661 c
+26.385 1.632 26.304 1.58 26.238 1.515 c
+26.169 1.444 26.109 1.357 26.061 1.249 c
+26.021 1.139 26.003 1.014 26.003 0.867 c
+27.472 0.867 l
+27.472 1.004 27.447 1.125 27.399 1.235 c
+27.359 1.341 27.304 1.43 27.237 1.5 c
+27.179 1.565 27.106 1.617 27.017 1.646 c
+26.929 1.683 26.834 1.706 26.738 1.706 c
+29.843 1.602 m
+29.299 1.602 l
+29.299 2.22 l
+29.887 2.22 l
+30.167 3.117 l
+30.74 3.117 l
+30.74 2.22 l
+31.975 2.22 l
+31.975 1.602 l
+30.74 1.602 l
+30.74 -0.103 l
+30.74 -0.324 l
+30.747 -0.393 30.769 -0.455 30.799 -0.515 c
+30.836 -0.565 30.89 -0.61 30.96 -0.646 c
+31.037 -0.676 31.152 -0.69 31.298 -0.69 c
+31.434 -0.69 31.57 -0.687 31.71 -0.676 c
+31.846 -0.658 31.978 -0.632 32.106 -0.602 c
+32.106 -1.205 l
+32.026 -1.216 31.948 -1.23 31.871 -1.249 c
+31.79 -1.261 31.713 -1.267 31.636 -1.278 c
+31.555 -1.286 31.467 -1.294 31.372 -1.294 c
+31.283 -1.301 31.185 -1.308 31.077 -1.308 c
+30.89 -1.308 30.728 -1.294 30.593 -1.264 c
+30.464 -1.227 30.35 -1.183 30.254 -1.132 c
+30.167 -1.084 30.093 -1.025 30.034 -0.955 c
+29.976 -0.878 29.932 -0.801 29.901 -0.72 c
+29.872 -0.632 29.851 -0.544 29.843 -0.455 c
+29.832 -0.36 29.828 -0.264 29.828 -0.176 c
+h
+34.863 -1.323 m
+34.576 -1.323 34.333 -1.282 34.128 -1.205 c
+33.922 -1.117 33.749 -0.995 33.614 -0.837 c
+33.473 -0.683 33.371 -0.496 33.305 -0.279 c
+33.234 -0.055 33.202 0.191 33.202 0.456 c
+33.202 0.75 33.234 1.008 33.305 1.235 c
+33.382 1.459 33.488 1.646 33.628 1.793 c
+33.775 1.948 33.951 2.066 34.157 2.146 c
+34.363 2.234 34.599 2.278 34.863 2.278 c
+35.087 2.278 35.289 2.249 35.465 2.19 c
+35.642 2.132 35.792 2.047 35.921 1.941 c
+36.046 1.841 36.149 1.72 36.23 1.573 c
+36.307 1.434 36.362 1.283 36.392 1.118 c
+35.48 1.073 l
+35.451 1.249 35.381 1.389 35.274 1.5 c
+35.175 1.606 35.032 1.661 34.848 1.661 c
+34.602 1.661 34.425 1.558 34.319 1.353 c
+34.209 1.154 34.157 0.867 34.157 0.485 c
+34.157 -0.309 34.392 -0.706 34.863 -0.706 c
+35.028 -0.706 35.171 -0.654 35.289 -0.544 c
+35.407 -0.437 35.48 -0.276 35.509 -0.058 c
+36.421 -0.103 l
+36.392 -0.272 36.336 -0.426 36.259 -0.573 c
+36.189 -0.72 36.087 -0.852 35.95 -0.97 c
+35.821 -1.08 35.663 -1.168 35.48 -1.234 c
+35.304 -1.294 35.098 -1.323 34.863 -1.323 c
+38.262 1.515 m
+38.379 1.786 38.53 1.985 38.718 2.103 c
+38.901 2.22 39.122 2.278 39.379 2.278 c
+39.584 2.278 39.754 2.242 39.894 2.176 c
+40.041 2.106 40.151 2.014 40.232 1.897 c
+40.32 1.779 40.378 1.636 40.408 1.47 c
+40.444 1.301 40.467 1.125 40.467 0.941 c
+40.467 -1.264 l
+39.555 -1.264 l
+39.555 0.735 l
+39.555 0.871 39.544 0.992 39.526 1.103 c
+39.515 1.209 39.489 1.297 39.453 1.367 c
+39.412 1.444 39.353 1.503 39.276 1.544 c
+39.206 1.58 39.114 1.602 38.996 1.602 c
+38.886 1.602 38.791 1.577 38.703 1.529 c
+38.615 1.478 38.534 1.411 38.468 1.323 c
+38.409 1.235 38.358 1.125 38.321 1 c
+38.291 0.882 38.277 0.75 38.277 0.603 c
+38.277 -1.264 l
+37.365 -1.264 l
+37.365 3.514 l
+38.277 3.514 l
+38.277 2.205 l
+38.277 2.135 38.269 2.066 38.262 1.999 c
+38.262 1.793 l
+38.262 1.735 38.254 1.679 38.248 1.632 c
+38.248 1.515 l
+h
+46.236 -2.631 m
+46.236 3.514 l
+48.162 3.514 l
+48.162 2.896 l
+47.089 2.896 l
+47.089 -2.013 l
+48.162 -2.013 l
+48.162 -2.631 l
+h
+52.56 1.47 m
+52.461 1.478 52.359 1.488 52.251 1.5 c
+52.141 1.518 52.02 1.529 51.885 1.529 c
+51.708 1.529 51.55 1.488 51.414 1.411 c
+51.274 1.341 51.156 1.243 51.061 1.118 c
+50.973 0.989 50.903 0.842 50.855 0.676 c
+50.815 0.507 50.796 0.331 50.796 0.147 c
+50.796 -1.264 l
+49.9 -1.264 l
+49.9 0.985 l
+49.9 1.11 49.889 1.235 49.87 1.353 c
+49.859 1.478 49.845 1.595 49.826 1.706 c
+49.815 1.823 49.801 1.918 49.782 1.999 c
+49.76 2.087 49.742 2.161 49.724 2.22 c
+50.605 2.22 l
+50.613 2.168 50.624 2.117 50.635 2.058 c
+50.653 1.999 50.668 1.933 50.679 1.867 c
+50.697 1.808 50.712 1.742 50.723 1.675 c
+50.73 1.606 50.742 1.544 50.752 1.484 c
+50.767 1.484 l
+50.804 1.602 50.855 1.709 50.914 1.808 c
+50.981 1.904 51.061 1.989 51.149 2.058 c
+51.237 2.124 51.341 2.18 51.458 2.22 c
+51.583 2.257 51.73 2.278 51.899 2.278 c
+52.024 2.278 52.141 2.271 52.251 2.263 c
+52.369 2.253 52.473 2.238 52.56 2.22 c
+h
+55.17 -1.323 m
+54.912 -1.323 54.684 -1.286 54.478 -1.22 c
+54.273 -1.143 54.096 -1.029 53.95 -0.881 c
+53.803 -0.727 53.685 -0.536 53.597 -0.309 c
+53.516 -0.085 53.479 0.181 53.479 0.485 c
+53.479 0.816 53.523 1.095 53.612 1.323 c
+53.707 1.558 53.836 1.742 53.994 1.881 c
+54.159 2.018 54.347 2.117 54.553 2.176 c
+54.758 2.242 54.967 2.278 55.184 2.278 c
+55.456 2.278 55.691 2.227 55.889 2.132 c
+56.095 2.043 56.261 1.911 56.39 1.735 c
+56.525 1.565 56.625 1.36 56.683 1.118 c
+56.75 0.882 56.787 0.617 56.787 0.324 c
+56.787 0.309 l
+54.42 0.309 l
+54.42 0.162 54.435 0.023 54.464 -0.103 c
+54.501 -0.231 54.555 -0.345 54.626 -0.441 c
+54.692 -0.529 54.776 -0.598 54.875 -0.646 c
+54.971 -0.698 55.085 -0.72 55.214 -0.72 c
+55.368 -0.72 55.507 -0.687 55.625 -0.617 c
+55.75 -0.551 55.839 -0.448 55.889 -0.309 c
+56.728 -0.382 l
+56.698 -0.481 56.643 -0.588 56.566 -0.706 c
+56.485 -0.816 56.382 -0.918 56.257 -1.014 c
+56.14 -1.103 55.985 -1.176 55.802 -1.234 c
+55.625 -1.294 55.412 -1.323 55.17 -1.323 c
+55.17 1.706 m
+55.081 1.706 54.993 1.69 54.905 1.661 c
+54.817 1.632 54.736 1.58 54.67 1.515 c
+54.6 1.444 54.541 1.357 54.493 1.249 c
+54.453 1.139 54.435 1.014 54.435 0.867 c
+55.904 0.867 l
+55.904 1.004 55.879 1.125 55.831 1.235 c
+55.791 1.341 55.735 1.43 55.669 1.5 c
+55.611 1.565 55.537 1.617 55.449 1.646 c
+55.361 1.683 55.265 1.706 55.17 1.706 c
+58.833 -1.264 m
+58.833 0.852 l
+58.833 1.018 58.826 1.154 58.819 1.264 c
+58.807 1.371 58.789 1.455 58.76 1.515 c
+58.738 1.58 58.709 1.632 58.671 1.661 c
+58.642 1.69 58.601 1.706 58.553 1.706 c
+58.495 1.706 58.44 1.675 58.393 1.617 c
+58.352 1.565 58.318 1.492 58.289 1.397 c
+58.26 1.309 58.235 1.195 58.216 1.058 c
+58.204 0.919 58.201 0.769 58.201 0.603 c
+58.201 -1.264 l
+57.452 -1.264 l
+57.452 1.47 l
+57.452 1.706 l
+57.452 1.926 l
+57.452 2.003 57.444 2.066 57.437 2.117 c
+57.437 2.22 l
+58.113 2.22 l
+58.113 2.132 l
+58.113 1.985 l
+58.121 1.926 58.127 1.867 58.127 1.808 c
+58.127 1.646 l
+58.142 1.646 l
+58.161 1.735 58.19 1.812 58.231 1.881 c
+58.268 1.959 58.312 2.028 58.363 2.087 c
+58.422 2.146 58.488 2.19 58.569 2.22 c
+58.646 2.257 58.734 2.278 58.833 2.278 c
+59.017 2.278 59.157 2.224 59.245 2.117 c
+59.34 2.018 59.411 1.86 59.451 1.646 c
+59.465 1.646 l
+59.502 1.742 59.542 1.831 59.583 1.911 c
+59.631 1.989 59.686 2.051 59.744 2.103 c
+59.804 2.161 59.87 2.205 59.951 2.234 c
+60.028 2.263 60.116 2.278 60.215 2.278 c
+60.351 2.278 60.465 2.253 60.553 2.205 c
+60.641 2.153 60.708 2.08 60.759 1.985 c
+60.818 1.885 60.855 1.756 60.876 1.602 c
+60.905 1.455 60.92 1.272 60.92 1.058 c
+60.92 -1.264 l
+60.2 -1.264 l
+60.2 0.852 l
+60.2 1.018 60.193 1.154 60.186 1.264 c
+60.174 1.371 60.157 1.455 60.126 1.515 c
+60.105 1.58 60.076 1.632 60.039 1.661 c
+60.009 1.69 59.968 1.706 59.921 1.706 c
+59.804 1.706 59.708 1.617 59.642 1.44 c
+59.583 1.272 59.553 1.014 59.553 0.661 c
+59.553 -1.264 l
+h
+64.937 0.485 m
+64.937 0.21 64.901 -0.04 64.834 -0.264 c
+64.764 -0.481 64.662 -0.669 64.525 -0.823 c
+64.386 -0.981 64.209 -1.103 63.997 -1.19 c
+63.779 -1.278 63.526 -1.323 63.232 -1.323 c
+62.956 -1.323 62.71 -1.278 62.497 -1.19 c
+62.291 -1.103 62.119 -0.981 61.982 -0.823 c
+61.843 -0.669 61.74 -0.481 61.674 -0.264 c
+61.604 -0.04 61.57 0.21 61.57 0.485 c
+61.57 0.738 61.601 0.974 61.659 1.191 c
+61.726 1.415 61.828 1.606 61.967 1.764 c
+62.104 1.929 62.28 2.058 62.497 2.146 c
+62.71 2.234 62.967 2.278 63.261 2.278 c
+63.573 2.278 63.835 2.234 64.041 2.146 c
+64.253 2.058 64.427 1.929 64.554 1.764 c
+64.691 1.606 64.79 1.415 64.849 1.191 c
+64.907 0.974 64.937 0.738 64.937 0.485 c
+63.982 0.485 m
+63.982 0.691 63.967 0.867 63.937 1.014 c
+63.916 1.162 63.879 1.283 63.82 1.382 c
+63.761 1.478 63.687 1.548 63.6 1.588 c
+63.511 1.636 63.401 1.661 63.276 1.661 c
+63.012 1.661 62.821 1.562 62.703 1.367 c
+62.586 1.18 62.526 0.886 62.526 0.485 c
+62.526 0.063 62.586 -0.243 62.703 -0.426 c
+62.821 -0.613 62.997 -0.706 63.232 -0.706 c
+63.357 -0.706 63.471 -0.687 63.57 -0.646 c
+63.665 -0.598 63.746 -0.525 63.806 -0.426 c
+63.872 -0.33 63.916 -0.206 63.937 -0.058 c
+63.967 0.088 63.982 0.268 63.982 0.485 c
+66.396 1.602 m
+65.852 1.602 l
+65.852 2.22 l
+66.44 2.22 l
+66.719 3.117 l
+67.293 3.117 l
+67.293 2.22 l
+68.527 2.22 l
+68.527 1.602 l
+67.293 1.602 l
+67.293 -0.103 l
+67.293 -0.324 l
+67.3 -0.393 67.322 -0.455 67.351 -0.515 c
+67.388 -0.565 67.443 -0.61 67.513 -0.646 c
+67.59 -0.676 67.704 -0.69 67.851 -0.69 c
+67.987 -0.69 68.123 -0.687 68.263 -0.676 c
+68.398 -0.658 68.531 -0.632 68.66 -0.602 c
+68.66 -1.205 l
+68.579 -1.216 68.502 -1.23 68.425 -1.249 c
+68.344 -1.261 68.267 -1.267 68.189 -1.278 c
+68.109 -1.286 68.02 -1.294 67.924 -1.294 c
+67.837 -1.301 67.737 -1.308 67.631 -1.308 c
+67.443 -1.308 67.282 -1.294 67.145 -1.264 c
+67.017 -1.227 66.903 -1.183 66.808 -1.132 c
+66.719 -1.084 66.646 -1.025 66.587 -0.955 c
+66.528 -0.878 66.484 -0.801 66.455 -0.72 c
+66.426 -0.632 66.403 -0.544 66.396 -0.455 c
+66.385 -0.36 66.381 -0.264 66.381 -0.176 c
+h
+71.412 -1.323 m
+71.155 -1.323 70.927 -1.286 70.721 -1.22 c
+70.515 -1.143 70.339 -1.029 70.192 -0.881 c
+70.045 -0.727 69.927 -0.536 69.84 -0.309 c
+69.759 -0.085 69.722 0.181 69.722 0.485 c
+69.722 0.816 69.765 1.095 69.854 1.323 c
+69.95 1.558 70.078 1.742 70.236 1.881 c
+70.401 2.018 70.588 2.117 70.794 2.176 c
+71 2.242 71.21 2.278 71.427 2.278 c
+71.698 2.278 71.934 2.227 72.132 2.132 c
+72.338 2.043 72.504 1.911 72.632 1.735 c
+72.768 1.565 72.867 1.36 72.926 1.118 c
+72.992 0.882 73.029 0.617 73.029 0.324 c
+73.029 0.309 l
+70.663 0.309 l
+70.663 0.162 70.677 0.023 70.706 -0.103 c
+70.743 -0.231 70.798 -0.345 70.868 -0.441 c
+70.935 -0.529 71.018 -0.598 71.118 -0.646 c
+71.213 -0.698 71.328 -0.72 71.456 -0.72 c
+71.61 -0.72 71.75 -0.687 71.868 -0.617 c
+71.993 -0.551 72.08 -0.448 72.132 -0.309 c
+72.97 -0.382 l
+72.94 -0.481 72.886 -0.588 72.809 -0.706 c
+72.728 -0.816 72.624 -0.918 72.5 -1.014 c
+72.382 -1.103 72.228 -1.176 72.044 -1.234 c
+71.868 -1.294 71.654 -1.323 71.412 -1.323 c
+71.412 1.706 m
+71.324 1.706 71.236 1.69 71.147 1.661 c
+71.059 1.632 70.978 1.58 70.912 1.515 c
+70.842 1.444 70.783 1.357 70.736 1.249 c
+70.696 1.139 70.677 1.014 70.677 0.867 c
+72.147 0.867 l
+72.147 1.004 72.121 1.125 72.074 1.235 c
+72.033 1.341 71.978 1.43 71.912 1.5 c
+71.853 1.565 71.779 1.617 71.691 1.646 c
+71.603 1.683 71.507 1.706 71.412 1.706 c
+74.285 -2.631 m
+74.285 -2.013 l
+75.359 -2.013 l
+75.359 2.896 l
+74.285 2.896 l
+74.285 3.514 l
+76.211 3.514 l
+76.211 -2.631 l
+h
+f
+Q
+q 1 0 0 1 76.2337 259.8106 cm
+0 0 m
+-1.808 0 l
+-1.808 -2.367 l
+-2.484 -2.367 l
+-2.484 2.984 l
+0.309 2.984 l
+0.309 2.41 l
+-1.808 2.41 l
+-1.808 0.573 l
+0 0.573 l
+h
+2.396 -2.44 m
+1.896 -2.44 1.514 -2.294 1.249 -1.999 c
+0.985 -1.706 0.852 -1.272 0.852 -0.69 c
+0.852 -0.22 l
+0.852 0.374 0.977 0.841 1.234 1.176 c
+1.499 1.517 1.859 1.691 2.323 1.691 c
+2.782 1.691 3.123 1.536 3.351 1.234 c
+3.586 0.941 3.707 0.478 3.719 -0.147 c
+3.719 -0.573 l
+1.499 -0.573 l
+1.499 -0.661 l
+1.499 -1.095 1.577 -1.407 1.734 -1.602 c
+1.899 -1.789 2.131 -1.881 2.425 -1.881 c
+2.62 -1.881 2.793 -1.849 2.94 -1.779 c
+3.087 -1.702 3.223 -1.584 3.351 -1.426 c
+3.69 -1.837 l
+3.403 -2.242 2.973 -2.44 2.396 -2.44 c
+2.323 1.132 m
+2.047 1.132 1.845 1.036 1.72 0.852 c
+1.591 0.665 1.517 0.374 1.499 -0.015 c
+3.072 -0.015 l
+3.072 0.073 l
+3.05 0.455 2.984 0.723 2.866 0.881 c
+2.749 1.047 2.564 1.132 2.323 1.132 c
+5.35 2.572 m
+5.35 1.617 l
+5.953 1.617 l
+5.953 1.087 l
+5.35 1.087 l
+5.35 -1.382 l
+5.35 -1.54 5.373 -1.658 5.423 -1.735 c
+5.483 -1.816 5.571 -1.852 5.689 -1.852 c
+5.776 -1.852 5.865 -1.837 5.953 -1.808 c
+5.953 -2.367 l
+5.806 -2.415 5.652 -2.44 5.497 -2.44 c
+5.24 -2.44 5.045 -2.348 4.91 -2.161 c
+4.77 -1.977 4.704 -1.716 4.704 -1.382 c
+4.704 1.087 l
+4.101 1.087 l
+4.101 1.617 l
+4.704 1.617 l
+4.704 2.572 l
+h
+8.099 -1.881 m
+8.312 -1.881 8.485 -1.819 8.613 -1.691 c
+8.75 -1.554 8.823 -1.363 8.834 -1.118 c
+9.452 -1.118 l
+9.429 -1.5 9.294 -1.819 9.04 -2.072 c
+8.783 -2.319 8.47 -2.44 8.099 -2.44 c
+7.607 -2.44 7.231 -2.29 6.967 -1.984 c
+6.71 -1.672 6.585 -1.205 6.585 -0.588 c
+6.585 -0.147 l
+6.585 0.448 6.71 0.904 6.967 1.22 c
+7.231 1.532 7.607 1.691 8.099 1.691 c
+8.5 1.691 8.819 1.558 9.055 1.294 c
+9.297 1.036 9.429 0.691 9.452 0.25 c
+8.834 0.25 l
+8.812 0.544 8.739 0.764 8.613 0.912 c
+8.496 1.058 8.323 1.132 8.099 1.132 c
+7.805 1.132 7.588 1.033 7.453 0.838 c
+7.312 0.65 7.239 0.341 7.231 -0.088 c
+7.231 -0.603 l
+7.231 -1.073 7.298 -1.407 7.437 -1.602 c
+7.584 -1.789 7.805 -1.881 8.099 -1.881 c
+10.848 1.205 m
+11.102 1.529 11.421 1.691 11.803 1.691 c
+12.509 1.691 12.866 1.22 12.876 0.279 c
+12.876 -2.367 l
+12.23 -2.367 l
+12.23 0.25 l
+12.23 0.563 12.174 0.783 12.068 0.912 c
+11.958 1.036 11.803 1.103 11.597 1.103 c
+11.439 1.103 11.293 1.047 11.156 0.941 c
+11.027 0.831 10.925 0.694 10.848 0.529 c
+10.848 -2.367 l
+10.2 -2.367 l
+10.2 3.278 l
+10.848 3.278 l
+h
+16.933 -1.881 m
+17.146 -1.881 17.319 -1.819 17.448 -1.691 c
+17.583 -1.554 17.657 -1.363 17.668 -1.118 c
+18.285 -1.118 l
+18.264 -1.5 18.127 -1.819 17.874 -2.072 c
+17.616 -2.319 17.304 -2.44 16.933 -2.44 c
+16.44 -2.44 16.066 -2.29 15.802 -1.984 c
+15.544 -1.672 15.419 -1.205 15.419 -0.588 c
+15.419 -0.147 l
+15.419 0.448 15.544 0.904 15.802 1.22 c
+16.066 1.532 16.44 1.691 16.933 1.691 c
+17.334 1.691 17.653 1.558 17.888 1.294 c
+18.131 1.036 18.264 0.691 18.285 0.25 c
+17.668 0.25 l
+17.646 0.544 17.572 0.764 17.448 0.912 c
+17.33 1.058 17.157 1.132 16.933 1.132 c
+16.639 1.132 16.422 1.033 16.286 0.838 c
+16.147 0.65 16.073 0.341 16.066 -0.088 c
+16.066 -0.603 l
+16.066 -1.073 16.132 -1.407 16.272 -1.602 c
+16.419 -1.789 16.639 -1.881 16.933 -1.881 c
+19.682 1.205 m
+19.935 1.529 20.255 1.691 20.637 1.691 c
+21.343 1.691 21.699 1.22 21.71 0.279 c
+21.71 -2.367 l
+21.063 -2.367 l
+21.063 0.25 l
+21.063 0.563 21.009 0.783 20.902 0.912 c
+20.791 1.036 20.637 1.103 20.432 1.103 c
+20.274 1.103 20.126 1.047 19.991 0.941 c
+19.862 0.831 19.759 0.694 19.682 0.529 c
+19.682 -2.367 l
+19.035 -2.367 l
+19.035 3.278 l
+19.682 3.278 l
+h
+24.709 -2.367 m
+24.668 -2.278 24.643 -2.132 24.635 -1.926 c
+24.4 -2.271 24.106 -2.44 23.754 -2.44 c
+23.39 -2.44 23.106 -2.344 22.9 -2.146 c
+22.703 -1.941 22.607 -1.654 22.607 -1.278 c
+22.607 -0.879 22.743 -0.559 23.019 -0.324 c
+23.291 -0.081 23.665 0.044 24.136 0.044 c
+24.621 0.044 l
+24.621 0.47 l
+24.621 0.706 24.566 0.871 24.459 0.97 c
+24.349 1.076 24.187 1.132 23.974 1.132 c
+23.775 1.132 23.613 1.073 23.489 0.956 c
+23.371 0.838 23.312 0.691 23.312 0.515 c
+22.665 0.515 l
+22.665 0.709 22.725 0.9 22.842 1.087 c
+22.967 1.271 23.129 1.419 23.328 1.529 c
+23.533 1.635 23.761 1.691 24.018 1.691 c
+24.419 1.691 24.724 1.587 24.93 1.382 c
+25.142 1.176 25.256 0.881 25.267 0.5 c
+25.267 -1.514 l
+25.267 -1.819 25.304 -2.084 25.385 -2.308 c
+25.385 -2.367 l
+h
+23.841 -1.852 m
+24.007 -1.852 24.157 -1.808 24.297 -1.72 c
+24.444 -1.631 24.55 -1.521 24.621 -1.382 c
+24.621 -0.441 l
+24.253 -0.441 l
+23.937 -0.441 23.694 -0.511 23.518 -0.647 c
+23.342 -0.775 23.254 -0.963 23.254 -1.205 c
+23.254 -1.434 23.297 -1.598 23.386 -1.706 c
+23.474 -1.804 23.625 -1.852 23.841 -1.852 c
+26.884 1.617 m
+26.899 1.176 l
+27.153 1.517 27.476 1.691 27.869 1.691 c
+28.575 1.691 28.932 1.22 28.942 0.279 c
+28.942 -2.367 l
+28.296 -2.367 l
+28.296 0.25 l
+28.296 0.563 28.24 0.783 28.134 0.912 c
+28.024 1.036 27.869 1.103 27.663 1.103 c
+27.505 1.103 27.359 1.047 27.222 0.941 c
+27.094 0.831 26.991 0.694 26.914 0.529 c
+26.914 -2.367 l
+26.267 -2.367 l
+26.267 1.617 l
+h
+29.78 -0.191 m
+29.78 0.426 29.89 0.889 30.118 1.205 c
+30.343 1.529 30.677 1.691 31.118 1.691 c
+31.519 1.691 31.823 1.514 32.029 1.161 c
+32.073 1.617 l
+32.662 1.617 l
+32.662 -2.411 l
+32.662 -2.899 32.533 -3.278 32.279 -3.543 c
+32.022 -3.807 31.669 -3.94 31.22 -3.94 c
+31.022 -3.94 30.802 -3.888 30.559 -3.792 c
+30.313 -3.693 30.133 -3.572 30.015 -3.425 c
+30.28 -2.984 l
+30.545 -3.248 30.842 -3.381 31.176 -3.381 c
+31.713 -3.381 31.989 -3.087 31.999 -2.499 c
+31.999 -1.97 l
+31.794 -2.286 31.492 -2.44 31.103 -2.44 c
+30.692 -2.44 30.368 -2.29 30.133 -1.984 c
+29.905 -1.672 29.788 -1.22 29.78 -0.632 c
+h
+30.441 -0.573 m
+30.441 -1.014 30.504 -1.345 30.632 -1.558 c
+30.757 -1.764 30.975 -1.866 31.28 -1.866 c
+31.602 -1.866 31.841 -1.702 31.999 -1.367 c
+31.999 0.617 l
+31.831 0.941 31.592 1.103 31.28 1.103 c
+30.985 1.103 30.769 0.999 30.632 0.794 c
+30.504 0.588 30.441 0.264 30.441 -0.177 c
+h
+35.043 -2.44 m
+34.543 -2.44 34.16 -2.294 33.896 -1.999 c
+33.631 -1.706 33.499 -1.272 33.499 -0.69 c
+33.499 -0.22 l
+33.499 0.374 33.624 0.841 33.881 1.176 c
+34.146 1.517 34.505 1.691 34.969 1.691 c
+35.428 1.691 35.77 1.536 35.997 1.234 c
+36.233 0.941 36.354 0.478 36.365 -0.147 c
+36.365 -0.573 l
+34.146 -0.573 l
+34.146 -0.661 l
+34.146 -1.095 34.223 -1.407 34.381 -1.602 c
+34.546 -1.789 34.778 -1.881 35.072 -1.881 c
+35.266 -1.881 35.44 -1.849 35.586 -1.779 c
+35.733 -1.702 35.869 -1.584 35.997 -1.426 c
+36.336 -1.837 l
+36.049 -2.242 35.619 -2.44 35.043 -2.44 c
+34.969 1.132 m
+34.694 1.132 34.491 1.036 34.366 0.852 c
+34.237 0.665 34.164 0.374 34.146 -0.015 c
+35.719 -0.015 l
+35.719 0.073 l
+35.696 0.455 35.631 0.723 35.513 0.881 c
+35.395 1.047 35.211 1.132 34.969 1.132 c
+39.085 -1.353 m
+39.085 -1.205 39.029 -1.084 38.923 -0.985 c
+38.813 -0.889 38.607 -0.771 38.306 -0.632 c
+37.96 -0.485 37.717 -0.364 37.57 -0.264 c
+37.424 -0.158 37.313 -0.04 37.247 0.088 c
+37.177 0.213 37.144 0.371 37.144 0.559 c
+37.144 0.881 37.262 1.151 37.497 1.367 c
+37.732 1.58 38.033 1.691 38.409 1.691 c
+38.79 1.691 39.099 1.577 39.334 1.352 c
+39.569 1.124 39.687 0.838 39.687 0.485 c
+39.041 0.485 l
+39.041 0.661 38.981 0.812 38.864 0.941 c
+38.746 1.066 38.592 1.132 38.409 1.132 c
+38.21 1.132 38.059 1.076 37.952 0.97 c
+37.842 0.871 37.791 0.738 37.791 0.573 c
+37.791 0.445 37.828 0.338 37.909 0.25 c
+37.986 0.169 38.177 0.066 38.482 -0.059 c
+38.96 -0.246 39.29 -0.434 39.467 -0.617 c
+39.643 -0.794 39.731 -1.022 39.731 -1.294 c
+39.731 -1.646 39.606 -1.926 39.363 -2.132 c
+39.128 -2.337 38.813 -2.44 38.423 -2.44 c
+38 -2.44 37.663 -2.323 37.409 -2.087 c
+37.152 -1.845 37.027 -1.54 37.027 -1.176 c
+37.674 -1.176 l
+37.68 -1.404 37.751 -1.58 37.879 -1.706 c
+38.004 -1.823 38.187 -1.881 38.423 -1.881 c
+38.636 -1.881 38.798 -1.833 38.908 -1.735 c
+39.026 -1.639 39.085 -1.511 39.085 -1.353 c
+42.583 -2.367 m
+42.583 1.087 l
+42.054 1.087 l
+42.054 1.617 l
+42.583 1.617 l
+42.583 2.072 l
+42.583 2.473 42.679 2.786 42.877 3.013 c
+43.083 3.237 43.361 3.351 43.715 3.351 c
+43.851 3.351 43.983 3.329 44.111 3.293 c
+44.082 2.749 l
+43.983 2.767 43.884 2.778 43.789 2.778 c
+43.413 2.778 43.23 2.514 43.23 1.984 c
+43.23 1.617 l
+43.906 1.617 l
+43.906 1.087 l
+43.23 1.087 l
+43.23 -2.367 l
+h
+46.316 0.999 m
+46.228 1.018 46.129 1.029 46.023 1.029 c
+45.688 1.029 45.453 0.845 45.317 0.485 c
+45.317 -2.367 l
+44.67 -2.367 l
+44.67 1.617 l
+45.302 1.617 l
+45.317 1.205 l
+45.493 1.529 45.736 1.691 46.052 1.691 c
+46.158 1.691 46.247 1.668 46.316 1.631 c
+h
+46.758 -0.191 m
+46.758 0.386 46.893 0.841 47.169 1.176 c
+47.452 1.517 47.823 1.691 48.286 1.691 c
+48.745 1.691 49.113 1.521 49.389 1.191 c
+49.672 0.867 49.819 0.419 49.83 -0.147 c
+49.83 -0.573 l
+49.83 -1.143 49.686 -1.598 49.403 -1.941 c
+49.127 -2.275 48.76 -2.44 48.3 -2.44 c
+47.838 -2.44 47.467 -2.278 47.184 -1.955 c
+46.908 -1.625 46.765 -1.183 46.758 -0.632 c
+h
+47.404 -0.573 m
+47.404 -0.977 47.481 -1.294 47.639 -1.529 c
+47.805 -1.764 48.026 -1.881 48.3 -1.881 c
+48.867 -1.881 49.161 -1.47 49.183 -0.647 c
+49.183 -0.191 l
+49.183 0.21 49.098 0.529 48.933 0.764 c
+48.775 1.007 48.558 1.132 48.286 1.132 c
+48.022 1.132 47.805 1.007 47.639 0.764 c
+47.481 0.529 47.404 0.21 47.404 -0.191 c
+h
+51.284 1.617 m
+51.299 1.249 l
+51.542 1.544 51.862 1.691 52.255 1.691 c
+52.696 1.691 53.004 1.492 53.181 1.103 c
+53.435 1.492 53.784 1.691 54.224 1.691 c
+54.96 1.691 55.334 1.228 55.356 0.309 c
+55.356 -2.367 l
+54.71 -2.367 l
+54.71 0.25 l
+54.71 0.544 54.654 0.757 54.548 0.897 c
+54.449 1.033 54.276 1.103 54.033 1.103 c
+53.835 1.103 53.673 1.022 53.549 0.867 c
+53.431 0.721 53.361 0.529 53.343 0.294 c
+53.343 -2.367 l
+52.681 -2.367 l
+52.681 0.279 l
+52.681 0.827 52.46 1.103 52.02 1.103 c
+51.685 1.103 51.45 0.941 51.315 0.617 c
+51.315 -2.367 l
+50.667 -2.367 l
+50.667 1.617 l
+h
+58.899 2.572 m
+58.899 1.617 l
+59.502 1.617 l
+59.502 1.087 l
+58.899 1.087 l
+58.899 -1.382 l
+58.899 -1.54 58.921 -1.658 58.972 -1.735 c
+59.031 -1.816 59.119 -1.852 59.237 -1.852 c
+59.325 -1.852 59.413 -1.837 59.502 -1.808 c
+59.502 -2.367 l
+59.354 -2.415 59.2 -2.44 59.046 -2.44 c
+58.789 -2.44 58.594 -2.348 58.458 -2.161 c
+58.318 -1.977 58.252 -1.716 58.252 -1.382 c
+58.252 1.087 l
+57.649 1.087 l
+57.649 1.617 l
+58.252 1.617 l
+58.252 2.572 l
+h
+60.913 1.205 m
+61.166 1.529 61.486 1.691 61.868 1.691 c
+62.573 1.691 62.93 1.22 62.941 0.279 c
+62.941 -2.367 l
+62.295 -2.367 l
+62.295 0.25 l
+62.295 0.563 62.239 0.783 62.133 0.912 c
+62.022 1.036 61.868 1.103 61.663 1.103 c
+61.505 1.103 61.357 1.047 61.221 0.941 c
+61.093 0.831 60.99 0.694 60.913 0.529 c
+60.913 -2.367 l
+60.266 -2.367 l
+60.266 3.278 l
+60.913 3.278 l
+h
+65.322 -2.44 m
+64.823 -2.44 64.441 -2.294 64.176 -1.999 c
+63.911 -1.706 63.779 -1.272 63.779 -0.69 c
+63.779 -0.22 l
+63.779 0.374 63.903 0.841 64.161 1.176 c
+64.426 1.517 64.786 1.691 65.249 1.691 c
+65.708 1.691 66.05 1.536 66.278 1.234 c
+66.513 0.941 66.634 0.478 66.645 -0.147 c
+66.645 -0.573 l
+64.426 -0.573 l
+64.426 -0.661 l
+64.426 -1.095 64.503 -1.407 64.661 -1.602 c
+64.826 -1.789 65.058 -1.881 65.352 -1.881 c
+65.547 -1.881 65.719 -1.849 65.866 -1.779 c
+66.014 -1.702 66.149 -1.584 66.278 -1.426 c
+66.616 -1.837 l
+66.33 -2.242 65.9 -2.44 65.322 -2.44 c
+65.249 1.132 m
+64.973 1.132 64.771 1.036 64.647 0.852 c
+64.518 0.665 64.444 0.374 64.426 -0.015 c
+65.999 -0.015 l
+65.999 0.073 l
+65.977 0.455 65.91 0.723 65.792 0.881 c
+65.675 1.047 65.491 1.132 65.249 1.132 c
+f
+Q
+q 1 0 0 1 147.2304 260.4136 cm
+0 0 m
+-0.339 0.029 l
+-0.626 0.029 -0.817 -0.096 -0.912 -0.339 c
+-0.912 -2.97 l
+-1.956 -2.97 l
+-1.956 1.014 l
+-0.985 1.014 l
+-0.956 0.573 l
+-0.79 0.914 -0.559 1.087 -0.265 1.087 c
+-0.148 1.087 -0.056 1.065 0.014 1.028 c
+h
+2.072 -3.043 m
+1.543 -3.043 1.124 -2.889 0.822 -2.573 c
+0.529 -2.249 0.382 -1.79 0.382 -1.191 c
+0.382 -0.882 l
+0.382 -0.258 0.517 0.228 0.793 0.573 c
+1.065 0.914 1.458 1.087 1.969 1.087 c
+2.468 1.087 2.84 0.926 3.087 0.602 c
+3.34 0.278 3.472 -0.199 3.484 -0.823 c
+3.484 -1.324 l
+1.411 -1.324 l
+1.429 -1.617 1.491 -1.834 1.602 -1.97 c
+1.72 -2.11 1.899 -2.176 2.146 -2.176 c
+2.487 -2.176 2.778 -2.058 3.013 -1.823 c
+3.424 -2.455 l
+3.295 -2.631 3.108 -2.775 2.865 -2.882 c
+2.62 -2.988 2.355 -3.043 2.072 -3.043 c
+1.425 -0.603 m
+2.454 -0.603 l
+2.454 -0.5 l
+2.454 -0.265 2.414 -0.088 2.337 0.029 c
+2.267 0.154 2.138 0.22 1.955 0.22 c
+1.778 0.22 1.646 0.151 1.558 0.014 c
+1.477 -0.115 1.433 -0.32 1.425 -0.603 c
+5.041 1.014 m
+5.071 0.646 l
+5.306 0.941 5.614 1.087 5.996 1.087 c
+6.397 1.087 6.676 0.903 6.834 0.544 c
+7.07 0.903 7.397 1.087 7.819 1.087 c
+8.514 1.087 8.866 0.602 8.878 -0.368 c
+8.878 -2.97 l
+7.849 -2.97 l
+7.849 -0.427 l
+7.849 -0.202 7.812 -0.04 7.746 0.058 c
+7.687 0.154 7.577 0.205 7.422 0.205 c
+7.224 0.205 7.085 0.087 6.996 -0.148 c
+6.996 -2.97 l
+5.953 -2.97 l
+5.953 -0.441 l
+5.953 -0.206 5.923 -0.04 5.865 0.058 c
+5.805 0.154 5.695 0.205 5.541 0.205 c
+5.365 0.205 5.221 0.11 5.115 -0.074 c
+5.115 -2.97 l
+4.072 -2.97 l
+4.072 1.014 l
+h
+9.539 -0.853 m
+9.539 -0.246 9.679 0.228 9.965 0.573 c
+10.248 0.914 10.642 1.087 11.141 1.087 c
+11.648 1.087 12.045 0.914 12.332 0.573 c
+12.615 0.228 12.758 -0.246 12.758 -0.853 c
+12.758 -1.118 l
+12.758 -1.717 12.615 -2.187 12.332 -2.529 c
+12.045 -2.874 11.648 -3.043 11.141 -3.043 c
+10.63 -3.043 10.233 -2.874 9.951 -2.529 c
+9.676 -2.187 9.539 -1.713 9.539 -1.103 c
+h
+10.583 -1.118 m
+10.583 -1.823 10.767 -2.176 11.141 -2.176 c
+11.494 -2.176 11.685 -1.882 11.715 -1.294 c
+11.715 -0.853 l
+11.715 -0.493 11.663 -0.221 11.567 -0.044 c
+11.469 0.132 11.326 0.22 11.141 0.22 c
+10.965 0.22 10.825 0.132 10.73 -0.044 c
+10.63 -0.221 10.583 -0.493 10.583 -0.853 c
+h
+14.611 1.984 m
+14.611 1.014 l
+15.139 1.014 l
+15.139 0.22 l
+14.611 0.22 l
+14.611 -1.75 l
+14.611 -1.908 14.629 -2.014 14.669 -2.073 c
+14.717 -2.132 14.802 -2.161 14.919 -2.161 c
+15.025 -2.161 15.11 -2.154 15.168 -2.132 c
+15.168 -2.94 l
+14.993 -3.007 14.802 -3.043 14.596 -3.043 c
+13.919 -3.043 13.574 -2.658 13.566 -1.882 c
+13.566 0.22 l
+13.111 0.22 l
+13.111 1.014 l
+13.566 1.014 l
+13.566 1.984 l
+h
+17.315 -3.043 m
+16.786 -3.043 16.367 -2.889 16.066 -2.573 c
+15.771 -2.249 15.625 -1.79 15.625 -1.191 c
+15.625 -0.882 l
+15.625 -0.258 15.76 0.228 16.036 0.573 c
+16.308 0.914 16.701 1.087 17.212 1.087 c
+17.712 1.087 18.083 0.926 18.329 0.602 c
+18.582 0.278 18.715 -0.199 18.726 -0.823 c
+18.726 -1.324 l
+16.654 -1.324 l
+16.672 -1.617 16.735 -1.834 16.845 -1.97 c
+16.962 -2.11 17.142 -2.176 17.389 -2.176 c
+17.73 -2.176 18.021 -2.058 18.256 -1.823 c
+18.667 -2.455 l
+18.539 -2.631 18.351 -2.775 18.109 -2.882 c
+17.863 -2.988 17.598 -3.043 17.315 -3.043 c
+16.668 -0.603 m
+17.697 -0.603 l
+17.697 -0.5 l
+17.697 -0.265 17.657 -0.088 17.58 0.029 c
+17.51 0.154 17.381 0.22 17.198 0.22 c
+17.021 0.22 16.889 0.151 16.801 0.014 c
+16.72 -0.115 16.676 -0.32 16.668 -0.603 c
+f
+Q
+q 1 0 0 1 166.6623 256.3712 cm
+0 0 m
+-0.397 0.264 l
+-0.162 0.588 -0.04 0.922 -0.03 1.263 c
+-0.03 1.881 l
+0.632 1.881 l
+0.632 1.352 l
+0.632 1.095 0.565 0.849 0.44 0.602 c
+0.324 0.359 0.176 0.158 0 0 c
+6.071 2.866 m
+6.071 2.248 5.957 1.782 5.733 1.469 c
+5.516 1.153 5.192 0.999 4.762 0.999 c
+4.34 0.999 4.027 1.18 3.821 1.543 c
+3.792 1.072 l
+3.189 1.072 l
+3.189 6.717 l
+3.836 6.717 l
+3.836 4.615 l
+4.049 4.957 4.358 5.13 4.762 5.13 c
+5.192 5.13 5.516 4.972 5.733 4.659 c
+5.957 4.355 6.071 3.887 6.071 3.262 c
+h
+5.423 3.248 m
+5.423 3.719 5.354 4.049 5.218 4.247 c
+5.089 4.442 4.88 4.542 4.586 4.542 c
+4.251 4.542 4.002 4.358 3.836 3.998 c
+3.836 2.117 l
+4.002 1.753 4.255 1.573 4.6 1.573 c
+4.895 1.573 5.104 1.675 5.232 1.881 c
+5.358 2.087 5.423 2.403 5.423 2.836 c
+h
+8.937 1.425 m
+8.72 1.139 8.407 0.999 7.996 0.999 c
+7.632 0.999 7.357 1.12 7.173 1.367 c
+6.996 1.62 6.901 1.984 6.894 2.454 c
+6.894 5.056 l
+7.54 5.056 l
+7.54 2.514 l
+7.54 1.885 7.724 1.573 8.099 1.573 c
+8.5 1.573 8.775 1.749 8.922 2.102 c
+8.922 5.056 l
+9.569 5.056 l
+9.569 1.072 l
+8.951 1.072 l
+h
+11.406 6.011 m
+11.406 5.056 l
+12.009 5.056 l
+12.009 4.527 l
+11.406 4.527 l
+11.406 2.057 l
+11.406 1.899 11.428 1.782 11.48 1.705 c
+11.538 1.624 11.627 1.587 11.744 1.587 c
+11.833 1.587 11.921 1.602 12.009 1.631 c
+12.009 1.072 l
+11.862 1.024 11.708 0.999 11.553 0.999 c
+11.296 0.999 11.102 1.091 10.965 1.278 c
+10.826 1.462 10.759 1.723 10.759 2.057 c
+10.759 4.527 l
+10.157 4.527 l
+10.157 5.056 l
+10.759 5.056 l
+10.759 6.011 l
+h
+15.11 5.056 m
+15.125 4.615 l
+15.378 4.957 15.702 5.13 16.095 5.13 c
+16.801 5.13 17.157 4.659 17.169 3.719 c
+17.169 1.072 l
+16.521 1.072 l
+16.521 3.69 l
+16.521 4.002 16.467 4.222 16.359 4.351 c
+16.249 4.475 16.095 4.542 15.889 4.542 c
+15.731 4.542 15.584 4.486 15.449 4.38 c
+15.32 4.27 15.217 4.134 15.14 3.968 c
+15.14 1.072 l
+14.493 1.072 l
+14.493 5.056 l
+h
+18.006 3.248 m
+18.006 3.825 18.142 4.28 18.418 4.615 c
+18.701 4.957 19.072 5.13 19.534 5.13 c
+19.994 5.13 20.361 4.961 20.637 4.63 c
+20.92 4.307 21.067 3.858 21.078 3.293 c
+21.078 2.866 l
+21.078 2.296 20.935 1.841 20.652 1.499 c
+20.376 1.165 20.009 0.999 19.55 0.999 c
+19.087 0.999 18.715 1.161 18.433 1.484 c
+18.157 1.815 18.013 2.256 18.006 2.807 c
+h
+18.653 2.866 m
+18.653 2.462 18.73 2.146 18.888 1.911 c
+19.054 1.675 19.274 1.558 19.55 1.558 c
+20.116 1.558 20.409 1.969 20.431 2.792 c
+20.431 3.248 l
+20.431 3.649 20.347 3.968 20.182 4.203 c
+20.024 4.446 19.806 4.571 19.534 4.571 c
+19.27 4.571 19.054 4.446 18.888 4.203 c
+18.73 3.968 18.653 3.649 18.653 3.248 c
+h
+22.769 6.011 m
+22.769 5.056 l
+23.372 5.056 l
+23.372 4.527 l
+22.769 4.527 l
+22.769 2.057 l
+22.769 1.899 22.79 1.782 22.842 1.705 c
+22.901 1.624 22.989 1.587 23.106 1.587 c
+23.195 1.587 23.283 1.602 23.372 1.631 c
+23.372 1.072 l
+23.224 1.024 23.07 0.999 22.915 0.999 c
+22.659 0.999 22.464 1.091 22.328 1.278 c
+22.188 1.462 22.121 1.723 22.121 2.057 c
+22.121 4.527 l
+21.519 4.527 l
+21.519 5.056 l
+22.121 5.056 l
+22.121 6.011 l
+h
+27.884 1.425 m
+27.667 1.139 27.355 0.999 26.943 0.999 c
+26.58 0.999 26.304 1.12 26.12 1.367 c
+25.944 1.62 25.848 1.984 25.84 2.454 c
+25.84 5.056 l
+26.487 5.056 l
+26.487 2.514 l
+26.487 1.885 26.671 1.573 27.046 1.573 c
+27.447 1.573 27.722 1.749 27.869 2.102 c
+27.869 5.056 l
+28.516 5.056 l
+28.516 1.072 l
+27.899 1.072 l
+h
+32.382 2.866 m
+32.382 2.238 32.265 1.768 32.029 1.455 c
+31.801 1.15 31.485 0.999 31.074 0.999 c
+30.669 0.999 30.361 1.15 30.148 1.455 c
+30.148 -0.456 l
+29.501 -0.456 l
+29.501 5.056 l
+30.089 5.056 l
+30.133 4.615 l
+30.346 4.957 30.655 5.13 31.059 5.13 c
+31.5 5.13 31.827 4.976 32.043 4.674 c
+32.257 4.369 32.371 3.913 32.382 3.307 c
+h
+31.735 3.248 m
+31.735 3.69 31.665 4.012 31.529 4.218 c
+31.39 4.432 31.17 4.542 30.868 4.542 c
+30.551 4.542 30.313 4.388 30.148 4.086 c
+30.148 2.013 l
+30.313 1.708 30.551 1.558 30.868 1.558 c
+31.162 1.558 31.375 1.66 31.515 1.866 c
+31.65 2.08 31.724 2.41 31.735 2.851 c
+h
+33.088 3.248 m
+33.088 3.854 33.198 4.321 33.425 4.644 c
+33.661 4.968 33.988 5.13 34.41 5.13 c
+34.792 5.13 35.091 4.972 35.307 4.659 c
+35.307 6.717 l
+35.953 6.717 l
+35.953 1.072 l
+35.366 1.072 l
+35.322 1.499 l
+35.116 1.165 34.811 0.999 34.41 0.999 c
+33.998 0.999 33.676 1.153 33.44 1.469 c
+33.205 1.793 33.088 2.248 33.088 2.836 c
+h
+33.734 2.866 m
+33.734 2.425 33.797 2.094 33.925 1.881 c
+34.061 1.675 34.281 1.573 34.586 1.573 c
+34.91 1.573 35.149 1.734 35.307 2.057 c
+35.307 4.072 l
+35.138 4.384 34.899 4.542 34.586 4.542 c
+34.281 4.542 34.061 4.438 33.925 4.233 c
+33.797 4.027 33.734 3.704 33.734 3.262 c
+h
+38.996 1.072 m
+38.956 1.161 38.931 1.308 38.923 1.514 c
+38.688 1.168 38.393 0.999 38.041 0.999 c
+37.678 0.999 37.394 1.095 37.188 1.294 c
+36.99 1.499 36.894 1.786 36.894 2.161 c
+36.894 2.561 37.03 2.881 37.306 3.116 c
+37.578 3.358 37.953 3.484 38.424 3.484 c
+38.908 3.484 l
+38.908 3.91 l
+38.908 4.145 38.853 4.31 38.746 4.409 c
+38.636 4.516 38.474 4.571 38.262 4.571 c
+38.063 4.571 37.901 4.513 37.776 4.395 c
+37.659 4.278 37.599 4.13 37.599 3.954 c
+36.953 3.954 l
+36.953 4.149 37.012 4.34 37.129 4.527 c
+37.254 4.71 37.416 4.858 37.615 4.968 c
+37.821 5.074 38.048 5.13 38.305 5.13 c
+38.706 5.13 39.011 5.026 39.217 4.821 c
+39.43 4.615 39.544 4.321 39.555 3.939 c
+39.555 1.926 l
+39.555 1.62 39.592 1.356 39.673 1.132 c
+39.673 1.072 l
+h
+38.129 1.587 m
+38.295 1.587 38.445 1.631 38.584 1.72 c
+38.732 1.808 38.838 1.918 38.908 2.057 c
+38.908 2.998 l
+38.54 2.998 l
+38.225 2.998 37.982 2.928 37.806 2.792 c
+37.63 2.664 37.541 2.476 37.541 2.234 c
+37.541 2.006 37.585 1.841 37.674 1.734 c
+37.761 1.635 37.913 1.587 38.129 1.587 c
+41.407 6.011 m
+41.407 5.056 l
+42.01 5.056 l
+42.01 4.527 l
+41.407 4.527 l
+41.407 2.057 l
+41.407 1.899 41.429 1.782 41.481 1.705 c
+41.539 1.624 41.628 1.587 41.745 1.587 c
+41.833 1.587 41.921 1.602 42.01 1.631 c
+42.01 1.072 l
+41.863 1.024 41.709 0.999 41.554 0.999 c
+41.297 0.999 41.102 1.091 40.966 1.278 c
+40.826 1.462 40.76 1.723 40.76 2.057 c
+40.76 4.527 l
+40.157 4.527 l
+40.157 5.056 l
+40.76 5.056 l
+40.76 6.011 l
+h
+44.2 0.999 m
+43.7 0.999 43.318 1.146 43.053 1.44 c
+42.789 1.734 42.657 2.167 42.657 2.749 c
+42.657 3.219 l
+42.657 3.814 42.781 4.28 43.039 4.615 c
+43.303 4.957 43.663 5.13 44.126 5.13 c
+44.585 5.13 44.928 4.976 45.156 4.674 c
+45.391 4.38 45.512 3.917 45.522 3.293 c
+45.522 2.866 l
+43.303 2.866 l
+43.303 2.778 l
+43.303 2.344 43.38 2.032 43.538 1.837 c
+43.704 1.65 43.935 1.558 44.229 1.558 c
+44.424 1.558 44.597 1.591 44.743 1.66 c
+44.89 1.738 45.027 1.855 45.156 2.013 c
+45.493 1.602 l
+45.206 1.198 44.776 0.999 44.2 0.999 c
+44.126 4.571 m
+43.851 4.571 43.648 4.475 43.523 4.292 c
+43.395 4.104 43.322 3.814 43.303 3.424 c
+44.876 3.424 l
+44.876 3.513 l
+44.854 3.895 44.788 4.163 44.67 4.321 c
+44.552 4.486 44.369 4.571 44.126 4.571 c
+48.874 6.011 m
+48.874 5.056 l
+49.477 5.056 l
+49.477 4.527 l
+48.874 4.527 l
+48.874 2.057 l
+48.874 1.899 48.896 1.782 48.948 1.705 c
+49.007 1.624 49.094 1.587 49.212 1.587 c
+49.3 1.587 49.389 1.602 49.477 1.631 c
+49.477 1.072 l
+49.33 1.024 49.175 0.999 49.021 0.999 c
+48.764 0.999 48.569 1.091 48.433 1.278 c
+48.294 1.462 48.227 1.723 48.227 2.057 c
+48.227 4.527 l
+47.625 4.527 l
+47.625 5.056 l
+48.227 5.056 l
+48.227 6.011 l
+h
+51.887 4.438 m
+51.799 4.457 51.7 4.469 51.594 4.469 c
+51.259 4.469 51.024 4.284 50.888 3.925 c
+50.888 1.072 l
+50.241 1.072 l
+50.241 5.056 l
+50.873 5.056 l
+50.888 4.644 l
+51.064 4.968 51.307 5.13 51.623 5.13 c
+51.729 5.13 51.818 5.107 51.887 5.071 c
+h
+54.43 1.072 m
+54.39 1.161 54.364 1.308 54.357 1.514 c
+54.122 1.168 53.827 0.999 53.475 0.999 c
+53.111 0.999 52.828 1.095 52.622 1.294 c
+52.424 1.499 52.329 1.786 52.329 2.161 c
+52.329 2.561 52.464 2.881 52.74 3.116 c
+53.012 3.358 53.387 3.484 53.857 3.484 c
+54.342 3.484 l
+54.342 3.91 l
+54.342 4.145 54.287 4.31 54.181 4.409 c
+54.07 4.516 53.908 4.571 53.696 4.571 c
+53.497 4.571 53.335 4.513 53.21 4.395 c
+53.093 4.278 53.034 4.13 53.034 3.954 c
+52.387 3.954 l
+52.387 4.149 52.446 4.34 52.564 4.527 c
+52.689 4.71 52.85 4.858 53.048 4.968 c
+53.254 5.074 53.482 5.13 53.74 5.13 c
+54.141 5.13 54.445 5.026 54.651 4.821 c
+54.864 4.615 54.978 4.321 54.989 3.939 c
+54.989 1.926 l
+54.989 1.62 55.026 1.356 55.107 1.132 c
+55.107 1.072 l
+h
+53.563 1.587 m
+53.728 1.587 53.879 1.631 54.019 1.72 c
+54.166 1.808 54.272 1.918 54.342 2.057 c
+54.342 2.998 l
+53.975 2.998 l
+53.659 2.998 53.416 2.928 53.24 2.792 c
+53.063 2.664 52.975 2.476 52.975 2.234 c
+52.975 2.006 53.019 1.841 53.108 1.734 c
+53.196 1.635 53.347 1.587 53.563 1.587 c
+57.37 1.558 m
+57.584 1.558 57.756 1.62 57.885 1.749 c
+58.021 1.885 58.094 2.076 58.105 2.322 c
+58.723 2.322 l
+58.7 1.94 58.565 1.62 58.311 1.367 c
+58.054 1.12 57.742 0.999 57.37 0.999 c
+56.878 0.999 56.503 1.15 56.238 1.455 c
+55.982 1.768 55.856 2.234 55.856 2.851 c
+55.856 3.293 l
+55.856 3.887 55.982 4.343 56.238 4.659 c
+56.503 4.972 56.878 5.13 57.37 5.13 c
+57.771 5.13 58.091 4.997 58.326 4.733 c
+58.569 4.475 58.7 4.13 58.723 3.69 c
+58.105 3.69 l
+58.083 3.983 58.01 4.203 57.885 4.351 c
+57.767 4.498 57.594 4.571 57.37 4.571 c
+57.077 4.571 56.859 4.472 56.724 4.278 c
+56.584 4.089 56.51 3.781 56.503 3.351 c
+56.503 2.836 l
+56.503 2.366 56.57 2.032 56.709 1.837 c
+56.855 1.65 57.077 1.558 57.37 1.558 c
+60.472 2.896 m
+60.134 2.499 l
+60.134 1.072 l
+59.472 1.072 l
+59.472 6.717 l
+60.134 6.717 l
+60.134 3.337 l
+61.368 5.056 l
+62.147 5.056 l
+60.883 3.395 l
+62.309 1.072 l
+61.559 1.072 l
+h
+63.573 1.072 -0.647 3.984 re
+63.618 6.1 m
+63.618 5.99 63.588 5.897 63.529 5.82 c
+63.47 5.751 63.375 5.718 63.25 5.718 c
+63.132 5.718 63.037 5.751 62.97 5.82 c
+62.912 5.897 62.883 5.99 62.883 6.1 c
+62.883 6.217 62.912 6.31 62.97 6.379 c
+63.037 6.456 63.132 6.497 63.25 6.497 c
+63.375 6.497 63.47 6.456 63.529 6.379 c
+63.588 6.298 63.618 6.206 63.618 6.1 c
+65.205 5.056 m
+65.22 4.615 l
+65.473 4.957 65.796 5.13 66.189 5.13 c
+66.895 5.13 67.252 4.659 67.263 3.719 c
+67.263 1.072 l
+66.616 1.072 l
+66.616 3.69 l
+66.616 4.002 66.561 4.222 66.454 4.351 c
+66.344 4.475 66.189 4.542 65.983 4.542 c
+65.825 4.542 65.679 4.486 65.543 4.38 c
+65.414 4.27 65.312 4.134 65.235 3.968 c
+65.235 1.072 l
+64.587 1.072 l
+64.587 5.056 l
+h
+68.1 3.248 m
+68.1 3.865 68.211 4.328 68.439 4.644 c
+68.663 4.968 68.998 5.13 69.438 5.13 c
+69.839 5.13 70.143 4.953 70.349 4.6 c
+70.394 5.056 l
+70.982 5.056 l
+70.982 1.028 l
+70.982 0.54 70.853 0.162 70.6 -0.103 c
+70.342 -0.368 69.989 -0.5 69.541 -0.5 c
+69.343 -0.5 69.122 -0.449 68.879 -0.353 c
+68.634 -0.254 68.453 -0.133 68.335 0.014 c
+68.601 0.455 l
+68.865 0.191 69.162 0.058 69.497 0.058 c
+70.033 0.058 70.309 0.353 70.32 0.941 c
+70.32 1.469 l
+70.114 1.153 69.813 0.999 69.424 0.999 c
+69.012 0.999 68.688 1.15 68.453 1.455 c
+68.225 1.768 68.108 2.219 68.1 2.807 c
+h
+68.762 2.866 m
+68.762 2.425 68.825 2.094 68.953 1.881 c
+69.078 1.675 69.295 1.573 69.6 1.573 c
+69.923 1.573 70.162 1.738 70.32 2.072 c
+70.32 4.056 l
+70.151 4.38 69.912 4.542 69.6 4.542 c
+69.306 4.542 69.089 4.438 68.953 4.233 c
+68.825 4.027 68.762 3.704 68.762 3.262 c
+h
+76.538 2.866 m
+76.538 2.248 76.424 1.782 76.2 1.469 c
+75.983 1.153 75.66 0.999 75.229 0.999 c
+74.807 0.999 74.494 1.18 74.288 1.543 c
+74.259 1.072 l
+73.657 1.072 l
+73.657 6.717 l
+74.303 6.717 l
+74.303 4.615 l
+74.517 4.957 74.825 5.13 75.229 5.13 c
+75.66 5.13 75.983 4.972 76.2 4.659 c
+76.424 4.355 76.538 3.887 76.538 3.262 c
+h
+75.891 3.248 m
+75.891 3.719 75.821 4.049 75.685 4.247 c
+75.556 4.442 75.348 4.542 75.053 4.542 c
+74.719 4.542 74.469 4.358 74.303 3.998 c
+74.303 2.117 l
+74.469 1.753 74.722 1.573 75.068 1.573 c
+75.362 1.573 75.571 1.675 75.699 1.881 c
+75.825 2.087 75.891 2.403 75.891 2.836 c
+h
+79.022 4.438 m
+78.934 4.457 78.835 4.469 78.728 4.469 c
+78.394 4.469 78.159 4.284 78.022 3.925 c
+78.022 1.072 l
+77.376 1.072 l
+77.376 5.056 l
+78.008 5.056 l
+78.022 4.644 l
+78.199 4.968 78.442 5.13 78.757 5.13 c
+78.864 5.13 78.953 5.107 79.022 5.071 c
+h
+81.565 1.072 m
+81.524 1.161 81.499 1.308 81.492 1.514 c
+81.256 1.168 80.962 0.999 80.609 0.999 c
+80.246 0.999 79.963 1.095 79.757 1.294 c
+79.558 1.499 79.462 1.786 79.462 2.161 c
+79.462 2.561 79.599 2.881 79.874 3.116 c
+80.146 3.358 80.522 3.484 80.992 3.484 c
+81.477 3.484 l
+81.477 3.91 l
+81.477 4.145 81.422 4.31 81.315 4.409 c
+81.205 4.516 81.043 4.571 80.83 4.571 c
+80.632 4.571 80.47 4.513 80.345 4.395 c
+80.227 4.278 80.168 4.13 80.168 3.954 c
+79.522 3.954 l
+79.522 4.149 79.581 4.34 79.698 4.527 c
+79.823 4.71 79.984 4.858 80.183 4.968 c
+80.389 5.074 80.617 5.13 80.873 5.13 c
+81.274 5.13 81.579 5.026 81.785 4.821 c
+81.999 4.615 82.112 4.321 82.124 3.939 c
+82.124 1.926 l
+82.124 1.62 82.16 1.356 82.241 1.132 c
+82.241 1.072 l
+h
+80.698 1.587 m
+80.863 1.587 81.014 1.631 81.153 1.72 c
+81.301 1.808 81.407 1.918 81.477 2.057 c
+81.477 2.998 l
+81.109 2.998 l
+80.794 2.998 80.551 2.928 80.374 2.792 c
+80.198 2.664 80.11 2.476 80.11 2.234 c
+80.11 2.006 80.154 1.841 80.242 1.734 c
+80.33 1.635 80.481 1.587 80.698 1.587 c
+83.74 5.056 m
+83.755 4.615 l
+84.009 4.957 84.332 5.13 84.725 5.13 c
+85.43 5.13 85.787 4.659 85.798 3.719 c
+85.798 1.072 l
+85.151 1.072 l
+85.151 3.69 l
+85.151 4.002 85.096 4.222 84.99 4.351 c
+84.879 4.475 84.725 4.542 84.52 4.542 c
+84.362 4.542 84.214 4.486 84.079 4.38 c
+83.95 4.27 83.847 4.134 83.77 3.968 c
+83.77 1.072 l
+83.123 1.072 l
+83.123 5.056 l
+h
+88.15 1.558 m
+88.363 1.558 88.536 1.62 88.665 1.749 c
+88.8 1.885 88.874 2.076 88.885 2.322 c
+89.502 2.322 l
+89.48 1.94 89.344 1.62 89.091 1.367 c
+88.833 1.12 88.521 0.999 88.15 0.999 c
+87.657 0.999 87.283 1.15 87.018 1.455 c
+86.761 1.768 86.636 2.234 86.636 2.851 c
+86.636 3.293 l
+86.636 3.887 86.761 4.343 87.018 4.659 c
+87.283 4.972 87.657 5.13 88.15 5.13 c
+88.551 5.13 88.87 4.997 89.105 4.733 c
+89.348 4.475 89.48 4.13 89.502 3.69 c
+88.885 3.69 l
+88.863 3.983 88.79 4.203 88.665 4.351 c
+88.547 4.498 88.374 4.571 88.15 4.571 c
+87.856 4.571 87.639 4.472 87.503 4.278 c
+87.364 4.089 87.29 3.781 87.283 3.351 c
+87.283 2.836 l
+87.283 2.366 87.349 2.032 87.489 1.837 c
+87.636 1.65 87.856 1.558 88.15 1.558 c
+90.899 4.644 m
+91.152 4.968 91.472 5.13 91.854 5.13 c
+92.559 5.13 92.916 4.659 92.927 3.719 c
+92.927 1.072 l
+92.281 1.072 l
+92.281 3.69 l
+92.281 4.002 92.225 4.222 92.119 4.351 c
+92.008 4.475 91.854 4.542 91.649 4.542 c
+91.491 4.542 91.343 4.486 91.208 4.38 c
+91.079 4.27 90.976 4.134 90.899 3.968 c
+90.899 1.072 l
+90.252 1.072 l
+90.252 6.717 l
+90.899 6.717 l
+h
+95.308 0.999 m
+94.809 0.999 94.427 1.146 94.162 1.44 c
+93.897 1.734 93.765 2.167 93.765 2.749 c
+93.765 3.219 l
+93.765 3.814 93.89 4.28 94.147 4.615 c
+94.412 4.957 94.772 5.13 95.235 5.13 c
+95.694 5.13 96.036 4.976 96.264 4.674 c
+96.499 4.38 96.621 3.917 96.631 3.293 c
+96.631 2.866 l
+94.412 2.866 l
+94.412 2.778 l
+94.412 2.344 94.489 2.032 94.647 1.837 c
+94.812 1.65 95.044 1.558 95.338 1.558 c
+95.533 1.558 95.705 1.591 95.852 1.66 c
+96 1.738 96.135 1.855 96.264 2.013 c
+96.602 1.602 l
+96.316 1.198 95.886 0.999 95.308 0.999 c
+95.235 4.571 m
+94.959 4.571 94.757 4.475 94.633 4.292 c
+94.504 4.104 94.43 3.814 94.412 3.424 c
+95.985 3.424 l
+95.985 3.513 l
+95.963 3.895 95.896 4.163 95.779 4.321 c
+95.661 4.486 95.478 4.571 95.235 4.571 c
+99.351 2.087 m
+99.351 2.234 99.296 2.356 99.189 2.454 c
+99.079 2.55 98.873 2.668 98.572 2.807 c
+98.226 2.954 97.984 3.075 97.837 3.175 c
+97.69 3.281 97.579 3.399 97.513 3.528 c
+97.444 3.652 97.411 3.81 97.411 3.998 c
+97.411 4.321 97.528 4.59 97.764 4.806 c
+97.999 5.02 98.3 5.13 98.674 5.13 c
+99.057 5.13 99.366 5.016 99.601 4.791 c
+99.836 4.564 99.954 4.278 99.954 3.925 c
+99.306 3.925 l
+99.306 4.101 99.248 4.251 99.13 4.38 c
+99.013 4.505 98.859 4.571 98.674 4.571 c
+98.476 4.571 98.325 4.516 98.219 4.409 c
+98.109 4.31 98.057 4.178 98.057 4.012 c
+98.057 3.884 98.094 3.777 98.175 3.69 c
+98.252 3.609 98.443 3.505 98.748 3.38 c
+99.225 3.193 99.557 3.006 99.733 2.822 c
+99.909 2.645 99.998 2.418 99.998 2.146 c
+99.998 1.793 99.873 1.514 99.63 1.308 c
+99.395 1.103 99.079 0.999 98.689 0.999 c
+98.267 0.999 97.928 1.117 97.675 1.352 c
+97.417 1.595 97.293 1.899 97.293 2.263 c
+97.939 2.263 l
+97.947 2.036 98.017 1.859 98.145 1.734 c
+98.271 1.616 98.454 1.558 98.689 1.558 c
+98.903 1.558 99.064 1.606 99.175 1.705 c
+99.292 1.801 99.351 1.929 99.351 2.087 c
+100.879 1.425 m
+100.879 1.543 100.912 1.639 100.983 1.72 c
+101.049 1.797 101.151 1.837 101.291 1.837 c
+101.438 1.837 101.544 1.797 101.614 1.72 c
+101.691 1.639 101.732 1.543 101.732 1.425 c
+101.732 1.315 101.691 1.223 101.614 1.146 c
+101.544 1.069 101.438 1.028 101.291 1.028 c
+101.151 1.028 101.049 1.069 100.983 1.146 c
+100.912 1.223 100.879 1.315 100.879 1.425 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 250.33 238.665 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 243.4906 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.485 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.485 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.485 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.559 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.69 15.056 -0.69 c
+15.192 -0.69 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.264 c
+14.221 -1.227 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+22.765 1.602 m
+22.765 -1.264 l
+21.868 -1.264 l
+21.868 1.602 l
+21.045 1.602 l
+21.045 2.22 l
+21.868 2.22 l
+21.868 2.485 l
+21.868 2.61 21.883 2.741 21.913 2.882 c
+21.949 3.017 22.02 3.135 22.119 3.234 c
+22.225 3.341 22.369 3.429 22.545 3.499 c
+22.722 3.564 22.946 3.601 23.221 3.601 c
+23.434 3.601 23.632 3.591 23.809 3.572 c
+23.985 3.55 24.137 3.532 24.264 3.514 c
+24.264 2.926 l
+24.137 2.944 23.993 2.959 23.838 2.969 c
+23.68 2.977 23.53 2.984 23.383 2.984 c
+23.254 2.984 23.152 2.969 23.074 2.94 c
+22.994 2.911 22.931 2.87 22.883 2.822 c
+22.832 2.771 22.799 2.708 22.78 2.631 c
+22.769 2.562 22.765 2.485 22.765 2.396 c
+22.765 2.22 l
+24.191 2.22 l
+24.191 1.602 l
+h
+26.738 -1.323 m
+26.481 -1.323 26.253 -1.286 26.047 -1.22 c
+25.841 -1.143 25.665 -1.029 25.518 -0.881 c
+25.371 -0.727 25.253 -0.536 25.165 -0.309 c
+25.084 -0.085 25.047 0.181 25.047 0.485 c
+25.047 0.816 25.091 1.095 25.18 1.323 c
+25.275 1.558 25.404 1.742 25.562 1.881 c
+25.727 2.018 25.915 2.117 26.121 2.176 c
+26.327 2.242 26.535 2.278 26.753 2.278 c
+27.025 2.278 27.26 2.227 27.458 2.132 c
+27.664 2.043 27.829 1.911 27.958 1.735 c
+28.094 1.565 28.193 1.36 28.252 1.118 c
+28.318 0.882 28.355 0.617 28.355 0.324 c
+28.355 0.309 l
+25.988 0.309 l
+25.988 0.162 26.003 0.023 26.032 -0.103 c
+26.069 -0.231 26.124 -0.345 26.194 -0.441 c
+26.26 -0.529 26.344 -0.598 26.444 -0.646 c
+26.539 -0.698 26.653 -0.72 26.782 -0.72 c
+26.936 -0.72 27.076 -0.687 27.194 -0.617 c
+27.318 -0.551 27.407 -0.448 27.458 -0.309 c
+28.296 -0.382 l
+28.266 -0.481 28.212 -0.588 28.135 -0.706 c
+28.054 -0.816 27.95 -0.918 27.825 -1.014 c
+27.708 -1.103 27.553 -1.176 27.37 -1.234 c
+27.194 -1.294 26.98 -1.323 26.738 -1.323 c
+26.738 1.706 m
+26.649 1.706 26.562 1.691 26.473 1.661 c
+26.385 1.632 26.304 1.58 26.238 1.515 c
+26.169 1.444 26.109 1.357 26.061 1.249 c
+26.021 1.139 26.003 1.014 26.003 0.867 c
+27.472 0.867 l
+27.472 1.004 27.447 1.125 27.399 1.235 c
+27.359 1.341 27.304 1.43 27.237 1.5 c
+27.179 1.565 27.106 1.617 27.017 1.646 c
+26.929 1.683 26.834 1.706 26.738 1.706 c
+29.843 1.602 m
+29.299 1.602 l
+29.299 2.22 l
+29.887 2.22 l
+30.167 3.117 l
+30.74 3.117 l
+30.74 2.22 l
+31.975 2.22 l
+31.975 1.602 l
+30.74 1.602 l
+30.74 -0.103 l
+30.74 -0.324 l
+30.747 -0.393 30.769 -0.455 30.799 -0.515 c
+30.836 -0.565 30.89 -0.61 30.96 -0.646 c
+31.037 -0.676 31.152 -0.69 31.298 -0.69 c
+31.434 -0.69 31.57 -0.687 31.71 -0.676 c
+31.846 -0.658 31.978 -0.632 32.106 -0.602 c
+32.106 -1.205 l
+32.026 -1.216 31.948 -1.23 31.871 -1.249 c
+31.79 -1.261 31.713 -1.267 31.636 -1.278 c
+31.555 -1.286 31.467 -1.294 31.372 -1.294 c
+31.283 -1.301 31.185 -1.308 31.077 -1.308 c
+30.89 -1.308 30.728 -1.294 30.593 -1.264 c
+30.464 -1.227 30.35 -1.183 30.254 -1.132 c
+30.167 -1.084 30.093 -1.025 30.034 -0.955 c
+29.976 -0.878 29.932 -0.801 29.901 -0.72 c
+29.872 -0.632 29.851 -0.544 29.843 -0.455 c
+29.832 -0.36 29.828 -0.264 29.828 -0.176 c
+h
+34.863 -1.323 m
+34.576 -1.323 34.333 -1.282 34.128 -1.205 c
+33.922 -1.117 33.749 -0.995 33.614 -0.837 c
+33.473 -0.683 33.371 -0.496 33.305 -0.279 c
+33.234 -0.055 33.202 0.191 33.202 0.456 c
+33.202 0.75 33.234 1.008 33.305 1.235 c
+33.382 1.459 33.488 1.646 33.628 1.793 c
+33.775 1.948 33.951 2.066 34.157 2.146 c
+34.363 2.234 34.599 2.278 34.863 2.278 c
+35.087 2.278 35.289 2.249 35.465 2.19 c
+35.642 2.132 35.792 2.047 35.921 1.941 c
+36.046 1.841 36.149 1.72 36.23 1.573 c
+36.307 1.434 36.362 1.283 36.392 1.118 c
+35.48 1.073 l
+35.451 1.249 35.381 1.389 35.274 1.5 c
+35.175 1.606 35.032 1.661 34.848 1.661 c
+34.602 1.661 34.425 1.558 34.319 1.353 c
+34.209 1.154 34.157 0.867 34.157 0.485 c
+34.157 -0.309 34.392 -0.706 34.863 -0.706 c
+35.028 -0.706 35.171 -0.654 35.289 -0.544 c
+35.407 -0.437 35.48 -0.276 35.509 -0.058 c
+36.421 -0.103 l
+36.392 -0.272 36.336 -0.426 36.259 -0.573 c
+36.189 -0.72 36.087 -0.852 35.95 -0.97 c
+35.821 -1.08 35.663 -1.168 35.48 -1.234 c
+35.304 -1.294 35.098 -1.323 34.863 -1.323 c
+38.262 1.515 m
+38.379 1.786 38.53 1.985 38.718 2.103 c
+38.901 2.22 39.122 2.278 39.379 2.278 c
+39.584 2.278 39.754 2.242 39.894 2.176 c
+40.041 2.106 40.151 2.014 40.232 1.897 c
+40.32 1.779 40.378 1.636 40.408 1.47 c
+40.444 1.301 40.467 1.125 40.467 0.941 c
+40.467 -1.264 l
+39.555 -1.264 l
+39.555 0.735 l
+39.555 0.871 39.544 0.992 39.526 1.103 c
+39.515 1.209 39.489 1.297 39.453 1.367 c
+39.412 1.444 39.353 1.503 39.276 1.544 c
+39.206 1.58 39.114 1.602 38.996 1.602 c
+38.886 1.602 38.791 1.577 38.703 1.529 c
+38.615 1.478 38.534 1.411 38.468 1.323 c
+38.409 1.235 38.358 1.125 38.321 1 c
+38.291 0.882 38.277 0.75 38.277 0.603 c
+38.277 -1.264 l
+37.365 -1.264 l
+37.365 3.514 l
+38.277 3.514 l
+38.277 2.205 l
+38.277 2.135 38.269 2.066 38.262 1.999 c
+38.262 1.793 l
+38.262 1.735 38.254 1.679 38.248 1.632 c
+38.248 1.515 l
+h
+46.075 0.838 1.866 -0.794 re
+46.075 0.044 m
+50.135 0.838 1.867 -0.794 re
+50.135 0.044 m
+54.478 2.22 m
+54.486 2.198 54.493 2.165 54.493 2.117 c
+54.501 2.076 54.508 2.028 54.508 1.97 c
+54.515 1.918 54.522 1.867 54.522 1.808 c
+54.522 1.646 l
+54.537 1.646 l
+54.596 1.764 54.663 1.86 54.743 1.941 c
+54.821 2.018 54.905 2.08 54.993 2.132 c
+55.081 2.19 55.17 2.227 55.258 2.249 c
+55.353 2.267 55.453 2.278 55.552 2.278 c
+55.758 2.278 55.937 2.234 56.095 2.146 c
+56.25 2.058 56.379 1.929 56.477 1.764 c
+56.585 1.606 56.662 1.415 56.712 1.191 c
+56.772 0.974 56.801 0.738 56.801 0.485 c
+56.801 0.221 56.772 -0.025 56.712 -0.249 c
+56.662 -0.467 56.585 -0.658 56.477 -0.823 c
+56.379 -0.981 56.246 -1.103 56.081 -1.19 c
+55.923 -1.278 55.735 -1.323 55.522 -1.323 c
+55.423 -1.323 55.324 -1.311 55.228 -1.294 c
+55.129 -1.271 55.037 -1.242 54.949 -1.19 c
+54.869 -1.143 54.791 -1.08 54.713 -0.999 c
+54.644 -0.922 54.585 -0.831 54.537 -0.72 c
+54.522 -0.72 l
+54.522 -0.808 l
+54.53 -0.849 54.537 -0.897 54.537 -0.955 c
+54.537 -1.117 l
+54.537 -1.294 l
+54.537 -2.631 l
+53.626 -2.631 l
+53.626 1.455 l
+53.626 1.621 53.618 1.768 53.612 1.897 c
+53.612 2.22 l
+h
+54.522 0.456 m
+54.522 0.228 54.541 0.037 54.582 -0.118 c
+54.63 -0.264 54.684 -0.382 54.743 -0.47 c
+54.809 -0.559 54.883 -0.625 54.964 -0.661 c
+55.041 -0.702 55.118 -0.72 55.199 -0.72 c
+55.295 -0.72 55.382 -0.698 55.463 -0.646 c
+55.552 -0.598 55.617 -0.529 55.669 -0.441 c
+55.728 -0.345 55.772 -0.22 55.802 -0.073 c
+55.839 0.081 55.86 0.268 55.86 0.485 c
+55.86 0.875 55.802 1.168 55.684 1.367 c
+55.574 1.562 55.419 1.661 55.214 1.661 c
+55.133 1.661 55.056 1.639 54.979 1.602 c
+54.898 1.562 54.824 1.5 54.758 1.411 c
+54.688 1.323 54.63 1.199 54.582 1.044 c
+54.541 0.886 54.522 0.691 54.522 0.456 c
+60.685 1.47 m
+60.586 1.478 60.483 1.488 60.377 1.5 c
+60.267 1.518 60.145 1.529 60.009 1.529 c
+59.833 1.529 59.675 1.488 59.538 1.411 c
+59.399 1.341 59.282 1.243 59.186 1.118 c
+59.098 0.989 59.028 0.842 58.981 0.676 c
+58.94 0.507 58.921 0.331 58.921 0.147 c
+58.921 -1.264 l
+58.025 -1.264 l
+58.025 0.985 l
+58.025 1.11 58.014 1.235 57.996 1.353 c
+57.984 1.478 57.969 1.596 57.951 1.706 c
+57.94 1.823 57.926 1.918 57.907 1.999 c
+57.886 2.088 57.867 2.161 57.848 2.22 c
+58.73 2.22 l
+58.738 2.168 58.748 2.117 58.76 2.058 c
+58.778 1.999 58.792 1.933 58.804 1.867 c
+58.823 1.808 58.837 1.742 58.848 1.675 c
+58.855 1.606 58.867 1.544 58.877 1.484 c
+58.892 1.484 l
+58.929 1.602 58.981 1.709 59.039 1.808 c
+59.105 1.904 59.186 1.989 59.274 2.058 c
+59.363 2.124 59.465 2.18 59.583 2.22 c
+59.708 2.257 59.855 2.278 60.024 2.278 c
+60.149 2.278 60.267 2.271 60.377 2.263 c
+60.494 2.253 60.597 2.238 60.685 2.22 c
+h
+62.615 2.22 m
+62.615 0.264 l
+62.615 0.125 62.622 0 62.644 -0.118 c
+62.663 -0.228 62.696 -0.32 62.746 -0.397 c
+62.794 -0.478 62.854 -0.54 62.923 -0.588 c
+62.989 -0.628 63.074 -0.646 63.173 -0.646 c
+63.261 -0.646 63.342 -0.628 63.423 -0.588 c
+63.511 -0.54 63.585 -0.47 63.644 -0.382 c
+63.702 -0.286 63.746 -0.176 63.776 -0.058 c
+63.812 0.067 63.835 0.206 63.835 0.353 c
+63.835 2.22 l
+64.731 2.22 l
+64.731 -0.484 l
+64.731 -0.72 l
+64.739 -0.801 64.746 -0.878 64.746 -0.955 c
+64.746 -1.147 l
+64.753 -1.198 64.76 -1.234 64.76 -1.264 c
+63.908 -1.264 l
+63.897 -1.234 63.886 -1.198 63.879 -1.147 c
+63.879 -0.955 l
+63.879 -0.889 63.872 -0.819 63.864 -0.75 c
+63.864 -0.573 l
+63.849 -0.573 l
+63.731 -0.837 63.577 -1.029 63.394 -1.147 c
+63.217 -1.264 63.015 -1.323 62.791 -1.323 c
+62.586 -1.323 62.412 -1.286 62.276 -1.22 c
+62.137 -1.153 62.027 -1.058 61.938 -0.941 c
+61.857 -0.823 61.799 -0.687 61.762 -0.529 c
+61.732 -0.363 61.718 -0.187 61.718 0 c
+61.718 2.22 l
+h
+67.983 -1.264 m
+67.983 0.721 l
+67.983 1.022 67.939 1.243 67.851 1.382 c
+67.77 1.529 67.634 1.602 67.44 1.602 c
+67.33 1.602 67.226 1.577 67.131 1.529 c
+67.043 1.478 66.962 1.411 66.896 1.323 c
+66.837 1.235 66.785 1.125 66.748 1 c
+66.719 0.882 66.705 0.75 66.705 0.603 c
+66.705 -1.264 l
+65.793 -1.264 l
+65.793 1.44 l
+65.793 1.661 l
+65.793 1.75 65.786 1.827 65.778 1.897 c
+65.778 2.088 l
+65.778 2.22 l
+66.631 2.22 l
+66.638 2.19 66.646 2.146 66.646 2.088 c
+66.646 1.897 l
+66.653 1.827 66.661 1.756 66.661 1.691 c
+66.667 1.621 66.675 1.565 66.675 1.529 c
+66.69 1.529 l
+66.808 1.793 66.958 1.985 67.145 2.103 c
+67.33 2.22 67.55 2.278 67.807 2.278 c
+67.991 2.278 68.153 2.249 68.292 2.19 c
+68.428 2.132 68.542 2.043 68.63 1.926 c
+68.718 1.808 68.781 1.665 68.822 1.5 c
+68.869 1.341 68.895 1.154 68.895 0.941 c
+68.895 -1.264 l
+h
+71.412 -1.323 m
+71.155 -1.323 70.927 -1.286 70.721 -1.22 c
+70.515 -1.143 70.339 -1.029 70.192 -0.881 c
+70.045 -0.727 69.927 -0.536 69.84 -0.309 c
+69.759 -0.085 69.722 0.181 69.722 0.485 c
+69.722 0.816 69.765 1.095 69.854 1.323 c
+69.95 1.558 70.078 1.742 70.236 1.881 c
+70.401 2.018 70.588 2.117 70.794 2.176 c
+71 2.242 71.21 2.278 71.427 2.278 c
+71.698 2.278 71.934 2.227 72.132 2.132 c
+72.338 2.043 72.504 1.911 72.632 1.735 c
+72.768 1.565 72.867 1.36 72.926 1.118 c
+72.992 0.882 73.029 0.617 73.029 0.324 c
+73.029 0.309 l
+70.663 0.309 l
+70.663 0.162 70.677 0.023 70.706 -0.103 c
+70.743 -0.231 70.798 -0.345 70.868 -0.441 c
+70.935 -0.529 71.018 -0.598 71.118 -0.646 c
+71.213 -0.698 71.328 -0.72 71.456 -0.72 c
+71.61 -0.72 71.75 -0.687 71.868 -0.617 c
+71.993 -0.551 72.08 -0.448 72.132 -0.309 c
+72.97 -0.382 l
+72.94 -0.481 72.886 -0.588 72.809 -0.706 c
+72.728 -0.816 72.624 -0.918 72.5 -1.014 c
+72.382 -1.103 72.228 -1.176 72.044 -1.234 c
+71.868 -1.294 71.654 -1.323 71.412 -1.323 c
+71.412 1.706 m
+71.324 1.706 71.236 1.691 71.147 1.661 c
+71.059 1.632 70.978 1.58 70.912 1.515 c
+70.842 1.444 70.783 1.357 70.736 1.249 c
+70.696 1.139 70.677 1.014 70.677 0.867 c
+72.147 0.867 l
+72.147 1.004 72.121 1.125 72.074 1.235 c
+72.033 1.341 71.978 1.43 71.912 1.5 c
+71.853 1.565 71.779 1.617 71.691 1.646 c
+71.603 1.683 71.507 1.706 71.412 1.706 c
+78.729 -2.631 m
+78.729 3.514 l
+80.654 3.514 l
+80.654 2.896 l
+79.581 2.896 l
+79.581 -2.013 l
+80.654 -2.013 l
+80.654 -2.631 l
+h
+85.052 1.47 m
+84.953 1.478 84.851 1.488 84.744 1.5 c
+84.634 1.518 84.512 1.529 84.377 1.529 c
+84.2 1.529 84.042 1.488 83.906 1.411 c
+83.766 1.341 83.649 1.243 83.554 1.118 c
+83.465 0.989 83.396 0.842 83.348 0.676 c
+83.307 0.507 83.288 0.331 83.288 0.147 c
+83.288 -1.264 l
+82.392 -1.264 l
+82.392 0.985 l
+82.392 1.11 82.381 1.235 82.363 1.353 c
+82.351 1.478 82.337 1.596 82.318 1.706 c
+82.307 1.823 82.293 1.918 82.274 1.999 c
+82.253 2.088 82.234 2.161 82.216 2.22 c
+83.097 2.22 l
+83.105 2.168 83.116 2.117 83.127 2.058 c
+83.145 1.999 83.16 1.933 83.171 1.867 c
+83.19 1.808 83.205 1.742 83.215 1.675 c
+83.223 1.606 83.234 1.544 83.245 1.484 c
+83.259 1.484 l
+83.296 1.602 83.348 1.709 83.406 1.808 c
+83.473 1.904 83.554 1.989 83.641 2.058 c
+83.73 2.124 83.833 2.18 83.951 2.22 c
+84.075 2.257 84.223 2.278 84.391 2.278 c
+84.516 2.278 84.634 2.271 84.744 2.263 c
+84.861 2.253 84.965 2.238 85.052 2.22 c
+h
+87.658 -1.323 m
+87.401 -1.323 87.173 -1.286 86.968 -1.22 c
+86.762 -1.143 86.585 -1.029 86.438 -0.881 c
+86.291 -0.727 86.174 -0.536 86.085 -0.309 c
+86.004 -0.085 85.968 0.181 85.968 0.485 c
+85.968 0.816 86.012 1.095 86.1 1.323 c
+86.195 1.558 86.324 1.742 86.482 1.881 c
+86.648 2.018 86.835 2.117 87.041 2.176 c
+87.246 2.242 87.456 2.278 87.673 2.278 c
+87.945 2.278 88.18 2.227 88.379 2.132 c
+88.584 2.043 88.749 1.911 88.878 1.735 c
+89.014 1.565 89.113 1.36 89.172 1.118 c
+89.239 0.882 89.275 0.617 89.275 0.324 c
+89.275 0.309 l
+86.908 0.309 l
+86.908 0.162 86.923 0.023 86.953 -0.103 c
+86.989 -0.231 87.045 -0.345 87.114 -0.441 c
+87.18 -0.529 87.265 -0.598 87.364 -0.646 c
+87.46 -0.698 87.573 -0.72 87.702 -0.72 c
+87.857 -0.72 87.996 -0.687 88.113 -0.617 c
+88.239 -0.551 88.327 -0.448 88.379 -0.309 c
+89.216 -0.382 l
+89.187 -0.481 89.131 -0.588 89.054 -0.706 c
+88.973 -0.816 88.871 -0.918 88.746 -1.014 c
+88.628 -1.103 88.474 -1.176 88.29 -1.234 c
+88.113 -1.294 87.901 -1.323 87.658 -1.323 c
+87.658 1.706 m
+87.57 1.706 87.481 1.691 87.394 1.661 c
+87.305 1.632 87.224 1.58 87.159 1.515 c
+87.089 1.444 87.03 1.357 86.982 1.249 c
+86.941 1.139 86.923 1.014 86.923 0.867 c
+88.393 0.867 l
+88.393 1.004 88.367 1.125 88.319 1.235 c
+88.279 1.341 88.224 1.43 88.158 1.5 c
+88.099 1.565 88.026 1.617 87.937 1.646 c
+87.849 1.683 87.754 1.706 87.658 1.706 c
+91.322 -1.264 m
+91.322 0.852 l
+91.322 1.018 91.315 1.154 91.307 1.264 c
+91.296 1.371 91.278 1.455 91.248 1.515 c
+91.226 1.58 91.197 1.632 91.16 1.661 c
+91.13 1.691 91.09 1.706 91.043 1.706 c
+90.984 1.706 90.929 1.675 90.881 1.617 c
+90.841 1.565 90.808 1.492 90.778 1.397 c
+90.748 1.309 90.723 1.195 90.704 1.058 c
+90.694 0.919 90.69 0.769 90.69 0.603 c
+90.69 -1.264 l
+89.94 -1.264 l
+89.94 1.47 l
+89.94 1.706 l
+89.94 1.926 l
+89.94 2.003 89.933 2.066 89.925 2.117 c
+89.925 2.22 l
+90.602 2.22 l
+90.602 2.132 l
+90.602 1.985 l
+90.609 1.926 90.617 1.867 90.617 1.808 c
+90.617 1.646 l
+90.631 1.646 l
+90.65 1.735 90.679 1.812 90.719 1.881 c
+90.756 1.959 90.8 2.028 90.852 2.088 c
+90.91 2.146 90.976 2.19 91.057 2.22 c
+91.134 2.257 91.223 2.278 91.322 2.278 c
+91.506 2.278 91.645 2.224 91.733 2.117 c
+91.829 2.018 91.899 1.86 91.939 1.646 c
+91.954 1.646 l
+91.99 1.742 92.031 1.831 92.071 1.911 c
+92.119 1.989 92.175 2.051 92.233 2.103 c
+92.292 2.161 92.358 2.205 92.439 2.234 c
+92.516 2.263 92.604 2.278 92.703 2.278 c
+92.84 2.278 92.954 2.253 93.042 2.205 c
+93.129 2.153 93.196 2.08 93.247 1.985 c
+93.306 1.885 93.343 1.756 93.365 1.602 c
+93.395 1.455 93.409 1.272 93.409 1.058 c
+93.409 -1.264 l
+92.689 -1.264 l
+92.689 0.852 l
+92.689 1.018 92.682 1.154 92.674 1.264 c
+92.663 1.371 92.645 1.455 92.615 1.515 c
+92.593 1.58 92.564 1.632 92.527 1.661 c
+92.498 1.691 92.458 1.706 92.41 1.706 c
+92.292 1.706 92.196 1.617 92.13 1.44 c
+92.071 1.272 92.042 1.014 92.042 0.661 c
+92.042 -1.264 l
+h
+97.43 0.485 m
+97.43 0.21 97.393 -0.04 97.326 -0.264 c
+97.256 -0.481 97.154 -0.669 97.018 -0.823 c
+96.878 -0.981 96.701 -1.103 96.489 -1.19 c
+96.271 -1.278 96.018 -1.323 95.724 -1.323 c
+95.448 -1.323 95.202 -1.278 94.989 -1.19 c
+94.783 -1.103 94.611 -0.981 94.475 -0.823 c
+94.335 -0.669 94.232 -0.481 94.166 -0.264 c
+94.096 -0.04 94.064 0.21 94.064 0.485 c
+94.064 0.738 94.093 0.974 94.151 1.191 c
+94.218 1.415 94.32 1.606 94.46 1.764 c
+94.596 1.929 94.772 2.058 94.989 2.146 c
+95.202 2.234 95.46 2.278 95.753 2.278 c
+96.065 2.278 96.327 2.234 96.533 2.146 c
+96.746 2.058 96.919 1.929 97.047 1.764 c
+97.183 1.606 97.282 1.415 97.341 1.191 c
+97.399 0.974 97.43 0.738 97.43 0.485 c
+96.474 0.485 m
+96.474 0.691 96.459 0.867 96.43 1.014 c
+96.408 1.162 96.371 1.283 96.312 1.382 c
+96.254 1.478 96.18 1.548 96.092 1.588 c
+96.003 1.636 95.893 1.661 95.768 1.661 c
+95.504 1.661 95.313 1.562 95.195 1.367 c
+95.078 1.18 95.018 0.886 95.018 0.485 c
+95.018 0.063 95.078 -0.243 95.195 -0.426 c
+95.313 -0.613 95.489 -0.706 95.724 -0.706 c
+95.849 -0.706 95.963 -0.687 96.063 -0.646 c
+96.158 -0.598 96.239 -0.525 96.298 -0.426 c
+96.364 -0.33 96.408 -0.206 96.43 -0.058 c
+96.459 0.088 96.474 0.268 96.474 0.485 c
+98.888 1.602 m
+98.344 1.602 l
+98.344 2.22 l
+98.932 2.22 l
+99.211 3.117 l
+99.785 3.117 l
+99.785 2.22 l
+101.019 2.22 l
+101.019 1.602 l
+99.785 1.602 l
+99.785 -0.103 l
+99.785 -0.324 l
+99.792 -0.393 99.814 -0.455 99.843 -0.515 c
+99.88 -0.565 99.936 -0.61 100.005 -0.646 c
+100.082 -0.676 100.196 -0.69 100.343 -0.69 c
+100.479 -0.69 100.615 -0.687 100.755 -0.676 c
+100.891 -0.658 101.023 -0.632 101.152 -0.602 c
+101.152 -1.205 l
+101.071 -1.216 100.994 -1.23 100.917 -1.249 c
+100.836 -1.261 100.759 -1.267 100.682 -1.278 c
+100.601 -1.286 100.512 -1.294 100.416 -1.294 c
+100.329 -1.301 100.229 -1.308 100.123 -1.308 c
+99.936 -1.308 99.774 -1.294 99.637 -1.264 c
+99.51 -1.227 99.396 -1.183 99.3 -1.132 c
+99.211 -1.084 99.138 -1.025 99.08 -0.955 c
+99.02 -0.878 98.976 -0.801 98.947 -0.72 c
+98.918 -0.632 98.895 -0.544 98.888 -0.455 c
+98.877 -0.36 98.874 -0.264 98.874 -0.176 c
+h
+103.904 -1.323 m
+103.647 -1.323 103.419 -1.286 103.213 -1.22 c
+103.007 -1.143 102.831 -1.029 102.685 -0.881 c
+102.537 -0.727 102.419 -0.536 102.332 -0.309 c
+102.251 -0.085 102.214 0.181 102.214 0.485 c
+102.214 0.816 102.257 1.095 102.346 1.323 c
+102.442 1.558 102.571 1.742 102.728 1.881 c
+102.893 2.018 103.082 2.117 103.287 2.176 c
+103.493 2.242 103.702 2.278 103.919 2.278 c
+104.191 2.278 104.426 2.227 104.624 2.132 c
+104.83 2.043 104.996 1.911 105.124 1.735 c
+105.26 1.565 105.359 1.36 105.418 1.118 c
+105.484 0.882 105.521 0.617 105.521 0.324 c
+105.521 0.309 l
+103.155 0.309 l
+103.155 0.162 103.169 0.023 103.198 -0.103 c
+103.236 -0.231 103.29 -0.345 103.36 -0.441 c
+103.427 -0.529 103.511 -0.598 103.61 -0.646 c
+103.706 -0.698 103.82 -0.72 103.948 -0.72 c
+104.102 -0.72 104.242 -0.687 104.36 -0.617 c
+104.485 -0.551 104.573 -0.448 104.624 -0.309 c
+105.463 -0.382 l
+105.433 -0.481 105.378 -0.588 105.301 -0.706 c
+105.22 -0.816 105.117 -0.918 104.992 -1.014 c
+104.875 -1.103 104.72 -1.176 104.536 -1.234 c
+104.36 -1.294 104.146 -1.323 103.904 -1.323 c
+103.904 1.706 m
+103.816 1.706 103.728 1.691 103.639 1.661 c
+103.552 1.632 103.471 1.58 103.404 1.515 c
+103.335 1.444 103.276 1.357 103.228 1.249 c
+103.188 1.139 103.169 1.014 103.169 0.867 c
+104.639 0.867 l
+104.639 1.004 104.613 1.125 104.566 1.235 c
+104.526 1.341 104.47 1.43 104.404 1.5 c
+104.345 1.565 104.272 1.617 104.183 1.646 c
+104.096 1.683 104 1.706 103.904 1.706 c
+106.774 -2.631 m
+106.774 -2.013 l
+107.847 -2.013 l
+107.847 2.896 l
+106.774 2.896 l
+106.774 3.514 l
+108.7 3.514 l
+108.7 -2.631 l
+h
+f
+Q
+q 1 0 0 1 70.6476 229.5009 cm
+0 0 m
+-0.941 0 l
+-0.941 -2.161 l
+-1.617 -2.161 l
+-1.617 3.19 l
+-0.118 3.19 l
+0.412 3.19 0.809 3.05 1.073 2.778 c
+1.345 2.502 1.484 2.105 1.484 1.588 c
+1.484 1.253 1.411 0.963 1.264 0.721 c
+1.118 0.474 0.912 0.287 0.647 0.162 c
+1.675 -2.117 l
+1.675 -2.161 l
+0.956 -2.161 l
+h
+-0.941 0.588 m
+-0.118 0.588 l
+0.166 0.588 0.389 0.676 0.559 0.852 c
+0.724 1.037 0.809 1.282 0.809 1.588 c
+0.809 2.271 0.493 2.616 -0.132 2.616 c
+-0.941 2.616 l
+h
+3.719 -2.234 m
+3.219 -2.234 2.837 -2.087 2.573 -1.793 c
+2.308 -1.5 2.176 -1.066 2.176 -0.484 c
+2.176 -0.014 l
+2.176 0.58 2.301 1.047 2.558 1.382 c
+2.822 1.723 3.183 1.897 3.645 1.897 c
+4.105 1.897 4.447 1.742 4.675 1.44 c
+4.91 1.147 5.031 0.684 5.042 0.059 c
+5.042 -0.367 l
+2.822 -0.367 l
+2.822 -0.455 l
+2.822 -0.889 2.899 -1.201 3.057 -1.396 c
+3.223 -1.583 3.454 -1.675 3.749 -1.675 c
+3.944 -1.675 4.116 -1.643 4.262 -1.573 c
+4.41 -1.496 4.546 -1.378 4.675 -1.22 c
+5.012 -1.631 l
+4.726 -2.036 4.296 -2.234 3.719 -2.234 c
+3.645 1.338 m
+3.37 1.338 3.167 1.242 3.043 1.058 c
+2.914 0.871 2.841 0.58 2.822 0.191 c
+4.395 0.191 l
+4.395 0.279 l
+4.373 0.661 4.307 0.929 4.189 1.087 c
+4.072 1.253 3.888 1.338 3.645 1.338 c
+6.438 1.823 m
+6.453 1.455 l
+6.695 1.75 7.015 1.897 7.408 1.897 c
+7.85 1.897 8.158 1.698 8.335 1.309 c
+8.588 1.698 8.937 1.897 9.378 1.897 c
+10.113 1.897 10.488 1.434 10.51 0.515 c
+10.51 -2.161 l
+9.864 -2.161 l
+9.864 0.456 l
+9.864 0.75 9.808 0.963 9.702 1.103 c
+9.602 1.239 9.43 1.309 9.187 1.309 c
+8.989 1.309 8.827 1.228 8.702 1.073 c
+8.584 0.927 8.515 0.735 8.496 0.5 c
+8.496 -2.161 l
+7.834 -2.161 l
+7.834 0.485 l
+7.834 1.033 7.614 1.309 7.173 1.309 c
+6.839 1.309 6.604 1.147 6.468 0.823 c
+6.468 -2.161 l
+5.821 -2.161 l
+5.821 1.823 l
+h
+11.348 0.015 m
+11.348 0.592 11.484 1.047 11.759 1.382 c
+12.042 1.723 12.414 1.897 12.877 1.897 c
+13.336 1.897 13.704 1.727 13.979 1.397 c
+14.262 1.073 14.409 0.625 14.42 0.059 c
+14.42 -0.367 l
+14.42 -0.937 14.277 -1.392 13.993 -1.735 c
+13.718 -2.069 13.351 -2.234 12.891 -2.234 c
+12.428 -2.234 12.057 -2.072 11.774 -1.749 c
+11.499 -1.419 11.356 -0.977 11.348 -0.426 c
+h
+11.994 -0.367 m
+11.994 -0.771 12.072 -1.087 12.23 -1.323 c
+12.395 -1.558 12.615 -1.675 12.891 -1.675 c
+13.457 -1.675 13.751 -1.264 13.773 -0.441 c
+13.773 0.015 l
+13.773 0.416 13.689 0.735 13.523 0.97 c
+13.365 1.213 13.149 1.338 12.877 1.338 c
+12.612 1.338 12.395 1.213 12.23 0.97 c
+12.072 0.735 11.994 0.416 11.994 0.015 c
+h
+16.331 -1.176 m
+17.081 1.823 l
+17.742 1.823 l
+16.566 -2.161 l
+16.081 -2.161 l
+14.89 1.823 l
+15.551 1.823 l
+h
+19.8 -2.234 m
+19.3 -2.234 18.918 -2.087 18.653 -1.793 c
+18.389 -1.5 18.257 -1.066 18.257 -0.484 c
+18.257 -0.014 l
+18.257 0.58 18.381 1.047 18.639 1.382 c
+18.903 1.723 19.264 1.897 19.726 1.897 c
+20.185 1.897 20.528 1.742 20.756 1.44 c
+20.991 1.147 21.112 0.684 21.122 0.059 c
+21.122 -0.367 l
+18.903 -0.367 l
+18.903 -0.455 l
+18.903 -0.889 18.98 -1.201 19.138 -1.396 c
+19.304 -1.583 19.535 -1.675 19.829 -1.675 c
+20.024 -1.675 20.197 -1.643 20.343 -1.573 c
+20.49 -1.496 20.627 -1.378 20.756 -1.22 c
+21.093 -1.631 l
+20.806 -2.036 20.376 -2.234 19.8 -2.234 c
+19.726 1.338 m
+19.451 1.338 19.248 1.242 19.123 1.058 c
+18.995 0.871 18.922 0.58 18.903 0.191 c
+20.476 0.191 l
+20.476 0.279 l
+20.454 0.661 20.388 0.929 20.27 1.087 c
+20.153 1.253 19.969 1.338 19.726 1.338 c
+25.268 1.205 m
+25.18 1.224 25.08 1.235 24.974 1.235 c
+24.64 1.235 24.405 1.051 24.268 0.691 c
+24.268 -2.161 l
+23.622 -2.161 l
+23.622 1.823 l
+24.253 1.823 l
+24.268 1.411 l
+24.445 1.735 24.687 1.897 25.003 1.897 c
+25.11 1.897 25.198 1.874 25.268 1.837 c
+h
+27.267 -2.234 m
+26.767 -2.234 26.385 -2.087 26.121 -1.793 c
+25.856 -1.5 25.724 -1.066 25.724 -0.484 c
+25.724 -0.014 l
+25.724 0.58 25.849 1.047 26.106 1.382 c
+26.37 1.723 26.73 1.897 27.194 1.897 c
+27.653 1.897 27.995 1.742 28.222 1.44 c
+28.457 1.147 28.579 0.684 28.59 0.059 c
+28.59 -0.367 l
+26.37 -0.367 l
+26.37 -0.455 l
+26.37 -0.889 26.448 -1.201 26.605 -1.396 c
+26.771 -1.583 27.002 -1.675 27.296 -1.675 c
+27.491 -1.675 27.664 -1.643 27.811 -1.573 c
+27.958 -1.496 28.094 -1.378 28.222 -1.22 c
+28.561 -1.631 l
+28.274 -2.036 27.844 -2.234 27.267 -2.234 c
+27.194 1.338 m
+26.918 1.338 26.716 1.242 26.591 1.058 c
+26.462 0.871 26.389 0.58 26.37 0.191 c
+27.943 0.191 l
+27.943 0.279 l
+27.921 0.661 27.855 0.929 27.738 1.087 c
+27.62 1.253 27.436 1.338 27.194 1.338 c
+29.986 1.823 m
+30.001 1.455 l
+30.244 1.75 30.564 1.897 30.957 1.897 c
+31.397 1.897 31.706 1.698 31.883 1.309 c
+32.136 1.698 32.485 1.897 32.926 1.897 c
+33.661 1.897 34.036 1.434 34.058 0.515 c
+34.058 -2.161 l
+33.411 -2.161 l
+33.411 0.456 l
+33.411 0.75 33.356 0.963 33.249 1.103 c
+33.151 1.239 32.978 1.309 32.735 1.309 c
+32.536 1.309 32.375 1.228 32.249 1.073 c
+32.133 0.927 32.062 0.735 32.044 0.5 c
+32.044 -2.161 l
+31.383 -2.161 l
+31.383 0.485 l
+31.383 1.033 31.162 1.309 30.722 1.309 c
+30.387 1.309 30.152 1.147 30.016 0.823 c
+30.016 -2.161 l
+29.369 -2.161 l
+29.369 1.823 l
+h
+34.896 0.015 m
+34.896 0.592 35.032 1.047 35.307 1.382 c
+35.59 1.723 35.962 1.897 36.424 1.897 c
+36.884 1.897 37.251 1.727 37.527 1.397 c
+37.81 1.073 37.957 0.625 37.968 0.059 c
+37.968 -0.367 l
+37.968 -0.937 37.824 -1.392 37.542 -1.735 c
+37.266 -2.069 36.899 -2.234 36.44 -2.234 c
+35.976 -2.234 35.605 -2.072 35.322 -1.749 c
+35.046 -1.419 34.903 -0.977 34.896 -0.426 c
+h
+35.542 -0.367 m
+35.542 -0.771 35.62 -1.087 35.777 -1.323 c
+35.943 -1.558 36.164 -1.675 36.44 -1.675 c
+37.005 -1.675 37.299 -1.264 37.321 -0.441 c
+37.321 0.015 l
+37.321 0.416 37.236 0.735 37.072 0.97 c
+36.914 1.213 36.696 1.338 36.424 1.338 c
+36.16 1.338 35.943 1.213 35.777 0.97 c
+35.62 0.735 35.542 0.416 35.542 0.015 c
+h
+39.659 2.778 m
+39.659 1.823 l
+40.261 1.823 l
+40.261 1.294 l
+39.659 1.294 l
+39.659 -1.176 l
+39.659 -1.334 39.68 -1.452 39.732 -1.529 c
+39.79 -1.61 39.879 -1.646 39.996 -1.646 c
+40.085 -1.646 40.172 -1.631 40.261 -1.602 c
+40.261 -2.161 l
+40.114 -2.209 39.96 -2.234 39.805 -2.234 c
+39.548 -2.234 39.353 -2.142 39.218 -1.955 c
+39.077 -1.771 39.012 -1.51 39.012 -1.176 c
+39.012 1.294 l
+38.409 1.294 l
+38.409 1.823 l
+39.012 1.823 l
+39.012 2.778 l
+h
+42.451 -2.234 m
+41.951 -2.234 41.569 -2.087 41.305 -1.793 c
+41.04 -1.5 40.908 -1.066 40.908 -0.484 c
+40.908 -0.014 l
+40.908 0.58 41.032 1.047 41.29 1.382 c
+41.554 1.723 41.915 1.897 42.377 1.897 c
+42.837 1.897 43.179 1.742 43.407 1.44 c
+43.642 1.147 43.763 0.684 43.774 0.059 c
+43.774 -0.367 l
+41.554 -0.367 l
+41.554 -0.455 l
+41.554 -0.889 41.631 -1.201 41.79 -1.396 c
+41.955 -1.583 42.187 -1.675 42.481 -1.675 c
+42.676 -1.675 42.848 -1.643 42.995 -1.573 c
+43.142 -1.496 43.278 -1.378 43.407 -1.22 c
+43.744 -1.631 l
+43.458 -2.036 43.028 -2.234 42.451 -2.234 c
+42.377 1.338 m
+42.102 1.338 41.9 1.242 41.775 1.058 c
+41.647 0.871 41.573 0.58 41.554 0.191 c
+43.127 0.191 l
+43.127 0.279 l
+43.106 0.661 43.039 0.929 42.921 1.087 c
+42.804 1.253 42.62 1.338 42.377 1.338 c
+47.919 1.205 m
+47.831 1.224 47.732 1.235 47.625 1.235 c
+47.291 1.235 47.056 1.051 46.919 0.691 c
+46.919 -2.161 l
+46.273 -2.161 l
+46.273 1.823 l
+46.905 1.823 l
+46.919 1.411 l
+47.096 1.735 47.339 1.897 47.655 1.897 c
+47.761 1.897 47.85 1.874 47.919 1.837 c
+h
+49.918 -2.234 m
+49.418 -2.234 49.036 -2.087 48.772 -1.793 c
+48.507 -1.5 48.375 -1.066 48.375 -0.484 c
+48.375 -0.014 l
+48.375 0.58 48.5 1.047 48.757 1.382 c
+49.022 1.723 49.381 1.897 49.845 1.897 c
+50.304 1.897 50.646 1.742 50.873 1.44 c
+51.108 1.147 51.23 0.684 51.241 0.059 c
+51.241 -0.367 l
+49.022 -0.367 l
+49.022 -0.455 l
+49.022 -0.889 49.099 -1.201 49.257 -1.396 c
+49.422 -1.583 49.653 -1.675 49.948 -1.675 c
+50.142 -1.675 50.315 -1.643 50.462 -1.573 c
+50.609 -1.496 50.745 -1.378 50.873 -1.22 c
+51.212 -1.631 l
+50.925 -2.036 50.495 -2.234 49.918 -2.234 c
+49.845 1.338 m
+49.569 1.338 49.367 1.242 49.242 1.058 c
+49.113 0.871 49.04 0.58 49.022 0.191 c
+50.594 0.191 l
+50.594 0.279 l
+50.572 0.661 50.506 0.929 50.389 1.087 c
+50.271 1.253 50.087 1.338 49.845 1.338 c
+52.315 -2.161 m
+52.315 1.294 l
+51.785 1.294 l
+51.785 1.823 l
+52.315 1.823 l
+52.315 2.278 l
+52.315 2.679 52.41 2.992 52.608 3.219 c
+52.814 3.443 53.093 3.557 53.446 3.557 c
+53.582 3.557 53.714 3.535 53.843 3.499 c
+53.813 2.955 l
+53.714 2.973 53.615 2.984 53.52 2.984 c
+53.144 2.984 52.961 2.72 52.961 2.19 c
+52.961 1.823 l
+53.637 1.823 l
+53.637 1.294 l
+52.961 1.294 l
+52.961 -2.161 l
+h
+56.342 -1.147 m
+56.342 -0.999 56.286 -0.878 56.18 -0.779 c
+56.07 -0.683 55.864 -0.565 55.563 -0.426 c
+55.218 -0.279 54.975 -0.158 54.827 -0.058 c
+54.68 0.048 54.57 0.166 54.505 0.294 c
+54.435 0.419 54.401 0.577 54.401 0.765 c
+54.401 1.087 54.519 1.357 54.754 1.573 c
+54.989 1.786 55.291 1.897 55.665 1.897 c
+56.047 1.897 56.357 1.783 56.592 1.558 c
+56.827 1.33 56.944 1.044 56.944 0.691 c
+56.298 0.691 l
+56.298 0.867 56.238 1.018 56.122 1.147 c
+56.003 1.272 55.849 1.338 55.665 1.338 c
+55.467 1.338 55.316 1.282 55.21 1.176 c
+55.1 1.077 55.048 0.945 55.048 0.779 c
+55.048 0.651 55.085 0.544 55.166 0.456 c
+55.243 0.375 55.434 0.272 55.739 0.147 c
+56.217 -0.04 56.548 -0.228 56.724 -0.411 c
+56.901 -0.588 56.988 -0.816 56.988 -1.087 c
+56.988 -1.44 56.864 -1.72 56.621 -1.926 c
+56.386 -2.131 56.07 -2.234 55.68 -2.234 c
+55.258 -2.234 54.919 -2.117 54.666 -1.881 c
+54.409 -1.639 54.283 -1.334 54.283 -0.97 c
+54.931 -0.97 l
+54.938 -1.198 55.008 -1.374 55.137 -1.5 c
+55.261 -1.617 55.445 -1.675 55.68 -1.675 c
+55.893 -1.675 56.055 -1.627 56.165 -1.529 c
+56.283 -1.433 56.342 -1.305 56.342 -1.147 c
+57.841 -3.233 m
+57.444 -2.969 l
+57.68 -2.645 57.801 -2.311 57.811 -1.97 c
+57.811 -1.352 l
+58.474 -1.352 l
+58.474 -1.881 l
+58.474 -2.138 58.407 -2.385 58.282 -2.631 c
+58.164 -2.874 58.017 -3.075 57.841 -3.233 c
+61.883 2.778 m
+61.883 1.823 l
+62.486 1.823 l
+62.486 1.294 l
+61.883 1.294 l
+61.883 -1.176 l
+61.883 -1.334 61.905 -1.452 61.957 -1.529 c
+62.015 -1.61 62.104 -1.646 62.221 -1.646 c
+62.31 -1.646 62.397 -1.631 62.486 -1.602 c
+62.486 -2.161 l
+62.339 -2.209 62.185 -2.234 62.03 -2.234 c
+61.773 -2.234 61.578 -2.142 61.443 -1.955 c
+61.302 -1.771 61.236 -1.51 61.236 -1.176 c
+61.236 1.294 l
+60.633 1.294 l
+60.633 1.823 l
+61.236 1.823 l
+61.236 2.778 l
+h
+63.897 1.411 m
+64.151 1.735 64.47 1.897 64.853 1.897 c
+65.558 1.897 65.915 1.426 65.925 0.485 c
+65.925 -2.161 l
+65.279 -2.161 l
+65.279 0.456 l
+65.279 0.769 65.223 0.989 65.117 1.118 c
+65.007 1.242 64.853 1.309 64.647 1.309 c
+64.489 1.309 64.342 1.253 64.205 1.147 c
+64.078 1.037 63.974 0.9 63.897 0.735 c
+63.897 -2.161 l
+63.251 -2.161 l
+63.251 3.484 l
+63.897 3.484 l
+h
+68.924 -2.161 m
+68.884 -2.072 68.858 -1.926 68.851 -1.72 c
+68.616 -2.065 68.321 -2.234 67.968 -2.234 c
+67.605 -2.234 67.322 -2.138 67.116 -1.94 c
+66.918 -1.735 66.822 -1.448 66.822 -1.072 c
+66.822 -0.673 66.958 -0.353 67.234 -0.118 c
+67.506 0.125 67.881 0.25 68.351 0.25 c
+68.836 0.25 l
+68.836 0.676 l
+68.836 0.912 68.781 1.077 68.674 1.176 c
+68.564 1.282 68.402 1.338 68.189 1.338 c
+67.991 1.338 67.829 1.279 67.704 1.162 c
+67.586 1.044 67.527 0.897 67.527 0.721 c
+66.881 0.721 l
+66.881 0.915 66.94 1.106 67.057 1.294 c
+67.182 1.477 67.344 1.625 67.542 1.735 c
+67.748 1.841 67.976 1.897 68.233 1.897 c
+68.634 1.897 68.938 1.793 69.144 1.588 c
+69.358 1.382 69.472 1.087 69.483 0.706 c
+69.483 -1.308 l
+69.483 -1.613 69.52 -1.878 69.601 -2.102 c
+69.601 -2.161 l
+h
+68.057 -1.646 m
+68.222 -1.646 68.373 -1.602 68.512 -1.514 c
+68.66 -1.425 68.766 -1.315 68.836 -1.176 c
+68.836 -0.235 l
+68.468 -0.235 l
+68.153 -0.235 67.91 -0.305 67.733 -0.441 c
+67.557 -0.569 67.469 -0.757 67.469 -0.999 c
+67.469 -1.228 67.513 -1.392 67.602 -1.5 c
+67.689 -1.598 67.84 -1.646 68.057 -1.646 c
+71.334 2.778 m
+71.334 1.823 l
+71.937 1.823 l
+71.937 1.294 l
+71.334 1.294 l
+71.334 -1.176 l
+71.334 -1.334 71.357 -1.452 71.409 -1.529 c
+71.467 -1.61 71.555 -1.646 71.673 -1.646 c
+71.761 -1.646 71.849 -1.631 71.937 -1.602 c
+71.937 -2.161 l
+71.791 -2.209 71.636 -2.234 71.482 -2.234 c
+71.224 -2.234 71.03 -2.142 70.894 -1.955 c
+70.754 -1.771 70.688 -1.51 70.688 -1.176 c
+70.688 1.294 l
+70.085 1.294 l
+70.085 1.823 l
+70.688 1.823 l
+70.688 2.778 l
+h
+77.391 -1.014 m
+77.994 1.823 l
+78.64 1.823 l
+77.655 -2.161 l
+77.141 -2.161 l
+76.362 0.691 l
+75.612 -2.161 l
+75.083 -2.161 l
+74.127 1.823 l
+74.76 1.823 l
+75.377 -0.941 l
+76.112 1.823 l
+76.626 1.823 l
+h
+80.742 -2.234 m
+80.242 -2.234 79.86 -2.087 79.596 -1.793 c
+79.331 -1.5 79.199 -1.066 79.199 -0.484 c
+79.199 -0.014 l
+79.199 0.58 79.324 1.047 79.581 1.382 c
+79.845 1.723 80.206 1.897 80.668 1.897 c
+81.128 1.897 81.47 1.742 81.698 1.44 c
+81.933 1.147 82.054 0.684 82.065 0.059 c
+82.065 -0.367 l
+79.845 -0.367 l
+79.845 -0.455 l
+79.845 -0.889 79.922 -1.201 80.08 -1.396 c
+80.246 -1.583 80.477 -1.675 80.772 -1.675 c
+80.967 -1.675 81.139 -1.643 81.286 -1.573 c
+81.433 -1.496 81.569 -1.378 81.698 -1.22 c
+82.035 -1.631 l
+81.749 -2.036 81.319 -2.234 80.742 -2.234 c
+80.668 1.338 m
+80.393 1.338 80.19 1.242 80.066 1.058 c
+79.937 0.871 79.864 0.58 79.845 0.191 c
+81.418 0.191 l
+81.418 0.279 l
+81.397 0.661 81.33 0.929 81.212 1.087 c
+81.095 1.253 80.911 1.338 80.668 1.338 c
+84.491 1.205 m
+84.402 1.224 84.303 1.235 84.196 1.235 c
+83.862 1.235 83.627 1.051 83.491 0.691 c
+83.491 -2.161 l
+82.844 -2.161 l
+82.844 1.823 l
+83.477 1.823 l
+83.491 1.411 l
+83.667 1.735 83.91 1.897 84.225 1.897 c
+84.333 1.897 84.421 1.874 84.491 1.837 c
+h
+86.49 -2.234 m
+85.99 -2.234 85.607 -2.087 85.343 -1.793 c
+85.079 -1.5 84.946 -1.066 84.946 -0.484 c
+84.946 -0.014 l
+84.946 0.58 85.071 1.047 85.328 1.382 c
+85.593 1.723 85.953 1.897 86.416 1.897 c
+86.875 1.897 87.217 1.742 87.445 1.44 c
+87.68 1.147 87.801 0.684 87.812 0.059 c
+87.812 -0.367 l
+85.593 -0.367 l
+85.593 -0.455 l
+85.593 -0.889 85.67 -1.201 85.828 -1.396 c
+85.993 -1.583 86.225 -1.675 86.519 -1.675 c
+86.714 -1.675 86.887 -1.643 87.033 -1.573 c
+87.18 -1.496 87.317 -1.378 87.445 -1.22 c
+87.783 -1.631 l
+87.496 -2.036 87.066 -2.234 86.49 -2.234 c
+86.416 1.338 m
+86.141 1.338 85.938 1.242 85.813 1.058 c
+85.684 0.871 85.611 0.58 85.593 0.191 c
+87.166 0.191 l
+87.166 0.279 l
+87.144 0.661 87.078 0.929 86.96 1.087 c
+86.842 1.253 86.658 1.338 86.416 1.338 c
+91.957 1.205 m
+91.87 1.224 91.77 1.235 91.664 1.235 c
+91.329 1.235 91.094 1.051 90.958 0.691 c
+90.958 -2.161 l
+90.311 -2.161 l
+90.311 1.823 l
+90.943 1.823 l
+90.958 1.411 l
+91.134 1.735 91.377 1.897 91.693 1.897 c
+91.799 1.897 91.888 1.874 91.957 1.837 c
+h
+93.956 -2.234 m
+93.457 -2.234 93.075 -2.087 92.811 -1.793 c
+92.545 -1.5 92.414 -1.066 92.414 -0.484 c
+92.414 -0.014 l
+92.414 0.58 92.538 1.047 92.795 1.382 c
+93.06 1.723 93.42 1.897 93.883 1.897 c
+94.342 1.897 94.684 1.742 94.912 1.44 c
+95.147 1.147 95.269 0.684 95.279 0.059 c
+95.279 -0.367 l
+93.06 -0.367 l
+93.06 -0.455 l
+93.06 -0.889 93.137 -1.201 93.295 -1.396 c
+93.461 -1.583 93.692 -1.675 93.986 -1.675 c
+94.181 -1.675 94.353 -1.643 94.5 -1.573 c
+94.648 -1.496 94.783 -1.378 94.912 -1.22 c
+95.25 -1.631 l
+94.964 -2.036 94.534 -2.234 93.956 -2.234 c
+93.883 1.338 m
+93.607 1.338 93.405 1.242 93.281 1.058 c
+93.152 0.871 93.079 0.58 93.06 0.191 c
+94.633 0.191 l
+94.633 0.279 l
+94.611 0.661 94.544 0.929 94.427 1.087 c
+94.309 1.253 94.126 1.338 93.883 1.338 c
+96.676 1.823 m
+96.69 1.455 l
+96.933 1.75 97.253 1.897 97.646 1.897 c
+98.087 1.897 98.396 1.698 98.572 1.309 c
+98.826 1.698 99.175 1.897 99.616 1.897 c
+100.351 1.897 100.726 1.434 100.748 0.515 c
+100.748 -2.161 l
+100.101 -2.161 l
+100.101 0.456 l
+100.101 0.75 100.046 0.963 99.939 1.103 c
+99.84 1.239 99.668 1.309 99.425 1.309 c
+99.226 1.309 99.065 1.228 98.939 1.073 c
+98.822 0.927 98.752 0.735 98.733 0.5 c
+98.733 -2.161 l
+98.072 -2.161 l
+98.072 0.485 l
+98.072 1.033 97.852 1.309 97.411 1.309 c
+97.077 1.309 96.842 1.147 96.705 0.823 c
+96.705 -2.161 l
+96.059 -2.161 l
+96.059 1.823 l
+h
+101.586 0.015 m
+101.586 0.592 101.721 1.047 101.997 1.382 c
+102.28 1.723 102.651 1.897 103.114 1.897 c
+103.574 1.897 103.941 1.727 104.216 1.397 c
+104.499 1.073 104.647 0.625 104.657 0.059 c
+104.657 -0.367 l
+104.657 -0.937 104.514 -1.392 104.231 -1.735 c
+103.955 -2.069 103.589 -2.234 103.129 -2.234 c
+102.666 -2.234 102.295 -2.072 102.012 -1.749 c
+101.736 -1.419 101.592 -0.977 101.586 -0.426 c
+h
+102.232 -0.367 m
+102.232 -0.771 102.309 -1.087 102.467 -1.323 c
+102.633 -1.558 102.853 -1.675 103.129 -1.675 c
+103.695 -1.675 103.988 -1.264 104.011 -0.441 c
+104.011 0.015 l
+104.011 0.416 103.926 0.735 103.761 0.97 c
+103.603 1.213 103.386 1.338 103.114 1.338 c
+102.849 1.338 102.633 1.213 102.467 0.97 c
+102.309 0.735 102.232 0.416 102.232 0.015 c
+h
+106.568 -1.176 m
+107.318 1.823 l
+107.979 1.823 l
+106.803 -2.161 l
+106.319 -2.161 l
+105.128 1.823 l
+105.789 1.823 l
+h
+110.037 -2.234 m
+109.538 -2.234 109.155 -2.087 108.891 -1.793 c
+108.626 -1.5 108.494 -1.066 108.494 -0.484 c
+108.494 -0.014 l
+108.494 0.58 108.619 1.047 108.876 1.382 c
+109.141 1.723 109.501 1.897 109.964 1.897 c
+110.423 1.897 110.765 1.742 110.993 1.44 c
+111.228 1.147 111.35 0.684 111.36 0.059 c
+111.36 -0.367 l
+109.141 -0.367 l
+109.141 -0.455 l
+109.141 -0.889 109.218 -1.201 109.376 -1.396 c
+109.542 -1.583 109.773 -1.675 110.066 -1.675 c
+110.261 -1.675 110.434 -1.643 110.581 -1.573 c
+110.728 -1.496 110.864 -1.378 110.993 -1.22 c
+111.331 -1.631 l
+111.044 -2.036 110.614 -2.234 110.037 -2.234 c
+109.964 1.338 m
+109.688 1.338 109.486 1.242 109.361 1.058 c
+109.233 0.871 109.159 0.58 109.141 0.191 c
+110.714 0.191 l
+110.714 0.279 l
+110.691 0.661 110.625 0.929 110.508 1.087 c
+110.39 1.253 110.207 1.338 109.964 1.338 c
+112.007 0.015 m
+112.007 0.621 112.117 1.087 112.345 1.411 c
+112.58 1.735 112.908 1.897 113.33 1.897 c
+113.712 1.897 114.01 1.738 114.226 1.426 c
+114.226 3.484 l
+114.874 3.484 l
+114.874 -2.161 l
+114.286 -2.161 l
+114.242 -1.735 l
+114.035 -2.069 113.731 -2.234 113.33 -2.234 c
+112.918 -2.234 112.595 -2.08 112.36 -1.764 c
+112.125 -1.44 112.007 -0.985 112.007 -0.397 c
+h
+112.654 -0.367 m
+112.654 -0.808 112.717 -1.139 112.844 -1.352 c
+112.981 -1.558 113.201 -1.66 113.507 -1.66 c
+113.829 -1.66 114.068 -1.5 114.226 -1.176 c
+114.226 0.838 l
+114.057 1.151 113.819 1.309 113.507 1.309 c
+113.201 1.309 112.981 1.205 112.844 1 c
+112.717 0.794 112.654 0.47 112.654 0.029 c
+h
+117.887 -2.161 m
+117.887 1.294 l
+117.357 1.294 l
+117.357 1.823 l
+117.887 1.823 l
+117.887 2.278 l
+117.887 2.679 117.982 2.992 118.18 3.219 c
+118.386 3.443 118.666 3.557 119.019 3.557 c
+119.154 3.557 119.287 3.535 119.416 3.499 c
+119.386 2.955 l
+119.287 2.973 119.188 2.984 119.092 2.984 c
+118.718 2.984 118.533 2.72 118.533 2.19 c
+118.533 1.823 l
+119.21 1.823 l
+119.21 1.294 l
+118.533 1.294 l
+118.533 -2.161 l
+h
+121.62 1.205 m
+121.532 1.224 121.433 1.235 121.326 1.235 c
+120.992 1.235 120.757 1.051 120.621 0.691 c
+120.621 -2.161 l
+119.973 -2.161 l
+119.973 1.823 l
+120.606 1.823 l
+120.621 1.411 l
+120.798 1.735 121.039 1.897 121.355 1.897 c
+121.463 1.897 121.55 1.874 121.62 1.837 c
+h
+122.061 0.015 m
+122.061 0.592 122.197 1.047 122.473 1.382 c
+122.756 1.723 123.127 1.897 123.59 1.897 c
+124.05 1.897 124.417 1.727 124.692 1.397 c
+124.975 1.073 125.122 0.625 125.133 0.059 c
+125.133 -0.367 l
+125.133 -0.937 124.99 -1.392 124.707 -1.735 c
+124.432 -2.069 124.064 -2.234 123.605 -2.234 c
+123.142 -2.234 122.77 -2.072 122.487 -1.749 c
+122.211 -1.419 122.068 -0.977 122.061 -0.426 c
+h
+122.708 -0.367 m
+122.708 -0.771 122.785 -1.087 122.943 -1.323 c
+123.109 -1.558 123.329 -1.675 123.605 -1.675 c
+124.17 -1.675 124.465 -1.264 124.486 -0.441 c
+124.486 0.015 l
+124.486 0.416 124.402 0.735 124.237 0.97 c
+124.079 1.213 123.862 1.338 123.59 1.338 c
+123.325 1.338 123.109 1.213 122.943 0.97 c
+122.785 0.735 122.708 0.416 122.708 0.015 c
+h
+126.589 1.823 m
+126.603 1.455 l
+126.846 1.75 127.165 1.897 127.559 1.897 c
+128 1.897 128.308 1.698 128.484 1.309 c
+128.738 1.698 129.087 1.897 129.529 1.897 c
+130.263 1.897 130.638 1.434 130.66 0.515 c
+130.66 -2.161 l
+130.013 -2.161 l
+130.013 0.456 l
+130.013 0.75 129.958 0.963 129.851 1.103 c
+129.752 1.239 129.579 1.309 129.338 1.309 c
+129.139 1.309 128.977 1.228 128.852 1.073 c
+128.735 0.927 128.665 0.735 128.646 0.5 c
+128.646 -2.161 l
+127.985 -2.161 l
+127.985 0.485 l
+127.985 1.033 127.765 1.309 127.323 1.309 c
+126.989 1.309 126.754 1.147 126.618 0.823 c
+126.618 -2.161 l
+125.972 -2.161 l
+125.972 1.823 l
+h
+134.203 2.778 m
+134.203 1.823 l
+134.805 1.823 l
+134.805 1.294 l
+134.203 1.294 l
+134.203 -1.176 l
+134.203 -1.334 134.225 -1.452 134.276 -1.529 c
+134.335 -1.61 134.423 -1.646 134.541 -1.646 c
+134.629 -1.646 134.717 -1.631 134.805 -1.602 c
+134.805 -2.161 l
+134.658 -2.209 134.504 -2.234 134.35 -2.234 c
+134.092 -2.234 133.897 -2.142 133.762 -1.955 c
+133.622 -1.771 133.556 -1.51 133.556 -1.176 c
+133.556 1.294 l
+132.953 1.294 l
+132.953 1.823 l
+133.556 1.823 l
+133.556 2.778 l
+h
+136.216 1.411 m
+136.47 1.735 136.79 1.897 137.172 1.897 c
+137.878 1.897 138.234 1.426 138.244 0.485 c
+138.244 -2.161 l
+137.598 -2.161 l
+137.598 0.456 l
+137.598 0.769 137.543 0.989 137.436 1.118 c
+137.326 1.242 137.172 1.309 136.966 1.309 c
+136.808 1.309 136.661 1.253 136.525 1.147 c
+136.397 1.037 136.293 0.9 136.216 0.735 c
+136.216 -2.161 l
+135.57 -2.161 l
+135.57 3.484 l
+136.216 3.484 l
+h
+140.626 -2.234 m
+140.126 -2.234 139.744 -2.087 139.48 -1.793 c
+139.215 -1.5 139.083 -1.066 139.083 -0.484 c
+139.083 -0.014 l
+139.083 0.58 139.208 1.047 139.465 1.382 c
+139.729 1.723 140.089 1.897 140.553 1.897 c
+141.012 1.897 141.354 1.742 141.581 1.44 c
+141.816 1.147 141.938 0.684 141.949 0.059 c
+141.949 -0.367 l
+139.729 -0.367 l
+139.729 -0.455 l
+139.729 -0.889 139.807 -1.201 139.964 -1.396 c
+140.13 -1.583 140.361 -1.675 140.656 -1.675 c
+140.85 -1.675 141.023 -1.643 141.17 -1.573 c
+141.317 -1.496 141.453 -1.378 141.581 -1.22 c
+141.92 -1.631 l
+141.633 -2.036 141.203 -2.234 140.626 -2.234 c
+140.553 1.338 m
+140.277 1.338 140.075 1.242 139.95 1.058 c
+139.821 0.871 139.748 0.58 139.729 0.191 c
+141.302 0.191 l
+141.302 0.279 l
+141.28 0.661 141.214 0.929 141.097 1.087 c
+140.979 1.253 140.795 1.338 140.553 1.338 c
+f
+Q
+q 1 0 0 1 216.9476 230.3099 cm
+0 0 m
+-0.338 0.029 l
+-0.625 0.029 -0.816 -0.096 -0.912 -0.339 c
+-0.912 -2.97 l
+-1.955 -2.97 l
+-1.955 1.014 l
+-0.985 1.014 l
+-0.956 0.573 l
+-0.79 0.914 -0.559 1.087 -0.264 1.087 c
+-0.147 1.087 -0.055 1.065 0.014 1.028 c
+h
+2.072 -3.043 m
+1.544 -3.043 1.124 -2.889 0.823 -2.573 c
+0.529 -2.249 0.382 -1.79 0.382 -1.191 c
+0.382 -0.882 l
+0.382 -0.258 0.518 0.228 0.794 0.573 c
+1.066 0.914 1.459 1.087 1.97 1.087 c
+2.469 1.087 2.84 0.926 3.087 0.602 c
+3.341 0.278 3.472 -0.199 3.484 -0.823 c
+3.484 -1.324 l
+1.411 -1.324 l
+1.429 -1.617 1.492 -1.834 1.602 -1.97 c
+1.72 -2.11 1.899 -2.176 2.146 -2.176 c
+2.487 -2.176 2.778 -2.058 3.013 -1.823 c
+3.425 -2.455 l
+3.296 -2.631 3.109 -2.775 2.866 -2.881 c
+2.62 -2.988 2.356 -3.043 2.072 -3.043 c
+1.425 -0.603 m
+2.454 -0.603 l
+2.454 -0.5 l
+2.454 -0.265 2.414 -0.088 2.337 0.029 c
+2.267 0.154 2.138 0.22 1.955 0.22 c
+1.779 0.22 1.646 0.151 1.558 0.014 c
+1.477 -0.115 1.433 -0.32 1.425 -0.603 c
+5.041 1.014 m
+5.072 0.646 l
+5.307 0.941 5.615 1.087 5.997 1.087 c
+6.398 1.087 6.677 0.903 6.834 0.544 c
+7.07 0.903 7.397 1.087 7.819 1.087 c
+8.515 1.087 8.867 0.602 8.878 -0.368 c
+8.878 -2.97 l
+7.85 -2.97 l
+7.85 -0.427 l
+7.85 -0.202 7.813 -0.04 7.746 0.058 c
+7.688 0.154 7.578 0.205 7.423 0.205 c
+7.225 0.205 7.085 0.087 6.996 -0.148 c
+6.996 -2.97 l
+5.953 -2.97 l
+5.953 -0.441 l
+5.953 -0.206 5.924 -0.04 5.865 0.058 c
+5.806 0.154 5.696 0.205 5.542 0.205 c
+5.365 0.205 5.222 0.11 5.115 -0.074 c
+5.115 -2.97 l
+4.072 -2.97 l
+4.072 1.014 l
+h
+9.54 -0.853 m
+9.54 -0.246 9.679 0.228 9.966 0.573 c
+10.248 0.914 10.642 1.087 11.142 1.087 c
+11.649 1.087 12.046 0.914 12.332 0.573 c
+12.615 0.228 12.758 -0.246 12.758 -0.853 c
+12.758 -1.118 l
+12.758 -1.717 12.615 -2.187 12.332 -2.529 c
+12.046 -2.874 11.649 -3.043 11.142 -3.043 c
+10.631 -3.043 10.234 -2.874 9.951 -2.529 c
+9.676 -2.187 9.54 -1.713 9.54 -1.103 c
+h
+10.583 -1.118 m
+10.583 -1.823 10.767 -2.176 11.142 -2.176 c
+11.495 -2.176 11.686 -1.881 11.715 -1.294 c
+11.715 -0.853 l
+11.715 -0.493 11.663 -0.221 11.568 -0.044 c
+11.469 0.132 11.326 0.22 11.142 0.22 c
+10.965 0.22 10.826 0.132 10.73 -0.044 c
+10.631 -0.221 10.583 -0.493 10.583 -0.853 c
+h
+14.611 1.984 m
+14.611 1.014 l
+15.14 1.014 l
+15.14 0.22 l
+14.611 0.22 l
+14.611 -1.75 l
+14.611 -1.908 14.629 -2.014 14.67 -2.073 c
+14.717 -2.132 14.802 -2.161 14.919 -2.161 c
+15.026 -2.161 15.11 -2.154 15.169 -2.132 c
+15.169 -2.94 l
+14.993 -3.007 14.802 -3.043 14.596 -3.043 c
+13.92 -3.043 13.575 -2.658 13.567 -1.881 c
+13.567 0.22 l
+13.111 0.22 l
+13.111 1.014 l
+13.567 1.014 l
+13.567 1.984 l
+h
+17.315 -3.043 m
+16.787 -3.043 16.367 -2.889 16.066 -2.573 c
+15.771 -2.249 15.625 -1.79 15.625 -1.191 c
+15.625 -0.882 l
+15.625 -0.258 15.761 0.228 16.037 0.573 c
+16.309 0.914 16.702 1.087 17.213 1.087 c
+17.712 1.087 18.084 0.926 18.329 0.602 c
+18.583 0.278 18.715 -0.199 18.726 -0.823 c
+18.726 -1.324 l
+16.654 -1.324 l
+16.673 -1.617 16.735 -1.834 16.845 -1.97 c
+16.962 -2.11 17.143 -2.176 17.389 -2.176 c
+17.731 -2.176 18.021 -2.058 18.256 -1.823 c
+18.668 -2.455 l
+18.539 -2.631 18.352 -2.775 18.109 -2.881 c
+17.863 -2.988 17.598 -3.043 17.315 -3.043 c
+16.669 -0.603 m
+17.697 -0.603 l
+17.697 -0.5 l
+17.697 -0.265 17.657 -0.088 17.58 0.029 c
+17.51 0.154 17.381 0.22 17.198 0.22 c
+17.022 0.22 16.889 0.151 16.801 0.014 c
+16.72 -0.115 16.676 -0.32 16.669 -0.603 c
+f
+Q
+q 1 0 0 1 239.7314 230.706 cm
+0 0 m
+-0.088 0.019 -0.188 0.03 -0.294 0.03 c
+-0.628 0.03 -0.864 -0.154 -1 -0.514 c
+-1 -3.366 l
+-1.646 -3.366 l
+-1.646 0.618 l
+-1.014 0.618 l
+-1 0.206 l
+-0.823 0.53 -0.581 0.691 -0.265 0.691 c
+-0.158 0.691 -0.071 0.669 0 0.632 c
+h
+1.999 -3.439 m
+1.499 -3.439 1.117 -3.293 0.852 -2.998 c
+0.588 -2.705 0.455 -2.271 0.455 -1.69 c
+0.455 -1.219 l
+0.455 -0.625 0.58 -0.158 0.837 0.177 c
+1.103 0.518 1.462 0.691 1.925 0.691 c
+2.385 0.691 2.726 0.537 2.954 0.235 c
+3.189 -0.058 3.31 -0.521 3.322 -1.146 c
+3.322 -1.572 l
+1.103 -1.572 l
+1.103 -1.66 l
+1.103 -2.094 1.18 -2.406 1.338 -2.601 c
+1.502 -2.788 1.734 -2.881 2.028 -2.881 c
+2.223 -2.881 2.395 -2.848 2.543 -2.778 c
+2.69 -2.701 2.826 -2.583 2.954 -2.425 c
+3.293 -2.836 l
+3.006 -3.241 2.576 -3.439 1.999 -3.439 c
+1.925 0.133 m
+1.65 0.133 1.448 0.037 1.323 -0.147 c
+1.194 -0.334 1.12 -0.625 1.103 -1.014 c
+2.675 -1.014 l
+2.675 -0.926 l
+2.653 -0.544 2.587 -0.276 2.469 -0.118 c
+2.352 0.048 2.167 0.133 1.925 0.133 c
+6.982 -1.572 m
+6.982 -2.2 6.864 -2.671 6.628 -2.983 c
+6.401 -3.289 6.085 -3.439 5.674 -3.439 c
+5.269 -3.439 4.961 -3.289 4.747 -2.983 c
+4.747 -4.895 l
+4.101 -4.895 l
+4.101 0.618 l
+4.689 0.618 l
+4.733 0.177 l
+4.945 0.518 5.255 0.691 5.659 0.691 c
+6.1 0.691 6.427 0.537 6.644 0.235 c
+6.857 -0.07 6.971 -0.525 6.982 -1.132 c
+h
+6.335 -1.19 m
+6.335 -0.749 6.265 -0.426 6.129 -0.22 c
+5.99 -0.007 5.769 0.103 5.468 0.103 c
+5.151 0.103 4.913 -0.051 4.747 -0.353 c
+4.747 -2.425 l
+4.913 -2.73 5.151 -2.881 5.468 -2.881 c
+5.762 -2.881 5.975 -2.778 6.115 -2.572 c
+6.25 -2.358 6.324 -2.028 6.335 -1.587 c
+h
+7.688 -1.19 m
+7.688 -0.613 7.823 -0.158 8.099 0.177 c
+8.382 0.518 8.753 0.691 9.215 0.691 c
+9.675 0.691 10.042 0.522 10.318 0.192 c
+10.601 -0.132 10.748 -0.58 10.759 -1.146 c
+10.759 -1.572 l
+10.759 -2.142 10.616 -2.597 10.333 -2.94 c
+10.057 -3.274 9.69 -3.439 9.231 -3.439 c
+8.768 -3.439 8.396 -3.278 8.114 -2.954 c
+7.838 -2.624 7.694 -2.183 7.688 -1.631 c
+h
+8.334 -1.572 m
+8.334 -1.976 8.411 -2.293 8.569 -2.528 c
+8.735 -2.763 8.955 -2.881 9.231 -2.881 c
+9.797 -2.881 10.09 -2.469 10.113 -1.646 c
+10.113 -1.19 l
+10.113 -0.789 10.028 -0.47 9.863 -0.235 c
+9.705 0.008 9.488 0.133 9.215 0.133 c
+8.951 0.133 8.735 0.008 8.569 -0.235 c
+8.411 -0.47 8.334 -0.789 8.334 -1.19 c
+h
+13.537 -2.352 m
+13.537 -2.204 13.483 -2.083 13.375 -1.984 c
+13.265 -1.888 13.059 -1.77 12.758 -1.631 c
+12.413 -1.484 12.17 -1.363 12.024 -1.263 c
+11.877 -1.157 11.766 -1.04 11.7 -0.911 c
+11.63 -0.786 11.597 -0.628 11.597 -0.44 c
+11.597 -0.118 11.715 0.151 11.95 0.368 c
+12.185 0.581 12.486 0.691 12.862 0.691 c
+13.244 0.691 13.552 0.578 13.787 0.353 c
+14.023 0.125 14.14 -0.161 14.14 -0.514 c
+13.493 -0.514 l
+13.493 -0.338 13.435 -0.187 13.317 -0.058 c
+13.2 0.067 13.045 0.133 12.862 0.133 c
+12.663 0.133 12.512 0.077 12.406 -0.029 c
+12.295 -0.128 12.244 -0.261 12.244 -0.426 c
+12.244 -0.554 12.28 -0.661 12.361 -0.749 c
+12.438 -0.83 12.629 -0.933 12.935 -1.058 c
+13.412 -1.246 13.743 -1.433 13.919 -1.616 c
+14.096 -1.793 14.184 -2.021 14.184 -2.293 c
+14.184 -2.645 14.059 -2.925 13.817 -3.131 c
+13.581 -3.336 13.265 -3.439 12.876 -3.439 c
+12.454 -3.439 12.116 -3.322 11.862 -3.087 c
+11.605 -2.844 11.48 -2.539 11.48 -2.175 c
+12.126 -2.175 l
+12.134 -2.403 12.203 -2.58 12.332 -2.705 c
+12.457 -2.822 12.641 -2.881 12.876 -2.881 c
+13.089 -2.881 13.251 -2.833 13.361 -2.734 c
+13.479 -2.638 13.537 -2.51 13.537 -2.352 c
+15.727 -3.366 -0.646 3.984 re
+15.771 1.661 m
+15.771 1.551 15.742 1.459 15.684 1.382 c
+15.625 1.312 15.529 1.279 15.405 1.279 c
+15.287 1.279 15.191 1.312 15.125 1.382 c
+15.066 1.459 15.037 1.551 15.037 1.661 c
+15.037 1.779 15.066 1.871 15.125 1.941 c
+15.191 2.018 15.287 2.058 15.405 2.058 c
+15.529 2.058 15.625 2.018 15.684 1.941 c
+15.742 1.86 15.771 1.768 15.771 1.661 c
+17.595 1.573 m
+17.595 0.618 l
+18.197 0.618 l
+18.197 0.088 l
+17.595 0.088 l
+17.595 -2.381 l
+17.595 -2.539 17.616 -2.657 17.668 -2.734 c
+17.726 -2.815 17.815 -2.851 17.932 -2.851 c
+18.021 -2.851 18.109 -2.836 18.197 -2.807 c
+18.197 -3.366 l
+18.05 -3.414 17.896 -3.439 17.741 -3.439 c
+17.485 -3.439 17.29 -3.347 17.153 -3.16 c
+17.014 -2.977 16.947 -2.715 16.947 -2.381 c
+16.947 0.088 l
+16.345 0.088 l
+16.345 0.618 l
+16.947 0.618 l
+16.947 1.573 l
+h
+18.756 -1.19 m
+18.756 -0.613 18.892 -0.158 19.168 0.177 c
+19.45 0.518 19.821 0.691 20.284 0.691 c
+20.743 0.691 21.111 0.522 21.387 0.192 c
+21.67 -0.132 21.817 -0.58 21.828 -1.146 c
+21.828 -1.572 l
+21.828 -2.142 21.684 -2.597 21.402 -2.94 c
+21.126 -3.274 20.758 -3.439 20.299 -3.439 c
+19.836 -3.439 19.465 -3.278 19.182 -2.954 c
+18.906 -2.624 18.763 -2.183 18.756 -1.631 c
+h
+19.403 -1.572 m
+19.403 -1.976 19.48 -2.293 19.638 -2.528 c
+19.803 -2.763 20.024 -2.881 20.299 -2.881 c
+20.865 -2.881 21.159 -2.469 21.181 -1.646 c
+21.181 -1.19 l
+21.181 -0.789 21.096 -0.47 20.931 -0.235 c
+20.773 0.008 20.556 0.133 20.284 0.133 c
+20.02 0.133 19.803 0.008 19.638 -0.235 c
+19.48 -0.47 19.403 -0.789 19.403 -1.19 c
+h
+24.312 0 m
+24.224 0.019 24.124 0.03 24.018 0.03 c
+23.683 0.03 23.448 -0.154 23.312 -0.514 c
+23.312 -3.366 l
+22.665 -3.366 l
+22.665 0.618 l
+23.297 0.618 l
+23.312 0.206 l
+23.488 0.53 23.731 0.691 24.047 0.691 c
+24.154 0.691 24.242 0.669 24.312 0.632 c
+h
+26.12 -2.278 m
+26.84 0.618 l
+27.531 0.618 l
+26.237 -3.924 l
+26.138 -4.266 25.995 -4.527 25.811 -4.704 c
+25.635 -4.88 25.433 -4.968 25.208 -4.968 c
+25.121 -4.968 25.007 -4.945 24.87 -4.909 c
+24.87 -4.365 l
+25.017 -4.38 l
+25.201 -4.38 25.348 -4.336 25.458 -4.247 c
+25.565 -4.159 25.653 -4.002 25.723 -3.777 c
+25.84 -3.336 l
+24.679 0.618 l
+25.385 0.618 l
+h
+27.81 -3.013 m
+27.81 -2.896 27.843 -2.8 27.914 -2.719 c
+27.979 -2.642 28.082 -2.601 28.222 -2.601 c
+28.369 -2.601 28.475 -2.642 28.545 -2.719 c
+28.622 -2.8 28.662 -2.896 28.662 -3.013 c
+28.662 -3.123 28.622 -3.215 28.545 -3.293 c
+28.475 -3.37 28.369 -3.41 28.222 -3.41 c
+28.082 -3.41 27.979 -3.37 27.914 -3.293 c
+27.843 -3.215 27.81 -3.123 27.81 -3.013 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 221.732 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 214.894 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.243 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.302 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.683 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.991 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.437 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.941 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.028 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.89 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.245 1.932 5.376 2.05 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.635 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.462 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.874 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.177 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.116 l
+14.497 3.116 l
+14.497 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.456 14.556 -0.515 c
+14.593 -0.566 14.648 -0.611 14.718 -0.647 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.688 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.603 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.279 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.309 14.835 -1.309 c
+14.648 -1.309 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.085 13.85 -1.025 13.791 -0.956 c
+13.733 -0.879 13.689 -0.802 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.456 c
+13.59 -0.36 13.586 -0.264 13.586 -0.177 c
+h
+21.986 2.219 m
+21.994 2.198 22.001 2.165 22.001 2.117 c
+22.009 2.076 22.016 2.028 22.016 1.97 c
+22.023 1.918 22.03 1.866 22.03 1.808 c
+22.03 1.646 l
+22.045 1.646 l
+22.104 1.764 22.17 1.859 22.251 1.94 c
+22.328 2.017 22.413 2.08 22.501 2.131 c
+22.589 2.19 22.677 2.227 22.765 2.248 c
+22.861 2.267 22.961 2.278 23.059 2.278 c
+23.265 2.278 23.445 2.234 23.603 2.146 c
+23.757 2.057 23.886 1.929 23.985 1.764 c
+24.092 1.606 24.169 1.415 24.22 1.19 c
+24.28 0.974 24.309 0.738 24.309 0.484 c
+24.309 0.22 24.28 -0.026 24.22 -0.25 c
+24.169 -0.467 24.092 -0.658 23.985 -0.823 c
+23.886 -0.981 23.754 -1.103 23.589 -1.191 c
+23.431 -1.279 23.243 -1.323 23.03 -1.323 c
+22.931 -1.323 22.832 -1.312 22.736 -1.294 c
+22.637 -1.272 22.545 -1.243 22.456 -1.191 c
+22.375 -1.143 22.298 -1.081 22.221 -1 c
+22.152 -0.923 22.093 -0.831 22.045 -0.721 c
+22.03 -0.721 l
+22.03 -0.809 l
+22.038 -0.849 22.045 -0.897 22.045 -0.956 c
+22.045 -1.118 l
+22.045 -1.294 l
+22.045 -2.631 l
+21.134 -2.631 l
+21.134 1.455 l
+21.134 1.62 21.126 1.768 21.119 1.896 c
+21.119 2.219 l
+h
+22.03 0.455 m
+22.03 0.228 22.049 0.037 22.089 -0.118 c
+22.137 -0.264 22.192 -0.382 22.251 -0.47 c
+22.317 -0.559 22.391 -0.625 22.471 -0.661 c
+22.549 -0.702 22.626 -0.721 22.707 -0.721 c
+22.803 -0.721 22.89 -0.698 22.971 -0.647 c
+23.059 -0.599 23.125 -0.53 23.177 -0.441 c
+23.235 -0.345 23.28 -0.221 23.31 -0.073 c
+23.346 0.081 23.368 0.268 23.368 0.484 c
+23.368 0.874 23.31 1.168 23.192 1.367 c
+23.081 1.562 22.927 1.66 22.722 1.66 c
+22.641 1.66 22.564 1.639 22.486 1.602 c
+22.406 1.562 22.332 1.5 22.265 1.411 c
+22.196 1.323 22.137 1.198 22.089 1.043 c
+22.049 0.885 22.03 0.691 22.03 0.455 c
+26.061 2.219 m
+26.061 0.264 l
+26.061 0.125 26.069 0 26.091 -0.118 c
+26.109 -0.228 26.142 -0.32 26.194 -0.397 c
+26.242 -0.478 26.3 -0.54 26.371 -0.588 c
+26.437 -0.628 26.521 -0.647 26.62 -0.647 c
+26.709 -0.647 26.789 -0.628 26.87 -0.588 c
+26.959 -0.54 27.032 -0.47 27.09 -0.382 c
+27.15 -0.287 27.194 -0.177 27.223 -0.059 c
+27.26 0.066 27.281 0.206 27.281 0.353 c
+27.281 2.219 l
+28.178 2.219 l
+28.178 -0.485 l
+28.178 -0.721 l
+28.185 -0.802 28.193 -0.879 28.193 -0.956 c
+28.193 -1.147 l
+28.201 -1.199 28.208 -1.235 28.208 -1.264 c
+27.355 -1.264 l
+27.344 -1.235 27.333 -1.199 27.326 -1.147 c
+27.326 -0.956 l
+27.326 -0.89 27.318 -0.819 27.312 -0.75 c
+27.312 -0.574 l
+27.296 -0.574 l
+27.179 -0.838 27.025 -1.029 26.841 -1.147 c
+26.664 -1.264 26.462 -1.323 26.238 -1.323 c
+26.032 -1.323 25.86 -1.286 25.724 -1.22 c
+25.584 -1.154 25.473 -1.058 25.386 -0.941 c
+25.305 -0.823 25.246 -0.688 25.209 -0.53 c
+25.18 -0.364 25.165 -0.187 25.165 0 c
+25.165 2.219 l
+h
+31.343 -0.647 m
+32.474 -0.647 l
+32.474 -1.264 l
+29.167 -1.264 l
+29.167 -0.647 l
+30.431 -0.647 l
+30.431 2.896 l
+29.505 2.896 l
+29.505 3.513 l
+31.343 3.513 l
+h
+35.407 -0.647 m
+36.538 -0.647 l
+36.538 -1.264 l
+33.232 -1.264 l
+33.232 -0.647 l
+34.495 -0.647 l
+34.495 2.896 l
+33.569 2.896 l
+33.569 3.513 l
+35.407 3.513 l
+h
+42.175 -2.631 m
+42.175 3.513 l
+44.101 3.513 l
+44.101 2.896 l
+43.028 2.896 l
+43.028 -2.014 l
+44.101 -2.014 l
+44.101 -2.631 l
+h
+48.5 1.469 m
+48.4 1.477 48.298 1.488 48.191 1.5 c
+48.081 1.517 47.96 1.529 47.823 1.529 c
+47.648 1.529 47.489 1.488 47.353 1.411 c
+47.214 1.341 47.096 1.242 47 1.117 c
+46.912 0.989 46.842 0.841 46.794 0.676 c
+46.754 0.507 46.736 0.33 46.736 0.147 c
+46.736 -1.264 l
+45.839 -1.264 l
+45.839 0.985 l
+45.839 1.109 45.828 1.234 45.81 1.352 c
+45.799 1.477 45.784 1.595 45.766 1.705 c
+45.755 1.822 45.74 1.918 45.722 1.999 c
+45.699 2.087 45.681 2.161 45.663 2.219 c
+46.545 2.219 l
+46.552 2.167 46.563 2.117 46.574 2.057 c
+46.593 1.999 46.607 1.932 46.618 1.866 c
+46.637 1.808 46.651 1.741 46.663 1.675 c
+46.67 1.606 46.68 1.543 46.692 1.484 c
+46.707 1.484 l
+46.743 1.602 46.794 1.708 46.854 1.808 c
+46.919 1.903 47 1.988 47.089 2.057 c
+47.177 2.124 47.28 2.179 47.397 2.219 c
+47.522 2.256 47.669 2.278 47.838 2.278 c
+47.964 2.278 48.081 2.271 48.191 2.263 c
+48.309 2.252 48.411 2.238 48.5 2.219 c
+h
+51.106 -1.323 m
+50.848 -1.323 50.62 -1.286 50.414 -1.22 c
+50.208 -1.143 50.032 -1.029 49.886 -0.882 c
+49.738 -0.728 49.62 -0.537 49.533 -0.309 c
+49.452 -0.085 49.415 0.18 49.415 0.484 c
+49.415 0.816 49.459 1.095 49.547 1.323 c
+49.643 1.558 49.772 1.741 49.93 1.881 c
+50.094 2.017 50.282 2.117 50.488 2.175 c
+50.694 2.242 50.903 2.278 51.12 2.278 c
+51.392 2.278 51.627 2.227 51.825 2.131 c
+52.031 2.043 52.197 1.911 52.325 1.735 c
+52.461 1.565 52.56 1.359 52.619 1.117 c
+52.685 0.881 52.722 0.617 52.722 0.324 c
+52.722 0.309 l
+50.356 0.309 l
+50.356 0.162 50.37 0.022 50.4 -0.103 c
+50.437 -0.231 50.491 -0.345 50.561 -0.441 c
+50.628 -0.53 50.712 -0.599 50.811 -0.647 c
+50.907 -0.698 51.021 -0.721 51.149 -0.721 c
+51.303 -0.721 51.443 -0.688 51.561 -0.617 c
+51.686 -0.551 51.774 -0.449 51.825 -0.309 c
+52.664 -0.382 l
+52.633 -0.482 52.579 -0.588 52.502 -0.706 c
+52.421 -0.816 52.318 -0.919 52.193 -1.014 c
+52.076 -1.103 51.921 -1.176 51.737 -1.235 c
+51.561 -1.294 51.347 -1.323 51.106 -1.323 c
+51.106 1.705 m
+51.017 1.705 50.929 1.691 50.84 1.66 c
+50.752 1.631 50.672 1.58 50.605 1.514 c
+50.536 1.444 50.477 1.356 50.429 1.249 c
+50.389 1.139 50.37 1.014 50.37 0.867 c
+51.84 0.867 l
+51.84 1.003 51.814 1.124 51.767 1.234 c
+51.727 1.341 51.671 1.429 51.605 1.5 c
+51.546 1.565 51.473 1.617 51.384 1.646 c
+51.297 1.683 51.201 1.705 51.106 1.705 c
+54.773 -1.264 m
+54.773 0.852 l
+54.773 1.018 54.765 1.153 54.758 1.264 c
+54.747 1.371 54.728 1.455 54.699 1.514 c
+54.677 1.58 54.648 1.631 54.611 1.66 c
+54.582 1.691 54.541 1.705 54.493 1.705 c
+54.435 1.705 54.379 1.675 54.331 1.617 c
+54.291 1.565 54.258 1.492 54.229 1.396 c
+54.2 1.308 54.173 1.194 54.156 1.058 c
+54.144 0.918 54.14 0.768 54.14 0.602 c
+54.14 -1.264 l
+53.391 -1.264 l
+53.391 1.469 l
+53.391 1.705 l
+53.391 1.926 l
+53.391 2.003 53.383 2.065 53.377 2.117 c
+53.377 2.219 l
+54.052 2.219 l
+54.052 2.131 l
+54.052 1.984 l
+54.06 1.926 54.067 1.866 54.067 1.808 c
+54.067 1.646 l
+54.082 1.646 l
+54.1 1.735 54.129 1.812 54.17 1.881 c
+54.206 1.959 54.251 2.028 54.302 2.087 c
+54.361 2.146 54.427 2.19 54.508 2.219 c
+54.585 2.256 54.673 2.278 54.773 2.278 c
+54.956 2.278 55.096 2.223 55.184 2.117 c
+55.28 2.017 55.349 1.859 55.39 1.646 c
+55.405 1.646 l
+55.442 1.741 55.482 1.83 55.522 1.911 c
+55.57 1.988 55.625 2.05 55.684 2.102 c
+55.743 2.161 55.809 2.205 55.889 2.234 c
+55.966 2.263 56.055 2.278 56.155 2.278 c
+56.29 2.278 56.404 2.252 56.492 2.205 c
+56.581 2.153 56.647 2.08 56.698 1.984 c
+56.757 1.885 56.793 1.756 56.816 1.602 c
+56.845 1.455 56.86 1.271 56.86 1.058 c
+56.86 -1.264 l
+56.14 -1.264 l
+56.14 0.852 l
+56.14 1.018 56.132 1.153 56.125 1.264 c
+56.114 1.371 56.095 1.455 56.066 1.514 c
+56.045 1.58 56.014 1.631 55.978 1.66 c
+55.949 1.691 55.908 1.705 55.86 1.705 c
+55.743 1.705 55.648 1.617 55.581 1.44 c
+55.522 1.271 55.493 1.014 55.493 0.661 c
+55.493 -1.264 l
+h
+60.876 0.484 m
+60.876 0.209 60.839 -0.04 60.774 -0.264 c
+60.704 -0.482 60.601 -0.669 60.465 -0.823 c
+60.325 -0.981 60.149 -1.103 59.935 -1.191 c
+59.719 -1.279 59.465 -1.323 59.172 -1.323 c
+58.896 -1.323 58.649 -1.279 58.436 -1.191 c
+58.231 -1.103 58.058 -0.981 57.922 -0.823 c
+57.782 -0.669 57.68 -0.482 57.613 -0.264 c
+57.543 -0.04 57.51 0.209 57.51 0.484 c
+57.51 0.738 57.539 0.974 57.599 1.19 c
+57.665 1.415 57.768 1.606 57.907 1.764 c
+58.043 1.929 58.22 2.057 58.436 2.146 c
+58.649 2.234 58.906 2.278 59.201 2.278 c
+59.513 2.278 59.774 2.234 59.98 2.146 c
+60.193 2.057 60.365 1.929 60.494 1.764 c
+60.63 1.606 60.729 1.415 60.788 1.19 c
+60.847 0.974 60.876 0.738 60.876 0.484 c
+59.921 0.484 m
+59.921 0.691 59.906 0.867 59.877 1.014 c
+59.855 1.161 59.818 1.282 59.76 1.382 c
+59.7 1.477 59.627 1.547 59.538 1.587 c
+59.451 1.635 59.34 1.66 59.216 1.66 c
+58.95 1.66 58.76 1.562 58.642 1.367 c
+58.524 1.18 58.466 0.885 58.466 0.484 c
+58.466 0.062 58.524 -0.243 58.642 -0.426 c
+58.76 -0.614 58.936 -0.706 59.172 -0.706 c
+59.297 -0.706 59.411 -0.688 59.509 -0.647 c
+59.605 -0.599 59.686 -0.526 59.744 -0.426 c
+59.81 -0.331 59.855 -0.206 59.877 -0.059 c
+59.906 0.088 59.921 0.268 59.921 0.484 c
+62.335 1.602 m
+61.792 1.602 l
+61.792 2.219 l
+62.379 2.219 l
+62.659 3.116 l
+63.232 3.116 l
+63.232 2.219 l
+64.467 2.219 l
+64.467 1.602 l
+63.232 1.602 l
+63.232 -0.103 l
+63.232 -0.324 l
+63.239 -0.393 63.261 -0.456 63.291 -0.515 c
+63.328 -0.566 63.382 -0.611 63.452 -0.647 c
+63.53 -0.676 63.644 -0.691 63.791 -0.691 c
+63.926 -0.691 64.062 -0.688 64.202 -0.676 c
+64.338 -0.658 64.471 -0.632 64.599 -0.603 c
+64.599 -1.205 l
+64.518 -1.216 64.441 -1.231 64.364 -1.249 c
+64.283 -1.261 64.205 -1.268 64.128 -1.279 c
+64.047 -1.286 63.96 -1.294 63.864 -1.294 c
+63.776 -1.301 63.677 -1.309 63.57 -1.309 c
+63.382 -1.309 63.221 -1.294 63.085 -1.264 c
+62.956 -1.228 62.842 -1.183 62.746 -1.132 c
+62.659 -1.085 62.586 -1.025 62.526 -0.956 c
+62.468 -0.879 62.424 -0.802 62.395 -0.721 c
+62.364 -0.632 62.343 -0.544 62.335 -0.456 c
+62.324 -0.36 62.32 -0.264 62.32 -0.177 c
+h
+67.351 -1.323 m
+67.094 -1.323 66.866 -1.286 66.661 -1.22 c
+66.455 -1.143 66.278 -1.029 66.131 -0.882 c
+65.984 -0.728 65.867 -0.537 65.778 -0.309 c
+65.697 -0.085 65.661 0.18 65.661 0.484 c
+65.661 0.816 65.705 1.095 65.793 1.323 c
+65.888 1.558 66.017 1.741 66.175 1.881 c
+66.341 2.017 66.528 2.117 66.734 2.175 c
+66.94 2.242 67.149 2.278 67.366 2.278 c
+67.638 2.278 67.873 2.227 68.072 2.131 c
+68.277 2.043 68.443 1.911 68.571 1.735 c
+68.707 1.565 68.807 1.359 68.865 1.117 c
+68.932 0.881 68.968 0.617 68.968 0.324 c
+68.968 0.309 l
+66.602 0.309 l
+66.602 0.162 66.617 0.022 66.646 -0.103 c
+66.682 -0.231 66.738 -0.345 66.808 -0.441 c
+66.873 -0.53 66.958 -0.599 67.058 -0.647 c
+67.153 -0.698 67.267 -0.721 67.396 -0.721 c
+67.55 -0.721 67.689 -0.688 67.807 -0.617 c
+67.932 -0.551 68.02 -0.449 68.072 -0.309 c
+68.909 -0.382 l
+68.88 -0.482 68.824 -0.588 68.747 -0.706 c
+68.666 -0.816 68.564 -0.919 68.439 -1.014 c
+68.321 -1.103 68.167 -1.176 67.983 -1.235 c
+67.807 -1.294 67.594 -1.323 67.351 -1.323 c
+67.351 1.705 m
+67.263 1.705 67.175 1.691 67.087 1.66 c
+66.999 1.631 66.918 1.58 66.852 1.514 c
+66.782 1.444 66.723 1.356 66.675 1.249 c
+66.634 1.139 66.617 1.014 66.617 0.867 c
+68.086 0.867 l
+68.086 1.003 68.061 1.124 68.013 1.234 c
+67.972 1.341 67.918 1.429 67.851 1.5 c
+67.792 1.565 67.719 1.617 67.631 1.646 c
+67.542 1.683 67.447 1.705 67.351 1.705 c
+70.221 -2.631 m
+70.221 -2.014 l
+71.294 -2.014 l
+71.294 2.896 l
+70.221 2.896 l
+70.221 3.513 l
+72.147 3.513 l
+72.147 -2.631 l
+h
+f
+Q
+q 1 0 0 1 46.321 201.1066 cm
+0 0 m
+-1.808 0 l
+-1.808 -2.367 l
+-2.484 -2.367 l
+-2.484 2.983 l
+0.309 2.983 l
+0.309 2.41 l
+-1.808 2.41 l
+-1.808 0.573 l
+0 0.573 l
+h
+2.396 -2.44 m
+1.897 -2.44 1.514 -2.294 1.249 -1.999 c
+0.985 -1.706 0.852 -1.272 0.852 -0.691 c
+0.852 -0.221 l
+0.852 0.374 0.977 0.841 1.234 1.176 c
+1.5 1.517 1.859 1.69 2.323 1.69 c
+2.782 1.69 3.123 1.535 3.351 1.234 c
+3.586 0.941 3.707 0.477 3.719 -0.148 c
+3.719 -0.574 l
+1.5 -0.574 l
+1.5 -0.662 l
+1.5 -1.096 1.577 -1.408 1.735 -1.603 c
+1.899 -1.79 2.132 -1.881 2.425 -1.881 c
+2.62 -1.881 2.793 -1.849 2.94 -1.779 c
+3.087 -1.702 3.223 -1.584 3.351 -1.426 c
+3.69 -1.838 l
+3.403 -2.242 2.973 -2.44 2.396 -2.44 c
+2.323 1.132 m
+2.047 1.132 1.845 1.036 1.72 0.852 c
+1.591 0.665 1.517 0.374 1.5 -0.015 c
+3.072 -0.015 l
+3.072 0.073 l
+3.05 0.455 2.984 0.723 2.866 0.881 c
+2.749 1.047 2.564 1.132 2.323 1.132 c
+5.35 2.572 m
+5.35 1.616 l
+5.953 1.616 l
+5.953 1.087 l
+5.35 1.087 l
+5.35 -1.382 l
+5.35 -1.54 5.373 -1.658 5.424 -1.735 c
+5.483 -1.816 5.571 -1.852 5.689 -1.852 c
+5.776 -1.852 5.865 -1.838 5.953 -1.808 c
+5.953 -2.367 l
+5.806 -2.415 5.652 -2.44 5.498 -2.44 c
+5.24 -2.44 5.045 -2.349 4.91 -2.161 c
+4.77 -1.977 4.704 -1.717 4.704 -1.382 c
+4.704 1.087 l
+4.101 1.087 l
+4.101 1.616 l
+4.704 1.616 l
+4.704 2.572 l
+h
+8.099 -1.881 m
+8.312 -1.881 8.485 -1.819 8.613 -1.691 c
+8.75 -1.555 8.823 -1.364 8.834 -1.118 c
+9.452 -1.118 l
+9.429 -1.5 9.294 -1.819 9.04 -2.073 c
+8.783 -2.319 8.47 -2.44 8.099 -2.44 c
+7.607 -2.44 7.231 -2.29 6.967 -1.985 c
+6.71 -1.673 6.585 -1.206 6.585 -0.588 c
+6.585 -0.148 l
+6.585 0.448 6.71 0.903 6.967 1.219 c
+7.231 1.532 7.607 1.69 8.099 1.69 c
+8.5 1.69 8.819 1.558 9.055 1.294 c
+9.297 1.036 9.429 0.69 9.452 0.249 c
+8.834 0.249 l
+8.812 0.544 8.739 0.764 8.613 0.911 c
+8.496 1.058 8.324 1.132 8.099 1.132 c
+7.805 1.132 7.588 1.032 7.453 0.837 c
+7.312 0.65 7.239 0.341 7.231 -0.088 c
+7.231 -0.603 l
+7.231 -1.073 7.298 -1.408 7.437 -1.603 c
+7.584 -1.79 7.805 -1.881 8.099 -1.881 c
+10.848 1.205 m
+11.102 1.529 11.421 1.69 11.803 1.69 c
+12.509 1.69 12.866 1.219 12.876 0.278 c
+12.876 -2.367 l
+12.23 -2.367 l
+12.23 0.249 l
+12.23 0.562 12.174 0.783 12.068 0.911 c
+11.958 1.036 11.803 1.102 11.597 1.102 c
+11.439 1.102 11.293 1.047 11.156 0.941 c
+11.028 0.83 10.925 0.694 10.848 0.529 c
+10.848 -2.367 l
+10.201 -2.367 l
+10.201 3.278 l
+10.848 3.278 l
+h
+16.933 -1.881 m
+17.146 -1.881 17.319 -1.819 17.448 -1.691 c
+17.583 -1.555 17.657 -1.364 17.668 -1.118 c
+18.285 -1.118 l
+18.264 -1.5 18.127 -1.819 17.874 -2.073 c
+17.616 -2.319 17.304 -2.44 16.933 -2.44 c
+16.44 -2.44 16.066 -2.29 15.802 -1.985 c
+15.544 -1.673 15.419 -1.206 15.419 -0.588 c
+15.419 -0.148 l
+15.419 0.448 15.544 0.903 15.802 1.219 c
+16.066 1.532 16.44 1.69 16.933 1.69 c
+17.334 1.69 17.654 1.558 17.889 1.294 c
+18.131 1.036 18.264 0.69 18.285 0.249 c
+17.668 0.249 l
+17.646 0.544 17.573 0.764 17.448 0.911 c
+17.33 1.058 17.157 1.132 16.933 1.132 c
+16.639 1.132 16.422 1.032 16.286 0.837 c
+16.147 0.65 16.073 0.341 16.066 -0.088 c
+16.066 -0.603 l
+16.066 -1.073 16.132 -1.408 16.272 -1.603 c
+16.419 -1.79 16.639 -1.881 16.933 -1.881 c
+19.682 1.205 m
+19.935 1.529 20.255 1.69 20.637 1.69 c
+21.343 1.69 21.699 1.219 21.71 0.278 c
+21.71 -2.367 l
+21.063 -2.367 l
+21.063 0.249 l
+21.063 0.562 21.009 0.783 20.902 0.911 c
+20.791 1.036 20.637 1.102 20.432 1.102 c
+20.274 1.102 20.126 1.047 19.991 0.941 c
+19.862 0.83 19.759 0.694 19.682 0.529 c
+19.682 -2.367 l
+19.035 -2.367 l
+19.035 3.278 l
+19.682 3.278 l
+h
+24.709 -2.367 m
+24.669 -2.278 24.643 -2.132 24.635 -1.926 c
+24.4 -2.272 24.106 -2.44 23.754 -2.44 c
+23.39 -2.44 23.106 -2.345 22.901 -2.147 c
+22.703 -1.941 22.607 -1.654 22.607 -1.279 c
+22.607 -0.879 22.743 -0.559 23.019 -0.324 c
+23.291 -0.081 23.665 0.043 24.136 0.043 c
+24.621 0.043 l
+24.621 0.47 l
+24.621 0.706 24.566 0.87 24.459 0.97 c
+24.349 1.076 24.187 1.132 23.974 1.132 c
+23.775 1.132 23.614 1.072 23.489 0.955 c
+23.372 0.837 23.312 0.69 23.312 0.514 c
+22.666 0.514 l
+22.666 0.708 22.725 0.9 22.842 1.087 c
+22.967 1.271 23.129 1.418 23.328 1.529 c
+23.534 1.635 23.761 1.69 24.018 1.69 c
+24.419 1.69 24.724 1.587 24.93 1.381 c
+25.142 1.176 25.257 0.881 25.267 0.5 c
+25.267 -1.515 l
+25.267 -1.819 25.304 -2.084 25.385 -2.309 c
+25.385 -2.367 l
+h
+23.842 -1.852 m
+24.007 -1.852 24.158 -1.808 24.297 -1.721 c
+24.444 -1.632 24.551 -1.522 24.621 -1.382 c
+24.621 -0.441 l
+24.253 -0.441 l
+23.937 -0.441 23.694 -0.511 23.518 -0.647 c
+23.342 -0.776 23.254 -0.963 23.254 -1.206 c
+23.254 -1.434 23.298 -1.599 23.386 -1.706 c
+23.474 -1.804 23.625 -1.852 23.842 -1.852 c
+26.884 1.616 m
+26.9 1.176 l
+27.153 1.517 27.476 1.69 27.869 1.69 c
+28.575 1.69 28.932 1.219 28.943 0.278 c
+28.943 -2.367 l
+28.296 -2.367 l
+28.296 0.249 l
+28.296 0.562 28.24 0.783 28.134 0.911 c
+28.024 1.036 27.869 1.102 27.663 1.102 c
+27.505 1.102 27.359 1.047 27.222 0.941 c
+27.094 0.83 26.991 0.694 26.914 0.529 c
+26.914 -2.367 l
+26.267 -2.367 l
+26.267 1.616 l
+h
+29.78 -0.192 m
+29.78 0.426 29.89 0.889 30.119 1.205 c
+30.343 1.529 30.677 1.69 31.118 1.69 c
+31.519 1.69 31.823 1.514 32.029 1.161 c
+32.074 1.616 l
+32.662 1.616 l
+32.662 -2.411 l
+32.662 -2.899 32.533 -3.278 32.279 -3.543 c
+32.022 -3.807 31.669 -3.94 31.22 -3.94 c
+31.022 -3.94 30.802 -3.888 30.559 -3.793 c
+30.313 -3.693 30.133 -3.572 30.015 -3.425 c
+30.28 -2.984 l
+30.545 -3.249 30.842 -3.381 31.176 -3.381 c
+31.713 -3.381 31.989 -3.088 32 -2.5 c
+32 -1.97 l
+31.794 -2.286 31.492 -2.44 31.103 -2.44 c
+30.692 -2.44 30.368 -2.29 30.133 -1.985 c
+29.905 -1.673 29.788 -1.22 29.78 -0.632 c
+h
+30.441 -0.574 m
+30.441 -1.015 30.504 -1.345 30.632 -1.559 c
+30.757 -1.764 30.975 -1.867 31.28 -1.867 c
+31.603 -1.867 31.842 -1.702 32 -1.368 c
+32 0.617 l
+31.831 0.941 31.592 1.102 31.28 1.102 c
+30.985 1.102 30.769 0.999 30.632 0.793 c
+30.504 0.588 30.441 0.264 30.441 -0.177 c
+h
+35.043 -2.44 m
+34.543 -2.44 34.16 -2.294 33.896 -1.999 c
+33.631 -1.706 33.499 -1.272 33.499 -0.691 c
+33.499 -0.221 l
+33.499 0.374 33.624 0.841 33.882 1.176 c
+34.146 1.517 34.506 1.69 34.969 1.69 c
+35.428 1.69 35.77 1.535 35.998 1.234 c
+36.234 0.941 36.354 0.477 36.365 -0.148 c
+36.365 -0.574 l
+34.146 -0.574 l
+34.146 -0.662 l
+34.146 -1.096 34.223 -1.408 34.381 -1.603 c
+34.547 -1.79 34.778 -1.881 35.072 -1.881 c
+35.266 -1.881 35.44 -1.849 35.586 -1.779 c
+35.733 -1.702 35.869 -1.584 35.998 -1.426 c
+36.336 -1.838 l
+36.049 -2.242 35.619 -2.44 35.043 -2.44 c
+34.969 1.132 m
+34.694 1.132 34.491 1.036 34.366 0.852 c
+34.237 0.665 34.164 0.374 34.146 -0.015 c
+35.719 -0.015 l
+35.719 0.073 l
+35.696 0.455 35.631 0.723 35.513 0.881 c
+35.395 1.047 35.212 1.132 34.969 1.132 c
+39.085 -1.353 m
+39.085 -1.206 39.029 -1.085 38.923 -0.985 c
+38.813 -0.89 38.607 -0.772 38.306 -0.632 c
+37.96 -0.485 37.718 -0.364 37.57 -0.265 c
+37.424 -0.158 37.313 -0.04 37.248 0.087 c
+37.177 0.213 37.144 0.371 37.144 0.558 c
+37.144 0.881 37.262 1.15 37.497 1.367 c
+37.732 1.579 38.034 1.69 38.409 1.69 c
+38.79 1.69 39.099 1.576 39.334 1.352 c
+39.569 1.124 39.687 0.837 39.687 0.484 c
+39.041 0.484 l
+39.041 0.661 38.981 0.812 38.864 0.941 c
+38.746 1.065 38.592 1.132 38.409 1.132 c
+38.21 1.132 38.059 1.076 37.953 0.97 c
+37.842 0.87 37.791 0.738 37.791 0.573 c
+37.791 0.444 37.828 0.338 37.909 0.249 c
+37.986 0.168 38.177 0.066 38.482 -0.059 c
+38.96 -0.246 39.291 -0.434 39.467 -0.618 c
+39.643 -0.794 39.731 -1.022 39.731 -1.294 c
+39.731 -1.646 39.606 -1.926 39.364 -2.132 c
+39.129 -2.338 38.813 -2.44 38.424 -2.44 c
+38 -2.44 37.663 -2.323 37.409 -2.087 c
+37.152 -1.845 37.027 -1.54 37.027 -1.176 c
+37.674 -1.176 l
+37.681 -1.405 37.751 -1.58 37.88 -1.706 c
+38.004 -1.823 38.188 -1.881 38.424 -1.881 c
+38.636 -1.881 38.798 -1.834 38.908 -1.735 c
+39.026 -1.64 39.085 -1.511 39.085 -1.353 c
+42.584 -2.367 m
+42.584 1.087 l
+42.054 1.087 l
+42.054 1.616 l
+42.584 1.616 l
+42.584 2.072 l
+42.584 2.472 42.679 2.785 42.877 3.013 c
+43.083 3.237 43.362 3.351 43.715 3.351 c
+43.851 3.351 43.983 3.329 44.111 3.293 c
+44.082 2.748 l
+43.983 2.767 43.884 2.778 43.789 2.778 c
+43.413 2.778 43.23 2.513 43.23 1.984 c
+43.23 1.616 l
+43.906 1.616 l
+43.906 1.087 l
+43.23 1.087 l
+43.23 -2.367 l
+h
+46.316 0.999 m
+46.228 1.018 46.129 1.028 46.023 1.028 c
+45.688 1.028 45.453 0.845 45.317 0.484 c
+45.317 -2.367 l
+44.67 -2.367 l
+44.67 1.616 l
+45.302 1.616 l
+45.317 1.205 l
+45.493 1.529 45.736 1.69 46.052 1.69 c
+46.158 1.69 46.247 1.668 46.316 1.631 c
+h
+46.758 -0.192 m
+46.758 0.386 46.894 0.841 47.169 1.176 c
+47.452 1.517 47.823 1.69 48.286 1.69 c
+48.745 1.69 49.113 1.521 49.389 1.19 c
+49.672 0.866 49.819 0.419 49.83 -0.148 c
+49.83 -0.574 l
+49.83 -1.143 49.686 -1.599 49.404 -1.941 c
+49.128 -2.275 48.76 -2.44 48.301 -2.44 c
+47.838 -2.44 47.467 -2.278 47.184 -1.956 c
+46.908 -1.625 46.765 -1.183 46.758 -0.632 c
+h
+47.404 -0.574 m
+47.404 -0.978 47.482 -1.294 47.639 -1.529 c
+47.805 -1.764 48.026 -1.881 48.301 -1.881 c
+48.867 -1.881 49.161 -1.47 49.183 -0.647 c
+49.183 -0.192 l
+49.183 0.209 49.098 0.529 48.934 0.764 c
+48.775 1.007 48.558 1.132 48.286 1.132 c
+48.022 1.132 47.805 1.007 47.639 0.764 c
+47.482 0.529 47.404 0.209 47.404 -0.192 c
+h
+51.285 1.616 m
+51.299 1.249 l
+51.542 1.543 51.862 1.69 52.255 1.69 c
+52.696 1.69 53.005 1.491 53.181 1.102 c
+53.435 1.491 53.784 1.69 54.224 1.69 c
+54.96 1.69 55.334 1.227 55.357 0.309 c
+55.357 -2.367 l
+54.71 -2.367 l
+54.71 0.249 l
+54.71 0.544 54.655 0.756 54.548 0.897 c
+54.449 1.032 54.276 1.102 54.033 1.102 c
+53.835 1.102 53.674 1.021 53.549 0.866 c
+53.431 0.72 53.362 0.529 53.343 0.294 c
+53.343 -2.367 l
+52.681 -2.367 l
+52.681 0.278 l
+52.681 0.826 52.461 1.102 52.02 1.102 c
+51.685 1.102 51.45 0.941 51.315 0.617 c
+51.315 -2.367 l
+50.667 -2.367 l
+50.667 1.616 l
+h
+58.899 2.572 m
+58.899 1.616 l
+59.502 1.616 l
+59.502 1.087 l
+58.899 1.087 l
+58.899 -1.382 l
+58.899 -1.54 58.921 -1.658 58.972 -1.735 c
+59.031 -1.816 59.12 -1.852 59.237 -1.852 c
+59.325 -1.852 59.413 -1.838 59.502 -1.808 c
+59.502 -2.367 l
+59.355 -2.415 59.201 -2.44 59.046 -2.44 c
+58.789 -2.44 58.594 -2.349 58.458 -2.161 c
+58.318 -1.977 58.252 -1.717 58.252 -1.382 c
+58.252 1.087 l
+57.649 1.087 l
+57.649 1.616 l
+58.252 1.616 l
+58.252 2.572 l
+h
+60.913 1.205 m
+61.166 1.529 61.486 1.69 61.869 1.69 c
+62.574 1.69 62.93 1.219 62.941 0.278 c
+62.941 -2.367 l
+62.295 -2.367 l
+62.295 0.249 l
+62.295 0.562 62.239 0.783 62.133 0.911 c
+62.023 1.036 61.869 1.102 61.663 1.102 c
+61.505 1.102 61.358 1.047 61.221 0.941 c
+61.093 0.83 60.99 0.694 60.913 0.529 c
+60.913 -2.367 l
+60.266 -2.367 l
+60.266 3.278 l
+60.913 3.278 l
+h
+65.322 -2.44 m
+64.823 -2.44 64.441 -2.294 64.176 -1.999 c
+63.911 -1.706 63.779 -1.272 63.779 -0.691 c
+63.779 -0.221 l
+63.779 0.374 63.904 0.841 64.161 1.176 c
+64.426 1.517 64.786 1.69 65.249 1.69 c
+65.709 1.69 66.05 1.535 66.278 1.234 c
+66.513 0.941 66.634 0.477 66.646 -0.148 c
+66.646 -0.574 l
+64.426 -0.574 l
+64.426 -0.662 l
+64.426 -1.096 64.503 -1.408 64.661 -1.603 c
+64.826 -1.79 65.058 -1.881 65.352 -1.881 c
+65.547 -1.881 65.719 -1.849 65.867 -1.779 c
+66.014 -1.702 66.149 -1.584 66.278 -1.426 c
+66.616 -1.838 l
+66.33 -2.242 65.9 -2.44 65.322 -2.44 c
+65.249 1.132 m
+64.973 1.132 64.771 1.036 64.647 0.852 c
+64.518 0.665 64.444 0.374 64.426 -0.015 c
+65.999 -0.015 l
+65.999 0.073 l
+65.977 0.455 65.91 0.723 65.792 0.881 c
+65.675 1.047 65.491 1.132 65.249 1.132 c
+f
+Q
+q 1 0 0 1 117.3177 201.7086 cm
+0 0 m
+-0.339 0.029 l
+-0.625 0.029 -0.817 -0.095 -0.912 -0.338 c
+-0.912 -2.969 l
+-1.956 -2.969 l
+-1.956 1.014 l
+-0.985 1.014 l
+-0.956 0.574 l
+-0.79 0.915 -0.559 1.088 -0.265 1.088 c
+-0.148 1.088 -0.055 1.066 0.014 1.029 c
+h
+2.072 -3.042 m
+1.543 -3.042 1.124 -2.888 0.823 -2.572 c
+0.529 -2.248 0.382 -1.789 0.382 -1.19 c
+0.382 -0.881 l
+0.382 -0.257 0.517 0.228 0.793 0.574 c
+1.065 0.915 1.458 1.088 1.969 1.088 c
+2.469 1.088 2.84 0.927 3.087 0.603 c
+3.34 0.279 3.472 -0.198 3.484 -0.823 c
+3.484 -1.323 l
+1.411 -1.323 l
+1.429 -1.617 1.492 -1.833 1.602 -1.97 c
+1.72 -2.109 1.899 -2.175 2.146 -2.175 c
+2.487 -2.175 2.778 -2.057 3.013 -1.822 c
+3.424 -2.454 l
+3.296 -2.631 3.108 -2.774 2.866 -2.881 c
+2.62 -2.988 2.356 -3.042 2.072 -3.042 c
+1.425 -0.602 m
+2.454 -0.602 l
+2.454 -0.5 l
+2.454 -0.264 2.414 -0.088 2.337 0.029 c
+2.267 0.154 2.138 0.221 1.955 0.221 c
+1.778 0.221 1.646 0.151 1.558 0.015 c
+1.477 -0.114 1.433 -0.32 1.425 -0.602 c
+5.041 1.014 m
+5.071 0.647 l
+5.306 0.941 5.614 1.088 5.997 1.088 c
+6.397 1.088 6.676 0.904 6.834 0.544 c
+7.07 0.904 7.397 1.088 7.819 1.088 c
+8.514 1.088 8.866 0.603 8.878 -0.367 c
+8.878 -2.969 l
+7.849 -2.969 l
+7.849 -0.426 l
+7.849 -0.202 7.812 -0.04 7.746 0.059 c
+7.687 0.154 7.577 0.206 7.422 0.206 c
+7.224 0.206 7.085 0.088 6.996 -0.147 c
+6.996 -2.969 l
+5.953 -2.969 l
+5.953 -0.441 l
+5.953 -0.206 5.924 -0.04 5.865 0.059 c
+5.805 0.154 5.695 0.206 5.541 0.206 c
+5.365 0.206 5.221 0.11 5.115 -0.073 c
+5.115 -2.969 l
+4.072 -2.969 l
+4.072 1.014 l
+h
+9.539 -0.852 m
+9.539 -0.246 9.679 0.228 9.965 0.574 c
+10.248 0.915 10.642 1.088 11.141 1.088 c
+11.648 1.088 12.045 0.915 12.332 0.574 c
+12.615 0.228 12.758 -0.246 12.758 -0.852 c
+12.758 -1.117 l
+12.758 -1.716 12.615 -2.186 12.332 -2.528 c
+12.045 -2.874 11.648 -3.042 11.141 -3.042 c
+10.63 -3.042 10.234 -2.874 9.951 -2.528 c
+9.676 -2.186 9.539 -1.712 9.539 -1.103 c
+h
+10.583 -1.117 m
+10.583 -1.822 10.767 -2.175 11.141 -2.175 c
+11.494 -2.175 11.685 -1.881 11.715 -1.294 c
+11.715 -0.852 l
+11.715 -0.492 11.663 -0.22 11.568 -0.044 c
+11.469 0.133 11.326 0.221 11.141 0.221 c
+10.965 0.221 10.825 0.133 10.73 -0.044 c
+10.63 -0.22 10.583 -0.492 10.583 -0.852 c
+h
+14.611 1.985 m
+14.611 1.014 l
+15.139 1.014 l
+15.139 0.221 l
+14.611 0.221 l
+14.611 -1.749 l
+14.611 -1.907 14.628 -2.013 14.669 -2.072 c
+14.717 -2.131 14.802 -2.161 14.919 -2.161 c
+15.025 -2.161 15.11 -2.153 15.169 -2.131 c
+15.169 -2.94 l
+14.993 -3.006 14.802 -3.042 14.596 -3.042 c
+13.92 -3.042 13.574 -2.657 13.566 -1.881 c
+13.566 0.221 l
+13.111 0.221 l
+13.111 1.014 l
+13.566 1.014 l
+13.566 1.985 l
+h
+17.315 -3.042 m
+16.786 -3.042 16.367 -2.888 16.066 -2.572 c
+15.771 -2.248 15.625 -1.789 15.625 -1.19 c
+15.625 -0.881 l
+15.625 -0.257 15.761 0.228 16.037 0.574 c
+16.309 0.915 16.702 1.088 17.213 1.088 c
+17.712 1.088 18.083 0.927 18.329 0.603 c
+18.583 0.279 18.715 -0.198 18.726 -0.823 c
+18.726 -1.323 l
+16.654 -1.323 l
+16.672 -1.617 16.735 -1.833 16.845 -1.97 c
+16.962 -2.109 17.142 -2.175 17.389 -2.175 c
+17.73 -2.175 18.021 -2.057 18.256 -1.822 c
+18.667 -2.454 l
+18.539 -2.631 18.351 -2.774 18.109 -2.881 c
+17.863 -2.988 17.598 -3.042 17.315 -3.042 c
+16.668 -0.602 m
+17.697 -0.602 l
+17.697 -0.5 l
+17.697 -0.264 17.657 -0.088 17.58 0.029 c
+17.51 0.154 17.381 0.221 17.198 0.221 c
+17.021 0.221 16.889 0.151 16.801 0.015 c
+16.72 -0.114 16.676 -0.32 16.668 -0.602 c
+f
+Q
+q 1 0 0 1 140.4975 198.7397 cm
+0 0 m
+-0.04 0.088 -0.066 0.235 -0.073 0.441 c
+-0.309 0.095 -0.602 -0.073 -0.955 -0.073 c
+-1.319 -0.073 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.102 0.713 -2.102 1.088 c
+-2.102 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.286 -1.043 2.41 -0.573 2.41 c
+-0.087 2.41 l
+-0.087 2.837 l
+-0.087 3.072 -0.143 3.237 -0.249 3.337 c
+-0.36 3.443 -0.521 3.499 -0.735 3.499 c
+-0.933 3.499 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.984 3.267 -1.866 3.454 c
+-1.741 3.638 -1.579 3.785 -1.382 3.896 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.29 4.056 0.015 3.954 0.221 3.748 c
+0.434 3.543 0.548 3.248 0.559 2.866 c
+0.559 0.852 l
+0.559 0.548 0.595 0.283 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.515 m
+-0.702 0.515 -0.551 0.559 -0.411 0.646 c
+-0.264 0.735 -0.158 0.845 -0.087 0.985 c
+-0.087 1.926 l
+-0.455 1.926 l
+-0.771 1.926 -1.014 1.856 -1.19 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.234 0.563 -1.084 0.515 -0.867 0.515 c
+2.176 3.983 m
+2.19 3.543 l
+2.444 3.884 2.768 4.056 3.161 4.056 c
+3.866 4.056 4.223 3.586 4.233 2.645 c
+4.233 0 l
+3.587 0 l
+3.587 2.616 l
+3.587 2.929 3.532 3.15 3.425 3.278 c
+3.315 3.403 3.161 3.469 2.955 3.469 c
+2.797 3.469 2.65 3.414 2.514 3.308 c
+2.385 3.197 2.282 3.061 2.205 2.896 c
+2.205 0 l
+1.559 0 l
+1.559 3.983 l
+h
+5.072 2.175 m
+5.072 2.782 5.182 3.248 5.409 3.572 c
+5.644 3.896 5.972 4.056 6.394 4.056 c
+6.776 4.056 7.074 3.898 7.291 3.586 c
+7.291 5.644 l
+7.938 5.644 l
+7.938 0 l
+7.35 0 l
+7.306 0.426 l
+7.1 0.092 6.795 -0.073 6.394 -0.073 c
+5.983 -0.073 5.659 0.081 5.424 0.397 c
+5.189 0.721 5.072 1.176 5.072 1.764 c
+h
+5.718 1.793 m
+5.718 1.352 5.781 1.022 5.909 0.808 c
+6.045 0.603 6.265 0.5 6.571 0.5 c
+6.894 0.5 7.133 0.661 7.291 0.985 c
+7.291 2.998 l
+7.122 3.31 6.883 3.469 6.571 3.469 c
+6.265 3.469 6.045 3.366 5.909 3.16 c
+5.781 2.955 5.718 2.631 5.718 2.19 c
+h
+11.275 3.983 m
+11.289 3.616 l
+11.532 3.91 11.851 4.056 12.245 4.056 c
+12.686 4.056 12.994 3.858 13.17 3.469 c
+13.424 3.858 13.773 4.056 14.215 4.056 c
+14.949 4.056 15.324 3.594 15.346 2.675 c
+15.346 0 l
+14.699 0 l
+14.699 2.616 l
+14.699 2.911 14.644 3.123 14.537 3.263 c
+14.438 3.399 14.265 3.469 14.024 3.469 c
+13.825 3.469 13.663 3.388 13.538 3.233 c
+13.421 3.087 13.351 2.896 13.332 2.66 c
+13.332 0 l
+12.671 0 l
+12.671 2.645 l
+12.671 3.193 12.451 3.469 12.009 3.469 c
+11.675 3.469 11.439 3.308 11.304 2.984 c
+11.304 0 l
+10.657 0 l
+10.657 3.983 l
+h
+17.742 -0.073 m
+17.242 -0.073 16.86 0.073 16.596 0.368 c
+16.331 0.661 16.199 1.095 16.199 1.675 c
+16.199 2.146 l
+16.199 2.741 16.324 3.208 16.581 3.543 c
+16.846 3.884 17.205 4.056 17.668 4.056 c
+18.128 4.056 18.47 3.902 18.697 3.601 c
+18.932 3.308 19.054 2.844 19.065 2.219 c
+19.065 1.793 l
+16.846 1.793 l
+16.846 1.705 l
+16.846 1.271 16.923 0.959 17.081 0.764 c
+17.246 0.577 17.477 0.485 17.771 0.485 c
+17.966 0.485 18.138 0.518 18.286 0.588 c
+18.433 0.665 18.569 0.783 18.697 0.941 c
+19.036 0.529 l
+18.749 0.125 18.319 -0.073 17.742 -0.073 c
+17.668 3.499 m
+17.393 3.499 17.191 3.403 17.066 3.219 c
+16.937 3.032 16.864 2.741 16.846 2.352 c
+18.418 2.352 l
+18.418 2.44 l
+18.396 2.822 18.33 3.09 18.213 3.248 c
+18.095 3.414 17.911 3.499 17.668 3.499 c
+21.49 3.366 m
+21.402 3.385 21.303 3.395 21.196 3.395 c
+20.862 3.395 20.627 3.212 20.49 2.851 c
+20.49 0 l
+19.844 0 l
+19.844 3.983 l
+20.476 3.983 l
+20.49 3.572 l
+20.667 3.896 20.91 4.056 21.226 4.056 c
+21.332 4.056 21.421 4.035 21.49 3.998 c
+h
+21.931 2.175 m
+21.931 2.793 22.042 3.256 22.269 3.572 c
+22.493 3.896 22.828 4.056 23.269 4.056 c
+23.669 4.056 23.975 3.881 24.18 3.528 c
+24.224 3.983 l
+24.812 3.983 l
+24.812 -0.044 l
+24.812 -0.532 24.684 -0.912 24.43 -1.176 c
+24.173 -1.44 23.82 -1.573 23.372 -1.573 c
+23.173 -1.573 22.953 -1.521 22.71 -1.426 c
+22.464 -1.326 22.284 -1.205 22.167 -1.058 c
+22.431 -0.617 l
+22.695 -0.882 22.994 -1.014 23.328 -1.014 c
+23.864 -1.014 24.139 -0.721 24.151 -0.133 c
+24.151 0.397 l
+23.945 0.081 23.644 -0.073 23.254 -0.073 c
+22.842 -0.073 22.519 0.077 22.284 0.382 c
+22.057 0.694 21.939 1.147 21.931 1.735 c
+h
+22.593 1.793 m
+22.593 1.352 22.655 1.022 22.784 0.808 c
+22.909 0.603 23.125 0.5 23.431 0.5 c
+23.754 0.5 23.993 0.665 24.151 0.999 c
+24.151 2.984 l
+23.981 3.308 23.743 3.469 23.431 3.469 c
+23.137 3.469 22.92 3.366 22.784 3.16 c
+22.655 2.955 22.593 2.631 22.593 2.19 c
+h
+27.194 -0.073 m
+26.693 -0.073 26.312 0.073 26.047 0.368 c
+25.783 0.661 25.65 1.095 25.65 1.675 c
+25.65 2.146 l
+25.65 2.741 25.775 3.208 26.032 3.543 c
+26.296 3.884 26.657 4.056 27.12 4.056 c
+27.58 4.056 27.921 3.902 28.149 3.601 c
+28.384 3.308 28.505 2.844 28.517 2.219 c
+28.517 1.793 l
+26.296 1.793 l
+26.296 1.705 l
+26.296 1.271 26.374 0.959 26.532 0.764 c
+26.697 0.577 26.929 0.485 27.223 0.485 c
+27.418 0.485 27.59 0.518 27.738 0.588 c
+27.884 0.665 28.02 0.783 28.149 0.941 c
+28.487 0.529 l
+28.201 0.125 27.771 -0.073 27.194 -0.073 c
+27.12 3.499 m
+26.844 3.499 26.643 3.403 26.518 3.219 c
+26.389 3.032 26.315 2.741 26.296 2.352 c
+27.869 2.352 l
+27.869 2.44 l
+27.848 2.822 27.781 3.09 27.664 3.248 c
+27.546 3.414 27.362 3.499 27.12 3.499 c
+32.397 0.485 m
+32.61 0.485 32.783 0.548 32.912 0.676 c
+33.047 0.812 33.121 1.003 33.132 1.249 c
+33.749 1.249 l
+33.727 0.867 33.591 0.548 33.338 0.294 c
+33.08 0.048 32.768 -0.073 32.397 -0.073 c
+31.904 -0.073 31.53 0.077 31.265 0.382 c
+31.008 0.694 30.883 1.161 30.883 1.779 c
+30.883 2.219 l
+30.883 2.815 31.008 3.27 31.265 3.586 c
+31.53 3.898 31.904 4.056 32.397 4.056 c
+32.798 4.056 33.117 3.925 33.352 3.66 c
+33.595 3.403 33.727 3.057 33.749 2.616 c
+33.132 2.616 l
+33.11 2.911 33.036 3.131 32.912 3.278 c
+32.794 3.425 32.621 3.499 32.397 3.499 c
+32.103 3.499 31.886 3.399 31.75 3.204 c
+31.611 3.017 31.537 2.708 31.53 2.278 c
+31.53 1.764 l
+31.53 1.294 31.596 0.959 31.736 0.764 c
+31.883 0.577 32.103 0.485 32.397 0.485 c
+36.527 0.353 m
+36.311 0.066 35.998 -0.073 35.586 -0.073 c
+35.223 -0.073 34.948 0.048 34.763 0.294 c
+34.587 0.548 34.491 0.912 34.485 1.382 c
+34.485 3.983 l
+35.131 3.983 l
+35.131 1.44 l
+35.131 0.812 35.314 0.5 35.69 0.5 c
+36.091 0.5 36.366 0.676 36.513 1.029 c
+36.513 3.983 l
+37.159 3.983 l
+37.159 0 l
+36.542 0 l
+h
+39.79 3.366 m
+39.702 3.385 39.603 3.395 39.497 3.395 c
+39.162 3.395 38.927 3.212 38.791 2.851 c
+38.791 0 l
+38.144 0 l
+38.144 3.983 l
+38.776 3.983 l
+38.791 3.572 l
+38.967 3.896 39.21 4.056 39.526 4.056 c
+39.632 4.056 39.721 4.035 39.79 3.998 c
+h
+42.084 3.366 m
+41.996 3.385 41.896 3.395 41.79 3.395 c
+41.456 3.395 41.22 3.212 41.084 2.851 c
+41.084 0 l
+40.438 0 l
+40.438 3.983 l
+41.07 3.983 l
+41.084 3.572 l
+41.261 3.896 41.503 4.056 41.819 4.056 c
+41.926 4.056 42.013 4.035 42.084 3.998 c
+h
+44.083 -0.073 m
+43.583 -0.073 43.201 0.073 42.936 0.368 c
+42.672 0.661 42.539 1.095 42.539 1.675 c
+42.539 2.146 l
+42.539 2.741 42.664 3.208 42.921 3.543 c
+43.187 3.884 43.546 4.056 44.009 4.056 c
+44.469 4.056 44.81 3.902 45.038 3.601 c
+45.273 3.308 45.394 2.844 45.406 2.219 c
+45.406 1.793 l
+43.187 1.793 l
+43.187 1.705 l
+43.187 1.271 43.264 0.959 43.422 0.764 c
+43.586 0.577 43.818 0.485 44.112 0.485 c
+44.307 0.485 44.479 0.518 44.627 0.588 c
+44.774 0.665 44.91 0.783 45.038 0.941 c
+45.377 0.529 l
+45.09 0.125 44.66 -0.073 44.083 -0.073 c
+44.009 3.499 m
+43.734 3.499 43.532 3.403 43.407 3.219 c
+43.278 3.032 43.204 2.741 43.187 2.352 c
+44.758 2.352 l
+44.758 2.44 l
+44.737 2.822 44.671 3.09 44.553 3.248 c
+44.436 3.414 44.251 3.499 44.009 3.499 c
+46.802 3.983 m
+46.817 3.543 l
+47.07 3.884 47.394 4.056 47.787 4.056 c
+48.492 4.056 48.849 3.586 48.86 2.645 c
+48.86 0 l
+48.213 0 l
+48.213 2.616 l
+48.213 2.929 48.158 3.15 48.051 3.278 c
+47.941 3.403 47.787 3.469 47.581 3.469 c
+47.423 3.469 47.276 3.414 47.14 3.308 c
+47.012 3.197 46.909 3.061 46.831 2.896 c
+46.831 0 l
+46.185 0 l
+46.185 3.983 l
+h
+50.682 4.939 m
+50.682 3.983 l
+51.285 3.983 l
+51.285 3.454 l
+50.682 3.454 l
+50.682 0.985 l
+50.682 0.827 50.705 0.709 50.756 0.632 c
+50.815 0.551 50.903 0.515 51.021 0.515 c
+51.108 0.515 51.197 0.529 51.285 0.559 c
+51.285 0 l
+51.139 -0.048 50.984 -0.073 50.829 -0.073 c
+50.572 -0.073 50.378 0.018 50.242 0.206 c
+50.102 0.389 50.036 0.65 50.036 0.985 c
+50.036 3.454 l
+49.433 3.454 l
+49.433 3.983 l
+50.036 3.983 l
+50.036 4.939 l
+h
+56.65 1.793 m
+56.65 1.176 56.537 0.709 56.313 0.397 c
+56.095 0.081 55.772 -0.073 55.342 -0.073 c
+54.919 -0.073 54.607 0.106 54.401 0.47 c
+54.372 0 l
+53.77 0 l
+53.77 5.644 l
+54.416 5.644 l
+54.416 3.543 l
+54.63 3.884 54.938 4.056 55.342 4.056 c
+55.772 4.056 56.095 3.898 56.313 3.586 c
+56.537 3.281 56.65 2.815 56.65 2.19 c
+h
+56.003 2.175 m
+56.003 2.645 55.934 2.977 55.798 3.175 c
+55.669 3.37 55.459 3.469 55.166 3.469 c
+54.831 3.469 54.582 3.285 54.416 2.925 c
+54.416 1.043 l
+54.582 0.68 54.835 0.5 55.181 0.5 c
+55.474 0.5 55.684 0.603 55.812 0.808 c
+55.937 1.014 56.003 1.33 56.003 1.764 c
+h
+59.135 3.366 m
+59.046 3.385 58.947 3.395 58.84 3.395 c
+58.506 3.395 58.271 3.212 58.135 2.851 c
+58.135 0 l
+57.489 0 l
+57.489 3.983 l
+58.12 3.983 l
+58.135 3.572 l
+58.312 3.896 58.553 4.056 58.87 4.056 c
+58.977 4.056 59.064 4.035 59.135 3.998 c
+h
+61.678 0 m
+61.637 0.088 61.611 0.235 61.604 0.441 c
+61.369 0.095 61.075 -0.073 60.722 -0.073 c
+60.358 -0.073 60.076 0.022 59.87 0.22 c
+59.671 0.426 59.575 0.713 59.575 1.088 c
+59.575 1.488 59.712 1.808 59.987 2.043 c
+60.259 2.286 60.633 2.41 61.104 2.41 c
+61.589 2.41 l
+61.589 2.837 l
+61.589 3.072 61.534 3.237 61.427 3.337 c
+61.317 3.443 61.156 3.499 60.942 3.499 c
+60.744 3.499 60.583 3.439 60.458 3.322 c
+60.34 3.204 60.281 3.057 60.281 2.881 c
+59.634 2.881 l
+59.634 3.075 59.693 3.267 59.81 3.454 c
+59.935 3.638 60.097 3.785 60.296 3.896 c
+60.502 4.002 60.729 4.056 60.986 4.056 c
+61.387 4.056 61.692 3.954 61.898 3.748 c
+62.111 3.543 62.225 3.248 62.236 2.866 c
+62.236 0.852 l
+62.236 0.548 62.273 0.283 62.353 0.058 c
+62.353 0 l
+h
+60.81 0.515 m
+60.976 0.515 61.126 0.559 61.266 0.646 c
+61.412 0.735 61.52 0.845 61.589 0.985 c
+61.589 1.926 l
+61.221 1.926 l
+60.905 1.926 60.664 1.856 60.487 1.72 c
+60.311 1.591 60.222 1.404 60.222 1.161 c
+60.222 0.933 60.267 0.768 60.355 0.661 c
+60.443 0.563 60.593 0.515 60.81 0.515 c
+63.853 3.983 m
+63.868 3.543 l
+64.121 3.884 64.444 4.056 64.838 4.056 c
+65.543 4.056 65.9 3.586 65.911 2.645 c
+65.911 0 l
+65.264 0 l
+65.264 2.616 l
+65.264 2.929 65.209 3.15 65.102 3.278 c
+64.992 3.403 64.838 3.469 64.632 3.469 c
+64.474 3.469 64.327 3.414 64.191 3.308 c
+64.062 3.197 63.96 3.061 63.883 2.896 c
+63.883 0 l
+63.236 0 l
+63.236 3.983 l
+h
+68.263 0.485 m
+68.475 0.485 68.649 0.548 68.777 0.676 c
+68.913 0.812 68.986 1.003 68.998 1.249 c
+69.615 1.249 l
+69.593 0.867 69.457 0.548 69.204 0.294 c
+68.946 0.048 68.634 -0.073 68.263 -0.073 c
+67.77 -0.073 67.395 0.077 67.131 0.382 c
+66.873 0.694 66.748 1.161 66.748 1.779 c
+66.748 2.219 l
+66.748 2.815 66.873 3.27 67.131 3.586 c
+67.395 3.898 67.77 4.056 68.263 4.056 c
+68.664 4.056 68.983 3.925 69.218 3.66 c
+69.46 3.403 69.593 3.057 69.615 2.616 c
+68.998 2.616 l
+68.976 2.911 68.902 3.131 68.777 3.278 c
+68.66 3.425 68.487 3.499 68.263 3.499 c
+67.968 3.499 67.752 3.399 67.616 3.204 c
+67.476 3.017 67.403 2.708 67.395 2.278 c
+67.395 1.764 l
+67.395 1.294 67.461 0.959 67.602 0.764 c
+67.748 0.577 67.968 0.485 68.263 0.485 c
+71.012 3.572 m
+71.265 3.896 71.585 4.056 71.967 4.056 c
+72.672 4.056 73.029 3.586 73.04 2.645 c
+73.04 0 l
+72.393 0 l
+72.393 2.616 l
+72.393 2.929 72.338 3.15 72.231 3.278 c
+72.121 3.403 71.967 3.469 71.761 3.469 c
+71.603 3.469 71.456 3.414 71.32 3.308 c
+71.191 3.197 71.089 3.061 71.012 2.896 c
+71.012 0 l
+70.364 0 l
+70.364 5.644 l
+71.012 5.644 l
+h
+78.684 1.147 m
+79.286 3.983 l
+79.934 3.983 l
+78.949 0 l
+78.434 0 l
+77.655 2.851 l
+76.905 0 l
+76.377 0 l
+75.421 3.983 l
+76.053 3.983 l
+76.67 1.22 l
+77.405 3.983 l
+77.92 3.983 l
+h
+81.316 0 -0.647 3.983 re
+81.36 5.027 m
+81.36 4.916 81.33 4.825 81.271 4.748 c
+81.212 4.678 81.117 4.644 80.992 4.644 c
+80.874 4.644 80.778 4.678 80.713 4.748 c
+80.654 4.825 80.624 4.916 80.624 5.027 c
+80.624 5.145 80.654 5.236 80.713 5.307 c
+80.778 5.384 80.874 5.424 80.992 5.424 c
+81.117 5.424 81.212 5.384 81.271 5.307 c
+81.33 5.226 81.36 5.134 81.36 5.027 c
+83.182 4.939 m
+83.182 3.983 l
+83.785 3.983 l
+83.785 3.454 l
+83.182 3.454 l
+83.182 0.985 l
+83.182 0.827 83.205 0.709 83.255 0.632 c
+83.315 0.551 83.403 0.515 83.52 0.515 c
+83.608 0.515 83.697 0.529 83.785 0.559 c
+83.785 0 l
+83.638 -0.048 83.483 -0.073 83.329 -0.073 c
+83.072 -0.073 82.877 0.018 82.741 0.206 c
+82.602 0.389 82.536 0.65 82.536 0.985 c
+82.536 3.454 l
+81.933 3.454 l
+81.933 3.983 l
+82.536 3.983 l
+82.536 4.939 l
+h
+85.196 3.572 m
+85.449 3.896 85.769 4.056 86.151 4.056 c
+86.857 4.056 87.213 3.586 87.224 2.645 c
+87.224 0 l
+86.577 0 l
+86.577 2.616 l
+86.577 2.929 86.523 3.15 86.416 3.278 c
+86.305 3.403 86.151 3.469 85.946 3.469 c
+85.788 3.469 85.64 3.414 85.505 3.308 c
+85.376 3.197 85.273 3.061 85.196 2.896 c
+85.196 0 l
+84.549 0 l
+84.549 5.644 l
+85.196 5.644 l
+h
+90.605 0 -0.647 3.983 re
+90.65 5.027 m
+90.65 4.916 90.62 4.825 90.561 4.748 c
+90.502 4.678 90.407 4.644 90.282 4.644 c
+90.164 4.644 90.068 4.678 90.002 4.748 c
+89.944 4.825 89.914 4.916 89.914 5.027 c
+89.914 5.145 89.944 5.236 90.002 5.307 c
+90.068 5.384 90.164 5.424 90.282 5.424 c
+90.407 5.424 90.502 5.384 90.561 5.307 c
+90.62 5.226 90.65 5.134 90.65 5.027 c
+92.472 4.939 m
+92.472 3.983 l
+93.075 3.983 l
+93.075 3.454 l
+92.472 3.454 l
+92.472 0.985 l
+92.472 0.827 92.494 0.709 92.545 0.632 c
+92.604 0.551 92.692 0.515 92.811 0.515 c
+92.898 0.515 92.986 0.529 93.075 0.559 c
+93.075 0 l
+92.927 -0.048 92.773 -0.073 92.619 -0.073 c
+92.362 -0.073 92.167 0.018 92.031 0.206 c
+91.891 0.389 91.826 0.65 91.826 0.985 c
+91.826 3.454 l
+91.223 3.454 l
+91.223 3.983 l
+91.826 3.983 l
+91.826 4.939 l
+h
+95.779 1.014 m
+95.779 1.161 95.724 1.282 95.618 1.382 c
+95.508 1.477 95.302 1.595 95.001 1.735 c
+94.654 1.881 94.413 2.003 94.265 2.102 c
+94.118 2.209 94.008 2.326 93.942 2.454 c
+93.872 2.58 93.839 2.738 93.839 2.925 c
+93.839 3.248 93.956 3.516 94.192 3.734 c
+94.427 3.946 94.729 4.056 95.103 4.056 c
+95.485 4.056 95.794 3.943 96.029 3.719 c
+96.264 3.491 96.382 3.204 96.382 2.851 c
+95.735 2.851 l
+95.735 3.028 95.676 3.179 95.558 3.308 c
+95.441 3.432 95.287 3.499 95.103 3.499 c
+94.905 3.499 94.754 3.443 94.648 3.337 c
+94.537 3.237 94.486 3.105 94.486 2.94 c
+94.486 2.811 94.523 2.705 94.604 2.616 c
+94.681 2.535 94.872 2.433 95.176 2.308 c
+95.654 2.12 95.985 1.933 96.161 1.749 c
+96.338 1.573 96.426 1.345 96.426 1.073 c
+96.426 0.721 96.301 0.441 96.059 0.235 c
+95.824 0.029 95.508 -0.073 95.118 -0.073 c
+94.695 -0.073 94.357 0.044 94.103 0.279 c
+93.846 0.522 93.721 0.827 93.721 1.191 c
+94.368 1.191 l
+94.376 0.962 94.446 0.786 94.573 0.661 c
+94.699 0.544 94.883 0.485 95.118 0.485 c
+95.331 0.485 95.493 0.532 95.603 0.632 c
+95.72 0.727 95.779 0.856 95.779 1.014 c
+101.012 0.353 m
+100.796 0.066 100.483 -0.073 100.071 -0.073 c
+99.708 -0.073 99.432 0.048 99.248 0.294 c
+99.072 0.548 98.976 0.912 98.969 1.382 c
+98.969 3.983 l
+99.616 3.983 l
+99.616 1.44 l
+99.616 0.812 99.799 0.5 100.175 0.5 c
+100.574 0.5 100.85 0.676 100.998 1.029 c
+100.998 3.983 l
+101.644 3.983 l
+101.644 0 l
+101.027 0 l
+h
+105.51 1.793 m
+105.51 1.165 105.392 0.694 105.157 0.382 c
+104.929 0.077 104.613 -0.073 104.202 -0.073 c
+103.797 -0.073 103.489 0.077 103.275 0.382 c
+103.275 -1.529 l
+102.629 -1.529 l
+102.629 3.983 l
+103.217 3.983 l
+103.261 3.543 l
+103.474 3.884 103.783 4.056 104.187 4.056 c
+104.628 4.056 104.955 3.902 105.172 3.601 c
+105.385 3.296 105.499 2.84 105.51 2.234 c
+h
+104.863 2.175 m
+104.863 2.616 104.794 2.94 104.657 3.146 c
+104.518 3.358 104.297 3.469 103.996 3.469 c
+103.68 3.469 103.441 3.314 103.275 3.013 c
+103.275 0.941 l
+103.441 0.636 103.68 0.485 103.996 0.485 c
+104.29 0.485 104.503 0.588 104.643 0.794 c
+104.779 1.007 104.852 1.338 104.863 1.779 c
+h
+108.289 1.014 m
+108.289 1.161 108.233 1.282 108.127 1.382 c
+108.017 1.477 107.811 1.595 107.509 1.735 c
+107.163 1.881 106.922 2.003 106.774 2.102 c
+106.627 2.209 106.517 2.326 106.451 2.454 c
+106.381 2.58 106.348 2.738 106.348 2.925 c
+106.348 3.248 106.465 3.516 106.701 3.734 c
+106.936 3.946 107.237 4.056 107.612 4.056 c
+107.994 4.056 108.303 3.943 108.538 3.719 c
+108.773 3.491 108.891 3.204 108.891 2.851 c
+108.244 2.851 l
+108.244 3.028 108.185 3.179 108.067 3.308 c
+107.95 3.432 107.796 3.499 107.612 3.499 c
+107.414 3.499 107.263 3.443 107.157 3.337 c
+107.046 3.237 106.995 3.105 106.995 2.94 c
+106.995 2.811 107.032 2.705 107.113 2.616 c
+107.19 2.535 107.381 2.433 107.685 2.308 c
+108.163 2.12 108.494 1.933 108.67 1.749 c
+108.846 1.573 108.935 1.345 108.935 1.073 c
+108.935 0.721 108.81 0.441 108.568 0.235 c
+108.333 0.029 108.017 -0.073 107.627 -0.073 c
+107.204 -0.073 106.866 0.044 106.612 0.279 c
+106.355 0.522 106.23 0.827 106.23 1.191 c
+106.877 1.191 l
+106.884 0.962 106.955 0.786 107.082 0.661 c
+107.208 0.544 107.392 0.485 107.627 0.485 c
+107.84 0.485 108.002 0.532 108.112 0.632 c
+108.229 0.727 108.289 0.856 108.289 1.014 c
+110.625 4.939 m
+110.625 3.983 l
+111.228 3.983 l
+111.228 3.454 l
+110.625 3.454 l
+110.625 0.985 l
+110.625 0.827 110.647 0.709 110.699 0.632 c
+110.758 0.551 110.845 0.515 110.963 0.515 c
+111.051 0.515 111.14 0.529 111.228 0.559 c
+111.228 0 l
+111.081 -0.048 110.926 -0.073 110.772 -0.073 c
+110.515 -0.073 110.32 0.018 110.184 0.206 c
+110.045 0.389 109.979 0.65 109.979 0.985 c
+109.979 3.454 l
+109.376 3.454 l
+109.376 3.983 l
+109.979 3.983 l
+109.979 4.939 l
+h
+113.638 3.366 m
+113.55 3.385 113.451 3.395 113.345 3.395 c
+113.01 3.395 112.775 3.212 112.639 2.851 c
+112.639 0 l
+111.992 0 l
+111.992 3.983 l
+112.624 3.983 l
+112.639 3.572 l
+112.815 3.896 113.058 4.056 113.374 4.056 c
+113.48 4.056 113.569 4.035 113.638 3.998 c
+h
+115.638 -0.073 m
+115.138 -0.073 114.756 0.073 114.491 0.368 c
+114.226 0.661 114.094 1.095 114.094 1.675 c
+114.094 2.146 l
+114.094 2.741 114.219 3.208 114.477 3.543 c
+114.741 3.884 115.101 4.056 115.564 4.056 c
+116.023 4.056 116.366 3.902 116.593 3.601 c
+116.829 3.308 116.95 2.844 116.96 2.219 c
+116.96 1.793 l
+114.741 1.793 l
+114.741 1.705 l
+114.741 1.271 114.818 0.959 114.976 0.764 c
+115.142 0.577 115.373 0.485 115.667 0.485 c
+115.861 0.485 116.035 0.518 116.181 0.588 c
+116.329 0.665 116.464 0.783 116.593 0.941 c
+116.931 0.529 l
+116.644 0.125 116.214 -0.073 115.638 -0.073 c
+115.564 3.499 m
+115.289 3.499 115.086 3.403 114.961 3.219 c
+114.833 3.032 114.76 2.741 114.741 2.352 c
+116.314 2.352 l
+116.314 2.44 l
+116.291 2.822 116.226 3.09 116.108 3.248 c
+115.99 3.414 115.807 3.499 115.564 3.499 c
+119.782 0 m
+119.742 0.088 119.717 0.235 119.709 0.441 c
+119.474 0.095 119.18 -0.073 118.828 -0.073 c
+118.464 -0.073 118.18 0.022 117.975 0.22 c
+117.777 0.426 117.681 0.713 117.681 1.088 c
+117.681 1.488 117.817 1.808 118.092 2.043 c
+118.365 2.286 118.739 2.41 119.21 2.41 c
+119.695 2.41 l
+119.695 2.837 l
+119.695 3.072 119.639 3.237 119.533 3.337 c
+119.423 3.443 119.261 3.499 119.048 3.499 c
+118.849 3.499 118.687 3.439 118.562 3.322 c
+118.446 3.204 118.386 3.057 118.386 2.881 c
+117.74 2.881 l
+117.74 3.075 117.798 3.267 117.916 3.454 c
+118.041 3.638 118.203 3.785 118.401 3.896 c
+118.607 4.002 118.835 4.056 119.092 4.056 c
+119.493 4.056 119.798 3.954 120.004 3.748 c
+120.216 3.543 120.33 3.248 120.341 2.866 c
+120.341 0.852 l
+120.341 0.548 120.378 0.283 120.459 0.058 c
+120.459 0 l
+h
+118.916 0.515 m
+119.081 0.515 119.231 0.559 119.371 0.646 c
+119.518 0.735 119.624 0.845 119.695 0.985 c
+119.695 1.926 l
+119.327 1.926 l
+119.011 1.926 118.768 1.856 118.592 1.72 c
+118.416 1.591 118.327 1.404 118.327 1.161 c
+118.327 0.933 118.371 0.768 118.46 0.661 c
+118.548 0.563 118.699 0.515 118.916 0.515 c
+121.958 3.983 m
+121.973 3.616 l
+122.215 3.91 122.535 4.056 122.928 4.056 c
+123.369 4.056 123.678 3.858 123.855 3.469 c
+124.108 3.858 124.457 4.056 124.898 4.056 c
+125.633 4.056 126.008 3.594 126.03 2.675 c
+126.03 0 l
+125.383 0 l
+125.383 2.616 l
+125.383 2.911 125.328 3.123 125.222 3.263 c
+125.122 3.399 124.95 3.469 124.707 3.469 c
+124.509 3.469 124.347 3.388 124.222 3.233 c
+124.104 3.087 124.035 2.896 124.016 2.66 c
+124.016 0 l
+123.354 0 l
+123.354 2.645 l
+123.354 3.193 123.134 3.469 122.693 3.469 c
+122.359 3.469 122.124 3.308 121.988 2.984 c
+121.988 0 l
+121.341 0 l
+121.341 3.983 l
+h
+127.044 0.353 m
+127.044 0.47 127.077 0.565 127.147 0.646 c
+127.213 0.723 127.316 0.764 127.456 0.764 c
+127.603 0.764 127.709 0.723 127.779 0.646 c
+127.856 0.565 127.896 0.47 127.896 0.353 c
+127.896 0.243 127.856 0.151 127.779 0.073 c
+127.709 -0.004 127.603 -0.044 127.456 -0.044 c
+127.316 -0.044 127.213 -0.004 127.147 0.073 c
+127.077 0.151 127.044 0.243 127.044 0.353 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 193.132 238.665 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 186.2965 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.263 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.024 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.034 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.477 -2.234 -0.515 c
+-2.138 -0.554 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.757 -3.322 1.985 c
+-3.322 2.18 -3.289 2.345 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.933 -2.645 3.003 -2.469 3.043 c
+-2.293 3.091 -2.102 3.12 -1.896 3.131 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.091 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.621 -0.279 2.484 c
+-0.202 2.345 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.503 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.386 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.201 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.634 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.83 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.188 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.029 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.006 7.699 -2.161 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.323 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.675 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.687 15.468 -0.675 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.26 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.3 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.263 c
+14.221 -1.227 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.024 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.359 13.586 -0.264 13.586 -0.176 c
+h
+21.986 2.22 m
+21.994 2.198 22.001 2.165 22.001 2.117 c
+22.009 2.076 22.016 2.029 22.016 1.97 c
+22.023 1.918 22.03 1.867 22.03 1.808 c
+22.03 1.646 l
+22.045 1.646 l
+22.104 1.764 22.17 1.86 22.251 1.941 c
+22.328 2.018 22.413 2.08 22.501 2.132 c
+22.589 2.19 22.677 2.228 22.765 2.249 c
+22.861 2.268 22.961 2.278 23.059 2.278 c
+23.265 2.278 23.445 2.234 23.603 2.147 c
+23.757 2.058 23.886 1.929 23.985 1.764 c
+24.092 1.606 24.169 1.415 24.22 1.191 c
+24.28 0.975 24.309 0.739 24.309 0.485 c
+24.309 0.221 24.28 -0.025 24.22 -0.249 c
+24.169 -0.467 24.092 -0.658 23.985 -0.823 c
+23.886 -0.981 23.754 -1.103 23.589 -1.19 c
+23.431 -1.278 23.243 -1.323 23.03 -1.323 c
+22.931 -1.323 22.832 -1.311 22.736 -1.294 c
+22.637 -1.271 22.545 -1.242 22.456 -1.19 c
+22.375 -1.142 22.298 -1.08 22.221 -0.999 c
+22.152 -0.922 22.093 -0.83 22.045 -0.72 c
+22.03 -0.72 l
+22.03 -0.808 l
+22.038 -0.849 22.045 -0.897 22.045 -0.955 c
+22.045 -1.117 l
+22.045 -1.294 l
+22.045 -2.631 l
+21.134 -2.631 l
+21.134 1.455 l
+21.134 1.621 21.126 1.768 21.119 1.897 c
+21.119 2.22 l
+h
+22.03 0.456 m
+22.03 0.229 22.049 0.037 22.089 -0.118 c
+22.137 -0.264 22.192 -0.382 22.251 -0.47 c
+22.317 -0.558 22.391 -0.625 22.471 -0.661 c
+22.549 -0.702 22.626 -0.72 22.707 -0.72 c
+22.803 -0.72 22.89 -0.698 22.971 -0.646 c
+23.059 -0.598 23.125 -0.529 23.177 -0.44 c
+23.235 -0.345 23.28 -0.22 23.31 -0.073 c
+23.346 0.081 23.368 0.269 23.368 0.485 c
+23.368 0.875 23.31 1.169 23.192 1.367 c
+23.081 1.563 22.927 1.661 22.722 1.661 c
+22.641 1.661 22.564 1.64 22.486 1.602 c
+22.406 1.563 22.332 1.5 22.265 1.411 c
+22.196 1.324 22.137 1.199 22.089 1.044 c
+22.049 0.886 22.03 0.691 22.03 0.456 c
+26.061 2.22 m
+26.061 0.265 l
+26.061 0.125 26.069 0 26.091 -0.118 c
+26.109 -0.228 26.142 -0.319 26.194 -0.397 c
+26.242 -0.477 26.3 -0.54 26.371 -0.588 c
+26.437 -0.628 26.521 -0.646 26.62 -0.646 c
+26.709 -0.646 26.789 -0.628 26.87 -0.588 c
+26.959 -0.54 27.032 -0.47 27.09 -0.382 c
+27.15 -0.286 27.194 -0.176 27.223 -0.058 c
+27.26 0.067 27.281 0.206 27.281 0.353 c
+27.281 2.22 l
+28.178 2.22 l
+28.178 -0.484 l
+28.178 -0.72 l
+28.185 -0.801 28.193 -0.878 28.193 -0.955 c
+28.193 -1.146 l
+28.201 -1.198 28.208 -1.234 28.208 -1.263 c
+27.355 -1.263 l
+27.344 -1.234 27.333 -1.198 27.326 -1.146 c
+27.326 -0.955 l
+27.326 -0.889 27.318 -0.819 27.312 -0.75 c
+27.312 -0.573 l
+27.296 -0.573 l
+27.179 -0.837 27.025 -1.028 26.841 -1.146 c
+26.664 -1.263 26.462 -1.323 26.238 -1.323 c
+26.032 -1.323 25.86 -1.286 25.724 -1.22 c
+25.584 -1.153 25.473 -1.058 25.386 -0.941 c
+25.305 -0.823 25.246 -0.687 25.209 -0.529 c
+25.18 -0.363 25.165 -0.187 25.165 0 c
+25.165 2.22 l
+h
+32.342 -0.249 m
+32.342 -0.419 32.301 -0.569 32.224 -0.706 c
+32.154 -0.833 32.052 -0.947 31.916 -1.043 c
+31.787 -1.132 31.625 -1.201 31.43 -1.249 c
+31.243 -1.297 31.027 -1.323 30.784 -1.323 c
+30.556 -1.323 30.358 -1.308 30.181 -1.278 c
+30.005 -1.249 29.847 -1.201 29.71 -1.132 c
+29.571 -1.055 29.461 -0.955 29.373 -0.837 c
+29.284 -0.72 29.215 -0.573 29.167 -0.397 c
+29.976 -0.279 l
+29.994 -0.378 30.023 -0.455 30.063 -0.515 c
+30.111 -0.573 30.17 -0.617 30.24 -0.646 c
+30.306 -0.675 30.387 -0.702 30.475 -0.72 c
+30.564 -0.731 30.666 -0.735 30.784 -0.735 c
+30.88 -0.735 30.975 -0.731 31.063 -0.72 c
+31.152 -0.702 31.229 -0.675 31.298 -0.646 c
+31.364 -0.617 31.416 -0.58 31.445 -0.529 c
+31.482 -0.481 31.504 -0.419 31.504 -0.338 c
+31.504 -0.242 31.474 -0.168 31.416 -0.118 c
+31.364 -0.07 31.298 -0.029 31.21 0 c
+31.122 0.037 31.011 0.067 30.886 0.088 c
+30.769 0.118 30.637 0.148 30.489 0.177 c
+30.35 0.214 30.211 0.254 30.063 0.294 c
+29.924 0.342 29.799 0.405 29.681 0.485 c
+29.571 0.563 29.483 0.661 29.417 0.779 c
+29.347 0.897 29.313 1.048 29.313 1.235 c
+29.313 1.389 29.344 1.532 29.402 1.661 c
+29.469 1.798 29.564 1.912 29.681 1.999 c
+29.806 2.087 29.964 2.153 30.152 2.205 c
+30.335 2.253 30.549 2.278 30.784 2.278 c
+30.967 2.278 31.144 2.257 31.313 2.22 c
+31.478 2.19 31.625 2.135 31.754 2.058 c
+31.879 1.989 31.989 1.889 32.077 1.764 c
+32.166 1.646 32.224 1.503 32.253 1.338 c
+31.46 1.264 l
+31.438 1.341 31.408 1.405 31.372 1.455 c
+31.331 1.515 31.283 1.559 31.225 1.588 c
+31.173 1.625 31.111 1.65 31.034 1.661 c
+30.953 1.669 30.872 1.676 30.784 1.676 c
+30.567 1.676 30.406 1.646 30.298 1.588 c
+30.188 1.536 30.137 1.448 30.137 1.324 c
+30.137 1.243 30.155 1.18 30.196 1.132 c
+30.244 1.081 30.306 1.044 30.387 1.014 c
+30.475 0.985 30.57 0.956 30.681 0.927 c
+30.788 0.904 30.909 0.882 31.048 0.853 c
+31.202 0.823 31.36 0.783 31.519 0.736 c
+31.673 0.684 31.813 0.622 31.931 0.544 c
+32.048 0.464 32.143 0.36 32.224 0.235 c
+32.301 0.107 32.342 -0.055 32.342 -0.249 c
+34.202 1.515 m
+34.319 1.786 34.47 1.985 34.657 2.103 c
+34.84 2.22 35.061 2.278 35.318 2.278 c
+35.524 2.278 35.694 2.242 35.833 2.176 c
+35.98 2.106 36.091 2.014 36.17 1.897 c
+36.259 1.779 36.318 1.636 36.347 1.47 c
+36.384 1.301 36.406 1.125 36.406 0.941 c
+36.406 -1.263 l
+35.495 -1.263 l
+35.495 0.736 l
+35.495 0.871 35.484 0.992 35.465 1.103 c
+35.455 1.21 35.428 1.297 35.392 1.367 c
+35.351 1.445 35.293 1.503 35.216 1.544 c
+35.146 1.58 35.054 1.602 34.936 1.602 c
+34.826 1.602 34.73 1.577 34.643 1.529 c
+34.554 1.478 34.473 1.411 34.408 1.324 c
+34.348 1.235 34.297 1.125 34.26 1 c
+34.231 0.882 34.216 0.75 34.216 0.603 c
+34.216 -1.263 l
+33.305 -1.263 l
+33.305 3.514 l
+34.216 3.514 l
+34.216 2.205 l
+34.216 2.135 34.209 2.066 34.202 1.999 c
+34.202 1.794 l
+34.202 1.735 34.194 1.68 34.186 1.632 c
+34.186 1.515 l
+h
+42.175 -2.631 m
+42.175 3.514 l
+44.101 3.514 l
+44.101 2.896 l
+43.028 2.896 l
+43.028 -2.013 l
+44.101 -2.013 l
+44.101 -2.631 l
+h
+46.075 0.838 1.866 -0.794 re
+46.075 0.044 m
+50.135 0.838 1.867 -0.794 re
+50.135 0.044 m
+54.214 1.602 m
+53.67 1.602 l
+53.67 2.22 l
+54.258 2.22 l
+54.537 3.117 l
+55.11 3.117 l
+55.11 2.22 l
+56.346 2.22 l
+56.346 1.602 l
+55.11 1.602 l
+55.11 -0.103 l
+55.11 -0.323 l
+55.118 -0.393 55.14 -0.455 55.17 -0.515 c
+55.206 -0.565 55.261 -0.61 55.331 -0.646 c
+55.409 -0.675 55.522 -0.691 55.669 -0.691 c
+55.806 -0.691 55.941 -0.687 56.081 -0.675 c
+56.217 -0.658 56.349 -0.632 56.477 -0.602 c
+56.477 -1.205 l
+56.396 -1.216 56.319 -1.23 56.242 -1.249 c
+56.161 -1.26 56.084 -1.267 56.007 -1.278 c
+55.926 -1.286 55.839 -1.294 55.743 -1.294 c
+55.654 -1.3 55.555 -1.308 55.449 -1.308 c
+55.261 -1.308 55.1 -1.294 54.964 -1.263 c
+54.835 -1.227 54.721 -1.183 54.626 -1.132 c
+54.537 -1.084 54.464 -1.024 54.405 -0.955 c
+54.347 -0.878 54.302 -0.801 54.273 -0.72 c
+54.243 -0.632 54.221 -0.544 54.214 -0.455 c
+54.203 -0.359 54.2 -0.264 54.2 -0.176 c
+h
+58.628 -1.323 m
+58.458 -1.323 58.308 -1.3 58.172 -1.263 c
+58.043 -1.216 57.929 -1.146 57.834 -1.058 c
+57.745 -0.97 57.676 -0.864 57.628 -0.735 c
+57.576 -0.598 57.554 -0.448 57.554 -0.279 c
+57.554 -0.073 57.587 0.096 57.657 0.235 c
+57.724 0.383 57.819 0.493 57.936 0.574 c
+58.061 0.661 58.204 0.724 58.363 0.765 c
+58.528 0.801 58.705 0.827 58.892 0.838 c
+59.612 0.853 l
+59.612 1.029 l
+59.612 1.147 59.601 1.249 59.583 1.338 c
+59.561 1.426 59.528 1.492 59.48 1.544 c
+59.44 1.602 59.392 1.64 59.333 1.661 c
+59.274 1.68 59.208 1.691 59.141 1.691 c
+59.072 1.691 59.01 1.68 58.95 1.661 c
+58.9 1.65 58.852 1.625 58.804 1.588 c
+58.763 1.559 58.73 1.507 58.701 1.441 c
+58.679 1.382 58.665 1.301 58.657 1.206 c
+57.716 1.249 l
+57.745 1.397 57.79 1.532 57.848 1.661 c
+57.915 1.786 58.01 1.897 58.127 1.985 c
+58.245 2.08 58.385 2.153 58.553 2.205 c
+58.73 2.253 58.936 2.278 59.172 2.278 c
+59.612 2.278 59.943 2.168 60.171 1.955 c
+60.406 1.75 60.523 1.441 60.523 1.029 c
+60.523 -0.235 l
+60.523 -0.455 l
+60.531 -0.515 60.546 -0.569 60.568 -0.617 c
+60.586 -0.658 60.616 -0.691 60.656 -0.72 c
+60.693 -0.742 60.744 -0.75 60.803 -0.75 c
+60.869 -0.75 60.939 -0.746 61.009 -0.735 c
+61.009 -1.22 l
+60.95 -1.23 60.895 -1.242 60.847 -1.249 c
+60.807 -1.26 60.766 -1.267 60.729 -1.278 c
+60.689 -1.286 60.645 -1.294 60.597 -1.294 c
+60.546 -1.3 60.487 -1.308 60.421 -1.308 c
+60.193 -1.308 60.028 -1.257 59.921 -1.146 c
+59.81 -1.028 59.748 -0.864 59.729 -0.646 c
+59.715 -0.646 l
+59.646 -0.756 59.575 -0.852 59.509 -0.941 c
+59.44 -1.022 59.363 -1.087 59.274 -1.146 c
+59.186 -1.205 59.087 -1.249 58.981 -1.278 c
+58.881 -1.308 58.763 -1.323 58.628 -1.323 c
+59.612 0.353 m
+59.186 0.339 l
+59.087 0.339 58.995 0.331 58.906 0.324 c
+58.826 0.312 58.76 0.287 58.701 0.25 c
+58.642 0.21 58.591 0.151 58.553 0.073 c
+58.513 0.004 58.495 -0.087 58.495 -0.205 c
+58.495 -0.374 58.528 -0.496 58.598 -0.573 c
+58.665 -0.654 58.763 -0.691 58.892 -0.691 c
+58.998 -0.691 59.098 -0.669 59.186 -0.617 c
+59.282 -0.569 59.363 -0.507 59.421 -0.426 c
+59.488 -0.349 59.538 -0.261 59.569 -0.162 c
+59.598 -0.055 59.612 0.059 59.612 0.177 c
+h
+63.261 -2.66 m
+63.045 -2.66 62.854 -2.634 62.688 -2.587 c
+62.519 -2.547 62.379 -2.484 62.262 -2.396 c
+62.144 -2.315 62.045 -2.219 61.967 -2.102 c
+61.898 -1.984 61.85 -1.855 61.821 -1.72 c
+62.717 -1.616 l
+62.754 -1.753 62.824 -1.859 62.923 -1.94 c
+63.019 -2.028 63.143 -2.072 63.291 -2.072 c
+63.379 -2.072 63.459 -2.057 63.54 -2.028 c
+63.617 -1.999 63.687 -1.944 63.746 -1.866 c
+63.806 -1.797 63.849 -1.705 63.879 -1.587 c
+63.916 -1.469 63.937 -1.323 63.937 -1.146 c
+63.937 -0.955 l
+63.937 -0.889 63.941 -0.83 63.952 -0.779 c
+63.952 -0.588 l
+63.937 -0.588 l
+63.839 -0.816 63.695 -0.977 63.511 -1.072 c
+63.324 -1.172 63.118 -1.22 62.894 -1.22 c
+62.688 -1.22 62.505 -1.183 62.35 -1.103 c
+62.203 -1.014 62.075 -0.897 61.967 -0.75 c
+61.869 -0.595 61.795 -0.411 61.747 -0.205 c
+61.696 0.008 61.674 0.243 61.674 0.5 c
+61.674 0.772 61.696 1.018 61.747 1.235 c
+61.806 1.448 61.887 1.632 61.998 1.779 c
+62.104 1.933 62.236 2.051 62.395 2.132 c
+62.549 2.22 62.736 2.264 62.952 2.264 c
+63.048 2.264 63.147 2.253 63.247 2.234 c
+63.342 2.213 63.43 2.18 63.511 2.132 c
+63.6 2.08 63.677 2.018 63.746 1.941 c
+63.824 1.86 63.886 1.768 63.937 1.661 c
+63.952 1.661 l
+63.952 1.808 l
+63.96 1.867 63.967 1.918 63.967 1.97 c
+63.974 2.029 63.982 2.076 63.982 2.117 c
+63.989 2.165 64 2.198 64.011 2.22 c
+64.863 2.22 l
+64.853 2.139 64.841 2.029 64.834 1.881 c
+64.834 1.411 l
+64.834 -1.161 l
+64.834 -1.415 64.797 -1.635 64.731 -1.822 c
+64.662 -2.006 64.558 -2.161 64.423 -2.278 c
+64.283 -2.403 64.118 -2.499 63.922 -2.557 c
+63.725 -2.624 63.504 -2.66 63.261 -2.66 c
+63.952 0.53 m
+63.952 0.742 63.926 0.919 63.879 1.058 c
+63.839 1.206 63.783 1.324 63.717 1.411 c
+63.658 1.5 63.588 1.559 63.511 1.588 c
+63.43 1.625 63.353 1.646 63.276 1.646 c
+63.177 1.646 63.085 1.621 62.997 1.573 c
+62.916 1.532 62.85 1.463 62.791 1.367 c
+62.74 1.279 62.696 1.162 62.659 1.014 c
+62.63 0.875 62.615 0.706 62.615 0.5 c
+62.615 0.125 62.673 -0.154 62.791 -0.338 c
+62.908 -0.515 63.07 -0.602 63.276 -0.602 c
+63.342 -0.602 63.415 -0.588 63.496 -0.558 c
+63.585 -0.521 63.658 -0.463 63.717 -0.382 c
+63.783 -0.294 63.839 -0.176 63.879 -0.029 c
+63.926 0.118 63.952 0.302 63.952 0.53 c
+68.895 -0.249 m
+68.895 -0.419 68.855 -0.569 68.777 -0.706 c
+68.707 -0.833 68.604 -0.947 68.469 -1.043 c
+68.34 -1.132 68.178 -1.201 67.983 -1.249 c
+67.796 -1.297 67.579 -1.323 67.336 -1.323 c
+67.109 -1.323 66.91 -1.308 66.734 -1.278 c
+66.557 -1.249 66.399 -1.201 66.264 -1.132 c
+66.124 -1.055 66.014 -0.955 65.925 -0.837 c
+65.838 -0.72 65.767 -0.573 65.72 -0.397 c
+66.528 -0.279 l
+66.547 -0.378 66.576 -0.455 66.617 -0.515 c
+66.665 -0.573 66.723 -0.617 66.792 -0.646 c
+66.859 -0.675 66.94 -0.702 67.028 -0.72 c
+67.116 -0.731 67.219 -0.735 67.336 -0.735 c
+67.432 -0.735 67.528 -0.731 67.616 -0.72 c
+67.704 -0.702 67.781 -0.675 67.851 -0.646 c
+67.918 -0.617 67.968 -0.58 67.999 -0.529 c
+68.035 -0.481 68.057 -0.419 68.057 -0.338 c
+68.057 -0.242 68.028 -0.168 67.968 -0.118 c
+67.918 -0.07 67.851 -0.029 67.763 0 c
+67.675 0.037 67.565 0.067 67.44 0.088 c
+67.322 0.118 67.189 0.148 67.043 0.177 c
+66.903 0.214 66.763 0.254 66.617 0.294 c
+66.476 0.342 66.352 0.405 66.235 0.485 c
+66.124 0.563 66.036 0.661 65.969 0.779 c
+65.9 0.897 65.867 1.048 65.867 1.235 c
+65.867 1.389 65.896 1.532 65.955 1.661 c
+66.021 1.798 66.117 1.912 66.235 1.999 c
+66.359 2.087 66.517 2.153 66.705 2.205 c
+66.888 2.253 67.101 2.278 67.336 2.278 c
+67.521 2.278 67.697 2.257 67.866 2.22 c
+68.031 2.19 68.178 2.135 68.307 2.058 c
+68.431 1.989 68.542 1.889 68.63 1.764 c
+68.718 1.646 68.777 1.503 68.807 1.338 c
+68.013 1.264 l
+67.991 1.341 67.961 1.405 67.924 1.455 c
+67.884 1.515 67.837 1.559 67.777 1.588 c
+67.726 1.625 67.664 1.65 67.586 1.661 c
+67.506 1.669 67.425 1.676 67.336 1.676 c
+67.12 1.676 66.958 1.646 66.852 1.588 c
+66.742 1.536 66.69 1.448 66.69 1.324 c
+66.69 1.243 66.708 1.18 66.748 1.132 c
+66.796 1.081 66.859 1.044 66.94 1.014 c
+67.028 0.985 67.124 0.956 67.234 0.927 c
+67.34 0.904 67.461 0.882 67.602 0.853 c
+67.756 0.823 67.914 0.783 68.072 0.736 c
+68.226 0.684 68.365 0.622 68.483 0.544 c
+68.601 0.464 68.697 0.36 68.777 0.235 c
+68.855 0.107 68.895 -0.055 68.895 -0.249 c
+70.221 -2.631 m
+70.221 -2.013 l
+71.294 -2.013 l
+71.294 2.896 l
+70.221 2.896 l
+70.221 3.514 l
+72.147 3.514 l
+72.147 -2.631 l
+h
+78.729 -2.631 m
+78.729 3.514 l
+80.654 3.514 l
+80.654 2.896 l
+79.581 2.896 l
+79.581 -2.013 l
+80.654 -2.013 l
+80.654 -2.631 l
+h
+85.052 1.47 m
+84.953 1.478 84.851 1.488 84.744 1.5 c
+84.634 1.518 84.512 1.529 84.377 1.529 c
+84.2 1.529 84.042 1.488 83.906 1.411 c
+83.766 1.341 83.649 1.243 83.554 1.118 c
+83.465 0.989 83.396 0.842 83.348 0.676 c
+83.307 0.507 83.288 0.331 83.288 0.148 c
+83.288 -1.263 l
+82.392 -1.263 l
+82.392 0.985 l
+82.392 1.11 82.381 1.235 82.363 1.353 c
+82.351 1.478 82.337 1.595 82.318 1.706 c
+82.307 1.823 82.293 1.918 82.274 1.999 c
+82.253 2.087 82.234 2.161 82.216 2.22 c
+83.097 2.22 l
+83.105 2.168 83.116 2.117 83.127 2.058 c
+83.145 1.999 83.16 1.933 83.171 1.867 c
+83.19 1.808 83.205 1.742 83.215 1.676 c
+83.223 1.606 83.234 1.544 83.245 1.484 c
+83.259 1.484 l
+83.296 1.602 83.348 1.709 83.406 1.808 c
+83.473 1.904 83.554 1.989 83.641 2.058 c
+83.73 2.124 83.833 2.18 83.951 2.22 c
+84.075 2.257 84.223 2.278 84.391 2.278 c
+84.516 2.278 84.634 2.271 84.744 2.264 c
+84.861 2.253 84.965 2.238 85.052 2.22 c
+h
+87.658 -1.323 m
+87.401 -1.323 87.173 -1.286 86.968 -1.22 c
+86.762 -1.142 86.585 -1.028 86.438 -0.881 c
+86.291 -0.727 86.174 -0.536 86.085 -0.309 c
+86.004 -0.084 85.968 0.181 85.968 0.485 c
+85.968 0.817 86.012 1.095 86.1 1.324 c
+86.195 1.559 86.324 1.742 86.482 1.881 c
+86.648 2.018 86.835 2.117 87.041 2.176 c
+87.246 2.242 87.456 2.278 87.673 2.278 c
+87.945 2.278 88.18 2.228 88.379 2.132 c
+88.584 2.043 88.749 1.912 88.878 1.735 c
+89.014 1.565 89.113 1.36 89.172 1.118 c
+89.239 0.882 89.275 0.618 89.275 0.324 c
+89.275 0.309 l
+86.908 0.309 l
+86.908 0.162 86.923 0.023 86.953 -0.103 c
+86.989 -0.231 87.045 -0.345 87.114 -0.44 c
+87.18 -0.529 87.265 -0.598 87.364 -0.646 c
+87.46 -0.698 87.573 -0.72 87.702 -0.72 c
+87.857 -0.72 87.996 -0.687 88.113 -0.617 c
+88.239 -0.551 88.327 -0.448 88.379 -0.309 c
+89.216 -0.382 l
+89.187 -0.481 89.131 -0.588 89.054 -0.706 c
+88.973 -0.816 88.871 -0.918 88.746 -1.014 c
+88.628 -1.103 88.474 -1.176 88.29 -1.234 c
+88.113 -1.294 87.901 -1.323 87.658 -1.323 c
+87.658 1.706 m
+87.57 1.706 87.481 1.691 87.394 1.661 c
+87.305 1.632 87.224 1.58 87.159 1.515 c
+87.089 1.445 87.03 1.357 86.982 1.249 c
+86.941 1.139 86.923 1.014 86.923 0.867 c
+88.393 0.867 l
+88.393 1.004 88.367 1.125 88.319 1.235 c
+88.279 1.341 88.224 1.43 88.158 1.5 c
+88.099 1.565 88.026 1.617 87.937 1.646 c
+87.849 1.683 87.754 1.706 87.658 1.706 c
+91.322 -1.263 m
+91.322 0.853 l
+91.322 1.018 91.315 1.154 91.307 1.264 c
+91.296 1.371 91.278 1.455 91.248 1.515 c
+91.226 1.58 91.197 1.632 91.16 1.661 c
+91.13 1.691 91.09 1.706 91.043 1.706 c
+90.984 1.706 90.929 1.676 90.881 1.617 c
+90.841 1.565 90.808 1.492 90.778 1.397 c
+90.748 1.309 90.723 1.195 90.704 1.058 c
+90.694 0.919 90.69 0.769 90.69 0.603 c
+90.69 -1.263 l
+89.94 -1.263 l
+89.94 1.47 l
+89.94 1.706 l
+89.94 1.926 l
+89.94 2.003 89.933 2.066 89.925 2.117 c
+89.925 2.22 l
+90.602 2.22 l
+90.602 2.132 l
+90.602 1.985 l
+90.609 1.926 90.617 1.867 90.617 1.808 c
+90.617 1.646 l
+90.631 1.646 l
+90.65 1.735 90.679 1.812 90.719 1.881 c
+90.756 1.959 90.8 2.029 90.852 2.087 c
+90.91 2.147 90.976 2.19 91.057 2.22 c
+91.134 2.257 91.223 2.278 91.322 2.278 c
+91.506 2.278 91.645 2.224 91.733 2.117 c
+91.829 2.018 91.899 1.86 91.939 1.646 c
+91.954 1.646 l
+91.99 1.742 92.031 1.831 92.071 1.912 c
+92.119 1.989 92.175 2.051 92.233 2.103 c
+92.292 2.161 92.358 2.205 92.439 2.234 c
+92.516 2.264 92.604 2.278 92.703 2.278 c
+92.84 2.278 92.954 2.253 93.042 2.205 c
+93.129 2.153 93.196 2.08 93.247 1.985 c
+93.306 1.885 93.343 1.757 93.365 1.602 c
+93.395 1.455 93.409 1.272 93.409 1.058 c
+93.409 -1.263 l
+92.689 -1.263 l
+92.689 0.853 l
+92.689 1.018 92.682 1.154 92.674 1.264 c
+92.663 1.371 92.645 1.455 92.615 1.515 c
+92.593 1.58 92.564 1.632 92.527 1.661 c
+92.498 1.691 92.458 1.706 92.41 1.706 c
+92.292 1.706 92.196 1.617 92.13 1.441 c
+92.071 1.272 92.042 1.014 92.042 0.661 c
+92.042 -1.263 l
+h
+97.43 0.485 m
+97.43 0.21 97.393 -0.04 97.326 -0.264 c
+97.256 -0.481 97.154 -0.669 97.018 -0.823 c
+96.878 -0.981 96.701 -1.103 96.489 -1.19 c
+96.271 -1.278 96.018 -1.323 95.724 -1.323 c
+95.448 -1.323 95.202 -1.278 94.989 -1.19 c
+94.783 -1.103 94.611 -0.981 94.475 -0.823 c
+94.335 -0.669 94.232 -0.481 94.166 -0.264 c
+94.096 -0.04 94.064 0.21 94.064 0.485 c
+94.064 0.739 94.093 0.975 94.151 1.191 c
+94.218 1.415 94.32 1.606 94.46 1.764 c
+94.596 1.929 94.772 2.058 94.989 2.147 c
+95.202 2.234 95.46 2.278 95.753 2.278 c
+96.065 2.278 96.327 2.234 96.533 2.147 c
+96.746 2.058 96.919 1.929 97.047 1.764 c
+97.183 1.606 97.282 1.415 97.341 1.191 c
+97.399 0.975 97.43 0.739 97.43 0.485 c
+96.474 0.485 m
+96.474 0.691 96.459 0.867 96.43 1.014 c
+96.408 1.162 96.371 1.283 96.312 1.382 c
+96.254 1.478 96.18 1.548 96.092 1.588 c
+96.003 1.636 95.893 1.661 95.768 1.661 c
+95.504 1.661 95.313 1.563 95.195 1.367 c
+95.078 1.18 95.018 0.886 95.018 0.485 c
+95.018 0.063 95.078 -0.242 95.195 -0.426 c
+95.313 -0.613 95.489 -0.706 95.724 -0.706 c
+95.849 -0.706 95.963 -0.687 96.063 -0.646 c
+96.158 -0.598 96.239 -0.525 96.298 -0.426 c
+96.364 -0.33 96.408 -0.205 96.43 -0.058 c
+96.459 0.088 96.474 0.269 96.474 0.485 c
+98.888 1.602 m
+98.344 1.602 l
+98.344 2.22 l
+98.932 2.22 l
+99.211 3.117 l
+99.785 3.117 l
+99.785 2.22 l
+101.019 2.22 l
+101.019 1.602 l
+99.785 1.602 l
+99.785 -0.103 l
+99.785 -0.323 l
+99.792 -0.393 99.814 -0.455 99.843 -0.515 c
+99.88 -0.565 99.936 -0.61 100.005 -0.646 c
+100.082 -0.675 100.196 -0.691 100.343 -0.691 c
+100.479 -0.691 100.615 -0.687 100.755 -0.675 c
+100.891 -0.658 101.023 -0.632 101.152 -0.602 c
+101.152 -1.205 l
+101.071 -1.216 100.994 -1.23 100.917 -1.249 c
+100.836 -1.26 100.759 -1.267 100.682 -1.278 c
+100.601 -1.286 100.512 -1.294 100.416 -1.294 c
+100.329 -1.3 100.229 -1.308 100.123 -1.308 c
+99.936 -1.308 99.774 -1.294 99.637 -1.263 c
+99.51 -1.227 99.396 -1.183 99.3 -1.132 c
+99.211 -1.084 99.138 -1.024 99.08 -0.955 c
+99.02 -0.878 98.976 -0.801 98.947 -0.72 c
+98.918 -0.632 98.895 -0.544 98.888 -0.455 c
+98.877 -0.359 98.874 -0.264 98.874 -0.176 c
+h
+103.904 -1.323 m
+103.647 -1.323 103.419 -1.286 103.213 -1.22 c
+103.007 -1.142 102.831 -1.028 102.685 -0.881 c
+102.537 -0.727 102.419 -0.536 102.332 -0.309 c
+102.251 -0.084 102.214 0.181 102.214 0.485 c
+102.214 0.817 102.257 1.095 102.346 1.324 c
+102.442 1.559 102.571 1.742 102.728 1.881 c
+102.893 2.018 103.082 2.117 103.287 2.176 c
+103.493 2.242 103.702 2.278 103.919 2.278 c
+104.191 2.278 104.426 2.228 104.624 2.132 c
+104.83 2.043 104.996 1.912 105.124 1.735 c
+105.26 1.565 105.359 1.36 105.418 1.118 c
+105.484 0.882 105.521 0.618 105.521 0.324 c
+105.521 0.309 l
+103.155 0.309 l
+103.155 0.162 103.169 0.023 103.198 -0.103 c
+103.236 -0.231 103.29 -0.345 103.36 -0.44 c
+103.427 -0.529 103.511 -0.598 103.61 -0.646 c
+103.706 -0.698 103.82 -0.72 103.948 -0.72 c
+104.102 -0.72 104.242 -0.687 104.36 -0.617 c
+104.485 -0.551 104.573 -0.448 104.624 -0.309 c
+105.463 -0.382 l
+105.433 -0.481 105.378 -0.588 105.301 -0.706 c
+105.22 -0.816 105.117 -0.918 104.992 -1.014 c
+104.875 -1.103 104.72 -1.176 104.536 -1.234 c
+104.36 -1.294 104.146 -1.323 103.904 -1.323 c
+103.904 1.706 m
+103.816 1.706 103.728 1.691 103.639 1.661 c
+103.552 1.632 103.471 1.58 103.404 1.515 c
+103.335 1.445 103.276 1.357 103.228 1.249 c
+103.188 1.139 103.169 1.014 103.169 0.867 c
+104.639 0.867 l
+104.639 1.004 104.613 1.125 104.566 1.235 c
+104.526 1.341 104.47 1.43 104.404 1.5 c
+104.345 1.565 104.272 1.617 104.183 1.646 c
+104.096 1.683 104 1.706 103.904 1.706 c
+106.774 -2.631 m
+106.774 -2.013 l
+107.847 -2.013 l
+107.847 2.896 l
+106.774 2.896 l
+106.774 3.514 l
+108.7 3.514 l
+108.7 -2.631 l
+h
+f
+Q
+q 1 0 0 1 95.2978 172.2447 cm
+0 0 m
+0 -2.103 l
+-0.675 -2.103 l
+-0.675 3.248 l
+1.029 3.248 l
+1.529 3.248 1.918 3.094 2.205 2.793 c
+2.488 2.499 2.631 2.105 2.631 1.617 c
+2.631 1.095 2.492 0.698 2.22 0.426 c
+1.955 0.151 1.577 0.008 1.088 0 c
+h
+0 0.573 m
+1.029 0.573 l
+1.33 0.573 1.563 0.661 1.72 0.837 c
+1.875 1.014 1.955 1.268 1.955 1.602 c
+1.955 1.926 1.871 2.183 1.706 2.381 c
+1.548 2.576 1.327 2.675 1.044 2.675 c
+0 2.675 l
+h
+5.439 -1.75 m
+5.222 -2.036 4.91 -2.176 4.499 -2.176 c
+4.135 -2.176 3.859 -2.055 3.675 -1.808 c
+3.499 -1.554 3.404 -1.191 3.396 -0.721 c
+3.396 1.881 l
+4.042 1.881 l
+4.042 -0.661 l
+4.042 -1.29 4.227 -1.602 4.601 -1.602 c
+5.002 -1.602 5.278 -1.426 5.424 -1.073 c
+5.424 1.881 l
+6.072 1.881 l
+6.072 -2.103 l
+5.453 -2.103 l
+h
+8.996 -1.088 m
+8.996 -0.941 8.941 -0.819 8.834 -0.721 c
+8.724 -0.625 8.518 -0.507 8.217 -0.368 c
+7.872 -0.221 7.629 -0.1 7.483 0 c
+7.335 0.106 7.225 0.224 7.159 0.353 c
+7.089 0.478 7.056 0.636 7.056 0.823 c
+7.056 1.147 7.173 1.415 7.408 1.631 c
+7.644 1.845 7.945 1.955 8.32 1.955 c
+8.702 1.955 9.01 1.841 9.246 1.617 c
+9.481 1.389 9.599 1.103 9.599 0.75 c
+8.952 0.75 l
+8.952 0.926 8.894 1.076 8.775 1.205 c
+8.659 1.33 8.503 1.396 8.32 1.396 c
+8.121 1.396 7.971 1.341 7.865 1.234 c
+7.755 1.135 7.703 1.003 7.703 0.837 c
+7.703 0.709 7.739 0.602 7.82 0.515 c
+7.898 0.434 8.088 0.33 8.393 0.206 c
+8.871 0.018 9.202 -0.169 9.378 -0.353 c
+9.555 -0.53 9.643 -0.757 9.643 -1.029 c
+9.643 -1.382 9.518 -1.661 9.276 -1.867 c
+9.041 -2.072 8.724 -2.176 8.335 -2.176 c
+7.913 -2.176 7.574 -2.058 7.321 -1.823 c
+7.063 -1.58 6.938 -1.276 6.938 -0.912 c
+7.585 -0.912 l
+7.593 -1.139 7.662 -1.316 7.791 -1.44 c
+7.915 -1.558 8.1 -1.617 8.335 -1.617 c
+8.548 -1.617 8.709 -1.569 8.819 -1.47 c
+8.937 -1.374 8.996 -1.246 8.996 -1.088 c
+11.127 1.469 m
+11.381 1.793 11.701 1.955 12.083 1.955 c
+12.788 1.955 13.145 1.484 13.156 0.544 c
+13.156 -2.103 l
+12.509 -2.103 l
+12.509 0.515 l
+12.509 0.827 12.454 1.047 12.347 1.176 c
+12.237 1.301 12.083 1.367 11.877 1.367 c
+11.719 1.367 11.572 1.311 11.437 1.205 c
+11.308 1.095 11.205 0.959 11.127 0.794 c
+11.127 -2.103 l
+10.481 -2.103 l
+10.481 3.542 l
+11.127 3.542 l
+h
+16.536 -2.103 -0.646 5.644 re
+17.419 0.073 m
+17.419 0.65 17.555 1.106 17.831 1.44 c
+18.113 1.782 18.485 1.955 18.947 1.955 c
+19.407 1.955 19.774 1.786 20.05 1.455 c
+20.333 1.132 20.48 0.683 20.491 0.118 c
+20.491 -0.309 l
+20.491 -0.879 20.347 -1.334 20.064 -1.675 c
+19.789 -2.01 19.422 -2.176 18.962 -2.176 c
+18.499 -2.176 18.128 -2.014 17.845 -1.691 c
+17.569 -1.359 17.426 -0.919 17.419 -0.368 c
+h
+18.066 -0.309 m
+18.066 -0.713 18.143 -1.029 18.301 -1.264 c
+18.466 -1.5 18.687 -1.617 18.962 -1.617 c
+19.528 -1.617 19.822 -1.205 19.844 -0.382 c
+19.844 0.073 l
+19.844 0.474 19.759 0.794 19.594 1.029 c
+19.437 1.271 19.219 1.396 18.947 1.396 c
+18.683 1.396 18.466 1.271 18.301 1.029 c
+18.143 0.794 18.066 0.474 18.066 0.073 c
+h
+22.71 -1.617 m
+22.924 -1.617 23.096 -1.554 23.225 -1.426 c
+23.36 -1.29 23.435 -1.099 23.445 -0.852 c
+24.062 -0.852 l
+24.041 -1.235 23.905 -1.554 23.651 -1.808 c
+23.394 -2.055 23.082 -2.176 22.71 -2.176 c
+22.218 -2.176 21.843 -2.024 21.579 -1.72 c
+21.321 -1.407 21.197 -0.941 21.197 -0.324 c
+21.197 0.118 l
+21.197 0.713 21.321 1.168 21.579 1.484 c
+21.843 1.797 22.218 1.955 22.71 1.955 c
+23.111 1.955 23.431 1.822 23.666 1.558 c
+23.908 1.301 24.041 0.955 24.062 0.515 c
+23.445 0.515 l
+23.423 0.808 23.35 1.029 23.225 1.176 c
+23.107 1.323 22.934 1.396 22.71 1.396 c
+22.416 1.396 22.2 1.297 22.064 1.103 c
+21.924 0.915 21.851 0.606 21.843 0.176 c
+21.843 -0.339 l
+21.843 -0.809 21.909 -1.143 22.049 -1.338 c
+22.196 -1.525 22.416 -1.617 22.71 -1.617 c
+26.855 -2.103 m
+26.815 -2.014 26.789 -1.867 26.782 -1.661 c
+26.547 -2.007 26.253 -2.176 25.9 -2.176 c
+25.536 -2.176 25.253 -2.08 25.047 -1.881 c
+24.849 -1.675 24.754 -1.389 24.754 -1.014 c
+24.754 -0.613 24.889 -0.294 25.165 -0.059 c
+25.437 0.183 25.812 0.309 26.282 0.309 c
+26.768 0.309 l
+26.768 0.735 l
+26.768 0.97 26.712 1.135 26.606 1.234 c
+26.495 1.341 26.334 1.396 26.121 1.396 c
+25.922 1.396 25.76 1.338 25.635 1.22 c
+25.518 1.103 25.459 0.955 25.459 0.779 c
+24.812 0.779 l
+24.812 0.974 24.871 1.165 24.989 1.352 c
+25.114 1.536 25.276 1.683 25.473 1.793 c
+25.679 1.899 25.907 1.955 26.165 1.955 c
+26.566 1.955 26.87 1.852 27.076 1.646 c
+27.289 1.44 27.403 1.147 27.414 0.764 c
+27.414 -1.249 l
+27.414 -1.554 27.451 -1.819 27.532 -2.043 c
+27.532 -2.103 l
+h
+25.988 -1.588 m
+26.154 -1.588 26.304 -1.544 26.444 -1.455 c
+26.591 -1.367 26.697 -1.257 26.768 -1.118 c
+26.768 -0.177 l
+26.4 -0.177 l
+26.084 -0.177 25.841 -0.246 25.665 -0.382 c
+25.488 -0.511 25.4 -0.698 25.4 -0.941 c
+25.4 -1.168 25.444 -1.334 25.533 -1.44 c
+25.621 -1.54 25.772 -1.588 25.988 -1.588 c
+29.12 -2.103 -0.647 5.644 re
+33.234 -1.617 m
+33.448 -1.617 33.621 -1.554 33.749 -1.426 c
+33.885 -1.29 33.959 -1.099 33.97 -0.852 c
+34.587 -0.852 l
+34.566 -1.235 34.429 -1.554 34.175 -1.808 c
+33.918 -2.055 33.606 -2.176 33.234 -2.176 c
+32.742 -2.176 32.368 -2.024 32.103 -1.72 c
+31.846 -1.407 31.721 -0.941 31.721 -0.324 c
+31.721 0.118 l
+31.721 0.713 31.846 1.168 32.103 1.484 c
+32.368 1.797 32.742 1.955 33.234 1.955 c
+33.635 1.955 33.955 1.822 34.19 1.558 c
+34.433 1.301 34.566 0.955 34.587 0.515 c
+33.97 0.515 l
+33.948 0.808 33.874 1.029 33.749 1.176 c
+33.631 1.323 33.459 1.396 33.234 1.396 c
+32.941 1.396 32.725 1.297 32.588 1.103 c
+32.449 0.915 32.375 0.606 32.368 0.176 c
+32.368 -0.339 l
+32.368 -0.809 32.434 -1.143 32.573 -1.338 c
+32.721 -1.525 32.941 -1.617 33.234 -1.617 c
+35.983 1.469 m
+36.237 1.793 36.557 1.955 36.939 1.955 c
+37.645 1.955 38.001 1.484 38.012 0.544 c
+38.012 -2.103 l
+37.365 -2.103 l
+37.365 0.515 l
+37.365 0.827 37.311 1.047 37.203 1.176 c
+37.093 1.301 36.939 1.367 36.733 1.367 c
+36.575 1.367 36.428 1.311 36.292 1.205 c
+36.164 1.095 36.06 0.959 35.983 0.794 c
+35.983 -2.103 l
+35.337 -2.103 l
+35.337 3.542 l
+35.983 3.542 l
+h
+41.011 -2.103 m
+40.97 -2.014 40.945 -1.867 40.937 -1.661 c
+40.702 -2.007 40.409 -2.176 40.055 -2.176 c
+39.692 -2.176 39.409 -2.08 39.202 -1.881 c
+39.004 -1.675 38.909 -1.389 38.909 -1.014 c
+38.909 -0.613 39.044 -0.294 39.32 -0.059 c
+39.592 0.183 39.967 0.309 40.438 0.309 c
+40.922 0.309 l
+40.922 0.735 l
+40.922 0.97 40.868 1.135 40.76 1.234 c
+40.65 1.341 40.488 1.396 40.276 1.396 c
+40.077 1.396 39.916 1.338 39.79 1.22 c
+39.673 1.103 39.615 0.955 39.615 0.779 c
+38.967 0.779 l
+38.967 0.974 39.027 1.165 39.144 1.352 c
+39.269 1.536 39.43 1.683 39.629 1.793 c
+39.835 1.899 40.062 1.955 40.32 1.955 c
+40.721 1.955 41.026 1.852 41.231 1.646 c
+41.444 1.44 41.558 1.147 41.569 0.764 c
+41.569 -1.249 l
+41.569 -1.554 41.606 -1.819 41.687 -2.043 c
+41.687 -2.103 l
+h
+40.143 -1.588 m
+40.309 -1.588 40.459 -1.544 40.599 -1.455 c
+40.746 -1.367 40.853 -1.257 40.922 -1.118 c
+40.922 -0.177 l
+40.555 -0.177 l
+40.239 -0.177 39.996 -0.246 39.82 -0.382 c
+39.644 -0.511 39.555 -0.698 39.555 -0.941 c
+39.555 -1.168 39.599 -1.334 39.688 -1.44 c
+39.776 -1.54 39.927 -1.588 40.143 -1.588 c
+43.187 1.881 m
+43.201 1.44 l
+43.455 1.782 43.778 1.955 44.171 1.955 c
+44.877 1.955 45.233 1.484 45.244 0.544 c
+45.244 -2.103 l
+44.598 -2.103 l
+44.598 0.515 l
+44.598 0.827 44.542 1.047 44.436 1.176 c
+44.326 1.301 44.171 1.367 43.965 1.367 c
+43.807 1.367 43.661 1.311 43.524 1.205 c
+43.395 1.095 43.293 0.959 43.216 0.794 c
+43.216 -2.103 l
+42.568 -2.103 l
+42.568 1.881 l
+h
+46.082 0.073 m
+46.082 0.69 46.192 1.153 46.42 1.469 c
+46.644 1.793 46.979 1.955 47.42 1.955 c
+47.82 1.955 48.125 1.778 48.33 1.425 c
+48.375 1.881 l
+48.963 1.881 l
+48.963 -2.146 l
+48.963 -2.635 48.835 -3.013 48.581 -3.278 c
+48.324 -3.543 47.971 -3.675 47.522 -3.675 c
+47.324 -3.675 47.104 -3.624 46.861 -3.528 c
+46.615 -3.429 46.435 -3.308 46.317 -3.161 c
+46.582 -2.72 l
+46.846 -2.984 47.144 -3.117 47.478 -3.117 c
+48.014 -3.117 48.29 -2.822 48.301 -2.234 c
+48.301 -1.706 l
+48.095 -2.022 47.794 -2.176 47.405 -2.176 c
+46.994 -2.176 46.67 -2.024 46.435 -1.72 c
+46.207 -1.407 46.09 -0.956 46.082 -0.368 c
+h
+46.743 -0.309 m
+46.743 -0.75 46.806 -1.081 46.934 -1.294 c
+47.06 -1.5 47.276 -1.602 47.581 -1.602 c
+47.904 -1.602 48.143 -1.437 48.301 -1.103 c
+48.301 0.881 l
+48.132 1.205 47.894 1.367 47.581 1.367 c
+47.287 1.367 47.071 1.264 46.934 1.058 c
+46.806 0.852 46.743 0.529 46.743 0.088 c
+h
+51.344 -2.176 m
+50.844 -2.176 50.462 -2.028 50.198 -1.735 c
+49.933 -1.44 49.801 -1.007 49.801 -0.426 c
+49.801 0.044 l
+49.801 0.64 49.926 1.106 50.183 1.44 c
+50.447 1.782 50.807 1.955 51.27 1.955 c
+51.73 1.955 52.072 1.801 52.299 1.5 c
+52.535 1.205 52.656 0.742 52.667 0.118 c
+52.667 -0.309 l
+50.447 -0.309 l
+50.447 -0.397 l
+50.447 -0.831 50.524 -1.143 50.682 -1.338 c
+50.848 -1.525 51.079 -1.617 51.374 -1.617 c
+51.568 -1.617 51.741 -1.584 51.888 -1.515 c
+52.035 -1.437 52.171 -1.319 52.299 -1.162 c
+52.638 -1.573 l
+52.351 -1.977 51.921 -2.176 51.344 -2.176 c
+51.27 1.396 m
+50.995 1.396 50.793 1.301 50.668 1.117 c
+50.539 0.929 50.466 0.64 50.447 0.249 c
+52.02 0.249 l
+52.02 0.338 l
+51.998 0.72 51.933 0.989 51.814 1.147 c
+51.697 1.311 51.513 1.396 51.27 1.396 c
+55.386 -1.088 m
+55.386 -0.941 55.331 -0.819 55.225 -0.721 c
+55.114 -0.625 54.909 -0.507 54.607 -0.368 c
+54.262 -0.221 54.019 -0.1 53.872 0 c
+53.726 0.106 53.616 0.224 53.549 0.353 c
+53.479 0.478 53.446 0.636 53.446 0.823 c
+53.446 1.147 53.564 1.415 53.799 1.631 c
+54.034 1.845 54.335 1.955 54.711 1.955 c
+55.093 1.955 55.401 1.841 55.636 1.617 c
+55.871 1.389 55.989 1.103 55.989 0.75 c
+55.342 0.75 l
+55.342 0.926 55.283 1.076 55.166 1.205 c
+55.048 1.33 54.894 1.396 54.711 1.396 c
+54.512 1.396 54.362 1.341 54.254 1.234 c
+54.144 1.135 54.092 1.003 54.092 0.837 c
+54.092 0.709 54.129 0.602 54.21 0.515 c
+54.287 0.434 54.479 0.33 54.784 0.206 c
+55.262 0.018 55.592 -0.169 55.769 -0.353 c
+55.945 -0.53 56.033 -0.757 56.033 -1.029 c
+56.033 -1.382 55.908 -1.661 55.665 -1.867 c
+55.43 -2.072 55.114 -2.176 54.725 -2.176 c
+54.302 -2.176 53.965 -2.058 53.711 -1.823 c
+53.454 -1.58 53.329 -1.276 53.329 -0.912 c
+53.975 -0.912 l
+53.982 -1.139 54.052 -1.316 54.181 -1.44 c
+54.306 -1.558 54.49 -1.617 54.725 -1.617 c
+54.938 -1.617 55.1 -1.569 55.21 -1.47 c
+55.328 -1.374 55.386 -1.246 55.386 -1.088 c
+59.443 2.836 m
+59.443 1.881 l
+60.046 1.881 l
+60.046 1.352 l
+59.443 1.352 l
+59.443 -1.118 l
+59.443 -1.276 59.465 -1.393 59.517 -1.47 c
+59.575 -1.551 59.664 -1.588 59.781 -1.588 c
+59.87 -1.588 59.958 -1.573 60.046 -1.544 c
+60.046 -2.103 l
+59.899 -2.15 59.744 -2.176 59.59 -2.176 c
+59.333 -2.176 59.139 -2.084 59.002 -1.897 c
+58.863 -1.712 58.796 -1.452 58.796 -1.118 c
+58.796 1.352 l
+58.194 1.352 l
+58.194 1.881 l
+58.796 1.881 l
+58.796 2.836 l
+h
+60.604 0.073 m
+60.604 0.65 60.741 1.106 61.016 1.44 c
+61.3 1.782 61.67 1.955 62.133 1.955 c
+62.593 1.955 62.96 1.786 63.236 1.455 c
+63.519 1.132 63.665 0.683 63.677 0.118 c
+63.677 -0.309 l
+63.677 -0.879 63.534 -1.334 63.251 -1.675 c
+62.975 -2.01 62.607 -2.176 62.148 -2.176 c
+61.685 -2.176 61.314 -2.014 61.03 -1.691 c
+60.755 -1.359 60.612 -0.919 60.604 -0.368 c
+h
+61.252 -0.309 m
+61.252 -0.713 61.329 -1.029 61.487 -1.264 c
+61.652 -1.5 61.872 -1.617 62.148 -1.617 c
+62.713 -1.617 63.008 -1.205 63.03 -0.382 c
+63.03 0.073 l
+63.03 0.474 62.946 0.794 62.78 1.029 c
+62.622 1.271 62.405 1.396 62.133 1.396 c
+61.869 1.396 61.652 1.271 61.487 1.029 c
+61.329 0.794 61.252 0.474 61.252 0.073 c
+h
+67.087 2.836 m
+67.087 1.881 l
+67.69 1.881 l
+67.69 1.352 l
+67.087 1.352 l
+67.087 -1.118 l
+67.087 -1.276 67.109 -1.393 67.16 -1.47 c
+67.22 -1.551 67.307 -1.588 67.425 -1.588 c
+67.513 -1.588 67.602 -1.573 67.69 -1.544 c
+67.69 -2.103 l
+67.542 -2.15 67.388 -2.176 67.234 -2.176 c
+66.977 -2.176 66.782 -2.084 66.646 -1.897 c
+66.507 -1.712 66.44 -1.452 66.44 -1.118 c
+66.44 1.352 l
+65.838 1.352 l
+65.838 1.881 l
+66.44 1.881 l
+66.44 2.836 l
+h
+69.101 1.469 m
+69.354 1.793 69.674 1.955 70.056 1.955 c
+70.761 1.955 71.118 1.484 71.129 0.544 c
+71.129 -2.103 l
+70.482 -2.103 l
+70.482 0.515 l
+70.482 0.827 70.428 1.047 70.32 1.176 c
+70.21 1.301 70.056 1.367 69.85 1.367 c
+69.692 1.367 69.545 1.311 69.41 1.205 c
+69.281 1.095 69.178 0.959 69.101 0.794 c
+69.101 -2.103 l
+68.454 -2.103 l
+68.454 3.542 l
+69.101 3.542 l
+h
+73.51 -2.176 m
+73.011 -2.176 72.629 -2.028 72.364 -1.735 c
+72.099 -1.44 71.967 -1.007 71.967 -0.426 c
+71.967 0.044 l
+71.967 0.64 72.092 1.106 72.349 1.44 c
+72.614 1.782 72.974 1.955 73.437 1.955 c
+73.896 1.955 74.238 1.801 74.466 1.5 c
+74.701 1.205 74.823 0.742 74.833 0.118 c
+74.833 -0.309 l
+72.614 -0.309 l
+72.614 -0.397 l
+72.614 -0.831 72.691 -1.143 72.849 -1.338 c
+73.015 -1.525 73.246 -1.617 73.539 -1.617 c
+73.734 -1.617 73.907 -1.584 74.054 -1.515 c
+74.201 -1.437 74.337 -1.319 74.466 -1.162 c
+74.804 -1.573 l
+74.517 -1.977 74.087 -2.176 73.51 -2.176 c
+73.437 1.396 m
+73.161 1.396 72.959 1.301 72.834 1.117 c
+72.706 0.929 72.632 0.64 72.614 0.249 c
+74.187 0.249 l
+74.187 0.338 l
+74.164 0.72 74.098 0.989 73.981 1.147 c
+73.863 1.311 73.68 1.396 73.437 1.396 c
+f
+Q
+q 1 0 0 1 174.4817 173.112 cm
+0 0 m
+-0.338 0.029 l
+-0.625 0.029 -0.816 -0.096 -0.911 -0.339 c
+-0.911 -2.97 l
+-1.955 -2.97 l
+-1.955 1.014 l
+-0.985 1.014 l
+-0.955 0.573 l
+-0.79 0.914 -0.559 1.087 -0.264 1.087 c
+-0.147 1.087 -0.055 1.066 0.015 1.028 c
+h
+2.072 -3.043 m
+1.544 -3.043 1.125 -2.889 0.823 -2.573 c
+0.53 -2.249 0.382 -1.79 0.382 -1.191 c
+0.382 -0.882 l
+0.382 -0.258 0.518 0.228 0.794 0.573 c
+1.066 0.914 1.459 1.087 1.97 1.087 c
+2.469 1.087 2.841 0.926 3.088 0.602 c
+3.341 0.279 3.473 -0.198 3.484 -0.823 c
+3.484 -1.324 l
+1.411 -1.324 l
+1.43 -1.617 1.492 -1.834 1.602 -1.97 c
+1.72 -2.109 1.9 -2.176 2.147 -2.176 c
+2.488 -2.176 2.778 -2.058 3.013 -1.823 c
+3.425 -2.455 l
+3.296 -2.631 3.109 -2.774 2.866 -2.881 c
+2.62 -2.988 2.356 -3.043 2.072 -3.043 c
+1.426 -0.603 m
+2.455 -0.603 l
+2.455 -0.5 l
+2.455 -0.265 2.415 -0.088 2.338 0.029 c
+2.267 0.154 2.139 0.22 1.955 0.22 c
+1.779 0.22 1.646 0.151 1.558 0.014 c
+1.478 -0.114 1.434 -0.32 1.426 -0.603 c
+5.042 1.014 m
+5.072 0.646 l
+5.307 0.941 5.615 1.087 5.997 1.087 c
+6.398 1.087 6.677 0.904 6.835 0.544 c
+7.071 0.904 7.398 1.087 7.82 1.087 c
+8.515 1.087 8.867 0.602 8.879 -0.368 c
+8.879 -2.97 l
+7.85 -2.97 l
+7.85 -0.426 l
+7.85 -0.202 7.813 -0.04 7.747 0.058 c
+7.688 0.154 7.578 0.205 7.423 0.205 c
+7.225 0.205 7.086 0.087 6.997 -0.148 c
+6.997 -2.97 l
+5.953 -2.97 l
+5.953 -0.441 l
+5.953 -0.206 5.924 -0.04 5.866 0.058 c
+5.806 0.154 5.696 0.205 5.542 0.205 c
+5.365 0.205 5.222 0.11 5.116 -0.073 c
+5.116 -2.97 l
+4.072 -2.97 l
+4.072 1.014 l
+h
+9.54 -0.853 m
+9.54 -0.246 9.679 0.228 9.966 0.573 c
+10.249 0.914 10.643 1.087 11.142 1.087 c
+11.649 1.087 12.046 0.914 12.333 0.573 c
+12.615 0.228 12.759 -0.246 12.759 -0.853 c
+12.759 -1.118 l
+12.759 -1.716 12.615 -2.186 12.333 -2.529 c
+12.046 -2.874 11.649 -3.043 11.142 -3.043 c
+10.631 -3.043 10.234 -2.874 9.951 -2.529 c
+9.675 -2.186 9.54 -1.713 9.54 -1.103 c
+h
+10.583 -1.118 m
+10.583 -1.823 10.768 -2.176 11.142 -2.176 c
+11.495 -2.176 11.686 -1.881 11.715 -1.294 c
+11.715 -0.853 l
+11.715 -0.493 11.664 -0.221 11.568 -0.044 c
+11.469 0.132 11.326 0.22 11.142 0.22 c
+10.965 0.22 10.826 0.132 10.73 -0.044 c
+10.631 -0.221 10.583 -0.493 10.583 -0.853 c
+h
+14.612 1.984 m
+14.612 1.014 l
+15.14 1.014 l
+15.14 0.22 l
+14.612 0.22 l
+14.612 -1.75 l
+14.612 -1.908 14.629 -2.014 14.67 -2.072 c
+14.718 -2.132 14.803 -2.161 14.92 -2.161 c
+15.026 -2.161 15.111 -2.153 15.169 -2.132 c
+15.169 -2.94 l
+14.993 -3.007 14.803 -3.043 14.596 -3.043 c
+13.92 -3.043 13.575 -2.657 13.567 -1.881 c
+13.567 0.22 l
+13.112 0.22 l
+13.112 1.014 l
+13.567 1.014 l
+13.567 1.984 l
+h
+17.315 -3.043 m
+16.787 -3.043 16.368 -2.889 16.066 -2.573 c
+15.772 -2.249 15.626 -1.79 15.626 -1.191 c
+15.626 -0.882 l
+15.626 -0.258 15.761 0.228 16.037 0.573 c
+16.309 0.914 16.702 1.087 17.213 1.087 c
+17.712 1.087 18.084 0.926 18.33 0.602 c
+18.583 0.279 18.716 -0.198 18.726 -0.823 c
+18.726 -1.324 l
+16.654 -1.324 l
+16.673 -1.617 16.735 -1.834 16.845 -1.97 c
+16.963 -2.109 17.143 -2.176 17.39 -2.176 c
+17.731 -2.176 18.021 -2.058 18.256 -1.823 c
+18.668 -2.455 l
+18.539 -2.631 18.352 -2.774 18.109 -2.881 c
+17.864 -2.988 17.598 -3.043 17.315 -3.043 c
+16.669 -0.603 m
+17.698 -0.603 l
+17.698 -0.5 l
+17.698 -0.265 17.658 -0.088 17.581 0.029 c
+17.511 0.154 17.382 0.22 17.199 0.22 c
+17.022 0.22 16.889 0.151 16.802 0.014 c
+16.721 -0.114 16.676 -0.32 16.669 -0.603 c
+f
+Q
+q 1 0 0 1 193.9437 170.4949 cm
+0 0 m
+0 0.118 0.033 0.214 0.103 0.294 c
+0.168 0.372 0.272 0.412 0.411 0.412 c
+0.559 0.412 0.665 0.372 0.735 0.294 c
+0.812 0.214 0.852 0.118 0.852 0 c
+0.852 -0.11 0.812 -0.201 0.735 -0.278 c
+0.665 -0.356 0.559 -0.396 0.411 -0.396 c
+0.272 -0.396 0.168 -0.356 0.103 -0.278 c
+0.033 -0.201 0 -0.11 0 0 c
+6.938 4.998 m
+6.938 1.191 l
+6.938 0.691 6.79 0.294 6.497 0 c
+6.21 -0.286 5.81 -0.426 5.292 -0.426 c
+4.762 -0.426 4.355 -0.29 4.072 -0.014 c
+3.796 0.269 3.659 0.669 3.659 1.191 c
+3.659 4.998 l
+4.322 4.998 l
+4.322 1.22 l
+4.322 0.857 4.395 0.588 4.542 0.412 c
+4.696 0.235 4.946 0.148 5.292 0.148 c
+5.633 0.148 5.88 0.235 6.026 0.412 c
+6.181 0.588 6.262 0.857 6.262 1.22 c
+6.262 4.998 l
+h
+9.834 0.662 m
+9.834 0.809 9.778 0.93 9.672 1.029 c
+9.562 1.125 9.356 1.243 9.055 1.382 c
+8.709 1.529 8.467 1.65 8.32 1.75 c
+8.172 1.856 8.062 1.974 7.996 2.103 c
+7.927 2.228 7.893 2.386 7.893 2.573 c
+7.893 2.897 8.01 3.165 8.246 3.381 c
+8.481 3.595 8.783 3.705 9.157 3.705 c
+9.539 3.705 9.848 3.591 10.083 3.367 c
+10.318 3.138 10.437 2.852 10.437 2.5 c
+9.789 2.5 l
+9.789 2.675 9.731 2.826 9.612 2.955 c
+9.496 3.08 9.341 3.146 9.157 3.146 c
+8.959 3.146 8.808 3.091 8.702 2.984 c
+8.592 2.885 8.54 2.753 8.54 2.587 c
+8.54 2.459 8.577 2.352 8.658 2.264 c
+8.735 2.183 8.926 2.08 9.231 1.956 c
+9.708 1.768 10.04 1.58 10.215 1.397 c
+10.392 1.22 10.48 0.992 10.48 0.721 c
+10.48 0.368 10.356 0.088 10.113 -0.118 c
+9.878 -0.323 9.562 -0.426 9.172 -0.426 c
+8.75 -0.426 8.411 -0.309 8.158 -0.073 c
+7.9 0.169 7.775 0.474 7.775 0.838 c
+8.422 0.838 l
+8.43 0.611 8.5 0.434 8.628 0.309 c
+8.753 0.192 8.937 0.133 9.172 0.133 c
+9.385 0.133 9.547 0.181 9.657 0.279 c
+9.774 0.375 9.834 0.504 9.834 0.662 c
+12.744 -0.426 m
+12.244 -0.426 11.862 -0.278 11.597 0.015 c
+11.333 0.309 11.2 0.743 11.2 1.324 c
+11.2 1.794 l
+11.2 2.389 11.326 2.856 11.582 3.19 c
+11.848 3.532 12.207 3.705 12.67 3.705 c
+13.13 3.705 13.471 3.55 13.699 3.249 c
+13.934 2.955 14.056 2.492 14.067 1.867 c
+14.067 1.441 l
+11.848 1.441 l
+11.848 1.353 l
+11.848 0.919 11.925 0.607 12.083 0.412 c
+12.247 0.225 12.479 0.133 12.773 0.133 c
+12.968 0.133 13.14 0.166 13.288 0.235 c
+13.435 0.313 13.571 0.431 13.699 0.588 c
+14.038 0.177 l
+13.751 -0.228 13.321 -0.426 12.744 -0.426 c
+12.67 3.146 m
+12.395 3.146 12.193 3.051 12.068 2.866 c
+11.939 2.679 11.866 2.389 11.848 1.999 c
+13.42 1.999 l
+13.42 2.087 l
+13.398 2.469 13.332 2.738 13.215 2.897 c
+13.097 3.061 12.913 3.146 12.67 3.146 c
+f
+Q
+212.273 172.01 -1.793 0.867 re
+215.007 172.01 -1.793 0.867 re
+217.08 175.096 m
+217.08 174.126 l
+217.609 174.126 l
+217.609 173.332 l
+217.08 173.332 l
+217.08 171.362 l
+217.08 171.204 217.098 171.098 217.139 171.04 c
+217.187 170.98 217.271 170.951 217.389 170.951 c
+217.495 170.951 217.58 170.959 217.638 170.98 c
+217.638 170.172 l
+217.462 170.105 217.271 170.069 217.065 170.069 c
+216.389 170.069 216.044 170.455 216.036 171.231 c
+216.036 173.332 l
+215.581 173.332 l
+215.581 174.126 l
+216.036 174.126 l
+216.036 175.096 l
+h
+220.063 170.142 m
+220.034 170.201 220.005 170.304 219.976 170.452 c
+219.788 170.194 219.538 170.069 219.226 170.069 c
+218.891 170.069 218.612 170.176 218.388 170.392 c
+218.172 170.616 218.064 170.907 218.064 171.26 c
+218.064 171.671 218.197 171.987 218.461 172.216 c
+218.727 172.451 219.108 172.568 219.608 172.568 c
+219.932 172.568 l
+219.932 172.891 l
+219.932 173.068 219.895 173.189 219.828 173.259 c
+219.77 173.336 219.681 173.376 219.564 173.376 c
+219.307 173.376 219.182 173.222 219.182 172.921 c
+218.138 172.921 l
+218.138 173.292 218.274 173.597 218.55 173.832 c
+218.822 174.074 219.171 174.2 219.593 174.2 c
+220.034 174.2 220.373 174.082 220.608 173.847 c
+220.85 173.619 220.975 173.295 220.975 172.877 c
+220.975 171.009 l
+220.975 170.664 221.023 170.396 221.122 170.201 c
+221.122 170.142 l
+h
+219.461 170.892 m
+219.568 170.892 219.66 170.911 219.741 170.951 c
+219.828 170.999 219.891 171.057 219.932 171.127 c
+219.932 171.95 l
+219.681 171.95 l
+219.505 171.95 219.362 171.892 219.255 171.774 c
+219.156 171.664 219.108 171.517 219.108 171.333 c
+219.108 171.04 219.226 170.892 219.461 170.892 c
+221.592 172.259 m
+221.592 172.906 221.71 173.391 221.945 173.714 c
+222.18 174.038 222.511 174.2 222.945 174.2 c
+223.298 174.2 223.569 174.057 223.768 173.773 c
+223.812 174.126 l
+224.753 174.126 l
+224.753 170.142 l
+224.753 169.635 224.609 169.246 224.327 168.981 c
+224.04 168.709 223.635 168.57 223.107 168.57 c
+222.879 168.57 222.644 168.613 222.401 168.702 c
+222.166 168.79 221.989 168.904 221.871 169.041 c
+222.224 169.76 l
+222.32 169.654 222.449 169.569 222.606 169.511 c
+222.761 169.444 222.908 169.407 223.047 169.407 c
+223.283 169.407 223.448 169.467 223.547 169.584 c
+223.654 169.694 223.709 169.87 223.709 170.113 c
+223.709 170.466 l
+223.51 170.201 223.253 170.069 222.93 170.069 c
+222.507 170.069 222.18 170.231 221.945 170.554 c
+221.717 170.885 221.6 171.356 221.592 171.965 c
+h
+222.636 171.994 m
+222.636 171.62 222.684 171.352 222.783 171.186 c
+222.879 171.017 223.033 170.936 223.238 170.936 c
+223.452 170.936 223.61 171.013 223.709 171.171 c
+223.709 173.068 l
+223.599 173.233 223.444 173.317 223.238 173.317 c
+223.033 173.317 222.879 173.233 222.783 173.068 c
+222.684 172.898 222.636 172.63 222.636 172.259 c
+h
+227.281 171.216 m
+227.281 171.304 227.236 171.381 227.149 171.451 c
+227.061 171.528 226.873 171.631 226.59 171.759 c
+226.157 171.936 225.859 172.116 225.694 172.303 c
+225.536 172.487 225.458 172.719 225.458 172.994 c
+225.458 173.336 225.58 173.619 225.825 173.847 c
+226.079 174.082 226.417 174.2 226.839 174.2 c
+227.27 174.2 227.619 174.086 227.884 173.862 c
+228.148 173.633 228.281 173.332 228.281 172.95 c
+227.236 172.95 l
+227.236 173.274 227.097 173.435 226.825 173.435 c
+226.715 173.435 226.627 173.398 226.561 173.332 c
+226.49 173.263 226.458 173.163 226.458 173.039 c
+226.458 172.95 226.494 172.869 226.575 172.803 c
+226.652 172.744 226.833 172.649 227.119 172.524 c
+227.549 172.366 227.847 172.189 228.015 171.994 c
+228.192 171.807 228.281 171.557 228.281 171.245 c
+228.281 170.892 228.148 170.606 227.884 170.392 c
+227.619 170.176 227.27 170.069 226.839 170.069 c
+226.546 170.069 226.285 170.124 226.061 170.231 c
+225.833 170.348 225.657 170.51 225.532 170.716 c
+225.414 170.922 225.355 171.142 225.355 171.377 c
+226.34 171.377 l
+226.34 171.19 226.377 171.054 226.458 170.965 c
+226.546 170.878 226.679 170.834 226.855 170.834 c
+227.138 170.834 227.281 170.959 227.281 171.216 c
+f
+q 1 0 0 1 231.6175 175.081 cm
+0 0 m
+0 -0.955 l
+0.602 -0.955 l
+0.602 -1.484 l
+0 -1.484 l
+0 -3.954 l
+0 -4.112 0.022 -4.23 0.073 -4.307 c
+0.132 -4.388 0.22 -4.424 0.338 -4.424 c
+0.426 -4.424 0.515 -4.409 0.602 -4.38 c
+0.602 -4.939 l
+0.455 -4.986 0.301 -5.012 0.147 -5.012 c
+-0.111 -5.012 -0.306 -4.92 -0.441 -4.733 c
+-0.581 -4.548 -0.647 -4.288 -0.647 -3.954 c
+-0.647 -1.484 l
+-1.25 -1.484 l
+-1.25 -0.955 l
+-0.647 -0.955 l
+-0.647 0 l
+h
+1.161 -2.763 m
+1.161 -2.186 1.297 -1.73 1.572 -1.396 c
+1.855 -1.055 2.227 -0.881 2.69 -0.881 c
+3.149 -0.881 3.516 -1.051 3.792 -1.381 c
+4.075 -1.705 4.222 -2.153 4.233 -2.719 c
+4.233 -3.145 l
+4.233 -3.715 4.089 -4.17 3.807 -4.512 c
+3.532 -4.847 3.164 -5.012 2.705 -5.012 c
+2.241 -5.012 1.87 -4.85 1.587 -4.527 c
+1.311 -4.196 1.168 -3.755 1.161 -3.204 c
+h
+1.807 -3.145 m
+1.807 -3.549 1.885 -3.866 2.042 -4.101 c
+2.208 -4.336 2.429 -4.453 2.705 -4.453 c
+3.27 -4.453 3.564 -4.041 3.586 -3.218 c
+3.586 -2.763 l
+3.586 -2.362 3.501 -2.042 3.337 -1.807 c
+3.179 -1.565 2.961 -1.44 2.69 -1.44 c
+2.425 -1.44 2.208 -1.565 2.042 -1.807 c
+1.885 -2.042 1.807 -2.362 1.807 -2.763 c
+h
+9.672 -3.145 m
+9.672 -3.773 9.554 -4.244 9.319 -4.556 c
+9.091 -4.861 8.775 -5.012 8.363 -5.012 c
+7.96 -5.012 7.65 -4.861 7.437 -4.556 c
+7.437 -6.468 l
+6.79 -6.468 l
+6.79 -0.955 l
+7.378 -0.955 l
+7.422 -1.396 l
+7.636 -1.055 7.945 -0.881 8.349 -0.881 c
+8.789 -0.881 9.117 -1.036 9.333 -1.337 c
+9.547 -1.643 9.66 -2.098 9.672 -2.705 c
+h
+9.025 -2.763 m
+9.025 -2.322 8.955 -1.999 8.819 -1.793 c
+8.679 -1.58 8.459 -1.469 8.157 -1.469 c
+7.842 -1.469 7.603 -1.624 7.437 -1.925 c
+7.437 -3.998 l
+7.603 -4.303 7.842 -4.453 8.157 -4.453 c
+8.452 -4.453 8.665 -4.351 8.804 -4.145 c
+8.941 -3.931 9.014 -3.601 9.025 -3.16 c
+h
+12.538 -4.586 m
+12.321 -4.872 12.009 -5.012 11.597 -5.012 c
+11.233 -5.012 10.958 -4.891 10.774 -4.644 c
+10.597 -4.39 10.502 -4.027 10.495 -3.557 c
+10.495 -0.955 l
+11.141 -0.955 l
+11.141 -3.498 l
+11.141 -4.126 11.326 -4.438 11.7 -4.438 c
+12.101 -4.438 12.376 -4.262 12.523 -3.91 c
+12.523 -0.955 l
+13.17 -0.955 l
+13.17 -4.939 l
+12.552 -4.939 l
+h
+16.095 -3.924 m
+16.095 -3.777 16.04 -3.656 15.933 -3.557 c
+15.823 -3.461 15.617 -3.343 15.316 -3.204 c
+14.971 -3.057 14.728 -2.936 14.581 -2.836 c
+14.434 -2.73 14.324 -2.612 14.258 -2.484 c
+14.188 -2.358 14.155 -2.2 14.155 -2.013 c
+14.155 -1.69 14.272 -1.421 14.507 -1.205 c
+14.742 -0.992 15.044 -0.881 15.419 -0.881 c
+15.802 -0.881 16.11 -0.995 16.345 -1.219 c
+16.581 -1.448 16.698 -1.734 16.698 -2.087 c
+16.051 -2.087 l
+16.051 -1.911 15.993 -1.76 15.875 -1.631 c
+15.757 -1.506 15.603 -1.44 15.419 -1.44 c
+15.22 -1.44 15.07 -1.495 14.963 -1.602 c
+14.853 -1.701 14.802 -1.834 14.802 -1.999 c
+14.802 -2.127 14.838 -2.234 14.919 -2.322 c
+14.996 -2.403 15.187 -2.506 15.492 -2.63 c
+15.97 -2.818 16.301 -3.006 16.477 -3.189 c
+16.654 -3.366 16.742 -3.594 16.742 -3.866 c
+16.742 -4.218 16.617 -4.498 16.374 -4.704 c
+16.139 -4.909 15.823 -5.012 15.434 -5.012 c
+15.011 -5.012 14.673 -4.895 14.42 -4.659 c
+14.162 -4.417 14.037 -4.112 14.037 -3.748 c
+14.684 -3.748 l
+14.692 -3.976 14.761 -4.152 14.89 -4.277 c
+15.015 -4.394 15.199 -4.453 15.434 -4.453 c
+15.646 -4.453 15.808 -4.406 15.918 -4.307 c
+16.037 -4.211 16.095 -4.082 16.095 -3.924 c
+18.227 -1.367 m
+18.48 -1.043 18.8 -0.881 19.182 -0.881 c
+19.887 -0.881 20.244 -1.352 20.255 -2.293 c
+20.255 -4.939 l
+19.608 -4.939 l
+19.608 -2.322 l
+19.608 -2.009 19.553 -1.789 19.446 -1.66 c
+19.336 -1.535 19.182 -1.469 18.976 -1.469 c
+18.819 -1.469 18.671 -1.525 18.535 -1.631 c
+18.407 -1.741 18.304 -1.878 18.227 -2.042 c
+18.227 -4.939 l
+17.58 -4.939 l
+17.58 0.706 l
+18.227 0.706 l
+h
+23.783 0 m
+23.783 -0.955 l
+24.385 -0.955 l
+24.385 -1.484 l
+23.783 -1.484 l
+23.783 -3.954 l
+23.783 -4.112 23.804 -4.23 23.856 -4.307 c
+23.915 -4.388 24.003 -4.424 24.121 -4.424 c
+24.209 -4.424 24.297 -4.409 24.385 -4.38 c
+24.385 -4.939 l
+24.238 -4.986 24.084 -5.012 23.93 -5.012 c
+23.673 -5.012 23.478 -4.92 23.342 -4.733 c
+23.202 -4.548 23.136 -4.288 23.136 -3.954 c
+23.136 -1.484 l
+22.534 -1.484 l
+22.534 -0.955 l
+23.136 -0.955 l
+23.136 0 l
+h
+27.193 -4.939 m
+27.153 -4.85 27.127 -4.704 27.12 -4.498 c
+26.884 -4.843 26.59 -5.012 26.237 -5.012 c
+25.874 -5.012 25.591 -4.916 25.385 -4.718 c
+25.186 -4.512 25.09 -4.226 25.09 -3.85 c
+25.09 -3.45 25.227 -3.131 25.502 -2.896 c
+25.774 -2.653 26.15 -2.528 26.62 -2.528 c
+27.105 -2.528 l
+27.105 -2.102 l
+27.105 -1.866 27.05 -1.701 26.943 -1.602 c
+26.833 -1.495 26.671 -1.44 26.458 -1.44 c
+26.26 -1.44 26.098 -1.499 25.973 -1.616 c
+25.855 -1.734 25.796 -1.881 25.796 -2.057 c
+25.15 -2.057 l
+25.15 -1.863 25.209 -1.672 25.326 -1.484 c
+25.451 -1.3 25.613 -1.153 25.811 -1.043 c
+26.017 -0.937 26.245 -0.881 26.502 -0.881 c
+26.902 -0.881 27.207 -0.984 27.413 -1.19 c
+27.627 -1.396 27.741 -1.69 27.752 -2.072 c
+27.752 -4.086 l
+27.752 -4.39 27.788 -4.656 27.869 -4.88 c
+27.869 -4.939 l
+h
+26.326 -4.424 m
+26.491 -4.424 26.642 -4.38 26.781 -4.292 c
+26.929 -4.203 27.035 -4.093 27.105 -3.954 c
+27.105 -3.013 l
+26.737 -3.013 l
+26.422 -3.013 26.179 -3.083 26.002 -3.218 c
+25.826 -3.347 25.738 -3.534 25.738 -3.777 c
+25.738 -4.005 25.782 -4.17 25.87 -4.277 c
+25.958 -4.376 26.109 -4.424 26.326 -4.424 c
+28.618 -2.763 m
+28.618 -2.146 28.729 -1.683 28.957 -1.367 c
+29.181 -1.043 29.516 -0.881 29.956 -0.881 c
+30.357 -0.881 30.662 -1.058 30.868 -1.411 c
+30.912 -0.955 l
+31.5 -0.955 l
+31.5 -4.982 l
+31.5 -5.471 31.371 -5.85 31.118 -6.115 c
+30.86 -6.379 30.507 -6.511 30.059 -6.511 c
+29.861 -6.511 29.64 -6.46 29.398 -6.364 c
+29.152 -6.265 28.972 -6.144 28.853 -5.997 c
+29.119 -5.556 l
+29.383 -5.82 29.68 -5.953 30.015 -5.953 c
+30.551 -5.953 30.827 -5.659 30.838 -5.071 c
+30.838 -4.542 l
+30.632 -4.858 30.331 -5.012 29.942 -5.012 c
+29.53 -5.012 29.207 -4.861 28.972 -4.556 c
+28.743 -4.244 28.626 -3.792 28.618 -3.204 c
+h
+29.281 -3.145 m
+29.281 -3.586 29.343 -3.917 29.471 -4.13 c
+29.597 -4.336 29.813 -4.438 30.118 -4.438 c
+30.441 -4.438 30.68 -4.273 30.838 -3.939 c
+30.838 -1.955 l
+30.669 -1.631 30.43 -1.469 30.118 -1.469 c
+29.824 -1.469 29.607 -1.572 29.471 -1.778 c
+29.343 -1.984 29.281 -2.308 29.281 -2.748 c
+h
+34.395 -3.924 m
+34.395 -3.777 34.341 -3.656 34.234 -3.557 c
+34.123 -3.461 33.917 -3.343 33.616 -3.204 c
+33.271 -3.057 33.028 -2.936 32.882 -2.836 c
+32.735 -2.73 32.624 -2.612 32.558 -2.484 c
+32.488 -2.358 32.455 -2.2 32.455 -2.013 c
+32.455 -1.69 32.573 -1.421 32.808 -1.205 c
+33.043 -0.992 33.344 -0.881 33.719 -0.881 c
+34.102 -0.881 34.41 -0.995 34.646 -1.219 c
+34.881 -1.448 34.998 -1.734 34.998 -2.087 c
+34.351 -2.087 l
+34.351 -1.911 34.293 -1.76 34.175 -1.631 c
+34.058 -1.506 33.903 -1.44 33.719 -1.44 c
+33.52 -1.44 33.37 -1.495 33.264 -1.602 c
+33.154 -1.701 33.102 -1.834 33.102 -1.999 c
+33.102 -2.127 33.138 -2.234 33.219 -2.322 c
+33.297 -2.403 33.488 -2.506 33.792 -2.63 c
+34.27 -2.818 34.601 -3.006 34.777 -3.189 c
+34.954 -3.366 35.043 -3.594 35.043 -3.866 c
+35.043 -4.218 34.917 -4.498 34.675 -4.704 c
+34.44 -4.909 34.123 -5.012 33.734 -5.012 c
+33.312 -5.012 32.973 -4.895 32.72 -4.659 c
+32.462 -4.417 32.338 -4.112 32.338 -3.748 c
+32.984 -3.748 l
+32.992 -3.976 33.061 -4.152 33.19 -4.277 c
+33.315 -4.394 33.499 -4.453 33.734 -4.453 c
+33.947 -4.453 34.108 -4.406 34.219 -4.307 c
+34.337 -4.211 34.395 -4.082 34.395 -3.924 c
+35.924 -4.586 m
+35.924 -4.469 35.957 -4.373 36.027 -4.292 c
+36.093 -4.215 36.196 -4.174 36.336 -4.174 c
+36.483 -4.174 36.589 -4.215 36.659 -4.292 c
+36.736 -4.373 36.776 -4.469 36.776 -4.586 c
+36.776 -4.696 36.736 -4.787 36.659 -4.865 c
+36.589 -4.942 36.483 -4.982 36.336 -4.982 c
+36.196 -4.982 36.093 -4.942 36.027 -4.865 c
+35.957 -4.787 35.924 -4.696 35.924 -4.586 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 164.535 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 157.6961 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.278 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.485 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.485 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.485 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.631 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.179 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.675 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.566 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.69 15.056 -0.69 c
+15.192 -0.69 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.603 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.309 14.835 -1.309 c
+14.648 -1.309 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.956 c
+13.733 -0.879 13.689 -0.801 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+21.986 2.219 m
+21.994 2.198 22.001 2.165 22.001 2.117 c
+22.009 2.076 22.016 2.028 22.016 1.97 c
+22.023 1.918 22.03 1.866 22.03 1.808 c
+22.03 1.646 l
+22.045 1.646 l
+22.104 1.764 22.17 1.86 22.251 1.941 c
+22.328 2.018 22.413 2.08 22.501 2.132 c
+22.589 2.19 22.677 2.227 22.765 2.249 c
+22.861 2.267 22.961 2.278 23.059 2.278 c
+23.265 2.278 23.445 2.234 23.603 2.146 c
+23.757 2.058 23.886 1.929 23.985 1.764 c
+24.092 1.606 24.169 1.415 24.22 1.191 c
+24.28 0.974 24.309 0.738 24.309 0.485 c
+24.309 0.22 24.28 -0.025 24.22 -0.25 c
+24.169 -0.467 24.092 -0.658 23.985 -0.823 c
+23.886 -0.981 23.754 -1.103 23.589 -1.191 c
+23.431 -1.278 23.243 -1.323 23.03 -1.323 c
+22.931 -1.323 22.832 -1.312 22.736 -1.294 c
+22.637 -1.272 22.545 -1.242 22.456 -1.191 c
+22.375 -1.143 22.298 -1.08 22.221 -0.999 c
+22.152 -0.922 22.093 -0.831 22.045 -0.721 c
+22.03 -0.721 l
+22.03 -0.808 l
+22.038 -0.849 22.045 -0.897 22.045 -0.956 c
+22.045 -1.117 l
+22.045 -1.294 l
+22.045 -2.631 l
+21.134 -2.631 l
+21.134 1.455 l
+21.134 1.621 21.126 1.768 21.119 1.897 c
+21.119 2.219 l
+h
+22.03 0.455 m
+22.03 0.228 22.049 0.037 22.089 -0.118 c
+22.137 -0.264 22.192 -0.382 22.251 -0.47 c
+22.317 -0.559 22.391 -0.625 22.471 -0.661 c
+22.549 -0.702 22.626 -0.721 22.707 -0.721 c
+22.803 -0.721 22.89 -0.698 22.971 -0.646 c
+23.059 -0.599 23.125 -0.529 23.177 -0.441 c
+23.235 -0.345 23.28 -0.22 23.31 -0.073 c
+23.346 0.081 23.368 0.268 23.368 0.485 c
+23.368 0.875 23.31 1.168 23.192 1.367 c
+23.081 1.562 22.927 1.661 22.722 1.661 c
+22.641 1.661 22.564 1.639 22.486 1.602 c
+22.406 1.562 22.332 1.5 22.265 1.411 c
+22.196 1.323 22.137 1.198 22.089 1.043 c
+22.049 0.885 22.03 0.691 22.03 0.455 c
+26.061 2.219 m
+26.061 0.264 l
+26.061 0.125 26.069 0 26.091 -0.118 c
+26.109 -0.228 26.142 -0.32 26.194 -0.397 c
+26.242 -0.478 26.3 -0.54 26.371 -0.588 c
+26.437 -0.628 26.521 -0.646 26.62 -0.646 c
+26.709 -0.646 26.789 -0.628 26.87 -0.588 c
+26.959 -0.54 27.032 -0.47 27.09 -0.382 c
+27.15 -0.287 27.194 -0.176 27.223 -0.058 c
+27.26 0.066 27.281 0.206 27.281 0.353 c
+27.281 2.219 l
+28.178 2.219 l
+28.178 -0.485 l
+28.178 -0.721 l
+28.185 -0.801 28.193 -0.879 28.193 -0.956 c
+28.193 -1.147 l
+28.201 -1.198 28.208 -1.234 28.208 -1.264 c
+27.355 -1.264 l
+27.344 -1.234 27.333 -1.198 27.326 -1.147 c
+27.326 -0.956 l
+27.326 -0.889 27.318 -0.819 27.312 -0.75 c
+27.312 -0.573 l
+27.296 -0.573 l
+27.179 -0.838 27.025 -1.029 26.841 -1.147 c
+26.664 -1.264 26.462 -1.323 26.238 -1.323 c
+26.032 -1.323 25.86 -1.286 25.724 -1.22 c
+25.584 -1.154 25.473 -1.058 25.386 -0.941 c
+25.305 -0.823 25.246 -0.687 25.209 -0.529 c
+25.18 -0.364 25.165 -0.187 25.165 0 c
+25.165 2.219 l
+h
+32.342 -0.25 m
+32.342 -0.419 32.301 -0.569 32.224 -0.706 c
+32.154 -0.834 32.052 -0.948 31.916 -1.043 c
+31.787 -1.132 31.625 -1.201 31.43 -1.249 c
+31.243 -1.297 31.027 -1.323 30.784 -1.323 c
+30.556 -1.323 30.358 -1.309 30.181 -1.278 c
+30.005 -1.249 29.847 -1.201 29.71 -1.132 c
+29.571 -1.055 29.461 -0.956 29.373 -0.838 c
+29.284 -0.721 29.215 -0.573 29.167 -0.397 c
+29.976 -0.279 l
+29.994 -0.378 30.023 -0.455 30.063 -0.515 c
+30.111 -0.573 30.17 -0.617 30.24 -0.646 c
+30.306 -0.676 30.387 -0.702 30.475 -0.721 c
+30.564 -0.731 30.666 -0.735 30.784 -0.735 c
+30.88 -0.735 30.975 -0.731 31.063 -0.721 c
+31.152 -0.702 31.229 -0.676 31.298 -0.646 c
+31.364 -0.617 31.416 -0.58 31.445 -0.529 c
+31.482 -0.482 31.504 -0.419 31.504 -0.338 c
+31.504 -0.243 31.474 -0.169 31.416 -0.118 c
+31.364 -0.07 31.298 -0.029 31.21 0 c
+31.122 0.037 31.011 0.066 30.886 0.088 c
+30.769 0.118 30.637 0.147 30.489 0.177 c
+30.35 0.214 30.211 0.254 30.063 0.294 c
+29.924 0.342 29.799 0.405 29.681 0.485 c
+29.571 0.563 29.483 0.661 29.417 0.779 c
+29.347 0.897 29.313 1.047 29.313 1.235 c
+29.313 1.389 29.344 1.532 29.402 1.661 c
+29.469 1.797 29.564 1.911 29.681 1.999 c
+29.806 2.088 29.964 2.153 30.152 2.205 c
+30.335 2.253 30.549 2.278 30.784 2.278 c
+30.967 2.278 31.144 2.256 31.313 2.219 c
+31.478 2.19 31.625 2.135 31.754 2.058 c
+31.879 1.988 31.989 1.889 32.077 1.764 c
+32.166 1.646 32.224 1.503 32.253 1.338 c
+31.46 1.264 l
+31.438 1.342 31.408 1.404 31.372 1.455 c
+31.331 1.514 31.283 1.558 31.225 1.588 c
+31.173 1.625 31.111 1.65 31.034 1.661 c
+30.953 1.668 30.872 1.675 30.784 1.675 c
+30.567 1.675 30.406 1.646 30.298 1.588 c
+30.188 1.536 30.137 1.448 30.137 1.323 c
+30.137 1.242 30.155 1.18 30.196 1.132 c
+30.244 1.08 30.306 1.043 30.387 1.014 c
+30.475 0.985 30.57 0.956 30.681 0.926 c
+30.788 0.904 30.909 0.882 31.048 0.852 c
+31.202 0.823 31.36 0.783 31.519 0.735 c
+31.673 0.684 31.813 0.621 31.931 0.544 c
+32.048 0.463 32.143 0.36 32.224 0.235 c
+32.301 0.106 32.342 -0.055 32.342 -0.25 c
+34.202 1.514 m
+34.319 1.786 34.47 1.984 34.657 2.102 c
+34.84 2.219 35.061 2.278 35.318 2.278 c
+35.524 2.278 35.694 2.242 35.833 2.176 c
+35.98 2.105 36.091 2.014 36.17 1.897 c
+36.259 1.779 36.318 1.635 36.347 1.47 c
+36.384 1.301 36.406 1.124 36.406 0.941 c
+36.406 -1.264 l
+35.495 -1.264 l
+35.495 0.735 l
+35.495 0.871 35.484 0.992 35.465 1.103 c
+35.455 1.209 35.428 1.297 35.392 1.367 c
+35.351 1.444 35.293 1.503 35.216 1.544 c
+35.146 1.58 35.054 1.602 34.936 1.602 c
+34.826 1.602 34.73 1.577 34.643 1.529 c
+34.554 1.477 34.473 1.411 34.408 1.323 c
+34.348 1.235 34.297 1.124 34.26 1 c
+34.231 0.882 34.216 0.75 34.216 0.603 c
+34.216 -1.264 l
+33.305 -1.264 l
+33.305 3.513 l
+34.216 3.513 l
+34.216 2.205 l
+34.216 2.135 34.209 2.065 34.202 1.999 c
+34.202 1.793 l
+34.202 1.735 34.194 1.679 34.186 1.631 c
+34.186 1.514 l
+h
+42.013 0.838 1.867 -0.794 re
+42.013 0.044 m
+46.368 2.219 m
+46.368 0.264 l
+46.368 0.125 46.376 0 46.398 -0.118 c
+46.416 -0.228 46.449 -0.32 46.501 -0.397 c
+46.549 -0.478 46.607 -0.54 46.677 -0.588 c
+46.743 -0.628 46.828 -0.646 46.927 -0.646 c
+47.015 -0.646 47.096 -0.628 47.177 -0.588 c
+47.265 -0.54 47.339 -0.47 47.397 -0.382 c
+47.457 -0.287 47.5 -0.176 47.53 -0.058 c
+47.567 0.066 47.588 0.206 47.588 0.353 c
+47.588 2.219 l
+48.485 2.219 l
+48.485 -0.485 l
+48.485 -0.721 l
+48.492 -0.801 48.5 -0.879 48.5 -0.956 c
+48.5 -1.147 l
+48.507 -1.198 48.515 -1.234 48.515 -1.264 c
+47.662 -1.264 l
+47.651 -1.234 47.64 -1.198 47.632 -1.147 c
+47.632 -0.956 l
+47.632 -0.889 47.625 -0.819 47.617 -0.75 c
+47.617 -0.573 l
+47.603 -0.573 l
+47.486 -0.838 47.331 -1.029 47.147 -1.147 c
+46.971 -1.264 46.769 -1.323 46.545 -1.323 c
+46.339 -1.323 46.167 -1.286 46.03 -1.22 c
+45.891 -1.154 45.78 -1.058 45.693 -0.941 c
+45.612 -0.823 45.552 -0.687 45.516 -0.529 c
+45.487 -0.364 45.472 -0.187 45.472 0 c
+45.472 2.219 l
+h
+54.361 -2.631 m
+54.361 3.513 l
+56.286 3.513 l
+56.286 2.896 l
+55.214 2.896 l
+55.214 -2.014 l
+56.286 -2.014 l
+56.286 -2.631 l
+h
+60.685 1.47 m
+60.586 1.477 60.483 1.488 60.377 1.5 c
+60.267 1.517 60.145 1.529 60.009 1.529 c
+59.833 1.529 59.675 1.488 59.538 1.411 c
+59.399 1.342 59.282 1.242 59.186 1.118 c
+59.098 0.989 59.028 0.842 58.981 0.676 c
+58.94 0.507 58.921 0.331 58.921 0.147 c
+58.921 -1.264 l
+58.025 -1.264 l
+58.025 0.985 l
+58.025 1.11 58.014 1.235 57.996 1.353 c
+57.984 1.477 57.969 1.595 57.951 1.706 c
+57.94 1.823 57.926 1.918 57.907 1.999 c
+57.886 2.088 57.867 2.161 57.848 2.219 c
+58.73 2.219 l
+58.738 2.168 58.748 2.117 58.76 2.058 c
+58.778 1.999 58.792 1.933 58.804 1.866 c
+58.823 1.808 58.837 1.742 58.848 1.675 c
+58.855 1.606 58.867 1.544 58.877 1.484 c
+58.892 1.484 l
+58.929 1.602 58.981 1.708 59.039 1.808 c
+59.105 1.903 59.186 1.988 59.274 2.058 c
+59.363 2.124 59.465 2.179 59.583 2.219 c
+59.708 2.256 59.855 2.278 60.024 2.278 c
+60.149 2.278 60.267 2.271 60.377 2.263 c
+60.494 2.253 60.597 2.238 60.685 2.219 c
+h
+63.291 -1.323 m
+63.033 -1.323 62.806 -1.286 62.6 -1.22 c
+62.395 -1.143 62.218 -1.029 62.071 -0.881 c
+61.924 -0.727 61.806 -0.536 61.718 -0.309 c
+61.637 -0.085 61.601 0.18 61.601 0.485 c
+61.601 0.816 61.645 1.095 61.732 1.323 c
+61.828 1.558 61.957 1.742 62.115 1.881 c
+62.28 2.018 62.468 2.117 62.673 2.176 c
+62.879 2.242 63.089 2.278 63.305 2.278 c
+63.577 2.278 63.812 2.227 64.011 2.132 c
+64.217 2.043 64.382 1.911 64.511 1.735 c
+64.647 1.565 64.746 1.359 64.805 1.118 c
+64.871 0.882 64.907 0.617 64.907 0.324 c
+64.907 0.309 l
+62.541 0.309 l
+62.541 0.162 62.555 0.022 62.586 -0.103 c
+62.622 -0.231 62.677 -0.345 62.746 -0.441 c
+62.813 -0.529 62.898 -0.599 62.997 -0.646 c
+63.093 -0.698 63.206 -0.721 63.335 -0.721 c
+63.49 -0.721 63.629 -0.687 63.746 -0.617 c
+63.872 -0.551 63.96 -0.449 64.011 -0.309 c
+64.849 -0.382 l
+64.82 -0.482 64.764 -0.588 64.687 -0.706 c
+64.606 -0.816 64.504 -0.919 64.379 -1.014 c
+64.261 -1.103 64.107 -1.176 63.922 -1.234 c
+63.746 -1.294 63.533 -1.323 63.291 -1.323 c
+63.291 1.706 m
+63.203 1.706 63.114 1.691 63.026 1.661 c
+62.938 1.631 62.857 1.58 62.791 1.514 c
+62.721 1.444 62.663 1.356 62.615 1.249 c
+62.574 1.139 62.555 1.014 62.555 0.867 c
+64.026 0.867 l
+64.026 1.003 64 1.124 63.952 1.235 c
+63.912 1.342 63.856 1.43 63.791 1.5 c
+63.731 1.565 63.658 1.617 63.57 1.646 c
+63.482 1.683 63.386 1.706 63.291 1.706 c
+66.954 -1.264 m
+66.954 0.852 l
+66.954 1.018 66.947 1.154 66.94 1.264 c
+66.929 1.371 66.91 1.455 66.881 1.514 c
+66.859 1.58 66.829 1.631 66.792 1.661 c
+66.763 1.691 66.723 1.706 66.675 1.706 c
+66.617 1.706 66.561 1.675 66.513 1.617 c
+66.473 1.565 66.44 1.492 66.411 1.396 c
+66.381 1.309 66.355 1.195 66.337 1.058 c
+66.326 0.919 66.322 0.768 66.322 0.603 c
+66.322 -1.264 l
+65.572 -1.264 l
+65.572 1.47 l
+65.572 1.706 l
+65.572 1.926 l
+65.572 2.003 65.566 2.065 65.558 2.117 c
+65.558 2.219 l
+66.235 2.219 l
+66.235 2.132 l
+66.235 1.984 l
+66.241 1.926 66.249 1.866 66.249 1.808 c
+66.249 1.646 l
+66.264 1.646 l
+66.282 1.735 66.312 1.812 66.352 1.881 c
+66.389 1.959 66.432 2.028 66.484 2.088 c
+66.543 2.146 66.609 2.19 66.69 2.219 c
+66.767 2.256 66.856 2.278 66.954 2.278 c
+67.138 2.278 67.278 2.223 67.366 2.117 c
+67.461 2.018 67.531 1.86 67.571 1.646 c
+67.586 1.646 l
+67.623 1.742 67.664 1.83 67.704 1.911 c
+67.752 1.988 67.807 2.051 67.866 2.102 c
+67.924 2.161 67.991 2.205 68.072 2.234 c
+68.149 2.263 68.237 2.278 68.336 2.278 c
+68.472 2.278 68.586 2.253 68.674 2.205 c
+68.762 2.153 68.828 2.08 68.88 1.984 c
+68.939 1.885 68.976 1.756 68.998 1.602 c
+69.027 1.455 69.042 1.272 69.042 1.058 c
+69.042 -1.264 l
+68.321 -1.264 l
+68.321 0.852 l
+68.321 1.018 68.314 1.154 68.307 1.264 c
+68.296 1.371 68.277 1.455 68.248 1.514 c
+68.226 1.58 68.196 1.631 68.159 1.661 c
+68.13 1.691 68.09 1.706 68.042 1.706 c
+67.924 1.706 67.829 1.617 67.763 1.44 c
+67.704 1.272 67.675 1.014 67.675 0.661 c
+67.675 -1.264 l
+h
+73.058 0.485 m
+73.058 0.21 73.021 -0.04 72.955 -0.264 c
+72.886 -0.482 72.782 -0.669 72.647 -0.823 c
+72.507 -0.981 72.331 -1.103 72.117 -1.191 c
+71.901 -1.278 71.647 -1.323 71.353 -1.323 c
+71.078 -1.323 70.831 -1.278 70.618 -1.191 c
+70.412 -1.103 70.239 -0.981 70.104 -0.823 c
+69.964 -0.669 69.861 -0.482 69.795 -0.264 c
+69.725 -0.04 69.692 0.21 69.692 0.485 c
+69.692 0.738 69.722 0.974 69.78 1.191 c
+69.846 1.415 69.95 1.606 70.089 1.764 c
+70.225 1.929 70.401 2.058 70.618 2.146 c
+70.831 2.234 71.089 2.278 71.382 2.278 c
+71.695 2.278 71.956 2.234 72.161 2.146 c
+72.375 2.058 72.547 1.929 72.676 1.764 c
+72.812 1.606 72.911 1.415 72.97 1.191 c
+73.029 0.974 73.058 0.738 73.058 0.485 c
+72.103 0.485 m
+72.103 0.691 72.088 0.867 72.059 1.014 c
+72.037 1.161 71.999 1.282 71.941 1.382 c
+71.882 1.477 71.809 1.548 71.721 1.588 c
+71.633 1.635 71.523 1.661 71.397 1.661 c
+71.133 1.661 70.941 1.562 70.824 1.367 c
+70.706 1.18 70.648 0.885 70.648 0.485 c
+70.648 0.062 70.706 -0.243 70.824 -0.426 c
+70.941 -0.613 71.118 -0.706 71.353 -0.706 c
+71.478 -0.706 71.592 -0.687 71.691 -0.646 c
+71.787 -0.599 71.868 -0.526 71.926 -0.426 c
+71.993 -0.331 72.037 -0.206 72.059 -0.058 c
+72.088 0.088 72.103 0.268 72.103 0.485 c
+74.521 1.602 m
+73.977 1.602 l
+73.977 2.219 l
+74.565 2.219 l
+74.844 3.117 l
+75.417 3.117 l
+75.417 2.219 l
+76.652 2.219 l
+76.652 1.602 l
+75.417 1.602 l
+75.417 -0.103 l
+75.417 -0.324 l
+75.425 -0.393 75.446 -0.455 75.476 -0.515 c
+75.513 -0.566 75.568 -0.61 75.638 -0.646 c
+75.715 -0.676 75.829 -0.69 75.976 -0.69 c
+76.112 -0.69 76.248 -0.687 76.387 -0.676 c
+76.523 -0.658 76.656 -0.632 76.784 -0.603 c
+76.784 -1.205 l
+76.703 -1.216 76.626 -1.231 76.549 -1.249 c
+76.468 -1.261 76.391 -1.268 76.314 -1.278 c
+76.233 -1.286 76.145 -1.294 76.049 -1.294 c
+75.961 -1.301 75.862 -1.309 75.756 -1.309 c
+75.568 -1.309 75.407 -1.294 75.27 -1.264 c
+75.142 -1.228 75.028 -1.183 74.933 -1.132 c
+74.844 -1.084 74.771 -1.025 74.712 -0.956 c
+74.653 -0.879 74.609 -0.801 74.58 -0.721 c
+74.55 -0.632 74.528 -0.544 74.521 -0.455 c
+74.509 -0.36 74.506 -0.264 74.506 -0.176 c
+h
+79.537 -1.323 m
+79.28 -1.323 79.052 -1.286 78.846 -1.22 c
+78.64 -1.143 78.464 -1.029 78.317 -0.881 c
+78.17 -0.727 78.052 -0.536 77.964 -0.309 c
+77.883 -0.085 77.846 0.18 77.846 0.485 c
+77.846 0.816 77.89 1.095 77.979 1.323 c
+78.074 1.558 78.203 1.742 78.361 1.881 c
+78.526 2.018 78.714 2.117 78.92 2.176 c
+79.126 2.242 79.334 2.278 79.552 2.278 c
+79.824 2.278 80.059 2.227 80.257 2.132 c
+80.463 2.043 80.628 1.911 80.757 1.735 c
+80.893 1.565 80.992 1.359 81.051 1.118 c
+81.117 0.882 81.154 0.617 81.154 0.324 c
+81.154 0.309 l
+78.787 0.309 l
+78.787 0.162 78.802 0.022 78.831 -0.103 c
+78.868 -0.231 78.923 -0.345 78.993 -0.441 c
+79.059 -0.529 79.143 -0.599 79.243 -0.646 c
+79.338 -0.698 79.452 -0.721 79.581 -0.721 c
+79.735 -0.721 79.875 -0.687 79.993 -0.617 c
+80.117 -0.551 80.206 -0.449 80.257 -0.309 c
+81.095 -0.382 l
+81.065 -0.482 81.011 -0.588 80.934 -0.706 c
+80.853 -0.816 80.749 -0.919 80.624 -1.014 c
+80.507 -1.103 80.352 -1.176 80.169 -1.234 c
+79.993 -1.294 79.779 -1.323 79.537 -1.323 c
+79.537 1.706 m
+79.448 1.706 79.361 1.691 79.272 1.661 c
+79.184 1.631 79.103 1.58 79.037 1.514 c
+78.968 1.444 78.908 1.356 78.86 1.249 c
+78.82 1.139 78.802 1.014 78.802 0.867 c
+80.271 0.867 l
+80.271 1.003 80.246 1.124 80.198 1.235 c
+80.158 1.342 80.103 1.43 80.036 1.5 c
+79.978 1.565 79.905 1.617 79.816 1.646 c
+79.728 1.683 79.633 1.706 79.537 1.706 c
+82.407 -2.631 m
+82.407 -2.014 l
+83.48 -2.014 l
+83.48 2.896 l
+82.407 2.896 l
+82.407 3.513 l
+84.333 3.513 l
+84.333 -2.631 l
+h
+90.91 -2.631 m
+90.91 3.513 l
+92.836 3.513 l
+92.836 2.896 l
+91.763 2.896 l
+91.763 -2.014 l
+92.836 -2.014 l
+92.836 -2.631 l
+h
+94.224 -1.264 m
+94.224 -0.97 l
+94.232 -0.834 94.24 -0.676 94.24 -0.5 c
+94.24 3.513 l
+95.151 3.513 l
+95.151 2.234 l
+95.151 2.072 l
+95.151 1.897 l
+95.151 1.845 95.144 1.801 95.136 1.764 c
+95.136 1.675 l
+95.151 1.675 l
+95.199 1.783 95.261 1.874 95.342 1.955 c
+95.419 2.032 95.504 2.095 95.592 2.146 c
+95.68 2.194 95.772 2.227 95.871 2.249 c
+95.967 2.267 96.065 2.278 96.165 2.278 c
+96.378 2.278 96.566 2.234 96.724 2.146 c
+96.878 2.058 97.006 1.929 97.106 1.764 c
+97.212 1.606 97.289 1.415 97.341 1.191 c
+97.389 0.974 97.415 0.735 97.415 0.47 c
+97.415 0.214 97.385 -0.025 97.326 -0.25 c
+97.268 -0.467 97.183 -0.658 97.077 -0.823 c
+96.967 -0.981 96.834 -1.103 96.68 -1.191 c
+96.522 -1.278 96.341 -1.323 96.136 -1.323 c
+96.036 -1.323 95.938 -1.312 95.842 -1.294 c
+95.753 -1.272 95.666 -1.242 95.577 -1.191 c
+95.489 -1.132 95.408 -1.066 95.342 -0.985 c
+95.272 -0.908 95.209 -0.808 95.151 -0.69 c
+95.136 -0.69 l
+95.136 -0.852 l
+95.136 -0.912 95.128 -0.97 95.122 -1.029 c
+95.122 -1.08 95.114 -1.128 95.107 -1.176 c
+95.107 -1.216 95.099 -1.246 95.092 -1.264 c
+h
+95.136 0.5 m
+95.136 0.264 95.155 0.066 95.195 -0.088 c
+95.242 -0.246 95.302 -0.368 95.371 -0.455 c
+95.437 -0.544 95.511 -0.61 95.592 -0.646 c
+95.669 -0.687 95.747 -0.706 95.827 -0.706 c
+96.033 -0.706 96.187 -0.61 96.298 -0.411 c
+96.415 -0.216 96.474 0.077 96.474 0.47 c
+96.474 0.684 96.452 0.867 96.415 1.014 c
+96.385 1.168 96.341 1.294 96.283 1.382 c
+96.231 1.477 96.165 1.55 96.077 1.602 c
+95.996 1.65 95.907 1.675 95.812 1.675 c
+95.732 1.675 95.654 1.654 95.577 1.617 c
+95.496 1.577 95.423 1.514 95.357 1.426 c
+95.298 1.338 95.242 1.213 95.195 1.058 c
+95.155 0.912 95.136 0.724 95.136 0.5 c
+101.299 1.47 m
+101.2 1.477 101.097 1.488 100.99 1.5 c
+100.88 1.517 100.759 1.529 100.622 1.529 c
+100.446 1.529 100.288 1.488 100.152 1.411 c
+100.013 1.342 99.895 1.242 99.799 1.118 c
+99.711 0.989 99.641 0.842 99.593 0.676 c
+99.554 0.507 99.535 0.331 99.535 0.147 c
+99.535 -1.264 l
+98.638 -1.264 l
+98.638 0.985 l
+98.638 1.11 98.627 1.235 98.609 1.353 c
+98.598 1.477 98.583 1.595 98.565 1.706 c
+98.554 1.823 98.539 1.918 98.521 1.999 c
+98.498 2.088 98.48 2.161 98.462 2.219 c
+99.344 2.219 l
+99.351 2.168 99.362 2.117 99.373 2.058 c
+99.392 1.999 99.406 1.933 99.417 1.866 c
+99.435 1.808 99.45 1.742 99.462 1.675 c
+99.469 1.606 99.479 1.544 99.491 1.484 c
+99.506 1.484 l
+99.542 1.602 99.593 1.708 99.653 1.808 c
+99.718 1.903 99.799 1.988 99.888 2.058 c
+99.976 2.124 100.079 2.179 100.196 2.219 c
+100.321 2.256 100.468 2.278 100.637 2.278 c
+100.763 2.278 100.88 2.271 100.99 2.263 c
+101.108 2.253 101.21 2.238 101.299 2.219 c
+h
+103.302 -1.323 m
+103.132 -1.323 102.982 -1.301 102.846 -1.264 c
+102.717 -1.216 102.604 -1.147 102.508 -1.058 c
+102.419 -0.97 102.35 -0.864 102.302 -0.735 c
+102.251 -0.599 102.228 -0.449 102.228 -0.279 c
+102.228 -0.073 102.261 0.096 102.332 0.235 c
+102.398 0.382 102.493 0.492 102.611 0.573 c
+102.735 0.661 102.879 0.724 103.037 0.765 c
+103.202 0.802 103.379 0.827 103.566 0.838 c
+104.287 0.852 l
+104.287 1.029 l
+104.287 1.147 104.275 1.249 104.257 1.338 c
+104.235 1.426 104.202 1.492 104.154 1.544 c
+104.114 1.602 104.066 1.639 104.007 1.661 c
+103.948 1.679 103.882 1.691 103.816 1.691 c
+103.747 1.691 103.684 1.679 103.625 1.661 c
+103.574 1.65 103.526 1.625 103.478 1.588 c
+103.437 1.558 103.404 1.507 103.375 1.44 c
+103.353 1.382 103.338 1.301 103.331 1.205 c
+102.39 1.249 l
+102.419 1.396 102.463 1.532 102.523 1.661 c
+102.589 1.786 102.685 1.897 102.802 1.984 c
+102.92 2.08 103.059 2.153 103.228 2.205 c
+103.404 2.253 103.61 2.278 103.845 2.278 c
+104.287 2.278 104.617 2.168 104.844 1.955 c
+105.08 1.749 105.198 1.44 105.198 1.029 c
+105.198 -0.235 l
+105.198 -0.455 l
+105.205 -0.515 105.22 -0.569 105.241 -0.617 c
+105.26 -0.658 105.289 -0.69 105.33 -0.721 c
+105.367 -0.742 105.418 -0.75 105.477 -0.75 c
+105.543 -0.75 105.613 -0.746 105.683 -0.735 c
+105.683 -1.22 l
+105.624 -1.231 105.569 -1.242 105.521 -1.249 c
+105.48 -1.261 105.44 -1.268 105.403 -1.278 c
+105.363 -1.286 105.319 -1.294 105.272 -1.294 c
+105.22 -1.301 105.161 -1.309 105.095 -1.309 c
+104.867 -1.309 104.701 -1.257 104.595 -1.147 c
+104.485 -1.029 104.422 -0.864 104.404 -0.646 c
+104.389 -0.646 l
+104.32 -0.757 104.25 -0.852 104.183 -0.941 c
+104.114 -1.022 104.036 -1.087 103.948 -1.147 c
+103.86 -1.205 103.761 -1.249 103.654 -1.278 c
+103.555 -1.309 103.437 -1.323 103.302 -1.323 c
+104.287 0.353 m
+103.86 0.338 l
+103.761 0.338 103.669 0.331 103.581 0.324 c
+103.5 0.312 103.433 0.287 103.375 0.25 c
+103.317 0.21 103.265 0.151 103.228 0.073 c
+103.188 0.004 103.169 -0.088 103.169 -0.206 c
+103.169 -0.374 103.202 -0.496 103.272 -0.573 c
+103.338 -0.654 103.437 -0.69 103.566 -0.69 c
+103.672 -0.69 103.772 -0.669 103.86 -0.617 c
+103.955 -0.569 104.036 -0.507 104.096 -0.426 c
+104.161 -0.349 104.213 -0.261 104.242 -0.162 c
+104.272 -0.055 104.287 0.059 104.287 0.177 c
+h
+108.597 -1.264 m
+108.597 0.721 l
+108.597 1.022 108.553 1.242 108.464 1.382 c
+108.384 1.529 108.248 1.602 108.053 1.602 c
+107.942 1.602 107.84 1.577 107.745 1.529 c
+107.656 1.477 107.575 1.411 107.51 1.323 c
+107.45 1.235 107.399 1.124 107.362 1 c
+107.333 0.882 107.318 0.75 107.318 0.603 c
+107.318 -1.264 l
+106.407 -1.264 l
+106.407 1.44 l
+106.407 1.661 l
+106.407 1.749 106.399 1.826 106.392 1.897 c
+106.392 2.088 l
+106.392 2.219 l
+107.244 2.219 l
+107.252 2.19 107.259 2.146 107.259 2.088 c
+107.259 1.897 l
+107.267 1.826 107.274 1.756 107.274 1.691 c
+107.281 1.621 107.288 1.565 107.288 1.529 c
+107.304 1.529 l
+107.421 1.793 107.572 1.984 107.759 2.102 c
+107.942 2.219 108.163 2.278 108.42 2.278 c
+108.605 2.278 108.765 2.249 108.906 2.19 c
+109.041 2.132 109.156 2.043 109.243 1.926 c
+109.332 1.808 109.394 1.665 109.434 1.5 c
+109.482 1.342 109.509 1.154 109.509 0.941 c
+109.509 -1.264 l
+h
+112.025 -1.323 m
+111.739 -1.323 111.496 -1.282 111.29 -1.205 c
+111.084 -1.117 110.912 -0.996 110.776 -0.838 c
+110.637 -0.684 110.533 -0.496 110.467 -0.279 c
+110.398 -0.055 110.365 0.191 110.365 0.455 c
+110.365 0.75 110.398 1.007 110.467 1.235 c
+110.544 1.459 110.651 1.646 110.791 1.793 c
+110.938 1.947 111.114 2.065 111.32 2.146 c
+111.526 2.234 111.761 2.278 112.025 2.278 c
+112.25 2.278 112.451 2.249 112.628 2.19 c
+112.804 2.132 112.956 2.047 113.083 1.941 c
+113.209 1.841 113.311 1.72 113.392 1.573 c
+113.469 1.433 113.525 1.282 113.554 1.118 c
+112.643 1.073 l
+112.613 1.249 112.543 1.389 112.437 1.5 c
+112.337 1.606 112.194 1.661 112.011 1.661 c
+111.765 1.661 111.588 1.558 111.481 1.353 c
+111.371 1.154 111.32 0.867 111.32 0.485 c
+111.32 -0.309 111.555 -0.706 112.025 -0.706 c
+112.191 -0.706 112.334 -0.654 112.451 -0.544 c
+112.569 -0.437 112.643 -0.276 112.672 -0.058 c
+113.584 -0.103 l
+113.554 -0.272 113.499 -0.426 113.422 -0.573 c
+113.352 -0.721 113.249 -0.852 113.114 -0.97 c
+112.985 -1.08 112.827 -1.168 112.643 -1.234 c
+112.466 -1.294 112.26 -1.323 112.025 -1.323 c
+115.428 1.514 m
+115.546 1.786 115.697 1.984 115.884 2.102 c
+116.067 2.219 116.289 2.278 116.545 2.278 c
+116.751 2.278 116.92 2.242 117.06 2.176 c
+117.207 2.105 117.317 2.014 117.398 1.897 c
+117.486 1.779 117.544 1.635 117.575 1.47 c
+117.611 1.301 117.633 1.124 117.633 0.941 c
+117.633 -1.264 l
+116.722 -1.264 l
+116.722 0.735 l
+116.722 0.871 116.711 0.992 116.692 1.103 c
+116.682 1.209 116.655 1.297 116.619 1.367 c
+116.578 1.444 116.52 1.503 116.443 1.544 c
+116.372 1.58 116.281 1.602 116.163 1.602 c
+116.053 1.602 115.957 1.577 115.869 1.529 c
+115.781 1.477 115.701 1.411 115.634 1.323 c
+115.575 1.235 115.524 1.124 115.487 1 c
+115.458 0.882 115.443 0.75 115.443 0.603 c
+115.443 -1.264 l
+114.531 -1.264 l
+114.531 3.513 l
+115.443 3.513 l
+115.443 2.205 l
+115.443 2.135 115.435 2.065 115.428 1.999 c
+115.428 1.793 l
+115.428 1.735 115.421 1.679 115.414 1.631 c
+115.414 1.514 l
+h
+118.959 -2.631 m
+118.959 -2.014 l
+120.033 -2.014 l
+120.033 2.896 l
+118.959 2.896 l
+118.959 3.513 l
+120.885 3.513 l
+120.885 -2.631 l
+h
+f
+Q
+q 1 0 0 1 67.9872 143.6443 cm
+0 0 m
+0 -2.103 l
+-0.676 -2.103 l
+-0.676 3.248 l
+1.029 3.248 l
+1.529 3.248 1.918 3.094 2.205 2.792 c
+2.488 2.499 2.631 2.105 2.631 1.616 c
+2.631 1.095 2.492 0.698 2.219 0.426 c
+1.955 0.151 1.577 0.007 1.087 0 c
+h
+0 0.573 m
+1.029 0.573 l
+1.33 0.573 1.562 0.661 1.72 0.837 c
+1.874 1.014 1.955 1.267 1.955 1.602 c
+1.955 1.925 1.87 2.183 1.706 2.381 c
+1.548 2.576 1.326 2.675 1.043 2.675 c
+0 2.675 l
+h
+5.438 -1.75 m
+5.222 -2.037 4.91 -2.176 4.498 -2.176 c
+4.135 -2.176 3.859 -2.055 3.675 -1.808 c
+3.499 -1.555 3.403 -1.191 3.395 -0.721 c
+3.395 1.881 l
+4.042 1.881 l
+4.042 -0.662 l
+4.042 -1.291 4.226 -1.603 4.601 -1.603 c
+5.002 -1.603 5.277 -1.426 5.424 -1.073 c
+5.424 1.881 l
+6.071 1.881 l
+6.071 -2.103 l
+5.453 -2.103 l
+h
+8.996 -1.088 m
+8.996 -0.941 8.941 -0.82 8.834 -0.721 c
+8.724 -0.626 8.518 -0.508 8.217 -0.368 c
+7.871 -0.221 7.628 -0.1 7.482 0 c
+7.335 0.106 7.225 0.224 7.158 0.353 c
+7.088 0.477 7.056 0.635 7.056 0.823 c
+7.056 1.146 7.173 1.414 7.408 1.631 c
+7.644 1.844 7.945 1.955 8.32 1.955 c
+8.702 1.955 9.01 1.841 9.246 1.616 c
+9.481 1.389 9.598 1.102 9.598 0.749 c
+8.952 0.749 l
+8.952 0.926 8.893 1.076 8.775 1.205 c
+8.658 1.33 8.503 1.396 8.32 1.396 c
+8.121 1.396 7.971 1.341 7.864 1.234 c
+7.754 1.135 7.703 1.003 7.703 0.837 c
+7.703 0.708 7.739 0.602 7.82 0.514 c
+7.898 0.433 8.088 0.33 8.393 0.205 c
+8.871 0.018 9.201 -0.169 9.378 -0.353 c
+9.554 -0.53 9.643 -0.757 9.643 -1.029 c
+9.643 -1.382 9.517 -1.661 9.275 -1.867 c
+9.04 -2.073 8.724 -2.176 8.334 -2.176 c
+7.912 -2.176 7.574 -2.058 7.32 -1.823 c
+7.063 -1.58 6.938 -1.276 6.938 -0.912 c
+7.585 -0.912 l
+7.592 -1.139 7.662 -1.316 7.79 -1.441 c
+7.915 -1.559 8.099 -1.617 8.334 -1.617 c
+8.548 -1.617 8.709 -1.569 8.819 -1.47 c
+8.937 -1.374 8.996 -1.246 8.996 -1.088 c
+11.127 1.469 m
+11.381 1.793 11.701 1.955 12.083 1.955 c
+12.788 1.955 13.145 1.484 13.156 0.544 c
+13.156 -2.103 l
+12.509 -2.103 l
+12.509 0.514 l
+12.509 0.826 12.454 1.047 12.347 1.176 c
+12.237 1.3 12.083 1.367 11.877 1.367 c
+11.719 1.367 11.572 1.311 11.436 1.205 c
+11.308 1.095 11.204 0.959 11.127 0.793 c
+11.127 -2.103 l
+10.481 -2.103 l
+10.481 3.542 l
+11.127 3.542 l
+h
+16.536 -2.103 -0.646 5.644 re
+17.419 0.073 m
+17.419 0.65 17.554 1.105 17.83 1.44 c
+18.113 1.782 18.485 1.955 18.947 1.955 c
+19.407 1.955 19.774 1.786 20.05 1.454 c
+20.332 1.132 20.48 0.683 20.49 0.118 c
+20.49 -0.309 l
+20.49 -0.879 20.347 -1.334 20.064 -1.676 c
+19.788 -2.01 19.422 -2.176 18.962 -2.176 c
+18.499 -2.176 18.128 -2.014 17.845 -1.691 c
+17.569 -1.36 17.426 -0.919 17.419 -0.368 c
+h
+18.065 -0.309 m
+18.065 -0.713 18.142 -1.029 18.3 -1.264 c
+18.466 -1.5 18.686 -1.617 18.962 -1.617 c
+19.528 -1.617 19.821 -1.206 19.844 -0.383 c
+19.844 0.073 l
+19.844 0.473 19.759 0.793 19.594 1.028 c
+19.436 1.271 19.219 1.396 18.947 1.396 c
+18.682 1.396 18.466 1.271 18.3 1.028 c
+18.142 0.793 18.065 0.473 18.065 0.073 c
+h
+22.71 -1.617 m
+22.923 -1.617 23.096 -1.555 23.225 -1.426 c
+23.36 -1.291 23.434 -1.099 23.445 -0.853 c
+24.062 -0.853 l
+24.041 -1.235 23.904 -1.555 23.651 -1.808 c
+23.393 -2.055 23.081 -2.176 22.71 -2.176 c
+22.217 -2.176 21.843 -2.025 21.579 -1.721 c
+21.321 -1.408 21.196 -0.941 21.196 -0.324 c
+21.196 0.118 l
+21.196 0.712 21.321 1.168 21.579 1.484 c
+21.843 1.797 22.217 1.955 22.71 1.955 c
+23.111 1.955 23.43 1.822 23.665 1.558 c
+23.908 1.3 24.041 0.955 24.062 0.514 c
+23.445 0.514 l
+23.423 0.808 23.349 1.028 23.225 1.176 c
+23.107 1.323 22.934 1.396 22.71 1.396 c
+22.416 1.396 22.2 1.296 22.063 1.102 c
+21.924 0.914 21.851 0.606 21.843 0.176 c
+21.843 -0.339 l
+21.843 -0.809 21.909 -1.143 22.049 -1.338 c
+22.196 -1.526 22.416 -1.617 22.71 -1.617 c
+26.855 -2.103 m
+26.815 -2.014 26.789 -1.867 26.782 -1.661 c
+26.547 -2.007 26.252 -2.176 25.9 -2.176 c
+25.536 -2.176 25.253 -2.08 25.047 -1.881 c
+24.849 -1.676 24.754 -1.389 24.754 -1.015 c
+24.754 -0.614 24.889 -0.294 25.165 -0.059 c
+25.437 0.183 25.812 0.309 26.282 0.309 c
+26.767 0.309 l
+26.767 0.735 l
+26.767 0.97 26.712 1.135 26.605 1.234 c
+26.495 1.341 26.333 1.396 26.121 1.396 c
+25.922 1.396 25.76 1.337 25.635 1.219 c
+25.518 1.102 25.459 0.955 25.459 0.779 c
+24.812 0.779 l
+24.812 0.974 24.871 1.165 24.989 1.352 c
+25.113 1.535 25.275 1.683 25.473 1.793 c
+25.679 1.899 25.907 1.955 26.165 1.955 c
+26.565 1.955 26.87 1.851 27.075 1.646 c
+27.289 1.44 27.403 1.146 27.414 0.764 c
+27.414 -1.25 l
+27.414 -1.555 27.451 -1.819 27.532 -2.043 c
+27.532 -2.103 l
+h
+25.988 -1.588 m
+26.153 -1.588 26.304 -1.544 26.443 -1.455 c
+26.591 -1.368 26.697 -1.257 26.767 -1.118 c
+26.767 -0.177 l
+26.4 -0.177 l
+26.084 -0.177 25.841 -0.246 25.664 -0.383 c
+25.488 -0.511 25.4 -0.699 25.4 -0.941 c
+25.4 -1.169 25.444 -1.334 25.533 -1.441 c
+25.621 -1.54 25.772 -1.588 25.988 -1.588 c
+29.119 -2.103 -0.646 5.644 re
+34.734 -0.309 m
+34.734 -0.927 34.62 -1.393 34.396 -1.706 c
+34.179 -2.022 33.855 -2.176 33.425 -2.176 c
+33.003 -2.176 32.691 -1.996 32.485 -1.632 c
+32.456 -2.103 l
+31.852 -2.103 l
+31.852 3.542 l
+32.5 3.542 l
+32.5 1.44 l
+32.713 1.782 33.022 1.955 33.425 1.955 c
+33.855 1.955 34.179 1.797 34.396 1.484 c
+34.62 1.179 34.734 0.712 34.734 0.087 c
+h
+34.088 0.073 m
+34.088 0.544 34.017 0.874 33.882 1.072 c
+33.753 1.267 33.543 1.367 33.249 1.367 c
+32.915 1.367 32.665 1.183 32.5 0.823 c
+32.5 -1.058 l
+32.665 -1.422 32.918 -1.603 33.264 -1.603 c
+33.558 -1.603 33.768 -1.5 33.896 -1.294 c
+34.021 -1.088 34.088 -0.772 34.088 -0.339 c
+h
+37.218 1.263 m
+37.13 1.282 37.03 1.294 36.924 1.294 c
+36.59 1.294 36.355 1.109 36.218 0.749 c
+36.218 -2.103 l
+35.572 -2.103 l
+35.572 1.881 l
+36.204 1.881 l
+36.218 1.469 l
+36.395 1.793 36.637 1.955 36.953 1.955 c
+37.06 1.955 37.148 1.932 37.218 1.896 c
+h
+39.761 -2.103 m
+39.721 -2.014 39.695 -1.867 39.688 -1.661 c
+39.453 -2.007 39.158 -2.176 38.805 -2.176 c
+38.441 -2.176 38.159 -2.08 37.953 -1.881 c
+37.755 -1.676 37.659 -1.389 37.659 -1.015 c
+37.659 -0.614 37.795 -0.294 38.071 -0.059 c
+38.343 0.183 38.717 0.309 39.187 0.309 c
+39.673 0.309 l
+39.673 0.735 l
+39.673 0.97 39.617 1.135 39.511 1.234 c
+39.401 1.341 39.239 1.396 39.027 1.396 c
+38.828 1.396 38.666 1.337 38.541 1.219 c
+38.424 1.102 38.364 0.955 38.364 0.779 c
+37.718 0.779 l
+37.718 0.974 37.776 1.165 37.894 1.352 c
+38.019 1.535 38.181 1.683 38.379 1.793 c
+38.585 1.899 38.813 1.955 39.07 1.955 c
+39.471 1.955 39.775 1.851 39.981 1.646 c
+40.195 1.44 40.309 1.146 40.319 0.764 c
+40.319 -1.25 l
+40.319 -1.555 40.357 -1.819 40.438 -2.043 c
+40.438 -2.103 l
+h
+38.894 -1.588 m
+39.059 -1.588 39.21 -1.544 39.349 -1.455 c
+39.497 -1.368 39.603 -1.257 39.673 -1.118 c
+39.673 -0.177 l
+39.305 -0.177 l
+38.989 -0.177 38.747 -0.246 38.57 -0.383 c
+38.394 -0.511 38.306 -0.699 38.306 -0.941 c
+38.306 -1.169 38.35 -1.334 38.438 -1.441 c
+38.526 -1.54 38.677 -1.588 38.894 -1.588 c
+41.936 1.881 m
+41.951 1.44 l
+42.204 1.782 42.528 1.955 42.921 1.955 c
+43.627 1.955 43.983 1.484 43.995 0.544 c
+43.995 -2.103 l
+43.347 -2.103 l
+43.347 0.514 l
+43.347 0.826 43.293 1.047 43.186 1.176 c
+43.076 1.3 42.921 1.367 42.715 1.367 c
+42.557 1.367 42.41 1.311 42.275 1.205 c
+42.146 1.095 42.044 0.959 41.965 0.793 c
+41.965 -2.103 l
+41.319 -2.103 l
+41.319 1.881 l
+h
+46.346 -1.617 m
+46.559 -1.617 46.732 -1.555 46.861 -1.426 c
+46.997 -1.291 47.07 -1.099 47.081 -0.853 c
+47.698 -0.853 l
+47.677 -1.235 47.54 -1.555 47.287 -1.808 c
+47.029 -2.055 46.717 -2.176 46.346 -2.176 c
+45.854 -2.176 45.479 -2.025 45.215 -1.721 c
+44.957 -1.408 44.832 -0.941 44.832 -0.324 c
+44.832 0.118 l
+44.832 0.712 44.957 1.168 45.215 1.484 c
+45.479 1.797 45.854 1.955 46.346 1.955 c
+46.747 1.955 47.066 1.822 47.301 1.558 c
+47.544 1.3 47.677 0.955 47.698 0.514 c
+47.081 0.514 l
+47.06 0.808 46.985 1.028 46.861 1.176 c
+46.743 1.323 46.57 1.396 46.346 1.396 c
+46.052 1.396 45.836 1.296 45.699 1.102 c
+45.56 0.914 45.487 0.606 45.479 0.176 c
+45.479 -0.339 l
+45.479 -0.809 45.545 -1.143 45.685 -1.338 c
+45.832 -1.526 46.052 -1.617 46.346 -1.617 c
+49.095 1.469 m
+49.348 1.793 49.668 1.955 50.05 1.955 c
+50.756 1.955 51.112 1.484 51.124 0.544 c
+51.124 -2.103 l
+50.476 -2.103 l
+50.476 0.514 l
+50.476 0.826 50.422 1.047 50.315 1.176 c
+50.204 1.3 50.05 1.367 49.845 1.367 c
+49.687 1.367 49.539 1.311 49.404 1.205 c
+49.275 1.095 49.172 0.959 49.095 0.793 c
+49.095 -2.103 l
+48.448 -2.103 l
+48.448 3.542 l
+49.095 3.542 l
+h
+54.651 2.836 m
+54.651 1.881 l
+55.254 1.881 l
+55.254 1.352 l
+54.651 1.352 l
+54.651 -1.118 l
+54.651 -1.276 54.673 -1.393 54.725 -1.47 c
+54.784 -1.551 54.871 -1.588 54.989 -1.588 c
+55.077 -1.588 55.166 -1.573 55.254 -1.544 c
+55.254 -2.103 l
+55.107 -2.151 54.952 -2.176 54.798 -2.176 c
+54.541 -2.176 54.347 -2.084 54.21 -1.897 c
+54.071 -1.713 54.004 -1.452 54.004 -1.118 c
+54.004 1.352 l
+53.402 1.352 l
+53.402 1.881 l
+54.004 1.881 l
+54.004 2.836 l
+h
+55.812 0.073 m
+55.812 0.65 55.949 1.105 56.224 1.44 c
+56.507 1.782 56.878 1.955 57.341 1.955 c
+57.801 1.955 58.168 1.786 58.443 1.454 c
+58.727 1.132 58.873 0.683 58.885 0.118 c
+58.885 -0.309 l
+58.885 -0.879 58.742 -1.334 58.458 -1.676 c
+58.183 -2.01 57.815 -2.176 57.356 -2.176 c
+56.893 -2.176 56.522 -2.014 56.238 -1.691 c
+55.963 -1.36 55.82 -0.919 55.812 -0.368 c
+h
+56.459 -0.309 m
+56.459 -0.713 56.537 -1.029 56.695 -1.264 c
+56.86 -1.5 57.08 -1.617 57.356 -1.617 c
+57.921 -1.617 58.216 -1.206 58.237 -0.383 c
+58.237 0.073 l
+58.237 0.473 58.154 0.793 57.988 1.028 c
+57.83 1.271 57.613 1.396 57.341 1.396 c
+57.077 1.396 56.86 1.271 56.695 1.028 c
+56.537 0.793 56.459 0.473 56.459 0.073 c
+h
+f
+Q
+q 1 0 0 1 131.2811 144.5107 cm
+0 0 m
+-0.338 0.03 l
+-0.625 0.03 -0.816 -0.095 -0.911 -0.338 c
+-0.911 -2.969 l
+-1.955 -2.969 l
+-1.955 1.015 l
+-0.984 1.015 l
+-0.955 0.574 l
+-0.789 0.915 -0.558 1.088 -0.264 1.088 c
+-0.147 1.088 -0.055 1.066 0.015 1.029 c
+h
+2.073 -3.042 m
+1.544 -3.042 1.125 -2.888 0.823 -2.572 c
+0.53 -2.248 0.383 -1.789 0.383 -1.19 c
+0.383 -0.881 l
+0.383 -0.257 0.518 0.229 0.794 0.574 c
+1.066 0.915 1.459 1.088 1.97 1.088 c
+2.469 1.088 2.841 0.927 3.088 0.603 c
+3.341 0.279 3.473 -0.199 3.484 -0.823 c
+3.484 -1.323 l
+1.411 -1.323 l
+1.43 -1.616 1.492 -1.833 1.603 -1.969 c
+1.72 -2.109 1.9 -2.175 2.147 -2.175 c
+2.488 -2.175 2.779 -2.057 3.014 -1.822 c
+3.425 -2.454 l
+3.296 -2.63 3.109 -2.774 2.866 -2.881 c
+2.621 -2.987 2.356 -3.042 2.073 -3.042 c
+1.426 -0.602 m
+2.455 -0.602 l
+2.455 -0.5 l
+2.455 -0.264 2.415 -0.087 2.338 0.03 c
+2.268 0.154 2.139 0.221 1.955 0.221 c
+1.779 0.221 1.646 0.151 1.559 0.015 c
+1.478 -0.114 1.434 -0.319 1.426 -0.602 c
+5.042 1.015 m
+5.072 0.647 l
+5.307 0.941 5.615 1.088 5.997 1.088 c
+6.398 1.088 6.677 0.904 6.835 0.545 c
+7.071 0.904 7.398 1.088 7.82 1.088 c
+8.515 1.088 8.867 0.603 8.879 -0.367 c
+8.879 -2.969 l
+7.85 -2.969 l
+7.85 -0.426 l
+7.85 -0.201 7.813 -0.04 7.747 0.059 c
+7.688 0.154 7.578 0.206 7.423 0.206 c
+7.225 0.206 7.086 0.088 6.997 -0.147 c
+6.997 -2.969 l
+5.953 -2.969 l
+5.953 -0.44 l
+5.953 -0.205 5.924 -0.04 5.866 0.059 c
+5.806 0.154 5.696 0.206 5.542 0.206 c
+5.366 0.206 5.222 0.111 5.116 -0.073 c
+5.116 -2.969 l
+4.072 -2.969 l
+4.072 1.015 l
+h
+9.54 -0.852 m
+9.54 -0.246 9.679 0.229 9.966 0.574 c
+10.249 0.915 10.643 1.088 11.142 1.088 c
+11.649 1.088 12.046 0.915 12.333 0.574 c
+12.616 0.229 12.759 -0.246 12.759 -0.852 c
+12.759 -1.117 l
+12.759 -1.716 12.616 -2.186 12.333 -2.528 c
+12.046 -2.873 11.649 -3.042 11.142 -3.042 c
+10.631 -3.042 10.234 -2.873 9.951 -2.528 c
+9.676 -2.186 9.54 -1.712 9.54 -1.102 c
+h
+10.583 -1.117 m
+10.583 -1.822 10.768 -2.175 11.142 -2.175 c
+11.495 -2.175 11.686 -1.881 11.716 -1.294 c
+11.716 -0.852 l
+11.716 -0.492 11.664 -0.22 11.568 -0.043 c
+11.469 0.133 11.326 0.221 11.142 0.221 c
+10.966 0.221 10.826 0.133 10.731 -0.043 c
+10.631 -0.22 10.583 -0.492 10.583 -0.852 c
+h
+14.612 1.985 m
+14.612 1.015 l
+15.14 1.015 l
+15.14 0.221 l
+14.612 0.221 l
+14.612 -1.749 l
+14.612 -1.907 14.629 -2.013 14.67 -2.072 c
+14.718 -2.131 14.803 -2.16 14.92 -2.16 c
+15.026 -2.16 15.111 -2.153 15.169 -2.131 c
+15.169 -2.94 l
+14.993 -3.006 14.803 -3.042 14.597 -3.042 c
+13.92 -3.042 13.575 -2.657 13.567 -1.881 c
+13.567 0.221 l
+13.112 0.221 l
+13.112 1.015 l
+13.567 1.015 l
+13.567 1.985 l
+h
+17.316 -3.042 m
+16.787 -3.042 16.368 -2.888 16.066 -2.572 c
+15.772 -2.248 15.626 -1.789 15.626 -1.19 c
+15.626 -0.881 l
+15.626 -0.257 15.761 0.229 16.037 0.574 c
+16.309 0.915 16.702 1.088 17.213 1.088 c
+17.712 1.088 18.084 0.927 18.33 0.603 c
+18.583 0.279 18.716 -0.199 18.727 -0.823 c
+18.727 -1.323 l
+16.655 -1.323 l
+16.673 -1.616 16.735 -1.833 16.846 -1.969 c
+16.963 -2.109 17.143 -2.175 17.39 -2.175 c
+17.731 -2.175 18.022 -2.057 18.257 -1.822 c
+18.668 -2.454 l
+18.539 -2.63 18.352 -2.774 18.109 -2.881 c
+17.864 -2.987 17.598 -3.042 17.316 -3.042 c
+16.669 -0.602 m
+17.698 -0.602 l
+17.698 -0.5 l
+17.698 -0.264 17.658 -0.087 17.581 0.03 c
+17.511 0.154 17.382 0.221 17.199 0.221 c
+17.022 0.221 16.89 0.151 16.802 0.015 c
+16.721 -0.114 16.676 -0.319 16.669 -0.602 c
+f
+Q
+q 1 0 0 1 154.0648 144.9077 cm
+0 0 m
+-0.088 0.019 -0.187 0.03 -0.294 0.03 c
+-0.628 0.03 -0.864 -0.154 -0.999 -0.515 c
+-0.999 -3.366 l
+-1.646 -3.366 l
+-1.646 0.618 l
+-1.014 0.618 l
+-0.999 0.206 l
+-0.823 0.53 -0.58 0.691 -0.264 0.691 c
+-0.158 0.691 -0.07 0.669 0 0.632 c
+h
+1.999 -3.439 m
+1.5 -3.439 1.118 -3.293 0.852 -2.998 c
+0.588 -2.705 0.456 -2.271 0.456 -1.691 c
+0.456 -1.22 l
+0.456 -0.625 0.58 -0.158 0.838 0.177 c
+1.103 0.518 1.463 0.691 1.926 0.691 c
+2.385 0.691 2.727 0.537 2.955 0.235 c
+3.19 -0.058 3.311 -0.521 3.322 -1.146 c
+3.322 -1.573 l
+1.103 -1.573 l
+1.103 -1.66 l
+1.103 -2.094 1.18 -2.406 1.338 -2.601 c
+1.503 -2.789 1.735 -2.881 2.028 -2.881 c
+2.223 -2.881 2.396 -2.848 2.543 -2.778 c
+2.69 -2.701 2.826 -2.583 2.955 -2.425 c
+3.293 -2.836 l
+3.006 -3.241 2.576 -3.439 1.999 -3.439 c
+1.926 0.133 m
+1.65 0.133 1.448 0.037 1.323 -0.147 c
+1.195 -0.334 1.121 -0.625 1.103 -1.014 c
+2.675 -1.014 l
+2.675 -0.926 l
+2.653 -0.544 2.587 -0.276 2.469 -0.118 c
+2.352 0.048 2.168 0.133 1.926 0.133 c
+6.982 -1.573 m
+6.982 -2.201 6.865 -2.672 6.629 -2.984 c
+6.402 -3.289 6.086 -3.439 5.674 -3.439 c
+5.27 -3.439 4.961 -3.289 4.748 -2.984 c
+4.748 -4.895 l
+4.101 -4.895 l
+4.101 0.618 l
+4.69 0.618 l
+4.733 0.177 l
+4.946 0.518 5.255 0.691 5.659 0.691 c
+6.101 0.691 6.427 0.537 6.644 0.235 c
+6.857 -0.07 6.971 -0.525 6.982 -1.132 c
+h
+6.336 -1.19 m
+6.336 -0.75 6.265 -0.426 6.13 -0.22 c
+5.99 -0.008 5.77 0.103 5.469 0.103 c
+5.152 0.103 4.913 -0.051 4.748 -0.353 c
+4.748 -2.425 l
+4.913 -2.73 5.152 -2.881 5.469 -2.881 c
+5.762 -2.881 5.976 -2.778 6.115 -2.572 c
+6.251 -2.359 6.325 -2.028 6.336 -1.587 c
+h
+7.688 -1.19 m
+7.688 -0.613 7.824 -0.158 8.099 0.177 c
+8.382 0.518 8.754 0.691 9.216 0.691 c
+9.676 0.691 10.043 0.522 10.319 0.191 c
+10.602 -0.132 10.749 -0.58 10.76 -1.146 c
+10.76 -1.573 l
+10.76 -2.142 10.616 -2.597 10.334 -2.94 c
+10.058 -3.274 9.691 -3.439 9.231 -3.439 c
+8.768 -3.439 8.397 -3.278 8.114 -2.954 c
+7.838 -2.624 7.695 -2.183 7.688 -1.631 c
+h
+8.334 -1.573 m
+8.334 -1.976 8.412 -2.293 8.569 -2.528 c
+8.735 -2.763 8.956 -2.881 9.231 -2.881 c
+9.797 -2.881 10.091 -2.469 10.113 -1.646 c
+10.113 -1.19 l
+10.113 -0.79 10.028 -0.47 9.864 -0.235 c
+9.706 0.008 9.488 0.133 9.216 0.133 c
+8.952 0.133 8.735 0.008 8.569 -0.235 c
+8.412 -0.47 8.334 -0.79 8.334 -1.19 c
+h
+13.538 -2.352 m
+13.538 -2.204 13.483 -2.084 13.376 -1.984 c
+13.266 -1.889 13.06 -1.771 12.759 -1.631 c
+12.413 -1.484 12.171 -1.363 12.024 -1.263 c
+11.877 -1.157 11.767 -1.04 11.701 -0.911 c
+11.63 -0.786 11.597 -0.628 11.597 -0.44 c
+11.597 -0.118 11.715 0.151 11.95 0.368 c
+12.185 0.58 12.487 0.691 12.862 0.691 c
+13.244 0.691 13.552 0.578 13.787 0.353 c
+14.023 0.125 14.14 -0.162 14.14 -0.515 c
+13.494 -0.515 l
+13.494 -0.338 13.435 -0.187 13.317 -0.058 c
+13.2 0.067 13.045 0.133 12.862 0.133 c
+12.663 0.133 12.513 0.077 12.406 -0.029 c
+12.295 -0.128 12.245 -0.261 12.245 -0.426 c
+12.245 -0.555 12.281 -0.661 12.362 -0.75 c
+12.439 -0.831 12.63 -0.933 12.935 -1.058 c
+13.413 -1.246 13.743 -1.433 13.92 -1.616 c
+14.097 -1.793 14.184 -2.021 14.184 -2.293 c
+14.184 -2.645 14.059 -2.925 13.818 -3.131 c
+13.582 -3.337 13.266 -3.439 12.877 -3.439 c
+12.454 -3.439 12.116 -3.322 11.862 -3.087 c
+11.605 -2.844 11.48 -2.539 11.48 -2.175 c
+12.127 -2.175 l
+12.135 -2.403 12.204 -2.579 12.332 -2.705 c
+12.457 -2.822 12.642 -2.881 12.877 -2.881 c
+13.089 -2.881 13.251 -2.833 13.361 -2.734 c
+13.479 -2.638 13.538 -2.51 13.538 -2.352 c
+15.728 -3.366 -0.647 3.984 re
+15.772 1.661 m
+15.772 1.551 15.743 1.459 15.684 1.382 c
+15.625 1.312 15.53 1.279 15.405 1.279 c
+15.287 1.279 15.192 1.312 15.125 1.382 c
+15.067 1.459 15.038 1.551 15.038 1.661 c
+15.038 1.779 15.067 1.871 15.125 1.941 c
+15.192 2.018 15.287 2.058 15.405 2.058 c
+15.53 2.058 15.625 2.018 15.684 1.941 c
+15.743 1.86 15.772 1.768 15.772 1.661 c
+17.595 1.573 m
+17.595 0.618 l
+18.198 0.618 l
+18.198 0.088 l
+17.595 0.088 l
+17.595 -2.381 l
+17.595 -2.539 17.617 -2.657 17.668 -2.734 c
+17.727 -2.815 17.816 -2.851 17.933 -2.851 c
+18.021 -2.851 18.109 -2.836 18.198 -2.807 c
+18.198 -3.366 l
+18.051 -3.414 17.897 -3.439 17.741 -3.439 c
+17.485 -3.439 17.29 -3.347 17.154 -3.16 c
+17.014 -2.976 16.948 -2.715 16.948 -2.381 c
+16.948 0.088 l
+16.345 0.088 l
+16.345 0.618 l
+16.948 0.618 l
+16.948 1.573 l
+h
+18.756 -1.19 m
+18.756 -0.613 18.892 -0.158 19.168 0.177 c
+19.451 0.518 19.821 0.691 20.285 0.691 c
+20.744 0.691 21.112 0.522 21.388 0.191 c
+21.67 -0.132 21.818 -0.58 21.828 -1.146 c
+21.828 -1.573 l
+21.828 -2.142 21.685 -2.597 21.402 -2.94 c
+21.126 -3.274 20.759 -3.439 20.299 -3.439 c
+19.836 -3.439 19.466 -3.278 19.183 -2.954 c
+18.907 -2.624 18.763 -2.183 18.756 -1.631 c
+h
+19.403 -1.573 m
+19.403 -1.976 19.48 -2.293 19.638 -2.528 c
+19.803 -2.763 20.024 -2.881 20.299 -2.881 c
+20.866 -2.881 21.159 -2.469 21.182 -1.646 c
+21.182 -1.19 l
+21.182 -0.79 21.097 -0.47 20.931 -0.235 c
+20.773 0.008 20.557 0.133 20.285 0.133 c
+20.02 0.133 19.803 0.008 19.638 -0.235 c
+19.48 -0.47 19.403 -0.79 19.403 -1.19 c
+h
+24.312 0 m
+24.224 0.019 24.125 0.03 24.018 0.03 c
+23.684 0.03 23.449 -0.154 23.312 -0.515 c
+23.312 -3.366 l
+22.666 -3.366 l
+22.666 0.618 l
+23.298 0.618 l
+23.312 0.206 l
+23.489 0.53 23.732 0.691 24.048 0.691 c
+24.154 0.691 24.243 0.669 24.312 0.632 c
+h
+26.121 -2.278 m
+26.84 0.618 l
+27.532 0.618 l
+26.238 -3.925 l
+26.138 -4.266 25.995 -4.527 25.812 -4.704 c
+25.635 -4.88 25.433 -4.968 25.209 -4.968 c
+25.121 -4.968 25.007 -4.946 24.871 -4.909 c
+24.871 -4.365 l
+25.018 -4.38 l
+25.201 -4.38 25.348 -4.336 25.459 -4.247 c
+25.566 -4.16 25.654 -4.002 25.724 -3.777 c
+25.841 -3.337 l
+24.68 0.618 l
+25.386 0.618 l
+h
+27.811 -3.013 m
+27.811 -2.896 27.844 -2.8 27.914 -2.719 c
+27.979 -2.642 28.083 -2.601 28.222 -2.601 c
+28.369 -2.601 28.476 -2.642 28.546 -2.719 c
+28.623 -2.8 28.663 -2.896 28.663 -3.013 c
+28.663 -3.123 28.623 -3.215 28.546 -3.293 c
+28.476 -3.37 28.369 -3.41 28.222 -3.41 c
+28.083 -3.41 27.979 -3.37 27.914 -3.293 c
+27.844 -3.215 27.811 -3.123 27.811 -3.013 c
+33.896 -2.013 m
+33.896 -1.749 33.822 -1.55 33.676 -1.411 c
+33.536 -1.263 33.279 -1.124 32.897 -0.985 c
+32.521 -0.849 32.235 -0.706 32.029 -0.558 c
+31.831 -0.411 31.684 -0.246 31.588 -0.058 c
+31.489 0.136 31.441 0.357 31.441 0.603 c
+31.441 1.022 31.581 1.371 31.868 1.646 c
+32.151 1.918 32.519 2.058 32.97 2.058 c
+33.282 2.058 33.562 1.985 33.808 1.837 c
+34.05 1.698 34.238 1.503 34.366 1.249 c
+34.503 1.004 34.572 0.736 34.572 0.441 c
+33.896 0.441 l
+33.896 0.772 33.815 1.029 33.661 1.206 c
+33.503 1.389 33.271 1.484 32.97 1.484 c
+32.706 1.484 32.492 1.404 32.338 1.249 c
+32.191 1.103 32.118 0.89 32.118 0.618 c
+32.118 0.389 32.195 0.199 32.353 0.044 c
+32.519 -0.103 32.772 -0.243 33.117 -0.367 c
+33.635 -0.536 34.007 -0.746 34.234 -0.999 c
+34.47 -1.257 34.587 -1.587 34.587 -1.999 c
+34.587 -2.439 34.443 -2.792 34.161 -3.057 c
+33.874 -3.314 33.492 -3.439 33.014 -3.439 c
+32.698 -3.439 32.411 -3.37 32.147 -3.233 c
+31.883 -3.098 31.669 -2.907 31.515 -2.66 c
+31.368 -2.418 31.295 -2.131 31.295 -1.808 c
+31.971 -1.808 l
+31.971 -2.142 32.062 -2.403 32.249 -2.587 c
+32.434 -2.774 32.691 -2.866 33.014 -2.866 c
+33.308 -2.866 33.529 -2.792 33.676 -2.645 c
+33.822 -2.491 33.896 -2.278 33.896 -2.013 c
+36.791 -3.439 m
+36.292 -3.439 35.91 -3.293 35.646 -2.998 c
+35.38 -2.705 35.249 -2.271 35.249 -1.691 c
+35.249 -1.22 l
+35.249 -0.625 35.374 -0.158 35.631 0.177 c
+35.895 0.518 36.255 0.691 36.718 0.691 c
+37.178 0.691 37.52 0.537 37.747 0.235 c
+37.982 -0.058 38.104 -0.521 38.115 -1.146 c
+38.115 -1.573 l
+35.895 -1.573 l
+35.895 -1.66 l
+35.895 -2.094 35.972 -2.406 36.13 -2.601 c
+36.296 -2.789 36.527 -2.881 36.821 -2.881 c
+37.016 -2.881 37.188 -2.848 37.336 -2.778 c
+37.483 -2.701 37.618 -2.583 37.747 -2.425 c
+38.086 -2.836 l
+37.799 -3.241 37.369 -3.439 36.791 -3.439 c
+36.718 0.133 m
+36.442 0.133 36.241 0.037 36.116 -0.147 c
+35.987 -0.334 35.914 -0.625 35.895 -1.014 c
+37.468 -1.014 l
+37.468 -0.926 l
+37.446 -0.544 37.38 -0.276 37.262 -0.118 c
+37.145 0.048 36.961 0.133 36.718 0.133 c
+39.746 1.573 m
+39.746 0.618 l
+40.349 0.618 l
+40.349 0.088 l
+39.746 0.088 l
+39.746 -2.381 l
+39.746 -2.539 39.769 -2.657 39.82 -2.734 c
+39.879 -2.815 39.967 -2.851 40.084 -2.851 c
+40.172 -2.851 40.261 -2.836 40.349 -2.807 c
+40.349 -3.366 l
+40.202 -3.414 40.047 -3.439 39.893 -3.439 c
+39.636 -3.439 39.441 -3.347 39.305 -3.16 c
+39.166 -2.976 39.1 -2.715 39.1 -2.381 c
+39.1 0.088 l
+38.497 0.088 l
+38.497 0.618 l
+39.1 0.618 l
+39.1 1.573 l
+h
+43.538 -3.366 -0.646 3.984 re
+43.583 1.661 m
+43.583 1.551 43.553 1.459 43.495 1.382 c
+43.436 1.312 43.34 1.279 43.216 1.279 c
+43.098 1.279 43.002 1.312 42.936 1.382 c
+42.877 1.459 42.848 1.551 42.848 1.661 c
+42.848 1.779 42.877 1.871 42.936 1.941 c
+43.002 2.018 43.098 2.058 43.216 2.058 c
+43.34 2.058 43.436 2.018 43.495 1.941 c
+43.553 1.86 43.583 1.768 43.583 1.661 c
+45.406 1.573 m
+45.406 0.618 l
+46.008 0.618 l
+46.008 0.088 l
+45.406 0.088 l
+45.406 -2.381 l
+45.406 -2.539 45.427 -2.657 45.479 -2.734 c
+45.537 -2.815 45.626 -2.851 45.743 -2.851 c
+45.832 -2.851 45.92 -2.836 46.008 -2.807 c
+46.008 -3.366 l
+45.861 -3.414 45.707 -3.439 45.552 -3.439 c
+45.296 -3.439 45.101 -3.347 44.965 -3.16 c
+44.825 -2.976 44.758 -2.715 44.758 -2.381 c
+44.758 0.088 l
+44.156 0.088 l
+44.156 0.618 l
+44.758 0.618 l
+44.758 1.573 l
+h
+48.712 -2.352 m
+48.712 -2.204 48.658 -2.084 48.551 -1.984 c
+48.441 -1.889 48.235 -1.771 47.934 -1.631 c
+47.588 -1.484 47.346 -1.363 47.199 -1.263 c
+47.052 -1.157 46.942 -1.04 46.875 -0.911 c
+46.806 -0.786 46.773 -0.628 46.773 -0.44 c
+46.773 -0.118 46.89 0.151 47.125 0.368 c
+47.361 0.58 47.662 0.691 48.037 0.691 c
+48.419 0.691 48.727 0.578 48.963 0.353 c
+49.198 0.125 49.315 -0.162 49.315 -0.515 c
+48.669 -0.515 l
+48.669 -0.338 48.61 -0.187 48.492 -0.058 c
+48.375 0.067 48.22 0.133 48.037 0.133 c
+47.838 0.133 47.688 0.077 47.581 -0.029 c
+47.471 -0.128 47.419 -0.261 47.419 -0.426 c
+47.419 -0.555 47.456 -0.661 47.536 -0.75 c
+47.614 -0.831 47.806 -0.933 48.11 -1.058 c
+48.588 -1.246 48.918 -1.433 49.095 -1.616 c
+49.271 -1.793 49.36 -2.021 49.36 -2.293 c
+49.36 -2.645 49.235 -2.925 48.992 -3.131 c
+48.757 -3.337 48.441 -3.439 48.051 -3.439 c
+47.629 -3.439 47.291 -3.322 47.037 -3.087 c
+46.78 -2.844 46.655 -2.539 46.655 -2.175 c
+47.301 -2.175 l
+47.309 -2.403 47.378 -2.579 47.507 -2.705 c
+47.632 -2.822 47.816 -2.881 48.051 -2.881 c
+48.265 -2.881 48.426 -2.833 48.536 -2.734 c
+48.654 -2.638 48.712 -2.51 48.712 -2.352 c
+53.299 -2.881 m
+53.512 -2.881 53.685 -2.818 53.813 -2.69 c
+53.95 -2.554 54.023 -2.362 54.034 -2.117 c
+54.651 -2.117 l
+54.629 -2.499 54.493 -2.818 54.239 -3.072 c
+53.982 -3.318 53.67 -3.439 53.299 -3.439 c
+52.807 -3.439 52.432 -3.289 52.167 -2.984 c
+51.91 -2.672 51.785 -2.204 51.785 -1.587 c
+51.785 -1.146 l
+51.785 -0.551 51.91 -0.095 52.167 0.221 c
+52.432 0.533 52.807 0.691 53.299 0.691 c
+53.699 0.691 54.019 0.559 54.254 0.294 c
+54.497 0.037 54.629 -0.309 54.651 -0.75 c
+54.034 -0.75 l
+54.012 -0.455 53.938 -0.235 53.813 -0.087 c
+53.696 0.059 53.523 0.133 53.299 0.133 c
+53.005 0.133 52.788 0.033 52.652 -0.162 c
+52.512 -0.349 52.439 -0.658 52.432 -1.087 c
+52.432 -1.602 l
+52.432 -2.072 52.498 -2.406 52.637 -2.601 c
+52.784 -2.789 53.005 -2.881 53.299 -2.881 c
+55.268 -1.19 m
+55.268 -0.613 55.405 -0.158 55.68 0.177 c
+55.963 0.518 56.334 0.691 56.797 0.691 c
+57.256 0.691 57.624 0.522 57.9 0.191 c
+58.183 -0.132 58.33 -0.58 58.341 -1.146 c
+58.341 -1.573 l
+58.341 -2.142 58.197 -2.597 57.915 -2.94 c
+57.639 -3.274 57.271 -3.439 56.812 -3.439 c
+56.349 -3.439 55.978 -3.278 55.695 -2.954 c
+55.419 -2.624 55.276 -2.183 55.268 -1.631 c
+h
+55.916 -1.573 m
+55.916 -1.976 55.993 -2.293 56.151 -2.528 c
+56.315 -2.763 56.537 -2.881 56.812 -2.881 c
+57.378 -2.881 57.672 -2.469 57.694 -1.646 c
+57.694 -1.19 l
+57.694 -0.79 57.609 -0.47 57.444 -0.235 c
+57.286 0.008 57.069 0.133 56.797 0.133 c
+56.533 0.133 56.315 0.008 56.151 -0.235 c
+55.993 -0.47 55.916 -0.79 55.916 -1.19 c
+h
+62.06 -1.573 m
+62.06 -2.201 61.942 -2.672 61.707 -2.984 c
+61.479 -3.289 61.163 -3.439 60.751 -3.439 c
+60.347 -3.439 60.038 -3.289 59.825 -2.984 c
+59.825 -4.895 l
+59.178 -4.895 l
+59.178 0.618 l
+59.766 0.618 l
+59.81 0.177 l
+60.024 0.518 60.332 0.691 60.737 0.691 c
+61.177 0.691 61.505 0.537 61.721 0.235 c
+61.935 -0.07 62.048 -0.525 62.06 -1.132 c
+h
+61.412 -1.19 m
+61.412 -0.75 61.343 -0.426 61.207 -0.22 c
+61.067 -0.008 60.847 0.103 60.545 0.103 c
+60.23 0.103 59.991 -0.051 59.825 -0.353 c
+59.825 -2.425 l
+59.991 -2.73 60.23 -2.881 60.545 -2.881 c
+60.839 -2.881 61.053 -2.778 61.192 -2.572 c
+61.329 -2.359 61.402 -2.028 61.412 -1.587 c
+h
+63.941 -2.278 m
+64.662 0.618 l
+65.352 0.618 l
+64.059 -3.925 l
+63.959 -4.266 63.816 -4.527 63.633 -4.704 c
+63.456 -4.88 63.253 -4.968 63.03 -4.968 c
+62.941 -4.968 62.827 -4.946 62.692 -4.909 c
+62.692 -4.365 l
+62.839 -4.38 l
+63.022 -4.38 63.17 -4.336 63.28 -4.247 c
+63.386 -4.16 63.475 -4.002 63.544 -3.777 c
+63.662 -3.337 l
+62.501 0.618 l
+63.206 0.618 l
+h
+69.747 -3.366 m
+69.707 -3.278 69.681 -3.131 69.674 -2.925 c
+69.439 -3.27 69.144 -3.439 68.792 -3.439 c
+68.427 -3.439 68.145 -3.343 67.939 -3.145 c
+67.741 -2.94 67.645 -2.653 67.645 -2.278 c
+67.645 -1.878 67.781 -1.558 68.057 -1.323 c
+68.329 -1.08 68.703 -0.955 69.173 -0.955 c
+69.659 -0.955 l
+69.659 -0.529 l
+69.659 -0.294 69.603 -0.128 69.497 -0.029 c
+69.387 0.077 69.225 0.133 69.012 0.133 c
+68.814 0.133 68.652 0.073 68.527 -0.044 c
+68.41 -0.162 68.35 -0.309 68.35 -0.484 c
+67.704 -0.484 l
+67.704 -0.29 67.762 -0.099 67.88 0.088 c
+68.005 0.272 68.167 0.42 68.365 0.53 c
+68.571 0.636 68.799 0.691 69.056 0.691 c
+69.457 0.691 69.761 0.588 69.967 0.383 c
+70.181 0.177 70.295 -0.118 70.306 -0.5 c
+70.306 -2.514 l
+70.306 -2.818 70.343 -3.083 70.423 -3.307 c
+70.423 -3.366 l
+h
+68.88 -2.851 m
+69.046 -2.851 69.196 -2.807 69.335 -2.719 c
+69.482 -2.631 69.589 -2.52 69.659 -2.381 c
+69.659 -1.44 l
+69.291 -1.44 l
+68.975 -1.44 68.733 -1.51 68.556 -1.646 c
+68.38 -1.774 68.292 -1.962 68.292 -2.204 c
+68.292 -2.433 68.336 -2.597 68.425 -2.705 c
+68.512 -2.803 68.663 -2.851 68.88 -2.851 c
+73.245 -2.352 m
+73.245 -2.204 73.19 -2.084 73.084 -1.984 c
+72.973 -1.889 72.768 -1.771 72.466 -1.631 c
+72.121 -1.484 71.878 -1.363 71.731 -1.263 c
+71.585 -1.157 71.475 -1.04 71.408 -0.911 c
+71.338 -0.786 71.305 -0.628 71.305 -0.44 c
+71.305 -0.118 71.423 0.151 71.658 0.368 c
+71.893 0.58 72.194 0.691 72.57 0.691 c
+72.952 0.691 73.26 0.578 73.495 0.353 c
+73.73 0.125 73.848 -0.162 73.848 -0.515 c
+73.202 -0.515 l
+73.202 -0.338 73.142 -0.187 73.025 -0.058 c
+72.907 0.067 72.753 0.133 72.57 0.133 c
+72.371 0.133 72.221 0.077 72.113 -0.029 c
+72.003 -0.128 71.952 -0.261 71.952 -0.426 c
+71.952 -0.555 71.989 -0.661 72.069 -0.75 c
+72.147 -0.831 72.338 -0.933 72.643 -1.058 c
+73.121 -1.246 73.451 -1.433 73.628 -1.616 c
+73.804 -1.793 73.892 -2.021 73.892 -2.293 c
+73.892 -2.645 73.767 -2.925 73.524 -3.131 c
+73.289 -3.337 72.973 -3.439 72.584 -3.439 c
+72.161 -3.439 71.824 -3.322 71.57 -3.087 c
+71.313 -2.844 71.188 -2.539 71.188 -2.175 c
+71.834 -2.175 l
+71.841 -2.403 71.912 -2.579 72.04 -2.705 c
+72.165 -2.822 72.348 -2.881 72.584 -2.881 c
+72.797 -2.881 72.959 -2.833 73.069 -2.734 c
+73.187 -2.638 73.245 -2.51 73.245 -2.352 c
+78.493 -3.366 m
+78.453 -3.278 78.427 -3.131 78.419 -2.925 c
+78.184 -3.27 77.89 -3.439 77.538 -3.439 c
+77.174 -3.439 76.891 -3.343 76.685 -3.145 c
+76.487 -2.94 76.391 -2.653 76.391 -2.278 c
+76.391 -1.878 76.527 -1.558 76.803 -1.323 c
+77.075 -1.08 77.449 -0.955 77.92 -0.955 c
+78.405 -0.955 l
+78.405 -0.529 l
+78.405 -0.294 78.349 -0.128 78.243 -0.029 c
+78.133 0.077 77.971 0.133 77.758 0.133 c
+77.559 0.133 77.398 0.073 77.273 -0.044 c
+77.156 -0.162 77.096 -0.309 77.096 -0.484 c
+76.45 -0.484 l
+76.45 -0.29 76.508 -0.099 76.626 0.088 c
+76.751 0.272 76.913 0.42 77.111 0.53 c
+77.317 0.636 77.545 0.691 77.802 0.691 c
+78.203 0.691 78.508 0.588 78.714 0.383 c
+78.927 0.177 79.041 -0.118 79.051 -0.5 c
+79.051 -2.514 l
+79.051 -2.818 79.088 -3.083 79.169 -3.307 c
+79.169 -3.366 l
+h
+77.626 -2.851 m
+77.792 -2.851 77.942 -2.807 78.081 -2.719 c
+78.228 -2.631 78.335 -2.52 78.405 -2.381 c
+78.405 -1.44 l
+78.037 -1.44 l
+77.721 -1.44 77.478 -1.51 77.302 -1.646 c
+77.127 -1.774 77.038 -1.962 77.038 -2.204 c
+77.038 -2.433 77.082 -2.597 77.17 -2.705 c
+77.258 -2.803 77.409 -2.851 77.626 -2.851 c
+80.668 0.618 m
+80.683 0.177 l
+80.937 0.518 81.26 0.691 81.653 0.691 c
+82.359 0.691 82.715 0.221 82.727 -0.72 c
+82.727 -3.366 l
+82.08 -3.366 l
+82.08 -0.75 l
+82.08 -0.437 82.025 -0.216 81.918 -0.087 c
+81.808 0.037 81.653 0.103 81.447 0.103 c
+81.289 0.103 81.143 0.048 81.006 -0.058 c
+80.878 -0.168 80.775 -0.305 80.698 -0.47 c
+80.698 -3.366 l
+80.051 -3.366 l
+80.051 0.618 l
+h
+87.445 -3.013 m
+87.228 -3.3 86.916 -3.439 86.504 -3.439 c
+86.141 -3.439 85.865 -3.318 85.681 -3.072 c
+85.505 -2.818 85.409 -2.454 85.401 -1.984 c
+85.401 0.618 l
+86.048 0.618 l
+86.048 -1.926 l
+86.048 -2.554 86.232 -2.866 86.607 -2.866 c
+87.007 -2.866 87.283 -2.69 87.43 -2.337 c
+87.43 0.618 l
+88.077 0.618 l
+88.077 -3.366 l
+87.46 -3.366 l
+h
+91.943 -1.573 m
+91.943 -2.201 91.825 -2.672 91.589 -2.984 c
+91.362 -3.289 91.046 -3.439 90.635 -3.439 c
+90.23 -3.439 89.922 -3.289 89.708 -2.984 c
+89.708 -4.895 l
+89.062 -4.895 l
+89.062 0.618 l
+89.65 0.618 l
+89.694 0.177 l
+89.906 0.518 90.216 0.691 90.62 0.691 c
+91.061 0.691 91.388 0.537 91.604 0.235 c
+91.818 -0.07 91.932 -0.525 91.943 -1.132 c
+h
+91.296 -1.19 m
+91.296 -0.75 91.226 -0.426 91.09 -0.22 c
+90.951 -0.008 90.73 0.103 90.429 0.103 c
+90.112 0.103 89.874 -0.051 89.708 -0.353 c
+89.708 -2.425 l
+89.874 -2.73 90.112 -2.881 90.429 -2.881 c
+90.723 -2.881 90.936 -2.778 91.076 -2.572 c
+91.211 -2.359 91.285 -2.028 91.296 -1.587 c
+h
+94.721 -2.352 m
+94.721 -2.204 94.666 -2.084 94.559 -1.984 c
+94.449 -1.889 94.243 -1.771 93.941 -1.631 c
+93.596 -1.484 93.354 -1.363 93.207 -1.263 c
+93.06 -1.157 92.95 -1.04 92.884 -0.911 c
+92.813 -0.786 92.78 -0.628 92.78 -0.44 c
+92.78 -0.118 92.898 0.151 93.133 0.368 c
+93.368 0.58 93.669 0.691 94.045 0.691 c
+94.427 0.691 94.735 0.578 94.97 0.353 c
+95.206 0.125 95.323 -0.162 95.323 -0.515 c
+94.677 -0.515 l
+94.677 -0.338 94.618 -0.187 94.5 -0.058 c
+94.383 0.067 94.228 0.133 94.045 0.133 c
+93.846 0.133 93.696 0.077 93.589 -0.029 c
+93.478 -0.128 93.428 -0.261 93.428 -0.426 c
+93.428 -0.555 93.464 -0.661 93.545 -0.75 c
+93.622 -0.831 93.813 -0.933 94.118 -1.058 c
+94.596 -1.246 94.926 -1.433 95.103 -1.616 c
+95.279 -1.793 95.367 -2.021 95.367 -2.293 c
+95.367 -2.645 95.242 -2.925 95.001 -3.131 c
+94.765 -3.337 94.449 -3.439 94.06 -3.439 c
+93.637 -3.439 93.299 -3.322 93.045 -3.087 c
+92.788 -2.844 92.663 -2.539 92.663 -2.175 c
+93.31 -2.175 l
+93.317 -2.403 93.387 -2.579 93.515 -2.705 c
+93.64 -2.822 93.825 -2.881 94.06 -2.881 c
+94.272 -2.881 94.434 -2.833 94.544 -2.734 c
+94.662 -2.638 94.721 -2.51 94.721 -2.352 c
+97.058 1.573 m
+97.058 0.618 l
+97.661 0.618 l
+97.661 0.088 l
+97.058 0.088 l
+97.058 -2.381 l
+97.058 -2.539 97.08 -2.657 97.131 -2.734 c
+97.191 -2.815 97.278 -2.851 97.396 -2.851 c
+97.484 -2.851 97.573 -2.836 97.661 -2.807 c
+97.661 -3.366 l
+97.513 -3.414 97.359 -3.439 97.205 -3.439 c
+96.948 -3.439 96.753 -3.347 96.617 -3.16 c
+96.478 -2.976 96.412 -2.715 96.412 -2.381 c
+96.412 0.088 l
+95.809 0.088 l
+95.809 0.618 l
+96.412 0.618 l
+96.412 1.573 l
+h
+100.071 0 m
+99.983 0.019 99.884 0.03 99.778 0.03 c
+99.443 0.03 99.208 -0.154 99.072 -0.515 c
+99.072 -3.366 l
+98.425 -3.366 l
+98.425 0.618 l
+99.057 0.618 l
+99.072 0.206 l
+99.248 0.53 99.491 0.691 99.807 0.691 c
+99.913 0.691 100.002 0.669 100.071 0.632 c
+h
+102.07 -3.439 m
+101.571 -3.439 101.189 -3.293 100.923 -2.998 c
+100.659 -2.705 100.526 -2.271 100.526 -1.691 c
+100.526 -1.22 l
+100.526 -0.625 100.652 -0.158 100.909 0.177 c
+101.174 0.518 101.534 0.691 101.997 0.691 c
+102.456 0.691 102.798 0.537 103.026 0.235 c
+103.261 -0.058 103.383 -0.521 103.393 -1.146 c
+103.393 -1.573 l
+101.174 -1.573 l
+101.174 -1.66 l
+101.174 -2.094 101.251 -2.406 101.409 -2.601 c
+101.574 -2.789 101.806 -2.881 102.099 -2.881 c
+102.294 -2.881 102.467 -2.848 102.614 -2.778 c
+102.762 -2.701 102.897 -2.583 103.026 -2.425 c
+103.364 -2.836 l
+103.077 -3.241 102.647 -3.439 102.07 -3.439 c
+101.997 0.133 m
+101.721 0.133 101.519 0.037 101.394 -0.147 c
+101.266 -0.334 101.193 -0.625 101.174 -1.014 c
+102.747 -1.014 l
+102.747 -0.926 l
+102.724 -0.544 102.658 -0.276 102.541 -0.118 c
+102.423 0.048 102.24 0.133 101.997 0.133 c
+106.215 -3.366 m
+106.175 -3.278 106.149 -3.131 106.142 -2.925 c
+105.907 -3.27 105.613 -3.439 105.26 -3.439 c
+104.896 -3.439 104.613 -3.343 104.408 -3.145 c
+104.209 -2.94 104.114 -2.653 104.114 -2.278 c
+104.114 -1.878 104.25 -1.558 104.525 -1.323 c
+104.797 -1.08 105.172 -0.955 105.642 -0.955 c
+106.128 -0.955 l
+106.128 -0.529 l
+106.128 -0.294 106.072 -0.128 105.966 -0.029 c
+105.856 0.077 105.694 0.133 105.48 0.133 c
+105.282 0.133 105.12 0.073 104.995 -0.044 c
+104.878 -0.162 104.819 -0.309 104.819 -0.484 c
+104.173 -0.484 l
+104.173 -0.29 104.231 -0.099 104.349 0.088 c
+104.474 0.272 104.636 0.42 104.834 0.53 c
+105.039 0.636 105.268 0.691 105.525 0.691 c
+105.925 0.691 106.23 0.588 106.436 0.383 c
+106.649 0.177 106.763 -0.118 106.774 -0.5 c
+106.774 -2.514 l
+106.774 -2.818 106.811 -3.083 106.892 -3.307 c
+106.892 -3.366 l
+h
+105.349 -2.851 m
+105.513 -2.851 105.664 -2.807 105.804 -2.719 c
+105.951 -2.631 106.057 -2.52 106.128 -2.381 c
+106.128 -1.44 l
+105.76 -1.44 l
+105.444 -1.44 105.201 -1.51 105.025 -1.646 c
+104.848 -1.774 104.76 -1.962 104.76 -2.204 c
+104.76 -2.433 104.804 -2.597 104.892 -2.705 c
+104.981 -2.803 105.131 -2.851 105.349 -2.851 c
+108.391 0.618 m
+108.406 0.25 l
+108.648 0.544 108.968 0.691 109.361 0.691 c
+109.802 0.691 110.111 0.493 110.288 0.103 c
+110.541 0.493 110.89 0.691 111.331 0.691 c
+112.066 0.691 112.441 0.228 112.463 -0.69 c
+112.463 -3.366 l
+111.815 -3.366 l
+111.815 -0.75 l
+111.815 -0.455 111.761 -0.243 111.654 -0.103 c
+111.555 0.033 111.383 0.103 111.14 0.103 c
+110.941 0.103 110.78 0.023 110.654 -0.132 c
+110.537 -0.279 110.467 -0.47 110.448 -0.706 c
+110.448 -3.366 l
+109.787 -3.366 l
+109.787 -0.72 l
+109.787 -0.172 109.567 0.103 109.126 0.103 c
+108.792 0.103 108.557 -0.058 108.42 -0.382 c
+108.42 -3.366 l
+107.774 -3.366 l
+107.774 0.618 l
+h
+113.477 -3.013 m
+113.477 -2.896 113.51 -2.8 113.58 -2.719 c
+113.646 -2.642 113.749 -2.601 113.889 -2.601 c
+114.035 -2.601 114.142 -2.642 114.211 -2.719 c
+114.289 -2.8 114.329 -2.896 114.329 -3.013 c
+114.329 -3.123 114.289 -3.215 114.211 -3.293 c
+114.142 -3.37 114.035 -3.41 113.889 -3.41 c
+113.749 -3.41 113.646 -3.37 113.58 -3.293 c
+113.51 -3.215 113.477 -3.123 113.477 -3.013 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+535.667 501.669 238.665 -21.46 re
+f
+0 0 0 0 k
+q 1 0 0 1 597.5209 489.2955 cm
+0 0 m
+-0.052 -1.069 -0.353 -1.878 -0.912 -2.425 c
+-1.463 -2.977 -2.234 -3.248 -3.234 -3.248 c
+-4.303 -3.248 -5.123 -2.903 -5.689 -2.204 c
+-6.247 -1.51 -6.527 -0.515 -6.527 0.779 c
+-6.527 2.352 l
+-6.527 3.645 -6.24 4.638 -5.659 5.336 c
+-5.071 6.03 -4.26 6.38 -3.219 6.38 c
+-2.201 6.38 -1.426 6.089 -0.896 5.513 c
+-0.368 4.943 -0.067 4.123 0.014 3.057 c
+-1.881 3.057 l
+-1.904 3.723 -2.007 4.179 -2.19 4.424 c
+-2.378 4.678 -2.72 4.807 -3.219 4.807 c
+-3.719 4.807 -4.079 4.63 -4.293 4.278 c
+-4.499 3.925 -4.612 3.341 -4.63 2.529 c
+-4.63 0.765 l
+-4.63 -0.168 -4.528 -0.808 -4.322 -1.161 c
+-4.108 -1.506 -3.745 -1.675 -3.234 -1.675 c
+-2.745 -1.675 -2.407 -1.558 -2.22 -1.323 c
+-2.025 -1.08 -1.918 -0.639 -1.896 0 c
+h
+1.433 -2.19 m
+1.433 -1.896 1.529 -1.657 1.727 -1.469 c
+1.922 -1.286 2.175 -1.19 2.491 -1.19 c
+2.792 -1.19 3.042 -1.286 3.241 -1.469 c
+3.447 -1.657 3.549 -1.896 3.549 -2.19 c
+3.549 -2.495 3.447 -2.741 3.241 -2.925 c
+3.042 -3.102 2.792 -3.189 2.491 -3.189 c
+2.186 -3.189 1.932 -3.098 1.727 -2.91 c
+1.529 -2.726 1.433 -2.484 1.433 -2.19 c
+14.115 4.675 m
+11.792 4.675 l
+11.792 -3.116 l
+9.896 -3.116 l
+9.896 4.675 l
+7.617 4.675 l
+7.617 6.247 l
+14.115 6.247 l
+h
+21.967 -3.116 m
+20.086 -3.116 l
+20.086 0.897 l
+17.294 0.897 l
+17.294 -3.116 l
+15.397 -3.116 l
+15.397 6.247 l
+17.294 6.247 l
+17.294 2.455 l
+20.086 2.455 l
+20.086 6.247 l
+21.967 6.247 l
+h
+28.542 0.941 m
+25.602 0.941 l
+25.602 -1.543 l
+29.086 -1.543 l
+29.086 -3.116 l
+23.706 -3.116 l
+23.706 6.247 l
+29.071 6.247 l
+29.071 4.675 l
+25.602 4.675 l
+25.602 2.455 l
+28.542 2.455 l
+h
+35.44 -1.543 m
+39.29 -1.543 l
+39.29 -3.116 l
+33.219 -3.116 l
+33.219 -1.984 l
+37.042 4.675 l
+33.19 4.675 l
+33.19 6.247 l
+39.217 6.247 l
+39.217 5.13 l
+h
+47.107 0.721 m
+47.107 -0.536 46.806 -1.51 46.21 -2.204 c
+45.611 -2.903 44.788 -3.248 43.741 -3.248 c
+42.69 -3.248 41.863 -2.907 41.256 -2.219 c
+40.657 -1.525 40.352 -0.558 40.345 0.676 c
+40.345 2.279 l
+40.345 3.561 40.643 4.564 41.242 5.292 c
+41.837 6.016 42.667 6.38 43.726 6.38 c
+44.762 6.38 45.582 6.02 46.181 5.307 c
+46.787 4.601 47.095 3.605 47.107 2.323 c
+h
+45.21 2.294 m
+45.21 3.135 45.086 3.763 44.843 4.175 c
+44.608 4.586 44.233 4.792 43.726 4.792 c
+43.226 4.792 42.852 4.59 42.609 4.189 c
+42.374 3.796 42.249 3.198 42.241 2.396 c
+42.241 0.721 l
+42.241 -0.095 42.362 -0.698 42.609 -1.087 c
+42.852 -1.481 43.23 -1.675 43.741 -1.675 c
+44.229 -1.675 44.593 -1.484 44.828 -1.103 c
+45.071 -0.72 45.2 -0.132 45.21 0.661 c
+h
+55.283 0.721 m
+55.283 -0.536 54.982 -1.51 54.386 -2.204 c
+53.788 -2.903 52.965 -3.248 51.917 -3.248 c
+50.866 -3.248 50.039 -2.907 49.433 -2.219 c
+48.834 -1.525 48.529 -0.558 48.521 0.676 c
+48.521 2.279 l
+48.521 3.561 48.819 4.564 49.418 5.292 c
+50.013 6.016 50.844 6.38 51.902 6.38 c
+52.938 6.38 53.758 6.02 54.357 5.307 c
+54.964 4.601 55.272 3.605 55.283 2.323 c
+h
+53.387 2.294 m
+53.387 3.135 53.262 3.763 53.019 4.175 c
+52.784 4.586 52.41 4.792 51.902 4.792 c
+51.402 4.792 51.028 4.59 50.785 4.189 c
+50.55 3.796 50.425 3.198 50.418 2.396 c
+50.418 0.721 l
+50.418 -0.095 50.538 -0.698 50.785 -1.087 c
+51.028 -1.481 51.406 -1.675 51.917 -1.675 c
+52.406 -1.675 52.77 -1.484 53.005 -1.103 c
+53.247 -0.72 53.376 -0.132 53.387 0.661 c
+h
+66.579 0.721 m
+66.579 -0.536 66.278 -1.51 65.682 -2.204 c
+65.083 -2.903 64.26 -3.248 63.213 -3.248 c
+62.162 -3.248 61.335 -2.907 60.729 -2.219 c
+60.13 -1.525 59.825 -0.558 59.818 0.676 c
+59.818 2.279 l
+59.818 3.561 60.115 4.564 60.714 5.292 c
+61.31 6.016 62.14 6.38 63.198 6.38 c
+64.235 6.38 65.054 6.02 65.653 5.307 c
+66.259 4.601 66.568 3.605 66.579 2.323 c
+h
+64.683 2.294 m
+64.683 3.135 64.558 3.763 64.316 4.175 c
+64.08 4.586 63.705 4.792 63.198 4.792 c
+62.698 4.792 62.324 4.59 62.081 4.189 c
+61.846 3.796 61.721 3.198 61.714 2.396 c
+61.714 0.721 l
+61.714 -0.095 61.835 -0.698 62.081 -1.087 c
+62.324 -1.481 62.702 -1.675 63.213 -1.675 c
+63.702 -1.675 64.065 -1.484 64.301 -1.103 c
+64.543 -0.72 64.672 -0.132 64.683 0.661 c
+h
+72.992 0.706 m
+70.052 0.706 l
+70.052 -3.116 l
+68.156 -3.116 l
+68.156 6.247 l
+73.345 6.247 l
+73.345 4.675 l
+70.052 4.675 l
+70.052 2.279 l
+72.992 2.279 l
+h
+83.817 0.368 m
+84.7 6.247 l
+86.581 6.247 l
+84.906 -3.116 l
+83.009 -3.116 l
+81.922 2.396 l
+80.848 -3.116 l
+78.938 -3.116 l
+77.262 6.247 l
+79.143 6.247 l
+80.025 0.368 l
+81.128 6.247 l
+82.716 6.247 l
+h
+94.493 0.721 m
+94.493 -0.536 94.192 -1.51 93.596 -2.204 c
+92.997 -2.903 92.174 -3.248 91.127 -3.248 c
+90.076 -3.248 89.249 -2.907 88.642 -2.219 c
+88.044 -1.525 87.738 -0.558 87.732 0.676 c
+87.732 2.279 l
+87.732 3.561 88.029 4.564 88.628 5.292 c
+89.224 6.016 90.053 6.38 91.112 6.38 c
+92.148 6.38 92.968 6.02 93.567 5.307 c
+94.173 4.601 94.481 3.605 94.493 2.323 c
+h
+92.596 2.294 m
+92.596 3.135 92.472 3.763 92.229 4.175 c
+91.994 4.586 91.619 4.792 91.112 4.792 c
+90.612 4.792 90.238 4.59 89.995 4.189 c
+89.76 3.796 89.635 3.198 89.627 2.396 c
+89.627 0.721 l
+89.627 -0.095 89.748 -0.698 89.995 -1.087 c
+90.238 -1.481 90.616 -1.675 91.127 -1.675 c
+91.615 -1.675 91.979 -1.484 92.214 -1.103 c
+92.457 -0.72 92.586 -0.132 92.596 0.661 c
+h
+98.906 0.309 m
+97.965 0.309 l
+97.965 -3.116 l
+96.083 -3.116 l
+96.083 6.247 l
+99.098 6.247 l
+100.045 6.247 100.777 6.001 101.288 5.513 c
+101.805 5.02 102.067 4.326 102.067 3.425 c
+102.067 2.18 101.614 1.309 100.714 0.809 c
+102.346 -3.027 l
+102.346 -3.116 l
+100.317 -3.116 l
+h
+97.965 1.881 m
+99.038 1.881 l
+99.42 1.881 99.703 2.003 99.891 2.249 c
+100.075 2.502 100.17 2.841 100.17 3.263 c
+100.17 4.204 99.807 4.675 99.083 4.675 c
+97.965 4.675 l
+h
+106.23 0.5 m
+105.48 -0.44 l
+105.48 -3.116 l
+103.584 -3.116 l
+103.584 6.247 l
+105.48 6.247 l
+105.48 2.161 l
+106.083 3.175 l
+107.817 6.247 l
+110.14 6.247 l
+107.449 2.132 l
+110.184 -3.116 l
+107.935 -3.116 l
+h
+113.054 -3.116 -1.896 9.363 re
+121.304 -3.116 m
+119.407 -3.116 l
+116.644 3.028 l
+116.644 -3.116 l
+114.748 -3.116 l
+114.748 6.247 l
+116.644 6.247 l
+119.407 0.103 l
+119.407 6.247 l
+121.304 6.247 l
+h
+129.421 -2.057 m
+129.057 -2.451 128.613 -2.749 128.084 -2.954 c
+127.555 -3.149 126.974 -3.248 126.349 -3.248 c
+125.269 -3.248 124.431 -2.917 123.835 -2.248 c
+123.237 -1.583 122.932 -0.613 122.925 0.661 c
+122.925 2.352 l
+122.925 3.645 123.204 4.638 123.762 5.336 c
+124.328 6.03 125.151 6.38 126.231 6.38 c
+127.249 6.38 128.014 6.122 128.525 5.615 c
+129.043 5.116 129.34 4.329 129.421 3.263 c
+127.584 3.263 l
+127.532 3.859 127.411 4.266 127.216 4.484 c
+127.018 4.696 126.709 4.807 126.291 4.807 c
+125.78 4.807 125.408 4.619 125.173 4.248 c
+124.945 3.873 124.828 3.282 124.82 2.469 c
+124.82 0.765 l
+124.82 -0.087 124.945 -0.713 125.203 -1.103 c
+125.456 -1.484 125.872 -1.675 126.452 -1.675 c
+126.823 -1.675 127.128 -1.602 127.363 -1.455 c
+127.525 -1.338 l
+127.525 0.383 l
+126.202 0.383 l
+126.202 1.808 l
+129.421 1.808 l
+h
+138.586 -1.205 m
+136.014 -1.205 l
+135.515 -3.116 l
+133.515 -3.116 l
+136.44 6.247 l
+138.16 6.247 l
+141.115 -3.116 l
+139.086 -3.116 l
+h
+136.425 0.383 m
+138.175 0.383 l
+137.292 3.719 l
+h
+145.05 0.309 m
+144.109 0.309 l
+144.109 -3.116 l
+142.228 -3.116 l
+142.228 6.247 l
+145.241 6.247 l
+146.189 6.247 146.921 6.001 147.431 5.513 c
+147.949 5.02 148.211 4.326 148.211 3.425 c
+148.211 2.18 147.758 1.309 146.858 0.809 c
+148.49 -3.027 l
+148.49 -3.116 l
+146.461 -3.116 l
+h
+144.109 1.881 m
+145.182 1.881 l
+145.564 1.881 145.848 2.003 146.035 2.249 c
+146.218 2.502 146.314 2.841 146.314 3.263 c
+146.314 4.204 145.95 4.675 145.227 4.675 c
+144.109 4.675 l
+h
+154.564 0.941 m
+151.625 0.941 l
+151.625 -1.543 l
+155.108 -1.543 l
+155.108 -3.116 l
+149.728 -3.116 l
+149.728 6.247 l
+155.093 6.247 l
+155.093 4.675 l
+151.625 4.675 l
+151.625 2.455 l
+154.564 2.455 l
+h
+160.836 -1.205 m
+158.264 -1.205 l
+157.765 -3.116 l
+155.766 -3.116 l
+158.691 6.247 l
+160.41 6.247 l
+163.365 -3.116 l
+161.337 -3.116 l
+h
+158.676 0.383 m
+160.425 0.383 l
+159.544 3.719 l
+h
+168.418 -0.661 m
+168.418 -0.279 168.319 0.008 168.124 0.206 c
+167.926 0.401 167.573 0.603 167.066 0.809 c
+166.125 1.168 165.449 1.588 165.037 2.058 c
+164.626 2.536 164.42 3.105 164.42 3.763 c
+164.42 4.546 164.699 5.178 165.258 5.66 c
+165.816 6.137 166.525 6.38 167.389 6.38 c
+167.966 6.38 168.481 6.255 168.932 6.012 c
+169.381 5.766 169.726 5.424 169.962 4.983 c
+170.204 4.542 170.328 4.042 170.328 3.484 c
+168.447 3.484 l
+168.447 3.925 168.352 4.256 168.168 4.484 c
+167.98 4.707 167.712 4.821 167.359 4.821 c
+167.025 4.821 166.764 4.723 166.581 4.528 c
+166.404 4.341 166.316 4.079 166.316 3.749 c
+166.316 3.491 166.419 3.256 166.625 3.043 c
+166.831 2.837 167.19 2.62 167.712 2.396 c
+168.624 2.072 169.285 1.669 169.696 1.191 c
+170.116 0.709 170.328 0.096 170.328 -0.646 c
+170.328 -1.462 170.068 -2.098 169.549 -2.557 c
+169.028 -3.021 168.323 -3.248 167.433 -3.248 c
+166.823 -3.248 166.272 -3.123 165.772 -2.881 c
+165.279 -2.627 164.894 -2.271 164.611 -1.808 c
+164.325 -1.338 164.184 -0.79 164.184 -0.162 c
+166.081 -0.162 l
+166.081 -0.702 166.183 -1.095 166.39 -1.338 c
+166.602 -1.583 166.951 -1.705 167.433 -1.705 c
+168.088 -1.705 168.418 -1.359 168.418 -0.661 c
+f
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+/GS2 gs
+0 TL/Fm1 Do
+Q
+q 1 0 0 1 597.8069 284.3244 cm
+0 0 m
+0 0.107 -0.055 0.206 -0.161 0.294 c
+-0.272 0.39 -0.507 0.518 -0.866 0.676 c
+-1.407 0.89 -1.774 1.118 -1.969 1.353 c
+-2.167 1.588 -2.263 1.875 -2.263 2.22 c
+-2.263 2.661 -2.109 3.018 -1.793 3.293 c
+-1.481 3.576 -1.069 3.72 -0.558 3.72 c
+-0.01 3.72 0.427 3.576 0.75 3.293 c
+1.081 3.018 1.25 2.639 1.25 2.161 c
+-0.058 2.161 l
+-0.058 2.573 -0.228 2.779 -0.558 2.779 c
+-0.698 2.779 -0.808 2.735 -0.896 2.646 c
+-0.984 2.558 -1.028 2.44 -1.028 2.294 c
+-1.028 2.176 -0.981 2.073 -0.881 1.985 c
+-0.786 1.904 -0.554 1.786 -0.191 1.632 c
+0.339 1.434 0.709 1.214 0.927 0.971 c
+1.151 0.736 1.264 0.427 1.264 0.044 c
+1.264 -0.396 1.095 -0.756 0.765 -1.028 c
+0.43 -1.294 -0.01 -1.425 -0.558 -1.425 c
+-0.922 -1.425 -1.246 -1.356 -1.529 -1.219 c
+-1.804 -1.072 -2.021 -0.874 -2.175 -0.617 c
+-2.333 -0.363 -2.41 -0.087 -2.41 0.206 c
+-1.176 0.206 l
+-1.168 -0.022 -1.113 -0.191 -1.014 -0.309 c
+-0.918 -0.426 -0.756 -0.484 -0.529 -0.484 c
+-0.176 -0.484 0 -0.323 0 0 c
+3.421 4.851 m
+3.421 3.631 l
+4.083 3.631 l
+4.083 2.661 l
+3.421 2.661 l
+3.421 0.192 l
+3.421 -0.007 3.444 -0.143 3.495 -0.22 c
+3.554 -0.29 3.66 -0.323 3.818 -0.323 c
+3.936 -0.323 4.039 -0.315 4.127 -0.293 c
+4.113 -1.308 l
+3.884 -1.385 3.645 -1.425 3.392 -1.425 c
+2.558 -1.425 2.135 -0.947 2.128 0.015 c
+2.128 2.661 l
+1.555 2.661 l
+1.555 3.631 l
+2.128 3.631 l
+2.128 4.851 l
+h
+7.041 -1.337 m
+7.001 -1.26 6.961 -1.132 6.924 -0.955 c
+6.696 -1.271 6.384 -1.425 5.983 -1.425 c
+5.561 -1.425 5.212 -1.29 4.939 -1.014 c
+4.675 -0.742 4.543 -0.382 4.543 0.059 c
+4.543 0.578 4.704 0.978 5.027 1.264 c
+5.358 1.548 5.839 1.694 6.468 1.706 c
+6.865 1.706 l
+6.865 2.103 l
+6.865 2.326 6.824 2.484 6.747 2.573 c
+6.666 2.661 6.556 2.705 6.409 2.705 c
+6.086 2.705 5.924 2.517 5.924 2.147 c
+4.63 2.147 l
+4.63 2.595 4.8 2.97 5.145 3.263 c
+5.486 3.565 5.924 3.72 6.453 3.72 c
+6.99 3.72 7.408 3.576 7.703 3.293 c
+8.004 3.018 8.158 2.617 8.158 2.087 c
+8.158 -0.249 l
+8.166 -0.683 8.232 -1.021 8.349 -1.263 c
+8.349 -1.337 l
+h
+6.277 -0.411 m
+6.413 -0.411 6.531 -0.382 6.629 -0.323 c
+6.737 -0.264 6.814 -0.198 6.865 -0.118 c
+6.865 0.912 l
+6.556 0.912 l
+6.328 0.912 6.152 0.842 6.027 0.706 c
+5.898 0.566 5.836 0.383 5.836 0.148 c
+5.836 -0.228 5.983 -0.411 6.277 -0.411 c
+11.131 0 m
+11.131 0.107 11.076 0.206 10.97 0.294 c
+10.859 0.39 10.624 0.518 10.264 0.676 c
+9.724 0.89 9.357 1.118 9.162 1.353 c
+8.963 1.588 8.867 1.875 8.867 2.22 c
+8.867 2.661 9.022 3.018 9.338 3.293 c
+9.65 3.576 10.062 3.72 10.573 3.72 c
+11.12 3.72 11.558 3.576 11.881 3.293 c
+12.212 3.018 12.381 2.639 12.381 2.161 c
+11.073 2.161 l
+11.073 2.573 10.903 2.779 10.573 2.779 c
+10.433 2.779 10.323 2.735 10.234 2.646 c
+10.147 2.558 10.103 2.44 10.103 2.294 c
+10.103 2.176 10.151 2.073 10.249 1.985 c
+10.345 1.904 10.577 1.786 10.94 1.632 c
+11.47 1.434 11.84 1.214 12.057 0.971 c
+12.281 0.736 12.395 0.427 12.395 0.044 c
+12.395 -0.396 12.227 -0.756 11.896 -1.028 c
+11.562 -1.294 11.12 -1.425 10.573 -1.425 c
+10.209 -1.425 9.885 -1.356 9.602 -1.219 c
+9.326 -1.072 9.11 -0.874 8.956 -0.617 c
+8.798 -0.363 8.721 -0.087 8.721 0.206 c
+9.955 0.206 l
+9.962 -0.022 10.018 -0.191 10.117 -0.309 c
+10.213 -0.426 10.375 -0.484 10.602 -0.484 c
+10.955 -0.484 11.131 -0.323 11.131 0 c
+14.299 3.19 m
+14.581 3.543 14.931 3.72 15.342 3.72 c
+15.791 3.72 16.137 3.558 16.372 3.234 c
+16.607 2.918 16.728 2.448 16.739 1.823 c
+16.739 -1.337 l
+15.431 -1.337 l
+15.431 1.808 l
+15.431 2.103 15.387 2.312 15.298 2.44 c
+15.21 2.565 15.063 2.631 14.857 2.631 c
+14.622 2.631 14.435 2.521 14.299 2.309 c
+14.299 -1.337 l
+13.006 -1.337 l
+13.006 5.719 l
+14.299 5.719 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 708.0578 323.0349 cm
+0 0 m
+47.933 0 l
+50.538 0 52.637 -2.099 52.637 -4.704 c
+52.637 -19.264 l
+52.637 -21.868 50.538 -23.963 47.933 -23.963 c
+0 -23.963 l
+-2.606 -23.963 -4.7 -21.868 -4.7 -19.264 c
+-4.7 -4.704 l
+-4.7 -2.099 -2.606 0 0 0 c
+f
+Q
+0 0 0 0 k
+723.657 314.105 -1.309 4.968 re
+722.29 320.367 m
+722.29 320.562 722.352 320.723 722.481 320.852 c
+722.606 320.988 722.779 321.057 722.996 321.057 c
+723.208 321.057 723.381 320.988 723.51 320.852 c
+723.635 320.723 723.701 320.562 723.701 320.367 c
+723.701 320.161 723.635 319.989 723.51 319.852 c
+723.393 319.723 723.22 319.661 722.996 319.661 c
+722.779 319.661 722.606 319.723 722.481 319.852 c
+722.352 319.989 722.29 320.161 722.29 320.367 c
+725.759 319.073 m
+725.803 318.588 l
+726.097 318.971 726.479 319.162 726.95 319.162 c
+727.802 319.162 728.243 318.563 728.272 317.368 c
+728.272 314.105 l
+726.964 314.105 l
+726.964 317.28 l
+726.964 317.552 726.921 317.754 726.832 317.882 c
+726.751 318.008 726.604 318.073 726.391 318.073 c
+726.156 318.073 725.972 317.956 725.847 317.721 c
+725.847 314.105 l
+724.539 314.105 l
+724.539 319.073 l
+h
+728.835 316.737 m
+728.835 317.56 728.97 318.166 729.246 318.559 c
+729.529 318.96 729.93 319.162 730.451 319.162 c
+730.834 319.162 731.15 319 731.407 318.676 c
+731.407 321.161 l
+732.716 321.161 l
+732.716 314.105 l
+731.54 314.105 l
+731.48 314.605 l
+731.216 314.212 730.871 314.017 730.451 314.017 c
+729.93 314.017 729.533 314.212 729.261 314.605 c
+728.985 315.006 728.842 315.594 728.835 316.369 c
+h
+730.143 316.413 m
+730.143 315.92 730.191 315.579 730.29 315.384 c
+730.397 315.197 730.576 315.104 730.834 315.104 c
+731.087 315.104 731.278 315.214 731.407 315.443 c
+731.407 317.707 l
+731.278 317.949 731.087 318.073 730.834 318.073 c
+730.599 318.073 730.422 317.975 730.304 317.78 c
+730.194 317.593 730.143 317.254 730.143 316.766 c
+h
+735.505 314.017 m
+734.836 314.017 734.314 314.212 733.932 314.605 c
+733.557 315.006 733.373 315.579 733.373 316.325 c
+733.373 316.722 l
+733.373 317.504 733.542 318.107 733.888 318.53 c
+734.229 318.948 734.725 319.162 735.372 319.162 c
+735.989 319.162 736.452 318.96 736.768 318.559 c
+737.092 318.166 737.254 317.578 737.254 316.795 c
+737.254 316.149 l
+734.682 316.149 l
+734.688 315.785 734.769 315.516 734.917 315.34 c
+735.064 315.171 735.284 315.09 735.578 315.09 c
+736.018 315.09 736.379 315.241 736.666 315.546 c
+737.18 314.752 l
+737.022 314.535 736.791 314.358 736.489 314.223 c
+736.184 314.087 735.858 314.017 735.505 314.017 c
+734.682 317.059 m
+735.989 317.059 l
+735.989 317.177 l
+735.978 317.479 735.923 317.707 735.827 317.853 c
+735.74 318.008 735.582 318.088 735.357 318.088 c
+735.129 318.088 734.964 318.004 734.858 317.838 c
+734.759 317.681 734.7 317.42 734.682 317.059 c
+739.473 317.603 m
+740.061 319.073 l
+741.428 319.073 l
+740.296 316.648 l
+741.472 314.105 l
+740.091 314.105 l
+739.488 315.663 l
+738.871 314.105 l
+737.489 314.105 l
+738.665 316.648 l
+737.533 319.073 l
+738.929 319.073 l
+h
+f
+q 1 0 0 1 706.8743 306.8918 cm
+0 0 m
+0 0.683 0.077 1.341 0.235 1.97 c
+0.389 2.605 0.632 3.167 0.956 3.659 c
+1.279 4.16 1.621 4.505 1.984 4.704 c
+2.219 3.983 l
+1.874 3.659 1.602 3.16 1.396 2.484 c
+1.198 1.815 1.095 1.043 1.088 0.162 c
+1.088 -0.118 l
+1.088 -1.04 1.183 -1.849 1.382 -2.543 c
+1.587 -3.238 1.866 -3.755 2.219 -4.102 c
+1.984 -4.807 l
+1.621 -4.619 1.279 -4.281 0.956 -3.792 c
+0.64 -3.3 0.401 -2.749 0.235 -2.132 c
+0.077 -1.515 0 -0.867 0 -0.191 c
+h
+5.045 -1.382 m
+5.045 -1.276 4.991 -1.176 4.883 -1.088 c
+4.773 -0.992 4.538 -0.864 4.178 -0.706 c
+3.638 -0.493 3.27 -0.264 3.075 -0.029 c
+2.878 0.206 2.782 0.492 2.782 0.837 c
+2.782 1.278 2.936 1.635 3.252 1.911 c
+3.564 2.194 3.976 2.337 4.486 2.337 c
+5.035 2.337 5.471 2.194 5.795 1.911 c
+6.126 1.635 6.294 1.257 6.294 0.779 c
+4.987 0.779 l
+4.987 1.19 4.818 1.396 4.486 1.396 c
+4.347 1.396 4.237 1.352 4.149 1.264 c
+4.06 1.176 4.016 1.058 4.016 0.912 c
+4.016 0.794 4.064 0.69 4.164 0.602 c
+4.259 0.521 4.49 0.404 4.854 0.249 c
+5.384 0.051 5.755 -0.169 5.972 -0.412 c
+6.196 -0.647 6.31 -0.956 6.31 -1.338 c
+6.31 -1.779 6.14 -2.139 5.81 -2.411 c
+5.475 -2.675 5.035 -2.808 4.486 -2.808 c
+4.123 -2.808 3.8 -2.738 3.516 -2.602 c
+3.241 -2.455 3.024 -2.257 2.87 -1.999 c
+2.712 -1.746 2.635 -1.47 2.635 -1.176 c
+3.869 -1.176 l
+3.877 -1.404 3.932 -1.573 4.031 -1.691 c
+4.127 -1.808 4.289 -1.867 4.517 -1.867 c
+4.869 -1.867 5.045 -1.706 5.045 -1.382 c
+8.463 3.468 m
+8.463 2.248 l
+9.124 2.248 l
+9.124 1.278 l
+8.463 1.278 l
+8.463 -1.191 l
+8.463 -1.389 8.484 -1.525 8.536 -1.602 c
+8.596 -1.673 8.702 -1.706 8.86 -1.706 c
+8.977 -1.706 9.08 -1.698 9.168 -1.675 c
+9.153 -2.691 l
+8.926 -2.768 8.687 -2.808 8.434 -2.808 c
+7.599 -2.808 7.177 -2.33 7.169 -1.367 c
+7.169 1.278 l
+6.596 1.278 l
+6.596 2.248 l
+7.169 2.248 l
+7.169 3.468 l
+h
+12.087 -2.72 m
+12.046 -2.643 12.006 -2.514 11.969 -2.338 c
+11.74 -2.654 11.428 -2.808 11.028 -2.808 c
+10.605 -2.808 10.256 -2.672 9.984 -2.396 c
+9.72 -2.124 9.587 -1.764 9.587 -1.323 c
+9.587 -0.805 9.749 -0.405 10.073 -0.118 c
+10.404 0.166 10.884 0.312 11.513 0.324 c
+11.91 0.324 l
+11.91 0.72 l
+11.91 0.944 11.869 1.103 11.792 1.19 c
+11.711 1.278 11.601 1.323 11.454 1.323 c
+11.131 1.323 10.969 1.135 10.969 0.764 c
+9.675 0.764 l
+9.675 1.213 9.845 1.587 10.19 1.881 c
+10.532 2.182 10.969 2.337 11.499 2.337 c
+12.035 2.337 12.453 2.194 12.748 1.911 c
+13.049 1.635 13.203 1.234 13.203 0.706 c
+13.203 -1.632 l
+13.211 -2.066 13.277 -2.404 13.394 -2.646 c
+13.394 -2.72 l
+h
+11.322 -1.793 m
+11.458 -1.793 11.576 -1.764 11.675 -1.706 c
+11.781 -1.646 11.858 -1.58 11.91 -1.5 c
+11.91 -0.47 l
+11.601 -0.47 l
+11.374 -0.47 11.197 -0.54 11.072 -0.676 c
+10.944 -0.816 10.881 -1 10.881 -1.235 c
+10.881 -1.61 11.028 -1.793 11.322 -1.793 c
+13.824 -0.088 m
+13.824 0.735 13.972 1.341 14.265 1.735 c
+14.559 2.135 14.971 2.337 15.5 2.337 c
+15.948 2.337 16.297 2.161 16.544 1.808 c
+16.588 2.248 l
+17.764 2.248 l
+17.764 -2.72 l
+17.764 -3.356 17.58 -3.84 17.22 -4.175 c
+16.867 -4.505 16.357 -4.675 15.692 -4.675 c
+15.426 -4.675 15.14 -4.619 14.838 -4.513 c
+14.533 -4.403 14.31 -4.266 14.163 -4.102 c
+14.603 -3.204 l
+14.728 -3.329 14.89 -3.433 15.089 -3.514 c
+15.283 -3.591 15.463 -3.631 15.632 -3.631 c
+15.927 -3.631 16.136 -3.561 16.264 -3.425 c
+16.39 -3.285 16.455 -3.061 16.455 -2.749 c
+16.455 -2.323 l
+16.209 -2.646 15.886 -2.808 15.486 -2.808 c
+14.956 -2.808 14.545 -2.602 14.25 -2.19 c
+13.964 -1.779 13.824 -1.202 13.824 -0.456 c
+h
+15.118 -0.412 m
+15.118 -0.875 15.177 -1.205 15.295 -1.411 c
+15.412 -1.617 15.603 -1.72 15.867 -1.72 c
+16.132 -1.72 16.327 -1.628 16.455 -1.44 c
+16.455 0.941 l
+16.327 1.147 16.136 1.249 15.883 1.249 c
+15.618 1.249 15.42 1.139 15.295 0.926 c
+15.177 0.72 15.118 0.382 15.118 -0.088 c
+h
+19.888 -2.72 -1.309 4.968 re
+18.52 3.542 m
+18.52 3.737 18.583 3.898 18.712 4.027 c
+18.836 4.164 19.01 4.233 19.226 4.233 c
+19.439 4.233 19.613 4.164 19.74 4.027 c
+19.866 3.898 19.931 3.737 19.931 3.542 c
+19.931 3.337 19.866 3.164 19.74 3.028 c
+19.623 2.899 19.451 2.836 19.226 2.836 c
+19.01 2.836 18.836 2.899 18.712 3.028 c
+18.583 3.164 18.52 3.337 18.52 3.542 c
+21.986 2.248 m
+22.03 1.764 l
+22.324 2.146 22.707 2.337 23.177 2.337 c
+24.029 2.337 24.471 1.738 24.5 0.544 c
+24.5 -2.72 l
+23.191 -2.72 l
+23.191 0.455 l
+23.191 0.727 23.147 0.929 23.059 1.058 c
+22.979 1.183 22.831 1.249 22.618 1.249 c
+22.383 1.249 22.2 1.132 22.074 0.897 c
+22.074 -2.72 l
+20.766 -2.72 l
+20.766 2.248 l
+h
+25.095 -0.088 m
+25.095 0.735 25.242 1.341 25.535 1.735 c
+25.83 2.135 26.241 2.337 26.771 2.337 c
+27.219 2.337 27.568 2.161 27.814 1.808 c
+27.858 2.248 l
+29.034 2.248 l
+29.034 -2.72 l
+29.034 -3.356 28.851 -3.84 28.49 -4.175 c
+28.138 -4.505 27.627 -4.675 26.962 -4.675 c
+26.697 -4.675 26.41 -4.619 26.109 -4.513 c
+25.805 -4.403 25.58 -4.266 25.433 -4.102 c
+25.874 -3.204 l
+25.999 -3.329 26.161 -3.433 26.359 -3.514 c
+26.553 -3.591 26.734 -3.631 26.903 -3.631 c
+27.197 -3.631 27.407 -3.561 27.534 -3.425 c
+27.66 -3.285 27.726 -3.061 27.726 -2.749 c
+27.726 -2.323 l
+27.48 -2.646 27.156 -2.808 26.756 -2.808 c
+26.227 -2.808 25.815 -2.602 25.521 -2.19 c
+25.234 -1.779 25.095 -1.202 25.095 -0.456 c
+h
+26.389 -0.412 m
+26.389 -0.875 26.447 -1.205 26.565 -1.411 c
+26.682 -1.617 26.873 -1.72 27.138 -1.72 c
+27.403 -1.72 27.598 -1.628 27.726 -1.44 c
+27.726 0.941 l
+27.598 1.147 27.407 1.249 27.153 1.249 c
+26.888 1.249 26.69 1.139 26.565 0.926 c
+26.447 0.72 26.389 0.382 26.389 -0.088 c
+h
+34.241 -2.72 m
+34.201 -2.643 34.16 -2.514 34.123 -2.338 c
+33.896 -2.654 33.583 -2.808 33.183 -2.808 c
+32.76 -2.808 32.411 -2.672 32.139 -2.396 c
+31.875 -2.124 31.742 -1.764 31.742 -1.323 c
+31.742 -0.805 31.904 -0.405 32.228 -0.118 c
+32.558 0.166 33.04 0.312 33.668 0.324 c
+34.065 0.324 l
+34.065 0.72 l
+34.065 0.944 34.025 1.103 33.948 1.19 c
+33.867 1.278 33.757 1.323 33.609 1.323 c
+33.286 1.323 33.124 1.135 33.124 0.764 c
+31.831 0.764 l
+31.831 1.213 32 1.587 32.345 1.881 c
+32.687 2.182 33.124 2.337 33.653 2.337 c
+34.19 2.337 34.609 2.194 34.903 1.911 c
+35.204 1.635 35.359 1.234 35.359 0.706 c
+35.359 -1.632 l
+35.366 -2.066 35.432 -2.404 35.55 -2.646 c
+35.55 -2.72 l
+h
+33.477 -1.793 m
+33.613 -1.793 33.73 -1.764 33.83 -1.706 c
+33.936 -1.646 34.013 -1.58 34.065 -1.5 c
+34.065 -0.47 l
+33.757 -0.47 l
+33.529 -0.47 33.352 -0.54 33.227 -0.676 c
+33.099 -0.816 33.036 -1 33.036 -1.235 c
+33.036 -1.61 33.183 -1.793 33.477 -1.793 c
+38.582 0.999 m
+38.155 1.029 l
+37.791 1.029 37.552 0.871 37.435 0.559 c
+37.435 -2.72 l
+36.126 -2.72 l
+36.126 2.248 l
+37.346 2.248 l
+37.391 1.72 l
+37.597 2.131 37.88 2.337 38.243 2.337 c
+38.391 2.337 38.508 2.315 38.596 2.278 c
+h
+41.051 -2.808 m
+40.382 -2.808 39.86 -2.613 39.478 -2.22 c
+39.103 -1.819 38.919 -1.246 38.919 -0.5 c
+38.919 -0.104 l
+38.919 0.679 39.089 1.282 39.434 1.705 c
+39.775 2.124 40.272 2.337 40.918 2.337 c
+41.536 2.337 41.998 2.135 42.314 1.735 c
+42.638 1.341 42.8 0.754 42.8 -0.029 c
+42.8 -0.676 l
+40.228 -0.676 l
+40.234 -1.04 40.315 -1.309 40.463 -1.484 c
+40.61 -1.654 40.83 -1.735 41.124 -1.735 c
+41.566 -1.735 41.925 -1.584 42.212 -1.279 c
+42.727 -2.072 l
+42.568 -2.29 42.337 -2.466 42.036 -2.602 c
+41.73 -2.738 41.404 -2.808 41.051 -2.808 c
+40.228 0.235 m
+41.536 0.235 l
+41.536 0.353 l
+41.525 0.654 41.47 0.881 41.374 1.029 c
+41.286 1.183 41.128 1.264 40.903 1.264 c
+40.676 1.264 40.51 1.18 40.404 1.014 c
+40.305 0.856 40.246 0.595 40.228 0.235 c
+45.725 -2.72 m
+45.684 -2.643 45.644 -2.514 45.607 -2.338 c
+45.379 -2.654 45.067 -2.808 44.666 -2.808 c
+44.244 -2.808 43.895 -2.672 43.623 -2.396 c
+43.359 -2.124 43.226 -1.764 43.226 -1.323 c
+43.226 -0.805 43.388 -0.405 43.711 -0.118 c
+44.042 0.166 44.523 0.312 45.152 0.324 c
+45.549 0.324 l
+45.549 0.72 l
+45.549 0.944 45.508 1.103 45.431 1.19 c
+45.35 1.278 45.24 1.323 45.093 1.323 c
+44.77 1.323 44.608 1.135 44.608 0.764 c
+43.315 0.764 l
+43.315 1.213 43.484 1.587 43.829 1.881 c
+44.171 2.182 44.608 2.337 45.137 2.337 c
+45.674 2.337 46.092 2.194 46.387 1.911 c
+46.688 1.635 46.842 1.234 46.842 0.706 c
+46.842 -1.632 l
+46.85 -2.066 46.916 -2.404 47.033 -2.646 c
+47.033 -2.72 l
+h
+44.961 -1.793 m
+45.096 -1.793 45.214 -1.764 45.314 -1.706 c
+45.42 -1.646 45.497 -1.58 45.549 -1.5 c
+45.549 -0.47 l
+45.24 -0.47 l
+45.013 -0.47 44.836 -0.54 44.711 -0.676 c
+44.583 -0.816 44.52 -1 44.52 -1.235 c
+44.52 -1.61 44.666 -1.793 44.961 -1.793 c
+49.694 -0.104 m
+49.694 -0.819 49.599 -1.496 49.414 -2.132 c
+49.238 -2.768 48.988 -3.326 48.664 -3.807 c
+48.342 -4.285 47.996 -4.619 47.636 -4.807 c
+47.401 -4.102 l
+47.743 -3.767 48.011 -3.256 48.209 -2.573 c
+48.415 -1.889 48.521 -1.11 48.533 -0.235 c
+48.533 0.014 l
+48.533 0.904 48.429 1.697 48.224 2.396 c
+48.026 3.102 47.754 3.634 47.401 3.998 c
+47.636 4.704 l
+47.871 4.586 48.11 4.384 48.356 4.101 c
+48.599 3.825 48.819 3.484 49.017 3.071 c
+49.223 2.66 49.385 2.194 49.503 1.675 c
+49.628 1.165 49.694 0.573 49.694 -0.104 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 646.1816 351.8337 cm
+0 0 m
+38.148 0 l
+40.754 0 42.852 -2.098 42.852 -4.704 c
+42.852 -10.231 l
+42.852 -12.835 40.754 -14.934 38.148 -14.934 c
+0 -14.934 l
+-2.605 -14.934 -4.704 -12.835 -4.704 -10.231 c
+-4.704 -4.704 l
+-4.704 -2.098 -2.605 0 0 0 c
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 649.541 345.8732 cm
+0 0 m
+-0.426 0.03 l
+-0.79 0.03 -1.029 -0.128 -1.147 -0.44 c
+-1.147 -3.719 l
+-2.455 -3.719 l
+-2.455 1.249 l
+-1.235 1.249 l
+-1.191 0.721 l
+-0.985 1.132 -0.702 1.338 -0.339 1.338 c
+-0.191 1.338 -0.074 1.316 0.014 1.279 c
+h
+2.469 -3.807 m
+1.801 -3.807 1.279 -3.612 0.897 -3.219 c
+0.521 -2.818 0.338 -2.245 0.338 -1.499 c
+0.338 -1.103 l
+0.338 -0.32 0.507 0.283 0.852 0.706 c
+1.194 1.125 1.69 1.338 2.337 1.338 c
+2.954 1.338 3.417 1.135 3.733 0.736 c
+4.056 0.342 4.218 -0.246 4.218 -1.028 c
+4.218 -1.675 l
+1.646 -1.675 l
+1.653 -2.04 1.734 -2.308 1.881 -2.484 c
+2.028 -2.653 2.248 -2.734 2.543 -2.734 c
+2.983 -2.734 3.343 -2.583 3.63 -2.278 c
+4.145 -3.072 l
+3.987 -3.289 3.755 -3.465 3.453 -3.601 c
+3.149 -3.737 2.822 -3.807 2.469 -3.807 c
+1.646 -0.764 m
+2.954 -0.764 l
+2.954 -0.646 l
+2.944 -0.345 2.888 -0.118 2.792 0.03 c
+2.705 0.184 2.547 0.265 2.322 0.265 c
+2.094 0.265 1.929 0.181 1.822 0.015 c
+1.723 -0.143 1.664 -0.404 1.646 -0.764 c
+8.658 -1.396 m
+8.658 -2.171 8.517 -2.77 8.246 -3.189 c
+7.981 -3.601 7.584 -3.807 7.056 -3.807 c
+6.651 -3.807 6.327 -3.645 6.085 -3.322 c
+6.085 -5.629 l
+4.777 -5.629 l
+4.777 1.249 l
+5.982 1.249 l
+6.026 0.794 l
+6.279 1.154 6.618 1.338 7.041 1.338 c
+7.57 1.338 7.966 1.147 8.232 0.765 c
+8.503 0.383 8.646 -0.213 8.658 -1.014 c
+h
+7.364 -1.058 m
+7.364 -0.58 7.309 -0.243 7.202 -0.044 c
+7.092 0.151 6.912 0.25 6.659 0.25 c
+6.394 0.25 6.202 0.14 6.085 -0.073 c
+6.085 -2.41 l
+6.202 -2.627 6.397 -2.734 6.673 -2.734 c
+6.927 -2.734 7.103 -2.627 7.202 -2.41 c
+7.309 -2.186 7.364 -1.849 7.364 -1.396 c
+h
+9.109 -1.072 m
+9.109 -0.32 9.286 0.268 9.639 0.691 c
+9.992 1.121 10.48 1.338 11.108 1.338 c
+11.744 1.338 12.24 1.121 12.593 0.691 c
+12.953 0.268 13.136 -0.323 13.136 -1.087 c
+13.136 -1.396 l
+13.136 -2.153 12.961 -2.745 12.608 -3.175 c
+12.255 -3.597 11.759 -3.807 11.123 -3.807 c
+10.484 -3.807 9.992 -3.597 9.639 -3.175 c
+9.286 -2.745 9.109 -2.153 9.109 -1.396 c
+h
+10.418 -1.396 m
+10.418 -2.289 10.653 -2.734 11.123 -2.734 c
+11.565 -2.734 11.8 -2.362 11.829 -1.616 c
+11.843 -1.072 l
+11.843 -0.625 11.777 -0.29 11.652 -0.073 c
+11.524 0.151 11.343 0.265 11.108 0.265 c
+10.892 0.265 10.719 0.151 10.594 -0.073 c
+10.476 -0.29 10.418 -0.625 10.418 -1.072 c
+h
+15.959 -2.381 m
+15.959 -2.275 15.904 -2.175 15.798 -2.087 c
+15.687 -1.992 15.452 -1.863 15.092 -1.705 c
+14.551 -1.492 14.184 -1.263 13.99 -1.028 c
+13.791 -0.793 13.695 -0.507 13.695 -0.162 c
+13.695 0.279 13.85 0.636 14.166 0.912 c
+14.478 1.195 14.89 1.338 15.401 1.338 c
+15.948 1.338 16.386 1.195 16.708 0.912 c
+17.04 0.636 17.209 0.258 17.209 -0.22 c
+15.9 -0.22 l
+15.9 0.191 15.731 0.397 15.401 0.397 c
+15.261 0.397 15.151 0.353 15.062 0.265 c
+14.975 0.177 14.931 0.059 14.931 -0.087 c
+14.931 -0.205 14.978 -0.309 15.077 -0.397 c
+15.173 -0.478 15.405 -0.595 15.768 -0.75 c
+16.297 -0.948 16.668 -1.168 16.885 -1.411 c
+17.109 -1.646 17.223 -1.955 17.223 -2.337 c
+17.223 -2.778 17.055 -3.138 16.724 -3.41 c
+16.389 -3.674 15.948 -3.807 15.401 -3.807 c
+15.037 -3.807 14.713 -3.737 14.43 -3.601 c
+14.155 -3.454 13.938 -3.256 13.784 -2.998 c
+13.626 -2.745 13.549 -2.469 13.549 -2.175 c
+14.783 -2.175 l
+14.79 -2.403 14.846 -2.572 14.945 -2.69 c
+15.041 -2.807 15.202 -2.866 15.43 -2.866 c
+15.783 -2.866 15.959 -2.705 15.959 -2.381 c
+19.23 -3.719 -1.309 4.968 re
+17.863 2.543 m
+17.863 2.738 17.925 2.899 18.054 3.028 c
+18.179 3.165 18.351 3.234 18.568 3.234 c
+18.781 3.234 18.954 3.165 19.083 3.028 c
+19.208 2.899 19.274 2.738 19.274 2.543 c
+19.274 2.338 19.208 2.165 19.083 2.029 c
+18.965 1.9 18.792 1.837 18.568 1.837 c
+18.351 1.837 18.179 1.9 18.054 2.029 c
+17.925 2.165 17.863 2.338 17.863 2.543 c
+21.655 2.469 m
+21.655 1.249 l
+22.316 1.249 l
+22.316 0.279 l
+21.655 0.279 l
+21.655 -2.19 l
+21.655 -2.389 21.677 -2.524 21.728 -2.601 c
+21.788 -2.672 21.894 -2.705 22.052 -2.705 c
+22.169 -2.705 22.272 -2.697 22.36 -2.674 c
+22.346 -3.69 l
+22.118 -3.767 21.879 -3.807 21.626 -3.807 c
+20.791 -3.807 20.369 -3.329 20.361 -2.366 c
+20.361 0.279 l
+19.789 0.279 l
+19.789 1.249 l
+20.361 1.249 l
+20.361 2.469 l
+h
+22.64 -1.072 m
+22.64 -0.32 22.817 0.268 23.169 0.691 c
+23.522 1.121 24.01 1.338 24.639 1.338 c
+25.275 1.338 25.771 1.121 26.123 0.691 c
+26.484 0.268 26.667 -0.323 26.667 -1.087 c
+26.667 -1.396 l
+26.667 -2.153 26.491 -2.745 26.138 -3.175 c
+25.786 -3.597 25.289 -3.807 24.654 -3.807 c
+24.014 -3.807 23.522 -3.597 23.169 -3.175 c
+22.817 -2.745 22.64 -2.153 22.64 -1.396 c
+h
+23.948 -1.396 m
+23.948 -2.289 24.184 -2.734 24.654 -2.734 c
+25.094 -2.734 25.329 -2.362 25.36 -1.616 c
+25.374 -1.072 l
+25.374 -0.625 25.308 -0.29 25.183 -0.073 c
+25.054 0.151 24.874 0.265 24.639 0.265 c
+24.423 0.265 24.25 0.151 24.124 -0.073 c
+24.007 -0.29 23.948 -0.625 23.948 -1.072 c
+h
+29.74 0 m
+29.313 0.03 l
+28.949 0.03 28.71 -0.128 28.593 -0.44 c
+28.593 -3.719 l
+27.285 -3.719 l
+27.285 1.249 l
+28.504 1.249 l
+28.549 0.721 l
+28.755 1.132 29.038 1.338 29.402 1.338 c
+29.549 1.338 29.666 1.316 29.755 1.279 c
+h
+31.974 -1.558 m
+32.635 1.249 l
+34.017 1.249 l
+32.385 -4.469 l
+32.139 -5.31 31.679 -5.732 31.004 -5.732 c
+30.846 -5.732 30.665 -5.703 30.46 -5.644 c
+30.46 -4.63 l
+30.621 -4.63 l
+30.816 -4.63 30.964 -4.59 31.062 -4.513 c
+31.169 -4.432 31.253 -4.295 31.313 -4.101 c
+31.415 -3.763 l
+29.975 1.249 l
+31.371 1.249 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 653.8919 273.5134 cm
+0 0 m
+32.606 0 l
+35.211 0 37.306 -2.098 37.306 -4.704 c
+37.306 -19.829 l
+37.306 -22.434 35.211 -24.529 32.606 -24.529 c
+0 -24.529 l
+-2.606 -24.529 -4.704 -22.434 -4.704 -19.829 c
+-4.704 -4.704 l
+-4.704 -2.098 -2.606 0 0 0 c
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 659.4808 265.2527 cm
+0 0 m
+0.455 2.793 l
+1.691 2.793 l
+0.661 -2.175 l
+-0.411 -2.175 l
+-1.117 0.706 l
+-1.822 -2.175 l
+-2.896 -2.175 l
+-3.925 2.793 l
+-2.675 2.793 l
+-2.234 0 l
+-1.573 2.793 l
+-0.661 2.793 l
+h
+1.951 0.47 m
+1.951 1.224 2.128 1.812 2.481 2.234 c
+2.834 2.664 3.322 2.882 3.95 2.882 c
+4.586 2.882 5.082 2.664 5.435 2.234 c
+5.795 1.812 5.978 1.22 5.978 0.455 c
+5.978 0.147 l
+5.978 -0.61 5.803 -1.201 5.45 -1.631 c
+5.097 -2.055 4.601 -2.263 3.965 -2.263 c
+3.326 -2.263 2.834 -2.055 2.481 -1.631 c
+2.128 -1.201 1.951 -0.61 1.951 0.147 c
+h
+3.26 0.147 m
+3.26 -0.746 3.495 -1.191 3.965 -1.191 c
+4.406 -1.191 4.642 -0.819 4.671 -0.073 c
+4.686 0.47 l
+4.686 0.919 4.619 1.253 4.494 1.47 c
+4.366 1.694 4.185 1.808 3.95 1.808 c
+3.734 1.808 3.561 1.694 3.436 1.47 c
+3.318 1.253 3.26 0.919 3.26 0.47 c
+h
+9.051 1.544 m
+8.625 1.573 l
+8.261 1.573 8.022 1.415 7.904 1.103 c
+7.904 -2.175 l
+6.596 -2.175 l
+6.596 2.793 l
+7.817 2.793 l
+7.86 2.263 l
+8.066 2.675 8.349 2.882 8.713 2.882 c
+8.86 2.882 8.978 2.859 9.066 2.822 c
+h
+11.241 -0.264 m
+10.903 -0.646 l
+10.903 -2.175 l
+9.595 -2.175 l
+9.595 4.881 l
+10.903 4.881 l
+10.903 1.073 l
+11.065 1.309 l
+11.962 2.793 l
+13.504 2.793 l
+12.064 0.75 l
+13.652 -2.175 l
+12.153 -2.175 l
+h
+15.283 -2.175 -1.309 4.968 re
+13.916 4.087 m
+13.916 4.281 13.978 4.443 14.107 4.571 c
+14.232 4.707 14.406 4.777 14.622 4.777 c
+14.836 4.777 15.008 4.707 15.137 4.571 c
+15.262 4.443 15.328 4.281 15.328 4.087 c
+15.328 3.881 15.262 3.707 15.137 3.572 c
+15.019 3.443 14.846 3.381 14.622 3.381 c
+14.406 3.381 14.232 3.443 14.107 3.572 c
+13.978 3.707 13.916 3.881 13.916 4.087 c
+17.382 2.793 m
+17.425 2.308 l
+17.72 2.69 18.102 2.882 18.572 2.882 c
+19.424 2.882 19.866 2.282 19.895 1.088 c
+19.895 -2.175 l
+18.587 -2.175 l
+18.587 1 l
+18.587 1.272 18.543 1.473 18.455 1.602 c
+18.374 1.727 18.227 1.793 18.013 1.793 c
+17.778 1.793 17.595 1.675 17.47 1.44 c
+17.47 -2.175 l
+16.162 -2.175 l
+16.162 2.793 l
+h
+20.49 0.455 m
+20.49 1.278 20.638 1.885 20.931 2.278 c
+21.226 2.679 21.637 2.882 22.167 2.882 c
+22.614 2.882 22.964 2.705 23.21 2.352 c
+23.254 2.793 l
+24.43 2.793 l
+24.43 -2.175 l
+24.43 -2.811 24.246 -3.296 23.886 -3.63 c
+23.534 -3.961 23.023 -4.131 22.358 -4.131 c
+22.092 -4.131 21.806 -4.075 21.504 -3.969 c
+21.2 -3.859 20.976 -3.723 20.829 -3.557 c
+21.269 -2.66 l
+21.394 -2.786 21.556 -2.888 21.755 -2.969 c
+21.949 -3.046 22.129 -3.087 22.298 -3.087 c
+22.593 -3.087 22.802 -3.017 22.931 -2.881 c
+23.056 -2.741 23.122 -2.517 23.122 -2.205 c
+23.122 -1.779 l
+22.875 -2.102 22.552 -2.263 22.152 -2.263 c
+21.622 -2.263 21.211 -2.057 20.916 -1.646 c
+20.63 -1.234 20.49 -0.658 20.49 0.088 c
+h
+21.784 0.133 m
+21.784 -0.331 21.843 -0.661 21.961 -0.867 c
+22.078 -1.073 22.269 -1.176 22.534 -1.176 c
+22.798 -1.176 22.993 -1.084 23.122 -0.897 c
+23.122 1.484 l
+22.993 1.691 22.802 1.793 22.549 1.793 c
+22.284 1.793 22.086 1.683 21.961 1.47 c
+21.843 1.264 21.784 0.926 21.784 0.455 c
+h
+f
+Q
+q 1 0 0 1 653.8806 255.7758 cm
+0 0 m
+0 0.823 0.135 1.429 0.411 1.822 c
+0.694 2.223 1.095 2.425 1.617 2.425 c
+1.999 2.425 2.315 2.263 2.572 1.94 c
+2.572 4.424 l
+3.881 4.424 l
+3.881 -2.631 l
+2.705 -2.631 l
+2.645 -2.132 l
+2.381 -2.525 2.036 -2.72 1.617 -2.72 c
+1.095 -2.72 0.698 -2.525 0.426 -2.132 c
+0.151 -1.731 0.008 -1.143 0 -0.368 c
+h
+1.309 -0.324 m
+1.309 -0.816 1.356 -1.158 1.455 -1.353 c
+1.562 -1.54 1.741 -1.632 1.999 -1.632 c
+2.252 -1.632 2.444 -1.521 2.572 -1.294 c
+2.572 0.97 l
+2.444 1.213 2.252 1.338 1.999 1.338 c
+1.764 1.338 1.587 1.238 1.469 1.043 c
+1.359 0.856 1.309 0.518 1.309 0.029 c
+h
+6.023 -2.631 -1.308 4.968 re
+4.656 3.63 m
+4.656 3.825 4.719 3.987 4.847 4.116 c
+4.972 4.251 5.145 4.322 5.361 4.322 c
+5.575 4.322 5.747 4.251 5.876 4.116 c
+6.001 3.987 6.067 3.825 6.067 3.63 c
+6.067 3.424 6.001 3.252 5.876 3.116 c
+5.758 2.988 5.585 2.925 5.361 2.925 c
+5.145 2.925 4.972 2.988 4.847 3.116 c
+4.719 3.252 4.656 3.424 4.656 3.63 c
+9.385 1.087 m
+8.959 1.117 l
+8.596 1.117 8.357 0.959 8.239 0.646 c
+8.239 -2.631 l
+6.93 -2.631 l
+6.93 2.337 l
+8.151 2.337 l
+8.195 1.808 l
+8.401 2.219 8.683 2.425 9.047 2.425 c
+9.194 2.425 9.311 2.404 9.4 2.366 c
+h
+11.854 -2.72 m
+11.186 -2.72 10.664 -2.525 10.282 -2.132 c
+9.907 -1.731 9.724 -1.158 9.724 -0.412 c
+9.724 -0.015 l
+9.724 0.768 9.893 1.371 10.238 1.793 c
+10.58 2.212 11.075 2.425 11.723 2.425 c
+12.34 2.425 12.802 2.223 13.119 1.822 c
+13.442 1.429 13.604 0.841 13.604 0.058 c
+13.604 -0.588 l
+11.031 -0.588 l
+11.039 -0.952 11.12 -1.22 11.266 -1.397 c
+11.414 -1.565 11.634 -1.646 11.929 -1.646 c
+12.369 -1.646 12.729 -1.496 13.016 -1.191 c
+13.531 -1.985 l
+13.373 -2.201 13.141 -2.378 12.839 -2.514 c
+12.534 -2.65 12.207 -2.72 11.854 -2.72 c
+11.031 0.324 m
+12.34 0.324 l
+12.34 0.441 l
+12.328 0.742 12.274 0.97 12.178 1.117 c
+12.089 1.271 11.932 1.352 11.707 1.352 c
+11.48 1.352 11.314 1.267 11.208 1.103 c
+11.108 0.944 11.05 0.683 11.031 0.324 c
+15.956 -1.646 m
+16.338 -1.646 16.529 -1.404 16.529 -0.912 c
+17.764 -0.912 l
+17.753 -1.452 17.58 -1.889 17.249 -2.22 c
+16.926 -2.554 16.503 -2.72 15.985 -2.72 c
+15.345 -2.72 14.857 -2.525 14.516 -2.132 c
+14.181 -1.731 14.015 -1.143 14.015 -0.368 c
+14.015 0.029 l
+14.015 0.812 14.177 1.404 14.501 1.808 c
+14.832 2.219 15.327 2.425 15.985 2.425 c
+16.533 2.425 16.962 2.252 17.279 1.911 c
+17.591 1.577 17.753 1.103 17.764 0.484 c
+16.529 0.484 l
+16.529 0.757 16.485 0.97 16.397 1.117 c
+16.309 1.271 16.162 1.352 15.956 1.352 c
+15.728 1.352 15.566 1.267 15.47 1.103 c
+15.372 0.944 15.316 0.636 15.309 0.176 c
+15.309 -0.324 l
+15.309 -0.735 15.327 -1.022 15.368 -1.176 c
+15.405 -1.334 15.47 -1.452 15.559 -1.529 c
+15.655 -1.61 15.786 -1.646 15.956 -1.646 c
+19.84 3.557 m
+19.84 2.337 l
+20.502 2.337 l
+20.502 1.367 l
+19.84 1.367 l
+19.84 -1.103 l
+19.84 -1.301 19.862 -1.437 19.914 -1.515 c
+19.972 -1.584 20.079 -1.617 20.237 -1.617 c
+20.355 -1.617 20.457 -1.61 20.546 -1.588 c
+20.531 -2.602 l
+20.303 -2.679 20.064 -2.72 19.81 -2.72 c
+18.977 -2.72 18.554 -2.242 18.547 -1.279 c
+18.547 1.367 l
+17.973 1.367 l
+17.973 2.337 l
+18.547 2.337 l
+18.547 3.557 l
+h
+20.825 0.014 m
+20.825 0.768 21.001 1.356 21.354 1.778 c
+21.707 2.208 22.196 2.425 22.824 2.425 c
+23.459 2.425 23.956 2.208 24.309 1.778 c
+24.669 1.356 24.852 0.764 24.852 0 c
+24.852 -0.309 l
+24.852 -1.066 24.676 -1.658 24.323 -2.088 c
+23.97 -2.51 23.474 -2.72 22.838 -2.72 c
+22.2 -2.72 21.707 -2.51 21.354 -2.088 c
+21.001 -1.658 20.825 -1.066 20.825 -0.309 c
+h
+22.133 -0.309 m
+22.133 -1.202 22.368 -1.646 22.838 -1.646 c
+23.28 -1.646 23.515 -1.276 23.544 -0.53 c
+23.559 0.014 l
+23.559 0.463 23.493 0.797 23.368 1.014 c
+23.239 1.238 23.059 1.352 22.824 1.352 c
+22.607 1.352 22.435 1.238 22.31 1.014 c
+22.192 0.797 22.133 0.463 22.133 0.014 c
+h
+27.925 1.087 m
+27.498 1.117 l
+27.135 1.117 26.896 0.959 26.778 0.646 c
+26.778 -2.631 l
+25.47 -2.631 l
+25.47 2.337 l
+26.69 2.337 l
+26.734 1.808 l
+26.94 2.219 27.222 2.425 27.586 2.425 c
+27.733 2.425 27.851 2.404 27.939 2.366 c
+h
+30.158 -0.47 m
+30.82 2.337 l
+32.201 2.337 l
+30.57 -3.381 l
+30.324 -4.222 29.865 -4.645 29.188 -4.645 c
+29.03 -4.645 28.851 -4.615 28.645 -4.557 c
+28.645 -3.543 l
+28.806 -3.543 l
+29.001 -3.543 29.148 -3.502 29.248 -3.425 c
+29.354 -3.344 29.439 -3.208 29.497 -3.013 c
+29.6 -2.675 l
+28.16 2.337 l
+29.556 2.337 l
+h
+f
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+/GS3 gs
+0 TL/Fm2 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+/GS3 gs
+0 TL/Fm3 Do
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 4 M 1 j 1 J []0 d
+q 1 0 0 1 649.192 239.6172 cm
+0 0 m
+3.193 5.056 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 651.2461 242.8702 cm
+0 0 m
+0.33 -1.473 l
+1.421 2.253 l
+-1.474 -0.335 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 651.2462 242.8702 cm
+0 0 m
+0.33 -1.473 l
+1.421 2.253 l
+-1.474 -0.335 l
+0 0 l
+h
+S
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm4 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm5 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm6 Do
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 584.4681 270.0962 cm
+0 0 m
+5.453 5.056 l
+S
+Q
+q 1 0 0 1 588.3553 273.7013 cm
+0 0 m
+-0.058 -1.507 l
+1.955 1.815 l
+-1.507 0.059 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 588.3554 273.7014 cm
+0 0 m
+-0.059 -1.507 l
+1.955 1.815 l
+-1.507 0.059 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 637.7262 286.0224 cm
+0 0 m
+0 0.459 0.081 0.809 0.25 1.044 c
+0.426 1.286 0.676 1.411 1 1.411 c
+1.301 1.411 1.532 1.279 1.691 1.014 c
+1.72 1.353 l
+2.161 1.353 l
+2.161 -1.66 l
+2.161 -2.032 2.061 -2.315 1.866 -2.514 c
+1.679 -2.708 1.419 -2.807 1.088 -2.807 c
+0.941 -2.807 0.771 -2.767 0.588 -2.69 c
+0.401 -2.62 0.264 -2.532 0.177 -2.425 c
+0.368 -2.088 l
+0.573 -2.293 0.798 -2.396 1.043 -2.396 c
+1.444 -2.396 1.65 -2.168 1.661 -1.72 c
+1.661 -1.338 l
+1.503 -1.573 1.282 -1.691 1 -1.691 c
+0.684 -1.691 0.441 -1.573 0.264 -1.338 c
+0.096 -1.103 0.008 -0.771 0 -0.338 c
+h
+0.485 -0.279 m
+0.485 -0.613 0.532 -0.86 0.632 -1.014 c
+0.727 -1.172 0.889 -1.249 1.118 -1.249 c
+1.359 -1.249 1.544 -1.128 1.661 -0.882 c
+1.661 0.603 l
+1.544 0.846 1.367 0.97 1.132 0.97 c
+0.904 0.97 0.742 0.889 0.647 0.735 c
+0.548 0.577 0.492 0.339 0.485 0.015 c
+h
+3.227 -1.631 -0.501 2.984 re
+3.256 2.146 m
+3.256 2.058 3.231 1.985 3.183 1.926 c
+3.142 1.874 3.073 1.852 2.977 1.852 c
+2.888 1.852 2.818 1.874 2.771 1.926 c
+2.73 1.985 2.712 2.051 2.712 2.132 c
+2.712 2.22 2.73 2.293 2.771 2.352 c
+2.818 2.411 2.888 2.44 2.977 2.44 c
+3.073 2.44 3.142 2.411 3.183 2.352 c
+3.231 2.293 3.256 2.223 3.256 2.146 c
+4.534 2.072 m
+4.534 1.353 l
+4.991 1.353 l
+4.991 0.956 l
+4.534 0.956 l
+4.534 -0.897 l
+4.534 -1.014 4.553 -1.103 4.594 -1.161 c
+4.63 -1.22 4.7 -1.249 4.8 -1.249 c
+4.858 -1.249 4.92 -1.242 4.991 -1.22 c
+4.991 -1.631 l
+4.873 -1.668 4.759 -1.691 4.652 -1.691 c
+4.453 -1.691 4.303 -1.624 4.197 -1.484 c
+4.098 -1.348 4.05 -1.153 4.05 -0.897 c
+4.05 0.956 l
+3.594 0.956 l
+3.594 1.353 l
+4.05 1.353 l
+4.05 2.072 l
+h
+8.099 -0.867 m
+8.099 -0.76 8.058 -0.673 7.981 -0.602 c
+7.9 -0.525 7.75 -0.437 7.526 -0.338 c
+7.262 -0.231 7.074 -0.139 6.967 -0.058 c
+6.857 0.019 6.78 0.106 6.732 0.206 c
+6.681 0.301 6.659 0.419 6.659 0.559 c
+6.659 0.801 6.747 1.004 6.923 1.162 c
+7.1 1.326 7.324 1.411 7.599 1.411 c
+7.894 1.411 8.129 1.323 8.305 1.147 c
+8.482 0.977 8.569 0.765 8.569 0.5 c
+8.085 0.5 l
+8.085 0.636 8.033 0.75 7.938 0.838 c
+7.85 0.933 7.736 0.985 7.599 0.985 c
+7.453 0.985 7.339 0.944 7.262 0.867 c
+7.181 0.798 7.144 0.698 7.144 0.574 c
+7.144 0.474 7.173 0.397 7.232 0.339 c
+7.291 0.279 7.43 0.198 7.659 0.103 c
+8.018 -0.044 8.264 -0.187 8.393 -0.324 c
+8.529 -0.452 8.599 -0.625 8.599 -0.837 c
+8.599 -1.095 8.503 -1.301 8.32 -1.455 c
+8.143 -1.613 7.908 -1.691 7.614 -1.691 c
+7.298 -1.691 7.044 -1.602 6.85 -1.425 c
+6.662 -1.242 6.57 -1.01 6.57 -0.735 c
+7.056 -0.735 l
+7.063 -0.904 7.115 -1.036 7.202 -1.132 c
+7.298 -1.22 7.437 -1.264 7.614 -1.264 c
+7.769 -1.264 7.886 -1.23 7.967 -1.161 c
+8.056 -1.095 8.099 -0.995 8.099 -0.867 c
+9.687 2.072 m
+9.687 1.353 l
+10.142 1.353 l
+10.142 0.956 l
+9.687 0.956 l
+9.687 -0.897 l
+9.687 -1.014 9.705 -1.103 9.745 -1.161 c
+9.782 -1.22 9.852 -1.249 9.951 -1.249 c
+10.01 -1.249 10.073 -1.242 10.142 -1.22 c
+10.142 -1.631 l
+10.025 -1.668 9.911 -1.691 9.804 -1.691 c
+9.606 -1.691 9.455 -1.624 9.349 -1.484 c
+9.249 -1.348 9.201 -1.153 9.201 -0.897 c
+9.201 0.956 l
+8.746 0.956 l
+8.746 1.353 l
+9.201 1.353 l
+9.201 2.072 l
+h
+12.137 -1.631 m
+12.108 -1.565 12.087 -1.455 12.079 -1.308 c
+11.902 -1.565 11.682 -1.691 11.418 -1.691 c
+11.142 -1.691 10.925 -1.617 10.771 -1.469 c
+10.624 -1.315 10.55 -1.099 10.55 -0.823 c
+10.55 -0.522 10.653 -0.279 10.859 -0.103 c
+11.065 0.081 11.348 0.177 11.711 0.177 c
+12.064 0.177 l
+12.064 0.5 l
+12.064 0.676 12.024 0.798 11.946 0.867 c
+11.866 0.944 11.748 0.985 11.594 0.985 c
+11.447 0.985 11.322 0.941 11.227 0.852 c
+11.138 0.765 11.094 0.654 11.094 0.53 c
+10.609 0.53 l
+10.609 0.676 10.653 0.816 10.741 0.956 c
+10.83 1.103 10.947 1.213 11.094 1.294 c
+11.248 1.371 11.422 1.411 11.609 1.411 c
+11.921 1.411 12.156 1.33 12.314 1.176 c
+12.469 1.029 12.549 0.809 12.549 0.515 c
+12.549 -0.985 l
+12.557 -1.22 12.594 -1.422 12.652 -1.587 c
+12.652 -1.631 l
+h
+11.491 -1.249 m
+11.609 -1.249 11.719 -1.216 11.829 -1.147 c
+11.936 -1.08 12.013 -0.995 12.064 -0.897 c
+12.064 -0.191 l
+11.8 -0.191 l
+11.565 -0.191 11.377 -0.243 11.241 -0.338 c
+11.113 -0.437 11.05 -0.58 11.05 -0.764 c
+11.05 -0.933 11.079 -1.055 11.138 -1.132 c
+11.204 -1.213 11.322 -1.249 11.491 -1.249 c
+14.57 -0.867 m
+14.57 -0.76 14.53 -0.673 14.453 -0.602 c
+14.372 -0.525 14.221 -0.437 13.997 -0.338 c
+13.733 -0.231 13.545 -0.139 13.438 -0.058 c
+13.328 0.019 13.251 0.106 13.203 0.206 c
+13.152 0.301 13.13 0.419 13.13 0.559 c
+13.13 0.801 13.218 1.004 13.394 1.162 c
+13.571 1.326 13.795 1.411 14.071 1.411 c
+14.365 1.411 14.6 1.323 14.776 1.147 c
+14.953 0.977 15.041 0.765 15.041 0.5 c
+14.556 0.5 l
+14.556 0.636 14.504 0.75 14.408 0.838 c
+14.321 0.933 14.207 0.985 14.071 0.985 c
+13.924 0.985 13.81 0.944 13.733 0.867 c
+13.652 0.798 13.615 0.698 13.615 0.574 c
+13.615 0.474 13.645 0.397 13.703 0.339 c
+13.762 0.279 13.901 0.198 14.13 0.103 c
+14.489 -0.044 14.736 -0.187 14.865 -0.324 c
+15 -0.452 15.071 -0.625 15.071 -0.837 c
+15.071 -1.095 14.975 -1.301 14.791 -1.455 c
+14.614 -1.613 14.379 -1.691 14.086 -1.691 c
+13.77 -1.691 13.516 -1.602 13.321 -1.425 c
+13.134 -1.242 13.042 -1.01 13.042 -0.735 c
+13.527 -0.735 l
+13.534 -0.904 13.585 -1.036 13.674 -1.132 c
+13.77 -1.22 13.909 -1.264 14.086 -1.264 c
+14.24 -1.264 14.358 -1.23 14.438 -1.161 c
+14.526 -1.095 14.57 -0.995 14.57 -0.867 c
+16.014 1.044 m
+16.199 1.286 16.434 1.411 16.72 1.411 c
+17.249 1.411 17.518 1.058 17.529 0.353 c
+17.529 -1.631 l
+17.043 -1.631 l
+17.043 0.324 l
+17.043 0.559 17.003 0.724 16.926 0.823 c
+16.845 0.919 16.727 0.97 16.573 0.97 c
+16.455 0.97 16.345 0.929 16.249 0.852 c
+16.151 0.771 16.074 0.665 16.014 0.53 c
+16.014 -1.631 l
+15.53 -1.631 l
+15.53 2.602 l
+16.014 2.602 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 653.715 276.3362 cm
+0 0 m
+-10.796 5.008 -20.332 5.81 -29.005 3.726 c
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 626.7867 280.5622 cm
+0 0 m
+0.787 1.286 l
+-2.594 -0.624 l
+1.286 -0.791 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 626.7867 280.5622 cm
+0 0 m
+0.787 1.286 l
+-2.594 -0.625 l
+1.286 -0.791 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 606.5934 264.4701 cm
+0 0 m
+0 0.459 0.081 0.808 0.25 1.043 c
+0.426 1.286 0.676 1.411 1 1.411 c
+1.301 1.411 1.532 1.278 1.69 1.014 c
+1.72 1.353 l
+2.161 1.353 l
+2.161 -1.661 l
+2.161 -2.032 2.062 -2.315 1.867 -2.514 c
+1.679 -2.708 1.419 -2.807 1.088 -2.807 c
+0.941 -2.807 0.772 -2.767 0.588 -2.69 c
+0.401 -2.62 0.265 -2.532 0.177 -2.425 c
+0.368 -2.087 l
+0.574 -2.293 0.798 -2.396 1.044 -2.396 c
+1.444 -2.396 1.65 -2.168 1.661 -1.72 c
+1.661 -1.338 l
+1.503 -1.573 1.283 -1.691 1 -1.691 c
+0.684 -1.691 0.441 -1.573 0.265 -1.338 c
+0.096 -1.103 0.008 -0.771 0 -0.338 c
+h
+0.485 -0.279 m
+0.485 -0.613 0.533 -0.86 0.632 -1.014 c
+0.728 -1.172 0.89 -1.249 1.118 -1.249 c
+1.36 -1.249 1.544 -1.128 1.661 -0.881 c
+1.661 0.603 l
+1.544 0.845 1.367 0.97 1.132 0.97 c
+0.904 0.97 0.742 0.889 0.647 0.735 c
+0.548 0.577 0.493 0.338 0.485 0.015 c
+h
+3.223 -1.631 -0.5 2.984 re
+3.252 2.146 m
+3.252 2.058 3.227 1.984 3.179 1.926 c
+3.138 1.874 3.069 1.852 2.973 1.852 c
+2.885 1.852 2.815 1.874 2.768 1.926 c
+2.727 1.984 2.708 2.051 2.708 2.132 c
+2.708 2.219 2.727 2.294 2.768 2.352 c
+2.815 2.411 2.885 2.44 2.973 2.44 c
+3.069 2.44 3.138 2.411 3.179 2.352 c
+3.227 2.294 3.252 2.223 3.252 2.146 c
+4.532 2.072 m
+4.532 1.353 l
+4.987 1.353 l
+4.987 0.956 l
+4.532 0.956 l
+4.532 -0.897 l
+4.532 -1.014 4.549 -1.103 4.59 -1.161 c
+4.627 -1.22 4.696 -1.249 4.796 -1.249 c
+4.854 -1.249 4.917 -1.242 4.987 -1.22 c
+4.987 -1.631 l
+4.869 -1.668 4.755 -1.691 4.649 -1.691 c
+4.451 -1.691 4.3 -1.625 4.193 -1.484 c
+4.094 -1.349 4.046 -1.154 4.046 -0.897 c
+4.046 0.956 l
+3.591 0.956 l
+3.591 1.353 l
+4.046 1.353 l
+4.046 2.072 l
+h
+8.096 -0.867 m
+8.096 -0.761 8.056 -0.673 7.978 -0.603 c
+7.898 -0.526 7.747 -0.437 7.522 -0.338 c
+7.258 -0.231 7.071 -0.139 6.964 -0.058 c
+6.853 0.019 6.776 0.106 6.729 0.206 c
+6.677 0.301 6.655 0.419 6.655 0.559 c
+6.655 0.802 6.743 1.003 6.92 1.161 c
+7.096 1.326 7.321 1.411 7.596 1.411 c
+7.89 1.411 8.125 1.323 8.301 1.147 c
+8.478 0.977 8.566 0.765 8.566 0.5 c
+8.081 0.5 l
+8.081 0.636 8.029 0.75 7.934 0.838 c
+7.846 0.933 7.732 0.985 7.596 0.985 c
+7.449 0.985 7.335 0.944 7.258 0.867 c
+7.177 0.798 7.14 0.698 7.14 0.573 c
+7.14 0.474 7.169 0.397 7.229 0.338 c
+7.287 0.279 7.427 0.198 7.655 0.103 c
+8.015 -0.044 8.262 -0.187 8.39 -0.324 c
+8.526 -0.452 8.596 -0.625 8.596 -0.838 c
+8.596 -1.095 8.5 -1.301 8.316 -1.455 c
+8.14 -1.613 7.905 -1.691 7.611 -1.691 c
+7.294 -1.691 7.041 -1.602 6.847 -1.426 c
+6.659 -1.242 6.567 -1.01 6.567 -0.735 c
+7.052 -0.735 l
+7.059 -0.904 7.111 -1.037 7.199 -1.132 c
+7.294 -1.22 7.435 -1.264 7.611 -1.264 c
+7.765 -1.264 7.882 -1.231 7.963 -1.161 c
+8.052 -1.095 8.096 -0.996 8.096 -0.867 c
+9.687 2.072 m
+9.687 1.353 l
+10.143 1.353 l
+10.143 0.956 l
+9.687 0.956 l
+9.687 -0.897 l
+9.687 -1.014 9.706 -1.103 9.746 -1.161 c
+9.783 -1.22 9.852 -1.249 9.951 -1.249 c
+10.01 -1.249 10.073 -1.242 10.143 -1.22 c
+10.143 -1.631 l
+10.026 -1.668 9.911 -1.691 9.804 -1.691 c
+9.606 -1.691 9.455 -1.625 9.349 -1.484 c
+9.249 -1.349 9.202 -1.154 9.202 -0.897 c
+9.202 0.956 l
+8.746 0.956 l
+8.746 1.353 l
+9.202 1.353 l
+9.202 2.072 l
+h
+12.135 -1.631 m
+12.104 -1.565 12.083 -1.455 12.075 -1.309 c
+11.899 -1.565 11.678 -1.691 11.414 -1.691 c
+11.138 -1.691 10.922 -1.617 10.768 -1.469 c
+10.62 -1.315 10.547 -1.099 10.547 -0.823 c
+10.547 -0.522 10.65 -0.279 10.855 -0.103 c
+11.061 0.081 11.344 0.177 11.708 0.177 c
+12.061 0.177 l
+12.061 0.5 l
+12.061 0.676 12.021 0.798 11.944 0.867 c
+11.863 0.944 11.745 0.985 11.591 0.985 c
+11.443 0.985 11.319 0.941 11.223 0.852 c
+11.135 0.765 11.09 0.654 11.09 0.53 c
+10.606 0.53 l
+10.606 0.676 10.65 0.816 10.738 0.956 c
+10.826 1.103 10.944 1.213 11.09 1.294 c
+11.245 1.371 11.418 1.411 11.605 1.411 c
+11.917 1.411 12.152 1.33 12.31 1.176 c
+12.465 1.029 12.546 0.808 12.546 0.515 c
+12.546 -0.985 l
+12.553 -1.22 12.59 -1.422 12.649 -1.587 c
+12.649 -1.631 l
+h
+11.487 -1.249 m
+11.605 -1.249 11.715 -1.216 11.826 -1.147 c
+11.932 -1.08 12.01 -0.996 12.061 -0.897 c
+12.061 -0.191 l
+11.796 -0.191 l
+11.561 -0.191 11.374 -0.243 11.238 -0.338 c
+11.109 -0.437 11.046 -0.58 11.046 -0.764 c
+11.046 -0.933 11.076 -1.055 11.135 -1.132 c
+11.201 -1.213 11.319 -1.249 11.487 -1.249 c
+14.567 -0.867 m
+14.567 -0.761 14.527 -0.673 14.45 -0.603 c
+14.369 -0.526 14.218 -0.437 13.993 -0.338 c
+13.729 -0.231 13.542 -0.139 13.436 -0.058 c
+13.325 0.019 13.247 0.106 13.201 0.206 c
+13.149 0.301 13.126 0.419 13.126 0.559 c
+13.126 0.802 13.215 1.003 13.391 1.161 c
+13.567 1.326 13.791 1.411 14.067 1.411 c
+14.361 1.411 14.597 1.323 14.772 1.147 c
+14.949 0.977 15.038 0.765 15.038 0.5 c
+14.552 0.5 l
+14.552 0.636 14.501 0.75 14.406 0.838 c
+14.317 0.933 14.203 0.985 14.067 0.985 c
+13.92 0.985 13.806 0.944 13.729 0.867 c
+13.648 0.798 13.612 0.698 13.612 0.573 c
+13.612 0.474 13.641 0.397 13.7 0.338 c
+13.758 0.279 13.898 0.198 14.126 0.103 c
+14.486 -0.044 14.732 -0.187 14.861 -0.324 c
+14.997 -0.452 15.067 -0.625 15.067 -0.838 c
+15.067 -1.095 14.971 -1.301 14.787 -1.455 c
+14.612 -1.613 14.376 -1.691 14.082 -1.691 c
+13.766 -1.691 13.513 -1.602 13.318 -1.426 c
+13.13 -1.242 13.039 -1.01 13.039 -0.735 c
+13.523 -0.735 l
+13.531 -0.904 13.582 -1.037 13.671 -1.132 c
+13.766 -1.22 13.906 -1.264 14.082 -1.264 c
+14.236 -1.264 14.354 -1.231 14.435 -1.161 c
+14.523 -1.095 14.567 -0.996 14.567 -0.867 c
+16.011 1.043 m
+16.195 1.286 16.43 1.411 16.717 1.411 c
+17.246 1.411 17.514 1.058 17.525 0.353 c
+17.525 -1.631 l
+17.041 -1.631 l
+17.041 0.324 l
+17.041 0.559 17 0.724 16.923 0.823 c
+16.842 0.919 16.724 0.97 16.57 0.97 c
+16.453 0.97 16.342 0.929 16.247 0.852 c
+16.147 0.771 16.07 0.665 16.011 0.53 c
+16.011 -1.631 l
+15.526 -1.631 l
+15.526 2.602 l
+16.011 2.602 l
+h
+21.417 -0.279 m
+21.417 -0.75 21.332 -1.103 21.167 -1.338 c
+20.998 -1.573 20.759 -1.691 20.447 -1.691 c
+20.141 -1.691 19.91 -1.58 19.755 -1.352 c
+19.755 -2.778 l
+19.271 -2.778 l
+19.271 1.353 l
+19.711 1.353 l
+19.741 1.014 l
+19.896 1.278 20.127 1.411 20.432 1.411 c
+20.763 1.411 21.009 1.294 21.167 1.058 c
+21.332 0.831 21.417 0.492 21.417 0.044 c
+h
+20.931 0 m
+20.931 0.331 20.877 0.577 20.77 0.735 c
+20.671 0.889 20.509 0.97 20.285 0.97 c
+20.05 0.97 19.873 0.852 19.755 0.617 c
+19.755 -0.926 l
+19.873 -1.154 20.054 -1.264 20.299 -1.264 c
+20.513 -1.264 20.671 -1.187 20.77 -1.029 c
+20.877 -0.875 20.931 -0.632 20.931 -0.309 c
+h
+21.77 0 m
+21.77 0.43 21.872 0.771 22.078 1.029 c
+22.292 1.282 22.57 1.411 22.916 1.411 c
+23.258 1.411 23.534 1.282 23.74 1.029 c
+23.952 0.783 24.066 0.449 24.077 0.029 c
+24.077 -0.279 l
+24.077 -0.713 23.967 -1.055 23.754 -1.309 c
+23.549 -1.565 23.269 -1.691 22.916 -1.691 c
+22.57 -1.691 22.298 -1.569 22.093 -1.323 c
+21.887 -1.08 21.777 -0.746 21.77 -0.324 c
+h
+22.255 -0.279 m
+22.255 -0.595 22.313 -0.838 22.431 -1.014 c
+22.556 -1.183 22.718 -1.264 22.916 -1.264 c
+23.346 -1.264 23.57 -0.956 23.592 -0.338 c
+23.592 0 l
+23.592 0.301 23.526 0.544 23.401 0.721 c
+23.283 0.897 23.121 0.985 22.916 0.985 c
+22.718 0.985 22.556 0.897 22.431 0.721 c
+22.313 0.544 22.255 0.301 22.255 0 c
+h
+26.664 -0.279 m
+26.664 -0.75 26.58 -1.103 26.414 -1.338 c
+26.246 -1.573 26.007 -1.691 25.694 -1.691 c
+25.39 -1.691 25.157 -1.58 25.003 -1.352 c
+25.003 -2.778 l
+24.519 -2.778 l
+24.519 1.353 l
+24.96 1.353 l
+24.989 1.014 l
+25.143 1.278 25.375 1.411 25.679 1.411 c
+26.011 1.411 26.256 1.294 26.414 1.058 c
+26.58 0.831 26.664 0.492 26.664 0.044 c
+h
+26.179 0 m
+26.179 0.331 26.124 0.577 26.018 0.735 c
+25.918 0.889 25.757 0.97 25.533 0.97 c
+25.297 0.97 25.121 0.852 25.003 0.617 c
+25.003 -0.926 l
+25.121 -1.154 25.301 -1.264 25.548 -1.264 c
+25.76 -1.264 25.918 -1.187 26.018 -1.029 c
+26.124 -0.875 26.179 -0.632 26.179 -0.309 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 619.5765 272.9739 cm
+0 0 m
+6.644 -3.018 14.244 -5.796 25.808 -5.322 c
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 643.2534 267.5639 cm
+0 0 m
+-1.022 -1.11 l
+2.664 0.111 l
+-1.11 1.026 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 643.2535 267.5639 cm
+0 0 m
+-1.022 -1.11 l
+2.664 0.111 l
+-1.11 1.026 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 664.6398 301.7215 cm
+0 0 m
+0 0.459 0.081 0.808 0.25 1.043 c
+0.427 1.286 0.676 1.411 1 1.411 c
+1.301 1.411 1.532 1.278 1.69 1.014 c
+1.721 1.352 l
+2.161 1.352 l
+2.161 -1.661 l
+2.161 -2.032 2.062 -2.315 1.867 -2.514 c
+1.68 -2.708 1.419 -2.808 1.088 -2.808 c
+0.941 -2.808 0.772 -2.768 0.588 -2.691 c
+0.401 -2.62 0.265 -2.533 0.177 -2.425 c
+0.368 -2.087 l
+0.574 -2.294 0.798 -2.396 1.044 -2.396 c
+1.445 -2.396 1.65 -2.168 1.661 -1.72 c
+1.661 -1.338 l
+1.503 -1.573 1.283 -1.691 1 -1.691 c
+0.684 -1.691 0.441 -1.573 0.265 -1.338 c
+0.096 -1.103 0.008 -0.772 0 -0.339 c
+h
+0.485 -0.279 m
+0.485 -0.614 0.533 -0.86 0.632 -1.014 c
+0.728 -1.172 0.89 -1.249 1.118 -1.249 c
+1.36 -1.249 1.544 -1.129 1.661 -0.882 c
+1.661 0.602 l
+1.544 0.845 1.368 0.97 1.133 0.97 c
+0.904 0.97 0.742 0.889 0.647 0.735 c
+0.548 0.577 0.493 0.338 0.485 0.014 c
+h
+3.227 -1.632 -0.499 2.984 re
+3.256 2.146 m
+3.256 2.057 3.231 1.984 3.182 1.926 c
+3.142 1.874 3.072 1.851 2.977 1.851 c
+2.889 1.851 2.819 1.874 2.771 1.926 c
+2.731 1.984 2.712 2.05 2.712 2.131 c
+2.712 2.219 2.731 2.293 2.771 2.352 c
+2.819 2.41 2.889 2.439 2.977 2.439 c
+3.072 2.439 3.142 2.41 3.182 2.352 c
+3.231 2.293 3.256 2.223 3.256 2.146 c
+4.532 2.072 m
+4.532 1.352 l
+4.987 1.352 l
+4.987 0.955 l
+4.532 0.955 l
+4.532 -0.897 l
+4.532 -1.014 4.549 -1.103 4.59 -1.162 c
+4.627 -1.22 4.697 -1.249 4.796 -1.249 c
+4.855 -1.249 4.917 -1.243 4.987 -1.22 c
+4.987 -1.632 l
+4.869 -1.669 4.755 -1.691 4.649 -1.691 c
+4.451 -1.691 4.3 -1.625 4.193 -1.484 c
+4.094 -1.349 4.046 -1.154 4.046 -0.897 c
+4.046 0.955 l
+3.591 0.955 l
+3.591 1.352 l
+4.046 1.352 l
+4.046 2.072 l
+h
+7.894 0.897 m
+7.824 0.904 7.751 0.911 7.674 0.911 c
+7.416 0.911 7.24 0.771 7.144 0.5 c
+7.144 -1.632 l
+6.66 -1.632 l
+6.66 1.352 l
+7.13 1.352 l
+7.144 1.043 l
+7.269 1.286 7.453 1.411 7.688 1.411 c
+7.765 1.411 7.828 1.396 7.879 1.367 c
+h
+9.272 -1.691 m
+8.897 -1.691 8.614 -1.584 8.42 -1.367 c
+8.221 -1.143 8.125 -0.817 8.125 -0.383 c
+8.125 -0.015 l
+8.125 0.426 8.217 0.771 8.405 1.028 c
+8.599 1.282 8.875 1.411 9.228 1.411 c
+9.569 1.411 9.823 1.297 9.992 1.072 c
+10.168 0.845 10.261 0.5 10.271 0.029 c
+10.271 -0.279 l
+8.611 -0.279 l
+8.611 -0.353 l
+8.611 -0.676 8.669 -0.912 8.786 -1.058 c
+8.904 -1.199 9.073 -1.264 9.301 -1.264 c
+9.448 -1.264 9.573 -1.243 9.683 -1.191 c
+9.79 -1.132 9.893 -1.044 9.992 -0.927 c
+10.242 -1.235 l
+10.036 -1.54 9.713 -1.691 9.272 -1.691 c
+9.228 0.985 m
+9.022 0.985 8.867 0.914 8.772 0.779 c
+8.673 0.639 8.618 0.426 8.611 0.132 c
+9.786 0.132 l
+9.786 0.205 l
+9.764 0.477 9.713 0.675 9.625 0.793 c
+9.536 0.918 9.405 0.985 9.228 0.985 c
+12.123 -0.867 m
+12.123 -0.761 12.083 -0.673 12.006 -0.603 c
+11.925 -0.526 11.774 -0.437 11.55 -0.339 c
+11.286 -0.231 11.098 -0.14 10.992 -0.059 c
+10.882 0.018 10.804 0.106 10.756 0.205 c
+10.705 0.301 10.683 0.419 10.683 0.558 c
+10.683 0.801 10.771 1.003 10.947 1.161 c
+11.124 1.326 11.348 1.411 11.624 1.411 c
+11.917 1.411 12.152 1.323 12.329 1.146 c
+12.505 0.977 12.594 0.764 12.594 0.5 c
+12.108 0.5 l
+12.108 0.635 12.057 0.75 11.961 0.837 c
+11.873 0.933 11.759 0.985 11.624 0.985 c
+11.477 0.985 11.363 0.944 11.286 0.867 c
+11.205 0.797 11.168 0.698 11.168 0.573 c
+11.168 0.474 11.198 0.397 11.256 0.338 c
+11.315 0.279 11.454 0.198 11.682 0.103 c
+12.042 -0.044 12.289 -0.188 12.418 -0.324 c
+12.553 -0.453 12.623 -0.625 12.623 -0.838 c
+12.623 -1.095 12.528 -1.301 12.343 -1.455 c
+12.168 -1.613 11.932 -1.691 11.638 -1.691 c
+11.323 -1.691 11.069 -1.602 10.874 -1.426 c
+10.687 -1.243 10.595 -1.011 10.595 -0.736 c
+11.08 -0.736 l
+11.088 -0.904 11.138 -1.037 11.227 -1.132 c
+11.323 -1.22 11.462 -1.264 11.638 -1.264 c
+11.793 -1.264 11.911 -1.231 11.992 -1.162 c
+12.079 -1.095 12.123 -0.996 12.123 -0.867 c
+14.137 -1.691 m
+13.762 -1.691 13.48 -1.584 13.284 -1.367 c
+13.087 -1.143 12.991 -0.817 12.991 -0.383 c
+12.991 -0.015 l
+12.991 0.426 13.083 0.771 13.27 1.028 c
+13.465 1.282 13.74 1.411 14.093 1.411 c
+14.435 1.411 14.689 1.297 14.857 1.072 c
+15.034 0.845 15.126 0.5 15.137 0.029 c
+15.137 -0.279 l
+13.476 -0.279 l
+13.476 -0.353 l
+13.476 -0.676 13.534 -0.912 13.652 -1.058 c
+13.77 -1.199 13.939 -1.264 14.167 -1.264 c
+14.313 -1.264 14.438 -1.243 14.549 -1.191 c
+14.656 -1.132 14.758 -1.044 14.857 -0.927 c
+15.107 -1.235 l
+14.901 -1.54 14.579 -1.691 14.137 -1.691 c
+14.093 0.985 m
+13.887 0.985 13.733 0.914 13.638 0.779 c
+13.538 0.639 13.483 0.426 13.476 0.132 c
+14.652 0.132 l
+14.652 0.205 l
+14.629 0.477 14.579 0.675 14.49 0.793 c
+14.402 0.918 14.269 0.985 14.093 0.985 c
+16.181 2.072 m
+16.181 1.352 l
+16.636 1.352 l
+16.636 0.955 l
+16.181 0.955 l
+16.181 -0.897 l
+16.181 -1.014 16.199 -1.103 16.239 -1.162 c
+16.276 -1.22 16.345 -1.249 16.445 -1.249 c
+16.503 -1.249 16.566 -1.243 16.636 -1.22 c
+16.636 -1.632 l
+16.518 -1.669 16.405 -1.691 16.298 -1.691 c
+16.1 -1.691 15.949 -1.625 15.842 -1.484 c
+15.743 -1.349 15.695 -1.154 15.695 -0.897 c
+15.695 0.955 l
+15.24 0.955 l
+15.24 1.352 l
+15.695 1.352 l
+15.695 2.072 l
+h
+20.995 -1.632 m
+20.495 -1.632 l
+20.495 0.22 l
+18.878 0.22 l
+18.878 -1.632 l
+18.363 -1.632 l
+18.363 2.381 l
+18.878 2.381 l
+18.878 0.661 l
+20.495 0.661 l
+20.495 2.381 l
+20.995 2.381 l
+h
+23.563 0.22 m
+22.152 0.22 l
+22.152 -1.206 l
+23.798 -1.206 l
+23.798 -1.632 l
+21.637 -1.632 l
+21.637 2.381 l
+23.769 2.381 l
+23.769 1.94 l
+22.152 1.94 l
+22.152 0.661 l
+23.563 0.661 l
+h
+26.132 -0.588 m
+24.779 -0.588 l
+24.456 -1.632 l
+23.942 -1.632 l
+25.235 2.381 l
+25.661 2.381 l
+26.969 -1.632 l
+26.44 -1.632 l
+h
+24.912 -0.148 m
+25.999 -0.148 l
+25.455 1.675 l
+h
+27.285 -1.632 m
+27.285 2.381 l
+28.256 2.381 l
+28.715 2.381 29.072 2.234 29.328 1.94 c
+29.593 1.646 29.725 1.234 29.725 0.706 c
+29.725 0.029 l
+29.725 -0.5 29.593 -0.912 29.328 -1.206 c
+29.072 -1.492 28.696 -1.632 28.197 -1.632 c
+h
+27.8 1.94 m
+27.8 -1.206 l
+28.197 -1.206 l
+28.557 -1.206 28.818 -1.103 28.976 -0.897 c
+29.141 -0.691 29.226 -0.389 29.226 0.014 c
+29.226 0.72 l
+29.226 1.139 29.141 1.448 28.976 1.646 c
+28.818 1.841 28.579 1.94 28.256 1.94 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 700.2778 300.817 cm
+0 0 m
+-10.796 -4.575 -16.874 -12.02 -16.76 -23.684 c
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 683.4952 279.2684 cm
+0 0 m
+-1.077 1.055 l
+0.026 -2.668 l
+1.059 1.077 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 683.4952 279.2684 cm
+0 0 m
+-1.077 1.055 l
+0.026 -2.668 l
+1.059 1.077 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 702.545 271.8379 cm
+0 0 m
+0 0.459 0.081 0.808 0.25 1.043 c
+0.427 1.286 0.676 1.411 1 1.411 c
+1.301 1.411 1.532 1.278 1.69 1.014 c
+1.72 1.353 l
+2.161 1.353 l
+2.161 -1.661 l
+2.161 -2.032 2.062 -2.315 1.867 -2.514 c
+1.68 -2.708 1.419 -2.807 1.088 -2.807 c
+0.941 -2.807 0.772 -2.767 0.588 -2.69 c
+0.401 -2.62 0.265 -2.532 0.177 -2.425 c
+0.368 -2.087 l
+0.574 -2.293 0.798 -2.396 1.044 -2.396 c
+1.445 -2.396 1.65 -2.168 1.661 -1.72 c
+1.661 -1.338 l
+1.503 -1.573 1.283 -1.69 1 -1.69 c
+0.684 -1.69 0.441 -1.573 0.265 -1.338 c
+0.096 -1.103 0.008 -0.771 0 -0.338 c
+h
+0.485 -0.279 m
+0.485 -0.613 0.533 -0.86 0.632 -1.014 c
+0.728 -1.172 0.89 -1.249 1.118 -1.249 c
+1.36 -1.249 1.544 -1.128 1.661 -0.881 c
+1.661 0.603 l
+1.544 0.845 1.368 0.97 1.133 0.97 c
+0.904 0.97 0.742 0.889 0.647 0.735 c
+0.547 0.577 0.493 0.338 0.485 0.015 c
+h
+3.223 -1.631 -0.5 2.984 re
+3.252 2.146 m
+3.252 2.058 3.227 1.984 3.179 1.926 c
+3.138 1.874 3.069 1.852 2.974 1.852 c
+2.885 1.852 2.816 1.874 2.768 1.926 c
+2.727 1.984 2.708 2.051 2.708 2.132 c
+2.708 2.219 2.727 2.294 2.768 2.352 c
+2.816 2.411 2.885 2.44 2.974 2.44 c
+3.069 2.44 3.138 2.411 3.179 2.352 c
+3.227 2.294 3.252 2.223 3.252 2.146 c
+4.532 2.072 m
+4.532 1.353 l
+4.987 1.353 l
+4.987 0.956 l
+4.532 0.956 l
+4.532 -0.897 l
+4.532 -1.014 4.549 -1.103 4.59 -1.161 c
+4.627 -1.22 4.697 -1.249 4.796 -1.249 c
+4.855 -1.249 4.917 -1.242 4.987 -1.22 c
+4.987 -1.631 l
+4.869 -1.668 4.755 -1.69 4.649 -1.69 c
+4.451 -1.69 4.3 -1.625 4.193 -1.484 c
+4.094 -1.349 4.046 -1.154 4.046 -0.897 c
+4.046 0.956 l
+3.591 0.956 l
+3.591 1.353 l
+4.046 1.353 l
+4.046 2.072 l
+h
+8.184 -1.631 m
+8.154 -1.565 8.133 -1.455 8.125 -1.309 c
+7.949 -1.565 7.728 -1.69 7.464 -1.69 c
+7.188 -1.69 6.972 -1.617 6.817 -1.469 c
+6.67 -1.315 6.596 -1.099 6.596 -0.823 c
+6.596 -0.522 6.699 -0.279 6.905 -0.103 c
+7.111 0.081 7.394 0.177 7.757 0.177 c
+8.11 0.177 l
+8.11 0.5 l
+8.11 0.676 8.07 0.798 7.992 0.867 c
+7.913 0.945 7.795 0.985 7.64 0.985 c
+7.493 0.985 7.368 0.941 7.273 0.852 c
+7.184 0.765 7.14 0.654 7.14 0.53 c
+6.656 0.53 l
+6.656 0.676 6.699 0.816 6.787 0.956 c
+6.876 1.103 6.993 1.213 7.14 1.294 c
+7.294 1.371 7.468 1.411 7.655 1.411 c
+7.967 1.411 8.202 1.33 8.36 1.176 c
+8.515 1.029 8.596 0.808 8.596 0.515 c
+8.596 -0.985 l
+8.603 -1.22 8.64 -1.422 8.698 -1.587 c
+8.698 -1.631 l
+h
+7.537 -1.249 m
+7.655 -1.249 7.765 -1.216 7.875 -1.147 c
+7.982 -1.08 8.059 -0.996 8.11 -0.897 c
+8.11 -0.191 l
+7.846 -0.191 l
+7.611 -0.191 7.423 -0.243 7.287 -0.338 c
+7.159 -0.437 7.096 -0.58 7.096 -0.764 c
+7.096 -0.933 7.126 -1.055 7.184 -1.132 c
+7.25 -1.213 7.368 -1.249 7.537 -1.249 c
+9.073 0 m
+9.073 0.459 9.154 0.808 9.324 1.043 c
+9.5 1.286 9.75 1.411 10.073 1.411 c
+10.356 1.411 10.577 1.294 10.735 1.058 c
+10.735 2.602 l
+11.219 2.602 l
+11.219 -1.631 l
+10.778 -1.631 l
+10.749 -1.309 l
+10.591 -1.565 10.367 -1.69 10.073 -1.69 c
+9.756 -1.69 9.515 -1.573 9.338 -1.338 c
+9.162 -1.095 9.073 -0.75 9.073 -0.309 c
+h
+9.559 -0.279 m
+9.559 -0.613 9.606 -0.86 9.706 -1.014 c
+9.801 -1.172 9.962 -1.249 10.19 -1.249 c
+10.433 -1.249 10.617 -1.132 10.735 -0.897 c
+10.735 0.617 l
+10.606 0.852 10.425 0.97 10.19 0.97 c
+9.962 0.97 9.801 0.889 9.706 0.735 c
+9.606 0.577 9.559 0.338 9.559 0.015 c
+h
+11.69 0 m
+11.69 0.459 11.771 0.808 11.94 1.043 c
+12.116 1.286 12.366 1.411 12.69 1.411 c
+12.972 1.411 13.193 1.294 13.351 1.058 c
+13.351 2.602 l
+13.835 2.602 l
+13.835 -1.631 l
+13.395 -1.631 l
+13.365 -1.309 l
+13.207 -1.565 12.983 -1.69 12.69 -1.69 c
+12.374 -1.69 12.131 -1.573 11.954 -1.338 c
+11.778 -1.095 11.69 -0.75 11.69 -0.309 c
+h
+12.175 -0.279 m
+12.175 -0.613 12.223 -0.86 12.322 -1.014 c
+12.418 -1.172 12.579 -1.249 12.807 -1.249 c
+13.049 -1.249 13.233 -1.132 13.351 -0.897 c
+13.351 0.617 l
+13.222 0.852 13.042 0.97 12.807 0.97 c
+12.579 0.97 12.418 0.889 12.322 0.735 c
+12.223 0.577 12.175 0.338 12.175 0.015 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 693.4612 270.8827 cm
+0 0 m
+10.797 4.571 16.875 12.016 16.764 23.68 c
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 710.2439 292.4276 cm
+0 0 m
+1.077 -1.055 l
+-0.025 2.668 l
+-1.055 -1.077 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 710.244 292.4276 cm
+0 0 m
+1.077 -1.055 l
+-0.025 2.668 l
+-1.055 -1.077 l
+0 0 l
+h
+S
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm7 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm8 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm9 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm10 Do
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 745.1474 289.2892 cm
+0 0 m
+-3.205 6.184 l
+S
+Q
+q 1 0 0 1 742.9236 293.5782 cm
+0 0 m
+1.437 -0.456 l
+-1.228 2.37 l
+-0.456 -1.438 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 742.9237 293.5782 cm
+0 0 m
+1.437 -0.456 l
+-1.228 2.37 l
+-0.456 -1.438 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 711.5555 341.2288 cm
+0 0 m
+0 0.459 0.081 0.808 0.25 1.043 c
+0.427 1.286 0.676 1.411 1 1.411 c
+1.301 1.411 1.532 1.278 1.69 1.014 c
+1.721 1.352 l
+2.161 1.352 l
+2.161 -1.661 l
+2.161 -2.032 2.062 -2.315 1.867 -2.514 c
+1.68 -2.708 1.419 -2.808 1.088 -2.808 c
+0.941 -2.808 0.772 -2.768 0.589 -2.691 c
+0.401 -2.62 0.265 -2.533 0.177 -2.425 c
+0.368 -2.087 l
+0.574 -2.294 0.798 -2.396 1.044 -2.396 c
+1.445 -2.396 1.65 -2.168 1.661 -1.72 c
+1.661 -1.338 l
+1.503 -1.573 1.283 -1.691 1 -1.691 c
+0.684 -1.691 0.441 -1.573 0.265 -1.338 c
+0.096 -1.103 0.008 -0.772 0 -0.339 c
+h
+0.485 -0.279 m
+0.485 -0.614 0.533 -0.86 0.632 -1.014 c
+0.728 -1.172 0.89 -1.249 1.118 -1.249 c
+1.36 -1.249 1.544 -1.128 1.661 -0.882 c
+1.661 0.602 l
+1.544 0.845 1.368 0.97 1.133 0.97 c
+0.904 0.97 0.743 0.889 0.647 0.735 c
+0.548 0.577 0.493 0.338 0.485 0.014 c
+h
+3.223 -1.632 -0.5 2.984 re
+3.253 2.146 m
+3.253 2.057 3.227 1.984 3.179 1.926 c
+3.138 1.874 3.069 1.852 2.974 1.852 c
+2.885 1.852 2.816 1.874 2.768 1.926 c
+2.727 1.984 2.708 2.05 2.708 2.131 c
+2.708 2.219 2.727 2.293 2.768 2.352 c
+2.816 2.41 2.885 2.44 2.974 2.44 c
+3.069 2.44 3.138 2.41 3.179 2.352 c
+3.227 2.293 3.253 2.223 3.253 2.146 c
+4.532 2.072 m
+4.532 1.352 l
+4.987 1.352 l
+4.987 0.955 l
+4.532 0.955 l
+4.532 -0.897 l
+4.532 -1.014 4.549 -1.103 4.59 -1.162 c
+4.627 -1.22 4.697 -1.249 4.796 -1.249 c
+4.855 -1.249 4.917 -1.243 4.987 -1.22 c
+4.987 -1.632 l
+4.869 -1.669 4.755 -1.691 4.649 -1.691 c
+4.451 -1.691 4.3 -1.625 4.194 -1.484 c
+4.094 -1.349 4.046 -1.154 4.046 -0.897 c
+4.046 0.955 l
+3.591 0.955 l
+3.591 1.352 l
+4.046 1.352 l
+4.046 2.072 l
+h
+7.684 -1.264 m
+7.849 -1.264 7.982 -1.216 8.081 -1.118 c
+8.177 -1.022 8.231 -0.879 8.243 -0.691 c
+8.699 -0.691 l
+8.688 -0.977 8.585 -1.216 8.389 -1.411 c
+8.202 -1.598 7.967 -1.691 7.684 -1.691 c
+7.321 -1.691 7.041 -1.573 6.847 -1.338 c
+6.648 -1.103 6.552 -0.757 6.552 -0.294 c
+6.552 0.029 l
+6.552 0.478 6.644 0.823 6.832 1.058 c
+7.026 1.294 7.31 1.411 7.684 1.411 c
+7.986 1.411 8.229 1.311 8.404 1.117 c
+8.588 0.918 8.688 0.654 8.699 0.324 c
+8.243 0.324 l
+8.221 0.548 8.162 0.713 8.067 0.823 c
+7.978 0.929 7.849 0.985 7.684 0.985 c
+7.468 0.985 7.306 0.912 7.199 0.764 c
+7.1 0.625 7.045 0.397 7.038 0.073 c
+7.038 -0.309 l
+7.038 -0.661 7.086 -0.912 7.184 -1.058 c
+7.291 -1.199 7.456 -1.264 7.684 -1.264 c
+8.992 0 m
+8.992 0.43 9.095 0.771 9.301 1.029 c
+9.515 1.282 9.794 1.411 10.139 1.411 c
+10.481 1.411 10.756 1.282 10.962 1.029 c
+11.175 0.783 11.289 0.448 11.3 0.029 c
+11.3 -0.279 l
+11.3 -0.713 11.19 -1.055 10.977 -1.309 c
+10.771 -1.565 10.492 -1.691 10.139 -1.691 c
+9.794 -1.691 9.522 -1.569 9.316 -1.323 c
+9.11 -1.081 9 -0.746 8.992 -0.324 c
+h
+9.478 -0.279 m
+9.478 -0.595 9.536 -0.838 9.654 -1.014 c
+9.779 -1.183 9.941 -1.264 10.139 -1.264 c
+10.569 -1.264 10.793 -0.956 10.816 -0.339 c
+10.816 0 l
+10.816 0.301 10.749 0.544 10.624 0.72 c
+10.506 0.897 10.345 0.985 10.139 0.985 c
+9.941 0.985 9.779 0.897 9.654 0.72 c
+9.536 0.544 9.478 0.301 9.478 0 c
+h
+12.197 1.352 m
+12.212 1.072 l
+12.388 1.297 12.627 1.411 12.932 1.411 c
+13.263 1.411 13.494 1.264 13.623 0.97 c
+13.806 1.264 14.068 1.411 14.402 1.411 c
+14.961 1.411 15.244 1.066 15.254 0.382 c
+15.254 -1.632 l
+14.77 -1.632 l
+14.77 0.338 l
+14.77 0.551 14.729 0.709 14.652 0.808 c
+14.571 0.914 14.438 0.97 14.255 0.97 c
+14.108 0.97 13.991 0.912 13.902 0.794 c
+13.814 0.683 13.759 0.544 13.74 0.367 c
+13.74 -1.632 l
+13.255 -1.632 l
+13.255 0.353 l
+13.245 0.764 13.072 0.97 12.74 0.97 c
+12.495 0.97 12.322 0.845 12.227 0.602 c
+12.227 -1.632 l
+11.741 -1.632 l
+11.741 1.352 l
+h
+16.158 1.352 m
+16.173 1.072 l
+16.349 1.297 16.588 1.411 16.894 1.411 c
+17.224 1.411 17.455 1.264 17.584 0.97 c
+17.768 1.264 18.029 1.411 18.363 1.411 c
+18.922 1.411 19.205 1.066 19.216 0.382 c
+19.216 -1.632 l
+18.731 -1.632 l
+18.731 0.338 l
+18.731 0.551 18.691 0.709 18.613 0.808 c
+18.533 0.914 18.4 0.97 18.216 0.97 c
+18.07 0.97 17.952 0.912 17.864 0.794 c
+17.775 0.683 17.72 0.544 17.702 0.367 c
+17.702 -1.632 l
+17.216 -1.632 l
+17.216 0.353 l
+17.206 0.764 17.033 0.97 16.702 0.97 c
+16.456 0.97 16.283 0.845 16.188 0.602 c
+16.188 -1.632 l
+15.703 -1.632 l
+15.703 1.352 l
+h
+20.193 -1.632 -0.5 2.984 re
+20.223 2.146 m
+20.223 2.057 20.197 1.984 20.149 1.926 c
+20.109 1.874 20.039 1.852 19.944 1.852 c
+19.855 1.852 19.786 1.874 19.738 1.926 c
+19.697 1.984 19.678 2.05 19.678 2.131 c
+19.678 2.219 19.697 2.293 19.738 2.352 c
+19.786 2.41 19.855 2.44 19.944 2.44 c
+20.039 2.44 20.109 2.41 20.149 2.352 c
+20.197 2.293 20.223 2.223 20.223 2.146 c
+21.498 2.072 m
+21.498 1.352 l
+21.953 1.352 l
+21.953 0.955 l
+21.498 0.955 l
+21.498 -0.897 l
+21.498 -1.014 21.517 -1.103 21.557 -1.162 c
+21.594 -1.22 21.663 -1.249 21.762 -1.249 c
+21.821 -1.249 21.883 -1.243 21.953 -1.22 c
+21.953 -1.632 l
+21.836 -1.669 21.722 -1.691 21.615 -1.691 c
+21.417 -1.691 21.266 -1.625 21.16 -1.484 c
+21.06 -1.349 21.012 -1.154 21.012 -0.897 c
+21.012 0.955 l
+20.557 0.955 l
+20.557 1.352 l
+21.012 1.352 l
+21.012 2.072 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 715.6204 326.614 cm
+0 0 m
+-4.572 10.797 -12.017 16.875 -23.68 16.764 c
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 694.071 343.3966 cm
+0 0 m
+1.059 1.077 l
+-2.664 -0.025 l
+1.077 -1.055 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 694.071 343.3966 cm
+0 0 m
+1.059 1.077 l
+-2.664 -0.025 l
+1.077 -1.055 l
+0 0 l
+h
+S
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm11 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm12 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm13 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm14 Do
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 634.5146 329.9282 cm
+0 0 m
+5.074 5.056 l
+S
+Q
+q 1 0 0 1 638.0789 333.4787 cm
+0 0 m
+-0.004 -1.507 l
+1.889 1.882 l
+-1.506 0.003 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 638.0789 333.4787 cm
+0 0 m
+-0.004 -1.507 l
+1.889 1.882 l
+-1.506 0.003 l
+0 0 l
+h
+S
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS4 gs
+0 TL/Fm15 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS5 gs
+0 TL/Fm16 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS5 gs
+0 TL/Fm17 Do
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 563.3862 425.8473 cm
+0 0 m
+54.018 0 l
+56.624 0 58.722 -2.098 58.722 -4.704 c
+58.722 -21.406 l
+58.722 -24.01 56.624 -26.109 54.018 -26.109 c
+0 -26.109 l
+-2.606 -26.109 -4.701 -24.01 -4.701 -21.406 c
+-4.701 -4.704 l
+-4.701 -2.098 -2.606 0 0 0 c
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 570.5405 419.8831 cm
+0 0 m
+-0.426 0.029 l
+-0.79 0.029 -1.029 -0.129 -1.147 -0.441 c
+-1.147 -3.719 l
+-2.454 -3.719 l
+-2.454 1.249 l
+-1.234 1.249 l
+-1.191 0.721 l
+-0.985 1.132 -0.702 1.338 -0.338 1.338 c
+-0.191 1.338 -0.073 1.316 0.015 1.279 c
+h
+2.469 -3.807 m
+1.801 -3.807 1.278 -3.612 0.896 -3.219 c
+0.522 -2.818 0.338 -2.246 0.338 -1.5 c
+0.338 -1.103 l
+0.338 -0.32 0.507 0.283 0.852 0.706 c
+1.195 1.124 1.69 1.338 2.337 1.338 c
+2.955 1.338 3.418 1.135 3.734 0.735 c
+4.057 0.341 4.218 -0.247 4.218 -1.029 c
+4.218 -1.675 l
+1.646 -1.675 l
+1.654 -2.04 1.735 -2.308 1.881 -2.484 c
+2.028 -2.653 2.249 -2.734 2.543 -2.734 c
+2.984 -2.734 3.344 -2.583 3.63 -2.279 c
+4.145 -3.072 l
+3.987 -3.289 3.755 -3.465 3.454 -3.601 c
+3.15 -3.738 2.822 -3.807 2.469 -3.807 c
+1.646 -0.764 m
+2.955 -0.764 l
+2.955 -0.646 l
+2.944 -0.345 2.888 -0.118 2.793 0.029 c
+2.705 0.183 2.547 0.264 2.323 0.264 c
+2.095 0.264 1.929 0.181 1.823 0.015 c
+1.723 -0.143 1.665 -0.405 1.646 -0.764 c
+5.997 1.249 m
+6.041 0.794 l
+6.335 1.154 6.718 1.338 7.188 1.338 c
+7.688 1.338 8.04 1.118 8.246 0.676 c
+8.529 1.118 8.937 1.338 9.467 1.338 c
+10.326 1.338 10.774 0.738 10.803 -0.455 c
+10.803 -3.719 l
+9.496 -3.719 l
+9.496 -0.544 l
+9.496 -0.261 9.455 -0.058 9.378 0.059 c
+9.297 0.183 9.165 0.25 8.981 0.25 c
+8.735 0.25 8.555 0.103 8.437 -0.191 c
+8.437 -3.719 l
+7.144 -3.719 l
+7.144 -0.559 l
+7.144 -0.276 7.104 -0.07 7.026 0.059 c
+6.945 0.183 6.809 0.25 6.614 0.25 c
+6.387 0.25 6.211 0.133 6.086 -0.103 c
+6.086 -3.719 l
+4.792 -3.719 l
+4.792 1.249 l
+h
+11.333 -1.072 m
+11.333 -0.32 11.509 0.268 11.862 0.691 c
+12.215 1.121 12.704 1.338 13.332 1.338 c
+13.968 1.338 14.464 1.121 14.817 0.691 c
+15.177 0.268 15.36 -0.324 15.36 -1.088 c
+15.36 -1.396 l
+15.36 -2.153 15.184 -2.745 14.832 -3.175 c
+14.479 -3.597 13.982 -3.807 13.347 -3.807 c
+12.707 -3.807 12.215 -3.597 11.862 -3.175 c
+11.509 -2.745 11.333 -2.153 11.333 -1.396 c
+h
+12.642 -1.396 m
+12.642 -2.289 12.877 -2.734 13.347 -2.734 c
+13.787 -2.734 14.023 -2.363 14.053 -1.617 c
+14.067 -1.072 l
+14.067 -0.625 14.001 -0.29 13.876 -0.073 c
+13.747 0.15 13.567 0.264 13.332 0.264 c
+13.115 0.264 12.943 0.15 12.818 -0.073 c
+12.7 -0.29 12.642 -0.625 12.642 -1.072 c
+h
+17.492 2.469 m
+17.492 1.249 l
+18.153 1.249 l
+18.153 0.279 l
+17.492 0.279 l
+17.492 -2.19 l
+17.492 -2.389 17.514 -2.524 17.565 -2.601 c
+17.624 -2.672 17.731 -2.705 17.889 -2.705 c
+18.007 -2.705 18.109 -2.697 18.198 -2.676 c
+18.183 -3.69 l
+17.955 -3.767 17.716 -3.807 17.463 -3.807 c
+16.628 -3.807 16.206 -3.329 16.199 -2.366 c
+16.199 0.279 l
+15.625 0.279 l
+15.625 1.249 l
+16.199 1.249 l
+16.199 2.469 l
+h
+20.762 -3.807 m
+20.093 -3.807 19.572 -3.612 19.189 -3.219 c
+18.815 -2.818 18.631 -2.246 18.631 -1.5 c
+18.631 -1.103 l
+18.631 -0.32 18.8 0.283 19.146 0.706 c
+19.487 1.124 19.983 1.338 20.63 1.338 c
+21.247 1.338 21.71 1.135 22.026 0.735 c
+22.35 0.341 22.512 -0.247 22.512 -1.029 c
+22.512 -1.675 l
+19.939 -1.675 l
+19.946 -2.04 20.027 -2.308 20.174 -2.484 c
+20.322 -2.653 20.542 -2.734 20.835 -2.734 c
+21.277 -2.734 21.637 -2.583 21.924 -2.279 c
+22.438 -3.072 l
+22.28 -3.289 22.049 -3.465 21.747 -3.601 c
+21.442 -3.738 21.115 -3.807 20.762 -3.807 c
+19.939 -0.764 m
+21.247 -0.764 l
+21.247 -0.646 l
+21.236 -0.345 21.182 -0.118 21.086 0.029 c
+20.997 0.183 20.839 0.264 20.615 0.264 c
+20.388 0.264 20.222 0.181 20.116 0.015 c
+20.016 -0.143 19.958 -0.405 19.939 -0.764 c
+27.605 0 m
+27.179 0.029 l
+26.815 0.029 26.576 -0.129 26.458 -0.441 c
+26.458 -3.719 l
+25.15 -3.719 l
+25.15 1.249 l
+26.37 1.249 l
+26.414 0.721 l
+26.62 1.132 26.903 1.338 27.266 1.338 c
+27.414 1.338 27.532 1.316 27.619 1.279 c
+h
+30.074 -3.807 m
+29.406 -3.807 28.883 -3.612 28.502 -3.219 c
+28.126 -2.818 27.943 -2.246 27.943 -1.5 c
+27.943 -1.103 l
+27.943 -0.32 28.112 0.283 28.457 0.706 c
+28.799 1.124 29.296 1.338 29.942 1.338 c
+30.559 1.338 31.023 1.135 31.339 0.735 c
+31.661 0.341 31.823 -0.247 31.823 -1.029 c
+31.823 -1.675 l
+29.251 -1.675 l
+29.259 -2.04 29.34 -2.308 29.487 -2.484 c
+29.633 -2.653 29.853 -2.734 30.148 -2.734 c
+30.589 -2.734 30.948 -2.583 31.235 -2.279 c
+31.75 -3.072 l
+31.592 -3.289 31.36 -3.465 31.059 -3.601 c
+30.754 -3.738 30.427 -3.807 30.074 -3.807 c
+29.251 -0.764 m
+30.559 -0.764 l
+30.559 -0.646 l
+30.549 -0.345 30.493 -0.118 30.398 0.029 c
+30.31 0.183 30.152 0.264 29.928 0.264 c
+29.699 0.264 29.534 0.181 29.427 0.015 c
+29.328 -0.143 29.269 -0.405 29.251 -0.764 c
+36.263 -1.396 m
+36.263 -2.171 36.122 -2.771 35.851 -3.189 c
+35.586 -3.601 35.189 -3.807 34.661 -3.807 c
+34.256 -3.807 33.932 -3.645 33.691 -3.322 c
+33.691 -5.629 l
+32.382 -5.629 l
+32.382 1.249 l
+33.587 1.249 l
+33.631 0.794 l
+33.885 1.154 34.223 1.338 34.646 1.338 c
+35.175 1.338 35.572 1.147 35.836 0.765 c
+36.108 0.382 36.251 -0.213 36.263 -1.014 c
+h
+34.969 -1.058 m
+34.969 -0.58 34.914 -0.243 34.807 -0.044 c
+34.697 0.15 34.517 0.25 34.264 0.25 c
+33.999 0.25 33.808 0.14 33.691 -0.073 c
+33.691 -2.41 l
+33.808 -2.628 34.003 -2.734 34.279 -2.734 c
+34.532 -2.734 34.709 -2.628 34.807 -2.41 c
+34.914 -2.186 34.969 -1.849 34.969 -1.396 c
+h
+36.714 -1.072 m
+36.714 -0.32 36.891 0.268 37.244 0.691 c
+37.597 1.121 38.085 1.338 38.713 1.338 c
+39.349 1.338 39.846 1.121 40.199 0.691 c
+40.558 0.268 40.742 -0.324 40.742 -1.088 c
+40.742 -1.396 l
+40.742 -2.153 40.566 -2.745 40.213 -3.175 c
+39.86 -3.597 39.364 -3.807 38.728 -3.807 c
+38.089 -3.807 37.597 -3.597 37.244 -3.175 c
+36.891 -2.745 36.714 -2.153 36.714 -1.396 c
+h
+38.023 -1.396 m
+38.023 -2.289 38.258 -2.734 38.728 -2.734 c
+39.17 -2.734 39.405 -2.363 39.434 -1.617 c
+39.449 -1.072 l
+39.449 -0.625 39.382 -0.29 39.258 -0.073 c
+39.129 0.15 38.948 0.264 38.713 0.264 c
+38.497 0.264 38.324 0.15 38.2 -0.073 c
+38.082 -0.29 38.023 -0.625 38.023 -1.072 c
+h
+f
+Q
+q 1 0 0 1 564.4878 408.9507 cm
+0 0 m
+0 0.684 0.077 1.341 0.235 1.97 c
+0.39 2.606 0.632 3.167 0.956 3.66 c
+1.279 4.16 1.621 4.505 1.985 4.704 c
+2.22 3.983 l
+1.875 3.66 1.603 3.16 1.397 2.484 c
+1.199 1.816 1.096 1.043 1.088 0.162 c
+1.088 -0.118 l
+1.088 -1.04 1.183 -1.849 1.382 -2.543 c
+1.588 -3.237 1.867 -3.755 2.22 -4.101 c
+1.985 -4.806 l
+1.621 -4.619 1.279 -4.281 0.956 -3.792 c
+0.64 -3.3 0.401 -2.749 0.235 -2.132 c
+0.077 -1.514 0 -0.867 0 -0.191 c
+h
+4.032 2.249 m
+4.075 1.764 l
+4.37 2.146 4.752 2.337 5.222 2.337 c
+6.075 2.337 6.516 1.738 6.546 0.544 c
+6.546 -2.72 l
+5.237 -2.72 l
+5.237 0.455 l
+5.237 0.727 5.193 0.929 5.104 1.058 c
+5.024 1.183 4.877 1.249 4.664 1.249 c
+4.429 1.249 4.245 1.132 4.12 0.897 c
+4.12 -2.72 l
+2.812 -2.72 l
+2.812 2.249 l
+h
+9.65 -2.72 m
+9.61 -2.643 9.569 -2.514 9.532 -2.337 c
+9.305 -2.653 8.992 -2.807 8.592 -2.807 c
+8.169 -2.807 7.82 -2.672 7.549 -2.396 c
+7.284 -2.124 7.151 -1.764 7.151 -1.323 c
+7.151 -0.804 7.313 -0.405 7.637 -0.118 c
+7.967 0.166 8.449 0.312 9.077 0.324 c
+9.474 0.324 l
+9.474 0.721 l
+9.474 0.944 9.434 1.103 9.357 1.191 c
+9.276 1.278 9.166 1.323 9.018 1.323 c
+8.695 1.323 8.533 1.135 8.533 0.765 c
+7.24 0.765 l
+7.24 1.213 7.408 1.588 7.755 1.881 c
+8.096 2.182 8.533 2.337 9.062 2.337 c
+9.599 2.337 10.018 2.194 10.311 1.911 c
+10.613 1.635 10.768 1.235 10.768 0.706 c
+10.768 -1.631 l
+10.775 -2.065 10.841 -2.404 10.959 -2.645 c
+10.959 -2.72 l
+h
+8.886 -1.793 m
+9.022 -1.793 9.139 -1.764 9.239 -1.705 c
+9.345 -1.646 9.422 -1.58 9.474 -1.5 c
+9.474 -0.47 l
+9.166 -0.47 l
+8.937 -0.47 8.761 -0.54 8.636 -0.676 c
+8.507 -0.816 8.445 -0.999 8.445 -1.234 c
+8.445 -1.61 8.592 -1.793 8.886 -1.793 c
+12.755 2.249 m
+12.8 1.793 l
+13.093 2.153 13.476 2.337 13.946 2.337 c
+14.446 2.337 14.799 2.117 15.005 1.675 c
+15.287 2.117 15.695 2.337 16.225 2.337 c
+17.085 2.337 17.533 1.738 17.562 0.544 c
+17.562 -2.72 l
+16.254 -2.72 l
+16.254 0.455 l
+16.254 0.738 16.214 0.941 16.136 1.058 c
+16.056 1.183 15.923 1.249 15.74 1.249 c
+15.493 1.249 15.313 1.103 15.196 0.808 c
+15.196 -2.72 l
+13.902 -2.72 l
+13.902 0.441 l
+13.902 0.724 13.862 0.929 13.785 1.058 c
+13.704 1.183 13.567 1.249 13.373 1.249 c
+13.145 1.249 12.969 1.132 12.844 0.897 c
+12.844 -2.72 l
+11.55 -2.72 l
+11.55 2.249 l
+h
+20.252 -2.807 m
+19.583 -2.807 19.061 -2.612 18.679 -2.219 c
+18.305 -1.819 18.121 -1.246 18.121 -0.5 c
+18.121 -0.103 l
+18.121 0.68 18.29 1.282 18.635 1.706 c
+18.977 2.124 19.473 2.337 20.12 2.337 c
+20.737 2.337 21.201 2.135 21.517 1.735 c
+21.839 1.341 22.001 0.754 22.001 -0.029 c
+22.001 -0.676 l
+19.429 -0.676 l
+19.437 -1.04 19.518 -1.309 19.664 -1.484 c
+19.811 -1.654 20.031 -1.735 20.326 -1.735 c
+20.767 -1.735 21.127 -1.584 21.413 -1.278 c
+21.928 -2.072 l
+21.77 -2.29 21.538 -2.466 21.237 -2.602 c
+20.931 -2.738 20.605 -2.807 20.252 -2.807 c
+19.429 0.235 m
+20.737 0.235 l
+20.737 0.353 l
+20.726 0.654 20.671 0.882 20.576 1.029 c
+20.487 1.183 20.329 1.264 20.105 1.264 c
+19.877 1.264 19.712 1.18 19.605 1.014 c
+19.506 0.856 19.447 0.595 19.429 0.235 c
+22.662 -2.057 m
+22.662 -1.852 22.729 -1.683 22.868 -1.544 c
+23.015 -1.407 23.2 -1.338 23.427 -1.338 c
+23.64 -1.338 23.817 -1.407 23.956 -1.544 c
+24.104 -1.672 24.177 -1.845 24.177 -2.057 c
+24.177 -2.275 24.104 -2.448 23.956 -2.572 c
+23.817 -2.701 23.64 -2.763 23.427 -2.763 c
+23.2 -2.763 23.015 -2.701 22.868 -2.572 c
+22.729 -2.448 22.662 -2.275 22.662 -2.057 c
+22.662 1.706 m
+22.662 1.911 22.729 2.08 22.868 2.219 c
+23.015 2.356 23.2 2.425 23.427 2.425 c
+23.64 2.425 23.817 2.356 23.956 2.219 c
+24.104 2.091 24.177 1.918 24.177 1.706 c
+24.177 1.488 24.104 1.315 23.956 1.191 c
+23.817 1.062 23.64 1 23.427 1 c
+23.2 1 23.015 1.062 22.868 1.191 c
+22.729 1.315 22.662 1.488 22.662 1.706 c
+26.98 -0.073 m
+26.98 0.68 27.157 1.268 27.51 1.69 c
+27.863 2.12 28.351 2.337 28.979 2.337 c
+29.615 2.337 30.111 2.12 30.464 1.69 c
+30.824 1.268 31.008 0.676 31.008 -0.088 c
+31.008 -0.397 l
+31.008 -1.154 30.832 -1.745 30.479 -2.175 c
+30.126 -2.598 29.63 -2.807 28.995 -2.807 c
+28.355 -2.807 27.863 -2.598 27.51 -2.175 c
+27.157 -1.745 26.98 -1.154 26.98 -0.397 c
+h
+28.289 -0.397 m
+28.289 -1.29 28.524 -1.735 28.995 -1.735 c
+29.436 -1.735 29.671 -1.363 29.7 -0.617 c
+29.714 -0.073 l
+29.714 0.374 29.648 0.709 29.523 0.926 c
+29.395 1.151 29.215 1.264 28.979 1.264 c
+28.763 1.264 28.59 1.151 28.465 0.926 c
+28.347 0.709 28.289 0.374 28.289 -0.073 c
+h
+34.08 1 m
+33.654 1.029 l
+33.29 1.029 33.051 0.871 32.933 0.559 c
+32.933 -2.72 l
+31.626 -2.72 l
+31.626 2.249 l
+32.846 2.249 l
+32.889 1.72 l
+33.095 2.132 33.378 2.337 33.742 2.337 c
+33.889 2.337 34.007 2.315 34.095 2.278 c
+h
+35.991 -2.72 -1.308 4.969 re
+34.624 3.543 m
+34.624 3.738 34.687 3.899 34.815 4.027 c
+34.94 4.164 35.113 4.233 35.329 4.233 c
+35.543 4.233 35.715 4.164 35.844 4.027 c
+35.969 3.899 36.035 3.738 36.035 3.543 c
+36.035 3.337 35.969 3.164 35.844 3.028 c
+35.726 2.899 35.553 2.837 35.329 2.837 c
+35.113 2.837 34.94 2.899 34.815 3.028 c
+34.687 3.164 34.624 3.337 34.624 3.543 c
+36.752 -0.088 m
+36.752 0.735 36.899 1.341 37.193 1.735 c
+37.487 2.135 37.899 2.337 38.427 2.337 c
+38.876 2.337 39.225 2.161 39.471 1.808 c
+39.515 2.249 l
+40.691 2.249 l
+40.691 -2.72 l
+40.691 -3.355 40.507 -3.84 40.147 -4.174 c
+39.794 -4.505 39.283 -4.675 38.618 -4.675 c
+38.354 -4.675 38.067 -4.619 37.766 -4.513 c
+37.461 -4.403 37.236 -4.266 37.09 -4.101 c
+37.531 -3.204 l
+37.656 -3.329 37.818 -3.432 38.016 -3.513 c
+38.211 -3.59 38.391 -3.63 38.56 -3.63 c
+38.853 -3.63 39.063 -3.561 39.192 -3.425 c
+39.316 -3.285 39.383 -3.061 39.383 -2.749 c
+39.383 -2.323 l
+39.137 -2.645 38.813 -2.807 38.412 -2.807 c
+37.884 -2.807 37.472 -2.602 37.178 -2.19 c
+36.891 -1.779 36.752 -1.201 36.752 -0.455 c
+h
+38.045 -0.411 m
+38.045 -0.875 38.104 -1.205 38.221 -1.411 c
+38.339 -1.617 38.531 -1.72 38.795 -1.72 c
+39.06 -1.72 39.254 -1.628 39.383 -1.44 c
+39.383 0.941 l
+39.254 1.147 39.063 1.249 38.809 1.249 c
+38.545 1.249 38.346 1.139 38.221 0.926 c
+38.104 0.721 38.045 0.382 38.045 -0.088 c
+h
+42.819 -2.72 -1.309 4.969 re
+41.452 3.543 m
+41.452 3.738 41.514 3.899 41.643 4.027 c
+41.768 4.164 41.94 4.233 42.157 4.233 c
+42.371 4.233 42.543 4.164 42.672 4.027 c
+42.797 3.899 42.863 3.738 42.863 3.543 c
+42.863 3.337 42.797 3.164 42.672 3.028 c
+42.554 2.899 42.381 2.837 42.157 2.837 c
+41.94 2.837 41.768 2.899 41.643 3.028 c
+41.514 3.164 41.452 3.337 41.452 3.543 c
+44.917 2.249 m
+44.961 1.764 l
+45.255 2.146 45.637 2.337 46.107 2.337 c
+46.96 2.337 47.401 1.738 47.43 0.544 c
+47.43 -2.72 l
+46.123 -2.72 l
+46.123 0.455 l
+46.123 0.727 46.078 0.929 45.99 1.058 c
+45.909 1.183 45.762 1.249 45.549 1.249 c
+45.314 1.249 45.13 1.132 45.005 0.897 c
+45.005 -2.72 l
+43.697 -2.72 l
+43.697 2.249 l
+h
+50.26 -0.103 m
+50.26 -0.819 50.165 -1.496 49.981 -2.132 c
+49.805 -2.767 49.554 -3.326 49.231 -3.807 c
+48.908 -4.285 48.563 -4.619 48.203 -4.806 c
+47.967 -4.101 l
+48.309 -3.767 48.577 -3.256 48.775 -2.572 c
+48.981 -1.889 49.088 -1.11 49.099 -0.235 c
+49.099 0.015 l
+49.099 0.904 48.996 1.698 48.79 2.396 c
+48.592 3.102 48.32 3.634 47.967 3.998 c
+48.203 4.704 l
+48.438 4.586 48.677 4.384 48.922 4.101 c
+49.165 3.825 49.386 3.484 49.584 3.072 c
+49.79 2.66 49.951 2.194 50.069 1.675 c
+50.194 1.165 50.26 0.573 50.26 -0.103 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 661.8731 424.5284 cm
+0 0 m
+54.019 0 l
+56.624 0 58.723 -2.099 58.723 -4.704 c
+58.723 -21.409 l
+58.723 -24.015 56.624 -26.109 54.019 -26.109 c
+0 -26.109 l
+-2.606 -26.109 -4.7 -24.015 -4.7 -21.409 c
+-4.7 -4.704 l
+-4.7 -2.099 -2.606 0 0 0 c
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 669.0274 418.5641 cm
+0 0 m
+-0.426 0.029 l
+-0.789 0.029 -1.028 -0.129 -1.146 -0.441 c
+-1.146 -3.719 l
+-2.454 -3.719 l
+-2.454 1.249 l
+-1.234 1.249 l
+-1.19 0.72 l
+-0.984 1.132 -0.702 1.338 -0.338 1.338 c
+-0.191 1.338 -0.073 1.315 0.015 1.278 c
+h
+2.469 -3.807 m
+1.802 -3.807 1.279 -3.612 0.898 -3.219 c
+0.522 -2.818 0.339 -2.246 0.339 -1.5 c
+0.339 -1.103 l
+0.339 -0.32 0.508 0.283 0.853 0.706 c
+1.195 1.124 1.691 1.338 2.338 1.338 c
+2.955 1.338 3.418 1.135 3.734 0.735 c
+4.057 0.341 4.219 -0.246 4.219 -1.029 c
+4.219 -1.675 l
+1.646 -1.675 l
+1.654 -2.04 1.735 -2.308 1.882 -2.484 c
+2.029 -2.654 2.249 -2.734 2.544 -2.734 c
+2.984 -2.734 3.344 -2.583 3.631 -2.278 c
+4.146 -3.072 l
+3.988 -3.289 3.756 -3.466 3.454 -3.601 c
+3.15 -3.738 2.822 -3.807 2.469 -3.807 c
+1.646 -0.765 m
+2.955 -0.765 l
+2.955 -0.647 l
+2.944 -0.345 2.889 -0.118 2.793 0.029 c
+2.705 0.183 2.547 0.264 2.323 0.264 c
+2.095 0.264 1.93 0.18 1.823 0.014 c
+1.724 -0.144 1.665 -0.405 1.646 -0.765 c
+5.997 1.249 m
+6.041 0.794 l
+6.336 1.153 6.718 1.338 7.188 1.338 c
+7.688 1.338 8.041 1.117 8.247 0.676 c
+8.53 1.117 8.937 1.338 9.467 1.338 c
+10.327 1.338 10.775 0.738 10.804 -0.456 c
+10.804 -3.719 l
+9.496 -3.719 l
+9.496 -0.544 l
+9.496 -0.262 9.455 -0.059 9.378 0.058 c
+9.297 0.183 9.166 0.249 8.981 0.249 c
+8.736 0.249 8.555 0.103 8.438 -0.191 c
+8.438 -3.719 l
+7.144 -3.719 l
+7.144 -0.559 l
+7.144 -0.276 7.104 -0.07 7.026 0.058 c
+6.945 0.183 6.81 0.249 6.615 0.249 c
+6.388 0.249 6.211 0.132 6.086 -0.103 c
+6.086 -3.719 l
+4.792 -3.719 l
+4.792 1.249 l
+h
+11.333 -1.073 m
+11.333 -0.32 11.51 0.268 11.863 0.691 c
+12.216 1.12 12.704 1.338 13.332 1.338 c
+13.968 1.338 14.464 1.12 14.817 0.691 c
+15.177 0.268 15.361 -0.324 15.361 -1.087 c
+15.361 -1.397 l
+15.361 -2.153 15.185 -2.745 14.832 -3.175 c
+14.479 -3.597 13.983 -3.807 13.347 -3.807 c
+12.708 -3.807 12.216 -3.597 11.863 -3.175 c
+11.51 -2.745 11.333 -2.153 11.333 -1.397 c
+h
+12.642 -1.397 m
+12.642 -2.29 12.877 -2.734 13.347 -2.734 c
+13.788 -2.734 14.024 -2.363 14.053 -1.617 c
+14.068 -1.073 l
+14.068 -0.625 14.001 -0.291 13.876 -0.073 c
+13.748 0.151 13.567 0.264 13.332 0.264 c
+13.116 0.264 12.943 0.151 12.818 -0.073 c
+12.7 -0.291 12.642 -0.625 12.642 -1.073 c
+h
+17.492 2.469 m
+17.492 1.249 l
+18.153 1.249 l
+18.153 0.279 l
+17.492 0.279 l
+17.492 -2.19 l
+17.492 -2.389 17.515 -2.525 17.565 -2.602 c
+17.625 -2.672 17.731 -2.705 17.889 -2.705 c
+18.007 -2.705 18.109 -2.697 18.198 -2.675 c
+18.184 -3.69 l
+17.955 -3.767 17.717 -3.807 17.463 -3.807 c
+16.629 -3.807 16.206 -3.329 16.199 -2.367 c
+16.199 0.279 l
+15.626 0.279 l
+15.626 1.249 l
+16.199 1.249 l
+16.199 2.469 l
+h
+20.759 -3.807 m
+20.09 -3.807 19.568 -3.612 19.186 -3.219 c
+18.812 -2.818 18.628 -2.246 18.628 -1.5 c
+18.628 -1.103 l
+18.628 -0.32 18.797 0.283 19.142 0.706 c
+19.484 1.124 19.98 1.338 20.627 1.338 c
+21.245 1.338 21.708 1.135 22.024 0.735 c
+22.346 0.341 22.508 -0.246 22.508 -1.029 c
+22.508 -1.675 l
+19.936 -1.675 l
+19.944 -2.04 20.025 -2.308 20.171 -2.484 c
+20.318 -2.654 20.539 -2.734 20.833 -2.734 c
+21.274 -2.734 21.634 -2.583 21.92 -2.278 c
+22.435 -3.072 l
+22.277 -3.289 22.045 -3.466 21.744 -3.601 c
+21.44 -3.738 21.112 -3.807 20.759 -3.807 c
+19.936 -0.765 m
+21.245 -0.765 l
+21.245 -0.647 l
+21.234 -0.345 21.178 -0.118 21.083 0.029 c
+20.995 0.183 20.836 0.264 20.613 0.264 c
+20.384 0.264 20.219 0.18 20.112 0.014 c
+20.013 -0.144 19.954 -0.405 19.936 -0.765 c
+27.605 0 m
+27.179 0.029 l
+26.815 0.029 26.576 -0.129 26.458 -0.441 c
+26.458 -3.719 l
+25.151 -3.719 l
+25.151 1.249 l
+26.371 1.249 l
+26.415 0.72 l
+26.62 1.132 26.903 1.338 27.267 1.338 c
+27.414 1.338 27.532 1.315 27.62 1.278 c
+h
+30.075 -3.807 m
+29.406 -3.807 28.885 -3.612 28.502 -3.219 c
+28.127 -2.818 27.944 -2.246 27.944 -1.5 c
+27.944 -1.103 l
+27.944 -0.32 28.112 0.283 28.457 0.706 c
+28.8 1.124 29.296 1.338 29.943 1.338 c
+30.56 1.338 31.023 1.135 31.339 0.735 c
+31.663 0.341 31.824 -0.246 31.824 -1.029 c
+31.824 -1.675 l
+29.251 -1.675 l
+29.259 -2.04 29.34 -2.308 29.487 -2.484 c
+29.633 -2.654 29.854 -2.734 30.148 -2.734 c
+30.589 -2.734 30.949 -2.583 31.236 -2.278 c
+31.75 -3.072 l
+31.593 -3.289 31.361 -3.466 31.059 -3.601 c
+30.755 -3.738 30.427 -3.807 30.075 -3.807 c
+29.251 -0.765 m
+30.56 -0.765 l
+30.56 -0.647 l
+30.549 -0.345 30.494 -0.118 30.398 0.029 c
+30.31 0.183 30.152 0.264 29.928 0.264 c
+29.7 0.264 29.535 0.18 29.428 0.014 c
+29.329 -0.144 29.27 -0.405 29.251 -0.765 c
+36.263 -1.397 m
+36.263 -2.172 36.124 -2.771 35.852 -3.19 c
+35.587 -3.601 35.19 -3.807 34.661 -3.807 c
+34.257 -3.807 33.933 -3.645 33.691 -3.322 c
+33.691 -5.63 l
+32.382 -5.63 l
+32.382 1.249 l
+33.588 1.249 l
+33.631 0.794 l
+33.885 1.153 34.223 1.338 34.647 1.338 c
+35.175 1.338 35.572 1.147 35.837 0.764 c
+36.108 0.382 36.252 -0.214 36.263 -1.014 c
+h
+34.969 -1.058 m
+34.969 -0.58 34.915 -0.243 34.807 -0.044 c
+34.697 0.151 34.518 0.249 34.264 0.249 c
+33.999 0.249 33.808 0.139 33.691 -0.073 c
+33.691 -2.411 l
+33.808 -2.627 34.003 -2.734 34.279 -2.734 c
+34.532 -2.734 34.709 -2.627 34.807 -2.411 c
+34.915 -2.186 34.969 -1.849 34.969 -1.397 c
+h
+36.715 -1.073 m
+36.715 -0.32 36.891 0.268 37.244 0.691 c
+37.597 1.12 38.086 1.338 38.714 1.338 c
+39.35 1.338 39.846 1.12 40.199 0.691 c
+40.559 0.268 40.742 -0.324 40.742 -1.087 c
+40.742 -1.397 l
+40.742 -2.153 40.567 -2.745 40.214 -3.175 c
+39.861 -3.597 39.364 -3.807 38.728 -3.807 c
+38.09 -3.807 37.597 -3.597 37.244 -3.175 c
+36.891 -2.745 36.715 -2.153 36.715 -1.397 c
+h
+38.023 -1.397 m
+38.023 -2.29 38.258 -2.734 38.728 -2.734 c
+39.17 -2.734 39.405 -2.363 39.434 -1.617 c
+39.449 -1.073 l
+39.449 -0.625 39.383 -0.291 39.258 -0.073 c
+39.129 0.151 38.949 0.264 38.714 0.264 c
+38.497 0.264 38.325 0.151 38.2 -0.073 c
+38.082 -0.291 38.023 -0.625 38.023 -1.073 c
+h
+f
+Q
+q 1 0 0 1 662.4357 407.6318 cm
+0 0 m
+0 0.683 0.077 1.341 0.235 1.969 c
+0.389 2.605 0.632 3.167 0.955 3.659 c
+1.278 4.16 1.62 4.505 1.984 4.704 c
+2.219 3.983 l
+1.874 3.659 1.602 3.16 1.396 2.484 c
+1.198 1.815 1.095 1.043 1.087 0.162 c
+1.087 -0.118 l
+1.087 -1.04 1.182 -1.849 1.381 -2.543 c
+1.587 -3.238 1.866 -3.755 2.219 -4.102 c
+1.984 -4.807 l
+1.62 -4.619 1.278 -4.281 0.955 -3.793 c
+0.639 -3.3 0.4 -2.749 0.235 -2.132 c
+0.077 -1.515 0 -0.867 0 -0.191 c
+h
+4.031 2.248 m
+4.075 1.764 l
+4.369 2.146 4.751 2.337 5.221 2.337 c
+6.074 2.337 6.515 1.738 6.545 0.544 c
+6.545 -2.72 l
+5.236 -2.72 l
+5.236 0.455 l
+5.236 0.727 5.192 0.929 5.103 1.058 c
+5.023 1.183 4.876 1.249 4.663 1.249 c
+4.428 1.249 4.244 1.132 4.119 0.897 c
+4.119 -2.72 l
+2.811 -2.72 l
+2.811 2.248 l
+h
+9.653 -2.72 m
+9.612 -2.643 9.572 -2.514 9.535 -2.338 c
+9.308 -2.654 8.995 -2.808 8.595 -2.808 c
+8.172 -2.808 7.823 -2.672 7.551 -2.396 c
+7.287 -2.124 7.154 -1.764 7.154 -1.324 c
+7.154 -0.805 7.316 -0.405 7.64 -0.118 c
+7.97 0.165 8.452 0.312 9.08 0.323 c
+9.477 0.323 l
+9.477 0.72 l
+9.477 0.945 9.437 1.103 9.359 1.19 c
+9.278 1.278 9.168 1.323 9.022 1.323 c
+8.698 1.323 8.536 1.135 8.536 0.764 c
+7.243 0.764 l
+7.243 1.213 7.412 1.587 7.757 1.882 c
+8.099 2.183 8.536 2.337 9.065 2.337 c
+9.602 2.337 10.021 2.194 10.314 1.911 c
+10.616 1.635 10.77 1.234 10.77 0.706 c
+10.77 -1.632 l
+10.778 -2.066 10.844 -2.404 10.961 -2.646 c
+10.961 -2.72 l
+h
+8.889 -1.794 m
+9.024 -1.794 9.142 -1.764 9.242 -1.706 c
+9.348 -1.646 9.425 -1.58 9.477 -1.5 c
+9.477 -0.47 l
+9.168 -0.47 l
+8.941 -0.47 8.764 -0.541 8.639 -0.676 c
+8.511 -0.816 8.448 -1 8.448 -1.235 c
+8.448 -1.61 8.595 -1.794 8.889 -1.794 c
+12.755 2.248 m
+12.799 1.793 l
+13.093 2.153 13.475 2.337 13.945 2.337 c
+14.445 2.337 14.798 2.117 15.004 1.675 c
+15.287 2.117 15.694 2.337 16.224 2.337 c
+17.084 2.337 17.532 1.738 17.562 0.544 c
+17.562 -2.72 l
+16.253 -2.72 l
+16.253 0.455 l
+16.253 0.738 16.213 0.941 16.135 1.058 c
+16.055 1.183 15.922 1.249 15.739 1.249 c
+15.492 1.249 15.312 1.103 15.195 0.808 c
+15.195 -2.72 l
+13.901 -2.72 l
+13.901 0.44 l
+13.901 0.723 13.861 0.929 13.784 1.058 c
+13.703 1.183 13.567 1.249 13.372 1.249 c
+13.144 1.249 12.968 1.132 12.843 0.897 c
+12.843 -2.72 l
+11.549 -2.72 l
+11.549 2.248 l
+h
+20.251 -2.808 m
+19.582 -2.808 19.06 -2.613 18.678 -2.22 c
+18.304 -1.819 18.12 -1.246 18.12 -0.5 c
+18.12 -0.103 l
+18.12 0.679 18.289 1.282 18.634 1.705 c
+18.977 2.124 19.472 2.337 20.119 2.337 c
+20.737 2.337 21.2 2.135 21.516 1.734 c
+21.838 1.341 22 0.753 22 -0.03 c
+22 -0.676 l
+19.428 -0.676 l
+19.436 -1.04 19.517 -1.309 19.663 -1.484 c
+19.81 -1.654 20.031 -1.735 20.325 -1.735 c
+20.766 -1.735 21.126 -1.584 21.412 -1.279 c
+21.927 -2.072 l
+21.769 -2.29 21.537 -2.466 21.236 -2.602 c
+20.931 -2.738 20.604 -2.808 20.251 -2.808 c
+19.428 0.235 m
+20.737 0.235 l
+20.737 0.353 l
+20.725 0.654 20.67 0.881 20.575 1.028 c
+20.486 1.183 20.328 1.263 20.104 1.263 c
+19.877 1.263 19.711 1.18 19.605 1.014 c
+19.505 0.856 19.447 0.595 19.428 0.235 c
+22.662 -2.058 m
+22.662 -1.852 22.728 -1.683 22.867 -1.544 c
+23.014 -1.407 23.199 -1.338 23.426 -1.338 c
+23.64 -1.338 23.816 -1.407 23.955 -1.544 c
+24.103 -1.673 24.176 -1.845 24.176 -2.058 c
+24.176 -2.275 24.103 -2.448 23.955 -2.573 c
+23.816 -2.701 23.64 -2.764 23.426 -2.764 c
+23.199 -2.764 23.014 -2.701 22.867 -2.573 c
+22.728 -2.448 22.662 -2.275 22.662 -2.058 c
+22.662 1.705 m
+22.662 1.911 22.728 2.08 22.867 2.219 c
+23.014 2.356 23.199 2.425 23.426 2.425 c
+23.64 2.425 23.816 2.356 23.955 2.219 c
+24.103 2.09 24.176 1.918 24.176 1.705 c
+24.176 1.488 24.103 1.315 23.955 1.19 c
+23.816 1.062 23.64 0.999 23.426 0.999 c
+23.199 0.999 23.014 1.062 22.867 1.19 c
+22.728 1.315 22.662 1.488 22.662 1.705 c
+31.01 -0.397 m
+31.01 -1.172 30.871 -1.771 30.599 -2.19 c
+30.335 -2.602 29.938 -2.808 29.408 -2.808 c
+29.005 -2.808 28.681 -2.646 28.439 -2.323 c
+28.439 -4.63 l
+27.131 -4.63 l
+27.131 2.248 l
+28.336 2.248 l
+28.38 1.793 l
+28.633 2.153 28.972 2.337 29.394 2.337 c
+29.923 2.337 30.32 2.146 30.584 1.764 c
+30.856 1.381 31 0.786 31.01 -0.015 c
+h
+29.718 -0.059 m
+29.718 0.419 29.662 0.756 29.556 0.955 c
+29.445 1.15 29.265 1.249 29.012 1.249 c
+28.747 1.249 28.556 1.139 28.439 0.926 c
+28.439 -1.411 l
+28.556 -1.628 28.751 -1.735 29.026 -1.735 c
+29.281 -1.735 29.456 -1.628 29.556 -1.411 c
+29.662 -1.187 29.718 -0.849 29.718 -0.397 c
+h
+34.05 -2.264 m
+33.786 -2.627 33.425 -2.808 32.977 -2.808 c
+32.514 -2.808 32.169 -2.654 31.933 -2.338 c
+31.698 -2.025 31.581 -1.569 31.581 -0.971 c
+31.581 2.248 l
+32.874 2.248 l
+32.874 -1 l
+32.882 -1.482 33.043 -1.72 33.36 -1.72 c
+33.661 -1.72 33.877 -1.595 34.006 -1.338 c
+34.006 2.248 l
+35.314 2.248 l
+35.314 -2.72 l
+34.094 -2.72 l
+h
+39.933 -0.397 m
+39.933 -1.202 39.801 -1.804 39.536 -2.205 c
+39.28 -2.61 38.879 -2.808 38.331 -2.808 c
+37.898 -2.808 37.556 -2.617 37.302 -2.234 c
+37.258 -2.72 l
+36.053 -2.72 l
+36.053 4.336 l
+37.36 4.336 l
+37.36 1.851 l
+37.603 2.175 37.927 2.337 38.331 2.337 c
+38.879 2.337 39.28 2.135 39.536 1.734 c
+39.79 1.341 39.922 0.756 39.933 -0.015 c
+h
+38.64 -0.073 m
+38.64 0.455 38.584 0.808 38.478 0.985 c
+38.379 1.161 38.202 1.249 37.949 1.249 c
+37.674 1.249 37.479 1.124 37.36 0.881 c
+37.36 -1.382 l
+37.479 -1.617 37.677 -1.735 37.964 -1.735 c
+38.206 -1.735 38.379 -1.658 38.478 -1.5 c
+38.574 -1.334 38.629 -1.014 38.64 -0.544 c
+h
+41.903 -2.72 -1.309 7.056 re
+44.178 -2.72 -1.309 4.968 re
+42.81 3.542 m
+42.81 3.737 42.873 3.898 43.002 4.027 c
+43.126 4.163 43.299 4.233 43.516 4.233 c
+43.729 4.233 43.902 4.163 44.03 4.027 c
+44.155 3.898 44.221 3.737 44.221 3.542 c
+44.221 3.337 44.155 3.164 44.03 3.027 c
+43.913 2.899 43.741 2.836 43.516 2.836 c
+43.299 2.836 43.126 2.899 43.002 3.027 c
+42.873 3.164 42.81 3.337 42.81 3.542 c
+46.879 -1.735 m
+47.261 -1.735 47.452 -1.492 47.452 -1 c
+48.687 -1 l
+48.676 -1.54 48.503 -1.977 48.172 -2.308 c
+47.849 -2.643 47.426 -2.808 46.908 -2.808 c
+46.268 -2.808 45.78 -2.613 45.438 -2.22 c
+45.104 -1.819 44.938 -1.231 44.938 -0.456 c
+44.938 -0.059 l
+44.938 0.723 45.1 1.315 45.424 1.72 c
+45.754 2.131 46.25 2.337 46.908 2.337 c
+47.456 2.337 47.885 2.164 48.202 1.822 c
+48.514 1.488 48.676 1.014 48.687 0.397 c
+47.452 0.397 l
+47.452 0.669 47.408 0.881 47.319 1.028 c
+47.232 1.183 47.084 1.263 46.879 1.263 c
+46.651 1.263 46.489 1.18 46.393 1.014 c
+46.295 0.856 46.239 0.548 46.232 0.087 c
+46.232 -0.412 l
+46.232 -0.823 46.25 -1.11 46.291 -1.264 c
+46.328 -1.422 46.393 -1.54 46.482 -1.617 c
+46.577 -1.698 46.71 -1.735 46.879 -1.735 c
+51.336 -0.103 m
+51.336 -0.819 51.24 -1.496 51.057 -2.132 c
+50.881 -2.768 50.631 -3.326 50.307 -3.807 c
+49.983 -4.285 49.638 -4.619 49.278 -4.807 c
+49.043 -4.102 l
+49.385 -3.767 49.653 -3.256 49.852 -2.573 c
+50.058 -1.889 50.164 -1.11 50.175 -0.235 c
+50.175 0.014 l
+50.175 0.904 50.072 1.697 49.866 2.396 c
+49.668 3.102 49.395 3.634 49.043 3.998 c
+49.278 4.704 l
+49.513 4.586 49.752 4.384 49.998 4.101 c
+50.241 3.825 50.461 3.484 50.66 3.072 c
+50.866 2.66 51.028 2.194 51.145 1.675 c
+51.27 1.165 51.336 0.573 51.336 -0.103 c
+f
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm18 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm19 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm20 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm21 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm22 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm23 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm24 Do
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 585.9855 434.2035 cm
+0 0 m
+5.053 -5.074 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 589.5352 430.6393 cm
+0 0 m
+-1.51 0.003 l
+1.882 -1.889 l
+0 1.511 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 589.5352 430.6393 cm
+0 0 m
+-1.51 0.003 l
+1.882 -1.889 l
+0 1.511 l
+0 0 l
+h
+S
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm25 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm26 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm27 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm28 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm29 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm30 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm31 Do
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS3 gs
+0 TL/Fm32 Do
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 710.4792 434.3578 cm
+0 0 m
+-5.075 -5.056 l
+S
+Q
+q 1 0 0 1 706.9185 430.8089 cm
+0 0 m
+0 1.509 l
+-1.893 -1.882 l
+1.506 -0.005 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 706.9186 430.8089 cm
+0 0 m
+0 1.509 l
+-1.893 -1.882 l
+1.506 -0.005 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 695.2289 383.6354 cm
+0 0 m
+0 0.459 0.081 0.808 0.25 1.043 c
+0.426 1.286 0.676 1.411 1 1.411 c
+1.301 1.411 1.532 1.278 1.691 1.014 c
+1.72 1.352 l
+2.161 1.352 l
+2.161 -1.661 l
+2.161 -2.032 2.061 -2.315 1.866 -2.514 c
+1.679 -2.708 1.419 -2.807 1.088 -2.807 c
+0.941 -2.807 0.771 -2.768 0.588 -2.69 c
+0.401 -2.62 0.264 -2.532 0.177 -2.425 c
+0.368 -2.087 l
+0.573 -2.294 0.798 -2.396 1.043 -2.396 c
+1.444 -2.396 1.65 -2.168 1.661 -1.72 c
+1.661 -1.338 l
+1.503 -1.573 1.282 -1.69 1 -1.69 c
+0.684 -1.69 0.441 -1.573 0.264 -1.338 c
+0.096 -1.103 0.008 -0.771 0 -0.338 c
+h
+0.485 -0.279 m
+0.485 -0.613 0.533 -0.86 0.632 -1.014 c
+0.727 -1.172 0.889 -1.249 1.118 -1.249 c
+1.359 -1.249 1.544 -1.128 1.661 -0.882 c
+1.661 0.603 l
+1.544 0.845 1.367 0.97 1.132 0.97 c
+0.904 0.97 0.742 0.889 0.647 0.735 c
+0.548 0.577 0.492 0.338 0.485 0.015 c
+h
+3.227 -1.631 -0.501 2.983 re
+3.256 2.146 m
+3.256 2.057 3.231 1.984 3.183 1.926 c
+3.142 1.874 3.073 1.852 2.977 1.852 c
+2.888 1.852 2.818 1.874 2.771 1.926 c
+2.73 1.984 2.712 2.051 2.712 2.132 c
+2.712 2.219 2.73 2.293 2.771 2.352 c
+2.818 2.41 2.888 2.44 2.977 2.44 c
+3.073 2.44 3.142 2.41 3.183 2.352 c
+3.231 2.293 3.256 2.223 3.256 2.146 c
+4.531 2.072 m
+4.531 1.352 l
+4.987 1.352 l
+4.987 0.956 l
+4.531 0.956 l
+4.531 -0.897 l
+4.531 -1.014 4.549 -1.103 4.59 -1.161 c
+4.627 -1.22 4.696 -1.249 4.796 -1.249 c
+4.854 -1.249 4.917 -1.242 4.987 -1.22 c
+4.987 -1.631 l
+4.869 -1.669 4.755 -1.69 4.648 -1.69 c
+4.451 -1.69 4.299 -1.625 4.193 -1.484 c
+4.094 -1.349 4.046 -1.154 4.046 -0.897 c
+4.046 0.956 l
+3.59 0.956 l
+3.59 1.352 l
+4.046 1.352 l
+4.046 2.072 l
+h
+8.804 -0.279 m
+8.804 -0.75 8.721 -1.103 8.555 -1.338 c
+8.386 -1.573 8.147 -1.69 7.834 -1.69 c
+7.53 -1.69 7.298 -1.58 7.144 -1.353 c
+7.144 -2.778 l
+6.659 -2.778 l
+6.659 1.352 l
+7.1 1.352 l
+7.129 1.014 l
+7.283 1.278 7.515 1.411 7.82 1.411 c
+8.151 1.411 8.397 1.294 8.555 1.058 c
+8.721 0.831 8.804 0.492 8.804 0.044 c
+h
+8.32 0 m
+8.32 0.33 8.264 0.577 8.158 0.735 c
+8.058 0.889 7.898 0.97 7.673 0.97 c
+7.437 0.97 7.262 0.852 7.144 0.617 c
+7.144 -0.926 l
+7.262 -1.154 7.441 -1.264 7.688 -1.264 c
+7.9 -1.264 8.058 -1.187 8.158 -1.029 c
+8.264 -0.875 8.32 -0.632 8.32 -0.309 c
+h
+10.774 -1.367 m
+10.616 -1.584 10.381 -1.69 10.069 -1.69 c
+9.804 -1.69 9.598 -1.598 9.452 -1.411 c
+9.311 -1.228 9.246 -0.952 9.246 -0.588 c
+9.246 1.352 l
+9.731 1.352 l
+9.731 -0.559 l
+9.731 -1.022 9.87 -1.249 10.157 -1.249 c
+10.451 -1.249 10.649 -1.118 10.76 -0.852 c
+10.76 1.352 l
+11.26 1.352 l
+11.26 -1.631 l
+10.789 -1.631 l
+h
+13.251 -0.867 m
+13.251 -0.761 13.211 -0.673 13.134 -0.603 c
+13.053 -0.526 12.902 -0.437 12.678 -0.338 c
+12.413 -0.231 12.226 -0.14 12.12 -0.059 c
+12.01 0.018 11.932 0.106 11.884 0.206 c
+11.833 0.301 11.811 0.419 11.811 0.559 c
+11.811 0.801 11.898 1.003 12.075 1.161 c
+12.251 1.326 12.476 1.411 12.752 1.411 c
+13.045 1.411 13.28 1.323 13.457 1.147 c
+13.633 0.977 13.722 0.764 13.722 0.5 c
+13.236 0.5 l
+13.236 0.636 13.185 0.75 13.089 0.838 c
+13.001 0.933 12.887 0.985 12.752 0.985 c
+12.604 0.985 12.49 0.945 12.413 0.867 c
+12.332 0.798 12.295 0.698 12.295 0.573 c
+12.295 0.474 12.326 0.397 12.384 0.338 c
+12.443 0.279 12.582 0.198 12.81 0.103 c
+13.17 -0.044 13.417 -0.187 13.545 -0.324 c
+13.681 -0.453 13.751 -0.625 13.751 -0.838 c
+13.751 -1.095 13.656 -1.301 13.471 -1.455 c
+13.296 -1.613 13.06 -1.69 12.766 -1.69 c
+12.45 -1.69 12.197 -1.602 12.002 -1.426 c
+11.815 -1.242 11.723 -1.01 11.723 -0.735 c
+12.208 -0.735 l
+12.215 -0.904 12.266 -1.037 12.355 -1.132 c
+12.45 -1.22 12.59 -1.264 12.766 -1.264 c
+12.92 -1.264 13.038 -1.231 13.119 -1.161 c
+13.207 -1.095 13.251 -0.996 13.251 -0.867 c
+14.695 1.043 m
+14.879 1.286 15.114 1.411 15.401 1.411 c
+15.93 1.411 16.199 1.058 16.21 0.353 c
+16.21 -1.631 l
+15.724 -1.631 l
+15.724 0.324 l
+15.724 0.559 15.684 0.723 15.607 0.823 c
+15.526 0.918 15.408 0.97 15.254 0.97 c
+15.137 0.97 15.026 0.929 14.931 0.852 c
+14.832 0.771 14.755 0.665 14.695 0.529 c
+14.695 -1.631 l
+14.21 -1.631 l
+14.21 2.602 l
+14.695 2.602 l
+h
+20.101 -0.279 m
+20.101 -0.75 20.016 -1.103 19.852 -1.338 c
+19.682 -1.573 19.443 -1.69 19.131 -1.69 c
+18.826 -1.69 18.595 -1.58 18.44 -1.353 c
+18.44 -2.778 l
+17.955 -2.778 l
+17.955 1.352 l
+18.396 1.352 l
+18.425 1.014 l
+18.58 1.278 18.811 1.411 19.116 1.411 c
+19.447 1.411 19.693 1.294 19.852 1.058 c
+20.016 0.831 20.101 0.492 20.101 0.044 c
+h
+19.616 0 m
+19.616 0.33 19.561 0.577 19.455 0.735 c
+19.355 0.889 19.193 0.97 18.969 0.97 c
+18.734 0.97 18.558 0.852 18.44 0.617 c
+18.44 -0.926 l
+18.558 -1.154 18.738 -1.264 18.984 -1.264 c
+19.197 -1.264 19.355 -1.187 19.455 -1.029 c
+19.561 -0.875 19.616 -0.632 19.616 -0.309 c
+h
+22.071 -1.367 m
+21.913 -1.584 21.677 -1.69 21.365 -1.69 c
+21.101 -1.69 20.895 -1.598 20.748 -1.411 c
+20.608 -1.228 20.542 -0.952 20.542 -0.588 c
+20.542 1.352 l
+21.027 1.352 l
+21.027 -0.559 l
+21.027 -1.022 21.167 -1.249 21.454 -1.249 c
+21.747 -1.249 21.946 -1.118 22.056 -0.852 c
+22.056 1.352 l
+22.556 1.352 l
+22.556 -1.631 l
+22.086 -1.631 l
+h
+25.271 -0.279 m
+25.271 -0.75 25.186 -1.103 25.022 -1.338 c
+24.852 -1.573 24.61 -1.69 24.286 -1.69 c
+23.971 -1.69 23.736 -1.554 23.581 -1.278 c
+23.551 -1.631 l
+23.11 -1.631 l
+23.11 2.602 l
+23.596 2.602 l
+23.596 1.029 l
+23.75 1.282 23.981 1.411 24.286 1.411 c
+24.61 1.411 24.852 1.294 25.022 1.058 c
+25.186 0.823 25.271 0.474 25.271 0.015 c
+h
+24.787 0 m
+24.787 0.353 24.735 0.603 24.639 0.75 c
+24.54 0.897 24.378 0.97 24.154 0.97 c
+23.908 0.97 23.721 0.831 23.596 0.559 c
+23.596 -0.852 l
+23.713 -1.118 23.904 -1.249 24.169 -1.249 c
+24.382 -1.249 24.54 -1.176 24.639 -1.029 c
+24.735 -0.875 24.787 -0.632 24.787 -0.309 c
+h
+26.242 -1.631 -0.5 4.233 re
+27.447 -1.631 -0.5 2.983 re
+27.476 2.146 m
+27.476 2.057 27.451 1.984 27.403 1.926 c
+27.362 1.874 27.293 1.852 27.197 1.852 c
+27.108 1.852 27.039 1.874 26.991 1.926 c
+26.95 1.984 26.932 2.051 26.932 2.132 c
+26.932 2.219 26.95 2.293 26.991 2.352 c
+27.039 2.41 27.108 2.44 27.197 2.44 c
+27.293 2.44 27.362 2.41 27.403 2.352 c
+27.451 2.293 27.476 2.223 27.476 2.146 c
+29.148 -1.264 m
+29.313 -1.264 29.446 -1.216 29.545 -1.118 c
+29.641 -1.022 29.695 -0.879 29.707 -0.691 c
+30.163 -0.691 l
+30.152 -0.977 30.048 -1.216 29.853 -1.411 c
+29.666 -1.598 29.431 -1.69 29.148 -1.69 c
+28.785 -1.69 28.505 -1.573 28.311 -1.338 c
+28.112 -1.103 28.016 -0.757 28.016 -0.294 c
+28.016 0.029 l
+28.016 0.478 28.108 0.823 28.296 1.058 c
+28.49 1.294 28.773 1.411 29.148 1.411 c
+29.45 1.411 29.693 1.311 29.868 1.117 c
+30.052 0.918 30.152 0.654 30.163 0.324 c
+29.707 0.324 l
+29.685 0.548 29.626 0.713 29.531 0.823 c
+29.442 0.929 29.313 0.985 29.148 0.985 c
+28.932 0.985 28.77 0.912 28.663 0.764 c
+28.564 0.625 28.509 0.397 28.502 0.073 c
+28.502 -0.309 l
+28.502 -0.661 28.55 -0.912 28.648 -1.058 c
+28.755 -1.199 28.92 -1.264 29.148 -1.264 c
+32.22 1.352 m
+32.235 1.073 l
+32.411 1.297 32.65 1.411 32.955 1.411 c
+33.286 1.411 33.518 1.264 33.647 0.97 c
+33.83 1.264 34.091 1.411 34.425 1.411 c
+34.984 1.411 35.266 1.066 35.278 0.382 c
+35.278 -1.631 l
+34.792 -1.631 l
+34.792 0.338 l
+34.792 0.551 34.752 0.709 34.675 0.808 c
+34.594 0.915 34.462 0.97 34.279 0.97 c
+34.131 0.97 34.013 0.912 33.926 0.794 c
+33.838 0.683 33.782 0.544 33.764 0.368 c
+33.764 -1.631 l
+33.279 -1.631 l
+33.279 0.353 l
+33.267 0.764 33.095 0.97 32.764 0.97 c
+32.518 0.97 32.346 0.845 32.25 0.603 c
+32.25 -1.631 l
+31.765 -1.631 l
+31.765 1.352 l
+h
+37.255 -1.631 m
+37.225 -1.565 37.203 -1.455 37.196 -1.309 c
+37.02 -1.565 36.799 -1.69 36.535 -1.69 c
+36.259 -1.69 36.043 -1.617 35.887 -1.47 c
+35.741 -1.316 35.667 -1.099 35.667 -0.823 c
+35.667 -0.522 35.77 -0.279 35.976 -0.103 c
+36.182 0.081 36.465 0.176 36.828 0.176 c
+37.181 0.176 l
+37.181 0.5 l
+37.181 0.676 37.141 0.798 37.063 0.867 c
+36.983 0.945 36.866 0.985 36.711 0.985 c
+36.564 0.985 36.439 0.941 36.344 0.852 c
+36.255 0.764 36.211 0.654 36.211 0.529 c
+35.727 0.529 l
+35.727 0.676 35.77 0.816 35.858 0.956 c
+35.947 1.103 36.064 1.213 36.211 1.294 c
+36.365 1.371 36.538 1.411 36.726 1.411 c
+37.038 1.411 37.273 1.33 37.431 1.176 c
+37.585 1.029 37.666 0.808 37.666 0.515 c
+37.666 -0.985 l
+37.674 -1.22 37.711 -1.422 37.769 -1.588 c
+37.769 -1.631 l
+h
+36.608 -1.249 m
+36.726 -1.249 36.836 -1.216 36.946 -1.147 c
+37.053 -1.081 37.13 -0.996 37.181 -0.897 c
+37.181 -0.191 l
+36.917 -0.191 l
+36.681 -0.191 36.494 -0.243 36.358 -0.338 c
+36.23 -0.437 36.167 -0.58 36.167 -0.765 c
+36.167 -0.933 36.197 -1.055 36.255 -1.132 c
+36.321 -1.213 36.439 -1.249 36.608 -1.249 c
+39.688 -0.867 m
+39.688 -0.761 39.647 -0.673 39.57 -0.603 c
+39.49 -0.526 39.338 -0.437 39.114 -0.338 c
+38.85 -0.231 38.663 -0.14 38.555 -0.059 c
+38.445 0.018 38.368 0.106 38.32 0.206 c
+38.269 0.301 38.247 0.419 38.247 0.559 c
+38.247 0.801 38.335 1.003 38.512 1.161 c
+38.688 1.326 38.912 1.411 39.187 1.411 c
+39.482 1.411 39.717 1.323 39.893 1.147 c
+40.07 0.977 40.158 0.764 40.158 0.5 c
+39.673 0.5 l
+39.673 0.636 39.621 0.75 39.526 0.838 c
+39.438 0.933 39.324 0.985 39.187 0.985 c
+39.041 0.985 38.927 0.945 38.85 0.867 c
+38.769 0.798 38.732 0.698 38.732 0.573 c
+38.732 0.474 38.761 0.397 38.821 0.338 c
+38.879 0.279 39.019 0.198 39.247 0.103 c
+39.607 -0.044 39.853 -0.187 39.981 -0.324 c
+40.118 -0.453 40.188 -0.625 40.188 -0.838 c
+40.188 -1.095 40.091 -1.301 39.908 -1.455 c
+39.731 -1.613 39.496 -1.69 39.203 -1.69 c
+38.886 -1.69 38.632 -1.602 38.438 -1.426 c
+38.25 -1.242 38.159 -1.01 38.159 -0.735 c
+38.644 -0.735 l
+38.651 -0.904 38.703 -1.037 38.79 -1.132 c
+38.886 -1.22 39.026 -1.264 39.203 -1.264 c
+39.357 -1.264 39.474 -1.231 39.555 -1.161 c
+39.644 -1.095 39.688 -0.996 39.688 -0.867 c
+41.275 2.072 m
+41.275 1.352 l
+41.73 1.352 l
+41.73 0.956 l
+41.275 0.956 l
+41.275 -0.897 l
+41.275 -1.014 41.294 -1.103 41.334 -1.161 c
+41.371 -1.22 41.441 -1.249 41.539 -1.249 c
+41.599 -1.249 41.661 -1.242 41.73 -1.22 c
+41.73 -1.631 l
+41.613 -1.669 41.499 -1.69 41.393 -1.69 c
+41.194 -1.69 41.044 -1.625 40.937 -1.484 c
+40.838 -1.349 40.79 -1.154 40.79 -0.897 c
+40.79 0.956 l
+40.334 0.956 l
+40.334 1.352 l
+40.79 1.352 l
+40.79 2.072 l
+h
+43.256 -1.69 m
+42.881 -1.69 42.598 -1.584 42.403 -1.367 c
+42.204 -1.143 42.109 -0.816 42.109 -0.382 c
+42.109 -0.015 l
+42.109 0.426 42.201 0.771 42.389 1.029 c
+42.584 1.282 42.859 1.411 43.212 1.411 c
+43.553 1.411 43.807 1.297 43.976 1.073 c
+44.153 0.845 44.244 0.5 44.255 0.029 c
+44.255 -0.279 l
+42.594 -0.279 l
+42.594 -0.353 l
+42.594 -0.676 42.653 -0.912 42.771 -1.058 c
+42.888 -1.199 43.058 -1.264 43.285 -1.264 c
+43.432 -1.264 43.557 -1.242 43.667 -1.191 c
+43.774 -1.132 43.877 -1.043 43.976 -0.926 c
+44.226 -1.235 l
+44.02 -1.54 43.696 -1.69 43.256 -1.69 c
+43.212 0.985 m
+43.006 0.985 42.852 0.915 42.756 0.779 c
+42.657 0.64 42.601 0.426 42.594 0.133 c
+43.77 0.133 l
+43.77 0.206 l
+43.748 0.478 43.696 0.676 43.608 0.794 c
+43.521 0.918 43.388 0.985 43.212 0.985 c
+45.902 0.897 m
+45.832 0.904 45.759 0.912 45.681 0.912 c
+45.424 0.912 45.248 0.771 45.152 0.5 c
+45.152 -1.631 l
+44.667 -1.631 l
+44.667 1.352 l
+45.138 1.352 l
+45.152 1.043 l
+45.277 1.286 45.46 1.411 45.695 1.411 c
+45.773 1.411 45.836 1.396 45.886 1.367 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 672.622 356.2839 cm
+0 0 m
+-0.114 20.002 26.63 15.225 20.909 37.795 c
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 694.0562 392.01 cm
+0 0 m
+1.298 -0.771 l
+-0.654 2.583 l
+-0.772 -1.297 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 694.0562 392.01 cm
+0 0 m
+1.298 -0.771 l
+-0.654 2.584 l
+-0.772 -1.297 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 635.2201 383.5761 cm
+0 0 m
+0 0.46 0.081 0.809 0.25 1.044 c
+0.426 1.287 0.676 1.411 0.999 1.411 c
+1.301 1.411 1.532 1.279 1.691 1.015 c
+1.72 1.353 l
+2.161 1.353 l
+2.161 -1.66 l
+2.161 -2.032 2.061 -2.315 1.866 -2.513 c
+1.679 -2.708 1.419 -2.807 1.087 -2.807 c
+0.941 -2.807 0.771 -2.767 0.588 -2.69 c
+0.401 -2.62 0.264 -2.532 0.176 -2.425 c
+0.368 -2.087 l
+0.573 -2.293 0.798 -2.395 1.043 -2.395 c
+1.444 -2.395 1.65 -2.167 1.661 -1.72 c
+1.661 -1.337 l
+1.503 -1.572 1.282 -1.69 0.999 -1.69 c
+0.684 -1.69 0.441 -1.572 0.264 -1.337 c
+0.096 -1.102 0.008 -0.771 0 -0.338 c
+h
+0.485 -0.278 m
+0.485 -0.613 0.532 -0.86 0.632 -1.014 c
+0.727 -1.172 0.889 -1.249 1.117 -1.249 c
+1.359 -1.249 1.544 -1.128 1.661 -0.881 c
+1.661 0.603 l
+1.544 0.846 1.367 0.971 1.132 0.971 c
+0.904 0.971 0.742 0.89 0.646 0.736 c
+0.547 0.578 0.492 0.339 0.485 0.015 c
+h
+3.223 -1.631 -0.5 2.984 re
+3.252 2.147 m
+3.252 2.058 3.227 1.985 3.179 1.926 c
+3.138 1.875 3.069 1.852 2.973 1.852 c
+2.884 1.852 2.815 1.875 2.767 1.926 c
+2.726 1.985 2.708 2.051 2.708 2.132 c
+2.708 2.22 2.726 2.294 2.767 2.352 c
+2.815 2.411 2.884 2.44 2.973 2.44 c
+3.069 2.44 3.138 2.411 3.179 2.352 c
+3.227 2.294 3.252 2.224 3.252 2.147 c
+4.531 2.073 m
+4.531 1.353 l
+4.987 1.353 l
+4.987 0.956 l
+4.531 0.956 l
+4.531 -0.897 l
+4.531 -1.014 4.549 -1.102 4.59 -1.161 c
+4.627 -1.219 4.696 -1.249 4.796 -1.249 c
+4.854 -1.249 4.917 -1.242 4.987 -1.219 c
+4.987 -1.631 l
+4.869 -1.668 4.755 -1.69 4.648 -1.69 c
+4.45 -1.69 4.299 -1.624 4.193 -1.484 c
+4.094 -1.348 4.046 -1.153 4.046 -0.897 c
+4.046 0.956 l
+3.59 0.956 l
+3.59 1.353 l
+4.046 1.353 l
+4.046 2.073 l
+h
+8.801 -0.278 m
+8.801 -0.749 8.717 -1.102 8.551 -1.337 c
+8.382 -1.572 8.143 -1.69 7.831 -1.69 c
+7.526 -1.69 7.294 -1.579 7.14 -1.352 c
+7.14 -2.778 l
+6.655 -2.778 l
+6.655 1.353 l
+7.096 1.353 l
+7.125 1.015 l
+7.279 1.279 7.511 1.411 7.817 1.411 c
+8.147 1.411 8.393 1.293 8.551 1.058 c
+8.717 0.831 8.801 0.493 8.801 0.044 c
+h
+8.316 0 m
+8.316 0.331 8.261 0.578 8.154 0.736 c
+8.055 0.89 7.894 0.971 7.669 0.971 c
+7.434 0.971 7.258 0.853 7.14 0.618 c
+7.14 -0.926 l
+7.258 -1.153 7.437 -1.263 7.684 -1.263 c
+7.897 -1.263 8.055 -1.186 8.154 -1.028 c
+8.261 -0.874 8.316 -0.631 8.316 -0.309 c
+h
+10.774 -1.367 m
+10.616 -1.583 10.381 -1.69 10.069 -1.69 c
+9.804 -1.69 9.598 -1.598 9.452 -1.411 c
+9.311 -1.227 9.246 -0.951 9.246 -0.588 c
+9.246 1.353 l
+9.731 1.353 l
+9.731 -0.558 l
+9.731 -1.022 9.87 -1.249 10.157 -1.249 c
+10.451 -1.249 10.649 -1.117 10.759 -0.852 c
+10.759 1.353 l
+11.26 1.353 l
+11.26 -1.631 l
+10.789 -1.631 l
+h
+13.251 -0.866 m
+13.251 -0.76 13.211 -0.672 13.134 -0.602 c
+13.053 -0.525 12.902 -0.436 12.678 -0.338 c
+12.413 -0.231 12.226 -0.139 12.12 -0.058 c
+12.01 0.019 11.932 0.107 11.884 0.206 c
+11.833 0.302 11.811 0.42 11.811 0.559 c
+11.811 0.802 11.898 1.004 12.075 1.162 c
+12.251 1.327 12.476 1.411 12.752 1.411 c
+13.045 1.411 13.28 1.324 13.457 1.147 c
+13.633 0.977 13.722 0.765 13.722 0.5 c
+13.236 0.5 l
+13.236 0.636 13.185 0.75 13.089 0.838 c
+13.001 0.934 12.887 0.985 12.752 0.985 c
+12.604 0.985 12.49 0.944 12.413 0.867 c
+12.332 0.798 12.295 0.699 12.295 0.574 c
+12.295 0.474 12.326 0.397 12.384 0.339 c
+12.443 0.279 12.582 0.198 12.81 0.103 c
+13.17 -0.043 13.417 -0.187 13.545 -0.323 c
+13.681 -0.452 13.751 -0.625 13.751 -0.837 c
+13.751 -1.095 13.656 -1.3 13.471 -1.454 c
+13.295 -1.612 13.06 -1.69 12.766 -1.69 c
+12.45 -1.69 12.197 -1.602 12.002 -1.425 c
+11.815 -1.242 11.723 -1.01 11.723 -0.735 c
+12.208 -0.735 l
+12.215 -0.904 12.266 -1.036 12.355 -1.132 c
+12.45 -1.219 12.59 -1.263 12.766 -1.263 c
+12.92 -1.263 13.038 -1.23 13.119 -1.161 c
+13.207 -1.095 13.251 -0.995 13.251 -0.866 c
+14.692 1.044 m
+14.875 1.287 15.11 1.411 15.397 1.411 c
+15.927 1.411 16.195 1.058 16.206 0.353 c
+16.206 -1.631 l
+15.721 -1.631 l
+15.721 0.324 l
+15.721 0.559 15.68 0.724 15.603 0.823 c
+15.522 0.919 15.405 0.971 15.25 0.971 c
+15.133 0.971 15.023 0.93 14.927 0.853 c
+14.828 0.772 14.751 0.665 14.692 0.53 c
+14.692 -1.631 l
+14.207 -1.631 l
+14.207 2.602 l
+14.692 2.602 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 656.8204 356.6593 cm
+0 0 m
+-0.118 20.001 -28.66 16.157 -38.177 39.485 c
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 619.4479 394.1704 cm
+0 0 m
+1.393 -0.584 l
+-1.007 2.47 l
+-0.584 -1.393 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 619.4479 394.1704 cm
+0 0 m
+1.393 -0.584 l
+-1.007 2.47 l
+-0.584 -1.393 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 588.0693 386.1895 cm
+0 0 m
+0 0.459 0.081 0.808 0.249 1.043 c
+0.426 1.286 0.676 1.411 0.999 1.411 c
+1.301 1.411 1.533 1.278 1.691 1.014 c
+1.72 1.352 l
+2.161 1.352 l
+2.161 -1.661 l
+2.161 -2.032 2.061 -2.315 1.866 -2.514 c
+1.679 -2.708 1.419 -2.808 1.088 -2.808 c
+0.941 -2.808 0.771 -2.768 0.588 -2.691 c
+0.401 -2.62 0.264 -2.532 0.176 -2.425 c
+0.368 -2.087 l
+0.573 -2.294 0.798 -2.396 1.043 -2.396 c
+1.444 -2.396 1.65 -2.168 1.66 -1.72 c
+1.66 -1.338 l
+1.503 -1.573 1.282 -1.69 0.999 -1.69 c
+0.683 -1.69 0.441 -1.573 0.264 -1.338 c
+0.095 -1.103 0.008 -0.772 0 -0.339 c
+h
+0.484 -0.279 m
+0.484 -0.614 0.532 -0.86 0.632 -1.014 c
+0.727 -1.172 0.889 -1.249 1.117 -1.249 c
+1.359 -1.249 1.544 -1.128 1.66 -0.882 c
+1.66 0.602 l
+1.544 0.845 1.367 0.97 1.132 0.97 c
+0.904 0.97 0.742 0.889 0.646 0.735 c
+0.548 0.577 0.492 0.338 0.484 0.014 c
+h
+3.227 -1.632 -0.5 2.984 re
+3.256 2.146 m
+3.256 2.057 3.23 1.984 3.183 1.926 c
+3.142 1.874 3.072 1.851 2.977 1.851 c
+2.888 1.851 2.818 1.874 2.771 1.926 c
+2.73 1.984 2.712 2.05 2.712 2.131 c
+2.712 2.219 2.73 2.293 2.771 2.352 c
+2.818 2.41 2.888 2.439 2.977 2.439 c
+3.072 2.439 3.142 2.41 3.183 2.352 c
+3.23 2.293 3.256 2.223 3.256 2.146 c
+4.534 2.072 m
+4.534 1.352 l
+4.991 1.352 l
+4.991 0.955 l
+4.534 0.955 l
+4.534 -0.897 l
+4.534 -1.014 4.553 -1.103 4.594 -1.162 c
+4.63 -1.22 4.7 -1.249 4.799 -1.249 c
+4.858 -1.249 4.92 -1.243 4.991 -1.22 c
+4.991 -1.632 l
+4.873 -1.669 4.758 -1.69 4.652 -1.69 c
+4.453 -1.69 4.303 -1.625 4.197 -1.484 c
+4.097 -1.349 4.05 -1.154 4.05 -0.897 c
+4.05 0.955 l
+3.594 0.955 l
+3.594 1.352 l
+4.05 1.352 l
+4.05 2.072 l
+h
+6.865 -1.632 m
+6.865 0.955 l
+6.483 0.955 l
+6.483 1.352 l
+6.865 1.352 l
+6.865 1.691 l
+6.872 1.992 6.953 2.227 7.1 2.396 c
+7.247 2.572 7.456 2.66 7.732 2.66 c
+7.827 2.66 7.927 2.645 8.025 2.616 c
+7.996 2.204 l
+7.927 2.212 7.853 2.219 7.775 2.219 c
+7.501 2.219 7.364 2.021 7.364 1.631 c
+7.364 1.352 l
+7.864 1.352 l
+7.864 0.955 l
+7.364 0.955 l
+7.364 -1.632 l
+h
+9.323 -1.69 m
+8.948 -1.69 8.665 -1.584 8.47 -1.367 c
+8.272 -1.143 8.176 -0.816 8.176 -0.383 c
+8.176 -0.015 l
+8.176 0.426 8.268 0.771 8.455 1.028 c
+8.65 1.282 8.926 1.411 9.279 1.411 c
+9.62 1.411 9.874 1.297 10.043 1.072 c
+10.219 0.845 10.311 0.5 10.323 0.029 c
+10.323 -0.279 l
+8.661 -0.279 l
+8.661 -0.353 l
+8.661 -0.676 8.72 -0.912 8.837 -1.058 c
+8.955 -1.199 9.124 -1.264 9.352 -1.264 c
+9.5 -1.264 9.624 -1.243 9.735 -1.191 c
+9.841 -1.132 9.944 -1.044 10.043 -0.927 c
+10.293 -1.235 l
+10.088 -1.54 9.764 -1.69 9.323 -1.69 c
+9.279 0.985 m
+9.073 0.985 8.918 0.914 8.823 0.779 c
+8.724 0.639 8.669 0.426 8.661 0.132 c
+9.837 0.132 l
+9.837 0.205 l
+9.815 0.478 9.764 0.675 9.675 0.793 c
+9.587 0.918 9.455 0.985 9.279 0.985 c
+11.366 2.072 m
+11.366 1.352 l
+11.821 1.352 l
+11.821 0.955 l
+11.366 0.955 l
+11.366 -0.897 l
+11.366 -1.014 11.384 -1.103 11.424 -1.162 c
+11.461 -1.22 11.532 -1.249 11.63 -1.249 c
+11.69 -1.249 11.752 -1.243 11.821 -1.22 c
+11.821 -1.632 l
+11.704 -1.669 11.59 -1.69 11.484 -1.69 c
+11.285 -1.69 11.135 -1.625 11.028 -1.484 c
+10.929 -1.349 10.881 -1.154 10.881 -0.897 c
+10.881 0.955 l
+10.425 0.955 l
+10.425 1.352 l
+10.881 1.352 l
+10.881 2.072 l
+h
+13.317 -1.264 m
+13.483 -1.264 13.615 -1.216 13.714 -1.118 c
+13.81 -1.022 13.865 -0.879 13.876 -0.691 c
+14.331 -0.691 l
+14.321 -0.977 14.217 -1.216 14.023 -1.411 c
+13.836 -1.598 13.6 -1.69 13.317 -1.69 c
+12.953 -1.69 12.675 -1.573 12.479 -1.338 c
+12.281 -1.103 12.185 -0.757 12.185 -0.294 c
+12.185 0.029 l
+12.185 0.478 12.278 0.823 12.465 1.058 c
+12.66 1.294 12.943 1.411 13.317 1.411 c
+13.618 1.411 13.861 1.311 14.038 1.117 c
+14.221 0.918 14.321 0.654 14.331 0.323 c
+13.876 0.323 l
+13.853 0.548 13.795 0.713 13.699 0.823 c
+13.612 0.929 13.483 0.985 13.317 0.985 c
+13.101 0.985 12.939 0.911 12.833 0.764 c
+12.733 0.625 12.677 0.397 12.671 0.073 c
+12.671 -0.309 l
+12.671 -0.661 12.718 -0.912 12.818 -1.058 c
+12.924 -1.199 13.089 -1.264 13.317 -1.264 c
+15.21 1.043 m
+15.393 1.286 15.628 1.411 15.915 1.411 c
+16.444 1.411 16.712 1.058 16.724 0.353 c
+16.724 -1.632 l
+16.239 -1.632 l
+16.239 0.323 l
+16.239 0.558 16.199 0.723 16.122 0.823 c
+16.041 0.918 15.923 0.97 15.769 0.97 c
+15.651 0.97 15.54 0.929 15.445 0.852 c
+15.345 0.771 15.268 0.665 15.21 0.529 c
+15.21 -1.632 l
+14.724 -1.632 l
+14.724 2.601 l
+15.21 2.601 l
+h
+f
+Q
+q 1 0 0 1 594.5256 379.6043 cm
+0 0 m
+0 0.43 0.104 0.771 0.309 1.028 c
+0.522 1.282 0.802 1.411 1.147 1.411 c
+1.488 1.411 1.764 1.282 1.97 1.028 c
+2.182 0.783 2.297 0.448 2.308 0.029 c
+2.308 -0.279 l
+2.308 -0.713 2.198 -1.055 1.985 -1.309 c
+1.779 -1.565 1.5 -1.691 1.147 -1.691 c
+0.802 -1.691 0.53 -1.569 0.324 -1.324 c
+0.118 -1.081 0.008 -0.746 0 -0.324 c
+h
+0.485 -0.279 m
+0.485 -0.595 0.544 -0.838 0.661 -1.014 c
+0.786 -1.183 0.948 -1.264 1.147 -1.264 c
+1.577 -1.264 1.801 -0.956 1.823 -0.339 c
+1.823 0 l
+1.823 0.301 1.756 0.544 1.632 0.72 c
+1.515 0.897 1.353 0.985 1.147 0.985 c
+0.948 0.985 0.786 0.897 0.661 0.72 c
+0.544 0.544 0.485 0.301 0.485 0 c
+h
+3.984 0.897 m
+3.914 0.904 3.84 0.911 3.763 0.911 c
+3.506 0.911 3.329 0.771 3.234 0.5 c
+3.234 -1.632 l
+2.749 -1.632 l
+2.749 1.352 l
+3.219 1.352 l
+3.234 1.043 l
+3.358 1.286 3.543 1.411 3.778 1.411 c
+3.855 1.411 3.917 1.396 3.969 1.367 c
+h
+f
+Q
+q 1 0 0 1 589.5942 373.0192 cm
+0 0 m
+0 0.459 0.081 0.808 0.25 1.043 c
+0.426 1.286 0.676 1.411 0.999 1.411 c
+1.301 1.411 1.532 1.278 1.691 1.014 c
+1.72 1.352 l
+2.161 1.352 l
+2.161 -1.661 l
+2.161 -2.032 2.061 -2.315 1.866 -2.514 c
+1.679 -2.708 1.419 -2.808 1.087 -2.808 c
+0.941 -2.808 0.771 -2.768 0.588 -2.691 c
+0.401 -2.62 0.264 -2.533 0.176 -2.425 c
+0.368 -2.087 l
+0.573 -2.294 0.798 -2.396 1.043 -2.396 c
+1.444 -2.396 1.65 -2.168 1.661 -1.72 c
+1.661 -1.338 l
+1.503 -1.573 1.282 -1.691 0.999 -1.691 c
+0.684 -1.691 0.441 -1.573 0.264 -1.338 c
+0.096 -1.103 0.008 -0.772 0 -0.339 c
+h
+0.485 -0.279 m
+0.485 -0.614 0.532 -0.86 0.632 -1.014 c
+0.727 -1.172 0.889 -1.249 1.117 -1.249 c
+1.359 -1.249 1.544 -1.128 1.661 -0.882 c
+1.661 0.602 l
+1.544 0.845 1.367 0.97 1.132 0.97 c
+0.904 0.97 0.742 0.889 0.646 0.735 c
+0.547 0.577 0.492 0.338 0.485 0.014 c
+h
+3.223 -1.632 -0.5 2.984 re
+3.252 2.146 m
+3.252 2.057 3.227 1.984 3.179 1.926 c
+3.138 1.874 3.069 1.851 2.973 1.851 c
+2.884 1.851 2.815 1.874 2.767 1.926 c
+2.726 1.984 2.708 2.05 2.708 2.131 c
+2.708 2.219 2.726 2.293 2.767 2.352 c
+2.815 2.41 2.884 2.439 2.973 2.439 c
+3.069 2.439 3.138 2.41 3.179 2.352 c
+3.227 2.293 3.252 2.223 3.252 2.146 c
+4.531 2.072 m
+4.531 1.352 l
+4.987 1.352 l
+4.987 0.955 l
+4.531 0.955 l
+4.531 -0.897 l
+4.531 -1.014 4.549 -1.103 4.59 -1.162 c
+4.627 -1.22 4.696 -1.249 4.796 -1.249 c
+4.854 -1.249 4.917 -1.243 4.987 -1.22 c
+4.987 -1.632 l
+4.869 -1.669 4.755 -1.691 4.648 -1.691 c
+4.45 -1.691 4.299 -1.625 4.193 -1.484 c
+4.094 -1.349 4.046 -1.154 4.046 -0.897 c
+4.046 0.955 l
+3.59 0.955 l
+3.59 1.352 l
+4.046 1.352 l
+4.046 2.072 l
+h
+8.801 -0.279 m
+8.801 -0.75 8.717 -1.103 8.551 -1.338 c
+8.382 -1.573 8.143 -1.691 7.831 -1.691 c
+7.526 -1.691 7.294 -1.58 7.14 -1.353 c
+7.14 -2.778 l
+6.655 -2.778 l
+6.655 1.352 l
+7.096 1.352 l
+7.125 1.014 l
+7.279 1.278 7.511 1.411 7.817 1.411 c
+8.147 1.411 8.393 1.294 8.551 1.058 c
+8.717 0.831 8.801 0.492 8.801 0.044 c
+h
+8.316 0 m
+8.316 0.33 8.261 0.577 8.154 0.735 c
+8.055 0.889 7.894 0.97 7.669 0.97 c
+7.434 0.97 7.258 0.852 7.14 0.617 c
+7.14 -0.927 l
+7.258 -1.154 7.437 -1.264 7.684 -1.264 c
+7.897 -1.264 8.055 -1.187 8.154 -1.029 c
+8.261 -0.875 8.316 -0.632 8.316 -0.309 c
+h
+10.774 -1.367 m
+10.616 -1.584 10.381 -1.691 10.069 -1.691 c
+9.804 -1.691 9.598 -1.598 9.452 -1.411 c
+9.311 -1.228 9.246 -0.952 9.246 -0.588 c
+9.246 1.352 l
+9.731 1.352 l
+9.731 -0.559 l
+9.731 -1.022 9.87 -1.249 10.157 -1.249 c
+10.451 -1.249 10.649 -1.118 10.759 -0.853 c
+10.759 1.352 l
+11.26 1.352 l
+11.26 -1.632 l
+10.789 -1.632 l
+h
+12.34 -1.632 -0.5 4.233 re
+13.545 -1.632 -0.5 4.233 re
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 600.5115 395.7545 cm
+0 0 m
+19.682 -14.736 18.124 -46.64 36.296 -49.646 c
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 634.7023 346.454 cm
+0 0 m
+-1.228 -0.878 l
+2.631 -0.433 l
+-0.878 1.227 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 634.7023 346.4541 cm
+0 0 m
+-1.228 -0.878 l
+2.631 -0.433 l
+-0.878 1.227 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+535.667 213.99 238.665 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 571.6026 198.5045 cm
+0 0 m
+0 9.363 l
+2.484 9.363 l
+3.572 9.363 4.439 9.014 5.086 8.32 c
+5.74 7.621 6.074 6.674 6.086 5.468 c
+6.086 3.954 l
+6.086 2.72 5.762 1.749 5.115 1.043 c
+4.469 0.345 3.572 0 2.425 0 c
+h
+1.897 7.79 m
+1.897 1.573 l
+2.454 1.573 l
+3.09 1.573 3.535 1.738 3.792 2.072 c
+4.046 2.404 4.178 2.977 4.189 3.792 c
+4.189 5.409 l
+4.189 6.291 4.064 6.901 3.821 7.247 c
+3.586 7.588 3.175 7.769 2.587 7.79 c
+h
+6.913 0.926 m
+6.913 1.22 7.008 1.459 7.206 1.646 c
+7.401 1.83 7.655 1.926 7.971 1.926 c
+8.272 1.926 8.522 1.83 8.72 1.646 c
+8.926 1.459 9.029 1.22 9.029 0.926 c
+9.029 0.621 8.926 0.374 8.72 0.191 c
+8.522 0.015 8.272 -0.073 7.971 -0.073 c
+7.665 -0.073 7.412 0.018 7.206 0.206 c
+7.008 0.389 6.913 0.632 6.913 0.926 c
+20.266 3.116 m
+20.215 2.047 19.914 1.238 19.355 0.691 c
+18.804 0.139 18.032 -0.133 17.032 -0.133 c
+15.963 -0.133 15.144 0.213 14.578 0.912 c
+14.019 1.606 13.74 2.602 13.74 3.896 c
+13.74 5.468 l
+13.74 6.761 14.026 7.754 14.607 8.452 c
+15.195 9.147 16.008 9.496 17.047 9.496 c
+18.065 9.496 18.84 9.205 19.37 8.628 c
+19.898 8.058 20.201 7.239 20.281 6.173 c
+18.385 6.173 l
+18.362 6.838 18.26 7.295 18.076 7.541 c
+17.889 7.794 17.547 7.923 17.047 7.923 c
+16.548 7.923 16.187 7.746 15.974 7.393 c
+15.769 7.041 15.655 6.456 15.636 5.644 c
+15.636 3.881 l
+15.636 2.947 15.739 2.308 15.945 1.955 c
+16.158 1.61 16.521 1.44 17.032 1.44 c
+17.521 1.44 17.859 1.558 18.047 1.793 c
+18.242 2.036 18.348 2.477 18.37 3.116 c
+h
+28.244 3.836 m
+28.244 2.58 27.943 1.606 27.347 0.912 c
+26.749 0.213 25.926 -0.133 24.878 -0.133 c
+23.827 -0.133 23 0.21 22.394 0.897 c
+21.795 1.591 21.49 2.558 21.483 3.792 c
+21.483 5.394 l
+21.483 6.677 21.78 7.68 22.379 8.407 c
+22.975 9.132 23.805 9.496 24.864 9.496 c
+25.899 9.496 26.719 9.136 27.318 8.422 c
+27.925 7.717 28.233 6.721 28.244 5.438 c
+h
+26.348 5.409 m
+26.348 6.25 26.223 6.879 25.98 7.291 c
+25.745 7.702 25.371 7.908 24.864 7.908 c
+24.363 7.908 23.989 7.706 23.746 7.306 c
+23.511 6.913 23.386 6.313 23.379 5.512 c
+23.379 3.836 l
+23.379 3.021 23.5 2.418 23.746 2.028 c
+23.989 1.635 24.367 1.44 24.878 1.44 c
+25.367 1.44 25.731 1.631 25.966 2.014 c
+26.208 2.396 26.337 2.984 26.348 3.778 c
+h
+32.286 9.363 m
+34.065 2.587 l
+35.814 9.363 l
+38.283 9.363 l
+38.283 0 l
+36.388 0 l
+36.388 2.528 l
+36.564 6.438 l
+34.697 0 l
+33.404 0 l
+31.536 6.438 l
+31.713 2.528 l
+31.713 0 l
+29.817 0 l
+29.817 9.363 l
+h
+42.553 9.363 m
+44.332 2.587 l
+46.081 9.363 l
+48.551 9.363 l
+48.551 0 l
+46.655 0 l
+46.655 2.528 l
+46.831 6.438 l
+44.965 0 l
+43.671 0 l
+41.804 6.438 l
+41.981 2.528 l
+41.981 0 l
+40.084 0 l
+40.084 9.363 l
+h
+52.336 0 -1.897 9.363 re
+59.913 7.79 m
+57.591 7.79 l
+57.591 0 l
+55.695 0 l
+55.695 7.79 l
+53.416 7.79 l
+53.416 9.363 l
+59.913 9.363 l
+h
+65.047 2.454 m
+65.047 2.837 64.948 3.123 64.753 3.322 c
+64.554 3.516 64.202 3.719 63.695 3.925 c
+62.754 4.285 62.078 4.704 61.666 5.174 c
+61.254 5.652 61.049 6.221 61.049 6.879 c
+61.049 7.661 61.328 8.294 61.887 8.775 c
+62.445 9.253 63.155 9.496 64.018 9.496 c
+64.595 9.496 65.11 9.371 65.561 9.128 c
+66.01 8.882 66.355 8.54 66.59 8.099 c
+66.833 7.659 66.958 7.158 66.958 6.6 c
+65.077 6.6 l
+65.077 7.041 64.981 7.372 64.797 7.599 c
+64.61 7.823 64.342 7.937 63.988 7.937 c
+63.654 7.937 63.394 7.838 63.209 7.644 c
+63.033 7.456 62.945 7.195 62.945 6.865 c
+62.945 6.607 63.047 6.372 63.253 6.159 c
+63.459 5.953 63.82 5.737 64.342 5.512 c
+65.253 5.189 65.914 4.785 66.326 4.307 c
+66.745 3.825 66.958 3.212 66.958 2.469 c
+66.958 1.654 66.697 1.018 66.179 0.559 c
+65.657 0.095 64.951 -0.133 64.062 -0.133 c
+63.452 -0.133 62.901 -0.008 62.401 0.235 c
+61.909 0.488 61.522 0.845 61.24 1.309 c
+60.953 1.779 60.814 2.326 60.814 2.955 c
+62.71 2.955 l
+62.71 2.414 62.812 2.021 63.018 1.779 c
+63.232 1.532 63.581 1.411 64.062 1.411 c
+64.716 1.411 65.047 1.756 65.047 2.454 c
+68.876 -2.367 m
+67.906 -1.852 l
+68.141 -1.396 l
+68.413 -0.885 68.556 -0.375 68.568 0.133 c
+68.568 1.587 l
+70.155 1.587 l
+70.141 0.294 l
+70.141 -0.166 70.026 -0.636 69.802 -1.118 c
+69.574 -1.606 69.266 -2.022 68.876 -2.367 c
+74.877 0 m
+74.877 9.363 l
+77.773 9.363 l
+78.75 9.363 79.496 9.143 80.007 8.702 c
+80.514 8.268 80.771 7.632 80.771 6.791 c
+80.771 6.321 80.661 5.913 80.448 5.571 c
+80.242 5.226 79.952 4.976 79.581 4.821 c
+80 4.692 80.323 4.446 80.551 4.087 c
+80.786 3.734 80.904 3.285 80.904 2.749 c
+80.904 1.845 80.653 1.161 80.154 0.691 c
+79.654 0.228 78.926 0 77.979 0 c
+h
+76.773 4.072 m
+76.773 1.573 l
+77.979 1.573 l
+78.32 1.573 78.581 1.675 78.758 1.881 c
+78.934 2.094 79.022 2.396 79.022 2.778 c
+79.022 3.62 78.714 4.05 78.096 4.072 c
+h
+76.773 5.453 m
+77.758 5.453 l
+78.492 5.453 78.86 5.835 78.86 6.6 c
+78.86 7.019 78.772 7.324 78.596 7.511 c
+78.427 7.695 78.155 7.79 77.773 7.79 c
+76.773 7.79 l
+h
+85.306 3.425 m
+84.366 3.425 l
+84.366 0 l
+82.484 0 l
+82.484 9.363 l
+85.497 9.363 l
+86.445 9.363 87.176 9.117 87.687 8.628 c
+88.206 8.136 88.466 7.441 88.466 6.541 c
+88.466 5.295 88.015 4.424 87.114 3.925 c
+88.746 0.088 l
+88.746 0 l
+86.717 0 l
+h
+84.366 4.997 m
+85.438 4.997 l
+85.821 4.997 86.103 5.119 86.291 5.365 c
+86.475 5.619 86.57 5.957 86.57 6.379 c
+86.57 7.32 86.207 7.79 85.482 7.79 c
+84.366 7.79 l
+h
+94.452 1.911 m
+91.88 1.911 l
+91.381 0 l
+89.382 0 l
+92.306 9.363 l
+94.026 9.363 l
+96.981 0 l
+94.953 0 l
+h
+92.291 3.499 m
+94.041 3.499 l
+93.159 6.835 l
+h
+104.636 0 m
+102.739 0 l
+99.975 6.144 l
+99.975 0 l
+98.08 0 l
+98.08 9.363 l
+99.975 9.363 l
+102.739 3.219 l
+102.739 9.363 l
+104.636 9.363 l
+h
+112.738 3.116 m
+112.686 2.047 112.385 1.238 111.827 0.691 c
+111.275 0.139 110.504 -0.133 109.504 -0.133 c
+108.435 -0.133 107.616 0.213 107.05 0.912 c
+106.491 1.606 106.211 2.602 106.211 3.896 c
+106.211 5.468 l
+106.211 6.761 106.498 7.754 107.079 8.452 c
+107.667 9.147 108.479 9.496 109.519 9.496 c
+110.537 9.496 111.312 9.205 111.842 8.628 c
+112.37 8.058 112.671 7.239 112.752 6.173 c
+110.857 6.173 l
+110.834 6.838 110.732 7.295 110.548 7.541 c
+110.36 7.794 110.019 7.923 109.519 7.923 c
+109.019 7.923 108.659 7.746 108.446 7.393 c
+108.241 7.041 108.126 6.456 108.108 5.644 c
+108.108 3.881 l
+108.108 2.947 108.21 2.308 108.416 1.955 c
+108.63 1.61 108.993 1.44 109.504 1.44 c
+109.993 1.44 110.331 1.558 110.518 1.793 c
+110.713 2.036 110.82 2.477 110.842 3.116 c
+h
+120.686 0 m
+118.805 0 l
+118.805 4.013 l
+116.012 4.013 l
+116.012 0 l
+114.116 0 l
+114.116 9.363 l
+116.012 9.363 l
+116.012 5.571 l
+118.805 5.571 l
+118.805 9.363 l
+120.686 9.363 l
+h
+127.257 4.056 m
+124.317 4.056 l
+124.317 1.573 l
+127.801 1.573 l
+127.801 0 l
+122.421 0 l
+122.421 9.363 l
+127.786 9.363 l
+127.786 7.79 l
+124.317 7.79 l
+124.317 5.571 l
+127.257 5.571 l
+h
+133.019 2.454 m
+133.019 2.837 132.92 3.123 132.725 3.322 c
+132.527 3.516 132.174 3.719 131.667 3.925 c
+130.726 4.285 130.05 4.704 129.639 5.174 c
+129.227 5.652 129.021 6.221 129.021 6.879 c
+129.021 7.661 129.3 8.294 129.859 8.775 c
+130.418 9.253 131.127 9.496 131.99 9.496 c
+132.567 9.496 133.082 9.371 133.533 9.128 c
+133.982 8.882 134.327 8.54 134.562 8.099 c
+134.805 7.659 134.93 7.158 134.93 6.6 c
+133.049 6.6 l
+133.049 7.041 132.953 7.372 132.769 7.599 c
+132.582 7.823 132.313 7.937 131.96 7.937 c
+131.626 7.937 131.366 7.838 131.181 7.644 c
+131.005 7.456 130.917 7.195 130.917 6.865 c
+130.917 6.607 131.02 6.372 131.226 6.159 c
+131.432 5.953 131.792 5.737 132.313 5.512 c
+133.225 5.189 133.886 4.785 134.298 4.307 c
+134.717 3.825 134.93 3.212 134.93 2.469 c
+134.93 1.654 134.669 1.018 134.151 0.559 c
+133.629 0.095 132.924 -0.133 132.035 -0.133 c
+131.424 -0.133 130.873 -0.008 130.373 0.235 c
+129.88 0.488 129.495 0.845 129.212 1.309 c
+128.926 1.779 128.785 2.326 128.785 2.955 c
+130.682 2.955 l
+130.682 2.414 130.784 2.021 130.99 1.779 c
+131.204 1.532 131.553 1.411 132.035 1.411 c
+132.688 1.411 133.019 1.756 133.019 2.454 c
+143.985 1.911 m
+141.412 1.911 l
+140.912 0 l
+138.913 0 l
+141.838 9.363 l
+143.559 9.363 l
+146.513 0 l
+144.484 0 l
+h
+141.824 3.499 m
+143.573 3.499 l
+142.691 6.835 l
+h
+154.167 0 m
+152.271 0 l
+149.508 6.144 l
+149.508 0 l
+147.611 0 l
+147.611 9.363 l
+149.508 9.363 l
+152.271 3.219 l
+152.271 9.363 l
+154.167 9.363 l
+h
+155.92 0 m
+155.92 9.363 l
+158.405 9.363 l
+159.492 9.363 160.359 9.014 161.006 8.32 c
+161.66 7.621 161.994 6.674 162.006 5.468 c
+162.006 3.954 l
+162.006 2.72 161.682 1.749 161.036 1.043 c
+160.388 0.345 159.492 0 158.345 0 c
+h
+157.817 7.79 m
+157.817 1.573 l
+158.375 1.573 l
+159.01 1.573 159.455 1.738 159.712 2.072 c
+159.966 2.404 160.099 2.977 160.109 3.792 c
+160.109 5.409 l
+160.109 6.291 159.984 6.901 159.742 7.247 c
+159.507 7.588 159.095 7.769 158.507 7.79 c
+h
+172.361 7.79 m
+170.039 7.79 l
+170.039 0 l
+168.142 0 l
+168.142 7.79 l
+165.864 7.79 l
+165.864 9.363 l
+172.361 9.363 l
+h
+177.322 1.911 m
+174.75 1.911 l
+174.25 0 l
+172.251 0 l
+175.176 9.363 l
+176.896 9.363 l
+179.851 0 l
+177.821 0 l
+h
+175.161 3.499 m
+176.911 3.499 l
+176.028 6.835 l
+h
+187.182 1.058 m
+186.818 0.665 186.373 0.368 185.844 0.162 c
+185.314 -0.033 184.734 -0.133 184.109 -0.133 c
+183.029 -0.133 182.191 0.199 181.596 0.867 c
+180.996 1.532 180.692 2.502 180.684 3.778 c
+180.684 5.468 l
+180.684 6.761 180.963 7.754 181.522 8.452 c
+182.089 9.147 182.912 9.496 183.992 9.496 c
+185.01 9.496 185.774 9.238 186.284 8.731 c
+186.803 8.231 187.101 7.445 187.182 6.379 c
+185.344 6.379 l
+185.293 6.975 185.171 7.383 184.977 7.599 c
+184.778 7.813 184.47 7.923 184.051 7.923 c
+183.54 7.923 183.169 7.736 182.933 7.364 c
+182.706 6.99 182.588 6.398 182.581 5.585 c
+182.581 3.881 l
+182.581 3.028 182.706 2.404 182.963 2.014 c
+183.217 1.631 183.631 1.44 184.212 1.44 c
+184.583 1.44 184.888 1.514 185.123 1.661 c
+185.285 1.779 l
+185.285 3.499 l
+183.963 3.499 l
+183.963 4.924 l
+187.182 4.924 l
+h
+192.709 2.454 m
+192.709 2.837 192.609 3.123 192.414 3.322 c
+192.216 3.516 191.863 3.719 191.356 3.925 c
+190.415 4.285 189.739 4.704 189.328 5.174 c
+188.916 5.652 188.71 6.221 188.71 6.879 c
+188.71 7.661 188.99 8.294 189.548 8.775 c
+190.107 9.253 190.816 9.496 191.679 9.496 c
+192.256 9.496 192.771 9.371 193.223 9.128 c
+193.671 8.882 194.016 8.54 194.251 8.099 c
+194.494 7.659 194.619 7.158 194.619 6.6 c
+192.738 6.6 l
+192.738 7.041 192.642 7.372 192.458 7.599 c
+192.271 7.823 192.003 7.937 191.65 7.937 c
+191.315 7.937 191.055 7.838 190.871 7.644 c
+190.694 7.456 190.606 7.195 190.606 6.865 c
+190.606 6.607 190.709 6.372 190.915 6.159 c
+191.121 5.953 191.481 5.737 192.003 5.512 c
+192.914 5.189 193.575 4.785 193.987 4.307 c
+194.406 3.825 194.619 3.212 194.619 2.469 c
+194.619 1.654 194.359 1.018 193.84 0.559 c
+193.318 0.095 192.613 -0.133 191.724 -0.133 c
+191.113 -0.133 190.562 -0.008 190.062 0.235 c
+189.57 0.488 189.185 0.845 188.901 1.309 c
+188.615 1.779 188.475 2.326 188.475 2.955 c
+190.371 2.955 l
+190.371 2.414 190.474 2.021 190.679 1.779 c
+190.893 1.532 191.242 1.411 191.724 1.411 c
+192.377 1.411 192.709 1.756 192.709 2.454 c
+f
+Q
+0.797 0.801 0.141 0.02 K
+0.709 w 1 j 1 J
+q 1 0 0 1 535.6673 213.6594 cm
+0 0 m
+-254.669 0 l
+-254.669 -82.694 l
+-499.999 -82.694 l
+S
+Q
+0.793 0.801 0.129 0.016 k
+288.19 207.39 37.732 -54.746 re
+f
+0 0 0 0 k
+q 1 0 0 1 297.4504 198.7321 cm
+0 0 m
+0.294 0 0.445 0.195 0.455 0.588 c
+1.426 0.588 l
+1.426 0.154 1.294 -0.198 1.029 -0.47 c
+0.765 -0.735 0.426 -0.867 0.015 -0.867 c
+-0.496 -0.867 -0.889 -0.713 -1.161 -0.397 c
+-1.426 -0.073 -1.565 0.397 -1.573 1.014 c
+-1.573 1.338 l
+-1.573 1.962 -1.44 2.44 -1.176 2.764 c
+-0.904 3.094 -0.507 3.263 0.015 3.263 c
+0.445 3.263 0.786 3.123 1.043 2.851 c
+1.297 2.576 1.426 2.194 1.426 1.706 c
+0.455 1.706 l
+0.455 1.918 0.415 2.088 0.338 2.205 c
+0.268 2.33 0.151 2.396 -0.015 2.396 c
+-0.191 2.396 -0.32 2.33 -0.397 2.205 c
+-0.478 2.076 -0.522 1.827 -0.529 1.455 c
+-0.529 1.043 l
+-0.529 0.721 -0.515 0.493 -0.485 0.368 c
+-0.448 0.239 -0.393 0.147 -0.324 0.088 c
+-0.246 0.029 -0.139 0 0 0 c
+1.911 1.323 m
+1.911 1.929 2.051 2.404 2.337 2.749 c
+2.62 3.09 3.013 3.263 3.513 3.263 c
+4.021 3.263 4.417 3.09 4.704 2.749 c
+4.987 2.404 5.13 1.929 5.13 1.323 c
+5.13 1.058 l
+5.13 0.459 4.987 -0.011 4.704 -0.353 c
+4.417 -0.698 4.021 -0.867 3.513 -0.867 c
+3.002 -0.867 2.606 -0.698 2.323 -0.353 c
+2.047 -0.011 1.911 0.463 1.911 1.073 c
+h
+2.955 1.058 m
+2.955 0.353 3.138 0 3.513 0 c
+3.866 0 4.057 0.294 4.087 0.882 c
+4.087 1.323 l
+4.087 1.683 4.035 1.955 3.94 2.132 c
+3.84 2.308 3.697 2.396 3.513 2.396 c
+3.337 2.396 3.198 2.308 3.102 2.132 c
+3.002 1.955 2.955 1.683 2.955 1.323 c
+h
+6.747 3.19 m
+6.776 2.822 l
+7.011 3.117 7.32 3.263 7.703 3.263 c
+8.103 3.263 8.382 3.08 8.54 2.72 c
+8.775 3.08 9.103 3.263 9.525 3.263 c
+10.219 3.263 10.572 2.778 10.583 1.808 c
+10.583 -0.794 l
+9.554 -0.794 l
+9.554 1.749 l
+9.554 1.974 9.517 2.135 9.452 2.234 c
+9.392 2.33 9.282 2.381 9.128 2.381 c
+8.93 2.381 8.79 2.263 8.702 2.028 c
+8.702 -0.794 l
+7.659 -0.794 l
+7.659 1.735 l
+7.659 1.97 7.628 2.135 7.57 2.234 c
+7.511 2.33 7.401 2.381 7.247 2.381 c
+7.071 2.381 6.927 2.286 6.82 2.102 c
+6.82 -0.794 l
+5.777 -0.794 l
+5.777 3.19 l
+h
+12.332 3.19 m
+12.362 2.822 l
+12.597 3.117 12.906 3.263 13.288 3.263 c
+13.689 3.263 13.968 3.08 14.126 2.72 c
+14.361 3.08 14.688 3.263 15.111 3.263 c
+15.805 3.263 16.158 2.778 16.169 1.808 c
+16.169 -0.794 l
+15.14 -0.794 l
+15.14 1.749 l
+15.14 1.974 15.104 2.135 15.037 2.234 c
+14.978 2.33 14.868 2.381 14.714 2.381 c
+14.516 2.381 14.375 2.263 14.288 2.028 c
+14.288 -0.794 l
+13.244 -0.794 l
+13.244 1.735 l
+13.244 1.97 13.215 2.135 13.155 2.234 c
+13.097 2.33 12.987 2.381 12.833 2.381 c
+12.656 2.381 12.513 2.286 12.406 2.102 c
+12.406 -0.794 l
+11.362 -0.794 l
+11.362 3.19 l
+h
+18.036 -0.794 -1.043 3.984 re
+16.948 4.218 m
+16.948 4.374 16.996 4.501 17.095 4.601 c
+17.201 4.707 17.338 4.763 17.506 4.763 c
+17.683 4.763 17.819 4.707 17.918 4.601 c
+18.025 4.501 18.08 4.374 18.08 4.218 c
+18.08 4.05 18.025 3.913 17.918 3.807 c
+17.819 3.708 17.683 3.66 17.506 3.66 c
+17.338 3.66 17.201 3.708 17.095 3.807 c
+16.996 3.913 16.948 4.05 16.948 4.218 c
+20.05 4.16 m
+20.05 3.19 l
+20.579 3.19 l
+20.579 2.396 l
+20.05 2.396 l
+20.05 0.426 l
+20.05 0.268 20.068 0.162 20.108 0.103 c
+20.156 0.044 20.241 0.015 20.359 0.015 c
+20.465 0.015 20.549 0.023 20.608 0.044 c
+20.608 -0.764 l
+20.432 -0.831 20.241 -0.867 20.035 -0.867 c
+19.359 -0.867 19.013 -0.482 19.006 0.294 c
+19.006 2.396 l
+18.55 2.396 l
+18.55 3.19 l
+19.006 3.19 l
+19.006 4.16 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 331.7103 197.9382 cm
+0 0 m
+-0.04 0.088 -0.066 0.235 -0.073 0.441 c
+-0.309 0.096 -0.603 -0.073 -0.956 -0.073 c
+-1.319 -0.073 -1.602 0.023 -1.808 0.221 c
+-2.007 0.426 -2.102 0.713 -2.102 1.088 c
+-2.102 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.286 -1.043 2.411 -0.573 2.411 c
+-0.088 2.411 l
+-0.088 2.837 l
+-0.088 3.072 -0.143 3.238 -0.25 3.337 c
+-0.36 3.444 -0.522 3.499 -0.735 3.499 c
+-0.933 3.499 -1.095 3.44 -1.22 3.323 c
+-1.338 3.205 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.076 -1.984 3.267 -1.866 3.454 c
+-1.741 3.639 -1.58 3.786 -1.382 3.896 c
+-1.176 4.002 -0.948 4.057 -0.691 4.057 c
+-0.291 4.057 0.015 3.954 0.22 3.749 c
+0.434 3.543 0.548 3.248 0.559 2.866 c
+0.559 0.853 l
+0.559 0.548 0.595 0.283 0.676 0.059 c
+0.676 0 l
+h
+-0.867 0.515 m
+-0.702 0.515 -0.551 0.559 -0.411 0.647 c
+-0.264 0.736 -0.158 0.846 -0.088 0.985 c
+-0.088 1.926 l
+-0.455 1.926 l
+-0.771 1.926 -1.014 1.856 -1.191 1.72 c
+-1.367 1.592 -1.455 1.405 -1.455 1.162 c
+-1.455 0.934 -1.411 0.769 -1.323 0.661 c
+-1.234 0.563 -1.084 0.515 -0.867 0.515 c
+2.176 3.984 m
+2.19 3.543 l
+2.444 3.884 2.767 4.057 3.16 4.057 c
+3.866 4.057 4.222 3.587 4.233 2.646 c
+4.233 0 l
+3.587 0 l
+3.587 2.617 l
+3.587 2.929 3.532 3.15 3.425 3.278 c
+3.314 3.404 3.16 3.469 2.955 3.469 c
+2.797 3.469 2.649 3.414 2.514 3.308 c
+2.385 3.198 2.282 3.061 2.205 2.896 c
+2.205 0 l
+1.558 0 l
+1.558 3.984 l
+h
+6.791 2.176 m
+6.791 2.753 6.927 3.209 7.202 3.543 c
+7.485 3.884 7.857 4.057 8.32 4.057 c
+8.779 4.057 9.147 3.888 9.422 3.558 c
+9.705 3.234 9.852 2.786 9.863 2.22 c
+9.863 1.794 l
+9.863 1.224 9.72 0.769 9.437 0.426 c
+9.161 0.092 8.794 -0.073 8.334 -0.073 c
+7.871 -0.073 7.501 0.088 7.217 0.412 c
+6.942 0.742 6.799 1.183 6.791 1.735 c
+h
+7.437 1.794 m
+7.437 1.389 7.515 1.073 7.673 0.838 c
+7.838 0.603 8.058 0.485 8.334 0.485 c
+8.9 0.485 9.194 0.897 9.216 1.72 c
+9.216 2.176 l
+9.216 2.577 9.132 2.896 8.966 3.131 c
+8.808 3.373 8.592 3.499 8.32 3.499 c
+8.056 3.499 7.838 3.373 7.673 3.131 c
+7.515 2.896 7.437 2.577 7.437 2.176 c
+h
+13.582 1.794 m
+13.582 1.176 13.468 0.709 13.244 0.397 c
+13.027 0.081 12.704 -0.073 12.274 -0.073 c
+11.851 -0.073 11.539 0.107 11.333 0.47 c
+11.304 0 l
+10.701 0 l
+10.701 5.644 l
+11.348 5.644 l
+11.348 3.543 l
+11.561 3.884 11.869 4.057 12.274 4.057 c
+12.704 4.057 13.027 3.899 13.244 3.587 c
+13.468 3.282 13.582 2.816 13.582 2.19 c
+h
+12.935 2.176 m
+12.935 2.646 12.866 2.977 12.729 3.175 c
+12.601 3.37 12.391 3.469 12.097 3.469 c
+11.763 3.469 11.513 3.286 11.348 2.926 c
+11.348 1.044 l
+11.513 0.68 11.767 0.5 12.112 0.5 c
+12.406 0.5 12.615 0.603 12.744 0.809 c
+12.869 1.014 12.935 1.33 12.935 1.764 c
+h
+15.081 3.984 m
+15.081 -0.5 l
+15.081 -1.234 14.791 -1.602 14.214 -1.602 c
+14.074 -1.602 13.953 -1.579 13.847 -1.543 c
+13.847 -0.999 l
+13.916 -1.018 14.001 -1.028 14.111 -1.028 c
+14.217 -1.028 14.295 -0.985 14.346 -0.897 c
+14.406 -0.816 14.435 -0.675 14.435 -0.47 c
+14.435 3.984 l
+h
+15.111 5.027 m
+15.111 4.917 15.081 4.825 15.023 4.748 c
+14.963 4.678 14.868 4.645 14.743 4.645 c
+14.626 4.645 14.53 4.678 14.464 4.748 c
+14.406 4.825 14.375 4.917 14.375 5.027 c
+14.375 5.145 14.406 5.237 14.464 5.307 c
+14.53 5.384 14.626 5.424 14.743 5.424 c
+14.868 5.424 14.963 5.384 15.023 5.307 c
+15.081 5.226 15.111 5.134 15.111 5.027 c
+17.536 -0.073 m
+17.036 -0.073 16.654 0.073 16.39 0.368 c
+16.125 0.661 15.993 1.095 15.993 1.676 c
+15.993 2.147 l
+15.993 2.741 16.118 3.209 16.375 3.543 c
+16.64 3.884 16.999 4.057 17.463 4.057 c
+17.922 4.057 18.264 3.903 18.491 3.601 c
+18.726 3.308 18.848 2.845 18.859 2.22 c
+18.859 1.794 l
+16.64 1.794 l
+16.64 1.706 l
+16.64 1.272 16.717 0.96 16.875 0.765 c
+17.04 0.578 17.271 0.485 17.565 0.485 c
+17.76 0.485 17.933 0.518 18.08 0.588 c
+18.227 0.665 18.363 0.783 18.491 0.941 c
+18.83 0.53 l
+18.543 0.125 18.113 -0.073 17.536 -0.073 c
+17.463 3.499 m
+17.187 3.499 16.985 3.404 16.86 3.219 c
+16.731 3.032 16.658 2.741 16.64 2.352 c
+18.212 2.352 l
+18.212 2.44 l
+18.19 2.822 18.124 3.091 18.007 3.248 c
+17.889 3.414 17.705 3.499 17.463 3.499 c
+21.02 0.485 m
+21.232 0.485 21.406 0.548 21.534 0.676 c
+21.67 0.813 21.743 1.004 21.755 1.249 c
+22.372 1.249 l
+22.35 0.867 22.214 0.548 21.961 0.294 c
+21.703 0.048 21.391 -0.073 21.02 -0.073 c
+20.527 -0.073 20.153 0.077 19.888 0.383 c
+19.63 0.695 19.505 1.162 19.505 1.779 c
+19.505 2.22 l
+19.505 2.816 19.63 3.271 19.888 3.587 c
+20.153 3.899 20.527 4.057 21.02 4.057 c
+21.421 4.057 21.74 3.925 21.975 3.66 c
+22.217 3.404 22.35 3.057 22.372 2.617 c
+21.755 2.617 l
+21.733 2.911 21.659 3.131 21.534 3.278 c
+21.417 3.425 21.244 3.499 21.02 3.499 c
+20.725 3.499 20.509 3.4 20.373 3.205 c
+20.233 3.017 20.16 2.708 20.153 2.278 c
+20.153 1.764 l
+20.153 1.294 20.218 0.96 20.359 0.765 c
+20.505 0.578 20.725 0.485 21.02 0.485 c
+23.974 4.939 m
+23.974 3.984 l
+24.577 3.984 l
+24.577 3.454 l
+23.974 3.454 l
+23.974 0.985 l
+23.974 0.827 23.996 0.709 24.048 0.632 c
+24.106 0.551 24.195 0.515 24.312 0.515 c
+24.401 0.515 24.488 0.53 24.577 0.559 c
+24.577 0 l
+24.43 -0.047 24.276 -0.073 24.121 -0.073 c
+23.864 -0.073 23.669 0.019 23.534 0.206 c
+23.393 0.389 23.328 0.651 23.328 0.985 c
+23.328 3.454 l
+22.725 3.454 l
+22.725 3.984 l
+23.328 3.984 l
+23.328 4.939 l
+h
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 301.3602 186.6493 cm
+0 0 m
+0 -0.646 -0.103 -1.132 -0.309 -1.455 c
+-0.515 -1.778 -0.837 -1.94 -1.278 -1.94 c
+-1.624 -1.94 -1.903 -1.786 -2.117 -1.469 c
+-2.146 -1.866 l
+-3.102 -1.866 l
+-3.102 3.778 l
+-2.057 3.778 l
+-2.057 1.794 l
+-1.863 2.058 -1.61 2.19 -1.294 2.19 c
+-0.852 2.19 -0.529 2.029 -0.323 1.706 c
+-0.118 1.382 -0.01 0.912 0 0.294 c
+h
+-1.043 0.25 m
+-1.043 0.669 -1.084 0.948 -1.161 1.088 c
+-1.242 1.235 -1.381 1.309 -1.587 1.309 c
+-1.804 1.309 -1.962 1.21 -2.057 1.014 c
+-2.057 -0.793 l
+-1.969 -0.981 -1.808 -1.072 -1.573 -1.072 c
+-1.378 -1.072 -1.242 -1.01 -1.161 -0.881 c
+-1.084 -0.756 -1.043 -0.5 -1.043 -0.118 c
+h
+2.587 1.103 m
+2.249 1.132 l
+1.962 1.132 1.771 1.008 1.676 0.765 c
+1.676 -1.866 l
+0.632 -1.866 l
+0.632 2.117 l
+1.602 2.117 l
+1.632 1.676 l
+1.798 2.018 2.029 2.19 2.323 2.19 c
+2.44 2.19 2.532 2.168 2.602 2.132 c
+h
+4.91 -1.866 m
+4.881 -1.808 4.851 -1.705 4.821 -1.558 c
+4.634 -1.815 4.385 -1.94 4.072 -1.94 c
+3.738 -1.94 3.458 -1.834 3.234 -1.616 c
+3.017 -1.392 2.911 -1.103 2.911 -0.75 c
+2.911 -0.338 3.043 -0.022 3.308 0.206 c
+3.572 0.441 3.954 0.559 4.454 0.559 c
+4.777 0.559 l
+4.777 0.882 l
+4.777 1.058 4.74 1.18 4.675 1.249 c
+4.616 1.327 4.528 1.367 4.41 1.367 c
+4.152 1.367 4.028 1.213 4.028 0.912 c
+2.984 0.912 l
+2.984 1.283 3.12 1.588 3.396 1.823 c
+3.668 2.066 4.017 2.19 4.439 2.19 c
+4.881 2.19 5.218 2.072 5.453 1.837 c
+5.696 1.61 5.821 1.287 5.821 0.867 c
+5.821 -0.999 l
+5.821 -1.344 5.869 -1.612 5.968 -1.808 c
+5.968 -1.866 l
+h
+4.307 -1.117 m
+4.414 -1.117 4.505 -1.099 4.586 -1.058 c
+4.675 -1.01 4.737 -0.951 4.777 -0.881 c
+4.777 -0.058 l
+4.528 -0.058 l
+4.351 -0.058 4.208 -0.118 4.102 -0.235 c
+4.002 -0.345 3.954 -0.492 3.954 -0.675 c
+3.954 -0.97 4.072 -1.117 4.307 -1.117 c
+7.526 2.117 m
+7.556 1.72 l
+7.791 2.033 8.092 2.19 8.467 2.19 c
+9.151 2.19 9.503 1.709 9.525 0.75 c
+9.525 -1.866 l
+8.482 -1.866 l
+8.482 0.676 l
+8.482 0.9 8.445 1.062 8.379 1.162 c
+8.309 1.257 8.191 1.309 8.026 1.309 c
+7.838 1.309 7.691 1.213 7.585 1.029 c
+7.585 -1.866 l
+6.541 -1.866 l
+6.541 2.117 l
+h
+11.715 -1.072 m
+12.009 -1.072 12.16 -0.878 12.171 -0.484 c
+13.141 -0.484 l
+13.141 -0.918 13.009 -1.271 12.744 -1.543 c
+12.48 -1.808 12.142 -1.94 11.73 -1.94 c
+11.219 -1.94 10.826 -1.786 10.554 -1.469 c
+10.29 -1.146 10.15 -0.675 10.143 -0.058 c
+10.143 0.265 l
+10.143 0.89 10.275 1.367 10.539 1.691 c
+10.812 2.022 11.208 2.19 11.73 2.19 c
+12.16 2.19 12.501 2.051 12.759 1.779 c
+13.012 1.503 13.141 1.121 13.141 0.632 c
+12.171 0.632 l
+12.171 0.846 12.131 1.014 12.054 1.132 c
+11.984 1.257 11.866 1.324 11.701 1.324 c
+11.524 1.324 11.396 1.257 11.319 1.132 c
+11.238 1.004 11.194 0.754 11.186 0.383 c
+11.186 -0.029 l
+11.186 -0.353 11.201 -0.58 11.231 -0.706 c
+11.267 -0.833 11.322 -0.926 11.392 -0.985 c
+11.469 -1.043 11.576 -1.072 11.715 -1.072 c
+14.758 1.764 m
+14.982 2.047 15.258 2.19 15.581 2.19 c
+15.942 2.19 16.217 2.062 16.405 1.808 c
+16.599 1.551 16.698 1.169 16.698 0.661 c
+16.698 -1.866 l
+15.655 -1.866 l
+15.655 0.647 l
+15.655 0.882 15.614 1.048 15.537 1.147 c
+15.468 1.253 15.354 1.309 15.199 1.309 c
+15.011 1.309 14.865 1.224 14.758 1.058 c
+14.758 -1.866 l
+13.714 -1.866 l
+13.714 3.778 l
+14.758 3.778 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 331.7103 184.7829 cm
+0 0 m
+-0.04 0.088 -0.066 0.235 -0.073 0.441 c
+-0.309 0.096 -0.603 -0.073 -0.956 -0.073 c
+-1.319 -0.073 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.102 0.713 -2.102 1.087 c
+-2.102 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.286 -1.043 2.41 -0.573 2.41 c
+-0.088 2.41 l
+-0.088 2.837 l
+-0.088 3.072 -0.143 3.237 -0.25 3.337 c
+-0.36 3.443 -0.522 3.499 -0.735 3.499 c
+-0.933 3.499 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.076 -1.984 3.267 -1.866 3.454 c
+-1.741 3.638 -1.58 3.785 -1.382 3.896 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.291 4.056 0.015 3.954 0.22 3.748 c
+0.434 3.543 0.548 3.248 0.559 2.866 c
+0.559 0.852 l
+0.559 0.548 0.595 0.283 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.515 m
+-0.702 0.515 -0.551 0.559 -0.411 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.985 c
+-0.088 1.926 l
+-0.455 1.926 l
+-0.771 1.926 -1.014 1.856 -1.191 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.234 0.563 -1.084 0.515 -0.867 0.515 c
+4.924 3.366 m
+4.836 3.385 4.737 3.395 4.63 3.395 c
+4.295 3.395 4.06 3.212 3.925 2.851 c
+3.925 0 l
+3.278 0 l
+3.278 3.983 l
+3.91 3.983 l
+3.925 3.572 l
+4.101 3.896 4.343 4.056 4.659 4.056 c
+4.766 4.056 4.854 4.035 4.924 3.998 c
+h
+6.923 -0.073 m
+6.423 -0.073 6.041 0.073 5.777 0.368 c
+5.512 0.661 5.38 1.095 5.38 1.675 c
+5.38 2.146 l
+5.38 2.741 5.505 3.208 5.762 3.543 c
+6.026 3.884 6.387 4.056 6.85 4.056 c
+7.31 4.056 7.651 3.902 7.879 3.601 c
+8.114 3.308 8.235 2.844 8.247 2.219 c
+8.247 1.793 l
+6.026 1.793 l
+6.026 1.705 l
+6.026 1.272 6.104 0.96 6.262 0.764 c
+6.427 0.577 6.659 0.485 6.953 0.485 c
+7.148 0.485 7.32 0.518 7.468 0.588 c
+7.614 0.665 7.75 0.783 7.879 0.941 c
+8.216 0.529 l
+7.93 0.125 7.501 -0.073 6.923 -0.073 c
+6.85 3.499 m
+6.574 3.499 6.372 3.403 6.247 3.219 c
+6.119 3.032 6.045 2.741 6.026 2.352 c
+7.599 2.352 l
+7.599 2.44 l
+7.578 2.822 7.511 3.09 7.393 3.248 c
+7.276 3.414 7.092 3.499 6.85 3.499 c
+9.319 0 m
+9.319 3.454 l
+8.79 3.454 l
+8.79 3.983 l
+9.319 3.983 l
+9.319 4.439 l
+9.319 4.84 9.415 5.152 9.613 5.38 c
+9.819 5.604 10.098 5.718 10.451 5.718 c
+10.587 5.718 10.72 5.696 10.848 5.66 c
+10.819 5.115 l
+10.72 5.134 10.62 5.145 10.525 5.145 c
+10.15 5.145 9.966 4.88 9.966 4.351 c
+9.966 3.983 l
+10.643 3.983 l
+10.643 3.454 l
+9.966 3.454 l
+9.966 0 l
+h
+12.744 -0.073 m
+12.245 -0.073 11.862 0.073 11.597 0.368 c
+11.333 0.661 11.2 1.095 11.2 1.675 c
+11.2 2.146 l
+11.2 2.741 11.326 3.208 11.583 3.543 c
+11.848 3.884 12.208 4.056 12.671 4.056 c
+13.13 4.056 13.471 3.902 13.7 3.601 c
+13.935 3.308 14.056 2.844 14.067 2.219 c
+14.067 1.793 l
+11.848 1.793 l
+11.848 1.705 l
+11.848 1.272 11.925 0.96 12.083 0.764 c
+12.248 0.577 12.48 0.485 12.773 0.485 c
+12.968 0.485 13.141 0.518 13.288 0.588 c
+13.435 0.665 13.571 0.783 13.7 0.941 c
+14.038 0.529 l
+13.751 0.125 13.321 -0.073 12.744 -0.073 c
+12.671 3.499 m
+12.395 3.499 12.193 3.403 12.068 3.219 c
+11.939 3.032 11.866 2.741 11.848 2.352 c
+13.421 2.352 l
+13.421 2.44 l
+13.398 2.822 13.332 3.09 13.215 3.248 c
+13.097 3.414 12.914 3.499 12.671 3.499 c
+16.492 3.366 m
+16.405 3.385 16.305 3.395 16.199 3.395 c
+15.864 3.395 15.629 3.212 15.493 2.851 c
+15.493 0 l
+14.846 0 l
+14.846 3.983 l
+15.478 3.983 l
+15.493 3.572 l
+15.669 3.896 15.912 4.056 16.228 4.056 c
+16.334 4.056 16.422 4.035 16.492 3.998 c
+h
+18.491 -0.073 m
+17.992 -0.073 17.61 0.073 17.345 0.368 c
+17.08 0.661 16.948 1.095 16.948 1.675 c
+16.948 2.146 l
+16.948 2.741 17.073 3.208 17.33 3.543 c
+17.595 3.884 17.955 4.056 18.418 4.056 c
+18.877 4.056 19.219 3.902 19.447 3.601 c
+19.682 3.308 19.803 2.844 19.815 2.219 c
+19.815 1.793 l
+17.595 1.793 l
+17.595 1.705 l
+17.595 1.272 17.672 0.96 17.83 0.764 c
+17.995 0.577 18.227 0.485 18.521 0.485 c
+18.716 0.485 18.888 0.518 19.035 0.588 c
+19.183 0.665 19.318 0.783 19.447 0.941 c
+19.785 0.529 l
+19.499 0.125 19.069 -0.073 18.491 -0.073 c
+18.418 3.499 m
+18.142 3.499 17.94 3.403 17.816 3.219 c
+17.687 3.032 17.613 2.741 17.595 2.352 c
+19.168 2.352 l
+19.168 2.44 l
+19.146 2.822 19.079 3.09 18.962 3.248 c
+18.844 3.414 18.66 3.499 18.418 3.499 c
+21.211 3.983 m
+21.226 3.543 l
+21.479 3.884 21.803 4.056 22.196 4.056 c
+22.901 4.056 23.258 3.586 23.268 2.645 c
+23.268 0 l
+22.622 0 l
+22.622 2.616 l
+22.622 2.929 22.566 3.15 22.46 3.278 c
+22.35 3.403 22.196 3.469 21.99 3.469 c
+21.832 3.469 21.685 3.414 21.549 3.308 c
+21.421 3.197 21.317 3.061 21.24 2.896 c
+21.24 0 l
+20.594 0 l
+20.594 3.983 l
+h
+25.62 0.485 m
+25.834 0.485 26.007 0.548 26.135 0.676 c
+26.271 0.812 26.344 1.003 26.356 1.249 c
+26.973 1.249 l
+26.951 0.867 26.815 0.548 26.561 0.294 c
+26.304 0.048 25.992 -0.073 25.62 -0.073 c
+25.128 -0.073 24.754 0.077 24.488 0.382 c
+24.232 0.694 24.106 1.161 24.106 1.779 c
+24.106 2.219 l
+24.106 2.815 24.232 3.271 24.488 3.586 c
+24.754 3.899 25.128 4.056 25.62 4.056 c
+26.021 4.056 26.341 3.925 26.576 3.66 c
+26.819 3.403 26.951 3.057 26.973 2.616 c
+26.356 2.616 l
+26.333 2.911 26.26 3.131 26.135 3.278 c
+26.017 3.425 25.845 3.499 25.62 3.499 c
+25.327 3.499 25.109 3.399 24.974 3.204 c
+24.835 3.017 24.76 2.708 24.754 2.278 c
+24.754 1.764 l
+24.754 1.294 24.82 0.96 24.959 0.764 c
+25.106 0.577 25.327 0.485 25.62 0.485 c
+29.148 -0.073 m
+28.648 -0.073 28.266 0.073 28.002 0.368 c
+27.737 0.661 27.605 1.095 27.605 1.675 c
+27.605 2.146 l
+27.605 2.741 27.73 3.208 27.987 3.543 c
+28.251 3.884 28.612 4.056 29.074 4.056 c
+29.534 4.056 29.876 3.902 30.104 3.601 c
+30.339 3.308 30.46 2.844 30.471 2.219 c
+30.471 1.793 l
+28.251 1.793 l
+28.251 1.705 l
+28.251 1.272 28.328 0.96 28.487 0.764 c
+28.652 0.577 28.884 0.485 29.178 0.485 c
+29.373 0.485 29.545 0.518 29.693 0.588 c
+29.839 0.665 29.975 0.783 30.104 0.941 c
+30.441 0.529 l
+30.155 0.125 29.725 -0.073 29.148 -0.073 c
+29.074 3.499 m
+28.799 3.499 28.598 3.403 28.472 3.219 c
+28.344 3.032 28.27 2.741 28.251 2.352 c
+29.824 2.352 l
+29.824 2.44 l
+29.803 2.822 29.736 3.09 29.618 3.248 c
+29.501 3.414 29.317 3.499 29.074 3.499 c
+33.822 4.939 m
+33.822 3.983 l
+34.425 3.983 l
+34.425 3.454 l
+33.822 3.454 l
+33.822 0.985 l
+33.822 0.827 33.845 0.709 33.896 0.632 c
+33.955 0.551 34.043 0.515 34.161 0.515 c
+34.249 0.515 34.337 0.529 34.425 0.559 c
+34.425 0 l
+34.279 -0.048 34.123 -0.073 33.969 -0.073 c
+33.712 -0.073 33.518 0.019 33.381 0.206 c
+33.242 0.389 33.176 0.65 33.176 0.985 c
+33.176 3.454 l
+32.573 3.454 l
+32.573 3.983 l
+33.176 3.983 l
+33.176 4.939 l
+h
+34.984 2.175 m
+34.984 2.753 35.12 3.208 35.395 3.543 c
+35.678 3.884 36.049 4.056 36.513 4.056 c
+36.972 4.056 37.339 3.888 37.615 3.557 c
+37.898 3.233 38.044 2.786 38.056 2.219 c
+38.056 1.793 l
+38.056 1.224 37.913 0.768 37.63 0.426 c
+37.354 0.092 36.986 -0.073 36.527 -0.073 c
+36.064 -0.073 35.693 0.088 35.41 0.411 c
+35.135 0.742 34.991 1.183 34.984 1.735 c
+h
+35.63 1.793 m
+35.63 1.389 35.708 1.073 35.866 0.838 c
+36.031 0.603 36.251 0.485 36.527 0.485 c
+37.093 0.485 37.387 0.897 37.409 1.72 c
+37.409 2.175 l
+37.409 2.576 37.325 2.896 37.159 3.131 c
+37.001 3.373 36.785 3.499 36.513 3.499 c
+36.248 3.499 36.031 3.373 35.866 3.131 c
+35.708 2.896 35.63 2.576 35.63 2.175 c
+h
+42.657 0 m
+42.616 0.088 42.59 0.235 42.583 0.441 c
+42.348 0.096 42.054 -0.073 41.701 -0.073 c
+41.337 -0.073 41.055 0.022 40.849 0.22 c
+40.65 0.426 40.554 0.713 40.554 1.087 c
+40.554 1.488 40.691 1.808 40.966 2.043 c
+41.238 2.286 41.613 2.41 42.083 2.41 c
+42.568 2.41 l
+42.568 2.837 l
+42.568 3.072 42.513 3.237 42.407 3.337 c
+42.297 3.443 42.135 3.499 41.922 3.499 c
+41.724 3.499 41.562 3.439 41.437 3.322 c
+41.319 3.204 41.26 3.057 41.26 2.881 c
+40.614 2.881 l
+40.614 3.076 40.672 3.267 40.79 3.454 c
+40.915 3.638 41.076 3.785 41.275 3.896 c
+41.481 4.002 41.709 4.056 41.965 4.056 c
+42.366 4.056 42.671 3.954 42.877 3.748 c
+43.091 3.543 43.204 3.248 43.216 2.866 c
+43.216 0.852 l
+43.216 0.548 43.252 0.283 43.333 0.058 c
+43.333 0 l
+h
+41.79 0.515 m
+41.955 0.515 42.106 0.559 42.245 0.646 c
+42.393 0.735 42.499 0.845 42.568 0.985 c
+42.568 1.926 l
+42.201 1.926 l
+41.885 1.926 41.643 1.856 41.466 1.72 c
+41.29 1.591 41.202 1.404 41.202 1.161 c
+41.202 0.933 41.246 0.768 41.334 0.661 c
+41.422 0.563 41.572 0.515 41.79 0.515 c
+47.316 0.485 m
+47.529 0.485 47.702 0.548 47.831 0.676 c
+47.966 0.812 48.04 1.003 48.051 1.249 c
+48.668 1.249 l
+48.647 0.867 48.51 0.548 48.257 0.294 c
+47.999 0.048 47.687 -0.073 47.316 -0.073 c
+46.823 -0.073 46.449 0.077 46.185 0.382 c
+45.927 0.694 45.803 1.161 45.803 1.779 c
+45.803 2.219 l
+45.803 2.815 45.927 3.271 46.185 3.586 c
+46.449 3.899 46.823 4.056 47.316 4.056 c
+47.717 4.056 48.037 3.925 48.272 3.66 c
+48.514 3.403 48.647 3.057 48.668 2.616 c
+48.051 2.616 l
+48.029 2.911 47.956 3.131 47.831 3.278 c
+47.713 3.425 47.54 3.499 47.316 3.499 c
+47.022 3.499 46.806 3.399 46.669 3.204 c
+46.53 3.017 46.457 2.708 46.449 2.278 c
+46.449 1.764 l
+46.449 1.294 46.515 0.96 46.655 0.764 c
+46.802 0.577 47.022 0.485 47.316 0.485 c
+49.286 2.175 m
+49.286 2.753 49.422 3.208 49.697 3.543 c
+49.981 3.884 50.351 4.056 50.815 4.056 c
+51.274 4.056 51.642 3.888 51.918 3.557 c
+52.2 3.233 52.347 2.786 52.358 2.219 c
+52.358 1.793 l
+52.358 1.224 52.215 0.768 51.932 0.426 c
+51.656 0.092 51.289 -0.073 50.829 -0.073 c
+50.366 -0.073 49.996 0.088 49.712 0.411 c
+49.437 0.742 49.294 1.183 49.286 1.735 c
+h
+49.933 1.793 m
+49.933 1.389 50.01 1.073 50.168 0.838 c
+50.333 0.603 50.553 0.485 50.829 0.485 c
+51.395 0.485 51.689 0.897 51.711 1.72 c
+51.711 2.175 l
+51.711 2.576 51.627 2.896 51.461 3.131 c
+51.303 3.373 51.087 3.499 50.815 3.499 c
+50.55 3.499 50.333 3.373 50.168 3.131 c
+50.01 2.896 49.933 2.576 49.933 2.175 c
+h
+53.813 3.983 m
+53.828 3.616 l
+54.071 3.91 54.39 4.056 54.783 4.056 c
+55.224 4.056 55.533 3.859 55.71 3.469 c
+55.963 3.859 56.312 4.056 56.753 4.056 c
+57.488 4.056 57.863 3.594 57.885 2.675 c
+57.885 0 l
+57.238 0 l
+57.238 2.616 l
+57.238 2.911 57.183 3.123 57.077 3.263 c
+56.977 3.399 56.805 3.469 56.562 3.469 c
+56.363 3.469 56.202 3.389 56.077 3.233 c
+55.959 3.087 55.889 2.896 55.871 2.66 c
+55.871 0 l
+55.21 0 l
+55.21 2.645 l
+55.21 3.194 54.989 3.469 54.548 3.469 c
+54.214 3.469 53.979 3.308 53.842 2.984 c
+53.842 0 l
+53.196 0 l
+53.196 3.983 l
+h
+59.473 3.983 m
+59.487 3.616 l
+59.729 3.91 60.049 4.056 60.443 4.056 c
+60.884 4.056 61.192 3.859 61.368 3.469 c
+61.622 3.859 61.971 4.056 62.412 4.056 c
+63.147 4.056 63.522 3.594 63.544 2.675 c
+63.544 0 l
+62.897 0 l
+62.897 2.616 l
+62.897 2.911 62.842 3.123 62.736 3.263 c
+62.636 3.399 62.464 3.469 62.221 3.469 c
+62.023 3.469 61.861 3.389 61.736 3.233 c
+61.618 3.087 61.549 2.896 61.53 2.66 c
+61.53 0 l
+60.869 0 l
+60.869 2.645 l
+60.869 3.194 60.649 3.469 60.207 3.469 c
+59.873 3.469 59.638 3.308 59.502 2.984 c
+59.502 0 l
+58.855 0 l
+58.855 3.983 l
+h
+65.22 0 -0.646 3.983 re
+65.264 5.027 m
+65.264 4.917 65.235 4.825 65.175 4.748 c
+65.117 4.678 65.021 4.644 64.896 4.644 c
+64.778 4.644 64.683 4.678 64.617 4.748 c
+64.558 4.825 64.529 4.917 64.529 5.027 c
+64.529 5.145 64.558 5.236 64.617 5.307 c
+64.683 5.384 64.778 5.424 64.896 5.424 c
+65.021 5.424 65.117 5.384 65.175 5.307 c
+65.235 5.226 65.264 5.134 65.264 5.027 c
+67.087 4.939 m
+67.087 3.983 l
+67.689 3.983 l
+67.689 3.454 l
+67.087 3.454 l
+67.087 0.985 l
+67.087 0.827 67.109 0.709 67.16 0.632 c
+67.219 0.551 67.307 0.515 67.425 0.515 c
+67.513 0.515 67.601 0.529 67.689 0.559 c
+67.689 0 l
+67.542 -0.048 67.388 -0.073 67.234 -0.073 c
+66.976 -0.073 66.781 0.019 66.646 0.206 c
+66.506 0.389 66.44 0.65 66.44 0.985 c
+66.44 3.454 l
+65.837 3.454 l
+65.837 3.983 l
+66.44 3.983 l
+66.44 4.939 l
+h
+68.498 3.175 m
+68.498 3.293 68.531 3.389 68.601 3.469 c
+68.666 3.547 68.77 3.586 68.909 3.586 c
+69.056 3.586 69.163 3.547 69.233 3.469 c
+69.31 3.389 69.35 3.293 69.35 3.175 c
+69.35 3.065 69.31 2.973 69.233 2.896 c
+69.163 2.815 69.056 2.778 68.909 2.778 c
+68.77 2.778 68.666 2.815 68.601 2.896 c
+68.531 2.973 68.498 3.065 68.498 3.175 c
+68.468 -1.073 m
+68.071 -0.808 l
+68.306 -0.485 68.427 -0.151 68.439 0.191 c
+68.439 0.808 l
+69.1 0.808 l
+69.1 0.279 l
+69.1 0.022 69.034 -0.224 68.909 -0.47 c
+68.792 -0.713 68.645 -0.915 68.468 -1.073 c
+73.084 0.485 m
+73.297 0.485 73.47 0.548 73.598 0.676 c
+73.734 0.812 73.808 1.003 73.819 1.249 c
+74.436 1.249 l
+74.414 0.867 74.278 0.548 74.025 0.294 c
+73.767 0.048 73.455 -0.073 73.084 -0.073 c
+72.591 -0.073 72.217 0.077 71.952 0.382 c
+71.695 0.694 71.57 1.161 71.57 1.779 c
+71.57 2.219 l
+71.57 2.815 71.695 3.271 71.952 3.586 c
+72.217 3.899 72.591 4.056 73.084 4.056 c
+73.484 4.056 73.804 3.925 74.039 3.66 c
+74.282 3.403 74.414 3.057 74.436 2.616 c
+73.819 2.616 l
+73.796 2.911 73.723 3.131 73.598 3.278 c
+73.48 3.425 73.308 3.499 73.084 3.499 c
+72.79 3.499 72.573 3.399 72.437 3.204 c
+72.298 3.017 72.223 2.708 72.217 2.278 c
+72.217 1.764 l
+72.217 1.294 72.283 0.96 72.422 0.764 c
+72.57 0.577 72.79 0.485 73.084 0.485 c
+77.229 0 m
+77.189 0.088 77.162 0.235 77.156 0.441 c
+76.92 0.096 76.626 -0.073 76.273 -0.073 c
+75.909 -0.073 75.627 0.022 75.421 0.22 c
+75.222 0.426 75.127 0.713 75.127 1.087 c
+75.127 1.488 75.263 1.808 75.539 2.043 c
+75.81 2.286 76.185 2.41 76.655 2.41 c
+77.141 2.41 l
+77.141 2.837 l
+77.141 3.072 77.085 3.237 76.979 3.337 c
+76.869 3.443 76.707 3.499 76.494 3.499 c
+76.296 3.499 76.134 3.439 76.009 3.322 c
+75.891 3.204 75.832 3.057 75.832 2.881 c
+75.186 2.881 l
+75.186 3.076 75.244 3.267 75.362 3.454 c
+75.487 3.638 75.649 3.785 75.847 3.896 c
+76.053 4.002 76.281 4.056 76.538 4.056 c
+76.938 4.056 77.243 3.954 77.449 3.748 c
+77.663 3.543 77.777 3.248 77.788 2.866 c
+77.788 0.852 l
+77.788 0.548 77.825 0.283 77.905 0.058 c
+77.905 0 l
+h
+76.362 0.515 m
+76.527 0.515 76.678 0.559 76.817 0.646 c
+76.965 0.735 77.071 0.845 77.141 0.985 c
+77.141 1.926 l
+76.773 1.926 l
+76.457 1.926 76.215 1.856 76.038 1.72 c
+75.862 1.591 75.774 1.404 75.774 1.161 c
+75.774 0.933 75.818 0.768 75.906 0.661 c
+75.994 0.563 76.144 0.515 76.362 0.515 c
+79.404 3.983 m
+79.419 3.543 l
+79.672 3.884 79.996 4.056 80.389 4.056 c
+81.095 4.056 81.451 3.586 81.462 2.645 c
+81.462 0 l
+80.815 0 l
+80.815 2.616 l
+80.815 2.929 80.761 3.15 80.653 3.278 c
+80.543 3.403 80.389 3.469 80.183 3.469 c
+80.025 3.469 79.878 3.414 79.743 3.308 c
+79.614 3.197 79.511 3.061 79.434 2.896 c
+79.434 0 l
+78.787 0 l
+78.787 3.983 l
+h
+84.799 3.572 m
+85.052 3.896 85.372 4.056 85.754 4.056 c
+86.46 4.056 86.816 3.586 86.827 2.645 c
+86.827 0 l
+86.18 0 l
+86.18 2.616 l
+86.18 2.929 86.126 3.15 86.019 3.278 c
+85.908 3.403 85.754 3.469 85.549 3.469 c
+85.391 3.469 85.243 3.414 85.108 3.308 c
+84.979 3.197 84.876 3.061 84.799 2.896 c
+84.799 0 l
+84.152 0 l
+84.152 5.644 l
+84.799 5.644 l
+h
+89.826 0 m
+89.785 0.088 89.76 0.235 89.752 0.441 c
+89.517 0.096 89.223 -0.073 88.871 -0.073 c
+88.507 -0.073 88.223 0.022 88.018 0.22 c
+87.82 0.426 87.724 0.713 87.724 1.087 c
+87.724 1.488 87.86 1.808 88.136 2.043 c
+88.408 2.286 88.782 2.41 89.253 2.41 c
+89.738 2.41 l
+89.738 2.837 l
+89.738 3.072 89.683 3.237 89.576 3.337 c
+89.466 3.443 89.304 3.499 89.091 3.499 c
+88.892 3.499 88.731 3.439 88.606 3.322 c
+88.489 3.204 88.43 3.057 88.43 2.881 c
+87.783 2.881 l
+87.783 3.076 87.842 3.267 87.959 3.454 c
+88.084 3.638 88.246 3.785 88.445 3.896 c
+88.65 4.002 88.878 4.056 89.135 4.056 c
+89.536 4.056 89.841 3.954 90.047 3.748 c
+90.259 3.543 90.373 3.248 90.384 2.866 c
+90.384 0.852 l
+90.384 0.548 90.421 0.283 90.502 0.058 c
+90.502 0 l
+h
+88.959 0.515 m
+89.124 0.515 89.275 0.559 89.414 0.646 c
+89.561 0.735 89.668 0.845 89.738 0.985 c
+89.738 1.926 l
+89.37 1.926 l
+89.054 1.926 88.811 1.856 88.636 1.72 c
+88.459 1.591 88.371 1.404 88.371 1.161 c
+88.371 0.933 88.414 0.768 88.503 0.661 c
+88.591 0.563 88.742 0.515 88.959 0.515 c
+92.457 0.985 m
+93.207 3.983 l
+93.868 3.983 l
+92.692 0 l
+92.207 0 l
+91.017 3.983 l
+91.678 3.983 l
+h
+95.926 -0.073 m
+95.427 -0.073 95.044 0.073 94.779 0.368 c
+94.515 0.661 94.383 1.095 94.383 1.675 c
+94.383 2.146 l
+94.383 2.741 94.508 3.208 94.764 3.543 c
+95.03 3.884 95.39 4.056 95.853 4.056 c
+96.312 4.056 96.653 3.902 96.881 3.601 c
+97.116 3.308 97.238 2.844 97.249 2.219 c
+97.249 1.793 l
+95.03 1.793 l
+95.03 1.705 l
+95.03 1.272 95.107 0.96 95.265 0.764 c
+95.43 0.577 95.662 0.485 95.955 0.485 c
+96.15 0.485 96.323 0.518 96.47 0.588 c
+96.617 0.665 96.753 0.783 96.881 0.941 c
+97.22 0.529 l
+96.933 0.125 96.503 -0.073 95.926 -0.073 c
+95.853 3.499 m
+95.577 3.499 95.375 3.403 95.25 3.219 c
+95.121 3.032 95.048 2.741 95.03 2.352 c
+96.603 2.352 l
+96.603 2.44 l
+96.58 2.822 96.514 3.09 96.397 3.248 c
+96.279 3.414 96.096 3.499 95.853 3.499 c
+101.791 0 m
+101.75 0.088 101.725 0.235 101.717 0.441 c
+101.482 0.096 101.189 -0.073 100.836 -0.073 c
+100.472 -0.073 100.189 0.022 99.983 0.22 c
+99.784 0.426 99.689 0.713 99.689 1.087 c
+99.689 1.488 99.825 1.808 100.1 2.043 c
+100.372 2.286 100.748 2.41 101.218 2.41 c
+101.702 2.41 l
+101.702 2.837 l
+101.702 3.072 101.648 3.237 101.542 3.337 c
+101.431 3.443 101.27 3.499 101.056 3.499 c
+100.858 3.499 100.696 3.439 100.571 3.322 c
+100.453 3.204 100.395 3.057 100.395 2.881 c
+99.748 2.881 l
+99.748 3.076 99.807 3.267 99.924 3.454 c
+100.049 3.638 100.21 3.785 100.409 3.896 c
+100.615 4.002 100.843 4.056 101.1 4.056 c
+101.501 4.056 101.806 3.954 102.012 3.748 c
+102.224 3.543 102.338 3.248 102.35 2.866 c
+102.35 0.852 l
+102.35 0.548 102.386 0.283 102.467 0.058 c
+102.467 0 l
+h
+100.923 0.515 m
+101.089 0.515 101.24 0.559 101.38 0.646 c
+101.526 0.735 101.633 0.845 101.702 0.985 c
+101.702 1.926 l
+101.336 1.926 l
+101.019 1.926 100.777 1.856 100.601 1.72 c
+100.424 1.591 100.336 1.404 100.336 1.161 c
+100.336 0.933 100.38 0.768 100.468 0.661 c
+100.557 0.563 100.707 0.515 100.923 0.515 c
+f
+Q
+q 1 0 0 1 437.8812 189.7368 cm
+0 0 m
+0 -0.971 l
+0.53 -0.971 l
+0.53 -1.764 l
+0 -1.764 l
+0 -3.734 l
+0 -3.892 0.019 -3.998 0.059 -4.057 c
+0.107 -4.116 0.192 -4.146 0.31 -4.146 c
+0.416 -4.146 0.5 -4.138 0.559 -4.116 c
+0.559 -4.925 l
+0.383 -4.991 0.192 -5.027 -0.014 -5.027 c
+-0.69 -5.027 -1.036 -4.642 -1.043 -3.866 c
+-1.043 -1.764 l
+-1.499 -1.764 l
+-1.499 -0.971 l
+-1.043 -0.971 l
+-1.043 0 l
+h
+3.057 -1.985 m
+2.72 -1.956 l
+2.433 -1.956 2.242 -2.08 2.147 -2.323 c
+2.147 -4.954 l
+1.103 -4.954 l
+1.103 -0.971 l
+2.073 -0.971 l
+2.103 -1.411 l
+2.268 -1.07 2.5 -0.897 2.793 -0.897 c
+2.911 -0.897 3.003 -0.919 3.072 -0.956 c
+h
+5.38 -4.954 m
+5.351 -4.896 5.322 -4.792 5.292 -4.645 c
+5.104 -4.902 4.855 -5.027 4.543 -5.027 c
+4.208 -5.027 3.929 -4.921 3.705 -4.704 c
+3.487 -4.48 3.381 -4.19 3.381 -3.837 c
+3.381 -3.425 3.514 -3.109 3.778 -2.881 c
+4.042 -2.646 4.425 -2.529 4.925 -2.529 c
+5.248 -2.529 l
+5.248 -2.205 l
+5.248 -2.029 5.212 -1.908 5.145 -1.838 c
+5.087 -1.76 4.998 -1.721 4.881 -1.721 c
+4.623 -1.721 4.499 -1.875 4.499 -2.176 c
+3.454 -2.176 l
+3.454 -1.804 3.591 -1.5 3.866 -1.264 c
+4.138 -1.022 4.487 -0.897 4.91 -0.897 c
+5.351 -0.897 5.689 -1.015 5.924 -1.25 c
+6.167 -1.478 6.292 -1.801 6.292 -2.22 c
+6.292 -4.087 l
+6.292 -4.432 6.34 -4.7 6.438 -4.896 c
+6.438 -4.954 l
+h
+4.778 -4.204 m
+4.884 -4.204 4.977 -4.186 5.056 -4.146 c
+5.145 -4.098 5.208 -4.039 5.248 -3.969 c
+5.248 -3.146 l
+4.998 -3.146 l
+4.821 -3.146 4.678 -3.205 4.572 -3.323 c
+4.472 -3.433 4.425 -3.58 4.425 -3.763 c
+4.425 -4.057 4.543 -4.204 4.778 -4.204 c
+8.482 -4.16 m
+8.776 -4.16 8.927 -3.965 8.937 -3.572 c
+9.908 -3.572 l
+9.908 -4.006 9.775 -4.358 9.511 -4.63 c
+9.247 -4.896 8.908 -5.027 8.497 -5.027 c
+7.986 -5.027 7.593 -4.873 7.321 -4.557 c
+7.056 -4.233 6.916 -3.763 6.909 -3.146 c
+6.909 -2.822 l
+6.909 -2.198 7.041 -1.721 7.306 -1.397 c
+7.578 -1.066 7.975 -0.897 8.497 -0.897 c
+8.927 -0.897 9.268 -1.037 9.525 -1.309 c
+9.779 -1.584 9.908 -1.966 9.908 -2.455 c
+8.937 -2.455 l
+8.937 -2.242 8.897 -2.073 8.819 -1.956 c
+8.75 -1.831 8.632 -1.764 8.467 -1.764 c
+8.291 -1.764 8.162 -1.831 8.085 -1.956 c
+8.004 -2.084 7.96 -2.334 7.953 -2.705 c
+7.953 -3.117 l
+7.953 -3.44 7.967 -3.668 7.996 -3.793 c
+8.034 -3.921 8.088 -4.013 8.158 -4.072 c
+8.235 -4.131 8.342 -4.16 8.482 -4.16 c
+11.834 -3.425 m
+11.554 -3.734 l
+11.554 -4.954 l
+10.51 -4.954 l
+10.51 0.691 l
+11.554 0.691 l
+11.554 -2.352 l
+11.672 -2.161 l
+12.391 -0.971 l
+13.641 -0.971 l
+12.48 -2.617 l
+13.744 -4.954 l
+12.553 -4.954 l
+h
+15.64 -5.027 m
+15.111 -5.027 14.692 -4.873 14.391 -4.557 c
+14.097 -4.233 13.95 -3.774 13.95 -3.175 c
+13.95 -2.866 l
+13.95 -2.242 14.086 -1.757 14.361 -1.411 c
+14.633 -1.07 15.026 -0.897 15.537 -0.897 c
+16.037 -0.897 16.408 -1.058 16.655 -1.382 c
+16.908 -1.706 17.041 -2.183 17.051 -2.808 c
+17.051 -3.308 l
+14.978 -3.308 l
+14.997 -3.602 15.059 -3.818 15.169 -3.955 c
+15.287 -4.094 15.468 -4.16 15.714 -4.16 c
+16.056 -4.16 16.345 -4.042 16.581 -3.807 c
+16.993 -4.439 l
+16.864 -4.616 16.676 -4.759 16.434 -4.865 c
+16.187 -4.973 15.923 -5.027 15.64 -5.027 c
+14.993 -2.587 m
+16.023 -2.587 l
+16.023 -2.484 l
+16.023 -2.249 15.982 -2.073 15.905 -1.956 c
+15.835 -1.831 15.707 -1.764 15.522 -1.764 c
+15.346 -1.764 15.214 -1.834 15.126 -1.97 c
+15.045 -2.099 15.001 -2.305 14.993 -2.587 c
+17.492 -2.837 m
+17.492 -2.191 17.598 -1.706 17.816 -1.382 c
+18.04 -1.058 18.363 -0.897 18.786 -0.897 c
+19.098 -0.897 19.352 -1.029 19.55 -1.294 c
+19.55 0.691 l
+20.609 0.691 l
+20.609 -4.954 l
+19.653 -4.954 l
+19.609 -4.543 l
+19.392 -4.865 19.117 -5.027 18.786 -5.027 c
+18.375 -5.027 18.055 -4.873 17.831 -4.557 c
+17.613 -4.233 17.5 -3.763 17.492 -3.146 c
+h
+18.536 -3.102 m
+18.536 -3.495 18.573 -3.77 18.654 -3.925 c
+18.741 -4.083 18.889 -4.16 19.094 -4.16 c
+19.3 -4.16 19.451 -4.069 19.55 -3.881 c
+19.55 -2.073 l
+19.451 -1.878 19.3 -1.779 19.094 -1.779 c
+18.896 -1.779 18.756 -1.86 18.668 -2.014 c
+18.58 -2.161 18.536 -2.433 18.536 -2.822 c
+h
+25.062 -4.586 m
+24.846 -4.881 24.555 -5.027 24.195 -5.027 c
+23.831 -5.027 23.552 -4.906 23.358 -4.66 c
+23.169 -4.406 23.078 -4.039 23.078 -3.558 c
+23.078 -0.971 l
+24.121 -0.971 l
+24.121 -3.572 l
+24.121 -3.965 24.247 -4.16 24.503 -4.16 c
+24.739 -4.16 24.908 -4.057 25.018 -3.851 c
+25.018 -0.971 l
+26.062 -0.971 l
+26.062 -4.954 l
+25.091 -4.954 l
+h
+29.913 -3.087 m
+29.913 -3.716 29.803 -4.197 29.59 -4.528 c
+29.373 -4.862 29.053 -5.027 28.634 -5.027 c
+28.311 -5.027 28.05 -4.896 27.855 -4.63 c
+27.855 -6.483 l
+26.811 -6.483 l
+26.811 -0.971 l
+27.767 -0.971 l
+27.811 -1.338 l
+28.006 -1.044 28.274 -0.897 28.619 -0.897 c
+29.039 -0.897 29.358 -1.055 29.575 -1.368 c
+29.788 -1.673 29.902 -2.143 29.913 -2.779 c
+h
+28.869 -2.822 m
+28.869 -2.44 28.825 -2.172 28.737 -2.014 c
+28.656 -1.86 28.517 -1.779 28.311 -1.779 c
+28.105 -1.779 27.95 -1.867 27.855 -2.043 c
+27.855 -3.911 l
+27.944 -4.079 28.098 -4.16 28.326 -4.16 c
+28.532 -4.16 28.671 -4.079 28.752 -3.911 c
+28.829 -3.734 28.869 -3.462 28.869 -3.087 c
+h
+32.309 -3.881 m
+32.309 -3.793 32.265 -3.716 32.177 -3.645 c
+32.089 -3.568 31.901 -3.466 31.618 -3.337 c
+31.185 -3.161 30.886 -2.98 30.722 -2.793 c
+30.564 -2.61 30.487 -2.378 30.487 -2.103 c
+30.487 -1.76 30.608 -1.478 30.853 -1.25 c
+31.107 -1.015 31.445 -0.897 31.868 -0.897 c
+32.297 -0.897 32.647 -1.011 32.912 -1.235 c
+33.176 -1.463 33.309 -1.764 33.309 -2.147 c
+32.265 -2.147 l
+32.265 -1.823 32.125 -1.661 31.853 -1.661 c
+31.743 -1.661 31.655 -1.698 31.589 -1.764 c
+31.519 -1.834 31.486 -1.933 31.486 -2.058 c
+31.486 -2.147 31.522 -2.228 31.603 -2.294 c
+31.68 -2.352 31.861 -2.448 32.147 -2.573 c
+32.577 -2.731 32.875 -2.907 33.043 -3.102 c
+33.22 -3.289 33.309 -3.539 33.309 -3.851 c
+33.309 -4.204 33.176 -4.491 32.912 -4.704 c
+32.647 -4.921 32.297 -5.027 31.868 -5.027 c
+31.574 -5.027 31.313 -4.973 31.089 -4.865 c
+30.861 -4.748 30.685 -4.586 30.56 -4.381 c
+30.442 -4.175 30.383 -3.955 30.383 -3.72 c
+31.368 -3.72 l
+31.368 -3.907 31.405 -4.042 31.486 -4.131 c
+31.574 -4.219 31.707 -4.263 31.883 -4.263 c
+32.166 -4.263 32.309 -4.138 32.309 -3.881 c
+35.175 0 m
+35.175 -0.971 l
+35.705 -0.971 l
+35.705 -1.764 l
+35.175 -1.764 l
+35.175 -3.734 l
+35.175 -3.892 35.194 -3.998 35.234 -4.057 c
+35.281 -4.116 35.366 -4.146 35.484 -4.146 c
+35.59 -4.146 35.675 -4.138 35.734 -4.116 c
+35.734 -4.925 l
+35.557 -4.991 35.366 -5.027 35.16 -5.027 c
+34.485 -5.027 34.139 -4.642 34.132 -3.866 c
+34.132 -1.764 l
+33.676 -1.764 l
+33.676 -0.971 l
+34.132 -0.971 l
+34.132 0 l
+h
+38.233 -1.985 m
+37.895 -1.956 l
+37.608 -1.956 37.417 -2.08 37.321 -2.323 c
+37.321 -4.954 l
+36.278 -4.954 l
+36.278 -0.971 l
+37.248 -0.971 l
+37.277 -1.411 l
+37.442 -1.07 37.674 -0.897 37.968 -0.897 c
+38.086 -0.897 38.177 -0.919 38.248 -0.956 c
+h
+40.305 -5.027 m
+39.776 -5.027 39.357 -4.873 39.056 -4.557 c
+38.762 -4.233 38.615 -3.774 38.615 -3.175 c
+38.615 -2.866 l
+38.615 -2.242 38.751 -1.757 39.027 -1.411 c
+39.298 -1.07 39.692 -0.897 40.202 -0.897 c
+40.702 -0.897 41.074 -1.058 41.319 -1.382 c
+41.573 -1.706 41.705 -2.183 41.716 -2.808 c
+41.716 -3.308 l
+39.644 -3.308 l
+39.663 -3.602 39.725 -3.818 39.835 -3.955 c
+39.952 -4.094 40.133 -4.16 40.378 -4.16 c
+40.721 -4.16 41.011 -4.042 41.246 -3.807 c
+41.658 -4.439 l
+41.529 -4.616 41.342 -4.759 41.099 -4.865 c
+40.853 -4.973 40.588 -5.027 40.305 -5.027 c
+39.659 -2.587 m
+40.687 -2.587 l
+40.687 -2.484 l
+40.687 -2.249 40.647 -2.073 40.569 -1.956 c
+40.5 -1.831 40.371 -1.764 40.187 -1.764 c
+40.012 -1.764 39.879 -1.834 39.79 -1.97 c
+39.71 -2.099 39.665 -2.305 39.659 -2.587 c
+44.186 -4.954 m
+44.156 -4.896 44.127 -4.792 44.097 -4.645 c
+43.91 -4.902 43.661 -5.027 43.348 -5.027 c
+43.013 -5.027 42.734 -4.921 42.51 -4.704 c
+42.293 -4.48 42.187 -4.19 42.187 -3.837 c
+42.187 -3.425 42.319 -3.109 42.584 -2.881 c
+42.848 -2.646 43.231 -2.529 43.73 -2.529 c
+44.054 -2.529 l
+44.054 -2.205 l
+44.054 -2.029 44.017 -1.908 43.95 -1.838 c
+43.892 -1.76 43.804 -1.721 43.686 -1.721 c
+43.428 -1.721 43.304 -1.875 43.304 -2.176 c
+42.26 -2.176 l
+42.26 -1.804 42.396 -1.5 42.672 -1.264 c
+42.944 -1.022 43.293 -0.897 43.715 -0.897 c
+44.156 -0.897 44.494 -1.015 44.729 -1.25 c
+44.972 -1.478 45.097 -1.801 45.097 -2.22 c
+45.097 -4.087 l
+45.097 -4.432 45.145 -4.7 45.244 -4.896 c
+45.244 -4.954 l
+h
+43.583 -4.204 m
+43.69 -4.204 43.782 -4.186 43.862 -4.146 c
+43.95 -4.098 44.013 -4.039 44.054 -3.969 c
+44.054 -3.146 l
+43.804 -3.146 l
+43.627 -3.146 43.484 -3.205 43.378 -3.323 c
+43.278 -3.433 43.231 -3.58 43.231 -3.763 c
+43.231 -4.057 43.348 -4.204 43.583 -4.204 c
+46.817 -0.971 m
+46.846 -1.338 l
+47.081 -1.044 47.39 -0.897 47.773 -0.897 c
+48.172 -0.897 48.452 -1.081 48.61 -1.441 c
+48.845 -1.081 49.172 -0.897 49.595 -0.897 c
+50.289 -0.897 50.642 -1.382 50.653 -2.352 c
+50.653 -4.954 l
+49.624 -4.954 l
+49.624 -2.411 l
+49.624 -2.187 49.587 -2.025 49.521 -1.926 c
+49.463 -1.831 49.352 -1.779 49.198 -1.779 c
+48.999 -1.779 48.86 -1.897 48.772 -2.132 c
+48.772 -4.954 l
+47.728 -4.954 l
+47.728 -2.426 l
+47.728 -2.191 47.699 -2.025 47.64 -1.926 c
+47.582 -1.831 47.471 -1.779 47.316 -1.779 c
+47.14 -1.779 46.997 -1.875 46.89 -2.058 c
+46.89 -4.954 l
+45.847 -4.954 l
+45.847 -0.971 l
+h
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 310.3894 176.5843 cm
+0 0 m
+0 -0.97 l
+0.53 -0.97 l
+0.53 -1.764 l
+0 -1.764 l
+0 -3.734 l
+0 -3.892 0.019 -3.998 0.059 -4.056 c
+0.106 -4.116 0.191 -4.145 0.309 -4.145 c
+0.415 -4.145 0.5 -4.137 0.559 -4.116 c
+0.559 -4.924 l
+0.382 -4.991 0.191 -5.027 -0.015 -5.027 c
+-0.691 -5.027 -1.037 -4.641 -1.043 -3.865 c
+-1.043 -1.764 l
+-1.5 -1.764 l
+-1.5 -0.97 l
+-1.043 -0.97 l
+-1.043 0 l
+h
+2.984 -4.953 m
+2.955 -4.895 2.925 -4.792 2.896 -4.644 c
+2.708 -4.902 2.458 -5.027 2.146 -5.027 c
+1.812 -5.027 1.532 -4.92 1.309 -4.704 c
+1.091 -4.48 0.985 -4.189 0.985 -3.836 c
+0.985 -3.424 1.117 -3.109 1.382 -2.881 c
+1.646 -2.645 2.028 -2.528 2.528 -2.528 c
+2.851 -2.528 l
+2.851 -2.205 l
+2.851 -2.028 2.815 -1.907 2.749 -1.837 c
+2.69 -1.76 2.602 -1.72 2.484 -1.72 c
+2.227 -1.72 2.102 -1.874 2.102 -2.175 c
+1.058 -2.175 l
+1.058 -1.804 1.195 -1.5 1.47 -1.264 c
+1.742 -1.022 2.091 -0.897 2.514 -0.897 c
+2.955 -0.897 3.293 -1.014 3.528 -1.249 c
+3.77 -1.477 3.896 -1.801 3.896 -2.219 c
+3.896 -4.087 l
+3.896 -4.432 3.943 -4.7 4.042 -4.895 c
+4.042 -4.953 l
+h
+2.381 -4.204 m
+2.488 -4.204 2.579 -4.185 2.66 -4.145 c
+2.749 -4.097 2.811 -4.039 2.851 -3.969 c
+2.851 -3.146 l
+2.602 -3.146 l
+2.425 -3.146 2.282 -3.204 2.176 -3.322 c
+2.076 -3.432 2.028 -3.579 2.028 -3.763 c
+2.028 -4.056 2.146 -4.204 2.381 -4.204 c
+4.513 -2.836 m
+4.513 -2.19 4.63 -1.705 4.866 -1.382 c
+5.101 -1.058 5.431 -0.897 5.865 -0.897 c
+6.217 -0.897 6.489 -1.04 6.688 -1.323 c
+6.732 -0.97 l
+7.673 -0.97 l
+7.673 -4.953 l
+7.673 -5.461 7.53 -5.85 7.247 -6.115 c
+6.96 -6.387 6.556 -6.526 6.026 -6.526 c
+5.799 -6.526 5.564 -6.482 5.321 -6.394 c
+5.086 -6.306 4.91 -6.192 4.792 -6.056 c
+5.145 -5.336 l
+5.24 -5.442 5.369 -5.527 5.527 -5.585 c
+5.681 -5.652 5.828 -5.689 5.968 -5.689 c
+6.203 -5.689 6.369 -5.629 6.468 -5.512 c
+6.574 -5.402 6.629 -5.226 6.629 -4.983 c
+6.629 -4.63 l
+6.431 -4.895 6.174 -5.027 5.85 -5.027 c
+5.428 -5.027 5.101 -4.865 4.866 -4.542 c
+4.638 -4.211 4.52 -3.74 4.513 -3.131 c
+h
+5.556 -3.102 m
+5.556 -3.476 5.604 -3.744 5.704 -3.91 c
+5.799 -4.079 5.953 -4.16 6.159 -4.16 c
+6.372 -4.16 6.53 -4.083 6.629 -3.925 c
+6.629 -2.028 l
+6.519 -1.863 6.365 -1.778 6.159 -1.778 c
+5.953 -1.778 5.799 -1.863 5.704 -2.028 c
+5.604 -2.198 5.556 -2.466 5.556 -2.836 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 331.7103 171.6313 cm
+0 0 m
+-0.04 0.087 -0.066 0.235 -0.073 0.44 c
+-0.309 0.095 -0.603 -0.074 -0.956 -0.074 c
+-1.319 -0.074 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.102 0.712 -2.102 1.088 c
+-2.102 1.488 -1.966 1.807 -1.691 2.042 c
+-1.419 2.285 -1.043 2.41 -0.573 2.41 c
+-0.088 2.41 l
+-0.088 2.836 l
+-0.088 3.072 -0.143 3.237 -0.25 3.336 c
+-0.36 3.443 -0.522 3.498 -0.735 3.498 c
+-0.933 3.498 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.984 3.266 -1.866 3.453 c
+-1.741 3.638 -1.58 3.785 -1.382 3.895 c
+-1.176 4.001 -0.948 4.056 -0.691 4.056 c
+-0.291 4.056 0.015 3.954 0.22 3.748 c
+0.434 3.542 0.548 3.248 0.559 2.865 c
+0.559 0.852 l
+0.559 0.548 0.595 0.282 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.514 m
+-0.702 0.514 -0.551 0.558 -0.411 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.984 c
+-0.088 1.925 l
+-0.455 1.925 l
+-0.771 1.925 -1.014 1.855 -1.191 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.234 0.562 -1.084 0.514 -0.867 0.514 c
+4.924 3.366 m
+4.836 3.384 4.737 3.395 4.63 3.395 c
+4.295 3.395 4.06 3.212 3.925 2.851 c
+3.925 0 l
+3.278 0 l
+3.278 3.983 l
+3.91 3.983 l
+3.925 3.571 l
+4.101 3.895 4.343 4.056 4.659 4.056 c
+4.766 4.056 4.854 4.035 4.924 3.998 c
+h
+6.923 -0.074 m
+6.423 -0.074 6.041 0.073 5.777 0.367 c
+5.512 0.661 5.38 1.095 5.38 1.675 c
+5.38 2.146 l
+5.38 2.741 5.505 3.208 5.762 3.542 c
+6.026 3.883 6.387 4.056 6.85 4.056 c
+7.31 4.056 7.651 3.902 7.879 3.601 c
+8.114 3.307 8.235 2.844 8.247 2.219 c
+8.247 1.793 l
+6.026 1.793 l
+6.026 1.705 l
+6.026 1.271 6.104 0.959 6.262 0.764 c
+6.427 0.577 6.659 0.484 6.953 0.484 c
+7.148 0.484 7.32 0.517 7.468 0.588 c
+7.614 0.665 7.75 0.783 7.879 0.941 c
+8.216 0.529 l
+7.93 0.124 7.501 -0.074 6.923 -0.074 c
+6.85 3.498 m
+6.574 3.498 6.372 3.403 6.247 3.218 c
+6.119 3.031 6.045 2.741 6.026 2.352 c
+7.599 2.352 l
+7.599 2.439 l
+7.578 2.822 7.511 3.09 7.393 3.248 c
+7.276 3.413 7.092 3.498 6.85 3.498 c
+9.319 0 m
+9.319 3.453 l
+8.79 3.453 l
+8.79 3.983 l
+9.319 3.983 l
+9.319 4.438 l
+9.319 4.839 9.415 5.151 9.613 5.379 c
+9.819 5.604 10.098 5.718 10.451 5.718 c
+10.587 5.718 10.72 5.695 10.848 5.659 c
+10.819 5.115 l
+10.72 5.134 10.62 5.144 10.525 5.144 c
+10.15 5.144 9.966 4.88 9.966 4.351 c
+9.966 3.983 l
+10.643 3.983 l
+10.643 3.453 l
+9.966 3.453 l
+9.966 0 l
+h
+12.744 -0.074 m
+12.245 -0.074 11.862 0.073 11.597 0.367 c
+11.333 0.661 11.2 1.095 11.2 1.675 c
+11.2 2.146 l
+11.2 2.741 11.326 3.208 11.583 3.542 c
+11.848 3.883 12.208 4.056 12.671 4.056 c
+13.13 4.056 13.471 3.902 13.7 3.601 c
+13.935 3.307 14.056 2.844 14.067 2.219 c
+14.067 1.793 l
+11.848 1.793 l
+11.848 1.705 l
+11.848 1.271 11.925 0.959 12.083 0.764 c
+12.248 0.577 12.48 0.484 12.773 0.484 c
+12.968 0.484 13.141 0.517 13.288 0.588 c
+13.435 0.665 13.571 0.783 13.7 0.941 c
+14.038 0.529 l
+13.751 0.124 13.321 -0.074 12.744 -0.074 c
+12.671 3.498 m
+12.395 3.498 12.193 3.403 12.068 3.218 c
+11.939 3.031 11.866 2.741 11.848 2.352 c
+13.421 2.352 l
+13.421 2.439 l
+13.398 2.822 13.332 3.09 13.215 3.248 c
+13.097 3.413 12.914 3.498 12.671 3.498 c
+16.492 3.366 m
+16.405 3.384 16.305 3.395 16.199 3.395 c
+15.864 3.395 15.629 3.212 15.493 2.851 c
+15.493 0 l
+14.846 0 l
+14.846 3.983 l
+15.478 3.983 l
+15.493 3.571 l
+15.669 3.895 15.912 4.056 16.228 4.056 c
+16.334 4.056 16.422 4.035 16.492 3.998 c
+h
+18.491 -0.074 m
+17.992 -0.074 17.61 0.073 17.345 0.367 c
+17.08 0.661 16.948 1.095 16.948 1.675 c
+16.948 2.146 l
+16.948 2.741 17.073 3.208 17.33 3.542 c
+17.595 3.883 17.955 4.056 18.418 4.056 c
+18.877 4.056 19.219 3.902 19.447 3.601 c
+19.682 3.307 19.803 2.844 19.815 2.219 c
+19.815 1.793 l
+17.595 1.793 l
+17.595 1.705 l
+17.595 1.271 17.672 0.959 17.83 0.764 c
+17.995 0.577 18.227 0.484 18.521 0.484 c
+18.716 0.484 18.888 0.517 19.035 0.588 c
+19.183 0.665 19.318 0.783 19.447 0.941 c
+19.785 0.529 l
+19.499 0.124 19.069 -0.074 18.491 -0.074 c
+18.418 3.498 m
+18.142 3.498 17.94 3.403 17.816 3.218 c
+17.687 3.031 17.613 2.741 17.595 2.352 c
+19.168 2.352 l
+19.168 2.439 l
+19.146 2.822 19.079 3.09 18.962 3.248 c
+18.844 3.413 18.66 3.498 18.418 3.498 c
+21.211 3.983 m
+21.226 3.542 l
+21.479 3.883 21.803 4.056 22.196 4.056 c
+22.901 4.056 23.258 3.586 23.268 2.645 c
+23.268 0 l
+22.622 0 l
+22.622 2.616 l
+22.622 2.929 22.566 3.149 22.46 3.278 c
+22.35 3.403 22.196 3.469 21.99 3.469 c
+21.832 3.469 21.685 3.413 21.549 3.307 c
+21.421 3.197 21.317 3.06 21.24 2.896 c
+21.24 0 l
+20.594 0 l
+20.594 3.983 l
+h
+25.62 0.484 m
+25.834 0.484 26.007 0.548 26.135 0.675 c
+26.271 0.812 26.344 1.003 26.356 1.249 c
+26.973 1.249 l
+26.951 0.866 26.815 0.548 26.561 0.294 c
+26.304 0.047 25.992 -0.074 25.62 -0.074 c
+25.128 -0.074 24.754 0.077 24.488 0.382 c
+24.232 0.694 24.106 1.161 24.106 1.778 c
+24.106 2.219 l
+24.106 2.815 24.232 3.27 24.488 3.586 c
+24.754 3.898 25.128 4.056 25.62 4.056 c
+26.021 4.056 26.341 3.924 26.576 3.659 c
+26.819 3.403 26.951 3.057 26.973 2.616 c
+26.356 2.616 l
+26.333 2.91 26.26 3.131 26.135 3.278 c
+26.017 3.424 25.845 3.498 25.62 3.498 c
+25.327 3.498 25.109 3.399 24.974 3.204 c
+24.835 3.017 24.76 2.707 24.754 2.278 c
+24.754 1.764 l
+24.754 1.294 24.82 0.959 24.959 0.764 c
+25.106 0.577 25.327 0.484 25.62 0.484 c
+29.148 -0.074 m
+28.648 -0.074 28.266 0.073 28.002 0.367 c
+27.737 0.661 27.605 1.095 27.605 1.675 c
+27.605 2.146 l
+27.605 2.741 27.73 3.208 27.987 3.542 c
+28.251 3.883 28.612 4.056 29.074 4.056 c
+29.534 4.056 29.876 3.902 30.104 3.601 c
+30.339 3.307 30.46 2.844 30.471 2.219 c
+30.471 1.793 l
+28.251 1.793 l
+28.251 1.705 l
+28.251 1.271 28.328 0.959 28.487 0.764 c
+28.652 0.577 28.884 0.484 29.178 0.484 c
+29.373 0.484 29.545 0.517 29.693 0.588 c
+29.839 0.665 29.975 0.783 30.104 0.941 c
+30.441 0.529 l
+30.155 0.124 29.725 -0.074 29.148 -0.074 c
+29.074 3.498 m
+28.799 3.498 28.598 3.403 28.472 3.218 c
+28.344 3.031 28.27 2.741 28.251 2.352 c
+29.824 2.352 l
+29.824 2.439 l
+29.803 2.822 29.736 3.09 29.618 3.248 c
+29.501 3.413 29.317 3.498 29.074 3.498 c
+32.955 2.175 m
+32.955 2.811 33.043 3.413 33.22 3.983 c
+33.396 4.548 33.639 5.045 33.955 5.468 c
+34.15 5.732 34.337 5.923 34.514 6.04 c
+34.646 5.585 l
+34.352 5.31 34.109 4.887 33.926 4.321 c
+33.738 3.752 33.635 3.119 33.616 2.425 c
+33.616 2.131 l
+33.616 1.267 33.734 0.503 33.969 -0.162 c
+34.154 -0.662 34.381 -1.044 34.646 -1.309 c
+34.514 -1.735 l
+34.285 -1.577 34.061 -1.327 33.837 -0.985 c
+33.249 -0.103 32.955 0.947 32.955 2.175 c
+37.277 1.014 m
+37.277 1.161 37.221 1.282 37.115 1.381 c
+37.005 1.477 36.799 1.595 36.498 1.734 c
+36.153 1.881 35.91 2.002 35.763 2.102 c
+35.615 2.208 35.505 2.326 35.44 2.454 c
+35.37 2.58 35.337 2.738 35.337 2.925 c
+35.337 3.248 35.455 3.516 35.69 3.733 c
+35.925 3.946 36.226 4.056 36.6 4.056 c
+36.983 4.056 37.292 3.943 37.527 3.719 c
+37.762 3.49 37.88 3.204 37.88 2.851 c
+37.233 2.851 l
+37.233 3.027 37.174 3.178 37.057 3.307 c
+36.939 3.432 36.785 3.498 36.6 3.498 c
+36.402 3.498 36.251 3.443 36.145 3.336 c
+36.035 3.237 35.983 3.104 35.983 2.94 c
+35.983 2.811 36.02 2.705 36.101 2.616 c
+36.178 2.535 36.369 2.432 36.674 2.308 c
+37.152 2.119 37.483 1.932 37.659 1.749 c
+37.836 1.572 37.924 1.344 37.924 1.072 c
+37.924 0.72 37.799 0.44 37.556 0.235 c
+37.321 0.029 37.005 -0.074 36.615 -0.074 c
+36.193 -0.074 35.854 0.043 35.601 0.278 c
+35.344 0.521 35.219 0.826 35.219 1.19 c
+35.866 1.19 l
+35.873 0.962 35.943 0.786 36.072 0.661 c
+36.197 0.544 36.38 0.484 36.615 0.484 c
+36.829 0.484 36.99 0.532 37.101 0.631 c
+37.218 0.727 37.277 0.856 37.277 1.014 c
+39.614 4.939 m
+39.614 3.983 l
+40.217 3.983 l
+40.217 3.453 l
+39.614 3.453 l
+39.614 0.984 l
+39.614 0.826 39.636 0.708 39.688 0.631 c
+39.746 0.55 39.835 0.514 39.952 0.514 c
+40.041 0.514 40.128 0.529 40.217 0.558 c
+40.217 0 l
+40.07 -0.048 39.916 -0.074 39.761 -0.074 c
+39.504 -0.074 39.309 0.018 39.173 0.205 c
+39.033 0.389 38.967 0.65 38.967 0.984 c
+38.967 3.453 l
+38.364 3.453 l
+38.364 3.983 l
+38.967 3.983 l
+38.967 4.939 l
+h
+43.024 0 m
+42.983 0.087 42.958 0.235 42.95 0.44 c
+42.715 0.095 42.422 -0.074 42.069 -0.074 c
+41.705 -0.074 41.422 0.022 41.217 0.22 c
+41.018 0.426 40.922 0.712 40.922 1.088 c
+40.922 1.488 41.059 1.807 41.334 2.042 c
+41.606 2.285 41.98 2.41 42.451 2.41 c
+42.936 2.41 l
+42.936 2.836 l
+42.936 3.072 42.881 3.237 42.774 3.336 c
+42.664 3.443 42.503 3.498 42.289 3.498 c
+42.091 3.498 41.929 3.439 41.805 3.322 c
+41.687 3.204 41.628 3.057 41.628 2.881 c
+40.981 2.881 l
+40.981 3.075 41.04 3.266 41.157 3.453 c
+41.282 3.638 41.444 3.785 41.643 3.895 c
+41.848 4.001 42.076 4.056 42.333 4.056 c
+42.734 4.056 43.039 3.954 43.245 3.748 c
+43.457 3.542 43.571 3.248 43.583 2.865 c
+43.583 0.852 l
+43.583 0.548 43.619 0.282 43.7 0.058 c
+43.7 0 l
+h
+42.157 0.514 m
+42.322 0.514 42.473 0.558 42.613 0.646 c
+42.759 0.735 42.866 0.845 42.936 0.984 c
+42.936 1.925 l
+42.568 1.925 l
+42.252 1.925 42.01 1.855 41.834 1.72 c
+41.657 1.591 41.569 1.404 41.569 1.161 c
+41.569 0.933 41.613 0.768 41.701 0.661 c
+41.79 0.562 41.94 0.514 42.157 0.514 c
+45.2 3.983 m
+45.215 3.542 l
+45.468 3.883 45.791 4.056 46.185 4.056 c
+46.89 4.056 47.247 3.586 47.257 2.645 c
+47.257 0 l
+46.611 0 l
+46.611 2.616 l
+46.611 2.929 46.555 3.149 46.449 3.278 c
+46.339 3.403 46.185 3.469 45.979 3.469 c
+45.821 3.469 45.674 3.413 45.537 3.307 c
+45.409 3.197 45.306 3.06 45.229 2.896 c
+45.229 0 l
+44.583 0 l
+44.583 3.983 l
+h
+48.095 2.175 m
+48.095 2.782 48.205 3.248 48.433 3.571 c
+48.668 3.895 48.996 4.056 49.418 4.056 c
+49.801 4.056 50.098 3.898 50.315 3.586 c
+50.315 5.644 l
+50.962 5.644 l
+50.962 0 l
+50.374 0 l
+50.33 0.426 l
+50.124 0.091 49.819 -0.074 49.418 -0.074 c
+49.007 -0.074 48.683 0.08 48.448 0.396 c
+48.213 0.72 48.095 1.176 48.095 1.764 c
+h
+48.743 1.793 m
+48.743 1.352 48.805 1.021 48.934 0.808 c
+49.069 0.602 49.29 0.5 49.595 0.5 c
+49.918 0.5 50.156 0.661 50.315 0.984 c
+50.315 2.998 l
+50.146 3.31 49.907 3.469 49.595 3.469 c
+49.29 3.469 49.069 3.366 48.934 3.16 c
+48.805 2.954 48.743 2.63 48.743 2.19 c
+h
+54.004 0 m
+53.964 0.087 53.938 0.235 53.931 0.44 c
+53.696 0.095 53.402 -0.074 53.049 -0.074 c
+52.685 -0.074 52.402 0.022 52.196 0.22 c
+51.997 0.426 51.902 0.712 51.902 1.088 c
+51.902 1.488 52.038 1.807 52.314 2.042 c
+52.586 2.285 52.961 2.41 53.431 2.41 c
+53.917 2.41 l
+53.917 2.836 l
+53.917 3.072 53.861 3.237 53.755 3.336 c
+53.644 3.443 53.483 3.498 53.269 3.498 c
+53.071 3.498 52.909 3.439 52.784 3.322 c
+52.666 3.204 52.608 3.057 52.608 2.881 c
+51.961 2.881 l
+51.961 3.075 52.02 3.266 52.138 3.453 c
+52.263 3.638 52.425 3.785 52.623 3.895 c
+52.828 4.001 53.057 4.056 53.314 4.056 c
+53.714 4.056 54.019 3.954 54.225 3.748 c
+54.438 3.542 54.552 3.248 54.563 2.865 c
+54.563 0.852 l
+54.563 0.548 54.6 0.282 54.68 0.058 c
+54.68 0 l
+h
+53.137 0.514 m
+53.302 0.514 53.453 0.558 53.593 0.646 c
+53.74 0.735 53.846 0.845 53.917 0.984 c
+53.917 1.925 l
+53.549 1.925 l
+53.233 1.925 52.99 1.855 52.814 1.72 c
+52.637 1.591 52.549 1.404 52.549 1.161 c
+52.549 0.933 52.593 0.768 52.681 0.661 c
+52.77 0.562 52.92 0.514 53.137 0.514 c
+57.209 3.366 m
+57.121 3.384 57.021 3.395 56.915 3.395 c
+56.581 3.395 56.346 3.212 56.209 2.851 c
+56.209 0 l
+55.563 0 l
+55.563 3.983 l
+56.194 3.983 l
+56.209 3.571 l
+56.386 3.895 56.628 4.056 56.944 4.056 c
+57.051 4.056 57.139 4.035 57.209 3.998 c
+h
+57.649 2.175 m
+57.649 2.782 57.76 3.248 57.988 3.571 c
+58.223 3.895 58.55 4.056 58.973 4.056 c
+59.355 4.056 59.652 3.898 59.87 3.586 c
+59.87 5.644 l
+60.516 5.644 l
+60.516 0 l
+59.928 0 l
+59.884 0.426 l
+59.679 0.091 59.373 -0.074 58.973 -0.074 c
+58.561 -0.074 58.237 0.08 58.002 0.396 c
+57.767 0.72 57.649 1.176 57.649 1.764 c
+h
+58.297 1.793 m
+58.297 1.352 58.359 1.021 58.488 0.808 c
+58.623 0.602 58.844 0.5 59.149 0.5 c
+59.473 0.5 59.712 0.661 59.87 0.984 c
+59.87 2.998 l
+59.7 3.31 59.461 3.469 59.149 3.469 c
+58.844 3.469 58.623 3.366 58.488 3.16 c
+58.359 2.954 58.297 2.63 58.297 2.19 c
+h
+62.912 2.131 m
+62.912 1.043 62.665 0.077 62.177 -0.765 c
+61.913 -1.214 61.637 -1.536 61.354 -1.735 c
+61.236 -1.309 l
+61.538 -1.015 61.784 -0.563 61.971 0.043 c
+62.166 0.65 62.266 1.315 62.266 2.042 c
+62.266 2.175 l
+62.266 3.104 62.11 3.939 61.809 4.674 c
+61.64 5.074 61.449 5.394 61.236 5.629 c
+61.354 6.04 l
+61.626 5.853 61.89 5.556 62.148 5.144 c
+62.655 4.292 62.912 3.285 62.912 2.131 c
+65.499 2.175 m
+65.499 2.752 65.635 3.208 65.911 3.542 c
+66.193 3.883 66.565 4.056 67.028 4.056 c
+67.487 4.056 67.855 3.887 68.13 3.557 c
+68.413 3.233 68.56 2.785 68.571 2.219 c
+68.571 1.793 l
+68.571 1.223 68.427 0.768 68.145 0.426 c
+67.87 0.091 67.502 -0.074 67.043 -0.074 c
+66.579 -0.074 66.208 0.087 65.925 0.411 c
+65.65 0.742 65.507 1.183 65.499 1.734 c
+h
+66.146 1.793 m
+66.146 1.389 66.223 1.072 66.381 0.837 c
+66.546 0.602 66.767 0.484 67.043 0.484 c
+67.608 0.484 67.903 0.897 67.924 1.72 c
+67.924 2.175 l
+67.924 2.576 67.84 2.896 67.675 3.131 c
+67.517 3.373 67.3 3.498 67.028 3.498 c
+66.763 3.498 66.546 3.373 66.381 3.131 c
+66.223 2.896 66.146 2.576 66.146 2.175 c
+h
+71.055 3.366 m
+70.967 3.384 70.868 3.395 70.761 3.395 c
+70.427 3.395 70.191 3.212 70.056 2.851 c
+70.056 0 l
+69.409 0 l
+69.409 3.983 l
+70.041 3.983 l
+70.056 3.571 l
+70.232 3.895 70.475 4.056 70.791 4.056 c
+70.897 4.056 70.985 4.035 71.055 3.998 c
+h
+75.465 0 m
+75.425 0.087 75.399 0.235 75.392 0.44 c
+75.157 0.095 74.862 -0.074 74.509 -0.074 c
+74.145 -0.074 73.863 0.022 73.657 0.22 c
+73.459 0.426 73.363 0.712 73.363 1.088 c
+73.363 1.488 73.499 1.807 73.775 2.042 c
+74.047 2.285 74.421 2.41 74.891 2.41 c
+75.377 2.41 l
+75.377 2.836 l
+75.377 3.072 75.321 3.237 75.215 3.336 c
+75.105 3.443 74.943 3.498 74.73 3.498 c
+74.532 3.498 74.37 3.439 74.245 3.322 c
+74.127 3.204 74.068 3.057 74.068 2.881 c
+73.422 2.881 l
+73.422 3.075 73.48 3.266 73.598 3.453 c
+73.723 3.638 73.885 3.785 74.083 3.895 c
+74.289 4.001 74.517 4.056 74.774 4.056 c
+75.174 4.056 75.479 3.954 75.685 3.748 c
+75.899 3.542 76.013 3.248 76.024 2.865 c
+76.024 0.852 l
+76.024 0.548 76.061 0.282 76.142 0.058 c
+76.142 0 l
+h
+74.598 0.514 m
+74.763 0.514 74.914 0.558 75.053 0.646 c
+75.201 0.735 75.307 0.845 75.377 0.984 c
+75.377 1.925 l
+75.009 1.925 l
+74.694 1.925 74.451 1.855 74.274 1.72 c
+74.098 1.591 74.01 1.404 74.01 1.161 c
+74.01 0.933 74.054 0.768 74.142 0.661 c
+74.23 0.562 74.38 0.514 74.598 0.514 c
+77.64 3.983 m
+77.655 3.542 l
+77.908 3.883 78.232 4.056 78.625 4.056 c
+79.331 4.056 79.687 3.586 79.698 2.645 c
+79.698 0 l
+79.051 0 l
+79.051 2.616 l
+79.051 2.929 78.997 3.149 78.889 3.278 c
+78.779 3.403 78.625 3.469 78.419 3.469 c
+78.261 3.469 78.114 3.413 77.979 3.307 c
+77.85 3.197 77.747 3.06 77.67 2.896 c
+77.67 0 l
+77.023 0 l
+77.023 3.983 l
+h
+82.256 2.175 m
+82.256 2.752 82.392 3.208 82.667 3.542 c
+82.951 3.883 83.321 4.056 83.785 4.056 c
+84.244 4.056 84.611 3.887 84.887 3.557 c
+85.17 3.233 85.317 2.785 85.328 2.219 c
+85.328 1.793 l
+85.328 1.223 85.185 0.768 84.902 0.426 c
+84.626 0.091 84.258 -0.074 83.799 -0.074 c
+83.336 -0.074 82.965 0.087 82.683 0.411 c
+82.407 0.742 82.263 1.183 82.256 1.734 c
+h
+82.903 1.793 m
+82.903 1.389 82.98 1.072 83.138 0.837 c
+83.303 0.602 83.524 0.484 83.799 0.484 c
+84.366 0.484 84.659 0.897 84.682 1.72 c
+84.682 2.175 l
+84.682 2.576 84.597 2.896 84.431 3.131 c
+84.273 3.373 84.057 3.498 83.785 3.498 c
+83.52 3.498 83.303 3.373 83.138 3.131 c
+82.98 2.896 82.903 2.576 82.903 2.175 c
+h
+89.047 1.793 m
+89.047 1.176 88.933 0.708 88.709 0.396 c
+88.492 0.08 88.169 -0.074 87.739 -0.074 c
+87.316 -0.074 87.003 0.106 86.798 0.47 c
+86.768 0 l
+86.166 0 l
+86.166 5.644 l
+86.812 5.644 l
+86.812 3.542 l
+87.026 3.883 87.334 4.056 87.739 4.056 c
+88.169 4.056 88.492 3.898 88.709 3.586 c
+88.933 3.281 89.047 2.815 89.047 2.19 c
+h
+88.4 2.175 m
+88.4 2.645 88.331 2.977 88.194 3.175 c
+88.065 3.37 87.857 3.469 87.562 3.469 c
+87.228 3.469 86.978 3.285 86.812 2.925 c
+86.812 1.043 l
+86.978 0.679 87.232 0.5 87.577 0.5 c
+87.871 0.5 88.08 0.602 88.209 0.808 c
+88.334 1.014 88.4 1.33 88.4 1.764 c
+h
+90.546 3.983 m
+90.546 -0.5 l
+90.546 -1.235 90.255 -1.603 89.679 -1.603 c
+89.54 -1.603 89.418 -1.58 89.312 -1.544 c
+89.312 -1 l
+89.382 -1.019 89.466 -1.029 89.576 -1.029 c
+89.683 -1.029 89.76 -0.985 89.811 -0.897 c
+89.87 -0.817 89.9 -0.676 89.9 -0.47 c
+89.9 3.983 l
+h
+90.575 5.026 m
+90.575 4.916 90.546 4.824 90.488 4.747 c
+90.429 4.677 90.333 4.644 90.208 4.644 c
+90.091 4.644 89.995 4.677 89.929 4.747 c
+89.87 4.824 89.841 4.916 89.841 5.026 c
+89.841 5.144 89.87 5.236 89.929 5.306 c
+89.995 5.383 90.091 5.423 90.208 5.423 c
+90.333 5.423 90.429 5.383 90.488 5.306 c
+90.546 5.225 90.575 5.134 90.575 5.026 c
+93.001 -0.074 m
+92.501 -0.074 92.119 0.073 91.855 0.367 c
+91.589 0.661 91.458 1.095 91.458 1.675 c
+91.458 2.146 l
+91.458 2.741 91.583 3.208 91.84 3.542 c
+92.104 3.883 92.464 4.056 92.927 4.056 c
+93.387 4.056 93.729 3.902 93.956 3.601 c
+94.192 3.307 94.313 2.844 94.324 2.219 c
+94.324 1.793 l
+92.104 1.793 l
+92.104 1.705 l
+92.104 1.271 92.181 0.959 92.339 0.764 c
+92.505 0.577 92.736 0.484 93.031 0.484 c
+93.225 0.484 93.398 0.517 93.545 0.588 c
+93.692 0.665 93.827 0.783 93.956 0.941 c
+94.294 0.529 l
+94.008 0.124 93.578 -0.074 93.001 -0.074 c
+92.927 3.498 m
+92.652 3.498 92.449 3.403 92.325 3.218 c
+92.196 3.031 92.123 2.741 92.104 2.352 c
+93.677 2.352 l
+93.677 2.439 l
+93.655 2.822 93.589 3.09 93.471 3.248 c
+93.353 3.413 93.17 3.498 92.927 3.498 c
+96.485 0.484 m
+96.698 0.484 96.871 0.548 96.999 0.675 c
+97.135 0.812 97.208 1.003 97.22 1.249 c
+97.837 1.249 l
+97.815 0.866 97.679 0.548 97.426 0.294 c
+97.168 0.047 96.856 -0.074 96.485 -0.074 c
+95.992 -0.074 95.618 0.077 95.352 0.382 c
+95.096 0.694 94.97 1.161 94.97 1.778 c
+94.97 2.219 l
+94.97 2.815 95.096 3.27 95.352 3.586 c
+95.618 3.898 95.992 4.056 96.485 4.056 c
+96.885 4.056 97.205 3.924 97.44 3.659 c
+97.683 3.403 97.815 3.057 97.837 2.616 c
+97.22 2.616 l
+97.197 2.91 97.124 3.131 96.999 3.278 c
+96.881 3.424 96.709 3.498 96.485 3.498 c
+96.191 3.498 95.974 3.399 95.838 3.204 c
+95.699 3.017 95.625 2.707 95.618 2.278 c
+95.618 1.764 l
+95.618 1.294 95.684 0.959 95.823 0.764 c
+95.97 0.577 96.191 0.484 96.485 0.484 c
+99.439 4.939 m
+99.439 3.983 l
+100.042 3.983 l
+100.042 3.453 l
+99.439 3.453 l
+99.439 0.984 l
+99.439 0.826 99.462 0.708 99.512 0.631 c
+99.572 0.55 99.66 0.514 99.778 0.514 c
+99.865 0.514 99.954 0.529 100.042 0.558 c
+100.042 0 l
+99.895 -0.048 99.74 -0.074 99.586 -0.074 c
+99.329 -0.074 99.134 0.018 98.998 0.205 c
+98.859 0.389 98.793 0.65 98.793 0.984 c
+98.793 3.453 l
+98.19 3.453 l
+98.19 3.983 l
+98.793 3.983 l
+98.793 4.939 l
+h
+102.511 2.175 m
+102.511 2.811 102.6 3.413 102.776 3.983 c
+102.953 4.548 103.194 5.045 103.51 5.468 c
+103.705 5.732 103.893 5.923 104.069 6.04 c
+104.202 5.585 l
+103.907 5.31 103.665 4.887 103.481 4.321 c
+103.294 3.752 103.191 3.119 103.173 2.425 c
+103.173 2.131 l
+103.173 1.267 103.29 0.503 103.526 -0.162 c
+103.709 -0.662 103.937 -1.044 104.202 -1.309 c
+104.069 -1.735 l
+103.842 -1.577 103.618 -1.327 103.393 -0.985 c
+102.805 -0.103 102.511 0.947 102.511 2.175 c
+106.936 0 m
+106.895 0.087 106.87 0.235 106.862 0.44 c
+106.627 0.095 106.333 -0.074 105.98 -0.074 c
+105.617 -0.074 105.334 0.022 105.128 0.22 c
+104.929 0.426 104.834 0.712 104.834 1.088 c
+104.834 1.488 104.97 1.807 105.245 2.042 c
+105.517 2.285 105.892 2.41 106.363 2.41 c
+106.847 2.41 l
+106.847 2.836 l
+106.847 3.072 106.793 3.237 106.685 3.336 c
+106.575 3.443 106.414 3.498 106.201 3.498 c
+106.003 3.498 105.841 3.439 105.716 3.322 c
+105.598 3.204 105.54 3.057 105.54 2.881 c
+104.892 2.881 l
+104.892 3.075 104.952 3.266 105.069 3.453 c
+105.193 3.638 105.355 3.785 105.554 3.895 c
+105.76 4.001 105.987 4.056 106.245 4.056 c
+106.645 4.056 106.951 3.954 107.156 3.748 c
+107.369 3.542 107.483 3.248 107.495 2.865 c
+107.495 0.852 l
+107.495 0.548 107.531 0.282 107.612 0.058 c
+107.612 0 l
+h
+106.068 0.514 m
+106.234 0.514 106.384 0.558 106.524 0.646 c
+106.671 0.735 106.778 0.845 106.847 0.984 c
+106.847 1.925 l
+106.48 1.925 l
+106.164 1.925 105.922 1.855 105.745 1.72 c
+105.569 1.591 105.48 1.404 105.48 1.161 c
+105.48 0.933 105.525 0.768 105.613 0.661 c
+105.701 0.562 105.852 0.514 106.068 0.514 c
+109.111 3.983 m
+109.126 3.542 l
+109.38 3.883 109.702 4.056 110.096 4.056 c
+110.801 4.056 111.158 3.586 111.169 2.645 c
+111.169 0 l
+110.522 0 l
+110.522 2.616 l
+110.522 2.929 110.467 3.149 110.361 3.278 c
+110.25 3.403 110.096 3.469 109.891 3.469 c
+109.733 3.469 109.585 3.413 109.449 3.307 c
+109.32 3.197 109.218 3.06 109.141 2.896 c
+109.141 0 l
+108.494 0 l
+108.494 3.983 l
+h
+112.756 3.983 m
+112.771 3.542 l
+113.025 3.883 113.348 4.056 113.741 4.056 c
+114.447 4.056 114.803 3.586 114.814 2.645 c
+114.814 0 l
+114.167 0 l
+114.167 2.616 l
+114.167 2.929 114.113 3.149 114.006 3.278 c
+113.895 3.403 113.741 3.469 113.536 3.469 c
+113.378 3.469 113.23 3.413 113.095 3.307 c
+112.966 3.197 112.863 3.06 112.786 2.896 c
+112.786 0 l
+112.139 0 l
+112.139 3.983 l
+h
+115.653 2.175 m
+115.653 2.752 115.788 3.208 116.064 3.542 c
+116.347 3.883 116.718 4.056 117.181 4.056 c
+117.64 4.056 118.007 3.887 118.283 3.557 c
+118.566 3.233 118.713 2.785 118.724 2.219 c
+118.724 1.793 l
+118.724 1.223 118.581 0.768 118.298 0.426 c
+118.022 0.091 117.655 -0.074 117.195 -0.074 c
+116.733 -0.074 116.361 0.087 116.079 0.411 c
+115.803 0.742 115.659 1.183 115.653 1.734 c
+h
+116.299 1.793 m
+116.299 1.389 116.376 1.072 116.534 0.837 c
+116.7 0.602 116.92 0.484 117.195 0.484 c
+117.762 0.484 118.055 0.897 118.078 1.72 c
+118.078 2.175 l
+118.078 2.576 117.993 2.896 117.828 3.131 c
+117.67 3.373 117.453 3.498 117.181 3.498 c
+116.916 3.498 116.7 3.373 116.534 3.131 c
+116.376 2.896 116.299 2.576 116.299 2.175 c
+h
+120.415 4.939 m
+120.415 3.983 l
+121.018 3.983 l
+121.018 3.453 l
+120.415 3.453 l
+120.415 0.984 l
+120.415 0.826 120.436 0.708 120.488 0.631 c
+120.547 0.55 120.635 0.514 120.752 0.514 c
+120.841 0.514 120.929 0.529 121.018 0.558 c
+121.018 0 l
+120.87 -0.048 120.716 -0.074 120.561 -0.074 c
+120.305 -0.074 120.11 0.018 119.973 0.205 c
+119.834 0.389 119.768 0.65 119.768 0.984 c
+119.768 3.453 l
+119.165 3.453 l
+119.165 3.983 l
+119.768 3.983 l
+119.768 4.939 l
+h
+123.825 0 m
+123.784 0.087 123.759 0.235 123.751 0.44 c
+123.516 0.095 123.222 -0.074 122.869 -0.074 c
+122.506 -0.074 122.223 0.022 122.017 0.22 c
+121.818 0.426 121.723 0.712 121.723 1.088 c
+121.723 1.488 121.859 1.807 122.134 2.042 c
+122.406 2.285 122.782 2.41 123.252 2.41 c
+123.736 2.41 l
+123.736 2.836 l
+123.736 3.072 123.682 3.237 123.575 3.336 c
+123.465 3.443 123.303 3.498 123.09 3.498 c
+122.892 3.498 122.73 3.439 122.605 3.322 c
+122.487 3.204 122.429 3.057 122.429 2.881 c
+121.782 2.881 l
+121.782 3.075 121.841 3.266 121.958 3.453 c
+122.083 3.638 122.244 3.785 122.443 3.895 c
+122.649 4.001 122.877 4.056 123.134 4.056 c
+123.534 4.056 123.84 3.954 124.045 3.748 c
+124.258 3.542 124.372 3.248 124.384 2.865 c
+124.384 0.852 l
+124.384 0.548 124.42 0.282 124.501 0.058 c
+124.501 0 l
+h
+122.957 0.514 m
+123.123 0.514 123.274 0.558 123.414 0.646 c
+123.56 0.735 123.667 0.845 123.736 0.984 c
+123.736 1.925 l
+123.369 1.925 l
+123.053 1.925 122.811 1.855 122.634 1.72 c
+122.458 1.591 122.37 1.404 122.37 1.161 c
+122.37 0.933 122.414 0.768 122.502 0.661 c
+122.591 0.562 122.741 0.514 122.957 0.514 c
+126.236 4.939 m
+126.236 3.983 l
+126.838 3.983 l
+126.838 3.453 l
+126.236 3.453 l
+126.236 0.984 l
+126.236 0.826 126.258 0.708 126.309 0.631 c
+126.368 0.55 126.456 0.514 126.574 0.514 c
+126.662 0.514 126.75 0.529 126.838 0.558 c
+126.838 0 l
+126.691 -0.048 126.537 -0.074 126.383 -0.074 c
+126.125 -0.074 125.93 0.018 125.795 0.205 c
+125.655 0.389 125.589 0.65 125.589 0.984 c
+125.589 3.453 l
+124.986 3.453 l
+124.986 3.983 l
+125.589 3.983 l
+125.589 4.939 l
+h
+129.028 -0.074 m
+128.529 -0.074 128.147 0.073 127.882 0.367 c
+127.617 0.661 127.485 1.095 127.485 1.675 c
+127.485 2.146 l
+127.485 2.741 127.61 3.208 127.867 3.542 c
+128.132 3.883 128.492 4.056 128.955 4.056 c
+129.414 4.056 129.756 3.902 129.984 3.601 c
+130.219 3.307 130.34 2.844 130.352 2.219 c
+130.352 1.793 l
+128.132 1.793 l
+128.132 1.705 l
+128.132 1.271 128.209 0.959 128.367 0.764 c
+128.532 0.577 128.764 0.484 129.058 0.484 c
+129.253 0.484 129.425 0.517 129.572 0.588 c
+129.719 0.665 129.855 0.783 129.984 0.941 c
+130.322 0.529 l
+130.036 0.124 129.606 -0.074 129.028 -0.074 c
+128.955 3.498 m
+128.679 3.498 128.477 3.403 128.353 3.218 c
+128.224 3.031 128.15 2.741 128.132 2.352 c
+129.704 2.352 l
+129.704 2.439 l
+129.683 2.822 129.616 3.09 129.498 3.248 c
+129.381 3.413 129.197 3.498 128.955 3.498 c
+130.998 2.175 m
+130.998 2.782 131.108 3.248 131.336 3.571 c
+131.571 3.895 131.898 4.056 132.321 4.056 c
+132.703 4.056 133.001 3.898 133.217 3.586 c
+133.217 5.644 l
+133.864 5.644 l
+133.864 0 l
+133.276 0 l
+133.232 0.426 l
+133.026 0.091 132.721 -0.074 132.321 -0.074 c
+131.91 -0.074 131.586 0.08 131.351 0.396 c
+131.116 0.72 130.998 1.176 130.998 1.764 c
+h
+131.645 1.793 m
+131.645 1.352 131.707 1.021 131.836 0.808 c
+131.972 0.602 132.193 0.5 132.497 0.5 c
+132.821 0.5 133.059 0.661 133.217 0.984 c
+133.217 2.998 l
+133.049 3.31 132.81 3.469 132.497 3.469 c
+132.193 3.469 131.972 3.366 131.836 3.16 c
+131.707 2.954 131.645 2.63 131.645 2.19 c
+h
+136.26 2.131 m
+136.26 1.043 136.014 0.077 135.526 -0.765 c
+135.26 -1.214 134.985 -1.536 134.703 -1.735 c
+134.585 -1.309 l
+134.886 -1.015 135.133 -0.563 135.32 0.043 c
+135.514 0.65 135.613 1.315 135.613 2.042 c
+135.613 2.175 l
+135.613 3.104 135.459 3.939 135.158 4.674 c
+134.989 5.074 134.798 5.394 134.585 5.629 c
+134.703 6.04 l
+134.974 5.853 135.239 5.556 135.496 5.144 c
+136.003 4.292 136.26 3.285 136.26 2.131 c
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 305.5389 158.4271 cm
+0 0 m
+-1.088 0 l
+-1.088 2.294 l
+-2.675 2.294 l
+-2.675 0 l
+-3.763 0 l
+-3.763 5.351 l
+-2.675 5.351 l
+-2.675 3.19 l
+-1.088 3.19 l
+-1.088 5.351 l
+0 5.351 l
+h
+3.615 2.323 m
+1.94 2.323 l
+1.94 0.897 l
+3.925 0.897 l
+3.925 0 l
+0.852 0 l
+0.852 5.351 l
+3.925 5.351 l
+3.925 4.454 l
+1.94 4.454 l
+1.94 3.19 l
+3.615 3.19 l
+h
+7.1 1.103 m
+5.629 1.103 l
+5.336 0 l
+4.204 0 l
+5.865 5.351 l
+6.865 5.351 l
+8.54 0 l
+7.393 0 l
+h
+5.865 1.999 m
+6.865 1.999 l
+6.364 3.911 l
+h
+9.04 0 m
+9.04 5.351 l
+10.466 5.351 l
+11.083 5.351 11.575 5.149 11.95 4.748 c
+12.321 4.355 12.513 3.815 12.523 3.131 c
+12.523 2.264 l
+12.523 1.559 12.336 1.004 11.965 0.603 c
+11.59 0.198 11.083 0 10.436 0 c
+h
+10.127 4.454 m
+10.127 0.897 l
+10.451 0.897 l
+10.811 0.897 11.068 0.989 11.215 1.176 c
+11.362 1.371 11.436 1.698 11.436 2.161 c
+11.436 3.102 l
+11.436 3.601 11.366 3.947 11.23 4.146 c
+11.09 4.341 10.855 4.443 10.524 4.454 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 331.7103 158.4271 cm
+0 0 m
+-0.04 0.088 -0.066 0.235 -0.073 0.441 c
+-0.309 0.096 -0.603 -0.073 -0.956 -0.073 c
+-1.319 -0.073 -1.602 0.023 -1.808 0.221 c
+-2.007 0.426 -2.102 0.713 -2.102 1.088 c
+-2.102 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.286 -1.043 2.411 -0.573 2.411 c
+-0.088 2.411 l
+-0.088 2.837 l
+-0.088 3.072 -0.143 3.238 -0.25 3.337 c
+-0.36 3.444 -0.522 3.499 -0.735 3.499 c
+-0.933 3.499 -1.095 3.44 -1.22 3.323 c
+-1.338 3.205 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.076 -1.984 3.267 -1.866 3.454 c
+-1.741 3.639 -1.58 3.786 -1.382 3.896 c
+-1.176 4.002 -0.948 4.057 -0.691 4.057 c
+-0.291 4.057 0.015 3.954 0.22 3.749 c
+0.434 3.543 0.548 3.248 0.559 2.866 c
+0.559 0.853 l
+0.559 0.548 0.595 0.283 0.676 0.059 c
+0.676 0 l
+h
+-0.867 0.515 m
+-0.702 0.515 -0.551 0.559 -0.411 0.647 c
+-0.264 0.736 -0.158 0.846 -0.088 0.985 c
+-0.088 1.926 l
+-0.455 1.926 l
+-0.771 1.926 -1.014 1.856 -1.191 1.72 c
+-1.367 1.592 -1.455 1.405 -1.455 1.162 c
+-1.455 0.934 -1.411 0.769 -1.323 0.661 c
+-1.234 0.563 -1.084 0.515 -0.867 0.515 c
+6.159 1.794 m
+6.159 1.166 6.041 0.695 5.806 0.383 c
+5.579 0.077 5.262 -0.073 4.85 -0.073 c
+4.447 -0.073 4.138 0.077 3.925 0.383 c
+3.925 -1.529 l
+3.278 -1.529 l
+3.278 3.984 l
+3.866 3.984 l
+3.91 3.543 l
+4.123 3.884 4.432 4.057 4.836 4.057 c
+5.277 4.057 5.604 3.903 5.821 3.601 c
+6.034 3.296 6.148 2.841 6.159 2.234 c
+h
+5.512 2.176 m
+5.512 2.617 5.442 2.94 5.307 3.146 c
+5.167 3.359 4.946 3.469 4.645 3.469 c
+4.329 3.469 4.09 3.315 3.925 3.013 c
+3.925 0.941 l
+4.09 0.636 4.329 0.485 4.645 0.485 c
+4.939 0.485 5.152 0.588 5.292 0.794 c
+5.428 1.008 5.501 1.338 5.512 1.779 c
+h
+7.703 0 -0.647 5.644 re
+10.76 0 m
+10.72 0.088 10.693 0.235 10.686 0.441 c
+10.451 0.096 10.157 -0.073 9.804 -0.073 c
+9.44 -0.073 9.157 0.023 8.952 0.221 c
+8.754 0.426 8.658 0.713 8.658 1.088 c
+8.658 1.488 8.794 1.808 9.07 2.043 c
+9.342 2.286 9.716 2.411 10.186 2.411 c
+10.672 2.411 l
+10.672 2.837 l
+10.672 3.072 10.616 3.238 10.51 3.337 c
+10.4 3.444 10.238 3.499 10.025 3.499 c
+9.826 3.499 9.664 3.44 9.54 3.323 c
+9.422 3.205 9.363 3.057 9.363 2.881 c
+8.717 2.881 l
+8.717 3.076 8.775 3.267 8.893 3.454 c
+9.018 3.639 9.18 3.786 9.378 3.896 c
+9.584 4.002 9.812 4.057 10.069 4.057 c
+10.469 4.057 10.774 3.954 10.98 3.749 c
+11.194 3.543 11.308 3.248 11.318 2.866 c
+11.318 0.853 l
+11.318 0.548 11.355 0.283 11.436 0.059 c
+11.436 0 l
+h
+9.893 0.515 m
+10.057 0.515 10.209 0.559 10.348 0.647 c
+10.495 0.736 10.602 0.846 10.672 0.985 c
+10.672 1.926 l
+10.304 1.926 l
+9.988 1.926 9.745 1.856 9.569 1.72 c
+9.392 1.592 9.305 1.405 9.305 1.162 c
+9.305 0.934 9.349 0.769 9.437 0.661 c
+9.525 0.563 9.676 0.515 9.893 0.515 c
+13.7 0.485 m
+13.913 0.485 14.086 0.548 14.214 0.676 c
+14.35 0.813 14.423 1.004 14.435 1.249 c
+15.052 1.249 l
+15.03 0.867 14.894 0.548 14.641 0.294 c
+14.383 0.048 14.071 -0.073 13.7 -0.073 c
+13.207 -0.073 12.833 0.077 12.567 0.383 c
+12.311 0.695 12.185 1.162 12.185 1.779 c
+12.185 2.22 l
+12.185 2.816 12.311 3.271 12.567 3.587 c
+12.833 3.899 13.207 4.057 13.7 4.057 c
+14.1 4.057 14.42 3.925 14.655 3.66 c
+14.898 3.404 15.03 3.057 15.052 2.617 c
+14.435 2.617 l
+14.412 2.911 14.339 3.131 14.214 3.278 c
+14.096 3.425 13.924 3.499 13.7 3.499 c
+13.406 3.499 13.189 3.4 13.053 3.205 c
+12.914 3.017 12.84 2.708 12.833 2.278 c
+12.833 1.764 l
+12.833 1.294 12.898 0.96 13.038 0.765 c
+13.185 0.578 13.406 0.485 13.7 0.485 c
+17.228 -0.073 m
+16.727 -0.073 16.345 0.073 16.081 0.368 c
+15.817 0.661 15.684 1.095 15.684 1.676 c
+15.684 2.147 l
+15.684 2.741 15.809 3.209 16.066 3.543 c
+16.33 3.884 16.691 4.057 17.153 4.057 c
+17.613 4.057 17.955 3.903 18.183 3.601 c
+18.418 3.308 18.539 2.845 18.55 2.22 c
+18.55 1.794 l
+16.33 1.794 l
+16.33 1.706 l
+16.33 1.272 16.407 0.96 16.565 0.765 c
+16.731 0.578 16.962 0.485 17.257 0.485 c
+17.452 0.485 17.624 0.518 17.771 0.588 c
+17.918 0.665 18.054 0.783 18.183 0.941 c
+18.521 0.53 l
+18.234 0.125 17.804 -0.073 17.228 -0.073 c
+17.153 3.499 m
+16.878 3.499 16.676 3.404 16.551 3.219 c
+16.422 3.032 16.349 2.741 16.33 2.352 c
+17.903 2.352 l
+17.903 2.44 l
+17.882 2.822 17.816 3.091 17.698 3.248 c
+17.581 3.414 17.396 3.499 17.153 3.499 c
+24.018 1.147 m
+24.621 3.984 l
+25.267 3.984 l
+24.283 0 l
+23.769 0 l
+22.99 2.852 l
+22.24 0 l
+21.71 0 l
+20.756 3.984 l
+21.387 3.984 l
+22.005 1.22 l
+22.74 3.984 l
+23.254 3.984 l
+h
+26.591 3.572 m
+26.844 3.896 27.164 4.057 27.546 4.057 c
+28.251 4.057 28.608 3.587 28.619 2.646 c
+28.619 0 l
+27.972 0 l
+27.972 2.617 l
+27.972 2.929 27.917 3.15 27.811 3.278 c
+27.7 3.404 27.546 3.469 27.341 3.469 c
+27.183 3.469 27.035 3.414 26.9 3.308 c
+26.771 3.198 26.668 3.061 26.591 2.896 c
+26.591 0 l
+25.944 0 l
+25.944 5.644 l
+26.591 5.644 l
+h
+31 -0.073 m
+30.501 -0.073 30.119 0.073 29.853 0.368 c
+29.589 0.661 29.457 1.095 29.457 1.676 c
+29.457 2.147 l
+29.457 2.741 29.582 3.209 29.839 3.543 c
+30.104 3.884 30.464 4.057 30.927 4.057 c
+31.386 4.057 31.728 3.903 31.956 3.601 c
+32.191 3.308 32.313 2.845 32.323 2.22 c
+32.323 1.794 l
+30.104 1.794 l
+30.104 1.706 l
+30.104 1.272 30.181 0.96 30.339 0.765 c
+30.504 0.578 30.736 0.485 31.029 0.485 c
+31.224 0.485 31.397 0.518 31.544 0.588 c
+31.692 0.665 31.827 0.783 31.956 0.941 c
+32.294 0.53 l
+32.007 0.125 31.577 -0.073 31 -0.073 c
+30.927 3.499 m
+30.651 3.499 30.449 3.404 30.324 3.219 c
+30.196 3.032 30.122 2.741 30.104 2.352 c
+31.677 2.352 l
+31.677 2.44 l
+31.655 2.822 31.588 3.091 31.471 3.248 c
+31.353 3.414 31.17 3.499 30.927 3.499 c
+34.749 3.366 m
+34.661 3.385 34.561 3.396 34.455 3.396 c
+34.121 3.396 33.885 3.212 33.749 2.852 c
+33.749 0 l
+33.103 0 l
+33.103 3.984 l
+33.734 3.984 l
+33.749 3.572 l
+33.926 3.896 34.168 4.057 34.484 4.057 c
+34.591 4.057 34.678 4.035 34.749 3.998 c
+h
+36.748 -0.073 m
+36.248 -0.073 35.866 0.073 35.601 0.368 c
+35.337 0.661 35.204 1.095 35.204 1.676 c
+35.204 2.147 l
+35.204 2.741 35.329 3.209 35.586 3.543 c
+35.851 3.884 36.211 4.057 36.674 4.057 c
+37.134 4.057 37.475 3.903 37.703 3.601 c
+37.938 3.308 38.059 2.845 38.071 2.22 c
+38.071 1.794 l
+35.851 1.794 l
+35.851 1.706 l
+35.851 1.272 35.928 0.96 36.086 0.765 c
+36.251 0.578 36.483 0.485 36.777 0.485 c
+36.972 0.485 37.144 0.518 37.292 0.588 c
+37.439 0.665 37.574 0.783 37.703 0.941 c
+38.042 0.53 l
+37.755 0.125 37.325 -0.073 36.748 -0.073 c
+36.674 3.499 m
+36.398 3.499 36.197 3.404 36.072 3.219 c
+35.943 3.032 35.869 2.741 35.851 2.352 c
+37.424 2.352 l
+37.424 2.44 l
+37.402 2.822 37.336 3.091 37.218 3.248 c
+37.101 3.414 36.917 3.499 36.674 3.499 c
+41.657 1.088 m
+42.377 3.984 l
+43.068 3.984 l
+41.774 -0.558 l
+41.676 -0.9 41.533 -1.161 41.348 -1.338 c
+41.172 -1.514 40.97 -1.602 40.746 -1.602 c
+40.658 -1.602 40.544 -1.579 40.408 -1.543 c
+40.408 -0.999 l
+40.554 -1.014 l
+40.739 -1.014 40.885 -0.97 40.996 -0.881 c
+41.102 -0.793 41.19 -0.635 41.26 -0.411 c
+41.377 0.03 l
+40.217 3.984 l
+40.922 3.984 l
+h
+43.509 2.176 m
+43.509 2.753 43.646 3.209 43.921 3.543 c
+44.203 3.884 44.575 4.057 45.038 4.057 c
+45.497 4.057 45.865 3.888 46.14 3.558 c
+46.424 3.234 46.57 2.786 46.582 2.22 c
+46.582 1.794 l
+46.582 1.224 46.438 0.769 46.155 0.426 c
+45.88 0.092 45.512 -0.073 45.053 -0.073 c
+44.59 -0.073 44.219 0.088 43.935 0.412 c
+43.66 0.742 43.517 1.183 43.509 1.735 c
+h
+44.156 1.794 m
+44.156 1.389 44.234 1.073 44.392 0.838 c
+44.556 0.603 44.777 0.485 45.053 0.485 c
+45.618 0.485 45.913 0.897 45.934 1.72 c
+45.934 2.176 l
+45.934 2.577 45.85 2.896 45.685 3.131 c
+45.527 3.373 45.31 3.499 45.038 3.499 c
+44.774 3.499 44.556 3.373 44.392 3.131 c
+44.234 2.896 44.156 2.577 44.156 2.176 c
+h
+49.448 0.353 m
+49.231 0.067 48.918 -0.073 48.507 -0.073 c
+48.143 -0.073 47.868 0.048 47.684 0.294 c
+47.507 0.548 47.412 0.912 47.405 1.382 c
+47.405 3.984 l
+48.051 3.984 l
+48.051 1.441 l
+48.051 0.813 48.235 0.5 48.61 0.5 c
+49.011 0.5 49.286 0.676 49.433 1.029 c
+49.433 3.984 l
+50.079 3.984 l
+50.079 0 l
+49.462 0 l
+h
+52.711 3.366 m
+52.623 3.385 52.523 3.396 52.417 3.396 c
+52.082 3.396 51.847 3.212 51.711 2.852 c
+51.711 0 l
+51.064 0 l
+51.064 3.984 l
+51.696 3.984 l
+51.711 3.572 l
+51.887 3.896 52.13 4.057 52.446 4.057 c
+52.553 4.057 52.641 4.035 52.711 3.998 c
+h
+f
+Q
+q 1 0 0 1 389.5808 160.1769 cm
+0 0 m
+0.353 2.234 l
+1.352 2.234 l
+0.529 -1.75 l
+-0.339 -1.75 l
+-0.897 0.558 l
+-1.455 -1.75 l
+-2.323 -1.75 l
+-3.146 2.234 l
+-2.147 2.234 l
+-1.794 0 l
+-1.264 2.234 l
+-0.53 2.234 l
+h
+1.749 0.367 m
+1.749 0.974 1.888 1.448 2.175 1.793 c
+2.458 2.135 2.851 2.308 3.351 2.308 c
+3.858 2.308 4.255 2.135 4.542 1.793 c
+4.825 1.448 4.968 0.974 4.968 0.367 c
+4.968 0.103 l
+4.968 -0.497 4.825 -0.967 4.542 -1.309 c
+4.255 -1.654 3.858 -1.823 3.351 -1.823 c
+2.84 -1.823 2.443 -1.654 2.161 -1.309 c
+1.885 -0.967 1.749 -0.493 1.749 0.118 c
+h
+2.792 0.103 m
+2.792 -0.603 2.977 -0.956 3.351 -0.956 c
+3.704 -0.956 3.895 -0.661 3.925 -0.073 c
+3.925 0.367 l
+3.925 0.727 3.873 0.999 3.777 1.176 c
+3.678 1.352 3.534 1.44 3.351 1.44 c
+3.175 1.44 3.035 1.352 2.94 1.176 c
+2.84 0.999 2.792 0.727 2.792 0.367 c
+h
+7.57 1.22 m
+7.231 1.249 l
+6.945 1.249 6.754 1.124 6.659 0.881 c
+6.659 -1.75 l
+5.614 -1.75 l
+5.614 2.234 l
+6.585 2.234 l
+6.614 1.793 l
+6.78 2.135 7.011 2.308 7.305 2.308 c
+7.422 2.308 7.515 2.285 7.584 2.248 c
+h
+9.437 -0.221 m
+9.157 -0.53 l
+9.157 -1.75 l
+8.114 -1.75 l
+8.114 3.895 l
+9.157 3.895 l
+9.157 0.852 l
+9.275 1.043 l
+9.995 2.234 l
+11.245 2.234 l
+10.083 0.588 l
+11.347 -1.75 l
+10.157 -1.75 l
+h
+12.803 -1.75 -1.043 3.984 re
+11.715 3.262 m
+11.715 3.418 11.763 3.546 11.862 3.645 c
+11.968 3.752 12.104 3.807 12.274 3.807 c
+12.45 3.807 12.586 3.752 12.685 3.645 c
+12.791 3.546 12.847 3.418 12.847 3.262 c
+12.847 3.094 12.791 2.958 12.685 2.851 c
+12.586 2.753 12.45 2.705 12.274 2.705 c
+12.104 2.705 11.968 2.753 11.862 2.851 c
+11.763 2.958 11.715 3.094 11.715 3.262 c
+14.566 2.234 m
+14.596 1.837 l
+14.831 2.15 15.133 2.308 15.507 2.308 c
+16.191 2.308 16.544 1.826 16.565 0.867 c
+16.565 -1.75 l
+15.522 -1.75 l
+15.522 0.793 l
+15.522 1.018 15.486 1.18 15.419 1.278 c
+15.349 1.374 15.232 1.425 15.066 1.425 c
+14.879 1.425 14.732 1.33 14.626 1.146 c
+14.626 -1.75 l
+13.581 -1.75 l
+13.581 2.234 l
+h
+17.183 0.367 m
+17.183 1.014 17.3 1.499 17.535 1.822 c
+17.771 2.146 18.102 2.308 18.535 2.308 c
+18.888 2.308 19.16 2.165 19.359 1.881 c
+19.403 2.234 l
+20.343 2.234 l
+20.343 -1.75 l
+20.343 -2.257 20.2 -2.646 19.917 -2.911 c
+19.63 -3.183 19.226 -3.323 18.697 -3.323 c
+18.47 -3.323 18.234 -3.278 17.992 -3.19 c
+17.756 -3.102 17.58 -2.988 17.462 -2.852 c
+17.815 -2.132 l
+17.911 -2.238 18.039 -2.323 18.197 -2.382 c
+18.351 -2.448 18.499 -2.484 18.638 -2.484 c
+18.873 -2.484 19.039 -2.425 19.137 -2.308 c
+19.245 -2.198 19.299 -2.022 19.299 -1.779 c
+19.299 -1.426 l
+19.101 -1.691 18.844 -1.823 18.52 -1.823 c
+18.098 -1.823 17.771 -1.661 17.535 -1.338 c
+17.308 -1.008 17.19 -0.537 17.183 0.073 c
+h
+18.227 0.103 m
+18.227 -0.272 18.274 -0.54 18.374 -0.706 c
+18.47 -0.875 18.624 -0.956 18.829 -0.956 c
+19.042 -0.956 19.201 -0.879 19.299 -0.721 c
+19.299 1.176 l
+19.189 1.341 19.035 1.425 18.829 1.425 c
+18.624 1.425 18.47 1.341 18.374 1.176 c
+18.274 1.007 18.227 0.738 18.227 0.367 c
+h
+22.695 0.367 m
+22.695 1.014 22.802 1.499 23.018 1.822 c
+23.243 2.146 23.566 2.308 23.989 2.308 c
+24.301 2.308 24.554 2.175 24.753 1.911 c
+24.753 3.895 l
+25.811 3.895 l
+25.811 -1.75 l
+24.856 -1.75 l
+24.812 -1.338 l
+24.595 -1.661 24.319 -1.823 23.989 -1.823 c
+23.577 -1.823 23.257 -1.669 23.033 -1.353 c
+22.817 -1.029 22.703 -0.559 22.695 0.058 c
+h
+23.739 0.103 m
+23.739 -0.291 23.775 -0.566 23.856 -0.721 c
+23.945 -0.879 24.091 -0.956 24.297 -0.956 c
+24.503 -0.956 24.654 -0.864 24.753 -0.676 c
+24.753 1.132 l
+24.654 1.326 24.503 1.425 24.297 1.425 c
+24.099 1.425 23.959 1.344 23.871 1.19 c
+23.783 1.043 23.739 0.771 23.739 0.382 c
+h
+27.663 -1.75 -1.043 3.984 re
+26.576 3.262 m
+26.576 3.418 26.623 3.546 26.723 3.645 c
+26.829 3.752 26.965 3.807 27.134 3.807 c
+27.311 3.807 27.446 3.752 27.546 3.645 c
+27.652 3.546 27.708 3.418 27.708 3.262 c
+27.708 3.094 27.652 2.958 27.546 2.851 c
+27.446 2.753 27.311 2.705 27.134 2.705 c
+26.965 2.705 26.829 2.753 26.723 2.851 c
+26.623 2.958 26.576 3.094 26.576 3.262 c
+30.426 1.22 m
+30.089 1.249 l
+29.802 1.249 29.611 1.124 29.516 0.881 c
+29.516 -1.75 l
+28.471 -1.75 l
+28.471 2.234 l
+29.442 2.234 l
+29.471 1.793 l
+29.637 2.135 29.868 2.308 30.162 2.308 c
+30.28 2.308 30.372 2.285 30.441 2.248 c
+h
+32.5 -1.823 m
+31.97 -1.823 31.551 -1.669 31.249 -1.353 c
+30.956 -1.029 30.809 -0.57 30.809 0.029 c
+30.809 0.338 l
+30.809 0.962 30.945 1.448 31.22 1.793 c
+31.492 2.135 31.885 2.308 32.396 2.308 c
+32.896 2.308 33.267 2.146 33.514 1.822 c
+33.767 1.499 33.9 1.022 33.911 0.397 c
+33.911 -0.103 l
+31.837 -0.103 l
+31.856 -0.397 31.918 -0.614 32.029 -0.75 c
+32.147 -0.89 32.327 -0.956 32.573 -0.956 c
+32.915 -0.956 33.205 -0.838 33.44 -0.603 c
+33.852 -1.235 l
+33.723 -1.411 33.535 -1.555 33.293 -1.661 c
+33.047 -1.768 32.782 -1.823 32.5 -1.823 c
+31.853 0.617 m
+32.882 0.617 l
+32.882 0.72 l
+32.882 0.955 32.841 1.132 32.764 1.249 c
+32.694 1.374 32.566 1.44 32.382 1.44 c
+32.205 1.44 32.073 1.371 31.985 1.234 c
+31.904 1.106 31.86 0.9 31.853 0.617 c
+35.939 -0.956 m
+36.233 -0.956 36.383 -0.761 36.394 -0.368 c
+37.364 -0.368 l
+37.364 -0.802 37.233 -1.154 36.968 -1.426 c
+36.703 -1.691 36.365 -1.823 35.953 -1.823 c
+35.442 -1.823 35.049 -1.669 34.777 -1.353 c
+34.513 -1.029 34.374 -0.559 34.366 0.058 c
+34.366 0.382 l
+34.366 1.007 34.499 1.484 34.763 1.808 c
+35.035 2.138 35.432 2.308 35.953 2.308 c
+36.383 2.308 36.726 2.167 36.982 1.896 c
+37.236 1.62 37.364 1.238 37.364 0.75 c
+36.394 0.75 l
+36.394 0.962 36.354 1.132 36.277 1.249 c
+36.207 1.374 36.09 1.44 35.924 1.44 c
+35.748 1.44 35.619 1.374 35.542 1.249 c
+35.461 1.12 35.417 0.871 35.409 0.5 c
+35.409 0.087 l
+35.409 -0.235 35.424 -0.463 35.454 -0.588 c
+35.49 -0.717 35.546 -0.809 35.615 -0.867 c
+35.693 -0.927 35.799 -0.956 35.939 -0.956 c
+39.172 3.204 m
+39.172 2.234 l
+39.702 2.234 l
+39.702 1.44 l
+39.172 1.44 l
+39.172 -0.53 l
+39.172 -0.688 39.191 -0.794 39.232 -0.853 c
+39.28 -0.912 39.363 -0.941 39.481 -0.941 c
+39.588 -0.941 39.673 -0.933 39.731 -0.912 c
+39.731 -1.72 l
+39.555 -1.786 39.363 -1.823 39.158 -1.823 c
+38.482 -1.823 38.137 -1.437 38.129 -0.661 c
+38.129 1.44 l
+37.674 1.44 l
+37.674 2.234 l
+38.129 2.234 l
+38.129 3.204 l
+h
+40.055 0.367 m
+40.055 0.974 40.194 1.448 40.481 1.793 c
+40.764 2.135 41.157 2.308 41.657 2.308 c
+42.164 2.308 42.561 2.135 42.848 1.793 c
+43.13 1.448 43.274 0.974 43.274 0.367 c
+43.274 0.103 l
+43.274 -0.497 43.13 -0.967 42.848 -1.309 c
+42.561 -1.654 42.164 -1.823 41.657 -1.823 c
+41.146 -1.823 40.749 -1.654 40.466 -1.309 c
+40.19 -0.967 40.055 -0.493 40.055 0.118 c
+h
+41.098 0.103 m
+41.098 -0.603 41.282 -0.956 41.657 -0.956 c
+42.01 -0.956 42.201 -0.661 42.23 -0.073 c
+42.23 0.367 l
+42.23 0.727 42.179 0.999 42.083 1.176 c
+41.984 1.352 41.84 1.44 41.657 1.44 c
+41.48 1.44 41.341 1.352 41.245 1.176 c
+41.146 0.999 41.098 0.727 41.098 0.367 c
+h
+45.875 1.22 m
+45.537 1.249 l
+45.251 1.249 45.06 1.124 44.965 0.881 c
+44.965 -1.75 l
+43.92 -1.75 l
+43.92 2.234 l
+44.89 2.234 l
+44.92 1.793 l
+45.085 2.135 45.317 2.308 45.611 2.308 c
+45.728 2.308 45.821 2.285 45.89 2.248 c
+h
+47.772 -0.015 m
+48.3 2.234 l
+49.403 2.234 l
+48.095 -2.352 l
+47.897 -3.017 47.529 -3.352 46.993 -3.352 c
+46.864 -3.352 46.721 -3.329 46.567 -3.293 c
+46.567 -2.469 l
+46.684 -2.484 l
+46.849 -2.484 46.97 -2.448 47.051 -2.382 c
+47.128 -2.323 47.191 -2.213 47.242 -2.058 c
+47.316 -1.794 l
+46.17 2.234 l
+47.286 2.234 l
+h
+f
+Q
+441.968 158.427 -0.646 3.984 re
+442.012 163.454 m
+442.012 163.344 441.983 163.252 441.924 163.175 c
+441.865 163.105 441.769 163.072 441.644 163.072 c
+441.527 163.072 441.432 163.105 441.366 163.175 c
+441.306 163.252 441.277 163.344 441.277 163.454 c
+441.277 163.572 441.306 163.664 441.366 163.734 c
+441.432 163.811 441.527 163.851 441.644 163.851 c
+441.769 163.851 441.865 163.811 441.924 163.734 c
+441.983 163.653 442.012 163.561 442.012 163.454 c
+444.923 159.441 m
+444.923 159.589 444.867 159.71 444.761 159.809 c
+444.651 159.905 444.445 160.023 444.144 160.162 c
+443.798 160.309 443.556 160.43 443.408 160.53 c
+443.261 160.636 443.151 160.754 443.085 160.882 c
+443.015 161.008 442.982 161.166 442.982 161.353 c
+442.982 161.675 443.099 161.945 443.335 162.161 c
+443.57 162.374 443.872 162.485 444.246 162.485 c
+444.628 162.485 444.937 162.371 445.172 162.146 c
+445.407 161.918 445.525 161.632 445.525 161.279 c
+444.878 161.279 l
+444.878 161.455 444.819 161.606 444.702 161.735 c
+444.584 161.86 444.43 161.926 444.246 161.926 c
+444.048 161.926 443.897 161.871 443.791 161.764 c
+443.68 161.665 443.629 161.532 443.629 161.367 c
+443.629 161.239 443.666 161.132 443.747 161.044 c
+443.824 160.963 444.015 160.86 444.32 160.735 c
+444.798 160.548 445.129 160.36 445.305 160.177 c
+445.481 160 445.569 159.772 445.569 159.5 c
+445.569 159.148 445.445 158.868 445.202 158.662 c
+444.967 158.457 444.651 158.354 444.261 158.354 c
+443.838 158.354 443.5 158.471 443.247 158.706 c
+442.989 158.949 442.864 159.254 442.864 159.618 c
+443.512 159.618 l
+443.519 159.39 443.589 159.214 443.718 159.088 c
+443.842 158.971 444.026 158.913 444.261 158.913 c
+444.474 158.913 444.636 158.961 444.746 159.059 c
+444.863 159.155 444.923 159.284 444.923 159.441 c
+448.744 162.411 m
+448.759 161.97 l
+449.012 162.311 449.336 162.485 449.729 162.485 c
+450.434 162.485 450.791 162.014 450.802 161.073 c
+450.802 158.427 l
+450.155 158.427 l
+450.155 161.044 l
+450.155 161.357 450.1 161.577 449.993 161.706 c
+449.883 161.831 449.729 161.897 449.523 161.897 c
+449.365 161.897 449.218 161.841 449.083 161.735 c
+448.954 161.625 448.851 161.488 448.774 161.323 c
+448.774 158.427 l
+448.127 158.427 l
+448.127 162.411 l
+h
+451.64 160.603 m
+451.64 161.18 451.776 161.636 452.052 161.97 c
+452.335 162.311 452.705 162.485 453.168 162.485 c
+453.628 162.485 453.995 162.315 454.271 161.985 c
+454.554 161.661 454.701 161.213 454.712 160.647 c
+454.712 160.221 l
+454.712 159.651 454.569 159.196 454.286 158.853 c
+454.01 158.519 453.642 158.354 453.183 158.354 c
+452.72 158.354 452.349 158.516 452.067 158.839 c
+451.791 159.169 451.647 159.611 451.64 160.162 c
+h
+452.287 160.221 m
+452.287 159.817 452.364 159.5 452.522 159.265 c
+452.687 159.03 452.908 158.913 453.183 158.913 c
+453.75 158.913 454.043 159.324 454.066 160.147 c
+454.066 160.603 l
+454.066 161.004 453.981 161.323 453.815 161.558 c
+453.657 161.801 453.441 161.926 453.168 161.926 c
+452.904 161.926 452.687 161.801 452.522 161.558 c
+452.364 161.323 452.287 161.004 452.287 160.603 c
+h
+458.519 159.574 m
+459.122 162.411 l
+459.768 162.411 l
+458.783 158.427 l
+458.269 158.427 l
+457.49 161.279 l
+456.74 158.427 l
+456.211 158.427 l
+455.256 162.411 l
+455.888 162.411 l
+456.505 159.647 l
+457.241 162.411 l
+457.754 162.411 l
+h
+f
+0.69 w
+288.095 207.441 207.848 -54.945 re
+S
+0.793 0.801 0.129 0.016 K
+0.799 w
+q 1 0 0 1 563.3749 119.0558 cm
+0 0 m
+26.08 -0.095 26.08 27.113 48.253 27.098 c
+133.768 27.098 l
+155.865 27.113 155.865 -0.095 181.335 0 c
+S
+Q
+q 1 0 0 1 312.2897 119.0558 cm
+0 0 m
+458.551 0 l
+S
+Q
+0 0 0 0 k
+q 1 0 0 1 541.4877 119.0558 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.828 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.076 -5.828 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.076 0 0 c
+f
+Q
+0.797 0.801 0.129 0.016 K
+0.709 w
+q 1 0 0 1 541.4877 119.0558 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.828 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.076 -5.828 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 567.4135 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.076 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 567.4135 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.076 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 593.3356 119.0558 cm
+0 0 m
+0 -2.076 -1.684 -3.755 -3.756 -3.755 c
+-5.833 -3.755 -7.512 -2.076 -7.512 0 c
+-7.512 2.076 -5.833 3.755 -3.756 3.755 c
+-1.684 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 593.3356 119.0558 cm
+0 0 m
+0 -2.076 -1.684 -3.755 -3.756 -3.755 c
+-5.833 -3.755 -7.512 -2.076 -7.512 0 c
+-7.512 2.076 -5.833 3.755 -3.756 3.755 c
+-1.684 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 619.2567 119.0558 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.076 -5.832 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 619.2567 119.0558 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.076 -5.832 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 645.1788 119.0558 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.828 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.076 -5.828 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 645.1788 119.0558 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.828 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.076 -5.828 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 671.1036 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.759 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.076 -5.832 3.755 -3.759 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 671.1036 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.759 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.076 -5.832 3.755 -3.759 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 697.0258 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.076 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 697.0258 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.076 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 722.9478 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.076 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 722.9478 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.076 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 774.7947 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.759 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.076 -5.832 3.755 -3.759 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 774.7947 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.759 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.076 -5.832 3.755 -3.759 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 748.8699 119.0558 cm
+0 0 m
+0 -2.076 -1.68 -3.755 -3.756 -3.755 c
+-5.829 -3.755 -7.512 -2.076 -7.512 0 c
+-7.512 2.076 -5.829 3.755 -3.756 3.755 c
+-1.68 3.755 0 2.076 0 0 c
+f
+Q
+q 1 0 0 1 748.8699 119.0558 cm
+0 0 m
+0 -2.076 -1.68 -3.755 -3.756 -3.755 c
+-5.829 -3.755 -7.512 -2.076 -7.512 0 c
+-7.512 2.076 -5.829 3.755 -3.756 3.755 c
+-1.68 3.755 0 2.076 0 0 c
+h
+S
+Q
+q 1 0 0 1 619.2567 146.1501 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.072 -5.832 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.072 0 0 c
+f
+Q
+q 1 0 0 1 619.2567 146.1501 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.072 -5.832 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.072 0 0 c
+h
+S
+Q
+q 1 0 0 1 645.1788 146.1501 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.828 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.072 -5.828 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.072 0 0 c
+f
+Q
+q 1 0 0 1 645.1788 146.1501 cm
+0 0 m
+0 -2.076 -1.679 -3.755 -3.755 -3.755 c
+-5.828 -3.755 -7.511 -2.076 -7.511 0 c
+-7.511 2.072 -5.828 3.755 -3.755 3.755 c
+-1.679 3.755 0 2.072 0 0 c
+h
+S
+Q
+q 1 0 0 1 671.1036 146.1501 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.759 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.072 -5.832 3.755 -3.759 3.755 c
+-1.683 3.755 0 2.072 0 0 c
+f
+Q
+q 1 0 0 1 671.1036 146.1501 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.759 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.072 -5.832 3.755 -3.759 3.755 c
+-1.683 3.755 0 2.072 0 0 c
+h
+S
+Q
+q 1 0 0 1 697.0258 146.1501 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.072 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.072 0 0 c
+f
+Q
+q 1 0 0 1 697.0258 146.1501 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.515 -2.076 -7.515 0 c
+-7.515 2.072 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.072 0 0 c
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 689.9702 155.2305 cm
+0 0 m
+-0.008 12.072 l
+6.46 12.072 l
+6.468 0 l
+3.227 -3.561 l
+h
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 691.3192 165.4168 cm
+0 0 m
+2.396 0 l
+2.396 0.368 l
+2.984 0.368 l
+2.984 0 l
+3.116 0 l
+3.487 0 3.777 -0.096 3.983 -0.294 c
+4.189 -0.5 4.292 -0.794 4.292 -1.176 c
+4.292 -1.382 4.237 -1.639 4.13 -1.955 c
+3.468 -1.852 l
+3.546 -1.625 3.586 -1.415 3.586 -1.22 c
+3.586 -0.933 3.428 -0.786 3.116 -0.779 c
+2.984 -0.779 l
+2.984 -1.249 l
+2.396 -1.249 l
+2.396 -0.779 l
+0 -0.779 l
+h
+0 -1.529 2.984 -0.794 re
+0 -2.323 m
+2.102 -3.719 m
+2.984 -4.072 l
+2.984 -4.895 l
+1.529 -4.218 l
+0 -4.924 l
+0 -4.087 l
+0.941 -3.719 l
+0 -3.366 l
+0 -2.528 l
+1.529 -3.233 l
+2.984 -2.558 l
+2.984 -3.395 l
+h
+-0.339 -5.398 m
+-0.339 -4.825 l
+4.012 -6.03 l
+4.012 -6.603 l
+h
+0 -8.279 m
+0.048 -8.249 0.125 -8.224 0.235 -8.206 c
+0.037 -8.066 -0.059 -7.882 -0.059 -7.647 c
+-0.059 -7.39 0.022 -7.181 0.191 -7.015 c
+0.357 -6.857 0.573 -6.78 0.837 -6.78 c
+1.15 -6.78 1.388 -6.876 1.558 -7.073 c
+1.735 -7.269 1.822 -7.551 1.822 -7.927 c
+1.822 -8.176 l
+2.057 -8.176 l
+2.194 -8.176 2.285 -8.151 2.337 -8.103 c
+2.396 -8.052 2.425 -7.985 2.425 -7.897 c
+2.425 -7.699 2.311 -7.603 2.088 -7.603 c
+2.088 -6.824 l
+2.359 -6.824 2.587 -6.927 2.763 -7.133 c
+2.947 -7.339 3.042 -7.603 3.042 -7.927 c
+3.042 -8.249 2.954 -8.5 2.778 -8.676 c
+2.609 -8.86 2.37 -8.955 2.057 -8.955 c
+0.646 -8.955 l
+0.389 -8.955 0.191 -8.989 0.044 -9.058 c
+0 -9.058 l
+h
+0.559 -7.809 m
+0.559 -7.897 0.573 -7.971 0.602 -8.029 c
+0.639 -8.095 0.683 -8.147 0.735 -8.176 c
+1.352 -8.176 l
+1.352 -7.985 l
+1.352 -7.846 1.308 -7.739 1.22 -7.661 c
+1.139 -7.592 1.029 -7.559 0.881 -7.559 c
+0.665 -7.559 0.559 -7.64 0.559 -7.809 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 612.076 155.9689 cm
+0 0 m
+-0.007 25.419 l
+6.465 25.419 l
+6.468 0 l
+3.227 -3.561 l
+h
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 615.2952 179.954 cm
+0 0 m
+0.449 0 0.802 -0.106 1.058 -0.324 c
+1.324 -0.536 1.455 -0.831 1.455 -1.205 c
+1.455 -1.587 1.324 -1.881 1.058 -2.087 c
+0.802 -2.3 0.449 -2.41 0 -2.41 c
+-0.191 -2.41 l
+-0.654 -2.41 -1.01 -2.3 -1.263 -2.087 c
+-1.521 -1.881 -1.646 -1.587 -1.646 -1.205 c
+-1.646 -0.823 -1.521 -0.522 -1.263 -0.309 c
+-0.999 -0.103 -0.643 0 -0.191 0 c
+h
+-0.191 -0.779 m
+-0.731 -0.779 -0.999 -0.918 -0.999 -1.205 c
+-0.999 -1.469 -0.775 -1.61 -0.323 -1.631 c
+0 -1.646 l
+0.272 -1.646 0.474 -1.606 0.603 -1.529 c
+0.739 -1.448 0.809 -1.341 0.809 -1.205 c
+0.809 -1.076 0.739 -0.974 0.603 -0.897 c
+0.474 -0.816 0.272 -0.779 0 -0.779 c
+h
+0.647 -4.182 m
+0.661 -3.932 l
+0.661 -3.715 0.566 -3.568 0.383 -3.491 c
+-1.587 -3.491 l
+-1.587 -2.712 l
+1.397 -2.712 l
+1.397 -3.447 l
+1.073 -3.476 l
+1.326 -3.594 1.455 -3.759 1.455 -3.976 c
+1.455 -4.064 1.441 -4.137 1.411 -4.197 c
+h
+-1.587 -4.505 2.984 -0.779 re
+-1.587 -5.284 m
+2.161 -4.461 m
+2.286 -4.461 2.389 -4.498 2.469 -4.579 c
+2.547 -4.656 2.587 -4.758 2.587 -4.887 c
+2.587 -5.012 2.547 -5.115 2.469 -5.196 c
+2.389 -5.273 2.286 -5.313 2.161 -5.313 c
+2.043 -5.313 1.945 -5.273 1.867 -5.196 c
+1.786 -5.126 1.75 -5.024 1.75 -4.887 c
+1.75 -4.758 1.786 -4.656 1.867 -4.579 c
+1.945 -4.498 2.043 -4.461 2.161 -4.461 c
+0 -5.699 m
+0.489 -5.699 0.853 -5.787 1.088 -5.964 c
+1.33 -6.14 1.455 -6.383 1.455 -6.699 c
+1.455 -6.971 1.345 -7.184 1.132 -7.331 c
+1.397 -7.36 l
+1.397 -8.066 l
+-1.587 -8.066 l
+-1.969 -8.066 -2.256 -7.956 -2.454 -7.742 c
+-2.66 -7.526 -2.763 -7.221 -2.763 -6.832 c
+-2.763 -6.662 -2.726 -6.485 -2.66 -6.302 c
+-2.601 -6.126 -2.517 -5.993 -2.41 -5.905 c
+-1.881 -6.169 l
+-1.947 -6.247 -2.007 -6.346 -2.057 -6.464 c
+-2.105 -6.581 -2.131 -6.688 -2.131 -6.787 c
+-2.131 -6.963 -2.087 -7.088 -1.999 -7.169 c
+-1.918 -7.247 -1.785 -7.287 -1.602 -7.287 c
+-1.352 -7.287 l
+-1.55 -7.14 -1.646 -6.942 -1.646 -6.699 c
+-1.646 -6.383 -1.525 -6.14 -1.278 -5.964 c
+-1.025 -5.787 -0.673 -5.699 -0.22 -5.699 c
+h
+-0.205 -6.479 m
+-0.481 -6.479 -0.683 -6.512 -0.808 -6.581 c
+-0.926 -6.659 -0.985 -6.776 -0.985 -6.934 c
+-0.985 -7.088 -0.933 -7.206 -0.823 -7.287 c
+0.603 -7.287 l
+0.728 -7.206 0.794 -7.088 0.794 -6.934 c
+0.794 -6.776 0.728 -6.659 0.603 -6.581 c
+0.474 -6.512 0.272 -6.479 0 -6.479 c
+h
+-1.587 -8.488 2.984 -0.779 re
+-1.587 -9.267 m
+2.161 -8.444 m
+2.286 -8.444 2.389 -8.481 2.469 -8.562 c
+2.547 -8.639 2.587 -8.742 2.587 -8.871 c
+2.587 -8.995 2.547 -9.099 2.469 -9.18 c
+2.389 -9.257 2.286 -9.297 2.161 -9.297 c
+2.043 -9.297 1.945 -9.257 1.867 -9.18 c
+1.786 -9.109 1.75 -9.007 1.75 -8.871 c
+1.75 -8.742 1.786 -8.639 1.867 -8.562 c
+1.945 -8.481 2.043 -8.444 2.161 -8.444 c
+1.397 -10.491 m
+1.103 -10.506 l
+1.338 -10.682 1.455 -10.91 1.455 -11.197 c
+1.455 -11.715 1.091 -11.979 0.368 -11.991 c
+-1.587 -11.991 l
+-1.587 -11.212 l
+0.309 -11.212 l
+0.485 -11.212 0.607 -11.185 0.676 -11.138 c
+0.754 -11.087 0.794 -10.998 0.794 -10.873 c
+0.794 -10.726 0.721 -10.612 0.574 -10.535 c
+-1.587 -10.535 l
+-1.587 -9.756 l
+1.397 -9.756 l
+h
+-1.926 -12.66 m
+-1.926 -12.086 l
+2.425 -13.292 l
+2.425 -13.865 l
+h
+-1.587 -14.277 m
+0.809 -14.277 l
+0.809 -13.909 l
+1.397 -13.909 l
+1.397 -14.277 l
+1.529 -14.277 l
+1.9 -14.277 2.19 -14.372 2.396 -14.57 c
+2.602 -14.776 2.705 -15.07 2.705 -15.453 c
+2.705 -15.658 2.65 -15.915 2.543 -16.232 c
+1.881 -16.128 l
+1.959 -15.9 1.999 -15.692 1.999 -15.496 c
+1.999 -15.21 1.841 -15.063 1.529 -15.056 c
+1.397 -15.056 l
+1.397 -15.526 l
+0.809 -15.526 l
+0.809 -15.056 l
+-1.587 -15.056 l
+h
+-1.587 -15.805 2.984 -0.793 re
+-1.587 -16.598 m
+0.515 -17.995 m
+1.397 -18.348 l
+1.397 -19.171 l
+-0.058 -18.495 l
+-1.587 -19.201 l
+-1.587 -18.362 l
+-0.646 -17.995 l
+-1.587 -17.643 l
+-1.587 -16.804 l
+-0.058 -17.51 l
+1.397 -16.834 l
+1.397 -17.672 l
+h
+-1.926 -19.675 m
+-1.926 -19.102 l
+2.425 -20.307 l
+2.425 -20.88 l
+h
+-1.587 -22.555 m
+-1.539 -22.526 -1.462 -22.501 -1.352 -22.482 c
+-1.55 -22.343 -1.646 -22.159 -1.646 -21.924 c
+-1.646 -21.666 -1.565 -21.456 -1.396 -21.292 c
+-1.23 -21.134 -1.014 -21.057 -0.75 -21.057 c
+-0.437 -21.057 -0.198 -21.152 -0.029 -21.35 c
+0.148 -21.545 0.235 -21.828 0.235 -22.202 c
+0.235 -22.453 l
+0.47 -22.453 l
+0.607 -22.453 0.698 -22.427 0.75 -22.379 c
+0.809 -22.327 0.838 -22.262 0.838 -22.173 c
+0.838 -21.975 0.724 -21.88 0.5 -21.88 c
+0.5 -21.101 l
+0.772 -21.101 1 -21.203 1.176 -21.409 c
+1.36 -21.614 1.455 -21.88 1.455 -22.202 c
+1.455 -22.526 1.367 -22.776 1.191 -22.952 c
+1.022 -23.137 0.783 -23.232 0.47 -23.232 c
+-0.941 -23.232 l
+-1.198 -23.232 -1.396 -23.264 -1.543 -23.335 c
+-1.587 -23.335 l
+h
+-1.028 -22.085 m
+-1.028 -22.173 -1.014 -22.247 -0.985 -22.306 c
+-0.948 -22.372 -0.904 -22.423 -0.852 -22.453 c
+-0.235 -22.453 l
+-0.235 -22.262 l
+-0.235 -22.121 -0.279 -22.015 -0.367 -21.938 c
+-0.448 -21.868 -0.558 -21.835 -0.706 -21.835 c
+-0.922 -21.835 -1.028 -21.916 -1.028 -22.085 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 548.514 156.6265 cm
+0 0 m
+-1.058 0 l
+-1.058 -3.572 l
+-1.558 -3.572 l
+-1.558 0 l
+-2.616 0 l
+-2.616 0.441 l
+0 0.441 l
+h
+0.823 -0.897 m
+1.007 -0.654 1.242 -0.529 1.529 -0.529 c
+2.058 -0.529 2.326 -0.881 2.337 -1.587 c
+2.337 -3.572 l
+1.852 -3.572 l
+1.852 -1.616 l
+1.852 -1.381 1.812 -1.216 1.735 -1.117 c
+1.654 -1.022 1.536 -0.97 1.382 -0.97 c
+1.264 -0.97 1.154 -1.01 1.058 -1.087 c
+0.96 -1.168 0.882 -1.275 0.823 -1.411 c
+0.823 -3.572 l
+0.338 -3.572 l
+0.338 0.661 l
+0.823 0.661 l
+h
+3.406 -3.572 -0.499 2.984 re
+3.436 0.206 m
+3.436 0.118 3.41 0.044 3.362 -0.014 c
+3.322 -0.066 3.252 -0.087 3.156 -0.087 c
+3.069 -0.087 2.999 -0.066 2.951 -0.014 c
+2.911 0.044 2.892 0.11 2.892 0.191 c
+2.892 0.279 2.911 0.353 2.951 0.412 c
+2.999 0.47 3.069 0.5 3.156 0.5 c
+3.252 0.5 3.322 0.47 3.362 0.412 c
+3.41 0.353 3.436 0.283 3.436 0.206 c
+5.523 -2.807 m
+5.523 -2.701 5.483 -2.612 5.405 -2.543 c
+5.325 -2.466 5.174 -2.377 4.95 -2.278 c
+4.685 -2.171 4.498 -2.08 4.391 -1.999 c
+4.281 -1.922 4.204 -1.833 4.156 -1.734 c
+4.104 -1.639 4.083 -1.521 4.083 -1.381 c
+4.083 -1.139 4.171 -0.937 4.347 -0.779 c
+4.524 -0.613 4.748 -0.529 5.024 -0.529 c
+5.317 -0.529 5.552 -0.617 5.729 -0.793 c
+5.905 -0.962 5.993 -1.176 5.993 -1.44 c
+5.508 -1.44 l
+5.508 -1.304 5.457 -1.19 5.361 -1.103 c
+5.273 -1.007 5.159 -0.955 5.024 -0.955 c
+4.877 -0.955 4.762 -0.995 4.685 -1.072 c
+4.605 -1.143 4.567 -1.242 4.567 -1.367 c
+4.567 -1.466 4.597 -1.543 4.656 -1.602 c
+4.715 -1.66 4.854 -1.741 5.082 -1.837 c
+5.442 -1.984 5.689 -2.127 5.818 -2.263 c
+5.953 -2.392 6.023 -2.564 6.023 -2.778 c
+6.023 -3.035 5.928 -3.241 5.743 -3.395 c
+5.567 -3.553 5.332 -3.63 5.038 -3.63 c
+4.723 -3.63 4.469 -3.542 4.274 -3.366 c
+4.087 -3.183 3.994 -2.95 3.994 -2.675 c
+4.48 -2.675 l
+4.487 -2.844 4.538 -2.977 4.627 -3.072 c
+4.723 -3.16 4.862 -3.204 5.038 -3.204 c
+5.193 -3.204 5.31 -3.171 5.39 -3.102 c
+5.479 -3.035 5.523 -2.936 5.523 -2.807 c
+8.213 -3.572 -0.5 2.984 re
+8.243 0.206 m
+8.243 0.118 8.216 0.044 8.169 -0.014 c
+8.129 -0.066 8.058 -0.087 7.963 -0.087 c
+7.875 -0.087 7.805 -0.066 7.757 -0.014 c
+7.717 0.044 7.699 0.11 7.699 0.191 c
+7.699 0.279 7.717 0.353 7.757 0.412 c
+7.805 0.47 7.875 0.5 7.963 0.5 c
+8.058 0.5 8.129 0.47 8.169 0.412 c
+8.216 0.353 8.243 0.283 8.243 0.206 c
+10.329 -2.807 m
+10.329 -2.701 10.29 -2.612 10.212 -2.543 c
+10.132 -2.466 9.98 -2.377 9.756 -2.278 c
+9.492 -2.171 9.305 -2.08 9.198 -1.999 c
+9.088 -1.922 9.01 -1.833 8.962 -1.734 c
+8.912 -1.639 8.889 -1.521 8.889 -1.381 c
+8.889 -1.139 8.977 -0.937 9.153 -0.779 c
+9.33 -0.613 9.554 -0.529 9.83 -0.529 c
+10.124 -0.529 10.359 -0.617 10.535 -0.793 c
+10.712 -0.962 10.8 -1.176 10.8 -1.44 c
+10.315 -1.44 l
+10.315 -1.304 10.263 -1.19 10.168 -1.103 c
+10.08 -1.007 9.966 -0.955 9.83 -0.955 c
+9.683 -0.955 9.569 -0.995 9.492 -1.072 c
+9.411 -1.143 9.375 -1.242 9.375 -1.367 c
+9.375 -1.466 9.404 -1.543 9.463 -1.602 c
+9.521 -1.66 9.661 -1.741 9.889 -1.837 c
+10.249 -1.984 10.495 -2.127 10.624 -2.263 c
+10.76 -2.392 10.83 -2.564 10.83 -2.778 c
+10.83 -3.035 10.734 -3.241 10.55 -3.395 c
+10.373 -3.553 10.138 -3.63 9.845 -3.63 c
+9.529 -3.63 9.275 -3.542 9.08 -3.366 c
+8.893 -3.183 8.801 -2.95 8.801 -2.675 c
+9.286 -2.675 l
+9.294 -2.844 9.345 -2.977 9.433 -3.072 c
+9.529 -3.16 9.668 -3.204 9.845 -3.204 c
+9.999 -3.204 10.117 -3.171 10.198 -3.102 c
+10.286 -3.035 10.329 -2.936 10.329 -2.807 c
+14.019 -3.572 m
+13.99 -3.505 13.968 -3.395 13.961 -3.248 c
+13.784 -3.505 13.564 -3.63 13.299 -3.63 c
+13.024 -3.63 12.806 -3.557 12.652 -3.41 c
+12.505 -3.256 12.432 -3.039 12.432 -2.763 c
+12.432 -2.462 12.534 -2.219 12.74 -2.043 c
+12.946 -1.859 13.229 -1.764 13.593 -1.764 c
+13.945 -1.764 l
+13.945 -1.44 l
+13.945 -1.263 13.905 -1.143 13.828 -1.072 c
+13.747 -0.995 13.629 -0.955 13.475 -0.955 c
+13.328 -0.955 13.203 -0.999 13.108 -1.087 c
+13.02 -1.176 12.976 -1.286 12.976 -1.411 c
+12.49 -1.411 l
+12.49 -1.263 12.534 -1.124 12.623 -0.985 c
+12.711 -0.837 12.829 -0.727 12.976 -0.646 c
+13.13 -0.569 13.303 -0.529 13.49 -0.529 c
+13.802 -0.529 14.038 -0.61 14.196 -0.764 c
+14.35 -0.911 14.431 -1.132 14.431 -1.425 c
+14.431 -2.925 l
+14.438 -3.16 14.475 -3.362 14.533 -3.528 c
+14.533 -3.572 l
+h
+13.373 -3.189 m
+13.49 -3.189 13.6 -3.156 13.71 -3.087 c
+13.818 -3.021 13.895 -2.936 13.945 -2.836 c
+13.945 -2.131 l
+13.681 -2.131 l
+13.446 -2.131 13.259 -2.183 13.122 -2.278 c
+12.993 -2.377 12.931 -2.52 12.931 -2.705 c
+12.931 -2.873 12.961 -2.994 13.02 -3.072 c
+13.086 -3.152 13.203 -3.189 13.373 -3.189 c
+15.467 -0.588 m
+15.482 -0.911 l
+15.665 -0.658 15.908 -0.529 16.202 -0.529 c
+16.731 -0.529 16.999 -0.881 17.01 -1.587 c
+17.01 -3.572 l
+16.525 -3.572 l
+16.525 -1.616 l
+16.525 -1.381 16.485 -1.216 16.407 -1.117 c
+16.326 -1.022 16.209 -0.97 16.055 -0.97 c
+15.937 -0.97 15.827 -1.01 15.732 -1.087 c
+15.632 -1.168 15.555 -1.275 15.497 -1.411 c
+15.497 -3.572 l
+15.011 -3.572 l
+15.011 -0.588 l
+h
+20.281 -3.307 m
+20.123 -3.524 19.888 -3.63 19.576 -3.63 c
+19.311 -3.63 19.105 -3.538 18.958 -3.351 c
+18.819 -3.167 18.753 -2.892 18.753 -2.528 c
+18.753 -0.588 l
+19.237 -0.588 l
+19.237 -2.499 l
+19.237 -2.961 19.377 -3.189 19.663 -3.189 c
+19.958 -3.189 20.156 -3.057 20.266 -2.792 c
+20.266 -0.588 l
+20.766 -0.588 l
+20.766 -3.572 l
+20.295 -3.572 l
+h
+23.463 -2.219 m
+23.463 -2.69 23.379 -3.042 23.214 -3.278 c
+23.044 -3.513 22.805 -3.63 22.493 -3.63 c
+22.188 -3.63 21.957 -3.52 21.803 -3.293 c
+21.803 -4.718 l
+21.317 -4.718 l
+21.317 -0.588 l
+21.758 -0.588 l
+21.787 -0.926 l
+21.942 -0.661 22.173 -0.529 22.479 -0.529 c
+22.809 -0.529 23.056 -0.646 23.214 -0.881 c
+23.379 -1.109 23.463 -1.448 23.463 -1.896 c
+h
+22.979 -1.94 m
+22.979 -1.61 22.923 -1.363 22.817 -1.205 c
+22.717 -1.051 22.556 -0.97 22.331 -0.97 c
+22.096 -0.97 21.92 -1.087 21.803 -1.323 c
+21.803 -2.866 l
+21.92 -3.094 22.1 -3.204 22.346 -3.204 c
+22.559 -3.204 22.717 -3.127 22.817 -2.969 c
+22.923 -2.815 22.979 -2.572 22.979 -2.248 c
+h
+25.359 -2.807 m
+25.359 -2.701 25.319 -2.612 25.242 -2.543 c
+25.161 -2.466 25.01 -2.377 24.787 -2.278 c
+24.521 -2.171 24.334 -2.08 24.228 -1.999 c
+24.118 -1.922 24.041 -1.833 23.993 -1.734 c
+23.941 -1.639 23.919 -1.521 23.919 -1.381 c
+23.919 -1.139 24.008 -0.937 24.184 -0.779 c
+24.36 -0.613 24.584 -0.529 24.86 -0.529 c
+25.153 -0.529 25.389 -0.617 25.566 -0.793 c
+25.741 -0.962 25.83 -1.176 25.83 -1.44 c
+25.344 -1.44 l
+25.344 -1.304 25.294 -1.19 25.198 -1.103 c
+25.109 -1.007 24.995 -0.955 24.86 -0.955 c
+24.713 -0.955 24.598 -0.995 24.521 -1.072 c
+24.44 -1.143 24.404 -1.242 24.404 -1.367 c
+24.404 -1.466 24.434 -1.543 24.492 -1.602 c
+24.551 -1.66 24.691 -1.741 24.918 -1.837 c
+25.279 -1.984 25.525 -2.127 25.654 -2.263 c
+25.789 -2.392 25.859 -2.564 25.859 -2.778 c
+25.859 -3.035 25.764 -3.241 25.58 -3.395 c
+25.404 -3.553 25.169 -3.63 24.874 -3.63 c
+24.558 -3.63 24.305 -3.542 24.11 -3.366 c
+23.923 -3.183 23.831 -2.95 23.831 -2.675 c
+24.316 -2.675 l
+24.323 -2.844 24.375 -2.977 24.463 -3.072 c
+24.558 -3.16 24.698 -3.204 24.874 -3.204 c
+25.028 -3.204 25.146 -3.171 25.227 -3.102 c
+25.315 -3.035 25.359 -2.936 25.359 -2.807 c
+26.95 0.133 m
+26.95 -0.588 l
+27.407 -0.588 l
+27.407 -0.985 l
+26.95 -0.985 l
+26.95 -2.836 l
+26.95 -2.954 26.969 -3.042 27.01 -3.102 c
+27.046 -3.16 27.116 -3.189 27.216 -3.189 c
+27.274 -3.189 27.337 -3.183 27.407 -3.16 c
+27.407 -3.572 l
+27.289 -3.609 27.175 -3.63 27.068 -3.63 c
+26.869 -3.63 26.719 -3.564 26.613 -3.424 c
+26.514 -3.289 26.466 -3.094 26.466 -2.836 c
+26.466 -0.985 l
+26.01 -0.985 l
+26.01 -0.588 l
+26.466 -0.588 l
+26.466 0.133 l
+h
+29.107 -1.043 m
+29.038 -1.036 28.964 -1.028 28.887 -1.028 c
+28.63 -1.028 28.454 -1.168 28.359 -1.44 c
+28.359 -3.572 l
+27.873 -3.572 l
+27.873 -0.588 l
+28.344 -0.588 l
+28.359 -0.897 l
+28.483 -0.654 28.667 -0.529 28.902 -0.529 c
+28.979 -0.529 29.042 -0.544 29.093 -0.573 c
+h
+30.482 -3.63 m
+30.107 -3.63 29.824 -3.524 29.63 -3.307 c
+29.431 -3.083 29.336 -2.755 29.336 -2.322 c
+29.336 -1.955 l
+29.336 -1.514 29.427 -1.168 29.614 -0.911 c
+29.81 -0.658 30.085 -0.529 30.438 -0.529 c
+30.78 -0.529 31.033 -0.643 31.202 -0.867 c
+31.378 -1.095 31.471 -1.44 31.482 -1.911 c
+31.482 -2.219 l
+29.82 -2.219 l
+29.82 -2.293 l
+29.82 -2.616 29.88 -2.851 29.997 -2.998 c
+30.115 -3.138 30.283 -3.204 30.512 -3.204 c
+30.659 -3.204 30.784 -3.183 30.894 -3.131 c
+31 -3.072 31.103 -2.984 31.202 -2.866 c
+31.453 -3.175 l
+31.247 -3.48 30.923 -3.63 30.482 -3.63 c
+30.438 -0.955 m
+30.233 -0.955 30.078 -1.025 29.982 -1.161 c
+29.883 -1.301 29.828 -1.514 29.82 -1.808 c
+30.996 -1.808 l
+30.996 -1.734 l
+30.975 -1.462 30.923 -1.263 30.835 -1.146 c
+30.747 -1.022 30.615 -0.955 30.438 -0.955 c
+33.422 -3.572 m
+33.392 -3.505 33.371 -3.395 33.363 -3.248 c
+33.186 -3.505 32.966 -3.63 32.702 -3.63 c
+32.426 -3.63 32.209 -3.557 32.055 -3.41 c
+31.908 -3.256 31.835 -3.039 31.835 -2.763 c
+31.835 -2.462 31.937 -2.219 32.143 -2.043 c
+32.349 -1.859 32.632 -1.764 32.995 -1.764 c
+33.348 -1.764 l
+33.348 -1.44 l
+33.348 -1.263 33.308 -1.143 33.231 -1.072 c
+33.15 -0.995 33.032 -0.955 32.878 -0.955 c
+32.731 -0.955 32.606 -0.999 32.511 -1.087 c
+32.423 -1.176 32.378 -1.286 32.378 -1.411 c
+31.893 -1.411 l
+31.893 -1.263 31.937 -1.124 32.026 -0.985 c
+32.114 -0.837 32.232 -0.727 32.378 -0.646 c
+32.533 -0.569 32.706 -0.529 32.893 -0.529 c
+33.205 -0.529 33.44 -0.61 33.599 -0.764 c
+33.753 -0.911 33.834 -1.132 33.834 -1.425 c
+33.834 -2.925 l
+33.841 -3.16 33.878 -3.362 33.936 -3.528 c
+33.936 -3.572 l
+h
+32.775 -3.189 m
+32.893 -3.189 33.003 -3.156 33.113 -3.087 c
+33.219 -3.021 33.297 -2.936 33.348 -2.836 c
+33.348 -2.131 l
+33.084 -2.131 l
+32.849 -2.131 32.661 -2.183 32.525 -2.278 c
+32.397 -2.377 32.334 -2.52 32.334 -2.705 c
+32.334 -2.873 32.363 -2.994 32.423 -3.072 c
+32.488 -3.152 32.606 -3.189 32.775 -3.189 c
+34.869 -0.588 m
+34.884 -0.867 l
+35.061 -0.643 35.299 -0.529 35.605 -0.529 c
+35.935 -0.529 36.167 -0.675 36.296 -0.97 c
+36.479 -0.675 36.741 -0.529 37.075 -0.529 c
+37.633 -0.529 37.917 -0.874 37.927 -1.558 c
+37.927 -3.572 l
+37.442 -3.572 l
+37.442 -1.602 l
+37.442 -1.389 37.402 -1.23 37.325 -1.132 c
+37.244 -1.025 37.111 -0.97 36.928 -0.97 c
+36.781 -0.97 36.663 -1.028 36.575 -1.146 c
+36.487 -1.257 36.432 -1.396 36.413 -1.573 c
+36.413 -3.572 l
+35.928 -3.572 l
+35.928 -1.587 l
+35.917 -1.176 35.744 -0.97 35.414 -0.97 c
+35.168 -0.97 34.995 -1.095 34.9 -1.338 c
+34.9 -3.572 l
+34.414 -3.572 l
+34.414 -0.588 l
+h
+41.742 -2.219 m
+41.742 -2.69 41.657 -3.042 41.491 -3.278 c
+41.323 -3.513 41.08 -3.63 40.757 -3.63 c
+40.44 -3.63 40.205 -3.495 40.051 -3.219 c
+40.022 -3.572 l
+39.581 -3.572 l
+39.581 0.661 l
+40.066 0.661 l
+40.066 -0.911 l
+40.22 -0.658 40.452 -0.529 40.757 -0.529 c
+41.08 -0.529 41.323 -0.646 41.491 -0.881 c
+41.657 -1.117 41.742 -1.466 41.742 -1.926 c
+h
+41.256 -1.94 m
+41.256 -1.587 41.205 -1.338 41.109 -1.19 c
+41.011 -1.043 40.849 -0.97 40.625 -0.97 c
+40.378 -0.97 40.191 -1.109 40.066 -1.381 c
+40.066 -2.792 l
+40.184 -3.057 40.375 -3.189 40.639 -3.189 c
+40.853 -3.189 41.011 -3.116 41.109 -2.969 c
+41.205 -2.815 41.256 -2.572 41.256 -2.248 c
+h
+43.417 -1.043 m
+43.347 -1.036 43.274 -1.028 43.197 -1.028 c
+42.94 -1.028 42.763 -1.168 42.667 -1.44 c
+42.667 -3.572 l
+42.183 -3.572 l
+42.183 -0.588 l
+42.653 -0.588 l
+42.667 -0.897 l
+42.792 -0.654 42.977 -0.529 43.212 -0.529 c
+43.289 -0.529 43.351 -0.544 43.403 -0.573 c
+h
+45.214 -3.572 m
+45.185 -3.505 45.163 -3.395 45.155 -3.248 c
+44.979 -3.505 44.758 -3.63 44.494 -3.63 c
+44.219 -3.63 44.001 -3.557 43.847 -3.41 c
+43.7 -3.256 43.627 -3.039 43.627 -2.763 c
+43.627 -2.462 43.729 -2.219 43.935 -2.043 c
+44.141 -1.859 44.425 -1.764 44.788 -1.764 c
+45.14 -1.764 l
+45.14 -1.44 l
+45.14 -1.263 45.1 -1.143 45.023 -1.072 c
+44.942 -0.995 44.824 -0.955 44.67 -0.955 c
+44.523 -0.955 44.398 -0.999 44.303 -1.087 c
+44.215 -1.176 44.171 -1.286 44.171 -1.411 c
+43.686 -1.411 l
+43.686 -1.263 43.729 -1.124 43.818 -0.985 c
+43.906 -0.837 44.024 -0.727 44.171 -0.646 c
+44.325 -0.569 44.498 -0.529 44.685 -0.529 c
+44.997 -0.529 45.233 -0.61 45.391 -0.764 c
+45.545 -0.911 45.626 -1.132 45.626 -1.425 c
+45.626 -2.925 l
+45.633 -3.16 45.67 -3.362 45.728 -3.528 c
+45.728 -3.572 l
+h
+44.568 -3.189 m
+44.685 -3.189 44.795 -3.156 44.905 -3.087 c
+45.013 -3.021 45.09 -2.936 45.14 -2.836 c
+45.14 -2.131 l
+44.876 -2.131 l
+44.641 -2.131 44.454 -2.183 44.317 -2.278 c
+44.189 -2.377 44.126 -2.52 44.126 -2.705 c
+44.126 -2.873 44.156 -2.994 44.215 -3.072 c
+44.281 -3.152 44.398 -3.189 44.568 -3.189 c
+46.663 -0.588 m
+46.677 -0.911 l
+46.86 -0.658 47.103 -0.529 47.397 -0.529 c
+47.926 -0.529 48.194 -0.881 48.205 -1.587 c
+48.205 -3.572 l
+47.721 -3.572 l
+47.721 -1.616 l
+47.721 -1.381 47.68 -1.216 47.603 -1.117 c
+47.522 -1.022 47.405 -0.97 47.25 -0.97 c
+47.133 -0.97 47.022 -1.01 46.927 -1.087 c
+46.827 -1.168 46.75 -1.275 46.692 -1.411 c
+46.692 -3.572 l
+46.206 -3.572 l
+46.206 -0.588 l
+h
+49.79 -3.204 m
+49.955 -3.204 50.087 -3.156 50.187 -3.057 c
+50.282 -2.961 50.337 -2.818 50.348 -2.631 c
+50.804 -2.631 l
+50.792 -2.917 50.69 -3.156 50.495 -3.351 c
+50.308 -3.538 50.073 -3.63 49.79 -3.63 c
+49.425 -3.63 49.146 -3.513 48.951 -3.278 c
+48.753 -3.042 48.658 -2.697 48.658 -2.234 c
+48.658 -1.911 l
+48.658 -1.462 48.749 -1.117 48.937 -0.881 c
+49.132 -0.646 49.414 -0.529 49.79 -0.529 c
+50.091 -0.529 50.333 -0.628 50.509 -0.823 c
+50.694 -1.022 50.792 -1.286 50.804 -1.616 c
+50.348 -1.616 l
+50.326 -1.392 50.267 -1.227 50.171 -1.117 c
+50.083 -1.01 49.955 -0.955 49.79 -0.955 c
+49.572 -0.955 49.41 -1.028 49.304 -1.176 c
+49.205 -1.315 49.15 -1.543 49.142 -1.866 c
+49.142 -2.248 l
+49.142 -2.601 49.19 -2.851 49.29 -2.998 c
+49.396 -3.138 49.562 -3.204 49.79 -3.204 c
+51.682 -0.897 m
+51.866 -0.654 52.101 -0.529 52.388 -0.529 c
+52.917 -0.529 53.185 -0.881 53.196 -1.587 c
+53.196 -3.572 l
+52.711 -3.572 l
+52.711 -1.616 l
+52.711 -1.381 52.67 -1.216 52.593 -1.117 c
+52.512 -1.022 52.394 -0.97 52.24 -0.97 c
+52.123 -0.97 52.013 -1.01 51.917 -1.087 c
+51.818 -1.168 51.741 -1.275 51.682 -1.411 c
+51.682 -3.572 l
+51.197 -3.572 l
+51.197 0.661 l
+51.682 0.661 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w
+q 1 0 0 1 602.8229 154.5574 cm
+0 0 m
+5.862 3.679 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 606.8806 157.1041 cm
+0 0 m
+-0.338 -1.47 l
+2.257 1.419 l
+-1.474 0.335 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 606.8807 157.1041 cm
+0 0 m
+-0.338 -1.47 l
+2.257 1.419 l
+-1.474 0.335 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 634.8231 174.5965 cm
+0 0 m
+-1.058 0 l
+-1.058 -3.572 l
+-1.558 -3.572 l
+-1.558 0 l
+-2.616 0 l
+-2.616 0.441 l
+0 0.441 l
+h
+0.823 -0.897 m
+1.007 -0.654 1.242 -0.53 1.529 -0.53 c
+2.058 -0.53 2.326 -0.882 2.337 -1.588 c
+2.337 -3.572 l
+1.852 -3.572 l
+1.852 -1.617 l
+1.852 -1.382 1.812 -1.216 1.735 -1.118 c
+1.654 -1.022 1.536 -0.97 1.382 -0.97 c
+1.264 -0.97 1.154 -1.01 1.058 -1.088 c
+0.96 -1.168 0.882 -1.276 0.823 -1.411 c
+0.823 -3.572 l
+0.338 -3.572 l
+0.338 0.661 l
+0.823 0.661 l
+h
+3.406 -3.572 -0.5 2.984 re
+3.436 0.206 m
+3.436 0.118 3.41 0.044 3.362 -0.015 c
+3.322 -0.066 3.252 -0.088 3.157 -0.088 c
+3.069 -0.088 2.999 -0.066 2.951 -0.015 c
+2.911 0.044 2.892 0.11 2.892 0.191 c
+2.892 0.279 2.911 0.353 2.951 0.411 c
+2.999 0.47 3.069 0.5 3.157 0.5 c
+3.252 0.5 3.322 0.47 3.362 0.411 c
+3.41 0.353 3.436 0.283 3.436 0.206 c
+5.523 -2.808 m
+5.523 -2.701 5.483 -2.612 5.406 -2.543 c
+5.325 -2.466 5.174 -2.377 4.95 -2.278 c
+4.686 -2.172 4.498 -2.08 4.391 -1.999 c
+4.281 -1.922 4.204 -1.834 4.156 -1.735 c
+4.104 -1.639 4.083 -1.521 4.083 -1.382 c
+4.083 -1.139 4.171 -0.937 4.347 -0.779 c
+4.524 -0.613 4.748 -0.53 5.024 -0.53 c
+5.317 -0.53 5.552 -0.617 5.729 -0.794 c
+5.905 -0.963 5.993 -1.176 5.993 -1.44 c
+5.509 -1.44 l
+5.509 -1.305 5.457 -1.191 5.361 -1.103 c
+5.274 -1.007 5.159 -0.956 5.024 -0.956 c
+4.877 -0.956 4.763 -0.996 4.686 -1.073 c
+4.605 -1.143 4.568 -1.242 4.568 -1.367 c
+4.568 -1.467 4.597 -1.544 4.656 -1.602 c
+4.715 -1.661 4.854 -1.742 5.082 -1.837 c
+5.442 -1.985 5.689 -2.128 5.818 -2.263 c
+5.953 -2.392 6.023 -2.565 6.023 -2.778 c
+6.023 -3.036 5.928 -3.241 5.744 -3.396 c
+5.567 -3.553 5.332 -3.631 5.039 -3.631 c
+4.723 -3.631 4.469 -3.543 4.274 -3.366 c
+4.087 -3.183 3.994 -2.951 3.994 -2.675 c
+4.48 -2.675 l
+4.487 -2.845 4.538 -2.977 4.627 -3.072 c
+4.723 -3.161 4.862 -3.204 5.039 -3.204 c
+5.193 -3.204 5.31 -3.171 5.391 -3.102 c
+5.479 -3.036 5.523 -2.936 5.523 -2.808 c
+8.214 -3.572 -0.5 2.984 re
+8.243 0.206 m
+8.243 0.118 8.216 0.044 8.169 -0.015 c
+8.129 -0.066 8.058 -0.088 7.963 -0.088 c
+7.875 -0.088 7.805 -0.066 7.757 -0.015 c
+7.717 0.044 7.699 0.11 7.699 0.191 c
+7.699 0.279 7.717 0.353 7.757 0.411 c
+7.805 0.47 7.875 0.5 7.963 0.5 c
+8.058 0.5 8.129 0.47 8.169 0.411 c
+8.216 0.353 8.243 0.283 8.243 0.206 c
+10.327 -2.808 m
+10.327 -2.701 10.286 -2.612 10.209 -2.543 c
+10.128 -2.466 9.978 -2.377 9.753 -2.278 c
+9.488 -2.172 9.301 -2.08 9.195 -1.999 c
+9.084 -1.922 9.007 -1.834 8.96 -1.735 c
+8.908 -1.639 8.885 -1.521 8.885 -1.382 c
+8.885 -1.139 8.974 -0.937 9.151 -0.779 c
+9.327 -0.613 9.55 -0.53 9.826 -0.53 c
+10.121 -0.53 10.356 -0.617 10.532 -0.794 c
+10.708 -0.963 10.797 -1.176 10.797 -1.44 c
+10.311 -1.44 l
+10.311 -1.305 10.26 -1.191 10.165 -1.103 c
+10.076 -1.007 9.962 -0.956 9.826 -0.956 c
+9.679 -0.956 9.565 -0.996 9.488 -1.073 c
+9.407 -1.143 9.371 -1.242 9.371 -1.367 c
+9.371 -1.467 9.4 -1.544 9.459 -1.602 c
+9.517 -1.661 9.658 -1.742 9.885 -1.837 c
+10.246 -1.985 10.491 -2.128 10.62 -2.263 c
+10.756 -2.392 10.826 -2.565 10.826 -2.778 c
+10.826 -3.036 10.73 -3.241 10.547 -3.396 c
+10.371 -3.553 10.136 -3.631 9.841 -3.631 c
+9.525 -3.631 9.272 -3.543 9.077 -3.366 c
+8.889 -3.183 8.798 -2.951 8.798 -2.675 c
+9.282 -2.675 l
+9.29 -2.845 9.342 -2.977 9.43 -3.072 c
+9.525 -3.161 9.665 -3.204 9.841 -3.204 c
+9.995 -3.204 10.113 -3.171 10.194 -3.102 c
+10.282 -3.036 10.327 -2.936 10.327 -2.808 c
+14.016 -3.572 m
+13.986 -3.506 13.964 -3.396 13.957 -3.248 c
+13.781 -3.506 13.56 -3.631 13.296 -3.631 c
+13.02 -3.631 12.803 -3.557 12.648 -3.41 c
+12.502 -3.256 12.428 -3.04 12.428 -2.764 c
+12.428 -2.462 12.531 -2.22 12.737 -2.043 c
+12.943 -1.86 13.226 -1.764 13.589 -1.764 c
+13.942 -1.764 l
+13.942 -1.44 l
+13.942 -1.264 13.901 -1.143 13.824 -1.073 c
+13.743 -0.996 13.626 -0.956 13.471 -0.956 c
+13.325 -0.956 13.2 -1 13.105 -1.088 c
+13.016 -1.176 12.972 -1.286 12.972 -1.411 c
+12.487 -1.411 l
+12.487 -1.264 12.531 -1.124 12.619 -0.985 c
+12.708 -0.838 12.825 -0.728 12.972 -0.647 c
+13.126 -0.57 13.299 -0.53 13.486 -0.53 c
+13.799 -0.53 14.034 -0.611 14.192 -0.765 c
+14.346 -0.912 14.427 -1.132 14.427 -1.426 c
+14.427 -2.926 l
+14.435 -3.161 14.471 -3.362 14.53 -3.528 c
+14.53 -3.572 l
+h
+13.369 -3.19 m
+13.486 -3.19 13.596 -3.157 13.707 -3.087 c
+13.814 -3.021 13.891 -2.936 13.942 -2.837 c
+13.942 -2.132 l
+13.677 -2.132 l
+13.442 -2.132 13.255 -2.183 13.119 -2.278 c
+12.991 -2.377 12.928 -2.521 12.928 -2.705 c
+12.928 -2.874 12.958 -2.995 13.016 -3.072 c
+13.082 -3.153 13.2 -3.19 13.369 -3.19 c
+16.742 -3.572 -0.5 4.233 re
+17.315 -1.941 m
+17.315 -1.511 17.419 -1.168 17.624 -0.912 c
+17.837 -0.658 18.117 -0.53 18.462 -0.53 c
+18.804 -0.53 19.079 -0.658 19.285 -0.912 c
+19.499 -1.158 19.613 -1.492 19.623 -1.911 c
+19.623 -2.22 l
+19.623 -2.653 19.513 -2.995 19.3 -3.248 c
+19.094 -3.506 18.815 -3.631 18.462 -3.631 c
+18.117 -3.631 17.845 -3.51 17.639 -3.263 c
+17.433 -3.021 17.323 -2.687 17.315 -2.263 c
+h
+17.801 -2.22 m
+17.801 -2.535 17.859 -2.778 17.977 -2.955 c
+18.102 -3.123 18.264 -3.204 18.462 -3.204 c
+18.892 -3.204 19.116 -2.896 19.138 -2.278 c
+19.138 -1.941 l
+19.138 -1.639 19.073 -1.397 18.948 -1.22 c
+18.83 -1.044 18.668 -0.956 18.462 -0.956 c
+18.264 -0.956 18.102 -1.044 17.977 -1.22 c
+17.859 -1.397 17.801 -1.639 17.801 -1.941 c
+h
+21.09 -3.204 m
+21.255 -3.204 21.387 -3.157 21.487 -3.057 c
+21.582 -2.962 21.637 -2.818 21.648 -2.631 c
+22.104 -2.631 l
+22.092 -2.918 21.99 -3.157 21.795 -3.352 c
+21.608 -3.539 21.373 -3.631 21.09 -3.631 c
+20.725 -3.631 20.446 -3.514 20.251 -3.278 c
+20.053 -3.043 19.958 -2.697 19.958 -2.234 c
+19.958 -1.911 l
+19.958 -1.463 20.05 -1.118 20.237 -0.882 c
+20.432 -0.647 20.715 -0.53 21.09 -0.53 c
+21.391 -0.53 21.633 -0.628 21.81 -0.823 c
+21.994 -1.022 22.092 -1.286 22.104 -1.617 c
+21.648 -1.617 l
+21.626 -1.393 21.567 -1.228 21.471 -1.118 c
+21.384 -1.01 21.255 -0.956 21.09 -0.956 c
+20.873 -0.956 20.711 -1.029 20.604 -1.176 c
+20.505 -1.316 20.45 -1.544 20.443 -1.867 c
+20.443 -2.249 l
+20.443 -2.602 20.49 -2.851 20.59 -2.999 c
+20.696 -3.138 20.862 -3.204 21.09 -3.204 c
+24.029 -3.572 m
+24 -3.506 23.978 -3.396 23.971 -3.248 c
+23.794 -3.506 23.574 -3.631 23.309 -3.631 c
+23.033 -3.631 22.817 -3.557 22.662 -3.41 c
+22.516 -3.256 22.442 -3.04 22.442 -2.764 c
+22.442 -2.462 22.545 -2.22 22.751 -2.043 c
+22.956 -1.86 23.239 -1.764 23.603 -1.764 c
+23.956 -1.764 l
+23.956 -1.44 l
+23.956 -1.264 23.916 -1.143 23.838 -1.073 c
+23.757 -0.996 23.64 -0.956 23.486 -0.956 c
+23.339 -0.956 23.214 -1 23.118 -1.088 c
+23.03 -1.176 22.986 -1.286 22.986 -1.411 c
+22.501 -1.411 l
+22.501 -1.264 22.545 -1.124 22.633 -0.985 c
+22.721 -0.838 22.838 -0.728 22.986 -0.647 c
+23.14 -0.57 23.312 -0.53 23.501 -0.53 c
+23.813 -0.53 24.048 -0.611 24.206 -0.765 c
+24.36 -0.912 24.441 -1.132 24.441 -1.426 c
+24.441 -2.926 l
+24.448 -3.161 24.485 -3.362 24.544 -3.528 c
+24.544 -3.572 l
+h
+23.383 -3.19 m
+23.501 -3.19 23.611 -3.157 23.721 -3.087 c
+23.827 -3.021 23.904 -2.936 23.956 -2.837 c
+23.956 -2.132 l
+23.692 -2.132 l
+23.456 -2.132 23.268 -2.183 23.133 -2.278 c
+23.004 -2.377 22.942 -2.521 22.942 -2.705 c
+22.942 -2.874 22.971 -2.995 23.03 -3.072 c
+23.096 -3.153 23.214 -3.19 23.383 -3.19 c
+25.551 -3.572 -0.5 4.233 re
+29.589 -2.22 m
+29.589 -2.691 29.504 -3.043 29.34 -3.278 c
+29.171 -3.514 28.928 -3.631 28.604 -3.631 c
+28.288 -3.631 28.053 -3.495 27.899 -3.219 c
+27.869 -3.572 l
+27.428 -3.572 l
+27.428 0.661 l
+27.914 0.661 l
+27.914 -0.912 l
+28.068 -0.658 28.299 -0.53 28.604 -0.53 c
+28.928 -0.53 29.171 -0.647 29.34 -0.882 c
+29.504 -1.118 29.589 -1.467 29.589 -1.926 c
+h
+29.105 -1.941 m
+29.105 -1.588 29.053 -1.338 28.957 -1.191 c
+28.858 -1.044 28.696 -0.97 28.472 -0.97 c
+28.226 -0.97 28.039 -1.11 27.914 -1.382 c
+27.914 -2.793 l
+28.031 -3.057 28.222 -3.19 28.487 -3.19 c
+28.7 -3.19 28.858 -3.117 28.957 -2.969 c
+29.053 -2.815 29.105 -2.573 29.105 -2.249 c
+h
+31.265 -1.044 m
+31.195 -1.037 31.122 -1.029 31.044 -1.029 c
+30.788 -1.029 30.611 -1.168 30.516 -1.44 c
+30.516 -3.572 l
+30.03 -3.572 l
+30.03 -0.588 l
+30.501 -0.588 l
+30.516 -0.897 l
+30.64 -0.654 30.824 -0.53 31.06 -0.53 c
+31.137 -0.53 31.199 -0.544 31.25 -0.574 c
+h
+33.062 -3.572 m
+33.032 -3.506 33.011 -3.396 33.003 -3.248 c
+32.827 -3.506 32.606 -3.631 32.342 -3.631 c
+32.066 -3.631 31.85 -3.557 31.695 -3.41 c
+31.548 -3.256 31.474 -3.04 31.474 -2.764 c
+31.474 -2.462 31.577 -2.22 31.783 -2.043 c
+31.989 -1.86 32.272 -1.764 32.636 -1.764 c
+32.988 -1.764 l
+32.988 -1.44 l
+32.988 -1.264 32.949 -1.143 32.871 -1.073 c
+32.79 -0.996 32.673 -0.956 32.518 -0.956 c
+32.371 -0.956 32.246 -1 32.151 -1.088 c
+32.062 -1.176 32.018 -1.286 32.018 -1.411 c
+31.534 -1.411 l
+31.534 -1.264 31.577 -1.124 31.665 -0.985 c
+31.754 -0.838 31.871 -0.728 32.018 -0.647 c
+32.172 -0.57 32.346 -0.53 32.533 -0.53 c
+32.845 -0.53 33.08 -0.611 33.238 -0.765 c
+33.393 -0.912 33.473 -1.132 33.473 -1.426 c
+33.473 -2.926 l
+33.481 -3.161 33.518 -3.362 33.577 -3.528 c
+33.577 -3.572 l
+h
+32.415 -3.19 m
+32.533 -3.19 32.643 -3.157 32.753 -3.087 c
+32.86 -3.021 32.937 -2.936 32.988 -2.837 c
+32.988 -2.132 l
+32.724 -2.132 l
+32.488 -2.132 32.301 -2.183 32.166 -2.278 c
+32.037 -2.377 31.974 -2.521 31.974 -2.705 c
+31.974 -2.874 32.004 -2.995 32.062 -3.072 c
+32.128 -3.153 32.246 -3.19 32.415 -3.19 c
+34.51 -0.588 m
+34.524 -0.912 l
+34.709 -0.658 34.95 -0.53 35.245 -0.53 c
+35.774 -0.53 36.043 -0.882 36.053 -1.588 c
+36.053 -3.572 l
+35.568 -3.572 l
+35.568 -1.617 l
+35.568 -1.382 35.528 -1.216 35.451 -1.118 c
+35.37 -1.022 35.252 -0.97 35.098 -0.97 c
+34.98 -0.97 34.87 -1.01 34.774 -1.088 c
+34.675 -1.168 34.598 -1.276 34.539 -1.411 c
+34.539 -3.572 l
+34.054 -3.572 l
+34.054 -0.588 l
+h
+37.637 -3.204 m
+37.803 -3.204 37.934 -3.157 38.034 -3.057 c
+38.129 -2.962 38.185 -2.818 38.196 -2.631 c
+38.651 -2.631 l
+38.64 -2.918 38.537 -3.157 38.343 -3.352 c
+38.155 -3.539 37.92 -3.631 37.637 -3.631 c
+37.273 -3.631 36.994 -3.514 36.799 -3.278 c
+36.6 -3.043 36.505 -2.697 36.505 -2.234 c
+36.505 -1.911 l
+36.505 -1.463 36.597 -1.118 36.785 -0.882 c
+36.98 -0.647 37.262 -0.53 37.637 -0.53 c
+37.938 -0.53 38.181 -0.628 38.358 -0.823 c
+38.541 -1.022 38.64 -1.286 38.651 -1.617 c
+38.196 -1.617 l
+38.173 -1.393 38.115 -1.228 38.019 -1.118 c
+37.931 -1.01 37.803 -0.956 37.637 -0.956 c
+37.42 -0.956 37.259 -1.029 37.152 -1.176 c
+37.053 -1.316 36.997 -1.544 36.99 -1.867 c
+36.99 -2.249 l
+36.99 -2.602 37.038 -2.851 37.138 -2.999 c
+37.244 -3.138 37.409 -3.204 37.637 -3.204 c
+39.53 -0.897 m
+39.713 -0.654 39.949 -0.53 40.235 -0.53 c
+40.764 -0.53 41.032 -0.882 41.044 -1.588 c
+41.044 -3.572 l
+40.558 -3.572 l
+40.558 -1.617 l
+40.558 -1.382 40.518 -1.216 40.441 -1.118 c
+40.36 -1.022 40.242 -0.97 40.088 -0.97 c
+39.97 -0.97 39.86 -1.01 39.765 -1.088 c
+39.665 -1.168 39.588 -1.276 39.53 -1.411 c
+39.53 -3.572 l
+39.044 -3.572 l
+39.044 0.661 l
+39.53 0.661 l
+h
+41.616 -3.308 m
+41.616 -3.219 41.639 -3.146 41.69 -3.087 c
+41.749 -3.028 41.826 -2.999 41.925 -2.999 c
+42.032 -2.999 42.109 -3.028 42.16 -3.087 c
+42.219 -3.146 42.249 -3.219 42.249 -3.308 c
+42.249 -3.389 42.219 -3.454 42.16 -3.514 c
+42.109 -3.572 42.032 -3.601 41.925 -3.601 c
+41.826 -3.601 41.749 -3.572 41.69 -3.514 c
+41.639 -3.454 41.616 -3.389 41.616 -3.308 c
+f
+Q
+633.001 164.439 -0.501 4.013 re
+634.36 168.143 m
+634.36 167.423 l
+634.816 167.423 l
+634.816 167.026 l
+634.36 167.026 l
+634.36 165.174 l
+634.36 165.056 634.378 164.968 634.419 164.91 c
+634.455 164.85 634.526 164.821 634.625 164.821 c
+634.684 164.821 634.746 164.829 634.816 164.85 c
+634.816 164.439 l
+634.698 164.403 634.584 164.38 634.478 164.38 c
+634.279 164.38 634.129 164.447 634.023 164.586 c
+633.923 164.723 633.875 164.917 633.875 165.174 c
+633.875 167.026 l
+633.42 167.026 l
+633.42 167.423 l
+633.875 167.423 l
+633.875 168.143 l
+h
+637.017 164.439 -0.5 2.984 re
+637.046 168.217 m
+637.046 168.129 637.021 168.056 636.973 167.996 c
+636.932 167.945 636.863 167.923 636.767 167.923 c
+636.679 167.923 636.61 167.945 636.562 167.996 c
+636.521 168.056 636.502 168.121 636.502 168.202 c
+636.502 168.291 636.521 168.364 636.562 168.422 c
+636.61 168.482 636.679 168.511 636.767 168.511 c
+636.863 168.511 636.932 168.482 636.973 168.422 c
+637.021 168.364 637.046 168.295 637.046 168.217 c
+639.13 165.203 m
+639.13 165.311 639.089 165.399 639.012 165.469 c
+638.931 165.546 638.781 165.634 638.557 165.733 c
+638.293 165.839 638.104 165.931 637.998 166.012 c
+637.888 166.089 637.811 166.178 637.763 166.277 c
+637.711 166.373 637.69 166.49 637.69 166.629 c
+637.69 166.872 637.778 167.074 637.954 167.232 c
+638.131 167.398 638.355 167.482 638.63 167.482 c
+638.924 167.482 639.159 167.394 639.336 167.217 c
+639.512 167.048 639.6 166.835 639.6 166.571 c
+639.116 166.571 l
+639.116 166.707 639.064 166.82 638.968 166.909 c
+638.881 167.005 638.767 167.056 638.63 167.056 c
+638.484 167.056 638.37 167.015 638.293 166.938 c
+638.212 166.868 638.175 166.77 638.175 166.644 c
+638.175 166.545 638.204 166.468 638.262 166.409 c
+638.322 166.35 638.461 166.269 638.689 166.174 c
+639.049 166.026 639.295 165.883 639.424 165.748 c
+639.56 165.619 639.629 165.446 639.629 165.233 c
+639.629 164.976 639.534 164.771 639.351 164.615 c
+639.174 164.458 638.939 164.38 638.645 164.38 c
+638.329 164.38 638.075 164.469 637.88 164.645 c
+637.693 164.829 637.601 165.06 637.601 165.336 c
+638.087 165.336 l
+638.094 165.167 638.145 165.035 638.233 164.939 c
+638.329 164.85 638.468 164.807 638.645 164.807 c
+638.8 164.807 638.917 164.84 638.998 164.91 c
+639.086 164.976 639.13 165.075 639.13 165.203 c
+641.864 166.689 m
+642.187 166.689 l
+642.382 166.689 642.532 166.751 642.643 166.88 c
+642.75 167.005 642.804 167.173 642.804 167.379 c
+642.804 167.85 642.621 168.085 642.261 168.085 c
+642.085 168.085 641.944 168.019 641.849 167.894 c
+641.75 167.765 641.703 167.599 641.703 167.394 c
+641.217 167.394 l
+641.217 167.717 641.313 167.982 641.511 168.187 c
+641.706 168.401 641.956 168.511 642.261 168.511 c
+642.573 168.511 642.823 168.408 643.01 168.202 c
+643.195 168.004 643.29 167.732 643.29 167.379 c
+643.29 167.192 643.238 167.015 643.143 166.85 c
+643.043 166.692 642.922 166.575 642.775 166.497 c
+643.147 166.358 643.334 166.045 643.334 165.556 c
+643.334 165.203 643.235 164.917 643.039 164.704 c
+642.842 164.487 642.584 164.38 642.261 164.38 c
+641.938 164.38 641.676 164.476 641.482 164.675 c
+641.283 164.881 641.188 165.152 641.188 165.498 c
+641.688 165.498 l
+641.688 165.281 641.736 165.108 641.834 164.983 c
+641.941 164.866 642.085 164.807 642.261 164.807 c
+642.437 164.807 642.577 164.866 642.687 164.983 c
+642.794 165.108 642.849 165.295 642.849 165.542 c
+642.849 166.03 642.617 166.277 642.158 166.277 c
+641.864 166.277 l
+h
+646.127 164.807 m
+646.293 164.807 646.424 164.854 646.524 164.954 c
+646.619 165.049 646.675 165.193 646.685 165.38 c
+647.141 165.38 l
+647.13 165.093 647.027 164.854 646.833 164.66 c
+646.644 164.472 646.409 164.38 646.127 164.38 c
+645.763 164.38 645.484 164.498 645.289 164.733 c
+645.09 164.968 644.995 165.314 644.995 165.777 c
+644.995 166.101 l
+644.995 166.549 645.087 166.894 645.274 167.129 c
+645.469 167.364 645.752 167.482 646.127 167.482 c
+646.428 167.482 646.671 167.383 646.847 167.188 c
+647.031 166.99 647.13 166.725 647.141 166.394 c
+646.685 166.394 l
+646.663 166.618 646.605 166.784 646.509 166.894 c
+646.421 167.001 646.293 167.056 646.127 167.056 c
+645.91 167.056 645.748 166.982 645.642 166.835 c
+645.543 166.695 645.487 166.468 645.48 166.144 c
+645.48 165.762 l
+645.48 165.409 645.528 165.16 645.627 165.012 c
+645.734 164.873 645.899 164.807 646.127 164.807 c
+647.435 166.071 m
+647.435 166.501 647.538 166.843 647.743 167.1 c
+647.957 167.354 648.236 167.482 648.581 167.482 c
+648.923 167.482 649.199 167.354 649.405 167.1 c
+649.618 166.853 649.732 166.519 649.742 166.101 c
+649.742 165.791 l
+649.742 165.358 649.632 165.016 649.42 164.763 c
+649.214 164.505 648.934 164.38 648.581 164.38 c
+648.236 164.38 647.964 164.501 647.758 164.748 c
+647.552 164.991 647.442 165.325 647.435 165.748 c
+h
+647.92 165.791 m
+647.92 165.476 647.978 165.233 648.096 165.056 c
+648.221 164.888 648.383 164.807 648.581 164.807 c
+649.011 164.807 649.235 165.116 649.258 165.733 c
+649.258 166.071 l
+649.258 166.373 649.192 166.614 649.067 166.791 c
+648.949 166.967 648.787 167.056 648.581 167.056 c
+648.383 167.056 648.221 166.967 648.096 166.791 c
+647.978 166.614 647.92 166.373 647.92 166.071 c
+h
+650.64 167.423 m
+650.654 167.144 l
+650.831 167.368 651.07 167.482 651.375 167.482 c
+651.705 167.482 651.937 167.335 652.065 167.041 c
+652.249 167.335 652.51 167.482 652.844 167.482 c
+653.403 167.482 653.686 167.136 653.697 166.453 c
+653.697 164.439 l
+653.212 164.439 l
+653.212 166.409 l
+653.212 166.622 653.171 166.78 653.094 166.88 c
+653.013 166.986 652.881 167.041 652.697 167.041 c
+652.55 167.041 652.433 166.982 652.344 166.865 c
+652.256 166.755 652.201 166.614 652.183 166.438 c
+652.183 164.439 l
+651.698 164.439 l
+651.698 166.423 l
+651.687 166.835 651.514 167.041 651.184 167.041 c
+650.937 167.041 650.764 166.916 650.669 166.674 c
+650.669 164.439 l
+650.183 164.439 l
+650.183 167.423 l
+h
+654.601 167.423 m
+654.615 167.144 l
+654.792 167.368 655.031 167.482 655.336 167.482 c
+655.666 167.482 655.898 167.335 656.026 167.041 c
+656.21 167.335 656.471 167.482 656.805 167.482 c
+657.364 167.482 657.647 167.136 657.659 166.453 c
+657.659 164.439 l
+657.173 164.439 l
+657.173 166.409 l
+657.173 166.622 657.133 166.78 657.056 166.88 c
+656.975 166.986 656.842 167.041 656.659 167.041 c
+656.512 167.041 656.394 166.982 656.306 166.865 c
+656.217 166.755 656.163 166.614 656.144 166.438 c
+656.144 164.439 l
+655.66 164.439 l
+655.66 166.423 l
+655.648 166.835 655.475 167.041 655.145 167.041 c
+654.898 167.041 654.725 166.916 654.63 166.674 c
+654.63 164.439 l
+654.145 164.439 l
+654.145 167.423 l
+h
+658.636 164.439 -0.5 2.984 re
+658.665 168.217 m
+658.665 168.129 658.639 168.056 658.592 167.996 c
+658.551 167.945 658.482 167.923 658.386 167.923 c
+658.297 167.923 658.228 167.945 658.18 167.996 c
+658.139 168.056 658.121 168.121 658.121 168.202 c
+658.121 168.291 658.139 168.364 658.18 168.422 c
+658.228 168.482 658.297 168.511 658.386 168.511 c
+658.482 168.511 658.551 168.482 658.592 168.422 c
+658.639 168.364 658.665 168.295 658.665 168.217 c
+659.94 168.143 m
+659.94 167.423 l
+660.396 167.423 l
+660.396 167.026 l
+659.94 167.026 l
+659.94 165.174 l
+659.94 165.056 659.959 164.968 659.999 164.91 c
+660.036 164.85 660.105 164.821 660.205 164.821 c
+660.263 164.821 660.326 164.829 660.396 164.85 c
+660.396 164.439 l
+660.279 164.403 660.165 164.38 660.057 164.38 c
+659.859 164.38 659.708 164.447 659.602 164.586 c
+659.502 164.723 659.455 164.917 659.455 165.174 c
+659.455 167.026 l
+658.999 167.026 l
+658.999 167.423 l
+659.455 167.423 l
+659.455 168.143 l
+h
+662.303 165.203 m
+662.303 165.311 662.263 165.399 662.185 165.469 c
+662.104 165.546 661.954 165.634 661.73 165.733 c
+661.465 165.839 661.278 165.931 661.171 166.012 c
+661.061 166.089 660.984 166.178 660.936 166.277 c
+660.884 166.373 660.863 166.49 660.863 166.629 c
+660.863 166.872 660.951 167.074 661.127 167.232 c
+661.304 167.398 661.528 167.482 661.804 167.482 c
+662.097 167.482 662.332 167.394 662.509 167.217 c
+662.685 167.048 662.773 166.835 662.773 166.571 c
+662.288 166.571 l
+662.288 166.707 662.237 166.82 662.141 166.909 c
+662.053 167.005 661.939 167.056 661.804 167.056 c
+661.657 167.056 661.542 167.015 661.465 166.938 c
+661.384 166.868 661.347 166.77 661.347 166.644 c
+661.347 166.545 661.377 166.468 661.436 166.409 c
+661.495 166.35 661.634 166.269 661.862 166.174 c
+662.222 166.026 662.469 165.883 662.598 165.748 c
+662.733 165.619 662.803 165.446 662.803 165.233 c
+662.803 164.976 662.708 164.771 662.523 164.615 c
+662.347 164.458 662.112 164.38 661.818 164.38 c
+661.502 164.38 661.248 164.469 661.054 164.645 c
+660.867 164.829 660.774 165.06 660.774 165.336 c
+661.26 165.336 l
+661.266 165.167 661.318 165.035 661.407 164.939 c
+661.502 164.85 661.642 164.807 661.818 164.807 c
+661.972 164.807 662.089 164.84 662.17 164.91 c
+662.259 164.976 662.303 165.075 662.303 165.203 c
+665.993 164.439 m
+665.964 164.505 665.941 164.615 665.933 164.763 c
+665.757 164.505 665.537 164.38 665.272 164.38 c
+664.996 164.38 664.78 164.454 664.626 164.601 c
+664.479 164.755 664.405 164.972 664.405 165.247 c
+664.405 165.549 664.508 165.791 664.714 165.968 c
+664.919 166.152 665.202 166.247 665.566 166.247 c
+665.919 166.247 l
+665.919 166.571 l
+665.919 166.747 665.879 166.868 665.802 166.938 c
+665.721 167.015 665.603 167.056 665.449 167.056 c
+665.302 167.056 665.177 167.011 665.081 166.924 c
+664.993 166.835 664.949 166.725 664.949 166.6 c
+664.464 166.6 l
+664.464 166.747 664.508 166.887 664.597 167.026 c
+664.684 167.173 664.802 167.283 664.949 167.364 c
+665.104 167.441 665.276 167.482 665.463 167.482 c
+665.775 167.482 666.01 167.401 666.169 167.247 c
+666.323 167.1 666.404 166.88 666.404 166.585 c
+666.404 165.086 l
+666.411 164.85 666.448 164.649 666.507 164.484 c
+666.507 164.439 l
+h
+665.345 164.821 m
+665.463 164.821 665.574 164.854 665.684 164.925 c
+665.79 164.991 665.867 165.075 665.919 165.174 c
+665.919 165.88 l
+665.655 165.88 l
+665.42 165.88 665.232 165.829 665.096 165.733 c
+664.967 165.634 664.905 165.49 664.905 165.307 c
+664.905 165.137 664.934 165.016 664.993 164.939 c
+665.059 164.858 665.177 164.821 665.345 164.821 c
+667.47 167.115 m
+667.654 167.358 667.889 167.482 668.175 167.482 c
+668.705 167.482 668.973 167.129 668.984 166.423 c
+668.984 164.439 l
+668.499 164.439 l
+668.499 166.394 l
+668.499 166.629 668.458 166.795 668.381 166.894 c
+668.3 166.99 668.183 167.041 668.028 167.041 c
+667.911 167.041 667.801 167.001 667.705 166.924 c
+667.606 166.843 667.529 166.736 667.47 166.6 c
+667.47 164.439 l
+666.985 164.439 l
+666.985 168.673 l
+667.47 168.673 l
+h
+670.586 164.38 m
+670.211 164.38 669.928 164.487 669.733 164.704 c
+669.535 164.929 669.44 165.255 669.44 165.689 c
+669.44 166.057 l
+669.44 166.497 669.532 166.843 669.719 167.1 c
+669.914 167.354 670.189 167.482 670.542 167.482 c
+670.884 167.482 671.138 167.368 671.306 167.144 c
+671.483 166.916 671.574 166.571 671.585 166.101 c
+671.585 165.791 l
+669.925 165.791 l
+669.925 165.718 l
+669.925 165.395 669.983 165.16 670.101 165.012 c
+670.218 164.873 670.388 164.807 670.615 164.807 c
+670.762 164.807 670.887 164.829 670.997 164.881 c
+671.104 164.939 671.207 165.027 671.306 165.145 c
+671.556 164.836 l
+671.35 164.532 671.026 164.38 670.586 164.38 c
+670.542 167.056 m
+670.336 167.056 670.182 166.986 670.086 166.85 c
+669.987 166.71 669.931 166.497 669.925 166.203 c
+671.101 166.203 l
+671.101 166.277 l
+671.078 166.549 671.026 166.747 670.939 166.865 c
+670.851 166.99 670.718 167.056 670.542 167.056 c
+673.526 164.439 m
+673.496 164.505 673.474 164.615 673.467 164.763 c
+673.291 164.505 673.07 164.38 672.805 164.38 c
+672.53 164.38 672.313 164.454 672.159 164.601 c
+672.011 164.755 671.938 164.972 671.938 165.247 c
+671.938 165.549 672.042 165.791 672.247 165.968 c
+672.453 166.152 672.736 166.247 673.1 166.247 c
+673.453 166.247 l
+673.453 166.571 l
+673.453 166.747 673.412 166.868 673.335 166.938 c
+673.254 167.015 673.137 167.056 672.982 167.056 c
+672.835 167.056 672.71 167.011 672.615 166.924 c
+672.526 166.835 672.482 166.725 672.482 166.6 c
+671.997 166.6 l
+671.997 166.747 672.042 166.887 672.129 167.026 c
+672.217 167.173 672.335 167.283 672.482 167.364 c
+672.636 167.441 672.809 167.482 672.996 167.482 c
+673.309 167.482 673.544 167.401 673.702 167.247 c
+673.856 167.1 673.937 166.88 673.937 166.585 c
+673.937 165.086 l
+673.945 164.85 673.981 164.649 674.041 164.484 c
+674.041 164.439 l
+h
+672.879 164.821 m
+672.996 164.821 673.106 164.854 673.218 164.925 c
+673.324 164.991 673.401 165.075 673.453 165.174 c
+673.453 165.88 l
+673.187 165.88 l
+672.952 165.88 672.765 165.829 672.63 165.733 c
+672.501 165.634 672.438 165.49 672.438 165.307 c
+672.438 165.137 672.468 165.016 672.526 164.939 c
+672.593 164.858 672.71 164.821 672.879 164.821 c
+674.415 166.071 m
+674.415 166.531 674.496 166.88 674.665 167.115 c
+674.841 167.358 675.091 167.482 675.415 167.482 c
+675.697 167.482 675.918 167.364 676.076 167.129 c
+676.076 168.673 l
+676.561 168.673 l
+676.561 164.439 l
+676.121 164.439 l
+676.091 164.763 l
+675.932 164.505 675.709 164.38 675.415 164.38 c
+675.099 164.38 674.856 164.498 674.679 164.733 c
+674.504 164.976 674.415 165.321 674.415 165.762 c
+h
+674.901 165.791 m
+674.901 165.457 674.948 165.211 675.047 165.056 c
+675.142 164.898 675.304 164.821 675.533 164.821 c
+675.774 164.821 675.959 164.939 676.076 165.174 c
+676.076 166.689 l
+675.948 166.924 675.768 167.041 675.533 167.041 c
+675.304 167.041 675.142 166.961 675.047 166.806 c
+674.948 166.648 674.901 166.409 674.901 166.086 c
+h
+677.131 163.645 m
+676.837 163.836 l
+677.013 164.083 677.105 164.333 677.116 164.586 c
+677.116 165.042 l
+677.615 165.042 l
+677.615 164.645 l
+677.615 164.458 677.565 164.274 677.469 164.087 c
+677.38 163.903 677.266 163.755 677.131 163.645 c
+f
+q 1 0 0 1 633.2211 158.6623 cm
+0 0 m
+0.544 2.176 l
+1.058 2.176 l
+0.088 -1.234 l
+0.018 -1.488 -0.085 -1.679 -0.22 -1.808 c
+-0.36 -1.944 -0.511 -2.013 -0.676 -2.013 c
+-0.746 -2.013 -0.831 -1.999 -0.926 -1.969 c
+-0.926 -1.558 l
+-0.823 -1.573 l
+-0.688 -1.573 -0.58 -1.535 -0.5 -1.469 c
+-0.412 -1.4 -0.345 -1.282 -0.294 -1.117 c
+-0.206 -0.779 l
+-1.073 2.176 l
+-0.544 2.176 l
+h
+1.238 0.823 m
+1.238 1.253 1.341 1.595 1.547 1.852 c
+1.76 2.106 2.039 2.234 2.385 2.234 c
+2.726 2.234 3.002 2.106 3.208 1.852 c
+3.421 1.606 3.535 1.272 3.546 0.853 c
+3.546 0.544 l
+3.546 0.111 3.436 -0.231 3.223 -0.484 c
+3.017 -0.742 2.737 -0.867 2.385 -0.867 c
+2.039 -0.867 1.768 -0.746 1.562 -0.5 c
+1.356 -0.257 1.245 0.077 1.238 0.5 c
+h
+1.723 0.544 m
+1.723 0.229 1.783 -0.014 1.899 -0.191 c
+2.024 -0.359 2.186 -0.44 2.385 -0.44 c
+2.815 -0.44 3.039 -0.132 3.061 0.485 c
+3.061 0.823 l
+3.061 1.125 2.995 1.367 2.87 1.544 c
+2.753 1.72 2.591 1.808 2.385 1.808 c
+2.186 1.808 2.024 1.72 1.899 1.544 c
+1.783 1.367 1.723 1.125 1.723 0.823 c
+h
+5.501 -0.544 m
+5.343 -0.76 5.108 -0.867 4.796 -0.867 c
+4.531 -0.867 4.325 -0.775 4.178 -0.588 c
+4.039 -0.404 3.973 -0.128 3.973 0.235 c
+3.973 2.176 l
+4.457 2.176 l
+4.457 0.265 l
+4.457 -0.199 4.597 -0.426 4.883 -0.426 c
+5.178 -0.426 5.376 -0.294 5.486 -0.029 c
+5.486 2.176 l
+5.986 2.176 l
+5.986 -0.808 l
+5.516 -0.808 l
+h
+9.183 -0.044 m
+9.183 0.063 9.143 0.151 9.066 0.221 c
+8.985 0.298 8.834 0.387 8.61 0.485 c
+8.345 0.592 8.158 0.684 8.052 0.765 c
+7.941 0.842 7.864 0.93 7.817 1.029 c
+7.765 1.125 7.742 1.243 7.742 1.382 c
+7.742 1.625 7.831 1.827 8.008 1.985 c
+8.183 2.151 8.407 2.234 8.683 2.234 c
+8.977 2.234 9.213 2.147 9.389 1.97 c
+9.565 1.801 9.654 1.588 9.654 1.324 c
+9.168 1.324 l
+9.168 1.459 9.117 1.573 9.022 1.661 c
+8.933 1.757 8.819 1.808 8.683 1.808 c
+8.536 1.808 8.422 1.768 8.345 1.691 c
+8.264 1.621 8.228 1.522 8.228 1.397 c
+8.228 1.297 8.257 1.22 8.316 1.162 c
+8.374 1.103 8.515 1.022 8.742 0.927 c
+9.103 0.779 9.348 0.636 9.477 0.5 c
+9.613 0.372 9.683 0.198 9.683 -0.014 c
+9.683 -0.272 9.587 -0.477 9.404 -0.632 c
+9.228 -0.789 8.992 -0.867 8.698 -0.867 c
+8.382 -0.867 8.129 -0.779 7.934 -0.602 c
+7.746 -0.419 7.655 -0.187 7.655 0.088 c
+8.139 0.088 l
+8.147 -0.081 8.198 -0.213 8.287 -0.309 c
+8.382 -0.397 8.522 -0.44 8.698 -0.44 c
+8.852 -0.44 8.97 -0.407 9.051 -0.338 c
+9.139 -0.272 9.183 -0.172 9.183 -0.044 c
+11.197 -0.867 m
+10.822 -0.867 10.539 -0.76 10.344 -0.544 c
+10.146 -0.319 10.051 0.008 10.051 0.441 c
+10.051 0.809 l
+10.051 1.249 10.142 1.595 10.329 1.852 c
+10.524 2.106 10.8 2.234 11.152 2.234 c
+11.495 2.234 11.748 2.12 11.917 1.897 c
+12.093 1.669 12.185 1.324 12.197 0.853 c
+12.197 0.544 l
+10.535 0.544 l
+10.535 0.47 l
+10.535 0.148 10.595 -0.087 10.712 -0.235 c
+10.83 -0.374 10.998 -0.44 11.227 -0.44 c
+11.373 -0.44 11.499 -0.419 11.609 -0.367 c
+11.715 -0.309 11.818 -0.22 11.917 -0.103 c
+12.167 -0.411 l
+11.961 -0.716 11.638 -0.867 11.197 -0.867 c
+11.152 1.808 m
+10.947 1.808 10.793 1.738 10.697 1.602 c
+10.598 1.463 10.543 1.249 10.535 0.956 c
+11.711 0.956 l
+11.711 1.029 l
+11.69 1.301 11.638 1.5 11.549 1.617 c
+11.462 1.742 11.329 1.808 11.152 1.808 c
+13.666 -0.867 m
+13.292 -0.867 13.008 -0.76 12.814 -0.544 c
+12.615 -0.319 12.52 0.008 12.52 0.441 c
+12.52 0.809 l
+12.52 1.249 12.612 1.595 12.799 1.852 c
+12.993 2.106 13.269 2.234 13.622 2.234 c
+13.964 2.234 14.217 2.12 14.387 1.897 c
+14.563 1.669 14.655 1.324 14.666 0.853 c
+14.666 0.544 l
+13.005 0.544 l
+13.005 0.47 l
+13.005 0.148 13.064 -0.087 13.182 -0.235 c
+13.299 -0.374 13.468 -0.44 13.696 -0.44 c
+13.843 -0.44 13.968 -0.419 14.078 -0.367 c
+14.184 -0.309 14.287 -0.22 14.387 -0.103 c
+14.637 -0.411 l
+14.431 -0.716 14.107 -0.867 13.666 -0.867 c
+13.622 1.808 m
+13.417 1.808 13.263 1.738 13.167 1.602 c
+13.068 1.463 13.012 1.249 13.005 0.956 c
+14.181 0.956 l
+14.181 1.029 l
+14.159 1.301 14.107 1.5 14.019 1.617 c
+13.931 1.742 13.799 1.808 13.622 1.808 c
+16.812 -0.808 -0.5 2.984 re
+16.841 2.97 m
+16.841 2.881 16.816 2.808 16.768 2.749 c
+16.727 2.698 16.658 2.675 16.562 2.675 c
+16.474 2.675 16.404 2.698 16.357 2.749 c
+16.316 2.808 16.297 2.874 16.297 2.955 c
+16.297 3.043 16.316 3.117 16.357 3.175 c
+16.404 3.234 16.474 3.263 16.562 3.263 c
+16.658 3.263 16.727 3.234 16.768 3.175 c
+16.816 3.117 16.841 3.047 16.841 2.97 c
+18.117 2.896 m
+18.117 2.176 l
+18.572 2.176 l
+18.572 1.779 l
+18.117 1.779 l
+18.117 -0.073 l
+18.117 -0.191 18.135 -0.279 18.175 -0.338 c
+18.212 -0.397 18.282 -0.426 18.381 -0.426 c
+18.44 -0.426 18.502 -0.419 18.572 -0.397 c
+18.572 -0.808 l
+18.454 -0.845 18.341 -0.867 18.234 -0.867 c
+18.036 -0.867 17.885 -0.801 17.778 -0.661 c
+17.679 -0.525 17.631 -0.33 17.631 -0.073 c
+17.631 1.779 l
+17.176 1.779 l
+17.176 2.176 l
+17.631 2.176 l
+17.631 2.896 l
+h
+19.039 -1.602 m
+18.745 -1.411 l
+18.921 -1.165 19.013 -0.914 19.025 -0.661 c
+19.025 -0.205 l
+19.524 -0.205 l
+19.524 -0.602 l
+19.524 -0.789 19.472 -0.974 19.377 -1.161 c
+19.289 -1.344 19.175 -1.492 19.039 -1.602 c
+22.518 1.72 m
+22.449 1.727 22.375 1.735 22.298 1.735 c
+22.042 1.735 21.865 1.595 21.77 1.324 c
+21.77 -0.808 l
+21.284 -0.808 l
+21.284 2.176 l
+21.755 2.176 l
+21.77 1.867 l
+21.894 2.11 22.078 2.234 22.313 2.234 c
+22.391 2.234 22.453 2.22 22.504 2.19 c
+h
+23.416 -0.808 -0.5 2.984 re
+23.445 2.97 m
+23.445 2.881 23.42 2.808 23.372 2.749 c
+23.331 2.698 23.262 2.675 23.166 2.675 c
+23.077 2.675 23.008 2.698 22.96 2.749 c
+22.919 2.808 22.901 2.874 22.901 2.955 c
+22.901 3.043 22.919 3.117 22.96 3.175 c
+23.008 3.234 23.077 3.263 23.166 3.263 c
+23.262 3.263 23.331 3.234 23.372 3.175 c
+23.42 3.117 23.445 3.047 23.445 2.97 c
+23.989 0.823 m
+23.989 1.283 24.07 1.632 24.239 1.867 c
+24.415 2.11 24.665 2.234 24.988 2.234 c
+25.29 2.234 25.521 2.103 25.679 1.837 c
+25.708 2.176 l
+26.15 2.176 l
+26.15 -0.837 l
+26.15 -1.209 26.05 -1.492 25.855 -1.691 c
+25.668 -1.885 25.407 -1.984 25.076 -1.984 c
+24.93 -1.984 24.76 -1.944 24.577 -1.866 c
+24.39 -1.797 24.253 -1.708 24.165 -1.602 c
+24.357 -1.263 l
+24.562 -1.469 24.787 -1.573 25.032 -1.573 c
+25.433 -1.573 25.639 -1.344 25.65 -0.897 c
+25.65 -0.515 l
+25.492 -0.75 25.271 -0.867 24.988 -0.867 c
+24.673 -0.867 24.43 -0.75 24.253 -0.515 c
+24.085 -0.279 23.996 0.052 23.989 0.485 c
+h
+24.474 0.544 m
+24.474 0.21 24.521 -0.037 24.621 -0.191 c
+24.716 -0.349 24.878 -0.426 25.105 -0.426 c
+25.348 -0.426 25.533 -0.305 25.65 -0.058 c
+25.65 1.426 l
+25.533 1.669 25.356 1.794 25.12 1.794 c
+24.893 1.794 24.731 1.713 24.635 1.559 c
+24.536 1.401 24.481 1.162 24.474 0.838 c
+h
+27.171 1.867 m
+27.355 2.11 27.59 2.234 27.877 2.234 c
+28.406 2.234 28.674 1.881 28.685 1.176 c
+28.685 -0.808 l
+28.201 -0.808 l
+28.201 1.147 l
+28.201 1.382 28.16 1.548 28.083 1.646 c
+28.002 1.742 27.884 1.794 27.73 1.794 c
+27.613 1.794 27.502 1.754 27.407 1.676 c
+27.307 1.595 27.23 1.488 27.171 1.353 c
+27.171 -0.808 l
+26.686 -0.808 l
+26.686 3.425 l
+27.171 3.425 l
+h
+29.857 2.896 m
+29.857 2.176 l
+30.313 2.176 l
+30.313 1.779 l
+29.857 1.779 l
+29.857 -0.073 l
+29.857 -0.191 29.876 -0.279 29.916 -0.338 c
+29.953 -0.397 30.023 -0.426 30.122 -0.426 c
+30.181 -0.426 30.243 -0.419 30.313 -0.397 c
+30.313 -0.808 l
+30.196 -0.845 30.082 -0.867 29.975 -0.867 c
+29.776 -0.867 29.626 -0.801 29.519 -0.661 c
+29.42 -0.525 29.373 -0.33 29.373 -0.073 c
+29.373 1.779 l
+28.916 1.779 l
+28.916 2.176 l
+29.373 2.176 l
+29.373 2.896 l
+h
+31.28 0.324 m
+31.28 0.537 31.297 0.713 31.338 0.853 c
+31.386 1 31.5 1.176 31.677 1.382 c
+31.883 1.632 l
+32.007 1.816 32.074 2.003 32.074 2.19 c
+32.074 2.386 32.029 2.536 31.941 2.646 c
+31.86 2.753 31.75 2.808 31.603 2.808 c
+31.463 2.808 31.345 2.756 31.25 2.66 c
+31.162 2.562 31.118 2.429 31.118 2.264 c
+30.632 2.264 l
+30.632 2.565 30.721 2.808 30.898 2.984 c
+31.081 3.168 31.316 3.263 31.603 3.263 c
+31.897 3.263 32.124 3.168 32.294 2.984 c
+32.471 2.797 32.558 2.543 32.558 2.22 c
+32.558 1.904 32.43 1.592 32.176 1.279 c
+31.926 0.971 l
+31.816 0.823 31.765 0.607 31.765 0.324 c
+h
+31.544 -0.264 m
+31.632 -0.264 31.698 -0.294 31.75 -0.353 c
+31.798 -0.404 31.823 -0.47 31.823 -0.558 c
+31.823 -0.639 31.798 -0.706 31.75 -0.764 c
+31.698 -0.812 31.632 -0.837 31.544 -0.837 c
+31.445 -0.837 31.372 -0.812 31.324 -0.764 c
+31.283 -0.706 31.264 -0.639 31.264 -0.558 c
+31.264 -0.47 31.283 -0.404 31.324 -0.353 c
+31.372 -0.294 31.445 -0.264 31.544 -0.264 c
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 678.972 167.5189 cm
+0 0 m
+7.555 -2.874 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 684.5352 165.4025 cm
+0 0 m
+-1.377 -0.618 l
+2.492 -0.949 l
+-0.618 1.377 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 684.5352 165.4025 cm
+0 0 m
+-1.377 -0.618 l
+2.492 -0.949 l
+-0.618 1.377 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 501.1054 110.5303 cm
+0 0 m
+-1.058 0 l
+-1.058 -3.572 l
+-1.558 -3.572 l
+-1.558 0 l
+-2.616 0 l
+-2.616 0.441 l
+0 0.441 l
+h
+0.823 -0.897 m
+1.008 -0.654 1.243 -0.529 1.529 -0.529 c
+2.058 -0.529 2.326 -0.881 2.338 -1.587 c
+2.338 -3.572 l
+1.852 -3.572 l
+1.852 -1.616 l
+1.852 -1.381 1.812 -1.216 1.735 -1.117 c
+1.654 -1.022 1.536 -0.97 1.382 -0.97 c
+1.264 -0.97 1.154 -1.01 1.058 -1.087 c
+0.96 -1.168 0.882 -1.275 0.823 -1.411 c
+0.823 -3.572 l
+0.339 -3.572 l
+0.339 0.661 l
+0.823 0.661 l
+h
+3.407 -3.572 -0.5 2.984 re
+3.437 0.206 m
+3.437 0.118 3.41 0.044 3.363 -0.014 c
+3.323 -0.066 3.252 -0.087 3.157 -0.087 c
+3.069 -0.087 2.999 -0.066 2.951 -0.014 c
+2.911 0.044 2.893 0.11 2.893 0.191 c
+2.893 0.279 2.911 0.353 2.951 0.412 c
+2.999 0.47 3.069 0.5 3.157 0.5 c
+3.252 0.5 3.323 0.47 3.363 0.412 c
+3.41 0.353 3.437 0.283 3.437 0.206 c
+5.524 -2.807 m
+5.524 -2.701 5.484 -2.612 5.406 -2.543 c
+5.326 -2.466 5.174 -2.377 4.95 -2.278 c
+4.686 -2.171 4.499 -2.08 4.391 -1.999 c
+4.281 -1.922 4.204 -1.834 4.156 -1.734 c
+4.105 -1.639 4.083 -1.521 4.083 -1.381 c
+4.083 -1.139 4.171 -0.937 4.348 -0.779 c
+4.524 -0.613 4.748 -0.529 5.024 -0.529 c
+5.318 -0.529 5.553 -0.617 5.729 -0.793 c
+5.906 -0.962 5.994 -1.176 5.994 -1.44 c
+5.509 -1.44 l
+5.509 -1.304 5.457 -1.19 5.362 -1.103 c
+5.274 -1.007 5.16 -0.955 5.024 -0.955 c
+4.877 -0.955 4.763 -0.995 4.686 -1.072 c
+4.605 -1.143 4.568 -1.242 4.568 -1.367 c
+4.568 -1.466 4.597 -1.543 4.657 -1.602 c
+4.715 -1.66 4.855 -1.741 5.083 -1.837 c
+5.443 -1.984 5.689 -2.127 5.818 -2.263 c
+5.954 -2.392 6.024 -2.564 6.024 -2.778 c
+6.024 -3.035 5.928 -3.241 5.744 -3.395 c
+5.567 -3.553 5.332 -3.63 5.039 -3.63 c
+4.723 -3.63 4.469 -3.542 4.274 -3.366 c
+4.087 -3.183 3.995 -2.95 3.995 -2.675 c
+4.48 -2.675 l
+4.487 -2.844 4.539 -2.977 4.627 -3.072 c
+4.723 -3.16 4.862 -3.204 5.039 -3.204 c
+5.193 -3.204 5.311 -3.171 5.391 -3.102 c
+5.48 -3.035 5.524 -2.936 5.524 -2.807 c
+8.214 -3.572 -0.5 2.984 re
+8.243 0.206 m
+8.243 0.118 8.217 0.044 8.169 -0.014 c
+8.129 -0.066 8.059 -0.087 7.963 -0.087 c
+7.876 -0.087 7.805 -0.066 7.758 -0.014 c
+7.718 0.044 7.699 0.11 7.699 0.191 c
+7.699 0.279 7.718 0.353 7.758 0.412 c
+7.805 0.47 7.876 0.5 7.963 0.5 c
+8.059 0.5 8.129 0.47 8.169 0.412 c
+8.217 0.353 8.243 0.283 8.243 0.206 c
+10.33 -2.807 m
+10.33 -2.701 10.29 -2.612 10.213 -2.543 c
+10.132 -2.466 9.981 -2.377 9.757 -2.278 c
+9.492 -2.171 9.305 -2.08 9.199 -1.999 c
+9.089 -1.922 9.011 -1.834 8.963 -1.734 c
+8.912 -1.639 8.89 -1.521 8.89 -1.381 c
+8.89 -1.139 8.978 -0.937 9.154 -0.779 c
+9.33 -0.613 9.555 -0.529 9.831 -0.529 c
+10.124 -0.529 10.359 -0.617 10.536 -0.793 c
+10.712 -0.962 10.801 -1.176 10.801 -1.44 c
+10.315 -1.44 l
+10.315 -1.304 10.264 -1.19 10.168 -1.103 c
+10.08 -1.007 9.966 -0.955 9.831 -0.955 c
+9.683 -0.955 9.569 -0.995 9.492 -1.072 c
+9.411 -1.143 9.375 -1.242 9.375 -1.367 c
+9.375 -1.466 9.405 -1.543 9.463 -1.602 c
+9.522 -1.66 9.661 -1.741 9.889 -1.837 c
+10.249 -1.984 10.496 -2.127 10.624 -2.263 c
+10.76 -2.392 10.83 -2.564 10.83 -2.778 c
+10.83 -3.035 10.735 -3.241 10.55 -3.395 c
+10.375 -3.553 10.139 -3.63 9.845 -3.63 c
+9.529 -3.63 9.276 -3.542 9.081 -3.366 c
+8.894 -3.183 8.802 -2.95 8.802 -2.675 c
+9.287 -2.675 l
+9.294 -2.844 9.345 -2.977 9.434 -3.072 c
+9.529 -3.16 9.669 -3.204 9.845 -3.204 c
+9.999 -3.204 10.117 -3.171 10.198 -3.102 c
+10.286 -3.035 10.33 -2.936 10.33 -2.807 c
+14.02 -3.572 m
+13.991 -3.505 13.968 -3.395 13.961 -3.248 c
+13.785 -3.505 13.564 -3.63 13.299 -3.63 c
+13.024 -3.63 12.807 -3.557 12.653 -3.41 c
+12.505 -3.256 12.432 -3.039 12.432 -2.763 c
+12.432 -2.462 12.535 -2.219 12.74 -2.043 c
+12.947 -1.859 13.23 -1.764 13.594 -1.764 c
+13.947 -1.764 l
+13.947 -1.44 l
+13.947 -1.263 13.906 -1.143 13.829 -1.072 c
+13.748 -0.995 13.63 -0.955 13.476 -0.955 c
+13.328 -0.955 13.204 -0.999 13.108 -1.087 c
+13.02 -1.176 12.976 -1.286 12.976 -1.411 c
+12.491 -1.411 l
+12.491 -1.263 12.535 -1.124 12.623 -0.985 c
+12.711 -0.837 12.829 -0.727 12.976 -0.646 c
+13.13 -0.569 13.303 -0.529 13.49 -0.529 c
+13.803 -0.529 14.038 -0.61 14.196 -0.764 c
+14.35 -0.911 14.431 -1.132 14.431 -1.425 c
+14.431 -2.925 l
+14.439 -3.16 14.475 -3.362 14.534 -3.528 c
+14.534 -3.572 l
+h
+13.373 -3.189 m
+13.49 -3.189 13.6 -3.156 13.711 -3.087 c
+13.818 -3.021 13.895 -2.936 13.947 -2.836 c
+13.947 -2.131 l
+13.681 -2.131 l
+13.446 -2.131 13.259 -2.183 13.123 -2.278 c
+12.994 -2.377 12.932 -2.52 12.932 -2.705 c
+12.932 -2.873 12.962 -2.994 13.02 -3.072 c
+13.087 -3.152 13.204 -3.189 13.373 -3.189 c
+16.669 -0.588 m
+16.684 -0.911 l
+16.867 -0.658 17.11 -0.529 17.404 -0.529 c
+17.933 -0.529 18.201 -0.881 18.213 -1.587 c
+18.213 -3.572 l
+17.727 -3.572 l
+17.727 -1.616 l
+17.727 -1.381 17.687 -1.216 17.61 -1.117 c
+17.529 -1.022 17.411 -0.97 17.257 -0.97 c
+17.139 -0.97 17.029 -1.01 16.933 -1.087 c
+16.835 -1.168 16.757 -1.275 16.698 -1.411 c
+16.698 -3.572 l
+16.214 -3.572 l
+16.214 -0.588 l
+h
+18.664 -1.94 m
+18.664 -1.51 18.768 -1.168 18.973 -0.911 c
+19.186 -0.658 19.466 -0.529 19.811 -0.529 c
+20.153 -0.529 20.428 -0.658 20.634 -0.911 c
+20.848 -1.157 20.962 -1.492 20.972 -1.911 c
+20.972 -2.219 l
+20.972 -2.653 20.862 -2.994 20.649 -3.248 c
+20.443 -3.505 20.164 -3.63 19.811 -3.63 c
+19.466 -3.63 19.194 -3.509 18.988 -3.262 c
+18.782 -3.021 18.672 -2.686 18.664 -2.263 c
+h
+19.15 -2.219 m
+19.15 -2.535 19.208 -2.778 19.326 -2.954 c
+19.451 -3.123 19.613 -3.204 19.811 -3.204 c
+20.241 -3.204 20.465 -2.896 20.487 -2.278 c
+20.487 -1.94 l
+20.487 -1.639 20.421 -1.396 20.296 -1.22 c
+20.179 -1.043 20.017 -0.955 19.811 -0.955 c
+19.613 -0.955 19.451 -1.043 19.326 -1.22 c
+19.208 -1.396 19.15 -1.639 19.15 -1.94 c
+h
+22.645 -1.043 m
+22.575 -1.036 22.501 -1.028 22.423 -1.028 c
+22.167 -1.028 21.99 -1.168 21.895 -1.44 c
+21.895 -3.572 l
+21.409 -3.572 l
+21.409 -0.588 l
+21.88 -0.588 l
+21.895 -0.897 l
+22.02 -0.654 22.203 -0.529 22.439 -0.529 c
+22.516 -0.529 22.578 -0.544 22.629 -0.573 c
+h
+23.471 -0.588 m
+23.486 -0.867 l
+23.662 -0.643 23.901 -0.529 24.206 -0.529 c
+24.537 -0.529 24.769 -0.675 24.897 -0.97 c
+25.081 -0.675 25.342 -0.529 25.676 -0.529 c
+26.234 -0.529 26.518 -0.874 26.529 -1.558 c
+26.529 -3.572 l
+26.043 -3.572 l
+26.043 -1.602 l
+26.043 -1.389 26.003 -1.23 25.926 -1.132 c
+25.845 -1.025 25.713 -0.97 25.529 -0.97 c
+25.382 -0.97 25.264 -1.028 25.176 -1.146 c
+25.088 -1.257 25.033 -1.396 25.014 -1.573 c
+25.014 -3.572 l
+24.53 -3.572 l
+24.53 -1.587 l
+24.519 -1.176 24.345 -0.97 24.015 -0.97 c
+23.769 -0.97 23.597 -1.095 23.501 -1.338 c
+23.501 -3.572 l
+23.015 -3.572 l
+23.015 -0.588 l
+h
+28.505 -3.572 m
+28.476 -3.505 28.455 -3.395 28.447 -3.248 c
+28.27 -3.505 28.05 -3.63 27.786 -3.63 c
+27.51 -3.63 27.293 -3.557 27.138 -3.41 c
+26.992 -3.256 26.918 -3.039 26.918 -2.763 c
+26.918 -2.462 27.021 -2.219 27.227 -2.043 c
+27.433 -1.859 27.715 -1.764 28.079 -1.764 c
+28.432 -1.764 l
+28.432 -1.44 l
+28.432 -1.263 28.392 -1.143 28.314 -1.072 c
+28.233 -0.995 28.116 -0.955 27.962 -0.955 c
+27.815 -0.955 27.69 -0.999 27.595 -1.087 c
+27.506 -1.176 27.462 -1.286 27.462 -1.411 c
+26.977 -1.411 l
+26.977 -1.263 27.021 -1.124 27.109 -0.985 c
+27.198 -0.837 27.315 -0.727 27.462 -0.646 c
+27.616 -0.569 27.789 -0.529 27.977 -0.529 c
+28.289 -0.529 28.524 -0.61 28.682 -0.764 c
+28.837 -0.911 28.917 -1.132 28.917 -1.425 c
+28.917 -2.925 l
+28.925 -3.16 28.962 -3.362 29.02 -3.528 c
+29.02 -3.572 l
+h
+27.859 -3.189 m
+27.977 -3.189 28.087 -3.156 28.197 -3.087 c
+28.303 -3.021 28.38 -2.936 28.432 -2.836 c
+28.432 -2.131 l
+28.168 -2.131 l
+27.932 -2.131 27.745 -2.183 27.609 -2.278 c
+27.48 -2.377 27.418 -2.52 27.418 -2.705 c
+27.418 -2.873 27.447 -2.994 27.506 -3.072 c
+27.572 -3.152 27.69 -3.189 27.859 -3.189 c
+30.027 -3.572 -0.5 4.233 re
+32.933 -3.204 m
+33.099 -3.204 33.232 -3.156 33.33 -3.057 c
+33.426 -2.961 33.481 -2.818 33.492 -2.631 c
+33.948 -2.631 l
+33.937 -2.917 33.834 -3.156 33.639 -3.351 c
+33.452 -3.538 33.217 -3.63 32.933 -3.63 c
+32.57 -3.63 32.291 -3.513 32.096 -3.278 c
+31.898 -3.042 31.802 -2.697 31.802 -2.234 c
+31.802 -1.911 l
+31.802 -1.462 31.894 -1.117 32.081 -0.881 c
+32.276 -0.646 32.559 -0.529 32.933 -0.529 c
+33.235 -0.529 33.477 -0.628 33.654 -0.823 c
+33.837 -1.022 33.937 -1.286 33.948 -1.616 c
+33.492 -1.616 l
+33.471 -1.392 33.411 -1.227 33.316 -1.117 c
+33.228 -1.01 33.099 -0.955 32.933 -0.955 c
+32.717 -0.955 32.555 -1.028 32.449 -1.176 c
+32.349 -1.315 32.295 -1.543 32.287 -1.866 c
+32.287 -2.248 l
+32.287 -2.601 32.335 -2.851 32.434 -2.998 c
+32.54 -3.138 32.706 -3.204 32.933 -3.204 c
+34.242 -1.94 m
+34.242 -1.51 34.345 -1.168 34.551 -0.911 c
+34.764 -0.658 35.043 -0.529 35.389 -0.529 c
+35.73 -0.529 36.006 -0.658 36.212 -0.911 c
+36.424 -1.157 36.538 -1.492 36.55 -1.911 c
+36.55 -2.219 l
+36.55 -2.653 36.44 -2.994 36.226 -3.248 c
+36.021 -3.505 35.742 -3.63 35.389 -3.63 c
+35.043 -3.63 34.771 -3.509 34.566 -3.262 c
+34.36 -3.021 34.25 -2.686 34.242 -2.263 c
+h
+34.727 -2.219 m
+34.727 -2.535 34.786 -2.778 34.903 -2.954 c
+35.028 -3.123 35.19 -3.204 35.389 -3.204 c
+35.819 -3.204 36.043 -2.896 36.064 -2.278 c
+36.064 -1.94 l
+36.064 -1.639 35.998 -1.396 35.873 -1.22 c
+35.756 -1.043 35.594 -0.955 35.389 -0.955 c
+35.19 -0.955 35.028 -1.043 34.903 -1.22 c
+34.786 -1.396 34.727 -1.639 34.727 -1.94 c
+h
+37.442 -0.588 m
+37.457 -0.867 l
+37.633 -0.643 37.872 -0.529 38.178 -0.529 c
+38.508 -0.529 38.74 -0.675 38.869 -0.97 c
+39.052 -0.675 39.314 -0.529 39.647 -0.529 c
+40.206 -0.529 40.489 -0.874 40.5 -1.558 c
+40.5 -3.572 l
+40.015 -3.572 l
+40.015 -1.602 l
+40.015 -1.389 39.975 -1.23 39.898 -1.132 c
+39.817 -1.025 39.684 -0.97 39.501 -0.97 c
+39.354 -0.97 39.235 -1.028 39.148 -1.146 c
+39.06 -1.257 39.004 -1.396 38.986 -1.573 c
+38.986 -3.572 l
+38.501 -3.572 l
+38.501 -1.587 l
+38.49 -1.176 38.317 -0.97 37.986 -0.97 c
+37.741 -0.97 37.568 -1.095 37.473 -1.338 c
+37.473 -3.572 l
+36.987 -3.572 l
+36.987 -0.588 l
+h
+41.404 -0.588 m
+41.419 -0.867 l
+41.595 -0.643 41.834 -0.529 42.139 -0.529 c
+42.47 -0.529 42.701 -0.675 42.83 -0.97 c
+43.013 -0.675 43.274 -0.529 43.609 -0.529 c
+44.168 -0.529 44.45 -0.874 44.461 -1.558 c
+44.461 -3.572 l
+43.977 -3.572 l
+43.977 -1.602 l
+43.977 -1.389 43.936 -1.23 43.859 -1.132 c
+43.778 -1.025 43.646 -0.97 43.462 -0.97 c
+43.314 -0.97 43.197 -1.028 43.109 -1.146 c
+43.021 -1.257 42.965 -1.396 42.948 -1.573 c
+42.948 -3.572 l
+42.462 -3.572 l
+42.462 -1.587 l
+42.451 -1.176 42.279 -0.97 41.948 -0.97 c
+41.702 -0.97 41.529 -1.095 41.433 -1.338 c
+41.433 -3.572 l
+40.949 -3.572 l
+40.949 -0.588 l
+h
+45.439 -3.572 -0.5 2.984 re
+45.469 0.206 m
+45.469 0.118 45.442 0.044 45.395 -0.014 c
+45.354 -0.066 45.284 -0.087 45.189 -0.087 c
+45.101 -0.087 45.031 -0.066 44.983 -0.014 c
+44.943 0.044 44.924 0.11 44.924 0.191 c
+44.924 0.279 44.943 0.353 44.983 0.412 c
+45.031 0.47 45.101 0.5 45.189 0.5 c
+45.284 0.5 45.354 0.47 45.395 0.412 c
+45.442 0.353 45.469 0.283 45.469 0.206 c
+46.747 0.133 m
+46.747 -0.588 l
+47.203 -0.588 l
+47.203 -0.985 l
+46.747 -0.985 l
+46.747 -2.836 l
+46.747 -2.954 46.765 -3.042 46.806 -3.102 c
+46.842 -3.16 46.913 -3.189 47.012 -3.189 c
+47.071 -3.189 47.133 -3.183 47.203 -3.16 c
+47.203 -3.572 l
+47.085 -3.609 46.971 -3.63 46.865 -3.63 c
+46.666 -3.63 46.516 -3.564 46.41 -3.424 c
+46.31 -3.289 46.262 -3.094 46.262 -2.836 c
+46.262 -0.985 l
+45.806 -0.985 l
+45.806 -0.588 l
+46.262 -0.588 l
+46.262 0.133 l
+h
+47.669 -4.365 m
+47.376 -4.174 l
+47.552 -3.928 47.644 -3.678 47.655 -3.424 c
+47.655 -2.969 l
+48.155 -2.969 l
+48.155 -3.366 l
+48.155 -3.553 48.103 -3.737 48.008 -3.925 c
+47.919 -4.108 47.806 -4.255 47.669 -4.365 c
+f
+Q
+515.596 100.373 -0.5 2.984 re
+515.625 104.151 m
+515.625 104.063 515.599 103.989 515.551 103.931 c
+515.511 103.879 515.441 103.858 515.345 103.858 c
+515.257 103.858 515.187 103.879 515.139 103.931 c
+515.099 103.989 515.081 104.055 515.081 104.136 c
+515.081 104.225 515.099 104.298 515.139 104.357 c
+515.187 104.415 515.257 104.446 515.345 104.446 c
+515.441 104.446 515.511 104.415 515.551 104.357 c
+515.599 104.298 515.625 104.228 515.625 104.151 c
+516.903 104.078 m
+516.903 103.357 l
+517.36 103.357 l
+517.36 102.96 l
+516.903 102.96 l
+516.903 101.109 l
+516.903 100.991 516.922 100.903 516.963 100.844 c
+516.999 100.785 517.069 100.756 517.169 100.756 c
+517.227 100.756 517.29 100.763 517.36 100.785 c
+517.36 100.373 l
+517.242 100.336 517.128 100.315 517.021 100.315 c
+516.822 100.315 516.672 100.381 516.566 100.521 c
+516.467 100.656 516.419 100.851 516.419 101.109 c
+516.419 102.96 l
+515.963 102.96 l
+515.963 103.357 l
+516.419 103.357 l
+516.419 104.078 l
+h
+519.513 103.049 m
+519.696 103.291 519.932 103.416 520.218 103.416 c
+520.747 103.416 521.016 103.064 521.027 102.358 c
+521.027 100.373 l
+520.542 100.373 l
+520.542 102.329 l
+520.542 102.564 520.502 102.729 520.424 102.828 c
+520.344 102.923 520.226 102.975 520.072 102.975 c
+519.954 102.975 519.843 102.935 519.748 102.858 c
+519.648 102.777 519.571 102.67 519.513 102.534 c
+519.513 100.373 l
+519.028 100.373 l
+519.028 104.606 l
+519.513 104.606 l
+h
+523.099 100.373 m
+523.07 100.44 523.048 100.55 523.041 100.697 c
+522.864 100.44 522.644 100.315 522.379 100.315 c
+522.104 100.315 521.886 100.388 521.732 100.535 c
+521.585 100.689 521.512 100.907 521.512 101.182 c
+521.512 101.483 521.615 101.726 521.821 101.902 c
+522.027 102.086 522.31 102.181 522.673 102.181 c
+523.026 102.181 l
+523.026 102.505 l
+523.026 102.682 522.985 102.802 522.908 102.873 c
+522.827 102.95 522.71 102.99 522.555 102.99 c
+522.409 102.99 522.283 102.946 522.188 102.858 c
+522.1 102.769 522.056 102.659 522.056 102.534 c
+521.57 102.534 l
+521.57 102.682 521.615 102.821 521.703 102.96 c
+521.791 103.108 521.909 103.218 522.056 103.299 c
+522.21 103.376 522.383 103.416 522.57 103.416 c
+522.883 103.416 523.118 103.336 523.276 103.181 c
+523.43 103.034 523.511 102.813 523.511 102.52 c
+523.511 101.02 l
+523.519 100.785 523.555 100.583 523.614 100.417 c
+523.614 100.373 l
+h
+522.453 100.756 m
+522.57 100.756 522.68 100.789 522.79 100.859 c
+522.898 100.924 522.975 101.009 523.026 101.109 c
+523.026 101.814 l
+522.761 101.814 l
+522.526 101.814 522.339 101.763 522.203 101.667 c
+522.075 101.568 522.012 101.425 522.012 101.241 c
+522.012 101.072 522.041 100.951 522.1 100.874 c
+522.166 100.793 522.283 100.756 522.453 100.756 c
+525.532 101.138 m
+525.532 101.244 525.491 101.333 525.414 101.402 c
+525.333 101.479 525.183 101.568 524.959 101.667 c
+524.695 101.774 524.506 101.865 524.4 101.946 c
+524.29 102.023 524.213 102.112 524.165 102.211 c
+524.113 102.306 524.091 102.424 524.091 102.564 c
+524.091 102.806 524.18 103.008 524.356 103.166 c
+524.533 103.332 524.757 103.416 525.032 103.416 c
+525.327 103.416 525.562 103.328 525.738 103.152 c
+525.915 102.983 526.002 102.769 526.002 102.505 c
+525.518 102.505 l
+525.518 102.641 525.466 102.755 525.37 102.843 c
+525.282 102.939 525.169 102.99 525.032 102.99 c
+524.885 102.99 524.772 102.95 524.695 102.873 c
+524.614 102.802 524.577 102.703 524.577 102.578 c
+524.577 102.479 524.606 102.402 524.664 102.343 c
+524.724 102.285 524.863 102.204 525.091 102.108 c
+525.451 101.961 525.697 101.818 525.826 101.682 c
+525.962 101.553 526.032 101.381 526.032 101.167 c
+526.032 100.91 525.936 100.704 525.753 100.55 c
+525.576 100.392 525.341 100.315 525.047 100.315 c
+524.731 100.315 524.477 100.403 524.282 100.579 c
+524.095 100.763 524.003 100.995 524.003 101.271 c
+524.488 101.271 l
+524.496 101.101 524.547 100.969 524.635 100.874 c
+524.731 100.785 524.87 100.741 525.047 100.741 c
+525.202 100.741 525.319 100.774 525.4 100.844 c
+525.488 100.91 525.532 101.009 525.532 101.138 c
+527.59 102.005 m
+527.59 102.435 527.693 102.777 527.899 103.034 c
+528.111 103.288 528.391 103.416 528.737 103.416 c
+529.078 103.416 529.354 103.288 529.56 103.034 c
+529.773 102.788 529.886 102.453 529.898 102.034 c
+529.898 101.726 l
+529.898 101.292 529.788 100.951 529.574 100.697 c
+529.368 100.44 529.09 100.315 528.737 100.315 c
+528.391 100.315 528.119 100.436 527.914 100.683 c
+527.708 100.924 527.598 101.259 527.59 101.682 c
+h
+528.075 101.726 m
+528.075 101.41 528.134 101.167 528.251 100.991 c
+528.377 100.822 528.538 100.741 528.737 100.741 c
+529.167 100.741 529.391 101.05 529.412 101.667 c
+529.412 102.005 l
+529.412 102.306 529.346 102.549 529.221 102.725 c
+529.104 102.902 528.942 102.99 528.737 102.99 c
+528.538 102.99 528.377 102.902 528.251 102.725 c
+528.134 102.549 528.075 102.306 528.075 102.005 c
+h
+530.79 103.357 m
+530.806 103.034 l
+530.989 103.288 531.232 103.416 531.525 103.416 c
+532.055 103.416 532.323 103.064 532.334 102.358 c
+532.334 100.373 l
+531.849 100.373 l
+531.849 102.329 l
+531.849 102.564 531.808 102.729 531.731 102.828 c
+531.65 102.923 531.533 102.975 531.378 102.975 c
+531.261 102.975 531.151 102.935 531.055 102.858 c
+530.956 102.777 530.879 102.67 530.82 102.534 c
+530.82 100.373 l
+530.335 100.373 l
+530.335 103.357 l
+h
+533.948 100.315 m
+533.572 100.315 533.289 100.421 533.094 100.638 c
+532.896 100.862 532.801 101.19 532.801 101.623 c
+532.801 101.99 l
+532.801 102.431 532.893 102.777 533.08 103.034 c
+533.275 103.288 533.551 103.416 533.903 103.416 c
+534.245 103.416 534.499 103.303 534.667 103.078 c
+534.844 102.85 534.935 102.505 534.947 102.034 c
+534.947 101.726 l
+533.285 101.726 l
+533.285 101.653 l
+533.285 101.329 533.345 101.094 533.462 100.947 c
+533.58 100.807 533.749 100.741 533.977 100.741 c
+534.124 100.741 534.249 100.763 534.359 100.814 c
+534.465 100.874 534.569 100.961 534.667 101.079 c
+534.918 100.77 l
+534.711 100.465 534.388 100.315 533.948 100.315 c
+533.903 102.99 m
+533.697 102.99 533.543 102.92 533.447 102.784 c
+533.348 102.644 533.293 102.431 533.285 102.137 c
+534.461 102.137 l
+534.461 102.211 l
+534.44 102.483 534.388 102.682 534.3 102.799 c
+534.212 102.923 534.079 102.99 533.903 102.99 c
+538.706 101.726 m
+538.706 101.256 538.621 100.903 538.457 100.668 c
+538.287 100.432 538.048 100.315 537.736 100.315 c
+537.431 100.315 537.2 100.425 537.045 100.653 c
+537.045 99.227 l
+536.56 99.227 l
+536.56 103.357 l
+537.001 103.357 l
+537.03 103.019 l
+537.185 103.284 537.416 103.416 537.721 103.416 c
+538.052 103.416 538.299 103.299 538.457 103.064 c
+538.621 102.836 538.706 102.497 538.706 102.05 c
+h
+538.221 102.005 m
+538.221 102.336 538.166 102.582 538.06 102.74 c
+537.96 102.894 537.798 102.975 537.574 102.975 c
+537.339 102.975 537.163 102.858 537.045 102.622 c
+537.045 101.079 l
+537.163 100.851 537.343 100.741 537.589 100.741 c
+537.802 100.741 537.96 100.818 538.06 100.976 c
+538.166 101.13 538.221 101.373 538.221 101.697 c
+h
+540.695 100.373 m
+540.664 100.44 540.643 100.55 540.635 100.697 c
+540.459 100.44 540.238 100.315 539.974 100.315 c
+539.698 100.315 539.481 100.388 539.327 100.535 c
+539.18 100.689 539.107 100.907 539.107 101.182 c
+539.107 101.483 539.209 101.726 539.415 101.902 c
+539.621 102.086 539.904 102.181 540.268 102.181 c
+540.62 102.181 l
+540.62 102.505 l
+540.62 102.682 540.58 102.802 540.503 102.873 c
+540.422 102.95 540.304 102.99 540.15 102.99 c
+540.003 102.99 539.878 102.946 539.783 102.858 c
+539.695 102.769 539.65 102.659 539.65 102.534 c
+539.165 102.534 l
+539.165 102.682 539.209 102.821 539.298 102.96 c
+539.386 103.108 539.504 103.218 539.65 103.299 c
+539.805 103.376 539.978 103.416 540.165 103.416 c
+540.477 103.416 540.712 103.336 540.871 103.181 c
+541.025 103.034 541.106 102.813 541.106 102.52 c
+541.106 101.02 l
+541.113 100.785 541.15 100.583 541.208 100.417 c
+541.208 100.373 l
+h
+540.047 100.756 m
+540.165 100.756 540.275 100.789 540.385 100.859 c
+540.492 100.924 540.569 101.009 540.62 101.109 c
+540.62 101.814 l
+540.356 101.814 l
+540.121 101.814 539.934 101.763 539.797 101.667 c
+539.669 101.568 539.606 101.425 539.606 101.241 c
+539.606 101.072 539.635 100.951 539.695 100.874 c
+539.76 100.793 539.878 100.756 540.047 100.756 c
+542.921 102.902 m
+542.851 102.909 542.777 102.917 542.7 102.917 c
+542.443 102.917 542.267 102.777 542.172 102.505 c
+542.172 100.373 l
+541.686 100.373 l
+541.686 103.357 l
+542.156 103.357 l
+542.172 103.049 l
+542.297 103.291 542.48 103.416 542.715 103.416 c
+542.792 103.416 542.855 103.401 542.906 103.372 c
+h
+544.296 100.315 m
+543.92 100.315 543.637 100.421 543.443 100.638 c
+543.244 100.862 543.149 101.19 543.149 101.623 c
+543.149 101.99 l
+543.149 102.431 543.241 102.777 543.428 103.034 c
+543.623 103.288 543.899 103.416 544.252 103.416 c
+544.593 103.416 544.847 103.303 545.015 103.078 c
+545.192 102.85 545.284 102.505 545.295 102.034 c
+545.295 101.726 l
+543.633 101.726 l
+543.633 101.653 l
+543.633 101.329 543.693 101.094 543.81 100.947 c
+543.928 100.807 544.097 100.741 544.325 100.741 c
+544.472 100.741 544.597 100.763 544.707 100.814 c
+544.813 100.874 544.917 100.961 545.015 101.079 c
+545.266 100.77 l
+545.06 100.465 544.736 100.315 544.296 100.315 c
+544.252 102.99 m
+544.046 102.99 543.891 102.92 543.795 102.784 c
+543.697 102.644 543.641 102.431 543.633 102.137 c
+544.809 102.137 l
+544.809 102.211 l
+544.788 102.483 544.736 102.682 544.648 102.799 c
+544.56 102.923 544.427 102.99 544.252 102.99 c
+546.162 103.357 m
+546.177 103.034 l
+546.361 103.288 546.603 103.416 546.897 103.416 c
+547.427 103.416 547.695 103.064 547.706 102.358 c
+547.706 100.373 l
+547.221 100.373 l
+547.221 102.329 l
+547.221 102.564 547.18 102.729 547.103 102.828 c
+547.022 102.923 546.904 102.975 546.75 102.975 c
+546.633 102.975 546.522 102.935 546.427 102.858 c
+546.328 102.777 546.251 102.67 546.191 102.534 c
+546.191 100.373 l
+545.707 100.373 l
+545.707 103.357 l
+h
+548.892 104.078 m
+548.892 103.357 l
+549.349 103.357 l
+549.349 102.96 l
+548.892 102.96 l
+548.892 101.109 l
+548.892 100.991 548.911 100.903 548.951 100.844 c
+548.988 100.785 549.058 100.756 549.158 100.756 c
+549.216 100.756 549.278 100.763 549.349 100.785 c
+549.349 100.373 l
+549.231 100.336 549.117 100.315 549.01 100.315 c
+548.811 100.315 548.661 100.381 548.555 100.521 c
+548.456 100.656 548.408 100.851 548.408 101.109 c
+548.408 102.96 l
+547.951 102.96 l
+547.951 103.357 l
+548.408 103.357 l
+548.408 104.078 l
+h
+f
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 550.895 108.4612 cm
+0 0 m
+5.56 4.844 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 554.8461 111.9046 cm
+0 0 m
+-0.104 -1.507 l
+2.009 1.75 l
+-1.504 0.103 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 554.8461 111.9047 cm
+0 0 m
+-0.104 -1.507 l
+2.009 1.75 l
+-1.504 0.103 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 684.3624 112.9743 cm
+0 0 m
+-1.058 0 l
+-1.058 -3.572 l
+-1.558 -3.572 l
+-1.558 0 l
+-2.616 0 l
+-2.616 0.441 l
+0 0.441 l
+h
+0.823 -0.897 m
+1.008 -0.654 1.243 -0.53 1.529 -0.53 c
+2.058 -0.53 2.326 -0.882 2.338 -1.588 c
+2.338 -3.572 l
+1.852 -3.572 l
+1.852 -1.617 l
+1.852 -1.382 1.812 -1.216 1.735 -1.118 c
+1.654 -1.022 1.536 -0.97 1.382 -0.97 c
+1.264 -0.97 1.154 -1.01 1.058 -1.088 c
+0.96 -1.168 0.882 -1.276 0.823 -1.411 c
+0.823 -3.572 l
+0.339 -3.572 l
+0.339 0.661 l
+0.823 0.661 l
+h
+3.406 -3.572 -0.5 2.984 re
+3.437 0.206 m
+3.437 0.118 3.41 0.044 3.362 -0.015 c
+3.323 -0.066 3.252 -0.088 3.157 -0.088 c
+3.069 -0.088 2.999 -0.066 2.951 -0.015 c
+2.911 0.044 2.892 0.11 2.892 0.191 c
+2.892 0.279 2.911 0.353 2.951 0.411 c
+2.999 0.47 3.069 0.5 3.157 0.5 c
+3.252 0.5 3.323 0.47 3.362 0.411 c
+3.41 0.353 3.437 0.283 3.437 0.206 c
+5.523 -2.807 m
+5.523 -2.701 5.483 -2.612 5.405 -2.543 c
+5.325 -2.466 5.174 -2.377 4.95 -2.278 c
+4.686 -2.172 4.499 -2.08 4.391 -1.999 c
+4.281 -1.922 4.204 -1.834 4.156 -1.735 c
+4.105 -1.639 4.083 -1.521 4.083 -1.382 c
+4.083 -1.139 4.171 -0.937 4.347 -0.779 c
+4.524 -0.613 4.748 -0.53 5.024 -0.53 c
+5.318 -0.53 5.553 -0.617 5.729 -0.794 c
+5.906 -0.962 5.993 -1.176 5.993 -1.44 c
+5.509 -1.44 l
+5.509 -1.305 5.457 -1.191 5.361 -1.103 c
+5.274 -1.007 5.16 -0.956 5.024 -0.956 c
+4.877 -0.956 4.763 -0.996 4.686 -1.073 c
+4.605 -1.143 4.568 -1.242 4.568 -1.367 c
+4.568 -1.467 4.597 -1.544 4.656 -1.602 c
+4.715 -1.661 4.854 -1.742 5.083 -1.837 c
+5.442 -1.984 5.689 -2.128 5.818 -2.263 c
+5.953 -2.392 6.024 -2.565 6.024 -2.778 c
+6.024 -3.036 5.928 -3.241 5.744 -3.395 c
+5.567 -3.553 5.332 -3.63 5.039 -3.63 c
+4.723 -3.63 4.469 -3.543 4.274 -3.366 c
+4.087 -3.183 3.994 -2.951 3.994 -2.675 c
+4.48 -2.675 l
+4.487 -2.844 4.538 -2.977 4.627 -3.072 c
+4.723 -3.16 4.862 -3.204 5.039 -3.204 c
+5.193 -3.204 5.31 -3.171 5.391 -3.102 c
+5.479 -3.036 5.523 -2.936 5.523 -2.807 c
+8.214 -3.572 -0.5 2.984 re
+8.243 0.206 m
+8.243 0.118 8.217 0.044 8.169 -0.015 c
+8.129 -0.066 8.059 -0.088 7.963 -0.088 c
+7.875 -0.088 7.805 -0.066 7.757 -0.015 c
+7.717 0.044 7.699 0.11 7.699 0.191 c
+7.699 0.279 7.717 0.353 7.757 0.411 c
+7.805 0.47 7.875 0.5 7.963 0.5 c
+8.059 0.5 8.129 0.47 8.169 0.411 c
+8.217 0.353 8.243 0.283 8.243 0.206 c
+10.326 -2.807 m
+10.326 -2.701 10.286 -2.612 10.209 -2.543 c
+10.128 -2.466 9.978 -2.377 9.753 -2.278 c
+9.488 -2.172 9.301 -2.08 9.195 -1.999 c
+9.084 -1.922 9.007 -1.834 8.96 -1.735 c
+8.908 -1.639 8.886 -1.521 8.886 -1.382 c
+8.886 -1.139 8.974 -0.937 9.151 -0.779 c
+9.327 -0.613 9.551 -0.53 9.827 -0.53 c
+10.12 -0.53 10.356 -0.617 10.533 -0.794 c
+10.708 -0.962 10.797 -1.176 10.797 -1.44 c
+10.311 -1.44 l
+10.311 -1.305 10.26 -1.191 10.165 -1.103 c
+10.076 -1.007 9.962 -0.956 9.827 -0.956 c
+9.679 -0.956 9.565 -0.996 9.488 -1.073 c
+9.407 -1.143 9.371 -1.242 9.371 -1.367 c
+9.371 -1.467 9.4 -1.544 9.459 -1.602 c
+9.517 -1.661 9.658 -1.742 9.885 -1.837 c
+10.246 -1.984 10.492 -2.128 10.62 -2.263 c
+10.756 -2.392 10.826 -2.565 10.826 -2.778 c
+10.826 -3.036 10.73 -3.241 10.547 -3.395 c
+10.371 -3.553 10.136 -3.63 9.841 -3.63 c
+9.525 -3.63 9.272 -3.543 9.077 -3.366 c
+8.889 -3.183 8.798 -2.951 8.798 -2.675 c
+9.282 -2.675 l
+9.29 -2.844 9.342 -2.977 9.43 -3.072 c
+9.525 -3.16 9.665 -3.204 9.841 -3.204 c
+9.995 -3.204 10.113 -3.171 10.194 -3.102 c
+10.282 -3.036 10.326 -2.936 10.326 -2.807 c
+14.016 -3.572 m
+13.986 -3.506 13.964 -3.395 13.957 -3.248 c
+13.781 -3.506 13.56 -3.63 13.295 -3.63 c
+13.02 -3.63 12.803 -3.557 12.649 -3.41 c
+12.502 -3.256 12.428 -3.04 12.428 -2.764 c
+12.428 -2.462 12.532 -2.219 12.737 -2.043 c
+12.943 -1.86 13.226 -1.764 13.59 -1.764 c
+13.943 -1.764 l
+13.943 -1.44 l
+13.943 -1.264 13.902 -1.143 13.825 -1.073 c
+13.744 -0.996 13.627 -0.956 13.472 -0.956 c
+13.325 -0.956 13.2 -1 13.104 -1.088 c
+13.016 -1.176 12.972 -1.286 12.972 -1.411 c
+12.487 -1.411 l
+12.487 -1.264 12.532 -1.124 12.619 -0.985 c
+12.708 -0.838 12.825 -0.727 12.972 -0.647 c
+13.126 -0.57 13.299 -0.53 13.486 -0.53 c
+13.799 -0.53 14.034 -0.61 14.192 -0.765 c
+14.346 -0.912 14.427 -1.132 14.427 -1.426 c
+14.427 -2.925 l
+14.435 -3.16 14.471 -3.362 14.531 -3.528 c
+14.531 -3.572 l
+h
+13.369 -3.19 m
+13.486 -3.19 13.596 -3.157 13.708 -3.087 c
+13.814 -3.021 13.891 -2.936 13.943 -2.837 c
+13.943 -2.132 l
+13.677 -2.132 l
+13.442 -2.132 13.255 -2.183 13.12 -2.278 c
+12.991 -2.377 12.928 -2.521 12.928 -2.705 c
+12.928 -2.874 12.958 -2.995 13.016 -3.072 c
+13.082 -3.153 13.2 -3.19 13.369 -3.19 c
+16.669 -0.588 m
+16.684 -0.867 l
+16.86 -0.643 17.099 -0.53 17.404 -0.53 c
+17.735 -0.53 17.966 -0.676 18.095 -0.97 c
+18.279 -0.676 18.539 -0.53 18.874 -0.53 c
+19.432 -0.53 19.715 -0.875 19.726 -1.558 c
+19.726 -3.572 l
+19.241 -3.572 l
+19.241 -1.602 l
+19.241 -1.389 19.201 -1.231 19.123 -1.132 c
+19.042 -1.025 18.911 -0.97 18.726 -0.97 c
+18.58 -0.97 18.462 -1.029 18.375 -1.147 c
+18.286 -1.257 18.231 -1.396 18.213 -1.573 c
+18.213 -3.572 l
+17.727 -3.572 l
+17.727 -1.588 l
+17.716 -1.176 17.544 -0.97 17.213 -0.97 c
+16.966 -0.97 16.794 -1.095 16.698 -1.338 c
+16.698 -3.572 l
+16.214 -3.572 l
+16.214 -0.588 l
+h
+21.233 -3.63 m
+20.858 -3.63 20.575 -3.524 20.38 -3.308 c
+20.182 -3.083 20.087 -2.756 20.087 -2.323 c
+20.087 -1.955 l
+20.087 -1.514 20.178 -1.168 20.366 -0.912 c
+20.561 -0.658 20.836 -0.53 21.189 -0.53 c
+21.531 -0.53 21.784 -0.643 21.953 -0.867 c
+22.13 -1.095 22.221 -1.44 22.232 -1.911 c
+22.232 -2.219 l
+20.571 -2.219 l
+20.571 -2.294 l
+20.571 -2.616 20.63 -2.851 20.748 -2.999 c
+20.866 -3.138 21.035 -3.204 21.263 -3.204 c
+21.409 -3.204 21.534 -3.183 21.645 -3.131 c
+21.751 -3.072 21.854 -2.984 21.953 -2.866 c
+22.203 -3.175 l
+21.997 -3.48 21.674 -3.63 21.233 -3.63 c
+21.189 -0.956 m
+20.983 -0.956 20.829 -1.025 20.733 -1.161 c
+20.634 -1.301 20.579 -1.514 20.571 -1.808 c
+21.747 -1.808 l
+21.747 -1.735 l
+21.725 -1.463 21.674 -1.264 21.586 -1.147 c
+21.498 -1.022 21.365 -0.956 21.189 -0.956 c
+23.879 -1.043 m
+23.809 -1.037 23.736 -1.029 23.659 -1.029 c
+23.401 -1.029 23.225 -1.168 23.129 -1.44 c
+23.129 -3.572 l
+22.645 -3.572 l
+22.645 -0.588 l
+23.115 -0.588 l
+23.129 -0.897 l
+23.254 -0.654 23.438 -0.53 23.673 -0.53 c
+23.75 -0.53 23.813 -0.544 23.864 -0.573 c
+h
+24.096 -1.941 m
+24.096 -1.481 24.176 -1.132 24.345 -0.897 c
+24.522 -0.654 24.772 -0.53 25.095 -0.53 c
+25.396 -0.53 25.628 -0.661 25.786 -0.926 c
+25.816 -0.588 l
+26.256 -0.588 l
+26.256 -3.601 l
+26.256 -3.973 26.157 -4.255 25.963 -4.453 c
+25.775 -4.648 25.514 -4.748 25.184 -4.748 c
+25.037 -4.748 24.868 -4.707 24.684 -4.63 c
+24.496 -4.561 24.36 -4.472 24.272 -4.366 c
+24.463 -4.027 l
+24.669 -4.233 24.893 -4.336 25.139 -4.336 c
+25.54 -4.336 25.745 -4.108 25.756 -3.66 c
+25.756 -3.278 l
+25.598 -3.513 25.378 -3.63 25.095 -3.63 c
+24.779 -3.63 24.536 -3.513 24.36 -3.278 c
+24.191 -3.042 24.103 -2.712 24.096 -2.278 c
+h
+24.581 -2.219 m
+24.581 -2.554 24.629 -2.801 24.727 -2.955 c
+24.823 -3.113 24.985 -3.19 25.213 -3.19 c
+25.456 -3.19 25.639 -3.069 25.756 -2.822 c
+25.756 -1.338 l
+25.639 -1.095 25.463 -0.97 25.228 -0.97 c
+24.999 -0.97 24.838 -1.051 24.742 -1.205 c
+24.644 -1.363 24.588 -1.602 24.581 -1.926 c
+h
+27.848 -3.63 m
+27.472 -3.63 27.19 -3.524 26.995 -3.308 c
+26.797 -3.083 26.701 -2.756 26.701 -2.323 c
+26.701 -1.955 l
+26.701 -1.514 26.793 -1.168 26.98 -0.912 c
+27.175 -0.658 27.451 -0.53 27.804 -0.53 c
+28.145 -0.53 28.399 -0.643 28.567 -0.867 c
+28.744 -1.095 28.836 -1.44 28.847 -1.911 c
+28.847 -2.219 l
+27.186 -2.219 l
+27.186 -2.294 l
+27.186 -2.616 27.245 -2.851 27.362 -2.999 c
+27.48 -3.138 27.649 -3.204 27.877 -3.204 c
+28.024 -3.204 28.149 -3.183 28.259 -3.131 c
+28.366 -3.072 28.469 -2.984 28.567 -2.866 c
+28.818 -3.175 l
+28.612 -3.48 28.289 -3.63 27.848 -3.63 c
+27.804 -0.956 m
+27.598 -0.956 27.443 -1.025 27.348 -1.161 c
+27.248 -1.301 27.193 -1.514 27.186 -1.808 c
+28.362 -1.808 l
+28.362 -1.735 l
+28.34 -1.463 28.289 -1.264 28.201 -1.147 c
+28.112 -1.022 27.979 -0.956 27.804 -0.956 c
+31.493 -3.204 m
+31.659 -3.204 31.79 -3.157 31.89 -3.057 c
+31.985 -2.961 32.041 -2.818 32.052 -2.631 c
+32.507 -2.631 l
+32.496 -2.918 32.393 -3.157 32.199 -3.352 c
+32.012 -3.539 31.776 -3.63 31.493 -3.63 c
+31.129 -3.63 30.85 -3.513 30.655 -3.278 c
+30.456 -3.042 30.361 -2.697 30.361 -2.234 c
+30.361 -1.911 l
+30.361 -1.463 30.453 -1.118 30.641 -0.882 c
+30.836 -0.647 31.118 -0.53 31.493 -0.53 c
+31.794 -0.53 32.037 -0.628 32.213 -0.823 c
+32.397 -1.022 32.496 -1.286 32.507 -1.617 c
+32.052 -1.617 l
+32.029 -1.393 31.971 -1.228 31.875 -1.118 c
+31.787 -1.01 31.659 -0.956 31.493 -0.956 c
+31.276 -0.956 31.114 -1.029 31.008 -1.176 c
+30.909 -1.315 30.853 -1.544 30.846 -1.866 c
+30.846 -2.249 l
+30.846 -2.602 30.894 -2.851 30.994 -2.999 c
+31.1 -3.138 31.266 -3.204 31.493 -3.204 c
+32.801 -1.941 m
+32.801 -1.511 32.904 -1.168 33.11 -0.912 c
+33.323 -0.658 33.602 -0.53 33.948 -0.53 c
+34.289 -0.53 34.565 -0.658 34.771 -0.912 c
+34.984 -1.158 35.098 -1.492 35.109 -1.911 c
+35.109 -2.219 l
+35.109 -2.653 34.998 -2.995 34.786 -3.248 c
+34.58 -3.506 34.3 -3.63 33.948 -3.63 c
+33.602 -3.63 33.33 -3.51 33.124 -3.263 c
+32.918 -3.021 32.808 -2.687 32.801 -2.263 c
+h
+33.286 -2.219 m
+33.286 -2.535 33.345 -2.778 33.463 -2.955 c
+33.587 -3.123 33.749 -3.204 33.948 -3.204 c
+34.377 -3.204 34.602 -2.896 34.624 -2.278 c
+34.624 -1.941 l
+34.624 -1.639 34.558 -1.396 34.433 -1.22 c
+34.315 -1.043 34.154 -0.956 33.948 -0.956 c
+33.749 -0.956 33.587 -1.043 33.463 -1.22 c
+33.345 -1.396 33.286 -1.639 33.286 -1.941 c
+h
+36.002 -0.588 m
+36.016 -0.867 l
+36.193 -0.643 36.432 -0.53 36.737 -0.53 c
+37.068 -0.53 37.299 -0.676 37.427 -0.97 c
+37.612 -0.676 37.872 -0.53 38.207 -0.53 c
+38.765 -0.53 39.048 -0.875 39.06 -1.558 c
+39.06 -3.572 l
+38.574 -3.572 l
+38.574 -1.602 l
+38.574 -1.389 38.534 -1.231 38.456 -1.132 c
+38.376 -1.025 38.244 -0.97 38.06 -0.97 c
+37.913 -0.97 37.795 -1.029 37.707 -1.147 c
+37.619 -1.257 37.564 -1.396 37.545 -1.573 c
+37.545 -3.572 l
+37.06 -3.572 l
+37.06 -1.588 l
+37.049 -1.176 36.876 -0.97 36.546 -0.97 c
+36.299 -0.97 36.127 -1.095 36.031 -1.338 c
+36.031 -3.572 l
+35.546 -3.572 l
+35.546 -0.588 l
+h
+39.964 -0.588 m
+39.978 -0.867 l
+40.155 -0.643 40.393 -0.53 40.698 -0.53 c
+41.029 -0.53 41.261 -0.676 41.389 -0.97 c
+41.573 -0.676 41.834 -0.53 42.168 -0.53 c
+42.727 -0.53 43.01 -0.875 43.021 -1.558 c
+43.021 -3.572 l
+42.536 -3.572 l
+42.536 -1.602 l
+42.536 -1.389 42.495 -1.231 42.418 -1.132 c
+42.337 -1.025 42.205 -0.97 42.021 -0.97 c
+41.874 -0.97 41.757 -1.029 41.668 -1.147 c
+41.58 -1.257 41.525 -1.396 41.506 -1.573 c
+41.506 -3.572 l
+41.022 -3.572 l
+41.022 -1.588 l
+41.011 -1.176 40.837 -0.97 40.507 -0.97 c
+40.261 -0.97 40.089 -1.095 39.993 -1.338 c
+39.993 -3.572 l
+39.507 -3.572 l
+39.507 -0.588 l
+h
+43.998 -3.572 -0.5 2.984 re
+44.028 0.206 m
+44.028 0.118 44.002 0.044 43.954 -0.015 c
+43.914 -0.066 43.844 -0.088 43.748 -0.088 c
+43.66 -0.088 43.59 -0.066 43.542 -0.015 c
+43.502 0.044 43.484 0.11 43.484 0.191 c
+43.484 0.279 43.502 0.353 43.542 0.411 c
+43.59 0.47 43.66 0.5 43.748 0.5 c
+43.844 0.5 43.914 0.47 43.954 0.411 c
+44.002 0.353 44.028 0.283 44.028 0.206 c
+45.306 0.133 m
+45.306 -0.588 l
+45.762 -0.588 l
+45.762 -0.985 l
+45.306 -0.985 l
+45.306 -2.837 l
+45.306 -2.955 45.325 -3.042 45.365 -3.102 c
+45.402 -3.16 45.472 -3.19 45.571 -3.19 c
+45.63 -3.19 45.693 -3.183 45.762 -3.16 c
+45.762 -3.572 l
+45.645 -3.609 45.531 -3.63 45.424 -3.63 c
+45.225 -3.63 45.075 -3.564 44.968 -3.425 c
+44.87 -3.289 44.822 -3.094 44.822 -2.837 c
+44.822 -0.985 l
+44.365 -0.985 l
+44.365 -0.588 l
+44.822 -0.588 l
+44.822 0.133 l
+h
+46.229 -4.366 m
+45.934 -4.175 l
+46.111 -3.929 46.203 -3.678 46.214 -3.425 c
+46.214 -2.969 l
+46.713 -2.969 l
+46.713 -3.366 l
+46.713 -3.553 46.663 -3.738 46.567 -3.925 c
+46.478 -4.108 46.364 -4.255 46.229 -4.366 c
+f
+Q
+693.76 102.817 -0.501 2.984 re
+693.789 106.595 m
+693.789 106.507 693.762 106.433 693.715 106.374 c
+693.675 106.323 693.604 106.301 693.509 106.301 c
+693.421 106.301 693.351 106.323 693.303 106.374 c
+693.263 106.433 693.245 106.499 693.245 106.58 c
+693.245 106.669 693.263 106.742 693.303 106.8 c
+693.351 106.86 693.421 106.889 693.509 106.889 c
+693.604 106.889 693.675 106.86 693.715 106.8 c
+693.762 106.742 693.789 106.672 693.789 106.595 c
+695.063 106.522 m
+695.063 105.801 l
+695.52 105.801 l
+695.52 105.404 l
+695.063 105.404 l
+695.063 103.552 l
+695.063 103.434 695.082 103.347 695.123 103.288 c
+695.159 103.229 695.229 103.199 695.328 103.199 c
+695.387 103.199 695.449 103.207 695.52 103.229 c
+695.52 102.817 l
+695.402 102.78 695.288 102.759 695.181 102.759 c
+694.982 102.759 694.832 102.825 694.726 102.964 c
+694.626 103.1 694.579 103.295 694.579 103.552 c
+694.579 105.404 l
+694.123 105.404 l
+694.123 105.801 l
+694.579 105.801 l
+694.579 106.522 l
+h
+697.677 105.493 m
+697.86 105.735 698.095 105.86 698.382 105.86 c
+698.911 105.86 699.179 105.507 699.19 104.801 c
+699.19 102.817 l
+698.705 102.817 l
+698.705 104.772 l
+698.705 105.007 698.664 105.173 698.587 105.272 c
+698.506 105.368 698.389 105.419 698.235 105.419 c
+698.117 105.419 698.007 105.379 697.912 105.302 c
+697.812 105.221 697.735 105.114 697.677 104.978 c
+697.677 102.817 l
+697.191 102.817 l
+697.191 107.051 l
+697.677 107.051 l
+h
+701.259 102.817 m
+701.23 102.883 701.207 102.994 701.201 103.141 c
+701.024 102.883 700.804 102.759 700.539 102.759 c
+700.264 102.759 700.046 102.832 699.892 102.979 c
+699.745 103.133 699.672 103.35 699.672 103.625 c
+699.672 103.927 699.775 104.17 699.981 104.346 c
+700.187 104.529 700.469 104.625 700.833 104.625 c
+701.186 104.625 l
+701.186 104.949 l
+701.186 105.125 701.145 105.246 701.068 105.316 c
+700.987 105.393 700.87 105.433 700.715 105.433 c
+700.569 105.433 700.443 105.389 700.347 105.302 c
+700.26 105.213 700.216 105.103 700.216 104.978 c
+699.73 104.978 l
+699.73 105.125 699.775 105.265 699.863 105.404 c
+699.951 105.551 700.069 105.662 700.216 105.742 c
+700.37 105.819 700.542 105.86 700.73 105.86 c
+701.043 105.86 701.278 105.78 701.436 105.624 c
+701.59 105.478 701.671 105.257 701.671 104.963 c
+701.671 103.464 l
+701.678 103.229 701.715 103.027 701.774 102.861 c
+701.774 102.817 l
+h
+700.613 103.199 m
+700.73 103.199 700.84 103.232 700.951 103.303 c
+701.057 103.368 701.134 103.453 701.186 103.552 c
+701.186 104.257 l
+700.921 104.257 l
+700.686 104.257 700.499 104.207 700.363 104.111 c
+700.234 104.012 700.172 103.868 700.172 103.685 c
+700.172 103.515 700.201 103.394 700.26 103.317 c
+700.326 103.236 700.443 103.199 700.613 103.199 c
+703.692 103.582 m
+703.692 103.688 703.651 103.777 703.574 103.846 c
+703.493 103.923 703.343 104.012 703.119 104.111 c
+702.854 104.217 702.666 104.309 702.56 104.39 c
+702.45 104.467 702.373 104.556 702.325 104.654 c
+702.273 104.75 702.252 104.868 702.252 105.007 c
+702.252 105.25 702.34 105.452 702.516 105.61 c
+702.693 105.776 702.917 105.86 703.192 105.86 c
+703.486 105.86 703.721 105.772 703.898 105.595 c
+704.074 105.427 704.162 105.213 704.162 104.949 c
+703.678 104.949 l
+703.678 105.084 703.626 105.198 703.53 105.287 c
+703.443 105.383 703.328 105.433 703.192 105.433 c
+703.046 105.433 702.932 105.393 702.854 105.316 c
+702.774 105.246 702.737 105.147 702.737 105.022 c
+702.737 104.923 702.766 104.845 702.824 104.787 c
+702.884 104.728 703.023 104.647 703.251 104.552 c
+703.611 104.405 703.857 104.261 703.986 104.126 c
+704.122 103.997 704.191 103.824 704.191 103.611 c
+704.191 103.353 704.096 103.148 703.913 102.994 c
+703.736 102.836 703.501 102.759 703.207 102.759 c
+702.891 102.759 702.637 102.846 702.443 103.023 c
+702.255 103.207 702.163 103.438 702.163 103.714 c
+702.648 103.714 l
+702.656 103.545 702.707 103.413 702.795 103.317 c
+702.891 103.229 703.03 103.185 703.207 103.185 c
+703.362 103.185 703.479 103.218 703.559 103.288 c
+703.648 103.353 703.692 103.453 703.692 103.582 c
+706.485 106.522 m
+706.485 105.801 l
+706.94 105.801 l
+706.94 105.404 l
+706.485 105.404 l
+706.485 103.552 l
+706.485 103.434 706.503 103.347 706.543 103.288 c
+706.58 103.229 706.65 103.199 706.749 103.199 c
+706.808 103.199 706.871 103.207 706.94 103.229 c
+706.94 102.817 l
+706.823 102.78 706.709 102.759 706.602 102.759 c
+706.404 102.759 706.253 102.825 706.146 102.964 c
+706.047 103.1 705.999 103.295 705.999 103.552 c
+705.999 105.404 l
+705.544 105.404 l
+705.544 105.801 l
+705.999 105.801 l
+705.999 106.522 l
+h
+709.627 103.669 m
+710.082 105.801 l
+710.568 105.801 l
+709.832 102.817 l
+709.45 102.817 l
+708.862 104.949 l
+708.289 102.817 l
+707.907 102.817 l
+707.172 105.801 l
+707.657 105.801 l
+708.127 103.729 l
+708.671 105.801 l
+709.053 105.801 l
+h
+710.733 104.448 m
+710.733 104.878 710.836 105.221 711.042 105.478 c
+711.254 105.732 711.534 105.86 711.879 105.86 c
+712.221 105.86 712.496 105.732 712.702 105.478 c
+712.916 105.231 713.03 104.897 713.041 104.479 c
+713.041 104.17 l
+713.041 103.736 712.93 103.394 712.717 103.141 c
+712.511 102.883 712.232 102.759 711.879 102.759 c
+711.534 102.759 711.262 102.879 711.056 103.126 c
+710.85 103.368 710.74 103.702 710.733 104.126 c
+h
+711.218 104.17 m
+711.218 103.854 711.277 103.611 711.395 103.434 c
+711.519 103.266 711.681 103.185 711.879 103.185 c
+712.309 103.185 712.534 103.494 712.555 104.111 c
+712.555 104.448 l
+712.555 104.75 712.49 104.993 712.365 105.169 c
+712.247 105.346 712.085 105.433 711.879 105.433 c
+711.681 105.433 711.519 105.346 711.395 105.169 c
+711.277 104.993 711.218 104.75 711.218 104.448 c
+h
+716.829 104.17 m
+716.829 103.7 716.745 103.347 716.579 103.112 c
+716.411 102.876 716.172 102.759 715.859 102.759 c
+715.554 102.759 715.322 102.869 715.168 103.097 c
+715.168 101.67 l
+714.683 101.67 l
+714.683 105.801 l
+715.124 105.801 l
+715.154 105.463 l
+715.308 105.728 715.539 105.86 715.844 105.86 c
+716.175 105.86 716.421 105.742 716.579 105.507 c
+716.745 105.279 716.829 104.941 716.829 104.493 c
+h
+716.344 104.448 m
+716.344 104.78 716.289 105.026 716.182 105.184 c
+716.083 105.338 715.921 105.419 715.698 105.419 c
+715.462 105.419 715.285 105.302 715.168 105.066 c
+715.168 103.523 l
+715.285 103.295 715.466 103.185 715.712 103.185 c
+715.925 103.185 716.083 103.262 716.182 103.42 c
+716.289 103.574 716.344 103.817 716.344 104.14 c
+h
+718.813 102.817 m
+718.784 102.883 718.762 102.994 718.755 103.141 c
+718.578 102.883 718.358 102.759 718.094 102.759 c
+717.818 102.759 717.601 102.832 717.446 102.979 c
+717.3 103.133 717.226 103.35 717.226 103.625 c
+717.226 103.927 717.329 104.17 717.535 104.346 c
+717.741 104.529 718.024 104.625 718.387 104.625 c
+718.74 104.625 l
+718.74 104.949 l
+718.74 105.125 718.699 105.246 718.622 105.316 c
+718.541 105.393 718.424 105.433 718.269 105.433 c
+718.123 105.433 717.998 105.389 717.903 105.302 c
+717.814 105.213 717.77 105.103 717.77 104.978 c
+717.285 104.978 l
+717.285 105.125 717.329 105.265 717.417 105.404 c
+717.506 105.551 717.623 105.662 717.77 105.742 c
+717.924 105.819 718.097 105.86 718.285 105.86 c
+718.597 105.86 718.832 105.78 718.99 105.624 c
+719.144 105.478 719.225 105.257 719.225 104.963 c
+719.225 103.464 l
+719.233 103.229 719.27 103.027 719.328 102.861 c
+719.328 102.817 l
+h
+718.167 103.199 m
+718.285 103.199 718.395 103.232 718.505 103.303 c
+718.611 103.368 718.689 103.453 718.74 103.552 c
+718.74 104.257 l
+718.476 104.257 l
+718.24 104.257 718.053 104.207 717.917 104.111 c
+717.789 104.012 717.726 103.868 717.726 103.685 c
+717.726 103.515 717.755 103.394 717.814 103.317 c
+717.88 103.236 717.998 103.199 718.167 103.199 c
+721.041 105.346 m
+720.97 105.352 720.897 105.36 720.82 105.36 c
+720.563 105.36 720.386 105.221 720.291 104.949 c
+720.291 102.817 l
+719.806 102.817 l
+719.806 105.801 l
+720.276 105.801 l
+720.291 105.493 l
+720.415 105.735 720.6 105.86 720.835 105.86 c
+720.912 105.86 720.974 105.845 721.026 105.816 c
+h
+722.418 102.759 m
+722.044 102.759 721.761 102.865 721.566 103.082 c
+721.367 103.306 721.272 103.633 721.272 104.066 c
+721.272 104.434 l
+721.272 104.876 721.364 105.221 721.552 105.478 c
+721.747 105.732 722.022 105.86 722.374 105.86 c
+722.716 105.86 722.969 105.746 723.139 105.522 c
+723.315 105.294 723.407 104.949 723.418 104.479 c
+723.418 104.17 l
+721.757 104.17 l
+721.757 104.096 l
+721.757 103.773 721.816 103.538 721.934 103.39 c
+722.051 103.251 722.22 103.185 722.448 103.185 c
+722.595 103.185 722.72 103.207 722.83 103.258 c
+722.936 103.317 723.04 103.405 723.139 103.523 c
+723.389 103.214 l
+723.183 102.909 722.859 102.759 722.418 102.759 c
+722.374 105.433 m
+722.169 105.433 722.015 105.364 721.919 105.228 c
+721.82 105.088 721.764 104.876 721.757 104.581 c
+722.933 104.581 l
+722.933 104.654 l
+722.911 104.926 722.859 105.125 722.771 105.242 c
+722.683 105.368 722.551 105.433 722.374 105.433 c
+724.286 105.801 m
+724.3 105.478 l
+724.484 105.732 724.726 105.86 725.02 105.86 c
+725.55 105.86 725.818 105.507 725.828 104.801 c
+725.828 102.817 l
+725.344 102.817 l
+725.344 104.772 l
+725.344 105.007 725.303 105.173 725.226 105.272 c
+725.145 105.368 725.028 105.419 724.873 105.419 c
+724.756 105.419 724.646 105.379 724.55 105.302 c
+724.451 105.221 724.374 105.114 724.315 104.978 c
+724.315 102.817 l
+723.829 102.817 l
+723.829 105.801 l
+h
+727.012 106.522 m
+727.012 105.801 l
+727.468 105.801 l
+727.468 105.404 l
+727.012 105.404 l
+727.012 103.552 l
+727.012 103.434 727.031 103.347 727.071 103.288 c
+727.108 103.229 727.177 103.199 727.276 103.199 c
+727.335 103.199 727.397 103.207 727.468 103.229 c
+727.468 102.817 l
+727.35 102.78 727.236 102.759 727.129 102.759 c
+726.931 102.759 726.78 102.825 726.674 102.964 c
+726.574 103.1 726.527 103.295 726.527 103.552 c
+726.527 105.404 l
+726.071 105.404 l
+726.071 105.801 l
+726.527 105.801 l
+726.527 106.522 l
+h
+729.375 103.582 m
+729.375 103.688 729.334 103.777 729.257 103.846 c
+729.176 103.923 729.026 104.012 728.802 104.111 c
+728.537 104.217 728.35 104.309 728.243 104.39 c
+728.133 104.467 728.056 104.556 728.008 104.654 c
+727.956 104.75 727.935 104.868 727.935 105.007 c
+727.935 105.25 728.022 105.452 728.199 105.61 c
+728.375 105.776 728.6 105.86 728.875 105.86 c
+729.169 105.86 729.404 105.772 729.581 105.595 c
+729.757 105.427 729.845 105.213 729.845 104.949 c
+729.36 104.949 l
+729.36 105.084 729.309 105.198 729.213 105.287 c
+729.125 105.383 729.011 105.433 728.875 105.433 c
+728.728 105.433 728.614 105.393 728.537 105.316 c
+728.456 105.246 728.419 105.147 728.419 105.022 c
+728.419 104.923 728.449 104.845 728.508 104.787 c
+728.567 104.728 728.706 104.647 728.934 104.552 c
+729.294 104.405 729.541 104.261 729.668 104.126 c
+729.805 103.997 729.874 103.824 729.874 103.611 c
+729.874 103.353 729.779 103.148 729.595 102.994 c
+729.419 102.836 729.184 102.759 728.889 102.759 c
+728.573 102.759 728.32 102.846 728.126 103.023 c
+727.938 103.207 727.846 103.438 727.846 103.714 c
+728.332 103.714 l
+728.338 103.545 728.39 103.413 728.478 103.317 c
+728.573 103.229 728.714 103.185 728.889 103.185 c
+729.044 103.185 729.161 103.218 729.242 103.288 c
+729.331 103.353 729.375 103.453 729.375 103.582 c
+730.878 103.949 m
+730.437 103.949 l
+730.408 106.83 l
+730.922 106.83 l
+h
+730.672 103.361 m
+730.767 103.361 730.838 103.332 730.878 103.272 c
+730.925 103.222 730.952 103.155 730.952 103.067 c
+730.952 102.987 730.925 102.92 730.878 102.861 c
+730.838 102.813 730.767 102.788 730.672 102.788 c
+730.584 102.788 730.514 102.813 730.466 102.861 c
+730.414 102.92 730.393 102.987 730.393 103.067 c
+730.393 103.155 730.414 103.222 730.466 103.272 c
+730.514 103.332 730.584 103.361 730.672 103.361 c
+f
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 734.152 110.909 cm
+0 0 m
+5.56 4.843 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 738.1018 114.3481 cm
+0 0 m
+-0.103 -1.502 l
+2.01 1.753 l
+-1.507 0.107 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 738.1018 114.3482 cm
+0 0 m
+-0.103 -1.502 l
+2.01 1.753 l
+-1.506 0.107 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 767.8644 128.2905 cm
+0 0 m
+-0.008 16.393 l
+6.46 16.393 l
+6.468 0 l
+3.227 -3.564 l
+h
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 772.5238 142.4204 cm
+0 0 m
+-0.279 -0.03 l
+-0.055 -0.206 0.059 -0.434 0.059 -0.721 c
+0.059 -1.014 -0.081 -1.224 -0.353 -1.353 c
+-0.081 -1.518 0.059 -1.756 0.059 -2.072 c
+0.059 -2.602 -0.305 -2.87 -1.029 -2.881 c
+-2.984 -2.881 l
+-2.984 -2.103 l
+-1.073 -2.103 l
+-0.908 -2.103 -0.79 -2.076 -0.721 -2.029 c
+-0.643 -1.977 -0.603 -1.9 -0.603 -1.794 c
+-0.603 -1.646 -0.69 -1.532 -0.867 -1.455 c
+-0.956 -1.47 l
+-2.984 -1.47 l
+-2.984 -0.691 l
+-1.087 -0.691 l
+-0.912 -0.691 -0.79 -0.665 -0.721 -0.618 c
+-0.643 -0.566 -0.603 -0.485 -0.603 -0.368 c
+-0.603 -0.239 -0.676 -0.136 -0.823 -0.059 c
+-2.984 -0.059 l
+-2.984 0.72 l
+0 0.72 l
+h
+-2.984 -4.601 m
+-2.936 -4.572 -2.859 -4.546 -2.749 -4.528 c
+-2.947 -4.388 -3.042 -4.204 -3.042 -3.969 c
+-3.042 -3.712 -2.961 -3.502 -2.793 -3.337 c
+-2.627 -3.179 -2.41 -3.102 -2.146 -3.102 c
+-1.833 -3.102 -1.594 -3.198 -1.426 -3.396 c
+-1.249 -3.591 -1.161 -3.873 -1.161 -4.248 c
+-1.161 -4.499 l
+-0.926 -4.499 l
+-0.79 -4.499 -0.698 -4.472 -0.646 -4.424 c
+-0.588 -4.374 -0.559 -4.307 -0.559 -4.219 c
+-0.559 -4.021 -0.673 -3.925 -0.896 -3.925 c
+-0.896 -3.146 l
+-0.625 -3.146 -0.397 -3.248 -0.22 -3.454 c
+-0.037 -3.66 0.059 -3.925 0.059 -4.248 c
+0.059 -4.572 -0.029 -4.821 -0.206 -4.998 c
+-0.374 -5.182 -0.613 -5.278 -0.926 -5.278 c
+-2.337 -5.278 l
+-2.595 -5.278 -2.793 -5.31 -2.94 -5.38 c
+-2.984 -5.38 l
+h
+-2.425 -4.131 m
+-2.425 -4.219 -2.41 -4.293 -2.381 -4.351 c
+-2.344 -4.418 -2.3 -4.469 -2.249 -4.499 c
+-1.631 -4.499 l
+-1.631 -4.307 l
+-1.631 -4.167 -1.675 -4.061 -1.764 -3.984 c
+-1.845 -3.913 -1.955 -3.881 -2.102 -3.881 c
+-2.319 -3.881 -2.425 -3.961 -2.425 -4.131 c
+-2.19 -6.978 m
+-2.124 -6.978 -2.057 -6.942 -1.999 -6.876 c
+-1.94 -6.817 -1.866 -6.677 -1.779 -6.464 c
+-1.643 -6.141 -1.506 -5.92 -1.367 -5.802 c
+-1.231 -5.685 -1.058 -5.627 -0.852 -5.627 c
+-0.588 -5.627 -0.374 -5.718 -0.206 -5.906 c
+-0.029 -6.089 0.059 -6.34 0.059 -6.655 c
+0.059 -6.978 -0.029 -7.236 -0.206 -7.435 c
+-0.374 -7.629 -0.599 -7.728 -0.881 -7.728 c
+-0.881 -6.949 l
+-0.64 -6.949 -0.515 -6.847 -0.515 -6.641 c
+-0.515 -6.56 -0.544 -6.494 -0.603 -6.435 c
+-0.654 -6.383 -0.721 -6.361 -0.808 -6.361 c
+-0.878 -6.361 -0.937 -6.39 -0.985 -6.45 c
+-1.036 -6.508 -1.11 -6.644 -1.205 -6.861 c
+-1.323 -7.184 -1.455 -7.408 -1.602 -7.537 c
+-1.741 -7.674 -1.926 -7.743 -2.161 -7.743 c
+-2.425 -7.743 -2.643 -7.64 -2.807 -7.435 c
+-2.965 -7.236 -3.042 -6.978 -3.042 -6.655 c
+-3.042 -6.427 -2.998 -6.232 -2.911 -6.067 c
+-2.822 -5.898 -2.705 -5.766 -2.558 -5.671 c
+-2.404 -5.582 -2.234 -5.538 -2.057 -5.538 c
+-2.057 -6.273 l
+-2.198 -6.28 -2.3 -6.317 -2.367 -6.376 c
+-2.436 -6.435 -2.469 -6.531 -2.469 -6.67 c
+-2.469 -6.876 -2.377 -6.978 -2.19 -6.978 c
+0.735 -8.967 m
+0 -8.967 l
+0 -9.363 l
+-0.588 -9.363 l
+-0.588 -8.967 l
+-2.072 -8.967 l
+-2.19 -8.967 -2.275 -8.981 -2.323 -9.01 c
+-2.363 -9.047 -2.381 -9.114 -2.381 -9.202 c
+-2.381 -9.279 -2.373 -9.342 -2.352 -9.393 c
+-2.969 -9.378 l
+-3.017 -9.249 -3.042 -9.106 -3.042 -8.952 c
+-3.042 -8.453 -2.756 -8.195 -2.175 -8.187 c
+-0.588 -8.187 l
+-0.588 -7.85 l
+0 -7.85 l
+0 -8.187 l
+0.735 -8.187 l
+h
+-3.042 -10.874 m
+-3.042 -10.469 -2.925 -10.157 -2.69 -9.933 c
+-2.447 -9.716 -2.102 -9.61 -1.661 -9.61 c
+-1.411 -9.61 l
+-0.941 -9.61 -0.58 -9.712 -0.324 -9.918 c
+-0.07 -10.124 0.059 -10.418 0.059 -10.801 c
+0.059 -11.171 -0.066 -11.451 -0.309 -11.638 c
+-0.544 -11.833 -0.896 -11.932 -1.367 -11.932 c
+-1.749 -11.932 l
+-1.749 -10.389 l
+-1.976 -10.396 -2.142 -10.44 -2.249 -10.521 c
+-2.348 -10.61 -2.396 -10.745 -2.396 -10.932 c
+-2.396 -11.186 -2.304 -11.403 -2.117 -11.58 c
+-2.602 -11.888 l
+-2.73 -11.789 -2.837 -11.645 -2.925 -11.462 c
+-3.002 -11.285 -3.042 -11.087 -3.042 -10.874 c
+-1.205 -10.389 m
+-1.205 -11.168 l
+-1.132 -11.168 l
+-0.956 -11.168 -0.823 -11.138 -0.735 -11.08 c
+-0.64 -11.021 -0.588 -10.922 -0.588 -10.786 c
+-0.588 -10.657 -0.64 -10.558 -0.735 -10.492 c
+-0.834 -10.433 -0.992 -10.396 -1.205 -10.389 c
+-0.75 -13.66 m
+-0.735 -13.409 l
+-0.735 -13.193 -0.831 -13.045 -1.014 -12.968 c
+-2.984 -12.968 l
+-2.984 -12.189 l
+0 -12.189 l
+0 -12.925 l
+-0.324 -12.954 l
+-0.07 -13.072 0.059 -13.237 0.059 -13.454 c
+0.059 -13.542 0.044 -13.615 0.015 -13.674 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 774.5925 108.726 cm
+0 0 m
+0.008 -14.188 l
+-6.464 -14.188 l
+-6.471 0 l
+-3.23 3.564 l
+h
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 769.4918 104.7137 cm
+0 0 m
+0 0.808 l
+1.721 0.808 l
+1.721 1.999 l
+0 1.999 l
+0 2.807 l
+4.013 2.807 l
+4.013 1.999 l
+2.396 1.999 l
+2.396 0.808 l
+4.013 0.808 l
+4.013 0 l
+h
+1.735 -2.496 m
+1.735 -1.231 l
+0.676 -1.231 l
+0.676 -2.731 l
+0 -2.731 l
+0 -0.423 l
+4.013 -0.423 l
+4.013 -2.716 l
+3.337 -2.716 l
+3.337 -1.231 l
+2.396 -1.231 l
+2.396 -2.496 l
+h
+0.823 -4.917 m
+0.823 -3.815 l
+0 -3.595 l
+0 -2.741 l
+4.013 -3.991 l
+4.013 -4.74 l
+0 -6.005 l
+0 -5.137 l
+h
+1.5 -3.991 m
+1.5 -4.74 l
+2.926 -4.373 l
+h
+0 -6.155 m
+4.013 -6.155 l
+4.013 -7.214 l
+4.013 -7.684 3.859 -8.056 3.558 -8.331 c
+3.263 -8.614 2.856 -8.757 2.338 -8.757 c
+1.69 -8.757 l
+1.162 -8.757 0.743 -8.617 0.441 -8.345 c
+0.148 -8.07 0 -7.688 0 -7.199 c
+h
+3.337 -6.964 m
+0.676 -6.964 l
+0.676 -7.214 l
+0.676 -7.479 0.743 -7.662 0.882 -7.772 c
+1.029 -7.89 1.272 -7.949 1.617 -7.949 c
+2.323 -7.949 l
+2.694 -7.949 2.955 -7.894 3.102 -7.787 c
+3.249 -7.688 3.326 -7.512 3.337 -7.258 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 733.9356 155.8438 cm
+0 0 m
+-1.058 0 l
+-1.058 -3.572 l
+-1.559 -3.572 l
+-1.559 0 l
+-2.617 0 l
+-2.617 0.441 l
+0 0.441 l
+h
+0.823 -0.897 m
+1.007 -0.654 1.242 -0.529 1.529 -0.529 c
+2.057 -0.529 2.326 -0.881 2.337 -1.587 c
+2.337 -3.572 l
+1.851 -3.572 l
+1.851 -1.616 l
+1.851 -1.381 1.811 -1.216 1.734 -1.117 c
+1.653 -1.022 1.535 -0.97 1.381 -0.97 c
+1.263 -0.97 1.153 -1.01 1.058 -1.087 c
+0.959 -1.168 0.882 -1.275 0.823 -1.411 c
+0.823 -3.572 l
+0.338 -3.572 l
+0.338 0.661 l
+0.823 0.661 l
+h
+3.41 -3.572 -0.5 2.984 re
+3.439 0.206 m
+3.439 0.118 3.413 0.044 3.366 -0.014 c
+3.326 -0.066 3.255 -0.087 3.16 -0.087 c
+3.072 -0.087 3.002 -0.066 2.954 -0.014 c
+2.913 0.044 2.896 0.11 2.896 0.191 c
+2.896 0.279 2.913 0.353 2.954 0.412 c
+3.002 0.47 3.072 0.5 3.16 0.5 c
+3.255 0.5 3.326 0.47 3.366 0.412 c
+3.413 0.353 3.439 0.283 3.439 0.206 c
+5.523 -2.807 m
+5.523 -2.701 5.483 -2.612 5.406 -2.543 c
+5.325 -2.466 5.174 -2.377 4.949 -2.278 c
+4.685 -2.171 4.498 -2.08 4.391 -1.999 c
+4.28 -1.922 4.203 -1.833 4.156 -1.734 c
+4.104 -1.639 4.082 -1.521 4.082 -1.381 c
+4.082 -1.139 4.17 -0.937 4.347 -0.779 c
+4.523 -0.613 4.747 -0.529 5.023 -0.529 c
+5.317 -0.529 5.552 -0.617 5.728 -0.793 c
+5.905 -0.962 5.994 -1.176 5.994 -1.44 c
+5.508 -1.44 l
+5.508 -1.304 5.456 -1.19 5.361 -1.103 c
+5.273 -1.007 5.159 -0.955 5.023 -0.955 c
+4.876 -0.955 4.762 -0.995 4.685 -1.072 c
+4.604 -1.143 4.567 -1.242 4.567 -1.367 c
+4.567 -1.466 4.596 -1.543 4.656 -1.602 c
+4.714 -1.66 4.854 -1.741 5.082 -1.837 c
+5.442 -1.984 5.688 -2.127 5.817 -2.263 c
+5.953 -2.392 6.023 -2.564 6.023 -2.778 c
+6.023 -3.035 5.927 -3.241 5.743 -3.395 c
+5.567 -3.553 5.332 -3.63 5.038 -3.63 c
+4.722 -3.63 4.469 -3.542 4.274 -3.366 c
+4.086 -3.183 3.994 -2.95 3.994 -2.675 c
+4.479 -2.675 l
+4.486 -2.844 4.538 -2.977 4.627 -3.072 c
+4.722 -3.16 4.862 -3.204 5.038 -3.204 c
+5.192 -3.204 5.31 -3.171 5.39 -3.102 c
+5.479 -3.035 5.523 -2.936 5.523 -2.807 c
+8.213 -3.572 -0.499 2.984 re
+8.242 0.206 m
+8.242 0.118 8.216 0.044 8.168 -0.014 c
+8.128 -0.066 8.058 -0.087 7.962 -0.087 c
+7.875 -0.087 7.804 -0.066 7.757 -0.014 c
+7.717 0.044 7.698 0.11 7.698 0.191 c
+7.698 0.279 7.717 0.353 7.757 0.412 c
+7.804 0.47 7.875 0.5 7.962 0.5 c
+8.058 0.5 8.128 0.47 8.168 0.412 c
+8.216 0.353 8.242 0.283 8.242 0.206 c
+10.329 -2.807 m
+10.329 -2.701 10.289 -2.612 10.212 -2.543 c
+10.131 -2.466 9.98 -2.377 9.756 -2.278 c
+9.491 -2.171 9.304 -2.08 9.198 -1.999 c
+9.088 -1.922 9.01 -1.833 8.962 -1.734 c
+8.911 -1.639 8.889 -1.521 8.889 -1.381 c
+8.889 -1.139 8.978 -0.937 9.153 -0.779 c
+9.33 -0.613 9.554 -0.529 9.83 -0.529 c
+10.123 -0.529 10.359 -0.617 10.535 -0.793 c
+10.711 -0.962 10.8 -1.176 10.8 -1.44 c
+10.314 -1.44 l
+10.314 -1.304 10.264 -1.19 10.168 -1.103 c
+10.079 -1.007 9.965 -0.955 9.83 -0.955 c
+9.683 -0.955 9.568 -0.995 9.491 -1.072 c
+9.41 -1.143 9.374 -1.242 9.374 -1.367 c
+9.374 -1.466 9.404 -1.543 9.462 -1.602 c
+9.521 -1.66 9.66 -1.741 9.888 -1.837 c
+10.248 -1.984 10.495 -2.127 10.624 -2.263 c
+10.759 -2.392 10.829 -2.564 10.829 -2.778 c
+10.829 -3.035 10.734 -3.241 10.549 -3.395 c
+10.374 -3.553 10.138 -3.63 9.844 -3.63 c
+9.528 -3.63 9.275 -3.542 9.08 -3.366 c
+8.893 -3.183 8.801 -2.95 8.801 -2.675 c
+9.286 -2.675 l
+9.293 -2.844 9.344 -2.977 9.433 -3.072 c
+9.528 -3.16 9.668 -3.204 9.844 -3.204 c
+9.998 -3.204 10.116 -3.171 10.197 -3.102 c
+10.285 -3.035 10.329 -2.936 10.329 -2.807 c
+14.019 -3.572 m
+13.99 -3.505 13.967 -3.395 13.96 -3.248 c
+13.784 -3.505 13.564 -3.63 13.298 -3.63 c
+13.023 -3.63 12.806 -3.557 12.652 -3.41 c
+12.505 -3.256 12.431 -3.039 12.431 -2.763 c
+12.431 -2.462 12.534 -2.219 12.74 -2.043 c
+12.945 -1.859 13.229 -1.764 13.593 -1.764 c
+13.946 -1.764 l
+13.946 -1.44 l
+13.946 -1.263 13.905 -1.143 13.828 -1.072 c
+13.747 -0.995 13.63 -0.955 13.475 -0.955 c
+13.328 -0.955 13.203 -0.999 13.107 -1.087 c
+13.019 -1.176 12.976 -1.286 12.976 -1.411 c
+12.49 -1.411 l
+12.49 -1.263 12.534 -1.124 12.623 -0.985 c
+12.71 -0.837 12.828 -0.727 12.976 -0.646 c
+13.13 -0.569 13.302 -0.529 13.489 -0.529 c
+13.802 -0.529 14.037 -0.61 14.195 -0.764 c
+14.349 -0.911 14.43 -1.132 14.43 -1.425 c
+14.43 -2.925 l
+14.438 -3.16 14.474 -3.362 14.534 -3.528 c
+14.534 -3.572 l
+h
+13.372 -3.189 m
+13.489 -3.189 13.6 -3.156 13.71 -3.087 c
+13.817 -3.021 13.894 -2.936 13.946 -2.836 c
+13.946 -2.131 l
+13.681 -2.131 l
+13.446 -2.131 13.258 -2.183 13.122 -2.278 c
+12.994 -2.377 12.931 -2.52 12.931 -2.705 c
+12.931 -2.873 12.961 -2.994 13.019 -3.072 c
+13.086 -3.152 13.203 -3.189 13.372 -3.189 c
+15.54 -3.572 -0.5 4.233 re
+17.653 -2.807 m
+17.653 -2.701 17.613 -2.612 17.535 -2.543 c
+17.455 -2.466 17.304 -2.377 17.08 -2.278 c
+16.816 -2.171 16.628 -2.08 16.521 -1.999 c
+16.411 -1.922 16.334 -1.833 16.286 -1.734 c
+16.234 -1.639 16.213 -1.521 16.213 -1.381 c
+16.213 -1.139 16.301 -0.937 16.477 -0.779 c
+16.654 -0.613 16.878 -0.529 17.153 -0.529 c
+17.447 -0.529 17.682 -0.617 17.859 -0.793 c
+18.035 -0.962 18.123 -1.176 18.123 -1.44 c
+17.639 -1.44 l
+17.639 -1.304 17.587 -1.19 17.491 -1.103 c
+17.404 -1.007 17.289 -0.955 17.153 -0.955 c
+17.007 -0.955 16.893 -0.995 16.816 -1.072 c
+16.735 -1.143 16.698 -1.242 16.698 -1.367 c
+16.698 -1.466 16.727 -1.543 16.786 -1.602 c
+16.845 -1.66 16.984 -1.741 17.212 -1.837 c
+17.572 -1.984 17.819 -2.127 17.947 -2.263 c
+18.083 -2.392 18.153 -2.564 18.153 -2.778 c
+18.153 -3.035 18.057 -3.241 17.874 -3.395 c
+17.697 -3.553 17.462 -3.63 17.169 -3.63 c
+16.852 -3.63 16.598 -3.542 16.404 -3.366 c
+16.216 -3.183 16.124 -2.95 16.124 -2.675 c
+16.61 -2.675 l
+16.617 -2.844 16.668 -2.977 16.756 -3.072 c
+16.852 -3.16 16.992 -3.204 17.169 -3.204 c
+17.323 -3.204 17.44 -3.171 17.521 -3.102 c
+17.609 -3.035 17.653 -2.936 17.653 -2.807 c
+18.509 -1.94 m
+18.509 -1.51 18.613 -1.168 18.818 -0.911 c
+19.031 -0.658 19.311 -0.529 19.656 -0.529 c
+19.997 -0.529 20.273 -0.658 20.479 -0.911 c
+20.693 -1.157 20.806 -1.492 20.817 -1.911 c
+20.817 -2.219 l
+20.817 -2.653 20.707 -2.994 20.494 -3.248 c
+20.288 -3.505 20.009 -3.63 19.656 -3.63 c
+19.311 -3.63 19.039 -3.509 18.833 -3.262 c
+18.627 -3.021 18.517 -2.686 18.509 -2.263 c
+h
+18.995 -2.219 m
+18.995 -2.535 19.053 -2.778 19.17 -2.954 c
+19.296 -3.123 19.457 -3.204 19.656 -3.204 c
+20.086 -3.204 20.31 -2.896 20.332 -2.278 c
+20.332 -1.94 l
+20.332 -1.639 20.266 -1.396 20.141 -1.22 c
+20.024 -1.043 19.862 -0.955 19.656 -0.955 c
+19.457 -0.955 19.296 -1.043 19.17 -1.22 c
+19.053 -1.396 18.995 -1.639 18.995 -1.94 c
+h
+f
+Q
+q 1 0 0 1 727.6223 145.6867 cm
+0 0 m
+-0.03 0.067 -0.052 0.177 -0.059 0.324 c
+-0.235 0.067 -0.456 -0.058 -0.721 -0.058 c
+-0.996 -0.058 -1.213 0.015 -1.367 0.162 c
+-1.515 0.316 -1.588 0.533 -1.588 0.809 c
+-1.588 1.11 -1.485 1.353 -1.279 1.529 c
+-1.073 1.713 -0.79 1.808 -0.426 1.808 c
+-0.074 1.808 l
+-0.074 2.132 l
+-0.074 2.309 -0.114 2.429 -0.191 2.5 c
+-0.272 2.577 -0.389 2.617 -0.545 2.617 c
+-0.691 2.617 -0.817 2.573 -0.912 2.484 c
+-1 2.396 -1.044 2.286 -1.044 2.161 c
+-1.529 2.161 l
+-1.529 2.309 -1.485 2.448 -1.397 2.587 c
+-1.309 2.735 -1.191 2.845 -1.044 2.926 c
+-0.89 3.003 -0.717 3.043 -0.53 3.043 c
+-0.217 3.043 0.018 2.962 0.176 2.808 c
+0.33 2.661 0.411 2.44 0.411 2.147 c
+0.411 0.647 l
+0.419 0.412 0.455 0.21 0.515 0.044 c
+0.515 0 l
+h
+-0.647 0.383 m
+-0.53 0.383 -0.42 0.416 -0.309 0.485 c
+-0.202 0.551 -0.125 0.636 -0.074 0.736 c
+-0.074 1.441 l
+-0.339 1.441 l
+-0.574 1.441 -0.761 1.389 -0.896 1.294 c
+-1.025 1.195 -1.088 1.052 -1.088 0.867 c
+-1.088 0.699 -1.058 0.578 -1 0.5 c
+-0.934 0.42 -0.817 0.383 -0.647 0.383 c
+2.726 0 -0.5 4.233 re
+3.296 1.632 m
+3.296 2.062 3.399 2.404 3.605 2.661 c
+3.818 2.914 4.097 3.043 4.442 3.043 c
+4.785 3.043 5.06 2.914 5.265 2.661 c
+5.479 2.415 5.593 2.08 5.604 1.661 c
+5.604 1.353 l
+5.604 0.919 5.493 0.578 5.28 0.324 c
+5.074 0.067 4.795 -0.058 4.442 -0.058 c
+4.097 -0.058 3.825 0.063 3.619 0.31 c
+3.414 0.551 3.303 0.886 3.296 1.309 c
+h
+3.781 1.353 m
+3.781 1.037 3.84 0.794 3.958 0.618 c
+4.082 0.449 4.244 0.368 4.442 0.368 c
+4.872 0.368 5.097 0.676 5.119 1.294 c
+5.119 1.632 l
+5.119 1.933 5.053 2.176 4.928 2.352 c
+4.81 2.529 4.648 2.617 4.442 2.617 c
+4.244 2.617 4.082 2.529 3.958 2.352 c
+3.84 2.176 3.781 1.933 3.781 1.632 c
+h
+7.073 0.368 m
+7.239 0.368 7.372 0.416 7.47 0.515 c
+7.566 0.611 7.621 0.754 7.632 0.941 c
+8.088 0.941 l
+8.077 0.655 7.974 0.416 7.779 0.221 c
+7.592 0.034 7.357 -0.058 7.073 -0.058 c
+6.709 -0.058 6.431 0.059 6.236 0.294 c
+6.038 0.53 5.942 0.875 5.942 1.338 c
+5.942 1.661 l
+5.942 2.11 6.034 2.455 6.221 2.691 c
+6.416 2.926 6.699 3.043 7.073 3.043 c
+7.374 3.043 7.617 2.944 7.794 2.749 c
+7.977 2.55 8.077 2.286 8.088 1.956 c
+7.632 1.956 l
+7.61 2.18 7.551 2.345 7.455 2.455 c
+7.368 2.562 7.239 2.617 7.073 2.617 c
+6.857 2.617 6.695 2.544 6.589 2.396 c
+6.489 2.257 6.434 2.029 6.427 1.706 c
+6.427 1.324 l
+6.427 0.971 6.474 0.721 6.574 0.574 c
+6.68 0.434 6.846 0.368 7.073 0.368 c
+10.01 0 m
+9.98 0.067 9.959 0.177 9.951 0.324 c
+9.774 0.067 9.554 -0.058 9.29 -0.058 c
+9.014 -0.058 8.797 0.015 8.643 0.162 c
+8.496 0.316 8.422 0.533 8.422 0.809 c
+8.422 1.11 8.525 1.353 8.731 1.529 c
+8.937 1.713 9.219 1.808 9.583 1.808 c
+9.936 1.808 l
+9.936 2.132 l
+9.936 2.309 9.896 2.429 9.819 2.5 c
+9.738 2.577 9.62 2.617 9.466 2.617 c
+9.319 2.617 9.194 2.573 9.098 2.484 c
+9.01 2.396 8.966 2.286 8.966 2.161 c
+8.481 2.161 l
+8.481 2.309 8.525 2.448 8.613 2.587 c
+8.702 2.735 8.819 2.845 8.966 2.926 c
+9.12 3.003 9.294 3.043 9.481 3.043 c
+9.793 3.043 10.028 2.962 10.186 2.808 c
+10.341 2.661 10.422 2.44 10.422 2.147 c
+10.422 0.647 l
+10.429 0.412 10.466 0.21 10.524 0.044 c
+10.524 0 l
+h
+9.363 0.383 m
+9.481 0.383 9.591 0.416 9.701 0.485 c
+9.807 0.551 9.884 0.636 9.936 0.736 c
+9.936 1.441 l
+9.672 1.441 l
+9.437 1.441 9.249 1.389 9.113 1.294 c
+8.984 1.195 8.922 1.052 8.922 0.867 c
+8.922 0.699 8.951 0.578 9.01 0.5 c
+9.076 0.42 9.194 0.383 9.363 0.383 c
+11.531 0 -0.5 4.233 re
+15.573 1.353 m
+15.573 0.882 15.488 0.53 15.324 0.294 c
+15.154 0.059 14.912 -0.058 14.588 -0.058 c
+14.272 -0.058 14.037 0.077 13.883 0.353 c
+13.853 0 l
+13.412 0 l
+13.412 4.233 l
+13.898 4.233 l
+13.898 2.661 l
+14.052 2.914 14.283 3.043 14.588 3.043 c
+14.912 3.043 15.154 2.926 15.324 2.691 c
+15.488 2.455 15.573 2.106 15.573 1.646 c
+h
+15.089 1.632 m
+15.089 1.985 15.037 2.234 14.941 2.382 c
+14.842 2.529 14.68 2.602 14.456 2.602 c
+14.21 2.602 14.023 2.463 13.898 2.191 c
+13.898 0.78 l
+14.015 0.515 14.206 0.383 14.47 0.383 c
+14.684 0.383 14.842 0.456 14.941 0.603 c
+15.037 0.757 15.089 1 15.089 1.324 c
+h
+17.249 2.529 m
+17.179 2.536 17.105 2.544 17.028 2.544 c
+16.772 2.544 16.595 2.404 16.5 2.132 c
+16.5 0 l
+16.014 0 l
+16.014 2.984 l
+16.485 2.984 l
+16.5 2.675 l
+16.624 2.918 16.808 3.043 17.043 3.043 c
+17.121 3.043 17.183 3.028 17.234 2.999 c
+h
+19.046 0 m
+19.016 0.067 18.994 0.177 18.987 0.324 c
+18.811 0.067 18.59 -0.058 18.326 -0.058 c
+18.05 -0.058 17.834 0.015 17.679 0.162 c
+17.532 0.316 17.458 0.533 17.458 0.809 c
+17.458 1.11 17.562 1.353 17.767 1.529 c
+17.973 1.713 18.256 1.808 18.62 1.808 c
+18.973 1.808 l
+18.973 2.132 l
+18.973 2.309 18.932 2.429 18.855 2.5 c
+18.774 2.577 18.657 2.617 18.503 2.617 c
+18.355 2.617 18.231 2.573 18.135 2.484 c
+18.046 2.396 18.002 2.286 18.002 2.161 c
+17.518 2.161 l
+17.518 2.309 17.562 2.448 17.649 2.587 c
+17.738 2.735 17.855 2.845 18.002 2.926 c
+18.156 3.003 18.329 3.043 18.517 3.043 c
+18.829 3.043 19.064 2.962 19.222 2.808 c
+19.376 2.661 19.457 2.44 19.457 2.147 c
+19.457 0.647 l
+19.465 0.412 19.502 0.21 19.561 0.044 c
+19.561 0 l
+h
+18.399 0.383 m
+18.517 0.383 18.627 0.416 18.738 0.485 c
+18.844 0.551 18.921 0.636 18.973 0.736 c
+18.973 1.441 l
+18.708 1.441 l
+18.472 1.441 18.285 1.389 18.15 1.294 c
+18.021 1.195 17.958 1.052 17.958 0.867 c
+17.958 0.699 17.988 0.578 18.046 0.5 c
+18.113 0.42 18.231 0.383 18.399 0.383 c
+20.494 2.984 m
+20.508 2.661 l
+20.693 2.914 20.935 3.043 21.229 3.043 c
+21.758 3.043 22.026 2.691 22.037 1.985 c
+22.037 0 l
+21.552 0 l
+21.552 1.956 l
+21.552 2.191 21.512 2.356 21.435 2.455 c
+21.354 2.55 21.236 2.602 21.082 2.602 c
+20.964 2.602 20.854 2.562 20.758 2.484 c
+20.66 2.404 20.582 2.297 20.523 2.161 c
+20.523 0 l
+20.038 0 l
+20.038 2.984 l
+h
+23.617 0.368 m
+23.783 0.368 23.915 0.416 24.014 0.515 c
+24.11 0.611 24.165 0.754 24.176 0.941 c
+24.631 0.941 l
+24.62 0.655 24.518 0.416 24.323 0.221 c
+24.136 0.034 23.9 -0.058 23.617 -0.058 c
+23.253 -0.058 22.974 0.059 22.779 0.294 c
+22.582 0.53 22.486 0.875 22.486 1.338 c
+22.486 1.661 l
+22.486 2.11 22.578 2.455 22.765 2.691 c
+22.96 2.926 23.243 3.043 23.617 3.043 c
+23.918 3.043 24.161 2.944 24.338 2.749 c
+24.521 2.55 24.62 2.286 24.631 1.956 c
+24.176 1.956 l
+24.154 2.18 24.095 2.345 23.999 2.455 c
+23.912 2.562 23.783 2.617 23.617 2.617 c
+23.401 2.617 23.239 2.544 23.132 2.396 c
+23.033 2.257 22.978 2.029 22.971 1.706 c
+22.971 1.324 l
+22.971 0.971 23.018 0.721 23.118 0.574 c
+23.224 0.434 23.39 0.368 23.617 0.368 c
+25.514 2.675 m
+25.697 2.918 25.932 3.043 26.219 3.043 c
+26.748 3.043 27.016 2.691 27.027 1.985 c
+27.027 0 l
+26.542 0 l
+26.542 1.956 l
+26.542 2.191 26.502 2.356 26.424 2.455 c
+26.344 2.55 26.227 2.602 26.072 2.602 c
+25.954 2.602 25.844 2.562 25.749 2.484 c
+25.649 2.404 25.572 2.297 25.514 2.161 c
+25.514 0 l
+25.028 0 l
+25.028 4.233 l
+25.514 4.233 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 757.5933 146.8594 cm
+0 0 m
+7.559 -2.874 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 763.1567 144.7467 cm
+0 0 m
+-1.378 -0.622 l
+2.492 -0.949 l
+-0.617 1.374 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 763.1568 144.7467 cm
+0 0 m
+-1.378 -0.622 l
+2.492 -0.949 l
+-0.617 1.374 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 710.7031 92.4728 cm
+0 0 m
+0.809 2.014 l
+1.382 2.014 l
+0.25 -0.5 l
+0.25 -1.999 l
+-0.264 -1.999 l
+-0.264 -0.5 l
+-1.396 2.014 l
+-0.823 2.014 l
+h
+1.382 -0.368 m
+1.382 0.062 1.484 0.405 1.691 0.661 c
+1.904 0.915 2.183 1.043 2.529 1.043 c
+2.87 1.043 3.146 0.915 3.352 0.661 c
+3.564 0.415 3.678 0.081 3.69 -0.338 c
+3.69 -0.647 l
+3.69 -1.08 3.58 -1.422 3.366 -1.675 c
+3.161 -1.933 2.881 -2.058 2.529 -2.058 c
+2.183 -2.058 1.912 -1.937 1.706 -1.691 c
+1.5 -1.448 1.389 -1.114 1.382 -0.691 c
+h
+1.867 -0.647 m
+1.867 -0.962 1.926 -1.205 2.043 -1.382 c
+2.168 -1.55 2.33 -1.631 2.529 -1.631 c
+2.959 -1.631 3.183 -1.323 3.204 -0.706 c
+3.204 -0.368 l
+3.204 -0.066 3.138 0.176 3.013 0.353 c
+2.896 0.529 2.734 0.617 2.529 0.617 c
+2.33 0.617 2.168 0.529 2.043 0.353 c
+1.926 0.176 1.867 -0.066 1.867 -0.368 c
+h
+5.644 -1.735 m
+5.486 -1.951 5.251 -2.058 4.939 -2.058 c
+4.675 -2.058 4.469 -1.966 4.322 -1.779 c
+4.183 -1.595 4.116 -1.319 4.116 -0.956 c
+4.116 0.985 l
+4.601 0.985 l
+4.601 -0.926 l
+4.601 -1.389 4.74 -1.617 5.027 -1.617 c
+5.321 -1.617 5.519 -1.484 5.63 -1.22 c
+5.63 0.985 l
+6.13 0.985 l
+6.13 -1.999 l
+5.659 -1.999 l
+h
+7.915 0.529 m
+7.846 0.536 7.772 0.544 7.695 0.544 c
+7.437 0.544 7.262 0.405 7.166 0.133 c
+7.166 -1.999 l
+6.681 -1.999 l
+6.681 0.985 l
+7.151 0.985 l
+7.166 0.676 l
+7.291 0.919 7.475 1.043 7.71 1.043 c
+7.787 1.043 7.85 1.029 7.901 0.999 c
+h
+f
+Q
+q 1 0 0 1 722.2789 91.7823 cm
+0 0 m
+0.264 1.675 l
+1.014 1.675 l
+0.397 -1.309 l
+-0.25 -1.309 l
+-0.676 0.411 l
+-1.088 -1.309 l
+-1.749 -1.309 l
+-2.367 1.675 l
+-1.617 1.675 l
+-1.353 0 l
+-0.956 1.675 l
+-0.397 1.675 l
+h
+1.073 0.278 m
+1.073 0.727 1.18 1.08 1.396 1.337 c
+1.61 1.602 1.903 1.734 2.279 1.734 c
+2.66 1.734 2.955 1.602 3.16 1.337 c
+3.373 1.08 3.484 0.727 3.484 0.278 c
+3.484 0.087 l
+3.484 -0.375 3.373 -0.732 3.16 -0.985 c
+2.955 -1.243 2.66 -1.368 2.279 -1.368 c
+1.897 -1.368 1.595 -1.243 1.382 -0.985 c
+1.176 -0.721 1.073 -0.364 1.073 0.087 c
+h
+1.852 0.087 m
+1.852 -0.452 1.992 -0.721 2.279 -0.721 c
+2.543 -0.721 2.682 -0.497 2.705 -0.044 c
+2.72 0.278 l
+2.72 0.551 2.679 0.753 2.602 0.881 c
+2.521 1.018 2.414 1.087 2.279 1.087 c
+2.15 1.087 2.047 1.018 1.97 0.881 c
+1.889 0.753 1.852 0.551 1.852 0.278 c
+h
+5.255 0.926 m
+5.005 0.941 l
+4.788 0.941 4.642 0.845 4.564 0.661 c
+4.564 -1.309 l
+3.785 -1.309 l
+3.785 1.675 l
+4.52 1.675 l
+4.549 1.352 l
+4.667 1.606 4.833 1.734 5.049 1.734 c
+5.137 1.734 5.211 1.72 5.269 1.69 c
+h
+6.533 -0.162 m
+6.313 -0.397 l
+6.313 -1.309 l
+5.534 -1.309 l
+5.534 2.925 l
+6.313 2.925 l
+6.313 0.631 l
+6.416 0.779 l
+6.96 1.675 l
+7.886 1.675 l
+7.019 0.44 l
+7.975 -1.309 l
+7.077 -1.309 l
+h
+8.878 -1.309 -0.779 2.984 re
+8.055 2.439 m
+8.055 2.564 8.091 2.668 8.172 2.748 c
+8.249 2.826 8.353 2.865 8.482 2.865 c
+8.606 2.865 8.709 2.826 8.79 2.748 c
+8.867 2.668 8.908 2.564 8.908 2.439 c
+8.908 2.322 8.867 2.223 8.79 2.146 c
+8.72 2.065 8.617 2.028 8.482 2.028 c
+8.353 2.028 8.249 2.065 8.172 2.146 c
+8.091 2.223 8.055 2.322 8.055 2.439 c
+10.102 1.675 m
+10.117 1.381 l
+10.293 1.616 10.521 1.734 10.807 1.734 c
+11.326 1.734 11.59 1.371 11.601 0.646 c
+11.601 -1.309 l
+10.822 -1.309 l
+10.822 0.588 l
+10.822 0.764 10.797 0.885 10.749 0.955 c
+10.697 1.032 10.609 1.072 10.484 1.072 c
+10.337 1.072 10.223 0.999 10.146 0.852 c
+10.146 -1.309 l
+9.367 -1.309 l
+9.367 1.675 l
+h
+11.884 0.278 m
+11.884 0.768 11.973 1.132 12.149 1.367 c
+12.325 1.61 12.567 1.734 12.883 1.734 c
+13.155 1.734 13.369 1.624 13.516 1.411 c
+13.545 1.675 l
+14.25 1.675 l
+14.25 -1.309 l
+14.25 -1.691 14.14 -1.977 13.928 -2.176 c
+13.71 -2.382 13.406 -2.484 13.016 -2.484 c
+12.847 -2.484 12.671 -2.448 12.486 -2.382 c
+12.311 -2.323 12.178 -2.238 12.089 -2.132 c
+12.355 -1.603 l
+12.432 -1.669 12.531 -1.727 12.648 -1.779 c
+12.766 -1.827 12.873 -1.852 12.972 -1.852 c
+13.149 -1.852 13.273 -1.808 13.354 -1.721 c
+13.431 -1.64 13.471 -1.507 13.471 -1.324 c
+13.471 -1.073 l
+13.325 -1.272 13.126 -1.368 12.883 -1.368 c
+12.567 -1.368 12.325 -1.246 12.149 -1 c
+11.973 -0.746 11.884 -0.393 11.884 0.058 c
+h
+12.663 0.073 m
+12.663 -0.202 12.696 -0.405 12.766 -0.53 c
+12.843 -0.647 12.961 -0.706 13.119 -0.706 c
+13.273 -0.706 13.391 -0.655 13.471 -0.545 c
+13.471 0.881 l
+13.391 1.007 13.273 1.072 13.119 1.072 c
+12.961 1.072 12.843 1.007 12.766 0.881 c
+12.696 0.753 12.663 0.551 12.663 0.278 c
+h
+15.739 0.278 m
+15.739 0.768 15.819 1.132 15.989 1.367 c
+16.154 1.61 16.393 1.734 16.709 1.734 c
+16.945 1.734 17.136 1.631 17.282 1.425 c
+17.282 2.925 l
+18.076 2.925 l
+18.076 -1.309 l
+17.356 -1.309 l
+17.327 -1 l
+17.169 -1.246 16.962 -1.368 16.709 -1.368 c
+16.393 -1.368 16.154 -1.25 15.989 -1.015 c
+15.831 -0.772 15.746 -0.42 15.739 0.043 c
+h
+16.518 0.073 m
+16.518 -0.221 16.548 -0.427 16.606 -0.545 c
+16.673 -0.655 16.783 -0.706 16.93 -0.706 c
+17.084 -0.706 17.201 -0.64 17.282 -0.5 c
+17.282 0.852 l
+17.201 0.999 17.091 1.072 16.945 1.072 c
+16.797 1.072 16.687 1.014 16.621 0.897 c
+16.551 0.786 16.518 0.58 16.518 0.278 c
+h
+19.278 -1.309 -0.779 2.984 re
+18.454 2.439 m
+18.454 2.564 18.491 2.668 18.572 2.748 c
+18.649 2.826 18.753 2.865 18.881 2.865 c
+19.006 2.865 19.108 2.826 19.189 2.748 c
+19.266 2.668 19.307 2.564 19.307 2.439 c
+19.307 2.322 19.266 2.223 19.189 2.146 c
+19.12 2.065 19.017 2.028 18.881 2.028 c
+18.753 2.028 18.649 2.065 18.572 2.146 c
+18.491 2.223 18.454 2.322 18.454 2.439 c
+21.255 0.926 m
+21.005 0.941 l
+20.789 0.941 20.641 0.845 20.564 0.661 c
+20.564 -1.309 l
+19.785 -1.309 l
+19.785 1.675 l
+20.519 1.675 l
+20.549 1.352 l
+20.667 1.606 20.832 1.734 21.049 1.734 c
+21.137 1.734 21.211 1.72 21.269 1.69 c
+h
+22.688 -1.368 m
+22.283 -1.368 21.971 -1.25 21.747 -1.015 c
+21.531 -0.772 21.423 -0.427 21.423 0.014 c
+21.423 0.264 l
+21.423 0.735 21.527 1.095 21.732 1.352 c
+21.938 1.606 22.232 1.734 22.614 1.734 c
+22.986 1.734 23.264 1.61 23.453 1.367 c
+23.647 1.132 23.746 0.779 23.746 0.309 c
+23.746 -0.074 l
+22.202 -0.074 l
+22.21 -0.302 22.254 -0.467 22.335 -0.574 c
+22.423 -0.673 22.559 -0.721 22.747 -0.721 c
+23 -0.721 23.217 -0.628 23.393 -0.441 c
+23.702 -0.927 l
+23.603 -1.055 23.459 -1.162 23.276 -1.25 c
+23.1 -1.327 22.901 -1.368 22.688 -1.368 c
+22.202 0.47 m
+22.982 0.47 l
+22.982 0.544 l
+22.982 0.72 22.952 0.852 22.894 0.941 c
+22.835 1.036 22.736 1.087 22.599 1.087 c
+22.471 1.087 22.372 1.036 22.306 0.941 c
+22.247 0.841 22.21 0.683 22.202 0.47 c
+25.091 -0.721 m
+25.315 -0.721 25.429 -0.574 25.429 -0.279 c
+26.164 -0.279 l
+26.164 -0.603 26.061 -0.867 25.855 -1.073 c
+25.657 -1.272 25.407 -1.368 25.106 -1.368 c
+24.724 -1.368 24.43 -1.25 24.224 -1.015 c
+24.018 -0.772 23.915 -0.42 23.915 0.043 c
+23.915 0.294 l
+23.915 0.753 24.01 1.105 24.209 1.352 c
+24.415 1.606 24.709 1.734 25.091 1.734 c
+25.422 1.734 25.683 1.631 25.87 1.425 c
+26.065 1.219 26.164 0.929 26.164 0.558 c
+25.429 0.558 l
+25.429 0.723 25.4 0.852 25.341 0.941 c
+25.29 1.036 25.201 1.087 25.076 1.087 c
+24.947 1.087 24.856 1.036 24.797 0.941 c
+24.739 0.841 24.702 0.654 24.694 0.382 c
+24.694 0.073 l
+24.694 -0.173 24.702 -0.346 24.724 -0.441 c
+24.753 -0.53 24.789 -0.599 24.841 -0.647 c
+24.9 -0.699 24.981 -0.721 25.091 -0.721 c
+27.34 2.41 m
+27.34 1.675 l
+27.737 1.675 l
+27.737 1.087 l
+27.34 1.087 l
+27.34 -0.397 l
+27.34 -0.515 27.355 -0.599 27.384 -0.647 c
+27.421 -0.688 27.487 -0.706 27.575 -0.706 c
+27.652 -0.706 27.715 -0.699 27.767 -0.676 c
+27.752 -1.294 l
+27.623 -1.341 27.48 -1.368 27.326 -1.368 c
+26.826 -1.368 26.568 -1.081 26.561 -0.5 c
+26.561 1.087 l
+26.223 1.087 l
+26.223 1.675 l
+26.561 1.675 l
+26.561 2.41 l
+h
+27.884 0.278 m
+27.884 0.727 27.991 1.08 28.207 1.337 c
+28.421 1.602 28.714 1.734 29.09 1.734 c
+29.471 1.734 29.766 1.602 29.971 1.337 c
+30.185 1.08 30.295 0.727 30.295 0.278 c
+30.295 0.087 l
+30.295 -0.375 30.185 -0.732 29.971 -0.985 c
+29.766 -1.243 29.471 -1.368 29.09 -1.368 c
+28.708 -1.368 28.406 -1.243 28.193 -0.985 c
+27.987 -0.721 27.884 -0.364 27.884 0.087 c
+h
+28.663 0.087 m
+28.663 -0.452 28.803 -0.721 29.09 -0.721 c
+29.354 -0.721 29.493 -0.497 29.516 -0.044 c
+29.531 0.278 l
+29.531 0.551 29.49 0.753 29.413 0.881 c
+29.332 1.018 29.225 1.087 29.09 1.087 c
+28.961 1.087 28.858 1.018 28.781 0.881 c
+28.7 0.753 28.663 0.551 28.663 0.278 c
+h
+32.066 0.926 m
+31.816 0.941 l
+31.599 0.941 31.453 0.845 31.375 0.661 c
+31.375 -1.309 l
+30.596 -1.309 l
+30.596 1.675 l
+31.331 1.675 l
+31.36 1.352 l
+31.478 1.606 31.644 1.734 31.86 1.734 c
+31.948 1.734 32.022 1.72 32.08 1.69 c
+h
+33.352 -0.015 m
+33.749 1.675 l
+34.587 1.675 l
+33.602 -1.75 l
+33.455 -2.257 33.179 -2.514 32.779 -2.514 c
+32.679 -2.514 32.573 -2.492 32.456 -2.455 c
+32.456 -1.852 l
+32.544 -1.852 l
+32.669 -1.852 32.764 -1.827 32.823 -1.779 c
+32.882 -1.738 32.926 -1.661 32.955 -1.544 c
+33.028 -1.338 l
+32.161 1.675 l
+32.999 1.675 l
+h
+f
+Q
+743.813 83.889 -0.5 2.984 re
+743.843 87.667 m
+743.843 87.578 743.816 87.505 743.769 87.445 c
+743.729 87.395 743.659 87.372 743.563 87.372 c
+743.475 87.372 743.405 87.395 743.357 87.445 c
+743.317 87.505 743.299 87.571 743.299 87.652 c
+743.299 87.74 743.317 87.813 743.357 87.872 c
+743.405 87.931 743.475 87.96 743.563 87.96 c
+743.659 87.96 743.729 87.931 743.769 87.872 c
+743.816 87.813 743.843 87.744 743.843 87.667 c
+745.926 84.652 m
+745.926 84.76 745.886 84.848 745.809 84.918 c
+745.728 84.995 745.577 85.083 745.352 85.182 c
+745.088 85.288 744.901 85.381 744.795 85.461 c
+744.684 85.539 744.606 85.627 744.559 85.726 c
+744.508 85.822 744.485 85.939 744.485 86.079 c
+744.485 86.321 744.573 86.524 744.75 86.682 c
+744.926 86.847 745.15 86.931 745.426 86.931 c
+745.72 86.931 745.956 86.843 746.131 86.667 c
+746.308 86.497 746.397 86.285 746.397 86.02 c
+745.911 86.02 l
+745.911 86.156 745.859 86.27 745.765 86.358 c
+745.676 86.454 745.562 86.505 745.426 86.505 c
+745.279 86.505 745.165 86.464 745.088 86.387 c
+745.007 86.318 744.97 86.219 744.97 86.094 c
+744.97 85.994 745 85.917 745.059 85.858 c
+745.117 85.799 745.257 85.718 745.485 85.623 c
+745.845 85.476 746.091 85.333 746.22 85.197 c
+746.356 85.068 746.426 84.895 746.426 84.683 c
+746.426 84.425 746.33 84.22 746.146 84.064 c
+745.971 83.907 745.735 83.829 745.441 83.829 c
+745.125 83.829 744.872 83.918 744.677 84.095 c
+744.489 84.278 744.398 84.509 744.398 84.785 c
+744.882 84.785 l
+744.89 84.616 744.941 84.484 745.03 84.388 c
+745.125 84.3 745.265 84.256 745.441 84.256 c
+745.595 84.256 745.713 84.289 745.794 84.359 c
+745.882 84.425 745.926 84.525 745.926 84.652 c
+748.572 86.564 m
+748.755 86.807 748.99 86.931 749.277 86.931 c
+749.807 86.931 750.075 86.578 750.086 85.873 c
+750.086 83.889 l
+749.601 83.889 l
+749.601 85.843 l
+749.601 86.079 749.56 86.244 749.483 86.343 c
+749.402 86.439 749.285 86.491 749.13 86.491 c
+749.013 86.491 748.903 86.45 748.807 86.373 c
+748.708 86.292 748.631 86.185 748.572 86.049 c
+748.572 83.889 l
+748.087 83.889 l
+748.087 88.122 l
+748.572 88.122 l
+h
+751.688 83.829 m
+751.313 83.829 751.03 83.937 750.835 84.153 c
+750.637 84.378 750.542 84.704 750.542 85.138 c
+750.542 85.506 l
+750.542 85.946 750.634 86.292 750.821 86.549 c
+751.016 86.803 751.291 86.931 751.644 86.931 c
+751.986 86.931 752.24 86.817 752.408 86.593 c
+752.585 86.366 752.676 86.02 752.687 85.55 c
+752.687 85.24 l
+751.027 85.24 l
+751.027 85.167 l
+751.027 84.844 751.085 84.609 751.203 84.461 c
+751.32 84.322 751.49 84.256 751.718 84.256 c
+751.864 84.256 751.989 84.278 752.099 84.33 c
+752.206 84.388 752.309 84.477 752.408 84.594 c
+752.658 84.286 l
+752.452 83.981 752.129 83.829 751.688 83.829 c
+751.644 86.505 m
+751.438 86.505 751.284 86.435 751.188 86.299 c
+751.089 86.16 751.034 85.946 751.027 85.652 c
+752.203 85.652 l
+752.203 85.726 l
+752.18 85.998 752.129 86.196 752.041 86.314 c
+751.953 86.439 751.82 86.505 751.644 86.505 c
+754.334 86.416 m
+754.264 86.424 754.191 86.431 754.114 86.431 c
+753.856 86.431 753.68 86.292 753.584 86.02 c
+753.584 83.889 l
+753.099 83.889 l
+753.099 86.873 l
+753.57 86.873 l
+753.584 86.564 l
+753.709 86.807 753.892 86.931 754.128 86.931 c
+754.205 86.931 754.268 86.917 754.32 86.887 c
+h
+755.712 83.829 m
+755.337 83.829 755.054 83.937 754.86 84.153 c
+754.661 84.378 754.565 84.704 754.565 85.138 c
+754.565 85.506 l
+754.565 85.946 754.657 86.292 754.845 86.549 c
+755.039 86.803 755.315 86.931 755.668 86.931 c
+756.009 86.931 756.263 86.817 756.432 86.593 c
+756.608 86.366 756.701 86.02 756.711 85.55 c
+756.711 85.24 l
+755.051 85.24 l
+755.051 85.167 l
+755.051 84.844 755.109 84.609 755.226 84.461 c
+755.344 84.322 755.513 84.256 755.741 84.256 c
+755.889 84.256 756.013 84.278 756.124 84.33 c
+756.23 84.388 756.333 84.477 756.432 84.594 c
+756.682 84.286 l
+756.476 83.981 756.153 83.829 755.712 83.829 c
+755.668 86.505 m
+755.462 86.505 755.307 86.435 755.212 86.299 c
+755.113 86.16 755.058 85.946 755.051 85.652 c
+756.226 85.652 l
+756.226 85.726 l
+756.204 85.998 756.153 86.196 756.065 86.314 c
+755.976 86.439 755.844 86.505 755.668 86.505 c
+f
+0.793 0.801 0.129 0.016 K
+0.567 w 1 j 1 J
+q 1 0 0 1 757.869 91.9766 cm
+0 0 m
+6.53 2.899 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 762.4514 94.0083 cm
+0 0 m
+-0.543 -1.407 l
+2.436 1.081 l
+-1.408 0.544 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 762.4514 94.0083 cm
+0 0 m
+-0.543 -1.407 l
+2.436 1.081 l
+-1.408 0.544 l
+0 0 l
+h
+S
+Q
+0 0 0 0 k
+q 1 0 0 1 317.2173 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.512 -2.076 -7.512 0 c
+-7.512 2.076 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+f
+Q
+0.797 0.801 0.129 0.016 K
+0.709 w 1 j 1 J
+q 1 0 0 1 317.2173 119.0558 cm
+0 0 m
+0 -2.076 -1.683 -3.755 -3.755 -3.755 c
+-5.832 -3.755 -7.512 -2.076 -7.512 0 c
+-7.512 2.076 -5.832 3.755 -3.755 3.755 c
+-1.683 3.755 0 2.076 0 0 c
+h
+S
+Q
+q
+320.12 122.007 211.589 -5.589 re
+W n
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS6 gs
+0 TL/Fm33 Do
+Q
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 325.9445 111.6592 cm
+0 0 m
+-1.058 0 l
+-1.058 -3.572 l
+-1.558 -3.572 l
+-1.558 0 l
+-2.616 0 l
+-2.616 0.44 l
+0 0.44 l
+h
+0.819 -0.897 m
+1.004 -0.655 1.239 -0.53 1.525 -0.53 c
+2.055 -0.53 2.323 -0.882 2.334 -1.588 c
+2.334 -3.572 l
+1.849 -3.572 l
+1.849 -1.617 l
+1.849 -1.382 1.808 -1.216 1.731 -1.118 c
+1.65 -1.022 1.532 -0.971 1.378 -0.971 c
+1.261 -0.971 1.151 -1.011 1.055 -1.088 c
+0.956 -1.169 0.879 -1.276 0.819 -1.411 c
+0.819 -3.572 l
+0.335 -3.572 l
+0.335 0.661 l
+0.819 0.661 l
+h
+3.406 -3.572 -0.5 2.984 re
+3.436 0.205 m
+3.436 0.118 3.41 0.043 3.362 -0.015 c
+3.322 -0.067 3.252 -0.088 3.157 -0.088 c
+3.069 -0.088 2.999 -0.067 2.951 -0.015 c
+2.911 0.043 2.892 0.11 2.892 0.191 c
+2.892 0.278 2.911 0.353 2.951 0.411 c
+2.999 0.47 3.069 0.5 3.157 0.5 c
+3.252 0.5 3.322 0.47 3.362 0.411 c
+3.41 0.353 3.436 0.282 3.436 0.205 c
+5.523 -2.808 m
+5.523 -2.702 5.483 -2.613 5.405 -2.544 c
+5.325 -2.467 5.174 -2.378 4.95 -2.278 c
+4.686 -2.172 4.498 -2.08 4.391 -1.999 c
+4.281 -1.922 4.204 -1.834 4.156 -1.735 c
+4.104 -1.64 4.083 -1.522 4.083 -1.382 c
+4.083 -1.139 4.171 -0.938 4.347 -0.78 c
+4.524 -0.614 4.748 -0.53 5.024 -0.53 c
+5.317 -0.53 5.552 -0.618 5.729 -0.794 c
+5.906 -0.963 5.993 -1.176 5.993 -1.441 c
+5.509 -1.441 l
+5.509 -1.305 5.457 -1.191 5.361 -1.103 c
+5.274 -1.008 5.16 -0.956 5.024 -0.956 c
+4.877 -0.956 4.763 -0.996 4.686 -1.073 c
+4.605 -1.143 4.568 -1.243 4.568 -1.368 c
+4.568 -1.467 4.597 -1.544 4.656 -1.603 c
+4.715 -1.661 4.854 -1.742 5.082 -1.838 c
+5.442 -1.985 5.689 -2.128 5.818 -2.264 c
+5.953 -2.392 6.023 -2.565 6.023 -2.779 c
+6.023 -3.036 5.928 -3.242 5.744 -3.396 c
+5.567 -3.554 5.332 -3.631 5.039 -3.631 c
+4.722 -3.631 4.469 -3.543 4.274 -3.367 c
+4.087 -3.183 3.994 -2.951 3.994 -2.675 c
+4.48 -2.675 l
+4.487 -2.845 4.538 -2.977 4.627 -3.072 c
+4.722 -3.161 4.862 -3.205 5.039 -3.205 c
+5.193 -3.205 5.31 -3.172 5.391 -3.102 c
+5.479 -3.036 5.523 -2.937 5.523 -2.808 c
+8.214 -3.572 -0.5 2.984 re
+8.243 0.205 m
+8.243 0.118 8.217 0.043 8.169 -0.015 c
+8.129 -0.067 8.059 -0.088 7.963 -0.088 c
+7.875 -0.088 7.805 -0.067 7.757 -0.015 c
+7.717 0.043 7.699 0.11 7.699 0.191 c
+7.699 0.278 7.717 0.353 7.757 0.411 c
+7.805 0.47 7.875 0.5 7.963 0.5 c
+8.059 0.5 8.129 0.47 8.169 0.411 c
+8.217 0.353 8.243 0.282 8.243 0.205 c
+10.326 -2.808 m
+10.326 -2.702 10.286 -2.613 10.209 -2.544 c
+10.128 -2.467 9.977 -2.378 9.753 -2.278 c
+9.488 -2.172 9.301 -2.08 9.195 -1.999 c
+9.084 -1.922 9.007 -1.834 8.96 -1.735 c
+8.908 -1.64 8.885 -1.522 8.885 -1.382 c
+8.885 -1.139 8.974 -0.938 9.151 -0.78 c
+9.326 -0.614 9.55 -0.53 9.826 -0.53 c
+10.12 -0.53 10.356 -0.618 10.532 -0.794 c
+10.708 -0.963 10.797 -1.176 10.797 -1.441 c
+10.311 -1.441 l
+10.311 -1.305 10.26 -1.191 10.165 -1.103 c
+10.076 -1.008 9.962 -0.956 9.826 -0.956 c
+9.679 -0.956 9.565 -0.996 9.488 -1.073 c
+9.407 -1.143 9.371 -1.243 9.371 -1.368 c
+9.371 -1.467 9.4 -1.544 9.459 -1.603 c
+9.517 -1.661 9.658 -1.742 9.885 -1.838 c
+10.246 -1.985 10.491 -2.128 10.62 -2.264 c
+10.756 -2.392 10.826 -2.565 10.826 -2.779 c
+10.826 -3.036 10.73 -3.242 10.547 -3.396 c
+10.371 -3.554 10.135 -3.631 9.841 -3.631 c
+9.525 -3.631 9.272 -3.543 9.077 -3.367 c
+8.889 -3.183 8.798 -2.951 8.798 -2.675 c
+9.282 -2.675 l
+9.29 -2.845 9.342 -2.977 9.43 -3.072 c
+9.525 -3.161 9.665 -3.205 9.841 -3.205 c
+9.995 -3.205 10.113 -3.172 10.194 -3.102 c
+10.282 -3.036 10.326 -2.937 10.326 -2.808 c
+14.016 -3.572 m
+13.986 -3.506 13.964 -3.396 13.957 -3.249 c
+13.781 -3.506 13.56 -3.631 13.295 -3.631 c
+13.02 -3.631 12.803 -3.558 12.648 -3.41 c
+12.501 -3.256 12.428 -3.04 12.428 -2.764 c
+12.428 -2.463 12.531 -2.22 12.737 -2.043 c
+12.943 -1.86 13.226 -1.764 13.589 -1.764 c
+13.942 -1.764 l
+13.942 -1.441 l
+13.942 -1.264 13.902 -1.143 13.824 -1.073 c
+13.744 -0.996 13.627 -0.956 13.471 -0.956 c
+13.325 -0.956 13.199 -1 13.104 -1.088 c
+13.016 -1.176 12.972 -1.287 12.972 -1.411 c
+12.487 -1.411 l
+12.487 -1.264 12.531 -1.125 12.619 -0.985 c
+12.707 -0.838 12.825 -0.728 12.972 -0.647 c
+13.126 -0.57 13.299 -0.53 13.486 -0.53 c
+13.799 -0.53 14.034 -0.611 14.192 -0.765 c
+14.346 -0.912 14.427 -1.133 14.427 -1.426 c
+14.427 -2.926 l
+14.435 -3.161 14.471 -3.363 14.53 -3.528 c
+14.53 -3.572 l
+h
+13.369 -3.19 m
+13.486 -3.19 13.596 -3.157 13.707 -3.088 c
+13.814 -3.021 13.891 -2.937 13.942 -2.837 c
+13.942 -2.132 l
+13.677 -2.132 l
+13.442 -2.132 13.255 -2.183 13.119 -2.278 c
+12.991 -2.378 12.928 -2.521 12.928 -2.705 c
+12.928 -2.874 12.958 -2.995 13.016 -3.072 c
+13.082 -3.153 13.199 -3.19 13.369 -3.19 c
+15.464 -0.588 m
+15.478 -0.912 l
+15.662 -0.659 15.904 -0.53 16.199 -0.53 c
+16.727 -0.53 16.996 -0.882 17.007 -1.588 c
+17.007 -3.572 l
+16.522 -3.572 l
+16.522 -1.617 l
+16.522 -1.382 16.482 -1.216 16.405 -1.118 c
+16.324 -1.022 16.206 -0.971 16.052 -0.971 c
+15.934 -0.971 15.823 -1.011 15.728 -1.088 c
+15.629 -1.169 15.551 -1.276 15.493 -1.411 c
+15.493 -3.572 l
+15.008 -3.572 l
+15.008 -0.588 l
+h
+19.293 -3.572 -0.5 2.984 re
+19.322 0.205 m
+19.322 0.118 19.296 0.043 19.248 -0.015 c
+19.208 -0.067 19.138 -0.088 19.042 -0.088 c
+18.955 -0.088 18.884 -0.067 18.837 -0.015 c
+18.797 0.043 18.778 0.11 18.778 0.191 c
+18.778 0.278 18.797 0.353 18.837 0.411 c
+18.884 0.47 18.955 0.5 19.042 0.5 c
+19.138 0.5 19.208 0.47 19.248 0.411 c
+19.296 0.353 19.322 0.282 19.322 0.205 c
+20.424 -0.588 m
+20.439 -0.912 l
+20.623 -0.659 20.866 -0.53 21.159 -0.53 c
+21.689 -0.53 21.957 -0.882 21.968 -1.588 c
+21.968 -3.572 l
+21.483 -3.572 l
+21.483 -1.617 l
+21.483 -1.382 21.442 -1.216 21.365 -1.118 c
+21.284 -1.022 21.167 -0.971 21.012 -0.971 c
+20.895 -0.971 20.785 -1.011 20.689 -1.088 c
+20.59 -1.169 20.513 -1.276 20.454 -1.411 c
+20.454 -3.572 l
+19.969 -3.572 l
+19.969 -0.588 l
+h
+23.048 -3.572 -0.5 2.984 re
+23.077 0.205 m
+23.077 0.118 23.052 0.043 23.004 -0.015 c
+22.963 -0.067 22.894 -0.088 22.799 -0.088 c
+22.71 -0.088 22.641 -0.067 22.593 -0.015 c
+22.552 0.043 22.534 0.11 22.534 0.191 c
+22.534 0.278 22.552 0.353 22.593 0.411 c
+22.641 0.47 22.71 0.5 22.799 0.5 c
+22.894 0.5 22.963 0.47 23.004 0.411 c
+23.052 0.353 23.077 0.282 23.077 0.205 c
+24.357 0.132 m
+24.357 -0.588 l
+24.812 -0.588 l
+24.812 -0.985 l
+24.357 -0.985 l
+24.357 -2.837 l
+24.357 -2.955 24.375 -3.043 24.415 -3.102 c
+24.452 -3.161 24.522 -3.19 24.621 -3.19 c
+24.68 -3.19 24.742 -3.183 24.812 -3.161 c
+24.812 -3.572 l
+24.694 -3.609 24.581 -3.631 24.474 -3.631 c
+24.276 -3.631 24.125 -3.565 24.018 -3.425 c
+23.919 -3.29 23.871 -3.095 23.871 -2.837 c
+23.871 -0.985 l
+23.416 -0.985 l
+23.416 -0.588 l
+23.871 -0.588 l
+23.871 0.132 l
+h
+25.808 -3.572 -0.5 2.984 re
+25.837 0.205 m
+25.837 0.118 25.812 0.043 25.764 -0.015 c
+25.724 -0.067 25.654 -0.088 25.558 -0.088 c
+25.47 -0.088 25.4 -0.067 25.352 -0.015 c
+25.312 0.043 25.294 0.11 25.294 0.191 c
+25.294 0.278 25.312 0.353 25.352 0.411 c
+25.4 0.47 25.47 0.5 25.558 0.5 c
+25.654 0.5 25.724 0.47 25.764 0.411 c
+25.812 0.353 25.837 0.282 25.837 0.205 c
+28.01 -3.572 m
+27.979 -3.506 27.958 -3.396 27.95 -3.249 c
+27.774 -3.506 27.553 -3.631 27.289 -3.631 c
+27.013 -3.631 26.797 -3.558 26.642 -3.41 c
+26.495 -3.256 26.422 -3.04 26.422 -2.764 c
+26.422 -2.463 26.524 -2.22 26.73 -2.043 c
+26.936 -1.86 27.219 -1.764 27.582 -1.764 c
+27.935 -1.764 l
+27.935 -1.441 l
+27.935 -1.264 27.895 -1.143 27.818 -1.073 c
+27.738 -0.996 27.62 -0.956 27.465 -0.956 c
+27.318 -0.956 27.193 -1 27.098 -1.088 c
+27.01 -1.176 26.965 -1.287 26.965 -1.411 c
+26.481 -1.411 l
+26.481 -1.264 26.524 -1.125 26.613 -0.985 c
+26.701 -0.838 26.819 -0.728 26.965 -0.647 c
+27.12 -0.57 27.293 -0.53 27.48 -0.53 c
+27.792 -0.53 28.027 -0.611 28.185 -0.765 c
+28.34 -0.912 28.421 -1.133 28.421 -1.426 c
+28.421 -2.926 l
+28.428 -3.161 28.465 -3.363 28.523 -3.528 c
+28.523 -3.572 l
+h
+27.362 -3.19 m
+27.48 -3.19 27.59 -3.157 27.7 -3.088 c
+27.807 -3.021 27.884 -2.937 27.935 -2.837 c
+27.935 -2.132 l
+27.671 -2.132 l
+27.436 -2.132 27.249 -2.183 27.112 -2.278 c
+26.984 -2.378 26.921 -2.521 26.921 -2.705 c
+26.921 -2.874 26.951 -2.995 27.01 -3.072 c
+27.075 -3.153 27.193 -3.19 27.362 -3.19 c
+29.531 -3.572 -0.5 4.233 re
+32.438 -3.205 m
+32.602 -3.205 32.735 -3.157 32.834 -3.057 c
+32.93 -2.962 32.985 -2.819 32.995 -2.631 c
+33.452 -2.631 l
+33.44 -2.918 33.338 -3.157 33.143 -3.352 c
+32.955 -3.539 32.72 -3.631 32.438 -3.631 c
+32.074 -3.631 31.794 -3.514 31.599 -3.278 c
+31.401 -3.043 31.306 -2.698 31.306 -2.234 c
+31.306 -1.912 l
+31.306 -1.463 31.397 -1.118 31.584 -0.882 c
+31.779 -0.647 32.062 -0.53 32.438 -0.53 c
+32.739 -0.53 32.981 -0.628 33.157 -0.823 c
+33.342 -1.022 33.44 -1.287 33.452 -1.617 c
+32.995 -1.617 l
+32.974 -1.393 32.915 -1.228 32.82 -1.118 c
+32.731 -1.011 32.602 -0.956 32.438 -0.956 c
+32.22 -0.956 32.059 -1.029 31.952 -1.176 c
+31.853 -1.316 31.798 -1.544 31.79 -1.867 c
+31.79 -2.249 l
+31.79 -2.602 31.838 -2.852 31.937 -2.999 c
+32.044 -3.138 32.209 -3.205 32.438 -3.205 c
+33.745 -1.941 m
+33.745 -1.511 33.849 -1.169 34.054 -0.912 c
+34.267 -0.659 34.547 -0.53 34.892 -0.53 c
+35.233 -0.53 35.509 -0.659 35.715 -0.912 c
+35.929 -1.158 36.043 -1.492 36.053 -1.912 c
+36.053 -2.22 l
+36.053 -2.654 35.943 -2.995 35.73 -3.249 c
+35.524 -3.506 35.245 -3.631 34.892 -3.631 c
+34.547 -3.631 34.275 -3.51 34.069 -3.263 c
+33.863 -3.021 33.753 -2.687 33.745 -2.264 c
+h
+34.231 -2.22 m
+34.231 -2.536 34.289 -2.779 34.407 -2.955 c
+34.532 -3.124 34.694 -3.205 34.892 -3.205 c
+35.322 -3.205 35.546 -2.897 35.568 -2.278 c
+35.568 -1.941 l
+35.568 -1.64 35.502 -1.397 35.377 -1.22 c
+35.26 -1.044 35.098 -0.956 34.892 -0.956 c
+34.694 -0.956 34.532 -1.044 34.407 -1.22 c
+34.289 -1.397 34.231 -1.64 34.231 -1.941 c
+h
+36.95 -0.588 m
+36.964 -0.867 l
+37.141 -0.643 37.38 -0.53 37.685 -0.53 c
+38.015 -0.53 38.247 -0.676 38.376 -0.971 c
+38.559 -0.676 38.821 -0.53 39.155 -0.53 c
+39.713 -0.53 39.996 -0.875 40.008 -1.559 c
+40.008 -3.572 l
+39.522 -3.572 l
+39.522 -1.603 l
+39.522 -1.389 39.482 -1.231 39.405 -1.133 c
+39.324 -1.025 39.191 -0.971 39.008 -0.971 c
+38.861 -0.971 38.743 -1.029 38.655 -1.147 c
+38.567 -1.257 38.512 -1.397 38.493 -1.573 c
+38.493 -3.572 l
+38.008 -3.572 l
+38.008 -1.588 l
+37.997 -1.176 37.824 -0.971 37.494 -0.971 c
+37.248 -0.971 37.075 -1.096 36.979 -1.338 c
+36.979 -3.572 l
+36.494 -3.572 l
+36.494 -0.588 l
+h
+40.911 -0.588 m
+40.926 -0.867 l
+41.103 -0.643 41.341 -0.53 41.647 -0.53 c
+41.977 -0.53 42.208 -0.676 42.337 -0.971 c
+42.52 -0.676 42.782 -0.53 43.116 -0.53 c
+43.675 -0.53 43.958 -0.875 43.968 -1.559 c
+43.968 -3.572 l
+43.484 -3.572 l
+43.484 -1.603 l
+43.484 -1.389 43.443 -1.231 43.366 -1.133 c
+43.285 -1.025 43.153 -0.971 42.969 -0.971 c
+42.822 -0.971 42.705 -1.029 42.616 -1.147 c
+42.528 -1.257 42.473 -1.397 42.455 -1.573 c
+42.455 -3.572 l
+41.969 -3.572 l
+41.969 -1.588 l
+41.959 -1.176 41.786 -0.971 41.456 -0.971 c
+41.209 -0.971 41.036 -1.096 40.941 -1.338 c
+40.941 -3.572 l
+40.456 -3.572 l
+40.456 -0.588 l
+h
+44.947 -3.572 -0.5 2.984 re
+44.976 0.205 m
+44.976 0.118 44.949 0.043 44.902 -0.015 c
+44.862 -0.067 44.791 -0.088 44.696 -0.088 c
+44.608 -0.088 44.538 -0.067 44.49 -0.015 c
+44.45 0.043 44.432 0.11 44.432 0.191 c
+44.432 0.278 44.45 0.353 44.49 0.411 c
+44.538 0.47 44.608 0.5 44.696 0.5 c
+44.791 0.5 44.862 0.47 44.902 0.411 c
+44.949 0.353 44.976 0.282 44.976 0.205 c
+46.25 0.132 m
+46.25 -0.588 l
+46.707 -0.588 l
+46.707 -0.985 l
+46.25 -0.985 l
+46.25 -2.837 l
+46.25 -2.955 46.269 -3.043 46.31 -3.102 c
+46.346 -3.161 46.416 -3.19 46.515 -3.19 c
+46.574 -3.19 46.636 -3.183 46.707 -3.161 c
+46.707 -3.572 l
+46.589 -3.609 46.474 -3.631 46.368 -3.631 c
+46.17 -3.631 46.019 -3.565 45.913 -3.425 c
+45.813 -3.29 45.766 -3.095 45.766 -2.837 c
+45.766 -0.985 l
+45.31 -0.985 l
+45.31 -0.588 l
+45.766 -0.588 l
+45.766 0.132 l
+h
+47.173 -4.366 m
+46.879 -4.175 l
+47.056 -3.929 47.147 -3.679 47.158 -3.425 c
+47.158 -2.97 l
+47.658 -2.97 l
+47.658 -3.367 l
+47.658 -3.554 47.607 -3.738 47.511 -3.925 c
+47.423 -4.109 47.309 -4.256 47.173 -4.366 c
+f
+Q
+324.063 101.502 -0.5 2.984 re
+324.092 105.279 m
+324.092 105.192 324.067 105.117 324.019 105.059 c
+323.978 105.007 323.909 104.986 323.814 104.986 c
+323.725 104.986 323.656 105.007 323.608 105.059 c
+323.567 105.117 323.548 105.184 323.548 105.265 c
+323.548 105.352 323.567 105.427 323.608 105.485 c
+323.656 105.544 323.725 105.574 323.814 105.574 c
+323.909 105.574 323.978 105.544 324.019 105.485 c
+324.067 105.427 324.092 105.356 324.092 105.279 c
+325.368 105.206 m
+325.368 104.486 l
+325.823 104.486 l
+325.823 104.089 l
+325.368 104.089 l
+325.368 102.237 l
+325.368 102.119 325.386 102.031 325.426 101.972 c
+325.463 101.913 325.533 101.884 325.632 101.884 c
+325.691 101.884 325.754 101.891 325.823 101.913 c
+325.823 101.502 l
+325.706 101.465 325.592 101.443 325.485 101.443 c
+325.287 101.443 325.136 101.509 325.03 101.649 c
+324.93 101.784 324.882 101.979 324.882 102.237 c
+324.882 104.089 l
+324.427 104.089 l
+324.427 104.486 l
+324.882 104.486 l
+324.882 105.206 l
+h
+327.98 104.177 m
+328.165 104.419 328.4 104.544 328.686 104.544 c
+329.215 104.544 329.484 104.192 329.495 103.486 c
+329.495 101.502 l
+329.009 101.502 l
+329.009 103.457 l
+329.009 103.692 328.969 103.858 328.892 103.956 c
+328.811 104.052 328.693 104.103 328.539 104.103 c
+328.421 104.103 328.311 104.063 328.215 103.986 c
+328.117 103.905 328.04 103.798 327.98 103.663 c
+327.98 101.502 l
+327.496 101.502 l
+327.496 105.735 l
+327.98 105.735 l
+h
+331.564 101.502 m
+331.534 101.568 331.512 101.678 331.504 101.825 c
+331.328 101.568 331.107 101.443 330.843 101.443 c
+330.567 101.443 330.351 101.516 330.197 101.664 c
+330.049 101.818 329.976 102.034 329.976 102.31 c
+329.976 102.611 330.079 102.854 330.284 103.031 c
+330.49 103.214 330.773 103.31 331.137 103.31 c
+331.49 103.31 l
+331.49 103.633 l
+331.49 103.81 331.45 103.931 331.373 104.001 c
+331.292 104.078 331.174 104.118 331.02 104.118 c
+330.872 104.118 330.748 104.074 330.652 103.986 c
+330.564 103.898 330.519 103.787 330.519 103.663 c
+330.035 103.663 l
+330.035 103.81 330.079 103.949 330.167 104.089 c
+330.255 104.236 330.373 104.346 330.519 104.427 c
+330.675 104.504 330.847 104.544 331.034 104.544 c
+331.346 104.544 331.581 104.463 331.739 104.309 c
+331.894 104.162 331.975 103.941 331.975 103.648 c
+331.975 102.148 l
+331.982 101.913 332.019 101.711 332.078 101.546 c
+332.078 101.502 l
+h
+330.916 101.884 m
+331.034 101.884 331.145 101.917 331.255 101.987 c
+331.361 102.053 331.438 102.137 331.49 102.237 c
+331.49 102.942 l
+331.225 102.942 l
+330.99 102.942 330.802 102.891 330.667 102.796 c
+330.538 102.696 330.476 102.553 330.476 102.369 c
+330.476 102.2 330.505 102.079 330.564 102.002 c
+330.63 101.921 330.748 101.884 330.916 101.884 c
+333.996 102.266 m
+333.996 102.372 333.956 102.461 333.879 102.53 c
+333.798 102.607 333.647 102.696 333.422 102.796 c
+333.158 102.902 332.971 102.994 332.865 103.075 c
+332.754 103.152 332.676 103.24 332.629 103.339 c
+332.578 103.434 332.556 103.552 332.556 103.692 c
+332.556 103.935 332.644 104.136 332.82 104.294 c
+332.996 104.46 333.221 104.544 333.497 104.544 c
+333.79 104.544 334.025 104.456 334.202 104.28 c
+334.378 104.111 334.467 103.898 334.467 103.633 c
+333.981 103.633 l
+333.981 103.769 333.93 103.883 333.834 103.971 c
+333.746 104.066 333.632 104.118 333.497 104.118 c
+333.349 104.118 333.235 104.078 333.158 104.001 c
+333.077 103.931 333.04 103.831 333.04 103.706 c
+333.04 103.607 333.07 103.53 333.129 103.471 c
+333.187 103.413 333.327 103.332 333.555 103.236 c
+333.915 103.089 334.162 102.946 334.29 102.81 c
+334.426 102.682 334.496 102.509 334.496 102.295 c
+334.496 102.038 334.4 101.832 334.216 101.678 c
+334.04 101.52 333.805 101.443 333.511 101.443 c
+333.195 101.443 332.942 101.531 332.747 101.707 c
+332.559 101.891 332.468 102.123 332.468 102.399 c
+332.952 102.399 l
+332.96 102.229 333.011 102.097 333.1 102.002 c
+333.195 101.913 333.335 101.869 333.511 101.869 c
+333.665 101.869 333.783 101.902 333.864 101.972 c
+333.952 102.038 333.996 102.137 333.996 102.266 c
+336.612 104.486 m
+336.628 104.162 l
+336.811 104.415 337.054 104.544 337.347 104.544 c
+337.877 104.544 338.145 104.192 338.156 103.486 c
+338.156 101.502 l
+337.671 101.502 l
+337.671 103.457 l
+337.671 103.692 337.63 103.858 337.553 103.956 c
+337.472 104.052 337.355 104.103 337.2 104.103 c
+337.083 104.103 336.973 104.063 336.877 103.986 c
+336.778 103.905 336.701 103.798 336.642 103.663 c
+336.642 101.502 l
+336.157 101.502 l
+336.157 104.486 l
+h
+338.608 103.133 m
+338.608 103.563 338.71 103.905 338.916 104.162 c
+339.13 104.415 339.409 104.544 339.755 104.544 c
+340.096 104.544 340.372 104.415 340.578 104.162 c
+340.79 103.916 340.904 103.582 340.915 103.162 c
+340.915 102.854 l
+340.915 102.42 340.805 102.079 340.592 101.825 c
+340.387 101.568 340.107 101.443 339.755 101.443 c
+339.409 101.443 339.137 101.564 338.931 101.811 c
+338.725 102.053 338.615 102.387 338.608 102.81 c
+h
+339.093 102.854 m
+339.093 102.538 339.152 102.295 339.269 102.119 c
+339.394 101.95 339.556 101.869 339.755 101.869 c
+340.184 101.869 340.408 102.177 340.431 102.796 c
+340.431 103.133 l
+340.431 103.434 340.364 103.677 340.239 103.854 c
+340.121 104.03 339.961 104.118 339.755 104.118 c
+339.556 104.118 339.394 104.03 339.269 103.854 c
+339.152 103.677 339.093 103.434 339.093 103.133 c
+h
+344.705 102.854 m
+344.705 102.383 344.62 102.031 344.454 101.796 c
+344.285 101.56 344.046 101.443 343.734 101.443 c
+343.429 101.443 343.198 101.553 343.043 101.781 c
+343.043 100.355 l
+342.558 100.355 l
+342.558 104.486 l
+342.999 104.486 l
+343.028 104.147 l
+343.183 104.412 343.414 104.544 343.72 104.544 c
+344.05 104.544 344.296 104.427 344.454 104.192 c
+344.62 103.964 344.705 103.625 344.705 103.177 c
+h
+344.219 103.133 m
+344.219 103.464 344.164 103.71 344.057 103.868 c
+343.959 104.022 343.797 104.103 343.572 104.103 c
+343.337 104.103 343.161 103.986 343.043 103.75 c
+343.043 102.208 l
+343.161 101.979 343.341 101.869 343.587 101.869 c
+343.801 101.869 343.959 101.946 344.057 102.104 c
+344.164 102.258 344.219 102.501 344.219 102.825 c
+h
+346.689 101.502 m
+346.66 101.568 346.637 101.678 346.629 101.825 c
+346.454 101.568 346.233 101.443 345.968 101.443 c
+345.693 101.443 345.476 101.516 345.322 101.664 c
+345.175 101.818 345.101 102.034 345.101 102.31 c
+345.101 102.611 345.204 102.854 345.41 103.031 c
+345.615 103.214 345.899 103.31 346.263 103.31 c
+346.615 103.31 l
+346.615 103.633 l
+346.615 103.81 346.575 103.931 346.498 104.001 c
+346.417 104.078 346.299 104.118 346.145 104.118 c
+345.998 104.118 345.873 104.074 345.777 103.986 c
+345.689 103.898 345.645 103.787 345.645 103.663 c
+345.16 103.663 l
+345.16 103.81 345.204 103.949 345.293 104.089 c
+345.38 104.236 345.498 104.346 345.645 104.427 c
+345.8 104.504 345.972 104.544 346.159 104.544 c
+346.471 104.544 346.707 104.463 346.865 104.309 c
+347.019 104.162 347.1 103.941 347.1 103.648 c
+347.1 102.148 l
+347.107 101.913 347.144 101.711 347.203 101.546 c
+347.203 101.502 l
+h
+346.042 101.884 m
+346.159 101.884 346.27 101.917 346.38 101.987 c
+346.486 102.053 346.564 102.137 346.615 102.237 c
+346.615 102.942 l
+346.351 102.942 l
+346.116 102.942 345.928 102.891 345.792 102.796 c
+345.663 102.696 345.601 102.553 345.601 102.369 c
+345.601 102.2 345.63 102.079 345.689 102.002 c
+345.756 101.921 345.873 101.884 346.042 101.884 c
+348.915 104.03 m
+348.846 104.037 348.772 104.045 348.695 104.045 c
+348.438 104.045 348.262 103.905 348.166 103.633 c
+348.166 101.502 l
+347.681 101.502 l
+347.681 104.486 l
+348.152 104.486 l
+348.166 104.177 l
+348.291 104.419 348.474 104.544 348.709 104.544 c
+348.787 104.544 348.85 104.529 348.9 104.5 c
+h
+350.294 101.443 m
+349.919 101.443 349.636 101.549 349.441 101.766 c
+349.243 101.99 349.147 102.318 349.147 102.751 c
+349.147 103.118 l
+349.147 103.559 349.239 103.905 349.426 104.162 c
+349.621 104.415 349.897 104.544 350.249 104.544 c
+350.591 104.544 350.845 104.431 351.014 104.207 c
+351.19 103.978 351.282 103.633 351.293 103.162 c
+351.293 102.854 l
+349.632 102.854 l
+349.632 102.78 l
+349.632 102.457 349.691 102.222 349.808 102.075 c
+349.926 101.936 350.095 101.869 350.323 101.869 c
+350.47 101.869 350.595 101.891 350.705 101.942 c
+350.812 102.002 350.915 102.09 351.014 102.208 c
+351.264 101.899 l
+351.058 101.593 350.735 101.443 350.294 101.443 c
+350.249 104.118 m
+350.043 104.118 349.889 104.049 349.794 103.912 c
+349.694 103.773 349.64 103.559 349.632 103.266 c
+350.808 103.266 l
+350.808 103.339 l
+350.786 103.611 350.735 103.81 350.646 103.927 c
+350.558 104.052 350.426 104.118 350.249 104.118 c
+352.16 104.486 m
+352.175 104.162 l
+352.359 104.415 352.601 104.544 352.896 104.544 c
+353.424 104.544 353.693 104.192 353.704 103.486 c
+353.704 101.502 l
+353.218 101.502 l
+353.218 103.457 l
+353.218 103.692 353.178 103.858 353.101 103.956 c
+353.02 104.052 352.902 104.103 352.748 104.103 c
+352.63 104.103 352.52 104.063 352.425 103.986 c
+352.326 103.905 352.249 103.798 352.19 103.663 c
+352.19 101.502 l
+351.705 101.502 l
+351.705 104.486 l
+h
+354.891 105.206 m
+354.891 104.486 l
+355.346 104.486 l
+355.346 104.089 l
+354.891 104.089 l
+354.891 102.237 l
+354.891 102.119 354.909 102.031 354.949 101.972 c
+354.986 101.913 355.056 101.884 355.155 101.884 c
+355.214 101.884 355.277 101.891 355.346 101.913 c
+355.346 101.502 l
+355.229 101.465 355.115 101.443 355.009 101.443 c
+354.81 101.443 354.66 101.509 354.552 101.649 c
+354.454 101.784 354.406 101.979 354.406 102.237 c
+354.406 104.089 l
+353.95 104.089 l
+353.95 104.486 l
+354.406 104.486 l
+354.406 105.206 l
+h
+357.25 102.266 m
+357.25 102.372 357.21 102.461 357.132 102.53 c
+357.052 102.607 356.9 102.696 356.677 102.796 c
+356.412 102.902 356.225 102.994 356.118 103.075 c
+356.008 103.152 355.931 103.24 355.883 103.339 c
+355.832 103.434 355.809 103.552 355.809 103.692 c
+355.809 103.935 355.898 104.136 356.074 104.294 c
+356.25 104.46 356.474 104.544 356.75 104.544 c
+357.044 104.544 357.28 104.456 357.456 104.28 c
+357.632 104.111 357.721 103.898 357.721 103.633 c
+357.235 103.633 l
+357.235 103.769 357.184 103.883 357.089 103.971 c
+357 104.066 356.886 104.118 356.75 104.118 c
+356.603 104.118 356.489 104.078 356.412 104.001 c
+356.331 103.931 356.295 103.831 356.295 103.706 c
+356.295 103.607 356.324 103.53 356.383 103.471 c
+356.441 103.413 356.582 103.332 356.809 103.236 c
+357.17 103.089 357.415 102.946 357.544 102.81 c
+357.68 102.682 357.75 102.509 357.75 102.295 c
+357.75 102.038 357.654 101.832 357.471 101.678 c
+357.294 101.52 357.059 101.443 356.765 101.443 c
+356.449 101.443 356.195 101.531 356 101.707 c
+355.813 101.891 355.721 102.123 355.721 102.399 c
+356.206 102.399 l
+356.214 102.229 356.265 102.097 356.353 102.002 c
+356.449 101.913 356.588 101.869 356.765 101.869 c
+356.919 101.869 357.037 101.902 357.118 101.972 c
+357.206 102.038 357.25 102.137 357.25 102.266 c
+f
+0.793 0.801 0.129 0.016 K
+0.567 w
+q 1 0 0 1 321.7337 107.7302 cm
+0 0 m
+-4.686 4.844 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 318.5327 111.0419 cm
+0 0 m
+1.507 -0.026 l
+-1.856 1.914 l
+-0.026 -1.511 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 318.5327 111.042 cm
+0 0 m
+1.507 -0.026 l
+-1.856 1.914 l
+-0.026 -1.511 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 K
+0.8 w
+q 1 0 0 1 644.4808 108.7265 cm
+0 0 m
+0.008 -15.25 l
+-6.464 -15.25 l
+-6.468 0 l
+-3.227 3.564 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 640.4863 106.5801 cm
+0 0 m
+1.881 -0.382 l
+1.881 -1.191 l
+-1.103 -0.368 l
+-1.103 0.368 l
+1.881 1.191 l
+1.881 0.382 l
+h
+-1.103 -3.087 m
+-1.103 -2.293 l
+1.969 -2.293 l
+1.66 -1.529 l
+2.322 -1.529 l
+2.91 -2.998 l
+2.91 -3.087 l
+h
+-0.706 -4.12 m
+-0.581 -4.12 -0.478 -4.156 -0.397 -4.237 c
+-0.32 -4.326 -0.279 -4.432 -0.279 -4.561 c
+-0.279 -4.696 -0.32 -4.802 -0.397 -4.883 c
+-0.478 -4.972 -0.581 -5.016 -0.706 -5.016 c
+-0.834 -5.016 -0.937 -4.972 -1.014 -4.883 c
+-1.095 -4.802 -1.133 -4.696 -1.133 -4.561 c
+-1.133 -4.432 -1.095 -4.326 -1.014 -4.237 c
+-0.937 -4.156 -0.834 -4.12 -0.706 -4.12 c
+0.558 -7.886 m
+0 -7.886 -0.427 -7.783 -0.721 -7.578 c
+-1.014 -7.379 -1.162 -7.096 -1.162 -6.724 c
+-1.162 -6.361 -1.022 -6.078 -0.736 -5.872 c
+-0.441 -5.674 -0.03 -5.571 0.5 -5.564 c
+1.263 -5.564 l
+1.811 -5.564 2.234 -5.659 2.528 -5.857 c
+2.822 -6.063 2.969 -6.35 2.969 -6.724 c
+2.969 -7.096 2.822 -7.379 2.528 -7.578 c
+2.241 -7.783 1.83 -7.886 1.294 -7.886 c
+h
+1.367 -7.107 m
+1.679 -7.107 1.914 -7.071 2.072 -7.004 c
+2.238 -6.945 2.322 -6.85 2.322 -6.724 c
+2.322 -6.479 2.032 -6.35 1.454 -6.342 c
+0.455 -6.342 l
+0.132 -6.342 -0.114 -6.372 -0.279 -6.431 c
+-0.437 -6.497 -0.515 -6.596 -0.515 -6.724 c
+-0.515 -6.85 -0.441 -6.945 -0.294 -7.004 c
+-0.14 -7.071 0.091 -7.107 0.397 -7.107 c
+h
+-0.706 -8.32 m
+-0.581 -8.32 -0.478 -8.357 -0.397 -8.437 c
+-0.32 -8.525 -0.279 -8.632 -0.279 -8.76 c
+-0.279 -8.897 -0.32 -9.003 -0.397 -9.084 c
+-0.478 -9.172 -0.581 -9.216 -0.706 -9.216 c
+-0.834 -9.216 -0.937 -9.172 -1.014 -9.084 c
+-1.095 -9.003 -1.133 -8.897 -1.133 -8.76 c
+-1.133 -8.632 -1.095 -8.525 -1.014 -8.437 c
+-0.937 -8.357 -0.834 -8.32 -0.706 -8.32 c
+-1.103 -11.484 m
+-1.103 -10.69 l
+1.969 -10.69 l
+1.66 -9.926 l
+2.322 -9.926 l
+2.91 -11.395 l
+2.91 -11.484 l
+h
+f
+Q
+q 1 0 0 1 573.4436 98.4888 cm
+0 0 m
+-1.058 0 l
+-1.058 -3.572 l
+-1.558 -3.572 l
+-1.558 0 l
+-2.616 0 l
+-2.616 0.44 l
+0 0.44 l
+h
+0.823 -0.897 m
+1.007 -0.655 1.242 -0.53 1.529 -0.53 c
+2.058 -0.53 2.326 -0.882 2.337 -1.588 c
+2.337 -3.572 l
+1.852 -3.572 l
+1.852 -1.617 l
+1.852 -1.382 1.812 -1.216 1.735 -1.118 c
+1.654 -1.022 1.536 -0.971 1.382 -0.971 c
+1.264 -0.971 1.154 -1.011 1.058 -1.088 c
+0.96 -1.169 0.882 -1.276 0.823 -1.411 c
+0.823 -3.572 l
+0.338 -3.572 l
+0.338 0.661 l
+0.823 0.661 l
+h
+3.406 -3.572 -0.5 2.984 re
+3.436 0.205 m
+3.436 0.118 3.41 0.043 3.362 -0.015 c
+3.322 -0.067 3.252 -0.088 3.156 -0.088 c
+3.069 -0.088 2.999 -0.067 2.951 -0.015 c
+2.911 0.043 2.892 0.11 2.892 0.191 c
+2.892 0.278 2.911 0.353 2.951 0.411 c
+2.999 0.47 3.069 0.5 3.156 0.5 c
+3.252 0.5 3.322 0.47 3.362 0.411 c
+3.41 0.353 3.436 0.282 3.436 0.205 c
+5.523 -2.808 m
+5.523 -2.702 5.483 -2.613 5.405 -2.544 c
+5.325 -2.467 5.174 -2.378 4.95 -2.278 c
+4.685 -2.172 4.498 -2.08 4.391 -1.999 c
+4.281 -1.922 4.204 -1.834 4.156 -1.735 c
+4.104 -1.64 4.083 -1.522 4.083 -1.382 c
+4.083 -1.139 4.171 -0.938 4.347 -0.78 c
+4.524 -0.614 4.748 -0.53 5.024 -0.53 c
+5.317 -0.53 5.552 -0.618 5.729 -0.794 c
+5.905 -0.963 5.993 -1.176 5.993 -1.441 c
+5.508 -1.441 l
+5.508 -1.305 5.457 -1.191 5.361 -1.103 c
+5.273 -1.008 5.159 -0.956 5.024 -0.956 c
+4.877 -0.956 4.762 -0.996 4.685 -1.073 c
+4.605 -1.143 4.567 -1.243 4.567 -1.368 c
+4.567 -1.467 4.597 -1.544 4.656 -1.603 c
+4.715 -1.661 4.854 -1.742 5.082 -1.838 c
+5.442 -1.985 5.689 -2.128 5.818 -2.264 c
+5.953 -2.392 6.023 -2.565 6.023 -2.779 c
+6.023 -3.036 5.928 -3.242 5.743 -3.396 c
+5.567 -3.554 5.332 -3.631 5.038 -3.631 c
+4.723 -3.631 4.469 -3.543 4.274 -3.367 c
+4.087 -3.183 3.994 -2.951 3.994 -2.675 c
+4.48 -2.675 l
+4.487 -2.845 4.538 -2.977 4.627 -3.072 c
+4.723 -3.161 4.862 -3.205 5.038 -3.205 c
+5.193 -3.205 5.31 -3.172 5.39 -3.102 c
+5.479 -3.036 5.523 -2.937 5.523 -2.808 c
+8.213 -3.572 -0.5 2.984 re
+8.243 0.205 m
+8.243 0.118 8.216 0.043 8.169 -0.015 c
+8.129 -0.067 8.058 -0.088 7.963 -0.088 c
+7.875 -0.088 7.805 -0.067 7.757 -0.015 c
+7.717 0.043 7.699 0.11 7.699 0.191 c
+7.699 0.278 7.717 0.353 7.757 0.411 c
+7.805 0.47 7.875 0.5 7.963 0.5 c
+8.058 0.5 8.129 0.47 8.169 0.411 c
+8.216 0.353 8.243 0.282 8.243 0.205 c
+10.326 -2.808 m
+10.326 -2.702 10.286 -2.613 10.209 -2.544 c
+10.128 -2.467 9.977 -2.378 9.753 -2.278 c
+9.488 -2.172 9.301 -2.08 9.194 -1.999 c
+9.084 -1.922 9.007 -1.834 8.959 -1.735 c
+8.908 -1.64 8.885 -1.522 8.885 -1.382 c
+8.885 -1.139 8.974 -0.938 9.151 -0.78 c
+9.327 -0.614 9.55 -0.53 9.826 -0.53 c
+10.12 -0.53 10.356 -0.618 10.532 -0.794 c
+10.708 -0.963 10.797 -1.176 10.797 -1.441 c
+10.311 -1.441 l
+10.311 -1.305 10.26 -1.191 10.165 -1.103 c
+10.076 -1.008 9.962 -0.956 9.826 -0.956 c
+9.679 -0.956 9.565 -0.996 9.488 -1.073 c
+9.407 -1.143 9.371 -1.243 9.371 -1.368 c
+9.371 -1.467 9.4 -1.544 9.459 -1.603 c
+9.517 -1.661 9.658 -1.742 9.885 -1.838 c
+10.246 -1.985 10.491 -2.128 10.62 -2.264 c
+10.756 -2.392 10.826 -2.565 10.826 -2.779 c
+10.826 -3.036 10.73 -3.242 10.547 -3.396 c
+10.37 -3.554 10.135 -3.631 9.841 -3.631 c
+9.525 -3.631 9.271 -3.543 9.076 -3.367 c
+8.889 -3.183 8.798 -2.951 8.798 -2.675 c
+9.282 -2.675 l
+9.29 -2.845 9.342 -2.977 9.429 -3.072 c
+9.525 -3.161 9.664 -3.205 9.841 -3.205 c
+9.995 -3.205 10.113 -3.172 10.194 -3.102 c
+10.282 -3.036 10.326 -2.937 10.326 -2.808 c
+14.015 -3.572 m
+13.986 -3.506 13.964 -3.396 13.957 -3.249 c
+13.78 -3.506 13.56 -3.631 13.295 -3.631 c
+13.02 -3.631 12.803 -3.558 12.648 -3.41 c
+12.502 -3.256 12.428 -3.04 12.428 -2.764 c
+12.428 -2.463 12.531 -2.22 12.737 -2.043 c
+12.943 -1.86 13.226 -1.764 13.589 -1.764 c
+13.942 -1.764 l
+13.942 -1.441 l
+13.942 -1.264 13.901 -1.143 13.824 -1.073 c
+13.743 -0.996 13.626 -0.956 13.471 -0.956 c
+13.325 -0.956 13.2 -1 13.104 -1.088 c
+13.016 -1.176 12.972 -1.287 12.972 -1.411 c
+12.486 -1.411 l
+12.486 -1.264 12.531 -1.125 12.619 -0.985 c
+12.708 -0.838 12.825 -0.728 12.972 -0.647 c
+13.126 -0.57 13.299 -0.53 13.486 -0.53 c
+13.799 -0.53 14.034 -0.611 14.192 -0.765 c
+14.346 -0.912 14.427 -1.133 14.427 -1.426 c
+14.427 -2.926 l
+14.435 -3.161 14.471 -3.363 14.53 -3.528 c
+14.53 -3.572 l
+h
+13.369 -3.19 m
+13.486 -3.19 13.596 -3.157 13.707 -3.087 c
+13.814 -3.021 13.891 -2.937 13.942 -2.837 c
+13.942 -2.132 l
+13.677 -2.132 l
+13.442 -2.132 13.255 -2.183 13.119 -2.278 c
+12.991 -2.378 12.928 -2.521 12.928 -2.705 c
+12.928 -2.874 12.957 -2.995 13.016 -3.072 c
+13.082 -3.153 13.2 -3.19 13.369 -3.19 c
+16.845 0.132 m
+16.845 -0.588 l
+17.301 -0.588 l
+17.301 -0.985 l
+16.845 -0.985 l
+16.845 -2.837 l
+16.845 -2.955 16.864 -3.043 16.904 -3.102 c
+16.941 -3.161 17.01 -3.19 17.11 -3.19 c
+17.169 -3.19 17.231 -3.183 17.301 -3.161 c
+17.301 -3.572 l
+17.184 -3.609 17.07 -3.631 16.963 -3.631 c
+16.764 -3.631 16.613 -3.565 16.507 -3.425 c
+16.407 -3.29 16.36 -3.095 16.36 -2.837 c
+16.36 -0.985 l
+15.904 -0.985 l
+15.904 -0.588 l
+16.36 -0.588 l
+16.36 0.132 l
+h
+19.297 -3.572 m
+19.266 -3.506 19.245 -3.396 19.237 -3.249 c
+19.061 -3.506 18.84 -3.631 18.576 -3.631 c
+18.3 -3.631 18.084 -3.558 17.93 -3.41 c
+17.782 -3.256 17.708 -3.04 17.708 -2.764 c
+17.708 -2.463 17.812 -2.22 18.017 -2.043 c
+18.223 -1.86 18.506 -1.764 18.87 -1.764 c
+19.223 -1.764 l
+19.223 -1.441 l
+19.223 -1.264 19.183 -1.143 19.106 -1.073 c
+19.025 -0.996 18.907 -0.956 18.753 -0.956 c
+18.605 -0.956 18.481 -1 18.385 -1.088 c
+18.296 -1.176 18.252 -1.287 18.252 -1.411 c
+17.768 -1.411 l
+17.768 -1.264 17.812 -1.125 17.899 -0.985 c
+17.988 -0.838 18.105 -0.728 18.252 -0.647 c
+18.406 -0.57 18.58 -0.53 18.767 -0.53 c
+19.079 -0.53 19.314 -0.611 19.472 -0.765 c
+19.627 -0.912 19.708 -1.133 19.708 -1.426 c
+19.708 -2.926 l
+19.715 -3.161 19.752 -3.363 19.811 -3.528 c
+19.811 -3.572 l
+h
+18.649 -3.19 m
+18.767 -3.19 18.877 -3.157 18.988 -3.087 c
+19.094 -3.021 19.171 -2.937 19.223 -2.837 c
+19.223 -2.132 l
+18.958 -2.132 l
+18.723 -2.132 18.535 -2.183 18.4 -2.278 c
+18.271 -2.378 18.208 -2.521 18.208 -2.705 c
+18.208 -2.874 18.238 -2.995 18.296 -3.072 c
+18.363 -3.153 18.481 -3.19 18.649 -3.19 c
+20.185 -1.941 m
+20.185 -1.482 20.266 -1.133 20.436 -0.897 c
+20.612 -0.655 20.862 -0.53 21.185 -0.53 c
+21.487 -0.53 21.718 -0.662 21.876 -0.927 c
+21.905 -0.588 l
+22.346 -0.588 l
+22.346 -3.602 l
+22.346 -3.973 22.247 -4.256 22.052 -4.454 c
+21.865 -4.649 21.604 -4.748 21.273 -4.748 c
+21.126 -4.748 20.957 -4.707 20.773 -4.63 c
+20.586 -4.561 20.45 -4.472 20.362 -4.366 c
+20.553 -4.028 l
+20.758 -4.233 20.983 -4.337 21.229 -4.337 c
+21.63 -4.337 21.836 -4.109 21.847 -3.66 c
+21.847 -3.278 l
+21.689 -3.514 21.468 -3.631 21.185 -3.631 c
+20.869 -3.631 20.627 -3.514 20.45 -3.278 c
+20.281 -3.043 20.193 -2.712 20.185 -2.278 c
+h
+20.671 -2.22 m
+20.671 -2.554 20.718 -2.801 20.818 -2.955 c
+20.913 -3.113 21.074 -3.19 21.303 -3.19 c
+21.545 -3.19 21.729 -3.069 21.847 -2.822 c
+21.847 -1.338 l
+21.729 -1.096 21.552 -0.971 21.317 -0.971 c
+21.09 -0.971 20.928 -1.052 20.833 -1.206 c
+20.733 -1.364 20.677 -1.603 20.671 -1.926 c
+h
+22.909 -3.308 m
+22.909 -3.219 22.931 -3.146 22.982 -3.087 c
+23.041 -3.028 23.118 -2.999 23.217 -2.999 c
+23.324 -2.999 23.401 -3.028 23.453 -3.087 c
+23.511 -3.146 23.54 -3.219 23.54 -3.308 c
+23.54 -3.389 23.511 -3.454 23.453 -3.514 c
+23.401 -3.572 23.324 -3.602 23.217 -3.602 c
+23.118 -3.602 23.041 -3.572 22.982 -3.514 c
+22.931 -3.454 22.909 -3.389 22.909 -3.308 c
+26.061 -3.572 -0.5 4.012 re
+27.421 0.132 m
+27.421 -0.588 l
+27.877 -0.588 l
+27.877 -0.985 l
+27.421 -0.985 l
+27.421 -2.837 l
+27.421 -2.955 27.44 -3.043 27.48 -3.102 c
+27.517 -3.161 27.586 -3.19 27.686 -3.19 c
+27.744 -3.19 27.807 -3.183 27.877 -3.161 c
+27.877 -3.572 l
+27.759 -3.609 27.646 -3.631 27.538 -3.631 c
+27.34 -3.631 27.189 -3.565 27.083 -3.425 c
+26.984 -3.29 26.936 -3.095 26.936 -2.837 c
+26.936 -0.985 l
+26.48 -0.985 l
+26.48 -0.588 l
+26.936 -0.588 l
+26.936 0.132 l
+h
+30.078 -3.572 -0.501 4.233 re
+30.647 -1.941 m
+30.647 -1.511 30.751 -1.169 30.956 -0.912 c
+31.17 -0.659 31.449 -0.53 31.794 -0.53 c
+32.136 -0.53 32.411 -0.659 32.617 -0.912 c
+32.83 -1.158 32.945 -1.492 32.955 -1.912 c
+32.955 -2.22 l
+32.955 -2.654 32.845 -2.995 32.632 -3.249 c
+32.426 -3.506 32.147 -3.631 31.794 -3.631 c
+31.449 -3.631 31.177 -3.51 30.971 -3.263 c
+30.765 -3.021 30.655 -2.687 30.647 -2.264 c
+h
+31.133 -2.22 m
+31.133 -2.536 31.191 -2.779 31.309 -2.955 c
+31.434 -3.124 31.596 -3.205 31.794 -3.205 c
+32.224 -3.205 32.448 -2.897 32.471 -2.278 c
+32.471 -1.941 l
+32.471 -1.64 32.404 -1.397 32.279 -1.22 c
+32.162 -1.044 32 -0.956 31.794 -0.956 c
+31.596 -0.956 31.434 -1.044 31.309 -1.22 c
+31.191 -1.397 31.133 -1.64 31.133 -1.941 c
+h
+33.294 -1.941 m
+33.294 -1.511 33.396 -1.169 33.602 -0.912 c
+33.815 -0.659 34.094 -0.53 34.44 -0.53 c
+34.782 -0.53 35.058 -0.659 35.263 -0.912 c
+35.476 -1.158 35.59 -1.492 35.601 -1.912 c
+35.601 -2.22 l
+35.601 -2.654 35.491 -2.995 35.278 -3.249 c
+35.072 -3.506 34.792 -3.631 34.44 -3.631 c
+34.094 -3.631 33.822 -3.51 33.616 -3.263 c
+33.411 -3.021 33.3 -2.687 33.294 -2.264 c
+h
+33.778 -2.22 m
+33.778 -2.536 33.837 -2.779 33.955 -2.955 c
+34.08 -3.124 34.241 -3.205 34.44 -3.205 c
+34.869 -3.205 35.094 -2.897 35.116 -2.278 c
+35.116 -1.941 l
+35.116 -1.64 35.05 -1.397 34.925 -1.22 c
+34.807 -1.044 34.646 -0.956 34.44 -0.956 c
+34.241 -0.956 34.08 -1.044 33.955 -1.22 c
+33.837 -1.397 33.778 -1.64 33.778 -1.941 c
+h
+36.791 -2.205 m
+36.527 -2.5 l
+36.527 -3.572 l
+36.042 -3.572 l
+36.042 0.661 l
+36.527 0.661 l
+36.527 -1.867 l
+37.453 -0.588 l
+38.042 -0.588 l
+37.101 -1.838 l
+38.173 -3.572 l
+37.6 -3.572 l
+h
+39.856 -2.808 m
+39.856 -2.702 39.816 -2.613 39.739 -2.544 c
+39.658 -2.467 39.507 -2.378 39.283 -2.278 c
+39.019 -2.172 38.831 -2.08 38.725 -1.999 c
+38.615 -1.922 38.537 -1.834 38.489 -1.735 c
+38.438 -1.64 38.416 -1.522 38.416 -1.382 c
+38.416 -1.139 38.504 -0.938 38.68 -0.78 c
+38.857 -0.614 39.081 -0.53 39.357 -0.53 c
+39.65 -0.53 39.885 -0.618 40.062 -0.794 c
+40.238 -0.963 40.327 -1.176 40.327 -1.441 c
+39.842 -1.441 l
+39.842 -1.305 39.79 -1.191 39.695 -1.103 c
+39.607 -1.008 39.492 -0.956 39.357 -0.956 c
+39.21 -0.956 39.096 -0.996 39.019 -1.073 c
+38.938 -1.143 38.901 -1.243 38.901 -1.368 c
+38.901 -1.467 38.931 -1.544 38.989 -1.603 c
+39.048 -1.661 39.187 -1.742 39.415 -1.838 c
+39.775 -1.985 40.022 -2.128 40.151 -2.264 c
+40.286 -2.392 40.356 -2.565 40.356 -2.779 c
+40.356 -3.036 40.261 -3.242 40.077 -3.396 c
+39.901 -3.554 39.665 -3.631 39.372 -3.631 c
+39.056 -3.631 38.802 -3.543 38.607 -3.367 c
+38.42 -3.183 38.328 -2.951 38.328 -2.675 c
+38.813 -2.675 l
+38.821 -2.845 38.871 -2.977 38.96 -3.072 c
+39.056 -3.161 39.195 -3.205 39.372 -3.205 c
+39.526 -3.205 39.644 -3.172 39.725 -3.102 c
+39.812 -3.036 39.856 -2.937 39.856 -2.808 c
+42.547 -3.572 -0.5 4.233 re
+43.752 -3.572 -0.5 2.984 re
+43.781 0.205 m
+43.781 0.118 43.756 0.043 43.708 -0.015 c
+43.667 -0.067 43.598 -0.088 43.502 -0.088 c
+43.413 -0.088 43.344 -0.067 43.296 -0.015 c
+43.255 0.043 43.237 0.11 43.237 0.191 c
+43.237 0.278 43.255 0.353 43.296 0.411 c
+43.344 0.47 43.413 0.5 43.502 0.5 c
+43.598 0.5 43.667 0.47 43.708 0.411 c
+43.756 0.353 43.781 0.282 43.781 0.205 c
+45.177 -2.205 m
+44.913 -2.5 l
+44.913 -3.572 l
+44.428 -3.572 l
+44.428 0.661 l
+44.913 0.661 l
+44.913 -1.867 l
+45.839 -0.588 l
+46.427 -0.588 l
+45.487 -1.838 l
+46.559 -3.572 l
+45.986 -3.572 l
+h
+47.808 -3.631 m
+47.434 -3.631 47.151 -3.525 46.956 -3.308 c
+46.758 -3.084 46.663 -2.756 46.663 -2.323 c
+46.663 -1.956 l
+46.663 -1.515 46.754 -1.169 46.941 -0.912 c
+47.136 -0.659 47.411 -0.53 47.764 -0.53 c
+48.107 -0.53 48.36 -0.643 48.529 -0.867 c
+48.705 -1.096 48.797 -1.441 48.808 -1.912 c
+48.808 -2.22 l
+47.147 -2.22 l
+47.147 -2.294 l
+47.147 -2.617 47.206 -2.852 47.324 -2.999 c
+47.441 -3.138 47.61 -3.205 47.839 -3.205 c
+47.985 -3.205 48.11 -3.183 48.22 -3.132 c
+48.327 -3.072 48.429 -2.984 48.529 -2.866 c
+48.779 -3.175 l
+48.573 -3.481 48.25 -3.631 47.808 -3.631 c
+47.764 -0.956 m
+47.559 -0.956 47.405 -1.025 47.309 -1.162 c
+47.21 -1.301 47.155 -1.515 47.147 -1.808 c
+48.323 -1.808 l
+48.323 -1.735 l
+48.301 -1.463 48.25 -1.264 48.161 -1.147 c
+48.074 -1.022 47.941 -0.956 47.764 -0.956 c
+51.954 -3.572 m
+51.924 -3.506 51.902 -3.396 51.895 -3.249 c
+51.719 -3.506 51.498 -3.631 51.234 -3.631 c
+50.958 -3.631 50.742 -3.558 50.586 -3.41 c
+50.44 -3.256 50.366 -3.04 50.366 -2.764 c
+50.366 -2.463 50.469 -2.22 50.675 -2.043 c
+50.881 -1.86 51.164 -1.764 51.527 -1.764 c
+51.88 -1.764 l
+51.88 -1.441 l
+51.88 -1.264 51.839 -1.143 51.762 -1.073 c
+51.682 -0.996 51.564 -0.956 51.41 -0.956 c
+51.263 -0.956 51.138 -1 51.043 -1.088 c
+50.954 -1.176 50.91 -1.287 50.91 -1.411 c
+50.426 -1.411 l
+50.426 -1.264 50.469 -1.125 50.557 -0.985 c
+50.646 -0.838 50.763 -0.728 50.91 -0.647 c
+51.064 -0.57 51.237 -0.53 51.425 -0.53 c
+51.737 -0.53 51.972 -0.611 52.13 -0.765 c
+52.284 -0.912 52.365 -1.133 52.365 -1.426 c
+52.365 -2.926 l
+52.373 -3.161 52.41 -3.363 52.468 -3.528 c
+52.468 -3.572 l
+h
+51.307 -3.19 m
+51.425 -3.19 51.535 -3.157 51.645 -3.087 c
+51.752 -3.021 51.829 -2.937 51.88 -2.837 c
+51.88 -2.132 l
+51.616 -2.132 l
+51.38 -2.132 51.193 -2.183 51.057 -2.278 c
+50.929 -2.378 50.866 -2.521 50.866 -2.705 c
+50.866 -2.874 50.896 -2.995 50.954 -3.072 c
+51.02 -3.153 51.138 -3.19 51.307 -3.19 c
+f
+Q
+q 1 0 0 1 559.3175 89.0664 cm
+0 0 m
+0.574 2.249 l
+1.073 2.249 l
+0.177 -0.735 l
+-0.176 -0.735 l
+-1.072 2.249 l
+-0.588 2.249 l
+h
+2.444 -0.794 m
+2.07 -0.794 1.786 -0.688 1.592 -0.47 c
+1.393 -0.246 1.297 0.081 1.297 0.515 c
+1.297 0.881 l
+1.297 1.323 1.389 1.668 1.577 1.926 c
+1.771 2.179 2.047 2.308 2.4 2.308 c
+2.741 2.308 2.995 2.194 3.165 1.97 c
+3.341 1.741 3.433 1.396 3.444 0.926 c
+3.444 0.617 l
+1.783 0.617 l
+1.783 0.544 l
+1.783 0.22 1.841 -0.015 1.959 -0.162 c
+2.076 -0.301 2.245 -0.368 2.473 -0.368 c
+2.621 -0.368 2.745 -0.345 2.856 -0.294 c
+2.962 -0.235 3.065 -0.147 3.165 -0.029 c
+3.414 -0.338 l
+3.209 -0.643 2.885 -0.794 2.444 -0.794 c
+2.4 1.881 m
+2.194 1.881 2.039 1.812 1.945 1.675 c
+1.845 1.536 1.79 1.323 1.783 1.029 c
+2.959 1.029 l
+2.959 1.103 l
+2.936 1.374 2.885 1.573 2.797 1.691 c
+2.708 1.816 2.577 1.881 2.4 1.881 c
+5.09 1.793 m
+5.02 1.801 4.946 1.808 4.869 1.808 c
+4.612 1.808 4.436 1.668 4.341 1.396 c
+4.341 -0.735 l
+3.855 -0.735 l
+3.855 2.249 l
+4.325 2.249 l
+4.341 1.94 l
+4.465 2.183 4.649 2.308 4.884 2.308 c
+4.961 2.308 5.023 2.293 5.075 2.263 c
+h
+6.901 0.029 m
+6.901 0.135 6.861 0.224 6.784 0.294 c
+6.703 0.371 6.552 0.459 6.328 0.559 c
+6.064 0.665 5.876 0.757 5.77 0.838 c
+5.659 0.915 5.582 1.003 5.534 1.103 c
+5.483 1.198 5.461 1.315 5.461 1.455 c
+5.461 1.698 5.549 1.899 5.725 2.057 c
+5.902 2.223 6.126 2.308 6.402 2.308 c
+6.696 2.308 6.931 2.219 7.107 2.043 c
+7.284 1.874 7.372 1.661 7.372 1.396 c
+6.887 1.396 l
+6.887 1.532 6.835 1.646 6.739 1.735 c
+6.652 1.83 6.538 1.881 6.402 1.881 c
+6.255 1.881 6.141 1.841 6.064 1.764 c
+5.983 1.694 5.946 1.595 5.946 1.469 c
+5.946 1.371 5.976 1.294 6.034 1.234 c
+6.093 1.176 6.232 1.095 6.461 0.999 c
+6.82 0.852 7.067 0.709 7.196 0.573 c
+7.331 0.445 7.402 0.272 7.402 0.058 c
+7.402 -0.199 7.306 -0.405 7.122 -0.559 c
+6.945 -0.717 6.71 -0.794 6.417 -0.794 c
+6.101 -0.794 5.847 -0.706 5.652 -0.53 c
+5.465 -0.345 5.373 -0.114 5.373 0.162 c
+5.858 0.162 l
+5.865 -0.008 5.916 -0.14 6.005 -0.235 c
+6.101 -0.324 6.24 -0.368 6.417 -0.368 c
+6.571 -0.368 6.689 -0.335 6.77 -0.264 c
+6.857 -0.199 6.901 -0.1 6.901 0.029 c
+8.386 -0.735 -0.5 2.984 re
+8.416 3.042 m
+8.416 2.955 8.389 2.881 8.342 2.822 c
+8.302 2.77 8.231 2.749 8.136 2.749 c
+8.048 2.749 7.978 2.77 7.93 2.822 c
+7.89 2.881 7.872 2.947 7.872 3.028 c
+7.872 3.116 7.89 3.19 7.93 3.248 c
+7.978 3.308 8.048 3.337 8.136 3.337 c
+8.231 3.337 8.302 3.308 8.342 3.248 c
+8.389 3.19 8.416 3.119 8.416 3.042 c
+8.96 0.897 m
+8.96 1.326 9.062 1.668 9.268 1.926 c
+9.481 2.179 9.76 2.308 10.106 2.308 c
+10.448 2.308 10.723 2.179 10.93 1.926 c
+11.142 1.679 11.256 1.345 11.267 0.926 c
+11.267 0.617 l
+11.267 0.183 11.157 -0.158 10.944 -0.412 c
+10.738 -0.669 10.459 -0.794 10.106 -0.794 c
+9.76 -0.794 9.488 -0.673 9.283 -0.426 c
+9.077 -0.183 8.967 0.151 8.96 0.573 c
+h
+9.444 0.617 m
+9.444 0.301 9.503 0.058 9.621 -0.118 c
+9.746 -0.287 9.908 -0.368 10.106 -0.368 c
+10.536 -0.368 10.76 -0.059 10.782 0.559 c
+10.782 0.897 l
+10.782 1.198 10.716 1.44 10.591 1.617 c
+10.473 1.793 10.311 1.881 10.106 1.881 c
+9.908 1.881 9.746 1.793 9.621 1.617 c
+9.503 1.44 9.444 1.198 9.444 0.897 c
+h
+12.16 2.249 m
+12.175 1.926 l
+12.358 2.179 12.601 2.308 12.895 2.308 c
+13.424 2.308 13.692 1.955 13.704 1.249 c
+13.704 -0.735 l
+13.218 -0.735 l
+13.218 1.22 l
+13.218 1.455 13.178 1.621 13.101 1.72 c
+13.02 1.816 12.902 1.866 12.748 1.866 c
+12.63 1.866 12.52 1.826 12.424 1.749 c
+12.326 1.668 12.248 1.562 12.189 1.426 c
+12.189 -0.735 l
+11.705 -0.735 l
+11.705 2.249 l
+h
+16.9 0.029 m
+16.9 0.135 16.86 0.224 16.783 0.294 c
+16.702 0.371 16.551 0.459 16.327 0.559 c
+16.063 0.665 15.875 0.757 15.769 0.838 c
+15.659 0.915 15.581 1.003 15.533 1.103 c
+15.482 1.198 15.46 1.315 15.46 1.455 c
+15.46 1.698 15.549 1.899 15.724 2.057 c
+15.901 2.223 16.125 2.308 16.401 2.308 c
+16.694 2.308 16.93 2.219 17.106 2.043 c
+17.282 1.874 17.371 1.661 17.371 1.396 c
+16.886 1.396 l
+16.886 1.532 16.835 1.646 16.739 1.735 c
+16.651 1.83 16.536 1.881 16.401 1.881 c
+16.254 1.881 16.14 1.841 16.063 1.764 c
+15.982 1.694 15.946 1.595 15.946 1.469 c
+15.946 1.371 15.975 1.294 16.033 1.234 c
+16.092 1.176 16.231 1.095 16.459 0.999 c
+16.819 0.852 17.066 0.709 17.195 0.573 c
+17.33 0.445 17.4 0.272 17.4 0.058 c
+17.4 -0.199 17.305 -0.405 17.121 -0.559 c
+16.945 -0.717 16.709 -0.794 16.416 -0.794 c
+16.1 -0.794 15.846 -0.706 15.651 -0.53 c
+15.464 -0.345 15.372 -0.114 15.372 0.162 c
+15.857 0.162 l
+15.865 -0.008 15.915 -0.14 16.004 -0.235 c
+16.1 -0.324 16.239 -0.368 16.416 -0.368 c
+16.57 -0.368 16.688 -0.335 16.769 -0.264 c
+16.856 -0.199 16.9 -0.1 16.9 0.029 c
+17.756 0.897 m
+17.756 1.326 17.86 1.668 18.066 1.926 c
+18.279 2.179 18.558 2.308 18.903 2.308 c
+19.245 2.308 19.52 2.179 19.726 1.926 c
+19.94 1.679 20.054 1.345 20.064 0.926 c
+20.064 0.617 l
+20.064 0.183 19.954 -0.158 19.741 -0.412 c
+19.535 -0.669 19.256 -0.794 18.903 -0.794 c
+18.558 -0.794 18.286 -0.673 18.08 -0.426 c
+17.874 -0.183 17.764 0.151 17.756 0.573 c
+h
+18.242 0.617 m
+18.242 0.301 18.301 0.058 18.418 -0.118 c
+18.543 -0.287 18.705 -0.368 18.903 -0.368 c
+19.333 -0.368 19.557 -0.059 19.58 0.559 c
+19.58 0.897 l
+19.58 1.198 19.514 1.44 19.389 1.617 c
+19.271 1.793 19.109 1.881 18.903 1.881 c
+18.705 1.881 18.543 1.793 18.418 1.617 c
+18.301 1.44 18.242 1.198 18.242 0.897 c
+h
+22.236 -0.735 -0.5 2.984 re
+22.265 3.042 m
+22.265 2.955 22.24 2.881 22.192 2.822 c
+22.152 2.77 22.082 2.749 21.986 2.749 c
+21.898 2.749 21.829 2.77 21.781 2.822 c
+21.74 2.881 21.722 2.947 21.722 3.028 c
+21.722 3.116 21.74 3.19 21.781 3.248 c
+21.829 3.308 21.898 3.337 21.986 3.337 c
+22.082 3.337 22.152 3.308 22.192 3.248 c
+22.24 3.19 22.265 3.119 22.265 3.042 c
+23.541 2.969 m
+23.541 2.249 l
+23.996 2.249 l
+23.996 1.852 l
+23.541 1.852 l
+23.541 0 l
+23.541 -0.118 23.559 -0.206 23.599 -0.264 c
+23.636 -0.324 23.707 -0.353 23.805 -0.353 c
+23.865 -0.353 23.927 -0.345 23.996 -0.324 c
+23.996 -0.735 l
+23.879 -0.771 23.765 -0.794 23.659 -0.794 c
+23.46 -0.794 23.31 -0.728 23.202 -0.588 c
+23.104 -0.452 23.056 -0.258 23.056 0 c
+23.056 1.852 l
+22.6 1.852 l
+22.6 2.249 l
+23.056 2.249 l
+23.056 2.969 l
+h
+24.787 3.19 m
+24.742 2.175 l
+24.375 2.175 l
+24.39 3.499 l
+24.787 3.499 l
+h
+26.613 0.029 m
+26.613 0.135 26.572 0.224 26.495 0.294 c
+26.414 0.371 26.263 0.459 26.04 0.559 c
+25.775 0.665 25.588 0.757 25.481 0.838 c
+25.371 0.915 25.294 1.003 25.246 1.103 c
+25.195 1.198 25.172 1.315 25.172 1.455 c
+25.172 1.698 25.261 1.899 25.437 2.057 c
+25.613 2.223 25.837 2.308 26.113 2.308 c
+26.407 2.308 26.643 2.219 26.819 2.043 c
+26.995 1.874 27.083 1.661 27.083 1.396 c
+26.598 1.396 l
+26.598 1.532 26.547 1.646 26.452 1.735 c
+26.363 1.83 26.249 1.881 26.113 1.881 c
+25.966 1.881 25.852 1.841 25.775 1.764 c
+25.694 1.694 25.658 1.595 25.658 1.469 c
+25.658 1.371 25.687 1.294 25.746 1.234 c
+25.804 1.176 25.944 1.095 26.172 0.999 c
+26.533 0.852 26.778 0.709 26.907 0.573 c
+27.042 0.445 27.113 0.272 27.113 0.058 c
+27.113 -0.199 27.017 -0.405 26.834 -0.559 c
+26.657 -0.717 26.422 -0.794 26.128 -0.794 c
+25.812 -0.794 25.558 -0.706 25.363 -0.53 c
+25.176 -0.345 25.084 -0.114 25.084 0.162 c
+25.569 0.162 l
+25.577 -0.008 25.629 -0.14 25.716 -0.235 c
+25.812 -0.324 25.951 -0.368 26.128 -0.368 c
+26.282 -0.368 26.4 -0.335 26.481 -0.264 c
+26.569 -0.199 26.613 -0.1 26.613 0.029 c
+30.92 0.617 m
+30.92 0.147 30.836 -0.206 30.67 -0.441 c
+30.501 -0.676 30.262 -0.794 29.949 -0.794 c
+29.645 -0.794 29.413 -0.684 29.259 -0.455 c
+29.259 -1.881 l
+28.773 -1.881 l
+28.773 2.249 l
+29.215 2.249 l
+29.244 1.911 l
+29.398 2.175 29.629 2.308 29.935 2.308 c
+30.265 2.308 30.512 2.19 30.67 1.955 c
+30.836 1.727 30.92 1.389 30.92 0.941 c
+h
+30.435 0.897 m
+30.435 1.228 30.379 1.473 30.273 1.631 c
+30.174 1.786 30.012 1.866 29.788 1.866 c
+29.552 1.866 29.376 1.749 29.259 1.514 c
+29.259 -0.029 l
+29.376 -0.258 29.556 -0.368 29.803 -0.368 c
+30.016 -0.368 30.174 -0.291 30.273 -0.133 c
+30.379 0.022 30.435 0.264 30.435 0.588 c
+h
+32.614 1.793 m
+32.544 1.801 32.471 1.808 32.393 1.808 c
+32.137 1.808 31.96 1.668 31.864 1.396 c
+31.864 -0.735 l
+31.379 -0.735 l
+31.379 2.249 l
+31.85 2.249 l
+31.864 1.94 l
+31.989 2.183 32.173 2.308 32.408 2.308 c
+32.486 2.308 32.548 2.293 32.599 2.263 c
+h
+32.831 0.897 m
+32.831 1.326 32.933 1.668 33.139 1.926 c
+33.353 2.179 33.631 2.308 33.978 2.308 c
+34.319 2.308 34.595 2.179 34.801 1.926 c
+35.013 1.679 35.127 1.345 35.138 0.926 c
+35.138 0.617 l
+35.138 0.183 35.028 -0.158 34.815 -0.412 c
+34.609 -0.669 34.33 -0.794 33.978 -0.794 c
+33.631 -0.794 33.36 -0.673 33.154 -0.426 c
+32.948 -0.183 32.838 0.151 32.831 0.573 c
+h
+33.315 0.617 m
+33.315 0.301 33.375 0.058 33.492 -0.118 c
+33.617 -0.287 33.779 -0.368 33.978 -0.368 c
+34.408 -0.368 34.631 -0.059 34.653 0.559 c
+34.653 0.897 l
+34.653 1.198 34.587 1.44 34.462 1.617 c
+34.344 1.793 34.183 1.881 33.978 1.881 c
+33.779 1.881 33.617 1.793 33.492 1.617 c
+33.375 1.44 33.315 1.198 33.315 0.897 c
+h
+37.737 0.617 m
+37.737 0.147 37.652 -0.206 37.487 -0.441 c
+37.317 -0.676 37.076 -0.794 36.752 -0.794 c
+36.436 -0.794 36.201 -0.658 36.046 -0.382 c
+36.017 -0.735 l
+35.576 -0.735 l
+35.576 3.499 l
+36.06 3.499 l
+36.06 1.926 l
+36.215 2.179 36.447 2.308 36.752 2.308 c
+37.076 2.308 37.317 2.19 37.487 1.955 c
+37.652 1.72 37.737 1.371 37.737 0.912 c
+h
+37.251 0.897 m
+37.251 1.249 37.2 1.5 37.105 1.646 c
+37.005 1.793 36.843 1.866 36.619 1.866 c
+36.373 1.866 36.185 1.727 36.06 1.455 c
+36.06 0.044 l
+36.178 -0.22 36.37 -0.353 36.634 -0.353 c
+36.847 -0.353 37.005 -0.279 37.105 -0.133 c
+37.2 0.022 37.251 0.264 37.251 0.588 c
+h
+39.71 -0.735 m
+39.68 -0.669 39.659 -0.559 39.651 -0.412 c
+39.474 -0.669 39.254 -0.794 38.99 -0.794 c
+38.714 -0.794 38.497 -0.721 38.343 -0.573 c
+38.196 -0.419 38.122 -0.202 38.122 0.073 c
+38.122 0.374 38.225 0.617 38.431 0.794 c
+38.637 0.977 38.919 1.073 39.283 1.073 c
+39.636 1.073 l
+39.636 1.396 l
+39.636 1.573 39.596 1.694 39.519 1.764 c
+39.438 1.841 39.32 1.881 39.166 1.881 c
+39.019 1.881 38.894 1.837 38.799 1.749 c
+38.71 1.661 38.666 1.55 38.666 1.426 c
+38.181 1.426 l
+38.181 1.573 38.225 1.712 38.313 1.852 c
+38.402 1.999 38.519 2.109 38.666 2.19 c
+38.82 2.267 38.994 2.308 39.181 2.308 c
+39.493 2.308 39.728 2.227 39.886 2.072 c
+40.041 1.926 40.122 1.705 40.122 1.411 c
+40.122 -0.088 l
+40.129 -0.324 40.166 -0.526 40.224 -0.691 c
+40.224 -0.735 l
+h
+39.063 -0.353 m
+39.181 -0.353 39.291 -0.32 39.401 -0.25 c
+39.507 -0.183 39.584 -0.1 39.636 0 c
+39.636 0.706 l
+39.372 0.706 l
+39.137 0.706 38.949 0.654 38.813 0.559 c
+38.684 0.459 38.622 0.316 38.622 0.133 c
+38.622 -0.037 38.651 -0.158 38.71 -0.235 c
+38.776 -0.316 38.894 -0.353 39.063 -0.353 c
+42.863 0.617 m
+42.863 0.147 42.778 -0.206 42.613 -0.441 c
+42.444 -0.676 42.202 -0.794 41.878 -0.794 c
+41.562 -0.794 41.327 -0.658 41.172 -0.382 c
+41.143 -0.735 l
+40.702 -0.735 l
+40.702 3.499 l
+41.187 3.499 l
+41.187 1.926 l
+41.342 2.179 41.573 2.308 41.878 2.308 c
+42.202 2.308 42.444 2.19 42.613 1.955 c
+42.778 1.72 42.863 1.371 42.863 0.912 c
+h
+42.377 0.897 m
+42.377 1.249 42.327 1.5 42.231 1.646 c
+42.132 1.793 41.97 1.866 41.745 1.866 c
+41.5 1.866 41.312 1.727 41.187 1.455 c
+41.187 0.044 l
+41.305 -0.22 41.496 -0.353 41.76 -0.353 c
+41.974 -0.353 42.132 -0.279 42.231 -0.133 c
+42.327 0.022 42.377 0.264 42.377 0.588 c
+h
+43.833 -0.735 -0.5 4.233 re
+45.317 0.073 m
+45.861 2.249 l
+46.376 2.249 l
+45.406 -1.161 l
+45.336 -1.415 45.233 -1.606 45.097 -1.735 c
+44.957 -1.87 44.806 -1.941 44.642 -1.941 c
+44.571 -1.941 44.487 -1.926 44.392 -1.897 c
+44.392 -1.484 l
+44.494 -1.5 l
+44.631 -1.5 44.737 -1.463 44.818 -1.396 c
+44.906 -1.326 44.972 -1.209 45.024 -1.043 c
+45.112 -0.706 l
+44.245 2.249 l
+44.773 2.249 l
+h
+49.433 -0.735 m
+49.404 -0.669 49.382 -0.559 49.375 -0.412 c
+49.198 -0.669 48.978 -0.794 48.713 -0.794 c
+48.438 -0.794 48.22 -0.721 48.066 -0.573 c
+47.919 -0.419 47.846 -0.202 47.846 0.073 c
+47.846 0.374 47.949 0.617 48.155 0.794 c
+48.361 0.977 48.643 1.073 49.007 1.073 c
+49.36 1.073 l
+49.36 1.396 l
+49.36 1.573 49.319 1.694 49.242 1.764 c
+49.161 1.841 49.044 1.881 48.889 1.881 c
+48.742 1.881 48.617 1.837 48.521 1.749 c
+48.434 1.661 48.39 1.55 48.39 1.426 c
+47.904 1.426 l
+47.904 1.573 47.949 1.712 48.037 1.852 c
+48.125 1.999 48.243 2.109 48.39 2.19 c
+48.544 2.267 48.717 2.308 48.904 2.308 c
+49.217 2.308 49.452 2.227 49.61 2.072 c
+49.764 1.926 49.845 1.705 49.845 1.411 c
+49.845 -0.088 l
+49.853 -0.324 49.889 -0.526 49.948 -0.691 c
+49.948 -0.735 l
+h
+48.787 -0.353 m
+48.904 -0.353 49.014 -0.32 49.124 -0.25 c
+49.231 -0.183 49.308 -0.1 49.36 0 c
+49.36 0.706 l
+49.095 0.706 l
+48.86 0.706 48.673 0.654 48.536 0.559 c
+48.408 0.459 48.346 0.316 48.346 0.133 c
+48.346 -0.037 48.375 -0.158 48.434 -0.235 c
+48.5 -0.316 48.617 -0.353 48.787 -0.353 c
+50.881 2.249 m
+50.896 1.926 l
+51.079 2.179 51.322 2.308 51.616 2.308 c
+52.145 2.308 52.413 1.955 52.425 1.249 c
+52.425 -0.735 l
+51.939 -0.735 l
+51.939 1.22 l
+51.939 1.455 51.899 1.621 51.822 1.72 c
+51.741 1.816 51.623 1.866 51.469 1.866 c
+51.351 1.866 51.241 1.826 51.146 1.749 c
+51.046 1.668 50.969 1.562 50.911 1.426 c
+50.911 -0.735 l
+50.425 -0.735 l
+50.425 2.249 l
+h
+54.078 0.897 m
+54.078 1.326 54.181 1.668 54.387 1.926 c
+54.6 2.179 54.879 2.308 55.225 2.308 c
+55.567 2.308 55.842 2.179 56.047 1.926 c
+56.261 1.679 56.375 1.345 56.386 0.926 c
+56.386 0.617 l
+56.386 0.183 56.276 -0.158 56.062 -0.412 c
+55.856 -0.669 55.577 -0.794 55.225 -0.794 c
+54.879 -0.794 54.607 -0.673 54.401 -0.426 c
+54.196 -0.183 54.086 0.151 54.078 0.573 c
+h
+54.563 0.617 m
+54.563 0.301 54.622 0.058 54.74 -0.118 c
+54.865 -0.287 55.027 -0.368 55.225 -0.368 c
+55.655 -0.368 55.879 -0.059 55.901 0.559 c
+55.901 0.897 l
+55.901 1.198 55.835 1.44 55.71 1.617 c
+55.592 1.793 55.43 1.881 55.225 1.881 c
+55.027 1.881 54.865 1.793 54.74 1.617 c
+54.622 1.44 54.563 1.198 54.563 0.897 c
+h
+58.984 0.617 m
+58.984 0.147 58.9 -0.206 58.734 -0.441 c
+58.565 -0.676 58.322 -0.794 57.999 -0.794 c
+57.683 -0.794 57.448 -0.658 57.294 -0.382 c
+57.264 -0.735 l
+56.823 -0.735 l
+56.823 3.499 l
+57.308 3.499 l
+57.308 1.926 l
+57.462 2.179 57.694 2.308 57.999 2.308 c
+58.322 2.308 58.565 2.19 58.734 1.955 c
+58.9 1.72 58.984 1.371 58.984 0.912 c
+h
+58.499 0.897 m
+58.499 1.249 58.447 1.5 58.352 1.646 c
+58.252 1.793 58.091 1.866 57.867 1.866 c
+57.62 1.866 57.433 1.727 57.308 1.455 c
+57.308 0.044 l
+57.425 -0.22 57.616 -0.353 57.882 -0.353 c
+58.094 -0.353 58.252 -0.279 58.352 -0.133 c
+58.447 0.022 58.499 0.264 58.499 0.588 c
+h
+59.929 2.249 m
+59.929 -1.118 l
+59.918 -1.665 59.696 -1.941 59.267 -1.941 c
+59.168 -1.941 59.079 -1.926 59.002 -1.897 c
+59.002 -1.484 l
+59.054 -1.492 59.112 -1.5 59.193 -1.5 c
+59.27 -1.5 59.33 -1.47 59.37 -1.411 c
+59.418 -1.353 59.443 -1.242 59.443 -1.087 c
+59.443 2.249 l
+h
+59.943 3.042 m
+59.943 2.955 59.918 2.881 59.87 2.822 c
+59.829 2.77 59.76 2.749 59.664 2.749 c
+59.575 2.749 59.505 2.77 59.458 2.822 c
+59.418 2.881 59.399 2.947 59.399 3.028 c
+59.399 3.116 59.418 3.19 59.458 3.248 c
+59.505 3.308 59.575 3.337 59.664 3.337 c
+59.76 3.337 59.829 3.308 59.87 3.248 c
+59.918 3.19 59.943 3.119 59.943 3.042 c
+61.67 -0.794 m
+61.296 -0.794 61.013 -0.688 60.818 -0.47 c
+60.619 -0.246 60.523 0.081 60.523 0.515 c
+60.523 0.881 l
+60.523 1.323 60.616 1.668 60.803 1.926 c
+60.997 2.179 61.273 2.308 61.626 2.308 c
+61.967 2.308 62.221 2.194 62.391 1.97 c
+62.567 1.741 62.659 1.396 62.67 0.926 c
+62.67 0.617 l
+61.009 0.617 l
+61.009 0.544 l
+61.009 0.22 61.067 -0.015 61.185 -0.162 c
+61.302 -0.301 61.472 -0.368 61.699 -0.368 c
+61.847 -0.368 61.971 -0.345 62.082 -0.294 c
+62.189 -0.235 62.291 -0.147 62.391 -0.029 c
+62.64 -0.338 l
+62.435 -0.643 62.111 -0.794 61.67 -0.794 c
+61.626 1.881 m
+61.42 1.881 61.266 1.812 61.171 1.675 c
+61.071 1.536 61.016 1.323 61.009 1.029 c
+62.185 1.029 l
+62.185 1.103 l
+62.162 1.374 62.111 1.573 62.023 1.691 c
+61.935 1.816 61.803 1.881 61.626 1.881 c
+64.11 -0.368 m
+64.276 -0.368 64.408 -0.32 64.507 -0.22 c
+64.602 -0.125 64.658 0.018 64.669 0.206 c
+65.125 0.206 l
+65.113 -0.081 65.011 -0.32 64.816 -0.515 c
+64.629 -0.702 64.394 -0.794 64.11 -0.794 c
+63.746 -0.794 63.467 -0.676 63.272 -0.441 c
+63.074 -0.206 62.979 0.139 62.979 0.603 c
+62.979 0.926 l
+62.979 1.374 63.07 1.72 63.258 1.955 c
+63.453 2.19 63.735 2.308 64.11 2.308 c
+64.411 2.308 64.654 2.209 64.83 2.014 c
+65.014 1.816 65.113 1.55 65.125 1.22 c
+64.669 1.22 l
+64.647 1.444 64.588 1.61 64.492 1.72 c
+64.404 1.826 64.276 1.881 64.11 1.881 c
+63.893 1.881 63.731 1.808 63.625 1.661 c
+63.526 1.521 63.471 1.294 63.463 0.97 c
+63.463 0.588 l
+63.463 0.235 63.511 -0.015 63.611 -0.162 c
+63.717 -0.301 63.883 -0.368 64.11 -0.368 c
+66.15 2.969 m
+66.15 2.249 l
+66.605 2.249 l
+66.605 1.852 l
+66.15 1.852 l
+66.15 0 l
+66.15 -0.118 66.168 -0.206 66.208 -0.264 c
+66.245 -0.324 66.315 -0.353 66.414 -0.353 c
+66.474 -0.353 66.536 -0.345 66.605 -0.324 c
+66.605 -0.735 l
+66.488 -0.771 66.374 -0.794 66.268 -0.794 c
+66.069 -0.794 65.919 -0.728 65.811 -0.588 c
+65.713 -0.452 65.664 -0.258 65.664 0 c
+65.664 1.852 l
+65.209 1.852 l
+65.209 2.249 l
+65.664 2.249 l
+65.664 2.969 l
+h
+f
+Q
+q 1 0 0 1 594.1917 83.3777 cm
+0 0 m
+0 0.478 0.062 0.929 0.191 1.353 c
+0.326 1.783 0.515 2.153 0.75 2.469 c
+0.896 2.664 1.036 2.807 1.176 2.896 c
+1.263 2.558 l
+1.047 2.352 0.867 2.032 0.72 1.602 c
+0.58 1.18 0.507 0.709 0.5 0.191 c
+0.484 -0.029 l
+0.484 -0.676 0.577 -1.249 0.764 -1.749 c
+0.9 -2.12 1.066 -2.41 1.263 -2.616 c
+1.176 -2.925 l
+0.999 -2.815 0.827 -2.631 0.661 -2.367 c
+0.22 -1.708 0 -0.922 0 0 c
+3.197 -1.631 m
+3.167 -1.565 3.145 -1.455 3.138 -1.309 c
+2.961 -1.565 2.741 -1.691 2.477 -1.691 c
+2.201 -1.691 1.984 -1.617 1.83 -1.469 c
+1.683 -1.315 1.61 -1.099 1.61 -0.823 c
+1.61 -0.522 1.712 -0.279 1.918 -0.103 c
+2.123 0.081 2.406 0.177 2.77 0.177 c
+3.123 0.177 l
+3.123 0.5 l
+3.123 0.676 3.083 0.798 3.006 0.867 c
+2.925 0.945 2.807 0.985 2.653 0.985 c
+2.506 0.985 2.381 0.941 2.285 0.852 c
+2.198 0.765 2.153 0.654 2.153 0.53 c
+1.668 0.53 l
+1.668 0.676 1.712 0.816 1.801 0.956 c
+1.888 1.103 2.007 1.213 2.153 1.294 c
+2.308 1.371 2.48 1.411 2.668 1.411 c
+2.98 1.411 3.215 1.33 3.373 1.176 c
+3.528 1.029 3.609 0.808 3.609 0.515 c
+3.609 -0.985 l
+3.615 -1.22 3.653 -1.422 3.711 -1.587 c
+3.711 -1.631 l
+h
+2.55 -1.249 m
+2.668 -1.249 2.778 -1.216 2.888 -1.147 c
+2.994 -1.08 3.071 -0.996 3.123 -0.897 c
+3.123 -0.191 l
+2.859 -0.191 l
+2.624 -0.191 2.437 -0.243 2.3 -0.338 c
+2.171 -0.437 2.109 -0.58 2.109 -0.764 c
+2.109 -0.933 2.138 -1.055 2.198 -1.132 c
+2.263 -1.213 2.381 -1.249 2.55 -1.249 c
+4.644 1.353 m
+4.659 1.029 l
+4.843 1.282 5.086 1.411 5.379 1.411 c
+5.909 1.411 6.177 1.058 6.188 0.353 c
+6.188 -1.631 l
+5.703 -1.631 l
+5.703 0.324 l
+5.703 0.559 5.662 0.724 5.585 0.823 c
+5.504 0.919 5.387 0.97 5.232 0.97 c
+5.115 0.97 5.005 0.929 4.909 0.852 c
+4.81 0.771 4.733 0.665 4.674 0.53 c
+4.674 -1.631 l
+4.189 -1.631 l
+4.189 1.353 l
+h
+7.198 1.353 m
+7.214 1.029 l
+7.397 1.282 7.64 1.411 7.933 1.411 c
+8.463 1.411 8.731 1.058 8.742 0.353 c
+8.742 -1.631 l
+8.257 -1.631 l
+8.257 0.324 l
+8.257 0.559 8.216 0.724 8.139 0.823 c
+8.058 0.919 7.941 0.97 7.786 0.97 c
+7.669 0.97 7.559 0.929 7.463 0.852 c
+7.364 0.771 7.287 0.665 7.228 0.53 c
+7.228 -1.631 l
+6.743 -1.631 l
+6.743 1.353 l
+h
+9.19 0 m
+9.19 0.43 9.294 0.771 9.499 1.029 c
+9.712 1.282 9.992 1.411 10.337 1.411 c
+10.678 1.411 10.954 1.282 11.16 1.029 c
+11.374 0.783 11.487 0.449 11.498 0.029 c
+11.498 -0.279 l
+11.498 -0.713 11.388 -1.055 11.175 -1.309 c
+10.969 -1.565 10.69 -1.691 10.337 -1.691 c
+9.992 -1.691 9.72 -1.569 9.514 -1.323 c
+9.308 -1.08 9.198 -0.746 9.19 -0.324 c
+h
+9.676 -0.279 m
+9.676 -0.595 9.734 -0.838 9.851 -1.014 c
+9.977 -1.183 10.138 -1.264 10.337 -1.264 c
+10.767 -1.264 10.991 -0.956 11.013 -0.338 c
+11.013 0 l
+11.013 0.301 10.946 0.544 10.822 0.721 c
+10.705 0.897 10.543 0.985 10.337 0.985 c
+10.138 0.985 9.977 0.897 9.851 0.721 c
+9.734 0.544 9.676 0.301 9.676 0 c
+h
+12.571 2.072 m
+12.571 1.353 l
+13.026 1.353 l
+13.026 0.956 l
+12.571 0.956 l
+12.571 -0.897 l
+12.571 -1.014 12.59 -1.103 12.629 -1.161 c
+12.667 -1.22 12.737 -1.249 12.835 -1.249 c
+12.895 -1.249 12.957 -1.242 13.026 -1.22 c
+13.026 -1.631 l
+12.909 -1.668 12.795 -1.691 12.689 -1.691 c
+12.49 -1.691 12.34 -1.625 12.233 -1.484 c
+12.134 -1.349 12.086 -1.154 12.086 -0.897 c
+12.086 0.956 l
+11.63 0.956 l
+11.63 1.353 l
+12.086 1.353 l
+12.086 2.072 l
+h
+15.022 -1.631 m
+14.993 -1.565 14.971 -1.455 14.963 -1.309 c
+14.786 -1.565 14.566 -1.691 14.302 -1.691 c
+14.026 -1.691 13.809 -1.617 13.655 -1.469 c
+13.508 -1.315 13.435 -1.099 13.435 -0.823 c
+13.435 -0.522 13.537 -0.279 13.743 -0.103 c
+13.949 0.081 14.232 0.177 14.596 0.177 c
+14.948 0.177 l
+14.948 0.5 l
+14.948 0.676 14.908 0.798 14.831 0.867 c
+14.75 0.945 14.632 0.985 14.478 0.985 c
+14.331 0.985 14.206 0.941 14.111 0.852 c
+14.023 0.765 13.978 0.654 13.978 0.53 c
+13.493 0.53 l
+13.493 0.676 13.537 0.816 13.626 0.956 c
+13.714 1.103 13.832 1.213 13.978 1.294 c
+14.133 1.371 14.306 1.411 14.493 1.411 c
+14.805 1.411 15.041 1.33 15.199 1.176 c
+15.353 1.029 15.434 0.808 15.434 0.515 c
+15.434 -0.985 l
+15.441 -1.22 15.478 -1.422 15.536 -1.587 c
+15.536 -1.631 l
+h
+14.375 -1.249 m
+14.493 -1.249 14.603 -1.216 14.713 -1.147 c
+14.82 -1.08 14.898 -0.996 14.948 -0.897 c
+14.948 -0.191 l
+14.684 -0.191 l
+14.449 -0.191 14.262 -0.243 14.125 -0.338 c
+13.997 -0.437 13.934 -0.58 13.934 -0.764 c
+13.934 -0.933 13.963 -1.055 14.023 -1.132 c
+14.089 -1.213 14.206 -1.249 14.375 -1.249 c
+16.646 2.072 m
+16.646 1.353 l
+17.102 1.353 l
+17.102 0.956 l
+16.646 0.956 l
+16.646 -0.897 l
+16.646 -1.014 16.665 -1.103 16.705 -1.161 c
+16.742 -1.22 16.812 -1.249 16.911 -1.249 c
+16.97 -1.249 17.032 -1.242 17.102 -1.22 c
+17.102 -1.631 l
+16.984 -1.668 16.87 -1.691 16.764 -1.691 c
+16.565 -1.691 16.415 -1.625 16.309 -1.484 c
+16.209 -1.349 16.161 -1.154 16.161 -0.897 c
+16.161 0.956 l
+15.706 0.956 l
+15.706 1.353 l
+16.161 1.353 l
+16.161 2.072 l
+h
+18.624 -1.691 m
+18.248 -1.691 17.965 -1.584 17.771 -1.367 c
+17.572 -1.143 17.477 -0.816 17.477 -0.382 c
+17.477 -0.015 l
+17.477 0.426 17.568 0.771 17.756 1.029 c
+17.951 1.282 18.227 1.411 18.58 1.411 c
+18.921 1.411 19.175 1.297 19.343 1.073 c
+19.52 0.845 19.612 0.5 19.623 0.029 c
+19.623 -0.279 l
+17.962 -0.279 l
+17.962 -0.353 l
+17.962 -0.676 18.021 -0.912 18.138 -1.058 c
+18.256 -1.198 18.425 -1.264 18.653 -1.264 c
+18.8 -1.264 18.925 -1.242 19.035 -1.191 c
+19.141 -1.132 19.245 -1.043 19.343 -0.926 c
+19.594 -1.234 l
+19.388 -1.54 19.064 -1.691 18.624 -1.691 c
+18.58 0.985 m
+18.374 0.985 18.219 0.915 18.123 0.779 c
+18.025 0.64 17.969 0.426 17.962 0.133 c
+19.137 0.133 l
+19.137 0.206 l
+19.116 0.478 19.064 0.676 18.977 0.794 c
+18.888 0.919 18.755 0.985 18.58 0.985 c
+19.931 0 m
+19.931 0.459 20.012 0.808 20.182 1.043 c
+20.358 1.286 20.608 1.411 20.931 1.411 c
+21.214 1.411 21.435 1.294 21.593 1.058 c
+21.593 2.602 l
+22.077 2.602 l
+22.077 -1.631 l
+21.637 -1.631 l
+21.607 -1.309 l
+21.449 -1.565 21.225 -1.691 20.931 -1.691 c
+20.615 -1.691 20.373 -1.573 20.196 -1.338 c
+20.02 -1.095 19.931 -0.75 19.931 -0.309 c
+h
+20.417 -0.279 m
+20.417 -0.613 20.465 -0.86 20.564 -1.014 c
+20.66 -1.172 20.821 -1.249 21.049 -1.249 c
+21.292 -1.249 21.475 -1.132 21.593 -0.897 c
+21.593 0.617 l
+21.464 0.852 21.284 0.97 21.049 0.97 c
+20.821 0.97 20.66 0.889 20.564 0.735 c
+20.465 0.577 20.417 0.338 20.417 0.015 c
+h
+24.485 2.072 m
+24.485 1.353 l
+24.94 1.353 l
+24.94 0.956 l
+24.485 0.956 l
+24.485 -0.897 l
+24.485 -1.014 24.503 -1.103 24.544 -1.161 c
+24.581 -1.22 24.65 -1.249 24.749 -1.249 c
+24.808 -1.249 24.87 -1.242 24.94 -1.22 c
+24.94 -1.631 l
+24.822 -1.668 24.708 -1.691 24.602 -1.691 c
+24.404 -1.691 24.253 -1.625 24.147 -1.484 c
+24.047 -1.349 23.999 -1.154 23.999 -0.897 c
+23.999 0.956 l
+23.544 0.956 l
+23.544 1.353 l
+23.999 1.353 l
+23.999 2.072 l
+h
+26.936 -1.631 m
+26.906 -1.565 26.884 -1.455 26.877 -1.309 c
+26.701 -1.565 26.48 -1.691 26.216 -1.691 c
+25.94 -1.691 25.723 -1.617 25.568 -1.469 c
+25.422 -1.315 25.348 -1.099 25.348 -0.823 c
+25.348 -0.522 25.451 -0.279 25.657 -0.103 c
+25.863 0.081 26.146 0.177 26.509 0.177 c
+26.862 0.177 l
+26.862 0.5 l
+26.862 0.676 26.821 0.798 26.744 0.867 c
+26.663 0.945 26.546 0.985 26.391 0.985 c
+26.245 0.985 26.12 0.941 26.025 0.852 c
+25.936 0.765 25.892 0.654 25.892 0.53 c
+25.407 0.53 l
+25.407 0.676 25.451 0.816 25.539 0.956 c
+25.628 1.103 25.745 1.213 25.892 1.294 c
+26.046 1.371 26.219 1.411 26.407 1.411 c
+26.719 1.411 26.954 1.33 27.112 1.176 c
+27.266 1.029 27.347 0.808 27.347 0.515 c
+27.347 -0.985 l
+27.355 -1.22 27.391 -1.422 27.45 -1.587 c
+27.45 -1.631 l
+h
+26.289 -1.249 m
+26.407 -1.249 26.517 -1.216 26.627 -1.147 c
+26.734 -1.08 26.811 -0.996 26.862 -0.897 c
+26.862 -0.191 l
+26.598 -0.191 l
+26.362 -0.191 26.175 -0.243 26.039 -0.338 c
+25.911 -0.437 25.848 -0.58 25.848 -0.764 c
+25.848 -0.933 25.877 -1.055 25.936 -1.132 c
+26.002 -1.213 26.12 -1.249 26.289 -1.249 c
+27.825 0 m
+27.825 0.459 27.906 0.808 28.074 1.043 c
+28.251 1.286 28.502 1.411 28.824 1.411 c
+29.126 1.411 29.358 1.278 29.516 1.014 c
+29.545 1.353 l
+29.986 1.353 l
+29.986 -1.661 l
+29.986 -2.032 29.886 -2.315 29.692 -2.514 c
+29.504 -2.708 29.244 -2.807 28.913 -2.807 c
+28.766 -2.807 28.597 -2.767 28.413 -2.69 c
+28.226 -2.62 28.09 -2.532 28.001 -2.425 c
+28.192 -2.088 l
+28.398 -2.293 28.622 -2.396 28.868 -2.396 c
+29.269 -2.396 29.475 -2.168 29.486 -1.72 c
+29.486 -1.338 l
+29.328 -1.573 29.107 -1.691 28.824 -1.691 c
+28.508 -1.691 28.266 -1.573 28.09 -1.338 c
+27.92 -1.103 27.833 -0.771 27.825 -0.338 c
+h
+28.31 -0.279 m
+28.31 -0.613 28.358 -0.86 28.457 -1.014 c
+28.552 -1.172 28.714 -1.249 28.942 -1.249 c
+29.185 -1.249 29.368 -1.128 29.486 -0.881 c
+29.486 0.603 l
+29.368 0.845 29.192 0.97 28.957 0.97 c
+28.729 0.97 28.567 0.889 28.471 0.735 c
+28.373 0.577 28.317 0.338 28.31 0.015 c
+h
+31.566 -0.029 m
+31.566 -0.852 31.382 -1.58 31.022 -2.205 c
+30.816 -2.547 30.611 -2.786 30.405 -2.925 c
+30.302 -2.616 l
+30.537 -2.389 30.721 -2.051 30.86 -1.602 c
+31.008 -1.143 31.081 -0.643 31.081 -0.103 c
+31.081 0 l
+31.081 0.694 30.967 1.323 30.742 1.881 c
+30.615 2.176 30.467 2.411 30.302 2.587 c
+30.405 2.896 l
+30.599 2.756 30.794 2.535 30.993 2.234 c
+31.375 1.588 31.566 0.831 31.566 -0.029 c
+f
+Q
+0.567 w 1 j 1 J
+q 1 0 0 1 627.4703 96.4198 cm
+0 0 m
+5.747 3.34 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 631.3726 98.6865 cm
+0 0 m
+-0.386 -1.458 l
+2.308 1.341 l
+-1.455 0.387 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 631.3727 98.6865 cm
+0 0 m
+-0.386 -1.458 l
+2.308 1.341 l
+-1.455 0.387 l
+0 0 l
+h
+S
+Q
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+0 G
+/GS2 gs
+0 TL/Fm34 Do
+Q
+0 0 0 0 k
+q 1 0 0 1 121.1352 111.1992 cm
+0 0 m
+-1.323 0 l
+-1.808 -1.043 l
+-2.323 -1.043 l
+-0.382 2.969 l
+0.044 2.969 l
+0.603 -1.043 l
+0.118 -1.043 l
+h
+-1.117 0.441 m
+-0.058 0.441 l
+-0.264 2.263 l
+h
+2.903 1.941 m
+2.845 1.617 l
+3.057 1.871 3.304 1.999 3.58 1.999 c
+3.763 1.989 3.91 1.918 4.021 1.793 c
+4.138 1.675 4.2 1.503 4.212 1.279 c
+4.219 1.151 4.219 1.029 4.212 0.912 c
+3.888 -1.043 l
+3.403 -1.043 l
+3.741 0.927 l
+3.755 1.132 l
+3.755 1.415 3.638 1.562 3.403 1.573 c
+3.215 1.573 3.04 1.463 2.874 1.249 c
+2.786 1.132 l
+2.419 -1.043 l
+1.933 -1.043 l
+2.462 1.941 l
+h
+6.81 1.985 m
+7.082 1.985 7.287 1.871 7.427 1.646 c
+7.721 3.19 l
+8.191 3.19 l
+7.456 -1.043 l
+7.03 -1.043 l
+7.074 -0.72 l
+6.876 -0.985 6.641 -1.109 6.369 -1.103 c
+6.122 -1.091 5.931 -0.995 5.795 -0.808 c
+5.656 -0.625 5.59 -0.353 5.59 0 c
+5.59 0.166 5.608 0.382 5.648 0.647 c
+5.696 0.919 5.766 1.151 5.854 1.338 c
+6.06 1.786 6.376 2.003 6.81 1.985 c
+6.06 -0.118 m
+6.067 -0.481 6.221 -0.669 6.516 -0.676 c
+6.622 -0.676 6.725 -0.654 6.824 -0.602 c
+6.92 -0.544 7.015 -0.448 7.104 -0.309 c
+7.368 1.205 l
+7.28 1.43 7.125 1.548 6.913 1.558 c
+6.471 1.588 6.207 1.268 6.119 0.603 c
+6.078 0.279 6.06 0.037 6.06 -0.118 c
+12.48 2.66 m
+12.347 1.941 l
+12.788 1.941 l
+12.715 1.544 l
+12.274 1.544 l
+11.965 -0.309 l
+11.965 -0.426 l
+11.954 -0.584 12.009 -0.661 12.127 -0.661 c
+12.175 -0.661 12.245 -0.654 12.333 -0.632 c
+12.289 -1.043 l
+12.179 -1.08 12.075 -1.095 11.98 -1.087 c
+11.811 -1.087 11.686 -1.022 11.597 -0.881 c
+11.51 -0.746 11.48 -0.555 11.51 -0.309 c
+11.803 1.544 l
+11.362 1.544 l
+11.436 1.941 l
+11.877 1.941 l
+11.994 2.66 l
+h
+14.637 1.632 m
+14.85 1.874 15.092 1.992 15.357 1.985 c
+15.551 1.985 15.703 1.918 15.813 1.793 c
+15.919 1.675 15.978 1.503 15.989 1.279 c
+15.996 1.151 15.996 1.029 15.989 0.912 c
+15.666 -1.043 l
+15.181 -1.043 l
+15.518 0.927 l
+15.533 1.132 l
+15.533 1.415 15.416 1.562 15.181 1.573 c
+14.993 1.573 14.817 1.463 14.651 1.249 c
+14.564 1.132 l
+14.196 -1.043 l
+13.71 -1.043 l
+14.446 3.19 l
+14.93 3.19 l
+h
+17.742 -1.043 m
+17.272 -1.043 l
+17.786 1.941 l
+18.271 1.941 l
+h
+17.903 2.734 m
+17.903 2.812 17.926 2.881 17.978 2.94 c
+18.025 2.999 18.088 3.028 18.168 3.028 c
+18.246 3.028 18.308 2.999 18.359 2.94 c
+18.418 2.889 18.44 2.822 18.433 2.734 c
+18.433 2.654 18.407 2.587 18.359 2.529 c
+18.308 2.469 18.246 2.44 18.168 2.44 c
+18.08 2.429 18.01 2.455 17.962 2.514 c
+17.911 2.573 17.893 2.646 17.903 2.734 c
+20.939 -0.264 m
+20.968 -0.088 20.869 0.059 20.645 0.177 c
+20.278 0.368 l
+20.09 0.474 19.958 0.588 19.881 0.706 c
+19.8 0.823 19.767 0.963 19.778 1.132 c
+19.785 1.374 19.884 1.58 20.072 1.75 c
+20.266 1.914 20.505 1.999 20.792 1.999 c
+21.057 1.989 21.263 1.9 21.409 1.735 c
+21.564 1.565 21.633 1.353 21.615 1.087 c
+21.145 1.087 l
+21.153 1.235 21.119 1.353 21.042 1.44 c
+20.972 1.529 20.881 1.573 20.763 1.573 c
+20.615 1.573 20.498 1.532 20.41 1.455 c
+20.322 1.374 20.266 1.272 20.248 1.147 c
+20.218 1 20.285 0.875 20.454 0.779 c
+20.924 0.544 l
+21.266 0.345 21.428 0.092 21.409 -0.22 c
+21.388 -0.496 21.28 -0.716 21.086 -0.881 c
+20.887 -1.04 20.648 -1.109 20.366 -1.103 c
+20.09 -1.091 19.866 -1.007 19.69 -0.837 c
+19.52 -0.661 19.443 -0.434 19.454 -0.147 c
+19.94 -0.147 l
+19.929 -0.324 19.962 -0.455 20.042 -0.544 c
+20.131 -0.632 20.241 -0.676 20.38 -0.676 c
+20.534 -0.676 20.663 -0.639 20.763 -0.559 c
+20.858 -0.481 20.917 -0.382 20.939 -0.264 c
+25.423 -1.043 m
+24.952 -1.043 l
+25.467 1.941 l
+25.951 1.941 l
+h
+25.584 2.734 m
+25.584 2.812 25.606 2.881 25.658 2.94 c
+25.705 2.999 25.768 3.028 25.849 3.028 c
+25.926 3.028 25.988 2.999 26.04 2.94 c
+26.098 2.889 26.121 2.822 26.113 2.734 c
+26.113 2.654 26.088 2.587 26.04 2.529 c
+25.988 2.469 25.926 2.44 25.849 2.44 c
+25.76 2.429 25.691 2.455 25.643 2.514 c
+25.591 2.573 25.573 2.646 25.584 2.734 c
+28.619 -0.264 m
+28.648 -0.088 28.55 0.059 28.326 0.177 c
+27.958 0.368 l
+27.771 0.474 27.638 0.588 27.561 0.706 c
+27.48 0.823 27.447 0.963 27.458 1.132 c
+27.466 1.374 27.565 1.58 27.752 1.75 c
+27.947 1.914 28.186 1.999 28.472 1.999 c
+28.737 1.989 28.943 1.9 29.09 1.735 c
+29.244 1.565 29.313 1.353 29.296 1.087 c
+28.825 1.087 l
+28.833 1.235 28.799 1.353 28.722 1.44 c
+28.652 1.529 28.561 1.573 28.443 1.573 c
+28.296 1.573 28.178 1.532 28.09 1.455 c
+28.002 1.374 27.947 1.272 27.929 1.147 c
+27.899 1 27.965 0.875 28.134 0.779 c
+28.604 0.544 l
+28.947 0.345 29.108 0.092 29.09 -0.22 c
+29.068 -0.496 28.961 -0.716 28.766 -0.881 c
+28.567 -1.04 28.328 -1.109 28.046 -1.103 c
+27.771 -1.091 27.546 -1.007 27.37 -0.837 c
+27.201 -0.661 27.123 -0.434 27.135 -0.147 c
+27.62 -0.147 l
+27.609 -0.324 27.642 -0.455 27.723 -0.544 c
+27.811 -0.632 27.921 -0.676 28.06 -0.676 c
+28.215 -0.676 28.344 -0.639 28.443 -0.559 c
+28.538 -0.481 28.598 -0.382 28.619 -0.264 c
+33.87 2.66 m
+33.738 1.941 l
+34.179 1.941 l
+34.106 1.544 l
+33.664 1.544 l
+33.356 -0.309 l
+33.356 -0.426 l
+33.345 -0.584 33.4 -0.661 33.518 -0.661 c
+33.566 -0.661 33.635 -0.654 33.724 -0.632 c
+33.679 -1.043 l
+33.569 -1.08 33.466 -1.095 33.371 -1.087 c
+33.202 -1.087 33.076 -1.022 32.989 -0.881 c
+32.9 -0.746 32.871 -0.555 32.9 -0.309 c
+33.194 1.544 l
+32.754 1.544 l
+32.827 1.941 l
+33.267 1.941 l
+33.386 2.66 l
+h
+36.027 1.632 m
+36.241 1.874 36.483 1.992 36.748 1.985 c
+36.943 1.985 37.093 1.918 37.203 1.793 c
+37.31 1.675 37.369 1.503 37.38 1.279 c
+37.387 1.151 37.387 1.029 37.38 0.912 c
+37.057 -1.043 l
+36.571 -1.043 l
+36.91 0.927 l
+36.924 1.132 l
+36.924 1.415 36.806 1.562 36.571 1.573 c
+36.384 1.573 36.208 1.463 36.043 1.249 c
+35.954 1.132 l
+35.586 -1.043 l
+35.102 -1.043 l
+35.837 3.19 l
+36.322 3.19 l
+h
+39.732 -1.103 m
+39.397 -1.091 39.147 -0.97 38.982 -0.735 c
+38.813 -0.5 38.755 -0.176 38.805 0.235 c
+38.835 0.53 l
+38.894 0.989 39.033 1.353 39.262 1.617 c
+39.486 1.881 39.769 2.007 40.114 1.999 c
+40.397 1.989 40.606 1.889 40.746 1.706 c
+40.893 1.518 40.966 1.253 40.966 0.912 c
+40.937 0.588 l
+40.893 0.309 l
+39.291 0.309 l
+39.268 0.14 39.262 0.008 39.262 -0.088 c
+39.262 -0.276 39.305 -0.422 39.393 -0.529 c
+39.482 -0.628 39.607 -0.676 39.775 -0.676 c
+39.893 -0.687 40.011 -0.669 40.128 -0.617 c
+40.246 -0.559 40.363 -0.47 40.481 -0.353 c
+40.716 -0.661 l
+40.599 -0.808 40.452 -0.922 40.276 -0.999 c
+40.099 -1.066 39.916 -1.103 39.732 -1.103 c
+40.085 1.573 m
+39.761 1.592 39.534 1.397 39.409 0.985 c
+39.335 0.721 l
+40.481 0.721 l
+40.481 0.794 l
+40.5 0.871 40.511 0.96 40.511 1.058 c
+40.5 1.389 40.357 1.562 40.085 1.573 c
+45.398 -1.103 m
+45.111 -1.091 44.899 -0.977 44.752 -0.75 c
+44.487 -2.19 l
+44.016 -2.19 l
+44.737 1.941 l
+45.163 1.941 l
+45.105 1.602 l
+45.31 1.867 45.549 1.999 45.824 1.999 c
+46.089 1.989 46.283 1.885 46.412 1.691 c
+46.549 1.503 46.611 1.239 46.603 0.897 c
+46.593 0.738 46.567 0.53 46.53 0.264 c
+46.501 0.008 46.449 -0.206 46.383 -0.382 c
+46.177 -0.874 45.847 -1.109 45.398 -1.103 c
+46.133 1.014 m
+46.104 1.374 45.946 1.558 45.663 1.558 c
+45.446 1.565 45.251 1.455 45.075 1.22 c
+44.81 -0.338 l
+44.899 -0.555 45.049 -0.673 45.266 -0.691 c
+45.479 -0.702 45.655 -0.628 45.795 -0.47 c
+45.931 -0.305 46.019 -0.051 46.06 0.294 c
+46.108 0.636 46.133 0.875 46.133 1.014 c
+49.315 -1.043 m
+49.304 -0.985 49.3 -0.933 49.3 -0.881 c
+49.315 -0.72 l
+49.109 -0.977 48.882 -1.103 48.639 -1.103 c
+48.394 -1.103 48.203 -1.025 48.066 -0.867 c
+47.937 -0.713 47.883 -0.507 47.904 -0.249 c
+47.923 0.052 48.051 0.298 48.286 0.485 c
+48.521 0.669 48.823 0.765 49.198 0.765 c
+49.536 0.765 l
+49.58 1.087 l
+49.61 1.411 49.485 1.573 49.213 1.573 c
+49.065 1.573 48.941 1.529 48.845 1.44 c
+48.746 1.36 48.687 1.253 48.669 1.118 c
+48.199 1.118 l
+48.217 1.36 48.33 1.565 48.536 1.735 c
+48.743 1.911 48.981 1.999 49.257 1.999 c
+49.521 1.989 49.727 1.904 49.874 1.75 c
+50.021 1.592 50.08 1.367 50.05 1.073 c
+49.815 -0.397 l
+49.793 -0.496 49.786 -0.595 49.786 -0.691 c
+49.801 -0.999 l
+49.801 -1.043 l
+h
+48.727 -0.661 m
+48.97 -0.661 49.176 -0.544 49.345 -0.309 c
+49.477 0.397 l
+49.257 0.412 l
+49.069 0.412 48.904 0.375 48.757 0.309 c
+48.61 0.239 48.507 0.14 48.448 0.015 c
+48.39 -0.103 48.367 -0.235 48.39 -0.382 c
+48.408 -0.569 48.521 -0.661 48.727 -0.661 c
+52.847 -0.264 m
+52.876 -0.088 52.777 0.059 52.553 0.177 c
+52.186 0.368 l
+51.998 0.474 51.866 0.588 51.789 0.706 c
+51.708 0.823 51.675 0.963 51.686 1.132 c
+51.693 1.374 51.792 1.58 51.98 1.75 c
+52.174 1.914 52.413 1.999 52.7 1.999 c
+52.965 1.989 53.171 1.9 53.317 1.735 c
+53.472 1.565 53.541 1.353 53.523 1.087 c
+53.053 1.087 l
+53.061 1.235 53.027 1.353 52.95 1.44 c
+52.88 1.529 52.788 1.573 52.671 1.573 c
+52.523 1.573 52.406 1.532 52.317 1.455 c
+52.23 1.374 52.174 1.272 52.156 1.147 c
+52.126 1 52.193 0.875 52.362 0.779 c
+52.832 0.544 l
+53.174 0.345 53.336 0.092 53.317 -0.22 c
+53.296 -0.496 53.188 -0.716 52.994 -0.881 c
+52.795 -1.04 52.556 -1.109 52.274 -1.103 c
+51.998 -1.091 51.774 -1.007 51.598 -0.837 c
+51.428 -0.661 51.351 -0.434 51.362 -0.147 c
+51.847 -0.147 l
+51.837 -0.324 51.87 -0.455 51.95 -0.544 c
+52.039 -0.632 52.149 -0.676 52.288 -0.676 c
+52.442 -0.676 52.571 -0.639 52.671 -0.559 c
+52.766 -0.481 52.825 -0.382 52.847 -0.264 c
+55.941 2.66 m
+55.808 1.941 l
+56.25 1.941 l
+56.176 1.544 l
+55.735 1.544 l
+55.426 -0.309 l
+55.426 -0.426 l
+55.415 -0.584 55.471 -0.661 55.588 -0.661 c
+55.636 -0.661 55.706 -0.654 55.794 -0.632 c
+55.75 -1.043 l
+55.64 -1.08 55.537 -1.095 55.442 -1.087 c
+55.272 -1.087 55.147 -1.022 55.06 -0.881 c
+54.971 -0.746 54.942 -0.555 54.971 -0.309 c
+55.265 1.544 l
+54.824 1.544 l
+54.898 1.941 l
+55.338 1.941 l
+55.456 2.66 l
+h
+57.569 -0.47 m
+57.657 -0.463 57.73 -0.484 57.79 -0.544 c
+57.848 -0.602 57.878 -0.676 57.878 -0.764 c
+57.878 -0.852 57.848 -0.926 57.79 -0.985 c
+57.73 -1.043 57.657 -1.072 57.569 -1.072 c
+57.481 -1.08 57.408 -1.062 57.348 -1.014 c
+57.29 -0.955 57.26 -0.881 57.26 -0.794 c
+57.26 -0.698 57.29 -0.617 57.348 -0.559 c
+57.408 -0.5 57.481 -0.47 57.569 -0.47 c
+64.283 -1.043 m
+63.798 -1.043 l
+64.121 0.809 l
+62.563 0.809 l
+62.239 -1.043 l
+61.74 -1.043 l
+62.43 2.969 l
+62.931 2.969 l
+62.622 1.249 l
+64.195 1.249 l
+64.489 2.969 l
+64.988 2.969 l
+h
+67.043 -1.103 m
+66.708 -1.091 66.459 -0.97 66.293 -0.735 c
+66.124 -0.5 66.065 -0.176 66.116 0.235 c
+66.146 0.53 l
+66.205 0.989 66.345 1.353 66.572 1.617 c
+66.796 1.881 67.079 2.007 67.425 1.999 c
+67.708 1.989 67.918 1.889 68.057 1.706 c
+68.204 1.518 68.277 1.253 68.277 0.912 c
+68.248 0.588 l
+68.204 0.309 l
+66.602 0.309 l
+66.58 0.14 66.572 0.008 66.572 -0.088 c
+66.572 -0.276 66.617 -0.422 66.704 -0.529 c
+66.792 -0.628 66.918 -0.676 67.087 -0.676 c
+67.205 -0.687 67.322 -0.669 67.44 -0.617 c
+67.557 -0.559 67.675 -0.47 67.792 -0.353 c
+68.028 -0.661 l
+67.91 -0.808 67.762 -0.922 67.586 -0.999 c
+67.41 -1.066 67.226 -1.103 67.043 -1.103 c
+67.396 1.573 m
+67.072 1.592 66.844 1.397 66.719 0.985 c
+66.646 0.721 l
+67.792 0.721 l
+67.792 0.794 l
+67.81 0.871 67.822 0.96 67.822 1.058 c
+67.81 1.389 67.667 1.562 67.396 1.573 c
+71.022 1.484 m
+70.941 1.503 70.868 1.515 70.802 1.515 c
+70.567 1.515 70.368 1.371 70.214 1.087 c
+69.846 -1.043 l
+69.362 -1.043 l
+69.89 1.941 l
+70.347 1.941 l
+70.272 1.632 l
+70.449 1.885 70.648 2.007 70.875 1.999 c
+70.912 1.999 70.978 1.985 71.066 1.955 c
+h
+72.988 -1.103 m
+72.654 -1.091 72.404 -0.97 72.238 -0.735 c
+72.07 -0.5 72.011 -0.176 72.063 0.235 c
+72.092 0.53 l
+72.151 0.989 72.29 1.353 72.518 1.617 c
+72.742 1.881 73.025 2.007 73.37 1.999 c
+73.653 1.989 73.863 1.889 74.002 1.706 c
+74.15 1.518 74.223 1.253 74.223 0.912 c
+74.193 0.588 l
+74.15 0.309 l
+72.547 0.309 l
+72.525 0.14 72.518 0.008 72.518 -0.088 c
+72.518 -0.276 72.562 -0.422 72.65 -0.529 c
+72.739 -0.628 72.863 -0.676 73.032 -0.676 c
+73.15 -0.687 73.268 -0.669 73.385 -0.617 c
+73.503 -0.559 73.62 -0.47 73.738 -0.353 c
+73.973 -0.661 l
+73.856 -0.808 73.709 -0.922 73.532 -0.999 c
+73.356 -1.066 73.172 -1.103 72.988 -1.103 c
+73.341 1.573 m
+73.017 1.592 72.79 1.397 72.665 0.985 c
+72.591 0.721 l
+73.738 0.721 l
+73.738 0.794 l
+73.757 0.871 73.767 0.96 73.767 1.058 c
+73.757 1.389 73.613 1.562 73.341 1.573 c
+79.772 -0.176 m
+80.58 1.941 l
+81.065 1.941 l
+79.831 -1.043 l
+79.463 -1.043 l
+79.272 1.087 l
+78.346 -1.043 l
+77.964 -1.043 l
+77.773 1.941 l
+78.228 1.941 l
+78.317 -0.118 l
+79.213 1.941 l
+79.596 1.941 l
+h
+83.402 -1.043 m
+83.392 -0.985 83.388 -0.933 83.388 -0.881 c
+83.402 -0.72 l
+83.197 -0.977 82.969 -1.103 82.727 -1.103 c
+82.48 -1.103 82.289 -1.025 82.153 -0.867 c
+82.025 -0.713 81.969 -0.507 81.991 -0.249 c
+82.01 0.052 82.139 0.298 82.374 0.485 c
+82.609 0.669 82.91 0.765 83.285 0.765 c
+83.623 0.765 l
+83.667 1.087 l
+83.697 1.411 83.572 1.573 83.3 1.573 c
+83.153 1.573 83.028 1.529 82.932 1.44 c
+82.833 1.36 82.775 1.253 82.756 1.118 c
+82.286 1.118 l
+82.304 1.36 82.418 1.565 82.623 1.735 c
+82.829 1.911 83.068 1.999 83.344 1.999 c
+83.608 1.989 83.814 1.904 83.961 1.75 c
+84.108 1.592 84.167 1.367 84.138 1.073 c
+83.903 -0.397 l
+83.88 -0.496 83.873 -0.595 83.873 -0.691 c
+83.888 -0.999 l
+83.888 -1.043 l
+h
+82.814 -0.661 m
+83.057 -0.661 83.263 -0.544 83.432 -0.309 c
+83.564 0.397 l
+83.344 0.412 l
+83.157 0.412 82.991 0.375 82.844 0.309 c
+82.697 0.239 82.594 0.14 82.536 0.015 c
+82.477 -0.103 82.455 -0.235 82.477 -0.382 c
+82.495 -0.569 82.609 -0.661 82.814 -0.661 c
+86.938 -0.264 m
+86.968 -0.088 86.868 0.059 86.644 0.177 c
+86.276 0.368 l
+86.089 0.474 85.956 0.588 85.879 0.706 c
+85.798 0.823 85.765 0.963 85.777 1.132 c
+85.784 1.374 85.883 1.58 86.07 1.75 c
+86.265 1.914 86.504 1.999 86.791 1.999 c
+87.055 1.989 87.261 1.9 87.408 1.735 c
+87.562 1.565 87.633 1.353 87.614 1.087 c
+87.144 1.087 l
+87.151 1.235 87.118 1.353 87.041 1.44 c
+86.97 1.529 86.879 1.573 86.762 1.573 c
+86.615 1.573 86.497 1.532 86.409 1.455 c
+86.32 1.374 86.265 1.272 86.247 1.147 c
+86.218 1 86.284 0.875 86.453 0.779 c
+86.923 0.544 l
+87.265 0.345 87.427 0.092 87.408 -0.22 c
+87.386 -0.496 87.28 -0.716 87.085 -0.881 c
+86.887 -1.04 86.648 -1.109 86.365 -1.103 c
+86.089 -1.091 85.865 -1.007 85.688 -0.837 c
+85.519 -0.661 85.442 -0.434 85.453 -0.147 c
+85.938 -0.147 l
+85.927 -0.324 85.96 -0.455 86.041 -0.544 c
+86.129 -0.632 86.239 -0.676 86.38 -0.676 c
+86.534 -0.676 86.662 -0.639 86.762 -0.559 c
+86.857 -0.481 86.916 -0.382 86.938 -0.264 c
+91.994 -0.691 m
+92.148 -0.691 92.277 -0.643 92.377 -0.544 c
+92.483 -0.437 92.56 -0.29 92.612 -0.103 c
+93.052 -0.103 l
+93.012 -0.397 92.89 -0.643 92.685 -0.837 c
+92.479 -1.025 92.237 -1.109 91.965 -1.103 c
+91.7 -1.091 91.487 -1.014 91.333 -0.867 c
+91.175 -0.713 91.083 -0.496 91.053 -0.22 c
+91.032 -0.055 91.032 0.11 91.053 0.279 c
+91.097 0.574 l
+91.157 1.044 91.292 1.401 91.509 1.646 c
+91.733 1.889 92.027 2.007 92.391 1.999 c
+92.663 1.989 92.876 1.885 93.023 1.691 c
+93.177 1.492 93.251 1.235 93.243 0.912 c
+92.788 0.912 l
+92.795 1.341 92.655 1.562 92.362 1.573 c
+91.928 1.592 91.671 1.286 91.583 0.661 c
+91.524 0.268 91.498 0 91.509 -0.147 c
+91.527 -0.5 91.689 -0.683 91.994 -0.691 c
+95.24 1.632 m
+95.452 1.874 95.695 1.992 95.959 1.985 c
+96.154 1.985 96.304 1.918 96.415 1.793 c
+96.522 1.675 96.58 1.503 96.591 1.279 c
+96.599 1.151 96.599 1.029 96.591 0.912 c
+96.268 -1.043 l
+95.783 -1.043 l
+96.121 0.927 l
+96.136 1.132 l
+96.136 1.415 96.018 1.562 95.783 1.573 c
+95.595 1.573 95.419 1.463 95.254 1.249 c
+95.165 1.132 l
+94.798 -1.043 l
+94.313 -1.043 l
+95.048 3.19 l
+95.533 3.19 l
+h
+99.354 -1.043 m
+99.344 -0.985 99.34 -0.933 99.34 -0.881 c
+99.354 -0.72 l
+99.149 -0.977 98.922 -1.103 98.679 -1.103 c
+98.432 -1.103 98.241 -1.025 98.105 -0.867 c
+97.977 -0.713 97.922 -0.507 97.943 -0.249 c
+97.962 0.052 98.091 0.298 98.326 0.485 c
+98.561 0.669 98.862 0.765 99.238 0.765 c
+99.575 0.765 l
+99.62 1.087 l
+99.649 1.411 99.524 1.573 99.252 1.573 c
+99.105 1.573 98.98 1.529 98.884 1.44 c
+98.785 1.36 98.727 1.253 98.708 1.118 c
+98.238 1.118 l
+98.256 1.36 98.37 1.565 98.575 1.735 c
+98.781 1.911 99.02 1.999 99.296 1.999 c
+99.56 1.989 99.766 1.904 99.913 1.75 c
+100.06 1.592 100.119 1.367 100.09 1.073 c
+99.855 -0.397 l
+99.832 -0.496 99.825 -0.595 99.825 -0.691 c
+99.84 -0.999 l
+99.84 -1.043 l
+h
+98.767 -0.661 m
+99.009 -0.661 99.215 -0.544 99.384 -0.309 c
+99.516 0.397 l
+99.296 0.412 l
+99.109 0.412 98.943 0.375 98.796 0.309 c
+98.649 0.239 98.546 0.14 98.488 0.015 c
+98.429 -0.103 98.407 -0.235 98.429 -0.382 c
+98.447 -0.569 98.561 -0.661 98.767 -0.661 c
+101.534 0.574 m
+101.592 1.004 101.732 1.353 101.96 1.617 c
+102.195 1.881 102.49 2.007 102.843 1.999 c
+103.066 1.989 103.254 1.918 103.4 1.793 c
+103.548 1.675 103.651 1.507 103.709 1.294 c
+103.776 1.077 103.797 0.842 103.768 0.588 c
+103.739 0.309 l
+103.669 -0.125 103.518 -0.47 103.283 -0.735 c
+103.055 -0.992 102.768 -1.109 102.415 -1.103 c
+102.188 -1.091 102.004 -1.029 101.858 -0.912 c
+101.71 -0.786 101.6 -0.617 101.534 -0.411 c
+101.475 -0.199 101.461 0.044 101.49 0.309 c
+h
+101.96 0.103 m
+101.949 -0.143 101.982 -0.334 102.063 -0.47 c
+102.151 -0.61 102.276 -0.683 102.446 -0.691 c
+102.651 -0.702 102.82 -0.628 102.96 -0.47 c
+103.107 -0.305 103.202 -0.058 103.254 0.264 c
+103.298 0.588 l
+103.313 0.794 l
+103.313 1.037 103.269 1.228 103.18 1.367 c
+103.099 1.503 102.978 1.573 102.812 1.573 c
+102.596 1.58 102.415 1.492 102.269 1.309 c
+102.129 1.121 102.041 0.86 102.004 0.53 c
+101.96 0.25 l
+h
+106.51 -0.264 m
+106.539 -0.088 106.44 0.059 106.215 0.177 c
+105.848 0.368 l
+105.661 0.474 105.528 0.588 105.451 0.706 c
+105.37 0.823 105.337 0.963 105.349 1.132 c
+105.355 1.374 105.455 1.58 105.642 1.75 c
+105.837 1.914 106.076 1.999 106.363 1.999 c
+106.627 1.989 106.833 1.9 106.98 1.735 c
+107.134 1.565 107.204 1.353 107.186 1.087 c
+106.716 1.087 l
+106.723 1.235 106.689 1.353 106.612 1.44 c
+106.542 1.529 106.45 1.573 106.333 1.573 c
+106.186 1.573 106.068 1.532 105.98 1.455 c
+105.892 1.374 105.837 1.272 105.819 1.147 c
+105.789 1 105.856 0.875 106.024 0.779 c
+106.495 0.544 l
+106.837 0.345 106.999 0.092 106.98 -0.22 c
+106.958 -0.496 106.851 -0.716 106.656 -0.881 c
+106.458 -1.04 106.219 -1.109 105.937 -1.103 c
+105.661 -1.091 105.436 -1.007 105.26 -0.837 c
+105.091 -0.661 105.014 -0.434 105.025 -0.147 c
+105.51 -0.147 l
+105.499 -0.324 105.532 -0.455 105.613 -0.544 c
+105.701 -0.632 105.812 -0.676 105.951 -0.676 c
+106.105 -0.676 106.234 -0.639 106.333 -0.559 c
+106.429 -0.481 106.488 -0.382 106.51 -0.264 c
+108.237 -1.852 m
+107.957 -1.646 l
+108.175 -1.382 108.303 -1.117 108.354 -0.852 c
+108.428 -0.441 l
+108.913 -0.441 l
+108.854 -0.808 l
+108.784 -1.227 108.578 -1.577 108.237 -1.852 c
+f
+Q
+q 1 0 0 1 170.9031 102.5161 cm
+0 0 m
+0.808 2.117 l
+1.294 2.117 l
+0.058 -0.867 l
+-0.31 -0.867 l
+-0.5 1.264 l
+-1.426 -0.867 l
+-1.808 -0.867 l
+-1.999 2.117 l
+-1.544 2.117 l
+-1.455 0.058 l
+-0.559 2.117 l
+-0.177 2.117 l
+h
+3.027 1.808 m
+3.241 2.05 3.484 2.167 3.748 2.161 c
+3.943 2.161 4.093 2.094 4.203 1.97 c
+4.31 1.852 4.369 1.679 4.38 1.455 c
+4.388 1.326 4.388 1.205 4.38 1.087 c
+4.056 -0.867 l
+3.571 -0.867 l
+3.91 1.103 l
+3.924 1.308 l
+3.924 1.591 3.806 1.738 3.571 1.749 c
+3.384 1.749 3.208 1.639 3.042 1.425 c
+2.954 1.308 l
+2.587 -0.867 l
+2.102 -0.867 l
+2.836 3.366 l
+3.322 3.366 l
+h
+6.732 -0.927 m
+6.397 -0.915 6.148 -0.794 5.982 -0.559 c
+5.813 -0.324 5.754 0 5.805 0.411 c
+5.835 0.706 l
+5.894 1.165 6.034 1.529 6.262 1.793 c
+6.485 2.057 6.769 2.183 7.114 2.175 c
+7.397 2.165 7.607 2.065 7.746 1.881 c
+7.893 1.694 7.966 1.429 7.966 1.087 c
+7.937 0.764 l
+7.893 0.484 l
+6.291 0.484 l
+6.269 0.316 6.262 0.183 6.262 0.088 c
+6.262 -0.1 6.306 -0.246 6.393 -0.353 c
+6.482 -0.452 6.607 -0.5 6.776 -0.5 c
+6.894 -0.511 7.011 -0.493 7.129 -0.441 c
+7.247 -0.382 7.364 -0.294 7.482 -0.177 c
+7.717 -0.485 l
+7.599 -0.632 7.452 -0.746 7.276 -0.823 c
+7.099 -0.89 6.915 -0.927 6.732 -0.927 c
+7.085 1.749 m
+6.761 1.768 6.533 1.573 6.408 1.161 c
+6.335 0.897 l
+7.482 0.897 l
+7.482 0.97 l
+7.5 1.047 7.511 1.135 7.511 1.234 c
+7.5 1.565 7.357 1.738 7.085 1.749 c
+10.715 1.66 m
+10.634 1.679 10.561 1.691 10.495 1.691 c
+10.26 1.691 10.061 1.547 9.907 1.264 c
+9.539 -0.867 l
+9.055 -0.867 l
+9.583 2.117 l
+10.039 2.117 l
+9.965 1.808 l
+10.142 2.061 10.341 2.183 10.568 2.175 c
+10.605 2.175 10.671 2.161 10.759 2.131 c
+h
+12.681 -0.927 m
+12.347 -0.915 12.097 -0.794 11.931 -0.559 c
+11.762 -0.324 11.704 0 11.755 0.411 c
+11.785 0.706 l
+11.843 1.165 11.983 1.529 12.211 1.793 c
+12.435 2.057 12.718 2.183 13.063 2.175 c
+13.346 2.165 13.556 2.065 13.695 1.881 c
+13.842 1.694 13.916 1.429 13.916 1.087 c
+13.886 0.764 l
+13.842 0.484 l
+12.24 0.484 l
+12.218 0.316 12.211 0.183 12.211 0.088 c
+12.211 -0.1 12.255 -0.246 12.343 -0.353 c
+12.431 -0.452 12.556 -0.5 12.725 -0.5 c
+12.843 -0.511 12.961 -0.493 13.078 -0.441 c
+13.196 -0.382 13.313 -0.294 13.431 -0.177 c
+13.666 -0.485 l
+13.549 -0.632 13.402 -0.746 13.225 -0.823 c
+13.049 -0.89 12.865 -0.927 12.681 -0.927 c
+13.034 1.749 m
+12.71 1.768 12.483 1.573 12.358 1.161 c
+12.284 0.897 l
+13.431 0.897 l
+13.431 0.97 l
+13.449 1.047 13.46 1.135 13.46 1.234 c
+13.449 1.565 13.306 1.738 13.034 1.749 c
+18.127 2.117 m
+18.069 1.793 l
+18.281 2.047 18.528 2.175 18.803 2.175 c
+18.987 2.165 19.134 2.094 19.245 1.97 c
+19.362 1.852 19.424 1.679 19.436 1.455 c
+19.443 1.326 19.443 1.205 19.436 1.087 c
+19.112 -0.867 l
+18.627 -0.867 l
+18.965 1.103 l
+18.979 1.308 l
+18.979 1.591 18.862 1.738 18.627 1.749 c
+18.439 1.749 18.264 1.639 18.098 1.425 c
+18.01 1.308 l
+17.642 -0.867 l
+17.157 -0.867 l
+17.686 2.117 l
+h
+20.887 0.75 m
+20.946 1.18 21.086 1.529 21.313 1.793 c
+21.549 2.057 21.842 2.183 22.195 2.175 c
+22.42 2.165 22.607 2.094 22.754 1.97 c
+22.9 1.852 23.004 1.683 23.062 1.469 c
+23.129 1.253 23.151 1.018 23.121 0.764 c
+23.091 0.484 l
+23.022 0.051 22.871 -0.294 22.636 -0.559 c
+22.408 -0.816 22.121 -0.933 21.769 -0.927 c
+21.541 -0.915 21.358 -0.852 21.21 -0.735 c
+21.063 -0.611 20.953 -0.441 20.887 -0.235 c
+20.828 -0.023 20.814 0.22 20.843 0.484 c
+h
+21.313 0.279 m
+21.302 0.033 21.335 -0.158 21.416 -0.294 c
+21.504 -0.434 21.629 -0.507 21.798 -0.515 c
+22.004 -0.526 22.173 -0.452 22.312 -0.294 c
+22.46 -0.129 22.555 0.118 22.607 0.441 c
+22.651 0.764 l
+22.665 0.97 l
+22.665 1.213 22.621 1.404 22.534 1.543 c
+22.453 1.679 22.331 1.749 22.166 1.749 c
+21.949 1.756 21.769 1.668 21.622 1.484 c
+21.483 1.297 21.394 1.036 21.358 0.706 c
+21.313 0.426 l
+h
+f
+Q
+q 1 0 0 1 198.4339 102.7654 cm
+0 0 m
+0.661 1.867 l
+1.47 1.867 l
+0.206 -1.117 l
+-0.515 -1.117 l
+-0.852 1.867 l
+-0.073 1.867 l
+h
+3.396 -1.176 m
+3.15 -1.176 2.944 -1.117 2.778 -0.999 c
+2.61 -0.881 2.488 -0.72 2.411 -0.514 c
+2.341 -0.301 2.323 -0.058 2.352 0.206 c
+2.367 0.412 l
+2.433 0.9 2.587 1.279 2.822 1.544 c
+3.057 1.808 3.359 1.933 3.734 1.926 c
+4.006 1.915 4.222 1.827 4.38 1.661 c
+4.535 1.503 4.623 1.279 4.645 0.985 c
+4.663 0.809 4.663 0.636 4.645 0.47 c
+4.586 0.118 l
+3.102 0.118 l
+3.09 0.037 3.087 -0.04 3.087 -0.118 c
+3.105 -0.382 3.238 -0.514 3.484 -0.514 c
+3.708 -0.525 3.929 -0.44 4.145 -0.264 c
+4.41 -0.735 l
+4.3 -0.874 4.152 -0.981 3.969 -1.058 c
+3.792 -1.135 3.601 -1.176 3.396 -1.176 c
+3.66 1.279 m
+3.443 1.287 3.293 1.147 3.204 0.853 c
+3.146 0.662 l
+3.91 0.662 l
+3.929 0.757 3.94 0.842 3.94 0.912 c
+3.946 1.147 3.855 1.268 3.66 1.279 c
+7.493 1.118 m
+7.258 1.147 l
+7.052 1.147 6.883 1.048 6.758 0.853 c
+6.42 -1.117 l
+5.656 -1.117 l
+6.17 1.867 l
+6.89 1.867 l
+6.832 1.544 l
+6.92 1.68 7.001 1.779 7.082 1.838 c
+7.169 1.897 7.265 1.926 7.375 1.926 c
+7.435 1.926 7.508 1.912 7.596 1.881 c
+h
+9.801 -0.294 m
+9.819 -0.168 9.716 -0.055 9.492 0.044 c
+9.276 0.14 9.114 0.235 9.007 0.324 c
+8.897 0.42 8.816 0.522 8.757 0.632 c
+8.706 0.739 8.688 0.86 8.698 1 c
+8.706 1.264 8.816 1.485 9.022 1.661 c
+9.235 1.838 9.496 1.926 9.801 1.926 c
+10.095 1.915 10.323 1.823 10.492 1.646 c
+10.668 1.478 10.756 1.257 10.756 0.985 c
+9.992 0.985 l
+9.992 1.11 9.97 1.199 9.933 1.25 c
+9.893 1.309 9.834 1.338 9.756 1.338 c
+9.669 1.338 9.588 1.309 9.521 1.25 c
+9.463 1.191 9.426 1.118 9.419 1.029 c
+9.39 0.912 9.477 0.802 9.683 0.706 c
+9.897 0.618 10.051 0.545 10.139 0.485 c
+10.411 0.298 10.539 0.048 10.521 -0.264 c
+10.51 -0.452 10.452 -0.613 10.344 -0.749 c
+10.246 -0.889 10.109 -0.995 9.933 -1.072 c
+9.764 -1.139 9.58 -1.176 9.374 -1.176 c
+9.081 -1.165 8.838 -1.072 8.654 -0.897 c
+8.478 -0.712 8.39 -0.477 8.39 -0.191 c
+9.124 -0.191 l
+9.114 -0.338 9.132 -0.448 9.183 -0.514 c
+9.242 -0.573 9.323 -0.602 9.434 -0.602 c
+9.521 -0.602 9.598 -0.58 9.669 -0.529 c
+9.735 -0.47 9.779 -0.393 9.801 -0.294 c
+12.517 -1.117 m
+11.767 -1.117 l
+12.281 1.867 l
+13.045 1.867 l
+h
+12.384 2.617 m
+12.384 2.741 12.421 2.845 12.501 2.926 c
+12.579 3.014 12.678 3.057 12.796 3.057 c
+12.902 3.057 12.994 3.017 13.075 2.94 c
+13.163 2.859 13.207 2.764 13.207 2.646 c
+13.207 2.517 13.163 2.415 13.075 2.338 c
+12.994 2.257 12.898 2.22 12.781 2.22 c
+12.663 2.22 12.565 2.253 12.487 2.323 c
+12.417 2.4 12.384 2.5 12.384 2.617 c
+15.578 1.926 m
+15.879 1.915 16.114 1.812 16.283 1.617 c
+16.449 1.419 16.54 1.154 16.563 0.823 c
+16.563 0.632 l
+16.533 0.052 16.39 -0.396 16.137 -0.72 c
+15.89 -1.036 15.563 -1.183 15.152 -1.176 c
+14.916 -1.165 14.718 -1.102 14.564 -0.984 c
+14.416 -0.86 14.306 -0.687 14.24 -0.47 c
+14.17 -0.257 14.152 -0.007 14.181 0.279 c
+14.196 0.427 l
+14.255 0.897 14.406 1.264 14.651 1.529 c
+14.894 1.801 15.202 1.933 15.578 1.926 c
+14.93 -0.029 m
+14.93 -0.353 15.026 -0.521 15.225 -0.529 c
+15.489 -0.551 15.659 -0.349 15.74 0.074 c
+15.769 0.279 l
+15.798 0.545 15.813 0.709 15.813 0.78 c
+15.813 1.103 15.709 1.268 15.504 1.279 c
+15.357 1.287 15.232 1.22 15.136 1.073 c
+15.048 0.934 14.99 0.728 14.961 0.456 c
+14.938 0.192 14.93 0.03 14.93 -0.029 c
+18.811 1.867 m
+18.767 1.573 l
+18.973 1.816 19.2 1.933 19.458 1.926 c
+19.664 1.915 19.819 1.831 19.929 1.676 c
+20.046 1.529 20.105 1.312 20.105 1.029 c
+20.075 0.78 l
+19.767 -1.117 l
+19.002 -1.117 l
+19.311 0.78 l
+19.326 0.941 l
+19.333 1.154 19.256 1.264 19.09 1.264 c
+19.01 1.264 18.944 1.243 18.884 1.206 c
+18.826 1.166 18.774 1.118 18.738 1.058 c
+18.356 -1.117 l
+17.591 -1.117 l
+18.105 1.867 l
+h
+24.515 -0.529 m
+24.721 -0.54 24.845 -0.393 24.897 -0.087 c
+25.617 -0.087 l
+25.577 -0.411 25.448 -0.675 25.234 -0.881 c
+25.029 -1.087 24.772 -1.183 24.471 -1.176 c
+24.184 -1.165 23.96 -1.084 23.794 -0.926 c
+23.636 -0.771 23.545 -0.551 23.515 -0.264 c
+23.486 -0.029 23.493 0.25 23.545 0.574 c
+23.603 0.904 23.706 1.18 23.853 1.397 c
+24.089 1.757 24.43 1.933 24.882 1.926 c
+25.176 1.915 25.4 1.801 25.558 1.588 c
+25.724 1.382 25.797 1.103 25.779 0.75 c
+25.058 0.75 l
+25.058 0.927 l
+25.058 1.151 24.97 1.268 24.794 1.279 c
+24.529 1.287 24.368 1.077 24.309 0.647 c
+24.264 0.148 l
+24.235 0 24.224 -0.124 24.235 -0.22 c
+24.253 -0.419 24.345 -0.521 24.515 -0.529 c
+28.307 1.926 m
+28.608 1.915 28.843 1.812 29.012 1.617 c
+29.178 1.419 29.269 1.154 29.292 0.823 c
+29.292 0.632 l
+29.263 0.052 29.119 -0.396 28.866 -0.72 c
+28.619 -1.036 28.292 -1.183 27.881 -1.176 c
+27.646 -1.165 27.447 -1.102 27.293 -0.984 c
+27.146 -0.86 27.035 -0.687 26.969 -0.47 c
+26.899 -0.257 26.881 -0.007 26.911 0.279 c
+26.925 0.427 l
+26.984 0.897 27.135 1.264 27.381 1.529 c
+27.623 1.801 27.932 1.933 28.307 1.926 c
+27.661 -0.029 m
+27.661 -0.353 27.756 -0.521 27.954 -0.529 c
+28.218 -0.551 28.388 -0.349 28.469 0.074 c
+28.498 0.279 l
+28.527 0.545 28.542 0.709 28.542 0.78 c
+28.542 1.103 28.439 1.268 28.233 1.279 c
+28.087 1.287 27.962 1.22 27.866 1.073 c
+27.778 0.934 27.719 0.728 27.69 0.456 c
+27.667 0.192 27.661 0.03 27.661 -0.029 c
+31.541 1.867 m
+31.497 1.573 l
+31.702 1.816 31.931 1.933 32.187 1.926 c
+32.393 1.915 32.548 1.831 32.658 1.676 c
+32.775 1.529 32.835 1.312 32.835 1.029 c
+32.805 0.78 l
+32.496 -1.117 l
+31.732 -1.117 l
+32.041 0.78 l
+32.055 0.941 l
+32.062 1.154 31.985 1.264 31.82 1.264 c
+31.739 1.264 31.673 1.243 31.614 1.206 c
+31.555 1.166 31.503 1.118 31.467 1.058 c
+31.085 -1.117 l
+30.321 -1.117 l
+30.836 1.867 l
+h
+35.432 2.602 m
+35.314 1.867 l
+35.696 1.867 l
+35.594 1.279 l
+35.212 1.279 l
+34.948 -0.22 l
+34.948 -0.309 l
+34.936 -0.437 34.984 -0.5 35.094 -0.5 c
+35.123 -0.5 35.186 -0.496 35.285 -0.484 c
+35.212 -1.102 l
+35.102 -1.15 34.969 -1.176 34.815 -1.176 c
+34.599 -1.165 34.433 -1.084 34.315 -0.926 c
+34.205 -0.771 34.161 -0.558 34.183 -0.294 c
+34.447 1.279 l
+34.109 1.279 l
+34.212 1.867 l
+34.551 1.867 l
+34.668 2.602 l
+h
+38.317 1.118 m
+38.082 1.147 l
+37.876 1.147 37.707 1.048 37.582 0.853 c
+37.244 -1.117 l
+36.48 -1.117 l
+36.994 1.867 l
+37.714 1.867 l
+37.656 1.544 l
+37.743 1.68 37.824 1.779 37.905 1.838 c
+37.994 1.897 38.089 1.926 38.2 1.926 c
+38.258 1.926 38.331 1.912 38.42 1.881 c
+h
+40.658 1.926 m
+40.959 1.915 41.194 1.812 41.363 1.617 c
+41.529 1.419 41.62 1.154 41.643 0.823 c
+41.643 0.632 l
+41.614 0.052 41.47 -0.396 41.217 -0.72 c
+40.97 -1.036 40.643 -1.183 40.232 -1.176 c
+39.996 -1.165 39.798 -1.102 39.644 -0.984 c
+39.497 -0.86 39.386 -0.687 39.32 -0.47 c
+39.25 -0.257 39.232 -0.007 39.262 0.279 c
+39.276 0.427 l
+39.335 0.897 39.486 1.264 39.732 1.529 c
+39.974 1.801 40.283 1.933 40.658 1.926 c
+40.011 -0.029 m
+40.011 -0.353 40.107 -0.521 40.305 -0.529 c
+40.569 -0.551 40.739 -0.349 40.82 0.074 c
+40.849 0.279 l
+40.878 0.545 40.893 0.709 40.893 0.78 c
+40.893 1.103 40.79 1.268 40.584 1.279 c
+40.438 1.287 40.313 1.22 40.217 1.073 c
+40.128 0.934 40.07 0.728 40.041 0.456 c
+40.018 0.192 40.011 0.03 40.011 -0.029 c
+43.48 -1.117 m
+42.73 -1.117 l
+43.451 3.117 l
+44.23 3.117 l
+h
+f
+Q
+q 1 0 0 1 247.9403 102.5161 cm
+0 0 m
+0.809 2.117 l
+1.294 2.117 l
+0.059 -0.867 l
+-0.309 -0.867 l
+-0.5 1.264 l
+-1.425 -0.867 l
+-1.808 -0.867 l
+-1.999 2.117 l
+-1.543 2.117 l
+-1.455 0.058 l
+-0.559 2.117 l
+-0.176 2.117 l
+h
+3.631 -0.867 m
+3.62 -0.809 3.616 -0.757 3.616 -0.706 c
+3.631 -0.544 l
+3.425 -0.802 3.198 -0.927 2.955 -0.927 c
+2.708 -0.927 2.517 -0.849 2.381 -0.691 c
+2.253 -0.537 2.198 -0.331 2.22 -0.073 c
+2.238 0.228 2.367 0.474 2.602 0.661 c
+2.837 0.845 3.138 0.941 3.514 0.941 c
+3.851 0.941 l
+3.896 1.264 l
+3.925 1.587 3.8 1.749 3.528 1.749 c
+3.381 1.749 3.256 1.705 3.161 1.617 c
+3.061 1.536 3.003 1.429 2.984 1.294 c
+2.514 1.294 l
+2.532 1.536 2.646 1.741 2.851 1.911 c
+3.057 2.087 3.296 2.175 3.572 2.175 c
+3.836 2.165 4.042 2.08 4.189 1.926 c
+4.337 1.768 4.395 1.543 4.366 1.249 c
+4.131 -0.221 l
+4.108 -0.32 4.102 -0.42 4.102 -0.515 c
+4.116 -0.823 l
+4.116 -0.867 l
+h
+3.043 -0.485 m
+3.285 -0.485 3.491 -0.368 3.66 -0.133 c
+3.792 0.573 l
+3.572 0.588 l
+3.385 0.588 3.219 0.551 3.072 0.484 c
+2.926 0.415 2.822 0.316 2.764 0.191 c
+2.705 0.073 2.683 -0.059 2.705 -0.206 c
+2.723 -0.393 2.837 -0.485 3.043 -0.485 c
+7.162 -0.088 m
+7.192 0.088 7.092 0.235 6.868 0.353 c
+6.501 0.544 l
+6.313 0.65 6.181 0.764 6.104 0.881 c
+6.024 0.999 5.99 1.139 6.001 1.308 c
+6.008 1.55 6.107 1.756 6.295 1.926 c
+6.49 2.09 6.729 2.175 7.015 2.175 c
+7.279 2.165 7.485 2.076 7.632 1.911 c
+7.787 1.741 7.857 1.529 7.838 1.264 c
+7.368 1.264 l
+7.375 1.411 7.342 1.529 7.265 1.617 c
+7.196 1.705 7.104 1.749 6.986 1.749 c
+6.839 1.749 6.722 1.708 6.633 1.631 c
+6.545 1.55 6.49 1.448 6.471 1.323 c
+6.442 1.176 6.508 1.051 6.677 0.955 c
+7.148 0.72 l
+7.489 0.521 7.651 0.268 7.632 -0.044 c
+7.611 -0.32 7.504 -0.54 7.31 -0.706 c
+7.111 -0.864 6.872 -0.933 6.589 -0.927 c
+6.313 -0.915 6.089 -0.831 5.913 -0.661 c
+5.744 -0.485 5.667 -0.258 5.677 0.029 c
+6.163 0.029 l
+6.152 -0.147 6.184 -0.279 6.265 -0.368 c
+6.354 -0.456 6.464 -0.5 6.604 -0.5 c
+6.758 -0.5 6.886 -0.463 6.986 -0.382 c
+7.082 -0.305 7.14 -0.206 7.162 -0.088 c
+12.689 -0.603 m
+12.491 -0.831 12.252 -0.933 11.969 -0.927 c
+11.752 -0.927 11.587 -0.852 11.469 -0.706 c
+11.358 -0.551 11.308 -0.335 11.308 -0.059 c
+11.322 0.191 l
+11.645 2.117 l
+12.116 2.117 l
+11.807 0.176 l
+11.792 -0.015 l
+11.782 -0.154 11.8 -0.264 11.851 -0.353 c
+11.899 -0.441 11.977 -0.485 12.087 -0.485 c
+12.351 -0.507 12.565 -0.375 12.733 -0.088 c
+13.116 2.117 l
+13.6 2.117 l
+13.086 -0.867 l
+12.63 -0.867 l
+h
+16.214 -0.088 m
+16.243 0.088 16.143 0.235 15.919 0.353 c
+15.551 0.544 l
+15.364 0.65 15.232 0.764 15.155 0.881 c
+15.074 0.999 15.041 1.139 15.052 1.308 c
+15.059 1.55 15.158 1.756 15.346 1.926 c
+15.541 2.09 15.78 2.175 16.066 2.175 c
+16.331 2.165 16.536 2.076 16.684 1.911 c
+16.838 1.741 16.908 1.529 16.889 1.264 c
+16.419 1.264 l
+16.426 1.411 16.393 1.529 16.316 1.617 c
+16.247 1.705 16.154 1.749 16.037 1.749 c
+15.89 1.749 15.772 1.708 15.684 1.631 c
+15.596 1.55 15.541 1.448 15.522 1.323 c
+15.493 1.176 15.559 1.051 15.728 0.955 c
+16.199 0.72 l
+16.54 0.521 16.702 0.268 16.684 -0.044 c
+16.661 -0.32 16.555 -0.54 16.36 -0.706 c
+16.162 -0.864 15.923 -0.933 15.64 -0.927 c
+15.364 -0.915 15.14 -0.831 14.963 -0.661 c
+14.795 -0.485 14.718 -0.258 14.728 0.029 c
+15.214 0.029 l
+15.202 -0.147 15.236 -0.279 15.316 -0.368 c
+15.405 -0.456 15.515 -0.5 15.655 -0.5 c
+15.809 -0.5 15.938 -0.463 16.037 -0.382 c
+16.133 -0.305 16.191 -0.206 16.214 -0.088 c
+19.146 -0.927 m
+18.811 -0.915 18.562 -0.794 18.396 -0.559 c
+18.227 -0.324 18.168 0 18.219 0.411 c
+18.249 0.706 l
+18.308 1.165 18.448 1.529 18.676 1.793 c
+18.899 2.057 19.183 2.183 19.528 2.175 c
+19.811 2.165 20.02 2.065 20.16 1.881 c
+20.307 1.694 20.38 1.429 20.38 1.087 c
+20.351 0.764 l
+20.307 0.484 l
+18.705 0.484 l
+18.683 0.316 18.676 0.183 18.676 0.088 c
+18.676 -0.1 18.72 -0.246 18.807 -0.353 c
+18.896 -0.452 19.021 -0.5 19.19 -0.5 c
+19.308 -0.511 19.425 -0.493 19.543 -0.441 c
+19.66 -0.382 19.778 -0.294 19.896 -0.177 c
+20.131 -0.485 l
+20.013 -0.632 19.866 -0.746 19.69 -0.823 c
+19.513 -0.89 19.329 -0.927 19.146 -0.927 c
+19.499 1.749 m
+19.175 1.768 18.947 1.573 18.822 1.161 c
+18.749 0.897 l
+19.896 0.897 l
+19.896 0.97 l
+19.914 1.047 19.925 1.135 19.925 1.234 c
+19.914 1.565 19.771 1.738 19.499 1.749 c
+22.82 2.161 m
+23.092 2.161 23.298 2.047 23.438 1.822 c
+23.732 3.366 l
+24.202 3.366 l
+23.468 -0.867 l
+23.041 -0.867 l
+23.085 -0.544 l
+22.886 -0.809 22.651 -0.933 22.379 -0.927 c
+22.134 -0.915 21.942 -0.819 21.806 -0.632 c
+21.666 -0.449 21.6 -0.177 21.6 0.176 c
+21.6 0.341 21.619 0.559 21.66 0.823 c
+21.707 1.095 21.777 1.326 21.865 1.514 c
+22.071 1.962 22.387 2.179 22.82 2.161 c
+22.071 0.058 m
+22.078 -0.305 22.232 -0.493 22.527 -0.5 c
+22.633 -0.5 22.736 -0.478 22.836 -0.426 c
+22.931 -0.368 23.026 -0.272 23.114 -0.133 c
+23.379 1.382 l
+23.291 1.606 23.137 1.723 22.923 1.735 c
+22.483 1.764 22.217 1.444 22.13 0.779 c
+22.089 0.455 22.071 0.213 22.071 0.058 c
+25.455 -0.294 m
+25.544 -0.287 25.617 -0.309 25.676 -0.368 c
+25.735 -0.426 25.764 -0.5 25.764 -0.588 c
+25.764 -0.676 25.735 -0.75 25.676 -0.809 c
+25.617 -0.867 25.544 -0.897 25.455 -0.897 c
+25.367 -0.904 25.294 -0.886 25.234 -0.838 c
+25.176 -0.779 25.147 -0.706 25.147 -0.617 c
+25.147 -0.522 25.176 -0.441 25.234 -0.382 c
+25.294 -0.324 25.367 -0.294 25.455 -0.294 c
+f
+Q
+q 1 0 0 1 138.5097 93.1379 cm
+0 0 m
+0.691 4.013 l
+1.587 4.013 l
+2.017 4.002 2.337 3.844 2.543 3.543 c
+2.749 3.237 2.822 2.837 2.763 2.337 c
+2.675 1.706 l
+2.587 1.147 2.389 0.721 2.087 0.426 c
+1.782 0.139 1.396 0 0.926 0 c
+h
+1.117 3.572 m
+0.573 0.426 l
+0.926 0.426 l
+1.268 0.426 1.547 0.53 1.764 0.735 c
+1.977 0.941 2.117 1.253 2.175 1.675 c
+2.278 2.352 l
+2.296 2.499 2.308 2.646 2.308 2.793 c
+2.296 3.036 2.227 3.223 2.102 3.352 c
+1.984 3.487 1.808 3.561 1.573 3.572 c
+h
+4.119 1.617 m
+4.178 2.047 4.318 2.396 4.546 2.66 c
+4.781 2.925 5.074 3.05 5.428 3.042 c
+5.652 3.032 5.839 2.961 5.986 2.837 c
+6.134 2.72 6.236 2.55 6.294 2.337 c
+6.361 2.12 6.383 1.885 6.354 1.631 c
+6.325 1.353 l
+6.254 0.919 6.104 0.573 5.868 0.309 c
+5.641 0.052 5.354 -0.066 5.001 -0.058 c
+4.773 -0.048 4.59 0.015 4.443 0.133 c
+4.295 0.257 4.185 0.426 4.119 0.632 c
+4.06 0.845 4.046 1.087 4.075 1.353 c
+h
+4.546 1.147 m
+4.534 0.9 4.567 0.709 4.648 0.573 c
+4.737 0.434 4.862 0.36 5.031 0.353 c
+5.236 0.341 5.405 0.415 5.545 0.573 c
+5.692 0.738 5.787 0.985 5.839 1.309 c
+5.883 1.631 l
+5.898 1.837 l
+5.898 2.08 5.854 2.271 5.766 2.411 c
+5.685 2.547 5.564 2.616 5.398 2.616 c
+5.182 2.624 5.001 2.535 4.854 2.352 c
+4.715 2.165 4.627 1.903 4.59 1.573 c
+4.546 1.294 l
+h
+8.507 2.984 m
+8.448 2.66 l
+8.661 2.914 8.908 3.042 9.183 3.042 c
+9.367 3.032 9.514 2.961 9.624 2.837 c
+9.741 2.72 9.804 2.547 9.816 2.323 c
+9.822 2.194 9.822 2.072 9.816 1.955 c
+9.492 0 l
+9.007 0 l
+9.345 1.97 l
+9.359 2.176 l
+9.359 2.458 9.242 2.606 9.007 2.616 c
+8.819 2.616 8.643 2.506 8.478 2.294 c
+8.39 2.176 l
+8.022 0 l
+7.537 0 l
+8.066 2.984 l
+h
+11.752 3.91 m
+11.546 2.911 l
+11.193 2.911 l
+11.399 4.233 l
+11.811 4.233 l
+h
+13.924 3.705 m
+13.791 2.984 l
+14.232 2.984 l
+14.159 2.587 l
+13.718 2.587 l
+13.409 0.735 l
+13.409 0.617 l
+13.398 0.459 13.454 0.382 13.571 0.382 c
+13.619 0.382 13.689 0.389 13.776 0.412 c
+13.733 0 l
+13.622 -0.037 13.519 -0.052 13.424 -0.044 c
+13.255 -0.044 13.13 0.022 13.041 0.162 c
+12.954 0.297 12.924 0.489 12.954 0.735 c
+13.247 2.587 l
+12.806 2.587 l
+12.88 2.984 l
+13.321 2.984 l
+13.438 3.705 l
+h
+17.822 0 m
+17.352 0 l
+18.088 4.233 l
+18.558 4.233 l
+h
+19.976 0 m
+19.505 0 l
+20.02 2.984 l
+20.505 2.984 l
+h
+20.137 3.778 m
+20.137 3.855 20.16 3.925 20.211 3.983 c
+20.259 4.042 20.322 4.072 20.402 4.072 c
+20.48 4.072 20.542 4.042 20.594 3.983 c
+20.652 3.932 20.675 3.865 20.667 3.778 c
+20.667 3.697 20.641 3.63 20.594 3.572 c
+20.542 3.513 20.48 3.484 20.402 3.484 c
+20.314 3.472 20.245 3.499 20.197 3.557 c
+20.145 3.616 20.126 3.69 20.137 3.778 c
+22.585 0.75 m
+23.482 2.984 l
+23.981 2.984 l
+22.659 0 l
+22.291 0 l
+21.894 2.984 l
+22.364 2.984 l
+h
+25.926 -0.058 m
+25.591 -0.048 25.341 0.073 25.176 0.309 c
+25.007 0.544 24.948 0.867 24.999 1.278 c
+25.028 1.573 l
+25.088 2.032 25.227 2.396 25.455 2.66 c
+25.679 2.925 25.963 3.05 26.308 3.042 c
+26.591 3.032 26.8 2.932 26.94 2.749 c
+27.087 2.562 27.16 2.296 27.16 1.955 c
+27.131 1.631 l
+27.087 1.353 l
+25.485 1.353 l
+25.462 1.183 25.455 1.051 25.455 0.956 c
+25.455 0.768 25.499 0.621 25.587 0.515 c
+25.676 0.415 25.801 0.368 25.969 0.368 c
+26.087 0.357 26.204 0.374 26.322 0.426 c
+26.439 0.485 26.557 0.573 26.675 0.691 c
+26.91 0.382 l
+26.792 0.235 26.646 0.121 26.47 0.044 c
+26.293 -0.022 26.109 -0.058 25.926 -0.058 c
+26.279 2.616 m
+25.955 2.635 25.727 2.44 25.602 2.028 c
+25.529 1.764 l
+26.675 1.764 l
+26.675 1.837 l
+26.693 1.914 26.705 2.003 26.705 2.102 c
+26.693 2.433 26.55 2.606 26.279 2.616 c
+30.916 0 m
+30.445 0 l
+30.96 2.984 l
+31.445 2.984 l
+h
+31.077 3.778 m
+31.077 3.855 31.1 3.925 31.151 3.983 c
+31.199 4.042 31.261 4.072 31.342 4.072 c
+31.419 4.072 31.482 4.042 31.534 3.983 c
+31.592 3.932 31.614 3.865 31.607 3.778 c
+31.607 3.697 31.581 3.63 31.534 3.572 c
+31.482 3.513 31.419 3.484 31.342 3.484 c
+31.254 3.472 31.184 3.499 31.137 3.557 c
+31.085 3.616 31.066 3.69 31.077 3.778 c
+33.525 2.984 m
+33.466 2.66 l
+33.679 2.914 33.925 3.042 34.201 3.042 c
+34.385 3.032 34.532 2.961 34.642 2.837 c
+34.759 2.72 34.822 2.547 34.833 2.323 c
+34.84 2.194 34.84 2.072 34.833 1.955 c
+34.51 0 l
+34.025 0 l
+34.362 1.97 l
+34.377 2.176 l
+34.377 2.458 34.26 2.606 34.025 2.616 c
+33.837 2.616 33.661 2.506 33.495 2.294 c
+33.408 2.176 l
+33.04 0 l
+32.554 0 l
+33.084 2.984 l
+h
+39.32 0.353 m
+39.474 0.353 39.603 0.401 39.702 0.5 c
+39.808 0.607 39.885 0.754 39.937 0.941 c
+40.378 0.941 l
+40.338 0.647 40.217 0.401 40.011 0.206 c
+39.805 0.019 39.563 -0.066 39.291 -0.058 c
+39.026 -0.048 38.813 0.029 38.659 0.177 c
+38.501 0.331 38.409 0.548 38.379 0.823 c
+38.357 0.989 38.357 1.154 38.379 1.323 c
+38.424 1.617 l
+38.482 2.087 38.618 2.444 38.835 2.69 c
+39.06 2.932 39.353 3.05 39.717 3.042 c
+39.989 3.032 40.202 2.929 40.349 2.734 c
+40.504 2.535 40.577 2.278 40.569 1.955 c
+40.114 1.955 l
+40.121 2.385 39.981 2.606 39.687 2.616 c
+39.254 2.635 38.996 2.33 38.908 1.706 c
+38.85 1.312 38.824 1.043 38.835 0.897 c
+38.853 0.544 39.015 0.36 39.32 0.353 c
+42.565 2.675 m
+42.778 2.918 43.02 3.036 43.285 3.028 c
+43.48 3.028 43.631 2.961 43.741 2.837 c
+43.847 2.72 43.906 2.547 43.917 2.323 c
+43.924 2.194 43.924 2.072 43.917 1.955 c
+43.594 0 l
+43.108 0 l
+43.447 1.97 l
+43.461 2.176 l
+43.461 2.458 43.344 2.606 43.108 2.616 c
+42.921 2.616 42.744 2.506 42.58 2.294 c
+42.491 2.176 l
+42.123 0 l
+41.639 0 l
+42.374 4.233 l
+42.859 4.233 l
+h
+46.68 0 m
+46.669 0.059 46.665 0.11 46.665 0.162 c
+46.68 0.324 l
+46.474 0.066 46.247 -0.058 46.004 -0.058 c
+45.759 -0.058 45.567 0.019 45.431 0.177 c
+45.302 0.331 45.248 0.536 45.269 0.794 c
+45.288 1.095 45.416 1.341 45.651 1.529 c
+45.886 1.712 46.188 1.808 46.563 1.808 c
+46.901 1.808 l
+46.945 2.132 l
+46.975 2.454 46.85 2.616 46.578 2.616 c
+46.43 2.616 46.306 2.572 46.21 2.484 c
+46.111 2.404 46.052 2.296 46.034 2.161 c
+45.564 2.161 l
+45.582 2.404 45.695 2.609 45.902 2.778 c
+46.108 2.955 46.346 3.042 46.622 3.042 c
+46.886 3.032 47.092 2.947 47.239 2.793 c
+47.386 2.635 47.445 2.411 47.415 2.117 c
+47.18 0.647 l
+47.158 0.548 47.151 0.449 47.151 0.353 c
+47.166 0.044 l
+47.166 0 l
+h
+46.092 0.382 m
+46.335 0.382 46.541 0.5 46.71 0.735 c
+46.842 1.44 l
+46.622 1.455 l
+46.434 1.455 46.269 1.419 46.122 1.353 c
+45.975 1.282 45.872 1.183 45.813 1.058 c
+45.755 0.941 45.732 0.808 45.755 0.661 c
+45.773 0.474 45.886 0.382 46.092 0.382 c
+48.863 1.617 m
+48.922 2.047 49.061 2.396 49.289 2.66 c
+49.524 2.925 49.819 3.05 50.172 3.042 c
+50.395 3.032 50.584 2.961 50.73 2.837 c
+50.877 2.72 50.98 2.55 51.039 2.337 c
+51.105 2.12 51.127 1.885 51.097 1.631 c
+51.068 1.353 l
+50.998 0.919 50.848 0.573 50.613 0.309 c
+50.385 0.052 50.098 -0.066 49.745 -0.058 c
+49.518 -0.048 49.333 0.015 49.187 0.133 c
+49.04 0.257 48.93 0.426 48.863 0.632 c
+48.805 0.845 48.79 1.087 48.819 1.353 c
+h
+49.289 1.147 m
+49.279 0.9 49.312 0.709 49.393 0.573 c
+49.481 0.434 49.605 0.36 49.775 0.353 c
+49.981 0.341 50.15 0.415 50.289 0.573 c
+50.436 0.738 50.532 0.985 50.584 1.309 c
+50.627 1.631 l
+50.642 1.837 l
+50.642 2.08 50.598 2.271 50.509 2.411 c
+50.428 2.547 50.308 2.616 50.142 2.616 c
+49.925 2.624 49.745 2.535 49.599 2.352 c
+49.458 2.165 49.37 1.903 49.333 1.573 c
+49.289 1.294 l
+h
+53.836 0.779 m
+53.865 0.956 53.765 1.103 53.541 1.22 c
+53.173 1.411 l
+52.986 1.517 52.855 1.631 52.777 1.749 c
+52.696 1.866 52.663 2.007 52.674 2.176 c
+52.681 2.418 52.78 2.624 52.968 2.793 c
+53.163 2.959 53.402 3.042 53.688 3.042 c
+53.953 3.032 54.158 2.944 54.306 2.778 c
+54.46 2.609 54.53 2.396 54.511 2.132 c
+54.041 2.132 l
+54.048 2.278 54.015 2.396 53.938 2.484 c
+53.869 2.572 53.776 2.616 53.659 2.616 c
+53.512 2.616 53.394 2.576 53.306 2.499 c
+53.218 2.418 53.163 2.315 53.144 2.19 c
+53.115 2.043 53.181 1.918 53.35 1.823 c
+53.821 1.588 l
+54.162 1.389 54.324 1.135 54.306 0.823 c
+54.283 0.548 54.177 0.327 53.982 0.162 c
+53.784 0.004 53.545 -0.066 53.262 -0.058 c
+52.986 -0.048 52.762 0.037 52.585 0.206 c
+52.417 0.382 52.34 0.61 52.35 0.897 c
+52.836 0.897 l
+52.824 0.721 52.857 0.588 52.938 0.5 c
+53.027 0.412 53.137 0.368 53.277 0.368 c
+53.431 0.368 53.56 0.405 53.659 0.485 c
+53.755 0.563 53.813 0.661 53.836 0.779 c
+56.4 1.132 m
+55.974 1.132 l
+56.429 4.013 l
+56.93 4.013 l
+h
+55.768 0.25 m
+55.768 0.327 55.791 0.397 55.841 0.455 c
+55.889 0.515 55.959 0.548 56.047 0.559 c
+56.125 0.559 56.188 0.53 56.238 0.47 c
+56.286 0.412 56.312 0.341 56.312 0.264 c
+56.312 0.183 56.286 0.118 56.238 0.059 c
+56.188 0 56.121 -0.029 56.032 -0.029 c
+55.952 -0.037 55.889 -0.019 55.841 0.029 c
+55.791 0.088 55.768 0.162 55.768 0.25 c
+f
+Q
+q 1 0 0 1 206.7387 88.644 cm
+0 0 m
+-0.47 -2.749 l
+-0.529 -3.171 -0.687 -3.499 -0.941 -3.734 c
+-1.186 -3.969 -1.514 -4.079 -1.926 -4.072 c
+-2.319 -4.061 -2.612 -3.94 -2.807 -3.705 c
+-3.006 -3.469 -3.083 -3.153 -3.042 -2.749 c
+-2.572 0 l
+-1.778 0 l
+-2.234 -2.764 l
+-2.248 -2.999 l
+-2.248 -3.256 -2.123 -3.389 -1.866 -3.396 c
+-1.691 -3.406 -1.554 -3.363 -1.455 -3.263 c
+-1.359 -3.157 -1.29 -2.992 -1.249 -2.764 c
+-0.793 0 l
+h
+2.124 -3.19 m
+2.143 -3.065 2.04 -2.951 1.816 -2.852 c
+1.599 -2.756 1.437 -2.66 1.33 -2.573 c
+1.22 -2.477 1.139 -2.374 1.081 -2.264 c
+1.029 -2.157 1.011 -2.036 1.022 -1.897 c
+1.029 -1.632 1.139 -1.411 1.345 -1.235 c
+1.559 -1.058 1.819 -0.971 2.124 -0.971 c
+2.419 -0.981 2.646 -1.073 2.816 -1.249 c
+2.992 -1.419 3.08 -1.64 3.08 -1.912 c
+2.315 -1.912 l
+2.315 -1.786 2.294 -1.698 2.257 -1.646 c
+2.216 -1.588 2.157 -1.559 2.08 -1.559 c
+1.992 -1.559 1.912 -1.588 1.845 -1.646 c
+1.786 -1.706 1.75 -1.779 1.742 -1.867 c
+1.713 -1.985 1.801 -2.095 2.007 -2.19 c
+2.22 -2.278 2.374 -2.352 2.462 -2.411 c
+2.735 -2.598 2.863 -2.849 2.845 -3.161 c
+2.833 -3.348 2.775 -3.51 2.668 -3.645 c
+2.569 -3.786 2.433 -3.892 2.257 -3.969 c
+2.087 -4.035 1.904 -4.072 1.698 -4.072 c
+1.405 -4.061 1.162 -3.969 0.977 -3.793 c
+0.801 -3.609 0.713 -3.373 0.713 -3.088 c
+1.448 -3.088 l
+1.437 -3.234 1.455 -3.344 1.507 -3.41 c
+1.565 -3.469 1.646 -3.499 1.756 -3.499 c
+1.845 -3.499 1.922 -3.477 1.992 -3.425 c
+2.058 -3.366 2.103 -3.289 2.124 -3.19 c
+5.281 -4.072 m
+5.035 -4.072 4.829 -4.013 4.663 -3.896 c
+4.495 -3.778 4.373 -3.616 4.296 -3.41 c
+4.227 -3.198 4.208 -2.955 4.237 -2.691 c
+4.252 -2.484 l
+4.318 -1.995 4.472 -1.617 4.707 -1.353 c
+4.943 -1.088 5.245 -0.963 5.619 -0.971 c
+5.891 -0.981 6.108 -1.07 6.265 -1.235 c
+6.421 -1.393 6.508 -1.617 6.531 -1.912 c
+6.548 -2.087 6.548 -2.261 6.531 -2.425 c
+6.471 -2.778 l
+4.987 -2.778 l
+4.976 -2.859 4.972 -2.936 4.972 -3.013 c
+4.991 -3.278 5.123 -3.41 5.369 -3.41 c
+5.594 -3.421 5.814 -3.337 6.03 -3.161 c
+6.295 -3.631 l
+6.185 -3.77 6.038 -3.877 5.854 -3.954 c
+5.677 -4.031 5.486 -4.072 5.281 -4.072 c
+5.546 -1.617 m
+5.328 -1.61 5.178 -1.75 5.089 -2.043 c
+5.031 -2.234 l
+5.795 -2.234 l
+5.814 -2.139 5.825 -2.055 5.825 -1.985 c
+5.833 -1.75 5.74 -1.628 5.546 -1.617 c
+12.495 -3.558 m
+12.347 -3.726 12.149 -3.851 11.907 -3.94 c
+11.672 -4.028 11.422 -4.072 11.157 -4.072 c
+10.911 -4.072 10.701 -4.017 10.525 -3.911 c
+10.348 -3.803 10.209 -3.653 10.113 -3.454 c
+10.014 -3.26 9.966 -3.036 9.966 -2.778 c
+9.955 -2.643 9.959 -2.5 9.981 -2.352 c
+10.084 -1.691 l
+10.161 -1.132 10.338 -0.702 10.613 -0.397 c
+10.896 -0.085 11.263 0.066 11.715 0.058 c
+12.098 0.047 12.384 -0.071 12.582 -0.294 c
+12.777 -0.522 12.877 -0.849 12.877 -1.279 c
+12.112 -1.279 l
+12.098 -1.058 l
+12.075 -0.776 11.936 -0.628 11.672 -0.618 c
+11.296 -0.599 11.047 -0.849 10.922 -1.367 c
+10.892 -1.573 l
+10.774 -2.352 l
+10.753 -2.529 10.745 -2.675 10.745 -2.793 c
+10.753 -2.999 10.797 -3.153 10.878 -3.248 c
+10.966 -3.337 11.087 -3.389 11.245 -3.396 c
+11.381 -3.396 11.539 -3.348 11.715 -3.248 c
+11.863 -2.514 l
+11.304 -2.514 l
+11.422 -1.897 l
+12.759 -1.897 l
+h
+14.56 -4.013 m
+13.81 -4.013 l
+14.325 -1.029 l
+15.089 -1.029 l
+h
+14.427 -0.279 m
+14.427 -0.154 14.464 -0.052 14.545 0.029 c
+14.622 0.118 14.722 0.162 14.839 0.162 c
+14.946 0.162 15.038 0.121 15.119 0.044 c
+15.206 -0.037 15.25 -0.133 15.25 -0.25 c
+15.25 -0.379 15.206 -0.482 15.119 -0.559 c
+15.038 -0.64 14.942 -0.676 14.824 -0.676 c
+14.707 -0.676 14.608 -0.643 14.531 -0.574 c
+14.46 -0.497 14.427 -0.397 14.427 -0.279 c
+17.581 -0.294 m
+17.463 -1.029 l
+17.845 -1.029 l
+17.742 -1.617 l
+17.359 -1.617 l
+17.095 -3.117 l
+17.095 -3.205 l
+17.084 -3.333 17.132 -3.396 17.242 -3.396 c
+17.272 -3.396 17.334 -3.392 17.434 -3.381 c
+17.359 -3.998 l
+17.249 -4.046 17.118 -4.072 16.963 -4.072 c
+16.746 -4.061 16.581 -3.98 16.463 -3.822 c
+16.353 -3.668 16.309 -3.454 16.331 -3.19 c
+16.596 -1.617 l
+16.258 -1.617 l
+16.36 -1.029 l
+16.698 -1.029 l
+16.816 -0.294 l
+h
+19.624 -2.778 m
+18.992 -2.778 l
+19.374 0 l
+20.182 0 l
+h
+18.741 -3.645 m
+18.741 -3.521 18.778 -3.418 18.859 -3.337 c
+18.936 -3.248 19.036 -3.205 19.153 -3.205 c
+19.271 -3.205 19.366 -3.246 19.447 -3.323 c
+19.524 -3.404 19.565 -3.499 19.565 -3.616 c
+19.565 -3.745 19.524 -3.851 19.447 -3.94 c
+19.366 -4.017 19.271 -4.057 19.153 -4.057 c
+19.025 -4.057 18.922 -4.017 18.845 -3.94 c
+18.774 -3.863 18.741 -3.763 18.741 -3.645 c
+f
+Q
+0.793 0.801 0.129 0.016 K
+0.8 w
+q 1 0 0 1 534.418 129.4519 cm
+0 0 m
+-0.008 35.572 l
+6.464 35.572 l
+6.471 0 l
+3.23 -3.564 l
+0 0 l
+h
+S
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 537.5488 161.6247 cm
+0 0 m
+1.675 -0.264 l
+1.675 -1.014 l
+-1.309 -0.397 l
+-1.309 0.249 l
+0.411 0.676 l
+-1.309 1.087 l
+-1.309 1.749 l
+1.675 2.366 l
+1.675 1.617 l
+0 1.352 l
+1.675 0.955 l
+1.675 0.397 l
+h
+0.279 -1.07 m
+0.727 -1.07 1.08 -1.176 1.338 -1.393 c
+1.602 -1.606 1.735 -1.9 1.735 -2.275 c
+1.735 -2.657 1.602 -2.951 1.338 -3.157 c
+1.08 -3.37 0.727 -3.48 0.279 -3.48 c
+0.088 -3.48 l
+-0.375 -3.48 -0.732 -3.37 -0.985 -3.157 c
+-1.243 -2.951 -1.367 -2.657 -1.367 -2.275 c
+-1.367 -1.893 -1.243 -1.592 -0.985 -1.378 c
+-0.721 -1.172 -0.364 -1.07 0.088 -1.07 c
+h
+0.088 -1.849 m
+-0.453 -1.849 -0.721 -1.988 -0.721 -2.275 c
+-0.721 -2.539 -0.497 -2.679 -0.044 -2.701 c
+0.279 -2.716 l
+0.551 -2.716 0.754 -2.675 0.881 -2.598 c
+1.018 -2.517 1.088 -2.411 1.088 -2.275 c
+1.088 -2.146 1.018 -2.043 0.881 -1.966 c
+0.754 -1.885 0.551 -1.849 0.279 -1.849 c
+h
+0.926 -5.251 m
+0.941 -5.002 l
+0.941 -4.785 0.845 -4.638 0.661 -4.561 c
+-1.309 -4.561 l
+-1.309 -3.782 l
+1.675 -3.782 l
+1.675 -4.516 l
+1.352 -4.546 l
+1.606 -4.663 1.735 -4.829 1.735 -5.045 c
+1.735 -5.134 1.72 -5.207 1.69 -5.266 c
+h
+-0.162 -6.531 m
+-0.397 -6.31 l
+-1.309 -6.31 l
+-1.309 -5.531 l
+2.925 -5.531 l
+2.925 -6.31 l
+0.632 -6.31 l
+0.779 -6.413 l
+1.675 -6.957 l
+1.675 -7.882 l
+0.44 -7.015 l
+-1.309 -7.971 l
+-1.309 -7.074 l
+h
+-1.309 -8.096 2.984 -0.779 re
+-1.309 -8.875 m
+2.44 -8.052 m
+2.564 -8.052 2.668 -8.088 2.749 -8.169 c
+2.826 -8.247 2.866 -8.349 2.866 -8.478 c
+2.866 -8.603 2.826 -8.706 2.749 -8.787 c
+2.668 -8.864 2.564 -8.904 2.44 -8.904 c
+2.322 -8.904 2.223 -8.864 2.146 -8.787 c
+2.065 -8.717 2.028 -8.613 2.028 -8.478 c
+2.028 -8.349 2.065 -8.247 2.146 -8.169 c
+2.223 -8.088 2.322 -8.052 2.44 -8.052 c
+1.675 -10.099 m
+1.381 -10.113 l
+1.616 -10.29 1.735 -10.517 1.735 -10.804 c
+1.735 -11.322 1.371 -11.587 0.646 -11.597 c
+-1.309 -11.597 l
+-1.309 -10.819 l
+0.588 -10.819 l
+0.764 -10.819 0.885 -10.793 0.955 -10.745 c
+1.032 -10.693 1.072 -10.606 1.072 -10.481 c
+1.072 -10.334 0.999 -10.219 0.852 -10.142 c
+-1.309 -10.142 l
+-1.309 -9.363 l
+1.675 -9.363 l
+h
+0.279 -11.884 m
+0.768 -11.884 1.132 -11.973 1.367 -12.149 c
+1.61 -12.326 1.735 -12.568 1.735 -12.884 c
+1.735 -13.156 1.624 -13.369 1.411 -13.516 c
+1.675 -13.546 l
+1.675 -14.251 l
+-1.309 -14.251 l
+-1.69 -14.251 -1.977 -14.14 -2.176 -13.928 c
+-2.382 -13.71 -2.484 -13.406 -2.484 -13.016 c
+-2.484 -12.847 -2.448 -12.671 -2.382 -12.487 c
+-2.323 -12.311 -2.238 -12.179 -2.132 -12.09 c
+-1.602 -12.355 l
+-1.669 -12.432 -1.727 -12.532 -1.779 -12.649 c
+-1.827 -12.767 -1.852 -12.873 -1.852 -12.972 c
+-1.852 -13.149 -1.808 -13.274 -1.72 -13.355 c
+-1.639 -13.432 -1.507 -13.472 -1.323 -13.472 c
+-1.073 -13.472 l
+-1.272 -13.325 -1.367 -13.126 -1.367 -12.884 c
+-1.367 -12.568 -1.246 -12.326 -1 -12.149 c
+-0.746 -11.973 -0.393 -11.884 0.058 -11.884 c
+h
+0.073 -12.663 m
+-0.202 -12.663 -0.405 -12.696 -0.53 -12.767 c
+-0.647 -12.844 -0.706 -12.962 -0.706 -13.12 c
+-0.706 -13.274 -0.655 -13.391 -0.544 -13.472 c
+0.881 -13.472 l
+1.007 -13.391 1.072 -13.274 1.072 -13.12 c
+1.072 -12.962 1.007 -12.844 0.881 -12.767 c
+0.754 -12.696 0.551 -12.663 0.279 -12.663 c
+h
+0.088 -14.685 0.646 -1.352 re
+0.088 -16.037 m
+-0.206 -17.525 m
+1.675 -17.907 l
+1.675 -18.716 l
+-1.309 -17.893 l
+-1.309 -17.157 l
+1.675 -16.334 l
+1.675 -17.143 l
+h
+-1.367 -20.083 m
+-1.367 -19.678 -1.249 -19.366 -1.014 -19.142 c
+-0.772 -18.925 -0.426 -18.819 0.014 -18.819 c
+0.264 -18.819 l
+0.735 -18.819 1.095 -18.921 1.352 -19.127 c
+1.606 -19.333 1.735 -19.627 1.735 -20.01 c
+1.735 -20.38 1.61 -20.66 1.367 -20.847 c
+1.132 -21.042 0.779 -21.141 0.309 -21.141 c
+-0.073 -21.141 l
+-0.073 -19.597 l
+-0.302 -19.605 -0.467 -19.649 -0.574 -19.73 c
+-0.673 -19.819 -0.721 -19.954 -0.721 -20.141 c
+-0.721 -20.395 -0.628 -20.612 -0.441 -20.788 c
+-0.927 -21.097 l
+-1.055 -20.998 -1.162 -20.854 -1.249 -20.671 c
+-1.326 -20.494 -1.367 -20.296 -1.367 -20.083 c
+0.47 -19.597 m
+0.47 -20.376 l
+0.544 -20.376 l
+0.72 -20.376 0.852 -20.347 0.941 -20.289 c
+1.036 -20.23 1.088 -20.131 1.088 -19.994 c
+1.088 -19.866 1.036 -19.767 0.941 -19.701 c
+0.841 -19.642 0.683 -19.605 0.47 -19.597 c
+0.926 -22.868 m
+0.941 -22.618 l
+0.941 -22.402 0.845 -22.254 0.661 -22.177 c
+-1.309 -22.177 l
+-1.309 -21.398 l
+1.675 -21.398 l
+1.675 -22.134 l
+1.352 -22.163 l
+1.606 -22.28 1.735 -22.446 1.735 -22.662 c
+1.735 -22.751 1.72 -22.824 1.69 -22.883 c
+h
+-0.515 -24.471 m
+-0.449 -24.471 -0.382 -24.434 -0.324 -24.368 c
+-0.265 -24.309 -0.191 -24.169 -0.103 -23.956 c
+0.033 -23.632 0.168 -23.412 0.309 -23.295 c
+0.444 -23.177 0.617 -23.118 0.823 -23.118 c
+1.088 -23.118 1.301 -23.21 1.469 -23.397 c
+1.646 -23.582 1.735 -23.831 1.735 -24.147 c
+1.735 -24.471 1.646 -24.727 1.469 -24.926 c
+1.301 -25.121 1.076 -25.22 0.794 -25.22 c
+0.794 -24.441 l
+1.036 -24.441 1.161 -24.338 1.161 -24.133 c
+1.161 -24.052 1.132 -23.985 1.072 -23.927 c
+1.022 -23.875 0.955 -23.853 0.867 -23.853 c
+0.797 -23.853 0.739 -23.883 0.69 -23.941 c
+0.639 -24 0.565 -24.136 0.47 -24.353 c
+0.353 -24.677 0.22 -24.9 0.073 -25.029 c
+-0.067 -25.165 -0.25 -25.234 -0.485 -25.234 c
+-0.75 -25.234 -0.967 -25.132 -1.132 -24.926 c
+-1.29 -24.727 -1.367 -24.471 -1.367 -24.147 c
+-1.367 -23.919 -1.323 -23.725 -1.235 -23.559 c
+-1.147 -23.39 -1.029 -23.258 -0.882 -23.162 c
+-0.728 -23.074 -0.559 -23.03 -0.382 -23.03 c
+-0.382 -23.765 l
+-0.522 -23.772 -0.625 -23.809 -0.691 -23.868 c
+-0.761 -23.927 -0.794 -24.022 -0.794 -24.162 c
+-0.794 -24.368 -0.702 -24.471 -0.515 -24.471 c
+-1.309 -25.587 2.984 -0.78 re
+-1.309 -26.367 m
+2.44 -25.544 m
+2.564 -25.544 2.668 -25.581 2.749 -25.661 c
+2.826 -25.739 2.866 -25.841 2.866 -25.97 c
+2.866 -26.094 2.826 -26.198 2.749 -26.279 c
+2.668 -26.356 2.564 -26.396 2.44 -26.396 c
+2.322 -26.396 2.223 -26.356 2.146 -26.279 c
+2.065 -26.209 2.028 -26.106 2.028 -25.97 c
+2.028 -25.841 2.065 -25.739 2.146 -25.661 c
+2.223 -25.581 2.322 -25.544 2.44 -25.544 c
+0.279 -26.782 m
+0.727 -26.782 1.08 -26.888 1.338 -27.106 c
+1.602 -27.318 1.735 -27.613 1.735 -27.987 c
+1.735 -28.369 1.602 -28.663 1.338 -28.869 c
+1.08 -29.082 0.727 -29.192 0.279 -29.192 c
+0.088 -29.192 l
+-0.375 -29.192 -0.732 -29.082 -0.985 -28.869 c
+-1.243 -28.663 -1.367 -28.369 -1.367 -27.987 c
+-1.367 -27.605 -1.243 -27.304 -0.985 -27.09 c
+-0.721 -26.884 -0.364 -26.782 0.088 -26.782 c
+h
+0.088 -27.561 m
+-0.453 -27.561 -0.721 -27.701 -0.721 -27.987 c
+-0.721 -28.251 -0.497 -28.392 -0.044 -28.413 c
+0.279 -28.428 l
+0.551 -28.428 0.754 -28.388 0.881 -28.311 c
+1.018 -28.23 1.088 -28.123 1.088 -27.987 c
+1.088 -27.858 1.018 -27.756 0.881 -27.678 c
+0.754 -27.598 0.551 -27.561 0.279 -27.561 c
+h
+1.675 -30.214 m
+1.381 -30.229 l
+1.616 -30.406 1.735 -30.633 1.735 -30.919 c
+1.735 -31.438 1.371 -31.702 0.646 -31.713 c
+-1.309 -31.713 l
+-1.309 -30.934 l
+0.588 -30.934 l
+0.764 -30.934 0.885 -30.909 0.955 -30.861 c
+1.032 -30.809 1.072 -30.721 1.072 -30.597 c
+1.072 -30.449 0.999 -30.335 0.852 -30.258 c
+-1.309 -30.258 l
+-1.309 -29.479 l
+1.675 -29.479 l
+h
+f
+Q
+q 1 0 0 1 442.9198 145.1834 cm
+0 0 m
+-1.058 0 l
+-1.058 -3.572 l
+-1.558 -3.572 l
+-1.558 0 l
+-2.616 0 l
+-2.616 0.441 l
+0 0.441 l
+h
+0.819 -0.897 m
+1.003 -0.654 1.238 -0.529 1.525 -0.529 c
+2.055 -0.529 2.323 -0.881 2.333 -1.587 c
+2.333 -3.572 l
+1.849 -3.572 l
+1.849 -1.617 l
+1.849 -1.382 1.808 -1.216 1.731 -1.117 c
+1.65 -1.022 1.532 -0.97 1.378 -0.97 c
+1.261 -0.97 1.151 -1.01 1.055 -1.087 c
+0.956 -1.168 0.879 -1.275 0.819 -1.411 c
+0.819 -3.572 l
+0.334 -3.572 l
+0.334 0.661 l
+0.819 0.661 l
+h
+3.406 -3.572 -0.5 2.984 re
+3.436 0.206 m
+3.436 0.118 3.41 0.044 3.362 -0.014 c
+3.322 -0.066 3.252 -0.088 3.156 -0.088 c
+3.069 -0.088 2.998 -0.066 2.951 -0.014 c
+2.911 0.044 2.892 0.11 2.892 0.191 c
+2.892 0.279 2.911 0.353 2.951 0.412 c
+2.998 0.47 3.069 0.5 3.156 0.5 c
+3.252 0.5 3.322 0.47 3.362 0.412 c
+3.41 0.353 3.436 0.283 3.436 0.206 c
+5.523 -2.807 m
+5.523 -2.701 5.483 -2.612 5.405 -2.543 c
+5.325 -2.466 5.174 -2.377 4.95 -2.278 c
+4.685 -2.171 4.498 -2.08 4.391 -1.999 c
+4.281 -1.922 4.204 -1.834 4.156 -1.735 c
+4.104 -1.639 4.083 -1.521 4.083 -1.382 c
+4.083 -1.139 4.171 -0.937 4.347 -0.779 c
+4.523 -0.613 4.748 -0.529 5.024 -0.529 c
+5.317 -0.529 5.552 -0.617 5.729 -0.794 c
+5.905 -0.962 5.993 -1.176 5.993 -1.44 c
+5.508 -1.44 l
+5.508 -1.304 5.457 -1.19 5.361 -1.103 c
+5.273 -1.007 5.159 -0.955 5.024 -0.955 c
+4.877 -0.955 4.762 -0.995 4.685 -1.072 c
+4.604 -1.143 4.567 -1.242 4.567 -1.367 c
+4.567 -1.466 4.597 -1.543 4.656 -1.602 c
+4.715 -1.66 4.854 -1.741 5.082 -1.837 c
+5.442 -1.984 5.689 -2.127 5.818 -2.263 c
+5.953 -2.392 6.023 -2.564 6.023 -2.778 c
+6.023 -3.035 5.928 -3.241 5.743 -3.395 c
+5.567 -3.553 5.332 -3.63 5.038 -3.63 c
+4.722 -3.63 4.469 -3.542 4.274 -3.366 c
+4.087 -3.183 3.994 -2.95 3.994 -2.675 c
+4.48 -2.675 l
+4.486 -2.844 4.538 -2.977 4.627 -3.072 c
+4.722 -3.16 4.862 -3.204 5.038 -3.204 c
+5.192 -3.204 5.31 -3.171 5.39 -3.102 c
+5.479 -3.035 5.523 -2.936 5.523 -2.807 c
+8.213 -3.572 -0.5 2.984 re
+8.243 0.206 m
+8.243 0.118 8.216 0.044 8.169 -0.014 c
+8.129 -0.066 8.058 -0.088 7.963 -0.088 c
+7.875 -0.088 7.805 -0.066 7.757 -0.014 c
+7.717 0.044 7.699 0.11 7.699 0.191 c
+7.699 0.279 7.717 0.353 7.757 0.412 c
+7.805 0.47 7.875 0.5 7.963 0.5 c
+8.058 0.5 8.129 0.47 8.169 0.412 c
+8.216 0.353 8.243 0.283 8.243 0.206 c
+10.326 -2.807 m
+10.326 -2.701 10.286 -2.612 10.208 -2.543 c
+10.128 -2.466 9.977 -2.377 9.753 -2.278 c
+9.488 -2.171 9.301 -2.08 9.194 -1.999 c
+9.084 -1.922 9.007 -1.834 8.959 -1.735 c
+8.908 -1.639 8.885 -1.521 8.885 -1.382 c
+8.885 -1.139 8.974 -0.937 9.15 -0.779 c
+9.326 -0.613 9.55 -0.529 9.826 -0.529 c
+10.12 -0.529 10.356 -0.617 10.532 -0.794 c
+10.708 -0.962 10.796 -1.176 10.796 -1.44 c
+10.311 -1.44 l
+10.311 -1.304 10.26 -1.19 10.165 -1.103 c
+10.076 -1.007 9.962 -0.955 9.826 -0.955 c
+9.679 -0.955 9.565 -0.995 9.488 -1.072 c
+9.407 -1.143 9.371 -1.242 9.371 -1.367 c
+9.371 -1.466 9.4 -1.543 9.459 -1.602 c
+9.517 -1.66 9.658 -1.741 9.885 -1.837 c
+10.246 -1.984 10.491 -2.127 10.62 -2.263 c
+10.756 -2.392 10.826 -2.564 10.826 -2.778 c
+10.826 -3.035 10.73 -3.241 10.547 -3.395 c
+10.37 -3.553 10.135 -3.63 9.841 -3.63 c
+9.525 -3.63 9.271 -3.542 9.076 -3.366 c
+8.889 -3.183 8.797 -2.95 8.797 -2.675 c
+9.282 -2.675 l
+9.29 -2.844 9.342 -2.977 9.429 -3.072 c
+9.525 -3.16 9.664 -3.204 9.841 -3.204 c
+9.995 -3.204 10.113 -3.171 10.194 -3.102 c
+10.282 -3.035 10.326 -2.936 10.326 -2.807 c
+14.015 -3.572 m
+13.986 -3.505 13.964 -3.395 13.957 -3.248 c
+13.78 -3.505 13.56 -3.63 13.295 -3.63 c
+13.02 -3.63 12.803 -3.557 12.648 -3.41 c
+12.501 -3.256 12.428 -3.039 12.428 -2.763 c
+12.428 -2.462 12.531 -2.219 12.737 -2.043 c
+12.943 -1.859 13.225 -1.764 13.589 -1.764 c
+13.942 -1.764 l
+13.942 -1.44 l
+13.942 -1.264 13.901 -1.143 13.824 -1.072 c
+13.743 -0.995 13.626 -0.955 13.471 -0.955 c
+13.325 -0.955 13.2 -0.999 13.104 -1.087 c
+13.016 -1.176 12.972 -1.286 12.972 -1.411 c
+12.486 -1.411 l
+12.486 -1.264 12.531 -1.124 12.619 -0.985 c
+12.707 -0.837 12.825 -0.727 12.972 -0.646 c
+13.126 -0.569 13.299 -0.529 13.486 -0.529 c
+13.799 -0.529 14.034 -0.61 14.192 -0.764 c
+14.346 -0.912 14.427 -1.132 14.427 -1.425 c
+14.427 -2.925 l
+14.435 -3.16 14.471 -3.362 14.53 -3.528 c
+14.53 -3.572 l
+h
+13.369 -3.189 m
+13.486 -3.189 13.596 -3.156 13.707 -3.087 c
+13.814 -3.021 13.891 -2.936 13.942 -2.836 c
+13.942 -2.131 l
+13.677 -2.131 l
+13.442 -2.131 13.255 -2.183 13.119 -2.278 c
+12.99 -2.377 12.928 -2.52 12.928 -2.705 c
+12.928 -2.873 12.957 -2.994 13.016 -3.072 c
+13.082 -3.152 13.2 -3.189 13.369 -3.189 c
+16.845 0.133 m
+16.845 -0.588 l
+17.301 -0.588 l
+17.301 -0.985 l
+16.845 -0.985 l
+16.845 -2.836 l
+16.845 -2.954 16.864 -3.042 16.904 -3.102 c
+16.941 -3.16 17.01 -3.189 17.109 -3.189 c
+17.168 -3.189 17.231 -3.183 17.301 -3.16 c
+17.301 -3.572 l
+17.184 -3.609 17.069 -3.63 16.962 -3.63 c
+16.764 -3.63 16.613 -3.564 16.507 -3.424 c
+16.407 -3.289 16.36 -3.094 16.36 -2.836 c
+16.36 -0.985 l
+15.904 -0.985 l
+15.904 -0.588 l
+16.36 -0.588 l
+16.36 0.133 l
+h
+19.296 -3.572 m
+19.266 -3.505 19.245 -3.395 19.237 -3.248 c
+19.061 -3.505 18.84 -3.63 18.576 -3.63 c
+18.3 -3.63 18.084 -3.557 17.929 -3.41 c
+17.782 -3.256 17.708 -3.039 17.708 -2.763 c
+17.708 -2.462 17.812 -2.219 18.017 -2.043 c
+18.223 -1.859 18.506 -1.764 18.869 -1.764 c
+19.223 -1.764 l
+19.223 -1.44 l
+19.223 -1.264 19.183 -1.143 19.105 -1.072 c
+19.025 -0.995 18.907 -0.955 18.753 -0.955 c
+18.605 -0.955 18.48 -0.999 18.385 -1.087 c
+18.296 -1.176 18.252 -1.286 18.252 -1.411 c
+17.768 -1.411 l
+17.768 -1.264 17.812 -1.124 17.899 -0.985 c
+17.988 -0.837 18.105 -0.727 18.252 -0.646 c
+18.407 -0.569 18.58 -0.529 18.767 -0.529 c
+19.079 -0.529 19.314 -0.61 19.472 -0.764 c
+19.627 -0.912 19.708 -1.132 19.708 -1.425 c
+19.708 -2.925 l
+19.715 -3.16 19.752 -3.362 19.81 -3.528 c
+19.81 -3.572 l
+h
+18.649 -3.189 m
+18.767 -3.189 18.877 -3.156 18.988 -3.087 c
+19.094 -3.021 19.171 -2.936 19.223 -2.836 c
+19.223 -2.131 l
+18.958 -2.131 l
+18.723 -2.131 18.535 -2.183 18.399 -2.278 c
+18.271 -2.377 18.208 -2.52 18.208 -2.705 c
+18.208 -2.873 18.238 -2.994 18.296 -3.072 c
+18.362 -3.152 18.48 -3.189 18.649 -3.189 c
+20.185 -1.94 m
+20.185 -1.481 20.266 -1.132 20.436 -0.897 c
+20.612 -0.654 20.862 -0.529 21.185 -0.529 c
+21.486 -0.529 21.718 -0.661 21.876 -0.926 c
+21.905 -0.588 l
+22.346 -0.588 l
+22.346 -3.601 l
+22.346 -3.972 22.247 -4.255 22.052 -4.453 c
+21.865 -4.648 21.604 -4.748 21.273 -4.748 c
+21.126 -4.748 20.957 -4.707 20.773 -4.63 c
+20.586 -4.56 20.45 -4.472 20.361 -4.365 c
+20.553 -4.027 l
+20.758 -4.233 20.983 -4.336 21.229 -4.336 c
+21.629 -4.336 21.836 -4.108 21.847 -3.659 c
+21.847 -3.278 l
+21.689 -3.513 21.468 -3.63 21.185 -3.63 c
+20.869 -3.63 20.627 -3.513 20.45 -3.278 c
+20.28 -3.042 20.193 -2.712 20.185 -2.278 c
+h
+20.671 -2.219 m
+20.671 -2.554 20.718 -2.8 20.818 -2.954 c
+20.913 -3.112 21.074 -3.189 21.302 -3.189 c
+21.545 -3.189 21.729 -3.068 21.847 -2.822 c
+21.847 -1.338 l
+21.729 -1.095 21.552 -0.97 21.317 -0.97 c
+21.089 -0.97 20.928 -1.051 20.832 -1.205 c
+20.733 -1.363 20.677 -1.602 20.671 -1.926 c
+h
+22.908 -3.307 m
+22.908 -3.219 22.931 -3.146 22.982 -3.087 c
+23.041 -3.028 23.118 -2.998 23.217 -2.998 c
+23.324 -2.998 23.401 -3.028 23.453 -3.087 c
+23.511 -3.146 23.54 -3.219 23.54 -3.307 c
+23.54 -3.388 23.511 -3.454 23.453 -3.513 c
+23.401 -3.572 23.324 -3.601 23.217 -3.601 c
+23.118 -3.601 23.041 -3.572 22.982 -3.513 c
+22.931 -3.454 22.908 -3.388 22.908 -3.307 c
+26.061 -3.572 -0.5 4.013 re
+27.421 0.133 m
+27.421 -0.588 l
+27.877 -0.588 l
+27.877 -0.985 l
+27.421 -0.985 l
+27.421 -2.836 l
+27.421 -2.954 27.439 -3.042 27.48 -3.102 c
+27.517 -3.16 27.586 -3.189 27.686 -3.189 c
+27.744 -3.189 27.806 -3.183 27.877 -3.16 c
+27.877 -3.572 l
+27.759 -3.609 27.646 -3.63 27.538 -3.63 c
+27.34 -3.63 27.189 -3.564 27.083 -3.424 c
+26.984 -3.289 26.936 -3.094 26.936 -2.836 c
+26.936 -0.985 l
+26.48 -0.985 l
+26.48 -0.588 l
+26.936 -0.588 l
+26.936 0.133 l
+h
+30.078 -3.572 -0.5 4.233 re
+30.647 -1.94 m
+30.647 -1.51 30.75 -1.168 30.956 -0.912 c
+31.169 -0.658 31.449 -0.529 31.794 -0.529 c
+32.136 -0.529 32.411 -0.658 32.617 -0.912 c
+32.83 -1.157 32.944 -1.492 32.955 -1.911 c
+32.955 -2.219 l
+32.955 -2.653 32.845 -2.994 32.631 -3.248 c
+32.426 -3.505 32.147 -3.63 31.794 -3.63 c
+31.449 -3.63 31.177 -3.509 30.971 -3.263 c
+30.765 -3.021 30.655 -2.686 30.647 -2.263 c
+h
+31.133 -2.219 m
+31.133 -2.535 31.191 -2.778 31.309 -2.954 c
+31.434 -3.123 31.596 -3.204 31.794 -3.204 c
+32.224 -3.204 32.448 -2.896 32.471 -2.278 c
+32.471 -1.94 l
+32.471 -1.639 32.404 -1.396 32.279 -1.22 c
+32.161 -1.043 32 -0.955 31.794 -0.955 c
+31.596 -0.955 31.434 -1.043 31.309 -1.22 c
+31.191 -1.396 31.133 -1.639 31.133 -1.94 c
+h
+33.294 -1.94 m
+33.294 -1.51 33.396 -1.168 33.602 -0.912 c
+33.815 -0.658 34.094 -0.529 34.44 -0.529 c
+34.782 -0.529 35.058 -0.658 35.263 -0.912 c
+35.476 -1.157 35.59 -1.492 35.601 -1.911 c
+35.601 -2.219 l
+35.601 -2.653 35.49 -2.994 35.278 -3.248 c
+35.072 -3.505 34.792 -3.63 34.44 -3.63 c
+34.094 -3.63 33.822 -3.509 33.616 -3.263 c
+33.411 -3.021 33.3 -2.686 33.294 -2.263 c
+h
+33.778 -2.219 m
+33.778 -2.535 33.837 -2.778 33.955 -2.954 c
+34.079 -3.123 34.241 -3.204 34.44 -3.204 c
+34.869 -3.204 35.094 -2.896 35.116 -2.278 c
+35.116 -1.94 l
+35.116 -1.639 35.05 -1.396 34.925 -1.22 c
+34.807 -1.043 34.646 -0.955 34.44 -0.955 c
+34.241 -0.955 34.079 -1.043 33.955 -1.22 c
+33.837 -1.396 33.778 -1.639 33.778 -1.94 c
+h
+36.788 -2.205 m
+36.523 -2.499 l
+36.523 -3.572 l
+36.038 -3.572 l
+36.038 0.661 l
+36.523 0.661 l
+36.523 -1.866 l
+37.449 -0.588 l
+38.038 -0.588 l
+37.097 -1.837 l
+38.17 -3.572 l
+37.597 -3.572 l
+h
+39.856 -2.807 m
+39.856 -2.701 39.816 -2.612 39.739 -2.543 c
+39.658 -2.466 39.507 -2.377 39.283 -2.278 c
+39.018 -2.171 38.831 -2.08 38.725 -1.999 c
+38.615 -1.922 38.537 -1.834 38.489 -1.735 c
+38.438 -1.639 38.416 -1.521 38.416 -1.382 c
+38.416 -1.139 38.504 -0.937 38.68 -0.779 c
+38.857 -0.613 39.081 -0.529 39.357 -0.529 c
+39.65 -0.529 39.885 -0.617 40.062 -0.794 c
+40.238 -0.962 40.327 -1.176 40.327 -1.44 c
+39.841 -1.44 l
+39.841 -1.304 39.79 -1.19 39.694 -1.103 c
+39.606 -1.007 39.492 -0.955 39.357 -0.955 c
+39.21 -0.955 39.095 -0.995 39.018 -1.072 c
+38.938 -1.143 38.901 -1.242 38.901 -1.367 c
+38.901 -1.466 38.931 -1.543 38.989 -1.602 c
+39.048 -1.66 39.187 -1.741 39.415 -1.837 c
+39.775 -1.984 40.022 -2.127 40.151 -2.263 c
+40.286 -2.392 40.356 -2.564 40.356 -2.778 c
+40.356 -3.035 40.261 -3.241 40.076 -3.395 c
+39.901 -3.553 39.665 -3.63 39.371 -3.63 c
+39.056 -3.63 38.802 -3.542 38.607 -3.366 c
+38.42 -3.183 38.328 -2.95 38.328 -2.675 c
+38.813 -2.675 l
+38.821 -2.844 38.871 -2.977 38.96 -3.072 c
+39.056 -3.16 39.195 -3.204 39.371 -3.204 c
+39.526 -3.204 39.644 -3.171 39.724 -3.102 c
+39.812 -3.035 39.856 -2.936 39.856 -2.807 c
+42.546 -3.572 -0.5 4.233 re
+43.752 -3.572 -0.5 2.984 re
+43.781 0.206 m
+43.781 0.118 43.756 0.044 43.708 -0.014 c
+43.667 -0.066 43.598 -0.088 43.502 -0.088 c
+43.413 -0.088 43.344 -0.066 43.296 -0.014 c
+43.255 0.044 43.237 0.11 43.237 0.191 c
+43.237 0.279 43.255 0.353 43.296 0.412 c
+43.344 0.47 43.413 0.5 43.502 0.5 c
+43.598 0.5 43.667 0.47 43.708 0.412 c
+43.756 0.353 43.781 0.283 43.781 0.206 c
+45.173 -2.205 m
+44.909 -2.499 l
+44.909 -3.572 l
+44.425 -3.572 l
+44.425 0.661 l
+44.909 0.661 l
+44.909 -1.866 l
+45.836 -0.588 l
+46.424 -0.588 l
+45.483 -1.837 l
+46.555 -3.572 l
+45.982 -3.572 l
+h
+47.808 -3.63 m
+47.434 -3.63 47.151 -3.524 46.956 -3.307 c
+46.758 -3.083 46.662 -2.755 46.662 -2.323 c
+46.662 -1.955 l
+46.662 -1.514 46.754 -1.168 46.941 -0.912 c
+47.136 -0.658 47.411 -0.529 47.764 -0.529 c
+48.107 -0.529 48.36 -0.643 48.529 -0.867 c
+48.705 -1.095 48.797 -1.44 48.808 -1.911 c
+48.808 -2.219 l
+47.147 -2.219 l
+47.147 -2.293 l
+47.147 -2.616 47.206 -2.851 47.324 -2.998 c
+47.441 -3.138 47.61 -3.204 47.838 -3.204 c
+47.985 -3.204 48.11 -3.183 48.22 -3.131 c
+48.327 -3.072 48.429 -2.984 48.529 -2.866 c
+48.778 -3.175 l
+48.573 -3.48 48.25 -3.63 47.808 -3.63 c
+47.764 -0.955 m
+47.559 -0.955 47.405 -1.025 47.309 -1.161 c
+47.21 -1.301 47.155 -1.514 47.147 -1.808 c
+48.323 -1.808 l
+48.323 -1.735 l
+48.301 -1.462 48.25 -1.264 48.161 -1.147 c
+48.073 -1.022 47.941 -0.955 47.764 -0.955 c
+51.953 -3.572 m
+51.924 -3.505 51.902 -3.395 51.895 -3.248 c
+51.718 -3.505 51.498 -3.63 51.234 -3.63 c
+50.958 -3.63 50.741 -3.557 50.586 -3.41 c
+50.44 -3.256 50.366 -3.039 50.366 -2.763 c
+50.366 -2.462 50.469 -2.219 50.675 -2.043 c
+50.881 -1.859 51.164 -1.764 51.527 -1.764 c
+51.88 -1.764 l
+51.88 -1.44 l
+51.88 -1.264 51.839 -1.143 51.762 -1.072 c
+51.681 -0.995 51.564 -0.955 51.41 -0.955 c
+51.263 -0.955 51.138 -0.999 51.043 -1.087 c
+50.954 -1.176 50.91 -1.286 50.91 -1.411 c
+50.425 -1.411 l
+50.425 -1.264 50.469 -1.124 50.557 -0.985 c
+50.646 -0.837 50.763 -0.727 50.91 -0.646 c
+51.064 -0.569 51.237 -0.529 51.425 -0.529 c
+51.737 -0.529 51.972 -0.61 52.13 -0.764 c
+52.284 -0.912 52.365 -1.132 52.365 -1.425 c
+52.365 -2.925 l
+52.373 -3.16 52.41 -3.362 52.468 -3.528 c
+52.468 -3.572 l
+h
+51.307 -3.189 m
+51.425 -3.189 51.535 -3.156 51.645 -3.087 c
+51.752 -3.021 51.829 -2.936 51.88 -2.836 c
+51.88 -2.131 l
+51.616 -2.131 l
+51.38 -2.131 51.193 -2.183 51.057 -2.278 c
+50.929 -2.377 50.866 -2.52 50.866 -2.705 c
+50.866 -2.873 50.895 -2.994 50.954 -3.072 c
+51.02 -3.152 51.138 -3.189 51.307 -3.189 c
+54.045 -1.94 m
+54.045 -1.481 54.125 -1.132 54.295 -0.897 c
+54.471 -0.654 54.721 -0.529 55.045 -0.529 c
+55.327 -0.529 55.548 -0.646 55.706 -0.881 c
+55.706 0.661 l
+56.19 0.661 l
+56.19 -3.572 l
+55.75 -3.572 l
+55.72 -3.248 l
+55.562 -3.505 55.338 -3.63 55.045 -3.63 c
+54.728 -3.63 54.486 -3.513 54.309 -3.278 c
+54.133 -3.035 54.045 -2.69 54.045 -2.248 c
+h
+54.53 -2.219 m
+54.53 -2.554 54.578 -2.8 54.677 -2.954 c
+54.773 -3.112 54.934 -3.189 55.162 -3.189 c
+55.404 -3.189 55.588 -3.072 55.706 -2.836 c
+55.706 -1.323 l
+55.577 -1.087 55.397 -0.97 55.162 -0.97 c
+54.934 -0.97 54.773 -1.051 54.677 -1.205 c
+54.578 -1.363 54.53 -1.602 54.53 -1.926 c
+h
+57.823 -3.63 m
+57.447 -3.63 57.165 -3.524 56.969 -3.307 c
+56.772 -3.083 56.676 -2.755 56.676 -2.323 c
+56.676 -1.955 l
+56.676 -1.514 56.768 -1.168 56.955 -0.912 c
+57.15 -0.658 57.426 -0.529 57.778 -0.529 c
+58.12 -0.529 58.374 -0.643 58.542 -0.867 c
+58.719 -1.095 58.811 -1.44 58.822 -1.911 c
+58.822 -2.219 l
+57.161 -2.219 l
+57.161 -2.293 l
+57.161 -2.616 57.22 -2.851 57.337 -2.998 c
+57.455 -3.138 57.624 -3.204 57.852 -3.204 c
+57.998 -3.204 58.124 -3.183 58.234 -3.131 c
+58.341 -3.072 58.443 -2.984 58.542 -2.866 c
+58.792 -3.175 l
+58.586 -3.48 58.264 -3.63 57.823 -3.63 c
+57.778 -0.955 m
+57.572 -0.955 57.418 -1.025 57.323 -1.161 c
+57.223 -1.301 57.168 -1.514 57.161 -1.808 c
+58.337 -1.808 l
+58.337 -1.735 l
+58.315 -1.462 58.264 -1.264 58.175 -1.147 c
+58.087 -1.022 57.954 -0.955 57.778 -0.955 c
+60.038 -2.836 m
+60.612 -0.588 l
+61.111 -0.588 l
+60.215 -3.572 l
+59.862 -3.572 l
+58.966 -0.588 l
+59.45 -0.588 l
+h
+62.482 -3.63 m
+62.107 -3.63 61.824 -3.524 61.63 -3.307 c
+61.431 -3.083 61.335 -2.755 61.335 -2.323 c
+61.335 -1.955 l
+61.335 -1.514 61.427 -1.168 61.615 -0.912 c
+61.809 -0.658 62.085 -0.529 62.438 -0.529 c
+62.779 -0.529 63.033 -0.643 63.203 -0.867 c
+63.379 -1.095 63.471 -1.44 63.481 -1.911 c
+63.481 -2.219 l
+61.821 -2.219 l
+61.821 -2.293 l
+61.821 -2.616 61.879 -2.851 61.997 -2.998 c
+62.114 -3.138 62.284 -3.204 62.511 -3.204 c
+62.658 -3.204 62.783 -3.183 62.893 -3.131 c
+63 -3.072 63.103 -2.984 63.203 -2.866 c
+63.452 -3.175 l
+63.246 -3.48 62.923 -3.63 62.482 -3.63 c
+62.438 -0.955 m
+62.232 -0.955 62.078 -1.025 61.982 -1.161 c
+61.883 -1.301 61.828 -1.514 61.821 -1.808 c
+62.997 -1.808 l
+62.997 -1.735 l
+62.974 -1.462 62.923 -1.264 62.835 -1.147 c
+62.746 -1.022 62.614 -0.955 62.438 -0.955 c
+64.422 -3.572 -0.5 4.233 re
+64.996 -1.94 m
+64.996 -1.51 65.098 -1.168 65.304 -0.912 c
+65.517 -0.658 65.796 -0.529 66.142 -0.529 c
+66.484 -0.529 66.76 -0.658 66.966 -0.912 c
+67.178 -1.157 67.292 -1.492 67.303 -1.911 c
+67.303 -2.219 l
+67.303 -2.653 67.193 -2.994 66.98 -3.248 c
+66.774 -3.505 66.495 -3.63 66.142 -3.63 c
+65.796 -3.63 65.524 -3.509 65.319 -3.263 c
+65.113 -3.021 65.003 -2.686 64.996 -2.263 c
+h
+65.48 -2.219 m
+65.48 -2.535 65.539 -2.778 65.657 -2.954 c
+65.782 -3.123 65.944 -3.204 66.142 -3.204 c
+66.572 -3.204 66.796 -2.896 66.818 -2.278 c
+66.818 -1.94 l
+66.818 -1.639 66.752 -1.396 66.627 -1.22 c
+66.509 -1.043 66.348 -0.955 66.142 -0.955 c
+65.944 -0.955 65.782 -1.043 65.657 -1.22 c
+65.539 -1.396 65.48 -1.639 65.48 -1.94 c
+h
+69.887 -2.219 m
+69.887 -2.69 69.802 -3.042 69.636 -3.278 c
+69.468 -3.513 69.229 -3.63 68.917 -3.63 c
+68.612 -3.63 68.379 -3.52 68.225 -3.293 c
+68.225 -4.718 l
+67.741 -4.718 l
+67.741 -0.588 l
+68.181 -0.588 l
+68.211 -0.926 l
+68.365 -0.661 68.597 -0.529 68.902 -0.529 c
+69.233 -0.529 69.478 -0.646 69.636 -0.881 c
+69.802 -1.109 69.887 -1.448 69.887 -1.896 c
+h
+69.401 -1.94 m
+69.401 -1.61 69.347 -1.363 69.239 -1.205 c
+69.141 -1.051 68.979 -0.97 68.755 -0.97 c
+68.52 -0.97 68.343 -1.087 68.225 -1.323 c
+68.225 -2.866 l
+68.343 -3.094 68.523 -3.204 68.769 -3.204 c
+68.983 -3.204 69.141 -3.127 69.239 -2.969 c
+69.347 -2.815 69.401 -2.572 69.401 -2.248 c
+h
+71.404 -3.63 m
+71.03 -3.63 70.746 -3.524 70.552 -3.307 c
+70.353 -3.083 70.258 -2.755 70.258 -2.323 c
+70.258 -1.955 l
+70.258 -1.514 70.349 -1.168 70.537 -0.912 c
+70.731 -0.658 71.007 -0.529 71.36 -0.529 c
+71.702 -0.529 71.955 -0.643 72.125 -0.867 c
+72.301 -1.095 72.393 -1.44 72.404 -1.911 c
+72.404 -2.219 l
+70.743 -2.219 l
+70.743 -2.293 l
+70.743 -2.616 70.802 -2.851 70.919 -2.998 c
+71.037 -3.138 71.206 -3.204 71.434 -3.204 c
+71.581 -3.204 71.706 -3.183 71.816 -3.131 c
+71.922 -3.072 72.025 -2.984 72.125 -2.866 c
+72.375 -3.175 l
+72.169 -3.48 71.845 -3.63 71.404 -3.63 c
+71.36 -0.955 m
+71.155 -0.955 71 -1.025 70.904 -1.161 c
+70.806 -1.301 70.75 -1.514 70.743 -1.808 c
+71.919 -1.808 l
+71.919 -1.735 l
+71.897 -1.462 71.845 -1.264 71.757 -1.147 c
+71.669 -1.022 71.537 -0.955 71.36 -0.955 c
+74.05 -1.043 m
+73.981 -1.036 73.906 -1.029 73.829 -1.029 c
+73.572 -1.029 73.396 -1.168 73.301 -1.44 c
+73.301 -3.572 l
+72.815 -3.572 l
+72.815 -0.588 l
+73.285 -0.588 l
+73.301 -0.897 l
+73.426 -0.654 73.609 -0.529 73.844 -0.529 c
+73.921 -0.529 73.984 -0.544 74.035 -0.573 c
+h
+74.785 0.353 m
+74.741 -0.661 l
+74.373 -0.661 l
+74.388 0.661 l
+74.785 0.661 l
+h
+76.611 -2.807 m
+76.611 -2.701 76.571 -2.612 76.493 -2.543 c
+76.412 -2.466 76.262 -2.377 76.038 -2.278 c
+75.774 -2.171 75.586 -2.08 75.479 -1.999 c
+75.369 -1.922 75.292 -1.834 75.244 -1.735 c
+75.193 -1.639 75.171 -1.521 75.171 -1.382 c
+75.171 -1.139 75.259 -0.937 75.435 -0.779 c
+75.612 -0.613 75.836 -0.529 76.111 -0.529 c
+76.406 -0.529 76.641 -0.617 76.817 -0.794 c
+76.994 -0.962 77.081 -1.176 77.081 -1.44 c
+76.597 -1.44 l
+76.597 -1.304 76.545 -1.19 76.45 -1.103 c
+76.362 -1.007 76.248 -0.955 76.111 -0.955 c
+75.965 -0.955 75.851 -0.995 75.774 -1.072 c
+75.693 -1.143 75.656 -1.242 75.656 -1.367 c
+75.656 -1.466 75.685 -1.543 75.745 -1.602 c
+75.803 -1.66 75.942 -1.741 76.171 -1.837 c
+76.53 -1.984 76.777 -2.127 76.905 -2.263 c
+77.041 -2.392 77.112 -2.564 77.112 -2.778 c
+77.112 -3.035 77.016 -3.241 76.832 -3.395 c
+76.655 -3.553 76.42 -3.63 76.127 -3.63 c
+75.81 -3.63 75.556 -3.542 75.362 -3.366 c
+75.174 -3.183 75.082 -2.95 75.082 -2.675 c
+75.568 -2.675 l
+75.575 -2.844 75.627 -2.977 75.714 -3.072 c
+75.81 -3.16 75.95 -3.204 76.127 -3.204 c
+76.281 -3.204 76.398 -3.171 76.479 -3.102 c
+76.568 -3.035 76.611 -2.936 76.611 -2.807 c
+f
+Q
+q 1 0 0 1 453.7345 138.0102 cm
+0 0 m
+0.015 -0.324 l
+0.199 -0.07 0.441 0.059 0.735 0.059 c
+1.264 0.059 1.532 -0.294 1.544 -0.999 c
+1.544 -2.984 l
+1.058 -2.984 l
+1.058 -1.029 l
+1.058 -0.794 1.018 -0.628 0.941 -0.529 c
+0.86 -0.434 0.742 -0.382 0.588 -0.382 c
+0.47 -0.382 0.36 -0.422 0.264 -0.5 c
+0.166 -0.58 0.088 -0.687 0.029 -0.823 c
+0.029 -2.984 l
+-0.455 -2.984 l
+-0.455 0 l
+h
+1.992 -1.352 m
+1.992 -0.922 2.095 -0.58 2.3 -0.324 c
+2.514 -0.07 2.793 0.059 3.138 0.059 c
+3.48 0.059 3.755 -0.07 3.961 -0.324 c
+4.175 -0.569 4.289 -0.904 4.299 -1.323 c
+4.299 -1.631 l
+4.299 -2.065 4.189 -2.406 3.977 -2.66 c
+3.77 -2.917 3.491 -3.042 3.138 -3.042 c
+2.793 -3.042 2.521 -2.921 2.315 -2.675 c
+2.109 -2.433 1.999 -2.098 1.992 -1.675 c
+h
+2.477 -1.631 m
+2.477 -1.947 2.535 -2.19 2.653 -2.366 c
+2.778 -2.535 2.94 -2.616 3.138 -2.616 c
+3.568 -2.616 3.792 -2.308 3.815 -1.691 c
+3.815 -1.352 l
+3.815 -1.051 3.748 -0.808 3.624 -0.632 c
+3.506 -0.455 3.344 -0.367 3.138 -0.367 c
+2.94 -0.367 2.778 -0.455 2.653 -0.632 c
+2.535 -0.808 2.477 -1.051 2.477 -1.352 c
+h
+5.373 0.721 m
+5.373 0 l
+5.828 0 l
+5.828 -0.397 l
+5.373 -0.397 l
+5.373 -2.248 l
+5.373 -2.366 5.39 -2.454 5.431 -2.514 c
+5.468 -2.572 5.538 -2.601 5.637 -2.601 c
+5.696 -2.601 5.758 -2.595 5.828 -2.572 c
+5.828 -2.984 l
+5.71 -3.021 5.596 -3.042 5.49 -3.042 c
+5.292 -3.042 5.141 -2.977 5.035 -2.836 c
+4.935 -2.701 4.887 -2.506 4.887 -2.248 c
+4.887 -0.397 l
+4.432 -0.397 l
+4.432 0 l
+4.887 0 l
+4.887 0.721 l
+h
+7.349 -3.042 m
+6.975 -3.042 6.691 -2.936 6.497 -2.719 c
+6.298 -2.495 6.203 -2.167 6.203 -1.735 c
+6.203 -1.367 l
+6.203 -0.926 6.294 -0.58 6.483 -0.324 c
+6.677 -0.07 6.953 0.059 7.306 0.059 c
+7.647 0.059 7.9 -0.055 8.07 -0.279 c
+8.247 -0.507 8.338 -0.852 8.349 -1.323 c
+8.349 -1.631 l
+6.688 -1.631 l
+6.688 -1.705 l
+6.688 -2.028 6.747 -2.263 6.865 -2.41 c
+6.982 -2.55 7.151 -2.616 7.379 -2.616 c
+7.526 -2.616 7.651 -2.595 7.761 -2.543 c
+7.867 -2.484 7.971 -2.396 8.07 -2.278 c
+8.32 -2.587 l
+8.114 -2.892 7.79 -3.042 7.349 -3.042 c
+7.306 -0.367 m
+7.1 -0.367 6.945 -0.437 6.85 -0.573 c
+6.751 -0.713 6.695 -0.926 6.688 -1.22 c
+7.864 -1.22 l
+7.864 -1.147 l
+7.842 -0.874 7.79 -0.676 7.703 -0.559 c
+7.614 -0.434 7.482 -0.367 7.306 -0.367 c
+11.406 -2.219 m
+11.406 -2.113 11.366 -2.024 11.289 -1.955 c
+11.208 -1.878 11.057 -1.789 10.834 -1.691 c
+10.568 -1.583 10.381 -1.492 10.275 -1.411 c
+10.165 -1.334 10.088 -1.246 10.04 -1.147 c
+9.988 -1.051 9.966 -0.933 9.966 -0.794 c
+9.966 -0.551 10.055 -0.349 10.231 -0.191 c
+10.406 -0.025 10.631 0.059 10.907 0.059 c
+11.2 0.059 11.436 -0.029 11.612 -0.206 c
+11.788 -0.374 11.877 -0.588 11.877 -0.852 c
+11.391 -0.852 l
+11.391 -0.716 11.341 -0.602 11.245 -0.515 c
+11.156 -0.419 11.042 -0.367 10.907 -0.367 c
+10.76 -0.367 10.645 -0.407 10.568 -0.484 c
+10.487 -0.555 10.451 -0.654 10.451 -0.779 c
+10.451 -0.878 10.481 -0.955 10.539 -1.014 c
+10.598 -1.072 10.738 -1.153 10.965 -1.249 c
+11.326 -1.396 11.572 -1.539 11.701 -1.675 c
+11.836 -1.804 11.906 -1.977 11.906 -2.19 c
+11.906 -2.447 11.811 -2.653 11.627 -2.807 c
+11.451 -2.965 11.216 -3.042 10.921 -3.042 c
+10.605 -3.042 10.352 -2.954 10.157 -2.778 c
+9.97 -2.595 9.878 -2.362 9.878 -2.088 c
+10.363 -2.088 l
+10.37 -2.256 10.422 -2.389 10.51 -2.484 c
+10.605 -2.572 10.745 -2.616 10.921 -2.616 c
+11.075 -2.616 11.193 -2.583 11.274 -2.514 c
+11.362 -2.447 11.406 -2.348 11.406 -2.219 c
+12.259 -1.352 m
+12.259 -0.922 12.362 -0.58 12.567 -0.324 c
+12.781 -0.07 13.06 0.059 13.406 0.059 c
+13.747 0.059 14.023 -0.07 14.229 -0.324 c
+14.442 -0.569 14.556 -0.904 14.566 -1.323 c
+14.566 -1.631 l
+14.566 -2.065 14.456 -2.406 14.244 -2.66 c
+14.038 -2.917 13.758 -3.042 13.406 -3.042 c
+13.06 -3.042 12.788 -2.921 12.582 -2.675 c
+12.376 -2.433 12.266 -2.098 12.259 -1.675 c
+h
+12.744 -1.631 m
+12.744 -1.947 12.803 -2.19 12.92 -2.366 c
+13.045 -2.535 13.207 -2.616 13.406 -2.616 c
+13.835 -2.616 14.059 -2.308 14.082 -1.691 c
+14.082 -1.352 l
+14.082 -1.051 14.015 -0.808 13.891 -0.632 c
+13.773 -0.455 13.612 -0.367 13.406 -0.367 c
+13.207 -0.367 13.045 -0.455 12.92 -0.632 c
+12.803 -0.808 12.744 -1.051 12.744 -1.352 c
+h
+16.742 -2.984 -0.5 2.984 re
+16.772 0.794 m
+16.772 0.706 16.746 0.632 16.698 0.574 c
+16.658 0.522 16.588 0.5 16.492 0.5 c
+16.405 0.5 16.334 0.522 16.286 0.574 c
+16.247 0.632 16.228 0.698 16.228 0.779 c
+16.228 0.867 16.247 0.941 16.286 1 c
+16.334 1.058 16.405 1.087 16.492 1.087 c
+16.588 1.087 16.658 1.058 16.698 1 c
+16.746 0.941 16.772 0.871 16.772 0.794 c
+18.047 0.721 m
+18.047 0 l
+18.502 0 l
+18.502 -0.397 l
+18.047 -0.397 l
+18.047 -2.248 l
+18.047 -2.366 18.065 -2.454 18.105 -2.514 c
+18.142 -2.572 18.212 -2.601 18.311 -2.601 c
+18.37 -2.601 18.433 -2.595 18.502 -2.572 c
+18.502 -2.984 l
+18.385 -3.021 18.271 -3.042 18.165 -3.042 c
+17.966 -3.042 17.816 -2.977 17.708 -2.836 c
+17.61 -2.701 17.562 -2.506 17.562 -2.248 c
+17.562 -0.397 l
+17.106 -0.397 l
+17.106 0 l
+17.562 0 l
+17.562 0.721 l
+h
+19.293 0.941 m
+19.248 -0.073 l
+18.881 -0.073 l
+18.896 1.249 l
+19.293 1.249 l
+h
+21.119 -2.219 m
+21.119 -2.113 21.078 -2.024 21.001 -1.955 c
+20.92 -1.878 20.77 -1.789 20.546 -1.691 c
+20.281 -1.583 20.093 -1.492 19.987 -1.411 c
+19.877 -1.334 19.8 -1.246 19.752 -1.147 c
+19.7 -1.051 19.678 -0.933 19.678 -0.794 c
+19.678 -0.551 19.767 -0.349 19.943 -0.191 c
+20.12 -0.025 20.344 0.059 20.619 0.059 c
+20.913 0.059 21.148 -0.029 21.325 -0.206 c
+21.501 -0.374 21.589 -0.588 21.589 -0.852 c
+21.105 -0.852 l
+21.105 -0.716 21.053 -0.602 20.957 -0.515 c
+20.869 -0.419 20.755 -0.367 20.619 -0.367 c
+20.472 -0.367 20.359 -0.407 20.281 -0.484 c
+20.201 -0.555 20.164 -0.654 20.164 -0.779 c
+20.164 -0.878 20.193 -0.955 20.251 -1.014 c
+20.311 -1.072 20.45 -1.153 20.677 -1.249 c
+21.038 -1.396 21.284 -1.539 21.413 -1.675 c
+21.549 -1.804 21.618 -1.977 21.618 -2.19 c
+21.618 -2.447 21.523 -2.653 21.34 -2.807 c
+21.163 -2.965 20.928 -3.042 20.634 -3.042 c
+20.318 -3.042 20.064 -2.954 19.869 -2.778 c
+19.682 -2.595 19.59 -2.362 19.59 -2.088 c
+20.075 -2.088 l
+20.083 -2.256 20.134 -2.389 20.222 -2.484 c
+20.318 -2.572 20.457 -2.616 20.634 -2.616 c
+20.788 -2.616 20.906 -2.583 20.987 -2.514 c
+21.074 -2.447 21.119 -2.348 21.119 -2.219 c
+25.425 -1.631 m
+25.425 -2.102 25.342 -2.454 25.176 -2.69 c
+25.007 -2.925 24.768 -3.042 24.455 -3.042 c
+24.151 -3.042 23.919 -2.932 23.765 -2.705 c
+23.765 -4.13 l
+23.28 -4.13 l
+23.28 0 l
+23.721 0 l
+23.75 -0.338 l
+23.904 -0.073 24.136 0.059 24.44 0.059 c
+24.772 0.059 25.018 -0.058 25.176 -0.294 c
+25.342 -0.521 25.425 -0.86 25.425 -1.308 c
+h
+24.941 -1.352 m
+24.941 -1.022 24.885 -0.775 24.779 -0.617 c
+24.679 -0.463 24.518 -0.382 24.294 -0.382 c
+24.058 -0.382 23.883 -0.5 23.765 -0.735 c
+23.765 -2.278 l
+23.883 -2.506 24.062 -2.616 24.309 -2.616 c
+24.521 -2.616 24.679 -2.539 24.779 -2.381 c
+24.885 -2.227 24.941 -1.984 24.941 -1.66 c
+h
+27.12 -0.455 m
+27.05 -0.448 26.977 -0.441 26.9 -0.441 c
+26.642 -0.441 26.466 -0.58 26.37 -0.852 c
+26.37 -2.984 l
+25.885 -2.984 l
+25.885 0 l
+26.356 0 l
+26.37 -0.309 l
+26.495 -0.066 26.678 0.059 26.914 0.059 c
+26.991 0.059 27.054 0.044 27.105 0.015 c
+h
+27.333 -1.352 m
+27.333 -0.922 27.436 -0.58 27.642 -0.324 c
+27.854 -0.07 28.134 0.059 28.479 0.059 c
+28.821 0.059 29.097 -0.07 29.302 -0.324 c
+29.516 -0.569 29.63 -0.904 29.641 -1.323 c
+29.641 -1.631 l
+29.641 -2.065 29.531 -2.406 29.317 -2.66 c
+29.111 -2.917 28.832 -3.042 28.479 -3.042 c
+28.134 -3.042 27.862 -2.921 27.656 -2.675 c
+27.451 -2.433 27.34 -2.098 27.333 -1.675 c
+h
+27.818 -1.631 m
+27.818 -1.947 27.877 -2.19 27.995 -2.366 c
+28.12 -2.535 28.281 -2.616 28.479 -2.616 c
+28.909 -2.616 29.134 -2.308 29.155 -1.691 c
+29.155 -1.352 l
+29.155 -1.051 29.09 -0.808 28.964 -0.632 c
+28.847 -0.455 28.685 -0.367 28.479 -0.367 c
+28.281 -0.367 28.12 -0.455 27.995 -0.632 c
+27.877 -0.808 27.818 -1.051 27.818 -1.352 c
+h
+32.242 -1.631 m
+32.242 -2.102 32.158 -2.454 31.993 -2.69 c
+31.823 -2.925 31.581 -3.042 31.258 -3.042 c
+30.942 -3.042 30.707 -2.907 30.552 -2.631 c
+30.522 -2.984 l
+30.082 -2.984 l
+30.082 1.249 l
+30.567 1.249 l
+30.567 -0.324 l
+30.721 -0.07 30.952 0.059 31.258 0.059 c
+31.581 0.059 31.823 -0.058 31.993 -0.294 c
+32.158 -0.529 32.242 -0.878 32.242 -1.338 c
+h
+31.757 -1.352 m
+31.757 -0.999 31.706 -0.75 31.611 -0.602 c
+31.511 -0.455 31.349 -0.382 31.125 -0.382 c
+30.879 -0.382 30.692 -0.521 30.567 -0.794 c
+30.567 -2.205 l
+30.684 -2.469 30.875 -2.601 31.14 -2.601 c
+31.353 -2.601 31.511 -2.528 31.611 -2.381 c
+31.706 -2.227 31.757 -1.984 31.757 -1.66 c
+h
+34.212 -2.984 m
+34.183 -2.917 34.16 -2.807 34.154 -2.66 c
+33.977 -2.917 33.757 -3.042 33.492 -3.042 c
+33.217 -3.042 32.999 -2.969 32.845 -2.822 c
+32.698 -2.668 32.625 -2.451 32.625 -2.175 c
+32.625 -1.874 32.727 -1.631 32.933 -1.455 c
+33.139 -1.271 33.422 -1.176 33.786 -1.176 c
+34.138 -1.176 l
+34.138 -0.852 l
+34.138 -0.676 34.098 -0.555 34.021 -0.484 c
+33.94 -0.407 33.822 -0.367 33.668 -0.367 c
+33.521 -0.367 33.396 -0.411 33.3 -0.5 c
+33.213 -0.588 33.169 -0.698 33.169 -0.823 c
+32.683 -0.823 l
+32.683 -0.676 32.727 -0.536 32.816 -0.397 c
+32.904 -0.249 33.022 -0.139 33.169 -0.058 c
+33.323 0.019 33.495 0.059 33.683 0.059 c
+33.996 0.059 34.231 -0.022 34.389 -0.176 c
+34.543 -0.324 34.624 -0.544 34.624 -0.837 c
+34.624 -2.337 l
+34.631 -2.572 34.668 -2.774 34.726 -2.94 c
+34.726 -2.984 l
+h
+33.566 -2.601 m
+33.683 -2.601 33.793 -2.568 33.903 -2.499 c
+34.01 -2.433 34.087 -2.348 34.138 -2.248 c
+34.138 -1.543 l
+33.874 -1.543 l
+33.639 -1.543 33.452 -1.595 33.315 -1.691 c
+33.186 -1.789 33.124 -1.932 33.124 -2.117 c
+33.124 -2.285 33.154 -2.406 33.213 -2.484 c
+33.279 -2.564 33.396 -2.601 33.566 -2.601 c
+37.365 -1.631 m
+37.365 -2.102 37.281 -2.454 37.115 -2.69 c
+36.946 -2.925 36.704 -3.042 36.38 -3.042 c
+36.064 -3.042 35.829 -2.907 35.675 -2.631 c
+35.646 -2.984 l
+35.204 -2.984 l
+35.204 1.249 l
+35.69 1.249 l
+35.69 -0.324 l
+35.844 -0.07 36.075 0.059 36.38 0.059 c
+36.704 0.059 36.946 -0.058 37.115 -0.294 c
+37.281 -0.529 37.365 -0.878 37.365 -1.338 c
+h
+36.88 -1.352 m
+36.88 -0.999 36.828 -0.75 36.733 -0.602 c
+36.633 -0.455 36.472 -0.382 36.248 -0.382 c
+36.002 -0.382 35.814 -0.521 35.69 -0.794 c
+35.69 -2.205 l
+35.806 -2.469 35.998 -2.601 36.263 -2.601 c
+36.475 -2.601 36.633 -2.528 36.733 -2.381 c
+36.828 -2.227 36.88 -1.984 36.88 -1.66 c
+h
+38.339 -2.984 -0.5 4.233 re
+39.82 -2.175 m
+40.363 0 l
+40.878 0 l
+39.908 -3.41 l
+39.838 -3.663 39.735 -3.854 39.599 -3.983 c
+39.459 -4.119 39.309 -4.189 39.143 -4.189 c
+39.074 -4.189 38.989 -4.174 38.894 -4.145 c
+38.894 -3.734 l
+38.996 -3.748 l
+39.133 -3.748 39.239 -3.711 39.32 -3.645 c
+39.409 -3.576 39.474 -3.458 39.526 -3.293 c
+39.614 -2.954 l
+38.747 0 l
+39.276 0 l
+h
+43.935 -2.984 m
+43.906 -2.917 43.884 -2.807 43.877 -2.66 c
+43.7 -2.917 43.48 -3.042 43.216 -3.042 c
+42.94 -3.042 42.723 -2.969 42.568 -2.822 c
+42.422 -2.668 42.348 -2.451 42.348 -2.175 c
+42.348 -1.874 42.451 -1.631 42.657 -1.455 c
+42.862 -1.271 43.145 -1.176 43.509 -1.176 c
+43.862 -1.176 l
+43.862 -0.852 l
+43.862 -0.676 43.822 -0.555 43.744 -0.484 c
+43.663 -0.407 43.546 -0.367 43.392 -0.367 c
+43.245 -0.367 43.12 -0.411 43.024 -0.5 c
+42.936 -0.588 42.892 -0.698 42.892 -0.823 c
+42.407 -0.823 l
+42.407 -0.676 42.451 -0.536 42.539 -0.397 c
+42.627 -0.249 42.745 -0.139 42.892 -0.058 c
+43.046 0.019 43.219 0.059 43.407 0.059 c
+43.719 0.059 43.954 -0.022 44.112 -0.176 c
+44.266 -0.324 44.347 -0.544 44.347 -0.837 c
+44.347 -2.337 l
+44.354 -2.572 44.392 -2.774 44.45 -2.94 c
+44.45 -2.984 l
+h
+43.289 -2.601 m
+43.407 -2.601 43.517 -2.568 43.627 -2.499 c
+43.733 -2.433 43.81 -2.348 43.862 -2.248 c
+43.862 -1.543 l
+43.598 -1.543 l
+43.362 -1.543 43.175 -1.595 43.039 -1.691 c
+42.91 -1.789 42.848 -1.932 42.848 -2.117 c
+42.848 -2.285 42.877 -2.406 42.936 -2.484 c
+43.002 -2.564 43.12 -2.601 43.289 -2.601 c
+47.368 -0.455 m
+47.298 -0.448 47.224 -0.441 47.147 -0.441 c
+46.89 -0.441 46.713 -0.58 46.618 -0.852 c
+46.618 -2.984 l
+46.133 -2.984 l
+46.133 0 l
+46.603 0 l
+46.618 -0.309 l
+46.743 -0.066 46.927 0.059 47.162 0.059 c
+47.239 0.059 47.301 0.044 47.353 0.015 c
+h
+48.745 -3.042 m
+48.371 -3.042 48.088 -2.936 47.893 -2.719 c
+47.695 -2.495 47.6 -2.167 47.6 -1.735 c
+47.6 -1.367 l
+47.6 -0.926 47.691 -0.58 47.879 -0.324 c
+48.074 -0.07 48.349 0.059 48.702 0.059 c
+49.044 0.059 49.297 -0.055 49.466 -0.279 c
+49.643 -0.507 49.734 -0.852 49.745 -1.323 c
+49.745 -1.631 l
+48.084 -1.631 l
+48.084 -1.705 l
+48.084 -2.028 48.143 -2.263 48.261 -2.41 c
+48.379 -2.55 48.547 -2.616 48.775 -2.616 c
+48.922 -2.616 49.047 -2.595 49.157 -2.543 c
+49.264 -2.484 49.367 -2.396 49.466 -2.278 c
+49.716 -2.587 l
+49.51 -2.892 49.187 -3.042 48.745 -3.042 c
+48.702 -0.367 m
+48.496 -0.367 48.342 -0.437 48.246 -0.573 c
+48.147 -0.713 48.092 -0.926 48.084 -1.22 c
+49.26 -1.22 l
+49.26 -1.147 l
+49.238 -0.874 49.187 -0.676 49.098 -0.559 c
+49.011 -0.434 48.878 -0.367 48.702 -0.367 c
+50.362 -2.984 m
+50.362 -0.397 l
+49.981 -0.397 l
+49.981 0 l
+50.362 0 l
+50.362 0.339 l
+50.37 0.64 50.451 0.875 50.598 1.044 c
+50.744 1.22 50.954 1.309 51.23 1.309 c
+51.326 1.309 51.425 1.294 51.524 1.264 c
+51.494 0.852 l
+51.425 0.86 51.351 0.867 51.274 0.867 c
+50.998 0.867 50.862 0.669 50.862 0.279 c
+50.862 0 l
+51.363 0 l
+51.363 -0.397 l
+50.862 -0.397 l
+50.862 -2.984 l
+h
+52.821 -3.042 m
+52.446 -3.042 52.163 -2.936 51.968 -2.719 c
+51.77 -2.495 51.675 -2.167 51.675 -1.735 c
+51.675 -1.367 l
+51.675 -0.926 51.766 -0.58 51.954 -0.324 c
+52.149 -0.07 52.425 0.059 52.777 0.059 c
+53.119 0.059 53.372 -0.055 53.541 -0.279 c
+53.718 -0.507 53.809 -0.852 53.821 -1.323 c
+53.821 -1.631 l
+52.159 -1.631 l
+52.159 -1.705 l
+52.159 -2.028 52.219 -2.263 52.336 -2.41 c
+52.454 -2.55 52.623 -2.616 52.851 -2.616 c
+52.997 -2.616 53.123 -2.595 53.233 -2.543 c
+53.339 -2.484 53.443 -2.396 53.541 -2.278 c
+53.791 -2.587 l
+53.585 -2.892 53.262 -3.042 52.821 -3.042 c
+52.777 -0.367 m
+52.571 -0.367 52.417 -0.437 52.321 -0.573 c
+52.222 -0.713 52.167 -0.926 52.159 -1.22 c
+53.335 -1.22 l
+53.335 -1.147 l
+53.314 -0.874 53.262 -0.676 53.173 -0.559 c
+53.086 -0.434 52.953 -0.367 52.777 -0.367 c
+55.467 -0.455 m
+55.397 -0.448 55.324 -0.441 55.247 -0.441 c
+54.989 -0.441 54.813 -0.58 54.717 -0.852 c
+54.717 -2.984 l
+54.232 -2.984 l
+54.232 0 l
+54.702 0 l
+54.717 -0.309 l
+54.842 -0.066 55.026 0.059 55.261 0.059 c
+55.338 0.059 55.401 0.044 55.452 0.015 c
+h
+56.841 -3.042 m
+56.466 -3.042 56.184 -2.936 55.989 -2.719 c
+55.791 -2.495 55.695 -2.167 55.695 -1.735 c
+55.695 -1.367 l
+55.695 -0.926 55.787 -0.58 55.974 -0.324 c
+56.169 -0.07 56.444 0.059 56.797 0.059 c
+57.139 0.059 57.393 -0.055 57.562 -0.279 c
+57.738 -0.507 57.83 -0.852 57.84 -1.323 c
+57.84 -1.631 l
+56.18 -1.631 l
+56.18 -1.705 l
+56.18 -2.028 56.238 -2.263 56.356 -2.41 c
+56.474 -2.55 56.643 -2.616 56.871 -2.616 c
+57.017 -2.616 57.142 -2.595 57.252 -2.543 c
+57.36 -2.484 57.462 -2.396 57.562 -2.278 c
+57.811 -2.587 l
+57.605 -2.892 57.283 -3.042 56.841 -3.042 c
+56.797 -0.367 m
+56.591 -0.367 56.437 -0.437 56.342 -0.573 c
+56.242 -0.713 56.188 -0.926 56.18 -1.22 c
+57.356 -1.22 l
+57.356 -1.147 l
+57.333 -0.874 57.283 -0.676 57.194 -0.559 c
+57.106 -0.434 56.973 -0.367 56.797 -0.367 c
+58.708 0 m
+58.723 -0.324 l
+58.906 -0.07 59.149 0.059 59.443 0.059 c
+59.972 0.059 60.24 -0.294 60.252 -0.999 c
+60.252 -2.984 l
+59.766 -2.984 l
+59.766 -1.029 l
+59.766 -0.794 59.726 -0.628 59.649 -0.529 c
+59.568 -0.434 59.45 -0.382 59.296 -0.382 c
+59.178 -0.382 59.068 -0.422 58.973 -0.5 c
+58.873 -0.58 58.796 -0.687 58.738 -0.823 c
+58.738 -2.984 l
+58.252 -2.984 l
+58.252 0 l
+h
+61.835 -2.616 m
+62 -2.616 62.133 -2.568 62.232 -2.469 c
+62.328 -2.373 62.382 -2.23 62.394 -2.043 c
+62.85 -2.043 l
+62.839 -2.329 62.736 -2.568 62.54 -2.763 c
+62.353 -2.95 62.118 -3.042 61.835 -3.042 c
+61.472 -3.042 61.192 -2.925 60.998 -2.69 c
+60.799 -2.454 60.703 -2.109 60.703 -1.646 c
+60.703 -1.323 l
+60.703 -0.874 60.795 -0.529 60.982 -0.294 c
+61.177 -0.058 61.46 0.059 61.835 0.059 c
+62.137 0.059 62.379 -0.04 62.555 -0.235 c
+62.739 -0.434 62.839 -0.698 62.85 -1.029 c
+62.394 -1.029 l
+62.372 -0.804 62.313 -0.639 62.218 -0.529 c
+62.129 -0.422 62 -0.367 61.835 -0.367 c
+61.618 -0.367 61.457 -0.441 61.35 -0.588 c
+61.251 -0.727 61.196 -0.955 61.189 -1.278 c
+61.189 -1.66 l
+61.189 -2.013 61.236 -2.263 61.335 -2.41 c
+61.442 -2.55 61.607 -2.616 61.835 -2.616 c
+64.301 -3.042 m
+63.926 -3.042 63.643 -2.936 63.448 -2.719 c
+63.25 -2.495 63.155 -2.167 63.155 -1.735 c
+63.155 -1.367 l
+63.155 -0.926 63.246 -0.58 63.434 -0.324 c
+63.629 -0.07 63.904 0.059 64.257 0.059 c
+64.599 0.059 64.853 -0.055 65.021 -0.279 c
+65.198 -0.507 65.289 -0.852 65.301 -1.323 c
+65.301 -1.631 l
+63.639 -1.631 l
+63.639 -1.705 l
+63.639 -2.028 63.698 -2.263 63.816 -2.41 c
+63.934 -2.55 64.103 -2.616 64.331 -2.616 c
+64.477 -2.616 64.602 -2.595 64.713 -2.543 c
+64.819 -2.484 64.922 -2.396 65.021 -2.278 c
+65.271 -2.587 l
+65.065 -2.892 64.742 -3.042 64.301 -3.042 c
+64.257 -0.367 m
+64.051 -0.367 63.897 -0.437 63.801 -0.573 c
+63.702 -0.713 63.647 -0.926 63.639 -1.22 c
+64.815 -1.22 l
+64.815 -1.147 l
+64.793 -0.874 64.742 -0.676 64.654 -0.559 c
+64.566 -0.434 64.433 -0.367 64.257 -0.367 c
+65.713 -3.777 m
+65.418 -3.586 l
+65.595 -3.341 65.686 -3.09 65.697 -2.836 c
+65.697 -2.381 l
+66.197 -2.381 l
+66.197 -2.778 l
+66.197 -2.965 66.145 -3.149 66.05 -3.337 c
+65.962 -3.52 65.848 -3.667 65.713 -3.777 c
+f
+Q
+q 1 0 0 1 492.43 131.4251 cm
+0 0 m
+0.014 -0.324 l
+0.198 -0.07 0.441 0.059 0.735 0.059 c
+1.264 0.059 1.533 -0.294 1.543 -0.999 c
+1.543 -2.984 l
+1.058 -2.984 l
+1.058 -1.029 l
+1.058 -0.794 1.018 -0.628 0.941 -0.529 c
+0.86 -0.434 0.742 -0.382 0.588 -0.382 c
+0.47 -0.382 0.36 -0.422 0.264 -0.5 c
+0.166 -0.58 0.088 -0.687 0.029 -0.823 c
+0.029 -2.984 l
+-0.456 -2.984 l
+-0.456 0 l
+h
+1.995 -1.352 m
+1.995 -0.922 2.098 -0.58 2.304 -0.324 c
+2.517 -0.07 2.796 0.059 3.142 0.059 c
+3.484 0.059 3.759 -0.07 3.965 -0.324 c
+4.178 -0.569 4.292 -0.904 4.303 -1.323 c
+4.303 -1.631 l
+4.303 -2.065 4.193 -2.406 3.979 -2.66 c
+3.774 -2.917 3.495 -3.042 3.142 -3.042 c
+2.796 -3.042 2.524 -2.921 2.319 -2.675 c
+2.113 -2.433 2.003 -2.098 1.995 -1.675 c
+h
+2.481 -1.631 m
+2.481 -1.947 2.539 -2.19 2.657 -2.366 c
+2.782 -2.535 2.944 -2.616 3.142 -2.616 c
+3.572 -2.616 3.796 -2.308 3.818 -1.691 c
+3.818 -1.352 l
+3.818 -1.051 3.752 -0.808 3.627 -0.632 c
+3.509 -0.455 3.347 -0.367 3.142 -0.367 c
+2.944 -0.367 2.782 -0.455 2.657 -0.632 c
+2.539 -0.808 2.481 -1.051 2.481 -1.352 c
+h
+5.373 0.721 m
+5.373 0 l
+5.828 0 l
+5.828 -0.397 l
+5.373 -0.397 l
+5.373 -2.248 l
+5.373 -2.366 5.39 -2.454 5.431 -2.514 c
+5.468 -2.572 5.538 -2.601 5.637 -2.601 c
+5.696 -2.601 5.758 -2.595 5.828 -2.572 c
+5.828 -2.984 l
+5.71 -3.021 5.596 -3.042 5.49 -3.042 c
+5.292 -3.042 5.141 -2.977 5.034 -2.836 c
+4.935 -2.701 4.887 -2.506 4.887 -2.248 c
+4.887 -0.397 l
+4.432 -0.397 l
+4.432 0 l
+4.887 0 l
+4.887 0.721 l
+h
+9.028 -2.984 m
+8.999 -2.917 8.978 -2.807 8.97 -2.66 c
+8.793 -2.917 8.573 -3.042 8.309 -3.042 c
+8.033 -3.042 7.816 -2.969 7.661 -2.822 c
+7.515 -2.668 7.441 -2.451 7.441 -2.175 c
+7.441 -1.874 7.544 -1.631 7.75 -1.455 c
+7.956 -1.271 8.239 -1.176 8.602 -1.176 c
+8.955 -1.176 l
+8.955 -0.852 l
+8.955 -0.676 8.914 -0.555 8.837 -0.484 c
+8.756 -0.407 8.639 -0.367 8.485 -0.367 c
+8.338 -0.367 8.213 -0.411 8.118 -0.5 c
+8.029 -0.588 7.985 -0.698 7.985 -0.823 c
+7.5 -0.823 l
+7.5 -0.676 7.544 -0.536 7.632 -0.397 c
+7.721 -0.249 7.838 -0.139 7.985 -0.058 c
+8.139 0.019 8.312 0.059 8.5 0.059 c
+8.812 0.059 9.047 -0.022 9.205 -0.176 c
+9.359 -0.324 9.44 -0.544 9.44 -0.837 c
+9.44 -2.337 l
+9.448 -2.572 9.485 -2.774 9.543 -2.94 c
+9.543 -2.984 l
+h
+8.382 -2.601 m
+8.5 -2.601 8.61 -2.568 8.72 -2.499 c
+8.827 -2.433 8.904 -2.348 8.955 -2.248 c
+8.955 -1.543 l
+8.691 -1.543 l
+8.455 -1.543 8.268 -1.595 8.132 -1.691 c
+8.004 -1.789 7.941 -1.932 7.941 -2.117 c
+7.941 -2.285 7.97 -2.406 8.029 -2.484 c
+8.095 -2.564 8.213 -2.601 8.382 -2.601 c
+10.477 0 m
+10.491 -0.324 l
+10.675 -0.07 10.917 0.059 11.212 0.059 c
+11.74 0.059 12.009 -0.294 12.02 -0.999 c
+12.02 -2.984 l
+11.535 -2.984 l
+11.535 -1.029 l
+11.535 -0.794 11.495 -0.628 11.418 -0.529 c
+11.337 -0.434 11.219 -0.382 11.065 -0.382 c
+10.947 -0.382 10.836 -0.422 10.741 -0.5 c
+10.642 -0.58 10.564 -0.687 10.506 -0.823 c
+10.506 -2.984 l
+10.021 -2.984 l
+10.021 0 l
+h
+13.674 -1.352 m
+13.674 -0.922 13.776 -0.58 13.982 -0.324 c
+14.196 -0.07 14.475 0.059 14.82 0.059 c
+15.162 0.059 15.438 -0.07 15.644 -0.324 c
+15.856 -0.569 15.97 -0.904 15.981 -1.323 c
+15.981 -1.631 l
+15.981 -2.065 15.871 -2.406 15.658 -2.66 c
+15.453 -2.917 15.173 -3.042 14.82 -3.042 c
+14.475 -3.042 14.202 -2.921 13.997 -2.675 c
+13.791 -2.433 13.681 -2.098 13.674 -1.675 c
+h
+14.159 -1.631 m
+14.159 -1.947 14.217 -2.19 14.335 -2.366 c
+14.46 -2.535 14.622 -2.616 14.82 -2.616 c
+15.25 -2.616 15.474 -2.308 15.496 -1.691 c
+15.496 -1.352 l
+15.496 -1.051 15.43 -0.808 15.305 -0.632 c
+15.187 -0.455 15.025 -0.367 14.82 -0.367 c
+14.622 -0.367 14.46 -0.455 14.335 -0.632 c
+14.217 -0.808 14.159 -1.051 14.159 -1.352 c
+h
+18.58 -1.631 m
+18.58 -2.102 18.495 -2.454 18.329 -2.69 c
+18.161 -2.925 17.918 -3.042 17.595 -3.042 c
+17.279 -3.042 17.043 -2.907 16.889 -2.631 c
+16.86 -2.984 l
+16.419 -2.984 l
+16.419 1.249 l
+16.904 1.249 l
+16.904 -0.324 l
+17.058 -0.07 17.29 0.059 17.595 0.059 c
+17.918 0.059 18.161 -0.058 18.329 -0.294 c
+18.495 -0.529 18.58 -0.878 18.58 -1.338 c
+h
+18.094 -1.352 m
+18.094 -0.999 18.042 -0.75 17.947 -0.602 c
+17.848 -0.455 17.687 -0.382 17.462 -0.382 c
+17.216 -0.382 17.028 -0.521 16.904 -0.794 c
+16.904 -2.205 l
+17.022 -2.469 17.213 -2.601 17.477 -2.601 c
+17.691 -2.601 17.848 -2.528 17.947 -2.381 c
+18.042 -2.227 18.094 -1.984 18.094 -1.66 c
+h
+19.524 0 m
+19.524 -3.366 l
+19.513 -3.913 19.293 -4.189 18.863 -4.189 c
+18.763 -4.189 18.675 -4.174 18.597 -4.145 c
+18.597 -3.734 l
+18.649 -3.74 18.708 -3.748 18.788 -3.748 c
+18.867 -3.748 18.925 -3.719 18.965 -3.659 c
+19.013 -3.601 19.039 -3.491 19.039 -3.337 c
+19.039 0 l
+h
+19.538 0.794 m
+19.538 0.706 19.513 0.632 19.465 0.574 c
+19.424 0.522 19.355 0.5 19.259 0.5 c
+19.171 0.5 19.102 0.522 19.054 0.574 c
+19.013 0.632 18.994 0.698 18.994 0.779 c
+18.994 0.867 19.013 0.941 19.054 1 c
+19.102 1.058 19.171 1.087 19.259 1.087 c
+19.355 1.087 19.424 1.058 19.465 1 c
+19.513 0.941 19.538 0.871 19.538 0.794 c
+21.265 -3.042 m
+20.891 -3.042 20.608 -2.936 20.413 -2.719 c
+20.215 -2.495 20.12 -2.167 20.12 -1.735 c
+20.12 -1.367 l
+20.12 -0.926 20.211 -0.58 20.398 -0.324 c
+20.593 -0.07 20.868 0.059 21.221 0.059 c
+21.564 0.059 21.817 -0.055 21.986 -0.279 c
+22.162 -0.507 22.254 -0.852 22.265 -1.323 c
+22.265 -1.631 l
+20.604 -1.631 l
+20.604 -1.705 l
+20.604 -2.028 20.663 -2.263 20.781 -2.41 c
+20.899 -2.55 21.067 -2.616 21.296 -2.616 c
+21.442 -2.616 21.567 -2.595 21.677 -2.543 c
+21.784 -2.484 21.886 -2.396 21.986 -2.278 c
+22.236 -2.587 l
+22.03 -2.892 21.707 -3.042 21.265 -3.042 c
+21.221 -0.367 m
+21.016 -0.367 20.862 -0.437 20.766 -0.573 c
+20.667 -0.713 20.612 -0.926 20.604 -1.22 c
+21.78 -1.22 l
+21.78 -1.147 l
+21.758 -0.874 21.707 -0.676 21.618 -0.559 c
+21.531 -0.434 21.398 -0.367 21.221 -0.367 c
+23.706 -2.616 m
+23.871 -2.616 24.003 -2.568 24.103 -2.469 c
+24.198 -2.373 24.253 -2.23 24.264 -2.043 c
+24.72 -2.043 l
+24.709 -2.329 24.606 -2.568 24.411 -2.763 c
+24.224 -2.95 23.989 -3.042 23.706 -3.042 c
+23.342 -3.042 23.062 -2.925 22.867 -2.69 c
+22.669 -2.454 22.574 -2.109 22.574 -1.646 c
+22.574 -1.323 l
+22.574 -0.874 22.666 -0.529 22.853 -0.294 c
+23.048 -0.058 23.331 0.059 23.706 0.059 c
+24.007 0.059 24.249 -0.04 24.426 -0.235 c
+24.61 -0.434 24.709 -0.698 24.72 -1.029 c
+24.264 -1.029 l
+24.242 -0.804 24.184 -0.639 24.088 -0.529 c
+24 -0.422 23.871 -0.367 23.706 -0.367 c
+23.489 -0.367 23.327 -0.441 23.22 -0.588 c
+23.121 -0.727 23.066 -0.955 23.059 -1.278 c
+23.059 -1.66 l
+23.059 -2.013 23.106 -2.263 23.206 -2.41 c
+23.312 -2.55 23.478 -2.616 23.706 -2.616 c
+25.745 0.721 m
+25.745 0 l
+26.201 0 l
+26.201 -0.397 l
+25.745 -0.397 l
+25.745 -2.248 l
+25.745 -2.366 25.764 -2.454 25.804 -2.514 c
+25.841 -2.572 25.911 -2.601 26.01 -2.601 c
+26.069 -2.601 26.131 -2.595 26.201 -2.572 c
+26.201 -2.984 l
+26.083 -3.021 25.969 -3.042 25.863 -3.042 c
+25.664 -3.042 25.514 -2.977 25.408 -2.836 c
+25.308 -2.701 25.26 -2.506 25.26 -2.248 c
+25.26 -0.397 l
+24.804 -0.397 l
+24.804 0 l
+25.26 0 l
+25.26 0.721 l
+h
+26.697 -2.719 m
+26.697 -2.631 26.719 -2.558 26.771 -2.499 c
+26.829 -2.44 26.906 -2.41 27.006 -2.41 c
+27.112 -2.41 27.189 -2.44 27.241 -2.499 c
+27.299 -2.558 27.329 -2.631 27.329 -2.719 c
+27.329 -2.8 27.299 -2.866 27.241 -2.925 c
+27.189 -2.984 27.112 -3.013 27.006 -3.013 c
+26.906 -3.013 26.829 -2.984 26.771 -2.925 c
+26.719 -2.866 26.697 -2.8 26.697 -2.719 c
+f
+Q
+0.567 w 1 j 1 J
+q 1 0 0 1 522.4669 140.1895 cm
+0 0 m
+6.928 0.754 l
+S
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 527.2745 140.7116 cm
+0 0 m
+-0.949 -1.176 l
+2.649 0.291 l
+-1.177 0.948 l
+h
+f*
+Q
+0.906 0.785 0.617 0.969 K
+0.283 w 0 j 0 J
+q 1 0 0 1 527.2745 140.7116 cm
+0 0 m
+-0.949 -1.176 l
+2.649 0.291 l
+-1.177 0.948 l
+0 0 l
+h
+S
+Q
+ endstream endobj 21 0 obj <</I true/K false/S/Transparency/Type/Group>> endobj 28 0 obj <</BBox[198.027 568.502 444.988 526.485]/Group 63 0 R/Length 127/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>/Shading<</Sh0 64 0 R>>>>/Subtype/Form>>stream
+q
+198.027 568.502 246.962 -42.017 re
+W n
+q
+0 g
+/GS0 gs
+246.9619598 0 0 246.9619598 198.026535 547.4932861 cm
+BX /Sh0 sh EX Q
+Q
+ endstream endobj 29 0 obj <</BBox[589.44 293.423 621.568 278.07]/Group 65 0 R/Length 293/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 592.6808 293.4232 cm
+0 0 m
+25.646 0 l
+27.443 0 28.887 -1.448 28.887 -3.241 c
+28.887 -12.112 l
+28.887 -13.909 27.443 -15.353 25.646 -15.353 c
+0 -15.353 l
+-1.793 -15.353 -3.241 -13.909 -3.241 -12.112 c
+-3.241 -3.241 l
+-3.241 -1.448 -1.793 0 0 0 c
+f
+Q
+ endstream endobj 30 0 obj <</BBox[738.029 266.921 763.227 262.629]/Group 66 0 R/Length 5754/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 740.4832 263.5406 cm
+0 0 m
+0.456 2.131 l
+0.941 2.131 l
+0.206 -0.853 l
+-0.176 -0.853 l
+-0.764 1.278 l
+-1.337 -0.853 l
+-1.72 -0.853 l
+-2.454 2.131 l
+-1.969 2.131 l
+-1.499 0.058 l
+-0.955 2.131 l
+-0.573 2.131 l
+h
+1.69 1.822 m
+1.875 2.065 2.11 2.19 2.396 2.19 c
+2.926 2.19 3.194 1.837 3.205 1.132 c
+3.205 -0.853 l
+2.72 -0.853 l
+2.72 1.103 l
+2.72 1.338 2.679 1.502 2.602 1.602 c
+2.521 1.697 2.404 1.749 2.249 1.749 c
+2.132 1.749 2.022 1.708 1.926 1.631 c
+1.827 1.55 1.75 1.444 1.69 1.308 c
+1.69 -0.853 l
+1.206 -0.853 l
+1.206 3.38 l
+1.69 3.38 l
+h
+5.278 -0.853 m
+5.248 -0.786 5.226 -0.676 5.218 -0.53 c
+5.042 -0.786 4.822 -0.912 4.557 -0.912 c
+4.281 -0.912 4.065 -0.838 3.911 -0.691 c
+3.763 -0.537 3.69 -0.32 3.69 -0.044 c
+3.69 0.257 3.793 0.5 3.998 0.675 c
+4.204 0.86 4.487 0.955 4.851 0.955 c
+5.204 0.955 l
+5.204 1.278 l
+5.204 1.455 5.164 1.577 5.087 1.646 c
+5.006 1.723 4.888 1.764 4.734 1.764 c
+4.587 1.764 4.462 1.72 4.366 1.631 c
+4.278 1.543 4.233 1.433 4.233 1.308 c
+3.749 1.308 l
+3.749 1.455 3.793 1.595 3.881 1.734 c
+3.969 1.881 4.087 1.992 4.233 2.072 c
+4.388 2.15 4.561 2.19 4.748 2.19 c
+5.06 2.19 5.295 2.109 5.453 1.955 c
+5.608 1.808 5.689 1.587 5.689 1.294 c
+5.689 -0.206 l
+5.696 -0.441 5.733 -0.643 5.792 -0.809 c
+5.792 -0.853 l
+h
+4.63 -0.47 m
+4.748 -0.47 4.859 -0.437 4.969 -0.368 c
+5.075 -0.302 5.152 -0.217 5.204 -0.118 c
+5.204 0.588 l
+4.939 0.588 l
+4.704 0.588 4.516 0.536 4.381 0.44 c
+4.252 0.341 4.19 0.198 4.19 0.014 c
+4.19 -0.154 4.219 -0.276 4.278 -0.353 c
+4.344 -0.434 4.462 -0.47 4.63 -0.47 c
+6.901 2.851 m
+6.901 2.131 l
+7.357 2.131 l
+7.357 1.734 l
+6.901 1.734 l
+6.901 -0.118 l
+6.901 -0.235 6.92 -0.324 6.961 -0.383 c
+6.997 -0.441 7.067 -0.47 7.167 -0.47 c
+7.225 -0.47 7.288 -0.463 7.357 -0.441 c
+7.357 -0.853 l
+7.24 -0.89 7.126 -0.912 7.019 -0.912 c
+6.82 -0.912 6.67 -0.846 6.564 -0.706 c
+6.465 -0.57 6.417 -0.375 6.417 -0.118 c
+6.417 1.734 l
+5.961 1.734 l
+5.961 2.131 l
+6.417 2.131 l
+6.417 2.851 l
+h
+9.658 2.851 m
+9.658 2.131 l
+10.113 2.131 l
+10.113 1.734 l
+9.658 1.734 l
+9.658 -0.118 l
+9.658 -0.235 9.676 -0.324 9.717 -0.383 c
+9.754 -0.441 9.823 -0.47 9.922 -0.47 c
+9.981 -0.47 10.043 -0.463 10.113 -0.441 c
+10.113 -0.853 l
+9.996 -0.89 9.881 -0.912 9.775 -0.912 c
+9.577 -0.912 9.426 -0.846 9.32 -0.706 c
+9.22 -0.57 9.172 -0.375 9.172 -0.118 c
+9.172 1.734 l
+8.717 1.734 l
+8.717 2.131 l
+9.172 2.131 l
+9.172 2.851 l
+h
+10.426 0.779 m
+10.426 1.209 10.529 1.55 10.735 1.808 c
+10.947 2.061 11.227 2.19 11.572 2.19 c
+11.914 2.19 12.19 2.061 12.395 1.808 c
+12.609 1.562 12.723 1.227 12.734 0.808 c
+12.734 0.5 l
+12.734 0.066 12.623 -0.276 12.41 -0.53 c
+12.204 -0.786 11.925 -0.912 11.572 -0.912 c
+11.227 -0.912 10.955 -0.79 10.749 -0.544 c
+10.544 -0.302 10.433 0.033 10.426 0.455 c
+h
+10.911 0.5 m
+10.911 0.183 10.97 -0.059 11.088 -0.235 c
+11.212 -0.405 11.373 -0.485 11.572 -0.485 c
+12.002 -0.485 12.227 -0.177 12.248 0.44 c
+12.248 0.779 l
+12.248 1.08 12.183 1.323 12.057 1.499 c
+11.94 1.675 11.778 1.764 11.572 1.764 c
+11.373 1.764 11.212 1.675 11.088 1.499 c
+10.97 1.323 10.911 1.08 10.911 0.779 c
+h
+15.905 -0.853 m
+15.876 -0.786 15.853 -0.676 15.846 -0.53 c
+15.67 -0.786 15.449 -0.912 15.184 -0.912 c
+14.909 -0.912 14.692 -0.838 14.538 -0.691 c
+14.39 -0.537 14.317 -0.32 14.317 -0.044 c
+14.317 0.257 14.421 0.5 14.626 0.675 c
+14.832 0.86 15.115 0.955 15.479 0.955 c
+15.832 0.955 l
+15.832 1.278 l
+15.832 1.455 15.791 1.577 15.714 1.646 c
+15.633 1.723 15.516 1.764 15.361 1.764 c
+15.214 1.764 15.089 1.72 14.993 1.631 c
+14.905 1.543 14.861 1.433 14.861 1.308 c
+14.376 1.308 l
+14.376 1.455 14.421 1.595 14.508 1.734 c
+14.596 1.881 14.714 1.992 14.861 2.072 c
+15.015 2.15 15.188 2.19 15.375 2.19 c
+15.688 2.19 15.923 2.109 16.081 1.955 c
+16.235 1.808 16.316 1.587 16.316 1.294 c
+16.316 -0.206 l
+16.324 -0.441 16.36 -0.643 16.42 -0.809 c
+16.42 -0.853 l
+h
+15.258 -0.47 m
+15.375 -0.47 15.485 -0.437 15.596 -0.368 c
+15.703 -0.302 15.78 -0.217 15.832 -0.118 c
+15.832 0.588 l
+15.566 0.588 l
+15.331 0.588 15.144 0.536 15.009 0.44 c
+14.88 0.341 14.817 0.198 14.817 0.014 c
+14.817 -0.154 14.847 -0.276 14.905 -0.353 c
+14.972 -0.434 15.089 -0.47 15.258 -0.47 c
+16.794 0.779 m
+16.794 1.238 16.875 1.587 17.044 1.822 c
+17.22 2.065 17.47 2.19 17.794 2.19 c
+18.076 2.19 18.297 2.072 18.455 1.837 c
+18.455 3.38 l
+18.94 3.38 l
+18.94 -0.853 l
+18.5 -0.853 l
+18.47 -0.53 l
+18.311 -0.786 18.088 -0.912 17.794 -0.912 c
+17.478 -0.912 17.235 -0.794 17.058 -0.559 c
+16.882 -0.316 16.794 0.029 16.794 0.47 c
+h
+17.279 0.5 m
+17.279 0.166 17.326 -0.081 17.426 -0.235 c
+17.522 -0.393 17.683 -0.47 17.912 -0.47 c
+18.153 -0.47 18.338 -0.353 18.455 -0.118 c
+18.455 1.396 l
+18.327 1.631 18.147 1.749 17.912 1.749 c
+17.683 1.749 17.522 1.668 17.426 1.514 c
+17.326 1.356 17.279 1.117 17.279 0.793 c
+h
+19.41 0.779 m
+19.41 1.238 19.491 1.587 19.66 1.822 c
+19.836 2.065 20.087 2.19 20.41 2.19 c
+20.693 2.19 20.914 2.072 21.072 1.837 c
+21.072 3.38 l
+21.557 3.38 l
+21.557 -0.853 l
+21.116 -0.853 l
+21.087 -0.53 l
+20.929 -0.786 20.704 -0.912 20.41 -0.912 c
+20.094 -0.912 19.851 -0.794 19.676 -0.559 c
+19.499 -0.316 19.41 0.029 19.41 0.47 c
+h
+19.896 0.5 m
+19.896 0.166 19.944 -0.081 20.042 -0.235 c
+20.138 -0.393 20.3 -0.47 20.528 -0.47 c
+20.771 -0.47 20.954 -0.353 21.072 -0.118 c
+21.072 1.396 l
+20.943 1.631 20.763 1.749 20.528 1.749 c
+20.3 1.749 20.138 1.668 20.042 1.514 c
+19.944 1.356 19.896 1.117 19.896 0.793 c
+h
+22.67 0.279 m
+22.229 0.279 l
+22.2 3.16 l
+22.714 3.16 l
+h
+22.464 -0.309 m
+22.56 -0.309 22.629 -0.339 22.67 -0.397 c
+22.718 -0.449 22.743 -0.515 22.743 -0.603 c
+22.743 -0.684 22.718 -0.75 22.67 -0.809 c
+22.629 -0.856 22.56 -0.882 22.464 -0.882 c
+22.376 -0.882 22.306 -0.856 22.259 -0.809 c
+22.207 -0.75 22.185 -0.684 22.185 -0.603 c
+22.185 -0.515 22.207 -0.449 22.259 -0.397 c
+22.306 -0.339 22.376 -0.309 22.464 -0.309 c
+f
+Q
+ endstream endobj 31 0 obj <</BBox[580.256 333.713 630.219 328.304]/Group 67 0 R/Length 12062/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 582.8727 330.7593 cm
+0 0 m
+-0.029 -0.434 -0.154 -0.765 -0.367 -1 c
+-0.584 -1.228 -0.893 -1.338 -1.294 -1.338 c
+-1.697 -1.338 -2.021 -1.187 -2.263 -0.882 c
+-2.499 -0.57 -2.616 -0.154 -2.616 0.367 c
+-2.616 1.103 l
+-2.616 1.621 -2.495 2.032 -2.248 2.337 c
+-2.006 2.639 -1.672 2.793 -1.249 2.793 c
+-0.866 2.793 -0.569 2.675 -0.353 2.44 c
+-0.139 2.205 -0.022 1.87 0 1.44 c
+-0.514 1.44 l
+-0.536 1.771 -0.602 2.007 -0.72 2.146 c
+-0.837 2.282 -1.014 2.352 -1.249 2.352 c
+-1.525 2.352 -1.734 2.242 -1.881 2.028 c
+-2.028 1.812 -2.102 1.5 -2.102 1.087 c
+-2.102 0.353 l
+-2.102 -0.052 -2.036 -0.36 -1.896 -0.574 c
+-1.76 -0.79 -1.558 -0.897 -1.294 -0.897 c
+-1.028 -0.897 -0.845 -0.834 -0.735 -0.706 c
+-0.617 -0.58 -0.544 -0.345 -0.514 0 c
+h
+0.882 1.396 m
+1.066 1.639 1.301 1.764 1.588 1.764 c
+2.117 1.764 2.386 1.411 2.396 0.706 c
+2.396 -1.279 l
+1.912 -1.279 l
+1.912 0.676 l
+1.912 0.912 1.871 1.076 1.794 1.176 c
+1.713 1.271 1.595 1.323 1.441 1.323 c
+1.324 1.323 1.213 1.282 1.118 1.205 c
+1.018 1.124 0.941 1.018 0.882 0.881 c
+0.882 -1.279 l
+0.397 -1.279 l
+0.397 2.954 l
+0.882 2.954 l
+h
+4.466 -1.279 m
+4.436 -1.213 4.414 -1.103 4.406 -0.956 c
+4.23 -1.213 4.009 -1.338 3.745 -1.338 c
+3.469 -1.338 3.252 -1.264 3.098 -1.118 c
+2.951 -0.963 2.878 -0.746 2.878 -0.47 c
+2.878 -0.169 2.98 0.073 3.186 0.249 c
+3.392 0.434 3.675 0.529 4.039 0.529 c
+4.391 0.529 l
+4.391 0.852 l
+4.391 1.029 4.351 1.151 4.274 1.22 c
+4.193 1.297 4.075 1.338 3.921 1.338 c
+3.774 1.338 3.649 1.294 3.554 1.205 c
+3.466 1.117 3.421 1.007 3.421 0.881 c
+2.937 0.881 l
+2.937 1.029 2.98 1.168 3.069 1.308 c
+3.157 1.455 3.275 1.565 3.421 1.646 c
+3.576 1.723 3.749 1.764 3.936 1.764 c
+4.248 1.764 4.484 1.683 4.642 1.529 c
+4.796 1.382 4.877 1.161 4.877 0.867 c
+4.877 -0.632 l
+4.884 -0.867 4.921 -1.07 4.979 -1.235 c
+4.979 -1.279 l
+h
+3.818 -0.897 m
+3.936 -0.897 4.046 -0.864 4.156 -0.794 c
+4.263 -0.728 4.341 -0.643 4.391 -0.544 c
+4.391 0.162 l
+4.127 0.162 l
+3.892 0.162 3.705 0.11 3.568 0.014 c
+3.44 -0.085 3.377 -0.228 3.377 -0.412 c
+3.377 -0.58 3.407 -0.702 3.466 -0.779 c
+3.531 -0.86 3.649 -0.897 3.818 -0.897 c
+5.913 1.705 m
+5.928 1.382 l
+6.112 1.635 6.354 1.764 6.648 1.764 c
+7.177 1.764 7.446 1.411 7.456 0.706 c
+7.456 -1.279 l
+6.972 -1.279 l
+6.972 0.676 l
+6.972 0.912 6.931 1.076 6.854 1.176 c
+6.773 1.271 6.656 1.323 6.501 1.323 c
+6.384 1.323 6.273 1.282 6.178 1.205 c
+6.078 1.124 6.001 1.018 5.943 0.881 c
+5.943 -1.279 l
+5.457 -1.279 l
+5.457 1.705 l
+h
+7.909 0.353 m
+7.909 0.812 7.99 1.161 8.158 1.396 c
+8.335 1.639 8.584 1.764 8.908 1.764 c
+9.209 1.764 9.441 1.631 9.599 1.367 c
+9.628 1.705 l
+10.07 1.705 l
+10.07 -1.309 l
+10.07 -1.679 9.97 -1.962 9.775 -2.161 c
+9.588 -2.356 9.327 -2.455 8.996 -2.455 c
+8.849 -2.455 8.68 -2.415 8.497 -2.338 c
+8.309 -2.267 8.173 -2.18 8.085 -2.072 c
+8.276 -1.735 l
+8.482 -1.941 8.706 -2.043 8.952 -2.043 c
+9.353 -2.043 9.559 -1.816 9.569 -1.367 c
+9.569 -0.985 l
+9.411 -1.22 9.191 -1.338 8.908 -1.338 c
+8.592 -1.338 8.349 -1.22 8.173 -0.985 c
+8.004 -0.75 7.916 -0.419 7.909 0.014 c
+h
+8.393 0.073 m
+8.393 -0.261 8.441 -0.507 8.541 -0.661 c
+8.636 -0.819 8.798 -0.897 9.025 -0.897 c
+9.268 -0.897 9.452 -0.775 9.569 -0.53 c
+9.569 0.955 l
+9.452 1.198 9.276 1.323 9.04 1.323 c
+8.813 1.323 8.651 1.242 8.555 1.087 c
+8.456 0.929 8.401 0.69 8.393 0.367 c
+h
+11.66 -1.338 m
+11.286 -1.338 11.003 -1.231 10.808 -1.014 c
+10.61 -0.79 10.514 -0.463 10.514 -0.029 c
+10.514 0.338 l
+10.514 0.779 10.606 1.124 10.793 1.382 c
+10.988 1.635 11.263 1.764 11.616 1.764 c
+11.958 1.764 12.212 1.65 12.381 1.425 c
+12.557 1.198 12.649 0.852 12.659 0.382 c
+12.659 0.073 l
+10.999 0.073 l
+10.999 0 l
+10.999 -0.324 11.057 -0.559 11.175 -0.706 c
+11.293 -0.846 11.462 -0.912 11.69 -0.912 c
+11.836 -0.912 11.961 -0.889 12.072 -0.838 c
+12.179 -0.779 12.281 -0.691 12.381 -0.574 c
+12.63 -0.882 l
+12.424 -1.187 12.102 -1.338 11.66 -1.338 c
+11.616 1.338 m
+11.41 1.338 11.256 1.268 11.161 1.132 c
+11.061 0.992 11.007 0.779 10.999 0.484 c
+12.175 0.484 l
+12.175 0.559 l
+12.152 0.831 12.102 1.029 12.013 1.147 c
+11.925 1.271 11.793 1.338 11.616 1.338 c
+14.512 -0.515 m
+14.512 -0.408 14.471 -0.32 14.394 -0.25 c
+14.313 -0.173 14.163 -0.085 13.939 0.014 c
+13.675 0.121 13.486 0.213 13.38 0.293 c
+13.27 0.371 13.193 0.459 13.145 0.559 c
+13.093 0.654 13.072 0.771 13.072 0.912 c
+13.072 1.153 13.16 1.356 13.336 1.514 c
+13.513 1.679 13.737 1.764 14.012 1.764 c
+14.306 1.764 14.541 1.675 14.718 1.5 c
+14.894 1.33 14.982 1.117 14.982 0.852 c
+14.498 0.852 l
+14.498 0.989 14.446 1.103 14.35 1.19 c
+14.263 1.286 14.148 1.338 14.012 1.338 c
+13.866 1.338 13.752 1.297 13.675 1.22 c
+13.594 1.151 13.557 1.051 13.557 0.926 c
+13.557 0.827 13.586 0.75 13.644 0.69 c
+13.704 0.632 13.843 0.551 14.071 0.455 c
+14.431 0.309 14.677 0.166 14.806 0.029 c
+14.942 -0.1 15.011 -0.272 15.011 -0.485 c
+15.011 -0.742 14.916 -0.948 14.733 -1.103 c
+14.556 -1.261 14.321 -1.338 14.027 -1.338 c
+13.711 -1.338 13.457 -1.249 13.263 -1.073 c
+13.075 -0.889 12.983 -0.658 12.983 -0.382 c
+13.469 -0.382 l
+13.476 -0.551 13.527 -0.684 13.615 -0.779 c
+13.711 -0.867 13.85 -0.912 14.027 -0.912 c
+14.182 -0.912 14.299 -0.879 14.38 -0.809 c
+14.468 -0.742 14.512 -0.643 14.512 -0.515 c
+17.702 -0.912 m
+17.867 -0.912 17.999 -0.864 18.099 -0.765 c
+18.194 -0.669 18.249 -0.526 18.261 -0.339 c
+18.716 -0.339 l
+18.705 -0.625 18.602 -0.864 18.407 -1.058 c
+18.22 -1.246 17.985 -1.338 17.702 -1.338 c
+17.338 -1.338 17.058 -1.22 16.864 -0.985 c
+16.665 -0.75 16.57 -0.405 16.57 0.058 c
+16.57 0.382 l
+16.57 0.831 16.661 1.176 16.85 1.411 c
+17.044 1.646 17.326 1.764 17.702 1.764 c
+18.003 1.764 18.246 1.664 18.422 1.469 c
+18.606 1.271 18.705 1.007 18.716 0.676 c
+18.261 0.676 l
+18.238 0.9 18.18 1.066 18.084 1.176 c
+17.995 1.282 17.867 1.338 17.702 1.338 c
+17.485 1.338 17.324 1.264 17.216 1.117 c
+17.118 0.977 17.062 0.75 17.055 0.426 c
+17.055 0.044 l
+17.055 -0.309 17.103 -0.559 17.202 -0.706 c
+17.309 -0.846 17.474 -0.912 17.702 -0.912 c
+19.009 0.353 m
+19.009 0.783 19.113 1.124 19.319 1.382 c
+19.532 1.635 19.811 1.764 20.156 1.764 c
+20.498 1.764 20.773 1.635 20.979 1.382 c
+21.193 1.135 21.307 0.801 21.318 0.382 c
+21.318 0.073 l
+21.318 -0.36 21.207 -0.702 20.995 -0.956 c
+20.788 -1.213 20.509 -1.338 20.156 -1.338 c
+19.811 -1.338 19.539 -1.216 19.333 -0.97 c
+19.127 -0.728 19.017 -0.393 19.009 0.029 c
+h
+19.495 0.073 m
+19.495 -0.243 19.554 -0.485 19.672 -0.661 c
+19.796 -0.831 19.958 -0.912 20.156 -0.912 c
+20.586 -0.912 20.811 -0.603 20.833 0.014 c
+20.833 0.353 l
+20.833 0.654 20.767 0.897 20.642 1.072 c
+20.524 1.249 20.362 1.338 20.156 1.338 c
+19.958 1.338 19.796 1.249 19.672 1.072 c
+19.554 0.897 19.495 0.654 19.495 0.353 c
+h
+22.215 1.705 m
+22.229 1.425 l
+22.406 1.65 22.645 1.764 22.949 1.764 c
+23.28 1.764 23.512 1.617 23.64 1.323 c
+23.824 1.617 24.085 1.764 24.419 1.764 c
+24.978 1.764 25.261 1.419 25.272 0.735 c
+25.272 -1.279 l
+24.787 -1.279 l
+24.787 0.69 l
+24.787 0.904 24.746 1.062 24.669 1.161 c
+24.588 1.268 24.456 1.323 24.272 1.323 c
+24.125 1.323 24.008 1.264 23.919 1.147 c
+23.831 1.036 23.776 0.897 23.757 0.72 c
+23.757 -1.279 l
+23.273 -1.279 l
+23.273 0.706 l
+23.262 1.117 23.089 1.323 22.758 1.323 c
+22.512 1.323 22.34 1.198 22.244 0.955 c
+22.244 -1.279 l
+21.758 -1.279 l
+21.758 1.705 l
+h
+26.176 1.705 m
+26.19 1.425 l
+26.367 1.65 26.606 1.764 26.911 1.764 c
+27.241 1.764 27.473 1.617 27.601 1.323 c
+27.786 1.617 28.046 1.764 28.38 1.764 c
+28.939 1.764 29.222 1.419 29.233 0.735 c
+29.233 -1.279 l
+28.748 -1.279 l
+28.748 0.69 l
+28.748 0.904 28.708 1.062 28.63 1.161 c
+28.549 1.268 28.417 1.323 28.233 1.323 c
+28.087 1.323 27.969 1.264 27.881 1.147 c
+27.792 1.036 27.738 0.897 27.719 0.72 c
+27.719 -1.279 l
+27.234 -1.279 l
+27.234 0.706 l
+27.223 1.117 27.05 1.323 26.72 1.323 c
+26.473 1.323 26.3 1.198 26.205 0.955 c
+26.205 -1.279 l
+25.72 -1.279 l
+25.72 1.705 l
+h
+30.211 -1.279 -0.5 2.984 re
+30.24 2.499 m
+30.24 2.41 30.215 2.337 30.167 2.278 c
+30.126 2.227 30.057 2.205 29.961 2.205 c
+29.872 2.205 29.803 2.227 29.755 2.278 c
+29.714 2.337 29.696 2.404 29.696 2.484 c
+29.696 2.572 29.714 2.645 29.755 2.705 c
+29.803 2.763 29.872 2.793 29.961 2.793 c
+30.057 2.793 30.126 2.763 30.167 2.705 c
+30.215 2.645 30.24 2.576 30.24 2.499 c
+31.515 2.425 m
+31.515 1.705 l
+31.971 1.705 l
+31.971 1.308 l
+31.515 1.308 l
+31.515 -0.544 l
+31.515 -0.661 31.534 -0.75 31.574 -0.809 c
+31.611 -0.867 31.68 -0.897 31.78 -0.897 c
+31.838 -0.897 31.901 -0.889 31.971 -0.867 c
+31.971 -1.279 l
+31.853 -1.316 31.74 -1.338 31.632 -1.338 c
+31.435 -1.338 31.283 -1.272 31.177 -1.132 c
+31.078 -0.996 31.03 -0.802 31.03 -0.544 c
+31.03 1.308 l
+30.574 1.308 l
+30.574 1.705 l
+31.03 1.705 l
+31.03 2.425 l
+h
+33.496 -1.338 m
+33.121 -1.338 32.838 -1.231 32.644 -1.014 c
+32.445 -0.79 32.349 -0.463 32.349 -0.029 c
+32.349 0.338 l
+32.349 0.779 32.441 1.124 32.629 1.382 c
+32.823 1.635 33.099 1.764 33.452 1.764 c
+33.793 1.764 34.047 1.65 34.217 1.425 c
+34.392 1.198 34.485 0.852 34.495 0.382 c
+34.495 0.073 l
+32.835 0.073 l
+32.835 0 l
+32.835 -0.324 32.893 -0.559 33.01 -0.706 c
+33.128 -0.846 33.297 -0.912 33.525 -0.912 c
+33.672 -0.912 33.797 -0.889 33.907 -0.838 c
+34.014 -0.779 34.117 -0.691 34.217 -0.574 c
+34.466 -0.882 l
+34.26 -1.187 33.937 -1.338 33.496 -1.338 c
+33.452 1.338 m
+33.246 1.338 33.091 1.268 32.996 1.132 c
+32.897 0.992 32.842 0.779 32.835 0.484 c
+34.011 0.484 l
+34.011 0.559 l
+33.988 0.831 33.937 1.029 33.849 1.147 c
+33.76 1.271 33.629 1.338 33.452 1.338 c
+34.805 0.353 m
+34.805 0.812 34.884 1.161 35.054 1.396 c
+35.231 1.639 35.48 1.764 35.804 1.764 c
+36.087 1.764 36.307 1.646 36.465 1.411 c
+36.465 2.954 l
+36.95 2.954 l
+36.95 -1.279 l
+36.509 -1.279 l
+36.48 -0.956 l
+36.322 -1.213 36.098 -1.338 35.804 -1.338 c
+35.487 -1.338 35.245 -1.22 35.069 -0.985 c
+34.892 -0.742 34.805 -0.397 34.805 0.044 c
+h
+35.289 0.073 m
+35.289 -0.261 35.337 -0.507 35.436 -0.661 c
+35.532 -0.819 35.694 -0.897 35.921 -0.897 c
+36.164 -0.897 36.347 -0.779 36.465 -0.544 c
+36.465 0.97 l
+36.336 1.205 36.156 1.323 35.921 1.323 c
+35.694 1.323 35.532 1.242 35.436 1.087 c
+35.337 0.929 35.289 0.69 35.289 0.367 c
+h
+39.21 1.396 m
+39.393 1.639 39.629 1.764 39.916 1.764 c
+40.445 1.764 40.713 1.411 40.724 0.706 c
+40.724 -1.279 l
+40.239 -1.279 l
+40.239 0.676 l
+40.239 0.912 40.199 1.076 40.122 1.176 c
+40.041 1.271 39.923 1.323 39.769 1.323 c
+39.651 1.323 39.541 1.282 39.445 1.205 c
+39.346 1.124 39.269 1.018 39.21 0.881 c
+39.21 -1.279 l
+38.726 -1.279 l
+38.726 2.954 l
+39.21 2.954 l
+h
+42.323 -1.338 m
+41.948 -1.338 41.664 -1.231 41.47 -1.014 c
+41.272 -0.79 41.176 -0.463 41.176 -0.029 c
+41.176 0.338 l
+41.176 0.779 41.268 1.124 41.456 1.382 c
+41.65 1.635 41.926 1.764 42.279 1.764 c
+42.62 1.764 42.874 1.65 43.043 1.425 c
+43.219 1.198 43.311 0.852 43.322 0.382 c
+43.322 0.073 l
+41.662 0.073 l
+41.662 0 l
+41.662 -0.324 41.72 -0.559 41.837 -0.706 c
+41.955 -0.846 42.124 -0.912 42.352 -0.912 c
+42.499 -0.912 42.624 -0.889 42.734 -0.838 c
+42.84 -0.779 42.944 -0.691 43.043 -0.574 c
+43.293 -0.882 l
+43.087 -1.187 42.763 -1.338 42.323 -1.338 c
+42.279 1.338 m
+42.073 1.338 41.918 1.268 41.822 1.132 c
+41.724 0.992 41.668 0.779 41.662 0.484 c
+42.838 0.484 l
+42.838 0.559 l
+42.815 0.831 42.763 1.029 42.676 1.147 c
+42.587 1.271 42.455 1.338 42.279 1.338 c
+44.968 1.249 m
+44.899 1.257 44.825 1.264 44.748 1.264 c
+44.49 1.264 44.314 1.124 44.218 0.852 c
+44.218 -1.279 l
+43.734 -1.279 l
+43.734 1.705 l
+44.204 1.705 l
+44.218 1.396 l
+44.344 1.639 44.527 1.764 44.762 1.764 c
+44.84 1.764 44.902 1.749 44.954 1.72 c
+h
+46.346 -1.338 m
+45.972 -1.338 45.689 -1.231 45.494 -1.014 c
+45.296 -0.79 45.2 -0.463 45.2 -0.029 c
+45.2 0.338 l
+45.2 0.779 45.292 1.124 45.479 1.382 c
+45.674 1.635 45.949 1.764 46.302 1.764 c
+46.644 1.764 46.898 1.65 47.067 1.425 c
+47.243 1.198 47.335 0.852 47.346 0.382 c
+47.346 0.073 l
+45.685 0.073 l
+45.685 0 l
+45.685 -0.324 45.743 -0.559 45.861 -0.706 c
+45.979 -0.846 46.148 -0.912 46.376 -0.912 c
+46.523 -0.912 46.647 -0.889 46.758 -0.838 c
+46.865 -0.779 46.967 -0.691 47.067 -0.574 c
+47.316 -0.882 l
+47.111 -1.187 46.788 -1.338 46.346 -1.338 c
+46.302 1.338 m
+46.096 1.338 45.942 1.268 45.847 1.132 c
+45.747 0.992 45.693 0.779 45.685 0.484 c
+46.861 0.484 l
+46.861 0.559 l
+46.838 0.831 46.788 1.029 46.699 1.147 c
+46.611 1.271 46.479 1.338 46.302 1.338 c
+f
+Q
+ endstream endobj 32 0 obj <</BBox[592.773 327.187 617.18 322.836]/Group 68 0 R/Length 5562/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 595.2274 323.7479 cm
+0 0 m
+0.456 2.131 l
+0.941 2.131 l
+0.206 -0.853 l
+-0.176 -0.853 l
+-0.764 1.278 l
+-1.338 -0.853 l
+-1.72 -0.853 l
+-2.454 2.131 l
+-1.97 2.131 l
+-1.5 0.058 l
+-0.955 2.131 l
+-0.573 2.131 l
+h
+1.735 -0.853 -0.5 2.984 re
+1.764 2.925 m
+1.764 2.836 1.738 2.763 1.69 2.705 c
+1.65 2.653 1.58 2.631 1.484 2.631 c
+1.397 2.631 1.326 2.653 1.279 2.705 c
+1.239 2.763 1.22 2.83 1.22 2.91 c
+1.22 2.998 1.239 3.072 1.279 3.131 c
+1.326 3.189 1.397 3.219 1.484 3.219 c
+1.58 3.219 1.65 3.189 1.69 3.131 c
+1.738 3.072 1.764 3.002 1.764 2.925 c
+2.94 -0.853 -0.5 4.233 re
+4.145 -0.853 -0.5 4.233 re
+8.183 0.5 m
+8.183 0.029 8.1 -0.324 7.934 -0.559 c
+7.765 -0.794 7.522 -0.912 7.199 -0.912 c
+6.883 -0.912 6.648 -0.775 6.494 -0.5 c
+6.464 -0.853 l
+6.024 -0.853 l
+6.024 3.38 l
+6.508 3.38 l
+6.508 1.808 l
+6.662 2.061 6.894 2.19 7.199 2.19 c
+7.522 2.19 7.765 2.072 7.934 1.837 c
+8.1 1.602 8.183 1.253 8.183 0.793 c
+h
+7.699 0.779 m
+7.699 1.132 7.647 1.381 7.552 1.529 c
+7.453 1.675 7.291 1.749 7.067 1.749 c
+6.82 1.749 6.633 1.61 6.508 1.338 c
+6.508 -0.073 l
+6.626 -0.339 6.817 -0.47 7.082 -0.47 c
+7.294 -0.47 7.453 -0.397 7.552 -0.25 c
+7.647 -0.096 7.699 0.147 7.699 0.47 c
+h
+9.683 -0.912 m
+9.309 -0.912 9.025 -0.805 8.831 -0.588 c
+8.632 -0.364 8.536 -0.037 8.536 0.397 c
+8.536 0.764 l
+8.536 1.205 8.628 1.55 8.816 1.808 c
+9.01 2.061 9.286 2.19 9.639 2.19 c
+9.981 2.19 10.234 2.076 10.404 1.851 c
+10.58 1.624 10.672 1.278 10.683 0.808 c
+10.683 0.5 l
+9.022 0.5 l
+9.022 0.426 l
+9.022 0.103 9.081 -0.133 9.199 -0.279 c
+9.316 -0.42 9.484 -0.485 9.712 -0.485 c
+9.86 -0.485 9.985 -0.463 10.095 -0.412 c
+10.201 -0.353 10.304 -0.265 10.404 -0.148 c
+10.653 -0.456 l
+10.448 -0.761 10.124 -0.912 9.683 -0.912 c
+9.639 1.764 m
+9.434 1.764 9.279 1.694 9.183 1.558 c
+9.084 1.419 9.029 1.205 9.022 0.911 c
+10.198 0.911 l
+10.198 0.985 l
+10.176 1.257 10.124 1.455 10.036 1.573 c
+9.947 1.697 9.816 1.764 9.639 1.764 c
+13.74 -0.088 m
+13.74 0.018 13.7 0.106 13.623 0.176 c
+13.542 0.253 13.391 0.342 13.167 0.44 c
+12.902 0.548 12.715 0.639 12.609 0.72 c
+12.498 0.797 12.421 0.885 12.374 0.985 c
+12.322 1.08 12.299 1.198 12.299 1.338 c
+12.299 1.579 12.388 1.782 12.565 1.94 c
+12.74 2.105 12.965 2.19 13.24 2.19 c
+13.534 2.19 13.77 2.102 13.945 1.926 c
+14.122 1.756 14.211 1.543 14.211 1.278 c
+13.725 1.278 l
+13.725 1.415 13.674 1.529 13.579 1.616 c
+13.49 1.712 13.376 1.764 13.24 1.764 c
+13.093 1.764 12.979 1.723 12.902 1.646 c
+12.821 1.577 12.785 1.477 12.785 1.352 c
+12.785 1.253 12.814 1.176 12.873 1.117 c
+12.931 1.058 13.072 0.977 13.299 0.881 c
+13.66 0.735 13.906 0.592 14.034 0.455 c
+14.17 0.326 14.24 0.154 14.24 -0.059 c
+14.24 -0.316 14.144 -0.522 13.961 -0.676 c
+13.785 -0.834 13.549 -0.912 13.255 -0.912 c
+12.939 -0.912 12.686 -0.823 12.491 -0.647 c
+12.303 -0.463 12.212 -0.231 12.212 0.044 c
+12.696 0.044 l
+12.704 -0.125 12.755 -0.258 12.844 -0.353 c
+12.939 -0.441 13.079 -0.485 13.255 -0.485 c
+13.409 -0.485 13.527 -0.452 13.608 -0.383 c
+13.696 -0.316 13.74 -0.217 13.74 -0.088 c
+16.224 -0.853 m
+16.195 -0.786 16.173 -0.676 16.166 -0.53 c
+15.989 -0.786 15.769 -0.912 15.504 -0.912 c
+15.229 -0.912 15.011 -0.838 14.857 -0.691 c
+14.71 -0.537 14.637 -0.32 14.637 -0.044 c
+14.637 0.257 14.739 0.5 14.945 0.675 c
+15.152 0.86 15.434 0.955 15.798 0.955 c
+16.151 0.955 l
+16.151 1.278 l
+16.151 1.455 16.11 1.577 16.033 1.646 c
+15.952 1.723 15.834 1.764 15.68 1.764 c
+15.534 1.764 15.408 1.72 15.313 1.631 c
+15.225 1.543 15.181 1.433 15.181 1.308 c
+14.695 1.308 l
+14.695 1.455 14.739 1.595 14.828 1.734 c
+14.916 1.882 15.034 1.992 15.181 2.072 c
+15.335 2.15 15.508 2.19 15.695 2.19 c
+16.008 2.19 16.243 2.109 16.401 1.955 c
+16.555 1.808 16.636 1.587 16.636 1.294 c
+16.636 -0.206 l
+16.644 -0.441 16.68 -0.643 16.739 -0.809 c
+16.739 -0.853 l
+h
+15.578 -0.47 m
+15.695 -0.47 15.805 -0.437 15.915 -0.368 c
+16.023 -0.302 16.1 -0.217 16.151 -0.118 c
+16.151 0.588 l
+15.886 0.588 l
+15.651 0.588 15.464 0.536 15.327 0.44 c
+15.199 0.342 15.136 0.199 15.136 0.014 c
+15.136 -0.154 15.166 -0.276 15.225 -0.353 c
+15.291 -0.434 15.408 -0.47 15.578 -0.47 c
+17.422 -0.853 m
+17.422 1.734 l
+17.041 1.734 l
+17.041 2.131 l
+17.422 2.131 l
+17.422 2.469 l
+17.43 2.771 17.511 3.006 17.658 3.175 c
+17.804 3.351 18.014 3.439 18.29 3.439 c
+18.385 3.439 18.485 3.424 18.583 3.395 c
+18.554 2.984 l
+18.485 2.991 18.411 2.998 18.334 2.998 c
+18.058 2.998 17.922 2.8 17.922 2.41 c
+17.922 2.131 l
+18.422 2.131 l
+18.422 1.734 l
+17.922 1.734 l
+17.922 -0.853 l
+h
+19.881 -0.912 m
+19.506 -0.912 19.223 -0.805 19.028 -0.588 c
+18.83 -0.364 18.734 -0.037 18.734 0.397 c
+18.734 0.764 l
+18.734 1.205 18.826 1.55 19.013 1.808 c
+19.208 2.061 19.484 2.19 19.836 2.19 c
+20.178 2.19 20.432 2.076 20.601 1.851 c
+20.777 1.624 20.869 1.278 20.881 0.808 c
+20.881 0.5 l
+19.219 0.5 l
+19.219 0.426 l
+19.219 0.103 19.278 -0.133 19.395 -0.279 c
+19.513 -0.42 19.682 -0.485 19.91 -0.485 c
+20.057 -0.485 20.182 -0.463 20.293 -0.412 c
+20.399 -0.353 20.501 -0.265 20.601 -0.148 c
+20.851 -0.456 l
+20.645 -0.761 20.322 -0.912 19.881 -0.912 c
+19.836 1.764 m
+19.63 1.764 19.476 1.694 19.381 1.558 c
+19.281 1.419 19.227 1.205 19.219 0.911 c
+20.395 0.911 l
+20.395 0.985 l
+20.373 1.257 20.322 1.455 20.233 1.573 c
+20.145 1.697 20.013 1.764 19.836 1.764 c
+21.321 -0.588 m
+21.321 -0.5 21.343 -0.426 21.394 -0.368 c
+21.454 -0.309 21.531 -0.279 21.629 -0.279 c
+21.737 -0.279 21.814 -0.309 21.865 -0.368 c
+21.924 -0.426 21.953 -0.5 21.953 -0.588 c
+21.953 -0.669 21.924 -0.736 21.865 -0.794 c
+21.814 -0.853 21.737 -0.882 21.629 -0.882 c
+21.531 -0.882 21.454 -0.853 21.394 -0.794 c
+21.343 -0.736 21.321 -0.669 21.321 -0.588 c
+f
+Q
+ endstream endobj 33 0 obj <</BBox[579.503 320.602 630.821 315.104]/Group 69 0 R/Length 14034/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+580.003 316.31 -0.5 4.013 re
+580.94 316.31 m
+580.94 318.897 l
+580.557 318.897 l
+580.557 319.294 l
+580.94 319.294 l
+580.94 319.632 l
+580.948 319.933 581.028 320.168 581.176 320.338 c
+581.322 320.514 581.532 320.602 581.808 320.602 c
+581.903 320.602 582.003 320.587 582.101 320.558 c
+582.072 320.147 l
+582.003 320.153 581.929 320.161 581.852 320.161 c
+581.576 320.161 581.44 319.962 581.44 319.573 c
+581.44 319.294 l
+581.939 319.294 l
+581.939 318.897 l
+581.44 318.897 l
+581.44 316.31 l
+h
+584.413 317.119 m
+584.956 319.294 l
+585.471 319.294 l
+584.501 315.883 l
+584.431 315.63 584.328 315.439 584.193 315.31 c
+584.052 315.174 583.902 315.104 583.736 315.104 c
+583.667 315.104 583.582 315.119 583.487 315.149 c
+583.487 315.561 l
+583.589 315.546 l
+583.726 315.546 583.832 315.582 583.913 315.648 c
+584.002 315.719 584.067 315.836 584.119 316.001 c
+584.207 316.34 l
+583.34 319.294 l
+583.869 319.294 l
+h
+585.652 317.942 m
+585.652 318.372 585.754 318.713 585.96 318.971 c
+586.173 319.224 586.452 319.353 586.797 319.353 c
+587.14 319.353 587.415 319.224 587.621 318.971 c
+587.834 318.724 587.948 318.389 587.959 317.971 c
+587.959 317.662 l
+587.959 317.229 587.848 316.887 587.636 316.633 c
+587.43 316.376 587.15 316.251 586.797 316.251 c
+586.452 316.251 586.18 316.373 585.974 316.619 c
+585.769 316.861 585.658 317.196 585.652 317.618 c
+h
+586.136 317.662 m
+586.136 317.346 586.195 317.103 586.313 316.928 c
+586.437 316.758 586.599 316.677 586.797 316.677 c
+587.227 316.677 587.452 316.986 587.474 317.603 c
+587.474 317.942 l
+587.474 318.243 587.408 318.485 587.283 318.661 c
+587.165 318.838 587.003 318.927 586.797 318.927 c
+586.599 318.927 586.437 318.838 586.313 318.661 c
+586.195 318.485 586.136 318.243 586.136 317.942 c
+h
+589.914 316.575 m
+589.756 316.357 589.521 316.251 589.209 316.251 c
+588.944 316.251 588.738 316.343 588.591 316.531 c
+588.451 316.714 588.385 316.99 588.385 317.354 c
+588.385 319.294 l
+588.87 319.294 l
+588.87 317.383 l
+588.87 316.92 589.01 316.692 589.297 316.692 c
+589.59 316.692 589.789 316.824 589.899 317.089 c
+589.899 319.294 l
+590.399 319.294 l
+590.399 316.31 l
+589.928 316.31 l
+h
+593.685 316.31 m
+593.654 316.376 593.633 316.486 593.625 316.633 c
+593.449 316.376 593.228 316.251 592.964 316.251 c
+592.688 316.251 592.472 316.325 592.317 316.471 c
+592.17 316.626 592.097 316.843 592.097 317.119 c
+592.097 317.42 592.2 317.662 592.405 317.838 c
+592.611 318.023 592.894 318.118 593.258 318.118 c
+593.611 318.118 l
+593.611 318.441 l
+593.611 318.618 593.571 318.739 593.493 318.809 c
+593.413 318.886 593.295 318.927 593.141 318.927 c
+592.993 318.927 592.868 318.882 592.773 318.794 c
+592.685 318.706 592.64 318.596 592.64 318.47 c
+592.156 318.47 l
+592.156 318.618 592.2 318.757 592.288 318.897 c
+592.376 319.044 592.494 319.154 592.64 319.235 c
+592.795 319.312 592.968 319.353 593.155 319.353 c
+593.467 319.353 593.702 319.272 593.86 319.118 c
+594.015 318.971 594.096 318.75 594.096 318.456 c
+594.096 316.957 l
+594.103 316.722 594.14 316.519 594.198 316.354 c
+594.198 316.31 l
+h
+593.037 316.692 m
+593.155 316.692 593.265 316.725 593.376 316.795 c
+593.482 316.861 593.559 316.945 593.611 317.045 c
+593.611 317.751 l
+593.346 317.751 l
+593.111 317.751 592.923 317.699 592.787 317.603 c
+592.659 317.504 592.596 317.361 592.596 317.177 c
+592.596 317.008 592.626 316.887 592.685 316.81 c
+592.75 316.729 592.868 316.692 593.037 316.692 c
+595.911 318.838 m
+595.842 318.846 595.767 318.853 595.69 318.853 c
+595.433 318.853 595.257 318.713 595.161 318.441 c
+595.161 316.31 l
+594.676 316.31 l
+594.676 319.294 l
+595.146 319.294 l
+595.161 318.985 l
+595.287 319.228 595.47 319.353 595.705 319.353 c
+595.782 319.353 595.845 319.338 595.896 319.309 c
+h
+597.286 316.251 m
+596.91 316.251 596.628 316.357 596.433 316.575 c
+596.235 316.799 596.139 317.126 596.139 317.56 c
+596.139 317.927 l
+596.139 318.368 596.231 318.713 596.418 318.971 c
+596.613 319.224 596.889 319.353 597.242 319.353 c
+597.583 319.353 597.837 319.239 598.005 319.014 c
+598.182 318.786 598.274 318.441 598.285 317.971 c
+598.285 317.662 l
+596.624 317.662 l
+596.624 317.589 l
+596.624 317.265 596.683 317.03 596.8 316.883 c
+596.918 316.743 597.087 316.677 597.315 316.677 c
+597.462 316.677 597.587 316.7 597.697 316.751 c
+597.804 316.81 597.907 316.897 598.005 317.015 c
+598.256 316.706 l
+598.05 316.402 597.726 316.251 597.286 316.251 c
+597.242 318.927 m
+597.035 318.927 596.881 318.857 596.785 318.721 c
+596.686 318.581 596.631 318.368 596.624 318.073 c
+597.8 318.073 l
+597.8 318.148 l
+597.778 318.42 597.726 318.618 597.638 318.736 c
+597.55 318.86 597.417 318.927 597.242 318.927 c
+599.799 317.942 m
+599.799 318.401 599.88 318.75 600.049 318.985 c
+600.226 319.228 600.475 319.353 600.798 319.353 c
+601.082 319.353 601.302 319.235 601.46 319 c
+601.46 320.543 l
+601.945 320.543 l
+601.945 316.31 l
+601.504 316.31 l
+601.475 316.633 l
+601.317 316.376 601.093 316.251 600.798 316.251 c
+600.482 316.251 600.24 316.369 600.064 316.604 c
+599.887 316.847 599.799 317.192 599.799 317.633 c
+h
+600.284 317.662 m
+600.284 317.328 600.332 317.082 600.432 316.928 c
+600.527 316.77 600.688 316.692 600.916 316.692 c
+601.159 316.692 601.342 316.81 601.46 317.045 c
+601.46 318.559 l
+601.332 318.794 601.151 318.912 600.916 318.912 c
+600.688 318.912 600.527 318.831 600.432 318.676 c
+600.332 318.518 600.284 318.279 600.284 317.956 c
+h
+602.412 317.942 m
+602.412 318.372 602.514 318.713 602.72 318.971 c
+602.934 319.224 603.213 319.353 603.559 319.353 c
+603.9 319.353 604.176 319.224 604.382 318.971 c
+604.594 318.724 604.708 318.389 604.719 317.971 c
+604.719 317.662 l
+604.719 317.229 604.609 316.887 604.396 316.633 c
+604.191 316.376 603.911 316.251 603.559 316.251 c
+603.213 316.251 602.94 316.373 602.735 316.619 c
+602.529 316.861 602.419 317.196 602.412 317.618 c
+h
+602.897 317.662 m
+602.897 317.346 602.956 317.103 603.073 316.928 c
+603.198 316.758 603.36 316.677 603.559 316.677 c
+603.988 316.677 604.212 316.986 604.234 317.603 c
+604.234 317.942 l
+604.234 318.243 604.168 318.485 604.043 318.661 c
+603.925 318.838 603.764 318.927 603.559 318.927 c
+603.36 318.927 603.198 318.838 603.073 318.661 c
+602.956 318.485 602.897 318.243 602.897 317.942 c
+h
+605.689 316.31 -0.5 2.984 re
+605.719 320.088 m
+605.719 319.999 605.693 319.926 605.645 319.867 c
+605.606 319.816 605.535 319.794 605.44 319.794 c
+605.352 319.794 605.282 319.816 605.234 319.867 c
+605.194 319.926 605.175 319.992 605.175 320.072 c
+605.175 320.161 605.194 320.234 605.234 320.294 c
+605.282 320.352 605.352 320.382 605.44 320.382 c
+605.535 320.382 605.606 320.352 605.645 320.294 c
+605.693 320.234 605.719 320.165 605.719 320.088 c
+606.818 319.294 m
+606.832 318.971 l
+607.017 319.224 607.258 319.353 607.553 319.353 c
+608.082 319.353 608.35 319 608.361 318.294 c
+608.361 316.31 l
+607.877 316.31 l
+607.877 318.265 l
+607.877 318.5 607.836 318.665 607.759 318.765 c
+607.678 318.86 607.56 318.912 607.406 318.912 c
+607.289 318.912 607.178 318.871 607.083 318.794 c
+606.983 318.713 606.906 318.607 606.847 318.47 c
+606.847 316.31 l
+606.362 316.31 l
+606.362 319.294 l
+h
+608.814 317.942 m
+608.814 318.401 608.894 318.75 609.063 318.985 c
+609.24 319.228 609.489 319.353 609.813 319.353 c
+610.114 319.353 610.346 319.22 610.504 318.956 c
+610.533 319.294 l
+610.974 319.294 l
+610.974 316.28 l
+610.974 315.91 610.875 315.627 610.68 315.428 c
+610.493 315.233 610.231 315.134 609.901 315.134 c
+609.754 315.134 609.585 315.174 609.402 315.251 c
+609.213 315.322 609.078 315.409 608.989 315.516 c
+609.18 315.854 l
+609.386 315.648 609.61 315.546 609.857 315.546 c
+610.258 315.546 610.464 315.773 610.474 316.222 c
+610.474 316.604 l
+610.316 316.369 610.096 316.251 609.813 316.251 c
+609.497 316.251 609.254 316.369 609.078 316.604 c
+608.909 316.839 608.82 317.17 608.814 317.603 c
+h
+609.298 317.662 m
+609.298 317.328 609.346 317.082 609.446 316.928 c
+609.541 316.77 609.703 316.692 609.93 316.692 c
+610.173 316.692 610.356 316.814 610.474 317.059 c
+610.474 318.544 l
+610.356 318.786 610.18 318.912 609.945 318.912 c
+609.718 318.912 609.556 318.831 609.46 318.676 c
+609.361 318.518 609.306 318.279 609.298 317.956 c
+h
+614.873 317.662 m
+614.873 317.192 614.788 316.839 614.623 316.604 c
+614.454 316.369 614.211 316.251 613.888 316.251 c
+613.572 316.251 613.337 316.388 613.182 316.662 c
+613.153 316.31 l
+612.712 316.31 l
+612.712 320.543 l
+613.197 320.543 l
+613.197 318.971 l
+613.352 319.224 613.583 319.353 613.888 319.353 c
+614.211 319.353 614.454 319.235 614.623 319 c
+614.788 318.765 614.873 318.416 614.873 317.956 c
+h
+614.387 317.942 m
+614.387 318.294 614.337 318.544 614.241 318.691 c
+614.142 318.838 613.98 318.912 613.756 318.912 c
+613.51 318.912 613.323 318.772 613.197 318.5 c
+613.197 317.089 l
+613.315 316.824 613.506 316.692 613.77 316.692 c
+613.984 316.692 614.142 316.766 614.241 316.913 c
+614.337 317.067 614.387 317.31 614.387 317.633 c
+h
+616.843 316.31 m
+616.814 316.376 616.791 316.486 616.784 316.633 c
+616.608 316.376 616.386 316.251 616.122 316.251 c
+615.846 316.251 615.63 316.325 615.476 316.471 c
+615.328 316.626 615.255 316.843 615.255 317.119 c
+615.255 317.42 615.358 317.662 615.563 317.838 c
+615.769 318.023 616.053 318.118 616.417 318.118 c
+616.769 318.118 l
+616.769 318.441 l
+616.769 318.618 616.729 318.739 616.652 318.809 c
+616.571 318.886 616.453 318.927 616.299 318.927 c
+616.151 318.927 616.027 318.882 615.931 318.794 c
+615.843 318.706 615.799 318.596 615.799 318.47 c
+615.314 318.47 l
+615.314 318.618 615.358 318.757 615.446 318.897 c
+615.534 319.044 615.652 319.154 615.799 319.235 c
+615.954 319.312 616.126 319.353 616.313 319.353 c
+616.625 319.353 616.861 319.272 617.019 319.118 c
+617.173 318.971 617.254 318.75 617.254 318.456 c
+617.254 316.957 l
+617.261 316.722 617.298 316.519 617.357 316.354 c
+617.357 316.31 l
+h
+616.196 316.692 m
+616.313 316.692 616.424 316.725 616.534 316.795 c
+616.641 316.861 616.718 316.945 616.769 317.045 c
+616.769 317.751 l
+616.504 317.751 l
+616.269 317.751 616.082 317.699 615.946 317.603 c
+615.817 317.504 615.755 317.361 615.755 317.177 c
+615.755 317.008 615.784 316.887 615.843 316.81 c
+615.91 316.729 616.027 316.692 616.196 316.692 c
+618.863 316.677 m
+619.029 316.677 619.162 316.725 619.26 316.824 c
+619.356 316.92 619.411 317.063 619.422 317.25 c
+619.878 317.25 l
+619.867 316.964 619.764 316.725 619.569 316.531 c
+619.382 316.343 619.147 316.251 618.863 316.251 c
+618.5 316.251 618.221 316.369 618.026 316.604 c
+617.828 316.839 617.732 317.184 617.732 317.647 c
+617.732 317.971 l
+617.732 318.42 617.824 318.765 618.011 319 c
+618.206 319.235 618.489 319.353 618.863 319.353 c
+619.165 319.353 619.407 319.253 619.584 319.058 c
+619.768 318.86 619.867 318.596 619.878 318.265 c
+619.422 318.265 l
+619.401 318.489 619.341 318.655 619.246 318.765 c
+619.158 318.871 619.029 318.927 618.863 318.927 c
+618.647 318.927 618.485 318.853 618.379 318.706 c
+618.279 318.566 618.225 318.339 618.217 318.015 c
+618.217 317.633 l
+618.217 317.28 618.265 317.03 618.364 316.883 c
+618.47 316.743 618.636 316.677 618.863 316.677 c
+621.024 317.677 m
+620.76 317.383 l
+620.76 316.31 l
+620.275 316.31 l
+620.275 320.543 l
+620.76 320.543 l
+620.76 318.015 l
+621.686 319.294 l
+622.274 319.294 l
+621.333 318.044 l
+622.406 316.31 l
+621.833 316.31 l
+h
+624.166 316.575 m
+624.008 316.357 623.773 316.251 623.461 316.251 c
+623.197 316.251 622.991 316.343 622.844 316.531 c
+622.704 316.714 622.638 316.99 622.638 317.354 c
+622.638 319.294 l
+623.123 319.294 l
+623.123 317.383 l
+623.123 316.92 623.262 316.692 623.549 316.692 c
+623.843 316.692 624.041 316.824 624.151 317.089 c
+624.151 319.294 l
+624.652 319.294 l
+624.652 316.31 l
+624.181 316.31 l
+h
+627.349 317.662 m
+627.349 317.192 627.264 316.839 627.099 316.604 c
+626.929 316.369 626.691 316.251 626.379 316.251 c
+626.073 316.251 625.842 316.361 625.688 316.589 c
+625.688 315.164 l
+625.203 315.164 l
+625.203 319.294 l
+625.643 319.294 l
+625.673 318.956 l
+625.828 319.22 626.059 319.353 626.364 319.353 c
+626.694 319.353 626.941 319.235 627.099 319 c
+627.264 318.772 627.349 318.434 627.349 317.986 c
+h
+626.863 317.942 m
+626.863 318.272 626.809 318.518 626.702 318.676 c
+626.603 318.831 626.441 318.912 626.217 318.912 c
+625.982 318.912 625.805 318.794 625.688 318.559 c
+625.688 317.015 l
+625.805 316.787 625.986 316.677 626.231 316.677 c
+626.445 316.677 626.603 316.754 626.702 316.913 c
+626.809 317.067 626.863 317.31 626.863 317.633 c
+h
+629.248 317.074 m
+629.248 317.181 629.208 317.269 629.131 317.339 c
+629.05 317.416 628.899 317.504 628.675 317.603 c
+628.411 317.71 628.223 317.802 628.117 317.882 c
+628.007 317.96 627.929 318.048 627.881 318.148 c
+627.83 318.243 627.808 318.36 627.808 318.5 c
+627.808 318.742 627.896 318.945 628.072 319.103 c
+628.249 319.268 628.473 319.353 628.749 319.353 c
+629.043 319.353 629.278 319.264 629.454 319.088 c
+629.631 318.919 629.719 318.706 629.719 318.441 c
+629.234 318.441 l
+629.234 318.578 629.182 318.691 629.086 318.779 c
+628.999 318.875 628.885 318.927 628.749 318.927 c
+628.602 318.927 628.488 318.886 628.411 318.809 c
+628.33 318.739 628.293 318.64 628.293 318.515 c
+628.293 318.416 628.323 318.339 628.381 318.279 c
+628.44 318.221 628.579 318.14 628.808 318.044 c
+629.167 317.897 629.414 317.754 629.543 317.618 c
+629.678 317.489 629.749 317.317 629.749 317.103 c
+629.749 316.847 629.653 316.641 629.469 316.486 c
+629.292 316.328 629.057 316.251 628.764 316.251 c
+628.448 316.251 628.194 316.34 627.999 316.516 c
+627.812 316.7 627.72 316.931 627.72 317.207 c
+628.205 317.207 l
+628.213 317.038 628.263 316.905 628.352 316.81 c
+628.448 316.722 628.587 316.677 628.764 316.677 c
+628.918 316.677 629.036 316.71 629.117 316.78 c
+629.204 316.847 629.248 316.945 629.248 317.074 c
+630.748 317.442 m
+630.307 317.442 l
+630.277 320.323 l
+630.792 320.323 l
+h
+630.542 316.854 m
+630.638 316.854 630.707 316.824 630.748 316.766 c
+630.796 316.714 630.821 316.648 630.821 316.56 c
+630.821 316.479 630.796 316.413 630.748 316.354 c
+630.707 316.307 630.638 316.28 630.542 316.28 c
+630.454 316.28 630.384 316.307 630.337 316.354 c
+630.285 316.413 630.262 316.479 630.262 316.56 c
+630.262 316.648 630.285 316.714 630.337 316.766 c
+630.384 316.824 630.454 316.854 630.542 316.854 c
+f
+ endstream endobj 34 0 obj <</BBox[581.388 313.958 628.804 308.548]/Group 70 0 R/Length 10862/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 582.7852 311.7234 cm
+0 0 m
+0.808 2.014 l
+1.381 2.014 l
+0.249 -0.5 l
+0.249 -1.999 l
+-0.265 -1.999 l
+-0.265 -0.5 l
+-1.397 2.014 l
+-0.823 2.014 l
+h
+1.385 -0.367 m
+1.385 0.063 1.488 0.405 1.693 0.662 c
+1.907 0.915 2.186 1.044 2.531 1.044 c
+2.873 1.044 3.149 0.915 3.355 0.662 c
+3.568 0.416 3.682 0.081 3.692 -0.338 c
+3.692 -0.646 l
+3.692 -1.08 3.582 -1.421 3.37 -1.675 c
+3.164 -1.932 2.884 -2.057 2.531 -2.057 c
+2.186 -2.057 1.914 -1.936 1.708 -1.69 c
+1.502 -1.448 1.392 -1.113 1.385 -0.69 c
+h
+1.87 -0.646 m
+1.87 -0.962 1.929 -1.205 2.046 -1.381 c
+2.171 -1.55 2.333 -1.631 2.531 -1.631 c
+2.961 -1.631 3.185 -1.323 3.208 -0.706 c
+3.208 -0.367 l
+3.208 -0.066 3.142 0.177 3.017 0.353 c
+2.899 0.53 2.738 0.618 2.531 0.618 c
+2.333 0.618 2.171 0.53 2.046 0.353 c
+1.929 0.177 1.87 -0.066 1.87 -0.367 c
+h
+5.644 -1.734 m
+5.486 -1.951 5.251 -2.057 4.939 -2.057 c
+4.674 -2.057 4.469 -1.965 4.321 -1.778 c
+4.182 -1.595 4.116 -1.319 4.116 -0.955 c
+4.116 0.985 l
+4.6 0.985 l
+4.6 -0.926 l
+4.6 -1.389 4.74 -1.616 5.026 -1.616 c
+5.321 -1.616 5.519 -1.484 5.629 -1.219 c
+5.629 0.985 l
+6.129 0.985 l
+6.129 -1.999 l
+5.659 -1.999 l
+h
+9.415 -1.999 m
+9.385 -1.932 9.363 -1.822 9.356 -1.675 c
+9.18 -1.932 8.959 -2.057 8.694 -2.057 c
+8.419 -2.057 8.201 -1.984 8.047 -1.837 c
+7.9 -1.683 7.827 -1.466 7.827 -1.19 c
+7.827 -0.889 7.93 -0.646 8.135 -0.47 c
+8.342 -0.286 8.625 -0.191 8.988 -0.191 c
+9.341 -0.191 l
+9.341 0.133 l
+9.341 0.31 9.3 0.431 9.223 0.5 c
+9.142 0.578 9.025 0.618 8.87 0.618 c
+8.724 0.618 8.598 0.574 8.503 0.485 c
+8.415 0.397 8.371 0.287 8.371 0.162 c
+7.885 0.162 l
+7.885 0.31 7.93 0.449 8.018 0.588 c
+8.106 0.736 8.224 0.846 8.371 0.927 c
+8.525 1.004 8.698 1.044 8.885 1.044 c
+9.198 1.044 9.433 0.963 9.591 0.809 c
+9.745 0.662 9.826 0.441 9.826 0.148 c
+9.826 -1.352 l
+9.834 -1.587 9.87 -1.789 9.929 -1.955 c
+9.929 -1.999 l
+h
+8.768 -1.616 m
+8.885 -1.616 8.995 -1.583 9.105 -1.514 c
+9.213 -1.448 9.29 -1.363 9.341 -1.263 c
+9.341 -0.558 l
+9.076 -0.558 l
+8.841 -0.558 8.654 -0.61 8.517 -0.706 c
+8.39 -0.804 8.326 -0.947 8.326 -1.132 c
+8.326 -1.3 8.356 -1.421 8.415 -1.499 c
+8.481 -1.579 8.598 -1.616 8.768 -1.616 c
+11.642 0.53 m
+11.571 0.537 11.498 0.545 11.421 0.545 c
+11.164 0.545 10.987 0.405 10.892 0.133 c
+10.892 -1.999 l
+10.406 -1.999 l
+10.406 0.985 l
+10.877 0.985 l
+10.892 0.676 l
+11.017 0.919 11.2 1.044 11.436 1.044 c
+11.513 1.044 11.575 1.029 11.627 1 c
+h
+13.02 -2.057 m
+12.644 -2.057 12.361 -1.951 12.167 -1.734 c
+11.968 -1.51 11.873 -1.182 11.873 -0.749 c
+11.873 -0.382 l
+11.873 0.059 11.964 0.405 12.153 0.662 c
+12.347 0.915 12.623 1.044 12.976 1.044 c
+13.317 1.044 13.57 0.93 13.74 0.706 c
+13.916 0.478 14.008 0.133 14.019 -0.338 c
+14.019 -0.646 l
+12.358 -0.646 l
+12.358 -0.72 l
+12.358 -1.043 12.417 -1.278 12.534 -1.425 c
+12.652 -1.565 12.821 -1.631 13.049 -1.631 c
+13.196 -1.631 13.321 -1.609 13.431 -1.558 c
+13.537 -1.499 13.641 -1.411 13.74 -1.294 c
+13.99 -1.602 l
+13.784 -1.907 13.46 -2.057 13.02 -2.057 c
+12.976 0.618 m
+12.77 0.618 12.615 0.548 12.519 0.412 c
+12.421 0.273 12.365 0.059 12.358 -0.235 c
+13.533 -0.235 l
+13.533 -0.161 l
+13.512 0.111 13.46 0.31 13.373 0.427 c
+13.284 0.551 13.151 0.618 12.976 0.618 c
+15.533 -0.367 m
+15.533 0.092 15.613 0.441 15.783 0.676 c
+15.96 0.919 16.209 1.044 16.533 1.044 c
+16.816 1.044 17.036 0.927 17.194 0.691 c
+17.194 2.234 l
+17.679 2.234 l
+17.679 -1.999 l
+17.238 -1.999 l
+17.209 -1.675 l
+17.051 -1.932 16.826 -2.057 16.533 -2.057 c
+16.216 -2.057 15.974 -1.94 15.798 -1.705 c
+15.621 -1.462 15.533 -1.117 15.533 -0.675 c
+h
+16.018 -0.646 m
+16.018 -0.98 16.066 -1.227 16.165 -1.381 c
+16.261 -1.539 16.423 -1.616 16.65 -1.616 c
+16.893 -1.616 17.076 -1.499 17.194 -1.263 c
+17.194 0.25 l
+17.065 0.485 16.885 0.603 16.65 0.603 c
+16.423 0.603 16.261 0.522 16.165 0.368 c
+16.066 0.21 16.018 -0.029 16.018 -0.353 c
+h
+18.146 -0.367 m
+18.146 0.063 18.248 0.405 18.454 0.662 c
+18.667 0.915 18.947 1.044 19.293 1.044 c
+19.634 1.044 19.91 0.915 20.115 0.662 c
+20.328 0.416 20.442 0.081 20.454 -0.338 c
+20.454 -0.646 l
+20.454 -1.08 20.344 -1.421 20.13 -1.675 c
+19.924 -1.932 19.645 -2.057 19.293 -2.057 c
+18.947 -2.057 18.675 -1.936 18.469 -1.69 c
+18.263 -1.448 18.153 -1.113 18.146 -0.69 c
+h
+18.63 -0.646 m
+18.63 -0.962 18.69 -1.205 18.807 -1.381 c
+18.932 -1.55 19.094 -1.631 19.293 -1.631 c
+19.723 -1.631 19.946 -1.323 19.968 -0.706 c
+19.968 -0.367 l
+19.968 -0.066 19.902 0.177 19.777 0.353 c
+19.66 0.53 19.498 0.618 19.293 0.618 c
+19.094 0.618 18.932 0.53 18.807 0.353 c
+18.69 0.177 18.63 -0.066 18.63 -0.367 c
+h
+21.423 -1.999 -0.5 2.984 re
+21.453 1.779 m
+21.453 1.691 21.427 1.617 21.379 1.559 c
+21.339 1.507 21.269 1.485 21.174 1.485 c
+21.086 1.485 21.016 1.507 20.968 1.559 c
+20.928 1.617 20.909 1.684 20.909 1.764 c
+20.909 1.852 20.928 1.926 20.968 1.985 c
+21.016 2.043 21.086 2.073 21.174 2.073 c
+21.269 2.073 21.339 2.043 21.379 1.985 c
+21.427 1.926 21.453 1.856 21.453 1.779 c
+22.551 0.985 m
+22.566 0.662 l
+22.75 0.915 22.993 1.044 23.287 1.044 c
+23.816 1.044 24.084 0.691 24.095 -0.014 c
+24.095 -1.999 l
+23.61 -1.999 l
+23.61 -0.043 l
+23.61 0.192 23.569 0.357 23.492 0.456 c
+23.411 0.551 23.294 0.603 23.139 0.603 c
+23.022 0.603 22.912 0.563 22.817 0.485 c
+22.717 0.405 22.64 0.298 22.582 0.162 c
+22.582 -1.999 l
+22.096 -1.999 l
+22.096 0.985 l
+h
+24.547 -0.367 m
+24.547 0.092 24.628 0.441 24.797 0.676 c
+24.974 0.919 25.223 1.044 25.547 1.044 c
+25.848 1.044 26.079 0.912 26.237 0.647 c
+26.267 0.985 l
+26.708 0.985 l
+26.708 -2.028 l
+26.708 -2.399 26.609 -2.682 26.414 -2.881 c
+26.227 -3.075 25.965 -3.175 25.635 -3.175 c
+25.487 -3.175 25.319 -3.135 25.135 -3.057 c
+24.947 -2.987 24.812 -2.899 24.724 -2.792 c
+24.915 -2.454 l
+25.121 -2.66 25.344 -2.763 25.591 -2.763 c
+25.992 -2.763 26.197 -2.535 26.208 -2.087 c
+26.208 -1.705 l
+26.05 -1.94 25.83 -2.057 25.547 -2.057 c
+25.231 -2.057 24.988 -1.94 24.812 -1.705 c
+24.643 -1.469 24.554 -1.138 24.547 -0.706 c
+h
+25.032 -0.646 m
+25.032 -0.98 25.08 -1.227 25.179 -1.381 c
+25.275 -1.539 25.437 -1.616 25.664 -1.616 c
+25.907 -1.616 26.09 -1.495 26.208 -1.249 c
+26.208 0.235 l
+26.09 0.478 25.915 0.603 25.679 0.603 c
+25.451 0.603 25.289 0.522 25.194 0.368 c
+25.094 0.21 25.04 -0.029 25.032 -0.353 c
+h
+28.975 -1.999 -0.5 2.984 re
+29.005 1.779 m
+29.005 1.691 28.979 1.617 28.932 1.559 c
+28.891 1.507 28.82 1.485 28.726 1.485 c
+28.637 1.485 28.567 1.507 28.519 1.559 c
+28.479 1.617 28.461 1.684 28.461 1.764 c
+28.461 1.852 28.479 1.926 28.519 1.985 c
+28.567 2.043 28.637 2.073 28.726 2.073 c
+28.82 2.073 28.891 2.043 28.932 1.985 c
+28.979 1.926 29.005 1.856 29.005 1.779 c
+30.28 1.706 m
+30.28 0.985 l
+30.736 0.985 l
+30.736 0.588 l
+30.28 0.588 l
+30.28 -1.263 l
+30.28 -1.381 30.298 -1.469 30.339 -1.529 c
+30.376 -1.587 30.445 -1.616 30.544 -1.616 c
+30.603 -1.616 30.665 -1.609 30.736 -1.587 c
+30.736 -1.999 l
+30.618 -2.036 30.503 -2.057 30.397 -2.057 c
+30.199 -2.057 30.048 -1.992 29.942 -1.851 c
+29.842 -1.716 29.795 -1.521 29.795 -1.263 c
+29.795 0.588 l
+29.339 0.588 l
+29.339 0.985 l
+29.795 0.985 l
+29.795 1.706 l
+h
+31.202 -2.792 m
+30.908 -2.601 l
+31.085 -2.355 31.176 -2.105 31.187 -1.851 c
+31.187 -1.396 l
+31.687 -1.396 l
+31.687 -1.793 l
+31.687 -1.98 31.636 -2.164 31.54 -2.352 c
+31.452 -2.535 31.338 -2.682 31.202 -2.792 c
+34.682 0.53 m
+34.613 0.537 34.539 0.545 34.461 0.545 c
+34.204 0.545 34.028 0.405 33.932 0.133 c
+33.932 -1.999 l
+33.447 -1.999 l
+33.447 0.985 l
+33.917 0.985 l
+33.932 0.676 l
+34.058 0.919 34.241 1.044 34.476 1.044 c
+34.553 1.044 34.616 1.029 34.667 1 c
+h
+35.579 -1.999 -0.5 2.984 re
+35.608 1.779 m
+35.608 1.691 35.583 1.617 35.535 1.559 c
+35.494 1.507 35.424 1.485 35.329 1.485 c
+35.241 1.485 35.17 1.507 35.123 1.559 c
+35.083 1.617 35.064 1.684 35.064 1.764 c
+35.064 1.852 35.083 1.926 35.123 1.985 c
+35.17 2.043 35.241 2.073 35.329 2.073 c
+35.424 2.073 35.494 2.043 35.535 1.985 c
+35.583 1.926 35.608 1.856 35.608 1.779 c
+36.152 -0.367 m
+36.152 0.092 36.233 0.441 36.402 0.676 c
+36.578 0.919 36.828 1.044 37.152 1.044 c
+37.453 1.044 37.684 0.912 37.842 0.647 c
+37.872 0.985 l
+38.313 0.985 l
+38.313 -2.028 l
+38.313 -2.399 38.214 -2.682 38.019 -2.881 c
+37.832 -3.075 37.57 -3.175 37.24 -3.175 c
+37.092 -3.175 36.924 -3.135 36.74 -3.057 c
+36.552 -2.987 36.417 -2.899 36.329 -2.792 c
+36.52 -2.454 l
+36.726 -2.66 36.949 -2.763 37.196 -2.763 c
+37.597 -2.763 37.802 -2.535 37.813 -2.087 c
+37.813 -1.705 l
+37.655 -1.94 37.435 -2.057 37.152 -2.057 c
+36.836 -2.057 36.593 -1.94 36.417 -1.705 c
+36.248 -1.469 36.159 -1.138 36.152 -0.706 c
+h
+36.637 -0.646 m
+36.637 -0.98 36.685 -1.227 36.784 -1.381 c
+36.88 -1.539 37.042 -1.616 37.269 -1.616 c
+37.512 -1.616 37.695 -1.495 37.813 -1.249 c
+37.813 0.235 l
+37.695 0.478 37.519 0.603 37.283 0.603 c
+37.056 0.603 36.894 0.522 36.799 0.368 c
+36.699 0.21 36.645 -0.029 36.637 -0.353 c
+h
+39.334 0.676 m
+39.519 0.919 39.754 1.044 40.04 1.044 c
+40.569 1.044 40.838 0.691 40.849 -0.014 c
+40.849 -1.999 l
+40.363 -1.999 l
+40.363 -0.043 l
+40.363 0.192 40.323 0.357 40.246 0.456 c
+40.165 0.551 40.047 0.603 39.893 0.603 c
+39.775 0.603 39.665 0.563 39.569 0.485 c
+39.471 0.405 39.393 0.298 39.334 0.162 c
+39.334 -1.999 l
+38.85 -1.999 l
+38.85 2.234 l
+39.334 2.234 l
+h
+42.021 1.706 m
+42.021 0.985 l
+42.476 0.985 l
+42.476 0.588 l
+42.021 0.588 l
+42.021 -1.263 l
+42.021 -1.381 42.039 -1.469 42.079 -1.529 c
+42.116 -1.587 42.186 -1.616 42.285 -1.616 c
+42.344 -1.616 42.407 -1.609 42.476 -1.587 c
+42.476 -1.999 l
+42.359 -2.036 42.245 -2.057 42.139 -2.057 c
+41.94 -2.057 41.79 -1.992 41.682 -1.851 c
+41.583 -1.716 41.536 -1.521 41.536 -1.263 c
+41.536 0.588 l
+41.08 0.588 l
+41.08 0.985 l
+41.536 0.985 l
+41.536 1.706 l
+h
+43.487 -0.866 m
+43.046 -0.866 l
+43.016 2.014 l
+43.531 2.014 l
+h
+43.282 -1.454 m
+43.377 -1.454 43.446 -1.484 43.487 -1.543 c
+43.535 -1.595 43.56 -1.66 43.56 -1.749 c
+43.56 -1.83 43.535 -1.896 43.487 -1.955 c
+43.446 -2.002 43.377 -2.028 43.282 -2.028 c
+43.193 -2.028 43.124 -2.002 43.076 -1.955 c
+43.024 -1.896 43.002 -1.83 43.002 -1.749 c
+43.002 -1.66 43.024 -1.595 43.076 -1.543 c
+43.124 -1.484 43.193 -1.454 43.282 -1.454 c
+44.74 -0.866 m
+44.74 -0.654 44.758 -0.477 44.799 -0.338 c
+44.847 -0.191 44.961 -0.014 45.137 0.192 c
+45.343 0.441 l
+45.468 0.626 45.534 0.813 45.534 1 c
+45.534 1.195 45.489 1.345 45.401 1.455 c
+45.32 1.563 45.21 1.617 45.063 1.617 c
+44.924 1.617 44.807 1.566 44.711 1.47 c
+44.622 1.372 44.578 1.239 44.578 1.073 c
+44.093 1.073 l
+44.093 1.374 44.182 1.617 44.358 1.794 c
+44.541 1.977 44.776 2.073 45.063 2.073 c
+45.358 2.073 45.585 1.977 45.754 1.794 c
+45.931 1.607 46.019 1.353 46.019 1.029 c
+46.019 0.713 45.89 0.401 45.636 0.088 c
+45.387 -0.22 l
+45.277 -0.367 45.225 -0.584 45.225 -0.866 c
+h
+45.005 -1.454 m
+45.092 -1.454 45.159 -1.484 45.21 -1.543 c
+45.258 -1.595 45.283 -1.66 45.283 -1.749 c
+45.283 -1.83 45.258 -1.896 45.21 -1.955 c
+45.159 -2.002 45.092 -2.028 45.005 -2.028 c
+44.905 -2.028 44.832 -2.002 44.784 -1.955 c
+44.743 -1.896 44.726 -1.83 44.726 -1.749 c
+44.726 -1.66 44.743 -1.595 44.784 -1.543 c
+44.832 -1.484 44.905 -1.454 45.005 -1.454 c
+f
+Q
+ endstream endobj 35 0 obj <</BBox[535.367 366.838 774.632 366.237]/Group 71 0 R/Length 124/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 K
+0.601 w 4 M 1 j 1 J [4.808 4.808]0 d
+/GS0 gs
+q 1 0 0 1 535.6673 366.537 cm
+0 0 m
+238.665 0 l
+S
+Q
+ endstream endobj 36 0 obj <</BBox[741.792 364.828 774.49 359.389]/Group 72 0 R/Length 8999/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+742.571 360.595 -0.779 4.233 re
+742.99 362.182 m
+742.99 362.631 743.097 362.983 743.313 363.24 c
+743.526 363.505 743.82 363.637 744.195 363.637 c
+744.577 363.637 744.872 363.505 745.077 363.24 c
+745.29 362.983 745.4 362.631 745.4 362.182 c
+745.4 361.991 l
+745.4 361.528 745.29 361.172 745.077 360.917 c
+744.872 360.661 744.577 360.536 744.195 360.536 c
+743.813 360.536 743.511 360.661 743.299 360.917 c
+743.093 361.183 742.99 361.539 742.99 361.991 c
+h
+743.769 361.991 m
+743.769 361.451 743.908 361.183 744.195 361.183 c
+744.46 361.183 744.6 361.407 744.622 361.858 c
+744.637 362.182 l
+744.637 362.454 744.596 362.656 744.519 362.785 c
+744.438 362.92 744.331 362.991 744.195 362.991 c
+744.066 362.991 743.964 362.92 743.887 362.785 c
+743.806 362.656 743.769 362.454 743.769 362.182 c
+h
+746.79 361.183 m
+747.014 361.183 747.128 361.33 747.128 361.623 c
+747.862 361.623 l
+747.862 361.3 747.76 361.035 747.554 360.83 c
+747.355 360.632 747.106 360.536 746.804 360.536 c
+746.422 360.536 746.128 360.653 745.923 360.888 c
+745.716 361.131 745.614 361.484 745.614 361.947 c
+745.614 362.197 l
+745.614 362.656 745.709 363.009 745.907 363.255 c
+746.114 363.508 746.407 363.637 746.79 363.637 c
+747.12 363.637 747.382 363.535 747.569 363.329 c
+747.764 363.123 747.862 362.833 747.862 362.461 c
+747.128 362.461 l
+747.128 362.627 747.098 362.756 747.039 362.843 c
+746.988 362.939 746.9 362.991 746.775 362.991 c
+746.646 362.991 746.555 362.939 746.495 362.843 c
+746.437 362.744 746.4 362.557 746.393 362.285 c
+746.393 361.976 l
+746.393 361.73 746.4 361.557 746.422 361.462 c
+746.451 361.374 746.488 361.304 746.54 361.256 c
+746.598 361.204 746.679 361.183 746.79 361.183 c
+749.553 360.595 m
+749.524 360.642 749.498 360.72 749.479 360.83 c
+749.34 360.632 749.156 360.536 748.921 360.536 c
+748.664 360.536 748.454 360.616 748.289 360.786 c
+748.13 360.951 748.053 361.168 748.053 361.432 c
+748.053 361.744 748.149 361.983 748.348 362.153 c
+748.543 362.329 748.826 362.417 749.2 362.417 c
+749.45 362.417 l
+749.45 362.652 l
+749.45 362.789 749.424 362.88 749.377 362.932 c
+749.325 362.991 749.259 363.02 749.171 363.02 c
+748.972 363.02 748.876 362.906 748.876 362.681 c
+748.098 362.681 l
+748.098 362.954 748.201 363.182 748.406 363.358 c
+748.612 363.542 748.876 363.637 749.2 363.637 c
+749.524 363.637 749.774 363.549 749.95 363.373 c
+750.133 363.203 750.229 362.965 750.229 362.652 c
+750.229 361.241 l
+750.229 360.984 750.262 360.786 750.332 360.639 c
+750.332 360.595 l
+h
+749.082 361.153 m
+749.171 361.153 749.244 361.168 749.304 361.197 c
+749.369 361.234 749.42 361.278 749.45 361.33 c
+749.45 361.947 l
+749.259 361.947 l
+749.119 361.947 749.013 361.902 748.936 361.815 c
+748.866 361.734 748.833 361.623 748.833 361.476 c
+748.833 361.26 748.913 361.153 749.082 361.153 c
+751.431 360.595 -0.779 4.233 re
+754.621 362.829 m
+754.37 362.843 l
+754.154 362.843 754.007 362.748 753.929 362.564 c
+753.929 360.595 l
+753.15 360.595 l
+753.15 363.579 l
+753.886 363.579 l
+753.915 363.255 l
+754.033 363.508 754.198 363.637 754.415 363.637 c
+754.503 363.637 754.576 363.622 754.635 363.593 c
+h
+756.053 360.536 m
+755.65 360.536 755.337 360.653 755.113 360.888 c
+754.896 361.131 754.79 361.476 754.79 361.918 c
+754.79 362.168 l
+754.79 362.638 754.893 362.997 755.099 363.255 c
+755.304 363.508 755.598 363.637 755.98 363.637 c
+756.352 363.637 756.631 363.512 756.818 363.269 c
+757.013 363.034 757.112 362.681 757.112 362.211 c
+757.112 361.829 l
+755.569 361.829 l
+755.575 361.601 755.62 361.436 755.701 361.33 c
+755.789 361.23 755.925 361.183 756.113 361.183 c
+756.366 361.183 756.583 361.274 756.759 361.462 c
+757.067 360.977 l
+756.969 360.848 756.826 360.742 756.641 360.653 c
+756.465 360.576 756.267 360.536 756.053 360.536 c
+755.569 362.373 m
+756.348 362.373 l
+756.348 362.446 l
+756.348 362.623 756.319 362.756 756.259 362.843 c
+756.201 362.939 756.101 362.991 755.966 362.991 c
+755.837 362.991 755.737 362.939 755.671 362.843 c
+755.613 362.744 755.575 362.586 755.569 362.373 c
+759.691 361.991 m
+759.691 361.521 759.611 361.156 759.456 360.903 c
+759.298 360.657 759.06 360.536 758.736 360.536 c
+758.49 360.536 758.295 360.632 758.148 360.83 c
+758.148 359.448 l
+757.37 359.448 l
+757.37 363.579 l
+758.089 363.579 l
+758.119 363.299 l
+758.273 363.523 758.479 363.637 758.736 363.637 c
+759.049 363.637 759.284 363.52 759.442 363.284 c
+759.607 363.049 759.691 362.696 759.691 362.226 c
+h
+758.927 362.182 m
+758.927 362.476 758.891 362.681 758.825 362.799 c
+758.766 362.917 758.655 362.976 758.501 362.976 c
+758.343 362.976 758.226 362.91 758.148 362.785 c
+758.148 361.374 l
+758.226 361.245 758.347 361.183 758.515 361.183 c
+758.663 361.183 758.766 361.245 758.825 361.374 c
+758.891 361.509 758.927 361.715 758.927 361.991 c
+h
+759.893 362.182 m
+759.893 362.631 760.001 362.983 760.217 363.24 c
+760.431 363.505 760.724 363.637 761.099 363.637 c
+761.481 363.637 761.775 363.505 761.981 363.24 c
+762.195 362.983 762.305 362.631 762.305 362.182 c
+762.305 361.991 l
+762.305 361.528 762.195 361.172 761.981 360.917 c
+761.775 360.661 761.481 360.536 761.099 360.536 c
+760.717 360.536 760.416 360.661 760.202 360.917 c
+759.997 361.183 759.893 361.539 759.893 361.991 c
+h
+760.672 361.991 m
+760.672 361.451 760.813 361.183 761.099 361.183 c
+761.364 361.183 761.503 361.407 761.526 361.858 c
+761.54 362.182 l
+761.54 362.454 761.499 362.656 761.422 362.785 c
+761.341 362.92 761.235 362.991 761.099 362.991 c
+760.971 362.991 760.867 362.92 760.79 362.785 c
+760.709 362.656 760.672 362.454 760.672 362.182 c
+h
+763.928 361.388 m
+763.928 361.455 763.892 361.521 763.826 361.58 c
+763.766 361.638 763.627 361.711 763.415 361.8 c
+763.091 361.935 762.87 362.072 762.752 362.211 c
+762.635 362.347 762.576 362.521 762.576 362.726 c
+762.576 362.991 762.669 363.203 762.856 363.373 c
+763.039 363.549 763.29 363.637 763.606 363.637 c
+763.928 363.637 764.186 363.549 764.385 363.373 c
+764.579 363.203 764.678 362.98 764.678 362.696 c
+763.899 362.696 l
+763.899 362.939 763.797 363.064 763.591 363.064 c
+763.51 363.064 763.444 363.034 763.385 362.976 c
+763.334 362.924 763.311 362.858 763.311 362.77 c
+763.311 362.7 763.34 362.641 763.4 362.594 c
+763.458 362.542 763.594 362.469 763.811 362.373 c
+764.134 362.255 764.358 362.124 764.487 361.976 c
+764.624 361.837 764.693 361.653 764.693 361.418 c
+764.693 361.153 764.591 360.936 764.385 360.771 c
+764.186 360.613 763.928 360.536 763.606 360.536 c
+763.377 360.536 763.182 360.58 763.018 360.668 c
+762.848 360.757 762.716 360.874 762.621 361.021 c
+762.532 361.175 762.488 361.345 762.488 361.521 c
+763.223 361.521 l
+763.23 361.381 763.267 361.278 763.326 361.212 c
+763.385 361.142 763.48 361.109 763.62 361.109 c
+763.826 361.109 763.928 361.201 763.928 361.388 c
+765.825 360.595 -0.779 2.984 re
+765.002 364.343 m
+765.002 364.468 765.038 364.57 765.119 364.651 c
+765.196 364.728 765.299 364.769 765.428 364.769 c
+765.553 364.769 765.656 364.728 765.736 364.651 c
+765.814 364.57 765.854 364.468 765.854 364.343 c
+765.854 364.225 765.814 364.126 765.736 364.049 c
+765.667 363.968 765.564 363.932 765.428 363.932 c
+765.299 363.932 765.196 363.968 765.119 364.049 c
+765.038 364.126 765.002 364.225 765.002 364.343 c
+767.243 364.314 m
+767.243 363.579 l
+767.64 363.579 l
+767.64 362.991 l
+767.243 362.991 l
+767.243 361.505 l
+767.243 361.388 767.258 361.304 767.288 361.256 c
+767.324 361.216 767.39 361.197 767.479 361.197 c
+767.556 361.197 767.618 361.204 767.67 361.227 c
+767.655 360.609 l
+767.527 360.562 767.383 360.536 767.228 360.536 c
+766.729 360.536 766.472 360.822 766.465 361.403 c
+766.465 362.991 l
+766.126 362.991 l
+766.126 363.579 l
+766.465 363.579 l
+766.465 364.314 l
+h
+767.787 362.182 m
+767.787 362.631 767.893 362.983 768.111 363.24 c
+768.323 363.505 768.618 363.637 768.992 363.637 c
+769.374 363.637 769.669 363.505 769.875 363.24 c
+770.087 362.983 770.197 362.631 770.197 362.182 c
+770.197 361.991 l
+770.197 361.528 770.087 361.172 769.875 360.917 c
+769.669 360.661 769.374 360.536 768.992 360.536 c
+768.61 360.536 768.309 360.661 768.096 360.917 c
+767.89 361.183 767.787 361.539 767.787 361.991 c
+h
+768.566 361.991 m
+768.566 361.451 768.705 361.183 768.992 361.183 c
+769.257 361.183 769.397 361.407 769.418 361.858 c
+769.433 362.182 l
+769.433 362.454 769.393 362.656 769.316 362.785 c
+769.235 362.92 769.129 362.991 768.992 362.991 c
+768.863 362.991 768.761 362.92 768.684 362.785 c
+768.603 362.656 768.566 362.454 768.566 362.182 c
+h
+771.969 362.829 m
+771.719 362.843 l
+771.502 362.843 771.355 362.748 771.278 362.564 c
+771.278 360.595 l
+770.499 360.595 l
+770.499 363.579 l
+771.234 363.579 l
+771.263 363.255 l
+771.381 363.508 771.546 363.637 771.763 363.637 c
+771.851 363.637 771.925 363.622 771.984 363.593 c
+h
+773.255 361.888 m
+773.652 363.579 l
+774.49 363.579 l
+773.505 360.154 l
+773.358 359.647 773.083 359.389 772.682 359.389 c
+772.583 359.389 772.476 359.411 772.358 359.448 c
+772.358 360.051 l
+772.447 360.051 l
+772.572 360.051 772.667 360.076 772.726 360.124 c
+772.784 360.165 772.829 360.242 772.859 360.36 c
+772.932 360.565 l
+772.065 363.579 l
+772.902 363.579 l
+h
+f
+ endstream endobj 37 0 obj <</BBox[733.362 374.614 774.497 369.293]/Group 73 0 R/Length 10421/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 734.8322 372.6739 cm
+0 0 m
+-0.25 0.014 l
+-0.467 0.014 -0.614 -0.081 -0.691 -0.265 c
+-0.691 -2.234 l
+-1.47 -2.234 l
+-1.47 0.749 l
+-0.736 0.749 l
+-0.706 0.426 l
+-0.588 0.679 -0.423 0.808 -0.206 0.808 c
+-0.118 0.808 -0.044 0.793 0.014 0.764 c
+h
+1.433 -2.294 m
+1.028 -2.294 0.716 -2.176 0.492 -1.941 c
+0.276 -1.698 0.168 -1.353 0.168 -0.912 c
+0.168 -0.662 l
+0.168 -0.192 0.272 0.168 0.477 0.426 c
+0.683 0.679 0.977 0.808 1.359 0.808 c
+1.731 0.808 2.009 0.683 2.198 0.44 c
+2.392 0.205 2.491 -0.148 2.491 -0.618 c
+2.491 -1 l
+0.947 -1 l
+0.955 -1.228 0.999 -1.393 1.08 -1.5 c
+1.168 -1.599 1.304 -1.646 1.492 -1.646 c
+1.745 -1.646 1.962 -1.555 2.138 -1.368 c
+2.447 -1.852 l
+2.348 -1.981 2.204 -2.087 2.021 -2.176 c
+1.845 -2.253 1.646 -2.294 1.433 -2.294 c
+0.947 -0.456 m
+1.727 -0.456 l
+1.727 -0.383 l
+1.727 -0.206 1.697 -0.074 1.639 0.014 c
+1.58 0.11 1.481 0.161 1.344 0.161 c
+1.216 0.161 1.117 0.11 1.051 0.014 c
+0.992 -0.085 0.955 -0.243 0.947 -0.456 c
+3.484 0.749 m
+3.513 0.47 l
+3.69 0.694 3.917 0.808 4.203 0.808 c
+4.498 0.808 4.708 0.668 4.835 0.396 c
+5.001 0.668 5.24 0.808 5.556 0.808 c
+6.085 0.808 6.354 0.444 6.364 -0.279 c
+6.364 -2.234 l
+5.585 -2.234 l
+5.585 -0.324 l
+5.585 -0.158 5.56 -0.04 5.512 0.029 c
+5.46 0.106 5.383 0.147 5.277 0.147 c
+5.13 0.147 5.016 0.058 4.939 -0.118 c
+4.953 -0.206 l
+4.953 -2.234 l
+4.174 -2.234 l
+4.174 -0.339 l
+4.174 -0.162 4.149 -0.04 4.101 0.029 c
+4.049 0.106 3.968 0.147 3.85 0.147 c
+3.723 0.147 3.619 0.073 3.542 -0.074 c
+3.542 -2.234 l
+2.763 -2.234 l
+2.763 0.749 l
+h
+6.57 -0.647 m
+6.57 -0.199 6.676 0.154 6.894 0.411 c
+7.106 0.675 7.401 0.808 7.775 0.808 c
+8.158 0.808 8.452 0.675 8.658 0.411 c
+8.87 0.154 8.981 -0.199 8.981 -0.647 c
+8.981 -0.838 l
+8.981 -1.301 8.87 -1.658 8.658 -1.912 c
+8.452 -2.168 8.158 -2.294 7.775 -2.294 c
+7.393 -2.294 7.092 -2.168 6.879 -1.912 c
+6.673 -1.646 6.57 -1.291 6.57 -0.838 c
+h
+7.349 -0.838 m
+7.349 -1.378 7.489 -1.646 7.775 -1.646 c
+8.041 -1.646 8.18 -1.422 8.201 -0.971 c
+8.216 -0.647 l
+8.216 -0.375 8.176 -0.173 8.099 -0.044 c
+8.018 0.091 7.912 0.161 7.775 0.161 c
+7.647 0.161 7.544 0.091 7.467 -0.044 c
+7.386 -0.173 7.349 -0.375 7.349 -0.647 c
+h
+10.194 1.484 m
+10.194 0.749 l
+10.59 0.749 l
+10.59 0.161 l
+10.194 0.161 l
+10.194 -1.324 l
+10.194 -1.441 10.208 -1.526 10.238 -1.573 c
+10.275 -1.613 10.341 -1.632 10.429 -1.632 c
+10.506 -1.632 10.568 -1.625 10.62 -1.603 c
+10.605 -2.22 l
+10.476 -2.268 10.333 -2.294 10.179 -2.294 c
+9.679 -2.294 9.422 -2.007 9.414 -1.426 c
+9.414 0.161 l
+9.076 0.161 l
+9.076 0.749 l
+9.414 0.749 l
+9.414 1.484 l
+h
+12.104 -2.294 m
+11.7 -2.294 11.388 -2.176 11.164 -1.941 c
+10.946 -1.698 10.84 -1.353 10.84 -0.912 c
+10.84 -0.662 l
+10.84 -0.192 10.944 0.168 11.149 0.426 c
+11.355 0.679 11.649 0.808 12.031 0.808 c
+12.402 0.808 12.681 0.683 12.868 0.44 c
+13.063 0.205 13.163 -0.148 13.163 -0.618 c
+13.163 -1 l
+11.619 -1 l
+11.627 -1.228 11.671 -1.393 11.752 -1.5 c
+11.84 -1.599 11.976 -1.646 12.163 -1.646 c
+12.417 -1.646 12.633 -1.555 12.81 -1.368 c
+13.119 -1.852 l
+13.02 -1.981 12.876 -2.087 12.692 -2.176 c
+12.516 -2.253 12.317 -2.294 12.104 -2.294 c
+11.619 -0.456 m
+12.398 -0.456 l
+12.398 -0.383 l
+12.398 -0.206 12.369 -0.074 12.311 0.014 c
+12.251 0.11 12.153 0.161 12.016 0.161 c
+11.887 0.161 11.788 0.11 11.723 0.014 c
+11.663 -0.085 11.627 -0.243 11.619 -0.456 c
+16.103 0 m
+15.852 0.014 l
+15.636 0.014 15.489 -0.081 15.412 -0.265 c
+15.412 -2.234 l
+14.632 -2.234 l
+14.632 0.749 l
+15.368 0.749 l
+15.397 0.426 l
+15.515 0.679 15.68 0.808 15.897 0.808 c
+15.985 0.808 16.058 0.793 16.118 0.764 c
+h
+17.535 -2.294 m
+17.132 -2.294 16.82 -2.176 16.595 -1.941 c
+16.378 -1.698 16.272 -1.353 16.272 -0.912 c
+16.272 -0.662 l
+16.272 -0.192 16.375 0.168 16.581 0.426 c
+16.786 0.679 17.08 0.808 17.462 0.808 c
+17.834 0.808 18.113 0.683 18.3 0.44 c
+18.495 0.205 18.594 -0.148 18.594 -0.618 c
+18.594 -1 l
+17.051 -1 l
+17.058 -1.228 17.102 -1.393 17.183 -1.5 c
+17.271 -1.599 17.407 -1.646 17.595 -1.646 c
+17.848 -1.646 18.065 -1.555 18.241 -1.368 c
+18.549 -1.852 l
+18.451 -1.981 18.308 -2.087 18.123 -2.176 c
+17.947 -2.253 17.749 -2.294 17.535 -2.294 c
+17.051 -0.456 m
+17.83 -0.456 l
+17.83 -0.383 l
+17.83 -0.206 17.801 -0.074 17.741 0.014 c
+17.683 0.11 17.583 0.161 17.448 0.161 c
+17.319 0.161 17.219 0.11 17.153 0.014 c
+17.095 -0.085 17.058 -0.243 17.051 -0.456 c
+21.173 -0.838 m
+21.173 -1.309 21.093 -1.673 20.938 -1.926 c
+20.781 -2.172 20.542 -2.294 20.218 -2.294 c
+19.972 -2.294 19.777 -2.198 19.63 -1.999 c
+19.63 -3.381 l
+18.852 -3.381 l
+18.852 0.749 l
+19.571 0.749 l
+19.601 0.47 l
+19.756 0.694 19.961 0.808 20.218 0.808 c
+20.531 0.808 20.766 0.69 20.924 0.455 c
+21.09 0.22 21.173 -0.133 21.173 -0.603 c
+h
+20.409 -0.647 m
+20.409 -0.353 20.373 -0.148 20.307 -0.03 c
+20.248 0.087 20.137 0.147 19.983 0.147 c
+19.825 0.147 19.708 0.08 19.63 -0.044 c
+19.63 -1.455 l
+19.708 -1.584 19.829 -1.646 19.997 -1.646 c
+20.145 -1.646 20.248 -1.584 20.307 -1.455 c
+20.373 -1.32 20.409 -1.114 20.409 -0.838 c
+h
+21.375 -0.647 m
+21.375 -0.199 21.483 0.154 21.699 0.411 c
+21.913 0.675 22.206 0.808 22.582 0.808 c
+22.964 0.808 23.257 0.675 23.463 0.411 c
+23.677 0.154 23.787 -0.199 23.787 -0.647 c
+23.787 -0.838 l
+23.787 -1.301 23.677 -1.658 23.463 -1.912 c
+23.257 -2.168 22.964 -2.294 22.582 -2.294 c
+22.199 -2.294 21.898 -2.168 21.684 -1.912 c
+21.479 -1.646 21.375 -1.291 21.375 -0.838 c
+h
+22.154 -0.838 m
+22.154 -1.378 22.295 -1.646 22.582 -1.646 c
+22.846 -1.646 22.985 -1.422 23.008 -0.971 c
+23.022 -0.647 l
+23.022 -0.375 22.981 -0.173 22.904 -0.044 c
+22.823 0.091 22.717 0.161 22.582 0.161 c
+22.453 0.161 22.349 0.091 22.272 -0.044 c
+22.192 -0.173 22.154 -0.375 22.154 -0.647 c
+h
+25.41 -1.441 m
+25.41 -1.374 25.374 -1.309 25.308 -1.25 c
+25.249 -1.191 25.109 -1.118 24.897 -1.029 c
+24.573 -0.894 24.352 -0.757 24.234 -0.618 c
+24.117 -0.482 24.059 -0.31 24.059 -0.103 c
+24.059 0.161 24.151 0.374 24.338 0.544 c
+24.521 0.72 24.772 0.808 25.088 0.808 c
+25.41 0.808 25.668 0.72 25.867 0.544 c
+26.061 0.374 26.16 0.151 26.16 -0.133 c
+25.381 -0.133 l
+25.381 0.11 25.279 0.235 25.073 0.235 c
+24.992 0.235 24.926 0.205 24.867 0.147 c
+24.816 0.095 24.793 0.029 24.793 -0.059 c
+24.793 -0.129 24.822 -0.188 24.882 -0.235 c
+24.94 -0.287 25.076 -0.36 25.293 -0.456 c
+25.616 -0.574 25.84 -0.706 25.969 -0.853 c
+26.106 -0.992 26.175 -1.176 26.175 -1.411 c
+26.175 -1.676 26.073 -1.893 25.867 -2.058 c
+25.668 -2.216 25.41 -2.294 25.088 -2.294 c
+24.859 -2.294 24.664 -2.249 24.5 -2.161 c
+24.33 -2.073 24.198 -1.956 24.103 -1.808 c
+24.014 -1.654 23.97 -1.485 23.97 -1.309 c
+24.705 -1.309 l
+24.712 -1.449 24.749 -1.551 24.808 -1.617 c
+24.867 -1.687 24.963 -1.721 25.102 -1.721 c
+25.308 -1.721 25.41 -1.628 25.41 -1.441 c
+27.311 -2.234 -0.779 2.983 re
+26.487 1.514 m
+26.487 1.639 26.524 1.741 26.605 1.822 c
+26.682 1.899 26.785 1.94 26.914 1.94 c
+27.039 1.94 27.141 1.899 27.222 1.822 c
+27.299 1.741 27.34 1.639 27.34 1.514 c
+27.34 1.396 27.299 1.296 27.222 1.219 c
+27.153 1.138 27.05 1.102 26.914 1.102 c
+26.785 1.102 26.682 1.138 26.605 1.219 c
+26.524 1.296 26.487 1.396 26.487 1.514 c
+28.726 1.484 m
+28.726 0.749 l
+29.122 0.749 l
+29.122 0.161 l
+28.726 0.161 l
+28.726 -1.324 l
+28.726 -1.441 28.74 -1.526 28.77 -1.573 c
+28.806 -1.613 28.872 -1.632 28.961 -1.632 c
+29.038 -1.632 29.1 -1.625 29.152 -1.603 c
+29.137 -2.22 l
+29.009 -2.268 28.865 -2.294 28.71 -2.294 c
+28.211 -2.294 27.954 -2.007 27.947 -1.426 c
+27.947 0.161 l
+27.608 0.161 l
+27.608 0.749 l
+27.947 0.749 l
+27.947 1.484 l
+h
+29.269 -0.647 m
+29.269 -0.199 29.376 0.154 29.593 0.411 c
+29.805 0.675 30.1 0.808 30.474 0.808 c
+30.856 0.808 31.151 0.675 31.357 0.411 c
+31.569 0.154 31.679 -0.199 31.679 -0.647 c
+31.679 -0.838 l
+31.679 -1.301 31.569 -1.658 31.357 -1.912 c
+31.151 -2.168 30.856 -2.294 30.474 -2.294 c
+30.092 -2.294 29.791 -2.168 29.578 -1.912 c
+29.372 -1.646 29.269 -1.291 29.269 -0.838 c
+h
+30.048 -0.838 m
+30.048 -1.378 30.188 -1.646 30.474 -1.646 c
+30.739 -1.646 30.879 -1.422 30.9 -0.971 c
+30.916 -0.647 l
+30.916 -0.375 30.875 -0.173 30.798 -0.044 c
+30.717 0.091 30.611 0.161 30.474 0.161 c
+30.345 0.161 30.243 0.091 30.166 -0.044 c
+30.085 -0.173 30.048 -0.375 30.048 -0.647 c
+h
+33.451 0 m
+33.202 0.014 l
+32.984 0.014 32.837 -0.081 32.76 -0.265 c
+32.76 -2.234 l
+31.981 -2.234 l
+31.981 0.749 l
+32.716 0.749 l
+32.745 0.426 l
+32.863 0.679 33.028 0.808 33.246 0.808 c
+33.333 0.808 33.407 0.793 33.466 0.764 c
+h
+34.553 -2.234 -0.779 2.983 re
+33.73 1.514 m
+33.73 1.639 33.767 1.741 33.848 1.822 c
+33.925 1.899 34.028 1.94 34.156 1.94 c
+34.281 1.94 34.384 1.899 34.465 1.822 c
+34.542 1.741 34.583 1.639 34.583 1.514 c
+34.583 1.396 34.542 1.296 34.465 1.219 c
+34.395 1.138 34.293 1.102 34.156 1.102 c
+34.028 1.102 33.925 1.138 33.848 1.219 c
+33.767 1.296 33.73 1.396 33.73 1.514 c
+36.263 -2.294 m
+35.858 -2.294 35.546 -2.176 35.322 -1.941 c
+35.105 -1.698 34.998 -1.353 34.998 -0.912 c
+34.998 -0.662 l
+34.998 -0.192 35.101 0.168 35.307 0.426 c
+35.513 0.679 35.806 0.808 36.188 0.808 c
+36.56 0.808 36.839 0.683 37.027 0.44 c
+37.221 0.205 37.321 -0.148 37.321 -0.618 c
+37.321 -1 l
+35.777 -1 l
+35.785 -1.228 35.829 -1.393 35.91 -1.5 c
+35.997 -1.599 36.134 -1.646 36.321 -1.646 c
+36.575 -1.646 36.791 -1.555 36.968 -1.368 c
+37.277 -1.852 l
+37.177 -1.981 37.034 -2.087 36.851 -2.176 c
+36.674 -2.253 36.475 -2.294 36.263 -2.294 c
+35.777 -0.456 m
+36.556 -0.456 l
+36.556 -0.383 l
+36.556 -0.206 36.527 -0.074 36.468 0.014 c
+36.409 0.11 36.31 0.161 36.174 0.161 c
+36.045 0.161 35.947 0.11 35.88 0.014 c
+35.821 -0.085 35.785 -0.243 35.777 -0.456 c
+38.901 -1.441 m
+38.901 -1.374 38.864 -1.309 38.798 -1.25 c
+38.739 -1.191 38.599 -1.118 38.386 -1.029 c
+38.063 -0.894 37.842 -0.757 37.725 -0.618 c
+37.607 -0.482 37.549 -0.31 37.549 -0.103 c
+37.549 0.161 37.64 0.374 37.828 0.544 c
+38.012 0.72 38.262 0.808 38.577 0.808 c
+38.901 0.808 39.158 0.72 39.357 0.544 c
+39.552 0.374 39.65 0.151 39.65 -0.133 c
+38.871 -0.133 l
+38.871 0.11 38.769 0.235 38.563 0.235 c
+38.482 0.235 38.416 0.205 38.357 0.147 c
+38.305 0.095 38.283 0.029 38.283 -0.059 c
+38.283 -0.129 38.313 -0.188 38.372 -0.235 c
+38.43 -0.287 38.567 -0.36 38.783 -0.456 c
+39.107 -0.574 39.33 -0.706 39.459 -0.853 c
+39.596 -0.992 39.665 -1.176 39.665 -1.411 c
+39.665 -1.676 39.562 -1.893 39.357 -2.058 c
+39.158 -2.216 38.901 -2.294 38.577 -2.294 c
+38.349 -2.294 38.154 -2.249 37.99 -2.161 c
+37.821 -2.073 37.688 -1.956 37.593 -1.808 c
+37.504 -1.654 37.46 -1.485 37.46 -1.309 c
+38.195 -1.309 l
+38.203 -1.449 38.239 -1.551 38.298 -1.617 c
+38.357 -1.687 38.453 -1.721 38.592 -1.721 c
+38.798 -1.721 38.901 -1.628 38.901 -1.441 c
+f
+Q
+ endstream endobj 38 0 obj <</BBox[543.633 459.986 581.22 454.709]/Group 74 0 R/Length 9136/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 544.8536 457.5314 cm
+0 0 m
+-0.706 0 l
+-0.706 -1.617 l
+-1.22 -1.617 l
+-1.22 2.396 l
+-0.088 2.396 l
+0.302 2.396 0.595 2.293 0.794 2.087 c
+1 1.881 1.103 1.579 1.103 1.19 c
+1.103 0.944 1.048 0.727 0.941 0.544 c
+0.831 0.357 0.68 0.213 0.485 0.118 c
+1.25 -1.588 l
+1.25 -1.617 l
+0.721 -1.617 l
+h
+-0.706 0.44 m
+-0.088 0.44 l
+0.118 0.44 0.283 0.507 0.412 0.646 c
+0.537 0.783 0.603 0.962 0.603 1.19 c
+0.603 1.697 0.368 1.955 -0.103 1.955 c
+-0.706 1.955 l
+h
+2.58 -1.676 m
+2.205 -1.676 1.922 -1.569 1.727 -1.353 c
+1.529 -1.129 1.434 -0.802 1.434 -0.368 c
+1.434 0 l
+1.434 0.44 1.525 0.786 1.713 1.043 c
+1.908 1.297 2.183 1.425 2.536 1.425 c
+2.878 1.425 3.132 1.311 3.3 1.087 c
+3.477 0.86 3.568 0.515 3.579 0.044 c
+3.579 -0.265 l
+1.918 -0.265 l
+1.918 -0.339 l
+1.918 -0.661 1.977 -0.897 2.095 -1.044 c
+2.213 -1.183 2.382 -1.249 2.61 -1.249 c
+2.756 -1.249 2.881 -1.228 2.992 -1.176 c
+3.098 -1.118 3.201 -1.029 3.3 -0.912 c
+3.55 -1.22 l
+3.344 -1.525 3.021 -1.676 2.58 -1.676 c
+2.536 0.999 m
+2.33 0.999 2.176 0.929 2.08 0.793 c
+1.981 0.654 1.926 0.44 1.918 0.147 c
+3.094 0.147 l
+3.094 0.22 l
+3.072 0.492 3.021 0.69 2.933 0.808 c
+2.845 0.933 2.712 0.999 2.536 0.999 c
+4.447 1.367 m
+4.462 1.087 l
+4.638 1.311 4.877 1.425 5.182 1.425 c
+5.513 1.425 5.744 1.278 5.873 0.985 c
+6.056 1.278 6.317 1.425 6.652 1.425 c
+7.21 1.425 7.493 1.08 7.504 0.397 c
+7.504 -1.617 l
+7.019 -1.617 l
+7.019 0.353 l
+7.019 0.565 6.978 0.723 6.901 0.823 c
+6.82 0.929 6.689 0.985 6.504 0.985 c
+6.357 0.985 6.24 0.926 6.151 0.808 c
+6.064 0.698 6.008 0.558 5.991 0.382 c
+5.991 -1.617 l
+5.505 -1.617 l
+5.505 0.367 l
+5.494 0.779 5.322 0.985 4.991 0.985 c
+4.744 0.985 4.572 0.86 4.476 0.617 c
+4.476 -1.617 l
+3.992 -1.617 l
+3.992 1.367 l
+h
+7.849 0.014 m
+7.849 0.444 7.952 0.786 8.158 1.043 c
+8.372 1.297 8.651 1.425 8.996 1.425 c
+9.338 1.425 9.613 1.297 9.819 1.043 c
+10.032 0.797 10.147 0.463 10.157 0.044 c
+10.157 -0.265 l
+10.157 -0.699 10.047 -1.04 9.834 -1.294 c
+9.628 -1.551 9.349 -1.676 8.996 -1.676 c
+8.651 -1.676 8.379 -1.555 8.173 -1.309 c
+7.967 -1.066 7.857 -0.732 7.849 -0.309 c
+h
+8.335 -0.265 m
+8.335 -0.581 8.393 -0.823 8.511 -1 c
+8.636 -1.169 8.798 -1.249 8.996 -1.249 c
+9.426 -1.249 9.65 -0.941 9.673 -0.324 c
+9.673 0.014 l
+9.673 0.316 9.606 0.558 9.482 0.735 c
+9.363 0.911 9.202 0.999 8.996 0.999 c
+8.798 0.999 8.636 0.911 8.511 0.735 c
+8.393 0.558 8.335 0.316 8.335 0.014 c
+h
+11.231 2.087 m
+11.231 1.367 l
+11.686 1.367 l
+11.686 0.97 l
+11.231 0.97 l
+11.231 -0.882 l
+11.231 -1 11.248 -1.088 11.289 -1.147 c
+11.326 -1.206 11.396 -1.235 11.495 -1.235 c
+11.554 -1.235 11.616 -1.228 11.686 -1.206 c
+11.686 -1.617 l
+11.568 -1.654 11.454 -1.676 11.348 -1.676 c
+11.15 -1.676 10.999 -1.61 10.893 -1.47 c
+10.793 -1.334 10.745 -1.139 10.745 -0.882 c
+10.745 0.97 l
+10.29 0.97 l
+10.29 1.367 l
+10.745 1.367 l
+10.745 2.087 l
+h
+13.207 -1.676 m
+12.833 -1.676 12.549 -1.569 12.355 -1.353 c
+12.156 -1.129 12.061 -0.802 12.061 -0.368 c
+12.061 0 l
+12.061 0.44 12.152 0.786 12.341 1.043 c
+12.535 1.297 12.811 1.425 13.164 1.425 c
+13.505 1.425 13.758 1.311 13.928 1.087 c
+14.104 0.86 14.196 0.515 14.207 0.044 c
+14.207 -0.265 l
+12.546 -0.265 l
+12.546 -0.339 l
+12.546 -0.661 12.605 -0.897 12.723 -1.044 c
+12.84 -1.183 13.009 -1.249 13.237 -1.249 c
+13.384 -1.249 13.509 -1.228 13.619 -1.176 c
+13.725 -1.118 13.829 -1.029 13.928 -0.912 c
+14.178 -1.22 l
+13.972 -1.525 13.648 -1.676 13.207 -1.676 c
+13.164 0.999 m
+12.958 0.999 12.803 0.929 12.707 0.793 c
+12.609 0.654 12.553 0.44 12.546 0.147 c
+13.722 0.147 l
+13.722 0.22 l
+13.7 0.492 13.648 0.69 13.56 0.808 c
+13.472 0.933 13.34 0.999 13.164 0.999 c
+17.058 0.911 m
+16.989 0.918 16.915 0.926 16.838 0.926 c
+16.581 0.926 16.405 0.786 16.309 0.515 c
+16.309 -1.617 l
+15.824 -1.617 l
+15.824 1.367 l
+16.294 1.367 l
+16.309 1.058 l
+16.434 1.3 16.617 1.425 16.852 1.425 c
+16.93 1.425 16.993 1.411 17.044 1.381 c
+h
+18.437 -1.676 m
+18.062 -1.676 17.779 -1.569 17.584 -1.353 c
+17.386 -1.129 17.29 -0.802 17.29 -0.368 c
+17.29 0 l
+17.29 0.44 17.382 0.786 17.569 1.043 c
+17.764 1.297 18.04 1.425 18.392 1.425 c
+18.734 1.425 18.988 1.311 19.157 1.087 c
+19.333 0.86 19.425 0.515 19.437 0.044 c
+19.437 -0.265 l
+17.775 -0.265 l
+17.775 -0.339 l
+17.775 -0.661 17.834 -0.897 17.951 -1.044 c
+18.069 -1.183 18.238 -1.249 18.466 -1.249 c
+18.613 -1.249 18.738 -1.228 18.849 -1.176 c
+18.955 -1.118 19.057 -1.029 19.157 -0.912 c
+19.406 -1.22 l
+19.201 -1.525 18.878 -1.676 18.437 -1.676 c
+18.392 0.999 m
+18.186 0.999 18.032 0.929 17.937 0.793 c
+17.837 0.654 17.783 0.44 17.775 0.147 c
+18.951 0.147 l
+18.951 0.22 l
+18.929 0.492 18.878 0.69 18.789 0.808 c
+18.701 0.933 18.569 0.999 18.392 0.999 c
+21.993 -0.265 m
+21.993 -0.736 21.909 -1.088 21.744 -1.324 c
+21.575 -1.559 21.336 -1.676 21.024 -1.676 c
+20.719 -1.676 20.487 -1.565 20.333 -1.338 c
+20.333 -2.764 l
+19.848 -2.764 l
+19.848 1.367 l
+20.289 1.367 l
+20.318 1.028 l
+20.472 1.294 20.704 1.425 21.009 1.425 c
+21.34 1.425 21.586 1.308 21.744 1.072 c
+21.909 0.845 21.993 0.507 21.993 0.058 c
+h
+21.509 0.014 m
+21.509 0.345 21.454 0.592 21.347 0.75 c
+21.247 0.904 21.086 0.985 20.862 0.985 c
+20.627 0.985 20.451 0.867 20.333 0.632 c
+20.333 -0.912 l
+20.451 -1.139 20.63 -1.249 20.877 -1.249 c
+21.089 -1.249 21.247 -1.172 21.347 -1.014 c
+21.454 -0.86 21.509 -0.618 21.509 -0.294 c
+h
+22.346 0.014 m
+22.346 0.444 22.45 0.786 22.655 1.043 c
+22.868 1.297 23.148 1.425 23.493 1.425 c
+23.835 1.425 24.11 1.297 24.316 1.043 c
+24.53 0.797 24.644 0.463 24.654 0.044 c
+24.654 -0.265 l
+24.654 -0.699 24.544 -1.04 24.331 -1.294 c
+24.125 -1.551 23.846 -1.676 23.493 -1.676 c
+23.148 -1.676 22.876 -1.555 22.67 -1.309 c
+22.464 -1.066 22.354 -0.732 22.346 -0.309 c
+h
+22.832 -0.265 m
+22.832 -0.581 22.89 -0.823 23.008 -1 c
+23.133 -1.169 23.295 -1.249 23.493 -1.249 c
+23.923 -1.249 24.147 -0.941 24.169 -0.324 c
+24.169 0.014 l
+24.169 0.316 24.103 0.558 23.979 0.735 c
+23.861 0.911 23.699 0.999 23.493 0.999 c
+23.295 0.999 23.133 0.911 23.008 0.735 c
+22.89 0.558 22.832 0.316 22.832 0.014 c
+h
+26.535 -0.853 m
+26.535 -0.746 26.495 -0.659 26.418 -0.588 c
+26.337 -0.511 26.186 -0.423 25.962 -0.324 c
+25.698 -0.217 25.511 -0.125 25.404 -0.044 c
+25.294 0.033 25.216 0.121 25.169 0.22 c
+25.118 0.316 25.095 0.434 25.095 0.573 c
+25.095 0.816 25.184 1.018 25.359 1.176 c
+25.536 1.341 25.76 1.425 26.036 1.425 c
+26.33 1.425 26.566 1.338 26.741 1.161 c
+26.918 0.992 27.006 0.779 27.006 0.515 c
+26.521 0.515 l
+26.521 0.65 26.47 0.764 26.375 0.852 c
+26.286 0.947 26.172 0.999 26.036 0.999 c
+25.889 0.999 25.775 0.959 25.698 0.881 c
+25.617 0.812 25.581 0.712 25.581 0.588 c
+25.581 0.488 25.61 0.411 25.669 0.353 c
+25.727 0.294 25.867 0.213 26.095 0.118 c
+26.454 -0.03 26.701 -0.173 26.83 -0.309 c
+26.965 -0.437 27.036 -0.611 27.036 -0.823 c
+27.036 -1.081 26.94 -1.287 26.757 -1.441 c
+26.58 -1.599 26.344 -1.676 26.051 -1.676 c
+25.735 -1.676 25.481 -1.588 25.286 -1.411 c
+25.099 -1.228 25.007 -0.996 25.007 -0.721 c
+25.492 -0.721 l
+25.5 -0.89 25.551 -1.022 25.639 -1.118 c
+25.735 -1.206 25.874 -1.249 26.051 -1.249 c
+26.205 -1.249 26.323 -1.216 26.404 -1.147 c
+26.492 -1.081 26.535 -0.981 26.535 -0.853 c
+28.021 -1.617 -0.501 2.984 re
+28.05 2.161 m
+28.05 2.072 28.024 1.999 27.977 1.94 c
+27.936 1.888 27.866 1.866 27.771 1.866 c
+27.682 1.866 27.613 1.888 27.565 1.94 c
+27.524 1.999 27.506 2.065 27.506 2.146 c
+27.506 2.234 27.524 2.308 27.565 2.366 c
+27.613 2.425 27.682 2.454 27.771 2.454 c
+27.866 2.454 27.936 2.425 27.977 2.366 c
+28.024 2.308 28.05 2.238 28.05 2.161 c
+29.328 2.087 m
+29.328 1.367 l
+29.785 1.367 l
+29.785 0.97 l
+29.328 0.97 l
+29.328 -0.882 l
+29.328 -1 29.347 -1.088 29.388 -1.147 c
+29.424 -1.206 29.494 -1.235 29.593 -1.235 c
+29.652 -1.235 29.714 -1.228 29.785 -1.206 c
+29.785 -1.617 l
+29.667 -1.654 29.552 -1.676 29.446 -1.676 c
+29.248 -1.676 29.097 -1.61 28.991 -1.47 c
+28.891 -1.334 28.844 -1.139 28.844 -0.882 c
+28.844 0.97 l
+28.388 0.97 l
+28.388 1.367 l
+28.844 1.367 l
+28.844 2.087 l
+h
+30.097 0.014 m
+30.097 0.444 30.2 0.786 30.406 1.043 c
+30.618 1.297 30.898 1.425 31.243 1.425 c
+31.585 1.425 31.861 1.297 32.066 1.043 c
+32.28 0.797 32.393 0.463 32.405 0.044 c
+32.405 -0.265 l
+32.405 -0.699 32.295 -1.04 32.081 -1.294 c
+31.875 -1.551 31.596 -1.676 31.243 -1.676 c
+30.898 -1.676 30.626 -1.555 30.42 -1.309 c
+30.215 -1.066 30.104 -0.732 30.097 -0.309 c
+h
+30.582 -0.265 m
+30.582 -0.581 30.641 -0.823 30.758 -1 c
+30.883 -1.169 31.044 -1.249 31.243 -1.249 c
+31.673 -1.249 31.898 -0.941 31.919 -0.324 c
+31.919 0.014 l
+31.919 0.316 31.853 0.558 31.728 0.735 c
+31.611 0.911 31.449 0.999 31.243 0.999 c
+31.044 0.999 30.883 0.911 30.758 0.735 c
+30.641 0.558 30.582 0.316 30.582 0.014 c
+h
+34.076 0.911 m
+34.007 0.918 33.933 0.926 33.856 0.926 c
+33.598 0.926 33.423 0.786 33.327 0.515 c
+33.327 -1.617 l
+32.842 -1.617 l
+32.842 1.367 l
+33.313 1.367 l
+33.327 1.058 l
+33.452 1.3 33.635 1.425 33.87 1.425 c
+33.948 1.425 34.011 1.411 34.061 1.381 c
+h
+35.308 -0.809 m
+35.852 1.367 l
+36.366 1.367 l
+35.395 -2.043 l
+35.326 -2.297 35.223 -2.488 35.087 -2.617 c
+34.948 -2.753 34.797 -2.822 34.631 -2.822 c
+34.562 -2.822 34.477 -2.808 34.381 -2.778 c
+34.381 -2.367 l
+34.485 -2.382 l
+34.62 -2.382 34.726 -2.345 34.807 -2.278 c
+34.896 -2.209 34.962 -2.091 35.013 -1.926 c
+35.102 -1.588 l
+34.234 1.367 l
+34.763 1.367 l
+h
+f
+Q
+ endstream endobj 39 0 obj <</BBox[547.65 453.562 561.199 449.27]/Group 75 0 R/Length 3370/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 548.1057 452.3131 cm
+0 0 m
+0.015 -0.324 l
+0.198 -0.07 0.441 0.058 0.736 0.058 c
+1.264 0.058 1.533 -0.294 1.544 -1 c
+1.544 -2.984 l
+1.058 -2.984 l
+1.058 -1.029 l
+1.058 -0.794 1.018 -0.628 0.941 -0.53 c
+0.86 -0.434 0.742 -0.382 0.588 -0.382 c
+0.47 -0.382 0.36 -0.422 0.265 -0.5 c
+0.166 -0.58 0.088 -0.688 0.03 -0.823 c
+0.03 -2.984 l
+-0.455 -2.984 l
+-0.455 0 l
+h
+3.628 -2.984 m
+3.598 -2.918 3.576 -2.808 3.568 -2.66 c
+3.392 -2.918 3.172 -3.043 2.907 -3.043 c
+2.631 -3.043 2.415 -2.969 2.261 -2.822 c
+2.113 -2.668 2.04 -2.452 2.04 -2.176 c
+2.04 -1.874 2.143 -1.632 2.348 -1.455 c
+2.554 -1.272 2.837 -1.176 3.201 -1.176 c
+3.554 -1.176 l
+3.554 -0.852 l
+3.554 -0.676 3.514 -0.555 3.437 -0.485 c
+3.356 -0.408 3.238 -0.368 3.084 -0.368 c
+2.937 -0.368 2.812 -0.412 2.716 -0.5 c
+2.628 -0.588 2.583 -0.698 2.583 -0.823 c
+2.099 -0.823 l
+2.099 -0.676 2.143 -0.537 2.231 -0.397 c
+2.319 -0.25 2.437 -0.14 2.583 -0.059 c
+2.739 0.018 2.911 0.058 3.098 0.058 c
+3.41 0.058 3.645 -0.023 3.803 -0.177 c
+3.958 -0.324 4.039 -0.544 4.039 -0.838 c
+4.039 -2.338 l
+4.046 -2.573 4.083 -2.774 4.142 -2.94 c
+4.142 -2.984 l
+h
+2.98 -2.602 m
+3.098 -2.602 3.209 -2.569 3.319 -2.499 c
+3.425 -2.433 3.502 -2.348 3.554 -2.249 c
+3.554 -1.544 l
+3.289 -1.544 l
+3.054 -1.544 2.866 -1.595 2.731 -1.69 c
+2.602 -1.79 2.54 -1.933 2.54 -2.117 c
+2.54 -2.286 2.569 -2.407 2.628 -2.484 c
+2.694 -2.565 2.812 -2.602 2.98 -2.602 c
+5.075 0 m
+5.09 -0.279 l
+5.266 -0.056 5.505 0.058 5.81 0.058 c
+6.141 0.058 6.373 -0.088 6.501 -0.382 c
+6.685 -0.088 6.945 0.058 7.28 0.058 c
+7.838 0.058 8.121 -0.287 8.133 -0.97 c
+8.133 -2.984 l
+7.647 -2.984 l
+7.647 -1.014 l
+7.647 -0.802 7.607 -0.643 7.53 -0.544 c
+7.449 -0.437 7.317 -0.382 7.133 -0.382 c
+6.986 -0.382 6.868 -0.441 6.78 -0.559 c
+6.692 -0.669 6.637 -0.809 6.619 -0.985 c
+6.619 -2.984 l
+6.134 -2.984 l
+6.134 -1 l
+6.122 -0.588 5.95 -0.382 5.619 -0.382 c
+5.373 -0.382 5.2 -0.507 5.104 -0.75 c
+5.104 -2.984 l
+4.62 -2.984 l
+4.62 0 l
+h
+9.639 -3.043 m
+9.264 -3.043 8.981 -2.936 8.787 -2.72 c
+8.588 -2.496 8.493 -2.168 8.493 -1.735 c
+8.493 -1.367 l
+8.493 -0.927 8.584 -0.58 8.772 -0.324 c
+8.967 -0.07 9.243 0.058 9.596 0.058 c
+9.937 0.058 10.19 -0.056 10.359 -0.279 c
+10.536 -0.507 10.628 -0.852 10.639 -1.323 c
+10.639 -1.632 l
+8.978 -1.632 l
+8.978 -1.706 l
+8.978 -2.028 9.037 -2.263 9.154 -2.411 c
+9.272 -2.55 9.441 -2.616 9.669 -2.616 c
+9.816 -2.616 9.941 -2.595 10.051 -2.543 c
+10.157 -2.484 10.261 -2.396 10.359 -2.278 c
+10.61 -2.587 l
+10.404 -2.892 10.08 -3.043 9.639 -3.043 c
+9.596 -0.368 m
+9.39 -0.368 9.235 -0.437 9.139 -0.574 c
+9.041 -0.713 8.985 -0.927 8.978 -1.22 c
+10.153 -1.22 l
+10.153 -1.147 l
+10.132 -0.875 10.08 -0.676 9.993 -0.559 c
+9.904 -0.434 9.771 -0.368 9.596 -0.368 c
+10.947 -1.353 m
+10.947 -0.893 11.028 -0.544 11.198 -0.309 c
+11.374 -0.067 11.624 0.058 11.947 0.058 c
+12.23 0.058 12.451 -0.059 12.609 -0.294 c
+12.609 1.249 l
+13.093 1.249 l
+13.093 -2.984 l
+12.653 -2.984 l
+12.623 -2.66 l
+12.465 -2.918 12.241 -3.043 11.947 -3.043 c
+11.631 -3.043 11.389 -2.926 11.212 -2.691 c
+11.036 -2.448 10.947 -2.103 10.947 -1.661 c
+h
+11.433 -1.632 m
+11.433 -1.966 11.481 -2.213 11.58 -2.367 c
+11.676 -2.525 11.836 -2.602 12.065 -2.602 c
+12.307 -2.602 12.491 -2.484 12.609 -2.249 c
+12.609 -0.735 l
+12.48 -0.5 12.3 -0.382 12.065 -0.382 c
+11.836 -0.382 11.676 -0.463 11.58 -0.617 c
+11.481 -0.775 11.433 -1.014 11.433 -1.338 c
+h
+f
+Q
+ endstream endobj 40 0 obj <</BBox[628.727 237.005 656.757 232.713]/Group 76 0 R/Length 5146/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 630.1232 234.7709 cm
+0 0 m
+0.808 2.013 l
+1.382 2.013 l
+0.25 -0.5 l
+0.25 -1.999 l
+-0.264 -1.999 l
+-0.264 -0.5 l
+-1.396 2.013 l
+-0.823 2.013 l
+h
+1.386 -0.368 m
+1.386 0.062 1.488 0.404 1.694 0.661 c
+1.907 0.914 2.186 1.043 2.531 1.043 c
+2.874 1.043 3.15 0.914 3.355 0.661 c
+3.568 0.415 3.682 0.081 3.693 -0.339 c
+3.693 -0.647 l
+3.693 -1.081 3.583 -1.422 3.37 -1.676 c
+3.164 -1.933 2.884 -2.058 2.531 -2.058 c
+2.186 -2.058 1.914 -1.937 1.708 -1.691 c
+1.503 -1.448 1.392 -1.114 1.386 -0.691 c
+h
+1.87 -0.647 m
+1.87 -0.963 1.93 -1.206 2.047 -1.382 c
+2.172 -1.551 2.333 -1.632 2.531 -1.632 c
+2.961 -1.632 3.186 -1.324 3.208 -0.706 c
+3.208 -0.368 l
+3.208 -0.067 3.142 0.176 3.017 0.353 c
+2.899 0.529 2.738 0.617 2.531 0.617 c
+2.333 0.617 2.172 0.529 2.047 0.353 c
+1.93 0.176 1.87 -0.067 1.87 -0.368 c
+h
+5.644 -1.735 m
+5.486 -1.952 5.251 -2.058 4.939 -2.058 c
+4.674 -2.058 4.469 -1.966 4.322 -1.779 c
+4.182 -1.595 4.116 -1.32 4.116 -0.956 c
+4.116 0.985 l
+4.601 0.985 l
+4.601 -0.927 l
+4.601 -1.389 4.74 -1.617 5.027 -1.617 c
+5.321 -1.617 5.519 -1.484 5.629 -1.22 c
+5.629 0.985 l
+6.13 0.985 l
+6.13 -1.999 l
+5.659 -1.999 l
+h
+7.783 -0.368 m
+7.783 0.091 7.864 0.44 8.033 0.675 c
+8.21 0.918 8.459 1.043 8.783 1.043 c
+9.066 1.043 9.286 0.926 9.444 0.691 c
+9.444 2.234 l
+9.93 2.234 l
+9.93 -1.999 l
+9.488 -1.999 l
+9.459 -1.676 l
+9.301 -1.933 9.076 -2.058 8.783 -2.058 c
+8.467 -2.058 8.224 -1.941 8.048 -1.706 c
+7.871 -1.463 7.783 -1.118 7.783 -0.676 c
+h
+8.268 -0.647 m
+8.268 -0.981 8.316 -1.228 8.415 -1.382 c
+8.511 -1.54 8.673 -1.617 8.9 -1.617 c
+9.143 -1.617 9.326 -1.5 9.444 -1.264 c
+9.444 0.249 l
+9.315 0.484 9.135 0.602 8.9 0.602 c
+8.673 0.602 8.511 0.521 8.415 0.367 c
+8.316 0.209 8.268 -0.03 8.268 -0.353 c
+h
+10.396 -0.368 m
+10.396 0.062 10.499 0.404 10.705 0.661 c
+10.917 0.914 11.197 1.043 11.543 1.043 c
+11.884 1.043 12.16 0.914 12.366 0.661 c
+12.579 0.415 12.692 0.081 12.704 -0.339 c
+12.704 -0.647 l
+12.704 -1.081 12.594 -1.422 12.38 -1.676 c
+12.174 -1.933 11.896 -2.058 11.543 -2.058 c
+11.197 -2.058 10.925 -1.937 10.72 -1.691 c
+10.514 -1.448 10.404 -1.114 10.396 -0.691 c
+h
+10.881 -0.647 m
+10.881 -0.963 10.94 -1.206 11.057 -1.382 c
+11.183 -1.551 11.344 -1.632 11.543 -1.632 c
+11.973 -1.632 12.197 -1.324 12.218 -0.706 c
+12.218 -0.368 l
+12.218 -0.067 12.153 0.176 12.027 0.353 c
+11.91 0.529 11.748 0.617 11.543 0.617 c
+11.344 0.617 11.183 0.529 11.057 0.353 c
+10.94 0.176 10.881 -0.067 10.881 -0.368 c
+h
+15.879 -1.999 m
+15.85 -1.933 15.827 -1.823 15.819 -1.676 c
+15.644 -1.933 15.423 -2.058 15.158 -2.058 c
+14.883 -2.058 14.666 -1.985 14.512 -1.837 c
+14.365 -1.683 14.291 -1.467 14.291 -1.191 c
+14.291 -0.89 14.394 -0.647 14.6 -0.47 c
+14.805 -0.287 15.089 -0.191 15.453 -0.191 c
+15.805 -0.191 l
+15.805 0.132 l
+15.805 0.309 15.765 0.43 15.688 0.5 c
+15.607 0.577 15.489 0.617 15.335 0.617 c
+15.188 0.617 15.063 0.573 14.967 0.484 c
+14.879 0.397 14.835 0.286 14.835 0.162 c
+14.35 0.162 l
+14.35 0.309 14.394 0.448 14.483 0.588 c
+14.57 0.735 14.688 0.845 14.835 0.926 c
+14.99 1.003 15.162 1.043 15.349 1.043 c
+15.661 1.043 15.897 0.962 16.055 0.808 c
+16.209 0.661 16.29 0.44 16.29 0.147 c
+16.29 -1.353 l
+16.297 -1.588 16.334 -1.79 16.393 -1.955 c
+16.393 -1.999 l
+h
+15.232 -1.617 m
+15.349 -1.617 15.46 -1.584 15.57 -1.515 c
+15.676 -1.448 15.754 -1.364 15.805 -1.264 c
+15.805 -0.559 l
+15.541 -0.559 l
+15.306 -0.559 15.118 -0.611 14.982 -0.706 c
+14.853 -0.805 14.791 -0.948 14.791 -1.132 c
+14.791 -1.301 14.82 -1.422 14.879 -1.5 c
+14.946 -1.58 15.063 -1.617 15.232 -1.617 c
+17.4 -1.999 -0.5 4.233 re
+18.601 -1.999 -0.5 4.233 re
+21.111 1.705 m
+21.111 0.985 l
+21.567 0.985 l
+21.567 0.588 l
+21.111 0.588 l
+21.111 -1.264 l
+21.111 -1.382 21.13 -1.47 21.17 -1.529 c
+21.207 -1.588 21.277 -1.617 21.376 -1.617 c
+21.435 -1.617 21.498 -1.61 21.567 -1.588 c
+21.567 -1.999 l
+21.45 -2.036 21.336 -2.058 21.229 -2.058 c
+21.03 -2.058 20.88 -1.992 20.773 -1.852 c
+20.675 -1.717 20.627 -1.521 20.627 -1.264 c
+20.627 0.588 l
+20.17 0.588 l
+20.17 0.985 l
+20.627 0.985 l
+20.627 1.705 l
+h
+22.519 0.675 m
+22.703 0.918 22.938 1.043 23.225 1.043 c
+23.754 1.043 24.022 0.691 24.033 -0.015 c
+24.033 -1.999 l
+23.548 -1.999 l
+23.548 -0.044 l
+23.548 0.191 23.507 0.357 23.43 0.455 c
+23.349 0.551 23.232 0.602 23.077 0.602 c
+22.96 0.602 22.85 0.562 22.755 0.484 c
+22.655 0.404 22.578 0.297 22.519 0.162 c
+22.519 -1.999 l
+22.034 -1.999 l
+22.034 2.234 l
+22.519 2.234 l
+h
+25.635 -2.058 m
+25.261 -2.058 24.978 -1.952 24.783 -1.735 c
+24.584 -1.511 24.488 -1.183 24.488 -0.75 c
+24.488 -0.383 l
+24.488 0.058 24.581 0.404 24.768 0.661 c
+24.962 0.914 25.238 1.043 25.591 1.043 c
+25.932 1.043 26.186 0.929 26.356 0.706 c
+26.532 0.478 26.624 0.132 26.634 -0.339 c
+26.634 -0.647 l
+24.974 -0.647 l
+24.974 -0.721 l
+24.974 -1.044 25.032 -1.279 25.15 -1.426 c
+25.267 -1.565 25.437 -1.632 25.664 -1.632 c
+25.812 -1.632 25.936 -1.61 26.047 -1.559 c
+26.154 -1.5 26.256 -1.411 26.356 -1.294 c
+26.605 -1.602 l
+26.399 -1.908 26.076 -2.058 25.635 -2.058 c
+25.591 0.617 m
+25.385 0.617 25.231 0.548 25.136 0.411 c
+25.036 0.272 24.981 0.058 24.974 -0.235 c
+26.15 -0.235 l
+26.15 -0.162 l
+26.127 0.11 26.076 0.309 25.988 0.426 c
+25.899 0.551 25.768 0.617 25.591 0.617 c
+f
+Q
+ endstream endobj 41 0 obj <</BBox[562.794 453.503 574.785 448.153]/Group 77 0 R/Length 2760/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 562.7944 450.9161 cm
+0 0 m
+0 0.449 0.106 0.802 0.323 1.058 c
+0.536 1.324 0.83 1.455 1.205 1.455 c
+1.587 1.455 1.881 1.324 2.087 1.058 c
+2.3 0.802 2.41 0.449 2.41 0 c
+2.41 -0.191 l
+2.41 -0.654 2.3 -1.01 2.087 -1.263 c
+1.881 -1.521 1.587 -1.646 1.205 -1.646 c
+0.823 -1.646 0.521 -1.521 0.309 -1.263 c
+0.103 -0.999 0 -0.643 0 -0.191 c
+h
+0.779 -0.191 m
+0.779 -0.731 0.918 -0.999 1.205 -0.999 c
+1.469 -0.999 1.61 -0.775 1.631 -0.323 c
+1.646 0 l
+1.646 0.272 1.606 0.474 1.529 0.603 c
+1.448 0.739 1.341 0.809 1.205 0.809 c
+1.076 0.809 0.974 0.739 0.897 0.603 c
+0.816 0.474 0.779 0.272 0.779 0 c
+h
+4.182 0.647 m
+3.931 0.662 l
+3.715 0.662 3.568 0.566 3.491 0.383 c
+3.491 -1.587 l
+2.711 -1.587 l
+2.711 1.397 l
+3.447 1.397 l
+3.476 1.073 l
+3.594 1.327 3.759 1.455 3.976 1.455 c
+4.064 1.455 4.137 1.441 4.197 1.411 c
+h
+5.284 -1.587 -0.779 2.984 re
+4.461 2.161 m
+4.461 2.286 4.498 2.389 4.579 2.469 c
+4.656 2.547 4.758 2.587 4.887 2.587 c
+5.012 2.587 5.115 2.547 5.196 2.469 c
+5.273 2.389 5.313 2.286 5.313 2.161 c
+5.313 2.043 5.273 1.945 5.196 1.867 c
+5.126 1.786 5.024 1.75 4.887 1.75 c
+4.758 1.75 4.656 1.786 4.579 1.867 c
+4.498 1.945 4.461 2.043 4.461 2.161 c
+5.703 0 m
+5.703 0.489 5.791 0.853 5.967 1.088 c
+6.144 1.33 6.387 1.455 6.703 1.455 c
+6.975 1.455 7.187 1.345 7.335 1.133 c
+7.364 1.397 l
+8.07 1.397 l
+8.07 -1.587 l
+8.07 -1.969 7.96 -2.256 7.746 -2.454 c
+7.53 -2.66 7.224 -2.763 6.834 -2.763 c
+6.666 -2.763 6.489 -2.726 6.306 -2.66 c
+6.129 -2.601 5.997 -2.516 5.909 -2.41 c
+6.173 -1.881 l
+6.25 -1.947 6.35 -2.006 6.468 -2.057 c
+6.585 -2.105 6.692 -2.131 6.79 -2.131 c
+6.967 -2.131 7.092 -2.087 7.173 -1.999 c
+7.25 -1.918 7.291 -1.786 7.291 -1.602 c
+7.291 -1.352 l
+7.143 -1.55 6.945 -1.646 6.703 -1.646 c
+6.387 -1.646 6.144 -1.525 5.967 -1.278 c
+5.791 -1.024 5.703 -0.672 5.703 -0.22 c
+h
+6.482 -0.205 m
+6.482 -0.481 6.515 -0.683 6.585 -0.808 c
+6.662 -0.926 6.78 -0.984 6.938 -0.984 c
+7.092 -0.984 7.21 -0.933 7.291 -0.823 c
+7.291 0.603 l
+7.21 0.728 7.092 0.794 6.938 0.794 c
+6.78 0.794 6.662 0.728 6.585 0.603 c
+6.515 0.474 6.482 0.272 6.482 0 c
+h
+9.267 -1.587 -0.779 2.984 re
+8.444 2.161 m
+8.444 2.286 8.481 2.389 8.562 2.469 c
+8.639 2.547 8.742 2.587 8.87 2.587 c
+8.995 2.587 9.099 2.547 9.18 2.469 c
+9.257 2.389 9.296 2.286 9.296 2.161 c
+9.296 2.043 9.257 1.945 9.18 1.867 c
+9.109 1.786 9.007 1.75 8.87 1.75 c
+8.742 1.75 8.639 1.786 8.562 1.867 c
+8.481 1.945 8.444 2.043 8.444 2.161 c
+10.491 1.397 m
+10.506 1.103 l
+10.682 1.338 10.91 1.455 11.197 1.455 c
+11.715 1.455 11.979 1.092 11.991 0.368 c
+11.991 -1.587 l
+11.212 -1.587 l
+11.212 0.31 l
+11.212 0.485 11.185 0.607 11.138 0.676 c
+11.087 0.754 10.998 0.794 10.873 0.794 c
+10.726 0.794 10.612 0.721 10.535 0.574 c
+10.535 -1.587 l
+9.756 -1.587 l
+9.756 1.397 l
+h
+f
+Q
+ endstream endobj 42 0 obj <</BBox[575.101 453.401 577.027 449.3]/Group 78 0 R/Length 944/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 575.7475 450.4608 cm
+0 0 m
+0 0.214 0.019 0.389 0.059 0.53 c
+0.106 0.676 0.221 0.852 0.397 1.058 c
+0.603 1.309 l
+0.728 1.492 0.794 1.679 0.794 1.866 c
+0.794 2.061 0.75 2.213 0.661 2.323 c
+0.58 2.429 0.47 2.484 0.324 2.484 c
+0.184 2.484 0.067 2.433 -0.029 2.337 c
+-0.118 2.238 -0.162 2.105 -0.162 1.941 c
+-0.646 1.941 l
+-0.646 2.242 -0.559 2.484 -0.382 2.66 c
+-0.199 2.844 0.037 2.94 0.324 2.94 c
+0.617 2.94 0.846 2.844 1.014 2.66 c
+1.191 2.473 1.279 2.219 1.279 1.897 c
+1.279 1.58 1.151 1.268 0.896 0.956 c
+0.647 0.647 l
+0.537 0.5 0.485 0.283 0.485 0 c
+h
+0.264 -0.588 m
+0.353 -0.588 0.42 -0.617 0.47 -0.676 c
+0.518 -0.727 0.544 -0.794 0.544 -0.882 c
+0.544 -0.962 0.518 -1.029 0.47 -1.088 c
+0.42 -1.135 0.353 -1.161 0.264 -1.161 c
+0.166 -1.161 0.092 -1.135 0.044 -1.088 c
+0.004 -1.029 -0.014 -0.962 -0.014 -0.882 c
+-0.014 -0.794 0.004 -0.727 0.044 -0.676 c
+0.092 -0.617 0.166 -0.588 0.264 -0.588 c
+f
+Q
+ endstream endobj 43 0 obj <</BBox[539.629 446.977 584.802 441.539]/Group 79 0 R/Length 9416/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 541.0248 444.743 cm
+0 0 m
+0.808 2.014 l
+1.382 2.014 l
+0.25 -0.5 l
+0.25 -1.999 l
+-0.264 -1.999 l
+-0.264 -0.5 l
+-1.396 2.014 l
+-0.823 2.014 l
+h
+1.386 -0.368 m
+1.386 0.062 1.488 0.405 1.694 0.661 c
+1.907 0.915 2.186 1.043 2.532 1.043 c
+2.874 1.043 3.15 0.915 3.355 0.661 c
+3.568 0.415 3.682 0.081 3.693 -0.338 c
+3.693 -0.647 l
+3.693 -1.081 3.583 -1.422 3.37 -1.675 c
+3.164 -1.933 2.884 -2.058 2.532 -2.058 c
+2.186 -2.058 1.914 -1.937 1.708 -1.69 c
+1.503 -1.448 1.393 -1.114 1.386 -0.69 c
+h
+1.87 -0.647 m
+1.87 -0.963 1.93 -1.205 2.047 -1.382 c
+2.172 -1.551 2.334 -1.631 2.532 -1.631 c
+2.962 -1.631 3.186 -1.323 3.208 -0.706 c
+3.208 -0.368 l
+3.208 -0.066 3.142 0.176 3.017 0.353 c
+2.899 0.529 2.738 0.617 2.532 0.617 c
+2.334 0.617 2.172 0.529 2.047 0.353 c
+1.93 0.176 1.87 -0.066 1.87 -0.368 c
+h
+5.644 -1.735 m
+5.486 -1.951 5.251 -2.058 4.939 -2.058 c
+4.675 -2.058 4.469 -1.966 4.322 -1.779 c
+4.182 -1.595 4.116 -1.319 4.116 -0.956 c
+4.116 0.985 l
+4.601 0.985 l
+4.601 -0.926 l
+4.601 -1.389 4.74 -1.617 5.027 -1.617 c
+5.321 -1.617 5.519 -1.484 5.629 -1.22 c
+5.629 0.985 l
+6.13 0.985 l
+6.13 -1.999 l
+5.659 -1.999 l
+h
+7.008 1.926 m
+6.963 0.912 l
+6.596 0.912 l
+6.611 2.234 l
+7.008 2.234 l
+h
+8.438 -1.264 m
+9.01 0.985 l
+9.51 0.985 l
+8.613 -1.999 l
+8.261 -1.999 l
+7.364 0.985 l
+7.85 0.985 l
+h
+10.881 -2.058 m
+10.506 -2.058 10.223 -1.951 10.028 -1.735 c
+9.83 -1.511 9.735 -1.183 9.735 -0.75 c
+9.735 -0.382 l
+9.735 0.058 9.826 0.405 10.014 0.661 c
+10.209 0.915 10.485 1.043 10.837 1.043 c
+11.179 1.043 11.432 0.929 11.601 0.706 c
+11.778 0.478 11.869 0.133 11.881 -0.338 c
+11.881 -0.647 l
+10.219 -0.647 l
+10.219 -0.721 l
+10.219 -1.043 10.279 -1.278 10.396 -1.426 c
+10.514 -1.565 10.683 -1.631 10.911 -1.631 c
+11.057 -1.631 11.183 -1.61 11.293 -1.558 c
+11.399 -1.5 11.502 -1.411 11.601 -1.294 c
+11.852 -1.602 l
+11.645 -1.907 11.322 -2.058 10.881 -2.058 c
+10.837 0.617 m
+10.631 0.617 10.477 0.548 10.381 0.411 c
+10.282 0.272 10.227 0.058 10.219 -0.235 c
+11.395 -0.235 l
+11.395 -0.162 l
+11.374 0.11 11.322 0.309 11.233 0.426 c
+11.146 0.551 11.013 0.617 10.837 0.617 c
+15.64 -0.647 m
+15.64 -1.118 15.555 -1.47 15.39 -1.706 c
+15.221 -1.941 14.982 -2.058 14.67 -2.058 c
+14.365 -2.058 14.133 -1.947 13.978 -1.72 c
+13.978 -3.146 l
+13.494 -3.146 l
+13.494 0.985 l
+13.935 0.985 l
+13.964 0.646 l
+14.119 0.912 14.35 1.043 14.655 1.043 c
+14.986 1.043 15.232 0.926 15.39 0.69 c
+15.555 0.463 15.64 0.125 15.64 -0.324 c
+h
+15.154 -0.368 m
+15.154 -0.037 15.1 0.21 14.994 0.368 c
+14.894 0.522 14.732 0.603 14.508 0.603 c
+14.273 0.603 14.096 0.485 13.978 0.25 c
+13.978 -1.294 l
+14.096 -1.521 14.277 -1.631 14.523 -1.631 c
+14.736 -1.631 14.894 -1.554 14.994 -1.396 c
+15.1 -1.242 15.154 -1 15.154 -0.676 c
+h
+17.334 0.529 m
+17.264 0.536 17.19 0.544 17.113 0.544 c
+16.856 0.544 16.68 0.405 16.584 0.133 c
+16.584 -1.999 l
+16.099 -1.999 l
+16.099 0.985 l
+16.569 0.985 l
+16.584 0.676 l
+16.709 0.918 16.893 1.043 17.128 1.043 c
+17.205 1.043 17.268 1.029 17.319 0.999 c
+h
+17.547 -0.368 m
+17.547 0.062 17.65 0.405 17.856 0.661 c
+18.069 0.915 18.348 1.043 18.693 1.043 c
+19.035 1.043 19.311 0.915 19.517 0.661 c
+19.73 0.415 19.844 0.081 19.855 -0.338 c
+19.855 -0.647 l
+19.855 -1.081 19.744 -1.422 19.532 -1.675 c
+19.326 -1.933 19.046 -2.058 18.693 -2.058 c
+18.348 -2.058 18.076 -1.937 17.87 -1.69 c
+17.664 -1.448 17.554 -1.114 17.547 -0.69 c
+h
+18.032 -0.647 m
+18.032 -0.963 18.091 -1.205 18.209 -1.382 c
+18.333 -1.551 18.495 -1.631 18.693 -1.631 c
+19.123 -1.631 19.347 -1.323 19.37 -0.706 c
+19.37 -0.368 l
+19.37 -0.066 19.304 0.176 19.179 0.353 c
+19.061 0.529 18.899 0.617 18.693 0.617 c
+18.495 0.617 18.333 0.529 18.209 0.353 c
+18.091 0.176 18.032 -0.066 18.032 -0.368 c
+h
+22.456 -0.647 m
+22.456 -1.118 22.372 -1.47 22.207 -1.706 c
+22.038 -1.941 21.795 -2.058 21.471 -2.058 c
+21.155 -2.058 20.92 -1.922 20.766 -1.646 c
+20.737 -1.999 l
+20.296 -1.999 l
+20.296 2.234 l
+20.781 2.234 l
+20.781 0.661 l
+20.935 0.915 21.167 1.043 21.471 1.043 c
+21.795 1.043 22.038 0.926 22.207 0.69 c
+22.372 0.455 22.456 0.106 22.456 -0.353 c
+h
+21.972 -0.368 m
+21.972 -0.015 21.92 0.235 21.824 0.382 c
+21.725 0.529 21.564 0.603 21.34 0.603 c
+21.093 0.603 20.906 0.463 20.781 0.191 c
+20.781 -1.22 l
+20.899 -1.484 21.089 -1.617 21.354 -1.617 c
+21.567 -1.617 21.725 -1.544 21.824 -1.396 c
+21.92 -1.242 21.972 -1 21.972 -0.676 c
+h
+24.426 -1.999 m
+24.397 -1.933 24.375 -1.823 24.367 -1.675 c
+24.191 -1.933 23.971 -2.058 23.706 -2.058 c
+23.43 -2.058 23.214 -1.984 23.059 -1.837 c
+22.913 -1.683 22.838 -1.467 22.838 -1.191 c
+22.838 -0.889 22.942 -0.647 23.148 -0.47 c
+23.353 -0.287 23.636 -0.191 24 -0.191 c
+24.353 -0.191 l
+24.353 0.133 l
+24.353 0.309 24.312 0.43 24.235 0.5 c
+24.154 0.577 24.037 0.617 23.883 0.617 c
+23.736 0.617 23.611 0.573 23.515 0.485 c
+23.426 0.397 23.383 0.287 23.383 0.162 c
+22.898 0.162 l
+22.898 0.309 22.942 0.448 23.03 0.588 c
+23.118 0.735 23.235 0.845 23.383 0.926 c
+23.537 1.003 23.709 1.043 23.897 1.043 c
+24.209 1.043 24.444 0.962 24.602 0.808 c
+24.757 0.661 24.837 0.441 24.837 0.147 c
+24.837 -1.353 l
+24.845 -1.588 24.882 -1.789 24.941 -1.955 c
+24.941 -1.999 l
+h
+23.779 -1.617 m
+23.897 -1.617 24.008 -1.584 24.118 -1.514 c
+24.224 -1.448 24.301 -1.363 24.353 -1.264 c
+24.353 -0.559 l
+24.089 -0.559 l
+23.853 -0.559 23.665 -0.611 23.53 -0.706 c
+23.401 -0.805 23.339 -0.948 23.339 -1.132 c
+23.339 -1.301 23.368 -1.422 23.426 -1.5 c
+23.493 -1.58 23.611 -1.617 23.779 -1.617 c
+27.579 -0.647 m
+27.579 -1.118 27.495 -1.47 27.329 -1.706 c
+27.16 -1.941 26.917 -2.058 26.595 -2.058 c
+26.279 -2.058 26.043 -1.922 25.889 -1.646 c
+25.859 -1.999 l
+25.419 -1.999 l
+25.419 2.234 l
+25.903 2.234 l
+25.903 0.661 l
+26.058 0.915 26.289 1.043 26.595 1.043 c
+26.917 1.043 27.16 0.926 27.329 0.69 c
+27.495 0.455 27.579 0.106 27.579 -0.353 c
+h
+27.094 -0.368 m
+27.094 -0.015 27.042 0.235 26.948 0.382 c
+26.848 0.529 26.686 0.603 26.462 0.603 c
+26.216 0.603 26.028 0.463 25.903 0.191 c
+25.903 -1.22 l
+26.021 -1.484 26.212 -1.617 26.477 -1.617 c
+26.69 -1.617 26.848 -1.544 26.948 -1.396 c
+27.042 -1.242 27.094 -1 27.094 -0.676 c
+h
+28.553 -1.999 -0.5 4.233 re
+30.034 -1.191 m
+30.578 0.985 l
+31.092 0.985 l
+30.122 -2.425 l
+30.052 -2.679 29.949 -2.87 29.813 -2.999 c
+29.674 -3.135 29.523 -3.204 29.358 -3.204 c
+29.288 -3.204 29.203 -3.19 29.107 -3.16 c
+29.107 -2.749 l
+29.211 -2.764 l
+29.346 -2.764 29.454 -2.727 29.535 -2.66 c
+29.622 -2.591 29.689 -2.473 29.74 -2.308 c
+29.828 -1.97 l
+28.961 0.985 l
+29.49 0.985 l
+h
+33.076 0.985 m
+33.091 0.706 l
+33.267 0.929 33.506 1.043 33.811 1.043 c
+34.142 1.043 34.374 0.897 34.503 0.603 c
+34.686 0.897 34.948 1.043 35.282 1.043 c
+35.84 1.043 36.123 0.698 36.134 0.015 c
+36.134 -1.999 l
+35.649 -1.999 l
+35.649 -0.029 l
+35.649 0.183 35.609 0.341 35.532 0.441 c
+35.451 0.548 35.318 0.603 35.135 0.603 c
+34.987 0.603 34.869 0.544 34.782 0.426 c
+34.694 0.316 34.638 0.176 34.62 0 c
+34.62 -1.999 l
+34.135 -1.999 l
+34.135 -0.015 l
+34.123 0.397 33.951 0.603 33.62 0.603 c
+33.375 0.603 33.202 0.478 33.106 0.235 c
+33.106 -1.999 l
+32.621 -1.999 l
+32.621 0.985 l
+h
+38.111 -1.999 m
+38.082 -1.933 38.06 -1.823 38.052 -1.675 c
+37.876 -1.933 37.655 -2.058 37.391 -2.058 c
+37.115 -2.058 36.899 -1.984 36.744 -1.837 c
+36.597 -1.683 36.523 -1.467 36.523 -1.191 c
+36.523 -0.889 36.627 -0.647 36.832 -0.47 c
+37.038 -0.287 37.321 -0.191 37.685 -0.191 c
+38.038 -0.191 l
+38.038 0.133 l
+38.038 0.309 37.997 0.43 37.92 0.5 c
+37.839 0.577 37.722 0.617 37.568 0.617 c
+37.42 0.617 37.296 0.573 37.2 0.485 c
+37.111 0.397 37.067 0.287 37.067 0.162 c
+36.583 0.162 l
+36.583 0.309 36.627 0.448 36.714 0.588 c
+36.803 0.735 36.92 0.845 37.067 0.926 c
+37.221 1.003 37.394 1.043 37.582 1.043 c
+37.894 1.043 38.129 0.962 38.287 0.808 c
+38.441 0.661 38.522 0.441 38.522 0.147 c
+38.522 -1.353 l
+38.53 -1.588 38.567 -1.789 38.626 -1.955 c
+38.626 -1.999 l
+h
+37.464 -1.617 m
+37.582 -1.617 37.692 -1.584 37.803 -1.514 c
+37.909 -1.448 37.986 -1.363 38.038 -1.264 c
+38.038 -0.559 l
+37.773 -0.559 l
+37.537 -0.559 37.35 -0.611 37.215 -0.706 c
+37.086 -0.805 37.024 -0.948 37.024 -1.132 c
+37.024 -1.301 37.053 -1.422 37.111 -1.5 c
+37.178 -1.58 37.296 -1.617 37.464 -1.617 c
+39 -0.368 m
+39 0.092 39.081 0.441 39.25 0.676 c
+39.426 0.918 39.677 1.043 40 1.043 c
+40.282 1.043 40.504 0.926 40.662 0.69 c
+40.662 2.234 l
+41.146 2.234 l
+41.146 -1.999 l
+40.706 -1.999 l
+40.676 -1.675 l
+40.518 -1.933 40.294 -2.058 40 -2.058 c
+39.684 -2.058 39.441 -1.941 39.265 -1.706 c
+39.089 -1.463 39 -1.118 39 -0.676 c
+h
+39.486 -0.647 m
+39.486 -0.981 39.533 -1.228 39.632 -1.382 c
+39.728 -1.54 39.889 -1.617 40.118 -1.617 c
+40.36 -1.617 40.544 -1.5 40.662 -1.264 c
+40.662 0.25 l
+40.533 0.485 40.353 0.603 40.118 0.603 c
+39.889 0.603 39.728 0.522 39.632 0.368 c
+39.533 0.21 39.486 -0.029 39.486 -0.353 c
+h
+42.778 -2.058 m
+42.403 -2.058 42.12 -1.951 41.925 -1.735 c
+41.727 -1.511 41.632 -1.183 41.632 -0.75 c
+41.632 -0.382 l
+41.632 0.058 41.724 0.405 41.911 0.661 c
+42.106 0.915 42.381 1.043 42.734 1.043 c
+43.076 1.043 43.33 0.929 43.498 0.706 c
+43.675 0.478 43.766 0.133 43.777 -0.338 c
+43.777 -0.647 l
+42.117 -0.647 l
+42.117 -0.721 l
+42.117 -1.043 42.175 -1.278 42.293 -1.426 c
+42.41 -1.565 42.58 -1.631 42.807 -1.631 c
+42.954 -1.631 43.079 -1.61 43.189 -1.558 c
+43.296 -1.5 43.399 -1.411 43.498 -1.294 c
+43.748 -1.602 l
+43.542 -1.907 43.219 -2.058 42.778 -2.058 c
+42.734 0.617 m
+42.528 0.617 42.374 0.548 42.278 0.411 c
+42.179 0.272 42.123 0.058 42.117 -0.235 c
+43.293 -0.235 l
+43.293 -0.162 l
+43.27 0.11 43.219 0.309 43.131 0.426 c
+43.043 0.551 42.91 0.617 42.734 0.617 c
+f
+Q
+ endstream endobj 44 0 obj <</BBox[541.904 440.392 560.038 434.983]/Group 80 0 R/Length 4112/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 541.9035 437.7458 cm
+0 0 m
+0 0.489 0.087 0.853 0.264 1.088 c
+0.44 1.33 0.683 1.455 0.999 1.455 c
+1.271 1.455 1.484 1.345 1.631 1.133 c
+1.66 1.397 l
+2.366 1.397 l
+2.366 -1.587 l
+2.366 -1.969 2.256 -2.256 2.043 -2.454 c
+1.826 -2.66 1.521 -2.763 1.132 -2.763 c
+0.962 -2.763 0.786 -2.726 0.602 -2.66 c
+0.426 -2.601 0.293 -2.516 0.205 -2.41 c
+0.47 -1.882 l
+0.547 -1.947 0.646 -2.006 0.764 -2.057 c
+0.881 -2.105 0.988 -2.131 1.087 -2.131 c
+1.263 -2.131 1.388 -2.087 1.469 -1.999 c
+1.547 -1.918 1.587 -1.786 1.587 -1.602 c
+1.587 -1.352 l
+1.44 -1.55 1.242 -1.646 0.999 -1.646 c
+0.683 -1.646 0.44 -1.525 0.264 -1.278 c
+0.087 -1.024 0 -0.672 0 -0.22 c
+h
+0.779 -0.205 m
+0.779 -0.481 0.812 -0.683 0.881 -0.808 c
+0.959 -0.926 1.076 -0.984 1.234 -0.984 c
+1.388 -0.984 1.506 -0.933 1.587 -0.823 c
+1.587 0.603 l
+1.506 0.728 1.388 0.794 1.234 0.794 c
+1.076 0.794 0.959 0.728 0.881 0.603 c
+0.812 0.474 0.779 0.272 0.779 0 c
+h
+3.564 -1.587 -0.779 2.984 re
+2.741 2.161 m
+2.741 2.286 2.778 2.389 2.859 2.469 c
+2.936 2.547 3.039 2.587 3.167 2.587 c
+3.293 2.587 3.395 2.547 3.476 2.469 c
+3.553 2.389 3.594 2.286 3.594 2.161 c
+3.594 2.043 3.553 1.945 3.476 1.867 c
+3.406 1.786 3.303 1.75 3.167 1.75 c
+3.039 1.75 2.936 1.786 2.859 1.867 c
+2.778 1.945 2.741 2.043 2.741 2.161 c
+4.983 2.132 m
+4.983 1.397 l
+5.379 1.397 l
+5.379 0.809 l
+4.983 0.809 l
+4.983 -0.675 l
+4.983 -0.793 4.997 -0.878 5.026 -0.926 c
+5.063 -0.966 5.13 -0.984 5.218 -0.984 c
+5.295 -0.984 5.357 -0.977 5.409 -0.955 c
+5.394 -1.572 l
+5.265 -1.62 5.122 -1.646 4.968 -1.646 c
+4.469 -1.646 4.211 -1.359 4.203 -0.779 c
+4.203 0.809 l
+3.865 0.809 l
+3.865 1.397 l
+4.203 1.397 l
+4.203 2.132 l
+h
+7.989 -0.999 m
+8.213 -0.999 8.326 -0.852 8.326 -0.558 c
+9.061 -0.558 l
+9.061 -0.881 8.959 -1.146 8.753 -1.352 c
+8.554 -1.55 8.305 -1.646 8.003 -1.646 c
+7.621 -1.646 7.327 -1.529 7.121 -1.294 c
+6.915 -1.051 6.813 -0.698 6.813 -0.235 c
+6.813 0.015 l
+6.813 0.474 6.908 0.827 7.106 1.073 c
+7.312 1.327 7.607 1.455 7.989 1.455 c
+8.319 1.455 8.58 1.353 8.768 1.147 c
+8.962 0.941 9.061 0.651 9.061 0.279 c
+8.326 0.279 l
+8.326 0.445 8.297 0.574 8.238 0.662 c
+8.187 0.757 8.099 0.809 7.974 0.809 c
+7.845 0.809 7.754 0.757 7.694 0.662 c
+7.636 0.563 7.599 0.375 7.592 0.103 c
+7.592 -0.205 l
+7.592 -0.452 7.599 -0.625 7.621 -0.72 c
+7.65 -0.808 7.688 -0.878 7.738 -0.926 c
+7.798 -0.977 7.879 -0.999 7.989 -0.999 c
+10.15 -1.587 -0.779 4.233 re
+10.568 0 m
+10.568 0.449 10.674 0.802 10.892 1.058 c
+11.104 1.324 11.399 1.455 11.773 1.455 c
+12.156 1.455 12.45 1.324 12.656 1.058 c
+12.868 0.802 12.979 0.449 12.979 0 c
+12.979 -0.191 l
+12.979 -0.654 12.868 -1.01 12.656 -1.263 c
+12.45 -1.521 12.156 -1.646 11.773 -1.646 c
+11.391 -1.646 11.09 -1.521 10.877 -1.263 c
+10.671 -0.999 10.568 -0.643 10.568 -0.191 c
+h
+11.347 -0.191 m
+11.347 -0.731 11.487 -0.999 11.773 -0.999 c
+12.039 -0.999 12.178 -0.775 12.199 -0.323 c
+12.214 0 l
+12.214 0.272 12.174 0.474 12.097 0.603 c
+12.016 0.739 11.91 0.809 11.773 0.809 c
+11.645 0.809 11.542 0.739 11.465 0.603 c
+11.384 0.474 11.347 0.272 11.347 0 c
+h
+14 1.397 m
+14.015 1.103 l
+14.192 1.338 14.42 1.455 14.706 1.455 c
+15.224 1.455 15.489 1.092 15.5 0.368 c
+15.5 -1.587 l
+14.721 -1.587 l
+14.721 0.31 l
+14.721 0.485 14.695 0.607 14.647 0.676 c
+14.596 0.754 14.508 0.794 14.383 0.794 c
+14.235 0.794 14.121 0.721 14.044 0.574 c
+14.044 -1.587 l
+13.265 -1.587 l
+13.265 1.397 l
+h
+17.076 -1.646 m
+16.672 -1.646 16.359 -1.529 16.135 -1.294 c
+15.919 -1.051 15.812 -0.706 15.812 -0.264 c
+15.812 -0.014 l
+15.812 0.456 15.915 0.817 16.12 1.073 c
+16.326 1.327 16.621 1.455 17.003 1.455 c
+17.373 1.455 17.653 1.33 17.841 1.088 c
+18.036 0.853 18.135 0.5 18.135 0.03 c
+18.135 -0.353 l
+16.591 -0.353 l
+16.598 -0.58 16.643 -0.746 16.723 -0.852 c
+16.812 -0.951 16.947 -0.999 17.135 -0.999 c
+17.389 -0.999 17.606 -0.907 17.782 -0.72 c
+18.09 -1.205 l
+17.992 -1.334 17.848 -1.44 17.664 -1.529 c
+17.487 -1.606 17.29 -1.646 17.076 -1.646 c
+16.591 0.192 m
+17.371 0.192 l
+17.371 0.265 l
+17.371 0.441 17.341 0.574 17.282 0.662 c
+17.223 0.757 17.124 0.809 16.988 0.809 c
+16.86 0.809 16.76 0.757 16.694 0.662 c
+16.635 0.563 16.598 0.405 16.591 0.192 c
+f
+Q
+ endstream endobj 45 0 obj <</BBox[561.412 440.45 582.315 436.099]/Group 81 0 R/Length 4153/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 561.7944 436.1588 cm
+0 0 m
+0 2.587 l
+-0.382 2.587 l
+-0.382 2.984 l
+0 2.984 l
+0 3.322 l
+0.008 3.623 0.088 3.858 0.235 4.027 c
+0.382 4.204 0.592 4.292 0.867 4.292 c
+0.963 4.292 1.062 4.278 1.162 4.248 c
+1.132 3.836 l
+1.062 3.844 0.989 3.851 0.912 3.851 c
+0.636 3.851 0.5 3.653 0.5 3.263 c
+0.5 2.984 l
+1 2.984 l
+1 2.587 l
+0.5 2.587 l
+0.5 0 l
+h
+2.693 2.528 m
+2.624 2.535 2.55 2.543 2.473 2.543 c
+2.216 2.543 2.039 2.404 1.945 2.132 c
+1.945 0 l
+1.459 0 l
+1.459 2.984 l
+1.93 2.984 l
+1.945 2.675 l
+2.069 2.917 2.253 3.042 2.488 3.042 c
+2.565 3.042 2.628 3.028 2.679 2.998 c
+h
+2.911 1.631 m
+2.911 2.061 3.013 2.404 3.219 2.66 c
+3.433 2.914 3.711 3.042 4.057 3.042 c
+4.399 3.042 4.675 2.914 4.881 2.66 c
+5.093 2.414 5.207 2.08 5.218 1.661 c
+5.218 1.352 l
+5.218 0.918 5.108 0.577 4.895 0.324 c
+4.69 0.066 4.41 -0.059 4.057 -0.059 c
+3.711 -0.059 3.439 0.062 3.234 0.309 c
+3.028 0.551 2.918 0.885 2.911 1.309 c
+h
+3.396 1.352 m
+3.396 1.036 3.454 0.794 3.572 0.617 c
+3.697 0.448 3.859 0.368 4.057 0.368 c
+4.487 0.368 4.711 0.676 4.733 1.294 c
+4.733 1.631 l
+4.733 1.933 4.667 2.175 4.542 2.352 c
+4.424 2.528 4.263 2.616 4.057 2.616 c
+3.859 2.616 3.697 2.528 3.572 2.352 c
+3.454 2.175 3.396 1.933 3.396 1.631 c
+h
+6.115 2.984 m
+6.13 2.705 l
+6.306 2.928 6.545 3.042 6.85 3.042 c
+7.181 3.042 7.412 2.896 7.541 2.602 c
+7.724 2.896 7.985 3.042 8.32 3.042 c
+8.879 3.042 9.161 2.697 9.172 2.014 c
+9.172 0 l
+8.688 0 l
+8.688 1.97 l
+8.688 2.182 8.647 2.341 8.57 2.44 c
+8.489 2.547 8.357 2.602 8.173 2.602 c
+8.025 2.602 7.908 2.543 7.82 2.425 c
+7.732 2.315 7.676 2.175 7.659 1.999 c
+7.659 0 l
+7.173 0 l
+7.173 1.984 l
+7.162 2.396 6.99 2.602 6.659 2.602 c
+6.413 2.602 6.24 2.477 6.144 2.234 c
+6.144 0 l
+5.659 0 l
+5.659 2.984 l
+h
+11.308 2.675 m
+11.491 2.917 11.726 3.042 12.013 3.042 c
+12.542 3.042 12.81 2.69 12.821 1.984 c
+12.821 0 l
+12.336 0 l
+12.336 1.955 l
+12.336 2.19 12.296 2.356 12.218 2.454 c
+12.138 2.55 12.021 2.602 11.866 2.602 c
+11.748 2.602 11.638 2.562 11.543 2.484 c
+11.443 2.404 11.366 2.296 11.308 2.161 c
+11.308 0 l
+10.822 0 l
+10.822 4.233 l
+11.308 4.233 l
+h
+14.423 -0.059 m
+14.049 -0.059 13.766 0.048 13.571 0.264 c
+13.373 0.488 13.277 0.816 13.277 1.249 c
+13.277 1.617 l
+13.277 2.057 13.369 2.404 13.556 2.66 c
+13.751 2.914 14.026 3.042 14.379 3.042 c
+14.722 3.042 14.975 2.928 15.144 2.705 c
+15.32 2.477 15.412 2.132 15.423 1.661 c
+15.423 1.352 l
+13.762 1.352 l
+13.762 1.278 l
+13.762 0.956 13.821 0.721 13.939 0.573 c
+14.057 0.434 14.225 0.368 14.453 0.368 c
+14.6 0.368 14.725 0.389 14.836 0.441 c
+14.942 0.5 15.044 0.588 15.144 0.706 c
+15.393 0.397 l
+15.188 0.092 14.865 -0.059 14.423 -0.059 c
+14.379 2.616 m
+14.174 2.616 14.02 2.547 13.924 2.41 c
+13.824 2.271 13.77 2.057 13.762 1.764 c
+14.938 1.764 l
+14.938 1.837 l
+14.916 2.109 14.865 2.308 14.776 2.425 c
+14.688 2.55 14.556 2.616 14.379 2.616 c
+17.07 2.528 m
+17 2.535 16.926 2.543 16.849 2.543 c
+16.592 2.543 16.415 2.404 16.32 2.132 c
+16.32 0 l
+15.835 0 l
+15.835 2.984 l
+16.305 2.984 l
+16.32 2.675 l
+16.445 2.917 16.629 3.042 16.864 3.042 c
+16.941 3.042 17.003 3.028 17.055 2.998 c
+h
+18.448 -0.059 m
+18.073 -0.059 17.789 0.048 17.595 0.264 c
+17.396 0.488 17.301 0.816 17.301 1.249 c
+17.301 1.617 l
+17.301 2.057 17.392 2.404 17.581 2.66 c
+17.775 2.914 18.051 3.042 18.404 3.042 c
+18.745 3.042 18.998 2.928 19.168 2.705 c
+19.344 2.477 19.436 2.132 19.447 1.661 c
+19.447 1.352 l
+17.786 1.352 l
+17.786 1.278 l
+17.786 0.956 17.845 0.721 17.962 0.573 c
+18.08 0.434 18.249 0.368 18.477 0.368 c
+18.624 0.368 18.749 0.389 18.859 0.441 c
+18.965 0.5 19.069 0.588 19.168 0.706 c
+19.418 0.397 l
+19.212 0.092 18.888 -0.059 18.448 -0.059 c
+18.404 2.616 m
+18.198 2.616 18.043 2.547 17.947 2.41 c
+17.849 2.271 17.793 2.057 17.786 1.764 c
+18.962 1.764 l
+18.962 1.837 l
+18.94 2.109 18.888 2.308 18.801 2.425 c
+18.712 2.55 18.58 2.616 18.404 2.616 c
+19.888 0.264 m
+19.888 0.353 19.91 0.426 19.962 0.485 c
+20.02 0.544 20.097 0.573 20.197 0.573 c
+20.303 0.573 20.38 0.544 20.432 0.485 c
+20.49 0.426 20.52 0.353 20.52 0.264 c
+20.52 0.183 20.49 0.118 20.432 0.058 c
+20.38 0 20.303 -0.029 20.197 -0.029 c
+20.097 -0.029 20.02 0 19.962 0.058 c
+19.91 0.118 19.888 0.183 19.888 0.264 c
+f
+Q
+ endstream endobj 46 0 obj <</BBox[705.559 469.842 760.518 464.403]/Group 82 0 R/Length 12288/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 707.7492 466.6519 cm
+0 0 m
+-1.353 0 l
+-1.676 -1.043 l
+-2.19 -1.043 l
+-0.897 2.969 l
+-0.47 2.969 l
+0.837 -1.043 l
+0.309 -1.043 l
+h
+-1.22 0.441 m
+-0.133 0.441 l
+-0.676 2.263 l
+h
+1.55 1.94 m
+1.565 1.617 l
+1.749 1.87 1.992 1.999 2.285 1.999 c
+2.815 1.999 3.083 1.646 3.094 0.941 c
+3.094 -1.043 l
+2.609 -1.043 l
+2.609 0.912 l
+2.609 1.147 2.568 1.312 2.491 1.411 c
+2.41 1.507 2.293 1.558 2.138 1.558 c
+2.021 1.558 1.911 1.517 1.815 1.44 c
+1.716 1.359 1.639 1.253 1.58 1.117 c
+1.58 -1.043 l
+1.095 -1.043 l
+1.095 1.94 l
+h
+3.542 0.588 m
+3.542 1.018 3.645 1.359 3.85 1.617 c
+4.064 1.87 4.343 1.999 4.689 1.999 c
+5.03 1.999 5.306 1.87 5.512 1.617 c
+5.725 1.371 5.839 1.037 5.85 0.617 c
+5.85 0.309 l
+5.85 -0.125 5.739 -0.467 5.527 -0.721 c
+5.321 -0.977 5.041 -1.103 4.689 -1.103 c
+4.343 -1.103 4.072 -0.981 3.866 -0.735 c
+3.659 -0.492 3.549 -0.158 3.542 0.264 c
+h
+4.027 0.309 m
+4.027 -0.008 4.086 -0.25 4.203 -0.426 c
+4.328 -0.595 4.49 -0.676 4.689 -0.676 c
+5.119 -0.676 5.342 -0.368 5.365 0.25 c
+5.365 0.588 l
+5.365 0.889 5.298 1.132 5.174 1.309 c
+5.056 1.484 4.895 1.573 4.689 1.573 c
+4.49 1.573 4.328 1.484 4.203 1.309 c
+4.086 1.132 4.027 0.889 4.027 0.588 c
+h
+6.923 2.66 m
+6.923 1.94 l
+7.378 1.94 l
+7.378 1.544 l
+6.923 1.544 l
+6.923 -0.309 l
+6.923 -0.426 6.942 -0.515 6.982 -0.573 c
+7.019 -0.632 7.089 -0.661 7.187 -0.661 c
+7.247 -0.661 7.309 -0.654 7.378 -0.632 c
+7.378 -1.043 l
+7.261 -1.08 7.147 -1.103 7.041 -1.103 c
+6.842 -1.103 6.692 -1.037 6.585 -0.897 c
+6.485 -0.761 6.437 -0.566 6.437 -0.309 c
+6.437 1.544 l
+5.982 1.544 l
+5.982 1.94 l
+6.437 1.94 l
+6.437 2.66 l
+h
+8.326 1.631 m
+8.51 1.874 8.745 1.999 9.032 1.999 c
+9.562 1.999 9.83 1.646 9.841 0.941 c
+9.841 -1.043 l
+9.356 -1.043 l
+9.356 0.912 l
+9.356 1.147 9.315 1.312 9.238 1.411 c
+9.157 1.507 9.04 1.558 8.885 1.558 c
+8.768 1.558 8.658 1.517 8.562 1.44 c
+8.463 1.359 8.386 1.253 8.326 1.117 c
+8.326 -1.043 l
+7.842 -1.043 l
+7.842 3.19 l
+8.326 3.19 l
+h
+11.443 -1.103 m
+11.068 -1.103 10.785 -0.996 10.59 -0.779 c
+10.391 -0.555 10.296 -0.228 10.296 0.206 c
+10.296 0.573 l
+10.296 1.014 10.389 1.359 10.576 1.617 c
+10.771 1.87 11.046 1.999 11.399 1.999 c
+11.74 1.999 11.994 1.885 12.163 1.661 c
+12.34 1.433 12.431 1.087 12.442 0.617 c
+12.442 0.309 l
+10.782 0.309 l
+10.782 0.235 l
+10.782 -0.088 10.84 -0.324 10.958 -0.47 c
+11.075 -0.61 11.245 -0.676 11.472 -0.676 c
+11.619 -0.676 11.744 -0.654 11.854 -0.603 c
+11.961 -0.544 12.064 -0.455 12.163 -0.338 c
+12.413 -0.647 l
+12.207 -0.952 11.883 -1.103 11.443 -1.103 c
+11.399 1.573 m
+11.193 1.573 11.039 1.503 10.943 1.367 c
+10.844 1.228 10.788 1.014 10.782 0.721 c
+11.958 0.721 l
+11.958 0.794 l
+11.935 1.066 11.883 1.264 11.796 1.382 c
+11.708 1.507 11.575 1.573 11.399 1.573 c
+14.089 1.484 m
+14.019 1.492 13.946 1.5 13.868 1.5 c
+13.611 1.5 13.435 1.359 13.339 1.087 c
+13.339 -1.043 l
+12.854 -1.043 l
+12.854 1.94 l
+13.325 1.94 l
+13.339 1.631 l
+13.464 1.874 13.647 1.999 13.883 1.999 c
+13.96 1.999 14.023 1.984 14.074 1.955 c
+h
+16.897 1.484 m
+16.826 1.492 16.753 1.5 16.676 1.5 c
+16.419 1.5 16.242 1.359 16.147 1.087 c
+16.147 -1.043 l
+15.661 -1.043 l
+15.661 1.94 l
+16.132 1.94 l
+16.147 1.631 l
+16.271 1.874 16.455 1.999 16.691 1.999 c
+16.768 1.999 16.83 1.984 16.882 1.955 c
+h
+18.274 -1.103 m
+17.899 -1.103 17.616 -0.996 17.422 -0.779 c
+17.223 -0.555 17.128 -0.228 17.128 0.206 c
+17.128 0.573 l
+17.128 1.014 17.219 1.359 17.407 1.617 c
+17.602 1.87 17.878 1.999 18.231 1.999 c
+18.572 1.999 18.825 1.885 18.994 1.661 c
+19.171 1.433 19.263 1.087 19.274 0.617 c
+19.274 0.309 l
+17.612 0.309 l
+17.612 0.235 l
+17.612 -0.088 17.672 -0.324 17.789 -0.47 c
+17.907 -0.61 18.075 -0.676 18.304 -0.676 c
+18.451 -0.676 18.576 -0.654 18.686 -0.603 c
+18.792 -0.544 18.896 -0.455 18.994 -0.338 c
+19.245 -0.647 l
+19.039 -0.952 18.715 -1.103 18.274 -1.103 c
+18.231 1.573 m
+18.025 1.573 17.87 1.503 17.774 1.367 c
+17.676 1.228 17.62 1.014 17.612 0.721 c
+18.788 0.721 l
+18.788 0.794 l
+18.767 1.066 18.715 1.264 18.627 1.382 c
+18.539 1.507 18.406 1.573 18.231 1.573 c
+20.141 1.94 m
+20.155 1.661 l
+20.332 1.885 20.571 1.999 20.876 1.999 c
+21.207 1.999 21.439 1.852 21.566 1.558 c
+21.751 1.852 22.011 1.999 22.346 1.999 c
+22.904 1.999 23.187 1.654 23.199 0.97 c
+23.199 -1.043 l
+22.713 -1.043 l
+22.713 0.926 l
+22.713 1.139 22.673 1.297 22.596 1.396 c
+22.515 1.503 22.383 1.558 22.199 1.558 c
+22.052 1.558 21.934 1.5 21.846 1.382 c
+21.758 1.272 21.703 1.132 21.684 0.956 c
+21.684 -1.043 l
+21.2 -1.043 l
+21.2 0.941 l
+21.188 1.352 21.015 1.558 20.685 1.558 c
+20.439 1.558 20.266 1.433 20.17 1.191 c
+20.17 -1.043 l
+19.685 -1.043 l
+19.685 1.94 l
+h
+23.544 0.588 m
+23.544 1.018 23.646 1.359 23.852 1.617 c
+24.066 1.87 24.345 1.999 24.691 1.999 c
+25.032 1.999 25.308 1.87 25.514 1.617 c
+25.726 1.371 25.84 1.037 25.852 0.617 c
+25.852 0.309 l
+25.852 -0.125 25.742 -0.467 25.528 -0.721 c
+25.323 -0.977 25.043 -1.103 24.691 -1.103 c
+24.345 -1.103 24.073 -0.981 23.868 -0.735 c
+23.661 -0.492 23.551 -0.158 23.544 0.264 c
+h
+24.029 0.309 m
+24.029 -0.008 24.088 -0.25 24.205 -0.426 c
+24.33 -0.595 24.492 -0.676 24.691 -0.676 c
+25.121 -0.676 25.344 -0.368 25.366 0.25 c
+25.366 0.588 l
+25.366 0.889 25.3 1.132 25.175 1.309 c
+25.057 1.484 24.896 1.573 24.691 1.573 c
+24.492 1.573 24.33 1.484 24.205 1.309 c
+24.088 1.132 24.029 0.889 24.029 0.588 c
+h
+26.925 2.66 m
+26.925 1.94 l
+27.38 1.94 l
+27.38 1.544 l
+26.925 1.544 l
+26.925 -0.309 l
+26.925 -0.426 26.943 -0.515 26.983 -0.573 c
+27.02 -0.632 27.09 -0.661 27.189 -0.661 c
+27.248 -0.661 27.311 -0.654 27.38 -0.632 c
+27.38 -1.043 l
+27.263 -1.08 27.149 -1.103 27.043 -1.103 c
+26.844 -1.103 26.693 -1.037 26.586 -0.897 c
+26.487 -0.761 26.439 -0.566 26.439 -0.309 c
+26.439 1.544 l
+25.984 1.544 l
+25.984 1.94 l
+26.439 1.94 l
+26.439 2.66 l
+h
+28.905 -1.103 m
+28.531 -1.103 28.248 -0.996 28.053 -0.779 c
+27.854 -0.555 27.758 -0.228 27.758 0.206 c
+27.758 0.573 l
+27.758 1.014 27.851 1.359 28.038 1.617 c
+28.233 1.87 28.508 1.999 28.861 1.999 c
+29.203 1.999 29.456 1.885 29.626 1.661 c
+29.802 1.433 29.894 1.087 29.905 0.617 c
+29.905 0.309 l
+28.244 0.309 l
+28.244 0.235 l
+28.244 -0.088 28.303 -0.324 28.421 -0.47 c
+28.538 -0.61 28.707 -0.676 28.934 -0.676 c
+29.082 -0.676 29.206 -0.654 29.317 -0.603 c
+29.424 -0.544 29.526 -0.455 29.626 -0.338 c
+29.875 -0.647 l
+29.67 -0.952 29.346 -1.103 28.905 -1.103 c
+28.861 1.573 m
+28.656 1.573 28.501 1.503 28.406 1.367 c
+28.306 1.228 28.251 1.014 28.244 0.721 c
+29.42 0.721 l
+29.42 0.794 l
+29.398 1.066 29.346 1.264 29.258 1.382 c
+29.17 1.507 29.038 1.573 28.861 1.573 c
+32.753 1.484 m
+32.683 1.492 32.61 1.5 32.533 1.5 c
+32.275 1.5 32.099 1.359 32.003 1.087 c
+32.003 -1.043 l
+31.519 -1.043 l
+31.519 1.94 l
+31.989 1.94 l
+32.003 1.631 l
+32.128 1.874 32.312 1.999 32.547 1.999 c
+32.624 1.999 32.687 1.984 32.738 1.955 c
+h
+34.131 -1.103 m
+33.756 -1.103 33.473 -0.996 33.279 -0.779 c
+33.08 -0.555 32.984 -0.228 32.984 0.206 c
+32.984 0.573 l
+32.984 1.014 33.076 1.359 33.264 1.617 c
+33.458 1.87 33.734 1.999 34.087 1.999 c
+34.428 1.999 34.682 1.885 34.851 1.661 c
+35.028 1.433 35.12 1.087 35.13 0.617 c
+35.13 0.309 l
+33.47 0.309 l
+33.47 0.235 l
+33.47 -0.088 33.528 -0.324 33.646 -0.47 c
+33.763 -0.61 33.932 -0.676 34.16 -0.676 c
+34.307 -0.676 34.432 -0.654 34.542 -0.603 c
+34.649 -0.544 34.752 -0.455 34.851 -0.338 c
+35.101 -0.647 l
+34.895 -0.952 34.572 -1.103 34.131 -1.103 c
+34.087 1.573 m
+33.881 1.573 33.726 1.503 33.631 1.367 c
+33.532 1.228 33.477 1.014 33.47 0.721 c
+34.646 0.721 l
+34.646 0.794 l
+34.623 1.066 34.572 1.264 34.484 1.382 c
+34.395 1.507 34.264 1.573 34.087 1.573 c
+37.688 0.309 m
+37.688 -0.162 37.603 -0.515 37.438 -0.75 c
+37.269 -0.985 37.03 -1.103 36.718 -1.103 c
+36.413 -1.103 36.182 -0.992 36.027 -0.765 c
+36.027 -2.19 l
+35.542 -2.19 l
+35.542 1.94 l
+35.983 1.94 l
+36.012 1.602 l
+36.167 1.866 36.398 1.999 36.703 1.999 c
+37.034 1.999 37.281 1.882 37.438 1.646 c
+37.603 1.419 37.688 1.08 37.688 0.632 c
+h
+37.203 0.588 m
+37.203 0.919 37.148 1.165 37.042 1.323 c
+36.942 1.477 36.78 1.558 36.556 1.558 c
+36.321 1.558 36.145 1.44 36.027 1.205 c
+36.027 -0.338 l
+36.145 -0.566 36.325 -0.676 36.571 -0.676 c
+36.784 -0.676 36.942 -0.599 37.042 -0.441 c
+37.148 -0.287 37.203 -0.044 37.203 0.279 c
+h
+38.041 0.588 m
+38.041 1.018 38.143 1.359 38.349 1.617 c
+38.563 1.87 38.842 1.999 39.187 1.999 c
+39.529 1.999 39.805 1.87 40.011 1.617 c
+40.223 1.371 40.338 1.037 40.348 0.617 c
+40.348 0.309 l
+40.348 -0.125 40.238 -0.467 40.025 -0.721 c
+39.82 -0.977 39.54 -1.103 39.187 -1.103 c
+38.842 -1.103 38.57 -0.981 38.364 -0.735 c
+38.158 -0.492 38.048 -0.158 38.041 0.264 c
+h
+38.526 0.309 m
+38.526 -0.008 38.584 -0.25 38.702 -0.426 c
+38.827 -0.595 38.989 -0.676 39.187 -0.676 c
+39.617 -0.676 39.841 -0.368 39.864 0.25 c
+39.864 0.588 l
+39.864 0.889 39.797 1.132 39.673 1.309 c
+39.554 1.484 39.393 1.573 39.187 1.573 c
+38.989 1.573 38.827 1.484 38.702 1.309 c
+38.584 1.132 38.526 0.889 38.526 0.588 c
+h
+42.23 -0.279 m
+42.23 -0.173 42.189 -0.085 42.112 -0.015 c
+42.031 0.062 41.881 0.151 41.657 0.25 c
+41.393 0.357 41.204 0.449 41.098 0.529 c
+40.988 0.607 40.911 0.694 40.863 0.794 c
+40.811 0.889 40.79 1.007 40.79 1.147 c
+40.79 1.389 40.878 1.591 41.054 1.749 c
+41.231 1.914 41.455 1.999 41.73 1.999 c
+42.025 1.999 42.26 1.911 42.436 1.735 c
+42.612 1.565 42.7 1.352 42.7 1.087 c
+42.216 1.087 l
+42.216 1.224 42.164 1.338 42.068 1.426 c
+41.98 1.521 41.867 1.573 41.73 1.573 c
+41.583 1.573 41.47 1.532 41.393 1.455 c
+41.312 1.386 41.275 1.286 41.275 1.161 c
+41.275 1.062 41.304 0.985 41.362 0.926 c
+41.422 0.867 41.561 0.786 41.79 0.69 c
+42.149 0.544 42.395 0.401 42.524 0.264 c
+42.66 0.136 42.73 -0.037 42.73 -0.25 c
+42.73 -0.507 42.634 -0.713 42.451 -0.867 c
+42.274 -1.025 42.039 -1.103 41.745 -1.103 c
+41.429 -1.103 41.175 -1.014 40.981 -0.838 c
+40.793 -0.654 40.701 -0.422 40.701 -0.147 c
+41.187 -0.147 l
+41.194 -0.316 41.245 -0.449 41.333 -0.544 c
+41.429 -0.632 41.568 -0.676 41.745 -0.676 c
+41.9 -0.676 42.017 -0.643 42.098 -0.573 c
+42.186 -0.507 42.23 -0.408 42.23 -0.279 c
+43.714 -1.043 -0.5 2.983 re
+43.744 2.734 m
+43.744 2.645 43.718 2.572 43.671 2.514 c
+43.631 2.462 43.56 2.44 43.465 2.44 c
+43.377 2.44 43.307 2.462 43.259 2.514 c
+43.219 2.572 43.201 2.639 43.201 2.72 c
+43.201 2.807 43.219 2.881 43.259 2.94 c
+43.307 2.998 43.377 3.028 43.465 3.028 c
+43.56 3.028 43.631 2.998 43.671 2.94 c
+43.718 2.881 43.744 2.811 43.744 2.734 c
+45.023 2.66 m
+45.023 1.94 l
+45.478 1.94 l
+45.478 1.544 l
+45.023 1.544 l
+45.023 -0.309 l
+45.023 -0.426 45.042 -0.515 45.082 -0.573 c
+45.119 -0.632 45.188 -0.661 45.287 -0.661 c
+45.346 -0.661 45.409 -0.654 45.478 -0.632 c
+45.478 -1.043 l
+45.361 -1.08 45.247 -1.103 45.14 -1.103 c
+44.942 -1.103 44.791 -1.037 44.685 -0.897 c
+44.585 -0.761 44.537 -0.566 44.537 -0.309 c
+44.537 1.544 l
+44.082 1.544 l
+44.082 1.94 l
+44.537 1.94 l
+44.537 2.66 l
+h
+45.79 0.588 m
+45.79 1.018 45.894 1.359 46.1 1.617 c
+46.313 1.87 46.592 1.999 46.937 1.999 c
+47.279 1.999 47.554 1.87 47.76 1.617 c
+47.974 1.371 48.088 1.037 48.099 0.617 c
+48.099 0.309 l
+48.099 -0.125 47.988 -0.467 47.776 -0.721 c
+47.569 -0.977 47.29 -1.103 46.937 -1.103 c
+46.592 -1.103 46.32 -0.981 46.114 -0.735 c
+45.908 -0.492 45.798 -0.158 45.79 0.264 c
+h
+46.276 0.309 m
+46.276 -0.008 46.335 -0.25 46.453 -0.426 c
+46.577 -0.595 46.739 -0.676 46.937 -0.676 c
+47.367 -0.676 47.591 -0.368 47.614 0.25 c
+47.614 0.588 l
+47.614 0.889 47.548 1.132 47.423 1.309 c
+47.305 1.484 47.143 1.573 46.937 1.573 c
+46.739 1.573 46.577 1.484 46.453 1.309 c
+46.335 1.132 46.276 0.889 46.276 0.588 c
+h
+49.771 1.484 m
+49.701 1.492 49.628 1.5 49.551 1.5 c
+49.293 1.5 49.117 1.359 49.021 1.087 c
+49.021 -1.043 l
+48.536 -1.043 l
+48.536 1.94 l
+49.006 1.94 l
+49.021 1.631 l
+49.146 1.874 49.33 1.999 49.565 1.999 c
+49.642 1.999 49.705 1.984 49.756 1.955 c
+h
+51.001 -0.235 m
+51.546 1.94 l
+52.06 1.94 l
+51.09 -1.47 l
+51.02 -1.723 50.918 -1.914 50.781 -2.043 c
+50.642 -2.179 50.491 -2.249 50.326 -2.249 c
+50.256 -2.249 50.172 -2.234 50.076 -2.205 c
+50.076 -1.793 l
+50.178 -1.808 l
+50.315 -1.808 50.421 -1.771 50.502 -1.706 c
+50.59 -1.635 50.656 -1.517 50.708 -1.353 c
+50.796 -1.014 l
+49.929 1.94 l
+50.457 1.94 l
+h
+52.138 -0.779 m
+52.138 -0.69 52.159 -0.617 52.211 -0.559 c
+52.269 -0.5 52.346 -0.47 52.446 -0.47 c
+52.553 -0.47 52.63 -0.5 52.681 -0.559 c
+52.74 -0.617 52.769 -0.69 52.769 -0.779 c
+52.769 -0.86 52.74 -0.926 52.681 -0.985 c
+52.63 -1.043 52.553 -1.073 52.446 -1.073 c
+52.346 -1.073 52.269 -1.043 52.211 -0.985 c
+52.159 -0.926 52.138 -0.86 52.138 -0.779 c
+f
+Q
+ endstream endobj 47 0 obj <</BBox[698.93 463.095 712.887 458.964]/Group 83 0 R/Length 3851/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 701.5309 459.523 cm
+0 0 m
+-0.088 -0.118 l
+-0.345 -0.412 -0.713 -0.559 -1.19 -0.559 c
+-1.631 -0.559 -1.977 -0.419 -2.219 -0.133 c
+-2.466 0.162 -2.595 0.573 -2.601 1.103 c
+-2.601 1.881 l
+-2.601 2.447 -2.491 2.87 -2.263 3.146 c
+-2.04 3.428 -1.701 3.572 -1.249 3.572 c
+-0.867 3.572 -0.569 3.462 -0.353 3.248 c
+-0.139 3.032 -0.022 2.726 0 2.337 c
+-0.5 2.337 l
+-0.521 2.579 -0.588 2.77 -0.706 2.911 c
+-0.823 3.057 -1.007 3.131 -1.249 3.131 c
+-1.536 3.131 -1.745 3.035 -1.881 2.851 c
+-2.021 2.664 -2.087 2.366 -2.087 1.955 c
+-2.087 1.132 l
+-2.087 0.727 -2.013 0.419 -1.866 0.206 c
+-1.712 -0.011 -1.484 -0.118 -1.19 -0.118 c
+-0.926 -0.118 -0.72 -0.056 -0.573 0.073 c
+-0.5 0.147 l
+-0.5 1.072 l
+-1.234 1.072 l
+-1.234 1.5 l
+0 1.5 l
+h
+1.077 -0.5 -0.5 2.983 re
+1.106 3.277 m
+1.106 3.189 1.081 3.116 1.033 3.057 c
+0.992 3.006 0.923 2.984 0.827 2.984 c
+0.738 2.984 0.669 3.006 0.621 3.057 c
+0.58 3.116 0.563 3.183 0.563 3.263 c
+0.563 3.351 0.58 3.424 0.621 3.484 c
+0.669 3.542 0.738 3.572 0.827 3.572 c
+0.923 3.572 0.992 3.542 1.033 3.484 c
+1.081 3.424 1.106 3.355 1.106 3.277 c
+2.382 3.204 m
+2.382 2.484 l
+2.837 2.484 l
+2.837 2.087 l
+2.382 2.087 l
+2.382 0.235 l
+2.382 0.118 2.4 0.029 2.44 -0.029 c
+2.477 -0.088 2.547 -0.118 2.646 -0.118 c
+2.705 -0.118 2.768 -0.11 2.837 -0.088 c
+2.837 -0.5 l
+2.72 -0.536 2.606 -0.559 2.499 -0.559 c
+2.301 -0.559 2.15 -0.493 2.043 -0.353 c
+1.945 -0.217 1.896 -0.023 1.896 0.235 c
+1.896 2.087 l
+1.441 2.087 l
+1.441 2.484 l
+1.896 2.484 l
+1.896 3.204 l
+h
+5.039 -0.5 -0.501 2.983 re
+5.068 3.277 m
+5.068 3.189 5.042 3.116 4.994 3.057 c
+4.954 3.006 4.884 2.984 4.788 2.984 c
+4.7 2.984 4.63 3.006 4.582 3.057 c
+4.542 3.116 4.524 3.183 4.524 3.263 c
+4.524 3.351 4.542 3.424 4.582 3.484 c
+4.63 3.542 4.7 3.572 4.788 3.572 c
+4.884 3.572 4.954 3.542 4.994 3.484 c
+5.042 3.424 5.068 3.355 5.068 3.277 c
+7.151 0.264 m
+7.151 0.371 7.111 0.459 7.034 0.529 c
+6.953 0.606 6.802 0.694 6.578 0.794 c
+6.313 0.9 6.126 0.992 6.02 1.072 c
+5.91 1.151 5.832 1.238 5.785 1.338 c
+5.733 1.433 5.711 1.55 5.711 1.69 c
+5.711 1.933 5.799 2.135 5.976 2.293 c
+6.151 2.458 6.376 2.543 6.652 2.543 c
+6.945 2.543 7.181 2.454 7.357 2.278 c
+7.533 2.109 7.622 1.896 7.622 1.631 c
+7.136 1.631 l
+7.136 1.768 7.086 1.881 6.99 1.97 c
+6.901 2.065 6.787 2.117 6.652 2.117 c
+6.504 2.117 6.39 2.076 6.313 1.999 c
+6.232 1.929 6.196 1.83 6.196 1.705 c
+6.196 1.606 6.225 1.529 6.284 1.469 c
+6.342 1.411 6.483 1.33 6.71 1.234 c
+7.071 1.087 7.317 0.944 7.445 0.808 c
+7.581 0.68 7.651 0.507 7.651 0.293 c
+7.651 0.037 7.556 -0.169 7.372 -0.324 c
+7.196 -0.482 6.961 -0.559 6.666 -0.559 c
+6.35 -0.559 6.097 -0.47 5.902 -0.294 c
+5.714 -0.11 5.623 0.121 5.623 0.397 c
+6.107 0.397 l
+6.115 0.228 6.167 0.095 6.255 0 c
+6.35 -0.088 6.49 -0.133 6.666 -0.133 c
+6.82 -0.133 6.938 -0.1 7.019 -0.029 c
+7.107 0.037 7.151 0.135 7.151 0.264 c
+10.841 -0.5 m
+10.811 -0.434 10.789 -0.324 10.782 -0.177 c
+10.606 -0.434 10.385 -0.559 10.12 -0.559 c
+9.845 -0.559 9.628 -0.485 9.474 -0.339 c
+9.326 -0.183 9.253 0.033 9.253 0.309 c
+9.253 0.61 9.357 0.852 9.562 1.029 c
+9.768 1.213 10.051 1.308 10.415 1.308 c
+10.768 1.308 l
+10.768 1.631 l
+10.768 1.808 10.727 1.929 10.65 1.999 c
+10.569 2.076 10.452 2.117 10.297 2.117 c
+10.15 2.117 10.025 2.072 9.93 1.984 c
+9.841 1.896 9.797 1.785 9.797 1.66 c
+9.312 1.66 l
+9.312 1.808 9.357 1.947 9.444 2.087 c
+9.532 2.234 9.65 2.344 9.797 2.425 c
+9.951 2.502 10.124 2.543 10.311 2.543 c
+10.624 2.543 10.859 2.462 11.017 2.308 c
+11.171 2.161 11.252 1.94 11.252 1.646 c
+11.252 0.147 l
+11.26 -0.088 11.296 -0.291 11.356 -0.456 c
+11.356 -0.5 l
+h
+10.194 -0.118 m
+10.311 -0.118 10.422 -0.085 10.532 -0.015 c
+10.639 0.052 10.716 0.135 10.768 0.235 c
+10.768 0.941 l
+10.502 0.941 l
+10.267 0.941 10.08 0.889 9.945 0.794 c
+9.816 0.694 9.753 0.551 9.753 0.367 c
+9.753 0.198 9.783 0.077 9.841 0 c
+9.908 -0.081 10.025 -0.118 10.194 -0.118 c
+f
+Q
+ endstream endobj 48 0 obj <</BBox[714.378 463.257 737.088 458.964]/Group 84 0 R/Length 5616/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 714.3777 460.6105 cm
+0 0 m
+0 0.489 0.081 0.852 0.25 1.087 c
+0.416 1.33 0.655 1.455 0.971 1.455 c
+1.206 1.455 1.397 1.353 1.544 1.147 c
+1.544 2.646 l
+2.338 2.646 l
+2.338 -1.587 l
+1.617 -1.587 l
+1.588 -1.278 l
+1.43 -1.525 1.224 -1.646 0.971 -1.646 c
+0.655 -1.646 0.416 -1.529 0.25 -1.294 c
+0.092 -1.051 0.008 -0.698 0 -0.235 c
+h
+0.78 -0.206 m
+0.78 -0.5 0.809 -0.706 0.867 -0.823 c
+0.934 -0.933 1.044 -0.985 1.191 -0.985 c
+1.345 -0.985 1.463 -0.918 1.544 -0.779 c
+1.544 0.573 l
+1.463 0.721 1.353 0.794 1.206 0.794 c
+1.059 0.794 0.948 0.735 0.882 0.617 c
+0.813 0.507 0.78 0.301 0.78 0 c
+h
+3.539 -1.587 -0.779 2.983 re
+2.716 2.161 m
+2.716 2.286 2.753 2.389 2.833 2.469 c
+2.911 2.547 3.014 2.587 3.142 2.587 c
+3.267 2.587 3.371 2.547 3.451 2.469 c
+3.528 2.389 3.568 2.286 3.568 2.161 c
+3.568 2.043 3.528 1.944 3.451 1.866 c
+3.381 1.786 3.278 1.749 3.142 1.749 c
+3.014 1.749 2.911 1.786 2.833 1.866 c
+2.753 1.944 2.716 2.043 2.716 2.161 c
+5.366 -0.794 m
+5.366 -0.727 5.329 -0.661 5.263 -0.603 c
+5.204 -0.544 5.064 -0.47 4.851 -0.382 c
+4.528 -0.246 4.308 -0.11 4.19 0.029 c
+4.072 0.166 4.013 0.338 4.013 0.544 c
+4.013 0.808 4.105 1.022 4.293 1.191 c
+4.476 1.367 4.726 1.455 5.042 1.455 c
+5.366 1.455 5.623 1.367 5.821 1.191 c
+6.016 1.022 6.115 0.798 6.115 0.515 c
+5.336 0.515 l
+5.336 0.757 5.233 0.882 5.027 0.882 c
+4.946 0.882 4.881 0.852 4.822 0.794 c
+4.77 0.742 4.748 0.676 4.748 0.588 c
+4.748 0.518 4.778 0.459 4.836 0.412 c
+4.896 0.36 5.031 0.287 5.248 0.191 c
+5.571 0.073 5.796 -0.058 5.924 -0.206 c
+6.06 -0.345 6.13 -0.529 6.13 -0.764 c
+6.13 -1.029 6.027 -1.246 5.821 -1.411 c
+5.623 -1.569 5.366 -1.646 5.042 -1.646 c
+4.815 -1.646 4.62 -1.602 4.454 -1.514 c
+4.285 -1.426 4.153 -1.309 4.057 -1.161 c
+3.969 -1.007 3.925 -0.838 3.925 -0.661 c
+4.66 -0.661 l
+4.667 -0.801 4.704 -0.904 4.763 -0.97 c
+4.822 -1.04 4.917 -1.073 5.057 -1.073 c
+5.263 -1.073 5.366 -0.981 5.366 -0.794 c
+7.354 2.132 m
+7.354 1.396 l
+7.751 1.396 l
+7.751 0.808 l
+7.354 0.808 l
+7.354 -0.676 l
+7.354 -0.794 7.369 -0.878 7.398 -0.926 c
+7.435 -0.966 7.5 -0.985 7.589 -0.985 c
+7.666 -0.985 7.728 -0.977 7.78 -0.956 c
+7.765 -1.573 l
+7.637 -1.621 7.493 -1.646 7.339 -1.646 c
+6.839 -1.646 6.582 -1.359 6.575 -0.779 c
+6.575 0.808 l
+6.236 0.808 l
+6.236 1.396 l
+6.575 1.396 l
+6.575 2.132 l
+h
+9.526 0.647 m
+9.276 0.661 l
+9.058 0.661 8.912 0.566 8.834 0.382 c
+8.834 -1.587 l
+8.056 -1.587 l
+8.056 1.396 l
+8.79 1.396 l
+8.82 1.073 l
+8.937 1.326 9.103 1.455 9.32 1.455 c
+9.407 1.455 9.482 1.44 9.54 1.411 c
+h
+10.628 -1.587 -0.779 2.983 re
+9.804 2.161 m
+9.804 2.286 9.841 2.389 9.922 2.469 c
+9.999 2.547 10.103 2.587 10.231 2.587 c
+10.356 2.587 10.459 2.547 10.54 2.469 c
+10.617 2.389 10.658 2.286 10.658 2.161 c
+10.658 2.043 10.617 1.944 10.54 1.866 c
+10.469 1.786 10.367 1.749 10.231 1.749 c
+10.103 1.749 9.999 1.786 9.922 1.866 c
+9.841 1.944 9.804 2.043 9.804 2.161 c
+13.469 -0.191 m
+13.469 -0.673 13.388 -1.036 13.233 -1.278 c
+13.075 -1.525 12.829 -1.646 12.499 -1.646 c
+12.241 -1.646 12.035 -1.529 11.881 -1.294 c
+11.851 -1.587 l
+11.132 -1.587 l
+11.132 2.646 l
+11.911 2.646 l
+11.911 1.147 l
+12.057 1.353 12.252 1.455 12.499 1.455 c
+12.829 1.455 13.072 1.33 13.218 1.087 c
+13.373 0.852 13.457 0.5 13.469 0.029 c
+h
+12.69 0 m
+12.69 0.312 12.653 0.522 12.586 0.632 c
+12.528 0.738 12.425 0.794 12.278 0.794 c
+12.109 0.794 11.988 0.721 11.911 0.573 c
+11.911 -0.779 l
+11.988 -0.926 12.109 -0.999 12.278 -0.999 c
+12.425 -0.999 12.528 -0.952 12.586 -0.852 c
+12.645 -0.757 12.678 -0.565 12.69 -0.279 c
+h
+15.217 -1.309 m
+15.059 -1.536 14.843 -1.646 14.571 -1.646 c
+14.295 -1.646 14.086 -1.554 13.939 -1.367 c
+13.799 -1.172 13.733 -0.897 13.733 -0.544 c
+13.733 1.396 l
+14.512 1.396 l
+14.512 -0.559 l
+14.512 -0.845 14.608 -0.985 14.806 -0.985 c
+14.982 -0.985 15.107 -0.912 15.188 -0.764 c
+15.188 1.396 l
+15.967 1.396 l
+15.967 -1.587 l
+15.247 -1.587 l
+h
+17.249 2.132 m
+17.249 1.396 l
+17.646 1.396 l
+17.646 0.808 l
+17.249 0.808 l
+17.249 -0.676 l
+17.249 -0.794 17.264 -0.878 17.294 -0.926 c
+17.33 -0.966 17.397 -0.985 17.485 -0.985 c
+17.562 -0.985 17.625 -0.977 17.676 -0.956 c
+17.661 -1.573 l
+17.533 -1.621 17.39 -1.646 17.235 -1.646 c
+16.736 -1.646 16.478 -1.359 16.47 -0.779 c
+16.47 0.808 l
+16.133 0.808 l
+16.133 1.396 l
+16.47 1.396 l
+16.47 2.132 l
+h
+19.161 -1.646 m
+18.756 -1.646 18.444 -1.529 18.22 -1.294 c
+18.003 -1.051 17.897 -0.706 17.897 -0.264 c
+17.897 -0.015 l
+17.897 0.455 17.999 0.816 18.205 1.073 c
+18.411 1.326 18.705 1.455 19.088 1.455 c
+19.458 1.455 19.738 1.33 19.925 1.087 c
+20.12 0.852 20.219 0.5 20.219 0.029 c
+20.219 -0.353 l
+18.676 -0.353 l
+18.683 -0.58 18.727 -0.746 18.808 -0.852 c
+18.896 -0.952 19.032 -0.999 19.219 -0.999 c
+19.473 -0.999 19.69 -0.908 19.867 -0.721 c
+20.175 -1.205 l
+20.075 -1.334 19.932 -1.44 19.749 -1.529 c
+19.572 -1.606 19.374 -1.646 19.161 -1.646 c
+18.676 0.191 m
+19.454 0.191 l
+19.454 0.264 l
+19.454 0.441 19.425 0.573 19.366 0.661 c
+19.308 0.757 19.208 0.808 19.073 0.808 c
+18.944 0.808 18.845 0.757 18.778 0.661 c
+18.72 0.563 18.683 0.405 18.676 0.191 c
+20.374 0 m
+20.374 0.489 20.454 0.852 20.623 1.087 c
+20.788 1.33 21.027 1.455 21.344 1.455 c
+21.579 1.455 21.77 1.353 21.916 1.147 c
+21.916 2.646 l
+22.71 2.646 l
+22.71 -1.587 l
+21.991 -1.587 l
+21.961 -1.278 l
+21.803 -1.525 21.597 -1.646 21.344 -1.646 c
+21.027 -1.646 20.788 -1.529 20.623 -1.294 c
+20.465 -1.051 20.381 -0.698 20.374 -0.235 c
+h
+21.153 -0.206 m
+21.153 -0.5 21.182 -0.706 21.241 -0.823 c
+21.307 -0.933 21.417 -0.985 21.564 -0.985 c
+21.718 -0.985 21.836 -0.918 21.916 -0.779 c
+21.916 0.573 l
+21.836 0.721 21.725 0.794 21.579 0.794 c
+21.432 0.794 21.322 0.735 21.255 0.617 c
+21.185 0.507 21.153 0.301 21.153 0 c
+h
+f
+Q
+ endstream endobj 49 0 obj <</BBox[738.525 463.257 767.419 458.964]/Group 85 0 R/Length 6236/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 739.5979 459.7581 cm
+0 0 m
+0.573 2.248 l
+1.073 2.248 l
+0.177 -0.735 l
+-0.176 -0.735 l
+-1.073 2.248 l
+-0.588 2.248 l
+h
+2.44 -0.794 m
+2.065 -0.794 1.783 -0.688 1.588 -0.47 c
+1.39 -0.246 1.294 0.081 1.294 0.515 c
+1.294 0.881 l
+1.294 1.323 1.386 1.668 1.573 1.926 c
+1.768 2.179 2.043 2.308 2.396 2.308 c
+2.738 2.308 2.992 2.194 3.16 1.97 c
+3.337 1.741 3.429 1.396 3.439 0.926 c
+3.439 0.617 l
+1.779 0.617 l
+1.779 0.544 l
+1.779 0.22 1.837 -0.015 1.955 -0.162 c
+2.072 -0.301 2.242 -0.368 2.469 -0.368 c
+2.616 -0.368 2.741 -0.345 2.851 -0.294 c
+2.959 -0.235 3.061 -0.147 3.16 -0.029 c
+3.41 -0.339 l
+3.204 -0.643 2.882 -0.794 2.44 -0.794 c
+2.396 1.882 m
+2.19 1.882 2.036 1.812 1.941 1.675 c
+1.841 1.536 1.787 1.323 1.779 1.029 c
+2.955 1.029 l
+2.955 1.103 l
+2.932 1.374 2.882 1.573 2.793 1.691 c
+2.705 1.816 2.572 1.882 2.396 1.882 c
+5.086 1.793 m
+5.016 1.801 4.943 1.808 4.866 1.808 c
+4.609 1.808 4.432 1.668 4.336 1.396 c
+4.336 -0.735 l
+3.851 -0.735 l
+3.851 2.248 l
+4.322 2.248 l
+4.336 1.94 l
+4.461 2.183 4.645 2.308 4.881 2.308 c
+4.958 2.308 5.02 2.293 5.072 2.263 c
+h
+6.898 0.029 m
+6.898 0.135 6.857 0.224 6.78 0.294 c
+6.699 0.371 6.549 0.459 6.325 0.559 c
+6.059 0.665 5.872 0.757 5.766 0.837 c
+5.656 0.915 5.579 1.003 5.531 1.103 c
+5.479 1.198 5.457 1.315 5.457 1.455 c
+5.457 1.698 5.546 1.899 5.722 2.057 c
+5.898 2.223 6.122 2.308 6.398 2.308 c
+6.692 2.308 6.927 2.219 7.104 2.043 c
+7.279 1.874 7.368 1.66 7.368 1.396 c
+6.883 1.396 l
+6.883 1.532 6.832 1.646 6.736 1.735 c
+6.648 1.83 6.533 1.882 6.398 1.882 c
+6.251 1.882 6.137 1.841 6.059 1.764 c
+5.979 1.694 5.943 1.595 5.943 1.469 c
+5.943 1.371 5.972 1.294 6.03 1.234 c
+6.089 1.176 6.229 1.095 6.456 0.999 c
+6.817 0.852 7.063 0.709 7.192 0.573 c
+7.327 0.445 7.397 0.272 7.397 0.058 c
+7.397 -0.198 7.302 -0.405 7.119 -0.559 c
+6.942 -0.717 6.707 -0.794 6.413 -0.794 c
+6.097 -0.794 5.843 -0.706 5.648 -0.53 c
+5.461 -0.345 5.369 -0.114 5.369 0.162 c
+5.854 0.162 l
+5.862 -0.008 5.913 -0.14 6.001 -0.235 c
+6.097 -0.324 6.236 -0.368 6.413 -0.368 c
+6.567 -0.368 6.685 -0.335 6.765 -0.264 c
+6.853 -0.198 6.898 -0.1 6.898 0.029 c
+8.386 -0.735 -0.5 2.983 re
+8.415 3.042 m
+8.415 2.954 8.39 2.881 8.342 2.822 c
+8.301 2.771 8.232 2.749 8.137 2.749 c
+8.048 2.749 7.978 2.771 7.93 2.822 c
+7.89 2.881 7.871 2.947 7.871 3.028 c
+7.871 3.116 7.89 3.189 7.93 3.248 c
+7.978 3.307 8.048 3.337 8.137 3.337 c
+8.232 3.337 8.301 3.307 8.342 3.248 c
+8.39 3.189 8.415 3.119 8.415 3.042 c
+8.956 0.897 m
+8.956 1.326 9.058 1.668 9.264 1.926 c
+9.477 2.179 9.756 2.308 10.102 2.308 c
+10.444 2.308 10.72 2.179 10.925 1.926 c
+11.138 1.679 11.252 1.345 11.263 0.926 c
+11.263 0.617 l
+11.263 0.183 11.153 -0.158 10.94 -0.412 c
+10.734 -0.669 10.454 -0.794 10.102 -0.794 c
+9.756 -0.794 9.485 -0.673 9.279 -0.426 c
+9.073 -0.183 8.962 0.151 8.956 0.573 c
+h
+9.44 0.617 m
+9.44 0.301 9.5 0.058 9.617 -0.118 c
+9.742 -0.287 9.903 -0.368 10.102 -0.368 c
+10.532 -0.368 10.756 -0.059 10.778 0.559 c
+10.778 0.897 l
+10.778 1.198 10.712 1.44 10.587 1.617 c
+10.47 1.793 10.308 1.882 10.102 1.882 c
+9.903 1.882 9.742 1.793 9.617 1.617 c
+9.5 1.44 9.44 1.198 9.44 0.897 c
+h
+12.16 2.248 m
+12.174 1.926 l
+12.359 2.179 12.601 2.308 12.895 2.308 c
+13.424 2.308 13.693 1.955 13.704 1.249 c
+13.704 -0.735 l
+13.218 -0.735 l
+13.218 1.22 l
+13.218 1.455 13.178 1.621 13.101 1.72 c
+13.02 1.816 12.902 1.866 12.748 1.866 c
+12.63 1.866 12.52 1.826 12.424 1.749 c
+12.326 1.668 12.248 1.562 12.189 1.425 c
+12.189 -0.735 l
+11.704 -0.735 l
+11.704 2.248 l
+h
+16.488 -0.368 m
+16.654 -0.368 16.787 -0.32 16.885 -0.221 c
+16.981 -0.125 17.036 0.018 17.047 0.206 c
+17.503 0.206 l
+17.492 -0.081 17.389 -0.32 17.194 -0.515 c
+17.007 -0.702 16.772 -0.794 16.488 -0.794 c
+16.125 -0.794 15.846 -0.676 15.651 -0.441 c
+15.453 -0.206 15.357 0.139 15.357 0.602 c
+15.357 0.926 l
+15.357 1.374 15.449 1.72 15.636 1.955 c
+15.831 2.19 16.114 2.308 16.488 2.308 c
+16.79 2.308 17.033 2.209 17.209 2.013 c
+17.392 1.816 17.492 1.55 17.503 1.22 c
+17.047 1.22 l
+17.026 1.444 16.966 1.61 16.871 1.72 c
+16.783 1.826 16.654 1.882 16.488 1.882 c
+16.272 1.882 16.11 1.808 16.004 1.66 c
+15.904 1.521 15.85 1.294 15.842 0.97 c
+15.842 0.588 l
+15.842 0.235 15.89 -0.015 15.989 -0.162 c
+16.095 -0.301 16.261 -0.368 16.488 -0.368 c
+17.793 0.897 m
+17.793 1.326 17.897 1.668 18.102 1.926 c
+18.315 2.179 18.595 2.308 18.94 2.308 c
+19.281 2.308 19.557 2.179 19.763 1.926 c
+19.976 1.679 20.09 1.345 20.101 0.926 c
+20.101 0.617 l
+20.101 0.183 19.991 -0.158 19.778 -0.412 c
+19.572 -0.669 19.293 -0.794 18.94 -0.794 c
+18.595 -0.794 18.323 -0.673 18.117 -0.426 c
+17.911 -0.183 17.801 0.151 17.793 0.573 c
+h
+18.279 0.617 m
+18.279 0.301 18.337 0.058 18.455 -0.118 c
+18.58 -0.287 18.742 -0.368 18.94 -0.368 c
+19.37 -0.368 19.594 -0.059 19.616 0.559 c
+19.616 0.897 l
+19.616 1.198 19.55 1.44 19.425 1.617 c
+19.308 1.793 19.146 1.882 18.94 1.882 c
+18.742 1.882 18.58 1.793 18.455 1.617 c
+18.337 1.44 18.279 1.198 18.279 0.897 c
+h
+20.997 2.248 m
+21.012 1.926 l
+21.196 2.179 21.439 2.308 21.733 2.308 c
+22.262 2.308 22.53 1.955 22.541 1.249 c
+22.541 -0.735 l
+22.056 -0.735 l
+22.056 1.22 l
+22.056 1.455 22.015 1.621 21.938 1.72 c
+21.857 1.816 21.74 1.866 21.585 1.866 c
+21.468 1.866 21.358 1.826 21.263 1.749 c
+21.163 1.668 21.086 1.562 21.027 1.425 c
+21.027 -0.735 l
+20.542 -0.735 l
+20.542 2.248 l
+h
+24.327 1.793 m
+24.257 1.801 24.184 1.808 24.106 1.808 c
+23.85 1.808 23.673 1.668 23.578 1.396 c
+23.578 -0.735 l
+23.092 -0.735 l
+23.092 2.248 l
+23.563 2.248 l
+23.578 1.94 l
+23.702 2.183 23.886 2.308 24.122 2.308 c
+24.199 2.308 24.261 2.293 24.313 2.263 c
+h
+24.544 0.897 m
+24.544 1.326 24.646 1.668 24.852 1.926 c
+25.066 2.179 25.345 2.308 25.691 2.308 c
+26.032 2.308 26.308 2.179 26.514 1.926 c
+26.726 1.679 26.84 1.345 26.852 0.926 c
+26.852 0.617 l
+26.852 0.183 26.742 -0.158 26.528 -0.412 c
+26.323 -0.669 26.043 -0.794 25.691 -0.794 c
+25.345 -0.794 25.073 -0.673 24.868 -0.426 c
+24.662 -0.183 24.551 0.151 24.544 0.573 c
+h
+25.029 0.617 m
+25.029 0.301 25.088 0.058 25.205 -0.118 c
+25.33 -0.287 25.492 -0.368 25.691 -0.368 c
+26.121 -0.368 26.345 -0.059 26.366 0.559 c
+26.366 0.897 l
+26.366 1.198 26.3 1.44 26.175 1.617 c
+26.058 1.793 25.896 1.882 25.691 1.882 c
+25.492 1.882 25.33 1.793 25.205 1.617 c
+25.088 1.44 25.029 1.198 25.029 0.897 c
+h
+27.821 -0.735 -0.5 4.233 re
+f
+Q
+ endstream endobj 50 0 obj <</BBox[701.664 456.672 764.976 451.232]/Group 86 0 R/Length 14524/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 703.1923 453.2021 cm
+0 0 m
+0 0.106 -0.04 0.195 -0.118 0.264 c
+-0.198 0.341 -0.349 0.43 -0.574 0.53 c
+-0.838 0.636 -1.025 0.728 -1.132 0.808 c
+-1.242 0.886 -1.319 0.974 -1.367 1.073 c
+-1.419 1.168 -1.44 1.286 -1.44 1.426 c
+-1.44 1.669 -1.353 1.87 -1.176 2.028 c
+-1 2.194 -0.775 2.278 -0.5 2.278 c
+-0.206 2.278 0.029 2.19 0.206 2.014 c
+0.382 1.845 0.47 1.631 0.47 1.367 c
+-0.015 1.367 l
+-0.015 1.503 -0.066 1.617 -0.162 1.706 c
+-0.25 1.801 -0.364 1.852 -0.5 1.852 c
+-0.647 1.852 -0.761 1.812 -0.838 1.735 c
+-0.919 1.665 -0.956 1.565 -0.956 1.44 c
+-0.956 1.341 -0.926 1.264 -0.867 1.205 c
+-0.809 1.147 -0.669 1.066 -0.441 0.97 c
+-0.081 0.823 0.166 0.68 0.294 0.544 c
+0.43 0.416 0.5 0.243 0.5 0.029 c
+0.5 -0.228 0.405 -0.434 0.22 -0.588 c
+0.044 -0.746 -0.191 -0.823 -0.485 -0.823 c
+-0.802 -0.823 -1.055 -0.735 -1.249 -0.559 c
+-1.436 -0.374 -1.529 -0.143 -1.529 0.133 c
+-1.044 0.133 l
+-1.037 -0.037 -0.985 -0.169 -0.896 -0.264 c
+-0.802 -0.353 -0.661 -0.397 -0.485 -0.397 c
+-0.331 -0.397 -0.214 -0.364 -0.133 -0.294 c
+-0.044 -0.228 0 -0.129 0 0 c
+1.768 0.044 m
+2.311 2.219 l
+2.826 2.219 l
+1.856 -1.191 l
+1.786 -1.444 1.683 -1.635 1.547 -1.764 c
+1.407 -1.899 1.257 -1.97 1.091 -1.97 c
+1.022 -1.97 0.937 -1.955 0.841 -1.926 c
+0.841 -1.514 l
+0.945 -1.529 l
+1.08 -1.529 1.187 -1.492 1.268 -1.426 c
+1.356 -1.356 1.422 -1.238 1.473 -1.073 c
+1.562 -0.735 l
+0.694 2.219 l
+1.224 2.219 l
+h
+4.59 0 m
+4.59 0.106 4.549 0.195 4.472 0.264 c
+4.391 0.341 4.241 0.43 4.016 0.53 c
+3.752 0.636 3.564 0.728 3.458 0.808 c
+3.347 0.886 3.27 0.974 3.223 1.073 c
+3.171 1.168 3.15 1.286 3.15 1.426 c
+3.15 1.669 3.237 1.87 3.414 2.028 c
+3.59 2.194 3.815 2.278 4.09 2.278 c
+4.384 2.278 4.619 2.19 4.796 2.014 c
+4.972 1.845 5.06 1.631 5.06 1.367 c
+4.575 1.367 l
+4.575 1.503 4.523 1.617 4.428 1.706 c
+4.34 1.801 4.226 1.852 4.09 1.852 c
+3.943 1.852 3.829 1.812 3.752 1.735 c
+3.671 1.665 3.634 1.565 3.634 1.44 c
+3.634 1.341 3.663 1.264 3.723 1.205 c
+3.781 1.147 3.921 1.066 4.149 0.97 c
+4.509 0.823 4.755 0.68 4.883 0.544 c
+5.02 0.416 5.089 0.243 5.089 0.029 c
+5.089 -0.228 4.993 -0.434 4.81 -0.588 c
+4.634 -0.746 4.399 -0.823 4.104 -0.823 c
+3.788 -0.823 3.535 -0.735 3.341 -0.559 c
+3.152 -0.374 3.061 -0.143 3.061 0.133 c
+3.546 0.133 l
+3.553 -0.037 3.605 -0.169 3.693 -0.264 c
+3.788 -0.353 3.929 -0.397 4.104 -0.397 c
+4.259 -0.397 4.376 -0.364 4.457 -0.294 c
+4.546 -0.228 4.59 -0.129 4.59 0 c
+6.181 2.94 m
+6.181 2.219 l
+6.637 2.219 l
+6.637 1.823 l
+6.181 1.823 l
+6.181 -0.029 l
+6.181 -0.147 6.2 -0.235 6.24 -0.294 c
+6.277 -0.353 6.346 -0.382 6.445 -0.382 c
+6.504 -0.382 6.566 -0.374 6.637 -0.353 c
+6.637 -0.764 l
+6.519 -0.801 6.405 -0.823 6.298 -0.823 c
+6.1 -0.823 5.949 -0.757 5.843 -0.617 c
+5.743 -0.482 5.696 -0.287 5.696 -0.029 c
+5.696 1.823 l
+5.24 1.823 l
+5.24 2.219 l
+5.696 2.219 l
+5.696 2.94 l
+h
+8.158 -0.823 m
+7.783 -0.823 7.501 -0.717 7.306 -0.5 c
+7.107 -0.276 7.011 0.052 7.011 0.485 c
+7.011 0.852 l
+7.011 1.294 7.104 1.639 7.291 1.897 c
+7.486 2.15 7.761 2.278 8.114 2.278 c
+8.455 2.278 8.709 2.165 8.878 1.941 c
+9.055 1.712 9.147 1.367 9.157 0.897 c
+9.157 0.588 l
+7.497 0.588 l
+7.497 0.515 l
+7.497 0.191 7.555 -0.044 7.673 -0.191 c
+7.79 -0.33 7.96 -0.397 8.187 -0.397 c
+8.334 -0.397 8.459 -0.374 8.569 -0.324 c
+8.676 -0.264 8.779 -0.176 8.878 -0.058 c
+9.128 -0.368 l
+8.922 -0.673 8.598 -0.823 8.158 -0.823 c
+8.114 1.852 m
+7.908 1.852 7.754 1.783 7.658 1.646 c
+7.559 1.507 7.503 1.294 7.497 1 c
+8.673 1 l
+8.673 1.073 l
+8.65 1.345 8.598 1.544 8.511 1.661 c
+8.422 1.786 8.29 1.852 8.114 1.852 c
+10.025 2.219 m
+10.04 1.941 l
+10.216 2.165 10.454 2.278 10.759 2.278 c
+11.09 2.278 11.322 2.132 11.451 1.837 c
+11.634 2.132 11.895 2.278 12.23 2.278 c
+12.788 2.278 13.071 1.933 13.082 1.249 c
+13.082 -0.764 l
+12.597 -0.764 l
+12.597 1.205 l
+12.597 1.419 12.557 1.577 12.48 1.675 c
+12.399 1.783 12.266 1.837 12.083 1.837 c
+11.935 1.837 11.818 1.779 11.73 1.661 c
+11.642 1.551 11.586 1.411 11.568 1.235 c
+11.568 -0.764 l
+11.083 -0.764 l
+11.083 1.22 l
+11.072 1.631 10.899 1.837 10.568 1.837 c
+10.323 1.837 10.15 1.712 10.054 1.47 c
+10.054 -0.764 l
+9.569 -0.764 l
+9.569 2.219 l
+h
+13.56 -0.5 m
+13.56 -0.411 13.581 -0.338 13.633 -0.279 c
+13.692 -0.22 13.77 -0.191 13.868 -0.191 c
+13.975 -0.191 14.052 -0.22 14.104 -0.279 c
+14.163 -0.338 14.192 -0.411 14.192 -0.5 c
+14.192 -0.58 14.163 -0.646 14.104 -0.706 c
+14.052 -0.764 13.975 -0.794 13.868 -0.794 c
+13.77 -0.794 13.692 -0.764 13.633 -0.706 c
+13.581 -0.646 13.56 -0.58 13.56 -0.5 c
+17.242 1.235 m
+18.05 3.248 l
+18.624 3.248 l
+17.492 0.735 l
+17.492 -0.764 l
+16.978 -0.764 l
+16.978 0.735 l
+15.846 3.248 l
+16.419 3.248 l
+h
+18.624 0.867 m
+18.624 1.297 18.726 1.639 18.932 1.897 c
+19.145 2.15 19.424 2.278 19.771 2.278 c
+20.112 2.278 20.388 2.15 20.593 1.897 c
+20.806 1.65 20.92 1.316 20.931 0.897 c
+20.931 0.588 l
+20.931 0.154 20.821 -0.187 20.608 -0.441 c
+20.402 -0.698 20.122 -0.823 19.771 -0.823 c
+19.424 -0.823 19.153 -0.702 18.947 -0.455 c
+18.741 -0.213 18.631 0.121 18.624 0.544 c
+h
+19.108 0.588 m
+19.108 0.272 19.168 0.029 19.285 -0.147 c
+19.41 -0.316 19.572 -0.397 19.771 -0.397 c
+20.201 -0.397 20.424 -0.088 20.446 0.53 c
+20.446 0.867 l
+20.446 1.168 20.38 1.411 20.255 1.588 c
+20.138 1.764 19.976 1.852 19.771 1.852 c
+19.572 1.852 19.41 1.764 19.285 1.588 c
+19.168 1.411 19.108 1.168 19.108 0.867 c
+h
+22.886 -0.5 m
+22.728 -0.717 22.493 -0.823 22.181 -0.823 c
+21.916 -0.823 21.71 -0.731 21.564 -0.544 c
+21.423 -0.36 21.358 -0.085 21.358 0.279 c
+21.358 2.219 l
+21.843 2.219 l
+21.843 0.309 l
+21.843 -0.154 21.982 -0.382 22.269 -0.382 c
+22.563 -0.382 22.761 -0.25 22.871 0.015 c
+22.871 2.219 l
+23.372 2.219 l
+23.372 -0.764 l
+22.901 -0.764 l
+h
+26.157 -0.397 m
+26.322 -0.397 26.455 -0.349 26.553 -0.25 c
+26.649 -0.154 26.705 -0.011 26.715 0.177 c
+27.171 0.177 l
+27.16 -0.11 27.058 -0.349 26.863 -0.544 c
+26.675 -0.731 26.439 -0.823 26.157 -0.823 c
+25.793 -0.823 25.514 -0.706 25.319 -0.47 c
+25.121 -0.235 25.025 0.11 25.025 0.573 c
+25.025 0.897 l
+25.025 1.345 25.117 1.691 25.304 1.926 c
+25.499 2.161 25.782 2.278 26.157 2.278 c
+26.458 2.278 26.701 2.18 26.877 1.984 c
+27.061 1.786 27.16 1.521 27.171 1.191 c
+26.715 1.191 l
+26.693 1.415 26.634 1.58 26.539 1.691 c
+26.451 1.797 26.322 1.852 26.157 1.852 c
+25.94 1.852 25.778 1.779 25.672 1.631 c
+25.572 1.492 25.518 1.264 25.51 0.941 c
+25.51 0.559 l
+25.51 0.206 25.558 -0.044 25.657 -0.191 c
+25.764 -0.33 25.929 -0.397 26.157 -0.397 c
+29.097 -0.764 m
+29.067 -0.698 29.045 -0.588 29.038 -0.441 c
+28.862 -0.698 28.641 -0.823 28.376 -0.823 c
+28.101 -0.823 27.884 -0.75 27.729 -0.603 c
+27.583 -0.448 27.509 -0.231 27.509 0.044 c
+27.509 0.345 27.612 0.588 27.818 0.765 c
+28.024 0.948 28.307 1.043 28.67 1.043 c
+29.023 1.043 l
+29.023 1.367 l
+29.023 1.544 28.982 1.665 28.905 1.735 c
+28.824 1.812 28.708 1.852 28.552 1.852 c
+28.406 1.852 28.281 1.808 28.185 1.72 c
+28.097 1.631 28.053 1.521 28.053 1.396 c
+27.568 1.396 l
+27.568 1.544 27.612 1.683 27.7 1.823 c
+27.789 1.97 27.906 2.08 28.053 2.161 c
+28.207 2.238 28.38 2.278 28.567 2.278 c
+28.88 2.278 29.115 2.198 29.273 2.043 c
+29.427 1.897 29.508 1.675 29.508 1.382 c
+29.508 -0.118 l
+29.516 -0.353 29.552 -0.555 29.611 -0.721 c
+29.611 -0.764 l
+h
+28.45 -0.382 m
+28.567 -0.382 28.677 -0.349 28.788 -0.279 c
+28.895 -0.213 28.972 -0.129 29.023 -0.029 c
+29.023 0.676 l
+28.758 0.676 l
+28.523 0.676 28.336 0.625 28.2 0.53 c
+28.072 0.43 28.009 0.287 28.009 0.103 c
+28.009 -0.066 28.039 -0.187 28.097 -0.264 c
+28.163 -0.345 28.281 -0.382 28.45 -0.382 c
+30.545 2.219 m
+30.559 1.897 l
+30.743 2.15 30.985 2.278 31.28 2.278 c
+31.808 2.278 32.077 1.926 32.088 1.22 c
+32.088 -0.764 l
+31.603 -0.764 l
+31.603 1.191 l
+31.603 1.426 31.563 1.592 31.486 1.691 c
+31.405 1.786 31.287 1.837 31.133 1.837 c
+31.015 1.837 30.904 1.797 30.809 1.72 c
+30.709 1.639 30.632 1.532 30.574 1.396 c
+30.574 -0.764 l
+30.089 -0.764 l
+30.089 2.219 l
+h
+34.329 1.911 m
+34.513 2.153 34.748 2.278 35.035 2.278 c
+35.565 2.278 35.833 1.926 35.843 1.22 c
+35.843 -0.764 l
+35.359 -0.764 l
+35.359 1.191 l
+35.359 1.426 35.318 1.592 35.241 1.691 c
+35.16 1.786 35.043 1.837 34.888 1.837 c
+34.771 1.837 34.661 1.797 34.565 1.72 c
+34.466 1.639 34.389 1.532 34.329 1.396 c
+34.329 -0.764 l
+33.844 -0.764 l
+33.844 3.469 l
+34.329 3.469 l
+h
+37.913 -0.764 m
+37.883 -0.698 37.861 -0.588 37.853 -0.441 c
+37.678 -0.698 37.456 -0.823 37.192 -0.823 c
+36.917 -0.823 36.7 -0.75 36.546 -0.603 c
+36.398 -0.448 36.325 -0.231 36.325 0.044 c
+36.325 0.345 36.428 0.588 36.633 0.765 c
+36.839 0.948 37.123 1.043 37.487 1.043 c
+37.839 1.043 l
+37.839 1.367 l
+37.839 1.544 37.799 1.665 37.722 1.735 c
+37.641 1.812 37.523 1.852 37.369 1.852 c
+37.221 1.852 37.096 1.808 37.001 1.72 c
+36.913 1.631 36.869 1.521 36.869 1.396 c
+36.384 1.396 l
+36.384 1.544 36.428 1.683 36.516 1.823 c
+36.604 1.97 36.722 2.08 36.869 2.161 c
+37.023 2.238 37.196 2.278 37.383 2.278 c
+37.695 2.278 37.931 2.198 38.089 2.043 c
+38.243 1.897 38.324 1.675 38.324 1.382 c
+38.324 -0.118 l
+38.331 -0.353 38.368 -0.555 38.427 -0.721 c
+38.427 -0.764 l
+h
+37.266 -0.382 m
+37.383 -0.382 37.493 -0.349 37.604 -0.279 c
+37.711 -0.213 37.788 -0.129 37.839 -0.029 c
+37.839 0.676 l
+37.574 0.676 l
+37.339 0.676 37.152 0.625 37.016 0.53 c
+36.887 0.43 36.824 0.287 36.824 0.103 c
+36.824 -0.066 36.854 -0.187 36.913 -0.264 c
+36.979 -0.345 37.096 -0.382 37.266 -0.382 c
+39.702 -0.029 m
+40.275 2.219 l
+40.775 2.219 l
+39.878 -0.764 l
+39.526 -0.764 l
+38.629 2.219 l
+39.114 2.219 l
+h
+42.146 -0.823 m
+41.771 -0.823 41.488 -0.717 41.293 -0.5 c
+41.094 -0.276 40.999 0.052 40.999 0.485 c
+40.999 0.852 l
+40.999 1.294 41.091 1.639 41.279 1.897 c
+41.473 2.15 41.749 2.278 42.102 2.278 c
+42.443 2.278 42.697 2.165 42.866 1.941 c
+43.043 1.712 43.134 1.367 43.145 0.897 c
+43.145 0.588 l
+41.485 0.588 l
+41.485 0.515 l
+41.485 0.191 41.543 -0.044 41.661 -0.191 c
+41.778 -0.33 41.948 -0.397 42.175 -0.397 c
+42.322 -0.397 42.447 -0.374 42.557 -0.324 c
+42.664 -0.264 42.767 -0.176 42.866 -0.058 c
+43.116 -0.368 l
+42.91 -0.673 42.586 -0.823 42.146 -0.823 c
+42.102 1.852 m
+41.896 1.852 41.742 1.783 41.646 1.646 c
+41.547 1.507 41.491 1.294 41.485 1 c
+42.661 1 l
+42.661 1.073 l
+42.638 1.345 42.586 1.544 42.499 1.661 c
+42.41 1.786 42.278 1.852 42.102 1.852 c
+46.287 -0.764 m
+46.258 -0.698 46.235 -0.588 46.228 -0.441 c
+46.052 -0.698 45.832 -0.823 45.567 -0.823 c
+45.291 -0.823 45.075 -0.75 44.92 -0.603 c
+44.774 -0.448 44.7 -0.231 44.7 0.044 c
+44.7 0.345 44.803 0.588 45.009 0.765 c
+45.214 0.948 45.497 1.043 45.861 1.043 c
+46.214 1.043 l
+46.214 1.367 l
+46.214 1.544 46.173 1.665 46.096 1.735 c
+46.015 1.812 45.898 1.852 45.743 1.852 c
+45.597 1.852 45.472 1.808 45.376 1.72 c
+45.287 1.631 45.244 1.521 45.244 1.396 c
+44.758 1.396 l
+44.758 1.544 44.803 1.683 44.891 1.823 c
+44.979 1.97 45.096 2.08 45.244 2.161 c
+45.398 2.238 45.57 2.278 45.758 2.278 c
+46.071 2.278 46.306 2.198 46.463 2.043 c
+46.618 1.897 46.698 1.675 46.698 1.382 c
+46.698 -0.118 l
+46.706 -0.353 46.743 -0.555 46.802 -0.721 c
+46.802 -0.764 l
+h
+45.641 -0.382 m
+45.758 -0.382 45.869 -0.349 45.979 -0.279 c
+46.085 -0.213 46.162 -0.129 46.214 -0.029 c
+46.214 0.676 l
+45.949 0.676 l
+45.714 0.676 45.526 0.625 45.391 0.53 c
+45.262 0.43 45.2 0.287 45.2 0.103 c
+45.2 -0.066 45.229 -0.187 45.287 -0.264 c
+45.354 -0.345 45.472 -0.382 45.641 -0.382 c
+48.72 0 m
+48.72 0.106 48.68 0.195 48.602 0.264 c
+48.521 0.341 48.371 0.43 48.147 0.53 c
+47.882 0.636 47.695 0.728 47.588 0.808 c
+47.478 0.886 47.401 0.974 47.353 1.073 c
+47.301 1.168 47.28 1.286 47.28 1.426 c
+47.28 1.669 47.367 1.87 47.544 2.028 c
+47.72 2.194 47.945 2.278 48.22 2.278 c
+48.514 2.278 48.749 2.19 48.926 2.014 c
+49.102 1.845 49.19 1.631 49.19 1.367 c
+48.705 1.367 l
+48.705 1.503 48.654 1.617 48.558 1.706 c
+48.47 1.801 48.356 1.852 48.22 1.852 c
+48.073 1.852 47.959 1.812 47.882 1.735 c
+47.801 1.665 47.764 1.565 47.764 1.44 c
+47.764 1.341 47.794 1.264 47.853 1.205 c
+47.912 1.147 48.051 1.066 48.279 0.97 c
+48.639 0.823 48.886 0.68 49.013 0.544 c
+49.15 0.416 49.219 0.243 49.219 0.029 c
+49.219 -0.228 49.124 -0.434 48.94 -0.588 c
+48.764 -0.746 48.529 -0.823 48.235 -0.823 c
+47.918 -0.823 47.665 -0.735 47.471 -0.559 c
+47.284 -0.374 47.191 -0.143 47.191 0.133 c
+47.677 0.133 l
+47.683 -0.037 47.735 -0.169 47.823 -0.264 c
+47.918 -0.353 48.059 -0.397 48.235 -0.397 c
+48.389 -0.397 48.506 -0.364 48.587 -0.294 c
+48.676 -0.228 48.72 -0.129 48.72 0 c
+51.336 2.219 m
+51.351 1.941 l
+51.527 2.165 51.766 2.278 52.071 2.278 c
+52.402 2.278 52.633 2.132 52.762 1.837 c
+52.946 2.132 53.206 2.278 53.541 2.278 c
+54.1 2.278 54.382 1.933 54.394 1.249 c
+54.394 -0.764 l
+53.909 -0.764 l
+53.909 1.205 l
+53.909 1.419 53.869 1.577 53.792 1.675 c
+53.711 1.783 53.578 1.837 53.394 1.837 c
+53.247 1.837 53.129 1.779 53.042 1.661 c
+52.953 1.551 52.898 1.411 52.88 1.235 c
+52.88 -0.764 l
+52.394 -0.764 l
+52.394 1.22 l
+52.383 1.631 52.211 1.837 51.88 1.837 c
+51.634 1.837 51.461 1.712 51.365 1.47 c
+51.365 -0.764 l
+50.881 -0.764 l
+50.881 2.219 l
+h
+56.371 -0.764 m
+56.341 -0.698 56.319 -0.588 56.312 -0.441 c
+56.136 -0.698 55.915 -0.823 55.65 -0.823 c
+55.375 -0.823 55.158 -0.75 55.004 -0.603 c
+54.856 -0.448 54.783 -0.231 54.783 0.044 c
+54.783 0.345 54.887 0.588 55.092 0.765 c
+55.298 0.948 55.581 1.043 55.945 1.043 c
+56.298 1.043 l
+56.298 1.367 l
+56.298 1.544 56.257 1.665 56.18 1.735 c
+56.099 1.812 55.982 1.852 55.827 1.852 c
+55.68 1.852 55.555 1.808 55.459 1.72 c
+55.371 1.631 55.327 1.521 55.327 1.396 c
+54.842 1.396 l
+54.842 1.544 54.887 1.683 54.974 1.823 c
+55.062 1.97 55.18 2.08 55.327 2.161 c
+55.481 2.238 55.654 2.278 55.841 2.278 c
+56.154 2.278 56.389 2.198 56.547 2.043 c
+56.701 1.897 56.782 1.675 56.782 1.382 c
+56.782 -0.118 l
+56.79 -0.353 56.826 -0.555 56.886 -0.721 c
+56.886 -0.764 l
+h
+55.724 -0.382 m
+55.841 -0.382 55.951 -0.349 56.062 -0.279 c
+56.169 -0.213 56.246 -0.129 56.298 -0.029 c
+56.298 0.676 l
+56.032 0.676 l
+55.797 0.676 55.61 0.625 55.474 0.53 c
+55.346 0.43 55.284 0.287 55.284 0.103 c
+55.284 -0.066 55.313 -0.187 55.371 -0.264 c
+55.438 -0.345 55.555 -0.382 55.724 -0.382 c
+57.819 2.219 m
+57.833 1.897 l
+58.017 2.15 58.26 2.278 58.554 2.278 c
+59.083 2.278 59.351 1.926 59.362 1.22 c
+59.362 -0.764 l
+58.877 -0.764 l
+58.877 1.191 l
+58.877 1.426 58.837 1.592 58.76 1.691 c
+58.679 1.786 58.561 1.837 58.407 1.837 c
+58.289 1.837 58.179 1.797 58.083 1.72 c
+57.984 1.639 57.907 1.532 57.848 1.396 c
+57.848 -0.764 l
+57.363 -0.764 l
+57.363 2.219 l
+h
+60.726 0.044 m
+61.269 2.219 l
+61.784 2.219 l
+60.813 -1.191 l
+60.744 -1.444 60.641 -1.635 60.505 -1.764 c
+60.365 -1.899 60.215 -1.97 60.049 -1.97 c
+59.98 -1.97 59.895 -1.955 59.799 -1.926 c
+59.799 -1.514 l
+59.903 -1.529 l
+60.038 -1.529 60.144 -1.492 60.225 -1.426 c
+60.314 -1.356 60.379 -1.238 60.431 -1.073 c
+60.52 -0.735 l
+59.652 2.219 l
+60.182 2.219 l
+h
+f
+Q
+ endstream endobj 51 0 obj <</BBox[623.858 230.42 661.638 225.011]/Group 87 0 R/Length 8912/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 624.3423 228.8612 cm
+0 0 m
+0.184 0.243 0.42 0.368 0.706 0.368 c
+1.235 0.368 1.503 0.015 1.515 -0.691 c
+1.515 -2.675 l
+1.029 -2.675 l
+1.029 -0.72 l
+1.029 -0.484 0.989 -0.319 0.912 -0.22 c
+0.831 -0.124 0.713 -0.073 0.559 -0.073 c
+0.441 -0.073 0.331 -0.114 0.235 -0.191 c
+0.136 -0.272 0.059 -0.378 0 -0.514 c
+0 -2.675 l
+-0.484 -2.675 l
+-0.484 1.559 l
+0 1.559 l
+h
+3.583 -2.675 m
+3.554 -2.609 3.532 -2.499 3.525 -2.352 c
+3.348 -2.609 3.128 -2.734 2.863 -2.734 c
+2.587 -2.734 2.371 -2.66 2.216 -2.513 c
+2.07 -2.358 1.996 -2.142 1.996 -1.866 c
+1.996 -1.565 2.099 -1.323 2.305 -1.146 c
+2.51 -0.962 2.793 -0.866 3.157 -0.866 c
+3.51 -0.866 l
+3.51 -0.544 l
+3.51 -0.367 3.47 -0.246 3.392 -0.176 c
+3.311 -0.099 3.194 -0.058 3.04 -0.058 c
+2.893 -0.058 2.768 -0.103 2.672 -0.191 c
+2.584 -0.278 2.54 -0.389 2.54 -0.514 c
+2.055 -0.514 l
+2.055 -0.367 2.099 -0.228 2.187 -0.087 c
+2.275 0.059 2.392 0.169 2.54 0.25 c
+2.694 0.327 2.866 0.368 3.055 0.368 c
+3.367 0.368 3.602 0.287 3.76 0.133 c
+3.914 -0.014 3.995 -0.235 3.995 -0.529 c
+3.995 -2.028 l
+4.002 -2.263 4.039 -2.466 4.098 -2.63 c
+4.098 -2.675 l
+h
+2.937 -2.293 m
+3.055 -2.293 3.165 -2.26 3.275 -2.19 c
+3.381 -2.123 3.458 -2.04 3.51 -1.94 c
+3.51 -1.234 l
+3.246 -1.234 l
+3.01 -1.234 2.822 -1.286 2.687 -1.381 c
+2.558 -1.481 2.496 -1.624 2.496 -1.807 c
+2.496 -1.977 2.525 -2.098 2.584 -2.175 c
+2.65 -2.256 2.768 -2.293 2.937 -2.293 c
+5.604 -2.308 m
+5.77 -2.308 5.902 -2.26 6.001 -2.16 c
+6.097 -2.065 6.152 -1.922 6.163 -1.734 c
+6.619 -1.734 l
+6.608 -2.021 6.505 -2.26 6.31 -2.454 c
+6.122 -2.642 5.887 -2.734 5.604 -2.734 c
+5.241 -2.734 4.962 -2.616 4.767 -2.381 c
+4.568 -2.146 4.472 -1.801 4.472 -1.337 c
+4.472 -1.014 l
+4.472 -0.565 4.564 -0.22 4.752 0.015 c
+4.946 0.25 5.23 0.368 5.604 0.368 c
+5.906 0.368 6.149 0.269 6.325 0.074 c
+6.508 -0.124 6.608 -0.389 6.619 -0.72 c
+6.163 -0.72 l
+6.141 -0.496 6.082 -0.33 5.987 -0.22 c
+5.898 -0.114 5.77 -0.058 5.604 -0.058 c
+5.388 -0.058 5.226 -0.132 5.12 -0.278 c
+5.02 -0.419 4.965 -0.646 4.958 -0.97 c
+4.958 -1.352 l
+4.958 -1.705 5.006 -1.955 5.104 -2.102 c
+5.212 -2.241 5.376 -2.308 5.604 -2.308 c
+7.765 -1.308 m
+7.501 -1.602 l
+7.501 -2.675 l
+7.015 -2.675 l
+7.015 1.559 l
+7.501 1.559 l
+7.501 -0.97 l
+8.426 0.309 l
+9.015 0.309 l
+8.074 -0.941 l
+9.147 -2.675 l
+8.574 -2.675 l
+h
+9.922 -2.675 -0.5 2.984 re
+9.952 1.103 m
+9.952 1.015 9.926 0.941 9.878 0.882 c
+9.837 0.831 9.768 0.809 9.673 0.809 c
+9.584 0.809 9.515 0.831 9.467 0.882 c
+9.426 0.941 9.407 1.008 9.407 1.088 c
+9.407 1.176 9.426 1.25 9.467 1.309 c
+9.515 1.368 9.584 1.397 9.673 1.397 c
+9.768 1.397 9.837 1.368 9.878 1.309 c
+9.926 1.25 9.952 1.18 9.952 1.103 c
+11.051 0.309 m
+11.065 -0.014 l
+11.248 0.239 11.491 0.368 11.786 0.368 c
+12.314 0.368 12.582 0.015 12.594 -0.691 c
+12.594 -2.675 l
+12.109 -2.675 l
+12.109 -0.72 l
+12.109 -0.484 12.069 -0.319 11.992 -0.22 c
+11.911 -0.124 11.793 -0.073 11.639 -0.073 c
+11.521 -0.073 11.41 -0.114 11.315 -0.191 c
+11.216 -0.272 11.138 -0.378 11.08 -0.514 c
+11.08 -2.675 l
+10.595 -2.675 l
+10.595 0.309 l
+h
+13.046 -1.043 m
+13.046 -0.584 13.127 -0.235 13.295 0 c
+13.472 0.243 13.722 0.368 14.045 0.368 c
+14.346 0.368 14.579 0.235 14.737 -0.029 c
+14.766 0.309 l
+15.207 0.309 l
+15.207 -2.705 l
+15.207 -3.075 15.107 -3.358 14.913 -3.557 c
+14.725 -3.752 14.464 -3.85 14.134 -3.85 c
+13.987 -3.85 13.818 -3.81 13.634 -3.733 c
+13.446 -3.663 13.311 -3.575 13.222 -3.469 c
+13.413 -3.131 l
+13.619 -3.336 13.843 -3.439 14.089 -3.439 c
+14.49 -3.439 14.696 -3.212 14.707 -2.763 c
+14.707 -2.381 l
+14.549 -2.616 14.328 -2.734 14.045 -2.734 c
+13.729 -2.734 13.486 -2.616 13.311 -2.381 c
+13.141 -2.146 13.053 -1.815 13.046 -1.381 c
+h
+13.531 -1.323 m
+13.531 -1.657 13.579 -1.903 13.678 -2.057 c
+13.773 -2.215 13.935 -2.293 14.163 -2.293 c
+14.406 -2.293 14.589 -2.171 14.707 -1.925 c
+14.707 -0.44 l
+14.589 -0.198 14.413 -0.073 14.178 -0.073 c
+13.95 -0.073 13.788 -0.154 13.693 -0.309 c
+13.594 -0.467 13.538 -0.706 13.531 -1.028 c
+h
+18.18 -0.147 m
+18.109 -0.139 18.036 -0.132 17.959 -0.132 c
+17.702 -0.132 17.525 -0.272 17.43 -0.544 c
+17.43 -2.675 l
+16.945 -2.675 l
+16.945 0.309 l
+17.415 0.309 l
+17.43 0 l
+17.555 0.243 17.739 0.368 17.974 0.368 c
+18.051 0.368 18.113 0.353 18.165 0.324 c
+h
+19.08 -2.675 -0.5 2.984 re
+19.109 1.103 m
+19.109 1.015 19.084 0.941 19.036 0.882 c
+18.995 0.831 18.926 0.809 18.83 0.809 c
+18.741 0.809 18.672 0.831 18.624 0.882 c
+18.583 0.941 18.566 1.008 18.566 1.088 c
+18.566 1.176 18.583 1.25 18.624 1.309 c
+18.672 1.368 18.741 1.397 18.83 1.397 c
+18.926 1.397 18.995 1.368 19.036 1.309 c
+19.084 1.25 19.109 1.18 19.109 1.103 c
+19.649 -1.043 m
+19.649 -0.584 19.73 -0.235 19.899 0 c
+20.075 0.243 20.326 0.368 20.649 0.368 c
+20.95 0.368 21.182 0.235 21.34 -0.029 c
+21.369 0.309 l
+21.81 0.309 l
+21.81 -2.705 l
+21.81 -3.075 21.711 -3.358 21.517 -3.557 c
+21.328 -3.752 21.068 -3.85 20.737 -3.85 c
+20.59 -3.85 20.421 -3.81 20.237 -3.733 c
+20.05 -3.663 19.914 -3.575 19.826 -3.469 c
+20.017 -3.131 l
+20.223 -3.336 20.447 -3.439 20.693 -3.439 c
+21.093 -3.439 21.299 -3.212 21.311 -2.763 c
+21.311 -2.381 l
+21.153 -2.616 20.931 -2.734 20.649 -2.734 c
+20.333 -2.734 20.09 -2.616 19.914 -2.381 c
+19.745 -2.146 19.657 -1.815 19.649 -1.381 c
+h
+20.135 -1.323 m
+20.135 -1.657 20.183 -1.903 20.281 -2.057 c
+20.377 -2.215 20.539 -2.293 20.767 -2.293 c
+21.01 -2.293 21.193 -2.171 21.311 -1.925 c
+21.311 -0.44 l
+21.193 -0.198 21.016 -0.073 20.781 -0.073 c
+20.553 -0.073 20.392 -0.154 20.297 -0.309 c
+20.197 -0.467 20.142 -0.706 20.135 -1.028 c
+h
+22.832 0 m
+23.015 0.243 23.25 0.368 23.537 0.368 c
+24.067 0.368 24.335 0.015 24.345 -0.691 c
+24.345 -2.675 l
+23.861 -2.675 l
+23.861 -0.72 l
+23.861 -0.484 23.82 -0.319 23.743 -0.22 c
+23.662 -0.124 23.545 -0.073 23.391 -0.073 c
+23.273 -0.073 23.163 -0.114 23.067 -0.191 c
+22.968 -0.272 22.891 -0.378 22.832 -0.514 c
+22.832 -2.675 l
+22.346 -2.675 l
+22.346 1.559 l
+22.832 1.559 l
+h
+25.521 1.029 m
+25.521 0.309 l
+25.978 0.309 l
+25.978 -0.087 l
+25.521 -0.087 l
+25.521 -1.94 l
+25.521 -2.057 25.54 -2.146 25.581 -2.204 c
+25.617 -2.263 25.687 -2.293 25.787 -2.293 c
+25.845 -2.293 25.908 -2.285 25.978 -2.263 c
+25.978 -2.675 l
+25.86 -2.711 25.746 -2.734 25.639 -2.734 c
+25.44 -2.734 25.29 -2.667 25.184 -2.528 c
+25.085 -2.392 25.037 -2.197 25.037 -1.94 c
+25.037 -0.087 l
+24.581 -0.087 l
+24.581 0.309 l
+25.037 0.309 l
+25.037 1.029 l
+h
+28.131 0 m
+28.314 0.243 28.55 0.368 28.837 0.368 c
+29.365 0.368 29.633 0.015 29.645 -0.691 c
+29.645 -2.675 l
+29.16 -2.675 l
+29.16 -0.72 l
+29.16 -0.484 29.12 -0.319 29.043 -0.22 c
+28.962 -0.124 28.844 -0.073 28.69 -0.073 c
+28.572 -0.073 28.461 -0.114 28.366 -0.191 c
+28.266 -0.272 28.189 -0.378 28.131 -0.514 c
+28.131 -2.675 l
+27.646 -2.675 l
+27.646 1.559 l
+28.131 1.559 l
+h
+31.247 -2.734 m
+30.872 -2.734 30.589 -2.627 30.394 -2.41 c
+30.196 -2.186 30.101 -1.859 30.101 -1.425 c
+30.101 -1.058 l
+30.101 -0.617 30.192 -0.272 30.379 -0.014 c
+30.574 0.239 30.85 0.368 31.203 0.368 c
+31.545 0.368 31.798 0.254 31.967 0.03 c
+32.143 -0.198 32.235 -0.544 32.247 -1.014 c
+32.247 -1.323 l
+30.585 -1.323 l
+30.585 -1.396 l
+30.585 -1.72 30.645 -1.955 30.762 -2.102 c
+30.88 -2.241 31.048 -2.308 31.277 -2.308 c
+31.424 -2.308 31.549 -2.285 31.659 -2.234 c
+31.765 -2.175 31.868 -2.087 31.967 -1.969 c
+32.218 -2.278 l
+32.012 -2.583 31.688 -2.734 31.247 -2.734 c
+31.203 -0.058 m
+30.997 -0.058 30.843 -0.128 30.747 -0.264 c
+30.648 -0.404 30.593 -0.617 30.585 -0.911 c
+31.761 -0.911 l
+31.761 -0.837 l
+31.74 -0.565 31.688 -0.367 31.599 -0.249 c
+31.512 -0.124 31.379 -0.058 31.203 -0.058 c
+33.893 -0.147 m
+33.823 -0.139 33.749 -0.132 33.672 -0.132 c
+33.415 -0.132 33.239 -0.272 33.143 -0.544 c
+33.143 -2.675 l
+32.658 -2.675 l
+32.658 0.309 l
+33.128 0.309 l
+33.143 0 l
+33.268 0.243 33.452 0.368 33.687 0.368 c
+33.764 0.368 33.826 0.353 33.878 0.324 c
+h
+35.267 -2.734 m
+34.892 -2.734 34.61 -2.627 34.415 -2.41 c
+34.217 -2.186 34.121 -1.859 34.121 -1.425 c
+34.121 -1.058 l
+34.121 -0.617 34.213 -0.272 34.4 -0.014 c
+34.595 0.239 34.87 0.368 35.223 0.368 c
+35.565 0.368 35.819 0.254 35.987 0.03 c
+36.164 -0.198 36.256 -0.544 36.266 -1.014 c
+36.266 -1.323 l
+34.606 -1.323 l
+34.606 -1.396 l
+34.606 -1.72 34.664 -1.955 34.782 -2.102 c
+34.9 -2.241 35.069 -2.308 35.297 -2.308 c
+35.443 -2.308 35.568 -2.285 35.679 -2.234 c
+35.786 -2.175 35.888 -2.087 35.987 -1.969 c
+36.237 -2.278 l
+36.031 -2.583 35.708 -2.734 35.267 -2.734 c
+35.223 -0.058 m
+35.017 -0.058 34.863 -0.128 34.767 -0.264 c
+34.668 -0.404 34.613 -0.617 34.606 -0.911 c
+35.782 -0.911 l
+35.782 -0.837 l
+35.759 -0.565 35.708 -0.367 35.62 -0.249 c
+35.532 -0.124 35.399 -0.058 35.223 -0.058 c
+37.222 -1.543 m
+36.781 -1.543 l
+36.752 1.338 l
+37.266 1.338 l
+h
+37.016 -2.131 m
+37.112 -2.131 37.182 -2.16 37.222 -2.219 c
+37.27 -2.271 37.296 -2.337 37.296 -2.425 c
+37.296 -2.506 37.27 -2.572 37.222 -2.63 c
+37.182 -2.678 37.112 -2.705 37.016 -2.705 c
+36.928 -2.705 36.858 -2.678 36.81 -2.63 c
+36.759 -2.572 36.737 -2.506 36.737 -2.425 c
+36.737 -2.337 36.759 -2.271 36.81 -2.219 c
+36.858 -2.16 36.928 -2.131 37.016 -2.131 c
+f
+Q
+ endstream endobj 52 0 obj <</BBox[699.591 449.925 766.733 444.647]/Group 88 0 R/Length 17061/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 700.8254 448.3809 cm
+0 0 m
+-0.07 0.008 -0.143 0.015 -0.22 0.015 c
+-0.478 0.015 -0.654 -0.125 -0.75 -0.397 c
+-0.75 -2.528 l
+-1.234 -2.528 l
+-1.234 0.455 l
+-0.764 0.455 l
+-0.75 0.147 l
+-0.625 0.389 -0.44 0.515 -0.205 0.515 c
+-0.128 0.515 -0.066 0.5 -0.014 0.47 c
+h
+1.378 -2.587 m
+1.004 -2.587 0.721 -2.481 0.526 -2.263 c
+0.327 -2.04 0.231 -1.712 0.231 -1.278 c
+0.231 -0.912 l
+0.231 -0.47 0.324 -0.125 0.511 0.133 c
+0.706 0.386 0.981 0.515 1.334 0.515 c
+1.676 0.515 1.929 0.401 2.099 0.177 c
+2.275 -0.052 2.367 -0.397 2.378 -0.867 c
+2.378 -1.176 l
+0.717 -1.176 l
+0.717 -1.249 l
+0.717 -1.573 0.775 -1.808 0.893 -1.955 c
+1.01 -2.094 1.18 -2.161 1.407 -2.161 c
+1.555 -2.161 1.679 -2.138 1.79 -2.088 c
+1.897 -2.028 1.999 -1.94 2.099 -1.822 c
+2.348 -2.132 l
+2.143 -2.437 1.819 -2.587 1.378 -2.587 c
+1.334 0.088 m
+1.128 0.088 0.974 0.019 0.879 -0.118 c
+0.779 -0.257 0.724 -0.47 0.717 -0.764 c
+1.893 -0.764 l
+1.893 -0.691 l
+1.871 -0.419 1.819 -0.22 1.731 -0.103 c
+1.643 0.023 1.511 0.088 1.334 0.088 c
+3.245 0.455 m
+3.26 0.177 l
+3.437 0.401 3.675 0.515 3.98 0.515 c
+4.31 0.515 4.542 0.368 4.671 0.073 c
+4.854 0.368 5.116 0.515 5.45 0.515 c
+6.009 0.515 6.292 0.169 6.302 -0.515 c
+6.302 -2.528 l
+5.818 -2.528 l
+5.818 -0.559 l
+5.818 -0.345 5.777 -0.187 5.7 -0.088 c
+5.619 0.019 5.486 0.073 5.303 0.073 c
+5.156 0.073 5.039 0.015 4.95 -0.103 c
+4.862 -0.213 4.807 -0.353 4.788 -0.529 c
+4.788 -2.528 l
+4.303 -2.528 l
+4.303 -0.544 l
+4.293 -0.133 4.119 0.073 3.789 0.073 c
+3.543 0.073 3.37 -0.052 3.275 -0.294 c
+3.275 -2.528 l
+2.789 -2.528 l
+2.789 0.455 l
+h
+6.648 -0.897 m
+6.648 -0.467 6.751 -0.125 6.957 0.133 c
+7.169 0.386 7.449 0.515 7.794 0.515 c
+8.136 0.515 8.412 0.386 8.617 0.133 c
+8.831 -0.114 8.945 -0.448 8.956 -0.867 c
+8.956 -1.176 l
+8.956 -1.61 8.846 -1.951 8.632 -2.205 c
+8.426 -2.462 8.147 -2.587 7.794 -2.587 c
+7.449 -2.587 7.177 -2.466 6.971 -2.219 c
+6.766 -1.977 6.655 -1.643 6.648 -1.22 c
+h
+7.133 -1.176 m
+7.133 -1.492 7.192 -1.735 7.31 -1.911 c
+7.435 -2.08 7.596 -2.161 7.794 -2.161 c
+8.224 -2.161 8.449 -1.852 8.47 -1.234 c
+8.47 -0.897 l
+8.47 -0.596 8.405 -0.353 8.279 -0.176 c
+8.162 0 8 0.088 7.794 0.088 c
+7.596 0.088 7.435 0 7.31 -0.176 c
+7.192 -0.353 7.133 -0.596 7.133 -0.897 c
+h
+10.028 1.176 m
+10.028 0.455 l
+10.485 0.455 l
+10.485 0.059 l
+10.028 0.059 l
+10.028 -1.793 l
+10.028 -1.911 10.047 -1.999 10.088 -2.057 c
+10.124 -2.117 10.194 -2.146 10.294 -2.146 c
+10.352 -2.146 10.415 -2.138 10.485 -2.117 c
+10.485 -2.528 l
+10.367 -2.564 10.253 -2.587 10.146 -2.587 c
+9.947 -2.587 9.797 -2.521 9.691 -2.381 c
+9.592 -2.246 9.544 -2.051 9.544 -1.793 c
+9.544 0.059 l
+9.088 0.059 l
+9.088 0.455 l
+9.544 0.455 l
+9.544 1.176 l
+h
+12.009 -2.587 m
+11.634 -2.587 11.352 -2.481 11.157 -2.263 c
+10.959 -2.04 10.863 -1.712 10.863 -1.278 c
+10.863 -0.912 l
+10.863 -0.47 10.955 -0.125 11.142 0.133 c
+11.337 0.386 11.612 0.515 11.965 0.515 c
+12.307 0.515 12.561 0.401 12.73 0.177 c
+12.906 -0.052 12.998 -0.397 13.009 -0.867 c
+13.009 -1.176 l
+11.348 -1.176 l
+11.348 -1.249 l
+11.348 -1.573 11.406 -1.808 11.524 -1.955 c
+11.642 -2.094 11.811 -2.161 12.039 -2.161 c
+12.185 -2.161 12.311 -2.138 12.421 -2.088 c
+12.528 -2.028 12.63 -1.94 12.73 -1.822 c
+12.979 -2.132 l
+12.773 -2.437 12.451 -2.587 12.009 -2.587 c
+11.965 0.088 m
+11.759 0.088 11.605 0.019 11.51 -0.118 c
+11.41 -0.257 11.356 -0.47 11.348 -0.764 c
+12.524 -0.764 l
+12.524 -0.691 l
+12.501 -0.419 12.451 -0.22 12.362 -0.103 c
+12.274 0.023 12.141 0.088 11.965 0.088 c
+15.857 0 m
+15.787 0.008 15.713 0.015 15.636 0.015 c
+15.379 0.015 15.202 -0.125 15.107 -0.397 c
+15.107 -2.528 l
+14.622 -2.528 l
+14.622 0.455 l
+15.092 0.455 l
+15.107 0.147 l
+15.232 0.389 15.416 0.515 15.651 0.515 c
+15.728 0.515 15.79 0.5 15.842 0.47 c
+h
+17.235 -2.587 m
+16.86 -2.587 16.577 -2.481 16.382 -2.263 c
+16.184 -2.04 16.089 -1.712 16.089 -1.278 c
+16.089 -0.912 l
+16.089 -0.47 16.18 -0.125 16.368 0.133 c
+16.563 0.386 16.838 0.515 17.191 0.515 c
+17.533 0.515 17.786 0.401 17.955 0.177 c
+18.132 -0.052 18.223 -0.397 18.234 -0.867 c
+18.234 -1.176 l
+16.573 -1.176 l
+16.573 -1.249 l
+16.573 -1.573 16.632 -1.808 16.75 -1.955 c
+16.867 -2.094 17.037 -2.161 17.264 -2.161 c
+17.411 -2.161 17.536 -2.138 17.646 -2.088 c
+17.753 -2.028 17.856 -1.94 17.955 -1.822 c
+18.205 -2.132 l
+17.999 -2.437 17.676 -2.587 17.235 -2.587 c
+17.191 0.088 m
+16.985 0.088 16.831 0.019 16.735 -0.118 c
+16.636 -0.257 16.581 -0.47 16.573 -0.764 c
+17.749 -0.764 l
+17.749 -0.691 l
+17.727 -0.419 17.676 -0.22 17.588 -0.103 c
+17.5 0.023 17.367 0.088 17.191 0.088 c
+20.792 -1.176 m
+20.792 -1.646 20.708 -1.999 20.542 -2.234 c
+20.373 -2.469 20.135 -2.587 19.822 -2.587 c
+19.517 -2.587 19.285 -2.477 19.131 -2.249 c
+19.131 -3.675 l
+18.646 -3.675 l
+18.646 0.455 l
+19.087 0.455 l
+19.117 0.118 l
+19.271 0.382 19.503 0.515 19.807 0.515 c
+20.137 0.515 20.384 0.397 20.542 0.162 c
+20.708 -0.066 20.792 -0.405 20.792 -0.852 c
+h
+20.307 -0.897 m
+20.307 -0.565 20.252 -0.32 20.145 -0.162 c
+20.046 -0.008 19.884 0.073 19.661 0.073 c
+19.425 0.073 19.248 -0.044 19.131 -0.279 c
+19.131 -1.822 l
+19.248 -2.051 19.429 -2.161 19.675 -2.161 c
+19.888 -2.161 20.046 -2.084 20.145 -1.926 c
+20.252 -1.771 20.307 -1.529 20.307 -1.205 c
+h
+21.145 -0.897 m
+21.145 -0.467 21.248 -0.125 21.454 0.133 c
+21.667 0.386 21.946 0.515 22.292 0.515 c
+22.633 0.515 22.909 0.386 23.115 0.133 c
+23.328 -0.114 23.441 -0.448 23.453 -0.867 c
+23.453 -1.176 l
+23.453 -1.61 23.343 -1.951 23.129 -2.205 c
+22.923 -2.462 22.645 -2.587 22.292 -2.587 c
+21.946 -2.587 21.674 -2.466 21.469 -2.219 c
+21.263 -1.977 21.153 -1.643 21.145 -1.22 c
+h
+21.63 -1.176 m
+21.63 -1.492 21.689 -1.735 21.806 -1.911 c
+21.931 -2.08 22.093 -2.161 22.292 -2.161 c
+22.722 -2.161 22.946 -1.852 22.967 -1.234 c
+22.967 -0.897 l
+22.967 -0.596 22.901 -0.353 22.776 -0.176 c
+22.659 0 22.497 0.088 22.292 0.088 c
+22.093 0.088 21.931 0 21.806 -0.176 c
+21.689 -0.353 21.63 -0.596 21.63 -0.897 c
+h
+25.334 -1.764 m
+25.334 -1.658 25.294 -1.569 25.216 -1.5 c
+25.136 -1.422 24.985 -1.334 24.761 -1.234 c
+24.496 -1.128 24.309 -1.036 24.202 -0.956 c
+24.092 -0.878 24.015 -0.79 23.967 -0.691 c
+23.915 -0.596 23.894 -0.478 23.894 -0.338 c
+23.894 -0.095 23.981 0.106 24.158 0.264 c
+24.334 0.43 24.559 0.515 24.835 0.515 c
+25.128 0.515 25.363 0.426 25.54 0.25 c
+25.716 0.081 25.805 -0.133 25.805 -0.397 c
+25.319 -0.397 l
+25.319 -0.261 25.268 -0.147 25.172 -0.058 c
+25.084 0.037 24.97 0.088 24.835 0.088 c
+24.687 0.088 24.573 0.048 24.496 -0.029 c
+24.415 -0.099 24.378 -0.198 24.378 -0.324 c
+24.378 -0.422 24.408 -0.5 24.467 -0.559 c
+24.526 -0.617 24.665 -0.698 24.893 -0.794 c
+25.253 -0.941 25.5 -1.084 25.628 -1.22 c
+25.764 -1.348 25.834 -1.521 25.834 -1.735 c
+25.834 -1.992 25.739 -2.198 25.554 -2.352 c
+25.378 -2.51 25.143 -2.587 24.849 -2.587 c
+24.533 -2.587 24.28 -2.499 24.085 -2.323 c
+23.898 -2.138 23.805 -1.907 23.805 -1.631 c
+24.291 -1.631 l
+24.297 -1.801 24.349 -1.933 24.438 -2.028 c
+24.533 -2.117 24.673 -2.161 24.849 -2.161 c
+25.003 -2.161 25.121 -2.128 25.201 -2.057 c
+25.29 -1.992 25.334 -1.893 25.334 -1.764 c
+26.819 -2.528 -0.5 2.983 re
+26.848 1.249 m
+26.848 1.161 26.822 1.087 26.774 1.029 c
+26.734 0.977 26.664 0.956 26.568 0.956 c
+26.481 0.956 26.41 0.977 26.363 1.029 c
+26.323 1.087 26.304 1.154 26.304 1.235 c
+26.304 1.323 26.323 1.396 26.363 1.455 c
+26.41 1.514 26.481 1.544 26.568 1.544 c
+26.664 1.544 26.734 1.514 26.774 1.455 c
+26.822 1.396 26.848 1.326 26.848 1.249 c
+28.127 1.176 m
+28.127 0.455 l
+28.582 0.455 l
+28.582 0.059 l
+28.127 0.059 l
+28.127 -1.793 l
+28.127 -1.911 28.145 -1.999 28.185 -2.057 c
+28.222 -2.117 28.292 -2.146 28.392 -2.146 c
+28.45 -2.146 28.513 -2.138 28.582 -2.117 c
+28.582 -2.528 l
+28.465 -2.564 28.351 -2.587 28.245 -2.587 c
+28.046 -2.587 27.896 -2.521 27.789 -2.381 c
+27.69 -2.246 27.642 -2.051 27.642 -1.793 c
+27.642 0.059 l
+27.186 0.059 l
+27.186 0.455 l
+27.642 0.455 l
+27.642 1.176 l
+h
+28.895 -0.897 m
+28.895 -0.467 28.997 -0.125 29.203 0.133 c
+29.417 0.386 29.696 0.515 30.042 0.515 c
+30.383 0.515 30.659 0.386 30.865 0.133 c
+31.077 -0.114 31.191 -0.448 31.202 -0.867 c
+31.202 -1.176 l
+31.202 -1.61 31.092 -1.951 30.879 -2.205 c
+30.674 -2.462 30.394 -2.587 30.042 -2.587 c
+29.696 -2.587 29.424 -2.466 29.218 -2.219 c
+29.012 -1.977 28.902 -1.643 28.895 -1.22 c
+h
+29.38 -1.176 m
+29.38 -1.492 29.439 -1.735 29.556 -1.911 c
+29.681 -2.08 29.843 -2.161 30.042 -2.161 c
+30.471 -2.161 30.695 -1.852 30.718 -1.234 c
+30.718 -0.897 l
+30.718 -0.596 30.651 -0.353 30.526 -0.176 c
+30.408 0 30.248 0.088 30.042 0.088 c
+29.843 0.088 29.681 0 29.556 -0.176 c
+29.439 -0.353 29.38 -0.596 29.38 -0.897 c
+h
+32.875 0 m
+32.805 0.008 32.731 0.015 32.654 0.015 c
+32.397 0.015 32.22 -0.125 32.125 -0.397 c
+32.125 -2.528 l
+31.64 -2.528 l
+31.64 0.455 l
+32.11 0.455 l
+32.125 0.147 l
+32.25 0.389 32.434 0.515 32.669 0.515 c
+32.746 0.515 32.808 0.5 32.86 0.47 c
+h
+33.775 -2.528 -0.501 2.983 re
+33.805 1.249 m
+33.805 1.161 33.778 1.087 33.731 1.029 c
+33.691 0.977 33.621 0.956 33.525 0.956 c
+33.437 0.956 33.367 0.977 33.319 1.029 c
+33.279 1.087 33.261 1.154 33.261 1.235 c
+33.261 1.323 33.279 1.396 33.319 1.455 c
+33.367 1.514 33.437 1.544 33.525 1.544 c
+33.621 1.544 33.691 1.514 33.731 1.455 c
+33.778 1.396 33.805 1.326 33.805 1.249 c
+35.509 -2.587 m
+35.135 -2.587 34.852 -2.481 34.657 -2.263 c
+34.458 -2.04 34.363 -1.712 34.363 -1.278 c
+34.363 -0.912 l
+34.363 -0.47 34.455 -0.125 34.642 0.133 c
+34.837 0.386 35.112 0.515 35.465 0.515 c
+35.807 0.515 36.06 0.401 36.23 0.177 c
+36.406 -0.052 36.498 -0.397 36.509 -0.867 c
+36.509 -1.176 l
+34.848 -1.176 l
+34.848 -1.249 l
+34.848 -1.573 34.907 -1.808 35.025 -1.955 c
+35.142 -2.094 35.311 -2.161 35.539 -2.161 c
+35.686 -2.161 35.811 -2.138 35.921 -2.088 c
+36.028 -2.028 36.13 -1.94 36.23 -1.822 c
+36.48 -2.132 l
+36.274 -2.437 35.95 -2.587 35.509 -2.587 c
+35.465 0.088 m
+35.26 0.088 35.106 0.019 35.01 -0.118 c
+34.911 -0.257 34.855 -0.47 34.848 -0.764 c
+36.024 -0.764 l
+36.024 -0.691 l
+36.002 -0.419 35.95 -0.22 35.862 -0.103 c
+35.774 0.023 35.642 0.088 35.465 0.088 c
+38.362 -1.764 m
+38.362 -1.658 38.321 -1.569 38.244 -1.5 c
+38.163 -1.422 38.012 -1.334 37.788 -1.234 c
+37.523 -1.128 37.336 -1.036 37.229 -0.956 c
+37.119 -0.878 37.042 -0.79 36.994 -0.691 c
+36.943 -0.596 36.92 -0.478 36.92 -0.338 c
+36.92 -0.095 37.009 0.106 37.186 0.264 c
+37.361 0.43 37.585 0.515 37.861 0.515 c
+38.155 0.515 38.391 0.426 38.567 0.25 c
+38.743 0.081 38.832 -0.133 38.832 -0.397 c
+38.346 -0.397 l
+38.346 -0.261 38.295 -0.147 38.2 -0.058 c
+38.111 0.037 37.997 0.088 37.861 0.088 c
+37.714 0.088 37.6 0.048 37.523 -0.029 c
+37.442 -0.099 37.406 -0.198 37.406 -0.324 c
+37.406 -0.422 37.435 -0.5 37.494 -0.559 c
+37.552 -0.617 37.693 -0.698 37.92 -0.794 c
+38.281 -0.941 38.526 -1.084 38.655 -1.22 c
+38.791 -1.348 38.861 -1.521 38.861 -1.735 c
+38.861 -1.992 38.765 -2.198 38.582 -2.352 c
+38.405 -2.51 38.17 -2.587 37.876 -2.587 c
+37.56 -2.587 37.306 -2.499 37.111 -2.323 c
+36.924 -2.138 36.833 -1.907 36.833 -1.631 c
+37.317 -1.631 l
+37.325 -1.801 37.377 -1.933 37.464 -2.028 c
+37.56 -2.117 37.699 -2.161 37.876 -2.161 c
+38.03 -2.161 38.148 -2.128 38.229 -2.057 c
+38.317 -1.992 38.362 -1.893 38.362 -1.764 c
+42.05 -2.528 m
+42.021 -2.462 41.999 -2.352 41.992 -2.205 c
+41.815 -2.462 41.595 -2.587 41.33 -2.587 c
+41.055 -2.587 40.837 -2.514 40.683 -2.367 c
+40.536 -2.212 40.463 -1.995 40.463 -1.72 c
+40.463 -1.419 40.566 -1.176 40.772 -0.999 c
+40.978 -0.816 41.261 -0.721 41.624 -0.721 c
+41.977 -0.721 l
+41.977 -0.397 l
+41.977 -0.22 41.936 -0.099 41.859 -0.029 c
+41.778 0.048 41.661 0.088 41.506 0.088 c
+41.36 0.088 41.234 0.044 41.139 -0.044 c
+41.051 -0.133 41.007 -0.243 41.007 -0.368 c
+40.521 -0.368 l
+40.521 -0.22 40.566 -0.081 40.654 0.059 c
+40.742 0.206 40.86 0.316 41.007 0.397 c
+41.161 0.474 41.334 0.515 41.521 0.515 c
+41.834 0.515 42.069 0.434 42.227 0.279 c
+42.381 0.133 42.462 -0.088 42.462 -0.382 c
+42.462 -1.881 l
+42.47 -2.117 42.506 -2.319 42.565 -2.484 c
+42.565 -2.528 l
+h
+41.404 -2.146 m
+41.521 -2.146 41.631 -2.113 41.742 -2.043 c
+41.849 -1.977 41.926 -1.893 41.977 -1.793 c
+41.977 -1.087 l
+41.712 -1.087 l
+41.477 -1.087 41.29 -1.139 41.154 -1.234 c
+41.026 -1.334 40.963 -1.477 40.963 -1.661 c
+40.963 -1.83 40.992 -1.951 41.051 -2.028 c
+41.117 -2.109 41.234 -2.146 41.404 -2.146 c
+44.483 -1.764 m
+44.483 -1.658 44.442 -1.569 44.365 -1.5 c
+44.284 -1.422 44.134 -1.334 43.91 -1.234 c
+43.646 -1.128 43.458 -1.036 43.351 -0.956 c
+43.241 -0.878 43.164 -0.79 43.116 -0.691 c
+43.065 -0.596 43.043 -0.478 43.043 -0.338 c
+43.043 -0.095 43.131 0.106 43.307 0.264 c
+43.484 0.43 43.708 0.515 43.983 0.515 c
+44.278 0.515 44.513 0.426 44.689 0.25 c
+44.866 0.081 44.953 -0.133 44.953 -0.397 c
+44.469 -0.397 l
+44.469 -0.261 44.417 -0.147 44.322 -0.058 c
+44.234 0.037 44.12 0.088 43.983 0.088 c
+43.837 0.088 43.723 0.048 43.646 -0.029 c
+43.565 -0.099 43.528 -0.198 43.528 -0.324 c
+43.528 -0.422 43.557 -0.5 43.616 -0.559 c
+43.675 -0.617 43.814 -0.698 44.043 -0.794 c
+44.402 -0.941 44.648 -1.084 44.777 -1.22 c
+44.913 -1.348 44.983 -1.521 44.983 -1.735 c
+44.983 -1.992 44.887 -2.198 44.704 -2.352 c
+44.527 -2.51 44.292 -2.587 43.998 -2.587 c
+43.682 -2.587 43.428 -2.499 43.234 -2.323 c
+43.046 -2.138 42.954 -1.907 42.954 -1.631 c
+43.44 -1.631 l
+43.447 -1.801 43.499 -1.933 43.586 -2.028 c
+43.682 -2.117 43.821 -2.161 43.998 -2.161 c
+44.153 -2.161 44.27 -2.128 44.351 -2.057 c
+44.44 -1.992 44.483 -1.893 44.483 -1.764 c
+47.453 -1.72 m
+47.997 0.455 l
+48.511 0.455 l
+47.54 -2.955 l
+47.471 -3.208 47.368 -3.399 47.232 -3.528 c
+47.092 -3.663 46.942 -3.734 46.776 -3.734 c
+46.707 -3.734 46.622 -3.719 46.526 -3.69 c
+46.526 -3.278 l
+46.63 -3.293 l
+46.765 -3.293 46.871 -3.256 46.952 -3.19 c
+47.041 -3.12 47.107 -3.002 47.158 -2.837 c
+47.247 -2.499 l
+46.379 0.455 l
+46.909 0.455 l
+h
+48.691 -0.897 m
+48.691 -0.467 48.793 -0.125 48.999 0.133 c
+49.213 0.386 49.492 0.515 49.838 0.515 c
+50.179 0.515 50.455 0.386 50.661 0.133 c
+50.873 -0.114 50.987 -0.448 50.998 -0.867 c
+50.998 -1.176 l
+50.998 -1.61 50.888 -1.951 50.675 -2.205 c
+50.47 -2.462 50.19 -2.587 49.838 -2.587 c
+49.492 -2.587 49.22 -2.466 49.014 -2.219 c
+48.808 -1.977 48.698 -1.643 48.691 -1.22 c
+h
+49.176 -1.176 m
+49.176 -1.492 49.235 -1.735 49.352 -1.911 c
+49.477 -2.08 49.639 -2.161 49.838 -2.161 c
+50.268 -2.161 50.491 -1.852 50.514 -1.234 c
+50.514 -0.897 l
+50.514 -0.596 50.447 -0.353 50.322 -0.176 c
+50.204 0 50.044 0.088 49.838 0.088 c
+49.639 0.088 49.477 0 49.352 -0.176 c
+49.235 -0.353 49.176 -0.596 49.176 -0.897 c
+h
+52.953 -2.263 m
+52.795 -2.481 52.56 -2.587 52.248 -2.587 c
+51.983 -2.587 51.777 -2.495 51.631 -2.308 c
+51.491 -2.124 51.425 -1.849 51.425 -1.484 c
+51.425 0.455 l
+51.91 0.455 l
+51.91 -1.455 l
+51.91 -1.918 52.049 -2.146 52.336 -2.146 c
+52.631 -2.146 52.828 -2.014 52.939 -1.749 c
+52.939 0.455 l
+53.439 0.455 l
+53.439 -2.528 l
+52.968 -2.528 l
+h
+57.415 -1.675 m
+57.871 0.455 l
+58.356 0.455 l
+57.62 -2.528 l
+57.238 -2.528 l
+56.65 -0.397 l
+56.077 -2.528 l
+55.695 -2.528 l
+54.96 0.455 l
+55.445 0.455 l
+55.916 -1.617 l
+56.459 0.455 l
+56.841 0.455 l
+h
+60.149 -2.528 m
+60.119 -2.462 60.097 -2.352 60.09 -2.205 c
+59.914 -2.462 59.693 -2.587 59.428 -2.587 c
+59.153 -2.587 58.936 -2.514 58.782 -2.367 c
+58.634 -2.212 58.561 -1.995 58.561 -1.72 c
+58.561 -1.419 58.664 -1.176 58.87 -0.999 c
+59.076 -0.816 59.359 -0.721 59.723 -0.721 c
+60.076 -0.721 l
+60.076 -0.397 l
+60.076 -0.22 60.035 -0.099 59.958 -0.029 c
+59.877 0.048 59.76 0.088 59.605 0.088 c
+59.458 0.088 59.333 0.044 59.237 -0.044 c
+59.149 -0.133 59.105 -0.243 59.105 -0.368 c
+58.62 -0.368 l
+58.62 -0.22 58.664 -0.081 58.752 0.059 c
+58.84 0.206 58.958 0.316 59.105 0.397 c
+59.259 0.474 59.432 0.515 59.619 0.515 c
+59.932 0.515 60.167 0.434 60.325 0.279 c
+60.479 0.133 60.56 -0.088 60.56 -0.382 c
+60.56 -1.881 l
+60.568 -2.117 60.604 -2.319 60.664 -2.484 c
+60.664 -2.528 l
+h
+59.502 -2.146 m
+59.619 -2.146 59.729 -2.113 59.84 -2.043 c
+59.947 -1.977 60.024 -1.893 60.076 -1.793 c
+60.076 -1.087 l
+59.81 -1.087 l
+59.575 -1.087 59.388 -1.139 59.252 -1.234 c
+59.124 -1.334 59.061 -1.477 59.061 -1.661 c
+59.061 -1.83 59.091 -1.951 59.149 -2.028 c
+59.216 -2.109 59.333 -2.146 59.502 -2.146 c
+61.597 0.455 m
+61.611 0.133 l
+61.795 0.386 62.038 0.515 62.331 0.515 c
+62.861 0.515 63.129 0.162 63.14 -0.544 c
+63.14 -2.528 l
+62.655 -2.528 l
+62.655 -0.573 l
+62.655 -0.338 62.615 -0.172 62.538 -0.073 c
+62.457 0.023 62.339 0.073 62.185 0.073 c
+62.067 0.073 61.957 0.033 61.861 -0.044 c
+61.762 -0.125 61.685 -0.231 61.626 -0.368 c
+61.626 -2.528 l
+61.141 -2.528 l
+61.141 0.455 l
+h
+64.323 1.176 m
+64.323 0.455 l
+64.779 0.455 l
+64.779 0.059 l
+64.323 0.059 l
+64.323 -1.793 l
+64.323 -1.911 64.342 -1.999 64.382 -2.057 c
+64.419 -2.117 64.489 -2.146 64.587 -2.146 c
+64.647 -2.146 64.709 -2.138 64.779 -2.117 c
+64.779 -2.528 l
+64.662 -2.564 64.548 -2.587 64.441 -2.587 c
+64.242 -2.587 64.092 -2.521 63.985 -2.381 c
+63.886 -2.246 63.839 -2.051 63.839 -1.793 c
+63.839 0.059 l
+63.382 0.059 l
+63.382 0.455 l
+63.839 0.455 l
+63.839 1.176 l
+h
+65.275 -2.263 m
+65.275 -2.175 65.297 -2.102 65.348 -2.043 c
+65.408 -1.984 65.485 -1.955 65.584 -1.955 c
+65.69 -1.955 65.767 -1.984 65.819 -2.043 c
+65.878 -2.102 65.907 -2.175 65.907 -2.263 c
+65.907 -2.344 65.878 -2.41 65.819 -2.469 c
+65.767 -2.528 65.69 -2.558 65.584 -2.558 c
+65.485 -2.558 65.408 -2.528 65.348 -2.469 c
+65.297 -2.41 65.275 -2.344 65.275 -2.263 c
+f
+Q
+ endstream endobj 53 0 obj <</BBox[703.328 443.501 763.072 438.121]/Group 89 0 R/Length 13734/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 704.9891 443.2802 cm
+0 0 m
+0.5 0 l
+0.5 -2.836 l
+0.5 -3.219 0.401 -3.524 0.206 -3.748 c
+0.008 -3.965 -0.257 -4.072 -0.588 -4.072 c
+-0.922 -4.072 -1.187 -3.969 -1.382 -3.763 c
+-1.569 -3.557 -1.661 -3.274 -1.661 -2.911 c
+-1.161 -2.911 l
+-1.161 -3.138 -1.114 -3.314 -1.014 -3.439 c
+-0.908 -3.568 -0.764 -3.63 -0.588 -3.63 c
+-0.411 -3.63 -0.272 -3.564 -0.162 -3.424 c
+-0.055 -3.289 0 -3.094 0 -2.836 c
+h
+2.609 -3.748 m
+2.452 -3.965 2.216 -4.072 1.903 -4.072 c
+1.639 -4.072 1.433 -3.979 1.286 -3.792 c
+1.147 -3.609 1.08 -3.333 1.08 -2.969 c
+1.08 -1.029 l
+1.565 -1.029 l
+1.565 -2.94 l
+1.565 -3.403 1.706 -3.63 1.992 -3.63 c
+2.286 -3.63 2.485 -3.499 2.595 -3.233 c
+2.595 -1.029 l
+3.094 -1.029 l
+3.094 -4.012 l
+2.624 -4.012 l
+h
+5.089 -3.248 m
+5.089 -3.142 5.049 -3.054 4.972 -2.984 c
+4.891 -2.907 4.74 -2.818 4.516 -2.719 c
+4.252 -2.612 4.064 -2.52 3.958 -2.44 c
+3.848 -2.362 3.771 -2.275 3.723 -2.175 c
+3.671 -2.08 3.649 -1.962 3.649 -1.822 c
+3.649 -1.579 3.738 -1.378 3.914 -1.22 c
+4.09 -1.055 4.314 -0.97 4.59 -0.97 c
+4.883 -0.97 5.119 -1.058 5.295 -1.234 c
+5.471 -1.404 5.56 -1.617 5.56 -1.881 c
+5.075 -1.881 l
+5.075 -1.745 5.024 -1.631 4.928 -1.543 c
+4.84 -1.448 4.725 -1.396 4.59 -1.396 c
+4.443 -1.396 4.329 -1.436 4.252 -1.514 c
+4.171 -1.583 4.135 -1.683 4.135 -1.808 c
+4.135 -1.907 4.164 -1.984 4.222 -2.043 c
+4.281 -2.102 4.421 -2.182 4.648 -2.278 c
+5.009 -2.425 5.255 -2.568 5.384 -2.705 c
+5.519 -2.833 5.589 -3.006 5.589 -3.219 c
+5.589 -3.476 5.494 -3.682 5.31 -3.836 c
+5.134 -3.994 4.898 -4.072 4.605 -4.072 c
+4.289 -4.072 4.035 -3.983 3.84 -3.807 c
+3.653 -3.623 3.561 -3.391 3.561 -3.116 c
+4.046 -3.116 l
+4.054 -3.285 4.104 -3.418 4.193 -3.513 c
+4.289 -3.601 4.428 -3.645 4.605 -3.645 c
+4.759 -3.645 4.877 -3.612 4.958 -3.542 c
+5.045 -3.476 5.089 -3.377 5.089 -3.248 c
+6.677 -0.309 m
+6.677 -1.029 l
+7.133 -1.029 l
+7.133 -1.425 l
+6.677 -1.425 l
+6.677 -3.278 l
+6.677 -3.395 6.695 -3.484 6.736 -3.542 c
+6.772 -3.601 6.842 -3.63 6.942 -3.63 c
+7 -3.63 7.063 -3.623 7.133 -3.601 c
+7.133 -4.012 l
+7.015 -4.049 6.901 -4.072 6.795 -4.072 c
+6.596 -4.072 6.446 -4.006 6.339 -3.865 c
+6.24 -3.73 6.192 -3.535 6.192 -3.278 c
+6.192 -1.425 l
+5.737 -1.425 l
+5.737 -1.029 l
+6.192 -1.029 l
+6.192 -0.309 l
+h
+10.036 -1.484 m
+9.966 -1.477 9.893 -1.469 9.816 -1.469 c
+9.558 -1.469 9.382 -1.61 9.286 -1.881 c
+9.286 -4.012 l
+8.802 -4.012 l
+8.802 -1.029 l
+9.272 -1.029 l
+9.286 -1.338 l
+9.411 -1.095 9.595 -0.97 9.83 -0.97 c
+9.907 -0.97 9.97 -0.985 10.021 -1.014 c
+h
+11.414 -4.072 m
+11.039 -4.072 10.756 -3.965 10.562 -3.748 c
+10.363 -3.524 10.267 -3.197 10.267 -2.763 c
+10.267 -2.396 l
+10.267 -1.955 10.359 -1.61 10.547 -1.352 c
+10.741 -1.099 11.017 -0.97 11.37 -0.97 c
+11.711 -0.97 11.965 -1.084 12.134 -1.308 c
+12.311 -1.536 12.403 -1.881 12.413 -2.352 c
+12.413 -2.66 l
+10.753 -2.66 l
+10.753 -2.734 l
+10.753 -3.057 10.811 -3.293 10.929 -3.439 c
+11.046 -3.579 11.216 -3.645 11.443 -3.645 c
+11.59 -3.645 11.715 -3.623 11.825 -3.572 c
+11.932 -3.513 12.035 -3.424 12.134 -3.307 c
+12.384 -3.616 l
+12.178 -3.921 11.855 -4.072 11.414 -4.072 c
+11.37 -1.396 m
+11.164 -1.396 11.009 -1.466 10.914 -1.602 c
+10.815 -1.741 10.76 -1.955 10.753 -2.248 c
+11.929 -2.248 l
+11.929 -2.175 l
+11.906 -1.903 11.855 -1.705 11.767 -1.587 c
+11.678 -1.462 11.546 -1.396 11.37 -1.396 c
+13.28 -1.029 m
+13.296 -1.308 l
+13.471 -1.084 13.71 -0.97 14.015 -0.97 c
+14.346 -0.97 14.578 -1.117 14.707 -1.411 c
+14.89 -1.117 15.152 -0.97 15.486 -0.97 c
+16.044 -0.97 16.327 -1.315 16.338 -1.999 c
+16.338 -4.012 l
+15.853 -4.012 l
+15.853 -2.043 l
+15.853 -1.83 15.813 -1.672 15.736 -1.573 c
+15.655 -1.466 15.522 -1.411 15.339 -1.411 c
+15.191 -1.411 15.073 -1.469 14.986 -1.587 c
+14.898 -1.697 14.842 -1.837 14.824 -2.013 c
+14.824 -4.012 l
+14.339 -4.012 l
+14.339 -2.028 l
+14.328 -1.617 14.155 -1.411 13.824 -1.411 c
+13.579 -1.411 13.406 -1.536 13.31 -1.778 c
+13.31 -4.012 l
+12.825 -4.012 l
+12.825 -1.029 l
+h
+17.845 -4.072 m
+17.47 -4.072 17.187 -3.965 16.993 -3.748 c
+16.794 -3.524 16.698 -3.197 16.698 -2.763 c
+16.698 -2.396 l
+16.698 -1.955 16.79 -1.61 16.978 -1.352 c
+17.172 -1.099 17.448 -0.97 17.801 -0.97 c
+18.142 -0.97 18.396 -1.084 18.565 -1.308 c
+18.741 -1.536 18.834 -1.881 18.844 -2.352 c
+18.844 -2.66 l
+17.184 -2.66 l
+17.184 -2.734 l
+17.184 -3.057 17.242 -3.293 17.359 -3.439 c
+17.477 -3.579 17.646 -3.645 17.874 -3.645 c
+18.021 -3.645 18.146 -3.623 18.256 -3.572 c
+18.363 -3.513 18.466 -3.424 18.565 -3.307 c
+18.815 -3.616 l
+18.609 -3.921 18.286 -4.072 17.845 -4.072 c
+17.801 -1.396 m
+17.595 -1.396 17.44 -1.466 17.345 -1.602 c
+17.246 -1.741 17.19 -1.955 17.184 -2.248 c
+18.359 -2.248 l
+18.359 -2.175 l
+18.337 -1.903 18.286 -1.705 18.198 -1.587 c
+18.109 -1.462 17.977 -1.396 17.801 -1.396 c
+21.417 -2.66 m
+21.417 -3.131 21.332 -3.484 21.167 -3.719 c
+20.997 -3.954 20.756 -4.072 20.432 -4.072 c
+20.116 -4.072 19.881 -3.935 19.726 -3.659 c
+19.697 -4.012 l
+19.256 -4.012 l
+19.256 0.221 l
+19.74 0.221 l
+19.74 -1.352 l
+19.895 -1.099 20.127 -0.97 20.432 -0.97 c
+20.756 -0.97 20.997 -1.087 21.167 -1.323 c
+21.332 -1.558 21.417 -1.907 21.417 -2.366 c
+h
+20.931 -2.381 m
+20.931 -2.028 20.88 -1.778 20.785 -1.631 c
+20.685 -1.484 20.523 -1.411 20.299 -1.411 c
+20.053 -1.411 19.866 -1.55 19.74 -1.822 c
+19.74 -3.233 l
+19.858 -3.499 20.05 -3.63 20.314 -3.63 c
+20.527 -3.63 20.685 -3.557 20.785 -3.41 c
+20.88 -3.256 20.931 -3.013 20.931 -2.69 c
+h
+22.919 -4.072 m
+22.545 -4.072 22.262 -3.965 22.067 -3.748 c
+21.868 -3.524 21.773 -3.197 21.773 -2.763 c
+21.773 -2.396 l
+21.773 -1.955 21.865 -1.61 22.052 -1.352 c
+22.247 -1.099 22.522 -0.97 22.875 -0.97 c
+23.217 -0.97 23.471 -1.084 23.64 -1.308 c
+23.816 -1.536 23.908 -1.881 23.919 -2.352 c
+23.919 -2.66 l
+22.258 -2.66 l
+22.258 -2.734 l
+22.258 -3.057 22.317 -3.293 22.435 -3.439 c
+22.552 -3.579 22.721 -3.645 22.949 -3.645 c
+23.096 -3.645 23.221 -3.623 23.331 -3.572 c
+23.438 -3.513 23.54 -3.424 23.64 -3.307 c
+23.89 -3.616 l
+23.684 -3.921 23.36 -4.072 22.919 -4.072 c
+22.875 -1.396 m
+22.67 -1.396 22.516 -1.466 22.42 -1.602 c
+22.321 -1.741 22.265 -1.955 22.258 -2.248 c
+23.434 -2.248 l
+23.434 -2.175 l
+23.412 -1.903 23.36 -1.705 23.272 -1.587 c
+23.185 -1.462 23.052 -1.396 22.875 -1.396 c
+25.566 -1.484 m
+25.496 -1.477 25.422 -1.469 25.344 -1.469 c
+25.088 -1.469 24.911 -1.61 24.816 -1.881 c
+24.816 -4.012 l
+24.33 -4.012 l
+24.33 -1.029 l
+24.801 -1.029 l
+24.816 -1.338 l
+24.941 -1.095 25.124 -0.97 25.359 -0.97 c
+25.437 -0.97 25.5 -0.985 25.55 -1.014 c
+h
+27.771 -0.309 m
+27.771 -1.029 l
+28.226 -1.029 l
+28.226 -1.425 l
+27.771 -1.425 l
+27.771 -3.278 l
+27.771 -3.395 27.789 -3.484 27.829 -3.542 c
+27.866 -3.601 27.935 -3.63 28.035 -3.63 c
+28.093 -3.63 28.156 -3.623 28.226 -3.601 c
+28.226 -4.012 l
+28.108 -4.049 27.995 -4.072 27.888 -4.072 c
+27.69 -4.072 27.538 -4.006 27.432 -3.865 c
+27.333 -3.73 27.285 -3.535 27.285 -3.278 c
+27.285 -1.425 l
+26.83 -1.425 l
+26.83 -1.029 l
+27.285 -1.029 l
+27.285 -0.309 l
+h
+28.538 -2.381 m
+28.538 -1.951 28.641 -1.61 28.847 -1.352 c
+29.06 -1.099 29.34 -0.97 29.685 -0.97 c
+30.027 -0.97 30.302 -1.099 30.508 -1.352 c
+30.721 -1.598 30.835 -1.932 30.846 -2.352 c
+30.846 -2.66 l
+30.846 -3.094 30.736 -3.436 30.522 -3.69 c
+30.317 -3.946 30.038 -4.072 29.685 -4.072 c
+29.34 -4.072 29.067 -3.95 28.862 -3.704 c
+28.656 -3.461 28.546 -3.127 28.538 -2.705 c
+h
+29.024 -2.66 m
+29.024 -2.976 29.082 -3.219 29.2 -3.395 c
+29.325 -3.564 29.487 -3.645 29.685 -3.645 c
+30.115 -3.645 30.339 -3.337 30.36 -2.719 c
+30.36 -2.381 l
+30.36 -2.08 30.295 -1.837 30.169 -1.66 c
+30.052 -1.484 29.89 -1.396 29.685 -1.396 c
+29.487 -1.396 29.325 -1.484 29.2 -1.66 c
+29.082 -1.837 29.024 -2.08 29.024 -2.381 c
+h
+34.003 -3.748 m
+33.845 -3.965 33.61 -4.072 33.297 -4.072 c
+33.032 -4.072 32.827 -3.979 32.679 -3.792 c
+32.54 -3.609 32.474 -3.333 32.474 -2.969 c
+32.474 -1.029 l
+32.959 -1.029 l
+32.959 -2.94 l
+32.959 -3.403 33.099 -3.63 33.385 -3.63 c
+33.679 -3.63 33.878 -3.499 33.988 -3.233 c
+33.988 -1.029 l
+34.488 -1.029 l
+34.488 -4.012 l
+34.017 -4.012 l
+h
+37.185 -2.66 m
+37.185 -3.131 37.101 -3.484 36.935 -3.719 c
+36.766 -3.954 36.527 -4.072 36.215 -4.072 c
+35.91 -4.072 35.678 -3.961 35.524 -3.734 c
+35.524 -5.159 l
+35.039 -5.159 l
+35.039 -1.029 l
+35.48 -1.029 l
+35.509 -1.367 l
+35.663 -1.103 35.895 -0.97 36.2 -0.97 c
+36.531 -0.97 36.777 -1.087 36.935 -1.323 c
+37.101 -1.55 37.185 -1.889 37.185 -2.337 c
+h
+36.7 -2.381 m
+36.7 -2.05 36.645 -1.804 36.538 -1.646 c
+36.439 -1.492 36.278 -1.411 36.053 -1.411 c
+35.818 -1.411 35.642 -1.529 35.524 -1.764 c
+35.524 -3.307 l
+35.642 -3.535 35.821 -3.645 36.068 -3.645 c
+36.281 -3.645 36.439 -3.568 36.538 -3.41 c
+36.645 -3.256 36.7 -3.013 36.7 -2.69 c
+h
+37.541 -2.381 m
+37.541 -1.922 37.622 -1.573 37.791 -1.338 c
+37.967 -1.095 38.218 -0.97 38.541 -0.97 c
+38.824 -0.97 39.044 -1.087 39.203 -1.323 c
+39.203 0.221 l
+39.688 0.221 l
+39.688 -4.012 l
+39.247 -4.012 l
+39.218 -3.69 l
+39.06 -3.946 38.835 -4.072 38.541 -4.072 c
+38.225 -4.072 37.982 -3.954 37.806 -3.719 c
+37.63 -3.476 37.541 -3.131 37.541 -2.69 c
+h
+38.027 -2.66 m
+38.027 -2.994 38.075 -3.241 38.173 -3.395 c
+38.269 -3.553 38.431 -3.63 38.659 -3.63 c
+38.901 -3.63 39.085 -3.513 39.203 -3.278 c
+39.203 -1.764 l
+39.074 -1.529 38.894 -1.411 38.659 -1.411 c
+38.431 -1.411 38.269 -1.492 38.173 -1.646 c
+38.075 -1.804 38.027 -2.043 38.027 -2.366 c
+h
+41.786 -4.012 m
+41.757 -3.946 41.734 -3.836 41.727 -3.69 c
+41.551 -3.946 41.33 -4.072 41.065 -4.072 c
+40.789 -4.072 40.573 -3.998 40.419 -3.851 c
+40.272 -3.696 40.198 -3.48 40.198 -3.204 c
+40.198 -2.903 40.301 -2.66 40.507 -2.484 c
+40.712 -2.3 40.996 -2.205 41.36 -2.205 c
+41.712 -2.205 l
+41.712 -1.881 l
+41.712 -1.705 41.672 -1.583 41.595 -1.514 c
+41.514 -1.436 41.396 -1.396 41.242 -1.396 c
+41.095 -1.396 40.97 -1.44 40.874 -1.529 c
+40.787 -1.617 40.742 -1.727 40.742 -1.852 c
+40.257 -1.852 l
+40.257 -1.705 40.301 -1.565 40.39 -1.425 c
+40.477 -1.278 40.595 -1.168 40.742 -1.087 c
+40.897 -1.01 41.069 -0.97 41.257 -0.97 c
+41.569 -0.97 41.805 -1.051 41.963 -1.205 c
+42.117 -1.352 42.198 -1.573 42.198 -1.866 c
+42.198 -3.366 l
+42.204 -3.601 42.241 -3.803 42.3 -3.969 c
+42.3 -4.012 l
+h
+41.139 -3.63 m
+41.257 -3.63 41.367 -3.597 41.477 -3.528 c
+41.583 -3.461 41.661 -3.377 41.712 -3.278 c
+41.712 -2.572 l
+41.448 -2.572 l
+41.213 -2.572 41.025 -2.624 40.889 -2.719 c
+40.76 -2.818 40.698 -2.961 40.698 -3.146 c
+40.698 -3.314 40.727 -3.436 40.787 -3.513 c
+40.853 -3.594 40.97 -3.63 41.139 -3.63 c
+43.41 -0.309 m
+43.41 -1.029 l
+43.866 -1.029 l
+43.866 -1.425 l
+43.41 -1.425 l
+43.41 -3.278 l
+43.41 -3.395 43.428 -3.484 43.469 -3.542 c
+43.505 -3.601 43.575 -3.63 43.675 -3.63 c
+43.733 -3.63 43.796 -3.623 43.866 -3.601 c
+43.866 -4.012 l
+43.748 -4.049 43.634 -4.072 43.528 -4.072 c
+43.33 -4.072 43.178 -4.006 43.072 -3.865 c
+42.973 -3.73 42.925 -3.535 42.925 -3.278 c
+42.925 -1.425 l
+42.47 -1.425 l
+42.47 -1.029 l
+42.925 -1.029 l
+42.925 -0.309 l
+h
+45.391 -4.072 m
+45.016 -4.072 44.733 -3.965 44.538 -3.748 c
+44.34 -3.524 44.244 -3.197 44.244 -2.763 c
+44.244 -2.396 l
+44.244 -1.955 44.336 -1.61 44.523 -1.352 c
+44.718 -1.099 44.994 -0.97 45.346 -0.97 c
+45.688 -0.97 45.942 -1.084 46.111 -1.308 c
+46.287 -1.536 46.379 -1.881 46.391 -2.352 c
+46.391 -2.66 l
+44.729 -2.66 l
+44.729 -2.734 l
+44.729 -3.057 44.788 -3.293 44.905 -3.439 c
+45.023 -3.579 45.192 -3.645 45.42 -3.645 c
+45.568 -3.645 45.692 -3.623 45.803 -3.572 c
+45.909 -3.513 46.012 -3.424 46.111 -3.307 c
+46.361 -3.616 l
+46.156 -3.921 45.832 -4.072 45.391 -4.072 c
+45.346 -1.396 m
+45.14 -1.396 44.986 -1.466 44.891 -1.602 c
+44.791 -1.741 44.737 -1.955 44.729 -2.248 c
+45.905 -2.248 l
+45.905 -2.175 l
+45.883 -1.903 45.832 -1.705 45.743 -1.587 c
+45.655 -1.462 45.523 -1.396 45.346 -1.396 c
+48.635 -0.309 m
+48.635 -1.029 l
+49.092 -1.029 l
+49.092 -1.425 l
+48.635 -1.425 l
+48.635 -3.278 l
+48.635 -3.395 48.654 -3.484 48.695 -3.542 c
+48.731 -3.601 48.801 -3.63 48.9 -3.63 c
+48.959 -3.63 49.021 -3.623 49.092 -3.601 c
+49.092 -4.012 l
+48.974 -4.049 48.86 -4.072 48.753 -4.072 c
+48.554 -4.072 48.404 -4.006 48.298 -3.865 c
+48.198 -3.73 48.151 -3.535 48.151 -3.278 c
+48.151 -1.425 l
+47.695 -1.425 l
+47.695 -1.029 l
+48.151 -1.029 l
+48.151 -0.309 l
+h
+50.043 -1.338 m
+50.227 -1.095 50.462 -0.97 50.748 -0.97 c
+51.278 -0.97 51.546 -1.323 51.557 -2.028 c
+51.557 -4.012 l
+51.072 -4.012 l
+51.072 -2.057 l
+51.072 -1.822 51.031 -1.657 50.954 -1.558 c
+50.873 -1.462 50.756 -1.411 50.601 -1.411 c
+50.484 -1.411 50.374 -1.452 50.278 -1.529 c
+50.179 -1.61 50.102 -1.716 50.043 -1.852 c
+50.043 -4.012 l
+49.558 -4.012 l
+49.558 0.221 l
+50.043 0.221 l
+h
+53.159 -4.072 m
+52.784 -4.072 52.502 -3.965 52.307 -3.748 c
+52.109 -3.524 52.013 -3.197 52.013 -2.763 c
+52.013 -2.396 l
+52.013 -1.955 52.105 -1.61 52.292 -1.352 c
+52.487 -1.099 52.762 -0.97 53.115 -0.97 c
+53.457 -0.97 53.711 -1.084 53.879 -1.308 c
+54.056 -1.536 54.148 -1.881 54.158 -2.352 c
+54.158 -2.66 l
+52.498 -2.66 l
+52.498 -2.734 l
+52.498 -3.057 52.556 -3.293 52.674 -3.439 c
+52.791 -3.579 52.961 -3.645 53.188 -3.645 c
+53.335 -3.645 53.46 -3.623 53.57 -3.572 c
+53.678 -3.513 53.78 -3.424 53.879 -3.307 c
+54.129 -3.616 l
+53.923 -3.921 53.601 -4.072 53.159 -4.072 c
+53.115 -1.396 m
+52.909 -1.396 52.755 -1.466 52.66 -1.602 c
+52.56 -1.741 52.505 -1.955 52.498 -2.248 c
+53.674 -2.248 l
+53.674 -2.175 l
+53.651 -1.903 53.601 -1.705 53.512 -1.587 c
+53.424 -1.462 53.291 -1.396 53.115 -1.396 c
+55.026 -1.029 m
+55.041 -1.308 l
+55.217 -1.084 55.456 -0.97 55.76 -0.97 c
+56.092 -0.97 56.323 -1.117 56.452 -1.411 c
+56.635 -1.117 56.896 -0.97 57.231 -0.97 c
+57.79 -0.97 58.072 -1.315 58.083 -1.999 c
+58.083 -4.012 l
+57.599 -4.012 l
+57.599 -2.043 l
+57.599 -1.83 57.558 -1.672 57.481 -1.573 c
+57.4 -1.466 57.268 -1.411 57.084 -1.411 c
+56.936 -1.411 56.819 -1.469 56.731 -1.587 c
+56.643 -1.697 56.587 -1.837 56.57 -2.013 c
+56.57 -4.012 l
+56.084 -4.012 l
+56.084 -2.028 l
+56.074 -1.617 55.901 -1.411 55.569 -1.411 c
+55.324 -1.411 55.151 -1.536 55.055 -1.778 c
+55.055 -4.012 l
+54.57 -4.012 l
+54.57 -1.029 l
+h
+f
+Q
+ endstream endobj 54 0 obj <</BBox[720.868 437.21 745.375 431.389]/Group 90 0 R/Length 5115/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 721.2498 432.6827 cm
+0 0 m
+0 2.587 l
+-0.382 2.587 l
+-0.382 2.983 l
+0 2.983 l
+0 3.322 l
+0.008 3.623 0.088 3.858 0.235 4.027 c
+0.382 4.203 0.592 4.292 0.867 4.292 c
+0.963 4.292 1.062 4.277 1.162 4.247 c
+1.132 3.836 l
+1.062 3.844 0.989 3.85 0.912 3.85 c
+0.636 3.85 0.5 3.652 0.5 3.262 c
+0.5 2.983 l
+1 2.983 l
+1 2.587 l
+0.5 2.587 l
+0.5 0 l
+h
+2.697 2.528 m
+2.628 2.535 2.554 2.543 2.477 2.543 c
+2.22 2.543 2.043 2.403 1.948 2.131 c
+1.948 0 l
+1.463 0 l
+1.463 2.983 l
+1.933 2.983 l
+1.948 2.674 l
+2.072 2.917 2.257 3.042 2.492 3.042 c
+2.569 3.042 2.631 3.027 2.683 2.998 c
+h
+4.075 -0.059 m
+3.701 -0.059 3.418 0.047 3.223 0.264 c
+3.024 0.488 2.929 0.816 2.929 1.249 c
+2.929 1.616 l
+2.929 2.057 3.021 2.403 3.208 2.66 c
+3.403 2.913 3.678 3.042 4.031 3.042 c
+4.373 3.042 4.627 2.929 4.796 2.705 c
+4.972 2.476 5.064 2.131 5.075 1.66 c
+5.075 1.352 l
+3.414 1.352 l
+3.414 1.278 l
+3.414 0.955 3.473 0.72 3.591 0.573 c
+3.708 0.434 3.877 0.367 4.104 0.367 c
+4.252 0.367 4.377 0.389 4.487 0.44 c
+4.594 0.5 4.696 0.588 4.796 0.706 c
+5.045 0.396 l
+4.84 0.091 4.517 -0.059 4.075 -0.059 c
+4.031 2.616 m
+3.826 2.616 3.672 2.547 3.576 2.41 c
+3.476 2.271 3.422 2.057 3.414 1.764 c
+4.59 1.764 l
+4.59 1.837 l
+4.568 2.109 4.517 2.308 4.428 2.425 c
+4.34 2.55 4.208 2.616 4.031 2.616 c
+5.384 1.631 m
+5.384 2.09 5.465 2.439 5.633 2.674 c
+5.799 2.917 6.049 3.042 6.383 3.042 c
+6.677 3.042 6.901 2.925 7.059 2.69 c
+7.088 2.983 l
+7.53 2.983 l
+7.53 -1.147 l
+7.044 -1.147 l
+7.044 0.264 l
+6.887 0.047 6.666 -0.059 6.383 -0.059 c
+6.06 -0.059 5.81 0.058 5.633 0.294 c
+5.465 0.529 5.384 0.87 5.384 1.323 c
+h
+5.868 1.352 m
+5.868 1.018 5.916 0.768 6.016 0.602 c
+6.122 0.444 6.288 0.367 6.516 0.367 c
+6.74 0.367 6.916 0.474 7.044 0.69 c
+7.044 2.293 l
+6.905 2.506 6.729 2.616 6.516 2.616 c
+6.288 2.616 6.122 2.532 6.016 2.366 c
+5.916 2.208 5.868 1.969 5.868 1.646 c
+h
+9.635 0.264 m
+9.477 0.047 9.242 -0.059 8.93 -0.059 c
+8.665 -0.059 8.459 0.033 8.312 0.22 c
+8.173 0.404 8.106 0.679 8.106 1.043 c
+8.106 2.983 l
+8.592 2.983 l
+8.592 1.072 l
+8.592 0.61 8.731 0.382 9.018 0.382 c
+9.312 0.382 9.511 0.514 9.621 0.779 c
+9.621 2.983 l
+10.12 2.983 l
+10.12 0 l
+9.65 0 l
+h
+11.73 -0.059 m
+11.356 -0.059 11.073 0.047 10.878 0.264 c
+10.679 0.488 10.583 0.816 10.583 1.249 c
+10.583 1.616 l
+10.583 2.057 10.676 2.403 10.863 2.66 c
+11.057 2.913 11.333 3.042 11.686 3.042 c
+12.027 3.042 12.281 2.929 12.451 2.705 c
+12.627 2.476 12.719 2.131 12.729 1.66 c
+12.729 1.352 l
+11.069 1.352 l
+11.069 1.278 l
+11.069 0.955 11.127 0.72 11.245 0.573 c
+11.362 0.434 11.532 0.367 11.759 0.367 c
+11.906 0.367 12.031 0.389 12.141 0.44 c
+12.248 0.5 12.351 0.588 12.451 0.706 c
+12.7 0.396 l
+12.494 0.091 12.171 -0.059 11.73 -0.059 c
+11.686 2.616 m
+11.48 2.616 11.326 2.547 11.231 2.41 c
+11.131 2.271 11.076 2.057 11.069 1.764 c
+12.245 1.764 l
+12.245 1.837 l
+12.222 2.109 12.171 2.308 12.083 2.425 c
+11.994 2.55 11.863 2.616 11.686 2.616 c
+13.596 2.983 m
+13.612 2.66 l
+13.795 2.913 14.038 3.042 14.332 3.042 c
+14.861 3.042 15.129 2.69 15.14 1.984 c
+15.14 0 l
+14.655 0 l
+14.655 1.955 l
+14.655 2.19 14.614 2.356 14.537 2.454 c
+14.456 2.55 14.339 2.601 14.185 2.601 c
+14.067 2.601 13.957 2.561 13.862 2.484 c
+13.762 2.403 13.685 2.296 13.627 2.16 c
+13.627 0 l
+13.141 0 l
+13.141 2.983 l
+h
+16.324 3.704 m
+16.324 2.983 l
+16.779 2.983 l
+16.779 2.587 l
+16.324 2.587 l
+16.324 0.735 l
+16.324 0.617 16.342 0.529 16.382 0.47 c
+16.419 0.411 16.489 0.382 16.588 0.382 c
+16.646 0.382 16.709 0.389 16.779 0.411 c
+16.779 0 l
+16.661 -0.037 16.548 -0.059 16.441 -0.059 c
+16.243 -0.059 16.092 0.007 15.985 0.147 c
+15.886 0.282 15.838 0.477 15.838 0.735 c
+15.838 2.587 l
+15.383 2.587 l
+15.383 2.983 l
+15.838 2.983 l
+15.838 3.704 l
+h
+17.775 0 -0.501 4.233 re
+19.26 0.808 m
+19.804 2.983 l
+20.318 2.983 l
+19.348 -0.427 l
+19.278 -0.68 19.175 -0.871 19.039 -1 c
+18.899 -1.135 18.749 -1.206 18.583 -1.206 c
+18.514 -1.206 18.429 -1.191 18.333 -1.162 c
+18.333 -0.75 l
+18.437 -0.765 l
+18.572 -0.765 18.679 -0.728 18.76 -0.662 c
+18.848 -0.592 18.915 -0.474 18.965 -0.31 c
+19.054 0.029 l
+18.186 2.983 l
+18.716 2.983 l
+h
+21.876 0.264 m
+21.876 0.353 21.899 0.426 21.949 0.484 c
+22.009 0.544 22.086 0.573 22.185 0.573 c
+22.291 0.573 22.369 0.544 22.42 0.484 c
+22.479 0.426 22.508 0.353 22.508 0.264 c
+22.508 0.183 22.479 0.118 22.42 0.058 c
+22.369 0 22.291 -0.03 22.185 -0.03 c
+22.086 -0.03 22.009 0 21.949 0.058 c
+21.899 0.118 21.876 0.183 21.876 0.264 c
+21.876 3.087 m
+21.876 3.175 21.899 3.248 21.949 3.307 c
+22.009 3.366 22.086 3.395 22.185 3.395 c
+22.291 3.395 22.369 3.366 22.42 3.307 c
+22.479 3.248 22.508 3.175 22.508 3.087 c
+22.508 3.006 22.479 2.94 22.42 2.881 c
+22.369 2.822 22.291 2.792 22.185 2.792 c
+22.086 2.792 22.009 2.822 21.949 2.881 c
+21.899 2.94 21.876 3.006 21.876 3.087 c
+24.125 1.602 m
+24.125 0.779 23.941 0.051 23.582 -0.574 c
+23.376 -0.915 23.169 -1.154 22.963 -1.294 c
+22.861 -0.985 l
+23.096 -0.757 23.28 -0.42 23.42 0.029 c
+23.567 0.488 23.64 0.988 23.64 1.529 c
+23.64 1.631 l
+23.64 2.326 23.526 2.954 23.302 3.513 c
+23.173 3.806 23.026 4.041 22.861 4.218 c
+22.963 4.527 l
+23.158 4.388 23.353 4.167 23.551 3.866 c
+23.934 3.218 24.125 2.462 24.125 1.602 c
+f
+Q
+ endstream endobj 55 0 obj <</BBox[320.12 122.007 531.709 116.418]/Group 91 0 R/Length 125/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 92 0 R>>/ExtGState<</GS0 12 0 R>>/Shading<</Sh0 93 0 R>>>>/Subtype/Form>>stream
+q
+320.12 122.007 211.589 -5.589 re
+W n
+q
+0 g
+/GS0 gs
+212.3990173 0 0 139.9599457 319.716156 119.2128296 cm
+BX /Sh0 sh EX Q
+Q
+ endstream endobj 56 0 obj <</BBox[80.2243 128.522 302.099 76.7775]/Group 94 0 R/Length 1745/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 217.0464 128.5219 cm
+0 0 m
+-12.446 0 -23.272 -2.697 -28.986 -6.673 c
+-31.187 -6.214 -33.551 -5.953 -35.983 -5.953 c
+-41.337 -5.953 -46.243 -7.162 -50.141 -9.161 c
+-52.585 -7.919 -55.856 -7.136 -59.461 -7.136 c
+-64.727 -7.136 -69.287 -8.764 -71.573 -11.135 c
+-74.947 -10.329 -79.352 -9.834 -84.196 -9.834 c
+-91.013 -9.834 -96.981 -10.815 -100.372 -12.274 c
+-103.823 -11.439 -107.74 -10.95 -111.904 -10.95 c
+-125.666 -10.95 -136.822 -16.11 -136.822 -22.478 c
+-136.822 -27.097 -130.935 -31.077 -122.458 -32.915 c
+-124.924 -33.932 -126.408 -35.215 -126.408 -36.612 c
+-126.408 -39.901 -118.25 -42.565 -108.185 -42.565 c
+-103.875 -42.565 -99.913 -42.079 -96.793 -41.26 c
+-95.863 -41.863 -94.805 -42.189 -93.681 -42.189 c
+-92.133 -42.189 -90.722 -41.547 -89.587 -40.47 c
+-86.603 -41.984 -82.384 -42.935 -77.688 -42.935 c
+-74.98 -42.935 -72.426 -42.605 -70.176 -42.05 c
+-69.544 -44.417 -63.745 -46.283 -56.672 -46.283 c
+-50.112 -46.283 -44.641 -44.681 -43.377 -42.565 c
+-40.415 -43.832 -36.454 -44.608 -32.103 -44.608 c
+-31.268 -44.608 -30.463 -44.571 -29.662 -44.516 c
+-24.992 -48.768 -14.14 -51.744 -1.488 -51.744 c
+11.198 -51.744 22.082 -48.76 26.73 -44.494 c
+27.987 -44.575 29.307 -44.608 30.637 -44.608 c
+41.639 -44.608 50.558 -41.69 50.558 -38.077 c
+50.558 -37.512 50.315 -36.971 49.907 -36.45 c
+53.178 -36.138 55.463 -35.413 55.463 -34.568 c
+55.463 -34.322 55.258 -34.087 54.905 -33.87 c
+55.633 -33.892 56.349 -33.914 57.092 -33.914 c
+72.525 -33.914 85.052 -28.696 85.052 -22.295 c
+85.052 -15.889 72.525 -10.715 57.092 -10.715 c
+53.362 -10.715 49.812 -11.027 46.56 -11.575 c
+43.962 -10.697 39.783 -10.113 35.076 -10.113 c
+34.183 -10.113 33.323 -10.142 32.474 -10.183 c
+29.101 -4.358 15.842 0 0 0 c
+f
+Q
+ endstream endobj 57 0 obj <</BBox[549.22 269.428 582.876 265.077]/Group 95 0 R/Length 6396/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 551.4096 266.1785 cm
+0 0 m
+-1.352 0 l
+-1.675 -1.043 l
+-2.19 -1.043 l
+-0.896 2.97 l
+-0.47 2.97 l
+0.838 -1.043 l
+0.309 -1.043 l
+h
+-1.22 0.441 m
+-0.132 0.441 l
+-0.676 2.264 l
+h
+3.05 0.324 m
+2.786 0.03 l
+2.786 -1.043 l
+2.3 -1.043 l
+2.3 3.19 l
+2.786 3.19 l
+2.786 0.662 l
+3.711 1.941 l
+4.299 1.941 l
+3.358 0.691 l
+4.432 -1.043 l
+3.859 -1.043 l
+h
+5.203 -1.043 -0.5 2.984 re
+5.233 2.735 m
+5.233 2.646 5.207 2.573 5.159 2.514 c
+5.12 2.463 5.049 2.44 4.954 2.44 c
+4.866 2.44 4.796 2.463 4.748 2.514 c
+4.707 2.573 4.689 2.639 4.689 2.72 c
+4.689 2.808 4.707 2.881 4.748 2.94 c
+4.796 2.999 4.866 3.028 4.954 3.028 c
+5.049 3.028 5.12 2.999 5.159 2.94 c
+5.207 2.881 5.233 2.812 5.233 2.735 c
+6.335 1.941 m
+6.35 1.617 l
+6.533 1.871 6.776 1.999 7.071 1.999 c
+7.599 1.999 7.867 1.646 7.879 0.941 c
+7.879 -1.043 l
+7.394 -1.043 l
+7.394 0.912 l
+7.394 1.147 7.354 1.312 7.276 1.411 c
+7.196 1.507 7.078 1.559 6.924 1.559 c
+6.805 1.559 6.695 1.518 6.6 1.441 c
+6.5 1.36 6.423 1.253 6.365 1.118 c
+6.365 -1.043 l
+5.88 -1.043 l
+5.88 1.941 l
+h
+8.327 0.588 m
+8.327 1.048 8.408 1.397 8.577 1.632 c
+8.754 1.875 9.003 1.999 9.327 1.999 c
+9.61 1.999 9.83 1.881 9.988 1.646 c
+9.988 3.19 l
+10.473 3.19 l
+10.473 -1.043 l
+10.032 -1.043 l
+10.003 -0.72 l
+9.845 -0.977 9.621 -1.102 9.327 -1.102 c
+9.01 -1.102 8.768 -0.984 8.592 -0.749 c
+8.415 -0.507 8.327 -0.161 8.327 0.279 c
+h
+8.812 0.309 m
+8.812 -0.025 8.86 -0.272 8.96 -0.426 c
+9.055 -0.584 9.216 -0.661 9.444 -0.661 c
+9.687 -0.661 9.87 -0.544 9.988 -0.309 c
+9.988 1.206 l
+9.86 1.441 9.679 1.559 9.444 1.559 c
+9.216 1.559 9.055 1.478 8.96 1.324 c
+8.86 1.166 8.812 0.927 8.812 0.603 c
+h
+12.145 0.588 m
+12.145 1.018 12.248 1.36 12.453 1.617 c
+12.667 1.871 12.946 1.999 13.292 1.999 c
+13.633 1.999 13.909 1.871 14.115 1.617 c
+14.328 1.371 14.442 1.037 14.453 0.618 c
+14.453 0.309 l
+14.453 -0.124 14.343 -0.467 14.13 -0.72 c
+13.924 -0.977 13.644 -1.102 13.292 -1.102 c
+12.946 -1.102 12.675 -0.981 12.469 -0.735 c
+12.263 -0.492 12.152 -0.158 12.145 0.265 c
+h
+12.63 0.309 m
+12.63 -0.007 12.689 -0.249 12.806 -0.426 c
+12.931 -0.595 13.093 -0.675 13.292 -0.675 c
+13.722 -0.675 13.945 -0.367 13.968 0.25 c
+13.968 0.588 l
+13.968 0.89 13.901 1.133 13.777 1.309 c
+13.66 1.485 13.498 1.573 13.292 1.573 c
+13.093 1.573 12.931 1.485 12.806 1.309 c
+12.689 1.133 12.63 0.89 12.63 0.588 c
+h
+15.1 -1.043 m
+15.1 1.544 l
+14.718 1.544 l
+14.718 1.941 l
+15.1 1.941 l
+15.1 2.278 l
+15.107 2.58 15.188 2.816 15.335 2.984 c
+15.482 3.161 15.692 3.249 15.967 3.249 c
+16.062 3.249 16.162 3.234 16.261 3.205 c
+16.232 2.793 l
+16.162 2.801 16.089 2.808 16.011 2.808 c
+15.736 2.808 15.599 2.61 15.599 2.22 c
+15.599 1.941 l
+16.099 1.941 l
+16.099 1.544 l
+15.599 1.544 l
+15.599 -1.043 l
+h
+19.293 -1.043 m
+19.264 -0.977 19.241 -0.866 19.233 -0.72 c
+19.057 -0.977 18.837 -1.102 18.572 -1.102 c
+18.296 -1.102 18.08 -1.028 17.926 -0.882 c
+17.779 -0.727 17.705 -0.511 17.705 -0.235 c
+17.705 0.067 17.808 0.309 18.014 0.485 c
+18.219 0.669 18.502 0.765 18.867 0.765 c
+19.219 0.765 l
+19.219 1.088 l
+19.219 1.264 19.179 1.386 19.102 1.455 c
+19.021 1.532 18.903 1.573 18.749 1.573 c
+18.602 1.573 18.477 1.529 18.381 1.441 c
+18.293 1.353 18.249 1.243 18.249 1.118 c
+17.764 1.118 l
+17.764 1.264 17.808 1.405 17.897 1.544 c
+17.984 1.69 18.102 1.801 18.249 1.881 c
+18.404 1.959 18.576 1.999 18.763 1.999 c
+19.075 1.999 19.311 1.918 19.469 1.764 c
+19.624 1.617 19.704 1.397 19.704 1.103 c
+19.704 -0.396 l
+19.711 -0.631 19.748 -0.833 19.807 -0.999 c
+19.807 -1.043 l
+h
+18.646 -0.661 m
+18.763 -0.661 18.874 -0.628 18.984 -0.558 c
+19.09 -0.492 19.168 -0.407 19.219 -0.309 c
+19.219 0.397 l
+18.955 0.397 l
+18.72 0.397 18.532 0.346 18.396 0.25 c
+18.267 0.151 18.205 0.008 18.205 -0.176 c
+18.205 -0.345 18.234 -0.467 18.293 -0.544 c
+18.359 -0.625 18.477 -0.661 18.646 -0.661 c
+22.931 -0.278 m
+22.931 -0.172 22.89 -0.084 22.813 -0.014 c
+22.732 0.063 22.581 0.151 22.358 0.25 c
+22.092 0.357 21.905 0.449 21.799 0.53 c
+21.689 0.607 21.612 0.695 21.564 0.794 c
+21.512 0.89 21.49 1.008 21.49 1.147 c
+21.49 1.389 21.579 1.592 21.755 1.75 c
+21.931 1.915 22.155 1.999 22.431 1.999 c
+22.725 1.999 22.96 1.912 23.137 1.735 c
+23.312 1.565 23.401 1.353 23.401 1.088 c
+22.916 1.088 l
+22.916 1.224 22.865 1.338 22.769 1.426 c
+22.681 1.522 22.566 1.573 22.431 1.573 c
+22.284 1.573 22.17 1.532 22.092 1.455 c
+22.012 1.386 21.976 1.287 21.976 1.162 c
+21.976 1.062 22.005 0.985 22.063 0.927 c
+22.122 0.867 22.262 0.786 22.489 0.691 c
+22.85 0.545 23.096 0.401 23.225 0.265 c
+23.36 0.136 23.43 -0.037 23.43 -0.249 c
+23.43 -0.507 23.335 -0.712 23.152 -0.866 c
+22.975 -1.024 22.74 -1.102 22.446 -1.102 c
+22.13 -1.102 21.876 -1.014 21.681 -0.837 c
+21.494 -0.654 21.402 -0.422 21.402 -0.147 c
+21.887 -0.147 l
+21.895 -0.316 21.946 -0.448 22.034 -0.544 c
+22.13 -0.631 22.269 -0.675 22.446 -0.675 c
+22.6 -0.675 22.718 -0.643 22.798 -0.573 c
+22.886 -0.507 22.931 -0.407 22.931 -0.278 c
+24.371 1.632 m
+24.555 1.875 24.79 1.999 25.076 1.999 c
+25.606 1.999 25.874 1.646 25.885 0.941 c
+25.885 -1.043 l
+25.4 -1.043 l
+25.4 0.912 l
+25.4 1.147 25.359 1.312 25.282 1.411 c
+25.201 1.507 25.084 1.559 24.93 1.559 c
+24.812 1.559 24.702 1.518 24.606 1.441 c
+24.507 1.36 24.43 1.253 24.371 1.118 c
+24.371 -1.043 l
+23.886 -1.043 l
+23.886 3.19 l
+24.371 3.19 l
+h
+27.487 -1.102 m
+27.112 -1.102 26.83 -0.995 26.635 -0.779 c
+26.437 -0.554 26.341 -0.228 26.341 0.206 c
+26.341 0.574 l
+26.341 1.015 26.433 1.36 26.62 1.617 c
+26.815 1.871 27.091 1.999 27.443 1.999 c
+27.785 1.999 28.039 1.885 28.207 1.661 c
+28.384 1.434 28.476 1.088 28.487 0.618 c
+28.487 0.309 l
+26.826 0.309 l
+26.826 0.235 l
+26.826 -0.087 26.884 -0.323 27.002 -0.47 c
+27.12 -0.61 27.289 -0.675 27.517 -0.675 c
+27.663 -0.675 27.789 -0.654 27.899 -0.602 c
+28.006 -0.544 28.108 -0.455 28.207 -0.338 c
+28.457 -0.646 l
+28.251 -0.951 27.929 -1.102 27.487 -1.102 c
+27.443 1.573 m
+27.237 1.573 27.083 1.503 26.988 1.368 c
+26.888 1.228 26.834 1.015 26.826 0.721 c
+28.002 0.721 l
+28.002 0.794 l
+27.979 1.066 27.929 1.264 27.84 1.382 c
+27.752 1.507 27.62 1.573 27.443 1.573 c
+29.427 -1.043 -0.5 4.233 re
+30.306 -1.043 m
+30.306 1.544 l
+29.924 1.544 l
+29.924 1.941 l
+30.306 1.941 l
+30.306 2.278 l
+30.313 2.58 30.394 2.816 30.541 2.984 c
+30.688 3.161 30.898 3.249 31.173 3.249 c
+31.268 3.249 31.368 3.234 31.467 3.205 c
+31.438 2.793 l
+31.368 2.801 31.295 2.808 31.218 2.808 c
+30.942 2.808 30.805 2.61 30.805 2.22 c
+30.805 1.941 l
+31.305 1.941 l
+31.305 1.544 l
+30.805 1.544 l
+30.805 -1.043 l
+h
+f
+Q
+ endstream endobj 58 0 obj <</BBox[553.089 262.843 578.922 258.492]/Group 96 0 R/Length 6110/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 553.4717 258.55 cm
+0 0 m
+0 2.587 l
+-0.383 2.587 l
+-0.383 2.984 l
+0 2.984 l
+0 3.322 l
+0.007 3.624 0.087 3.859 0.235 4.027 c
+0.382 4.204 0.591 4.293 0.866 4.293 c
+0.962 4.293 1.061 4.278 1.161 4.248 c
+1.132 3.836 l
+1.061 3.844 0.988 3.851 0.911 3.851 c
+0.635 3.851 0.5 3.653 0.5 3.263 c
+0.5 2.984 l
+0.999 2.984 l
+0.999 2.587 l
+0.5 2.587 l
+0.5 0 l
+h
+1.359 1.631 m
+1.359 2.061 1.462 2.404 1.668 2.66 c
+1.881 2.914 2.16 3.042 2.506 3.042 c
+2.848 3.042 3.123 2.914 3.329 2.66 c
+3.542 2.414 3.656 2.08 3.667 1.661 c
+3.667 1.353 l
+3.667 0.919 3.557 0.577 3.343 0.324 c
+3.137 0.066 2.859 -0.058 2.506 -0.058 c
+2.16 -0.058 1.888 0.062 1.683 0.309 c
+1.477 0.551 1.367 0.885 1.359 1.309 c
+h
+1.844 1.353 m
+1.844 1.037 1.903 0.794 2.021 0.617 c
+2.146 0.449 2.308 0.368 2.506 0.368 c
+2.936 0.368 3.16 0.676 3.182 1.294 c
+3.182 1.631 l
+3.182 1.933 3.116 2.176 2.991 2.352 c
+2.873 2.529 2.711 2.616 2.506 2.616 c
+2.308 2.616 2.146 2.529 2.021 2.352 c
+1.903 2.176 1.844 1.933 1.844 1.631 c
+h
+5.339 2.529 m
+5.269 2.535 5.196 2.543 5.119 2.543 c
+4.862 2.543 4.685 2.404 4.589 2.132 c
+4.589 0 l
+4.104 0 l
+4.104 2.984 l
+4.575 2.984 l
+4.589 2.675 l
+4.714 2.918 4.898 3.042 5.134 3.042 c
+5.211 3.042 5.273 3.028 5.325 2.999 c
+h
+7.548 3.705 m
+7.548 2.984 l
+8.003 2.984 l
+8.003 2.587 l
+7.548 2.587 l
+7.548 0.735 l
+7.548 0.617 7.565 0.53 7.606 0.47 c
+7.643 0.412 7.713 0.382 7.812 0.382 c
+7.871 0.382 7.933 0.389 8.003 0.412 c
+8.003 0 l
+7.885 -0.037 7.771 -0.058 7.665 -0.058 c
+7.467 -0.058 7.316 0.008 7.21 0.147 c
+7.11 0.283 7.062 0.478 7.062 0.735 c
+7.062 2.587 l
+6.607 2.587 l
+6.607 2.984 l
+7.062 2.984 l
+7.062 3.705 l
+h
+8.955 2.675 m
+9.138 2.918 9.374 3.042 9.66 3.042 c
+10.19 3.042 10.458 2.69 10.469 1.984 c
+10.469 0 l
+9.984 0 l
+9.984 1.955 l
+9.984 2.19 9.944 2.356 9.866 2.454 c
+9.786 2.55 9.668 2.602 9.514 2.602 c
+9.396 2.602 9.286 2.562 9.19 2.484 c
+9.091 2.404 9.014 2.296 8.955 2.161 c
+8.955 0 l
+8.469 0 l
+8.469 4.233 l
+8.955 4.233 l
+h
+12.068 -0.058 m
+11.692 -0.058 11.409 0.048 11.215 0.264 c
+11.016 0.489 10.921 0.816 10.921 1.249 c
+10.921 1.617 l
+10.921 2.058 11.013 2.404 11.2 2.66 c
+11.395 2.914 11.671 3.042 12.024 3.042 c
+12.365 3.042 12.619 2.929 12.787 2.705 c
+12.964 2.477 13.056 2.132 13.067 1.661 c
+13.067 1.353 l
+11.406 1.353 l
+11.406 1.278 l
+11.406 0.956 11.465 0.721 11.582 0.573 c
+11.7 0.434 11.869 0.368 12.097 0.368 c
+12.244 0.368 12.369 0.389 12.479 0.441 c
+12.585 0.5 12.689 0.588 12.787 0.706 c
+13.038 0.397 l
+12.832 0.092 12.508 -0.058 12.068 -0.058 c
+12.024 2.616 m
+11.818 2.616 11.663 2.547 11.567 2.411 c
+11.469 2.271 11.413 2.058 11.406 1.764 c
+12.582 1.764 l
+12.582 1.837 l
+12.56 2.109 12.508 2.308 12.421 2.425 c
+12.332 2.55 12.199 2.616 12.024 2.616 c
+15.139 2.984 m
+15.154 2.705 l
+15.33 2.929 15.569 3.042 15.875 3.042 c
+16.205 3.042 16.437 2.896 16.565 2.602 c
+16.749 2.896 17.01 3.042 17.344 3.042 c
+17.903 3.042 18.186 2.697 18.197 2.014 c
+18.197 0 l
+17.712 0 l
+17.712 1.97 l
+17.712 2.183 17.672 2.341 17.595 2.44 c
+17.514 2.547 17.381 2.602 17.198 2.602 c
+17.051 2.602 16.933 2.543 16.845 2.425 c
+16.756 2.315 16.701 2.176 16.683 1.999 c
+16.683 0 l
+16.198 0 l
+16.198 1.984 l
+16.187 2.396 16.014 2.602 15.683 2.602 c
+15.438 2.602 15.264 2.477 15.169 2.234 c
+15.169 0 l
+14.684 0 l
+14.684 2.984 l
+h
+19.704 -0.058 m
+19.328 -0.058 19.046 0.048 18.852 0.264 c
+18.653 0.489 18.557 0.816 18.557 1.249 c
+18.557 1.617 l
+18.557 2.058 18.649 2.404 18.836 2.66 c
+19.031 2.914 19.307 3.042 19.66 3.042 c
+20.001 3.042 20.255 2.929 20.423 2.705 c
+20.6 2.477 20.693 2.132 20.703 1.661 c
+20.703 1.353 l
+19.042 1.353 l
+19.042 1.278 l
+19.042 0.956 19.101 0.721 19.218 0.573 c
+19.336 0.434 19.505 0.368 19.733 0.368 c
+19.88 0.368 20.005 0.389 20.115 0.441 c
+20.222 0.5 20.325 0.588 20.423 0.706 c
+20.674 0.397 l
+20.468 0.092 20.145 -0.058 19.704 -0.058 c
+19.66 2.616 m
+19.453 2.616 19.299 2.547 19.204 2.411 c
+19.104 2.271 19.05 2.058 19.042 1.764 c
+20.218 1.764 l
+20.218 1.837 l
+20.196 2.109 20.145 2.308 20.057 2.425 c
+19.968 2.55 19.835 2.616 19.66 2.616 c
+22.555 0.765 m
+22.555 0.871 22.515 0.96 22.438 1.029 c
+22.357 1.106 22.206 1.195 21.982 1.294 c
+21.717 1.4 21.53 1.492 21.423 1.573 c
+21.313 1.65 21.236 1.738 21.188 1.837 c
+21.136 1.933 21.115 2.051 21.115 2.19 c
+21.115 2.433 21.203 2.635 21.379 2.793 c
+21.556 2.959 21.78 3.042 22.056 3.042 c
+22.349 3.042 22.584 2.955 22.761 2.778 c
+22.937 2.609 23.026 2.396 23.026 2.132 c
+22.54 2.132 l
+22.54 2.267 22.489 2.381 22.393 2.469 c
+22.305 2.565 22.191 2.616 22.056 2.616 c
+21.909 2.616 21.795 2.576 21.717 2.499 c
+21.637 2.429 21.599 2.33 21.599 2.205 c
+21.599 2.105 21.629 2.028 21.688 1.97 c
+21.747 1.911 21.886 1.83 22.114 1.735 c
+22.474 1.588 22.721 1.444 22.85 1.309 c
+22.985 1.18 23.055 1.007 23.055 0.794 c
+23.055 0.536 22.96 0.331 22.775 0.177 c
+22.599 0.019 22.364 -0.058 22.07 -0.058 c
+21.755 -0.058 21.501 0.029 21.306 0.206 c
+21.119 0.389 21.026 0.621 21.026 0.897 c
+21.512 0.897 l
+21.519 0.727 21.57 0.595 21.659 0.5 c
+21.755 0.412 21.894 0.368 22.07 0.368 c
+22.225 0.368 22.343 0.401 22.422 0.47 c
+22.511 0.536 22.555 0.636 22.555 0.765 c
+24.951 0.765 m
+24.951 0.871 24.911 0.96 24.834 1.029 c
+24.753 1.106 24.602 1.195 24.378 1.294 c
+24.113 1.4 23.926 1.492 23.82 1.573 c
+23.709 1.65 23.632 1.738 23.584 1.837 c
+23.533 1.933 23.511 2.051 23.511 2.19 c
+23.511 2.433 23.598 2.635 23.775 2.793 c
+23.951 2.959 24.176 3.042 24.452 3.042 c
+24.745 3.042 24.98 2.955 25.157 2.778 c
+25.333 2.609 25.422 2.396 25.422 2.132 c
+24.936 2.132 l
+24.936 2.267 24.885 2.381 24.789 2.469 c
+24.701 2.565 24.587 2.616 24.452 2.616 c
+24.304 2.616 24.19 2.576 24.113 2.499 c
+24.032 2.429 23.995 2.33 23.995 2.205 c
+23.995 2.105 24.026 2.028 24.084 1.97 c
+24.143 1.911 24.282 1.83 24.51 1.735 c
+24.87 1.588 25.117 1.444 25.245 1.309 c
+25.381 1.18 25.451 1.007 25.451 0.794 c
+25.451 0.536 25.356 0.331 25.171 0.177 c
+24.995 0.019 24.76 -0.058 24.466 -0.058 c
+24.15 -0.058 23.897 0.029 23.702 0.206 c
+23.515 0.389 23.423 0.621 23.423 0.897 c
+23.908 0.897 l
+23.914 0.727 23.966 0.595 24.055 0.5 c
+24.15 0.412 24.29 0.368 24.466 0.368 c
+24.62 0.368 24.738 0.401 24.819 0.47 c
+24.907 0.536 24.951 0.636 24.951 0.765 c
+f
+Q
+ endstream endobj 59 0 obj <</BBox[539.662 256.198 591.994 250.76]/Group 97 0 R/Length 10262/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 540.7341 252.7729 cm
+0 0 m
+0.545 2.176 l
+1.058 2.176 l
+0.088 -1.234 l
+0.019 -1.488 -0.084 -1.679 -0.22 -1.807 c
+-0.359 -1.944 -0.51 -2.013 -0.675 -2.013 c
+-0.745 -2.013 -0.83 -1.999 -0.926 -1.969 c
+-0.926 -1.558 l
+-0.823 -1.572 l
+-0.687 -1.572 -0.58 -1.535 -0.5 -1.469 c
+-0.411 -1.4 -0.345 -1.282 -0.293 -1.117 c
+-0.205 -0.779 l
+-1.072 2.176 l
+-0.544 2.176 l
+h
+1.239 0.823 m
+1.239 1.253 1.342 1.595 1.548 1.852 c
+1.761 2.106 2.04 2.234 2.386 2.234 c
+2.727 2.234 3.003 2.106 3.209 1.852 c
+3.422 1.606 3.535 1.272 3.547 0.853 c
+3.547 0.545 l
+3.547 0.111 3.437 -0.231 3.223 -0.484 c
+3.018 -0.742 2.739 -0.866 2.386 -0.866 c
+2.04 -0.866 1.768 -0.746 1.563 -0.5 c
+1.357 -0.257 1.247 0.077 1.239 0.5 c
+h
+1.724 0.545 m
+1.724 0.229 1.783 -0.014 1.9 -0.191 c
+2.025 -0.359 2.187 -0.44 2.386 -0.44 c
+2.816 -0.44 3.04 -0.132 3.061 0.485 c
+3.061 0.823 l
+3.061 1.125 2.995 1.368 2.87 1.544 c
+2.753 1.721 2.591 1.808 2.386 1.808 c
+2.187 1.808 2.025 1.721 1.9 1.544 c
+1.783 1.368 1.724 1.125 1.724 0.823 c
+h
+5.501 -0.544 m
+5.343 -0.76 5.108 -0.866 4.796 -0.866 c
+4.532 -0.866 4.326 -0.775 4.179 -0.588 c
+4.039 -0.404 3.973 -0.128 3.973 0.235 c
+3.973 2.176 l
+4.458 2.176 l
+4.458 0.265 l
+4.458 -0.199 4.597 -0.426 4.884 -0.426 c
+5.178 -0.426 5.376 -0.294 5.486 -0.029 c
+5.486 2.176 l
+5.987 2.176 l
+5.987 -0.808 l
+5.517 -0.808 l
+h
+7.641 0.823 m
+7.641 1.283 7.722 1.632 7.89 1.867 c
+8.067 2.11 8.316 2.234 8.64 2.234 c
+8.923 2.234 9.143 2.117 9.301 1.881 c
+9.301 3.425 l
+9.787 3.425 l
+9.787 -0.808 l
+9.345 -0.808 l
+9.316 -0.484 l
+9.158 -0.742 8.934 -0.866 8.64 -0.866 c
+8.324 -0.866 8.081 -0.749 7.905 -0.514 c
+7.728 -0.272 7.641 0.074 7.641 0.515 c
+h
+8.125 0.545 m
+8.125 0.21 8.173 -0.037 8.272 -0.191 c
+8.368 -0.349 8.53 -0.426 8.757 -0.426 c
+9 -0.426 9.184 -0.309 9.301 -0.073 c
+9.301 1.441 l
+9.172 1.676 8.993 1.794 8.757 1.794 c
+8.53 1.794 8.368 1.713 8.272 1.559 c
+8.173 1.401 8.125 1.162 8.125 0.838 c
+h
+10.253 0.823 m
+10.253 1.253 10.356 1.595 10.562 1.852 c
+10.775 2.106 11.054 2.234 11.4 2.234 c
+11.741 2.234 12.017 2.106 12.223 1.852 c
+12.436 1.606 12.549 1.272 12.561 0.853 c
+12.561 0.545 l
+12.561 0.111 12.451 -0.231 12.237 -0.484 c
+12.032 -0.742 11.753 -0.866 11.4 -0.866 c
+11.054 -0.866 10.782 -0.746 10.577 -0.5 c
+10.371 -0.257 10.261 0.077 10.253 0.5 c
+h
+10.739 0.545 m
+10.739 0.229 10.797 -0.014 10.914 -0.191 c
+11.04 -0.359 11.201 -0.44 11.4 -0.44 c
+11.83 -0.44 12.054 -0.132 12.075 0.485 c
+12.075 0.823 l
+12.075 1.125 12.01 1.368 11.884 1.544 c
+11.767 1.721 11.605 1.808 11.4 1.808 c
+11.201 1.808 11.04 1.721 10.914 1.544 c
+10.797 1.368 10.739 1.125 10.739 0.823 c
+h
+13.457 2.176 m
+13.472 1.852 l
+13.656 2.106 13.899 2.234 14.192 2.234 c
+14.722 2.234 14.99 1.881 15.001 1.176 c
+15.001 -0.808 l
+14.516 -0.808 l
+14.516 1.147 l
+14.516 1.382 14.475 1.548 14.398 1.646 c
+14.317 1.742 14.2 1.794 14.045 1.794 c
+13.928 1.794 13.818 1.754 13.722 1.676 c
+13.623 1.595 13.546 1.488 13.486 1.353 c
+13.486 -0.808 l
+13.002 -0.808 l
+13.002 2.176 l
+h
+15.607 3.117 m
+15.563 2.103 l
+15.196 2.103 l
+15.21 3.425 l
+15.607 3.425 l
+h
+16.831 2.897 m
+16.831 2.176 l
+17.286 2.176 l
+17.286 1.779 l
+16.831 1.779 l
+16.831 -0.073 l
+16.831 -0.191 16.85 -0.278 16.89 -0.338 c
+16.927 -0.396 16.996 -0.426 17.095 -0.426 c
+17.154 -0.426 17.216 -0.419 17.286 -0.396 c
+17.286 -0.808 l
+17.169 -0.845 17.055 -0.866 16.948 -0.866 c
+16.75 -0.866 16.599 -0.801 16.493 -0.661 c
+16.393 -0.525 16.345 -0.33 16.345 -0.073 c
+16.345 1.779 l
+15.89 1.779 l
+15.89 2.176 l
+16.345 2.176 l
+16.345 2.897 l
+h
+21.178 0.044 m
+21.634 2.176 l
+22.119 2.176 l
+21.384 -0.808 l
+21.002 -0.808 l
+20.414 1.324 l
+19.84 -0.808 l
+19.458 -0.808 l
+18.724 2.176 l
+19.208 2.176 l
+19.678 0.103 l
+20.223 2.176 l
+20.605 2.176 l
+h
+23.912 -0.808 m
+23.883 -0.742 23.861 -0.631 23.853 -0.484 c
+23.677 -0.742 23.456 -0.866 23.192 -0.866 c
+22.917 -0.866 22.699 -0.793 22.545 -0.646 c
+22.398 -0.492 22.325 -0.276 22.325 0 c
+22.325 0.302 22.427 0.545 22.633 0.721 c
+22.839 0.904 23.122 1 23.486 1 c
+23.838 1 l
+23.838 1.324 l
+23.838 1.5 23.798 1.621 23.721 1.691 c
+23.64 1.768 23.522 1.808 23.368 1.808 c
+23.221 1.808 23.096 1.764 23.001 1.676 c
+22.913 1.588 22.868 1.478 22.868 1.353 c
+22.383 1.353 l
+22.383 1.5 22.427 1.64 22.516 1.779 c
+22.604 1.926 22.722 2.036 22.868 2.117 c
+23.023 2.194 23.196 2.234 23.383 2.234 c
+23.695 2.234 23.931 2.153 24.089 1.999 c
+24.243 1.852 24.324 1.632 24.324 1.338 c
+24.324 -0.161 l
+24.331 -0.396 24.368 -0.598 24.426 -0.764 c
+24.426 -0.808 l
+h
+23.265 -0.426 m
+23.383 -0.426 23.493 -0.393 23.603 -0.323 c
+23.71 -0.257 23.788 -0.172 23.838 -0.073 c
+23.838 0.632 l
+23.574 0.632 l
+23.339 0.632 23.152 0.581 23.015 0.485 c
+22.887 0.387 22.824 0.243 22.824 0.059 c
+22.824 -0.11 22.853 -0.231 22.913 -0.309 c
+22.979 -0.389 23.096 -0.426 23.265 -0.426 c
+25.36 2.176 m
+25.375 1.852 l
+25.558 2.106 25.801 2.234 26.095 2.234 c
+26.624 2.234 26.892 1.881 26.903 1.176 c
+26.903 -0.808 l
+26.418 -0.808 l
+26.418 1.147 l
+26.418 1.382 26.378 1.548 26.301 1.646 c
+26.22 1.742 26.103 1.794 25.948 1.794 c
+25.831 1.794 25.72 1.754 25.625 1.676 c
+25.525 1.595 25.448 1.488 25.39 1.353 c
+25.39 -0.808 l
+24.904 -0.808 l
+24.904 2.176 l
+h
+28.091 2.897 m
+28.091 2.176 l
+28.546 2.176 l
+28.546 1.779 l
+28.091 1.779 l
+28.091 -0.073 l
+28.091 -0.191 28.108 -0.278 28.149 -0.338 c
+28.185 -0.396 28.256 -0.426 28.355 -0.426 c
+28.414 -0.426 28.476 -0.419 28.546 -0.396 c
+28.546 -0.808 l
+28.428 -0.845 28.314 -0.866 28.208 -0.866 c
+28.01 -0.866 27.859 -0.801 27.752 -0.661 c
+27.653 -0.525 27.605 -0.33 27.605 -0.073 c
+27.605 1.779 l
+27.15 1.779 l
+27.15 2.176 l
+27.605 2.176 l
+27.605 2.897 l
+h
+30.847 2.897 m
+30.847 2.176 l
+31.302 2.176 l
+31.302 1.779 l
+30.847 1.779 l
+30.847 -0.073 l
+30.847 -0.191 30.865 -0.278 30.905 -0.338 c
+30.942 -0.396 31.012 -0.426 31.111 -0.426 c
+31.17 -0.426 31.233 -0.419 31.302 -0.396 c
+31.302 -0.808 l
+31.185 -0.845 31.071 -0.866 30.964 -0.866 c
+30.766 -0.866 30.615 -0.801 30.508 -0.661 c
+30.409 -0.525 30.362 -0.33 30.362 -0.073 c
+30.362 1.779 l
+29.906 1.779 l
+29.906 2.176 l
+30.362 2.176 l
+30.362 2.897 l
+h
+31.615 0.823 m
+31.615 1.253 31.717 1.595 31.923 1.852 c
+32.137 2.106 32.416 2.234 32.761 2.234 c
+33.103 2.234 33.378 2.106 33.584 1.852 c
+33.797 1.606 33.911 1.272 33.922 0.853 c
+33.922 0.545 l
+33.922 0.111 33.812 -0.231 33.599 -0.484 c
+33.393 -0.742 33.114 -0.866 32.761 -0.866 c
+32.416 -0.866 32.143 -0.746 31.938 -0.5 c
+31.732 -0.257 31.622 0.077 31.615 0.5 c
+h
+32.1 0.545 m
+32.1 0.229 32.158 -0.014 32.276 -0.191 c
+32.401 -0.359 32.563 -0.44 32.761 -0.44 c
+33.191 -0.44 33.415 -0.132 33.437 0.485 c
+33.437 0.823 l
+33.437 1.125 33.371 1.368 33.246 1.544 c
+33.128 1.721 32.966 1.808 32.761 1.808 c
+32.563 1.808 32.401 1.721 32.276 1.544 c
+32.158 1.368 32.1 1.125 32.1 0.823 c
+h
+36.094 -0.808 -0.501 2.984 re
+36.124 2.97 m
+36.124 2.881 36.098 2.808 36.05 2.749 c
+36.01 2.698 35.94 2.675 35.844 2.675 c
+35.756 2.675 35.686 2.698 35.638 2.749 c
+35.598 2.808 35.58 2.874 35.58 2.955 c
+35.58 3.043 35.598 3.117 35.638 3.175 c
+35.686 3.234 35.756 3.263 35.844 3.263 c
+35.94 3.263 36.01 3.234 36.05 3.175 c
+36.098 3.117 36.124 3.047 36.124 2.97 c
+37.226 2.176 m
+37.24 1.852 l
+37.425 2.106 37.667 2.234 37.961 2.234 c
+38.49 2.234 38.758 1.881 38.769 1.176 c
+38.769 -0.808 l
+38.284 -0.808 l
+38.284 1.147 l
+38.284 1.382 38.244 1.548 38.167 1.646 c
+38.086 1.742 37.968 1.794 37.814 1.794 c
+37.696 1.794 37.586 1.754 37.49 1.676 c
+37.392 1.595 37.315 1.488 37.255 1.353 c
+37.255 -0.808 l
+36.77 -0.808 l
+36.77 2.176 l
+h
+40.349 -0.44 m
+40.515 -0.44 40.648 -0.393 40.746 -0.294 c
+40.842 -0.199 40.897 -0.055 40.908 0.133 c
+41.363 0.133 l
+41.353 -0.154 41.25 -0.393 41.055 -0.588 c
+40.868 -0.775 40.632 -0.866 40.349 -0.866 c
+39.985 -0.866 39.707 -0.749 39.511 -0.514 c
+39.314 -0.278 39.218 0.067 39.218 0.53 c
+39.218 0.853 l
+39.218 1.301 39.31 1.646 39.497 1.881 c
+39.692 2.117 39.975 2.234 40.349 2.234 c
+40.65 2.234 40.893 2.135 41.07 1.941 c
+41.253 1.742 41.353 1.478 41.363 1.147 c
+40.908 1.147 l
+40.885 1.371 40.827 1.536 40.731 1.646 c
+40.644 1.754 40.515 1.808 40.349 1.808 c
+40.133 1.808 39.971 1.735 39.864 1.588 c
+39.765 1.448 39.71 1.22 39.703 0.897 c
+39.703 0.515 l
+39.703 0.162 39.75 -0.087 39.85 -0.235 c
+39.956 -0.374 40.122 -0.44 40.349 -0.44 c
+42.29 -0.808 -0.5 4.233 re
+44.476 -0.544 m
+44.318 -0.76 44.083 -0.866 43.771 -0.866 c
+43.506 -0.866 43.3 -0.775 43.154 -0.588 c
+43.013 -0.404 42.948 -0.128 42.948 0.235 c
+42.948 2.176 l
+43.432 2.176 l
+43.432 0.265 l
+43.432 -0.199 43.572 -0.426 43.859 -0.426 c
+44.153 -0.426 44.351 -0.294 44.461 -0.029 c
+44.461 2.176 l
+44.961 2.176 l
+44.961 -0.808 l
+44.49 -0.808 l
+h
+45.413 0.823 m
+45.413 1.283 45.494 1.632 45.664 1.867 c
+45.839 2.11 46.09 2.234 46.412 2.234 c
+46.696 2.234 46.917 2.117 47.075 1.881 c
+47.075 3.425 l
+47.559 3.425 l
+47.559 -0.808 l
+47.118 -0.808 l
+47.089 -0.484 l
+46.931 -0.742 46.707 -0.866 46.412 -0.866 c
+46.096 -0.866 45.855 -0.749 45.678 -0.514 c
+45.502 -0.272 45.413 0.074 45.413 0.515 c
+h
+45.899 0.545 m
+45.899 0.21 45.946 -0.037 46.045 -0.191 c
+46.14 -0.349 46.302 -0.426 46.53 -0.426 c
+46.773 -0.426 46.957 -0.309 47.075 -0.073 c
+47.075 1.441 l
+46.946 1.676 46.765 1.794 46.53 1.794 c
+46.302 1.794 46.14 1.713 46.045 1.559 c
+45.946 1.401 45.899 1.162 45.899 0.838 c
+h
+49.188 -0.866 m
+48.812 -0.866 48.529 -0.76 48.334 -0.544 c
+48.136 -0.319 48.041 0.008 48.041 0.441 c
+48.041 0.809 l
+48.041 1.25 48.133 1.595 48.32 1.852 c
+48.515 2.106 48.791 2.234 49.143 2.234 c
+49.485 2.234 49.739 2.12 49.907 1.897 c
+50.084 1.669 50.175 1.324 50.187 0.853 c
+50.187 0.545 l
+48.525 0.545 l
+48.525 0.47 l
+48.525 0.148 48.585 -0.087 48.702 -0.235 c
+48.82 -0.374 48.989 -0.44 49.217 -0.44 c
+49.363 -0.44 49.489 -0.419 49.599 -0.367 c
+49.705 -0.309 49.808 -0.22 49.907 -0.103 c
+50.157 -0.411 l
+49.951 -0.716 49.628 -0.866 49.188 -0.866 c
+49.143 1.808 m
+48.937 1.808 48.783 1.738 48.687 1.603 c
+48.588 1.463 48.533 1.25 48.525 0.956 c
+49.701 0.956 l
+49.701 1.029 l
+49.68 1.301 49.628 1.5 49.54 1.617 c
+49.452 1.742 49.319 1.808 49.143 1.808 c
+50.628 -0.544 m
+50.628 -0.455 50.649 -0.382 50.701 -0.323 c
+50.76 -0.264 50.838 -0.235 50.936 -0.235 c
+51.043 -0.235 51.12 -0.264 51.172 -0.323 c
+51.231 -0.382 51.26 -0.455 51.26 -0.544 c
+51.26 -0.625 51.231 -0.691 51.172 -0.749 c
+51.12 -0.808 51.043 -0.837 50.936 -0.837 c
+50.838 -0.837 50.76 -0.808 50.701 -0.749 c
+50.649 -0.691 50.628 -0.625 50.628 -0.544 c
+f
+Q
+ endstream endobj 60 0 obj <</BBox[733.061 286.676 768.669 281.238]/Group 98 0 R/Length 5722/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 735.7654 284.1042 cm
+0 0 m
+0 -0.551 -0.118 -0.977 -0.353 -1.278 c
+-0.588 -1.573 -0.919 -1.72 -1.338 -1.72 c
+-1.76 -1.72 -2.095 -1.58 -2.338 -1.294 c
+-2.573 -0.999 -2.697 -0.588 -2.705 -0.058 c
+-2.705 0.69 l
+-2.705 1.228 -2.587 1.65 -2.352 1.955 c
+-2.109 2.257 -1.775 2.411 -1.353 2.411 c
+-0.933 2.411 -0.603 2.263 -0.368 1.97 c
+-0.133 1.675 -0.011 1.257 0 0.721 c
+h
+-0.5 0.69 m
+-0.5 1.121 -0.573 1.44 -0.721 1.646 c
+-0.86 1.852 -1.07 1.955 -1.353 1.955 c
+-1.628 1.955 -1.837 1.845 -1.984 1.631 c
+-2.124 1.426 -2.19 1.118 -2.19 0.706 c
+-2.19 0 l
+-2.19 -0.411 -2.124 -0.727 -1.984 -0.941 c
+-1.837 -1.157 -1.628 -1.264 -1.353 -1.264 c
+-1.07 -1.264 -0.86 -1.168 -0.721 -0.97 c
+-0.584 -0.775 -0.511 -0.47 -0.5 -0.058 c
+h
+0.948 1.323 m
+0.962 1 l
+1.147 1.253 1.389 1.382 1.683 1.382 c
+2.212 1.382 2.481 1.029 2.491 0.324 c
+2.491 -1.661 l
+2.007 -1.661 l
+2.007 0.294 l
+2.007 0.53 1.966 0.694 1.889 0.794 c
+1.808 0.889 1.69 0.941 1.536 0.941 c
+1.419 0.941 1.309 0.9 1.213 0.823 c
+1.113 0.742 1.036 0.636 0.977 0.5 c
+0.977 -1.661 l
+0.492 -1.661 l
+0.492 1.323 l
+h
+3.572 -1.661 -0.5 4.233 re
+5.056 -0.852 m
+5.6 1.323 l
+6.115 1.323 l
+5.145 -2.088 l
+5.074 -2.341 4.972 -2.532 4.836 -2.66 c
+4.696 -2.797 4.546 -2.866 4.38 -2.866 c
+4.31 -2.866 4.226 -2.851 4.131 -2.822 c
+4.131 -2.41 l
+4.233 -2.425 l
+4.369 -2.425 4.476 -2.389 4.557 -2.323 c
+4.644 -2.252 4.711 -2.135 4.762 -1.97 c
+4.85 -1.631 l
+3.983 1.323 l
+4.513 1.323 l
+h
+8.172 -1.661 -0.5 2.984 re
+8.202 2.117 m
+8.202 2.028 8.176 1.955 8.129 1.897 c
+8.089 1.845 8.018 1.823 7.923 1.823 c
+7.834 1.823 7.765 1.845 7.717 1.897 c
+7.676 1.955 7.659 2.022 7.659 2.102 c
+7.659 2.19 7.676 2.263 7.717 2.323 c
+7.765 2.381 7.834 2.411 7.923 2.411 c
+8.018 2.411 8.089 2.381 8.129 2.323 c
+8.176 2.263 8.202 2.194 8.202 2.117 c
+9.305 1.323 m
+9.319 1 l
+9.502 1.253 9.745 1.382 10.04 1.382 c
+10.568 1.382 10.836 1.029 10.848 0.324 c
+10.848 -1.661 l
+10.362 -1.661 l
+10.362 0.294 l
+10.362 0.53 10.323 0.694 10.246 0.794 c
+10.165 0.889 10.047 0.941 9.892 0.941 c
+9.775 0.941 9.664 0.9 9.569 0.823 c
+9.469 0.742 9.392 0.636 9.334 0.5 c
+9.334 -1.661 l
+8.849 -1.661 l
+8.849 1.323 l
+h
+11.296 -0.029 m
+11.296 0.43 11.377 0.779 11.546 1.014 c
+11.723 1.257 11.972 1.382 12.295 1.382 c
+12.579 1.382 12.799 1.264 12.957 1.029 c
+12.957 2.572 l
+13.442 2.572 l
+13.442 -1.661 l
+13.001 -1.661 l
+12.972 -1.338 l
+12.814 -1.595 12.59 -1.72 12.295 -1.72 c
+11.979 -1.72 11.737 -1.602 11.561 -1.367 c
+11.384 -1.124 11.296 -0.779 11.296 -0.338 c
+h
+11.781 -0.309 m
+11.781 -0.643 11.829 -0.889 11.929 -1.043 c
+12.024 -1.201 12.185 -1.278 12.413 -1.278 c
+12.656 -1.278 12.839 -1.161 12.957 -0.926 c
+12.957 0.588 l
+12.829 0.823 12.648 0.941 12.413 0.941 c
+12.185 0.941 12.024 0.86 11.929 0.706 c
+11.829 0.548 11.781 0.309 11.781 -0.015 c
+h
+15.07 -1.72 m
+14.695 -1.72 14.412 -1.613 14.217 -1.396 c
+14.019 -1.172 13.924 -0.845 13.924 -0.411 c
+13.924 -0.044 l
+13.924 0.397 14.015 0.742 14.203 1 c
+14.398 1.253 14.674 1.382 15.026 1.382 c
+15.368 1.382 15.621 1.268 15.79 1.043 c
+15.967 0.816 16.058 0.47 16.07 0 c
+16.07 -0.309 l
+14.408 -0.309 l
+14.408 -0.382 l
+14.408 -0.706 14.468 -0.941 14.585 -1.087 c
+14.703 -1.228 14.871 -1.294 15.1 -1.294 c
+15.247 -1.294 15.372 -1.271 15.482 -1.22 c
+15.588 -1.161 15.692 -1.073 15.79 -0.956 c
+16.041 -1.264 l
+15.835 -1.569 15.511 -1.72 15.07 -1.72 c
+15.026 0.956 m
+14.82 0.956 14.666 0.886 14.57 0.75 c
+14.471 0.611 14.416 0.397 14.408 0.103 c
+15.584 0.103 l
+15.584 0.177 l
+15.563 0.449 15.511 0.647 15.422 0.765 c
+15.335 0.889 15.202 0.956 15.026 0.956 c
+17.334 0.235 m
+17.849 1.323 l
+18.422 1.323 l
+17.598 -0.147 l
+18.437 -1.661 l
+17.878 -1.661 l
+17.348 -0.544 l
+16.82 -1.661 l
+16.261 -1.661 l
+17.084 -0.147 l
+16.276 1.323 l
+16.834 1.323 l
+h
+22.221 -0.808 m
+22.677 1.323 l
+23.162 1.323 l
+22.427 -1.661 l
+22.044 -1.661 l
+21.456 0.47 l
+20.883 -1.661 l
+20.502 -1.661 l
+19.767 1.323 l
+20.251 1.323 l
+20.722 -0.75 l
+21.265 1.323 l
+21.648 1.323 l
+h
+23.956 -1.661 -0.5 2.984 re
+23.985 2.117 m
+23.985 2.028 23.96 1.955 23.912 1.897 c
+23.871 1.845 23.802 1.823 23.706 1.823 c
+23.617 1.823 23.548 1.845 23.5 1.897 c
+23.459 1.955 23.441 2.022 23.441 2.102 c
+23.441 2.19 23.459 2.263 23.5 2.323 c
+23.548 2.381 23.617 2.411 23.706 2.411 c
+23.802 2.411 23.871 2.381 23.912 2.323 c
+23.96 2.263 23.985 2.194 23.985 2.117 c
+25.161 -1.661 -0.5 4.233 re
+26.362 -1.661 -0.5 4.233 re
+30.405 -0.309 m
+30.405 -0.779 30.32 -1.132 30.155 -1.367 c
+29.986 -1.602 29.743 -1.72 29.42 -1.72 c
+29.104 -1.72 28.868 -1.583 28.714 -1.309 c
+28.685 -1.661 l
+28.244 -1.661 l
+28.244 2.572 l
+28.729 2.572 l
+28.729 1 l
+28.884 1.253 29.115 1.382 29.42 1.382 c
+29.743 1.382 29.986 1.264 30.155 1.029 c
+30.32 0.794 30.405 0.445 30.405 -0.015 c
+h
+29.92 -0.029 m
+29.92 0.324 29.868 0.573 29.773 0.721 c
+29.674 0.867 29.512 0.941 29.288 0.941 c
+29.042 0.941 28.854 0.802 28.729 0.53 c
+28.729 -0.881 l
+28.847 -1.147 29.038 -1.278 29.302 -1.278 c
+29.516 -1.278 29.674 -1.205 29.773 -1.058 c
+29.868 -0.904 29.92 -0.661 29.92 -0.338 c
+h
+31.904 -1.72 m
+31.529 -1.72 31.247 -1.613 31.052 -1.396 c
+30.853 -1.172 30.757 -0.845 30.757 -0.411 c
+30.757 -0.044 l
+30.757 0.397 30.85 0.742 31.037 1 c
+31.232 1.253 31.507 1.382 31.86 1.382 c
+32.201 1.382 32.456 1.268 32.625 1.043 c
+32.801 0.816 32.893 0.47 32.903 0 c
+32.903 -0.309 l
+31.243 -0.309 l
+31.243 -0.382 l
+31.243 -0.706 31.301 -0.941 31.419 -1.087 c
+31.536 -1.228 31.706 -1.294 31.933 -1.294 c
+32.08 -1.294 32.205 -1.271 32.315 -1.22 c
+32.423 -1.161 32.525 -1.073 32.625 -0.956 c
+32.874 -1.264 l
+32.668 -1.569 32.345 -1.72 31.904 -1.72 c
+31.86 0.956 m
+31.654 0.956 31.5 0.886 31.405 0.75 c
+31.305 0.611 31.25 0.397 31.243 0.103 c
+32.419 0.103 l
+32.419 0.177 l
+32.396 0.449 32.345 0.647 32.257 0.765 c
+32.169 0.889 32.037 0.956 31.86 0.956 c
+f
+Q
+ endstream endobj 61 0 obj <</BBox[739.087 280.091 762.253 275.799]/Group 99 0 R/Length 5385/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 740.2189 276.2256 cm
+0 0 m
+0.166 0 0.298 0.048 0.397 0.147 c
+0.493 0.243 0.547 0.386 0.559 0.573 c
+1.014 0.573 l
+1.004 0.287 0.9 0.048 0.706 -0.147 c
+0.518 -0.334 0.283 -0.426 0 -0.426 c
+-0.363 -0.426 -0.643 -0.309 -0.837 -0.073 c
+-1.036 0.162 -1.132 0.507 -1.132 0.97 c
+-1.132 1.294 l
+-1.132 1.742 -1.04 2.087 -0.852 2.323 c
+-0.658 2.558 -0.374 2.675 0 2.675 c
+0.302 2.675 0.544 2.576 0.721 2.381 c
+0.904 2.182 1.004 1.918 1.014 1.588 c
+0.559 1.588 l
+0.537 1.812 0.478 1.977 0.383 2.087 c
+0.294 2.194 0.166 2.249 0 2.249 c
+-0.216 2.249 -0.378 2.176 -0.484 2.028 c
+-0.584 1.889 -0.639 1.661 -0.646 1.338 c
+-0.646 0.956 l
+-0.646 0.603 -0.598 0.353 -0.5 0.206 c
+-0.393 0.066 -0.228 0 0 0 c
+1.309 1.264 m
+1.309 1.694 1.411 2.036 1.617 2.294 c
+1.831 2.547 2.109 2.675 2.455 2.675 c
+2.797 2.675 3.072 2.547 3.278 2.294 c
+3.491 2.047 3.605 1.712 3.616 1.294 c
+3.616 0.985 l
+3.616 0.551 3.506 0.21 3.293 -0.044 c
+3.087 -0.301 2.808 -0.426 2.455 -0.426 c
+2.109 -0.426 1.837 -0.305 1.632 -0.058 c
+1.426 0.183 1.316 0.518 1.309 0.941 c
+h
+1.794 0.985 m
+1.794 0.669 1.852 0.426 1.97 0.25 c
+2.095 0.081 2.257 0 2.455 0 c
+2.885 0 3.109 0.309 3.131 0.926 c
+3.131 1.264 l
+3.131 1.565 3.065 1.808 2.94 1.984 c
+2.822 2.161 2.66 2.249 2.455 2.249 c
+2.257 2.249 2.095 2.161 1.97 1.984 c
+1.852 1.808 1.794 1.565 1.794 1.264 c
+h
+4.509 2.616 m
+4.524 2.337 l
+4.7 2.562 4.939 2.675 5.244 2.675 c
+5.575 2.675 5.806 2.529 5.935 2.234 c
+6.119 2.529 6.379 2.675 6.714 2.675 c
+7.273 2.675 7.555 2.33 7.566 1.646 c
+7.566 -0.368 l
+7.082 -0.368 l
+7.082 1.602 l
+7.082 1.816 7.041 1.974 6.964 2.072 c
+6.883 2.18 6.751 2.234 6.567 2.234 c
+6.42 2.234 6.302 2.176 6.215 2.058 c
+6.126 1.947 6.071 1.808 6.053 1.631 c
+6.053 -0.368 l
+5.567 -0.368 l
+5.567 1.617 l
+5.557 2.028 5.384 2.234 5.053 2.234 c
+4.807 2.234 4.634 2.109 4.538 1.866 c
+4.538 -0.368 l
+4.054 -0.368 l
+4.054 2.616 l
+h
+8.47 2.616 m
+8.485 2.337 l
+8.661 2.562 8.9 2.675 9.205 2.675 c
+9.536 2.675 9.768 2.529 9.897 2.234 c
+10.08 2.529 10.341 2.675 10.675 2.675 c
+11.234 2.675 11.516 2.33 11.528 1.646 c
+11.528 -0.368 l
+11.043 -0.368 l
+11.043 1.602 l
+11.043 1.816 11.003 1.974 10.926 2.072 c
+10.845 2.18 10.712 2.234 10.529 2.234 c
+10.381 2.234 10.263 2.176 10.176 2.058 c
+10.088 1.947 10.032 1.808 10.014 1.631 c
+10.014 -0.368 l
+9.529 -0.368 l
+9.529 1.617 l
+9.517 2.028 9.345 2.234 9.014 2.234 c
+8.769 2.234 8.596 2.109 8.5 1.866 c
+8.5 -0.368 l
+8.015 -0.368 l
+8.015 2.616 l
+h
+12.505 -0.368 -0.499 2.984 re
+12.534 3.41 m
+12.534 3.322 12.509 3.248 12.461 3.19 c
+12.421 3.138 12.351 3.117 12.256 3.117 c
+12.168 3.117 12.098 3.138 12.05 3.19 c
+12.009 3.248 11.991 3.315 11.991 3.395 c
+11.991 3.484 12.009 3.557 12.05 3.616 c
+12.098 3.674 12.168 3.705 12.256 3.705 c
+12.351 3.705 12.421 3.674 12.461 3.616 c
+12.509 3.557 12.534 3.487 12.534 3.41 c
+13.814 3.337 m
+13.814 2.616 l
+14.269 2.616 l
+14.269 2.219 l
+13.814 2.219 l
+13.814 0.368 l
+13.814 0.25 13.832 0.162 13.872 0.103 c
+13.909 0.044 13.979 0.015 14.078 0.015 c
+14.137 0.015 14.199 0.023 14.269 0.044 c
+14.269 -0.368 l
+14.151 -0.405 14.038 -0.426 13.931 -0.426 c
+13.733 -0.426 13.582 -0.36 13.475 -0.22 c
+13.376 -0.085 13.328 0.11 13.328 0.368 c
+13.328 2.219 l
+12.873 2.219 l
+12.873 2.616 l
+13.328 2.616 l
+13.328 3.337 l
+h
+15.368 3.337 m
+15.368 2.616 l
+15.824 2.616 l
+15.824 2.219 l
+15.368 2.219 l
+15.368 0.368 l
+15.368 0.25 15.387 0.162 15.427 0.103 c
+15.464 0.044 15.534 0.015 15.632 0.015 c
+15.691 0.015 15.754 0.023 15.824 0.044 c
+15.824 -0.368 l
+15.707 -0.405 15.592 -0.426 15.485 -0.426 c
+15.287 -0.426 15.136 -0.36 15.03 -0.22 c
+14.93 -0.085 14.883 0.11 14.883 0.368 c
+14.883 2.219 l
+14.427 2.219 l
+14.427 2.616 l
+14.883 2.616 l
+14.883 3.337 l
+h
+17.345 -0.426 m
+16.97 -0.426 16.688 -0.32 16.492 -0.103 c
+16.294 0.121 16.199 0.449 16.199 0.882 c
+16.199 1.249 l
+16.199 1.691 16.291 2.036 16.478 2.294 c
+16.673 2.547 16.948 2.675 17.301 2.675 c
+17.643 2.675 17.897 2.562 18.065 2.337 c
+18.242 2.109 18.334 1.764 18.344 1.294 c
+18.344 0.985 l
+16.684 0.985 l
+16.684 0.912 l
+16.684 0.588 16.742 0.353 16.86 0.206 c
+16.977 0.066 17.147 0 17.374 0 c
+17.521 0 17.646 0.023 17.756 0.073 c
+17.864 0.133 17.966 0.22 18.065 0.338 c
+18.315 0.029 l
+18.109 -0.276 17.786 -0.426 17.345 -0.426 c
+17.301 2.249 m
+17.095 2.249 16.941 2.18 16.846 2.043 c
+16.746 1.904 16.691 1.691 16.684 1.396 c
+17.86 1.396 l
+17.86 1.47 l
+17.837 1.742 17.786 1.941 17.698 2.058 c
+17.61 2.182 17.477 2.249 17.301 2.249 c
+18.653 1.264 m
+18.653 1.723 18.734 2.072 18.903 2.308 c
+19.079 2.55 19.329 2.675 19.653 2.675 c
+19.936 2.675 20.156 2.558 20.314 2.323 c
+20.314 3.865 l
+20.8 3.865 l
+20.8 -0.368 l
+20.359 -0.368 l
+20.329 -0.044 l
+20.171 -0.301 19.946 -0.426 19.653 -0.426 c
+19.337 -0.426 19.094 -0.309 18.918 -0.073 c
+18.741 0.169 18.653 0.515 18.653 0.956 c
+h
+19.138 0.985 m
+19.138 0.651 19.186 0.405 19.285 0.25 c
+19.381 0.092 19.543 0.015 19.771 0.015 c
+20.013 0.015 20.197 0.133 20.314 0.368 c
+20.314 1.881 l
+20.185 2.117 20.006 2.234 19.771 2.234 c
+19.543 2.234 19.381 2.153 19.285 1.999 c
+19.186 1.841 19.138 1.602 19.138 1.278 c
+h
+21.402 -0.103 m
+21.402 -0.015 21.424 0.059 21.475 0.118 c
+21.534 0.177 21.612 0.206 21.71 0.206 c
+21.818 0.206 21.895 0.177 21.946 0.118 c
+22.005 0.059 22.034 -0.015 22.034 -0.103 c
+22.034 -0.183 22.005 -0.25 21.946 -0.309 c
+21.895 -0.368 21.818 -0.397 21.71 -0.397 c
+21.612 -0.397 21.534 -0.368 21.475 -0.309 c
+21.424 -0.25 21.402 -0.183 21.402 -0.103 c
+f
+Q
+ endstream endobj 62 0 obj <</BBox[736.166 273.506 765.648 268.067]/Group 100 0 R/Length 6993/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+q 1 0 0 1 738.7823 270.5519 cm
+0 0 m
+-0.029 -0.434 -0.154 -0.765 -0.368 -1 c
+-0.584 -1.228 -0.893 -1.338 -1.294 -1.338 c
+-1.698 -1.338 -2.022 -1.187 -2.263 -0.882 c
+-2.499 -0.57 -2.616 -0.154 -2.616 0.367 c
+-2.616 1.103 l
+-2.616 1.621 -2.495 2.032 -2.249 2.337 c
+-2.007 2.639 -1.672 2.793 -1.249 2.793 c
+-0.867 2.793 -0.57 2.675 -0.353 2.44 c
+-0.14 2.205 -0.023 1.87 0 1.44 c
+-0.515 1.44 l
+-0.536 1.771 -0.603 2.007 -0.721 2.146 c
+-0.838 2.282 -1.014 2.352 -1.249 2.352 c
+-1.525 2.352 -1.735 2.242 -1.881 2.028 c
+-2.028 1.812 -2.102 1.5 -2.102 1.087 c
+-2.102 0.353 l
+-2.102 -0.052 -2.036 -0.36 -1.897 -0.574 c
+-1.76 -0.79 -1.558 -0.897 -1.294 -0.897 c
+-1.029 -0.897 -0.846 -0.834 -0.735 -0.706 c
+-0.617 -0.58 -0.544 -0.345 -0.515 0 c
+h
+0.881 1.396 m
+1.066 1.639 1.301 1.764 1.587 1.764 c
+2.117 1.764 2.385 1.411 2.396 0.706 c
+2.396 -1.279 l
+1.911 -1.279 l
+1.911 0.676 l
+1.911 0.912 1.87 1.076 1.793 1.176 c
+1.712 1.271 1.594 1.323 1.44 1.323 c
+1.323 1.323 1.213 1.282 1.117 1.205 c
+1.018 1.124 0.941 1.018 0.881 0.881 c
+0.881 -1.279 l
+0.397 -1.279 l
+0.397 2.954 l
+0.881 2.954 l
+h
+2.837 0.353 m
+2.837 0.783 2.94 1.124 3.146 1.382 c
+3.358 1.635 3.638 1.764 3.983 1.764 c
+4.326 1.764 4.601 1.635 4.806 1.382 c
+5.02 1.135 5.134 0.801 5.145 0.382 c
+5.145 0.073 l
+5.145 -0.36 5.034 -0.702 4.821 -0.956 c
+4.615 -1.213 4.336 -1.338 3.983 -1.338 c
+3.638 -1.338 3.366 -1.216 3.16 -0.97 c
+2.955 -0.728 2.844 -0.393 2.837 0.029 c
+h
+3.322 0.073 m
+3.322 -0.243 3.381 -0.485 3.499 -0.661 c
+3.623 -0.831 3.785 -0.912 3.983 -0.912 c
+4.413 -0.912 4.638 -0.603 4.659 0.014 c
+4.659 0.353 l
+4.659 0.654 4.594 0.897 4.469 1.072 c
+4.351 1.249 4.189 1.338 3.983 1.338 c
+3.785 1.338 3.623 1.249 3.499 1.072 c
+3.381 0.897 3.322 0.654 3.322 0.353 c
+h
+5.479 0.353 m
+5.479 0.783 5.582 1.124 5.787 1.382 c
+6.001 1.635 6.28 1.764 6.626 1.764 c
+6.967 1.764 7.243 1.635 7.449 1.382 c
+7.661 1.135 7.776 0.801 7.786 0.382 c
+7.786 0.073 l
+7.786 -0.36 7.676 -0.702 7.464 -0.956 c
+7.258 -1.213 6.978 -1.338 6.626 -1.338 c
+6.28 -1.338 6.008 -1.216 5.802 -0.97 c
+5.596 -0.728 5.486 -0.393 5.479 0.029 c
+h
+5.964 0.073 m
+5.964 -0.243 6.023 -0.485 6.14 -0.661 c
+6.265 -0.831 6.427 -0.912 6.626 -0.912 c
+7.056 -0.912 7.279 -0.603 7.302 0.014 c
+7.302 0.353 l
+7.302 0.654 7.235 0.897 7.11 1.072 c
+6.993 1.249 6.832 1.338 6.626 1.338 c
+6.427 1.338 6.265 1.249 6.14 1.072 c
+6.023 0.897 5.964 0.654 5.964 0.353 c
+h
+9.668 -0.515 m
+9.668 -0.408 9.628 -0.32 9.55 -0.25 c
+9.469 -0.173 9.319 -0.085 9.095 0.014 c
+8.831 0.121 8.643 0.213 8.536 0.294 c
+8.426 0.371 8.349 0.459 8.301 0.559 c
+8.249 0.654 8.228 0.771 8.228 0.912 c
+8.228 1.153 8.316 1.356 8.492 1.514 c
+8.669 1.679 8.893 1.764 9.168 1.764 c
+9.462 1.764 9.697 1.675 9.874 1.5 c
+10.051 1.33 10.138 1.117 10.138 0.852 c
+9.654 0.852 l
+9.654 0.989 9.602 1.103 9.506 1.19 c
+9.419 1.286 9.305 1.338 9.168 1.338 c
+9.022 1.338 8.908 1.297 8.831 1.22 c
+8.75 1.151 8.713 1.051 8.713 0.926 c
+8.713 0.827 8.742 0.75 8.801 0.691 c
+8.86 0.632 8.999 0.551 9.227 0.455 c
+9.587 0.309 9.833 0.166 9.962 0.029 c
+10.098 -0.1 10.167 -0.272 10.167 -0.485 c
+10.167 -0.742 10.072 -0.948 9.889 -1.103 c
+9.712 -1.261 9.477 -1.338 9.183 -1.338 c
+8.867 -1.338 8.613 -1.249 8.419 -1.073 c
+8.232 -0.889 8.139 -0.658 8.139 -0.382 c
+8.625 -0.382 l
+8.632 -0.551 8.683 -0.684 8.771 -0.779 c
+8.867 -0.867 9.007 -0.912 9.183 -0.912 c
+9.338 -0.912 9.455 -0.879 9.536 -0.809 c
+9.624 -0.742 9.668 -0.643 9.668 -0.515 c
+11.682 -1.338 m
+11.307 -1.338 11.025 -1.231 10.83 -1.014 c
+10.631 -0.79 10.535 -0.463 10.535 -0.029 c
+10.535 0.338 l
+10.535 0.779 10.628 1.124 10.815 1.382 c
+11.009 1.635 11.285 1.764 11.638 1.764 c
+11.979 1.764 12.233 1.65 12.403 1.425 c
+12.579 1.198 12.671 0.852 12.681 0.382 c
+12.681 0.073 l
+11.021 0.073 l
+11.021 0 l
+11.021 -0.324 11.079 -0.559 11.197 -0.706 c
+11.314 -0.846 11.484 -0.912 11.711 -0.912 c
+11.858 -0.912 11.983 -0.889 12.093 -0.838 c
+12.2 -0.779 12.303 -0.691 12.403 -0.574 c
+12.652 -0.882 l
+12.446 -1.187 12.123 -1.338 11.682 -1.338 c
+11.638 1.338 m
+11.432 1.338 11.278 1.268 11.183 1.132 c
+11.083 0.992 11.028 0.779 11.021 0.484 c
+12.197 0.484 l
+12.197 0.559 l
+12.174 0.831 12.123 1.029 12.035 1.147 c
+11.946 1.271 11.814 1.338 11.638 1.338 c
+16.518 -0.426 m
+16.974 1.705 l
+17.458 1.705 l
+16.724 -1.279 l
+16.342 -1.279 l
+15.754 0.852 l
+15.181 -1.279 l
+14.798 -1.279 l
+14.063 1.705 l
+14.549 1.705 l
+15.019 -0.368 l
+15.563 1.705 l
+15.945 1.705 l
+h
+18.252 -1.279 -0.5 2.984 re
+18.282 2.499 m
+18.282 2.41 18.256 2.337 18.208 2.278 c
+18.168 2.227 18.098 2.205 18.003 2.205 c
+17.914 2.205 17.845 2.227 17.797 2.278 c
+17.756 2.337 17.738 2.404 17.738 2.484 c
+17.738 2.572 17.756 2.645 17.797 2.705 c
+17.845 2.763 17.914 2.793 18.003 2.793 c
+18.098 2.793 18.168 2.763 18.208 2.705 c
+18.256 2.645 18.282 2.576 18.282 2.499 c
+20.369 -0.515 m
+20.369 -0.408 20.328 -0.32 20.251 -0.25 c
+20.17 -0.173 20.02 -0.085 19.796 0.014 c
+19.532 0.121 19.343 0.213 19.237 0.294 c
+19.127 0.371 19.05 0.459 19.002 0.559 c
+18.95 0.654 18.929 0.771 18.929 0.912 c
+18.929 1.153 19.017 1.356 19.193 1.514 c
+19.37 1.679 19.594 1.764 19.869 1.764 c
+20.164 1.764 20.399 1.675 20.575 1.5 c
+20.751 1.33 20.839 1.117 20.839 0.852 c
+20.355 0.852 l
+20.355 0.989 20.303 1.103 20.207 1.19 c
+20.12 1.286 20.006 1.338 19.869 1.338 c
+19.723 1.338 19.609 1.297 19.532 1.22 c
+19.451 1.151 19.414 1.051 19.414 0.926 c
+19.414 0.827 19.443 0.75 19.502 0.691 c
+19.561 0.632 19.7 0.551 19.929 0.455 c
+20.288 0.309 20.534 0.166 20.663 0.029 c
+20.799 -0.1 20.869 -0.272 20.869 -0.485 c
+20.869 -0.742 20.773 -0.948 20.59 -1.103 c
+20.413 -1.261 20.178 -1.338 19.884 -1.338 c
+19.568 -1.338 19.314 -1.249 19.12 -1.073 c
+18.932 -0.889 18.84 -0.658 18.84 -0.382 c
+19.326 -0.382 l
+19.333 -0.551 19.384 -0.684 19.472 -0.779 c
+19.568 -0.867 19.708 -0.912 19.884 -0.912 c
+20.039 -0.912 20.156 -0.879 20.237 -0.809 c
+20.325 -0.742 20.369 -0.643 20.369 -0.515 c
+22.383 -1.338 m
+22.008 -1.338 21.725 -1.231 21.531 -1.014 c
+21.332 -0.79 21.236 -0.463 21.236 -0.029 c
+21.236 0.338 l
+21.236 0.779 21.328 1.124 21.516 1.382 c
+21.71 1.635 21.986 1.764 22.339 1.764 c
+22.68 1.764 22.934 1.65 23.103 1.425 c
+23.28 1.198 23.372 0.852 23.382 0.382 c
+23.382 0.073 l
+21.722 0.073 l
+21.722 0 l
+21.722 -0.324 21.78 -0.559 21.898 -0.706 c
+22.015 -0.846 22.185 -0.912 22.412 -0.912 c
+22.559 -0.912 22.684 -0.889 22.794 -0.838 c
+22.901 -0.779 23.004 -0.691 23.103 -0.574 c
+23.353 -0.882 l
+23.147 -1.187 22.824 -1.338 22.383 -1.338 c
+22.339 1.338 m
+22.133 1.338 21.978 1.268 21.883 1.132 c
+21.784 0.992 21.729 0.779 21.722 0.484 c
+22.898 0.484 l
+22.898 0.559 l
+22.875 0.831 22.824 1.029 22.736 1.147 c
+22.647 1.271 22.516 1.338 22.339 1.338 c
+24.323 -1.279 -0.5 4.233 re
+25.808 -0.47 m
+26.352 1.705 l
+26.866 1.705 l
+25.896 -1.706 l
+25.826 -1.959 25.724 -2.15 25.587 -2.278 c
+25.448 -2.415 25.297 -2.484 25.132 -2.484 c
+25.062 -2.484 24.978 -2.469 24.882 -2.44 c
+24.882 -2.028 l
+24.984 -2.043 l
+25.121 -2.043 25.227 -2.007 25.308 -1.941 c
+25.396 -1.87 25.462 -1.753 25.514 -1.588 c
+25.602 -1.249 l
+24.735 1.705 l
+25.264 1.705 l
+h
+f
+Q
+ endstream endobj 100 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 12 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 1.0/op false>> endobj 99 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 98 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 97 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 96 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 95 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 94 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 91 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 93 0 obj <</AntiAlias false/ColorSpace 92 0 R/Coords[0.0 0.0 1.0 0.0]/Domain[0.0 1.0]/Extend[true true]/Function 101 0 R/ShadingType 2>> endobj 92 0 obj [/Separation/Black/DeviceCMYK<</C0[0.0 0.0 0.0 0.0]/C1[0.0 0.0 0.0 1.0]/Domain[0 1]/FunctionType 2/N 1.0/Range[0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]>>] endobj 101 0 obj <</Bounds[0.25 0.75]/Domain[0.0 1.0]/Encode[0.0 1.0 0.0 1.0 0.0 1.0]/FunctionType 3/Functions[102 0 R 102 0 R 102 0 R]>> endobj 102 0 obj <</C0[0.0]/C1[0.0]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 90 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 89 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 88 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 87 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 86 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 85 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 84 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 83 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 82 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 81 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 80 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 79 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 78 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 77 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 76 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 75 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 74 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 73 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 72 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 71 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 70 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 69 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 68 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 67 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 66 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 65 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 63 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 64 0 obj <</AntiAlias false/ColorSpace/DeviceCMYK/Coords[0.0 0.0 1.0 0.0]/Domain[0.0 1.0]/Extend[true true]/Function 103 0 R/ShadingType 2>> endobj 103 0 obj <</Bounds[0.5]/Domain[0.0 1.0]/Encode[0.0 1.0 0.0 1.0]/FunctionType 3/Functions[104 0 R 105 0 R]>> endobj 104 0 obj <</C0[0.792969 0.800781 0.128906 0.015625]/C1[0.652344 0.660156 0.0 0.0]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 105 0 obj <</C0[0.652344 0.660156 0.0 0.0]/C1[0.0 0.0 0.0 0.0]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 22 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 106 0 R/Type/ExtGState/ca 1.0/op false>> endobj 23 0 obj <</AIS false/BM/Normal/CA 0.75/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 0.75/op false>> endobj 24 0 obj <</AIS false/BM/Normal/CA 0.899994/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 0.899994/op false>> endobj 25 0 obj <</AIS false/BM/Normal/CA 0.666672/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 0.666672/op false>> endobj 26 0 obj <</AIS false/BM/Normal/CA 0.669998/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 0.669998/op false>> endobj 27 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 107 0 R/Type/ExtGState/ca 1.0/op false>> endobj 107 0 obj <</BC 108 0 R/G 109 0 R/S/Luminosity/Type/Mask>> endobj 108 0 obj [0.0] endobj 109 0 obj <</BBox[320.12 122.007 531.709 116.418]/Group 110 0 R/Length 117/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>/XObject<</Fm0 111 0 R>>>>/Subtype/Form>>stream
+0 g
+/GS0 gs
+320.12 122.007 211.589 -5.589 re
+f
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0.709 w 4 M 1 j 1 J []0 d
+0 TL/Fm0 Do
+Q
+ endstream endobj 110 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 111 0 obj <</BBox[320.12 122.007 531.709 116.418]/Group 112 0 R/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 113 0 R>>/ExtGState<</GS0 12 0 R>>/Shading<</Sh0 114 0 R>>>>/Subtype/Form>>stream
+q
+320.12 122.007 211.589 -5.589 re
+W n
+q
+0 g
+/GS0 gs
+212.3990173 0 0 139.9599457 319.7158203 119.2128906 cm
+BX /Sh0 sh EX Q
+Q
+ endstream endobj 112 0 obj <</CS/DeviceCMYK/I true/K false/S/Transparency/Type/Group>> endobj 114 0 obj <</AntiAlias false/ColorSpace 113 0 R/Coords[0.0 0.0 1.0 0.0]/Domain[0.0 1.0]/Extend[true true]/Function 115 0 R/ShadingType 2>> endobj 113 0 obj [/DeviceN[/Black]/DeviceCMYK 116 0 R 117 0 R] endobj 115 0 obj <</Bounds[0.25 0.75]/Domain[0.0 1.0]/Encode[0.0 1.0 0.0 1.0 0.0 1.0]/FunctionType 3/Functions[118 0 R 119 0 R 120 0 R]>> endobj 118 0 obj <</C0[1.0]/C1[0.05]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 119 0 obj <</C0[0.05]/C1[0.05]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 120 0 obj <</C0[0.05]/C1[1.0]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 116 0 obj <</Domain[0.0 1.0]/FunctionType 4/Length 267/Range[0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]>>stream
+{1.000000 2 1 roll 1.000000 2 1 roll 1.000000 2 1 roll 0 index 1.000000
+cvr exch sub 2 1 roll 5 -1 roll 1.000000 cvr exch sub 5 1
+roll 4 -1 roll 1.000000 cvr exch sub 4 1 roll 3 -1 roll 1.000000
+cvr exch sub 3 1 roll 2 -1 roll 1.000000 cvr exch sub 2 1
+roll pop } endstream endobj 117 0 obj <</Process 121 0 R/Subtype/NChannel>> endobj 121 0 obj <</ColorSpace/DeviceCMYK/Components[/Cyan/Magenta/Yellow/Black]>> endobj 106 0 obj <</BC 122 0 R/G 123 0 R/S/Luminosity/Type/Mask>> endobj 122 0 obj [0.0] endobj 123 0 obj <</BBox[198.027 568.502 444.988 526.485]/Group 124 0 R/Length 92/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>/XObject<</Fm0 125 0 R>>>>/Subtype/Form>>stream
+0 g
+/GS0 gs
+198.026 568.502 246.962 -42.017 re
+f
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 TL/Fm0 Do
+Q
+ endstream endobj 124 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 125 0 obj <</BBox[198.026 568.502 444.988 526.485]/Group 126 0 R/Length 128/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 113 0 R>>/ExtGState<</GS0 12 0 R>>/Shading<</Sh0 127 0 R>>>>/Subtype/Form>>stream
+q
+198.026 568.502 246.962 -42.017 re
+W n
+q
+0 g
+/GS0 gs
+246.9619904 0 0 246.9619904 198.0263672 547.4936523 cm
+BX /Sh0 sh EX Q
+Q
+ endstream endobj 126 0 obj <</CS/DeviceCMYK/I true/K false/S/Transparency/Type/Group>> endobj 127 0 obj <</AntiAlias false/ColorSpace 113 0 R/Coords[0.0 0.0 1.0 0.0]/Domain[0.0 1.0]/Extend[true true]/Function 128 0 R/ShadingType 2>> endobj 128 0 obj <</Bounds[0.5]/Domain[0.0 1.0]/Encode[0.0 1.0 0.0 1.0]/FunctionType 3/Functions[129 0 R 130 0 R]>> endobj 129 0 obj <</C0[0.0]/C1[0.643137]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 130 0 obj <</C0[0.643137]/C1[0.682759]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 5 0 obj <</Intent 131 0 R/Name(Layer 1)/Type/OCG/Usage 132 0 R>> endobj 131 0 obj [/View/Design] endobj 132 0 obj <</CreatorInfo<</Creator(Adobe Illustrator 20.1)/Subtype/Artwork>>>> endobj 11 0 obj <</LastModified(D:20161031172420Z)/Private 133 0 R>> endobj 133 0 obj <</AIMetaData 134 0 R/AIPDFPrivateData1 135 0 R/AIPDFPrivateData10 136 0 R/AIPDFPrivateData100 137 0 R/AIPDFPrivateData101 138 0 R/AIPDFPrivateData102 139 0 R/AIPDFPrivateData103 140 0 R/AIPDFPrivateData104 141 0 R/AIPDFPrivateData105 142 0 R/AIPDFPrivateData106 143 0 R/AIPDFPrivateData107 144 0 R/AIPDFPrivateData108 145 0 R/AIPDFPrivateData109 146 0 R/AIPDFPrivateData11 147 0 R/AIPDFPrivateData110 148 0 R/AIPDFPrivateData111 149 0 R/AIPDFPrivateData112 150 0 R/AIPDFPrivateData113 151 0 R/AIPDFPrivateData114 152 0 R/AIPDFPrivateData115 153 0 R/AIPDFPrivateData116 154 0 R/AIPDFPrivateData117 155 0 R/AIPDFPrivateData118 156 0 R/AIPDFPrivateData119 157 0 R/AIPDFPrivateData12 158 0 R/AIPDFPrivateData120 159 0 R/AIPDFPrivateData121 160 0 R/AIPDFPrivateData122 161 0 R/AIPDFPrivateData123 162 0 R/AIPDFPrivateData124 163 0 R/AIPDFPrivateData125 164 0 R/AIPDFPrivateData126 165 0 R/AIPDFPrivateData127 166 0 R/AIPDFPrivateData128 167 0 R/AIPDFPrivateData129 168 0 R/AIPDFPrivateData13 169 0 R/AIPDFPrivateData130 170 0 R/AIPDFPrivateData131 171 0 R/AIPDFPrivateData132 172 0 R/AIPDFPrivateData133 173 0 R/AIPDFPrivateData134 174 0 R/AIPDFPrivateData135 175 0 R/AIPDFPrivateData136 176 0 R/AIPDFPrivateData137 177 0 R/AIPDFPrivateData138 178 0 R/AIPDFPrivateData139 179 0 R/AIPDFPrivateData14 180 0 R/AIPDFPrivateData140 181 0 R/AIPDFPrivateData141 182 0 R/AIPDFPrivateData142 183 0 R/AIPDFPrivateData143 184 0 R/AIPDFPrivateData144 185 0 R/AIPDFPrivateData145 186 0 R/AIPDFPrivateData146 187 0 R/AIPDFPrivateData147 188 0 R/AIPDFPrivateData148 189 0 R/AIPDFPrivateData149 190 0 R/AIPDFPrivateData15 191 0 R/AIPDFPrivateData150 192 0 R/AIPDFPrivateData151 193 0 R/AIPDFPrivateData152 194 0 R/AIPDFPrivateData153 195 0 R/AIPDFPrivateData154 196 0 R/AIPDFPrivateData155 197 0 R/AIPDFPrivateData156 198 0 R/AIPDFPrivateData157 199 0 R/AIPDFPrivateData158 200 0 R/AIPDFPrivateData159 201 0 R/AIPDFPrivateData16 202 0 R/AIPDFPrivateData160 203 0 R/AIPDFPrivateData17 204 0 R/AIPDFPrivateData18 205 0 R/AIPDFPrivateData19 206 0 R/AIPDFPrivateData2 207 0 R/AIPDFPrivateData20 208 0 R/AIPDFPrivateData21 209 0 R/AIPDFPrivateData22 210 0 R/AIPDFPrivateData23 211 0 R/AIPDFPrivateData24 212 0 R/AIPDFPrivateData25 213 0 R/AIPDFPrivateData26 214 0 R/AIPDFPrivateData27 215 0 R/AIPDFPrivateData28 216 0 R/AIPDFPrivateData29 217 0 R/AIPDFPrivateData3 218 0 R/AIPDFPrivateData30 219 0 R/AIPDFPrivateData31 220 0 R/AIPDFPrivateData32 221 0 R/AIPDFPrivateData33 222 0 R/AIPDFPrivateData34 223 0 R/AIPDFPrivateData35 224 0 R/AIPDFPrivateData36 225 0 R/AIPDFPrivateData37 226 0 R/AIPDFPrivateData38 227 0 R/AIPDFPrivateData39 228 0 R/AIPDFPrivateData4 229 0 R/AIPDFPrivateData40 230 0 R/AIPDFPrivateData41 231 0 R/AIPDFPrivateData42 232 0 R/AIPDFPrivateData43 233 0 R/AIPDFPrivateData44 234 0 R/AIPDFPrivateData45 235 0 R/AIPDFPrivateData46 236 0 R/AIPDFPrivateData47 237 0 R/AIPDFPrivateData48 238 0 R/AIPDFPrivateData49 239 0 R/AIPDFPrivateData5 240 0 R/AIPDFPrivateData50 241 0 R/AIPDFPrivateData51 242 0 R/AIPDFPrivateData52 243 0 R/AIPDFPrivateData53 244 0 R/AIPDFPrivateData54 245 0 R/AIPDFPrivateData55 246 0 R/AIPDFPrivateData56 247 0 R/AIPDFPrivateData57 248 0 R/AIPDFPrivateData58 249 0 R/AIPDFPrivateData59 250 0 R/AIPDFPrivateData6 251 0 R/AIPDFPrivateData60 252 0 R/AIPDFPrivateData61 253 0 R/AIPDFPrivateData62 254 0 R/AIPDFPrivateData63 255 0 R/AIPDFPrivateData64 256 0 R/AIPDFPrivateData65 257 0 R/AIPDFPrivateData66 258 0 R/AIPDFPrivateData67 259 0 R/AIPDFPrivateData68 260 0 R/AIPDFPrivateData69 261 0 R/AIPDFPrivateData7 262 0 R/AIPDFPrivateData70 263 0 R/AIPDFPrivateData71 264 0 R/AIPDFPrivateData72 265 0 R/AIPDFPrivateData73 266 0 R/AIPDFPrivateData74 267 0 R/AIPDFPrivateData75 268 0 R/AIPDFPrivateData76 269 0 R/AIPDFPrivateData77 270 0 R/AIPDFPrivateData78 271 0 R/AIPDFPrivateData79 272 0 R/AIPDFPrivateData8 273 0 R/AIPDFPrivateData80 274 0 R/AIPDFPrivateData81 275 0 R/AIPDFPrivateData82 276 0 R/AIPDFPrivateData83 277 0 R/AIPDFPrivateData84 278 0 R/AIPDFPrivateData85 279 0 R/AIPDFPrivateData86 280 0 R/AIPDFPrivateData87 281 0 R/AIPDFPrivateData88 282 0 R/AIPDFPrivateData89 283 0 R/AIPDFPrivateData9 284 0 R/AIPDFPrivateData90 285 0 R/AIPDFPrivateData91 286 0 R/AIPDFPrivateData92 287 0 R/AIPDFPrivateData93 288 0 R/AIPDFPrivateData94 289 0 R/AIPDFPrivateData95 290 0 R/AIPDFPrivateData96 291 0 R/AIPDFPrivateData97 292 0 R/AIPDFPrivateData98 293 0 R/AIPDFPrivateData99 294 0 R/ContainerVersion 11/CreatorVersion 20/NumBlock 160/RoundtripVersion 20>> endobj 134 0 obj <</Length 1246>>stream
+%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 17.0 %%AI8_CreatorVersion: 20.1.0 %%For: (David Fran\615a) () %%Title: (Untitled-2) %%CreationDate: 31/10/2016 17:24 %%Canvassize: 16383 %%BoundingBox: -9 -621 1763 9 %%HiResBoundingBox: -9 -621 1762.47226464325 9 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 13.0 %AI12_BuildNumber: 174 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%CMYKProcessColor: 1 1 1 1 ([Registration]) %AI3_Cropmarks: 0 -612 792 0 %AI3_TemplateBox: 396.5 -306.5 396.5 -306.5 %AI3_TileBox: 0 -612 792 0 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 0 %AI9_ColorModel: 2 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI17_Begin_Content_if_version_gt:17 1 %AI9_OpenToView: -280.375731213439 535.367481625915 0.6667 2125 1336 18 0 0 93 55 0 0 0 1 1 0 1 1 0 0 %AI17_Alternate_Content %AI9_OpenToView: -280.375731213439 535.367481625915 0.6667 2125 1336 18 0 0 93 55 0 0 0 1 1 0 1 1 0 0 %AI17_End_Versioned_Content %AI5_OpenViewLayers: 7 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 135 0 obj <</Length 12281>>stream
+%%BoundingBox: -9 -621 1763 9 %%HiResBoundingBox: -9 -621 1762.47226464325 9 %AI7_Thumbnail: 128 48 8 %%BeginData: 12140 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45534D534D534D534D534D534D534D534D534D534D534D534D534D53 %4D534D534D534D534D534D534D534D534D534D534D534D534D534D534D53 %287EFD0AFF7D4D4D534D534D534D534D534D534D534D534D534D534D534D %534D534D534D534D534D534D534D534D534D534D534D534D534D534D534D %534D534D2853284D2853284D2853284D2853284D2853284D2853284D2853 %284D2853284D2853284D2853284D2853284D2853284D2853284D2853284D %284D77FD0AFF7E284D2853284D2853284D2853284D2853284D2853284D28 %53284D2853284D2853284D2853284D2853284D2853284D2853284D285328 %4D2853284D5328534D5328534D5328534D5328534D5328534D5328534D53 %28534D5328534D5328534D5328534D5328534D5328534D5328534D532853 %4D53287EFD0AFF77534D5328534D5328534D5328534D5328534D5328534D %5328534D5328534D5328534D5328534D5328534D5328534D5328534D5328 %534D5328534D284D284D284D2853284D2853284D2853284D2853284D2853 %284D2853284D2853284D2853284D2853284D2853284D2853284D2853284D %2853282953FD0AFF7E2853284D2853284D2853284D2853284D2853284D28 %53284D2853284D2853284D2853284D284D284D2853284D284D284D284D28 %4D284D284D2853534D534C5329774C534D534D534D534D534D534D534D53 %4D534D534D534D534D534C534D534D534D534D534D534D534D534D534D53 %4D534D53287EFD0AFF77534D5329534C5328534D534D534D534D534D534D %534D534D534D534D534D534D534D534D534D534D534D5328534D534D534D %534D5353534D534D284D28994C296F9A284D2853284D284D284D2853284D %2853284D2853284D2853284D2853284D2853284D2853284D2853284D2853 %284D2853284D77FD0AFF7E285393766F9A29534D534D4D2853284D285328 %53285328532853284D2853284D2853284D287E7D7E537E7D7E7D7E7D7D53 %A253A277A27E532853532977939A709A93537E7E777E537E53534D532853 %4D5328534D5328534D5328534D5328534D534C534D534D534D5328534D53 %28534D532853287EFD0AFF7D4D4CC299BB997EA87E7EA84D5328534D5328 %534D534D534D5328534D534D534D5328534D537EA87D5377A27EA27EA853 %53A27EA87E7E7E535328282976C193B599C14CA97E7E7D7EA8A84C4D284D %2853284D2853284D2853284D2853284D2853284D2853284D2853284D2853 %284D2853284D284D77FD0AFF7E28294CBB995228534C53284D284D284D28 %4D284D284D284D284D2853284D2853284D284D285328532853284D4D5328 %53285328534D53284D284D534D539AC1BBC1765353774D7E537753534D53 %28534D5328534D5328534D534C534D534D534D534D534D534C534D532853 %4D534D534D534D53287EFD0AFF7E77537E7D7E537E777E537E777E777E77 %7E777E777E777E777E777E777E777E777E777E777E537E777E777E777E53 %7E777E537E777E537E777E532853284D76C25229285328532853284D2853 %28532853284D28534D532853284D285328532853284D28534C5328534D53 %4C534D534C534D4D284D77FD0DFF7EA87EA87EA87EA87EA9A8A8A8A9A8A9 %A2FFA8A87EA87EA8A8A9A8A8A8A9A8A8A8A9A8FFA8A87EA8A2A9A8A8A2A9 %A8A9A8A9A8A8A2FFFFFF53285329534C534D537D7E7E77537E7E7E777E77 %777E7E7D7E777E535328534D5328534D5328534D534C534D534C534D5328 %534D534D534D53287EFD0DFF532853284D284D537E7EA97EA27EA2A87E7E %7E284D285377A8A2A97EA8A2A87EA97EA2A853284D297E7DA27E7E7DA2A8 %A97EA2A87E7EFFFF284D284D284D284D28A27D7E4D7D77A87E7E7D7753A8 %7D7E7D7E5353284D284D284D284D284D284D284D284D284D284D284D284D %284D284D282953FD0DFF7D7E7DA27D7E7EA8A2A87EA87EA87EA87ECB7E7E %7DA27EA87EA87EA87EA87EA8A2A87EFFA8A87EA87EA87EA8A2A87EA8A2A8 %7EA87EA9FFFF534D534D534D534D5353775353537753775377FD0753774D %534D534D534D534D534D534D534D534D534D534D534D534D534D534D5328 %7EFD0DFFFD057D52FFCFFFA8FFA8FFA8FFA8FFFFA87D7DCFFD0BFFA8FFFF %FFA9FD0AFFA87DA87EA9FFFFFFA8A9A8A9A8A9A8A9A8A8A2A9A8A87EA8A2 %A87EA9A8A8A8A9A8A8A8A9A8A8A2A9A8A8A2A9A2A8A8A9A8A9A8A9A8A9A8 %A9A8A8A2A9A8A8A2A9A8A9A8FD0DFFA852527D527D537D7D7D527D52527D %7D7DFD0FFF7D5253FFA87753A8FD08FF5377537777FD05FFA84C5353534D %5353777DA27EA87EA87EA27EA953534D5353534D777DA27EA27E7E7EA8A2 %A94D5353534D53537777A27EA87E7E7EA27DFD10FFA27DA27D7D7DA853A8 %7D7DA8FFFFFFCFFFFFA87DA8FD057DA8FD09FFCB7EA27EFD08FFA87D7E7D %CBFD05FF7E534D5353534D5353A27DA87EA27E7E7EA2A25353534D534D53 %537E7E7E7DA27D7E7EA87E534D5353534D53537E7DA27EA27DA27E7EA8FD %0FFFA87D527D527DFD05527D777D537D7DFFA852527D527D527D527D527D %5253527752FFFFCB7EA87DA8A2FFA87E7EA877A8A8FD07FFA87DA87DA87D %A8A2A87EA87D7EA8FFCBFFA9FF7DA87DFD07FFA9FFCBFFA8FFFFA8527D52 %7D5253537D525253524C7D537752FD10FFA8537D7DFD04A8CBA8A8A2A8A8 %A8A2CBFFFFFD06A852522752275252522752FFFFA87E7EA97EA2FFFF77A8 %7EA87EFD08FFFD04A87D52527D5252527D52FD047DA8FFA8525277A8FD06 %7D52FD047DFF7DFD042752275227522728272727522752A8FD0FFF7D7D52 %5353A87DA87DA87DA87DA87DA87DFF7D7D7DA8FD067DAEFFFFFFAEFD05FF %7EA27E7E7EFFA8A9A87E7EA9FD09FFFD04A8527D537D527D527D52527DA8 %A8FFA87D525252A87DA87DA87D7D7DA87DA8A8A8525252FD07FFA9FD18FF %A8A8A87DA852522752272827522752A8FFA8FFA8FFA852527D527D527D53 %7D527DFD05FFA8A8FFA2A9FFFFA2A27EA9FD07FFA8A853527753527D527D %FD05527D7DFFA8A27DA87D527DA87DA87DA87DA87DA87DA8A8FD087DFFFF %FFA8FFFFFFA8FD11FF7D7D7DA8A8FFCFFD0AA8FF7D7D7DA87D7D7DA87DA8 %7DA87DA87DA87DFFFFFFA8FF7EA9A8FFA8FFA8FFA9FFA8A27EA9FD06FF7D %527D5277527D52777DA87DA87D7DA8FFA8FFA8A8527D527D7DA87D7D7DA8 %7D7DA8A87D7D7DA87D7D527D7D7E7DA2FD047DA8FD11FFA8FD047DA87DA8 %7DA87DA87DA87DA8FFFFA8FFA8FFA8FFFFFF7DA87DA87DA87DA8FD09FFA9 %A8A9FD04FFA8A9A9FD04FFA8FD067DA87D7D7DFD06A8FFA8FD07527DA87D %A8A2A87DA87DFF7D27274C2752272727FD087DFD11FFA8A8527DA8A87DA8 %7DA87DA87DA87DA87DFFA8FFA9FFA8FFA8CBA87D7D7D777D7D7D77FD07FF %A87D7D7E77A2A8A8A8FD10FF7DA87D7D527D7D7DA8FF527D527D7D7D52A2 %FD087DA8FF7DFD07A8FD19FFA87D5352FD08A8CFFD04A8FF772853284D28 %53537E7EA27E7E7EA27E7EA8FFFFFFA8A87EA97EFFA9FFA9FFA8FFCBFD06 %FFA87E777E7D7E777E7EA87EA97EA87EA8A2A8A8A87D5252A85253FD09A8 %FF7D5227FD04527752522752275227522752A8FD11FF777D527D5253527D %527D527D525352FF7E77537E5377537E7EA87EA87EA27EA87EA9FFFFFFA8 %53777DFD05FF777EA2537EFD05FFA228534D532853537E7EA27E7E77A27E %7E53FFFFA8527D527D527D527D777D777D5253A8A8275252532752287D52 %7D5252527D525252FD10FF534D5377A27EA27EA27EA27EA87EA27EA2A8A8 %7DA87DA87DA87DA87D7D7DFD09FFA9CBA8CBFFFFA8A9A8A87EA877A9FD05 %FFA87E7DA87DA87EFD04A8A9A8A8A8A9A8A9A87D527D52A2AEFFA8FFA8FF %A8FFA8FFA8FFA87E7DA27EA27DA2A8FFA8FFCBFFA8FFA9FD11FF537E77A2 %7EA9A2A87EA9A2A87EA87EA87ECB7DFD0B527D7D527D7DFD05FFA2A9A8CB %A8FFA8CBA8FFA87DA8FD05FFCBFD047D777D52FFFFFFA8FFA8FFA8FD04FF %A87D7752525277527DFD06527DA94D534DFD04537E7E7E7D7E7E7E7DA277 %FD10FFA87D7DA2A87DFD0DFF7D5227FD055227524C5252522752FD04FFA8 %7EA8A2FD05A8A9FFFF7DA2FD05FFA87E2752274C2752FD052728272727A8 %FFA8A8A85227FD075277525252A8A87753775377537E7EA87EA87EA97EA8 %7E7EA8FD0FFFA8A87D5252527DA27D7D777D527D527D7DFFFD047D777D52 %7DA8FFA8FFA8FFA8FFA8FFFF7E53A8A8A97EA87DA27D7EA8FFA87E7EFD05 %FFA827522752525227525252275228522752A8FF7D7D7DA87DFFA8FD0BFF %A8A87DA8A8FD1CFFA8A8A8FD047D537D7DA8A8A87DA87EA8FFFFA852527D %5253527D527D7D7D527D77A8FFFF7EA2A2FD04FFA87EA8A8FD0AFFFD05A8 %A9A8A8A8FFA8A8A8A9A85227A8FD04FFA8FF7D52527D5252527D527D527D %A877525352FD077D77FD047DA8FD10FF7DA8527D7D7D52527DA87DA8A8A8 %A2A8A8FFFFFF7DA87DA87DA87D5227522752522752FD07FFA8A97DFD0DFF %A8FD047D527D7DFD05FFCFFD05FF7D7D52A8FFFFA8FFA8FFA8FFFFFFA8FF %FFFF527D5277527D7DA27DA87DA87DA87DA8A8FD11FFA87D537D7DA87DA8 %7D7D7DA87DA87DA8FD1AFF7EA8FD0DFFA8FF7D525277525352525253527D %525352FFFF7D4CFD09524C52272827A2A8A87DA87D7DFD04A87DA87DA87D %A87DA8FD10FF777D527D527DFD0BA8FFA8A8A2A9A8A9A8CBA8A9A8CBA8A9 %A8FFA8FF5377537E7DA27E7E7D7E7D7E7D7E7D7E77A8FD04FFA853785377 %53775353537E7EA27EA27D7E7EFF7DA87D7DA8FD07FFA8A87DA8A8FFA8FF %A8FFFFFF52FD067DA87D7DA2FD10FFA8527D527D5252527D527D527D527D %527DA97853787EA2A8FFCBFFA9FFA9FFA8FFA9FFFF7729777DA8A8A87DA8 %A8A8A2A87E7E7EA27EFD04FF7E4D28534D532853285353A27EA2FD047EA8 %A87D5227FD097D7E7D7DA8A87D5252527D527D7DA8A8FFA8A8A8FFA8FD11 %FF7D7D527DFD04527DFFA8A8A8FFA8A8A8FF537D53A27EA27EA87EA87EA8 %7EA2A2FFFFFFA8FFA9FFA8CBA8FFA8FFA9FFA8FFA8FFA8FD05FFA87DA852 %7DA8FFA8FFA8CBA8CBA8A9A8CBA8FFA8A8527D527D527D527D7D7D527D52 %537DFFFD057DA27D7D525228524C5252527DFD11FFA8FFFF7D527D52FD04 %7DA8537D777DA953537E7EA87EA27EA87E7E7EA2A8FFA9FD07FFA8FF7E7E %7DA8FD0BFF7D28527D52A8A8FFA8FFA8FFA8FFA8FFA8FFA87D7DA8FD06FF %A8A9FD05A8CBA8A8A2A87DA87DA8FFFF7D7D7DA87D7D7DA8FD10FF7D7D52 %A87D7D527D52A8A8FFA8FFA8FFA8A94D7E537E7EA87EA87DA87EA8A8FD05 %FF7DA87EA87E7EA8A8777E7D7EFFFF7EA2FD06FFA8277D7DA8FFFFA8FFCA %FFA8FFCAFFA8FFFFFFA8FFA8FFCBFFFFFFA8FD077DA8FF775328FD0452A8 %7DA87DA87DFD04A8FD11FFA87D537D527D7D7D52FD047DA87DA8A9FFA9FF %A9FFFFFFCBFFFFFFFD057EA87E7EFD04FFA8FFA9FFA8FFA8FFFFA9A2A8A8 %FD04FFA87DA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA87D7D7D527DFD0CA8 %FFA87D7D537D7D7D527D527D527D2827007DFD10FFA9FFFD06A8A2A87DA8 %7D7D7DA8A8FD0DFF7E7E7D7753A87EFFFFFFA9FD0BFFA8A8FD05FF527D7D %7D777D777D7DA87D7D52FD047DFFFFFF52525253FD07527D52537DFF7DA8 %7DCBA8A8FD08FFA8A8A8FD13FFA8A9A8A9A8A9A8A27D7E77787EA9A8CBA8 %A8A8CBA8FFFFFFA9FD05FFA8CBA8CBA2A9A8FFA9FFA8FFA8A97EA9A8A2A8 %FFA8FD04FF7DFD10277DA87D527D53A8A8FFA8FFA8FFA8FFA8FFA8FFA852 %275252522777527D527D777D7DA27DA8FD13FF7E4C53537D537E7EA27DA2 %7D7E535353A8A27E77A2A8FD09FF7D7753A8A8FFA8FFA87EA8FFCBA87D7E %7EFFA8A9FD04FFA827527DA87DA87DA87DA8A8A87DA87D7DA8FD06FFFD05 %7D527D7D7D527DA8FFFD08A8A1A8A8A8FD0452A8FD14FFA2777E7EA87EA8 %7EA27E7E77A8A8FD11FFA9FFFFA97EA27DA2A8FD05FFA8CB7EA8FFFFFFA2 %A9A8A87DCBA8CBA8FFA9FFA8FFA8FFA8FFA8A9A2A9A8A8A2A9A8A9A8A9A2 %A8A2A9A8A8A8A9A8A9A8A9A8A9A8A9A8A9A8A9A8A87DA27DA8A8A9A8FD0B %FFA2A8A8CBA8A97EA8A2A87EA27E7E537E77A27ECBA8A8A8A9A8A8A2A9A8 %A8A2A9A8A8A2A9A8A9A8A97EA87D7E7DA9A8A9A8A8A8A9A87E7EA9A8A84D %287EA87E7EA27D7E7DA877777EA27EA27EA2774D2853284D2853284D2853 %284D2853284D2853284D2853284D2853284D2853295328537753287EFD0A %FF774D28A2777E7E7E777E7EA853A27EA27DA87E7E4D4D284D2853284D28 %53284D2853284D2853284D28534D532853284D2853284D28534D4D285328 %284D2853284D2853284D285328532853284D2853284D2853284D2853284D %2853284D2853284D2853284D2853284D2853284D2853284D2853282953FD %0AFF7E2853284D2853284D2853284D285328532853284D2853284D285328 %4D2853284D2853284D2853284D2853284D2853284D2853284D2853284D28 %53534D534D534D534D534D534D5328534D534D534D534D534D534D534D53 %4D534D534D534D534D534D534D534D534D534D534D534D534D534D53287E %FD0AFF77534D534D534D534D534D534D534D5328534D534D534D534D534D %534D534D534D534D534D534D534D534D534D534D534D534D534D534D534D %534D284D2853284D2853284D2853284D2853284D2853284D2853284D2853 %284D2853284D2853284D2853284D2853284D2853284D2853284D2853284D %77FD0AFF7E2853284D2853284D2853284D2853284D2853284D2853284D28 %53284D2853284D2853284D2853284D2853284D2853284D2853284D285328 %4D285353284D2853284D2853284D2853284D2853284D2853284D2853284D %2853284D2853284D2853284D2853284D2853284D2853284D2853284D2853 %287EFD0AFF7729284D284D284D284D284D284D284D284D284D284D284D28 %4D284D284D284D284D284D284D284D284D284D284D284D284D284D284D28 %4D284D28537E777E537E777E537E777E537E777E537E777E537E777E537E %777E537E777E537E777E537E777E537E777E537E777E537E777E537E777E %537E7EFD0AFFA87D7E7DA27E7E7DA27E7E7DA27E7E7DA27E7E7DA27E7E7D %A27E7E7DA27E7E7DA27E7E7DA27E7E7DA27E7E7DA27E7E7DA27E7E7DA27E %7E7DA27E7E %%EndData endstream endobj 136 0 obj <</Filter[/FlateDecode]/Length 29427>>stream
+H‰¬—KfÅ …W=ÜI$’¢ì²ë‘Œx¬ Y@„Z
+b
+5…ÂÓ]¨èÇ„Z}.ìxã„ŠoÚ$ã"†ªå1# °»¹
+@›ô ç¨Ñz1U°ïtµr:öÇÄÄ¢ø¡ì'™g„ð™šuí©D½|œcTvÐ߸ÄßM·ÒŸ‹áJ§Ú›†ïp ••^^o¦8äY‡LPÔa’1xôyh«‰7õ5Ã+@̾.€ž€”Ž°HC™ÿ'¾úú×ßýøáã¿üüý¯ÿyþÂO_ü™)ËÝ¿|¾úûÇ_üù‡ç‹o¾ùúÇýô·_>~/ì—ÏŸ@þ•?ügÏwr–¦MòyÌçáƾhjúô0ÇžŸ*aÖé9†kQó9«ÑI/±© càÞ  ijŠŽEõÞ
+Õú0¶4qØŸo¡9„yê¨Ï=Ü$쥿¾ÆÔV
+Hɺ
+ôô¶móz«"Ë[ŠcÕ«³¾/Š-T.«Ù8Ö3õuÅ+š—ñÎEÐÀB0x×—€£…€Žß=Š¤…P죥e9¼Ï,+”¿ÇízÂnQvÙ%y¯¡åH{ ñ,Hô[ú°/ã t½Çm!25”à ¢GÉ…”\ K€µ™6Û¯sóÿ‘oÇ™ôÁ2>d%Lö‡b6îí"Ô“¾j …"±ÊE°…ÚË„šP£?B@®C(+®§Õd‡l
+@Þ–
+1 ) Kë"Ä—µ›ã-»‰µ{BCkQ¬
+2%wÊÆ”1Š{/’Ô”(ÿ<d‹’ ´iä"CAOL1¢g}* †‘BG»ùªCŠÃÔÑÌì|¸ÊBdæýÐd‰öS©ùHÀ‚(?î‚_Nsú¡2†œeïâ'ëKëT&ÚâAÂ=”d`LÍ ;ÜÓA%"Ö-3JP2ÔW6²¸^šavût‰Eh€b=mjßQ â"iKËW6v¾tLfÒ^ˆÎ¿ÊÙb¶t¼ÕpK>{ÑšHn}Ò0D SC UA. Bd TjL†Ô°_DP !è?Š)„1dÝ4w!BêÞŠ ](Ü/Š…-‘í²cÛ©Ž'U¸ˆÕ•=©Î
+‡úQT¨‰q´ßì1²çÚJˆ‰ö\sÝž¥Žê$¢‰iƒ&Õ»¥Ø¥Ç!³V!h-cB¼[@ÜŒ·eÑÔ°
+ÓDÖÒs½G˜ ‚!ÅÉ.¤hIÂñƒÂT¾1MdúÑ2¡f’ ) ïlŠJ§TLzX†¤³wØLO¡¤žjèeºœeÈSC±B”7?…ŽŠ¢§OrûýX£Êïø¨ƒ(ºœ.›…ÀÑBC]ˆ±Æg³·eT ¿v¥1oð|QtåWt•ìjSž#àκvS¹Å<ó2ù|‘U0â†ÏÒª'©¸‡Öª§(ûsG`&ýFöuÏïùò!WW “!i¼z8Ò%z8ÒTùª,øÇÐñ<Í‚Wržb‡„›S’ÔYTõ€€³õeþ» 6·>%[b2 ™ì¸ÅÝ¿Þt rcIB!y](6–—­(! A^HKbB/
+F’/ A+Jç ú)#ôù&† P¡=T¨òß•’ÛŠlée"_¸C¾ø=×3¦Î%k*"ԉΓ&yï?B7ê_PȮԉýªV…GR)+‰‡s—ö«ÈøðtÂØô
+CÊ3õ«wÄk^DrT0=5´ÿ+Xx[ï w&%'±d—]O9ÌšÒLƒÑM¡›)&CÿÇHà+zx-ÔíSœ§àõTõ9ƒ-Ë/çEH`²*¡àÁ>Ba\Äœ«èªÏˆÄ…
+{¡R¤¤WïɲƒäíB «ÎuôTÉõˆY±#ˆ>¯/lyµæó½î“r¤ ô4bñ}ÈQÀ¡‘Fq›Bâ1Éb£û& DêZ?¦¦•áì]F•8=;§I’vú³_±Ãú˜¨†! Š¹%z|p¬•²§Çy)’ØDB
+"DÒÏÿKw™åXrÃ@ð*}‚v©.æó;"UÕ ûk0]|ÚHfÅÕæŒyn}2àÜ ˜Øâ-ó,#…QZÁ2Ý@åµ(Å!Ïìx(&Äb#iŽ( âÎTÎ(RòòNµöÕu¬‰gÜÂ(÷ë+Wâ.Í–R»hÍ|B~§ßwÁ³ãî5†…!`ÔîHK¿­{ÝÜò„)©Îzu‚c1²‚¼¢Dâ‡ÛVyKi‚Õ,c•¥q¾ÒSÙ¨ Gp\ÛßuÔS¢@ü¹ X5\ÐíL²Ùx%ZÈ«ÙtÎAýè:‰VrÌEö–c.Çy-„!€66*çöm”ò:Ö&© RË``—ðoú‘),ÀÖš”ÃfF0 ¼(QãºÞF䶭ÑS¤Ö²QR=·ÎÄp*\ŒcZÆuÒcð°8Á¤·C4<ìÒ„8®©–ÆÖï×b’âC=N¶Ã.¬+!(°~¨~æ2seÒuD@¤,L‚À
+vj8F8õ‘Ïs>Ãœ+pMT—m% …Y.wâ5óáPµ;ÿ_3\…ða8ƒmO‹ïZ(7)vÊ}õ}'˜8ÝÂ}©W®–ðDL²è ÷LuKSý>ÔãÈßÎÉÏ-¶ïô`PÉÜÊðÁx`Г"¦@3·ŽB‘™ç½q²†ì2ÔÂÕNG´…¾y¦ôO};Ô˜gÊ7"´¹ÑÀ/[›á
+ZÇ\·ÊMÀÚgÙßp€;_5 aÚ«j½.²†p˜ìDvjŠØvg
+ÀV?•J<z7•Ñí¤5£4"ɪÔR\Ýë*ÐçíßuiÙ±Cadx>ñ8®”ûNZ«LF¢†fLBÁ´m”9$ê‘äY‰ˆ®}CYZ@¡Å´:òoŸq‹íN—JzU³òȨ&ªƒ²øË–¢•NgD“L÷ÐÎü*f8K|Œ?cBÁCù¸ÜÑâBTkW4E9à%3ÓqÒÌI ñW{¹8$„óQ?y ýùvÙ¶Óɪ ‚ZˆÝ8«C~Åú!+zµ@ѱïú¬±N.2”`îNR¹ÑKÕã2‡Ý\‡‹PÇ>7!t£E:°(Îí ¨±cû=‰¶sŠóŒüƒ¾`Xv7„—eÎËFKcŸ‚rˆ¸¡ºXcÉ‘«Ä’µqÊ¥]!*4 ¦Êû¸Òžë篬£»ëÆÀ¬|A—$›
+ÐÃ
+¿Â™šÄLñ<ƒ÷K4^6b™ PŒD‘¸Èöl:É©3PÒÃø¢oØ]aX×ÎzÃ44œ¢Ã©(¸Ìò؇ù¡º¿Àvl“äq3Ù³Òê)ã>ƒ®<‚qj" ‹“`pÁ:Æ•#¬‹Üà¦ÕÙÇ|®A(ÀS¿óʵGx×X™ëŒG.gLz¢¢¡^.?¹4,Â3ÌçRGE‘úb¸QGç1±:žÄˈí“qz‚}ÎîÄ ”øˆ™a …×ßîNÌØ(Q Ü^7ŠšLÚ†”AD¦Y"€•z#ògLÉ#RG¤6^#ØßÄÔQ[ôAãXŸ(˜ÿñ ('oYpÌé£R%–Òû8ª £ö 5ôïC°ð"Üî#N£:/a£ÖH”àKÔd^hY'(Ç5xº™ñ׈±Rþáý0Í$üoƒÒîî(uÍG*ó˜âAïÎS‡ÑÆ €o0ßõûþ–ux<Õ¦¸-™õG¾†ÿ «[`(eãÅR“|FqºsÀæ9?Ťž+A=8Éž­iÏ<Çè£Ì´_zÕPìÖ?âhû°˜zcÈÚ'R8gê}Qt”ûÒ+vNšM±ÛCÇ¡2tk®O/ì»Óœ»¨
+*7éâWayÈX܈ª¿´yxc ÷}¹‚a48œX«º=xˆ¹iö ¬täD¨ ¹ “²‘ôxKvª¶l‘S[lHXÛdÇ—WRx«TN n“îUº’ÂF™ùMU .6KŸJrejDjÐqFŠãëNXÎ&¢Úno–À¥q¿n÷qLû|ŒEB?/òž$Š‹×Ù¤iúvÈ)Òç:1õ×1üˆefƒ­ÔŽúæ˜áoÚ~ ¸Vªú ˜ì–E|)î2Æïú×X2®Da)35Òy:#¦UØ~J¿ÀˆZ"°b¯ˆÊ¼;ñ‡–5*u}w̦­K!-HZÇ¢¿ÃVäÄQmëŽÆšB Za)VD æž…á¼bW·Íí5Þ#„ÁÖ5ñ™²›2ç’÷iè} HÙwå]†b–ìL4ˆ+íÀΡ0kü¤Qeï•èðâ¨Rñ`.qTnÔZP;RLWîJ®Õ2ÒnÚ…ÃftŒÃ]ýÃè¿Žý8I8!‘\ÌŒÁ!ËŒ89JÈ9c¸™×-jw0Ë¡‚zÑ—ÿ¨øG'{xQï©Û UuBU>âP¨3bw5ddÀrº-ëExd­÷äÄi”9Œò—ô!Àü·;¯‰&*>‡¥ùuýbªN6t#ºŠ›êzÕTt-³O3`Z×›úL @)A>¸Q7ÃGŸY¦QåD½ÁÛYmkµu7ê1NÄYí5 ¢Ö7 ƒ…ºw—8 ’ÒÕ ÖÍ* !ÂÁ²ßˆ°äšˆ{Ö¥c&âBguV»;˜¢6ZÒC0ÏPîÂÐòI„óÛ¶b€Ý®ÄRðþôÕnÿ
+ÇA¡æ-0òÁÍR‰~=ÒØø]Ût9ÙMeEÆÑ~´Û”ã(tùá„Œ¹
+ «…
+:v“êžNj_’†,·ÒsM½ûDH§Í‡Ò¨ áÁöå‘mûÈIðU¾#l¡ø;ïÆ),²òuþ’]à^îè„@ÑâÜÅié¾|…¢CL“äf‡BWŽ¶…qê|WúœxÏB@û‘4ç#~Ê3Ñå%3@÷ X›vgÞ®[¯½ ©#SGPÜTC Sb"“„t f_Ó(aÙ´i¥… pC‚hÓI¯%vŽmÓ@ -uí`œàÊPÃ=b„‰ñ¦÷ ßýì˜ “WÓ¤bòb®^gK1Ørãø±‡¯…ð
+uç’ÓæLšq·MÈbçb•6Yf¨”Z#tâ Ï &=X†!Úá|Pm]?”î.êLÓtéwä; ¬,o¥•åÃóæ2T9ù>%qê9O«M{­ê;b|ícúNNDŽ“»NÖƒ‚x€}§Ù‹î âeÝJâ²¼Ë\ÜÅÝ£s|¤§(㈉9Æ šN#võp-¤§ç¿göbUX§’AG|ÍçÉhNSC… q/xìøší>T{>[A´M¬Aùõ8îT´|³§5¢Â%¦¾aÌ‚˜Kô…;À
+îø.ú3÷¼ýƒŸ/ß¼ÿðÍ»·Þýø÷ï~ü>û=M ݵöùãË¿|xÿî‡>>ûê«7oßþôýŸüð­ØÏ¿ùG~øÿ‹¿þF •Ç›ïøë§ÿþó‹ýïoòçþï_ùw½ûÛÏaG‚ŸÓÉ) =ôn´džÍCßߘ͠§zô”…¡)y.€Sûêƒ#«wy0vX"|®gØÓ,ÙòP“?<(6™Hä Ñ0ô‰·ßŽ¦VѪqN_£m[ª•Èic¬'|Ì‚Õ¼™@q«ME«™qPíÒ Lw"F ¢NUÓ~ièð½ÆQ÷-€¢ð]rH»>n„a-‚ð+¤Ù¤««g1_X+˺t¹ö‘V-z€"o„ž]Dy
+ôàý:-»ÍL¤1 ò⇖›\}3Ò=¶©&[aúŒf™4ͳ°Î¶wו¨ä¥Ò²8/ëq·×1"[=¶‰×S, m;u2ÀpW¸ŒÙî8lÇHøƒ¯ø€Ês{'½BírâiœPíÖ¹ÕNmb™z÷g­ÛmÌ´†~:)ÊŒò‘©®8ØÙ² ¨ÿ([FKÇg}£îšÂ™©:avg2.D³² !U¯iR¬¯‚£lúqƒSònι7Ø'™Pñ¸ï·+8‡Y7éÏ
+S=ì»lm¥ÑÖºó¡ÔÃ2 ÷N2ÃÐkÅ ·¡m‰uÖ-KÈ@§¡æµúô–âû+Q·s‡/´»bÆ“Ö gðVÅxrJ"I©ô–¦þâH5› »wzš‘øË AèN$˜Þ?íçhNÌ
+/‰ÔTÓ«Áóãƒ}„BÑêÔpÁó[œ¿77\g|ßñbhÕq´DËúÅî[%ÌÖ{ÐòcîÃ9kÍCÀœ‰yÑ81u:|=û‚`J4w´¼® „9NýÞ‚ ,dñ$Q4·Vmèýƒš®(
+¬§"4Á>"lõh5ëÔk® Îû£ëÕöv‹"mº’AŠr¾Àæ³3”´Ž]¢Sgýú®d¤á{Ý_¢–3˜ÁhÕC†÷[5ªíUmÆÎÚA×K‚·Îó‰è7â¸7@×Õ¤é5J1tj&§o®¹§Š¾Âh秞ª3_¡ðnšCh¨c¹¦ÅÂg˜AWˆjiŒÓ™GÚ„<¦oŤäÁ×wÇ.Y‡¡jÞ¬²
+rÅ}—çäø°Ö|†ëõøVàÊ·ê9²áå€ ¸òVTÀå˜& ØýFt×a‡wgS‹Ùä¤Ç«°Ü1ÂÂXÌ=CjŽ³kÛ:~9¥ÜÇû 3ÓA ,à/aI(‰à@ ŸZ'¹°;TÊc¥½¹ëÜ7W<-SÉÁJ©{apâ^Æu °(Ô¡7Ñüœ5îk¼-ª§l{„"gLënö_sa¿j9ù*¯~ý¿N±Æ6H]ÿ¢öK*ª ÂòDÓ¾6?ü‹ßGlÍ Çº
+1”º’èh2²ÚdØqâBìøI¼n#CÑ/Í;zÔ²¨ê.¢¨9ð2ͯé†@`†(&¡f©“¬ âÜAÀ¹ÇÞ yAµ÷Nd…&¡íw¢‡‹¨ãPDÁÍ> º)IÝó™Ë¯£1=(¡‹è½ÝØVŒ·¹ƒf‚£¬,ÝvÌíÊð\î5›%Cåâ'ÔŽ*G/s¥äÍ àAànq̺a\ïÇ(
+ Õ2Ÿݹ寰îõ9žNƇįZ&?í4È=±m y, ÁpŠ ËÚ½TÜõ0Nmz{BWšõ(8XGZyâBé&{žQ«þ×T|ýN"žî%É“õáÀõ *¼ôX3®‚Oö ",­!
+K<‚²(¨’vqŒÄ‰«n" D-%;áu6
+jã4Å2”ai_{¬¼
+¾áŒÓ°%ÂiH+ab¤Fcs/ê ' O¬êù»‰2P4} š!­ç:3^µ˜š
+P]–6Äl’wþ=Vt]ÿH=ó÷K{ÕG¦¶ö‡¾þ³PЃ­ðjhÖx˜·ô‰F5]ññ,ŒˆtÏÿ1ê­8:Õï¼â
+ŠÂ³pc‚éAëјž¶ø±Ñ ´ãÁU1’‰ËÀB[z|k~"Ÿ:]ô7âΕgЕ¯öúþi£-ËR5^¬~YoÕеèC1‚<®"8H.p ~mï4ýTÁéÀCO—ò‘Éæ×K¥ ¥+˜PõÌŸ;­ $¹°Ÿè¼?²Kqô¶~8r7rðÜlëñŠ9°/åq
+Y¢h¿À5óô'µÄRF ²eýÁÊ5ÒEq´ïw§ûŠwYO¦œf…3Ú„†Zjb4Ý=¦V7ðéæÕY߈Ü
+¦ú6µqeƒìø
+ëˤGÅéö<
+%n"L÷q,†C IJã ä~®2ZÇ b$óm0Ô^NÏ<q`þpÂØqÓ—F‘¢£A©üä Þ­,9óóI:om{B‹ÀžF“y×Bb$f½÷œ@(Â×áNƒ‰yh8ŒÉƒ¢¼¿­G´µùóg!Pæ€AR0K¨s»×8qYÇv¿ë ;œnæJs'Ÿ/À¿¶ÒAÂÑ­0DV/îd9¹”m‰o¡º½ÔÐe\¯À`; k˜Ù8­†ðAÝ^ˆ¸“c=:ûq?_Ö‘'œ(Zé;gy ã,#7¾ ¾Êæ …
+ŠêE€SEp¨S{âÑX‡Þ¡“Ù‰È
+?×ïNH._2¦w9Y¸G¯‡š)#ú0äº-±TZ §l9ž¬»7ÿ_Ö˥Ǯ«ˆÂåí®Ï~ï#›  JaF–e;È‚¸-Óøßó}µÏm'}/3’Øé>UûUµVA:ÑžêÚ“¶ê±ÁÀL¤ ÛÖ~&èJ~ðb«·<i±âf ™ôæ8©ïãðrÜ„»
+reÊBiX@AˆPÞ-Èl6;ìÚ(;fŸ%kó‰šg‚Èÿ¼ÑÍc=©4e6ƺîÉ
+iØmÀsœÝ€cá¾¢ª%ªVÕãP¼$rß
+ϏR
+‚=Ó1\ïö[Ô:¨q$8ÆPš›–+içÝ›ñeòñúë{l–Â=§Î0¸?¬`\*ÁÊ´_LtmŒíÖ½’0îÕ¸{ä×Ò „a æ.L`Hp„‘fÆ ²Ñtpq:ÀC-šd<ö–Õ—G_µ\ÞÐW}À ¤¤ÞØÇÌ &EÞ%-i„¦û-ôµÀ‹ ‚7¾ЯœŠ´†zÚ<©ƒ"*©O…eCþÃ>èAMgˆ·5I¼iîŠÈ D_“!(b´ÇÃs ¥c„„×ô
+¹ZÔà Êu¶eP󾤞ê/âÑ@éÒ +êÌ^F>NB
+Ä&–1`ò³ÁœŒ,|T@èøªTœÈD‹âJó;PÝB+4 ™æY猥‘pÝƨÖ!«©‘êjzQ¬„[å|3wÚhr^Ë|eO„ÇXd~EŠSc
+X© R×û9J‚÷çÉ®1²^¾ó%0€]&š‹‚„÷Á¤• ç1€¦¦åƒ2ԚϘÔ:¨x3{Ax†£¬<å÷<]Í“Y ÙËÌr¢X·´—Ǿ<ºi{Ì¡Km>ö“Z(åJY¥ŽFKç-¹çÅ+ý˜,X&Cæ<ú¡ØÕB}1Lfõ·¡–ììggZdgâ늣7ïc3´Èrf=‡#«¦°Ë¾Hõ# 8Ä–
+I®CbÀhDx†mx
+QË@ˆFe´ƒˆ¢ÃÛ‚MjŒŠe¬¢X
+®ó(Aáêpm·ÓºH8ŒüàØ‹±KÚø5 jv \­…f Ã1¶zœå—ŠTÁ•Á OfÞ\®5Ú!g3 8¬Ík­±¢ÓN±J©ÌCàЙUY(í'ÙY8­Ýp@æNâÃ<ÞM:ÇWÙxìÂGÀ +u!Ûäå<!Q7Å6[°ŠmÁ<å.­ œ5º‹Ó¶CBVg^{ÆEÁœÃH±m;RN×"…ÑžX ´p¦©q@=ïFDf¸Hy¯,£ÃHû»ÎˆÏ Ÿ±y/»\E @,™H¹Åe3emÆüæªÔÅ@†VrÒ€rSjZW;X­Æ|ÁÈÒ”<™ è`.e•ÈÆ&eQˆAèøuèQ¥ëh´ÕÀ7$R]û…ÉÆ—@ΛÔÉ4 ö•Â˜ ߈’UAݹ)tiNgºäú{Ü@¶®ÖOµx$=ƒ¡ç.8¬ø&4N2´T>j[[²{¦?lEãnä5@xÚj+òW6OQ)å± ÌŒ¡mí²°… ­¸iC°Hå*˜Ã
+Ö?åÚÄ
+ÚUä$ÖxfÑ ûD¡ØF‘°ÚœÁH D‡?˜GÑý äÆý}f¡ pÀü‚Çg`Ì
+ °
+¹Ø;Èœov.nN‹! Ö¤)¹îüÐ#Èn¤ì¢üúù{”u Êì1ª
+uµ{ÝfEÃ/ø´hSÈÂæDø¹bUàu2@÷ž1öEÒ‰„¶'Æ|ù;â¼×‡?]ž©yP*qö‰¯Y,öòEŽxÖÍxdy
+taù<ô|¼gžM†Ô¸!+U•ÆŒ‡™ ƒò™È ™° Ciø¹Î£º“ÞiøõÛ1ÞÝÞd˜Ôújƒæ:üàigÌĹœ¼„mÖaÜ8†MÈ€Ò¶‘OUßaTxÁ4*” ‘¡H›Î m"… w‚ãg õ™5šËDû`ËêüÌòuK¾?¦•>‡\E Âm¶/<φ¼¹(ðÝ\ÿÈÁ»¡w%ÐhФ úï*»ÏÒ ¦'~Û'>a[©à7Ft~fDE€qk„C´¾¬Ã—€mIw«ašú·
+hÖÐ=Üæä-
+ÄKè¨J"ÄéΉ£{`.­Ã :)"`vï˜S-œ}ddW¡Äwûòù£Ó\MÔjM©<:(‚Tü¾%À*Ž:}¾k}­ÿ‘”̆‰Ã¡^ž+ &ãüxNžª¦Þ
+,je’Ì_óÙí3#ïl§ÁÊzù’ÅTCCP
+²ílÛ€  B‡û™qÐHÅ×)¤½^çg–¸@ÁÐ2¦
+«'¦bûvÊ'¿è!úVì¦;=ª¦@úÀk$k\Ɇ1[ûòù„°íãx„ùùsø›vì®P°¹üG°¼cè[TK
+T椢òj¡eCeÍ«²Žœ;5€>Š¶ )èF.ÿ‚-c¬å|¶ UVÀ矣M»cï>Md s"ƒ¿ö [ïôžöÏ{'4¹Ô
+lhoÛ„uGïü­"2ƒ8‘±ETwY##~p ®"2žù*¥R'j|Ð1zLo¾AeÄ>§‡ë±±CƒÏÇõ
+]€Aéx`€€ZŸ©#F\à—Ïg 6±6Jꃒdçª)šõ[ÆN¥@—ÏyÅgÖDH¡TŽ`ÀCÙ2®1Â'òÜ3*x1V°¢F2–ä^qý%ÞË•a¢r“Ž§À_1b±ÌÔ~
+iJ–5E_‹Ï8öyyÌ*„¿ßD¼%»ïQÍF1z_à"çÊh8T·éN@¸ìÀ)&'“ƒ£
+ï€5ÁÕµ{§BÉx¸¡‘èG),M F¡ñ9Liû5ð?gaM©'ùk,ÇdÃ5Â:B°š)Çž×ÿËVϸ+Îßæç7U
+¹£hN°Öqt>㨟ԜÔá½Z]ËÕ”îôÏæ„Äu
+‡@T‰¬ì%„]g,Âò." ÕAãGDÊg.™Œà~ùlïí×6½›ãRAç09¦“œÍrzsW –:NòûJø8u Áîe+•»==¬iCŒù ä’€-‘•!vL üÁ ÀQqå÷
+äÞXd`ãü²QF©ˆZ3µˆ:år”ÖÎN®áNµžˆF•ÄÙúŒ#T¹>rŽ©ˆ¥úÊ{(¥IÑè:4!¨ÐOí³µ„Aeú &jqQôwJ§Dü^h–­TBµi]rÈXn̺¼¾½†XApªV gŽ7ã}¿C±Zñ9f r+GÛiyì¯ïp“êF
+ì<äy¶†HäL¸Zd˜«!+¥n
+¼/uÃìRòf¼À`(«¶gÄy"sÁÂÆ¡oÖñQoñLY¤B×h8
+fÑ6ed6O…#½‰P"¸iŠ=¸¯ô=Š2A ‚ÒBÔÀZÙ«SÃ{uµD¶¸ (­„¥!óñã–CT@ ýå<£ð„£8;Gê Ó9Š¤vOªÈCgç B›’]:ïE$E.±6TÈ‚e‰Èà]ª"c‡ýñBŠ6D
+ž,}®¾7µRîïçPê\Hh†Íóô]L¦'jëä±<‚§÷N>Œb«Qnî ÔÆDDïžeQè3"ÐÑO1³SÈÖ ì#‚RIFðò_;ÏCDÁ}]˜ó}b˜Î³ ¿jå-à°4LÄñY§PÏÓË:Þöâ¾³×”ê
+‘¢4(îŒ6î<KJæ·Xûa­ž¼¨`o¯óŒZ€æòÄdœ½xï­vçzÔ:ƒ¾i´qh^´†o|(¨ߣº(ç”Ëclñ+°Îw&cŽ-öpän²ã°†·7\ö„0+a]8PÂÞáXeQÃYØ;ìÔ5‘½ÐWÎ!g]…àFn Ñ°ˆ(o(ûŽ~ô„utžnHÉÔØc L]Jês°£ Å@Û E.¥äüÓ
+”ZŠ ~xF<X%vzDqt˜Ì‡ëÑ.-$
+fP Ea~tMÆ\¹D¼oA “ïµHÿZ.¢Pu$¼ªHÈŠä#<Î;kxQùH…z„ÂœôÏTÆ:üÛ•0ó1Š¼È3ŠM@´P7!y¤A„$éjRb^lF$­DZUËÅ=‡»44ìõG¬³>]¬Ãi@"ÖÁîÑ,*£_œý9Y¦`r …º•—«òòë*”‰ŠòûwsúáæœÖ$]õ9™g·6°ÉýjˆÌéEÉEW×=#0¨·¢5éíÄg”¼Âdm¨7ìm[ã‚ähæ~y2µ'+@\¾èš€~p%8N–!x=Ù°eä‡($ÙÒMÀÛäŸu 'mkÓ˜ …Xœ|íÄa´|œ A¹Js2ÑW›š-·uú7ÿñ;ÿ¤ë—ùóãç_}¡ rºRüùçŸwëC)àÞ³‚mI»@/Û¨ÿ¹£xäa}:"j.û¼KÔ=ÚãYb9"†6ƒˆªM5â´@é…Ö8¦ˆ»[oŠª‰8fîÅÊŽ†b{šùFŸ Â$ihSiž¦í± I;9fë¼O¡¸%Çhšœw­&=ÌȱèFÉÇïø¨b [iÇDñPégù2gDŒj/„ÍâQÃfý´>*¤ÅóÞQ"ŒQ!œ‰ ö«s’¸ ÁFöÔK‰T›Tr/ õz”f.ÅÓ&¨,›|È G–¸¨.!H¤q@9ÎtóU P‡6Çé~èéÄ9”#ùèPUO@ýŒþ¥š»šLÓ²Q6zdsB/Ò¶´ÑÞ
+ÞØßG¬OI=ˆ„< h—²ÒÅÂ4ÞÇqÑ3”
+9F££çz|—‹Ñ
+)nWõµ™ñO4‘O²;eCFåÝPðÌ7ÇòJÊCËÚ¾Ô<]5è‘Ïi'6ª™
+²4‹òe³b„îd3(Vº‹Œêóa¦èa€”i®oœâ¢,A³Ö!+dY¸¹{Y‹ÓðÜÏŒsë "Ú’ì“"ÔŒq–OÛ‹f]RÓ5(b ½ËSŽyçF¥5N9vT¥•½×7¯}ÎjY'°²üÿ:r¶>ÃÍxI¢;Æ`ÃX^°ºÂ[pœ:JË
+6 ¥Tï:bš 6dð¿Ôd/•÷ÍwŸ¿üøñן~ûùóŸoùé«2WU ùúùæ§/Ÿ?þöËóÕ÷ß÷áÃï¿þûÓ—ŸÍýúù™ÿâ¿„?^¹Tæ«pi4/of3™ûˆR˜Ó2V@¨"tuJP^ôyö2æ¹³-‚Û4"@/
+Oua– Ö
+KѺ7¸8ÊFò•_³p‡>@–„,ì²859Õi"I:32
+oˆwEG?9#?üc9”ÀZXô]†TE¼Ny=±ˆ RÄà˜ Õ>_Á/ñPÅïÍâB5€ãŒÕèêYÃIÌV„`µ"F¬†çP5Hèë؆D "Qà6ÊQËe¾™TbàåòKÀúÑ¡‚[©ÖÊtÔÚèÕ h³ßQÂõ9Øa5â«É ç„xÆ*È'…
+ãÙÍŠ!dd:2&½fLgÅPŸ ÍnÒó#`vÅuìà[6
+e!ÅÚR¨&kì64¢‘tÚ~p ²}ŸjwŒôò¢@ÇŒÔt~fíg£´Ñ½øV…éOÕ@X†ÕÐû\$ÛyÕñl•ªô›N=,H•2ÀÞ¼Û¼š}ißéOã8C–ÚYq
+ò ÂÂDXƒaÝûx9q@«5ß•
+÷u”c¤Äl²8AÝQš¬˜t§ÏhÅÈ
+ê#Ò*‚¹'ÉŽ^cäu¤@2±˜orÒkÝ`À³ º É7ûä]¬ôT´ªïg«2àj†ÇBw!sU±@ ð -n´ X Ó¡Ð}©‚×rŸeL”ë#Çsr˜z&¶Ý+ôc ÍD¤9lJ´•|dµ|¨Ë*6Yúl­ðqk°ûÙAJ9+•{X>Zg9
+\=0äæåAPTAö¡¢PØ€õ‹x_ø0³8TòîuÁmĪíêrªdÑm¹K‚; 5íx+:þs8º[" €´835﯒u@+‚}•HÖëø·â…d|õÝþ}~¯×ño•ã7EO”Õó÷Øø6*©Mjè²ø¸[
+ñêW4~;e à ͞
+~1\L@M
+€Ÿ§ ®¯HT:ÜŒP¬
+BO3Ü@îÒžñê
+ß`bc‡eÂfí.¬Ëasô5*‡yHÝqšX˜zpo„$(íÆhvKü¸:ˆßýÑ‹×Ŭí»ØdMÄã‚OÜü•ÇéoN[”å®æ{P®"©#]h¡¬¿R1µg©¶d7:¥É{IÈãµJàxÝú=²H-¾T1ˆg¯ >˱£ϽwK²ÀË)Kìùÿ r•­xåæàî!rF,wy™
+ žÖÀ“ð;‚ª‚ø¨}”ãå·–kú7fÈŠ©´‡Ôg«Ø×)§„b99ZÚLk©ƒò°Ó𬴓€Žd,uùL¡Aô5¶£%xw
+lÑÐ-zÅ8€Ð]Y²Ý8ÜJ® ½÷žzý0Sõ@:¿’x40×læš^…sZLÒaJ˜/Þ‹6"K´Æ ô­êØÇÎnÜx(½V‡ ñ{›js«91iâäU»ŠÏT”‰˜¤—ë4'©YîüóŽÌÆà‹ykHõ±/ÚN¥Ì 0r4iª+î&jêŠ+òàËzSµÃ¦‘­,Vì‹®«k"ù}Г´²|)9!ëU§©=¡ ‚¸vŠ Ç
+ï">ÊHFɯÀ¼âB-ù`O.DZ€äýn7Ä×X$˱]þÀ‘çp/^ÎüΙ€äѵ»µ“
+‰[-’]Úañ"§¡yà&jì ×ú4é|· ±Ñ«b'ò­ wu…¬*0mUøÏÈ—6ƒ|Ñ­¤ÿ´à˜¤î4C >#7©æ°|0„‚é©Þ—;ñž°8¸.Gß /;Ò.P²Dñº ö®NX3Às;™€ó¡Ê¡‘ÿð3@ù Ñ ‘9ŽÜƒ¦ïN= ðĮĿéz*Îp3àâ=8d½ò¨“=[XÁ§Ïµ\ÙþaÐŒÞhÂÔŠr@w‘^•äz(‘‹ºÐSáKÙOÑ­* 'ÀH͈sß34¼Ó¥HøÙ}¸/=qœ RYkUµPÏ{Ë JѤ¡Lh`P°…¦¹`‚î
+Y*iÞö2Òëô’^gÈ\U†ïÀNp…WãüpŠJÓ¯ÏãÙ²Æ R@ÒÇ]Q„¶DpÆ´±Oaª®“™@w˜=«Öö^ˆ<@µ¡à™hþeA.Æôåáߢe£**/# œˆ€k3bÔŒøD£q
+M›»DóÏIv
+Dº„ÃÃ1ŸhçÍ„ @B‘·E3`þôd4 ½êƒšHõxØ(æÍÅ-µï»ÆçVßÏÁ¨*J(Pþýô°¿MmDz(5·»ªË'¹¾Fl†Ö¡áãc­ ëSåb)ÛFÐh;#Š-×
+Ç›¨¢fR± «ïÆ,RÅ\â"4u|…÷°ë ÁȈøyÁ`þÌÌ¿y½ÎP „‘1Õx4Ä·©iþ3'\Œé‹ T=£‡“@ILäŒ-Æ …z€_ePwšYT²±ÈÌ9.,pÎôëÛJÊÖ+*|Rȱ9Ðù"¦•!BÅó= €1F±–AØœê"e4
+ʌ
+DóÄ÷ê}Qºj¥Õ<R¦|jÚÚáÆü[P "Ý%2éú&yr½Ë¼ŒüÏÍ—J«5O²W9–ÚÖ‘aU-ÖQ›?Ô@ÑÛx¢¶‰Eöä<î…¬>qêƒ
+c_é~èÈœ¤‰Ö»bCûzEÔ\»ý™ÞªeÈÎ[G¹B£¹ûCïÿ Ô—æc=›Bai-ÛÔR€Éôõ¥QèåèºA'K#âóFj¸¦†¥ÈDaÕz20Ä!òŠ¯ÁA¦¹
+³«À ùƒšQe3Œ#²¨½Â÷Ä’©ÕÁs4×6óœ„Nú€R˜Al#¶¦·•Û¤m$ƒ ÷Áé4¹âSª4û{ÁùÍ ¾qiÁpŸ–TûIêÇœ’ÒmûD(HJ„úŒñż䴅®zE=c’ê˨-}:®ˆb8¥ÜYÔˆF=™‘¯b›+œ ŸŠÔñšHÝ“øV#ŒïA9eFáhwDµ.v3wàû'"äv"Ô­A1c›7Oº-ö@JgDáµZ…‚öy'A#ìÈ“¦;EcS º ”àoˆ:Am€EE!Òž¿ðý1ÿ˘—*÷£Bñ2v4Ü®g¥Z¦wÈ~xåøn b:wÊlU4b˜“¬)6ħEÄè_1æ&C`"H‡l¿8íI¾È“‚‚Á}‘`M¨9÷o‡„Å Ev7GK™w<[£¤m
+s{Ð ¦Iƒ5ÈžËOéÚc‘)“b"âöBVçõ†•‰Q1æ!Ùñ4°†Îôã²@ôàV­€~ùñ÷;äÊM̃ßjî‡÷}l7 6Ôá£o(Ú»ãkÊÁ™Ï§èˆå¬H˜PDîÀF‡„g‡vð&n¸Ehi3Xaa¥ëÎómì Žêñ'ßCñÕøùF
+5Àažp?ñTëSŒP4uøÓ$BÏB5FÒ
+Qõ‡ÙՠÓjX*êÌ)b%òÔÁ[°Ù*#õ›]OßF–'ÕçÞ÷Œ*9\þeöQY¤En9d‡TªöÙI§ûr!ÂðøûÖÿóûT‡b€üÉ×Ùý"0CD×&ˆ˜Æ]ä»ú2?ó·5”û§fñ 9M×42)úŽõäVFî if¶CÜ‘u#"SVto\4:–ô…4ùÄ(Uª
+ísð:Áó=ô¢¡qúÊQS ¸{èñzˆ• šŒÍGdzcëƒóÕÞŽcÖ÷U×K$ b­î•^£fhõÑó_Z«‰ä!()u<¼´Rå&¹f°ÅTøË@ž
+ƒk‰RÔÛ>âA$øïîÒ§ž™—gàJR I¹4„ŸËðÆI¼S7mê„7u¦Tõzü'æ…ýU®‘(Liµ•§@,‹vÚ¡5LLV ÏŒðÓå¹…¬5~C‡ôØüM²u
+ æj_UÈæöyÕÅåSZhQ‹lÑù4⪞42(¥li¶«á³-÷…ëçmtýÕ˜KA¤°UÒ
+¼“·W¥àƒ4ýp×®ñ9VcÇç³$"|9’ÓÎXM„F €&ií/ PˆSÔ
+ÎÅ‘ ëÅ线®Õ8Š+cKŠå€Ñ#žÖDÀ²"ö¸õ²’uÎE¬žóc­dRaó¦Êýÿ¼<\Ë].™AÒ¦ÒñÑ,T¨³ÆNš9%i½h ÒE»g Äôrlî“KNÑCF% ÑÀ¬3ñ®n×nÔ+#êè¦ë£or
+T)9O¦)žd[(Îc
+±çµ1)ôá‡=WZ6ÎD›ß" s/ ©Y,¸§²-‚1Kñ$»  ±ŸGGǹ½²çðö^ü‘îNµ”¸_™—®$®_4tnüüÔzâ=àç¡2(¤’ºÚÒnÄ †cHùÓÆ
+ë‡Þ@°[úéÚJÖ8÷JŸ¢°¾a<Lµ…#¦2XÒæR%Tzj¾¨WKÙ^×ë\ _•Ssp÷+”%Wußúà5ÜBXÆè90t$[!°²·2_¬Ók4€"rÎeF;Ÿ69wUˆXiÒ(9=¼"G…[46×Nc³W´ù½^w)Ê\ãI”!WÔTêa¡ ˆ¢D4˜:q™ã¸A×SQV@P9ûe%h6PL=Eó‰ZW»W:Ƙ鈕õRÉ
+H‰Œ—A’\7DO ;ô¦ƒ \ËKÝb"¼jÝë—$~M¨ê—§¥°$We“H$Ê1fÿ©Ý>ÇŒ°ˆµº×«V>=ÂËl¥6;YÇ\6£›ûDés¬ê7ˆŸ?þþQ>þúRÇ×ÚÇh=ª õx/Õ&_ÆÇï5›Ë½•5ȼ®¾úì}îG)£Å\ܺ¤”Á)­®°u ηcÔÂQçUQ
+÷ðUáÿ=WµÑFX><¯êÞ+¡´Qç9§Wç)³Å4ëÓ›rt¢Ÿ6úh³´Õ_
+dþ Ji:ºKæõZ‹Emeg°Åç*ÝéTî‰Íh _ÅC¾9x4Ÿáçób–rKÄ&xfW‹ót¯œë­Ý@²³­Ry4}wŽÏÝÞ²t0ªÂsš²ÂGƒ<c²+œæŽG·´.&•™/4㢃¿Î)\laÆj¦7ÚZ«ò«ûHCÖš([Œ]põ Ä‘Ùn»gÐ× ·16»ýyDÀXÞ¹"£šÝ œ‰?Ù¾Åõþ¸r¬¸­º÷ÿuÕj![ÈO‚FiŽ
+ÑÙd÷o$rƒoÎá…}*ÙT#{g@&‰0ÎìÓ'(lEKNé}y DOvæÈÆ3è5„ô1Jêî·@MbCÂÚ¹Ÿ$Þ€$út‚7í -%³À€MÍ”ÿr*m3“ˆHhÂ`.BNé”Ø9œÒx}ÊJ ÷ê)m¡/oj1ùW“cBò¤‘ßAÐ47¼
+#ƃÅwç… ýzB¾¦ÎNsسƒ&ýdp³¦´ œ4²)üs$H™•Wn:?Sçv”ç˜)—ˆém]ó„ ¦Sü@cX=¯ å>/™íðâ s[qG15cTäõ]Ðs~}§Kw3oÛ]p?Œµ¥È þ„Î@@ lœ†¾º(Qý$AV‚!ÈT«=åLb:P[Mõ©´[AVOóر¬NÇ3Ʋ$|FEÀã9eQŽnÊõŸ^å®Á¶¢”yFZ‡;”= ‰ò±n û*¥WžR·ûs꧖w“x§%ž4¬[%êàä0V¼r¥±æ¿Fƒ5MñSþgÙ)‹L×t\6‘%îjÝ_{^Ì v)W}ÓÏ‘ßv˜rZµÍõoÏ¡úˆsÀ£”gÁœ[R yWOuN#(K×UòB‘CA]V™}a—”N?H
+Â>b·¨}J‰L+æwA’Eæ Ÿhûrbš³›a.jÝKg“pË5–Ašv×Â…Ep"“AdUËp“BI¸’¨êj;J×p¨ä•^¦›ãœÓø p‘±½ КåXšûÔ‹¯Ñƒnórõ©®ÂPÈw¿9§ãu+ÞÅóôÉaÖ÷Rf‚4;6ò½ü*¤ÎŃ„Œ WXBWÛ# ؃Ê{¤±µVÏÔà|:¼Žå¦÷
+3+I\B˜8\Ìh~ !SLÌ£¢TUF—CŽvÒ3é=bD½Zäs0*=²¬a$ˆ^lè‚bM›Ì#T¶S‰Û–Úõc‰nAš(“gµ<GÓ„pùå_1Ù…-eÍžQ!JÚLþ«è¨‹‹N~ºB ”„P7‹“<H™©û8<Èãã¿ÄOÿ/‘^^óñ‰¸B!iw+irý_GƒõÙñÄ¹× ‚L–!„_´í
+›“Æ#“p%‚tøžO—&p÷d3æìEù
+ÓÄ´ÎImôH•÷Ô¦¶!4­õUŸ(ùÊddîÝiÜ® )~£“è愼V2J@¾aöJâ`SI öðzS49Êž¹$Š8éK×É\Ç,jOüخ܋h¨ îoɪÔõ¢ôKÁV³ë¢' ¬\ËÕDn?¸èµ³á²ïÀ¢$¦ßÙšuBÙæø1-AHÐba{4íŒKE°r]AHd‚zWS-ŒÍIÛ¤¯g\T8‹é
+K‘dj›ž3n ·²ô úõÔe3ð—rà—,½€(.W/7”<µÔ¥¦#¿Ça·±WKâM×j‰.œ 8“8‘5èöà/7W9É~íN4÷`Ïãš”ËÆõ—AÞÇ\šg3!…x¥Á—ý’WHŒõ çý9”¡è=ŒÏs ç2ÿ[KÙBa2v™ì…­cW¡·ÿ¡¼<Žì8‚ j| ­ÅWxëÂÿ+_vWâÏÔ
+.˜VÄél±˜U–Än£ãÜ¢àcãˤNpÄåìùtï(‹—_¼­á±¦Úe!ªÐDÏg+LÚTN'}•*¯Ã ö×ë ô]ÓQ‡“ál+ J"à ;7+\¾ ,Flé¤"^@[ä¨\Äz ÈôáÃpã’´ÉÉØiØ´+!Az»„ÞV´2Ø
+¢¼Ô ^1>u„ï0‘O$
+ºøY¹y¶ý «À; @غüÉ!ç=6ùÞÛ[‘t9t¨
+‰@m>]ÒæÀ^Å ~ÒÅ`ÃP%HNÎ:—¤>Î*É©ôø”tÃïW䈱õ“nPÞ
+|Ô“Ÿto%^ÒuŠ®I—’Deñ“.±ê‰ÁI‡èîI—«Œè_TwIº¼WoŒÿVx¿€åß².çlQ¹ççÑæ/+0P~øYWÐ+J@«ûaWÞƒ±áútú)ì*Æ >ÄÁ<ý°”I R t˜ñ]ƒ&¥©YÃçgZ¡wbzŸÇ\[èác?üüu)i¨¯Úó¹ð5êRT¤ÍË‹ ß³î›¸?Ö=cþuŠkÒ½•Ü‚®N‚&0º)íãÞ‚.%bµcã:Ÿ‚.@W38ZYÊå]VŽ˜â†}.ź´p`­›¢§WâÒ’t¢{ÐuŠ®A—‰çÀ ?ëB¬K×ë"./êÒ)xQF˜~ÔE2ÎIdqâÓ5¢Ò=Ê Ò¨‡¨+N„æ3Ú–¼¢Í°£†nöáGÝ i×fÀ%=D]`€™ Mf™ŸuAvFìÐü¤{+¸å\!m“U¬¡?ä\¯è’sUÒÙ‹f†Ð[Îsó#‹GϦØHuú‰¸Ìœ§sÅŒ‡rí8ñôsƒä-†úÛ|Š¹4÷æÅBœÇ]b.Ýæùs"´êÇSPC<k.ó)æPÌKd늿Ž”«B´ö æsei"s°|äSÌ…êÐ5Æ|¼fás¥ô€¤YóAß%æRÂ>(ñðÍ=æ^‹Ü˜ë]b.H*öDÔ¹¨‘˜8ùážrï5÷ ò7ÙÝ!wtN„¤pšM0iDâ˜z_ê<Ú8e@å`càÈc…Åþ탸·–ÆYG¨”žqÐ]‚«$yáJwJ~î­èËX¬ãÊïË°'ÇÅôY»‚Ä †ìl¾+ŒUÙ†Üg•iÎ`½Ý߇ŸUT‰ÓW‹¼$¡í2,9H…ù)“Ü … †¿ Ù› Ú‡ˆ w°ûÀïGT€@ª?Êêò‰Ò@'éa”ÕSô¿WÈŸdƒ1U8ÊÓÎBŸH™8ζR¦0½“¬w&hîþ†ÂÿRÔHËYñeƒ€Ífð+™^ZÓ6`"ÅÖEëôàŸU¢ýȹÉRÄ…å>fI&¸¡¢Ìùd=êMe8÷¹ÓÆNçðÃir*¸pæ¼íu
+‹}̽N Œ7ñi&{ÚÚgRnhSÎÀáíôÒ¹b;;ýq*ÿÓÆÞ?zNj †Ó#¹_»ˆ¼I@›=»%Xmš$(9&äшºä]E·™ÙlñØ°¤˜@·ªS#OÂÓã·Íª%Â$€",§Ò1Ì®khV­òF
+’Ðù.I ¶(m˜^SÇϽUðy±~|Ü
+ÚÚG%^
+‹$1aÞ6á° S` Cê_-RÞ£²få‚ÔçCÑF”_?/DDB¤­ãäOuŒåíaʇ ©‡ö< Šq8|A0€¯Qr
+„Ç- h|@³§½÷"wæÀO
+N#}·H×!F£Hj†-†SÄà`7E±ýy¦ô0¯ô sñózÂÓ¼àXaG{8ºŽ¢O–#P]G
+«g
+^”½Õ?}—È| $Ø
+Ü@N‰]•Ò˜2ÌõèÚû9A–ÚÆö˜Á‚pS¼'oMRn©×HXÄà€íqÁ¿Bp®æŒ :œèdâÉ
+o¾Ì0kSÔí $<àÿ,u}x
+¬«ÆêUë…,“3YÌâ•=e~¤^z‘’ TJdXªš—£ Ô÷mw€úV䜢˜rⱯoåÆBUfôúÓEà­èõ˜š@òÁst3 lÁlü)u‹3E<ª üó¨Ù)Z%%t®Ç;®n¿Y*j&Ž°‰\hQ‘u8Þ±}7¡²Øš=E¼–_*qkû®dy’ìî.jDÚZšn%bú*ëSwÄ˃r@¹kh†
+Š]ëpŠ^VTÅÁƒÏ/fËÈ6QÊHÚúX%Ø«ÖWæ_.Ì­ÏcÊ
+5x´ÒÍkê͘ðJor´%`a‘Ù¹ÏÉ’7´ %ÄÞ‹Ÿ·‚Û0¿¬kPrWês¹\çŒ
+é”Jzc8v =âR2ÕX†¶¢È3(³€äý9C‘a.­/û|XNz= iûÀ
+ÞJ {x²ä:
+t°éÉ”””Ù´=Æ{ (æ“zྼ8^™ ÂHY\œï¾«ˆõ÷kû–ðá¢X|CúpFàß}žð rà¿öâ¹çÐÍJ'Øñ“ÝÔ¶ófÀŸÀX ôëÓ¥Œþ²W`áI¾:Üq²˜ÁÊ ³~8x»KÞ¿äzº¨"L1ãd
+²ê8ÞÈl©Bv6o"l“ÿ©&!w:òd‘“i›vÄjD©WÈDÜÇðyx‹¸ÉHr†—mr‘x©‡Y~)±µa
+Œ,  [Ã+(‰áˆëÒ2£÷
+Wqßk:EVâ½üçÜ$öPóJ4ÿ*¸¡Üu4
+ÈZÚaÝãL¶Œ±`><– DÝ "i ðÂÀÛ{eYa~=ÑAòVí®”ôÐiéFøCL"k–Η·€ó ²YÄŠ®gÐÑúÅ|â
+v¤”+`Ý}t\О‡ï>š Ä‹áB‰¾‘ü’ï£YöÂðÀ‘ó“¾¹—M+ Þžõ»E^àý_”.‹üñ7¥Q¤¯‰¸Ìò€Ò®/<Sƒ,‘7bNÕ©Y›~€Åí–®
+à‹O(¡¹‰faÆJH9ü
+L†t¯‘P }ÿ,Ç[aé5C©}“ø¬"WwJ2é@‚‰^å]"¾d½QI£2­\;g5“ü^@NåÑny„Ïš#yÄp Ø—¢×.j¼§
+@á?ÖT»ð”ÚÿÔ'BË·/šÒsè…)@UV~Dë;‰}ɲCp˜9“?•¨ýвpAôVÐ(V/*9¤¾>îñåRcŒãU㟶*$ó†Á÷…ÐÌ$µ»š‡ÅȉÐ\úa¸ûÖRuEX¦
+ª×L‰k%3©æö! F–)5:ßâ
+•&°èÛà‘̦UtÓ€„;®Çˆ&Ÿ®8ºîÛ‰#:˜`;Æ
+¥Ï‚äç¾g%<4îðÀ1Nñ3´ ‘ôýߣÂ
+4Át¹Îc„GNóm É.´D¢–¨<±¼ÒSƒ£X¸rÔCQ’ê'¸‡ÿo«èmÿªèfoövö& ,.ðï@<›¿^ÜÍÊ–ò™Àïc8
+Á}˜Çï¼BbÊ’ÖÞž¡¥7éwèjàáãÑÉ`Äúƒz¶Ð̬b@äú±D«Åï—>CWÛG}¿U¿5'‘ à´ y0÷nLÿ>c)s)âíL
+öÌ(6µ…\š P5Gö#¡WëA1„0z%ßÐ>ÑÁ_áÖä‰NßÀ;ç.Ó'÷ìmaJµ´Õ¡“1‹ÔÕKdüQ'ôB)ÔÜ`läÈŸLçrÎŒtƒ<6½&—½çXœX¡ÅO%j¿lÏØÝjÂ'ω%ÊȦ¾>c(-«¯ é\˜’ɲðO[%Ež½ÉçørÆÑu{˜&^Í뉹°>ÜU³2bÖ&8S‚Á+æ`¿ ia[{ô)6fÌ;8ìPrd¦{Ñχ"Ø·ÖdLËb¦CQ¬¸q˜MëŽOBWUDÙ™’¼•¦µ­q3.ˆµ:PwåÂò[Ô7‚ ¢ˆŽa.à™QVé¼ï§Ü&éÖÆ„…J&Äñ¾qo9CA!zÏñPá'1no %=}±q°Ó6
+A«$‰µ;oê{Rz8ž Ïr-™’,H:(>òøW˜ò£*’K(ü*É ƒìþÕÊò±U–.ë€êâ¾ÀFÀJ0Šß‡—eOÎÜÃâòQpÝææÒ„í°ÁléÉí‰fnÝ 5çïp8Ö‘ÝòÍÜày–ßÝEnR™&kë—
+²íjúŲÂ4l>ØÜY"wÚ`4?j M »næ·†"ññÈ‹†
+1­aõ:H7ùÑâ’À{Éì5/í3®B‰ß‘£ °xÈ~åÒp̘VÚ*Qg ¾±d< Æ’¤º‹2ïEмA»% :m®þ19lÝp÷4’”‚Ÿ—Mã´Jg]rØQ¹Aþäxzø½Ä{Œ@±:ÞŽßѸ©<uòÏ@¨¦6´vš¦¼ÐÅûÉ@õ}Ò+*ôî^õ‹Èdôï¼b‹ÌNTÛŠ|*~`;)±Øp¾òÏ+JMÕ¤±vžïUÅŽ„4¯løV#÷;pmZFQ¯©k ?Xê¶bòá4÷­B—n¹­O•*wâ"/Ü77šÒjÏv0Q«ÙØÔŠ¹í}M«gQr®>ïþ((BTXŸ°Ÿ¾ÃU™ÚÐuü6Z71±‡<F\RÜ×´(ß äk¾ /+€µâî³yX¿[VƉ"*‚Á]ÖðEwöýz(Âè “Š¨6Yº‰P=¿Ã*IÊ›4´)ŽØ*™ëÃé6ƒ%™gÚ¤ µ^@—„ìWQŠÚ&Ö¹¯•RˆS Ñü¨Ž"*ú°w›¢X®¨Ôðãe’œG®áô|çaí­ná­|ÿmI‚¿ì*”¬Žýl5T$D[.% ‘’Žê~G›:@#kÓ¾Ó(êñpsÑ’²/§µ Óf~1¾65’X¹1˜·ZÉ¡éf–AŽ@>y7§´ÒX×¾^±‘m#&¦hI$œ,Ñã : †Å:ŽTM”‡R¿ˆv®Õ•30L §Ø£zГg¬°¸Ê +,é0¿T§tï#(¡–ì”Ø8ÑmšÙXìúô¾ ê÷•ÙtZB´}ØMFž'v L¸* Y& -‡rT@¦ Cßµ÷cH#¬gay÷Y¹O8T)¦ÚY¤
+¯Ì¹[C¸êÜŸ“ú­ÀæÍç¡¢(ó¿AäxÒË;A·IT€04qORÉ)ƒ°B£cÏÌäbÈÈ’ÙX6h`>ÈZ&Ãlê@¾ŠÙpu#lR(2WÛ9èKVß#5]Ùå#=¯èb†U‚cd}¸¦o©)$eÂS(Óƒ5Oý-$±¹ŸF>èG˜µ¹AŒ02$…¨ƒ¢"à çÄVž¾ƒ•€ñªÇÛ
+Œ Kæ] ‰,Ñ’ÙJˆblp•ëýy 9LÏͳ¹SDçÅ[6ÚÑ[Õ”fÛ%èÍJghS³ô¸Él; å Hqo‹^}ãá0î„ÛSôþT"f·[P¢+’…„}SNfLÑuö³‰ë~ì’°’&ûf߉¸‘†(¬_Z%p0Ž)öy\"ˆ¡QàH¶Æ-S¾Š‰[›q(ãïm·™yÖ8ä*óÓ°¾úÏOc^Øf JOjJ7Q2t$ We+i³‰-<^¢8È_%Ì+ó®†k3Äb:«^’8rÐCK‰å&O‹O*û;䵬´˜§!?–¶ØTþÞ-A¹è阸ܶÁáIqÜ,òÃ+r‘˜€¿`sú ¤G¶§Ãrh¾ü„…â62§_M€k
+'Ü;P
+æäå¾ð®Sv? ûNÍü™wæf·)uÅHzzŽú”d“ Xî… ”«Äóí(H«€q9±§ò3rƳ=æ?"`âJ.jï¢}OË~M¢Ÿjoò>¨1Eü™ Å¢çè|d‘"+3¤?¾S“T
+¸øjåß I¿äŠ1j<®
+\Éâ@dcÈZ¼ÕbJ° M `ú:|¯¸)ì­¿ß-ºÉ°Wt‘añ[Ñ/Çž\¦Û*
+äééA‡×ÒÆ®þuW‰£¨›HHꥺ
+ªíže°!jsJ œÄ¢Á(¾  6’K”ÎúBÌhð’Æ™…lÕuÛ2òqÛ!¸[pNð :|…Ÿ«Ãà‚áµ(þ9ºé0_êZz$tyWG‡yp«=÷äsß×üù‡7<9H©ƒ.f3«Š~Àc3íµF¤3åÙž±†ÞÞ˜§¢*-0ûšèyÀ€´·Ìß±zA7ZBÜAÙˆ ½¥¤=›c»8éLgÞ9´Ôª}‡z0™IH'Ñá[²ä«íg”‘x%ï©9%ë(å](Ë “?}'Ò…4᜗k×>ÜДŤØÅ JÁrÛ«ðù(Î!…¾yýZ$y˜\‘>³l{‡¯g ƒ-"Àrµ[dtc>Ý–0ðhK»ª•àmÿ#¦SbM [Ù@Ü —×ï0LJX>ý+-Gk§vÐæÙFü5ß fø-.9F?G•YiC£½d'v¤+¢üð8`|Ž¬>””¥ú¨ ŒvŽúÈïŸÞ¢CP4›÷éïo·(®Ïрǖt˜‰ o¢5›K£Åk…´o>dø ˜1âQ…¦j7o¨üèTí&ɧÌä\‚ÍRQÐ’ûöö½®˜U©îûn€Ç03h·Mÿt4ÀŠ¡¼üäZ*}N7’5Èsn—ÑH¬« ÿ{Â
+/’,Kæ¿]”2ʆf¤wo»¾Wtq<gQŠØ‘ú8|ÇC›‚£¬ãÁñ\Gá:žë@ï\a¡Û€Ì_”¯ðà¥Wxìm—\죮0],6‰]Ýîà
+v%‰D¿X&àá–h½ø}º€±8îà_›¥%„¼…:=š Kh£‰e ‚¿­h0¢¨5O£ˆ´£Ìñ-þÄwn8óuûÆSk
+˜‹¢®®¢&A¨ZÖ¥-¡î+ÃBÁ¨œ¥ ž§îI\+pHØC–+Éñíƒî5t+2dñs´¯hžüˆËœØ©Ð$i,ö$è*Ñ™’Ãñƃ(1Y¨j.Š"2ÒâÞã´ZÅ}!`kAR@@{Âß×0þùr‘šû—@Þ?—5ûmE
+E´S˜7ÇLƒ ù
+Œfá3ÜdX`s– |ƒ
+Mq¾–i› ÅC#ŠUdºCnì>›˜ 7Ó’!+@O =4Šäëò‘Sâ’Òµèý¡¨Èúó4Ç4À)ŠíÆË9# ¢'àÆ–èÿÇTP¦Ü=ÁÆZͽ¿^vUAA4H`˜Ó¿ÊÊÌZ
+(ùX®¤ÌtBk;µ'f[X•:ÝI¶R"²6úú`p¤|§…óo=—Žô»S'ˆ³y‡Ž´5šœ5Š¸O,»@œÆ A¤8¸ë«uª¢6#eëž ’>ÎJ^tPÍŠ#vVbÅWÛúhr wº¿Œ‚MÚ”¼xÓ‘ŽÉ³ ꬈„UF(d´¿€0Ž)nl5´U¼æ6©Â¸€Þ_€T×A·æd›Áõ¬’{š? 2Lp*ºh4pM …¼Chu¢:îÕþi+Ü„«»GNˆB1kl…sÜ­{Á€®Fš¯eH
+´P.U{ÊÆ]->ƒäÒ¢,á(´B¾€ð–Å4:5{b£æ¹ÂÄÆÙ³Ú<Yþrò{9Ô‘M@q¯ïâu}rÖ÷ïدšmÂ\”©šG8ŠµY:¥‹_ju1Ú¥>. áQ™d%æ÷Û×·9HÊÆ5B
+J¾4P3+jEö
+)‰d¡³‘°ÑBLi [:).iüòЉS¬“²!FAƒ"/Ÿø©8riS¿±;#Ülл([PÔî›A®À£7ÒHk…Ä[g»Þ¼aueÎ:“®`uo`ª2@Й3£Gä*éW~×FŠ›ðío ka=UØ·pñ¥˜_J¡
+4y‘Î]
+¿¯ÆãAîìq+«í齶¬gø„]”®Vñû,ÉEA<nò™Ò:U@á¼ÜŒŽ<ršÏÔ‹0w¾Å
+"MÏ*¶²t=“Yâ8ty†AQ¾ zÚî€x2vS÷¨¾¢†zê¬!îåvß
+ )¢AˆÓøvhäD—CøÏdp0”•x\æi+0Ì“…wTb¶Ìj Ù¦$̓ªŒ‡Ëix„ L S±< ‚Vï¢8E³ØV„V¦]Æ ðœøPÓQL*åq íæV<I5"KÓc)t©A¸a¯ç)Å!È52NSç[u¼ß#—ùVJ†HŒ²ð²uøŠ®*ûrHRº%úsANN*ƒ\ g+„³“ øýZ§ .×yÔMcÖ j5gá9±ž§šj±ëÒ£Š„qîG<¦„Û–Ùš~ø¢>0„TmY”Ëx(‰ÒµIÌV* e‡åä© HãoÞ)OqŽ~Ù+«2"Iaóã0"ÕÚ45‡ âCýM\ò‹#Ú¨FLjaD©ÜiÅÅ;4J²Û#ìÕ"W×8ãÈʸÜMù)r™Êsílô3#õ Âw‚·”»kíû2
+…(Ì1‡r9ñ,ž
+HDüx&1äñ¢ÁQN_ŽqØŠéÉåkÃ)Ú©vî) oqšÈ:s°8)Z’aª/Â!  ÷Ä,c'z•y‰ 5Ñ6èõ­Þxt¢Qpänª–éD·áõó{“)Yˆš|ó£ñÖF6EäI6ê܃˵ÇMÞŸðÙÜÓ$Õ8zó’/j+"+ªSÌT¿ù¤ÿïß1.Ùa~ü!(AÆ#OÃeN¬ò®T„üeˆJø*{ž„û„94˜©ø‹#2©˜„\ƒ`Í‹¤c!4‘I–’êm#º­^‰¿ƒàë¾ôí¶ezÂs¯~–‰¢4â „=‡Ñd
+o«Šè;A b&*-):[‘¶‰bP„¹ÁO¼HèGÀŠ­ƒ€ÉYÈ´nÊ’äA8ºAH^sc|å¤×^ßØA%ËšÄVJ¥M`G®<ÅæD¤˜Ô²­3h“BYùƒm(>¯žË+<ÉŠ±¬ë‘™i‚}½¿
+E¶%i1]Z,oª5%¡z¾ÌP!®}ùaPLØG(á„.¶˜\ÁõËòE8mÑ\&n}ø:Ûˆ;D;a2©&ìØ[°Cžâ¨#ª¬õB>½÷Ú"Æl8Ú.ßi*§ð{øë2*|oÓ󤼚¹“hJ’q…Ì'£ €aÕ¢IÝ
+s 9ú^jŽ1ãñP]„W´ÙImˆºtÜ,£èE?©× ƒ¥€#ºë—B¶jѺýM´BJ¶Tó*‘VŸrúiÇdiŠ®€v å%¾¢Ça Œ3ç„5>hëˆ#ð™•Hî§R@0êÄÎs©ñÔ¶Ú|áÞýT†Ë›å}„ >0gdcY›é†×ç™Úôþ”U­Œ1¯& ßPÂL )¸Šò‘4&k:ŠO2Cÿ'° “ã¤M Ô5µ6²m !i¦ÛºŸ9B<°Î±ý4ˆ®œ‹¾s ¤qR š׊­È`Ø! ùû Ƭ‹¬é‘ªÉË7c€$£À‘î‰nešØ!ÜEÁGón´ ý΂ãö#J†ß57·=MeY5$]þJhonB¼–ä¹J;Æ\5°h’l¯ÖÁ6O‘ýe»‰QR¿õy<6*¡è¤Žš3]EpÿÈ/8o‘]4_‡]šD‚QD%äpÞEK>³ í!x2!n[a¯o)êMÜ2"B{tû›ñ2I’äÖèUúeœÀa--u‹¿-ÝûŸ(e&úۤM*HÐÕdYfÆ°º<XÚ\KIÂMÕ…þûa‚O²²†%¯tª`-ktO@7ÅÍy¿|(Ÿ&„pçFQPlþˆO9/!Ø`
+{¨AáNÉ©F®,(ªj‡P¹KX…®¼QL¡¬:7*T´“̲ŸÝ{Y+•@hòP}ÖY鶀&s—ˆqðÕhìgHóƒØÌ¢©nQˆ
+åò|¥OiUáÞÔxt¤öæn¨ùmkæÍhî{Ÿ„ ë kF8-âV53¥øxºûM9]€UòÅnu"2ô™Á}„k] Gˆùî|f^
+Âd/ù°AŸíûWúç²Tçªm µ!!ú÷XD¶R6àR€w‹Uÿ=ËX÷?-y;êûOîãצK†»E·z{¼öGÑáNÉàzøxí÷’ÓµwŸëÚ¿þ¦´/ˆŠè3–ÞÈ`Xà…[ÿ÷¡HœAbö.¦> a£‚^R‘-F\Tç%Ĥ+ý-wFZé¶
+(Áó,1¯õP L^güÞÞGÕ d¨ÜéÔ€}°pÒ÷wº²O“Sò£&¡Š#V³z ].c(çF‹£Ð›œ‘Ø9©H¤„j~†g%zÜÌ3€"/
+„A-½_eúw b©LŸîreá_´úŠ §A ÿYå,î£^‹„l#ØgXþ*ðµÉ†¹Ã§úxyl ëÑôzZ¹g>±©k#½AǬ2oB@ý3‹'g©ë~wû¸3r¶#þ^+~y0Yd%­ñð"ä!åç0
+Ep`>’Ÿž)7M(ì.ýŸ$Ú®”:QZ[qå7*n2‘Ø
+¬Ó<W¼1ú_
+ògL´=/W«;þxq”fÛº%2
+ˆba@{ã©ÂyÆØó.¹¼:Y·Ùã$ŽÍF.ÊÍû‘ç&Æõ“Ä“¬xZ;»tŒ¢`‰¯·Ë¤ª„nvS´ºJ®ÀÄgY”|%5<WÙí}eƒëá[Lú]|{‘V¸òâíáöcê6tÄçiK´&C÷¡ˆ!:á¼Mþºf3‡_¹°2ýò¥1Ï©9N¿´rèd—å3ä¡ï ”Ó ç¶x™2CI(lnÜÄfôøó3PÌ%_%û³e}'»’qÎ<d”)NÞÜôvÝ`Ãl#QãþŽq¥õïÀ¡
+Ÿ ÃoÌï‡ É}…´€0ÌæÏfüì
+ ?®'³s {DQ2µxêKN<p|Ch½ÏŒ`—Ò¬ýÈ;¶÷.Ÿr󎤜A ç»ˆNIðÅ+Φch;ÃÝ#<Àô&)í¡¤™"Š
+jAƒ§Tz:¸H˜ƒÜ‘'"Ácˆ#[õnÁAÉò£èAŒrÇŠãKrtpI)àd€ƒÝ?%"ØPÓ¢„U
+å Ї€#Œà‰jó¡$³„y€7Ùé8 ÆË\hÇ9†ÝSbí}G‡C$úehpϱs#/!›àJ9ìî5M¼Ó
+d©Š¾½ˆŸ)¬÷áÏ
+°6ƒ]¡Ó‡BôhkßV «Ž¼F3?M{‰ {ôÓ !„+ö;ùnÁMÀ[Õ·Ó¡(c±¬²
+Ò@&ÀÌ%Õ¬ñ-lÝ’~™¨@¦½8ø²8Ñ·M=æÅÀsq¢œ'æÓÒ}Ù ¢‰‚žãa/%›*a{šÕ’ûñ;â ä
+aøåxJЕÄ%™îáV±èR°ÊÞ†û§¢ÂÃzõãˆ)7„QCOEªƒð³_6ŽON ×!ü2Â8”…Ø‘ Ãü
+­Ï=ÁÔ¶“£ þàþ¼I€÷UtpÅg *ž¦J¸!·A¥#í›0L¸œîÈGü)hdPd+/9þn8&²ÈÔ.%vyœtŠï yGæSÇow‰AŸ*eJ|1íß6)ïß™ì–pVõ|Jð0
+´Õ§[ÁJJòüm¿Ôm:”FÉ&)¾J®#ÿeF‡çBsÎ%ÒY­¶º½·È¸bí¸Ò(ùˆ‘:MAVZÛr
+H‰”—Kr$6DOà;Ô¦‚à$×í¥oѳ’ï¿—$¨±ª¨ Ýa««R $‰Ç¿rKÏ>[+%Ò²?þúÃ’ù³ÕÙK)¹Œñx}\Aõ ¦ö9l$›œôo`ÉŸ3>“÷Ö„ÊÏžºåî­–ÞoL-ÕjÞ/ ¿¾¥1w07¯Ä•ø_n£Úw þäóšÜGñ"=mòQ.Õr+4øÙjuëãÇ [¸ùLž¸ùœÍêÿzOÁ%Ü@õYÍzž=9õYÅ»‚föbÓjöñ¸Cš{2%ÀJÿâ%Sÿ2z*óñ;BñY™£ÔY ž2÷œ4<Îáf6¸¯-ù<aFm#uêÕö×#óû\¥¥6N˜™ÉDš¼s¨×Ö»[*©ísf÷Þu™iÁX}LO¿ 㙪Ãç>À]H½B§"—dìœÃfs
+¸_4ž\°pçÚbN'šå<çF(j¤/ç "µœ œ(ý»c(ê<k–ƒh³$’Á6¢O·6Æì­\n‘f1êÉÞtïžë }דÄP¸1k¯çÂÕŠJâ9õ0NiÖkç£è]Õª_Ü0í ás/hv⵫Õ
+ürÙÉZk*º…˜”ʃÉiÄ)•h¯Ò‰ß× Ñž…ÒædJñ*~æ¹Ñ&)aN<Ý, Ц͉ÎÅóW´&ÁãÓy¶²Zh¤æñt¾›©tx1C·ˆ›ihú-nciª9…û6̽1T"£ŠêòLÎGH$ É
+ªQïʳ‹è—·C¨ ÷4½Fq[mДF×'4+ e"»(®p©§ÓÐê¼!ÐnÔI«÷3Ã
+ŒÝñ0PjÎøa°•Ä¥bl«EwÓÖI˽ÎA$F¬Pc»Ÿ‚ Pz
+}‡ˆ\Ýd×x¹*¼Õ¤¦†çà'©¶òÓÓ?ý_}\Ȫô ÌÌ»S+ªgnËÚ±h¡÷ÁI6¢öîxoüŠª‹
+%¦žJ•Ï•_Ô¸>Zc%ÉwÄ‹¨ÿBÿú¿Jª¥ç—d)âCŠYEv¿Íæï½â³è…ÛaÂRÅv µbìµx0™TኚvÉ aYÍt5웦c9?“[ÙÔ´¡ðB¬òCB#_Ðv»:ǹdÈÂh×ú)ˆE HÒ„hJÜ\„òUe˜ ísx^ÑÔzîë½9VnlCš–ƒƒè›ÑoУˆ„[„ªƒr,W, ÞQ¹ÅÁc­Jqw¤B•)9ŠW6Â4À‹Ú$ÛB øÉBr, 2Å4Û¬üµ
+…ÛFëä§ÏBÊÆÀ)L}©ÅÇÖRN™c%>NrëC—ÑxZ”€ûÃÔ“cú9¡ËtiݲMíåصBÄ)°ŠÈ?·3!èxØKäXs)Ãn´jO•ùÅ;ù@æB)h aí¨F…k=eø ÝÄFxÜ¿9gîÎ×ô ®wzKÌ^^„È­¾~¿ðQRC e,jï9Î1µŸ³é"Ë!ªR8 šƒb³›¤¸‹Ñ±,+4ò DÞ¦˜qZÆ`½Ü‡N
+QaÂ2háx6’ƒC¿âWÙi»ïØldT>eg c†afÐkàKVBL¨¸\ú†À!ø¦’ë7̈òYÔf'Ô?寊öoÎy]G/÷uô2y`ˆD‚B”µÝI9)P@Šº9ãmúULÙF±µh²4és½ƒ(=ßË”Ø7²ýÅ”ß!ÿœŒ¿®oúŠù¸šhÅßáâݲKÎH% ÌQ'تlWgÛvG­*-ÚíÅ‘ÖQcÙHe©hˆIƒh($öË· ÃD·H/Šmãs]û'bGzoÇ0=:i Ìs™)wÚ4‹·Ðªü8|e+FãA/T»œÉÀ›åÍPÅxxfoHH²-³ÉdÀb™ÓGh™ ’gT ß&ŒhËi2PÚçdPc‚Àš%s‹LeUnɾ³Ê™l«…iƒ™HS* šýÚe[ ɨÌY7æ™v¦•…55Aí0É–É÷¼'™œ&EDÒ± ñ#vä÷r1µ7%ÃâàuhÑÇí2òáVÖ–âs\…áïžtÒI_îË=2>×<§úŒÓ÷•±Q°•Š3vÞ±ˆæ\ëÐØ»åf!‰u¨Éñ²¡¹¿CA>ž‰¼§»
+{ûf-
+ãE "X‚ó_@ wÔ
+“N (…£“,«X$<þQ_œÂNYuËÆ!&˜@ˆ½™Q˜sSæ!L#ò ÁxqôJ‘‚0IÈß´ã³ÁÃlγSúG¢Pð½@¾¿ýý–ßÿ„×)ÔmÈœŸÔæ^©«÷»¿ÿ~*6ÏRج8Y´]گί‡C²*Z®9‚ÍdMêŽ3’ÂÑHIÔn*é¯b¼¶j€Ò’“àb?„
+Ñs²z_›ƒ,vsGÝ ªä€/n{$ =—¸÷9åÙPe6x>DG€ L-_…~d-“qØtç€tü)M@j K`OyZÐEµ%&¶LIhé ôœ-ìÉ<U¤H-Œä²< Qªáöá ›„wfIf@à1º«ô.$Í9F–Q W “ALtk=šYH  %;hˆ’Y¦ÑÏù<XäCtc ^@> ¨fYŸÎ†àh(}uÞ¡Ô¸(äéÑ¿ T¥Þ¨‡ÜŽƒU ^ƒ«³œeåé½û´9!_ƒäŠI¼1ûŽé?Lˆ`ÑtÎå؇á2¡Wqjb.½¥Ö h‘)a;Yœ!Ó
+$±¶,/álÌv šGÅ$tÖ®»QzT}…EÆï¤ë–¥‘œb ñp´r\ ÿšµm³9D“T†’ÇC7 ƒì5jŽ« Ne^ ñ8ºö¿Î ¸(ȆQ‡È;øÂñ‚fùõ)Ú‹«>ƒ6 :íÅŒ¿^³5oXÝóžÆXÊ…ÌåèG“Óz‘ ( ÉÔ»WnQ@¨Ó$þ*Ó7ÐJÙŽ|<œæz$Ë~ÞNdƒ-¾$•3Ѩ2sŒ“e(a ¯¢øŠ÷ÞÇZ¡>Œ­2WÔE$ ²D£Nè+®bkè1"ãUŒ›š!Û²sª]; ,ü¸1Ež¸Ó÷Û^)¢É·â]ظêÿ,ßv4™ÞT–×ú‡â>Ö9Q•^·=½˜q¦ß6CS´+í¡;6¡Û*Hw,Ì:4&çÛ) ý´›X6Ô^–!Gm 8£@âÜv ~4]nWx¬‚A–6Žâ×l5Ä…wt-Çä \lwÐ!ŠÍ0­Ýɹóå8c¨69ª?SÔ
+È’g(šâìtÈ´3!Ìe>&Œ ðg—#nSÒ†µeôÄ
+ð6Þœ§‹ü%ç‹™<>­k+´É#í«äóuý×Û ÉÁ©!ZômL‘üë|| Äuø‰®•*Ð/奴Åô¯Ç—©Èh9«Êˆ42N…X¥
+ij>œVíÑ“0s¿8YgÊ@J›
+$aSqÕ²“ä«TíJ/™^xÞh—„
+YD’ý&”
+Q`”±:)1QÂ
+—SOsŒ €p°©¹Û9i/m
+¾a( !f¦ÚPT»*²UOÖ‰¤Ù±Ÿ,òÏmü•uî%ošÄð•Œ7¾jm¬Søgì;} aÐ!ãMµ\pЭF×ø¤|È‚hYN¿žfâÝùo~ØS”™5F@z © !xf­£6r$Âö¯yÖH (¦å¨ ƒÅˆæL¦Ѫü6_…p°ˆy‚Ša‘®qŒœŒªÊ«Áºp ÊÏñ¹¢¸|¡‘†¼j‹ŽÃãjö>¯ÝGÏøýºšõ›t¶â=´×W³ñü¦¥Ã _@ÁÞS^ª´ç@Ò#‹<ãÅuì0dxs#²
+\ñåH3ÉŽ‰ÐeÂIþ]"Y|7€Ú„P-Kïƒ?CG¾@¢È ¬—àÌæ ©¸ûÝΰ‰8
+ ±÷HEtYîÕñÔo,BR£6$}çi
+†ÀñÏ¥}©Ý¿[ɇIBçŽáÒaF%¯¸‰¨¡Fd<Ø8ÌgUmc—ö¿„'’w@¾°9»¾€T z“ S¿Ï ,ŸLíH+^œç×à§a?8·4ðüUOÀ¯ÊòÒҔ峞åõ0$ì}¯~ôÄÁ¼°Œ°îL­„ah»·
+í¢èQã5ò¯ü¡6eÇU”ÂÒa~œÃ°óxŠýîƒSå?TŒx²Ž” ª‚W«,{È_±DRcNÑÑÆ6âMã=P?b+FÜž}–wi¯ã*!†ÌƪW>ŒJ—v{Å'ð3”
+/Q\øggñ%ÐÇÛî‰÷ùÍø%¼6:5~úpæMf)áØúË2·XN^Çu½E#˜”Šî1®n‡ÌVµ„;‚‘ÌLeo
+BjÐŒÈgç ˜AÞÿ²›d¸KŒA Pg½Ùb<ÍWŠ §é “T±C³Ì»EÆy’å( ËöbØ¡&…æOï×¥‘ÖÊòÿ©Ö³z8€)ŠÃÝxQ¡ ýd¡ï»}#;uÍXÞNü.ï`{eÜKô³ò¬Ï– 8aV® {„2Û§c ©*ß5Wt‹ˆBÉDº«»"^O›Ñ_¸[Š
+ídå…1[ùŠÏ¤Òãdà A0ŒÝc|ö~QL1V¬ëñ 5BÝWûÛ9ŒU}µÙhH‘´¹±72ÀXóœsUÇc/—¢’/&˜È9×s½Â´âQšg|Äb«‚NhDk&zJï·†2* ¬Ú§äqÖvÓRìzÔ#„MLìÖö«¦ŒtÕÆö£j3$öíÁHÖ¸ˆO e´z³-ÄœçñºEf?¶./åf²W}p u…¾Ðò¡}à ‡Pè…šñ‘s6oØÏMèWÕàè Ô®È÷ð?çsÈMl8Ü‘”?_@ªü;r® ÈŒþýöÇ¿ßÒû¿þzûãŸ7\KÕRÉn5Å1P”I[´þþ;0œ”›5ÒAÈ SÇŒ`£ja( 3zeVA½e7ñÇþOÅD̸Œ[¨×0Ó
+zÞÁ__ØÓ,v~ÿó yÜ(à†£6´ZLÌV!t22¿¯  —YJ®t)¿¤Û¿S@3ô@ꆡrAuª>Læ1N`wœ2f3¶³O3OqÎ’ÝÁ.QNk-6»géÞ¨8ƇæqDÜ­-{] Ö~\+Ô§ÎrÚýY9ò¦v£ ‡ü‡ñ2É’äÆè t‡<A<ÎÃ:{©[Ô6uÿm#@¯Êpº”=¼nE¡H:`°ÙùpÀ>­¯¬T'}¶v$-È
+±C6þCQˆÙòíÖ×±ˆ6šœü=²Sòæ5°¶¾Šé)‘I·}ã•p§>$õëÓ‘d‡!ïšøX!mŽjç¬XÊ RqNd 0rJ¾¡x—ñ¦IwmêŸ%~Uz!¤¸bçíœÃÌß_sÀÎý«|ïÎÉï=>nÄ·YýúÉÚhÂmxдR–¯<œ÷ϱ¨¿:?CYM©ác•°Ks ,Nž°~„a“ÄÄözÄÔÖÇ"ê»ôî§÷¾Äœž¤w ¤ÌÜ•?æ<Kæaöwé%RÑzÌ5èAÂA4z  ¬)î×0¾4䕳•,7!'–7‚pä8ÿFf„t)b1½ì¾Ìc4ÉÎÑÓÀž¼‚ Â.“d»_D/ñ²Jµ{Rh"6}`ß|•oEß&ÿu,š/d³P°û5Ä‹€š‹„œ —“f¤û”ïqt_V=ô©Ó¢z}8
+Œ›•åèVDDc·P²’ͪá-CˆÍJ ]Z§¨Xó‰0ßK67ËÁ˜3×Ó9 \“Ö»‚ß~ ¹ˆ6‡”|¿’RÓM»>gúc›¯2`—`Jeg·î0µœa6Бlè-%ì«ÞãAâÃG“/çÇyõøH -vûªßÏ%–¬âK˜DqÖߤ¬âðɲ-f[ã[ÑMx•HI±uiÄÈ?.‚åÉôL<ŒÝ®ûQ‘€íÊ2®+ôž‹m¢×°(¸*ãrò
+š>FÁiºÅJdÙIt1wWfv
+AUh?âž
+£ 2ù¨äH‘ßO¿ñŸeÄôÏaό֢rXò«€SÏ+*”fß/Êe7eG²G’!×:­ª ô~àz­îð²~‰`-o%2ØBaÅßGóK§"45²fJïE7(êÅ"¡©
+Y—ê%¨L m_5Å&R®Z
+PdV}â<`FqX}P¤ŠS"–ñµŸÄfãðÖåiÒƒ"± 8À\zÍœC«Pzµ’¥Xì,Úë_Åÿ§alÞ(û*Ç
+PÆŽ0,™†Ú8ÝÎa_X—~u¸Ò¹°²# HÐ#-¶f.ØL
+¢lA/‡¢¯‡¢’yûý‹Xndpíô20«HD+ÉߊrUx´åYR3€1ÂïíÆTf ìgQÛÍÏ K9R:›Êà ¿•øMjXlíêîí2k)ÓÎ[øDöÅ«Ù1ñAÃ4}øw#൓NÝZPz[:ÕöÓ/`”ÔQ;'ó vñ€›r´Í …ŸJ¸£CjQÀ¿®z/Âú÷ %4R ®eâ×Â~2ôÊó𥨹ý‘¬W=øŒB0zÞW‘!')qpq&ÝËØ
+?¦(
+Ê )iç8¦ûKX§Hç°\ÓJ0r;kwÝ„ÆÁV’ @Yb>’zð
+ÕŠž÷r¨Øxà±D&rÓÉO˜ÎÆZhÑš?†^ÄN9ÒJ@ Vyg¿
+~àÞäe.i7i8t·±C6ð«S~g• YÀNšîhòÎf¢Þ¡>”°‘8 ND>]ZX^| ]̇¢//Šòò…M»ìs åýu[˜±®yGAÆWð÷Ró&Kϳ”n–C ¼º,›+é_tLƒ¯ÎG¿ñ¤Ýœ7cü6Σ¿†g›D+˯ž8wà›S½ÌjP² %M—þþ2Ë;ðV„ Û»ÚÁÙ>½¦g¿àsõcnS>‹¥×±=”üéS—›ýøŸ®K/š7'8˜ã@üËÑÆÅe•)â°Îê_cÅj®M\2‡Ù¬¤’ªèZ¸DÑ)_¦Ê>>¾$ؘÙù­¬â@ñ‰íy„@ày…¢‹±¥I:%D¯P")8ÊÞœF㺅çL‹|÷„ 9îeŠþ~(
+(à-ÇOÞÓš3,Ô³ë¢^SêK6÷zà™u¶‹C&Ò¯N׋•ôEC¨Ñÿ/—ä:rˆ®À{è(þ9öT»ðÔÞÿ´O’`ٺŒäxáx-C,Häg™¿øk} ò‚øeWýœ
+SÑæA {f¬Ù"_t‰wF?›VÙ‰.ÐFØ]¤÷%Ì’&A%nÄæÜ£ÄISÂá­åë¡„ÎÜŠ« AÊÜÄ5ø[Ñ ‰Ü˜=fò5ï
+¬Yµà˜’Ãû”¿²í1v}(Ê1ûZ¼ ±#ƒPhôEª˜,ý}ðwGÄja!ùé|L"I!P¾Ëµü;ãÇ’ö´.“àeà›VüQ½Fþö+Û&â!½УŸ,ð,!üü ‘ѶÇ“ðOhÎ.áIx´ÓÃ"¤¨èÅwý^™Â,5C]«~ oáƒ2-×ñtp+ é
+θ2àèXrì C>,-Þ!ôÐæUö)¨Ë`zX7•4^
+A”%E•Q±4ññ°†5¨9oŠ#QñLR^%±uÖÚ`Œ¼·FF6ùùbÜ[‘Üp•Q"É@Ïsÿ;‰
+AA2?œŠ^¨ì÷ÓÇ Všž Í9úû•o~úA
+ÔÂ&‹žùûœ©(áj G‹`¢_åEÜ‘Û‡à2OŒóèƘf#Äy  ³ñ­9t:þ•ì[mÉR*~Pÿ;b”ÿ‹©¬¤§®¯pü~™b*Ѻ½-zy1
+ŽEP ’#ì˺Um¨#=‰Y\Huà€ª?M&"™7HžXkËcªmß›ßGópvFL}4}: -Ũ‘Í¢%ù(N” ÐxþÞ¼ú¡ÄÇ®ÏW]<‹œ@H|;üy¸Í+˜__uD|#A-xOvÎo¶ 3$Löw‹Ž<_¦·açÈ£öIæ øZé{áÿ5ê{ K Ü ^²òbÔÕFú ¨x´ák KˆsRíYÂA»ó1`ŠÀà§ÊѨ bÅ0LãƒQRq gz%\Ãî&V±Ä½…%oÄtìdZc”¿%Ø ßÂÛÞÐñ¤Þp©ý¥Ïwëá1“·’"m+݇;ׄ–-ÄLè*éP4Kp¾4‰¯ì‚£†ÈÈiÝ“a¹À_`ÂßOœ$š’žK’ Âk^–°êX´°zæªr0ËšNq‘
+SGãª?ßl~„ß+Ìðp¬Êë¡èý¡ZÅÇÚÄâ«"#8ӠƇ†™ƒa½!M —Ýàx¿2&8ԢϫB˜2Ñ”yÿ°Ý,FÆ&6gWò6„
+m°LpCìëø äÊtœ'õÕÄþåh›þ—}`&õ@UðŠ£)¬ÑÉ‘¯‘N'o_ŸP#Ü٠9¼ƒ§å9xñ<–dõk^~JÒF Ò¹B8Y¶,–¶Î)“¸ØÞ-uŠ2Ÿœ×ÝKHŸ] ,
+eËGŸŠl=ˆ§øùœü½èŠóÆlxìiì
+–¨Š•9Éo ‘@&E>¢¯Å ¤ï„HÛ9K&9<3ÙaôwBd€ž¾ròt!‰+Kyë™#È$Pð^öĈ„98‰õ†ÝÏÚu¢BÏHÐ:‡%b‚Œõz:ÚÓZ/ѶsÇ"MZ¡í¸Ãâë~(z¡Ä3q|«¨á…Y]h…Ý{vjÈñÖ>î–CR2l7²ÛQvMíÞ^r*>
+aÐíÑKɤÜN¬cEì霨ô š¥Íq„EX„pšW ¾Zoio1”ŒŸÅð8å¾ þD2J?Ïí“ÈL\óù»E'#ªÏ‘è=Eõ“¢W
+€ y¿°d€€|š<û¦CE¶@œ¦…’b§*¡NGR¥¤ócãÓÙò¡ÄÇÕF'K» AÖbBɱQÅ"­H$·â¼
+ˆ}`áÒÚOQø×BÖ‚ª õ;bÚöÊ3~P>DIV”ºúíP4K{ˆâ·´+ˆ
+’Á®–ÞU AƳeæ3Í ä"Aò9³ˆé ؉Ö9º0¤*?´^˜ÄÒ¯)Ju¶Í†ìÇþÔ­OŸDt£Ääç@û<Û˜áV7d„¼V–šèÂÈᨵ¼¥â_ÃçŸ"?tGf56pˆøf6Ò»ƒs rxÁúú>Gñ_[L)êÖ/Ù䀗x!ïÚN— Ø8‚"/XŸoƒ3è—D9ÅNjÃúz »ÌÁV&GËG-„i·ø¢ØÈàSñ"d‰Åi`f: J=Ü4&-ÙQèîwðaP}° ÖŠçëï§sæ§05a—ˆZº,®©•æ‘Üë“-3>çÜ—ã­‰&‡™³pEþr(N¨Íþ¥7 `ПᶚcH„ÿ¡dNA‚¶ÆÝŠ`Yö<‰%0Ùë1+åÞÖ é‹m½Iïs\Á7=“pD4¦Ùµ•ü–åI|0±öè½ÁŸ@2M‘×#gÏ€„VÉÒXmcâãPñDNŠÞEÈ2™cÈt²Í›žnEx2l›]X*¶i}d
+ùø^˜ }´ `»7b=x–Zæ4HXŠm÷W’N{GN%Ðèjßò±Z€Cã¡ÓCÍïs VɘÚb\€Î\÷ äôX<œuÛÛyäžv¦eqŒW'#¸íw›¬%ݼ""„ô5D þ*Ѐ‡ŒXE w>öý§[1œ•j¦A¾—ÆöDø("ƾ_ÜÖq8eâèh›Ê|'žG˜|²_ TB,¥93qQ«~LÁFaýЕ™Jø‰‰þÐ'KÞÌ3»"kgNõ-~É8U ="ùp×:ô&ããmÛnAŸxH“‡W@Q\
+õp„r|wçÑqs+’
+ú:çV[À91q‚‘œÖê1%xài§£ó> NÂÃ]yÉÿŒ—Ir[IDO ;𴜇5·¼…¶Ðý·ý<3’%àÇoAe’ GNáCmh̘loÄ#hµ¨ß9Ïô·º>Þ‘àï¥Â§†"`ÛVOSzˆ°`‹–¥. /ÄOcŠˆŒ›Þ½LÆ·»Ý[ ú¸1ObU‰ïê hG *¥IdQ\‘)Âÿ³7!/d¨ë°
+Có{o…îñ9tYŒUš%å{{™)û‰7›½ú+’ Ð?©Ÿç°~ï'Ä®qžÇñ]—I}fDû¬“}þ)Ž¯/YâË«–ÛhlÑø®pWÍðè¶Ð&«6»VešeuKó=¤bêsÏ6Ù7^Ï!G|·tª»'œÉñVðUrdqC(%^¶^ISæ
+Ç$}–†œ­bg–á|.o檓q«J8æ^'jA¥~¬wÔJ“­J¿\SÒä´{%DóŽPßy#xÙî-Ð -øÓõZ³AíÚ£Ð(5'§gÿlLÃc;bîhA8i Ç®]ú¡¢ÊùT¥a[(wh{X+Vo¬ÐIlæ‘tüÙU= Òg¢Šp¦™æh,¾ˆ>E69¨ËýÚ^AÏÉõ{ß{ ©)Ê-Ž+æ±1t,ÉIýŠY: T««å¿ƒ|4rˆl•ÛäÚ"ØRœD+í§AEûÀè©ì4Œúà)ÞŇ@p|¨’|ˆÞ›å,sWÁäÀÃà™Å[g« ¨RYÍ2Î3ïu&¯/v·:Ì"Ž¬M+8¶“A¤ˆ3†Ê
+d÷V\ÙÅ>póÈ\‘?x¿`¯ƒmï`ðÇ]ûýß.þ¢ßÃî÷ñ™‹¬šâ¢Çœ „½ƒtx|㪽Ð]Àš â¤+C‰Æœ c?ƒ‚W<„êt¨À¶âÛ$2J7¨†g¨MM›ÙVMsÓ¡Ïe…4[%Cš™ùJ.¹ªÇ8,Çhô’^õÀ;%DÉWU×V4â8&ZU@,sCøvÓK0þÅq¢'õ4PæS}eÒdÄQÜ^O¯&9c˜þÈZ@ hŒ·XE`f ZùlôŒŸºÂ^;íU4Š˜Ï8`¤y]Ÿ‹¡¸v¨Ñˆ à‘Y‘¶¥!Îì¥ÅKÆ–ÁÓæ:i'Y•rà~Œl¬ÅI­»ËabF<Aîˆé ôí‚ h†DkO&yS“Êõâ³d‘×?ºùpîPø?7D àJ2HLòiŠfù§³PÖ2dGR>‰“bÊ7¨×6—j˜º±™ý’[%3pA*‘‘Ķ*Q7­-n7ï­Cƒ@³A`ç4j .†Í ‹4(’°§‰…á¡ËÉtég^”©ÈH£™ÛåAI;D:<‰± M¢¼4¶/ì+UñÂĘÙo OáöËæ%0‡2éÑ=n@è$šJ­ú4vÇ+ÅmÙ̈¢!“‡hv›_Q%~®gÏ®X»7fòÖ'2`l|kÇÿÚµ0{9w(9Zž­¢t¨È°É£2P"™-wÀ˜>öÿø¿.«ˆÓú‚^Ã&FvüêOuQ7ºªu*çUOLr¨–œ€lA0Ñ_âׄÓq™]YýÏdeN¹!|Ʊ{›ŽN¦ˆ†ÍXÏV¼í>h1Mâ-"À$[g XS…8Y
+\“ÒÙˆ¡å=q=çù¨ç$ǤšRÛËè­”‡àì#;üpíòÜî}u²Z¼±ãäÖ<AÆÌ6:È€¿ j…>”çç²Ý"ÐåHϘÇ;SjÑ/z
+/‘fqBé´äŠTêAh¾7ˆhD¡‹gzô"tßîvo^òäãÔ”R+Qv.BfH4c?–—ºÂ®ÁL1.׌Ñ"C†– ôØß{+<:¡Nr{Ò¾+bkm{‡ÖBœÇMbŠL3Ù
+[w_ ¶SÏêjt!µ»uè2Dâ'¿å  )Ž¯Šx¥¯Y`´v[¿‚\Ùà<x”Œ©> †©TêImÚ՛ܾJ3§rx¿JžÄD¶æå »:Ú„ªÉÇÅ»uB€ˆðµÌà KøbR…_þ0|â‹E%<N¶°T¿"á²›YŸð%ÄÔfÛ
+€ÂÀÿhë^6ÑE™+K°,J¹<ué×/äõ{X>‰‰"ä½òfð²Ý[ ^ðÇë´9¦ˆvéDRIJ]=ªh©TÚ~¡ÀNð°Òi\¡Q_&òûØ E¾6(#Z¾kBç# LsZîL{–4ËÜ !$aC‰p)½  À2 yVþ.!¡7ƒ¾~9 1 •Æ§×Ëæj@ƒ`‡àbé]вpeõQ"ž›]@}FP  ÷±9C¯µË“K•ÒËç-ˆbÈTœv3=FYGj«±‹B Ozõ%y¼t$5Ô)ž
+¶•B•m1YÅ
+Y¤Ê²Œ÷AÎŒ\·{ 䨋zQ CL¡ý¢«.‚°@ãÁÃ!‘Wqáb!€µ ¤Ÿ¤ð·¸€ˆ!‘ ˜˜Ð]Q
+wêG.âB!Dípg¾¶€Àj¸iúcàžµˆ6!Ñ1'wÚ(º¶ËÕ•‰¤)EĪ+-ä"-W×ìd¾T@…pç·® o
+UwZ6mˆâ˜ØÚ5èxNGË·ìVs®Yk³LkWÞ,v” Xhwë`QÀã–—ÓŒ;jÆóѳíæV€pì‘gǬô)%KŽ´²6Ñ oµÏ3"…ˆ³q@ƒœ
+ê[þ^0nF ò„VJÌ^ÆuAì6J+üˆ!Æ=n@«+h8jîÇá~»–Ç¡rB®œ˜çmgä¤q8H¶
+¦§ÉB!÷£·2{³—‚V–­œÅÊP³Ûf^Å$0˜Á™r½o
+†þ¢0åfeEfL–Óa0E\¨÷´EØ—‚ÍÇæ9å´I«1‰\ë¿þ#sL,U3¢´£y­Û€…ÏÌÃåŽhËГýãå’\É­Ñô´‚üÆí¡wá©´ÿ©O’àu¨
+ÕÖ{“ŽëY™ þ05ƒ$l$Ù¡uŠLÔ´Þôµ™8 ¯çš<Lf²GÎuÙ@ºŽ!"†+†ííÎ×ýå¶sÏ0j¬“cvË „ÕŠ% ¾Œ§v!;­ÉÚHŠ}úç ¸“é rUìk”ª¸˜Ê¦´!ØÉZ·‰ö­“‰üÕw|¿aÜòt¨$ýÑ®OßÿW¸Mß‚5É2BjìË@‰—P¥Væ“Â'Ô¦NM÷lÙWøÌÞÁôPÇÒ”Y)B%bû ž ¬–É»
+Ð ´ „M3]e³™.aô'v¶ÏNŒ@'fõÏ!]„&ö»ÿ5ŠwNò™žx¯«Ø6 5*2BéŸÃå„Ur 5MŽÄ ÐW¬IíÌÍMão —c,€(‚ ¡êq௠Oä=ÐE䩇ÎÕ1{rE·jß#®Ä‘ÏZHQLæ’ÿ䉼:AG£kë ‚¬LÈ…ÚyÂb7Å:H²ÌIöȳ¨CXlåeÕuEÈz"D~Q0O¸,£ôD”!w¢—a¸"/H£ØÈÖñþ›Æ_1nÇ=)Ϥ í‘;W+ñè*ñò"râ™@\Y®àþdU0¿4xAÂÒšž`Nî¿å<|‰O+‰Dš¶›8Ÿå!ʱ±çÅ?ØääŠüÿ©÷ù¿€¦ñê ÖHZó*”|‹šJªòå‚pÐ^Èœ1·^´—¡ðï1Mæç+«‡“¹ë‹Ž@fC|Ù¥J³S›W+¬\´|õ$Í%’…„ÞÛ¡§X‚ü± üƒê‡¸'žÔÞG,’Þ2 A´Fh ~ªduŸ$?QHmj5¹ç
+åÞ*F­ ¼âØvû䤷<] ÷Ó1Úyª¶ÓQì{ŸRªùÛ}žaµeÄ„—£˜‚Ü¥¼öršK¨…£é(n1×Ö;ê®à Ñé í¸¶L ~…bEƸ˜ÀÂcˆµ3BY©RhýéœKÇï_scŽóª;oÕ¹óøVbo¾µêŸŸ æ+œùbs¬°hÀËy_.{æ,„†¬VíÓSl½“Eó8²X+¶Ö¬fAŠ"ß'þ0$r9žÏœO'š ÙÄÙ)}Bì•›’5„šuVSœØ ^ïïÁšÑã©RŒt .àæ<«¯aØ3ODßÖ7¶"öÛj_ƒZ ÐÈöÌå\5õC…CÑ3Ì%œ±,F…–L¦›”¿âž=¢µf!Nk™…cÜýû—úÖúO4_I:B½ˆ§ö9P|°À’Äb: pÔo¤ âtß~Vö©J×ï——ªÜ±´nƒ2„o¢ê¿Ï £D¼
+|ØÌtraMÅÔ;ñ€H@íùᢨš×ó<7ýy>÷>”Ú ³½ð·Ç,¾6Í‚  †Z2¥Y Ž"¶@À`üFÞø ›É6ø#÷çDþ×í«Û«C›*+8ç°úu…ƈCìs˜{ÜKËÎ)ZeÕ¿d
+t¨WKqåûÜ%ºó8-aŠ–Õ­Ëñ5´!Ò¸x4QŒ™ñ}CVLÖ~S»ùg×Põ«´õv.Ǥ—–‘eÖÉ|mbtžõs§”[#€@î¦ÉP>üíuK®OBÚ¨]ã´–Z¢&Ù̘`ÌAŒ›örùÚ
+f̾¥S>¤áÆ¡ƒp|)ƒHFjxˆ•c?i{1ßW|cæ†TaC°D#iÚªá,[¨çª ‡v¡ØU¤çH]»F\çh÷á–JS‹1‡#8irUéo$]U¾<º“pf®Ô0Œüs7‚·ë~ºÈ‚?]ÐŽðQ‰‰B"¨É8Íž
+Kç"‘ƒ˜-e»‰ É‡Œž
+zY{SjOçhZ&ƒù_2§¼]ÃW³ñ†ÁUgSz'ü¤Â/9çsUÐ
+Ø«Ø=pgb°ê`Ö¬ =°Ù=A¸££l‘°ÐÞW]A­j8ð?ŒÕΡà„äJ=É›M§SrüÖ^µD\KJtV$»Šy&KAõa ~2u™”ч³‡•ÌÖŸ±ºI¶ˆ éë'v«= c±¹ãQ=ža£¿ÛÔÁBF™ÞŒ]o„žLP1Áβ —ºAÐ(6+Ì¢Ø)…èS«Þ¹ŠÏ¥ÈóùdVÒ;<¨­ÛTiÃÄžz?ËD$Ùàëñuq@|nº:Ȧ@çÜç ù³3z'Ȥ¤ÁãœnÃpR+u興Y]¯µ¿¯"fAÚ¾d~«Œ¥ŠHÓùÃñÖ{<ÓTIXÔƒ2bžH‘H®}­7þ9r‹rf¶«JFóô²!k€c›Å@P‰àRúOø³Œ;ˆÿïLþ/ãeŽÙ‘Ñà)Àr_ı¦uN
+:Ýôv}Û"bµ#ÃIü<2È°ñþz4#¯‰èÇ#PH£²åšeYQC^ Ü1,­5]e,~²\EÈ°Ì8†e2õÐ iŸS‚&=|üìÅfÞ
+z…ØUƒ>B3žµµÈ°&ÄcY$Óy4êÔôð° òþ‰ Óaî³Bf}:‚F¥ªs›’\êY£ùˆ¿+Ö_[Ö6æ±Òl8‰¼ÅoÉjˆfŒ~û˜Ê1(Tª±ÌˆèW:fº¡¸ ÞÉ0‚RÊIn­0ŽœµÈÑ;êFÔ£©ÛÓ°‹ú@¢B´\ÚÇPû^ØÔ{C†¬*ƒ?Ânã3tm2„Øt ëªJ—`õØÔuï³û9úÿHž„'¯á ›?ôD©`gc%Z*njø@é8” ?¿ä¡Lç¢+EÁX–²¡ûÞ^ôfh»? ‰%n¶Ç±Nf šuo؃àä7ä™pT,ñò¤LGXCܪ};åÖ2קÜ;ïÓ¾’Èx$œþ½Ì@Æb°»6ûˆ‡ý5o( þƒ—ÏB& °aÝŒ$E@±­»ÞЧ’‡cØ'p>åþô®»‚¾_yÓç~z‚¨†¢£—ŒÜƘڥ…T®§6P¨?Õé€>=_ú Kõ¬ÔWB_©ºL^¨÷æòîGÌ÷+±)üû-¼ÿë?oü÷ß•‰0L
+¢sNS,‰»@h6Ùª[Îò“"G÷P± ¡uYĨÒZȺI´‹ÇbV¦ ZâE–$­ƒào
+‹—™6ÈH\ÚÎ,ÏtGI"9®&K=BGÚ|(jUÀ½–¼Æ^i"êÚò¾J¦‘éÄ}4ƒ@aR8Àj†áù£29 ¹ˆÁ³–ñìè¾¢áb²¯ÜàíJ÷m‘(£¼p†Å}ÅÜ› :Z æ%¶—AÞ@\o{sQ·Ó]ò{ÿSÐø"“3rÌ¡…IÉÀrÐùÄ.)˜¯¤îr@Ÿ.9,ÅŽ¸Í\°_æ€Ý邤Rñ=lçÔ1AD(M‚¶„@—HŽ=bø0yx¥¢´|í«`^“´Þ xFOaOÂ7 øÑ`c—ê±ÔÊÆÄÎì@¾vYÚ¾‰0žƒã ÷dËä*ÒúRd Øá%Ù)«’|Ç87IQ³„1uc˜¯Bm)øÈûfÇK5ÛMƒ{ˆ4iðß_(ºöz8ýñrp9x¶ð÷‰OhnyÏ
+‚2dúJæu3T0P"D·ŠüÜ1}³¬ºAð{VDØÖH „¼Cc¦8¤O1sŸƒázäT7ãHfˆÞ¬>¯Dæñ'l ¿Þ|P–ýƒóp$ÖÏ7ú9Âô2t1íˈy¢¬bÝBŒæàžIütù =µõÈ›A˜o:<—DQNè´”ÌÖ}ƒp¢\«°²5Yo=äoü"ùmXÒbM6°e8\_r 6: —6Ú„»ýs’âÄ„!xÎÆeÒPá,9ænC&j£‰‰öÎh¬<|Ä’Ñe_•?hR…˜­ÖЉyųbHúªVV«0ê:ܦ”üóþ2*¡#*ê¸ #¨,¿ª ’Ž]P´¦÷IJÛf1RÈLÐ_… TSË3£µõA'0fl6fŒ›Ž Ž ‘Ö‘P$T+E•H|F±^K³}"µù‹Zä8J£°eƒšø”sáXI‘
+¬—OÃfÕqQÎÆsUë±°Yãqj>á§IÖK{r
+*éÃ)Re º?á3­ä˜ èF.Ï¢–
+ÒuʨT\LK)¸ ÚDœa1« è¨?£™Ì{Å,ÖÂÞ–4©aOmçrÒù*ÿµoªº[B 7ÑoQêôI)äU"‰d9” ‰ƒˆiA@:»
+½°Î\Ðê$bƒz–7žç£]bÒQ$Qzvœ¨Ä}9¢’¶Â\=È d´M/ž=‚™)ðoD,'ãsˆï^·÷ÕÈ NÔJ^qy5ã™Íû{³¼
+j…&Àe0HòaŸî
+–âϱgY?ÅZQŸP 
+„¥1ò‘òôW€;òFÐ\”Á8ËqHÞ²´Hþ.Ý+º$œm{M„™BR³ÏAŽÖe\ì1E
+‚©€Ë‘šÝö[¢!¥1M¬îuSý€xn.ÝJIKgœ k}C‘ ¢ñ£…²<¦ŒWÜDpE …Ø9˜X„]Ê;&Ae ÛGµJ^1C1Z)®Õw{­Ú5Á8GdŒôu‡XÐÉ[[¯$Ö4.¶Ö÷{îËê>“ÎÒS?^šLO¼Û¨@oÚŒ‡±kVÃÒ½utëNøŽžòM;¨c‘Š,ý§;Ù Ý »ºËá>*}½ÇhÎþîÜeãýzó†×Y‹š–Ù+å„¡ýÃäÆ„HAMì¸ Õ¤zâKl/R7–E:Ç#$g/®,¯rRO뜀ËüoîÝÆNRû’LhóW 7¬Pžôb“‰ªã}<¨<='v©s*Qæ)D–7D¢‚çé!}ž:Ð#¥“Š’GüiR"ª‰qÍgéá¹(;mº›SFA‘T÷L„3y<Áà)ÊžÏpÞ@l Æ߬—Ÿé¼€¦Æ5ÐɨŸ|.ƒµ-ŒŠÏ•u-›”ÆBÉ‘ç‰ýq’å Æ9&‘
+žxj† û9t“]`7ã–§2Ý¥-YÑ…ßж81‘ì:è¶PW$â/}ìÙ
+Ð. E¯^@mïô)ãnü~c)X˜¯Æ³ãiµzزoÇà˜'± gè[bz€ ¤ð$„¾ )¡3jËIIªâ÷Œ\SÞk{à±6˜1 ´SCÓŠðòt@ 7¦Œ²Æv 9@àL]Ž+¥›¬¤~z*ë|LçKðsDûÁ°‹¥4(ShŒ çÉŽ‘…™!=ŒðþâЊØ'í‡#¶É¨Œ­ImÐ:&VëÞsPëô1LÑ=AfL©²ú›. V‰™¥ÄÔt¤uev´
+1ßP”ž ' Góg3~úb¸5î,KÐâs'0tT8»/æ܆}äbOÞ(eÚ‚²ê $¯L„ÃyOÏmñ€'È7½€þAdkÎÇ·@½#ÝòZg'Ü"ëÀ{ÓVAUø?‹2D±y¬;Yð‡¨¨F~Î1„…á×\r.½e
+)ÍßÞøêhZ®c"›b
+±ä“<ÏÉBÃN³©§ÙÝ9H†›ª»>ÝFCÁ3• ñˆ·)Ö؃ÂÀœž+ãÈÜw4{É "ËǸá¸Qñ1È…Œª
+ŽÙt- Ãc!@ºã
+#³ëÔÎãï;ö!0ÝEÖæàH 0G(DOäÅ}Ÿ+è…}| ÂÓ¡ŠÝé·1“£øó=˜å¼¼)ÛVµ#8Û`ŽáˆN:PpœˆY,>×v*¹–?oMÁ–HgË2§Gì¨aív>…ñrȘѶ¹‚«ã Å²ó=Ö)›³#íøy*¸ˆàtÊÁh_HÀè–>]sÀú\_ØŽ¾·øz½8lÀ¬WžÏš–ENì…‰KÝ[ú²í«†x›¦ßlç=¦hƒ5è|º¥»E–Šñ¾¤Ïßÿ
+H‰Œ—Mr\7 „O ;Ì2E
+½ýøøçCn'ÐøºGí.Ñ/d¿sZS·REëí×÷.Q›‡„k»]õ&¥.V'Äz”^½€ªReðß Ä'„ÛxÎë<þkUFéE<lß\»›h·rUHï½ó!þ·Ù|ÿÆS£ 6|ÕµNÒä:IïÕ5‹4Zõ™RTµ••y
+@ªfÁRïÜ5BèÙXo¢K4V ·ÏhÜM† áŸõöóãtظGW+á´Âã¶nZ[éQë„Œ¦46´ô]›V*Ìñ6ú ²^Þ½ÒÊOÏŸ±»ç a Ò[që=DmA*/€eœdBQ\«5i&»›êƒWókd·©¹ðʵÆ](C4+#Ž~oc4þÒÂØì“þŸÇIù2)ï÷4%ò5c}Æt).ü:‚Æõ=E›®äïÐdÏ©› sƒ] =› ¿BDÉ2F¹—Öà-Ξ®›»ð‹B…'H;—’B | — ÓbüæÂØÁ‘ÓÙxJÆœŒîÕÕx5)Qh·WßA"ÂLùa_·ñ–¤£8sŽ¡3`>T9qá›ó1ÔQªm}‡¶{‡TCèÇ’!•ìúüÔ ñÉŸ$ëÆ<LØ;¡T†¬6j¶§Ð…Yh.äLdT£ÏZg…’Ó‘4ÞÂñ¨áÍx„¬¾oêá;ÔÈÛ4«¥¬t?Q HÒ´2b<Ì÷QTEÈPF…âQÆíD÷¸7þ›RU“#„ uÕ¨ò<èO£u fJê‰\Uö»Órèî“æ@Ô'h7¦L#›š j3Á˜¿ ÂSÊVU%uéÇñ¸·@èzS4}P.%?‚š¶†8””ß&„G[à&*'í¸ Ñk*y±%]1Á±gùšG1‰ëUo[î¥raF/¥#Š[5ºØÂúH¥£Ä„˜M¾¸ê²Nâ¦é UûËïp7rFìw…üÕ+‡Ÿ:¤àƒ¥#@µ§t<ƒUa™ÂT“XúâRÒѪ±žNµ‡Yãð£j^õcÂ
+¿&gÕäÏRÞxªÁ,=§£vp%igë(î/Â\3Ì=[:!!E*õó}”üð妺 #%ø04æwÌÉKx/Å^¼ü>Û,^@¾±ôóã:ñœçÈœbÚ=Þfð鸷@ºp¯ÐÒ¿×jä4»Îûµ0¤JbXw¶„áa™°yy;YBBhªdxlv”r2æ½kÑB®p‚\íçx¹ô™>ZKR@iÂ×¾ò7½ŸÒ¹c¼d•CZÿü8€Ö›÷É
+iË°ÐtïP„èL¥:o£H)4¯¸¾74‰5«e$þš“Cåú¬ÎÞ¡þ«Ò„%§DaÞÇrüƒ•³°Hņ0{X!¯­%
+YÁÍ »Àïq=lQgáx ì€ÅK^¨Ob<HXX*ÖæÑgèÂíX +V<! [Aå$(ÜRLJ‡±,!I°¬’ì~‘ˆ`I"Ì™`ð!ôdöýÚ'á83¸V„è„+öhBèJÏ­¦ˆ ë¨ÜMº¥Þūˆ1m«¥¹ÓxM÷g·Ùjh¼ ãëºYÃZTµmbàa| 3‰µ¨]Ž:RœÕæWÈËxIßǤJgˆÉi?2ú2a©S4jlZðMä Ù‘ý¢ÿîWKà™ßJ‹Ñø%pÔ
+9A0c6“7utŠ7ÔrZzÞÐÒÏ™±ùiA­JßNI0}He_zK*Èñƒ;þ _XTcÇ™Ç
+Qä~xŒ„.è†PñBÞ8@² =[’çw;~DKÆäv%[Þøiäê"6ùž’»ÅvGŠI0ãÎk;}ÄœrOïxêLÁäš…·@§ˆÓXuø›Ýmˆ3èûµ‘¯‘ͯøsb2Í55~o10«e—+«
+Ù܆ùÍæ„ý£µZ¢î,A)ãéáÅ‘½jÿÞW᱈CCéHü¨Ä R[Îg7¸H9’j“`DÛŒmaI¤1¹Ç„¡:ƒ”ÖЧ
+9·GºkŸ\aÞIw”i¿]ù”ØßÖüöî^×o´ÑŸöOæš’ɧˆ ÀÍÈâläsí¥KbÐ#]bÍÐÿœÎ*”wõÃP.‚Ds¨k-º¬“2gbÊŸÑ"Z覮Má[Â,é´ÏÛH¥ Íߧp`l‰ÓO
+ `öEcòv„H7
+I´QÞºS€ú– ?]ª‰‡m*»Ä—óÅqØ*‹AjsÞ-Á¦ï +B¬©çÝßË·—ho{9Mp”ö¶˜žb³uªwy7ÿ&+‹á0aÚ×Èñ&¸ŽnÚÉZYFÄ ¿[”Q±­û* :JË;DžfÕõ2Pþi»pÙ–¾¾fðT³¿¼­ët“^!ý¤½š·ý½€> DHb©ØµºAH vkeîw‚nÔdûžœŸB uŽÇI]ê,]a aw•!$vClOÏöÁ £&ûœ
+t1/Öè•Ë% åkn€P-xM
+ɬ«ªÔ°aêNÿœÆvÁ¨YYѺÚäsü‹!X[VÖîòu¾< ·u0Ħ›‡Çl
+pÅ@+C†…aŠöM±`‘Tž”µØ
+“Ú~öä1$_Q„C*öì!âû|”CC¶ö9M’™±ÇS_6]¦Iƒ˜>0Æêì^1>ñÜÛ}ÿ˜ÛØxºß½8·!¾—ØY†oúý“‘y {¹2Z…Ôõ€Uç¼/•Å°LÙêô‚L5Œ„ˆ<¤‰#‚ ¡Ö¾?JpâÍÓÔ¦ æåù©¾«$ ƒ±ÅÏ”ºü`]D¦†t©·‡âª¶×ú›ú"?$q2TåŸÃ@+óàOÇVI} ›žXøò†
+(»ÜýÑ%Hv’%˜›²öÑ“ügÆvYS‚¿ùHø'ísèt[ä Ò8BRž»Ý„ïй8,ϦÙ"ß@ßZÿé‚0 }>‡IözÞA‚D$#WßÌCbÁ‰Œ€’›‹Í2á}­ï—ãÑP
+vèøîÌÀ#_Ðv³”)Šî:ê+WzÎ\c3!ëÌüôÏQ¦mÜÊ3c?Cû©2Šlû• !VÜ Šà™l•³¤=öý0oζd´›éH{0v@è ) u‚§)CYöÊÛ<""ŸÇ<ÏM^ÏC{‹£as>°Š—NžPÞÊ{‹o ÈäÊ8k]ÈMTMRþs”(ÓGéö²A·ë~"C(Câìe ö¸: rš¾C¯ i¬(ñ£°´<”\]-AQcºðVíåÍ´¾ÕÊ ˆ_I:ü˜¦)FÉLÛ§ ~F\àïGØ˘4ãú†@Õì$K1¢±¾òšŽ¯`2»NþдW Ñy5Ÿ(?
+ÇËëh³‰Dp ›® &º0@Cú…>ísuáŸàKÛå (b¶6íé0Ú8`ÃîïåãžÖ!Ãî­ ÕXÞZš%>Cýhz2í*<hW)Vg$a¹XMRWÙíàÏqjº
+BÉ6Ôm·€èaâ‚lŽ ¾Sü ÙÚ,ëI±ÊŸSÞœÍZEÜœB¼ÀÞ3´`U¸¼¨Ákö‚‘îò eC*º71Ña:»‰T Éð„áqO,>Žâ˜qˆ=Ç}%› ÜDW°ã<Jñ5³a3Ù£9Šô#»UŽ]Å‘Lù¼´Ì8"Jh“]…è–ˆäôÚ ¢CšÐ”géýÏyM¤äÄòÿøSÐu?²¨¶ÏEu–ÒvD¨øØÆÉ>çëĸaÕd— ‚neÔ×´! çNÖ|#ˆškŠÖ7sŦ´©`µû(/5-‡ÂÓŠ½™šE
+ Vx&ö,Œ pÍdÅ)JÝœdbðPÕ¬«F–N1 µ<S'€ôóIOõžžæ¨ORå“ÆeÍnm@Ô”„ü;o:7]A|zZÐÑ8_þÇ0èò¹¥ÅÞ£ø)/ÊŸç¦[m Ò V.p
+´³Jk©<´éß»çÖíË)ÎÈ\¿Ä½ÛƒœN¹Ë="ÊÁnº­AN“ƒñaqe³ÿ\§_ï­ËyÇ”é©Ä—5GJxU•ž¸à
+ò Àí:—0þë›a¼þ4r)‹‚çÚÔrÉ–_:¦%´õêk½âGÅaŒK÷l~J´*±Ân>>‡±›?]ä“Z$îÎÅÝš‚²˜]…+«eží0€Šš)IXÌ 7Ö‹ƒ~iAäFDúixu•âec}اs$“ å0Û?*†ŸÓÏt°sèg|0’w²ÃmÓ¼{F ™YbD%Ä!ö)M‰ìþ
+FÝ Ÿ0|-½½ÜÓä[ïçÜúÀÚÁˆ«â¬ÛN¬ˆ@ œÚÞí¼ŒbJþÀ»ãÄÚ|9³…3"¬áEð[´ïšúâ®Jçž?±Æ=ìqÇÓ•U :ºÆ»3……ýÒž÷¨óX†€å§=K,‡Œ-ÍaéŽÙàG&šÕíe‘âÁeó͆bOŽ)H+ΗǶémHv£Éöa÷9§H.bžŽE¸8çD®d»»2ǃqSKïƒ&õXžŽa"'–°‡ÜÏÇ”ð¿Suçýâ­ýy/Œ>‘cÉ›ÇòÌ ÚPÄþCŒËšyyâ,©ùzgVé“Ä Kª•Tˆ¢„d&nǽA™`SŽ!íQ.fÆMÕ͹×V\ÖtÈAv¶îV8Ç0€µîiÃ×3Ò…_³½€ÜqýUaÈÕt@UÍE0§MCÙ†9^|=®¿®T³iå6ís­DÅÐ`m\û…Vò_ ϹéÏ›õÍ©²ÏLÙŒ1…fÔ
+)Sôùkƒ  ¶œ‰ZÈÂ^i×1@ÐêÁxPc –8ã‘:»i
+P`çzfs›í»žÂ½Ø…µ„µ‹"+þ¥>ÔP6¢º-Û…ææ¸.ás悪ĄÈÔ–™Nô÷(¤Œk")&VìÓ@4œžÎpª0«5rûÔ¦‚`%Ю¦æA÷œˆ‚¤¤Q6$ãh"D›ë)οIì÷¾‰wŠÙùˆ#ß㔀yAËz}jÕŸ[þëMÑU!—ïcfg;l:xLgARìþ&®–™ôåÙ–>Ï€O¤’á4­C<— #Û
+ÍÊîjïKÛXóØPFÝÄys@05°SÉÜB„ gÔžÃ+‚z Í  ¾§ãŽQÞCOWBqæS&uœ‹µ´R‘Qÿa¼L’ã8b(zß'`ä<¬é¥ná­tÿ­ßÏD¶­*”ØŒ¨h¡³²€?ÐÎl—í¥ø1Ÿ49™¹ƒm¨ç¥/pF
+ =PÂgÛ‡ëÌœžFsÅ›úM˜°:¦º%UÄ3@G´—Jòì7ò¾ Ca€º^Ë„ ½ÎA™)çÃQàÒU~ê1Yp(#(lÁæ:þ̓+Ri%ê&ßßO Ä‹â´Û˜J!IeÊ­NÑÅ“ø¤ñVQ_¹6èBãàâR„GŸQP¸1æBT8 µ]R¥&àX‚¶K %@ëÇ£Oä®uéÅ–¸9R)?ý¸b\NÕh¬Ûh“ÛØÊÌN
+IÁΠ1ev•îjrJ Aÿ±³ù瀞.fÂ3µSRª^3;Fô
+f8—ÁÍ°Ò"ÔΓà[h€’³ä–äO’±ÙÇÐ:­··ÞÈßa+´ì.ÅÒ YD:|€Aã°È¼˜ÉíÌB2XNqzëñ˜“hôF ƒM Ë¾Ç©„5'6ŸÝ÷œÆLúõŸ$Ú-*|”üïE· HIQkRÞ%RÂ~-´ »)ªËM’‚CÝ HÉн‰¸Ü
+d |Ó“Ý‚’+¦š‡"‚±`JÉ€N‡ìþ |Í•ñ
+Ö‚*?À·¼°ÌŽ@jòÖÖ"G !s—¨•èžTv— É
+#¾(w4irbypGPx·Äå[ä]ˆ2mÕГ“À²ï¾(r%1ôàå¤#¶Ú™)—žŽÿ Ö¨4q.Ó$휻Íã aÍæ$äðRÿl:úp%X 8ÛµþaŒ ’øn8ŽÇײIc”Sraî.=¢‡˜Ðyõgz—¬2Ì!“ç‹©ßÎD ŒÄ¬ŠËjÈoœÜêObÝ
+€[³¿9`„k˜k0‰¿Ò•Q+ž. ÉëMóÆsi0j8 {ñ ¦¡)¢ŒP¨Èg+8à{²ÊöIwzñ&£)!‘§J2:áÓTÀØA$ˆIÇÎ7}½Ìë6”xŽÚ…1pE}
+,
+2lrL¶‚ŠhBT4Ù(ÌhöW/}l ôÀ
+`®XàURñqÀñtŸ5ðâ‰a±’1?™›B¶jš…NöÏQ¶Êb–˜Ì³Ñu¾D«¡]A`«ƒÎcÀŒ
+'vèvsG«R?#&QÒéìŸS3Ÿ£ÇíÁiQ¤ }Fì–¬Gq2ûÀ zÙÌË9]ü>…‚ÜÏPQâXëÕ
+`|0KV0/ Ñ
+“ÎBéVñ¸9Ö•vÖ½èTººˆò¤*…
+4JÔ­„vã‰`jë0ÞB¶…HeÃâZäNœÍc´ÚsâÝ"=.bê«QãçCtˆ¹…! å“5ãE0—!ûmÎððåR”žÆ•y¦?0Ôö0vB ¢¡Õ<]s ä5ÜÍ,¦D.‘ÙÝÃkÛR ˆ˜¯Aï]yùç;ä½²WÌX3@KëåÀ/Ï
+z÷±¾9)—"äC@¸Êð#IBM
+{n…Ãp‡ötLo%Ùðuç.ÿ·Þ ( …ëãŠÑšÙ[ÑÖc.‹(ãhÛI]‘. ‚e›öæu…PÔ=ø–Dí›}íYZÉôZb¯ÎúɶÆ蟃 …þØ0þnvåç^¬õ¸Kh±ÌIl½ì.F ‚Qàáý(n¨ž÷†„-Bé| </£¥ÐÆg¡o
+’åÔI“G•þPòH¿þòŠ<¸+¾ÁÁU õí"oo{«èB þv]ŠÅ|ü­R¼R:C‚Û@óË-šê9ë±5eÍNºˆÏŽÙ1ðg$
+U(ª „÷c‹Í®AŒ}öµÆZ”XÜŒ‘ò
+$€6‡=dŠè†4³ægJƤCâvòAc«êàÑuîØ„ÝK:ÆÂ9›Ï!„«¯¿ü¢„¡p!É2ÿt‹PàR’{±û$ Ñùê®m K‘µ#ï‘ù©0ÊÞWعfNÈbH§Øc®`D÷cXÐW9f‰TÅèÈz0˜ßê”Ø8•$KÛüsn¿ßæ†ïn¼÷æ†ã{‡}ø—ñrG®#G¢è
+¸Ù1 ñ7'äÊ(ÆjÒœýϹ@âI¬‡R?K”˜Bù¹÷ä—Jý|eh4_aÍW~ÏZ8en /ç}ƒØ
+Aø¼N²>E§ÏÝ‹nœšÖ)þ[ç°(.r³1Ôwe_kÈiî
+1¢:%òWÑ„‹Ó;ãìrY§Ü[±å“ô2‚F&:Ž»B˜ˆYÍïõc>fîppÛïo‡ cÕÔ•Q'ãôÛþ¹]Gðã•9ý…½&Ñcô
+hÙNJþy bÍ‘ïY¢›&D!0#;µõ¬<EõZõ²Ê¼ãÌ1í™þÍ‹z‰IìWðÆëH½„’ÔÒÕe~ãë ±Òû”Í®ƒŸCæ§ÚÄ$¼‡/ÝœCµK¨(îé·‘„cžR[!Ú#qGõÛÍþôÄÁès'>žEÓÇ’z»l¶{Æ‘ò±>f l1×ãh<?ü0bÏ <s)ƒP´UIÖq*&Êh¼±yóÒŒyà kßN…ÒÑ´h v0gðÔ¡†Õ'ä'8‡þcÿâP¡Ñzð8c7úD»xÑhê""@Á"(Oßʯ8ŠÕÆ
+i²´\GŒÛXÎ3Fͽ…®35FJÙ!Rú!®P¼çš²VIÈÙÁˆÿc¸%ß’¨©†$¢Y£Ï¡ŠþôCØ$Š°°²ƒ[dž¿¸å¹áAòÈÉŠ5›Jf]ܵ\ÖJùBÌIõ±* ãαý!ˆnfÌ¥Gî¤[˜]*iq<>Eaì‘ÿžŠqâHMYEz¨gUgØ̸Œ eºM§_qb- Þa\QÛa®9€Áev‡!b‘´òÏ®†M…•CÞ©—~s(Êà?¥~Œ`ÂÆ 3ìZI~¾2[ #ÍcFh´ºñŠ‘¡ˆí 
+JXL‚¹¥z@E”•¢•3bÒ?ÏKæ_¢W•ÙÜ-”‚
+¡/Wñ»‹Å%H<´‡ È5n%x
+¢äÜP+Ww¿‚²xh`Æmú¼ï‘ÔïìÍö†ØI;]ÒcÞ÷yúÝb?^°aê_ÿy Z8™„qÝå?b´4ß·ðíï7ÜH³›IÚªN¿!O¼î:P®п‹â9~¡ü5æÇ!fp-
+‹{ ÷.1¯ÜçÇä‘ÿ÷í¯ÿM‚©ù²ä–õ#XÇÖ£Ÿ(þýFUi£Æ’ÓÔ ÿBß;9Ã
+~£Ö&?žÉ)Ú$
+[-çŸJcŽ ‰üÌ9©ïR!M¸†@ìOq_ZÉÓ› ¢RtH×–Ö9¹Qrìļm
+ÄÇœÁ~›ªå ¨ÉÛ¶c$‹²ïO)QY,Öü>ì]ˆíÐæ•ýª; Ä™ÎB58Y\zQoIfH±=>õ5(¿ÃM›®Ðüœ!`mZm‘åt$L9Žå˜À,‹àI ;’ñ¨cJÙ?‡jf%Hhå5*šçüš€í¡áa4æ±/!ÞIŠ‹ÊÐbwç Zó°ïý©0é±âÇR0A<½ú…ÿOy™$GrÃPô¾CŸ@ÁyXËË>‡Wòý·~ŸªTYÌh9¢Ã¶Ô0™>þÐI]+ý¹ÁAÖT„ï¯ÊÚÇsª¸,y‘`q.þzŽtâ
+ú'SÅ#ÅxÒ+ãPDT€ P§œ o1ª¡¸Åaµ鯂—3€­ô ¿W§Ð!ûÞ.rDà>™íPpáòGf]ó<QR¼_…c£U0AgËÛÂmg „¬z(²5ד¢l%Ò[JG¬„6 Ûa+×à#‰r‚ p”²ß…_!ÍÙŠ )~d|Õv˜)ÃÃÄ]2,FmWðäâXôf¨K’ú¢“~X
+˜”’j²íL~Ÿ£wCýllÚ% .᧰ì"%À Ù]r»DÜ(9*å}E"×.ÚËÕpŸ~•}΢I>¹xs’Bzw+)@ „0wIT
+åníPâöïU<Nç°#Dº®·¯¡¿±¢Ñ­¡CÛA»Ò£u™L„Aän±«’
+0ð%)L}î^&„?ì=uá°Ë:¸Ìáò —=!Í|ÊïJ&\ÃÏ«¦¨îÙ²T ã¬ï%`Ð\=2æ%̇`§
+x
+)ˆ¼íìç¾GSšPß·úË*pÓDÁÖ}ñåý ~è7Ú=|CDEø¶»®×SF®Y‰˜ê• þæ·"Ý ËÐÇ›sPpR$¨Éé|U¦Ý|l”E?W‰Þa­~`o¾nŠX‚a†MÓ°Ë"ZÙ‘ædIBùr­>‹»Kˆž2µ‹ *€$K–=µ§£1d–å¼Ü»Ozˆ’7o!r1W|¬éødˆñù1E<‰y¼lRA†Õ@°_„ŸDÇYlQL»Ãâ6lXÚÇ,o”5{F‰ƒµ Å·RÖ”¦˜¹Ÿ.±$Tó ½Î_³—$´uŽàØ72þÔh—ŽÇÃsîtD V®ÊèfÏ ™æL·¾”|þõÏ_ñ×ߺ™%,Br›îØþHAÚ¯oŠ;ª„iKyušY
+‚£ñÐûùœ¸z£D—v/)-0˹K´ª >© k¡Í‚_ý*L"bÁ¼Ç’ùªõÏÑÎX‰•HÁpP¤ÖäýÑ™~Sò‚?»êRTH+S1ÉËvÕåkp®r• „Ž¯Âsæ1'ÄOú_ué–2aD‹ô¸ÊRLY%‹Q„À
+,{‘Ñ,Ùé@ó1Ïâ)q.—åÙðÇTðÀ…Èwˆ£vUSìaEa7+Š¸<šLª‰¶2¸ä¾ºžwwdÏ©ê¶[”4\°`Íw Ôvòà“±,»¨‹<¶¡¶O¾ìø%䜸‚ˆ£0Ø«&ã7](T¤‚+Ç"òç׉¸ˆA¥@­|¡òÊP\6Wf>ª£é åêw\ùVT>Dð:?®FSÐè½$:øÓ ”$‹ëï’.C†4¬AŠ¹¨(‡X¶ƒ®!l»®*ïÎë›k|F—óÚ
+ÉØad>e0Ú¾K 8RTY‹´JÀ´Ü‚ÞæW±C ,ç-Ûâ³rœ›C¾ì‘ë¢L˜À àÙMèÂucýÍà ÇWhéAÉxÐG6³ä˜4˜MD–Gå áþb
+ª»Qfùpì#º¶6¡¸ô(u7¢ð1B&ö.[pu`à ¼¡ÍqLøŒÆ©¬“î€P< u+7
+ç]ÈÐVwå<\7;aa±Ù‘å6y¡rûƒÜý&À$U97#À ÏÝ"½c²D?L¡•ðY•8kÚ”’âJ‘Gõ€8p{¬ §9 ‚ì&qLÏKƒŸ§fWêDâyMðÀÊH*„N>Mó=¬žl’`ŸBvA0{!ÇÁW4!Ó'7'/ák¥Î·©‚x Öcè‡ÛÚŠ#BÕñí9‹Ñ¿õ¸yýš9ØHdlä»W1> Â2Îvîd5H…j¼7ðÚã¬5œ ›ÓãݬÀn 0óry–·">ÊyÜಗy%bÎuNø“"(/òE±žq|žë6üI–釡DuH3C/Nßý‚D,M¾ˆìÀÛ{뇢ß7E²vú9ˆºVQ[ö˜ÆiÊÿ(z¿ŽØ('quü¸_¿‡\i±_G§ØxlC®?.‚açŒÚ¹ ]ôÖÌKÑ×O:þ{¦Úì*Káp‹rd¥Éžï¹\kNÍ̲•ØãÌ ÿ¨èrÛ*bq&øJÈÃñ“îŠx¬;1åî²?–,nÊ\•^îFŠ=Ì:ÚYVâP éLF:Ø%Z*ñ ¦[ §"‹ÚL›>„Bîbfc/¿Š&;`§½L‘_04ün1íshÃX¯h†¢Ôñ¾UÌZó.áþ¯ÙŒËãf=%‡Tü&Y:\ÂöÝr˜¶)PÇ>%窬شQ†çf÷‰ÔírË^x”‘UJòdÚmG@¼Ôô+|€¡•`$A‹g(á™…»QÕa%¨ñ"ˆ]v‰„%×eÅ%vÕÀw”°vè|Ž ÊÊ6ˬïDff¨}W$>uªcÙ?†#á
+†>ìQ®¬ò5<ÊšÃÖ’ò &Ú¸Ïa@ ÊÁZ‰¬VKbCò!:Œ`¢ñãer%×­Q ä-àAbÆZ)SZþoÿ ó‰¬BöF"»ƒÀC1¼,òßÝ@;—2])iæξ]HæciNg;—ÕŠh}Ï>Ç´?k(BæOŸ¤¬E¡}üd, IN1^Î`Æh8†÷ÈÊ`}½U[Žø“ׄsùK™^E¸, în³^ ÞM¶'¡{Ëq;‡@CŸ˜°Óð—/Þô”ú
+>£t
+–wëÕ¿©¦îÎ7oÏÎB¨åÎ\²FX™yÒµ¿àU tÌ‚Y>¢ï˜W¦E * ÅǸÙøä7Þÿú†\mQûñ?!¦·x-­ðÎX¤¼ '…me³Å¨HŽÔo  aœòðgq(‹¬'3„»ÂH22Pà 6ÔûhÈžN¬¸‡¤Š]äÔSõsÐfF…DУ T¢°ØA`ÀõTdÃk2„l‰­U¶Í‡Ö„Ì’œ¼S¤ E-dR³Ó–$É#s>€rÎ}º«ã; `ƒpÔV\„*ʪêYî2á ËS¼‚*ATyx"R¤ÑhóŽÀX£'Ã(û<㥔™øø:Ýé¿€¾>€D’}÷ ƒU”{ŽËà†yÁÇG;°˜pk+cùÃ\Œ~0—åì•’ÉÒ¤¤^>šË¹â;稕ìu^Ãã
+ŠC —Ñ”ÿb °g4¡6½ÅU-I`[YÑÌÉ:óI“”:Ï9Ê0œÑq,
+l͘íÒDsh„Àá„7 X$«=®Â½ˆ&É­Ï /¾„X3žsŒ•"aÕfùº.\;åôaC›,k“‡êõYPRšgŽwÌY ¡@–sºÏí|µì8:U}Æ [£TÖ!–4vÆ4³Tl ŒÚÊ1³kPqã:gbw4è¥ù#©Ý,ÛÖ›2Òî”Q9V˜–ÑÂsÑäûVÁ¹2+²šÈSS»qNÙOb7ÇBUÂ#oÀ`\V?Ì6<°àÓµâ&B†®<Y²Šš4Š¥h畆>àì@¨@fspE¼<¿é Ôü]£4ûc–H o‘ ð—0Š&±ø»†
+«×<‘²Á†è%Tø©¡³÷­¾²Ž|ŠÒî9YĪ•¡MÛηÁâÿ˜Mê&kჼÝ8šD ž‰H
+L:T¿‰3y‚¼Ü°1D)JY§€Ê°eÈ“{ÃëÑ¡mÆ$Ü#„×XÔÆ ‹fS0Öc _;β¨¾„;w»E´²§zݬӟ ˜¬šîü©U&dѬ°²ø ¬š
+iÈnÜ “·ËYÞ!òAƒ5·PW
+„Å ’µ<êðTˆš1Ûä.H#'ð³ÔH^r§C†s8ʆbi—&
+cP1[5®BXUVèa9hhœ«´ÍÎ9d'D…Za‘ß.„ü4YÀNw)k$Wl0Ô´Ó„¿jþ´,ö4ñ˜ƒpJ<ƒôp^5¡""ît8Â,…Õ€­ÚA(ã–óáÑ6”œ±ˆÄô~ œ¸B´Ð?eÉÑ‹dþ)4|ÈV?Yòׯõ›Dxd&­g&i^&<
+øÇT¹O¢à@%6)!Úlòó‘6“ì7º³µéï¿î &h™äéŒ( ÊR ȵƒ0NU£[úçM
+õ}‚6! S×B)Vß9ä;˜ËfŸß9‘üºÔE‹Ö¹WkRG‰Í8€þ¹½ ØaK†šgOSxzßîízßì ùžÒþ9}sµW×w|A|~ã¥W=>~ ˆ3”άžË˜¡4U»™ M²ʳº;<”´à<Ëe…YŒ?£ä’Q'ƒ(&‰Ño§*@øºTаî vîp2Ü”}+˜@C‹Eƒ(h78Bñ§sÓý0Ît¼ÓÞ w…$hÑí'/„õÇQm¨·É­À^.H›‰m©2øD…:È#ögXåô"ZèNc-Mó»Ð¼¤ˆF£À!s-3äÛJ“öxI™ÀªpÐmA0ˆYf ¾˜ˆíD.ÔN”¾´›uPä,¥’ê:„)­<_W椈#›ŒZØ­Œ®dùV
+.°9JT·Çbó^¸ÏµN&]P|ÍÌ;Lñ9²M´‹'Ì?áZi›Ôù>Œ}+$[±QI^O*…úò®tÙZGÚN3bK÷¾‘›B6äò B½3V½Žè[azY
+R‡o((Ÿqr³XùQÕ³L5F¿¬uÐ[ìÍ¢Éb®Jn”ðÝ@Ô[Xc AN{« ¨<’\:%œ˜m;rQ =mÐŒÖ1ëzc
+qz@´IŽ¡àøb׊jã–½cµï7²ä*׃×=uàâ½ CÝúÍ:J?*%ûoç= `eƒ`OŠz8-–³£ÊyÉûs‚qب5 gŒÊeʼAÇ› ^‡y‡Ç˜ï†#HG²à¹àXŽ®½Õ…sÊò|PR}“Î…¹^õýD€åQàU„æJ_øJ¤tYZBØ'¾°qÁ™1iUœ2ÛraõŸ@@
+wÂŒ ¨u¨8Î*
+—C„Ö‡È%ZíQ‰¬«MI|{§w„M“H6âÍ:×8z€¼ÇÑHV‘Uu¶AA*Å4úã$ñÚ8žpfSâèÄðq±3O5z1ß…eûÍ:/¶üò·6þ¼¹Õ èó褹RA9ùÚêØÄ£pUvHž¬ÂH`îúÑßV›*ØŸ¹ÚN††ÅûÀ£x«2‡Ø«´¹_ž–ĵõâa ³PÞFÝPX;Žg\ûa;]%äm¤#˜´äº~‘]eùVÖ4IUÐÕ@#ŒßÜê°Ü.V"v•Ô,u¦iÔ/ÏÌCñÙx6,6'K9©=Á)H­S±ÕA£ Ÿ°‹‰Ó…ÛÛ NýX È÷Ý8 Xö>¦Õ H‰ÊY7“–c¸fð2Ø®#¦9·‘˜ ÷aÊ)Ú Šа½EK
+SÅgÚ³ƒ¨:•¥Ò¤1W^8%ˆVAgDRŠ²X9ÊÍ AÈ(bý"üWU¸€>ï@±Œˆÿã¡ãQ]8Vúx“€`äKR²úKÁùbP„¾û#]dŠm[ëè! %¿þ‚$¡€?27” 6Ì%¯­ð>2ºîh“|
+Ù¶wÒÎ>rÕ£Ó»äX†p(–Hšó£šõéµ v¬{èøNŠ]˜®x^ŠòÓN\}CèýŽÈ&‘–AĸØ6·Å/³»ªÕ‘YRáŽ÷òéºÃôÜ}êX±aìä]×F@èGÌ*PÏ$0?“á}6(/n¶k@pBã¢%Þ¬C%PÒ;9·Ê¨§ezyÓò¼ø+¨ŠR™+8€/žW¿‚0~¸`¸Wz½ $…Œ¦òZÊÑ+ˆDÍ ‚gmò>´˜gc㉲¼ª_=2®t5vçȪi½¬ßKñÀïxôБaÈ}˜
+Ôê ²œ^îI‰VÛM$u¶ù+La°±‰Þo½Õ"úAÉ®ß@^zTí¬Ë—Œü€Ñø
+eIâP“òT«•E»ÌÀ¤]¢•ò@Õ
+‹9®é‰Ë< "½V‡¨-•:Ëêe1 è¹hc
+¤vê׆äÃëøK•LŽ4c·J_ˬJí<Ý2Õ”Á] Øá$Û8— Œp˜Àªí(©08+FÇ%Inn$1ä\ëÈ¡ÐJPã^‡£sŸ )ˆA0–ÝÆ*éå½zë¾sÄ3ô¨»¾[¸…€õP6ʳÝ툅‚ÅúšÐ„»äé˜{§7P¥Ù¸é”³u°ýËîÃP…Fbâ¿X ¿kö±-ã&ð~¼Ö±øûÁÀq©3zcu¨¶†éàÜž•¬$}`ÅlVUcöCNÙ=aȆÎýÆ)Êp°I²ÞJÊ®ÔܾŠÁ‚`P¶>BÑxÿôãåŽ[ç Dáh®]$‡Ï2P«Ö+0’*)³ÿ|‡3¼€tyb{ô“œ9sxÓS2%™t²Z¥¤º¦Å—¸IP²Üß1 ®¦,üøf¸ŽnÔwž—ªHP1(L÷ûýÍû`AÓÖb¢Ø9! é±Ë™Ÿ0a†7ö’ÞŠ´&(Çî½0  hWù§E jÒµHJ;^º¦lô!1ÎØ
+šå£'“b‰@-ç`L^g«‹¡ûþÖ“´ÆgŠKWã—ðq°!Úá’Ão«¬™—0=ùÑá£nþ
+Q³ '¥^y·á"J­Š¶!}ŠËö8)š]k^Åp–ƒS’˜7ù³†ˆA²ù0… OTï ºU'–]Œ“´ô”8¯\Ũè1¨ú$Ø/ò¨.8´w{ë ºB¿+äoW‰¤d,¾9–ÌðQ€D³Û|l°!Bꪔ0%À Yµå%q×ü™`Ì’ ÚdU0’A»xÕ¢}äð3ð>ø;,Nj.J¦ÖbjéG¿z½ÛÚ}òŒï×Ý4E"&
+¬>JðûYBbÙJqd‰|)‰.cš
+iV/êb…D\˜Éa:ã(b%
+ø¶Q¯†Çˆ‹JyôS>ô÷u(b|ì%*“opìK/"%Fû ¸:†±òAWäh6Ù™béCmô
+5
+MÆÌÙWÜ
+¬TÇF¬
+M‡â'‘¢æ ´[Ä')cÉ{£®^ñ0?¾£è1´jÈó—ÿhÀ4®ŒõIÙ@TÎw« øHÆAÏãN±TX1F³â>ÈP3E^ÀÃD{°g#Ž*ì….WW„Ùg7HŸ1Qrl/ E7lj4²Ì}\½Å·Š¾wϼO×+l=VP6²\O‘aJn_6‘‚ïœ ŠÃ(oXŒé™¾ãP…؎çÜà.1ÉŽd*-­Cciä(¿z%
+·œaíoß"à]잶žâüP /´ÉÊç(^½… ðâÆ1öT<ãÀŽ`&p·Þb²ë²³!Y\¬*ƒñä¯Ó8Re.Zgg 7mIJ£¸Ÿ4E f±rÒ&%kG×j‡ˆÒ⎞žè[ÁæCPñ¦Êå»–•5ƒ‡}Ìàµ!²„AøBØò¼·q +÷…š³Œ¼ íõa¹q“‹¸ D/Ýacf“ø  8*ô" Ò•T< >ÇÜ,ú!mòkö¢¤f~&‘¤ôþö÷ÛÏ_oéÇŸ½ýüï-þ ÷¨†J­$÷0¤Zqš ÿñï‹à¢<B®C^"k„ÇJ«E‰V ù·yIÃåHÎƆÇ"\€mp$ua©k˜h]/‚ãh.oA
+»dh®ÀO)ÏK|·€¶î%S†Þ‰ûžKöQÌ6+,c—Ïlã6þ`
+H‰Œ—Anœ¹„O;ô¦!R")­3ËÜ"À[Å÷ßÎGI¿1î_ýÆ@
+µ¼Ê ^šj´«VÕ)dãphÅ©òf£l„z»¸©-¾ƒˆ!‚;µÅwjµÎu|&'I1h¥ÎÿÆî¿yc*å¯ÜÔZbГހ³…V>þ|§¹~Í”þ„#è
+/³s‚ÇÉLqcö 2m¦D²…¡ÝºÐ%&SA­ð–„ßa„'Ø(ñǂ̲“Èq€ü^'M¡«àš½‹CújPÇ&R-[‘¸<DÌ7ÑÁZú>)™dö¶¨úšÕ@çeÕRb•ƒÐp¥SÇñ)ñÿ:Ö,k~€Þö~q‡äÌ©Á‘¤aµFJ¥3R
+Í'ÈQB6grf±k¦PE.9,I{R„îj ¤ŸbˆÄ²«ÑÉz­4U±wq¦~dô}K)©Kœ(sPr¶ŽÝ†ÿÉÃI×Çß mÏÂðh”:³”äã̬oåžGЀHDòV1¯ÞH$úÕ˜‡)z³C†>Ö¤QÝ:)ðÑ*Z™ú²§3 fÈÌî´ñr@¤¾tèÞú>Š‡¡tžl}È SPP÷ZjÄ௬ó${š8¤è£#‘½(B³îâQB 'Å4ÞJÔê£ÊJ_‡‚iµÐi]Ýq
+mñü•áä×Q·ìð} ¤Nµî£n)¦¬ˆbyQâV©/͉[Áïqn´y¹Í}·GH\sÝ&ò8·‰Ây8|‡øR©ŸWãUü ÂUTúA»?6w*>1Ý\‹è×tÖŒ—ãn ?ß¹ÓÒŒÒIng~`Á½nÙäñʈ³ô2o@Þ:w
+îΙ “
+â)­GÛ©ô+lÅq¹áíŒ
+¡nˆÑ,ÒÔ=R>ãÍÁzA€`7íÑ9eò äHåÆžK=Å¥bÆ Ï¾Œ‡@L"E'Ît!Þ\™vˆðcæËí”uá%¤µÿttT1¢Em®ÚzÀ…}L0‚À¡”#ã†ÁâiŒó Z¨Tt¡lº9å¥Þק\úæôI—þ»¦æÒÅצá©P¿~22ÚƒO—D
+ü.;‚ÿObÍh[Q"šMRãuÂni7’ÁÇ5^×»DQ<›^*RÂ˃OÆþòág@ ¡D[à«»sx`ÐÞÑ×Ø.Øö&žãï­
+ÅÃf¥ØYFÒåT?Ðß=èÀ†ö1/ÍBm4Œš_·Ø(kç!¾›±Mjô¯>®ú¶“ÿp¯È>%qdž— À/D Ú–ÜÅw*ú–b÷‘æ;
+t)Úk/aQBÉá»/×]‚>ò¦/ü43¢±¤ÌVºµ›AÏíæß!£æ ø̧ Rèwƒr¸Œ2äóÍBÙÄУ›¦a
+ºü*
+Á@În´Ùçà)Ð9¡;¯ÄTK nóOÌj7i¥‘q呼yH¨\Á*ÚÔæY†˜~iÁï[ù ìÇàü.÷ª1Í»¡{‘5~µ³û$LüSʲ#ÈúSç´vQ¡6~¸èL!•%gG±@±
+ 63foz–@*µæ7i³ ØU%€Ñu¢ó–ArŪ(ćïhËñf[!„t‘wJÜa¤¦2<]¾B„–ôÅÿŒ—Ir·EWà=h ôÍX¨]xJíúÏ-$žø
+Yv„E'Q@6÷žÌW7_/P[Ép.iŸ
+:B›Õ´ûDdxÔŒ7¬V…ðH/c\¢õˆÆF¤m`<D\5àKÒŽõ¡3F{%\‚¿&›>]D‹lyMÓ¸¾­¸<¥Û`1À¬*£ö×HP€x@³µ’È+ÇíÑšèÉd<ã°FDÌŒqVˬéßâ„<©Ó[Ð/7Hëü™¡ko^P¡Þ
+„ª •p^Á.¿kÇ©·˜´ÚBÈwi‘Èþgc 3äµ´ÒÏûj2pþØýC¨‚
+‡p¦½š2O¼:h7µ6ÚØXô©½}•R"2¼_¤ÍŽ"üÔê-k½ÕÂú#;‰Dš¸ìGȪé;Ô­/ W7æ‚í£ïW¿}>aKlæÖ[hUK¥Hß.ÌÚ6a~餖˜a©‰ w¤–ùå22Û¦É(kJ®QˆžöwhjÏ·\lKŒÜÅaÏV'p³°v×Ú—°÷@_å¹yŽ’ðZ&¾dmHÁùÒÆÔwäŸ å$†ÎŸä¨Æ“öM²÷s°…D.ðõȺ15úØ[¯ŠöÊX·Â
+ˆìQ–­õ©Áv¤¢äþšö"%³·/Ñߨ5Ìw_ñS<pˆÕBZAÌÙÌ¢ñ§¦¢É›µÍ„S]Øh°^žÜ‚.cÄ p*Œh%`á†S‹h1ÍGN†"_!lXLe
+\9Ðwy™´ö
+¼’ÆÉ ºBX` Óÿù¬½Òïøo54ÅÞSQ¹C]Ue±`¤1wòkP y¡â˜"¿ÎA*kLkvL…ƒª,´Xʀ߮:`fÊš¥w&L,°·ìĘDø¡«¨/:›ŒMÕ¦3ÇòN„€gä?y>Nª´ïÍõª;¬ÅPÃÍœÜ82±÷ÒH£Ä¢—tËqf÷ÃLszÀ
+§œ'ždä™"YX“N‚¤Ábò =.2a
+òû2Ž%ØÈ —Ùyú¥ÜT˜Ì·TFÜOÿšÁKå€FÕ˜éÇYO+oåô5—¶ht
+óRǃæjdx4l›Ó†ƒ„$4M°&ÅY04aŒ=*h1!5-¦ ´‰ÌšõÑìœÏðY–>Dö‡72§Uß'vª¸×Ú+Ò*ç1· O ê(5‹A-vvÉ+z½<hªØð6
+±WŽ}W—¨øàWâÅŽã•dR' »ûåø!?TØÆ<\õeQ¬Ñ¦£œueÉ4‘½<’™¸l>‰ô/—®÷gŒ!‚îPÙ‡$êvˆÔ
+UÅ/Èï\›!,Q¸œZ!œ0dt• S(« PqOæ=dš¾IKÀÜO/f~\8@­gnö&•»bq˜øˆm™y¦õ˜‚Žç !‰@MÂk{‰Ÿ>/YAȃFi­6˜o%ðìîo¶ùSîúã|°óÕg¬-î/Ĥ€Õ°}½ öˆ™šVô_Ž >VÞÔµ\-ÚA&§~†—fRäœm¾™¦”–.«äD4œÊ-íí¤˜¶åGþC™æAî›ÿv„ž¦ ?]ƒx†\iÆRÀ–Yšøx6)bÎ%Ø“š¶>:§ËAVË"waCܽCõàáդ boEÈ);Új°#=á,—>]6´ÎÐ(pN1 J3hI®¼B:cǬ©55±§WÝ&yé[Ôz!òÇ_!tiÈáZ|Ý!']Ô
+Ô—þp·Ÿ£T3CÒ¡k™%¤ëªÕûñïjŠÛ4´Œnña QŽ¬¬»0í¯µ×¨µî)
+Üæpó¿§Ìùõ©RÊuŸúgU ×rCèZ^ç
+~^åhïIGž©9ÚøH°7ªÿufþ°z CW' UO¬~¬NC’Ð2¼y+Õ;«s+°k¨Ykq w' ‘DáJZ1‡áªÚ︠my‡Yž…w,— (ˆ.ÄSÃéÒ͸+žvM¶Çê„@™"’wéV'H­ Dm.«‚)r¼¼7—Õ‚ªWqBÝ[ÚÕ½ ƒÕÏ ƒÕueÌL4ZRuY]Og¦n¬—Õ•ÂÞqá!/òP]£f£`$6ºŒí©åÁêê @-dö¼1üsè.*tMòðX]=J=kÕ뾫«×ñi€×W—Õ51‚
+œrtYÝ›¼ƒÕÿ:ž_Œ„,¼†a*µŸJö{Å4¥¹¶8^#ø®v VÃ*^}Š"$ Ð4Å;$—BÓM‚„Z‚_ Ñ´ãHqLMn^çĬ=¡Ê&ˆCó@÷$ÔÅBxhFj`žõ|öÃ< ühTµ|+ 2iå!&з _Aè®É?yù›b:> ‰j–AZbþËz;!táïi[`é¤4ÐafXÅüºXvz¢Nä7pš•ž¬‚æ¬Da+ê  î‘qt”‡ZýgÅþQù֢ؽ]h|Œ†õ—FˆKǼýùQÅè Y÷11˜k\Ó wÊYÙ¦}
+=gÖq¹>l€YdRÌ›ZļQè:‡{àa”,™Dx9EÆ'­Z! ÐDõ§Ô²šÃ Öh¦*Yá ³Áõ9ãRÚ~ó7íôê°o ¹ûÛÑЧ¿iÙÁ‡Úµ3ùþvÞó7úP)–eßß¡˜Ü˜“^·¡æsƈ¼ÍÂë NF{­^ì›A:@¨ {ýÀܘ¦gƒÖÛ.s…¤Æö·¤ÍAûOØ!EnÖÒñào^Ð m|ÙøV³Ãûyƒ·:ãŠÐAä
+ k'Æä\ Ñj²ÏôŠÁ²¨óÇ\Á¢£¬Øf¨m>åŠòJÚ9H‰ç\!ð@"Ìؽ¤HÄ»2®óœd„à[†á‹ ã*&n¶¥}TW3$ÿ^tèŸ á/í+Cöv
++n3øU礤¿$y®pß°ˆš”ÎæÛ}ñèCHΞ:)¢é žv¾ÍHQàK¸Ø7ÂC~±n¹ŸIð7\úùVäŠæ㦸7zŠ®÷"‹î…¿ˆê4%%WIJ÷øËbAgB´¨§ã-0
+ÅûÈŸLÀC°èZå´xkÇÜ”cr¥‚Ã=™˜7±5>€üA8z|x%¬ô1RŠ‡’5ôÀõä÷мv*Z%ëÉÜ8ä|V€
+´mB¤>·ÖíÐQžŽˆçU°²T)‘²?&X·aÑ1]¦mÓiqÅÔ?+Òš*þ¡Éô(¼‹ïïD¯:êSwBòxü@–Ipo¡xríù!
+ŠçdªS—<²ïeÇ(¨±rr¿h‡ïÖ°lE—ËZ· ð8¦é´Ë¿ò¡y yV,€œ¦¥Â*ÎqH:³“*=ÖÄ ïÅ-wÄO­… êb?–ðYˆ‘Nãóúû¨ÿ_¯ï,Xxdè½u®¸tÀŽë°0HQz-÷LåP´J:vˆµƒ®6“¸*Èô6/ÀWüo9“C!SA“:»:D÷0zzéEXÿÚeãØ‘UËÚT^ìV
+u(êi»È>Jφcêôau$ ˜ô)Û…aMÌ8™ùþÇú‡ê‹lyï!É«j}¢4k |hL|‚àÿCù;å2ú,;!óÛã«þu|€ÄAsëh›†4€ï¶}¥"kíJz;<6Dòg_QE%ršä¯´Yº ,5 Šï÷¤:þ—¶Ô}e²$I{W•]/Ëœˆªxi£Ë"<óÀôà¸pýñüú’ɬînŽäJ Ã[XIaîX€:œZ|*ÖØÇ}îr¦3—Õ¯¢( ÓwÚáÜ<B©8燒5Îyì£nEÇ@ÉpvêööÙ%ânJk’ Ì“ù Þ È:¾£ìðD®™Nèï­p;ÿ€>[Ù‚|Šg›çXzæ„C ÍJøÞ'± nÆ–X¹HH«íTòÄO?Šþ>u¹˜¾-gdüt(Â^“<¹"Òᤊ×Ì-Š"ÌI%9ÔJ&
+Õ*‚ˆ0˾¸gÅÖÑ=ž
+Õdš¢¤— ÔÕ†ë†Å»ÜžEŠ*AMG}ͧ
+ÆpÌY‘éc•àáa×úñF+55x‚X\IðÈ<}…IËønŒ!þ"VìrY½Nl$ìÁåÒ܈·é²m[´¡·5±êãé;›ðH·_÷Ëä—@še^çãÞdÀ!0ÚÊù3åE
+&€…¿Š0’ôj7ˆgãÊŒ¬s.'±ÝìFNVRð…›rÜœ ‡QsÎc÷_PBE¢-H
+íBõÊRV‚‚2®!yp "¦Œ*ŠÞÃKѱ?e¾=`k•Ÿ:}-ºóן°·‘|A<áöMúi6·Èt’óȤc‘ü½hÊ›sŒ OúX%D=ý!ǹĭÈ”¾|%±ȆK¥•³¢\&&K›ºRê:
+ɧáÙŠÈF2b£>VI á´± [3èÖôãV‚¶Ê†£¸ÑJ².“Œܧ•–
+â²C³£ÇÍ2ê/+ª¤WØDÁÈŠª®Çô³:a„ÀXËôgÁÃÌ'É+zÉ$UœTäSUÿhþÍNwò¢o¨}ZŽ*„—l „ߪ˜N“Y%XþŒ(ØK;–\RÔç_ç¢,{‚˜A}þð[ÜÒ䳪è¤Øag†´ð»±F°b“ØŒ\½9XnEÆn¢Ã| †Ø!li{æ²"y¤Ò²ÕÈifv›üà#/Ü…Ô4ü3±ccÙS eû¸O3ÍòƒAZa õ{ä?p¡"L. t¹ä÷ÈaØ$cJQdé0ñ#wäÊ‘qˆ¿‡_x%(óp´ã‘Ôj¿*É
+,œ¼‡Þ´gƒt’›u6E`Ø?N»ýà:^W»v,¹ìð§­:JŠ$%´¡n'r,ñ¶é‡<0j ,®Ó(ÁÝ€•8Ü©ª5$ù½iÒm°|#$C–2\¬½<ÓS´¤ÀRV„±–¿C“c¶Ï°hCŒ÷wPò’°´Z v`µT%ë¨=¹¥6Ê©h•Iȧ‰7¥óm(AGJí'òôªBPÊ$´Ž¯JçæPBÀU\©3;|(ùuPû¤kMÆb¡¶/;sº žPÀ“£´Ã£ŒµeÓÁ_tçtmÎb¯NKÙ`œ†?êG‹UÒ´&•þ&ÃñeT·GÅ‘Ÿ¿ó8§ÛüÄßùQw_{sX…k‡³Q¾wò*-NX<[ôjå„7ükEØW¾3SOùP´J:¿¶hò9ER"–Ç/4gä©ä0 „†T»žž^øÊ{€_=æ_2½Áh/fWþáÎ8ÅÚü;äÒ"'P«—`Û£„±Z­£Íb²âBƒ.p›ì¨Ñä¥èË‹˜ ±5àgv¯À^&± Íå'2íÛZ^_ŽÀÃöˆ/!È›L'‘§)—˜Þ/GSHßS¯8B¡¢TorkƒÝªé ÓÂ=Ø`ùãe’$×­CÑh¹ûf,kšJûŸú\L)óñÉùýa•P|$€Ûy“/ó|)YŸÂ}†¬“ÙÌ›sd–Œ¯µy¶L<‚Uî2$þny¾"Ö ³ì/2 ÚÓçtbôx‹ø!úKUé`B ¬„»0¸‰PóX2þ‘¦
+·’·bá6ù-{‡­A_ø‚ŸzWl¸AËG9WgÑir«7¶€"TRÒ=9‡U b9Áú…A[€ÃÝJx#T5$•æK14“¯K?÷wÂ
+&Nø—ˆŒ%fIRµc7LF ušFwYƒ>òMÉËŠ~ÿv*:ì:äˆ|Ó°OkNà»|죢7B8#ë­èKäòøW¥üMdñàå²¹þO½øe5 Rl¹~Ò*`4ÄD–·W]§J Rΰ%*¨8©i`L&s[1X ñœ®MØv™<ëʪ*‰ìbûô¸(ùÆ•n(QØàYÛD%Â!¨Ê Éü{nlã5j¾Î5DE€Àÿ³“X“Kׄá~…|¬ÐñXü€kš·¦ô&Ë
+¦Á³w4øtêv?BÇ)‚ï¼Ú¼LI]¯)»ëŠ8Š˜Ì Z Åh\77 ¤éª$ÐÕ¨-¤iΨЃ2SŽ,„Í‚<DïEZ±ú†‰ìSŸŒ1ú9Ø.p]sp“üí+r×V÷H =3À†?φMÝä-ɶ÷9áÊZ¼ŠŸÈZ‰%d4$e«`rEê’ÂÚv*ð“"ê£nêæ+]–Øø—
+N„ˤ2\Õñ…Yö7§Úírl§Sx?ìAZ±°KxB„O»?› ªðÄ}‰×Àê²;CÌý­˜Õ^í 7eëÈÄtð¢ÚÆM ¶€FÐ0Ÿí»½X»qF>ŠébSp¥•_V‚ÑdÍSÕèu¾æ0zIûS=BþSÇxÚ" ³%Ã'D¼É„÷(${n'§?žqô"H¬d"«WòIµlÓ»ø‹ý,û6IR-JŽÉJ@PAañûUXñÉ Ø'ûRdsº(?]%
+¼§­MîÛ²‘ѯÂàr6^f& QW¶©BŸ
+×D'{C± ,'xÝ·a_¥¾yåW[b¾Íšcì¼5EFŒIâKÊMÉ2ßœ’Óø †÷"¢b›´üÑÎð5‹}ú»e£¢XuØ±Ì v7C@[øbüíù% {°æysª4‘ûŽ·îÖ?ø&«Áaz‰FÉ.Â~Ñ7¢´å¨húf6¶h‰x†Ù|R8tYŒÎÅZƒC¶MGæqé’Áèt BºË†]!
+a
+ûhÎ×#26˜®­w-0úQö•'ëxaRÛŠˆ
+Yž“ÅÞŸº‘Oå–P‡ìçÀç
+ªSãJ&jˆk‚¯@4ç¡ä‡5ZBë°u²çsVÊ­ñ®cX%A~„ên†NEpC’ÆÝn=æc¸î)Zn^P Ïôý] µÃ¶·3vÅH˜Oøêw|*o ªå»æ©h• k¼•ÛÔ Ð°‰Ç°p ó­û5‚®—°íÒ–bÚŸ‚aCÉËtºØ°(2UyÁJf’ëaƒ¹éÍÈ7%/KúýÛ©è°í²Öˆ¡ä9OkNð»|죢7J8cë­èË襂(‡$S«—ÜfPûå,Ýa9˜n³„eÃ( d3¢mšÅJ=È /ËaQ2Ä][­$Ëå‡l°ü!UŠ-oP„ I ïç `iþ)ü!û ‘ÆbìsQ V]+üCÜ‹€†Ä…|sJ–''s†–ï®"ÃÃ1Cy’&Õ³RçmkØo „Ó»_J^基`ËÚ×bæétÔN—×óÍ«’øCþô?ÆË%½’[‡Á+è=xçÓû1v†ÙE¦öþ§ù!Q•ö½™t7ŽJ"A
+Z_#'-FJƒ 8‰FS7
+¹ç8#a¸0=Ì~™ÙôˆÜè2—žÚ¥tŠÉ!u¥?´ «[3fÐb$e'ûn—áÏ\7Ùm–ZF¢ñrëɧ° #óåùò…þO
+¾…>–WY"1$ÐB/‹âÄ]Gè1Q… AÁ³+ô±²‡¨ŠJ ¾Ð¡Ëü‘R=j" R¾3ñÃâŸÓ^‰}†þw$ÖUh ¹ã©Y?íÄfÇCcÃWz |šTê«—Û0™dÎÒ3Qd<½ŠÅ²vL.ãÐþ1ƒÁˆŒ.
+7O¾ ½ 0/1< ýä³lë
+£‰T_èˆÆ,¾°0>½8˜“ůFs…H…隬†+ô‚ÀrVªÙ×ù Äï8³ÇHÂ3=zÁäʼºÈ<hBÆ„ý¡WU‡6+%<NqÁº“´©öùpNy‰z$ÒVueË„fX9âAæ¯ ¿>„ºÆ3•ÁåË?¹2ÿGý^2ÿö— áÕ‚ÒÔ¬–_Æ Å¢,,»J"Ÿ.hÂÙÞ1ÚF_³
+¤Xo¡ŸØƒ” Kbd6Du%Õ“—ìz„¤BUöÖã ýaìK³íq¾¤||°Q¬OEvYz µY]wˆ#H}ÉÕ‚®h A¦³YŸ"Ô“Æ‹Ö¼á“ØvÇÚTòŽš“W2Ùá"£dpSü7l,œ]
+B¬±j'K‘rñ÷ªev×x P¸Lw€}'15ÈWâÃ!—vßor£÷¢ý‘ø^_g¾õ韟LŒ†+¬á
+pÒÂðb›åå¼O„íQl(?ì{‘i°ôØ4J[ëdhrCòó†È(CBûÂÙCŽ©=±·¯›«ådÍV-L,­O„eøÝ´¥VnBÔµï^j`òªÀé)ûÇ@w8€½Tºi—á¹.Xîg š¤ý&R-CwðW›bZÍx‰»-Í„p˜bÄð ¢ÚBB%fDhÂ(û6 :U”éGSó÷_>è[ç?\Ðä$d¿+פ¾ß5äx÷(É„»Õuï áÑÄKê3LãHîahb—<Oçã•ò¥´AHëYFqh7¢vÎDŒ¦åµVp.ú€JÌ­ü9²L=• váoíÄFç2£ÏÀ¬f³njÐc:o"pÁ#t8CUM« &=' [ú’Fñ¯@ÑaªŒÑôÆ4„:Ê›?xÀ¾R  ¡áŸŸLç^†2ADE|ð‰äøÄbb:(ô2”ê{LN%@ßãIDEˆ‚ž,ýÂK±n%—i× va_{°|TVB€ñ«Wb11°2û˜®X…=æ`5¬p@{/{W5#ÃÑqì·SCÔ+Nô«/¾S ‰ŒR£–Ї¤æM
+nwn´kíuŽÝ ~¥
+=à2\ùñØ^‹’˜]qÅÚðUfT¢~U²aï9ËÝÏô¸5Ňõ%”‘¡g³­¶(Ò󞔊Ðá÷9œÈ3ûÚVv¶’§²0Ó…Ð`ÒíÐz9I·ï¿|Ð7‘ûpAˆeMt¡g¼´¿yW&Ó_ 3–§¯¥”i€Å-iß³"ÿ·‚Ë$È$FèŸÞîmp|äÚN×D $®Løw>u=GCSAð¬cGEê
+¯ÉàÆÑ¢åˆi3ãÓ®(Ꙏ¦`Ê%PªTÇY5™<öO’[|󦯻°ˆ h[*nƒÇ#«¬OQ¸íPyá èÃF†m´ã„±¨±Êô$7:§%8jAy¦“¯<]¢á$Cb›[¿kÇPbì–@°,E,f&Ø”ä1…"·tâཙ÷P Ì:“èðpJé%ˆÿµ¶Ó„®,”©$g1 o:M dF(‹JÆþÅO®ƒåõYŒçUöJŸê\ÉLD€QÎe8A\ã˜ê脆Nµ#¦VÆ»7¸U@¨›¤„x÷áa&™ …
+_­†¬›²3¶¾õù
+®¹Žd!Œ|`—Qm`´þ@KU2Ù‡Ñ^Žõ «¯àõv§f»é»)*0 ¬ZÒö2M
+ß%ÛMTÄM‚šçK— ÌQ
+eÕò?̹Š¶šj zÙØ®ØÑŽÊ1~ˆ@²Ð‹ø×ÖG®'ºÂ`Joט/‹á.âzjz˜žrO¹Ý4¨Úø™t°¿îß°¸<-3T gfŠ-’EäK™ùo† æÔ‡hO2œ)pŒ6vÔºc•tÏýN1ŸB¬mx$“™™ªvwN[ûM®XðNÑ9Ø­Öïì8çU4 gWا`GÌ„¦g”¿R†¤õÃÇÌ.xnZ[ë]È®.ª ’ÀZ‰<¬°Ê+
+£t9Û»ì¸RŽö.€©Èyx:¸ò§EŠšÉ“\K2k ‚»îÏa{¾çßQ4rK^§Ý@!ŒIž±Ã)&¨t»fÒÄt>è%<“æ³GIEÄ•Ìê0@wáÎù1ÊÂMjp?ä©IW’/AN·ƒHµk¥’š7c¼ñ»|ì­ Iðgë%è÷–—ähi‚–K/¿Àm¶ºTÖD*Ëz¬"
+mú„Ù¡%Ú‡éRåMÇå0ËÏX\vÿA[ôa0K†ód2Y{àn KWÓ‚ò!Fø\õvÐ܈%Úè¼ëë&ˆÙÄRÂZöµÉÌÃ-R‡5UÅÍ-QõS{€§™qBþìÆ`Ïb¯‰Œf¼;¹Òª‡öÙˆÁûò\
+—»šbóXx
+!-`;!¥HåÔÑwá ª‚#Þ,HÛéÅFÛç”&ÖcO9äûÌS
+a¡Ãä6yX!~jae]½wÿ˜„v€Ìj‘jCÔ¸Y f±¤'¡9}Ø6DE àéÕæÌ ÂF¸}ÒšbÈq BL9‚¿bEÛsÏ®“'wδšáMCo
+ËŸL;$w,4bÆÉB´ü° JËyº>Ìp‚2ÅLaJ¶é¡˜ÒÎ CÆ°ÚêÊ5hÌ¡KqBùØlÓè×+fÑ„å>è»›S^ÊíÝä¥m¼]Úïš™K_óë ÃSþ¼31®p†‹*ÉúAç¼o7¨#V­j3î{eˆ#Ïë©F®@#¦I ò!­J*`t–;Ô¡=n–^Ê‚;‰ÆÄ~¡½£ÙŒÂøÜ}8ŽM&iÊ3~/… ±c¢YÀ³›cèez”â#üÃ$ªµ»üÙçÊÓÞô‚Šîô¸¿”áØG,7Y;YIu¦å…™GQ¬YÐv Qni†å7 éHm…X¥hJv6Tt˜Ò;AO•ÿrƒØsK–éJ¦ûþXL,­™î̶0Ø7k-÷¶§#×8cÃ"Kêˆ/âI2:ƒ!bÍüé<Õ+yìsh‡H²Å¥–«V^ᇶèk×Á?»L©Ó5ŠÕsZ:ŠlѨžyû‰ˆµJ…àªN®›
+¥,S[}Ió
+zÑ4‚X—Ò ´›s2ì‰È°Gô`M&¿
+‘.B‡wH©"3Å#jð,ŠÅ†;7Dúx)““U½'‘oh5ä}ŽF\©áˆ—Á Þ’¦B7ÓF…h¸î‚:AOFöåaˆüAX /õïʵ«
+``ï<]?µÛŸ©Œxžþ”Âeór]Ö‚4fú¸…ׂºÌ¡ÆÀGþ˜BõÏÑØÀå‘koLä6 pÕΉ*X›j"U¼i!É •Ü´hmJ»ÓëšÏTLø æÓ°?¼¡éë6…qHqëÅeøЫ*ÂÑÆÓöîpã— / ¢ 0-1¯áoeÛK‚Žóô¶ñ–ÃROíi%ƒ-åieüM2Þ ^ çå%
+¤›`ƒŽ £UôN>ØöDý×r^wb¨””1¸;S*ÚaÂ<»ƒÃå
+‹&/BøX Ä¿I8sAûC棻CpS@Ÿv
+31šº, a1`-RË8všAu|¼Ù’åΡW4,·V?T\½No Þ2ò º'?þÞ%<–+Ž<“oÚÔƒSãùXyŠ’špqì$À ¯•ûmÚE]î7¶Æwf²wí–Í&•›Úoú±B0ð‰ÄgÑLÍUj‘Ð膌|YÿÐ7«;¼ DÞ@_ЙUùkÐ¥וù9¬ œD‚Êæµ/í§Sç““ kšG¢‰
+µ3/K*äÖ,É(¼†ºñìKå=ϦǴâ2 µeÿ:žÿW7÷q<›š£ÌȾMú”Íò^Ã…_´.dÓž>׬Ú<Õì­ª•eŠÕΩùãu„…râáSt{B
+j‘BRá¬lz0Zo^TÄ<gIÚ7.O˜\ÌŽÀræÎqä«¢(æ<{ ¼
+Tó×!\’óoÒ
+åբĆq¤•´D¶`<ÓXç©XÌÜ–_k2‘¤· !³ú(ä3¦Â‰buÑ[-+bj"
+¶Â-ämŒŽ0˜Cш²ó`§-"côtû‘X<GU0_ˆìŠ:$p™ày9DU‰LIbòå¯IÙ4ù.2)ž;··ÝT<êçd~ªè è7nßœSPŸ*ÿ/×ÛSÆE×Þ™Ú‚ð“Q)¹mKA‡K3GÕõ¥¬m•$§]ᮄQà*F÷š
+ðŸ¯~˜É®ïGñVþô®½Îæ׳‘mÝZÛ”G8¶§8 ÏyɹQ‹~R56ƒ€±¨!›Ÿ´ªÔÓE"'l¬—ëSðÀ’Îçb—xL‹ÏÛTæ€xT^ÑÔݼïÏ?¢ù Ð%
+pK?ÒŒ‰¿MÅ^OïESI#úö€J½ #·m%UŠ ’*SLÂJ¹ê&ã>æ§Çµ¯&¥l—¦E{í³ŸŸˆÍ5²2ðÃãS M‚ÛÒÉÓ×6&ñ±Š*²–rœú'LÖP’ý%æŒÑ€ÇúìÊ0…2Œ´%€Ÿ0ÓÊš{{¤ FUf­"¢÷Y<ÖP¨”V/ #@JB¶ú
+Al$NSh7f½_…Æ^]1Î-ÜHôp~ŸhÒ•¾YHT?Çî ꥟€ÿ¹Ü›ÛÝÛæ ˯¿ø`#6¢f•ºú³¥¿¡Î¨A¨1UÛÉ¿ž‰¸ „ë“f”ŠÜµÑ½¥Åd@4”d‹©¤3¡EC˹y&sAÉ0W î
+H‰Œ—Mr\9„Oà;Ô¦‚ €µ{©[tĬäûoç Ê¡z¯&Ôîp¸¤,’
+¶]Œ ÄžæÅzü7dlvÜ€F+pƒ'šdå/ +yòŒ4¨ŽMEÊ*N }’ž|²5΀*Á–]6º[•©sG~aô|N#èÖ´Ï™IÒZ™“(|C¤pȤðè¶ò/Ã1…dZÝŸãOåmDXË×k¨¹{­mì×P™1Z%òáùŸ³Bቶ®Rj¬>h¶®‡Î¤B¹G] Tfq÷n.š­sR³r µRß©Ãv¥úPêIÑi¸UÎ Dj<¹¶ ÒÇmŽ¢uˆžŸÙ”Í‹ ÈŸÍ¥£ÚJ“l‰é
+U@ße`ÒŠ0)y»‚>Rº]áþ¤™ÏŒy[Z½cÔϘ¿Þ€Gš51 ~ «v–„ÛP¼F¢k¢Ò êÁ/ßØŽ” Æêî5@®UªÔóœç9îå9H4ÇÛ´ ñ¶ÊH¿-KÑ¢?iò:Ú¶Ißë¢F‹
+ªÈOÚ|s
+Õ«®~ÿ ­ãÂú»Zt#
+I?x»O N› D˜õ>¿7ou:W½‚kA´±ûׄcë\ÒÜDÕftœÑmž«.Ùá÷t
+S¿õ¼ê’b*âaV­ŸÀ/•úIN\
+þzÎ m^_sC¿kTWãpùCÇï[atò¡–þñ­R¿¿Ãéaä®ÓÿdAiôÂ0v×é7 {ñy¹îúüÉ›R3*,~FÑÞ=½<MbØùrJ÷/ÿŽ¹é—Ë. ÏŸ¼h?\–kfaúwOÁ§—=˜Eî‡_Ax<¡¥Jc[ZÓˆàŽ×ÉÈ¿]|€ðÀõ(&óM¾ÿÝWV²³¤.aðô#Š0K Í)á¦Oß7LNôµ{B0 ðœ-¬%ápȘšnq›[@ìƒájóc§Ñ˜<ø#[ÝÇ„{B¶ÜZ"&Ô¶J\õÜD÷Ć‘ ÁâÃÍ|}ï*Øè˜Q¤‡¦È‚Ym‹-‘]às?Çb+ÁVéys£bXÄu-†[j‡ˆl²Ùõì(Å4°mDÇÖÎíòWö®‹ÄqÑ߸ùd2² 2V¦0Ø÷)¬
+8 IÜÅŒ5Š‡€|
+Þ³VEv1-ˆ Œ²UŽýýëD~eyKvâ~…'bŠȨX51 ‚\ÛJS2»NîYñm0ìplC4ÆITîü[q]=°4‘*ÒÝ&ˆõŒ‹ïwÊÎT‚DÁ#ᑺÔ쥚1* 9ýú7^`¢0Rµm'/:¡køÝÐè¡2Îö€,Ï :áÈ›××]ÎBYA4+Nk¯Õ¢!`Ã[KØáì*…3±ÐÊq‘,¥t, ê»dÅà ²X¿…¼hÅ–q·ÑƒüŒ,(Äf2‘¶¹%åÕ„\/b`‘åtÉ5a¢cá´ AôH“Oó K¬«½F­ài64ºÈÜmŒsV7g¨×uΠ-xKRv`œG«ñ ¶Àa°á í>G¨X=.
+…=7EA•,'ßCÑŒÆ<\üêè[ìWˆW¦Òp a—µ ‚P,ÎdŒó½l>+8T/ Úùša÷±Å}f)SšÀ61:/&§Àeo•î±„•„tv-:„$ž&fú;U®ç*^ϲ…pÖ¾Å`]K;¨ù‚Ðj zMñG¿é*B’ÍãȤÚ¢pr®º€(. ‚ZfT»b!"º!˜O."IJ!suxG®r„ÄŠRèÓv®2¦JH{݃vBw†®µ,9õàWÎ+†Åa±¹!0;Ì–´ß@ÿ(LŒô­ë×cb1a"YxÎ| ŒŒ•‰áÚ_8ëÅFCÃÉŒ ]CǼ‹ŸÜ„b²Ö×<†T [-«™a†YF½ª¾(‘ê£*sly 3¹l5ÏÐçÒŠ‡G}gÏȹI´EŸŠMB—=Œèq¦Ê^Ò(s1:šŒÒm'ÇT¿G§‡Ç~tfa¿d3¹©<Í92;“ô–*9^/ÕüŽÈ‹P,!nö+ǯ§4ÆN!µÌ_h\L‚çGévDð¬†@ñäCP؈װP‹ÌLsÄXb‚f/,bi™ôÒpýü5ÆÆ=" ;zázµw@”/ô;  B+’
+êÝ[N|±60È¿ô cùŠ½cë _pç êkm‰übÙ`+ûFªVÒ[ˆ_íB$œy–b  ùìøM=­@5ð0qÛH%µðŽ–äÕè@ «“R<aj‰ígœ¦›!ËsRó*ÛÒ §L¾ºŽ·ÖØ0-õÚ}Ä@FF7%h)³Ó—õÌÖ}3 ÇüB[¸9òsÕ„åŸ1}cãÚçÄ$CH†çºÂ
+ÊRVàº@>m"€€¦°Uõõ;¤Xþ•8XPu¿ _$žò+#?j:§Æš—×;Þ¹0"ņ¶¼}ßlˆ<a>  tlH,î`ÿ€ŸÂ€Ähœp’qÀº{VØyâI²¹x›^`;D½Däÿ‡˜Ëâ}ýd;?´Äoÿ×çÎ2*ªvw(ã1MŠ5I'|AJ:ÂVWƒ¶ˆx®Å0ËA©¤é ™Nþ•IcPEfQ
+­†?;„b—‚ÀùþLdNƒ“‡€2pQã÷ª>9z™u„1ÓlçX@3‰(ŽSœãø‘K»¯7¹ŒÍéE—ñ»Væ2Ä×ú–á©OŸ?Ù-W°åÂv2aø|X;|ïûb0ñ7±Êª-Ý.r¸¡Q—ðRÂ,KCê%ØÕåáª4¶ü©µ»Hiµ֖ŽZ „ˆå,Ðü£N^ú~P[ ز¸štÙ,©¿µá1qƒl«;¬Ê¼”Ýy) -ƒwX€O[4ª1gq”]"¡. 1ì;{®•=ªª-CˆR6¿ ·ÀÛçbþŠ£‘R& öò-~Å<õýë„Áãò¬šˆ¼ÛI°KÇeÖ¾mZ×’d,—öwÑÛÈ¡¸ ?A.(¶ùx·Ä
+^®0…s¨Ñh±”AmÙ^¸sò2”¸p9Ô˱Bwn®m'1èøWÜÈdpÜÃ&+ WÓvù²eÜÜKŒ@#Ò?ÊÛyíø
+«@X1œ%ÃR Ò ?PJ¡Žá³ú¦–幫ÃÛNž@WI3¨dÊ0ÄäßáM"¶«d&x¼äŽc2Q(©íý„ðƒˆ\-â[¿ùH†[Ø!´ÝÝ„ª‹Pz¹Od½ñ¬W¿« ÷ÈJµ”»ê^ O]ÚG½‚Š”²¥Zà‡·ómd+³2 íåM‰¹ƒè2“ÓöA—Êðçbï”Röƒ^«ËrM’ÀýêKž ~Ò¥Õ×ï\æz™Ëàuà¼\1$¢¨x^h­Q„láŸÛôë±täÚ$?¿¹âÉûÒ%FiH$û]Œzœðõ¸£§þçœ/ÒéQÎiÖŠa…È5xÌP´«_@§[ex†DMèÏAø§È5ÙÑJzú8žöÌä0¥JÔ¯w¨PÇ(d& Q^&+1J”ƒy*ÁÕmTÂRÇŠ™Ã
+È;X‰ä¡ZÞL°´ÙŸ ëJš½ÊŒò¥ïòX7†Æ¸vlŠ>,a°N l*7°I²}>H¼§(ßí£èh3äcøÐ} ONÑtaß±#¼a?*ÂÙå¹™U?mXC¤±ü=+_ï&ðï“,7ÖбŠ¬%B²áê
+\nHZý¨Ü9“vê‰ÉWS×Q(FüÍÒ…ëýa5H…•+ÔTª—:
+1ŠvqÏã¯qGâ!w.Í‘–£Äeë{‰äÕ¢^ß@V8K䱺€ ù9Ô9Wx
+“ 8'†þØŠ–˜ŸyÐKLe¢unžË©ŠôB48Ȧ\ •—
+hbrøJ"ûa%Ëo£Ê@àqÑW&òú¯á/*œŒÛ€ãÆce²¦ºéá^>Àûä7|¸ˆsšFÇ+SÃÄÙ S¼ýÂ~ÃL(*š¼:®•aršY­èëÄÍ™ÙùSRÖ«º—˜õ $¬@póa—ʼnêµW×â³NÚ¢ÌÑÌfH¦h¦èiŸÄžÁ©Œæ>´Þ•¨ÆØË‘°0É–c„ËË»M9™‡ÈÐ@ƒ¶UX¨Žö27»W˜ ä[ôYÂÓ–ÞÇp’ÔWÔ©–|€ì…=¸“?Žß¯Ã¸â™¶kr] ÷D ‚ˆåħàG .ˆùª^G9§¤¦T¯€Èp»à'ÑÂjýz£"·èÈë–·Òœ¶?Òeb …!jlªxA0¬Ç#ϳoxY b*ñM ªËÖµ¯,ºA’²Wƒ¦A"Š^Q@l@s±®ª(œÌrM»Ï tÉ&ÿRáåR!»w˜«hš0>¾¤ø”Nr ì&Ö€:ª¶Å¶×+®tÔhI[ÓÁ.agRáïx°*]+s¤Šôøÿ ŸvTTaùC.~þ{"¿Íˆ…âXV”R 1˜&-1Lí]¥Sx‚$U5:x
+´@d%"Ù,bà tŒ:n…ÉüXå#æùÞ<±Q1°ì?¨\“ÛSûóçÆ/;Á/Åní¼Í4ƒŸ(rüÍ6«©Ý\>ãö;2Ñ’A6›eAko§ýˆSYÒ>êïsøG¨* ªÌÞAâÖÄfé4‰ ô¸'öEÀ2ö£#É°kÚÍ#F!Æ' ‚!' âQ§kA.CZ0ºè÷ÓN¢eè¸Ëoƒ¨ã6!ÿdíÈÌ´|,˳/<Øø¤®Éçd0PÅlO+ˆ‡tõËýNoæmYž@7 %̘*ÔÑFLÖø@PÆöt±@h9m½½Ðzi2öd{š®¦uiD2’ƒ Œˆ^=°ë§Ä÷X–¶I@w8¶ªÛöh⨳ÖcŸþÑíß¹CH|7#…Éh÷íƒARSˆ Ü'ïí¦:ü+rJ˜mÒB^Þ±­ðÏÿñ2IŽ#‡¡è t@ÁœÖÚö-¼•ï¿í÷I°ÔUÉTËŽèA†I&ðñ‡ì¤À€ñ² ªut¢
+š¼
+@Ý;“‡`È¢ñèúñ1¯h¾|Ó ò0'ÛÅE&m6.u.tG„)ý²è˜¼tV—×èÂEÐ
+\nµîÿùÙ[Ýààßt6?Z–†ØuG¦×¢¦EëGQÚûa¨5ÄZè|È`ä)·?z5?Q¾ž†™…Ý«û¨]N§³åÛþà[Û¯T±Îé!ý¸„þ°?Ub?$ïÇ¥Û‰R>nuÿq±¾S`*¤^‚D«üq»ÙÁkÑøàªV¢²i›h,Ð&f‹m›pü~Vú‚t‘€óqF¥—µ§8WžTMÀG„Ò",ÂCÞpZVžWU¬U¤m€³+¡¤êØò»I¤(¶‹·dÙ¸ã9ÒÆ
+ÓÆÚ7ýÒ Œ{¥ßüD “TŠ¬SÀ·cÇ>ßÎEOœøu,BßeyX%éùºŒ0”yôÒwè
+6Ö1¶ +m&ÛÙÝ®ÕÈ5¨ ç­ë
+•8]!i_õójN…$ÒBXÏZ.„åO² õ[ üZs²< lXL[]FRs²kž/ûE vKaŽOdï]j®5 ã’µŽ`÷}Uà­r”§Ù³ÀJ&¹œÅ L&›óÙ1© +$³îQ€’DXž²ƒ»ÝÄ ü^§PNízŒ—À/Y¸ f«ÄÈ6á¥íºŸJü*DO=©éîEJ
+ŒrÞ¸`¥H£´¦ç›’'„~¾ŠH(å@øùòp· —šÓæ].ûUÑ ë¥fÑJdÝ Ë¬íÑ’A¡"¡ÕÉ+§"Æj³ûÜû,Aܹ
+˜gC(_ ®
+&XwŒSSÐ%Ð̹RDâjYEø
+®#(],Âd{cÚf•ð“"¨åº!^Š*ÒZvÑì,b'€'ñ†t¹ÎɵˆÏ!¿
+ýç>\¶w–¤Ø0éD?Īœ*ÇÄÜ÷Mò–¸tz³=· èËŠ@âªõΘ"÷r¨9šn-Åà%2•CCü11FÖMZíR¬"Ü2+w7Ëä1\XVÖ1x›¢<hÑÝ2"Wµüø‚~SR™vêdÓàŒz*b@ú¡6úU¼–ãÒ¶‚6¸¨²_ÓÑH’ƒ!ÇÒÞ»xßÝ›¥Ý?ÅR-Îè^T-ø )É'^9— ‘Â8yÁ‰›
+l×q^S¨`¡I؇aÇSðÍüdÎÎÅ1Y”)u-ŒxBé ÎÙi¬0{Ÿì›Q3@‰Åo‚ q^
+~ì ÛA ÍKWEåf7%h!ó›õÇ6\ŠPX
+Šî)ú9 ¥VvŠ;ɱbQÔ_|ïJW÷’vÜe·‰4àý{0$èbˈ™NnxÞ18%këñ ¾F©ÇI‰Iô¸Yz*¬ù)@œsx†Ú“Œ«ä 8Ò\.Âós¯P<¢gr¾Îxþ0‘t2’$ÓO!]Ya!ÛòX<´ƒ_!ï'¬…ü.È|«öHâ¦TáK "Ös…‘t2´Á1c_ôZÓMXLÊW~
+“Å)¡¸©Š©:Ç(f÷R˜ùµàFp©Ë å$Íbá±O¨^’r/¤x†·d(¾+Oªz0fZ'Îc/Ò1Ÿm<€+0Î)ç'ª†.ÉSPoa[‚lWuŒko©› ©0&þNÞŒ/qaìpqöS­²qŒ¥-'~NÔ²“ësp8•Ï’UX¸95òÞfp2aQÚsÍ×®A×àÆÜþÐMp)¡îòèIî§u?ióyxg ëËÝ!‰¡\žiw¸DÉ_«{Ü´Žub9±7¾qà6nzö”Ì:Ïò©Â1ƒTL©¥¦»cLî{„¿Ø)%÷ik1.N÷Pć*Õ›ì5½a’ Ợ/‚Ú¦ù´ø~v˜ÐN¬„ Ç]É“Sý\†¥EW1ËüE÷Æ<Ï„?çÿ½)ªX«º
+å¤àïZW ïgÜq[ÜG !EzÈž¯>†’ÆC]ëã1Øb‰¢­§†H4 ùý—ñrIÎä¸ð æ³Ö‚QïÇÒ1Zrë8¼2½ôýý%€fˆWk
+‡)*ÙU$™ñŽ·@[¦[—þ¶ Âù‰ 2†k¹b}EÄ9hJ&6Ùž¾‚£ÜRŸþtfþ+›öçѼV±R=T†zómæeŽ§úÞ _úGÝ@È)!m‡Û°U´{fÇ…<¼
+‘¥%°™¼ŽºU‡ÿÎB@P˜ÀŸçcmwfû°9æC§¾@â¨×~ß?s#Íý27òu#qŸdöõ,.'‡QÐv²xYŸ_:õ9x“ ±•Ôb•a °‰MÀ–¹£1üMG•×ô~
+uÐHƒÐ,ü¡Hªg¬ÒV6åÛ˜ã#„¸ÒhÒZ•Ú×Q7PfŽð°½Æwx Ñh—!óÉV“+’³ýÝò· ΀8$ÉâçÈvq+ÏŒÝàšjgéšb°’IV²£»tBå©h1¶«;£›Å†Ùnë
+é$ ƒ£”˜‹Ÿ„%¤K yUö0ÐVÒDçÕßÕ¤ƒæT/Ná_´}Qé*Ž=ÅÈÈ0§6Ì}¶).)n6ê>£Uä¢*þuûÂ=š ‘#× ­)Fï'„Æ¡gòÂðÿŠé¥ ð„åz}AÄì?K“-—®ç~°r庂}•ñ@8ÞG\Û–‘Æ 9DGµw˜´Ïn‚‡nÐ%ªƒ/Jƒ@Ô᳂˜€;`Ô€L 4) Ä™I4Í
+›wHÌø®
+äªÑÎß1Ÿ1±±\—ÁïmæY‘#X,Š³Øºs‚FAjKtèïuRÍ’)Ñs^‹ÝK¥öïʇUI9Ú‰‰#öÁ­4 JS:¶U-×Q/ Ãl & ®úw>V°^éuŠŒ™åì ÀÊAŒÓ„:Žh³bÁå:AïY -ÛAÈْ٘.&”cŠBq¥ìÒÆ¿v&ÑÞóØKfˆ€ÙBö‘»Êð´¢Ñ
+­%¡û …‹¹ãv«“­c”QÐÓ½—
+¥¨*0f"$=“ðÔƒ9'Ùpà²+ÚØE9ͺ߆J«,ANñžupB°45CA°´ëœ¯˜ú&"…Õýº{JÑ0l&k@ÀV©hÍ"„Fƒ×ìE!Ìz0S¶çU¼Í7V/LÔrÐ$|ò;–>Øg—ÎÓnz¹›¶
+‹bžð¡ŸãŽ¸ ÂÅôy›—¯`\Yl¥/vu„LŒ‘ݾ\6Õ}w‡Ð l½Æ¿·k¹d6grã¤
+ð°Ô«'ëÄ`OyÍéÕÓZãð„l‡ÐW¹ ºòrˆRÆI¦+_+“’³Z—JйËQ¤+3mTÚ\ÓûÝ´öé/­šGHfVó4±˜¹»³Lø øÈ+òôqqXlxŒUï±W¥©­±h›#&n »Ã”‡
+ªiÖ™­êç°0·¯5rD”&-3FÞ2(ÅŠç´a©'òÑrcN.ˆ#úgñ5Âüúq
+÷ÇåáytÒÙÛçŽgS•… Wßþr1dz¶Tקl9.10ǦAÍV†!ö
+# -&­Q€ðɱ0ûçü~UâH=kMt-†Èy­Xë©ë2mÂÖ¡ÍäL?âÓéªj´„t$_/"yÂRÈå@4¢vhR´}Øc¿ËÓÚÉ(õRqÊøy&i±‹áFÞ®“þ~*ÝÒ•úÖ{QP¦KŒI±hŠëûN6aˆÀrÏêçÚ±,ÕÔQv¾“”©Ué½ëNA%“Œ»»ø<psÅ-kE‘…f‚Ù­‘d£ÐqÒI3¶ç·ª|ÆòOû8w ÀäÕ{Àÿóôf‰í’MíºËׯäùE=R=êHžo,Qª Ò>˘šy•ö5»_E-k“±Úø¡Õk|e/&ê£z‚ÐK†édZøʯHAñCÚüÓæ÷‚\|GìrE~e«Öé Š&ÅV]8žëåÛò‰bcP« _ñV_V*àB Öbjmb‡VP2J"Ò¨ ²ªPr—óøÒKv>“‰”ž Æѵ©ì<¦À4¶1<ú
+6(cUJw{95ýÐ
+çáO¢ *ž6ASK²2Ÿ¶NóÏæ„)æ
+}F$ÀÅ^Of=2ëÜ>H§Üs¢šŸ‘ô ž…/—ǧKò3M=SµFye‹^W“Jíèw´ÖºãH]Ä*0Ú“3k(?˜¡™yîÈQtîF7i‰¸Hé›þùÄ4··FNGl$ÂLF+kîb!~QÞÃò™‚^œ ÿ~ ¦ŠÎ•hØÕHFC1Ôæ¹'U‹¦Ü”åÓ ä"!qð0†þp''è|ùU}àÁÔkôƒÜ“îɼ½?Éø.Là,ŠòÎmֲ䄎Dðó*L`¸B¸‘…çéV¡¼0û…4y ¤FdhG-å9çLŠ”®Lq¾S3NÜ––Ž»0x|À’èd=î ñꤓƹü$„®2?Q‰Í’g}I˜dóó‚çûëHf‘©Â]ñ´ç )ôÃ7TÜ3n¯ Ê6NÙfºÉ /´õG&úàǯ>Ðö
+­¾ô夌…ê,ºÝ ‹OÙm‚QL…€õÀc%™ã߬,l¬¢„ ÂI\–ü,;f? \@îÒñ2\›c² êH2À$lR¬C£dÙÁ6É ðº‚U¬Ûɘ–²Pv*o!ëS5·ŽÅ½)ºçŒIëJ0¹ßÒïuAðì~Þ!ò—´ ƒõ*|„å™ k™S—ÕÒÙl,;ä
+fß9)ܪ‚å¼CÈq Á¯B% “qÉâ´ÿï_NÐ}ñy»AÔ[bÅhÀ°½ 5×~Âd›Y”ÏPÌÚhΧËr0Ä©œÍ/ÿžÁ,ˆŠ‘ ,±|YX=ºvÂâ™äª'
+H#*­  Œ@ŽÜˆ­Xx›¢ø®YÉù%®3˜ò¶ø¤’“%”’ù˜{~žÀn)¢Uö·0°Œ…©š Ë>äos®ÁÓ»¥”rTÛçŠÚ–˜Ç~7ù£°Ym½DÔ!ÖÆ0í£ì Aʱ›ô¾:‚|.²êˆ¦žšÒÿ?mXg˜¡L«VÖ­9µ±£bEûQY•–˜¶`.C“£™ˆ[aéµäЩ¹he5…&…ƒ¸y ¥˜Ä!áJNj_Î0IÁ
+n;Ñ·ý¥Ÿ¦rY1lÄ;ÀÄÕböY™Š ¥êÊ0óV Z†Z*‹kxpóYù òNê˜D"¯¸‚ÉÜ'Aþœwò‚Ž×¬|¹'Ý“é0ÒÏß…é…©F°p kß*ºÚ«ðÇ*Lg!¢wH5æ]÷èk©ã5¤ugêòÒ©md­½1=æüä%áøÜgAŒrQV Q$`ä¾Éñ–^n2A`^a¸T_Ô…¿­ö°.à„/ ¤¬3‡‡uA'U>=›ŒÐ®ùÁøIš×A‰© kßp⥩2i—§l¢º½?©ïnƒ² õ–/Ÿ¡õ€e
+×òV
+jDY*Ï9_=[LfeoÛ ÏÖDPQhÈ\´žý ºØ±´‡""Ÿ2©ï‚nžý<ïÏþ£oÏ~û'Ð-É4™}§zNz¸™|–Ò3Å~1åÅõ9INH€ÆGB?RxWwöÍþø9Ý,ùÍà½U“e¯UœxK'IM¿J‚J5úÙQ¿€~9(·<†¬s¸8mZ§
+ÂÕâúŠ"&G&ë+20 }ÚÝb*¼ùp®_þ«ç Í"›âÊh½üÜ"/13 þÈv}|§NÐxŸ1‹¤â0½…qÅÑ;üñjfvÄ,%®á¢_1¿n˜ëŃR+t©®sß]¾–ÉzË/1ÖßÅÜšòôüÐÇ7jä•ÌŸn…ÿ$÷¢å±6mœ·+yÃtEM‡Hß‚©¢ÑØ¢Vëæ2 êb™t*E +ÇJ&· fƒ“µ)çV‹Ô‹±¯Jê\ûSä@yä´*Ës ×ÆÊN©@P]ìÌÖ8,ú‰O-\HÁ´[»ŸÃ ɱÒÃdõ~ÞI nZ©ò«G•÷m¬\å^4-)U0¯6î%¾@~ïT|éƒ,ÝoIˆÛÛí28keå0 ê^Þ¤Œ¿¬“ŸØwÊùÒCi
+l·*1“cÞnB%Ä%Ì×F=@¢QäKlžûåùêÏ„Ù¦·1¶ž1s¿ÒWåª8LjTß´žiLqð~`mo·Y¨
+ø€ò°Ý!_õóÓ¯ ˜ú)7"Ež|dõ…ªû{ú’yËJëâ/ˆD*<²™Ûvzß ÖTÆfÁÍÐ9–µáÂ(_]!|?_!‡Öî jÄ)­ÖýX@
+DÃK\µZÔ‚-‡è›ä‘VSÄ92ŒOkiüùãÊïìYx)bãÛ
+ö ²w¥TùŒÍÏb^-æžÓ!¹§åY ÿrä´øÐ îK³Ìrië¼²7lŽü!ÃkVÌ¿ÝŸ¢°™1a]‹¬¦‹ð#ù³U‡ÅpÅœ/¿½ŸzqËA_ÃÈßèWQçÇÄtAXq,±Ÿ–É/T;OÂ1±HÔ¢ùJª3¸TŽêõFhb+b#~JI,@ÊB=ªgUÂÚ™Ü^¯¼¾Î¡à.ý|ê ¤s¤ãĨ8ÇÒÌÉŒf°‚oЊŸV¼IË º˜‚Zʹ䑹Ú§x”…±fwñ…ÆL=ƒføøðâi—!ÇrðºÏËŸæîçgpãRl±(¹â`u«-èviÃêQãô°—ýr„ªL1ñg^@b¿•ƒV±Ìõ¬ý€*øåÞ¸¤Ö¹†}"ð‹pÖ!"ÊÞÎR¤W$=¢ž’à<ß¡ºŒÏ8W Ã,Æx ¦ªiz:ÞR<iž||
+¦Ðú”åÔ1ÿ+*Œl#PL`0
+diHµÙ®Æ‹à·kÜNRyáˆ4kRj¿1¦Ž2C€ÔNn†ÒK‡¹’¿¶¹”]b,p‚ `â[>¯j¼6O ž‰ÀOMK£\ôÎj¿4™»@Ùj>ÈC~dK¤t
+N[Ž;ê§fP …f),G쩹Ó;XÃûæ
+ÈB({X tÈŽe=#Ž©Á»<9wôë)ìªt%ËÄû <¿Gƒ Ñ¢Ç
+^˜Å¢Þ⤽
+={iT`¯V›ˆU¼:íqkóS"À¢É4r¤hdÛ.͈Ñ˳Õä¬VHò¼Uãgô
+½)'ŸÀÁ¡–¨)’’”Ÿ¡ÔÇCý MGñ9cE-ºÁœ¤Ü‹¼ºb*h“æ?×ôÄN´*áÒò»¤ÿýÝ×úô׿~h4‹!oíù§våü‘†PÚ·ôöŸ™ËÈdçÈýÖõºÞ;ÊCÓYœ a2J…â’wzýºº1²²ÂCñmôùs ïÜé×vÁüûÇ_ÿÝ51Ž}¿n̦h¡?Bú£?Aqþ_¯X Z=íß#׫
+e:¡+®Ë¾ƒÍ‘aØù^¹Ó:[¢@h ¦Œ¯%- ~‰ÎS ¶ÃÒK\bz7¤Tb¤b[©LØ$=UB:ŸRÌ0asˆFC`FΕI9¬@C«A@„e"1ÁÒ!RW‰K½AâS2ô¹_^îçÐ`ÞÄöæ@Z®Œð’t9d¢¡²9ÂnJ›j©
+P%ü´gSÕ!Àjº‘©8§K9ØTˆÃÑú"—7;C3>œGt3±M…™’Ï—¸+ÞHžAŠ¬A™´z€¬Ï[Û¢€`ƈIæ¸n©‹s$}[òÅ¿¦UJd%„áFܘQÄ¡Æš-ÎÙÁ—,1Âí¸,þ®D‡ˆ[>+j«ŸŸú
+— ‰Ò`>ç4ÛšCºq¿¥—~- \JJ_¶>ë7ª%<Ø› ˜¥tÔEÿB0ø‹‹óýÆ­i×^óÊ¿Hð‹À iÃ|uŽ†Šn1Œç[ø);¦ªMC‚´)t/(Æç
+n"ÊÙã¤d‚ÄÁ¯ÃïÄ£ˆ`vê—sâ##Í@za4 ú?Š#`Ò3cùeZCüÅ@1L!2ámK0?JŠÀB¹ÍÁ3@/(Ãq ¡HPÅ°‰«Äèù-y—CÆ%VsRÑ
+H_í(dUùmÉŠˆoΆ·¢ï‡"}ºMñI-G©×¹"cyˆ¼
+"n[’ù;43!¥+j’¦M×zè3 9xØî%ðrÂêÆð©$©ä8Hï„GÌi‘±‚D³Š‹Kéhy`bÁ¦8V£=•ðÌ"Ã?' ‘Kåë +²¶k" ñ&…†c›|TÐ{Ð/_â%UÒÂ+bà/«{MSºÅàÀzmãPôíE²ÿ7˜â>ŲDrÿw »€ïË%xâ½;*áü˼|ïX/vH¡±;Eð8ÐP/ß™SùÂ0K`C„—$FO‘Ä?Ç£øfñÑXþû¡h¢Ù<”ek~–‰Œe Ü=‚
+ª>°’y—ÐÈW±Õã›ìwÄ{—cKc}/Ÿ¼<踻wPV %
+¬iß¼BùžkÏK“=‚yˆ{»Gùëº
+˜"&y’Æä™»F#Mxøžâ ýE¶jlcF :7dW·T¸«H¥×8 @áùr®›$sEÝir$û;$—4 ¶búeà –¼â¬wvÈ€KÊeû<%ÜÖ°\'NBRÈlj´…¿CÝ4Xõ¯À0MfÓϹ~(‰ó\ñé#?kÈ«ì/Ìo5Îapóv¬R‹nr¶¹û›­¢Yä龌ˆ,™„8©aqv2螘d”‡&½ÚL nž 2]ƒÞYó£.§„$Ï:/%~Ô”ùã3 ëé;ˈ—¨QÀK€Þ ¶`G/D| œW‘+¸ÿá¢\±Y-Ž¢U¨´ý¬InGê h0[e»„-`¨O_ð®Hcgœ$‹Ô´¼ˆø’P‹šwâ¤d4Ɔ“˜îø@Ìípd¿1öe@“Gó~ŽâM±¥†ì!“¼9<A ‚¿¦¼†=”?0„N¸õ>êÿòÍßPSÚÔ”•pú”Õä#c\D“BofÊ:BŽ‘ô9>k®
+¦ƒØÉ&ûÀ5Áš*Žø„­á0j<T
+ö ›ì‹'Ÿ¦Ai#€Ú:=C±7Á§¯Aæ`µÓðÜÙ¥4iñZ[»BÚ¨6@Þ_5´F(JØWÜͤkÉOÂ¥¢?H!ܹ¿ƒ¨õ.Íj`1¡Ì8#š»âj&xN¯}O¢V9Nb…„VmÄÓ:`—î^%º¼™ärƒ¹( Ò…’í¡híNfÄQE­‚@`:“r
+‡ó²Z8D6æ.˜UÅûÓŒD‚½h-CˆqÃå:(]øv’úO~ªôÎ?#èB9žbÑaÑ0
+±á$ÀéEÔxÉ„·Ø`ýH ÜÛ­ý¢ÃpŽÄ›“Wù¼3*ëhå>|è˜A©ãµ°Ÿ%Ž/š7)RË|úV¡k ËómÀ!l2ªüôª,v•Î
+Q^„õÂöÁXÕ¡ ·6y¨–¢D y(y Ì€•´£ä¿™&œâ«Á` k|¿tò¬}N‘.ƒ.ƒèG£²°PP/%_…Ùá0k™ñ Αžâb=_ý0F
+êÔ§ºk•è ëÊéE«Rš"ÁÞNØ­±g(ï¨õ°0ßG á:0Ü¥l¬Cü)á Y[¸,¬\3ê%³Šß §õ4Í$²ïR¯îàêÊZpu²n E¾0ÐáÀda€†ë"v]®‘äêW™o¿)dÇõ¹_ñÃ…"`„’ãJò ›–;£æ9´þÍ‹Wµ˜CïÈŠi¾a‚¯2
+z‡ü$íà|êµ™ÆQ’\Ôuú£ø[m×à¹ÚWQR)ìHŽõ ƒÊû;r< ±*¨ŸK^xÿϯSQߊ†Ù`%6DO5,0%µóY8`lmc|,M}(AG6K–ÚŠ—+ùe%œì®®€ pCúôï µ=!4óN4¯¶ÞpÒ•=¥j‚œEm%2΢;VýÆL’í
+Xkëw ,ÛÑκj²nÜoy *3d¼?CD÷Ù鸌.€„6ùò¿dƒXéC…„/2˜öô™1.†Ýq”˜íN[î h#ч.üÅŽ:l>Š°¸’·¯À †FWD*@Ä'ÓÅzûaƒµG¤ûí}GF,ÔÒÞ}c4|L!B%øΕ¯<“ãå]¹]OV"àŸf߉a6è,ò‡‘)!)ÆçGë„ |£÷o jm5 þ3Ë&ôq¸ >•öV LÐñë?Ÿï›Ê_ä7î«Ãn”CElÂ;ÌßÃ%…"­ì(Y,2Öã$¥WF›ãPâG©1È)6Ÿ¿S¾4É<$Dq›|³¨º“R#ä@zŸ‡Lå$Úå¦Å‘wªÒH&ß^qTãoúÐ
+¤¿¦²ÇDèñ¨Ì'ù(`GìÜcû´aä†SQ¸ÞIƒàq49猹‚ogXZxÕÀŠë€ªÄÒà¹ü>ÔCv µóæTáAí•œ½èƒÀÂ1NOe§ %Ÿæð£KÚrz“ßlæ¹ÉP³\[é1Ïv´áØ¥âF“46ôx‡¥ÚaÒvŒÇoË€´fè$‹gû8¶Ž”Žèš¥êÛIuU—ú/ãe’d¹ Ñø*¤8¯½­[8«îûoýR"äÚWCÈ|È!¿þñ7[U,\›kâŒá?Qþ·D ªr*X%ÙPð}oD‡–-˜ nö=·ã U ‘½(5Y( íȤÊååí¦À`¾2SgùTãm5¾À)Š’î7BùHô¾¾³Ý">(LY’ñ8›ÇQàÔSQ ¨¢=‰l#êþÛN gQŒã"W‰ÇéÂatÁ”™ Êé>R49”¸9;t¤ƒ^µëk0
+¨Ô»ªˆ¡ÕË ë¼µ­ÖehòÄPÑ(Þ‰OIÇGŽØ˜qm˜V ¶@Ìi'Ì‘U“n¯ŒTï¯ep²NÖÄ“åmŸ£\¬–9ˆˆ·×¨
+!§S†V))—B$Á¥ñî~\ãì‹B$Á§™¸tÛP"\.TR²CŒ'…HÒˆnò%ß ²PHË€„=ÝIü‚ð¥é‡a^5Ñ!Õ§©$àáÃCøRb©ÉÞ¾ò(x}‰í:àN^z7g^!$ð2Üy$]t!ÏÞqÉò¯7¯BVê(éL¶ÕbdS#)nÌÇ^ òÖ¼MÐ%ˆc{ˆA8À ¸#t! æB\ßH ‹R/QS£l €èó~üiÇ(è
+*R½íóÏÄKãÑðÉ<vÑ&DŒ1ÞÕá‹££h´âIæVø9¿U’ãF‡'PŸµ.â½ÕTy.q¥P‰5ÝÚQË:j^|ƒêäý[!áèQ(¤£¬
+H‰”—Krd·DW =Ô\ ’ãöлp„GêýOßÁ%X¶¤[ƒçp·­RA‰D¢»ëèÚc<þÐÞžsˆ¹[ŸÍýñׇŸua:dêÿ²nÚ»›˜éÅÓ£Í9[7»}Þ‚ÆSuöÙ›™ŒÁIÿ|èãÏ„Ú³™:ß”±"¡ý¹šE(Ÿôßo@ÃE†4]KõqA|õ¥­GŸÁ•tùpÒ ÑÆjSemHS¾Ô"x°>þÞ‘\F[ÒÇ°¶AÃ̺†Æ>†?
+±V‘´­à:«Ë¹‹ÎPU’þ”PÃ× 1'’Ú˜"×Nä6º7™S*)Ð)3š´‚hkd•B©'DŸ‹ç\¯½
+KEr¿Ø¡
+oŠóTSzS’*ÃõD2¡]FL—:F—ÍÕ‚?§­­sŽb¨«´9”ãÈĆdÁI ½Rü#êêPBÇ<¡dÍ€²Í†×hðÑÆš}Ÿ#ÓûòÆ{5C—]Öhç7äÖàXç¯P_A‘ ¢½-=çh³I;p!ÛHR¯L±Çm㙹3Ñì’W¥x¸û‹”^9ù¤{ëQpbú„¸›ãIBYòÀÃoù÷ R¡ 4½á]ÚxwÎœ] ýxB]Í •æ*Š®€´ŒKÄ©äœNõÓÀ¤2ˆLTrÜÊÖ§‘w-»zóž9½•
+Ò‡Jº)¥X'Ð7Å
+Þ‘ªuÊ7Õ¢Nm¢ÊÃG›·âOÔªuTˆ¿ÄBte5Aܤ=î„8žd1
+n«óòMСý²µ_SÅ©$ÛM³ž|s U©`kk?ÌP’†Te“üm~!Ðȉ<çнü¨èÙa2r‚ -ZrCîëâêçù+'rÒȳF'DåîdòqAf›LÀ勃ó¹:¤4åS7äzTÎÑØNðÑ©M;,•gdŠœ µ:¡kt¬1Û>M„(ÓFÓ Jà —ڪ˴”ÄÖUÔ6„k. Z„ïžP# KÇQ_Go’Ú÷9™Zv&ý/ n>‘3”tC vq=¾:7„z3.x©¦¨;Ǩd0Ž¸á Ù$x©÷m Ð ~¢ž­m›|ÀURýU«œ<ÅP£‚aph*†e¯Š/Ïà'7-ée)®ÚÀm$¸sÍ“‘žj•¯Û§d¸4å«sz^ ­¢^ÁXÑî7#:É
+'9ëí9‹Geƒêë6¶è-¦ÁŒò'’ODÛºôíÂGt&²Åa•>žì}ö˜Õ£´%ÚŠVÉ<Ù!ÈůåŤK?¾Ò€^×
+¸ï‰ôÔ™¯“ùIWû›sˆÃ\$7 ˆw4Y±’×oC9ÉÄs¥+®cH
+s“<¯BˆåÌ·b:˜ÊÏ´ïÒW¡~€0Ûž2Ý‹ò7C~ù2½yÔÐç­‡æÒŒ~¥É0EÑtKh¢] Ìb&aŒuklÇÕS !°üR5Þ R·¦Ÿšã³^feºúLQØRw |€m”3=è æq–øR‘¾ÍŸç0>P9Ã/ɱ0ѳ1qB}”¨Ïti’ÃìÐÝS%g¼fÃÈöà¨vr“ @ð¡i ÇÂb_¼BéðNôß
+@gvaúÕ3P'’ÓÅÖ)%É_‚­µ£úÁ1èÿVUÁÍ0Æk0`
+µ®Ž¢_ßHÒMþ˜Ïý°$º0Ô_xÎÚ-~ŒULååw¥r‘HD—q;Ÿ³ÞBöׂ•Ž %ö*'yJVGêRM_ü¿f„nIFB÷ÕogYËým¥9L-~Éþ×Ùð ôù4’è3E¥ÙíŒB5¡&nôÍúÀ«3À­œô¯‰7'éáŸÓ4’õîôçkF{¤_Å
+ÒõtšÌ¶‰|ȃv&âDÊ0¡SÆ‹ŒwªgnâÔå*òé*+¸oÚßA8QA5Æ¿‘ØéôZý
+#47®zŸ'!
+šŽ\ÆìÍ9YþM”5n!¨1ÿ–åæÓ^/ÿ
+bD3«X^Qqúõó 0N4¶cYN.Æ­^¾ÕZ)C¸˜‚ˆXö¦¾Ö&ŽÀa´=ÎËÑ…`èþãÔ1 jÉïís„È}ÖÞ7Níï&Ecìbºn†Cmœ™9ƒ½rE¢ÓÏÈ;Ó°ÀÒZØ’é8‰ZbX
+ÙÑèYL§ÔLE^¹—Àõ”œt BÂIáGO±G' • L-:z%J{8,Ò`¹ÙeE¨#˜Ìp†bÜ@~}üó¡??ÒmãÎÑQhQÆ!í’¦†°<=~¿1#wå9éçk|µüBÏ^Âbà^¦“e$¾ i
+p­ÿ{=?A’#5“²:šdOŽ­KáZ6¿¦l7Ûü¢iùÑf…ÂÃà}<7Ä ¡¯ø=¹M×W¡Ì02é7Ï9Ⓣµ²"ºÏ¡\Lt¯E
+! ú€3݉ô˜óÝç7¦ªù3 5´Ë_ˆk0mT Íã‚ Ä6/>ˆæž:Rýdž°g1QQÖìjbFQ—«C‚nŒ¼t¦BCóÁmšr„ôÊ »õìNb ’$ôüÛN„Y l`á;;\9í’¦wyÕ“zsHP²À:Ò>¦{n{r±Mfë4¥¿|!éÅç||ó´^¼Í¯z‘ø¼\îƒ.ý½AžS:Ö4
+”^»åã®öb׮ű‹
+‡"¹zú&sJÏÊ žÖ¸‘´ZÌ#tÄOÝ!æ6Çïñ˜ž6H÷9W)RÂŽÄ eÖéÝvåÙ¸“‚Àz.Ì
+Êìž«H]P©³6UÚÅ•†,e6ƒ.4ëƒp)RÁ¼îûR‚ÐÁ8ÑÒÓ«ÿÚ¼_Yа §~ª²¸À»L+†Dw<„sÙ•I=Gßk¥Lãt®ˆX ”Rç û!âÛÔ>!C’ÌC¶&ˆohXïö»ýSÎ]²ô¾ÏÑÆæ]¨¶B¬RFy¤O³©f T*;è€h
+@mq²®1Û,öÝAls5l„ç$HwH¥ä2ë¼DBÈK\ÑüS´Ì\ÀÒZ•­Ò´ð ?ã©qG…‘¬ÁÀ'2½‡)àÃ*¡ )<Mz9Oöý– ©{7`Ì‹ IšÍO°Ç&€«HD–=oL4Õ°Œh½"¦–L»K#ªžM÷ÅzPW«ßåU£G”½`«¦¤q2ÀSò M2æ½Ô»Y¼/’ñ—ÛíD|Ft3i2îõl'¨)²SÊMБ¾g‹Ò´^ÆÎ&¸–¿È„šyvö)Áɱ¼§Œ“:þ­âìÂØ+LHŠø!¯ëœ,%…½"ˆ–ªNt²å7!§ÈÂÑŽ «Væ0Z”¡Œ=È™“¡N¢Y¯…ž@5"jnOýlEˆ8¦Dä|݇Š§[0÷‚©ª•scñUUK‰™1‡H]7mcrÈ"se‰'¼0GŠÏчû‘²Ð4ô}Ž°–ÍBŒ‚2
+b<¦Öv…‘‡Ú
+!ß e­ê KßÆ>/iš9„¥»áìÇÎò¥ÅÎH‹©}ñ1>p««ß+Á =ã]Ð4h".ÓWÒ‡#^¤Åÿ
+z¹îÞï¾?›IA©g‰ð‡Ô±9h@†É‰O%Ä¢HÒšy
+„ie=£èÈZ‘ê“9¦Ó-DXD@æÝ!Rx‘Ž²Äž‹ŠÝ"%¬_r’L³$¦ç+Ëšæ'ƒJhCAuÎ÷j_§P^îçiÖÙû$ÖŽÁÝzßÉè&åØÄÛvŽñÝÂ9.ÉÕ%KÏÿ`ù~Ù9eÐ;P›—ܧ~T£ân&ÝÇÆÂ<&\“ sç­ÚuQQ³
+ÒˆMx¼qàfnp‰ ÷#¹Ä-i22Êœ‰‡ôHsYK³— "µ Ÿã1„g¦A¦‡ô¾„xA—B¡e€Æã9día^OÃ(ϱÛó-,`.èÖ :Ä!
+BEu9„9Á&å\Ä<¦ ytŠ¯Jp¥kEJ’üEXŸófpQzB)(¸Ö‡Í4R›ú.ýÇ·Ÿ~û>~þýÛO}c¾&)¸bHº·‹ƒ–^ä#}üùÄÑ Žc)ê‚’‚º`ñ¼C‚ÄÌÏAq‡Ä 9Ñz\ÇYÈ‚!™·Q+裵 ÉpPHYU—BØç°Ù°¾iùBp$´Ó>‚Ú
+¬2`ÑnB|O•¶Ÿ2gç
+t Êܺ4/Nú‰ø¯ÈéÜ6ê%Ä7½4ü±¹yÌËôÝçô6ÅïµyÛ…÷ÿûNi ë:Åô¾'§#m"Ú{õn‚J+"Î LüØ!€¨l]ŠÞ‘>ÙNqî´>…ÅèIŠa'ßÁb
+š•ù=™SÜ¢4Ш”‘ºP¨D?ÄAtÑqLÆCHCû±X)§ðÏ>¼ aàs†£ä}à!^K½º~˜ 9!F.8o S"g5ŒcÐæå‚I
+³Ž/uÍFY6‘á>Ddå6׸D3ÑÓ¤–”†‚¶ ¤
+’¤Ò5ÐWD6ÇÔ€‹e ùBE´! ‹Ì0‘“„íTìO¥ÿ¼M«P±gɇLBÐcæÌfïdº÷UãSftáMµ¢J” ©õ‡`Žfv¡Ò¹é->$-¦‘Dö‹¡3H†ï‹5úMÀÚZÞ'Usþåjº§Ö¡Ð®v'T†–§3Á1UÂDB}ÜQk¨sÔ ¢š"ÞÊXµ÷ˆ3À j§PLúb:ú¥ÒÀ¢³ƒ>öãÈ,^T Ͻ°Ö 9‰žº :ŽIû±hv+ÔVx€©„ÛC œ3ªÀÓÆFžÛ*ìGZGì úî ÞWkìyxb 'œ& òzqZˆÑ΂¥`¾‘ƒÙû†aŒeÛ= ü(»Ôü³oŒ
+û>‘ЖéR;`eÐHï}üR–¤¥a\=8®Ö‡N~ ñÈ
+E|*ú'lqÊQER†÷•_<G¼è5r3L‡xµ/úVÔ?–§°§¥†‰Í‹Ìßa³E9Øög؈Œ0>¹Ø4!ìLuF‰Í½J½BÌ»C:é¯Û««œVÒ<´i=\aˆä…dÛòáÍ5vŒÈÛ„D–5¬t7Ì8ã;fÝ $T$}ì?w!zì û»-ò½`‚Z>¸–»¨ÃÇúêiÉ´›ö}}ziCÕð­)@‚¬APä%ìËxœ –;yid×±ËrÉ ÂÁþ†ð8kSúü‰£Zñoˆ*§ÈȼMK†!g~ƒ»|/lJ³7´žš#îÉMÑ „€ZS8ƒf~NM…ؼ¬L7«o¬ £wH±»ä¢/Ï]dÍÝêæÒ(ie5ˆâó6âVŠWö}Ïâ÷/¿äÖ5ÈÉvB”Ií’²Æ2<€ØW…ˆ×|àQÅ,ýä£Í8_•EîwÄ+S÷óFð…nûëL=—ýýª !(;÷f–ý´)-E6Qa]¶qÒ§"wÌs°³ÊÍfP$CÅì¨r dGYɵŸÊðÿ±7!äàäÁÕvRŠÖ‚è
+AæÅÙMÓ„?`²¬èctr.1ÜAÔ›$í›^0â?ÑÆ:†j6qËDí‚嘓[Fc©ù‚ð ÖÁÐʉ@*è’¦v²<¶…†zŸæ›™£/ƒY¸‹\7]ƒTR5gÇÐ;qz}œ@øNF—`Ä–m R´;AŒŠ«eJÖàÑFÐ0ÛÅ3IX¡“
+ÓŽDcÀ÷eÄÑ«ãrœú,ê"³†ØÌ!© ºf^-nE[fk:ר`
+´+‰ÀNžl t­à/,¶àâfN!-ûäÉ—`€ÂzÌ%ÈmõÄzKÎMZæòCÞª¬Å?ÃÊ9¤‰7è‚iEŒ|öÄPîMÇ›°T>¤Žw{8BŒ+5†M br&ø‘Zög£,€Ͷ‡Á+²)0Óæ)vÉ’öý^Ä*ÓŠ­»QM‚„¾p¿#'Æþà„²Ž[›Ü•â$7„ò¦ªû ˆ ÛX¥T™Ãü®
+¦cqÁFY݆Ѳ¸ ƒýÄ6eì5Ui m®d²ýœÐÓjýüÉþÞ”ÕË›2Þ™€ºæ¼‘žèÝËteùÒãëù¡Ôˆà´qM;K”¢ÝöcÐe.>Ž×ýt±ŸŸ7L~ƒœl³%b@:^‰ËÛ-µ´W„‘]ìCÝK?2ãQ¢ó{?b@­–Ý}~1CGœ2Ö&=3ÈéÆ_56¸
+çz€ü^5d/ÈÈ! ãîñœ ëãñšÑäb•Òò܉kx?µëÄ4]9—xÁtä;`PÉë²(‘¥ã쉨 ÖŒjá¹/qC!Â;<RQj„…{ÓF0¡ˆvÅЧ±
+p¸‹Ò·µ´+³|ʾïÉúƒµû¹Z—ìsù¾ºÎI|DÊ8ñ²J¥Êv¹æ£Ì‘éå딲°Ü,ýtš¾—ë~º(Âq²® W—ŒUÌëM(ü.k”Æ—c¾(ÕT¸N+“îÛIòzÝôù“7ùÓ±Áð†ÙÅœ^5ŸŽG9ÿÇÎg@ǧ_¯;>ýoošOŸñË0 ,]<Ø}‰? DÖc+“æN˜‰Àâ09é!$ÿW‰ éÐ^Ž¹LÇ9G}” à~çH«9 íx6¿ƒp-ìwì îmBH&e»î5äRÒJ¨@ð{Pâ­¤÷:R¯Ù,ë˜kwU[o–…Â[¹„^@Ÿ^„§Ç’ÅР Á‰IRÝÇ@¬®ìR“W‡œ…‡G¢¦ó"…£É«+ÉYû5å3g¾„¤Õ
+ˆ±Ì%D»#PRÛj’¯Çþ($Ü@¬jMuXPâ¾êD¬a É…ûªñ´]âîºJ¿XfÓëÁ±á,ZóF@Y²K\þŒO§o
+&oîP;ìíð†R?Î…*8²~ÇÀï‰üçÃâ<­W–àróç‘×mrÕÁMØŒC,ä%9ͤ®|^¾$ø ¤_ÝßDLÚx-×Ç
+îÅÔyv|ÙßOÒ Ê’¹}“ãtu}«‰í(Ñq§˜kæqÞ‹³h±†kÏË!ˆ3ïg5 ×»0ò·&œ<ìDžG©Ì_¢Èn<Ø<M™O»])ñ8•Ó:'2×™öpSð
+ÊæàPõ-&Ú–ö-D”©ï<µXGdÒŽ8zÕ0Ö”›¥îŸÃÌ1¼ä̺E~¨Ñ‚'Oñ¼eˆ++Ê„} ¥ôþv2Cgü&&‘Šjkr…„áÌmÀ w¢ôú8EàŽ’Ïl+žž@ج £—Zw!µ!MÇi6O=|ñ¡ ë‚È%é'¸ÁNR*ÎP=Ø›ù'DÒ“ŸB}ÝÌÇW‡œjD-h¾!©3ßd·Q½:X²jBÏúâW1F¤ÚÒl¥ÓÓ9Qïkø«¸„]ð= èc^ÚFÀ ¡åѾ:Ž€ 1–û†Ò‚¨‰a.~¨J—µñSæF⯨{9#à"ÁßJvÞ}P
+_ÌøŒéD¯ ÏPAdð¸teßÅD²I0\’©JƒÆv.ú[_J|5‡þÝ8s\1z–—íc©uL#ý+¶N!á@"–Ns‰4jIæ’}êsάó7TXv+㌗9’%·EWÐ{(›F¦‡ÁTf¹\C–ªMí_ç_ªL$õIF°«ãxÃÆP†fÔf’„c2‹÷Ii«^0 ¡
+W}s›ñførŒÀ8@¶£¥€ÄÛó9š
+¾SöÉÂV Q-‚ä<‰©ÀñðÁL®WF_Ï"rË:¦ðBL:µ³á>@XyÆÿÒ·-b5Åê4"nnúúz
+‘á!Œ¬?J<Œ–àœÄyÙ¡^^ɘcÕ›Twhv‰2KÃWœiõè¡—º™{šû¸Zu#„¤;'i*dÃsJ¯©ðÐ5›_Å ¡|¹Sžâ{¥*tíºråÒ²Á¾3
+>ð„éîÅü­ªY˜®½å¬Þ#Ò?)Ü“ÕÀÕ}ƒžA=ßµŠ"CÒ´ö
+—Õw›C
+ÿ2IÞÒmÂ
+rœÙ¹•ß>1ˆuŠ]SSŸŽ1./gw@ɉO‰JcNøb‘Œ¼Åñºh &ƒOÏýål$g'ò[t6¿9ÌÖ‚œ(Ûßòä›Sý}ÚŒÖFšÀ+ ^ø^#†ޔ߶ihO ð‡†²çå¶út7<4o ±+€"£;B1ŸÅYágÃ
+_ßå]1P |ÁëçOáù¸$¤®òð$v #Àmû¦[iÒœa‡Þ}œ %3I;Úõ âWÝ~?ç66÷¯¹ßéU×)f£;"‘çoW¡hŸ3½Jùñ­Q¯ÅëHh,=cSÄÚ{›
+#R—O¨f‚–¶)­NÍ ²e_…{Oô¥É™LˆšT¸øÓÉp,8$Ÿ÷EM.@WÓµ ‚~Œ`·N©â†;ÑO¥ „8GsHlòC”// h\¾@ÒGz§0å &±+õêo2ŠNq-HÕ˜u}]¶K70Ó8ß•  aŒ,¥û1¼ÒÄ×Í·ŸÂ`Ke¨£?ªŠ ‰83w
+ƒNS:ïÛ Æ)“ºüQüÌGß:|?WbÅ9ê{i±qË IÄ1qö¾i¶“W¶=Æ5£JÈ>aÒ‹s£
+4 ¦ÀD¾uƒÀ‚aôÄj¶×MË8KþXãÃߎ*!ñBóÅ<†Xj÷ˇ91ÆZ˜  A„b²nû’ dcA«WHª|ˆCJ`‡EÉö4ÃŒ3í9l8Fv¹Ã{³ŠZì¢Ò™yWá]¬r“¤ÖMÑdŸË‚@cØF2“Xf^((U³_§(Ÿ‰EÖ1-ëŒØðÕ‚
+äÊS}ÝT5’Ê&‡d^
+Úâ…
+ó†+z²æ=·zÄŒ~Îúg&ª
+Ÿ¬¼™èã@&>m0íÓ¼Æ;ÄònXüñ£˜Y~9qfãé;\†fçÅwð÷\‡¢9‘.J‡Ýë#.ó¬œHá˜!CœÞO _Ù*hîÌ&ô˜•MÊCl*»UÌ1ÏKcüWÓ˜†E &z1Ë äîw7‹¡À”û¼s•®—妊ÿÇ(1LÝß·@ÉLò2·¤
+L8@êQ׈þ
+òàHÁ ›J
+Ù¦›_EŒDwáq¯¾Ð±–݉¸Í{Y¹Á‡ÏXOU‘•î5ÎM.#Lð¶VÇ(VœÒËø<¬ú
+á` d
+îƒóeð€0øì{”Ý(v®ÖÇVíoÄïÿþ1¦Ê¸;.^Vx!eï¢ýüï‘´’†¹¸ °Bãq¸rq1@Ö¯„ËÎ’e(Ã*¥Sf@ÒîŽi’¼ûÑÄgüÛY â5¬WÛ
+üÈVSmïhâ ÷‰AŸ+žž è$¢´ó*†(Ù!(™b>º÷qù/iå‹™ô£:¯XšíêOò=Z#îy¾ÓŠÖ¶û•¹ Ã?$jã@FžZlæ=\}Rè%KGáÄä±Ìç·¯B현QQ6"ÅE,a¹ü
+ DCšüoñ±Á>4ô‰šŽq¾Ã?ÓÌÔ[÷ùñA(hVn$PÞˆ ·ã¾A·ÕD˜a>Œìý=EÅ’.˜#µè븿ÁíØ~¹Dć
+. èXQ°Q
+È›ÈAËÉ7r=Úñê¸D–Eß¾ý‘=Ï Ãìí ÁØ *ð qêé$‘ |‹²„§‹;Лè˜ ³&v4­B%Ÿ$ʈ[•{õú ‚/È¥<@þߤÜ_õúçžþk[:6OJèǤi\yñ1<ÐZµ!ªøi1ª1»lPw‹KHÃ.þÊò4Hþ£xVÓ6X€Yø¥ì]Ê>ô•áa| æéGU )@÷ïîðÛ(/m;YÅ.æëN~2lî"I¬8
+5µÜüÆbiò2¨>ÓDŽLñ„ÜB?´Â;ÛLÆ_†¿xˆŽ€àâ()Bvš4#r¾£ÉÂ!.w $K’€v죘'ö¿ˆç÷ï°ü(6\ëꦫÊ^(j{
+©Uô@BlŠýòò anåˆðv¯‰E2uëÂ>ªì
+€„o ÐuŽ˜à·—.£ ˜àá{×G¹ôj'8
+Ô·˜ãJ¦IßÎi¨Ì§€g1YMoxÂv \ÀÌ;[©jUfnâéüuãQMF&ÚYázý éþ.Æ‚!èŠÅMg–·è“Ž›CX:úk|&
+hâZUƒïúQü F]ãÊ&@Æãöé#JœàÍ8-Ÿ
+XÏD°|D™uZ´K§bö`eÙ¢‚0WⳄ§³S0 Bò«ðÁÞwÆ=>¬ÝÄ7ëæ®øòðÝp*¶Ry¸ qq©uA¡ßO¸p’†¯O|T²¡ÏDÓjá->ª B6ŒÕYù©Gx|F-eëzÝz±óMså¦XE,Áqü,ŸŽ¡J óæý^ã¨)qÅ•ŒäOJ8 6\þ1³ÅŒ5x—Ÿ @®r¡Ñœ÷:çÙ}nZÉqŒ†*á…O«Ñ=89L¯îÛ$òjV¥Ì‡âÂnÀ-4)zýÞ0‹Žì¯$Ø’
+wˆ<+1Kõ[qRe´§ …|&eIïs7ñD¨œž‚Ý@Ï18ÍÄ€Ó¨&£·!ør ‰að_AÃÅ6ÃÚ9†ŒÇË«L‘/ÄÔa(Úé÷óÌ^\(SJScîåÓè®iJb±µX&>ƒ¢ÎæF.ÍD·KµÉx„Õ!lV£zÉË÷Ezý¸ ™”ôâ[Uϸ<#Îh-?‰Ž!Ó
+DAwXX#$…žRø”m>¾¦ozezIU©Ü% m] ŸË.aCÂû`ŠLÍè®=ЦO Èc×Ð0òFüÇKÃ,©JB]<Æ gväÏwzt¥3Ï()S–eç o¹+äò”¥ \Ñ°ÛÌ.T#NA”;—C|>ÓW=<ñióU?É4%Ǭy´Cõ¤9¾³ÊDúælæ­pÈW·k«nÁƒ–³§ )žÍAPf6+,yËñWy;$wñdÈ“?|9(~þåe’$ÉCÑèu‚6ÎÃZÛº…Ìz•uÿm¿O®
+wz+¥M)#~p
+þ"¿W.¡ ©!\Ë— Ò fÇf‚Xm䆇CÐç„ŒŸ³ØÀÍg‰Š·PI9­±L³Í¤ÄsOѬ=F úNo…£éÛú](Å€L2oVƒm{)G ÞþТ7¢V¬åŒ`´Ðø‹pmtÈ|øÈHE[…]³†°˜@e q“ÛÈÝ‚Ch‹h¬ua‘eUž»§£|4„ƒdNP‘„Ûøq
+ôOfÌUÑQÎ)f©Šçr¼¶º 5+óYdb]à—v#êa;(:>…¸„¸ë¨ùªÒHƒ´‰Ñn«ËB14Pê ­¸2GìäJ|îÄ äBÇZº¬Ë^beøe÷!@$pÆ­Ú@öõÿPl/W‡ÃBÈMàÄvsìv˜ÑÙ6üŠp5w/XRâp¹á…*üFŒ¢¡‡@è@'F¨až‚í4Ô½ƒ”‹1ù ™÷u–™0·ÒkI3¯ô»›·„‰$ƒÆŽÑwb¤G¨(™çC&©
+³Û’‰Æ’0¥¨f˜¢1;IööÈÒmûšª¹¶j]>ßÜÈŠñ ¯réÝšy<<ªç€d™¡„òAÿhWŒ›Áöÿe³,·¨7`÷}/ÚM¡•°•èì_ˆ™û5ûñ‰ùù‚ùçŸ{3Å¢å¬k}…<$Ÿ
+”‘HBEWü`  Áµ¹µ=¢ß¶ÄOF ŽKãõLУ‡Ýþ£·FTú¸/y=¸œšek5¨'È`zº®ŸóbN"´…ÏY&Ü3%aÁrÁ\ì±6ÍFîRSfV
+»–áu=R>â,áìt0ïÕ ‚É­ËýÖÝÓ1½H„Ñn'Ï©À°8m'rœÎapƒ¨M*\Ô»Aäe°Ú6\| –—f%“Wðÿ,„Mt
+Q6 kôi`ÐÊ«h"ESËüùÇ„³N‘$w·'[uníû´­H\ôº/§„[Ës‡àëà™K¼nÍÍ )ÈÊnDôÄor³ˆ¢‹ç‰ ‚½›m5›†ÉúXý”t”b®u
+Ø”—Ò#`·V“Ú3ÁˆëÖ-åîo9dÂq9ûNðTQêB,ö2*†—ßn§)­k6„>¤çÉ) ¶•¬ gVî4l1³¸ÌÕÑ©æë XÀH¼+â‘‹dXè›r€ØV™º¯ÃÄÃœËñr+˜CÃÝDR¾
+§ã£Õ)N%]^!û“~°†Ê  ¡¡ ¼ç4€¸:)˜õh
+¬UÞð>¼
+°=EV ÁqPúì©Œ€Àu¡dƒh†£ßÒTŽ‘
+gÇ%7†Ð¤7 òÎÓÖ¡XÐ9B=’AP&¥Ž:Z> „ ,'’gêôn—¢®˜$;2›Óüè^±[éÝ:ïN³·0¿›œÂv"Ö‘!q=ïË ä-—‰ÿè~b¼
+;³½féådÃüÆ:“ÀÒ—ª¥íÊ2]ªO³Ö™°=#àÃœK©.…­¢ 4ü‚2¾õ( L#­ÏÅó["ªòN<˜;‹œuš)Ü›1ˆI¡ 6{†^`î®Î0±òÄ؉a
+ÓÖ‘ß&À®»ßJŒîdØ·zÑqþƒû‘Õº”0V¾À—ÄKÉ;9RD¨
+¤FyÒkD|CÃÒ¼‹2lœÝå( 4üœƒŸ§äÀ|ʈÓóˆZeÕ¹ÏË´3+¾d%¨H!h¢í0.²ü¦G¢Ÿ‡žD¼ô•LùxpN§Tx2:‡à˜JZ.Š½¯Ó…H^Ä›©;ù¸rW Ÿ`mè³ë(wU•N 3¯ò]4¼¥µDŽƒT\)žÕºB+Žr´j¿^ñj~˜7F%<9ægŒSÑo+ʽ)
+/gÈ·ÒXõÈÌI²#×À
+; „õsš• áԎý×NíK™‘„p$# §Ïå÷*¸ã®Éoe0Z c‰VQÁ¸Òx“‡ÆˆðÄó2œÞÔ¨Æ4Œ´×)È1$E ´f"óhŽlè°ÓT%†™°¼vÞ‰„rzªŸ;O±äxâTÏ)ƒ”v¬¥Ê9J¾™Î_?NE'“GË112ŽýÆÿ©èåEÏ(GIBA˜ÎWÞYN"@Ĩ´K–£†$=oÑmû#ËÕÅ’bã2.YŽ±‡@[æ-ËQD¤åx
+±ä3xl—¤ˆhIg{qöA+ÐÄð­¸÷‚Ǫ­³RD§r·[‘ Í&ƒ=™·!täcɾճ|²€³ý<¦}Éðàj•‡æñVý‹¥}4¡¹¿½N_#Ç2ú˸שÂ1ŸåíËûrÏ@˜l!¥WLÃÒ§ÛŠ~‚‰©ÞÖ½KGG²#ã x
+œR耰¡‡¦m)éŠhWrT€Réî…[Ù¥H5Qç1ö:)ŽFÄâ÷xý¨ÉÛ.ƒ±¢µŠà2ú~?'êSt3ëÃDsªš‘~žF¼ëî¡5¼Ô‘*ÚFO™ðâð{Qæep)$‹)ùy&7MÃ
+èPêíÖ Å@ž-9%ºœˆ~+468_žr+¹îññUÈÛ%˜é0yš,ëkOÍù; ÌÖxh+¶*+–¬eäT\‹TtUPøÞ%›¤Ì– „]Ù§f9×OW1O\µOQù,=hÎx¸€q„ôm<‘…{×Ø/Ó÷­Ä¦GíG™SøZ'éxúVÇù4ÐŽh±½âåRq~ÉQt\bËéø8”4„RZ¼U'¦¤¨-¤«Û¥UeÒPW*ñV=ZNI"2!\,C Âÿr9
+ ÎÞ£Y«Fe°â4#î’È Š]è¨ES¥„ù ÝN“†2
+ò°ÞŠñµ]Ÿ­¹a%Ô:c+†G^XÖ!Ö"Y*Ž\œ”ñÔóÜòí7àÎ0ÿ
+¦À¤ë㧊*&JK y}³¨ó•èI–>ÜE}âþÉ°ÖÛ·‹æI7>
+o*ÒSöÍÁKp9ôØ”R»!Fxrq^_2E»±¼ñgNƒÙŒ@À_£•oÕÉ×½Ù©ZIþivÖq0]nlô§‘ùýèÉCÒÔ~±ŸÒÂÁ‹j¾Ú–f'ÉÀ…D=ÌOá‰Y½¡ir%ìÙÜgÕÝô7ÌfÑЦ…g /BeÔR½7´06äŽÜDAÞÊ1¦ôòJ¨r²jÆíCqàSŽ­x+~A:ë¾'¼A`cZi„W[qE Ü”L^±H|xÆšî­& •uæ²Ë€_=už~à#N‰ngM±SÂùku Ú½ä+*_ŠLÃÍŒ°×©ç4D.!Š Î(B¿ì_U=>tï>]F¿+o*¯ø2KtÔDD¯9bß1°Š8¡Ñ‰|š³-/Ay?ú°{?CM L†FÓ«-X ìdA!¤¤…joÞ^h 6{…8`ä&MNª·}£dO’‚m1ðçз›
+届Ø3ì†þ `q‰~b®N¤¬$9}Hù켊(ûRâ[qŽ‰ •jã¾ÆüŒ•;B맑ëák®Ë)°Ë®çmXáË#´²Ñ£;7Æ3˜Ì>¢ßÇY1¥‰'@ë¯ÀÝ¢2ôαÇüC cøãFW—"ÞíöáE(>kݵ2# ÜN†÷©!رj*¨ O‚ê®Xf¬)ÁôÙŠ"–È[v|FPîÜ… g+®Bû¹‡J–¹ <´•k AM.˜_ÔîÏ?Šºt O+iÔvѦŸ¸˜5.EŸE-ë?~¦™nfÜiõÊ##pÐ6dr…]ÒœŒ6g´¬/&eâÚ Cù@W˜y›Å?‹Kc« }8µ(Ê¿%=¢¢#èêU^##‘ojK·TgCƒR÷‘É{U¾cºÖB|€ølúȆ»ø9}+LC¡)Ë¿GãÃ’ERÈÅÛðfn~äQ@PÞ{–šNæ1 #ë¥$¬˜ô'ܲçuèë‘.W”tn»32ÉC¯Óò
+ªn¯´4ˆÁXa–13»‘Qéü**Â0ªOŸÎÌ}7IJ÷@nBTW½ Á#òF¦ù­˜ÕÛ¡c¬X¶ÒKŸ^Ä!-w×à àœ¦„ê&&æ©ø‘{SäBP‚E ³øWí†à®JŽK¦ˆÆ¨òó—àeD±Å§#€(
+葆{9\AäàÔÍB»è|äžœI,6W{I ƒ†¤±|§._àEg¶@¥@Úì¡‚hÐc^&ËL{öÃðE€n¾týíÑ'–ÀШÅû‚‡êP'N²žˆê÷d&ˆSZÁ„ü‹vð2²Q©ŒùR
+Šò ŒÐiºÉcå…ê/IšINí’+£I}P½J<u ¸ÀeL7f§áw ¾ƒÎ´äˆRx~4¤ Ì;‘ ÓHymu³üAwQ’ˆf/aÎ
+~ ,¾‘Y—¼eÙÁÅÌw ­BóNþPˆ
+DhFkð¾Y§¶|+ú|*êÌL.ùx(a´:$?æ
+H‰Œ—A’d9
+DOPwÈ Lš„€u÷²oÑf³Ê¾ÿv_(l*âÿ±ÉE•e¤‡Àqœ/é¡ßCL¦wW±¯‰Ïï9g!½ŸüýëäsEë}™Í¯{ˆŽ¾Ö‘iö‰üØZª¯P}.km­Ö7hiëªyZ×:gt þ´|mˆ5×àÒæí²Ì§…Ž¼ð õò¾øÏÅÁ=œã.Ë‚çk“HôXÚÇŠÞÆ×j’Šebvú)ÐRé"+¼òã“2øð¶æÉrÌîAnæë]³¹µ5†Ö}æ˜6[~t“”j§`¯,O*1ZHœû,±¡“wØy—Š® &ÜÕ¢C‡÷~²£ËÅóuhAb(¹qð EÙL†*÷*ÐhÂÁKäTk…¨õÞÚ¨sõäz}jnÖyom^U•1‡ú å“S9‰*ˆÊôIŽaÇ>'È&D‘õª6ÂÃvOÈúnÉ‘5xšÞ@®Pë»S¨È =Ÿ3V›YR2v®<Z“5‡ËØIÂVƒà£^5á1‡À¦f'”‚j«Cž¨B¨g}upÔ>g M˧Þë6&¤wr•¥…ÐéÆåHÅKA‘Æ—zÜ~6ˆÈKÚrëR,#oCóµ ™|Àu¥UÈgÏ·'5
+âÙ=æÉ0: <&_/÷1¡XsXU/§ý|Mçq²ÏqnL(Øu’Ó•‡ÿ B·Žà+¡G¼Œ †¦h[;”}·f¤ˆƒô°T4“[}mìçʾ¨z¦îø¢gA„Zñ½ýpJêäâ!~Bõ!ª|²ôÅR®éˆàä oƒTï]Æý¨§6Oï}²Ô`Š
+ „ÏýéœlßÈ$K±} 9<Kh­º)ß'è×ÍÃ+”Oú @ÖJ Ó&q^¿ ŠÏ›xm•j††Áç!-Ã`×¹ïç„ðïÖyPöð‘®ÈB-ºFÛ¥HérAèFƒqØØ|0Šê6ÍA<ì@óÊI¿éÔŒðtÿ>uÉ| ®¼!&u‡óB=²_mBÌÝy L'Äc`f_ýµS<¯"AŒÂÿ„rÒ¿J¦‡ÍêáPJJŽCË‹‚$¸Cž¾KÁÈà7º¾¥’„9 OM6‘sÀ‹RÍüÒ¡Å0e)íÒá94è¸ ­SR&™Fðà>Ë@àÌB?Önrý¦å%tÜ+Rƒ‘e¼æpÀ~J¨ýtpÇ
+Š± iMEÀËéaŠfN¨¾ó-ˆ]Ð*ÎÁZ KÃüU
+n&|tBy^m%Â6ÝMJHºã 2Òoñ÷CW¡°”iRýî¡s}&ƒÖ)6¬yBŠ'y ¶º!ðEPZÎî’¾<&®ð5F¥%„ÑæUÜ$àé¾êM4(äÓ« )
+nCšõbxwüß|…zr{ešx“y@Vã°¬ååGúÁm–y÷Äß’ÐTI?†Û
+
+uâàª8pÙ d› »ÚÉ^äpô,Tb󙼜Ú# µg°Ù äïM>l1jEÃÈã9hUúÍ 2ÓÞ¤rïì}ðœWå\K:®£Ë=ë›ÃfÊ %w”…ݧÏ)kG2%•>O v ‰‚p=t…V¨_8svN¡+’Õ'IªMý†Ô«ðùùvú%6„Á̦±8[熆„……³Hl)Ý1.ûpBQNæîF*Sa imVv>¤‡t9ˆx½ê‚3£7°Eb¯HÿSqRœÚ'8–Da[`§)Ã…Œ´¨)ÿ›IíÏ%ätA(õb¦D%Û9¥×ØT¸³™š.¿‘Ì"4·%Pø8T4F"Í옱±ÏID·á¥+³Ø QZô«~‰i!hàžÉ1Ó¿Szüݪ1ƒítx-dR‘2ª¬Ù>†Æf9…dÑ7-PBáO$$«´NA’¸¬$,„&'RnœlÄ«$32À9µÝ÷9+0µˆ6¾ÈŠ„Cr|ýŒ[<T~A[è8¡>@Ð#ÅD+”t›¬v£Ø¦ð3s2ÏzxKÏzò"Y[º€åëõ*T_8¯Ò\²umì\ue:)(½— f—Š°QÖm$½.¯d
+‘<݆!ËЧÕ^ï¾y-že¡îuNteÂœÑå Ð ìÆj'T`±™&½%6„MP‚èv ¸rä;zyÅßåLQ¢Û–ÏÓ4Æ+ù'¦–$ÏÛLb­[¯x·
+)qdeƒ,·!pªRYNî0¿ÓâಘgÚÌ auà ¤¨Qf?˜L}i C‰"¤ƒ.~1¿—™©$ëåéHÈY2 ˆ+)îÇIÿîÈç•cE¡›½Êù;-
+œ‡ujÙ·Ыìûoõ>iæ©p§¥¬®2ê£Ù`Àê¤Í©Es
+Ù~<A|n~DYùO hÔk§¿Ó¯hQÛ•a//uCØFÜ!¬x²ú¶LìQ´Õ£‡<vå0Ø2§¾V‚=úQ>2•Qè$E±ºŸF‡=¤ “Y24?2ê/‰)­w¢©%¿6tüæx'ÄÎÌ1ÀS*)œ@î{±ƒŠ¯¨Šõ:Gi:qžæ¦ð$lÄX5?OSՙ̋Ö$;a’IÄqŽJ^,ãʼn‚QœC0å=ÍŠ*ZЯÃÒ´Bã÷Í‚ºÌs…™æb šméÃ,ÊÍ* }ÕÞ0iε-$\X ÊÛ$ «”³-&<öÌÃÕ™
+>˜‡ÍvU?BAu _ÿú›­Æ€ü(U 5ò5¼´}V¢ÃëýsÕ•n•éв^üªµ0DœZvtC ‰ i¹A”ÛFZbcÒ7B‘A–ª<äöN4)Ò@BÙ][Ç !0pø^‡¬¦ÞK˜ãS@QdIÒ:ïa¥e’Š:åeZ˜æg ²Ù9Gamu#Hšq˜ípXÛ(I
+^!•x¢.”vZgÈh"ìA-÷}[ûàe \¬M:o&AB9ÎG>AW?€î%<@nïpB<žfÃÜÆs[t%$(ŽÕ¾db# X¢½A¤Xø1ÓÏVL$LôÛVô::I >è¹á$¤ ˜Ê&«CéËä™Ò½Â¤Œëèå
+›°çìâJ?§|•ˆöÚZ„Q‡·µÿ1ÝJÑP@j¨ ¶6z’AJ9¯3ëßÙ o`ëdå*TZR½!©Šï#ö°Z}m5ùlJ½iq;r)j/bÀŒ¶NE
+Ze¡ÔÅ@d$
+ c¯ƒÉ„2«A /
+ÉãçhÆÖ ¦=Ï-¥<dA+p˜3%eƒ†(0Ë3îY/ëâ Ó†¨ªU&¨lŽíÑ ¼!¥yøN0 ©AêòOñ ù"ÛITY€ Rr\· oTƒT˜ Áœ~):¥Aäè#Åú2ªT?OšÏNÃØÅ £ª†¥™&©ßŠ U§eêË…¯ã¼q#áeƒ h‘æ&"¹]‰'ÝF(ºD÷˜²—X΄ KtæÁõò+CéÍlLÎ4æÔŽ.å
+"q½®4Yæ¶k¤
+NœîJW•I£ƒ'¤ÁìÐe¾˜¦–ÈÌéÎ'ùaŒŠAh¦‹6÷Ÿ
+rZG^S„«ÝŠ©+xJî`|£o“¡`¿”8­æ|ÝwÂÑñ™¤ÁŽgÒpY66†-•l{0Ÿƒ:§òuËE°^
+„Žàb:Ý,ÉÒ äø[#}t[D³™?A}ÇËÓu'ÃVNVv—8ð+Ó°–¶Õ2ȘĒÍ|Ïdåp%Û:‘ ¢'xÝ YçG&’U÷
+úYî c¿KžÛU¿Éî¼%#67ˆ­»æEÖÛL«Ò4»¥<8•A¶lÚ¡@ÅøQƒñ>5 . „hNQÃHĈ¢c¥Ìáð[Úo•‘nqžÌå.ªk„«3JÑö¨ø\ òÒ\³â‡$'cP1O’y¾Ñ²×Ч^ûœ
+
+ÞÒÃô7ÿ^vr¹Ì‚”„3v¬x„uìÀ^JWcö¦€Æ›âe­ñ Âi¹ºLÐÕ7#ao1å2!D““Ž0`î9ÆêÚiUkÑ>2mE2~š"eá
+þâ³»žÁaM¢J+µ@ßâMˆ  Ÿ'kB‚ÿR^&ÉrA=î ´å<¬ÅR·ÀL+éþ[žgFTW6™aˆOüÊ)Âý9:óu—0 X—(ã|;*A4Ùtœ6xÄ8’+ž3þEL¼æaêô¾ƒ aèבŽ%ˆ±|«36ðñuô{öŒ{$ í|ý½ˆ'Âv”§ÙbrƒŒóWãgÅMüu"Ñ}—`p—B«!m&ðéøÂc?:T€ ’{ü@uú 17¥o9Áq„54Á2  z†l)µNAÕs¶Ý§†)½k¾T”™A`³uϤ°
+7÷N17ˆ‘÷ƒ’l Ÿüá̓kÅ5aî*Q5'“€]ó)0‰j¯l5L*½]ˆ%Ñ¥ôhŠtq—€¡È®Ð£½/hki+Á[É·/?¾Ä¯h½„.!nÝ$¥,Ê_í"Í>zžÚö,ŠhŽ™ùM°h?}|q4zRÊýP´Jܪ«N2¢X ÿÞh!P-´ì'£ŸËüÖ1L‰¯µ—iß´ü~ÁÄî²øÒ³"mt‡/Õsª°¥xhƒŽv*až,
+ÍÓ·Ï à±åZíÈ< M7<5!ï,…%Øå5úŒ¹7-@üÂb­l !\*¸šg[‰-5„€|†×úôî“Â<Z±zg[÷í=ÚœSÍŒœáv˜Ò^*½$\è!É‹ØïËSÜ߉zÉÍ¥v#£˜ º¸Kè4
+.µ¹¶§—@ÔcÙKaM]”wꤤWd¼0-õøm-ÔÞÈ€#wYá'Y_uAôš°pŠoT™eõ&&VeÜŠ]ÎC(jD˜‚°Êõ(GsÁ«ôk¥Õ›ïHSØÒD“µ‚»ðLÁ^Œ-_@9ä¾¥)¾r
+5â ýP´J¢¢R,Û‹AWAÚKxœ»$Ȥ˜¦-Xrí\)‡iÞ[üÊïuïÄŽä1ÊF]ï .äOŠn“E=x$)ñªAê"´ôŽ°`à«6ÏFANÆ¥unßVBQð¥ _ÞŸÁ׸}µ˜gXš‡¥R»VZ×ÇUŒa%@q[é¤÷è+1ôÒ} `¢c‡ÇB¶éî`×vÍ@3%´! >Ëس/õ(¢Kó:E¬¶©V=Q¸–ŽÀbÀÅJ¦Ú›ýs™•-™6ö:SDÆK’‰?þt<”÷;pátÿì
+(8†`ùfà1D’•ƒÍÖšQ¤9º%Ÿ"µÒ¿e““ðžÁ%J´d½EŽ¤¤æçˆn˜C€=Ó¦%k{^!Xø>]í÷åªü0ÂóÓº¸FZP…ø£‹©Õ´.5
+ÇÞÇGz—$TX³{3!¢Ù¶À…)c Áo0jMó(êü-jvy ošþU«ç8»£¿¹Ù~¸ ,º]äKþ
+ÞZJWäCÂAHöd˜ÝõV<~ ¦¹ñ Ü«=¯%7óòUZ‡®Éýï·¬ê>øÀ÷Óh_ÎÎ…xOÐÁëŸéh¬ÂÇôK&äàÇ2*|ÞÛü= Qí(3µð2î3Ù +¾*1fϯ·[JWƒ.@ÃñüôÒ[Š”¹V¿a¶Ee®}ø‰ ŒC¤2 åöÑ4$ò
+UAI°LŒÍȇ¦wú{`Jû¡Z^0¸‡Ž?4
+ŒÊ;Ž)wQ‹
+ i®t-u7
+z³–Ü7ð ‚Õ£Uœöß™vÐdãràq·†z¢Ižzí[ôP·‡=yÔ1ío€Î–<¹êÐòQ·è!»ãúlnÊÍz()"/A/¸O®ræÆ䯔-zÜM|ƒ
+‚u¹,UŠ[ô˜!±A{Ã2wôØ…ÜÐãÿ ÂÑ£*§43Þóp‚š~Ã[Œ=6A  ˆv6³ÕÆX*Ž¤¿ z;îóOîäW¿}ëâDóêô ím³„ð-蟇 ·›Aaâ­ñù£ ûq·÷ýQІdÊ‹Vç'HUü‹˜;îÝRy úü“|ϲ|üPhz±gå”âœÂÙ ì)9É&dP¿¶AsÉ"<Œ¹ÌFO¬uDIÓ‘ŠÔ 4©Ã‹=¢°iBJѬ°
+Zde,*æO;‰Œ0ÌÈg*”ÉâÂa&IŽÏIj0?*X1Rº…ðT °ŠT, tþ"2ÏqÅȳ&bÅ½ˆöt ëZÞ<$NéCÊÔµˆhaú ¯›3…Å °Pê±D‹  Ue; k#€a¹1’nù‹x:Á£hRáÁª˜ÓBŽRñct¹ÔMˆÅFKˆ±¶Þ}gZ$k[Gá×u UÙ/S&™u˜ÏóBf‚(? »NÊ“7ÇèMÕe*Á‘öÈeô²ç#HWÉ’c AÊ1^ý(r‚]«–Nt
+
+ò‰&¿ó Þ„«âÕ>C‹Ø¥~¾I“ v€á—™õž$•¼ÿš²“'ùIÀw ÀK¶ x#è¬S´ï@9ì EàÃpР$ÔZ·!m¶Vf Ð×xu â=ä*L
+ñ+àBÉ݃9`·`Ã8Ò6Ø^sÐJ<¢¥šôJcæ¸Ù9¬)ò vœ|,Ûüxwýw ñ£IZ k½¹Ø€‘Y¿QÄÆd0Ÿ´†‡P^|T–ä(€¾Qÿ¦ê&œ"¤KÁ)^@oP%+ÄmÆåô}ªÐ0º¹Iù£ñÈ€ÔuÒErš‘‚b»*]„ëbô;d› 5ªI³/|ÓQ5m’;/Íãc¯ØP …e^HÈCÈ›¨GÿƒéTGQ2Íhd8•|X®šþÓ‘]‰Î¤–› k±„ Î=¿Ã3ùr*_3ªÌ‚ýˆ[‚áIžkïgס;š&CI‚ø°:qþÇ  ¢L%«… ^$’âgKu°WÉLçe ¬õã~tÚy’*ÌZ3šµsu) ’¥gØg*ö„­’àh!Ü–õôZ^,¤Ð<ƒe-Äd!]£5ݸ¯“HÐXzX ¸'ÿG„³Ÿ”ã`ȱºá!`;ùcÝ9bµD{e-u¹[ÈÄc,
+æ/s'UŽ»hS<;,ˆ±‹´?
+@Ö?]Ny6õÄz³៵È
+‹A¾àëcÔ#ü%qkfŸimx˜½¸ ¹5—).XÕºð[ÈÏu<+ÉšâÓw˜’¡)KkoÍÚ>¸÷ñC¶O+£[2é:é±ýa) HÖ‹1×s)¥”¨|FÌÖQ@ÅNÛVv Ä éÈÙÚÕ³ÃÔ!˜(nîÚGKBý´¥?\ÈdUJž·MÅQƒ(Fùk+Â1~˜½ ‚OS´åÏ,Žü+¦í=t))Ëqkeô¨Î…(ú©úïÖp ú|‚ †üŽ#úÖbIdÅî£î]ˆG¡E”Ž^gÍêüYS8'‰·ÂJ «6©™eùF½ ùz<…ðïEÛN._À s ¡UÐC!Z7ü3 Zàð¸Ê
+†IàÐð‹1út=Ý“4ì„h/‘ç“WG-Hº‡`M rœÏæ—X±Õ2‡ô´Ù1tiè–gUy‹UQ•WÍíå…CÆQVˆZJ«êU«C`²ã\,DWF¼Á‡~&™-E 9ºç‡% ùØ÷Ö)"°ÌA‹!o=:ÛYo/Yô%tg \f ºmu²t·
+  S—ïph`ÓB¤ÝôbìêäØÓŒ‹`óÒA“ºÝð$&õ*´UsZÀ¡-Æ‘9ò§Øgf$` iñD˜fŽÓkð€íÊŸ$¦J(Qf3²£RÛêo»û,ÿÕ„]iö-_ð¶Õ
+žÁeÁC¸ÎVȇðâ¬ý Y]GuH
+GÒ¡ýŽq‚ìq9FÅ3\ÿc¼\rë*‚0¼ïÁãHDý~ ‘zÊ
+"%CöÏ÷wUŸäúöISîÓ]Uÿ+ò]úÍŠÏ»TX×ãŽíúÔÇ"¦ŸyÚ±Àº )kÂxÜÊdR–¶'­Ì¨’[sq•7A „"q°ì©O|€ÞˆóH~ŽâJ‡Ôm…µùmlÓÇÉ*!:`v˵˜¦¤A_b\4pô°ú9â Î`:.Æ ’.0ËQv ÄŠ"vŽöÅX´(`u÷§ˆ àz ?Ù‹ &D|P´s²¼†¿oJÁ¡´<6§×¨(!™!GpÏûS?EÞ;ø &7¤Af K ʼ•'n™ÒC‘,wÕâFÅ¿O‘¼XeÀ¢Ðæ<~D-LrvÂÈøBº>xÞ®
+_¨%´kçpl"~¤x ò£L•D{‹È:qYœT¬¢HfÙ†AåæAlXB쇓%“Œ‘Ëo)Á"² J?¦I-Kg«º3.f•»ò_æïq`€>ó°,¡ß²Æ‹1±±™‚R¤èÅ‚C¨®ÄZ¦¸òŠU"M‚âÎbC 椬Á‚g±Á¦¡¦€
+ò⇒Ýc1(‚'ñ",+ÖvZŽÕÀÀ *‰.žC;ll3ŽF§*H¢›YÁñZœh¨Ÿ”V”¯Ø~;‡?¢Ë=ÇâŸâr¸ ¢ÍqÀGˆ#$/!"®;6êâ!’F>à`;+VœLD³àŠeV’`
+ÉKCcPðñ{‡c™#][ïà$®Wñ²yø0Ù8] íîÞ„Ö”±]´…UÌp]»)aiûDßêÕ¿§"Q!î©ûœÇÛÈFÓ@ŠÚÇã«]¿)ê»`ÈÌ8¹ŽöbîèVöS[Ø:o%h1‹^Y„áÏ.¼‡ÖaãÎ2­x¬,Ý_ªlbÎ
+½ÕŠV¨T¶*[‚šÊA•è‘1gL‰lÿ€ÁÄdÛ‘jr`fØÀ*7+0iÜns¡RhñSXꊳkðl>R ?
+‘éQÿã2‘üÅ*SÕ}»À¯¸Gãç@Iy5/Yš´JÃ1Æ5ëŠ<7›¤iÛ.Cÿ0òÎ:RÁsGJzäZ"‚:䵺Ìè†ï9ïÍ¥.^jsh6P…qÁ”…ºg™a%È0";¾bð÷1—fWóºd¨y°"a‘d`cñóimx8´Û”f@ã§åAjK‹@é<ˆ‚ÈÍMÙ‡¢U è*ÿã¶"±0r(qx.w"¯çí9¹+ »„‡7™øèbƒqyɲ×$Âý%ºÃ7È5ÍýòFÂøâR<8$Ù@ZÃŽY ;‚$Ô&_õÇiRЃðƒ5šWBÁ½âDAYn[…f”25¹ƒƒ|Ùfn- ˆ3›ƒC1ì°#ŽËK 'FõÕY÷ë—D®5nùå#I «Ås¤Ö†_ÃT¸"²ãD*FJ÷’É–àîrÝî¡ÄË©SüSòƒ°$Õͨ†_åÑZösžRmVÿ#¦‡({SPfQdItæíåß—O¿„×?ÿyùôßK|ýë÷Ä;s‘Ñ€!ÿ
+peJ´
+ž]
+.KY¬Hü— -µÝÁ‹Q1v™tÑ”­îe\(d·dÀcîVÄo`ó$š³Y\r`ðÙJrˆ9rØf|ÇSÉf:0%ò«›Æ>žÃRÇ$ÂWúeŠü q Û4X0‘pÅþí7é/¦5SÝ VJªA¦Ä?ƒ°¶4OßÒ€ˆ  ýìåX¼Úìý¡ŸãJtÕ‰(žì5Šº¤^„£À¯Â8D”oVD6Â]3×2E”% Žë˜^Â/.hZI£ÃÈUòme.¢`ºwH÷”·ªÕ‹ðf<¦ø7Ú)E'+Ý¡Ìn+’ò{!{ íð[ó×gZAßY•Q ôg¨Iî¹èý¦aÇP0¾z=(šOpf´1ùðëåÈ+” ¢ÌŸÅ4¢l6ÌJ04iIÕ<0ØÃråÐîÇÈÍã™~Jv Ž‰<n&õ«‰¿}çgÌHÅz!\|³‘[°^²7 ÔȈgXª±ñ.·Þð“øÅj%Øa¹ü"½ÐÃñSüÖó“xŸ¦”…²“ b
+à8_dHcjóPâ[QÉ\FáéîÐ3År39€S„¿x"«¿ùÄÒà\ŠÉ˜€ª‰Ï½ZÏ,1þŽq(M¬†nÚÈ#ËÈB¡¬nô'œ@²âé}puxà[@"¶:][! Ò–}å sÅ"ÉMîðPUO{ÝÂp¶e™Ã5ÙQpÏðÆ™
+®%‘õ;µbˆh`â/ï„v²‰±ú¡ç Ò´}xnfþ~, ÞÈÌzwNÒ²ýµú9¹ ÜØ/"F2C‰h¼4e*j‘Phé×ò÷ðq€­bmy˜M2·e9†¹K ›Õ@/ |‡¨ºó6•
+ Y1“ìfƒqzÎî¡,´G¡ãÇ >7Š4–yÍ6o˜åSPYx3ç±·˜üg;W™è—ˆ;¹¡7·K\ùÒ3Á±7]¯š”.±€ýh D‚‘š7¢LSsM­‚¯˜sm~ŒõD†\J~
+Ë~9Û"3G¶Kñ+³^ o¹7ã
+ùÉšMTSòÄ÷ÌYÏã÷ÝÅHzŠ;(ü)íi‡5ó‰GÉ'é$ÇàÂòØD>bx)3Ñ ô_%Lĥ挖Խ6cJíqèý9ï/Î!î4JË»(HŒh«E¸‰•°ÒÑÒ%^†ŸùŽõäúöø¯ËŸÞc ìDÆ Q¨¥Å‚!„$JN`Ã6ª²ìU¤®OþnMŽ-ø}ˆq„ Ö ±sš¬ê‹S¢ö£0-ò¦GHT&{Qäo÷§. ºK8Ix¯QírŸÚ_?v3äpÛÍ4$l|m  €
+ƺ€9Hd”Y|yˆ»Ç”só؈Kr‚ ÝÀµ¥›¨žC_‹g¼;'.sAÞÌ®%²¶Ú«®ŽB46yŸò?mup1iÇÚŻ쿃7ÒrHݜݡ{d
+ ‘Öaòyö¡{Ä;B¨¸éÖw]ÿ
+g@Dñ"²r
+2> Ì+ÿžã1Ǭ¦¹9ñ½ >— É‚Îje–¯Þ…ÆE±òÙrLTêe¨ù±ïtöo€]ˆV¡?™Q2~Èì5[D{æ D.*c×;Slãq¸ψåBªuþ tšEn¬*0XÙíXQZÍùžÖPB# áÌæA.÷bü;Ò'¹¤¤ˆõ+³õ³Ì]’Kf šWé7¦ ÈL4/@œÝíßÀtÙ% åã$£ÌŠ;#Ȧ"`A~Ó>UXwê‰öäd^xzA`±¡òœ`¹›þ·)K$UmAöù£U‘ 8„C_>~A¸hù·­û8~
+âL““WÒd6íèÀË?÷ö"ù^®.Ø8Xth§í&[ì¼\¶
+1£C ˜“!ù5{‚i{a±DCópØo¿ÖJÂ]„éHDÁœŠîmÆF´^E®3°gؗér'&ÓzŒzFž“.ÛÇ9šŽÉé§|&R€ºkÀÄgFVÑ0Ûâ"²+žcs
+!Û»±$²¦K7:è’ O‹ZWÍ6áCîžžõÀùÀ~/‘[/Ž¡f2,LóÜ"ámë¶Ág§·aàqIÈFQN^‹Ÿdç´ò´IÈŽ‚  ¨„l0Dß7"Ö-Gmù ,á÷ñ XoìÉ W¡
+ì0óèH"Er ´æ±(!i×¹Æ%áGC…¼Ò‘¢ôào¿ö±,x‚µ¸nºžŽaShZ!B·+õSÅåñn7­b)sÍZ½!rg팡E›¸U2ÎÏhs]Ä]”ȱûž§$vJJ«û¥`_M}ðÖ&OÏ-&jÏbáÂ^e)9I­çS¨¦¥m­Í ,QCk„Ý…‡d,jpݲˆûAÜ8 ˜Éƒ¹>]Ev#zºsáëÆå …Ý
+®ISʳ¬\ÿ·Ÿ­Œä&ÀRÛ3av¼e·R2<hÈ4X8XQ:g‚ß4â3±ù CÁ1&>˜#èø\=ÈbB¡¹)=öלšŠ1°¯ð9Ý°ç!!‚(?X×í cÉ8:ž>%¥ y §h¬FkÔQo-¶dô÷yÇŸiÄû|„Š;ùÝWMÂèdt•œK¶è»{X%íÆûzË{š;|†2ƒ÷`7v!ú‚O‰6ï1à€4¿«1vC²ÞTÇ°6OìÝ=‡o…Žƒ{Âg }ˆg"ûip­gÒ ¨ë{™„¦ÅQ±ºö¢Wˆ;ç÷ˆCdZ[1üùÖú$Õ>ŸÃ太͇£×vö߸à†Ck碵ŸMaÓ~u€çøb§ –6N;Gy–‹ñËqŠËU’<jk€(¦Qç²<òg]LMçbnâ V·ƒc"~€<¤i¶ÚB(ÄJÃ)JìKä¿9–™›&?î{‰"¹Sñ(ë”àcl±jÈS@!‚¼ŒE—•ˆë¨ÖS$C pöš¸Í̓*Ä'õ[IQüú\Jä¼l€K•8Dõà7å®'RóˆEöÌך\4öÞˆ®NìDeI#IÔ1{ZM"ñ·œ0êh©×¨A×Èî€?0¾ÍöSŠAZ+|r¶¬û׬{ÄA>^WïžÃ0ùðƒÛ{+W:æ’¿¬ãèpƒ“ÏõÞ÷«Þ÷á“é.
+†è„ÇCd7"¬h¡H<Žs•2„]÷+;í|Âã( ¨Š¦jš”Q÷Ó¤F‹qN,°ös*GÈ|qåAÛ2_àQ’*Ä3‡z”¨Ãx)é«›E
+ öªÕ¬Xô8$d9Ìœ°@›MÑ• MÓø@tò£)zÈ!©Mìçkm@»ä X!;g· R"RäK2R
+² or°ÈO
+¨ p³Í•U,'Ú¨
+E“CGëte9ÃøDÕ~.˜]K±ùq¸i“UñÉÂG)°®¯b„&>hñ£ÚÂá/ä#m~Ô“Úð‡LÂ{‹DZª0ƒäk&?þì¤ 0žY˜j÷Û(_é!^3ñȱ™Ñ¨<Ÿ|³¸>Yu¼îC9I¿{tɼÊCÄÝvõ|N†} I*ü&З"׊ŠJŠ"´a¼¸”)‰â’ŠEг‡Ø j$K„ÇuŠ|Ћ³Çc’æ„cãb=O…E"WžÀ¤)‘·7bº%C1^”ã•XãMõî1U›™b|+˜Ì@çS¡[¨Š)–¤òRl ©³YG¢½a÷P×çˆãùK­ZËyÎweŒZl‹•­sós!ÃKBðdå×ç°zr"ÊÎOÉ:8ƒŒpû¼»Õq=~•¬Ö&KÚvSÒ˜l#ÔµmŸü ȹÑLó”ô¹ÈŠãuˆŽAµúÝXxžNÖ°õ`
+êyµ$î6…û[Tª,xL…MáD5ÑÒ¼£›ÃˆûG¤³8~\sv‚¸h8áÚÙßë}Íÿq¤šÌ€/½qm€HÊÚ1íRÕÀ#®ï0{ñb±=dÙìâ ‰#Ê]E°>Cú_G‘"[øW4AÜP~h ¿hŒFÔ×f€Pß8úJá:4×îei»›O ¿ô³…á” ¹XäœÙÛø|TÝÂp¬ÐÉßxƒó
+Æœˆi¡aÏ*<QÀV·ÏéŽÅEè·Í÷œUd¬%Y=Æ»¡'³ã#÷«Þ7â_†l ¬I±—b4œó)’[TmE´áðrIzÏ]ïKÂ6Rµ’àÛK"MÌgˆyÍ= Dc§)"ª™³²x¤¸¦Ñ³å`4­³)ÌçtŽÊñJÊ«ÙB`±C¤É’$b ©ÒÙtI5$ãU‘=0¨HýísXéQl¥eÓÍpnÝ[ÖÁù Z‘_ø ¬W‘
+BÃrtî“F›½ÈË4Ràäa†ª>Yw
+@„ýþD1ƒK·jý¬[Æ:x6›F³C
+1uµ¶¥ª °)²›Ö2íYl½m}-¿É‘ûeŸ è°?õ0E–e'ôºÄÕȦ S\÷Û9-ÿß±÷. ÿɸ0€!LIe@šÖ„ä–ŠÚ„Ét\ŸZ= Áyù„6}³ÃǺDÅ{!W%×±†3  Ë’pÿÄññ?ãå’gáä9AŠ·Ð:Ûžÿ6_ƒ˜”mþ”g55–A«Š?{ 0Ó°&-_JâŽQ¨Fg™¿<­ƒ…D`¹ [)¸Ú7ËWàx
+’†­¢«¨¨êÃãM
+èŒÍ¤F­†dšm¥a`“™³ŸwÒv¥RŽCÚ(g§ÿ3û?çcp5z‹ G‚OrbëG ÿï»h´©T ìR´JÄI0;á€øÖYš{,e— Þ””æé{KLrê‚U”<;ÿDï/{ä –5“<=k)î.L¢Q—*ì¹H°ó°1ÃŽ`“ àš‚š¿%E²!h5ן$ZeiñRô¶‹ 0ºbVj¹­XqìqÊg3t]{sð¸B¨’¶i3…‡‚I–Äù¬ë1<-»ÀÊyXô.a•_&êÝ­È„WÌ"Þœ=x˜\òC‰Ì
+þ“îèVd.‡Aè^‡D‰ºaÐÚy¬‘¹P9<šBUÀ<$ŸòñŽÌÌ|Û¼«ÆÉ_°|RðÝØÁ+0dÀzP“5cæøÆå ÿGòïÎ'1Šø¤Ò.ì=Š€s}ÕyH(ã¿Ñš¶ú‚øŠ©Ê€«×–
+£„oR?Ì€‡‡Æ_ì…ù–yQf8‚©²qTÑ@Œ—Œ`‡éç•¥0ˆÃ"œ?°^V0Ym^בõ€}åVÛ ±i™U´aQJP¼ÉèŽfqÓ=AÞ.»d~àC  £ öA‰´é8 Ü^§È(ê‚Z”
+÷b[9  ‹EË‘à2ªz;Lð©¡…*¤UXÌÐ+#¢!åøjm*ÂnXЄ]ãiü‘a'!`¿Ü‹>HìÛwtxùT
+é:„QÆ2ÎÜÎ/%•±¾_‹$daîH&T¸ÜœmåBž¡µo}Œ?×í¾Qôņ¾=9wÀ´hÁå~†l•fJM‡P–,³ ܶ–E¢Â_ºÅÓ&Ý,ÓÀtnà«‚ÌS<aǤ;|gŒao{Æ¡¾VOúl´ŒN‚†©óR[A@¤ö.†Í~´*‘ÎçȨÜpÁ)ûùW€
+H‰Œ—AŽ\9DOà;Ô&!R)­=KߢY¹ï¿Ç/þêq}åØ §³¢D‰ ƒ¢¯a2çÔ¾†üK›¾$d¨ëìSõãû79‚¢·nÓÝ›ÛÇÏ â§üÛ瘽@=LF›s}\µ6ÍœmÈl=Zë3ß6}¸ŠpöòW†ò×ÔÅ \Õå|Ž¿¼ÛZ>'7¯Ûˆ £qí±!¶¦·Ö¤­:e¨ñ®a¡¾îP}¬îmõÙtƒ:o˜"sÉÔ}Ž.åÍÞD뜫›¡ìˆWØè>9z4ßIþ’µ^c‘[=âãG‚ÆËW³å®–•øCÐ$œ÷¦YÑ z„û#P¼b‰¹¯ˆÙbÞ_«¯ Â@?¾ýç›|ü;¡òZFa­É$ÁWªT”јÒäãïÁÀª«s};€.ˆŽ€EÍgH{!úŠÅS:¤­¢Ì.sÁõ]\
+nyOŸ.}×e-ó˜P+Zßçøhœd™ƒ RÞzöµ6$´5W¨ù»ê>@ ÊZf ÊüÇu~ëtøu
+¡^Hý»!”ƒáÆ]+—°¹ çªP´vï1׈»æt‘z·qGa\"öQÔñܸêIl0 6 9óïw<þåÛ¦|JÓJÚ¹Üò‹v僜ðRþ–ê9ºµ\°ý~ –킬mÕÍ*tð‚det =õ6E¥djŸ”oÍF²Y„2Uz¨æ"mÈ)Š³ºÒL„1áôÒw*M9HþJŠé•–+” qC‚’s1È·!(ô¢iŠËù
+K©Í&ÎÕÂ\tL9@n±…{¨ŸõåïÎ!Í}(aë6‘Ú4®yÔøW¥!*Èšù;ñkV÷T¡HT™;)è—S˜{€tf,cŽ›h5ƒq žîS¶ðƒÀû0׈]Ťxw¤Vºß¿=A—!¢r˜~P£vˆt¡9¨øÂw0ì1åœLãgPG¿…a#˜2 Ï1zI¶&Å`@ÃWÖu çjò»üCuV‹Œ1rH4ãï k N)OªqAÒQ¢¸Í¥aXenÑû¸¶! Šàc£Ý,h:.IBÿµCÑLÂ@‰9JXÇ“Öx%c.÷er@Ç[žà–áPŸ^:E2 ÝJà9°]S“©•©ôxÇ##¾Ã o@)t¬t܈ŒýÜ F
+€,Nœç5VŸ|ýkBì¡ ßÖËs~rªk¿F$A –ŸþJþ™Šä¤TxΡ§¥)v
+˜»
+¬Y-147 "F'‚ÿ_LSwõËÝ V¾ÊóÒ…€à/#öï#•¥È®nªÚ‹ûaðÊŸ^ë÷ /võÇ1Ü€®t­JïvÚu§ò
+‡fJaðK…ûØ4„é2xhµÆÕê}ä´(Qèdç˜ýsëX·d²P¡©û$µÑݲze¹ÅJM"”_HúýÛt¦»B\T#_²Þ6ÎtjÁG¸?}‘…Sw=@ÿcüÙd#-4Æ6Nœ½d†ÙÖ—!û¨³‰-¸T–™}+þ/¤½ÑO»N½=Û5ÖXxÖ®-ó‘Üq¼]y²µðCÛÛ'I0¹ž²û™E,=^ ½6i .Ë`x[܈L0eÒáX—šò’âÁ°ÅŒ˜@?w
+)³†Ñ˜ }k$%ù™uK!Ë{ˆÐ¹ÌâNo7Ê< ¦M¯m×s1a‘%E2ëé’G’¨ËÚÇh®¤i¦Z=×n‚ Cå „÷1Æ{Ã>Q¡ |9:‚5 ™ûœ4ˆHÍ~O^‰NÎŽºmH®½ù
+hñô鉒´ž{ƒW¯²Ï1Á¹qÕ³å–0P‚˜SÎôûÿg™I2
+ºHå$Åx´+©ÈBW·BŸç”Úöû!P˜u¸xËV%Sˆ‘p/<e€aùn±G£ð%xÄød<³ ûo*%yxžÔhÌã9[éòyÖV69!×J=ÙŽÊ41º§qÿe¼Ì‘ä°‘(z‚¾CÛ2Ø4'4&Ý9C^ÓÔýç}d¢%V¡H2B U1 [þüKyµÎÞÊ ‚™Q¸–67q'Ûpç,y('…fãǧS45êf&æã‚ppüÂ8áÖnJx),ã´R£$ñÁç›{IV,É‚•>ÎVOEL$$­^Ý×áÊ `ÈÂÏþ ¸\,8ÐÜÆ 2À&(ç0Úçø•Âǘ'<4€‘’s?3=hG¿¦—`fA"†:•€ ðfŽ)(·’WäôCÑ×KÑEø\ö& Ïz)¸Xå@óaTR(Ú‰o SÈyHȈ ¾ ¢]ÂxÂS}rT³¤qx’Eœ„ˆ‹oÂ|ù:r#zéâ‘‘å4(š^’RáBríR­@Æê㶭 ð6ttÖz$Iˆb0f]«Ø8%;†aHÈ"ŸüÔDÇGî¥3ãP<`&lJ¬jQ\œæ˜º|˜ð©„ç^’ ¦ÝM° {zC˜x®ù¸×@Œ#~ÈöV‚{x
+‚', D0î`¯ @é‚?ðãÜwçÔO\ µóÑKÙŒ%²Óÿùö\´ѤoèwÎOE¢TI£}3.M7°Ʀ\¢!Ãmy .u¶$bpµÂ²ïh€^žó<=g“_©ð?µO\Ž~öï^TÀò,RÒ;³P’£"7R‚çè ~åè;õñÅøºð´±‘Ú©YÈ‚ó‹!+-¥€!b#ÐM*eß2³¯‚¡6Ú=&‡IJÚl”íb)ÑH ú‘ƒà‘pt„¸ƒlm>XL%†¹ U.E__‰ç0‰œ’ýˆ¢žra6r
+™ø¦…böÕxà Ã&ºïÕRÂÐàg”$.…ý.²¤Ï€ýæ0¾$èäö’Ð$Rª[%)aœf–¡™/{õóžÿãçTjFÍÅÕÖ<p4<„_o)ºV ›UŽŸÃub÷$`Ö½¢añ<±fë(âÝÊÛÙÁºMð°ÇûüÛóî
+ŽÁkÀ:#æuÀ6ä—rpøT"Þ­è;2rñT”83tl¹{çŠîXT*£%ÖN /…žŸÄ ab« •ÅÅ€ôfz|?ˆ%2”¨Q’Û·+÷>÷ýÂá‚V„f‡;‡U@ÚvŠuDÕI|&òKh¥ 5,ØW²†ãI’'ß æW„+
+ð0 ·ÄæXÅP@ Y>Y^´‹Å–ã\QBrÀQ)MEŸÈ$ka!’3ëOEV¥ÏŠÃãè5~£ì0jÈ#Ä÷ñ¢¨X°¬–FÓA\Ó—g7[Y@º7ÜFÀ–µ‰
+S·
+Ò‰<3% Cˆþ@ÈgŠòHX ’^¢×4f<÷|)Ù½h`§Á×°ã}öE,Ëð¥ãà3>C ûê%Š@¼hkÇëÒ¥”ÅoÆCÑŲPT¿E Ð~¯fûK¼ “¶ÍkØ ¨0Ô”Ÿ=;w©¸¼
+¨:³¨¨cL<æ
+VT¨ÏJбãÿc-ÞfJ”ààyPæX&^€Õ‹}óødF¯AiX:5Ç¡™U +í,6Ãá¨Êʼ[‘»Q úƒf@Ms\J¾ù~ sÙ¦ë:È9æua›F·sÈ3§™‹—ìÃÊ[¤XeêÒ’Ãú­8ÌìBCýt¾}ð †^‚ƒÌßX5žxáÌ9 ž2J~Þt…‡äø¨Ä2× 3»ù›ï~ª©R¡íî^TBû 6TqÍp/Ðo+bßíÈÊ{ê14c)Wãó}<5^Ï +CžK±LJ¦|Êeá„]Âó€è©Ç°t‘dÝȲ×d¿g‘)¢ï0w-BÐÉC A³>^IˆY˜iøfÐHF`†ÈE< „zµ8Ž°?ä,@Fêìr72¬§óeß
+8\¢ò<”&ohÜ¢ ›º˜·%]ô€ó]A_Œ ž+ (Q¢Ô‰bó'©`[ã© ’9e¡sŸ·g°ÍA¬bšb 0oAp¹K0{õüòPrúÍÅAd…X^­#“2×XXNÄ£0»7£c“çÒkÕ‚–¢ ø<m ÙÍðæ ,eá&Î4årâT¥Ûœl´ƒ=Ê™ŒÊE?nسx’Û2})u®Áh3] ik³ COG´Ã<*Mé"!Dé]£Ý¯ÙQDŠdO)1†ùóà±DíUy
+SØÖ1ù§š,Y5l\‹ë¯òàw¡¨Ê·Ætœ~SÚ.`“Q4ÔŽÿ`Á‰ ÒŒtz%û¤_õsÌÅUeEåþ[?– â ‚œ~zÅ Q´ã@ö§9L—`Oç¦1‹V¶:¨¡™%†õlMàœu9Ç£­C>,ˆŽûL¶7߉&ZÌP·oƒ0Áéf?Û׿V»*¾§Ô¾·˜äC»¬‘Üv5¹bCZžÚÕ 9üém´î·«Q2 ¹!{½X”³xnVì¡]íE)XQ\Nyh;ì…vµ¬ŸcÉѼ#0÷mø{ŒDU÷»ù-·Kjéj×äw¢ŠN oYAá› 1~‡½¬VšÊˆŠú†Õíh2Ž²ð/hl^DŸ^ü€ê9}MÄjøñŠÛ9ÙÒ l²®ÁÎSüI@¸¿±!ѤÁ˜´´럋IJ—c"ðùq2ÜXçÀH)œ1ÕË ‚¸!Õô-äðø•,2)0r¨«)7ž:Šé-ÕZõ–qœÞ±õ@Ÿ»>%¶“NÞ@BaÑû†¨„C)g6!üŸœ$¼ë#Á%~a«>„Ä^pºwlc~ƒÙhûSó¤˜Ö↰Ì•Nn5{€`°pbÁÑ¿>u¡+]-³KtåR²¶ˆa{ŒßدäF­“|¡Fžò¾
+/¥ð§©˜*f‘ÏY~€àp@,Í:_º¤,™AEGÛ:_õȽÊØv
+÷Â+Ò1
+ƒAÍT:Œ=]</!Û²Ýrf±’&Щý¨òŽ" Þ=g\y>W|6!" ŒíòÊÏöíâ9
+r‘˜o¼”ô·<¶ô
+r£ÌWGö×qÿþß¿þú{¥š!?2_Ë_¾B{
+‘˜ÔI 瀘x|Qnü’¨ðcBºr(]maCðg8gE$•0FÒÉÍO5Ò˘§ÄæÚ™^Ù%þ¥"$M©9¥ ‰¸¶
+z±¡c9‹sZný)b
+zê°¼§7[—´š4n½ŠõÃÓ`]Ï1,é•ÕÅ;¿AöÃß
+èŸóÖAÐQQ%$ÆvÝÛ97 fÍr²¬÷úÔe,æË
+
+U×ôß@d ¶”iˆ’ÑfS,/ý~?Nzã‘•^øÂ>Üqa°c=3ÿšä§@ `œêºeCp*'œ–Aù "\ìÇQ|b¢Ñã?:ß@ýëêQD®)ýHUnQZÍ®~ŒbÇ<$3T†xÝz{1­µáŸS¹!&
+Åè‹.ØÙn¨¬øšqy¸¯=—úç¼µÁÓžK;}í¹Œ…§=Î^´ÇAܵçOƒüÚƒEA ’TÖŒyÚsyÚßoêˆð[õµûŽ³MÓ1Ø“öpSe]Ò%tæký0Ü; ’1ã ÃTçº=H–‰ŸÁàÕèŸÃ’´Œ*%úÚãŒó]{n OVÒ‹ƒpsü&È—>Ç­sC jû ˆs«‰t<i¯‹róµ‡2f d,Uò5ãÒ_{è*c@6@êÓ9øšÊŒàÍ×fŒÎ •L‘=h£šPñ˜
+nÒמÛVܵÇYœ›öüi¹~iµžB Ç]Pq'€_C¯ÚCcBcª±@DGËÞ@뤄sM¤$¯¡M< ³Ž=2ûÇÏýb>Ú);ì§0»O?¥ÑUˆlÞ¬²¶‘™eô±@…–R|ÁÓð^¦#Ú•öj¯FÁëvx`íJâµâŽv!úËzdeFJšL“A6€1{úš
+Ýå’¹Õ’Ï6è`Éf³uuHJÚï‚áŽÞ2Ò¹
+ „¥fD˜‘ì7êÙñÖnï”ÛÈ\ïâŒÞýMÎ_kã,µÂZ¨ÿ¬½3éÔâ×¼)[H똭öÞÝ@Zdñ¥Khõ±@ ‰ÌJñÛ 8?†ƒ…|°Øî羺¹êÏÐP»PœólÚŠGÂ&ܑ֥_æ¾ Ñ¨eŤ³\ZÚ„£Øˈï$l¤}Ž5<jm´’×9¨„MøÚ..>¢u›iþ„ˆ~v ûSØçÐdÍûÓ1ŒqÏx¦-Ã4kñǽë¾YD›8šº3<×Á&dyôµ=<Ñ¢ó»È¸ÞÕ¾2ÒÅĨ|]F#ôm\3((m`—ª=œ°¢Í¬aÌÚYB3þ„´ÛkYþ¢Ñ'Þvö+fžUd¨Çú–¤š¬zø2gIz• Ød§à<b)'‡Ò6>ó©ì"œäwýÃx¹$Yrë0t½‡ZÁ ý?ãzCUûŸ¾‰j»2™öµáð5Z)‘ ¸lóEÌclë¥wAÞÞ>÷è¢
+ÞpÝ@{³s+| >“ÈF2âƒ;IyT=`Ì’"ÜÇ‚$\ ¡ƒ ²°
+k™X“‡_â;⟲ï\!ˆ F‡¥+ÞM (þ|âs|x[â–’«…„{e°©D*‡DMûÔ¥¾@Øš091ÏÕíÓb”¸ôû~Î5÷Û8컿Êa1ÂT!x°p…DÑÛäŸú<ƒÇmXb¡Ì>ñþ6â$Y‹<4²¾Ñ¿‚\͸~îúzçN¦EFÆð?%{g}¨Pý†ò³ QÆG&ˆFíæWg
+ÚÅ°OTÍ›ŒnYc“«Æ‹g‹á¹ðKfqð”ö8À™O Éë¡çæ0zÿ¡EÁjá’uë¶!•#¦tÞÈåÙ#´Góó×ä—'Á ö†ßÞù
+ÿ_Ò½>Ë}ˆUpQyÕUø” CŒS‚«ÂÇÆKy‡§ð‚àG:«‘[%OÄÁfÌø+zßú”¶·(œjŠÙ?ÊÐ!æ4±Ï=e„YnÜבž=#)§Æ€þú
+ˆ“½©>5<ì-ÒçÔÚǽE~á`ôÉGÕ?'Òy\R{ñ%óJZ$éõ‰¿a<–‘gÖ•GªT¼‘¿`<•’G¯øà>|‘'ääXS>?‰¼riê\;}‘§¸¸Ò ó0ÎlÐ9:Îý™D.iRú ?á…Á¨ü(ÁõUžb儽`ú£Ì‹e™{$Û“y1y"è%lžÌ/ˆ$“žõ*Oæнåkøâ
+VÇÓ9˜*Œüx» Ÿ8+íTã(ÒHDH3ا2Šë3¦(žé*SœÉúVö²‡uÊíÃÆrüÜÚ-—ÌΖœ·½&œÁe1ìÌmNçSÿ>‡ØwÜ°FTÿ„´«â{ƒ˜Õ1Qžçï“ȈbÔ;E9Òë³”?ö¶!è?Ƙ\ÙÌG…ÊE3^½u-~C¡CSˆK;
+6‚)Sñ¦yû’ì}lI$:‡Ý^ø•mm ‚Ô¨*‡´}Ü;±ï Íšc²­å€z¦€Jˆ*×—ÝuR*‹ö­®7—¨ÆO{V(¶Ì‚»ÑU>>ÅBàOV˜F ”è9~Ìw ê+h‘ì2’FŒK× ‘ÊP/l«ÍNM%¯î%Û$€Ð_xSðx܃xŒÄ>ísJ]4ËçÝ…IÿøTqŠ~þºƒ|²WÙþæ~½¾ òðö¹·@QðfëÚ~d9fBî<NçƒßʘFVŽÈ< ŒQi®ðµA˜˜¬0×Ò<™µÁò±O>„Í¢Æ'7×k ²RX›ˆ@ãAVû ¬ILéT¤òÏ©òH µ$ÇfmÈOärí¬§QBÆ)@¿Ýþæ2aíëŠC¥±uãmÐÝ’\1Ë“¹º¸Ý­<ò’âbφu¦ Ñ­´!ì" ®Lé“%„Ÿb®èŠkÅî€Ô´[‰•í×w?Q Ê
+{q ¿7/Z‘Yö» )äS,(¾úx”0tüÛ6Z@r óÿŒ—Ir%·EWà=hì›±<ô.<}µÿ©Ï%A…_&²¤øáoW "“ÀÅmJR¾:¯J nX;WQŽ„fât'¯M,˜€} ŸA7
+ð¶G±üyH<æ. ò:23Õ|æ ´3-<èÆÅ¥È99 ά¢õQXt[À×O¶t-óÇß*U¾ AI §ç
+h2p'†ˆGñÉÈí^\ ‘N#â†Yˆ“NZ B‹ßˆrΆYªC‘#vã²€,4…ôçìã‰õ;µapýi};uÙÉp
+ `_ì{Ù1CŸçª9‹å>mÍiÓ2÷©ï©Ã°…£à‡l½)¢ÁÓûp0¸:<&Î A<=`°Á8C8ÿ¼üÿéêµk0 ‹˜Ça„ ‚Ìa›5do
+à`ŠvÖX ¬
+Œ¾§èõ“åÙ;ÖØ`•7ª³ø{aRy„uõe›¿X¶€ñ&²”@.p=KÔ½nkjñßP‡ &E"=Áœùk$€{ §™­ Xº¢h´lýˆÐ ÂÖÔ(±C0E«NÑËŠ
+vM
+3Úž5|)È"nÔ}Ù``c}u}zz¤;Æ#déõÍ u`Å“u0º ”.ÿxã¼ñE@µ¡²i”韃 "ز¦ ÌfÕ¦€iæ¡i—”Qs°8É
+™ëkÞƒ~o
+Ç}”€{ÌmÏW=1W0Qô¹"ìR¤³ž·"úÆÇ@(˜©—[!g;¬\°ïb ñE¤ZŒû!ÈŽÅ—/1_àH“q¿–5LÜ<ýEAë´ÉÆÙ5a¹†Zýqƒ§yßiçç–Ø‚b¬IáÙ›ãvï"öÖvÆu‰]TlyÊg÷Þã%€$ïü6bEIeóÃÉ÷s‚è0³Jnìjy?
+”§Ôu©q½ 5)­h
+zAË‘oàKÃ@ JJ¶g1õ,᜽}ŽüpáGiT[ Uô‹0Ò< ».‚o’„¡¤ìŸÛ(—`>˜½}®”•&œš
+1
+gÿ
+éKð¡¾i-Ì„äŒx™ýüw#Ë{ŠÄ¢ŒSÀX>G©ä×9°'¤ÃäßÒ<&$›Ò)Á¦íµÅ³^EŽ®´ÞŽë›hzÈSî¾ísd1
+¸«öô˜Öß™:ä”gµC‡9"¦{9ãyBQRL-|kR`&ÇDô»m…I¡‘Æ_:L{OŒZã†Ë•K0ãµÕô=*¬#g?a‹º×=?h°c,埠B_’á¥ÑfG‘oX>BÝ{Ë©LŠù«ÕÜÜw„qoÖCX ^Ÿd2m¢Šª“QÛ±þjXÔ[Ï[糕\<îT{ëRjT߇âZçîV¤(*ñÎO¨vסD˜Ì*£uŽÜfÇ<ñ’a—ƒ^˜u‰X´;ÁΓ{Sûy6BÞ ©ÃY%Õ&~žQW
+´©Q‰fwr@üIPžÔ÷±@ȳc›¦b0ü<;ź!Œí¬(<±!Ø:j°v-Ÿ[ᮧX¡n£,+É]sëu3îR…ºc[Æ|ÍzCÍÙ‡ƒx"o ßh9s(ŠfëSÒ¸˜|7,ö/$¹A¬ün!
+§"D à ³ÅQÌ΂GdÓY7„3´]7›/Z'ÖÊ6èñ&tK"›™Ñç0Z•Úµ½NfV’âÂÉ\©ɲåH€‰EЭÂèC¨†toU8ù€™#¿Y|ÉTšeXWƒ”wu !‹¼!Êe˜ñÔ»[ V˜ÁAi_aj½_!UðŽ Þ™ý4JïǨ f‰& ÕÞ6ç’àJ9œ… i"S\šs…¬VìC±‰NñµÓt±EU:Yi§~
+¬1–Lör ÙE¶¶@p¼¬^Û¾úävaÁ»  tû9Æ›‹Ûf?}U¯ã¯˜úÊÀÐGú”¿ K¢ºAÿúëDðá¹'ùh‰» ´ Y«öm°:õQÎÊ5cC"B…kÓ:\zË^i[B@òFümG1h_$¢+Rä½N‰2#tY·Ë+²X-¬"ÞÄó/9Û
+ëT’f!§uäþdjOO§A:ÅPjºCÓ†©%û¯3•òϹܧ÷ÄwÄ·JÙFW Û’Ý0ÄœêÍ;ŠŠË<9êá_‰ÊApª
+òÙÏë]žf¼¯eØgrè7ïWqÉ+Cv·¹…º@l«KÁ½u.mãæÒ~þ­.mì½Îm®oüOCµWÅb]‚€Z-ñGeŦV^³×ð»ˆÙˆÝýv@.i\·»>r&£ü^ø úM¥y~(#°ªA°ÐqˆtŸ”¹7Ìï0¾ GxšÑcs¥˜±*'?Ê<ɱîD:øN"¿{
+‹Ll’pW²Š‹ElInHþ:„·‚ìŒQŽ½ÁåbïÑOto#¸YŒÙÈi_Šd|°1¬oü…ÌDzZG‡iMAgø‡i¸ nO&œÇGÝ/Õ”×:ïŒã÷—³ˆbdÐÒgµ2T©¶( ƒ`e90¾ÉɬRÁ;ÆkDgÌO1ãÇ v½¢øen»/. z2¼V)ßÞ§7ºÀ4w(UìªC?/¨ú™®ÞÜÂY}óõ‚9¶†l{Þ‡³Â¡4&ÅÙÅš…”ª¨Ø BŸé§2™Õ“(Qꪪ±¾¨C™%æÓ;÷e˜AnUð,vmÕ“u1:›È*½Nu³Ûa˜/*¥ø?ãe’œGnDáè\÷‚yX:襶>Â;jéûû{@‚UV‹
+·Õ-=¢€ÞÀ}?~ÜA~#Øš¨|'¦Gn¹Â;õÄÈàà°u»[ÐîV#­vœÈÞb’)pþšiNn·Ê2Δ¦zêV¡édÈ@x:FëÚ-nÃváÁ1ë~™¡”P×ä6žº•U d0òWíÊ4´a§û„Õì6h"Þ¿"Ö†°wÞç—ÝkÞÙjyWk×äwû
+íÀ)ñy·^1>Çÿ‰¼7Çgt-ñjX[|àx¨)‰¹žŸ‘§Qõ“Åu9ðH*áÇ€à_9›Ÿ,Ot˜§8a@
+ìuñϙﴆÕ@q’OÎ@ø湇–žHüN§ù±ú@ò[טÕ>CôÙ9¼×‰ä°œÏñ$J*EWSÍ¥ܢÕ!2öî“<ªŽ†
+ù‰6ˆ‹ÜŽìƒ¯i>m„w¦‘×39õäE}Ü4ã®g¢ ÂA =rŒî9¢-Nƒö%—åhÚEâ±=±üävB{“#þUWß9,9,/HÀ%$¬¹,¢ÉÓTÉ{}hWV.`µ J\‹Û. "ÑÙiiôz±IE+R‰õ‰å©œ ­<žÐ"”pÂ+Ë‹'sÓóÈG
+fžæÕ/ú½Ÿ“º2+ ¤<]&ö‚Þ¥0ðdŒiwö‹ãŒàµÄä¥U¿¾3Êšú°§>q‰BW1¥^çû˜ȇÜ~@ ºUFF 'ówåT‘ý®i…Ý×B¥!ªi}—¢ z2;‘ßqý©ÎJ
+_GóùüÒN_.ûàŸC4䈦–Ùeð4¬g-¤
+„7ð“Ü2Ú‡0!ž&ÈóPÕn2DêeØŠ¦òïfjßaÎ|Þ4Cç·1#vu¢w”b–'Åûgáü@_ÿúþø_ÿýñ×ÿ,6X ×TÙoXÝhS?…»jü¾äöҤà f€…ß6„.á÷{8éh*1€6—\
+±Ú­ ÀO5rÌ>'Qµ'vS8 "kEÏç݇9QHH'ë@¬Ÿô4:ÂûÓ9/-wns÷U×tªãMû¥Æä¥W¿¾³5Ñ6Þù‡÷V‚ÃtÎû퀶å`±!u,'„MŠº*ëb%@KRÆ)-:A|ä‡rˆõ8ŽkhÓÍ‘5Ö‡?ÍnøS?`Sˆ—ß¼œéµÞ±š2† CA?|Ë¡Nâ,ÌÁq¯ö§³MˆœYâÑ曤Ä*Ko\;Ö— k lÙ
+\ø
+ÅÐdCࢠntò4*ò-•o{“ÐKã?]<yãaRÄsõÉË@.²fØ‹<£DtA˜14¶Ý¬3
+!†„Må Ñ sÍR8û‚ELCnW€pÎAƒÚÉüÄé_%`É~ñáM$F&<ˆ÷O~i€°Ö)çÆ×;—FO]A|!c˜ÞüÛpפ0šGrßÄÉä3ÃQäe_ºÔ²½ˆ£iÞ›Wa CÎ+‹&“Û© Ä:õÚpï˜ËÔx—¹ Ÿ÷(gˆ/µq6áZà?mÔ‡TþxõGѤôÅÁx‘,o€ãÆS¡ŸdÑ”*Ö¥9 M÷zùX:°!$Vy„,î1鼋T37dŠI¥-Õª}¥gAc ‹}Á­Ekš@ºÁJb†ç†ÈÑš
+S™Ð!º3ê¼eƒH%A€Ü =ÿØ/§]IV}¤î€~>€ ²ÂdfÞg~¶?S<ÝiÁæ3æ¡9Aåÿ„—ÑŽB! DÿhÓ[ŠÀóþÿ?í[Œ^IöѤ"v†öŒJL€x!J•À
+H‰”—Mrd7„O ;èó‚Xk–¾EGÌJ}ÿí| ù­*–-û§-—²H$­Hqñ÷¿ÞjëãÒ0mE4JÿÔr5•Ú[îíýóÆ»yÓÞ½”ú>!mH3qþ3!õªÃk—a±!]‹‰¯j ¢C¢¨õ^´¾ÿZ¡FôÒkç˲@ÖK­f¢]FžãWáÌÚ”Ûø‚t~[
+ì%D¥Äð.®'È å—U­–yÁçtw·î£Ž¨¡ƒ¯„E_Wã†Z¾ÞmÈEL„Ø;i ©µï8™$õÐÍò”¸jõÁëyÔ³ÒºU+P\ÒF·b­ù†Ô¥H¸xÿx›sSé·é¾oñâÞŠ«Ä*&E~Ào˼Ž\¥ŠE4~·ŠÉ£º[u2¬}ATÞD¢:'„ýòñö¿ ¦¤Ç5¢Ž,fñ«šöèRHW¼ÿÞ“Ö{ç}º@ÍcÀ¦AøyévÕÒ›7
+*û$‘ZÓxë„4jGNmÑo1†z_ª ¤P­gÄ,•]p8Š•.óéϧØ#ZmVFµ}_à|FGØ !-Ð`ðiBâjQ›4š¡úãí
+Á¼å["Ð檑:ëAtˆÑÑÙ-ˆ(Â@šß®c¸Ë¨5l; <ƒÌxCG["^
+ô©­)‘EÏçÔkÐß5û¢íI!0·¶€Hc#ÔP’ÒZi{ÎVG7˜³
+ÿ‚dj0%ªHÛÓø
+¼f2ÅÀG€HgÛC <ãsÉÔÃĽ“D$Æ)E9\ž’éÏ@z¸‡Â? ÜlmPÙÙfÇS 3æ€,T¤}_…òeO–ªº $ÄÉŠ­NÏA%îƒÚŽ„CasÈpg"à*¥òÃû„`îÈ>ÌvMWI©z«’-=餻NuXj#§­>~=”ýó&R@hJ÷u$3Sr8µm {‰œ·ð‘‚€öí—Q<œèÝî÷ã¤ÔuÏ
+& FgÀlöêôʱ*ílùž´òr«2…äd¸ôò\#æ‹Þï˦+·
+ûÝZÜ:çJíNN¤÷¦k`äûn”ÁbØ­¹ È^·nýýÐP©Ê%gÍ–;Ðßwæ‘8¿+™H¦Îb•¹³Ìôàö«Ÿ@Oãv‚,¾2ä_€:†=¹Z±-7è)Ü?ƒr³°´sâé&[O ²â 6€:s dx~‡f¹÷¦‡tŠß—8õéѧÝ«вnl1+Ý`œŠÅ¥ßŒ6|ÁÈm·ºŽ¡>P¹Å׺ÔÓÌ…¨Æ‚¶QI~> v¤y_ÖH-ýÕ1N/VYé÷9nT‡Bä}ºƒÛе™=ƒÜÚÎ1]šÃ×Û1};D|ºˆ|V
+c*_ÇöÊ䔜û%õE›þ`ýìzM[9liÁ÷mðÙ…—q£ 3‹ŽCŒ¿ô)´&Ý-ð€Æ€N¹çø6si ù×k]§ âð9^c—+‘l¼€|£èÇÛt$»ÍE’[3å_€žðî Q8öÖ#h Œ\p! {m®‡€¿7?·HÝ= ÿœíŸ „dÂtØx/hÃÝôÊè FÌœÆ2¤6ó}1ÜŪÏ=&r9<ÒXUQQRõö"†+fCüoˆ¦_‹|ÖÒß–C0I”›ëÇÛtZ‡¸²g’4ïòSPZ?)Š×Îþ|ÊÑŽ>a7—ËʽŠ6·Ø¶…¢2†zͽg4xnz?ÞYgÓLLدªSæÌšø>'{#UÚ÷¥À*áÌKRlAMxRÈURúÒÙùAÇ
+™g¬ŽMzšÝ ž} âØGAû ŸSÒk§0­HB×å¨æÎåNNÃæKàÀ—dbðV»SLLËeñß·azЃ¿ï*üiä?Þ cÑ+‰'ÅxaúSÐc~þ¤Kg3OËß"¥‡Ö`Þ†ôÛÊó{ÐdZÈy½jI=4Tö«É„¶Ã]ÙTË•îêô™0iÙì²&Ï Ä°EÂW6Ó÷TªÂÿ>!
+þ¸•m~I#žnÍÁ¶ 9°TÌé®T¶Rc²RõÞ=¿þzBS¦•çm%Äo[i|­î ±GÀ_™ò‘Ö›!†I—•A’hT·µ5a"¿Ãªn/Å’ ˜–N7,ƲYŠÏ†ØGæ“e
+Ÿ•qåM7`O K°š{3o5žç¡(ê¿Ô¹ŸyQHÇ-Aÿ&
+·èë½a/<]ããe‹>E ¥Uf°"Ù>*$Yc·ðÌÆ~]BfàveHÏ'd”fÔ-?Ð:¸À'<“M?‚VA‚ä“7_ÈÆ"€¯ØÏIᎀ¥‚ Œ Ý)>aµBYÚ\La‹>Vë;£IÍS{
+Ë2˜© ?|¦üLgc7?q¸(¿$B*‘ybE¡ñ”íQ<
+<N%-.õ¨û—’uTy€~Èäôôx…s0SÍ^Åȃ,æ©ààjÚAŠ
+5
+0]œ}·/pw(¥Àž~þãEQ`ä+8íra
+í9ï|uá…“WÙƒø³öƒ‰ÙIŠ°`H„N[´E8³Èöwº\}È„«Ó\„JmÜ ZIÁ €‚‹“Èl‰¿]fþùP3#ºÙäôÏuˆ§*³Ù¾FˆL˜ëhƒƒìÚ)é€PǶz^NR [Ò| ÇÈK#{Td-¥=˜w ߢ©Až¥¸<y)9ŒL/Dz"`ï;QN©F”º Š<åe³í0cc°W3ò‹%×죀¹2/¿±²ÖYÕS^óÞ^DINa_™K øw~½9ë$NšE+ç ¿ïåú˜ßᬋIb{ÐG§¦õ‰bÇZÏL²!\jÕT+"zX†´ hÂúà>3Æ $õ,p4}ÄAMˆ2¢©ú(?Ç1ƒôÒǼzÍjǹK0£rOÔ §dT™!`j¹"Kþw*Žš¡}·:¢0£
+—«zeoLmGŪÈÊOÒ•µ\·"E<¨¢°|<®íÒa}%º ½–}y÷Œq2Œ˜O<J‚€ÐO2§÷ôk‰5ùk Ýï|ƒd‹1q0 U’7O±úFð·}[ø.ôô¡®V6~¼9à’ja­à-öS K%þ¨¡X˜üȨV†
+d µ¦âlÇ,™FYǼ¯•sὨtµ î ‰jç® Hñð3m¦¬1®™KEcyŠ`:Ö
+<6àš1»¢ Ö»¼ÜÄŸ ïcªh¢§’ì:ì»,’ÕÚþκñÄ~7Ë ˆñõ°§à”0¢)ŸFÞ¬ç¨KMf“詶ï0Må‡4»ë1ua¾ Sc.ÎÀÿn;
+ùÍŠ!Ìœa løž'@̆°›ª¿ï`üë_ÝaòÂÙÊqxÊû‹IÓ·š²ü€*º=b«ëî°ƒ•0!
+ˆû¬ ˆSšlc¼Ï„ÅnõuFT. ØÞ§YI5>p]x¥†æ¶G®Ó8¦æ7â¬OßYGýŸòrIŽëV‚è
+´¯€Q…?ÆšjšÒûŸ¾“@¡ev£m>;!ÑE
+ÓØKœ˜ŠÇLtZ—áKK¾ÕQ5¨×ŒÛÃ2`Èxû®è‰Ž¡k&2™jw¤~©¸IÓsͯ{M6”vŸÂ—Ï{5)ÝžóÑR¦€Q€:!Ø èqWÐ ™Û‘µEs*nNš´Ó¨_¢QÒ¡&B&еÓÃØ'kð
++nªsÔ-³]\ =6¤E¨ÿ¥$A½‰ä0b~_‡W@5áF÷¬3{á<žÅAČՄŒ—Ì9—?ã‚(ÓH,—33ÌI{BÛ9N#¿@9ˆX,G
+´_¼ê=‹AW^ö5½ÆÆ^/D}¸Bñg‚"wrw.Ì€øŒ‹0CÔ‹‰¬„™? Ôox|QFÆÙ¥Hagh*€ ¥~AÞÆ=ñ…9ÖÉDI6¯£Õk`¡¢’ñøÃœ1º©;<ÑÊÆþ¸ɣɊ
+%ŽîQȹn „^æäÕÞ@ FßeðQ2™†kÅ®í4íC’AÜ@ŸBÿAñ¡Ì3æ^‡ 7å•BÞ±zˆ«B|¢„ßÁñ ô u ,qËÛpê4ùÇ2¦•Ú¥è×›¢¥sàE—XF硹ßc4ž@ŠÀ°]sé ªš4„ u2Ψ@Ø -!`^ö÷Þˆ§Çx¿—Œ3bsBî¹ß(¦ˆ4,½}ªò?G)ÌÑ]Ø=Ûf¢;ÐjröÙoXOær;”* )v:®ç+kRB'À Íênlš¸M©7]Çéî¿Ð›žù'뮶Vó
+1 2ôh‹¢é5o±âÂAØ<â¾.¼¢2ò&ÁEÈÄ„âžMq¬Éwü/%•èRVh“ŽZø/Ìyõ3d&ìø¥ˆ[eºÐ&x“c¯N­hš$%kõo×|áÔ_o6ûï"Ý5Úv9Ó¾ÈK‘2OV ª¾ÞŒ.uGüäÕž
+Lw¤-Àu±"¯=ÏZLCñ®»¨™,Âçúˆe @Üþ ™ýµ>šíqlˆÐÓ ØV‹øÜ%Î2IØSÓ¥ä÷¾Â4šÜ‡iïÖÁÀòbÕK—½>„ÎÛŠÈi=Hô¥æ~Ú
+qÍE‹^¥%{C*ÓØÆW5Hmãޫܱ°ô½û¥$îõÁ•öÆõËG}cF–Rœ†Ã:&'J»å$³¹Øþò©O`(y»v¶âð +äÆâ­ÐÅÄÃ}¯B;æF°$7DÛp*ÏÆiÞ”|éП?.E×^ψ!&Kéüÿ(z¿Ëvß(zR„ëh=ýÚê’>Pl‚Z"ÚeI¼¶¸¼ÔÖ‰'‘¾0ç5‚d¾Xj—<øIY¹êÏ]’•?Cß%½Š Hlzéß{+<ï4Dµ* L¸÷L{,`G  ¼”çB±bº©GÍ
+KF䧺½×N(Φ ‘£†oÆRdhsìeI1wc˼1_ñݶ­’$Šq7¼G;¹Ø™…÷V2{æ40à­øì“W·ê*I‰ß‹DA§­E{*¸~fù¡cˆÈèæÖÚÙ¨iP–¢{2m­‚§îuêPÞª'WcNX±oïRÂœòÁ¸u?[=}&û›Ô³¦h4b(@nƒ
+̲KÈU°)”Ñ£¤`š©4’óPÞE*|½kã÷o!QÙ¯
+­_ÎýN¡! Çùòµ„wâ n(7 —¹„}5—×’Ï{‰$›Dt·–RÁÃÍšxÊs¸-™"˜n“"äs\¸=.˜{cDJ­ëà¿\Aï%f;›¡8jûU//ù¥$z¦zÐ"†Êß­ÃÍÑhhh>[a;™pIWÍ¿B"’“=9]t´ÎÅhÞK9[ñÐ,IÐO&ÍH‰2÷2BÁÒ´TÜ°‰h {²Ú|S-`{½ú„—¢Ìÿ¬< ¯r„É9"¤P e
+P¶
+6!ì-ðÙJM‘ir} ¦TĤ`=Ä´âb­ÚC7òÄ׎ãt â×Å1Q<\ƒDzhi˜Ô(/—ðk @zÅŒèãB—"NL±!Ö‘‹5áÀÉWŠµI‘aÄgÓ™ xìñ5µÂ
+–q}ü™%†Òfð”1ƒ|%CÖüd©¯TæÛ ¤uv·ÓŒ˜Ü¼¡D¤š.áCaÍ=Œ6/Eˆlû4x!‡5h>ÐxÈG
+j›¯—sc‰‚ùE
+zƒ¬Õú›§hðsï¸D[†Ÿ 8" |©ˆò°Â Öùn% ¹
+® ¹â’I¡r}Í…*¹ôYå–(âéÄñHX<;RK@åWó‚.J0€:p¦¸b´Ÿ¸Â„Ò»„&aÊ?¯P”io¡ÛÏ—¢ë£·l#ÿô=Çß)zÀÏïLéN¦V?dËX¦Ä",ì•tñÖæK-^:¾Ë¥+9©$‹%°¤ ¸%N´&Û•Å2”ð~ w·ž{” çx
+ü ¢$»ÃŽw+ö¢ +r•g(üÿÇx™$É•Aô¼C ó°.-u‹Þ’÷ßê9†”%~ Y’ÉZLzã1ø€MÒtnHW¡?ôùˆ&C™&d}J*›”DJºƒÁa‘ o·Aú(MîË~S`‹pR¥ïSžµA V0¥[…NíO}‚˜l 6Éõußã2 œÌ7[0SÑù¤ÂÞ` d1û;GaÊ«Ê&2ßÐJÿ²Ê Äc6ºs0Ût@Ö§Žv[çCcÝæ>ûUÇq'QM
+{"«@ÕMZÚÒòѨï÷æ¡­Xi7cÕ×æA{‘<…ƒ½’Æ z®ºñ9“4þv§EúÕCØö°Ö ïKRE‹44Lu+R«ø_¯6KÙ 8Ž$
+ÛþïåsÉàdiÁ#g Ða{Éøiâirˬ* 2°sÉ"OјŸ‹k¦Œ%’K\â ÒwT?Áâ–ÜG5¦=ÄÃh+‘ªOD‚J´!ôÏ€¬/a¬KåÛag¥ç9rˆ*Na;ZÜP#G´ä³ºo%.ô^Gjü¶ºuÎЪ1mDŸ÷XËtVjئ—àYQ¾“?o!•¯b šî/
+Ó T:Ú»}^^Ž N‰oÏFÒp<Œ… ]*a¹Ý^Óζˤ¨O-*Æ
+â£ËË_§Vä¢øP®ÈÇ„~ÿ2@欓ñ:åد[c€žûg|î ƒÌÕ:A‹_Ü+2sdOn¦Ä
+/ÕeœJŠ»xò¹_ñ~A°G\§—a‰\ÇÞŒ
+ÓTÌú b˜4q\™”0ǹ¼ µq²v¿®© BŽ¦¬<ãŸù)xžÞð¹I>í%&êd-êç9ì¾Ãçé÷ΦH(E>º~ zŽç€p]pÉ>F4œ˜§¬†²¾9¾}Ÿ…û‚fÊš &šÿ_urüÿ’8Ùˆ87†â0±jDÆÿÎs¨9VŠä:7˜æ2Cë‚Ô, ]ò:„-äÃâáXæ`³ålž§šÑÅYô5~ ²sô9—¥åÒXËÉâ`‡’áí"Þ  GU{i·f%¾„´Þ[¼ô<½ôÞ.—= ;†»cqbšœÔˆ§®G šô쳂'ƬN”>ã&¢—÷ý!È ÷¿÷¤÷ÆS hl£ÒÒ…ßBþÉ`Pw¾ð{ãÅì Ÿ›ßdøÉï@¤àëÑŠI†M&¢å )Û¦ùA†Ò-´•m®=™dè”B—ð6-; C’;'PÅÛhø—šì±#ûòk®z&IÎV,‚JWôâ:b|ˆÈeÞ!ÁXÌS'ÆWkÞ8èL
+b@˜OªFÏØ’&D‹ív\~BBï𚦹Ì4 ˆ£¸1̨(#Œx¼æS™Ç`çp|,y_b.=‘Wy$9šÔ‘ Èø6ž€¸4ÔÝ8Ÿ1ÍÓ…®“ð| þå'O@¹eV†8êJS” s  BQÀÒl™Ê=áO¬oš+]¨%Ï6<÷É—”yù6}®tæ˜ß/‡ªÄƒ´(¨$)L–šçøÈ^–óVlÈÓs‘¤°ŠÌM$;<Ä€¬v‚Ç’K›Êžç|vܸÍ99æ«Î 4ªsαQcòÑ«~°3Ú.·· Ý‘ÍâýÁ8îÏÄs”/!÷’çµRÁWTý²ü* €ÍÁã÷´S4Ñ}Üþ(÷$™£p±¼‹¤µ ¤¿} û…Ÿ€°;³”£)Ù´-VãOÛ¢ÃòÏö}SfxAÓQw¶ ‰;@êˆ|œVG àñõ"x ÃÊ4m}‰ÔÅ3Òé%–‚µI©-Áˆ0‚6¤H"Nñn„n9$¼Ç!iîñ :Úþû‚‘r‘
+5¾ë:mÒ
+ÄÇth dÄû.'ÝG‚Q}XÂn[k‚Âðª±*ÈßðöÆ0Œ@ Hç/™’Úò¬ÞŽY¦¨¢´ïù¥:QJñÍ
+%¶ý.¤
+­ÒsþÝ Á%">4+®W‘”4 µ²^ÅìBì
+ ¹óí SÌœ¾-•8ñKïkF‘<*¡GNˆ¢¡‹ƒÚ{F3¢Ù€·³Â6ô‡ç×/caäe¸ *Î ÜúÒçâɦ‡fhë)çÚÎÏ>Ak;ä ±Éq{"Ö“üÉåõ1ç†SW2¼•ÁKê7MâA¤¶ß5Fœ£ãjT%p
+%n‹Þ2y–`Ç[m+hôòi)µg8zç¼} C“±dpu¡Š*åW¤§Ø
+p¥ìŒIå¢:ÃPîù«wJú2Ó> ‹ïùäK0Côe9 °1õâ¶xˆŠ'rû¶6AëKÝàG'­f Z4Ó²pë:,cQƒ[Œã¹J!=Š0&„yæ\êÕ—µˆDB([Bíßü‡7*r[ëÎõšš(:~vÁ¼Îµ¾´†P vÌ’Î?q•{7ós@Ø©$ˆ(˜*Õ@¢¿è+ &“ÍÎäºýLJ¾
+Ñ(/)0¦dê« ´X*Èh^ôµB‰ˆl‹…K}DT“ž1¹¦¼‚`’üY» Ž–=é`ç؇¼êÎìGp¸ëiòªÃê™ëݶÏvòZñþECW¾œS^c&#ôÛÜ~Ê+5ßÉ…p‘ך¡þþ?ÆË%¹’[¢+x{Ð
+nðÿû {žJûŸú$ ¶}«Pj…ÖREHd&ˆ’ûáÚ+VJ`Ëñ䜋½z›½Þ@ž½z ‹½Þ®|·×ÛÓ={½•ðn¯·FÜŒñÖOÇ^E ­&nªî²'nQ¬HÿXO]{…£(f!Ç—šãƒ½.®Ó\Ú;Ž]üõ:0w{½Íg¯· öìu=‹u9H±»ö
+¤k†¸aÍ®»ÞTÉs×Ubù,ø,ÞíU‰Òãj° ¹Æè5óæ¯pbhà©÷ô¿mŠÌJÎá,ªÐFs hÉ:w \ƒ]$îÄ4nV×aï<wö®'ŽÃz ‹ÃêÊ•t¨ÏL®ÃÁY3´q¤‡]%D¢!™ÓµØ[#\ÓûÞ<Íc‹Ö¢‚ø“ßLÚõÇ…€¾*ÿµA¬Ì0ª2ºúåhk¬¼•¡„¯ó€Þ»‚>r§_ûê8CcLŽ[êÓÕ¯ Q3NTE*³o…;0ôM¬<·úèþ>縀Pvô“2¾%™9¡šÆç #3Ðtdgq‰…SO¥”-œå98qnÝè¾£B‚4ÔçÔΔŒnDxælÂø²B æ0 3‘¸³<%«°ÚÙÊ @¸䌧mH¥.Q[Gqë$ö~=5÷3?}f$^Uîq E+Áõ fëZ%Fìç.}¤œl«¥ˆ‚c0«øfÛÉZšC‰ËrïU¾B0¢îhêäÁ—f”@H+”ôwÕÊ‹üGZBáRÛmXþ¸Â8ŒFù)zÌq¯ ôjàE²é’¢ûò{;óÚ’.´i8+2ŽÆ„ÓØø@ ÚÙz ˜H4©"è÷l–².¬´4 ’Ò Vd¤:Ã2Àôz"©¬R{I©56Ø„£íê@Æâyžïï*ß@Þ\QŸ®g„Xúã„: û¬;ÇýtÕŸÏŸˆ”iVAÀ^\¬& Qy–„ ×ÛZ怟e¤ c3Â+7dlÔLpœ …¸¶9 BX ö¢Ÿå6r.Î’Z$œ‹ØƒJAYi?dh
+¹Ì(F‘"@¶ñߺ!Cgê(¿±£°~Ô¥péöôŠŒöÒ¸VŸnSd«(ÓøüªƒV˜ýP ¨„îíq~È[¯ì¨ ¨½P:~DçÆï—A
+¹ˆõ¡o#Îìbd'ýïtš\ˆ.–Óˆ.¹ š® ¤E*“hN³†ªxH$ÛÙÌç( ;¢ÁÇ®N”±ñ TÐŽ*\·!®¬}R“àb¸É * “@¨Í_Þ8h†)"ÉClkõ@\/a‰©BM»OËlQ!˜ÞF®B@PËÏÓƒ.8ÛX{…<_ägMsþ[Åm†0ݦ梎¼çÃi„k{ßÛç
+ÿ×å3'¾ÅC²ÓÖ¯ b
+
+ŸªÈûШï.n„ßÜP¼™ÚC6jÿ¹U ÑwmºA6RÅ1ǵKn‘DcÚ¦—b¯ÈfÚMyŠ╇QHÆÞPl4`
+®oÂ2T*·óô Ÿp)«'f›?v=„"‡C ‹Ý:¿™6k¸Ðšû•úb¨ÑÂ
+×ö¢„
+“µ³BM/d^ä"Í-ù6ûßaÞß lL+•“”-ÙxƆ&"Ǹ× S!Ñnýt¡Ççÿœ³DV\®¯i.vÖû©¸¾Ü®ˆ¬uÂ~B-¦”×âbe';¾Îî'ë|æz6]R0¢IF â9Š ¯D„3¥qåj1†}Í!ëæb XGŠ˜ÈDڇÿëÞû÷O˜üëߘ‘^Rk€´ãþE{¿0ÈnW±0± PÔ­6{Ì¥íïcC¢T±ÍùäŒJÁpì·Ÿ›“»THÍè‰+Ay›47¬RJØbx˜yš´›Ku ÆùÔ/„r<|’;X¢´áú·qèüö*Ÿó ä%ÚÛá*ñµ¢¤-oÿ ˆ)¤‘ØMçöJŽ£ËÐ@öûÔÐD¹ÓYqß²ÏÄA£IHôÍÏ>¢h [âNÓÏ>pþDá¦;¹ÆúÞè†Á¬ö‡¯pÿ!gâ6~öa rËÌØ)Î=û$‚bRn§§'û65RlÍتŸIµÆ“}Zø6Ó^§®#¡ ãXŠY©i:G};[šBZªæI$ó¿#‰BÚŠ±­ ¼c4ä•B"ÏKŸ‘ ÃÌy° ETDŸåÔY­ª]A:-x±š¼TU8$ÔcwÐ ,!«©mkFi«â'Óžý6DÆ€,í(Þ¨ •ÈQì(ÔÉ&D-â(®!ñS“•ê9‰s¹ïÀñ “V¤RB›êDmZ V>la` £ÚDVÌB@a7Ð’g—!ª±ýÃxyÉ•Ä@Ô‚õL”g^iÊøÞ— Éî®æ.ƒƒ_
+‰„Éœ­éµÅW\ˆnq0舆ÖlËà Q[NãÒµˆGˆ Öq¯Èh6Š½šG$²ÂËg8b#ð gÉ=qçÝ8.bж­C‹ À0Y‰dŽ”Ý^ïR÷paLBr’/zN­vb=?d…‹rUçX©a¡@«•t¥8ŒF^qÀwh/V0vÒÆ ‚'õ¢¼E/ é?LI/‘Ï èú•¨h€mÐn°tga¯O‚>°æë*ìÌ nÙ€ò8
+Ç`[’Ÿ—€4N »ELÁ‚˜·c—
+ËÖ5‡NGk£c’Õê,ѪÄOœÛ‡•À4›×9ç5B9vÚ/oR²ð€­ à–íqxÍ\~õrgH«!£+“"IÙK–ÒBGyåœ.›0i¡-"nY;FÆGµ@É@ž·35óÆÄ×þÌŽÕæÅP'󊟇¹±É#vR"E³Ð ,3ˆeñØ >+zÀ
+%2ÂDZW¾UàòÑvI
+ÈMÖ4›¾¯Oóã¡kuϺ²Ą́±Ó „3½n»ˆg‹«çù[XP#@º“ç¾Æmâèª-Ã;âéܳàôɦr)y\K°,`–æ¼ÙÉ‘‹
+ñŽkîKÑwÉ>3ø‘-8ß¡jœÜ§NBظçã<tsa'C?ò–z³Ä
+LLI8©ã÷ÐádÑšÆ yÒCó@}~-†®wùxÚ<®yñ½—ÜI6ib¥ùUÈáÒUŠD•eK¹kòÓ`x<`äNÙ¥
+M¬5ó)fÀÊžp1Ü‹Ô 5ü¸[ÐBNÃÆhä燒?]êOyÙ“(»2ÒóÊøú±<;¥Mfh/Ô*žË¼†ºº!Wdw#Še¨úð„ ’oÅÓ7¹Ç‡y)¤(¨•¨WÖˆëõù,¶ÊÄÁa‚yÚæ·Çç'ÝZCqJÀ–Z5”ÄìöÄ$®–à ?ÿ¹%smŽ» pØ1'@¯…½~*ú¶"þ ›#Ó¯E”ÀF´ƒN€•,1Gp.%‰BLI©÷àÇá™=Š4e¡…—ï2êE¶ÖÖÑ‘¹.óç[áUaÓ„7·F\Jžò‰oõR”ŠÃ@gí¶Ž:°5F?– ºO…È(áyf/ŸYtŽï€rsPØ!îÍ‘½¥0óYñ‘½´Oü;’ú '}³€æt–Ï Øè’¢
+7‚†w¶ÃÐl0Û9]ôi…L@Á’ƽñÿRÖ<E00°H6¿¯§upWËJø ‹‚äXüÞ´
+’OS)bè¼Ä`…MÁŠ¨ØÍ–éغµ”â|'Ô¬È À•ÛJ˜4bjNûRrvš` ®G÷ü°Îü"6
+ZCÍt†§LB6˜—&-1ÿéÙ[0ß܉_¦ Ú •ãÜÙ J 
+·µÙNYV@¯Wœð*eÊ¿à‹o÷~-ñ†¬‘•OëÀ8t[4îsÕÀRÄ`ßš)Ɔ š´l2¾Õ3(to@×ËÆÿ¸ ‹
+x–Tª7®ŸJ¶ü±
+«¿õ¦§„ãÊ›!ugŽ+—‹0|P‹_ÅÝk¢ iX‰ôTR¡a‰7„¿³ Q:ÆŠ"žo3qÒðb딂=Cfhµ•0jàŸ ÃrY·˜$ýèæRSÊVƒùk«ZGê’|³¸†4té[E{TsÒ:ï¯ÇB²ÐÌ?* û2me!ƒÖ@F°;)(ûã´²0DÓlVÂCul(Ôã«ð·eðÐÅy°?)7XPÅwzÒD—n¥E2¥§]~h.‰y„ýrv±!؉Â8´ïu¨SóØýâGÕ@6nõRâíÄ ‹–ùÝïëÐñ†Á€¦— Â&¸Àtïÿ±HìY•ö´õ•ÿÓxJ¼ÐÁÕ…
++yÅböc·¬‚7@?]šÞVÌß@—„y{/¾›‰ŸviÇ7¹Ÿ8[‡5yU’æôk É\'ëÃ¥„&~€w kôVÄ3C‚EvcÔî먟( &ÕM,F®‰IFò­úê’‘=F@~%Ls!‘‰ L_$L°H¢{Gy?p»”¦jÿÀ¿ùçoßS¿‹TeK$pÌ/Ô]ê´Rö©gxÁÁ œ Çß Þ
+üU]áë DEbB°X-@ö®­Šô !×w´†#\DrK‡ì§ùø©nAJ䃥••¤N Å5XOf¹ŒKUa,v_^<(ª €Â…… #1 ÄI´’àŠð„ÖO£6=ƒ>n@¼‰„—í  œɶ VÙi¨_¶{GµÑX˜…=Äé1nu;BV¦2_$Φ3„iûáA;cr9þvˆÇEh¡g)´@üÃz/Oh<*œúâ@¯L"•xþ@(Vôí©±QäbÜÕ,&ÃS¦X­Wá/¸b’í¹&†#sJ¹ží±†bŒ’k߬6}š—ÕN
+á’ÁgHÄ}J1C«çarñä—Ë+æóð’™ÏbPP3´]ÌkCÍ!d¬§°n#yÊb¯×¦‹±˜Ï8®3ÓÒÈííDÁ íûUs±|cñÒ me§ïÚ¨)TÚ9ꟑÎjØbÓŽJ~Ÿ¹ÌY%8(o'ºsISÖ9%¶´8Ãýz—ûà1©TÌýœûØ ll…ÐTv^ë‰ÝN?ñð‚äwŠ¤ôHÅü „’Ójä"úJËËÇáíÒîÖ¯Ã@ê‘ü;™Mh¢x~”‹R–­ú‚PzCºÊÖOì©@ó4mžAÚÓ¤ˆ†'h¹¹C9€Ø®ÚŽâ~Y‡©ú(Œ¶Õ‚ ¥”éÕ†P½ðäa辶´pµû>/ ‡Ýúù•<ý)ŽÊu aYÊÔRÍ3þ<‚h» 4„o,­Ä
+^ÂäöãÍ·í†é®R•ùë@òFðÖkH,D¶7âFÃý3!iJPl‚õvçìqrO‚°SxÙSð£¦õÞîüù;ìðÀ“ri¸CCv‡¤Å!x?Ní:2ûPña‰Ú{¼Cä­(¤5—ñWÃC×›$)GÇ.†yº5W¦íÆ„‹Ú7IÒà/ÙÏ|ݘáR†åBq!02eh ¹¹JAtG]Çi{ˆº`>½Y-wʉ
+©›iº«O9op³L?•vyÉH/ Ù‘Ö[vÜÍ ô2€§ã¾
+5 ,v~îÝ™lê,®d}'À}e«´'‹_qÉ…Œézaʉ ©
+>¨ mÀš *“J?
+Ö
+šLQQ,Qo!/ˆœ6…`Ö×Q‰âK ÉÉÐÉ#±Ž±ê‘E•éˆ3žÛIóÂÌUBçá‘TéTå#Af³#vNfLæ+÷AÏ L%E†Ýµï“X^È)j»IF ¬2 øR¶av‘¾æŠŒ3†<¼›©ÞåÆƘ« ¦XÚˆxÇI* Ã!Œ(³4X7Ãe›”Ø‹¦¿†ÁÏ°Ø[" \
+—di`VõÏ(µ A˜ÔèªÍ*9šF{ÎÚµmúI/øCÍkuÉâoï¼¾\ªý¨É€˜ó™|-´¤•ÂBà¸G*TU;J©†Ì$ˆEfaÚê×EÈ@dnŽ Ê6¢+æí ^@¸;뼂W 6††ðKk nɨma‘]º©_O µ$ƒãò]k`_Þ‚q¥Á¤»bq?¾ÍÅÍ4† zJ<‰::$Š~¨Ñ I@˜xŽQÑ`Uå
+†Êï¯âèiÇ^5Óâ>Â7‹ºè…:Òœ¨þ58ïa% Úµð>lJM„3­a‰Y¹ö'ѯ‰ÝÅßD‚Ás7qÌŸ=•œNô]¾ˆD刷°g?üõ;œõÂZóÍ]”
+„A$ò7fðºþJæ¥QøÊ°_Ýá-ïdî·¿+“(¶C!”]ïñzÔ“·
+Üî;ÑDŠ¢šíÊx¡|rÊÇEXÞå®™e°`‚@ZK‘ðÒ±lA^´©™’ö™ •Zü6¬K®÷V¿c˜ðötÜÆUbJËšŽŒ™§ò+¸(k×Q,U£•-ûm
++G|vßRûäÅàç±ü&—puó‘‰"—Ƨ˜éî²5—’ÈBpHšbO›Cd*ð|è9û¡×ï°nñyIŽ__–E ÁâÀú£ZÑWXMZ­ó$,ÒÇC\Ñ!øÉž÷€Á vº5úxeúñ ú?ÞN ì
+ÑBʽà9SÞ/gÌc£Éí87Ǽ1Þ¿ïQÚáŠóâéë²Ñ(î’ *;þ*h%BBuÌ/ ÷ 'ãØob
+¶.bîpªîÈ2cK Ùâ1î£Þ5°7kp§ ;y5&,ß D¦±}†ƒHè¡ 8á¢ÉµVs=@æQíËòMâÛïÀE
+¸&¦‡bŒf_þg²¨=ÔfôÇÛ4¤àf}1§iËà²xœ—Gü•Ñd
+¥a3Ž»ï0eazß°{YE4– ØÕO2)v•C¤ò£X7l&è
+U ùTlPï¦ÐJ©á ªœóR™
+¦€çÍœÙv±«Ò6Ǻ¡´?•|
+ëÎç¤Ú4”Å–raö’öÊ”>$S]mú"*X†‡—ðt¿‰ß‚ÊܛÛÖ2édMvþIϬ¯Ü!\gñd¨¨´Bˆz'I» Î
++aïù ÷¨$V5šŸo÷Ù höÕß¿ìK_Þ‘âïƒD¹Œ%©ò«QÙ!ˆŒÕÇ‚Xû&F]Ã:§Õ†ÑÐgå @Ý!´TÕÆi‡´ÕÏCDðz§  XϪ'[g|ðëa4Ž´‰í$GDÅ÷æ@HQÑ㌾bK,¡B£4}¢¦R [ÇZ=Ú‹‚Ù¸à˜ N¾b0ð5µ™#¤LNM&4[\…tD¿ ¯âœŒp4ßJÉÈ <cjÁjÁnáf²†Z•L;„隷ÈCˆËDÊ=;m¹©££Ud¹C0ȶ`i3C”ÖAÔŒt÷„;µ„·ø÷º*fGÊ>½:Ðj¢‚¬QÍŃ)”$y‰§TIŒë1ƒ¶ ¢µÚMÒ ™ÿ¤öMÑÌ^Ë~LéÂ^¶PÆuŸ
+ë Æð_‡*HMàÜK£·Çé$§d[ðÖéë%èÛ>GOÇŽPÌ´1‰"p—x ©ë‚$G æçÛ)è0žub¹Mð‹1ÿ>(ñ~˜rO†Ÿ{Ðo×ý*Õ^ÊOúbпOAè…Л/^
+£?ê-§Ê£Ï0" -Dº«<^D›ÄT9%vO¸hG£Mu+RÇéA$ ȶ*tÿà“ñð@;½.º]¥àòSRB~ÂÉù›.A_ têÀÔXyï¼·.g¡á™8Æøû#ÉüDâCc¡_Š fY!P<ÍÊÒ-¢‹çj™/?‚X±Ïæ嵂@mŠVk·›(^^+$ûÞ>‡Í4u@±spú(w*sñ$!¥GÄŠ‡ü'À
+H‰Œ—KÒ7 „Oà;øQ‘x‘\{ë[¸*«äþÛ|‚ãD¥ügãH-Ä£»Ùú×?Dâ5\zÓ¾Lç×_ºú|©ÉïÚbC¦5wŸKšø× ÑgïÒF÷>6dõÖ-F_:/Èx­:cZŸÝ¦víÃv¤ñkõá­¯~Æ«5ŽÔ±øbísøÕÎ¹Ý ¢ÝekÍlC”ÏÉOç¢"õÞ½…¸z<“wÐ¥ù±­‚ô0‹%16„ÔÚÕ©çÞs¡5bŒ*^ÄÒæÒìáæÄÓ–Ëhú\¿x Gv ×1ºP¡º 4„ëW7C­›ç§ºÏÉKL[£ ƒS|J"üµdò%Ù´ ·h3Æ$¡Èwp[œ^3a2™ u©cÆhKݸS#“Vª7ׂˆ,z`¢éÍætW.k_¿Uù(M8IÅʯ/Í2Èø´él§:$ÌÈŒÑÞ}q À쪕2…èÖ)·Ø`ìgŽ wØ)‹ØË™6=5¦2Ry¾U‚­lØF¨ø&4¼š9¨œRcÓ ªÑsÜ‚Ÿ@3¤¯pf£VúÆ´1çNи¸ÑðjµÒAqå½ ×\Ëàd}èÔ½ «q%µJÇ”ÒQO#Ç}LVb1n^#…ÖÓÚ½!*åâM3ùtÌÕï6©¹|JæêSN±ÎÏw `‹Z÷Oµa¤éI®‡=Wx¼&Ã+¸÷üÔ)¸í‘©ÚÆc¿0Qt“Òè™ZsB{Qh#UH"3(ˆ&3ãÞô01œ<ë}Ö:{j pcëOŽý94%À8\,žžrM¿ýÃÎA–LÈbÐN’]Eßë %Œq­K—.·®³n+’Ÿk—´A­UèRèc €°çDiäWõ5¯èRÆ°W_zG‚’§t¾ñºT*׊žÞ³Æ“!bÒt°î©H{‚Äb†ßx=e[9·B1
+ÏÄŽßõkŒƒa»ŽQì:×R­W)Œ¿ ®Y< õ[á­B?$ùÛZœP´6’\ô€Xx…H°HR-ÇHà˜°Á90ç)ZRÙ°”鶚aëXLÆ1‡è!À·Èò 2¾å(ÚyL&þE딾z3ðþ$RoˆÚ^u’!ÐøtÌ nù­­“ Õ¼üM[1mKV6wb¡ãù“ÐÑ?¬1ûVÏË@›tx<zk ¸ ©˜ Ûæƒnb~N¨ð4R¬+ÊB]ÒK¿× ¼Ì¯ûœ,&c‚—*Èd‚ ã• ¾…ùe(·d2ZFE‰ÁØ 5˜™¶ËQCù€ü ª}ÎH“Œk“=öB®—‡d~d’*ë’¬öe\®=}ÀÜîfr äÚ<Àæñ‡&²¸Õ¿JcíäÕÀuWvëé‹ëÍ°àZ䎨|’º4@[£ 8$Þ&¤à•²§ß`PÎ3;3'ülD‘ƒMQ
+°z +&ð~:ž ñ$r²ÃýY¦+F«[1OÉfI¿ ¥òäò¨”a¥õ3ƒs–QJ—ÂFÜÈÜÓþRoë=µÒ_õõ‰N€pðJÛsŸ‚sa÷ ÍV,E
+gCK©^åY"?‹óßGEŠY¾îZ>iÆã£ÈâÑÉ~0Û·Ãy{VÓ¨4NóbµRùZeÀÕ¾àdè'•@=ëUÁï)z®pA„Ö²úÓLl3ÓÎUuÈ6™•Vù?4®k£2f'Ø%ÌKuŠMH?†óog/g’ZË'X»#ÁŸ\ƒÒKÕo’)’ [ÇÉÓ(²ÃŠÌc*<3fŸ\ÊS:蔎ïÀˆÀÙè¦ß›í§OPÅ,û‡f#9-5p«£š::ʸAaDJÏxL8BKülxùÌëMËF 2÷£#n–©s°Ê“lzž¼”ÒtCê•MJ8‚Ç{$l<bî?º•¥>¡Y¢@Ü眀ŸÓ+{óž ˆR~<"GÓ¦‘R|€¤ç¡›ð>žìÛ—?¿¤ÅxÉ`
+€¢WµÂ-½Q>ñƒ¿DKÙ3|úͤÿ}½Iõ÷NqB MŸAy_.Ìb\ô$<kG¹@ ª¡ÎƒÙümÌC0o/Ë!c9ùê·AOxö;ßFYÓÎ-´O˜Åœ³B؈êD&gyw>|BÄÅ4G±·'1Ž„
+Ä+4'·j²g86Rª
+’«`á­­ Ê …1Ö±!×û•0i¹O(Á×ä%6‚µqœ<%tîcÒ4 Á £ù0 ¯Í‚¬\Zƒ)zARN‡„>ŸH<)-Œƒ†S& ¹î;! ÎZÓgƒ*èe.<CÆ,òfÔ7HEJk
+ÛHÙ‹_ÎalÐén©7²„ûÁm¿Ào²äåä¶ùï=}îÀ÷Ó™jÔõ<ä%›2ºÏI¢F·ò¬‚`ÐK2ŒJ«v½ìÚ:åƒ_Û嬠¹o_žAë*0éAòÛ  À÷¢S,“Å[SŒý†h\Þ!?NÁ|#7ã ðóÚ¨)·e²,ý¼V¸9º0GO‰)P bÐC駂ü ¶ÐšzOÙ“eÛª.à°GˆøDB f21>ït»Ù¹‘Îs O­ô!¸Œ=|wrÞ~x"ÞM@€ðÃ5 èbx§&
+ÄmøJòyz@”Á•”¿ )Y/ÝHÏ-ÌwgT(Âàó'sã
+õ¶SOç rãR]?ÝŠ  qW˜ ·ÔôY¡RmÈ¿g»ì4<m5Ý/E·Ô¾|èÑj_ò)jrisAp-x¬°ÖaS‚Å°ù õ¦iØ$C-Öfìs [:ŠÚª\ètcü;6„‚C«lTmxäÓ8>åJÒçñpyâ “"œy¦µÙrÆÏ >nV¹\6"|tV¸ ¼xb'ŠÙo£¿‘ÏžŠUé9ëäÛú¦Pã%ر1´²¡Ö /O¹ŒÌ¯ZþÆÖŠ
+p+l°T°¶ÆÄkÃC§…¨•GIvʾaÇ? .‡géá<î8ÿk,¾oÿ±m(Á5¶!ëîN·SÝþãDÂÔ“©€t¶´bRù‡ò2É­#W¢è
+´¯@`ߌõ‡µ‹j$ïZç&#žÿË$•Wc_‘ÉhnSõÿôvÝ÷O¾É>ýqÖM®O×+9¾˜ð ô×ô¶b(_ÔÌT‘ã
+Ž^Êüª1¼d@ê’ Dx{ðGyÀ³™9Z…/+¡5mÞoèÀ¿‰Kd'¦i ƒÃ©U®Þ‰ò+„׳ z/Ù§™G„°[n1
+ˆ8jïJûsLE÷ã
+,·V¯-wLz™l\pq0)›TÊ0u½‘¹^DÿÀÏŒ¨(¶;Eü\jdzÛý€@JHIýòľÓT
+7EžÖ¬|Š$½jX»BVPäÉà z`…ŸÛmÂKI‹„kDûôÒG5wècÖÿx¬ŒVŠ)2ظ™Â.¢I‡âíÇD:Éå<‰×ûSaæ—O4ãÒu1éf
+´ƒ«p^´”™ÁHVûâW¬¥u¯MSÓŸ?K÷›Ø(.WE}D™ª†*ÃAVcl_,‡eˆ«ßhw톚à$£äƒÓsEd;»IÔÂ>ñÅ’bGœ%R°Î™R!f4”Ãl%ìJÖ¨‹sN3šäÀ.#”z;Ãoˆœà{Wx˵~ §ªAw­|{¸]UµÜ ‚?]ö§LPHi©Q_h«y§˜Z|dà°± Ì8rO5üƒ² ¾ Ÿ™Yç6êP`†°†=}¡²òGpÞÂ>T­ÍtHÅ ÀàÉ#Ë[³«0à,q¶I (ÛÁn¥}þ«à¡7gùõ±í"+Mç{ õ9ÚO1»´öÄÜÓšA"ȶ}ZÒÄ”~Ó hfðÜCZÂ?Ú€Sò!­a5Yb?¦5–fè¥Áû°¢24­2€‡´DŠ£0+§´ÆšõcŸÖ€6¬Òü‹ï)+^‡iY«dOK»s®ZÈf©=£ØBh7;K\|Œy#"©¿4æFÇÏ9Ž+^rpoÃdžG·Â9½m!•ò5%Iº•^"sÝÆØ\л?Fý9Ä×<ŲØï-h³;Ðm')DÞ8¨ÇØî„ ¨4?FËa'bø£lméÛç–ÑÊ÷ÄoŽ KÖúa'¢øØ Fu¿‚p*–¤É0ï–V‚c’gÞÇJÂ`XYô´] Aâ¼JRn£ „ çK˜ŒQO¬/9*VrúËÆd´Rº¼œ­  /ïZÒskì&*ÉMYVÚª‡+ ……ïcti‹SeŠXRÎÍ9ZchêÔÛÍŠU£øMAõTõÑ*—n¿ìÜ¡†EóÏ­xîÁ°Qsû”óe-kÞ¨ŒßC«±ÐHŸ˜ 6™NÃÚð%íúÉ…PÄ)yÒ~‡wD½„3 £ƒëWj½c6J•ë§\.ê_D ?QàÌ‹e^×òn@6 ¸ìæÂDÙî˜Ç8AÓ÷
+‹ ¼Ld‰pÄáWq5)Ÿæ‘x9t|²sô·ŒjT!¶/¦Y{–áÍ׆áÇ“u£_EÈÁƒ_é‹}W&j%µÌÌ!hˆfßl €¤öquæw‹•Hn_Y\x:û&OÕ}Ø öÎÁý,‚ÆÒðmÓˆ‰i˜ú3–|úMò°„Ñ伤\„ŠAC+¬€`ŒPoÆÄ©”)¤1ÈÏšˆä6y~Óã ¨
+ÿR^&×rÝ:À9(-6
+‚2Fþb¬÷Ò ™K‘›ÈZ¡Ðø$õ0!èeÔ:'øŠDD
+AÝacEÿ#ŸT(OÒËFPä‚zr‹}×q2%Li·#:8£ü”šu(²™f'ëÄ ü‚ý ,AÌØ;2€ŽXª³Ná}
+À÷¦E.…|×µ{ ‰ýF¹ Ø»=É·s,! PFyܯRNô‘“‹==)ë3DæßK“ù…ÅBûiÔ{3ƒ:
+êbéKZ}P2Яàö¹RŒ,ó;c¦©Ûýœ SIþqŸW*”k¶íÛL¬„®Ãf¨ O
+¯žù<‡gbSÈ·‘säM}÷\Ü@‡‘Ù}žSäÖ éÌŸLXŠÎÚé\Éh¡ŽG½®°}ÛÀžgÞC.:ò3_šÉÃ3ê+•¡ƒšL¢ðC0P7`'ºmù[H´ŠýE01]Ç›½Ÿ£n:ªp ^埸m“
+»á@[ÀOÉñ·ã•ÉzjÔcžÀ°bPªìÍ·Û‹©8ßÀÖÖ¡–¬¨rÄ<ûíÜ5k´3Ãð>·çàq@_DLÿóÛb)Òh |ѸBÐ*{¶c@*“ÃQžk€·›ÒaT-^>`˜¤z
+Z#µæ„ÿ?‚˜;;·ÒÿØéPG()ñ®9¨S}T3?BÒÙ¾óV€Ÿ_©ÒUÌŒnsH‚px(ægÐç¥2ðBVͶ†ýJÌ¥JŸÉ¾ôѹŒäCucþêv[_‰¹”ò?k´Jùí/…âI‡d‘ b‚p8ˆ\þÆ¿ý½ƒ Ú¹LܸÄìž“?ŠP­b.Q±y‡˜Ä)|Ñ}"AèQ©XŸ‡X;[Ššf¿Ë>‡mo<2-ø8JÃøÆmG`3ÖP]ŽLÐSAóÐØ™O&D‘˜ëý0Öû˜ØŸ¹ÏÁÃNM@*ñ*%ÀN5ÁîYHM¢Ô.²d¥Ê˜Gˆ
+ƒ+˜;þµ‡ßØYÜ‘Q°ˆƒÉiÜ(üçCÀ¯y!­Åò—q¬…SP´ª*T#„GËÏÕ#í><Œ‚°>M’1ÇÓ51Îè|¤Zí¤~°Ðn¢1è;DR™Y+ÃýAµ)ˆ%QEk=MGŽ"šFÛj”¤ Šâ±~F n>¤›˜9É
+dCóýd¢gl
+O–ŠaO[“VM:gPÞü[¡uúÄÏj®)FúЗ(žÞ0·q´Ð„ÌÀÙ‰p¨MV¦÷y7:C\Õÿc¼L²äÈq zÝA'Ðã<¬³–uŽ^©î¿­o$à©p§×k­‘@ÀhØ[j*d?~hMbÒJD­t}#D›Íš¡Ìœ$]ûÀ Š¼o‘6„
+‹ÐØx”sȵG¿ø¤Zô<ïeg®sXíÍBºby³ ¡H“ãSŽaœÔE— tIv)T
+¥´,å÷¾åAA0°!sï¦ýž+Â÷œ3{óiè´;u០ÔÉ´ R¨'7§ŸµU§/©)‘¡¹ê=×Áа™œ‚›|O&°RÁ´!ü׈Óq™þ3¬ØNˆ
+  `®/Ïùõã:y¼)·J–±ÿ7è”ä {’£¤¥uùœä€ôe8ºÆú%ÉÂfå*jIr@H «Ò9È@*è:ã5É1Z0‚ÉUÜÝÞ’jŸ°Íï}Or@hw¬nÊÁCÚ#É1ê4½K¿{0º%9 $ ff\éêÀ@DT°)¯¼Ùè3–Yƒµ2™GÏ?BÚF¨¸ôônæÍâ¯_scäG# ¢Ò0õüØ›Fs³ŽÞŽJñ8,]ƒ¦]:sÝÚØtæ¦Á^ÿßøó/AWöBU)ÈžJé!0<Á4þügƒÈ‚•-§,co~X>âΨÇâ©Ó´ Ü£Oª{źEEEj¼C¦’V‹V0— +ƒ+’í0 7)ËЖj È†Ý ªÝôJ^£É ·f[¹3Ô¡›;‚3àGÌ©ÝŠI¢ï gãæ åY[ݲT• p¹ó¹vR6“ÖÍ׆p”,·”©„‰7¦~yCp.°*íÁ}+.$m2m „ÐК]W϶NÂÙäŽÀؽZ¹–›íBb‘ö†¥][}‚ÕŸ D;Ù2•)à?¥[¥À¤«„j »7"ÏóR@ƒ T"WFjä«~X- VÀYl$Í“àÛêö_š8µ&6XGó;÷ Ò‰$tNçîˆ5rT«w~]C®/èÁiÍUyZßÜZ¿XÖ9Vß ý¦-
+Ôl|<ÖÖÖA¤¡èôµ—áÀ_4ŠADÿEV:Ù"‘Ò…%ñßw¢X¦ñuh"úVõê!ðµ@èÁÚ\E
+=ËrX›C Ú&ØD6»R"LÞ|#NÁŠg:‹¬‡fÝa~0…¤¸.Ù¨ó‚qÀ…NÍ⼶úOÆ9…MN²m(¢•mð»È¸&YTª¿ÉI Ä
+hÂT¹iÚ{
+k€6üðF`p´ÊñMpY¼ŽK
+uD{·¤Ègù¯)§ƒ~0v˜Åv˜f2pz_¤Ò#݃“1èˆ×z^S4t“zÆáöuðLøCLd<R)x7­óðÜ_ǽÙà¿_@mÅ>¢“ä¦àª-ÌÓ¬3V# d#’UÌ;¡Â”"ìN+ª¼Ö\pWg‰çå xYhs4̓ñ_ &L~w–å¦æÊGc‘à¢Ó)ÇEv;Úlp«P%¾—²úVx§G6ùgCHZDLì‡AX„ÂÃmwåRÙáŸXÜÂ>¼2 ê {ÒRv¨óªŠÙ‰éµœŽy-ssÓT'Q¡…äõû€ØNôÛT(ÙÛ:YóÄѧšÇagï0q¹ŒûáNŒo Ù—©²ÓüƒQö:ÏŽQÄ‹ùµÜÍ€DŽ`ŒßàýƒL¤±Ä¾÷î[ݼ…œc]"E{)—ÄK>óÈ‚ÃÄåŽFñ-Ø„«ï-ø……ƒ@þ<y!•¼˜ª©ÿ 2äFˆÑ^ŠîÞ øåËÈ oÉîZë01¿Ây¢ÈÊ&Š|¦±ã(Ãkƒæer«A¸SæD!”·ç„%‹~¥ù]”1¤èô$Œ;÷:jœ"b,}C¸bÂr4ë ÁcCájõ´!ʹöêa^¾eš·±^ b-üÅÝ^GÒ5–‚ÙixYâ)N{8ÙÞܸ´?³
+ -œ gCô\m‚Ojšè8rYþS¶i#p“çk» Å*‘ê Û ÁEÀ°>„
+Mm˜10‡‡Ïãe—?{ýŒøàý¯'†€K Ùy÷è„»M+ÃȳÆãf@
+JqX„’gº âgH§A`è Ãàv^þxˆÙ!¸«ž¨^URºÎƒIƒƒàI›þB~l±îZ)ÇÐé„;ÄÀ›+!¦Xøÿ
+ŽnJŠ²ìúIˆmE¿¢ÓD¢ÙžÖá…E]SçO̘"ŸŒ±&aöäú{ʇXe[ÅÌHZ»‚±¯*:‚ö:lgÊ­—sfª«TÊ£X5W<¥½P«ìôÆ«äxöz¹÷•VBK ¥µ;449ss©¼rBä~Jœ\‰žæH¹"S,;iIÁæÉŠrßÙró³!TlgÎl% )Vp£K0Øv˜›×<=2Š¦
+µéã¹®­/§m%—ÛaÊ>Óa®¶“D–È-´Xyš1j ÛÏ­óâï€ÀГ‹)þQÞÙíù|û­Íâ‡<W‘RÇîCœäÏiòÑßÿ:‚”ä†2D_vãëÄßW'¼¶Ú0âfÓ¤<80wp 5D•$ šýª±Éùä'× ½=ßÁn“#®Òü¤½ŽsVrŽõœUÂá â…z<@l+&M¥ÐíZÜ°¯Še×V´v[¼¼Ù)ÀÛô|Qk9ض—²UÑrfAh—„·GÛ^G^M†nz¬„^hÂÔN•*Ã"4Êaï‘ƨ2PvBr(0,å•N‘‚&;DT¢T ‚Ó„|k—bIXE¼ M/Èà
+¨{þ~]‹öBúºo5°ªô/]a¥b x¹Ð%݆ÈZ3¥XÉåÎh-JOŸ uUÇ™+ö~¾@·VþS `â •ÕZú1è»üzÂ|ËOGHÄÍàÅp`²ÿGÈ=i@wûº kÓpÃ’‰°Î‡è„€7Üþ #¾ÕK ÄûÃ:òÁØlЮè "»!Egš·bšqÒÏÖž¶ªJ!T ?ð)P¹´£ð^Þp}îTpq
+…TE¨ZvÁY‘vfH J/v
+àRmq†¿Ö ‚×Ð4¯BÜ‚€ݦoUeR¡Öœ60¶¶ëæv`è†u¨£WA¹NeµÙ ŠdPÄR^;}5t)ÃóCîÑÌ»h©ìPÒ`8S/¨¦Ø´­©/²9Ktº‹Î'ïz=Ö“ ,!f>+5.ï…jª7ÕCþ€c'>?Ûi¦*À3 ½ß ¿v÷…%â.{X‡o>ÂðPK!±§uðÕx?5:–€±¢2“í$ ^ágèÍnÞù
+@PÖÍša1ÿ TV¬S‹O4H’ƒ„0†½°j‰#
+úµV”ló ÚsñïF'åº W ÞRŒ¡Zk±.T‚Yó©„€z@{Ö}ü*8Ì RÔ¾hjä[Ý(s óU.=ëòÝ%&³{Ê5™F¡ãâ‘Itk‡zGöÖd£°MÈ ¤¸Fù" w/ eÃÑ b5Ò€{ç$âE»»DXCÒjw^¡ßp<u;µ«µÄ‚s\´"A ¤ªfuB“ ÂTÍ{4Ì
+I2€˜—šO³z]g2gHûLߊuKiYži#´=‰ ´KõªUH’ÖµÓü(â=¬’¼Ê.Æ¥" ÓâÅüh<ÃW^ýô÷o„ÿùö
+ÁpbB¤ ;šÞ!XZì/ IÕxØ
+Üšï‡w¥ F{*~#‚ uO-‚Š©ŽõÖRjãG–Hn%Þ¡¯}þž~掙™G¯þ.“»jH†-…N²Ð1Jl©Ä"d üÓw"¦˜"ůf¥o™aÛ ·2ä4­¶Cˆ2áä°xŒ-Y;Ãuša£,©hÓÃØ="­ô`7+Øl*ŒÒ^ØSv¦?Õ'¥z(2ÒÕká²ñÄchpæVO¤^FÍ¥d—Š*¢gd*1Óð“›³FÍÂPÑÔ´tÜ™>97­ÌÄjÆa0[÷äbýÇä@CRl/7°<_qZÐÊéƒ Î\‡Î²>[*êÝPT(à0UÛ)ù{Qç<MCW@ EÐynœ7š£ Ð딌#p<`
+ËéPòóÇo-–• 0–D;f6Ù)Ä~|‹nAie a‘Móùcš:Ý–ûú›=ýÒÖ?þ¡´óš9FMëX
+ 2»„në‚üæ#ؤùØÅPWÛRÈOÙµ-AR¨Zöªø6èc£Rz‚#êK9N%4¶SuªCQ%r°%ýv*`uðFy— ß‘ä*æ8^ ÀyzÔ`]o…„3Ѐb<⫺& ÅÒÝ•à}Œ{î缕X²:Þr¬|ÿŽ¦J€Þy¸^tƒVjÃÇ
+FcBuœaÂ0bF–£_L謃WlÉOEëðYnÀ„à6äÚÃ;`Uí¨
+L4­þ7V7ÍiëÍe94¡}ç¦\¼ÔQ$!¡¶£VzˆŠ«%\ºô*¤ô
+ !•0ß&:_´€öh
+}«$qˆF¸™ÕJ´Õ=PiÛ% Ng{xá´Ä‚7—ø™Ù`bCšG³&ûŠµâHtÍÇ*ÑÁ™þ1P¢Hz¯±]‚’PhW~ 6Dm(ÏŸ|%z²Ç¬V5Ú`#ð²°¿Çý‰%”… X ÿ×âwh¡•TqFg‡Éu5
+b@×…—ºdõ3IÆw“Ð"ÆžôÕStˆPÙŠ°qš—fîÃ…™û Äž¯í`X¼BÜqAªÒø¿´n‡^ÒíSQÅž“AÂpbEë4sÖ“%M`‰^£•qÝÅ°w‹åPâ6ËE‘A4ðOßIB×BG6;¸’¨däåá”…Í!t‡ýºòý†œïúLhz«¼Ñ[ª’„ÔKò¯pdêD[©ê«K-UY*
+ÌÌ\è@8¬Ò4äÓŠ¸ßª¼
+šð DDþcdžî‘èžtˆ£ðô­¸hlå»ð°:º!Z«GM@Æöí£â£Á3ä+4¿—¢À©è)Ž%ÌCÍ·§’&4ÛŒô¸R]rGfÃ/#äöqó¶Ê+6¦³œÒ
+Ö‚8þ"kM—iNBPÃH$Á`Ap·†9NH ÉۦϱaZ[lcK-PKvagè#Di;®‘hÉ©Uà ¸öõJÞùö­ˆËÊ:@$GÄùqÚŒ<@ítMy8ì­ç'=9}©&ÌZVm9ƒ˜A ØbÛŸÁ6‹–NÛ†=¼jŒ‘;wËhäš·Žf:‹áÐáŠr!C ´)0±wœE|Z6˜½ýÔ0l’†/»„$…– ’¨° vÈÓ¥à%\\Ô˜exà¡!'Õ¥]TÈ.ÌxÖPP‘‚6]lËxÀ@`­Î\Ò®ÜîÑÐ#·ÍìôX&D’P
+¶ßR1+ aÖ^ÊÄb|!Þ °ëÜúx‘­04†¨x8h É úRÓ‹ù¶ZŽ’tº=Æ,#á´fÍv5Š~”æ½ @$–•QZÅT®bœH¨G‹æµéD–ë—o.R¢uZ¶ÃáŠ|Î4=-´±uV‘a:¬#ÞþÄè­ÐÕø?s+úz*Ò2™[=z ûáºÕ÷:þS ÓT*=B÷_Ž‡m’_pO?8)ÛÉi€Æçn0öf%]!‡adæS V$£W®ü…<ãÙ±û;`ó šç–=ñ ¶hÇÙKrdZº¿<”TÔ‘žñk¥T16vmÀÅ”Œ´„6Úwt È<­j%‘¿ïL§›&0+0ªþà/£»^S
+®“¿Öp_‚ É€ìîëXŽò/œ7(¾,5&¡T#h1$œ1åÒ»„aOŠ6©Ô¢ˆ1Ÿ€³ýäp N€D:ÖÂè|…<‘ý…ô­ ËB^âFš]¢Æ,±Û qIâáKEYY,Jv–KiOžSñæ™5«×€´K’&Âc;>t.‘—Êa%¼@Á¥«“&QÝ•w ò]¥8Ù˜BB
+wàÉu&J0êÉ:Íz=,\¶CÉÏ¿Ä´^Â6xf€C”° `Áí$#râï]$IVäjÛ±(‚?š¾ˆ<î§/)lè/û¡h•ÀxZ&®N/A›ÅBaC{ü„*¢BÛÜO¶ 
+ h:ãÅϵÞ1Eß4Èj\Óî²ø©g¥Ëé[Šxƶ³Õ2*1„. œvª"g£ÛDÄ”¦Ë(VC"Ëtt­ÓV¢W‚\lÛLÕ º€Lß%™ßИ˜ÍØ%t:åÇ!ŠøtìÔìŒƤ®£ÛŽååUÄi+)€¥ ›í\ñ“÷šh‰c&û}Œ‹Z!ˆ:uê‰}è\3„¤yà™!u^ö…*ÊÄ’KL12Þdºu{5/ 9JÊ[µ`‘Wf’P‘m7‹–¸®áqç¥Ä–"À¢€øø!Ð Á·’ÞÄ,„ý”o}N ŸÀãI.&¸éSÂ%Š¯¾;¶(XÛzÇ ¥VA×! Ï°K’˜;’û, bn\ÂýHéS”ÄsÕ߉ØÊ}IxöW2:ÏC0+ôè±´•{ßÃxCÊ9 KÉš
+Ñî
+y€Eó•øF¥­Yé+$\! ´ ¿É-RâU{(A‡1#h\3øïßÈÍ/”)le¢ÅhYz—€W¶2å^¹
+Ì,«Í¡«P®ûò²IÒã6‚è tŸ`ÿ?kzÉ[8Â+êþ[½ªšê¯ÑÖX”©q€ª2_Ñþ~{…û¤ä#Ôm5Q2¹bMeÝ…Ù1\JíäÕ²~}-àaŒä)CËùOeÏ@‚²2¶‚My^¿.¸“  ®h½¥q†*c`tƒ£$H©I…`éxïš5$³lLR ÀÀg-
+:Àªoùà>.IQùr~Âè2í»'#D¾ xœ†=ü‹Ý
+¼Ï÷Öx´R$=´´Î3ó-ŠÜd€š2Zô,jk¬€Œ¹ûMQen<èRÍ4h 6'ê–(ƒù šº»¯PŠ>ä"·ˆ§´äƒs\f9y…¡ ›°5ÎBÚŸÖ Ò‚3®F JZuCòFn¤Ù<ñˆŸaÖç'¼»‹¿iëa_E·r™çØQƒ„þf'Ú4·œ,®!ÅB¢Š½å0^YðePxú|IIP‚µz`Úê+_†"‡é™-÷(¬ªŠôû»I¬¼¸¼Ž'‚óãm‹/ÂòÃ8†òbAä—ë³ß„(kÌÁ‚º­7ô–bP0¨Ìu—°$ó_0 º€»&c Ù_êï3câ.‘²z–ŽíYÓP›%D3~j"0ÛÒ›.>R"¨!êÈõ"*|Ž:Éixii> \ à<zg!
+=t¼e ÎTËX½ÙCQ¼´TÃrH‹fn¤*¶ÏpQ óë œjáwp3R@Œ»àɳ_ ™ÏÛQŽÕh”»aNóí5±É
+kË6}ò/à!`¸–*Ü‘ ºƒ‘Q‰*¨vPÄ/>Ée|
+›§¸¸ôëŽÃ€÷É
+ª`‹š>Q7¡šÝ0
+†ÃMÄûPžÄ¼  i2V2< ý¬¸™þ?N"h€86_Zw>D°-×€á0`ÔxÜLCíØLazD(
+16Æ:GÞç2»T…d¿^_J“âjÛ«ix~+0]Î jeóMyŸš—{Ù0ÿp‡Ðªpª^Qfd ±ó
+xý™>ÀDq;}ß½²ÀËŽIlrþPx#Ü«ü9!Á¼AXZÑwbÓª9/ØM®šóè‰m¥›¾WçuÒ—^’Ìáeš_0ÅJlÒÁFQxZ"³¨¸q§ÌBõ[Qy T™ªÕÈ@iïuȨ…¦—ú笔öC‘[}Ø[®±”â"¦²ƒ1ÌçBî“È™wpCªw9¡â[9Ðàœ2'?í$;®Lœ¢¥aéŒ,Én‡÷Çš"å…C›èjÚÎèpMES¢ÕKäØ«Øa>ó|Éלڊ7çúlÍ–ÓØo&Ñ"ßžû¡«6@ò7" t™oÝ@´Æ®Ô¡­Ú¹tX‡H wŠmUô wكهäÇÿe³ÆO9Œ^ã< üŸñ:8PÀaó5ü]ôó(RÏ•WÆðDZ(EÔó?é]Äôˆ½Ñ…’Ptà
+ŸÏÀH˜›dRó\5cgý¶è´Ùü©Š¦¤~QÊ?‹NðØî[¢JIDʈaÚ«ƒŽ" ’2®Ž>IfS‰p½Ç£¤­
+Ð É;³%"#g VLy ±–IÑåSA¥@ $mb‰ÒB:
+F7ûªñ¥@Š)®ABjÙsN'V±­¸©¢nïëtÝ&`ª$• %,ÐC1#~ –3¡ÎûŸ]ƒbUÜ‚_œGºZeŠCe–¯C·ã²ØMK[’y:,°LŒû‰ºæmó'ÑÍì~m4$bÇÃID„-OkHÐyx'8Žç¨GXÑW)”„8nóÑýL°ˆK×:w‘O!ãs\rG$Ì‹®.h|˜¯«‰€†kŠèÅ™„˜#¨ãqdUN§È ÉúAb;•‰W2’ÄzË FT¤´=(<&J}PkeŽtÊ
+ï¼{[=—¡É©5ª‡;<xI×Iª ü†u:\‰{t;i¥©Ëìù¦ñœŠdÌ^†IrDµ¬Óò ¸øbR¡›«íz—«lF…c‚!]Æu ÅzÒF\D¶ó`SSË;Xo[œQAàcŒÑH/¶»|z’Ƥ`§!ð(Æ g’íDsDý–ç>Êd¥l!RVšÎ3²±O#é/Ý«´0}à‡IU^ÃPFtÉ-СDYáÀá°ÇdçÈlÂ.F¾´x¾àrŒK@Rx“P¹øA:µk§ÿÉ?†Tµ/ôŠi—Í° ADš X?{ اèçÚ®á(½ðŠo¬R18&QÚ†”xc DÔ[`ì¬a“ì·D'ƈÜTÏ!‘7Ûž¾%:]Ác»o‰ rÝAä$ù
+‚äïð‰!Qå6f £ø2DÁ|P'Úß $|ðT$ÁBt}älðÆ (pÙÆSWgÊ'ƒðN|3ÿO‰wᱫvY{ŽëðU”†¤¨'‰í¤m)­»ãÃPŸ­&VžÆ×ù€ª¼‰˜–ï¿@ȳ£ë¨yéAª³ó Ç¯B 喸ÔÞ_ II7<ÇÉôN!H˜EZÅ°Ò#… ÉÔƒÚ»„øB!ÏŒxp}SÊâ…qäIxêÑVpÆ:Wòƒå ê9bˆ$±¨°zäå‹ïêv^8D"L‹Gú±y%ÉY~Ia—#‡<ýÀ!¿Ê‚â\t祈¥/w9Iò€aR龬Óæ­ï8X ÎK6µ¼9ä âÈ‘Ê $ÿÜù*S$¿ÙŽ:þ?D·í~}çLvôÇZ´ŽZφ%1š¸ÞD?_D·&["înòQï o‰žÛ=¾ï[¢Ó”/Z¡‘ÏaÄï‹Nì÷¸ÌѯïÜøz˜ý[ÒDŸq‚Ä°¹­â
+PWFMØã<De¡ŸŒ!¯¦J
+ì•7c º–à/ÃÚ,‘.
+V猻¸ì*|3HD°ï”‡&ƒrâD”qú¹×á;µ`£gùRþ
+iÁKgKòEàÓpKc¯3€×É7)Õ·¤‘]I6Zó–ôRù,¦>$¶ Ùe}Z‡š+¹{? ŽÎÅýR‘)—üÃ1ûðbÍ¢5L«Îé;qëüC;FçýÞ„²aò»{à¦÷‘SζWÈq<Øa0ó‚ùÖ«/pô95¨T¾WÚCoJ¼-âÿƒÆ Xcý( W5ÑvÝ”š»Rõ&у/š²«¡ý{okÔj¾@×2wœº=ÕàÙZ’A{ª0t€s°á2(•“|Ê‹$é'#R׿Ÿê!ªƒš%%“Jgy… ù’åG*=ÇñÉ©¿è.—$9nˆ^E'˜ ~×smG÷ßêe(©»Ø‡C¶Ñ$ H䧢}¸4ÞÓîYñåfŒÙ"VüŸ=6*8Ó¡up½'
+<z1…J°ŠZÐm—È” =Øñ¾K®¯’˜úc.–æµ%‡ÖfÔ#£EÕa¦ÂÔäoÛÇ´\+‹[ üíû!ÊT4®¹E‚4s--Ö5—]Âx«a5Xä¸ Å`…Ô±kú•TIXà} Ö‘-ǃ./á"Zu Yu—ÐN¬@µ¹K`‚,ˆœò½'å*âBk?¥6yÔ¶ŸÍ<±Dg¤U`3ˆ# éïÚL!{âaâhÂ
+ÂÇÌÅFð‡QFqù@.Ð9c/£@q[­Í[è4@Ûü=¤ᔋö@‡j˜¯ø僃Í8*˜h"†K/ni€‰éí
+iÀ×àkåS·ÞóÑ §ÈúÖWaùñK•g‡ýH÷b&ýÅò›ÔÀùnIIzÂ5Dó%Okû(".*šÈ5¯_§ÇÈ“’
+
+;6¥:ÜùkWpyÁÍrnIRP3TgùÞA 5Ã×½5Ï]‡"ü­,·’î<žS¿`Lfy–ü¡ÑdŽ@‰„÷}¼ ý• Hò~ŽERþzfl´?MOÇŦ{Ô$å“N{ ¯×jæ: >²ÉHT|9ôÝM[Ǥs
+™Ô¢ƒ ŸÁtz›ò:6
+8ñ¤ÄÕÀŽ’%H϶—j~Iû›DÏî|r½ þÛA’þW€
+H‰l—QŽd· EW=ÔO
+lNvŒˆÇƒÁ‘Õý«N®y˜—Æ7dæN\¬î;'ö6„ Æ-ä@öÍj„Ñu»‘àyÅ÷ +¡V´Ý(c/[î´2; îw"J00›¿ “K2(ê–Cn
+f¨jG°TÙ€B>¢3Ä»EµEg
+Å$Áᕤ“òê€ùù.žªï
+µŒ_ìR“;m ¤°!’ªOmeóC^’aäÚ;ë€îìðóÜaÌáù.d_Ïb֙ð]8÷Öóà%è‹âÒï J»×ØKÈ2Â%r¥ÑEHµÛd~"&Ô¶“·TºiE#«¦Qɘ¢’E‡¤ð][«CûO]‚mYÖ æ­ëimQøÂØÈ1 vù1×oäÞ$Iq+au )¤4ØÔº™[×Ãèä?4Ù™±æVQ-¾ÃÏ4þ´Ùºj{7ƼÞ¹Ô]ÇØKFù—RÉcÀ½¨c˜‹¹HÒó©•:æU«îMðD†úôÉO A6K¢5!†z9Ðœ]lš­¢4é.뤆ÿHä÷g¼˜p 2œÊ ñ1 qÑ7ŒÁÕ¡ $£h†˜Çqÿ{ÁézÚQ+‚´t]ÿM‹6…´B¯() ‡…“B±ðê†wFö»Ý ÍGö%¯!ÒÝôœá®¤„P?R‰°Î‹ƒ•GA÷Ì"*¼d5è4 X>ƒm½Z&°À
+,8d…â"Âd ¨­çšÉ
+Uc[³¼!Xl\4Îû þ©õNéZ†Bä15žK¨“­±8X;m¨Û™òt:ïS½1HÏfðγwÿÊŽÆ´fØÛqF"“ÓfmZ¿É'ìÉ ä4“˜| ¸(Wj€ ZŠXa–’ÝJöTÒëSJ…£A±¤ñR˜GaÄÓOÞ
+\=4t²3?˯›Š¢¥—”âa¼di1$š´è¹]vx÷剘;¬[ÙáùZ ŒßØåÆ®S0V/!ð•wtë5¼H° ÝçÚpØdä!˜v0êST_/<æ¨z‹]­†¦Ñ±:`åÊ‹F¥‰C9È,æG‘»ûTœ­àíZ«Õ¨ÐM²P@UŒlðPŒT»óf´¢”Œ‹iIö >Üò„]ZóÖÃ7îa@Ÿy8´‚… – A€ü±Z¦†ÏAž{ŽÃ¾H6w@£é5ƒŸÍà… +[A“ŽØzê´8c
+Óêi2vÐHÍø‡‹sho¶4§Ð-4TÃ.Z¨#bó6ïR E[Ni™¶{i1¶ò\M‹†–tˆ€ÖUª
+’xÌÕÉ =P ®ã¶±ò¸3’äœ6ªÓð%"5³K#€¤4@ú,¸=†ú †ÛÓNmFZ°§„Ò}ê°Õ,ØÌçMѦ‰/“LQ0’9™íhŒbuÌé®F/ºN
+ì’}W%À:PbtÎ[i”’– b®m³‰Nɪ½‘CcWiè#¡üWцâ˜z»<Ý@ꣴ6O@iQc¡ãÿ¬—mT¹…Áü‡û%DaðËõõuò‰·$$³D-Qµ&L0Ýhh‚ø÷yNÙîº{–|ˆv‡»n¹\®:u!“g;I“ IÖ†¯îaÞâ£2mßÔ+ûn¢¨û{L¡Ðf'hÓô"-”®)µ%XAé
+}ÈuŸ–²ýIÈt-v*Ä7}ì…ᦖB#¸úªê¹RI"aŽ …n% V”^øXØ­›×É2)õÊŸZ¼}[IŠZÒCֶƯi[èF8ak0ÚÆÕ¼Þ‘,™ ;‘ÞðGˆl2Fƒž¡"x
+ñÖâ@+LùÔçzvÕÔÄÈP ½ e·‘˜YXÃ1·jE{\ŠÂ…¡B. f:·q¤ÏÂ!¨¢I¸BoÑ ”)£I
+£„–¦<̃Õ:°œÔö
+C/§ 3òž¼3Å r`‘„#èWÅÀeOX!B– ˜›h¬V1T«,÷²*@$ìž©œm 2œy}ºŒ ¦Ù£b}C0‘â¡BQ³GDO'irÇV·X%5•hÄÍÎgF
+}ŽÔðõä©a}‰@´YÐæƒ7Þ÷ñd†T ÁF¹Š¦ÎڌۢyU~ØæcƒŒpìŒáp/ÍõÃkÚÔTÌ -Eý3Ç(Â:¾ðâñ…΀YÔ^
+í$)(Y!p¨'l“¦N€©ŒŒ4Æpbƒ2­)€…>æJƒÍ`S±{½š‘/ÊbÆô áM *µ‰X<“1çj%L… †Š8¶¨5­4t(y<ᇣíàij,‚8/âHïÌš‘½ìÑ­D r°G´ÌS&MU<zj>t`’Ä3·qî X“ 4Hp”de´ªSpbï G€‹pÎ{J‚òÔ·¦! îÄov~ŒI±0?8‰;©$ù\c‚{ÐFIª€Œì n/. à"=c³ÈÕ
+n¹
+®Uò¨æöñ„ Ö)D½“8¥Ä‘>Ο2`‚A~Ȩ½É‘Q¦i;Ò0)Ñ‚¬£$^ž¸o†‚›„:.‘¦›Ý‡qgÅfy¢›x3AqÎí>Äš1’¨Ïj¥¹ŒSÐÜ09Ì€Ñ ¢¨ éž“f!Ñ+ "6 ND°Á‰Y©@˜¿.äzPJ*²D4cõ#é*?³é>,¦ImŸ¼SÛ•(Y€£Õµ%H¢ Æ~RÓc#è3‚œKu‘:P¿¨DcY8Z7[r‹Õ;°F+²€TL`X¨±th¥3«;ÍòÃÓVÀ1æXßš\z½5´Z±øf!q˜æc ëÐ2W(/â—GV`ÏyQ·MtÅì謢Î’¢,Dý¤J3U¥¼Ô—%ŒæÖ7¹£¬Š† ¬Âܬàï-q,±Z„,-YJ­R©¤ÉFQ„Q³i4Ázd0w¨e.ÖeÍ‘`BVzQW@`öU’&(hQbE[]° ~ ù@¯ß§v*
+nc_À¸ä†[‰$›4ž¬r€Ô¤ò¯sµI;š¶æüÈ
+Ô²¹j% 9дÆÜ`ŽN”k†E#¤ÀG¨†¼µRý\ÉÆøÄ’EŸ]-Lnp5Ó€³˜ˆ
+B'›Ž) <bIð6™
+mÌb÷LDU=Hë%Ñ…af#û很Óúµ6—8ŽwžGVŸ“3‰*úX-µw#Õ—IÝŒ³&u›wÖk¢f+'áνAœU™¡
+·k"¾gð×ÔrxZ¨ÔÙ5Öoй6ÆŠnÊYW+­Î¿Œe¸§µišõÔúïWÃõó sŽ¨1–‹J5“Z][ã
+ÓÕŽ
+™Ê Ä÷D™Še×[²Ï}ØrÑ[v­°\W€ï$ï— ÇÒË•Ö÷K<‚âfUŠ{ÅÁnðüt C*ý‚«†’ó.i—æïmQ[ípÀ²§{+Òü9ÄÒ˜%.’C5ØåJ’%œÄ,w+ëÊ`MU"™Š¦ÒYd*ê½@iC’<Ö8‚ZðEϲ=?±|$›–.+»é˜éÃâ Þ<~?,ÂÚœ|Êg8ãO„«òØúw” ÂH N)d~²x¯úÄæ Öm&Yø·žm‰.«?ÕEQC
+Õ§)¾ÃÞ›3_…”ÒË´•KÓ­ü%bm^é« óÕX?Õï:›JŽ³½Þ¼ÊÍÃþH%nPMœ¦èd÷öíÌ^.QW,ˆ›~Ù
+opôà†ÃTŒ£bv€Vgn={“Án<ƒÙ‹2ý"
+ÕNn^˜Á%2,Üýøó6¤–+ø+Ë𢵺֊„Å¢qZ5é V§@®mWU~ 0Ô2ÏÖÓ¼uó&ãp.½Í€Uhp•cšw}¿{ß_°@iÕhÊ*à§Ô°,Îò1NÌ[Ÿñ@—Ïîwã…Âz23`%õå3o®ãFñ«·x¤â²ŒgGsس+æË篼r:UGÜd%òJ|MÀ%:È¡`» ‰n‰×„G"¼P³8+]°Ü¸Üfp¥¦Eéº,µH­]tT«ÊSgÓÙÞòï5ƒdï:ªµëÂ5ßcÆÑR¶eÀ¯>noÀF5dfGÝâ¿cç nÇú8˜²¸¯ƒe Ìt#@› J¿íÄ–J©¨Ëßôƒù9§$¥ŸÃ© >æàivÛñ¼vS€Ú¥ýüìÌ‚S˜ƒÊd”.lÕÀR¾~ÿ=/ÒÑ
+#5 R¶â$ßûý[â7c‹d¬ã(rÓÔöo1@ö·`VʼníAx&mÀ2OË8X
+ÏÖàJ‰¢¶²"nñ÷<7°#¼z1)N•X§¦tho¤í…úÐs/i¬bj˜þ“k¢tŠ®+Í0GWËâÃTY+m=çOÚJH/hM‚}wLôxÈ =Y®œX*+z¾ï6±XT!Bpï± ^w€}(-/˜ÿôC0§¬ß/lÞ•
+ ×çrD†0Üži ÍìéuöVÖÇ:ãN«W\|¹²k›)@í“kìÚ Ùß[¹¸
+­ ÎýÍ.”ñßÚ?g¨»\’&lí÷½˜A\ê|/X:_=ÿ
+ãH^—šë—vÆX=„1~žik×;N4€15- F‚¡é•õm‹”Ó˜ÝdZ9º^<8$4I“Koû n$¦ú~;.2XÅ;
+rÖ^‡î±tXˆEæð åŠÚÅô'MŸN‹;RŒ^Ǩô°Ðªd9€£äY‚Y
+€`„?íðYÿ’ïÈAÏ]pt…Y°l2n{e;Ò™›u>D[­}¹¾5†Ia=kihEëia
+óõ
+0Áö^ɸÚ?¦ªùñYGµO©äUàÜÛþS^¦†þCr`µZ×õ2€
+K¼Q¢µ“ì½ÔË€dÝŸuÞ~èAÖý•X
+&x›Œ[:õD=n‚ßÄ^ë ÐMâ³ k—t“³h=gúnÌARLØ‚JùêàûÙš@VTë—Èx¬µ%û p'Áö4µ!x΋OŸ¬xZIå¤ûX\aô:ãl'Hgw•ë
+0"*
+hs{ήºÇÚÞT·*€ìKûgþ®º‚ßÒKX{¥\V%ÁçXïÚݵ¶ÄѯR8jX'î·}8«
+”¨IN5õÃÈ©Há54LÑBJ$VðQúù‡ðd=¡'aC(銠À«
+t/A—Tut0ÐÊ9÷¼b݃R¹É¨st p*¥Œê¯r
+„×K¤é’ Ùec’שּׂç¯8Ÿð”2mÊ¢æÖ]Hš»¯Ø”i£G½ßªzN‡øÞqX—›Mõìy~¹ÙÎÉ¿}+†-XŸÅÁ‘bW³ñÀÝ6v ˈ—ïœ<tõNžåU
+Óð@\ä\~jr<¯$Ê dÓž°þòðriº¶z.Pfù4ÙG€»É ÍÞ…YùÌp„ÿð{éOtúÐa—è¾uÄÌ‰à„­ŸCrŸ}
+†’ Oa»¨š¹Ë‘g1_ø û[dpÍ*¶{­­IEípÎõ«Ì/€?º–6œ—bÕ8zºtAªŽ®B
+0D_Ã{}/7³á¶º•›Øv…5ùËÕPÖ­EÕ>ˆãR0軀6ÕÍ üÆÍz‰ù‘êÆZÔø’y}TùV'íIi!¬cÙk¼®1Jì_ÔÞ¾
+ìIð\1^cñûè;‰SLÀqFTÐE‚ëˆöªÜÐjm6ïÖZo¥Ú{´[•™C±ïÕ ú¸Û;Jóv{¿u<_IÃüpó€9Ú†~<: ÐÊé68;°EFUòÔiš•8jÕž/Wˬ¯Þ^Rúüo7õ¬¹_†ܽtÕ.×~ƒE·æ
+pÅNÓ
+:“w8»-¸sèÅÂ[*À
+§‘ÇÜ SßɲÅZvFãB?w~Mê:lå¶fɸVw5éì&pœ/ÀhzHCó23èsߌ;W»E* µÁ hi-äÅzŠ¸Êfg
+)<ê¶/I~–³>ÞðšàEí3Äi’FÇØÅ
+t¥LÈ âqÄ 9g\°0,y–Ë/ð ó—{µ3Lõ]ÐÁXGMv%«·)¦Â«à>¾¤ßA´¼Ëöx¾%ÜIYR¼Ö&ØvcÞ¯?ü¾ou”õ–Oü-GŠî½÷•ëzî7tI@dR¤†ÂbOgçàÔ_7-ŸõùpE½›ÕŒ—Î,¾Ž&t®,ÊóÔçŸÑÆ8×Êâª÷êN—Þ¹'Í(
+PiÍ ¸“»1‚í¹IQÑÁ—³Äߦ¾x¡©Ð_¨‰ †À*è*3  ©QåÕ±€ÚJ+"ïb
+hƒ4“Ygoá
+”§ðQÒ›¼ç.¥£eÝ!˜V.6ê«Øç·w€=³YKå/<¸•Þ¯pÙÄ
+VÕFžûf+³tå÷HFQH««ù¹ ¶Bâš–A“—™·¾ :YÅÚA%Ò" JÌ5ÊBõUÃp%¹Ú¶ÊUn‘ŒÊ4Gõ(!gÖ-jÿ­Œ]µžUQÛÏd%OÁ$R1øŽ!¯¦¸oh7•ÁCÁqmÒàyÍ•(‰Ê¨åš¨\4¾•Ê¤Žù–…4 }«Ø …%Æïç4Rýy}l‰ÿQŸáÄmšµ6ëPÀ!æ7&˜õ-:°‰Ä„GvM)A€XŠÇáM0~Å@áQ€¡LÄq¿ÄaL”ÇTËžÓX•û
+= ’²Lû%Ù3,gâ-°xqËG£Pþ}Vâ˜N†Ï‚U<ŸµV:V lYåDå9Ç™ïä‚ñBØk*[­„yl´õ¡FŒÈ(0/“¬_ =ú óÎH/¢Ä ÁÑs:l*›Àà=¿žKN@ßj~é¬î&亲Cd rÖÃg-ì3>-‹\–ˆ^„•ÆªXyZl: ƒÉ1hLÀ
+“‹d£X5F Œx0QÇ{J†;@ï1Ê›ÆTßp
+̆1ÈV6n„wŽvR^¢°˜qÅ
+‰©ÊvP;‘5­âÊ
+ 7^ Ø:jERxíª¨E›!N¹+%Å|ÐâÕH5´Ëše%éh(3TÕ!$žgÐT͉âÕ1Qam™, ‚Äêóâ"·’FBƒe‡¬(³àT¶Ù,jŠØˆùèy®€’*<bbŠmë²Í”ÑKr²/0O"OŸÄº~™m€tŬ–
+òšòžÏz€F¾2Í–wqÞ-_mn4¬Aâú·yºàMÔ)g8ªò ÁËÎEtIGƒa¡:~—vDáÐö•Ïûº³ÜJ½xïŠ@Áu,®[R¶S[Ûã¤Óߊ°BìœX—Ý/’T,Í@aƒZ•ZÖÄÉ‘Eþs ›œ¸Ì«Ã˜”ÿ½Ô¼ðbsð'CŸ¶íØì\ž{íô™•£oά®ÎŸ^.àkóï/.Á/X^žéÏÏ5m
+!ÁS×€…ÁMZeþ
+Ö`ëØ…§†a\Ñ2Õ5±%80°Œ;÷÷@À-JCsPU-‘“oö`éÝ G
+iÌ"å3n+öT×½TÒ©¦S7¾£q…Ïz\õ ˆÆ-2V]£PU_ R¶—×UÍùeUWøNÏYüžür9:é§a³¸¸&o~õXÞx° k¡ÐËŒßG¥­ã4ªï#¢Ê6:®8J­2nK)jìÝYP4î,©/WQ‡|k^q®´bî¡c€Èg&!›´²®l.‡— »hsÎÖI¡
+…Š࿕ѳµˆä&!o¦ãã—‰¸¾»1ô–¢61ÝÕ¨SšªlÜz ú{þ‰äLyEÚ@‡u\Ô9V‹[®Ï.½ƒý : ¼(#“Ž±âêhÃöãÖ÷óó=ŸP U+ Û$ äÑPˆGGÁ^ îÄÀ^# »ÕdÂ<LÆ4mj±¨EÌã^Çá†×O£>`ÈT‰Z…¥Uk?°R€¿ÂÒxãÖLϱԿº?Z t¿W˜õjˆpÐXXÔ\Ú›|q¿ëýW¿ž ÖM人µÕu.Ülz76Õö7jÕ&åP¯¥°,ùzã—+ÞzØõ!U4ÑQåed ƒzMâT°]FA^#‡ÜG¹à-wêIÄ¥,ùlhšÏoÐðsÜ4P ›zÉ5SY„bæ1ã0úˆ°u˜MzÍtRÛKFT­<
+X€r.åÒ±¨[Ïb>;‹‡¦HØk.E’j̧¡`¿Éx]tÖi$S.= 8ë7‰³s×?Û|">Íç‚ß³›ß³9ŸŸßÞ®?Ÿ¼Ç¥½.±ˆYX×M&åM<®èà3ª¾
+,>WŒK.ðB*dN-aè§1]/ŸÖöó¸~°W¶ä—$_1»¼ŽNÿr˜ž~H§ìÊR\Ó±¿.?•_$“NÅÑÆäOÀ»n6k×Ôs“?²éé
+¿y¯Œ:55XÓUMj:*°UÂçBwÙ´]z¨àð`ˆÁ‚>ó˜êËø›?ì>cÓ0˜SÃBÆa*,¿@ÆíÒ ˆÇÂÞrÁ[$f–î- ?Û]:±·,8UŽ©[³3‚†©ñ“ï<µ];Æ¥~\?X’_ÌýÜý×÷…Ç*ËÚVw›¹œÇA§¬c æPí\‡¸ÏUO»M j“íÍk/î-ê[Jqûèdº¾4ÕöÞÎcM —ô[±P¨Û³}ï><^ŽØD U4 T §jwQz
+x‡
+Ï¥M¤îà’² ¬¾FÁfQÌ7À’ì0¦}ž£œÛI#†Aø†Á½ö÷¬dý¶ŽÐIÛè!â1ÿui_§týGiE;Ë®‡>'–Íš •"ªöÒŠ¤¡Ž; ¯s^Ï›—þ)>í2‘°CAÂ>+þ ìR‘WG‚•Nƒìßð…¸ç[ÜF`’ËxltÆ¡{µ¦ºr
+ðÈ4¿¸ êæáˉ»õÍà>3ùðž‘ ý•ˆ¤‚$$½Pˆ
+N¢âÀ¥ª4 Q‡’‚-b.m¸Î`Ê6SŒýçÎÓÁãÕ°QXÇnÿ›Ã'§kC^Ž™…Õ¸aˆDÌ£dÒ.å³>?“qhˇ›S¿ñ¹[ÉÔD¨Š:µtJÝÅ¥4=õ¬YÆfÌJU´íGÕmëÈ«U©ÐÔÿ6Ý®7X´$b"’6q3Ž0E+ ò³œpŽ]”\fê…û<`æ–bê.Ö DÛ_Nj;w—Fð{]oÿªlæñ»«û󦎭ٙ¢ò˜<¬ëç1“ˆÍBó9Yl"H#~+ ytµ¨U@ÿŸã2}jã¾ÃøËN§Ó™N_¶q2M&uÇIÓÒØñ0›Ó˜p #s :V÷¹BB|àØqÛL\ã Ì)$ÐJZ skuì¡]IéÒ/}±o¤™ßî|Ï÷y>×$Èý_úÞ]·¼nùaë…ãYŸÆ€³Vô=„i"Ü¢*Ƨ¼[Àí.l5¦ýºî}à˜Ó²
+ts2§mʺum¤GÙHûT홤™
+è…TØfÜz%)<íütwJv}k¹¾?…ÜH/ª›Oæd•GÓÒò“YyUÆ£n%1ÍÝØ” dï%ûérAŽÑaM7á]§Š&¨º~ÝÍ…Á3‚CJtAacvý4®îËúÅÕ„§¿4½"­ÊúF¼=½ªå'ÝþѼ¦ùxV {©¸u<+,;xÕõåñ›o(·®ýÃë¾’Ý—’«'ïTµ^++‰7ú†íÉö‹«oüný_e—µ|³êXÜfÜ]DêR³°¸æ|˜‡û8Z꿶=ÕøñîÛ;…åÇnäVڧ퀜eWõ>¤gÃ6K!2< ™m`ÂÚ~&d“~m0
+óAT–‡Yp¤)×Ñxoùñ|ã§õ hmgFðurNX^¾8Û°ZÏ@+YÐrƧë&‰Y)ÈЬ_Å£€w˜(ªå¶˜¸ÅȬ™™u»öâ6ˆ2ÐB Õ7­Žâ¦ÅÂÅQCö§¸1öŒ‹Ú9Ü(Nyäõ…IYŒ™´…˜QSˆUlDÛGx‘
+˜*íSßûØ
+î¸Â^>¦•$1%okzðÚöŒ¢Š‹Œ~·Z‹q³Ž‹X‡Xð\i¡|Wögºþ¾ÿNRÁ†MH!î|ÂDGÆsQçC:rÿg66úOf™ê¹t²(¹ÎbŠÖšEÇm:&Næ +ÀÜ+ÊÆ”w°ŠXê+eýŠÈr>å×v¹eÕû zþ‰gÌž\µ"¤WÓ–õˆo/.ïN‹Kt­)*Ù™¬ŽþØüGr˜€&fijYÃK¯@¬Ö¦W´_uçpAT‘x.,ɺMý§a§34Ó>yCÒ;XC¬(nïÏÈ*fä7b¯û.›ú.ýúµªöÙY›ˆt[%™e}jQT±þ¾ýªó/“Ÿ'çUMàkÞË3çÀcH_oYÊÍÿ*½ÜñUjYðMjiàZÖ¯ì-k;÷g$7N#‹QîíîÉ’¨œX(˸ÅÛoÿxçªù}àqû¹U».³ªæ¼”$&Ûþ”xÖz!ñ7’òÄ´¨tDVò›ÍGòê¢ßaÉgp¸¼ :AkÒ#­9\–Vžx•ày&Yáœ?€=R^mû¹·¢zqÞÍÅôƒ¹5½˜Æ!SaÖ9ÿ£ŸiÜé‚ÙI˜ ¾Ÿ^S´mšõ…¸v€[Wv¸Å7Þ©Ï"®‰³-tèlSÑIa]— wWIr vER™ÃÕü\È â"-±Q!×=zmì vÝg€ 2 Ó”_ÑDø•-äš®Ÿ™”ì:ª=ç*âpÑa‹šØÐâ†Í~¶es7PCa5q1ØÙ5èE e6ägÃãOØõ‰™|ld¢°9dá6Lf
+û×ÆàºÞnVÐA³¬ÈÃ@f}jøRô™[d@ÉKze5TÈfJùô½I¯¢!·†ª¸õѧù¸óqaËæ*&¬#܆¡bÊvËd`6Ps6 (2ö”‹>xq¶íxLfLaçœ=,6ò¨£@Ôù·®[Pètíà•]4¦á§=’›„g°:ƒÉ¿ËâªN¼€‰Œÿ;·~*ƒÛLÉUð±sô—ˆcô¿VG
+“·„þÓ|ÄÆž÷Uƒ|„<g0}‹+x)OOÙΛÖ?ïL·}¾ Þý~ÞÖš”^ÛzÞ9íV5Al#½†.bÑÔ|TÙýåÖ$ÿ³|Ä„ìÍkš¡£09?—ĤuYŸ¨*åí¾²ý¢åã÷/zÿ–Y6öQ!T½÷Vz-íQßIyÁs`¾³ýWÏ >¹óÉ#é•_-YZ/f,}É9åmbV\NÌ ”§e5Ä¢¬úxvà[Ò+kHyÄ5™U¤96#„©?œ–/ôÂ]ˆk’ˢ볂’£Ea©ç‡ú°uÎ"ÚbT+¤0èBËòÚ£ÅÁJbIVM,Jo¼Ÿîý:ôŒéô´†žK{`ϽÊbIZµ÷¶ó‹Ä$ï³ãIÅÎ+á•Ç²«¿~ß]z, ^)#Áˆ)œ¥l¥ú~ðìžs_e¡ËBÎ 9ȇ ŽÊ’Ëòú”{°Š¯N{2ÀìtPþ»6dæ&rŽ1a×xôɬé„d@v›
+hø0
+ Ý wœ…¹SÀ”YLÅ£q~Óv¤0e EõlÌjec6·iµÀ`Gä#(·9ØÍ!øÏ>ÌFÇŸ1‘xŸý^>><ž‡ìƒgœ…~•¶ÌâêÎLX¸épa œò›D¹UKúÕí¤OÚ>ÏVý
+ìü#}·?hôËj´»ß÷ý|žGÛš]€ûòQ&<<^è$ œ§ØmÈ]ÿÜxyó£ÌO5îÎw_È®è[ß.™Z½S’¿“’ÅcG΋ÖSKêºÝYõwëÓÊóÉË?Ûé¿|”´˜·=ȵ]º|i`ÃÝqzÏ#9™]lùŒòJ«öç¤UE¯8Ct8+þ£ü –p¸¿biZ{ÖøQìQÃÈñ†B#MÌþ¢ÞAÃê?Þz&;sDê»ø€®†ûÜq˾Ü|ÑüÉÛç·ÿ
+=P->CÖƒ\¥ýÆžrÔx—ºóÐÛ¯:OoÎHND×þéíZμO ­<øã×Iòí”O]G-#5{s}²ÅÅ’mâH]{1
+ìs¸„Ô¼ƒR6dêç!û* £Š¡­ÞöªœWßY
+Ú-Ùy´úóVޫ뤽ºvEÓÈB&W‘¦Oç…õ鶓,!lÀª¥ ³’ñ›ûh˜)bêÿ-cµ½K[€1u–¨[¥8_ôãŸý‘&‡Kb¯¨›KA“Šm6³i« fs€KZq&1YmÅépCh;]ÈÏHeM)†4 ¹• ðEzè¡ÏØ“Jbü—JzÖúoUR:àõm8Cu9ábHÛ xŽ¾’ó¤ ?ö½èM&Œ¶C^ IJ“ºŽœ_U›g9™z·W¡GƒÀWñ¦ùÄÈd9iP²q­ŒOj;9ñ;nè£ã†;»à Ò†³ñ‰yœ€K8œÀ H‰ÔµüêÆbÀ gI‡9ëë­Þ÷ö\CùÈèƒÿûnĤÞlç!×بy œš€g¶ä g¸¸Ø ÷ž”Sc?WÀ…Ž€ÿÊi]/'öäšÕ.v\‰ÄUH£¥ÃÂ\“Á±rÒbbÂu‰ÄTá_eçfÆ®}P‰ O¥ï»ùÔðd!¦éØ™W]Þ˜15e]&.>1WÎbÂVC18ñŒ
+<%©È÷¿Æ:‚£lÂnç’W‘°Ü¥ƒN3v2!;ôì¸OyegYY½;\efÅÐ}òrÁch¥WïR‹Îì".ßöö`–6_Ȫöfûª¹€Uìq‰# k#v3 ¹ACÿmÍvœz;-?³¿€ÔòA;ƇGŠà¶E/ÖÉ®ÚPÚg’3«i~©÷âƳº?ï¹¥'©ye5íú7gºÏm½ý šU¥€YQð)¿Ýžnøøõ“Ú)w×9zYw›áª]³l’óàùå;)OˉÂBó§…%ézUô-“¢ n¸á–ÞšîþºLÚÌBÔ9$ÄlT°Q­¬è×J„8¼“®3 G¯`}\ddâp©ÿúösé§û¯äg‹ËH½ªi¦ ]»
+3í
+ðÒqÆyŸ‹¹Fi‘{€«™ ÝÀ’˜ι-Ï<hgcv+ÿÃÂuùUS 9Ì¥èèÔÛL¶5¯¾N“†ž£4n:†9|¿n-§Mšb8$â°Ã—)`ìC`26†ë„$¦=Y-á°3Q»™
+(ëödÿà‚¸¡ÊGœNjU×’[Ö+EŒŠÊÚУ
+ô£´`LðÑ"úág6êr11›)ÌJÙð™áÇÇo\SBÆbÍÃ{:\”Aæ^¢ƒ‰
+vöë5ðÒ‚¸ª3ŒÖ²8Ò eÈØ£¸~¼Vð³Þ¾‹5¶eŒrj4¡Cj³U]7Co-ôo3i²ºÔý¹ Ží9UÜÈkÑùœÓ (…1y)¦äÓ~YgÊ-l)ļR\!`ÂZ ²>Nò®´_-…ÜÕç#àJµî )úIŸ’ l)+†^žÐJ“Kª^ƯÇ
+!£• L•¨ÎT]GU•¸ÖÄ„•Ò'‚³X&HïðHÖ‹K aÃpiÍôK)n}]
+\BÁ¥‚¢öBÄ`Ù]Æx‰Yå-Ø+¸ÉÃbÌ0Rˆ,å8n„ÑVb,¹Ìk}§®?I-‡³+–G¡O© y¬3?.F­“5~;Z—ò
+AIçÚKÖ™ø«¦´ljÛiF6ƒ(é1hË«ÃOÊ1L›vJ:÷/ç}â;™eÁUÊ/é,÷µJpç ôzΫ<t*;÷—¤×¡ÃïU×qSi‘ÂY9)Æ! Ÿ>Ëød]›s=uÁ‡íÇ“óâŽÊªa,Ch‡2.ù-ï¼à=¤‡&P.<³+³ª†Ï‡=t"ìƒeQcÆ-mg¹›uÉÛsn„ubÞg½u¯D~ŸxÑs¥Á””¿æK¢[]Ü‹{s½?ÆŸÝ9•ž\+ùÕbʇòÓïd×*.“6¹(ïŠOõ~G;Á­Üòn†vfmÂFçƒÛÿÀÛOýÎfhùw%ª¿—òŠÛHŸŒUðkä•Ž ±/¤líߤ–Ùgö¦Û¾¬ƒMÅUqï«¿ž"4Š"ì3íQõC'ôüjQ’à7mÏp~¢ì¸º™ùÑο³Ø–sé¤C/Í, Ý䢒•µ«ØÎ]ßIo|õYjN‹2¡ñé¤S#ŠO žMÃWpàÜZb( ;–x3tžt¨é ͸5ÂŒ—“”[,†ß2ÀÕñÙÞKzNA-¡ÂjÄ4Aùý…„O}tPÖSŠ âbS¬½æÖ¿BêÿDŒµž¬ÄFßP¡{3YÿÈ£´—ËYª«ºá*ðã—ue’ʥ꥽j~!,î*GTÒ"8sÆD,O(øþ}ïâÖ<ï\rAÙV Œ”OÜΕƒÕ¨ùÅž‡ÛsKoï»%m;¶¡Æͼú”é_}7x>çÕ©˜¨åEau²]o$C¨(ë—³í¢ëñ)NÝÁœª£–™LÄ8’ ˜G™¨FÇDõÃ…ˆV[Ž¢2È ÁÆ,rsóƒìúÿ9.óç&Î;ŒÿéLgÚi:“ i!ÓN(!!c,߇°­û–VZiu[¶b0‡“š†k
+¨0°pB}•Ù‘žmʾƒáÏ¡3ýCÀç–¸¸ÏÉÄ0-
+Ïœ¥vßÒQÍe‘°)iÜ8ZßnKæé¦æâN}yÓÔ_?{»6üyáÞõO;†6KŽw¬SLê抸ùíòü‰&<ž£ CÏþªòkðÀ‘@5õ¸ÇZKÍ/ÒÄÂ]Ðë/4t 6ÈXÜ8ÖjåLÓ,.»T]ÿ˜‹é‡ÿ"Ó=›ÄBOw{M×. ªVÎ<ËÆf¿Û[•üåxS}f£gq›œ…ï
+ó÷9`µcèsLÖ娦¬²âKù—à˶<ÏvM5î4ò9hÕa‚Ž¤k¥­JúÚÑ6øLQÐäíW|É»DeP-M8-<î‡Ìš»Íg»Ü <³eRN3K,,—çÞ¶¡¯U˜»Û쮢ÿvŸÆ,tR3À§ôìüJ9îF#^”%n=òswÀïƒ<j2¨‘!E%&¿$d¡äôíÅU?´J•öÛ7Ä’ÿ.Ÿqt¹Pß$}ÈØŸ›yï\#ç]h<ÝTÈg)\Ù\¢ó6½XÄ\BÑ âè,›t!tÒe½éÛ»‰M™¦ö7eçËa]/wk1‡jÝÔ[ ¡à× IÈ›vZ7Ô ={kWOì>ºòûw‡?fºÜ5Q]‹»Mݞ¤P…HZ•íŒIÖ MÒ6p$èÁ_=í‡ôW¹˜Çr° lú«aå…w«C'ö_Œ~Á„ã¬ÇÇDt}LTw•Š
+&ŽLQý5>éÐtYŒ‡,apu ùÆZ 뇡G…Ìâ“j*°PO—ëÉ{<ü:•º…[ebÊ4IE ƒ<è&}>¸#BÖó¹ÿëé^»èt·³¹Hh8\ñC;¯ïäôã.ï=Ž){ê1§U̸ÁKm*‘pá¾ø¤aœë$aU@_: KÏ2i§›ÉÍÝ­‘vm…tZ)ȶà°S¤ü²@Æù´å†¿On‹y÷ƒ[¤lÊ8Æã: µ£îÝßœþç^Hy‘Íy‚\Ñfâá7Bné5›ù) œøØ*(ü·›……GMÐj«è[ò.›µÌ¶K.œƒf0×y
+I>9
+Ÿ†óï/G•½ÕˆM!¤Wš™à¿„ÛY‹"ãÇQtæ0d:ܺñ …»GIí
+ÝÏsë`ÍxfÉzNX?®­¼õebäxÑu¥ù£íŠ˜Mì½2~R=qºÍZº !ELk %gB³J>U¡¯É9ÏX vCY3o—l7ÿyö·/ˆ¿ÿ^Møp÷‘r5µrÞ‰}ðµÆªk@ÎÀl8æâ÷ñÏ 'tð“Ÿ«^—Ê/ÖÖŒ2®1k½,fÌŸ«9ÄPOÛ†¾!Nýê[ä“„UÜÝÝôEßmúçÞÃ6q˜v——&N&1sm­!ÃJ*ÖRñ§z><ÓHXûö–Æ>Ú]úðèõØ_¤åÜ{Ý8x…ö«Ð1ºåÐ\§„L*,ø0‹ŒJyϨ
+>lä0ëÑšk°Øæü¤Úë–z˜²^¬eMdÐLg±´«¤¯ Zò‘»íè¼¾IRͬc@d=bµ·‹ˆó—*ŠB§¯,¸µˆÃÜ×ý#¡P#™®'î~ÝL߇œ‰g)Çö:v{û'÷%™Ÿ}.@7jý´ÈûÂ-6—¸ÐŒRœîù\ál×֦εr8¢oFžˆeµÁ[®6ò–¾zÚ|¡Ušº,IR+ßYÒ·§çÛ•à6足™×¶HFƒ³ šiäŸ÷¥pˆ±™ÿLe°'¸K+¸ :Úßm`è/U‚ê– ¼§ÙFÊÖ÷æ'ÃÇR¹­—˜0ð*s>\äÂÔJä‰Z‰~Û€sK<áXÜ"i´L4¡Êi†j÷Þ¾”Æõ nÿ!»)"°P 8L/§u>ÓKt°["qG§$ø_<^ÊÎ<Ô*áGíM?ä#…‹ð rÈ¡—^abB‡ÿÀÍz1<×.Æž¶ ¡=šV²~LÖÓž)ëÓöæšå‚’
+2BúÎWÇë RK1Þ£r¢•ÂÍm˜¹Â£ŽZÆ=Q}Ù³àèïæbÔf9|m9w°l»,ݳö<Q/{Íd1äûýýƒî­PãJžÆu`­`j®O|vøzütåéåþóäæGzša4ŽD`7äMµùé/uðD>Ró~¬çl¦ýÀ#N™u@.¸Gá·6³ñgZÉïW
+Øô£Û;ßLÎ^ýu&>ø;9K -¶çߨIÈù¼GIÂÔÌa¦ƒ”kð ëÝËznî.;/u`¶àQ5–pëeÜÕÓÿÞ+Û¹Ý×ö
+ ìì¼Èß_iqáY±è÷‰%Ò[+¸GW¡çÐ)¥ yºîú÷+o#›“¹ø …=Õ·b úfhN¯*E¯åí3«@7
+¡Ù:;—6f>to7AN¤ £ÂyÆ;EûïÛ_6Ÿé½‡½zWÂÝ:‡Œ ç@ |çÍ“Ÿühü«VpèE…û2+À+"÷0)òXƒ¥<MÈ}#WËþ
+~Kn¯d<·Ä¤µ¯•˜º,¦ [¯9®Ië®zt›'1 ‡UΗ¤ìZ>2«"ÚÅÀL§Œ!õ”é|=a> ½g²§/!€Œ!ÝB3 t¬¹â¼ºûýä©7/ŸÊìÔU!m<Û映ÆÝy.åc_6sÄÔQÒÕ/BÇoÃœ$ßÆìpÇ#ÿcØl8"qÈ8d¶iÙy­gSX{3c>_ÏÙ¯ÉyÊ¥äh¼]ÏB¯É<áQËÌ´÷Ù©ð·Õžaö©áãT|ð7?{ªe ¸Åu[áÁ;@sÝ*E¿ßÁ°w[¨G+xL‡9;›L\Ûbîê[ûÚFàh$Ô©t·sÚ&ňEÂ}°êèƒ<ŽÈ\ä~ƒõ!õ\(¬”bóà3½Ô†N.æiLå! ¸«õ_ŽËô»iôŒâÿG9ý6SÚsÚ9´eé
+¼<ãÖt•Ãcx94>Zë%ÞùÆs˪nÒ7¦Ëz,òrxbüKrâÑYÒ ZÑ Ó~µ
+Œ LX7PáÝ<1n…® ™¬’¡[ÇÉMžÐñYaB kD a†ù°â»åÜRÙg2ç`½}¼¬¼õëöøCtÄ„F±BlöG.9ñ˜Kèäå-Uß,>ŒÜå7Õ%ðìBÒ„n[&ÊÛÓ/€ƒ^ÂzQOç“Ðk’#æB ñЗ²ÐÕ ÚO|ÓÓÌû)ÈxÍRÚÖŽZL\ “0aL”ñé…Ù(d-ñŒ £ó.6>ó=¿³Qƒ”‰j„¹Mis.h1Q¡ÿ¬“ÄÜ›Cà´Å'g^r¬ÓTëg¢f”€WEfŸåƒã¥ÄôS>6ý”öãLĬ{I
+›†L-½Ç¤å˜Y|ô¯òö#{qov©ôqò¿ån‚c¢ü¦¦›©;XBÓ“ßT÷€gu{p%CLÏÁS€áp6Þ3*ù¨¦7e“Uí¾_8x7üíÞ/ʪŸ¤W²MKÖÔ“p,µ.¯Ú+þÛÞkáŸvº¾âÊV.¨íaüzaúåA›iº•ñcÝ\Ð Ê:ô¤kTJÆtLÈj Ý¦ì&8YWÖP0çY§º%ãPÖ¦lŠ›Çë²ïŽíúö½eUÝÁ²ô*å–×’nE]æ:ëUµÙä·‚?´ž‹¾ìüêpþïµ(iŸiº­2ïÕõné­”cð*D;Ò.uÛÉš¶©è·¨Î ‹ÁŸô ?v?cSÞN¯Jÿ™±Io°uÇiÜýÔl,ÅGŒ9ŸQ”¶«›rmGؾÐv­ô_üøªç<µŽµ|cø6ÎGF˧Ïq‹5ïU H»ø*íê»x–0ªÊñ©'\ô·ÜA`.«–ä ðŽ‚/[zm)®“šû´GÙ˜²)k ÄNùÍ2º LË€3„IÆ„U)·è:åAš ~]oE;™ÐzhÃÚ²>¤™ +¡ó m§Q£ŒéûÙ
+z «ïçà^`‚",¦|ìÁ3È’Ç|¨,& Èç-³ `=i`éOncÿΪº<xHoNÎ9°ÎÝ_†/¢&„~®ô.Ò…´C'e6q„t›÷VômEàÄp 2gìÊÚ¼_ÞÀú‘VÞ¯2^ÓŸ˜zÊ&¦ž°ïg_”>Ì,aÏ !\‘à2Ò£¾{¸<xqçñÅo‡.¥lÒj6bÓ„E_ˆá§óå¨^ÂU‚¬C^ZSTEçEçw_Ê.¥Öк½7×2kò[›^pðNQµýSßß?üno¡ç|Ʀ¨¦€ÃR^ ºõJr…´)뿦“‚¹Î:uGËHõár3µ‚5íÿ¬¼–ÙÐu.#5; ÃM­¢59ª5 ó¿ÿ³øBâ¹ðë?Šÿ|ðfø2½ÜÉû‘–ýñ•ÝwâK'z'ëQ4Ð^´åd ­=\Tߎ>þádYÕPðš/ÚšsJndí—Ó«ƒ³ë’ë¬iλ5‚£Ueõö‚äå0
+÷ßÿ²ÿºëkÊ.«a½Ú.jC^C»õÔÚ|´„ÜÊmà9'>ÀzqIÖnêÉ:LÂOK*¸ŸôÛý·ýß.ü=ã”V“>E#@Û˜€¶ü\R©L@}¯Ññ›]Þ7"£=º¾bØ¢gFq1¢—Tòº’Q´ m¢œªfx§¦,<C>`–Ð+Fû'Ì':í3Šó>C?éPÖؤߑn´i÷äÊþ¢üú ôÒ¥iËØÕ¾ç3a”žÂb ×|Zºt´(»F;±îRÌ:Y‚É…ÌíÒõÒª–ÜzçxI~ýà-xŸMݘuhï’6³0½Ž÷ç½f1í6ö­ ÕŸ–¤×²NE1 Ó.­ ï3pÀŸy˜§1‚ýŸãò|J3Ûãø«»sg_ìì&îÞd“hŒ)š¨1±ÄÞMì]¤Jñ‘"
+&¹1UM EA±EEņÒ{ˆ%eî½û‡Üã¾zfž9Ï<gÎù–ÏϘÀ·Â%…Öxíž%èzj…OM.w^÷ÒÐêŽÖðÿì°ˆß78$°ö†ù¢e4y—È¥æBÖ¡†Iüïþ“þÛœö£uüt‡ mwàWé .p6¦É¶,½
+‰IöIJ‰GÙšT³‘ û|sÌ ý·Œ”æ”’Ìc-ñ[ÍQk/àQ¦ÑŽBãç‘þ#%É €’ŒŸ ¤ý„x‹ˆš©ÿHˆ·‹É.i{¡ ÌÐ/ÊŽj—œYæ¶å˜Æpww„°ª§Å¿©x•¿m}@Ç™Çðé^àIÐMbJ†iœœªÀ¯YEµW·þË<†ÊðÌv·X¦:ë»[<s¬Fó%ûŒ -"TÜ¡‚˜P’òôCuæ bšYB-pNÑJ=Šöb›ë’bî{¦±É%þÁöH˽ÙÇ¿ZD¸Œk<Ú™– cðhË2Î5MÈt)ˆnàk’Vì¡–Ú&ñóüÖÀâó.§ªg£çïÓ²ö>’în # Bì·ŒRx8˨µK¡¬c úòw‡RÿfH©Õ§`NZÃy®ädÝÐ0à~5˜Û–º¡X¿?ŠˆÙ„Eì¼k¾±û}s÷æ¦QHJt‚3sMQ‹sµ^9£Ô>¥Ú'Hv†š®‚¾½©{ƒˆ°Š)ÙÇk=LŸš‹u€~.óÍ·æYe˜„ƒ\âž
+œiF
+™hF
+ŸuO½”j;Äœl@¹Œ]#ЧmHÎ¥jBÒ§jCÊb1¬}D°N·ˆW¦yFШpSÕ¯yiË pJΤpKĘa-·ŠV¬{O­zQ¯V’cB¦vR!—g>ѧvXË£nI—_*‘[#µ€R»ŠU¾”[)°€NÓ¤s\È¥s¹”k<Æ¡qNÓ°ƒƒÈ¡rQµ‡V ¥nC¢mF a=k>
+¤sM ­|T¤oD žg= ¥~JfA¯|P.Í£tQº‘i2µ‡S²‚O¯€O©wK¥oE«|T–mL“^?ÇžqH¦yX mH
+¹†QÈ–^,¬yI i8·€E¾“j?³‚i;Â’qFЧx[Ì¡g9ǘ_+ÁŽZ!À]%µƒU³€S¾Žh6¨v_#¹ƒ`#Å™h7×f2ÌžqIÁ•h3¾‘c1¾“_3¨{<¹ŒP ÌŸyXÇ™rI¼e,ÈqEÈŸl?Èžf6Å›a.Ä™^+¾‘U#²…J±‚N¼p:»‘m8Å›l=Á”c.ºŠ\!»\$³†Xª~W’fDœoGÅ›gAÇŸnD¸ˆZ®€S¦wM­€O´†Pº‹PÉ¡a6¡~FžqBШycÉ tQ¸Š\!³„P¶ˆU»‹Y¥vDs>{S/¦yK!׫qUƘ^.Ä–Y(Çš].¿’W)È™R(ŘF±‚>Å¢dEÏ°q\ΨnLË£qK¸Œc(Óg2»‹`'À’c2»Ža.°„TÇže8ÈŸd:Á•]1ÆœmB¹Žb*²„Z¸Œ^%À™lBɤtRÁ–e2®€K
+H‰Œ—M’d· „OÐw¨µ"\A–ŠÑR[ŸÀa­FKßß_>‚ew÷+[­ M«”E@"‘ˆÑk-£[üã£Zس.ï±JëQ3›Ow‹>jxØ|
+¾¹·éÄæW¹œfŠ
+™¼·’\v˜s•àÿô 2Q§Ó…0fgÚj'þå±kÊ|*†ñð¨p´ñ¥YZ"ÂÛš vŸšÒ`êÃݦ@¦uú…„̵O¹ê@¢ۆ¸]<¥›Èö\ÄRîŒ ÉG- >ÓÚW"±ÝŒ&‰ ¢ a69¤×ö9Sýµ´]O Ê
+îj"Uê¢Ø⎣¤ØØE„Ã2p¨ÎKÈÌ°¼L-Â`.«ä¨!ÝTRyú3W`¿ÄFD×q¿Ç7ÉaK/`Â}„¾3Ž 9DH·ÓÈÀö!þ¨~¼æ5·.©9–ÈofuT·Û!«ŠXà{DZKÝЩiÒÑN²k(Ú]–€Ö®ÄàC8CYJyg‹((žÏuoón:ï“]üqÛžˆ"”5419ÛâkÔ0>Ú hCdí1_¶ü@ 5µ"–ód5™.~N¡0.³ Ì'tì>ðÏ™¨3Ù(
+>SÈ(çn&ÝATq æ oAa@Úür’,š>9‹‰A-iµµRsŸIOjÎPØ%¥¤±ëºu;Lƒ)icíyMˆo HœàX6DÀâ`G¨-¯ê¨Ö¶QF–´>:•°„à£cúл—‡»ÜëƘé hU
+};nGbKka æ†`áTILĮ̈°¡ (M€t;—¿5‘;W &7ËW¤Sq)—]^:Å‹†€©,Ç{…¶.¥¢½ƒè@© 5ÙÅo 0FŸ|EÌΗ×@‚îþ»ÆÄmTŸ­ÞT™¯,}ëXò"ÃÆ*$ZîSX$)
+F¯^i24¸dL­ %Ù¹ë-a1C0ʯñ@Ü>å~6ˆ› $Ó€ÃÉ×Ì#iž•q¬ÅŒ?nû³K–BsSŸ$0^T/Z(èš¹+®!È]¤S
+›k‘:S “ÇnÙdŸ%ö‹Rص ãÌ.˜~/'&ƒ»Ô!8žã¾x‹¿†ùtÙýªûýÑòÅx¾ÐVp8›.Ο‹Íg}—@šntì:éa: .Žö´x<ÂT{ÕŠ*a 5›ê´4S¯ROz8iA(Ý5®^n’¸ j$½Ø ŠÁ6Á4¥˜VZJŽ$©Ü$of#™im¸,GÚ|ÞP‡ŒFáÊËÚí™è.·Îþ9Oß]bÒd®ÓŒËr ¹ÒÓ¿ÿ½îýØA9…ÁÐEÏÞ¼ô'Õ¯jâbY¸FÅ-–á€öjSÝ+yC£¨gà̹uÊ9Æ­»umF†Ä¡ØsËÕ #†!b•[gæixu’šÖì è‚P'ÚŽ•ù¢ßé~óvÉö¼
+C3ÞžÕ”¯V^©¹£õª'DJ
+5 Dî“b ¾ Ï‘¸¤ðÌ!æo"`Q§ƒüXß
+ ½ážµ…æM6›|¢ö¤ "µdíZôÖã~¹mãšá.º¿Pש*ª ô_þþQ¿þóã—}ÔÇoìXstBOÆ*ñ­Vž8裟ž¡>M±S^dAŦØ5:{BXxÙ#0‘øpm*‡Œ
+v•Ÿ­ g0ÊT)¥B Ê@„€ R;‘f ¦µÏi¢^¿öÁ Aâ(ž¿)‚ÈÔM-¸×šõr]¥Ñ%'EuâÍ9t¸¼<±ÚµÑé5"kA+Õ6juuý¸V1Å=tˆv‹¸f×wOÖG$ßÄd´ãç)ó¤a%×EÐ-Zó*ÉØOf³¢ Qœ<Ç5{ÙwdËîBÿ
+É,Náí9Ÿ ÙgúÒ«1û]A09½Ö6¨ßyˆçÉkÒs=nØåëiòÁ ú–ñÁ¦HúèøÜ¢þ?“‡ÿåÞ /cõô<èl†îŸo@Ê+¯«Ïö»\Þ•FìàU“®å8 ‰†¯X½Y¾Íî¦QÎÓ±Ô~©O½@xb8?W²ži*wAú†L­-t8gfƒq(‹.ÃÛï Yú%KÔµ*®ûs "['c.¼^ãȲã”ç¼c½¢ÚyÈ׿/“ä¸n ˆž€wà ˜‡5·º·­ûoýUh‘Ýøf;,G˜J5ä0ÉRç©'%F=´×ÈÉŽå‰n`‚—^
+õˆ=äTú5庸2B×fZ9ç
+T$óÌäôFEp|>Ü»1cp…3ùAèœå`E¼Ôl‚‚ÛsÄD+)‡àÐNÍx„l¶ƒÚX4Ê]Îçp7ˆ„epÖÄÕ妄ÅNùˆ±NPRµ¸©75v™ís˜ŒW•ãJNˆ±¤§t&½¦íÁóqf‡„r¿€z ÉüLû¦ÿ_-m¡IϘ¸Óˆ£G£³ŠÛñ_‚©¶pÀ” ,T0úRáGЂ°$Œü®#©•¦¢ÂtÀ U¦%®{ 1ÁAVQ_V\ ’+ (~–Y™vNVÑèX[Ëë"NIvTÔãR0›ú¹´R†‹ïk5xúDÑ°>,ô<€þ\€
+nn9&Eƒ› YlEs–ÒTFüR°Çop|³³”¬0ˆ?Å! 8#YŽ]ŸïDöeWñû¨mI€E’èëdç`"µZ”û¢[¿uýóNÓ@™1f¼×þÇzø€¬ C¯óqƒÔJ¢K´½Aü3kƤwƒðxö¥¡~¾EÝð'¬š=¢¤øô"´rô H8ƒáH|‚-¾C1†õâ
+m|S¶Ps=Ž¢¾˜>'9B×^^ÄNCÈu­½^ÎÒ° ¹›$á¯îÅxiÞXá6 _ŒðݬgyznüAž
+rLñÁæÜŽ ±&ƒJ£8mÚgcN‰±RZoW§1ÄFò`rà<e4îä=I
+£ôÈjXròHÖýT#kWðµÅ{ÑV¤âôrhû‚ÀÀl’w9ÕÌú—’¯Ža‡±e²PþîÒKÃ?†sD7ãÀ9]Íi÷Œ:ŠJm»¹Ú&à k›åQæòöÙ’Ó¦Éqo„„˜%líV‰¢é%”iƒñˆ9öœíCU)ç˜á*š=t[T²•Ü4Œ'¯Ç¡RÁ¬üc|@l†fs™ó4À$¡kGÈ—±Sd=)* zÑu%<d¿6è»Ý˜2"0x¿j°žtŸ²ñt¬9£ vtlÿôHηWü_üêhašð6#¶ø,0–ŽZ\ß•.02T&ïôùÃ…Háj+¶¾SW"‚}Da‰È÷9B ¹2æ~kI@ùØyœy-.©tBÁ/0·ÉÎ)°@Äí`ƒ]u#2Œ¤°F~UÅ«MæéP]>kѸüùv
+)@7/æ Úé%@ Rˆeû€õ"²H0ïÜš:cä©ÝÓá=0ÒÌQ7S(åÀ~GÑ ÊÓåóü}²¼{Ú¶¯ðHØ—Zùìáfünb8Ãœ@¦1B·üZ
+/†V·šz÷ä "gÕ•»âîú÷¼äW±)p6oj{¾`pý 1j^À˜oõ‘O#øÛ  ”òÁÏOÌå°gT[£ÎÎ 2\òG'ÿù˜eùÎ*F5ˆB /ñƒáv€`ÇøË
+n ˆ¥Ì±‡ž i$|GSc÷í°ÇÍÉ΂S?z»®ÀFçø dO !2±møã9Ãn‚&Æ6šø*Y{èkñ/öSaÖeNïæî$ËÜ©÷±ÂÆh8¿îßSVªHlj´cx3>™1Ë{×kàÿB]Ÿ!a5¡É<·Y÷MO ¬¹%™V˜ÃÎáNi¬6Î× &û_-èèÝšâª1¶Ä™æM< yØ;ÑU©`¹L…h!(ø±x£¨¬È7Ç懱@¨°Éüq®ä­(yli WìôôçÄø€Îÿ˜<hß”8Òß¹RŠ(µ-ó–·ã¨kYÊl'ŠÝZ© m qñ}YõA”i[-Kœ¬Ù
+öóÇwµ»ºÍ œˆS…ÔZrvZIrÀ£ Þ‰†Ç-Šps^S‰#\ôϲ{Ä/Œ;7ß;“æçîIHü ›Öþ-Me퉖nUJô¢p±sH ª†>ýáø¹Ñ(OS^;CHFsâ0
+p@r¨Þà{/r zÎüW9è>¯Ž Ú”¾†M—ìˆ&ïö¾†‹…„yø}ʨøåÈE”¯=vº…¥™Kræ´sYƒ.ÓÖ’(¹I-½WŒø¬yhd—Å!Û`<‚Ž=/"¶ií{tŠœ¿n©/3ç'q(Ó±^f 4KË×›õ~Æ<(!EC“©øË •ûs¼îÐc¤¼A
+Á 5™V<Aæóóí
+„é`xq€ÑÇo‚GAÀÿc¼L’ä¸a(zÝ¡OPÁyX·—º…¶Òý·~Ÿ«£²RËv„[‚’$€?m`¨dÆŒAªÛ%hfuÊ–b3sÞ%,*Ú d ®Ülbü“dy•ØEr«BJ+NÑï]‰¤‚¸½_ÀE–ÛV¢b OÝr÷nÁ¯?ØO³‡˜ƒ2_4׫gÕ
+±*º4™i'ôßN*0ü€›´7u0)tcZú¡m®§ŽÆý ^ ‘“ºµ—œ‚‘úÌà¦UMêfhÓà[QVêÁÁèväÉ5J¯HæóÕO;ìõ¾+& &m
+!PÁï;E?oŠØÈÉñö͸›"
+Pu³0qhpëíõ¡´:¯í¤A˜‘ÿŠ•
+3Â_%{xJ•þÛÆÁŒCT)õ
+FÈ6[ƒ®Šn˜eûŒä6FoÕaf©_œ½à %æI†à»i••éŒáó "ï’ 2H·ž ß”»°Ò1úø/.ýÜ‘7a•µ`—Èæp™É36ᾕ`*&àLsnJ¡ Í@š¨çÆ'†ŒÑ÷°o“£k·Ác2BÁï¯Õkùà“"ý&ÚŽ­h¯¥í(±"¬‚…/»$-N¢­Ðé.á\;‰ã¥ÄŽ"¼œ¼šUÁ²!ü|xéý2tÔ¨³ß?*‰²Xç®7)±5,Á¸éð[ÉËœÎI×¢ñ3t^Ìç2¬#4‰µEÝ}œÉÒª6Íιt¦ÑLœfR{òá´—
+ÌPá6-¹Sº”ØI—i{ß¹ìŒw™Ëî¹oº¬0‘Wâ/ÂAõÃAg]*9 ·àeD_x»b«»tœÙ<÷RTh9[ÇO°õ°Ô÷V”¡kbìDð͒Nˆ¸Ê.A!^Ä‚Ñì9wžITNÉêÔ±zŽÑˆÓÿNDµ£4›¦$+)äOiYÛŽ!¢­°9¶alËE íE÷CVùüá1½–¤¬òc?>äx fX‘~mMt_§ÈǘWBP5+î°½»D~Ø/}/±½>kô¿“±¤Ê2¤pî¢ðÆÁA¶gWÈŒ¶:åÁVIWÀ!Dº<AMã½|™@ Œ„ ©£;‰¿Ýj£‰¨¾_ÁE›\-:e¾]7Ð]®kÑϯP‡áÉt‚f´øÃÆ6y¦f¦Ý)’É™Sö£-˜R‚'@Ï¡#KëÑ´zèî.!ˆF
+¬j²'éAÔ¯mùüáa-,ÍGÉ}‡ƒ·£dbèCWZÄ6}Ø…‹2G”UÛî^ɶíbôn¬5Yw–à•X‹YKV>ƒ¹î~GShYF%1ÛÕpmÌz†3oÞÇlÁ{4X6LµçsRç‘ʲ ^^pä‘u[ÿëjÉû’hpF8äšoJälÙ=ßLÏ£þºÇ?¿r*Y1Í"Tåà¹û?7Em`ô
+ü–QÌÒ¸ZoŽ›oç*÷2  Ž(£”Æ~1Õ1ϵ!Jz ¢ó3ë*Â*ÝF{>'v$D7ß-*RÄÇCY59%gߧœ¤{lòå;ZBèîc¶VÂs§F߀©müPžÃŸGçI`bÐü\ø—¡ý»f©
+ü¢ÈEðá¬:ÐÁOÖž‘ž±Ì)i¨d’&šüë;¸úRZWì„yÁBº W$_
+º¾mñ]sády^0‚é0kI!"y—ˆø¾;D•`h\5(Ûó p'úÁœ}£„ÿ+Ì|f¿×<[Ø`«$»-;%ë(œ§| öÃ-’ŠÒ@ÚصC›tµã&¤áÅJh/u—ÈÇ ¤$˜7¯EWNü}S$gÑ1ÂEßïbúbñjš,”r•ÌpÒ.œøxzÛ%JáCŒc”ótìÓÔ~TÓvúÝ…ù¾LÃpŠ4¨ag’DKźÜ:ü$—™¨UÉz34ÿ;×™;·¹îŽûªë:ݹn²Óc§äeV¿¾›/‘«e…‡TÞØ:§CÄ€à¼Ü— ”Gï>T™õH†šué©N{~¶EæKr¨õôHvB¿2»Pk}t¶^Ü´SÊ;%¾OÞ¡V-ÁEÈÁ§Ví3&µ¤'³J,*ÂTnx©U„±KØ£BÕ¦àëÚöü1 ¶£*àZsÒ`¦P¶âì*éÄ#Ep7jílCHeœü'zÉÝ&UôÿÊ”’_‹®ƒÿ}SÄ|—ÄNíÃDz”ÀÞæ% 3V–ÆåYôt¤a2®§Ý€RÐM´%Çóò€ƒA!æήØŽÆknm-†+!Kš5}Óâ±å»ia+â„Í›\‹ÿv« E)À~,KË Ø°mZªr`Öj…SR°µËoZôép›|î #X’4ÇÆB+uèGl”µZ$öÀ[#.€)ÓL-Ž`ž“þ
+Ì/‘, ]ìÃúŸ›"Æ[Dsc]œÙd©%µã«šyòš–>ÌBŠ½Ç¼1ó1 E<þ¾F"ШƒÒü„¢HPw¯bÄé¹G!²7? PÂ:†)zÏvcÌ¿n\0U»žŠ%ð¡bfAgÆ¶Ô «K2+,A²¤x£ß»ݘ×4zgcë д*´¿° óÆË$9’\¢'è;Ô Ê8kmu‹ÞJ÷ßösL)3-™ýúÖ’AApøpN’/Ï>Rº'ÈáéÀöÝg0ÙXobFÜ
+ŠÀ°bØŒO^g)ÍB¿píø½i||„4+àJ²º:柰¤YW_¤‡ý¦âb
+ñ—dQš¶äž|²+Ÿ7EÜ]ht$úÍa"–”_<x™@‰Ò3]¯€¹[1}Kvš
+,p°oÞá‘¡5ypÞ’A;2`½&
+•*>Šý¬5aÅJ°)ãN³ðw¾•LäSf0B>œÆEý¨9b0ôÈW¢€ÇDôÑ >Fv™ýF|e4ë;i߸ËU;ÈÎÒRBª²¸£ùM‘˜'9"qQWü8% &ðc•vmºJ‹YgX 
+A±Ù) +d±$d¦¹0”!*¸èg“ÞCSß²‘œ Œ¡ò!R‚=,2¬¶ˆÊTu˜ðÃéU^X9*Jö¢r£g]úÝwä…ñ ¹ßf¬ÜØd´–áIVNг.Ê\4çP Äë µ0Tµ¢ˆ¢6fÊP«ƒMǃ䱛Ó›b@߬¢
+unØ :I %q*ÅŠ˜RFHœkžº3äåWšÔ°ø£.Và/ýåܤÔfÏJb
+ëàOg“XLœ½(é_;©²¸4˜jÛ ÊÉ}JÐ]Ì„þÓZ2Ürk#ûQÈ‹ÌwKÁÛÈE‘â†wr)ÊÒf^ +šÙêÒ‹Ëkn[jŸŠ>¼= ¯)X,ì¼­.#ir’CWc½ˆ,‡QR§fT¹éôÁi:©„éX›GNä!!¹ªã¾e 2þÁ¿£+W>pX ö¹a ¹Ô(7%ÊšD¨¨º}Ô¥h²d7ıOÿŽFÚD­Û¾>§]¸I—Øüº§N$Kä½¢LàGUXA‡>7¾ÐWœrHs:5P"-£”ú‚?
+´‹Íêúçl £ã- %wüôTô~SÄø l÷ÜüôRD2Á)
+©e®X'R%ô-[R¶uOu-r![ @†ÃôoxKbæˆÅìíåD©Y1×sRœíʬrñÇã;I=üÏ£Tn+¡ñÿÉKLÓuôµÂ'1V‹žŠVÉ‚±ü _…uE7ÐÀèPÇcŸ˜×nŒÎe‹và±2[HûÒØ­©Då?§Ì¸PòW¼F[ù¨–›ÅT/
+Þ|e¸
+Vºì‡¢›¢.uŽ °ïÅâø.¯§Z¼/êŒi/F(¬0*+G”Ô’b×hRßO×g€QwQ’<Cx ¶•M&¶·z ƒ4*qw‡/3†2v‚úžçü¨ÆšALPÊôèÃñ»¬‰wQ)øoNÊǬ&Üh…¡8À|“ùÑ9ƒ0õsæ+úrV¿ì±k¶0<“³ N¶Ü[õò3m¬£†¹9
+Šˆ0Žƒ…ZØ+Ò è ‡$àçˆ}°ù
+j7_‘Õ»©îërovI¨¯éÏa
+¯âí4ª#È8 8–(ÜÉ.RlÃØa+ºÂIµÞ¸"Z“ø¤¨cÞº«‚´qÿE¼õî;Xu0'û·»*Ú9XÍö+óG8'bŸrw…OÐ8‰ŠÞ䌄@qþªÒå—ÉMݨ…’Œà±ÚµnPà¾ØRIë<–œ‚ÒkÑ뙜¦x0ˆ¿­9mßå°_=Âq¯^jµ¬8€e‚ëI®`2¤+^?­¦JURœc1M…L©ÌDù
+iûN'@eH¤¶t ®jéŠ QçJü(1y©œJŒ0]Q^¢z ›7Óâ¢è÷Êó±6Þ•Ã;g·¯‰Ä»®Þ»þïp0±IÊœi~Çâ½ÁÄŽu­å¦ä)Û¾ùÆ
+;c C*GG*Ÿ1Œ«j_Ô
+"Ó|¹eÞÛ±d¨Ü¯ýf„§©qñ~åzJQùÃèÙ…¾.ܺÌ@Ôô`’
+he8Cu%xź³þd˜D/Õr¼_ÁÉ„ñ(VŽ×a-çîU‚p ®¤ÓßLö¤¦Zášî‰¶¯Z/M_Ä\Ù¨výŒëI( Î~mûŽv(ÝÆí/êä9j…¸4eöÄøÞÏK±¨Ã-Ð$Ëq7ä¦æ Â1t¨*£ôsÆ®¸8»…ÁÑKûDjßuà!$B)TÜyÔ h5¤À7Híʧw¦¥oâ9y«³^؆J éõLý÷pZ›€|Â:üJ¤e­4¹À/0qF'ÕÜÞPðDþ¯¿0ù<SB«]”óW€*!“žOØJ,?_[c¨ RiÊ®Ue@&ÈûDßÁç8†L†¹eúâ<¤ŸÌ°ªÕµó(ÚɈíð#MRR„ëWqR”÷T'Çx#ÒGG”Ö¦ÝÏ<
+êJ(æñò \Îз8¤RTSWM ÉÓîJéë<*+û³šn|úN±…U n¤ý®Ž·'í>·µÃ+$í)teµvp©5â ° ?‡†È'
+Vcÿèraó` ÷`&¸L = 3-¾Ii¤=–¢çªÀž·ˆâ¶ø$ŠšS5UàûÓ”ˆðÿÐÊKXàÜ/ˆwâôôñ”$Æ_𧒆¿Š¦míH,HE`±ÐR#òõb{‹Y“Ð/v â‰"¨j‰çþà1y<)‘Ô óB§ òeµøvµFH`^6ÙZ*+4³&i|êTŠÊã‰GmYæ“—wçp] 9cŠQ6Y¢N¥%æ :aÀ¬õ-2‹×8çceš8+RÆ+#åj;½¦,…‹µ¢rI]Egß@ߨ°S)+>õ±©£6’g»€~¾)›ÁRÈ~KÜ׌njÛ1ªHUil+¤»fŸ 6×-Âǧ“3°€ATŽ—V"Þ’¬†Zàß@bô‚\„l2?n‡œÝÑv‚Ï¢^ÁÐÔçZg a¢»åS`xãÎýñý¡o$užÃkç@$‘ ;ª®KÓW¸’ëwÈa)F'~Ç弞³t #®Q”EN5;a7rãZ{‰ûCÒy5è#!øô݃‘òBëgeÂõëñ„ÞQœ]rb•Q„鯉S¸rØ4»zÄŒ¯ ¶3²zÉ„Ýûù„¨K»8éD'€Î“¹O«@<ßYu%Á7«4d­í)ÿ —`j{=·“¿ño1µÁ$A¶µæeá9H¤jÃÞßU¡A¦¬<rî[C„ªx•e‡p?˜/³~ŒcÎI?õÑ66a¡•
+U˜UƒÉpÃ~cåp8^BèãQXíö+ò•LvBÊf¹±·s•öƒ½m8"²eÐÇÔŽ‘²ŸR\+Ü0׆©u,½<·bÔóì3c¥H£ĸ;‡ ß(ûé‚iúßèq=‹'êb!B ˆ– R$ÞwÃúóÐËt†‡q+Êà×`ü@< %T>ã9ýhh"`Jó¤Ÿ1§Bn]H‘ ™¬ïÐ9lôãnò½Îª{dðÌ?õKîÝ…(‚–È6;fÄù4.Œi’ÿ!ŒÅê?ƒ˜ªâ‡Ufóó 虌¼2¿6RW×;FÇëßpëxe&Œ™3”£z_-Vã‰Ô¨'2@Aâà«3O52"Ç]ƒ_fÓrƒä˜ÆDéê-g1Ähfšw¸ÞîC÷bÝûò75/þ{XYAh¶¯TÛ™Ò; N£„Vdü£ÀË:íGu
+Ç&S
+¹g½ð áq©„¼8yoíi‹Ý >ÐgÂj'¸XaÈ·[þ.(S$êBýj@q1+ºÛC9WŠhV"=«©j£{¤gDs¨(âô¥ž*HrÐÁ´¨Ã Ö„©t,Å4iO-ÙÓsCnÒÀzy™Ø
+¹
+#Ê zHÛo±©a˜Ú`èÅ0ãxûÑé|z€4’“Y‹'!{l?
+uŽI,W®8•oÔ+ãÁ7Iƃ\]¦Ø)Št]MÒk1ÎŽ¦C]h:ò0Éœ$šgaYFü¦LþMN¥¼[¸áTÁÖÎO‰ÅC©"T·üàA]žÂ¿D‹iôYý
+›û$Ó;B‡ªêœËߨüàºàõ&Ùòé"Äv%f2ë»sPÏK¶rTŽlOÒEÖ±?Ê é±Ò„ !ùòÍÝgÐktÄx&EUð,ÅßAÒ°VJž‹Å"ó8X›“²?€¾¾8:F˜'©W±Ó£°ËEƒ,K÷[4m%¿ÚÞ¨jQ*èZU.î:3Ut¿*ó:ÃN¨NSÙ! Ú0q>—°Û'Ž©è‡ÐIúôù"~8_“¬ØAì\=ü exƒµ¶ ^nŠ‹ì·N{dÐQU26~¬ÊBÉcÍ! ÏäÐf±ª”Œö¡6®Q™ŒÌ "º¥Ÿ=D/™%­V% •»CG´à×UÍO6‰M…ZõÙ§¡ý”föq?‡k]Y#A®í¦ øe !Z7ùÙO//L@AK/B¶! }4 cÅ€S“¸¼#Ö3B.iÜ%e&Oþó¿ †=žt)®›æ…ç×ý–?ùÿx] sÒ¤®Y3_ñÕ}ïKÊë-Zv“UÑÎi§ƒŽ¯g ×d >§jZâBG™‡¤q¬em^ˆ…÷w›xFi $3J½¯øUŒ´ßBÅÁ\8™]wzf&»%ØSQçVaR¹£1¾Tÿ2^æÈuÝZçÀX
+}ºäP©GàRF…š¿×h½{Ï+½à—?©Í à4»¡/Sw&Hë„Lã‘?Zú~ßü9}bírïÎʉ”¸b¥‹w/¹1DÅ…ˆ0Ã}¨éÒìT²ëˆK€$9Ÿýr¼l ,‘A$5¸H/„Ð<„Ýú¦X”/×Ð>kÜJ!—j¬ƒ^Ítñ±ý™„Ô¢º MED™awî2½n^¶—U™ì1=û ç aSAÉ.S±U¤M¼ûØš²œ—fm?›oÂEqLKdÚïFEYL›•5ÀÈÈGÕ=êÑÊSR·»@oÕ½ªœà#:§rajE²Å|Ä̵˜LdiIôŠ’Ræ+Ž`‹)Ì{ô¾ Áõ!åX‰Qm£Ð°™ 0(ŠQMn²Uø×TO…n²hW2 —2.Y¥Èu§Æžçf Š(”¹í™G²›H=§Ø± ÿ;L²‹0m¶¹úÉû|z>ú6æÇ^—EåcÁCA[Çîöué®bøï+Š)aýòÏ[xÿëÇÛ—_oñýï·ˆžKìPú6ó–õ¹ä8Õ,“W¥³°#e×}ƒüµúþÄÙC; )-Oã‚tÉ‚â”U¢…‚Sú1¡sÈ(N]1mHV1ªaoÑØF#1†pŒê±@ü"G´>œ À:ZºV¨1$dÆf&´1rJÌ E5Ñyhºòï.âXòŠ'>žæú¬ Ì‘ôøb¼Ù‘^
+ßUƒ4™L”»K<ð_…¿Èše;
++“É|ËnË+Fâ¥ÐÅ|·–â"Ø-ܲ$G,E—Âý¹ë
+ a5B{LÜtüÈO»•z„&ÕàæA¢Œé
+E<*f$
+UG‘«)Ê1oÅ6ª4…£Î­çÁÿÃ%lê „ƒ†V÷]hì 㣿4ÂÂÖòWLCôµ&(mpüº;ˆér)%H"®ç‚`4Žæ@ñû0 %jŽÈ÷ÃÈ%Ìd
+ëÝE¯ªtÃfŒu.È <Wêi<*¢ ˆ™¹™2-ø\ûØ%&µ²_Lóž‹_ã¬H…}š¨kXKÊèTR`yò¬˜Ø3KœlB‰èLTðaÕ‹“qÖQ‰M†ù¨¬eœ¡%`R•2Äù‚ˆ‡9Ù´×›—™íͬ‰c—†Vq¦d$Ëä{s@2ÔÂæCùß qEyí¡ú8”Y”ʘ#ZzÎÙÉ%‰«øºit•}ãlœ½m{]Ò0‚Íàh0=Ï”¯?=Çži“1Å$fõvÀ‡û3ô¡ã˜8ÛÖS+âI¨F¨I£ÔÅ%Õœž³GäÚ^Ž²Þ¿3p-™KÆv´L½Eìç iw{ƒùù30¨ Ôeõ­0‰ž^Ï£ò2ÀIc°KœäÀµn(¡ésåò°GcÏx5%šuÖ†`/IpXÛFûcî“å@€$6E¹zŽWdâZkÇ
+ˆ=!ÝÈšLPg9æΣÀXR„J‹¼{Õ–Ôj^ttí ]ûi ¶Ú‹Œmq
+æ-È1X…BýªÜ£iÇHòa,>ù,Êø
+RèøŠ€{ok ë–YYÄ­?¡ƒÐ°µ Jž‡Ç "cŠ‰ÍÁNbî)vªÖ¶¼
+ƒY"½~Nbþ±,Ø…ýïô}íç Š×Ët¹Xõ8\\Gq<Õ‚Y‹þwäÈ\¢×™.¶!¥*mÒ>ãé9…ÍÒº­$W&Œ)%-1r°ž˜‡K³(è·Yx $‹…OÒJ (cO†bæª$Šô7O…Nˆ'£f-…ÒÃh¦šªO¹²íE–E¥f°À ä ˜mÏÅÍ`)ÜŽIÆ÷„‡£\8k(ÛÀ©[s vÔrÄâå4ýï4Í¿¬ ×.(LG {5ÄXœ,;Q¬0%@nÈ°(îÛ›rûÀÚ¨TEyu¾
+R³rQù”çYã(àsƒ’Q°… p‰Ø„æ¶
+
+rÛ©×lQkù‡—@W^þx…¼°vª
+•&Ä`Û€Dr.n®Txró¼ 0¿“KJÝ-üÔ¸/=Ò=Yä²`é7Õ¾ã³ì+²*xLT¯ïÔÐe2×”Xö£î¡)üÀ +SÁ·FŒ7ʦî¥#ÿP™4mC–_™;‚±â$h>Ëé>pâÓ®·ì#Ó–ð~Ç'<€>¬>Q›1{Z!å
+Z…Ç¡©©Í2/V))‰„a´3v´q¶cé…Qq‡ÿ³ëƒéÐ D0GÝ Ü%ÃΗ겡‚0­±#˜"ˆ å&Muö!8ª$å­õó¨+3ÄBËÉï|•e8ˆ-=xÓó€ýàhHbÎPÞB›œøi 2Õ}ªwhOÞE•*+3
+ñ®Xï0Ž›
+A~™Lƒ ÊCRÓˆ1>ªìÔþ΢ž.0ŽáB2‰ÄÀ=h|‘ULùôdÃåÕèbµ^3]ᇓ¯ßa„!O ÆùÜ£ƒŒTÙ†êc¬¢Évš‘»1©Fk+êD t;ßÁ;G¢I+
+(Œ† ó_}D
+í&kzЄ’²&)Ü^ÎAS™Å`l\‡Ñ¥Ô½RÔn®Ûz7È@²Ù>v-9—3® ïO@Úu¶˜µM;›ÝA’ËŽ”Â3 Ñ‘—K3-¤¹x¥·íœ¯t)*2Êpl=œÊ,a\+Uç;ÚF<'t¸ç‘æICŸœlŽÉe«¤1ª±£*»å2ž}ý"Ô £#ú’zƒ`5°Œ«vÍìÓD¯$k˜ ôá‚8,¼°¼°ñBöƒú°ÁVA\jÑ«HuC
+t¾“¾âãY­?/£”–)sÍ69Œ{P`8‘!ÖÁvÀySþ
+¹@%ŒW3ûCÞc?H68y)Î
+„ÍÕ]–¬!Øèæ!¤Èªò­NW÷gø ù±V†k¶@(Z` á‚æ@ì Š[2«M­üïÈdB E¤9Ý»ð V·DP5>{¿£lþP sK£ßlíçÆ—ú:ˆß»tºbPáŠø~i¾Ý…mÃC&i@}ò&4ÍÇx!~ç¨[i83dîn¾ûFwp«¬ž¦ÈoÔÄuí÷ý3·™¹_æ6{÷G93|-Ž³×ÿaŸ”ßp²h‚{r¬uëEÛŽCú˜wt»cn»¼@²gl|Á½ º˜¶ïîq¯€0 xõæwKèï ™½ŠŒJ[ª’ Ø•J2¬óìV厄ܙo VÊÖO‚›aXl·
+üÛ¶!,D,·,ÂÃÐdH{}”ë##%J c¸[Ö]kЭŠŒ3ÞzSžXŒ#“
+¥%íˆÈ`¨e¯'¤ÐlüË1V÷t˜ùwd"³YÍM¯êi*ê|8'cÙ ™¥•j-ÐɈ½eø 2õؤM³N¤üÜn;˜I¸©g±R{ç09c»ÿ1åE¢¤,£²®ª/~I#F$júçT¢ãp{>}h(ìàÿb>mä
+rëC¶5@Œý;›üäÒüßø{Ó|­¯N›Aâh^ ö–èžH¾¶ž»(Ä8\’’qòd>V鬦•9û6ÏW:¬Ò/„ZÑ?¦3®\EF P¸Ü „È¢ôɳÇÃp
+g¿)ÜAv°¼œ÷çå6TzV*·¿ 6I$Ç2Ç1¿$JÓ™6{]ËüAÅ!œé@^Ù=*{Š”¨eÁ?£†¹ÂhC ë‡Æ{ÆlÖÿn:n­÷Ì S&W*>j&ˆ±â_y¾¦Š›éÉ2j§#íu׫Mü‰ø]E+ÃÎêá}“ÌQdä:)ή1 è"öÄ >`ýõÖ«’¥Ä=_?е÷_ üݽ‰i_†d"·›ïÇê0…mÈ¡ÛÓ‰¸g.0ƒTpmiQ–¹BI¿—‚éX¢Ì©|¶™Œ¢Ò¤VSqž]¾ Ì7ˆ­r–‹cžÆÓ9ë'ˆ ß;ãù\9v«ããlJk¢Û™Õ4à‡ržyœUßàe~j•¡’r_ :ûØ-+{ òVßÎÃ3%n™¸î”ˆ_i|ÿ<Wý÷vî4T ‰*2 ûèHÅŸ *ü†>ǘ\=)š'ˆFVØžžåb µE$ Á#¸œé=OÇÉ32Ýì, açžxêǾ—À.À i2• P’Ѭ:¼d²FÎÁê6éH°êǘ$@(òu~¨1¤
+8 ¯ "3È·Í#ü”ž
+HLXüŽY¨,™T U÷Ù@âdy(ÕßQ
+à÷0TV4èÙúDë¹9¯)z€ÀïIÕ£sµòºRÛ×(•“ZŒi—½ïJ\â#å¢>¼g+‘v†>||Ÿ§ÀoÕï“Y3Ÿ„°cÞ[pW[+=ÒHTV9plõÕLƒ%‡‡7žÑ„J9 Œ ¿ç&#²œÇê0σí`Ô§I«æ¼(GÂt¦¿ü| ÍCÏ?ïË¢¬@tEGizµeyß9YDTͦÖÍëö^A_êÈ‘jO–]¦þQ¥AÍ؆&à¡:Bí™Å;!©ß£`9
+Äýßç™äÓ1p86FËk|Ã蓪ˆ[åd­wxï¦c'‹<]RhË9 ÿÞ@-8_wÚÐ öŽÂ„f·&ÓÊ1SCŽ«áŠê¹ªàoÌBX³”§xÀh0»ˆx0RæÛ*ƒ†4!Fá º
+3פœFqhHd
+Ñø]àªÛDaÁê;å;W¬Æ´W3‚‘­náÁý$î¼ùäÊKæ„W„-ÝcäE™x–Ô
+<PúÏâ,#„b¥½v
+Ú”ÔL4¿ž«”h®l´¹†ŽÔ`èémÝçDvonuZ)b]ÄòõéB.óùë‡r'€—OÌœYä@ÞöÝ®ûôNî^]0fÚE «_x´ñÇ@°V%`LOnµ¹Ø³“<¶%fqk²R* ã0‘/M"Ž’? _ÏŠ,ÈÀXMˆŸo}r+ †fŒ°<¼È?'ýŸñ29r#¢¨òa,èÀ–XÎ=ÇöBWÉÿë¼_ PÃ" ¦"¤ˆ³P
+ "FœÙ¦¯“ƒ
+'uJnÎg÷/ 2 ‘eÈcÇÔW‰1LXúfsCì\8¦LÍ
+\8ŒLÕ¼DÔ°F÷HøR¤Lˆœ¡} Xp¯ôrÇï!¢:@Pq`lŠènq©“3“=0÷9æmºg"øСõ˜éRM¼u…Ë»>§K5Gc•Glº¥K5'Ò=®,?"ÄÿK&.ôØ•jÚ>¥^þÁ¸¬\°+ÏÀj`_^'æˆðNš Bó º*Jק:σÎÛ˜l€èǪѹÔùÂÑQ­4—Áæs3èEqN½CÐÍ)á(M`²#/ÊðnòÕ//ŠÅiäÅ;t’l#<(°qx$6íIð;2ý¼l6„­‹œå[ù`”"—¨þýžE?˜Gy˜¿‚è—ÂIòb9††•³]¶­ÊPg`á;2l›¦œ±ÎSÉÕ
+û°*›Ô¸ÅxZ''3°©M/Ž 8IPwYQ*"ÍÌb‡^ÖÓîÌþ‘oŽhçrAS%pÌünÑ.és­ ZB‘ý¥èyãL
+1­#ÊlÎg©Î?tˆƒÍ#($ø€n‰B¨yº©è¤ÅzÌ3êjÆ‚YÛÃ:`´v£ÍÝtM˜0ï&úcMN‡±>⧟ï
+H‰Œ—AŽ£¹ …OPwðz€¢HŠÒ2è,g›42›T/sÿ|”ô¦lyÆ@£»Êý,Qäãã£Þûh.^šÕÛ?ªÆ½7“Í›Ü~~ˆ©>`úÝFÓ"¦·Ûï$ªÜ›jÅZÂLî^†E¥ß~-Œ‰Çh­jó…á7ÑZZ©· ‘¢–‡Xi 2Ô›ʵ@긷QùLÕM Ö^9¼”¨¯Î)^[+ææûª.^ͬKŸÑÔN:F-C{èØj=¬Wbc]Õg<MLJRÉÜp{~mB|„*¹”õðr.GÖ1®«øB—ê^º,qP‰0WRñãã
+˜zø0úÒ¤«nˆòêB­7¢QâæÙ»­/Γû¤;W‰êô¹AJe úÆØ F…–œŒš62Ã¥5­¯Þm ½EÈW홋Êeä5βAŠCÉŸH¯ý,>‡j>‹X£¥Í_œãpdžwÞµû&ºÓ#Íz³¤¸Ñ¤q?ŠWrÅ÷›üŽGhã™ûMÒb 52{Øî£÷æÙÁëÿË=ŠðDôŒ"¼€”Žb‘½@óW-AŠ–gƃFJ}¾
+lE,a\‰~z6þ_HSƒ_ïþ–=úœÉÖS„¨_¿JR@¼ƒ[8²žJùÙ¥Ó"©çs²_Ð7‡¶[™†ð°rÓÓ»”¢ªm¿¨ÔÎy|cøÅÎ@îJCMÃö´öˆ#­âr;ô
+¢7*J|»W¾÷%¨Ù9ÙpáãjÌoÍûˆÙ}Y¹™wy_w ¯Yo´­í¦ã/ÈT»úÝ'žuÝNz”åîèjrBì«Y@â9Ñ÷Ëëì]]•ŠÚA¾¢ aÑÅÕŽÅ|€ì«*Y'żÅÎç8úõ­eø;T>2íÒ½&×íWµ¬/$…p~14=E‹^ÜÅ l °5¶eE3R›;ÛG\Íðçƒüi/‚ bCV9•„þ€4•dÔUÎgLê£CHZsßBavÔmc ~CÓIÔýn+#u¡l=Ê‹-œcz?{ôÈcxðÖµ
+%±ZS‰E8Žº¿™?Öd­wh‰¨]=óg‡ûkš”VnPtTŸûu5V%99ðkå:‡H!/áªÙy6ÒÈÏu/}a†z>‡™Cxõ«Í -‘Xˆù((7¶ŠJ¤ª®š?‚Nß“’bௌÎèh:¹ŽRô”mïú82G.:,cà.ËTs’$LÀh—p£þ:JêÄÊ44$ÓBˆv\Š1
+®¥âkü
+$øM"§È8@æUlv%%‡á~MºJsžµmj4JÚ¸Ûvž g"U51¥Õfw<bÒ¬tzS¢-³rÀà½ØuPú6bœæ>«½G CHÙ$P>‰íØH•Fà~wŽÒ4™²§…uú!»0æ†DúF¤È–:Í®•a&/µNF¿o³RDV"÷Wç|¯÷1šï¼9>ꑇä<²øâä[¥~¾Ó2Ù]åê.a"±j6Xt8ï× äðΙ¤ÑV\hvol°êõRiD÷×öë¢Y’"m«VL•@€_¡V)¥ÚÖM&{j-"4çn{A,&vÒ²SírËØnð#xÊUûçs ´[A8ñ¥~Eã\KŽb:U ÄÝèstwÄØ’;ZŽ GÌ'WvjÎÅ`c[KE¹gObàñ‡;Ç
+/–f¿#c¢{ÃÎÓE^EšØ Å/zÌ À(@‚uă^í Y1y7sRÖˆxl©TåÚó;”dïªÛœsJ²ë P@Ôðrp-¿N ”±’.ƒ¦lk#†ŸÆ½êÛ ƒµy¾îMq'ƒ{ ºÆ΄°ÁÅ¡=åmBÐO0›¶)17’´ (UjÔ‹Á§s*g_Ã
+™D]oˆ<.F¯G
+3?Þþû!SZžP`þÉãh³(Epû™Ð©a ?mtˆ¯yý~µ‚]‚¬§ôŒ'èùºGÐç;1-‹òÏÿ|üö?Þ‰ÚÌ—³È×L¢œ¨·ô»ù//Õìr FKÍŸk…C+)ç’Ë㯭”=GºCõßÔÒë¦b°ö·AŒ
+Ɉ‚„nÐÓuoxêHÑ);Õ(3ÄÛƒvÐw1ßßÿùFŽ¾6/ÈÄ´AN<;+ÅÎù€/!lظÍÜ…YÉðëÏ((ôõÙ:‰ÀwE
+h¤H†®´Å† D–¡Ó¬%Æ9*À´–a’ãNX„ØG&íl£a)ëB a§#رH'[ȳuCð4¨`Ú¨qÝ„W£6Ý#vÀéÁ›:¦-LHÁ”gñ%vbFz°€¥ù†à;z^T&Dù ¢M#kë* J£•rÕ…É%Ò²YŠÍÔ€Àlz¥‹bz§…hp¬A¾€0gZ,—ÖyŸ‰d!J·èóˆ´Xƒ7_Î'ŒÎpØ—aWJÚÄÑsC™ËÞ,¦sùÈ—&:rÓ¶^ž'ckÎk©Ê 6f5‰À`Û:g–³°Î^ìC˜+¤Aá‡/5á;xi÷ wx•õë*åXi–†«èLÊ€ YçØOtÌ·Í-Ôʳ2m“äÿŒ—I’¹DOÀ;Ô h˜‡5u–^Q÷ßê9‘ìÊÐ$©M좀<|àš2ä+`Oê.Þ ÃM ”D{Èz¯Ch{iB/ÓòבûåÅ퀬Dx‹qL„ŸNò”Ê+R“ Mw¢”ÜZm=$öÚðp®Å0¥!-ÚV5‘©¹T¾s™è@Tâ4«*¢ü%Í>ß¿—!Ù„¥ñ9oNµåõ“a‰¹Ð®Î ¡­±쾓rú
+?¡xòj¾SàZPbbÞ Î¦NŠën¾.0¬¦ „ç­d*q>B2•1‹! gÏDÀ%eå…²ŒŒá¡ ±µÁJª=p
+Lhù¥`é&ø‰‰ßßœ¨„û3°ç1’ò HTÌžp>ÃÔßC™òö7¸ >ˆ‡ÓöâP¯.áŽ+»}‚øÒÚV£¨¤ÝߌüuESˆðµ!\'‡‰ô’Á]@lié± 5Y¦ÇWGV¡ e¿ ò1YuÉúIˆÅ¡°ë½ŽŽA:¥MKãЄäŒj¦nl¼U8/ýK˜„ŒÓâfu+úÞ〠¡B¡çèïePÂ
+Ù,þÙê bòÕ(%—eè…i-»H~BÚ²C}Ç|ž3KójJóòç„&D*h9­iCøD´‚óD%5A‰ýÐDH ¨vêK<vЕ>bŠ±óÿ©øy²(*fü.Œ_ÕØ{òrXÚi¤—8#;2'Aëqß©¡$8c—|¦+ÖºA‚!7&ãëPE¢ú ÁÒM±Ý
+A(ÍÃ8<æZ加l®S#]ÙÂ
+*×ujÁQTGö#Æ-¥Ú¢%¿¦ß¬ó¦9ãgÏ“ ®mJ[볊íCÖZ“xé³0¥~„œøиðÈN𸠶N;ã³ç C[¤`îm¥ÚC»iÁŽÃ´s+uZ ç8
+\*|DYƒÐ?I\!øå‘eeCäߟ=i0mQ½ºTö#Ðòµ!Hm×ÃI¢i“ø¸ðŽ^HØë.IA™~àV¾S$VP[Gðð‰¢\±úbÙë°2Ëâþ
+Qa1ÃþQ ’wùEnßž[¶J1Ã÷ªoU{8rôóðF,AØé-íuCœ/Ìš¸-ë¨P>ÖðÁÐI Sk…íºg'tU.©ù¸ Ÿ!Oêf–„S nœß>|Õ˜ÀÊ\9ßM5PU¶ë
+g«ÝaRevs¥€Jj:c¥ \±Mh.ûoo
+eHº~)p@
+C+ú¤sA?AL”¢nŽÁB'f§º¿ ¦!ñÍYú.p@ ;¡9eלÇœs4úÌcBÖc`ÆÓ«¾7/
+ó/tŒT7Dê_”£“­Â”j•ÌcFߊ_#Ëí|2ÊéB4¹ ú–1¯èÔòG3
+ö\~[ó)¶èôÏÅJgda¢ä®éƒ’5²xxì
+šÛ]ÓX¤(GæÖu$Êð"ùH¥ :½d<œóv‡ ýÛÿó€aøÕÄ•â¦à¤ãÞ‰Óè_Õ§æ*Ï)äÓ‘!"P•f¦æ"Š-i,’ 2æhš“£õAV”šýPýzŸœoáhÄ®,‰ÃðÇ°ýJjÓÍ+Ï08Õ‹ý)ºÞÓ£‡š·ä³!d¬±`wŸÌê±ÓgÑ”÷h‡gÝŸ\äW餶Ážu0ÿ4‹5½¢qRÔys³Ò¢{* q%¯ßÄ®*H*Û˜òÓ9Yëć—Ÿ¯¥ ÀA«ÚãGá1˜ë^™9ȪYj¸ÎÊá±)oþÝ0RÆ¢$¾ AUà5ÌÃ
+{™‡Ú}uêf-
+ S´m`ðð•Ô'òG_FnÔ"2‚œuX™ï£€ˆY i¨Ddï%Ðåyuðïú1öl¢È¾™ØˆPúÉÞS?ªJQ¬‚,97‘jrð'lˆ­C‘5ÂRõË"Ã08z »õí–Á‡+Ûu4ŠáÌ”ÚÎÁÚñ9bþbd 5ˆ$ÅBXP„ Šµ~tãIEføÍ«åêç}.‚”
+ÇWyViÊ6rLV¿ {6m´í ’@ô6œ g?ôyŽÔZJÓOÆJ²f4uKFcÊÐñ%ž*‹¹x0‡ÛBT¬L¦n  Ö+>SÞ%ÙÇàö
+D€èðŸ 7æÿúu•¥hM2¢¬÷ý
+ 0t‡q3z£*ˆ ’[O w¿Tý”´G~!ñ:°®æmƒ>ñqø{xül(¨ñL¤PVØÕKXéÑ<©>=ð1¯ÁÝfRCšB³y[ Èï“ß<U"§8Lòé
+íHX°éfÎJ¤y°eŸé°YÛLæ;”§@aƒ’”›âyv>,,ã(vß,ˆ©Bu†OÈׯuY|Éuiuì¾Æ°gg ¿ÿAÊrjg×Ì=@i5G&r«u|QÏ™é¨~±=f;5‰ÛŽY(U{“óÉQ®
+Š2‚Û¦Ný:G3Sv¶†1À´ÅzŽ+À«B›¼o»
+õ,aã§s²ÞҖ몬°‡tÀà’lõ-‡ÛU´‡«ŠŽ3’áó8*—Ï}NTîò戔Èw2vUÓgÂÔå]@ºŽ†âˆ6¿£·üU½*¼% ¶bAb¦Prf¦|HXbdÝû†Œ‰DV}0k¼AM,÷fÂba‘5Æ"m ïrqyqÏ‚@Z‰R,",†1ÖQ®9C(Ø'±Óså/jNᬖ~ ºÙÁï'Ð[†:B Ý.vËä–5Ž˜7ÿº‰Žìh·Ñ) 3¦L:Âûe¸Ýÿ¿¥¿éâï \¸ç9ûÃ9´wJ~Pª'¬Íë˜òxS„Üh®2Ƈs˜½K“Üò$ eBt5¶âs‹ãdK;€¾ „ˆU‰Tñ|™ ³$è¯q~òòùé'Ð{ˆ{Ïv»aAB<O;ú0qd±â£•FÑ¢Ã)Qa‚°/ùº N‡ËG¶‹ž„I"6vç
+ˆ9‚ú±G+9x‚üM’¾ä.æØ\ódÍÄA",I³u¨‹‚6eׇ¨ìÖÙ3¿ ¾LXnÖ¨%#mô¢/Ir2Xg¾;?êÃÛp-ÃIWd'rKº‘™L3Ôä¿&°š¾Ò÷ç9ñ @fä³æ£^ñÑ`rÝ.%¾
+ÿ‰X!Íå0èv8<Us_€ºªEŽèÉÎa¬gÉ*½oy[ñˆ?yCpd·Þ]_±tÈPŒtò›PÎÑ¥§þbª„0THyÄ}ÌTê%ƒ2àN‘^C–`Cø•¬aѹxŠHyo1}¥XÜÕ²úMÙ¹˜–{}”*ÊcÐBŒ¨‰^åÅ|óö€ÈmĪàÊöÉ ›ÈœýÒ‡ý«YƶÑ~cõ«½¦Rk!¨ÐÑ?G î‡9 Εš-Qël¶ò®ÜóÏ ŒÊ²Yð ùkCÚKEMˆ#þcùŽ–Y'8Œãe?‚üýåß?¨Îªáïÿ€ã…ÃOúâZ̬ ÝG
+Š´@oFwCHMê¶ù+€¡‡—qn‹1‰’ùI1õü cšDÔp˜ÐÍQX02Ä•c’k\bÝÊqÑ­£d6­iUQFDëƒ?@> þ~$ÓµÔ]%;ÌH]±`„ƒ A¨ð‡!t·° ›†ºnˆÂæ
+B•Ì”ó^&€‚ôdç[ <šœþáœÒš7_¼ˆ·2ÒLèVVÛL@Úö¶0Œ•B±žiXùPX&ú6§§§tuŠùNlnÝ3QH‰—ÄÉUò¸:N@µ­²•!)2ÛgSÎ0­y™¨OˆMe€àªÇcÐzUWÎÄÞ‚“`O²Í-ú‘_À¡Åðø‘´tÝD炤o'ÂÏO1¢$ ù´¯¦Úº @;RÅküE›×ÒÞh¦¼ÄŠxNö¥½‘-¢C½ë”2Ž¤W/ŠI‘àèWÝÈ“a(òE̼ÜÛ‰‚ †(ˆóJ©¥#äFå_°~pÖ׎³WˆÒ<ˆ¬±>1ω})ž «ˆ¡Ê•ëO–›¾t›A’(ŒŸkv„ fƒÃØCs>lwÄ@y)çpéÀÉâ—X!»J‹¬4ÿ;âñëà«új`ÎW¡ï Æ>ðK¸ÌIBøÔjþ]‘c‚?FqHTÆ@¬0Pc™t‚y¸ÆçDÕsK<j稧* é|•6†žñÑ)Ík0­ _ZÓõäL T§ "9Ș^@ˆ “<|ñÞ… ÄœºBù§¶ÆD0ª>ÜSY¥!SÌq… g°›ðÏ ‡¦’)ÌPøêJF'ˆÓ~)š¶ÿ1^&¹•Ü@=ï œ‡µ¼ì[ðJ}ÿ­_™%ÿú, á6ЖS$+32†ÞçÓ1 J…Rú8?¦"k|Ïh-=}SÇËŠH(‹v££™^„¼ è
+è¶\œÀÇu³4r
+¾AKƒœËSÈ€¡šŽ+40/,Ž#—ü{ tŪd9Ňc*¸!=ö§Ü2ƒ…*ÛãMUFR ‡’<SZÁk®<K%Q›€Q)>^Eï±7yÊê=œC\ƒ¯@bü(ÅGºœõâÇ«"se¿­øÔ?öÄÂ¥<•¼ üëxU >ÊZ€Xª>~©hb*!C’Ð,^„YE•£H§ÿýPÃÖâÄšrØš%¸.D)_Ê‹Ap"·Ò
+Íd7’UÁ)M9Ó¼ƹ׸òÈSÅÿ æ?âBaX)#©Á\À‰ìhŸžn ÒOÐÕ6«oED·&‚œÁ]«Ñjˆ…5ñ‹n×}ÿÉ›ÖÓ—×îèÔ"0¶ÛÉŸ2äÂ)èÿýPDraþ¦à°K ‚Š¼T˦̙ÑAœmQ*¨æs0@<µ!Ë
+†}¿f?vÆÅŒÄSîŠ+
+»‹Ð¸)жjฅÂràk…áw€‹€§h§˜Õö”IcEV‘YSv!Ô-T`‚„¥•µ›|#ÀÎÍe K‹wBoCú8m¸ÒŠReï´#Sмõ^@4%xgŠ‚è•ßÚ'ÞbN8S¦È¥|ä?2h."Öf>øFAŽgHY4{ñ‹Ë§¤ Á×}œ+n”þûgÿÀø3íRLAÞ
+G|+¹ìÔ= ÉÞuÙÒƒ»“Ú‰3˜éà#'°-! æ-êÀ×ç0@Öbœ%É” ÿrœ={IvSÄDÈè$c¸"Fqˆ}7Päà/·#ñÔ½yü§¬ÊE< rÕ½7R/ɳv=W߃9ï&«Q¥"©<”b@«‚àø¹ê^´¢iD3Bjç0ET›æÀª%x\ïÊ&íñªÈÚ(ä)–aGºÇ’[wb†>옸ñ)ü1ÐS±Ò%GïE)‰Æ›óÀÜ°²4ôÅùPòmü:~ÓkÍ÷ÑFóQlg`c©³ï®$S *„fýƒ´pÛ+ðÍÛÖ½R’Oz ¾˜ŠžÑ>o"že¢Î&PµTfÎbçèb´f¨¦
+»7J:”¸ |•Ó9rš"”Xí«؆øIŽpôëx>B…½0t *úÍ~9µŠ’¿ŽG† å¸ ç26Zؾ7œþa©Ay‚úQ8µ.·‰8Õqò‰Ö›-@J‘
+¸ŒvógD+½™a&´†X …ægÕJè@JÚ=ì¥_µ,26±dw pj
+Á4Ùƒ…=<bfê̬‘\&*^.![. à´l–‡B›ùqu!ã¼~€bŠži7>j… xÜá
+ÎO´)lm¢ÅS{—M”ç€Q…ã$fkS„ýª€‘í³‚Vb/Ú“mñd[K0…oÖðØ|H¢²ï¸ÔCBv,K:ƒyH|ŸÙX¸Š÷yVD‡‰³¬ÓÖÎR5zL(±#$ùÊòm›)0,a@øšDØ€´-ÙA¸MÐÌðþ¿£ßiò­j> qn ã:j¥OÙ‚ C#pÉáG1Á“ý‘ý¦ùþ*É^^÷w(–œ•Ã¬ßÉ
+ýÆ<Z“B€ÖšðzÔ#«Ô.B¶p™TÔñP™÷˜:¨â¤mZÖ
+BÇͦHM"ìýRʹ&„n1àæ¿h¹&•$P :³3.Ë\4´EYHÑrIߘgöLÖ&É¢¥¹OMÁÁ}O´"»É?ÓË·,…Ý»+.Î:PLN9‰†ƒç}×øûJxŠO_-ñ2jIo »1’­©Zn[#ýW´}d#\NÎÈn”!Ö]$¯cèSmûòQ/Ò¶9η¡½ø¸;Êõîû£ˆA­›~µû£¤ÕL,ôe}Œ‰ÃqðŸù ⶶ$ ½öJý}§qLüd ‚H–Ôf؆ÀÈ4í'1ørÉþ Ë$ å‘ izN@Ã_ ™Ã†ÕyoIqŠhb\ÃùÉBY–"-Ã7ü¢“g›Ä#ʹàžNÄ­f ‹µ¬”ÃÄü:÷ár;dÑ ¤‘ˆIóät!ua*²©:i—¼¡Nó¥œA)Õ
+˜›Ä`"2‘FH&¤êŽ%ÈæËcjé†Ç,¤?ѼæǺù̵]Cº˜©Á2`Ö†F~ß/Ô¯»/UÑöÀZ?‚xRŠQ–<.tUóÖA6¥ý5VßRô<Ú*û]TÈÔØžLE"ê)ä C§ï°Er5a e8‘ƒ]°åê$ªÐiµaÊR-. ìÚ0 ÐZSh7¬Ç*Uå:´Hoû3°É^ ²:ýr“ý?ÿ8•egM¥-ï×KPK6tÀ¼á2…@Òø-´‡<“AœÝ_ ]¡ 11^ÀºØ°†½ÈdaˆÈ
+ˆ,¢ÑA?_‚ØïÐhØ‘¹ÝAKaökx bS@áC…h=$Š®‡LšžT³xÖo›_@Ä”ÙPão=p8îA…Y`gÇ êÆ×cÐr\F
+A"Ë%9`dc¢ÆuXcÀ£þ•ù±£Ø*‹ÞÈ ¤X ö¢ñ%?U˨ËØ'‡Âƒ´9§ªØQP{Áos{ÿN_µ¡Ú~ª‡Kyœ%Í%ŠÊ©ÌáÂÿÞ Èª‡The™GºZ=Š<eù™üϽó’#Ȳ1«ƒœÌ¹«b›^Ÿ17¡ûµƒ`=´¾"i‰]5Ûm(G5êUô&ÝwIüƒC”à›Ñ&mþOQr‚¿!‘òÉ!„°<,¶ºñ¡»ªUK´Ãrü;Ÿ¢ŠwÚ†7Í2Ž ¢Îé49B?„ØQdóë!ºGÞHDM:â ,_G}º€Z}ÐKeŽtò Ÿ¼Ï±z~†ÏjZð°žn¼BØrðˆÎ–ëî3Àô?1)ih¦vÝìÞc*a‰wíÏð+#ÊŸ–¥k8%&ZÈlPçÕ7‹SೡúI!KOô€º6Ø“ ¯©%r0(ÚaywŒ;2‰z 0ÚZsèa»ÎLeš$ÒqVq}Èž˜uÌâd
+²‡Z¡õ»‡ÙE=rö?íïHäo×½º*A\¡Iäáy(9rzm{
+Üb¸.ÄxžŠJ²ÄðÈ4M¯à-KÝÖ))ҽŶW2áz—´´S”µƒxN¯ç;üÃIâ…y:°Ð9Hb±á~è«ÄL¦ì4ÄÌGEÿÉ‹_<´’`3µâ~ex–M1žÍX^=©*ÉÎÅ8Øõ™’‚«Áäw„ýE…ã¯.åGW¨¼aœ+g¼ Vˆ_È=œ’.öC]Ó–pJ†ü„â*õRâGm±tÔ`ß¾c2äIëqÔœÒ>Öa[--r‡ºùc l}
+÷2h/ÑÄ·*Ž@‘:2DÈq†g”dæNQ«ˆªò|GT¹°ýùxŠŠP{*º™ò‡kdí
+•y++5ØÖçø¦R;Â…œ§èŸ~Àט×6A@«XyfÅËi<±âÏìq`Q‘ Œönzo
+›á‚÷À¹n5çm:A²jÑvc­í¢íù6÷îðѦHR\h4.]“SÂä*£`3¼d@
+ SÉbjz¹pÏZ[ôÉÛÚ”/aîâßÁ ²ÔS¦Ú»ƒ·ƒ+„71Ü¢H ’?
+ É®³FØŠ Ü.kXü$„BMÖ²“Š–wNÿ ŒÃÐ@_íWÖ±³vk÷¼<¬#YƯ°Ó§Ñ–Þ‹VœKþiü‘<œé/0kÖ·´%dÓMø›ÏQoExÝ—þá;¯¡ôRòžJ/EY›@ƒœ“±hÞŒ"VHϸ’)¡PÃÉ¢¤G‹Þ‹Já°Ô*ùm³ä÷’?…ñçõMßk~]=4 “C¿{›%%Óå©@Yì*®ªä«±µ³QÒNk÷.±–àvF>³X¶‹ÝVLÑÞ»ùm¡ŠpöŒ'u*éx_Hk<‰íÏ?êE?Þ¿ƒzôè;=ò£XÓŽòI‹hgF´’Ä,.ÌÕ€(’‚7m sp
+ƒ)JÈh²¬ö[h'ݱTÜä¥H'³sÖïðT›»9˜§!SQ‚á2™#sk‡T"ÇlÞ\ÜáöqK˜Âæ׎S‚³\Êä4©ñÝÜñÿ°‚ˆÅÓß—’›©½^¼A
+èíëvùp¯”Ò×¼>
+Ã?ðq{7êÓ?¥'¢Ö(Ù'KW÷+oI(5•£Sd1wƒOJ;ë
+vð=A8Ì·ŽZWœ”ÏX+”áÎI·þñaìtãtŽ÷9”¥‚l›«¯ö”5þn“XqôW}fÞ/B&HÅ£erI]&7Ò­<ê 3XÈ/ì£ÄÜ\-ËÆU|Ô¼jm¸1Â.ñ¡ýïÒð½æק–Ð`Uˆ«Âp|<Wƒ¶îÙ
+–{Ã;fþ-w|±b¢}0þ,ŽÿOÄ«²ÑÑ¿…¼ Büö©„ME+
+®>GélPŒ¡ˆräR\—žØð3ÀÀFIü=?” B>{ŸÔ°+I^&G øÑá 6œí‡yY:o íí™›Á[¡àS²‹1\õ²º{–•gÆX¿ýò"9â¤uð³8#]ˆ=û,*HNMä6˵5Tð÷)ÉyùÊIWØ%¹$Çð¥H¾ðd¾#ëXd°ŒJ°Àš!`Fž‡/B{1}06<º~?üµˆ?1Lˆ ó‚Zô‘*ÚÁš{ÈZqlm”À;}FÄÉ ü|hÅÓYy®ë.½Ê×îcÞA¤w£˜ñnì(J틇ó° ç¢1¬àâØh(…y"Årò·pöÓq ŠtÙ÷SBÿÆ$gvÓ&3i„H‹ˆØÀkug˜8*¯*]‡ªü;0¬ˆQŠ „(d%Ýå}™¬ÿ*Íyv\@7xé—’Ÿýç¯üõ¯¿!±X@Eö²»mØ^É4ûe_ÿ|(*Ï’Øý8•°]5 wJ²>T´[››TÂ#ŒFCðîPFï¨)Ծ߯¢$‘­v£ÃÀ²r̘þ/Ò–×=ÖýÃea¼ÊöB”à`ø$Éä°6%Y™ C… ÄIukr›ñÉÔ%a¦ø¸KD¿–571³*<èW›1šL–IqÖõƒ¥jœŽõ¨È ? d`kyøk—ðÝ"±Çqx‰%¸ü¯v*v¢KÀn¸/Óç 9Ë{Eß‹Þã…®Œ©å$$ÃÜ‘¢ÔÄÏ»1{maüZ”k¹>ÆÂÒ*"¹¯ÅÀLšÜïY2k¾)ô¢.Jî
+—š‡¿Jó4y>TöEPÆî ¾®²8dvˆãæÎ!M4ŸgÚ™¨làÚ˜«6š—0)D!)ônTúRKý¨i ¿‘¹~sp§¤Ñ÷„]},
+D ’¿Ó?”Tž,Ç>°ûÏQoE(F‡Oõ[ÿ$Ç d\!1;ÓK؀͠3 Ük:PB6¬º\ƒ,“¬  ƒâ.IäYƒa>NøŒÅ$b¬KVy`@‹ŒrˆqŠ/Œ§w‡@ã¸ä(A±Ø=aQ²fÁL“©ÜÁ)Ezx+’wå¿ýU⦶+·‘Ö>È™­¤½D G“1.^ò²W
+ir­À³Gýïåû›MGp쇆1[íI²´_m$ž=½!ê²Jèâ٠ü/Ã
+jéb£ ZÎ"SÀëÑ^¶¢8‰É8Áu
+[úg %Ø·©œs¸PÞDÿuì=ŒuS.›å6^ ˜Àæ:E&'P›0ç–XƼ³eZ ­qOëª7*!™~¬}Ð|ŠÅîõ!†ÿû
+Žÿ«ˆãPC&£ÏGº€ýd¡¸*!΋Ôc[²T±§]Nœ4Ü"± |ΩûÒ¦¬¸.>Ö)Aî½¢Ðä"/âÖ4º¡qõ|‡
+ÀªÄ ¢¼xŽEÎïZè®Ø9Ï^ï¶x¯–ñ븜ùS2Bxá{˜}_΂ѤÔP ƒÈÜ£­iæ Á4ÂD¤»ýÅ4”æÒÏ}
+ >ÑX®ýr¤™8ÄÛ`@L=ó&õw…DÌëJ¢ÁØT9*½2K·d×X4Ú )Õ}9’àH
+ßÜÌ°Š ݘs/¿‘Ù«v¶Dbð@e„PŸ“sY²gŠ³¸}A*CL>]ÕZ\k#aÛ0Hl ?3f‡âfÖþÉ8òCGÞê0±3J vŒ˜–Å¢ Ãê-L®þn Û`´ ·;([HCdǾ)>d¶ýêòe™ùM\¬sª…uó¥ÑÃßN ÍHÖœ)R{ÖïŠrÉAþ'yq._!Blp,aóÀåUûïWEQxã$w;ôi®ÎÔìÇ$ª]dÅ UMišVÑ:…ˆˆ÷¨žEd^ùCiÒÜ7Áá,ÓR«`„™4úÔúás“¥x„ˆ#KUÀ£OŽXÃåúšäéL3‡°5ÃZˆ(ó2ÇÓƒÏÚL-Œ#
+sGk Dœ®”VßX[x¾¨u­õ²5¦ äæ¾ìZ:ÅÚñWo!ô.陹 ¾žË Ð38ãí9HD2}B&”úÅLírõ¦}òöϨ‚šÕ†¬¥]eÌ&¿ˆ;“Ÿ³¥‘¤6L™ëC)Kð!‡~¢1Ô€ag¾±X{iè&^-*Jˆ]eÝÙ¬é7u™;-€ËôÔ÷:=1iØ02ÁVz,ì"þÝ2άy ·¶’épx\\}Šò¾Â%QœY'CÖâ‰xáïrõuªÀ@bÝëó¦¤BÖe[%©ÉÄ0Hçp‹pQa‚ þþ #”wŠHÿýñÇ¿>Âã}üñŸ<}àA÷‡ÉX á,íØt¼iyü:Ú¤±2ªòXÔaAúZ¥ƒ éåÔÕ"LAªúHÛz™‰ÚH3´ÅQ! MQ@2oŒrØX§açðÌ<33 Áu‰&‘7ãiÎâ7Q:º¨à’ÎçÀù €ïžç‘t Kq­Ê»GEy©‚²ŽÚÓ¹8QÖj.BØŸ|-ñrmÕ¾ê
+¢m\ƒ³'*>_„¡c¨k@NÙ’G‹!Ä}Ókq4ø3T ÙôC‰5$BI±ž:uAx£^ú}:äufŽ_ò:{Ç]gø^˜Ã¶\«û»…Ò
+BëƒüÙ#†°vc ÔmqX^kÇÏx†ÞúRÈÖøÀ‰rt£æ?ì£!6_‘e¾qÛ¶Áxsˆ™i¹²ZŒe±õI¸“†ø®x †ÏC\ø¬DÒY´šÐ’àænX—c|êšc d%•b+³AF€ÒÄv8†Uh@]t":d öIÊ@Øñc‚”O‡,“ÑlŒ3
+E“§ÑÂ#‰+†]ëžN‘½¬–iN‡4TŽ5h&Bšº¼¼Œó‚0 8™6,X
+B‡ Ô!¾>NÜ'r‰ Îÿa œ4–…ÞR,Wk¶s½ŠýE/Ø­t
+º:Z¯që
+IÎv‡x0Ýꯂ5Tä¼^T«Â¤Ž²X÷¸¦3ËìF,HëÇsªŠÎÛ'ó}ÝáF,fPºŠ½ªuAØ”sÄ®«
+q•Æ²¿¬¾dÏ~Cy‚~Ÿ‚Ž@q½îôù799P´wh&I
+v´, yÆ Ïüƒ×C3Æ©åz³ÌßœªÖ´8$±©TKÑB($b Uê•ù²àŽ:šõŒ}&
+,1`:›OVà*ð
+55+0à=¼9úR$R B³Wi€´yq5•Áó<F#ûnwuvRÎ=4­À˜(7Ð>tòGˆ_Ģđ“µ=ñda–x6Ô¶kñjö¡!>ä*ÚoJ²1Ôœ™Ýó)°n'›<(“%Éb6_ÍU¦„9z£Àa¼žžBªè
+<£äÝëðlçtO†–QÎn« rMÌÕ_WafÇö^9—†oâ©ùí@ªG«((ãÏhÕ'¨¡1ІüÊçz¤ÉíìUu‹Ž¸ø±ðtŸ¿¢tdt‚Ë 0by<2|:˜&n­1%+à¤|ø6¿Š_²°zîHWƒK¨!¬%b(« !I™WÀ@t¦‚>=ج }~õ’u½
+z3¿³)¾{mO/¤J,¥R:5ôâWuÁ(G:±…Cý¹h£µdOÎ(NG~ð„Jä"×!ÑÓ·`€aX4”u[AvV‘9e~ªsÌMdJn±›#¶‡?µêãoÿè¾
+Á3B+>~ƒ
+H‰Œ—M’d9
+„OPwÈ L˜’€u÷²n1f³Ê¾ÿv>žPVwÄ‹ìªÿÊô8ŽóhSf“Ðeÿý!c¶ÇXÓ–šÿé+ÖEÕg³9úÇÑÞ-úìÍdCº7i>Ç#
+ÂÁÖLÃ-!Î)îºÖ¢q®zuçê97öŸ?þ÷C>þ:ü«!Òüú×-ÈND:d6éöqA<:OÑ6­oÈì¼µ“kCl®Ñ‡u½!*‰X­é”+t@‹¬ù\³Iâ1ZXø>G䉹·™õˆ 8ÂŽLíi1›w‰fër]e3˜2(Àý9<‚{cy³Šføh|„ù†(å[dKdlÐè_fü¸Ùé>ÔÍ—ÅÇç»Ëâá$GbWµ»ãæ2„ËÆÉÎÓÓŸ •eõ˜ÞZÛź;&£½¹ß†òñ€Ñ¥ŸzŽ®”ÛÚª`Ö
+(Þ(°ž‹œˆÛ²1šn̘«©¹’œbNÐ)mú’U7IŸÞgPŠU7½tAÀNC…Ï8W}Ïä$}Û¤'PØ$¼¦GõGFîÒÂeù&ý ˆf„‚1´¹|\|[©—WmÑ‹ѳï/HŸ+Èöˆ™ °öhä#šL
+»¥P#ó6FÛ©Óñ¨í•lQ |zC¦rðˆE¯hõEËÐNÃn Ezç¿YJ‰ûc !aôä¯é}0Ït~~Ô-çMh¯$½ñÏü™ •êE»ó¶>D£#Ö¢å3~Öu²$Åž@½Im¤{5<J«®£/N>]ˆlÎìò&Ä«»õ19¯ï继F“l9—qË\ uÕ>:L„€¿céèÑiÕ¸?fÃɳDêé´Ï@^MèÅ0Ô°-h~²ã4\p*ª‡a<ˆZ\)![äŠfœÝŠ©4ºFg®Rðʼn*¾Åú¸ïh¢™‚:7}ß[›=ã1É(zBQ*±è“¦P’óþÚ â› Í ìtAPCm©‹îÈAôá2uG®ˆ #ò÷Ú´B‡‰)°º¢·¨G_:V3¤‘JÛIR4^uhcuLå/NiRmH0Ÿ›}¿ŸÈïOôÝ“V•u²qúù„²vb¼Ø¸é:Ó!à!èÐV1/ŒÀÊÑc~lž€òë’3wMû¤LˆýØ*œÇ@¯Sø'¤ž”§g0$BóÿêuNC7¨âÐ}W‚äÄpã›ò®Xßý_2= àº8ØƸ«âéhNºäUѲhF.ÆbA4è…Le;U£¬Þ‚Á¥%¯ÔлäûRü )¨Ð°u^
+%³˜\Íâw8 HÛ]¸míPñ ‚ xòíDá7An@È[VZSwí_@7d¤†ä‚:Œî“´85p­ˆqr†3¥gO=þ.Šõò'R¿Š"MØÆDx¿³›Ò¿Š"Td¢¶Ë_Éý94ʾR7ã^'3 £Š}«‰ó 12$Æ.'š8¯éàŒõ:'îø
+„¤ü=Dª°EÈgA@§¸ÐM¥‰ ÒÊæW¿¿€žDñ^9~ diä°|ä‡O ,UpĘÓ»ìŒ×KÅÔʘá¸0qXA"‡CÐñÇÝ,ö ê˜Y’#
+è¼çˆØžž–À‘ÍÔ )kW]éáÅóùqo6iô™Í6ù;HÝÄzP¤·òûsf
+ä’Ü äxß¹Ä-O¾ bØÏN€w¶¶ˆÁ^‚{&;£œâ:k æȉ#i@Ë«ÓxeTx›ž8øäù1˜ÉJq´ÕUhL&©]ühüRº×b×N”ú¥žŽ“¹Ë‰Æ7æ08]ïUðßÔ4…XfJgx‰
+ƒ‚l¹fnÍí|2ÒiraMAÈN1øʺ.ë 6µÑÏ),”BDÓ©s“Îy¹J‘v0h
+£Û®²s t0s.{¾‚)•[­Tª‰8ý
+ãëòY™ì
+bL*^1 0«‹T ûÕ¨‹Ðsä›Ñ ]7k„>áäŒRþÝ<ç‰lZ¬•ÁZ
+ét#SrŽ}JF‹ÎáºêÙò0§©]‚NÄ°’èDŒsÑ ¥ÖÔfö ­s(eÉìk 3úÌž€.Vˆeð*ÿ«E­«H„ç`)<¬A=ôxþÊGN.tôñ¾'ð/”²Õ宋êÝdû[Ç¢V»Í™áïQ³Ülž)ˆÆjî¬i
+’»g΋U#ŽOcQYr{¸Î
+rîÆSç¸c%Ø!cŒ &†3ŽÛ%
+Ž!_¶è[Gª;ûŒ˜|uY¢GƒÆÙÌm…™PÉa!e+â´­ÜNó±”Àˆl‹RH¡Ùz&èòN–þúy ²tTy5.‰ýù„¼°/ðºk§HÄhÙØÃ(¿B¡ØÑ:cµø—›'vªùY;0§ðžþÔZ_’ÇÜ!’«Áž=Ń¯°ÛÕË¡véêü½Ù- tÚF¦Ä†¤£Í•ìß@ê"l7Ó!Ò„ÞŸƒ\äè& fz¢IJp,o(µ%W,€ÈF%FMÑ•Ÿ_ ƒ¯Fº ÷ÉÍÌí­¢QJÝZ_­Ç¨Ü¤!¿,!ao “)E63ÛygË$u<{?n@Ÿo@Øw…þ|e+®¦Cmé@õxËô¦4Û€fÑ„ìÈIÅ“àáÐß¿ô¿ÓÀÖæø²VL\²ìrç]xj²É® aæ€eŠxµ¯ÎBÀ¹+X7±ú¥*!<_þ á(]ý¨ÛuQËÍíÖç%u²…éõÎ.&E#M>Sm÷ÞÝ1Ád•bTÀ-×NÒÙ®=J«Í)LÓØCº ‰4Ѿˆsyå°ÕYnyÍH¡ßÇ0NINvС# O€Hí:’†‡ñQ¥dŠ³'Œ´ÍÅ›P:!¨¡ñìž›77 êÝP€‘‚R—™£Îô@Í8¬hf®¯…`ôKÓ{ HC,
+W¥Í°¤µ€Öäx)r&àzY<_ºw§Jµìþ"éPì!¦üôìÞ1—ܵ—rt„ë†2WNh½Vü3¯L­h‚Š¿Šµ¯•ô`O^fyu'ñ _Â0uá‡cH²ŠÆÖf÷\äC"'ƹ®¥ oÄIrfCÝzH
++kÛqÊÖЧ¢µ÷øÐi'¬)ÚìëàÈŠ‚ÖÒ.&ùV‚¯sÒ£)üúq/Âá¼^`ß|šGRŽ%FÈ“Æñóveñzâ™yîÛÓEÙØ×ÑÓøºŸþµ…“’1;é𺟗A\hòu7ol ,Ø;9¨6Øp!L0÷M¦˜læ¤h+Ö­ú}%1€˜¥èŸ~“dá¼õžmÁL`>Ì×áeaN>¯c¸x§}ÍóXá—¢w/bSõˆVÜ\T4DÔÍßÚz÷J¬Vý>˜V®ñÄÔ¯´ÄÈG•RƒÃ˜þtY6§1úýtt‘£ËÝ÷Ý ÆN[ÏwÃtÎóô]A>!ÒÍœ-ä0
+p Fs艟VæVX@¦=_Ÿdp²|ˆ”Ê_Ï>B…8ÜI”ðÆÀËÊím”–D
+ë~)™Ÿš°Dŧs
+&
+ò¢¦I)€”ÀrêÒ,ÁS`3`ïí¼Q;FÀw©%s°I%^íO½ÉcjÅÒK×)éáÑPðÅÐÙ ˆeUðOp
+'ù‚Ò‹„JŽ‚ÙþÜ úb=ƒÎñ«Ì)Oæ‹NZ͆º®Ž-¨xÕb8IYjqsÁ—’'fúRôϵHêË#AÎß9]Š²E®ÀëRrª„©ØþlÛÑž$9mŽ?y´8ÖýpäµÈàåü‰cœ$ Íâv¹óÄ@ñ‘/øÛ¹¿Dkãîñ!S¶Ѧ©Y°BGéÛ5žçTD ÷m
+;å² [Râ¯H¤cÓçÊàS PTlwûÂ1à{D'Â1É_€_%ò°ô>·þPB~cC´9t- àA`ÝR¿Ô¼ßk0Æ
+`YfAY¾( #b˜õ*“™†?{ß±ò$SÔÅ{ÃÔ´ô‘!ï½’ã©qÍžð˜Â6 °_EÈÂ^Uü[sùeAVtåÓÒñ¬¥Ÿ¢°…p#èýS
+ý<TÔ‡¼iü›‡ú‘w  ²Œ¯° D
+u«Ç”$# ’\A¨ÎJx˜+¯ÈýÆÌKJ … NRû:gÑ¿õõý;"¼luLh ²"¿v†ìdÐNiP܇™d–K/bA¹³@mÖâä7²Ašˆ•™¢îx§17'¿tÉ*РÏ é
+]¦­XºŸS˜bÃʃó ­ÞÃÕÓ2RÒ5›,¢ñO™€\Aq—É
+’$ì[é~N•d"ÏÐĨ÷ÛTyð"EµkÉü”Ú2vjïúë98ßnIÖç
+aÝ –VI³¸¥—°Ed°…X*‚‹ìÓ¢•£è:rvG*Aoûοés˜ŸV»Âa[¸¸ÑRY0¿pQäÉø­¥{—3LŒ4+
+] ©*¡=œÍ?@dÜ#ŠXlÚ8XõƒÝ«8Ã]ˆ¨’äZûX=|­¹¶ ãaPy¿}·è•˜ß¿ÃÞ“äg( ˜vD—à=Ú"öUF2“èÏ"Âm'ȧ¢8t!èo†s¹]ƒnY8(¯
+þ9uYéꇰQ™k&Ù‡9’ k9 ž›W”¡jÇ:§i0g´-¨xÒ"î-æ%C<Ü+V!]Jæôé.6 GoE³DôŠùeØ î¯VÍ+h
+œ¬´FHXö âÆí
+»"STNÔ{¡ãZMR /¤J4,o+p &³¦ü-v€£²V䳶oú+¨å3r­"U‹‡^ü>`tûBAÓ[¬Ì8gBâê+ŸütjØõ¶÷0Ô<!©­£eÖÓ+FI,£iù3ÒE¨ˆ ’e}©%"[ RÚP`@õ‡AÛ‘NL)ü/»ˆa"a"Þi1£|ŠpÈ{FÁ»£/ÈêÆèF &uÔ儹[%FQ±d]EPDR×Z²çœÎak´„«–r€4V:~1œéøA«Xñu‰–˜IúaW­¯Âã ST'Rýf{ë~ „ÏÇÍv‰uduÚܨȃ
+ü@Îøqâ–¶žÖ+ -)y9ŠÒº—%»é_<~¥*A³Ü9·DÛñ÷Xu
+°«ÎÇs2oú{e½†(yŠÑ>¦ÌÈPÄ]¹
+<Š_”FÔÓHÏ¿\64(ª`ôGó¡ªsM!ìOç"jHû»JïË©”Q\ÚbjÇâ Mt¼-/WO笞óQò2,cƒDIVfÛº«¾‹¤0”â°JòÃP~ÅmÈÿG„öµ²zC
+ýœœ‰íݾp6~—OÊÌBÈ—ÄrØÖg2úpŠ:¦a­A†”RùMCþ¾Žõ—7ä†Þóú°ýQg F>qYz!:Œç îg.™UVeÇTƒH‰¹?ø<­­ÏÌ{/Æ6ƒ!i"lÒÜõ\‚¦ÖgÚËôOÌ—×%ÉÞcXÌ…}Ç€¨ŸÒ@Ú'ßí+1LVBÚ ¢ª”&‹ëaòØâ¬vŒ¯ÕóÎÛçdúk†& uDš‹ƒNõ¹fÖÉèAƒg-8@ä4qwˆçvFPÍš¶¦qÎ~ŽZ çøEï® i‚…Ó¯ª:7@›º»èQ mi±
+ú1Ó•EÙú‰½ ^JSˆ¨¤ªöD½¿3øÇÿ®GÞC‰Óû!8ÙµJ!§L2¥ {Õ8Á¤}Cm ’Ø/ì@=;Bò­l_WöŠˆ‚TôMö:ä¶ mô‹šF†%MXÚg’ ¥³<U¾‘ƒ@µ'™S70“8ë0ä²®«Ølu‡È³ƒfƒˆAüCä…Œ¹65Ÿô§“ì*²jš"?Œþž —Q«±ÊWجMGµð!çDe)d¶>@VLYÞW]@H53¾ö>ŸkçÌœà+'¸6Q…T‚®øwÃd}’ìÖB`‰¨24·9á±½\MΟ‹Q8v¿gÀ}f[›À Va„ÃE’Êðç m<@ÎÊô òó
+Nk7ã‚m@Û)}9€¾@yty×Ü<ˆAôX¢<R_¦\ß5iØœ½ïñ¥8äÙ$Y´ê0ÐÚ;äÞW•qX3ŠÝóýǤ`÷waR¸W’cÙBŽf… ßz•ÿ£LãNPæ9¿ .2gèÇnz!Û »›E{I³‰›Ò9ô5Ë›¥#½¡oQ9 ¢1ú1ô B^¥àT¥ì'£•Ô*aEÖøRQÅ*¯ç¥áõ Œ$&ºüu³p|¿{%æ…|þ1ù9EÛ¾j_lv±(ò¥µ
+Iw°Æî&«†þ³*frêÜ@rBPcrØ׈–J¯Ô”ý²)UùPS츶€FmCP
+ò-Í {qâôº0îe¿çús»~½³‚—5Å ¸ûŒs1}‚R øI…FÙ6õ D[ù Æ.1ÿi³£¥•£9ÆÛ Ëhü<^÷èâ>¿@ €¶†YÌÃfF(Ô¡L¶-,·)#RÂÝL!;ÙíÁG£pœÆÇÌrø4©öÜýç·X¥µ6;F¥àð–Ƕî‹•Ñ
+F¸bM;eé$²vårH7Љìé3A5|…ˆû6è4€·ëÞ]Dá<[ИNO.€VÍœým º˜ñ‹4øœ@€÷†üÿ éÀÚ™Ìiç«-D]Vmǧ­öÛd|amÛ²€'b\eÃÄ)0ì ~wK~…×aªC»:"÷Û9ߺ{}®?íçCÓ[¹c¾ Sq¸Ök¼c"ÿÇx¹$ÇqAôºƒNÀ@á5·¼…¶œûoý(ŒÌi´Èp8lK@£*+?Ĉ$ƒý&Ô+¢¿«<.2`$WIËŒ„Ë€iõ¦½ Qº˜Â8 ê°&vpò¡J)LgG9m …´±Æp(IÁ¦F”òóª×"rSÒ2<™Ÿ¤rí±ít[G–C¯%ŸxHxeÞT79+>ÈF~úÏjŸEüâŒ]m'ðÉÄX{ƒåÃØPîà÷o¿ƒø°Ïsh¼d·”Ôùð"LšèHÞhåRe/tG/p }¹­9[E-
+ŠBæ‰>¨$qî°l‡èºf‰ '<d®;”ÜqÓ—¢cÀ_³æ¨µ­Øt*J"4ÿë µ"ePps+NG!4…¶±Jf¤jRgw}eÕ”ÏÒ]¬l'8!XyOæ™✜ +=$:!\⫤&¦Œ€ãʡįàR—mwçÄÊ,Z[™H_#L0ÕÐ}gd6ˆôÖ
+:= b%³I÷ÕƒÛåUj0?ž 
+9ì0 @ªÌ‡’M¶huã—”Çùq DŽÉŒÑ¯ÂÔc¸ N|•h
+^¨ÌYÒHÑÀµ²¯â…,k*q_ A‘¨>üµÎAW#kqƒ‹Vá `¢æ«ÇU2»½T|À‰õ86]¼É0
+ëãâ– %T²;½`,ÂÍ°¾úÿ,¥”ÑNøáQkhCÁRAñœtçÌh®Y¼,#t2ì¡%9^Å“ê7A´-%ño®rñ8”|r½ØÛ4Êøÿ>¶¯©H§Ö:,^JäB”5_è8%Ѧ8Fêþy,: ‘ùÑ:>yä­q4¥t;9œ“ 1}ÏãDfþîDÃœ² "<¯o ”Ô,kgþ=ÌýÊã@Œÿ+oÃdl ˨û¢må „ª¨nÄU‚!ÌóÕ¿¦cs‰C¤Öík2Ëž*æG²åzʪóJ®~Nà}Þ¶wË[lF— ¥Z }MŠ{ #`A*ÜËþZSµC†ð1ÕµëÖøQ¨f«ƒ>§/\\Špy« ‘ôá Áþ#!À»z
+ 9ÀqÎæ%¡¨°®ô2S`‡²zôäˆ@YÕ-&+A0+" buM]áI+3¶85ôÄP]“é Af
+Ýúþ˜/%Ž }ZÇ'×9¯çðºªeˆåK4}ôM„">Á+x´ÅƒñBJ±n\
+$»7`!PINv’ÏÊ•-(펅UC:5‰Úøî»""f‘fg8©½wŒyÝÑÙOPÓ±Ô‰åÃnnã  î¨ %Š>Ò¿ö¥ÙNñ«/;û8 ±2æDÆ>‹§)iµdçؼ„öšl¦0¶J ¦Œ¦§¶Kxl‹f‡²Ö±Uô©s7I¬ïu$–e€ii’'%x|Œ?oŒñ¸G”( ÜbHýf›¸’ˆ…R¤rÞkJ ,Ò}IX6˜”C†ñfAÔ€ˆÎàqv3à‚÷_ÇÂóçåܜ٥¦Atø¥åošÒ®–ñ)´Wºò³^%¤Wè,<c.ÛMУy²ŒþìPôu%s²; AÄÇ\|Ú×3Ðá–¤W,4ŒÛë)æú0nHÖNǼŒûú1Øœußµ7_;|X†/ƒúó“ùxz?JC¶
+@Æ0Ìc¾Šd‚Í°Ö6AMJL;Zò
+ÂHi£Ý˜ SÑ—Ñ‹ lZ B׳а¢$kѶ1‘f¦Oæ©]Ö† WÌ n¯Ý/Ï<GÈy–fŸÛ†ÜÌšz­Kí¨ª®|-ñMæKGV´¨é|àb³1ÐÅBÛS (ŠRæÉJþ¨BòPZEI6H±z¥âéðæ´(ÁË”ûÊtJšåÍÜ‘¼úd¬ñû¼zM€p»±¯ú÷~NDZßàDBéÍ}xJÞhäöë„ZöË4±… ZÒц‚“ß_
+ÿJŸXŠ9xŒ‰Œ"ÎçÅ1¡yšÚPO%ómöF³P¯†tÓ %YùÛw2 ÖavšøÁ“0ºM¬#G
+°ÃH×ð› ")íîœ
+;UàQʉ3(Ix
+LlýÄó* 2Àèú¤ç(^Α÷¬0&–|'4%Ë쮂7zPèšÓ´!M¦:·µê¯EGdžíýeˆzÇO‹¤;À4e¹¾ðó¦h#Ê~/DSÂœªN.Oq“bu•$À¾±°.q¸iåpân¡¬ .¦ö¹Ê’\š‡Rî_Ç6_
+ËZ†>çIÇLΩ·q &©iahãÝ9E/'6Ù¿á=L/ŒU‘S¸.Åq`÷@íf%ÎÙîMƒ¬0oe±¢nð© B8 œ¦‘× P˜Î…ÀÞŠŽ#g÷Œ<¬òŸé:kä›&¯ ‡¢(ñ•d:. “Åâ ’ãÞ¾×.6€?¥Ø¸9å:-1z7án¦NÔZ%Äd8t ÐF(†dW‰,jo—5¹@¶[=|­9µGN›ä>-?-:¥¡oéûoZ+á.üÑÉÕ>VûRçLG¶vRŽ.åO\3Á(ü¥¹«”4 <°Zu—°ì‰Á6rÎJC“H™Ý~úV…–sxhmSøfTöt„@|üƒS‚y…¡šj^*g6oá{Ʋ6ü&#Ž&TµCÑÇMF çnÓCÎvë·J¦SD*’Šä"
+zb`•2ßYËÞÖ„å-/'>½9Å<¡¤ÝSîEÞG[ò­—â=@kÏ;;àG2@†ëë<3äSù+ŸGõÝÈߟv^Í·ˆé6¾£LÐcL¦NK %Ò¦ m¬mÛ6pÔÈ°gåŒùcÖý*" !ÿ“¥qtPÅsu‘À°¢¼­*åM'‡@ K«„ö—*aY¢q-Á)u¡ff±<E0#ôøãeŽ$Y±DÑôJF(‹Ù#D¬¿Xê_‰Èþ97Ü_a/ÒHè6ŠÊ›1¹ûÈ”Ñ+ÝZ‘,‡ÑĘLù‚SÑÍ»,·I_^™{Kß‚ŒFš¬*ÿ u*ýÍqèkÊ¢%Ýó¸£ÿÈÓq¹8 ˆµ!J? Ï5Ô»uÀ°—)!`Ð4…›l¥ Ø€{¢:ýH¯ÛÅ’Vñ‹<{  M`$ËÚ©½(ôNIæz
+±gâx d ]êIòLt-™ü9Rç¹b(h7øpÔEÌ,Zƒ®ƒ2™=üÎœdÈ<4™ÊòM™nÖäKÌwì¢ ÎàeÏrÅâQ׃õ){ÛdW=}iÒ›T˜¶¬í
+y›™¢«ZlG4†‚ÆH•ª®˜U”/W%¨R‡…µÐª—¿óP *– ŠC5;âÂV_¯@·ÞáDhB§ÖS¸y¼Óôšßþÿ#Áò”ž7ùH*(êRô#ïŒNî¹­~øó¨ £8[eYuVLã@Pú@£Ô¬)ÔÙ꺷~}@5Ѩÿ@ë‹C Ûv7Ðã39üþÇßþæ¿þ/—i*dCÈ‘Ö<õÅ5ã0¸ŠÂRAý“!bžœœùi"ìfïÝIaÖï*Œa§§ÒÁb&QvU½(Lg’GGК9(!G&#™/Cg`g2m¯a^¼¢,ÐvȲoMyn.™["ÂpÈ€r‡©š>amì;àJé¾­®€Ôt¹“’|… ¸QÌâäÑÕÓ«*/á5³Hˆ½poâóA4Ùì/;!cÌiç!ÊΠ«|*iž…Vm¾¤Ž•å;«
+½B¼ÖWÌ8AŠY҈ƭØ
+ã;OvÕs¨«'c•C‘°¹Ûâ#%cîL–. üÄ BB¤o.R6Q ;@öNC¿c„ R^­SåÞaŽ:£ž²#èŠ(/ ˜ErLr›¢;Ic³òW±k'Vm lõîoCËèâUèlBn¥“ ²*©‹Ÿ ì/ ø‚&Ÿ^åøcòn ¥Wï4‡e?pâ€Ó\¶øÀ&ò ð¥I4âN$#þ_즠ʗz]ŠÁI=:cúƒU˜!fÄ×!Šñ¼X §Ú‚óO2H8†ÞŽÅD›ôàD:<Èô­ÈUx¥-ªPøP$€¡}€™–oVBf°1ҧˆ¯Õb8ËŽpjeçt LQ`mö=äFϦõ_‚“kqžóàaIL¼Z íªGd—ÇÖ:Ú­\;Ý@†ÓP(èØ×Áu\7ô¢bç­…ôÒ˜WŠIå1‰²…é×ó±ëp‚2Äc'µ~'A^
+-DÞ«9DŒ¯1<Z°±LÝfõ€¸z‚ê³—yº=,ƒ+ÆvBýHpsH_´•ë:ñ†P%Æ¥8Ze€ uþ^b¶ÔÇ´¦"nNO$^ù\.UDÚÆxµîïgm›þ„ÜñTŒ¸T¾8¢h¶‘j—ª¼Íä©1~Õë„EFPhIÈíc#ø äLÏ´(eÙÎM)ÒÚBd£õ`–AÄ©ÎÅ]‚b&ƒë=Ž šKdMÛŒmþ÷óœ"^Þãs³Äb¨{^Áh+‰²˜‡[E9Ù™ÒñÉÖÖ¥¨]DÉ|‘»””‡t ‰ÉG“W)§¾yBéòHãâ©í¼ÌÁ‹Jh-$±¾BüâVbsAwS77z.&¼óÒ”]ì\·©ípE“ñæþÛ2<ƒ¾Ž mX2–Ö^b;I9¿DímC1å-±8AßÎ4\Ø
+Qà¥òhÄG/q‡h® ­
+º*vÅPFáF¦jYÛÝÝ«¶°=–+‰³S8‰È†Ñ~EÀ]– `ˆóbÎëbqžûvmUy.JãµájÇ= B ZÔ*ø(púVM┸Îa¬9€À5±ùÄ2‘-È5¢ä“aåE½§•z ªAvV¼c*j*B†þmôÝwPf¤H‹Rxl”ï5À‰æìÑ«0·Å1A_qqª+[F%’«&o òýÆ´øÌø˜½~dôÄdóö”ƒ³Ð8;GŠÉóØ.Íц¦@K!Âl;vkAžcñšµ_A‹Yš:(×!T‘ˆ!G«LOÆv§å^˜NÇ”ó,Ä”6¶ÊᙚŒä†Ø’c²R|l]" âÖ×ÇiìWüâ,'¼ûÏiâÄÏèá7'$ üì‘7FÎuªµÚöÝ@æ@MÑY[QpŒ8 Y}Æ7„é]VãJQÏyC“gä”J.Ã%0Ž´j;—ä‹c†â”±šl_[^OÃBcð ‡w€ì­¸)ä…·ÁÔ­ó:t-#Å£0l8ðhPs¾„P½U${Mß‹‹‹•d[hî‹{ WpÊ%°-É«}“_ãsJ׊gÕò™ç”N33#¶"€pñ¬„œLñ " õ /è „cN(½„x¸i¹ƒ`ßN—Šh°þq:2ò Ù‹ÈF›qäñ"†¯ûfÓѯ«/Œ 9 Áíá«»BJŒôj1÷ƒZ¡vA]’Î.ÁJa~hQhoÏÞ3$zU¡Rš¦q^§¢ÑY™G1(X²¡Mš\at2•)—ëâ¨ÐPipûÅ_¹î)^i+¿öu!`Š¯`ÐMÇ”ý†‰÷€¾ÞÅh’è±$2jºMûèñŽ»`7ô…™Lð]fþk#Án¬5Xœ’À/üO«;xy™ÑùÁx“ߘ—3è´’ø„0¤V¯í]ÐÉIݶ;‚n·#Ñ„bí<ƒŽ+=?æ3èñ΋ïÂ|ühEr]Uãëç׎Qmª‰" X“ö¢óêáÙ"÷nx•ŽÇ(\““ícý×^:UòSÑH¨H‚AôÁˆmÀþO‚Æðcñm¬PbØþa¼LŽs· sP,ìËY>¾,|ýó?ûkh—ˆa‰®ò&õ#€Yz‘³ÙAç½ðpK$3
+~£s/‰àûÓ4èöx<ü †ª?´G¥B”LÓ©&nñ
+ =ÖEÎÞq)¬¢7ÐçMl3ƒŒƒ`´†_•3a.dàd³Þ­võí9(”×r‚HÕ{Lp›¯AÞqÖ©00¶²yEpA^Í·ã^¼,³—à È–{[¼]þµwÖâ„Z¡i3 Ôv‹_ãYä‹-²@”%ÛàñàÌÿ—Éu uBñÚr‡åJ˜²@
+¥aõª’²„Ë0ó(<]8Õ3É¡#1|\yÁ²ÂeCg4ÁD\†äÂ3)
+ŒA4&Á
+§dêæz:‚ðÖF^¬MýH™Œ©íª2ÄvÔ1®œÁ §zÖpU¹*Q ®×¢hv¾u‚‘ƒ¢V|³üô›(s¾TÅ¢†b‹q±×¥x|\WóôG
+¯¸¨ 5Y¯­ËâéÜ&Ñ
+XÁ]c°!…ˆÁôDWé& ( =y>hls'ÄŠÝýNÔ#NoÁë— #|¼°Æ­Úë'Y·‰ë¨¡ýŒP}˜‹™¤ÕMák­êdÈ„“Ùɯs©b£¿HxÎËÇ蘪 6sÖŽÿmˆC`¾íMÔ ƒÈgí ƶq‘h 4Ä¡Á¢¨ölzD"—Y«]ŽÙÜj—E[jßê%íÒ·BI­—T¢ÈI.%G^{eè‰w'è¦âB³+‹ÚKŽeØ 'ye/ØzÚýïÀ-ü
+lr˜»Cl!V+ Ê*†c™ƒb”õ
+evËÅ ÌfÒÖ‘†ª[e!ø2–_>ѲìdÚØ,ìŽuÂÁìôÇNbaŸä€>o†Ñè½£¸¹Ê¼¦`¾ŽUÞ™µæsf;?Ããîg¼RäЄ)‹9²¨¸û¹@CÕÞ2no1ÞX§CVyà|™É· oöuƒ—˜´ÐžuyZ²à2‹å ­¤uñ­)7ÐçMç¬ÁL“p‡M,¼L.ß‚LòÝÙàÆžÎÉxð§Ã“ vRbˆTwYB 66‡øÉOó“ƒi˜ñB°"ô-÷@åà†?ÏB<˜3}‰% –dôÁó‚Ÿ7%X•ÊjrITx\{¾­B#W0r‘s–‡UØAΘsœX¦s$¿{ r*UÉf ü3§Iæ1Ka0QäòØâ;È›ó½¯@Î2ìmq–á×Þýï¾êiÔB+x“1ÜW¥î#æ)=¸¯Zt(œ={™Õµ_@²Ú™ñZäæ¿€°Çí¨ÜôÁ€ê)P¬ƒG×€ÁÅš•Í€yÍ€í Í€)ÿ­ÞÏvr)”Ù].üv;áø¯_Ûõ¿ÿâL\ã€Ä°þkÿžã¿v·QÜ6 ~Çx²_wŒ·O*míP›èÖ¨µª®ÍsÍW­GIŸ‡OšÃ5_@p_—t­÷f¾j“×Âv2p×æÞÌâ'cÜgK®÷ÚŽõÚ0›ó‘ª^4:¿uO’†ÄÐÔùà¼øêF™¯¾óò0{; [G3iÈeÚÏ›14¹)´]±“ü¹Ø¿µ€ŽÑ¯lVwH&Рe…¨ç
+â?äðÜ^Ý k,
+Û Û-ï;‰à4º~z-’Ä…8»wyr#1«üËŽºò`"›ä¥Sñåí”ôJ\Û¨.àG›¾méHET&Õ¨¹â¦uú¢µË“ÃKü—ñ2GŽë‚è pÚ2½/¦®\@!K ©ûë寪‘0¿‡ œ^ª³2³PeËRP'_D[!#¼Ê°Õy_n>Z
+‡ƒ`‰wNŒ%¶‚Ë—H£m rr‘ÛÍN6ò¿×—4ýòû¹Œ|§ññ[b+Ä?bú[úö×[.\Oc¥§퇰 }̬ܗªI…xZ¢!§êýo ßN ‚ô益GÄ×è¾Ý3èã+g²¼ðëŸo¿üÍ=·l‚›ÍÅö©†Ðú´‡~rÓ* ⸔´uù¾°L´ÎÒd?^€J[k‘Ó¶E x¹:~;g“P}”Ñ¥º8W—<=o÷5¿Àíñ]¼x›÷A]"
+Ðj:É;.\hÉl š„ŸÝ 
+bp?EiÓŒ­hoÒ0 v‚qL¹‚ÛRˆ¸ÈŽè‘IÐ[}d·&AÐA ØÓû˜=Å;lñq%ê׸yñÜÆó2(Å?/I ˜ãíIÆ`D»ÎB±lþ*ÄŠn ˆÜ»,°\™ìv˜kŒ³±Mˆ{ºÔinRmÈ«ÌIÔ+&ØÙÉhÈ"1ËÖÁøê|T.„^ &sˆ ‡4Xrl4»œy‹9…0“®œ–†o¤„`Èžo›Ð}æ±Ükiq`†EO&˜o…p1_ØVp˜nÔ@›" JéJep¿ä° ¦¶Î-mÝ:ê˜ÚdÙ™Rð¶Õ=µÁ@"óìY]ÿc´?nº'’7¼~ ¦¼I¦cêc+ ´€៲݅@ŠÕ ¨t¤Þ¥ìJ %ç„ÐKH& wLŒŸZ éÐv¢ÉŠ:U"è ªt@Ž/æЈ/j}1bùKáf̲ÀZ =—tÇéÇ«#˜%v‚$È/òë`€0
+²]]«›QÕM¯Ø ;äíºF+qRŸÒÊ,ÇöeÝA˜VðDå_@nsí ôrìÄSf)GøƒÇÉ3OÙD,ç”_ü1‘6&„ùHA§Ò‰;÷yÒ
+"âB=LÜÓUpqt
+íJ¦2ŒÅ±ß½§õÎð×n‘¦$â‹
+ŽQl%Tžž( ù¶u¤ì½gÕÓË G›|Ž¤É Õaõ|wá¨]º‡åÑ„ÄtÈwâ©•îIPÝßó#Øm d±Þ¦k3…f£ø[—}˜
+eˆn/òê2˜y4΋Á½¿@Ï,þM FХ͙ézLÿÒC€ i0l¸#ˆ6¥ öu³)EÝI5xõ ÈÝ… )Š¦&‰eÍ->ùƒbquàU*ct
+tHð?Ï0»ÈTÙÿ4·H uó9#Ù2Lð…úÜJ~NáRñ9LMýäó딬ÍLøÉÈ^ã,è®=”`m(¸Æšóç®;DøbÁ+
+øìÿT\Q|£„”@ÌA0Ÿ½%^‘ ‡’!uÃÃÎ#“?úš—¢ï‡"Ç…ty¼4%Eadls}3iöU&† ”ëŸNï¯ÓIT­õy\ Si]7#¦¿&Ô„ D‰ ÊÎÕpžŸ;h_ô€/ɉqλ
++·yÐ÷Ëps5¶ ü,ó¾žÁð…É|ʘ'ß 2IÌ[µMÌ) çÂü5u2¹±Çë|®Ìo7O *]wô=kùŽŠ™+Þ/Eg+JšÑ«É/ „b»ìpÖ`úsdÌV«îÃ/Üx“UM’ˆ8ìP¢¬:˜ãƒÔÍ)`zJ«‘yáîD›j;cRÒͱ øŠS’Qg\0xI+ZñÂ$²v+v€á“¶¡žsߦLËž)%,éÚæ‹ÃŸ2 ±™’&á5ˆ²AêÞJJѪòV?E€é]¦DqÎá±:dMè(§„FAz‡xÞKXÈV“¬Ï«Õ{t-‚jËÕ–’!½-ä™X?¹Š–syÝFü N^õ)Žñ:='ŠzÖñÈÍh’·:Ï9p§¡µåÍ´ü*¼€” 1FU&»Á¥mFÖKIýúã‰+œ‚`ûuç‚Y’Uî9 ŽcÉw&oh¢Î/ËÍQlQy±6“¥FÌÜ}ÃE2`„ì &òpCùîfبÒõœíÁ͉
+¦¶Ž”û1S-ER~n¬÷Ï=Éme¶
+f0ŒûíŽwb$“Ñ­x™7»c°I!oë|†À.¯ƒß]€ádqª†c\—Ñð¶
+ "ÏTq@ÐßC–¯Ìèa7>ª‡ân>gÌóº……“‹6¾´†2t 93ZIIW³á2ÄP²V©m¥D l»«\Œ3™Ù|¯¼½%ƒGï.5¸F°‹‘gøt²ž
+ÌZnEß·"-7KͤQá~Ó_*
+)<B[ ±þë ÓÄïjr¾‚BFÅ6³?{0¦,ßÎz #ÃmØé‹-¶§•SSeHüGœG´2Ùo¶Û¶—5vD MBýÆ#‰{‘×8%ä]œ,Ǻø"6ï)Þjî„HD;¢UÛÚBé^‹L§²ç/7‘Cñðב«ƒa‰XJ_1)oмU„ô“àœ¸ŸÂÚòNk!Û+à>™}ž’òΣûH†¾@1íe'x)Ä@dêo7Ù~8µôFEkª|¶¼
+‚ŠLQn»mÿ»ò8„ ¥ª  %ǯ ´ó4ÿö"ù3æmÕ˜@²œèòÝ]M«@»˜¥I9åÊå DõDŠÕBOy¨!®¦Ý‹&0q €QÍ£Ì 2A¸g]ºÙþK6É\¼ƒžx~Å’CY\âà]ß¼ iñU!OŸÄfJ;¼ÕÞ˜„R¸xÛ—¾iIл» ÆF¦¸sx ºÍ¡ˆlóÛ ¸r6(µW«Ÿ3bÇá‘~.NÙòš7›õÿFL– iÚ„ˆeúG€
+H‰Œ—Mr\9„O ;è]A‚ ¬=˾EGÌJ}ÿí|x«Û¥Wr„-»œÅ ‘™œ«õþùÇèþX6bÊlüüüû£Ö×TµÖÛ*Ì\cy3“O þˆ®m­6›KAÀ›ñçàKÄØ­…dèr¾6›öÏ¿>.Ų`ÙÆîÔ-d5o¶×™œLÇÐàƒ iÓú4}CtD¨éš~ƒ¨Ä”{u]ï–émÆfú„¼\j=‚ó¬èRåsÒ8ë \{«õðÑ?»
+qYÖZSÛë¬.K§×Ý™&aÖ7Ä:ûЪ˜xt>à‹¶øÙŠ%$T$"6ˆÛXS§d!{¾ø¬Éš2B†ÍÕ—ŒÚJ{‡ÞÜuCæ\1z“‘ý­­g†&MÃ7hÉ’¹¤Ç î2ÞÈ_»Ù¤ÒnwiÆé\ƒÅ†}þú¸õ{Zh›2>ÿLq¯Ý}­°Ÿ‚®úD^~Ž ò_o@]TfŸ³‡\Ây† lˆÓ¯à?¨2õ/ˆXR[ç(ö¸wƈ֟vá[ki¿,p„Ý9AAÏå²Î>6d¸Ìá°¶ÕY²N¶Ìærvºº5¹ÒPzÌXS×N“7Œ vnÈèÖÕ»ñ½B Ì Ý«ÃHvf2>í¹SgDÂÑ‚¢rWsXÁ‡íu`-]æ»17¤Q¹¬EÖ¹ p݃>!h s
+Be(޶ΜwÔ†J¥%^Ô.¢!!ÎÞ5‚´ASŽ=oʳ!’Uî–ƒ¿kŒðò6|EAסÐÌcAR_Ø>þsnßU7Å„£4f{Ô2ðŠE¦®:1ÌC¡‰G'5n¹LeNw‡Á$ž1­©Ù©0×cÅ‘ãpVÉfR@?bÑDpéJÙ´Ög!@n6ž3Žprbiâß1_GEL1•3 YPa4ýV¹r¨¿ö…ÕT+u5ªŠ7¹©ÝÑQ4 í—ÖêÞct‘¦!]·zL·5'öI¯{3¥y>8,ïYƒ²¦UAb?ìkîs§S¿4 :êVL?"‰öØVÕòO„ejÜLBY^M—^;á^ø¯&Ñj§†”pÕä6in4¢¬•‚®½Ñ ¤6šNVPq º3útRÚŠúHn~ò¸ Æ®Ú|Þ ›„‹Þœ­OnAŸô’âKòÜ'ê; Â^øa"ÑÊùO
+ú^¾п£ÒŸÿýèŸÿIäxà®Ø2±áHÿoŠü÷ÍÎL0~Äô7ÐÉ&¨I±pê`Ìä
+D2Fê-ÔbÏÌLhœõÿ3ajÊ×Qvô¦1©±×Ìí’Åd8'cNÆ–d ó+I·ïÿ![!´³ ‰ˆ
+GÚ©=i ®â†¹ŽÐFfAÀ÷Ê™ÍѹËDøW*ðIœá“ݹFÊõ•à§#P}däÏÑ ;È
+´p†˜Ì0ËÞúƒ‡XdðlWrí[fLµ`ô éY×^o€™Vš‰gkÏ+$Š—q çV¯ $/5jFÙ.°°Ö.g
+‰Áa¡ÓzRðÿFªõûe'ap‰8ë<kZö€èœg«) ¥¢'·Óÿ€u=6y°Àᩧ H ’R¿½/“†ËÑ㓸eXl_
+Ï}æƒ\$Ç!,øˆŒ“OÏ$À¶#ž¦=jl“CC€Š}‘¢LNf>»ö7þºHê\“ýõñD‚Âêˆ7©g{(†ß ÷9óo˜¯LÎ8=¡ÙرÝ1ˆTÝHOXEsVpŸf]¤I)x)éÏøÚ—H}á!;¢Ä)YÊûéÚºfðß3ÝZøJš
+OÎÍa† ÇÑÏ*¤ÚDÈ ÏqI†òGI$¹êØÊWëN^¸)riæñ\)eÃLrM.€üµÖˆwó„ÁCÎV<V òØ,PÀQHÄÜQ.Ä3ƒ.ñ¡Ôi2à)e £Ô`"
+‚Ì…&û+r¨áú^Û|îôÒñŒ@ËSˆJÅŽ‡Î¡0›ÂwAêÿ²Ìnø4R†ûä”5-I7’$ÖI’ö+¶}Ãä©ŽL@qé–eà…ÖéÿÓ~
+zÙíÂà^0Ôò¥6åç˜ßÞ˜ï6ûh†˜kØYòë †
+ǯϞ's¨$a›XÏ™ê%³í«ˆ |­¸[yŠØû”X 빞svwHžlì)á"Ž
+a//IcQzÂãùëBtpî#`Ø ‡ ÀÁ.hm¹þCyx2Ò†÷\t9ɹìRâWHŒz‘3¿Ÿ‡±ÃTê®!äÎv,gKØq$Q±ó
+êÓIP ¶3¼Ë8Ëç
+9üu)Ê›óHQΉӆÖÒg¾¤ËS2Ñcl³ù¶ãÚæ˜%Œ7AÊõŸpE‰,‘oš—
+µÛ¥{A¤è!^— Ǥ˜Y‚\kãJÈÌÛ(û»5ýɯô^öyÀkΉæÿÌœÄT*%NçQ&Ê4±õ‘9òŒ@´\ˆ`"ƒMZtï ü&.)XHœP 0‡²Ê6y.×[ÞÜ%Œ•ß@®f¹”¸²ro3™ªÃŽ·sžtžåF¹)Ú|õ úÜ´±QÈyq•è 1ôøž)ªëŒÉÚ9gà-ñwdÇ8‡»x(+\½äŽvïFécÛ©¦·OAL»ñº¢²S-˵²‚¬B_¯5oj¾|Ò‡_6‹v©7»}·A~C¸$ŸÆæ7v+ë×vQ…«LÀ<xþVÑí:û[Ö•Iéþ è¥—Û~Yóc?ßA !aìZ‚§3=ß68n%2Exy!þȬŠÈ• Áz>*Ęrûª~8,³ˆbHeÄ/!êG˜˜G1h\;µ”µâªŠÃíŠY7ñ‰‹vΡç¹é¥©x‰B4g'Pb¼b`ÚÆf•à´r‡àô¸ªa¹ó$6ˆbpÆHŠ2Ï9PƒF²‰3²,‘€—¶'%H_ËXùRâWá§ê¯î瘜ø¶‡øš‚>J;ðS§†`mï·Ëÿ8;X•Îô[ÙgEŒT—¥Ø뜃Ga¾È_\5 ¬â±aÙ¿¦£´J‰r/tÄó–¶×t¡bLåA Ò³¥†Peå
+Ü•Ç5<|#¯~¹Žg çðuPâÂ|J¤–&§™¢ý)MØ_ŽÕ¥£qÁ«=GÿYü§4rlãålí8%¤Z¾WÂïA¶AîKÂùÇEXÂ&'=‚, @f0OGž\ÓVR‹ û5ëRBÓMy·_9«©¨³“Šøé4иZì(
+€Ðb3gJÉ¿.Ô[»¹ÀûòÂüÓ¼š­Z?Ôä¸
+ýlzÑÏ8 €€üöp &–zÀ4æç
+Xþ„lÍä^$Ãí¦QodldVžOnZôèÐ4iµDMyŸñãæ: Ìö®Å:··G(§äúFeÉ *Wü$gŠ`ìÇ 8zi8ª=ÈY 2B™héÆÂjMÁö¶¥{Ž(ôÃö¼ŒKˆ¹mýí{œ®§ ñ.¥è±Æª4¾@õˆ(ݯXöàÕ47“š²sAú•á€¢Õ)Š€$¥Z9•ì}íú{APL˜Ë(ãØ×éÞ
+iâ–©?i ã»ù 1Ïú Esà
+O–F4w!’™5Huø“§>i‰d|ã²<·ü!DîòáÌÆïÿ|*T™ˆ6°sºFêpqÏëôç¤ùf®©6>ÜoLµíR6
+V;ðXN‘Ji»@ûÆŽ ÁÉuùZ©% Žc—ë%f— t×Ô ^¢¦k:u¡$Ò`±œ‚-Š*
+8@µâùåÙ­¡H$ùù$uâ¨ÉJ!x¤lùUÖ6“:ùã_‚‡ÑŸ"ðÓ|¼‹aSMtâE~iæØØ ª y:›#Å ?ýE£Ë§“3 ¨jmì$­øŒ‹º‘ÑÕÂR«V›ë­hxGö†ùrŘaÝpãtQj­KNÈN¬h|(û¥(9t+«k,ùþZÚ¤ÑÀÄæ6E ‚˜h)ô›öÄ$ðùØ7!Üb[±œî‡šs8oMlLÚÕ%h‹f­–‚ZÞj.ú¡t”äåÄm»ñÚ Cý?ûý½\™‰²Òœ{s@½ø/b@†þù´=¸TQÌžtB0`@„ùꮦÊ:y¦$ù„ ­*= ]•ån&nnN8¦-iÇX,ØNñNµÏigÃL7‘¨v‹±Ð1
+\Üõø¹¼–m* ‡ žþb£¸û‘RiHèÄ~ÑGj/€Ö]¯7]Âì·ùÜ&ñ”ñÁ~†Þ˜âŘ"zÖX¸.H¿#²Xžíª¨ßD#˜i/ßÏiÀ©„Šó·°4sÒ׉` •ž ]±ßö¬m‡/~SC
+æÅ2‚ÐÇš´øÇ…¬÷HC g±@öÌ Xeƒ©ùºŒ¶œjq&¢Ê‚2oñÓÌüªW©Q†¥Æ#ÔѾÞõJ˜$ÙÇ,ø»Éž€LJyµØÄM¯ò©Ž±6‘H¼øM„Y(ÄI•A¬B^Ä\ºŸB÷É ¾ƒ¥í™)…= @D#…G÷é üŒ:íäCÎN%Å«ÕC0w5io­ÜKS§‡Yf¥+»=#‘´• zs ­tlüMòáÓcÀ.Ù‡dènËÛòVÖWñb¼ŸH—âµèr\Å ðHæ§~ŽØLâÑ×iÄʇÝDïÂÇh}aÊ<!Sf.
+¡
+TÂבڎnßµ¤†“?à”õ@ }˜‘ ¤p¤€¥…M™±•aË©n–Òú:²„èÚ©R"“Ôå˜/|m¢x°=_ê8´óâÙi%Ä\ÿúPÝk3Z¤µÿ´ßgéò]²¹ ìÊò,ø!ˆC/„
+Â{cæèž{{ 83†ô’¢?]cz¹„¡“à=âu²i“ŽiÙ!Ì'ìD·öô)ŠI¥ö°Lˆ”<†òÕ³â'w†p•Àâ Òò•‚¤ŠÅcàV¾©>6f¸h…˜,6¥Ãð˜1v‚9¸vŸ+&47’“…‡¯£ÄG3›\ÓC;„7€Ý)ø$Å„©´k…š´ èÛAL5dQßR=¬†ÏÄ 0`)ÜËÅ8Æ<ULn¸'Øw³ÇL(÷6„þò>b ªÀ÷{2 oÁETù{æ™’
+ßlÖ‹Ï;–hÊnüa Ò~cæ,‰@NãPÞ)Ù³µž­¨&®y¥ޡГ(oÏ1çøæ®Ì[²7ÎP"ÚpJ1QÍ]Ãìà±ÅÜ­F5¡IU9¶wÌwPÁ€éd‹­è5“éœWòÒ4P€&Ê›^Ë•E¾´öüd’Á°
+GÒ}™]ûDÛJ1nŒLµ›F…+ê!œ×±B<Ì{Ód™–²Ráå#VfdŸÁý;
+ì,Ãÿ*ܵa±†r©»E¿"Ÿ/Yì~s~œèëìÏ„É$œªHÅ‚di“Ÿ—_°õ>+<‡ã+Å7ªÈ¡T^=ŒÔ!ÞÉϘØÆJ]¾<Ö•X‘‚îë µ4&¯9„þ—[Ë®ÿ
+9#“OÔ釛Æ×Î&9lýÚ¬%0‚­L‡à–fÓ¬m!®ÙÇ3È_¦r1äûèýkYÌÏ
+ÓÎÐfœ±–³ ]P  8Á¤N ä <ÖdÀÖ¸@¤Á!ˆÂh¢D)›ï¤8Â×ÙA ìÆù¯³Y€öP‹I4RÀ‰¦_Jï‡ÂU oø!=éYìÄe:tÖzñ¢Å:nXVuœe4›Ä ÞÂË:&þ SÀ õ. ‘.Š:ƳÕOÉt[s6åÄÄ?‰D÷¡ ¯j+)Ô JŽ˜¦0òèóy>¦Øf¢¨Ã{Ld[a‰üm0jš.$;aÓKÉ¥2¥˜4‰2ê<¢¾¾Dnô£ïS)\ž˜qPWÎÅÁNÊIš—£'¡&5žîæt»é£:ÁÔP{j ç´ýÂÉí F ±ñN¦ôȘr³Ù!ÉÚ¦³UêLl6€|DrÛÜÞftç *SsâéÄá²úËâñôBj  ìJ;‡õLž…\î©u’kÏtµƒF¸º[S
+‹ûŽù%úŠáaGw•?ìÙõ«Î„¼ Þ»¤ÔóKe§’DwZ&j5%žÚ/éÀw¢=Yë˜[/€ÌŸü«¹ýÂÔãµISøLWõk6NèWÚ}õTŽ?Jõ#ß="#Nšé X¨‰+‡øÎÑ-…sgntOÊ8¡‹qA„|òsÜPWÐÐúœQò~šk° n¢÷ùéR€ ü69ÒŸOM…<Õ4}°FŒ ¬`ñÀ4-FÒ|§±]s¥ä±ÙâUøN¯ÞB ŠKÒpÍ·ê
+Zкtù´wÚ®ºúD èHS®Ç¾µ%n &Äy`œÊn|öÙ|õBjs¸™Í•VÝ(Âmx@ȹúl’µ”pð’Ùoç6ªŽ½g¢nãò}Ó§Ê Ue3§Zê– «ì×’gàw(ŽKŒ¢ ÄÁ=ǧrÂ’JaIãuS8,ÄåÏ:lMê±ÌjOMa5sQçZâÕ”cQrÖÔÅ‹bM<2ÝIS*júVø©6õ;OÈá!Iž':P\ƒÛ-ßì¸ Eí=çfOë¼öÅ[VSK
+e>|ü–5y[Ö±úù.ï†è}c®°bxˆåÕL” ÃTÜÒÝÙÚ.j(:úûñÀ°¬q†oE9¹›7&^Gv( jQ5D—Nü üß½AdH%e ÓRÄû¾B8Ÿ‡)üÜ
+¼‡\À¾»†Þ…ùïZç’rJ¯¾`Ùè&›`Äm(B‘1/þEß' ¾Ae1ä­Ÿ/–:@›vˆ—át05B4Ê:žCúý“ošAÈmiBN­ > ¯m~Í?@ÈÌ: з¢SÕfðgÙ$¿Šw*ÏU¾Cl+’X—¼æ0ÏëôS)ªÊôŽWžåDÍ&¾Ë˜;þ÷Ü1Êx³¥·¢rÁ@²m8&.–Œ7q4)æºÝd¤¾‰^ƒ›©3x ÷ÃÄPô^hõ
+ǧïh——*$.þr<×yó ¼·èkp‡x2 B*ÁÜÕĶ‚“Õ×ÁüïՃТU½äçVo"@£7*Š©Ëi >x¯Su]‡ùUŹ:u<ß*=ú_çU¦ÛÒ)‘ås"Ÿ³ ßã ðUn›´¦Ø…lb¬¤[òüK$ܦúrWƒR,ìR´ðVãÆiyq"O÷­^EB dýæXÎJ Èã=KÌHÂ$,dþÕJpËŽ¿ %ðQUÑÃwŠ Q™ŤyÅ·7£H± Γi°­¨6 *öè ”¼4ªØÝú¿§0Lu—A:Ž¯´š©Ï"ÎfLÛá¿NÕG-ù.çÇ0
+JJ+n󓊆(bb§½?œÙ\ªÞAX•uHI±>·úöâ×2!iH½a±Æ*Oâîµ*çÒcu¹ (b¦ ÐØ:£=ëVØð%Ó-„Z?á43¦X+Íøµ·›=,ú¯5}
+âàIÚ «qò~ÞîSÐK þ|R§]Î8-2ÜpÃÈ7Õ<`.gZ½SÍ™Ãî“O@§:½ïöæðr‹'™‰.ÑퟂN¥ü·­J~ýGHαÒ2¥=§ü „Ê$IÿÜ€0‘ˆ|‰k4ðm„1FiCØF…¸úha„:*P6
+EÌ¡º+~ ê
+ˆ7‘•ªŸ†sA
+|jµ”ña{ÞÔÍ2,^ëÌCˆMT«eéWº[çuxÁ5"£3…j†ªâ9HJÙôó"‚³ªÒØV¨)õäšÍArórŽ9¸PÏÈød¿wÕq0qÞ˜ìkù°+&±u±×ŒÜ€ó Oä$ɘ Áë6…žºna/=ò¶Ì
+Ýý—gÙ¦w¹3뉎åAÉ)Øtµ§k-¾Xoᘃ Üv¢ô£¡?*¯Æí1ÒGi„ï«ö& Ü@8Ö:.Í»“jÇ#1hgÒ½i44[R áœS ²½Ý
+‘ÑÅÖçr³‡]î=Ü•Oaë×Û"N¢b[w' 4ïÀ¨å£Ì¿wúÏãVô5Ý¥–Ì$ˆ¯_‰˜,ÈOAbþ̤aɨQúús‚t¨1óÙ7i 2•`BÓzåÛ^AÔ ÁysѸDçjƒ~ÿ0½íj-»*â‹`I˜Ôס#ÉmÌ‘™ì(ÓÊÚpðä»Ùüý‰#ý…yVH=µI·§Ïºäˆ¶‡0Í·–­Fü›íœ=óC±wQa,ÁX*0ÊámÄÄWl}òU°Ã£ ÷¡XÉt'ð‡åïVOÖÖö:’u‰.e€Á#eò8qCðAyñLˆm…0#D±q·LÄÖdõxŸçK¥ÇÄ r…±ã Ô3e‰iÃÆÛN‰0㋪›ƒ&,Ø1”œq¯+Ó>è{±Ó 󵥩7„%ÑñÊ:c‹9¼*–CéèRÛ
+éyI2SPj eÐÜÛ7$”™[Ï©îÞ„—@åS¶­+Œ…†À ̦*šät|+‹ÆÁÑ»ÿè ËŠå÷:™¼¥lÂï,b!L¤*=Ú "ÄqSÁ’(ãçèÒÊ€”¥B59 Å´ê#‹i0¥› ¬D³B «B@ð LíÔ+Û{͈ÀÓ¸i¤@kÝt¹F0! ÙŸK£]³Êë¤ód)ÜMZ4Ñ?Ö<Œ'œC·Ö˜ ÂA¨NÑ™j \¦„0ë[å ÍN%B›P ³­æö:0ŠŒÊëZüêÏ@ð9ºÀÍì0)‹çÿ/“$9wŸ@wÐ Ài-/}¯Ú÷ßúKø­êbÅ“­ˆ.¡8€‰Ä‚ýÙ DЮTàYW¢`ŽW÷e„5lh)!CôÑL}>%E>ÙÂ<`$3d ’Ñ5ÇE&´Ñt™ƒKÑWMè‹«&ï1áa©P®qGàG‚îÊVDîX/Œi¸äw…¤¨LÁ”皗 Êà;4ꇰS'«PB`%vô‡âRß߉³$3UýâV‡ áï|™94àÂOl[Ï 7˜ÜKè1?ø醈£ƒalÆzÆ<q,#5îæ¿&jÞx\;v¦pF!Ü !Ä™•ƒJ—(»Õgʹ`LõÿQòD€ÐñÅ05™Ý畺Þ%s³ÂÆÈ{ó‚H1  S7Â|áÉ4Ìê¸2ÏmšàW²_›Å¢â;û ›™[Á
+Í `ë,¢ÕË$¸âå¬@þŒÂšE_„§z«bÉ5Ë ¬¼|S÷æÇL$…^yMçu¾é<% M¤)åê¾Å&ßJQ•!ÈÙb+ȳu™9ÛL‚y±#Lé$JØ+á(h—¼y ÷îýƒQÚvêç¿~1XZ°Ÿ3˜¯G?ÄüuMåÖµ=URBÁësÊb®ÔÈiW‹@ïÜx§È cT~‚áKÑ.©t€qª»Æ&å.$°wðm^Â|$)B±¯§"¼ÙCýó"8›ŒE:[ã¬#Ú’VüEb…B¾n§‚„øáúì7ç¿æ5{U‚43#ô©¹à3#Ú–7ÚçÕ2gK/á ˜)‚£ÍKHI&©n2!–ñÐþ~¥¡N@oÃ_O¼Á ážiÈYgjŠPÜn¾|‚Äñ^LÔ‡(µ²ÓïfèVÔ†šÜB§ÉP+ôò"Š¤Ä=$|qï¡Îqq* PØgLŸþ5\ëâZÝoegí IÄ;¡]¼7Zê;*yí$‡ÄDØaQËíRâ[U½u6?®cÜ’içLÝK„¾ Osô½]Fÿ
+rÖ(@Û-JÚn9Óç ¬Kƒ‰”â—b+4)¦)
+5={ÙÀá^zGlp‡’è<rÌQºBƒK(“+¾[ñ³™(‡pææ¼$êˆã®—ßÊ:#A«r_ŸÖ©zªFt¨ŸSËC•Ï—¢{)!϶b§©\ƒÑšÞ¾"?’žñ
+IBHèIÎa)î)]D ÔÍÆøšù<Ô«½PS@ –æ[eQÉ$)/‘Ç3ÑÏÕ®V‘NÂ(ëAŸí0ÌAº¡™î9†óÅ çÄ!ÝÆ^Â#P ñöiAx=œ!éã·sÛŽTSìt™—¯›|è8U~óP‡{× +Q(Ški_ÚSRq…):1ýl!
+K­2¾Ñcp†3Ÿ(ÿòÀ µU"¡bÕYwŒœ#73Z›<Ä’Vtï¢`[)ÂN!¬Dþ£•¤Pu˜t &xïž#]ΤÀ·|3ä*¡Ée5ÎcãAÎ+,ÞÒš°ÎïâœyRÖ[vÔàÉ[¯)sü!ƒ¾Säû:Ð/7Þñ'~dÞ
+ÿØ‚¸
+nƒØ÷$4náC–&ÄAjpÄŽ¥­g¤T€VÝ°~+ùÆû® ¯Eò¥ê#µ”õ¾>qh• bn›É{0U8Y„®,AÐF±ÇssùXت­Haôjîðû:pŸHNïÆ~+M?jÏŸECнE@ß {|[GÎØbŒ5àV@½Æ•T¸œã§  ƒ—rTUhÕÿ§Ž‚eiœyœePÚ±–ûús+ÆGÄyJ@–Üg[ùRâ[•&JãRïëàF°1pøsbˆ|Hv¢„)²Þ¹l¬ò‡!ýýãV#ÍÈi‚•¿¼ˆª$×Úí÷&j'ìd¤O¹2é¢Wà,a¡!‡çUêŠê…™ç>Rf‰{ƒXžO”EXÄ
+mu_n4n€ÃwÞð¯<2jñYÑsL^×K˜lN1eµŸ·Xщ#¨‘bp\b!€â[q¹$I+GÉ` è'ë
+r~qb¬=oÏK´†@Â}ê‘©KZZ‡ó+én(Bt!P9±å­Ñ¨Y^—¢p½lN¡ä8˜w0¹ÜK\tù<qœ‰¶ðr3y”¡üxg@fBÔ1÷ónDum®ØäaËr¿?̦Ð2ÀüHxMòúób2¯–ôó ˆ_1ï@%Z ÙvW;&¬ÀÝ"±Ë@Íì²i‹ñ}äþ0«
+>
+ì1¶3#q´*<ö'JŒ©BÚc#`ohrz¢+àÁG?%ò[R[^Ň—§W_m¶T’•DÈzÆ%çÞÕ¹© ž5©Š¢bD[„BÔGû*ŸÐDd¦z)*Zç)Á~ pòÐ!È*¾EübÀa;9ï¡Ø(·äëpCF1Á’>,ž¦üTNj½”0)Ø
+´‡ZÆ`·ÌÚE»Ôþ]Ý@&¾šŸx*¶Ã>ÅàC , öyǯ(€¢Ÿ¢h‹ÙíÍ’êµÈM`5¥M±ev;íøR$±xR~øNeçN¯m=@–ke†ÌúãIê•&Ž RÎg¨³ð¬V'÷ ÿ(Ú8’Éaùíh,)¡±$ëuÔ7ÐÜ ÿ¤”;ä‹AyzÕÐo™Lë‡$Ìà¾V‡ûRlÙ†Çæ´dv¾ƒ´hpÙ»Úc€°ˆ
+¢Ø,æñ§ ÷ã>r§¸z'àm»Œ#ÉOW]n…îCzüîÉêú)èzõ?Ý)®Žñdá°±ÜÏ–Y+uˆ£üêï ËÚâ v8‚œ9 þ¤·ÌA _2áTÈ]L·”ó§®MÁ%qM[ã~ØOA_
+ðù“*y1gÅ ÉíÁó"ªå,T•3¸¸ï˜ß˜/>û·ÆJ1žc¦t3ã2Üž#?¼–Ø2šá¢Zuò·0 T!òcÐí8†®°O<gÔÇ;]@ß*p9í&¼nÛLbéçHƒc]ë"±;„‘hz~“°_!Ø|Ó‚iÏ TR&R"1=ÈÂð3$fNwC
+k ¥×KiŠµ ¥R­ á‘Cò»Ýž ¶´bÿ;I“Y>³éƒ! "¨ ‚ ·ÂePªæcЦtB(YNç+˜"ÞˆíÊù¥ZЪîä H’µ£™hC,a‰Zk,AØ3î"m(Â’½:·ù‰£xT5ÛÓwðʈ³„R<™®“m:„?甫_îû·O âi#
+"\ñò•Ud ÿòÏ4†ÃDhŠ“È!;dÛ!]¢;OýéžÉµPî_ÿºa°NxÈĈbçÌg”³H,2£ü
+)+,•×Q¬v’deÒ+{ËÖ3sHõ¶U·ïdƵC@†‡W±¥Šôx^üôßÎÜÔ©$¸ 9›¨ãpylÈÜ3Š±’/¡<6 p›êË"„ƒHU„‡½ªx z[1¹ãõU$Ê“¬ER˜ÎJ•w’§uRÌ ;IµøTåjBæƒèg%ÃQ¦Y·s”Ê…a£° ‹×šÜŒ‡×Lþ”íŒê0ã»weŒA|Y5Hrm7.G÷:©ã~%{m¾ï/Þ mˆÛ‚já]$×ÙãÙ_ý¯ã¤"’Q‹G‘@(ÞòöñnÂ΃ÓVÍ‹ö ‚HòD ê¯£þŸÁØ6ä׿…ÄíA¦Ä#Ø<ô0… )ÖDý°ˆÚ&ËwC ä¢nXˆ5–
+ƒ` åmÄ óAÍí ‹V¡(“•à¬3ÅÔB±‘?
+6QRã³:+g 1ùÐp¯„N,-]3¬+ÖøÐàðÄ8Rê8Ök_4¢H_¯#î`v !‹3Z0‚­ÚOŠ%¢)°T—`³¥ »?îa4æ\<•ÏQ\̦º°âQp††É`ײ§(Jy‹ïžÜ7“¿5†'×NEç¾>Ç=}¥S(&…û1’¿á‡Íp\
+ýƒÉ¢O½Öà$ªüò{Š±PSTZr=,ߊ£øF¤²eã¸DL>à é7x× ˜âÑ ö£‚¯nfÊÄo ðæÖ ¢È„6ÇÑ»ÌDÀ½8Ò ìB†Údz
+6Ë'vp,Vù!?}"]‰±ùí‹* |R2Ÿ ô{6þüPN›P€“夣h
+£ŸmA På 'Ð&R‹Sl¡S¢?ô›Ý YÇQhc°ŽÅEˆ2-Q‚bc¢ÜË&•>Í–fòä\,
+¤…Çïò½éò@ØF ¯ýlUSM|Š¿9©?:2™7É܇”¤Ë>„«àílu©ôÙÓÕ ³-ñ·ß<¶Øg=þ„¾I_à­vvÖKÁÈÏúÐR@xÕ5©5l™R‘ü7eêþ$†‡ìQ–ò&X‰µÈÅ!¥1¨øüF´I†3ù×:ÎQ¸¹Q¥‚%Šy7)ˆPß1v–1‘Ìh­±F?1Í!2+(ȸ0/°B˜¡ŠaL®‡€°³I)újÖä°6‚WïÄ1ìnë–©$ÓÚ­çû åuZW­Þ¿ÒY|B(ïn9&p +ïNÜÏ!°š&¶Çvs&ù½ú:ˆi ³Aä¶.ÏÞ¬ÒÀyÝæ½xý#ñ‘Œ‚”V.-ØG5˜‰P0Ñ”¯b¡øóN÷~mˆ¢,c…¥:#Á} +<´[@À³Ö\8‡d“Bqëô:J­fDðô‡u¾6¹l ²3ç£pˆ _uûL
+ãá+,$f  ÅžëUÌûDð/²Y­Ì]V_÷˜öí§s'#¾—×>LŽ®Ëº×§6bQ¶ßÛ ï5®F…[‹£0Ä k“üþ—ñrÉ®$·è
+zµ‚>$ø9¶‡^‡GåýO}ƒ²-)ßqºK‚˜ü
+1—Äzy‡U \3”ÐñÍ}^JØ'O²DóýùÒ÷"Ü©è
+óS¢gæ×ÅÉÑ£rˆ†1yËw`kÜ*è>ǯvhÝOZÂ4` 4 ”Ë0‰`€³ŸtC¶Q½ó¾5Jx)z
+¥GNXOɨSÆpBåUu ­Á†ñ)N‡ö,}ÝR$Ï·ÜÝ‚\Ú¥X°¡c¥ ù¡þ0ëÜÃPýVƒŠÇƒÄqn(–¿@ ±ÏüÒ99{Ì»áÏ(
+ÜB;ÿ‡LÄ €ÍeE¨á¯‚½/߰Ƙė+,ÁIÏ:â]qŠÆýÜuDL8 +D@š¹>”0C>ŠZÂÿï?ê¯êƒög¡Eu?¨pˆ‰WšýúŠªœH1uïKÑ)YòSÉS 1àšá'Ž»¢¤˜’$íòI)+sCŠ-êG“k:œï» ÃÀÉ9#lD ÂgÔlÜ’¡Œ Á/_êŠ,穳z†Eá5e8ãX
+Í€«UQHìÆä—|§Áä­ÒáŒõ-©u`?»)ñÞO„LǸhÕ+b蘊é—ë²åžJJ[Ú-!¼ m[^vKº€¸‰‘<ó{SZ|ªWV¥aÚa`Šèz`G餶»ŽnÇ~-„Yx1d»~Uç{‰lA, Ú`~òS?Š0sÉòîq;Ü zM”›1~( ·[Û³^`K@ZMfÿÁLEÊ ;€¾Þ1™ë~èc5³ˆtÖñc,%žŠÓeÇÜ`²ˆ¾•žMŠ²‚T$¤”ôØ»þI¸Íeý€2=š‹0¡h «
+µEy±}Þ&V)ÿ'Oa¢jëʧL¾d>eÐ9'ªP£ÅÔ²Úß“'C…[×bWàÞ,rÀÞàK™Ø§uDÖ Õ,=˜]UE¿}‡Pâ81û ½8ÄI¦Ú^|  â…ýÜù-øcã§ÑÛŸsÊ×ÇTBÛ§‡Oh­‘M2ð ÅòåÉfÆÂohuZö]'²`lO(3ÒòèpäëÞ/%«®[ÚÔ“{-IѼàè&9Ã?à$%Kæ9i¾•pÆßÐ^‹¤ù¡„‹!ºpXÊ®é½!ÒtpWèúõ¯°`1(*•úYôû­H3Îlk
+ÐÉzáüxŸwOàWz$”„1Üôuÿ‹¶Xd³zÈJBôCãÉ–1 P>ƒçæ!~ÃE<é^ÁA¤‚¢ñÝ›[†nxÞù©Ó¢ú…PHº‘ Nòg¤œç[Z‹O-Xx£Ä-\*ZáHu”¬¢”·1ù©Å肉èKìgÓ¤,<aÂp¡ÍØ¡á€PìFdŽ×H3&ÔÀ:Mxý³"=˜×‡@pœO«t/a¿qÁ ZáÈüg®˜]Ý*ÜŸScÈÖiXœ.Fþ0³¸½…ÈÚçŽOqyX™2Yl†vÖØA£ßEà»e#÷ªZyÅÜ/ÝÀ¬ÁÌ·A=N
+1ï1ãŠ3·ÔLñ– Ž¥á(+>Fƒ›¢
+na·¤O™ 5ÒX<-Éæs9ÀMS¦»æµè"ˆÅn•öBsœ?T`œ5\|è:•¯¬¦nu:¸#þaô&gCªƒžŸbx™ð¸'Shf8†…‹Ã¬KûÅJW² BÚ9î%È!‰€ãxì¦OægÕM3øCŠ__]CD´A’À¥xPÞ‰=.±ÿ;Lý?(;ñ
+Y`ôÎÖÂ]i¶8”ͺMº${KèôA³Ðs) D µˆ’ÃÀem€W<·C+p†-ãÙH¦¨œøG[®,BéN:[gl&öµ e$ÂÙ«ú»¸£„èÊ´;¡âKÉù”2ÛšNñi¬*]œï§ÂâPŠ¢¹J8Yz]6Ûfy>…ô(‘¦´«¯!cŠ"ѦGVÐ2xoÁ“*ÁÙ'+ .½p4Šb 6Ãa&센¡î˜à#ç1HÇ×Z|ʇ„
+0éÒÖÈž’‰…¸RÃ
+eè+‚ÂEÑþQ"7ÃÔ—xU<õ?ÞŠ˜›Ž•Æ¶cžsr®‚ç
+H‰”—Mrd7„OÐwЬ ’
+5G›CDln(GϘæsÕ9+Z˜ùuem½·Á Ý¢ ÞòEsZ!–Eok„ù#P¸¬%ÝšnÌþroÓM÷)‹D\‘¬.<BšÙ˜Ýà ’w“1‚.Ȝˆj‘ºÈ‡/ñaCe’¿:gÈ0—Þë2<qš4oä¸SR¹œ7Ùà¶xšžb®eY„©ãSµ4ÒBÞ׊^¡T2Ç}ž»Ø\ùì!'3SÈÿèÆ «”zM9þ”ºz´;uÖ㜢ÚEÚЕ·» ±D…âäªõX”Œæ# ¡Ñf³Ùúã×–á3ðd—‚Ûi½ÖäôUÇ·®ëZ•l“o)[ë±:C;%Š“ºOsP‘?‘X½«ÉyAv!ÿ:åª4­8¥¾Ï(Ñ¥S„ðÖf_‹!°HÅ¡£ŒOŒߟïäRÉPׂpQkÑÀȆô)jü?úð½t¥‰g=)ÞÅè)J²Ú¹w™\Pǘû54);ç«5¡}µ µ'#¿¯²w¢1ƒÞ¢Çõí×ÿšùö™Ýæ¼íµ?ÿÜõÎ÷Üë1.¿a~}ùt¥Å
+º†¾ö»ª_ R”c?²;æ~ÿEmôŸèy–ƒøMâÛ ›p«Óš¦Z±þè%7Ñþ#)]¹Díeܘ&
+rì¨Ûø`z`¹ávqX{ÏÆà*ÚêQ° *ÇLÁ»ñÛ{‡Ia á½@h}y‰‰ïcrJ¡Tó‚ §\f Ùø-:*€jÝ@*\JÃ#¡_Ÿ9!†È”žÛÒh\uÞ Þ9ó~sá
+ˆ˜¤ÖÌ“œH]´Ôê‹éÒÅÉšw:ÙYë¢1]6„ŸÀa«EEr&vv‚S•Ÿ?n ý@!ã …ù”¶‘]@Þ®bZ‘00³e_o·ò?·Øð"ú%Uö²‰pMƒ@ŸŽu^Â9²Í„œ@™)r¾úɹl2¤¡ßé_*£Z†TOzV˜Ä‚@ë‘’¦:OœÅ‹‘s2Uq(FŠ ²í)G‰
+ÐÀz Æè%Ì°!Df ‚’ÜA*”®tG´¾­aÛ<Å;æiO\œó£þŹ8Çt(cChzCmR'n gæ>ÔË99»'ÍIBî_„Q„ÔìÔÀI/ߣv'µd ”3N½ù ª Í‹‘èüîaIAàµÌ/C\¯œndž,x&¦N¨Ï‘ ¨eÂcᇑòM¢XÃYÝLJ°'y“‚ô¤•lF2²™);‘ZžBßc Ôq5†ÉbÎö1i–ÐXƵ.Cƒ§éÆQ_¾6U˜8fîXãMƒ2U™Ù‘<Ç"1ÍÂnG7!´}z”yùðdYƒüðY¼r¼Ýˆù3WŸPiTˆ=zñ  sakÕ9èˆÁ&ºòâ´$d,›«ï 9…©$Öç#Ôÿ3eA&S£i;|¼µ\qN8Loõç-h½üÙ¯ÖŒ¹åuÑ-Ѹæ·AOá>¾s§ºúóYÏzt]ÝHMŒÖÅðЯ[ÐÓ] ñžáJãü˜ Ý…{~ß·@wŽf$]\[÷yµ}/ey}|'ßWYÞþ“PaIêÜ!Ý–-rK6ÇII•æt­œøxŠ}Q’PxL4Îëb$Vºˆ½Ãø7mí‹}Âq¹ûíQ
+¡qFl=Œj·}åÎ …¦ÉcëDm`]²®›Š;vqBadkÌH…"<–Ì’¯ÎA\X•0më¼
+o¦¹ïPù ÁÃŒiÙÙ@r5L+Co>Ba9øÔ÷rû†[H³"Mê>…~UT+”|8àâr³HbˆÎG×u\2ŠÚžP/y7%¯jÎÜ,;Ñö}NOeCܪGñ†˜)ìç$+~¹)Ù,{Gü³»í„ÂÐ-VV,ŒV'CüÊNÈÔ«šó™’‹ËT
+”> :šíì8/§ð€0‡é‰#Ð’‰[­««^$7´ryo
+À€œìålòlR¡‡¿íò6ÂèÇ¡Ož:FÛ3d]ýŽ)Ò3%ø±!ã„úÌ8™â4îü½ÆÛ o=ëü ÿIÚWŠXòˆõà¿ßi4[Yæ~nüÂÅF›êÒ7_@>qúOè¿mú§¦øÎŽG‘í²…uiäg o>6ýç&@ 1¿íXš]Mç ­Ù¥ðÈéå™{$eµ^à³¥Ñ]Åÿx>ÚcåÆ´­¤¼SA-ò]æ7·]Hšõ½Éåâ¸k™r½*µ´.³”:äR¸ješYèT`R4ê˜t›I \¼Ž¡o6U0—Â[h6/~CÍÛÚeC4gš€Câ„Rdƒö Umƒ¤»Ža)‘õ(ªd¹ox„Íßß„ëô a%ÁhЩZÖ‡ìGyÓ±·ÒL²§Ûd'“ZаÔb£õ·=€2x7åå×ÓI
+€ÎÃÊtŒKoø¹Ï^C0§äAÓcò+7iÁ¡QVÝù`HCO¬Ê£…bÀ‚³—f¸tÐJ f}ñ
+(« –Œ$]ç äOvE®qñå9P¯æˆöÇm5#?räž™ÍÕ"65=]÷ðª Í ßÕ¡’î‹&Y»ûÈÄd^iIšð¤†;èêéÊÃpZÜ£Õ“zÃwÐ töƒuàÛ•ÆP*UÍ Oêt(%啽Åk©bo£‡á>TðD:}+!.G¤Ã&¦éifõCf-g†@ÅoØ¢”zbmCÐ R†÷ðÛ Fb.]k÷
+âŒNFyœqÎóRzyÝJï@T
+©¡ç¹2iA8Éte§¡ù5›Ð-—&„¿q8ó÷£>c`±ô8’Òïyrä_@~ÆŸ·z}Ü™è¼3Åؼêy¹¥ÜK£V$4Ñ0±-ê³ÍR1î »t¹ÆŽÐ‘¬åZém¥ý¤¥fñF-%†>¬ÛðM®L\y–ùE×_Ë ¤B=éÇë9äúç3›PgaâÿV‚¼dòëUº;ÎGÍÚ@æ`)ãdvQ˜8Y»´¿1‰§µ˜õ‘îCâ^þb¼Ü‘¤Jb(º‚ÞCÙctäÿcN€‰;+  ÌÙÿœ›)½¦ªóE@@ªüIº÷HÎFoSV8þ‡1ÐCgsòû, À0Õ”‹­ƒ_ä‰rU¹_5¸“% /(ÎH¾Ü.é»N©!ª2”b#Øä$ߘwÁK°Ev» ù„µ‡F,IBK‡ÃÄ©G¦‹4!.%âç4YÒûÛVˆ!UŒ55ᆈŸe3VYBJôÑ‹]ê`­$4ìpDÈB
+ê>ö褠 û‚ñ2 Ð`q5!Ü^Â]Ò®ÖùÎ$¥1­éYvÓÛÙ¶rB3*¯Ì~Ñ·£˜Ð¸ÂµNPE‰(’ÝŠ=ñ’7ÀLJ(>#nߤÃD2˜ÏXU2 Ã!
+: :³f;j7ôaA±0€Íö€ðédü+¢¡,¥Ù›§ )ülxË‘ ¢<
+=I
+7øØê5ˆy‘BBª­B;Kcñ
+±uøa‡ |J¤1\‡llB=[£Ü—£`»é ÒJfè5[%c²«ìJ7¯)°V׿Ý+—çÙ‚R/WãÆ0PlË@ µdq
+BÛt]<àðÞ!­Ü;¥Ü1taöÚ
+pd: »§3n DüXØ“Ü*-ÖyHš;¯n!RåêôþÉ‹÷óñXŒ¸$Õ‚T¥CLît„ßô®¦l¶Ù14S]ŠÝy™n·RWšÇlR˜”«loCO€_”ALW:y ´½öìTTtGnE7N3Š´š óuIQaÑÁæ<ù£Þ/ðX@ /öá%¢Õ––í  Æ…„ådÊUTöÒÄÜK¦ÚAù±·é*™ÑI°Û–\€E5­Fât]eJE™Àš v7Ϥ÷©õ^ˆñËQá'}—:”AïO•Ùà)å´CD÷
+“FÄD[h&±u²8‹YÔ#°.AªºÐŒh IþáO,åÕ©‹ =t³ ‰vÂ5”JN¢a9`Á<+‰©íH;„0œ¡e̽¥\’óÔñ"ŒOM\b¼Ã+N‡1ØVi}%q>ÇÅ¢ù• ÉBhwz åÂÅÅCCs®e— äàv\”ÖÒX€Áؾ?µ3£ššßB‚
+R`¼C:Ð
+8S( °8i?3rG5>òŒS]¨¨Œ œÇ†j<ˆr6©%Õ*¾
+ó%Ÿ„ GR$$h‚‚QEÈgi@¢[ÀaàeÐý£ —펃îáØE¦ÃE¡éóÍs!o|‚ª‰wXÈ_(ZžÓ@kv¼ã´Õä¹âf‘Æäf)Ê»à.’Oú×*gT¨ó’ü
+ºeî;N¨9éÅŒ Ú—NµJiÓœŸM¼¬/;W:t ¡ÂÁáÊèK¿ˆŸ›@ÑsxÏ ©1¶Þ†q¸”° H½wŸô¾ì;AÌ&˜º©!ƒwö+žK>Gz„…|€Â€††Ö}o½]—5»7ûò- ÝÏeÓ… ,éõ†k¥lM˜Aó¡îvè„ä§m({ Z!䉶ã0nC “÷\!Öž‹MÈšq»̈ß1“z ES0ªhƺ”€º ]¢Í¡«Ñ‡WiÑiÚÍq牽àgÙ9‰yðá Ôœ—ús:±°­J°Ô«cH¦<Yt"˜Ý(ÖéºÕš“µbØ)ä€2¸0¸Ç3òj”ég¯bº‡Âo£F÷Uê`±’¬Œ©ð¤0?äQï(Û°[ŠI•Àv¹ÚšÎ‚×iÚð­XSŒ8%Q
+ô
+9éLà S¢?Þþúç-<þþ÷í¯ÿÞâã뛦Üw¹JQŠ²’ÓêUVÁšxó_;†5ǤeÆr”× R’j(ò
+~ýtA™UhN‚áäÅ/ÁŽÕÚ²A\Y¯¤²ûFƒŸ€Ñ•Ó†ðUºBÛ¦ A’ÒM‹§<¼ŠÚÉzÚåAàR×*¡Ùµ2“äaùËþFÄ#Ö†öÔ¬¿ö\ƒ^ÓÁÇñ’—#䦤:ô±žñ(
+Ìì¡e1î
+²¦½&°\€° A{²-aE†X!+ZSšEjHÅ.ûù2Š¥d­å˹~Re¨Ì`i?\F>6D¦Ì²!„êF»ädÃ… ü¸’Ŧ$äÇ>gH x¦Ýùè>‹\Y­§„í@"+!†ÉîÃ|±tvÙµõƒÝ= 6qb\fš˜$Y©fÉÁ.L£i&5he"*D¿óßgÆD\œlo´ÎgÆ'!!Ƶ-Ä ®•Œßd€Â/à¢<ýÜ·K§ÉX„Ù¶’4'x»ô}‡M hjªÒ΂È,i%+#cÆ›Q¤Ö B{‰Õ¡‘£„j6eײÛyÇÔ¹HJ½Ê溣ï`”t
+ú@El*n åƒa¦#ï>ĵi¶ë†”¤M“ÝÓ†àX*Z8í+ Z3;0$¾ä›N´“0˜6H£¶v™6Œz<Z+—1ÎL\™ý ÖN˜MŽÑÍØX^)(.îaöà Q™ÙîÒ0™·èºe0Ê Á`äTàLÂ1cç Ö$LaìEr…*£Å¦•#Ä¡û„óF6€B…ÎQ—ù£ÂìRR~­¾¦BŠ¿BQ´ûÔšæµ:òŽþ×HhjÝäM™¦K.Ã0Í%G“ö‹BÅ“þ]I%ºÀdEÝpšH`࣪½WáoÈ'vÌûpr*´ê2{%cÙgñ)¬ÍƒÌÍ
+OBdѫs#ZbOQTs
+l¤ªŽ3T‹ºQÎÊÚABèG›3’1[ßÆ!·Œ•÷ORƦ2ŒpH2HkÌ+­ŒÄŽ’S![Ú¼'}††Ê%=þ˜ÛŸNáô&r ð6ÑWÈeV¿6ãÃ$©Í‰Êèo›røѪ=Šì0$Ë;0Þí†êMEPºT„=»h!Á6ùéÓg’¦F÷ìÆ|e'™´²!èâÅo6û
+‰îeb@³ô¨p”…
+m顪²Ã´=y–»Pw›rmWÍÞð
+„1X~ätþu$ ކݥ°V颸 àÁëæ{· Ö.LELØzÕÜ­ôŠ4mM¼Et¯ñ·¨¯c¿ I/ñWMs*•ÑhDãsL_6ÉU7¤†a¢3ùlü”¡¬1Z*¤´1Æ’O~¬eíe¨Ýj+Ë&q²wmx§Äœ?Gf¤„ý†0M<+»UQ1›z²¥–Mdôgç¿\LýTæ ²Ð©a<†ädA ,™·cwQ©½ö•ãõr‘€éœ!™:5¬ /Ô¢p^Î+W™Yym•œ M
+Ïy¢iˆ$Ù5å›X:Þ®±À?"/x¹òõ3Ì–:÷©¯xÍ`Áj-$ÝftV~i0ræ
+Í¥©»ö`Éï-Çuò9gÉ@W7<Pa4¢hh»BŠ»hê'e%DŽopÑ´U\âzéŽü;–Ì,
+ë%ðƒ£¦˜ÏQU+ÊdOÊBa4@íÏè.ÄIŽ¶
+ÕùðFý{DÿáÒÁë¸r!qWŒ<:÷(|*t±‘BˆõÓ=îúz‡¢ÆäºB%9’øçFù{ƒ¤w7]ô˜5‰ÆGƒŒ"ÑÀQ)¨ Zš³J\ìGZL|HYf“Pª™†²ƒye•Ý‘”"åÚßÑÞµ&z˜SæAOÊÔŸdö9#,·<›µ©d-ì` hjè:WÌ—aè8ÏέHã¥}„”}ÃÊѵå2Ÿ^^ñ!åÌy2/kOb_a¼^>H1ªj8&W„Qɼ¿£èÉS ø‰öÞOÇri2
+ÔÎõá;é©0£•±UƒÛ°óJžÂX«)á
+>kG±
+ÙIÔÑöΠt
+OkÚY0J–6ƒ¥¼@è
+âR^›oƒ<Æp@WSMQ¦·˜ß¹™júÇøËò¾ìç0T0ò‘æS¥Ó‰«Žˆ~dÔ1ºÓóËÿì¹Èmy:Ë‚Ã ©§SñËþÀ¨óÑT3‚ÇÕ”}SÍ$㇢Š|S €¯cRO®¦ú†ñ:Pov„MË£½ ú¾}__Ðe–?þ÷ƒñŸÈ,6€ÿƒq;AÑMÄí÷¡Jؽ†~u´ ø¸ŽÅrŒ. A‰p‰!H8“=ç=Éü2Š¥Õæï} DaùgX´™ŽîÁú„#[t— U:–¤…ÐîœÂZWÆi<”Š:c‡ ²PÕTä ÿAÍ1©ÖU¸©‰2 ûúÚ v…A~·h_@ ´²Pÿ,v¹ !~Q6Y¾ÄVÞ-Ç£'t¾ð'°hßõ锃åNpX°ï‘]Ç×0¡«±v¼m‹êAm´ºqq¨vÓ $­ÕåZï}ÛHó}µY
+2Ì8iŠòi:ýÅ~ádË<Wõ±Œ³~¤‹Ú0ÌØçȃÑ^Ì\yšÀ?O²¤+ì¡ŸŸØÈR ³f6ôÙ«ü4•³°4לlsVùèSM׈‰Ù…~F±,_ÈùGPa)jVaçùšz84iE³]…0õ"ºƒäl¿xž†c«é5⬃ã)4n¶ùQi÷ƒ„BŒþ9}ßDéòd"N±5ED!`¶˜»2γÌí¯¦2 :©Ïóp2R
+Û–ÕT\3É6ÐzÛü`4ps˜†Ü [ÒÂ×F7¨%M]퓃ˆ§EžŸlÿp'¡©7D¬J°Ô†dæV2*¯­@ :Ž2™ì!2ÝÑvK™–0N[ 4Eô^ŸmüE^ŒL‹ñÉéúõ
+c›™¤ŒþØDЃÀ†Hð\4Js@&vQ–Ôw­Žq˜w+ÓeϹ/ƒ’”váÞ\ü°†éÁÏQ¦ÂÀ"ƒÁgÛU8U67•·ý˜xçÁæ"ÛÓ®j¬*ÉŸ#¿‰V’éèU¯NöYˆ
+¯DMm:s…G¡¤IŽÖ l¼\Ô°N² I®žŽ{h®¼Pnz ­ÉFw ñ”’ó…4oß$~§6°ŸQ¾†9âU½„2!-xÚjª ÄÊ æˆ;±¯¹ƒþ««_ïˆï2§ufBd9‘D¤êãÖè¶f…g”vØ- ™kØgÊ[Aèf€LdelÚ1sYÄÈlÄ´8t²ZªªÁzp/þ9Èâ!¡Ï'g±„¬£†'Ô ¡<QH¬©SJ »N#ÿèž(½ì┃å!*8 kØ·iñ4rÖÂÃ|ÿ"šM‡fSßwceÕ#dƒ1¨÷eß%œP`GWÛ²Ð<Üž™—ŠÙÔ¡­7À(>CÔU8¤•o¾€ÌPU„€Äž›þ<„ÿ R01T1”‚ö•åKÀ[|Úž×(“ð}Xf,ð¢Õ ñ2Ôv °eI®ŠPbV6êÎâŸ1ÌA@ZŒwÁyÔ¼?]›€;<¸uC±_X\§@èSÍÁâ΄æpò,PmC
+^_;Iód]”ò(ÖdfΉcì VÏb„Üd•Ù•±!èHaS?Æie¨Šv%Kl7 f/Å xŠõõû«.sMi’Jÿa_\Å»¹G³¦œÀpJÖ­8d®,£7»±—óolUÔøçh^ä
+?µs°Mz[IÖM8…3¸Ê^UqZ¨G–êœn2Jä1ú$ZÜ9))ñÛ­ŠGã¢Ä
+49®
+“c6¤Ñòcn"Æb¼ÑÐ|r$#ƒkÔ´>Ob£?26¹b#}²‰uf?ÿ çd a¼§•>ß_ÈúpL\9/ê=Z¦cŒ¦¨k33Þ„O&àÎÇkâÙ}úAiãÉG–‰™«¤oƒ—Bu jÐeÁÞ]ÌÍÙB> áŠz1v­Aô¨R¶#Ö" ^G_XüÖ
+O n-½Ÿs ¤ )Â))Œ wÀxUPüAâÄ-vÕePÅcê!L—l”/ÓŽ¾®« *»2$Þ»3Éèi:7ýq³¶GCj„ùHßUÂH¶œ´]öŽŠývAw}ÓˆIËÂ1Iï`¾Å†_—½Â^\Ð,Š‹Û&z JªA%e;>6¢,GœôGmBf‡l´ÉðVY´~×,BÉ“B§Ñ8äªxÛ´š õu™´Ø ¨M;â¡V8@©;»ŠÕVz@âóÓ9B"Æl)’#Y%©F°W]:$)u±'3-æ¸c+®ÏÁv*ãµn¡±Ìž@S0Eµg!“Ãg:Õ+²<wª„9:ä–àû&ÛæŸÓ!;!±’ýð†õ«¸Á²!•êUåL»)ôýcoǹ ÊJ|·Å#ø¶;nâ’;§h¢Z»†$Q²¹üˆù6£?x oÚÛ'©Ø
+Ô‚ÿkÖÜÌæ•xåx?R×·Iâ׃©ã“ø¼Õ‘„o,ØNNÐ/„ÿÒ4 žP³Ñ ê×i.‹oÞNŽ,)†ì/ÂÔÃk¨œÙ?± 5é¬DH‘ÇäÓÙnêhåáUÔ†½ìæðØŒ^¬
+’Ùûˆ!T§8 ?4ëoMÿùJr@ÑL(™â—áÈóïÝ4ȪxÉȺF[y~yv䓧Äe– QàQðjÂŒ?…šåŒ¬Š÷ Ðè„T`*‘cúdg‹±ÃÅrå2ï0;ÛäB NE°ÝÝ<îøv‰¢S«õw7ŒL¼-'b&¼sQ’ï°—7Æ
+÷=Ò±´§_†ú.–,áh¢ÐƃÈ9­¿‹%£á4Ⱦt?91ÐŒvÕÄ·#Ý…t¹Ñ¶"¬aæ¢q¨žH§Í
+Æ«Ð`¼!Ù+,óØ‘]êÁÒ!tfH1t°ÆK°.6¦j °Û&—ØÄ8Rz1HÒ×¥º&ºø? qä’¤˜¯<3ªüˆW¢¸º=o˜rE/ÆNY¬­qÞ@õYÆú%Uu¬ú°7²ä‰LdObe¨+zÑPX!Ÿ¤“è@þ]ƒ!  ¡ÎA®í©·CR3WÕ=.È€ÁQÝJ]À(p©J «„È)Oaoù=ŒLÌ'FØ0ýð1ù3šŸCq@ß¿YµambÕ £X¬Šch218]
+]ÙÆ$³^ݸ~ŸV"S…:°º#{¥¾ZxIWCXÔéOG`剀dWl€ò³œú7¤c#)Çìö©H …‰±î¡ÜSXlYs@ßj2š8þlfS!'ãQš–åúØT#x›YïéÄ™
+Âv›ƒÒò„©Ð0H›j=ˆlºO¼ã•émÞ_bçùIéÐŒKÜEùÇüð–%_·Aiàú>¼¥ƒ€ ¨Qƒ•f[›¹­ïôm  óº¹@¥óÄÁÏûéYÆ [îû¤JBm‘-OÑ%%µ\Nd öß"ãu+ñ$ÞOÇ`Ô¨rk«Y³Án&óº»3”7ãl‡~¾Al5KFd1æñ¤)ÞÄ>ïFL5«c€lt虂ÔûU„ÆJ©øßtO)w PgÀÓX·d³ù#_{³@çݯ`Ý"¾æp.Q”UÀDIÙ›{;7PKlä^ñ^Ï«Â ©DÇôõ1fpÊSäib…ÂC8ž¾X‰§÷JÇ5Ýi„˜&ˆûE€bZÈöeÃبþx‘>±IÞ_¥ók)¬ì© >j¦còf`)1á=z&¿îŒNòšè3†ˆQÍãe÷œnŒÑPK 6%åµÝ?‡’‰`«<=’[!ÅÿKÑV´`"å=qe«íÈ13€ —¥M'dzƒFð@8ª¡å mUˆQ† ˆRÝbFŠyEñSZ'Á3V´s«e2Xµº\èõbò+²š³» õb"÷mÄ>…üWÈŠ®§sÂlä ¬AµÙ! 1™X[þò±Æ ÝJªL²Wá$‚Ô¾¾¶"_qÎ(sXq¸>®†5¬v º0aæÜ*ÌWV¡Qoسæ˜Ý­² _îBxç‚aùM[Ñ_Sˆ¯º&—Õúã~îúþÍ’š[ÚpFjÒüqrË‘‰iMQ<ñ©#‡ö‚±*®g"âc@z¼sÒf—Õš3_ómtv{ˤ—Ë}¸e!2Z‘`²áá«iÄÚÊøfY
+Mó…êše šURåöS-×2¼’éÚfYgØJή[V»öÕžžöÚé¹eŒ
+
+ŒˆR²¹;wú³>†K%/°tÙS8±LÈ¿v¦œ@éSµ¡73«¶GŠ$Þ{ˆ1]’:g«§ƒhgᇬ.6÷’Å™pñQoc5Ö8VFùˆixeÉ«ÖW.eÅU÷ˤlQ»ØNuTˆ ËÄ&93òý›Æ®þ—ù™¢"K{›_|7>µ/8©T{9R%¿ "ÔÔ»Fn,ª:Ž !·^²N0í⪲ p4ªš™ö_Á(;âˆÚ+àj"c÷ÆË7²c‰¢+àhË rŒÌ4…–Ù®V Èc›Ú¿Î}Yÿ³*D4œ¢rŠ¸Ž`5_†¿%|5¨D€äÆ
+™Õ‹ZiQ‘.S%Ÿ!†"&v²™úIq^iîÊ?¾Î
+ø:p(%kÕ¼dæ6±Ðt¯ÆVPuÏâ­¾ Uá®ë±f˜"˜É¶ƒcú§îª™‘bÝGÏë4¹T)–—p
+ßÁiß k­+ðCe’²OsÖcÂœ8ÌCIàÆ8žø±ô»e0p.‡t‚Åöñv2Ò«]g‰x-]zdHŒ56Ü–áÉ'4 ½wŸÀÓÙ Â’d¯ô‰Á„md±ƒE¦G 0 æö>xÇ ’¬•wóP$‚†`¸îg!ó UeÏ×¢Ï(…#±Öfê/ëàñÄOø¼Á®XÆs8õ&ùá39î`;¹öÜv˧Éï&ÙÞÐî¥Û›:ìì„âs ž5vrŒÍ•lŠ#ír³µCÉn9i”i‚€òÝ:(Û¶†8PxÎÍ;‘¹“!ǽ›fO”Gß`<™Éf\Q‡ž˜9ÚîÌdÔµÚ%´²§$ ™¿ †±/@¢nÇÆNä¯1 á˜ÄøaÀY¶lÒIryEf߶ì`8™ÅNÒ"@Œ]3ÄÆáp5Ið<)J¥¬‘c»"AÆ=žù—MùfiΣíO½u‡¯¤{y.Èr–(áQ
+gQœ‹Uz¢m ƒ¯G&˜dÐYv²…肳ØJCƒ
+Ð/ÑK–’’"ÓgÆÉüfHøõÉJ_Tœü™²qôäW ËȹëÐÛM£ŒØAx§G‰eÊ^ï܇OÍCîg¥¼™ÛÎ4EOÝ]0‡¦ÉC‰×ãá·Šê•YòJYŸ7EÌ ÏEa¡ã…¨¸¢A €qIB]Læ%E>wØ6ßÒM†Í
+ ÊŒðRD1PH‡¢ŸÇ¢ú±.ÖYăæëT“%©•w–
+Xœ!‘ •“©‚æ#óhJ½‘Sl³¦ É93D ÓÛŒëÙÆè«8QÂQ²Ör’¯Ø)+aèíÝ2r‘Bcª÷a&¶]Féj’;¢ml›Ñ”np¨#v2` ³-Í]h;Á&e™s_‡©Hü0Â9Á³"â4Þ¿Ñq±oòáp9YOÂ[Oîû—Ÿ
+yä üNOî›ÚÝhº|ñÑ}›]
+Õ$m[~^Ü·aÖÖLi½QŽî›’Á,5™Ç
+ñL#]\v*iRjþ“l»»ïSÅöKJ¦sâkÑ‹aÓ‘™p/7µ§ƒyÂCj¾ýæXvÊg C,òÉD°vçî¬d+oÞ[E³G¶2+Œ?ÒêQy4ͨ7cöÖ‚ ¡•Ý™K¥´ã:SO*«ÃÅ‘y}žFµáÿ2^&ÉqÝ@=îàt`ÖÞêÞJ÷ßú%RýQ¹q„èj| †¬— «?ø½ãŠÆVÛªÎ/5æ˜A ˆÔÒ!HVÍsZòÌ,ÌÖÖXpå¿.ƒ©Ó)ÔwÛ$nÏDˆ¤§úZAUn¯ÐºéŸK
+©‚˜œœA¿¶^ßE«x¹D|ÈB‰Ý3X$£¥‚çë˜Â©`Œ ÷ ÏÂÏElÒyV ¬™v!\Ã/aÁ‚$ž¥ÇÓóeÖA ŒÙD-i-MÙ‘o„ì’sf“Y86ò'c qÌæÇJàfÚ?ͪȌ£¯1‘N
+¿¢!»,vÁ
+³™W¢Œ1°&ÚV$¨|»îwSVÚ¹š‹„1c1ÁDok×Èà o.f[ܾÉI³{T Þà%ÊÀ2,?µTÍ4ÞÍ06ÛHº’^b«Öj9¼ÂeP"gI±[Ôkc­3„]ÈÚr⬵(ôU`[Ðf¹Ž ‹GôŒEw_¡åzÉø%¦_
+«×˜ä™þîøÛ†ç?ÕreÉÀZ ÂÝÛj‰c“«%…£Ðm‡o‘ÌrSKù1úÝ‹lÞWá™u>ìü˘G‚4tkGP·Ä,ÇÚ¶³Œc_ncéïýóÓ9ùZŸ5ô³,¦óŒ"¥%»S|-¤{ÆXDwÆ<€Ž€,™
+({2γ5û˜³êò èòNÚ\±yÎã꼬EkþbØ^«~ÃY7 Ã~°ß¨bZkò<Ç åðQ£²ÍÅ;Ðá\EºµÛo@‡GáäV+
+Tm £–¤OÿòVðt_•û #E
+„ÂÔ_ è¸JŠÄ@CÝÖ<ÜL‘ûBï}°Î½dûØù.¶ ÏåŽú{¶rqê‹çÉ¢ÈyqüÁsgˆÁsVÐÁsgÐÁsº²ô)v ¬És<œ0¥ß¥p:Hw µÔ$ÛEè÷¥½–þŒrFžàòØ-õYí¬©(øÎ>WáyNxùÆÜ
+ûxƒìé´©
+«9‰d¤ /™ÍÀT÷m¸N¤£3h.h9Ï&ÒÑ`0! É´‘.0ù,u?TôÆtþÅâhjŽä6°=˜ŽM¹ÊD7Eð:9™.×—È;ŠF‚…>¿Í Ç:ù9ƒ˜ä›–¶V—sâÑù­ïÅ>ö­6JYJxiý*! Ì!lRÐœ
+OóVœ‘G$Ðy…´B­á¢"i†8ø“7ÁBnŽA‘ÀçÐ|#ˆ‰ä2슊٭ó '®ê§Z:t™ÒÃá3„߈…JëÑYûƵLÃ3ë=ØçtnÃgpLùãu”×OÂZI)"íÌ+p†ù«kï›G±ÆµR™ Òö¾‘.»¬ÓW
+¡>–SÐr^÷w«Th¥bla
+;Cø‰aaÇh„¬,ÓN<4SÒ`Ÿƒ9ó®ò
+`d¦AsžéÉÞ ¡wèIT#tr>Ý:š—dPé.¬ÆXCq|ì[A9µ[þð1°3{„D
+sc¾vÎcÕ!¥a‘›AØgØVÐ;cQ™]XÓ×-Æ&¢ta¦sˆŒ­ Àè–Z÷wŒMW“CC
+mÆÎò;Ü4 ƒ.ŒMZOcÇPúõÇLPJ>[ŒM JîKlîÆ&[U}mPs1[åÔRË-¥ØLÆþ²âŸŒ­Ev¸°-Ý[W‡2èí¶8èØA„È
+Dc36ÏeÆI™¶ûaösö3æäk®‹ìK(k4ùZ– ”³ÊþÂ×J$Ú2Hl¾Vc ©4)m—M.V+A°`ǦkÍfP.ùïö)…t²w~pŸóN×D$Ù-5M¾Ñ5AòR2\©o¥{§kBd˜¾TMºVéît 7º¶‚ºÖH°éOÇéºsÃ¥Ž°“®Uîdž£Â&]kXŠW¥R߈~еä¢v†˜ær6]˜Ä)¶lÓ5!Q
+ŽËÇ]«æ0Ø›’3¹ ;às´ášX©•Ø§ÜàZ5§‡iü]½À5A%«Sø[1áZý抮դ¡€XÒþ¡6¸æáøÚÚuåhõ6g š@‹ ×_
+ä‚kŠË.‘Í[êî%È@ùŸ:‘aÄJÛ/[mä‡ÂTÚ’v0‚FwÆRÐz/ .a¡°=f¡²_CË2€Sëôï¬×t0A^[JyoÚcß 2
+ëÅÀ% —“ë¦K²QÑñ¿ý'B¿;¸3Ä°pVÐááΠÃÄéÊR[è’Ec™8^ŽŠŠ-hv1qÁ¥E/º6±DuÅ0õE8láDÔß»ÌAºa‰jCpxJ±ÏÓÏ·Z {Á¿Ù¸$¤çç$+÷~±q•ˆ>gʳðÝÅÁ E­³Ùz¸8…*¦: g.N Ac¿j´‚~­ àÑžúv<§˜Â±œ²‹–‹ÓÈô¨Rñ÷xqqÒÌ+ä‡{»¿»8Bä+î&¹½Ýßí×P¹¢‰¥»âUÅÍ0aµ)Z^¸IF·Óũ䆽¸¸8ÂSôÖ‘§tqq©ýúTæIO—d¶\Ђo9˜.N=
+Œ(ñy‘áâx9³ˆ64ï[0]œÖ Â%&ÂÔÀoÈä¿ŸëÖ½š¬ q9®MÊÁÌ1YÒ*ø} âš•¦÷ðÞx{QS«ærÕU.e*ªGÖ+7¯Ú;«É€Në,¦™
+-e¿ŠÀ
+4iuÙÄ6-Tºgäf“ñ-á=¿-qïd’§~æ´:¿UZWϳ•iàØÊ
+H‰Œ—Ïn¤¹ ÄŸ`ÞÁç¶!R¤DƒÉq®y‚Aròóþùñ?#v«³ »]­?dU±dÖe˜†½ýÙûxØÐ6E»/ó·ß?¤[{L×Ö-L—@ÄÔÆpQsCzk:Fhs™"±,šÎÕ/ˆ?Öä+sž®­úz¬!!Se.Ý aæÍ9£çV@ÂLÕæ³Ö‘9¤y[]{¤Ùz_=!öðán±ºÇÔ{«'NåxªÑùä×m˜åW“k»þ0YÓšû‹·¿6HF£Š‹ë÷ ê|‰Cªõèoï ŠGÌ,õZ¶ÖtAFp«6s± LGŸsC¸B_Á¯^~0ãzË%Þ~þ8hGÄX-ÚjÓ¹ ùpv’&ÔE¿ Ò‡5t[²1‘‡ltÌÛ| áê1¸§® ãŒ6D§®ÍÓeÄÛ o”"µÙëö+wòaÒê8½³„.f²›¯eéÇÒ8@ªùÝU[Ø`ƒWë˜u˜7,ƽ•_
+Ã6Ä¥·e7ï«Á3[­ÉÏþ7.ž½ý=¡ÑÄlÚ§û³÷ˆs®Ö7ל.Á!ç`=  b3”¸zI½Š@Ÿ±6Ä}AÖm7„PÔ€6²zßGOñMºÄ1·;
+ú_Ne¸—¡ÞaüƒbLGtjíâ*N4#Ü7GF:…zMí/ Pƒ?¶¬àüØé+È0þѯ;eùC±N;çÜú ÛMÃåc‰ØÁŠ†áÜÔÃk( mCVÞe
+»_!©ô 1UÛ˜§­¾BÙd-‚ “¯Uj>€Èh¾"]N.&&cf~çÍÒ¨6^>Q¥„ Ôœ·ˆÈ!dÔ$±&ª„yÅg¨¡dh_)9‡ó1kÆ!W^k>2`f—¤¶"/I÷‰WëdØ¢ù(JoHÆñŽËî€N' « èu8pyØPNt¼¹Å`·Ûîñç1*ŧvyàbê»ÊOWÎô*ƒ€+8{•A”úI³Ö ”‚DršÏ;¦L¯Ø43V¶¨AA²ãk݃“eG—+õº®þ:±P¨3<`Ÿßtñ´Ûw0ŸezfügL
+þfÿöÏüûGZÎãz
+©s)ÃýŽ/Ô„,ÁX÷ø ;B†ÝGA ’J%¿þ˜$kÆ Â‡ë?<CžÜó‚d¥r…ú·Al6Ñ;Aä± x
+òÊŠ½˜ôÅ7UÎ üLþ éÛ&=EãÈ@\™jžuV¤Ÿ$Ÿ…Kú¨ðAO•À¤<fgñŒ@È»ÀûmõÂ/8 ”™7§sbàœûTËrw‚éçƒõÈSIbèl¯ÃXš–v7¦”¾ ÇavÉGZúa.Žîc¦ë9
+] Io#àõu]Ü*íkÆíBHEÑ
+œDè£6Âb*ßɘÙgú3ëRJ‘é×$ShétÔ˜¤Œ‹½Î@ûè’ X¿ªÅÅó™÷ò)tÕV_@PQ|‡µ¨#s™¹è%AµÈP¼lÒoæh`ÄqÂZ¥CÀ–1ˆ1ò!@:NíØmnƒ’‡XcàßR'ôóÚT¸»ìªpð²0™LgOAÅrö§/ _/@ÄlìP(F ƒ†—¤Zš¦Ky3s<ç=÷­„ Þ°½•9¢ ˜+zšÔµ È 2F0eí&rd,mè•ŸVZîâ0(e¯“±âÊsQ/hVd\Á7„Ô¨¬‚ÉùRÀ…fl½1ž×é×ìà·|dܧañˆdŽõ2ÝLý™o®s+\wô+ê‡f÷ Õ.F ½›ªé…x7eW«Æ$
+C²-Ò;/ùÐnñ0dE}‡7}«JpEt‰?»k`ÎرsU
+µa^‚ÛÛöÃ^ˆ0&Ž^âì°ya‰J×}¸E ¸‘¨ óŒ›!0M²Bð:˜);ѽ¤2:Ä3ÅÉz ´Ù—çyý¬¯_¿#ÂÛ¤Ò,&@± }<Ê_ )¸ˆß“GÍ[Ñ/Žf—`…Ù Êh!$¢)lH­ÙK@5Ð0ÂA$H¥™ãáX¥ˆ‰éJ‰|9´I†
+a¸M”$±Eõ˜4Â+w¥±Š6±A&FÆ>µ0{â*€¨aó­ˆvÌî#…¿ªÎ¦`·M5%¤fã=rh–fQ£“·Ûkd²ÉöÜzä8,'ž’¤¶|Ò) ™Ñb BÕájÒ\|œzFZ‰R‡YcJwŒcõ™Í Íâ]0õg#iìdôÓˆ¼E¿y‡De‰3y`P©Ò¼¤7l]õXûP²7) ŸòÚé½æm¸tbÉÌX³d iÀZà;·)Ðëñ¯ì_ŠIçC¸ˆöãM£QY6"ÉßDêÊ<MV½Q? »ò%Ûõb–øKðKJЕïë0 YòŠ0â!÷ 
+àpQÉÍy…o™Œiª›Û3ŸáGÆëß”ñRÒè4¤Á£/÷:N
+ªlæ)‰ûÎ>ïEø4,?Ï,íödÆ ’‚)àéÄ”sÞØ©ŒíÓÔjˆ]‘ýx:,Ú¥±
+±¦âr÷Sî‚M6µ¬¶#©€Ÿ¡Ãcd‘ÔájÁa*³‚’ˆ¸ê/n§PH¥w8km—£‰-¤eÏU2 nŠLúðÈžãðþÚe A÷8®«&99rdÄ‹msÁýÚ:¦yªrmù¥tÌ¢â#‘)Û½¢°òH*91ðYƒ¥„‡i
+îÌWIV:tŸ²Ò[põ;óZ¤Ž'6æ³a=GÄ@±ã;1$QDÒ$¤`<$7I}dRµ‰^s|8öC´ÔW!üç¹ ÐT^ÃJžh+ÇÃC*!-4–Ð8riõx 5 qf~hË‚ß»ònx©ÞQ]ô²8Æ£Qà*F O&=‚ÒÒ‘–GE†ï˜ ôr¹•Ò‰ FŒQãKÑ×)âøL÷²Xi°.›Çîv]¾†³QÜЄȘIŽÒªrcô£Èý°µÞë‰ ·HF˜åXH”’‰’|ø2}CÔHò —õÞÊ·Š
+¥ÀŒå’ÿñ"ýÂ+¦ ¥·¢]‚¡è¨Ë:~Þ»
+hoBñ§q(Ö¨ø8~zÜŸ¿É1.¦ÆNr¢ä’¦æë(¸ð°ùõD¨H¶EK¼íô‡ëèV¬¢…ý—P5Çò£¨ºk#d2´†ã÷CKíØlxŠ{+úò"Ü”Õ|)¢[Ž2™Â«“ªi 3Ëþz ê ùãårp60N¢pÜqŒºHZ0ªì˜y:g“HP‡¯ÓÈL &'ŽÌÐÙµâ-ŸÄN"Y)†IŽºÅ:È]ZbáVcJ~¸¼"7IÆ©xŽ±îì“{—Þtp*xjf<ûÊìô•?ðß,Ì'Ç|Ç,°¸ê:´ûsÔ È/䓪ÒK‹"¤]2ÌÌ”á¦H*«zy/¡¥\D¹è"[y( Ioš _ͼrH€Ó•tÌâ¼)Î.I®:5ßD©ngü Lð}ª­ÄÕÌáyŽV»¢ê®
+oÛÃÍø”P
+å‘`¾vS)I‚åÄèžY‡ù„?—ñ¡fÂ`¯­~-- æÏj9É3qwŒiŒß¤QÒÐvƯðuÀûÄØȨ3ÂMÄöŠÙ˜µÌ¨ôH¦è7"^ì5èôŸÇ""†¡G !¯©ƒUA Îêv+¹³Ó[Ñ÷EL" géæ!§GMA4«(³å)mÈmö|÷â’*^.ažJþdéi_-PÚ’äÞƒWçÿœ‡ ˜÷xѶ™…C`¸[’‰­4åF
+ÂÒ§’F–È{‘â-§ÃA«¹7ýµæëC ;
+h³˜Ë+è| ˆ|IXK¤tjŠ´¤DÜFlAEبG ×^»O5¥w(˜í–Ë¥ä¸ ˆ"'ˆ©Œû:hC²þ[4üÄKŽþÀIÓKÒ$Çz°×71bÁ$ùNð*FpAũƉåZD»¦Ô¶K4« sµ3uÐ_cãaQG§SÓ„1p´ZFÎQó(’ìº(A—"t˘0:<Zl¦ãöa}ÙIàZG º—ÐZ:Ékõ#Œ
+ÃÃ<ñ¨ç<Ï¢ŸÕõë7øûªŠ êŽqtiuUòc²qéÀù (áñÑ4Y è‡ÐÞ•ûú¶*”ÐüŽµä†ÀŒ–Ĺì¾RçC}Œÿ8£ÞàŒ¦À;|„l£$.ÕZž,êŽRa‘q;*ÂÇ<þÎHÝß1a+8q
+íRôýZ? r,¹CW_^DCè3Æå7G®CºÕö‰TbÐÞöæ%°A*y°1¼„ÀF~Kàw\ðú?ߊF!@Êv‡t±¥¨,{óuJÛ­SòNÝÿÑóŸœ\‚ÈàŸFÚ¸ø™€G]$ànžÔrK_¦/Wœ
+ÂU2ÿ’&%“[A€–wZN™Ôð¨¬d x]ìî.!Úáê' ûtiq-›íž%uÓ‚ÃùÀê·?îE ) a—“gÑ‹9‹Ž2]¼qztªº_ÑÍq°”âÉ…Ëb0Þ0mˆ7Âè@·1p&f™¤z¥èkçŸ$ž A|=…)Û§e¦Ìª ÃfeÚ ÿ³ÛÆXþ+-ÈY]©Ç\Pca3²ø àê×1VÜž"_‡í°Nà°žá"6pg|d­x)<Q%)…gìSmAï¹ßþ¸ÖÐ9 )~¯îY¿±ÆïÔðaF  T<ŠˆNY¹f*^oUÁò
+Q˜à4k¢6 TÛ‰ÁBH|ii‘FnÀ¬¸öz¸Hˆ¢ÀK°8ëd<Wš¯T[³-{Ïn>ÿÌnz®Ïöá€íÙItNR´GK=}&Y9¥­ ÁCC”|–OxäŠ~Ñ- Ü­‡Ø=û¶Ö¹€à
+³Ìïà}Hš ÁÐ5QSK͆({õÊo62O×lÞHùÓÁí3Ày1~XWF>Ñ®³’!ºùtäÓyžTT××Ó%”b‘8£Ž X8tïè§!ŸLŽÃ›ÂpÑå3uü ÄHI––'"×ç/Ú‚Hy¹û&ùEi°;û(ü<[RVæ==÷±0qð
+·¶ó±xñ'l30]a™ås… µT¯-«Cg¨n•Ç[ÏbÏ.Ž{/?ï‚¢‘PÅ4‹—Ôqtëc¯"“c nÆi?Wé®@,‰% ènÖ3´œí|C¬õŒžû+h¶ÛW°vEÁw§Ý†®NÕ⽟#ŠGŒY;Ë6Ð –SNg¨h¥ÇF­VÁ7…_Èn[<€ÊðclâZ"”"[ÒLªH³zT!Èü2— fnÁËe}š m«ÃÒ4œr¼­ÓNž.†—'%¾±³7£
+¶.}9‡‡y¶Á’¼ÿTÎ¥¯ág HÂÕ/«ÍR²©úZò
+D—vØýÓN ÄNð©æm7”
+¡ç®Ú< )(Š‰¡½ñIdà0:ƒ¹ÓÁó;Š& gë4_NˆÞ@‚©~Nå}ÎÓ*ìÖœ‹DÕ YÄpçi™9”HŠ0¶oƒ,¾0@07œU™‚¹:…d+¥à&h.„‰ ~  ç]D‰7s
+œ ¿MP„Š›‚))•`†¹EÞz<o¨pɸÖ«ÈlöÍl_Äw¦,ÚEî9šd5vy’ŒseÛë”7@*WågËK²ß§HÉL4C,©2›õUÓEóî5.eÂãžÈ7cý{6MJM¦Ló=šÆ¸”óe›onÌzÊÖLˆ"PìòÒÛ|£SX[ŠötÀi™{RÌjQ
+˜ C†Ã{Bflý‚Àñ°Ÿ2ëÈZ'$‘Ù"
+òC±ÆtX 
+s¿ÝÑŸZ¹^þéC+Õ…”µäÈ·%„vã­ÔŒ•  ¯‹÷v?Åmž$¶r'Z€«¢¡LõÁ¹2Ða]¸RöÌÎRŽmÅà>CÅbh´…‹Ÿ3wÅêeÕF)P«â˜´Öý
+ý«…õ,ž$¨òäÀ¯¨tškH…r‘$r‰m1†œïîpe˜ŒKJì¸xÇÊ…¥KUD§ ¤'ÍTÓ‘òöP]×NE37!PZÑt–m'R?ÿÍöw”:Nlö²Uýì$—'$)I¶Û/uË”“?Ö
+ÃOÒ=@†ýQb‰p*F¡~ô4@ó4µ8)›õ;æýFp³/øjHœÑŸÛÎói`%¨æâ†:;Ç_Ï×S?¼Å%~¨]¾¥³Öo1Fmg0`ÈØÚõ;IæU‰ ­Ë@!*P“}X¤;^™0>ì£þ{ ÿð¨^ñ;ÈtÜ<$>Ž 0´5×lÛI£R „’.2Ž5ãVPH½yH~ŒvQ{¶ËöœÍº331ûf 7(®{ç ÉßÈc.‡pó'Èð€ÔRt„Öm™{÷‚\8LS¼O‰I¡óMLwyÉ1­ªvrÒ ]ô° ;ñ_5ýIl©z¹ ½±§‡T&…`bKÎ"ÇÕ4S’^/SÄ»˜d¦½]<$ÃŽHPÕžúK[ÞMd :Y|¶Mä±L¤:Mä²f1"}ÄlÆΨÉËIÊ­Í÷›‰<fú4‘|™g–XT›aŒÎŸL…Ä â‘žÙßa/ /ÖK¶‰Þ
+Á”{n7 ¨¢të}5M$ýƒØUèÇ6‘‚dœ L yBshÆw0§<@§dk™~vŸfÛ@R©f¬ør3Z¼æ|¢|áb #ểîi?V¨ˆ°åf a²ë{»|‡²3ê½Ë€™þQ+T[b°kéâ5Z¶åÙ8=Ó?ª3üMšþñœ>Ã?j(BCƒ–óâEReÈ1önH±;*Ófø.)g 5”&R3 ÒoÄ8Á¿Ò[FaŽjÀñ+0Ñ£uío‚eœö5Æ#“W5ž.ÍB ØžÝwMôý1 ¡¤8éÀ3à ˜Ã’!ŸBpèô YŒ"årÊ.mï…#® ‘pÓ³‘Hs8 ? àÇb/ËqBð†üPà ¦0!)áŠù†›ÉêGñ– Ã6i΢õ=@
+S¦‰
+’ŸŒ¡XµÎA°íôa³êÇ“¸ ƒïWi@ñRóùÃ: P&&'rIó,<Yætv'/HRð¨^¹o¾È§¬]©Í@¬WãMHœEÉÁüŠƒ0(Dʲà«ÛÒÿ¬ÈÛ› 80ø.CR×» Ð1~¼)uÁ¢ßÆØÀû`ö ÐcGía?’Žö=TôÁĺ·pY÷c-²2%ãLËÿèY&ã°/!Æ®˜Çª‡÷FùÖ^š7WˆlJLjPï˪â·“vÉ[«¤:L‡ùÏŽ…Ñî
+O%0V?ÏbiŠÅQ¸@²f¬:
+·Ní£ž ÇV—¡”¢/„(ôË£”›GbØõ;ŠÏâ£ipÖû,1…‡ê
+‘LŠÙªdõÖpë+ÇÐœw9†ÏzÓ1ľ„Ö]& ì™8VáÉo_­Ô¯×æ Giõ¦²t‹‡Aí9ôdn±2Èç8íóù ÊøøKЀ€àÇ*z1Eç=–þ61b³@,£šožnâöWÈ‹21=0ŒS&É­Jx®Ö^ú„ vE×´&„XJi}-T`¥<\"q‰±)S ¥¼0·.eY©
+úd€™¾Z';[@ã¼X'58 y·ËgHœÞiƒxÛ+*ÊpÉd¤>!¡ðbýP[JOì)a^g<A‘vôþe¼\’äºq(ºïA+Pð ’cõлð´zÿÓ>—
+ÅG÷XÙ*òóåEpÇË=™N |ÉÔxÕÜ;!H¶“3ƒÝþ¨ˆ‡?úw;æ1…ýƒÑ‘Ħ\§É±R’*xAÐð õ;ÊŒÚHG,»a‹MOljpÏóZÂ\°Ñ!¶r…ó©ßâXˆOñ(Êl @TØ•è%Q)¥1ò¢Ê^ÈèJãr~­Lñ‡ÄÂã%L¸òØ’ˆS^R'VüéÞiº1¸ô$Ž×ÅÙxMzíH(:ÐV~ù7sj±(Q‘=@üìóª#wþUº•ÄÜÙDi¥|? â 3ó¢#-‚‰‘´t»”yRʼ7á„ðNm¨ÇT Ìu¬º^ÆJüíŸÖÜ4³‘SQ“úÿ¦›GÛ!]&ë_¹4˜åe‡zÁírTO#",ùZPNb§y4×8"HÌDo³x”ÄÐMÅ̹ßÏre nmÅ»¸ßT~d‘ñ" ­)…£âU¬3Ī­ó)ƃ›ÖyjÜD‹ÑnÔÖû&„ UŸv-i ¶ÍÔ@æ<
+ÌŠkƒª#e)áØ*0˜!/~¬„* 6Ò(3¬8Jè)› oÃI %;v@ß72±„ÜÖr8!
+}Äp¡ò
+ÞTð‡± VÊɒ”3x‘~ÈX´Ö¼@*JÍõù`¤,,â5uñ0 –¿h$´[É]E(Âmèz®âÉׇĪ4IfÏAØ'>ô¹'Äû2‹“ó#=³Öˆ ¨íâíZ{¡¸adRâT"ÊJnWÁïãåS„êVú ,Œ
+Á{”–°¥”1ë¥$Æ€èÉðØÇýæ
+£Ø݈å&†´B›.%]ÛÎ>¤úÀ̓Íþ^òu/á;¬€TÛ¦ã<˪p‘¶Í;¡qÎS5áˆÙœ1UÀ (ƒ2WCÁó‹xŠ±ú¢–‡JÐG<Yíô
+KŠûòüýí…€š9—÷4¤GƸ«Œ~ôµ´çù°;×؃»Gn…q—gŽ|KÓ'Zdy.0/öéÚ—”52M$ÅäàξˢUŠŸø»x5TëZµJ
+é[œËb‘yìÙ"h6fÄ·WŽs¹®cZ¹n+ª€m¹›xP|`Ôô!;fÞŠºò^Så%ŽšK)±$B±FÆÇxôgEÉd¸æ%(gcš[ÜÊj•%¬jA‰û¼ýÛX¿þÄ}w2åh4ƒ8m†ŸW,qieb'r“¾ýe¼@þãð’¬rþ°f/‘2àÇàÒ¼$%-È<¹Ê§
+Ùð7)
+².“‰»YêæJÀ‡6\.ÙúIgò~T
+€¨(åňì1¹í|;€jH-L‘cŽ`Õ\ ˜2Rü” ž$PF¤dÞEœ\¢DA@˜sJeMÂäóAô̒š4$œ(3æGô"ï+g²+Ê‚0ÚÉœI[ÎÚ¬E M€ÁÜ0,Ê0.z¡ôŸâ¿1a쿆”jõIÛ™jñs;ò ³8jkК•WìïJvØî<³ô×—ž5Ozqã*-£Avœ`+ˆœØ H5Y×[Q‚O¬”‡À6Ϩ€2~'i ®#¾0B¹¹4?Ç$£]„ZwÝA‚"¾IÜ R˜á~žC»@H&¸Z݆¾´fÜ è°’29™QÂh¹­ÒóˆQ5õkM\›¤süU QSÛ¨
+ˈÀŸ$€‡Ï¢> ¥Óľ8gMI ±@¬q]tËâK­Î÷#¤ v.̨ülÞÖoR:§¨œ%€ëDc¬mÐìIÒOp“$ p ¸…¦§DðATå `5ÀK°&7šÖ¾åA‡O£«Z7Î) Jé~éŽN…„#ËKÓ:²_§¨‹·CºÂâf“ ¥p\›æ¡?neMËÏÜTu%Ƨ@ìgaÊåC ;%ÿkì›g ¼¡íh1úˆú9²2"li–/{«+ &R•#4ôªa¤Âuš'q™Ð
+ÐW¾óQ¸)‰-gÇêv?G0­ó{åÚ¾©Š¬ÎŠöŠ–¾%@båHG²à7;­ƒôå°M¦.k'C>c&·7D® ü¡ä—¸úC©öÛþ’±òÇD,žÓìfÿõ"ÎçíU¼š% i!Áì™gÅeè&nÑ•µifßG9Iˆqñ.pÑ/bp¶³ÀvÓº€¬ ? þIÎ U'PðSìËQ¢Œˆ(¢Œ!¥|º•Cs4',eqC­CýRô÷µ¡!Íñ%L°F" ˆ!’­Igqg®Ü4k~¶ñA ¬áʼ=‚-·‚Ì| /IJëtÙò-(þã“À‹–T¡ˆ]Œ&o¯;·R¢î5:ËÿÖï‡.|¤ƒ-ª(™¶_„,ð¡D„qÛ»jî©etl¡¯gC¡ëBhµ
+jÆ›;aâ6Ù †ºÏ©U»Æ‚ôXæ
+& QÖ ¤.ãésk7¿”k˜›ã‘7FG»StW»(“¡ú»Ør¾ýHgüŽM—0³T0¡|UØ&JݘEyËI·ÁÙ$?©Î^¯"¿Á¡pªaµL
+-²ç¶ ¨z$²W˜oÑì:Ú‚dŸÌV˶oì=ΊÕ[¾•îaª%•‹>¿ÿ×?§"‚ ÅŸÞÐë¶2eOzÃìóPôníá!Í•©:­> 5 4”Í‹xN¼û·D­çS°œ!G§EðQHÊšÖŸÄdqÉ|Pž~UDzwɪŒó*EóZ¾Çem×Ö ‘éXÀƒoQç(£í›nE(
+n #ÉÚ90Õ
+I³orŤMÅ6s¬I=ªëvõ·BQ‚¡ûó†Ol|p™ÃÛÇ á!âé|àÿù“%CÿPÞ:¤¿ó°‹¶‘ÄÊîtÉÒ5iPÝa†áBl!1ϨP@ ÊTä·]Â!ðCkÞE¾ûƒÒc|.Ð˱e[;‚¼Á{[wpÌätyãƒAÄÍÀ¦½5ïP²ñƒ•ƒ;Kó|Nä*æN°ŠûU¹S¦¬Bf¹ÉmÙày7¤žÑK>À1WJtZpßdèjÔeØ9k>(¢è
+ùNü*€q£SPñkÓv ;[Â<èûh²³ÉѬ0
+Q
+ó‡’3=½ý~(BÐä‚ ƒC HEœY£S3ö¾*‘a¨Üa3½¾<wóÔ:‰Ô¦¿Š-€20lœµq ücUVðÜ4Å·S²Ÿ7Œù4¶ºXÏÑ
+2Åæ,<¥Súò10½„¿S£ŸÎÁð’¦ð =@tÌW¶ü8·€ÿîÙ#@¹\,›ž®éʜ̪ö>Ä„$€á,ZhOó!l'ø
+¨|ŠëˆlóÄ]é%Áã^óþPÃEŒ‡šŽâŠ¡cCR~?TŽ¬{ L)6Kvg2hÀu±0eïT‡°kÅ© ï† ÌÓY"C¬ é2O°7c“æwoo òc¯Ü©Lè´ÃºåÒ3Û”‚Ï:KæÁár@6i˜H‰–e<'²¤ÅÅ1¦SŦ؄Í$w>†~³×#ã«òþÜ,šŒJ!n=°Y|‰Õ×سŠ"dRܾIÄ?¥¢è²y¸ìuH½Ö­È„vžaÁ…á}ð}C¼“¼2ð™[NH€G×詯ÿ¡á#GUH W»,ÜéÚî­cà…ùò²Þ¼dhqNøìˆ×`Mük^K>kêûO„÷ò¥8>v³H.øª|Øð+RVÀs.tÂt°®¸#ˆzdG²€þédZ |¥íÃa«˜H_!QEU¼
+4e»jºt+ÂA„ »Á*d›—¡yÍÈ톂W1‰™í²‚‘#Âõ;B"•üª
+î:ƒ5-ÂÜ‹h¦(ÊjC±=9‰{×ñÔ(†ÖÖÁCQ_3.—äŸÍr!£ætˆ1”8Œ°„€N<efQšë
+{z÷ÞŽ 5ËZ è]B®O
+̱©™¹V¥ŠŒþØMümŠ‚æE´{ሮËj¨$*M­·•L]*JéXN$¶7–†ˆ¶ë¦×"v† ]â\ýìÇm»õ†6Yµó EšÁ¢î@ñÅzûUø–!«;¹Ò AÈœÃÎAg¡‹)a?ãï;ÿºü#CEö©Šs<ä<Á6êÓn¸¯A¼Ð¯Ã î©Ê£ÿ1^îHrÜ@=ï°¶Œ ü?¦‚.]€!oeêþz‰*Lh§ÑäC†vsÑ@UVfÖ^<a¸’FC(6±r=¯dÛTÞFYæ¹9Ï@T)¶Gp%–Ô‹´ŽÙë&ŒbÏ Få˜4T
+úÄ\q#ï…åËÚÍ” ©cS‹Œ‚bšž‚ùm»»™ )ÒCãhĹ€xCnzi"»@l{ WB zõI(ª"®Ë¾X#gÆšAq ÷j½ÂO%2Yaßç
+ú¿·~¾cÀ¾a†µõAÜsÝŽØG±ÔlE>Ýçc-Zƒ‡X6'ãš³›h£}0<"í ¼=l‡äΡRU›‹ ÏÅ¡Ø[ª_œà‰²SÒ¶A'~HLÛø ;ƈ •E‡š¾‹L¼nX°$ˆSÆÛ µDt%ïQáý² èÅ®ê8C‚>³øJ¡ß+Ç1yÍ ŒìTTêÛÑ‘¯ ™Ìì Fê@ošpvÌè…¬LPl²sªv&œ*ÕnŠAòå½Ì¿î©bÚ‘ÔþüûÛÿòÝ ²H06X¦-ñA"â°Y…ò™¸œ9™Š€zK W­ŠDŒT‹P¤U+NbÏëòâdÅ*¸
+Y;ãØHm±¦"Ù@a>·èÇ (#«²ì©ðÓAʇ‰Ï9 ‘i*A\ƒÚBÁ>d‚dzÈZ¸,-šÕ8Išdª‡@áj†}Æå±úx¢~– ' :pÛ«_÷\Ê6=X'³&'.=¤Ø𨄶é‘`ú(ÌÖÚ\^AÞ3è8´<ç6/Êw˜­AZÄ×Õ!ú§ø%'/Mk›(Ò%óOQ•ÎtÊý¼ªöµF®”‡Vé`!¼è·äj X|¸‚ÁÒˆ„ú¶:]¹Èù( ö³5XN3ùØEüé!LÑRvðLú §/ž©F$…]ÚUÎ^wíýÉ3ƒÌ 6D½ëì™P~FD˜.~Î ÈÀ_ÂkIÚrÐ8·Ü³7Lm7lÜŸR8$b0å ÔKòH¢­(ÿ ç<é8kY¡aÅ!ØéXgY*‹ @ÌlÉæý*\?ð
+¢}”ÝA«£Ë¿ŸVúj¢ËB_%+ÓÜ¡þñWc)O}ðÝqxú»Ê;JPÂa²Ö¾q’F¶0ç¸Û)g/2¤V('#´ @íØä]„¾‚ˆK÷¬Ñçý›ao€ú
+ºDhíh$-ìpŽÐÚOÉКܖ·s¿Fh@¥å$ÙR:¤K3.Ñw-žè2nû)eÿ4jLÚÕl é£¥{\è˜#àñJzê]„$‰U—£c„RÐ+v9vÑs„>ñý¡¯ SÓeõm@U~u¡¯  /&2d>èò¹Ðç;ƒúÃæ9àp¡j6Ü 4=‘y6Ï’ÍÂ0h„ÓŽ- ‡ñe»UXB4‰uøI¥Ã?%Š
+3 èvï‚Ø!^åÙ€¦oç‰÷Œ( ñÉt)Þ}ˆI ²À€kó+¯
+ÕÈ-@n„Ç¢ô\YÓy{k¶¨ÓÙ ã]ÿ“<J:µ[&Ň’M'ƒÅÞòê Œ)D¹_w ÖF3’|7Ç'ˆm…k'3çÓA<%1‰$÷î8º@²¿½wŽk’7 ?å\%b™cÙ@lÚóùgÚIöRgq.ˆÒDŽϸ¨}ñî|©°úaÒ±VDuÇœÄNÒfÐS²A0ö@KÐkW}x¦íëî–„bó%®A'/¤´=‘cIQÍÎA‰2„´C¢Zþ‚Ñ+,hõ&tbÊ9‡÷ywÎT‡é©´ómÚ.h£¡ñîU¨¾ö ¤çâ€ÐæÄuÂN¯%>@¾´jé·”2Ù­|/CXbŠ‘?»{Ó—eÏËw©M×^K¨³Ås…G¡ÆaH(úM§¾@üS—Ž_Ϲðæz› ÿN¯ºðøµ:‡ix-ñï¦Êrbʤ@›‚2 ëï ç¢1ÈÈ´á;€FeÛçÊ‚:Bâe;#‡Öa¡ˆ²;?¨ž\ ÕàŸPnÏú3ôïÊ„oÞOhiÃÎ!ùSç¢ph"ïU’!`*3åa¾[áU —UºZÂS£s¶lÇgX< ]-®n‘Vâ`Ó%JMyeã2+¶¡{<Ç*Õ ,H¬Q«Ä¸?¹xu‰,9¾YŠ•MòýSA…à:­8„¹ÔÂÊÓÆ‚ ùÛb9Ñ·6b•Õ¨Y˜Ä$T<6ú}XÅ4"P¼ ;‡†þbr±Dœ­ƒ•R?Aˆ´„>\‚ÖŸ_zÅ(‡bí<|íº1µ”O’@<Út¦•žÛ»#»'—\=<+þO*ˆ„<[qƒþ¨ÚÔèÛDéÒVl²sµO<7™—õRUI¢Ly7k
+ f7S*Xp^wÄô¹›럢•.‘L3  $믢4 êÐÀ>k»r8®=ýé,-Zþ8¿;‡Ùƒ´Âäïhüãe’Ç Ñø:æa-/y nÉûoý™(‰](‰~–M5³1$"cˆX #Æö¡dÊpÑ`P—ïÕ‘KÄŠRR<}<AA4ù5Á$«$‹‘üÍ[Tla®kñáTó2º‚y°üê2+Œ8wŽ!Ôb7‹¦ßÖI$Ú3²˜c"ålÊ_åø /%Ž\ÒŒ û›ÓyÝp†)îÈÄœÞ 4û˜7èK„úVÌsºE-yoÅÄ!ÄkŸ‡©
+òÓãŒG³9Äâ©ŠNÉ£­âS ^ ÷«Øæ5¯E :¨ìÙÖ©ihH0
+ÓÇ\<:`³Úö­‚bFkùn7K2ù¼‰æ|Ò‚ÄH¾Š(‡!½:u­ÇÌÃr¢Þi$ñ!uN$."mH,:N=ßHÉ­1s8"攸Ti“U \$A–Fµa€a‘ÎÚ=o2ˆ¥ñUÄ‚aÜsÇW!bº:¶|$h JWm’I†¡O.pR*w*¾Äßé¥HÖ9á©“ÑgQ€%µåËÕR0Èš½!"¡þÁ;oJùånÞ
+é™Û
+F›¨•^sùUWñ/m_^ØÄßI2ÅQ¨9”8 €ƒB|3Ïë¬a–O‚§²Ÿz‹*ä8íkt%;aûž.Hìæ~(ãU”A(uËÊ ¥žd ‡1Ëœ¨ŽéÆd6üj_§þP‚ÈGvKŸ$GîÒ$4°j>¼†øä7«;œ‘‘^ôk¢r>»\NÏ}n;ϤÏ-/©únenë±{=¸­ÿj2(€§ .¿yM¹ÈoàЮHÁ¬Þ¼hSŠ,YßžóK‰#'CîH;•§uVµ†©òuäÛ—*; ³ªÉäHÐ_ªeÅS®ß÷N s<¿8û`qN¥mùj3Q:xxæ‰ù{(ùbW—«ýñÆ àhˆ%äáÓŠDü"lòÖQ(‰Rû€¸WØÌ~2;Œ©—p•À-¨Ù^¨ke%¦ÉCÿÎÌôRÒÖWDeŠT†úH Ü0èYéµnkÆluÞ$-yP ÆŽ†ûœµÑQyɾŒØ‚sÀ¸Nøïµ]é—¢·‡¢Œ^ÅåàAø‡Iw sÜv2DM-ˆ ” îËŒ W|zÁFâ¬Y­„W`eŒs?yÅwÛJ£¢#AñÞçe Z›õ•UëÏ.|„ÎlE¿XLæøHðÑ=æõHwëÑÒk›î~Ït«„
+G–…ã­Àt£ iT R\Åd`_ýê¡ * ñ°]د&¡,†-µ£‚iÒA(s#å%¹à÷É%ôó$—†x.ËCmEEÚq©Ú;{Ér¤²±—rË.s‰ ildˆÔè*ïU÷¤GI%–¹d[µ¥í™ÜÖ\¶ð±›T_@H±JPLçâ©È˜ûvPô²
+Ê{äßI¼Vo›w˜£©°“ë>  jÌÆ6-¯,ø76ýM˜¹íò¥°x«$~>ÁÁ
+zÜ€«y·E¨¶Í»XçŽÚ£‡Ó–QÊ/Í!`Ãá|•‘ˆ!„Ä<%k«†ÝW†`xÒÃ:m…HĆֱA ³­”áHåÞS fàÌFñ·"£„ N…5Ÿd6#ó`Q”ŽE í„â%ƒ¾N†2`ùî~õ—ï2$‹Ž*ħu >J ÖåVDèQNòøœÄSúÆT~zÜ;½À‚›ã3è<”1~œ°…Uæ$Ä#\d®%ÄS²Z¢ùÚéÏ@~ûe6@/Â
+A–ܶ5FŠ³è÷ùP¤õðýÌkXG‡ji×X…í¤M,Y¶¢~"Ú2Ý×ù.¶˜*²¢£+.$ F+Üy³§Û2ÇgÅ­¶•¬±õ ¸B|aÁÏË´¥™”[GÙò/*kܾwÇ|¦§ØŠzÙ<DdûFH¼0Ïüƒîê6baž±……†I\'²ôZkÿv7-È'qÞ‹oĬJ "ny•$‘cÇ鬄Å°Á‹ôÙnÆ_ª
+ðµV‚ß à•#¥¶{(K™ä­‰Z±o2áæ|Ù¼ÁÏ"`ÓAoÂÚX´^}|ØÀrÑ%Ž m•?-æžÖÁ,Ö™IMs—àIfx e~?†dä¼IW”@Œ ‚R4bc22ìX3ÄV†2Ž eŒN‚±IV©éÆ’Åf%=Ò<²Y±h¨ Jƒ«0FY Å% –hEÀœNb {NÎÌ$c±´ÓŠšÄ”á6œDÎx:ä%c™aìo®8ì U®–%þœ ”–­Ç÷uØ|phœ`l¾¿ãÁù´qÔ½¯^§aåIkñ)£×/«Ïy¦È€å“‹NÇ߉Œ‰,®ˆ"ìÀ„õÝ®ʈQ?e%uh¶Òú>¶”Þ×ÁÔðƈPïû4KÝȃ¥QWGaf8%8=£lf´w-ªä]
+ß €}EU<q÷7‡ðqµØÔ’›­CHlb=ñ*AV0PM>jx‰R#À\Z .¤Æ"»·¢Ó›ÒCŒ›žƒý½èeþ>¾3¤oW 9Hs©½S"N™÷ØÓO+Ñ– dÙî5«‚éÂÉeíÄHL &^ÆJdhð˜$/‰< ®+‹ð׋„ÉgXp˜ä‰¨L”šW»)yù´d†.Š’œòR8JH3Í Àx¨DŽ ^ЀþùÏ©(#zp~„cÎÕlºâ¸aì±9ñKч!h_DêÒ¡h•ä)ì¢á›¬ãÒ—9¤„ã瘎êAPMÇÇ2OÉ8š"Ø(ðÂÅ/_ÃãŒ*ÿÑ:#Ó˜;ï´¸Ä
+öùó¡„IÃõáLK0 u*-A#C_±J%zRů¸_ ¢e´´=ìVF*Ò¸¾_„0àr[ß·Âj°()$õ™ÙŸ”Ârzh,Òÿ„ãŸWŠáá!D-w‰ã}Ê>².ÊiaÏ+ÏÁó-ßë(#}ô
+/N¿Zer®¬xEÇy¢ÝÛ¶ õÕwþä‰E° –g«!¶«ÃÑ34{…L)ovƒ¨úÕ'g³Ñƒ¢±¾ól[xRmE’á@»9]¡†³”<)!bq‘‚´ï­Ÿ¡7´´nô4¸gLNôu9{^§iW†¢êõ`{Àðc]j=–à“iU$BÈÙínEú
+Q¿€]Ù@ÆçceÁqu§ʱ†¥«Î°ëbi[,œ¤)_SW+Öøï Äa\åNð4×·èȶ÷¬åmði„«Œx•dd%[?¼¤rqHå¼¾¦ÉHÑž·A
+‡;e7€ ¾0mî«Â¢¿åõó;¼²f‚ÊÕ°&îÛ]„kqÐ0`ù½ŠÔ@¿l– m¹(kD!‘ô‰ž¯+
+ˆs¤Æ˜ J„;'<ÅxªìŽ…)R%j).¢¥W1ò,
+’
+=üéEA8/1;¶læ–þEð
+ûæ=q*Þ„ó]òúó׬ºþ‡‘i¿š‹ZJœŒ¨r…& ‹ˆ·Ü];
+0Kdtaa)/cg}o«„ÆÃUHcÛŠ…¹Á+ÎC>Ö—8¤!sp¬¿ \§6YPÏW èî`®ò¬ xâYR%öƱMMª”ÌOa—Yd}È®—ç00ºÀÛlj"/
+¬³¤w’W†+€væ
+ endstream endobj 154 0 obj <</Filter[/FlateDecode]/Length 22162>>stream
+H‰Œ—Ar¹DO ;ô¦ƒ\k–¾ÅDü•}ÿí(²äQkÜ!GXng$H$ú³y‰¡møã/mòTé½…kõ¢*£?k7/ânÈ?€ì½ÄüÏþ{öVܼÖΤi©­QK›+M{‘F°‰èjR[)%¼ž‘Ô9H£Õº"ñ«hiµXÌc´ªy4·D(/h\MZmj7^øRÄôw¤ï ö,E Ò¸¢ùãÇÇÿ>Êãgï^"šhÈ:O
+yÓÞ¸áã× ˆçkÙµË〸{µQz«+GÖÜÕya1¯^J ‘*f«"­ŒÎßv^]2i}tTŸƒ¯e¢«ÆÊ’uî=Fíâ=z¶BDo1Šm G¨N=ª×QÃÊØŸÓy8᫇Œ nFñùJïÑ•2w´^UˆÚ•ªZ{|~ì@åY-K+AQ„‚
+Nåš’d&®xê{¡kaèòÓs‡£¨šEæÒËPó
+Ÿ0oÔì(+ ’§¨n›KfŠ6™#ï>ÏádDèï•ÆJHÎP©+MA\ŲBëÝÀ»¨Üc¥¤÷¦ñÆÝeò}|‰ýþQê QJ/6î’Ãox_ºÔ}†7o•:C½‚ßϺ”’ôØßƲ¿jŽÍæÛW¡W(L¤Uê_Ùûž›”4É.g$–xlŒ!:ŒàTºm™¾#Vœ—joNy¡Ìî&/Ô»¾çÊ_cú~ Û©}0¡ð&t¢–óÍôùÕsšÒ¥•hÿZ…iô+^l@?¶ X¼F{Áü|ãFK+°”øÉ[žËØËI¿&&÷’ßj[® w7ÈLã01NÆ?Ò“þó$6’湩<^ñ^hœo×À[uˆ¡¨šî G]Ó¢áÃ;ªncNºy†T_RÎîHû
+½'„bQÕ|ÊÒ÷ØÁNËf¨F×@ª1–ÝqU,ýÇnÑ”gcba½Ñ»G‰nbûc:dô¼Žs{,‚’°å—&„¬Ð¾Íý(«)H Wä1ý.  ù éÄ[JÕ» Mb°­Zº¤6‰qA~o©Lq&¨óHé©p}B:þë‡
+ÂJZ Ê1!–å2LÄ3‡-6”Y.JŠ¬Wt%Êaã°mdWcy½—ªBæÓ« ÷Ä ! WMÛ³?§ñd$åg:0v³ ì1¹7Mˆ±‡AR:b…2²_ÂãÊoâ2R“be‡„ïÜ>ÏQ†òÒåíQrÓÓÚòŸÊ>9H©§•ÌxÅìjΞEƒêxì·ADËÏØ&½M{Qu¼*«OíS X»¨Ÿ ¿cŸd–Pz‘RK¹íâ\ÇJj“ø¸):µáú(KÛ‚0\ƒ‚ÎÑɦ‰&%7ÊIäì.œƒ§«ýüØvé©IoÖ†¬½¿ Ú‰üä{Š¼÷'B‡¨21ÝnT>A<BÀíFå1qùbË·*ïŽ*ù¨FÊNíH/ƒÀwnÑ™_X€–ÂzZë×s‚-¨°Òƒ^·ò ]avw|îOW(v‘ •­íÏéÈQê¥ígZ½<ŒÊNÂ*buŠ±¥l÷½ÎãA¢Ô\2¼Ú^çñ 6ãã«¿.:íX–;`j;븥â´Óù èªó@)4»¨Ô­Î“D¦©£fä£Ýè¼;))ìC¿ú»ÎÁ^F¯¸‰Ø‰øQVTbQô~G êÎâëX2³½))­k©¾Õy¸þ¡ã„\t>Yˆ_ëy§"[OHá¶9à,¶:¤Ó‘ÉóU¡¿‚6UÏd«dÈ……½ Ú(ýôªô(BædBÙS š.-A·#‡ÎìF“O7ÕêùúëÿŒ—9r$GEO€;@¦
+&Ð*¬Œ»6©Kptï#—Õ† J9ÐN ~R<‰[r½AP“qÿ1ð] ûÃÄpÐçÛ®èDp·m‘Ô—[·Ã„Å÷Ý•µÖ´=gPÛÚöéÊ¥“›EºeuùÔBŒêl2ÿä¡uÎiW¹ t'F
+öfð2Ë©Ð!iþ ­Çc„šCè'’›× 8ÀIÙ°‚q¯cÿÖ‹fœHv`6}})ë\ü6ÐöAõ%#*sÀ<±‹˜i·øó)ò‹N(ÏGïP&@_>·«Àþb±Ø¼Aˆ›¶š›"Öü\ýcYƒƒ7csašp~S’þò.{—VO>Xµ+B¦ãN€bˆ\9eÓþI,°Ÿó8†Þý¬›Ÿ‡¼6ˆ3LÿÜäÕo/bôñŽŒÿºÛ›MúŽsD^¶æ¶5nÐèlú—rN<@ wcéÐ|fn[“³°lÕxrN+°’ >6h%HR:–'•Ÿ™L@wCƹ†FL’K|ØÂ_V#µ
+Ì&؆jŠ›kÑ®?åbÄGÐ6^.ÚmXV,ák#"a›¢Û+«£ ûíÏ·ðþû_o¿ýýßÿxÇ@â¸;ÞFì4RWÎê(µ²¦öíZÃœåøq©ŠýT;ÎPZ”ƒ²„¡+*2›^‚BpÝôõ˾ÄÁ)‡Aœ‹+ Ê2þ×Î!¶ ;­Ý)‰«ÈTÈ›{  :„¯oJüS3aP
+­öý9EJuD Ò=±êºrC[ Ä—-dìðF àÔ]þù¶+‚ ã?àš‚Ü| ^Z¾‚¿ëteÖ‚’
+…`P
+êcÙsÎ4CõÀ2çý|Ù§8
+Cëâ|½
+‹ò#ø~}©aà ÝòÄ\ÒA0íø‡Å ëÍãÊÈà¥/säºr$Š®@{ ]†ó`v¨Mº½E{t{ÿ}îCâ“ÿ#ŸÄ¨ŠÄK< ‡;Èy¨SoZWðiûSH
+÷b”j_çd,¾¢e¶µ©økX½…’íU 6"ç¶ ]DŒzA4s
+BAŽÂ`,HD…)á×FrgþJQ"¢F–ÊF`ÄB±Ù–Èg<Þo@3ÄSqNcù¨E÷çEŽÝÆ4Š´“R­i·†:MÊÖë›Ç–iÙ ¸ Æò˜äÁt+!õ¶£ Ë2”R†Mé ÉáÓá.¢Ã½V@™@ê«Íu ö%F Ï;ÊÝ9Iç¥Ú‰©x8¸¥ºÊÇìI¥éèòšäZYëùhùæÃÃðn
+æ°cpl’!Xb3¸:zŠ¢Æùr?@À9±ªÓú¤h:é¹'y‚æз-ÛPÉ`­1•u?÷Ïurh~°PxÇfË®Ž‰8)ð<g0þŠSka·QêœL჻¬
+´¹Å=qüjB…O;}pc±h¤ð†Ð¾)ušàí¤®”R-(Øn»"ÛŠn›ãqs–öu“U‹lV3Rm‰ºOÄU^žPÇ/í!|‚ü¶!ÄŽ¨a÷ÏÉ¢k¸¹A‡Öv2W+BKoþ¼U¶_îNxzPp¸8Ý·ô
+yû·>ˆÀ`âx±ée™µ ÏJ¢ ÀZ@¤ó$†)Ëè ÂÐ" DfÇNî}A°£ ÓDð’ùBĤ¥rr½_ÞxK±¸Š0…>”:dfôó4¯ÅûnÑ´ 4ZÓ‚\‰¨r㔈}‡o(.¨®þ9„^¸“*=²Û@–\˜‡µ¾ Ôtb¢ÛXÓ®*p º‹Æ_öÓ¡²³“p'H±˜óúXå|=VS}ÔJá¥Ê2¯ÇBh£ =o;fÕØÑì¾üb/© wÎ×&p“°9¤ÊPÜ>â,¨féFÇ<`pöø‘Èp½yC•/‰Öw#©äÒ1Å©æÇ—þ<Á°‡=†Dg
+½˜v^
+d7ÜÃ~€x~&6ÂY-­{%ˆšqB­•ÉÀ?c›Ç\RB¹Ä¯˜ýJlœÀÒ—º¯~qVêgE…&Ôë¡”ð)Tƒ˜0µþnPh0ÛÔƒR¹AášAx’!½7
+„F¾^ÃlÍx^È0µLëÍÀŸ.N È’ýcX{ù6è4×çµÊæ ’;ÉïÛ@ȤDZŸÒc3T›Ç>(°ÿä8FÄV똌´)^§ô©HšÛ‹È&9*e¨¦Lÿ&ƒ
+²ð²”vêCðW)Û„ñDt<†l¬U¬ÄÂï ãö¬A9[‘@¼yã^ÖN|fÝ @‚Z< ëãKÞ­÷OÍI䟦N"trØ0Ʋ¶ñ$[bP”1ÐÁ%ãïEF¦2 Ô&’: Rj£¤IÓh‹ÚóÍÎÁÖÍ#ó:(+Yn¦Z-¬-?èë<CÆÛ¸ó˜˜GÇaØ¡qKµ ’¿Œ¤ Ú½V p3÷fLÐû iŽÉëÖ¸*]vF ±EœÚç¦
+ÄkA$õºðHŸÒ Ziò’;Ž>ñØïõ¥Â.5ؾ&+OF7Ñÿ‰]ÇdªÀ$p™ä÷ê/-ÿõÉÑ5ï±wg9l:H9jJU¤–a!ëD¯“c·€üл¶Ïä‡ÊpT¶Ú²~-b§§ß„Ÿ|%°ÏÔm{ŸÀ"F)F¨7ýL—üúáX¡ÊFö^ýqzE®1üÿ¶¶ò(‘‚»¼1Å1aCà-F¹‚•öb¼Œ4ò£æW]÷ÂI µšË¯ž²òÚzO¸`úà·ÇÐ+nÌÌç¾Õ +Ãåxÿ%ËT‚èÃ+(«Íaœ5ˆÒ³ ,v3rÌëvZÓ9ò:‡ÈÀòA¥Û"Aù dP¿ŠAêµ*Ëígh…‹^*ÓÌ÷½@Wâø» $7CΔ¦ÂAÅl)üˆéX­ÂA@Å\3™OÌ>ë‚`ƒPœÑ0z‘ô÷Þ½
+¼ ñšu¦Ìü(Cï(¼’—­'ÓÉ4¢ääÉÕNñŽüvÉÄÆk„T¡ÚmÜ# DêŽfX)LKN2’êå¡Gj` Œý%ˆÚ‘ž•½å‘ÒÁ²¼u#eU œ;š
+i°,;€”Â0‘dSÁÀäüpD½jŒ¹Àê&N˜|ɧǫïe,hû¾ P1‹LnöðoLú™k¿v
+C@ø¥ÉÐSûÔ,Ó˜²¸œþ òí¤`d­gÿ6ˆMU² ê1Ac˜†¡
+ÉX#÷¦Õ9òöžÒîBÇþBîQWGŒLó%eae-mõ–ò¡¾9„ >Ñx<b¨~â(vùºÐŸrdF aÅ}{ÓåXX@«Öc¬ 0½éì¾é$KŨJÙø¼'™Æ[âÛghWÔ”²÷¼¡ Ü `+¤0i®íÚ4#Iü˜aíHÈ{ÏÙ4Bì‚‚¬¥çlöÔ'µ…?‰ ö=„¼aÓ ßÇ $%d‰ÌÙÐM‡TËd?ì+ŽMÒÙÉ¿
+ô‚ÎÖ›{+õ,B’ÀªÎ× ÓmœK Ñ ‡)‡3šlƒŸÃÁ\ݶeübA^V"7
+Fr,‡¿ŠVá¾X½øv á.*Ýú~LO™¸'¨#ådžTÇ–µ¯¤kc(E[{uíT‡„@™ºËAph£iÌ™Œ|
+mæ!°”Êr? Aãø4 þé¬ü-£â-;¬¦:4œ
+„)‰ê†70†qƒgϲ)iƒ¥ø•ÞäE €/‹ŸÏÉA’h?–¬”²‚wp+„rñcƒo¼6AÀ œÔ˜lå ¤{«×2W,–(;?pÅ„ø÷ZU¾ Ò“·ôv
+H´ºÛ9„e²±@ÑF‹[Š…GÒ l#Á-~d”L*ªyZ} 4¤"ƒc?鳑ٵ—t3.%´ýº`À˜•oaõ ú~ýú7$¼¼TÁÕ2·IÒÊ’óžGŠ¨0õ“« ‚¬°.¨ÍÜA3D*‰ÑcùÇ){"*Ô궕
+f”Á„ß}T ‚›_)ìJ3pz–”[çtu•ªÙ3ÖHhð¾ìÇp-H ×E§,ZñâE —õбҞ1_+&Ó*BL% Ô2V$Ç\À@šÔ bµ¼äÍ$5® c‘#]ÀÙ3î÷aÖ‹sÑäÆry¶Â‚âc
+÷–Ó¾<¨ú±uÀT—¨
+A/ãÑ㤪ü*:èb=l­C–-òMÂP Y¡ÎF02²g‰e/G¨È³€¸Ú@m>ëÀ
+ç„&ác¶t5ôå1…&ì—¥‚ )FúPÊÑ(ýäsBp%HJ*äÈr¤7SîØ…€Yö…aé¶<¦ð›½®~Ì­'Æû{æü\ÛH {
+u—ø§ñ<DÈn¤¦£)ߧ/.„Æøчxúz °QfÈã{uÖíj8ˆ¦M™vùRj^áÑÚ³3
+Í^žhèÔÍSvu|zæõÄ ÀÀÂ_¼ ˆ@MeÏ*°_|‰Îx¤¥A‘ʵu 3…Ö‹±P
+Ýš#A†çd’ÃEvo‰“¼º¹{5ŠKˆ˜²#¨fBÃ5¤½ pZfaÈÂOaDäÕà¾Ì®„õÍað#aþ­®¾É¡*æŽ
+ˆÈÌ4:¼Ú¾ c†‹1nŠbêA\(˜®sl~Xp÷wˆBjxb~‰ ?]%FB_9Ý‚¨&Ähô:-Ðj“ÙÑw¼¡Ÿ0jÙCÈÀ˜r¤³¦{âøDhH^Échɘâk\‹ù“PŸ~ªŠ¡pvTyõéäËdŽ˜i”|Ëý|¥€°
+
+%Ü}ç O^‚Žªgáwèæ¿ ÆX1ÛË1L£¬[H»ß m49!ßáÎ’°ì9azÈìûžÐ…¯U%õÒH}­méxW—Ð0L½E/!?Tª‹Ù¬9a"iÃâçÀD¢I\ÿç%Ȉ€ZñKlå©oøC.m… ÁDѯõh0ÓÐÀ´Ý×*‚8Œ —lØ_ë* tWQ WI"…´NÁh„¬’æRµe•€ÅR <öPÔoÇyŒm ⥧òvõÌLu€Úϯ!‘Û`JÓ[N›
+Q^B~´è{ç’¤'Xoº†«À;­£MœËk örú} B]ǤÔ+c+èqÝ-èë߼鷞þñ_Bóò¦ =q
+ 7LfPœõç%ˆKrLú=ñ]!ÈNðù0Ôݳ¾BÊ`”!LVÓqò¦ìó¿ÖM¨\ d²ƒwK*S§ !Īø§H6;0ÃŒLFoÎEl?Ò _ÒÛ8„Ì)ÑÜDT(Æp_õ<hhˆ:Jܤ5C äzŠµ¸2ôÅÉZºÅ² ¶ §¦#òÇXMã2Tí‰Û>¬
+,¾MO‘ !Ã8u;!ˆN©Ã¾[*$ÃU"˜ÿÇx™$Ç™ë@øï}çaÝ[ßÂ[ùþÛ÷%”­*–[‹îYÉHäÐx—W¡•yûÊCÞâ-\Ûò¦d: »e%
+îJ³wUL%œÖ¢Ÿ¹Á
+‡ZÇ=‡ _¯€ÐÁ¤q5¯ØpÛ˜ØÇiCèPa?Éf˜ë{-zq%··ÙåGEÆ¥HÜ TJÂuØ(rÛ‚6.nV 7ŠWZ:+ÁmC†˜¶U]1pùXyleYŒv‹z¸Ü:Ë
+m®K1u ‰“ Ö}mm@`M^wƒ~™Öz)q`è%€›÷s¤æýˆ~ yÇßl½Ô C¾õ@÷³—°{ñV\5X™å`žÌ] =å[ª3éÿA´~õÈ/XAªO ¤ÇÈXAy=€1$&UnÝ_ƒ²wA¹4œà7*ÞêÃÙò6¸`îñšsÍ– ÏïXðïlj!¶‚p„41ª¾#~üáøY¸jŽD*Àä ÷/k3}®-×Ø-ht³Zɘ£b´@x»”ü´«¤JL]Âýîœ,r-]
+ŽCJÁ.ƒ?“€­&ú  Úý* ;i5ßšs_Š.’¢*ÖÆR|£‚Ç£º4{EÑ–œ°bLí/EÏÿ’>­GÊ Ø«<ƒŸ£ƒ”z‹Gø|gÕc
+Ø$Z›ê;r.Š´4žåØoÏáòC˜Ûì3%D€%•ÄºüsÃ%,”Ñõ?¿Äß.²,èÿ)—´c‰¬3‚Ù‚GfQi(°¾Öœ
+²±gÜê[ÿ‚jL•Tfœ8§,¿lÊCS§1"E]ÄQMhŒS€BˆËÎ; ¡ÛF 8l~(Y÷– ¦’sîÀâÉßN%•ÿw+BËõ¼Š±­œüʪNÕç×})ú´‚xÔU×¥è”LLC$â—mV‘'8e%}B Lwq‚¹ÀàK[Ö‡b–1•é_àÔ›pt·sΓ;I®8qÒàÄðu¿)@Ä|Mj«ž‹ÐØyàOÀ«椃iõ˜VáC ú7+AILǼØ>¿ªSÓ•%W ôpþbúHÉEBèl2ÈWþ[c†*öE´F¹±Cª§##øU9v¢s
+”³î~?gÉæñ<EЯaå[-3;ãòSótÏ9 'XËce@<n¯Ñœ­4ľXl,àu8zFÌ9ªNÃr.÷6cdÚI0/+ˆÊ“`|–íÝSÑç›"àº`Si}@}eå»ÑÌ@ð^H?Üa8/a/ªâB1§A—²•?ã»S©MI‘/ñ Ø1¥ü%3ujÇÌèœ\ŠCKÙ•[YñÀ¾œ÷ï‘YЋ·
+òwøzø™“’àœääÏ71I6¿„+újœI;28oÖ*
+Oõãáû»cd*ÉQží¾RÎ
+Ë"v³àíÓÒ°Vh8hDs1¦­‰%ç#˜c(`Š"jV”’*˜@2û01Z•Ì…ÛK4n¾Ç)»lØ™ÇaeÌy¿á‡ØBÒÒ‘óZ„8¶ó½Û 
+µ7)+«3‡
+Öe%j|]ê 5%pWu«ùS[?¿¡¿Çœ¶Ò>d[NþÒvEšDÇ~½)úâdN‰,
+³Á¹éRWâ%.m[ K/ÒÖ§X —H½ä¬]<qZ\üº©¬0Pc̓µs’„`iZ¸3x€…ÙF¨
+Ï@^á+ù[
+²ëÒ Õo¥zk®ª*
+äå
+2t3¾\;I$°º×¦ß$”¾šÌò?§›¶D)cºn,Η-/
+,5áËý1µá•–\à¨qO§9<Ñ"m%1Œ‘2›3LÀ?!§«ŸÝKë$P™¯%ÅœöZhÙŒ›^jžöJ(Í&äl's.ìÈXÓK ÈZ¥›ÛK2¹¥P{L
+7ƒÃ²gÁpfÓ<›O
+•h‚‘ëÇ+ç`
+·ôeȦaˆè€ƒ}Òêѧ%M³Š1ì÷`Œfe9,§60q Kn LÀ×ëõ{Ø ˆ‹I-‡öò+ú
+«Pø8Æ ž·lA%ÚÁúô
+¥^)þ™?zÌ—®÷ï–k
+­†¸³‘û¥—- Àmvå§+b§R™Šœ;Æ~¥']pà²>?' CCasŹëÓ_J¼Ë¬te³b»=ã«<jÞ\ ®Í\]¦™Þ0cpîÃ`º BñÌð öë­¬`63è‘ç’øg¦eù÷—¾ÇñûÓcD1¿‡
+F¡[™Ûù°Ð-u_Æš8Õì*9C(jsÌcX«ñò°¥ÿ\4’’J*êrÆԃÖRÄfÂ6a¾Îç
+?ÉGè_ª‰‡fÀà3¯s
+Ä…´zƒúçcÅŽ"< /ä|WÃŒLÉbE>0»d'lT.OS¬Ç,JƒàÊ¡Ä?E˜èa®0twNXžŸ¥ÎÛˆ`qSœp‰ œÂļXS…þçôÜ ,Žc¶=ˆR$—5çíÎb€¥ÀqÛ`,A[Œé¦– Ê Cg~;Ž§¸ØæCÍǹ¦GÌ@Ô†:3›ù)Þ`Œ¥E=çðñ$Ô™lkEž™JiðÝ~7ê·Étù•!€Ä
+k¦NÜ„1ÏàÐb}°I<½Mï0L‡Fgt{àgé4ºö$MN’ó)ySø ¾3´þA‚C?zwáFŒ{m1J6ÕÖe#K:Ÿ#Ú
+dóˆ †‰4ÉEsÜ®òR„Z–o¤¹s-B˨¸ ¢?j}:Jb]yŒ[`! åP ©Í›½ 3Ì&ü5U×¢ÏÚúñ¶œ°
+,Ü«ƒ w‰ÕƒVÃX“IÏÒb«¤Éõs8à=£§¡är>úü¼CO“÷âOA{>§¿Iú§(6Öc‚T ã!Äiqnäµè¸OY¤7I­óÇE‡y-º$H•¸¦‰0ë1CR’Ųb¡ÝdHŠRÂ%1°ÃêK†¼ã’ý‚| <Ä6µÒo2døŸñ2I®cÇ¡èV¼‚ì›±k¨]ÔTµÿiK€i+“OV„ㇿ 1Iàâ6Ë°%IhïÎÁT(Ú\ø%CR¡ðÇðFÙ‡<2$EPÎJ¡°C¤°Œ„ÂŒ˜›y ‘¸?2ä£æ4òò*Êx¼\Ç‹D|‘æ°îJ:§¯Ýj>°¤¾Êô¤é¸c••h
+6DÙ.Ó5¥:ö‹WŠV‰"‰t4†¾2)s“Ë™q6Ùg‚¦kNFª@*«Z|‹"%'!ÙPH $‚šadÝ{Δӡ·»°IAÅT©¼äõèD£Cj°Q„â³æÓj÷ÃG"€þ)¼øtͬ
+N³JÙ8ÏÎr½8=üV÷¡WÞÈÒ’È\”ô¦Ê“øëîÈ(t«Jð²·àÌY¶sD8Ìò5MòÝPîøt?'Â:¬~–õðIuihGoR)V‚)‡±lÙ_Uжµ ž
+á;qÓþHÀ·AœTï{õü-‘~/¥¼&¤æ“ðÿOEýÕˆ*êiíìEOwpøÜÓüûNvõöªR·L—Ž†w] AÅ#òuŽ §¢ÓÕŸ;]ý_wZWÿõ•æW‘U P­.B?p–Ín/VƒXK|ÿ®Y4³JXÍ­òˆ1­D+ RbßHˆ Ÿ…ÌjèòóAšE¾ŽL–mòC07~­"‚ÐðFŽo5wù*ôÕK: UÂúú
+±Ó ÃâÞ¡ˆ â­'Žw9­ÛY¿ˆÙÊΟ·šOïàTW¡ý4E«$Õ4pý(µh&D96”ÁJH“µlÊQô7†–ÅÚ³|5«‹^øö§
+ÄÇÇñD´t¾)ùâ(üK"„‹íwza%ÊLÿ•Ó¶Š(¦ó[ –Q9.iH‡
+¤¬¥¾ßt?&Ù—äÊ樎ë›\´X\ÃûÙâZÌFêiâȘܪÐ&ÊC¤dÇðWa?É>rØŽ@BX®òTBˆá‰Š8Û¸¿ô("J§Y‚mçà(µš©õÍ“£k•Ò½¤òN¬ð~+%FRã¸ÖÄ›¹dÊ—‚Lœ¬pÞ;Ž"†ð[³’Ì/avpáÅaLËQZè,žJÞñÒ—¢cQ’Fâ嬧ñÒ¡¦K©y=Š¤ Ý¡5ÉݦËLtg[Ú‡×açC´ €Fµä ÆÈȅȽŒ“çðól D©pFxØ™à‹g[*ƒÃNZ³žÏÞoaÄB˜å~íÝ9 \Ì9m'*¢*XóØ£“-,9³8þ©Ò Uøàœ®…áÉE¬ÆÁ*
+ÀåLbWÛ¦£ò >Û­R8ˆjd0’E1È«OJ€Duø _’•#‡}˜3M||žK»œ+nEx¡Â‚>ç…œGÚHú¬7ÖÇažÉœ׈°™^vàܼbDÝÚ@ê]ú¿Õ³èoqýü·þš§KPª³v{-a’K´P2=¶¥Nа^­yòI–ý´¢ûãA²ð´Š vü`-«Ó”hæ` \zµþŠáœZsÝÏÅ¥c®z-hÞ
+Š‡J
+ÁM²±oJ˜F 1Œ-ÿùÒ·þ¸²¥¨
+v9În>3‰ ànÿÌ?b][—ÖÚó£Ú¨˜Ê¯æ³ ˆ/íû¬ àîë7£c¥ªS‰þtE^ƒÇ£Hór‰¦•þ|StÇ"Wlƈ}çR¾%²×¥úÓ;œ;/Ýòôwfò͸ayŠQøàuï Œ„ªÌz•ÇàOâ¤@I¾H ÷æÚ ê†¡nqÒÆ’
+H•ÐÒÌW°æÍ!&/;boŠ›…²Wõr%ÌŠ”.ǽ*ø‚áÊ5"‚AÃ*VBCšøɲTˆê³m˜¢‡-9uø§Eˆn–½ŸÙaq(ªXëX*9dN_†FnŽÀ±¢cC/‡ †JXÓ0pRÈglH4h'võoÛfºÌŸDƒü”É$‹ÒR÷s€&>6)“ø0¹§Ö2 Q0jÂìÊ»S`Åõœœ½$ýŸð2IŽ#†à—¸ƒø“þvV…f8aŸ,Ô$±T%~TÉï©Í Y¦·„©#£U.ïhEE榨8'¼’5c?Ñ @ÁEd ‘T
+´EÔT?Šê4?—áQÐ;h †¼kßÿ4ôYû.$l0ÍpV.0¶±^ÆRêGj/A« I%&Çf A]LÛÉ)9Í'¤åw ÀÓ2 þÜ1‚‹EžJº¿ &~Éèž N:††±=šm.¼ã!à掣À 2­ni'bà“”Æ¦µ—EÉI뼉©ƒ´«Ókx,Ç $¶ø­ÀÈ6(¥M
+yåÉÏ‚¾…ÄQ`ÇàQ¬%õÛw(ü9÷€X^êæPúÌÐCÚ9_EÛoÞ4¤ÈqTQƒ"±5ïÃTQÁÜ!(l|SŠ·âUË ¾D%Fý‡6i£C¯‰xOÁ2  õse†^dµXŒ¹dT°9SGe*CKhbd5¥e>˜õ’Iœ‘¿Â¡â;üÿ4í&ñGt0Kü&”Iåì€iw¹WÛ!’j
+µ—`!çAz¬±ß-É×$e”jGû!ú’Žî)’´ÐˆP§yB¸U`UéÉIƒ‘ÉÂ=wž´{CƒørbYkêGÏw4g:¼† œ½Eþû=@›…2L+½^ô7` "Ò»ö¨–$
+LãÓþ 0
+H‰Œ—Án¤¹ „Ÿ`Þ¡Ï ¤!‰EƒÝã\ó‹äÏ1ïŸ/Üþ³‹·«%‘,‹¿‰Î§ôf«©«Å£ËŠgøÒ9´ Ÿ 6,\Vkmñöl±æ¦Òí@ºIô¦_<þü±1£ÏÞ§ ƒáž05éÓô3–Êêm´±¢Ïà±hþ€,ï¢][Çï?þÅeËž«·ñPç2U^£Eü: ÙZ—æk = %-ãl1ûxlH÷åMɇUìn¡+†ƒË-¾´æZÝC†Ü@2x@Ê ÒLã
+þó9@ù¬‘z:uˆÕ\fBHÇ̈B'¡nÈlÝTIÇu Yf1mºW-†ú$Œ)Vǘ·¥áj'‡î)”A¢KÆðÞÜúAß®â+(Y¾õ'Ï¥¦m•?~žÀe4UÔD¾ Ò§ÌIÐkÀ1y¼}
+WB ˆwR8›x̼ˆ6­-á¬qD0$ ƒú äÏ˱"<f@âûs Nö‡¹®¢{:‚Ršû¬6ij æ(žR•ÑIbRò÷w »ä¬'Oµ,ÞÐùmä k”xÎ)뀸jM…r½ß€Þ¾Ó9?Oƒõ§:´[nDuÓ‡¿¾
+4ØKJY-Ýä(‹‚‰ÎTåJ‚)‰šDaÒ£p9JúÞ–ÇêgßÚö”²‰Ž4ÿÊÿÒ+ÙPBûhC˜µx”Ù.7Ž:¦ˆ2¯W©>”G
+Í?ûÑ@ˆªå©eßݵæ±/ ›ZÙb²-VSDµs
+«Ö çD,È$Q
+áF¯‹à/cD^Î䤮jž,)“÷ç,+‘Àxäýk€00qA&_Eµh|L.R&f÷¹ `/@ü×}†o *u]õ
+ÊÍÆÓ‰0÷¯™3Zv ʺŠ6cçô v]ô’ Ȧæ’ƨ|Üe˜¹zÚ×¼-ÔGDÕé¥Þ7§¼æî)¨÷9œúND–Ê•E÷M€v2íב„o4Ó_=7b°~âüf)ýU•ô4÷Þj=¹Yo?ƒ˜ë}dàŒ9Îþy{Ý èí;oúÉÓûÇÜór4²M¦Êœ@ý3°Õ¨}{üûÔÊ=‹Óæ/Ž›<L ŒC²ž!ROf›lrö+èçÈ&æ»á×F¤#OÐçë^AoßyS†ØÿçßþCœÝÈÈØ“PÝýϜ՞1æ:ÂßD*ÂZË%•,©9,JNÝ\c»íb~åz€½1]§T… ám¢ë» iOr¤i^¥ºAŸ®û†Š…TVºs[nìŠìsÌžob^ðö,eîûã„®S:,ok0ã‰×Á{¯^•ÐÅ×iröš«@’Ž™Vz_jQ3æÛú¿‚6.óûÔÖË a`7¶+¬ã0ø¥º$#é!G¤À˜bϱn8ó¥
+t²å뜒?£–X—뢤Ýࣤã@¯†bôY!)Â{¹J@lSÌ(ÚÎF¬gvp6Î9ØÃsèñh@Ø'fK«u;\J(!é«ÌÐ)¬sïJ×UÂ*ŠOa«sPœAÝø¤‚¢ýq‘ ‹«
+lM9A›µ½£ÝAXH„æ„Yï7½‚–¦ÙB\c»Á„0iÒS"Ý‚³¡3“`ZŽgXG°õ@(µ§Œ¾í=¨VÇA’å$"ÚEyáWîz•?ì"ŒÑÌQA°è9ò¡`ŽåKV‘µ~©«²‘é—çâiÛdDð†ƒ™'7LG”ù-¹P‘w+t~ÏÎgPìœÓùõLoS¼Á`°Fß›
+-‡·EQ†a‚L– ‚CÙXê˜Ünqäÿz02¸$n«†IãÊDèó‚°€åêõ-R°¹°¥1[|]7e9Yè¦EYOè•&i¬î…Bðg]ÈÞCúöŒ°æȧ¾_ô?çÌ…?Š=šs(=Õ‹ÚáÌ©Dãh“±¸]Ò²n@²×† ¿ö´MŽ¤îºŸ>)sP2Ý®«˜]´9î²UŽr¥„†féÁÌmõ„<î…Äâ©(’°
+Bßep&v8Äܘäl- ˜üñ¹©.¢9Xlòê"|.Kæ¹›®:GÚÎ$˜4F•ÑÏ(铑㢠d‰æ$- ‚ë*‚™};ñâ BÎi#=J9·|aÃÙuþËx™$‡q+AôºƒN 
+óZ[Ã+ùþÛÿ¨j‰$:¾ÃÞˆLr2`L{ƒPg«/Vc+êóçªÏ ˆI i†~•Î”H4ß £ &oÓùŽÎø æÒä£ OÚžú±Ä,MÕËÛÜ[̘ª\Þp¢åãhÈ<Js›3;Í„,=÷øц22ØÎô´
+ÊÓî§ô€Ô)\"ÿÕ3~Iš‡Èa„ñì°szµé‡ˆ†:öj¤ÔÅp<¼ý‘”q‘Y¡*¶“)V¼Ë5¤ìZaÓ²_ÉTí ØvD ×|[R@TŠÙ/R±ci. šI‰8Kï÷ ˆo1™ajkNy.,MeðDJlJé((VÎ]Šg’öd‹G{È}%ÀqãžRÿBøiXvS ¡G4³]G0fÊæ7‘K`ñ^ŽÇ‚m€ÿ­“Ñâ&æ“Ïi%;fޠ䥻£!J‚üÂ!2ï(¾a\"  Òȩ̈cwÆd¿%çÔÀýLw;¶qÀnÈ%l6K÷ò ¹¿Á1æ
+¶ÜÕ‘œ}a1œ1h…]ý­¡0% [±Á^B©1ÿN,¿ó-Þ1/t—ÓDÅ0Òu¿IÛ*×Þ£åò-òcÔõé'ãI˜®î7™ÚèøÒêƔт&eƒÂæŽJ†(Õ®†œ]ÄöCëºÕgt>Ï~gÉlÏÒ-´LKÏ2[ÓoÒ ŒQ¸yÜ-Ñ×sÄ0è·¼—<²)vPñ~HŒDÅš7Š¹Ëx$!ŒÈ»#Z<œ%"7An°ë÷ Aܸzß|˜ô¯€O¤ÿóÛWÈÚbV•v”ò~ß!òVxI¨Ë5k@.iÉJÞ
+Foi^Úc>zy ³É:¶i¯™`¢PEÎ §ñƒ¿€9iÛôs*/7&qÍ{B2ä?øÈésóbÇ­&œª_Õ²(Ÿ…Åc#©æóÚ!r"üÒj
+y<ö xñ–ýÔæâÁB+îõXHÞ‘hø@°Pøïiù h a$óÉëbB}^¾€5
+«Zíé¤"$춞¯ib2v±MÿàŠøOV¬5Ï ¼ ^-^bE7[kÊ |…Ä*|˜óK†U
+~œt2»O…?
+
+{ªNù âW‘ÆØÃ¥£ïç 3„Ž¦Zœc2͵Hnœñ°ü´Ü¿F)'Nm<ÚSYÏIkÁdˆ£=E‰h.o¿ä¤qˆ^‹[béð')¿Ë#1ýG/TÖv…Ùí,"+’}B¿‚â!6ÃYšK(ùaÄç0ã8NFü6…R°
+²”þ(
+GÉ9f¶«Ý§û2ÑìžÛKEŠ¤Äã9Mf¼Â½å½ÂÒ_yÙ1'𚨧1.q²8Xæ™Úe¥ŽP2œÌ 7Þ–?‡\š”r›­‹›³¹_Ÿ¸ªÚTkuØòóÛ?ºÌ(2÷ä½èî|¶œò•šœhw…^¡ðw[c´ßak>˜– ÁáÃú¸¾0Y&þƒóEóçñöcuR B«{Ìé
+s2³/ˆ„›m7vÛߥ^ð¹'F}™ 턽#ã°ä¸4õÚÏi| ;”‘U~£z ð÷Syµ„–S2É\D·õ&ÈŽ¼W©ÁA@­K*]k„¿AZà tì_ùA¢[2€¸ ‡0yØF½÷¸ sP<ËðñÂíB1
+·G1‡/ÃÃËõöC“…YJ؆¥ $˜‚=ƒ(qAý~ŽýPƒ0$(h@kˆ}B.ª“’þEý{Šä‡Û"§Š_z\+íLrŽƒÄœ2Ttç?L$£å^a
+Ìž%%%‡P—:%”±2¸\Lp“ôÅUT¥)­Ž‰–Ã(ß"êú'Óô`@o÷µ‚Ô5ÆLÖc>1û?ÐÃjP¤RR÷sªˆNÿéǨáR¨“fçI°<‡Û B=`dùZ(óç· èºš™G­m»Ö£ŒÿÔÚ‰8s\% ¿n·ý'̧ØûûdÄîÇa®Ï'>¿ø±“¶¢”z¥¨Ðg}E³YúÅеõ´â3#!}eËcx¾@ZVê‚·Ç7Ö—µëã‰2LoXª½”~zÁ+X,ßó¾$TŒvyúA:ÝBíY±%äûŸ«¾€>qÁòR^ ”·W}
+Ù¥wööª…¡„)rÁuÌ{uÖŽJÍz×Ý×/V§f™?Êo­Ä÷AÄÜzo9 X±Ì•Ý/18t/eÅ°eaŸ9:ñ}@í¡@X©yÌÄDŠlÉY1Èbv>û9}™ò»:_uè
+I½*ž¯ë¹ê3¨K€«¼r>F?xÛØüá88ÌŽaK}yøv‹å8ð×òGm5•·F’˜}4£î½*·W)›lÈv¯ü“Q¨ÏXTøöcdÅ ¶Ñü‹s‘…ƒ+°ŽJ–Ò¯_’Dò“ãðÖÖc.ƒöÝ0±l¦ òvš¤m2`TÙ·
+e7zß+?OtÂd'¥Îf6ÞiÉ=6š¡ÆÇ÷ñÜ #K˜É쟃ðB(ÈJ0NQ^ÄŸ˜¿)ó"&˜-›öÂ2F-éÊÃÚožȦã¬Îv­²ÈRM—NP4súåe’\Ç Ñè:ó°öVçðŠºÿÖ/ª–ø?à "ì°L%F 9°ŒûÛ-ªh"xf:Œ³˜ Ä|0I€ºÏ‘»©j÷°´¢)2F£³Â/pYyCêDí0›^Ä ÓѳÞnùŠxÅãÉ겶iûê¤ß­–W–ßÁ>P¿r|
+`~ÕgåB æh-t—úÂ
+ÈÊCxÈ´Ÿ#~A¶;[dçˆø+/Ëx(>Ô™UçVF?öêCÔó\$•-ñ`'Z•w ¦Ua¦ŠGeí$»iJ5<&œ"lŠÎчpå³Nå÷F/æêáêI¯ÐR[ª&Áz°ÒÁÕŠ_lÙ8zÄWˆK£[C9ž#‰Á)ëµìƒÇàØKS3a cãbÆ1E½cÌ~“:±…΀4îi‡ëÃ8™ì÷P™äW¢¢VJ~UÆd )16yr Tì9<û€=˜ò°ÓBŠ¨A¡¨ƒ”‡Äˆ —mCä!Cg”=â2¿°\…ÓB
+³”U‘êSšp•]}ýv4ëtHáÉCƒÿ4±mÜk%Gƒ2euf»DDcÎ/âZ«ímB{7DæÃ@¸n¹§è'É*R”j
+Rsƒo†êaë yñRÂU´FÀ'ò x
+bB“…ÍqG â50ra[áñ›)¹ºI”)¦yIK†W¢¿óyR/s4íž$†ä× /ÍŸábÉ/AƒïOáBZa#Ø%ÏñôéS¼¿¨Ì4ùL£c¸¤R„Ù]\ɱèbÖË6di ÃN)¸+xŒU¨ÏUZ3¾‰ÊØÃé6³4éS6®å´W®ÇŠ·¬¦þ鶼xF¼Ó·¬—XJÜüòàMyÇxbyí«iôö°<«aÅ‘P¡®(¬o½FÆ Œl¦u&î¢rÏòÒ6l"'»
+ׄ¿Š¤‚b$€ßã'¼ Xý”m¡)V!Eb&ä°qFön…ÛŽ,VM€±±žÎ1ìCå⠡䇓J!. æƒh4±áLú‘°ç29œœpŸÕ¯¢tWV40_+GËGõÚí*Œ1æ•ækñZrÐla½ ø‚Z0eÿ|û÷›Fè,'”žox}*Ê°äï¿6•„¢Ä¥å
+ýNwi,oÔ÷Á£Ý_À¢âÓƒ¸Y Ä/Zˆ?d[*A¡,Ž¤É„$+°!rQÄS vlØýÞÏ!7Ð,c1ÃÅüÅF`Ø2nclŽzý^»‰Æ ˜YÞÜ@H!û slM+ áœjö›ØI,FÄï/}Løº)O^EH»Â]QIS1Í ¾‚8‡ùbØÕÜ`Zæ‘ÊÃJ,å$¤&Ø»%Þ$µÒ0y¸JtѾ—’ÐjÚŠ'Ž‚AaÕú¼›%J8㹟0Dþš‡ïG©|¢E«_$ÓËÏú܉/ò.ŠßTø|ø*ó ~“½®Ë.
+#6‘âmûªÏÙM¿lì­ÈA)ЦÿˆÁ˜µž v“œuƒ ÛôGéh•÷¥8öþ\Åû˜
+%e†(å8üc"4Y•låX{‰$ý&$ƒU1ܪŠž >¾/ísТ\ÝÜó~ÐmCAP±u~ˆ—bWߦBúÓ Dø‘ñê0|ßÇp‡~FÚ‹T"Ö,p¥A ö!ñ 'Ⱥ
+»·äxÞq>'*~DÜ#â“ ²~ ÕêËB Bˆka¯^Ex„J°èfúâc buŠG16ÄÍ°¤•ÒK{yúÐy~o$9cJk¦~’ªf-úMØ‚Òä0—ûap Ž
+$×›`-F‹ñšìc˜7¦’m!°^Ð0<0ƒ=ŠÁÇKtü´m¹à·(ùUï  {1w%^ŽÑz`ÿ!·Ùn?ÙÝcQUÃXèãjúÚhØ÷ O‚¯ãq^ 2‹7“xÒWâASCq×íLû
+ÅnYâ@blØE¿Šö†9·W}é¸:† šïv
+ò‚ÿXëVo¶†m¿ïyÁ¨vhj£Dår RO¦‘Ø­ &iDþe7½™|]W¨}vêO5SëÿP)”ÈH3¤‚R锉Š¾0HÓÈNL}øLw—O³ Âfó7•v‰9Фˆí¥zsG'mJEo‘Yk•–(?鉂að™€"›Bb·t"POkL(¡’Ú­¦;g*¢üFȾ2s!fÇT=ER°õ° À6Y÷YG òÄî?+›Êe¦DÒdB!¢0Lì_LÎ ÄnRºËZâoç4-Z0JqŸGU” Hö1
+•¼ ¥©7ÆI²#Xöˆù°OVlE‚+÷9Ú¡¥Ü1:r
+Un—–$i3F¬›”¾.w„˜o¿¬“Úl‡¶±­tl…qvSÅ¥  -†ÝªËu—ÊŒ²#³fc<çF4ùç¦×µ·ìü˜ª¸}£¦‘Ì÷ŠS+À´ æ)ÍLÉ•q1ÁͨÏ‘gn:“AÒE”v5¶U°L 7É>ÒÍ5ƒÒNÇ4eoA›sÀú¢ñY Œë³:ýtX¡}1#N-|©ˆxÙIÝñKu·ð44 ªìaYE<@:ï‰C:C>9x
+’³’=l !ó3 ˜šA“ˆ´Ðp\RÆ‘ ¸xYF=GÜf»AþÒœýN/Ÿ@ÿ½šN‹MŽù나2ýP¢Çy@d:o& å«%>@}ʲK»nëdìQGOyÑxƒ<­«@D
+Ü-ã'>¹!zøkúÆxÃ}fR\iõikÄàGÆÏFŒŠ7*¯ƒM¿,Óº,q ÛN‘Q »0é˜+ý5ñx{šÚªƒŽ2L¥ÒÅ5¥3eÉÝÉ Æ‘Ó$±.‚’@¢­›¼ÂhóT˜r/1­_ÂÏCû*ìiºWWmMJX’L”5á,PÃgH—B—Kö¬‚F€8nȯY$3™yŸê™'é½õêÃgQGÿxóæëÄ;ƒì•= ‘åC;¼&lÕdåëÄ‚óêHמ°š÷Hç¬ë…OYïÓæ"# MÏ#Þ×!¯®¸„GO•…™$ÓNƒŽ«YC÷Ðø©9Œ’€‹‚Ž_äËØ«¨Xøz,HqåëlBø¨B1ÕŒÑî'GÁ0›°1磀xÁe³÷Ìè²·œÒœÙÌ* £'9G‹¢Wç«é êrkF?nMòaÎb~Iäb»#-)eš¯kéU[U7(œŸá.ˆ€y*¥â¾>5É„èÕ†ïy8o÷ ’'ïc›ÀÉÃŒ)§ÐüèæÑ3ONîØ,Û[è€âÍMÙä}«Bºâ„ˆ†ë1”·ÔgϪù­Ô1ÔÃùB­‰Ù`T|ºŽ~Rt2·Fj´ë:Ü‘©ÄøÏͽæÄcXý¶Öac}p¾Ý{å¢@ßEÔ¥ÌP¼È" ˜á÷׆ЅšJlfŒÏ`³LÛÍþ¬ìÙòe8
+>I!)õS<
+‘@6Û|ÍàGŠB+À¹Üœ£ÏªgLjvÁùLqt9OÌtƒëž›)HXjáˆGøAµ8]uóÏQú\f5•f…+Å[@ÛUwŠ×^á$ƒ ?Ãì!Ç  Ò0lyoç 4š´s\ ]Ñ åoñóz²ØFÌûüý¤úß¿â×­¨rP’£ŸŒ¨èVEê)ŒÃŸ H1,¢Ì)íñ„¡
+s›4m7D ‰XQz4HàM‘dáP‚¿öVcðB˨Ø9õ÷­s¯ÃÕ¦˜‡ÃµZóƒfœDwCøB*BhL˜#NR˜©ÕwZ9Šæªn.†”×Æ­§/ßÒ¯&[ìÓñ™õÊ÷ Ñל?tÅÊ83³ã…ÏÅ@0]–;B©!s%íe°Ä*£ŸåÐ8 R BµÔ b[%0¸òí¶ÎªMSD´up ˜„%ˆë¥ò·šñ¢[Lš‚OÐÒV¾Œˆ,ŸXJ¶W,Â…ù#d
+áo±ö‰<Ko7öÙN½û(ð[íÿu: ã]º%}há|'†;ŸÄÑÕÇÓË÷¨ Ã{@X&‚Õvù+°
+j ¨Ä„‚#Ã_
+}–ì°Ôb[µTa&iIåqŽU“¥Ý&VÀ¬+
+ôڳѵ›úæ‚ £Ì|<[ì¾<Çâ4R²[5 PÑ{Ó§ú \ɆŒIo¶®›Aúêˆo¤7ó”?›fê§2'‚‘=˜R<:\ƒ3V”Z?*…æŠD—Cc’}«‡â`0F¶Á5åòuÒ-&‚D!C¦Xˆ£”a+eãPp‘é9Ý”òÄŽx¢!íe-¶³üjFÄÂ$†ÜÈ`l!C Ö¼ª1™ÊrhÝwc,Qw@Ñˈ80HaCNi_Žš!üÈ~´1˜Í3ɧDJ8 ¿
+wv:¾S&yðÐD–âïô|c/±èà™4 ƒùÐË…{雼ŠÃ Øæ3ƒAd†)}8v‚ýìƒÈIû Œ0Çgo @ˆ£j•0<!Àú:3¼Úi–ö5"ÚðYÕå£5×cñ­ø …½áiE¯IMiW{oÜ¢L1—<õ¨Zp‰rØC¡I ª5w¶ç ’¤Žm¨/JdN34 “m%g`×´1k
+XÙÉæ¸b2wæ‘|ʃIAOqo¼vz'±Â'N
+Z5ÙhÒÑ>ð,ãré(<ÈÙ·z€XG â'¤€å¼NݺÆ^~íça*£}ô5¬J¼]ªrœDœÉ#ÍKqÐ,f[!{Žq©p•—Ë© Òݼ½ZåŒp{µž_!fF+FÈÔyƒžÓd¥ñPîì?ù¨7 :·ÈgFгþß”Ò@÷1Ô"ÖƒþýÙ $sB¡ÝO"™¦œDE·¸[5ägŒdàÕ” aºs¢e39Ö¼× F×0ˆ3Ò9tè4C#!“Y£ZL‚%€²¶A°ÔŒÆw‡, ’Ð5¼*iªž”íäa”oƒ<Œu•d¼éÀ=Ö¼— DƒJ3û$1ò\U/A3`©ØGN»ÙôÕp€M8º-Èñ5΃Öçá6¦êâø·º—ܘ…‹T|Ë$³ƒã‰¾ñakâf~BÉüÚé ÒÐÁ+¤¯ÓY`—:&pÕTÊñN°6~…à¯Z¥"HYƒ*dIûYÌœáAЊnÕ»£Ù»=mEk!(X;´¡ýlUÔè`N¦¶Xü¦ÑJcÚƒãG™w|×Û3kd]>C;•’‚9gEY4žµð*ãôžÊ•è.¾*©9ËÛ³©Ž2¾¡4q•ñ"·q…ÇÏ´ñÓ$ŠùåÚ‰šD²Rý%:R4ûáÃ3ó¤ð.¦™*ðÖ;îuxÌ‚
+âL'3˜(~a[6š‘€H<ÅÞŠ—Nz©QÊÿ/“,Ënˆ®@{ÈÔaߌåaíBÓ¬ýO}ƒ
+ƒrܯ[QÇ7wl6›¤­þ©ؾÃÈ̓ӟ+Ði@ i‡Ê­Dãu‹†íoŒ+lÀ
+ 9ø[ aBÊ€s­oK´Óâ šÍä«h°ZÉRΑ 7Ž-m~ÿÄÇ8¯2œ8äTREõÀ®^B`DsaéÚã*¾3î@š¿GÏÁ¢¹Y;P#_2×ÁœD8|Y¡óÅKpô‚”ä! Dé<Ë¿_Õ¡&ØgìÚ0Mò3ªÈDD^œLîÁ_L«X#p^½]Jü&-x¯ ÆÏ÷sÈôó+)‰È6¥„8öY­f‡qÒù„¯ïýÇ@ÈßëêÒ–¢ËÓè(n¬Ù9râ$FRœc&™¹ÉS2
+º½DL1¹X°9ÿu)z ½ŸV¼piÍ°ŽîpT…§ýÓ‘Èþsd‘ÓU1_4)éÅ…‡› 1åÜÿýp”J­­ž*mà#S|Uý'`ô¸¨*_àué…•då!<zòî)òÖpq$qS"Æ%>¢ô¸I|BÏßf¨
+½+Â쑾‡Ñ5ÿp”´ÉÚ¤æWÕrR
+)žh{<îL ¡`jTÇ|¿:뛪¿Pv\ÅvʺâÔú!‚"Û5ã&NÌ gcÃ×5 ’X£{EÐô ±¯å¹èÿz ·"¦eÛËvJÚdžˆÈ/+:|£õ;€WnS«Š6Úi,¿œO¤¿EÀ
+‘mÅ­´(x‰«ªòkT–j€I¢U•f§äòÁ@yžÒqËv +á]Y†mq)975NîÑæEoÎi0/D¤×Sòû'µ¬·ŠñKѼ®§±U)=78pýríÓ-/œòOµsèõ‰¡Z -¸³wëVR’¾‡O^Rຠَ¶r\¥tË“è²'ÚÊ"Bài-3 “í•-üAfäL;_%ÇÅGçâï<±²›*¬4,
+4=¾Ó+fbÛ`¯§„=gØ€”§Ä=S€ƒúçB58¤T_b “š‚Ø 7ñ 4ÝûKŽm-¯/iS¸†ØsÍ€¾`Á/E>ÌݱððæŒæü¦júÒ:diwwzSJ._ëk÷* Ð'ÄìƒC ÌS}¡äyسSvÞÁÚd1èïºÞÁö‡Xš¤ƒ }lø”‡ í6£êËobs †‰ÍÎëC Xûeü&†ŸDJ´Ð‹ˆ;`"#~~Óš2O´8ù‹‘_’›…Á®˜¤vóË~)q%ljy‹z-ºŠ}Ò&³e%ïz· òê"_ée\Ô™.>KÚß4%¿HS·SF†’ºø%XÐ7ä‹úq7A_›÷'§t ÕÇTŠ =NAoÈJþeE|îä·Ïo[IÖS“|Š—Ð ŒË,6¯‰ -)¦e;4&‚V ².Ìéé-$·±ü¾êÜ 'C#G³sØr$ÒþšØåʉ:ÅJ†Æµ$Ç%Ü™h[MÆUj6& Í@üf2’«tÈ€Š®tÂ^ç’CÛ BÒk/93d¹z?DŸð;üqÇMx¨
+@0.®Â0ÒÔÖ6ëŸJ9ô4YZ‘àsybKý»Øî’“šNM‰oIb˾}¾)‚‹\±l’Ís¡)[´ý2q‰Ë°DÝKXcè#4äÅ[ücW-…Ù±þÆR&›'1¡É@¼¶ÃïÛKȀ魧›=Å¥A<àoÔq@9í”NK  ëÙ?@½ÔgŒ‚—@g™
+þ[A0ÌèțƴW!ba¹/Ž˜qýª-chÏ]ÑN·¤ñ(ÚWc
+TlÀnœ©—¢ìšsIàŲøÊ®r¹¤/|6àÅmlÅy1^¼Ë*£³Uf^¹c½L*i‹h2·yÍ`
+Dâýƒ
+¾*‘#y§ïô0Þ“‰àZ((=. ŒPcz9xTä”ç©[ë5ÃGÎ|?0†™™A´Gx+M!Ð{Ãq¡p=à|Ø‚×5…¬ì‘'춹|ÖI"”©Ž!Ÿ(=Š·¬uÁ¢Ð[¡Dp§f6‘K‡«@ýh·lž“H“—Ó»ï0N
+/Y >žEØâñ¦ïDº(ÛD3èUüòâ*bˆ•ÕŽ«õW¹»®±¾š©ßš2ù7æ¡ãŸ]dãDX
+uü9úý8+1‘ƒSú{ÑäMIO@È¡
+$IL 7Ý<DHQ+È5ñu9Xeu˜/BÖÚìD‰T
+£;ßO#ÂSš³p¸·[aõñ%™iŸoÝMdEÀ:׽ǔ |çã³··ê2ØêŽå`¹_ß¼#ÎCÈ‘¾#‡’*žá €Ô4ÆÍgöóÌ<“3£<&(¥ÍŠR
+̤dÂè<^W0¤·|ó”T‚žô¾W/É<`Áõ ÿ+v‚QžP¦Ï 1¾» ötO¬#bΞê*Ž,Í­N/
+´¥;­Óm¥pζøNƒèK¶<%ëÒÏuâ~¸†FØCýˆ(³Ê÷ç",EáèÙw¢ 0G}'ÍN)d§y)ñAç;jÀ·uÈ(
+)[ ÕK$…GÉ„ðy;b®„[„*mæØŠ&
+MrÊ¡å3+Yß–’Á€õÅ'w‘§¯”#Š
+»×˜Acš—êqP! ,ñ…u0ÚY˜
+yWíEѺZZEK×+Æ£ j°ÛZg䔵å£=i~‰¹½ø¥<D 6y dݸú5WlŘ¾[ÑM*2H*¾UGA @“SaêyaÏüR"¨DÃÅÖæœoEBr•.ÊBc«gïÜ‹IÒþù‹vº.ÀÀŸ/E ©…ëqqï’¡ÜŽ@|—xyJŸJ&7)´JQ€y;M¤¨+Ñ•4ïsE Ó
+ó|Jä6åœÓðÃðL‚Y¡­Ø©‹cãdA£¬+ÈíÙUBØ…´¡4ó’“ü8³W‹—€ÿ_ØtˆygÑÇú?2ÍÂé!ßv)úô"r -ÕA}3œ
+–
+ÅÀ\S,w`üMÙzKµÚåªéh*‰>ÒØ=Ó›9], Å% ð“ƒ=s½4õ Â:=hÄs“ d„§k†Ô,ªÚ¼~1vÑ/¿Ö} <Yðd¸"?;Y¨iXÒ÷¤‚Þ«÷RéçRTUO¯À£Ôy˜Ð¿wx“9>‚~>½ÜÊ‚`/&‡ý§Æ¯ j¼à:$:ÔpˆL–`Vj"O¾sHÖâR3Ac¶ßz/À!Ý‚nnöó!ˆ+q¹¤…Q§¡«™°Þcš:Ke³˜+ˆ™À%™˜Ã"5Søä´s€Onˆw1S'E†F,,¦_†4/ÉŠ$˜¹IªàǪ§‚¿ñ¯M†Æ\×A‰ÈöZª)ã·Æ½º dà^D|ƒ1¤fÌ(­mÀÉt{”là
+¡5=ˆìZËû­)ÅBš˜‹
+‚òt„øÉ˺¢t[;„X*ð”‡HŸÏúD€Žì·a¸¸ `Ñí6tn85…q¸°¥«&N5ÑHQäÚj!CTò@M¹“§Â>QQ¤½CQSBrZ3î©`q¾{Ñ#¡Ã* ‚dßÔF ®‹†#²TèkJdgèv›,ÅFqg· ·Ê\íÖ‹˜qmwÌ]›¸$ÖëÕ¤x ä•;Cp™V¯"<¤’.1­qDPÎlCñ°¬Gr'‹ÉPgˆ×3ñ€ße€,H`")X·ôâUi÷Óì«Ùå—"1ƳžB,U&³Õh¯u‘Ƶl@›B¼R¡Î‡t "i‡ Z Þ$®8„øÖ½¬Ôû9ZVþÎOðÐùU‚úDw4¹Ãb©è&‹Ð
+~t]
+¹õ¤"Zþ[RÇ
+ÁMøÝB,Uc¨ÊÐÒÓ9Ð šÕÖ<ŠßÀ^õ¶Cø¤ :[w Ò™èZѯThþ°Ñÿ@ÏÑ&4`¯¢
+kNr.n° RÈÎÙL%²—홧›”Š¯}VO„âãòb
+
+4'jŸý©qŸÂ”©—Ñm>Q\
+ñ)ðÃx×£«H$™ÀÉ1äèßÁþ°±?JÐÐ2mKì$7‚ÚïÚ­¸±Ÿáˆ˜¥$Õ3A{Ä7Uý¯sBåõQ‚Ч¹ÊFÒÕ¾+]fÃòË ­çcuŽ&»´e$1ð*!؉¸ÊˆÓA%1ãŒsÝ! %·+I\´C֣ģmGtŠ‘§ñÊÔT¢L…ŠÃø1 Ð¥ôó
+©±U­»0hgDhϽ6èifg’žFîL­ØuÕS ƒ˜aÑÊnh˜Ð¶Ï¸&.9-$±ØSõlÝBKÍ.eìicxåVƲ¤ªñÔ¤=ÉÌ™ª à´ni ³sWÅJ·"Ç„rú;øó@@§ ¨ý:Täð®†OFžŸ¬4™i£ÌÇZ¾ÆÙ¬8™A¿¼>–}ŠºÀYm;§Èo&Ÿ¯ v€˜
+â©A5uW»D!”Îþy*æ•èn¶T™{Ð|ʱG=,µI˜oúŽµ{ˆj~›[ÈIØ‚h™IÑl}œn#%ž$ÖQýݽ¾
+ˆy`WQK¿
+˜$Aènifàæ’8 zº˜U¤2Š"šA9ôe%šõRV‡TÁvHõ3÷§YÓSPìSÓ
+†Có¥L$XvHN!
+ÀGt½ î3¡(RsC
+w®Zep’­wcÀfJ1]jAþÉb¤c GñB~K—+åTô#NëÊ4U;®ÓϘt¬_’ fl5oV
+ÔhH Ò þ V†²ÅaïFH…%$c92ôj8T‡ãà‹ §Xìæ–ï¹2CL»Œ•4ÃqÓB(/êy°èùHgQШ!Ž ø_Ùáôù’qETÅ>² !I›šZ4Qv©-ÁÒŒãå ï|ghÓ\ÐÛܨ
+5­ý§'â]´á̇Ý]½Ìà>3‚¤aAŸ$Q´>éd£3è·57Dp÷"té\B8€‘•7xc¡û݇ ÚÝ• •ÎAØb $>æ!'Ë\΀Åõò— 4'¡@¥`ŸAy¿0¨ÙË“40EoÝh“¿m›˜%å’63^&g*ýLn73@E‹{¥q.Ôi]BJ·îs
+W^­³
+¸Æ
+ˆùBC©R¯À°3…Þ¿OÍÔ<QïÝkЀ ‹jÎÜéOÕéhpp=Æan-É”ùþó&ˆçä±ëq
+aºäj ƒùƒ«¢dºrHMæ±–á¬iK²Ã-
+RÊøúŒ:DOÞϳƂ^/Ù–8$ÂK"Y>a‡h%jgÛº=”³)Näòv®ÂÐDoŸCË$ÓÙ¯¢^I‰Å3Õª¤r¨»W„‡ãWò T•Ìíd º8¨ÈMÇâ÷‚'Ù½Zw‡D$Üa›;¤#ãcg7šA““}F¹èkF_ƒ.V }4d˳(ô8A(, ÆfÏË2®¡²;dhÿÒ„â½®C¼ƒó[v’–ÐWµÁ£žÔ›CXG×&dݪ{Õ]Ub5*¥Þ„üÖ¥«¡õø’E^¼-¯zµÚ€\$Ðv3Kx«°€'7ñ¬*Gbv¦%ÞtŸ2ZéÚ>•Ö‚Àjƒ ¥ÊÁ¥«›¸/º³ÿr‡é ÂjBYé¢RHÄ6 ‚ìrFeç9±Wàp@<„ˈ°7 N·*†ff’˜K-_‚ Aî4ç>Fë€q ûÏ$
+oc ¬Ç"i0m`Óû•‚ }–0|á
+ )Alñœƒýsd¦5wRQ¥üÝ!R1úB>êÎÜ: ¤«µ’¶s (mÂÒy |t-xZ_•g”¥(_GN1r÷úÓXiQcÚÈl«þö~0¢a{NýP10e¹§Y8Ìò‚l•=¦A•÷s"¥K+¤8¥ ó( ž‘­¶]€¿/Êý°k.mžr{xdwEè
+ 
+ÊŒõØ÷9|]‡¢‡P(¼+2þ»…Mð 2•ÊÁË “Ö!‰‡á€Æàà³´ ¬óGlz·Ð5DoÙÇP~>-…lk]ÿ·ø^†~àS¸|œ#W±-U ßQµ¢`1ö-œ0®õÆ7ifŠ¦˜V½ò…¤ä¼[¼ö‘³˜š™¡øqÕ_ â:„<ˆ´ZßÎE×SB ßz¾i틹µEbK.qú†õC© 6¦ÌiîõÑ5cB'?ŸAÖŠ ~çï˜'ºtú$jx,Ìl
+Ôr
+H‰Œ—MŽ9„Oà;ÔæA”H‰\»—¾…YUÝ;SÊjW¦r\hh§ãIü CuÔ®!oÿiC^¦Òd´Ò›ØÛ¯¢}¼¢Ei5ZmömPy™›ÕRjñ¨oï r¾õV¼H­ºýúñß„õWW3é5bQÙ˪j‘QBÔß>d¯è!_Ìôk */ªµT‚)c‚n×]@ï߉i…^_UŠÕa±`2ø•ªÚ˜o DGé¸ÞH­VŠ¹‡uÒWX‰VŠú µèCF.M}¼ýž7µ^8³õVOP)1l Ó¡óí-¼´&Ý&dtb£¡ÒÅ'¤þU‚Öõ d]54¢V§"OǸl9õ§`¢v¯ƒô)§ö*¥…×3˜ke@hv¯[ŸÇÜë»üÑ¥óž+„ÀzuˆßZ¼mB±WÍÉarÛ%d/5)UøRGœ•»Ô%‰åc³©-€î]àiÓºoѲ.º´zwÎ…0›X¾ÒnŸÑ…½»ÂÜfàZÝÿ?I?縕×ð¹µViAùØ‚vú%¯æ脵ø6¨¾
+ìÔj(ìûµ½î[ }%Gä….¿?`z@JYº··â„ãFÏz¬‘+ŽLÉ'´ÞÕãD €Øÿ’½¼B*L/qÎÔhRƒÎ{;Ú.P]Ðm¸n‹`ü}Xãs>!"Þ·S ¿@~ÏÖ2ÄH’>>SÃ…8…$©W„õ·]€t²!>i5©±]äú}EÓz®>†ô›Q9Ш±ÆJÜ«Z™¿± ᤠ\fuVyBÆHáó”?!«Â=eÌÌÅëÓ9:º@6¦ñ ™.µB­Ç„Àƒ º& é£Õª„p^ý›‚\Öà4X£•¡î6ÏaÊFѵ‡SiµYG#›>Aþ$èÏ;̆é/ƒn!•ú·A»é»]÷-зüM6æ27g·@ÄIÏ CÀJ2¨~¨ËÔKžž¢~´-!Õ»£c½­ëzD3IC! âœ0×*1!c Š[«¶£³ )t’ið r¶B”u ”*ø°2Ë
+ÈW|Zý\‘7}¿ƒvåÁÊ9¢¦½?ÖðÚ(ü_¥ûPø·Ú^GLLÇ8J‰ÛÎ:÷ )u‰ü“v;ܯáW…ï%—©ObÂ|B[„£Nðˆ7 6ßIpœâ­¯RyÆÐm ”‘¹ðQf땾:ÝÀ‹6k”¡¦êÊ¡@¬"_—Pd9®âm—Ñ· Âsê¼:¥…‘ª|Œðd± QÃÈû|™$q’¹F­¿cÈPŨë~öëŠA-=¢Çˆ¾¬`jÁ_õÑÃ÷Ãl˜Tƒ«¼—n4–„ ¨l?Kb Ã’ƒ^ɱÚÛ¬bÅx3”SéòyI„Xÿá7…âñ£Ø@ÖU5ušQU}:æÒï{0WÚìrº‘ï^›+ƒï¾ÏÁ—6ýþƬäT•9UœÕA3ªMq9îc ª?9Ò³7Ûn Ac, çe‰_‡ÿlÒ iÇ#²©žÌkn7Ô¿Êgàƒ÷[nA?½
+†SƒTÅW/ Ö‰þìœÈ®ë;ÁØfγ"SØž—›åÝŒÉéÀR1,ìÖE÷ž‰7’[Y‘BÇ©ÔLaÍ0#<¨ Èi ¾,ÊC-Ù%,d¶¶%fÉó¢ÍĶ)ÒñÙjUaû¶Î³^~þ؃¾´þ} âRØò\NÏW^háA‚é"¶œiV7`iJg>5ï§ÂÑÑÜ/ÎÔñófŦ­}‚,Iú9³Ù«Àù¥÷߯]Â?Âœ¹Õü9%¹#á‘)ôý9Ëqžv<¹ÎX0ò…¼‘ï Ñ| ôTÒ®H£8A˜²ERä‹Û{Lg”B„ çˆúqLL~ø嵈(Az¶Ž›oûÁÃan‚‚,ÁøÛt®¼ z LÒfG|LŠ4>­ã}‘Ú9é°ãk­!‹x4o1!¬ÒqÒÂÕ‰bû¯ÉÊ÷ÏðŸûhFÇ2æ1ÈéÈ4F[%ÄäbéyŸzœ6sÍý¹b©#ƒñåVlïnÐûÑÔ¢)Þ±Ö£ñ¤2®ŒÙ Ô„ç6|H<²–‹Í !%©7h)ö™9
+žf™9ö?ÆË$9Žd¢'è;Ô h1knu m©ûoÿóEf"õ)µÌšETLpøÐO¶É(ÙóÜî¿;NvÞñÞø1C`ùaðÆLûRµÀÁHÑžUOœ±ø´Dá*F»SÄçTz™õ‰6¢ˆû‰µ‡…CZ.¶¿÷:Ì]—˯§„¾Ô,+7ŸÜK€{Ú‹x€Ó¼ÿç}c¸·¦TÇvbÆ^Þ‘éo-K±/ͽºr(÷¢¿*Ÿ7þö€Q„Ç@
+I[õnM¸kÈ­™žñÇ( ¦•þ´ŽdΚ4Cvš@ˆãçÄ?ƒ(ƒG$JÓv­àO˜E ÆaÚ6³*gh“ßæÔ¯úòæ…ÄÕ‰±'Óõòæ.I.°ñ¤CÂÌÎë
+]6»ÈPPM{²íÁsg†èf(JÔ˜wux"¯× ´
+–h§¥iä_Ò•èˆIßÑŽoóM%ÿd¬~ý•@£?Œ 6Ú±vQOþ`½îz,w”¢\áhÓµ|y?—!´E
+ƒ,ûÓ‚ÒŠªÀ‰‹¹_Èí±¨ VßGs¾Øャ‡s
+{$xt½9cU9jø`ÁÆÝ6Z¼«4l@ADÂEòæÕ<ŸSt±#>eü¨22v5.nEdð©¶#Â}·‚O4%9çãT¿gJ
+ç(Ÿ÷ä|nEõܶóœÏÿ;“F†WZ†X Éá8ã?»(‹®¥’|ûLI]¦R’wÌ(A‚‡Wuî|&r7¤f ¯äªÆ©v( ¨³OZiÃü3ÚÇlÑ—û^‡œØÙ¹¥vÌ1GéA0µ ¤G6V„0‹û¢j<·”7vÇaËŒL§èÊ7¬}±C+ëо¹ïUÞÈ=
+€‡:„Ösõ›ª
+Ã^yÅ!°öÊØy}s¦ƒ¡ïVÝkè·’µ–õÂÌ¢¿‘‰;Lì]<ªT¸ ì4‰¨ï¬SÃí´VìVº+£1sdâ;Ö„ï–ˆ 59G­áDwlœw W¨Y ¦Cn 8!”h§ÑwC":)ÕáÌânè­ˆÀ¦žWÜD‰ãå™·¨Ä<ZQBmîգ숯•Ï+_žP{óÎøÍR¶=º5"‚7:LZ™0•ÛÐK‰a§BNèPŽ;Þ:ð!Ô›€û9Ü%Á4ñÏ`ÊPg\_9Ä¢Lš3‚Ùß[ îed±ù&»†Lµž_ÞÌ$}€%%Î1a/oö¤üCˆ1aŒÞÿó¦øRôaEe*‰ô.¢MXÉ&OÕöÍaë ™/Õγš‰T¥šËMôN‘qÕ„ù÷‘ùCLŠ–ÿ¢ŽzxÖ¸—Ál¢¾@·Gó{‰W59n;/%†œÃÄéõ–üe’ ›äYS:m
+wN—+òz=ð@ÀÄ»; ´…0ñK7·¢²ˆ’T×Õ ½£³t±ý‹’ƒÀß­%Jpê`«h+ÎÔñhã“Á-ë„rX{ÑmûãåŽÇÑxZÁþ Æ~¡wáTÚún‘àØšf[Ê, j’@¡>ؤXÞS8©Þ¿Õsúƒ·ÿ8.Žw,üwlÇ™~¬¢X+î»OITïõ¶hÇý‹&ý)A3¾\0(›®×G_€ÌZôê'!3{A“z[ŸÁüÎUþ•ÃYÅÉ.¾PúÕd «­‘F$¯Âg`´æŽ«Â„ÁÖ<lŠ~ÝqR€¸KÅW=èÓÁ|ÛÔÔ{:Ò§jáò0²Úô‘œÝ É
+ÞCš—Ä„K2H/xi]Ëgƒ|-üƒU…Rÿ‹N»€°Á'`Êžéöݬþ=sÁ#lx´š:@çºã È¶%U9ß|Tí9³M*]…%€¸ì\JhO\pâ®Úµ³Íô{¼áÁ" E†j‰vãÚŒ¨­XAćÔñ1ócT‹¤´§cIž†Œ!½LxœŠr—RhðÞsò×¢ÁJ¬Ófy ªQäGÙ¿BÞávÑVV–¢·_þ‚id²„ 
+¥ÃŽù(¤ð²½Cl9føqÅ ÚÞý ä¸s\O{åÛ~)ê ,RÐÛ~âoÁ©@#Ãlé Œ—"ô€j¦Ž†‹ ­ÚY/D˜(ÛM~ì7z1—0am[OGÁ}›)u9J/‚¡5矠›ïŸÃO2¸ bÃn„èB.ò÷¨Š«r€‹ç)\€5òä´°±McOå
+‹ìÿ4lµùQ”b•H‘¼³ºeùÊ¿þ˜Îëô‰j?ðïDZˆàW;œ¤Ê·Y’È…p$‚å¯C•aRIeë«}€*X†m¢ÿPOˆ4Æ}u”Ñê@z¯=AU.>ë«tÌí¼öí!ÖßÈ\\Kõ­‹°ép¼¡€ˆ–'¼¹ËÎ/o:cžNÔmô¥qEa‹u/ :}»(>*Kñ‘º«%Æ/UEÀÔï‹¢¿T¾ThïaUŠEÙâÙÿÐC¢ö
+',âº
+/°^hðuP|˜ˆà˜ssI RÅYµ†»Óñ<Pafp‚š®A
+öÁFðD©<`,×wÀþP€hÏd:¤9&W7VI¤}„к߆ï!@ £¹R€z÷ôH~\ß †!`çÊ9eLs}(¨ƒ¹Ë ´ÍðךOÁu½ˆîËBG®énnÑ4Qa¨xæä#2¦Èl;´Ã®úI8_l<ë¸×·²ˆ:Â_ý„•¿! ŽæxC=í÷†0⎵çqþNž9 úcYý;Õ0A¨Eˆ {¬"3Kú3ó ³Þ”§ÔZsH€$Â!T @ww2äje}‡Î”ÐZNÞbL ÂõÌcš¤Œ‰m3÷;ÌŸž‚$¹äCÑûM–žAq]öí†t«ö¥8°22+×îõðŠÜ`j×dç-&q84úg`s¬hç1¾Ý°€d/·cLº3’‹§ùRâGQ@&L.ä×Ï€›¡H[”½„]‚:ô×Þœ$蔌\ó“豎WŠô¢2ÎQúºM9¢ž¡’ÜÝ{Ä -–’ÿõGÆ|8½)ùäÛ÷Q¯ENÀâ
+IåÎ)«Ê·
+¶dÅx˜—#\DÀ«¤@ÔYÛ,Ûñ¤=gIiîIGÑ’à|ÍÝXCÀ–ÚÞ*^Çú4Á!ÐsÈ Ž›’¡?Adç']jx‹Ø¼)Ù¬ÏL+2l91$R”•iP
+ìÏ€ðbq—°§R2iŸÄ€°èœ½¦ð( B›«o^­fš"â@V½³¬`‚ÜÂ87Ö¾0¾gXôÛ@iiêCð’,#cpôw«P[„ÝÊSa0@½Æ}y5aƒpA+‹„‡”é/!zìi`A¢ÚZ†C H’Ê…CranÇF,ò¥ä}—è§FÕŽN4+£d£íl̵´Üw“lp!߶.’I°·¡ïTÙà#^ÝÀí³ÃEO ë~›Áv3=§9ûÅ„$!¿È93†yžð —gœÜùî3àµίoÃ6hœ‹òœ“¾¨„•¶ê%¸|4.¯Ôç~3Ì~ÀGøËÁ4 *eeß΄#'…M]ó+£úS~v¦ÊЕ[uÕpä~YÚÐ#¦Lj}²ÿg ”_‘IqöGŠ6«u/¡Ëc6d—s¢ØVœÏ§FÆÞ&:”-¯¨ý£Ì=§öÆcÁ`½oWÆ«*‡½„‡(L—Vt
+/Àï#mý¹Îu®”žLú©è×M'…ù8vb^E¬õÈŠ±[ivž<k×þQbzG°ÛlòOrÔì——( ä¿í þ6Š¿×QMÙþ¢ÎÛCC˜)Ž”à±ÙH¸Ún†õÅÌ…Ž°Ñ ¥€¦,-ya1‡œJâáe;R&Òcc9ó&^!1QX%¸9:Oäi~݃Ҡ$·Þ_tG”X$;Ã,Á½£ñâ)_³"{!Ç–×4.%™ñ°‹CŠBþüq."d*ÒàE@Þû±è
+F® õih|ßaO¤ÁíÑ èÝÑæ 1CwWŽÈÓ¾ZY„¸WãÔð¨V<Èmó%ä¯J–W«GW~ýÁÜ dxÆÎV’èî>ƒÁ)0à0¿
+öYçWY L†Ç|‹_’|Úm5„¶@y`›Á¿\ð¬À”4ƯØë–œ„
+:ÅhÊú `Œ›Ëœ¥K;>Ä,ql‡Ç„Ú†.Ý}·†M©<k[4xˆ¶Þœw¢0àì÷¥Wø$Í<í“x{1¯¯8t7]¯ï‹AÂüX˜ýL‰ ]hùˆ?:lj Z2j{â¢"¬6P¡¸E»j&°½x_Epã*s÷rý”Ä–ü±¿+ëe]Tt&À¯ˆt*2=x(8ÒC6Ú7 ár%Ùù¡â0È6(3ÈîYŸ€¿Oì®ìò¸Ù`+•àÄ‚;&mpìZ;G<B×4ºÅ–¡@$^–ÔKPFZÝÿLÞ•µÂ£:Ù¾”Є‘âÓmí[Är0¯o½Äµ÷›"v$ »_9Šëÿg¼l²ëÊq¼‚ìÁ+ÈÑ/%ÓÃÚE¦Îþ§ýA¢|â'ݪwº¾’H
+…áoˆ­c n™¢o­÷Xt47®Ú{°pBÅ‚˜²µ;9}ƒøQ´[±€ù«OßI\g¾>¤¸Å?©LÅ‘NMö“ù®?‚ð^œÔZ£&Zf>&™X(«]ÀU´ A#…€ÕÑö$PžÖÆq‚nMçŠûÖ¶ºì¿A6Dá¸"|¾÷
+ú|gFÿY£¦(À;ö†Ù€¡hG`/]£|€"‹.0f9ºëD¹åﲋ·A´Ÿÿ‡@J˜ÿÏõ¸·@¨
+Œ2Œšö!ø"O–”¹D7m¶=šÖÇùr„_¿§5Û›bhxr«¶XgzCú =_ ¿WÙ9ÔV<®=}­®XOîuØ߆qëãE£dJ­Ð c?ý>šT¤ÍqÒ¤œÜÑß•‘RéóxHÞµÓ´Väqx•)óB( ö"šDó¹c.ÊèÃœ,Nθ`æ_á/²VRIÝG1%Ô“6‘²ˆxXc²º¾C^!FÂϦ’‰ðj«}‚üÍÑ_?n˜“ì 5C‰ÁÀ˜Ææº àqÜ[ Q¸× h
+ YögJJ4èsµË¤™¬­´s8 dìr,µè@
+”ý}
+ó%½.´_nD§¡Ü’@x&ìi\|ää÷ 2ßbÂ;¯+Š £`«&KŠ~#%J=rŒ‚À]™‘ënˆ‘ÏK
+²BD* 7ißÇ +îèò‚ Ö¤NíµÃÅÑ’ÃÈûè¨,¡CŒýô ³Mæõ.ò†©xWëê^NÊÃ×íºÊ]óu•›´h´/âØ=^¿£ä5©7³Pö]à‘Ö6dÜIš4ÉAë¥oi_Ñ^[ß:
+²—ó!eßµé(³¦¤§5ØÖ¦uc
+\¨
+G“áÚÇ}ò¸ºEcá‚’û¤ϵƒî¼,@½!k—ŒÀëöãÊÜ!*:ˆMƒ8#êëN™ Ó·´³(#;&„h°éƒ8}ÇŸ¿óÒM6¤HFõãÃWH
+.0gq«E’~"³c!æ‹ÿ«~Žd™nªŸ'×OÐ%‘t‚| ›)ßÛSl9Aç;+—L¬þ èûЃ*®êÙÊæü*8ƒI¡Ì×&ƒuÔ½ouÀZÕgä—WuR/hS…r±ç•SKƒ‹ÁÅöðˆ±ö¬p/qÞ2K¬ããF0žžÜ7(áÿ~‡…NÖŸRiIW-—†ü¹‚0f~_ø3W­IÑ4-’ZS³·A·®ǽ
+*\”®µñÄÈex‚¢‚}LôhR
+O á'7×Ó%° Ñ#— Yob©rÊ(½]ÅFk«™$|*ƒe0MÞ9Z¯Ê‡@`j«Ý9 ¢CÜŽŽµ˜l!ÈêU7ƒ#ˆŸÔ:%D·˜¿§ï ŸLlÜßáEø¿\pòÙ°R™iU
+«BqˆÌÈA¦p`CqÜ'#I'` —Ôžd¯/“昨½iµÉÐãaÓô=)uŽŸ1Iû$XF $` ÷zu+ ÎÕ§÷ÅY‰p?*½RTŸ*Ô¸‘X¸æ²IAÚÐëõ;Rl´° ‹ÐËoÓÅF108d0$`îýQ0_à·¾MfH‚VT/–“µµ™Ù['upÔ…¥«Äèw‘s[Óp0ÊòU.<RŽ8+
+H‘Èç† e9²[ô˜d µc2¡˜?›¾Tæ®DIÿg¼ÜÑäºu ¼‚»­@à+Ö gNGûOý¶­iYч$P¨ÇP»áQóuÑÚÑ&([>=Ñ!$%:(åÕa¥PüáЋ–Œ}&Aø›´vh 6ª!°-€BörÙÃ0)‰µã÷x iÑÓgx\¤—±d_ªq‰‚)ö%v®Œß}-8i["ÿh¢
+à)[èæ›Éá¸ãçn2ÿˬþ§ýöu±Ch°
+E+9¢¬°l ¹(¡²¢&tÛŸEÇ+R!%špQ<5€¶£ì=W ztG>ÎŒW‘1Q"B»ó:6—©°‰ËÛÊ•;<?})úx(Jê)3e·²Cž"E>¶fhº“±±x‡±×o°ݼ<ÄŽLXš*câ%&ÛLê,ãbÿò“$®¸f”öh,cAøB×ÆñPfFÊ'õû‰ éLØ*._8,°.j)×;Ñ1&™ÏCtÂ#R¢ BŸ¸œsX—ÞAÄ„<¢?ŸË%˜P•/¬°K´U@?ëc28¬`¹–Tö’EÔ‰PŽŽK ©Pƒ!•~^‹Þ‘È•E†E
+t‚Á /Œ:ºŽFš‰ëe_¨’vòìd!mî [ÕÿáJâAÂ>“
+C°—Né]ÆÜü3C&SJjå*\L*¡èdXd=èE…dÆ;Ñ´úôþ£ÇóÒQ¾H Æ,(‚¿»"†«kæåÕG¹ª±äøWV^ÎA•P‰,/ª~4RH tá=®ø£ÅSáløÀKRؽž†͆b–xkì¾&Ë™ 3‚O&["Êñ.òDÞK›O ø["ý—C˜þæè ÞjlK§ã–\jÑ
+ yÆaõÃpÃÐÛP"w‰aj>±Ê2ÉÕVüÚ
+À6¼.9n,!Ö‹¸¼x¼ÒâŸÑÑa¶%2W†N‘‡ÚK²‡|8jBxé9ê­k,_Qœš›TXäU @4Ðíð©`»$&F¤^D3ñ™²Û9‡SI[K6ðÀ†ÄÞ“øŽ(¸ÊÔyßøÇ+¿0ÒŽÏ uÌuY²ŸQ„”Š?L¡ÍôðpóUJ`Œáæ.A#Ác‘¦c¢zÄDKâR4Kæê/¸C.Ê­óeð¿Sd—ÆŒÍÂÀ(´¨]í†ð…ts½5æƒkÙ〳d\ÒݵP¢“ɯ¿J« ‘ö£ä?v‚)ý c[YÛ˜úQl$¿˜zV;/Fêòœ[”º6’T†7;KŒö”†Ûéc{ÇK…F
+Þ¨KƲ§¬
+Æÿ Í}z q¢0]0^bܽ§ÔÃæ î4X¾«ºüÕï†YlüŇàŠ>¡3ó’ ]‚AŒX î"æ!lpμ•<1Ó/E×"Tˆ é¼ÚóùP„ù•9àñÎL”$åîÕB H䱸‚´ ÈŒƒÚÐZp„7ì¹c~Eÿž*$[^‚›`º%–õ‡Ï£¤`vÙbo ö^Χ|)‰A IÌ óÍž¾#Qgù«åãC$~¥{Y,7%%)ÎÚ´:ÇkcrÂU akˆIÙÓaÑÄíÕ=2¬41Ú½¢›8]*È7~ľ€ˆdZº}>éŸ(üµz€ÆarZ¼
+–âh~9ìÊ~ ´¿ZÃ匨|òÙ*­WXî&ùå¬VÏ«È”ZWŒP¸¥Yh¸}è˜}ƒ•G˜{<!ÏòCþxÌ–¶ºKAÆòyVŠü†,áúÝÿ7ß^KZáÿ/·)ï‚xÙý;P>¶Ûp`ø‡û±©H§—ÈõAß,x5dž­Ï!hCÈ7µ|¥v‰>»…Ä b+!Œ©H`ˇ”%è9ŸIÒMìMåÐÍÿÝŠä„Š¾àMÃ
+^Š$6‹±ËmÄaØx>/«RfÛ-Ãë3®Ìöc¨±rAèà¹Éòäå\çkÍ/Âúù'ê!S`á­ (§Æš„Sý©ÅETåië1J ªeÊp‹}¼Ï¼4zib÷uƒØ*d4€¼˜ïþŒ+zPF¹æG•˜Ì »½þf¼L’ìÈq zÝ០ó°ÎZêÚJ÷ßÖsL“"Ò¯.Y›²$pø0òã½È ù#à!D“R»ÈÉ^Ñ%?Ò¡)u‡SLÍoù‘H £U—÷yéC,n@ ÅO·aÜ£Ÿ.‡ƒybJõ)A*ì% ,¦Ñ-Z%Œ¦`›q–ÑL0Má.Åð”!¡œ&1"¼gH`ɵ*£ùò†w/CÞŠ¼¡‡m
+1ª€ö´H½’Fsu¹ËYÀì̹Šrr
+Ž>ÆmY(*Ê\7£ƒ¿/¿>¶¥$«¥™ì½Øë•Šè9Q°üüæ-Ó
+¥d´z/‚ü4s,9^Þ“eÖ.£€šûéM±._Z¨¤Ïê˜_Þ nòu¨#ƒøDñ¾œ®µIð· iJIŒÙ,% ¦øŠHp³< ‡6© ¾Û¼äK?ºø ·¹{ïÛî]ðó›·ÅŽLê³ðºL²©cjs{\û¬*ëÓ+ÎÚÎYÔ"Ž.792©&7ª0þãè’žzḠZRœnºÓ‰RAgŒeçð=áR?¯O*d¶lÈAˆy¿bDuRÒ)~—v`Ø­H‘¯´³]ż±Û|y³a(ùÉ9]¶È« ‚)ô-“^æ? ¥ðÓ=Ï[QYTI‘¡?—u,3®§ìì
+dÙJpô. V¢«xSÇ/ä·x%{1¸ãbVI/owåû—‚~Jh_ÿéÂåà¦"`Ý’ÐdÐ
+³q¿Ü¢òÁœÑ ¾q¬«+rȨGIÐn@PPÑ@JÚ%K0&ö¨(óC¬“}>Ûµ¨1X‡hCæ†Â•ÎÈ
+÷Øq½Ñl Ž\T™À´KŠLXÄïAú£d]ÄWs´A»ŸÎ™rXëqdŸ¼—ð¾Cd¤Ú
+2Ôb|)Öðõµt§ÄºŒà"¨âLýzNÑ +› 'ÚkȤ0v. _Èdóù²ü UJËÐv0õ¥L
+ìÔ<N¾Qÿt]6Šo è´Z†Ÿ’à9èTÇx [PJÑ°íñá˜¤Ô Ÿ²ÛñD¿Kغ¡Â [’&K·†¶àøŽobh‘ V˜ý
+ÂõéãÄ”Ð@Ö:S„Ü$™¯‰Oä>i…ùpþ1Ó\Q xqü%þí¢€¢R>á…ß´ êËeõV4”‹"–=rÕ÷ýÜxb¹™Þ.ò²’S„†Adq7ˆ•\45æ®@ÿõ]‚5§‰M{ú”•”
+ƒ–$…scâ}7sÄ@†½.©M§äÇF7´Oç0-Á©Ûk"„È°ã.‘Ìܾ<Æo¦Ðn‚ÜÙ®K§9JW@¡ÝKͺhœX×ÚÊÚn´ËÊt2â1—ŽÒ ¶;l
+»9C/ãƒÅÆ$éAïÖ\öïç;Kj»Ü?J¥kÈ„õ&ð4õ[ÕÚÞe§¨q1xÂ
+¿vIÂ7CWuï)%S $¾
+-«xµõ¥íõ‡ Ò³ÁS„,Mrkr€iq½(J87Á ¸@„qœŠÎS”òXº]BØ :mí¿”¬›¶õ€€#3òÏAÇÑuœ¾L¨û*hsÒJäøôMMÉÃR õ‡Þ´åÆßÅï°SòǤÎU×"PPrãS¹ì忤ÂÌ<¼æòðU ·)ˆF¹)»êÖ4½ˆ»3o~ù-Æ!€#âi&õ{…Aâ6ðÛ)7Ð\Ÿâ€ïþIˆæ!ËÒ‘—¿
+¸t˜
+@G†Ad´Š‰,Êv’˜6ÜêÊ 8S¶ÈŽQBcsQßçTÈ&²F}ÓúôÙSþ´-ruÅRì‰Ä©°›B¸>Cd˜-+R²ÇLLø¬‘æ´—7J -ûÛ·ÈÝŠ.²Ë›B¤ æଳ«rRcoŸ…é׎ÖæÙðÝ?v>³ä1x&Ü>ç;EêùéY‰bæ´…ÁÌlòÝãÃ?%hh—d|Óæ3»u¹|ÐQ> èƒÍÎ#Ä2”Dںϑ©j˜^Úl¸€‰ù W•§’ß1úùÍ«ñÀ^dhC×4æãÚÜŠ¼¼]÷VÑ…üåºÁ(¥±o©(˜·«ƒ§ƒÛM0™Ý`›ÀË(Ö„X_©EZá!CÅP.™ùœCí+.µ]iåßnûõgÁ}O ŸT~ÚswIÀ9¤NÖä ó1Ñ âÄÆv kjfi lCñh õZãQ=™ׇ_aÌ·‹¼qÉ *éágüKÑOâdð_k€­Þ#%Á=$]d M'Óœ‰Ë¨»ÉœILˆÌ¦»éKs`D˜¨¸á‡8“ ‡SÀ5=žSe¿ØgøÎJƬm$Úf–¸øòÄ'N„蜫þŽÀÔ×”fM¢Ø캆=K/¸nüä×.bëؽ8ÇèNÑ*a‰Ú JÍêÇ+J8
+ÍõxVöw2xäYÙ«ÞøDW¥ÖÐr­Òð³úò™, /és Ÿ‚’ôiEï$ß°_ÙfšÊÔÁǘ4^­ýØîŸnLÜÅÿõ]¤XÏÎJ­YÜKòrKü
+Îñó›_ó³ŠÂ]ïkð÷"ŠŠd‹}y‚]~&þŒöä$V9ld6iâE<Ÿ~4DUBPJ j [A<[$˜…â±7x¨©"ÖÊÈÍ'*¯ÕV„Zm&­7"ö?ãerW’Ä` ÆY /÷å¬9Žs¥ü¿ö\ªY/#¥ê¾"^n
+ʹó³#L¾!•Ð ¢$Œ-Ë_ŠIôCBxÃø»¶)øXði®C“H›ÝJÇdF}¤Ôïû‚tNªs)u²ˆÁ…(XUG™ëˆ;4,½ì­P90ô¸¤0Ò)aÒ‹GJC|çaoE½õð*òlt„¶@›ý’Ë
+¹°Eïö2Ø6]ÎwL€&y⦇Ᾰêf†o¤¼ßïûè°¾#ÄÔU[s
+ A‚™Új‰D‡#!¤…zÁ?jé¯cA°
+²B(+þŒÐCq+VUˆ8ÎuòDRåWTë6²Y)<¦”ö
+SûDŒ)¶ôµì†‹—Ö[œu8@F@
+£µ52¹ú1ÈJQl‡A)Ÿ®Ië8– `NŽ Y¾&(©‚I©Ío-ì*LO­æyÿ‚qTCÆlE¥2`êàÝ”Y£¥ªèØdƹ¼ &u]¤o…'09$}ñÃâWÆؑȸ·ú3 G€Œ˜é <i<Ì&,¼=û£Zú§ÁU@4^Åj%NØGÃÄX-Åw¥/Ÿ0Ñœ}ä> ‘{ãÐ¥åYûB–ÊÒò~!J˜Ev\ôÐN¡I5&ÏN º!WD ñ¢PEÙ’<[Eû”¹CVÖ@À]š÷2@te%/1P¥<OMªÖS3ú,é hó ôSb„·×C‡´!]2Ï%8ÛÔg@UÚ‚Ä(6Œ'”àÔFänÃu€dª°«6%¨ŒA Zâ×+ÿ¹¤S¥Â¤‡ÈÝÔ;M›È!Zˆ3“á 2º ‚ÌïXZ˜
+öˆYÐ,³hq˜!¼ymGýb&ÉN6„'¡É6!•O4&$´vÖ5*IñQ áû j©º”Ó˜CA±%Õ8Z^aÉÒUÞÄù‰Àsd:¸7 c#¼×ÚéºN‘·'F¸–.gÉüè„êâíJŒ(
+ošüìŠói€ pœpSí6 o…Ú[=A‘†%‡3ðL]1Nƒg‡Ì07ÓåV …?H}úÞêxl‹È‚›ræöÓUÌ1ŠîéR©ïˆµÑ³ÞE Ê<ObP1Aá€*£:Ì.þ°$Wq"ÒBêÈ[~½úŽ]˜G‰{†ù®Ü¿çÕ©£cXÍè&È’Œc»èë“3MÉð•Ô’«'Ávg­õ{‚C³$oåyV!ˆ’¾ò l€JÁ5®4RÈ­¨//šd º¢1á‹<lÆpßPC|Êú’.qlÚS);Îûΰ¥ØɬÙöpAŽJ7—‚2bÓ’ù,&Âq‡¹#Q®ìbηuºËšØ8x5O W
+Ð<x¹UÐB^ñre{ØÂ2 ¨î:DÓë‚T^Ç))­¼G,hȃXyºà²¨èÔθËßGÆDƒ»†/1þèå¿. Q£cíôV™/DC¡lÈd 2&(¸Ë“¥ !DÀ9¤¬z–÷´šWDä2zÒ§1|ih90„ìR0Pùv÷FÕÄ‹f :61! ̸¬D~͵hõŽçð\«5Û€”J]‚׬µîDXi±R³ÿ—ƒƒ_ü¼2 •'eæ(-ÍuR“S Ûª:½Îy$ЭÓ$„%)ªåu4³7 9½÷AŒdÔ\íG°dmZý&d'(È‘b*Ú´$Ã
+Éfõâé§Z‚?ã.ÞŬÛJ§#iú«w1¿¡EÔ} ö:ݬó5˜"‚Ù %‘DíæzŠü ²(Æ U)¹­†+!òè!ò‚àÛ»­¢"„8˜k…„ÅÔx^Y umLR#ˆÀìØŠÝëLœ=wIx¬áF jÞõ¤Ì÷u<3h/¸rŠ¶Æ§q¥Ô ñ¦ñb``aˆ°à©ñ@=&skÉÖx (,:ÈÜn€¬’ÓzÒܘ<Qôc¥ñè©ñÐ UjrÁ‹Äù×Ðls«/3h¡.Õ¢“ÈÕã´]ЇÂû‘CÈ9—›À?1ÖÓxhàð6Æ©©],}ÿ›p}ÿñ_ C¶x@!#}àÉ)H˜`oÂñ¢ª*ÃL"É òô
+_3³MCje꜄ÞO\°.ƒ–²2çœ%£oyEš‚Îe’˜ª˜ûrlë!Eî ÕõC€ðÒ0Ik ÈØŠ XÄ5`h@ r_F’ÖÈdìu˼ èmë—QŒ>©ªŽþu”ÞAd¨]|m¡Š ø€0ÖÈüÜ åaûænyM·b^pýqk•ˆ:x|6$”e]=ðz™Úò“a˜hAç:Ï’ V[¨4ôF(s+^™“t–n;|¿AVA“½e†¼x[çQóó4OêX—:x>ΓÅç Ÿ½ðV¨ÿÐ/ê,·;+h¬u‘0ßå~› 2Žø3®—×¹ªvã°ëþ¤[$Hê&ÆssßÂÒ„ŽèaoØhð¯"Q2׆Ԯߗù‡î2Hbaà ú—ÄìœûþÿÜ•™8™é%U0É=[Ñ5ÑâË¥ôÙõ]Á ½,Íì3\Öc b]nC¤$Íᵤ;ÛÊ5„¸aÙð)ì\»7¤C+a£“R˜ráŒS%adçÏFႨ¤ÐfñìÔáú€9§ÍßÏtëüo 2"¡e CSV>ìr P<ê„4çPîS7šYzðŠHi°hÚ¾¬ë”ÿn2Í–jÁf:eé<£”ñ°øëºSð'¤Áš{yY•7H±GE‰}¸
+H‰Œ—A’[7 DOà;èQI
+æu™l¹3’6oe¬ L„¸œI|O³m·Wò€½•;҈Ƞa— ÔÖðØB¥=1tI¨n5+ð PFz?6“ˆ4ŸA ¶äéü²ÐâÝIvÜ÷C.ê;Рà1pVF]ÏË“zš¯+þ¦LùM8
+ä‘ËU ?¡2¸Ó¡ó½˜wRÜ[:FêbïÐçŸôwf­Ûßy!~‘¥x~?L÷Ïa“ ¹6ž$ !(8N[ÜŠœ¤8!ÍxóÈù¹$
+
+Šî
+R;êNT«ÝV} )–ÎO[µK÷…|Š
+j}Ë)æí®•èö¸‚X1ÏZS&A^¥Ú˜êJ©ºÎÐhkëýµR#µI—¡ÈÚn2 !lUm;±Ó+Œ&sÕêÆ Dgò‡eä!k‹P™«£øŸGÐ+3äQGD¼Ý¼ÛR·dOd<¯·Ü_¦U«\£ñDjBS+ÙéL¯¾‡P2ªb"vJ^‡Î¿&¸I2¾Ò#ÌßãYÐêz¯8Y“¿ÄR’ÍTOÉ©¡5WÆ#žV8ÒÌ÷“Óž²^7 ‘ + FY31f HGkeÓ}85H7TÛ×бÊO‰±ý´?¾Aä/4׺¥õ ÂYá…Ι\¬xå4[š¼­V‘æC¼ÙN±aÊe””BjN kð ZÏöSj˜Ú¶J³Z;E‚5ãN¤‰\ëÕÈ&…f.%
+±9¾;;CðHu+IAâÀ}+ÕDÏ¡­íá„Wì3#»^¯«œà*5÷®]¾ižÊ,[,©9õq±ëKÓï¹$ =ñ/cX3Ù^ ÉWêŠNHÙ‘±ºÉA=D“w!h­©–Í÷80/èx.‰gýûŽÎd†¯S§À  žìýç™Pähl ý Ÿ #·)†tŒ³\Ƚfl2žuÉÎóœóKs“ Öµdö A#²`
+
+”ÎÙþôºÑAõwpI¤õÙe©e}Ø-Z®}|¤­"2”MÒ‘írö§.A¹œ0?è÷9?½ô"¯ît0WÓØô•Æ
+%"ȾI…3ˆ¶ã„™É$žö¤oR†0ýõ‡†×Mÿ×Ly®»v™#ϱtpª:obÉ@—PNl (>·zÆDY«9]heìÊ Ö \ƚͰrZ^ÈnÂn+ z Ø¿Óÿ¶£TÊ~B0êW¬)~Al(k¢=µªðì¢Ú4!Lxm¹k´|h ·¢:y„fÌP²%ÏñÚ|,Z PW—a$zß’Gø»!œS9 V(cÒHÚ Â8ÜÍÒŒ„íª`*3º/uMs@\¥,Ó}Å(ÃÙXVÀÎon†Ž½@Ÿ 4W‹T¯Ð,³¹ÓJFwìË#ˆ… º£-ËF^†íqon‹³€š2·œäuúÃBÀÓdJ9ÜQ
+ضß.h*ùNSß@4Ý,IB5®«^@©3lúœ
+(ûÔˆcQéªgþŽÈÿ- ô+÷ÅÊ¿2ì‡AÛœ·ñê¿M“B¾ÍÝ,ñ铵¯L¡¥3Þˆ:9O
+¹‡bhD'öÅMyvÈÈ äýº‰Ðí©xCÖt±v(Ó$—2|5pÏP—Áþ<@.ö8fÁ¾X2ôrμ)Íà)ŒgdújóQBÅTþ«šæÚæj±¯‚ äRèE·"[³ÊîÓš’†/¤"Ûüq‚²=·ú’M(2w9{\õ "^ó(e_±Î„ìX$ º'ÔèÑ®Mvχc'¹ó¸éžP¦¹@)cÄc&œ )Ùò¯!?ë_ GulªöÜ$2þÅ‚ Ä"XºŒÀ;  “{‚¼“§/ ïGP. „å\ñ²>Ÿo@ŒQ¦¿ä Ií¹ŠìŽ]R‡š‹i[½¹5{$
+Ë!ï›.i@8¦Ul_ ¶|§sPœ?v¶Á¶vÄQª’Ú0˜¸Îýªñ"ø0Staë‚)óƒVJ@SW*´AéÛ$´©v/iX·ÒkðQ6Ü0nœ'ˉ—"D˜cRÍjfoÄŽpß4£Ä¹ì CÈ5Ãíÿøð7¬sp•ç|}”;Oûk.Eßäõó ~²¨˜pGƵÆtXõ?VDî<{ž8c•°I­#ÏøÉc†¡$!Ä£ÑÛŽZžwþ¶›0´MÉ¢nvbÓ²d
+ÝÃhÌ$°ÞO_8²$v§œgË»* m Í3Ž ×"Nk×Â
+þCR[ç¶Ì2a`)"šé%ú
+›Ü7 l[J¡mû¡¨³l…iÎŒ*~{üf»42Î5¹¸LB–Õ£Û(¢&Œµ-¨ˆx—!òLgLØ3H³ŸBÃùíe–¢lK]ƒØc ‹ÃrC?Y.ÄóB¦íÓEð°8n)|©ýPâÀеðÖx>ìàZáPlá.QЃtv¢Ã®§é>vÙ%bQÓÜÓ¾ŠaArgà’ŠŸJìì0á\NÅís0>¬9C–âŸ0H‡:ƒ¢ÌÚÐ@º›ìEsRá—tJ܃¯vŽú@ô4
+D9Æ,ÃW¢ê
+—ó‡v Q†.+smX
+6ÊÖe|8¦ä(“–]NÀ‰Ù0 fTwE(0{ qþQ40çäÎOUè­cì›H'«ŠMñí‚àpGá•Zé={’&çÉiìNó¾&¯–nÏY7 ²ß£$°žDÜÜzƒã£œcögc¼fsü*\2ö'‚‡±OÀ¨©Ë¬Ú9$ƒdÊm‹{J•€è§› !è„‚}Õ¥ áQX‚`ªÂ×0¦%}rÑAv4ÍæhüÀS¡§ÔŒµG?%h{ýø¹Óê²CÊJ¢Sþ¢9TmÐ÷ø‚FÜ(½‚²óW”…
+F¯F=”lª•*#cñî”^ÄOx§¾mZG…µ¬È§•L!d²0Å/êhÂÚ]ÓÚ/I2Û8­ê£!¼¦úwýÑHX‚;‡»NRXDÇKàs’iˆ{Œ(}kÉrêµH^}äÈi5ÌŠ˜G¡ìÜh—ej°ÄU§}"v4‡†ø'cæ¢>2e7ïÀ<tƒ«ÒþžKÑ5“ü¤¿Oö”öÇZµ³#æÿ±"H˜Ð˜ié «D$`'Ù˜ÞozÑ…Ta•„_“±»GYh°DŽŸ
+F†ìÌò¬\Â2â©y æ {Qèt9
+‡ïÀ)"EÝoA› ^^vŸ]ÀŸÒ¬÷ª_Šz†‹˜$4SmÕ¤ñVÑÐÚc{ ó(^ŠHz€SCÏ@Ç÷sy¤|„g?bƒ|õ}¹
+Sj~
+|PÕðØâcTüþ¥nSòèƒ-,6r¥7ä2ÆßƧ{ Nx%~ÛÌ7ßïÎa˜£Kç.á‰Ô `#;fÞ#ãØ·Tf‘^ÓÐÓ†E’ëmD"ËHl9äÈR€¥è¸(°ÃL÷9xVxÍ–
+Ô³OÍ*ÈfPã
++×
+¢_Uç.uþxÊ ˆ*+V¬tÜ—„Q :­Bùve{QŽG–È®EÏBQÝ´JÙoõ"OÁtÀ{ÑY×u¸ÍVÿRôý›
+Ÿ øjtPÀÀH„q&TJ²²ÉoˆY#å ±Ÿlàí9ßÿŒ—=rž9„Oà;8žÀÅ_ ·fB§{‚©Íäpï¿O <Ò˯Vr2#·I¾@£»AìU˜ˆ×àl|)Éd|¿LË€)q­¤Ëÿþ
+ :YãLÝ&BYæE5'ÐV8²Æº)+£güô½câk˜¾¬n?ÂÀRªÕuE>Àé:jSµnìg³‰PÌ
+4-¾ˆá!òKÎ= ENÒÉá˜ôˆêy~ÎKVÚÂïÄ«Ù¿ßæw^
+¡]‰"$•]‘·!`Xÿ¸€Þ”õ ²ÕÚƒ[ÆD<yU|:3MÎ êÆe2›.“†ƒÉ¢€V[þU[)£¤(>:’Ž—L9:Î,ÓlmxÓ[E¡ ˜CŠs`½O)Šülæ?ÑÌ$Ug»R½Âì’.LêAL©ªv˜¼!O³Îe3j—i
+‰¤%'²1›OQVÜ<!)ýÀIy—œ3¤•w*Úò¿oHe¸° ËÛµ¦ŠafQìæ1`5tïû ù[WÑYEa\ïãÅ9¤ï¶+šãÉ ÷ƒoš´€åÈú8A´ìþÆq+Ý$°Gužš¸ê§ƒðBèm_)ÍvmhÉŠ"^@ WšyG¦x6
+V1·¶}‹Cùø½)(wG°Ä¦½$Žêäγd§Š
+MüQÒ·ÚÔ Ó©­†€ví…B! ÏöT Â6µr{"â"¸mS>›ë‹SøoÆŽ«¶Âè)Ofs”â ‚nàèì8纔q¦ô:ÏMÔªF­Çs ?ˆ ‡L…™ št÷0dŽË ±2 Ô‘=`µë1²ÅYñ
+¬ÓökŒ|@'RKanH²ÑÎÑýH‹Ò¡Ï1À¸®iÚåGtM•tu„a]‹‡ÙQU~‘I{› Z„£¥W›ŸÃ©¨;TZË!ìt”Š5”R9d•¹ÿÐWo…Uè‰OÌߥK^™vŒŸS‡ž»š‹$E“°]Þ3±:k§ä1í¦üꉸ‹ÒGÌÏþƒØ)ò×êšô
+æÒ²¿¬S&8ï"2ŸdÆœ±ÄÌ"{÷ž’ÖD*‹ Žpš®…Rë¤W×@Ye=f˜ˆeGsÛCÊÙy0WÖ®xp^SéÍöoöU¬sÍ•
+ Qg2íž=ͼeæc•¨WyuR²?³sÕ§D…NP[Â;„[¯’3$M´ù¼çsÂ+N-k;z{²£¢¾[] P†„=‘‹ö> 1º:)á!~Hf“dÌ`E8Z¡¹¶X0­†>2ñ„Ù×a„rçBå9e½ëã(ÒÚrC§;,Gâ{HñV›L\q½„8ØÄ!Ã|Òj‚DÆF-ñäjýpBR¬ìq´é®Ûbà–S«Ó.G´¤á+i#È™m%4ŸMƒPÃÏïñæHRþ ÌïP ù#»æcíøƒÈÐç°)NS{™kõB%) ­ýªH«—T ¸òð'ÃKúOS¹¬­®$ô‰ž`”* ßÃÉxÔY7ñHµ¶'YE8=]b³ômñæ«Ï”N¥…00©6_m¶ÚÍ*¹Y÷sà,+
+áAlŠÓ²iærˆ¾¦WÞQ,>œ"`nÐdžéÕ°ÒJœbž¯j—¢`k#ΩÑ[Ë!äô)Ó<Fö
+üß@úû[ØF4¥|¹ÅØé3å£%>›OY(¸äüTøŒ—Kr7 „O;ø.¾kg©[d+Ý›¯ ð—5ñWR¶Ò’@£Ñ­ *Š
+~›²CVÙyyp[#Ö‰: ϸTt!z¦v
+ ’,÷ÝŠ¨­l¨®¹¼¨vè’—Ï"Ú†
+õv<ì[ ª]d ØŒ\ß&ãº8Qj¯òRu7>q9nœ—C€ù¥®å%Pëš5–Î˼EE>ªGì4­ä¶©JÂirJ¶KTeuDjSãâG!E¬û¢õwþNG0±“ µ¯ü…b‡.p_¶x¦~ŠítÅP– Ž\ÁÍë«ÕÑàZkþ*ÌÑ2ÉA¾ŒÄÀµOùÅÊ7HERøúpÀÐ"2/‚QN I’v4¿îh7e§qÆ…û„,H‘ñÝûâu­O»UÊ9MÍÍðGÉݤŽC1,3Þ“thGÖ
+ ™+; ~þúç:1½®pÇÅË~s½Ûaß]Õà8VWÐÛ§å—Ùéò8—yàë‡ Öâç‘Ô»Œ±aƒ§z`ìBt]@LŒc÷„I
+$ ìšõwÖ
+í59µ|€øQú¢³¥=}‡ö#–d¢°¿3Wý¨~õ–ÓìÆà":^8H]j¼xܵu•‡W}bH‘Ò-ƒ4 ʃ(5îŒu*U3p„$ BbDe¾¼éiQ_ZN÷„ƒªæEœ~—H—˜f û›ðºèÛlyúèRýŒCCÒÁ¢ø³)!Ç™ ¥É’¬áô£ÔíØq#Ûêà†±­8Ã0÷^@:c„Ž€Ì‡³ºöI0HÚª˜çF‡ÎͤYYMíîhFTÎò<3=âcÍÇn/÷ÆÐ d ,&Ÿ Ä„ÔówX ”¦Ë½804gTW´\‰Â`¥ZòÂ0<dÍQÕBë„5­» @0ßÌ<×¾Ó¨\!½´Ý'ì]Âüw¿
+?ikíN׉À¡„H¾}ÿ ÄÀk…€#üˆ<•µÃè'·×âeL«ÔŽ®;b¤o¢ŠÑ—%û‚†kz>}Ô tÏ![»ouѱ/±´Ç"›ÌA’¯ü0P™1ÁâšÕDMɉ,û²ÆœÚ¯‚ ÈÉù^ ]ÛlÁ ÷Ú ì¸|€¬¦Bi®Zvç¯ß lÁQ"]ÕÜ»Xð(„ˆu`^€kKÚ-ÞÔ™F(ƒ×ð¹•'.Ÿa­RXOß2ÇIÚª¶‡âúz'ŠÚÂnDßÚÊãibƒ/í‡i_uĆ.‚ÌÓ”Z·ôIÄsÌô4ÈQd6FÊÖÈé3ˆW–ÈuIl²›µ9/.üâá¨`ÍÆgŸôg~†§ÞÙAΕ_ý°”> „1値Æ7$·KÊheóUqÛ³K§A¨"FOYœ¯¼\‹`ˆlëê€Øó#÷’x€&Y„2áW¦ªÔÛOÙÄ*w«±‘̦ùáÉ!;$ÓPŠ™Í]!ûl í͆m®>¨6A­t^våwÐÛH¹¢x…e´HH&Ñia_ˆÕ
+]Š[M SÓ"wÞDØ=Í_´õ?;ˆ¹€5’èß‘«¬H~)Ûw04«{õ瞆  Üæ4j>¸n§GΪˆÂA=ZóÕ3>?,{ò¡%ÀðDƒ4I³–áp©r]ì0Ö·y(@’9„
+2GpéÛúŽOF½†š"Á,Ág¢D
+ƒ+Êæ³îø7-ýµd'KRß9܆_¬Í‡ƒH»pI–þ ïjüE–-égÕéèŠhêsŸvúC/)#'žòÇjOAz"¶¥å‡ïT¤[9¶l'ÊÖèLöÿŒ—K’7EW =hìAÿŸaGM=õ
+=“†½ÿ>—_èe"år‡ÑU(& \\Ô:g*„¿ä73î1ÉÖÇFË 5ïŠÝ‚<ˆd3˜""¤·/±?2Áh]0-‰N–±=àãw†x·fg5˜ÇhXh[ì`Ê`‚d«fß™ð.
+ñc¨r&KÝ|pþBP·R0¸i[fÝeW‰uVù{Ý=Â>$žM
+¬É=…‰Yµksú˜‘µ4àC"Óó¤ß½ûÇ §ûzogøe0€YLS¦} 5µÑµf/b†ÁEËõ¦…pìZbŒ®Bn@£Ýèó›äÕ›ÁÀG ÀíqG¹]ÚïçWzô7ÀÖ° Žúxì€ð"SMX› Âé,8}.®<›Dƒ§rBn€MШbg|{f° 鼂_SÿæbBšŒ¥–¼½ÌlÍ|ˆ®óW³>¶tlíW°Ñ€J.aǵ{eˆ"OŸ°#Ó<ŠBhßòDØX¤ê;´)=Hkã3CåŸé©X.úo„D\±Þ<x6}DM›4`†æNU„k.¦>â¾6!¬^4u„Áêa”
+}ÄmH¿KØ„DÀ$Ö1l„_ Û‹¸ö=Æ!l/è.Å$¬ í"6EÃö9‚7,íÝE߇%UHðî0¶C߇ܭðÞ°ÔÉxW¯”âÕuC䎕Q±Q/yÑ`dÆ?!6]Àþ
+'ñïÑü±éòEÚ—\Ä&„—ÊÇxBì[CÏ÷,1è†Ø^бeu,_]6
+%ÐGl/ ”£xº„õ´Ò
+în@Ð
+Ã3~ºAIW¾>0À¹J1X
+Yp(OhFƒÉÙ˜ù\¼ï—é·*Óx
+A¿zSŸ¤µÖrˆ÷‚Ù´BŠ;ˆÙPmß«µ)Êë²ß=±,F‘ì×8}*uöìE'ä4*ŸbXÕêÓ9°ldmìRód×)Â/³ft¨é®÷¥KæÄ@è/ Òpô8%M;²A&|?˜Ø»t@ªÒ–Ú?‰öB.áØ`šö$Û˜0ê6(€¬õ´ƒªP{¨Ûl
+êÊ
+Qš(¸È}]DÃœ"+‰Ö *2  › ‰Ë×¥¡À)¤Ö¾”´ØQúGÞIt‚X8k@ÌÈ£Ól² ‰4ÕÒ w)ÍA3G
+Ùð#º¹Ü“sSÖ=ÃNÈ[¥~|EƯ ƒCY 'úãU·ó~=%V½9ÈÛX"ö@•Üsd+YÏ'ÆôÚt©•jÍì­B¥ !Úͱۂ õx‚`¶ÂTÆœ’Õi³Ùð7Ù+÷Ê;ãIBÔ‘¯—ýsàðAÙIGJÅ¿Ì`‚%”J£·öô¨“˜DkÉbM…ðl?ÃNÄ[¡>¿yA—Âÿ|Ò²ÈÇxÊÈß½ã<…aÏL£+†ûò–§ÇÉÔ+çå— ÂfÀá
+LXz2‚ªnÔç;-jN &Põ×CÖ‚P±7ÿû""ËjX¶JHË€!ÝO…õÀTÝÃÂmB¸4 ÀN[Šƒ„TQl±í JÖ…¤ö9‰)IêK^üCHqf­r=ï¦và×e¬ÍóbŸjL¬7µUw÷œ®žlõé2âÖÛ9=?jÊRL~nÇ›h¯êgØ y«”Uý=oBÇYr­vã·ËA‡áù­i-ôÞD;dB!„pÞô–bॠcQ–iÉ{ϯBÈ.@ÌßI¼×éb_z¯·{λjÜÛ¼«Ï}ÔUÅNr®½àdøßzês·…<çí.œzZ¡Ö±ü³· §zŸsˆ÷_ïd®ÁŒaÕ6;z7xäiUt™ÛÜWw‚0Z-)´¯º2³¶0×h7³cAÚís:³
+‘ÑÌAƒŠZ·Lp5 8ÎoÙžþ‰ù^(U˜AnÂÆâkÉ“(\ÇôÒªWFƒYz-2¬CøùŽ«‘ø2žZ·*â ¹dô=R±Î¶äâa[€g2ñÉò˜ûœ7 
+Ã9yØÅ~Ž£&*h0qº}ƒlóC·‹ GÐɉqÆ!©5ãystîYóRoØ~l˽æq
+Âj}úÎ$¡
+d¿@…¥Šäæ1S¦˜­J¶™Æ÷cfñÕ„
+Ù£>ÑNÊ[yZTƒFÜ#¦•õñsÇ¥BisC6’Éàö§]ùÛA .2Ã^èÛB¢¸*Ï-± gOJXôíj©–#ÃèàRbr;¦0‚ÕX‹Úp¸Û*œ•@bûü;Èìéþú‰>bR9_{ø ÉبŀaQºËÙ $ÓiïDSÍPÁ}L­±ìµæʸð¢â×µsVL
+7¯, Äätøríí Sñtm6îÊIã,S´•B‡Ðó“9y"ÿšBìN2Å
+–fS“e½ê
+rRh°GɾyÖJ9ر-+R=¾J$ ›4È,8~‹£0ÇIN4Ì7F.Y
+ ³ðý
+•N‰ýFkp[Ù!T‡éÅL­ö&‰V«,(ºkÈJ‘°Sµ£c£[ ;‰+H,ËX§ð$q\J­:„L¦U™Žˆ/ç.tšâÜ&Ïý3­3Äp®l9{A„²j—©k@¸½JPÆÄ!E7Ì© ˆ"Ôšì†òÜÏè0`?NN Žâ[púÖy¡?-ë²ê¿@,n<‰±Ñ»Ø*Æ67O@°°¸.º.7Ž.ÎçË!l=ªÈšÐ–øò£0}årý#¾ƒû`,@yê2Õ0yüÔÂ9D.ùÜQ²„‚ÌÌ+;½C®£”
+Øl
+jöð–¡ØAÏómì³ÉºtÕ§W±]à
+\8WÈ`â˹ÆÈK¯öQ/ úÎú™ò‹h{\ùõ6@Š[ñ%9½
+H•ËJL »fõZ@M¼É;¬(àkòuU\§^½C‚¯=?~ç•9‡Û¼3ðøªw&ªó>‡ÿ4WîÔ
+,Úg+¶î¿Y.+µ¬=‰Â tXÍ÷ãNÊñÓ¤ü‡\øÏÿþãÿñ6ñ`4ÏJ¦¡¼Ø9ª–]DN &ÅR¤°j¡ ¬–äÊ5 —(Êk:£I±È»2æ}uçËâhœèÕÂ̱•xîÒ©
+/ô•µÎ8]äÔ©Huëøý;7ÞÜosãßéU7ß‹s›†{‰ÿzª\B ©žœ•§^2ËÇ“ep0>|ÐÀ¥ñ®„ü]Ì
+\1›Þ) ­èj¬cj2•
+èøZó!}A.(JEûjGS'S`äóû6ª]N%­ìQ1÷ óW’P[H;4ÞG¡L,ô»:Ø EâÃõ3@â öÚ5HGUŽi½!ôŠ:á(Äê?c|©ç:ýÿ¯ûÿ¿å)\žËÀµº#N[ê"¤8 `HË”
+H[yi|‡^ÂÏžˆFM¤Sëh†,¥"°ð$ë›U–Å*%¾C¨×j[N-ãG¦–x¶Pൺ¶(v1.4ÿÏx™dוã@t+^ûf\5ô.j*ïZ7@NÿÇïÔÉAÚ2ôHhR ®_`ˆá§kÍ–`dPbøÔå ò_Þ™—/Ê(1ÀönäIlÂg×0Ž¾”ȳ »ÁƒqÔKv,kûÉaøßYL›\²Û,LgZ[›è š3_f<BáÙ dPó‹þñ;[B²ºA^8N
+ûqÓÓÎÙ|Fy£ Ÿ&íçúÀÝ|ËK‰£¢©ŸÚ»Ïí;ü ž5ªî£ˆ5‚kלíñ®IiúÃäŠæÒ±X_„Ãס÷n’ÑG©§§‰q0E3Jrâd¦
+öR€caH*×-F‹ÜwòÄb~Ö|zÍ’bW«¢ÅÑL¼Æ ÑßN5ãß
+¨^Üáÿl ¤QFmÕ¿Ó3\F”`«}’á7¥Yo<Á¿M\àHŽÆyJ%Éäú…Å~ÙиԂŠ ao(“{²’•öµ/9î””MdQäòôž%1>ÂèH¯ EÎÜ/´ ¿ô!¥²B¡8¼Vî’q¼©@èàTÈ­º|ÔÐx„‹ªÅ›ýRôb‚ù`Ln7ÃŽò¤ÿ¼ØgÝÕÇE0ڸǞt÷ žùn›„<ñuñã
+qŠ%ùnÃc¿Ùy ʘœYnE¶ƒÃ
+áŸ_ƒJ Ð’uJÓ#-.u1Ýf ýQÄq4Vj–»ÝªH7¬ÎqÈlLÙ{CÆ”YÂF/¦èê|þÁ÷¸8!/`À›†tåsWïô”K…„ï
+Ia¢o™a S¥t2©ÓDëïß
+‚‘?åÞÅ.NFý<þpÅm)P ߇Ùé!j­g…ÁvÃkµ^¿gÓ<T†>ø9Jeiêd[Í­#²Àƒg€T˜óÎýD ÆP±V
+lëd7m™eNÆ1/¼áRtJ`æÍžÌ]"oÀ͵©›­YÉfÒΨÀ|kÐK‚a#ÉòŠ)>X¢ 3›|œí+[ ÄwϪHpÊ)Œõ©`
+ÓàWFÓqò!ö»ï¢$ˆ”|¡·ƒv¬jšËo±€?} ˆaäìBÝä°D ½x…XRŒžJ¾”øIX•,/™k{÷þMþ ß—Ù§}hL÷‰ãÁ&˜nO˜¯Ìs(.Äž€¬ªé†)(SŠ¿eœ×NŽbsûSÀNÃR9[b|”0œ€]Ò<yRÈ‘ÙÏs^j>o5š‹¶.âùŽô"ßCª Ï…²@p¬Uݾµ>Üè·’#ЬB.Û_„ÆPš¼¨l~ìƒÓC9~
+möÎä6¥j°úvM` j¡Q8P÷óUt/eÖ8jã”jù;¢‡k€çr>#ä±/áž^=ky—àÞÚ~çùiÖÿ/—$9R ˆž`îÐ'h# a­¹ÉlK÷ßê9D–LI¤¦L¦…ZÞ ÄÇ?t–!Wãï YòMåßN •c¡a†¾rƒd q”?»e*?Ä…¬“XVô=•ÑÞÆ÷´3!t¥“>z.×Ò!õt‰¾œ~ùLêÀÑÎH‰”#ÀÓ•‹(n ÐÉJbÈ£oØ@ü•ss’&¢l7º$¬ ò…sÜ;v's´‚Ž3Iå·ºAþPÓ×'’»Re9ñqIþp\V`­Ô•)˜F‰…íÑtM¹ŸBŠ<sØñÒeÏ;”EhŠ†BýD¼0üf?Š”@Ü ÙQæwºÅE 8î,:nëÂA\ÜAëç>…9h4¸¤§¸¸ƒ‚¸nqQ„ü%åý¬éc`Ä
+¬ki—RÝ£†‹¥†(š]m‰*?i]zEF J*œI÷02n£$Æ 5¼Iaà‹ÔOAç`¹´ä‚¢.Ðí´;æõÿ ê[ Ïš%À4îzå*ìx X?ˆ1âJ–ó™Є3àJöÉ,$°"SÞTY\Ée%ñšÚÛŒ-Z £"Œ.ãOð£ EŒØmÎ2FIåΨ‡<¾8¤dä¦â™rw{8k¢q‰k²:VùZ,ÙÐËATpõÈ0 +Q±÷Sý€`÷»BÖ:~z%H‰IàÕ+ð0µ¹÷T4zëé\éiþô.«^+†l4¯2 jÒ¾eL·P°74ÈEÃ*£×£
+ˆ=Š¿£µÁÂ)º^fšIÓ{²Ú>¦8L¼Ôt×ËT’¤:¾=®)Åø#¢ß1b‘ØvfóÛûîݤÏô¶Å‘>²ÀŸù8ÍÉ9kluUV²ŸÈc¿¿p䔋‹ÉÆM‘>Rd^JÎã¼D‰Z&~“…ìë;l+ÁƒíaìÙÛÄ'@I‰ÛVÚÃw˜ü-ÛjH§_†ÀÁV«¯°Zå4~3ˆF>¤p;®hàÈü¨‹/|„— §ò"óD1.[”±#p¢³E^õ£70A_ýÜ@,1Cyøs(ïõ
+Šf¢–È v§Éüâ¦]&;IE>ü§ˆÏ$¬ŒŽQÀä܈߇В ËXH/«4-9ññŸ²JÖdOÿ[G)¼2ÙÈñeÖ V¸{\Ùt Ö08ð%U1‹-?–µò7Ó‹
+s9ÒJƒ"]Yê¤ìfÁ•@³R€ùnÉ(ž9ÿñO
+H‰ ”‹;ŠÀïv昱±3¦yÌû™1ï7c’¤))*•¨ôð(äQ“:È[ŠÂ’’¤yÌ#D¢´÷l;çÞ{¾ï~ßýGîþ„ßïû~?«œœ|0JŒ<®
+º\¤ç†±À&À<^õÇ
+5çLLJ\ãU„l ÑV9«xO€2O3S÷DTàåiÀOPãÖ{1ÁlÐiom[`ä«Û ^ÊÆ|—õÎbƒíg¯Š¼Rפ}Û¢Ã?ÂBÅôT§í!|´]Éo:–°
+ÖÞcB¡/ëK<uœ"eCæc«ŸaýP t,Õ~lóryViy¨®=ë‘ž—÷Ä4÷1‰€ãQl I\«`bµ=UÑ·ºnæÍÊŽº”“lb¬ER}4YeŸ«É5Jêšd¤aû}«u1;›@ù[Řpƒ
+дä¸_«À·«\Šc¹ óUÇ£8V{–z†YZ ü$Dù|ä=Ù@žžN!¯×JÕ|è‹ýQfñå‡á¶³Y.|¼ÞôÓðÐÚnbÐë]¨y’Žú®ðNfØ mÜgGT8Eú›F áVI}Úº°*D@Èt™"æ>¿Öp°ÛBÌ˽~”ßÑ06d£ òø@€ô±ˆ @‹œ’ô¾&Û•S’øx€ý{·§Ý¾HJ2OWGŸMS"RÔZg©×®°*HÆ? ÜS°‹ÂOäL°Œ v#$þ¢bæ:/0óŽW§:/5¥¹}^$¥vV§?ë@¿r½R3Ë,rrÂáÊww¨`®x¹Õ_î¿Úùm[âs>CÍê'½q¥„],°ßí`ƒwûQ
+Ÿê:ÒÝ–›“ÜÏeØäk-³h¾ä½ÉGEýk—Ûð×n;ýj‰ Ú"
+Ü6E|
+ôîçž„LÇtë}Àó Ÿç°Ù¶9BÖ»ÎÒkK]ЛîE>Ü«ä!¶§ÚSÖú_ǪúÊ®ÛÆk’¿Ì"3O–ð/ÂZV½CF+Þ£îEô<œWI†ìL½þMÁÈ»ºÆ};OÉýå%ï²cžY»6NÉ|‹/¹dD‚Î lâùmƒhȯ> îKjþøÿ¬?ì=XïZWU@Ó ™‡¹.»Æ:Ô˜°Ê,¾¬î­H\ë+›#~RöÞ°Í`Óg%±jnùm÷"¹Ü·L ì}aââ#zvÛ±ªäÕ^…7„¬°AÄv¯ô6J¨¥ŽY\Îñ,2õ›ŽRVSËœ øL·´ã¥GÑYXã¢OW™•ãUñÛSØt’YéQÒË".jöù }Xç*¹8°56Üœ’õ}8×ö©ýmc¢e´>aGŒJ h»€œl6ŽÔ$jù7‚Z&Ú·Ênt}Äe}š„ý¾'†?p*èeÇË= þ5Q×é2»aOŒ}æ–Rª
+uMRÉÁ¾©“Xè40ëöÕÍ);Š¦$ß2ÀŠ™ÇÝ›&–fÄþ
+£rÞúWÄ}Nkm|¹3\} ,Ú’=Üè-Šù¾Å]lòÅ y¸’‹u‘$°, ø¸¿–wOR?:”älë!}
+^Õn?.i÷öh”í™$ù \ĉ‰Ýj:/à§IŸµ¬ÆÓÔ;Ϩ 9ͧzzõ©ŽPìœÄ¦»TÄì#5«l 5ïú²þø`—|¬ 埪è•n5§Ö&£ä®uÔEïàr\óBŒuœ”·$®4ˆ`Ïw¤°w†…þf0OfZ²í²šxë $Ò: î×ñ[Šæ”Í®Òð)r^˜†^aäA_m÷¢ÎÔU³»a97¶{qUÿ3‹ºÎôÈŒ“)x¼cþâ@…Ì:žÃY¡5œñ¥æ¾Ò¨ƒúG·¶‡»#g-ó«Ã¿£Àfßš¡äÝÑ0K[úš2×°©ÜÚ¬[’šô›ßW…l¿ dy}Škº.qW{îÔ r½Ë¤w™Ý|¬eÁ<ólè9è9M€ 'zÕð¤ãIXÂvoÁoëà0ß,®ìÜØÁ´ŽÒr
+bU4%¸æÈÀ?‹¹'ºv¢}š_oSÒ?ìŒR¬ý¤¤})#ËÖÏÌ\^òÿ~wI.ÿe•Oˆs¶A\£¬›Ýä=õh‡‹óOK‡ÓüCOsÂæ
+´t)Ï-]c>¼ÖWóÜ<Pô…BŸÓ´ÕŒÛtŒÖÅy”ˆ4¯ Ì1 Çã|膄‘¼×/(öOöü“­N£Ð$¬ |°8hær–NùÑŸ¸¯eTÙµ4ˆgÝp¶Ô†¿Xâü:v£}“~(mxæŸ!ä]8(÷,·¾Q|M†* 5‹HYçêÁ®s€h—7¾¶ Ã^yfZ¡›ipSÒ¾Þ.8Õ3êOô”ªóU>ÿÌÄÂ_¬³°žù†tûDíë4vO^÷â@‰ÍëªËýu°©ô¾‰‡{æ”3kϵ‚Sµ°y¿Ÿæ%çœM‹±{#¬úQm¬k›ë™Âæúh°À£þl‘¿ |6ËJ¿i“!ß;Û0â
+ÒÖ
+óî@IÈ°I‰ï9ð˜Eüñf7>c£·%Þ<
+þ]ëý¶òIöu­³ïd¾1Ç<µ-+ýÃclÉ Z˜ø=&}gŒ]¤“_ËÈ ·±Õ×Õ…ÿÁä¤]nHyRú<” Š¸}tãêÐýë?^‡ß
+!|xõ³–]}ß¡Ä€ÿÝâпY9t—‘R¹8ŒIÐö_j$˜˜y*BÅÄ„K1 ¿ak-ÀäÜw!œ|pˆRò§|•’û×eBÉ_—³À±¿_êm*Ѓ* [ ãôB÷,³Î6JȲàSíãœ2ó·@+$ÅÉHèÈA â¶ÿ™ñ±èJsfÆ¥ÚÄW!lpæ^qÆUBNÒ•ò„—!™DƒR"¥GßA“Ÿ„H‘Û»Rf¡{Z€<ÖŠ‡ó|Ô†Œ˜º
+°lW,‰ÛÒÕôÖ‡²VX¨ÓpGMÄ>Q¶"Ê›kï~‚CnÂ3/=¹wz”ñûÃKô¢Ìÿªh5‘ËÝèT`oÖ¿AE»(°u)"~ARcŸ&×L"Þ®ŠQ6Ën Ó²K|ôSÿÿ—ù;ZÇßHB²‰„ DVû.A,E-‰]…È"©-Bh„Vo/ZzÑ^[[UE[t¡Õ}y\:îôÞç™™gæ‡ùOæÌýáüzÞ÷|¾ï÷{ÎÑÇ?ÐIº¤òµ•dà 9)ø\!ßMŸ™èÙ‘›D°(Ü_NòÏ·¬¦³›ñûÞÛ‡ÕÇ{ÅÇU]z7eY·×=¿Ñ•òÄÕÿ`¨'f­¿E0o.ç k²hòh|CF²[" |:ÒCÝz
+2è³íE¡«ƒU)Ƿ۪η{µßwŒ¿#?^µ–}ZíP}߶~ì vœnwW½¼Ù$~<TølÜótÔœàÔkâ£1Iñ˜ŽÜ¼>+o)Hóì.Ê" ¾qr S%"èóä¾ãê|ÂÇÅγmGÇéΈõãê@õÛ}ÚË›5ñï–uÙ{ÓmQ¯nweî͘âŒ4ó~ÖUgÍíB4+ä!u1¡§$Ï+GÌÇ2½=À-:‘
+pÈ$÷c@°¼îèërÔ)Q˜ÚRòîH%ç']Õg{Öƃ»¦”ý;ý…›ÓŽì»ÎNá5£šèh(#تJp9阌È`4«(G‚:>R¸  û‚R1á@$àAŽf993Vã½]/Û½ÚvßÞ°ã4„¿šéÏÙœ´'Ívurúj½tÅ0•iiP‰<“} Öß2‚9ͦ•èX
+WÆ,‘× M¾e¸Öì4L«< Ú2“!KÀ ‹"¦P^@öÀAè<ò6~R« ?»^ñå¾>óíJSÌ—õöœ×³­²Ã›í k#ºÐ¹®fšK[C4¨òq—2S¡2^y">ˆ™4àS)  Q „Bª—7pn圄¸Ë°†«ì:l>qþí©£óÇž£ý?ZN6:Kß,Z¯™e×õu ƒ2ËÝ”¥t+Š1ƒ4*¤p¹  CÜ)À%‘AàçtooÀ¹a!Õ+–‰¡·<‡º;iŽ{ww¸îóúôØéc×ÈÉæñ튭ôѵÖðE{#{´µŒl)Q &rLv¤
+¤%Íw¡$šrœ¹8ÃçÅ-mÆ·æ¬Ãûí¢Óz£§™æh«õꮮÛÊ4¸ºÜ"l’P‚ñ#Q€†ØÑÀgq![”€3—T¯é[ƒ7G.§ζŞm÷ÔœîŽ×®4¾XmÚš›¨º;q%iÝ–ò|·¶%¦%O‰)KM —2´2"# .,éæ˜0 W&Âd‹ØŽ¢tê¬ñ{Í^q´Ø¥Ú»eIÞr™%ÛÖ¸í©þ”[}ZΔ¥ÍÜda4ÏT©RBˆåC0ttNÊÌ?:°©Tðq÷6‘ ÉA|hÉ(ð¾®U‡ïMåÇ¿ôä/ôü2¬^·ÆÏõ¶±Í•ž£Úz⨾ŧ½ü"^!•@(3
+ âP¾Žæù´¥qßÀh;2‚¢#ËèÜž”nY¸]Û6!-dÖÇèˆàïlqŽ)šÜšIŽm|œ¨ˆ9˜3B¡¤ˆ[‘ÉÌ_P¹¡‘mrQÌ&cv1çÄtŸ.ø³E.=æâ3jjÅêpoÁ††ùa['@ùÕ‚ö!q…mDñÞ:Ø_£ŠÌR)ÜÈ2Ôäî{Ž~iµA¾gO'„‚צ)Yë†UDŽØØ؈•Ñ¼£§TFPÏ"ZBÕ¿ý öt ®Ïâò<­aVLÉ™¿`F©¬ÓªêÀÞÎ[ƒ 0sR0T4Êàüâ çø§¤-A §aß‚¯ˆº)ŸŽ=|Áá’D]” ,*&NW$CÐx¶Ø×6˃z<Ü2Ðþ7Ë 9O-§áj+ÿHokû~T(/U²Yj1ïïJë‘œ%L5ö3^­k¸׸ýÔ NÄ<Bé©_®=[“NÇ<ðÄæGè¨;¥am–X<íŸe€U`W
+wuQ”U+¹.æb`N=Bñ¾ƒÙ~ì€r·¨2Ä—•úóep(îJÂN&v×Jª h0/·´]…úîZˆš.–ùÜ—ˆ³Ë ïқ㠽 g‹¤–]+þí¾ÖsÉúŒαÛ¶³q[&6.lÑ]zTO¬ŠL÷…& ù)RѶ¾Ÿ‚.8'Âlë$Ý[³Üæɾ²¨Ý~æäÑÌøú¬ð¥ËJóù’lüØIÇ»i­VrÏ
+s-¬Å>>_ë­Î7ÿÝ3ÑrÇ$ªºZèzšwi|›®~ó"bæÑ Ÿg,°gL™uòÈ!í`åŽôÝŸMŠšo’FBÇÅñØRj|qwº¯2fdà ª–Š~™òH
+cöѾˆ‘^ÑÓÞ%L¬.ËLçÝ]iÍý‹€jòN­d¬J^RϨj1c:Ü“Ø6­6eZzSÜó0ü‰Ò^ðhæsŽé…Œmj6e‘ç;þyº„ºÞ > ÕäZ‡†Ò‘ÜS
+qÞ¡Ö@6/kábÏ6‡ß†õøW°_&˻Ƥד0
+“FÎ
+¤ÆPÚ¦”•föàÏ“;qïø|Ê31Ÿtª53ð>nbµ§¬‚¡œ[Ì€÷ù”¯çcKWA™
+rsI/ uq@é…½Ìþ”™Þ˜w°Qp@Ì-\\ÎÊÁ~=–M~ Š%_ù¬¬•Õ\$üä›Æ?HèùxÈ£ú˜ÝWý¶ý™C™
+i¶/CÊÅÿ‹•‡BIÎËÆézîå|ttÁ§œ‚ýÊIȧP–‚êU["™gÉÅ#d,̶³O}÷ÎÖº¿ƒ\≖
+.’^$·™­‡GÉÅbøD¹T ŽÍÄ
+4Sr.æ93Nöp» vK¤ÅÅäeP$*úè}ec äe†–°÷ÓF>ù"0±ž² )aƒ :•²¤M)Ž[„#>'Ë~ ÿÚG“摺r@"‚ÜJÊÂhÉ:é'[öªg‹.¤o¥ñtýÈ;=p7¶E«-y´Ë}¡èaõÃn6&¹Ëê‰é™mE¿¸:”Ê£{ø713¥MY‘<µ—¶³‡ÂÛ}?Æv±¿@>&ºx8Ê(‰äQ#uº=Úï+¦A§€ŒÜ…ÛÅÔ¢K2
+!˜wPš .JkÞÅèÞà4ÜÐ º+@ÿ}\OoŽ‰uq+µ5a¥6¥ìÔú‚—…Š)w+1«”uJ9e¿(ûÙƒEµýh¡åöóþŸlò×ß]졬]Â<ßdÖƒ&êê üä–èþçÐñy`eäåéöð«óBUÂÄì)ø…Œœst çQ¨³Þi]á³j±p$Ÿ*‘5 "zpmäeÆ:Ú[òñH‘mú[Ø%ä2~Þ#dücó!õöå‰t"ë0€?x3þÉÕ°YDH˜þ/!ûkˆÍøârþ8–Íþq,™ˆ™x„qJÕebõí+›\}‰äçœ÷<¹ÑóÝÉlÛ­MÇ7§¿¢+¢[#¯×åw¤ÿ’Õ=%‹‚}éÓË.¢§ÃM)#úiLÛvçw­·i'‡tu$Vå}bÜHmˆiõi­ rÐ;sfjWx“òßȦ ^áu[£õ9=»%¥¥VGV©/Àd¯£ÖD×Þß?^èøGL‡®Ó5Fwf‡‡‰ì°ZŽµ”êã5zur[ÔŸÝb³6>)nº¯¹WòN¯_ ¹öˆ‘¿"«5j`4œíŒTÇvËcJÈ+"™ƒ:”c[¥.»ØïËNJ=dÅV&ôøçaéç°–X™0qP¾yê‹”YÎɻ՚œ“OÈ z±ÐûRÖÈ•šáª›¡Ur_ɯžƒ|ª‰Œ‹Õ ZIuˆÿÿŸâ2}JóÜÃðÌùr>tši“é’Óv¦=i›£mS£©ZÍ‚Æ ÷ˆŠ²ï›@PhÒ¨QãÀ”\pCÙ^VA›“äü)çÉð¼óÞ¿ë¾îa¡Ýÿi_3€ÂÀ®åÝi÷ elKñ<Ü)bá£Ú~X`P Ù…`çq^5¹ìLÃíÈl¾Î‚­—²Ë¥q³˜ÕÓ^%îhßXaÖ¨5ŸM÷U¼ßYxç“/O£D‹ƒ®k>Pá‹·_£ò’.5¶'?Ùbü+,Äû}ùÔ»ùÔ‡£¡‰÷ÇÒ‰ä&¥ýh ›¿3ß—ç[fÀO–©°¤ED¼Ø’+R–A~Ò*&C&ðÿ´„rÇ(âú2£òs¯ŠÙXCY•´Ü0ÉënDͬދ±èíþSYvW"ŠÚDä
+jUQ ¯%m’ÏT”û‘%fCZ/è>žìÉõÏ÷äy¦¹Cðk&éÃO]ŠÖ[Þe&Â1½ã™Àå®pºüÚLpMŒO{{ëŸjé­zEÃOGsýõYЇQÕÐÆG.
+³®x¦ÑÏ÷§P‰.)â噆EãDQò£.Î )§kÁ¾‘\«Lõ¯U¥­ÌÔ÷¦Ý4AîºäT°uŠœgö¿"`{W[{ýÀYm UFOÕg^l­ŒÿY-ο}  Ã>à2ÂÈiŽVøÍ;HÛ ¿£‚ÿÔ
+ ®È™ÉŒ‘¢ÏiÛRnÿ1nOÖP¾û‰’隤—(Ù$‚ZèS⳾蘘àZѯ6ׄ¼ÏëÀý»ÂBŸhéU-é_¢ýÀœåžD=³t…ì €ÃNˆ¹n9m!¥Ø&‰iîYr¶s¦6wo‚œ¾;‚KÔ´”F˜ûQÉÎy6æÇ}îYåQzAñšƒ jDìƒ%.îp¹¾&¨·ø4ÍMÖ9AõþJG§{©‘½¯¨‡x稠­aøãÍH´GŽM9œÃ§Ú&+âWš3®zòÃÎ ,ÊgSsÿ‘šTâž«yëUóì38àI-õs£[‰ÉÚ•àÞ½Uè˜û1O ùG6ñDÃÆNB{HyN.ks p¬áq}ßzsû±±±ëx£±Ï§ºTÛ@t̲
+—ýÌØ2AK:o¬Ž3 þýD׎¸¿/áTøU>¸gÙðÍìS ô;ôô'1øé99=÷º}”š©ì®MíÂf^ÜìE§~[rå´b}{Å#mkécç$ì•¡»,\RW¶ÞG,h™5ëÝù×·FÀ= ˜w{2äK»õÆ¿Ø€=^æתڠs‚¢{þU¾à‹¹õS`SkÇ'íŽâ’ìÄÌ35›ì[hÀOÕn©xe ž)É؇KT0ßÿ)«]V²Ên¨…àPc'ä‘u—¿3Å,+N¼õ3=?áÆÞ(“t¢mjÙ-‰Ð5e^6´äþÛ>;[ç04|Þ§ðIöÅ_ÿÛúµ4”K‚ˆ7µ][®Ëúc±.í÷í.ðÓ:D?€É.{ö‹¸êå}À+Bßj=fûcU̪8û_=Ș JÞëë~ “àÓuILV™MN]ojû¼&ä~Õ3ÿ>˜E'ïO#žÙ¤•q€'€Œ4²m
+N…SÎ,:R5 ·‘ñZ1èÆ2'û²”øqì“_ )×m2TÁ÷=a¯„›uß8KêÙÔS קz¾ ¨Þ3[ýv‘—ri£u¬­Ç®òÈÎùzøî -׳ÌÆøµ\ú©Žúf VKœ,ß{ç %ž:hi?2
+;\+Œ
+«ÚøÚ1G*8\%CƒÚÁ
+ë\¨‡®45nK8ø€V,s™_Åù§ÙP‡”úÎ<€ˆR‹aw\R&Ü>IÌÐ4‚nZA¡–þÒ»JAfˆ0ÆÁ 65°&àٔܚ­ Æ;Ï<¯Æ¯æNõLÌ“øÅÌb×I0ëTU¬}Š˜q¤æ6e¸÷ºl†kZH8‹H~• ö@Nβp[Áʺ¨ eýf¡Jy໋²”]¹/¥f›º+#·ªbÜ2ÌM{q˜J”÷—¹¦mlqÌàÓ ½ÐãPeÜFUÜf2aðÖÉ2¥Ô6CɱI˜Å_ ­ߌͽ>u=ÊÐ_~×Ô_fîß¼åžeïI¨9ÚVD¸u˜œê›g£lüëíìóyü?ˆ´ Â²Ä #øwWc TH+Œ?¿Ô {\f!u…7»Ð±¿Ôeßþi†Á9
+y왡–3]‰/ÔtEµ<Ö™QÜâUFW×UVÙx’Z²;†xìW³‰_MMÝq¨¦C|Ë<ʱŠ;TÔY?¢_ZÊéÛ`w,ƒø—ÒX¬¸üÕ Ë0&Ù9Å‚:§Dxï45Ç+%åZ‡ ùKÐèΪÄ.Š ÉGJ¡àPÉB,‹2®¬µG^~6ÔZý@Å®Yì®x!åæDü0—GE‡íˈ™ŽqBà« ûà˜aä#]Seô4=ïôø×ÉÚ´Èï&N£o‘V²û©ê¡mñÌ=C,8Vã ¿[Ù\¿0”±9N(ú¯éÃt`…MwL@cwF@¡öÉò˜½ÉŠØ ŽËöª…LÇ ì]àa<ÿ§¸ÌŸÒ¼Ö8>¹™{o’iL·˜¶6iÍbkbRc¢Æ˜ª1"Ȧ‚ìȾ(.Õh\£u‹
+P@6AÜÙQTb§÷ÎýGîéï¼3gæóÎ9Ïóù~9«öæÍåÖâø £èÔKs´´k›ƒGA úÙÒÒ~jápX%wþ¡æ$œ™™Ÿ†P±5 ;ÔJÝóõ™†^ð[*â3ð'—²…¦îEÇlÌRò¼¦^I`½_÷y'ðÉPI;CEƒE–¢H¯Sè×Q`Û"d‚GG†6ºfζçÛü¶ó ~Ë‘‘…óhèµÀ«Ã%^Ĭˆ šZAŸÔ
+(ÞyÜ)ƧîŽUG9ÇOŽY(Ç !Õ6ŠKôHHyrBpŠ'ûbxÜž8ì­rKÆ€Ød¬ª ¿¯&—ùôØ<¯‘ò÷sdÀd4 'Ë,"ðÅæùžÒ‡ šäcíÀL¸ipÖ)®OÖu¯âå~«m…FéÇÈ™lè³+°o"$|¬é}MôGâË+bjÒUmGöu);ýªŒžf­K:3¶
+·¦0ñ6!Ï¥à4ìÉ~%µÌ¯D§:$ÕÏ =7w§šÒB¦Î¾Óe¡pO\lóÂ%Æçy¥¤¢})ðT9!×>}µ5Œ~ìž!g:EÄ·ë bwÅ”‚iz¡¢¥&BÁ.þFÉ*W ªŽ3¯Ï6çÜØä¬~@½P ‹nÌs #g)ù÷7ßC_œéÙ¸#‡`š@<×ô=
+­ZCk-¿;WlSÐǶixœ}x¸ ™tfáóýz6Þ%§”¨È QØé–aSׇk¢ìÌ/´í9_›ûk¬MÞt¢r¾G¾zð¯µT¨uÊž¢¹Ö!‚=Õtäþøþk˜ èá?Œ,à”rŸR@3¼C%nÁãü‹LTPÏ%þµ.h™¸Í>5µÂ³@Ìõ«iUŸjá³½rFÝ ˜%ƒ::bOŠŒ_í¯º=P÷Û%ò›»„QÍù¿œ™x‡ÿÖ§¤”Ô XPË'ù4¤RÏRCÁ®8×'TRÐÈã„VºF}‹ ôöäᾕÔ3q`/(ø½ÿ‰Vµ=^Ÿ¸'£W®¢g¥
+@¾“Š×Ç0σ 7€e‡*~Óñ±Ø)†ÇZ§à1{’ÆWv)]&¨Šø$¬ˆÜ‘0ë¬ò–Æ¥ÄýI|Ú•¾Ú„‹=µÉ™y.,ðsnÙ
+J±G‚JZ(ŒúÄ{ýà #9Ì¿„ÉÿÏVÛàŸ«Â.ÿ¾`g þØ:‰xz¨&•ŸZ¸ìãU7hf“÷”„à,õþ%6ð-̳íqtÜÖxýk·ŒX´¯ ”ìˆIo—ºaQsø¬kîÙúì#C3Zó>ÿ'UwúW†²¨qrúÍ1\ê 2ÁŒI6Œ6æbS\Ù®Iøk‹+86 .)"Ù.©ŠõÈèõ'&a›S΂&<q˨‡!Ë.Á¥­@ï­ô×Ü–±ŠÂÛ _ ¡“¿sL"3Ö& œê„ðÝ92ìPÃlØš¨{¾2 ½gªüÙ2Rvs_™üC½o“2ËT,̉YØ}¾.|Ð6#–{ wÔ¼’psoÕm×1Û-çÖ±q>U Ü ss¸êa4æò(ò×KóÔ”k¸Ù_z$ Yÿµ
+úí‹ ˆöšGWí“ÄÒÐr×Ph™K?3*O EvQùÓåþ¬Ó@Vxhµíý©OâZl:¤ôŠSSÇЉžÛ|fäÔt˜OŽN·N”Ƹ$µ/?¯·öîi;ºóü¦Àr[ç±…K÷«(åÞy\†c“4Ñ”Þ^üè²{©•µ3GÍ÷¨éužEÒú±)yÔ’m¬.Ö:R~Û:Té‘à
+\
+J¥¦¯öîÎ4â–ó°6 !sWBxkãRìÒÆ”=>çÀÀÆúõ=£cß\`¹gòdåÝäéJk[ÈÒÒ
+Þ­‡à?fà’jJ©s‘ Û×´q­"ÂßóIâÊ{d´¥·îžVXüÝú`M¼}¦1kek ¾´Nv ¶ÇÛ;Ó<èö,5Û
+|ps
+“¸6UÐÓ Û2f•[ΆùÛ¹·aWFÊÙ_ÀgÔûõÔj‡—iŸ#Ííï–ø$¯Š‡ói@/qp¡UýÄÔŒqHÓLCµw7ÆqIöOôŽŒ42Ò,½œ7¦6ü#%½òÆ-ÿëÅÈ}Uoeôrf˜œ}æÑ!–ầƮÂ[Î)xÂê`y´©§à'ûGHtЄ-8Ô“Ê·gp¿YŤ¬cC+çÈÀ%˜yÌc‡æÓÓêœ ÿ§¸:¿š¾úxO{ÎÓ>=O«­×#Ö:¨ÕJÅÅ-K[62²HÈ™¿ ‚¦¢ŒH˜I$@È"ƒ$È d/°zêŸòü|q_Ü÷ÜÏ=÷ûY˜RÓDà ß
+¼à@Eiò-²`QÕ˜4®“ƒoé²K:Òlâº+º!ÈY"ýgÇ"Û"éÎÓ•{ÒöºõN| G6
+<´È€zÞã
+>优Tx>°Ê궋»Ë”üÊs9¶ÔCLÌĦ'­lfÔ¿°¶—³»@mð«yl¯ŒÙÝ ¶ú”èbûTkÚž‘åuÞ=˜C?qMÃó¥Wv$èrû{|ã<³:UɪNq‰y9¦8¾Jk /Ó‘që3˻ʻֱŠK Óq9;bh¦cºöêŽäùõ­‘ç×ö¥m™­8ȧáˆÅÕwLj7,³ cï«Ø:½Ó#Ef˜jRÖyÏ.8ÅMŸìÜaãâ©B
+'vá⡲§É.Be€=õ®]Œ-
+¯ÓP¡-€ÑS:¢ZjÇþ|wKŠËw/+¶Ç;ª/XF*/­OÞªLÙ™@üXâ¡LÈ|¥ ìÜŽ‘\%6‰G&4f¦ã#›<NTÇÄ ½}q}ŸðØ<8÷Á,>6qy1#­ëPƒ) èõ ©N3y2I*?ï•Rëö%äR%­êÌPSÚ×u·¿Q’sÚoùݳ@.×M“!£äê_&)ygwfP9Á>¬æÃbCL‡oô®‚½`“½/g!=Ê~ÞÖ4¥Ø:C,vɉ5A5È ºôo úÑÌá& ‚>cOdSÀ„ãqC_TÏ"Ô¸j¬-Ó-kê[¢Ôù
+hº7„5 Ö¬=²7ƒÉµ‹ -†±®Ÿ€ùd_ðt—d¸R·½=Ù•©4^åµ<:á¡ó3øëDgšg±»È!F?5M4Þr̶>ô*0E ‰›oáO” © çs?Ø^Š’ 7£R‹kžé^D庑Ï<Jr“i
+“ÎéÕ°
+ùìx›„ù´Ë8²¾œŒ™û^ûµ4xXÏ¢$-,Þ‘U07 %cß`Ô¯šòüß @ùÿ¦ðEgâj#ª¦Ã"*RMDIkÚŸÆdoÖ_2ŽT_´‹`Yn­uŽSwyåeCjP fÝ €t¸J¬ñ¯jükÄï*ñ¹jš®xÕ’6ñ'cVþ€OM®u+ˆ«TxÒÂæ€þŽ÷©˜û2B‰e‘Üd‚Ý’ÚêUâ*l"$Ä0ôâ¾m–ãœé,u/tæî.áJg9µ¿N÷”\K€~èUQ›öe”ZÏJOK@ <*6jONkòªXíkdÖ¶üýÀ”Üû”= ®Yl®sþÐ>½g— ‹öålØR?:R—yn{²åѼí±q¬ð¿º‘ –é–ŒµáúÌÍá†'‡rtÙG;л½€…¬Ž´Ýò(è¤I(Jš¨¸¸^éSÖü!¥ÿñ­”˜þ½SÒòôƒ•Óëâ!Æ)|¾WÍ¡›Fÿ˜úÇ?Ûh¤¿­ø–à:ªÐ2ÞvÇ·ÐSç_Úƒ«<BÂ28Ÿ´Ì% ¡gWlj¹×sÀ‚È°ŽÖ\¶¿«¿z°„É7Œ¶§ZF°÷’>¤ã56cƒS}^Î,:ûqïGn]ê¿Yϯ3
+Kÿnï=®božTâSàËBrS`ÒžÐóqSÿTd«o4´ÉÆ%
+ t0z|8Ý‘9…suƒ]~3 ¤6ù”½„ývù¥*E-†åøU|Z@×…J˜¨°¸™Ô1â]+ú.dÀÀÁƬ¢‘Ä>_”x9iZ‹Y$c1‹hèøÿöYD†eúÀ<ÖöÈ1G cs¯)º+ïäœFÏ>ÇÐW–¢á^²ÖÞsÎ!³ç©¥)›|ÐÃ8Ðù¼Jâ;Ûômúàxÿ*¼Ë„=ƒ3‰&Ϥª°•Çðj¸h߶´7²7¡8?èéYHஈpߤ©›3s\£ÔÄ€}B?0Ò°à¾-Rùûed¶~°1í`›ëÝf´&m=â l[Ä•†Œ|AÜ!œxÄ ‘Gòj˜P/0; «` dû÷ÄÂU$¤ö&ŒÂss¿8¨åv8å̪¯Þ¹ÜUy²N­¶LÀcÔ<ü·ž•ÖRqÁBõ©¢ý¥söÌ2ZŸ~ª$׸|¼CÆnø’þ»µ_äW“kýj|¥[…,:Þ„¿u,€Ÿž© ¨¥¿×¾È­3Í’ ü»½ÝnÅv™3"ff“¶¾˜©_š4ö÷ÆtÝ„ˆ¶‡4¢ŠBZΧáàüÛtð.þuò›Ð½: ¤ÕúÖp¯ÍÃÕ·â†4û½hQòõ"©ô’FX÷ý»êÖ¡4eƒöö²mrϽF.ÛB=´OKCº>†GÅ™ ›t!dJ<ZfË àñÎÅŽÜã5b©ZôîvHIEM’QÿNo§WA©0K¡ßO þ)mzöů6•Ñù/ZùÏ_Zšsƒ[ïÛ„²À6±Â-GæL2ö„yWTÌG!Grj•ZTsà ¬KsN"^
+›VUúPGÞõs§d9nVÆ-Ÿi:k]³-Ü«èÜ€š\°~Kɘ$¥ÌÀs¶iõYãSPAn`y´l¸}ž˜,£4F
+V³k•Þ·j"Ö }Ì>¦XG ‹PÖs:@_|¯dC”"Ø}ý(p¶Ž/1Î`Ãj.Ö+'»1™®™ÖûÎѦÛÚžâKj~íÍ©nè¼û—ÿ!€äÜðÈ %G˘Ë ì®c¢íþ‡9è/'ËàŸëä2 àëÈõåµà6ÔË=ÚÉef@Ö÷u´
+$13Ÿg_$¾v.Ò€ü&ÖœmSÁžu|Ñá8úé§éÇ5è6ðV÷H5ù_ÑÊûÚ$†—øå„ÒƒIÐ-Çl}šN\š:ra…’“*£ÞtN·fgO%¨×—ìClP@&¥FÔ4pB­ ë:JŽe­O<ëÐ̘ŽÿhäRÎT”º„Žý¯•‰û ¬ÿ9z…Ÿ,üž¤ž‰vÌÔÝ?Ûø‹KN,  ®®°@?ºf(…ÎqÔcË
+Ò[Ÿ’ ñ*ðUÞ-tùÑ:ì9Ðoñ‘8¬é"DtT艢!Ë%«Í<Ý€ç…ö
+NÙ-‡ó]Õ¶|VÜ 0¹…†KšÉˆ6.œíÖu6($u?/sËœ;Es^u/Ý£ìÁÕ=¬À&â—¡ =‹È·g›„z·æö4¥ô§Ã¥ŽZ×jgµ}º3×1‹Ïs­ŠOôš÷sø\ß±Ò£¢4nJÛ~•÷6<?ÛIB¡$hðÉfŸ– óµóA£@`˜&éFqY>]·s½ ¼Æ‚Ü€~3 Íúχ)ð!%ª8ºóŠËü›ítã÷ÜÛ¹Óé™étÚNÛÑeæhËæ–ÑÕ¢Z¤´ö5"‰¬²¯ßDV j_J‰"¤‰l$d³A6!—.çôœ¹Èýúíy~û<Ïy¿_Ÿ÷›\ÙÏxq=3îÆ¿T­ÙG¶^xI*tÏЊ·AÎí¨kí¨™F9l‘6ÚÄüiAáÕå7ÄTŸI.9佇+-þ³œd
+ï4»ÒÒ…þ}KA+˜ÛÞ€^j<^¬ÇçªÍ1êSq«oÙ™³mÔä.rÕªúÚ;µ¸ÎÜ
+ýeœž~¾ÿð\?1í› vÑU°
+ݲu ûñi•ýrŽö4ú|XCó
+³Z//¼>
+<º`îɺfé}qu]O>œÔ…[Û ]ÄŒõQÚ«SP/ÿs ¥»…ÿg¸<ü’Þ×8~nÝ[v´S™Õµ²Sž45GCS{ÏPT\ÈdËDPÀm™š3DEÙK®PPA%ÎÓ¯ûŸÜßðÏóù¼ß‚ÿZéø«eRé‘Ÿ±Ø[ÊÇþVŸÿÞÃ6ÉÀ^ÌñêÏ4Ô‘œZlA§Êy…Áª–LÀÁ2¼e¸‡œ _h«t~#fºf˜Å
+ržKE¹TŒÚC ¡æg{o!’/LüÀ%à.%>w_O)þad¢þ²ˆ¿¹8·™ÇØšj@jňèC«Ú=Ï®³á“L"è3µ ÷þl’Ï0)íéæ(:p-0¹8î^Ê+ߣôâ¨+`.÷ÔØd§
+›ìP õTàm°€;·däì]%³äÔÔ*>Òñh¶ñÚôC-­Ü9‰K4 óŸ c£nÛaÁíÄ|ÿ‚è—·ú y¾ßÙžÃÈøèéž:væoc¸Ø_?¡}:j|µ‚¢ EKYDbÀý_Éž¶:n}‚¼5…üóhžŽ¶«Èà•oèkS˜„#`oÿ·ÝÔiýV—~ j¤œ-·ºW:$›ª«­–ËU&éȨñš»gì‚oŒÔ¥:&HéöadÌöHM¢m¨"ÖÚ_úîXßDÜ¡YX>Íù[¿’€ó—=W2òîŒ×¥yJë’nÍP’~]~ôµ!«Â[ÐÑ=˜¯] )Ç)«Mú›Û
+ÒÂP—~º@‚/óù+âÏnscýÙ¼€{¾ »Mͬ=~¨`íO³¡ ˆ …VL¨‚û‚J÷ú\“rGXç9ŒÏº7Û\ô|o¼6Þ¥ Bí¸|Û>{OÃEº Bö‰‰wÌ ´Âü‡r^ƽå^X€s‚˜ybäÑö>9ÐqÑ'Faãñ,Ÿæ’³öåð(»`éxd —xijªwùŒ³ôØÀgDÜíIV¡u3×Y¦U™»ÁC„˜­ n™A\ùBÉ?™n
+wfâàÜsbᶎ‡kÅdz*Eeï/–8ä«U6é|‰9_Äåž™ñy
+\í*}¦ädÝm„¼ö dü~MBˆòü¡ÂçIZ`oa W{*Våü'È 7æž´>ÜÛÔû`ýkeä¾’UçÐrpô²Hß馬wÿÙˆ¯,M—óMìc=²+EÇ/v…è›2ý\Z
+dŒ y7H-viX8§‚qHñÉöQTª®µø­”–ñPß\ðϹ‡¬Áâ-_y
+|È"ä;
+k£Øp}®¥—ž»;Þ\³3FLÙ—R
+Z‘ ˜‰m5ª\Æ܇MpPÇæ¼{‘‰;ºß=G+?™«¯\ê« ×µVlŽ“À—⧊ݫ·RÖIéAù«ÑºLŸy4è@É€µQ žÖ¤…yiÛ±E;rL#ÌôVòýÔ-i+ý£OgyÔÓ9‘pGËBQJÞ?cçDþ\ã7»çþÔrí’š7–ÏÐgzA–^óbkš^L
+>¼¼É¯N952¨ÿú.è8_dàìã°!ÈïûjLÊ¥EØéÔpˆ“ —!øåå_pdb£~è°)9g[‚OZ¨|µ1Xj•ÒsàhOBöë›sŸ+’\@žnJPi–/eQÆvhÀ@]æmøÍu;õ‘C†HÛS²í2Dô‘¡ð­Ž¡‹Õ ðf£gnГ
+ e¨þ­Œ]±1\—±«¡{”¤,§’\àP±áÞ9>ùÈ,øt- vUÀ=åÔ"¿AÔº·(âz•Ì²uImü$%ózôYÐ î}°ŠQ2È@ÝNˆ »PŸõ2ÔÖS²õ"Ÿ- òîX:‹¢—Ú
+ïŽï@b¯ŒÖ§…hxл@ÖÓ+ö>DÒ™ŽŠÜøþÄÐ’nà§ÝT=ù¥õò’‚”qUÉ/Ž?¹
+<ŠÚw_Gp Û
+à£o*ÀaË
+wI±ÉŽQ4È)¯KÞ›o,ñÏs kC@®”Vð_Û é½¾£!yŠƒ~µ$FÄ؇¡±¾iD€Gªs¶µ@¾OÇ©ù±Æo?6rírfñî<»vWK*;61s¯íÄ¡ûæÉ…{b7ðÖ¥¤B]
+*Ä=M+¶‘Sí¸„-sº(îk[(%§Ò%ïflM´×m 1ÁÖ~Âk9£ |œœyË6LHöÈ9åû6z7°“{i©M´£g5mÏ5¢Ý* àëpuüžŽVé_â’¿웉N8¶4SM|öŽžG:Ò2±ºVHÔ)÷†¦¹äö†¤òѾÈ?1r›V¥ÄLóPMÂÎ æT³öôä"ŸŸm“Ö¾u«)E‡ <‚OM-;³´
+N¬<ξ‰Žûéä÷ýpŠÆ¾nÇÉ*‡çš¥×:g9U[rz¡GE‡|wtLïy¬# Ÿ¾&–¯¶9·´rNÍ<ºß$àÚeŒ|·‚È v…µ§2zµµ2Ö‚0tâžè…è¨Y.4t¹­ôŽm°êùædc¦y˜°ÜQ½Ñ/„¬KºQëýÌtÇxcÆJOy¤Y\æ¬{}làÔû ´J»—¼)%džY…ßV„ƒeAóþ"—¸=×Tê_æQ"Á¡IÜï_°½3¬r¯’Un!gZ»+ŸêDp)íÓm ¹â»ìc°„ð)RÝ\Ñ‹M
+k
+Ðï/ç%Þ¿ˆK{rq­+ÐÇnDŒcñÈÔYxWÖ”|­ þ0h´þů>ymú×) _Jˉ4‰Ëhø#=“èSÐË4EΡòGjVòMk<Ö«`À†)иµÑÚ¢atŠµ³2Á!!$ùElcS5-9aÝè×—ìÃ̼¥.r¾±»ú£w—2ÃÉ W1@7¿DŸ,×€ýZØ&) 0(2Ù¯o!›$”Òí2"À@5'–Fô+f¦¢Ï­šGÙ`˜{)!ç`Ž…óM5äȨ)·ÔôÜ(»¤&Á5†KØ¡dxåíDçx Ü+ç×zUíM)e§:¥äŒ€ËþŸâ:ñf{Mã
+8È1‚N`äF ÓîN±³BfY…/]øôc-¥dGE
+úG[>Ñ ªüçf&Ö5DÍtöc Ò‚À-»tOÓ.|Vz0ƒI¶Ê m]…Á[òšèC£|ž‡\Ä&ìh¾2‰ù€ã+wÔÔÎ!|¢M†ŠÚR’@޹ƲƒzºÒÜäšf•íiÅŒ#½”»§æÀ]j~í’R Õö³@ºNbœCÎ)ZbçN7aŸÑÑ!t|¤^ŠM°w‘ÁŽvÒS *LËG?U‘*î¥Õ!rVé#~~üM>ëÎÿç¶:BNßœb—¹g$¸õ™v–¶—’ÒýpGË/>32jªÚë0ì™c¤.Ê¥B¾õÎ6VmLÊú¾ÏµÍÈ’À]5µÚ;[—¼1TöÄØZpo›âßSº© dܶuÁ£-Jv1:?ùvlð?}†¥Œã2,­!&qÆ}51æ&òį™ Ö….wóÒái1? Jãžúæè?Öx<·¼"Âö¹àÁœ ó—Öª°Ÿ -¥Ñ =ÈTND
+ÃnÎ÷³·—DŒ ŠcIü'Yy 'F.öH#ÂocÓôMû‘oopóÃüÆé)÷œ1ÐÈßfEù‰?Ö¤½ß×¥²K;§ñØ@€z4¸ì¥®ÊÐQú‡
+ôl‘ùa¢UíÎ’sö´´{†·ÐR÷5bŒW tÄiFÁæ 2ÆÒ^òXÍ- ÆgÝrtÖÆì¨y¨ÅŽšöxô™Ž ?7²Q&ñÒÊcy”iN©MÅ„li¤l—®³Í6Ö‚tÏu6ûšùG3ô"Ÿº!ï«^u¨#äú´ä|ŸNBv
+å®onü§Ä†S°ÍM¥:½ 4G˜Múr~¸þÓ5jãŽÅÑûȼŸ‘\»…ü7tìѲŸ%,œîŒK@ŒšE¸„8ñ‹9ïveªª åœÔ&¤gÎØDCE78T @Ô¨‘Õrhãõ¥í|t5
+À§·ÈòfzSÊÈîJÙ•@Î==•õÌ-XçeÞµ9RxsC3.H’N‡_Ö°ûCvw£hß ¬ðÑßNÖ}š&ÞL¬P~;x‰ÿ>ºØ}µºMj®xô¸Eˆßßäv<R°äOÜ
+ôœx…´cXHɘ9yŸPòÈ…Q£„²ˆˆi«`(a±9HªîðFÎ÷Ģ〔Ÿ°JˆE¿zº‰°à•q;o kc¶e·ÆŸTœôŽ
+,¼S¤ýÊ‘Ú‚MFK™ tl•]—ÔsÚ"›¬gþUÎï{:>*ïQ
+ôÜ.x†z;ašæ%ÌsÂŒEŒ¸ˆ‰n0ž˜ïä4 欂éK½Àpq›ƒ/º
+Æq©É"’ù2¸ø:æšCØô¹þø/zÝ…
+RO$;‰I+ô<i–Ñ-
+JaIÎ&Im 0‡: 1¼@»^œ¸›Üât‡6 vÿ2Ø3@Ä#?²ÖÉêNXíy 4ˆ0f¨ìS)Éø¡S ì[f¹žµ„€n•ÔÎâÉ``jüazÑ\¶qpE§`Ô¿ÁkÕ0z.JpõŸ_²Ú3F #¼ˆ½º­ªû2²Ü}%oç “+íž|JiÞ+¤dbn[D͹Eã;ˆ)8è-Y#ñAÆ>јÛ‡}›¬fÁxã%FsíŸÊfÁPÅ!£l‚¡²ÍÃ\bÒ %ì"bVÈB
+U9(d"láUvøãÇ0PõòÇN}BÆ‘G0–²°Z÷–(w’›&i’<jâw^¶f÷æ:âÚ×cZ\mÆ4ÖP‚yøÀòè“ÀéßU?Ä(:lxiðÆîbó?Ïü˜{ov‡êO‚
+VxÉ#Úý$ª }ž;¥Õ1îV “A-ß±²ã3{±‰v€¹x‰Á„€ró3{ëCE{Q™¶QaÕYÆ)?HXÅ„¸–;‘7#ËQ0^Ôpfûä `cÏÚ„Ûw|Š½/³FÖ|C)ÑcÊýôÉþXÑŒßçpõ§4éFy‡íL-Øö'óêwu¼Õ‰ˆ­°j…ìV"õB
+pF éñ ÎgñxÎgW]ÐJÞ(|“<½3Ç#²•O*NÒD ;\¨¹ÀÕ¦_p3"ËYû13i•íd¬¬ç5LD+º”pÉ'ƒŠnÞV\CúöÂÄY+9”ü´Ãvê×é)!™èÙ¥IAë:•`îE+˜,ØßM^bK_½OÑ7®bÀ»k<ßûIu°ânW,‡SU+{®䬢ÌÕ~˜±R÷ kªä†6k ;wÊx’”,ßêyØ«í DîD`Z' îÔýÀzΘLánÞ …×I¹á: sI©ÜÕ ƒ{°D\qŠè>9<îRIžÅÌ2BÕ$m‡¤Ò¦ÞÉhˆ"¢¥;MŒ6×BŒŠþÚ'x{+­ÞºwiYÑq7Ác5‚|rÝ ®Ö=ô™²sw¬…³5J"¢ šø_I|P¼sÓ 'eCVŠ¿Ì:¾wÎîÝ*´ìÖ©<bè{îâÜïs6ä ›“»xæŠ2lT›:I˜ƒÏì\Þ,vÏÎUŽI¤÷ß”R8ëÑU‚:k3¢¶ç]Ú#·Fµt&V=>C•cAµbÑË?økV ¾ê„•'ƒ¤Â4LKtâ¨Ç8Än¤ rRó0¯ŒŒ*Šp?°+îݧeyºƒY Z ë8ã!-k,cÜÒÁnÄ òeˆKÌ;ä~J¨¼*ùûy™«™èë1)b/«p¯‘©ú8×ýÿ»ÄhEöúqòËQšMe¥æ^F‰åmÏË.æú -32¨b˜†ØŒý&e¡—B‡«ƒ›qSåÊ[ €ÞŽ#а ÒR¨x˜¥o2Z3"—&ÎÔ¤°ÎJó¨Ü{g"ýS#“ÿ·´RG,õœ¸†7á±ïFÈÉr­`²£f‹Ð¯Í8ê—!twü&%·ÓR}3*8LX9/Þsÿ™žìP› zTùíÑ.õ–dýа)DðTÜ‹À¤²K°’9•/y¥ÚI‡@ýÄ*Ð>R€©¯f~eb­ßë…TúnHml…´ö”UJ èuD—J·è?Ѭ'mF‘Cy²fk¦|j9n›£&ω[ãáøÖE=dxJ_gÜfÌ“î(€{fªî‘[h^
++ŽßTÌéC†þ[à ?äøè #ÑGOeK~ùéBPaXŽ¨Ä/JnðuÁNyt‰1^wÂÜĹNZ¥»˜øéIª7Jx˜?co–"J PêAzŸ‘þ]”¹qÆÞuƒ{³7y¾â—7ªEäâøøÊ!¤|ª§ÓþÒ­\xP'*‘5|ÜÍ„˜Š*éKgœWuÜu}Bâ§i c"££LMôÙ^…ëA„^öð »d7n.ܥˀ`ïÒÇY­¹ÉÏŠN`.j–­» fŠQ¢Ÿö+æÂúãMZ½âÑ[ALíà:ÿÇp}ö5u(
+ÒN {b|Xc>œ¾Äq”=q9µZ$¯:]ú -
+3ËQ³¥”)%jG»IïäCsæzˆ©FûåRôªľuIï×ô‘ý°Þ²Љ=í¨qÃHœ¯„6F„ ë#˜a/÷‰}ý©µ?$S½
+kmcv‰»CÏ^¬áQýOqXÒµ®ÚÉô1œàÊD7ÅÈX¨zÖY>¯* o|RFá5/š!›Æ5kCD½&JsÝUª=žµT‰ãÃbîb=mÆò¨;—ŠÇÎ]}Y9u.û6!/£2±¡ '…1¨+2óCÝAÈ4v죴|]%´}^#wÅ<l¼mA‡Ö°eÝR¤âè‹íZ{¯]¡BGhìQnx¬W¯0òž"³Ò\ñ(áסÊÉFÂxù.lá”’«
+äßÛÒÓÇ`Ðì}ÅïýMnHà7ÇVÐbã:”Ö$½0 Ô £O¶áFDÛ>xͬè7æŸ3úú.Á³Ôü°ÂÈÇh×]qîUÅ<ò@œm#ûÖÆhÝÐŽ?ß1 ‡ÂV´i§‚VÝ”K#©s*ø%n­®Å p·AÐz?‹„dÏ ²ï©Ê*™È…™‹»±”[غ©[µ•£?¶6Òz0#Ù""XìV¨ÑQ"Ü^Rߺí0DíÙuëcnñxØÓ]Yp†£dØš@š·„<³œ=8á¸ÖÑ%ý®©}ÝÄ<{;óuëYRvæ}”•©f¥‹4÷)™{KRæIPÀzë¡tîøQ/"r8\LÎÜ®\07(ÞN1ÏQÆ¡ÀO¹3òßfFèÏz±£wGÛi9付ڑkèö´™¡îë§È$”àlR5l³¨ÞÇ óï§.ùÎÿ½/2|Ûá ¾ìBÞO[ªÀɚʴÄkË&Èa´i×ðäÏljS¿Í`'Áb¨Ê,––û óõ>=»6î§ý±©\ü—!a‰hÑéR ¦WïsAÙž8}40lL›AÒÇ4?`jIç*JG’›êùßaÚ‘ w
+ÇŸiL,,ÀŸo­'\äMóo;ä õ1;DÜòA÷¢Ól{­”Á¯'Y²·Í ñ7ìÿc²vwzÔyƒ2æDÍN¸n‡ iý½Úmò«¸VÍ%L«ò|V¸õK#PQN;;2È€¤ý1 > CÊ-8¼á˜8¡É²Õ»]î^Ñ$m÷~Ϩ? Ý"IniÓ|ßÑiNinÑ$åŽ'\G5yOˆ žRŒl<ü].Pð¸ÿ|{«ðòˆî¦cAY½»’öÊß8CäÖ69jâ²—JfiÁû,ð]=‹{P%ãÝõ”M±>á̹Ç‘Œ ó*£(ñÁ ¶¨#¥­žˆ¢ôÑó¼rÏô¶Ëk9\^
+õÎ2âÔ
+,æ¨D®Û£0D»uƒ„Å›ÏËçÎåÞïI*+%^xÕa¼XÛ¬=ûä%=!ó^Pðœ’PTÉMHÏìPyd çOö\cÌsŒ2ä•C.­Y5±Ðk&ZͲI;¥T¯x {ís¢Xù(9€j锦vÙÒû†|?•—QÎÜË~•–ZÜÉD—3Ú€¤$€BU'àÌi/«Ù3³»Sjjg®Ã úî£U™üÛÖ¼îÃ
+ŸwŠþv¸T^Ç¢Öf}­ÖÁ›\~/`‚ÛuS3žL´-‹2~8E^ÎÁãU?—v_¬«›ý¾ª²ÿJ]…Ìs;bò~ˆ3é¶#T¹Î×Ã’í4‘{Ϩ±,t¯1¥¬~.±¢Žv¦ t¸qÿpáJ>p' —Ѭü …ª›JbÍsr.bQ ‡ôtÀl¢ ÆÅYPµÖE×N° ÓbY]SáŸ*›%g
+ž¥•ÔÄšðÌ“
+:p#¯¸|ÚJ½ÝääOw“îüŸ¡òúJ3A ø jªǸ‰%±·ØQL,£Ø
+*Ø°!¢€€ô¢"í£÷."ˆhÔhŒÉX’˜™³ÉìžÙ}سçì?³¾Þ‡ûô»¿;sdL–lP¢6+ù<¤•\„Ú£^ºáTá VË [{Ô‰[ü˜†!î'öMGî!ûAXqHzŠ²
+&€ºfäÙó% ½xÈ*§
+ñÚQGù¥‡$¸*{=â¼ÿ²Mw½9hR©@0Ü¢i »pâÚut ¼UdÂД<ÂÜ*:´¹Ï~«sj/3eËeö¾‹è2ÿËæBÛe‹Ùµ«&ô2G»bý»¬ü„¡òËgh§YãóÒ† ÛÚzÔ7
+*È@ ‚­¨_‚<Hiî'ÕC]+ñõÍÔ»Ø>Zæ;¿~‘3=VÞhÝ «,æ—5§Ãlù9Š"8.%{5÷®Ü®k[Šƒ·0n&罆$$V
+É4Wžd{ÚŠ–ܯo¡?ìì—> žIÞŽ¸Í9òm_cFóÏ= õÐ4WYÇf¿·ÎÂü+£¯jÍÝ\¦0‹AþM,?íÃÙ…cgÀ'í3w)$Åã†#‹4oBcxë¤Ë®>›ÖYô¸Ë0÷×>kò_‹#?¶©½_ü‹í_ŒÎ=#¶®b~—ÀíqÎ[Œ6ŒQçÒjBƒ’e‚Î6’©ÊÜyš¾€Î4V¯Ê@¤RÖ«õæ虈Ù}¬ËMçç®|éßG‚Åï;ó}ça>éCØ¤Û zÍ[A·Ìã³P4E£D¤+¡²CÅ“ÔH
+£I®G‰`¨ÅíjäB¢•vÙ/…6aÖâ›±ŠÄž{¹[®×-N)áçtµMÇ^E˜ã‰9wT,‡IÖ®ÕjÛö‹Bí%–.z‡ ðb%³¬ham—ðAJf7$-«¨ª£@^´.ÜÁà¤éd’5o…ã{¾šq§.vשXsZ¾ô ƾDäÔ÷þÕQÈ«6[|“ZCt\n¡µ–^iôâÖ”¶f™ÖÓÎSïu˜Žš²4›Ê1–ŠÁ0Nïp¡ÝnÓH$` ž„Õ´¿>ˆÿ½àóÿ÷M¼þŸs‰ìØÇí‹øÍÜ@зºy‹~ЩմÛt*”É ï0ZF»Ÿ`°xÆA0€Ñ;b,ÓFˆíöƽꅪO>æâÿ •×W‡ŸöqϼìÙ=ÉæL2“²³3“lb2›˜ª&Ç.VTTDQ¤Jo‚" UAzï]@Š  ‚k²gf˲¼ßsîýý¾Ï¹„Ⱥ–Ðyç¬O«ÉÛIcÅMí Äf&=<ªÏ¸<¡_#Ö¯-ŒÝYa£ eš.ʲ©~§x0AµuõÏ
+ ¸µw8®£ǵ½Ÿ™>,Šµï„
+ý€Ýë²f¶V%GAÒÔ úéЇ™(ǹÊrRì͇VµÛ†H£“#e
+H 1tód[P† ÐFàø?B«\íâß®ï¦>l-<…‘Y¯E)ÊmãÂê¹ÏQAÏs©ö»¼ÛVá{bzÖLÄ$DuÜQ¯R8äÓˆ'ÜJÑ°z™óJÁS´Èù¦Þ5ž ¨^5NZ%Z°}m0¡Oîëx3§þR%ÌåŸø¨“)5®5«! —}LƱŸO˜©}~õb¯ULi4 u£‘§Ti'×W$Í!»Á©b÷zõ¢Ÿn–t¬³òþeÒž—ƒ/ú—å-ó2¶¸pæCölKûÄă÷÷͸¶¤ß´cd‚bÖeÔ¦–Ú•pp°q»€0,hùSß+ÙSßêøðGv9·Ó¥“L…Œ´®-¶a×*`{6ìe?‹z@v•\¯÷ÌÓov ³ïSVÚX.°nÉúÖ q ÔͼjZ§cƒÛ§^]H¹ª&μæ«|H¢Úžc ªpÞwTÆÊ›Åeu _´þI.]i‹Û”ËÇ…½èf3ófð2È]¾‰
+´7ÑyƱ Ú–üÝ-ÞwJfŸ;”r”RêD«Yå­û¦–eA‘¹þ<†úflú Eú–Ãe=SÉ6FJ !jX'gõ´ž’7TòàG®‚tdZªõ,|ü«Ÿßô—€ôSØÄæáAOˆ Æ?…Ítb6"”Äí‹Ÿ–Ül•âß™Äø—ö Zæ…=¾[¢bdèÙÎ"ÿjwIz“¦Ó.·Ð×±9ðe 7]
+ÌSFlG2 RvÒ)bœF8¼ãÍeNÑÏ&˜Èb€ιýY'±/åàÀw<RiÊÇ¥ \ÁO#d¬DÀ‡Úw¡
+jW¤êÃuæ2
+¡&µhDo2,Èu6ªJiÆ4J¸ÃÀØ´ã[
+Û,N9ÊdíjÆ^†9­·Ââ¾÷iÍèS p'¼ŒjÈéب=#˜VÁjC‚ÞáGyÃ<p×ÌUÔ>òøĶ¿ ä7[4ü¡ ü:§ù1§y\°ÀêŽì˜–´Õª#vÞOIÀÍ¿… Ðs/¬ùÔ…n/¹ÐçA<¤è'€2NúØŸÇ=ÛéË1:®ÚýÞ}Ûôû#¼©¼‰–ý„áJÞ5ϵœôÉ› ý5J†ßD(³Ç.LïŽ
+þn[™Pcê‚kÈW)1bÚ3ÔÇc7~èÄ5(˜arúéºLõö)ÅtÍÎüURCîL«ŸËnàÜO?óG/‚8PÉ®æn®¿²IE\´‰œ6˜PÎýº­Àô¦µˆºˆtâYD
+{Sbëã<¬L… I©µèZÛØwò9‡<$g®ÍÊÛ¿?µÍvžo.R¯ãܵ/1žâ:LEVüÐ_sÚÑW§n:ª\dVÂŒ…¢ 9’ubû“N>!æX¡ûtK#âEÆ4?~hgLmØ‘¼mºãÔ‹,û1?ZÝ·q[¨v<šÕ#êBÂÎGÆñ¦›(j¸$ÃŽ]¤ÉmÙø³¨d¸æb›²PŽRû–ªo´ˆ¦Œò®ìE ^GðÊ&t`‡¾Ù·Â>\Fɨ¯)2éÄm
+
+ÚïeT#5y=¸öÌèþ}‡ˆ>MPñq ±;ª!wߤ—äEËLCZ2üÃ6oð¾o¡û^Jú©ìDw_©³9 nÄÁÜÉg
+Ï3v­Äþ‚—<UÙæi+)¡«²Ã–D´7a4a@ÔÇ 8ÀAH Ï„ÂŒ‡ ˹æ'÷ì´ñJŒ¼Ž1WAüì…5pÄ
+ÝUϾM* /l”ŽÛe VrÐLëŽxêå©Õ~éE÷^øZÐ/vó-ðÉ<Ô®§ÿŽ0è_袮ço9yë­##ðç’c¶ñÌKWüLlÁJ
+P³ÊO6z_Yz^Ú¦HY»Óô VÔú–æ“Óa€œ ä§;ã„´]âãÖ‹ü†Ð"£äu>ñx™ß] ûTqË1V—bý\sk{¬&þ?;]ÏÍíí.%­Ð6A) iÛ¡%ZÍ¡‰?0’‘©Â¯eVEVy˜c³¨Û7Ç,3 ²L_j! w¸ÞÝ}dä´ìÍbó¼³è4³„œëÖv3öÍýØ^I¢–çýþuQ§¹«ж"ö×x„€ž‡tí4—šÌ}/ƒ–»ªöFëxsÊŽäÇÜwKš’jJÁwG÷Kÿ >Ó+§@Ö‡šï½">ŒQ¶ÃÝX`–_œçÕl ­#˜Û;ãØ» 9}w’[l ÜÛüDJ )z¨'º^Î1˜Å¾ùæ쥰 Ë=Ð3ÆeW¼“ôR@ÕEtÏ;&wãˆ;¡y"$ºH/Þ•às¶Fš27‡P Nøä¨^€‹Ú‰Ž)lšm¤ä’]\uÃ;ƒÉ
+ñÉh¹ëýe¿)9˜Izö/räÌZÅC_I¬u
+Ñ|Dga#ªN, h«ñÈH]1ü®émQœ¾'ÿâJáùwu×ý
+^]J)ßwö̶@€yzKBH÷LSÒ–™°äò93Æ6Ú\tºÚß å¡-ƒ5 «¯Ÿ\2æ\
+-¢Ó Œ²¨–…ªXp·’UæÓˆè{*ãtµ‹}b‚Ë|œOíó/0 £†V¸UŒIú:„IpIÛ1€BÐ]¤ûg1iÎñ¦dÓûªk¶á¦DÐ/µ¡Uq¶§úò–Ÿ±¿,À¯ŠøÁùÖR`®%P¢s\Òºtœ–·· $xõ"¦EBƒlKZÒ³hÐ À´”]‚ÊÚ®3®ÿçæPãMÃ1ƒ{è”#¸g›²¢zü@ÇFœ®r±?¾¶·E äÖ("Þ£$—­÷öª6„wššçšÀ¥zdèä½™–œƒ¥bDÛ†ÙïhèÅ fU»ļàµÈ:T›ìžl‚ØG’}S˜Ì=µ+AeZ†7÷—º;†rd™^Рn:Ó&F?ð«éÅáev]ÈÀiØ‘`S6Xx{¼󳮀^@²Oo~ª÷)q9Áeá³ý ‘ðØ, ®±0‡–¾·¡Íªã¯ïfþgëî;^¡Ö8¦ÑÛ÷=
+2ä?lìdÛgÀÌPPŸ†4¬¦ˆ†ƒ k9ua0“l$dOM]ýPuiåâªu´%uoZPíšdä[‡ðI€¼î”± n¨«›èœ$gm ât} ¿[‡©Yû‹ÝT‡˜ü8¨b–„4­•ÞiZ鉖Zy¢ÇfèH0û4-çë0.ñ@+ÀF8p§ôé*ÞЛ;ÉLùËbWnLTÍ®ô¼í(6MÌ/ÿu_ÍEž®²NÖ˜µa1Ï)k¸k~Wþ›ùUåßìcÔ‡›£ÜüøÛ^)#×.&=Þ•¡N)Û)iI³|DÞØhLöHÛJ,ŸHéŸq~ãa1šÎê¿›ÞTßpŒ5Ü ÌS Ýs4¨²¿öæ(5?öÐð\°+¥=Y{SùûÚëüs:aþ¹•ž²ËÎ1tÊþ­Àöˆi.ôçåWUq‡Æ6üæ—† +ÿ… øìBwÑÙQrÖÏ;è{À³Ò 2Õ9IÉuHé9—äS0ª¦ù™ggø÷c—z2Îo},»Ô1J|*^ýÖF©ý úcSÈ?4µÓüÚ¨säíxE¢u¸öÚÆ@ÃU÷ô3¢m”ödk~Í>¿îž@%,=/<§äAbÁ®|Ç6ŠH1¼,ùÕ>NÈ -P;ã ¤øÔí1l’mqÛ#­¿²ÂÿDmf1.}ª£ô¼KÁ®Š®tº¶Û8îÁú›Â«n)2É7Ù˜R²Ö¹8ÝÛò[kƒõÙ§ëô}½¼5xXG)²WÝ´}.ý‡GV—t¸B­5 3z3®†µ,üŸ_…Â?Íí­9J¾{dà›Ò8 )é'·‚˜-De\(K¾ôWu_uêKçßk\Œ[ŠI×ó¡ôÌ3ú.è™ía$8“×¼öoAfœlò˜ß­ÂÞ ªµr_ÛZíYj|_tÙ:Vý¯C ›XáRíSt˜±›Ô÷½=XŠþkéàž®p))2{w´"Þôºàâí¾áðZß'¯šß]â6홨 
+éè5'&Öÿ).ó¶¤×5
+_gŸ†í6ˤÔÊ KMsÂÄT4DDp”sNƒœ EDÀYÁÉŇÔlï}ÎÕuö'9¿>Àûþñ<ëYë^Äk=»-Î
+\ëÍ|v¹PËúah`_ê1 s6ÅB™„ðÙ,+Ý>ÍN?Ö2‘6%ì`˜¨ïLi8ˆ?,vÚ×~Fˆ¡‹ÑwžÌµÜÝ>fq9tG‚í1áºfìƒ aÿÁHiŒMYcÊö6ð“\–;“]¶%DßSmiü™FÆ©š—¹-.
+:VqRŽÕeqGJJ°M™óòLK†ïJ‰SM¨û“ ™Oì<¢i  h{ˆl‘QCÕEQû_ÈÛÂüæ¡2¸´ýPĈsÜÒ v5gSU¢ÌZè\S:H[sÜåz®}. ‘¼Ëò %úýK[ãs5^C°«¨‘+üÔ‡³Íˆ{ã5q÷tmÙÏÆj ?[jmc•©ÇjvêéXÑ>ÌM¾žàeÊŠ"ÇëN#œˆ[ ío{°(é°ùvÔý##ðëÔCyž¿¾%Ém£7#`_žfåx®öä†^Ï º­JA³_±ÐN‡®÷Q¢®gËÞ^LÑ1ÈØ›í#*sî£Çºüg¥¹ñûR=Ë:No wf:ï &»Ÿj¨QV7ÃûÆd:øj±ŠlUÓa
+n‚;ÉÇ¡és[΀ÿn“LacM_(0rØaUHŽù °û•®$}_Žó[ëNt›nˆv’0ƒ×w$¹¨*ߪê3Íwæ^Ï·Õ_ÍT“NTä³0ÉMÁ v˜¨OvV×%ƒvåTøÉDIÊbÆëJÏÉ»Zøo˜¾øÿ¡œiWç"–(w%/Ñí|®"sKž½Ú“þä@ž¼/¥GXdEГ)FÒÞX1òsY²s3.ìpËiö‰¼i0Ë×4@ôÝö|¬*„ÙÇ‹
+ÏA¦>²×ÊGü3]kºÇ¯ï˜W@žéú¡šºÔûß—ëªÿ»ÑÐb×’c÷ehÈÁ—Üð= 1ìTSðm¦0þ[5Üénbb 1ì®a–ü÷ö§¹+ck—}¢»#ÅAfbïv罸3Û†ò»Z©.m$Þ1ôc®æk(ßçy¤s ùõ·96ÈwìÙTr{ Ö€9
+龿‰awÖ¥%;ÍI]KðÚ•U&nIË ëŸ)þ3­xÈtÆm¦ãòµ“ô|o„?Û™ë«kÍp·kY©ûRVÜ´,ú|œ hj¡„¹ûÂÐ…s;R=GÃI;TAW{“A‹m1Žsµ šÞ‚€7ÃPaÄ`y¬ÃRGA Y‚y``Ÿsã:òûˆ¯njkP÷W?Sƒ”ï±u=¼%¡C ‚Ï醔Fú©îÎc˜‹¼ÓOÝÒ·‡Œ6â|‡ß!@æRÈ‘’<V”'ÛǪ°‡ÊR”MM†ïH²½
+bÑÞ\LVå˜Å¤WÛÙ/e¤@›<?h¦yOÁ„Þùuo[ÄW¦<XZç:D ¿ghAºžª(пtœìËÙj’ésqôB ñéÉŸgê-G+‹âl£y¡+ü ˆ‘õÞS€ç¦ÿµÒÒd¯@ïÊ‹#vÄìx«„q¢Èö± ezoŠðž_{þVfÕ4éùž¦_œÉÁ¬vÏ×bœW>äz.ueù-}ÄB™¾ÇZnž]SÙ½7úsü7ûó¡¦^Ò‹ŒÇëiÌ ]êÌxl’C.f˜3­Yàɺ”€¿n<Ô"cÄìJŠ"m@·0Šˆþ½ ¸‹e¤ eU³Ód¨ûê7Q~ä-ue‚“ð‡59rg”ƒí #Ükp>ÿ¾^¨"ŸNVfûsCÆkM×'¹ÌÖ§ƒæêS@ßfÊpµ„PgaE¢ßÑté`¬
+=×öPÅÿì®®-Áiºê`UæÄþÜä•[µåÈóY^¾IH
+0õâ¶zs‚-ƒ$$àù‘+3¼ÖûÓÝÿÙ{W}®cV…9ž6 -þz¹“½ÔÑþÓÔøþr±4è
+ããHcýcVò_}uošt"¯Ã
+hüŒð;WUco´ML§´ldµI˜SEêRÝL»X®+ز?M
+H‰«}9æRŒ‰GˆßåâM¦§ˆ[`ølðý­D SFÉ83 «ßõB¤MLƒYy…Û}PÏÍnøÃPÁ3ëáÕ¹à}U~c¨ÄÿDÆȾ^oiºXmb‹))¶™üàíOo}·ûßù;ii)+Oþýl–öÞmKÎø±ÞôáZÃ!;„$°¶ ì¿Ú›ûzc ¢Ÿ!$Ÿi`›ùHëÃØç
+þÝPGÿeæp~™Øµßw*I.%¾Á…>ý2òÀ"À'Ê:rcÔý9¡N ³ôì ‹y¥¥~n×0¿n01‡| &Þg*ô`º,Æ2C‹w,Ve]¨98»¨2Ý%efÙ@ÇÍâ".d´Œ+«À!f#öf«’/ÍdË#uóS‰·~ígš,ö×t§º¯¶¦xO‘/ä¤íl°yq­h ¸¤tˆ}oãS“´½¹>{sØ#¯8Àʯ
+‚t}ùÞ§²ÊÜiõ{Ë%n¾<^;„ Uõ _î "oTÍ”}A]ÞÖt8Û†3Î5ÂÍÓ´Xó$.b­'÷‰y¼$ØìÓñ"#íR][r8}u8CL<•¶UëG«s¶úðþ+-¹ž‚šÄû²úÔ{—Ë­t‡¢­rk²*]Ú”ï~¼P™y pé…ººÀ¾T– lË÷’7!Ÿ*Z
+¼,"zæ‘Œ–nY Û\Ut»ÖÞúUÕP¾1„ Ÿ 'ÞÕC< ˜P§¼¦øVÛÞz«ýØdš"DX,Ä…²³Å%kÂëGÉ1+=„pI'6`„ž}GÑX¸
+i™/
+Ü›¿0OFïµN–†ŸŠË’w&P aC¶Ç$ð?®-½R2à»\˜ÏÚÇXOý'Èsó8*jw¼8Ì).»”Õ<VÚ=IKþëó6þZÍÌwI)à/}°§3Ìèÿˆ8'ŠŽ<_U7&\ÚD¥<¿;ÇÜ(Éï ƒpßÍ®ÿgÛKÛžóx{¤èÅÉ23²)×/;ÌýlhÈÇ" w¡f“¬s(a æ»Þ uß(ò6òˆ‘†ÙŠ¸F²{/ô—CHM?“Ôæ],QÓìl¢‰Weã—Äœ+Ê’ÎÕ4Øê0.8¡§CÈȺR–C.¤œÛ5&îv³®ÜÂgd¨:Q|Vö=ûbM願 ÙT$è'Ðaó5ÉϦXàçvI9ôf½šy£«kÕÏRg8àG–E"ü×ASÛO3§Úºˆ}«h{¬õ¿ 8 â.WÉY<l ügCqô‹oÆv.à4]ßµ$ø¹Ÿ´ÍCø­Î 2Ì)®ÉrnãWgºäläÉ
+cž¢D;ÄÔtÇ6úRJNq‰ [#0oýç\?ý(Êß%¯-´Šª`†1b˜eª"È௠ȹŒžy tÍÆ`A€¤|×8Œ‰¿]mo>–ÒÒl q>¬ë)² P§¼ºàDZ‹8° ºOȧ3ôt§„Sddžîôúh;2Êë“îrË¢ÿP4AŸ8ŬüƒÙªLý()ôz­‘~¦©A¹Lø±„”z¾LÎÜ2Õ¶ð?ŠËó+í4 ßwÎÙÍ$›ÉlœtÍšhìQ™$j4ØÐh`A¤‘ŽÄ¨5ŘXbED¥J¯Š¢AAE¤X2“Ùý°ÿÇþò¼ç9ïs?÷uQ‹‹bÔÊ(ó‘g†Vvbà¢:zí¹½ubæ¶î˽ӈÔý9tF`‘
+Þ“uBF©•·&i¥þ%VµoŸç›A¥ÅõÜÀÞÖðd)½ô‡c~ bÖyö•”òÍ ìcÇ[LæúLNXÇAµ<¤€» š‹ŠZĬ½y&Ø6Œ-‘ð_<´a*þt ÿÙðN­,RHE®sO·fí*x—„Or}¦–Yú1÷eœç?Ë9ËŽTZX'Â÷¼#ÅæÄ-JÂÑ#
+ª;ê7Æ÷”"ðE)3ïoû3Ðô¯«TØ·/<¦s— ôzFT#h;³½9_
+Ï,TX+õÎ4Ü÷ŒÁï&LBj`™Ó.ÕÜô/Ð*˘€†Ù1°¿$=3¾dh_—¤¬ W\OX)Mq3¶ñ v×3)¸äˆŽ é­GVÙ0†ÎüH,ú»RXc_ÎoÜž!ä[¡7—»«¯¹>6¥< =6p~½ZÙWwC;Í iùhÏ4ù‘çssŠu öú"ûÉ绚;gVZÓé:á˜h-’‹ 7LX@Kï΢Š<¿
+Ð¥ð­ilÑÆå‰é©pGÉ@ž¹ø,ïlsáÆäjÔÚ ¯tuE]¯úm|š[ɃßÝ¥½$ª%W½WíÁ?ðH°ê’¤˜ÏLk¶©|qåmÕOÞ)tVÌ$$„ÌB‚OÁª:Òð›·§ðYþy|ÁÞ2Ã:¾¤ë.ÿ‡N\ð¶ôÔÞ#Þ—ÓJCêκˆ’]ïŸ"†;êc^«_N¯p~D¥:Þ4\Û—uÔº§©`Ókè•Íá–4 _ÙïÑyŽßÑ™Þ1\^XÙ Û“‹=Ÿ[Óv>á@Exr¸Ô–ë“b²ƒ
+aùðÛu×'4(fx%<P ‰ACGÃéZ'6 Å?Ûkʉ¨pS»æ]¢¿¡”ýP³šÿp÷ Åì|rÔÐÙ°-i+\ýú/¹ða’oúàÿë*—T‹Oì"G‰Û…” ž ßU1+·Ú ]ãˆ{g± íùŽ
+û0!Ó+4í/uáÃË|tDK­Þ•â
+6>£ï»“3±[Žt¢vË{LºímãÈ2¥ð´ü ¬ ôm…Õ6“|J,hW†Ë ÙH-Ý+ØUãËÚ¤ˆ–ø
+¸ËcÛ@í?M½U—<3ˆ¾E\¾{›¸Î3¯¤³Z?ˆ¸cèƒ^ )…Ma¥°9,"|ÓÔâÑvÐ…DÆû饽 Ôý¨QˆwaÒ6†à×£ßßYåSOmthTGªpO£ÒÇÛ¿(€ÞÞ%ä¨Du—ƒjJeÂÂk‹y-q}ôp©ý‰w¶%mu¨î²©ûéGj p“`ºAlêÚt¨bBÂZFmÜÈm9”K¼óˆ\`rý
+FýùZoo@°ãáÑž„ï›mÏMhÙ §F15¤å",È[›³„ÇQëÐtÄ2<·ô ÎÖ€.0ÓžÇ4”ʘ†=ºaw™ƒ²Mâ@3äÊS[7Ï/kË÷IšÓw§“¿ŒÂïF´
+i1›˜©Ĥ˜ßàAU/àbÜ™…‡Žˆî)XÊŽ¤)-¬!AÎmÎ)ÀôëSÄB¿Šù¶)®pq#±òPŽ,t|¬N6½«LZùØxëÛqŸ_Ë@jûáÉ®q(nsÍ\fÌÌ&†T$°OŠÌ +ÚKBJZyÈÒ#\è©¿­~ Íëø¸¨I€;6v688¤3§³'§×'q9Qk齃c`( æ
+¿¼È%<mwRî× ±AC/{[Æk˜6ýl¢l-t£væØà€‡Š¶b×üO+©µ‰ÿs\æ_I¯yÿiî=gfÎíÞì–MMÝiê–šu­”n¹f*.˜;‚‚€²);Ê"*h¡×%ËÔÂQÙ Á‘}_ É–{ÎœùCæÛüü}~ø>Ÿó|Þï×k¿äØÜÛ§ ÏR5_æÚw¼ÝÝ}ºÏæ,6ÑwÖ[ÞÁÓ­ó­éa=6ô±$Ä|Ï* ÐÓà‰]Ó!¡<±Í‘ò½Ë”R`ç¡
+jSµEg"è-—}ß<½בêOŒ=]uw[TÝÛ~làßÁ2ôƒå)z~õyÝ@IÊþ8øJb þ í#î¾!€ÔCÏR€ÿ»]ï*÷‰QÙ&×%iýÍ%ÆÜ ¬’ËükœÍöáºà¯©â °?Ñu\ p‡¢€_uø¶þ†õmÕeÏ2"ûÓ.øÂ!Edù¨ï
+ªÐ¶Ð˜P´?=5ry_m¼q¯ŠøL÷ôª[B®Šhm€¯"›Ô&§ W\í‚$4´–Óm6Ý·Æ¢,öÔ_Ùà7çÇT¢m¾dš¨½t0Ó|Ë%m½V£ó>ï±i@/½NšB»Œ\–ÜwËéPó«Ö4 |NXå»eòã3–iDvbwp¦êgm¼€_ÝÆ€bZÀ‘ ,J\Ï¢Äô,RLÇêð
+FOm½<à$QŸÙ진4Œ&ùÀ¾Øp#¤@ƒ:zcÂÐð¯
+ÂJrå1ð=´ŠÏõÊÐ9»ojn™Æ+n8f0÷
+r½c¹»zssÝ'g<‹ëzÐ~®Ð+Çäl W¤š†Ë.{àÙ eY`Vè„M–7˜Òš^¼g•¥á•[¦åý}“_zV7þ‡{™PT±ëcˆLõ ,+i`¢ƒ«Hqœ:O-üq²='EF/LÙ›„Ý é¸ØíY£9fùO›£ðŒ¨šKJlqÉI#‡U“ê­ my.À-Ý4ø?ŠËü-É5ã?7sÍœ9g:ÓLÓ±šl3³c‹Y–[¹a– Ê""û¾#Š€¦m¦é)QLQP@ÙE\@EPáeQ05kÎ9óÌ{~z~y¯ë¹Þû¹ïïýùX_v;†ð³•ç@Ï* ëðUÉE.ìCLP‹.[*}JDVd†˜‹5÷>»lé©úi{ }ׯDç3¤gû "Ö¾³²Äü
+v.¬ãÀý“$È›'çã·b`vÆ ¼Æ]«
+L“ ¶wðó«Šæ{q;pr‰Ñ9z]ÂÁÀ
+,¹"êøìn—&<jÌÂDz”ØÜø<u<Ï!üwíÅ`r±«+bîä4Ìò€
+“½üKmšœvó„œréĬèΟ<ðÌÔ|#¤a?ÙTÒ
+¶TÔâæ60Ê
+ŽÃ®¯¾/þ—µçþ_S BrP/¡º†¹ÅkÃô÷`cfB¼÷û"ö«›KqO¡nzŸžw 4Þë$Äm•òkù¶{K/+ýÁÙ÷<#ª{!Œêú^§Ú›|£¸{ÞQbNHǃGLbò¦Š]fé†ýÇÒ^y2¨ÀÞŒké{V6<j¤@v¦ wtíÍKÃŒ|÷!ÛóžîŸÀÜŠ›¸ ‰¨»6)7 cVmM ®zbi|®•¸££CÖåÏ.®•]káÙ¡Ö³˜õå›ä|ßxDßFjZ¤Ôš¥•{Æ[îo©©%.9×5JÍÛ™áBc60E}´kÂjiH…Égï²ýUÑÅv=n¤U욥|ÀÜ-å?úÎÜ]—åUPk¢ô`Žò<e¥ÕÞT¾4€¸ä›dÕGfÙÐÅ>ðeÕ?ÎHà醮ºL¸‹S*aa5¸Çð9sï‘×\
+Øo‰ùVÒ¾S@Ù#UìLË–…¼¯=G+²•Qb¡¡–1)ã6> ÆçkÅNÏHò¿ L¡ï®ŠÀº0 ræãïUm s£¢ ëFô¸ÂuECÖº‘éÇÞJXYˆ”³C¼6BÈuö¡3£Z6úÛ²¤ó
+óþ"']ýsp¢¥4e—Š
+št=¨« ý¸ûI“
+hH… + ˜8Șž
+ùl£BÄ- ¾4HyœUùyµ³oSK©ZnÈtÔ^t€g%æðˆMDÚ6¶ãÝüªeEk­O#c¬OП{¡9 ýÈlç{è倪éᮑPù«§­3æ³O/Æmë…I‡¬+få!3ý™OÍñN4ÝŒÍ1ž¹ÛÄß|¼ÓÌjK/æŠoJ€:rI_üæ¿9^b6Ǧîx‹~´IKÏ®7?
+ZeëGr¡¡ zá‹«]ú¿€L~슿:™ä]ª"<Y}cSY{)îp÷]=Ã.%±J-+?õÅ+íÿæíV~qó%1¶ts™³ò11sksxVˆé85ž1j[Á,\SðÊÖGÛkšŽæ¨¹ƒ6 Pî1ìÏ+¿ /n~¢äµü:Ï'jÁÊ
+W3 Pkòæë~5¹
+÷^Á&&m*yÁùôÅÆ»6² é:ëÒä2ÔY¢ßL=ì ×lN>ºpàD%Çí‹¢•$L‚»À‹øÌŠ·¿À¸‘ÔSoµ[&Ú/­ÿ«÷Ò.àö˜YF]Œ×ó` ¿„q@äy§„ûñ=ùVlX9ø¸J@ó}Î;¦)zTò’猡ißíéz«Nƒ·è†3)5a–¶tâ®´sb<aÕ«’ž±Hzc‹Ì¦¸ÙXpKûAIsaoÒ0x«`G
+ï|J¨àƒ˜•
+V)1gæ·eV8-ÑbíæToUv…×zº6:v°¦ÆìC¸Ä² g{–Òž"]9^“ ‚°$b<p¿¡}³¥—ÑS&ëA0à‘œWê±JH9œð 2ÿÔnÿ‘S­Ÿ Öf\Ú'¥u6ïg,¢žÈ\_ýÁª”{üqx¤|¶˜´ CoI¶¦éÿ(yš°
+{²aWÖ!èLZ†:óÀ'›ÚÉRè©.b‚i §©ì“Òƒ° „ØyÀ[ù5¥$ë…Yড½rÆixX½gìp>'~™2ÊÉ9§:ÙBFÙ¼›KHY@/û=ŸÂj}I¾ uV}áxÕs÷ó®ú‡ÿ% 8kê
+ò"f0%a•Š~TZô‹yß?Š›Ä]¬cszèvD'¸»o”v…¦xu¡w¤ªè¥³ÉiÊÍØÔÔ
+ÒŸ¶¨Û(ø^ ´*ëÉ» à‚éq“œ°=Ǻ|M:ï{N<¿ö’r1:Ë?ã-<à¨î‚*ùQ(ïUòR6ÁýœcðNÚ4
+“’F).ë@XI ÃÍéοDçq/S®Æ ”«I«±èT
+*ÚWIÂ/
+[Š>„]"×Fõ=³VЫ!2 òGG¸Töˆû¶õPWhnàÈqBÑ)ÀaFJý‘o =o¸½=Û÷÷0Ð#æCÛ+JÆüñ\lAØ–wˆ˜‰Z—7QëÁY¿õ~ðÛ¨NØ’1£ƒeߘvwwksšZ•µH(i³”
+Í$™%Ž±ck\ˆ²/‚ˆ ®Ⱦ .±Só‡Ì—¾x/Ï÷ÕùÞ÷}žç·8‚¹œr°i¿îJ„9;ñVÖÆ@¸%ÄÊŠ´ÿÀ%ê>»÷«O«=ökµåUµpÏ£Éþ+{óÜI‹‚”µ‹sŽÑÁ´st0bP“?eŸÐ›öÏv§1ç3V&"iå"ÓVaÛ¡W)ü|òò$¨}R^…r1Ì@GÖ6,ˆ‡ÙI«º?m‘âÒ&NSÆ"øè92® +Nëدo©†R.ˆ´r3ö1U¬$ï¨}#«qóêÕˆŽY—²ÛÀw?lM’jÂïú®§-ÜæœêŠÌs¶§n†Þ¿Êy¨ªO«ÉºÅQ#¿%²Ày˜²Ã¤´ ä°gÛ:>Òÿ¶ïZhš~päzŧ·JXEè¬&¦¬6n â^sÒ*hݘaÜŒ›…­åU%¯ºÑBãÏFf±_ÆÉ7¶g(ײvNKðÔ–j÷N°¾){³Vq{ŠðUx_“1ñ[²6¦ì’1—Çž——a˜÷fà­˜‚gP’]’SN‚BÒI@@(­:OB0ï8¨É/k µ×¬Û ½syq¸ ’ÆÞÑùÎ/bïñ5;Ó]×’&jsxMÞ¥’æ]"ÊÁŠ’²RÆ6p?a¤ÖíLj=ÏQ ½Á×&ôÜÇi»’åyËAiH7þ0!h®ÍXĽE»5gdÔçç@¶Ý›gÜßœ¢Ü.x¤ÌˆQF Œ|{PÁÕõ!uiEÎ;ö úËN:"¾Ðùuø]ûÅàæéŽß©ÑöyÜ.ä}€Ž¼2æé:Ä<ñ È[˜ ¶‘æ?o½£#ó¾çÓ10o©e¸h¢ôÀ§‚Ë~ <Eõ¿°BSõ21Ñyüׇ go«‡‹k"bzIÐù©q›´;j‘+>˜]\â2ƒo0wý?uü£TÂA÷S|¼®¨‘ò]ÔÂlÉû$tÀ$U¯ÏqšÒ)ýtglª’É
+nQOÔÀjØÕqê÷Í|TqMLÿ“kNw%’¸¹UrA€É¨uÅ%zÃoæüÈ-¥UW•‚§Y±°[¢:fC~I„MÚh‘÷ø«…%vkqEJIYåÄ„Aü8²H­ó·ŸÝ™î¾tàá ü\ÜI€C8XebO7`þ¡fƤål2bÚ"@Çôœ¦¤îNš!,¸ŸXuËi¿ñ‰ ÂmŒw×ƨ E·˜PX•Róî'Ú„åßÿŒ‡ÌgQRbsbäî$ãÖÊ¿ÐB㸠³¸äàufÏw®¼¢]q½ÀŸg}Q´ò§Í|쮎õ(òMܪá
+Qò°Zã&Ê÷a=_ò?ùä®ÑÒº”~//Ë™)=£9ôº£fs]3ŽúÇg„³¿÷ýÔ|åctðEÁ/çƒâm©Çª¹øhC­©dpÂBÇx_a¾ôþ{n_ÏF…”°i u~g¾÷áOD9 À¼~a_ÑÂî ¼Æ#'E÷.J[Ïþ®ìRÐËnAWeUÐUp2…çp“fê½Â2È«0­èÈàÿqÅeqwÒF­ˆßø„½U¯¨dTè?ðl[l‘Ý’2òZ?¬CE×@cÊÒ]›0®ž
+%-if7ÅôÔ;;„›€è9Í¥%i/ÐÕ¾´YŒ^E¨Ý× <(­ð±‡ë"rÞ QAÎ#í[ä”s· 2bój÷-«Î6Œøkd[“53‘ñ2a“õìZT” ª'¬“ò6ˆ_dÜŒ÷\Úœ"_gõe–UÜ=“ eÏu$­OAßßÌ'íφsËCÂèBÝêÚÏ& ¼Ç%·Š[ ¨äQ›·c’t¦ÜeÎ3$K9 îè{Ú÷ÛoqgÔäl0Ì”ŒÓ‹Ûrv5'©µíÂŽŒ™‰Ø›¢ÔíNQ¯‡gè·sV˜PZR³csœÆÐkü¥íI•¼ÑTpг6jSÜH¬.²SKòœ{d(ª§?M´ý=mä ÓFqGBǬÿèƒy§[£?g<
+ÁžAÐ’óhUYÏسœwìEnuH3²š¼/Ñ—ØûE§Œ[T÷{dÔ†g%“&õ@ÒÄ@–\ttÊDk ¼"œ7È›ÿ4Ë¿{Æÿkï=¿É<Övä`DÞ)"Gf(ui‘s²Ú2.:é¶g—†¤¯VÑóQ‘ÖƒjP=TYÿ?ÅeþÔD–Àñ_­šÚ¶Ö-]«]Ygªt=ÆY¹d$\Ár;é„„KtvVQ0IÈ:$ä@’N:ݹ¸f¶öÿØÇïï‡î÷¾×GÛ…/ÂbÌ)êLÙùuI›¨–ð(™ÙÐÓÉ\ wh? Ø_îRïùÕÒ”MÅ \0; Þ5aeW~O½wˆ(™eX²1K½‘´3$œbæQñpD§Á¼Ï§‰å§¿a>…(‰ðëòˆþ„Y\i8?+¿}ÌÕSþWgÅá·¤{{!ô߈áß(Ȭ¸SAßûÒó
+óªx›³‚ª/cm?yGjÏ}žh)A¸$°C3ˆœnÔWœ
+} ÞÝ *ùEo¸dוŸ¢^?¦©»þ§'ô›gAVp0å¨"aN!Ø.œÂ)k%<òöµiÖÍVQÔÈ¿‡ÎKjò>Xø{H eÌ ""£áCÔ$«Í¸”ôœ»‹‹YdäŒKNKÙ$ —´=»¨`%íRrÊ%£ÆDàäõ¸CÍL‚ ·)©áIÖ-׋¦sñyaUQ²b “3ÎniÆ£ìÉ”r3î%Œâ#&
+ÖÆ:®¬‹ïG>*ɉ9y fQ¶¯MAåËÿ鼊ٻd@ìõ ÚÕÈGNQÜ*ª#–¤íù°Î,õ?Ç<zõØ“€»¤ÙÕÁɤKÍÌðË¢vEÞ Ñƒ°rø&ç7ô¦Ý:É~ ïŸ¿”SÉ
+ˆ@<tlÂÃy°ej½–:ÙžæG&™··ŒÂ&Ü¡þÔ=ùßW™"ƒt>ŠÕ¡÷‚¢à¨ðnÒýäõŽKѶÙCÔ*iÝ]VþXU “¤a{ZPe®Mµ_Åœâæ£ý˜´ólLR.}k»g×­šd Ñéó@ë)·2éàÝßØ÷éT~­*ç…¹3œ’Ïš VGÉç¢c­×ÓVfÕþ²‚}ÒªÂÚ®¬_ÁL€­‹ÎÒ®&æ:o&-¬"Â.®Ëºô4Ð3á’7§çä}j(ïë‚·”’°±ÊP»¬)nÕ2öÅŽ©W„ÙÔi7ÌBAf£ Òú”CÚ”v+Ú³H— üŽŒT1*®CzÊþ‚» ã¥4Ì®dÅ­ò–è¼¼!ú ¦lM
+ÊÃÕßn€oÝ™dÝN™$$pjnQ eý]R§dƒ|jJÚÅ 8©Ž[É×㦺‹‘ɦ‹8`,¢×f<OG¢¨Þÿ¦ö|øéûè ­0‡@Í€'A·–Æ™××gÄ$Àj= «²ué5åÁ×Îe¼G»ˆ¼·C-Ó¡wàÎ&xw£½]€[7¦9¥¦ÞÊŸÔ¥ßD§„¥À„ôÖ$`œYVq9i·’Ž.Ikc¤¾¾¯ùng†v-³(ïÌxGpO¯!aWÑS Ëâæ£ó@WnA3:G-I-0+Ij¿ÎŠ«q¯¾;<~Ϲm64 ?>OXé·r^~ý^@­H:»¸[s’jÔªãÆ,}²¸­W¾>;ë®9…üRsª9 ¼LÎûô½é%"Ô¨²¾þ! Ûg{_`Y.$¡G¦„e¨UÝI möHÏm?j“QQ›²-Ý ëúÂJéæ,ý¾o¸± çT,2 fí î†Eq«ÂÝ5áéÖí5º?ÖºŸ0 K“vYóÁŠFCøäì„ j[µ*± ¬ÍûeA¹`×µâ^í×±–ÌâÃÝ`Â5†?4]X%¬O´
+þÙà ó)ϤäÔ  ®
+:N>pêQ­
+]§4†Íìö°{¿¸CÅGßg¼*IÚ‘³>•8iW2¢&Ùô :NsÔÊjÏùäüÒÞüOùÀ?µ'&qÔ*êÇ)3‹Hi¨šÙì}ûαÔ}#¨a?ˆY‡Mó°÷»mç¹öâha9á„ÙI›ŒXB€— ô^Àv8Ì­Ëúžü+ï›{·òºÐ JMh\‰nÐ6ZCq{l¸¼qÏ-”šÓuJ`y¨Xx™l•w  n~Ü2ÖSÚXå}ÕDñàÑ‹ˆæõŠ!À3C˜E‚‹¯‘ë=‹-_¦ÿ°û¦ÿꙑ 8W6rô‘ßîXÄý)ð†T®ÓzÊÞI¨àáã&À ZÊíŒCF, Sâ‚gn*æ€F¶V˜•îŸi·¼/‡®~6üº/å—w¤ÌÒ¶œ‹9¥Ä’çñÓœ}z<cïEÕ„ëû¯ðWNWh• =¿ ³rZKn ±àSp³.#çQŽå
+}”ö;ž’/‡×YÍÀë8ň]Ú‚x +£=jâõ`ˆ„”t)Ù)÷â‹sã´À¿Ì®=xO« ©‰UÂÇübfa[ÌK»ÆÉApSµ´ºŒoB±K‰A ½
+]cÔ%üîœOHúõH_ì«fcN©èÄ*èN¹Uã`çÁÞ’÷ß’nyžãnVi+à&û¤òà«y9|u_Íê(þ`J:&%GïYw–ñ×"køëÀ™:³^ÿÓá£9ŸR~¤&W„68 ‡\”õJYa#¥.l¦ÜIþ
+™(Ugzzciwr¸Ñd‘‘1—”SÚUN¶ä‚¬Û—vp{.)#ìV0#N˜1‹ûϵìºýWƒW¼?<ø›yä>jdÄ6Ç¢V˜²½Ì¹cY$^Ž9•’R`f!¨¸9‡d8eൕ\ý¯‚Š.9-ï–QÃVawÄ2Å9×ÏrAž‚¹¢ÝÉúÈ …m^oy[L-úà±’{~!e’ ¦ÌÂnÌ,ΚTôäÆ؃ƒ×ÿº9WñûÀß$-ü®¼æ—ü³sYDF9Ö+ü¯ïÕ ÝÈnrÛKnˆTô*¹I»|4fVÑÃ`Oâ ž¸ž^‹jÁLêxMa½°#f…†²îiiÞ3%Í"0#nõÞPnDôüöœ æä\äákçæõÃ×pol ¼:Ö{ò±ïª%Ô„ôÐ<ŒmÎÁ˜c~êX õ
+ö( g×…çÙõ‘=šáb³p£ÿ#½tux¢¥xÇ"DC:Ö}ÜÄ¥n™9_ÕVíSrïGu|J`šZ´ü¶ê;l¦¹tÛˆ
+#ªÖÊpcškôþ9ûÛº ŽaZÆò\äžD©¶1ø¶E
+¥mÈé…ëcÔü¤‘KûËŠð&Ó òÄýY˜s¢‰À6HMSŠkŽÈ¸%KþÕvòp#*ÂæúŠÊ^è«K ª9õàz+²‡©öáÚóéƒ ®w éQ½¸3þ>šrÝ“Œâ½‰ª8ßDíeçЭ#æ—¹Ýt+™e!v¥ëE@+lÛ˜¤{¦e˜aÆ­hwÜ*~Žé¹4°›7Öä´ÜF@ éxdSý… ž}v%;ä%³îo‹@0³©{Þé'¦-Q³ÿl
+s:e”›ž)æ †Ó
+H‰Œ—Áne¹ D¿Àÿàõ
+9£#¼oýè¦óÕ|_ÞLò“) /è°ßxšß!óo¨T“ßG}•4ƒêÌJ‹‡¬ Cbxˆ ¼2hf¯‡F^‚Å|± ‰!!šèà>
+…küNÑIÔ™,6iY+ëU“AÏÉ M)±!ðƒYß@ÂÓU)TÌ‹¡–ù¹I‘=Ñwrïq”w•œf·%¦@höD&%?‘ Ø6`[Ï ¡ƒ$0„˦Ÿ8€tuÄýä¥ÛP(ÐWœ*€Ìá5?•-^¡û¸ÁFh,3ƒ!¶!ñEŽ¸€í“Äk×ôdÏ»RäŸË1—×›ä^DÏÒé‘ØÞ!)R;½2¹ÏÔL[í;ðN Þå¿^1O˜Élt™È´¨®Ÿ}î9ß2¬ü£é§Ã“£2tÞ†è$¦X/µmÞ˜¤´ëb’ïì ÆЄEÀ7)˜mÌ™ ‡E9:7ægä†@=ú‡w,x…ÞPk¼u¾Kõ‡w"Ì]øÜF2Å [næÅ ¬PoZÔó(>
+•jÍéùëU€k¥êK!%8IPˆE]æ}Ô„×&ŽÆÆ0É7‚nv„Ö4t™Ë¥ne#os‹£Iµ‚ÓcThÔ×¼9$ ækDãr7ʘÛMå˜Z£œL† I~Ä$ò‚qÙuùX×U3Se¹;4áWÛUi.ä{xЬˆ<ª&ÊSOñA_Ú‡KÄ =B˜šÒG† ïŽû¨P£Ã¥OÈf,M×äJÔGtZZ „A‹1PʾMÕ@A&`ú¾p§É0ôZù™0L  2!7(º þwü9ÅiË$‚ ªLè)­;JLŸðÏÚ²>#nBtÆ5A¹äcØÉzs#¸º<÷p™˜dYDù©…€p¼‡þÇÞÝÃFç"5õ¼ZCv¦ÜÀžc©T6ÑfìÜ¥¼*Åf78R€¯£S7#šQ¤Ëï÷Ȥ°2vˆå ;-H¡¥vì8Œ®Ï°¸_t…Ê Àí‚ÑLý#…Ôóã8^„¦ò{
+zÊÔ§ã^Aß¹Óºúû‚2Ý0ž ;ú¥­IDw®ëór¶ÏÊ6!Uü Z‡šÌ„ÀéæhA G‰bŒmM4l]fÛæÂPÀŽò_9[ Ɔ%€\- ñÀc ËåÞbð‹Uy`×Úq˜ê¡K’5üVõ8®Ñ¿€8›2ˆñ­÷I/˜å®[¶¡æƒ$¢~ÉÐh¹ó‡Á ¶¿ ?ž*ñ
+úøN¹TÙ²+‹åTËîæP„Õ‹ 0½ve?Å{ý|}f/èZ4– þè‘âd û=ZŸO Ã6˜ƒÉ¥,ñ°"Ý
+ækË ?ï^ ÉñmfÁçF8üÍ6Š¦:ëNø1aôt©òŽG‘@ŸeËäuhÅ¡o>€>¾CÆ_Š„¹E‹Õ¦®á
+ 1—&a¦„YhÉšÓìt.&r`”'iqA°Ex$Ö× óÈ:
+Qðrm°¾Íç8¦RrEúÃÏr}Š^º˜&HŸhœÖ„ej÷
+†¨Nr“±ÒB
+ÎÃZ^꽕î¿õû¨e²¬ÚtØ”$ü)
+ÿ ×pP+û¤ÿ…ç·
+†kÓ*ø? ·-ð^K|qB…„;ÚŸ|ùÇ1²êÉôË`oæü.YEc˜ô¦×ýM 7˪±&›K`Þ˜!”áÀôpA¼ÛwÖÿ3Á«³Ž!>ÂìÎ'%ƒUÂ^±©ì£.EŒÞ*UÛSŠ}§&0Ƀ&E“E[ûU? Xn°{I›ŠöIXHžÁŽæjEƒqgy÷”œh±e•™c‹1 $ØxÙûë,¯ ÒWBD“e[[v•š`ñ¿5T‘¬¸nÖ%­d²ú`Ø™ØïÔUætγ¼gŽß°#e heÛfGͶ"º-0…¼ÔÅ|–GdJ3—6ò)Þ>êRT@8%ì±Úw˜+ Ee«èÊtÀ«»LGM” ´;Ž N ›¥Ê[^ð_ZEjÒ²Ô‘é ±Þ$3Ž]²c`šuÔSdƒõ }èã|ÖB
+îÉ+HÝ4eaק$Óoʶñš$ç\·®€ØõÔÛŠr𠘄¡i¢€\­s0.jHÍ(^‚Ó |{ï>Û
+¾1‘?G¶•ü”ŽoòA2¶¶¾½ÂÝQ¤ B¿sÈdñX:è Dª¸Sé&…·•¹”8´Q‡¸¶¾Öówî”ΰʌðž+~xÔw9Ù·9®‘˜ˆü
+âV N†ñÙÿëT#ò#g,YX Ö ^t8’HGo¯µJèˆï<Ç'^†4ŒB)‚ø=rµH2/—Bldé†}§Ð&_Ñí[Û‘a E—¬6X
+É äù½¤X)¬1BUvsàUVó7Ä;‚ Ÿ•»ænq›\†ëYÿnÀcR°Éàð„æ]~C§iX8îA6¬v*¬ºþëX”3D,ƒÍ}„µ,k7J¥GE’*¼›
+‡u)L&¾Y —­Z }sqFÒ ³Ê€8tlヸ!ùg´“:Þç~—X­!¼‹ë ´÷øĽG´\tø}€o·B)]Ç¡N4ø¢ìª£×IèËLYÓŸñ ˆBbk¦úI¤]ðNòÅÀuÙ4âÐðì ÒG³¶¡R,zâ ä^ÄØe7¥Zò៧"(ƒ%E¨¼‘
+7–:EIÓ[ÁþW8 ——ŸË A‚YÕX‰$"_§¸¿¬%}'Á8 $#:tš^EçàzãäCpÕJtÙZ¨ÑØôþœ¹.Ç*ÚÖè2à• 81ÝÓJ”'˜VL°Ge¸® ~¸=(ZIJӛÃÞk0Z¶=Ç.ç&ªËŒÌŸ¨9q¼
+ŒúÇie¯E¾²¤9Qi­Š(!¢r0'-Q¼F^‘0½*gUk¦8„='‡%'"ªt[w|$yJv¬$IÉXFNaÌ—˲<>»s.Aôf; Gsõ³ˆ¹íl
+JŸóíp‚+ÕâçñU”ˆ˜º2ìt³Es3ˆf‡Òk“Öç2ÚÛ¡Å×ÍX£ê¨6"=¥4÷’?¯lØ¢4tæ1”w€,ð[O¼Ôh46[Ĉ›â7šq‡¢cê‡'R¤c[QE%!ȈŒõrÑa§Ë”´3ùåÕ¢ãæs“ÎC7åå¢ [ðRÛFzªØ.<yËVtË¥èó•ÙýU­Rd¡é‰öCñüÚ^\Ö4Õ*U|Ê÷2ÿEŸVR°|DÄZE”^„?œcß%MA®ŠzS¶’Ä[¸$nm ÒJro¶Â†‡d‘v§²–˜ÙÔí;P7€©ôÚñÍñEœ<B~R’{¡¸Ö÷I—rs+Ø%qÚg0Ëf?wbý1[ë†ú`Âæ%¹Mâ¤$¿Žë¯’ph¯v¦ÖºáœÕÂurUIû×ñ{—¢cÑ QI6—<W½Ps“z5ð•¥^ p’-
+þeÇ(¤‘:ŠÔâÁr“*v= ©0Ú^]‘+]OƒA9™¯¢J¥Da3
+°U/3O¾^îˆó™ÊD1v ÔÎá!Yïæ.— 1ÁŒ ·'ÃÀXϱ尳UN^ˆŒ´8ÅX³æÌÇ9ʹ†Qn* Ióë (<î†ØVÏ‹+þ‹9]ôWêf¾¹‘¨Ü]Zøp§ûÃÒÎxÝAé¡m@øNóX›½Ñ{4„ïeûꬬGüº‚¿¸?­ë^Æ] €ú¹e¬ÊLªaÑd=ák>çÄÃîâvx§œÝi~hK"çîa|¥—¨Ë¢1¨Á >3}×™‘¢|eÜ×s†è´/æfWwÝ s8êñ”
+8m¾>%$ó賈Äú.®/ ç'
+lB]öƒ‡$Ì@ô%Ôejáy7=-²œ#Ã…ã1d™ÍUàüžTÊ‹}Aå?Eô~§t{]yôÚð\úVbPxÒk1#ÃûcŽ1Yæˆ CCË»ÆPb–+STv¿©Ô ^ýõsáÕº“]=³øK²“3‹3‰!é,»:ì
+)bë¤|èŸ½Û‚Í ÐB
+ï¦óbäǨZÄ_TBCÆZ®An»N"Æ´(Û…˜ âçï ¬RËÇ ¬
+É/Ù—Ó–ëímI¸
+B€,vŠrÚ’j*+)÷ø®˜7Pxõ×ÏEõãìê‰h²ù_ŧÌhÎÕ1Ê»!£¸üöê˜Ln‚}lèü–ºÂ“0Âè$F^_ýUê~§t{]ÀR7PxÒk1£ÆüXñÓ˜¯ÿýÒn¢4g‚¿/¦Fº1µcÔšäPR%ÄÁ4~ĨÀv©»í\X-¸½M¡©øúU%cn;ƒÅËÛ#k >‡^Êẉ#Ñy—¥cý>Ÿt’ž¬ 燸Æù@¤¿J4Í.ó9_©ð“ÎÙƪ÷cÚC–àœë2ü)Ü™ä ’ñÉxò]Í—ò¦¦e^TÆ2X\;âÃÏp^›¦Ÿ3WÊ9Ì$¿øX3L”x¨8Õ s«¿
+$n-Š’ÍäzÂ÷¯“e<ÌFsŽ§ @xJŦƒkMûÓ\øørGIøâ_ ž-CYº†<c!÷^>Ñ÷-p4–ÍËÇphsÊoæûÛž^Êœãp
+±¡´™Èxf³Fá9 A©÷²•_{{”$…DÖ*—|æ¼È]â!O0:n›5`9 ênÇL|mÉxòÓn•—,È=dW¿‚£I°
+·›¯ý÷bþ‘à5Äö bJ¹{Û!˜v¢eæâÚá…uÌ… •6†/ §°šœEUÆ@@«Ï`pPvfa]Æ&ý¯†¨C4ÃȨaI0c0Þu©n6_z”iO–é#Û9£uÑ'ÚટÐa¶k¥úR'D”±®/½`Y(š¹Eýò¶ch ®gÀù%põV>9+:YLª_ ßQ#^AÏOºõ‡œùh]L
+>7‘ J«Žþnôo höoŸû$ò[¥cršFùM >…ûøÚ–p!êµxâp™Šˆ5ò´I º1áIóc~Q ³3.È#ÀL£ïžd±´@z’ÁÁO÷ÜØ^˜šò›; ¸<,6ƒo÷Ám
+OY4îûWW3öJ˜]¼ Ç”bÇÚG¨uå’¤ï«-—'â—¢LíÕ‰d;{.Ÿ()IŽeèžtUB]±“ãszÅÈ15’aúÀÝ7,ÛΡÑS;›Ì ú²æè©À (”NSŽû9øÿÚkJüYòÕêmñÇ8s–ÂG”ÙTb™©¼ëƒg –¼Ïìú³;a¯µî›@„€í™tóÀH#ªÓHº”³ M­‡WSfQ-Ÿmâ¿¿íI}'ŒhZÐÓA¬=¬clï¦l}ýˆÄð²'~{:„›b­LiµUoj²^À!øÁ!¡•Å»J ?•õÚ`ÙUf{Ú!hÕ\
+›Í3ÌGÑæpBÙT^ñû`Y;ñª°åR¬ùÓ+ÌñNÖ »¬[q­âÕ²-ËJ–*(fgë– (ɲU»¤¹zÆÛÒ£i_J7ÐÈgš\Áw€–¸£C8òÛ,E’[ºèÚÐQ&©çQ°
+£áùI O8f2Σ?¼"\?ü3ËŠòMµêÍ)Ò–Îþ`r½¿Q%N'ɼ(âïß‘Í£®½Óæ+)¦FÞHêÚõ-$lø7f¥—OµÇD£ v@ *—Ð…_J›ºñ~pRþnQäCžwŠŠ¯+à8dz^¬ÄEá—^›Ùž¿ìøLÃW°êr è¡­ÁÚfpx؃is_¸)éÓÆ›[5L
+j[‹(!’[ó
+äÛ“s‰æD‹ÅZ÷Mx0‘qÝæNtƒ<(²ßóÄå hnY7tK£³"gÃHKSéÎlô-©¡x”²È¾ç Yôb]Ht0G²Ó(;ÃIlÙð½Ó“•fç^pJVG÷2æðÍl ²t©£3:C®Ê^üøî‚Î:ùÁ‹pˆ¿Âeö-•ÜÑæ¾OÎp5¥›âGd†w÷{ÔK‘±â”
+CQ"|‰cú5™pšJs%š¯T­˜›‰HOp=|£”Äž/À|éGÓ¯
+O¿d’lÍ¡bL÷5Y†ƒó—¢ŸaQô‘!È8µÓc¹{”µ›ß†O,î_h‡/8vûÎ:õ¼É‡)3
+šV÷þÁÞ¬ÛØ„ŸÚ×r#b€•£ÇÖÃ'j^J|¿Ilî)Åß ¬(Á¹@Llð›’/nõ×mj+¼ŠÖ°Xø§|áñ­Ä¶×íi£üP‚UÇÐi”³ð ¸kÞžeç¨Ô¼³ƒ­¢Àü+äPö»Èé.WäŽýg‰Ù· ¨È™ã /JpXÌÕ}˜nÌ>c;æ©ÀS2R´©µg…4‡übáBÑG Û‰Ù™rl[ Ÿ7¡„u-kü"ŠPwfÇwë;C ¤À°™Í\a£’ÿ“ŸôRùU¹Y4aÿˆ.£uæé£Ây<
+ÂÑÔ:±ŽDvzi¼UuYÈKÍAƒ›.Œ3ååx‘pL/%>§"Eï¤テÁè´K\v/#Ç‹©Û[?±Ç£L^œÎzöî‰ašƒ@Ìõ­/ÏMhÒ$ãp¼ä°äë¤öÚ}ü]çñ ÐDö ?†•‡ÊÐÜ<OžŒŠ:WZºÔ\ûR”Ðè‰'Ì××M¨s=%#ù
+<ÉzcË(°Â]³‘ÿ›Ê+(ì¬ç3 9Ú^âTÈÇ)’àNò)!혴נdŸÄ*á®øö</ø,ÍUÔþV¯cE@øê‚þç)éôÄ$ u©Àñ¤‡ÖÊŸÍIYšÉ¨Æ:ß%×:,ŽQÉ—QùQ/E¸ØxÆ¿öÝ/Ç¢ŽIu[·ç«X$æ‚òp›ÿõµ;²’SNH"Pý;˜0:•Èt^’•@›zg5ùvA⨰šQ+éÝWä8Ôùæ.øEozÀøÙ›Ç2<;üWKå²÷x:;dvNLúîÍT‚¢óxøt(5÷¤ŠIò¾Ø¿LY±E~žŸÇcßæZ÷ñ m‡R‰.Y¢hø‡pöš·OxW>‡=pA2<ÎPŸÙÒ7%²F¤*‚‡Ç‡gQ‘nàê€ þï\y1žEšª{”È;`s*˜Ä‹-tË¿¢Ú‹gý¾
+cM£$Ï“t,»^•äœR0³ ê§DkG¯øYD(_ .@ܬþÈ7¦|ý;µðs˜ªúîU±¥b²û1üÑs°–$pßÚã®P‘zžüª×¸µ¯Ñ„ž5¯ƒ~½É-¯ï‰@÷h˼´6#
+je=%¦Ô„—g% #šNç¶þ)Ø)K¥ã´häÇ *²ØÛ®Rwœh•¸ÞÚy÷“OI˜äY—e1#:òˆvJp¦oå0&[¶ÐqÈþ\¹òKxÄú!õ¬üµ*†¾ð›SBt
+Ï;
+¤`´N š¢ñ—“^iëQòB~Ô£"@N@9ƒ€GÝ™æxc£Y#‹Ç¦7P)N~¹mfËýS.†æ0â^n9–Ç# ¶N‘-ÒagÀl ÍBYšÂŸ§(Zep
+|BÇÿRrý )’sLJH†kßøÚß-PÅêìÀz÷*þ ïY—LF5–S͈u¯ò +íMÉ—YIÕ~ùÇì0‰Q]üí%äÝ.ØŠóÓÇ?Ö$n;Sâ M|ÎÒ'÷i…%D£ª”µgÁ8Bçô æ· ¦mo@cñŽŒ€šà°×¢Ÿß¹Ño[¶ÿöÏ¿ü›WâŸ÷» úÜÎ?C§^È ôß?~hiø1ËïRVT"Ûrá¶þQd‹\É‚r7ƒZõP0ñÉWÿCy™äÆ‘ÄPô*>cØ÷Ò·è­|ÿm¿d¤­ªLC-À€¥úƒüC]p‰ïƒÈ°x@¼î`½m÷-P%Hœr+‰1sÐú n)xQ4û.赟ߩ“ªŸ·iê„'Ñ%OÖ7÷4C€`.ž5í¿ÄÓ¿"‘ë´!F`,ƒŸÖ‚˜¬.UªC($“Ô‘‰Öv3(gÏ V-²¢Ÿ
+­•îÒ’¡0hCl…7⇇ÄÈŸ­2±sè’×[n,sàLLš±UÖa;ö`û‹Öe¨tdŒ;„†x¬9„&­²ò,×Vh3l„îlj„:‹?›Üy¬“¬ì>RàªKï¡:·zƒ˜Ä±¢c<¬ÈTå!¦Îõôù
+H]Œ#ýt‚"öðNêvæI¡‘mÆiˆý±&«ÀüÝÁs3UH5RvvÒÄãdån‚#>õk%Lv¤äלhQõH_óœ¥í>‚1ë
+–j£èe‘´`?1½år1t¾ÌNQäµu–HÙÖ冰SM}/5!ë…3Åiõ[x‘¡èã‚ÎS.xà€ÐðFó@¹aðea Ì­ÚyômQ&¹4ϘOÃEaÆ#‹¨&¦“w¯5¦\ ©‡Yquìî>.Íz‘…a‡7K‡ÖìF?‰bƒtpß 6c8DÞ·ÿHB˜“¬tf€çG@ª!*<  ¥v‰ž{`,QBÅQ2$%Q ÈÌHºÒê³ÌºjÙ€p}l"£æßNñõÅÑ–BÄ ”è?yw£ Ä•'/õwO&û–“-þŠM–YÁýLF/;Á`¹~9(3ÐC)Ãn0?ï1'ó¬3˜a²Ë@Ÿ@,Øè'™¨U*ŒxãŒS&\ædà'œ¾ºÛ.3&!†Òþè­wÛ}ôB¼ŸO fî ©ÝÝ@:V!«
+D:@x<o?WR &Z õC&n
+ãm‚/á8d,"SÉàÒ²½ž :Ì(ØᨱE]93N<ø8ét5&5AFØ6„ËchúP|ã^`¾‘|ÂÈÑè8!å<!ÈŒB-míúe°œ6ýæí#ƒ ‚{嘫Dª{üëm »ÖžÖA˜zlθ¶úJòƒ ‰™|ðº9pl… õèq"³P\‰úÖ²hSÖ#_'®bgúRtŒ ËFø9‹àìáê΃Mçø/>8¤ˆëø ¹T’\OCóÇÜ
+§©p †ËŽŽ2 hfU£‡`ˆ(–)›Ä0H%zkœ{Óš˜læãZ‡^Ÿªç<·âÞYq¤¶í8 BéÙuÂaŒžÏVj%ô¤Ù*g+„‰iúè>€òàäDö¥O
+­FKˆa•q„QANº*†;Š—…¤±­ù:˜o.žÚyJ8_¹Lœ]¡P‹CÁNÝYÊØiàMÕ6\ëvz‘K™1:>þ4Yr¬ï;A¡ë³U¦/x¨HlC01,ÍŒeð14†ìè‹ÞÛÔù£?@äwòÀÞ¯Q®þê/~ʆüøGPRŠâ7SÓ·ŠàžöküzÀÈ¢ÒtXÓØ
+‚tò
+ugN8[º;Ã<úÉ•«h­“ál~¨¤eOÛÓàça?8­íÉ¢ÿ±;ÍZ#¨Ò\|e«åÙ g©<;zÀKtƒ¬˜7l¯ÃÛÄáœà6Ñ1&píçPµè&<tòö’ýBŽ˜P@øvÆ>¯˜P|-ÊÕ»žÅ×Á%ˆ}kñÞ
+«ÐcóògûÅN/úQ)ÃÅÛïí0 I&¦Ô|{'F´æÙ•ë,µS¾—ÚÈ ¡¸Xš«``˜b"ŸxùG:ÚÃ×A&'r›Ÿô”\‰–åEŸVhÿŽn¾¸5½<§n Ϋ'S{. z²ú1J‰¡|ëK ^+í‚;»ÁZØΆ0͘'¥¸²x¸ñT:ÍeJLoÆ&»1ýЛLãøq3Ü<RÎÌ…ÖõqzãˆñÁ#úÔ Å»vúÊ5â§Áì´=÷…°t­Z·?¹…|m<µä 9ýLxíáôÃo6”IÀl05ûò@Š ²tï\ C& 9„­ *ÁçÜÐÿ¿¾UOq“ê¤ÄcLÖÀÌÉZøÝ©LËæ£æ¼1‚ŠKè-žBÆ[¬|ŒÇT,˜ýw)1®
+ì»—äÕóæ-Rº’į£ØJl˜µ‘EWðþFã¯[‘Ù ÌÅù-¦Ž<`_?UDQÐfË·’’èPòÑ{«RøõœÏ¥ž GÂØý‹ùÿ¤ôqyí`)=À¶ñR2±yà}3ƒy*z æŸ*J\<̘̈&ÿ¡ˆkF-¥0ä7ŸßCÑ3ÖJОm+{ÈO%Mx(Bgén~†<¼¬sq¼"‡Æú\R'{á;­Y{ÝŠFVÿ(9_‡™^º )òÑ>5¶^rÙÞzÑ$1D£¥~oõQ$uCQ@»ú¶NâÃ:Š<–½” ¡( r#µòÖ P `nÜ8ð¥è×[ÑãBGè÷s‰Y§»ìò­dp=Yú9îQÿ(*X9^Šøò²NéÜÏÂÑH /%Âà*­’rß[)é
+5¥‚W¹ç—
+<
+ýÇ:Ó:¯Â”£íÈRM/ûŒ@\î1¯CäÌÒ©Šâ-粑Ûð4n  WZõ FÔ)Z²¶8”.·pÏ-ŽUº_}`¦¼ÇíÝú†jž[‰áIR§ªg“³ùSâ9 ˜ra¨ö’A.jôæDã$47vbvh,­×Ým·âÅ.Äî1/jMRj"\zÉR¿ð´7Žþsnƒå6±¦ëdÙ-ñG½ä^®ïH˸3±>缆c4î:no‹T¼ƒû²ÛÈF¸C¨¹ò.OpnÄc¾©{˜’ògòïp‘F…¹~$Ýw¹f,Úœ¯koLÉ 8äƒPݸ˜Ï¥»PjåÙß:Ô~(JÎ.ºŒ¤Ó PCWææGçqŸŒŠÇ÷p ò¥2‡—ˆÃ$Í-Ô¯2üЦÈö¾dÊ3n¹î—¨¼-’”„Z§@ê• Á:— s25ä_ö’!¤š4€oUÔŽ ˟ݧ@›ˆÈg…¦+û`Ef¾UVºãjÖì¡é‰FAkˆ]bŠ0pîhÑ£ÿŒ:¾Uauwš M x¯S/Ð…nǹ»ÊѲøÎê©Äwêc TûK£=xÇ‹¼ò5š–α]')›Ní”ÄWÓ¸½f0
+œ®¯4¦ÎmôûL>LhX›Ê(A=9„áíÃ*wÕE<tÿiK’¤t5ÿ.¡CÁî‘Bµ°'¢æ1öÀTÎP…}ÄÀL¾ÏК®U|+SôFÀûš.( Ó„½ž6‰1J0‚óÑ^*¾€æÏo§¢ÙñÞFwÌ+vüç¢S|;=œJchÃ88
+hÙ”“Lè-ÀQĨä)ýxIpek$£Y[?Þ1à ®NS¼&8Š
+qSó’û9ÁQÇ1='8æ&¨C1&Lü#ÁQĨTø ÕÈL‡\³Á•öï¢+
+Ç^Ïh}tñ¿±€ñüÑö“xñ=µ¿'‹Â1
+IÄ€m _[UP—­G†øš°Ô6Ê°|d Pù’ÔvûMѺJ`J[‹ó®UbúC è@˜¸Ñ[}&åFC›ñ6­¥SúÜóÉ °3tß_Ø_#$-+tNµó:h½VÑž–QþŽÍ eÍ­¤üHWgÐ?Y“ƒò”£„LV|¹wqFÜ_·+Ö2\&™Æ̓B¾Ä½«‡k£™ü5ÕT
+O€el5¯ Á¶ Á‡L.[&¬½U[!c@¨ëbÊĹA=W wP¦œôyñ<H͘!æly†muçÅ—Á’Ù{HMw¤ÈYY³Õc ]>/%€B:ýùí¥ˆÀ[”ÄdþéÌ;QGaÿ¬ùõRc•¥‘QÎu4aæƒ×FŽø)J–fl ®7¸"“jÑ}ùÄLC9Àé´ÓóQPor®ÛbŠ@n®[œ¯M¼søN« ¡²0bWðØK¡ƒ¾¼‘z9¦æ—CËòœÜߥª¼z…rGróHw4rÍîC;ãœ4Ép%FM—r{ÛZØ@pÜa²þ½¦öv×Ëú^¦:‡¢›ZXx]||akdØ”r%áÁ°¥0mx[‡kC:šXÒ?yé9q4àݹåÜr/‘±ðv½á£>0êcÉõ2¿äÕdwU×(¤ÑåÇ-yßL-Œí´qÕWx˜÷1xųÊî}Ù,9ˆ—²ºÑž"ÃÛ˜zBˆovñ *Äüû«³ñÄ^}3ôSÄÉ»—%ÔÞ+B¿Àœ¯Ï¥Yo© ÃÀÜ©Çå³.L9BrhF²‡Ý%EJf2•-&žfæWŒˆÚÔ|íV?Ìm6᱿”¦¢æ¾p@4¿ ‰Í‰‚—îÜ_E¤¼$Ë[íÕ½‹ñÚÚú|A‰ÏÇZ °I¸¾eà“ÎÖQßPê_‰LìFÊ*1ã ÜS+† 4‚\4Áï]„<’ø¦áRdD¦ ½®+DÅçPÚÍÍ–—`ôÍhº¼¯O&h|Ñà@Æ/8*jÁ»áZz¶íe8 3c1À” a)„ÎYw 
+p]C[b¢¼”
+.ÙÙ¡ª¨Ë¹s½kò}X«27B,–†šì¶]"@œe ®ƒ 0ñ#”nš8fUNò«}')<rç`C„¡¯RNÓ ’äb£v Ú÷hÈ/E¤!¾^º™Ùy&O¨1Gñ1ÊŽFËœw þ sÅá ÷Ï€"Y½ÎrAàzEçú°~³Q%L£@c…:m9¯&rx»/[ñö£XÑx÷÷g>ó“Ô«Ê|¹$<VˆKÕö™$åy,-²¹=Þ Ð:Ž‹aG ºÌê{=Jòåªý>Fð ³îï@
+ã†b¸Zô݃M¦Þ|rª6ñÅlûN¡ÞuÊ S‘3¢£h¡.âK~ÓuÔÈÿ…œì:ÊSœ¦e+¬v&Ù;Q<÷-ü³¿Iöí4¤Ab?
+lÞX ®++ÚÚ<€~0"D*Å;L_ÓŸx$¯* †ÉñmÍJAwÓìó[ù."¼Œµ®VAq3ë‚ ¤v$äºÈV”¼<áfuX¿ILýé÷Á)°HâtáÅ{óS<8êºÉ]¹[ä—ÝÞÍ”…e{r=lƒ4qO¨séQ‘LHk²¨g çê86¼zÞÓ¤׫;dŸR)%FÁ­ð×ÓgnB)wSÉúƒ_àöÔ›ú¢î~ü³Òo„D‡h)(l‹ƒ¨ãK þî†
+…÷äÜC÷ˆÊb*¥ÙüœžÙ¢k¯ñ˜|€4‚?ðÊé¼U@¸DF›K²(2¼g‹—©s¼»e,¸Öm]1Á¾¨g„ƒÝ›­ÍàR±<@°Ì†Ç¦<ðýã:ífø’ãÞÌeþtÐëûqà䀢B>Ŷ©àÂð j‰ÕgÛ'kÎsg¶ƒÏÐÖá7½Åº#¨ÃP<>"=|‡hAÃ`°°bÔ ‚I`šºçóQbÁ4c¢m˜ÅweÈ/»Jè3øy–ɽźDËJÓ‘@$›«ã:éôN'Èkº9Bþ4(Ozm³“KÝgÁ„Ì=e\Ò†íÙ±evR‡ø
+šNžËÌB´¦Å :À†uH3à¬jˆͳZm×£Pš¨Û—Dƒ^¢NÃTh¨*, ìû3Êb…GÔaw!Ä x!6Q7%ˆÜk/Â2ðf"o›W’¶Rï
+¤:¦ÌhŸ‘aAhë;ªMÀÌ°oÉ ˆ•âãèeCP©0µ’ݦ P…åš²àl¤LÀ?0!îï 3Ô’‚MûÌ)äÄ6½’
+sT B¡¸ açêÂÒÔ’ð£uÔübîGVe­UÛNÑ3“±?Cs!ÐÝê‡xsIî²Aª¨éî>Z‚ õìÑ’ã’y¾NYÐÎ`ìÏ°;ÿ·!ÆTæJ2)ÃNâ7x@®·9]ƺPŠ®YûÞåc°´ÒäJeœ?A3Ë).YT#Ô‰(ŸçC¯&מcyRAP¥Ä°Õj{Çêp£>ÖíŽc/5éÁ£“"-á®+ÞM¹lŠ²’mg ¹E`†}DZb;ª/<oýM›Q–=ñ·àËû±uÀÊiŽSçÏt6šÒqÉÿtã˜1PÈy4]€ %ŠX9$UùÚpÆ|mq»Ž[*ЋÖù°¼¢9‚b®SSzF(ˆ1yt²ù)¯.¡f•6é
+]â™&¨‚çÂ8îö+Â:Äç¦t_vïüDê†_c3íá)òš.–·åï‹íq
+èUÍ~’üN#gÂ2dô™nC÷6¼Ò§ˆá†är±¡cÚã‚W're%Åb3÷°)p+Õ„•Óñ–Ä r^°%¸­‡!Õ]²<máÈW2æ-F{[¤ýÇe‡bÛèdŒ |eÇ2a²2Ãן „oâûΔ˜¿µ^<×hD°Ü0«qª+žDV|¥¡3˜!Å Ñòž<g»ŽŠj–,wr»1äcp¢ló® ÑCLÈ?}‚^ùÏ;öþfÉvj¢fo§òÕèšÒn ƒÔÃE"L,œšè×ɬ}àç¾·íË’6†®æìô’—í#ÝR¢*>¿Òퟠ_ H’¬ÒÖAþÚÇAðC–þ?ƒä«ðÅ<dYWªÆ–¦ *_Ó:¡¦ü1èt"G«±€lUû { Ç}
+óbè˜Ú–ý¨¥t|s6ë²o8ÆQíʨL“m.Ö‘L)̇s1ŒT1|?0>˜8¦ºhįÊû¥Q[G’µÏrzû;Šq˜`XÓB:Yb¯Œ i²BЮè
+íhœzØû²€ø«lÏ_SCص9㑨'C–6$.w4± ~T­\ oRK¿^ÁC¨áÊ•Œ;eåWþ® +H}Òð£˜Zñ(Nͦ Ø´gµúQ+Í઻Wš…^P$8ο¸/Æ›{ÚèP 6“œÇß(–ËMÑÚbG¡€Ë`«7›¤e¢uF‹÷wjä$dûd”;)ÓÄaëY‚<:Y¨¤ÄŽº-Öý;¡6j$ÔçWe¶Zìñ-^@žˆd¼<idª#ÿÊôá=çjœý/ãe’ç áø>çaí,}åþÛ|E
+<¬D~q±ÙÛoü‡&§ÅMm ¹EøðèGòâ(î·cÀ JAd‹ŒƒÅÌ`ËÍJ ªÊ0F}Š¡~UU,ÊS/ó´ÅE| [ãzI4„{<òêÉ,ry¼é¹âK"ûmÓ\„)”b¥q+úxSÄgìZPÌt@–è9àéj˜Wm•"0gÞ*AшÂ$Ô…»Rû–ÛëôKŒG6ÁÏWïßÿòbbçh˜,{zm
+
+0 k5[·?;à] Ål
+zÜŒÒXw s5|¾ ÏÊ~ù+ÁWÊ]Ž, =gÒîT+ᯜÌÓcoHRÈ“>:å¸
+iĵBÍ£<-4ÊMl:f T¯ûò×½SfHoVô)^øŠ’«"jv)Z
+Zät®b+ŒÑüÌ4° á2ºŽiœ½ÍùØŒ*åj! ¢ÿ,ÝA³ò—Us!hRß}ï”°âÏmAñlHV¦ùˆbÅñ…:±`P ÞgU;æ<Šýœ†C‘3`ff‹
+~Ǹ-ãÇëϯ?+0ÒZÕ÷M|'ð®ü¼”É¢bh«”ØP¶$Éx?°‹3@WÕ€îîÈI&œ¢ï@/R =­:!Âv9³²CŠ¾ÝG2c6Ir·¸é ó¤¯ê¼ˆ øѧâ6
+‘{=_K(ó\Ÿ0Ì¢à;Kûf/sEJR9lå¡p.Ân9åíë’8( 3ã›MX
+*ÅK[Sà½_—ÿIy»‹¥®˜7ªMrdNtýÃj°!àGLÇÍ:¦Dü•—¯${Ìöc:bàè$Ï`êµZ ¦šAÑÃ'žø|-‰¥UºÄ!ðo d¨`“ðÎEiIb4]ÿ«Âu©W Õt+ÄРU#ðkФ G–²Ý‹h[ô}Åd[6u&':þ¥˜‘í¦ƒn%¨`ËMš]©›N‘ !î‡5|ž7bBÁ¯Ê¡ÑóÚ¢æô}5O5aòk ªdÍfC;ƒÉ¿$¯¯EËbžp­9t…”`²:ÿx×ñí¢— §Šá“ñ¾§Á{òH4.×·×}£¨á4jEÈbäV¤hv¼À:h¥Dÿ*A î;Ö¤Iäw9@Tô;È-ÕúØŽ3ɨªŒž¤·si'·‘îXu<ÅÌ~ÝeÑ׶­„çÈ¿&´¬¤5™~>yøÇàUáœÂf÷W!ÇT®Z›Åð¦ßtl2ëáþº’v+’
+´`cÈظ3ɉkÇÄ‘òöˆoÁ¸Š­ÚŠ‹PÔ %àÛ’iEý1¯e;G’¦<DŽÖñW92+!_$‚‰«pà4HLcÜ×¢ö ñJI̽Èt?n—Q®
+ŒAFh—f VxìÜž’¹aØ!žÄÒÿ¼ÑúñÝå¦0©kuƒç¥è•eÁ0 ZÙAwàß,úÂüß‘¬?GÙ’’,„CÏz#lÔð!´—1ß(Í­èåËu#¢½Ì5¿+¦34èay¾m'QÂ24Üm?Eôð6bçúvÑí:2.³–ýëùÝ\nE¯-¸\÷¢§@ññ®hmôE¨Z´[ ´'`€º5Þ” *ÜF¬h&:*‚ Ñ!rÏ
+H‰Œ—AŽ[9 DO;ôÆ(R”Ö™en`V=÷ßΣD9Óßß@ØeQ"‹Åbÿø«©?|ŽÙÊ,¦S>j)þfZ§Éhº!£×Rµ•&!Cm­Û†L)2Tª—Þ>~ÿX )U¼ÌÉßy-C§öa}Ÿ3½»hÎñ(Õdö©µŸhocø
+E‹Þ¬y̪8÷mÅûßv´#ž)ÏH_nc«ó>žASýúñO ]#ä"J±2¨ Bv‹ìÌoAí”-¤+®ô+@J ©£Å¿¹ûüÎòâ×£.óhÝÛÔ¦hpþÐì è×З[ y  JþÈò]¾½†{yÞ·@/Å[áÀçó&ßÇT\…9=Ýå‚ùüF¶WM>þdyPp·Ž”èâ]gôÒ}Ç.Ì ˆV£Nç.Š—G›L4
+Ó,¨¿MÓÎœ?uÁM·Sèûƒ‚ì= t‰Öòô-…(½0 i¹ÏAÒ"AŒ–º!È Â¢.'Rd„Ç;{iÒú¡^èPÆÁc1™¹Þfä [€²è>ñíaYfñ¬ePrCŽQŠTQˆ½•ÿD<)Åû
+”œ ”ž5eTÑh³áuƒe@!{ÌŸ,)bK²1*Ò‚¸¢ü­…•˜7ª«E(
+¯ýÍ9Ìâ’Þ°­y 1Š– ¢Ô Ä®•Cæ†)à3¬WËPá„PÐæÛÙ
+Ò·B×f7†÷ŽHRaoáññoΉ (*‰çÍW!š½+rowW1~ƒÁ2δ¤Øˆ¾Är#§‹58èÏ*…S£_Ñ…làK‹Ç|GprÞ
+ Dý”AòŸRqÑ_®/$‰¢×;Õº ø;õ ‡Ç
+퀌>Õï«Š-H*ìb&ä•/RŒ7Ãû`™‰­ÞB.’þõ/¨ÿ*k¬´S §÷W3òJ.Ø ¢Ø õ_ô¨-hFô³8F/2¡¬ˆ2ÆÈ7iØ…¯Ll +ç›cÔˆÕ’ îËûÕðJCz‚СiÄå~d‰0”gE‚äØËc[x?á½z èRh¹éÚ—ÄNϬÄÃ6ÈQšÓÈÛ>'ÞÍ"7uCÈÀdáz‡-ˆâLi
+–¨Œ#ÖRÆÄy
+pw%_Îe!ÝÚXú:¦±CPóÞ·þÓÙ<WCp™=X­„É‚sÞˆ•J€„’®Ó 1+ù8Ÿ×Pc
+G†3XšÁ)g wÏIì%
+j@*`êI1®¹c_YOf|m
+Uiy
+únË„‡%ßr
+¦ƒOTKÞ¥¢-ÒùÔºƒ‹ÇJ•´UÆ,â©í8*F,‚Îg£|ñg·²3VÛA}8ÜŸª\cðXILªÐZ$:œñ–
+øFRu~s„õÉ1…±@¯¦a“QÄ[ȵ½ul‚1r`ê~Õõ6˜q²€o¥Á‹Þ¾
+×Ïy%Ì% ˜¡$¶‡Zã¹ç1£`4¶ªïs0R8[*NÌÜw¨„÷×dÍÚw(w-W"äÔc(2rf†"O(qçdÍý Ù 3
+~ŸP´ ™@JÍßl€f˜QjÃV³7¸†j°¯ö,ƒ„«åÜn¹ï\é%{B3 ¶ÏÜ„q— {˜ä)}jÈ †d)ƒ}( Õžãv@Sí®Xó°šó¹‚„O
+RÔ– ´<DÔ”3  Ž0âf":þ~ Gív’AOÅFUcáçíx¹€>ߘaN£aMî ×!ùpI-ï  †xSý3îp•ÁoÈL‹1
+ý¢&â@x ®­)Ž\«C86üR¤r¦Ð)ª|,,¹M·Jˆ
+¹,<_D#'˃ü¥…yç÷7 ì&g#¾JŒAدÎr ‡à$ˆ&ü=/qˆ\Þq†ªÌ[>ϤHâÃH}Ìo ¹TRÒ\‡ ¬ŒzZðXqÒ‘ƒj1DY¬ ¯­µÆ!(÷Š
+F\DŒ Ÿ¸Mˆ#ÜÐ$ÊདF.ÛÑäl)Ô!|§t¬a5ß&)Gøö9åÙSe†~WÞh€½´èÑOAÖ2)oJ&‘Ý„óz²–æ/†š9a!ƒ.†í0ùµmß·è%`Ø£™¯T‘¿ÞÂNâ30̯²ž}úÂ+eíI›A!0¥rÑÞ…ds [î肽jJ|éé&OÃML]v”M»Š¹?ïƒe ÏúòÀj–õéÔOE»ªOû’.Ó0¬*ùÝ@5Ó@²,ƒx2gS|32Ù¼"ÂÃ{„bÔ¬ i̤ñ@¨ÇVT®!ãçMOð"<Œr•há Lc}`L²äÇ(—ºà6r“Zïà‡ÒËŸ{D;«HÄnÄÅ™ÆiÛBä´ñ¼ô·Ø9ª~½9çð{ü\;!¯e0óÇW©]é d\…¦ƒf6Ûüjá ÒìÕåMŽ‘QÅæ¢Ó;D>
+BQôìçªWUÆ}Ñ+‹¸z ’Ù¥r^ƒ™ ÁfáшaZoÒ$;´¥™jQ@¨ âžR½€»Óa&
+»3ë âHd*§›ƒéH_Ÿ—¬r¦¾à 9Ñ#ÄUffÎç«(À2´û8+&dÏrò
+}¯Ca84&Š¼Vá9ôGQqt·Â—m´fÛÎa”pŽ).[‹I¯Qdzd ůÑÖVK`ÂÓ<Ì8‚C !?~ØšI·(Ê^µs†bÛ­´åwð… 'C¦ Á åãÁºG¾)îm›R6šTòv««BkL¿ WÇj±ÿR×€ >lûî _µd)pÈpñ ¨Â]p0jÐ#r‹©¦ó·ñø ÈR)Ä°׸L¶˜ú¥¬Åä¯ÇWQ˜¢
+ä{Òš@øÔTE'qN8n
+z¥‘Ì\×[4‚¥‚·&›áÇ ˆ§‘KX kò;¹úÑ ‚‡–Ê8*ÆåUÕÅUÇF.Õ¡JI4”$3ûUÚFšÞòX=U­ÎUŒjvSÛ FVkôfØ«->€{¿.ü3sÎUX=«³“à&÷ÏØ€-æǪKÚûf_E¥¦®kÅU”QF\DïýDMÉ/
+?ù‘by"B¼Ý³–/ÔdgVûÍBMðÓ"ä}·µ@𴸜Œ£oolíø’×£»<úøQrUQÒÌãˆÈß1B Kt=Ï{¿A(T™“º^¾žfNäöÂÍ_NÁÌ0ëb® õ‡ÃñÌ@|µñ@gHR>‘^´,Øϳť™±sç&VJ©dQ _ŒÓ‡C¡TÜQ™­ª ûyëåЖ ¹X¶fŸØ´»YD~éÕ$ê-ßÍ)M—Ef¡g•­\Š}ÑnÊV°pS|T#ðmxŸ8bŠ™»†måÐU&lž&mjàtE½ñ!’g{×pIN¢D ã-²”-Ah'50é‹£P¦^ÚS‰•€Òb^£-¶ËCX;¿` ˜oh g>ÿüøå÷ôù¯?>~ùïGþüõCÊõµ™‡œÙ3+ú@òxfòyBýüî üDšòŒ!È—KŽÞ³èp¨ÀÈçv –Já<ÅKd¹K’ ¤ÝmpJ›ÛA¢:¹¢Œñs,øÈLµx ËÀ–*ª1´1–l‘½‚Âè&¦pŠ:
+_ýE.vµT™‹O‡ð3ubœG7ˆ}‰Ä%i’–¡[–P}(¤<fà „ËÏS³8‡:T%”® hlKÃÞõêÿ·\ƒ’|:q‘ Ô&{“PäJq; 𙘼cãv5{yC(,åÙÞzÆe‰-аÒ'¼ÂG‡0´Ê¢¨ažþõEUD4Wv¿"¯NÞˆ òcš¼øFÙZuD“y3sí­xÀqŠbd:öºùp\@4”s¯ò¾¿€^'‘SEZ]\
+ÕTB)[AìÍ;¾wBî}O™Ò@ܘž~öâlj„-ÀR0_Á-Še@Á<)gÊ~[ãŸ!1aM
++í~Këû¸)Î!~¡èr´#†Ù^“D˜G¼†êåÝXz¯]…æ °«Æ®—ÆlÃÕ=Êשv…÷³6S¹*‚v@Ø;Œ•4Ë;…òhL²¹½"VtYðØõ kü‹>ñY¢ß „PÅ3Ç4,náµapÎØ<Ö[Ξ_ŽfUÌ!PNÃM'xjž¹àÞ*gONqÒé#Û£ÅîÍQq (ÜŠõLÓ‘£ê!?’Ù¸Ñ/ Œêó¥ýŃ0ØÆsN쌗Ir$7EO ;ð4Ìúz©[pKÞÛïÃÙÆDuš©$UæO ÷?tœVPÿ/wDClÛŠó争óµ#¡¬ ³ç<Òq¢æè=ˆ9gÔø×27ªêÍ$NTfžz°‰>*~V¦¯åWgà'H­<–ó_ê%x/
+øm ŽzÿØ¿2®WŒYü8MZ[ÔÏøÿ¸T´qrŒ%•Kq49¼]^V#òc ÑµÕXÀ&°)òÁdÃda˜š“ì¹sŒ¶R22B1ûräT¿šÕœkëÄÏ&÷ÄîyøVSmÂÑÎèÒp”ÝæMé÷æ¤{ÑÝ(£QÖ ²RËæ¼b» ;B6 ?d¯z
+WkÁr±YÊë¬[S@ØoFŸðYàfyZ¡-ŠÑô¹©ÛÝ]C2À¦K9 [ƒ
+Ñ„{ð».²A¸qû?GÐTÌI¢E î£§@bm²øå]ž–‰DHŸtÝî=¢WTKC #Y ®hÛ‰ƒÄ‡AáQS6œ¨q¹0÷L‰Fv$6ehB*(u’ ±DÜ……‰/¢CDÒ§dë º´H[†Ó4ŒLl"g?ÍTâÄ1Žy@|Y i¬3Ð)æó2ƒ0sH)…6ªqz .Eé ¼í›/¹ƒ.=ûíçi!HæCôk)È/+øµ8ZI×y”Õ±kÒ¨å¯/2Ž»1œÈõ ´ œ†¸ƒŸ¦*±add… —<ÍÔt„¿&"Muªo°c#Pé[Q^oÀÐ]îo4<£lËÇx #aŒûÊ2'ȯ.]U¾NýÞ8£’ÊVê» ã ^·{tá…ãx]AÆ1æá@´•9jqLи`Ïx¹²9fZÃS%oµEóLå„Û’ûŠRyû‰˜ÅõþèTƒ÷Q®´÷î€ð{äVOõþ
+Th>}^ÿ-ÇLèÔ’§ÅÍòƒ …KFÏ’8ò†êa`F/çuå¦lNŠýìr€0ñ|Û{>ù©µã‚Ö©„ù©8Ø@’’!Ùf)ÊPж©;DÚ¢1‹ÎσØYõ¨5õ t’;|*uÐþ(œ7ÐÁ,@êŒ+däcAøϵ4×›Lƒ) ’äHqÔÓ!Q YÊ“v 1éxxtl«ÔDëu‘­½i%i
+k?Z ]oÉâ0ås_T¤Jzk{Z'÷!£=bõþŠØ¸&¯”s1#ÌnÌáΨPÒ$±ˆóÕ‚™Rw¼ŒPcQ\hä3['ðµÒRºÛ ûžþ¥> –#T9ò$»ïŠèôàñS}PC‘Oúº8öïMÌå±zµ ¼ÑX<Ÿ‹,wÜÐÜBd8½Ã—Q= ÓãáÑÕ)ýâ½v_ür­Æ‚ J¥l¦AD•ô e¬n7o C} ]ìtdÇß]™ùûú^,ÿñAÛ§ˆÿ¬bB‘Ð'y„_Ocú;(-eÍÊ(£‰a6EUÆóMü—˜zÆÎ6ǤfýÔ­*”„óQ£4âÀ1Thî ZqÚ2È#rE×Ý)H)ðäs5~Ç •°qÂ~€¬­ºj¢rÈ`?­Ãí"‹nü4 fÁ·ó´%:dÀÃ*í^¿¡nŽš"õÏ?gœ•uÍqÛ£Ý@ŒÑŸÌH˜¾T?ׯæi¤A´¬‰oê@¡kŽ/7Ù7/¬Ã{Ò!;;òIF€´u’î0 }uÃÚ‰rNíþœ2ܹ$½×âïIL¢¥‘ž–¹¼øý0·Æ9]êÖ€·Úܺø^àÃ4üz¨¯wFFÓlº*œœec±ÿã°ÞÏ 3½¨×M¢< ¼¨¸ƒ/7+âtfX}8  ÉV¡Œ›c¦¨Dj½vÞ¾Žæ¼ ¹â︿Øü94µ˜(þýÆíáO¾E=V£´:r€ó:t³¾Tj«û4œ¾(áe!¨ÐÇ£J!ÝÙÐýĸÔ!@ÛiÀˆ%¢_z<¨å‚ðV]ŒÈ Ò;ÓÿtQqË“/È&û¥ø©YFðÏ?gЯ—ÿ>‚P|<MÔÍ'Û +AÃäÍ„£+Óò
+Éo®œÆŒ<ñDÌ".¯L†g™ß3ìæN<%pe¹|+2fš>¤·øu"Ë[ŒS[-t®™<®Coõ„,f™—}¬Näw¼÷(A`"Ë„ ¿•ÃŒ·JL´­*‰¤,Ëà1wðAÇ<Θš­ÃSÓ
+"Ò1“@¶„éQYª<›A}ÛRñt¢‰X¯Û«h×Úý:<zÇju¬@t-WÐ)Àj;T‚ €þã_@¿Î$J õJÃi‘f#Hè•!i;桉h
+¸[ š3j¼ÏüÄcÈ<DaÆÏñ¦ÔuI!ç|¦Î¨èT„£VrнA•$Á
+¢ËÉ°ÿA¬…¿Jtod†­Öô.<ÃRô» ã«]·{D3Ì¥ƒhú÷40‘-ñ¸ÍÝ‚f˜ 7Tj8¢Ãæ0~Ò‡(8ê:›Aââq€ëYµ¡vž¥•–€u=#–äì+_Is•á <?‡†ë "®£p¸å
+o¬®É’`ùL]ˆ*iš¸bv+ÒhE•÷æi›Œ6‚)-sî¨ÒdMj É×ÑOË4“ç¦u;ÈÙ \ÀC†Q_[]Aˆ)ƒ`A—ù‘ñß±†ºuŠ çr[*Æ››%ajxŪ5Q¾¾‹Çä7Û$ÊC¼+ûïêu˜4Eù×zS:zÀËCàùA“ÁÍScb2 RmÔD™~È-cŸñå«Àd‚áæ5guÈÈ"”ÁŸæ—t ›ÐÑqGSê…”È~$[†á£…»ÁëKG)ïRÏù
+ǬläfoàPtŽœü4%6gó& 13ÿ"Gô•Eù_g$<eùÌêâÙTe®MH<eÓüfntvOh®#ÔÊvX‰¼;b±0s6v–"1© êiËЮS·®Æ4ð4rqaÿËx¹dÇyë@xÙƒVàÃ7ȱïP»ðTÞÿô~E€­XÍv4‰s$ˆü êцo‚2$D >[ð=ûTå®»=væÆ P_ƒÿdU§ ÌA˜.ä¾WW&ÿG·0”ùEI—غÔsÓSoÁ"mÜÄðà2o#m&ãµ$Ò7aó£­î%(­R"Ö¶?(‹'dúe¥G‘è3* ëXÝ/‚Z%Ã$ÒÇT™ä¾gŒKI ¶TB|ŽóçsÆ:&cÊZùøÑrÕC-J´» ¢Ùý}èÓƒˆ¥-†³ÝX^D¯¶!ÿ¡-øUA:íÌG‡Ù’¬¤‰ë^QADƒ‰‘ëæäÈs"Úh×¥è#Šp™Lt–ð5ÐÙZ[ôg<ŠÆ±bu„ÝàkÄ÷XíFšŒ½œMîœçÇ)²B--ûìð’Ê3ˆCÒòtÌD<Š[¸2ˆ¡ÖSÂñHAt¾æ%õ³’iÙ«s¸dÕ)åŸQ‚ÐÈN U
+€Ší’þ+Ø
+ùBù̬;·f­[–¬T±ÏÏîE •˜Æõêòû­Ëß-bELo(ókP™„H¹Ev”
+õËÌ„¢ÀXà póºaPÃ7i\À
+ßdKE™-> E˜ACì…wü/2ýf–…TWy9ð þ¾‰Ž*yÔðWI[H‰<%ËÃG€Øúaþ’]‚·Á<—òN!“ß{¢
+Ü(Ò]òõé_J¢Ë¸$—mëå9L—ˆÏªñÉô *Œ¤›ë@é(.¢Ê`ó•çª/ÀH’_)%ìÃú‚®_Ásˆ¯øøZñ* à–õQ禿Cùýá6:ag—5ûñµÎTqÀ˜…nŽú§"¦Èú࣠»±¿kŠHadožÇKÑðŒL/™|ÏÎ~GÁÖÄ'YÒ4ΧÓ3´}Ê†Ì ÙX6l¶h(Df0Žé¦¨»DW5Ý|- Ô—Òøq‚s^ˆ·¼0ŠÑU7Ž€;èã–Ot•냕€Î[±ÄiízCøìçõ"àa-DN“TBäúR¿[$BKlîŒÞu>5@Í“fƒ/]æ°Þš„±S½{‰Œ¸nWßO„W}y ™µèÒjêÌ@îEòtì3&³Æªâqa,aì«:5š¸º]H6‹A¬ºT0tÑD1s]/NaUðDRŒSX|¤I‚´s®V,g0Ï_;™¤ä’z@ôÓ(ʼhÎbr´<²”+ä³tîiÂ&eZÕK”½š*EÛ%T(‘@\ËqA‘(
+ ÿA^á/—_–5B¤¯y5zº8}Ñ÷|©ˆ¡ã[»lUO÷@%\p§Ñ!Î:³ÈO©yò.êžgUðd„~Õ˜˜*)ÕOè3)'ðÕt?ZÇhí$´:bÚÛÛ¸'šÅ÷Vñ×wØ]B@Y‡¼%eȵ]ˆç·× ùK0^ÓI°ÿŽL^4‰–ì)f"_#SDÌÂßpLnwö2™xtd¹ÝJöÓ s½¡}Ð÷ýû1dÙ
+n Á Ìþ±‡Q"gÝ0Ø9Ø;@ÇäÜGléSQÝÆÞ@„˜Æ»ñgX„¡¡}»ˆëêB¤O$Í›x)Ân+³ñIŽ³ñõ«˜ÅÖ¦%:m"(7
+(.ô/Ã1/ñ'ãçe5ð1 )س€Õ
+kÃÐÉ™i(Æ]†q1H¹Á&’¾^­ûÉUšáâvž¤`FW<<7é8TL|Û
+é (©3¡²¦HG¯~ÌÆ{Œn¥)Ìeá¹[”Ð:Ú7W9S•‘¸€íŸÿÜjn3Gbh/®™å}‰ž¯E_Wðãkúþ™eÊvë4.?¡/VøZ¾Ñ„ŽLjãe£æKÑ.™°ŸÚc²dh’í`ïÖ.áÐ׉õÍ'î ¦]ÕÝÝ'"ngÝ395ŸdYè½Ä‘õs
+O‡°Œq•‰Á&rS]å*yoà.¸Ù…g2Ò±™{ŸÉSQþ¡ RØ—!þ÷§#÷Œ)öþRôáEežØs.¹í ¨îã‚Km< îRÜþ¹Ì^„­8eŠZr"ð X¿ü*úËp3–ɼÁÃ@1š¾Ñ’2/Š6Ãá1z~Q!€é>&¶ðRD0`·aYÿ
+ßÍØBÁÍÃàÛªD¤%‰šå„Á¶ö×ÌvžD†úÕy S•LîÆ‘ùÇMN4ý
+½¤uTuJÔ.|Çqè+èæ%@áy¸¸à{Ýóï««"¼~Z-tá
+Ù…Þ„¡ ¨e·‡ß2/šà!Ð÷sò¾Š¯ÂGXtG‰ƒaÍKĸ/²Uôã\„T `9
+¿avŒw’já·PÖ
+ÚÍðÛÌ·—Ô,)ì¯ʈ[ˆÕšQÄB`•‘Ø\âíp¹uÖPKöÓäs­[¬Éš6÷RH&Çð¬!·óꨠáÎEЄ54wÿ=ß•ÝO¿&X u}=6ç'×Ù×ñ¼Ž¼õ(Ò, tú‹ÐÃDs=Øé©dp+²Ô}âYÀQä;×¥èãE.^ ‹F FᶔrÞõ ®ØY8Ê ‹Åî*Ï1õÞÔÁÔ<0˜%LñŽ»,„\QåÓ
+©{‰¼=
+ñ,^®
+Ñdz $+ sÀm0àÿ/w;’#ˆ~ÿalƒz?L2éê y”©ÿ׉®¬ NW6æ.v±Ëٜ̈ì½T4¯4éP<öÃUóE®>¥Æ›&œà9ÝòKNÉ|_ì†X 4˜pŠUÒW¨üèÖ›HÈ à•ö«O
+=¢ßÌW1ÝrŠ¾¸ëŸw,xÅÍ2¡ð{iñ5 X †ŽÔÿw¡Î‘û¤!Å™°«„6ö¼? O™85NXäQ’ Tx™AYÃå+L»ˆx†%)!4¬‡zŠ’G‘øèÎ-'uÑò[EN”ôŠnQR%šÌdm™û%‹e$4/Q²¨gýBåXü(y\Æ)AèÇBR¯ä÷ 8.–|ˆ’”@Ò|H/?ºEI "ăؗö%)Â:±-æ¬7KR"3Ò¯¼¬ä–%w²äYã]zoØBl4íÈö}Qí ¢ÖFþ¬èØÁ[ÑŸwÕö¹"›ìöê†ôŽ\$d‰3®}®Ÿ¬ØExuD§ˆ’ö ÁÒk³åã&g”pÖQG ³l¶d.å‘jùº´&cD‹›„ݤ“Íe7HÅžÃOÙÂþZ `^O§"Ø*é€0,¿pî¯æòvj¸cñG¼œ¢?VD†e^¡q+ÒfUne[Ö»†ºš¤.íéäÐaëÜÍAChÍ@—öÉ3tP#«Qö|°…î/“5CHl|Ø÷pË
+Þ}™ƒ"ºK«øä¤îÏ)`hÉ{vþa¼´;’ÄUP‡&ÑÈv&ݪ1Qt;Sù‚sô%\8ãÈÏ3¬V×S˜YD ù Ñ ¾^ ©€H‹Ò¿4×e:Ep1D}Q÷Yš#é˜GÄ ìØ_>X˜VYQ°Æâûn&vì¿›'÷CJ™gÌäç÷»_¤ç~ˆÐ3îÁý4VU(
+)ŽÖ>H™ûM­JXÓ|Øq®ŽND­’=9¹ÏÝD9ô$ÂEÿ9„P]”Ÿ”U2p®"“Œ¤|y¨£­®òž]äiSt'e&—ÿßIuqDŸ”1ƒ—²$qæ'-ü¦•‘ÙhzÁû]8€«DÉ!5‡{?OPôb=ÚQ·èrjÃ+Å.(k¾(fk˜Å}¨;(SÄwÐxÖ ”³L#Пa(óî€òYãÜy&"Ö°µÆ³ß-ò@ù\A”¿ÝÓÊÿCG¦ £4gë¯uîŸ!âG`^N¾zPo”YÎZ\\¤Ê’Óo”)*µÊ߀Šî‚2%H†¼¶ñ#”U’¹GúÙ[ ezÍc?BÞò@Ê*Â:˜üþ¢ò)ó²9g¥ñ¬i~:z/Ud«¤¬£T{6Î?\RV—É"ülŽäa°¡)S”aCIIËÏ©¨geÔÍÿ7V¦0 ,2ª>°2E°(DÆÜ•áÒ2%Ü'¦>„Y.-Ÿ%-{E-ŸE-ŸŸ|ðòqt—žÄ|\Äé^Ç…z.È`¤PyJgçýÇhkhÔØzÀ¬)ÕD*´ø
+Zæ Ô7Z>¥Âáe¯èNÌgÍÁÌúâ
+9“Í+Eƒ âêú+£0ÍE"Æ¥¯ç0{!ñzœÓJ
+¶3Ÿ"{ÁÀ•5õ·{y (D´ùôœ
+3£ Â~UjÆ¢/úð®‚SÑ;´a¸æ[GÑ ­Ëlo
+C\j/klþC—j³sñel +Ÿ}W§;à/ÿ¦«õ‰2+_ǯè*!bDQW{E!VvÉJ³«¤ñ] ‚m§ÊÈ‘¦FÕ^…bnæØÑ+·ëœ«&bSrÜ‹JÓ95oZRxV|ÒŸ?œoÚÊ@‰9Ïw‹Ü ¼½í­š›&¸«u/º þã_*—õÀ
+¹³®ZTsøHÎ)ÉÔQü+å½¼é~[ß\ºì'ìñˆ¸R’Ãg¥m<XfÒ°È}ï¹° XN1ÚÞ#“ÀtmP’ aâ!¦´d*„hKkQè¸ì—"jò¨2šíìÐœc·í"ì¤ü:¸.Þì
+öÓö0)úåÝÐЊ èÒ[W y·”H¼†Küµ¤0Dd¥Á¼×¾["3˜APûðáo¡ %W}!VÑÑÌ[ÑŸw:.PúÇ¿1ÃþçÇ?þ·˜ ûÌR“ÈcÖÚ÷cg¶&é“Ùéÿ'·ˆÿŒßdš>Ò)b§QÓ!j†Ê?VIBWi³lIøD[W ’Ã_â æ*à`Í‚òÃïõªô…cîˆ1ì*¿»žF–á"+ƒdù`à•ñãêžÙ›&|:%¿WõrÖ˜¸öøœ:i2‡Åï¯!aD¥ ÉÛU†ÁË°!3ò€XÔ׫`³Œ>“Äl^¤Ø:<6Zû°+…òйŸƒM£ejaIoÜú‹š®Ev ý›®¯"…>)D›TBã¸L,5åÁŽ¢‚E'EPQ1)¬wF
+751 ôŒÓ$»Õʯ•Fó»ÉU¸Ø3Õ×c0:~Éf6YŸØ^€ýÿŒ—9rd7EWÀ=´-#ó`vÐ-·WPÑËìý÷¹ÀC–òÿ—%J¡Py‰¼á.dYcªJ”«i)­ *d9Èr2‡qÏó Ä<Sq(ƒ÷Çpk|G¶@ã;V‰„Ví>|¼P)–åsZTdÂ…qÏ9­6à
+`bŸçxÖ8s&_GU=ˆmN‰x§Ì›BôÏéƤ¦(—f¶¨!ö¼¹ÏQV’I@cùšy4~Ãå“‚} nÅs@ÐZ'',H‰]“$?h?
+¤)ñ¶iõøa)5;÷óÃ1h°EQ”œ›ÂX‹Ú4XGw@_‡1iq’D›-OA6SlÙK^²Ë/¦9÷”ªð7éðƒ<Qðí V`
+Y²'ÑîƒGG\¡Ã]…Õd! ¬”=ž#Êy¦f´ƒgâÏVz³sh,—,¢cŸÞþ‰&—-Š±< <Ø‹T›Óö bBcÆÄ|o6½æ™K.er[è<FZÂü.è*L?¼Ï}”V8­5$uwÍz%ÔŒÅ\SƒžfAºÏ… ‹àþhNñ¼ÓFš´Óµ^Šš»iå¡w‘j±‘,˜r<C·ïÀ+ð$•í;öÑü@€íÒà+
+ßsÄ>„ôHµ ïÎáË,%›}VÊMæ!¥Z Ñ¡2þ£Ü¯ûùáaÃÄqž¸Øx
+LM¤Ô!<½ð„§k]1×}ø}V qÃÂ>'þ²®ŸÑ^~œûIg';ßaë [4_”´¡))ÔF`ùº\Žqó ä :OHÁÀ»žÇrAîR\?÷=ÐeQÝy¿‚d¹Íg)Ý,ˆê.ŽÊ8>^±×þ
+¹çBÚ1
+`dŸ¥å? 34´²z‹ÛÃpc¼Qä៸t¹Íx¶ð¥q "eæ~2~ûM,)xÎFvo#"ë‰â_o@ë8¤R†]‡ßÓêc¶—GÉš¬”]‡ŽM¹Ü`VBrÊîèõùp®¯¿jÇ‹Á Ä]‡i7Ÿfág
+|ÈF¯Æ^&àórìÁx °÷‰àÛkxs™YÔòÐÊ»7q0&ò€R»_šûð]Ê{¼4éç?Oðgbsi·¢WNÞi¿\·Vø òÁK€dHUi1–êç0W‘•áÈoŒ3 ¦Bê]C=^õbœ9¹@†òH£øŽ÷Út×93¦;Úâs›N”~ uþmΙþ‘Y|Ð_hؘݯNÔʆ™3”\ýß!—^}~ø ¿·þËÅp?Ò¬l'oߺÜÖ!;a£Yü6Ý—iĬhdE~­àruè!qÓoE~íƒ#^·%öDð¶[·s4]ôPâQ²{ iÍŠ/Pçxó( {—v+Br‹s_‚Çœe È*DVjé•Ôϧþ¼ŸKµXÊ\4–¼×ÓÒ_ê2,¹´ÇœÈ/ÅV(f#Ô‚È`ðÃa ¬$0µ¸ªX™ e›Xöóï‹„ýDW1MÕƒ¬·5ÂO™šB?ºå·‰¹²7v›µøÔ<Ò†T¹f±™”`©hbÂŽ¿ƒéª†ÜXxsLß—)dÈÜýË>R
+”žg{õ)ÂPdÜ”MÇôÏ„EU e6CÛ™~îá@9´/„f¬B1¹k•kÕï ¼b)ŠƒòžòFº4“äGû6(nCÿF©Û@ÝAáóLÚ¦QôÏ “ŒÄZ"q¼I[I2É0›&¸áÀ¿ô«[»ˆp²ÍÚ°a=…Lc¨³/%Ø
+.i)ÖO$דr'È臭6!Ë¥Tôe è‹Rð´ffœû#é˜Ú`ë'x†·óë%q—$O‡‚ÌyÆÆ YË¢©óÉ,üIP68¹IV™¸ÐûìÝ:ÊHv™ãi·ÁpÛ‘ÈiOžc,»X 9ÓôA¤€{¹•NZ<GÀ‰=ØjÁ]0<Ã<ö(óGPšÊ1³AV+˜olAy¶BòÉŒCŠ{Ó±D‰2È™T@•zà ÍŽb6û S9ógŒAÇ*|x´ApûÌ2Jñ;0j.g9OÇÞ׉ ¤Ã_>ãÒ‹*`’ÏÅVþ3SŠSÙŸ _£øÑ|Hž9"a aô_“–“<.éïúöµA¬:Ù §Ò=Ô®>ÐÖ ÆJ&MÎ_%*Ù΀CYtÄô Ad?YBVLýr@wg‚9Åtp:.ƒº}tw&ûs4º ²üTºÈ›Çº½pæ×ñr® 'öîq¦ÊÈ°’h„…(PBÑ”ÕáS¦@L÷d5Øéüîÿ‘eJíY Š‡%©uCðˆ€vÃXµ2Îñ’‚œOýq‚dîþúòýïÿ~üõ¿E,U»Ü+ÿØ&‰oA7²žfΡt®I³ašaw$1ˆøÞmž8GŻ͸QJL­V¥S
+ím[˜‘™$d~¤£àó >äfA “Ö¢×Îa
+û‹ûœ(G§£cܶ 
+MP-O A/áwB…[Xqc¬RÍðþPRhLL„_F
+ߦž¬Ö\±ÊxÖ)âʳ=Â7áQ‡Ò›³n el¦ÖÞ/ŒEKH’Åoc‚'¿öâ´}/¹JdŠpôõý( 0¿àÿ®|¿(fÌã ¡œÚºŒ
+8#ÑK.þ·t$-B'©ÄœÖ ¥›Ð÷—­:`Q&©ù—
+>ÿÂì¸rpSâKcA꡹<t_KŽ%‹ÁvmS>+E…¡¾Í$l Nš¯ÃÐ[9fúã¡}-ýçÙø«í’˜øÀ`£-Ó& SÇø:åý©ˆÇti?ÅñõEfŒß„m"Šµ‰e?Š4ÉÙX†èîFq"ø¢7gÉ Ql[ 4Ò8Û—÷"ª×h8;EŠ9
+1ÆääN/ûë}°ÐA&0ÔÊô¿†‡öé)Ó $Ó¾uJÎ
+jîÙ2=îô¿Ð·$fÑ>3V×;–$ÓNÃän©¨ C3^TH`l±A¥ômÈ°—ÞQ§Ý¤4¹w¼SsÞXض®a®ð49­o?€~?€tÂÑ`Äb]_©— …]Ú¦šdXø>皈N#.3¥Uàþ]#T¾´Õ. âŸÕŠN‘þäZ„ÖüŠ-ã:‡EH6õ¡[iºÆ#ìñ«`piÝŽzÿf±ïÕ´¡‘÷dmŽT§¦ÅDWër®× 7!üÆØ1
+ëlÝãjäô¾ç§[SªbwZ
+-ú](^TÐ3]÷ÏZ t“Ûr~Xà á¡&ý×¾K÷»dg±úÆVæL94JO²¨Ç—ýŠAňgô[þ}ªðOAÔðJØ“:¼Ž \‘aúƒv¯8#HYL*Ùú\Ûœè(•¸´ä§°ú…lú{!pt2h’BW•.fWÊê9Y׸ÍIãÖšwÍ`Ð
+bÌâסY•Iµœ9ðo\:Uû÷%HC¯Í7ǘ¤•¡-ÊeQŒÆ.N‡¯dj’Ÿ-™$” ùÐy‹É1fÂÕä=±ÙN
+»µÏIÈI!’e‹Šg%^I&íÑ! W^äú<B‰“¦½½÷á{,þˆšþ^·‘3bÿñ®öcÐ%°½@˜qc׳\ܯ QÒÒ®‘Ù\´ƒš‡é¬+©â°9Šœ5oël›Í²]B4ZÿTÏæèÚ
+I
+îÀ?e
+F|‘Åx‹ûÒ„y¼
+ÑJì_G É„&i®›Lò‚XŽL=ìÇd¢—C¢„\ úS2™ A™ïKîM8©ÞÿWÏ嘃„N¹“ÏͲW••R¨h(ôY@ll&•ðž20"à–#…iµŸÄr²Ø<#ÎÜD‹`Ñ? „W–(ëm'È,OùÒ7¨ëýᢶ-(‰´³¯„½##b²öž_õt²FE$\;Þ²ÄG“uã
+4§<1FQÑUˆ¹œõ­wG „a×K ­pÖêû¤3‹ãš„œôò£³"ÓLæÀ—íW]5 `ü–c\Κi†Ux%i)´£³„¹E ¹A?;ë;æÔt(RäÖlΣ³¾‚®;øúÉ¢NÓüëŸÿDjüE؉x5BÊf¨Kz¤qß „6!·ìã¾ÙˆOªð/Œ›¶áå7U¾K樶Ð
+ã„'&#Žñu˜aö„úŠÜtÝÍ/­ƒ@Ö9´Ïw&v{kÆÙ° béÁ›…ÜSÓ”/n¥§r»ØŸ´šrE„¦Å‹5hvÕ[s“©(3˜Æôòú$Ó?ʦ@"gý+jâ¢A6(E´˜mA]É•Åìºb"—(9LõˆrÁh,¸9„ÚI~ì]õcæ©\™Ì¸ÞŽÉëº`«ññaTýC7P™(ÓjÿWqQaùaˆ+¼(¶ Ä…ŽNË&{;‡R·±²¿T¢MDm>:ü oÆ6v¯ÛÆ_úƒÛ
+ä•CðÄn¾³ì¡%ІƒäP™¶?råÕàçµ¹R0NIG arCÆ©Õsbšý0y%Üÿ2^.¹qÄ0=Aîà ôµÎÖGqî¿Í+‰šd¦Õ‰Z-‘ÅúœbÕ,aȹcXw‡˜Ýd/Jì¡>´éL¼$S.¤jyק°Æl”Ë®ú}h cÌ1û9èO'µø"›¶Aâ,«w¬Ð`xÞÆþÐ{ Ø
+}Ãf®SÀSd}ùpÿð#¿—/h  ì®\7ËKó£çN(‚«1¶;¤ð$-èj…%±´ng#¼XñëAÒ)at»=7ø¥äŽ›^Š>Ed@š ’êÎ×M¾“¹
+%çÛ~‰G=²—0YñhÚÃl6Ìè×ýùãT‚Ü@cæ
+8œ2°KY\Øœ~°Tp*î
+TcµÌµòRD¼ã£à©˜ÊÂâåc¸KÈ R f sW§e·?âÓ±
+| 8´ßöÕ#
+8W®|éw‰r8p·A(ØéQÚ9œ¾
+/k+ºžòT~ |¬!÷.7¹L?1B¿ Þ_ AÚ˜‡mÉuìäÔ!qÐl´Ú1ê4Þm±Ý±=€ÁG9ç[Ex«©ðh=³†ò_Ÿ§ýúVÑ»ÃÙK¨° ~[q¯$Àã*f½­×c¦ÈhF;óøû$Žzð>ÑÃ9︨3­QÞÐË|ý`
+H‰Œ—Arœ·„Oà;Ì2E
+éÎÅëXãç JÓÃ% C é•'öëêðAŒî Ô‰Ðø `_É®œA³ÁÕGIO‘Zbx_H•}QÕˆ]ùp~>`Y×sxè°Œ2Ǿ -ÐIc5›ôÐ…ã¾›>ÉÝæ…`zqž£iþÒ “œ§k¥»f9h#H…¼‘µ?ѧ½kä•Äëìž;Ha'/™ÔâëÖ<±u*<|!´g4ŠÑd!²y`8r# BeÉ©YAÉ/ÙP‡HWi¶@ÍÀ²û´6PZéc&Èø x× Ív9É)߶-Ký$ÈQZùÏF ¥VȃÚnRÚsvàØ—”‘æ£/_=JžRú§™1šÓ£ÈL±Mãá*Í¢ëŽÃÏ5[”ZÉ‚0؆–êùÁ„€è©Á\¬à(*¤eX© ÃôS%¸;üÚ’¡ttÒ](áÇ|4ê¥ï©Ët7ò¸!šÐbVw7™÷øÜ]ËÚ9—ö„ÿç8”;¿È`ÛG!)‘rßå8 (Eª™Ã@õÎ3@žÕ=áoã5tðR´'’"_Uø㲇©Á62¼!\ Š8íäPJfs¦KM.jƒá«~ˆƒ ¶Ö+eþºMð}i­v9ÒBf=!" /a†€>$§–ï)Úk­ä™9¿é…ø1U5ûuAx"cÙl¶osÓìÁ¸bÈfZã:ê¿…}¾yŠ`:ýdö ;¿6ˆÔSväòš¤Ð/¸¥ä<ùZ TzO½xi%ò¡°§.þÐpèKŽÑ³àÚ3]aC§zÿ¤Û…%{“›ç(—’U«¼?îtAø!¥`Ô_“¶æÕd¨-žÞ@2 ©†«NIæÊN¸Ë_ýˆã¿€3ÂÊJàÄhb˜@¢¾H†›…ü‹êíqoyXŠÓ2xë1$õèå­“r…—É&‡Aß!ÅÈQÝ®‡ 3σ³=¥8ø"4¸:pkbŸü•¦é hÃŒ:&-Cvhýº!U›8_1¤{€(Üè`÷QÐv3eÛ¾²dO–tG&+~¢s¡ë6´YH.›/‰ã.ÀÊŽ’¤æX@¨þ8aN5—'®%(O½ôà·˜÷æûú“ÝÌal"äPÊôh_‹ßÛñëN4*]Ì|?‚cZéo_l^Ýå !/ ²¹ H¯ ‹¹¾ý»Žêt¨°$Èäa‚rw¶%®´âà%¨b?öQ4!úÆQê‚ Œ¹e Ÿcà²j4»^Ç€~ŠS)aIMíýÓmaÈùü¨’cœ‘Òkû”œ’[¬¦ÍŸR|ƒ|+ÕuÔ;(%Ö¡Ÿc<ηa À*ç¾Ðõü(– Ѓ—÷Wún¹ÉS¼ZI×ø8g8[–Á‹Í­íC¥¾AöQ·ŠßãÜxs¿Í§WÝx,Sñ0„îö8wƒ2æ±\EýùVªŸ¯æËe$ë¡ÙwóFøH«ûYÞA§5àvÜQ7~w§¥ΘÆù”ʵ•²+²KZ0µ½,
+zZ{
+Ž/ÆUæJêDYzlä˜%Žk±kßaƒ¥éXUâbhê:¼ ûÆ´
+Ö cë
+|tg ¼Ž ãJ"ÝÍ8Ž_qÛa/"h„¦¥:»[zDêᙶӡ c0bQNË@µÒ••F]üP4¸jýœÒš^íþVŸBüì`ÓÓù´Ž—¡@mI€Þ‹—ƒöL}s÷¢CàM}_D¹‹„fÂí}ÑäûPe0§'¢ÂÐk¡x¦Œ1%~6UÈ<k×ÙI“Ä黑X\*… ·(/´²Oå6n~A ašvf¥íÇð¼RßBÀ:IudYøcwL’|c S­â~Î ô±V_?Ù½ßò¤"WÀð!¡o’Ò*²+CPZ+Z‰ wR#úåªÄoVI "²'¬°Ç¯ñAû¾µh•i¥"M8ܽ#~ÛE¦ÒQ¶ûÓ)„/Dš€;!uÁ<TúÞ Á³Ë(úEt‹
+¯ä®WϬm"F}ìV]Kæ«,Kêém¬fqÔ8®?¼ 03Ýd·%÷Šjø 2#ƒŒ¨TY$ÌÉ!dJ9åfÎ#®!ƒI0_Ü
+æ)(3Hð ïì>3i£¹gj8e|ԓ龬g&¯C‚Ñ—P!w Š>î°L“«ªÍ!:ƒy-æ¯Þí·]…üåKœ”¹àÜŠ¼XŒvf”
+ÍêcoÖßšþ ~„Í@¨)¦­ŸÆßù!7(­9¿ºÆð¡è9¸~"ìjaTu Bµxò—‹';½ g¨t4WOù×dáúÜ;a*¸°Çº(~јÂU²‘† 3Ü„¥kÖŒ„TÒa†Š„PŒ-­¨Î¼Ê:ݹȋe)аߎ) ðÐX íì
+LÕMµÝa­¯CÐÕ-?üq«dwˆ3Cf–r öùé"ïe–m”±]×G–Òu˜
+™rÚ§iͱaz›¡Î¯Ó¯¸'%ÏÑYÈxÅ‚±ûÖ{ãblz›'ª¢¼‚mL}Z~Sƒ×¡±Ûƒ'{Iç« 6Â}Ý„•hfñÙ‚^3”QTŠ£
+þMM™ñgé‘x%Å)=ÿèdXW„#eÁòSÌÑÀ\/ûèdü «ñBÛ+Ï™ùìûÃê"kŽ»ÞÍ÷ê™ð
+ŽrñëákM¨ß|?¹«ÔZ†ð|‘¯jÅvT¨œ}ÿµ'ßÅœ|Ðï)ˆ 5{_|?_y±t­î4s¥*Õü#%Þëøîûƒf
+±åßm\¨K'/)ZvCdy: dKt®I4Í•JøMQÜFÿÞîÊðL˜³Ÿƒ-%ÑuvåØ»YPºè±=@Ôh€ïs_uÁ-Þ7{íœ(¿Ë§¦0|ø ldÔØå>5¥„•mFòù¬¼˜ŽÂ¨òž ÙxFOBb DŽMð'iBªkð #oÃb¢Í•@Æ9„ÞüyÒ¦Ð÷¤<¤5×ð×Ãré DæÄü$>»ž²oqÓøp^l.(›¶¬A@ô._ÜÜô%$†VÆ=o&cì{#Éžo™‡„4ÓÒdÇ9É9•9oe…䜫­e´tªSqf“ƒ Äo¢hMŠ!@Öˆ,­ïl˼Ã\!tÞõ6
+AºÜRG[.‘_}·|bš(é/ËãÀSÕ0]8ä¾ùtRá©€’¥O&*õ°“­çmQ'I…-v]@¯P‘WBüÙÐ>ÂQyõÆ?ë-\>ÒJŽÄŽƒañ§G
+‚Ìû—•îûªÉAžô}Ž1ÍBb`h^íYÊ“"åp1õ
+
+|4³Â±¡/1B{Ï
+lŒHmo·FV‚2l‘k“i›po×¾”X‡/íóÖ¹<üŞϗÝÇd²˜*Ì™b¢=æ  —†Ä3Æï²ÂÇL>6†5º[?®ÔÅ›ó(ã¿`üßow
+võÞÛ¼«õÐÏ÷ÒíAü¥ågÞPº\ŠV ±* uá¼ë•[@C(D(Ñ( F#´sô>%PNKƒï÷H‰WGGKñI• ^H7yÉ–KΙæ\éïdg€Hÿ¥ÆPœ‚˜!»‘ÌëRm&Å$3º˜—Šº/D¶»ªKiQñV—i|©èNek7bŸT
+Ú˜âÎãõ/ëPÂ,öËíèßq3¬_w n£D¹ÃãŠÌŽÒÇžÃ{Q¢;´¡â+dU¾Þ¼Íàmô׈û»wdøŸtÊY`ø6Ü«_J¬Ë€ƒFëOËàú#Yãtƒï«WNh%Œ,£;òÜO•ÙçÆU9ÃÙ©K‰–ÆCÂÌ22APd²1WÅl þ|4®Ys}÷QÚIŸØñ†›g«¿CùñQèm ÉÌ-!ºÙ­[Ã8eúzÍ&Šì0q¨` g¢‰> Z~¦î’ØTNˆÛ"3’ ÒIXÍçèsù8a«†p
+C›u‰¨˜ ³KòyŠW…n‚òE.|Wdá™&…™sžË:âßRñææŽçË¥|ÐKDH¤*LZrPpì†>èÕ"§ŽèÇ&ûýŸm—”eøïD·3ÜlcuÂÊÄ$F4¤šøâè$ÒPl»ÀIj½‡œ&₨Ø/ž´w·¼j.‰LË´>ä4°“&Ñë,Ç
+(Àvls‹†1„ƒh›à¬-1d:ðç›aem•W5¢JU¹c©ìuȹY¿ ÅÜb¢a­)ÙN·ÁÁ}¡Y #Ÿþ>]K~ QZ#í{¢Ðdf{CJ×–\BÛ¯0x1"z£ÅÀM_iIóÝà·¾laON Tn \kž-<¬S?ŠHj¬ù$:Ú^`RAÚ%-œ=bxÌBjqÀS·’ÏV¡3|DÝ ZʇäÞñ0-ЛÙ+!¹5e‘d±‡)Í|ƒk±!k
+¯EîXTn
+k
+gõqÀ®EÚ.®®ÓÜb"æño oAÔÝ!ŒWÁ œ»Ï{<õ ûûwË¡­•Ð>­ƒÏ„Ç°§‡øÚ™0•“¿o⯸åÀp˜>‰Îãĸc²k‘ÛžÎXÌM׌½ZäYdmG¨BØ`[ÏG½2=6dñ2Q”€Ö©àR¢E'Dz[‘wt€F!…ôj‘ìÖ9É*ãàvÑu»×Šd±a”<ñ,X‰¯‡"̦š¦ qšJüÐéè` ´É(-+éË*ãßã1^ VusÜŒî*Bª{gdñ·Yi«¬yd°?3Š?ìqh¤× zŸ»o¤ð‰+q*l§¬;€µpHæ¾ F–ÝžÇ&Q.üÀÞ½—àR´L5Xmn`ߊ<¤qÜ7á'¤i×"…1T‰&Õf×â þ ‹ø`*è¢À…À<Q°² дuÏÁ¬ž¿¨Ã̺ÊÃÎ"0Ë•Q×âeÁJ2S `j[!\¸% ÇÉ¡­É«Â\+bR’jcÊØû@g¢\›­z~(ù†Òo^‘‡÷ UŸÊ·±½\äÎàu»×Š.¼àŽ×µèwŽÌC„âc`íG}/J«çH*bÙôv”Œ&ÂDƒ-î`‡JRÏó
+Ä`ç’êúŽÔDðÉó˺ØÓ¼0eäâÜË0cPE^îÇ A”
+ ½f'"&AœAš†‘hv
+}F™ÙM6·—÷’@–]ìXì^цóDšaÜXÍ™°0,Í»aw `a‘Šf.FÁ+
+„¨ÏÍ®øÒ”5šõo…› Ù]Í¡ú@]H¢ÅÀÚ#¸©
+b„ Œàk¼Ç—7.hßnœÈ_t!èÈ(Uùœ†.Dd|L+© æ¹ÛiÈ:‰¤Ø*ÊPšfÅ °î zÖÉ«“W~·™xbšl=îˆ vïþìÁUVBôlõ÷Ýv<üŸÜ'"Ú¼4öÓŠ´:re)ó2_»H?' ¨‹$¤€p¬ø£CZ¥÷ü‚óJÖÝêÇÌ•9h¹„‡uÚê4ÙM³)åISç„òÇ»ìàR hµéšœ*å t[ánEN>â<2­zi/±]֜Թ¢é×CQC´”#µ¼;„MJÜ¡£¦à‰I%¡-¿II‘A a5ë†úbkB“<M$§N̯¬¼ÉÏ‹2i‚§÷z÷Ñ^ÐåìÒ#æiÓ)ùÜÐàƒ ;Á+Z%X¦A¯q€Çmb¯d±=+©C™£À¬Ö,J‘Õ™ÎV4‹ÀÐ9j˜LpÍ{™\#V¼Ê&Xÿ–þϵï7貦ꮸ?Þ¼ïÍÅ$Ž‚Õ\^-ºŽà×+sjã0v°’Fw òO·Èñã9Š]a&†y¾\äe„Ûv¯]rË×CQW2í("óÿ¾KÎÃü˜³êÛœ<T»¤EC*–é°âˆÒË£$›Ž ò’3k![:„’ƒÂ,³÷:82Dd+§D³$•¯ÝN3 Þ;•–ÛŠ+4‰ïó׉0N.i¿,}j §Yt¢wï)¸½ƒÌ¿Ù*/ºàѺ<ñŽjFív/47áeéRmv¯B eÁíHïŠIÿýJˆj->)/“GbѬXgÑKïs´sbXV›l¶ƒL¦ìníaX ²œ
+ 0Gp7ä&ëdËFñCHbG£—2’ÍI‰
+ÁGJ·ö[c8ñpÕY}2âK®Ân¤×*¦…Ùc2©Ûè<¯ƒ|3=åà'.
+CI“7äA ²A¬ðU@:욘¨ä¯pmƒ÷Ìe¼)bMF¢îÍš
+mÇ8„o|Ý£æë|½U¤y‡µaP>ëc‘lX’éÆÓB|@¶¢\=""1 Š¹tƒ’‚$U’z;5ãZâmÿÒÓÃ2W`è0 R¡Ym>¬ÊƒÎÄN…Ûé ¨¢1>Ôdw;ÁàÁ¦‰e±r,‰"
+MZ€w‚Í–ŠÚàDLhò"Q.i•±.¶£†I¼«¾}sedÁK`Où€}+lëà’ |{+$ƒc~ÖÈu0§ë•|cË´À§ð–a—DÌ8|&\F+Dû¼ùâJð)$LíÕ;U<?šÍ‘–IP)ÓÖaæØ½Ê X Y%)Ó5õE ̘1úD«
+\[ï~(º¿(ÂlÖ Îå2­S£Üt/![`oñ-y αJy“µI ™uL%“US¥-ÐAÉþ‚
+;\ÝζŒpÔ¹¿ó> Ã¹è•‡W¨›Âiö³ ð#Mº7ͨ¨ÁêôB:ï-/ô’Ȉ¸­Cg¥bΞ~klãXÈ@Ä«¼K”LˆòØ[!sÈ<k{d
+V³Í(ã…î£R˜°S‘pnaâ
+‹• û¿¤‘ëŽgê FrÆì%iÒ+hO°ñœùÄü+"
+g´9 X ô JÇmž—U•ß¬‚C1ÜBÐ8Vð²!Ç+ûí®5Ì'œ„]ƒÌ’·*tAÊá^ðTS°á—Æ¿òžÄ¶á%xµÖ™+ ÒÞ©÷€Í̺§O„‚.týÀC¹ ê"4Y ž *Ëàmz#ÅëÄŸ2²Uà^‡l S7Èn˜wš¯"ˆ@ ª¼Ùꡆ&6_õ"Su‘Ó›lþ±7/Ȉ‰ÿóй'Rbí~!YÕl®p¹DNȤB¬WÈ Æ7zIâdwÙB>”ø„×Æ”&÷iž×‘Ë®°h »„®J™ñöÓ94UbAš®e`¢1#P¸?.©ŠS¡Šý0$0Nâ¶J²Ze4+©<Å^ÕKJ•Çe6ª¦÷‚ï/ðáØ;i#ræMmW‚dŒÑ5„—ôé:^¤ÏðXVºü€È.‰YÓˆtÒñ½vµé…‹:¦l
+Л¯2øBé}ø,&Öæ<V`Ah;dÅ̲ȷsRוX3&RviAaáNyÔCÍý\£¡!È6€¿Š/"@:¢ã˜…â%UIç4þEn¢¦‚cÃ'Ð
+íLÅg½6M›9º'ÃÙŠ3c–d&wRH#FfÉ~o cŠ]Éa‘AÖ²e\ñ"OŸéf¾Ìàµ[ˆ3Y/#Mã@Gæ^‹÷í™6C„ð*O'GÒ 5)
+<Þ‡&•nUWut‘ΰ«$„á—B›PÀ)þðÚsŽ!rƒÙ¼WE¡— ÅÍÏêÿœ˜L› 1Ê:#Ê9×?úɘÆa†-"âð
+¢y ”
+†˜Q½S|x'CYÉ‚¯‰iï‚oë°þÃaƒvð.?mdÑkXåx"ÂàÆc 9=æÏäžåÜ&﹞Ҋx×2`Á¶—AÅiëpθ,JÄV‚­¼Ë>í—ß
+ŽF5å—ë(ò|UAëHîåeG=e{ûáÀ? |ÝPÉJeï„!çµ!Øl™\#-©Õ-#|››œ–¹ð‚xœz^ùô)Î+ÆìO%=®lE¿ÁÏÝç`ÊۙǸÐ2 ‡û• ÌÆ|zº¦yÄ!kf%¸,Ù‹~é.Ѧd0ïK㵋¬E~,C”×¢¹½'!z´¶+:
+$[á3°ÎûÚ%C‘à¬âÊ2Oå¥^[\õ! È´U,Ä
+fSƒqU—ÇDFÌ3,‹e(ð´}Lg•h4íÞGn໸ºúcø©6Ùý%øBC á¹7U0†pè»Hì™YBžäçÆî¤îö…­JŠ ´¯ïŠ$öçÕš%×M—Ë€¬•ó9×n788ø9Ö€ŠÞøa—€~¶7R¼5YI´¡WñÕ%”œA«ºÈ{Ÿ£¸7”.÷²
+d]ŸYaŸcÉþüª[‘¢•ž •¬ï§×°:]Û‡p´+ÑÜ¿Š-­) ¾Ž›>š£ÀePijÆ>ìó|<#¡þŒö¨ˆØ¥déTâ ÈH&Ð!>žƒ¢Ñö®Ðp41‚–eé È
+ƒòiÍÒÅ‚ˆõsX3(dBÅÀ¢9Ô1^J3ò°–Š°wuÖ6’
+”‡þi72ý3 × Ý\1¬~?-8ƒ‚paÁ"•#QL=N2ƒÂw{Å;áLm¢á@dèü›nÜF"+rC3姒7ø)¦¥‹)-]¯âó?<0婨˜uò$ê{5@%"_¼‹Ø<Å '/
+M™q¬vPÿì‹:ò(úhÕ/ÂÁ!­X×ÕÛ>§­B!5s€ÁÖE–­Ï’+1ü—Ë_e?h#Ì=F\ÅaÀ*ˆrUo&²°šÓ¿)ùÃ*’s‘ñÍC¨9}im°ç„Å]“ã5Sø/U¾ ;xÆwacËá%â ÒëÃò½•øž³{ ²/ßôyŽ¼ þµÏ¡[ì°É¨ôþôQ—ÛŸð‰öæÜ-’i¹dÕ¦Þg±H“¢hp†%™—rœøÒ*g€ÈìõˆJ€3Îʈ•ù*âv¬ûèiœ`L).+Ëac«p*¹“ÿ­hð~ÍAl³ù‹yÝ${¸Î" œ1Òe›TÁœPiZ\\¬²¬sAÖⶎÿ çAˆiVdX³w+k´omá«HFO“yçÂwÁÏ­¹õÂFã@©K7qhŸ•"ú>¡HXÝøü,lS“¿jÉý
+SÓ$‰’¦îíCúp.‚É oÐ…àU¢¤»«W;/ƒ h Wab™Ù6°…•Hc]ã*Ö›ž#S¯8©ŒgõŠ®^Âk±š
+±ì&?T`“Ñ1%ÐøÜÏJ
+ÎEÉ·` ír.çŠÎŠ@IÚñuä ÔúaÊ»¤éÚpc Všœ‹\l<”üpIâüºéVÓ¡9¬JnÛŸJðxÌ«_Ñõ©d@( £q¼¦Èˆ%/ã¥ÅÅŒ Ÿ²ü˧|mkƒÈâ4¢Õ\u† E”1ã<‰Íö
+dmÊO?óN3A¼Å()bC0&Ø¿Ø”¹¡¢™ÓpY9'HºaCÜ3K£V¼„lC®“ÀxIÀ&iJ)įâó¦RI úæê¶Da.
+–r*Šì’‘uq|Sã‹°’@Í,.b’Ûˬ‘,ùÄ
+ØÐyKvl1xï1À{@ª3 úgÂñ«¤L ' †¶3T!ùEøÌííÐ’”Ó7ˆÇùQL››€M_çhÞU±§¿F6§cз?Ç2a´é0qøl¿
+è! ¦Fû”‘˜çÏ} ½ È‘·à F‚0¤íÅ(™Ð#O²€è]"L&K¸7„‘RÁCúUÀ‘ €ál#8½˜ˆUì%° 8ÃÌHv;GöWô«8³t!;lßg¦ \‘YEnB«z“èýš%oóç·SÑÉÑ]FÆýë¢Sr;}$7Uð |hZýœÜ(* ¡p߷ܦ€E“»X½s[ß$‰ž°°ç_‚˜1‹÷æ”ÛÓ²VUtÎmW^!¸±¾óÛø €eåqàQrËmü&¥á ?ˆô=·7Pè^hÃgÞ¢¤.Ô@ɤJžHé~εU|F ®x e›C»~êïð jAn|öëª6¾ã¸_D€ÆP¹3Ü^¢QJ®þa@_HÌG͈};Ið éÿý–¿ÿ¥ÒôÃ0
+‘Œ1E¿Šàí¬’íúô ÃÃöL'O/©äZg¦Q¢¤¡Z°žÐþtXÑÄšº²QCÐ…÷–óåpöÏVÝÇ\ó„ú ™À|ƒGº¼v…<ãÿ·×¤’!sUj8â&Z‰Ó榰×â§ATì~ý&¦ø°í™I &%„<¾98¿Šž@
+íËØóúªÛ%ûè ³HwÂØß7æŸ7ï'Kš–taŸ‰2ioó%…áñArøv(Ú«“2.9…JÒš®´‘ÿÇx™$GvëPtµ‡Z‚=‰qyè]ÔTÿSŸKÏQ)¦¿ì°CJ!Ù€¸]öC‘_0½`çxˆõ±uCÄžìçd˜.hÝ…”j­ƒIçihi~ú)éäBæ#~õS‚Ád€èYœ—<ƒW eò&ÚŠ¢!îÃÀ‚†SÄ{uFc*®žuð*ÄR¤bRHàÍ8¸ï„¢‘™SîóN¸iÂø8VŠðß7LT9EN*¼|[Î:j4‡
+úƒáæCr`ì$Âò¤¡È¦ƒ` 鼟˜®ð0¬…Ûû3ŽP²0á…ÛðŒsápá¸öô(‚Ÿì³دÒ<Ï-®:…´å$ÜzI%).É5Æià•@ÜôKá(º Š%s©!+ãÜ#?½Zw
+˺Âa[¼qf"ðqr¥0ÜzŽ [­­ip6Í„–)Ð"Ì9d+ò%ñí “¶dý-•ka9œ¼
+“U8ü+îôÊ“…ؘQXÔc´šI§¡í²NIǽR‘WÆ*L¢ 9L5(ƒ˜Á’×0 ’d†}ø2Êk‚¹MÞŒ¬+™]+æ‡Ø
+'iÄV°BF]d&œ­üØQkÞÇU/IpÕOÃÙSQ•ôÿ@‰=ö¼”„Œ¬õ~Z LfÄú‘ rZ ±K•Ï¤ãðS("JÒä'\‹­4Ç܈&ywà8˜ 5¬X–ãž–ãè%
+HF­Wx!d”Í æã·¤.¸
+ØÖÝ=Eh]†£­Æ£K¡³Q‚ᾶÌý?Рºè˜§BŽkëEò÷Üçà5•®PÐ…X›/Ã&ëË :Ud381ÑW_lãˆinuoa/:³_½Ç|ÓzÜ›u1¬]ÂRàTÀôÀ{¼0ÚÛa †¡T†ºU,¹?Æ%Ã'E8’'h§yrÖÕ·€ýð§b”ªr#1â“úoG&ó„hÓ6…éJö
+ÌŠy6—Ô“ gó ¾
+X]Ð ‰A¶adlæ›Ð…+’x¿~ÜŠ€Œœ(|‡s,[:e#JÂß3ß½o©;M¢Ì»ôÃ]·"„[9å
+ž
+%UÞ†ûš*w^‚¨ªÏ@ÄPôßg£CKDF»ƒŠ¾E„aýHŒ™ö;±^ÀSP±u‰h±“ 'Œµvw¼øÆÐ Ÿ—_Î]OÜ×
+ë–ÅBgê^‚x±]ŽºŽ¸©ˆPRb'ÅIÜSç¦Kxˆ§Do”Så‘1÷šmÉŸð`£y ó†ÀëÖöŸŠ&s—e´ÛךO¯Á™7˜
+FǶàëzSŸpùÐEû·/E`T©Éï¹ø‘Ÿp ”8è 
+pÌ€øÅ…VäèÀ!àò9Rr/«îLø*É*‡§ÓIŠ\¹ù‘¥ ,Eˆ-W7«·¹Ë$EÉ`]f’kO™”ÊAËÎË4WÞÅ«~+\”q¨$ÜØŸa@”«]éØ
+T±UE²™ë#EFßðÊ_8š@Šq5Ú:²}uH•Ü˜È¬½sZ⎇lÁ-ù¾ŽöÆÉ$!àñ†|´h¿%ô×Új\{N™ÙÞ£{J¢CÔÈ°¸§kÑ
+ ÅYF
+E(9O;‰LYt¹À@“s­:Î)‘]VH.°›’u%³kv}uývYä×{‘œ/—ZÊNÛ7iøŒWdÓ˜—¢Ï7EÂîÎËúU†qþ‹—+¼piQ"ýâ<¸ðS"ã%âö˜‚ ë.º>* ù)½A°s€‡Ì÷å§ýC_¢¯îª°W
+.†ÇãσÂé]!óm'2+ž™œUêÛqÛ­Ÿ Gñç < ·‡ä7r)a|ëƒáÏkGWɪÁ‡Vß ê’«Êö÷N&¿ŽõÖ"> ±f1¼I£*  úeéo_é1æ;³2—ý‚ùÓy 1•)Â`Q*Ü,(”Éa’xð®Ø’§<¨RƒP¡T²‡ú#ëEœn¯Âðksmš …áˆí–áŽÚÑÀñÚbnŸ1OŠ‡AÌgy‰‹Y$û$nÌ™äž(«ëê(}»ê­Ï}§©š0‹«=£àFÀÿŠ}Ê÷ ˜™:”Ɣǖƒg g†çÉ‚*ðaq oâ™älµõÞ¨0Þ8BL>œÌH?î—õÒi ½u½”ÞÉ戲±Ärr)r¨ [Rb&‰{—T“Cjï óI 9¢ztš£-ÍÇiÐèK[î.é!@²e}Ïîr1{ýÙ]]1DF\‰¼¤<åá· H:.שԂp[ʤ”x ÒñÁeà®æH2íØŒtB;éÜv¨ +2F<|ö\ÉÍR¡uø¼É‹*„W莮÷üP¸Vâ?ó¨&Ù Ù}„çÖúNÿ ˆ§ºL!È«¶áë`úÑ?Mähg,_gMQhÜ0ët9x?ܾíÊ%…øV(:ÞùÎmž³Dõu`ŠBùª[®¯ì¹!´*å3õÄ·êL5ëë2,Àûq³0õÏÃ@Iòz”´¾_Š7!@^~¼GÁÎÄ“%žZˆ‘°âš‚ÃâCnapï4qeètf˜Òò¦€õeðq{©GÉoåý§Ø$ÇJ¯¬a2M‘£öØGè£ ?±„‚€p¥#åó§<‘„ãÛ}Qy%.qi` 0lÐdž/Œâ¬N_ÃçÇY<@¿çv‘”´CŠHKZõßb(²i>Ih Äáˆ/
+*áà~Ü!¡¯c+¾½@ ¯T& ñ꧄Z™©Â-óè)2-hBå𾯕 S­S¥ ¨£ZûŽ@"áŽ!ýìô RWŒ¦‘îùe…(ÒF¥·È`hìÊvý³Š²ÎîŸêH,@N[>7‚R p07 *&uƒ}CüJÛÞŒ¥ èÓ^@ _š|Ö}/<ϦTw?0u:C?#ë<ï}}×ïùz… äùšP5©¾5LÞgˆŸ¡ÃïMKAõk~H y‡©‚ÝJçu™;‰õgÊ¿A|Q)3¤Xíeʉ `9P½YÆ@¬VíŸ)GIñGJ_¿çž«müÑ+–?Ù—êD)ë…ý©+G•ùI§5ÒÉ°v¡¦~±üö è­EîmúêÙ_G­`ž¯õî± l4¡ë~ ±Ã$¼Hh9d
+·Ò
+õ¾–§SJËgOÌÞ•yòáRRGâÄ°rñœÅxJ‰©}lC
+7W…Ž_
+fý¬ƒÆHð ¹^?ØßZnzŒƒàÓ’CîË!è
+7ú”ãµ—lÁ8’`¢¤D¹•|Æyû)Ï¡ÍØh?-ÊHŽ
+!V>[e ìY|ò° kHJ”e-x î—"A1îDšzb졧;ßüÎ\Þ4(Š’­7C‡2˜Ë¬ÆVÄȃ™Y§Ó„§†6ûa¨A°œz>‡°OÓ[e_¥éæ|Qäù|²Ä‹9«Qãߪs %‰e³‡S s¨Ó¡Úìã+^ý\#^Š‘Gô,û° <4éTΑ3åÂE¹ªxÖý´P¼&—$VÑÖÅ¢Ó
+‡ÝÙ­¡hdHº±Ÿ$„:œÿ›BÆ–H)ÒÓ ô2ie°PÐE#݃š ª}–ÿ C®Nok5«+¶b[Ñ"7‹àÈt‚hg&… K
+ã.zÅ»† ·4ŽôŸ#àDF²çá ÚV·Ë˜´Ø ³Ëÿºªì]ü?ÆË%¹n¢[É
+RüÆžf™:ûŸæ4È÷JTâzU©g!)è>FäñÇ´uˆL'yÆJÄÁER–2íJ°*
+Žl©H!t>J’A*=]_xD§´Yž*^áÕ?Ÿ×A–ÛÙˆ€°¹°U2ä…%‚ÉV0hF  ÷%5K†H„Ù˜IE$ܤáw›½dˆû%^øv_r¥³mðÆ‹¢’} 6$Ø‘1y*áAøN|—ùðæÑ’75Ÿf¯‰+UJó˜Ö¥ÆMÆž’¬ƒé±SíþÝàî'–½»r{m,âRÏñäÝè£Ñ‡Ï0?ÓY•6ž É@JsôÂÿÄ C)Ñ” Å•¸ykÍ*³†žšCV©ú§=Ö¢¡¹+Þ;âxvy‚¼
+A;ä¹DÇI#þ‡¬4#eM>¢âKÞ]säRu.©t…ظ®’Ôð¯ÌLU/Á^àáÊ_棤è²ehÕ¶‚©• OõôVL“—âwžôÞ¸•hrd(úk§Ih‚™²•àêC‚<š¾¶•í$H«ŠdÃã×{BSÉDÏ‘Å]KÑ›0ä úÃOfkÊ„Ò˜.´X<ÊZ,s†$ñ1%´2Éó.€TpÀ'JŒ
+žCJŽ‹s[²±ð]Q"Ö±[¾Õûa A÷Ö‡m? v‘> Œ´õ8/ïífij·Âpß\®—„ J ƶ}%¦+'æv|§ËkkvX7É»5VEb
+†Ñ(o=™þÄK n¿’%|ƒäÝø†EA¶–ìŽå¦Â hÁíjP—lò±õËKÑçS=Îãñ!Õ÷º•óhdÞù©9eXàÓèÙCElØõpÃ2¶"Ü‚‰uëiß׊&ÝH
+ _ìs©‘ç"  kÛ/¢É€Gð§öpÆ»¢5矶óÒËø°ÿƒ3†þÅ+—‚·1رÈ@KàÐ ÂÃ/•4˜™¼‹Ô…o×È0z1˜~{tíðaÔÐGµã:2%t.:‹[÷'´G ÖŽ9P ÝJ+PeÜ£Wò<3ï¦H‘™SÕxX.ÒÒZ-?”¼B÷ïo€ù/žªL# øq°¼Ú$…ä¦@^ø³-"T .h8NUì¦9%™+Ò,‚¼ï]¶ûüΙŽ£g91‚çÜ­ôgS²;QƒáQË?Ž}+Úû?ç±CÇŸÊ©LÞl´4:Q¾ÜýØQJCo¢ÃPâñ¸dYºL ªúN–s‚NtCDÁPT•$t-㟥àpŒ{Éœ‡P„8"4£žÑìµKVE©š¾-JR¢óvqMËHJÑv*j?÷«d˜5 
+ßéRú˜Í*ÆI=¡– ô€ºñP[J ˆ„ N£Yç¦zí¶Jéw,Ó˜@¹ÛË< q”€”
+¨³{¼2E­°3 ‡ÒÎNqH}j;Ö¡z¹+G6aé™’0þ
+0
+H‰Œ—An¬7„Oðîà ¤AI$%®ß,}‹ÌÊsÿm>þ¢:±ý÷À8ÝÕ¢D²ŠÅÞfk2ÆcoÒ–LÑ·¿Æô‡ŒkŒµ¤÷·?¿6hö]ºèÚ .kô FÔ9aÃÕTzAÆÒÎ÷S¼Û ˆùòÞ‰ÖFûýë´ˆ=f³>¸ò{‚úƒ°3†ËðŸƒ:wŽ¾´½}¼
+55/Çi~Šê–õâ»ýÁýlHÈl½ â\Zy¸iADéËvHˆ=ÂÍ¥‡·fÏP6tÒCö•urC&úóÊJ>ÙQ-„̾DM¬­]ƒqG»T¤|rï­õ¸]Ö"$»r­‚Œîkr6ÿÞM3áCÎ)‹«Ë¢ƒ€\¡Ú#”¯i_=IÎ_ys²µ»¢=èܦº(V+H¬ÎbÑ¿2Ã{À$™U𶜎èp"ü„¢q…äé<çô©ô÷}³I_Þ3 × º¢½îkdòÎî-N$þ¤žY–j.ï0-ø{Õ9 &Å“ºÌ´àÚì"Ä×áº:7‡¿îè)'.צ›ÆAãC„Êt>y¿ ÷#Á>%Õs Y}«Á(»"‚ü®J€Åœ±¨ÖŠ Q…±aÒ ÷HŸS o°!èÍÑSï â4*}Ä‹s²ÌÍ8 ο€Ìæ”–r§—¡RUÕŸu·E*–@­b:Yi#Ú@­ª™Ñ$B“­ÔÀ”+ÇÐg¤¯ ¯bpù·¨¼€Ã}FJÈ«G}½ÿú/°¾GÌP®»Ë˜[¢œŒŽ·ÿ%hÒê°vè¸Á\wäkJKìf ®è .®°!jîP‹¬ž™„>À.šGÆuk@LþW,
+¥9âL¾÷12§)½`«.c¢œ;¸Íu§Ì„>ëEƒï[¬æ¨ðŽãj‰¢Ng@Ò|z-ûæ^þJë®Å—„–K+¼âv´û•ãÉð¡ó£1ý¸n…2Dε5ß cÈæ¬A ë”(åÑ[猶r²0_Ä_@`bÖy1Éüê3è«<}¿L
+!¡èkÔÓnõYp+]åFñ%î&9jÚXC÷1+cˆ­åÕ â1?¹ÁXU(£.Ñý9©7ä ÎE=~$Á}·²±p—0ÜIN6ß­Pâè–§ü¤ñDr
+Çe¨] G.ï”!êHÇm} ¨#SƒGí #ÔÈŸÍ:m†qA„_¡èDqÔ•aö’—]ŠÓ“bC­Ó”R‘F‚)€f-+CÑÌX›vÓ# s*é ð¸Ìºº V-Á!­Ø2ÇM:Ç?k°˜8Í;w¤à`E`nÓš)ˆnïc4'¿YÙC=}tl"ÄÃRh]×:Þ½àÿèP‰‡‚Àõ%6÷˜øH,jCö1Ù!œãÇ3äœÇ‰Ñ;kCfÇÿaïFT£ætžcb÷˜&w$
+-MÞa e²Kh]«NÂ"Ų8GQy!ÄœûÝ›–Ï&—aÇlâ–ÿÓO)Ód3þùé1oÀy£šf¤#ÁÝérjaÕè´ÓŸÓG_dHÆ ¤¨@äìáÞÖý1(å¦ þÏ ²´ŸLMÈX3Agê}œ¨™“8’ÎpÑâ|ÊzpŒä0ËU+SáwÔEŸwrFÂgÒ˜›WF­É{ƒÉM‚VKÏ\§ÌΑÁp¥÷×wéŽ{›·Üþ©2AbrÐ¥½:q
+ìEΤš>žŒcTKô[ú¯Kï› Ç 5©?n_™€ûU¡Nñ³ùd½ÝõpŽ(¢Ó[K/§ž²J×À=
+ãC?rtl.À—,.üg“Ú üH²×fªîǯ;RÍkײõ¤f²‡¾i­
+GÄðš›.h
+àmh~©Pßxõý±ôèbq,Ï×W 6ž6’i‹+ÇØò KŠäƒì¤} ”h’»d†ùîsœä²sd)jD¢m’Ë
+Oßœ{郙þa OM;5ô„ú2$˜Æ8$h½rùØçÜÜx[î&‚dh9é7dxϧgòkð3TQMþüçUS2xÖ8µ‚ôxÓ`媧×g6Dí+ P5f¾ CQÎ'ֻĸ1* ÿ“; }¨÷Îq£Ä%ó¬?x7Ñ¿v…béYvÕþ¬›ç¶â{ó—OO¯R®ò*9º…Cî/ Dè󚲞¡þ¯ÃxO#òöŸ„
+3 'î,y«¶ ì?{„aÐÂ4#-Xe')B0ýtAV$Ó EAÚÊ…fx!¤QEÉeöÅúýÄ\Žšû‹…Ž<#v¡ûœ‹ÃYÙü¬ªéf¢‹:@ÌÓK@ºŠ„z]{!¤Ô O¶ËYfQ0†hN?ÛÇ@hɾv ôµ¹R0‚Ú† (‹#ÅÍ›%´BÑJ”qæËjÕb¤â-íd3š±[!=µïB6í Û~éé âÓ2ö{s±Fqê¿}¼
+h€mw #Žö‚i#UZ·l2%›œ£„Š¸Çë2ŒÁ±š@I‰s/³.Å|ön:
+ÉU™n;1|(Ì71ù
+|Á£²”oW3±c¹²UÝ`t`Ìmh(ÑãQa÷¡ÿDjP<ƒÐq.‘+G+[ßêVÄòZY ŠÉŒM§1ûT™F€øÅy.žV›-|œt
+SMã&&ݸ=çÌ`±H̾fFy„Hk˜H,RL†)Æ^R3¼‚dž˜ãhôÅãcP Þ%6ê<0v€ÔRß–©¼Ûç<ÉÂ×ǘó4ž`‘FV²À[ÏLÙÛQC³A!¦{»¦§ŒSÐ2åá´c²K*Qs^×JðâYØ/É;ɹh5ÁÈ+bÐdn1ƒyœy2þÄä!É|µºèÑ“ª‘wˆ ûˆÃÆ÷gÍ$ú)«j;¡üØKäa»¦¶òBMÖqv (S˜ŠÓJ#ëJ; >*ú
+j£Ë"þq.’§ãRÐ ô—I0+íðû¯çï5°µã Ç¬'ííÄY„ŒœUSñ
+®šmË¢0!ŒÀ¼|
+·µ×A¹h<2ïžÿ›ß’I Ì~z3ëHo+pÂâ"÷\GÏ1ž&>'ÁýÖKÃz¾Že‡ ‚" ~çå\„Þ´4ÊÞþÂD`$»Â& å”Ô½ dbð‰QÇÙ¿)ÃWÂ)÷à —¢°0›¶e‹#
+x(J½îöI À4¡g†ègQö³#úñcUxQpY‘´+Àf ‚ä/+Á¹bÍá¾²KdLè?—È–()"ºÊ{Bø»HˆWdžƒm6™A¶Ì]‡HÃè»Ç„öpäf§òL*Ž|+p
+¢\/Ðí^Ð'ÎqVkUå+™µa°dW×dÈhNƒ¨xÃ2ù­ß*—e¬óªa×&ª‰ÍÞxÈYèi6¡R’ª”&‘mâ.é‘evûµÊK¬›U®;7R}[%'Ò±-}ßO¼+ˆüºB¡þ2(pD8¸‚ï¯K©k 6à2¬hDm‚þ†¨Ìj”íù–íܳY‰H ur M&jÒ˜Õá[Ÿ•
+œfznáeªp™Ö¥ù
+Fw0ÀݳÄ#¶¹þ\¥°ñ¸ø
+%`l"ºGòAŸÜ(Pโ”u+ÊAbÝḇ(Ñô>(KÜ%‘»k˜ +ÁT`‹vœ³KøVx,¤ó°qDQXPÆËÖ›\Ë nåqµœ/­ ¦ÐU€ĶɳʼåÉIxlÝJE
+ZO˜ÊËØVËs4rl¯Õ~69L%äÝ×:¨.‘–UÇö? eéSeüO}c«`·¯;=k²œÅÀå¼AÁKоbÓ€n4õA/úñù(%ç27ÔWèu—C îâÿ“9$x»‘a˜ëÁðžÐw+1 ó™â*lÔ_ÖQüQ™}s-­üº¶ïzŠð
+^åÀFo ß°ò)ÄÙ´©¸~ J-Ã|’/%A:`‚~Á:c|XGÖšÇRs•¨w@¯HO¸ó(gy9ð!Z³yöêäEC+fˆ$†/kò°À%*š¤L"³†¡`Á¿qà
+yL׊=7iæ­‡þ:SUŽ4[4­ú½ ±&Ž? ݨ½muaG… ™“&rcrÐÙ€*N•?öÝ?kÂKÑ÷§"b\ÅFsž»üt«Ž_ԉ’d4Ãoò¡[Ü]9°x7ËTZáÂÑ@Ú²%¬xöO%dQŒ
+è´ôûCÑ–´pLy÷ͤ&×v‹€JY´Ew€ãñbáš+Ö>ÇÈ×suð
+þ8ç<a€áí²éó´0AeˆûGîБü”ýÍQé¬TO®Ct± x…VÛA„ˆàÌ5Rxt”œeÒ™æ k„Z„ÞeÒVz°É»H_¢Bñ‚ â¼ÀýXR¦®z
+E™÷½âëÇäŸÿÖ‹˜s‚iŽÔi.‰æ€g†øïEbÁ9 %ÐéO+Q=¯Ål /ÑÔsŸ=F þ’~ÔkÈ L M*î¤(áG^|5<Za7– «ëoÑMk¡^¸¼+>ÃJxYl;ù ÖX…Iì4c+ÂÌÞÐ’.Ú\¢<µ­# ©XqdÜ"à(¼€•¤¡ã`÷>«¼x©Ìsñ±íŒÙBÝôî˜2ó‡ù×Z¦â0éÎQ²†Yù±­š˜¸Žzøbhˆaãb¦øëÇ­æ-UØ‘AW‚ñVoød¥¢K•î_  ŒæâˆUgÐûðgHÂ9L%ÁpG«W VëyMvLp{ÙkØs°.Þ¹ø2ïÈbÆUÂÜäü¡ä ¡³îÞà*ˆ|9ï´_¸‘ID
+yIf (Ö«j«ôKb:€ö˜í”ˆQãî@¦jƒï…Ü°ÇÄ
+°Œqé—Äp.ÎY¾KÆoöT0‹µà¼–Ša•ægh«N9ªöǼYEkPWÇc±7"eÁâ^Ây‹¢6ÄNïEÈ=¾kað¼3M âÑ9 \Ês´ÄõV³sæ½®©€öé’L'F0ÚG²àí8“E@*z‡y%ý9æM.vŠÊw¼$Ó} qóQ"º(#tzŒM’0Å(GVQhå_ûX*ðË°ˆ¶÷òX‹þKTN ÃÍ­ÑÊ5c«"¼R3YÃiIʲ|¼l×ôòë%¶AW÷Ó¼Ž”²Mg¸ÔÙê_æî7ã™Lk*¦bNŒ~zL2Ü9àJüA³½YW›ŽžÕíŠmã*ùô(!”±ð¥ë^Òh5†‚Ç? –Ûš,׶£WhŽþÓ¶¨ÆŠ”€(AÉÒ°u$¼Y·¸jX‘™Š]î
+
+L!£êÂw¥®ž¥ãÑa¾¡ï«ÞåF%²„Øê½Á¥å)úUîï÷ ÿWÛá H@cS. ¿´€ÿzÜŒ±\5,@𥒠«ŒÓ!”š(Ò]+±Cƒ7–ï묀œK‘ÒÍœŠdßqÞ¤‰nëÀõðÂ’SŒç¢CgcέßF$ËuÇyILè#Ú͉p‘ZFo d¸bïÍOÌ«l2Š éÁ)c :°–+J$YðÓäïtSó³]jáY“òf…‡ø¯ï”à~†4
+Q3LZÚUŽ­}ʬ"â>bB«R wÅòûsRD2cÌ› ®’þUèš],øD€¼?ØÇb ²z)ÁÁ­-á͇/˜ >K’Ñh2Æ¡îta‘=¦ÅÅ¢–ùêhN¶³5Tó.’”0òîšÊ£'U® [TÁ0ÄÃ5®RAÅB%ªxJè–íÇÆq¶Æôæ¬ Q¹¦´sÞ[D›È…9Áëô€¦WŸw›º'¿øuÍñKîG‚‰™Él%³=ðü½×XΞÖÒ¡ýX¥•.-:~à‰82D°`ˆO±ÕaN÷F*HtÐP„5jxÅ–à¿Q}'fŸ¶Ü)Á`Iåe+½„©ÆË š#_J¢Åø¥ncè@0Ûð?Å×Ïó $þ
+Ã`ҵ[®vgöxLK¶y™ŠŠx†˜ÚmÌúÖò>¯À_
+ȼy?|ºyx x'`çŸ"dh‘„à&N´‚qR”µå]Ë€‰™¹ùÂû@½«\J ŽZ²øûS·"FÌ
+M<B°îÜoÓY„çñQço_Š²Ñ ‹+6³;C9‰Û
+‘X¹0~ƒ½WL¡¸!Å2¨[qóUq™è:ëÐÊÑ'—Ï‚[DÇAKhÛšòHxï9ý>†‘55 ÂQHt¨“DüKHŸVùŠg6¡DZ‹ æ~éM27Ar}m»«¯x«èò¹sؽ]¿„btX³Ýæ×·“uå•ø%ÖÇ&¡2!±òQ@&q(fuû¶™
+¯\B:£ÃBJR
+¨TQȆ)O¿#ÕÄc™9 òÊæ1Ï3ÊêóýY¤d jOggK ÂC”•³³mŸD€ÂjM½ÖUë#$4ºmy)R ¤ZQ=°3çS‰2J–;ÉãPb ºÜ‰ÏMýéœÌlæb¯²K&` ê½eÇÅ×ÙŸB™º¼1ÒíøâƲ+¼=ïOA£¬”3³•µ 6o®WÐÞNÎȱ~h“áºÊ½÷Î@nÚ°éâHd³jϼÎVB&¾åÞ[Ôd¢éfC;KESWÆ,íDÖ´ô†d ±>ÓQ
+WX´
+‚hu‡² (ò¹˜CSâ{fgPf5dxR÷ËÌ$ìkÿD>ÌBŽêÌß-"É
+)¾÷ˆ‹d`ÿØ‹°&u*a¶4ÿƒ?¤ÿþøãŸáÛ?þõñÇ>â·??p^Y*BH¡=C\±@hSÕxY MHS`zp©Yô$Чa£“¢18›±ƒµUûASE¦ßJd‘(Ü4Ô Š ¡Oh‹ß††šˆ:y• '
+dU\©ox jÁ ÝP‰Œ-n€¸)nPSä?û"Řï5?jÒÂ5™
+¥™q7™oàquhùj2ûÞÆBn}˜Ôÿ·@62:¦—)îVeÖbq!VŸmtd(¸h‹Ç
+—{2v»tþ¬šéOà°ZÑ9+¢p¥
+Ë;Á”÷Ë_Ñ<¦Ä»);U®`C¥ 1q*FÆJZÎCˆ9§¹_KaGNªaÃÒùàÑ¢=uQ•._MœgYE×c.bòE'£ F\îÜ_‚™²0Ÿ«U4åá8¬’aè¡ð¤%ÝZ¬(ä2dô­¤©WMÆF…
+2v—"éb–%rÀO9c R˜V‚ༀ!sw¶TðåçœüÞI]ʾÚ>=Œ&6ßP”RrÈ|†SøÄ@NßOð¨pG&ÎË](†us¯‡ÿWág’Öò‡sÀNà£L€—ð$,ÜÀD'9^­Tš—UøpÀ2-7€åÖYJÆÕ÷¢ãv"ªÕ1(mB¤€ÞœŽ@a™°VgÒbˆƒUgá¾pVp.¢ýpùA¹Œ¸Ù'C_¸ðå´D_௉.Y6gw,›/:sào˜Tœ+²‡á8·cÝ·Ê‚Ÿ”@ áR„cV¬{ó5ØY—Çh”º *úy‰Ž•CHIµ=‘ŒÆe2é’ÇsÐL|ÓèøÙáüÅ@'¤Œ
+,Ï·ø›Ó­~yQR¼ÂùU§z ¨*J×,¸RÄ,貂¨;‰‚tg Øò©•¦ ˜ !)m?îÕP‚Wã. c>ÅŠI5ɯ‚ìùŒ{’uÅí¤d±A¯²_ÕJ:N
+þ®k [?ûšÝ@.…F—'\0tz üž ºoŒ È¿¤!=ö 0üQÁFMi€ûìñª‹;þ`1OAt¦kY{ÒÓím -…ÖNÎÇJ$—³²}õœ“¸…Ï/÷¢ÓÀY=–7iN©¾]ôâÑKdúšðùDg‹Á¬Gk„‘ *C,lÛã¬ø-(BÈqžÏº†&ž¢"áÁ§Âd,(Ôä%âÉŠ HÓjá½èЊ[e¸üÔç[Ñ•–¾ÃÝ‹â¿ý©Òú9Ìh’‰*Àâ«DüFvš¿%‰*ö"‹ ~¡„ÖqQÜÆV̪ƒ¾Ðäê:×XöµB~ÊHPQ®ô¡P”’¢M¸8WüØkdç0}–#|-èLc¬Ö.SÀ& 8s™ª{Éú–êKÐj|>Û7¤fðhó7Á¦…êÄ°\_pMÝ<Çÿ+ˆaU³-Щ¨,*lË[ÙÌE"îË#é> éÎ4gÃe¡)ÁÖÂ'§ûÎW΀ÙzÃ?^pE‰ö; ”HóãWk!)&sP‰›ÈF†â")õ˜»**bï>K6‚"„q<œr™öé&Ôœ^tCß½3 _û{(y™Ó_ï,Œv+ìÝŠCvzòþt8ï×±
+PîàZÑî•é×”;Þ™ªT¤
+Y7ÉEóÜI5R¿;§Q[öºå¥
+ ¶lB{Y
+$fßË·b„zÃVí…NëQT+P +N) kÆô]ãË]` ¢Ÿ¸3ï»rÀ®AUy )&úHuCà¨ÁŠ•™¢±Ó$nãoëÍ9‘áE Râ6ƒ n¬¾‚`›.d>± 2,{·—; ë ò.-òÛ o-^o{ ó²§îÀ_ÉõD¸\)Låı¦Ú‹pWÐj%rÚPœ¬Ý•œÚQ7„IêñÑ#mÈA ðeìK…àñZ8·íscÕJ1ît#®™Ð"űì%öLJи `³H‚W„ÝÃŒe„#ÁçCd lHf´0¿X¨ÆåËiÉ8<ÎèBãá‹.G3Ö]AŽCž‰blåÑeH/\ÏxÄ Ê™6jÅ£¤ý(*E ñM'wEÉ÷M%:ïÝ/{øsõ¼c^Zð,^'!t8÷(>Ü–ì:Ä*æñ¢»€Îð"NA?ó ¤ ‹YÖ’n«¾⿱
+h¤©,u7±ê
+’缕èÃçíï¢Í±§!a%€xÅÇŒëÜBÊ"³RÏq>ÂEá±Åõô†—U“Áu½RH1+vöÆs
+—5Dï`<—¨ˆ%áÈq2†Êjcº°ÉN*:kÈÔÑME*b ¨cúšh.½pR‘Zš;¦àæ$§=hš¶,—|bÑT¥‹²¨Íþ£óžxXršƒ7×Ѿ¶0‘/˜a4’õ&^»êeÚÓZ ¨?¢ aÁÈ…CÑ%×v®ú~µþQH}Pü¹ØßX¨‰üxL7ÑAX8F¯˜®˜…h¡’aèrìh•¹@Pà‡ ):%ãšÏÒM…ÜÇØŽšUh‡³6£è•.;µuŸC<­Ë“5#èÅ1‰YHà MªKBÜ•–a˜ERhÛшw#‰X4¤l³ï3æ÷ }ìšÁŽ`
+v”mHÀ½`_Øw»&Œ4™qªfG0ë¼wB«¾ À‘1_bйv¹ÉAK,bu!ñW±ÏpK5Êt0> ȲӻíÐu ùâ,çÀŸh“Ad›áÇZís&9€Ð‚ý9>.ˆ7ºäÒ–âyœ%Ì
+Œ¤N'Û?U!ÒÖx#L¯}w…‰Þ”4Y€Ðo¬8u/åëcxcQÇy– …7 ]PyüQ`{±9$´j*²U t¹ˆÕ‡a‘e«_‘¿` ·`;A£ Æ=lÄËíÜm­*(Fš,~q÷Ó½xŸ3ÞMò—î`I㞌 Rcѧ(”Ïßíʹ‹ Qf³«xè™1¤ïF—SÔÇG;…¡P¸b[G9“¡Õˌ¨É,úH¹‰ÅºQ!6p”–ôzs×Qc :£æ–ôH©sv 6Y4ÇRÿ»s°È8u½OÂÙc±¦qO_KÄ“ 2ƒ†öâ(2†lMÖ+–³çMàžZm¡;FC—,q+:AlD-ݤÂC\»Í‹]MÞ}²I›»ÍÝLÎiŒdÇ¿L3L$öJ½˜F¹0îŠNÉ¥ÀˆT”«¯}àÑ:Qˆˆ ³ø
+ɲSpM7 V Œ|Xè7nc‰øÓŠ‹UĪÃÀ!Tï@ê”ݹ¿n<¦þ§Àò±8 w*ð׶¤
+[¬YC­½uýtAÒ"ieFÄƒÖ ˆøŽ¡ÛÄ¥NsÈ Áæ1¦UÞÚ¦-EÍ+ű,¨ãGX:ý±É–ÆËÁÇÅõ@ 6d†æåé®PjåH‚I/7«ˆ:®e˜.î`›¹
+Íïù<< ¨Èü³‰*9p*Xew«zàÓHMÅÕT*ÿERÖ0ÈaôéžÂö‘ â "Tó,(u­*ƒmó™õùˆi°OÑh¬TÚìAŒ9ü˧ËTxùTºM>É{:##u3—@u»h
+^I»ÙÎÒ‘&DmÌó¦Ÿ6si$3öPžé•M žÞ~n¶Tk•B¶< ŠÆL¶o“Ì;ªÙ$‰¶4’hD™–Bä¡ $‹êdž$VÃ{ed½->÷(Kô“-^ÏQÓMäÉ97¥fB1_<wCpô«×/ffͨ#,Û^Kíj†mòÏÁ,0 xÑú ¡ «Y/0Ûd]•i4]SéÇ?G–—‚S?‰©a¦q´"Sϻ˜'oòKøù±¼÷üòüó›h~n¢¨·AhNIkÐ4+.&¬ÉP»èjAX¤&ñϦU<TþŠßYˆPåÛ“² åÀÙXÑŽ &’¬S©0§Ýn"c¡ÍH@Ú–‡VÀŒ°Q';]_²Zg›éi½ ¢i F€$¼éYN€7"Õ°sRg;;Q¶;Hzùƒ(Í¡ö¯Œš˜£UG²³,Ã,{–õ £k÷šï€À¥Êe%Ù)Ü QôbYç
+òzyi¤êĦXÞ1Œ
+E¤~¹¦»nég¸€Bëþ9œ>£JIÎ`p >@ק±!èÌ7skYÊßAW}•ð
+rªˆ…äWs
+·éò²Ðþ7 Eäû” Vž~5«‹êåœ{D¨y~@…5(KËÍƈ)ÌX3íxÙ
+-ᆤí¼£Üœ:öªÒ=cžm7µ¥•P™tŸiëÄó»6±Q~$aÿg¼L’¬Ê x‚¾'(Ó<¬Ùr ¶pÿm{høðŸRT™uƒY%=åé93‚„éʹÍOBðÚ1Øg`W?REàüRÔ†xj~\4JhЇsÂI„ç(¸UiéF»&™Ì·!¡Ê|iÄf²¶;‘áà–„y’•"·ëT"
+ð†fÕÏy¿Âþ™}ëPpÔV:úI Z#sóø,Ýß_/eÅ ³¹µ
+ƒÁ#V„\ ŽZé8›ÐF$%‹©6¤`[Šmâ†> =§ÈÄÈsû@T`*ZŽ‹‘`\)ðÓ2Ïî˜PRY’\
+z‡ë8‹>&‹Â"ÄhUiupòGÒ—Sú$d÷ñ{Ob8Ö&Øa—…ZŒtj˜]ïÅ"«Ë 1¤øÝ9„<¯­q¹å±°·!YeÇæ©.î·sfOY3Ë÷9<‰ÒnI)žè; C!µåN«'Ô¶Öy)û*nKy—Ý唣kîªÀ>Öºqn§2Dx§UDØËĹ'½ª‚xü—Ík}/4|+íÊ1yÔKÛº¨¾"§Ì}¹mɬ§*”MûûÄD¿Ï}+£-1¸²W© €-ŒT¬bXmª˜,­q£n`<9Ù P IÓžåÆ/ÐåÏ<H·LIÕ"ßm·Þ›ä缊Íwnì×sða¼Î‰}Osæ8èØéˆ)qbu ¯ÁH0xz¡#Ö>Dò°1›Àæ®ìôÐœÀ3/£Úù-4ÿ=?[-.d[CŸçg }úòA™%¤óãõü„é0è€ûn»)ùò¡R?L»›)“v9A}4ûªGk
+ K¦"ht 6à”Mr7hD$'¤“ñÇdB#AšRH>Ìy-Éð€¦)tF>°‰]°ÌpaÆSr"ãh'¶àÀGµ‰Tª’ŽßË݃)K>Ô—ŒÃîÐ<‰Ñi} Ð kUÛíþNŒN8Q•f×T~žì?ÄèTšÌÄÒrÛ$÷$F4üN#¬†!ÎD°°nA€ÙDF$IöD Ý‘†!ÄUMdDâ
+÷ª«Œ§äDFKs ã)²Jp´:ý·äŒzyÒ¸Öp½!ãQÊ'2ª÷À‹–5<LW9Òn¹“ê‹E—±(ó·Ý ¶£ÔSsce1€Q¥Í
+
+
+âr.æðnŸï"~Ì&!™aU#8ΫN€L*ÖÃ#3­©¸×EÏS@.FŠ a1_Îɸr‰í3–t×z‘b—ùWŽ‹ðM¼Î©Õýº(°ik(
+‹ÜÃV&ö¹œÊ«Ž)ÃÜÒ«äc&þ0@]¢Q–Ô+[Î:g¥àñÎd4õÅ*…q6ƒ$W˜ÕVØBÅÐ ·È#á’Z“U†”û•q\…=†AñògòM‘†ªâZ7OÝëÂõêÞd~ô0ÎÖ3žÁÔf¶( zWA`_ ‰¼¦x˜ÃÖ®ÚS)Áéó|MÆTK,«ó2I`fWó”°pɱ!ºÕœ€9°IMB¯¯ÖÃ’"/àè)Rpø˜ZÊv8ç0g-µM ¶Ä—‰]{_™$0T[X üÜœéMôÃÑG<’¡BÙÎtˆð3x‰Â’V‚J0:‹¦ŸX>#ìB‡xZ«`Ÿ:.î뻌)tøžzÏK0»×R«H3'ð œL'VmMuÝ$O§b]¤úLr¡lh¦]°$¼ý$Ý«ùâ®õi@Ä–8.ÿ_-ƒõÒœ¼h}/W6í»LƒU:”=ÂÓ"afT´@”‹„¢ÒêÊCñ–ï«g( Ó«†è×E”‰yÖÄpe=ݱä1 ÿg¼L’#¹• z‚¾OP†yXSKÞ¢·ìûoõ(‰™(벯oR³ Ú¬nFÖ3©Üæ÷)õ×ðíÁ¼û˱è
+Ž%{3að˜£d$ºˆ°Î¸îÁÿÝ­ pØäÏâ`+)ùŒÛõÀ0­˜ã)is¦hÊù¹ë_Vâ<W|ž<D‡hªC(rƒÊ4Ú>8UªY¾d3-µbt(Ћï@ûu1•~æW¦¢¥@®#Tƒ$mÏÒ¿0c†–Úâf EVh)a)ÓIÑ«}§ÎÈPDŒ¹w³,j ãÍØ"é~ -awSÙͨÃfŸ¿N T˜ …VCyÉá „<B
+MìÙƒ?½‰ëÉ$s§‰¬ì…#†èiM>+½ <ØÄÌ\îëÜA·Pò7 ¶Ü;:Ë¡A>†ƒ êDè{)5 ¬)/ešC:LÙ‚ðQf¯ãØx ²%f­Æ‹ÂG·µ~:<? P ·c_ö•;9+5v2 ‡ôòÌÛS2ûPé›Ï_'È­>ë6¼¯©=µ·A&k´\Ö;†1ËŒXIâaµ^+?as§>@Ck—úI©™Ù©cŽGêZ…]Á˜dXqvÕA°%rÀh²§NhuÔqù!ì§Aºìva™R9@~Û\Po¼+÷NùÕwŠÔ‰w²5 vÀ®RüâÌj§)߯â¿Äs\sì£è&îð+à <’™DHí;AÁgŠ¼Æ»áT<´?ü6ídžªøÓm2n SÓ›KZãF³×ë
+¿òP,ÍÑÙ†ÀC–|F6æŽMo5·)O.ge' EU2Uß.®¬8eæ9о0ñ£òönF']·åÖ:.‹TŠœ1»/dBÙ(†Y°“:IŽÂ+š 0Ä•Ôä¬S³Ï0ÞúAÃ?#¾Ì%ñ†ÞBŒ!¦Þ + ©µöIUѦhmÛTQ®L`¬þ‰(§‚Îc.$óéë/]7aþ"lû<é
+ºÄ.Ý8£5õ™„pŽxáJz"yùQXß..‹½yD†º:ÅÞ(9^¦˜2;ˆkœ­‹>¢œžRì}óåÏD¤KøøNm¯¢­¦ ­Ñ)ç`¢9g¿ÙBìdß—!ñ1›˜Àn±šŒö(™ªÜ$›09û$Šé9bµ‹Ã¢LF–5Û`V¥ë8í%¦ý|Ãv§!´¬ÞŒ‹(uem½¸³µèû¨AT<ë‡ö.V.BºLï¶çŠÑ­_ñ5¢1å1^Ø|"G á!ÿ%UÑÎéÍB¨^w2ûtìR<µó‚ðÁ©.fcŸ èú‘"ïÈ%³!Ü„.´ª8SJèõì-N¢*U1ßç§ %øŒ£ñªHÚ‡2û1|4V ¹[d^¥UA‚öü^4YcåÇf‘;F14%ÞâtÅðÃ&ãå« ‘Ø K# 2 vŠ?63þ?ÇnblI=“††Aj,¦æ×*KzavCîÎ3ja*û¹9xPýzâ‹k¤¼Êr> ø¿ª|Ù§ßFW?ôp›(Ø&ojf»`éÄOR+!ƒ(4I#ˆ‚Îö8dQQbãLSÙŠƒ‡Ò•]5¤EÔT²iˤ5ý N—0#V Ký„GÒÊ¡<7öQ7DŸ;§Q0Õ’0 pße4[-{Ñ¿´à
+W™úOÿ©e•WGoâN›ƒXS·“ ^½¸:%y"•ƒåß#Ñ
+ôÒzñæÛ’Wwí&(PeÓŒìeV´+ÏøñÃf=ue,»ˆ­ÛH;L¡(–}§p]©Mý˜Oe IŽLºÑ"7¦xj^‹õŽùÞ8þ@ÿÜÑY,ì´—‰æ·bÞv„¿CÇ‹êîÈ„6U^êöij¢ñå·ø,1]Ò‡æîwŒ¬¦+Åä5–Õ„^붗4\5ù¥Pέüð™¡ØSŽ“tþÌÔOðFëê”
+}@1¶ßKcy.ÖÕ!AnÓš¥ ~T ”Ùú^„(³„FŸÎ›³T¤Ô˾€üp¨nd ·Ò¶ÖíÏé-®àp#{ÝZ/‚6±š—xsÊg·ÃÞA™Z
+§á DŸ)=·Oc& ÔR‡öš=œEU­‰MÍ ˜É1ZЭ ’ø}Ü_dm½eõ¡”–Œ;ˆÇOö’™«õí¡_Àssàz¼L0jOÖ¾„€žál;êYGµkÏ)Ëm¿ø I‰¹\9Â!Mœ²2S™’»4Ùe¿.W]b€mÓÁ â*_ªþ¦Z
+SÉ3Ìï_§£†Â,´2C¶£°Áx- ƆDÙ7y&ãöÄ
+ ”(£´(ÑÈw¯âA±¹O°ÍÑϨMrVðiw³²œ^‘û4å1WØ ð¡›8¹æ²r‹«mÔ
+Û<lî©wyað¾O˜ûäqÛ85E¥='ÃÝ—ÌÛ£;·Ãaæ¸s+õ—»éÄ·½?Fé2¤Øt E²úÖÀfóR®N:›D‹Ìà¡ãWˆÏVÑŸ¸p/óüzÛ*WfOŸ·Áyu›wDi¬Q¬¼#)Ó3’ƶ£
+ÊŒH`XÝ0^Â킶˜{Z»I‚BWœ«C8‡àJ¸óèTäÄ0|ÖÍH’±ªòשÊï‚0³ìhÆìN·¹7dÇŠO8E×·fP˜Ôµ ƒtf[Þ;w†$©‰dë†0R6u”=•_ÂeÜ6Ò–&êêN~T+@Í„ø`OùºÝðÜNÖ#)|6è.œ¼°ÍN®ÃÌ…¶¡ær9Q¿4£ÓÛ`òdœÜ•sÅùB;1`0"ñŠnå½ãJä Z}i lWÛ);ÆxÏ/—ܺa ž wð rø_û(ÞÆ÷ߦZ¤l<qäà¡MŠÃaw¼ÕëA*¯ƒòw[ÀÏF•ÃÈçp¦oáCó2ó{È™9’­ìt.Ú¯Ê(á‡åë2+7ö÷»‡.äŽÇÌ—4еå¤, 2x%e:-C@ãÒX–b">ÆD£ïßI×â›ÓR /P†Ûªsò ï„eë1¢êH>çVPU7ÞѸ_GÅd ´3ĉïAXñ"¦ä8TP£¬­H·ÊÑI¹±Òwm€©±'êÁæ{x"rá‹íp!ÚŽ*Ýbœóá”/*Ô:¯Ì§$j1ár[žŠ§Î;æW`þþ9jä8*×ÑdhŒ{Ñ3ïu|i’xÙõnÌ’ 䧓<9hæåÆ0VȾö˜$¸I:œ˜ÑçóA#þàb…,É®ÌhYüš"~!úkñPEˆ#“8TۧŚe}?ÖøÝgXDÓªú37êáÂ"ŽÀµ£9#çÙ’4>•É¯µêÃbeâ`ˆýÔÑ}^ô42JdLlL‡V¢ËŒH€¡Åè3#’¼F
+Ìm‚@½õî#ªÆ ¨¼»¥ß®ú‡òý ©ž}^ÔSȧ•3^ S×Е±¬ñ`ãE$ ÊdÂœû<^D¤ñ€¬qfÛÆ‹Hd–”‘kˆ.0î’=͆Œ»ÈkÀˆ}óð¸ß³×_¡Q'‡f²^v¿ƒÆ­‘whÔ5$²­‘³Á‡Æë­{Τö¢`¬IHúΤF§³¹¥æ2#’bÿÅÊ3"jUÞZøŸ‘ð)þ5¥€›¥t‡Œžh£AÏ,‰6dÜE2Êßh¨Äu”pÍ+2"d
+H‰Œ—A²9„OÀÞæË’lyM/¹ÅDÌ
+î¿Oeš¿\𺃎~¸l)3•j2j/µýøŽúâ¡£—¢R<^ÝJ)á­Æ‚x4#Š¸Ç„Äè"Âo׉*=¬™†÷ÿ~4^¥Õ¦ê ÒWñ6Ú°hôqAøDçônuLˆtó&£t-6!îC‡µZÌüT+/©Ö;¯"Çs€ïV.)ûὩåßâó± V¢Kõf‰×°¢TB ȯ_Ž +&­U.ùñýŒánæbÕÚü’RÝR(é¸Þ]¸ŒioÜX¬OÿëÔfônâœ1¤wjûÝEÕ«ËpþÖªÑ,\\{Y%iUbh¯uB¤ :\º¬
+MÒ±qG¬^Ö>W þpÊ[·ïW¹±æð¤;ûn¥’?ü‹Ã·Ÿ ¿õi}êJžGWçòñíËÿ¾”&î7N/ÍýpÞ#¨¼’½yp-™÷‡Èͼ—˜ã7n÷U%«© X·JM!‚÷¼Õ®Ì)b# ãÕ»7
+Em½®†„xQ¤V¤(Y”zêü;ds¬¨UíÒÝŽç$“íÜr ß·A{j6LD³AÛ(*UŠPB}i 6îß‹îâät„eq†+0ÓV‰£*®>l#U5˜©cuJGÔÑZˆ´©âè·Î?‚*'µÒ£xÇ-糆Á-é`ˇðþàÞKéÉ1Ã7«Út¦ª¸SÓÚu?\ƒCªÑäe_Þz¥+Ë›ŠåÄÔ– vEúœ[&ã>ùoÄvä)jëü:ž¯ªA4n;uÎMàžS¹¤Òâ§ÁMÌ«mÞ‹ŒFM7=†4ƒ]f]RåX±hmkÊ”tc.b­¯ê¢TÜ ÒRÂMt£&=ð™±ßô7e¦ˆ1x…kÚ¶:œáõtY¦€Õ2zM2/—+XOW&W½hH›6è"u,ÑtŠ@øǯé«a‚ ~Õ³ô,‰ÜžÂ‚¢(z²àéxæƒßj×}0£¡§{~ÕÐQ/¼súµ„7RcŒÊ¾'µ¿8LТ§c´âÃô°/Ó¨ÂÈ“UŸÞÔ^ÈÇ,j˜ûùœþÊ”Á¹ònBs“•9¡ Ò/ÕçÜ[FËÌvF”ë¥ñ;ˆ®xþ9 AŒë>®‚%ëŸá V/’œáûS.V(Ä!ˆØ,aÑ.™'ÖÃøHÚ‰Tõ1!Ú†æÐ,{ž+½uï$‘»†Œ¬ôÙ£¸¶"é ©ýÕŠÑ­+îzhúÕÐàñDæÃxæE£5(¿ºùÓ9†6Ü2Óã¼Mg wô`3øõ(γÛÁÖR° Ñß!èÜzÁÙyü<G™©*tN@°¬£^ŸBaXã*LbÜ@§ž#>Éý„V|1.wÍ 4yqƒðø´Fä™U/H ¼½ m?Y_•f”Ú¶tïF]Zlj=׫5#™²iAxdþíz áþd^ÂW¬Ë¤Mz–Ë¡Û×/'Щ6Ø9ó~ÄtóO‚Þ]ùûg¬ûrøÓxӗêì„ùï˜úcbH#GS’5Ü9r¾¿3Ti½^mBºjm½¯®Zz¬mêG†[&S¬Ö·kJb>Ïá Ž<²‹¯9UgG€X¦fƒªLŽ7„ˆHd6D®ì1îš&oogH4G Ü\“-mT‰¹®ê±AÂA¹Nñ²ƒÖIr›Š¹Òá†A]ÈÉc¸­ò‘ñh=9ƒy7ÙŠtu2Y®.éø)WÑ©eNNã :ßÏ+Ç_ºýõW|Ï€E q.¿Ÿ³]AŠ“ \*§ptu‹}ÀωâÙ,ÒgrcB˜cV´moAýâ¦9ñæÞaA[²EzY©‘@l™>®O±*e0¿J¶¶UBAý’êÒÄ…^}x\Ä8aœ†Wã²Pðût'!7&o;­í–þ¾µäÃ/xaÄíyò¾µÜ¸|ßZ² ž‰…èýÞÛ~ Œ»3s˜"§Õæ¢;jÖ¬RX·ñ–Á«>!ð³(S»íŒJŸxîÚŨ)GÔdn‹³­Ê«QîÈ‘·-….f"¦ º>•‘d‰Í>í-'ÐÛÆzvŒO 5BUC¤Óyï ŠŽxæ’×_šÈM´š^2}SV¿Ì£d^Óe/I~­Ì<ÛÔˆ¸ Xô¥PtÃÄz̶Kã¿-}â­92”.ëÉeŒÐP›ø²˜Á
+Z°«f>žÎé Ül³o»<™lg,(˵к$Ã:…¨Âôç±w“ÜVYWÑHÌä“B‡×Ô“²HXÓežŽº–LR8c‚Ó­œIH‰åbvìH¿Ò¡¶ÚÚɸÉ4/Ûs”svÞð¯¾w;ø—û-ö¼<ON¢Ö(Ÿü»“~½¶-ü"'M¦.©Ë j&Ø ·øʼn!ª?-SêNŽ¸Je;€ùÓv„qÄ|á[?£.ÿõ+ë.£Ìâ5ö$ßáò7ÈE®Ž°vZ¶9½ŸÓ_¹p°åbç{ 3ÊÉŽ$›ˆëM5CÇúR’ˆ—Sû1'ñtH=Æ”ì‚ÿ^ŸòÓ tO=ósI%ˆÌ{þ
+‘óy”.ø'¹QY8œéP°ó±EžK}aæ^v‹ß²ƒÁ-ÛáàzÐ YðHRm >Ç AƆ+ô‡ (?çÜ¢ö§´¦ÄP³ÀYj}ü\ð{’–N™N‘îûgJð+³¨ç[Í«½þ8‚PUÏQÉñ¥¤Î‚ÙÜð³=2®i¬lópi“mU’°dDŸiëÿŒ—;R¬7…WÀnì`JïGè"%õ
+\Î ôþý©wæï1TAD£_ê>}i9Yt
+RxK«û.»Èioýe‡r»ų1c:Ið²‹1WËèž2zhø<ÊÉ¥DCh»¶xA%SNŽ „#ƒ(†õÉ{A¼"L³x
+½'*¼?)â^2½à¯ò@6‘Ë_ã·Õû¾KðBÌ…ׂñgY2ÎÃù<Ìœ¢Ä. ¡±¯µ§$µ!3Fpˆžûa
+,F,QÞWØ0{†*H·ã«Ë)ã¾^åïIø][ã ý±ÁNÉÝ þþÉƼ}ú¾\àé®ð)rÎûp‹0@@TH†m«Kå&o*Γa%ı ‰Ôšž8 ]ëÒ䆻ï€4</'CׇïËeòžÆS:Ä\=›´áŒÆÊó–¥ë6¬_.½ŒE*ý&õlÅÁ jä÷ˆû;
+¦d3qp3ˆä"Ewkú ÝC›ÿéò 8‚r*2‡}ë c¼¢»¹¿»EX ÞØ-/™ÓQÙ„Ø ¢‹¤îÝvNˆµÁÆdÓJ’Þ€žØž` V6"ÖhZXÉSªã¦Ò±18‰x\½žz4ù{Å!äÒ§¬„å©Ë) 
+/uÅnž¥EÄ[dð˜ä:XŠ‚™š+Ž6{ ‡ýØ­aqJjßÅÅd†–÷1E~VfÓ­»ì‰¦ÓÙƒËÊ‘$è,|6Ï›¾ÛË¥¸ÎDg¹Ñ ¿Kí‡ÕÐ\‹ún&Þé£â²ÚεɱÒA|K7¤³Ê+öee3ØÍÌÚýuíü\ñÙÚÁm²¿øƒäÑn26š¸K`VeÎïu—¹,8FIì%]ä)ý(5™ \ó5º8ýs’´{Ð ¸©ø·a‚X&Š {|³>%ü§´1°OÎÁPñ"œ$ÜvæÐjÞ¼v£‡®¡
+ö)庌ßJÎØW ‘,*ÌŸ!C± ·FÊaÕüsd“š–"•içàŒ1µìDÙÖ êUÀK¦¬:Ìþ Bú C”cw:Ì‚…Öã÷1²lƒ¸Usò%ò
+J&¯sN¬ ò[,å\‹¼™åX’Ù
+»>íz{oþ‚…ŒºƒEœ
+ÅêËœS÷Ô/EA –wyüCnÑËy7Ó„sNŒ|Ì-`¾Æ ! c®Nxá2uÏi
+&"Ài»ˆ¨Û“œ°!­ V…´„‚ž>7ŵú¤äŠ´Ç"Çe1•´”²èÆRJ iMpšÍÂèŠÍØþΧÒJx\`s+aˆL(—ÛJ:”¾´”sûaYPºÐZà׌‡Ò+TÇœ,š‚ƒºÀŠº3&ëP=‰®CKuŠšÞž]ÀÃ…üB€}8Eï?À¼¶ã¿X?ÿyùãßMÌ .²¢½b¤ˆÁµ)qt9¤ |ÅóÚ»¢·'EŠuÈ2Ñ–qwŠ”;©¡¥EØTÂÆ3ˆFÈÎ×¥I „š‚Úœ›âIéQ¢©9¯¢„Y¥·cd/*Aw ñŸTÊBà]‚½JàL;{œû]‰}ªH”JB¤Û³s”5ºœÉÉjœŠ (°ò·J$à}Â7N¢µOE¸¶ô†Æ™ˆ„Bæì ÏûÔ’«°ÆóàÄÈUò¹Ìÿý3¨?†"­òtóȺx€ÝÙûzâÆ$ÇQo}¹–3›ø³¢ATÂËê.!YVîk BðŒ|×ëëcô_knEßR½ßçàf*7Â>'q0Æï[Ÿn ÎsD"aG îõÅ/ª=qÔ2‘cÃùRœ1W‰ñ°å} 
+©ƒò4=¹^O‡`±A€ú¤Ã‡D²ZˆÅX]'ÜE’… :ƒIL© /K,8Ë!&ö#ÉZR¬Ð…"“¶1@óÀe¤¸ÊÀRl‰ŽÊÜÑÌ „*·+ö)2IÈœªõ=›ƒÜ²;µ²A¿VI?°²#Û’ÞZ±îà«[Éê)¿q8éñõÅ+J7yž¨LT6‰±}…:0å½[ÑL-ÈÛƒXV&…F±n£¨ìWLå3qðlèkQ+IÚcþí¸jQ:0IÔ³írðøÁж %1VTbÑغk0ôˆÛeÆ:¨1<Í „\1¯9%gèò§léÝ?ëMûäÆ{:1ŠQ²ÕŒ}C¹‹²Xt:¯Z“`GiÇ„pÀV@èŸÉ2äÃx=B1ë$54-Æ.â8Š”ißê4=J‡§Q~…éÜf5”Ji´j'#U¾@+ ‹³X)h‡ÔÖS:Ú*ÁÁô`ìá!ÊÒg¿§É•9p_øŠP!ó‘§Ù;˜+H¥3Û³ÙôR4— NƒµMl1C-е‹ŽŸ=ÊÒ›÷¹ŸAªìy‹íâ™STeà“ìyZm¢¤@ð|À,
+
+E„E Ù»LÉœqpÅÔeùX÷ÑŽàb_ \ÔçÁ<ÌÇÌ  jǤ¹\Ç*ŸÐ*|į¼K2¯íÐMŽÙ)±/Aðü}–±•Ë;ÂÕZ´ˆ©"3Y‰ÐƒÕé19~}ñŠ
+A`F–Š?ÔvM ¨”ßd ÆY=Y@pL}Ե뚊Êd6w¯„"T"to«ÖËêûàyµRé8ýcÜ:;Fó^«7HV Ic.7„çäÂQëmÞò\Q¹h}©½³_5à%=B.™ëÊ!ùw!väåörÍýéM¤ \*ÓBo<ÐjhB­&ëP² âÕd=XÏAyÇxya¢½iíø”W¥GÜ:ù±ï“xh”Ù<ê  ¸Ù ”èòž‹už,´ŸöÄ*”h—¬êQ?ö‘¤ú4Anf|L>É :´“×O¬†‘©™MYÄÜ#—Å
+¦‘E³W‘Õ ‘a8åpXš&â{,¹wÆ÷†ú·Äñ½úƒe¹aWU+»6ˆœ4ó Y5bŸŠä„R¤ìÔabøWLXús›‰w(!a›ÞV+O|,®
+!ž> Ñ¿4­X¨Š.ÌEÜg†ƒ“#ìIu¢q§0üpH9cQÙé4ôCƒ6Zˆ”Ëf”%dˆùÓ稒˜ÆpÖJ‘#<_†Ñ•Ü´ÊD6«“Æ×’²Kn Èï+SÁ8 …ºt–œAÄú ìã"R0A¤´HŽ°-ÎÄ:6†ÑšG>„­RБ䘆v?V£@`è;M#VŽà˜€~ÿæ`?ÒºªÊÃ=9›èîGÖ×È]8+:ÿÔÈ’…‹†äP¥ˆ+s :xRÓ…*hY‡zä&ÊÕ f…ЀÕö@•€X<m0áÉÏ®@¤çr”9Ÿs’|qƒ fÜ^©Ù€ ¤EâÙÈÒ¤ó©ßÎ<Ý_ÿAµÿõßoýï[Äzˆºh{çg‰Øp6Û´'ÎűÒ$ìFXÛåÎBß…
+N€ ëHUa®GT»gw ö)”ZËÇHǧsž*„‚ÆÓm`7\ãòó«ÔÙŠœ¦“ÃîÅ! ŒÐ‘8žJ|ƒ|jÕùÔ„YJmT²$Æ¿ Ðe´3ãáUZ9Ø#µž^¯ºT‡­n¼Va¬íÊ—CzhÿQ”Ý^] ö©KϽs.“ãÝæ:€Þ£ns|/Îmî%þýV-¶Oq XìoàÑFYâÃÃ8+Õµ|º‡øM"J÷)O ŽmaRšG¼Xdï¥x««K‰_BJÄ%ëN…@‚HuÃ0ð$.]½-±ÀÐÊÜ¡ß ÏTÜž)e‚Ó†@÷=d%«°¸Š/c£xê;·ŽÕ÷¨…˜´!‡$ˆÄ€Òá6B ¸”ª\´EN
+§3vS/ûRÆÕÐXh:>Ñ2.Ñœ³ \»c”«±!Ø„Ùqm{L Êe !NoÉ¡*’:N¦6”p7‚%`ÈlØC¨kÀø‡çîFð2Â6)Ó ´ Ëò‘…ÒLE¢FȜʳÑ*ØpVØ1Ì©õûSH³IîÕêƒMUj᯺AL<>TQ5Ú9ZÀεcˆ6;ò¥ô/Ð6d*‹iwŠ½Jhöš¤Ó­>cM3!4FЕé»Ä·ïú4Ì»‚j–Çyšf¿>YÁ›ø¯,ؽ‡ *Ú jÍR™ÝÄoV[ÃOñ ø£š²’Ôè¡f¤¤…À9MfŠ¡?§4þ®…—¥¶!‰¿ða”:ÕÊëÕÓ\ÊÓ9•¡ÂÏ ±„DóÉC1— !ÉFIÿ<‡ µ!Cñý›ƒ¹Û²YîFF¡‚ý°æŸAnÃøZàc”­·Ç®Þ¯ý)~l $ÄØ%/W
+²¢B½Hó“åZ½à»ü€ äçî:ìHºÒ¸zþnCµ ɳn÷ùr¬Û‡pêÛ¿…ŒÜ¼EãÏöåÒ±!®’¿ýå‚H:h¦bLgµÞ6òOÂ=aw "¶ê2ˆJ‹ÆÒŒ¢zW»9ñˆ¶Æ)R1µ£3:yˆƒÞ†b;Ê—ƒÄȲð`(BòLUËÇ+|‚¬Oq2wšðÏAj5cxø×Îwä *— ƒP(ÄR[¶ ¨DVa}¾óA+Dz¿R†=ŠˆCô  ´ý±Äºd9Šn;¯iíâå¸Ì¨˜E†eCh ÿ»ÃË8
+«²´TÈü$:†›FÉ ]Bö ò¥3+°(ø4ŒHÀm9H¼AŠE0zE£ ìC)ejÇdÆüpÊ¥á÷«ÜÇ{Òm
+L—J/?VɈs²iÚö0Øø.],^BÄí ‘[°±AüO׬ÛÁ˲(›oaÄ‹Ñ}¦6lh0ßÓ+È Ëy±HV¾yCÞ îRâsMI¸Æ÷´§cÐ|îÌíâ.ÉÁ–¹Œøqƒ®Kß
+­e!Ïät¢ÞU˜2kEýK˳M´6û—€P%ð'L;‡
+p/àO±k¨Ù|(¹,èߊ›>ˆ  ÉŒGÄ\kNл}ì{E:8âêZäÔBÄeW
+P)ÁËÀÜV ZE$‘.•`±F»Ö…½—îSB@d¡jò£•Hn«²•L~ÜøÏóÁR”$Y
+ÓÃRÀn0\üDëV‚ð$¥ËÙO%þ©2aÎ;âÝÏ…
+ÇÓmP i-æyx0ã©;’¢LHŸz|+y›ÕþÔµ./Ë’ö9×Û”+ÁF|xS¦OHži¾ÚwëMê¸jD2Í\î¦ÂðÁe'u)ñO]&~:ç¶7÷ÛÜöïôªÛ NPïôîÜÐPñü–¤=?”¼MêïøÐx:Ì>äíàK„Ó,#7ú¡èç©èÈ×Ï]‹>¿s'çÊv¦2ßÛ$µRG¥ˆ¤êW?á1Är®¹U$ƒq@•ÝòB"@0YT-)VRácÜíhÃÍ–¤=£
+I¢÷Ë>%VÇ@oÑŠ@1ÛÉÊòÖ”0%ô?Ó&«­ µ2ès©SÁÖÅYe!Ë“g£¨-§AV^Ô%EèY¨Û9¤ÞÉ°KênÐFoŽ2– ¡ËKÏnÓšP‰™ZÝ
+쾑kn±²Z#.¿.œ¹02ND-ÑK¤·YÄÑ{÷>ÊÒWZj%­‡'~â[ŸEG†H±½2iÐB5<¹Ÿ#7¤æh;2UD¨”eiN]71¥ôúÒµHú÷€žæôG‚ÖPMö´j¹› ˆwmÄÇ—Fü°M¦#ü‡Kßýãç¨-ƽxŽ¾Ñ‚fôsXvM  Ó![ƒÁ§Yùê!ejûT|½ŸÓª°Pá\œYömôw²¿j%Š!FÜ¿¿J­CÃ#›cŸj,k\ὺ»Rx@N²«$c•&P˜¯ã=º:Ñ ‡’ÁTpj`oFs>`“˜ƒÎáÛ¡èó¡<ƒ{–‡k:Æ™BÁ %£î3ðœº5Ûã;Œ—,V"kWpbSþew9ˆ*bÅψ€óœÞe´fô
+ ÌÝœÈ`f=žÆy©ðÅ
+w³ãbûñ”Âìäæ²DÜoÂðÎÅv.iô¨ã[kØ/J4†ÀõÏý%
+•Î¶>Ñk-ŠCB/P_×XV à™RÐp0+²†¯G!²zR­&b©‹
+qz²LÉáÓÌöÁüái¥GF3ó¥™}$ÀX«;;&á‚ù¶æ”t‘Õ[¢P,90`¯òC.,Kþª‚W/ÇSàj¶GˆIü*ÌV΀Ew ,䲫X +É,¬û‹ˆ%/Cþ;ùm
+-ýþ2/•?÷ﱈ~°~|9oÞ¡@ƒäÐøºm’®è¾w(”.Η±´—ä
+ýäøºø9'€ ·âR<Ø‘°—|˽ÃîõÁÐ_g~ ´¤Óô¦ÌQÎç°ÈP)UJÛ·xç`ú»{Ͻ9 ®$:;!,³Ì=_,+YÙ.–ÉSì‰ý±¿L?»ˆI©³ìŠ†k¨
+n—䔓ˆ=Ù¾½ÍýóX„<2·.ņ)ícìÒ_ú±…˜€»¹·c\n™Ð ½s£ƒ„š¿ž÷ÓbÄôYQm«“$QÃF…Uç+£§q´’¼™V¢K`3½oÇSX+Ò
+$Y^",ÛSé\ ÙX
+MœKÑÆôÆž1n>Ñq„alÖöç:´¤jQå“m™-W›¶NYÅêõúTÿðàýèöAú›êƒZö«R95(1¿GëUÍô^ØÊÔtVCÍx.žXì0 zcLU!ЈlGrh3·K¼Zñ«í÷ÄV„´çÙI¦Fö!ê”°wÈƺ|‰ií
+!µ-üŸƒV1f\0Ø=zM{ë|ŸWÐ+ 90Î'vó’·aê\ &»å•oj=ñ ›/jHû xBÍzƒ^Ÿýª†l™ÈŠLDïÖÙŠ×ÌÇižÔéð_“Æz«†N/“ÜÒ¤¦†½T4-´ö“K³ZmæPî¨ÞÐDߊTq©îˆOÕô¤†wÍøˆ È+†¿Ý½^@D)}£.:¾÷vÎUy´éA4)$mlu¹d!¯­4VLoäs‰7lA‰§Ð¼T=âã$O—ÂÍo™êHeå2isøÇäˆ3µwaµá2L™ÒÎõn•kh ȱW"P1øm×¢"ôW:‹týsžõ­4Mb‚<×£Ó1ÖÌFÝøÞ åÌs˜žçÅ™d‘Жî$ü%ë;/¤Zx 'Š O8âÕ–¿fÿ8ÉÅ¥y\¾àŸfCäÙ5õ`£¤c_5ð”Tš{¡¤§e¶®D7†ƒ]i/ÓÛ‚@Лx_aàeFAf,É—„tò.·2ø›‰†PwÎmymDMëÈyd•¹5¿DÙ©ÃÑ£$æ] ê8d:]ªQw=Œ51ÕäêCŒ³—x)O ”˜öÛÖ|˜fW7¢Ö4_]Ch&a Ðiþórî„?i2%ž¸÷sÿ… w>=AbÈws&Èå÷Ç"
+鈪D*~’T^9àU±þýNšyUê.Š­CùË:pƒ;¡Ây¶ÈÄè-‹yY>SLä„E«æ¸Øê·<TxüóßßdU|/3R©4|%~äæ•3¦?þþFó!$y{6!ÛÐh*vRÓ‰ÔR´­Κì óýÃHCnAˆ ‰óºÙ èó+'²õ_ÿýöçÿ¸%ÉíÜk"6Ý~x´^_ýû÷7º]è‰Îýż–=΢…gZåÜó
+ÂßT=ºìP˜ĘïH•ã«1bà3m7ÐËf_š #Étùv(¥m×ðR¿ z*ÀçWªô˜Lð5ºr«•X°Š! 0“R¤n¥ßÙŽa§e}$AôƵÓp©©‹®ÛÑ'êJ馷…ˆÐê
+a*·%A”n'4™ú0îµèþ!ŽãƸÒö˜yžMò’W’70ÙïDd”kU&¾;e©»4cú2â0Bè~àÔÅbÅV_grË^HX£C`ya˜
+|‚0¨ M3 õ?[õ ƒ‹º#3yˆM`7B7t`!d60Iï%éòÔšÑÆÔ@Pñ6Î(‰'QâÖ)átv<>߀Ž+iA;þq U;¶íׂ$ žx³ŽAÎì$E­~s½¦5†•XãWÖ2ÙëÇz£ò h±­r“úðèþ
+ìÂó#z}„‡æ+$âä Ž•‰+CùË·Êt]!Í-?oç^¬Ìót߉ø£Þ†}¾ d]òõ^â1{
+G+=¸×7îG¢Æ’ƒ6‹AõåÉ}§Æ×iw¨ˆ&ˆÓ¡©r¾í–ßwÝc>¼@'2’a‰U´ôÕ!¶f½Ú/ ã;üç:ša B²ÞŠ„°|;Å…<{:A÷@ -„Ëu™§œŒ§•l ¼š$ßÎæTÕUº›!Á ¹2݉ )¶Ri—zÖ<Ø{Ñ0°<vÂì8"oôÁ›HBëÙö¾!;Âd"Eac’1¡‚²9äp–;nCˆýYo¼v‰ |"M™` ÖEF¡¬GíËjƒÅŸd ¦¡Üê!IÈx!pÜã³­. ñšZNŠæ'V<–^ÕäýIÝ”ºWÛ-.U¸yÃm—‹×y•Â^6LS`ÍÔjôipž¸Ul²‹ÒÃi¨ÂÑWóŽ®sÑ9|<M.|ÞrlÅ6¸$þ2]ÚQy +)ª­#]<óWZf“ñ ©9C%›Áj%·[]š!“3§ýGȘDjTÙA 1P:Ñ[þ£6\+e'Õ9Ú§RYq˜_ ¾“DºQQ¢æ}ÅW,ÿbhs2Pám)!pd:Ê€4m
+Ós-å¢êÅÙ»ôjרŸt•i;û:PÒ[â0?åIãûað>£§—Ë…ð iº|péæÒP™´ïBY¬YÎQÚJF^b (²ê×c=æ^܈à[)nà=sv¦ÃÒ
+) À!$LT–í°aƒTì$‘†W\|BµEÌlyߪ-ÎV8Tñ[Á¢­úÑ’¾gËT˜
+ní¨ü+hJ·RëƒøNÜÿ4Ð:[§Zc +üº¦ó%; G!€!È>„)?àà%ÕäµD6 ®«ÅFY+áT?..•mL;u˜gº²<3]5
+%nQgΪÓwâô”›¹2ä°bÂ-†;3v¢Þh!ÍøXæiü”¦/¨ÖUœEg‹=_ ¾•ïÐ
+\ê«(y1ŒÒbò ø§9Ó_J䬓‹•È–¹—¸lȧ¢?ûlŘ=i8¼/rá„
+>°<.vN¼Æ.ÑÙàN0f†¤€TÐœœÓ«wäbÁôî¢Ò‰yp,šº—ᨄ$9؉ÑѪˆÉ¡ò.áõÈ¡ü9w§ÄvBÛCÆb½­£æÀTÛ‰Y83¨ú‚3¿Kñ ÓLúÊY¢[ûðøHïYÊé1´Jp+óìe¸µD˜†‰„PI{hØ¥äúl«Gl…çmS gÏÇiÚ
+_r¤Ûè^ŠŪaÛ•Þ/õhŽ¢ÙÚ‰Ó0Ç€·)c~F¤ýP!Ègà6œ
+ÛÞ”
+£æ㲊Z®ƒk߀=æ/j2ë|30œW|„ÁŽ1ã¯PÛo×Ê$0R, øàX¦Ž[EÇ&±(ïrëƒ)Š‰‰ž›ÍÂÑLéÖÖù>Ýme3n±FÃ% ´«‰è™L¦üìôkÀƒ<83§xûå–zEw˜Ä:.%ß §°6GÅÀ
+Pû-<ŽtŠðxQ4cí¾VIlƒ\ªÖÚ[DÑ#ššê–jJ(Pûëiâ7øÏÞŠ¡hmB”F©öíÄA &zo_Žo²¿1¶
+ÁÛ”>)5y„¢„u»T\z]Ì8ÝÖÄ—¼9ü mg¿¿‘¸úÈÀôþLñ¥aŒ‡×»ÿÜk;Í8g>hb õ-dåø‹âøÀv>ˆ)Éò(à3œ’OâwŠ¸3 Ï»™E¦Dĉ±™»dy5Xž†»UâÄbÊfB…õŒZý?;ÑNèÙŽ˜rð(0±þá€d#­`¶ÓÁ2 HîÈwÝ$:¬MÃ@–y^Š1QÓ£¹I&¾kÝ,#b 
+(¥¼œ`™¼yM-Ó?L~Ñ8 ‘”èz§Ìq„ꀽ~o ,4“´(zó;¬ó%†G£Y.ÅîÀ9s–6»ÿÞì]‘ž3æ⣆½#΋öÎToèӕ阱2};êò…ÿ§R´ÈϼD\LÑš“)ÿòj0n¤¼ÊÍa‚þõ˯‘uRä‚]ET -´40gòªîMšÍ«=* Û
+{öRAêï­Ž0Œæ¼ú.™H;&~Hü…„ŠOíåV2 „¸žÞœòQƒr¡É›žn;Š‘Ûè·’ŠsIJ+½Ge5;ºSdïÉ"uYï#
+ò
+rÖ›ÿeá'Â)ÁСÝlNo‘
+²­}WÐ6ÚDVtÑ÷sïÄû#-€iÞJ 5Kàm63Ýåh`{“íx å«’ýì4(¬D$°¶•T²Uª(¦y¡¨é…Õsq¸l‚0rŠ x@{{K¾â%QýyrÁþêóêÍ|©ß¨¶›ó1n9É{+ŠVga´Be; îËŽSÂ?ÁòM’h&NÇ9¬„þjÂùÃÅßmÅÏÌ#e3˜%¡Øä½^oEû
+D’tÃÝÛ‰áf Rãm5ÇŒ?:%5T+Ž ¶]‚¦\µιµm$ˆÈW×l“‰Îc©iq/#Aq•x”P¯¸"Òª¿4 tjõ¦ *aGÇÑNIÖ2¯Ýö:|Cð|‡´@Hƒva_Û)ØF’;°˜‡âàűtv"s6~ ÇM}*à´"Œ',"k$SåÜ’‡×üùÃ+òÜï ÝT©m|þ«"/Ã9Eϧ’@hšðþôC%5cð‰Xo!Ž¢Â“Vqú%ÄQ+(0–ÚüÇ0Lq¶?•[Œ£¨gØ/ŽSôˆq”0ce?ÆQ‚_15}·ÇüÊ2fê°ä#ÆQ"“/Sš<ƒ¼Ù„N æ3¥vóЬô¹ÎØá´éçVi»„-Õ6¬Çbþ$Ëo®}òÍ*Qò,£fÈ™h=
+,;y)aÒ⹎Ê<ŠX6™ù.Áxÿïøõ‡*\‰cN“þÚEòŽèvRá:”“‰ÉW
+kµ“">ñ!uÆâ3d¡4gR äˆ5W­0(JL²ÀÕÎê¡ë†©äù1çe£üCCç^» ^©™g™?M¡tj—:†q¦3¦ ƒ+‚(s¶âˆ&w8Gf“eÅB³+N¤‘x²éÃÃ)aúeû0ðì‚GU“qxðéýº)ò1”kǯ]‚¬rŠäÙ‰‡ìžÛè„•ÎË¿Zÿ†J™¥í{S¢¨‚­ÉXžnƒ5^‘Þ¯©Ô•ðŠµ¦±Kg
+,)žeƒÖp3:hJe
+àûŹW“ÑÂíeÐeb
+â+Ò™ÑHkSãz%:E{*ðÁü©¥=ÆJ^h…ÒFÍØ:&7yJð;´P9a4£‹})O2Í…£ ¿”c/†ÕŒâáGkOvš(½ ÿc¼L’ë¸ zßA'`
+õq(/ÃaëQ<>[ŠÎÆ,_½2Ÿ±è
+ ÌoZœJ‰8µF\X7ÙGë¤ ß(}"£ŽëÍúÊu ^E2u©vù`*$glÉò{È´hÖº’ßuÕ+÷Š¼ê Õ¿˜_€* ÁÄÉ
+k‹måë™ë;#ðfÄ€oíˆk„›»nÈ€%õ’¢$ošÑ³u—òE0’áJ·
+”•ùv`QH¥Ö#)J ÿYy|¦·Í’3ÍÚ®žHåÌ#ú9ü†œõzH|RKJbÁºE 4¶ÞƾJc•PÑš,"k«~ú×°ÔÈ'œëfð! ݽă%#¢øU•™ÕBñ„uN™ƒÃ¯¥©í Â5ù×ês.Fæ·ÐΔ ­[b+Y^ ÙÏ@F‰ ýµ&ßš%`$Þäó"ï(’¼Æ7 ææžiö ˜ÏyíÖÁ+bòdÉn?À%(¶»,
+Ç dÜCÜË
+¥BÇóØËK€Âï¥ÍYǃ+½™#ààŒŸ )ï)Gðš& ϳ÷›*Ч¸+ó óéŒSͦý5IÒå}Ž¤ÅÇèF¶·oêÓ*3'WØü³zƒR>ô6ïcä0³’LÌöÀÄìŽò’"ª3(-%ë„ú06ÒR 1—Œ=~H /h~+Ê›YÌ¥u
+¯ŒB©ë!Ê©”{ãHØ3–ù”÷~Ac`˜áÍ8æ·A§ë“Ï3hðÛ CW¸®Q`5•·A‡I¹óôùNŽ1Š8XÕꩳ1D71&5zy* tª¹}@Õ&™ù±17Ðéºô¡ÈÄÈš½ :ÕÜ «Ô1F¸§ô6èÔ˜[1Où[ÅWcâø@CØ fª·ý>‚h6É®d·yß ƒn(ÚqÜL‚ MË5u»CáÈ"À¼ JvÌe Ã "Ïô¢Bnf"j±£!X\ ,f•åîçV”\óÞ'„€È% Ò‚³\ñÑLÒ‚”0Ó
+æo\WὈ\Hg2ñ¦¢¦5þ[6€‡øW¹!³ atÈ ?t6ÿÞ*M@[³<¦ßÔªJ3ƒ£÷+et
+û‚yYç ‡O†H‡–N)²š¦ªžÆUW™Œ“Œ²\Ð8ž„ø‡Û!Øñc„ mH:o³‡G¢6ô)kÅ‚¬ÖÚðgWX$¶’Ðkþ1$Z*ˆl›?*3VJ°ϳ¯ÊòçÐB‰^œ4d¬Ç÷±ÎQÇY†Ç¿ñ`(^©W‡”(f \"‚mŠX–Ú¾Šþó)äJÿb|^µ¦¸¬c°
+š|ùX2ç2Ù±=@ÔôÆôÓâÞA/Û9é
+†‹ÜÁ÷2Wu’¡Vo»Š±4Q¹ª?Aþ”®=°‰Â×Øßó ôé îå%)•‡Ë‚úË/v??HÁuÞ_ÊÓӠמ /8@î ½ 8|Dh8nj"¡°µš_âal4<„Ô Qtø"/Å+IoÎù릌8úˆò…çSZB)J
+¬y:#È(Ù:¸šiÉ1X>€¼™…éƒ^x…s6<p[pšLŠš‘g^† C çì*ß„­­”‚@›Pg‹K9_FkIÕJK¥Ÿ“%µ ûë:„Ž²¨Ü7¿i2?×؈OÇD*á¥Ûì8`zø2ºÒLäŠ Ûß‹£c¦Åþ¨þ1"G÷Ù
+¹ª-H˜ñ¢ýYóòafP[ÒðñLIkþ(ª¢ °ºçX0•ÅdŨzß³7Â$ÿVë:¦ÐIœZîÉ©MÙ£”’ɸvŸ`,b+ó‘ò¾ªà¯˜k„Ú7Óòß×Ó:'c<¨ Åråd­y×àh‹o¦ìç?'ЋĜàM‡pœ?þ´0ªl’ú¸JYØjtƒAį8p¡úð‹Þã,%†b¢6üÌ¥˜†lXÓó¦ÑÛÕ¾;J@²}UßfYC¤¼5Ú:‡1+œÜyÔÁ„,…ç^ø5¬Œ_Uůè6_µrÿDåò:§±Ä^n?òŠ Œ”z˜7Å5±}þ'¼\’´ˆa|•9ÁTNœ¬¹÷_ó©ãô@ÿé¢`ƒè$¶,Éu³^õòŒHgCå’ãaèúxAØÜP¸ØOv­[‘¡"7î;}8: Ä\Gͼ3ÑçwΦ饈û6d>d—³€¼'Ã~gÁô
+ˆBzžõ£B2¿.\S¶èôªÆç‚´DÔãà´iÓ¸¼¨¿iÁÏ#ã÷­ÌƲ›èx®W$B&IŠ´Hd0‡5Œ4Ëôö !$ä”cÂxdQ›÷QÄIôÁßy‡]—…€—ñIöÛ×úD«ôŸWÌ+ Š 0Áï~€
+ zq¿™W¶ï‰ ¡$Áí¼æA³9ë‚à1mX}ŸDu°- +«Z'g´ÎÊͧ/ñC« 356ÿpGf•®HP_+³#=Ÿ¿£®Ð@wÖ*¤3h¤{ôa„ý%ã>5ÅQ䢡=‰nÞìŠQÙo΢ê$j*G”¯Kݦd×ã¨Æ¶TÉö¾ü«›ñ3¤¶ï£ZÁ™ñ*jiƒ ©xÂÔ_ºkGlßÒ5¾È›²ï“B
+'
+)ýãŠñ9†À&Uaÿ“õ_8@º
+ùAxa/y„1¾BBÊ%rÑÓÌp"1dáäÇ2:¦Iµ~n‰Àh/ôÀT²ÔP}ùŒ]\¢÷ ü¤1hd‘cåчÜ¥¸èô;L–'ÐNˆÓ! ShQcSgºRYŒV!Òàæv¶Xª§™ÑO÷Øà<ü• °ÇaCʬ¸þiÄW:ncwš—Zn-à"bŸusVc)²¸{l&ûIcP‘
+‘; endstream endobj 164 0 obj <</Filter[/FlateDecode]/Length 22487>>stream
+H‰Œ—K’œ7„O0w¨ LA
+Ák½ v"畲ŠöOašû` úЃH–g›c’K×h⃗\¾vg*­B]vª×[bÄ%É@sFS` áÙ ÌLëÐ…¢8 q‚”È•„ ‹Mö}AÑpycRÊ’ÓQs’¹mJç
+®ûsèìDµ!ÉëÌNµ^žúÒÚ€®N*÷ÊŒ2'ñõrZôIêçµ–êÄDŽv*ÈÄ †bœTß›‘qgbÔ¨*&¥žLx9"¥ŸLÍP4ª\ÇÉDÔTû’«¹NÍ-05ýdú§~MˆyG~%ÙðÙ©´F–ï=öˆ_Añ–S°&´—´ô06F‡á‹óniì®Tf©_ gï¾/Õ{öƒu«vR½¾ª³¬'ð›Ê)1I”õ;©ØùÄA õ´\a{ï·â[ç<T×°§&àˆn ¢¨¹2Ê%vv•–g÷ôëU/"­),9m¿J6šIIÛ†(ï¡`êÂÔ õÐo|0Yëß,õÞÛñsa|ÜpÆ`WL"²!¥yÐL•?8èOãâRÿÜú˜<Àüà9ï;<M2TÀp†ÌV”"1M!쓉¯5ö“l›øq­Øoüm“ÙŠÜMÙq²å%Y¨Iý
+ŒøcÐ-ÝÛó~ºY
+Lm{šÝúSÌÍd½7åæ÷ª={òøO" v§0Ëp‰Í¹ó§CƒPo»1Pº(ˆÇà˹¸.ôINê’gÏ Üm¯ãø`“Êò
+ŽKAG —ÐíýÄIܾ‹Á%`‰4\cÅÔ²BÈ€,HO–Òµ¸±Û¹ 8ÙË„àsä›7ª\(d‹íCÖ@’Ò‘“§¤Êâ[à©Ý3ÎÏè'ù<SøŠ}A2¥/úÂwâCsÜ’ÄdxKtâëvÂàÏ'ÊŒÃßÍ’ {ß#„oh>
+‰û.!Ýpk(@:½ d¥jæ™*%½}ˆƒ8S *œ ~¿äbù-=Û¹äø™¢Y /'Už2°zM›±AóFM‡;!BiFZœ±ã4ãwˆ ¡M÷õÃÖcv7•rOQo;ÒÍ—1&^w?á)/ ÔÕ†”øÊmÊèìN ó¸&Ìߪp¶›ieO׺3~Éî¬NOY™
+ý$r.ËX 2À:GŒUT·T¾&yAŒf§Ø nìŽÈɪ£.±Þ™Þ@¤Žœ‰ÖueËP©/O
+YûN….1g°[ :‘ÿ¡‘+L>>ÎÒÈB(EdÕ{êÄ„àsxÎ`fv"¸Ê iø©„0>Ø»²ß %Ž¼Ð™Qøæ™!ìÛ¦Sé’·²åMðYܘ˜ç ,{…*Ø5: Ì&šKæܤ0ĸÊfXƒÉ¤æ4= Rkl‘Jþ œ‹t>‡¾cótáYŠV–•çMˆû i×Cqlƒ³¯4EwE“ñœè¶âdZ¡˜\’‡¤å¦Õáò0zÛ&;¼C6ïäýÉ-‰Ûh⤿ödØr=ÙMöÈ]”‹è\ÚËËÞ¡4ƒ¾ëâ/@Å“ÊÑtßë'¹{cg#
+
+K=q¸îh½Û¤Ì(Âmø:t%Ó–kÌvüýª7ÛÍ” såNÊ/®üù¦|xÕ7Ðï«ÎûF‘°
+º§´%B⺃íÐâÔ¥«»¥)!\x^ÇÙ½N@cT²*»ëä!8L4–#bÅÒ©(hû«È¹Î³e©I=ñP_Û?+Ó«‚¼…A?:~½Ã’µžÁ`‰êÕ¥ð$§O8(;ùÝ?ÒÐÙúth°È~wM«Û“gÚÒÓdh‚²EĸàH𦠌Vƒ
+CevôNs5#ã~ù.AË0~á+
+¾³$½«ìNraÐ>¦¸q$Ñ4Vf ¢óªêÙ¥¶3Ávdv,vœT2v³ä÷M~“VÝB…±ÀôpáðC>@Þ¬í ¤,¸óˆ²ëýc0ãÊwP½>
+Ó߉À«ÛyRµçùøuçyz®Üݲ¢`£jÚMÆ}ì`†‰C¾¾ óØ¡ÙØ÷}éÿ/w$;‹
+¯`ö01ÁT¿!eBRV@AdBöÏw$õ5s§oAâ²Çš¿»¥£óà£Cˆþ·8ŠÁÁÃ.ËêEyÚx1N>þ‡ ÐÀ"ë`Ú.I¹¯:òÄܲŸÝ¶©‚V¬Å#­³M@ŽaOéIõ¢\ áGÉ
+¼¢ïJz”Èv‘ÖÑUœÌÀd k\J~W‡dO ù®/¾3Ìq³.Í—…’.Šèrià0ÏEs¢]ç¨ÉáCšïÞsEŠo8Ⱥ§»Ž%nîjÖ(Ñ¢d– ²Õ¯tô¥®Ï¨ßD5èÿRÉ“
+´`ä£d"P+su“g¦­€ÀÈ÷L«.Ÿ$PäE¤
+©!;ꬹaw°üøBØ¿ÊðÔ¦°û@¥>ÿ³*<}UÄ?5/R÷* H2@õE B°TäR§hƒ²ì
+B\€¥°ÅBÁçm¥‹8 «Šja|1=ç$ì4ͳ‹ƒ%R
+öèVòííÏ·üþ Ü^>ÆDé5 Ÿ£C¯a¾¾ßÿzQ„ÏÁë iJHŠöK“Ôក‡$  ä¸Ì;õXŠ=A¾<¤N¿û9^G¿ÚªQÄ5
+ø+Ó¾‚Á*ï
+ªâl„â[…Rý*eu;Pk|e¢Ýç¨"a‘Õ„; M-?c@ö¶£Ó{*V\y¸!!©‚•$%†EÓÏWžUf`ƒ¾en9¢ÄR 3¾%ŸùÃì·æ-9Jð9“еÆ*(ôŒzƒ¸LjÛd¤·š/áÂî ¾’¦†•dt ]!­tùÂ*‰É™r”TEOŸ}IH/æal9ÖÁ™Ïvæ)ÂBkàØìE&ÞHZÆû¿ß¡Ué”»ôö¢äD…f=¾ÕB¿˜W=R¡° tQ’!Y´«Í†Bó
+j¦ !Ïá-›{@IŠd:J ^9vL±Ï"à ö!à€Öãwp¢1ö4úÆÞ…T°~“ßT³ÏØ$DÄ0¯@§!îSl­Â¥‘Æl§dà—È*yOÇ2EÐ[Øœ%¾ šƒ°ïÈ£°nYè<VpBYæy–— åJd=œª•ÕÁ*Ÿ±ã(²V× ±•GÚþm⬤5¬;ŽÃ`_q¦²oùEIV·hú&º£¾ɨ€K.Øý;x~k*L…Dr‹”ØGMÆJ¸—†Òz‘h`}Ðæ|Ò
+94¥"Y÷A@uL’ÃG8l<~‡o5k Æ‰Â{ôyI+0’5–CR‚­®¢üJ¼ÚÇ[ᮈ9p˜ýd
+ ‚˜¹,ÿÏÏgÊŽ£–+qÇá,Æo#7h™†h%¼‹Ó=Ä Åž€#èä<?ÿOGOp¦ã9òECqËK2ª: Ñ¡@idØ9©ÊÇ…A¸˜bŽâ6%m æíE•WcGhlòïÀ^‰ý/ÚÌ(iЕ֥ŸÕ„’Ž$r~\_HV-ó~Ö–ŸFÃÇÿ,ªS•ýb &^Yý°Ä£m"®ƒ›DMM“E™|\_§Cbµdi.¨Î.]9{±²N
+x©ùÕr|*Ê…=§ûËOB
+B
+FÕè1ÖÂÃò,áC‘UЄ¢0¤<ÌYGo SWW¹*$%ŠxtlqÅ£%à
+„æ É¢èDz¨þg™ D°[ǾS±Â2ÂBãظ"悪%PιÙÿ wö3ð¡‘ÍX)ˆq§ÌðöW›÷uãžìâ·ëZöF´•a[¬eS2ÓÂå<.EV‚·§·´h?¾ƒ”ÀÃ:~å‰0ö*š/QBÿ¥•ŠPþì
+ébŽÆÉkªAãŒÉ—ý3ìàTAm§$w‘ß\ÜÆKØiä»6ò¥$:L?afBƒ½H]é2 }¸fËK
+Á>J8”XÈæõ»×éêCî¡›Ã5O«@QQ"O¦à
+ѸԠG˜ƒÍ5ÐÄ'N0ãï5ù‘c $=*¡O!\ˆBÌŠ¥"@Áɼöêl)½øÈéù…³mX.®yŒÐ= JB£ÇI…ÿ®Q~ygÓóÞ/*”OªÜI]_+|?Íœ`kWúËWn@]Ü´NB$…ùÀ£—ˆ ŠÜå„RÉ)!}9°Åu +Úê9 ©Tôâ Ó÷§úªÖ(äœ-P;ûû}–›‰p#îánTûhµ°y=J¤Ñ²áЧoƒ¶åÞEc£7KÕ«/ mÃa§RMü¥lá
+s¶3„W6ª*\àÖÅB ¬®°JEà`©33OÉÆÔN‚÷l4Ë6îsRY­
+4ªóš1áŸ&/iJô5Üvž&Ÿ&ù•u-ÁöêI[4ðùçÛO¿½¥÷Ÿÿxûéï·üþËûÔè Yç™L òÒ®Â.píï©Hû ½` PûK‘•0î ÁV‹—ðⱸ Í(^:¶v|™ŸÎ›³IF¼›µ—U³"…NqÊ2Ò¤ãNÓ‘fr®¯Ì¹}z”tùÚž«À%âHÖ§S²ea% ²è…=}²Þü¼:k>ýú¢h‰úñÚ 'ëEœÅùëEv±åeV<%`&g‘âÒP‹àDÌ(éjÙä!mõ‡› Kÿ0^&ÉqÜ@=îÀ00ky©[pKÞë÷‘‰–X’:ÂrXô'
+ÈáŒTëVŸò^4OÌ—uYuÆ»Á¦]æßÎažøŒ(fœ»õ¯®kVÂéÉÍu«””‚˜½Èµy›ù†mAjW½¯ ïŠ*·´ „€ìá"™}A <¯Þ–—[H°œr•V,V[ £nW±Ž“= ¬g¶sصºJŠA  Ü+>˜Ù;B0NFef¤d6 P$;‘Y¡ Ñ­÷WÐa‘pE ä^÷ÆJö[&&$¿ÜÃÏhÚØ[áËH¤µìݸLõ@Â[Pˆa†|ð«æ•UÌK9€à@È+”Gâwîý7ˆåÄn¢?1ßkUqVܤ€h5 4ù)Ľ–‹”0êÂ&–4NûœÉ½5õx+)[`WºšìR„ªŒÂ8sP=¥”®ôå´ §e‡Ð*q Œ¨‚6ëç3¦°mx4 S¶}¿ÖøUPe
+¡:I.(òY°Õ#uoé!ü0_écE”¨bOëè“• ´®lcÂÇT<ªÝ5›Ne‰Ð
+ˆ] 4,wÚñ|l𖬀[ÇÑpEÄüì€W©ªÌ¹Aøßt|ráv€ø§ ^JXpõîÙ­¨DÖ¶S»8È32śƒ?l
+h}
+í@™ tøÜ¿A"ò’ù'6ÂJp
+s”Â8*
+ˆîàλXéâUÆ¿á#f~pëó94JåÔýÊ%ùs’¯7”ÝUäe/œÆiT–9
+yðOÉ pa$Ä»ŽUOxU;'¨îXš´]|ãR(Ÿ
+óòmJþ8€ŽóÞÞ ªÊ¦Ì¯‚žwðð¹@ßXá¸\ß!Î.u—'ix§Ó¾´rhœ¶ab*©!ËOέ®òîŒ~Þnž\BÓ
+–êmADþ¸$¼ûÉ,È_ò.Ü?á”ÊÝø @e(
+~Ý íÄVÁ̺A¾ $-X<uwk¦Ûæ±-Ä×®‰UŒ‘ÚŲ»$Ìë¢`3hóï d¸P± ¥8w§2‹+”q+Û‰þi ÂG4ÈÛ$Uö¥&ºGº@Ž WBâYhf+wæù ôÝ©Þ@†Ÿ;AX1ÛzJ‘C~+K,[œ•œ“çý0^R¢`ú¦åx^–j)žÜê‚Dí.$IUíÂÀsP¼,Áåø
+:U§Læ" Æ»+áèJËŸ¯p÷¢ø·ÿt^Ã6cÇ¡«T
+šN¥Q Rôu °cYº» 𙑱õ2B.œe/B-á±”ŒÁåéˆ.P>v×ܘʬŽÎOª[-Ø uÂùãv B‚•P€rûƒFu $ïLî7Z;@Ö—A†JFãïΙ°gf¿ÂvZ8&‹ìÂ=Âü0±XÑh•‘ãTÚIئÕý(`6‹òÉvõì ¤Ý€
+™q¦hlà{Fx¦ã2ƒ€FzŸ‰8çÃHÝ¿h;­ó—×áàûrÛü3ÚàUø’ýr½:¡]uK6¡-cc$wVd„ƒù; ¼›rÛxò’6Ó]¹6üp™ëÜu¿Cq®S|¨ðaпuêã••ÑvÛ®J& |£7ÔËy_'(¶€~1WcU©¾«ceªœ›!y0Ù ¨$c_I6›òb'Ô´þ®Ž‹ÿÀ4»XŠÿ¢‚kè^tHÝ`sÏêÐú'Ï¢!Ë¢ w¯wÇÌ
+æuŠZ…±Ð™E÷ûùã úÖúÏ#ëÊ,2²C|gããÌ8®xn*œ©±tÕo¬!ÃÍ2+Ns¡8Pµ‚MØEfÜyVcÙ##b„€-gEÖš£=±ôx ^Ïtyp‹,Kƒc³`ú|N“\Ew<²"÷’n£ªÕç虺î¯âá BËfÖÀg¹2;1˜Ñú ðˆcŸo¾1#Ó@,øð
+wªô§? —ÍcFš†‚Ÿý©¿¯§E!`lbS2«Û(З¬ ÅÅõ½Å8¤Xü×vuµ¢lh[]/´kù ÌËæ:&B§­©„YƒÝˆ(µ ëÚ…”9)¦:ÆÝ9ÌQ@'F®Û@bö¹NÅCLƒDeAY©¶w‚’W$Ãi= .½-·…½¿5.WÐ)¼ò¹
+
+PÓCËgR›Žñçt$ˆ6熸¸ë@£
+l\ò‰áæY¸ér@»DYÃpC=ØR&YÄjgóª!]¤µfAÇ»ŸÁ=6üY
+S"«ËÃ!PÅ(–e#½ö&â‘™—qq•…/PXÚ™6Z d
+‡]<=ƒºóÒƳtno)QÆhÇ
+.ÅÅÒÅ_Óñ\ù[#´Q.Eï/Š& ùÓ Ù/ªt€ßâa¬(¿[EËøÃKpkC¨™+”m$b$ÝO#JðÙy?jœöü¿_ü×?ÅÞ’€H;ñ·=!–%û9IæµÆ.½Öï‡.|¤ƒÉŠÃ¥(ßðÄ^æd»ž†ÆnàùËöa”H  ødÆ\ñ I&Ó/Ä’ñÉw´çv£†]Bã!ò
+•áAï€|?ï%IŽ¥ád]øç?÷"ˆ%Q†§È§¢+k™ãâw\jA”é\OWlBçæ<^Mš˜*‚Έâé¿‚z*À²%4 á%\«I°»9¿ŒþÙäWvbBFÒ‹¬À^ ”<;˜TÃ4¼d(Ž]9\l—ªÃÚ†2ýJ’g1ò?¼KH‚äE˜3 Û•Åv˜¦î¦°)S6Œª2"?V%Sz
+”+ E&ºhA˜ª'-ÌÐöJÃõ/S_Jq˜ÂŠ/¡*(yÏ¿~
+—S*¡ÀîâÅ’w‘20·’
+Ë¡é]6¶Ä> @¼ÊþÀ
+'àôQèpõ@¼¶q{ö÷’hð”’ø±^ÏÑ °
+6öª„ò³ C’îÜf)“‘˜Á‡ßÚùÔ¯˜ È :‚Œþ¸ KÁǽã„ÎmžÀ±r2,ˆÊ?>õ[¿?lF!îÙjÉâ¼¹ –€[}ÞŠ¤œ7Æ6dûêSì‡Émsºt¡µð)øaï©L¤HˆÄ7®K“PpM½?φOV…ü‚l
+ÊU3nw¨ 4 `W•¤„O¥&S¼Ú+±CØêÆ ßÕ$r5
+B@gª^Á6(!pßxûÙú”8Ìy@?qÍXSH0>ÄRADcèW[Ü´þÌ ŸÖΧþ\$*“á04áŒ.EC¼ JÖnS4Œ\0³Ú¯¼M¡/k<è`÷
+zÀ¯°¦uxšYäáÑ~:(MJ2ÅQ"€ÝÎÞÒÚâ;S@ÂÌÁ4‡˜8"‰˜Ù¥$P±?0¤×s´¦ø8>œÈ5q~„CªòÄ£`“ðnÅKºô×:ö@L¿¨ÅÊò¢6A0CÆ3·
+[#cÖ_À‚W ¡1À¾[< wPáa¡¢0 º@G,û9 ðÀqppè5|Ó°Ïà6×wÖnl©ËÕçùÔï©]*@YÃ
+ÞMß»ÏgA¥¬õ%'}xÙFf¶ñ…kP*K ˜¤•ºó.Áava¥¦WÜ]ª€J—íîçŒ7Á•e‹ã6d½b¹Ô4£DK=4€ÐµžëR§«#õ©„¹—Ží”õ”½}÷¢DR0Ñœ¿-ÒÇp¤@¤¹ó¹Õ
+f‘²ßGË?“¾†ƒ’„Izù‹ådVPx)sÅ°¶ð‰,s7E8# /±¢)ìpqºújßÍQGk:¡Ö°[I|ª,FÌüÂdu2"³¹·°s,
+Œ‹©¼W›îA‚àcâ±K›œ ¶…¹6!€é)®®æ W©ä‚b¥>å2ÉåAô0SÓ³¹©8㽤›éd1ø™)S.ô7OÅÞÎËÝS²6-Ò´Qïìd#Ó6œ«Dm#ã —“¸ç žÐ+é†ËA[pk2¿ÕIë½N‡š•NA¢¼'†„ò‹iAr˜ð®‡QuS96)éx¼&Ú“£´Òò‚Põ•'bWÑéd䤪2q8AßA‚ØJÁPx›+l°kyCþì]“24ú`OžÄ’e˜³Ü„
+l1~R;›+X¼
+ül§&“L0¯›V¨BÒÆ'…^¯º$¸ùüŽDF–6Ìzì=÷
+Òµq
+í²¬Tâoƒœ=Ò‹¹í‘
+!ÓÂòôÚƒÞ÷H<kÕæÖûI£MÔµ©¹{¤W‰Ûþ'……´Ä^íq¼–90YÃë]­39¥ª@Ûn‹¤~42ÐÊPôø°HªÆ•ÙîXCØú¾HJ#´ÈÀ¢y[Àm‘<»ÝÙ$ ·è³4Q°1ŸVÉ#(6Ú•ÿ“M¯ ãN· ïߌ©MsýÔN*e¨ QÑ ¨åfi7Aˆ9ciÍ "¤Ñzð$•m‡¼i42fASgÓ2Æv–À×IäU³†»ª}Š~k²‰8ƒùJ똵?@‰Ñ.L0™Sÿ3[H‹´+AÁ¾D9™]¸ˆ]gÕ¬†Žìó+ºÚ ú¶ mG¸lƒÉCƒ…FX=Ô0\-i O/§oÕª\Üy6Ì•öʼnæ~yaBNH?Zw°ØÑš³vû¼
+ cz
+·B$â¸`Ó…å#Ä¥Ğ˰̤9¹éä‰s‡eµ“0–äÃrÐ$…æ@Ob>µ12p½Äê«àYˆ;ã0‚|G6»ä…Ø—<Kg¤ñé„uòP4º¬LoÑZUIerXY ˆª´¢µrº¬LHÇàÜÏEå³ÕT>‚¼’¹n˹g~jž#ÈCåóN*ÿ8£ •CcJàPxð€Ê¡RŒ¦afX]å „EŠ±æ¯7b$¢
+_;nµÁé†ÊÄT–!hcóë • AÒ ›r •’„¢áŒ¨L¦“D™E³¦ô€Ê
+B½¨ðâò*ð-nq?ëx8QDŽTÖË{c•äq/Ro¨¬ «{ο2|0îYL‡•ƒ œö@Áb{<'¤K
+âìÅeå€S4å|ä5\+D]¸>Ý“›ËÊ„dȉ_E¬Áeå3äde/æ`å#èdåãÆ+÷XùHàÉÊGNÓ:Ê陟ÚbòL†¯ßü®‘IØ
+š[›ËÊêQþbÀqÉúÀÊjõZ(4˾òÍ%Ž9Yù<‡• vìQ1H™„ò¹¬¬¡¢t”;D—•UòìQ9S²ÄöÃßaYÉ"‚8´¾kuƒ\¯œ,„øTJ…¼ä§sòˆºb§§\XÚ
+Æ®c²ö]¹Ø.<YÀ4¦¯sÞU'ľÄÄ@TCPøtN«øHÁ¤ÒþY!XÖˆN®ûªq
+m¦_^±î@h)F‡YÙHg3³†WQÂ(L±g•
+?ã@!ù~Nú/¬Qó,ß^±Ä)%lÓ?'#{ ¸9U»qAijBŠr[! NÊö)”‰9€z0åÉ$9É6J›Â`¹ æ½®cxïTæÉ«ug ¨<·0BÞ:ôëäözb’!þο9gÏùÔ/‚ÞÔÀª÷ Û?þ£Àˆå°“Å,Ìw¬âÒÖ?*A395ßOÂ'Ë&˜* êÛ:ËÆ ‰·b{’è*ªçâÖ¿!/¥…‚™-ë+-•ºàcC#42Y[7ËFìCª1WSƒpá à]ö©‰ëâ«|®»Ó/Œ ›—-½ý} è(¬´’MÍÉÃir»Q‘»r ÿz»džáq)Ø9x¡_%üÂ,D ;‰c@“PÉ”å£Gã
+"M!~Õ¡Çà@VKjïÝ»q]Õ‚ ¤º9*‰}˜)ÛÓ‘44FÎ@ Ç´¤Zölܺ׺WÉ´bSˆÌ³éöØ8Þ©ý¹Ô^Ůٹ"¦’Š¦Ì`!YÊ&ÿJó²8* n–”õµäT¸°O1§mwt
+k\oˆÂFUœª±¦FÙ×9UŽ7Q@frVËõ²} eaïm2gqûúã¡…iPpŠ•×¼ßsüÛ Ê
+œ["QªŽj!Räp¸$Ê·™N!“$Šñ©¼ØnlN„µEJÜ)OÇ
+Eà0S.èÓ=ºÃ ¢*u–|9[
+PILžô0jž]")v\^ÐŽ¬—d€9k*â÷ è#c
+q‹P= Þ’¼ÉfØóËi˜9©óÌ‹]§VUQÙ ¡I1™v†´ƒ-I¾‰òšÎ,6—ë›ÔTŠ:ß>çÁðÐLT
+Êb`Os½:g¨¥¯öm86SŠ- ã]©($~iæàIŠijÍ^¥G £U&º8qaAªpl<OÇoàOϧ82͸dÌñ¸o>ˆ9
+…r‚v(U =yª¨èË@Ê•¶¯”(Sd¼kg+ˆ7flqÝä•P
+\ŸgÀ‚}‹¶ÀXHç06›v£ZvcèŸúÍq1ÔÌîVÞÕÞdÔµ¿KIdØŽÂ3€&ô>W®H×}nû?Ž+«y¼|×;@œÄa81üö0ìY“ æ´¼Ëd²YañUЃ&}z_{#>-’t滕¼;öŒÁ-cLඦ@„Í”­æ8)¡H¡pg9¤²dºóÍç6f‹ €Q·A-°A#h€ g!lKkD31Ñ §$» ÖMž›ýC2„™ŒÕˆ}iDîÇ
+±-îs"û>1é%^dÇ»Y Ùøq¹§:®Îi‘enl™ýÛ„%ÈpDá¯^½Ša¢+˜¬ãð«ó8[N½ëÕ¯Wùóú\êÍpi^ï¿=®ž»â6k¹: ¤‘„ÖȾ»Îˆj=øK!Ö+çÞ±Kl4¿•ãUœ3ÁØ9¤~T÷Áñ>õÞsÎŒ+ÃLÇ\6Ê9‡H“—Å-Oÿ6øÙè±üãÕ«’bvƒ…é犤0"HN%º5~‚<öêãÍÝõþËa ú#Ûdﺿ²è‡\„‡›¡÷è=] ×$EK¾yª|_B;Xë u«ò}#{"LO 7Ì=Y–Róÿ6ӢѠ~õª5î ©¤ÿFà÷ÕyÞ,Õ8WEº¨zùË—gžJ„Ìr>õߺ,tÏÀOäàªê·ÞwŽ±Ý±b^)1+M0ùk8z%¼¢gERb ®Oá7øcAˆY8q˜ËݯAdä2xÏUxíí=*LÂöqø«ÜÛR=˜–$h—áÌH6ì¼b#šÃC6äÝL•n·FŸ±•µû§ô}•>áê_…ø–÷+%zõ%NYãž®Ž¡æJoMW²&`¢i,{ÒŠAZWa2|“EJ¸qý²7ý $›Ù¨úŒMïË"éÎØÈ„ù›¥¿
+’îˆÃè.‹1÷X8 2å«¢Ë<Š¬{gÊòÑÁ8äa{]QHÍuYõ#•‰˜HmjÇ®ag™Ã#ý41‰ÚãY´ö)’F«øy¤ÎUÅÁx±BeCRdÕG&>TbŸ
+Q£ÐÌáŸÃì(<1)ò‹ûÊ¢2 ÎÌ <™i듹³Ú@i‰Â,Ûùñæ€ÜV°6…³q¥çWAêzæêÙâ' ~ E)8Š³Å²Æ<’óØ®ÊÜ•Šýèc^u«’ej‰Sêáw ¾Xj͈g¯ ’a®!æéýª[õ}¹¾Â$ÎquNˆ¬9 ùص¡E/hÁB(JŒà¼n.k-5‘ Œ»Y ·:?ƒV”WAÄüõ
+{ÿN)wLРi™¶ìë“+Éšä)4a=A°€­Öî€>/@ÈÞ ¸nÄ}[Tù¯HÕ©ü"´´(Ž¶x+hŸ"D#$ÂàYá0$vAlJÀ±î¦wª6VÑŒ?H„W”‡ëÉ~ˆF~Ñxó ¿v‰MS£ìEƒ@cÙßUŸ ‚ú— 7Œ¸!8«pxÂT"ÁùÏó¡Ä^Pt99[C)pnØ›RJ쎔È* A¤H
+,ÿã…žü6ô¸•FÈ"mDÇ}oTÁcS¾áê®X"ª׵哨)£Ð%ûÑ0C°TóIMǽ¦ïçchUîIÙÌhÖ<ùZŒ›YˆØ#L%÷]°IDq =A0z°Ô„³#Íùxó1Ræ$ÃX¶e Í2'‘Ñà¼b×ɬ,ÏD¢ =äÒÍÀ`³ Çhp´xëâ&5µ !¼4W?h–ù ¡LÛ+ÇÁúÕÉá27f,¡ÝrÅðt°+‹òº·\Båõ‰¯mŽ)ùµ© "­-Ï0ù,Ø¿#Õ?$iéq™éä@äe°m~=.~„hšu
+~„wPææãÍÅÀ¡2ZMÚôEýoÆJ”ä`¾YRqn<¥
+F‚ðÐÐ+¼±g
+TçÁç#»-87<ªqè4
+z¤åÏŸp·¬V~ûŸ 
+2Í¡yD‚f¼05
+A` ¿ÓyL”‰¶¾&Zr~¥†á!ä‚òçîTr+7ÔVïF¼®6qÉvÝ'jPô¥ÚmØÇ7[kqïý­‡åh’µ{ùGsoÀ“Áˆy·KÕÅ­59“ÁË¢iH÷#‘Ò@5­ëd åx±Ñ—Cp Ô‹Sø¹qj‹ó§õÕí°eqÝÖVƒ6Õcâàʲú'7}U:3ƒµŠ÷gÅ)Ö±r
+dÛfg q ‰éôþÔŠVfÚr[FM¯¾àãs¼KÓf2J±¯Ó@€ç¦›ê¥[’ ƒDB¸XT.»Çmc¼éöY®ñ^¼Š±×(Í¢J¶±¨×öLê^Ø Ró©‚ÙØŒ§j%€øK0FJ¹ËBP°ŒAÚÑNL’Ÿ†¶àÔ$ÂKN]öºÊ²!@ÞN©l|L†^;"5¯ £ó§.Æ«; òIßç›®þT«Âô]dCÈ|0e äjDŸ’äøˆI_èóH ó`¼Ö„hMp\3Ì3ˆ©îð²ÄN¨88骷Ã3Ü›§,O–Ó5Ÿ6Gb’,0­Ivù†¤FÄÓ@àóom¤^g÷íÁ~ÄËSù4¿@n)iøü8Eö¿6îZ.½“îoŽù§Ø K‘¢¤u¹–Vèî^ýÕ2s@APûê÷Ä\'‹%ó5P-ÓˉüÚ°i°œÔ«^E†¶é–Ö¡âT¾Âæ§'*úz(vºcÇ
+8w(yLƒ£‡ž¦Ë8„§†>GbþûŸ
+ì…踥â­a ]Ë~btk¨¾ùê}‰´eéõ×qžAÊëç$ø·7MÔ¶V¬^ÓÄ£þË@ô Bj¦òÆ6É äéÏÊÁ ÷v´/‘þ*V¿/‚Ç}u³7Z¢´ü]*äYIJ‚Ò(l[gbz°9+{&ërzMÇé ‘•_;œê;ÁµòNìæ…L,¼NÌÝ«˜÷É%ì…L˜=½0|¥)¬,?1ržKQÀ³›Ã!D”5í«I¨Ÿ²&–q:{‚ùéë.ò­Y?ìö`‰q›>]«iŽ(ô8©« 1lª±üõÅ;9 LÆD,øï ~›T ›žó`ôïþåä0Ü;Å@wÜñ½¥_C³•<¹Iﶡ·!†z-ÍŸf÷@©€¶Ãâ¸K=^‘TŸ!¤€uò2°L*"Oܱˆì 6b^ªKÂâÝgÅ»g8
+68s“R÷à<bJH[U³ä[qaäÞ¿ƒCñc3ÙÀ·â¼.YªV{*±
+]²ƒqgý „уGe>yªüSPgþqér¾ñ¢Q–©uŸ ªAÝx|Oný˵v›™ Bþ*F£ú9‘Ñß å5(b½áã=H”ßž”³éRÉdž ´qWÂQÍ9®!á‹;€øN .ø´~x7X‡îÆÓ+²æu!¼ÙËsJ§Â.fÙv¡¤Æ°ÇÖñÊí ^|*ëAwwÒû1`„?[†]¡XLl÷A§­GÓ
+Í5·câc^ 6 ¢–çD8œa¤*åŒX~{V¹¸ƒÒÜÖ©p}íœýí@&sͬpÌnÙ ¢mŒ€«<+oYTw!Ç*LÜ­þÏx¹äÊuÃ@t+o¾”4v†YŠ³ÿiN‰TÛ¯[׈¾’Èb}°|s’6ðâ‘ðp89,þ>ˆ,ÊGÉmô¾x ^E…{š®’Ñ06³–jqZ‚~°«|GG©ˆKåly€Ã@óœA?7VUÁÊ`T'‡»zã¢1äcÉNEeu¾ÕK0ƒ§åq"!ùˆ×¶ªù¯,ô‰2ÿN%ü°È´4¾Óyc`+ó|(¡ý<<”V^G½ÁÊRh€ù•™
+%ö¡¾öá½H£¤U\Ùm·AyW
+ƒÎ–ó„‡Qc `$Ž>\Ñšöj‡•Ã‘P Œ<¡\¯Á|0omtŠQmÊiب´„„"„™8G£o$—T¢L
+´~Id,9`´—Å*ñtw-H"ê¯.Óf¡ã•W2naícZ.÷ؘ1,÷—qè[IEãaÛ)-zúÎ,¨É„[”)ì0ŽD š²Sêã)…eå†HŒ×BHË'†%'ùn6%KàMå¡ä›iý!oûõ·&6@bMl‚Ø:œ3„+ìšò·ŸEoL¸+8½e0lå8¢ßiy—Àà
+H‰”—Kr\·DW =p¯(ÔcM½ ExDïúN]àÊf7:L;–ØÌ
+õÉÌr›ÿÓž‰þo)üè}úCF›:ÅZÆEëMSÅôãsT‡™ŽémŸ4Â[·œÒÂ?.HØhÍ­}Yé=­E—‚Ä£›»Z³q€üúqlFªÎ¯ÞC´Dͧ²ƒ±ìªš&3
+‘®Ý¦«ôÔq›=cÆð>×Mõ5bUï²1ÓÇŒ˜Ü/}£žÓ½ëè×Mó¡ÒdDwo¤tA² ѬgK{ðc#ØÁwâ¾é¤BøI
+Ãg#$ûí4P“IÞbW‹îSøoÚòk5†)÷(}!òîòNVùäN"£8•ô•CZЀ¶rØ¡‡Þ)ŸˆSáŸ? czôá}ЕA3ÇwAÕ>Þ†Ò˜J-×Ñ1ô‹Î¦ÐçwfgXBnP:IñÝõSõ,ãã׈@<T¹“qµü¸ K:‘Ä*>¬Á\4®O_Ë "Å8¯íøk]¥ÁÓkd2Ìæ̺Ú:G¢WS‰÷}•»ERÅAb
+!Fb°ÂØWÑn¿¦Ðs]ŒÖà‹³/y‡ƒdrÜXçT1¼^໥…ãé£ézxÐêfØ6•‘’ÞŠoí¾ª®nLë¸è·H‘ç@«¨Åõ('âJ…³±˜u®ÁAbú†P×9f“f ’¤NI»X_Ì
+ˆÙRèðzÂòlɘ 2²¯â«£B|AŒ&¤ýçùçåèS÷ûªgP+ÝC‰½Ø!S¤VE×¾µT«iZIj¬üÑS‚lÒ7Âä×>œƒîü!´*Þ62¿& ò¼Îñ™H)_s?Ê$S4M6½þFn«i-€ôÛ»sŠ¾¼"Ÿw4põ‚Œç‚Ì™¦Ý­É†X °™‰îGM:gº²·Ñ a—1‹õ/]$ÑÙ+9¤Øèú€ƒÆš†Õ˜PßDMÆEPqC€ã
+«ûqï*ÿôâ‘N×ý;è´]œ@OÛEA˜bÆü¸]
+‰8à¯CÓ)= @þøå×n|Õœ„’ÊEWY$WgËtË… ˆi¡â\º…ƒ¸U‹+ÆåzyI¢Òhù€îówÿÚ÷˜[¯øF2LW
+?›\ñ€ñ¡Wx KìBLe*°öVÝð,H‰Ò4–:9ý±å%G­n–†r¨âº… ç×lœ_Äv!ëKò›ž2‰ãi¼„ùRñwCŠÄÅë4ÚÓ¡
+:ƒ”»)έaIR5Æ1χ‚ †C"\I¯êLï!.ÅHGì(©ó±_êÉI»Ì\GŸ–¡¾˜™ÅèöKQ ¥N4ÊtÒÔ­_Šyè-„S /ý·Ò"U¢f·_^yî´Ë Ä}ìüRT͆>*Ô äuËë~œfùÍhÚüv¹«HèIý”^fD‘KLÛ‚
+c^XÝ^ýi3Êyt* =b üíV=ù‹£J·† /h®3Dy½¦‡A¼¡’X´^ó’óĆ&JÎíÎŽ¥'ˆ*öA&JÞÁ¯öawüõ—ãáIj•=Î秺>§ëxЂç%•úäxâEéÃV»3jqCb¶wJ«8 wÕr ežg… ìcNß®ÓÂý&\€Qa/£|8Sw1»9ãë"<‚ÍQc “•ÄhüáاŠ]ˆÃØaMOSÞAĉÅS$×¹`&>é΂ÝÎ ÊÞ*qs¯ª¼„q5+C»Ù~‡6õá-¿@Œ6\–f£¨©¹ë@×T^=›!š ÃÈR(W)ŽaÄs5dæÕÁÐë÷—JiÈ%ÞºS)OUÖè’ K4 Gr#&³¼Zñë€HÂO'€à8Ì
+‘¹šÞ,h|'¿Œ)í°é¤‘4ÖÀ¶Çþ“‰„²ñbF!ûr=.ÀŽå%¤ÙìƒOéÒuyëÌÎ"÷0jtInƒoÛ—¾>ƒëv¿wÍÝï*”ÇcǪîí_"³dŠøk¯Žñózp¹–­]]¹ŒŽR¢ž)&Ð ‘3¶° ¤Zª¬ÎQ\%
+8•±•úˆŒÚtúÆY³ ¹¤SûÔ D²çpªÙ™½B¡¡´||)ÑÆHòÅÎD$¢UýË“羑’χðÊFžá‰†r"V÷:D0$ž¾ÛÈ…|ß‘XÖ6ßËK³)Ï%:CöË@$ú™q§j+Ãb>c,CsuÖ¬õ†Äb J‹édn¢â{WÐ:"ñ¦<ZLo*;géS ªJìŒ6ZJð0Ìá¶1 Mj\%oýÐ’Öƒ0þ$úÓ:8d‘rÚ©R–/æ /þ$¬Áõ5Û/‰U=Šô¶“f6%1ÓÎ ,ú¬$ã'šÆæL:¹•8Ú¶û
+õ„!7f©÷½Å4Ã9¶sÆî@/€­sl·­kÁòl²H¤Ä6 £ýgüH\ÈWÂJ-¢:ØÔÙ—WŸ¬œðÛ‘'šHŸ¦‚xd¨tr ®:]A?@Ä8‰/EdúóD/Âè¯±Õ éW3»…]Vé3ðb&ºy®RÁe'óҤ˸³½Å›Š8±P4‰ )s´Tö:°ŸÊfš_d5Ø*÷Ìÿ{{
+>DßšŒ_|Z“‡|ô$¾/ØöÀì覞CW,0€Ø´ éòÄ«×9œê±‰A²D)ÒžÏ{Gô Œ»ÒTK`÷«CÔ@Ø!”æ`>}Œ",¦)›MVã¶Uçj:§šl gö¤–½Nü„§PU ¯XÎ`‘Í{&þØk² (¢„>ÌIâ†xR7¤ôÚ5$Õs5MMI¶Š²3D— ýí\p” O%uÞ\º©}CzÕíòðó{Ö trÔ;¤JÕí%Î:hS @…ºA*ü™Œ'ƒ <•Fšm†W½€LT¹aº^_è†Ù¨Ãi@ Bó›PDØÅx
+šÜúF¶Æ£Q^)úbCcÓæ
+’s¹( LÖµçÕ…ÑηJ"t«91L˜)a» ”@ýÁÜm!¾·sý×`?ÿÄ…*^\ã©‘„¿,šÖͬ1 bJËÇõÊ سg‚£D>إˇñ*!²5êÀ†hêG’™T²•›íšDþtŠû:I§˜¤…SÉxr† ¨}Î Ñ©T°R:ÂÍ¡­DÆ™÷5^An“á¬YvüØ6¯ {XŸÓºdåPÚ]6Ž& –¦‘>Œi\
+ͼ×aðI®¨ô!HGl“"()zC"ÂÉ«ò©â@Ö§úK¶Rðšwë¼®Óqå¨B@+9A‚ÂÐU ZŽ"ÊÝNEÍP’&ú¤m•7:­Ršôìó/çcʘèˆÆø°-“X¸Ø(/“ä8’ˆž@wà ÊbÖì¥n¡-uÿm?@–º2‘Tõ7“}5FF
+l>É2Qœÿ•›’2™JÜ%Õ¿ÞAòÏg¬*ÀWSÞ©Ùqåh “‰‡üíÕˆs€BÊÒ;*ªæÇ|°^3BZšøé~›Ö@KÈk²µq4Ũª C"‹Ðø=<ö–„•ßÐ69œ¶HX½FÕ‘-—æ†!JrÔZÎzº UB$ã§Üχ½±}Ó­ ™ÝYÍ@ßïZKhød*¶.
+¢Q<¾wÆáIÂe¬Î9gp,B®ñ¨Ubã€LBÀ²BIã
+x‘âÚ-AIˆ’+/}þð‹NÊéò»E•3e9¸¡áÔ<fWl{ŶÂÎæ'sˆp7*Í°¦ô½ |²eˆ‚oÒŸDð9 Q+u¡×œë€c ìSÀ6Aµ—#¬ô²’-bÒ÷@A /q)N‰ƒEd8¢ßqwÏ<qî®<çÕÇXé µB"‹Ã–˜£ÂÆüƒAÆ¡ ¦UMŠž ‚‚Wœ,âXÑöÍ…gªÁ<2§ ÌòÄŠÃ bÍùo¼úÚE²çÚRZmüEJŠ¢LžgOëÐÙ(ÅsYðolú¹mv¥,kI¸ñÁX CNøŽEºå!m`›sJGQ§W%iØÇ*ÁˆK†áåÃ)Éå³ÝXš¶KzŠ)­6ŸK~íOAC|fªkwçÐõ(Õˇ{|Æ„Iv‰^…pÂ+æF ÔŸ¡k3ïaœ‹\”ùE¶HÉ1ÝíK‘Œô9æΰuûš×{Ãcd[º‚#ó
+Ø¿!q6M‰(ZQ‘ª¹)ŸoËMp1ã÷.‚ª™Z—ŒŠ°æp{æ—²®0+ ûª¨ÕšKù”H›sW+
+Ýšš(éCæ Lp}UŠV‚ø(âͺ´ŽŠ®˜ŠŽÍsÊ¿qÕ^÷—ºn3{Õ–™ã`,\@kû>¦4ú‡ÝÂÖšd-j/ »„%€] *u;¼3e‚ÉÜÖ¥ÈCªŽ@cqÅYïésä›–™^åa_nQ”Á$rN<&ÑŒÈ>!ÇlGWú€KzïV‚C•¥L§ä×Æ1…žU7üs&óo41·!ŒM²¨» É-3 Ží—š·A.Eî¶K;bLyß)ò()»Ji5&§èëÕ± «ÅcÔ§œ§ø{1?¶…¾¤°r¸ÂÙpKxb¹%»µ;½^#k,²ÙÓ’©5­ñ±T<3”Ìïæ]¢¿"Mõ˜{ˆô†ÔjÆN›„£„bqŠ¾v;(³C[3@[aÈ÷·` Œ3|øtì¯ï&àm¡ð8_÷W¡f(¶}¸´Aƒ‰EpdìÁ³¤Þ,=Õô<gÓ…oÎÁ
+ÄÔ@ìB“ózR~As úþ1¢*PfñëpKˆyŒClŽîQ:5/¡òË+’I("ïa¤ áóqê7jNÞÃ%ؽÀ•I±Öàÿto©5<Å ™ãüppUÙË =µ ¢¯Í¼Qk¡
+«4!«z¨µþ*f)Cg"uD(qXIlà€·c(:ù>Öú Š+°N*ÇZ¾®
+ýM0-{ÂuFùpVŽúÏ0¿y¬åËêžJl)ápî‚Á³
+XK€ AÂ…#ø÷9´'Œ!‡í¸O,$G8 E‹”ö$Ϣńá¶Ã®2œÐ‚iSêç¹¼Û^dÛ°ÑɨHßÑŸDB{RžvŸüˆfî¢6Jl,ñá´ß¸ï…òSzúñ¾ÿ`<G’ßîÎ0¥©´Œ]n­áÁb•|þe¼ÌÑãØa |ßA'ÐÇ}‰•úJ­û§ï/=õ í ØÆ°I P n®ÏøˆG?×N,›
+äVZó÷ó_S@ˆ^—êžP@ZË UMÑ«±¡Eq,cjGªË"—;l®2T< ö6C5fbÒ<.Ïé¢x7
+Iæa°@$*\iÏ$tX€*ôW[h[ Õ~ mE~04]øã—_Äê1ϱ­ëS‘F®Ì¯`‡Yj9L—ˆ\L›µ§‡ˆ—&òöîB+W”³P£„%U d¸—T—‰‹ëÇ·_tÖý³^Ón«pâ¸;gè"Bõ°…gsáxTš‹†è;?hÒ·dŸBáÑÕÙe4ö§àI GQȪve`„W`á˜û>§ðÂ%d{Ö+b ZZÁJF“¢ÏâÙª_nMiÀ“3¬¼Öý©Ç¯á5
+@ë9‡ =6*$…Dæ#óÐbâÞ }#pT½;‘"Ž%ŸbB‘³~ByÐl¶ËP_´~}3á€j0„
+{4è²<`QA>²•“³–“1ű§©“zÐ]˜‹%œÛB˜e{ež…ÑÂ:·ÜªVþťߺ1k&Âái‡¬ ¿nŠ¤`̈¯äEªF(ncíu‰EæìQh»„zÞBßš%ÇR“Z;˜­¦v=dÊSD¥3P_×è£Ò.h­/Ñ‹Ý1ª/‰£¤°œ_ªNÉúÞ*O:ݽ;‡Lk ¨ÉGì¸höµ9n UO<(HN6¿_j´±Ì.¸Å ¼>D§€jsrÖOvÒuñžE@eö4¤†€±{Ͼ–سFbSX›s”¸Ú1N´’ ʈݛ¥lF€¹gJ@%ŸYþÄ„ˆh
+”%}sµ”l‰‹Æ¹Ìø‚v„?]Îôóÿ~XÀ.ACBhvf„£nõåI
+8¯÷eÉì긦$"`MnáSÐÃÞw»:;0i,ô`~7Çæ¤=_äÀ¸Ù1~Ä[Ñ‚pº ö1§¸FOK×Tõ©"_\ÓÜRÔD¬‘íêóîT‹é[éˆ?Õc¾•à\VBáÒdDFbŽT`<0?ùÝ áܘ‚‰z'pn}ëïH:+§EN¯‰ËÈž5¥sGNQ‡ŠwI}q_oF ë:QI†jã”—!XTƸõØû'ó…z=–£{²"0}1Ð>mOñeIä;ìCä
+ÀYƒœÎ.AÇ
+q#g™z ´<1šbãäÒ\oö„üŸY¶=+Ö,ÉÕÓ›Ò4–íé0&œ.ÎV‚#Dš)6§ÄºLSe%NñÏaäa"ͤ–£É“
+)™ŒðÍèvÈ¿!J'Já{:(ÖXNÉ…¶qè5 îpþç+Ü.XeˆR‹ŒÐ3 ÀÜPPZJ?k¾X»5 †gdÏ1€Ÿ½Ò¢¶]B$$ã %1§dÝZ‹­ƒÃRýsXH¸‚Õ‚Î,þA¶:š5ÞÀ`!+²*$g+)d‚Êz½Ax-b¤\6â}Ü”ËJÆe OýÕ"—¼ù\©ä‡®ðú—¢Ÿ¿¨äŸ]ᓽQIÚØåa òC©ÆA‡†ß…R >àÞy-nåîx.ów˜ÅnƒñA9øóæcŒøEB¨oŸ¯
+ª¢Ûay¯râ€nµµ¦jÊÔ#JX‘1aÈ78?ÎfÇ*qG±°Ë'Œ˜îη´t°(Æ+ 9ã®d·×J(Ÿu
+k}´I,“åÁê†!E Ëƛʸ;‡`‡*A¾Û
+$'‚
+ýk¾![_êD'¶*ï«y9g,~—,çh—t¸Á»å]žìžH¤¦è tÉAÃKo®Ež?æ>M*JD)¯•ð©…Š)˜Í Š§
+~Ý ˆM»5 0 `¨¬y¬(”ò÷–¥D°›ü ¬93Ë8ç|š‡’ã¤IdõS
+žXŒ>šc1¾nŠOø·ÜiÎÛ*!nUp(„nÝJP‡qü.ËÒôÖD”KJDY–°ìnš tù|nV+et*‚ˆþ¶J$3ÚíÙÍSw9w ^2í’ªm'yŒ‡îÿ¿Ä>…biO‰}î9:þV6^Тïðƒ†DÄÐ7ó;I}Áä‹R÷ü¯E "?YEм£EOP0 ͸›Í& Y˜²ßEQѱÏOw€ÿÜB÷œŸƒE‰‚%»4ȨèG:8Q–òŒ—9nœG…OÀ;0V@ô¾„S¦>áHtèûû{]Õ#§y
+ðk˜>Q“C £»&  ¡c.»¼)­ÞÝ=eŠkMSI»©wâ@rŠÕ3
+ª¦Á×-),èðN€;Š>/ŠcÉ1L¯Í‰…Â’ˆÖÕ_‡ìi^Å¥±SQ®çBUL„]±ì›Ch?é¹Ç5ÎAàUJ®`ª
+K™A”Gµ­Hcˆi¿Ð„ÎðºJ¸bý­°#ŽÄ¨6«•¥å¾´l·rðo¥/â‡o¥`$/ÔÏ
+ÉlƒükηßåH«lVÒQ¦c¯`õ,T¹m ›¹¸q ðÅ°e »v‡Î™0nÊE‰`ÎJð¶T³»SÑ,ke.È
+:@)ÊØõPóy®Á œCsÄû-š¢¦’9²í|©P›e“àB
+g²?2Ð- 4Í#LMÆÌòÆÈ uÁ>1y³a NB‚+¾¬4l=àÀe~%¹tŽš8ZXöV]¢ŒŸÎ6¶8BVM)û:#cv!òÃn%™¹VzÙ¤.f,äw1±((á°ˆŒeS&(1•…á‚_°y2"ò¯ž!Ø
+k«æÆ%c©nv*æñe ýP²í/B[ÎvµŽÁƒ¯üâE†°ØáöËëŽà§!#Ñ»ÞòK$<q¢°‹8?’Š:ä¸Yδødx#P²ÁàjŠG!¸7 ÇqW3•{Û6)ø@Þ‚8>»R.±ÙS[ýÇx™$I‘ÄPôÜ`>ȧu÷’[°…ûoûýp)ŠÊôhÀ¬+K链þ0q³ð<‰¶øt&œ'›C~à¥óË@ÛòçU¹³”蔤±5ÑøÍÖ0R¯o%J„Â¥NK¸©â´+ºÒò sõ ®­Ìä%ð_—QΩ[ìÄÈ)ÒÊ>¹—ÐÌ£Õ‹_J臧0!Þ~4¡«»ßWª4}ÒÖôK-ñ!@Öí¶ˆ¯-r„mtónš"/>…«ŸMԟ̘|›,1êÏ:3Ë“9ÛÒø¬âµ.Ï–Qrx€¼SÆ<}?Q^ãÔÊÚEl30º±ç"~š"صA–/!æʾG
+kÙF9i¯£K`»ˆ©¿ç£Ý«&7HDI§GECÒM:|\G›v€”G³ÏFØ"v!4@+üÉ«3J'Él‘kr83¦¨·~O'K@'ᢢÁËŽÑ^à,(O¨éÊ<`8ðªXµˆLÁŸGÜ.«1ˆ Æ‘*ìJ¶¹
+ÈuÜTñ™r@E&^â-1/>oÄÅ_c§Œ*?T|ŸÂZº¨r¥ëPÜû·¨òTdâ8¢©Þéë.!>.¿¥Ÿ»aß™R$¼ïƬ'¦1M{W{Ÿ†<2ú®÷‚®€¤Jz*{þcÛ³­î¶è×à¢×]Â`]Ï•#>03¼0üª$ã[á¨õIŸ¡ê´Ø°j~¥ÄÙØF$çBÉ
+G‹+sðhjÀÒ
+Òm†d0Õ7!C©â £wǸÁ."ÀUŽ0h<4dÂPÔrœ¼—roBÝ]Óû:/ <acÏ)1¿K.¥µ‡MÄ ¨D#s~—ñM´Ê\Qq~]Jà|FŠŒñØ%Šød‰>tƒS·)Áû hÍF«GÌP‚YÇT-2e>CO5x#|_gØN
+wÇ3Á³Åb+FŽL{øÝFAŠ¤d{¨N´öä§Áð+Á·»dàúÚÀmÛušruWNIˆ‹­ Œ&ϲ3}`L–à–]Í2Ö|*¾nãÐ<pÀ má‡?—ÈËù‹/(–CGŸ±¶|»îutd ¯6óY_øW¬>STŒ:çHóP‡ ð‡WOë R vÜNKÀá­£ë£<ÜJl£Àˆ¼µeÇ×gÉ¢OåS;¾±¸Ü²˜±[½õJ4›³¨˜‘)Çž‹­™ &ªãÌÈQ ¬ÅdŽîî —,ÐOðá8"ù]\Þæá"%=“¬Ð
+NeßæS¸üu,¾Ñ+Ú
+? Â?Šˆ8z’êÖVf­BpŠ–ž¹†ÊÛNs*!E™)…£GJH\…†:À¨)fm’§J SJ”ª¤
+xù9JÄm¬BÇêzÜj$QÂlÅ:`”yH¡{L†ësŽ[5p+Çç"ÜfŸ|ƒ
+yyüÍð¯?nòû1èÿü‘ÿüŸ é ïÉ[¡»Õú÷Mü‰Ì}¹±‡Ža2û½­ 6(abíi<ä„àB‚¬Ï}ÌÌHá;–íaTÓ—l.Þ¶§‰D×™ÄÚªO+‹Ù iȽ¯sNmsŒêôGU*eƲ9$pecBA`¯Y¦¸Šg£gpHò™o
+¬-§Ís<‰,Nùæûx\ rì†A¬ó™L‘‘BLC^þúõ
++Oæ?o&21ý+g|œÓUgA²½ò)’Q‡çEŒ¢•‰¨£@¥,P¯9®Â7³¾Õ‡ðtÌ <¨sÊ—fQáhDÎýö6»qÂ%XÛÇæ1NÍcZa¸ìÖYÖv'JÁ’òWV<¦ 7C³ð´'ÓðÌ+2ˆD8—¢*K¡`¯«PöĈi0|ÔµR’;²§ )J`Άcy!£cÍ:'±¿ÖŽìÝ@š%Å!øäX{ØMÖcÁÉe^@¿@Ì
+UÄ,umH¥ q•&™Q$¯NiÆça
+j7Ôø¦{DuàM„Zò•
+¾+‘²<
+9ŠrAÓB`¼”%Έ‚Aî ½´jô|òHÒgkGêYᶦ¸ay
+ºd#ócdË2ª, 8ÇäŬÑîÜ%µhÔÕƒ¹AðÈυ
+ ÝÖO d7…ãðĈÊï’7ç
+íµƒTž©·«'}Þ@| Ö
+Ê’‹?D­€åÞ£åþ=–4k´Y&ï¸r& ¢i>CLèáºo–~mé;+m˜˜1‡à¼™#¹±KN‹«Ô,¹‰ææ Ÿ¡6\éç0iH=÷s¸E
+Œ‚Ì€ðm\Wº›I½bèìôëQÊ”øV’{<JKpÁR’s ŠÓc º¹ã4%$cï¬8•¡º°²»zbWÕ²"Ì+ÆÂäð² ·]@¿Ä(DELü2Z!÷¸¤`bÆ\ŠŒêÕú ‚l#N쾟‚è°u´†.ÇF°½z8}«Ï°°&ÿmQd¢ßO8!‚µDaµñ~Ê3ä¸ñº
+òCO;.Ä1KŒDÎà‚s ©$K˜™{t–ðh”ÍçOIÒ9쇽nj\T4ù>éxØÑc&·Ý¤o_Cøˆ ×È<ù°;d°÷”§.1 %¶ýl¢©&¡÷š‚i–ÅBˆ'ƽ‘—öAŽ^æwz¤¿¸X/_ÊçGÇ l\ýUÊ3¬jÁª5+WF.›lù$ï&œÐE_=å§ÁAÐz©’cD0¸gË»ûQ™,-úyŒ1SÄÑ”`ºVñÀŒHâuòeü*ÓÚki^ÙÍ‘-ÃØ9gw¼É÷ÅZ}•’WH¿L¸!χxºeÚôÊÙíO?Å6™*LÑÕ4²ÐØ¢„êa>ðT°".!¾§%£‚Ðt?ÇÈ/Y=JlÚJS&jތЭ|ÿå–þ>¦
+GÉÍ|5 l?Q5ûòýxªŒâNh"Ys¹ª_ú¹-»gÿ†Oœ‹âìˆó÷¹l%5ŒìÓbOØ›'¤ÇÛ~‚ùþú_?¨Ð©cGx›ö_*0îuüÄ|~Q7H£°[´¼öŸb.%ú¼ì‡ ÷®u »¼“ê1?Á|Öñ¿*äu, é?¤™ÊqYÌô˜½"-ìÃï+ˆÔ†ùÄ͉,µ#@°¨¸aä!»Ãg ]gu‡àh?È'™\b øÅŠ?E_Øy¸_òÕ‰[š@¯†%<uFS;¶YD†•õÐf„ÅU8©ñ#;Mú*pŽ'²*cÂÕ¼ìCÄ#÷-YâoˆÛ<ÞoæÅŒJöè§ê-ѳä̯ÊÅT…4Oâ n)²M„^Ì„D†T ì´ó#³€ÏeåZʷ̶oJ_’­„){ˆ~ ÈA¡œæÌ3Ã[§¾é‹.ÐYæ$…¥žòïÈÿ:¦±-ä­Ñolm]—woÈ(;\ œëZ= \ª²j¿ôÀ¯‚ÍèJ;!Šn"‰=k³¶‚§¸|r‰‰ /¡ülž#v^}$AQ¡fYzÝ”%Ç”¸F3m›öŽŸsärí<ê5å»2ØûÖbXtÖ‡Áð]Á»bј#Fí/¯¥‚b,'QÚpø)ÇŸ
+$/˜ÁEy’þÆÛ]ÎeÔ”ñ§çÉ“ ëN3ñkg:ApI:t(—OfMEp•ûâé S1T²ž¯ïÒX`NRešW[¬`@Ë«O±–ñÌŒ˜?½Æ´u´tZÌ(Û/f¬)>¹B“7)§oæJ)êÿt—Yr$! Doä
+ö'Ά”Ž{–(éòG“ Ï%žè]•5Ï’* $M‚¤³éð ©`h>55¤¹C'‘Ÿ­!åº|7$ó'#á«ÁSëœÄP† —Ÿ“Ø}AaäW k¸GMÞÝ+»5ä2³ÿÃHl¸ìÊ~þñô fÑ×p·ù&D LBœ¹¿C7Qh¬Ç±Im)Xðp·ÔPQefÊÌÛ|ˆvHë|Q3ÿQD¥¿ìD‹T3þÕe΂€V!–y¬­ª8ÂØQ1í0PózNj(ÃGÌæ.B5Ñ?ÆGÛöw*ÙwSæc¶˜FÚP$õ÷‘)Ë_žæýWÁ‰` 1^Gç ƒY¨]Þ—É«$°ž%Ó†œª·LOzt°­d¼qŽbïLõT;Ål99 :Ì!¸ƒ‚„]BÒAc‘Γ¾æDIv™¤´àÕåÅ
+ª
+ÈNÎ!„åðä%¯<GÑØŠüΗ ÉG2ép“kyŠ{áÂxùdr„>Š™Ï%•§uøÿqÍ8öƒK¡¤ð(ó,Á
+ït0k4gv˜ýËæÑ|¢ÇÑŽ&Ÿˆ·A§î6Öíûõ
+|[}>Wï®Ïæ‹y0—fþRŠ†‚–&ÖÝÕp`
+E
+ƒGàä”âØ–zÛ"åÆà âÆZ–ðäŽ=[§„g2V`Ðûã¡útPI$VàRÚ€FµÇ‹aªhLùÆa²Šì›ç –žL7Š…‡Í¡t9ï‡Ñe‰[“O竦(€&Ëòà5d€.åâæn`Å5:ïO ‡ýÅ£Ìç(ÖÐB ÑÓ¸ÀO‚àJk¡C«ÆOãøfEZ [‘~WžµqüW:$‘ÿo±_õO€
+H‰t—]®¹„W=x×DñGÏ™ 0OÉþ_ïÇÕÁÉi#Lb—E‰,‹Íú{ÕG·á3^ÿ›2ßC–÷Ö\W÷Wo6Þmhó©>»nˆL髯hÞ7¢7]n=|#¦…4ë1¼éëßÒ{´µºm.3ÆXi Q[£IâM‡Mýdéð0 1õ×Ïÿý诨þF ™Ú]Bû+Ö|ý~É{YÓFLóùÚÖÄ¢ StU+ŸÖ—Oß®Üu ™}%
+Ôd)G Û7Ò-ti'B JÊ1²!‘LàhmV®Ú—q”Þ¡¨Ppçð“¿æAz<Z¾ü‚XðrÒÙZ…â×…cS,!<KHÄp{€T(åCz²÷ý”ÙáFÓ6ýÜ…7"÷º!ä¶Ã”7ëMÂ9R“„hÌ9†wé£00Âà뮂ñIˆkÒæTa6›äÊÚ*Ò©Íu]!ì’e:—@6¡nkÍÃΉ¹5ßÇ´1iÆѸõFè$™.pØ®HJ±}ÒU™…M½XBZÈË,êÑ Õ\è“
+ÞÓi"šRë˜ljÖzUi-‡ªsÙ{„̤UC²åõ7H3WCÚÉMã;žÔ¢•%ð̆?¦Oá=Oš>Ìïì S*Ú6…õ=zò×›„G•{ çHClÕV“ìŽ6©÷¤BYŠVuíŸÎqHoš™¨PÙµ*ðËg½›P a¶ó#¾¸[—dFû’åèI¸ózjnEWL$ÖWy ²:ÉÖ¸ŸôMjÒe Uó¬úëI°¨“¯‰Ô+Rÿ,|(¶ 1‹ø¨ß õ—€B‰@ñò“P}=kõ„#Hé˜öòEÎSùÛV~”‚O"³{•Âßð~¡
+ô1¥ø½1âÒÏi±@›„‘bèp|$›""E°Fùh¶é{qËÅÀâNÆ鈀¶"
+é…Qæ«tê8æî,§r†zè†Pß6v»#”iÙRßÈýFÐòšÃîÞ#WNTÓ2HŽ!¥׳™(1/tÙ1Ì¡Òý$.J‡¤÷=¨á-òÓ¡Ž T'T†UIž® 2èG´‘“sŸs‰d*rëÂœ3º"GÆp9FPÛ™AÁ‡P…PÒbÈ”<"„¾ ›1{ý*-”šÔIÄ]9tÖQÅh\ÿÕÃÕ°:1%G—˜ ÔBȨ7M„–cgã4(—ãP4ç<œ„zš¤AÍ!Ús!ŒtBAè† û¼U‡±Èv÷ñ
+Öðv«Z9÷§Lö3B‚±NßrN<\øÈÎ4¦ Î*šuh"Õ¢4$:ÓGuqfÇ›eÑ×æ(¤Ž‘@Õíäx‘f-¿…G¦±¦G…¢¸Ž]âYÂãŽáa¼k…b*ÂZ*îc>
+]׵嗷Æå G¤éóɸc¤:Œº1%Šä£žä4ÒKé_ìç…¬ó‘ZÜ‘þùJ§‹0,™ÎaTá°]k=ßFßü[’%ôÊø
+¥ÅS‹íÇ/. › !B¿"2 ëW1E:²4­¸Ùé()0GŽøpdõK$™— _ãÏúúõh yWº>`X10d¤‡ªin½Ôg
+µµzu
+bg„á{ ‚÷œã8v,…„ÉEßÉäŒgV ¦‰OçÌlexOÎ
+Âʘk }°ÐYè.†Ë,”’´ Äÿ°
+ÐÎ}r ¢e±v×$]#=É&óœ„`Þ-{xT­ÒP,Š\Y~
+3¦œØ½µ©Ñkl]7Hh‰œÈÙîGNÙYÒB­:G(%OfÙ9òŸöu툟?þûÑ_ÿd<è‚RüжŒ
+ø6·ã=ÒÑñà¶7§„,”¥/|ªî$ œ®C±°ÀJ<÷Úç>‡1ŹХW¨¤ í—æ¤B¥æ¥M±sJº<ãOg Ü¡ú{9£éªW12?¸¦m’ÐK(BZç°È4Éñ7„´ü£¨,îÉrž~BE®J_>o±Ä"HÛæÈ¢§åÜãx^tEÇ'ÈwGù ”–"ሠ¯Ô9š£„ÞÐjöž[nŒÉ^ï&Ut¬*L&Ù]0z‡Î`hˆÁYr&5–’¤—õ§ X öJZ‘„{pZp+3¤àoÌ­”ÏH‘‚Ú-X’šõéœy-ˆ»q žUBÒo=Q=mfƒ¼
+…UaŒ¤ÊV¨¢¤KÌêÿ”—In\IDO ;èDÌú¶ºE½bÝÛÏÂÝS•ÌÈ*Ñß³çÀ\ ¯F¥PB¬§µŠ1Ɔršƒ§ ìciCˆB£TyÅšA?ãá,Ž7rÙbb˜þ]ÈçÏ»XŒF*%”&<—ˆ.$Aë1Éß7ý«æüBž’Éý£Äó1 …©s*ÚÁ°q¤Ää)( >®=Һ꒛QBx‚,°’m é¬LÁʱ‚Ð&Š=£™†a¡R›íAZ¡FÕÙ1˜]ùD¯"R•ëGOˆ<’¼ô8ƒ×
+¦,_BѶ¦¤3³_&æóê ¼'ñ~Qhre¨h\Ddg2•‚]úÔCøèÉS¢wE%›ÀY¼| ÍuNlIÛ¯:½Ã«q_fùÚÏÖçjÜø¡4g à¯è|„ÄEx+ÈÏ?eM@ p+;†"ŒšY5¯³á­q¥qzC„
+»4åQÎç¶ ¨ËoÉ݃J…Pÿ2ß^¢’ªðÚA“(}‹ÖÝBî8ôzŽ”LtÞ$þQTdWɼ™¢úçL¾È@Êë¥_î¿õcj¢0[ZÓ ÕµW‘l·s–À–îb.½~yþ¿~Ü‚Úq´©k×û¼·+CŽ¹çû{Ù0~”½ž—!„v]­Gñé$‚üu tKèm ÅÏAD“žìÿë Ù˜•ê;¿[°eŠÕWó‘!†úsͽ›C-Å‘"¹ï*„¬Ö¡vÑ׫†½t~ƒÞ÷Ö4JF·¶7â— ÆÑ®&ÐÉL’G°ä¢M„ldŘ³| ñ›´WYØÈxwh:ÕÈËTIÙ-n:»?¦V:Pl¹Ë…Gÿúq‚£FÑkgü´ 2eýåì8¤aNbàx(Û<ÍCÐÚY×YbýÓY­èš\Zà<@³¨9?>]³ÆHÒ±Tº,fW$™w4¤´Íx ¯à%“^ò(˜-dðÞÚK>›ìþ¨&Ï‘`ÌhQÑ4Ì1+Ž ©-RÃEIúç¨Dí¡¹„Ä0<wúë:D² ÅY±xe1XÆTBqÎþÚ7 ຄøMZi@ l¿;G,ÝUˆ5#¿<Ÿ²µ’ü³©|‘-i\–*¿ŠÅ %€Ï‡'gÊèmVÚmçt‚N.oV¦z
+…®£½1š80îÔ°x¹¸!5ФQÖ%(ÐÉÇ!bbèóÁÚ€8ÖòC,›ôZÁ¯ø"ÊñHaž¬¯ïž!%à eìþHùrZKfîÓvÜÜg·©Ù„ð#¬íç 6oIÆx ;ÚïŒ>f —œ(-7½–E#ùüÖëPIâRˆYf¤ßLöÊP1gºvÎ ÝR$G‰bšüÁS’fh[È_?þËem~t’º´8å€É"ýß
+¢hæ¦æ} úu "«
+-Ô,Û‚¸..Îðó[b(4Þª˜¬_"«¬§|€Å,é'¶_›Æ·‚n·myx–ŒÕÿ#è–—ë¾Ôă|,°Äég‚®Aøª°‹™Î-DâÁæEç·{H?-€M©…¬Û„€3Y;<'mÙ5]SC‹g ZÊÊ°­¶çòqA ¿ˆ‡P¢²œöB—i§
+íËÎáž%¸-Ñ7=A1‹®Þï‚FÈ&4ö›…FÆH˜Ä½B¿¯¬n›dø9ÒZñ'7¿,„
+ªY@¥üÉ ábKÝÃÈx‘Î6E’¹Ìu\bí7\ˇ3øqÓ¿ÂÅ/cŠÁ`¸üG÷ØY¨<K§„Æ /Ap:»­»·*g š­Ã¾óå²Ïï¼È^¥Û'•×“þ¾„\”?°N´÷~‰¹<ú¯ñ'«=Ù
+pòvØr’Ž6Y2 =ùkHþÈ‹"³+Œe8ÄoÑ Mȯ~?$@´9YŽp‚†V F¯óÛAã!‹ôö¶ïÄ<üçŸócI,š Kþ<º÷5[ ,YòI#AøŸœu7Çᱞrb#ùy"&>Ôùÿ<œ<‘SŽ®HzYÂþSµ›ì⧠}»JÌð¥3fEÒ»€H&<ßö„@‚AA[ƒ»%,(4¯‚ŠŒÍÖ% } ñ«p6KtbÜÁ ,„•ºŸ‚QdIxµ’‘ær4{N é‚€É/Q}» ËN %ݧ{-·™2õÒ‡Óĺ,—ÜîÉá$=iuôH,¡¾s{j”¨±D/5nÂä©4LʨT¡Ú9·¡ž)¬<Ñ÷ÕŠdÎÞô‡„xÈÔB¢'
+0aH\5ÑsÁ gA ìP9}°íˆ?(‹‡`U7!\õMÈEa2²§Cq¯A_Lû——“ŽÐǃœþTTêžòšgnnA«0œÆ-Ý{þ+bÀÅŸ\å©Þ]ÓB
+7˪ïÎ`¤zèJ7GPq¦Ñ\™ÔËšþHqFX×î]ófWjy¯Ó_\Ôþü€'N;† [¤°úMÀ&¨Ó³ßÄK7 oøMèLs·Ãéÿc¼L’à¸(zÝ'``J kyé[8Â+éþ[¿d¥nTX
+mØü,
+ÿz”c:•Óü;L2œgA€î±
+=ÙˆWÁ,K?v|'’×™§ñÌíècëoˆ¿’Ç ä¯väþ¨[bÐ{‘åŽÂùsÝÙUBVöÏ+ˆ˜¡|Ö!oèçÇnÙI ÝC¾¡·©tPû´(W!Àx”Ü3\Æ:-òÛÔ4¡²ÙGb¤yÿ° 0 0?jY$¹ɸ@òË@¼ÄÄ(’'Wÿ±8IÌ ›ùGö¢Z(4*ø£¸ï#IóȶÎØvýHElc÷^4…=¬ü€ÐÝÐÒ‡ÑÙ‘)ŽÛ
+‘¶‰Ù9¤—ÀxÁžÕoÃþÐs~(ùÙGÁO“§¢„3½|¥GlÊÔHæ?6´®ScØÓ)_‡ËðWQáN¿|q’0IF¹¼|C2Ϙ·ÚÚ½~, ÷0œC‰pú·.ì£Økâ@¡£#l U‡þ”Ê~ìÀ ‘i1@=¦bV©ÛÄ=§å~aãþüiB*ÏQXU‚-b|Ìíó¤_êù"‚aÇîÄwp * ”|œªˆ“G*ß¼Ò²óð>L|_QGgÜ`ãIm²áõÿpuÈäýþ³.ëYEÿ§„Ð>+‹Hù>d” ÞƒåZ:Ö^½8…ùPXªýTTÂf êýY2^è 6Œ H-òYFËkLcÀKÒ¤Ó½å:[ÕmUñÚ·ÉJ˸!#ƒʶd¹YxW;F|°EĸriU샤)a ¦Ź n¤çÝZ8ÎVþÔ"ƒB´ãS¿Vïâ[^ÜeÚ·×ï`>”‚”¡î·i»WÚÍþÜæëU –LÆM­—{uà0Â[¾×¸ÁÙxÔªaêo½´>ÙÐœï=‡·Ç[à45Õ
+c~rÚ¹ÚŒáªÌ 8F®ÙR+1vP„ßšÂnák,8" ЯJt¤ˆPÑÃ|aX=¥²&ŸŸØµ­ÚÎt„ˆXÁÙ 4ªx}wÛæÖilpy©ß.9ÈÄ ëÚ „Ž~—¿Ûèú% s¾lIÇRzŽjòâb@þ Â‹XJ.(~ 1A@°ÀÛK`SáC¶ØŸÍ[pã4æqßí'á‡iËÝ)‡ëMl×ìð‰™/ÕN§ðíðï ócg&Ã÷WYPOËЂ® UŬAð¡‰q“]C®ªrsv3\!I©ZóÚð7ÆŸ³®ضõ ð Öã¡
+âæ_+r@èk Gr¦ Za79jÆ«ŠÙì-ÔUÉp¡§#,ÿæZþÓü¼Ã…(±ºgGBh7nªÐ°ÌYv¥Ôåz¦
+ãÀ“*Û¹žM}×+™·Ê²˜šålV¯û;RÊòöv~a¦HÈØóv¾°¶Û«¬è'ZØ$IðäÀ±xK@þ)N–”¿&ë1#
+ÖÅíF’Óx2
+ƒ†“ƒl}ÖÕ%qÛôhú•u“¤•xUõ£ G©"¶Ë×A‰«³ÝL©ͳÿEÖ|xÔ\§óôz<" éþäµãù’Ž³4~Å”¢ŠìˆßGÄ•Ô^ºCP~Â+u‡`‰ŠXᤓIGR/¡ò ˆ’’p¨qŸ²›¹Õ°&âê
+CD~Í„@èc™=cÊ0 àJJÔ¢1æ¢(ÇÊiÊRÑ™Ê"­t5ãO¤F³7Ba!x'|6ìùN…‰‡l¾»,d—KÁK´kLNŠ}øX½ÈãFMŒÇÅûý;ßÚ°×Ò™q®ºýU•2¾ÕUÄÝXf
+ºO`V«cFÐ¥ªuÉ qÅ[UN=¼ èÇTV{ÌÀƒÂñȘY ¬° 'w¯ ‰N@Ê\„ô ½%zõh¯æ’Î_
+M]H‡3̉*A{ÙDÎiý#ZHÌ8v •b\£xSá_¾ã#\è_± e¡4”Ã|BáÂÍ/ü¦/ùÜ_ᛸg–¡?;•ˆŒkz1HRUS† ÒHøʯ,§9p`]8“>äö°w3„žQƒÙåÁŸIWÔ j±¿¸* ‹aFµÐ”hµ®"ÉáÂJÙS Tivº\Zå8Vöác¨jÇ1Ðî_¤¢Úy÷ˆxw&=™úG‹ˆÎ0å@¸?%¤ãuºQBøñJ?󥧳x·\"^a£RxØ”ÂÿIn°ŒW¾v“é¸iñ¼ÅAD®Á¸³Áþ&¬ ,‰Zéa•ÙV–¡¹©R?Ô”o¿APLvc‰
+B(´¬d`—|±!˜#¢ Ô?ÖKÚHM0MÅ+$·g ßÕv×Av¨ O™žÏHg"ƒ$¿!Ä%œ±¡™M Ã>ã(ñ:TÓ}C•ùa)“åó‡ÍÄkÆ4)€2ÁRt‡~"¢+ÜƆW¶kÅIÄ!J#æó\ºÇÇɇüÆS–·næù¶*J@2¹n ‚KDšÂÞ/=a åaÜøLkëú Yá.åz!Öœ›õVyy "Çw2¯Å‚/#›!EOP%ȶZë@ )ÅG¥8o&w‡|çMEñõÄz`VLv¨žÏ¨áC[){ª™0Å?‡ h,”‹ÅrŠ¬
+ ¬¸Ï)rÔoÔÓð,;!~[[žåF“䈳óP¼y0Jåâe†V
+«q ãO¨ ¶.á5Ò9SA7'DAóó©}uê˜tзwPŒ
+³rý6‘…†©íiYÖ ¨‰ÄˆÖ®”€ö„Ž’½‹N®cbhÚYfI Å‚™ýpi“6;$0‚W‹6d…‹kÚ“\0°”½ /9¥é­JqŒ’6†'{vÚK-_õä}Pcã]vãJ 4â£0úVnÇq'Ùî6šÔE²{’pW&­—HDÌOdgú9Ø?‚6u^V¾iZ2l¯*Ø8ä(ÑÜê& 7ˆKoÆká—çÓ9¬’M”ù•!á,ä±!Sr#a*Ff8[vFíòQØ‚Y÷R!‚eòmîRut ~`ÞÚ´+K¹¹ Ê "—#ò¡zËBa»ÅFKn]:¹=¥H}Ÿƒã˜š×TM³xq“a X¶ !Ý"â)3±Y7Š•‚žŠ‡’1h(zI¦9¹K2mŸC÷%: n¶sPfVºÈ”·|„œý
+#ƒVŽ¦¹«W©nÉ‘Ã…/æP<¯)–3eRY ƒr,ÕmC²P·’3Ñ8W¼ákü²Æ!SáÍæq3Vxód Ƚˆ•Z2·=$µc:ª·:¶¹g:“GɻL7;Ð*÷ YÒyOm‹$ù ­û´îÊëÌÀüÊd<ƒíbœÇ–*
+v
+ôIIKÃY Æ©mÙ¼‡N•)$Uãz5²9Öuôt4ÕjÉd#QÁZ1üö Ü"3A”Z¯MñE¡n+Ê÷Q1o ßO B\J  GͬÚ\yŠˆ }€iÞ„»d»¼‰bQ·iÉ!3ìYùá]XByyãRð7H¥ |a úcî ³€°ý¤‡cPÜ-Áʸ!}ƒŒDÓüù‰/Ú[™OçL,*ÿŠÅä¿£p¶%p2+Ù*´ÅOa`KØ"‹Åjúƒ* ²lÇ´Ýí‰Á|a;»"UïL!Ý]¥ªVLsO ÞˆÓƒ|LäaµÉ˜Š«óC§×J‹‘žòp ³Ï8“Á´÷äÅ„ÿúÄ©ÿÄÔÓ&mZDÕ]5_V§6&ÖºõèçôoGC*¤B£œ/a‚P‘•ë¸Œ?{ZÃTui®µÂÈæ
+:…›PsÐö…–”A§¼…ûT¶øÍ1ø¶™êM´ uµÛ ‚L@5ÎPκt‹ÄBi3•@8ástÝG…‘|v’üœ*O‹>dÓýÈr{„Ã4C‡Á‘éØåwR˜Öᡸ=_4î6ùh ‚F¦ZßÇ0—ØIq Ñ&–C?ÀM‘Y?xz‡tuó™éÁ,°!Œmâ
+°yt3-ävö¢µ³Ö=LAŒ¦ ‰À6„Sá4f¬Øe">¬Ã-ÚeQ1v‡X< ]&ö0
+uJfÙhüÆÐo“e™Áí<œ8d× º/¤²
+P"`ßÜ $²»J °‚„ˆÎ€Ë'ß|mêô’ôòÁ#p¶ÈZˆØëÂcâÒé,™<à$æ²C‘R…¢ø¥=œC« ždXFñ}‰„„3-²kÁûC‘Öá¡´ÆùªZÝésBü¶Qˆ‰¤fÊÞÌìK%ÖK†‚‚ÕŒôÁ+&ÆOÝ$‘ ÝŽØj©›Õ¦qY}§Oú¾èŽß|¸rI·IåΚ‚-DÇŒ$2S’E
+Ô“¼pïj9†q8d¢¯s\¹Ò©ŸÙ0LkCfÈßRÔZA®ÎÎê Dn3®q¶ä0…I[i±a@6(BW‚ðZÇü!OE~“²tO_¦ÔfÏüËʱz‚ô YɘëÁîžÚï±NÇ0iem£õ‡s´ÿ¨X^s-šÜªõ…Ìï¡¢øjà4¢ïH‹E:’õÆç7ÔŒ¿lj8AIr tÏ×iÂÑwùå!ÝÞñ)Hß vcÀÃèÆ7ÈûÄžP+•ýëÄZ7}?±_ùAsRH¤ŒÚ]ì÷Ê¢ô®š¾¡Rµ~ »à+à¥siNÿ†þô¿ŠªëG“­zá)™$­z¸  ý¯æ@5è±ÙöZqìKPLþ—ñ:I’9¢
+ÍÄ;µ~X§ =Î…°O¹IÈû¹ ¹w#$wùpàI;žæöåÞéK‘ªÉ4FwJ†‡lÛ|œ3¶>ûF‰•KYÎùÄ÷9\ë}¾x'Á3ÈØp ƒÓˆ9¨f
+]CöÛ¸·çæú4ó=òNms·‹äv¾.ÅZx½}Ü‹ü^;Hã8r
+Šz/Woh~÷í±D”b‚ë¨×Üê­(a‹ †ä½|Xç{}(yÏ£E˜–«¨ñsëXÇ°êh9Ĉ9B'8žÙT!Bt—ªõµÕ[‘ïè|öa/¦ü¹ä‹4þüp«/E¿m´{áIGS—qõx&¤s-û(ñO‹Ö%ž÷ÑÜm¬vÿ­§UúìáLœb–†Ê´x©yå& l–DDgq,ŒÈ-ç0pþÄu½^í¿+úNß%äm
+¶ô<h†xòõç¶,CAÐ$ã--Í
+WÞ‹Oê .êÀ2o1hë9ŠˆhØØ×®e(Cå5èQ¹ÄŒìñ¤0q4Q27S2›*´ÙLîˆÕ‹uê{Zã]×Ñ'¤ÆžŸ÷Ýíº’5,cU†•ÌWX ÷;­”n ìõÝA ¨^møM]Üö!U¼…^Xªùáç’7kû^Ä"Gº¼qXðÇÓiâÆmoº=^Šéçx™ÛlI0òÚ~½bž¡lžg¿PB㞘e»¼2'Ùùì}lQG«u®d*Š²¼ÞðÐ0¨-åîE'>=y*¨¯ã+br$í½M³§‰?¥·)^5{q…½R¹™¸}NÄé5òŽÅhEžuÏ¢
+¼?6)  o½èòZÞ/ó:¤—•„ì3µ{½;ÿ8õ¯\ŸZ—À<œ/Âÿª
+ߊ~*Z;yÅõQ]œÇ—ú·…T?”
+5~Þ8GfÂqÒô‹·JÚgĻݛ®lˆïå±[
+ExïÃí‰`áD¥•lP¯;J–*8wx7E 1›¨cx¡ªñƒ¡ïÆ®øCÍq#›|ó ªD\Rb݈yà4Wùî¨V «Þm½iè(""ìGA—Í€¬ŒnóM% LT4=[ žlĬA¥M1SÂí’Ÿ=½å‹vh
+A@¸Ôuz°êö
+Çk«íŒ²ñ·x«ݹDxh¹ú‰q×°0(ÓËÝAiÆ8žáYKÈìi]Õ< £–ÓÇ3þ:ÿÿ«Èv9¯ c®&]€%qÿõºY {J[“äð¥g•:_ˆ$%V˜Ä³³O;ô¾•f©Þl§Žð¤½t¯û9EÆK ì­¯Ct –Ñuþ"]ײ»FRØyF'¸Çy+
+àgCŽ×¸÷ÕøåÚ©/Ë)
+i$ؘü£oÕ˜v'8±—ðò‹fR¢Î~ >¬‹ÓM»Cwö=e•±Ç‘FdÆÍáê
+ÔуP ïjFMj€ÝcêÚ½+[sü‡ñ2G’ãX‚è æ#SË}ióE¨ÿ4RDÞŸÏ3"Ljª,¢ÐL{ç¾ô¼¹„LbÎïkt¤Ê©ñ{ïÖ7Yî£ÖaY ^ ÇnG›áY¥ÔʤÓlîL!obÞsúyx)ø»ó°îpÑf—7æ© ÂÔQNn=É^™ˆh ,yfÆLŽ¼)æE·Wcñæß{ÃîíÈ+
+)èÚ†°ã[!ÉrÒ`D/("ª.À¤Ç¯ ŠJ—"{@w1p-tÙ¯—r´=ŽÞ6­l„HCêõÉÛf©
+ N ™.W=Â:ÂC·  ÕW©®ìÚD!%ËŸäq€Ø€.¢†s[Z‡„¹Øˉ=+Ÿ2<ˆCÄ00ÇÙ[Õ bP⽿81Þ‡)åÝ4‘õÉMÓÒ4µ
+ã.]®À’`=šìýTO ³/¬ÕXÒÈFÍ÷p…à ‰G‚µr—†o9÷Ö4¨eªR€ááRà‹ÜÍ…›F[s€1¼“i‰µèÑ6#̨ºÓ:Y­¯+.¹–3¬x‘Á[d¾q’“+uw(|‰ö§%¥¶ø¬ÖÐ@d+¾ œÒ,žÞ3nÉá_Uë ‚ÿcf©Aài>ßþzûíÿoáý÷?ß~ûû-¾ÿï4 KË* ¬´t
+¬5*ƒ½ü!L*ró† sPC…—a‡ò®YìcQ+/Œ—œ!_XfÕwaˆ¬t ’£a ¡!k6P[¦P¨0ƒú@¡'Åd+ÅÒØ "£HÑÊ RIcQú)ø´›c±°\¡ÏäÛRÈK\{ÓZß’áq.AgˆiP—by_•àtäRìN8†Jɦ£A:S%Ï5Ó~ÅrA¶8¶@èv” cþý‰"
+ï½ Ë:2kæ™…úUÁÕ&á«7†&tÓ©êêlJÄ÷·ÝjŒÂr¯7ŒƒöŸò6!‚‰_jï1 ‚>(÷†5G 0Ž°4SRkY·IG–8Lß1DeK=4Å L]W›JS סÔÁõ2“6¬3î !iÆÔ·Ê]û*ü tiÃuäHc5
+QUu1\¥æÂoŽæOÉ÷î ¼h’Qú=—vnÈ5/(Ÿ’kññC†JpÈàƒ´ÆäTø d÷—"|ráz^G& CõÓOƒ*“Á”$£AÔ”¸S„$Ù­¨ä
+G#k¶JŒ¥ã?ÁAµ†”é„´Š‚ÆìR–<}
+>¢Weµl1Ƨ•’ÞÐAŒ,®‰÷GÈVAá´E˜ÃÇ&÷5ž]ÙÑ Œ”²Fìi Þte„+n<­ƒ+¦S±‚
+’ w ÿw*¤E¢LÇÊB:/6ˆÂx•±·Bsi¹Ï¶']Ñ;ædU‡’1¹2–ŽØ]9Íû¹ç–8&BN¯»-¸
+nY3º¯¤%ÕÈq%[ÞAøåïÇìÂÖU¬ÑýRUŽºlúA¤¢\è‚QÓ0¤Åz>5 æAãEÅ·±ã°)Ö)¬ø¡éÓ-£ÈÐ#GÉÆ‹F£Û Ç¥ª“'$>Ì.ˆ(¢:ëçãw_g(Z!ná´/¶’P³CP:¤…Hâ<©€!ÃpÞçÛTBYv$®áDc3ÿòVñU¶+4Ë„1ñBÆÝ7Pÿ€JKÀf|…›Ì +Zº“÷JÀžÝz–Í훺ï%FÌ4›‡žšÇJÓ·³-º× `[Ú
+*•>Dõ¶ ývœÀŸ1ß_™Òo6Ì…#ÀltT5¨ð8>ÈìæH‹#š v¨*­@÷h»Þ^QYÌÇúOù¥oÇí^!M]{as’™ÞFü+ i´\ônU@#V}¢ñ—$O&‘(fôŒ¿§rL ’2¼Í/ë5@c6¸µ~Q=©_€äå¸{@ñ³D%{ù ¢t+‹sBƒà²±Š/Èö†ü@óÁž–ɲFÜÂRìU1…Ð¥¸±³âø<UëÚ°ß „Dä¨gþ LÀÀ5Ю…#Í04ürR½ ŸW‹šîâŽ,±¸q\¥À)±=ïˆQb<š˜ß ”¨cб_ixÄ¢Œ:7q(×6|+·ÛèCÁ 夽oˆ°¼fŸ_ƶÂW‘­j=A.úùvÂœZ=ã atT»—×A§ñ»m÷èB ÇѺ‚½¬x0¨XS
+±ÇúãTpÿRôä‚À夳f2?e*IfJ´-‚º²ne› l<•S¢°.C2¥²ˆÏ{¢ªZ6­º@4)¨l?ZÈ “6d˜Òkܬh¤iÜaœÇu€ ¦–vï×ø¨$1Is
+ðØ×¹yBâ,cáÚåÑél’–—‚Že[:ZùSÕOnŸkZnLÒÙìÓʲÑõ¼“ÇìKÃçjd²f¨!R\¶5RnB(½iû( a,ûid@‘©6X¢;wcôÆŒâ@1& `1Ú$$[„*¡¥Q*–b³ù½.Eÿþ
+< ½¯ƒ‚5üJŽ!M[)#ê@(½øûò…ÈØsšþ~'¸x©òIîþk*Ís‡¾Üú[Œ–û†95bÒ-Ob~ìÃm¯W0w·}…ÜͶ¹ò0-ÞͶ ƒÂð§Ïþ`¶eºåïãìµA%ÁÞSìg“ÉG!’›Úâ;-r,‘B§§uèFX Ø'¾˜íkN^û€¹YmF‘—÷(G£­;Aî,žf9Øh{:Qµ+»·ÁìèD.²ìá賌‰Æα?Ûl<=y“"…ù`³AaŒa5]6ˆŠ^Ô‚Û§½¸ìäà²ï˜S‡3…l „Ô_fî¶ÝK ƒËþS8Ÿdâæ¿$rÔeÙ|¾÷Lj
+Š:Q™ºŠ$+ò}È«"*8çX•O
+̆©»MïL^aÐñtV×ô1ØŸ¤Ë’Ù@H¯R/n­T[‡ùŽ™á«Óå,!¯|^g‰ŒÌsÊOt› =HLvší@:9o´:(b_f$¹ƒÚ¹h=7ƒÈï‹A[ƒ`pÑt~›ú •ÉGÌ5bvfέXÝaë°s‹‹á|*Ú òØêèD&EþL? ¦ˆ ™‡N¹·J0bÐøÔí%êÞ
+'këàmÎöîì"lȸê8!°¬™Ÿ(zÈØí.˜†žÓ XÖ毇Ë%p…¶-6¶ªÄü "v&Ð6+Bÿ‡ñ2InÈ¡è rŸ Õó°v–¾…·òý·y¿ÐÙŒ’”«(
+êýñ Céùѽ…Bã·$æîÉäE¡•ÉböÜúb²ÿ°Tðê îz¤+ój!|\‡ï:0¸¯ÎgÆ?°ù|œ燿ÑcY Ž©BoÀ¸• ÂÈÇ"ØV
+N&p!®O…ÌZÉ^ðuÒÕyB=džT…Ù`È~ä0ÏXG<À?P›œ‹Å7œhzÉyN™{xßJ@[b³e¾BÔ&×Æåy›ã
+ƒN’Ÿ•õÕš1ULoc‰Ú.Ñ,0Ññ~µöq]Õäô0â£=¡®RZv€j|hW¿5pF7 Ìí¡0ü^2v“7&M YØ=}„·8%ϱá¹DÖ>gœ8ovÇ‘ù&hÕKчuüvûºS" &HëƒW7Ìž¼Å8uæj¯¢´kƒ‡'Ä¡â~uü³íµ&RNe
+eò7ôÁC3‡ˆ3GwæyT:fóå9?•øVpPJ{·ÊBÇô˜4#"
+Ô$Ľ8ï‹L=ë»ÜÅ„cEØiÅ0@ðÌ ‚1|~?ÛLµ/‰Q›°}-y¶«ßåj¿ýóW¦Çð"W§9”æýÿR<#_SûñPI'j`Q!Ãðö½•©Ñ‚G¬VdxV§YŸ)?wx[5 ³î,s³Cbá¶-RYÓ–Á3&¹ è;¬o *s“±=%2ÐûSÜbŒd%±pgÝ|â.‹H*—¢oŠpSÜSo?¼ˆKáuaëÆŠ#Õ0¨V1 S Yî¹<¬Uô…ì.FºxÅŸ¶“ò)eÄ;4Ø­ÈÁï¶m™¢˜Ä²W9ÿúÉ…Žè ÏtRàTp>fæè7Æ´êøâíFxèÒRH8´_Ñ´ì¿aŒ*QêÍ.?Oå¥Z½‘Â)aÚÈ­낃ÅHÓØ%×(ƒ VÚåØx)b²ú'`øñ¦èˆó$@±Ýë(Ó$%>…»:¨!„¥²ÃÇa92ÌC-1O€ÆOOõ{ÙD@ˆ#ñ6\Ϧüòî¯æþÀ™ mëo×á3J›Ù)Ì}‡BYxÈëÑ((‰aàÈ2«çN2cxr~! †Lš*D}¿ÚàSÐJŒ)–ÃÇŠ@‹«bt–ïƒé$?ÁÓÑP¦ÝÊ=y˜n½ш!Á‡†XÜý¥Ãÿ³(+bu<>Û`q)RP¢;èò
+C&8l9B$W-ÿ,×X
+x€“ó õ ² %ûSÁ³Áž¸×®2ö>3½ÛŠp!¸ç}È37&T'JXtµ&œž¤®Š8ó¼Â?á‡Ò«¬k`")bÑÒÇe \:ÌËH{ù+Ž»¦˜©Õ‰/¦ß ú©z©0*Oä÷…þ!Å’Nš4_ øëMçI‚Ìt…ä3Ñ2¼„>€ Tv®a%¼ч^?,˜ì>ÿ3þNMé>IÉ´Oï%‹½ÙqÖ:ý¤*¬.ÿ<è9z0]ë|“D áVr¶ê|@È©Çz»ŽÍ@S½cÈFÂkHuo 3/(Ë6f)Â`ÀvœŽPÌÎ^Iš_D.Üt< SŒf
+be’d!ÂTo.ñ‹+<²¸ Ä}ž¡)¥ZÎKô¥hU‘çí9ÛÑî½ô:ã9Ÿ`ò`ó‰Óañ.ˆ
+²Íø©`p/Q
+cþ—ÍÎKQþ2‡"±FX5;6þ³DÒ´ 4FfÙ~1Í«°üá²6€)(bÑŸ>É[Ç5¼¦À±d4P=–µp¶œçÔ÷ÓJŸÅ½'¦œl~r#^·’˜R¶âw@3ß×i§_ˆa:ïIØc(ë.¯U~käáçŪkØ&. ‘DOÛŠò‚‡Ò€d›óAa²ê†­VóÞðÐ%íÛJ’R‰q˜ºeÈ“145Õl¸`«“3AàE2qh³¸nØkr茫¹ÅSAËr§´ÌïÅ„\B`KrÇpëLG3Gœ¡íK‰÷˜ëÐ.ž¸¿Y‡ † åvV"Ï[ö”½Ê7 D£1GWF¹k€ÒYk‚%(¢…M«Þ/×m¦>踴9í^„¼*¯?z¾Š?\À6´•uËk…?ùr¸ó$Û*ðRKú¶nq(ýÁ õ+(ÒyLÄšÞ!Í?H·fWÈg—PÈ#³Ôü͹åÀë±¾(F+ËáDÅŸlŒ¿A†Ú}m#6ú/RûSÄ5û@Ÿ©¹1Î/¯™é¨‹ø÷5—ʈT¿þ.\‚z˜Æ“O Kâ ùŽ¶ ñ Ö†³Û•ÛÏÝ n†ÀG.ü >™5Wå葹±Ó(2ð]„OǾÿu)ⱎ›!½1ÔÅ–ÚK ýºHÛµÄt©Ág×"ÜAÁx0ùÐ÷Á%øa óÄÆÅ°¼ÏªÓJŠTçYÕ=™‡qÀˆV4QŽšôØä"ÍÐôV ¬Ï*×ÇxµE"¢ÂaÇùÖ^‚
+¼­€(Ì1jNéý§¾¯Qø:Ÿ]m)P@ò Ôð†fD¦†àZx'[基JGÊ2ʆߘxñãc‡ï¦‹uÔÇÜφ0™È ï¸õÑÿaj(ůuè&q‡ ÆÆ´!É¿Mo,yAÞ´ç½mÄ‹ â×£:sÑh Œ@ˆé ó3Æ$~’tê1à†á~C=™€¼i DZ„¹ÖÑ?Ü»ÂéòòñLéaÆ«õv‘ö*Ô=¿&˜üƪ«Ž.ÇÔÓ˜W*ï›@ÚÅMQ®ä"YÚ2±N9oH"Ž˜hqZ
+Ö­WYç"Y²÷Êò¢ADš¸ŸeØRÑ»ß&)ìBZì¹?|±‰èúhÇà«)IS.ð/õ$òÓXf÷)¤^™ô[g6yÌÂp[ìÞ58Ú_xþuÈ÷ åøäµ$<©¢N
+‡×x&lݶól³ÀP Ê°Ÿ-³•õFs¦@’!T“­<]x‹?a¢K£µcÊ••Fˆ¬‡ !ÌÊás¾h¦ä|ûó¯
+ XÑ0?j¾zžFROœÔ¥Ýò…%Ì@øNÕo—Åù1÷[~¼Â¦ØhH­©P´ƒ!¨6‘Ñ&Sf¢éÏpß1_²¥¾ÌY”T'_Ìv¢ã¸L§.áù2s<þ5†$]‘ªp…ƒxg±Lp” •ùZ *2è¦ÛËH8¢/7³NfÑ° Z>Ûçàº!7å,ÏX²M\ÊTÎ!Ôi`<^B„•/,ØÜ3ŒÉ`ÀÛ¬e /á) ñÖÝé"çàѱz[ ŒYevRjë¼ëör.Oòà’'e4–ÍðÄŒ}y½\ö_—Öá^’~¶° ÓKŒÍägê\À4ó3ø8¿¡Mí•œ¾Bc
+2€®çæaŒ¥ÑÈêö æÔnÊdœÜ„›ì=ºm
+¹!œ ËA(þ iÅíc¤ú¨æ´%Ý’ðÃüŒÝî»—wÐåœG¦/pÈöÛ(á
+ºQå‚+‹T0P`ŒÞƒ—W/ñ{%~³œJW¸üt!­ïÍ Å/ã 4õ:ùéœË>*¿–ãô ¯„PÍêÚù[Ôoç…‡ÆB\fÿŸ½ FLÆÒèX!nÛr÷Û·­û¦{ŸEû‰# ÖZ…wZ†ìLémÏŽvª¢géz…“»É !"¸°ˆ•BqT‘eëpNG$T*¶•"ϼÏ1[‡Æëˆã·Üsëg”ŸX5+„ÏaÕ¸=.U[§3§’ù|©/Í‚bf’‘Iêªú½”
+ ªJ¦÷ób‡Á }!Ûý @Ìådg¹äþ1veQ‘ §~|‚Áý–ùÓ¡ÕF[ÚñÊ|Š
+lÁ÷®àw÷¹LÁ¿…”€õ{æ?Ÿ\ReèQ^|_‡9ÂcS¹è‚Qc›»šrLXdH ÛåPàÍ9Ç0º~+Ëšpú¶| ‘1QB†ßüÌ(½9Ï+þá¶øÒœ~yHø‘­ÏuCø&Tl(šOÙÔ¸Š'¥;fB˜–‡†ܵƢRì+Y @_»<“%%íA9%
+×Að>V™¦bè±™*Oà+Úù ã(‰Ý2æ"°ßÅ{Q á•„ò>¥1MZmfÎ'u’;†ç-‚0ê@‡£^çK7ôŽ^³J¬ö>;Ê{È¢Å!©*»ÂàyÌ ‘ש”–÷
+“Zü†g|-nŸö½Ô+%r›âS2
+RºfÞÜŸÂ[ZäqÜÃ7›Mž1œº•IÐ{~맴‹»]«ùL1›N¿—Ã- kn¨&Ï·Aígò±P+4>"Ó•ø>\~LêÍ?iÖ—1·½Ø|:.- žîÜÿÇu…r¾MÚ·Y¦#ÔûÑä…Ök_0¹ra™Ý ,Ö†`­†)µÐ G¦Oœ¿?}Ž¯žðúÔÐ×në
+Z’QvªŽ"X9Ù†z¢ÎôY—NL2BâµÁX^ñ!­|ÖJ›hJ®Ã;nXhña'ä8U°!ÿ 0
+H‰Œ—M’c7„OÐw¨ Œ‚
+󅘣¥ îé÷[Þ@•ÉaS¸ª¯ÇáîÔ¡q
+ « ºXu(æßuÑ ‚àÚ¬÷O1¨B4~l»sæ„Ÿnmø‚PæÙ“’MËð$DÒ°o‚®ôµlDª‡Cg(.uN TcF ŠÎ]„Í¥QA6ãR¡9Ç*æ;(ƒ%;¿‘qójFó¹"?=¾OF
+<`‹¢}ŒSè@ètÂ{îý{
+KÞ<Kÿèס‘|-çQléŠIÂÕùAl‹2ÒŒ~³û1ª¤8z„Ý¢
+S|Xn­Í ›[2 úÝ¢d¯Ñ€zË$ㆿ}on¾Ðe®·Èä9’_'Ú³ QHfÄ¿¹ùBàÐ榤z/‘Sß²mh eh«w
+Bä°GÅ‚P9T½gi÷I–ªâ“˜§Y‹9Ÿ9†ð.åœ;tí5œÈqÈ:)T5:jG®ž½ZÙ(ϱš/}“@¢ê w=žSG$:¸¼ë ?Г.W?*ž÷Ìó>)ÞüÙ£%ê0Dç¾
+‡Ï9™oo%@ÇšDŒ«BÇP½šz •¿e*bÕóT<ÓœuM¶èy
+ˆõâ;n'“¶Ü è¥î¿?€wT®FÛΓÌBYñÞ$pZIÏ3¤wnjJ‹Ù}¯Q¦Õ¥Ôfrç¸6]ÃEl5­Œ3#ëáër2¼Ÿ½5vú “o¦œŽÁ¡ÕÇS¼&]+rjދᨇ±ʵéVwòJ†c½ãÁ°e$ûd0‰b}D8·® Oçe7;ËMâƒÉí½=á†P;6ßsè…Mò5¦jÛó¦¤åÚ?‡±-1eÊDÞú¶¦^ìÕÇ€xµ*Êy•}ǘã!Y²‚~p[×»^0¿ÿä=ëÙ”¹”ÀWÝ[@xCÁÖ³¬8jFönòa~…ÑÕ¢8„IHºqÒ£)ìµßv, J3ü“°Rzàqðw~]:¢ÕBiý°¸,?ÓÏ3sû=Ø'†±ÅÐWб8
+F§ôú±b/˜“ÉÃ0o:­«å@ÑAÔSVìéñK£G5ŸÎA àxg˜ÍÝ-pø÷}YoÞ+þׇ¼NÞ´ 8±šùUŠä×ê ‹úe+›ñéú²1Iƽ• ãå“0ù×Q»³Æ0‰±Ïk3>'ÆÛ_@fSke¾®6NT'QD¿—Y¢ŸÕÅ£tæÜéßFþÏNõÔýšžDï
+:ðøýIï<þ¿Ý<Fœi-­)!ÛfT!øÞ¤Ôcñø
+· V¶w)˧
+¾¨Ö°ÿ1^æH’ÝV]Aï¡m˜SQ4ËÕ
+:äMí_çÙUùßWgP¤ì›øÀî@šû-CÝ€ f7>¢/+ß7!?«ë%f©S$H uN;31´AòbnX“rê^èýV<]%Êh3#ål ‹±oòÑ‚B8°(ÌÎCc Qƶ‹„}è|˜gv“EÄC‡_)'çÞ*q’îDöBg›¸oL›"¹2–`çŖaž£K›ì ,«¤ }og}©w—Ãlö,\5k5iÝ0ˆ*‡z!ä'‚BtLèrä¿k\K&ÓqüK‡ä2€¶wt2rŸP­ L–º€L‡zÓÍo››;®_¹´ßS04Bmˆ$cÄÞã`Œö)…ÚyÞ”Ô£Ð4Émí¿pPåÌ Ô5úlDþé8M•/ÈUȳòìXß·±ÍoòdjúÙ;˜jÆrõík® T‡³£%9©YÏòUy½+Iàp/Ìríî¬'ܧüt›âÍÆÄ·)~†C{ò÷.²ÐL[ä?& Äq*­²®‚чø˜žhÔ±$Y¬r\YÜ·ÈÙ$kU?%a”¾!3ÁŽ”ãL:›šp€ÊÓ ˜Á4Óqêï?<üƒçÄã…vR‰ì=?Ø…‡-û3h.”ä!ÜqïÞ3H“Á?ùÝ´¯vmäV½½òç,6Ôií$'PŒ8/}CüÚSA¿ñ=8‹‘üc2ߦ «nvV“…€•óîVf©i.K;O™Ñ%ì}Ö¢ì^0^u2ãva†ëmŸ0n°d!ÐÖ(^îÍ}¾°5+4þü¤œ ,§ˆÂ˜¿òÔ„}] & gÃHYÍÜôHß\
+ÍÂužÕÌB°ìå{Øש×#×jÀ‚å]è0çxc›Äéô¡D¨ä¸¯¢iHr°sÃ8Ë"qe;†¯&®‡~Ïz‰«:šÀPΗž@myiŠ ‡.Æ\—‘´!alHÅÔí¡T~?Šm40-iàˆØçˆ{1Û©âˆðqñ€È­ƒ\÷)l ¿
+òÜ 9B]dN„`K‚é’$òîvü ôùÊXh‚Âí¡AÈ=>¯°'èä6 †zü7Ú.¨È!SWŠ½¢Ó‡;°O XÂbS5h7W–+Í77Dd™±™¹þÏÎ:VY›²!S¦Ža¿7¡¢u {uY¢Ur>ÙyHeËÿ`CÔhá\Œ†‰%SrÔËšØ+ÄK£¨5X gísp²¸¼Õ°†!Š(fP£µE ðB1Ç“HuˆX«ón7ÆF›Ãé}…HÍ4N“Ž¬Ò¡v.‡I³‘­ç€Ó™ž_¯Œ˜¦ñ¯3ÿúÏ¿þ»© †”¹ž´7Øo¸-ÂÍ}ȱK,â'`_³|“ú¸!Evœ—-ÆuAßsÚ‚p㬠‹gÉS|%ê‚ y´V´KuXÎ)Ì5LÂ(Æä3½ûåܨT‚/ÿ! Qxrf
+k•q÷çS já­{`3É‚‚s”„1V²ÈÃ*Ì㤙Œ aúSÇßgéù©É2²¥Çç~5rÿì+uE9¤ MvÌž9±{°8Ï|goìRËöok„i¤ÌÁLT †qoe?§‚o–Å+záRÚµŽ€aVŽB Èiß­p ”gâ¡–Éù; ¡ÕÊMA«íIv ¢·¨m§iûSì/ÿ¯_%d™Q˜’ÝL«
+ø§Óu¸‚r±ûÝôJѤæ™F®ÅZJ Aœ §£ÀÄ42Þr·¡ÈÁ¶® •æ@lkÀÈ‚÷~s I–BMÙÓ¹ ã„ÏdF݈?)±è ëQ¨ááæ‡ö(T$³‘94ÕŠ£èÃņûô ;³¥ÃÀÈ\ágZlƒà! ÷ž£Ù©càWÄ;¶æý‡BÒbÔuqC ·­s
+É$¬W“VH§À!õz[S› f$Ä(Ë;µ~* ã²rü4ÛdPÙÖäîÝO£J4Û¤©“¾ֳê •ðî!¬#šŸfEͽŸt‡:DqÈÈWX¿Ú8°|ø·åa÷³/ oúDE¸/EŽúU·—ϽzÚRØŸ@òÙÛ\ç.•#dÔŒ}7·!Æ.Eĸ-”úÊœ †( áRÎ}gèŒ62?ØÂã.<„ò+GàQÊÝ)Mňò¥Íi܆`ÌÙRþ¶=Ž]$Êç¼ÿð@Š4Ü?…Þ`ò‚×{<×áì!ÒÍïF!ºhe´ !"qûœšÙà_¨T,J~¼<á*˜Ûrœ«‚Sƒ¡êyy‚±˜úÞbw]üS‰½,@£Xc¹Ý¾IÃ9E)m"”1ý»ð£A àlëÝ›˜\"ôˆS,~m.ƒu­°ùÚ¨_¯ ñÇ#1ä&íæu9ô8¯Çýsƒ‰Ä¹Œ{–cü¹ -‹ý¸íïZsÎ@ÐÜX1ˆ½ÆçÌÍ ¥Fñ—ëœõ8Ì"é ßà[Þç¾;¾YƒÊÍJ”iÉþ)*ž‚Qzóï‚å•TPMwoÂrb7zn¬nñk#þ`ƒ™<Æʯ°ùÚ¨÷æ©ïŸ7 n3H…©·—¿±xdšˆ +µ^^®
+.K‡3Ê"‡ ¾÷á¢^Wž|VÀËb]Ïè0žîŽÉ¿ÍU0˜úÒ¸{cž µí8ÝçÂ\–Eå8aÈGw!C¡²`™$HOýßÅ\’…ðñK5!¥Z†£‘kq=úãN#ª¹Æ¤·Á,YßûÜ *Øvò±…X‰1Ãj¢ûÑXMbõ7&½M,jD;½¥ZËoorå*ãïžC$“XC%õ±yÐ1ׯÅÚE$“Úf¤°›]faˆPi=äNÞb
+àÀ–ÃÍ9eݦˆÈ‡G ÒeáÊDPËÝ«È@d²I"•»çTLbTõÄpÖÄ]y¬sƒäœÖ±›ž”;—c]÷–?ƒd,•F vƒþ,_Ä¥ñÂL_ùUäfí¢¨ ;üy’"1q°}ÏV!MSwûѨ!Ám®
+R£XqÂpÍÇ_@,8O |ö‚âsFHk*ýFaFˆýA×F%Rä ’ß©ôF²áT.Ó>,Da  ”Á×o:•H\N°•~s
+Ma´HIí¸3Þ™¦"·¿ùè6'Evz kB¥ðäîÕ3ÈíC\Lª
+æû­z=sòç+Äý;’¤å# Wú´¾–Éè"ÐðÝæø4ßhNB/Ôôq‚öŠRkÂÑ®›y ,HmÒoÙ ¹ñ;¢I˶?lRžÓƒwÛ^Ò~g“×uÈ13H;ß””áøRÏîa†#œ^kó2窢M )J=7>…`’ªôóî戉&tÔÇPw¢²Uê"}K5þñ2G’ã¢è æ´iL`_LŸru†¼¡©ûë}d¢ÉîB‘4!µrP@.ÿ¿ˆ¿Šª•„¡±ñìOqàÚ"Ž u¹Gè VI{+%
+Ê!IŠw¤s=›"Z¬yCùJmšäf—mA5
+ÆT`+(îà5‡ Ï-˜èåÃû,¨Ší“–%¨ ÿ…Azˆ¡/Š†ú¬Wf™Ý"–Ô#±¦‡ª4!(R©¤® ÖÛ@Ê‚\ÍŽ‘Vð唦kÓàþL75^ÅŠ(ùD@Ë!d½ –…vjýTâl‚,¿Máv¹«¨.§³IXï!]´.–fô¼P‚†Z•j_Êðï  $Qæ_4Fe@†ÁŸGG²ƒdI¬ 2ó°XñT@!r9¿1ucËáñ[¹t‘­,îÉ¢zŒ/ÊŠºy·×Î
+f¬z5ôrÐm–­©üŸž@#»ï/ýºƒÄu_ÿÁ¹ÿú÷íëŽx•î¤;jÑ…œrè¯Ô0 l'®¾«( £Jù³¿â%h… Î¤C­î_/¨ÈÀ+F²™¨ä’R¸¢çѱJ×#ˆ!ÿsñp{ì
+µGØvš mËVŠ]N;Ť%¥Yå\¬ßýáÊ,ð 7€§Ò×¹6˜Çjò,ªøF¿×”]$_êC§–eUY$Ì`K?}@ßL¯àUáД_!B¾)‘-Q—¿¬R ¤Hœß:²&Ì]—ŸB"µÑfÇzêÝq z‡Ï› ×VÔ•Yb˜ÐÏ«ª‡áæš A–µÖð¯íò®-}‘,Õ¶J0سd] ,õfx²ÅÆ»sxW^>ý6,ž|=*ê±”Š í­m9J•ÄŒ†Ew‚2[™Qý,¨ò¦´g;£€²ªÎòÙÂà4Ægø—Ê ãYhãÞzP÷€Ò…±­z^ƒæ‚<Ÿ²™Ç5É„dIRÑRúyDÞÑyj&ª3©ƒ6È—ò$Šèø–º‚¬ˆRFrõƒpÓØÑZ.2ƒá1°&Ø—ÔRv
+tBwÑc{½‚45ž]£Z=#Æ…±rñ¾`ß ì9¨ú¸;reqÔJº=RÒeá#±y%¿/Ò#1]eìO ´$ïm:3âj‰„hÀì@#l4ð»~î@þ&j![õÑ•6žRªýn>’ÕÇtت¥ÎA<S5¢«9¨RËfµÿ2C¸‘À_ë¨A`mïl‡ô‘#ß1††0’MPßäö$ðå`’UWÅáˆ
+YãET òfm$’2 ÍÛ¸0‰ à }Å?%EÆ÷‘™4ìÓ‚3ÑC8…î:>z¼‰Ãˆ ‚^ÀN—7‚d)µ ÈɯŒö&ˆfíz
+Q!„Ð[ßpÐT°Ç1ÃȪc' ÙŸz z¢»²DIS6ÅkðLõXŸÅnêï!4i
+Á<¢6Ü´·7†‡dFqBñÖŠ 'Vô¸0ÌŒ´`ŒBÿ"ѵ—Ñ·ž:’uoN&ÐZº,%÷ät%NÛ
+ÿû&„‘Ä~ÑcpwOÃKåD ‘Ž,‡ðs¦<®4Úg¼Ê—Wnþ*°VQÆ&IÍ]BÅÇ«ð3À;ÚçHøghA±s€h ^ô5lX9Ù/¹Ó°d´„û^ŽÊ¤Ë“´jìi›ÓøéÆ_ª‡ÄÑ^ÛAøWìO69A²“Næ踯ÑÃø1P°•ŸEW
+ »åý)~$‘¯é~F×N-]Fm!“?ra·3Üë„•R?Gà#¬Y[Xz|èç1Ð&ýh§ÈÌÊZ&œpÐáU2[ˆš(ñOóx‡ÁãOò¶M†Ïê‡  7å6×Çgc½òB5—ʘxØý m$Jmêñ”¢/ã>9×èo&ïR-Í™7'h:5h›L*¹cØ99–‡»Ðg !¿ÇÍuUŒT¡
+?'JIpð’hofcHCû)Dä‚”3JÜ7úâÅ•™m¬…‘· ObÆHçè?عh{×^I#f—{Ú€EÜÄöa!ý4/X´‹ù´N>’ŒS°rn' \ìgò™ƒÎà*Ö‹œ.>TóÊ×UÞ6¤ ŒÚÝ9èJCÊã2š>ít°‹k¾zš?›œ“Šš¹=¦àfQ-s—3¦©¦gh>ºÏ”©ô™Ã1ä•Vj3é‘Ö`Œ¦7$#´‰ž‡ÕßA&~™2Á@ü¤n.™ttƒÄŒo¢¼\ó.oÀpxêŠHôíß”dA§ía»è‰©f²Qfß‘oíYˆg HR^édÜ·ž‘?²é0õÜEfqáøŽ™‘×LÓ¥ã)|™öT¾jiû*ŠÁ¶2h³í¼<~DeΦ,
+°ï(5ûn üFzžšuŽæÁâœÊ³ËÝ“|½vòT¥·SÐK½ÿ¶ ¥¿h#ZùgA/ÎðyÔ
+¥SgŒBš‡Dàrº,‚+`VQJZ}UÂ3ð1¥bU¢÷”?í­+‚|QÖæ-Hæ™™šØÓMO<…x÷¡b1 {F?ŸC÷¡TÜIÎçÛ¤wÆ‚&’žõ»G¥÷:Q -÷vJ “
+Ñ@ŸÌÑ] jµ!£
+¬Û°^Ïéï,s*O0Ö<ܦ #±mL¢œDr}ª#o]²TËC0^²Cð7Y^FKç B.b{
+b …y[-þªËmE!šõ¹yQÕ…ªòþÔ%;`O›òB~ýrÎñ± bwµz
+ñZ]j~=çÒ9×Û\:ðúªK¿ææ0 ¯þÝLÉÌ¿þó¾üõïÛ×ÿøjŸïË椱Uõ‰¹¿ßÉ• ßÍèË
+ÑgBîÿ3^æHräH=ÁÜòeØÇØb©s‚Q«ï¯Îû€#ÉŒ@4K¡13aq¸ÿÅ6GœŽ‡;¢%ü>
+)£.PÎÚõ{# é5ò
+OµÓ;Ž
+²Ÿ©1­~bÝŠþà ÈW±ÇçÔÖ04MÕóê
+Ùc„8©C˜Úœ!¯aÞ8bȯ÷|ƒøV„\ù-¨é:X2¹ˆºh,à rµv2ƒñÇ_Ú°jÚÈkôCÎ6øû”õôt ‘bÖº½!%Žc΢·-Ï™úµdh£‡•[
+òSnŽÈm_üÏ I¤Ôvn¸Ó557P¤P ôVMKrx€ô–Û‚ ÿDB™à7ƒÍˆ£#L‚Ob|Æ5Ëÿx“5¥7^Ò_R‘ð×Ð=Þ5yœcÐ1Øà>G„¢ˆd?Ô^œ»d Y :@Žx\'ò#’€ƒvîRi€K¥±Z‡šâ «Aû¼êG¼ThãÕXü*^oƒ¬íQp_‡b’.‘Äæ[ÑDð$ÕêËc”\dÓñ`¥¯[5Ù,™²¾:ƒ­Ôˆ¿[žøåÀ2~ÕzÏÂK$ñpqã=ÙžùÄ:ó‘Hø¶®ëâ]ËG
+O©‰Î>/±é:ñ­0îxmõJõùËÃ1m9×”^·iü=BMQcjÅ­|Ù£@B%‚Ì3ꎈYÅ/s¬™³€$º…cÔñrV¸!ºbTö_'‡‹(Á¾SI’Gf)/Æ‚ qW<bÝÖ<)ð ùÖ)š£o-º³êR®š¼ ¦U©•TÄ+~.ÌDL¡Â;˾¾¼†0\çî8ñšÜÅ ,B3¶ ªàP+³!-‰³@+ÝòÔøQ®Æg4fÐE°ègæJºØêZ¦b¡7.çë(¡QFÎãÒ`d£§ó‹ Q"Š/ªä4rŒÆ¯Àqi‹?µ×Ï_î*Á(-9ÇÊI³½qžz{:ùxWŠANêcÏ"ÄÕf¼yób >Äú护òïFLu¦)&Ö£Ö1ãt<ËZ_ æ¥yãP ûÄÙÍH(l«„¡ºWQºŸ7…ÒÃÎGçæuGQêÑX} .Z|‡ÒY¹æQAÃtFwEQñ0²Æ*6ö›ŒZgYƒ+Dù²uÌ’öV7 ZÃ"ÁÉ~d^UFÃãýdäBzñÓÌæ¡)nS…ŠÕ²ëÙËÑÒì@©1Ì'qî÷ñ!N 0Ot£ÂSX‘¡ gƸ€>@‡­g \µÅ7Ð%9Ë¡Vé.IøAß“ñªwú;W*l‰å— B¯>.s¼­‡ õ9 ËUœH¡ '˜×åà³ÙâOÑåÀ41=Ÿ×a"jÒRuòsá”#!ãcn,ƒÅÕÖS¨¿Ä‹yêõ
+¼PõWÇÕRæ!/‘Ö:|KÞ1Öm»Æl^å`–°Ý LÏæz!78OÙn*Ø׃^Ab0ä¯)¢a}3R šMÛ0?VIkøyªeC‰´õ !~žèž ÿ$YÓgÅf„ƒT™Zä¬-†*[‘¼Ô©lwÐ)׆™GQ{Ò[ú6HßGâd8u› ûvßa²Ä•1hêëÔgK"¼©‚t9H¢BØKça'ñû1.j,ØÛ6Zovãk+}$¾ŠÍ:¹†ÂyÖÖ2´K+-æD09œžXêƒ mÚœºq×Q•kbV°öÚ
+Ž H`lAb¤÷ð:ååe;®Ù Cuq’2Þ.±Mg¡oKCÇá‰mzšóÅß]„Þ
+¢Ža.:%ä9²ÐÙ¼ÉW
+®‚P0ÁC KÍuÈ~uÇÙ@uÁŽŒ8öQn„rCnÞ±'8’H\ß¡“l=§8™++‘nû±ä-ïî£Þ‹h¨]Ë Ø¯L‚1ø£»³ÍXÄ‚bz^Ññ ¥ìo° ÑfMbÝ“šñ’%Jmìx„‘ÀêY|R ³<…,•{äËn9žÃšÀE Xhø5–ówä=pq=Éû2%ª†µ*ZfxÙ<ÍéþÌ òè"!?hÐËÑÕ@ïÌ´K°‡Ÿ3ˆ]©¶ôg{yŸi©ÛZ…K‰v› Î4Qy·¹š…½gõ,çCÑ籈n±ÿØÕÁd†ï¯°i…eðg.%]k*ÎI:_¾¸JX8>›²Üî0öˆÙÂÜþö›(fŸÔ€HÒ©7¢ã”?Ã|+qÜH‡H·µ•z÷R‡!¶Ê~†ÅÂ#²©¬†™•„²ûÚ:õ•+¦ò<)•¬¿§}VI„)ÒÞT"`)k?¨©mü¡Ÿõ¦„÷¡âñ°ñÜ…×"¦É ¿û›ÁÖ°X¶™ƒô3î70S á<c¬uarl]$¾2žÊt—ñq˜‰øŸ¸üÊ
+ŽMþ¯8¶æ0Áwê>©žHeÜü²øÍ’H‡ "EóÌ‚NY¦nùà®QÉþuº†T±­ûÞ±<·MûœÀŽè»ã0[Ä«-—í4ôh­hàá£,{"1#¶!öèEj7%­KÃÙ(oõZ4Í[“x€·°—1ƒJØ%5¾Ó‹³‰ñeP‚ê¹Ø§ÄêñKùK7‘ë4˜…|Ä5–¤õ=,ÎQ¡£Eß8ι.Ø
+ðÆþÜ|"ÈP*‘nóBçÅ[]Æ ,ÑÅåL蜜 ÕS[p Mtý$lo³ˆ™'ë;EÿŒNlï§Ñ³bÑv¨mȇÒ-ó\ìÈ…æBíùPôéE=BÖÌÊ3S5hŽd³—ÜÇVQ77 –BùXßK¬ üC?6ð&Ø2ÊòÕaŒ—é»(ksp7ðùÒud¶ R€W´9e¯åÛ _
+0Ê0ög zûŠ´° ŒP¬wXÖ[Úe[\à®Ľ$iB¸/ýÞ>ŠMç*·ß TÁta|s_¦ºä%’Ž%ïNuÚéÀÌS‡²ƒÏòf~•5ûuS$ƒŒG’GGæ‡ÌNKz— PI™h«ssð´àm ¬üËeƒ7 ?àkÒ;ñd
+ýãÑ¢hd!© qÓˆ—呃àáØz£c
+JdÅ+Á/w7#M\Ê]6/_jȆŒŠý àûc½œ2¢j*‡¢OoOT^âÐCÑ,!*P«t¿ªÐ°X mQ¯@ø×|°È)È(úÌlOÅRÒŸöéË"„à=“£„Ç&EjBÙ/E×™Ã:•°YŠ´±ö}Ô¥ˆQ<a4Ñ?Ó¢‰}ôÝ5¨Ò¯Uu_Ï(0|‹wñEöW‚NíìŸBºQ¾4¯æDÐÄ Ò3\ø=’~ÙžótieÛirzU<œá’è)*‰ßÁoÿÒäµ</% 'Ñ8Ìð*Q&ÄùÑáH¤ Ü›í®ÙE1'ã0»$H¤ãäÚ>Jþzˆþm½ŽÏb?0Ÿ›W_IJ9ËÆôKÁÎT7§ÁòÁK9©Ž
+#*!ÕvÚÈ&3SºµJ
+¸a F±Ù´‹Húÿf–ˆ ѽ)yD}¡ÿéK#ôÀ—±CÑçMìfòÒuæ˜ìRżþ.<f/efEßaq%‹v—É»MÅÂŒ¸Ÿ®@«ÓÒ.JÄ<”#Ûý(’&,Ëæw‡WQ†¹Øu
+–ßà¦Z
+' ÝJ< 7 ¯Ú–%¥yËw½í“š ¸ku-2jIò„ôP@4òMƒd…X1¢(b“DúØ|O¼VÉÀ®†šZZ)á.(!c†ñ’¬öˆÉ)YCÍh}d 2dôÏ)H`&þ4å{2ÏÀmqyÍV‚¹Ä•¶'„l+¾gâjÊžëµèN¾ë=¤
+*qäVe1ŠÀAÜj€Cæën¦& @¶e­)Á›PÌ{³Kpž"dPd²âhz–4;Wð8 OýJî$eD>)gñ ]1‚Q¢SòkãWsXœþ9iYÝ0Þí¼Ÿc»ËÜ%Œ-Vœn?§à±ùÀŒ‰xÞ„×S#8Ù:Ó–Å¢0fØŸœªÕµæq§€Üd×}°c®¸«Î~mX\‹Ü‰G®Ÿüý°Üß‹XAB
+÷ª™Å‰åUø„ùñš–H;–^çÓˆJ–¯â\¿KØa4
+²èHk‡†nI¸Öm—Ðb)°ZI¯d¹8e¹ui7e’ÇgÛuò¿".Äô®H(
+^»DÉ…ýcue3T’—ÊcC«]…ç¨Xh”-°«R—ÑçüvXŒ°°«û”•Ð¡Ï°×ðÓLâBãpK
+CŽRyz³¡>—…œ.½¦àÜø5¦µKð-1é»;E(›v‹ˆØ“u¢n1z’ÅÄ¿ò窉OBAvQ„*Õ-‘_­¬JÌê¦'Y_Â
+RswŠnn
+€†çdGm…ñšzè–݃¥­µ¦2v—'(AU “>1¯&‹óðË£Ø1¨ÁÏF¶aVÔ©"Tæ¤M"æÝy^Jì*¤":–ùîB$kÇg}dÇbئ
+–‘•¯ÏWü<߈3*ñ€T.mŠqãi?ÅÖBeýð èjËιQ㤱1Ö7%à o#+Jž}x-bž$ÔŽ'W# ú§WWl«-9‡kÈòÌöUoSñï{g.ä°¤R*
+{`TšåR”⎆𼙶f+A@Ø ÌG<»@Ù¸•v¸?Œ Š²ÏU/ˆ½)âÖ­!`‰Å„t[,8'2¨ÕJøëF"á;b:%€ ôÆHçª×"åP˜²EfÃYóQÂÊg;”(¢HV1F!eÁb 7 χÿÁ•sÜArÀxŸü8–èÀK½O¾êë5i­Q%½¢N¢3P`¥ÇJ[î)C‰Ï€%9ñ2Êlì%@ñ’°7ã›c—`†Ð <
+ØyJ Ú O¢äö<ªDoìvS!Ø
+40"yÜlV|b–15ÊÓJXFøºZxº¸õÿxE¸¥ŽJjtüs8ë2ÁDOkö÷¢¹(P²W&ýðŠ„ þ³ƒ#íÉ\‘°Õ‚/yÀhîü,/WÐœ’_øQÝÌ
+Dÿ€ƒÙÄu2ƳŪsÞã€QÁ–渚@ßÇéá­Æká„‘H]ù}¯EN¾\×Â~¨:EOg…ǯUšÉZty»X¢ ^^Š 'îsj^ŒÆ*!(Iqãpl%émK,æÍüžêálˆ¾š3JèÿÉN˜™›p,HSÒ>Gœ
+Œp$æ°WÑ z¿¨DªEK±9%모ýårˆ=dÿœ¸\,Ñ¥U«¨ô!ʈÌÑv…4$®7Ó]0=H2W7DnERq¾è‡Wyüs¿iq/ß1»Q¿œç:$ýlW,Ô¾•B˜ýÝ9]îŽ6´`% ȵ]B›¢@_Š±.$ÌBTÙž}U–’#®|ZJ¶>ð#Q¢Ê‡mÜÄÀöã¯
+d™1†Ú}ZàCKà
+ZŽ« €.GˆÕáaF—¦VDÒ Ò(o ŒÓôCZÕ©–ºÔñ\“ÄÇ'™l…é)½Û1ì¢\£«‘<{,V¸S"á^!—ýþ8aNžôˆšò²çoƒNÓ÷î-Ð…Ž“u‰]þú÷‡ØÆ‹ô©€åjÿ8åøñóŸ)¹Ð‹ ¼=…Œ“sMX=%ékQ”Ù˜¿˜6¿ØG.z–&
+Íþ.èš‚ŸwòdâÆ—Ú]­µ  Ⱦ5¦ˆÁ;@tÓ,Ÿ ÄÀë³!z
+¸~‹Àc¯‡ADãpQÀ[ƒ¤¨}·áH³Ak,‹ êfÔ
+DÆi2N^´ ÒcÉRDÏÍ2="Õ Â6ƒ‡¬‘Á;áV’"¥jàa 1(¼x¥^ž5$Íà‡ò įUŒ'ÊW“A&"ßpÔÑ
+z<墄‹IÉ>ÀfÚ ÖG jÛõjžë†> ±·âóË3¹ôèwéü’ÌawZ5©ƒ¾¥x€l¦ÒK­Ô»sÏÌøTQ>!,†ˆº>B5¦ÏK×s(-–(ïHü’loß©I4 odóê›þ˜²0öhòK¬#ù­C#…2rr €â긡{ö†ænÂZD¼Ü=‚ŸÛ°s8“C1†¹‡JÀ"±ywò<8 ÚɃö4ÿÑÞä˜o¦ŒKG¿±ª­0£ù„“üRF¡g»p‹!F_£ñýq&3tõ#¸€á¾›ñÐI_½±O,F™RmSЈÞa£áùÊšA´ˆÒo}ËUÊ*=ýž6+ɼA‡=Êñê¤0àöûÝ1ˆ"Rä½Ç „ôÑüø)Ö‹ÛH¢@خыûQø?©^ÛÝ,6Ì”•ñÙ½Q4BÈgðnF`š¨‰y*PWЕ NߌrøӞܽé äV{W³HbiÖcŒDM]~‚uò—Bëmm·¹@@
+Ó_ ]Ä®í^Åöv
+åéAªŽJÌ Á°ã¿ æà¥`SŽ7PQÇC#¹µsØÄh”ÊwýÊ™1§/ŠÞm­›E¶Õu:¡ÁØ7ñ1‰­Ëi”²ñ) |¸f+}.ˆ¯¤]Æ,‹ ö„÷ЪÃ!òbkï…c™(éØ¡èƒ(é æM1ÐUWgÄnçðý„ ÑÚÝ ¼Ê@f²•ý’–:!y [+;Ô èJP/·A†qUU&måøª çz(y¦„w-É©€oЪ ó=ÔÄ÷%Ž•¥³‘¨ô%ÍŽ/Ê^ªˆkÂLà¥î”’·+>J4*à×4NâQÉÎAÏÂ*Í3ëЀe„ ±`:wU"‰w¨AªŽÌömoå¾'
+šWÍ«öL=AI:›í—±:˜m@ÊñàœûÝ9d…?m†cNî|È„ÜâªÈØöãØ'ô
+€¨[¨öiJ(¬ææô»&Z£X; `\!š-è
+g1p¶Ï°«R,f¢”½Îm-u4[Ù‘dS1le¢{pYIª^ìDV‚Ew#Süµ2 é =>ºÎ½[A“‘æìZw$%†æç¯í@:ŸCA"² ÌGÔÞƒ¿ñ>f×K2}þû±†%Íí°‡þÀFaN[ùÅvGÙËè³€ë)(kKÒƒÄ-t_H‡2aJÚ]ˆ¡o๯{Ü¿†¦l(6>X,3ЩO»g°+SÃ0«'3Í!&þØ1Äå³Já¼g˜Ðt
+LêíÉ:Ä£á–v€x¨0µzÍ~>¤ð€¦'åßÎgͲ:®
+ V(ìyÓ{“k‡è›§oÀ4½ó9©^r¤ãäjý
+jB¤sç–PîC¿•uÀñ#ã@ËQr¯'I×.ƒ5ñWáð•Šlû-ÝÅR•µ8õ“k{ÃØ}›ÿc á;΢é°cü:‚Ò29²ü­ì¼Œ³àëxøŒwk;ÐHº­ÀØ1ÃcÔf ÔcOç@Æ*J΀ ù¼Ô ê‚P'6Ó€sÝö—¡¡ªÈx«!õ €äà€ðH qf—ikÌë1Z€žÇ¶ƒqà#ÿè†Ð¸ pòÓuW¤€û,4¶b/Võà…4n]ç
+'î„}Úö¢_ÕDýSË»)®Žõ
+'ã®v?‚HPgƒH€i÷3äi×;B®B|ñ¿Ò|ö'LªAm‘0jÛ0ŠÌ‡™®Ë¹˜aV
+‡¨/Õ,½¦G¨+OV0€¨³8ìANaš9A57HRôEçw¡žÜø äOk{†Ð¶MÔ´æÚ[&2+1ÅèÇA#dØKž‚„a›¤kžn|B\~Ä<åï„x. q­åÂL© õïåÔkfÄ%˜Ž.*—Å£Ê’Ý ´Þ1/±‰ö”¹ Q²cÿ*ë(kcˆÿñ]&GvÜ@µ@>ÐöåLy¢ëÐÿ«^6­a´‚95@£*+d'‰oË&[¸=‚f+ùÎ’{Éy%
+
+1C‘'É *ñò~Õ÷Hi*E>ªìÌn1ê.·™³¶‚ aƒ˜ é ÆóUþ@ÖURÐi#TYÏ)OÆyHC:åIß4s–ùŠõÆðã˜$Ã>5ð°#Æ3,*»@xç1Ou]‘“xÕã¶uæ¡qQ/î[p[Íi¡Ê6%«4ÎŒ¤zaÃ/ÃùMHïØ_ud5=)Œ[¯ ¦Ø@õ4}^+´£J¦YX6\“oêòO„H¬üKnR`PYÃÚñ«áR‘,Âl]Çà]ø¸ÖÃlçr(!É ¯Ê¼Ýñ£è¡1g·ô!Dس‹(;‹žO9|_z…(ºŸ&þ½ŠèõT~Ä%ºHÒ˶ðVkVœº›ºcêE”àð'þGõ²Ï‘Ÿ‰è‘¬À*¡µÜÓK²¦“6,ƒnc±»,>–"3ïj1ç…Ì‚H®c°¢„ÝbåŸP
+Ì3Hð·+Hme¥TJ€®‚.égî‹ø)¢0ç>‡o…Y*Çùƒ+‡v9ÖäsDŠ˜ò×tIå»°Jaø””ñ(ø)RóUüÏÄSNŸ#ß+GáuC(ÅñBHñ…ÏBšü˜_*€)kB«B¿õQÄ×á«hÞÁ_̪yyG1˜T”‹. ?Š/Ã&(V½d¨«ˆÌ…š
+š‚wïÐEÖ2KǾ©)a0•Y|Ó¡upì\€ø  ÌèW/.=–ÄÄÏK…ëÃý¨ÿ§1SXÌv°² '»IŒùÛXnh¢t§¤SVUIŽÂϤU‰„¸_0‰gpÑÒô_ËAó¡ÿ)w¡Ùü“V®.UdTRÖ™¶SB©lJ‚c•,PlÄ>¹„Y;¶¾›
+*Š-ãA¸Œ®I?v&·àcXÙÈOx”¿˜rä˜qÐt—dqGâsÜ?¥E¼o⇹ʕì­A0ÃåmF\§ˆ»fÆÎ4OU*Ì‘¢ž·’
+^.@훞E™4ƒq…a²ßµ`x" ÎÆXèC¹'I{×›äRá4fa>faX{™…t·Ï'/XºÆXr¬¸ÂRÔ„¶L¥œ‹É!ïQj"3‰?V*„2ÓK¸ÁØ…*ÝoÖ3,Ì¢Æeôñ†>°°`Ô”¬7ò̤p$ïb5ƒ€li<þfÒ#Søè ‚›¶æ)ýMYܱJd²`ê‘JÓEàǧüzÙQCœ]’[1öP”¤¾SZŽ%ûz)‚S°ªL~¦Í uOÓ”«”æ~É6¤1ƒ¾§m®Ø)L1o‰›WÐ<´jÁ~pôNÿ`BÔÙçÐAö:ä`;xÍ\Œ9ÖrB¹@ ~#ûª5N…€t³²Ì!³+ÅvZ¢ $uvµ
+>–¢9ÑZ”5l3Œâ
+pd)ÃO‚Å0t>ø¦$2AºµÎ¹ y¤ûœïùiišPC<iµJ¶zòD¨
+ûz*Z%ì$‹EÉç¯Áº±ó{}•üh“¾”ÝŒïƒ[4HõdþŒ{.\]þnØ4 ½›Ï•5ZSˆØ€Ã¢í›ÆBA¨NIj¹= jÄ¢ÓSÝ
+¶ ÑËñ€õSɃöýu*Ê— ™¬†P~½1:<;é•8^F ’J–½x©èUV/ÄxöËé_’_éÛå xá]˳SícX°…ñèê©€8ØÍ!›vÎÔŒD´Åöшº¬¾òíXÇ òD ¦¼SÞ#§PQ‡FÅD£m›¬v¡
+Ü7‘>ù æøW€
+H‰Œ—A²›7„Oà;èQ
+ü¹Ø¾2O>çoü™û0ˆ1Îð»:ÓïÐdô qCáòô¸|ÿö¿,•ŸNËeu`ò„}}Π¯|ñç1ŽTB
+E‘5bŠoÐ Ÿ1Q^³Øë,7æ(ù¶1‘wÎ8&jLÝñÂ?1©‹3KCÚÖãïÄdC¹™¨mükPöçÆuEléºIØd„ØÅu Ÿ<B7=Wµœ­ŽN$¿‰uálgd¥L•Ï4 ¿›v?S@¸U¯uè¢ ½z«R©@=bꆌ¦*oæ”Ê»yïg6{roƒAVëøTš¼Dk %‹>ô1„±„´ç/HK¦_áj¾J)ŽÉÊÓ|ƒâ÷ôo ¸ÖaZ&cÂäl‡ NÉàSMœ?vؼ!mõŒôb1Ø1B•B¹n@?
+´Zcú°›Ò¡_Ñö¦5Œý ­-Kæ¼ô`îcCfæIè?Z­‚K!é¥P¹&‚Ro:ú¥?’;aZPÙ<M{aÁb̽Ö]Ñq2ï}¾Já‰hµª6ŽN!0%@8ñšmªÕ–sæ‘_»É.CŠJÂœ/Ãœ’ïýU
+Ïã¦À1˜…4|~z52{e´~””©¦Z
+¢ÝÒ:º ¦ [¹D—'qX¬ép#å¶d0 9T&z3 UÈ{i=©B0”î(¹ ú^‡Êîiz«6Üx= KxŠ—é]¹žW©ß!e䌱äÄý~¯'Éñ÷H¢ì@užH
+ɇXœRIàü'Ž¶1…)ß^‡ÒdG= €ðHãB“Èëq„ÞøÿÒÒߪÿºx ŒRžiûD¦ŽÃF ?oA—4
+é×ÖB Þ>c{ ŒÊLŒÌB¼±#‡ :•€Á',,ßlôöÚxbÈ(DŽ|‹n ZF»DX-“Q£ªäØÓœÉù+v&[-ìymÈÌ!Â;ú´W)²éX©y[, ã@rP×)…ÌPÅÝÖyh¤Fct±uF¸$Mt¿]¿¾3ª»áùB._ç-µéÙpï8ž qˆÈ4_,Ηⅼ{ß ulNì<9¿@®RÁ#‚ÿ\´ú´ SM¨3r_¿?Sd7êð<©ý&ýÏ)äËN%Ä"²11ª}D<…‚PLt¯³ø u¼^¹+ºmÎ[ˆ^DÅäRÇ)õò¤C÷™
+°×Aàœ´¦ O© õxÜmXŸä)dœ!6yŠî’5z)®^6œÏ¢1¯,˜ÍñȈºN®bNî'9HADŒ¦®LQ;Wñ › -Ç)ErDqÖÛ\„¹˜ŠÐÈ+WquˆǺ?!„Ð\ä.¾Í!!Ï Ç•:ñ6Xù¦`ÌjË}nxèK¿xìæ_‡K õñÁ†“Çpö¯Xë~u½,1 zÖa»œ}ö5wŒÊŽrë0»’Ï;wÅpjÚ #}òYÆvOG½ö:x;3múëí”×@ŒÆ}¬¤bå •—‡žx›™DÆÁS*m“WzÅ| ¨CwÂ¥i,p_Ò?èŸ^{Y–yJ}•Q½4’c¥ ÔŽßñ-ÊÙ?@¾húwä¿mùŸÏì1ìÁ»úÍ“äç'Pjc’‚õ¸‡0X±v\Ï;HÎ ò>Ì[)ŽNr«¥rL$Ã?ô×2èù^•¸S‚·|(Ø¿ÂxϤ´lo Áðg@ü¹cÙIf =CKþ5èríÈhÙ®Äõãˆ>XŠ\*‚¼Cˆ¹ö¢ù† ú/½V¾iúÓ>­ƒ\å[ ³ùBºËI¼õc)zD´„èýä"a´Ÿ7@:GR›ÞÒ¾›ï®´¦W¥7¤F¯xiƧu¼e`&™5›Ÿ ¿ëÓ÷‡úªû$‹fødçE‹@Л•·~ñBù‡žæhŠÍXV²á3›n7·pr
+²-‚Ÿ-£ˆVGM´ŸœBÚZ”éµBÉbb°Í·9n›…)ÅË“ˆéFv@4+TcŽì>?ŽÊ|Ð0œé<y3ø"ßXJ9$\$ÙX[GR[z(GOžR(š“‘_I×<ÃÁBéjZÎPóæ8/›?¢ùC¾Eü4uÞ®„-Ì÷>âgÂ<^ˆâDZ¿L0‘ºqM7AôâCQƒ4ú|&ï÷gÔÉ ÎÉø°vcLxîa€ÑyªyË ·ã
+­FîŒs`DÍO2ÅLœ+zmWª”“L(òüP$Ì0@Wí¨á‹dA„J’lI©ŒÙ^g©r”¹pËÞ`4Þ¾„Ķ^ôzo&g4NHZ¹žImŽ4c†iž(Ý›%ZcÅ–*'쥺pbZé?µÕî<M%Z)žÛ$I9ê‹\ƒŒ¯mùO ‚z/G㪈¬ò¸´–A;2*ÃÏò
+Îg.,O¥@Éy3ꃾõòóuÄá ö…rç8
+&‰‘€Î©úYv‹’*|1‰uÞÈ·.Î"ÅšØ:rü¶;â»=4DOCbVâ¬PËT^;½€°#P;,­#
+»"m0³›ÕUtˆ/ºAb_ÒÑÊðú,úÇg=aPÊ”“XíòPÜ°)ØüéΩ„)Öðñ‘›Ïà“ûÝ€ÿzÇ¥ÿÄЋñ0ƒÂÊzÁêÉ{SÓ¶2Çïèhi¤T
+|\Eˆo‚š(×12žµ[ ª„(òðå]Lý¡× ôhÚÒÍfo‚
+ðõN•v1{eà—Æ¢ÞM1O —SõFúOèx¾:êu»w@§·ë5Ð
+‚óaÑAÛŒ*!‰3µ_£ÊÜì$ÀÅqMê‘-õ*CÚ…c‚py4¤R†­³^sRT+ñÀT%™ü™ Ád€3ƒÈˆO5εQ^¬‹Tfë-,JQgcpìN ]*Ri÷ÞœQ Ñò®qæøœ\¦x\JSy›¾ÃeUj¸’(4‘Xx”±ß
+cq"%†x7/»O&4ÜL蓹³ mʃIΧ ¦bð'Ö¬ÅËNú²ò’7…¦Ñ‚Ûg(YC³4n]_/8˜u¹nw¾šzêŒUöèš66îò÷í` ç”ÛâåO°Û©1¸Á5â Æ ¸;fxzF½,¥²îÄVê¹å¨>]ošxê !$%…îñƒôRd*.Æ·¢ÕóT¸ÝF‘†0’n v+Õ&dE3o,ƒO¤¤-¼\”—ZÚç½`Šø§;ùß O$æ$çõh™JÅ®­âá¹-uñ*ø ü•²fºÊ‡óá|c\,ÈPaÁæŶ"„‰–Fð飷ÍOg+ß"vô, j—»B€ ”mEÀ"B…Í8¥€0V2³Ö
+˜*Ùî– œž!"îat[Üv1Ò7¡öHsUgÒRª©ªÚð¦~ ¸Ê°¢FïFë”ÇÄͤæ³7ø{¯9ûQ 6æ‘¡CE£6?®£陶f7‡0»¢þšÝH‘xò@2ð[÷µ,ÜU/FAæ‡èjzñ”ÔðÙ w¾—A’h5¾½7ÍŠ}^ÙlíÄl°+Ì.ÊQR"4t½ÀÚ—¥®‡BËg–dT±°Y ¸âAÕ²E}Ï>(¾5“¨7cX*å;aü¼Í¡F“)Œi¯ÃTCéèÐÈ"> ñÆÂÎÐxçêÌûç?7 ’ÛŒM0srœ‘X±DÃ%øôuI¸Ubòf9«0ªÂóFõ@KÖ€QOôb|S(÷Œâºqõqœ(•„°m o—h~š¥Ø2rXfFØÓtŽ!éL6/™š¢ÊŒfß<TÆVo¾“š”¨¼¡1dV®•33;Åò(Ø;‹/ß"‹>»U:™ÄßÆŽˆ/’ë¹nÔÔj0öÅÕŒ~WDîÛ:4C·‰Ý Is”Yð{&œTšÛÀ#ñ
+ËÇo”†ªƒj„eà„X™ n«Æ 3"=Ì7ƾmÈ\6/è‹<ó;O…ñž²º` 34ÇŽ¶Œ+åG%Ø×i·0`Ø‚`Ô³Æ2ÐøúŠ°° £s»
+Ñ•² ¹Ò#½é'K¯pÉÃim'9|ÖÈÃß@1!)Ú ]Ö 4ìeö«²1¨¹¡2!‘¡Rƽ¶¢¤¸ÎŒà_@°tÅfåè›àƒâÍŠ£è…Ç/è6I,¢Ý k[±®)\ë`ºF¿,¦[ÑZÔúó‡Ëo"|zݯNZ]Ù®ºWÍ&&­ÎÙ¼„lÌh¾Ÿ4}oC@¹˜Ÿì[)ËT&œ3øV<²Q;ßê1vêÉåC>Ê—y¤f5jƒœÈ¯ ¶™‰¯ÐñazÓ“˜$ªz‹êqH£ˆ°A€z¼6‘)3e}øÈ=ÔóÌ6çjvp‰}¾ÏàV ÌàtÅÂÆ…ÊwH¡o„å˜&r\÷ÇŽæpdŠ ýàY]>™› ð؃Ah<yf̤­Â4­pëµÓ“6¹FŒ¤‚b¶e8ª²,Grˆb!ä6>9)ýC»Äò=sÂc§ºS)Z…×dþ'±×Šƒ¥ç…¹ä\,Aå?o«H&©rêð”Ax'% ÚüÆ(J RÙ¡éev…`˜¢ªÑlvQ2ˆAùåÇIÊŸˆÚw"… o$'|yO
+Ú
+‹ ¤Tô¦VלYÕ„År‘ÓéʆLïµÕ_­ÅO9­<tTXA{ê=Þ¤V¶ 9`4­<!’ÒV€Dš™† ÛÚ“èî ƒ5ø‚Ð.È <i»L:åÞçÒ TÑSL‹Lºù"ô >ëÜ6$`µd*í8_† ÉÕ²i’µå>͉ P”%…ªbÜ
+L«aÀkí{HË\—(›3¾¤ _ÂÅ*„Hv!T …Üi\$ròQb¼0…š’Ǫ‹ íeíÄT€„†áfÔ7¤k†4V± ¶Ÿã@‚üôÝ:«8O?1ÜÌD3'É èy@`7[…y@
+…6v7šä90Ù®3~†ÓgrÒÈ!Â"#4úíÍ1»axØð‡!oöy1dÀIE>°–nÚDnQò6¢!^Ã`ÃüF(ƒãÃYºÍ9ì -•dsÖ­uE'Œˆœ‡Ñ)…
+2¦€3£•
+¡I[s±u(ˆ:dØg}ÍöƦðÓ ýj÷6èÁÀb ;†=€ç0
+í¤ókh¨<W /ßÄXp˜è|}÷{g4]ä™}¨þ”g{QvDò`w›ù9&Àâ `­ywÆ<)YNií4”$Ž%£Ž|2v2…°êÕý bJðZ½Š~/«ðQr¤ýqÌtãL;FÝ¿buw oËjAš=]ƒi 4›¶’1Úå¿*òÕ¸X§ =©¡&ÑŽI
+L„¥˜dÍ’?ŽE˜¶*ie}AË—ï—"ˆK&ëÍ”ÀX({ÏÉ¿‡FÀÔŒd%m
+üªÎûcƒn›½gÒ'Ž+~•d¬ó &xà®36+ÙW5Eʾr¡¡6A¦C~â,Ãë —ajY7{qCzÙ¦Z‚-Cűþ#›/%8¤E $_² §¢Çzþ£¢A7H1ô&ÍôÇE´9ãœpÂÁÚáSÑ#ÝžJð78‚z·’OžŠ
+oÊ…9à# 
+›¡ ãTôýV™B;¬v3À”ð0 Uh¬·Œd&‘j߉"EEô‹ñrDY—åºKIŽ;ÒÌŸWÑ*òbËã0õ]Òq»Xb¼HÕoj@±„‹×E^&½¯¨Õåù+1L„§/U;%’v}–œCÉ¥Šl– ΞòΓJ4ÒÖ`¬_ˆÇæEûØs$؉ =cÁxïf_íësÔ•b~³<ž¿'— !£YµƒÈ~{ Ò®DÅôÇa×ÈŽìH—€å×E¬á 'ÛºÅ
+|t虶[á:Mô¢èɨ‡MxÉ+ïcB¬J|}ºJÂcÉ-Ÿ-ë‡Å9YßðU÷v,v¼ƒ=(¹):¾,Õþ~Ujⶡ¨« 20nkå¸6"( çœ/;J[@
+ TÁ¯Ù·™ŽØû¶ošQVth¼€ªHa ³AÐ¥šØÐlŸ…ÓÃ=ͺñ,sƒú@>/P ½à§ãä—Ö h°ÖûËWÃõ,„:MÐTÉà×ûE\'óæ QÁlì*Ý‹9ú6N–B0G¢ÝkêÏc@s~(Å©ö1PTM ¸»Ç!©á¿à­:ì1…_'Æ-'ò™uì&ÄMÉ°{€ÌÈ!ï´ñP—ù¾Ä3&ÓJ—ÏIõŠQ´?Ð&wõë9käÔdŸ&†qfc{u¬9«
+Nñ¿ãðáë*Š”³H·ÉÍf”õEÔŽ}N¬@½ËcY“ ¡“•€,kœ‘žº "ŽRqÛ<àÀ¸Ójë>»O;É·^RåÛ˜9+b4@÷•
+‚¢%ã»Ø=€L
+\ŠªÜBœT»ôP”h<ƒ<Bœ¼gi$'z q€‰!Ä…º[†£¦‚¶Î;g¸…I`+³â$ùÈpqÏœ€ÓéäBKŸÇ¬`J*þÙoùlÓ¨2gCø‡íæÿà<§’–sí;!B9®Ô‰½ÖO6,!)+¼nzV¨}HÜ_Ù`ÿ(zàØTæ¡Á`ÿï¯øåo¹‹F÷*.˜šôcU¨¨ ê8w’¼&½Æˆä|+%¸á4NêÔ¤†ù’;Kã2JÇTJ ¨ª$x/Jz<Eämxx{1çOìÀòU)Ž ØtÛ¿Bôb(˜+/aC'[¢ MÚSSâ3¢}E{;ex,Æ’,d¼‚ÜÂ>ÄM9™¶%à S…vzöÉbK<}÷…LEß/E2)4xh¾ìÖÏ#õµs†d3Ø8;”h‰¢¬„¹ÉÖ ®´B÷vúŠÍs¶”e2–Z%HG/!µ9ð°$ÙpV¢yJ›Yèv^7კ¾ÃÁÆâá4 tòá«j>n;%¸E™æPÝY½™8CÎú½\p·ç
+1ZÓWFµo¢f¢q¨^+¦Ž㢠zÒ>&)a±ós˜6Š”EVº”LA1w¾½íp,"qNMYZ_Š‘H0b=}?eÅYb Û‡ë;jq–ãJ€B´WH†¬Dy—ËØpÖ3ý\ ,g¡àÝun…¼ªo?ÁÈ­ Yùƒ”‚&_š^ä¼ /{0[NK"! 7‡277ôšvñ„*‡¶O³°€V&í‚)®ÿ3^&ÉqäJ=î ЀÀ¼ÖV·Ð–ºÿ¶Ÿ$«
+Ù*ûÖßDÑ$bð¡"ŽÝw†i•ˆÒ8¤„¨|d_ËI‡ÒÀ5Në¬4Žç×·ƒ+l½#³lRçR¾’/É·“• Rkȥäu÷ç VH¾¹š7Š,³ ÐçZzÀ‘´MêcîÖ'ÖËÆ œ ×{m`Ô¨ÈɬŽm‡:"/ëµÑæ²P ŠÅ#<ÉÏ1ùOrª8é xLbÖ`ä5¢€Ø;>GæßÜøIv bö½â§FÿÕöOòý], yÈ8´ZÊnÕ|°Ø5_d‘`¹ç5ÇYf,W8nHœtôµŒEõs –åôú.¨ EæßPd· PËhˆ2öñbÀ.!—ì7ñepEeܽƙí3†¸«@«%‚‘çwoä(¯Ê¦¹á#äoä2/¯øØr@òÚ¢ÑÜ+vò UGIwnêÿ]™ ÜÔò W¶|S) •[“«§yKb‹yØv¦2cjÍü ÚGcÄo;ð²Þ
+FIüµ\+AÛ·‘~vÉðË(D
+>v»¦*«BÖ‚“);Ô0A.0Iƒ± pošö<į‚%òœ€œîÎÁó³æ< ÛùQø'Zа‘ázÔ¾áÌàÈu•aHµ4I
+zN¨
+T†êá×l§lé“W¤@e¨‡ŽºS4 C+ö5?'q,RJÆJþÉ U$çû¾¨+À`˜ô#\;ÑÕÌ.’5ìz”)Nemµç=¦šqÇ›Ã#ëØ„Bˆ†’Ë•Ð~²åå¡-ÛÀZÀ­qqÎ ˆ}€€˜&ö6›Ú‰Ù(rj˜ó½|o€Tž.…V ÏŸ&í†L—âƒ`9(#É«²å‚ÈzÃ^c¾¤±èâ¢UB0âhüF¼Vïi­pYˆ‰ÌÝÅÁ®0_
+7 ‰Ó†Ö÷æ=ä4¿i6KTÚÜše¤)èǺX#QT=ìøÃJÍõ×å“ mŸã$§TzÚuÆb¤ªït£ˆG‹ÁÖ9ž‘þ* OCʽ^†™_Œƒµ¬x<^Á¹>ˆ®pÙßï O1¥l'ýõ“dm3»øF˜žíâÁãlüpQV»¢Ÿbª††–½Qc€H¥]å‡ú,ˆÄ;hÛÝù'~J¼¡§è{V‡T×w !”àZ”âÚµçQã¤ÔµG~n…ä‹/"N¨ë¤ÝÐçäd«Ê¼Äö”¢C‰iÒ´çë*VÃà]gæÏdQ/`œ6•ðW÷˜ª âSó5G¸• Ö7“2#<«¨ {Í|LŠ¾è
+á[Ã~”øP›F=Ö9˜$]Œ¶ù¤–=×sÅFèµ~ÿrK¿§©Ê SŒièÌïb8’moªÌý4U/ 2h2é WL«ãT¬<0$4ŸxT“ÚMñ
+GQ¨Ðå4´íý}PYF˜‰o|ùÍuo‚JðùNV9g1mÌ{3;ýß#H³Ž¹dÞ•Fæ…'PTBëÌNŸþHƒè‘W왇)þ+ –¢9“…—&\ºižslxqqˆÏcxSÞ«@A´d3“ ŠX“€ûMˆ1·ó¶>çRŽv“šöÇhá©
+.ϯÒCv€²Îøý
+•p¦Rõ±åb¾–îMÙ~=-çdIy’óÀ÷·Ašã¡–ŠSÆÔ凘dŠå "?š"~F8B vF€¼ˆªï—ìx`‘jŒ "!)˜.«3(M€ÂzЋCúÌ*ðYé7¹¤Û01öuчÔÔ1nÎáÄ&±Ý^E, 1L"ÿ]W%KønY>b’öŠµÖÏÁø°$l°?ݦ¶xC¥™È÷°µcèhZ9€>ÄØxèôÍe@xA0ƒ†Ûù“ÁÉátGIwO?€žKx‚<5â
+sÜ¿6]IËBT8xyC»d`íšo?Cx94çÓžÑA*âU&Uj¯HXþtZ›bG»o¨1ÄÐßsšÂ%X˜÷.KS½.Á-2–“O§z¦Ü™¯ZWØ©
+÷[p¸¼½¯1…{y'_ç4 ÈÜ‚é„©2¬Ê Dé‹‘DÚÚÒWÐkÿöv2 -oƒ ŠžFÖÎÅ92é…¤@ºó÷aØ[ñÏŽÁ§%0Ђ`Ðs™ৠƒ‰ÿÙ5`ˆ T'é«Ž0¨ŠaÓ0
+RLÙ¢±!‚A•S؃sY0«]˜WäT¡°Ú¾
+…ÎaºêCØAàûŒô/F "tmʽòîév€ˆêIHyp¶¨Â³ ×E7(ôÙ|¯XjˆºÚ7˜
+S@ŒºCø%• ŒaY” é…A<@6Y”\å~p±wçðåM:Ò÷b±Up ${s^Ò$Ãï6ö)zûµ"bWD¶tÆK£#q¡µ?†úCZT0ùMJ"˜*Žîæ$I¨ ›üŸßD"½Î1º;×âd
+ÂJ—4¼J½›ôЙTjT&ÊÎ{­÷žð=_{…ÜÁ\&§°¯bÐ$®ñAÇVdʇôOž ‡˜|[Šx,)®ÔÓïù€.FÏÒ…_?^§¥¬U +Ânù]P.ˆJÐ=Üõo„øœãðÝ4fŸ'³‚Œð^Ò ¿8±’E¦Ç!–Ôi|OŸ.褩I%-ȇ]ÅAÛp®é…g:ð¨ùí7ª†3eGyÂíM"=C"½ý(œ=šÅØoQà÷‰V².{ —ÁZç­ðˆJÅÑòuÕ3èyùO/
+¹|w$woz
+±r@P”o»D6
+•àâ¤Ç®)‡îs$z´ƒG•€9r„EANü*ŠåÛ{®ßÄù¬
+øD~yÿ+F¾´ˆ'M!‚n³(*Wè°§<X¿ kâÁÝA(Ÿ^ PÍïjŠY5ï1BCë'æQq^㇟!°'ç ¾Ž–ÙùYŠ2Ü<¿9 Œìj—»§ã˜Ð=èÕ+h¤à´¸¨ƒmTqÊ”·ƒ)úð¹á ifðd jŠ†·aNÀx”tSÉŸƒŠ]XE¶oÉ·î"«0
+D<æD ‚=ô—œÓ†úø<¶g¬„/+UœâoCsfÜ3PNI[:ÃïÑ A´ïߘ! Û˜¿ˆ@uzŸC ¾¨a嚸â¾ê³ejÝäï³Ï‰VMmeʾº¬QVA¡ê˳/GcB—ëµ{F@»¦]^‡
+>ÐüœÂ†1©úƒôÁ¼±Ü¨¸Èúʧ,Xﲘ]ìêE<kê£oá4Œ-okyq¯6‘
+)ÎQ¥>JB¦VÓ€éQy;óÆD9løÃiÎB]š|‘ósÃü°°qLŒVoÇ‹9ãs¦Üg… ´çu4×ÏÙ8PjþÁ¥"ªbL‘Ûø¶Å‰Üç•„ÂT—¨ïÄ€ë,ÈÌJ~n|ê/p«‡
+€6›|ÇŒ
+û0QJúÞîsLæYOw¬Ë>v¡)/1h©ÕèëœÊ>Jn'Šó›y?Ÿƒ§19IÍ8cË.Ç€Õ8¾´]d‹ÄÚºãï²ñp«´Ô±;`ØŠ
+¡gOÉK¹ö—’…4Á¾PC½¯ú(’>°‹•®s`0 Á6[Ï»d'MÚéBdÒ/RØ«.hˆ6ìrÂù~¤ßÊ ƒ‚Z‘ä
+>‚Øêt¿
+äSäàÏù6}A´ð³âl@ÉçažhÎ8–@Ȩd £_¶+{}¬æ?*R,é°dÚÚÿ]$/M6B<¿Aˆøuicºzx(Y Ð.YÞ‰çQ‚:ä·.¸´ù¸(Ê•ËѾd»(“]¸´?‡•‚%û^ÂÈnC:òR"€$¨¾•ûªÏ"¢)u&«º^Î1ÙðÇëUUº ÕÑÁù}DÖµ&k]{%§•¶S”ÏÛYË7¼Ä¤þpH]ý­ÁÂGÒ×<ohÀ&Õðñ=?Š~{뇚µ—Ë(éR,,h âó“)Aßon1ÐçÓEŸ-<•| âPòè¡¿I†È0ýò=î ù„·ýá¸æÂjqÖŠWákp Î_ ”¶}q«ß7ý¨ì}—jyN¡ƒˆ=.HíKż2N »‡IXÀ¶v6åPäÃÄET©yRáMDÊ:ª\Sq2EÑÏT£Á(ŽVªÂÊNÛÜ‚ë“ó>R²”ŠÜþZ[ª/“¸ÒÏ¥`Vaé¢RéfïŠ=VáG…ßt‘?×”•ßŽÉÈ TýM 8T¡ØE1)‘,œ™Å÷SRäÑ~ÓüZ$^„_“ˆ‡OðY¯mýû*á=ŠLP´#B7 £«oõz|Ò0üIâ5ø¢óè@’.j<´Ã$¶¹îcÚÐà@¬¹àÛ%¥P_^b¸þ©†“Bq@ÿÖ㢖&ãFz‚‰»’k—'þ"¹&— §NhU±´ØKÉŸ~ì×_§š9{¿‡
+a6ÿþJñÑmÊÍNwxòѸÒ40ŠèÜ¿Ç"$T¹ÒkƒP~|àQð€Óûˆ(gPGú´¶K
+ñú ¸ÏÁÀ³’Ô~¤•€vLêè Í ŠU3¨qT•!ËæˆÇÁè*’e-ûœÕD(t¿Ÿ”Z`Ã*‚C¾¸ÛxsŠ2»ÆUBßÛ9Ju+ÝJœ²0cø¢Q½Dõìùí?+56ɯ¼‰V{ÌG–n7ÞYlpÑN{®ÎU@ðÙãòø{Pll@ø=.âÁòªõfdyˆÊ(sŸÓ}Ñ&e_@Ñf¡ùåe“ÜVá«ø*þ\{–¹E¶Îý·ó5 ÊÑ_§¼H,C 4ºÙŸÄ!Ç*éOâjI’ÅUž
+' OFäk؉©€ÖdÍVÈÐâÔ÷ÖÄ@ÜÎÌT¤Íƒ°ÙöÃSFÃU‘ÈŽN´¾(h8ìåÓ(h,G^ƒŽdóÑ^ÕÒNEqáDEº'Õ,`€!…ä}ÏI">œ/àÚ
+–ˆG]‹„þÂ7;SA©'ñ¹—ØHLC‰î…³hËš5ÒiÞOÜ— Gu®uˆáÉ}MúÃ4ôMHotÝ"òI_oB^ýòOòç WrrЉMþ\
+d×è=Äx9³GìY€ŒZ³äØ7èF÷™#èçÝe+Ž ‹ÂKÒm#NŸx虪RØÉpþt)­ð]jÍÇBƆtaãiÛ]ȨXlúÁiß©.AxåI¦ÁÒÝ9Ì=I5òm-ÃÄå„ lÞ@~‡ /—XÔŠN€¯2,?0y¦%ÁáJ ÆÇêï*¬ˆA —íáy•X
+Ø`Kpaê¤ÉÉPÛže䆢ù˜Ê3«íÅv˳ÉLaý2Jƒ†µ±E D… ò–=¨'âüx*
+åAú„SÍæz
+C!ä;±]Žvê›ä·³9Ÿ´.{»&™1nâžrÙlø Úçà¼eБG?‡¼<HöW„`Ó‡æ„zQÏKÈV>Þ\ ²›þ~NïZ!sìÏ+kw Ú;²?ûÝÄXi‡pWØÈP¤áäÓ}ÖWÄMôF0 ÒaÌÍ|UÇ7`“QÈ6üỄ6 ä¶¡?DxânÈú
+bÀ¼CØ_!l³©I5ýD†)‚œË2“‡79‹Á``"6µö–>N—É¢DÔÈI½OÂÅ€rà T{–ïµ6ò¶L„£?çRayRñcœswìÔe5òT—†/[.{NYC%Øà&‘‹î% ›i˜Ó±ÑÅЙ•Ê75Œ%\Üæ²ä8ï`/vPöÖh²rÈPߎ´vnŒªŒžÎëi›Ù”¿Þm¹r:C6@ãîLŸYñµÈ)9
+åË3Z»]‡ Äïh&fÙ—C/ÂζŽ´ç›þû'fý¾>©± Ã>êe{ú³b²h›ÃVsúutÉ·²!•’"Ö¡» d„
+†’•PP˜Þß
+î¨P^Ç$QŽaMv£- ê
+#ˆ6‡ÆÊŒ<Hs‰k¨c´]AЂõ±Sõˆ•â‡
+×@6ÙÁ…áÆñÛÝέið®6œ;1lP¦ÙìÆWî8Ô26À-Mƒ"à+öÂH•˜·{ à¼3 ˆõÇ’;3\œ—í`L»&Š
+í<½MŸ0 j8—ã?é 7à†¿°ôÎÄm “I6g{úIUŠý˜ˆA×Tƺ…ŘCÕc¹ ‘D{ñz¾7ý—­póÁ7E ¦^¸Ò#ºð²¡]\èÏ)¨fâå™+,U•§¥]€»ÿ4æšìë'7ò‹_z•¡ym–›AÃy³÷˜_ǘË|Í ô¾¡!Øñ4„ç C¶··ý$ædaŒß˜L?y7U×*^b¾þ»Ò³ÿ(0hgéègLÈ¥ìŽ&å(oóç&¦ªbì ´a@x0s }û¬¬
+ö†¢ÓŠ@YÙta´…‚B –s£’®“Jëi3²QuP·Âz”…q)—,-Ë'¼Ú¢OœÊ@c«È ˜sŠfn:ñ5X¦Fñ C¼MFe„mße’¡%ç*Û&µ±yˆ| Ï
+C‰cb>xÞkCFµBGç1†3 }ï(~³6™1„h†:M%¤qY©%}/íæÄ8è_z{XÚ‚Ù…äÑöÒ†æàÑ
+pªÕSikÌ“v,ê‘M Ï¥õ‡!O3ò^"ñ±ö‡
+g
+Bù°Jˆ¿7½áÃ"“å©Pk ’jrhá>(@I!’õD_€N_!`!‡|‘B»3|‹îE‡ŸI·±IˆôÊÖh
+fΖ»J
+¶mY3&*(œ æz°¹'
+ínÐë£iÙé™.ÎIA¨(cOã$ ¢Fu‘“ÍwCȪM\!à È…²)õR2û&7éæ™
+Ü¡,óL„DeƲûËù/¿h‹hëQ¬0¸fP¸è¿>´¨ÑTšc+41´}A„å°/À™¶|ÎFi€÷ö: `¾ÖP­†1˜”Á)Øv5í“´J
+¯ã›,/|t‡ÌдŸ#(í„É5cDMÃ×ñ¹|r3úT·÷¸¤BkÕšâcŒlK=´)=ºCâ–:㌌6Πª#HG‹A0Âñn–\m.V 7Ý©>x Õ]õ!`M»Œ уhüŒT㘷‹
+¼@-å!Òä<:_~$î-„š¨Ä¨Wò÷Tê•·IÍç¼.£—÷eô$¿­ÐD“ëzk¥šú•‡PÝ+^Õ”etfnÒ¸¥>W½YÄ(Ž;zîß‘üñÅ^õôëŠÑ<‹jƒ—Ä娧˜ Í™©D‚ƒ™VÍù¸ÒmJä’ÉLM‡™ÆÊ¡‰‰cƒƒR¦ÛÊît؈’á›(¶dGÁÈ#Jh=«Úo~Ï«¼‚{ @vќϗLyÁDŸ“‡ ÊÔ|z"Xª—cš/è ‹G^.5ÜÐ/°&@O¸ô‘ÙÌ8ëΟÝ|¡
+VzV—·š/h*‰ íOÙ™7^áF†É‚sÂV dàu°:5«Í¿e¯`Úa[ ŒlZ˜€ÎIq“QTZ†æšqNfVøç v#Ô¤~Pl:P±Yqé³\C.T{ jØ}P˜*OÍëLjÛP]-Ô¯ø½žÉUéÇV³6˜±6aÅvAIPs²Uý
+¨¥ÞÜy6/L‹³ó†Ø»Õ†Ôb‚‰I7+Zœ‚CNÜœé¢Ø½xúOÑɳ3ÍÀYZÀb×ÉÂe©£u¯¦PUwQ@ ²¤˜W1c+ |ˆZ¹ÏƒÐ‚®Ý°ãônÎd2 ñS¼«d™GUÌÔÑLÇì—¿‚êhi‰e?ÛÙÛ9¼1•C­!Meƒ0wgäK¥Â÷ÏU(H2»^·‚H±¼Õ†Ï?­¯Åu1ŽQYó‚ÍIX»ŠÑ[þÜœñf´¹Fj€è°i€Bïù/#…;½lÚ¼{3Õ—Ðü6¤_Ç›Åg4…•¢HÐ<=@6ÂxAE:†¥3:‹0OËW£?«ˆ„Þ5…— _Ÿ‚ @Â’6›{kÐöÙÙ‡Ôf¨0ìý_§›<g9}£]íM9[,;Y6E=‘A~?‘»=B
+v¬òÍí¹
+ôaï²àYŠÅçiQ[Æ9|¾6¶~ê€ïh`Ñ!ˆ–É››ÃsÕÕ”Ù¹Ê@\`­ª§Z<AP„Dݺèí!5ñ™d|%¨øe|wAõRzŸñ æ} úA™fböʈѳ޴ʂ„u§î!@ßvÏ! ¢.óéR&bLF,_‚8WÂÊøpsO˜}§ÌK]=ôiÀB~^þ5ûíU@@çö¼ü5hP/1dr ãL Þë
+œÐ¶  þ^=„•¢‹~è²@g~,¸Úy8L j°ÏF œ/ªÁ—û9@y# 0O,úbDrUôšã:œÝ((hý»3 Tý
+d LGzY*t݈h¤[ž‘ýQ {ÍUæÄG#ó–S™LDtI ¡ÌÄM4¶4°‹†2ðlÙ­¹ŽjP3v—W¼z!(Ä ‘.µ‚&|!£/·Ÿßþ÷;ÿ´çÛ+ õîÞ§E ͬ$(æïA *ÅÀdg•dÍ–¦ Z(ÝCø ~ ÍMÝC€Ì5Ëy;Éâ·X ¡k©8‡ÎAüJ7ÌcìSJÃ;e¯;[,J@‡Ñ¨;
+Ék{ •¥NôE«QKÈ ï^½Ëžrz±à²Ží;
+.@%üž#êÚ²<„ÅTk@mæ:H§Ó|ô’V
+«4ájCž†g[ÔäÍ“–Õ$œ•‘^„†)&‰W¯!¤Qdkèù†à[€f\ÃÍCRôçµ=þS×Ñà  ‚EEí ›Ú³èýù¼ŒùšÚÚh!—¬:¥Ÿ 5$yÉfC{ö7Cvï¼à«  8æþëÓ*Da,(Ç>¦jïÒZš¢è„‚
+@fˆ¢Öæ Ý%g¡¦Dåq«ŽQE-šGØ©æàÅûÜRÿB êiR–‡FAy‡GÈ­Ð¥=G¯Ó)iÛwÙOš#Å%È„Å@tmOµÈÖ¿+™—1a5|XTÚ¢²+EH’ F†^}xç®O¹QÖbˆ_ |]N²KI‚Æ„WµaÀ$ퟥ‚äÇ ¥Lôö``¡c¥=#%¯6Ž…ÅQ
+³#Ìl‘¾)Ø>úR™ñè¸Ýð«øXf¬o ŽÜˆ¼ô}VçÕ Áüµ›pÖ±,
+«µä‚s
+ ßxíÈÛÈþQœÉ1œRóy•U>¯ò†vŸ³u¶$º²ÆÒÃ&)3‚ór„$i*é¨vÐ宣±áó*6 U¤fÁ)
+¸Æ
+º0¡‹•z.;"Cïßçâ=t¹û£e*P©¨Kàã w¨;l<D¾õ*ÙuMÖP)KÊÚ4_½¡|?>_(Þ£,QAÉp {:ò57õ:"®Ì°ÂÑßGe°iP|áo#
+—OZÅ8¸ø‘¯PJÚv2|
+w¸µ?£Ø1…y”¥h€’æÊ "„Š†…¸¯Âgc&[Ö¹6`|1êæ–¼¯bý¥•ºlmp]Œ v±¹h±gEÞ¿5ßO Mr³Ái²Q¾5œv‚˜ãUuöGí+!/ çŠò]Ø2ŸÄ•[Ë©[’ëãi®ŽY†lø$3ëmŠ«l¶45÷F¬ÉÛ•û\ЉwLsk¡8›u;nH™½Ú¿êW%8F“# øÀ¹¼vËvÎ5ç’eО^@”)ôȶvæï·¼…÷ßþzûñÏ[|ÿý6¶&N¹2Ó÷õŽ#‡¦XØ*§úu ].eC(J~7D†Ï„P¢A2²Ã3Ö&H§S…k9"Z…ß
+øc¶Ãy_GP»õ &Ž²"ß‚©vPxtV?ùaòÕC 41â[¼ÖªU‚IúýÓ‰•9+ 8¨2ýxc„0y?äà™Ä±L!Ï5>7þ¿€=aMZPhF;¢¥ï¸Ý’ïGD Õ|³¬d<oßäÄáy
+f7Måwm
+ƒÜ‘‘+ÌäE©º” ¢4K Ôï ‡›3ìÚ”Eç‘D0Š-ñôÐöÏ#ˆR †DühµËRˆÐü(P„ õ†Éø¦äÕ™h
+™|@=<œæà~¯{}~gG}•Û"Wö} ”kû-D|eg<Z>dN
+C^šlž» Em5wc ëšä Ú´E¦ÊKhÅ
+ÜÆË41VŒl]%{ÐJÖl튛bÄDŒ±sÒTŒí-ï©ŽÆѤv„`Ö%J ô÷ãí zÈBŸGÞžÏÃÉc‡–Û¿~2¤"~! ÀyÇ—? ‰¿ü©‚è^eõà©¿Ÿúð,Y—n^u™Û1êlÇQ÷ÖºpOæ«üK ChKÆÑdz©wá´a°€!Ó»hÈ—ñjÛöþQ®«ÒÖ§ îß;äqåžïãí´¼YˆRâHHNNÚ":¬Éž%ÏÑõ‘sóºr%û…·jGJ:Èâ*1ŒŠw¯q‡´STV;«ù‹[µý3ñ‘¡Ÿ{ù€ðÍdZ9tB óÕ1ô(°>¨yÝ}`¤Bâ›»OM¬£É²Þµ>QÑŽ¨EXi_ŃJEÊ÷Ã) ;ˆ…s
+ìUdƒ¬­GëO4 É›­p¤‡ªxAw j* †/h&üá‹ð91ù9ü³jh! ¿ê© ÏZ÷K¹ü0IM˜&qDvüË@p<S3GÞÆž€J¯˜¾–,†’-ã*œ—O„ÁŒÀµï Á±¥Êãg]H Ã߬ÿ_ã±*SnAÖ@Q(œO!=À”¬P«ãl¡ÑöbÑJÙËûd¡ÿ`
+H‰ì”ÙoÇ€ß è[¤-¶E•øˆmI¶ËFbÉ’ê¾Cݤxßäry‹¢d;Žë&uÝ$@G¶E'ïs)êŠÝʲ¤ˆ¢xì.—”íý?ҡܤiŸ
+äeÌG,æàÌî|¿ùÍ|÷ÝOƒ÷çC $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6x?Z±Ÿ 'Š‹ëÕÂ:>Á/â¾[T\Ã)íN¾žáE­EÅ#çkp¢N& d5·`Õ «ä\ÙEì|§ˆ¯ÄN¾‰AX.“ÈÔ ³KÀWŠNao¡—ÁSÔSxoUá½µ¸A/mç`ŠúUg­Ìù¯î“=j5_%bǽèÆÊÊÊO•`5àáš
+Q‘¥XÉñkFP‘ƒ.VZ‚µ`C%˜°0¾³èleå¥r¬²ªªS+ÎUV¼j7ÿ¸}á¸lþaüÿ¶Ç«ÿóyPè~XEiaÇák)EÕ¯ªÇrÿŽåÉS…1׋Î׉Œ2èZK_Æm-À·óؤPùÑ‚*/œ»PYYZ ae%¥À«¢ +ýçþþ~Þ÷ev,£´êxÕ±ãw/´ÕXUyYYe—ó‹”O?@mÊ´Ç&K8‰Þø’®gß­éK5}¹˜²—ˆ8I§øzÚ¯êH‡ñ!:jÑzuý; ’šø²‚s°¤i¥|ÆÑlТ 6uÚcâ§}Ä@Ò¯ïK¬ŠEcß³YuÛ“/Äõ;Óâºä’´.½,­§½2NÊſ̲̄Tàƒ»Éà{‰à„5¾lÚŸSs¾ýZX˜½”Z¾“YT7ÑNóÓ=~ lÜ~¤mÏôCq§àêî¯b{ºÿ|Ê)nÊF•=i§øåSu1B˜%­ZʯéI»•ÍI·²ñÙ#A%íÒr_ÆÆÇÙ°A”ñꇨ€EÍƬúL˜àï-Ê붦‡K÷gÅWç5­ŒßªJ»M¼ä’Mº7m¦]“¦Ä<ð™”íLqßxúYÛof†Ê·®'뵈٠Eþtf¤üÉ'½ÅÛŸžÝþrðÂÖƒ®7óüjàÐÁú¬Ê¤KÓžrãïÑ“8±™¨c"á×ÅvùÞ¢qp^7˜LL€õŒR!57Odà‘²!ƒ ãÕt¥ÜŠæ”ï=piZÒ>]Ruÿm¦ûôö,·Œ ):ŽÂ&Ù‹˜Õ˜Úìɳ¯%µ»sòF*8Fл!îQ4í;¥õI#6fPæ6,ÆLÄ$Ýu)Ú7¾ì?OûM¢T€x•í¹u‹åèÇ]vÕ¨ÌÆ1%$ìŠ^öKßÍx¤ LPÖDäm/×À÷bfM6JhR;œ!uÇ~EK* êÈÆôB*¬îaI£<¿b·1aØ=Ÿ¥LĨÜ÷È÷\â†LÉ®à£/Vͺl:î¾ß=Z·;²kãŽDÀ,ß÷ü¸,äEÚÇ ×è•›åVo–‹NÜÈFlú,i”å×ôbšTvS¤¦Ÿ‰âüüšEŸsRnM'4ɘ•›bbV<¿n5mÚ'™.È„¥m4)íJ‡$M‡niýÞ¢´ÕõÇŽŠØ}QÝ¡÷† 놜¢úgEWöç” LÄrÍ>F¯ê… ©êÊx%Œ_ÙóbìÕšOx5Ûsâ+Û ²ºimÍŠßN,é:ȑИŽ
+ü¤[Rè×Å=†ÁÇ3D žœ Ã““pþ–F*ÒÞål@TÏø…u¿ªýÀ£îLx‰áLÄaIyMü½¯$W¶><µ7Í+?\âW3.esÎkMûñ™ñk'¢<΢€
+G–GªŸ>ä¼¾7ÛñrØ0Áˆ¬r:bUÒa›škʆm&l‘Q—òÉs¤n%ñá|Ìf|¾i³3ärH›ôêûËún&`¡AþÅ„W¶¦:ŠWþÚþ{çxûkñG6yÆåÐÄçå×v§Ï<ù¸õõg_ôž¦âZÊ-ãz•-sï×þÒw«ñ·ô2ÁÏ‘6<¾È»´ùéõ_m=hýã×2^I]Ü5YÒ¦¥#&I&lÐ!›&GŽ[^l8nçb&1ËA.hòë“wò뎛Lœg¿¶'åÒvRNmW.„÷ç"n>ªáæÂꮌKÏeƒ6TtR)'O‚\E¹³’%-*6:f`c“·¨°M—ðšx)ߘœÛ‰ìšÍ̬›4ÔŠYÅ‚</œ*¦^Ÿ8÷xzøB~Ãñ!ûxâCvcÌžßt|pôÍû¹U›™àÃlXÙNEµù˜¸9¿&íJ†‡ß>ðó®æ׬š4I)pç¤ên&ªì̯*ºó1UoÚ/iØ[à]Êø ¢#òΧÏWï<`V ’„_Þü­[ôξWTCMâ›7þή…]7È6nÜcWïLѱ÷ï’&y:,oMåÍÉ°ž—ߘüˆ]5(A Ź˜c2uŒe£VíÑ.üçß x~g£&9Òpi—è*å7ðÓ¤c<±â9Ûܦã6ÕñwûÏí,¼wzw±ï­CŸŒ³>ÕW}KxæÄWD[qnåž{AT³ñIû¯Ÿ|Ü_œ\Tpò1‹.·a52+úÑlÔ({g~3­Z´Y’ìÎ*jvçU ¿ad?¤X¸ÕøÚöCyC.hÇŸG''“!ußÓG]Ø™î}óÐC ‚ûQÊÄãÉIº3/1
+èhéØÕ–mÓˆ,²„$dß!@‹lÙï–{s3ý‡ÌanÝÇ{ëœïû}ß)•bCÔt¼Â¿úËlßÅ£•‘æ¼ß¢Ø[’5§^óÿ¾ÿ“´.· g×÷0—¤9¿*ªß?xÅ3Ýñ»³ÂÚ‚Ç0”q«.ö]>šã\F×w©¨’‡{•Ý8x…´'§Â§J¡ñ1ÜoU`>m/á—u•cA1䎜……Äm¨W;@øm†œÇ8”þ`äà³ðh¸‡ }—b/n}œºýyx’u¾0oe–õ܃9ÙÍ£Ù‘ºÌ¬¤_“4¾g_Œ½º{nëGÎWÞç]_$^õ^%½FEظ(¨ùø–{%³4p‹òKZ)ßÐ 2 iÇHoò ´4ÄÄÇƘ˜Õ\Iš DPÁÂÀ7dÔ¢cδ’Cš>§íg‚fä?!£ô$"ë®Ä„wéèPSf‰} ]F8”ßaϺÄw³+üêrÔ 8IZÌ¿îŒNÐ1£
+ è…XÀªA¡/ £ó[d¼‰°Ój§¶ÆS›c*nÖq½ìÓš¸ér<2E§Æ&áyÄìŒ?+§lÓtÜñ¼§$Šî¼‹_ûù5¨àFÚÍù&íæUƒ.ÅyØÒ‹ð€šGÅ=dXÔB…ÅÀèþî½g](8•Ürxò*ê˜*úõÃ9Â-l(ØXéÇ|º:a%ãz\fRoèäã·XdÜ^¤DDÒFEF:Ê d¨GDXHØrä–´»4}ŸVûÄS&㯻j)T²QŸ–W+8•°ðüÌy Ø‹ˆŠO¼Ä£V3d^Ö#iCr.”vç|Ò{ûú«£/Ûþòñµ¨‘Ž>[=šWµþ23ômzItƒª8t@ÖM‡U½d
+xýdSÉGýÃ9×@uÁ#j@½Ò6Ø^Ø@ØY§–}¼¬ºŸ^’ƒ/ewÒKƒ×g{.¦ç†®àN5ëÓ;~ÕÞÏ«™EEÓ§wòúÜœ¦u÷ ëÂÆôÍÏÿnû#ºŽ°)ŸEM…¬º½UisÞe¬DíÓeØÇñÚÀõÝ™¶?í½p!½:X“vJï¼rB€nh¸tȬ¡"VƒÌÖ’d€ ë†1?Â!ºÁrÐ()Ã,™´½n&Bý5éå¶ó-$híãï›ì‡Áš
+ô‹Ó-‹å´‚‚–‹^u/0É0ŸIŒC†¢~E}‡ŒzÛö”Ü4éÈ„Ù@&Fíà‹ÇDD+(@ I£º’²Ø*)“‰Þ4jKàŸÊÖÄk:>j+…tÃy×H ÖË+I=Â$u*fS§ b?ç–¶âЩ
+^ôŸ
+åQN”lÜ'¨§Bª¾2äMÖ£dåÜã:"8>JGLªRØ E!ùW #}hH;XÙ±=£·L&
+z³ãøžÞRô6„ °ãn<$í*'aÖ'ïÚž_ß]5б‡ÿ:Ù´X*›5³˜)à@)(íÀ½Üoz.,
+k©ˆ^ÊlÚŸ“ññG¥¸}šˆM¾¥’_æ`¶±™¾/3«Â:Ê'ëd¢&5²=É,Oå<й=ò¶¼[Ü[ãWS~Yd9÷#=ÇNIãÁŠ†qMŒf7,RÌ­êF]÷Ò+¼Ë{óÃÕÇ+êμ×(ü¸,nŒÿ$ä0
+uêN"v{)¨,xGZ³nñíœGv÷`AR{¸0r3ùŽYÏÿò7ïM¿G—¬ÌiWä-ùUAí>ð}w–û·­7Ü¿f—íÀ5ߥ 3—€1˜·ÿZÞÉþº°Îù:¿Î»’_ºŽú5¼ýu„{° ¼y=º£¸WÃ޾ˬ jrkC׊ÎáÚÝ9Þ?·?<c}UÚU7”]‡‹¼ª7Ý_ì¼î<·óCןç„5;ó‚êqIÕoSOG+~›©Ì C#íptf]¢ÛGë¢úŒ[ÌÓK˜³þÝ#ïFXglaâša¾M'5âRT3L„ SaÖ%ÿÓ·DÈî€Ù É f€ˆÊºOS ³‰ Ñ 9÷Ð9|ëpQÙvs<9Ý6šOS2.î빜söTe× Ãz„õ¥’]
+kt̄бQ3v<&¢ωˆc’„NPæý²öœ_ÞEÕTR/§Fä¬wà1›ƒˆ˜”TÌj¬lYGO·­öÊ–QˤŒz: žÂ]Z¦ÂŽGTäÑs*ñd¡œ¤Ì&zK¯"a¦à¿n2¤î/… 2"h0á‡OÊà¸D¨WÙ\º÷Ì, ïʺ%·ñ°UŸ÷jú³nYk)jTЉ‡/Ê›ög̶ÕQÙ±ŒÓ[)ž”s¨m½–Ü2ÐÜD±‰t|ê§Ó]Ûsèdº¼ï¬§Ã–Ê$Ç&pÐ~ö¯ “n:°²‡ð©Ø—ðVÎ%n,úFî¡!±G¯J‰Éÿq\fßI¤i¾œ«¹ìÙ{<g»=Ž=vtl§ã6®IƒšÕ…%!T±&ì„lÄžŽËØÇÑÖÖÖD‡¬¤Ø 1» Š¢ŠZ jÏ2oÍEœ“â«ú¾÷÷{žgDÄeÉ-ÀÇC#ž‘ÿfœ<¬iˆþPÿ)öþ“÷U2 ý}N‡Í6‚4áÉé‰ÆÏ6^6Ú„ì^ŸvuE«Î¾yÒõ·Â¼®ü±™\èëÈÏZÚ€®,ÿ$>úæ±è`9aÑnOêvÁQè%›–¿/VÕCŠËø‚øäÚÓ†?®?•#üýTԦߞT-ô7ñÈx¿;¾®S«O¤_`woWüÅœ½ñó⌽37…
+ó>å™üT÷|V}5?«¾²çëþš\P_ÃÊ«DP[ÏĬÚ|@+Ø’ŸÞ›‘Á^(¯æüŠó;>iUvV^¸'øCøÛÚO?$ŒÝûI£œ
+ƒ ù55ÙÙÞKù9õ•ü¬êÂúKÙ‰è#Ñ‘­—ª«%à¹B
+ä${@EF¿##à„§±
+&ê4’‹ú2¤ƒ³Íw«¹|ÌÍ¥†ïÛy
+à7…°]»÷¥BF1±`eg`]~´¾E„­%ØO¾7ˆ Z_vž]~ý@æ¡à·;S]§³ &ÑÆœEä¿Óz»Ýz¸¸Ð/Æýh]~Nwcç•îêÚ Uuê‘ôÈ΄úÒ~ÊaÛžFjw¦uð%íú¤øøîtëÑìlË‘¼¿½jÏ×^UôógÈîg%¾—þ•ô—`}ÅE¤qåiÃø¿êƒÝªÿdi¤ñwÙ':à´!r·îO[O%'ö1S'6¶Q°Î·“’c›?Ý<´ñ¬ùÏÐøgÈN#5T¨_ÎÅú{Ù¨¹«
+ÝÖU]¦Ûõ©/2Kv-ppœë6zÑÖ›ŸS^ÈÍ´}A$'¹ò
+»¨¸È@ßVâà¨À>¹9D@øûÚé%‹š…ì«$û5l½ówTá~“Œ\t;²S¨
+3E-êŸ3Nׇe°±U— jo¬½ì©.†ì›ýžÂúzrs|¯èn’‹ ûÆf£—.8›Z&å´—’^Èj§Š¨ë‰`Ûq",­&0•€Œ#7˹• ðÅòнrb.ïƒJòÖ“ÊrR^Q7UÒFà]3ì¡ŽKz†J˜«<ÇTYæóÓÞ—‡üØó£×KTôòºœ² fã!gy·dQl¿†]¾J|÷‚MŽÜæRf0HØ”AÆð?f%•0w˜ËN'Ƨhp&9àN@HÌØF„t Å°YJc¶l@qaÏ/?†²ÑÑ»ÿ÷ݨE¼ÙÁB®Ñ1›–KÃ3;pè&ÁwÃØ.íý¡.´üÇ- ß“+N7ßq$fG(`Ôr-=0\^ÏdìV.å°”"‰Y5‘%_Mxk?©Ä‡ïì/;ɦ‡oq½øí”æÒú„¥1;;haã>8«qš‹‹ãÿã¸LŸšÈÓ8þlíßlí–µ;«[Z5³3®ë¸nÍzçÈR â(˜pHL wº;tî„c@•QgÁ‹@BÎÎ
+ùhž=gb&S%®GŠÀ¬4¼ ·lùù`Õ4Q[ÆuE¸§œO̓̽H“²;5¸ÃZÒ>YOLUS&{ƒŠ^„¿;/k®Å ¦¼•m:5¼‚ß>œ<|³ëÿÙ™ÏPqÛó¶l8ï_ÞñèÕÛ¤Õ¾üV ¾kÐ11­²™JÇÀŸ¼>MZ-{£2ëF;¹rQz]ÌDqœÉÐ`ȬK|¥0'¿Y ˜ôÓʦÂÌ5Ü &ÐÜÌœš!UüÌT둬³÷b9$ïüøkÿ¿i·Y»‡p1\ÅE ºJ„ÀrDy-8½ù¶ïÌæû¡k«“‚“ÅY8ÏP[Ñ%¼V Kï²A%oõå­#+¿Üú+í“óòà1«ÏÛ¿Øšºó÷¢³ïRÞ¼B„œs}úö‰Ì³Î¯s.YÛê þW¹ÑÅj“
+.ªØ|?ØTbÒOÀã0h-‰É€Ï…Nþ©Õ×ÇósòÖBX«INv½ùLÞD/8Föá õ0¡©’:)åWuF7ÿ%>þý˳ý×ˤ735悁ë+ùÍúJÃ3`V+auw™”ý¸2{ïÄŽ[tƒØH‹-@…Ldô¦áÀFÀ:8CîSA ì1ªJ ãÊ~„‰húØ„FÌ&Õ¢zZ£ø”A±ÏDCÇ%í9ØA:lÔÁžh}Ñ4ÒÈâJL‡Ô c?¥c\B-.’‚Ë ¶e%åÕKè€iÌÖþŠzËl§ÿ?“ÇžOÛ_Žø–&_IÏ—½&u5«ª‹1VÞËù‡ZÙEµ¨šVK˜„^GÇO²/Ö* w•$¸R£{bêþRHÓ l©äbˆ¨Ð+²óh/6âlÌì`¢&K=e°ì¯`h=­·0 ¤’ÂYì%Ò6\$ ›0٪˖GÕ´ã ¬T[&,õ4ôsR‡¬}\~ù;à“.Ǭ•˜uŒKáš¼w=Aè’˜¾[zÒqÚ÷S÷ù]ÿCç†wâÙê¼Y¹éÅ…Eà`½A&Š)¨0&ËÕ=±§ücTXÙUŽ(y¥ˆº›Šàr*¤ª„l#Ù£bíƒì‡zÒìØO¶ú
+ª®$ûå .)ÐÞ½y[É
+¶|ò®àØ£Û3²{Kà?q³ 8ÛÊøPÑ3ôßå·Žæ\¢¦õ¹¾«3êK‡Ø…‘Q:b2•Âš¾B@ÝU¨:væå-™×}§·?H.‡_tz§¹vh{JÔ¼Ÿ$³¬à`NË¡Á¶­ÙΓá‰s¿O?mù3í•ÝÙ[jÙø­ç4ãA8,ù†ZK â²³=§Wž·YÑñMrºçÂÙÙC‹=Wâÿã#L\-¢ÁÑ
+^Y[ÎÓ}†
+ˆš¨ˆœŸxzûh-$çúˆLR9
+!âzƒmVp ~’ *º× KóA\• hYŸ´¥AÄ°£8ô“¡±§ûð?P$*ŠNv~•sÁÞÄ“ ðìxÆP'·ˆ‚K¨…TTz—Mšì›n\”y¯i‡q€›<äMÃì¢É^KfØ}}Q‡gÝ¢¶7Ú‹Ç(·ÙVŒÛïBŸRQëÿ8.³æ&²3 _ä67S©\¥*UI%“É“jaÉÀ
+iFö^J¿­ÆÕe¿ü
+iFêÀ½<°V öœ…^§béAÈ0²·…tB‡/6_9fj¯Œ|ët1h&Q›Œ>+ǵ£o6&N·ÿPx¡näÜ eÔ®(£ÍÏyï 'Ô,†3»¼nºôô˜BFÁ¾_ÕVŽ }à,C•°nŠ•U»?yâ‘êÜ/ß®L\®§­kù’ª÷SX|awcòëüý/K/äí5Ì¢¦ãfY驶½ž±^êFó«“Ç™¸UD7΢ÈHe[ÙZêÿ“cðË_l»¯þ£‘q-cê>2®ã0›®‘t:Ù„à\q{ðXÑ/8¹û¸ïó*íàsêÉý°ð<Úô<Üg&jB'Lp˜EU@eïצOÓ‡…‡Ìü]†Yì£Â.=t!åMã8ùÒ0V ˜¡•ÑãH÷‘ÏŠv3›¸ñ¸²©ò«Ò•`Oø¤¸Ç1×ÌXÍ$ܱ·?(Î’A‹”!lærĦ,G:2j7 ‹á¹J¸:¿>yqiâ«_Ó[fe3=s‹Žë…\Ò(cýCh'ji£šOYõ;ÿŸd<ÿ+táÚŸÙùèÄâZ»~·shå|Íœs¶ |ÆbÚÑrPs•›&™˜EÆ¥Ô£õ´ áÁh˜36í[¦áÿ÷Â’ ï^HÎ6 } Üí¡ãêA–0H›ï÷|Òé Àáv#Hÿ^DÓ÷a[Ñöæ¹ä|1bæžJÏR1§‰ÍøV¸Ü<d»ËC&̪
+¦TùÕéû¦áVf²iÏu
+÷γ›“͸f¹´Ý^Ϙµò×ëÆž7ϵµ´ÝËÆ‚wq9ß
+¿ãò¿(9èþZÚjâp§‡Ão¬î‡£ü‹Մ%‰Å5x¶“MK:ɨ¢›#ôBèBsÞŸÊ8tÚ}½šž»Ïe¼w˜¤Nï'eSVׯ¬žPƒ§ÚÍìì2tò]Y‰&(L\¨›¬e͆Ã׶VŸÙky§É¸¼¥°MZŽZÄ{AeW~môèÁ3ÉY*¨„9QàN$ê4Pà3$t4ƒZÄE¿ºkCq˜§£¸¥n§¡;Þ?žz÷pú«²_?P‰hû‹ÛÈÕJP?|°­ï{ýD~®´‰tÿ”´èÚ1[Í‚G!<î²»¸KýXa¹V
+hºÖ%gX`ÑŠ_ÛWE­êûC×ï®OÊ-þñݽ¾ßžþVõkãV- Ì쨯cgw;È€º‡
+˯ð„QBáš¡J¸-æ¥P«¬µ©
+[HwòìÝúÀ±Üík=$¬¦:pI1b§±…’XxÀ´ø,1÷=E8Ÿ6Õí»k’³í<a’W¢NC›§ßÌë)p &©2¸f¸ ÔÈ “ .¼Tð_;REUµ”gžníMÌ7Ç&[^cT6szi#£fÐéo?®õ~^Ü’µÁÙ¨Ü(bàû¸Üì½*°Z|ŽNÛ-%Ì Üy&úrÙXƒßÓ ³¼µiØŒfÕ‚€#)Iƒ„_û„œ‰ëÅTâæ 6ïZ$S&EØt,îΚ¹É¦[Ü <gKc6-CøîbNÛÇ º«‘›Yª·ÖŽçf ›´ê¨¸¼‡ÅT£µôìr!êÐï‡\&†¸ñ„ËÎÜ‚¼Ÿ«eMZ.eÒÐ ½ø
+H.|Xëÿb÷éÐßé€fä0ítÓ!eVv’!`þqŠ|Ùý—Âڕߟ ­FA>¡›bãª>&¬ìaPÝ$íŸsÓ·¶ŒÊ¯æWÛ~w°9wCÞ[Oê$õVFFµCÔ$…ÙºWË9çø´j˜Á¦¿¡c‹l\3TKØôµŒÓËÃÌ‚“:ÈIP\×õ’/-S•-ã(‹Ë{ø´rˆŒiG)Ìije쬰
+©°Ç
+Ì!#C¢ åmÁ)x’MÄÕ¸ILGõãdHu•[ä-c¡Kh\ÖEA¿U E/…«À#5\j~µ„y}lîN%~{“%æî‚SɸAÈcÈV÷±0oTÂíd“Þ[<t=›ùyžn7wlŽfZ'â yO×Ì*G3ª‘*.ê(¢’ö
+j3ð)d©QÊ Ü75W0Qe/GÄà ýûÁi:isЙ™¥r¬8HØ $ô
+—P°IÝ—‚¼OzoòYÇ ë ¦fqe/‘uìnMþó£_r‘É8çþOr™5·mžQøoôª“é´u[ÏtbÛاq/õk³‰¢(’â¾ @€«H-v¼Å»5V­‰¬X%qÁU”ÜÚ’HQ\°ƒ”Ûü¾œ^àƒ>¼ßùÎyWDí<<#äî¼c™‡ àÄ×ÀV±¸+Â/eЪRôßò¤—Í:5íé‡9Xa‚Í°fk—p´³‰º@·ŽÒÝiÐÊ5Žö…ª4bl1(0ô šòCM‰‚’Š¨³•Óöl­ôUJãÏäÍÐ`¹É6dU§™Ö‡®…»Äœ‡ºkÉ{CB5µhËÍÚôI†@Ú%/ô¼å¨Çm}B†$dà;™
+ˆYÈšoÕã£çë릫\Ü­«-bCõŸqõÁ’å*—>#3&`Ì ^8Å ÏNÔrµºHŒòñéçl’Â* cçªó†KbÂmU·³·ôU—†¾ØûùûÏEX3Ÿ¤Ü­>Ƨ]Z6aàhË-Ž¶ vŠnW§H ³4>
+¼òÉçS2„« —’%}RæCŒ´C<¦VŠ˜K.¿âóÁBÖëSrñiCK.ÓaÉihl¨Nï-÷Ù[¾} æ½²¦¿\‹¢:!9ùXf"?JiÊs°æº½¿†ì.Ûûv—†ÿÞH‘ø^‹î@·lf&_pÀwâi2ªoºïàÒ¨˜È"t{j³4âÖl
+ÑË2Ø)ã”X´6S¯P°rEÓMÐ`DÈÞ[ãò÷bx¿XôO+[áÙÃ÷។2ònË fáÿçh‡º÷†ÿ³xÄѦ~>cSIy#tcÏöO#§ª«Æ‹Í¤cøýŠîëŽ)Åð+±yͨ ŸÇœlÖ®n1ú¾:mQJÁGBŽò‰0ÈŽ'BÇw—úñÐ;¡K…8ðaž¡H8]]Rp6"rG%†ÄàŸ4УFyÚ©>„}i—¨OcÆ®%xR$¼²ï®'ˆZL{voAý`~·HSèïzcÝx]wꄸJI‡ï·3¾ ›p¿ÿ§úxuÉÒ+@FB?mÅs}š ØØÏùý>t€Ý¨öëÝã¹æÚô£ÚŠÏu°äÚŸ·]oülºÎn€¦ ì¼Óž>XÔœ’iC_´ÐŒ¹5ͨ—àI!‹Àk|Ê9Ü‚³!Duçå¼éÖü㳿ãþö¨¸îÁ”¬çSˆ®•rivÁ×꫶>>³a(˜ éɾVSÀOþ[vÙDZ{©Õži&l}lÒxMè/Š)‡ú nxî>ùë—ŽÓŸ5W1{gÓþ´éñAž»*1TS‰[o4'+¨¾Fƒ¶¢Ž›B,”bÓ/”tp¢¾n¼²³0|âÃÜÀ‘êâð\Š°î,¢·öÞ!½"0F§xÐÎ;F|˜v¨¸´SÕ„~
+>鯧Pc5jëoƒÊ ‰‹]¶ÌxJÌx©–Ô]àA3íM‡A.ãžh¥™Ý‘Káe'IKK;5l1Ë9‡õ—2‚
+s(›¡§lÁi¬g ßÕÓ†+qý…V~ì—Ãq©0µ lÏÈEÿ” ºmo‡f¤-Ü+ÁÚ›Ê[O{=ì—À8´‘¯DÚ
+ç³I»ZÉ"æO%ù¥ì&:7ÖÕl=fºòqYýW.éTòÞ ô „g<Ëï‰ÅÐS±~Y‡usY·½Ic>GyAËîð ÷r÷àùjÓ$0-øÞLa¡ Õ ‡)ÿ¸’õG”<åïäqLÊ"c|W
+gƒOQ„œÿQOTèðC1M¢-Xg#fé…>båi ä‚]ï¾ßHN¿’ò$)dÐ1à£Á÷³ÃÇ7&¿ûMbºÿO|Ò´è®#ºfÊãªn¸uªÛ‹Ùú÷’.ÕNÒyûÃ’õjfÛ†>*¦­@»íJ³uõ¿óÎtîâùÐG®Ô£Ž‰ËxÀð­ tTHÂR>üœÏFž‰…ÐCèÈvD¿Ùž×_(½RW²¾q>‡:eĦ”\–òÇßÞt£À!V6ƒ;ÅœBÊS>àÈ ðê9±49/}Ód$›1÷WÖM7°OM†">.Û{¹øăVêî\“?hÒ>o#åÅ[°.6ç!9†p Yô¬@ IOΰÙVZLpò—iwÓVÆ?gÞuÎœy3§g†33¥N ,¡’=ÎâÝ–äE–-¯ !@ †rÊÐBCÈÒÄû"˱qœ MâX–¬ÕrBzú9æo^Ü7’-]Ý{Ÿçù=ü†—äónG%‹ ¬§©
+øÎûʼn–ý%íY%‹«n'ì—^‚¾ÂÓb|.0S¥<v²@-æä‚7
+=*¸j?Ù²›Ù¤m`ûµíÚÁškP¶—’öA>fj«E ×ù8°uØÚ%DÐ8ºžsÛø„­zHP¦`~1EÉLÍÊÙ©ùúFàv£€Û˜¸î2Õ_î™hž/.€Œqc\
+×sQ§†]C:wšøüý‚¶E¤ \B{©Nûƒ
+}ç…™yĦ C9†vóÀøuX'roãØãá659%ж1ÈlÝÞ*ÒU¹I”¥›Mê/3iK—˜ñ RÚçªg'gkgÄa— þ[
+ìg£DÀu—™‰Úû¨g£ŸÅçnþñ7èžr‡Þ‚I9ð8sG%ïäŽØrÚ•¬]§Ð8ÒØôÏ)[þ»êVà¾R ܃3j”‚óGۇʦÇÏoØþºµ òxJ¤§îW)ÒƤC“R~æ9øãí&ÕÉùŒ—é@HÞU£‚·XúÖ<—½·X£çÄ|ð^ :lSRÁ_÷þe÷ i8Œ~Ÿa2ßÆ+Ùà,pÕ]xÞ­z <¸¬ž'=Ð=|<˜ª¤Åyzú1 ù4-‚ï×(tð0<~¾žh­%Ñþã·¾ào¥ÀÜqÞkA BÖ7%C!æë´Ëªæ°1‹ôƒþ{”<+…À\%cÓT(`žâíAŸ M¯·É†uð9g×U~±^+¿±\bÖ°.˜·wj„¸å:³j8¸bnb¸¦¼ˆ\Ù{eøò`Áx¶‘ó8aq 4½MËF±6†õÀ™?zë†lp«IBÏ$ÜF2XŽ{9AŤs¬ù»2<— #]\¡Ù_G;Þ½F¿ÞyemÝ_6\â¦öʺ¥ý0bíüuÙr¹¼â3l¾0|µýrôŸ‡à?,xy%Œô42^¢‘ò{¤Œ}<:åÔî‚å&óÚªRßÈü'ÅÀ㢠´b›àâÖNÐЕò
+ÚÆ…‘v!fí8Ê9Í¿ƒŸžÀ`Ìšåè¥GÉÚ4Béæcæ‡+úVnÍÑ_§îü eï
+ãI<])B¯)º]RÓ(ЗªÐÕ :b““ÂF2Ǧ^#ÕM`mšÄå6.d0M%f¨ÒµÔwGϯ‹ù©oY¸.Ò­@#µ´öj-IâlêÁCͽx#‚¶”âÔSù­o’MbCí2‹y7x0pUvú;>ð«…ÉûJnò>÷BÖ…À»Æ¥4¢‘3)À¡ê¦mä\ðÑL£tg¥¾3ý³º|Ø(8ÜÓðiä&—²v‰Òǧ­}àY7÷#„Q &çëà)Àp„˜ïÊ9
+ô——u-ïž~ºûrⳟŒ-ÛÏ´ÿ¨®"ת«¦Vî•—ô-ÿûqô¯;Ïþ¼ý}Ï'rÂØ&'Ñ>!n ?°óU.bmâØM9éÐTWíÝ̺GË%¼6!åspa|¸ú Öy°d<ÏÂ9¯®Y¯UVˈ³ûKºï¯Ø;v,—v´gØ°þ"6\ªÂ¹®F-m{ËúsÉGm§è§ÝŸ¼ ÿ’F.†@·5òQ[Öž+¯Žœ’æ®ÃukûÁô›zœ´C­ƒ? 1èq¬·²l¼p¸¨ýWeYû¥˜°våÐO]N5ïvÖbNÍáŠõ›Zíj
+(Š}qIÒ9Iæ$™˜N›Ä]YD»d+¶bsIO¦ÿ¹ôCΩs9Uuïïûý~¾[ÏFA–üçdWÊ9Ý•p¿|’K¢NaO°´ß*í;Ô£ ¸óÙ |í—§Ea‡{ŽQœÙ”q?g{Wt‰Û4µ¦àF­2ªG'n;œx8æxUʈ˜8w’ËpCz™{÷dYDJÙeô“Ý'#éÝ'¯ÓÛÏ?œí=8{žYU°“N+jCïùæ©…‡s´Â½32 é Œ¯‰3[ÊßAÎÞC©1¶°vÕæ›þ+îqV¶€Ôy”Œ¢È\1Š‰Gjv•k’|u{„øµg¢çJÄÈ&ćaö!äÓ4T5rêRËüŽ8˜ë˜EÔ˜ç|ZîmL'löÎrÊ"fÑß<·æp‚qÓ#5 ïnÌ¿w––¿ûžtioŒvíHÉ(ÆÍÜÆä2·Õ«£•¸Õ´¢ˆiŒÙØ ¸i - w|´vs”t94ÏkÈØeÔ”¹›°@•1¥8¬§Æ PyÚÎmIZùÄ€žCpM@ùñE)É«$åzg:/ÅM¬š´]Ð7Ã5ø»>nFZZnu¬ $,
+JÚ®€b&YOlQFòkyàyÌ^U_žOC)ˆX˜„¨ƒÝ„;‘¶”SÐüʬòˆ)'Úþyc@t²6$J:X¸MD>ý8$N9¥´Ó 1”ÍëlFáKHsÜÂkßÔïtÊ!ÜöPˆ/?–‡Ì""îÒ’I_t‘S22oE­H³[ •x5pyô’è¿-bB›26qÏ—u)ówp¥@¯ñkèE « ·»Î¶þr:äñªœ‹/‰zq3¯5aFƒZ¸üH¼Ïˆ6Å÷¢F9)lPô%írn•’:„à×2ËbvÝɆ„/ ˆI‡”r ø3 æ)±> Œ&ˆ,Ë™‰•AfCAÖ#-+» œù=œ{ÂS+‚îÿn‹geLð¼„CBI®
+ºÂ6v£{ªˆ9„Œ/û^}Þ’ñRkh÷ñ¶Nm‰è1'¿=öæPÅ­Ø{¸Æ‘²ƒ !aù)é‚GÙ_|
+üþt™8ðOÜʨN:8ÍA#¿yg­ÚSÁå1“ ÌœGÇkrø{ƒƒgëàùKâÎ,EíÃîxò(j},wM‹ë#æ!¶O¯ ûõ‚–¸•Ó\ —õ”‚°…T€-Ò+³÷:yïÉú“g€§j“k´
+|‰Z™Ø Ã3 ÑÁYQ1#·öp†Y䞆K|*¸,mu,Ëh!-³2¼€6E ü çÛáyZ±_Ù“ãîýÁ rÀ¯î¿îÖrê/ZÿæžìÉ?sJ™13§Ñ“]£ìºP“òÀšÂ(ðê¸YNN9Là âæ‡ì°é!ì7HÚ·ßQØé½°7FÍÙùÐwåH •ž8ÅÔÓRÐ9!jF›3«bJfMÆŒY%=Ç ßžnœn ÉÒ+êÙîà
+¹›mÏÎy^ç_9?ÿÀ÷õý~_ŸËû£¬|ßÐŽõ:›(I4‘ýÆóC·$XÏU!ÂyU˜ëdì øŽÇîHKâ4u$—¹:¬Óš˜èqôÌ¥¦4í\ÁŒ¿œ.Iîs8ÁŠ9™)Ë\îDC-™w»Ê#ž*Š¡gw§f@Í#ä@]3ÊÅÐw7F,‹‰ÛÝôÄ_ÚZàùøã/Ì“”â¿ÛIðØ‘à<®gÙ©­ñÉ&v¥äçßj[[/Ô5$ó(ÈfvìNÑ{I˜î8ËM¾·×…÷Úè †ÉØÉ`ë%þ»,Ü…’z¦ CN@[XßU1Üép„ðúÇ
+ýßíOâ]-ãb¥šr$ç$ïõÓ‚‡ió`ÞëUQÈÐŽv³M¾5öE+„ˆèËYfÖ/=¿ð¯M>ý‡®4÷z© Î6Ið?èÏy±%É
+á¼;ÈŽ³N”¾7t‘}®Ôe/gÙ°5ûÃédAØbC²£ös¶Ëf;ÎS%H™Œº]°Îäåé—Êò ë-dS‚u]"—êrž¬|ƺ Ðaªªs½ÌÃL赺6Û‡©æ~ sE€?%Ó`^
+ëw2‰ñ5ug‚,ƒXßÛ¥rT;îù¥¼ýàMÛ4 j&ê;²Àípçå†OVÅh7IaÜã6\Ø}Û#ig0ÏO#€?š©H·Wò³.Õ£Ÿ*¹É¦˜ gÓŸ0’ÜÿX©GB.
+äçlu²Ã$ê(D…þ©dxý\àaŽF(« è½12do
+# ÝTC'ÀÔrVâf'1h½™à¹ß›ðý®3¤´ˆ!ÎG×B ´Ž•§ë›É^ûƒ%ïôFЂÚlËóÜíc„."xóàAvú€{'Ù0eu†³‚{°,HwÜlÁº{IþçÊ‚èÛUr©=ËWÊzg§ë ÚbîîPyò‚ˆà#çd¸ÈK3AóUeqÿ¶Ž‘c­Sø ³ifÊO¸ùïí:¡U†ö¯‰·_&9˜z®&Ix·›øÒ$¥C› M#)ðz¾¡ä§¶¶èfžr:F}{4D…X†òwúñ
+d n’ÐcLÃå¡Ö z̉Œ½ÓG’1ìd±
+ÝSVç¼2ÊÙ0Y5Ìq€ý§Šf§`g<üRo7ËMp8¦Ä7”%:Ä¿±¿T³1f95jo
+là<‰w ÔäKTÈ…Š©ï!®tÓ“æ[JÞœÏð f;åHR~&§ÅŸ)*
+·ÅŒ7s Ð#óÑV[±Ï7öo}SÝ?×[…ç3Íd«²‘lc'ïûí‰Ñ«87U-Ü~´rÿ°eUr‘kD_Ópu¶YZ“5J½¯ä¤9l¶zê»K
+NÒÃÕ–tÇ9¼Ðœþd½)ßÅ"©LÐ;ؘloe`ëy­×îVع—jr‚y$ß«5ñ±q¸0ðךˆ 0|ñF7ÑÿdŠ›½%&€µB¤Ó‚ ë©´*ÆQú!Áy£ é½×_ìw ãäuSœ§ù¨ðï÷V]S
+â·íÜTKÊž:´‚ÓyÒuRC.ñDN|혧äøõ¼F§Œšç‘à,C•7{Jî@Z—ú—$—™(û=ww’–z¾Ü×0ð’;[$¤¹å˜¸óUl†_×ê„tñr½‹í„z¯e‚™eù¤Œ’wª£vª:Ä„¤‘šèƒi|¡„‘£éÇ>Ÿ£üºÛWÿÜ-m{
+òë‹üÍòqÀo‚öÅÖ¢½ñ¦äÃѦ„ã bÂá>Æ/g¡œÒ¶R-§àúîpe”{ž–ͳ*È»¡‹Â׺kLµ¼ƒ0U2tÒ¿osÁÐØ\¼4 ;÷gpÉËÂRCÅa&ìw›¨òZ|œWÕI±Š)YŸÊïÚæÐ1!)ÂË+¬éS‚(]gU”†Uø$°ÀnØ«ûÌ«£—û ìy唬s%!= ¥AØmÎ?–ßØæˆiç+Mk}Å÷˜Y¿ìŠÊ£VzQ±‹œ¢‡ûcÕ1†vÜ–˜”ó±!ã†NXWüÕÐÕn«‰Ú,ŒXæe]]d¥]³}F= ¨ ojzé Þ…O¿¶Ô_ ;[ab7†ª£¥­°-?ÿß;¢ŠÇ–Ñ’G>)Û<ŽIVòòïz—hH_“Ä@ÊøæÆÁtõ›¢!É&mH<]$Â\Š–è.ÑÖYFiÈØÃw©™{cõ/¾L5ÄixÈ?æ°«nUSV`“®Ï¨zøÓk;³ Ù¡5f½Wä9æÉ1ã`ª*JË…EHo‚k\`i ö@„;çÈ9>MÕ£ ˆ97µÕ_l2ðuz•w­ í_ãRgðˆ­òç!}Ù·ÄkØõ—Ðÿ?6ih¿®>íĦügÿwÔ#97Ñ1öùšøo›}¢oæîž‹ÕNàt‘ŽtBúyâHÝ’/¢½ ßâ‚ònŠ_ÉÁ4̪Kck]Vw,Å'9;ª‚[ݽn=£Ê&®sKp‰cžœH+bB«ä÷g:FÅÅ:HriX•6 y,ç£Ì#Ä—Pî~åüÜ”dÃDCœ¿»7‰K1O6%S×æZŠnÙÅ\”[É©ÞiˆQóÞ‡-vä\7÷GM “s„×m[¹U
+doˆ°ƒzÞ­$ÃvFÞÿ!§§_Uƒ¹aS”´_f(é¿Z§%ê~JÒ'üÊJ:áÂÀ$~7ÓÑ_t”OCÎ=˜A?ÝŸ.~ð÷¬ß7Ûqnu Ò§ááÏV»Ác9³Øô±òî#ûW;?RÝŸ$%ü¤dgÞ4ãâÆéð0˜wÇ.&çy´÷Юë.W@BPϬ=YÄ$¸õM¹£ -`2íóì‡ÓÍë>õdýü«Ž’P5çØf IvIã[§œ
+÷©Á:×£ÀØŒØÁÅ9•Œ§’–4pð!“ ÃkèÀÙÈÙ¾¾1ÿúˆÌ¯ïÀÏâ^n~ªŒ\ë-¿»#ÂÆú´­O~Xî)ŽÔróoúµŒ:ϳòø3>y{¨ôžE„zb“ÓrNí^µ°Å¥`VXD¸x»„‚„xVâ×…~ 9ûË4:fµ§è¦3çºKE†VA²g‰‰Ù!¤ù |šOÏmr*›‡Sèø“ÏØØ㹚n q¶Á¬sC!Ï!%fž©ÈÈ Ž?×¼2òo)Ú2W± |Ð_vÿb™C¾0u ¼a›o©— aµñTEGÚ¦ð±›C¨?M¢ºä)³?Û ûaéxTÔbëá¥EÔc—7üzjÑ©®¹È¥¡œš@ê§,‡J¼æ”3[/6GæO×z…CGUAJßã“ýFˆ+Û|Þ%½4ëéÍô6!ÒÎL½#z8{¹?Yu4Wñhw¢(rs¸èÉ]~÷å– wêG¾e äÒÜÝ4vµéy„3-Xë’3 OæZá{³´ÌQ*òÎ*»)Þ/¢;Å|ÔH¶ŽiöiÜ-gù <Â釲u^(Ÿ&ŸkAœwö©ÙuÁe.ɻĪöêhÅ95Ó)£Ãí
+°l”’÷›QX}4¤zÄŒ|¯ŒUåRp±‡3m¹=uQÖ)
+
+6zo‚€‰XbÁÞ£X°¢(¢#¨Ø jÄ™dfn¾ÿä{çâ\µÎzÖ:û·Ÿ½ñˆÝAr¢mš‡½Ô Ê÷FHQÛåaÖ>RÜ9)ÏX_´Vñ17aỽô<ûB5ß6'¬øÖCLœSfyŒñrì¥òʨŽSRv†ÊCLmh˜NõÉwµ-7üÇ<÷¼;Òr
+ Íhß? ŸëvÀºÑA ]çzì÷–}8!£ÎçDÌ‹Åj¶mZX òŽ¹˜`wz°p4ÙY+DºjÅIîË™'ڲ俖ØEÿé~w„“}:UWu< ò:ÆË0·•2ãoÍI‘®–žÂ€ƒò—Q¡8¯*9à¥ïöò3ÁlŽ´ â"Œê\ør
+¶5AFnŽ²Š¾÷‘0Wúš&»IÕvø#srÁÞ4}±,âÿ\—}¶¯H%æaz’¤$ò‘¦¾(ûÇwEëè'Û½ ×k){ãIûÔÜã)&æ/“DdÓó*–;0þ‡ÓÔ”ýzÚùl]õ‘¶–»;,)Xk¥„‚QÑs’ƒw׋z3hŒM è*/tëÀ:’qÌŸ+ãöºißԄ˱ÞW¼?Îÿ¹Vr<Uòjd%æþÊÔïLÄz ;l³]gnÆ.*™odùÑ}Äœ{K2&l¿«µßUõq¥ƒ˜®$ø|—Ñags[#+m$¯´2¢Og붆„è=0ï\ÎÊèÆ/hƒâã}k&Ð6"ÈÙè¨Î\R0` ¼ðõ:Aänku¦­_Y¾Û&Ï_‘×$h©,E.5à@-+<îߟŒp³Î¦¤l›N=d›ý,<â£MÍÅA³ÒŒ'æ6i¶¥¿‰fî¬ÉÛhâGZ;kÐ-ÒOM‚˜qÚ*8ëW6Á«_òhUÊŽ?’—nõâÃ6{±p›Ž†ú±ÈçÙMõý—¦ú›^Ê14c~_mÅÁÁ¾n›—¯µàaß0Þ;]8ØaEäé0u1.@ ÈÐF• ÜÒ"Ï8ûÚÈ>ûú™²×-H×+ò<Á<˜a_•ˆ/Lu½»ó2Ö椠À:ÉGΈJÏç«WóRæÙ”¨Ì:HÚn/}u6ÊL¾šÆ$%-·º Ù®«JvÜå°ºþR+gY{ËBÌEïG)è«U»}EÕr¹\#?™”Os .—d²s}ãj¹Šv8Qe,~¿Þ†°ôâ^oõÓ’ëqIÔåYîz)ýÕ^¯°ør²N~2¬ m¶0"÷»8‰çšZš¥³ªdQY°?HK:¡%Íð‹.æ%ç3)3Šè'cUQ.ænü‡ ]]ÍŒì€i­•øÚÒÁNÝ3ÞÎ5z«ŸcngÅëÄX]öåJ#ÆØD†¯¶¢ý—¿ ü×Ú ÞÚ×$Ê_–Æé¿7T{ãôSoEÄá‚B|¾Ú<n_¯í°0¶4øx]Cîsc[Þó£)rÒŸ¦jÙ•IÙx¨•”YûØiÿ»~-~éþi¨k>ž(K\mM|þ­;ËïpŽœd7 –>jÔ÷ѧéZNH7§ô¾šVxG^˜þ51òZ)â=$ëw8þÜðzxp†:îwn
+•yCœ„r“þ¸ÆÌüãZ\°€ xáÐTžãÑHH2#Çöû*ÓÆ„8s3ÎÜɈ°ˆ³W;%i“
+6¬›MôVS+Öao
+>~r$ÅÆ8‡¾ƒˆ±ŽÒŒ(31Ì1'ø $ÖÏ@x?¢|Ÿèð@H;õp£]˜~ ‘ã&kÛ2‚±›±²¼ª®Í¯­Ž®¤<릹öSK݆Y´À~
+þY/©øÑ,ÊêøÔ ðrub^<s¨ü{{ˆñ^h F€{ƒsaâÓÎŒ<âþ ·h¹½>£*ð·j8%z¥tcH=&*{2Y…÷š—ƒô2R蜘ðº¹4å® #Ú‘œ€¸Ž òs ż¿ÉB†C±a7LõääóY>õt^@±ªÛZFŠuœkÕÂÁÜ¿7&*Ziá ô*n¨VÆ{;ZUÐMGyÊ âî㢂¯c£?8¼÷qü¸
+O |œqˆŠX¬J›VKã‡jØA”|')6 *ÈËpÄ%DB¢½A­za@~è[
+D’IÀ¡ ŒD²·xâÜþé9qawFR¿ YþóztìÏážï/‡LèÿÑþy³§òlÉ Þ›Ö fÕ2š¦ ×]›[àVŸ\ ¢)dÈ`³A¸“€M ' ¨>>€sÃÂ54¯LÀsu>ù`RŸüîшìãúÇ—=çèçíáÎßW-•;Óʨ%[+s\YE4Tˆ&/†±THºÆaH0Ð~þx<°ÑŒ`J
+¨Ï&»SÏ T}Zÿiä_»NÇÉ¢±àõ‚I|x¯;óNo[PG©'Š ‡8ƒC‰øR¼|P.ˆŽ²HðòþgÒ{- 8 4ådºMÈJ°‹ï‹¥ÎÂÿžO|;²t~>î}¿9Öóqßå<{2¢ùuH6ÜZç©’`‹¸±˜Â8>b\|ðîÀðõCsƒ êB‘–¥ ×1½5UDGÇÈ7wÛ…Ïgj˜‡må+N#wÕaI\uŽä,öØ8sZ#ãn·-ÒÑaÖ”Ëý[rë|4e­¤Þ:iÉ:U´éü¥ýõâ|ïÅÚŒõÃæX×ôçûòÂé¸zæ?ÛÓî, ”­ÍY2–'L¼…~ ßÑ¡b:•ZÆŒn€ÓWg`¶µ‘d…u¾†ªj‚¾TJ0——3ïjÔñû“Öªóå.ÉÙ¢œñP'¾Ü³|Øq˜Ïל]¿­ÌŸ/O*GUeDWGýµÇ.Gñ¼Ã•kWûêäÔvéM/YN5>%† ‘$äå@`‘ƒ $^ˆ7V42î¨õ‰Ï§GªŽïõ|X·7]nŽj?mŒjÞ,4ŽªÊ)ƒ²bïõmìúˆ%iÎÞ3Ñc5µt”
+%¸¤ˆXAÝÉ$¢~ åB&/ÉMœ”áV™Sá&+ið´+ÚBÙU¼ã¹ž¬/{c¦Ë »ìë¶]ýý`dâÇѸëÏCÇȧ}çÀÁ#WÛòÔŒd¤KÏj®®öÎJLÄæÄă8!b‚c€äC„„,¼ªVA7ÉuœE›%ymÈ"<½ï2_mMO^¬ Èþ^—O•WOÛß­Ž*^?œÒíÍKV&zã§ú4l]Ë-j}~)¾\˜ï&䧢Œ³‘w)@ôò‡(*Dlgy+}Þ8XüöÑ„õjÇÚ~µeªýörØþýdzíÇ©ãÎ_Çã³{6ÕÁRwÞî}kÑ‚Íö“¡+dPÖBí©©%uÞ¸A—W³“Ò<R¢b!/1ŠÒ2ݤÂ<¼89#«¬ô2+Û˜O§Œ¢óåÁŽ?ž8-lŽu_nÙÛ.V­µ‡·Õ¼W‹}ÅgΞ­¹Á§¾•ÞYYè%IMô¬‹<T7¤þ•â /~X0$„Çàj%uÔ’âBu±ojl# «4 ÿ§¸LŸÚ:¯8ìvÚét¦Óf¦Ë$i;w’¶nb»¡N ÄÆlc³cÌ&Z@h—®t¯VtÑB¬ !¡åjE’
+ê™EJÏ!bö© "¦ÌÀPÞ-òyG.(‘Ÿº|ÒÆM;!RÜÎÃ"<ltG2Þ˜Ãz5ìG¾EàÙþö¢àа*Šmiy|â` Æ$Öè­5;€­¡|¼BVaôŸ™9˜Â,)Ø–¶N°ìØM&1ëÄ*",³ÛcË‚Þè*<ìZV z6×@deeÆ+Ú·p“–Á¸!ø@…ÃýÅ"½ŒoÊÉyD.)!r¸ä‚e70Uñ¤ªb—ÜBΡŽÙ±·8ý8¾É}›Øa£:Ñ;‡FÚhoì Ê.¬j±ÊdÍ; ^G›ºîTÊ:Í
+øÛ>ð¯U±è‘e}n4n—Ð2Ÿ±s†’&Æ«C-ö‹Œ‘üêû(l¬ì3ÂÃC3©Ý³ÉíXŸã4l*„õjuq^ÕOÿ¸ÀÖDš–%xÛ¥b7E×eñM°ïÌFêÈy#Å P”÷Ï*r¾9MÖ§X­„f5—è°ê£¥-3/b&R³M5ö™mö@'gµº_þŠÁ|¼$–?Uóyõ:©àk5÷O9O|kGɹ{`TA÷Š£†WKA±¬•«û²RP—ƒ|vn—M:}û†™V/š6ï±CÜ­KÃÆ%‰O'q-QìêØ/v”ÌÆ5Ôƒ—@â?­·3/lóèø]!±èƒ8?„!Á{<QÍCÃlÅ6ÉO @_Ú*¦¢=¥ELìžà©áhc¦åÈ„f—¤ûϺ™G¿Þâ´ÿ.´ÊéÞÕT–%õ€yAÐêZ¿Û7*i»\”u‚”ã ܧ¨õ·'›ÿ>ßÀ˸*X
+0ƳZ_Þ Rì¼ñ°Y8n_¾Ú™´ZT³íV5ç>²9¶¹ÞmÅTØHo觛÷tä¦Èþ«‚¢§­|BtiâNÊ€»ûŸÄ¬©ì_°U°6ï‘
+Ó!õ uƒä6¥Í&þ¾÷›Š –Ü<jÊDyqjž|’Ú&>Î!(«vZOÉÍÁU‚b陓;Vt¢Üùæ͵€Út€5µ°x6íââSvj×á&®ñØ8ù$nšêF]að2 „Ên XrÌ¡³ËªíN÷U}Ôá”ô홃5\rÏ)³;³`(iŸpláÒV »è±s¦™W™é–“5òÃÃujK¤ä '¶%Á%¶g§Ž ÐP|þ,gæU]VÖJzý>$”]ÔÖ ÿÜJÑÅÆ=¬Ñ¬5pj†“àmÙ-d–="nÎÉŸ*z¤ÒŒ]e2AÊƉÉÍGFRsÚB}™Ú¦´gì
+tDµ¢þœ}ÅRÞŸÕå} :«9u oÃ?Ilôß( „çW8ïæ“3H-¤ê\^‰{ææÝêå#§fù‘±ÃÔæˆꙥ„$¨™Ä‹
+ꃸúS=£»ê•kË>:.aÄÔmL4¢<¾¸ð’ßíãmWA:ñ*,¢w`
+.1ô>¢´žïÏ™2YMÎk²^©,…Ї2ö™®œ“ƒ)ûþOq™=5±¦aüv®¦ÆqjFÇrjêhÑ)u<‡9u!ƒ+‹Ù—H0 dOHg!;tÂ"‚!A @ölÄ$@Y;[g#¸žÿdÚ«îꋯú«÷y~ÏóŠŸüíh|éÔ/•ÃîPÞIë;Ù#wœÌÞ¤Þ”³²û
+^2rqYûm_:ýÍ/öñY«Ý¿Hø¯k3®áãá]ùÛŒGþ[Ø ô¦}ÒI88¹Y ‚‹_öEà‰O Î:Ù¸€ºëZÖEÇä]à«‚œ†]c`ɯXA´%„‘y–ì<BÚÈl ½ï¹RvþÛ9ăuvŸqènb“ÙïòÈY¿HT8
+âíÖ‚C,)îMWüBaÑEï)»ý%'{0¸„½‘ÒñI'Þ)UÒ, ‡µ d¥ 3(ŠDŸÓe·˜ÿ=GÊÓ²W,„£ä¤‘ÎØèíy*f‹vä¿Íä¦C¦Ê9Ó5ºA{\rŽÒ*¡¸Ëê-8؉mVWTÃl-ºEÀ©O"‹ìàD dt!øÊ„ô©”…ý2¼ÙóSt{v11EߣÊ":nßáæHkÁ36ÙFIÈ]X‹ˆZ´‹G`„9+ùYÞNnÉÙ«œÆsšÑÎë–ÿ"¦¡?‹éˆOc&jKÜDmNZ¨ y'[ðŽ½Bt·5IØ›„Sv ²›=X´SŸÐW™7þ`–ÕÿP´³_f,bæѳҳúN}
+©§äV¼]ò©´Õ ™†ž"ù_ŸÔëÒH¶d‘9¶F±²ÁÚ³F°¿®à™Vfœò©Ò Õ ÕFµDTÚÎGö<6:´J®‰mÝ%çäBÙõòvP’5 É©:¤ÄßJë¹ýQ5³AÅh8sðW÷%0ñþs\Cz%…tq$ëžU„jïæ&ìæQ3ÀÙ#0¢f¡¿ÀùÏApþëlîË¡dÞ¥t¬¿¬Ú[ÆÝ o0mPka‹`øÄ Êó)¶
+Éi2? ñ¾c}~ƒ:R1;ÖP6Em—L`Ó¥”™5p²'| ˆÇË>‘ e¶y˜´C1WðN¾‹o3;}óÝ—ó=—`#²§y
+66±Ž¯NÅtûì˪³$TÅJózþ@NÃjɨiOBó˜+¢ÅÔÖH˾’xï7ê¢ÆkN!y›Üæô©ˆ·÷U½Wýªž«ÞE„Ã+ý?uK%‡;ô¥äÉÅ j°ácP¡,yÅ"Ä·/ÒˆGöW1U[üû²€Íÿ´½&ßõܾ[~œ\c=þ?ÅeöÔ–Fñª~è©©­±gºÛ¶ÛѶ§ÅУÂ(ØȨd5ìK „%„ìû
+
+áç>Ï+%ŸÌò­c„‡
+n+Äúªë¡ks«n¹®`ýÙ(,½ÞDç6PüÒÎÇç&Q2P}‡
+»sI5‡ží ™½öçɽŽ¢ý%X–uºè'ûüW¿‚ÖðqƒòÒ± æ7 ò#ÆWÓIðVÇ í nÉ¥Wí¹_é‡aÿ)­¯q/nÍQŠ²ã†ÑÓuz•yyÓ,lø»YX}EÁ~ò òµy zèç’ñÎj‰©ù¤°’VSãK?YØØ̘4 y™=¡àbø¯ƒN™i3³+©£7$AÏ
+®¡òu×\s 7R;ÄÊ B€v,“ î5bQp‹Rî““+]«”bç2î‘IÜtÛ>‹zâÛæc¿øyØ( ¥­ƒ¼„¾¯7câ‡tý¸è.“±‹c¦QÑGÕ`wÀ0ñ&¨á”\xDE«:^DÞ;šƒCŠ^hT…î^kË5Œ¾¸j›‚Ý8·q¨‡£³q=©1¨ú?Åõõ–Vž‡|v2;›q63ÙI1:é1Í–hbbªÆ®Ø°ƒ"("UŠ ©¶{cŒHPDEÅ‚4•¢(¶äɳÿÉž\œçwwîÞïûyÑo<j|¦]V ¾äB¸·•5)¢Ú7`oå8&¿ï)gWà÷õ
+\
+è†L—›äR‘¡=Ÿ½­á¼¡¹Ó£æÖo+êÒbÜ㇒;¦ÎâÛ"vú%Jç:àIQ]†±-7d¹·ð®mùtR~q‚Ÿq~¾½ ôpšI8˜ojwMR²`¿~ï@«“èR ß3q¼Èå. ©iú•a.µ$°Ài°JÉ©ëØØÕÁÊðÅö¢ÛCˆ(‹ðZȆ2²œÙU²ñV!ṧøª¾­àŠñCéM} ôŠ¡²Ò{àVU«BRòÒP-˜A>´ok‚RâG¿],º)Ä¿:;A{ꛬ«ôél—œQèŸn@ê8´#Ð,`–`.1ꉹ;ëŠòü¬y°*zi°ú±±qgKÄ‚ùÕÛ
+
+Wàž¥ –ÖÑ]3ïƒk–sÏ2ª\ ¯“„l¯ŽTXdQvfY§ªîmnZ±°7
+s‹ò-19m©»üîÚ@EĶ´æµ¾=ÿššŸya¹ñèÈÐÔêaß™zKÂÌ—G/öWD¯ôTÆnÞÚ×ÔÚdué6=ÿÔÔ6ðÕÜÒëÓ6 Mýe·–ú ¯-÷@¯‚^º·­ åoŠÈé†6ÄMë)Á7 m"ì˵aÌÓ)>üÆDb¯(.h›zÑñ™óà)9Îδ”¾ h•ÆÎœ.TÔ™ú´«?Èð‚œ#Å‘n¹èÜv%6Gß•w?``3ŽÌ‚V4º¶v °n%¹`ã3"Ò¯ð§KÍÝF6Í«¥û4ìº=5­Ô;A̳~DÅ[ÊÂçß—^· bãÅÌÒ(AÙ‹`ËPÍ+§„Qâ”ð±žqrºGLÈ°á²fšJîwVÄýkš_üjWÉãz• „†ŸôŸ¹÷ùw7†‘ñǦzÜMÝQèénØ3qczØws¹ÕÔÒ-)>Ù1ŠË}•dÿ :f¨2ÜØ\~œšy‰‰üiŒ˜x÷Û«É7M)ØøTñÐ&D<Ù–á³÷´ØœoV ÑoM#Æ%­Œâò¾,uŒÌT‡°$j}j+‹ØƒEŒ€GË£;d ¨GÅ®q+™åƒè¤_øyOþ1TpVFKü}µ¯0"0CƒŸšyÍGf‹™ëG• ãÙñBŧ%¯}FÄìé‰Ð­ÉšÔ¹ÎBзTä)è'—ŠGÓtV‡[Ä”,©sÜ¿Ò£?]as}ŠªøþÜK_nˆ¹7÷†G‹ÎÙÕS*Ö%¨gn=¹Âoy/:^ã¶|Yç4}±pxûF&έ¥—ûÁ{µ7Ë&HŠoL|n@Ï© è¸Ï$ á”lÃeaÎOÈè}5³Ê!"&láž»Çë²¼JbhŠè)"f[všV²¦`n*˜¥^-›°£!ø Ø,ùúû·?‡Nók1Ù‡óLèÅúÉèýiìå®W
+~Øû„- éíPI¼º/ç‚qôu‚¤#ç§IÜ‹+@ òAÆì0N´½jqç;ÇÛºôß7¡Îˆ‘\¿;‡ÊòÈk“}JàV™Øc³°Û«d#Röæ©ÕA­í‘ã²íãõ·¬#u7æÙåqÝ°ôïجK;Rtž}
+•ÎE¤Çm+:A-«usªé‘õmý-³¸ægÛø럔c–N~Ð äoͱàGjv˱EØ÷eCØÒÓQ¦Ø/^eœe°öÆ®‚T¸§„šÖÆÚïûÕ]Lð&,ÇÛÚ»ÃõIg'Ðiß.RŸþU ž÷É[ þëîñ¬´¢Du÷þæ‘’ªb¦^qÌ1N׈5ÇƶrÏ <Õ4RpÕ<Z[ï:2ËwWDÂ9Fõ‰¹G|l€è§k\RXÃ@ú•˜÷TUÒ®¼áñ¿7ƒûúž¾E>>dê~±AŒ€š?\Äåí|lÉœÂglj*îÝÓ Ø.µÄ§a4ùVØh÷|– ÔÒÖdS²{~Ã-®¸î“ãJwU”ípÃM×G&lOÉkß’ó·åÄ—[³¸§ž¹¶§ûjBÑ‘‘Ó0 L„Ö†!Ó€ôØÚ/=±
+ºc¶.ApÎxI ¥Ê»ÂBh»!÷ ñ|’aB'Ú›né…—6ÆêzdmÖqL²e”úØ-íA9%"”ë#¯Þù‰Zè~Ð1Ý’aŸ®M
+hõ> ÎyVíž’ƒô¯ˆ ŸjÝž',
+°±³„Ë÷(ˆeQ‹¨ÿHÇ'ªy8¿ô·Ž‹‹­sÇfzËÎ\[¶YÜpó³—éYàb\ó|´eŒ™mäæš» ÷–5ÿTÐJ.®ôÀn«kßuäŸSòëxg0ÛÛ¦ÔµÞ²kÞéÆôõ1x¢y 4Þó–5·— d¸S†{îž%DŒnØ‘-<VÄÌ¥ù ´&ï2¾Ø:Qs{©%oo…ŽØW@Í¡±,¢/€Á9¦Q)Ÿ¥ðÆÁ¢Kìý‹›“جõibî.ûüçQdrd•O êø´# pŽö*ÛJl“M©šnxüW+ÔyºÁzT@[ÿðÇ+fê›8_¬ñÌò6§P)ûJF£gA@5Žàžxæh°€ž×Ôp0¡E¨Å¯à ½HE‹Â¢+RêË+‡ËÑ!%–¨„å—½ „W€‡ø°•Ç>¶ó¸Ç€Cw§[Ψñ©¼Ýyn}HÇ@î«Ú “È”- ö±K‚IÛ›iæ™jÉUpŠo8§ÛKȵ³Üª;*¨*Þ#Åæð…‘eVc@ÉnÝ”"ó×ß•§ÙÅe×¢F5lìêtJÑ™›S°›ÎéÊËpå­mYcæ{G 觡  ‚¤üª41¥,ñd½ÿSÔÜÝÖ²1^Yk†­ÿu¼VÕ%Ed|ut ™'±ÏE觾վ÷QGŸâ@ÅD8$m §¦9¤„‚€–Õæ·ð9Á5:*d` ¶g‰y)wGN+ÛG%Û«®®—_[æ?¿h,wN`SçmÖ‰Ö\•¨ä²SŠÍ:Z¦Ôœ˜)¨+¶±ÉÁUAgÈ(…MÝ=‘µž¾SÛÀLÌ&:µv Âfî@/>4vT{õT¸~
+ÿì=µôÊ®Œßž¦«Xqƒˆ”?õÃïQѲ/l7üŸâ2Jó^£xçÞ™ÛÜNÛ4c&MÛ41MÚX›mnŒIlšÜ5Š;.,‚싲+HpßÁ²ˆ,‘]Å¢È*‚Kš´ùSî›ßÞïOï™yžóœÏù#°L-µÌP ‡©•W¥´¼‹.`žÄÖxÄ=|`$×&-ĺ èRاb£±`c†Và^ ì©ÈU1=à ¶ø/þÁÉãÛ„"@##aò“6ÉdÊ&ZÙ䨞PP6ƒüÊ–g!­:¢æ"¼‹ôÒUnkº½6ŽzõÃ$"ïëQÔósýÍà³ ¤ü vQÕåÈ[F¥²·ñî >?Í3M© «{H;ó„—¾%xvt žP4Þ:0µ—Åô6ÿjg{Ê%žŽZȘ™Ò7â*¢ÆŽÚðzi_ÏÄX¹ì]5¡pw—ë™%ÁlcíU!R~ÖÓQUŠýsX𦴤Ö]ÀžÝžÆ¾ð._º§w+íYì3ÇTÝÍíŦA5ø‘lG=ÕôÕf
+úU@C­wÈpO€=½~`ær“6>/´F¬pŒU][㥩;K¿C?øÖ6TžÖÑár64s¬=ïüÀnc'Ð
+j;[bfÀ:.Þ»L½·ÊFèDðžbéß1#¸ÎÂøæ1 ÍºËæ‹o&0ÿûÎ:Ú”Õ/¼òøzÚ¶±ZÐG7µ=¢i|²-+Ï4J*¯r¡·þ#€fœÙ›«Ëþ´Ãëß×SwØ\ ß{Ëg¿wò·»º>lÊ+À[2höÖ*ЃKÙyܘ‰G›¸ä¤‘Ë86pèÿ8™äOÛ4ܱ“ŒÞš?ðÎ’!ÀmeÅŒ’ᘱ‡7 ßZz„1V’ëF¢¼#TFÔ˜B¿ùпˆÍ8ûŽùMcºS¿åèküÝ$(½â®ÉðO£îíHñ¹Êî’+S„‡_nM!À'VéÔAE&­PàVDt$hèq 3âë©]2ulpOl|Vêó¿ìÔ–À}~Cg‹y‚ô|‰Ùð›cVrkÿ˜ECήuUgë9Uwœ€eå­÷c«„R+à¥!äü4õá7áUrÝ‘±WÓuSCR©_~P
+½‹¸Ü š\±µ@*2 Àoî®0ZÌBIÂHk‰jáC«ÍભÐK82s©‡ænVÂÒÍ‹9ä#[·èÈÆï>²_‘Z¾##½ôΞª{~­Éùå_¿Ÿÿb_te[Š+Úh¹é†e(»
+.i7¥—Æ©…é¶qR±€Ï\;Ø+o{â•Ã³¢:ô«ÓM
+îã¯ïÄÝ+M:E#3u`eÓŽ]lÁ‰[8™rJævQ¸+A=­˜oö2·ôgr!¥vê; ¥*¡aÖûfpàášËö¡ÊKžiDŽ_ÉlZâU§¯õÖfÄô
+16V úaS
+{¸¯j~lËÿÞ2”ÿ“köÈ0X2 Ö> «°%<ÜžÍe|¡v¨ùf@Ý…=vH¦tBʆ*iªnË;o)'ßûÊ;{öÞÍëÙ– í2â‹ ž×yêRÿãO~ÚbRþra±w˜|×dóÐ2£:¢à¶Ä´Ò‘«ÿí±»oéÈ&”Þ
+œ°_RVÐAÚ¹‡«Ò=5×÷¸¶á– ×þ~tN„ÞÁ?ZçUþ¨bA.Ž´e}˯Îø/ûõoÿFÜ;³;O(Û}K)
+©‰%q#µ>úŽÖrdS±,±!Ž›8„c;—T’ÊèÌS‡ZçSvÔÜÖ½ån¤gœ] ä4^ím|zFÞQùctI€ôMaAî¡êt³¤ Í9\}u{®äœDeùæQàÔ: Z&—„ hTÇ‚‡5¸ü“ç_A€uâ‚ËZañyßBSö©ƒIhð} öåþ*òqd­ ’4‘á;訿7h¨÷LÜ¡……Û_eÔ~æç¨AÀ8XKBk²}’uŸm{™¦4^Çÿ$iÌ;ïèï€x¨•®1äÝ­ItöîÕ…•®¢ŸÃ*JmPÅÇoLuMKÓÖÄÍ9û5lèD&-”æC+¡!nÆ{ÙQ6v"Â&6aïJnzS
+Ë´Ž´ÜqË•ƒXðE·äZx‰Uã—árLÝiZvî9{ÿë›2hšIS
+*o/ ê(sLÀ2RXæÎ
+îyì£èì„¥wüÄ+ÑÄìz@Ën ®Jøñõ1ʼn«»/jd"
+Î-ïjøòÿ—×WÚé…gÍ9ëÔÌdÒƬ4“ÉDÓN4ÍıÆn,Q0*¥ˆÒ¥JSD)"ÑØ{APDA¤ˆˆ" ‚ ½grfÎü)ççÅ{ñÝ|ïÚïÞϦ1óE5äQãËœªÆ܃%Ä[ëäù±Š
+,´!î÷¬ñHG
+J©©§þ§¡Æ¬¿õÔ¾øv¹×ZòOZÉ£˜%ué¾eÒ;ï
+¡Ø»B,uÊ3wFÀOôÂŒ+*Æësö~è“}µ\-ª¼±.¬ºmF¾znfXGzÿ»•Šúbj,BŸø4ðWÀ§¿ZÅ3Xx]!%2Ã'«K©šó£F6!x¦S‰/1õAïJ×R;AÿõR
+*Ø ŒKÃFX&‰™RJMÐýŽœ·Mã+ü:~{ÀÐÙ0rˆ¡­®nß:mºvœGÛŽñæ;è»Rú{Ÿ¾ƒéXaÖ­ŠŸŒbË®N
+oXû¡wUew×û ™{s8ÐñÍ.eseÔÄ$D78-Ñ-6+²Í`uÄ×üYŽRÊ£fá§Ð†pĹÂlñ=êX߆wª[!5é˜#¸)ùýiOÁ¬Û§WG¶ûWƒæ!CØ2`š?)¢›Âž€ƒ¶}ñ³’ UŠ`÷ ýÀßÚN±q SP·adä|ç4&ew¬á¾­¿6^Ó‘AÝ º9­¿‘qÿò7hÚ —ŒP°?‹IÛüK°ÁïïMÔ?vÌBxÈÅ@?ÀÛ(Ð!jöUß
+æ3 Ê\šáYת¤;`è➘â°©³Ý2M̳MÓ€ü&V¯P!®|®}°ùù2§öž¼ì*±¥2ó;ZÉ«ï7º¡hg|Ë:þþ¶¶»(nÿìÜ%-NJϹimH5Ž5>£ò.XzÙ`¯´‡TÓ Q¶, EHž¹êSÂZ
+âÄØF9VQª¢ZzýÿÌ ÜÀüiå ÛììˆÍÖ±ªûŸÇkïʈłæ¬ç˜à{»c”Û *yS¾©íÊ¿2Oûå "ù|WEâ·«œw7ÜšDnuZœu
+zåç |º{‘XæRQj–zàOe¼ê—Çk"±](öZ|²Î­a@|À½…,ŸQ X%æjûq©n-Ÿk[`AäLè-I}Î}õ©ÿÞüìW¢òCk¸ê!Ra\öƒ«‘w¿Ï‹nq™S'çPI¬p
+H‰Œ—A’œ;
+„Oà;Ô¦B @bí·ô-^ĬÚ÷ßÎÇ/©g^×_žv8íê, A’$-ìin-¬‹™>þe#ù`jož³ùxüú!-ús¶˜]5§Œoƒâ9M[6†…=>Þ€\s´LsôÇ®:ZŒ‘¾ ½Íèî)ÙeA¼é˜S‡-ˆŒÞ9XyÆßë*ï–>øÀú5wþ/á>mÓ+®v¹ïÏœsÌfÃT}A„è}
+Û d_Õ$B,F¿Ñ猜ÁC#ößiIîÇ‚Œn}&¹° 'ÅÚ"Rm]¥Ïà¤T‰z4/"é¹Î!7\Óz}°HŠ¹×ÇY’2§Yæ©Bhtnê!þøùãsWsyr—<–ÔäOòBB©/ZÄPb¤6]Ü€~ýø÷yüÔóÙ©ôÖûÊÓ|’Ã0)¼Èã÷¹ÜîT;o@„¼R[ò7|AH¾AGÒ7dB=>)!”p‡‘yULzôä'·} ïp±)Ðì*~{òÀÈ
+Ý$D½
+¤És/ˆô•7¾¼ 6º%Eª«$/ Ç•¿1§ðɯÏãW9mê ècÌfÏ–0o@¤ˆ'£ûX€h*^ æcý–PmûˆÑ§C¤áÒV: ó>­q‘ûÍË’
+©×Ë›#ÅEü ‚›D‚ƹêääX:—YÇÌ–Uƒ³Ë09ÇQÕ¹!€…Ðü”a$9¡nnfç&àÖ[—SÎ €Áà–+9‚ðï%cïè÷gÿ„ñíb<u'ˬ÷uá—6û½É1›"1–»{À[ÎÔõ¸BxOô>ŽŠ+gPNT16
+IŽ–b ƒL®cè8-²Ùqåt.â3![Hù<~è±»Ë>²S$$üèÄÐ,C[Ë8ð‚зzfß5…©ÃðÌØ#aHSÍ1÷0«QŽ4«­—3¨ S™V´8á¼€þ1Y?¾3~/oÊÏZYRx›¦<,褤•—/ÅgaK)៭Û£·dÇy\#¨Q“O݃ÌLCÿmA¨ ü"ï7¿×UXV(*ޟÊ&Õi$î·²/º ׫P<RP)D'/o½´àt³
+t“,•èÐ9´×÷˜¾{L ¦7Ÿ‘¼¾³-µI:©¼[–®Úc©ŠtLýñnŸêÈò> Éx³—AÄê76›=ê(yVM˜
+›óUD¢™rDãÐÙxN„ö‹)ª…ôÔ+Ï"ÍQ…Ìoƒx)šè¨6èK} ôÅäœF
+“XsO¼"Ìž¬“º2TÞ¯,¢ÉÚßÕâf¼”ôå”^ ¢ri۸厕k’½ÞÎøÂÓ’1\’–廼Ý+Û†å¶Ê„Î[ „¸4?[Îÿk­å“ Ö ´_¸ýÍ—ºW¬*oW'ò7äxÝÔýõº/ ïÄôß©i 7Î` 7Gf‘i“r ó÷ÑèØl3_sê è‚ –Ñ`&#[[fŸƒô IìÛ4Â\; ¬s•ñšK@ ƒâ´0ò¢-DåÍÊ«Ï«g°Ç±[¶ÌßJ¯ 9Ç{Ð!rĺvåõPõjëWÐXóŽ鉾ò]–ŠA@~¢ß>È­óQ›±ÆÙÐá&Âdy[n ’²6Ò¯>BjR@RY¾c<ÙÁx¤2>ÚšBF—ó˜ŽVÛ£aVwcT®MÈ1ËDÏfš+*@\Ÿ¿hRΠ@©q®z¹–•&c×^XÁ`:´dQÏöCùÍqAzuZAð§M¬m¿€³| í)neŒ ¿wª>wV‰©X®äpMòÿïøg&ÿüœšT¾ôª+•wZý{ÓcÔXÎj¥=<'HM®õÐÌ®· ,¢—iA
+G„\«+ÆšRÙAºÇü¤<'ð\ÌB?¡L1 îîbH1¹°#y;ëŠ`µ‹á`ú´73ó*݆N›Åý9ÎU¨)Œ¯Ù¹éÎYuÏa7ÅÛ³ÕÙ^}´öI˜ª¸âsU/¯$ð¡ïxXq˜OÙPøÍ•š‡XÇOŸÈ¤ÆûÔ;ß@ú¨í!êG3^@ã™HS^kž¬sÊcCoÃZíöãtëÄ5
+0šød‰Ý¡ÌÄÿ0^&Ùmå0ÝŠWàþ»†ÙE¦òþ§u (%}þŠ“I"A$ˆæ5Ì-Âpÿh)&â
+å°l2‚‰EltÇ^5ù<<}جs;FŽÕ²á… °i<7ø•3e,² ²DÀ‰Ž2"éH$V‚i;¤Ñæ­›¢]…:냨ì Ps±[Û Ða4F6L8¿Êb
+“¹ÑóÞ¨ÿ’q]!Ò¬Úúâç¼½¼|"ï+¤Ô¼ç¼©Ki§ÒŸ/¯*(ì‘Œí “Bª1örF›yyriåIž$ЃÍlÊø朄¬`…@6|INxúÞš„ÐìxV{_!l±ŽÔk_éq@p²Ã}FدÙê_D"PØ«{È‹J>… +Ö%(^¶{÷óDraT­ |ƒ.®õš2°9˜u@~|9ˆ 
+q•¢ RÊ°
+¾õá‚“—vЖ©`Y(¾0Þœ#à än`*ÑU6É&”\ùFÀ"úä<¹¸€Ö K»° ÑÄK`ç£ÔtOèe]® }Y;¬_ïšôj3ÏxYà· ‡m 4ÀŽÚ$>dZMöð ;]v@.lný¶#&ÑkNudŸ˜$“Ô9íÝ Ç”ëÝ° ó†'
+Ãg™thåUn%©2[¥{#ÛH"dìV© ŒT@3Ô;„ꂇ¨çtPR>žkÁ
+Åaͼ£ *r4:ðJªøîο
+WEó‹—!ï»~aÒºé­CPÃݹ>He7ó4¨:\X±U$md-6ɸ•lÐAÈ‘-cY.ÞD „$’dû‚FÂÑOСÁwÐŽjW½µáÊvÿǘ_¢Õ¸,CÐæò ÛÝòvü&¢0-û} ŸâúÆq£d–ØBóy1’­ù»DAÛ!Qaôy±S'Š•Iûl<&ˆýE¯‡ôûñTƒ `æ«Š˜!§"YA#çáí|_$ X¼®ÅY·ÌωŠ<r?Õ/|®ÞÃtÜk´ÊûIVʨíúƒîo²ùD„* š(S0Ì? ¨eG@­KPp€ÁQX-Ÿ(åæ.§-ñŒp‚Ù£eÄ‘Y‹5`bïg£˜-º‡a{‘ÞŠKÖÒ´cÈìZòqÃU`uo7û P¬&ź¸íŒ7×N_s¹LÌéM—É»Ôæ:¿—Ÿöà¥Q¿²,’«aï•„6Ï
+CÈÌBWð>…€Æp{<£e3)õ -„ &ošÌ–øôÒöÇ1ˆ¶dòg Ù—†mÉ–é
+ è>…̶€!Aæhß!I< # LÝwM ^ÇÞe‚Ø[¦YT:6j"Ó .ö´®î'i莓M1Åp7y×’-¢õ6äú
+øLPFËE&’ØA ^— DÔ}7 rѦ¬ê
+ e->/zÉëbˆµ[¾ü
+Z]²W¥í‘œ;ÛÚûRâÆșބDÖ'E­ˆF7
+¨&&‡„¼Ú}BP
+Ijœ€ˆÞ‘h±BÆŽðÑk#ÆJöƒîÜCŽ.ÀB ÝS®Ñ
+,UÝ!<‡=K]¼"‹„+ý&m9…à2mÊÈ‘;r!¤…·²YÀâÑõ8Z|bŒæMøÆMŒºÄÍ—­.7ÿ‘­?=n‚
+ 9u:j‹
+JUŽ¨é€7ÃSê±™/65O`’þe¼Ìq+;’(º‚ÚmD΃Ù`›t{„<ÒÔþûÜÌÈßàñ_( ªªË|™1ÜAŒè!5äf2ü͞æ¶
+êðZR$;ÕY7¢eC˜+îN
+µ¥ìŽ"/«É|íÉ`J²¬{oýµ4ÄdçtYÄŽy—˜î!ä÷\ºK ö¬Šáh܆±ÎäÕGÆ›EÃaË—AN®äsšn%•4†ú~e{Vd|û· g¬ ú0L V¯ÇÍÖ¢Åw財÷Zô½AðgS|bÄ«Ð(·ÑNªá¯²FŒanäaõÄ;ó”Þ%M<±0afŠly_ÆaŸƒZQN¶¯Û9„U^ÀKRå¯øK˜i¦v>uñÛ©¬–-\r› âpjµ ÁÕA¶¬£ÝƒÿØÌ å`ÏZ×Øç~>¤'6™ê’à]hÌ– UXánó<Z)N)í Ù±$Íc£§ùp{þú~e04Cáf†kT—…`éÙ r¦;b“&9=æË hU¢DÑ«Ô§;²O Y¢Q,¸H3£O Ý\8$Ñ® IKg{(+áiÝblÊ4ÿ_ù¡PŽ JE"!S4wP,po]þ•¤º);,³G¶2V‚"î3£ŒuÆÈ5“^@„¨€á$ V[²©¸‹•›Íâ®SêOح݃€©1×"†w¿YŽþ.«Â]`ýžlå±{ §ÉÓm6
+¡æiçœ(õ¢—üS¼ñùzeÆ4Žý‡‰ü×ßþúg³[ üTb¢õc¡Z®ÁCfn­MÓd2PÑA93óéaš&‡Wòù¹ä̓üNk ¡;á,Ã&x¸ ¾¢4,zßx {˜G>)•gkŠÙû´Œ˜@(-MÌ©àuÌ ™îs¤~k¾¶±Ô  nËööò¶J£næÂÇæ@¾vý(0„MX(·çÀ噜ÓÓœç6]v¯(ڞÑ1nà ,
+¬d£tÑÙb֖бŒ‹ýŒEÖ0±…‹/f©Uˬ÷þRU˜)„²Ñ]–Â1ÉÑl•–wj{’Ãë§í”bŠÑ– "à I,=YK9×ÈÚƒÅG±jŒØ°Qn,0˜ðŠ°¥)\® ‚@ôO‰ïø¸È0c°ÎM0¢II5.\sÊ°1)Ãe¼Ò>$oBi5ű •Äbëûœ"~F¡c:ô;ÄdEZ0 ¢„T+vàÐfL Äù×¥ã.Ÿú|Ä`læ¢|ë™JDèûðd U\ZÌ/Ë0Ò]†‹EÚ´’·æQ-–0˜½v
+›Š®ÓÁQ«™–ì:ÓsÔŠ»°&ñî³6Cò’Ôjº¼yer,ketŠl`´G#x$„À ‹<ÄãǸkœÞÓ$䡨³Se6q¥ó U×NWäxC’\¡ˆ!<S
+'Tê>ŽÿÇy¢‚”ÃŽz)I7ŇQ &7WSƆ) {^ê} Å¥¬9Á5`Z{Û7RÎ[D݃LÔ r UÁWP`|ô$IŸîç^Á§˜-h\‚»£”ÊôzŒ^{ÛÈ“Ÿ‚ÎDeQVBlÈRýAú ­uµˆ?c˜¶
+òwn¼L†5 {ödÿ¥³!6çº<Д†Ëž–ÍŸ3©‹™a`1¼…9OîßÇuµ¡àN“r¯Y/ï»:¬/{åŸÓ"†—1¶Ž5J¸æ`ãˆsÉÊ' ÍÊs‡¬ Æ›@ü ÞE¬;çë o+ž¿ö
+æiOýqÉf/oƒ•Ði&DÂf6¦(zœtíÇ=Ñç‚Ô$†¥~G~³´ ä‰RékÕ¬Ö—åÄb¶2ýs¨
+ù‹×‘×¢Ó¸ a8ƒK1{¤Ø‹‘–.´Ý[„™«’Ty¶]Ê H™kè
+C™ƒ ѨóèŽ(Ê#°­Xk(åãr:¡¥ t•i©½ ¢]i²žbáÎ5òGf|ohë=ÊîP·]‚àe)G{žK»
+ÇM»
+{mÈ…á`Ì®=ŠGChéÈçS Tª4?Ø}”è³48îsâ@˱iØmpJ‘L–¹R{{¡å¯¡@ˆ Â^-ÐÝj®Ô+Ás}ÒÀou„¾6ã+ÞŸ¥kĶ!´™G4Ú\r‘ ¡¦ Ø(ïRKÖZ<¼]\ ÖŠ„´Ï‰X0H‡_ÍŒRp¡¥»-z9QQz´)ÖQªšÐ$4ýÛ1Ï…\U•ýbÝû/ãåŽ›× Dáh®S|?Ê@)ÝfF:¥ÌþóÎð—õ_^[òˆ—œ9s‰G1ÀøH”ŸœºžŽ|“pH¹qüjaäߥ‡­Ÿ‚ƒæ€J ?lý¦†Ï6
+<§œŠPXÉ'ÁÜ(z>……œ‚ŠømßDÛ(ˆG¯%¬äŠ»9¸’ÆdŠ©Sô7£Â˜CT¥­Auîpe”\ß¡Pêešúô
+k693±!ºät÷íå\4Dý2½“ ýîDH„ÔZò¹CÑ»qVZ.É}áƒEÞÕ¦Pªô,wãÞñ
+69Tú¼$×ØBòwEô‰ýA»Î
+CM„?€Hé|¾êô†´‘X¿Lk%Ý®³’I3qß\hJöÄeÏKî@û|N[‡òêè·X«¤(Žj%bivµ÷â%JˆH4£íI°(UÄ}}…$|âÍ
+¬ªæ<w|c4lð†æ
+
+#míf©ñÄN«D–Ž ¦l3Œ‡P—“•@¢(¡ø´JüSœÊØ$ñîîNòͻۀ1þÎÒ˜k;¿jà!k ‡|×
+Ì'?ÀuÔèW™¢kô[Qw•OI\úUV
+3©Ú—PxžœûÙùœtšœÞ éäBUÁ Dà%myyk¯f2ãØO¯
+½_¶Å'Ð ßV¦9€U‚³Åcáz]JáûdùYY:y(ºJ Šr•­".õþ墶0‘›nØŠaâPTå‡"aG ÿ8à4ÎO;s‚£¦ð—³•àºäD±.)ÀŠY)x…¶{O±0+!Z÷Í2¯8j…’$kw?Í|Uà'ŸŒõRã×ÛcðUm$öaú³CãËPòL1ZÉzCìsÇ­„ã#«)
+¬óp—••<ñIfÀJ†‰†ìç(ä
+á
+}ܱmê÷YRÍlÞ^NE§öÅ ¨Ö¸sª‡¢'V~ÿ
+uËiÅo©4’k«@ oöC Y,ŸX°Ä‰üÿP´Jx2§ÃôÛàü
+\d­$—ÈåYÐtç±(bÐcQÚöXÊl¨$ö¿Ú9Pj‹ªíDABž5Í%·Tÿ:é©Ok7ñ´³ë)Q¼½œŠº"¢ÔCÊž}·¢´¶sÖSÉ»uñÇÜ`ŠVIÇ£^ú9Ðzd‚©Ônìf$0ÎR“Uð+•v¼™BŠø ÎRh~ãhv ×,¸ö‡£ìè>Š¶ó‡~P´ói¦¶¿t)ª´8æ ÛeǬaò `íSœSÉZ’³UÂøI5d¯æóî"Yå¤ ÎUÐ !•HK¨LËÅRÒ$vx°RÎàûˆåh‚á襶W¬ÈIåîvñkNf™XÉlètÚÐ(Å|¹Íâ¦Yko^-ˆY¾òÌÞÐŒ>`çšú´k·>Dër#Ù1Q}=!¹å“@éìUõ¡CÚ¨«<A<”xŸÆîîŽÑ—Øò^½BêÏÖÌ× ]…°ŠöÕHùuÿÜ9¹üÊÆù»Ñœž¦íŽmPJeõ…§ë´pÜ”ÀÃ8.„(o®¸!ã >¡ÉŒ«Û9 œ°f÷Íã•lC@w’½³
+ÂãcàC'žn+:D/¿¼CRcΘj¼[ô~-"?Ó#ö32´òç÷y›çq ˆ#ÔY·ÜÔ%ŒŽÚ9
+ú9G½9¤ëpú¨û¶4Ž.É7øªÀ²¼Ÿëø“­c-ÚþP&éÖ)Š„ÒœQ˜AèaÉo«„ˆ®!¦½q V¿bó’åñ»°çˆ‡9!Ó0s)RT¬ÚëYHšC€
+ï.{?³˜¿ü Ì0&ÒHÈd’ï%lVÏeœX÷4õgî¾ØOp:C=
+
+Dp5p}i2:’élÜÆG ¥Œ`*vDG&v°£ÇÄÈ™Ix ²As!ÐlcJ  ©¤£ì-¿1hÞ¸`GÛó]|©Žm­Éó} â©ˆý@ê*7‡¯|äÒ#X°´âaVÑœaG'8N€Ñõâ§HñQ_¤ ¦=.4@¸e;ÏÉVÑyzà¸À&°$xä;b`l_µ¸Šyã®Û¼øgäY@?~7ŸÎ`We|ˆ%qì»QFw%E®çâHVÀ”‰U- [¦Z$·œO÷åTí˜Â{Ù5o Tˆ¬Vy¦#ü†RoÁ&VñÁü˜2IÜ9^™J~óƒ×ÁCäÏNWÄ:…™¨ñù³Dê1sÔ8sßo(TdKØÆÊ‚´Rª…²B#ó!ØkÂÊ'^Œºx“×oa)Ö($•ñÆË$É#†¢'ðú9kyÉ[hÛºÿÖï'”›•%Ña…M
+Ì ø“ü#¬³×3ɲ™ÅJxpEDtmJV+`!
+¼ál5]-÷zg -(aõv0ËÖ -à 7Hû§õœ…’®v³
+³ÏÈ3jÑOB`E­óã4]Uj‹'Å“ºwúó>lR3/(¦îd¼UV4•¨Aßf“z):=sùŒëÄrÛ‹KÑKˆz·{«ÖUà —]ôuSÄÓæ”:ßæ3´Ðz@³éŠ³·‡ßÈ!+òª„î ð±ŽMÌ©…`ǽ3¦ÃW{cS3*…‡ä@¥Û:Ð9œ¹¾ö’]G=“ E†j—"âÜò¡ä§="GÁÖœ4ÏëðεżtSÛF<µ‚[M|L²ŒçKÑ!›è<¸ ­·¬¢ÄŸ¾ù+cÀÎ,Ÿ‰•÷+q0·8”ø#Ãè.:d <­¢ò%†ä[¥(UÞÏXÅðÈÅôU°©ô’¨ƒÖï­¯I„%x‘R›ÉZ«ŸÄZô° ¾Á™™`XÄ|[òmJ×#_ŠNóž`—Š8â«Ú-r.E' ^¶{«è…Îðz)rŽ‰
+›–‘’5Ÿ·&B°Æ1‡¢¤ ntIú°81b¶‡±kĈBÔ*ÑØs&üAf„‡“pøÄž1”7~XüÔCÐÓ­¾ô 50í©Z ?Á™…ì,S/X¹{DI¾¹‘÷Nx Ì$©¯ºY%aà‰ø ÿùˆÜd0-XÅdüC‚`•Œ•ô¢|™Sn~˜Œï’Ï|±•ŠXR2צå6Ì3 %/}N¶°Á¥Ö*Ò»Íî;Éo
+S.‘a«;ÂD¢= KµtF†šÆ¾R^?Þ]%"S“h?Ö9î§iä{Ëd‘¡¹ÈÌ>Xa5O<zœ7%WÓÇ‘áV¤#ç­êߊ¾nŠø@®ÀºÖm3ò£yŸ,€ ­(ÎD?2& VføÒ<$Íýȸ4q¾J_Þß å˜ŒvuÄ@÷„¤÷¦›ßJ|'(!‚Û7^×Iì`¦¹§w!yŒ=ùibÂÝq¾º5í`9×½•Ò– ?í<<|ñ"ÓGòS†åyüqrMhcu2Ç؈ócJòo.¹ñ9ùP©ª}ä^Áí9‡ù0EäÆœ¼§ <úæ 6¢ˆö¶g«"n%_ú|áR¹!K»Ìã›2pOÎIj&ÊÑ1=VÁëÁvI=wfÓPH=r‹‹hŒ±h†åo?«W×öÁ²b¡±Rüª;
+¶ ‡¦m…þOÉ«¤qˆ&3Ú;â
+KðÕ}ÆqtLa“‘u¡ÁØá.y’ì[¥Æ_󩘿C®$´²Y7øŒáG£ËÕKÉÚJz€Ÿ®e¹[‡¶Ø ·­iŒ­Ú,’Y©¬ôÀn~ïºbY 5—¢Áã0nXZ¬ä0Ô\öÒ|¨€ %Þ§Ãý_GÙ^/H-¸D;­AЉÌ#; ˆg'é@o 68r#¦é¾ÿ‰1Úõ"Cˆ%­~ØÂɱeì}+ئK â&
+¦®;hãã<Ÿ­O´µÂu†š¿±"\°yçCt‹lL -
+^‚³n r7H‡ãXâËeÜEÝ2! #ËëÏ£3tCFÙùcÐ¥¶Û €e8ŸÉ0xœ¤¬TLò¯‚¤Ùè‹b”³°­DÀ$ÿ¦ói.£ür©ã¼%ª‹è0ËêE$îLŒ”ûz·HIC\>:tÌ5¶£€aÀí°q*  B(¥½4hÊ œ"0ƒD±*ŸvlÁ°T
+(2Žê%„µšd¤¦•0¾| Ãߥ舯TÈþ©¾]4d»Kᬚ 5‡¢Ælã
+ÂðE®R«æ.a~&'ÄÑõKPzõ’l“Â[±¡Ñ“›“/PW¯E‡^pl,Ù)#w »½˜â¯›"® ‰Šš{ŸŠn“WƨÍmq UY‰­ ÅÎ0«=4γÓö¤§ßoˆ!f,â\ÏT¤  œ7­xq¼ôs’ì2'ýÎ9ù6ÌJ,¹[›†
+mJ³Yft5’«&â_¥?RušaöÉz½Ÿƒ÷-K`“õ¦’}ÒÖÜ}B­>xFöÄd!µH¨ýª„Oé™þ=Éâ—$ìL¬C@#Þµ98oDziâz(÷áËÉ/Á_Eè ®Ä
+lY“Ÿ×LŸñÒáãZË™dàs„úVôáf¬EãÖÈîeX >ã¹–• á„ÂÀB™…Æú-›©ä+&}O#”‡7I—dˆiçèa£`NËÛ¡ñHöµU¯àbZ°ú[‹dq1욎=MP2³`“ÝRÂßÃBjõÔ ùGRÒX!Xœqð©Y ”ÿ]®!D?>Ÿ‰'‰«XzÙé]·[S¤cµŸ¥Ø9 a×»q&.{ NÉ<;¡N@Øà[ÖÅåŠþkm’¸â\E/f/Mþ/è¿2åÏ8¸Û9ؼ
+*qêxL Of£<T·M„ ʾÕüÚ,}0®>ç§ôõ”WœÌ3Uc6¾GºéTA#j7¢̓
+æ„Ruqôæòº³Bä÷îÙÂÁoÆÑš‘(5•¼¿iKëÁósmÄ·KA|ûW vú¦d° ½‡ª“ ÕGsþ„
+|)Š=¤•
+‹K²Þý%†—16@ú×Kè{Åí,¸½Šˆ”¤MJ2ˆ³·¤V,åßÃâL<É`¬¾åÈŒÐQÃ.z¤é»´wƒùCgììÈL¼Ké…iÛ) ™×­Øn~ã2È/Û"Öen@‚¥¸bØuö¿®]®&êÐC¢Ã8ÚøÜ/3¦ÀÖè¬ZÛÕÓ ½$- 9ë/ѱÿ“†–œJ !´/¡
+SÈWÑh“Å §¢.ã#„&ÿ± f©Î2 Kà›åÉ¡-†)”F{#?\H¼`}Š:†ËÏòôþ‰eP>¨H³D1¦ŒŒM
+&8Þ,Øê`BªQûLˆƒqM×Ø!ä18ÝÏŒYOAŒÜœH,Ðôm€9 |Àt͘i5}³cïjÃŒ#Ð->ÜWƤ³³ô5¼º†ATt9Wëñ­„dÇŸGŸG\õUDž‚}É|ÀÃ8µ£`)é¦Vã’Á³q;e{ºC‰’èFûðŽºä]ãY¡ã ¡‹?7=ýÊ£šÙ©Qè{ȵÅ^Rò²Ž¡€ëHüûc (¬ma“6ÚÞ i•ÒÂþÈÿv¡pý-,§]±Û]˜—á·]X¤·ef×J>N€aÑë½Ö-3Ÿ›ÌE ›ù¾ƒäKÑ+Š ¾rîðGJ°rÀsÅŠó°~B¬}¸ƒ[ Šæ%„Aï®@ª]âYÚ{ï
+‘¶&ÒSõ És  ˆ‡/¶
+¤±"ƒ-âjF­Š,iŠmà’|<Õv_F¢ÁË·Ó[ñ&ÜsÐòNzßÒúðG:…2¦^Jžxé£èçZ„±eWX6|YÉÆK—"lmo¼D LJDÖÝ‹ÃÈHbÒü]7Kß%v8Õ‘z¯÷hÀã∑Nùž/àцa §ýZâ7¾ÒDW©<£=(˜ÀV<1:"IáÇN¶ ŽØJ´ò
+<H³=ÞàSË
+*Ç¢W<R‹×áÞª—tÅE4¼èrÞøy}À¸z…™ÂjG*Qé_QY ED
+ƒá ¿íELÒñáw8a¾E'†ì°‡Ì,¾÷·o0jFãˆQiÅk .~3{Á$Ô0yÆZïÝ<ÉP÷5(
+;a©‡h…<±‹°I¨ø@…³o¥à(ÏÀhït ¡„êKÎ>›7ÃÏ”³Ó_üû ;}_±AQg9Šê4Ýÿ†Eó].H‚XqŠ«’d 5üà¤&rÊ‚RÝ%
+˜ÈEöIf´á™ELÒù÷É'ÜÒ!Í”]›+O1q:ï5Ü“`
+T$Œu]÷E’ Â¾ÍyÁHB†p4%Ž™B  €~0y->Í ÉO·
+á.y^ qÙ&1e¥ÆFXÆ1~ZÒ!Û)}^¯•¿ñ#A1zHã«o;#H*êÊÓÍhƒšY*­+Lª‹]«fÌfŒ¦c˜Þj¤ü®êMóOðr9\`€1óq× ôeì<ôã
+LÀƒr•zIj8†·"z‚í4q¼ZP Ì%£ )r‘›²t—qí»¤ -[«¾
+Õà…Ï «}ôò®”ÂÙ[®ÎgˆKÉê¥ì¬8Ç&! #`Ö«
+0Î~®¾ãÎJsLPŽFΪ{ ’FβÕvÔZñeá•Eï.KH;È.A‰o„Ù@Pq|³ÇëÜ^ú~šb¢;ÝwïÌ ¿÷þsðíþùÉ°|‘7ÂPj©¥ù••»á*°ÄE C°ß® .ŠÚò6éŠuM¡wŸŠw‰ñqÈ|zŽ.ï† xŧ¼
+ŽÇ»áj54/èÇö‰É@™
+wA‡{ # %èãÒþNðéò¡ÄÒÃ’§<öñ+.úFoŸa4IǹãzMðýȘ×944SªݼIO
+T呸ãF0«¼Å\v¬MÀÐ(d­D袲4è&Aˆ! d§Ñ¤òúÇ]DL?u2ÅØ äÖéÍG8 0¢i¦ØV³ùT4A“ɨuÛ×JÒ·*žt…á4øÈ—¦ùÍ‘È…]ÅÓNWo”F…ŠC€%3­¸« yÍ ˆ)œÍ·zz‡'µûÅüزÚÞ‹ü,‰ðÓcˆ4aèú%¬QÑ3¶÷Ë7‘´wF¦JæsW$<Y†
+Zò扙½EÅžàfï¢>¬‡Œë~ŽŒ©o—VQBÔd¦Gf¿]2Ñ.}fÕ+
+NñÞ¥ìuP'ÒX«¾ó?” I
+¾Ðu¥soLûJ2аäÙJ”oX@kN9d 
+ ôâ·’§ã=`31ijqqyQ‚PÜ__žk u&¥¼å—xI؉ÂÀ¬]ô0W £„ñ`ÇKY <ezl…C×ûVþÌÅRyUi¿8Ù šçI[uÊx]’aÖº¡N |¼@Z»ÍpA„
+ç÷£BÊ‚Œø‚Ú.Q¶Ã¡ñNG¨ê2‰<J´CCÓoÜØ4§5@Cß<¦íuì&È”Gƒ¿d\…°`Gå6±qäÄ˃Ïþˆ]_‹>_AQ nUp®=eŸ»‡W‡3T>àÐÚ6g!T é°¦=zœÅ¾|W]¸ÖàDJ{¼S¨ä6" Šßò.SöN
+ú)'žîΩ‹—séÀpšŽÃÄ<v5—9™çÄsÂlHPÖtîŽq[}xZ‚W ᔫ·8(!™ð·`|2LPD4eÊdÜRÙë ˆ ‰v Ï0ˆsBLXÇ ”ˆU·+ðâ ‚L‰Iå›L–†W¨ÿ®·HVïyŠ¡Âað؉½Êe&÷Œ‡q6ÒïëÀÖJ`EJyN“-å,¤$Ío'
+<mIm
+çC`äyתžk–)a>}M3*€{IGDQŒz}QRpcfè?\`êåøù£}z‘Þ©îyug§™jÒé3ä¼ EîÓÏôß`$ý!øC¡h¹ S­
+
+ å)ÿÇx¹äÈ‘Ä0ôsŸ ŠïÚ³ô-fë¹ÿvSÊ»Re7`ÀÝÕ¬ø((’ª¼/´Ýr½ûÈ„I%¨ÚÝC*ç«Ê+P"Ä”öä%éÓrÛ^¥ÆL‰rÌÛF®Ä%†—W\DÑh!:’qæT{ ùÄÑïe ŒíE#ï•I¼_eøØîK UÈ›ëôæpt„x»g3m’¨"/³\`ÌP2‘Í4ŸæÊÑep"Òsì6»Ë;F[1È1戡†hŒÑеûËö#Õ¦½iÖÒDþ282Ïå@xiüä6.t¿Ò?³MÎ2AãÀ
+º;ÄNØ×â–ÛÞ­ÃÔC6æ÷½ßÊÀ'0û7—¢Û«H\w^ 2Qr­K+œ ~}§Øç‚šjÒmMñ0? éL I‹î77‚L£)šjÝØéQôÕ é)±Ñ£ºÈ :‡çËÁóWú‰^û¹Ìƒ1ŸŽ’ñîq¡Œ¿\­¤5j þ>º
+Ã'JpÜÌ>c~¼Áp~ŠÊÊ@ëŶÈØZ/sôóL¶è;_¾x Dƒ óÎØÕ!j9}K ÈRQäYqqlÙ…
+Eê°ÉùP˜j#ÖA·è†NkÖåëtºìnKÍŸêOO.ï,7;Œ
+ Ç!ŠGï8´¹ÎGÙcQ²2bà;]rÔŠ !¼2ä¢-ÎIcù:³Cb’²\Ñ»ªâmÉh@ˆ–4Í,.9 =Ù2ªÚ$9ÑæïÌŠ
+á.Ñ‹/øiÎ)ÿºYñ
+ë9›âô¦!”’X+Ñ¢€L]Ñ+z› òAeUÄ
+õdT[Iûéƒ2¸-iG%"’ËÒ”#ËnëMnú“xúŒÒ ”/(áXñHÍ®®±Ð"*qÌH#Ù· bz„1Ii-H¬bšNѶCHW‹öÝȦC$[U‹—xÍ'FÏ0¸š…ú1Ωƒm]¡Ñ9­×ïÎ#Ôu¦#xÔÒ 8‡ŠÓH ñ´z–gF0jd€f±È£¦?èè¹jhp@º¬Øíõ²·y44G I]ÚÆN!œI,4& TSëèâ4 ¯XG݇SÕXÇÔZ”‡ ,ü<5P$²[AlfUzŒÛ]i²­gØê[
+Â}3¸b!¡â—
+H‰Œ—MŽ\9„OÐw¨ LBâ(®{–} ³rß;%=•ïe·aÃ.dEJ ]í«wk¯×®Ñb~ýÇÂ^>gŽ.cfÄFdS1~h|eA¦IË©ýëÇ€ú«i—!³/¿Úœ–"ü—_ Ñ[JHæ¹êc´¡aÒädxo®iš_þ±Â1•=º‡=€~~
+7Ɇ²(A´oŠW%·!-¯BµÌ!B&¬äÈTª@ËÛÖ$®m:xvÛ=uƒÔ± ‚JÒ4KŽµNž]Š¹9@?A\¦¼z0W|imA’ƤçÇAÐa5@JæpôŽ$È"±C¿üÊ1¢‹þ¤JÃ?¸E[Ÿ)›¤¡‹ÏËÓÉh¸›=2ç r˜ƒàg]öxH1txBE4?´‚t*Ïorœ2²&®*5O+ð}o¼’ˆO-KÌe”*å×Ó´›¯šç̧‡>A˜™fÆ?p…Zþ¯ÞdHÇì"AÿCQ¤P¾ýýâí“Ú”^ØX d2ËSæ"2½-ø(flˆÕȃíªçñõZuÈ3p#¨lÇ•Â
+YXì@KÅŒ7Ì“œIÜ•à2ƒWê¿òC>k4v ÅÜ¥àeÈLí!cÍáwЂ/Ë8øu ­¤WækÖTq/5XpÛ§ž8ù†iæmñ«µ` Ûr­€gǪ€@MÝ­“ãòч<Ø4*W³ylˆŒr¤ÝT›ôLFü©Ög'=0#¦,;—§ ™â­4Wj;éA/pKe'å›yNÛ—²²wÐÏßé°¿v#6fz™ÖÐfgàtv>ÆvŸ»áð¨ùß›®T¨6IJVC—›¡jOôâ}n£ø‰¨Ø†T顤ÒÈ}ÙdzX˜=ž#ÉppÔ¶ë"ÆÍÈöO6¤^E¿c©„ÑÌX_´rä¢ ì.ôŽÞcå4$ˆåÛÈýè¡juÛ4ÿô=&?Š¬€¸â´GmÃók7ôAÁ`;b¾]±àùFôK±`w|º‡RLh‹·gü8…à çE#Ú§C0•é-~]ÃL"/ƒBç×»€0~).ðr°ÿFÁÅÔ¯ÿVzü•œ;tèµÖ 4 ¤
+6.¶ÞAsˆA˜µž?¸)·Ìö8ž¤"½ pí@@z8|_¾þØ#&1åCÝ}«+ Ïu~¬*»#- <ïûgS1~8Ëjv¤jíPJAùo€uMzJ*¼õ²
+: ñ±>›ís¤Ü{eæµä0dá’ã7•|ƒ\rŠD²·¸}:§5™è<È~E“µ:¡•ûUKVÝã¬0–šXËyÔ¬8‹1';(b-¾|IO*&™"·¶]eK#3‚†È­íð¹Þp
+è?¦jêÒø˜l‡'ô] ßÀsà‘5¸ÔK‰câ2ð÷s°y½Ë|×]1±M4‚Òà¾,U¾ÑPö›’ïSúµ·.NÈ:Z;÷—ÃŒtÞdÛ†½ý>ãò¹·¢ÏŸÜÉ #ÐZ@‹­DEœ33shŒ¸øë¦+˜YnùkJÀikUŠ¼J¬I’pîÈòCÜN‚Pì±l]—ž#¿øɾE]phƒ§!’f–0—uNàâo^Bö¬|cárª²ÿ–oüš¡Ñ% LxÁá¼èö†pó¯Ç,Á_‚°*_·J²H¶&I¤URð¼±#þ«$R[#‰´”ö§2M`W¹5ÄÍ4ò˜µÎI´ª‚²X¼D|}Ic4ÿ”RC¾u•È_Eˆnʪb/[»Ž#'È©¤ýULÅÁ-Gw{ì<Of5ÞT‘Ž EϽװ~ê nº6¿0n8Q¡”±"—lScžŠ«3®àh¼Þ*i
+Ç^Ul,QN§å0%]m_%“=1s¸ŒU¢pV±%#z œ”•þ0’n*~‡œÅ¬­x 3­*ÃÌëPôkgQ4„³š¸ó¦D`,X
+"lY~‡+‹û›—ò¡èó¦oJÃF`o’o/"ݾïÓ°~PUe«V‰Ò"ôBKÚ*A/
+©uÄül²‚%Ý[ñ°6ý^õý†§”üDf«9ì$®Ø¸O»™æK‰)Â?-i¹ÜC¬èæLÏxýËN[›ÄfÓî#ÃìÛp/ÈŸƒ:•Òsí: RH$­ù˱¸G èéP÷,ú8ú*Q>áV<cæˆS t”yfÿ™\Š*»Žr'ØØ™ÌæLãm-õñTœNòïr>l"œì<Á‡(BÙj~Rd«8LLnö‘+™Lü/WNÅ7•Ÿæì¼5§‰t0fGEfkå›®'*$©Œ=+±q‘VgcÐù¢øBHÏõð<†6´`}¸JriL—wl†Á³î#ìîÝcrA5}aùèb ˆU :K8…ÿ–Wð ÁOáá`iõøT‚·ãoáx'¨CVÆžÅ`”S¸#VÀAEå®æŒ“
+ƒ]±•-A5"Î;éÄó34P8Ö¨\êÚ+³9'Y½d9Í :Eû«KÐ!
+ƒÙ QN;ŸÓ?*¸ÖèX¿ aÐØ™Z‚+ƒÖŠn…Ð:¢dûSaøÜJlf«yý–+a=ý´fÀ¶8
+÷%ð½m…MO7%±¶¹«Ø“:9RW–›I‘¦Cѧµ"¹SZ)¦ïÏSðB~ÞàaVªhÚO\>wEDHÚUrþ³wpò eô—[ZXä2|©ø&¯LÀ¨x õ0¹î4Í—ß9¤›T“øL=ŸÁP@æi6®ÞÙCÙ4}0³ ú5˜" ʽÀù†'#)è¹–½
+ÝEób0k`ur’óË7%/FuZñÇ¿zѯÍxâöê7g¤aÙñkH‘ñüŽ¨=f‰æK²G•·§¤|€ÅØ Î\\ª­Z(NÙ¥4 EŠØðêk4]b±uNp¨V©µÓ,pˆv#¢±Öçy"(¬¸â âz:¥`ÿi—XvtŽÛ2à\½§…[a™A/‹ÀJ‘׺Špñð£2&¤ÿy,BaH4’ŸÙ•þÓe]5ùk°óæWÎrþúÑV_ ‘¥(°jÝÇ67
+ã¡ù»_÷Mä&2}»ºâbÆú?mkÀXT\Jôl`J~D5¬ïÎD +-«3ê9hRÒ xäª) òôî%Z~"›õ='ÒšbHDe×"Š^æþy,R…˜«rÎ(ëcˆ`â9M¨[,ر5EwŠÞA,
+ôO§#‘ _MñЛÌYsbWW‘²‚(S—×9¨;wäŸfgr¡Ê“‘),VëI¶è| ÈJhh±„@îËŒ&iŽþ¨,ŽÜŸèä%ˆ?ýCÃè¡H9'Ѭ±%ߘ^áŽXâuNLQ%£îÈœ ]—]ñ¾vH!6_MÚ_úûnþþ#‘‘qDv»œûÝ_ŠÞBÂ* "ß1Âfðï‰eV`òäN¸Óa2á9óÿÅ¥+bå!U¦²|ÐkzÏ^û—1TÖ´×XXЦXVIË`äÂÞÀ—’ù)4±Vü Úù#FE¾»¿Û2_O˜n«‚ÄÂ`‚?Š›½*ÊäüúçT„ÜBC[€>¬íº|KJ|0¥þ8Ýø]×/ï¾zƒÈóa˜4Ý­2«i{4ná¶!oàƒ,bH=p[=7XþýYFNÅBù9 ˜‘á£ÍüS½š &Ø­î+ÝH–µ?Îè:ˆ‹ð€ÛÊÿ„ð7ÑJ¼nD1[?òÜ×±ˆ-T@­À”Á?fI–…€Zó6
+ŽU*¥Ï£+Û–pÂ@{>`_§J¨õËN- £ƒŠ›w¢K !åLPÌT.Écw<g,¨ˆ·b²n΂°FòNçÛ\ üöª#Þ{`³êX,?4j¢³´etT$±E$m…IÇ*âs ¶¢šÿRCbÚìÁAì Œ4ÄÊ8û3Ñh^WÒ? D"9èÆ@Äù¦ÁÙŒ›SD¨j¨æ§”ôŒ<Á]\3#à¶Ö´à(Ô$d EWÑôCÂÄ69)¢„0𫵬s€ýèQ:êþÁ^“µìqW¼¯Œ\t1Ìý¥¿ïÕÁ ð
+~LSzx+Q$F’¸ISW@¿“¡pŒ‰÷ßï4všû;R#`º•ÇSX‰Š]ž¹
+¬q!§í† )B­•§´MÎ…ø-ñyeµÖ9"
+5n2TÀÁ0/)8²Èλ S§¢72<3ÆŠ6š®æ ŠK‘ȳ&´/>|äÀ¤±Ûl²Û”žæpIÒ®“%,Éfc5þÏx™#Éu$Aô¸CËÊ2#wq ¡Î Æ(±FœûÏóŒø ï,¢ ÐЋWnáË•ÜtmÍnWµè"4EÎ5oÎf^tž™úÉ,ÖƒÁÔˆÓw&’.ãèQE)`à ðóóŒCðS˜ËÅew!¸bÐY5ÈÃëæc£)äú$M®uÍ7c7DÉÁ’LsRÔkìSSÄ-sœÛïæ01G†t®[c’Ždë'±cY=÷+frË´dÅVô£Ñð¾ýÌ|¿cP‘íf’F™¡Úç³x +¨
+®ä¿Ž¡_:ôÝ®
+¢|n¥Ý” |îêa‚äu×üeÐÁwj;ÛÙPùЯg‚À¨pQxàÇÞí!ú"`ñÙp|Ю%ÍÓïÏTv&GºzäRÞ DyægÿÇ«®h]¾äÝ*DlYâ$¸UÎQ´¶>í‚s‘tð «]ýc Ê2þñïoIž›ÉáΉ“ÈA¦/aé}™rÓcöá> bç´œwÃ¥…Ò8jëaepͼn“Ü ?NªS0z%B-zÝêzþþ<žLÿõ×·?þÇ¿þ‡k@ Ê(Ëå-ù—\ì¡©Ç®p×ìye<q½cËÔÂ7Yê=Ñ=FB1$ßø…j­¯]LAªœuV
+\[<aCRBÍr]Bi¤ZPÌ õ2¥ ª# k#òÐ"O§$YäT .M¯3=¥¢R‡VæÛ$>cغf­C/1@4#/ëÀߘÖ0°Å!°*®‚9/ñÖb/rŒæq;ߪ>”…LEªÃA™®Ç¬àò ©YóÂP)ò®¶ v^ }¯GoEæÕûëÚê”±]ìh‰uèKz¯¢¤¥ôëñû­°rŒŸlŽH¢?ÌSWbÝ;ÁI*“ùðÞI,A³uzˆ&ôÄMÙ" !jò¤R½¥*Sc
+¾(šMAø¡ëDð+ú¦ÉÙm²8@b+èiNø±7ëPxl?z§a˜W7Ù·€º$§8„RÑCõ+²+Á&8cr€¼y ;·êŽ1ÎJ@ ÃõBQOÂíÏÊi¼Ðˆ0,¡fþÜêWõd´+Š2_§fYãÉË›CT+“ï)qH
+Ê¢ký÷”ÍÔØŠ¥×>ø—ÙA2òX,Ì“S /¡÷ä½Êæbˆ-1†ŸÇ±–ÌX’!‡H±'¾Ûã‹QAæ*ÖQ²•€ßbîPtå0”©9##|D7÷Zò!ºÝvµEèð<‘Jú9v‰¦WörõHtŸžL2Žc'%Ãí‹É¸úq줙¦²(áÅN7Pa:z…ÑÉA–}„ñÔ4Ð7JŠh4km£Žë” íKUBjÞ  jxƒ¶
+ið9hÁ½‹a¶V` *áá1±Œo>Ü*êIGÛ–~‹'¤šÜ#IdbÞ—‹™«±NMKÙ°Ò˜íØ97HŒ8:B]°&Û(Öyq™yw›Vîv„ÜÜêÎœ~“ƒÒ OJût’H69—‘oc›X#K1I<zR®íømèxæ`åÙxYˆ“¶«öÛíÜ€ÿ雪NI­»e’K,­Dä¤`kŸŒõŒiÈðY÷¸öÉ†Ô rƒ ¶çßÓƒ E¤ÇgÞÚD¢™P÷Ý'Ç'v ñm #V%ÇüO>ñ°¢×JEk,wŽ'P Y_hô7Ûe="Ûh6'Çèù•wÒ“rtèÅU\…S:‹Ž){'ÊÙÏ™¥wj9”èÇÄ8à+!RÃ7ôë9‰…¡ÚQð« ×GèkÉbÖ‘‰õ¦z¯ Ó›³G²‚Êü2h(»0×[zÏ/ð% Ò «  I6ÅA÷¢ÜAϯTîÇ5Íp
+ïÓ¤åÊY¸“¤L'³¡"3~Ðb“1ŸêWÐÓA’™'¼Ù´!0
+[òL‘׸6‡0Ê€ Ócð.¤Ý«T<…ËÖKz¦qê+¦2ɇ¯ù³;ƒånX&K†@æ2Ûáf7+5k«h<ZbX p°~÷[­*K2šóÀ­¶aº”ÈïÇBüŠy~¥XŸ\¨-Q%"šœIÚdÄ»@©¼®÷õî GÐišLdw;ñÎt˜¦ýVÚwœó ôôªàÐ[ŒKîІ,
+H‡9‚R'ñ`}·ˆä¥ÇÖÎ>=¢d|cÒÖ¤…wý‘$êÌädW@ç¡Oâ ›ÖAQž€ÊU]¦
+‹ÙODûüJ'þ$£©xÅô­ÎáßX‹>'vŸYÒ=çÙYà¡ÛÒMþö7Îâ÷»ýœ&öÄð®Ìµ³³Ðz)aÚ ˜õì,ú„%\L]g'`7M2ê8°•ßœŽ_ø/urK{3Mz,‚õW´}7rù!Öã Ôåívd!&C[2ßÔú·ï´kÝœNÔ:ÅeHGgœš…‡ÙÏ ˆxQ¤Y¼šo˜™_™ó–öä ÌFÿ<Rm… º†¦Ë¼6‡W
+Ó$[‹IšŠ+{ò”^é(®ÏPÇÕtßYY›t¬2¿OišˆÆÂ×Ë}c^p[Wh¢½ŽkT1Á¯]:¹¶ Å?‚d±ö|“.“´× ¶Q||SI
+®$q
+Üë£Ð"Ę²Â'1ä EÕ¹&3ÇlŒâÒO¶™äg‘»8”6»)^A…ÆF¬KWÂÜGÖÓYš™¾ñã½~hðî2¨¾Ç'âÂq †wµæ™`hã8ƒõù!ª •CR¶¨ “ÒfŒM±ºK‡çb…Êî´ïh“0À/´Üy)BH‹·Øa^Ö3YMÓ-Uu˜QýR´œ4Ïâ¤A(}7N¸/Ò%Á’_à8Mú¤(?P™:ú%ALa…š‘ÓÃÌÝÝÎ÷˜*ED¸N g€ˆ»r»ÅÂï¨ý0AÄö½Væi•„yŠóTi÷bÀÍ!@â˳9w€ØRÌ^×õÈ –/Ü»îVV_èûŒõB¹½Xc³ $aþ8“ß@âSå$?¨šeOæ£ÉÅ'‚Ùçu ÎDϲ™ÔÁ£PÊcH裹ˆ•²\$î-WÊáÖªýjRµ1$ÄÂq)&º.I}ÛæV¶å3Þ:ÔÁظðz<Y@[ÙçvÙmÄZÂ&·¨ç„ÇM6"Fu¯ç (?è}’}~Ž _¨à Eo_WG69²:Þ!|œ?¬Á?×+WÞ Â£½²S“|Õ¤|ÿg¼L²k9 º•¿‚w€¤{ª¥èïê@I~E–¥íc)DA6Ñ vuÃ/Qš±HÜ‹GâCQ¿] e3„Ì
+o±Ž/,’*²
+ŽðK¬+gi`*ž¸yNP·ªXg~
+ƒŠ)B|2§Èó>ç<ÐñºˆñˆŠŠ#÷AÞIG1½tócÅWcªÆ IMRª§¾`íJiiÏNò
+*Nâå6'!Ëþ UŒh\^âú\ ¬J4ä§;˜‰@Ú1ÐéÕI:Í<YZŽQ@òøÛ± øšÈÛâ¶,I 8S:“1.eß™D©|õgB í€XÇ>¶áíD>=.O7£µ?u!"P÷/èK]WFÓPP´íI?À^Ó§…аÉÖó7'â·ï˜ÏßôJm «­<I'qqï´u á­£†&®¶ÞÏ»ƒ>\3•YëD±ZŽÑÊoAÞ¦¨VØ&Qžb|î€ß°¤Çã !°±nÛ·É’i”¨X3ÂR`hÙ¤.¿“ºLØXοȈ‰L\Ѩv¹KSËePJR–‚ÀðÇW{­éÃEž4»þb‹1“\`ì©>0pDÊr¤ûiðŠ4÷
+þ%î´&JQxÃäƱ‡ö
+¡³~ÇVÁ‹ÅnÐ0Ó{}êÿ÷ó?ú“_¹—fºûp:ÿ×Ãð\êEž…mç½3ɼJ9o'?¾Î¤²,pœ 6¯†Š¸ó£¦™äÁì”ëÚÐ9ÓK2¸,*Ý©Ú„}Wÿgì=/ÈЖ ỗÛöwÄ0¸—|ÉäZõNaL±:´E4t]…-FG“
+•&ݶ%ê”
+Uå¥[‹ˆ–¥’y…V¥)AK^5òœIÕY6ô¶M"äåw}è—reCf oB¡;ùlc;à-ôt%Í-ç2L)TϦÉX}I ²|úº“ÊòhnƒÎ=ñ±îÄøëV‘íyÁ¨"ïe8<Mš üs•ª~üô9ª;L4L;ïD—dj‰oÿð:®
+ù· ·æh-zi¨`NO•:Aïy˽÷¯0ï ùáUÒaÑ«ým“#»§6(!\ÇÝmò :l2æWü1×&aƒx,6¾› ˆ…+8ãÀò¹69tžÕÜtqø[ „/ža¼Â³ÀóS‰sð_.>½ŸCp‰d$¾’k“A ·°Ùv®›g“£,©«µZum265‚º·æÚdAn€²Ç‘lòòlò :l²®L¨äeÒ)×&Owlò½€§Kž]б" sÝ-Ý”0àcÉöà’çTèSæÆ­Ù*x@VþÁ$ƒH”œëô™À<¬I7\Û,»Yû‚‰¨sŽ‹ë‘•ò<òËùí‘£ä1Ed­›yñè¯ º»d‰w5]b\fðÝ%GŒ ±Ýíi’(Ái¹ÁÃ#«ÐÐCÐ /ÏÝžM?=2³ |IØËÍÁÌ1Æßááxcv-r 8Î(qf4ʃE1臚99 dir ¾Gž`gˆ&?xää°û‰9<²î‹KlØ|Ôé‘õîÐ
+†ƒ™,Yå p¨0f×#«t‘‰Áe×ÛžlìxdMLjqXÿÆ
+GÌ>HÊ\¬áD>SI­ôøà‘5ä\Ÿ/Iµ]¬MÁŽÃ󔾺ùX&Ç$ÿ¸•ßB «Ž#Uª™ÁÒât19û€ù —ÊÞ„ˆÎàOðvùAˆdÞÐF^S¾Fè]ˆÃ”„©QÜÑÐ
+<žƒÉls—ÌÞ”80Ð(݇›ãƒ*ÐEU6¬ÉUb 9o8³ä*ñ„ðùlÿð ÄÈSât(±®lБÅRu•øxº£ÄGO)ž} 5`ŠÊÜÓSBð{v V‹éAŠ™ Ãõù·ZÓEž¸Å\Š«Å@l:QDÎb¬i7ö²°¶ÉÕb­ŒfŸÓ©™«ÅÇVyZüã~~kq(ˆ({8 ,Z|‚îZ³†[AÌìRÚ›±Ùhä,]âuc.Î ÆG×Uc•ºPhv0WGϾŸjÌöÒÉŒa5÷òé.îž3"Ò;\é°Éߎ—Ññ¦U‹ÀµS`°)¬sY´w5žø#‘!â2ƒ§Ÿ SÌ]ç}™?ª7‚EOç³õm\UÍñAU>l»1‚æj±:@Ä D쾆ž„ìh±æÁtC(1˜S…`ð?­—êj±†SăfíA‹5äh/²X¯Ú¼k±6…¶ ½ïŠF7->–ÉÑâ·òK‹Q<¼§~ˆÉ×bt×bA`¡
+j0˜gÂþº ›CE4!cÙh£'Å‚èRÅ ª\])ÖÅ!Nª<2äI±JÍ':óTÚJ 7uú~HñœÂwsÖ³{
+¥g’+ƒ€ìùR_"2ú›Ìò“‡We¥‹h'GO‹Ã˲ÔÚY¦òÔb ¼^-øš‹S‹ï O‹Ì©ÅÜ“œ€äò Å¼[ƒNª õA‹gý8$j¯Øû®Æ³ ´» å(WEN>ÕxŽÅ¥¼)_ŸºŸSÈ™,CÌS5Ö|Æö/ãå®#ÙmEÑ/èèXAäá3Æa§” ŠÔ
+øïµ6yؘ¾—…)Ë64Ò®Kò<ö#Ìԫ·³Ï9×tÂÔPùIç²ðNHXëvRãû>ÝÕø׋9Õ¸ôòÈ|SIúÙV/‹%!¸cîoÔ‰HŠªã€ù8aDiÌqSãKZ-5ci•
+ä—A¦Æ˜‡ÕÊâu–EslJ[udRrG±mZ#ø&Y¨W1Ô£@Õü&寧…9dºÒË ª‘©¡ÂàÌýùeµWS*lZiÖ¬¤Õ»FÙUþjJc$‡$_W?`>N˜CãÚÀCuF½‹7_:×hÔèüGYÇ^Š©ãª!Øì˜^Ýû{-À+!Væ“ò[-.̵!WÐç+]óæ’ô`­•Xi‡*Íæ†YïÀħõ¾‚\y`eÛªRm1«Æ0Œ×A‡ã*¬8d½3 ô*èÔ¹Ûq¯N¹–à5Ðiš®m¹‚>_éÝjqÍzR¾’ä÷±S‡+æ·KŸ±ÃKtî£yÃœ*^ƃmBmó³Æ}GÎ) kBŽDˆW1§žÝ~(⯪³jX"î@æ©›»úHc1H`ÿ<Áa#QN+'פ(Åa­kÜ>&½ezO 3B
+™ÛöÜ× ?÷ÉϹ`™ALN±¼ß¯’ùc¡T‘îævzv:ávrQàµ}Î¥,lcGÌ0ïÀ¥¸@È»¶¼x<6éñ&ÅÆÍäõö“®ßadÈèBÇîÛ$gNŽ£§ ”H$¯Ø£waŠcA6<ÅØÞO{•0QÇ ¤3ä{›~̥˃1ÒŽù
+
+J%•š¬5·N vÙHu1RN.5Æs^H••¯xs?Œ'KMgºü9ž»úË!ïFC-u zy–¢âyi_2oÆ‹(!¿ÓwÞn‡ìFN§ –ëDð~SÏq.ãæ¥ZU™ôù@Ð;
+Ž«‹%ìùûž,µ’Ë‹=j&0Ë$ÔcÎÂGt¡ýáyŽ5@³±:õ 5ÚË})ÕÌs7Bl2$|mrñDØà;J\{¬ÃZ!a’ÇtAT„X¨ûåfä!:—=ÐñÇš=1ŸNI£UñÑ­6¡Œd˜«Í±)*NÌÂösS7¡±R«®ó$Ði(ð
+ùq*âäʈ/#¥X`sŽ‡ee\ †@Tºò r}ú¼Ï t)âç toÆíÊ÷–þr~äÃûï½ýö¿·à“§AŒ?O! N­O·Á…M§úô{½® uÑÖŠ"~‚8鵈cJoƒïL5ÆÆ°5’¹¸ U®¾…‰í^­Q2–]¥?§>°º<´J“ü6ˆ9†¾ç± °(̘g ]¯â‘H„Fªí£:Ï¢ ¨ƒ°¸ˆ"ö”îÕ&97>§B )±ç
+&ОC8\\«0)¡$C~Üw”97 ,$×€Ô$ÑÃÇÛ¬Ž
+¢XPæ#N"ª“àl¦vúI 25™QjÑ¿£±ì>Ìí£•éÔñœ?×â5ƒ<z‰'Єp¯Þ,yU\(–lFŒû´ëá!…0Þ±ºu:Š¡Û¤œ¢}„3`r[ÀÐæãqÐç+kú1C_…µð½èÔ;d÷À,†móÄyx¡ÁxNß<Àõ^-açd#OéÍ!‰µ0ý±:ÄdÒ°E0¶os¤µŠ]b¤º@‰õÀ­”îGñZúÇÊ‹bDúÀÒW Ä‚Ð >Vw>ˆ»/ öã°u”œwa§ÑãQ\6c^g (6þrÂPæG(«4!,z°¦ò¯È¥[C„ºGL@P!Ñ‚Ò/®+xõV!Q•§™+ùfÈ!àÁ`ÆNQ^Nooû¨( HÆ3æq}Ç$’ÉzNNÎlicTºŒ£?\a“ùé‚(ñI€Xù²B© …¥'ra¤þõ‹y5Âd>˜´Úy'ç úã΢ñŒã™®!õqÝæþÌ1qsHM¬ï’“1yM.òü&@YòÜñý1kN!Ïáø6Åö.SOBg}ásŒ}™ª¸XC+C¨CµZèÐç|IxÅøÆ´^ŽïEõ)qõw [&›}uá{‰/߆&U‹Ùvž|¦2õ[Ñ/ÈÀ°Š„¸ä‹§”%Sýº ‡¥
+É/Nl„˜w–êÞ…"·3f"Y$ª5lâï´WÉb’µ-3&™¡ g*‡\Ù UØOº€ð?2¶¦i}G’ÈÁä'[~ÁÂý^?ÞMmÈaP¸CZ°^¤‹¹Œ]?… jUóò”Š‰Cq±B‰åýÀ6• =S€gn¼-Ô±§œfdÛò§¯ÈÜÊ*âÿܘ˜²CæêÒqFøVö©¬ÛvÉÊ¢ŠÍô©J Ò∦aDјf.ƺåŠþtµ<kãèŽ-HRèˆò ¹SÍZÚ1D?’ôzµÉH³¡ÊOáèZ¤{_}hì¸+8ýÝñÍ(üXµÃ.ºÐR=`>#WÏÌ[Þƒ%ãÄîàmm˜åLCê«0øJÓ‚ÎÞ$¸ãùè•-Ü´rû‰°äkðú¥ú»1ÌÂcÁ‰¯ŠŸ˜‡¼|±Š¥ñcømè¥EÆ8­µ¼@|<õòA}Ëxòƒ£ ‰œ‚G¯å 䛫SÒ‹ïÿQO±re÷`ËMÑSzþ/ãe’¤G‘DáèµÆŒßb–˜XÖ¶O€±¢´ìû÷÷Â=
+”Ù2@‚W‘>¼Y¨ÔÍâ\Uiß,S<€DA’‹&Ñ!Ô —ÈB¦dI3ª‚T=Ü{˜‚‹€ÐvëE:+z0Do‘ï Oâݼ«=³S¹&Ú èXçÏaxß-zÊ&e“ßä÷\Ã0;®G±®cöTx}xq;@fñtÒ‚ÀUøñ>µú ‚Oi²’MÓ» T®ð~Æy*a0¨£íÄéB¾è=Eöi•pQ!ökv;‡ f\»šï”Ìôꎚhí…P?¿tÅÀ´²ÜÊ8…ƒil•9®ãi(þÿpiƒÍaÇ6f¾6%¾Ä£b“£X7n:DùRÒ›¥‚ðÐWð·3&6šçë‚)™‘éÝ Åbr÷û'Ä‚ðèä_ÂW7ÉR >Î((„ {tdø{‰BQ5 ”žùuîçT¹RP%‰D]´¤°°!1æ24æÉ£$Ñö¯¸R|sfš0ÉëW(Ô 6Õ¹LêÆÏmCt_ã+¢)+à°†Œõغbà\†'/÷c7
+p¬ÍW]ìM†ª8—d/Z½,î†
+&ƒÄôä¾k‡¤5r\o~
+ØŸ‡ÛÃ.—O‚ÌÓ|Tš;LXú^™^ðøµ¸=¶V¯"?Ès=ÓÅó~ÆÔ•7de¢ÉÖÃeeYhÝ
+¯B`îaDô¥¹=fÞâ ©L§@(Ʀ·zO{‚å+ Œ9Ë9Þè Cߣ֎+U_òÆhr8ky@ø—xÝÅêîèp?f­œ¨£y ‚æIâÉÚ ŒÓ@ðe3B)s˜–èã
+‘Û)ߺ2&Z×ò!½£W8¨|°Ûep¸5öÓ"ùì£äÒ YÛÁÆdÜ”}iÀ²2N¼Þ0cnp’ƒÀÒ¦Û<ªÍ—çîႼE—qy€à5™@†¶LሩEɘwªEŒûpJÏËT!ã¤LOÐIJQ»}ŒÝm|ƒpà¬qP$üt‹Õ XzÈxh¶vpߟ@pû>ÐE…>¾"UïrWpÔkÄ€óº6ÑŒˆ¢élé#ˆžáFÅÑs4^#Ð<sû"ä²ëïÇO} „u°¥ºM{ÿ£ìCE–aÍ7IPÌQ#ˆao¬@H}ÈA»$’ºøÒƒ! "Me’=è^,;o¶cèbE9æû膩 Ί(„›ød< ú ÊüÏÛ9~@+ÙÒ´!¡óS§AhA¬,XHc_˜a†bykÛ$F¢brSîÛ*Hà÷™³±I,Ê6ùÈìC[½0*iúÓû?Õ¿³o`q‰xCÏ“µX/OÔ˜Šâ`Àó9 ê#톞ô[¯”fÖÒ!tnH¾ûŽc+–´!ÚÃo˜Ó²%™À>e<Îò tÚ‰Û羺léyâ/ ßxò&¿I–wîø)výx
+´ .ÃøŠ¡ã‰Í„àÖ§Êd Ñ*¬¨b‰‚mBZLètuÆ´d/2\-¤-ç«O•A£¨]©a'¥Á»*Á­ù9 Ø,,q.y©J@¬B` ’ñ 6ÑOEO¾Î3ö§XcNÖÿqcäƒ8¸Š„Ùõç\|†¥`né{›üK¡¢z{ߟº.J·a6ä F¨G±R$U3Ñäö º€Ä=8VjOôÏâM#Ô)N]’v²
+·Ï¢Í"ÂØt\z鲧½ûËËÊ‹C®ÕŽAE1h±ïòñu|`©²Ê. H$¢YdIL5ž½‹#°h©yn¡Àܯ1>k$@°ƒZD2¤»NMWÅ´ö
+Æfk?è΋€@•²Èwü’©ÌI]W{wáAP"tП é%Okq ­ïß Èd'ÆšZ1NÍîsM…9ê)“×Ç.WF…‰4PhRÔhÌÀíé7È÷Co îƒÚ½–ñ‡+Ê £ìÊ‘1ÁRíÛËËuèZÃ#èÚ‹ëï ý÷éÑL†·ßþüöËù·ýÒØM¢u©áª|À½ñJ¹_ˆ§kìxóKÊÃtC×,¨d$º ÿT.ý.œ KDÁ=Òk©ÜFAÂ_#‚Q}¯e™‹6B1S^ƒrî“X¹È^±`ó Äš œ³œr Gdš ômƒh‡Gxüܘ/‰~ÐóÚãâ‹Šc$¡äEBÇ×)ŒU™´º4ðX&‚FG>l&. ¯´n…Ñ·ßu{xÛ á-¶ÌËcʶˆ™_]Ú°ˆRÓxÀ¼Ÿ1J™ÄÅî%L J–¹/{¨R¢R).H–D*a¦j¦‰Wá, é²ö¯Ü ùç@M;Ñõ«aØ‚*KÁ™¢¯ÓÎa¢b."ã…Sb ?UÞVu–?ä·p‚üa%d’Œçm
+@t´º^ÜAù%ƒWR‚äðÖÎIƒß­ó”¸­à†’ ‚eT0É!ø,[Ígç~9Ö„úPèÜ D•×ÎR>oë”nTµ5°#ˆA5¤=Ï…˜²ùå
+šœ÷ÄÃÜÒ|æ·ûî zÀ<‘ }s$;ðØ9,6&‘ÅÉN”7+‘ÀúRµ[M›núiøö\ìýH†ЇƒÐ:ÙÉfëNdÛ)Pæ¯heF”€ÊÌeŸA®ËLsŽ5ÊØèà´Z[ˆ$”±×[3)6§H‰»@ Åw¹×*Gܱ+¾aÊJ°jÆâ9jÎèüñ2G®+G¢è
+¸ÚeüÀ<˜,Sn¯ £=Òìý×¹È%¾‡/JªT—@w˜­ »éxO>+»ñì;£²Ï‹?‡AXæºå kÏž’_±‘=b'»ò¸úÙŠ݃›$!ÛtlfI‰ÓWkr¢SÅÓe§ƒÒ–z2â{ÂZfYþÚ ÑJ¬+‰elN!.ñr'À`Ñ<ú”>Îd'i5©ÇÉ &ã"÷v¡5W’ã}æ·ïxòí§ââô +43PmȪϺæÇT¡k/¨dòj›dÈýHt²T‰kãR“w£¡)4 Ó1¢½?)-ÜH~Òà. re–Ùîõ||—Ï3A«Ð.µpAxì
+baß÷ d…Uõªû>÷ïÖx–Ö·‚vâ*—,D¬É¼ÿD­XƒÈÀUœÕÛËs±ï/‡ƒ²–¤CyP£ Òí¶IN«Éã4âñÕˆ¿:tÎ…ôF9¬hAáãçm´!œ\ÍÐJˆgˆûM8WN‡ORÚ­¤µ|û8$BÁ»&cÍ×¹¢rÓ<4—šŽè4y©†øþ<ê·Cü㧅ÀDŸÐ­ÓVÒ–cÄ´î0o ®ŽyåãEÙg!ðêðlØÎR]äN ÃÄ?“”
+Ãà5š{–.~ W–åÖç<  Z˜!²Ù“ŽÞ¦ãþüh³V†VÍÜü”¹Åz;®Ãœ #@˺ïaàqÇ{«teZ¸À«ÄŃü‡ sà)X
+-ŸÞÊ3–r o‘5>Ø9_B¨ŸSÛ¬yµ…‘o¶‰Æıžšqxß/M=}‡²#ÜÄÂôĤ2bh<ºŽAÊÏL*“ZUÄž|Tï&•qÇbdBRÂ0tãõ¼6’UÎU´û¨ßïÖ
+zÔö¡ÙF< *å?Ô« +çf¬Ä„hHúê Ø–s•¿| ƒ”*`É4°@¨ˆ¬WkçíéSBÅ”«$'ÈzZà:RÏĦ'ÛXxX?³îÑw¹ÐO]&j¡ ‰ž##Æ+ N‡40á|åíå: ´.ãզ… ‚^£f—›CÅVÃHF¹ÁÁµu¯l(a$î,ˆöòÒ±†ÄØkâÿÓ3TG«az †‰Gå:äwêd(sZÎ>ñt¥Òa'Ýĉ~bÈ0I)÷‰ÈÓ¡ øòþì;DØ9ʃ)/À©nH¹Áª¨°Q²NùÀ¿½œ@§Ft8*ˆãqŸº¬ÅûŸìŽ¯XY^+®!Z›4-'gmÅ ¢
+ß_µº`ÐJD£`¯§«D©¢¯±òÒýIÑžT˜<to4²jKƒ½4ž„üwy2ûj<Ùulº3a&|*³0€ãЧÄúÒïów.Ss¿ÍmúN¯ºMñ½:‡]¸Öø»z³Õ£ù0—]?ìúǾQI¢POéûºíúé¸k|w'±Æ_ÿÁ"ÿý¿—¿þÿ1ò¡QÃ@ø‰ˆ”ó§„L°%ëGÄí¶€ëE:e¶À„M£×‚0>üÁ†iÄú,Hoôyâ;«H?¶‡L3Œ
+3Û
+T%ê%òe ¾
+Οª)1òÛ­Ø}(Ô̸ÈÚ ìfÖvÐ?ïíÆúùV»O@•2_ífÉ„a;#5Åq £7’*ð‡¡ÚÎ0&$‚¾ß3mè M¾át‰“–Rõ8‚©`Gÿ\_¹ª&Çì L&.t°{;sÉ3C˜?Æ$+´€—%ºB¹ª·;Ëاˆ}û öËIͳτªDž׃n¤•¸7^Ä ó¹ßbú×t^QäÉ(¯Y - ^bdBbäN†)ò,œ…Æ+hAØ1¹Onâ4I`Ò,7¦p¤áù`pp¾ŸcYpÚX÷(K£H$¶õ
+c³“ØuA)lUÞßñZ[Ó‹C¤ªX&„t“6³&ÒãV6Ê7oZÔ6¤Në]¬=W@Dûôî5¬l#ó]g:€ža#Ø
+#TªÁžCWŠ Õ «9”™f¤"1b2»ÏaF D“bÇ-XYn0ŠC_‚ZDåÁ`ÊêTµ’-=ƒˆã2·KÓ<ÇÔI#9‰Q“—YS0ø: y6¿/ b°A”W‚xWé® QE0Ùn›C |Ó¹U2L—Qmt'NŽ9»P¤Ï.¼\ƒ¦uu64…ˆ$Uƒ¤ Ø!v™Ÿsˆç®”Ió£ø1Ogðì;E»Lƒã–}:C@´%?èñB³4”…¯'Ђ$qµØÙD”ÛteÏI”«ÐÐB3×¥ QÅ÷²Qh”`T¢_ë„ÞO£Â—Q$ªí3Œî-™ã¼ÇHaιjT¨ÛG]@T…r‹‚Š}§çÂúQôâ³¼ª0e”Jµw‡JW òT|%æš–à­SÚõ“…JÛ¿ùÀ¤Âkãxæb*eƒtŠ_²ÜcwS†ŽûÛ¾lÛ7ÑT®ý„2. O@\PúSᶶ)ã¢ç],K›Ã*U3¯Ì
+[¶¿™°…Ç©iP[<€~<iqàL:y™
+¿C„€ÖÀ¹É¾‚Ȫ/,”—FªÅ!üÖïû–ÿbzÂ#Ó„&Ç’NâÃ.Å,J¯ ò|2‚àMø'Æ}ŸkRbD&ñÒ«B¤ºC¢Ñ› Bû¨"˜g´÷x~*öðçä~„‚rÒÃêßIÐ,RJæro ‘Ðd¦bö»éŠumÙô%£¼AŒ³ì'¶†¦; o•ú„Üâž ˆƒ€C2Hç£ÈÊ!ÿ2^.IRÄ0½ÑDɶüY³ål‡ûoyY’;˜n7L°@í²¥T~@h/µå{ô†A'a±z&lÉ›¦~]&3â «áÎÔ ž9µ÷%#ÿŽÂ_6ùõ“žñFòÏm H¶Ð å%’7¹Û¯H‹Ú˜qò~Áç•\æ
+´¾‘ ‡0„µÂ£„Ô8þ_Ïìá;׬CÑ]‰ÓbïÑ0q)ü¿G‰Kø˹K¾ˆÊ¯øÚäS.¤¥ßÀÈMˆÊªç°` ]Éã ]^—w%Ä,Y;üOPý±SÎÌ‹¼KË+ßþºövÂÎ7àõp‘jµÞ¥øÕ^™áwÖ RŽ
+t(ø€>Ïž':œnq<1/m¦â”†«·D< +Ô:“‚iê"qT] ²š@Š•Ý^´ –Hß·ñöKVï¡ÏŽö¹AOÙgÌÎ죜
+¢ŠAARöÁª]&£uyªo„$XÀmfü4$­‹",ödI,Î ®Â(˜Ñy‘%  óeN½lê© QŒ£›ù©ñUE´±w‹RÞ
+H‰Œ—A’d9
+DOPwÈ Ì7!„€u÷rnÑf³ª¼ÿv_ú5SŠî,³XT¤‡$Àq¹Üeta-ƇÌÔ+͆YF“øø—Š\)£ #F“‚Œ«Œœê1FAú%é¢nãã¯7Ĭ¥fÌé¾ ÃµK}Ìæ:Å›õÞ­‰Ú‚„kæO9Bô­Oç9âã'Œ]ü}ôÙ£Íá?7HºEtÉÍK|H37‰û.Ùz‚é b"!Ÿ ß‚–4©¾ lc6ŸÞüyÏ (ºGºç¹ž£—…O5RÑÆ+æß?þóC>þ£ÖÓƒj¼vÞØxUØÏ3&C¥eŸiUÒ¤š“{Y)"•TB² ÓI^Ʊ}.ˆIªúlM¦ÜEyµÎ»{E¹Ïñnj£µ ÿ¸ä—×4²Û‚À’ŸtÏpmjØã
+âWs…/Ö¼mˆÝ—72aw¹^1z ‹#ú]¯×»úE‡w8|¬ç¸‘ú (µÉl‘ÇP•™¢6€v×j™ÞóÝ)M¡¶¦µnÏS"Rô<Ö’‚:u×ßµ|á„^ü•/øů”jsu'ƒã„è
+
+7q^>`¿Æ‹m5 §.HY%ê+²´I1ñâP U¬С ¯ÓéôÖúw1¿÷ÄÏoôÍê.ÇLœKæXÕàÒÂ"ÚÊ+|¾iÍ9Š(dáã†d¯!WÞ+¤$k*=ÖoHÐ\q»¶ $¸6<˜¬•s¸±¤Ä¸gzËZÐû[F}£†ÖU½T&á˜%mßd¸Å¬šb_Ts‘ÉÇØoNÁ”ÏbÂóS’Ðz*ÜVŸŸBJˆ€˜JÉQœS„hÊ%„žóûŠø­LÏE_0·èà­3ïæ<=¥B*ydzç1$aRÓQ¾?~U)áCTŒ²}4ºÊVÉéÖv• øBGë¦h/8Þ{–:”é+d_õ{µçüΙãk¾pïÕÃ_³s脯9þ§Žúc5^Ö’Šâ1C^ÛüóyOh9Þhû+æuÑx½ëU.þé=·\Ü ¦3·'÷çã°‹~¾e ›·ãóú¾ÿ¹WÚ²TGÊ™›.ÈX^Ö1TvAèV”z²‘ÍÝÈXsT®×ýºÏÁÒ¨•»_Ç`F¹¶¦Vßž¶ÀïYî± XwOËo ÷MìÌÞÆUqÂ܈éÉŽ´GoQF‹ŸÙ‚)©ÑÿÄT«F¾½H¼b˜¡„%,L¡U°—»˜I4Š—ëÀQ­»pä4;ùzœl8™KÅÛYù›4NrÛªÔë1=0
+"¿Cᇩz‹IPð£öÔr˜ÏU_HQî|ÈãljYzÕZÈêÖ¨!_œå_ß¡q1¾-Æ—…Áá…>÷y”Ä¡³7‘‡[ÂùDF„T®“5ØÙÆ‚à<³¶J'ÔZ_§x_ÛÖ¼AÀ±¡åî’u‹¿ÖŽ$x=ºOåY(ÿôÆÅ7hc…mùæH8ˆ&‚i0÷c J­
+hzß„§¡µÌœmï¤ðÁÖúÍ&ü½Üà|冱Éa41`tè·1¿kØÏ#Æáëa ú¶Zt–¤Ò|Ý[d–t0ÎD¤”²†iõ#¸ú=^ S86ÃæÍÇ-åU6I=æŒZI ‡š‹ïeKÐÔ
+“­ ÒðØRÕû äA‚Q)êœÏïP±¬ÌË‚ôêŠ,i R.=j2“÷ƒ±*eêQŽhïœ0†F”l#¹›ÆÌlI
+sAÔG©± ±Æ½Q³ËvÑë¢>QlÔnoÕbI†CldæÔœ^W5,ƒž4nnA¥é»à,Ñ,„.l(7‚$dEYLr©©:!áiÎ'ɳ¤¹ŒÍ6Þ{”= ï¶"]ZNdŽ¡¿‰›e?…7JX6ññô6…!HãxÊ‹°ódFy.öõéÌZ2P‰–û)ì¹ØWœþ¯Ãa•Xç´ÜçhͲaT÷8­h¦ÀäÌ8Bh:c“…X¾—Ô†6`|˜Ò&ãhÆ?ú°Í½±CBDÚÅÛ~M5-‘ÃáíQjý27Aíé@7Ý*q€ìB¢>tB#ˆ7çÀrôt*®!÷9lR$AÖ±õO&ŒÛ‹ô ¥ŒqßuËmz8I×’J*˜vŒÛ^"ùq˜åƒ($övLª9jnaqé忾1£þÏ¿µÿ2^&9rI=ïPkðyX
+ÜrÛG蕒˾¿ïf^ìŠðAC?}07ûƒz…y˜S½¿6„|D_ø.ûÓ@CJ͔ך ™‰>`Q1¿Z‡¡™ÜŒpÙúÀ]i‡Ð}Aªi]'¯ˆ…¡p±ÚtÖõð ;AÆõuÐèÊd%fË !®ºãLL:TÆPÃ9}+:§ÓÝC§²>¼¨d—ŒHâhçvu$LÜ®û@ou)Ä€¹K?€¤¸=‰‡AšÆ¶Ë Åjˆ@‰sÌ ŸÑ¢uYâ"Š¥ÎÛJSw:1f”­Ã°Yw?)xö†fZz‚ E0¥ü쎪PG—ILÅÖÊùº5NÝó öý—³Šð
+0äl’¤½\Ì*èmõâ2¡XÆ â´ýÊ$È(B>Nò "ç¬1¶`îý¢SéQÑ5ÏíÈ:FÕs™î37Œ?€HJ5~D†§'»8òM>Á 
+#ÒÚ;GÑÉБ ?÷ȆÜÔŠC-{/4G
+üƒZkð-â…[<¯£÷E¾©ô}> }¤vÃÅ~¾“&Ÿ •ë¹2È
+Þ Jâ…×·r2Þ’ð9ÕaŠ£fR"ùîwÊð›˜rÎè}]ì²K£Gˆ2­:çÈùQQž»×c/uuºmÒÚyJ/ŸÖ©#£tê¹mº‰¥úõV²AŠæ›iÎÁM7
+7Dåd‡Ï­xȨac·£FnRÆðu2AºI!í(ßRˆÂѧˆŒ+G‘Æ½Õ Ä®D/Î7}²Iîþ¹Ù‹ü^7ß0Ú„U¦fú"•çňeã¯ú‘ÝPGnáïYœÈ¡Üt¿ƒR`㦾Q‰'$̶á Õ1¾,4 ïÌ5º©å= <¾~6Ʀ-¥Rª:×›4Bd•)ÅCh_€ (Ùd›P•RšGYj4Ù^è{Ø—{3å%ÈVa€¢-½^ESôWrñÄÿ²¹ºösb®‚
+ÿ
+#Þ&'Ön ™Œˆþ0æÕ>(ÁM-(cõhAè/ Úwƒ$>]Å9Õ¦MMön{‘ƒKs!ô=D«äŠv•/óînEJ´`ê2x€R>ÊCŠPwoKОAH;RÏ®±.~c9ãL9Ä«F]›ÖÎÞ?7É;°;ncÉ€dÐÄÜgçœ0
+.ÔˇïdœcL6‡…Êp‹Á>§Ð½Â5E
+K‡óþAÜ\’€<òÓU¡"9$øÁ£6`eix”¶µæW‡}¸¿–.þakɸ#7õ€ÈŠ;ƒÏýÙBA$­¾Y
+<IŸ¯ñÈ{ßüÈÉ œsæ }d 3yôºòý6ƒ¾“Âñp]xÕx­¯°ìÙ†c¯ÞU­a(6^5&2…B¼¬gcµÅ5dyÂßÿœA?zÿ}©AíAµW‚=,r.ù¢8.<Õ^œsõ§#;²äÔÄô+ëè+ù^d‚é”XtB–Å)•Ý¸èK4eªAfÎú;«ç‘)ok¹¨šLð´¦ïçlÓµßf›ÒÓ£¶iß‹³íÌ^âÃîýhÕ×' j.)áÅi)„Xº=‹=F^a(ŒÃß#è`ø`N¨ »ƒÅûTLu1à d7Ðö¹@ŒCD¸p¢¨W³y=€*û¤&ó`ínÄwALÃ`>Qøn"Ei“y½äM„ö ¡™×4ØåÁ^± š£&æ&¿¿Ä‡ø¬Ä¸9ËøsíÊ*O0{Ž.ˆw–i@äðoùßÎÁµÈŸ ¾ï¤D—'7Þ •LŸ,
+ú–ÉŸY¬{ø:`Tňäšú)æ´ÛÇ>Ýõ<ï7-}œœßBåŒb0þ‡ùÅ!ÏnKÑ ËÀüIÀÌÌçPódc–V8”.âô&¹¯mÔæצü;³ÊéÖ×
+æ™üj2ÉïTye.„€Ù§ª®LBÁäfDiƒ.Y4Ó züNNž^+Âɼë?üé„9UØͯ/Ý@7õ²Ë ØŠKÍ,eâýI«Ô}ú»;G3èØ^ƒP6ÞeÄòU?z¨X¿òCy)²?ÉŽag©&Ÿ
+çh¦^ÆB§YúZ¢™f‹k3—ïÙÏÁ–O 7‡´ËËÞo#sO#+o~Ô|qÈè™Zõy.΄Pê
+»VbÌ&9b¶°¢Ù©¯ ë bfdmLÆÚÁð¶T7Ð÷TxÌ$ÎZ¬„ðpr¡_YUKÄËy¬ò âUNòšÊöxŽ(Û™/Ã;CÑÉ7/tºµÒW y˜?B¿Fg›È óû«
+åJ9ˆ|v ,?›U'´Ž‘Ðf¬Œ:Â%lµ¼?uå,òžL[, S•*Y|«/®–[U6¬Ù_EÿaоÁ\v(²HïʇóÓ£¯0†?±nÌWúÈâßáÇ-ÉM¨Lm!'%:ÓÑ* ‚ûjÄ#:Ÿ0í4 v_ÔºH'ˆ\ %ÈsÚ´‹³À¨ðI8$…¢lˆ¿™T5'§Q+Õ¤`íZ‡H‡V”ÅOÁRó3ÍI>ë™Ü%:éž[ÈYܯÀ<@ŠL<›b¯×§6%$+VÂᮓfI†)šœAf¶{­þjÒÝOµw‡hX1…„š®êA—#X0w®ŒÊH¼z’x~g‹$ûN¢æ†ŠXQXò|M0-ÒÙÇÆZ.ZKƒ@ÞID‡¤U¡~xæMNjܘ„ÏŸÎ(âuNd§Æ4^ÅN¬Òî¶ÑmlÈÙÖ/0‰IZÖdíÆ%2
+uh
+KÛ<dâ{'§dç@Q¨$sÒRñ_mRÓhöë
+jÒ3¶*ØVõW¡àt¢ôK¬ŒD ÍÝÉÛ%ø 7õ_?Yhf$¿³úÙõ×¨pëÜ®1F‰ø:þ)]šG;å`ËMM(ú_ÏÙ\™LNÅ£³4óòÃÝ-ÿúë_}0½d þp¯ƒ8B¤]*Ìn mOMIÖ­Ù´±«¸nˆÍoŪÃÂ0®u>¿ø<E ÙÈ ²
+_|™‡@53=ÀÃnòs
+s íÂj8Ž‰jÇpþB!B2‡6b@D|Ÿì„;8¤
+;¹kXÕm1`d¤÷gZø0Ã÷Sx¢I“[ê#–³µPay+]w÷d"ñ¡qÉ»®:y™Ø?Ô1I¶ÊnÂÀRš KPë-”œJêí•Si
+Dò¬rkɃgá˜â—ÊíÓ S*$r’qªˆ´òsˤÜÏRŽMPOMš ~tÕÉåMqIÇËp‚dÂ?ã…̶õ$òeŒ”Wôœ'-üê_Ú×!=²q=;OÇùcãz!OÔhx7`?nü´–f–ßPDè—÷ýšB
+tüõB ööJ­RFNÍE¿ÿš4PíõŸ7܈q#*i´Éöð¹oÑ`{õÌà4š†¬ýÞ¤¢WØ Q6®7e“¤TSôL„Â3]ôFÑà€h‰H"ÍbR7+!]{lSrW’p¹aˆï\Ÿ¦¢›ëvÛ¡ò2êcfLct{õ#äýT¿GPøÆhð‘œæ?&¶Qª:”µã•ï‡—¿¿@üvÂÜûp»ñ½™¿eŽ§È?ÿ~ùãß½h¤WÿG´³dga'åº&ÂYÌpQ¤qÚÝ@˜Ã˜J9¨dÑ%a×4v³¦´+ˆÆfNó62ü
+'"ša©› wºa,Füe“áþ1JÀ.Œho_¹’îù™VIk””ÀJ©ÿt‡4åÑB oë
+¸w“´¢SÈåÄ‚|Ý<Sz(ÂöÚCøHyOÆU üçÌ®&06t¦ÌŠ· Ò^2º¿ÿ‡R´~Öúìœ]ˆ 5ýnSp q ”“ž?’–˜o2"ݪE*uQž=Iñ?ýCâØ >¨ì a(×drÑ"ùi»¦
+úo›¬¾åFÍ—Ô=¦•­ây½i}K=ú £:®–\ýÄmXS¶Ž£‹uÆÖm`¾qQ.Ƀ{Ôf ™Wwˆ¾×¥¾®w«¼¬0³×ö†Šu•5c (v"CÍæD{Bìfè2q¢u6^»#®ÉS1k•Á<9åAݹ
+t²¢ZÈòdš ê׸
+>‚óG¶‹Ÿ)1(|êqL8ˆþóèY;ªê€bþÁÌq¢%ÆŽ’?qµñ…ˆz¨-Öq¹rŽm4¹#Èq}]–ÕêfÞŠ £}p‘È?.;BfGBÉ™½$Z©$Y HÏOÎ1 ÜÔBWÖ¢cPwQ´l1çpˆ‘¾ QEïâS?}@î S¸þ‚õ96ŠÂÿ`
+Du"-ªô8Bp,"Êh׸>õk¯ú²÷Ú¤1?è
+fTµ$Xä4H[1C?‡tlH{N‰p·|u†ŒsÍ?u€†e>?ÑÐhCh& ŸÚÕâ6ƒF%)Gm©ØéAçÒêáá:t¢LòY烈=ŒÅ·ÏÌNŒXÛÊP«|t“h¯ob#h1bÍÀ8ÉζSeJòÉê¹dA•4w‚°_õmœ˜P@2[
+¢@úôvgPt:a’Œ•Ï"ÀËON;ýðøÈ%i‡»žv’"Åü8ü‰O „p-SyvŽddHÂ]Yî·Ø(·'~¥¾ÎooŒåà .Èce€L…€<áá¹¾ÈO}º>õ¢,N\‰€ózºMÙo²»¾Îo*¼ å›Z
+3¢¬~¸¤ÿÇx™#W–Qt½‡²e00¦¢eÊÕ
+òX¦ö¯s‘‰jñOõî²’x@æÍ;,‹’K»Nê¥Ä?õ2ñÛ9/¸¹Ýæ}·W(>»sÙ…×ÿn§þ´Õch4é‡Ú|Ùu_½)/ÄÑá‘^k.ãüØ3~w#ç ƒÃã“Ó\€ØhDL*.Ô.ŽìU®F~ÁyZ‘,Gê©+ZÚ­Ž¢ÛóâÇÒeœk}³$¬‘ d)ò«‡O½U„ìvåÅâÃ:DÖL¥o!„4Ž‘»h"lÀxJ N!Ý‹µ>N¹Ÿ8%’ßiâ€Û2û¤¦HCdã
+O]’áDBAJoó!tþv¸Ž¨0(V yZ!”üÓyÌÂ
+-š–¢^P;‹H&3'Œé`¥²”%¶ ))žoØ*²M¦1ÓJ@]æflä­DWÒ£‰ÿRZª÷sÐ5 F Ð/©ê¿lf-VÁªZF¦4ˆFZ™ˆÇ+EyÐÊ’‚…?^ÙA3â[æËÏ"K§£Biøu$ÃQ+:c)l@+!f/¡9
+rC¾Íþ絨Q³lOPÄÔ½h
+LN€wÂámF6r™Ø(ý‚s@^°¨él’+«b×us!T
+º€%VŒh0„­ñ¥èÛÔ¿®E¢ì /d’þ±Ž|·zm›h7g-·n%%$vO&^lHatjGÜïf­à
+ȧ»£r4£Œ"i3P@(!Ç°.3àô€áÆ ¡gWª|)ùµZX’RjhçÀ:osàóöªçgsŽm9[|Ùºo£ú|g5Í"ňރbô¾9Ãcbm­Cyr-pú¨y bõè$2V&#™3ÿJ7gQú EVm†ôä[ä“]ÅMÌ'3qäªÂˆ¦mWýPpZÊ
+µ‡´ô»ù
+ûtþ÷ÿñ·ÿð™­""LÚš)5À%‚çzöÀ—ûÇúýl+<6,$Vªs–u¿<´ŸxÚ›‹X€”É=qi …üQ…¬GP¤Ÿ¹|Ðh)šŒJ Œ)kC)á~yZ²• z,7!!¬µÉH±R#¯ãò—­M–ç%È«a×sdyÔœÓo32¶>Ö©°ðSðYÌÙ*È3|>ëážE/m¢ŠYÉöŸv–
+è&ê<s±þuÈ=ø¤$q’,z¶æTÁ6%` åªÂ¥]
+Š£“—²7õšåª0`ÆŠÔ@Ðz)è^i$ÃR”º°qáÏÀ¡´›³«Ðú‰ÒZñgXö›9‡•¨5Iî+Uß0}
+ŽÉÃEø$}mùP€ˆA'¾L’ì,vN¡¾Éd†a%,2oR–d¨Å]Ž¸lú})ù´iΊ"ˆ\mãÏs˜æÀa Úý2°68e·Ð•øô(X»”©ÜÈi÷æÐ:[|è·Q}¾ƒã¿Âd’©HPfñë弟×"®Îúˆ×^8ìKáíXëžG–,q±éãêÓÜB^†‚ÿèZ™ûê¡âÎI‹¾­p7I¬À`â?¼‘ؤÃxpÇ9ú  ­l Ètwç ,!×%J,»{Þ…‚Æ‹Ñl¤«<¼ ‰QdRóµ7”À©ä%P¶TÔ_#Q»–0)<e‰Œ¶£þq/ú6ù¯k>`† bJÿá7EÃÊ›ã pÂr8÷òrD‘<Y”¡–|aádèB7Sÿe7þ¹D|`”6=%>ÇàÇL<]ªH'[x¥Ê—’½]•ndˆ²Ý9u^æ@èåM'ÒÞœûòÚàÛÚ}›Óç;»i)5úCÚÀ/Ö¾.%JO/þy-ºxŠá<ÃTß­‘õ"iQÙ]óò±·Š`Á¡ V´ÀLâ­¨&~,g ¦VIŠZ·Æ ²•°“MbÅäw‰Ä^”Ý|dXl’\£u[ŸÇÓòÖ>üœÐ3%ÛÞ†SVÐ…‘ë^ $›ÈVR'##ʺ«})ñOAN8“&çÿt6 Ta¿â>'3t9MÑ•• D¡^Õ½$ÊÍቢ9QÕpF¯aúŽ„Saç°Œi7¹UÝR­{ðï
+„qw“Yä;(–Éä¹_ŒªiNx×Û"¯‡š˜éVè÷cHh¡Ë:Ǽo\$5<[©ßÈ<-¦ÐsQ%Ùö6"<‹n,\GýDJò»5·8>öVÑËšÞñþRä+Ÿ–.õ S·±ú¿Æð§!’øåôÜ£î)E ñÝÁòïnO% ’xäî6øàcB¹}y¹wžݬ\*dÚ̬*; ž¼5š2AQý/<ÄÔ?4:/¤Â!TJɼŒüÝ
+U_–[}¬/xÞX1t£ê/;á’Éz'Ó° °FîG>úȤZ1ú|á
+¦¨*“ÚñViÝŠí”8í­$-Œ;ŒíÍ }”dÜv[:BAõèfs ‘x™ÇZ] ^«KÍOë\:çtšKžnuèäëëæáúÆ_Í•³<¥`@y£æeîñSMm›ˆeÞ@'6¼mw}|çLg
+)¸6l&77Ý×ñï \0Iµ5Â)Oò¶ „œÒaà4ÚÑ„AUåÜ{ìnMP ©·8@Œ
+.iÒê)F ò逧Ev»°]P†Ï”n‚£9‘ rG¬0½Ar1zߧ¹­¢xO`.‡¥`eR_4ÈL$Ù‚8E7R`®ÃG0KTï K´ýøqÚ¬`÷™\*Æ øf—“+ÎU‚5R<^üñ›³0£ ïx^‡*P_œVÔÓ-åNYÞ.õjb éÂŽ‹Ó@Ç;Çdzîbvùi.£¹•oƒ„@º¡7ÞN­%'C;Èù2¬Gv_“ªb%÷VŸ÷ñãqI^Âç@àóÈYtQÅö†!åê±å:8f£W;ãÎ%ɵ®Ô¡k/'8âÖYf—ì’¬€`cfdž ü£åPÁzP’ h/œË½9îËÐbm¨ÉÐÓj >Í®„Ùê±ååôàU昔™^´|0z…I±äÙ¨,ÀîÔ8*‹|t`Î@X©ÁƒÍýž@œibÃȸÒý"Òf­õ9†9£'Ôq‡·GëôÓ´d^²õc>¸ã‚ð²_jzZ†J œouœC‡-I ³î<óÛþ²^Æe¯DK¶4’â‘EÄYﶯ\ð­VgoºÿÛ+a÷í85•}eÌž¸wút²4ƒü1üÛ?üñï¥@*9GŹ¿ä!ÀJI?r8 öúï¡þÅç€ÌõOÇ3Öé-¬¯üÇy ê‰5ÁÀ†c˜¢ÂõûV2B&‡¤ÉVÒ¼‚œØ¸
+¯ÊÈ;„‡"aÂw>l=ã&hûš}‘=? ŒŽ“¿È.«À€fí‘
+f³5gäêg¿;m@£Èäî ©‡¦/J=ce’ÇSƒÊDå ²â¡™V†c1?Ñw‚[˜ú)Í=—ê‹ŠÿüçhYAŠÒš¥ÁFiŠ[vsà
+ ›ï)½/ß ( ³ìÞ­­Na\\î8;zÄ÷}©”õPŒÉö9¾¢*C^××A‹d<0CVÎùž1ü+]·04ƒ6™"Öî
+>¢ëý¦8–4ûóÇ4p<'=?,$œhã[ ªÉqó—ÝPO=ÓüqÒöîÎ"AÃÕX Q•f(|íl î¡u0a>“u Go &-yC_§ÓÈ­gÊÜÆ?§ {™—°}†™àf<¹—Kü¥ü€8@6ùP^&ÏÆz‡Ye¦kð3P5º
+£?K¡—Srºhè«vÙƒc ò€Y˜ö «î¾P4`wÖÚGŽ‘ÆæAò“ßa²Bßbœ«³—¦ˆÕ‰xä÷;~Å¥¢]`Œ[T¥WR22Ê#!§F¹¸‘¦¶ë› ðŒ¸Ïìö|
+WÖ~b#Öß:q'Ôë¤àñˆ ¼×!b¢ÐÈxinè¢Ï‡hWz•ÌU³BÜ0¼N‚
+ô h÷0P,|J¦ÌoƒÚš=ìç†õ
+>„!kDõãˆÖIÙ
+3#Ãgp£ÂkbÆúA(_,“í1á½ÈÙ5Y^˜%è›±yZN‡!&t[‡ñ„M«Ì C
+A*š §’¢*ÉuÌÈ_öˆŒ #OþÚÊ|]<ÿËx™dW±+QtÌx©.Úü&³ kæßýûH¡kœùìE.‘R(ŠSpshçª ÑÝZqIø—Û¯Ëо[>E΋—mcEÞ0QÂÙÞ•õr`'>°ÕÃÔ¡–ì„X•á&˜«ý霢 ¹|dL’Ô}¬;nDå ðÀnb´Ð ôW„`7 —jjÉȹ/DiQW{ûÆTòÑK
+
+ÝŒb•‰9·sÕ-Á%«4“´äÎ&br…üùð"g LBÖì WÏD_:.È®ª˜$”¼`ʆ'È:URÎö*Ñ+
+‚ðæô£,köÔ•Õœb:IþZ"%î0ÃÒÏÙ)@·Q,òw,m.vý%Ö•xÝ!h,*™?8öX& R_SŸ¡"éüxÕêc~ß'V[3VqJ9§G|g‚0—"òéȉb3†÷ÐÕ9c²˜ºGÊôtžÝÔà…—û¦Y
+Uã
+GEqŸ3ΨQÄ8N«ÐêÏnSÐ"î²æÀÅ%FåYÜÛñ² HoGšáX¾OT!3ñ =;c?AÏ,[²˜HãT-‚‘™ÚÄÂÉærµ„_“ð?*U~ ”eöJ>òé_øþë ÑáU® Üëq Sµ5qì Òh‚UÐGÞ’& °fÔx6;XAmAêØ]ÅÓPC`z3Æ¿³D `÷93«d”5'q‰úCÂuaOAZ‚#™¹ŸNĺ ³¥²¦µ­{?F"¡,¿koªà¦Þ@çaÓ[]q¤å¯~82Á®
+=N$
+ƒ¸¹¤¼éˆÖ¸äZdJn½ºsëø=™Ûäxº à½8ά_Kì„|jÕŸïìÌï9œÞÄõ탘>ö× ;¡˜šr°p¸2ÒáHô! Ž±C2ðšù Åî`á)0¥åº&Ö®‰SyõlÖZ£N|ø¾wýÎ…úLÖZKâíQ®U=O%?™ò¶\M̳<=Š“5J’ÕGˆ ùU ³²0µJ†Î,u6!‡¬›»4b¶¦$žuÚ¤›fc§½Ç|êù»ƒÑ“â†Á°¶oŠcÒ)þvtùÅÖñlJƒ1dóú“«¡|t2I–ÏCWC¨Sæ¤úväÚKÏÔÜFâ~ ƒ…6•¯\XÌ@Ê-ØⱈqNiDZ€ Cœ4_7Aå(~1•r눉„ Êe½I«Âö°³}Xîp
+7Æý§·MË* Èþ|½“ÿpc€èTÈV¯çÝ dÐ/AÛ¥sW¨B ZkÜ(„K*c©ˆ€æ$Àz|"ÇH…€=öŠóŒɼkgF·sP´z ér„Ž"„D‡ÚAÊÒ ”îœNˆ]ճ̞1æ§s˜5P©A'›„ŽaQÇÖ –•ÆãMËy8€Î»Dâ^¬[šøè½"º{Þ›Åe¼=æT«ë&,å ƒÆ 'ááÀfAs©ºO¿„ØÓ/%ôι4BœÅ£©±ˆÓm(ÀDýâ" ›ÄÛ`ÀYI @Š9ÍŸþ mrDÖ‡ˆ%O6¢£õÕ(ÿCYá-D(QO¤åÉ„Ðù‰¼î;/î’ç ýx»«úTv J©m;¼EÉâ$s6ô•z¨ê;ÍC ³ÂÆoŠ ™à@¥6|‰;avNLÿ@\ñ —Z3ÌütÎ-¸yä8. 1ZÌ]ÇZ}˜z¤Ò„–ÚLàx¥!+¨Ê†0y¹—oÝÄþ¾nÀ0<$þgÊȞ̓ÀŸ¨Ç6ßFª<eŸz(#Ü
+ÿqìÇ…3ní𸇶'üŒÌþ9 G„wÂÆŸlPœ0#ëz† êÒ<Õ|È' µˆèé5dðCcaCÌPTf‰;ÛôÓy‘ ,–9܈¶ê‡¨§òýµóÿ½^‹ªžÊ,3ÐM9ãⲜc[[(©£z\?™_„6àƒ²àcÃ6¶†Îr¾”P¦HF^ˆ²©d-.L~:'4”…®ý$#"­Ü–úQ="¸_„¤ÖP
+dA»€Á}UÃt”ɇ°thIqŸÃ²Å&Uwü(^ÝâÝ­?„pñà`8¢¦µ„÷ bé!eðç÷~zdà)«É›ïÄðoªÀäÏ6…91 p)L|Wa8¤¥Îú]8Wß`猪*=p÷W éŸû@¡-•Yªg¸˜¡Ê ƒÎË-|
+xDW¿iÖ”b'Éîùbý¼4ë~έå÷ln£ã½ê6÷ê8Ã~-±ò©U¾³4JRj¸k¢¿”¤J15ÁÏ;­µßè6Á* èAPîš.÷Q—‚gO+ý)ˆ± µ!Yè†üÓ«¨+Ô'ÆÕS‰~ëoBRS¡Ámw¼cØŠF9Bõ“Á>dyð½?JÕʨI+GÀø”:¥M…©£ ¸UßPLK¤^ÙBÆ ?¸¸žN骉KfÔ¼ Owƒ µÀ?A|æÌT
+6ø@•¨š*}è(Q+2¢!"èDw•¨ú€<BlHÓú²åÖOOþÜæâ~Ž¦‹äXUžz²aEIóÕ‘F—¹(º°HíËPÌ×MAî‡a¡øVœÎÍ)j½×£´1($ÔPÙšWBta'2·öŸþRÁÝÒÈ’¬&´¾ZOS¢ø‹Y ÜY“Eí%š -qYfÍVVÖ,$?ÃÕ2J}Ó7÷1RüA?Å$´Êÿ/wܪ‚ ˆ®À{pìÀšÿ'DN²‹Ì„ìŸSÓ3–x¯/~ ˆbîLwuuʆ{j6ëX4dJ+¾ÉDú Ï,$TYU©R|ä}.²ðe'èfˆW¸VÎGËKð*ðù@)U𗟨ç_O/ž˜ü:Ð8‡9g‘'çVK3¿ 41½°å ‚ šÐmä¹!<2ê† ñÐî<-K
+îwQ
+î›g6
+tx×@£6d¹P:Ÿöøª~üª(´uÒÁe¹1¢‚6âŽX<£0—4ôè€,äÆsS©eO»ŠàÔBÙ<_ .Úkuˤg2ömS*¨I-«h‡D~¶`¹F“@j€É³‰¤&ÚZ¶]‘r¯JÂ@Á &ænVä[ŽËL²»E+Q-e¨|uNfWÂ\n| ÈcU Q~3y›Y]Ëó\83 ”šM}>%?@Ø-íûd"ˬLÏÎAûT@ÎVNü#V‚ß›GA\bè]ñ·Ø¿ˆÁbç(Æþ¼\1‡:e¼-ëd– ÏœG¶RÒ"`jveý^û¾‘Ðm#ïêøþª`·•}ˆÈYºl{ø¯
+H‰Œ—Mn\9 „O;øÓ)‰"מen`V“ûoçã“dÀýÔ“6ı«õCVKRVGtV$>þª¡^jëUùχ˜”G·ÚÃT[µ )-¼Æ(¥NHsk^ïûòëÇR ë*ª"¯Ö)½U¯}ø„4)6J—®‰(ñˆ>ø*fu!̵·ÞG kŸ?N öp©UÂÌK‘ŸÔ†V
+MÙ®÷ aÑRÿÕ°B½†Œáí
+$sêóÏùø;«ä"pBÇТ«JÔ˜6ƒ¿4ØC¢u¸²‰ô tA¬çÉ›x_ëP¶QG/Ö]&¤› 1kÔ{q­Ti>(€÷ë耚)«4eLP§ö@¨oµ¹EjµA3Â5©
+­÷ªR#Ø…¦M‚Ô¼o5)3lLÒ: éô¹5êw
+iç^ý©çŸÐ£|Ñõ³Ã<6šPyÛô0ç&…ŸÈ ´z–ElÒKdËÔgµNˆªáÝÆ„ôæáÜdÀ­M"…e¥[[ÕT‹_ä»*mºA]gWáÅ@Ò½×ægDGwœ ·l:ÉqÇd‰tèeŸ³íϘ öÓ1ÓQî:f·ºCƒmRp«tHà½íKÃ+Ô·Ö¶M*¼Å€ #–ü8:;ò£¹Ü»~0©$îèT¼•—ë$#7´ÖqoOŒÝÁ†§x6pݪ¸Ñ\¨<ÚÞ*ýÙU:]„OÓ匵ÌVF
+’úà:Ûw¾Íž á8- ¶Õ(
+Èßô»=n˜fø*fØØúÁ2Þ%©Ø‚Žn78€h
+“ŠË¶Þ–zÍcØؤq‰jïƒÚË,á•¥p‹èÚð¤èYÔE ïèEÑ¥†Éå°dé´n»²FÓšÚ£5…0âÒC&1B*þÅaâ
+ò`(Ú˜ž~ˆ˜ÈÚªº‘¬6Û«u¸!>çdC0µˆ¢ºn%¸xdŠÙ©±æy©ÓH†®­Š§÷Ò9kÇ‘¢ zRKæÑ™t0/ÈYä‚HŽÔë˜`Á·>J8u½¢‚k°m#x”Q‹&8 ÆØ §íž@ÿ¾£ÎŸSÄ9ÉÒß‘H[[(æÈ!A¿_€î"EÐ0alJ}Doõz@zšòó¸Ý[ æRJ›£VE8€¨7ÜÃ
+”ç%â¿iDŠPî¡õðîüý”‹UìÁÕ2 œºfCbÍSzÇ]x¶ØlÙà¡ÇÑD¸²÷Õ´ÁÏròGW1Ó!Kɱ5×áÙ3r`È׃ñÛ°LHgP148p?ÏÜ1Oßf«ëðj*ù¨Óö W ¥¦û:!d1¡²$Ò}q‰ÖójL†Ü@šMÇ"„̦Ý6K/f©‘ \ǼW2œa©VûY;\=:y„ÄRNuu¾u㵪ý˜üÂ-Q—³ßÖ2œO³lÅuB®êÁ߉§XÍ'k÷½éGƒP;±cqôF;;ÉâNÇæà}EWo̟ȹ<«|`  dM§‹-ø?q9i_6íÑ3‡FÎdÇzÌø|.AœØ´],,þ¼ŽÕ¹14g:í7Ë` •Ê´ ›‘LL»s=hq܉&ÌWBÙžßßFåUlbw#i´ÓŒKÈu^Á Γ2! ºEQê tA¸!n’j– $‚sÊÐ"‹ó¨ŸRð¤[W2®‹PÂø¢<I:¸+/ºs$IK‹Œmþ.ãlél¤îж¥óÃs“jyËÞEG&]GÍìç}BZÚl΋6!ø1ŸJ¡bÜ°bø֑ɯu-Ô˜éÙö:…æ²7|]"ÅXú–6&„®-»µ¦FI+”3Gϱ:•¬é<b±š«heE5ë(Ñø­\¤Öe¹øóÉD½N‹œ‰æÈù2 g5Ë: °ä{‰Ý}‘8Z—±JÃë¡gEëâ9±ÕcVa«z…üÌs<MR°®bm¥Yãäùî%g®¼+²Ê¶ÎÌ[.½ª$×ÍI%ž™g!XÎ0çÛâ0ž…áñv@¬n¦6êÒîmz }™ ­éºQ«¹äùÌÙýi;>ZÒû ÷çÝÁ£Œü) mß×ìVßS-“_ä ‡11oE@LÙý’üþTºYÀýÁ5dÀ|²\œ^eÓHI¡è—àÝÎg¹1âéN‹YèfЂ«»ˆ±¯°cT7×Á?Q¥·1»]$‚Œ3ÔëãìÕ¹K`¦üzÇÐ×»‰hõné€Ëqrüso,-þ~Ê·$€påVªžÔòãe’ËÑôxÆ
+f™Ø2{“À}C&P¬¿x¢mr!émÈßuÅÅ_/J~:'gÍù6{à@×pÌèPrŤò¶ƒ-¤‹jÊ®+æÔ ¢ !ŸGÞû5èÔ2nƒGd4kø työ››þZòcC»’KéÝMSB
+cb›•Eï>˜Vð|;²r‚x+:ƒO¼ÀA=¸r _/Uûœn¨+%¢ÁI%mÔËGÜ ¦köêúÏS8‡u¥›ðÉØ¥•ªú!}?€TGÖJwÏí³’2Gö 2ùžµ)bÿ^!·ç|œ2™Ò«>«|)_¶Y °¼%„‘¢ŸÓðã#F:/Æ _!¬EåPËÁ„X³bÞ„‘Qª\Ôv@×c"Æ:Ó}lCrä^œCðE„å œsØßmúMdqS[Ëq%üã
+áÌøR:¬Ú ÞDÖ‰ò/q#b{ož:³É ±^Äšy†@{9A2„Ì£ih}S™>‡åA@ ,Z_†/*SD+ ŠqGø\àÈáׇSŒêòüw¾åX_f9;íòÝãÁÓ‹±£3¹¼H7±”üXç¯í°œâšâŒª`šÛ´ Bc?FæQÆĪ%œ¢——ÿŽW¯ú
+÷ü¾œ38²H°–k£O|€Á×Bÿn\¥^gÉ?‹3š¹ø:ÐU¯„Vîod¦m´qøþÞN¡Õ‘\Cžð̉£Ä˜ñš[냌 . x³P Ì`J>Á—[ß
+ ÁÌaºõjUk5¶.‹…úŽiÀ| –®
+Ö-¸@¶ÌÊ À(ÄŒ§s`1T—»óù1·™¸|ÔßÅ¢Ô‰_†×‡kèá{ÍƇó,wpYZíÎü1HBû>Î|­›Gë²oú™Ô—EïM>¬àÁjÚ&ër¾G7ª˜†s?£4ÐB[לö.©çÑDWº`ÿÅ)F£?&$‡Ú¬r:ó-H ); x›o#±Å䘜Îç xÛP oO†:ªØ Õ^‰1kμ»ÐJÔmú«<'õ†9xtžL€!ú%~ø[ wUÏ&Ñû¾ xÅX’·…ÆK]µé²)ƒfn ´CÊ”Å
+&‰°éß „{‰`Z î"(-_Ìâ×›n Ó×e=T±.©v¿=r ™¶ñáº_°?¼ cx‡…©Z!yCôçL­¤'uíZO ¢ùbü™Õ±.>]IvŸƒ[A†ñ™ñÑ&৻äËN~ãë7óþJ
+Ðn÷ ÃÿµH)â2û
+ cÛ!ÁÕÍHძ,ã×£ŠªœüÓ‚ßÅ82¢µƒ7^_nržlW®‡Ìà_N‰ÿ²Ym‘Ó¬ÕGTR ÍT ÷trkç)åð )ÐO´/>3иÊ
+OÊ>¢ü¹C{Þp^GÈÕr7…¹¿kHv:ó nûs(D˜,· y_»KÈùó¿Óþž’ è$Ë”ìüÁëHŽÖéOrÊFbÖKr¿Ob -ãÿ½8äLV¬ZÜ å†Dn¸‡y[=ÇE2ìK&›!±ýØΠįóÍíkñ需on-$?‡ñU^b–?,‘”ÅQÆÎ9,¹Ô#¿g´ÓÌʆ„ÝtÑ3K ííl°³ ä>ŽÓÁ•7¶Ä›
+ê»yÃÞVD99¶½œ7P/™ÐíS]¦8h>‹¶‰L“Ê"c¾;Ž€ÀÖ¶ù†ÎGÖƒI/þcŠy?%ÞJ…½¤Ýx«Âœ-øj~Ò»ŸusÚ¾ÿsag¬:;íW©È]ëVÃ’X@ìdD›YÄ|
+Ro„BXúúòòàÒ‹jk'Є[‹¿"$m™`.PP"ÀH RuÓ½;BšE*£¶B܆¨SÌ·@z˜æh´œ×1™`HqØZÓpüI¾MU>Chɘ6ÅÕñŽ¡ÊQ&í)3
+’qˆ"žìB,欥ölßI}cÞ-Ç>S?|]Ålr(Î/wv&\d¾jCî*3PƧüy5óaμ:Ï’Zퟢ̷ƒ jÜßiØŒ"/ž›íB“É@¨üF¤ K:D%ÿ½vUä³è~<lO±ÄÓMZ®BŠòúGS¤ùbþ‰•!=˜+Ûxô?Œ—ËŽœ·…Ÿ@ïÐk/dñ¾ ”•=A
+úd8Î?úq)@—0Ë|ˆv—TpVýlg®„Áeˆ9Y8Š—ÐücÆÿos%ÃHÚ7+sG`w^e•ÐrjRYÞàb+O%lvθ è}Ì‘ ÈYØq(z{PôašW¬ó*&f€uÜlÉa,ÙŽº{Ãu;.eMûáS¬)
+ÜtÃÛi†@óWMT,2_Ögqã`4¼ÓÃ?& ? zFœÉ\u'€á;|0óó­bɱ€¿sÄÕ¢œâíqª„Õy$0_®'j;‘ž€Åôd«ž#mbb7ŠCPÈ—/ï>¦½a<úØŒ|@Å,|HdC³@§|T-Mɇ%냒Z9#à
+„ú·ä1¶åˆðÜ~ŸKÑy}{Fƒ_eQK«ü)
+~(áMq¹·#Ò¯q_swÅrbõ‘>èé"qÚR5Z1vÑÇÓîjÞžØOmñOÿÄwù!ÔVïa‘*X¶›LCÇÏߘ™ßüB}¼…ù¿_ÿGPý™ÿü¿ŒƒjÆ»ßþ¸åÛß>ÁùRÌ– €‘…˜q0@Wg›Xƒzvcds’êBEoùW•£úçþ{îëÈ—õ[EÍuóõ‚Sú3×û›ýq‹A÷¸ýúEç`?!ÑaŸkÀ&c &L}™
+¢"ìaQÈ+¶ŠpÜ¡dqÅXJ v¼3À1¿FkDÜÔà «„EÆÞ ‡3D´œ±›˜SÓüºŽ"†p»3gˆåANÛG±Gø-Ö`.%\ ŽÐÄÇ,)/˜Ð8r’XJæQ °’o(D‡GßÁ*
+ÝN%ÞäN‡ ;(E:ÏH¸É>òˆä»lIzPÂC¨·Èúó§sÑ„¼‹þ±ù$¼‡H½ãrÜSEwËúöÌF/Ë&¦ Ð'××Þäá«áEâ´{×/ŠÀQâ‚\&­{í?vWòöãûÌKßþªBtÔJdÿ`Îâȉ ;º®_˜9Æ*Š‚E”H:8¡óǽ®R<Á­ êUB~þ|•dÌ/}͵°Â“Av›°!ÑvÞ+ÄS~ñùÖwP†‚Í"YøQÉò ÁvÉz*fb2c$O¡FUº¶0© Ê)‘à3­á}Ñ냢 ÏÌš—&ß¼¨
+ŽIQ2¶û—æ”x!£ÐZÛ-Õd3f”j_%ÐN®ùåýI¨o]FßY ;5h#ôvŸIbŒY´õøŒ¬Ä´­§õ£©žna„‡ ¶V=–—AB``#`@˜>í§ßð[9ùÔ”yáÌäß!®öܺðà*éG²ïc•y9Ð68>€•¸(‘WÏ ˆÁ}†\JÌ $XùIA @¸·cT%t2\÷T“ãþ h×ü¥èEæÓñ});X11r²$3‹þlÑ-JÎ4|ÜĦͶ¼ß]±÷ƒ”bë;¦DÂu‘¿QýÝRLÜRm[^hl¦;ì`¯Ç¹ß•lˆñkÎÁNE³„~Êq~öÛ$œ6
+wà­‘“#ëP™]{ QÒ:@³o&»Ò¾²d~FLL}&[ywè%‰Jí*ÙK
+úXùœÔ~%¿‘èçé"¬¿ ˜@ :(Ãw¦k±2*I _E¨:®øz‚ªŠ$)gi=)!tÒœ*8n0%Ì›~b{È ØxA A—þÿà;CJ-ɾ»ØÐ(é>Næ%\0êM’غÐrâç,®EwD‘Áñ´ÖËO%-zÀÃqù£SQUF‰P»°®M¸Ù—-ØaÀöMìèX|øN¬ìãÊ1è›ÄD»‡az^›ûQ°5 ¶³
+‡Ûó 2`¹KÅû*‘qsÃZ>”|]È
+qæüØÊ–™í…ûkÀJLj¥~–T–è—ÕƒÔ>ŽI­TX"Å*¤F? # Åç ÔÖAÐ0oMÙå ð¤ÊÎ4@IS0.é<ÿÔZÖ²**$œìc$€@7"hqŸ£d˜BmÅû‚'AclðY[Éü Éz7ùÀ/ òyí%ð «ÜR;8|?ek™»`C*Ü çJ¿_ȈâÙ*×}%H›ès¼ûQÉÖÙGÝÁ­öò’VýÆŒ sÌ_îE7¢UÖ®åÝ<l˜´Ø<'„†[C©BI}Å6
+V£÷mã°u¡ duª:ÓƶkÓ°g ¨,:¤Àˆœ¤è1¨ÑàˆJÂäX´Ÿ¥ï» </Z'ÄÛK
+I Ž+ÀÄJH¡šOèùõ©kŽ!Ó
+¦$?‡=fx
+X§ŠeŽÆý¡kMŠ€Jìž_ëmØ$¸Ðšg+ðRÕ;êdÊp¬ë20Z”I m7!ªt.ºr0h×pT¬•ã¼¢7Yí±qtþ“"\¨p<”lD˜Á~;ƒZ:I\ÉÛIHÞù“% «dR€Ö»{RI» ôþÈO[·ƒ¾„4Jb´Óñþaîá’Z,Y<–àGÐK" ¾­¸oãÊ{$ÊæRE_^ÔôeÔí9ö›õØ[Ç
+at¨ì·§’7:£éÇôÁøIËflÑëý7B±egã'yI,ÈöµCÑ,QfL¨­ô˜?Ã,âª@1aÉ!ìOSÇ„uô`½ƒ@ÊåÁ zð"ÞQ䶬Aw‘YÚ­¾¾ç.d ‰vÛä'q»,,ߊD“˜Fv f±•¢ä@ŠÀ^öCÑ—·l`ö€÷©h–0¸?Éy ;
+¸¥•Þ°‹÷ï@zðà 05ij•¨ðЖâ_’ee‚1=”
+kÁ?°~ÍÎdöYåo÷KGeªr×j—ºÎ)PN†h¶ÉŽ}@¸ÒXºu¨àwù2jÁ…ö—.E.b»0¢ýu¨šK7wÛ¬T Â:ú—ˆ,Ó_™="IÁ3üûÕ>(ójÞþ à‚˜lCÀñ¦•|CçyT'ø0*ÊÚœJžØé­è×±èWEP³“ö×M©UÍ©ö³%£,$a0Ô“µWºCÇ Ü´Íô5"ðv|ßm»ÿkЀM“ø ˆŽöX@;;Vdíã)ÕœHCñ)êUl–?*VG@²þ(½µ¸—ß+óÆa·dI…"Ÿ¥[Þà‹™¶öTÂÆ
+{ÃÎ:
+±.1F¯`[а»Ýãä¿ÿœkˆw02ÔŒ×\#»%ò
+/ï‘iLóL ~Äw[wU!ÚØ°¾zÛÈü=Ÿß&‰à1˜ÎØï¦á™®“¶ƒ³Ddeñ%8mIÐç¾ïÞ⃼ê~Îmà÷ÛÜ€sxÔ~÷ÖÜ0|oðæoƒúïOF»ön½L´’Óá¼ïcQ"÷áúEÄhϺ—lœ”ÅC§Ì1™À¸Ù*á`,w}9˜7´ož ¼¸‘œÎp½,Õ·˜½Ö;¬œýß}îÿ'ˆ‰€¦X}:F–`ýeÿ.wiŸM¤‰µ§7u˜ 6ZÚYõêsôy†[*’Ô
+ËÜu@ˆ3Åè%)³1„ Ûs
+ ¾„r±ëßÎEosÿz(Â+·ØWVX£Iݹ)
+Þ8ó2ÓJ’üØtdc£Ò%Âøb¯Žóe €„Ãÿ Ir¦K«]GʼJFT<…"X–¢|+ÙœüŠû1 ‹ë9j0§äNúÄ™ÊvùËÎG™®-ü9¶@yñÄ”ƒØ¡øqéº%
+G(“@Œšî&ÀˆôjŽ¾é·µ›Ÿ"9àÚþÔßwó‰ÄàI ù<Iä¡Cz¶8õc–Èdˆ,˜ËI€Ó"Q£ñ׫D^¨”±…û®‘¬
+.“Ë“·º~­ºD•
+RiŠ€'{Áª§ß%âÓígX“R4ÕþqÑÍíÏÏ‘—ØcQp}.R+oµ”g/!º(BâQgåa ‰ˆPý°z’•Ó8NÊC÷rî
+ÅdžG~5ð3«Œ§ÆÉy˜Tš¦hÅúmÑ£üNŸîvæ[€3}‰M5kŸ° Jƒ²m|ôD«7&H­!Ñb01¬Ë–ЕTŽÙLRµ ï[Î2|Â|b6…]dáHQÎlÆáAmâBì)ë¨ÙL‚ïö2J€lA³cl£E0²ߊuРß{ž êãòÌ–39*ÎÛU莡Š›åö¢ñ•dìOðåGfÈò+ËÀm‘Á¹i>L+¬&v%A‹G£ mCñDܤè•!ÎùÅ»M\€ÐµÄŒ……¯GE
+¾oªöYÎIØÄe¨x!Ž’ê/À8#CŽÖÊ…K¹yzHì¨GºßßyÍû6¯âó^õ*âwp¼~ “Á£âŽ7ÉÏ-õít“è2{­þ÷) oе1Ýüà%zµºwœc¿¼“™F#%Œib4TЋ²3µ}óöUû>O}n C©°Gô¬ªÿîi¼Ç5µdh„k^"‡D_§ýŽ¦0¡|!—sXÿZy¾b^$-'q0ƒÙÚ±—·¨ãԇЎÃQ™§,`ã|ˆ…„AGÿæëØèô)M¬âˆ~üFÖVn×zÀÖ†Á×ÄåL©ÁYØû¦òJ0#®bNü¹ïæ}ˆ¾oßf
+ÃÌ
+(´å’’)ÌaB¹€çž-ˆZÅCJšÄK¤àÛÌù÷wªh›xÖUm’Èg:¿Ø§]¦&%›Î8_i‚æ,,ÖÎQJ¤Î*g"j‚és㪕ާ}óš(òœT L.’Ä© 'ì[çÇIOÿ mÈÕ¸èÆE‹(½Ç7ñŒeÎ@¼Ç-ÅÇM¹(8[Â!¸™Ò=ò9j(š8 ^±E…û
+ÂwøHe¤øv~
+pÎeè­”Èw]…Åg W
+øßL§B'4Îgð¶›Ä0ÔJ,»Ð™áE<IX€YE)™<Z¾I¡
+Ü(`l'=E$ëètã´'–”Ú¨Ö—ì²”Z貨ý¢„#°ŠšCÐ{aRÒ‰Û~Xãè [Ò«ÍFò„˜‹Ö û
+ôœ“it³#ÒÜHK´êäËUÕÔÚqL / Z*írŽÂÕ—óq>fOÈúîzEÝ­_j©›Oã‚<‡ ÄdGëвÃÏŸ‰;ÿŽPÑ8 Çmårª-=ðÿøl™ÃÚ –Ë2mB
+„^Æ!ÛEBâ|¤R©>G=Eò×ÁÞ"4Ùß!ü];åXŽÕXêxƒÌSíQü3‹fÞåƒa”ë†EOåP'¦­a-G0O¨eæ e1 ÄÀM.¡tKÅK9c.Å‘œš høRCv¿#£¦T)§ºv1]†5Êpl´4n[X[¹½‰‚bıÆý3ØŒ!iª¶­Þ­bÕ¦eV c"åÀÆаŸüŽð×P
+Äã£Q–5꾚+ºÏ¨ŽèÇ D;÷¼E*½@­‰@¬ãø)­ßû8÷©…©!{êš9Ù#Å÷6l܉¿S½†8¬É½PÊìˆ"šA›Ú"EIˆ-Ý«žþ~eó“ÄŽ‚‡è"œ£åÛwšJŠg™1IÁ"šÆp´aÈ\¬¸på¼J„ó)ãW¾Iõ—ªß o¾ÄBÓêSÒEñ‰R¿­í4 fñÄØ8+ô Ìz¢ÍJ¢dÌìÂ,
+øƒ¦ ¦ ÌJ™gÕXrY…V Þˆ…d—eS8øïE¢ó²ˆó‹Æõ²Ë²2ÄüèÈ.ËvZø€/¬ú<–eo­<ºd-®>Ëv̘n$åê³,
+à£u÷Ý›eÉ›eÑ“e¹qÜôÂTôY–øÑîA«K7–í˜&2BÃeY=15ñ>î2¨J‚'ð{dª]X–Ò¢x£ø¦dO´$ü­F`©<’Ï,‹„¦%)ME|aYD,è¥ñ”E“9Òh'ŸYÖ“¼`VɈ¡Ÿv{YOô€YIHf2À³½|†Y5/ˆŠ?ó¥r¡Y¹‰éý0\šU‹³µMŽå
+­·’’Ï¢dŠëqlïÝÃÞ¢°%ó¤_˜–¹Y´s*>Ô2 6=Øöç@-#†¢/|O.Ó6È N*õì3­#y3-÷U…SWù€æ›iQø)(Á¦¥í¨·.3» -ÁSsÀ~˜ã…´^„H»ÚŽs ÃŠzÉ|!­Œ!œ)æëw’ʱcŠgz ­J‹ÞdPççUO¤]|H¨~þï­^”ù`Ú_Òê‚Ú/éÀJ¬±31é3?{ÿß[CÍ‘´AáEG´$& Løôqÿ@!
+³:¢ï7ÑÏ<ùÃD
+Д§—3kîC—èÖaD‹¦šúö.
+žrɵo D‚Uc̉C@ ˜ÍÃ[:ñ!¯A’±êC©tÊÜáÎgÙ+Ñë«w¶þoÒUá£<¸‚Æ|
+žÙY}°ÂBÂÁÓTÒx=§žÏ°ˆåäm9ëg¨ÉJ5g'ª=&à~~ùJ”ù@±ùöhô"͘cÛŸƒ'Þw6B Ñ ÂѼ® éu2Ão¼% Y<
+ÀÚzÜ)IÞ%X'Ì…Ý‚d³`Ó¸ÅòíŬ!X!óÙÅ×q¹VÏ‹Wë±`Ù[TsåÛt[Þ…Cø‰jŒôH®>¿þÆzJ«ðœR!©é†B'øøðØìË$uõ #µn‰v=î2Ù›„nÄ—³75æ0n1øú´ÏÌžY‰š¬Á>ƒ¯c¯8èðp|Kø¡VHÈÕˆIL0ðxò4ùC)¼x0 3w“¿ìâ7EQÙ3Ó)‹—¨È”1o¶ÍöÅŽíE Wú™šŠ˜ lNÙäŠÌ³–Ìá¡Ð‰€,¶“®„Êpù™M°P<¦íÏ`š˜T`ÙˆþØ¢.ÈÉeæmüUx¤&h5]ÆŸjgôÿQ^æH’ÜH=Aß¡e
+iX›HkŠTçc£U‰s¾
+¾ºÀª ¼krßµz#¨Ëi!lRÎEÔl‡^¶]$6’ER¦pó:Ûʧª6t‚`km&±û¼~¥¾¯(ôcÖˆ“”u§•æL¨Ëœó‡Ÿ 4H4+xÛõ11èô¨}¯þ’âÜ¢Ôš[Õ꯺L#k
+%ý‚¢å! ¸Ü?‡’‘mq4–ö®¶_›%ÝšR¬ ýض¦f‡‘Ú#˜0{DT~–íöz€á =ûwɘ4!Í á–³>®ÝoÞ?ÇÐ{‘áfÕV
+q‰9é}.&©Ëä¶eRL ‰jŠÙÖ™UDV‰@⤬ĔŽvåñ.æS?n@‰5áën)³>5b@"#hËÇ _‡õJm6v„´NS"ÑÌnÛ4Å)ÔÐÊÌ(*Ô ï•xéø øœÀÀ‰ÁЈB~š˜—3’HW@p¬Ï{4õŒ!Š6!(†hO‘¾X.@!…ÀõåU•,“ŠS‡ñæ9\Tê‰q_ç°ƒRŠz˂ཻÜ]]¥Ï]à
+Ìt`7öŽ¿, ¨\À0Y+~À: ‡.·RÓMÀŠÝ©¼šˆñ&`¹ sÀ/ÄF¥jûF¯ ˆÑ<µ°€pûB9p;ãfÛ%Y3D6Ç=Ò¯ HÔ ˶GäÚ­ßwýgÂâ4-©8·'»IXê>wüâÎf-É Ts#ä‡ý¨±oz¹D¬X•¨:Š’º o R7dyK̺‚œ åÎÃÈ æó#e7jéåÓ²¥[±ÏQKÔÑè’šµÔ9)ªŠo¼Ö_L†F1°tLV ~dÓÌWœ
+ÚJqmZ‚|Yï›°‘dŒ..¿
+2(!cN1Ü°SªÃNlkp [W“£®Ôñ&è¶<Ð)l‰í(WäaÙ [bM~Ç…)d¿ [ê;ŒDÉpÞ;½¼„-Ã@mä(^C’6Ýdß¼µÚEdâ;ƒÉõÙ.Ï Ê&xaKdÈáuXô}È9lÌË+Cj­V7lÁ£ê:Q3ºaë2ƒ^ÚÒ€‘ÅÈt19ÁK[:‰FÊÒ-eº¤­+É_ið+:i+¬ŽõãÒäµ:Ú
+»;I·¨òL3˾3%Îè1âøXyèØdõ™Î[Lø ­ŒÉðRÉ¢\¬ l€Ÿˆ^Ö: ¼¹ÌË‚@2’iç¢ÁCÙ嚇gúçž@W Ä1"Adݼ‹ác£¨°c`ÍfGŽê>eÌúPÊŠµºs’Gˆ¢Å‚G
+ò6ˆCó…±ä‡³„E{]`‚˜jòe›VP^ÄŸÓ'
+@f‚±áüª†×
+0C3ÿŒ„‹òõ¶íƒ2g`aZYC£n7ʪI›ï©Gë™l{~è˜HÜt„Ž¦™ÌyÔl•9 H*‹®ÈÐ;C®úCê°f'¬Ì¸`œfCƒá)ð¯•71#…LÃvØ8tÚºèãÕü{.0)“AÓâý=`ȽϾ`.6ý
+K(Œ•N6 èz®Ò$~ž”ÌÒYF~PÄz¶ñaŠ~؉›cèkdÃ}_´?!l7‹R‘ë%Ž)¢‘A̹¾D©$±´t÷±]x‚ÁúRÐ]PoˆzÏ&
+ÆÓ%]<àÀyƒÿÓ-0©e˜Kß> <TÓ­øa)Èêã¹slv–
+‰ÀQÂÅ~ü¯¦åÓÃxv‰!t±ó¿]œ÷¹·@1‰
+
+˜$†Qµ¬¬S´"Ïtä ÜNžÚuB‹(«µÜ*S•ÔkúOº›å®œ$¦ô7Å "C)¾DF™çðkÚÊ$ísbi˜ vn˜~Ìb)mšºd}
+ýÃâ")1úç4œÙ•NÏü%ˆâ£¦ý9D
+7æ鵸0s‘W‰£“8§ÉaN_ b!ÄÆá g•APZá6Æ4\Y–!qì*÷•®Hfëéň:Hsd=e&PØÝ9Œ
+Yäu›BI-[0ÂìÀ†­2-x¦ùttBÞe¸Sä>äÛQu@×Íp>÷5è´¬o,´Öþÿ áþïÛÿç,ÃmܶâÒ¡¿ 9!\ÀKèÇç ¨bŸänh¼fNÆv$Xf´È8î›û„@³…J`!Ç„Œó§i«ª|M˜6¯+šŠÝ²rα‡òeQx˜t áE+Ðä± Ñ°ïú»<Èñ)cG'£MñÎÉ©j-¸ÛÛ@˜¾Ôz½}•=,³~È®Á¯Z!¾ŸŠ_còÒ«ý©3¨ãw³ 6úîßBÆtm¶Ý¼jÈ{WD%­}¼VÕÖw*uƒy5–32V‘¿ìn§^
+LÌׄ€ÀTwævA(.uàªjš Œ¦¦‘&HgB(9sà<NÒ¯ÿa¼L’#¹• zÝ' aÖÔR·à–¼ÿVÏ@I•ÕäoÉì7å1ø@Qæ†L> 1":„v ø2ªï~u¬îŠ=ØFàc¿J ×ø.ôÏ PT²ƒ©ÉcQ Œ,ÿ`ßâù¢ÄÛùßÏØÞŽAGŸ­„ˆkJÖ µlH—¡g>Êy:«ôEÞõIdÇ‘÷óóSgþäˆm_¶H•ˆÖ¹øÁíÖO]ÿx¤,A™>€äÊj“M£i+S9Ï|`Šé펱†,$+ls=¤ëXu|CЧ ”ÞÐÚx`p"’DÚh‡ê?p pÀ>G>CΘ"mˆÊ%Û‡;-.D©“Æ&óSöx8 j¤®g¬zÙ¿¼YäÝðDá­ÑÚÊ€Ál“ë "^zhµ¡Ç\ɮТ0›Aâß±½$ ˜äȧ’Ÿ=}ÎF,ÀÝï$‹Ã%p¿Ò£Ûù ä ³”0œs³³wÎä‘…¿²ö5(<†S«Ù6 k/ö*d„#Z¢‡ð¨Ý²ÇpÇ1»
+…ïûœ<Ä’˜×Ã>ü
+}èңߌÑ+EL’v¯°•lœh+[Cò–•]ù¨´÷ÝaŽ_zm=Êb2˜g4. —e•h<ü0ù­eb‰¢,À—«F‡WƒÖ’W<äÿf#Ø?ÑM—vÄ£í²k½€F7…ãZ; ‹ÓÁB4Bc~ò†078DºV²±«
+üìä¹ýÕ9mRäÛ|@x¼¯íkX$¨oÈÛÃl òWia Lñ¡òePgª½û0℈øð¥ y
+£0‹;…Ô˜¥UÚ¤ø†Ûàlî³OfèXQ¢—m)ݤ ƒ©Ùs ”Ïò7"üNÿSæL& Ïte¤í[.“øí‚2¼
+'L­`*Ù}øb¿Ð;çÒ>F‚ˆ›(x·(£ÓQÛÉçÙM—©hïšý”E4ÅŽ¹ `3›²•­½€@pC#b<®úã ÿó0™>³tUÑqšä"dQö™·}{˜•nátȯ-áÑñTòQ(¬•h*h½ûFÀ6xéX$.¡…˜\Hò|w€¶øƒÌYÇbädŠÏ|D«4ÒE‹h­«“ 4ÞȚûr«é +*̲ t—[F0Á: k\Çù– þ2’Évž„d€aoâ«q/1ÖmʥϸÕ-à¸a;&Hó[Ð%ðˆ¥ESH˜%Í7›sªˆâ’üÇfˆ$!Ͻ©·aª¨kê¶Sò‹½,õ$´'æ½uâp(.$&B=/ 2½'{n¤PYÚÑŒnQ]¬
+ –Nkñî öŽcnxE¯k²rÁÀ±ÂÛí—®j/†
+7ÄN); /UÅ8¬C´»`DD›Òª; ÂES–±é¿¼`‘¹Á,ž—ß Š—Ó»^©ô8 {æ9jŒäÍÀª>WÞºé0.É…ƒä{V¡,†¤Ñ 6wáKÐv/ÐN’ƒz!|ÄGF˜Í”5¾( æhÛப
+i "žû2ÜN] 6—Ž{ç\ææþ5ÎüÝ_åÌñµ:Î6DHH®{xZªÇW‹³êßgtÂbn–ùÜ@×]÷n»sÆ_´HãíoA™ÏО-„aBØÆâ¼J0âp@Ø6‡.@Êo‚I@®í³Ì[˜ÌLKú.8Û2MÆÏ¢ëCÞR’²\¨¡‚´“Àc¡Q&ÿ°˜¿}Š‚E£Ãõ8¤gû‹ÀÌÊþ$²®Â°»Ž¬úç " §O-ëqZ¹âñ*ök.U¤`}hZ›ìÈM²Ó`—%¨ÈœA@Ldß]s@àT^‘cÞ—U¶0i[Ù‡!ðã”XvhAJMŠ'TÐÌþEm`>zy<æW3ØjSÔ ¥Â6ÄÝP ûqoØU×ì߫섆{·¼s.=¿Ímv¼WÝfð^g’¯5v O½úüÍÚhÃÂÞ°ò>xZ+8»šœó¾]PÔÅÄŽ·…€¦ J‚½®Áá£bšEÿ0AŽS?_D}+'Y]œºú!H GQ/ûÞzÇ©kÊ‚Éw·éŸ#J£CѤù_ƒKÓ_ÚñDÓû«þ2*–G<ž–ÍÅî««}N#O,bcç!
+À Ð+9tNï´ûïø_Ð6iÒü EÍëXîæ6·ùŸt5PbS£
+¿§5‘zïÑzKÅRÄ8S+3SÍΊ5el:NYéQ„²…Ï,³\9¼°ŽÎÙþ‘¼ïG8·ŠO€
+H‰”—Mr\9„Oà;èSA
+õªÅ8õŠÖ?~|ûß„7­ÅCä‚ñR+MíãçÔø€pÖ•—pÔ…!_Þ*ÿSkð‡V{Û ·`_
+s‰¦<Ámç˜/^é–è ”aZmÊ?ëáD–Êä”v.²×Kð¦¢z@\7NͺÕã!6û}¨Û‡ho^DŒ®ž.Õ­ôÎ Ìg#¬n˜ï@Mœ 3lµ®9¦üR¥u-+ÓJÞÕÞ5
+ièí ò÷þüþí„95ºÑ~ÊíTd<NÃô6{‡hÆÜÈà<V7Ð"yéhFÆlç±¼—’Ì?ZŸîýyÏÑ!Ô@Z9`î´" ríÂ(œY©h…ùòþÄ*€˜c/=Δ"/¦Œ ¾Ù™ „cEhÊÞ4žHE8¹ÔLAÕ§sZ¨;˜6ê™TîE8qÊs¢BY%›¨•ø™Rx•–Ž°EŠï‰ H rÜÍ+ß} 
+)>:\I’N)Íj=PJy!î”ùòO”R^Èy•ÌqŒ3¥
+¤W[×#oŒk—‰@}i 7ósƒdÕ…a$’p<‡Òn5Ébå™ÖoÐÃ7©¤¼ð'ŒoêËÆåÃiH/(ï.ü t›«ó>üª¦ø˜ãðA„‹bšúì3ƒoöb@¤ªf‹ëÌÞƒöEG«OHÃÓɘZ__ŒÌ®ZQ¼Î|´wî Vl­Cf›²ñO”edRw%Fi†ìëîyú‡ác´ê*§àÈ›:}VŠN§µ¢ÁÂçs°üÚè^`’ÓºeBÂ`[¥†}ÕÓ9!:ƒ¿X' í±Å‹aÖ6š”6uà_x³­ô!&Á'CLW$>à†d¹U„ìW®k›mßA§Š³^ýX-ÇëË ßÇïó #z òÇ_‰ÌÄ¡˜ª°Žôæ瑵õz6º Øw*ŸÊ6jÈùÇâY'B ^ÃëÙ:ðÏÌêª|{ h˜5»·åü÷•,!Ü$‡V÷ÖfŒ2©¢iÚ‰1Éå¯ô±7r¾#:µ©ƒ&€äí§3k mÀÈ@Ÿ+/L4ÒGètAXð¹A~×9)ÖpãPi B §«ý¶VHä({°Éž¿–zˆì²ÓÔ*°ñ´&N¾ôyNÉÿ—RÊôý5мËP|Ì B3M—YZýU„7Œ1”Áݵ,ˆ5îƒMmù d9§ êé>!°:”;b·•Û媘/
+·"±1K ÿµ½® ¡³$ç•>é—9ëO­÷Ïüf/³Ù;—
+øAÕâ4„?Ws@}ÐìmÓà~o¼ßVƒÁDÑ0T¾†C t*…•É Ѹhβ(E#³WÎÖû#”ŽÇœnãy‹ÄX5tb.c_ø7[” ¢ÈšM8ÉûÝ\ež/…`ˆËÑ¢ rXyì»\é®È‚¨ò„p l“ð¨5›ÁrÁ Õ¥þB0iÊ$?ÛÏF‡@}Uæ9žUêÞÛ6äš|φëþ
+‚Š‘‰\7'ÑR:
+6Gô^’Öø*uCf¤Ÿv5S‘ÂUá
+s óŽtÅ“)X…rq”mÎärBÜ®ÚSvp,Uø"éÙ9Æàñ÷Êt\B¿ä¬–_8gï÷%'ëŸóAb`jí§ý`ÊŒ‡³EÂöŠÅs¹ t>!Šb£Ùñêb(•å³èfFêd'Âb¦;ÏñL(ÂmºŽa\{&â‘jÄíùAKÜ@H4$‘ Ú¶mp˜‘«0T¶=.„åªfsM-SÖi Pп#gx_>% ÌC_â:¨[§(0ŒíëÜ@7YýüŠö®å²ò’j¹óÕXÜO $ÅÐN?'(RÞÓߺŸ^é*IŒ@0•ì³Pq:)ùä·+” väÀw5K¿[%méÞw£åâ>ÞS\¦³:¤H~ºÕþ+€0„Y†`ÏÓ‡·)ôX³C&˜ÈË (çJ6Vš‡ç˯[Wz¿j:õöpAÂZä­ÖϯvÕ¿\ËŸ Ñ æ¸²ûQëY]í t¸Ry1Œh6_óÚô„ù<ûè÷­ãóƒ‚ñ¶Ì‰\Ó²óÌuÄ6GÎ^‹£]NɼёË4_¾ºñ_V×IEF³4}èV;Ï~¡­xnDouIöÇG –~2ß+ `ägOçT®‚b墶 ì@=ôãT‰Û…¿Ëuâ™’¤Ë¤rº‡,Ã0T¸ä•eK!±9™';Lúð/‚ì¥:šê2^©^—²Œv:Åí\˜¡Ø×ÅÛ;ªZû„`ŠðËUËÊ]0‘IÁ |òt¬‘Ý»z‚­”°¹iY‘Ì(Iµ•=º
+}bj1l¿7è÷o'Ì¡ÓK>“µFíþè>|§hÆÜØà<W7Ðd–Bn%·cT–H[¢—[å“ŸD¾Š±„é–_š€Ç±•}«HgrMNßñö¥Ë¦F´pcÎ Fþq¼Ó ôù•‹¯÷å®hø)ÒpàÄŸ Ò9[ßås‚(ÞƒÛ ö¾ÞXiÞ䎂–Ãâ¹y±|=þdÎt؃7+ý2“lDXS=ŸƒJŒ–ña»µab aæ*Ø'$==”EeÌ­)_Ξ@•¹X½ƒºÊ}ºf8œž<Iý;ˆp<"÷4›Z÷É= D®E¯Å³ä**Ì9vÒ–çl=e1_Ô›n„€·W¤½pƒä”mkeЛAÂ`Û5lp5·ž!«ÿg¼Üuä<Ž(üû+ôýt$Ë0 Àr‰´ŒCðÛû;ÝÕcîü5«¥‘ËâßÝU§Î*$Vð+šY`¶H<)#›'ðª¸)~Zœ;
+Ú‚ÂóJJO¾ˆ©ƒ8Ø’A“ˆ|Eö3¬Û¬X‰qASŽš>0!0M|¢,ŸÆàx¼ÝögàÀ¨_¡žP¤‹aFvI[GáíÏB\صțx[°1ªžÙÄkÑÕýÉŠ®=~ý«
+ó U˜YšÞÊÆþZ!l¶ öŠædø­³Îq¡¬ÜÈŒ Éifß`œ9õï 9Iž)YÁ q3M³l´Ï¨sû3E;ŠØ£/FÃÎBpÞ% £Ùî¦äû’uT¥µà‡YýïTä[1l˜Ú¤ €ãQvïwÉÐGÂØ'±°±àÓ”{ôך"âg ÖmÙœ`Œà#m}ã™
+™µ©Õ‚Il##­c—@CX!õäBš¤0ö^N‹gDZeX Ðà…ø 9ÔÉfxóØÐ8hëÃÜ?”ØAƒò¾ó0nï.ïQã?é|×Ö8~l°SònN_?³/Z­pV«È«bÙVç{ßÜ"h«KoxcKûZ¸?¹cÜ_ñ9*ã%^Ï×µ=7ò ŒäD§Gµ- ïœÄ[¦¯’—É{j ÄöÊ”äúßÍ TÞ­Öéߦ®8ÈÊ!Þë£*þ>ÎA®kÃ>³,EÐP—á§D(2"¶6“À€eÒ«hPa; a)àx¥¢Ð}‰/Eïÿæa*€eŸ‘Ÿ7»ë‹M—:Q²¬&T»W{8ÇάþY`BÕjÃNe4û¼œ¦3¸ÑC§ sÀe o.§ÔäƆË,ðqÄå+ÂÕˆJóP6Æó<³ásd©%©Ê¨–A5>–·|²Ð<§wúohUàÛ„Ò:h€‘­-|¢¨8ž`w¹¬œÒbpÉtúx/¿SLj¿çóØ­žÓ3u¼›öek)íLD /岈°5 !ÆõªK¬’B“ Š¡=“ÇHmF9òØåÞ׺J:×pê8òˆíÅ™A‡ÑŽšrIY§8%먄Ëh, Ü[ž|'Ý2t>ÄMíÜFÓBY@kß%ìXagy/58¬Ì2¡ ­~ÛEP>ìQŽW7·* ‚ía›g-½‡{îóÒ@ï;cà2Q¨H½Ìmv.ã”#,Û\Î×Nz@…ì(­A—K-ö™
+©Ê÷ ¿|yñŠÚ-À¾R¢‚vA5 œóWÇÐ|¦†ÃŸnÈ0¨}sk€æcÌ šu‡ÜI)0NõDv Š¨ ¹¶Ìë5ïQ€ðÇO¿S‘¹Îv UQxìT=c—0·.õnÇ`1Jh>çnà¥ÆiÎD‰³&@žŸ­¹²ÒZw1.§æí3+sws7ŠÊhUˆÕwsnÞ± Üzmq! v ÅÓ'âêT*íu-`%]j)$ Ýws*BÀ¸6L’Žlõà%0ÃþÎ" 1j;. „ßmÜ ï¢nP‚À{9%ë(&R áõ¾†ßi<Byõ<66ãÂtµ4+Q’l¸­X/±)ˆâ±ËFˆSôŽêÞžEÚ
+h‰åŒIgóñŸ8ÆÁTéÆýæ­ J)ô¸µ$1›‚-@?ŠÍó|Îè¯ÞqŒÛC;­ Ï`žñ°ô,V|¼ðB0÷±={½
+{-EÍG6rÎàr“­Ÿî°"£Â?³¶„E™
+örЇ‰*ÚÊ]QYÑ(nê›iyãp·l% «‹3;·»“I*¸XY—YNt›¹ ?*§;ð}EFÚ3¸ór®È-95oŸÙ-­àÿBF~ t€i@8¯„FX ê·zÑZðåÊ_ÿF}| ë¿_þ‹Éý‘?þÊߣ,;Xî‘ñúÇkyý;!4#% rÌ+óÕÀ¶(~°t]N|‘‰BgX‘¤Tí€%àR¸ê?9÷/ú·û:Köo‰@qß|¿à7Jäz¿r³?^cÐ=^ùYçð8€QöSçùÚÁÞ » ‡H-KË
+é1pÇ ã³4
+õQ@D¿o
+6Næ½!—›’*TŠʢ§ˆ'`Nú¸r¯ŠÈ“ˆ#¿>]Ãa ™¡Ã”yáÙ«áˆ)ăÖî;c6 ¤-Ðú]’ð
+ìX%¸K‚QE<ê.)ØDbùí4Ôð¢nßÁ×”!~ÑÛ÷ Dˆèþ\N%¡÷ gTÁÖ*Á%ı:_7(x&•¿òŠV ÖW—«¹ž’¢T+¼U» óFG
+w+ö&Â&•é–tŽÊL.ÐŒ²ôS½ DMz rãX\ˆÔi%™Ááè¯ÔËÂZm•—¢q_Ü}ð&NLM2à5ÐçOÉL?‹€›â.º÷Pô?ÆË$¹Ž#¢'ðxFÍÃZ^ê(Ôý·~Y´ùûWۤÊ…® ‰¾~2£Ë6‚™]Ü
+×†Ò ì:RÃ/çåCQ—Zãïððãc—¤:ðýÚ•NŒTk"·]£…NèKÓJjŸü;ÅÑv×ðÕ+Æ•Tè-;—¶‘¨1˜ò„Ä´K0Õæ Bs— ˆ\µä°\%€2ËzÓ[â$òÔ²ùvQ,‹…mk¾ðËEn°cål+1Q’ôÇÍa°æƒÄËSXI7ÇvÛª’%Aklˈ€"¦æÆ+1½{ž7å%î^“ó3RP”•9ðÛlJõÅ›ß _€ÖÅŒWˆ§jà²g«Yƶ§”T©k‰ò¹ä…y|§{‘ˆçÎj3Úy-¾èð¼a šƺo%ËI’Ä„¿3ë¢ú=¯KqëJ²à³fR"V”ìRÝëàÛA|¸ôñ 
+6r–ƒNÕ')Q®Ëj%(MOëlB¬ÑOÃìI×!…]Â{2Ô˜óá·Ê Ÿ¼Ä˜{+°TÊŒûlÎ…8H!ö­I ’@…',Îïr¤‹’ÚC Ü(ÿ]à¶ÍOœä³p懚¯‡ I°›ŠÝ™†bÁ¾!¡¡[,cµ'#@åÆJ¢2!Tù¹ž˜y¯]®»8Á±2ø
+ç£3pÍ™¼7R¿@O„ÉòαwbåMÔÄz’}ýˆXH[}R&Ú°tt>¬­c
+ð/Κ•ãgEs4îUð,Óõ¶à{;ÏEÚÝ[…O†˜[­e7WŠ‘¡i¾N—ŒËzæäl§vá¦jÏÇ&™·BÈù…»ˆCRF0ÄÌÁZ{:EÀwwŸ?ÆŠWë³/ÓqÈ45äÿZ\~Õ‡ÔT±¸5T»¯lÛm&˜{é/¬aNX'λ›Mè2¸¶Ó¡äLO·¢ßEo¡åPCKÐ/€í
+)²Ðäì^ønÌÁ(¼pÕôdð¡ø†ß–Ïòhs
+ÜJ»cí7 Ó™°å¸Ä9žb pG'ƒ,I*OëÜ8C§iºÕXir3O‚®„’ëâüb§ùûмrØ{ÀŒÂvSæ+Ýšžåj§L~x*ah¦bU¡_»éa)-6¹mʹÕ|kîí|;·jòôÊ;}ØK¡ôÖÞF>R*0„_¿ÉÈ2æs?ñXvú
+°#ÈQöŽÏE³†Æ^&h¼*NÑ7’åZµFǧ䔷ì5½ŠÔ… Èó4o¶zdbÊCIãü°{¼ŒÌ­¡fâ9.í/ÃY
+³É [o™Ä¡òkvœ·¢qýú‰ÿ–EÅâ {Û[ÓÔYcUøã%Ô‡"†u ‰f>.Hø0£ä+§‹ÿ¯¹ÍÄïãf?*ºùϯ‡¢‰‰‡šI„B%®ä÷L  \ÆœÕJ
+r9˜—ë{¬XE´£ëyg=w?|ª¾0sÿÉ›Á
+ ;“Íö+}/1-V #óóã:LÛ”úõ1Ιõ­·ÿ:öë´ iƒ'Ý1‚pÈñ²qÚ½èÌ€ÓS99§ý:(
+€´´³¹Z Â
+‹y×ÍW ¤ˆ‰‹½å9(¢Y"—CÉ{J:ð>
+°µ²×Á 3®=mà<˜/ô|ì#Y¤Ë8y³ÞAÄŽõ@üšo*¤sÅ‘oxÞL.3Ê)iŸS€Õ#ωÒõe;­D®|š€7ÂW#ºÆWª)äÓËÖ1š ‡WÜËðoAÞ­: ù|²q-3”‡„²ââ²K¾Õ½ˆ»Èë²ÚŒvb<*/Ì-ÍûêÔ'®ì‰#K5~L—ÌÜÐð9üý`0€OÙ¦‚ç„hÂ`û½”†'äLqœ|ÍBZq‰xÞ³?Ú
+.Æ ¢ ÆúM^¾ÏJ%@ÁŸ0&×V¯E˜W‚"wdBšÝªkàk2‡ÂÉ™7N2\3+ïÅf…2-B
+©XÅ¿KH8H¶@àõ"D*’ûaijõn«çÆpÈ+n‚>-¨ è…_݆?¸!8I¹9rÂç]¥›w䫘Ð
+ññò$×Ê”‡Qqš8´•øò¢ÄÏ>eÌú"CŽ ƒÕ6ePJJkžšùb7q‘×n¥ïϪ[ª
+ú¶ Ú°Åé¶ç˜ÏW2dqû£BÝ]¶'ÊfÙƦ–¨Àè81ÌÜÏ1ÒïÙãÞ ¿ÛˆPsa)ú¥@ŠF§ (gH‹¸,Nq£ >æMÐP)üŽ'É£<Á.ÝŸ6ËCíMÄy_Òñ°Ž=R·x†ZŒWkÝßsæ{V¾<§Š”9 ϳôpÎô‡9®‚~oàU`³‡ÉH-²ës圂؉¼©ÒlTš=;]–Øo¾i&5 ó²,ºb¸Ç¶’CŠ‡Â
+¡6Žá‘]°†òÒÁášld³ ú·J¯œs’waA0ÊÅÞë<§tÌ —¹é]бÉv”PÓmƒ?‰2¥öµuÑïâXˆw ñ]ˆè“¨´ãjEâTdÜðuÄ"ÈÁÉä ü‚{biZZô®Æ÷ød‰§$°æh“Úùù-:M/€O.=Æ™¡Ám²¿¦òqR1”Hè3dPbÐðÀ³£
+'¼S„ Ðľ Õñ)¨ÞÕò¤ncœ XªÉË =°Kݶ±+€–uSzËåzêp ¬~š>UÓgýî8ÀäCH³:Å£9`Y¶ö a[`<J_ã a_÷ ”9|‡5¡dÆKá̸?‡ åÁø^ab/ƒ
+Jñb`o<{¤«ŠýÇà€”½4c¨.ŒÓe : H òI¾îÄ;_ŸZÐ;|ÝŸ+Ájð+¼öÉœ@qx“]Å_U—hç¸ÝhôµiÐÄ~ÕÊdnuÕ¤–o;2Îã*Žâ—…üo´® ðÓ_YßßœŒBQG-ÔN¸–¿’<6þJŽúË/ȤŸoþöÛÀ.)õó¬ÿ¾¥ÛŸÞ|qˆ‚Æ­m®r&ö#F·ñœÞ,3ìO7µq¿#¨w€E½Mº!k¤ø18…ÂôàUñ
+ø­zab˜:š‰Øh í/¼îϼn=q1Ÿá-Í÷ÎwÿJèÏ7ÇÃ</ñNO¹ýí;©Ö[‚ÓÊÒ©ó´9Z çѽÏi‘ï¤÷æºíwì‡÷Œ*¨ƒ¾«`Åî pD'!Ý
+ÝѺB¾Í“˜/€Ä«RžAŒvD“±‚ØJŸ/¤4—û÷×^þ a\€ª‚q½ð§õÒý¦
+ó_ß~ @P ) œÌ-Wefæ‹nˆÊ¯} Ó‡d´2ñ,Ù
+Ÿrã Q” elžcˆ]‘ßN¶]ÐA‘3¤\–-„-N²
+Þ30¤4‹ÈÛPíœgpA|E£ÅšðÛ€‚·<kºðãÿ0DBß¡î­ˆç´èjDíü!Hº¤Ë´°JkŸY.ô)’«=ÁŽï‘ÏZOüˆž:z=C$ßeñ¡Èš¶
+ˆåцÀÉM~­IØ…¬ÐuH6\“E„àš­ÇTg·p—ŽŽ…2ÔÄ!šæø!?öÉîDWëÒ˜½[&ž2Kë1p[¸ó"v.CAáÛÚû¡’0ð̲Ÿ4ˆim è|ÓKA ‰¡Ê¸ÐË â:Ü¡äh]|° ’¹Ehöb'±2ð:àWšÂ&0 ü¹4ƒ evšzœíýI^ˆj€»ÌË>¥„íÖ þÑ%ªß„XÓ]Å’wÉ}¸`G6þ«Z6 «œS)[|鶅ƒ×Uœò"ƒÙ÷xW°ÓH8irÞYtÎ>­\Á“éã•ÑÒú9…ž×9|nÁ£•«)<A „qC£„T
+Ôõà:CØI^«ì3„`”¬Ýi<EñŒlêbÓ=Òç­÷Åw’Þ©ìç9Èu™¥vÉšÖÄ=P¸v=®
+w
+ÜBr…Ž8©+ šUåÙ(n‚žhñó"Hv«³d/ž—‘ s,¸ `£>ž`’³cv6:j'ÎÖ¶‘ú]=õ‘ˆpgI˳†y ðÖúõÓ‰hNi>ëÓ¥k|±vzµ;çÐñs6'äì^uBà©6¬ ¼ yjÔÇ+3ó›%‡ÔÇ¢²Ø®TÝ&È;ç%LØûá9ð@¨Êb}¥¦ 
+‚ F·º¥2*¸SEm+©Œ¢žÕá¼ä¦2JªÐJ|Å-MQ‚8ŒÒ¸BON‰}*ê¼J,^%Num§›Ê(aX°/k?÷Ãï©ì,rRÙù±#•]W&kr
+à깞Þ
+ R˜T}ðW 9®±?sÄ‘ÊÎ:©ìÀÅÊNlÝS™‡¾#–ý+Ž?$ šÔU±ñùÉEg. /r+MƒzôsYÀIëŽÐÔæÂ3—bLžÂwÍËeju*Ø]תGQ)«Ï
+kQ
+² leÍî)`°ÃìòMußä3—yX¾ç²³Æ‰\AP´‰ÿ»öø[EN䢈¯C%´´?×°“H5µ×Ëô|v¯KOFqc™V僀˜DvUŸ’BݘîášèèhjÖÜs‹ÎîÀî`Áe¼¼XvÀôŒeÎFÜc™SrƲÛ¬Kwˆ
+
+-dÏí]Ãh¯Lb ÐD ÞÄ®f–ßNûœN¯äŽVÏ´M³pØø©'üüŸWƵy7Ÿb¿VQ„ôŠÞ?«¨i½áïe1¢"ä©ë­…=}];(ËaÏRÙÉCN'²Rµ­’:À{M);‡hªÚºzH‘†
+6Ç–JìQƱ#°ÙF11¤¨YÕÜzY±,å©Ý)±/ÕjOÂÍpBgu'½o¤;<-Ž¸J¤âbëw øÐvûTB¹.g7ÜLBEÝ⋲Q:0sò9ÿðÐNÇ!"
+t7ûÂÅQä¼
+iÊŒi‰\~¥!7Ÿè™Åñ`ãy\%&ò¼Î6Ù"Â~¹‰±>•dš`—< ÊUÔ‘–x÷\ #Âü»ŽJvNC q +UWI¶Qû÷fP˃•¸?•É ˜†b¹€"Ö
+þž²y«‚S*’ÒƒUÔ‚]ãÝ©¬(‡ ‘0Ýö%¬¶6c›òþ7c䊩›Uˆ,“KòÖ¼ÎA³X¨-ož¥@³HLqKn´cŸ:Š:C£Ÿ<²Ù§°ÒIµÌ½ê´A­Z¬Å…„èWHR<PШ@\ÛŸ¢å³w‰ñ
+¤È™ä¦÷ð¬-+üëk@ÜàÅ ÷¤%
+üöòÏv+ ˜Àå³U^…”?xs°‹›¸oÄ  ãqíÔgÍ—[UƔɊ{}¹: ¯Œc—öeYhÍ*ATžg*4šU‰0Ö’îç!‚—ë~6 œÐ'à5lÅÙä"÷b ÆÕÒpÆbufùQb°&°rˆÂˆþ9À†”
+zˆ‘™KÛúfŒ
+\ÒÄCh®NÑ—õ‡Û†é]%¬>ÛFߊ)L„©åi½+
+ü^Z|(Iâu2JGÝŸºi ì¨|çþ”@¯ Ò6™6쥼J˜-¨É¹ï’s“)`–áÜ7xIIJmÇäÁøhWñ ÿŒd>,ÐÇëRrÉ1x!ô·ÁCþ–/ÎRWQ˜òÚQþË5 þë=:šc%+ËóFÛÂ5ðVmÌé ú¦®ñ“ýO±_
+ p“åÜ:!ÙHt»çkæ^IfÂC( ïï/Ý‹BåIQ*”ìS§Oå«ídH¡¸¹aŸb7%ˆ![+‚  ç÷DEìm Ì,Ø+“§­É×*.‹W—µÑ³1âOEÓ$àVòDLE¿ŠŽ°rÔÔËè¡HÈ@Ýd
+u°Ä°òôý8%´ ü2¼G_„É´l_ªé!@¥´.
+p1û)"•Éãä?‘çH+,:P‚*¸’WäÑ…n3ézdæÑJ°;]˜äéöªÞq!\ÇÿÙ—ö:s%° Q¡6[wX Öâ×ÜFű„F²fá§Y0E
+éªSôå=ÑΕñÜ%KBmC&¦éà 嚣È{±hs¦ÉÕú£4ÛGéÏzÀ
+‡82$ßÛìÊ€iÈ×αå9‘…ŠvŸ³èïòúõ þ%Ê;¹%O«a©pyn7ü÷%Õg#C‘+Ñ„/ÂÐP¼´g’þCÑÇjüzøÜ7Šnô롈u
+¤»©÷ýX%I
+RÿCѹÎç¾QtãµnEÆ/]xù‹ôrÙ‘ä6¢èô?ÔZ‹ߥÑ^IcA€÷o¬ÖJôû>— V«2™ƒ2 -f¦$#nÜ, •›Åì)b“øÝÉ.§’ÀÑ —Ÿ˜)ì§ô X#µà‰1 V0 >ç,”É 3@¤{UQŽ%Š‰iYY¶•yÉ‹ÞF þ©ðQ`ÙÆ?J4?…«ä—Û=ÆHŠÐÿDȤf64¸‚g÷!N”èß`ÿaGõ.†àSγÕÆÏ3†j¾¿W›ÿ<*‹TdÝÃñ¢o˜M–oF< mÀµŽ@4uÐÙÄ ¼Ø,©â’0nŽ8DnV#­ˆë$>à€vÇIj¥¢¢}‡…䥘˜8K¸
+¿”{œÖñ\ªjXJŒÍã¨cQÅþâ©2aÄŽjð—+n9œñhM‹ÞÏÒ"6›¸,V„¸5*D·¬Q }…´—Ûe¸I¾«>æ› Ý.§q05EÃóAì‰Óº0G`&µñØúþ;Àe¡]Íçåç\ òw=š%‘¸›³vÔŽŠ¼˜øÀÄ‹¡B:§Î
+‚7)ûŸs
+Ë}W¾`›X
+ ngy(±“Ô«Pz¬ñê3¾×ZZqG¹–¥ÄTDPTìËUøª£Ål'Ë®6áà2¹˜—²Ö¶¶ò{Nõ}ÚÙÈvWù
+EƒOìOÙ]ä ˆÃ{ì /Á½
+
+Cö2>%ŠMõ2¬?ËÜšBhíiSô1‹ŠÜpߌ›¢QÂC*.$Oi¦‚tøõê•û(ÁŸµ*‚—š¾ÂaR~”M,¡ùgžïÊ07~XáÎâÉÀÔùžê€ù£Ï’Rè/E=¸( ôCBœØs±ý¦¨ÉçÒ
+ §0_¾)òЧ5pÛÝÀ¨óÜ ‚u~¸ÉÕ%&óímÓ?ŠRT{²½ÍÃ,Š]æºúœfiº ÿípñíð<Œ¸J9
+Ôw9ý9³î¥ho­máì¹ÈCÿl…¯’ŸX8{¾Ù¡Èp{kÔ°)½ô²m Ðx»<²ÔÞ¤\6JZQOaæª nHP7_vi3”QÂIY-œ³G!W˜‹ÿ2^îØ•¬HAÏA#Ђ¯­65‹çªçïö>ä+Ý$W]£ IQ Äçœõ!Ãì–Ep±í%H†*XWKàÏC]¨}À*ûÇáÊ "B‰IW:=ýuPýé¿SHPkø#’/ Z}„ieah™z÷Z÷³pü
+ùçþQ«ÅÕjØ4ˆ@¶
+j
+ñY·§ $ªàºtPnO—(*PÈØ&̓2ÊÄsé;Œöé8CA{T/–rúy#K³Ð`ægåÊü§8ÌëdÈ4¤¾ôÄ$ÖX‡ çy¯Aߧ £¹ 6Ø«Tãz2·§ [¬‹ÇQñÀ£ú»AÇ”ãóM4'=çí ›DŸòôVÐÜæ"ºSàž+/ey úy§v«Ä¦5„Ælld©>X†i"™ƒ~…Çt¾û
+¡÷ÔAìãâGñƸÁß©l`42ÍœwH&g­„÷ü¿Þ÷ëP£“7è(ƒ/ÄÍmˆ ,ñLþù«"òᘠ8¸{†Gêdz¥&ò3¥ mþŒðg§‰kSøJPéõŽ½±äáܚɳ–éshý#[@Žã÷§’Uy_'¥Ï^Ô#кÇTRB&û\0VyàxvWÛ¨lòSyˆøÕ_ÿ9z(d;t$àÝӸݎz3è×üFê5ä_è³È_@ÞBɆ_= ¨%uÚ\%bTçÍÖx™¸: ý^=„âQåVÆu"°æÑûØjËO…¯Zj¾Bk±ËaÁÕûdFšhÚÖW
+‹—¦ºBzIbihxD–œŠƒ`Ì`_ánðõªi¤¡hæNÄzú~ÒVYm1¯DD«Âx1–}!ÆÜNHÑœó¨­íâ¥[EEÃô¸FtxHAûY™ö3ã+;ÐkD>¸`m§ RkxŽ£d€6¤2Ÿ õ·z]Ø­P ?5$ãR–?fÙ;£óþž™¹kšÏ»*ÆE/¤ L>L‡WÌ©ìTÔÑG™ò±´äbÞ½dÕðǺ¥0a“d)Éë;ô;Þ™ôµB’1Ž¦–Š§J
+t²QòüÌ#¿þsRŠ°etvxÝoA‡F$˜«u6
+”"›XÙ¹±žbE½S¯^Œ"1Lül¶_ŽÄ‘Bé·Q~ÍlÓ{úØ 0DÚV³šõwz®ëþ²[,ãIaL ?~Eû)àT›ó.j&—®–³÷õGlrC/üIj¨ÉHÃ¥ÝL~$Éðn7k™'Ö¶’—>Ybʧq…`f¼;Š+' â§âãI¿ü¤ýíï[æÍ'4nÝî oñZƒ9$diwÅ-hy ½+
+’bƜݷðË
+dƒõºR¢;ÌzÚxa†ú—ÁHå¾›‚sÚ%ìå«!“ ?ýçµâ£L³inè\ þ°eK«)Z¯¡)ÜC¼)x ÊG•øô>!•„œI…£vU´¾Í˜“CÄdmÿlÅ¥ÊFÙ
+(IEÃ4QË|5ãi+¹8/r9˜¦OfUò†žº2QÝ»i"NÚ~׿¿¨è×DúDïb<ô€N÷¶`/¡•é%¶§ ­܆¤Mt"D¢cŒ¼íÄš† Y…‡D­Du¡—&J‘ 1+AÈ•œ¥YPÅàN
+©(œ>ƒÞ¡Q$Fëî
+¡D=º‡
+ñ¶AËë3Xé·µâ'Q“F…Ë ò+„NoÀ ò1')dùÊmuW’n²Ÿ…• z‚g9Âj‡"“táÂiZêbÆ$Q¤pžœOTàGu`š¬ÆaîçûA}JÖgH£24 RzÊJµÛjäSˆ^ÝÛ@‘Óõª[J YÚô+3R(XëN*Ó÷AËlZ €•ÊB¾Vùf £à´î.P‹E ­Ë?P+)ल¾ƒ°ƒ¬ðb;ÙÌ iò¿Ì·¦èœœHý‡çrLÎéìhšê K†ömJϬ/ÑÛ‚Fwë~ÿ?éånû(²À‚§Æ;PYM6ãï[ë®â&]°/Z†Ë­BB~¹Õ—O/ªzuúyªø&íE.¬ùüJ²iw.à÷Ñi¿‚.­Z²òwîã!$"i/$hsL%¢ry†ª”²GœÙ
+%ÃÐèpÆRõ ¶ Bc~CôÙsÛøqÒ þK¤Gey(ô%&Ì«IŽm“… (EÊš«I³e5­§OÄÇ.÷"oc+d:Ê¥[· $’;3 £_90•òeÏŸ”‹¦^> Ôš'ñ’êïD l±íÊ^.‹¹ª¿‰é«,k)[ŸÁ¤»9¸jéŒä˜ÍëÄMɆ»ô=Ç™rÙ“Àå`º{«Ÿ¼5š2Ÿ(`%0OÝÑ8Sæ°PFh”º$sÍàSEK¦käèU
+ ©Èyî®ÔA §…F;ˆŸ_\¯š!¨HþzE'ÑekIq~uŠ²Ž9ªý±;‚3L–ëÃW¢€“ÚåM
+lŽ5VÄdu«ëp9îWªYSèG6­ô¯¹ô!„lg®SŠµy líÌ’¨7=„mƒ‚€©#7fƒd c‰‡ Ÿ$ózŠW£€2~è£Þƒ|lzƬ£˜ˆÎö¡…0
+î³ÙùOÊ|Ê1Ë^hRf׬Â΂£§håƧjÞ
+ Žè<²&ç%BÆÌ@›¸ÁfÀ{Œ’ÓX È—Wü5æÐvtÍöð6†m ^ÖŸ²³2 EŽ`¯B‘}¦ÄÒf×4D­¼ÚÌzT HM‰×Üu$ª3½ ¿ü„óô–܈#Õ‡µ ~’ ¤¥ÄC´Ä
+:'ŽŽBXáZöÁ 
+ŸsX#ôŒÏÂ4™yF€<HYH"ø°n‚®µ> èÆë™}DVHæ>Ñç¹½ìÿ!Þ*-Ž´¶èmÝ$D_›hîÏ áƒŽ™KøÓdÒX8j9C*°V¥Ùþ\…>Aàg‘3
+Ë œÜKDGqÌÆ,
+H‰Ä—ÍŽ\¹ …Ÿ ß¡Ö¤ R”H-g5ã €ƒ
+ Ÿ ¯á­xócÀ.jéUÇA!ô<HX¡°MsìUÂnÈqSÜÍÕò$àÕ9½Áh[NˆQT¾‰ú˜÷j2J«åÌ»—Á´ñ‘Ù€ò`ïæmp.”þò¶¹,ÌYBÁºËºŒbŽfÐÜ×94©“hd.+uãÿÇP±d¥^ø³RQÚ¼?‡>4íµ2 4Ž ¢h¾ú‰Ñ9ŽiÂWBDsFôìgoaI™ZñxÎö¦EÇ¢N¡»èqi>!ÃJ0D³˜óÄ?íÆ]E­zÞô}&'éËIz‚,!m¤ZÌó< ®D‡l'é/ ¸Êä0H¾*ÀDÁBâ±ZæI&xiuBjçþÁ®")…z¡¤íAz†@ùªÖÖPO1à:YÅŽ0XC|B 㔊$1[OÀRùVô â$=ÔÕRJx{uLO™‡ˆÃeF ¼têÚ|B"gÉn½®¤ø’X…Îd 4Ù»—³£ð(5-§/ÿ”r†h¸p'V2Çç $wZOOIœVˆŒKªyјøÛ‡Öe&ÆÒÓ3€8 δ!éá3mÊ„M-Œ¥§´Ó2ÏaÒY?PÇC7oÄÿ™9ÕS7hBò“êô äœS&w$ÑhÎiÀIÚéÇ&Äp€FŽö ých‹YñR4„–>Æ1ÎJt°n‹œ³“ø)ð•HÉú­Ò0ê13Ãf0!š&ø”ÖWõJ¥*,ì “~hõÔ²Š‡V2?Pù½DÐG1b:rÍ}Írß™w’º<Ú ñíž7®&ô´Ü™"6UbšPºSŒa¯Î!AF‰•–¥ìk³ô¾Â&Q8io`:^ÝapM"&À±,
+nÏù¶@C–ÃCd×O –‚Iùf=¯ú–™:JüYY§|ìÈø+?Ž%í
+È*’i©B"Gëë½À¡C¨çr0&…ÙFßÄ'DàK Ã$Ú+;¸‚¨ºÅ%ωÉW‚îðÞ3 ЗU‚÷-ßoXÔE$%iö­psí#·° ©i;GE7MÞœåÁ¬e£ú1ª<Æpéó:£\H¬cœ `”Óêì„t ÄÂ7}ʲՓdAÝucЮþñ2Ù,·‚èèrÝ ó°4äU· eè½à]µ2 ÃïäeJù_WVw4D’¼SD\jšÖ¨¼VíbÝ^ƒ3”)«.åtÛUü>W}Iuõ~€Ñ!´h¹i°³¢¾ZJÖÉoŸÊÇ«h!~ÝÎDõú£jhüÁA@°.ÑÓãŦj‹­Qc¼ŠAH¾º‡¡ßÐÝêyú¹)QË߯b¼RNê2ßýž¦è0&¢J®ØNå`†y^ï>Û•(3.mYñ*Ú
+¸,Í„4åXæf¹Ã)“Â^bê_ÕíX'«+ríN"Öæ9…pÕ½ìÞ§è|²– BÊ{}R¾ß/:`JK¬ 
+>až¢Jv /O2£¦Ì¢‘ØìJˆ>g¬u¿gχ&%› ‘PI”¿ Ù.Ü4þͼˆ^Lñ˜5 •æ¦k|ÊÄžš ÷ §£ÿh¡MjÚ€R“w-£ìƧ­P<æz,qòO…¥.RS2bץȹ‚ÆDº`ˆ?»˜|3t²Ý¤´Œ9.Ö¢ œ¬g–¥ÂˆŒ>ÜmC\DjÀ8æ ÄnÊÞb¨üÕ9|¡Ç^[ÕÍ9à±Y|t…ýÕèßæ+ Ô1¡ b1D›ñ£ø^óßhS Ä(›k~‹KOZÜÌ«ÑÇröG[N÷ôýt,ç©Ûy2¾ãÈøVƒ4ñZdé+;R‘[B~Ö\áÿh¹&Ë2A¡[+;ôBù˜ÚX_Ø2a§ÚF~0²–dä9lyfv ²˜Ê"7q»w%ï-óÑ£ìŒ7-Š%‰bô÷é”Á÷ÈFq†ÐTð .Å¥£_c¹ ‹º±…f6&Ïè;Ý”—;—š²lrRœç°Áà™ÔMq¿¶l r–aqÅ#HvˆÙ„!lÔÃh@RÆS¾å½¸Åñ&gŠã=(1–bú™ÉVÖ:„嘖Ü24¹ 5u§ªÀ”ë9gÌmýþ„þ{Z2Ö&µ†âá’¬˜eü?õ¶î@(¡ Ø‹PSb]œŸÄ®¥ì Ða(Þ·×=:ºÏï ‚¢Õšì¼hCR´Ñ…ž—ybØç2­U&ËɈ%;Ú!—$_ ÖǼ
+:Gšw[| x¢¯ÒŠ­¿Ý{­I÷MébWi%#´áòlhÎhIZ Â<6×JJn›Rü¶«×¦ÑôœVd ‹[K"Ì­ùj ¯%©èa>V*:ò¨Î
+PBó+»Ý…±¡ì!—ܵŒ> íå¥&ÿí “s÷“‰çó?´›ÖEˆ\3·¶¨Œê©¸¡»‰B×2¼°êÍ¥d·Âä´ @Ûf‡’äZÓ¿Oƒ6x¾îIÐ)lgë2‚ᥙDVêØ5C:IŽÒÁl@Þ‘kùXÙß a¹ƒÍ3‹9Ãò§Êø¦ a`ÑR6›:…ˆ‡ dÂJ ˆb¹Ëjb‰dP\Ô‘Óæ9~Yì*™i=…@è „
+ô3b\ÆÈ=`Ç®Ni‘-.æ‹— ²êÒÆÁÛˆ
+^“ï³'ïëªÇä”á"`½5¯"<&H‘k¿ÉÛ: Ö‡rŸÏ95Íù5§æÛujâsrN£@ŠÙøz$­Hùr©·ûä±ÃiH-œ¹ûÇê¼9Þ*o=²%ŒãUGÐ÷'Þ3øâög!3æ>кqÚí’?.@Ò6òXäåo’ñãpn[†ŒŠt‡‡ãyB¤XHßvácÞTOÄwÖ±
+‰ê¹——cÑ“ƒñpѤ½$Ì)>Ýþ¯¤½ ßË.×$3çä"]rÒñéWºƒ'*üDHy™ ”r…°½¸zŒéTöHU&5´oÈ}:Úã]rÏX`’®}i^¥Œ—¶¾ž§N÷DÜ {æÇÅï –b~„¡õ5Nb9ŸCx)ýH9m! ZÓ|¢Í¼·bRˆà÷ÖmqÓkI!Ó×Uj@§E¨Û{8‘}µ0_Ö5 .ÞiHÏ
+-IÑÆ£) ‘£·Ó¬z¦–P5
+_VQ7ü$¾Àç ‚½’æ}<z@˜f»Üü„à¡©eàÞ’è]VÁïy@¬)¥2Hr«±îaJ1YF¦­sB§©ŸƒuÓ"1ö`ï•_O-ÿ½}ƒ§éþ`é£Ât{¡æ¥Z#Ãêôg‹måŠììOµwBÈ9V_eŸˆÌæÔùPb*g_äÁ`ÜF—Úk&Äž?,³šŒ2C+Á#X©hIÌ‘2móР足Üð[T™6­=†Ï0pa±j&¤ÜIÃVXçs´±Â7<s"PQ^“PŸ9œ)Ož™¦K÷
+ ­ÁRtŒ—ïY
+Àæ×
+®uí0\ éd:·[“F‡§ Qݼ_ËŽT°ÝîhÇh´†)¸8‡¶€«EBηýkNmqˆÊ®‚YluùX©Ãú%7bC>z%‡’y›bãìl*pbm]ÅÒ µ²ß}<CíR_þŽ|{‘"XÞt#t§ù /ÙÂ:³£/ÂÁ¿á”~½ùÛ?ùÌÀ„Aûd¢´Ûoéö.NYï…¼ǬŠ€y™§¦ˆ±1ŽŸVœN§ÿÄ¿„QOiØ(º6j¢#ìJR–¸<!0P–Ÿ¦%ˆnXat†"x{ùñý•øVâå<¿ÄSûñŒü_@½9BóÄ₹ýþíe\S=p²ƈ”»†(Å…Ûˆ¨D™J/þŸŽ.ô׸q÷já÷{0AÁíÍ“äß<¬†ö ‚O0W=ÇU'¥>Ì«ÉœŸ¾iŠü·çbÿ ÄÈ(C¢˜~ŒŠÕÓ}MîU!æÏë2Z_|Í
+ÚØà‰`”€¤`u š:Ê€²^¦=F2Õ 1X°qò.óéÂT‰ÙIÌáÅIŒ¡8¶WCøIk Æÿf¹Æ
+r‰PqF—6ű_A(ˆuGKÆs Q—mŸ@ÀˆM¼\ÜÜDYѤp
+µÎé„ØËñ—dýž¿Û9—6¨¹$H•`Ô궓ïðó
+yÚÅ×!S4ÞA žü=™Äh yK±ŸÌUwT… s ûÔŽå"²·^h†mþ¹Cr v(ÜSœkz.,ÚÜc~:g5 )Q¹c¶¤u˜¥yàÎÀä5%KWÕ£Êð=3|ðŽ-moG~±ÝÁ
+Yö½ÕX«¸HŽ¥óvLS2¦
+^û¾¬d V@Ò{‚`Õˆëk<îá XŠý8µ ŽAr.Ë)¼b4àz/™e¥ÀÏ%Zl“˜&o’>ßhÚjír@!¿¤ÑX
+í@v;b•o > Ò¯œ˜›õXãÝ Ææ…Ê7*nAŒ6Ì’²u
+Šæ]ŒíœûsÖ^AÕvMš‘%Œfà‹e‡p l3Å( ½Ž˜Là °Jî‡Ó*‰œL_†ÍKÔÄr&ºî‡®f(:!
+ Ç&m3mŸàÓ–r0Ü â(fDA4Ðq³·EP]6 Û¾n0âxš^‡±¸¸æ¯Ë2ah€h¥ism{jæ£WÝ“yhÿÆs‰La¤ópª-=/ÊÝ­,òŠhå>O6EÊIÃ\j¿³ßßXô×û8¼2£¯¿ü°tˆ',F!)ݵŠ•
+ê‘ãr7„ÐD1`ã@è (1T PÛMˆêTÓ˜=L ’P²lÄ}¸ÀµÁåÙž³FXõa/?t½¦1­awBì*nf:å Ήô¢‰wfŇFÄ×”"Í×Ò™,É.À"ñ½ jêÊDCÚ¬ñQ%„±ÀL7 ÄBü6C«û]Èl9´¿ÉÞ©†„c%ùO¿„ØÓ/%ôι4‚lðo¼¼ˆ©Ý†²h˜h\z+¦sÕ<Ìb)Ž] •Ì`ñxGó#&$“@75Ìßý„ãßGb´‰£¡ï<š¼í4í ÞÆT!Þ‹¡£Ð! g´"ع º}[5
+Ø"»UŒö€w¶!¨fyS?6äï„Ž’–i›¡·‚£_ ÚEçÅðS ‚Gö oï‰Ö#w–?_c«Ÿ dj) 2Öó
+j#´nñ2*RJ>÷Þzq 9<§uš$P=‚ÞÈè$×"ÏJ'i/h¬ÀØÑØ»T¢­c°Æ¸õÖ@çA‡¯‡6‘‘+²D¦ùpÐÞåw!>üÁè~
+=E ‰IÑ?P0¾˜¡9rV±6 ÜóÀÇ!R1ˆÎ&þ×à'¹¯ýW§g³ýQbF)ÛÞJ21´«éKéüRu¸-í›hó±QqqÞºA­šoÇ\&ïóñ´)f‚v¦}küòâÅPÍäÛ{†o!£u¡$•°(…Ï£ÂêŽ z&-Èi‡
+J5|2'R‚#B6p„R¡5,
+y€©ýëÜÌ—ŸìªW (ÍÛ•™o¸Ãè"õĉy>@0sX"ŽN!Ÿ£î t”JðÈ’?üÛ4¤šZ 6&?¼Js£iz9õûT0x<ع߄}«pQ l¥A ±v·Sˆté¸÷ËÜx·¹ÍŸ÷¨Û_‹sß– Ï>
+=ðÀ s[qï0,~q#ãŠüJ2¹(Z4,𫼸}ñü‚nGËT;<^
+ƒiËWöù„!ùŽT¡n„Øî„EÒpSËaÖCFŒšÛ Nì FMÀ ¯ÕÉË/&~¤\’¤Oy ÆHò×F%6×ÂoÐJn°§#‘qq
+%î°Aü°fŒe¤•PǶÝ=¦®%9~bTéÑ$©ì$¥v vRźftbË‘÷™Òèžo4{vç/¹'¥<» ·€ü›œ•AbŸ²HĶ9 Œìe-Jjm÷Ã:]!ÎjÏ’úwÔ˜´>ð°ÐDH=M³‹#ß•cJÍ΂)Õºž¢AX&´…D{ºG©ì¬œêŒ‘pûMû¿A,»r.|K1­*cT^[N?ñKØqÆWb9¹M0‹‹1l•¶á¤¹å®c1Øl¸¼ãá°XØTJÇ­:´ÿC{êã¸ó°ª8–ßžò¾‚¿Z.ÛÁˆ¡™f5>pG¤‹SÄÆt>qÇ sÏlÎaÐ߸Ѿ÷ K²Ø¡¬½ôÓ $, `8‘ôš
+J @w‹— æÖ”{¯«088ò[õãB`¬1â±*­ùyJÉ ªò 㴌¤Y
+î;1Žåw1^^p@—¼
+ø90BEù7ü1ÃtïùÌÎÄÝ´þ5o‡€~¹¡k‘?þ)(Ô¤-ˆR¥~ÆÅ€«™™mî J‘Ou^v
+²‚íï5%¦Ñ³™Tª`­á[v¢"ÿ‰‘Äw?ú+÷>økog—Ca³CÑè§ý¸ÆçÏÒ\>ÂRˆ6±ÌêŘu”ÖI?â9>}5EŠ%[~r¤ v–Ýžcá4zœ£¤-^eÊRÓ)³IÝßÁÔ)Á<ÂB!"BÓ¨is!|vRaè‘‚¥=ehñî2ÈÆóweg2[™9ÉÞ•ipJ2.°D©îÓ;~z2œý<ýRÂA áÇ
+ɇ Ñb-, ¯UF³#ÿŸ–83d´çÂIX÷ïDýÿ¤ ·¢\‰ƒæô¥”£î£ úl8"lµÌË4¬£êâ¹Å¥›Å)5„T÷wè""R2Í·#¾mD£AØ=ƒØ³Óúïß|ЧÖÿpAº ¼BvX†x_Ýá|É8Ä+3ÏÒáòíé2ÙÐ þL˜Ü¿LL=ælƒZ¦@3#ÖÅør)p“\nD¥•Öåðû5‚`ØÒ‚xøßa¸Ø@jÂp¿/“(:¿Ñ$1lƒ‹9D)¯@ËbÝGÑc(=P”šÍ³°j™Ñ§qì} çÙ%+ßz6‡Šåó"¿]å\qC礯×S›üÇ¿XæüçÛÿý)Ñ‹LE«¹f¿±1¢¸ãZVBpA¸zÇfÃ\ Ù†ÉrÆõAÔ螥½Ã M>iT}C¢0Bþ®¶MÔFƒS°ï0?0û„,š}‡Zc—
+ƾ„MŒ' 37-ç6ô¦DbGU5 0+Ë—¹ß”SP[îO·i®¬ñæ—‡Wipxsk‚[x“‚¬§þTãäS¯ì¨HC…žXèøáÝË!›%EÆÜW! a88µ¾ ø¹:U¨©¨–u©1~Zж–µeN¯.;êÒsï;—Éñns™@ÿU—IöªsÛ‡{¿Þ«ïÇ#:¯éA´Åñ‚fu¾($",m¯à Ä5‰£I³®# P¡Ö1šM"EYvª*Ò‚×Â;ÒŽzV’Qž
+aÚ^$e˜]Ä> Ô`uÎb( ÂÙJx9¯4§aUÜM“ñ6¤È%!œæ@ì(Ì -¥Ôóñ;X"yÆQã¹MÁ(H SYLJD‰G
+-ÛÃK@r“$¬Jè ߥAúTí¸Æû:ôˆ‚†Zíd4zÂ÷u´øM«ˆ°#Q–ƒu÷刽üRAï;—>p6S‘¯G·Ÿí¤œäƒí‘œ¹H/GŠ×ªíŒÎu#žJ^ ÙäuNÖßG}9Êo')¨"×ÄÝš7”é¢fÿ”ç™ú(Ån†S‚¦“\£ZžBæ²÷²!U®ŸK@Š²|53_d§ìæøjæ‰iöeäæ ¥Z?ø.‰8}Õ€Q
+@Ëù!±#ƒ×Å+’AðÄëymª¨­†íEx!‡¤¶ ¦; ?Ÿ@3Ä0óø=«``‚±Æ|.”åìÑÊz!5ˆ4ö&™ ÏÇsRb#\³AÍùÊî* ¦Nì0Ç8%Žr¡Aeû»üC}jÔ× ÿþ9‚“Òö:«a“Ѹ&2›gˆîþ¬n È„Vã„°˜¸â{ÃÜHЋ
+–õÉÒ†ñ;fZ±âÜrØY,¹¾Ö„ÎA¦ÛÛù ¢T ­ÌÍCdÉ? -\º·5FõiMj†xýð1×Ôm#gÄ~†š‹ T´)m²¤XÚfˆµKjÙšqÖg ý ÜGØx½oý,Þ,uI”§6~Ëï‚Äti(9>Ÿí¼
+“Ž‡H1æ~ô&ÅÄ6Ê6ö¶!3‘ jhÕOs׎mç(¸Š_h‘›¥¡—i²òA‹ÁÖœ#}Ú
+‡ˆyi8âˆA˜„Ä|Ìl»©ú%¶
+ÚKî`.Nħ‹ß±¿CÓ%–ÝcqaÏ)Å@+¯ü0~S€ÁPïfð|½¬MŠsVGŸRëÚ;_8ß>#BÁEÊý¼Ê…F¬PYQ„4¶»8!æ2ÓX·h2+\5Oßatà‰NûÒ1ðQ~SóžŒ /‡Nž c~¢|Ix3\Eä‘1”sø.?È‹##(Í üNÎ,m†»Ï %Î2ì––ñLFPf”A´£°|FÖe¼©}÷ WÃ,D¨0YõɵßíÃ×$º Ø¿¤|É’®žÇµ6°$2¹?]PzÅ:’ˆpÏé@é1h³â<þPÕš &*äÝŽû ÐçÐõ?ÆËÉ’ã¢'˜;Œ ¡,÷E¤ E¨<Z·Èûóy.ø•QœÀôxgeFx¸{|=`ÞÖÍ ©0Œ±ƒÑ«ñ>Hùœ²Í„àûÓ*» Lrȹ¾lð™NФ—TôUN`hó’|œ$ªl.›@"$gû}ßcg†ªðJsõ馢ÐíÜÌ©8q»ï/³[·P½Š»æØKXˆr’«%Ïq½œÊ–º·¸8ë'YFBÈã™ÙUÖÍæÙö‚vóœÊH† ò©õ)Â9OÛØô­zÁ¤*¥Õ6#tŒd}Š]7KöK^ÓN¦ÒQLÆ´Ñzå1¸ÚÔaÞĬÐêâïýõÃÂXd¯“‚¤Ø¶Ýø#Ð9€Æç>
+æd|HLA:\ŽñùfÁ9„§Îµö~eN¶šÆ•{yz‘‚²#…Þ·+%™»”ù¥¶M Si|ZAù¢@Ì!)íO ÀŠêÅu’ÙOû6Á3@öþéU¢`"çëǯWÝkÃW¸­'+oU˜M€î3™iJ Ñ©7È¢ÄÑñû9kî·1Øw¾ê$ñ½8Æ$h‹D~;ªS oõkåÅOï†l¯Ž¢šˆ ¤Ò
+Ž®ðòð¤xu˜Éñ©Z Á …x0*úê– ³Bî‚ðw½b¶Ó—â¥$b)µÓ
+qŠŒÞâz—­H"‰ l„/­0r9¯"Sþ%@3v í¤Ô]ŸŒ&‰¯óë]E&õX=à{ƒТ>Ñ‚¦“ñ±ËœwKïçè• í ]¹½ÆÐ΀žW¸=!<É#R¨þ‚f‡Üæ¿Ø4h^Õü7×9”ÉgLć4à«Šî¹LXÆ…Ðœ¾:•4¨˜ çOb «çaÔ×ÓBô1ˆÏeHÁà°#­ ˆwRGn½¦˜¿ƒ@Æ×µ]ãSÈ9ÚuöãÒ)ÅžK~ê×0a4…YèVS$4í¶Ý©;ŸàJ‹üÓ'Dc.ñ‡a«Î®é†DÁUÃc•Çqg¥ï\¢?Y*ÿ;ù*_z•b½p:[g}/Ï°Â+™Û*/P$„øì|´T^B$Ñ~ðšçw)dX,é¦í9¼K"±àÒ7:jÛ«þ.&Ï6dȳ»Îbúv¥;=j¨Åg`ìc:Ï“ 3m˳ 8;ƒÚœK LYûUŒ¶Ì;¾]<‹,Úf{“y ÜŽpHÛ÷x2€N’é>•-ÔURî dN™7@7™ˆH'´–újé¼ %gòAæ‹­ó匣¦ÃÒùÑ 2¦çr¥5ï‚ñ¼<MÈú”÷ŽU—lnë¸ÃÁžþ—{½ëü€;銽Ýë¦óƒƒ5°Oº-âï:/H-šùÒ^.ø®ó’"{KA³v
+$™´+Ìy°†X—㺚S+‰iZN½S%€ @iÿ‹Â¶ÙÊd±æF‰ó cOô¸ï‚ŽR ~¾ Š‰×‚“Æåü iŽ’Û÷§<¡‰c9ó«4ô(H,ää‚ò¢“umxÔ¬C
+'ý´ç‰¹©N\ãÂóK¿›Ìµ!F(2¡³¹í
+ÓyFýéLjWw×Ćæð„Ž­Ž‡{8ADËl;]4ÊØgÇž3?åµ¾ ÅQ£±ZŽçEÙ 9cžCv—, D ŠÂÛ¥›Áê€Àw—ZGþší4@¸¯ a#(p4í¨ëƾ ›Ùíì]ÌDÒšµ‚Í—G¤-ë{͘dDÑê®±‹ø;ýz¥zK¢ää4+Œ|"x¨YO¶}Í´l(Œm‹äK*x8GóB7¿·$ÏòC\hëbqB—\F‚ëÚI /J,v/EÉ]»qµí¬Ó=þê¾æî}^†pÈ«ˆYei×mê4ðìIJ}æm|o 5›˜]@ñÚ´ÑX¿ëUø*Ñó+Vá´ŒUY¹ŒÛÑ‹^Œvï7I+ëÆ^£šÓÊà~üi änžq*Õ¤Ìë-Ð"f¯¨´S©R
+*(mèi‡Ž–p^Ô÷«¹YSÌŽ{Ñ«Néœyð{ê’RuªÊ#O^÷ÿ=óÖúÇ¿ˆÈÿøÏ?þûƒ·Tå1Tt¨{vW õB©û÷ñ–1¼TÇ6úFÁH}8wűµ ç@z´ô'P½¯ˆ8âÅ:)SœEVÌD}­;ÁbIèÿ/“ô8n%_…'à‡yXËK¥yÿ­ÿ@&JV5ÊâÛøIŠ.
+¥#Ã*oÚЕÉô5k‰‹’Š4ÄÀ(Y…!í;|å j2ˆ”†VdeFƒˆ,!ÝÑ—üÝ!~´:dYg{ü«°t5-c$HìeHl§G$=TQ¡}áÅbÉeSƒ}9^þ‰‰Ö 6D¡Edl üB©
+@#['¸t'¤ ÚÊ wЂ@?lÐÄ;„WÉ 7îî%dÉi^¤D¶N$Æ¥¼ëƒMꔄ)ó–ŠQ‹Ü &̾ӵX„‡¾[*{À£ŒšC$]ìfÉ †¢ÀI»>„ ´Õ/¤uªêO>©Êغeågž—E’ ŸVñßA¯Ÿ¬Øò»:°²«Q_šv©®È‘Û‘]ù6Œv© –ñ
+•$ãwnD¾èì?3¸nDãŠSü8ïŠC€Xø«šá!þ•ø0u„ØÆ®·YGLi×¹Óq´ißépc‰:÷§ný×¥6aHÃð=Nð•C혬*#5÷€à‰UoEÊÈ»B™Œ‘?«Ëê#9¢èa~eQÞn,F¤ÊÒ9Ë'ñ»D½‡_GO¥jh²)Œ˜Ž˜ÃB°Ñ{kQ¶*‰éó‚=ã,nH[l>  B€äcµÖßAÇaCeL¬·UN6
+äM8“#+Á‡Ln ^©ú~ø”­gŠ!,6+ÈwðÝä;ˆÚFåæÄ×äK#b…•?6þÙ3&s×xÙÄ3¼P [Pœ>¡-x‹}âÙ{\ʽ2Aîš)œÚQ*¢œšÉÇ ”*S¢yŽ²ž‚0Ø03%Í>ñÒT ‚;’Ñ,¼f0r «~8òŒeÉ{Ýß@6&àllÝïÄñS¬&ã…—Jmƈ=EmA1½Îu´I'm”ÑDÌ3F2wÊûL¦'ø^Ét…VØÚíÒ´qEjèÁ–uyY‚”}ƒªÁÍÅ7+s.£ŒeêÅ ²iÜŽ»¤ÄO™g<M2ÇpúΔ¥bÏÊ‘EƒéIZÕ©°ÈÑ ·Ó!ú@•c`¿ö`0Õ’öQú~Ï$œ¥zÍ ÉŒ1 2yiò”K8–rš@XÕ”ÚPœk,h ö!\åCÈŠ‚JT6Á³˜p(®Þ(d?$yãDð
+ü•þ2;Á· Õë¨ÑÃE¹Èk²—ß 6–ag~r[uŽ‹wpLJ÷ l²ø»6$¨-²—Í ª1üÀã«K4œb[;*ú‚jZ³ï¤Vjp¢äA˜$8­µSyNjSqÑ‚î¦&Z¶s¸c€'cuý¾,=3 ‘&³AÁ”ï¬H´LpÁŒ@ïÔ Oçlx¢¨ïcæ}P^C'ò»P$ž5“AT£äÛ¬¿ï²ÐÁ,-GW"'-©íà ü D¤dŸâŠÎ^¤É-øg0¸W-ôÖ
+ Î OžG.ÐÒ?ê Df%¨2¿±r„j—üQt mQŽ½zü‘1Bs“Ñ:R@8£Õ1•}TãW?´gs?›€¤¹Lû¾/Éû¹!zWÖ5i_ +5•(Ê$­’"FOßi•5ç©D4¿ £…¿ӧ“‘f4çT|´WÑ–XÈ”ely…aIè"^ŽˆíT^JÓíI€Û`ØÖU:#R¨g×ÄRõË7T£%@¯P‘rã”+ûîVeC&4x;™žI‚HÁŽQ!P!Û—B™xÊŒ«ÆYQ_Źeãt“ íõf\™b6zœå[;oŸäEl±Äyþ“ÓDbX¼í–"s†±d%Œlî<¡Wa
+çâ‘X0’èì®.+»tâZvHY4¬0âµx5ënèlŒ"@(øü8Ý@¯ÙbfspRgVÉ
+ ÎVXDvœéÔøbXæ"D€`{úþŽÂ?öe)íbž£O¡"-Oäÿî7%-.KËçöQ°=ªÏÖ5§„TK˳ñ ö¶ Ra4{;0¾A ˆ56›œ°Ê±ÐªîmA4¤œH Ù¶ýÎ?&¹È2ŽÔl0þe¼L²ë¸a(º‚ìA+ø‡}3v†ÙE¦òþ§¹¿¥*TìAr *’ÀëàASÖ ªØx>Lêp^Š~ÛÀtQ;KÍ$Sê3pŸcœ(+U`€é-™ÚBÑ
+H¨ErcüK4¬ðȤÑ&þ;ÑΆYIJKh+¬MX‡•ðÆÁrÆ÷í¤ˆZžùè(Ñ,M ö
+'û:’N4¨§`Üì0£kM‹ù¸ìÚÛÀû»Çà5µ_c AŽÅ…Ç«`L_{òƒåmœ—;* |dÙjÐá@² Wgã êð (qç»VèU
+™QY½þ” Š+Ñ:
+I~ ŒËŠøî› ×I$Ÿ’ÉçTx+ºµ•Ðþrà?FÄ•¢É:£E¯†©8ó~Ò+|âð&9XÛ%(Fˆ æ[g†nND›”ôÐî¢ô†â“Çé¯$’õŠé “Èe°M¦ʆzàEÏ<¦Æ#IƒpÔnlHúwoÚvM÷—k_ñÍ;9 LÉ„ ¢'DöÝýÂïˆ?,¡v›Î¯8‰¤UŸJ` 0Š¸äÄ­]
+ùÓP ÙßDð÷ïàõ5Ü[ìd‹û¨Üï|Ÿ·s›+nÜW]ñwoÎÄN‡œ›Ô¿˜ WØäjø ÎÖ—W¹~ï§W¤$‚Ô
+H¡A<·E‚Lwjù}s-HüƒÞîÌMî@øpPØeãÐ^¨ŒÛ½ À›ü}IX KD6æTËÃw€3†‡Ç°ƒÑý6¬ ÀF‚ %><Š½!ÖIxNá,Yh%ñÇaƒÜŽ2)³Ÿ}®t‘@faÛ%'„9ä¿gRŠ”Mšlox+ú6ùχ"òbw­‘Ù‹˜Õº¢ !fGê`äsØË#Î ”D9)Ę̈ï&WÜmF²É iŠhH”5òMitÉ"ŽV^ÍîwRÖÂrxߦ°%öh\ÚvµàfKrünò®þì£À: J í‰+¥V…c“¡û´Ú–“rÕ:¹$ÛdûðywM¹¿'ç?¿L²¼Xº²vŽúd’NÑÕ$Ë«åJ ¹n-RŽIv b7/¬Çsî&YH b:oŠÝ5ÉúÒÞ×ä(^ªYÌ»))<xde†$9ͧï© «ÊÜ¢â˜d…¢€bm´gW“tjî&y-r$œë üßäZ®Ô—ö™P;w;ááÂ6;[=']m©¾@7÷heÝ1·k]“¼ŒÊùÌuÜÎe®°qu…ß½7W ; v`~÷Èßæ‹G¥WÆæQó“G^‹,ô„à‚ôÆ÷È‚†Â"l)¶ù`'ôb£¢kÃw¥²”Ëawo» ÞóÈú‚5Œš±ÕôôQé$ܶÌr¿Mã ‡‰#Ô‡75Ô¡`{gß"Û+ÁÖ_žäxµHJä$´–ΠnéÝ-Ò)ºZ$WcEY6Cß"y8.
+X:²3mXI›AÂÒ‹E´ÇâY8"÷AV´_Ì3 ø›ˆGxh04hB±a ×[|Ûß×; ‡åÎòƧï”&(c('âÓˆ‘úluÿ^Ï è´%•$w¤–÷1I§ x°ß·P<+ñ&µTìEø g’±ÑÊè#‚/Š26*®EîÀa~Èì.aÎ?-* ƒÒvÑíN—¢Ï?a¨™Y â1’í¶èiP
+ŠƒŸü´¢´ó9<5qAz´©ò±Jb
+
+§SaÈÉz!Οvrò>Ãã!Ü0W4Ù%,^)¯q¿ ±@’y;—ÜGvÚ
+µmD•’ïˆ5ªû"(TÉ;–‘©/è÷Wî4âùí» ì]o*4}Šp½÷"E–m`}\qhjx~Ö
+w›ù'1¯ÌÝÕîü16é^¼Â„ÈkœH›M ½$Z[$aäË%Xbû}‘ÿÂ<ø˜ÐFýWÅG"‘K¶µ8¤0"+çˆb'dd|WY EŸ 
+H‰”—On/7„Oà;øiHõ‡kgùn`Vöý·ùØ¢üž»ÕŽg€ §~-‰,VM¬×TKNC^sÏå°dÚ{i’úë_¥öcdÉuädVŠCôHÒS³>ŠÈ„Ôοi½}}{ÙÚ‘Z)œ–†y}96r«ü_ϪǑUGk­ÔyŸžGMZ³¦:!=§ZEµ›¥dk=÷äŸy”ÔÚjk9•§Ï$+£J6= –ùÏê˜9D³n©©T ˆií¥Ëèú' âwwÕx9gö‘¨cñQ3Ÿ®Ü¸LHQéÒ*Ë/I©…·þyÒdÔ²Râ2T_½üï%½þ=¡¥qQ)Ü\ã{#ñøaœ«¯ ‘\ü/ÅJÜ\•{h¥àQëÑähÉr™–«µÔÅ4JÝ
+7””GËm]ÚK)C *¦^j£´9ú¡’óÐl]'Dl<ÎJ´Ì/ÌjjD4¾éàjÜ°ÙÓgF³TÅ… “–Ò¨[Êó&J“5')4~OyþЭ$pL—ÑŽT³À×$VŠGÉÒäì”
+L¢ŠÌMïj߀¼„¥¨Zž“j]h<-ªŸC84A·óK|a”~Î| !3ʬÂâ8¬'c˜œŽy׊ "Z‘¨±r~ª¶ÿ ”NÞʳâí×±L6ñ0ËY&få1FW¤bñ‹ÁͤÎw÷Î 3~Mm¼î¸Þ¥à.a>:ûiðâѨs×1ß•O`ž˜)ÊDŒ¹6Ãú±õC¥\«¢ž'Y ´Îxw¡¾”¶TeÄ&„Ÿô¤´#>Ò)Ä 7O"ßV[*_¡»ù¸OJ”yÔâ4oQ »xEbnzÎ鶜åÉMÃS“‡ïd¤tÄYeo9ù`Òx/ãXê“åÜ@;Ëá¬!Œ[‡…²·¿±À…áÞ{Åíå;˹Up÷/]¸;ε•;ù1ân8ò] gƒ¸Îqø·á$;Ð¥Zhz¯ýÁpv ‹á
+ôá!Ú\ç$+ŸPX—ËÖwúÎn(®¾óŸÓõÛwO!
+óú0å”=ÁðGü}Ö†z•Ê›éª9ÜdRjiœªœP’SË„âpñÿØp4÷×=bLi1¬ŒƒnzàÁžñëó+ƒ±Ã6-‹6•šPG4J% ‚$oÔj÷Ÿ¾'ÁHèÇr[´¸.‘M{û5/îbÄ|ß“øí·ñŒóVÄP¼kÉÆŸ|‡[t µ5ÙÊCz…?‡&8Ky!ƒWtA²K¸ùFéßÑõf/»¥ÄÙ³Þc¯tÉGS9Km'b‹>´—ðÁ܇>+rB}*uÄÂé{+¢¸”t[’š‘|Â?./ºb4dp r˜‘%„ÿiÐW4‰Ñ_BhÎ%Íì{-cË`aFÖ9W ¡Pï[Öˆ¯ð4~WÝN7‰ä„PiØ#p'´’ :ðÂñÀÂGšºe®†’cÚGšú 13½†
+Üà?».=‰Ó°EòPÛ@žTé è×(¸Êp¼téb/"j#Tš-tû#"æ¾¥@Ç?¤µº
+V] ‹Sˆ#\kq¸"1˜:Ûæ
+ªôŠ Oqz¸íØA‹\Í‹Ä6ø?!(¢¯«Â„l qTnÆ\f*?}ç¢~œ©Urᢹù¶êš¾žôgÈÿœ–/òE¶Îç’ä.£À@, u¥ LD±Õ¯4 x'J1?6;IAp9Ÿ³;æýsíåëÕçÇðDÖ¢¼Ï#\Z]`€*ÌJ°ÿszýþK[ûMe˜•âá''<{•È§žbÐ-l[÷µ¹DØ¥þô…é|\ž£tˆN)!~¸~#w#\û´|O'›Ôí4ö©Cä‹î@S¯™|2%Å›ê7²{csˆ'á/²Ò×—º¸Žiÿ’»ä¢dŒ¹<­-¬ðp üë c¾½ì@Þ²YŒÉH$ 7'3ÐuW•Yecö`VfCÒ°öþ@ÁŠ‚‰K¬µ²üNÖ(<¯ëºÎ ôÅ\ßâÀ„ÓÔCˆ³$oäûc r[@}Q÷a9!ˆ[¢g°å&,5!Õ6UÏu‚9×¢’®`>ð0{vVi×ÛƒôŠ4TŸa*ÌPžß)î¯(¯–å}KâºluB|i^)He°DíihÿŠæ +iá
+i8³'2ĬOˆ$*5ЉZ6±òìÚ tÙÞ@ì2J=¼÷!¤«3¿…I‘}‚јթ/L?[ÊKáº?‹¨PÖÓ}×ôìÜj ™;2uõ‰¢ÊÞ”?Gþj·*ïìäÖ­Ýw.=ßÝæÂÝ«n¼WgC÷k7/½úç'cóëw.¨2â%ƒ
+J¢‰òtkRhFl”Öfj°²+©acê„8­\©”…„Åœ¸tobôäˎ鶩Š.F¿øíÍ`äº)h€c2£7×6$Ó$æh£¹B»Š6J¼ž'†«s„¡×0%¤„õ9fˆs„`X`I»)~bÛGËlñ¡Qé¤nWy)V”O‰Ö8,3:éMíâñ~ôºªÏŠÓn¤X®>cÞ.0ÇZž:¨¨#¼L]ŠAä*‘Žž•›ÏÕŠ •µÜžOVNص+ š>z‚4‡eˆX^ æ
+2SÛÀ‘3Mw³‘\»ç´t%³šG±
+Í—·¨þË&‹Ba0F¬ŠòºÓ'ÈR>0!öç@wä*î Bòh«ä¼U¼Ý ˆ¡cXe–†“ ¡þÖUt>«šR,OY 覓zõóœÂpÚ–ÉÎièºK n!tãÃzõWYdq
+7äQf&ù}Úè¬ò·1Nä¥]çÜÃ{)ðÉ;c› 7be+·«è>‚À˜?œÝÈ“`øYÔ˜Ì~±ÅH.k•íkÁ¹±šxëê ÀÚˆ.f£ïn{{¦¾jƒo~Å„ýüânU¤±ïjØÉ3é[yM4¾Õ¢æ›ÀûW7¾~û÷Å¿~ÏðwÖ86 £S£¸åŸ×ôúãTèsm¿^†Ã‰^SßÈ
+Ê8‚Q¤óñåîý‰w®çTÖ`žß³/Ÿü ô{ž÷/ûçÕ;½ãõ·Ÿ'ÅÁŽU²I÷”9)ní/É1Ü
+ÆÂÓx€ÜˆŸ +D;—±½y0lâù·Q²@=9I`7H(4Øáh®[DÿÅ©F³@ONmn¡‘þÄϲ´}ÕÁ=ÄOJÅ_Š¿°2ùeºÇ¯ÂÊÄ›}ŠŸüPìÑÉ×ñC·Ü•Ú=
+¦'}þ<—Oúøò¯ÿî÷—oþ¶x¶Sÿ|ùT~ì Å!_¢ÉoÙ x¤pG©‹Þ· ”¥Gq£¢Q"qD-Œ¼ê}xΧAÍ—DL(ëÒètÝ×Aº—
+?ÂîìÀ‘Ĉ>‰ÅcM¦$‘ƒê ‚ª
+’èÙ ØK¾‘eBèx\
+ÜΚ«µI?f-jm!Ž+ÿ•2\† ôJ˘2©I¶3 ñÄ1H±4!¤¾iä³ ì bWÑLlH1{qN‡áµ[(¡“˜ ”}±¨Kq|ð‡]½8G­Y­¼±­,7,Ö¬ƒ5@ðw@7Hg\(´.Í ˜’Sì’½˜K¡`xÁ¥3ÂOh"v*cQ/Naõ9m@öÝ
+i’f§n²„fá#µ»Ðäâ&Ãm t¹‚.b»ßtÄP"ÈuYc|Ý?&IEÖ$2AÑ„ä Üz¾çï˜
+ 9@PÍÙ9§CÉh)Ê{Ïß©TëŠSÉçlçøšMž£:÷ñ19›aÈ"p…“Ü{b¨>Üg/ŒÀàšo¿¯ÞñÈ„š\õÐÇhËÇ뎠·gÞd´fakDi¦¸;ë}‚°Uô ÆeP]ƒ¹á½óBwc7—<jl=½Æ®ÎFŽ•X̨8þ?;£ƒ¾ânÌ]«ÒÝ
+Û22Ȩ­–ˆBd7å¶?‡hU¥PÙÎÉŽu#ˆ'„=Æ{£”¯Q½Ó$TÖNë*œ`ŠKç.ÎÁ+eí~$áJáñ58%Ž”L³©wQá”RçîŽ<-ûsâMŠ¸wçb³À«Ë±¡P£Ý ¬˜ûiÌaÇ¥!VsCDÛ„GжÃÅ]™jÄH~Äu ©†&2a¸Ï‹;]–KšB-¡3b0€ŒN Q†mJR¹*ßÏȇ\‡4“šÉ2˜ÙÓ(×8Ÿ T eCбÌ_Ý}@ š›º…ØUŽ’£IÜŸãaD46"ùûkÙ%>LHë¼ WmZT•—’áœVT€àýL®Ú÷.½D9 !ÂçH–t¤®–àÀ/¶hÁ±ð>ži§0™‘?¤Lšg_AÛ’{åÇ“|f9> Òu$BªeoRPd»Öÿ(/“ä:r$ˆž@wÐ h˜‡5kÙ·ÐVºÿ¶Ÿ/ýÌÈ«ÚÚ¬È
+"02¹ØË‚âÀúÿ¾62HO vî¡ÏA—Æbê1íi^>ÀűMÛ̺ç`KáyVâì1,øó±¬ÿ&Bt 0h §r¶ËRØÏoN‘×þÇÀЛHú«E.ÑÿÁ7Ñ÷&=dÿX¼îõËŠ2Ú´œùè)*ZKîÔûp‰ž’iÊ‘\*£d6Yß”ã%"· û“és˜Õ?ÑNrÉø]‚–h×@¡A%=
+_©º4¯‚"­ƒDËÍߊ\q6îµTükN_-òhÞ+ºÐ¼J äÆȈ¸.Ñ rƒÑˆñlêé5ºXƒnz´ûé5Š&»A¨,ÅÝx~“`ø`x%ö)à¯Cæô‰C¸Ç Íõu™7¢W$I;Žž>ýJ‘7¡ÏÛf݈~å XÛët‰^%Ð&¡yjD+r‡ÞW{b üï‹5Í«¦'¬XÁÑ¥yJÂd?±Ùiú4aèÊüg¼¹Wòcs“ö3Àr/Ûw?'Œ£‰g‹/4/„O¸²ôâæoEn{*—fAø [ñÕ"æÿÊß‹æ¿ÿ£Ò siË°8+!.SeÜiS½S”á-½©—½¬”4x]‚¸Ü‰ü;™“þ&Œ]ÂLð؈ƺ•ÄxT䂳¦¥>Š¨çhžwŠðÔ8Ù8kîs ¶*©»OA„À’0Ì57R+•†Úœ’õ©/hRˆý2Èî9#…>é.ήœ8Þ4ÍJ0$¤ÉÐkÞ%QøLêþüæI­{D<ªh¼ì©9Eü{œCš²b(%½7:H*­Å_é‚€ÅB#;ö°L±ƒìðy:fªçŒ
+Ÿ8Sñ sŽçS+Fb¾{'Þå€áƒmÃûº(0DB×{•¼­l
+ÛŽ¤€.øÎvó}¯E¶›‰
+lT‹å 4ÉÙá^æyz²ØxìW’D†P˜–ãY`æ[¡±ÔN—ßf~ºŒâ±ÌïØ8–zh£§Íó /èÌóî)ÙÆ©ïT×+2èÝfLõä?šÐ®"]Ø)¾‚C96ùÏœiÂreÖZϨ OÖ®’ñð<®ÂÇÌtf†Y[…6ã
+ ®”Ìx´)
+#=¦3?ž³¼(r0VÂ#;X ëU×Q\/üéÍKr àó„Æ´cÖå@+tXƒyOÖ ÙÁgâìé°Umìy©ž¬« ¯“<0¤¾9hÒ)\(BŸ]o@¼?ÑŒx 9I–éÉÊÛM’Lç&Z ù-Vª5B¥ˆuQ47ìÙ„¦ ü‚Á†§ D–ÈpÆÍ9ºF£?”¼!ôó›Säb¤“ð Oÿ¡è¾Îç¾Ptáwµ®EÛ¾s+Þ^ÑÉÚ¬,ñ– iüâ±’r¨¬W¹ª¢¯r5Y ê&L#GÉJB+UjeJ éîÉvÒm£usÚ§ D\œ7#t$·É ‘ìS&ñbG´ö…Ò
+ æ
+ªCÞb;çÖâ,Ë7£\p{Õ[‰¡â6òû97à\oã
+t-’Óø¬x pµ=´ëÇCRÃÕ6nÙí>H-2hÝD*ˆÆ:š9³=’¯Ý¶õZbMfaXX&fÿæ@zÔ?¹ØmpDŒ’—-¾Ì³¦£±[Dƒè0$
+9QtC·gÁÃIîRâp¡µ‘
+n;¬&Jï¦ó““Š±ÚˆÝ‘"qFÛ}Pd ¬,¿5c¼AœÔé{§Ç¶®ðï4ÏO ''»òÅ©€È¬ ?ú0"¤?ŽüË—OÒÇ‹¿üÁGPAÐpžæòÏ%]~ZÓœZuæòAïÐÉq¬ˆöžb^g—ƒšÒô >h"J¥ ïÉØSÍ% ;7·„Žx§Ñs1á5sÁ[Üžç²Ê“g¿p¿Ÿ¹ß¾=/˜xê`ÜxÞüO¤/Ž«yîâ.sùò™Ó¦¢
+ Ë`nJhž Q9-Õ
+É×꧂¸70 [ðëþÔ!i\;j¦Ìß[™ëP´Ô%‹§!¡pèI•Q8“9ªOeË'ÏO†ˆxtö‚-2–fè Ñ19r„üÔWóJ!¬!“Ã"š%QÈè~LAaÆCEoÕ˜V¯¥ñ‘׌«ÄÀŠê[õZ… q{¶²ÖI‚}OZ«Û¡¿‘ì,ƒ¿iìØn±×¡0R bŽ%OIéD?b>%¸–úï+W.˜šòÜ
+Ä•!`˜´Æ•õÌ¡üéÉ­uSéý×]Õ
+=ɶònË¢¼Šb{!]†ñxu–DÁ€¾¹S%:—ÌuÀ•x„кisHHRÖÀ£7
+ùùÉé…
+Õý …ñdUZßéxDID‰Õ*I®‰t 'Ht ¬«­–z‡’H†'ƒ
+kX5P‘€æÍ›$)…‡2U“ªÙ¼%A\lú IBøL)iVuí„$-ÑIJÄi€‰2ÇÞ{’”Ä1UâUu€Ô=I"¡æá$R©ä’äfx¥: ÃÙÆÄ[’D2º¬Ù\; Öã ¿‘¤¤ä4þR8IÅžæ¡þæ\2tDˆÍ…mÖ· Éä&‹¯ž™¬œp$írX‘¿ÇÈ1ÿ1ˆTÂMŠ4wyÔX ihŽY¨ÓFUE<!HÝ™4qrÿÖN‘Âé(‰ëðp H ’\ÔLIÚ!¿³(eWÉ^U·×iä-†Äs–ìM‚D"h”ml›‹‰HXEÖ±š7  m¼„Jxöw HIà»FÖºtG…†w†ñ^Ñ?Z¢?‹“Ÿ‘•[rËrJûP«>áÇQu½ÃkÌÝâGÙvq¦“É}J Z ™UKí'üˆ¨¥¨× óÙ:”1!§Ë\%·ü(#¶ê­©ì!ðÈÃo3m‹/ÁäGe xqÒÌäÇ» ´øQy*P¿|±éúȈHÊÂÝ|³ññÎÙMó{è¡“kú5Eº¸ò:EL‰\ŽJoÕæ•DãÅ< ù~2ÖŒç¹$*,¹Ÿ™A&+›ð'Ûë”NÆ>Ô÷:2~Ž™®mëêç=%ÁÁ,Ñ æVMðûöÛ[‘ †™™šã©¤÷Š´å—<¾0¬¯'"6–¹BÎÊiÔ,^-6œ#oô)¡¹ãåIv´ Ü‚Ë"}¼ìGŒ-? fƒê±_+ä;Õ ø- ­›‰N#fÖâÉŒƒÙü6SƒñDô[}µ—IZ+xµW¦LEÖ„%vi+wsZ43Ó¾“fTº4…š÷Fš†
+Ô¶ëŸ8­ƒ¨ÊÂ& Z>\—äîbªäqOa%ÆQdÆ<á#Ø"’ky}OÓ0Ï&c$ôÞ­9œè úúž*ý­C‡#¸­E£©¼žˆ2æG b¥Ë”€œtnÊr³
+W @eJj#ç*F™·'ù P2Æò=œÀúÛñŸÿ«cñAÊÿÛ–eµXoš –ÑHÜN.’±(%xÉ׳u
+=œâ`0Ù,w0zùJ³¸ïÍ¢Ü4g·æΣF‰ÆOõΘ±—T©Äbfõ:±çÚUøcWÏñæf_ÐXç‡1œÑy2LÙ[áT…ešz­8KH;ž·i¡ò4rp¤x1rKfÈËñ5mpš!#[Nà¸{Ýéa"¿¼ |“@•ŽÉä…uô\ò@Ç8òz"âhÞE}r ¼ˆ}ŒœË?œ:,¬ƒ‹¬ËEµ\åLNF!®“gÍtÀªoWv¾m—„s®€oi'mŽìLðÞzÖ.ý …J›‰íl\Å5Ó&gZ^¢HSµþØN팷¬,À†ú+¨í½"Ó8Ù.sBd{,35OdúãmW¹óžÄ¯QcoEXÔäIj·DôÓ˜˜JxØy}ø¨‹ãÒ6¨#I#q”l£‹€9¬Õ^†´`âëIP¸v£g¯k¬´Hx„GÊ‚ÀÜú¹‘’k#ß5Äú1ÃÈu X¥ŸGUŠ‘êÝ êô²:þ‰D¯ˆ’KõºÕãºzk;¥ÑÃKÁÕ‹n;ýÅiT =ÅǯS¤©M¡¬ô^4$ääÞòUW#Iš Hø>«F=æ¨Äâg³
+×ðpæJSËAöu T ŒÙdfS‡‘Ɉr´Ëp»Z;Kv—ú b_ÂtF9Úôt—­Æö>.ƒ%|°žæ|~€êa:®ywkCr%!açØ&{ÔÎ`Öø
+&›®Ux‚\.¤#)mÄÑûwAú·Ã)¨°cQÃÁ6ƾˆŒuíP1mG±
+a½4šTˆ}jæÈŒð°KsÚ‡ _~Â…¬à'3sÔ7¤Ë &®cb$I›Ä¢ßjÿ ‚#ÐlÃ3PéÕ4D‹'ÏŸÒ=ûÏ¢µ(~ôgšW6 ƒ56$6¢ÆTwðhöt\ÓÌ}›
+¦z'iͤº³”Æ\ã?ÄOT@þÍÈ^®Z (× øì12‰Q^Í<A§º}>€hÚb†ë],s²®’…Aá*à%°.mA˜M§Ð—W$Š\ƒ?öÓ³òiÁZ-§2ÄZL%5.ö%ö
+‚A[Í&ê1ä ê+R ù÷‰θ(/€àmhØgìäRø ”ªÌ´#Å9À'èÓ†õÊFe‹äm‰˜¼ÙÔ÷ýrÀ¬ê¡¶:å ð¹¬i²D&Jzk«NÇ_E–±‹qî==UÐ,š«à»F:^ÐéçÝS&± ß²¥²û9P‡eœèO æ…`w¡ï R[ÌL,bµþï¤iŸ¢ Žµou ÎàýèPš[
+z±2×Öà°]5iA2<BËÊ´–v(*€1Êæ+Ìߌ—K’]¹ DWà=Ô
+$Áï¸=Ô.zZÚÿÔ'IàIzÅ+Ëa;Úe<^HäG+ŒPeVb•á¶âk ’݃JðÛÌ»ªõ`~A£ÚròlÆ
+-Ò$Ìèñ‡uv ãFñG¾yÃHR‡sî™wR•äÜ«£ó<a+ %½Aݸ¥ªl¦¶pŧhžH‚Ô5®éJ%ì[–Kô
+L'>y 4eÞ+X7
+“÷ÇàD}Sšî«î*÷äq)ú|("AÐ ]÷åeWÑ#Ì{\ q±"€ìAQ HH!ÆaÙW‹¡è‰±¢
+öZ*na£Ï® àjï%´UO‰\³ÉDY}Š›üm2mL%ãâ#¹êÆþ)òrßVO¡ðèôFNÙÌv+[
+,IîçÀ×@÷€Ëð
+®mŽÙ øòYA  › |ãSÈ"_«“;_7ââ3+"„\„Õ{ůfu;ÚÿêkFçL“+6áBÿ?N¿†5 Œñ ¬BÁðyÊ=b {Ë(Ø¥zJ@)S/†õ´~ªn;…m§Åv‘Å™#~ðÊ”icª‹9b ŠL¼
+ #ö ¸p Y ë(!T2_/íØÒ!Ë‘*Qq=ëÏC>ÒÁÇøÆCä¯QÒ|a2Ç„IDþËî4–x@®Nw< \‘ðæ©P0Õ‹ÙòSÁØA0.WK·ßß䃭Ç
+kƒøQmˆr;ƽ}Ì'nf…¯q”¥§’"Yâí€vðñ^$½MŠHëvF)zÇ¢® +áX‡k{Æ
+XQÚð—›à½V/C{Qó` ˆ 5zlá…•3†tŽŸ;(ƒ
+À­+@ºaN‚> 3yÎᶉñ›7ßj"VI=žÎ“£Ùaf¨×ïEJ²\Xr¶gß߈ã/kä{yŽ=ý¼×°ëf}ÐŽ\>œèŠUÙ9 ™ÄA箉:]B=&s¹G% œép]
+0‹}a¦.KÈ_•¿´âŸ™INNûù&\¬8fÖá&åI
+^ähvÚ^xÊ·9ǽÃO"ÉnÓP¼øœ¡pdiì&”WÃ
+Œ` zš‘¾¦sO„‚PvËåFïüDú‹&Û·¡¬‚À
+<ÉÍ2Æ"cùqJˆ4€ßƪÜÆΣý”l¯Ä†Á4ÞH‚›®¤ûïù³øÊCë)çŠÔË\SR¿áÂðsUVÐûÈ›Œu=
+X±Y@ äŽY/%ûSõ›œþ$¦õxÎ §hØ3œ
+6ª|9ãäÊŠ²@·yÎÁÔ…ýîäoi3ü4ï k‡L$$—‘£üíR:°˜%ÿçŒW}=7 3ãåŸR´ ƒF7êU2kƒäTî: ØhÐw)^é@ƒäQJX€.§Åó!€ó1FE”¡Í#6âͼÓÁ{I¨-Ãc¹éõÃ9ÌŠ˜¤Å¶Vï·ù‹·Wù§h„±P[A´
+º{ÕÆÆeõ M0§¤ã¡
+ÊÀ~D¯N‘aÝ«Š|†4©{ÁTק® v ÷¨’Y?иƒ»Å ªæ /ß 2"¤ž õ^vdBX’®sýSô·,ÃÓûœ=F?²?‚¸g&øi
+¨{yÍ3äf4Ç#J1`r
+µ;Åì7H|”ü
+;È£C×Ú÷!6Ö¨Õrb]¡ E¿Ñ¿,§íÒÀ®5µÙáuŸèM[?¿"À–5sãÚ”Œ÷zv¡Œâel»[L§›8©´¬îg­´’dø}Ad1é÷«çÎ7–€ŸçšM¯ŽÔ±RqyUO[÷s:œ‹¯#b½†õ’#»ÏSŸsä tŠ€à{Ò`V²ý*è”#O KŽ„²7üýØòv ’@"*C?ã!H‚SaPÞ;Ú1Hªp?a7E½'@ ˆ$Ü:bð$Õw¢Bdµ9žÎ©2_lF;l¾'I 7ãeåïZÑ>¸~—šÌ¼«óž$5ÍŸÂÂÅí3.Qò>ð'­¸‚Ž]¯Ì‚ ûÙžòæ 4è1œ@º¤hÑ­ÆíNÐçW6Õºb™²V<ä@” 5'5[h@ºÒ`Ö²GÀÖ±ÇÜ•
+
+ÅgP±¾yQai@j!šK-xq²¥²æþÔÕ2&:Ú¶æt€¬OåQ6ïWÎçäE°2oÛ9€Ò°'ÐKL!ö.iîû¤
+ŒáâÅüc´/@ôòK×ù.æ(ÛÛº2íReµrò ¯mñ;Àg†>B]¨Wɇ0N}2R˜hCÐæÔåž ŸÎ™¢wÔh q¦Lž}©R‡Ú‡ÌàK%âÁ‰àHLÙØœ
+Mön” “zn¢Ái1C])ÁßMã¸^nP­s5ç2ëìËð
+cu¥ƒÍjyZȤõŽ}ƒøÔ¬Gêþt
+ƒ¥S)ºtš$0&£ùÃ:‰=Ãùú›ˆ£i0±×¾t#Éދ¶é&Š5q€¶/KŒLÄ ÎÁ¤´IŽHXHÏ÷»Šá¯¯(¦„õ ßÿõŸoü—ßh¸..c}™‰.„Š S°CKcO ”‰}C@Ó
+‚À2¸šá Ÿv™¢Æ¢Ó¶‚,ŒIëÉÁPôȦEÍÆMÝ|ˆ¸Ieìº|È‚2­|éèM‘7å¹Qîâ™õ
+ñO!ö™,‹‡íOçTqQ §ÛÈ'¤°åøø¨¦×d@ŸJ#–џê{ƒ¼õié
+š0ÁêÖ÷§®—aC†¤Bõø&(·4TiÒ—ç^›AF¬º Ç%ÿÔ¥Â@ÖxjPb>vêñO]:~:ç27§Û\ÆïüªÛß«sÛ…{¿S?do¿ÿ©jèÐ…XÖZÐúƇ¯˜ô—`^쎼ñ8€¯ÈPqŠƒ`ë ⟪!?LúšÒ¦ü|Ž½Q
+ïÇ­hä\(?$i„èò' ÑTÆ)3S¤ó†ˆ<jbÏ'í(GhIs‰N3ïêG>~>€ˆp^gG1KŸâ¶*DÑîŒDã#ˆaÉ  ²OËExû$%„Ø 9ÍÊà’UŠ3yy ¤ˆÛL/O_1Ek™—ü¯2Ò(œ?¬<l.õMT>æøÔ«ß÷\ã^ãA*ÁÓK}®%"J#¶=LÛÐzä4 oÄd´ü-‰,^ò¾J&;Š¥OÓˆ…”™í³oºƒJbäçiØ9b>úˆà9gÒem"Äy„4<=Nö[ÄõãÛ¤*Ñz81™+9îÓÈ£ù<ƒÓŠ÷>È“bVä:íéq™§Tov˜üZ›óµAú\Ku ¡+Ãn„á+È®"ÙÓhŸ‚I[˃ì!ƒly,Ù8Ÿ#ä_T"t‡°þÄ ½zøÈCƒ'bDö›à)!÷ËV‡(2dfsös ¾Ü´o”ÌÎÏ gü³Ò ®×ŠCRìëp{ÓVù #¬T‹{ßo IiT.qؾ˜ãK ‘*wDú4•ŸGÝ)°?ÚÇ,V߉®°É–øŒóƒú”l“s&Óν™ðm
+cH)ib.·­yƒø§ ù(š¬u>Ck`'ÎêÂËvˆÍú¥@B l8‹±ý)‚àâ¯Ñ{†èsÛÔrR!(¥YÛ)ÿ&¨E¯_ÀyÇ!¤hKü Ò(‰¥…´½¥’W…¹ÛÉAl§lv´s=V@¡],GŽ¿³à?±éÒeÖêƒZð“žÌÄ_GRéo@Èv6ªÊ·ÜŠSÊÿD¯2ÂD³“a_ûg “0'œO?7hÑ#KP ÄÓñƒƒŸ¹ÔY?àÈÖB†àXYÚ²R,dVbmÓ?…lÐ ê*ùüeŸÂ‡²H‰PµÞ•aPRO%N?&Õ%–cbÄW z1Èda¬ÜC:@œšÃÄŒ–v>§AÎ…«L´;Dó_æf›¤ôÄ&ïOÁ}©ðW<l«
+::†çäÅi{„ü¢£Î=—o #·:RqÅav)V…&ÁqjPÈì†.S÷Žð
+CúxL¯qÆýº¸¨Q@¿/¼„ÍŠw> êÔrˆÆž}æ¯éGQÍdå¿Ž¹/Äác_
+%¹J5KïII·F гnyÅáÊÇjÄ]Lë âJ0•´7l,á Šk½ÞÁ=f¹£ßÿ,¾E ¶1]'
+H‰Œ—MÒœ· „O ;虂ÖÊÒ·pUVòý·yð’üi8å¸\þy…!A ÑÝ°¦ÞCÇ÷?¿©öÊfb1‡}ÿWËxé°a'è ™ª"Ísú\!’ü¯Î6³ï÷15s´Ì
+™¯ìæšÖÒÏUctx›½­ ÙldnXç˜ ®'ÎÇ
+qŽ•TÑóCHãÚIÆ­§«~ ¯Lo·®s_Õ-µMï}…LoÞLE¥Å
+Ý›(ζB|ŽÑ*jÆ<W…kÓ9©…­ ËÖZçæiOuì¥f½…Êp_!•Ió³éŠè9»§ñŠxøñí‰ ªåéÃÞcþ¸Æø+§y„öÙFÿþóCÐÌ1»èí;ãäGM$Æ
+Ó-FxøôBÉG§œ½ÇÊÃW:‘«8öÀ¤kŽ1÷9Þb´!ÒãÇ­kOï>t…tò=Eræ
+±™1FC»„ì«šÐítt~:Gefj¸ïcÜZ=
+høŽ Û€äywaœõ÷ÈЫhòU>É6(3Õë 8LÔ ÒVHhš8H¡B²óWt°Å¹?6Œ³ƒ}^äãôóCÐïí|Ã:Ï¢
+øß—Õ¸$‰¯”ç‹Qa “ÉX!ÁÈ
+4¨}ÔT¬p5] af;‚L é¾"dúG¯¡Y=Õ=Ê@vd&…Qç)kέJ6ÃÍÚü¿Q$hOOå·5N¡a[f‚aòÙ×U»j økÎ#Wd‘“â,A_;UðN%WÈ@xx¡1%ºBL˜D@G¬¨1² Pùv®RDAòÔ-ž\ÔF2±Ø‚uN /,à<}‹ÞàÅ°=4ž+DJ“•—ê‘+,²Qõv®"ÿ>™PTw1z ¬ê#ž”4
+-±þô­TŠ`¯¹Cè
+BY±pxå›
+ÝkÁeúfĉò4‹Ú6OàaUñŒZ$¸nivÃՈÂ~ ë|OÜV 8
+q"mõ‹¡ÑrH¸¿«†‚Áç®.í²¯Â.PK*9ïšú wvxÈÝ“ð%ùAcG™ ¶µ×’ø<WeÕ(Ë)àÿº€§£RÏáo9©ñ`}â¿ã
+BJœL+×+Bu€A‹áHå˜()ö„žó´“ƒz9´¬ï›¿Jï…Õrx¿rü; þ›þ2;‰²àûÂt9ÚQ~Ïï^b´±5B㺖Ò
+A
+p/¬‚s *EY²¶¡3?6.Å.{ëŸë*JcÂ5_k`­W³Ö9ýUð>m¼ O [!ø±ò½$Ü/!ÏU½6Eaq´ÏçX¥†%²ãHØ
+hŒÌ†—d‰,€îêªjm呤‘16áuÔwL~ÿ'läap·ÎßÔ÷óÂÞõ:´—Ö[ w’ûk¯D «âÇØB@Œ›ëÙYO:Ã’G1,nˆ]J¥mæSáàªǾXc´øÖtÞõè)€d³Îä§QjâWÈßAy°Š ©´ÛeÓ ^å a×›…ÕÖJ£úIGíó|½FüÒ Ú*º°1ÄiqQ
+ ú#¯7ÐéêÛq§«ÿîNóê©C'ÖR•+ƒ–²œÊÿ!äðj\ý"Ñ‘˜:[ID¢\¯C gAQš­r"
+ηilÝ¥D´š)ïô*@Ìã°ÎÎÕâ Ž °Oƒà£äx¤t˜ë Blä˜2 ÇuÔĪ¤v’z};߆5Sœ{ìéU„Kt\e½ZµEêc#ŸJ ùucå\ÝÜZõbGm-ß¿³g¿ÍFÀãî4¾—æ0 4‚êO"}€|™©_£G'Mµv4ºÏE6ö¢ëƒjì ¬—¡Â3¡÷ãi_1?¹‘i±±Q.Eg-‘›Yc´Ä¥uq­.ä_}~¸SÃs°˜H{ÀhË0ˆpäËSY!*pXxªÄâàù_4æz,”§ó $§‘þ秈’ô‡xeƒéIʱb7äyjŠÖv^9í S^ZIHe…n’“
+‚ªÐKÒnŠþ’’ ¬y”§_'Ý1è0V!¿¹òv¾Læ¿› d}zTa×lZÖVÖQ[m° Hïd÷vã­Â$ÆešfvêÔÈ8*¼:šOt+Èàù;áÅ¢ °jjçÛ
+v\T0»6y '<âüN¤BN<Rn$‘·’„fN*ª¥ bÙãY!{CdÉX3[4§=ˆ Ìûjà7,wJ@ˆƒ
+%Ú&DÉHÇü¥ ÉêªcªÕ†#²ÊÕò: 3¨|ªúdŸqìzXÍÈvRr¾h]a@`'Y¯ô£Ï)vÌJ‰ùqBÈ[ G"Gýsҹ݅Ñ8A‡â`Âü of(Ê„°™Z±!kæ·2aœ±Ä«í¤ reغ¼?Ú‰š< zo‚F ŽQ±p–/IŠH
+i(+Ìæ8ú}Tƒú æÂÇè›âLTA„ß)qæaÜ‚Þ ÿ¸¶3ú çGfxŲè§ÀÏ OßõTŒyˆ~Ý&OØÆ ?•š´’&„ œŠq tM¾`WÕê§'#LE19õ]{TÊÖMR+í†)miá¡.pŸ
+1Ä(Ô¼²C1*¿qé
+oYíþÖ¨«U0¯ÛÐtÞÑÚ0þ͸s¦xVØl(Ÿ_\ ?b” 'ôPEî2Ñ¥Hë š!¼òvÐ0A‡ƒ¸^K›Íx
+ËߘtT ÖG;\ûµe NZà@N¡ˆ­õé;h!"ä» UÇó0–‘D?²¼Å«–/I1ãÀÊšg7Щ8•ÝDïF@jú1( “ë]…¼Ûq êÄëO ßÏ W|ÁQ,¥†³ ü%|Îéb-ägÙk~ê¡«ïGÐÿ/—ä¼q
+¯À{È
+þ"Á÷8wè]djïz¿#‚Š-Qi§;UÝ ,’ÀÁyìîÄ%h´Çþã"² Ø0©œšŽûQ‘fÊ *¨Ó‹ä›Ûe„^Ÿ¦¢%/î$Ù<.ˆ„¿àAY¤åéQƒm9•é_êM˜¨òÔá”d‚BÕ]ÍÇ&çó‡Ùa“в±ñoŸ³ˆTÅbãâƒÉCo”ÁwìL-Œ‰n KyV²˜(Ýî›ôgÔ!A~¢ÇµØ×ݧ1ˆPcûkëh¡H/ÁC§ÖŠ¿¦e¯-â?òÜH"C–!•¹ß}x"fqÉ•0i—$;úú£~Zl#Ô¼}¸¬A4t¦G\Ε‹²”ÿ 48b›ð±– ×f–w}"ì
+Ê4/|]¿@-­cÙ÷6’m¢yt©)Ov4¼ ¬#u‹ô’ Ï:R‹.ŒžM‚—ûLN›²ø¥FÖö÷Û¾(˜Â]&9²îÛ"lIg6{¡ äW†©qqüãæ&:!Æ_Þù*©'òä‘è`Sªí!ÅE¯ÅjÔAiØÞ°Ýƹ3~Ú;/M;s8¡%úìk÷ ƒÏA˜ª:z”‘˜¸q¸rO!$È.ýys˜ã.eú¢7»†Â
+3ÇùeYȯ˜âÐ[8K¾/IÉ(—‰ùý¶[ßKч52 :SÖ(#Tºê8ßÍ]H(Zš%IŸ-x‹P%Ev—õ}Úgƒ…™"·è7X¯¡‹o.ôHCµ°c'·³¼”øQXh€•!Ê]ÑD Ç4pÓúº dC a?‡ïKÅg‘Òòç$¶
+ €ís\0Ø
+RR‹ÛÖ `phL&gþ`­Žq
+ª’e":NѾÌt™ °ÈÇh) °o@ÝWvÓÓª¨,y”@*ƒzáÓ7 rD±¡Ñ¥ ¾*Wßæþ@5(mêq~†Ù'éYh¾òœœÀLn!z šš¥á亯%ÇI…Èbx&þ3>|§¼ÚȘö?¯DÅ]]3lRRq£sLþlÚ ¯@‰É1t­Áñ¤ØtÙ‡Gáð0”dKÇ™r yn÷mݸ®ÌáÞ«UñŽ½3þ]žð3H)œîàþ™ï3
+¸ å¥áËéÞ Á»i]·¢ˆ÷k>á”w㞦@nJ¾†È??Áðû_Ú^º4=Ž>ôï†åó¡¦+bŒ@T4O7þüôß5ˆ¡‰"ÑfIÐ+ØS‹Ë¨Ç¤çµx΂l•e³ÖH
+‹….…0| òq´`o-+Á ˆ}Ú“C­°û,X}p¨eŽ‹.†•m!>…/y4{!hGõ…¨&f  t¡º0CŽaG>GÃ1,+ÝÚ3Ô®(
+xÄœþ°›žó¨-ïÇÀ$U‘`¨hE*¤´Y!+Á¯PG°¨ø¹å@DÆétåäº
+ÔĨõ=[”/Eêæ&o*ü$y–€Íž>ÓáJ@@˜ôyMØ€¬Û½d䪬™F_Ïβèè Ðx+ºhñÇÛî0°“8:Ò< BG”ðåA!ù2—K,D·]‰?»±z¸ænOßA&à'úœÆÒý!“Žó²êóLÈËa^=U$ÐÌ~vìÛ`‘h°IÝ-:y¡’#^È¿ÓøÕYµa~›¦xÂ|•Ž~íñw‡ü
+U…Ä_+Ÿm ¾Çx»@€Lê€co—Š÷îç—÷kŒq:è;cùQî(Ù|ߙڜZË;S”–û Ž¯¶ò¦ŒßdþŠ9š²D_-Ö†Œ€EJ§ë‹h¦è®Àor£¾¿RÇ7&mwç´)òï-[X¤I$‰ÎîZ™aĨŸ-I¶&mÈÊó*t ¹Æa¯Ò(|¡•}›dÅæØNý¦Ä¹êKC‚6Tîã—yy½hþþÏ?^Óò:€N¡ÈiÎx‡kÐÛø}þfFm”ǃ}¢ËMõ³oΈ±§3R{”/As"»Y³öˆ¶Ó1»@
+Ïu¹[‡à|&C·C"ùÁ›m/Gü‰pÁ¬ÓFòɬ8¤µVE…°!±a³«ÔX8·P‘•M×#r
+½„¬«:ÓN.ðFº?§ó©öÃ=ïÙt•UÅÌõ»WõGktpyéäW§‹èL=Ÿb èXØcÜz•” È$ãçUïA\Ù%£„éÃË&0pU­™at÷QaŠKì\ùÜôVœÀÕ|{‚}Š^‰ AúШܟõ{kÕ[ÈAÅk˯ç\€sÍæ@ïU _ŠãŒŒ\ʉ…Ï7/Cõç9{ £ åÆÓ€¯ƒP)²ñŽ.AÎp¹Í#ÿÈhqÆÚ+†²Í¥ÑÙ¿nbTZ
+K¹†ä”…†áhOÈ€$…hÌŸ…À—$+og]è4ìñÖã
+_ãGÿú>fdÀ$¯oÇd-lXÚ<ÒЯ°²Ã8!릆Ig.;šWÊÝ98]ÒFÛû(3ÄQJAÛ!ÔG³7Å츱?ÿøA/nìÓ Â%`H‰kÈe´|NébŠ&ºtÝÕÀv‡ œŒŠB8°UH,ööô<Á‹6èâÐ{aÕîÂ8 Œ{¥Å&ݤƣ€Eò«übý|ëÖõœ÷–_“¹ Ç{Ô×â\p|-±õ—Výû‹™Ñt…=]å–"Lç¸/7(=X@3…c½©K1ʃëc=™¿ÛÖÇ)áñé§Ha1 D}æͦÓ,bѦk¯¯wlº@FÓõ«ßî/<ÃÏÞ¿fÓP# õŽ|÷ªþ€Ô*r@'V‘7€Ì€ –†dþ°aY/¡Éhj
+Ù«âøÚd½ÒU¬f”,ïQv‚¾·þó&¦÷_.“ö]ÊÆ°“1”ü]Ç”ËéÙË9ƒM ´Õ53l²•Ýfž"óꥎã=ã6Ú¯îs@RcßàþvëñåKÈaæW\\Ï]t Œs™'›ŽMf2ëh;„¥¬Ó:Î>@ùpG¬¸çª"Ðd)ÇŒK•t÷3€†§æZz1½J ôÉ}þìA­SŠ„2í›þk>¿ ¥¸%küÞ<å¥ñö¯‡•URžTŒoG–±0>|¶À¥;àm%u§“M±aʆÄ0¢òYðÉ°(ÖÅAuøH
+£áó€‚ÂHB×rÞéY$ü?RÁ„·~ÄÔ·‰ßšÓŠMgæéw_r2 Si‡/¸“®üà÷à¬ÔGº;G¼‡ž!Uˆö0ïâb>–9Ùéö5óáQØWÖ¼wfçïf)yà&ˆ´üë oÁà:Œ+î"ŽƒTÆ‚Å
+ÇaW›Ã®O&;c¼QʨÑ}¾\\záÉNQ ÒZ’˜Žð:
+‡†Y*™]µB!2PQ6A&5Ù™¼N*Ôãs6ˆA§Ùí¢åõLW¢(F`¼QX`ØnBT=D ¢;禟gëÿ²SæCÖ¡iMiÝ1Ð_;ˆÙBx‡„ÆuÙ„Ðq䇈`5¡ ­Ç’ªZŒ Îc2û\­Ç0®Ì‰¡è]y?y¨€%1‰ç}L amùØÙ %¾Éìî@µVYÓOqŽ—2Ä=Û©{`äõ{ ú{D·È­ ¨døiA@|J’eT¥"T‚"-C¬‘Ø;FˆVæe(ª;-$2ßq©ªSå€cänàž"‡eû¢|çz:Ìߦ–—»FýÜð?OŠVh퓺G:ä̆a[¬ZGºíÐê˜Ì1evN+ sŒ.`2б尵–ì"¨Ù”/_Ð,°]úuØ)ìG¨! Øây".m.bÑ==d ĺÜ‹àSY]ØÜ‹Q:V6Z¿Æ\1¨tsÁ}@†gix]©áyxŒg»[>„e3€!{‚»Ä¨,f† 'WO®]÷t xàŒâ_—îlÒ~ÒɆEua¶sÜ!…¯ãÇ1¦G—h¼pkO ·ZjÐÊ9ŒPR„@X{qëyƒÚàA°aÓŠS9¿„e ÝB¤I,z·Q9A¯ŽÄã‹ßÅ ì\@تåu±bGíÂì6 ü±D Z>Ì«V3îšF_‹têÄäõ³½q,³¢×\ÈÓÃT”Ð&F*BÁÀi0.¨uÌÀ”LmUJòIÌú(‡ ðÙE´'Ä®šyÑ ípÏYЙr]A³h6>†·pºÅðÒÜÌ´Ù)Y‚
+ÕìÍëA°?qíl“
+ÄaN{z#iˆ£ÉqâP‰ P™‡Úq\Ä0îg=Aç!2\à\ݺR»G€?©8wñì‡Øð¾§ÝÇ´–Âì`°¿,†eŽ9©ñÈŸŠLñû¹nZèX°–J$ É0ñ,3îDí =9«é}3ò
+Σ ikNùÖ0™ÍPZÔ*;£…àl±©cÊpýû ~3_ˆÍe´êá— mÂeÆj€­øvhØôP ²$œY‚mEþ2ú±ìœkâ&ø¦ígšY6 i£©*+Ï`B0%1 þŠÀW­µGÈQn¾ÖfH |§ñ5&xZÈ@×I%<¹ƒš¨ëY/Xˆu‚Äe$MãöŽJñâig3} ú´r6'O/h…dJ
+ò¡¦N!.rEëI’RUy÷5kÿ€–¦ ÛO}e6ÃL‰ìª¢ƒS‚ó¶i©v ±ËÙÜ!x2ùŒ®Ýë&d¤,‚æÃ’ÏU—  ÓEd°ŸŽ†Eɹ=!•
+önœ-Nœ‘;†ïE ì¦Ä
+Bv(ÀàªnõÃã ¦vž¥ôŠÀüÍ·•`{Ñ¡¡/ B¹‘ÌÌì»Ë«B
+dV
+´Ú2~`̺r{ŒöŠ¦ößç&>ÅÄK²!CQ¼?¼á" ÉãN Úz<k…¯fÙÎx‹ÜÄkw9¬:;
+)… +«•ž¾s# ݦëULO0¼”¨'üi¯B”‹"}}¿êFb€XV¬:
+SQ\­€(rax&æ´<@*É—–2`˜ý¯¶5ÊšEôä>@÷†óΕ hÈ'ÖYm‹yh‰LâÛABvÌ.FaŒ½Z—hW
+˜Ì°Ñ8&š.¾ƒ2@ȺY1âJ“â ‡ût%Nü
+¾¼ÉànhpW”~Tó5Œ²Ô‡å¶n
+õŠZ|uXψ1ËïÈS¸ßÛÑï‘aéa¹êßY@[Ò(Ð$ØmØÐܯõÛÚÖ^pU` mL•oYú™Öw”‚4©mÇŒ=^—SS€`6y'pƱã*Újø6E9ÈMš’–Bëá­µƒ"_ÊîÓˆUZRMlŠd%à p³A:‹†í¬&ü, Ê_Éb(Ÿ]çÀüSd?>£Ä­P¥ùF´¶•ˆkxö¼¤šz%hB9ö›Ú§šZ|ñ?c;18bå>|áÓ¸ÁáIÜŸ4¾ uúÍT¤!Ž¯¤ÎbhFÝoª2ñÐû‚„ÆnFh¹[mx2CÚ'\Ø,ôìÝŠ¬¬læ¯Ï„ÉßË[ƒ}UÎlVtIìÉW§Z–ÍÎ68È S2MxñÉT´Oƒ8ŽE„2È›Å×âÚ}b#9¿­ÄE™EÛ
+,{”ïvO"ß”[lÍ|[“d"5½ñá;R:1;³¶ Q~2Ø\|OÉD*³ì.ïâ†\yÕð
+¸yÛ;ƒÚæ®±ü2nFl㓲œbÔʪ1΃¤Ô~8¬›añøò0>çXœßa¸¥ØÕ:Ò¾MÇVÀ„ìŠ )wAÜHv›u1ÕCò µí£’ØÛM»˜ìpÓãm,> ×n¹Q~E(°5¶ Çâ]‰áB˜ö9¿^Îe>sS¦…‰ˆ Ë]rÚà~`ÈmípgÀ[²cØÉäv@˜v¢Ë7<ÚkÂ-‹_®Ç)<5Ú‡‘Ü~(d9ÒÈX Ú®,ÀûâU#®Í?2‚l[^í„2C…l¤q:¾‡@U„ «l)ôfE"/zGäÂŒTggdÂ2k@rÇx““f·ó•Jˈ0Å.#sŽ?Õ‡±£XìIQüÇïP^*ˆ³ˆû6pdDÊ1‚×mÈvü¤`’g·G©Ç•@Ãú/Çë¦LödŽ~tB&]×¨êŸ JFÖ l:TÕ´êƒÏ)g¾#ÃÒ (vƒ`‚BLp|·~Væ0iÛÆ.1)—5kh¸"Ž…oë3l3C }î†Ê í_
+vÞVÇór£ŒÉÕ0Ä|8/šÓ`ÀÁhMBºm“D$Û@
+ê®öæ» }‹VÅ n™A@ûXíU¼Ciø™Y“ðRô„Of³ÙÒ,ð4í\ìd)+f×ê;X]6a迺™yÒ0òãB ä"Nêä: zgd´V!Ž§ïx›áGì.11R¦¤¼Š(qšÁÞ„¤@}Q&¶­‘„d03³íËbÆ"¿±—õ<k¨éSÜ5ŠÝFÎ ¿‘v‰Ñ¼£”nAðY8ÌߨÄ.‹;±%£ÿ;J‘)öÀ—l ÏŠK,o éÂ{) BÊz¹:Gwq2$³»‡>È_‹ÛqŸ9 õ·ûlkŸ”E´†ançq®}D5klø°\¢û-Ärà¦IÉ tw}|æNºúŸÿ%üçüù7¿“¥,+C§ÁŠáÕs`“›T¹^¯
+}È%oHm¼•¿µ´ MgMÊVMõ2ôµËl Äx3”
+OÓŸ°M$‰á‚À7RR¥5;ŠÌFÃÍÙI¸ì&]Ÿs9] MB©K/o5"öKXiÛEg€:5ÖT–e?åX2lÙ:ª_é‰Ù¸ÖS
+sgæ4¥®¿KOÓûá(ŒxÉ0†×Ö¤ܱ]'7e<?ÚI4 Âw±44g™¥N*a_žJNhóN—ÃÁ#-Û_]1¡§€h• 3ä%ú.qÂ.àîùÏìnÕ˜Q]Âé}‡u¤Z
+«qr§öoŸáÿå5;ɃQæªÛ. šÕ÷·nª¤‡\;„–GvµMÚsÙѾeô®‘Á¸JTbºZ „Y*$*æ»´â)¿º;ˆã6§9^åø™³:‡/:k|¸«³WŽKTÐn³Ïüô6‰™Ô¾û&M²g–bÆ'“ö‚$:랟Š“ ~ýê¡ÄÄkÕ äµüvopn¯òðVo.±· ¿Þª¯{ùŽ8=’˜/kmî"¯×c90OR"ãf\">CÀ"®’?SÖÐÇgîd¼Ñ53™'¶¾U¹JÞ1)M2óÝqÍ #b8£¬[a rn(uGmþ¨1°ô–ç´AÇq¿Å+¬áüSËH~<€4 B’{ù² Øb¢iĉã 6€j.ˆîÜË €{$‰ öЬ®yôÊË$=Ž\Â'è;èõqÖê¥oá­|ÿmÿA‚å.&S.?o^K“1èiò~uÅRF' aÚ(Iš@dÄaq"ö‚iãî†u¦$§À—°ÅÁJìS(± <´‹f‰šŒõÄàXIc#^Rû8Œb¿ðçi^ƒc2 “”\[].¾8I¤3re½i aÙ³\ asÃb<±${­5QÔž¬ù»À^= bcŠ?ŸÁܱjÕMåÖ]Y‘Œ'`]Ç=
+tÓåÐs±
+jǵ`"ºqTyÈ]²Pü|ɲ×:ñ‹y n·cS¢Hø‚“(å¦äžŸÿŠŽ@/<¾Q0òñ/Š®ËwøÜE!÷j/2rIÌ ªÊÙÕÅlˆ1*j¼˜U©Oyb&"ˆ’Ÿ6¥˜†üdÙQübÔÀíâL]y>¯³$ttÙ}àP2F›ÉZ¥ÉYº|sNyàöùš•`"=y`ÍÝ*Ò>ŠŽûzˆ·6Ú×¢}q~ÌûPR»#Ü´önŸ“„×Y)BÈ×M‘² $fƒ”g T¼ŸSb#| —Öì”T‹B†Yh=вÙaõj"­¢´«Ñ• ûÅV¥y‚Qb]Zµ±×˜ùr”ŽÏòFÂ;¸Ú¡Ä>…Rµ ìô„ÏvN⴬Ɣš•ÀŽ^ü¬Ð̽ŽK«¢{P‰òóÜù%0dÍ!—ô „­$Æôužl °ÐújŸ×AÓ­bC;ñ‚L‚íd’EÓ½è8òxðHR òv‘ÿì Cu)¯¢íN[Ñ×;k:¶ùã_•z”&a˜ÊPKÞÈ
+šUU˜ÝÌ~@ÇråÖˆ*H`æ/sÅ© ?Ú“ß”)€#„…‹u¹‰’æ*»U…u´Î’ ó¿²A—¯¸2\’É
+ë¯ü?Íu=j?†ig-+Öª˜«Ã
+¶6c[ŽqäãœìZò#Ë;LõðYRË*Šâ\yNýRtÚ¯LRˆ!gîó_F
+ƒÈÀÕ†1Q ™øʤ¦J$½²r#²Rþ#^µ<ãYã¡ãÚá.;‡ dÔ¨ç9¸‹â –>hA% ØñiŽ5s²þ&ËùPbŸÒM¼xkBètOÓ<?\WnY,.¾o‡Qìþ<Íkçhë2K¹”^Wl ‹ÀŒb¤;½¼°jì{)‡Ô æe5}D¹cò „ÌA®p½¬åÛÏ`"9c PA»*~ˆ‡Ë(v õᓹwF±ï¸Þ± Ø¢nCâo`;Øõy «›±ÚOƒLÇÌ
+jœvSò‚NkîkÑçi€¯Êª¦úE§Ý»|kµ™¢$€¤•öÛ´^"Ó©è53©“ˆ¿!uÔc, Ä‹¡ôWFÙã…Šð@äõR¨püQ‡õ«[©ê5ꔨ
+"UÓ‚=a‹–zŸ†Q<Š³…ÄÜý»Eÿ1^&IrÜ0=ï tpÖÚúÚªï¿õû$XîÊD¶´q8ª¡$ |üAž‹ÇZ 9³Ÿºñ¿ uòDZo=äžZ)¿›æôo˜yžm¬öæðh13ÒÈ"žêl;´‹@9+Ræ°UM 2‘Q¬c—°-\7+Yžqi‘.ÌœSrV5@ò°åé;òâìs³ìEI¤ç†ž !‚0voÚ…3·Å‘½^¤¥ze–cSËÌ
+­k÷Š ‡R9.×4«‹.¯ùµYt(ØÁVˆ&F(΋.Ï>Fì–¹ü˜ëCÕ`uãbÐQB8 HÿúVßi©vÊ8JëЫÃ8ŸV„fqIµÐ.Be2"5©:>BèÒ*$.¢Dš9¨¨”¤Ê>%¼H?~ín)A2+þ•ãX›§qk7þWÍ"šb¯ì+² EÔÖ@UÁ¥:½Z{ðÍlSDøÇFè­ÈsªÜ˜l)°3gÿ\$Y—sf_iü<¼€´ÙÝŠPÒ¬l›8H™ ¨ÉØˬØx$ÍRlÿælŠGCŠ£$Ó=ø¸Gþ¸\†Áª! ×Q‚2ºC™öJ3Gs
+6Œ[2Xï{ŸE0ˆNå-Sùƒe“SBö}N†²"á¤"IÅñë§K„³_vµëe£žÃCx0ÚÎèï~½`»àf²k9×¹~@#¤‡×䯗©™‚4Ø·ãéïoªòù®!dØuÀ
+¦c§MIKÛ˜Á›µ XPpY%0>@$¤¥3)Ž
+ÊnÏYå[ÑÛä{Eð2KïE¿ÝV¹ ï˜ø4­„µÇ­£x°=]úÎ’â ª‚fÎ|+ž§/Õà~é( üДÚj°Y¡ŽdϦHã¤Ü;[¾Ø(ß!qû¨Zb>4<»GLؘ
+Ï—OÞ dXļ—ãI \„<`p .VëÒ‘.f›ku‰ð5bz›,X6fPì;~,¾â€«9À
+α VšùÏ=«úg@ ᶟ{bX¿ÑÅõ­:˳¦:ÔͲÐÀ5AÖÁÓL–ƒ¹Æ]‚Y¤Ñe¦VÊÙµ° qfóv
+ Q¶ƒ ˆê²Vîwj—P“ÿ¸Í"PÙÎmT‡ÄÊKYI–Aå•%¼ø ÓBƒ÷ÓÀ‹Í™&¬žð2`g_üöŽ@õ˜jùÞè yhþá¬'¼À'°v˜çï]ƒÃŒtŒæ x/p¯Ú×oä÷-Šg©i¼ˆ†2¥>Í—"1³Jù¦YŠpLIiÖŠ°OâЮo¯¼näàäàÇ«i_N»DòR«Å)ùµÂÛâŠKNŸ!¬L OùÜÖXÖ™jÙzw¶÷ç?N‘çKý`¬¨ ~´“·"Q×D…%d—]ÄqRí
+k ý¬ÀÒ—Ã~…ª ¡ðÙœ‚÷—(¸È.<b’:Ü“Ïul}ÁZÆkòÞú „´›†ÓaÊŽÑÖV—"ÓÃr«ÄpÌuPþ`ÝàæFGyë"C%˜º
+±¦îˆ­ð_ç}­3L’98²Å­†ÁÔ2dÞïà÷ÆoN¸ž§*B˜Û††ñp`®/®°ùXU¢X˜åšÃƒÌOU^ŒS=âQÞ)¸d<‹ w.Ì—Ž*KÚÝV8cµD‰a§N Ìäà e¾ß…L,æÞRÙ/ $&OÇPèÍCQ"Íè”ó#:ë¤àzß ÃüB)ªÚaÚž=¯;ƒº€ÎÁaÔv„ƒ@ßÙ­ˆ×êM ÝÁ­Ó×V—¢3¯Á{ðLÜŠ¦éס~_-ἡ„tŠ,¡SÏõµl»0 ‹.˜]ì³’L¬÷' AaL0EâS²XY§•²a›’^ú¨yìk>ù¹«yÏ©ÿ
+H‰¤—ÁŽ¤· „Ÿ`Þ¡ÏÜ DRsr6°A
+S&s25Åxvl ‹¤æÎëˆvì@¤…”Ð^£,ˆ’”.>º´Åã‘YñhzòØ=¤µ–fm^åW‘êä* óJ2+¤ÜuQjN.Ü×9CI2Õá²²…ÀŠ:FN«¾¨s *’Y·šT¥)&u
+ý¶ÞvÄ°Ï1!׸ùꊇŒQ“lÚz^dJØæ!壟ÙP£D?Tˆ’q÷àè2!ŽúFjþ
+ÍæY¾aÏ‚î[b6DñµþÃæúo]s4×å÷Y“~ô<TG¯¢/Ðé[[tFû— æ #®hI=¹L-ÜÕ¾L†œÆDã6Êœ-åô•KÚq¬©³S3!Z-EŸ¦Xeã!L`Žn1Ï9º™õc²—Œ5[Š†*C'„!‘­+7ã*
+Ph¨d­íÏAÕ‰‡Æ}G jYzÊÀB4î³–¿ZÙcòy]›-¶¡À†áâ&ê¼4sÓH,Šß˜§"t ®†©3““ƒ>aWâtT
+¹‚e|g^¼ä_͹e*:¬Z Œü Y˜Ã[ƒu—ãQ»«Õöœß¾æ#s¶QÝp“›{¢ß&øñ¡N¯O´K6–ÌÆj|mqÔ5vÇ}y
+*íxÏ
+íÑ;MÝ è€0$Ù%(ë„TrŠ„Šd¯ìW* XW‘²Šém´…ÍDÞ‚2‘’¡2½ÆÌQå=nXVt< Úf›ëhœ‹fAìQ¹h¦+A=[OCzæ( ºæÄÐà¨
+ŽQ|v£¥)ë^ÑUYÍ6¬(a®šçtrÿò:+†_f¯å=õÑ1¬KÃØX`¤w“*k­b¸,É<˺‰mˆT4B \¯ÏèàYn¨½âg9“‘Œ"89h,ß— TAÖ7½_¸¡§¥Æýõ YKRG—ú„°9‘!k±¼Î«éÀT•ñè6œtmú­×®É‰
+ç×9yÇɹX1¥I€ˆ”ÁDrÓ,-ÿf¼L’ãÈ• zÝ'(Ã<¬ù—¼·äý·ÿy"PÍÊŒ”hmÖÖjy!|èn_ö¡¬4Úþæ`";@¶éö*Q’Œ/›»0Þ“‚RÙíˆ~iNsåøùÑ¥Ïߌð2­Ç´cH‰{ ËSb¼œö}Á2虸؎$r¢L,
+ex4„>okt¾ ãI¥2«˜Ó¸{”ÆÞÕÛë ¥_–…
+·ÐFBwb¼ƒ¼têóß{¹lQœYOø!;f´=Prf ¯–J:Ö÷
+ò|9{'?òm¬¸€^™òÃýÚo0ÔH*Än¬–·ö@-ŠSù_òe ‚--IVdC]|2ßŠÉ Ðr",A< Ò‘À ¡êyG­šËïâ… Ä±“ÞS
+¦¥&1ê1ËkR²/Q f¯Öíõ‰Ák{ó‚L,%‰_9Èçª"
+¤>vÏÁÖ“Ýpþ pq/ƒ1c x[ïÞhTF(Ùš_DšôP™ÞÐü_/}²1ŒJ bÞßü«p[ W›#ß= *R-1Ÿiî/Q(x…eÚa+è
+š¤‚uüG zjóŽrá Hq©·O'ˆMÄ©ßÞ9§¡¹Þƾ뫜!>WÇY…sÿµRïkó¨3\Ã׫~·(,Ô(×XP¢½yüŸˆ3
+Oçóúð@7¤ñú¹3èë7w2Ò`~C¨Ô¥ö-N/jú}ª¥æBŽÉËúÁGsïIä¡šT\–ò€ Êk²”FÑxYÂëD窈6—Iyö9¹ÅAvo¬sèëŽn&•i EÇ|ÎãSõ1|#ÿ&„J¦cÊWš¸WŒŠõNóZÐ9¡«ËJ­CrI(
+Ö–L‚@,Ø/éä€ù~hžF‚£¡C*ãþN íJ©ZFd•È_Ç,¦u Ý¥!ZFTí¨?ÞcßåIè*g2+ùù¢ è5°ê.,_‡<#™&À"Á øÜ(ËÐÌï59zKÎÏÒEÅÚ¤–Û
+èí´Šµv®B£É•£|üÞ¢bÕ­ûOl?•#©Ž¼G<káöjç°}eh“.•«ê°ã%M‚°:þå©›aê <´ÕÇ–óœ…ÊÄbÞˆH0ÏäÑŠ“"" Qœ ®mÛÅ0~êÚøjviVÛŠ® ‡Û«ÞZƒ€ /~\ÆSQ^éAy‡eÅ0á›C2N9“•e˜lÿ4¤Å?§r¤SJ¼U ¤[|~ D}”1amÉ° ‰Yuî/åH´y73Áe]Q3i\ÏÔVÄB“t‡ÏËa˜ªèº9×Ýk8$ ùO®GÏÊ$¦Ô:—Ë¢­é¸q‚‚"hS[eB¸”lº:å@^ìÁûúTã¾QìŠú²õ¦ý¬WŒcnÃÂß³`¨Þf ¬,»¡5[~Ÿ’œ< j\åq#ôÙ4üÞ³›¯¤¦ÄÛ›<f"V5V¨tc§NÖ©zÕ—²Ý
+Û<,u:A¶ë9[£ë9ÖÕ”¦±E7/w¸Õ·ÿéƒfe.»Øר€kF–I¶z¹¾+¦ŠÙ裾øv („Ü{I»ìæ5Ž1G]%WÈí ð+äs}idE3È)Ý3{;àÌíKPÙ±̸]fjz!èЬ§RHÓ«Xy0$¼‘aØnlD}Œ”dæ@0P4(Od·!Ñáü‚udaX»0‚Êê‘%²AÔ ²ÂÒçóÝ\nš©µ-‰E²ö*¬GÄ!óøézºK…_ö¡¢ÑJ–öæHŠÏ’Á»«¨(0Oª÷OŠziQÑ+Íë\yõ=#~4éóó«pÖ¤'ü"žJdPÊå°ïņ—gŠfÑÓCÂJK{ÝÎ:¶Vò’–…hc’5ã¢~™ÇÊ6“À W+NzL‘à<ôÒœê”u²G{úT
+ µäìûÝKÃ=ß̈֬qêŸS ¹]Öe$ÿ6C“¡^”¹Þ¾ª<°8øê¦_œòè£Òp(yúv ?údË{‚œºþuâ1Ô´¯wz–¤a`>Óâìùáò]
+£É³ì‡¿ðPêÉI°Âæ­—68ŽàÜN×Yh­Ür>Â9'‘yq¾Røò:·aF{aÓH-Þ½ŠQoR,æ Ýúea¨1õðx1ÞA^zõùïÝüøO®âÚ ó”c÷—ØT’7¢ôkà£V
+!É[ ´q(ÁÄp.o@ÊðâiÆ7oÜaoØKÚÑ~ gäŸPùüÔßwë?Ù )†«0Ö¤q^K]ÛÄèÐ÷ I¿½8 P…Ù5I<8õ@¹Ë&±{ÈëÛ!âà“’¡‘9gŒ#z$GV(esªºFÚxÛ1 ,PV8ÚÊÀ1f¬ÜaõPR>‰Ö7/€€ 8Y·óCGP&²)¾»9ƒñª’“”ÆÝE¢|$?k©Ý?‡åÿ3^æÈ•ÜJ]÷@[Æ Ìƒ© Ûî_CÞ£©ýë\ QŸ¯
+ÕÍ6ZAê6
+™È¼ƒÂC«)qé“‹Ö7½‘BrY5'Ö12[ÐHxÿÁcô¬ö°mb87$esÁè:òIhRšBŸ¥×~ÑjáñQç2FHFY·¼Ì¡×¨öÐ înÖ¯µDîQÃÅñ
+¦bšT”¾^‚+S£o+$:M ¼×J·Åb2dZé`;, Î.¹„’ö²¬vÌ ½°ô
+^ÿ²Û”â« ‡¶¡Åg±»dwqI˜‰2Eß6Áo¹$¹ç¶?q¤èt,1¡5¢C¦yºg%‡5&•¿qU¡j£Ï ’·çµX¶ k¤—OK(< #;ô4ÄÉ®gק Xu)Ø»· Hµ9Ä4Wª«“¹,36æ0Äíl!ŸãÑ5ø ¨‰6œk‡ÍúN™Ö£ŒiNxUÇ e–±tÌ3£zC™¼Åsç;ï¨×!ýÈRߟÃÓ£lÞre}ªÂSƒÿ‚é
+IÁ­¸ÜV”ÃÅñ¼^ ú5A…•¬ê„OЀTD*çZgND*kF‹’AV^ã¼>ÅVAX~Y\
+2Q“Û4 ’ߢ_‡aŽE†Ô¾ 8|rleq3 ¹¯æЉØåH“µ'Àæa/¥æyNt„6Šël*zy±mëSgõï܃Ór‚é4wSñûéúf~‰j™¼TæºG<ÍÈC¬mÄað1‹ëlÊ`Jn2 ¦V>!pÖ
+º`ýá…T¸'ÑñoÊÚ.÷ óUª4…,dÉ©‰¬Óç1,^‰Élq¿ÈHf$‰EdŠק. ~Fyfî8Ï!OfíbekÁîc]±t%NˆÌ1xçW6HÊ
+ù°Ñ*:š]SñzÞ€ v×a¼Ýš?íf¤Ìà‡|B<Ù×`óWŽ9”²ŠbㆵBrŽùk …â nºMU]¤¿­êêZ.ã·s?gúÛœÃð£ý%—ÁßÊ33(B"
+Ž¿DäH}]çzÑ¡çOÄj,$y†áìÀ&ë}M¼å‹KßBAÆuqchöÝ·ÅŠWÎm~zÞ€ŠxgW[4©!Gâ®ÑÁ‘,y±Å$
+Q-ðÀ2¸é(üµòÁ±GH’&ºE^/Ï°qÓççܺrÆ‚|€c$.úý9 W£©ð]ˆûË0£Õ;íZÈé®(ͺœsQRØ3òec¦û@böþòòTŸ^N †™q ¨èWåßóãØá„â }@wIô×”ËÃc‚ˆ(‘HT&èü¹3èù“;éêýúûŸ·¿þ}SEØ•ÿÙ\f ¡ó/Z‰Gø2U±á¿=oF x:ÈBå>ODÁ,+)âŠAé.>È1ÉâJJ¥Ôý9¿„ýb&I?¾ëÛç>H(Ɖp$´7!ã•7Yæ )`JñR ‰Ê¨šI…¡ñ©Ì¦S*¬ÑF¾„!Ç<x¹î4ÏÁ¡’çƒÁb%_&ÄÁ\¸2-¶ °ÄÚñOª~3>•ÐTŠòžaÎD+ _¨ÏlNRÒG»>ŒˆsqÚu:tÑ[kÍ ‘²¾t‘a¸6¦%ûĦTtt@HŠøWÌC€÷²Õͺ3—˜YÑĈVÎÑ?Ïs‡€²† b×ð˜(ÄŒ—9r9EO ;ðØ[.oA—¼¿;èê, G12¨ÏË_¸øþN¥|´ŠB7)¸.Yì(©ySSç@¾Üù{ÿNÓÊBkÄÖrsñYÔÐðŒq÷*¼ %­˜ì1¢_¦˜~2]ÍjÜa7*£s)ÒfiD“j¬…À(p4S1Ð÷ ˆÍ€,HxË}qßG0]ì $ðoüpJ,Wñ±Ll¨‹9_ÖÌ®dD´‹8œotzP³XÙîUcð“øÆVaÕ·#
++˜­ºÈg)ÄsëØ
+ïø|nS *`KmjF_l;“mKšÊW2ögÁß ’ …ªlõ9ê TP<æ í¨¦ÌDïâ°Ò¡cP?ë<ÎÃa lô3¬2•÷õ†÷Y¾\ öªɧ¤Û6’õ
+#YÒʱ)ÛbhÓ†t +TºAÔ)¦>2Uý5aÝ&w²;‰Fu’¨ÈhÛ˜³r¼{†b¬Yá—J "Bæ“ÈW£=zUôãKCãþÎÐõq#;kÖ@ëß`HÝYo P^‰²AÔèY¿š
+…„ &îªÜ@^Øß?/“˜—.«A~ºDx}ÿñv ËS™Ï€ÆhÈÎ9Ú°ÃسÊñ$#…¬œ¯
+ÜQ&>I[*ÙJéh-‡ÂžÈ]µ¦—¥&ظ“a'?˜OÆÂŽZOGs¬ä4
+Þ°ØòöÖŠŠMƧU¦ÐlF¸:&9N‰‚ɉ’ñQÖòFLw_<'LÿŠ_9ŒR¸-z`
+„«e&³ê¡¸Ÿäe™ Üñ±!PHÀ]Ʊu}(*56‚' cùç a5›LíÙO‹Ùå‡[DqÇ©©Ìx÷w Á ñŸeCøJWr½²!…èÖ×( bGUÖ
+¡hAÚÐIPÀò=îmºÚÒXïQï_…ârLó]uШpäyWà à¥Mç˜WŒ¸/üôáÝ¢#T˜G‘KŠÅ}MÇù5EüR¨ç˜6g‡ˆ9¦‘eâÁØ–´?S5drwÁNJ©–1¨~‹.;éÒjï;—q.s™;ÿM¯ãë•æm®åý÷&=nTÉ@¡Ѷ›­„Òtáñ\8üø'µâ€>=d JÑã``èõ¸+èû7wúÜW„‰ŠC¤$ý8\$-€Q!™Ÿ ¢¨³éÕ½;Žñ{ƒ˜Rœ•Ò¨ƒYLðè˜Ä4Ÿ†›¹cxš´!\úEiºAH¶yëÈÆíWWɈׂ
+ÒØÑï·Ôò}rÔ'ÿ „‰i‰T7<ke剰ŒÔ‡žøÞ*ìÕš7ÞŠgT,GS”)äk%ýJ+Éx Ýv|OÆÒ X),b’û4H²ª!6㠬߄årzõïñù|ÀØä 
+wÌŽ'-ç1]“æ:‘Œ^Éii'àT`ëÌ ºk"€ôŽŸ=´|cFâ k¼fÙüïàÇ"+|…ècØ9ªa> õª96Ž”.s[·Í(.zeŽyìÛ¹F þ÷ ˆ7Ã5ƒ£ú´ûàŸø8;uB‘¤ÏUs»¬--LÛh¨!´‘ò–÷<LlE:ûœ8§²Na'
+JHaØ9Øoú‘õ½ÊNdšr*·v1 Q5P¡‰(î.S(…<{÷(F‡m*òx½úÅy›®÷;—V}ýf”?Ÿ¹6â
+Ô-œô6‡×ïýÜ€jBsH Sm#FhD›VÍ)pÊbƼ!ƒS¯1ú€5±l:ª|®!—ŠYm{{h|>34ZµÙ HoíÑÇ{ëÒ("ÐÉ^›ñ€`9b…*Peÿ6ãÁ'b'Rî^5™?C&Ï-eÇç¼á—ؼ´ÊVùº´þûÑâE}Ò̇sã©_iXÓÐzõÞ‚ñ5]—·TñV?¥ä” 1k
+‡^ºð+Þºy…œ™Ÿ”JrÐ½ì± d7Bɧÿ6Ì(ÜÄ„Òo_¥Yo—‰ÖùÌü¶1T}5èf¼¼têë7ë¹]0¨kX!Íf>?"¦!ž%¾`«Wؘ¨þ`þt!ÍO÷¸_¦è˜ÈŠ¨Ò‚=­¨Ëæe*_ܵ„¦.T¶LÜ%]ÖÇ4HG.ö¯ÚDc‚B¨=¨q§Ül-’J æÑî £5¹ênm‹Šk˜‡>Ž,W­(†¾!˜¦é5¿é@ì(2^J\9u´ëŒ„à,XŸq ˆÂ÷< ‚odfq<‚M˜†(k÷×úU UÜ]êÇlâ¬'>Æ©r†
+‡m_IZ§*™YØ—¡o™:¯Ý ^‰CŒÄíw0À]÷ƒ©înC†m¡ÃåîQå¡©L¡Š#üâ
+õnßËÝðÿ0ÓƒwŒZ%<ç<W’m`4™« ¤
+³Í–¥*
+P\´ÚŸdñ†A ˆÎŸ`z_ã‰~!;Ìâ '^¾s=ÐÂC5 ‡!°¹˜G®žÖEjÍüò Ch8±âŒøÌç"vÍËj¾ñY+Ár¦µ!G¤eiÉ¢Ð.^Ø”¢B¡Ðc“FÁ ¢¹~Ö
+…ðn}© 7Á†÷dWe%^Ö Ö8*ÅÚAFÐ’U' j38žz˜,ÊX)h°¯‰åG£^òBß°ÔñäJ•Ž•oŠŒ ’6¶(Øp ˆ‰÷PÒœ\ÀÅ(5îlJ£e8 £Æ bîúC; I¸x'±=ŒNYuØ_ƒg¬2óãì†ãHŽá˜x;Ô¾ûÚ»+·iDµº¡Í¦t8l$*;3XºH¬€ÌͶ`-èðŽ°{®É;åæÄðJ>èè\|€\Ýr¬ßÿ÷MO’â 7
+6ÃúÂó vÌ;,z ´.qƒLŽ¾
+ˆä(k¯ê™
+6 dùÒF°®HE
+/ów¬ç3q"ÑYFÑ='jn5ÚQ®Ã>Fp*Ädç !}
+c8çý~
+R?2÷tÎ[¼¯yëç۫쪷¹`¤ E×D«ñÛp¡>8î¨ñÂ<@îCÿ·I¾ˆé¶!5d;o‰~?€.¶yAòìÊôÃ1Fï¨tº‚@ÐÀÀé•hp~¤à¡uÅÒPW–ö¤­2ÿÂ%+wpøXåDŽw–¢c‰œ
+ªÕ§ ¥Åèä¾éky:' Fƒž" ¤Rf!B°goè•‘«ržšMèkÄŒÀÛE±–âüÜ Xñ{ )ÿ‚$´½'\8v2ĽVLÌÜŸA
++M¸½5uåGD;æ>¤×"bÀ©½ÈÍD[n3d†¿Ûè'øˆ@…Ùð¥ÿÖÏB¨­ìiå¥÷†>ɬZ¯ÇBdÊÁo`Àb3F|"Xæ\¬>ƒ³‘O¦z¾4t5µ÷_N•wò«:ú˜ß½‰òdÓÄ?¹=@h8‚OL9«þ¼^ÿ‘Ÿ‚ÔÌç°¥e7X0¢¦LíÚÄ
+í!š½+"9 ŸO 0&…Ì•Ø­u@ÜÃPÂzaUYøaV
+‚;E‡¯~oPáJþŽÀ’}R+h}ž¬¯1—WE1R hVüÅ„ŽeIÝ®JMöy½Í¶(ð'¼‹:žst+î‚;‡0—Ù©ÆOM7d–
+B}€È.Â2°Éob9Ll³|?üõ
+zÈzr9•=-¯½ÊtÌŽI$¹@ëJ°Ör!ãˆqy¥½7YöV%vïä•»¥é•ùä¿SyðµõCƒS³4Ó>yè'ÐD¼!Ì"DA­_CñH‘ñȯNTþL,wÒ'f ·ÁOÅ0¦ýcüƒ-yæ 4TlqŒa!1’`Œ,àˆ“+[MS~Ò§´°:”c/Çò°TÜ/õI5šÄ<šÚHÃ*ý)A÷o,)B]›^ð$J˜æ0<c3‘
+Õ7™fz”óü©#«B‰!ç Q6Ä-ˆ†"©nõìšAK—ßt
+F ¯ÌX©†‚Ѭcï Uˆ—1…ða»?VŸ»Ì]úW넉±mÌg§)b¨Žáú/eZFD[á’Ð(“2²€3W9WŸìò#Ãÿg=›1U"rP—žÎYÄA‹R4ÙHN¢²‘ b SÇÓÂWrïwnúó½¬K÷¡í
+.äó¬gC¡È‘Ñ¥(AXiO’áÛ:‰5 Šo{²uˆ5†r´=6Ž¯C¬áÁºÆäÁ×9»¯»î¾ÎY‹»¯s¶ëÝ×-ˆ² f†Äèùºµ1Ø‚<øºÿ3^æÈ‘Y=î
+w¶ŽFÂ-¾!Æ[[Ç`ðbÍ\L7¶Ž_c­P»03ÁÅÖ“LpóÌí­¨!+8¸š{²lÝêÉ^ÕU›Ý~Ç’ÃÖA4Ž¬Á2q |9Z’Ô¬ ø11W[#.‚ÚÔ÷¾
+J>ÕCýÝ9â4ºÍØ<Îó¥kl‚ãK¡7Üؾ0Þ™±B¼¡øù)î¹m‰L8 ér mW¹6†—Œ;c0¸P~G?üúX‘^jß÷jœç•^ɉPÑ´Ïd<½ÀòD9nSí8#ÔBÀˆ”c}Jy¤I0jÍ âi'âÁž¯±È’¨³0ôoÈšÂr@ü8!lÆv-/€¬Í¸|î%Ðó²Ú3ÿŒù‡ÇNè7)§ O;Ë€ª çÓ güÜ€¼@µ¥fcØ’ÂÊÇõ='¦&†Ö.ÕIwŒŸH÷r4ûS×s´‡¹¢}~}
+j¥²§NHv™ydE¶Œ…Î%ë°÷ËJ
+Š&x¹/1¹Íc1¾o@¼új|ªö}‡c
+ʳæÛsXiìZ§§Ö•„›‰z-·œ±6¬?üT|³‰ú6‰+×»¯4mÛ$cÊR.¡-Ž×tGâ'oH.“agü’Ç6NƒÄ+}ú« }.ÉθªÌ7'Ã
+ ¢S² bµ¶úÉèÇ:¦Ã€¬ìU0Ù®ßá¸Êdà§Ömt-d
+~ABRˆ!êì 3Jô²¨iË`Â¥,oë ÄôAzTg¶ñ 2»åT[â–X¨1jh‘á_ñŒoƒ×o¢GÎ*s±˜Äv7ëÜO N1¦séx×O`å`a¯Û…TU¡ßœn:á-4Bµó]Gý)wB¦¦Ã>ŠÃ¿Eʸý\Füøý9!m”
+jô ’nÎ9H-­ý;ƒÌF¸µÄIFlÜm·Î 3}ÿ–àÿ1M¯!êpÙî–^PiåS›)-hý ŠŸsÜ6ý ôyb0ùkÔXŒkY §¹žGë].D£šãÒŽt”¬²& æ§*¬ÑÒw@\ƒñ²Ý8å8ŸÑÂa ›ž!¾uPÓÎ7q[ƒùÙN3Æ®O)é°Œl£ €°ÿÜ„ùLþå6Ú-¢iîÑß¿*à>à ÜÏú”pÖÆpË÷ÕR,÷ó2à•2·ð·=ÿxø}UR¬ÃRXNêg‚pè¥ÈnUÛ‘AÁx¼Ã~â=^ O(H=äÙ`œÂ(…[ÆÓ¦½D6Õ´¸Ùy@h UÒô¸uNª˜"-ZèËIUŸ`J”þ"ÓÆzÖÉ¿W#Ç8!Cøpä÷ ˆÝvžÿ©3êé¾ô
+¥vù‘)¡|Æ|½OßETi |ÇV‰Úð2ûݘ,Íý
+ø[¼4üšÓ*q@ᲧÉ2s˜‹šâBKV„ùÚ+š“«"ó`ŸÓ1¤>Ç–îÛDe°,'5!ä­®ÍiÁoÛ’©`TÌlûSŒ $Q÷þÉãÅÀ—Åxàì:Ù¦ln…ü „.þ»`Ò=†¤V õÇ›â DËV±Ð9Of½.Æ ôýf¬/EÝ™fÞ¯E†³;5K~}‹=ˆ2¤áƒ(‘_‡@ì'5Ew÷ªñâ ·ý>£±¿Ê”«çÜ^gGZ2ƒ…6XkµkÞ€ìžlè„Ë3¸^Ïéc‡\öuÑÏ'8˜™Z=gÇd'¬Ûà;;&€AhñшŒ¨aeI69HrŽ™jl™€>©*kÖš#~Š/Fpt²3›q[Œ‹Â†Î1å+‘Qò&鬧“¯4t?±@ò<†]ý쟂~Go¿¦É‘ºØb6 ÐÛnh¾:2AêdÓz`½>žÛ¶Qÿ”Q r°/ù4 4 ¸
+'xTþÛWH‰µ8?†=D’rª¹LL¹Á ]F@G”½
+Æn†›‹l4^PÑïÞÃÌ0´äG¼t²ër«kq¯ÃùÔ£¯øs祜»ÊÓQ/:Qã~L÷fkãÈt˜™áµMjÐ[YE¤6•Å/#Q‚0Ƹ:‘D()öeÞB…"ÊXämDt…ç{,@][…&qÇä>!pSͨéÓ—–_k´"U÷¢X|ƒyL…T¹1â‡}ˆH[ÇnÞ±´!íÃD\+q˜ þ-ïÙÁàÙ;R›ª aU–þÇÁ}¼Ù §¶› ¨MŒ®cj±]ëÂ-’icߤD©¢$ÜÏÊð+¼²‰³‡ÛuéÁJØ™ð¨0ä³;'„ºE'³Y+2ÖÓ1BÚ‘0–4B™A'ÈvlV$ÝÈÛçœæʺÍe<7]§üR›ë®œëk­ÜS›¾^ÙK­°Ÿ+\œWì¾MþúÆË$IŽ\¢WÑ Ò8€Óúou m¥ûoÿsYÊD«¬MmÝ%Ž‡Æb†H-¾Eh[âF›àÈ’”±B¢V’ãO1ÛŠèü˜ÚÃ/q›\Åóé©í/o¤\ÖâØ=«$ $^b°gl»!èg¾MëSY!I›ˆß¾þwȾˆoÑBiZÿp™M‰ËáÐ3@
+¸}n6Öý.:ÕEŸ— ç]Žäó9Êõrí2E=‘@ädºÔZ<§¥ˆµ1jb}úšK5/¯ÚW}Š"ƒó[*lj9ýðå7*“4³îÊ[ý»âÿ%ã¡•èJcÄÅèuÐ7ˆFdyLX<,®NŒPx¶Ù±:þ“$·e*¨£)qëÜq– L¡&ðvÛ‚¶PÌV¥öÇ^P§HÉÒ:‡¯Ä«™"Ñ\“"j5ŠÒÚƒÙIP0ÔÏéþ1õ5tkdœ}rgVó®ò zcž“h@ƒ*¢çF.p ˆLõ ¨ˆôú¡-E#hbU(tUåWM¸’=Uƒ¯Ø\é ÕÏ
+–\ëº q0¯QK Íß«$g¼X{nÔ¶ã[⥽sln§¹¯bôÓåI+„õ„eò×½,fþØW´u•10 ƒC)uwdÒ(¥qŸƒõ –°µªÆÑÑ Jõ"0D®4>  J«š÷˜6æ„I{ÉO× /A¿wÐ"¡¶£@ÚÊЄú{·Þ_T½s†„I§§Ù©ž¦<³Bðeú4°åtš¬õM²èž’–U&ñrTÀ›¯)“ øo²ô„£ºžjvi³ÎìÁ;Dpà8€àÔ« /f’QzÌb±“ü ”¡ïð²S‰)yVÚ;#I0õŒiµû½uÁÿÈöf-#ã觗ZÒÖÇÙ̘½ÁÊ×iï[¤øM4×£;Óí—ã ¢¾pP‹Ýâ¡*¶gŒÙ˜Á¾¹ýË#e§Ó+X–:ÚéÁöXƒ”¼3ì%/°»`Ekˆ÷Á¦ÜT5»Œüظ…Ì
+ôžö¾;q‘ÓIá<?Ûz¬I[*m}=~Ș&= _ Ë ʃÄ>¢cX_ÃÌHŸÎÑš—µ –ÃÍä>Ö¿VDEäL0&cÞÌ@2ÁÔ¯vnúoé|áœÉá£ú,<áœÑ¨Ú,˜íÎyA7œ³ÙòXø€Ÿ8GïSËÑšÉÅ9Bð=„Œ9ªÿµ.bÂC¹­çLžÅÏy÷xOÚO #
+‘ÂxÓÎD¿[­è'<žÃhz¶Á˜\ž#P£çä§fWž#ÃÒ§Ü|òœ
+Ú°¾ÊzrP÷Âsÿ¬ùÏqac™,Ÿ1•žÓ§ÓôI‹œoyÜÈJÔù__]ž#„ïàvžx΢¼L“€L—çò„üÔæòÜ=Äá9/èÆs÷ Ïé“£ô‹Ës<%•ß⟣Õ+Ï)ƒLÏÀ'Žž\žSYG±ÿù<'=ܘ±'œ»ö±wŒª‡š ÉÇ9+ßH\‰ùæˆÉZ^@¡«Ks„0z«ðEÌàáÜ=Äá9/èt÷¾p€n P ‹
+>ЩKq"„j>Щe˜Ù4<¼öt¡"D4 ] #n¡ÜøZ-.ˆÉæ¨ “9÷øt*:ÛHGø@GH5õ'ßtÀðt„ ÇŠ¿ƒúö
+XOÙÏNè”
+dw·˜!&''ëã¬9!zx†Š™ªû~ØbˆHü£$ŒóÁÊe(¦1N…ÓÔ}û ³Ún­õ<~r˜i”¬F3<Iùä6Åõºo]ÕÓû-è ²syi^ ‚:ç® 8F_ô†ëÇÙø÷ ¼<¯e v:!½kÄgÉÉué\_l§F4ñÁí b«€Áå|¾ÛçÆüÆØ’†x>µcÇÄÙ™ým‡0ƒ‘­,wËÌ„ $f•— ÆçЋ¬j{:wí/ôj´µ¢ÞƒXÐphtYèy_sS»†r‡Pé´0J_O—‹7)á°BDây¶kËçéb\HÛŒMâµJ¸Ö×M•9MUÃ<æ_IÆ jÆØÓZ@PÔWÚÃZ@=É•ôBÞÍý*Š!(§<½©`‘©©ZôSsÓÖ=ÁŽF?
+õë;BþùÞ2ø¹ó°ú…eŸ÷Ç ºñ3!P
+õ“k²SE¿µ¯¯ˆÚrEŽµdƒãùp@…3)µÅÂçBàÄØvs‘2ÊÜÕèrïµî.?4¸«çœêŸÓ°` 3KòÙÿšÆ j&jÔÕ•Qd,lnjú+·ŒA1ÅŽ|l'¡¹57D‹˜)ŸBØêb'è£î¿Ý ¶^ékÄÖCZwcI H:×Q)´OjÀ3¼Øè¾=âdI¸(‘vŠÉ߀¯[ÒÚyÈβ ¶Ø>8~&nÏìf•Þ D¼¶‚¯Œÿœ‹¶¼¯¹IÔ{ÕMé÷ìÜúå–b§í>*õë;½9GWIã… ÈzYùgA/#²öm¨™5ÖÎiS‘Ž¦ï˜à7Á6¡^œEHËŒ;žŠ~ü¶ /9š5ýñBæÛ‚¶È‹ >šTW  @K§ý0êà‡a+$Ñ%@%}¼C£•¼Çh|ñr¸?Å$Áûç BЖdŸN¿~M È=Ì¥M¦âš”»Œ¸ñ…\l{–h^jÎcñe…
+ùïÒð< ¹CäNòãÔï„óý2R¢1ó¼:Œ·îƒvñˆ–ãCóΚ†µÆãäV¸‹Æ"<ÆLáàdú4¹—
+H‰Œ—ÍŽ6„Ÿ`ÞaÎÒE‘ ç˜ë>A°{rŽûþûQT{‘nÍƆÇÀ´«õC«JÖÇÃç\Ñ£5ýüµ7yX—&*æú)–‹f15¤kµ5øŸÍÏoÿþhŸ¿}€´ÇX½7÷5e$t<¢15M×çŸ{¹%ª£ëT_Іô®+Dà 2{ ‰Õ=¢hZ»ÌèQu ¶øøó 2ËC[Ÿûr|KmH÷&ÍÎ:jmDØÜeCÔ»õ>ÝÛŠ+¤?\ÜdŠšràowP—°>ל.ýóû$A”])ˆ×f¶¯9‡´]ÂöXª3¦›GÓ‚,·¶¤wõ"£µe³IÝœ
+¶ ´ ×üU"}†²Š×F”´iŸ25ï°!“RvŸl´6Ä
+f›mèñGµ<œ Fä îË8—lÐe锳S,I¦Lç
+bü¡ J.gÓéúZÊŒÜ@»£
+sVÒXd¶FË'‹Û8ÄXПµQFƒÃ2ø¬{:ƒê —¯9—µFÎ ”òª²sž°<’Ì#ƒù†AËÜúa˜/ø`¨Š„¹¦Á¸uhê“]š5?;Á4Aé8Ò8Eö‡8ÂÄßV Å‘u 0´¬*¸›ÅUš}¡o§“)©{Л@
+[”é(¯ˆ‡q F`Ëiç)Uy¸(5åW>U¤¨wˆh¬w
+
+ÓTÁ_ì+¹ìušŽD|µNh(…—ç­ZŽÉÈÊ
+];ͽ “àaô¡Wýœve`Ê~ÜœTµûÂ]ªWPOQX`R„el­ +ˆX‚‚hµÖP'¡4C):ª™ŽÚ]ÏÕ/ FdŽ‡ÑèºúÖ¦ú¢É£&1 ¯†çœí#3φS¢â0eœ«£½cáG}s?pjíÕ6ýyuK[ÔT­ä‹Z tÆa}­ÓW|Nš÷‚dµÔ"ÝnWùµ¡@èiBGÒöÛÇ…m’«È±Îé±0@J˜&:¨l†BÃÐW¶ûÑtèIQ-;ë-æ™KŸâ¬ŒM”jö³J€Q2¦ â9Y9 ³?æÂÈ9 ĤjÕP¥/Pbí!ÁÒfCF†ŸÑK[LVh¡zìPrÝœýÇ#}'U}€æiÿŒÛ:óI»GÏØÞN«Væ@:Á”÷Ò“–;'Xü4|j:Ö4*¦è ˜Ô…•­@+Ÿ(©g•á"zO&!³6U½B^°\Ù1ô…´’ŽQGž|÷S^ëè$ŽJM?,í饩+™mvÏó‰@o¹FD-D§]Ä
+’;¸?î32xÅâ€
+VrÎ3/&}ãÏOƒØ gD»x*È› ˆAÇF ü\ã(µ Û˜;f¦¦P ÈDøª±Ár3Äð Š’F $àMÞŽñ4¤t:%zòÍC±í¯8AkRëLe’“§ëI ÔfM?mÉ&Tçr¶â½ÊÛ‚ ÓÖWëà¨ìÄZqN,é§ø¡”\@Až‰^Û~Ÿä½-Ÿ\IÄ~Tù tëDC>™8ž6]çÏ‚^ÇâûÏÌÎï{Ä,u3Œu!”§¨,¹ÿŸ_€Èh<Æ&ÔÓ,ABøoƒ GÓnñä´ÎYÏ0E"\Gâ¡Rky5î¸Û†·Y8u]z„—¡0žŠèD-Èt¬°@°IÄ[ÚVaí8-}O=‡ì­PH¥ý<wnë2Sv1½~? ~Çûy?y½¥'µ1Lø]¿1Æ5É9¸­ÝK|ü¥Ug«7PîœÊÜóy?ÍÚéŸZø–Ÿ÷[·"Ù ??
+˜™IÆ>ž³oE€I«¬u0gÝ£Ô Ô§:ðêœrmÕ ä°â¥åïë¼çý0¯ü»ÝéBã×Ú\†áµÂ7TeoÙ´‰pŒuÇeØŸ³‡õg´5ýJåß@ïÃ~Ùî&{¦-Ÿ¿%Ôƒ%`î³L|dR@q(r¯ã_@dCIͧU»/@ø?'`óKÅ^ÊO“ø„W éŒ,: ‘¡‰Så °Œ 4$ö­ŒrwDšaYµÌÈ'J ‚Ûy/ååÈ&Sý éΓÉ.½ÙYpbËd§÷u†lÞ–kp"ï„°Ðm$
+D#þ,²ôJ~Ê5ÞAXKCƒj¤¹Ó´·Í°(œFá#ä¶ÚŒIÈw"vmWŸË«{&#\Þ §ÊùÐqr¤šµŽBöœ—1Ÿ~ !üPníäNAñF*ÿÜç…™6H<9áYå…ZBñúÎ?ZƒxaÑ›Á$¾ÆúßVÿ—ÇIùV”§Ïâ
+¯‰qYïÏ+ˆ£ºù½!H›òJôæÙ … 9§Š„(úGac}!p
+ÇL¦ñÕ$úÑ¡A1 ‘>…c`šxШ€Ìwx²ÿÂàñ© {­äçrú•é˜×Þ`‡¯ÖÁÛ싱<§Áãx‹Ò0šTìaœÐÕ8‡!†Iãý) Aȧ]“³Sú4Leu‹Cf4£å
+~°oÆ9õ.4ÍÜÿ´Î%AÙzÄ/Éá¨
+K \Üfñw¡a½î)ðÙ¿0/\ó{×èfUœØшŽ„wv`ž•èüÓØ®²§ÀÀG$®)!î¨,Éâçng¬gƒkýÑi0Ñ'ðSÀw׈ 2á¡Ý Ý'—¢tÃJdRb
+LÙ€vJOÕ)9»Çž%´°âŸsñ;%t›ûD@³õ¬ â|ªÛ³;íf+PÉtšDƒZ)vå «d ìê@Õ*o…³“[BË' c‡d*ßH–Û£¡UµGÚäÑ+}QÔ&©tvâL¦[€Jg»„ycµR@Sl`?'Ãò¡
+C±òeBÂnàuJYÜÅÞ#IF&MtÖ[>0Ïܳkƒì ƒ± KcغˆûÑÀ~73, ’Aòßž‚Èq ¹ñ¹í(QûˆÄâ?‘+Ë€ñ…iž,ÒHý]¼kžñõEôûÄÇᇜȤØ`ÖjÏÁ KQf7”úÉeЕ^¡ÃnÊ ^¼[‰9 ŒÇ¬^H\_RËðb0={wNCRaž‹ÛÜË í;+<J÷(¹^|o‚Y·
+ßÍ¡d2ÂÜr[Äå´Ø)¹ SôÌ'Îmž)çz•—–è€8Ä1ù¡‹GÉ™:ýèvÍÊ‹€Ï‘ßÇ\¸¹/sáÏ{”ƒãgsîexvø›•²
+5„ÇsL§C@”+ƒôÞŽËï@‹NÏf"ò…Ò|öãéÎ1’þ“§x%ûSºN£s(Í»s´ ¶¥zžÅ†+œ3–q]xTlàÁYJ%U=köÿ´p¶RW©BH((¤–t4EG(ƒ¦íÔueZÂH]©®ÀXdó¸
+ïм0E+i9åü׫š„¡$nÄRÓµ‡PZÈ&à丂âQ]𸋢~
+p7Æ.q…9DZ˜ ×É~o.lÝv0úeR?²06æók’—;‚wÞ·ˆ«7"ŸžÒVr-XR<D&Äå2õ1ò¹
+ã,ܘ.õPP¨z'C™þR4Rpi¦œ“ÿÄÿŽùÙì
+ M§Cq©ãš¼GA ¨vþÛ gøçtÌ@”pƒÖæߦ3y2+˜)¾{T¦Ðþªú½é颣#@D‰‰ûcyÏ—%“ýë/¿èËä»EØ~¾Ä&0<öÇ Xf<,×'î>*ºÖn/‡÷Æ T0 ,wŽзór¬óR±D„=iY>h6#„:‰²dNvÊ D7YÞ±j¡RivñOy ˹ÉPïEÐïÎ\ër÷×Y»/súøÉnn‹”ç‹U[Å;Äp7Ìfm°W³6ˆØ†4jƒU2dÖ›rV¯M ZÂÛŽ±K8P¬‚7ÁpWÐÃ$uûS“(Ã9Zœ]Ä1mLéÜ{ÅÇé9ÖË4@%„´#2¿¶K,\Ï8¹àKÉjP
+¹5T™ÕRÿþ~æ¿>ƒZÎùi›¥Þ†ðϾ&«Ä*«æ™F*t†'Ï3ä3ðJÓ3¨¨ =h3|®iViTPjX'5Úš¦@l œ°´,b(ûœ —ŠôÃ1å3 ŽÔ£[’¸?þ»óhY À+Šä•ŒAGËóUtº¼2ä`™S>Þ +)•¢?µûTÁ‹¨Éÿûn&+Š5B+qÒ
+ËÄèÅØ_bì™gB®µùAL`‚emñM »ÖØ9g¼4l>÷Ù§@þéòã]ÂÔÐf²ÙySÄ×GZL‡ö—–gŒÞÍzÖ†²œ¢¿ºuaÈeæîpÂňÜFß&ä*I¼r\¥ŒgÝãtŠÐWTª ¤©m{nÅ£æ·ÕÌ#¤’©±Â[:…y¯”œ@ɦéµf¡¯¾GÞd°y9Œe ­ú
+—Û¾aÈíp“–¨2dôßòŸ¥smm¬¦NAØÜ^¨>ièÏ´Þ„W¨Y76‹Yü9dÝâÚˆ
+Óß`ܺ&÷]²Xí‰ÀÅ\F÷ω\ôw±¶1
+jĸZv ”#&™ȂZð–æs‚&™7¿˜±øç$æWÑ=öä¤Ôçmò+ŽvéÝ«
+‰©à¤(Io^%ËÌ! ýÀØ‹ß²é5m¸¤¯p¥cR€I•—Q³~ýåÕ
+çJgÍ«ˆCŠ˜²&ß[%=&ŒØ˜…ø%é¸=›(«'VDIœCæFúZßÅÀlR4o5a S <lCPÁOp—´q]5Þ$ôP¶]>æÇ5ø`Ä~À¨Ðë<kü¬ÁÄ)µý»¡èéi„
+¡}Í‹“ÌÔt›ÆáÎvÜ:Ë'×ëå“•¨Ê>Vc Ä©­Ò=œ%j È?Nsè¯xÞç²ìDo€§D¢ŒÎ…T]xB
+Ý6êTƒ0aÌZ0¦} Á›H[—óI4‡Þp_.˜¬d5‡ ¤«d}IŠSûüx9AL.*&/ÂÎ!°lw²s;l<©ÂšŒ¾uÅ›„$ÿ`ÆŒéTòÃæ9¡,2B°X±CÅ@Xq“Ql}º %(&Ì ^%Û£„ZÓy—xjÎ ·ŸJ~ŸÔ¯
+øæ´’®Å>j4èìjC·ñíº9Ƙ¿Æ4-‘éŠ7€¦¢ÕÓaˆAB+«Ü­=þV⺓a;E&X<²aÇËPÒ&þ7IuÓãÊÈ”å H%Çæ4),®¦Ë€p‰4€ÄŽ%L
+’Ã7âPŠ­ñ¡æÓàßEØ5JpjtL™jݘïf·qV/n’Ë`+íå=òS¹D+ÁÿñLäÒ¶—+¬à‰a`4×0(sÛñƒ iY«g+Áa%çÔ3YÞJ>¶kŠÖ Ÿy<g×v›¤‡Wí`ߺ³¯ÌÖãÓæ}šÕ/¬çJ’¶ÈîžКñj~“ÉŒ iÞòó¡HV±š¢U@sA#8g6¢¦,˜ÏV’™©¢MÌÑJø!à„Ñœád‡1ÇÌtPÏ”Iïõ*@ž³“
+y¶Š0K5ƨoÝšÕ9N%ëC•gŒ<v¶§sð"À
+Ò¨ÉXÑ•˜ihaâÕÊ«SÓ/­&PÄ['•^¾c ©±öØ<;‡¿e´e†Ï "¾Ì2:–û)cø—"b£2íÇc¯æO 㺠ˆá† {¸;„KšyžŒ´ÓíK8.6KÊÌ®ÞЕDÖŒ.‡ ŠâWðw~c®§ÎŽ5zýÂÌ¿ý¢éÈù,bB”úÿ/“ä8n Šž@wðÜ!1­µÕ-¸%ï¿õû€Yf‡#’ø‰røùÒóªy«q-: mQäN›Ã]‚ïégÔ1b¸Å%áÝ<$s±5is™r2íwz¿±¬!Áa¹g^n_p ¦&œ£H#1|³
+ŽÔc0q*Fi|6-Ë<ø3n!àD;ñi@<D'nK¤]ª|W²•Uö&VœÙ5§‘QÊFà½{kÓ›ÛÅç9I+*µ®Âˆ ˜€Ñ¦‚ÂÅ©º±$PI,¿´Æ`fVÊža‘ˆ¢¤=k´š™Ü‹r[Ú@K!~©W‚
+£®0Ú9=¢$âú\sº×ÑO¢÷-TÖÀ;EÖŠ)»l.cfWST›­òÓZ,Ã(aIèO~H
+ëSš€!$¤ÛSàgÍ,® Ø
+ÁŒdâ×}ŸÌ‘?svxn½\îQ:¿µq-
+îé´œÃ
+2]9jð¾¹ý䑲ӫϱz„#õ™ ‚Ê·iZþ7—<ësc3Ò¶÷)Rm•|Ù¥#º$AFÔ1–¾æMQ%ÐRIÒd;b´ÑÖuzk"º8ÎÖ9EˆC’Æ5­{µŽÁPü=ðCLãeÑ ‡|­c„¢ŸíVC 6–0êuù·MÚ“IÚÒ‡J<·n‹²HI³
+!Szš†Nq}I£CaD¨É“¼ÍOQeL\­%Ñ?gh×ËÂØ6:ÓÕtTW}N &íc˽#ï&Xä¤îÙ÷¿õóÅt¯hÁã7Lg4,;¾¤6¾a:Ot0D,+5Òû!¹L‡/©øhëy×ãw¦C‚÷R¿ä§µ¦C³í©Š£ËtHÀü—e. ÆðëÉ4]c»:‚Èüª4~M#ûç¨ôX¯ƒm—êpyúȦ¨C„²Î‚™©¸P§ŒVA,fÛÂÓý˜ó/¦3Ýr:HøpÃtº–Š •µÌ¾‡„e¬íxĶØïPg ñÍŸquáBܸ[ñ™.Ð0Š#ú“ O¦;$Ó9¢ƒéÍÉt‚±Jžµ‚F—邶ZZ—Ç|ºÔ3Ó™8@_Æ¢Kt™NYíT ‹á» ßN•BU ÛK}|ž£z¡kr}ëB¥ôõ±Iõëu ê‚j×Á:3HŸíȘëNɉu§ÆÁº³-¬“ˆüSnAéó°Ní€Í´ÑôëÔ궭zƒurÚŸñw?<Q©
+™¿ÒzÝ#¶åp„@ßõ¨SƛȆ°¶áŸ€g}Ý¥:$ÌIbß‘9¨ÎÔifUÆ]Æ Õ!Ò
+Bʘëì@ýò==ª¦tTv«U¹"oB†ùò'ÅÛÌgLšqåào2¹áçB,¬ 7²G"ø|,S‚SÁ!Œ„ØWUÐå (Yèt„SäÕ`»¢ƒ÷W5NSßúYst©Wï‡è °3C„‚ÆõÒ,WfÙ¡d¨­n³éŸ58'pHN™×CÁ ³îߢ‘0L‡‰ñ£'¹ /uhh¤Å˜ç1åÜÙHö4_.30WZÒÙ’žPók0^áy:#1ËÌ+"õŽÛ‚L‚ Á»ÍfM¨%š#N¡iÔÒ¶‹²Å4Zšz”. S¶äúÒæãóáÖ°•<lƒsê…>"Üiž=]ÐÕêvÃï¯ósÂŒ)x ÿ6³ÉþfZ) Å»sØHiÖ:3ÿ6d\› §ÔM>ç«(h®o%ûÁ9Ëï9§âï<½½PÃ>÷…œôÁx,mœ§}¸šƒš3· C‰ËïÅL[aáy:]~h¸A-ÍÊêwý©ö2´ÆÌ‹Cž¸ë¾±¹Xëðbóù\Ò8ªY.îžYw°™ê±Jú(!ëÕ?Hဵâߦò/\›”—}áóUz¬: æh~tê,dEhqÇxx±×êJ&²Ã­)=›ØÑ|Ëü»+²Ö%eS™ßBMºÙâö6_&;áHëåð
+ Ý·ŽS»¼™ñ™v¹]åÕeµ– Žb¢S4Ôf±¥®Ÿ³=ú£ëpJoª.Z¬5zsÒ«®ó6G•z¯:ªýŒÎÑ3gŒÖû–«·úóš[°ðƒ lÓ¦Ï9ù15 [­ ÕÂZI¿ôQ&"êYñ«=ì&Õu¡kÀá2¨Ô!¶)f èãö ’ÚÕ$B*O¢—%¸°òŠöl>žÓ¢ƒäšvÿÄQ#Ëd›’†íx»ÕÕ7C<úÍ †×̓yƒÃ»ç ;E:²ïVvƒ dàçAÖwç*쀩²~ÐÅ)•s—ïˆ7Ù÷B™]
+ê6j=±¸Ü”²àrA(g«"”ÑÌœ'Í£M8âÎ4MtF}]Ä–Õhˆ!˜Ü£ßqm>i²’V€PôŠ¯Œ5y§è‹Ñ9=,<˜câÊ×…$
+¶.<MËÇ_Ú×@öNÑ—¼Îáˆ,ÞÆ—£RðµV¦£’Qz2ÙÔ£#YŸÂ ¶âRùî®Ê‹Øw×—Tj9T:½Ì„+I9ŸÉK!^çÆÌ*å÷/Oä$B}CÖPµ.½,"[¸æ”uõÙÇŽ&ƒö‹L¢¬& üebm^¡ÓF†ÙL „Ñ$¥Þd ‘±]ñ­HÔÜl!ÉaP7Z†ªe$\/èŠÜó&[r§®ÍŒa[oÎaˉr )ï+Ù3€A?%tKZ„òšÆÄòØÖ~wŠ¼T0aô D—½¼½ úîÍï/ø÷×v’2d§C‹ì’p» 6X4.§7­ú.ðDnDj‚Wqvfþû1È#suåØ ‚y`Ž—Gƒ½$…V-¹­u°”lLbfléS’èC¬¾nLWg0‹(¢¸R«éͪ-®š_E,-F;&•žûD‡?Ðg¹Çê(Þf™xm&ÊåîWäÜ÷ejÒŒ§¸Ê”äĬVs”e‡Æ\ìð–Ôö§&Ð,P‘»ÒˆJ©È<ÏÝ(<³Ý<Ý£ŠõH¶õsƲ<JŠ(r¯Lú˜¢bb62L|ÜJ$;dz’£ mFm°Úõ%ÉC°Å³ ¨^Þ½pü|~bì2ôxÓoŠ@‰¬ÝµôzEšq™»~÷È“à*‹]fš+á™x6ź”e¯Ž¨ðäO`0‹ùÐkŒnLjfô´]¨$Ñ>¥×à ÀÚÃîÔ–ñÑrí¡e?Ð £­nBʯ äù!’
+y]9¾ÁÕrnF ‚cw¸qvñyŽ=(e-øÙ^©(HFC¾®“…ùQ8ëÂM^L} Úm~ªÐ×8¿xÃVû…aDthÏc(KÌ#ŒÏI@{àÌW&ˆ+¡Õ`µ[ÝÖ{hØf)DŠ‚èeÚ×sW<iÞ—F-¨¤¦ÈÚE”)…9.h&2"L(s 4x23ÿž–7Á˜"<ôãåŽÈ­DÑhŠüBC'õ
+ gšÐû÷¹¬¢ÞS7{,ÀÀһȪºCˆ”é,˜Æ'§ˆ :ù H€bàóá§te´µt‡à£Pm–¡§O~£ $>w€ì†›ÒÓ9tHå¢`Ñ«‰‹Ã ­X"ŠLis@:K£ªiî6(Ôvy K €X³qMVžXáýÌÁp艆ÓLˆrøp‘(Xܶ!˜|TU£dÕð¢tB£¢¸cYg©0ö¡ô}ñ†õ }JŽ\kñI^?[§"ñ*?ý¿A‘ï–§"z¼€¬¬ó
+eí%C‘br9bH‚HnÜÚu¼HœŒôU”hŸ\Ít5#ëk 7ÇïsêéC_pÉŸi©‹™ïBÞï#«É7•½67•È8óÓ߇00‰’Ĉ†ãÛT]; [ÚSêdzGºËñ!¶ÒõD‚£è’="x›9¨'¤ÂÆ™X“üê/Ëf÷¶?ôËÙ‘—ûíO$û÷¿_~û‡?hÁ²$9.†‰Y#Áà0ÉOõó‚ð_(îZZ
+$²½´ŸUžÊ«â²°<c÷xHe«¾j‹UÅÒ³‰$»lê4 ës mä`.0øîu\´¤BnÈ7HÃAÓï*-4`r0ÀA;ûÊ_)9AŒö)*ä•°cÐý0Z‚"wÍŠèŠ#’5uŸ…cꀓAhkW&«+qèSteÕBcó‰7\RRXç”&¡³üpÍc[Nu*ÖŽP½hš _2z=¬.Ãé¿:gð#\ç’S 4¦ñw´Ò_™mZ£6Z¨ûê³!°‘cƒƒê]UÍëÙn¾ Ý/ZŒ’/u?Ù†ÀÈàkŠ>:¼=§U‚÷—3Hþ)vÖ so7¿˜ÚV$Ê}jñ¬â.Qëò%‚Ä7Q™D%ÃE~ó!R2þRÀßp5Ö‡™Êûæ¬*ŽŠUŒJM)å;OEqÚ赉óM”€f0okYoý"*†Kc“­y9LF ôÊ÷Ðx2YÞM/üu‚nCuÏ À€ŸMW}“NÙ!Ò<‰ŒÞt¶“ÅkŒnõ¤Æk“M_-v"ò>sD/Z|„÷@R{¶–fÈchm,-Í7­^…µ`èÛ× »Àƒ•éˆÐRçRÛçÓŠ*&Y>lvŸ ßlyAéá°~FÈçÂĵa”ø0´To¹¼³Ìdø$KˆÉ4÷0‹ŽD
+tNeA
+3ÓŒO ÑçÇB¸J
+h” ¯^ž@Eá/3“cµó"ãp&§)øZÉ䀻ðag‹‚Ø*ôå‰uuÖª°[ie)íUZ2Jm_]ÄÁeEØÒhUÅÐÖ,Šï ñ³£­V
+ŠÇ~^ >:ˆêùmlFà?&É&–NúÈ>ÆpfÅ¡cÑ ’n$ ¶NzS<zƒ̤P_W·8cðzÚ-"—FRfðmø²u ‘aKÕð[3ïË{}<€ð«ËÝA É7öëâ©´uqhõ:ºœJØ7¬r߽܌©êClìŽ
+Œ¸!œŒâàíøx¦åñm–¼Bß,Jt€#èvòõÄ}ˆ¯&4 ÿB!?“é+R;rÛµ`„†ì@Út‚ñŒåÐÿžšR`Ãhx®0üFQŽgœÍIéQ _mÖ» YÉn;§÷È<tÚIö
+:G!œÎùIm´øù¤G°´ý9eÖ[ÃÞOïwç|C•2VÝý<f”ÁI˜·Ü÷—ÊNÖg“(¼ƒÛD~sMvN[^§¶ýUêþK2MYk“Ïdua*÷&¨:å¢;¼ØRֈĄ!Î25;‚Hz1>av 2Ú=°H‚mŠA'8"3]€–‹³_0P‘%€Q¢û© ÊlúÛEüƒŸÀà¸ˆÊ <ïઠÿ€nà’H‘óñO5 l“jy:ÈX9Ä*=U3%Ðòó£=ÝjÒ@~ª°µA—ÇáÛˆ>]C Çù…/ò/}áÿDé²£ù1_k‘„ÑÛLNk§;Ű¸ËºŸomµì¨ñqàNBïîbçHޚƠ+»!?-טOºBüK_~<çëØŠ¹ŽßñR×)><Îuî/ü_+å›G#ðž™úrÝÆm(/)’ñ“½y(—Ž Ëôã‚Ï ´•°0?ŽŸ»€>¾SÓ+½½ÉäÁ…â|*_§s
+®e»Î{éWo…ˆåLHHÓ@×Ï]Aß©i•þú‡   Óiì‰=at&U¤­ú'Áƈ»SZÎA€Dª ³´
+ٌ¯/ÍàW;H!ÊŒxåaìÊ즱Kd‚œC_Ž¥"²] °õ)
+D¼ˆªX‚Çs¨xòŒe?GéŒ×–nUƒ`p
+jŽ-ísôþr!—ìY!¬g·]äÐKƽ±:ý(cvõeÄ`ÊyCe^8Bz>çÖt—ÉA‰2QŽýT~d÷oƒtìO¨i®N»zHF ~+¦K²õá>
+e6fì+wS<»NÖįþ<‚ÈT9µC ~]ÎZáq(û±{‚ve—r6 Õ›a¹wÂcžƒ(2ïQ,+ÐÉì‡à}¨n‰‘¢J´ƒÁ–‹ÄyS-ÃYsˆdò!¶j>o—sM¼ŸÃ³:p%Í[iÂÎï©G?€õ”Â>JÆs“úÔ‹D3ç {ç…·I°ÅÞeO߉ñºdß) ‘s˜œBª{NiªôÑÕ»—¤·mÃ×Lyvåœ:ïí8¤Î{[ïç܆Cæ›gÎ’æTŽC†%æÝ m \tíá¥÷Ed¼Qü¨fç¨Ø!£_¦‡A¢Ëþ¼ž÷&¯6;ˆû;¿^.“Í‚¯ª|‰»çàÆ^1‹gÛA@Ü“öˆ0öb@[ØTòI—B3!"¾7æq KÃZbbÖÑéw&£ÄøtNà&u6šï-0[¡t5«´uyø ›VpuBR•çF@ V„¢q)¾=L
+dÆ?}ù[ Eö¨s¿é!lf$Mš6—âž ¬GhØmª{9A^¨Èæ%=œ<¾ âc ’†íg6Ó±0S E÷ÈGw±Ä#Õ´¬)ÉhÍàpáYÛ®Sy!ë'.´&ýgͺ@þ²¹@·e*Ióé”yÅS'*¸öiI¯x0ÂGÜÚ$½(Æò¦'ÐÉOâŒ1B¤%4ôû ÕH‹™ªøgùð;èã;»ó?{WØz~LÖlC “,¨ÐÖìÂßÁI ­#_DâÇþÌÓø=jÀ¢pÁXÝE‘oFÀ²·¡©²ES®ÁýTM6ˆ¥nv‹Œ0âR:º²‚öÃ&8»¤ÚÎ5‰J§O笒©(M?‚`6ð3¼¹A2‚Óœq¸G,%Õ8ÄœÙ'äbŒ:ôŒXäêãåô-¦V Bvfz9D78››ËÑ”éæÐ:CýËx™$W’ã@ôu‡<,8€Ã:·u‹Ü*ï¿íç$¨êÿaR[wV—b€Ã‡ù`îÔÁ!‰c¾Wl53e´\ãÛÜæùú(ÿÒ&øƒ¢°õ¿"dU&Nl€‹³Â’7½øóÿgî*‘´ù†¨–sw/âê­äfìþB¼},üîiî€áÞ–±´!í#œÀ3­y§Í ;~
+yò“¿E$[ÔFgylJø­,;
+/yÆÐDSu—tÌXå!Å|è J
+,ŒòäˆWO& ÏN“¯®Þ›úV¯^tÎ+º¢Ë¼4zÓ ìô^ç'ü^Oá^Ñá„!Ë„£%ùQÅË âÍC‰¡¸žïånž} (×%ƒjKÐî‚Æ+(fÖŸÅ›‡-@ôI`D•jáæa.½ü‰&×pPo%ñæÝÏyGÍý27ðEo
+@üÞ›`Þ;üÝJýþÊZ_À€¡{C·>d’ÐGsmŸ¾€uÍÒAÑ¿E—x‡Æ%"Þ23*¢ß¨á˜Ú°Ce*)b:~­
++*É’fÖéJa¶KêÀÊ`ÈÈKNse0Fïù@Þ>
+‹Ï%k?I #À”1ºãêûNÀ›K¦¼‚ èiÝJT t•æH£‚’?»…œ‰ÿ‘å‰! $u
+CÚë¹ òÊ›°å^bÄ,ÆU@¬¿ ÷ÈL Fè|¨
+vÂâð/ñK©`Aê§1wä8%ô„mœuزpßNü÷W,áùxÑ\¹‡µÈäÿõ[Mü0¹
+F]ÂNÇçLW 8™–1™†Åì
+\)¬G”Û…fÖ³®ÝìQ\%m íÕbÑ1T <
+!¬=f'ZZVžaõ‰åÒÇœ ?78=Ççà;H¯ºQäq
+»Ð11ÂWIƒf
+†â؃¡­ÇÊ [}ãýVÄ" 0*Ïv7õpi(ׇ¡,?.Ê4•mäH¯óSPD÷å
+ yvøV„öân²®–÷‚BD(iÏ›\ Ž§cúrê»BäÖP›lá¤T^à ¬Lì„)0XŒv˜
+®¯LÆö‡A‰”d”e_Z<pÈmpbßóAñø ÌÂ@iWà-ps2Ç>nœ—<«Ÿ÷¤îEÑP´¤WS?*z£äÏŸðö¿_*—ú45Öažnù)ÿ¯y ¢³T+¢’f
+Šø¼‡idÊ0æ%˜XŸ5îm—ðq´mpÉ WI·™ÔØ&‹"¡ w€½p Ä šôÖðsŠrˆ'¹9ÂR·*ÁáŠ^‚Ý1‘ê¦oüŠÍçVö{¶60 ´êýæ]#2íËüVó¹ÛCˆr%—Q^ŠV ­g˜âù²K„3Ãÿ¶kEŠ,côW<À.y³sö§*Q‰Ðaž_"r¡2eîSp!4N8%°&#€Õ+@8î´$`;ÎwnE“œ›PÀ.íÝ÷…ä Cý#Åwàú/»Ñg1dm®Huþz <!÷Ø¿¨ ª¨XüšcÒ
+7Õ-OT„L¡‘87XÏùK,€²N‹ü
+¢i
+< < ÇŠ(~ÿÔ õ
+„P/£òaR3QÆ-7¼U*Ú‰póý©¾Ì¶l¦ó®˜fvÄŸ]Â'9læ#uÚä‘ü:÷¢ÿ¡ÏÕ²Vì Ži^I·ž#p`½hhÇÅ–>RY1flë{iâd…0v©;Ä`AÔ
+C¦J 53>WæCȼEV—K3 †¬ø÷㢠dFEÀ]ªk˜ù¼;t!#Z”ÞOÈ|‹.”$
+•p.(2:ðÛû«éž¹ÿìeCÂ9Tqfþî®ê* ÈB¼©m¯ˆKx¹‚}ˆ$œ¨¥:4>ßÂËßRdžØazY§íc]lÛ DcØ”¤&©b¯¦4Ç Rf}atHãP5Nlÿ±^h½½(ËÕê÷ØŸÖ”uªJM­(Üeã˜AÎIÓΩÕaÙWklÀ­à_ÙCe¶bjÓẖÀ¼PGÈÂ\F#ªâ`k‹‡k†
+d2áöYÔI¸+¹<?ÚÞÃã°ïôh9/f§àè¦!2¦–ÑåÉ^â+²7 Ûã»QËí>#ÞkÍ•…Q´Yì8AoƒM/VÀT¦$ F+1RU:6±›ÊË366
+¦HÖóýí úl>?Ž¢EÚÐ,×Ýyø‚ƒÙ}9Ìè"e¨1µ’o+=¹/Ÿ‘=K½ZÌsyÁ8ó#Ú]Šµdþ[ñs`9.—³ÌË©uÅÉ’Ceo‡fòälhÙSEÿòç±€¤>–?+žx/ãÅIXh>º-ÛaHè14f,> »ØoxÎMuó
+§Üðø¬_ü^QëX»“5ŽÔ1'_1"WÈ_¥)¬ôÃœ~PÝÆ^¥ 0‚‰ÑFÒ°ÛIP0âK\,Õ7
+„ K¤ JJ©óqÕÕK"÷Ó¿ÞÂí—?Þ~úïZ"§nÐÍ †ú#ÅÒZ׃k½ý‡ß2×ÿ£Š'K·°þÿïÿ½EB+;7.Ý~f~îjB¢ š: 'úHÓ±Žìª¦Âö»+†)Œ‘0²ä\ \tS™Ó+ᆼV•@4‰Äj %ÍÓz‡@&µÑ@¨ “ƒK-ZU‚Ð=™·3ð ÂÜP2VUÖÈ RXé¹k\×—iÑ fV•(uYÕ6¢š,ËŸŒ³O
+§‚à&\ã+’CÔ.âZs€D.ï9åŸq€¬«â]>A y¼<g¨4ôKfÞ e”³Èé¡k‹ò[‚Õêѡ¸ Õ·à4IÀ™Ö îB@–r+¬, 2¤üªJZ'ø0%Ft9Œt
+ Ä/¤6 «¸ÿDdUÂ=‚¤—»8i°ȶdƒYÑÎx¯˜%rU{;êâ)uä…ÑÛ¸qš:Ø©ßzÆuy­$ñÐóÃII‹H¡òÃ0Ëô0pËjébÙûþÙ8–nô ¡]xç©xðèð‡EÔN—f€%Ü5“¯Î‘¶u¬Ñ!UAQî!»tÀRXÉÔ·î±v´òÈãªášÖÛ¯@S×ÖbH*Ó½ø˜ï]BÀfã‹ÛšØ+¨ðtôM1¥ ‚€¨¿–š©@"sªB¦ÁKyäÚ†Ñ ¨Yþñãõt@èoDNƒú£ÌÛrEBP3ðx°^ ‚¯gª½†@ÖÈ|¡í§|ø5DCÑVØå ø2úUï÷×ò2VˆÉ!2×ÌažÑk‡O”W23N Ré±™¬¹ÆðŠš]‘v™–+b¯g: c`â¯a•!Õᜧ4qLƒ¨—¨¬Àçí/'1²‰f±ò=ÍÆ‚ Ú”¦¬LiÄÁ+Ê\Í^Oþ
+ð{žÚt8äk«Oyš˜Ó]&ïZ˜Ã?—÷
+[¤×Š• ¡t»¬¥@‡Ø×Ã!•°¥Ï ŠdRÄ™³k¿„À÷Ô¹¿œòB¹M÷ÌÜ9ä>CsõCƒê’©àNðE Be[/§^>!¶ˆë!Ì”$«³nÒv`¤S"6<$Í¢" ¡u+1tDˆ«{4q¢<–Õ\Î+]C$2»×I i—ð]¾[¨ëàèl3s ’.*$ŒáÙ7ý˜’æáa´– [æ4ƒ¦•‹;ÕtQccît0 \H!ØZ|Çœÿ
+¿è§0‹Ä!+¨ùU…"ój1ìœCv¢8—{÷ÇC "'…˜R‡¦àÕ7=ƒ ?Ÿ9iVòúñ78ˆ.ÛxP(0¿’‡A(zÆJÄ´£XÂZTê%:ï«ŠG²
+Úç¿`èñ
+–bùF”Â0K㜠©(s_ózÁ«|ÉTrnÌkÍ¿8‡jRdÕ¶çfÔS°RÙ4—¦ñ#ÇùƒQ ®­üâ¾
+Bb†ìq{ ”¥âˆh®ÝYÎIÇï(Kg‰T>×ñ2kfÐõ^лóWÄÌÆ  V ©í›zæe’ÓˆA]chDZr‹+ˆ#ëþdþ%£¦!{T9RªÉIûŠ›–¼À;G«OBÃ.&}"³×íüŒð{ˆ…Ú̵ñâf8­ Ü'®äí$@Avåqc(Üä9XÜV7KyЖâROƒ°¸Úä ‹u‹÷`»¤öÁ2e¬Ù6¹?xwMqaj7:Á <u"Ï­e'8Ú yÙ|s¿…202“¾¸LhwþŸñ2É+†è |ŸÀÐ,j,s‹líûoóJŸêÀýÙH6`7[‹5˜ÌöñþúZåIJG7dšáÃæoó*£Zœn
+[3Òܺ o$hå &þ¹˜'æB
+Y Æ‘L{¨¾(V#ÿWa€9PšÅ‹Æ"É#câ®uè,¢£ôÌØ6ÐXÛF\À°g ‡½HíbÇÍs¸h)¿EŸ^²ùA€O²ä¸ Á–$^äYðÄ!Ý$1b\qSÿ¯±mqNãÇéȹ
+)²ëi¹ ô‚6c¢nöZd4æ¹¹"cÄQJw=ÄJæ»Ö!3
+r´8ö·'<Å­QÁ*Oݾ冚èJ7ôÝŸ&
+WVìÕE ‚‚eÙí}—LŒ@ïžÐ#ì`+¸Q¯WɘusK2/–ðAe9â)Â4!%Ÿ!&É|á¶ÌRÒ
+½£ŸÓxÚ"sjW ŸaLñ×ÓWÑA14ü‚á¼EEøðÉ’G™ãÏ·h3œ‘ ;ÔÌw?òíðøù,é\¼`«¥ú£þÄL¹Kç ÌCÔNBì­¢·l6ï
+H‰ ”iWš‡…ïêjzÍd›¡il†›eÕ&MMj4Š31&Š*N(‚ÈÌ /(³"8Ä)'PÄ•IDTDœQ41iV{Wï¹|;çÛ>û<{pÇó@±cýÒü¾è¡Zusk—¼&£HY9ÚÖÂhû &g¢å`^øŽŒ=Róp.%£hCJL´NÓNÍlðÔÄNM`]`…†<2Ñ«Ý&6Õ¡gVVÙì³Mžx}—´.²¼ÚfÑ3—»;\7ÃH½±Øœ¾=ˆŽ=RÅG:f嬰4|¥‡ïš©G¸fÉ°µ~äÓå®òû›2d´]‰Ip¨åZà14q¬SÌ
+—¾‘ëYlâ:Õõ¥=XçwÎ>Ä;Õ”C]ÜTP27¤ôÌÕžºg
+
+ôû^LüµTìÏ¢Š'w:k —;É…If@pë5v%îÐp°.=rdd \jB¾[M/s©é…fiMÜBgÙƒåBêÁb“È>O£Øçj÷Š€·7߀¶kY¨½!Ïgw8Ôµo7äEQƒ°&¥“>[-_mM=>SåÔÂ$ytøÜêùÎXTÛU™½¯ä£ö&ÙpǯÂ&§CÍèG`îLÆùÞZÈwÄŒ«Š†Š­àíTCvèž´r8M}í™!¿:TQrœJükÛ³ÔOK‘E£,ðx­Gë15
+|+`ð ŸQÀtÏ71÷TbP!&CÚ+³Â6»¨Hÿ,«z£§â¾µkîB?Ò±àWÍ¢›ŽabꦜžÍ«J¾Ú‰I»u¨ec‚÷”¹TøôõÁÒ_FYY7†@ès_EÔ®‘
+*“
+".M23"¿ZەDZȧáýz*Â<€üMÛ–sÃ:\þÄ«c×¾'¤ü°ØŠÈúbjéÿ´$æAÄ‘È÷ÌPrvGp/Wߣ£Õm5‘Ý$XØû£{œWkíúÒMˆ]“¶û虚¦ŠûíU‰—ÄE¿}ã” ‹mˆ(cKá]Ï4>Ó«¡WzU@Þ‰,ýd¤õ¥»‘†wˆ(·’G93µHög(°õ‘ª§¾EÌ·DÎ?1Q‘o³X_7x|ŠO èz¤‡ªîÁ\CÙö ò×õnø=cKî-ƒ(çŽW ”©ºË_¢2#ΉÏ/8¤õÄĵNLôf:&ÈôÓýIbºGÃBy´|†©lʸ½?~õÙÂø—9ï2<Zæ2ÿÜj–. ¶FkžJÙùq¯£ÚŸÄf,5ÛCÕ‰v*áD‡‡íÊ+ã—ÚaáR|ü¿-½¸8 %;’{:ÍËûßâËtTÀ ‚s¥G;Õ´¼ýYZ¡C×HÛ™aUêÀñ‚
+é®™FÌÁdfoŒ]hí£BE?“3B´lh¨SVñ»­›nîDÆ,6—ÜtÏÒ -ÒÊ8soq„ERi–†ï Ô>óL°K?/ñ™g6×gbî%éØÔÈÿ²dÑÜ6è7ŠšÝ*nµw–SPóë*!m«š²Ù $íK%+mè˜ *4Tß”^Tô@-(¸©!n[ú
+2‰ Ã}U)æ^B¼ ¹û ÜV°ªì½å¡fIiàœûnu
+؇F*bK^?/Í 5
+Òü½z
+b”‡ˆ æ„xu,ü¶š‚ðÈ Ÿ\#•Ÿ Íùoå´Ô{FQÎï–Ž²˜=-·N-AÆÛ¿b  £ñ--v>׫ŧ¹ÆБ«²ò°ÃYîôe„
+’Wå’á?nO «ƒ˜˜µÁâ0ÏXñ«µ¯°°myY쾉”y`ä`ŽMìí):Ü«$¦©¥GºjèÞ4¾£"CÏfÙ¸]- ±4Bü
+VEUÞy4ÏÂLãb½£èW›ãBÒ\+öÁ,„ ˜æßÔ6•Ý5~¬]—ï´"ô}iuêo}øœ»_J»°­öâŸ:‡ªÞzT@öŠ´îÝ7ÿÁº¬&Ù;æ»Ô”Ôm55Ç®á¢Ü3ꑱEèÑ5‘m¸{*y>½¨yoAÄw«ÙEkBä( q­½8Ìo€ï¯a °*nE…^¨K ¿mí©ŒµöbÂ…YwL]y!‹­¹÷†ø+Bä«_åu‰SPñ= õù¯61:æLÇÀ¬B=Ó7eé‰×%•Ï~é«¿¤¢$ÿ¦ä§½¸Pxɧ!Uý02‰?M,Ò÷Ví¡†Tà£",’’à™6t0-?þ†Ÿv}
+̽­2&ywL¢ÊÇꆂ›ðwÐZëÏÎŒð[lC=€
+B¹y/üÈI!þeÑ¿O°ÒÖĸP×8)Ù¥¬HwŽ¡ÞŸ.€UÇ BÎDÁ¹[â›#ç¸T„÷ƒÄ¨â‡¶àœªšD¸÷Ôz5ŸÚöµ ”C³Ë±±ÛÊš¸½Yzo–O²HL˜ó‡u€?×NŽça_/v¢Cm²âWž/hØ#U;Z Û£ãU·ÚŽ—yt›’ïå¼ZJѱ œ[¡Ö陥æîéA´>ëP3Š*ÒùÌ· Ql#Ĩ-wº jÙ×6Ñ\j^¹CÙÍÚi«Y—²ÓÌ}¤7JVNÐ0qÓ*#Ź”¼Òý).Ö Ï䞢ì/¶Švç8 ;3t¬SälȪ"÷t`¹o‘Oý
+w°¯&éØÔH;4¸»såHËÆëš‘G(™×§ n­KÊŸìO
+ðß[jÙMnQŠ¿„ôoðDcу^|Œß9Ö¯³"òâgb‚ÿŒ ,Ä$’WúIÑ“mØPõÏ K.ÆÜ‹yûè÷RÒµ VzÀ¼ ä©©³*Æ*¦%h»ˆoiyQþœÜwW÷ÔÜæ :Ô1Œ}i#ÿê«
+÷믌ðÓw”=ä£î'„Ý»HO¹¶&®}½ÐZþh‚üs (äd¿½Œ|t!óï[ÊÂï_bã/gE?ºHL|vÑò~Çnt¨]Š~bèʽ7Öwµõ·Ÿ¼îå’´1d+ÀŒ`Cgiôñ<X{4Ç®÷¨˜ESô¼mié“ NÜ sê•[Å*‘ÑŠ#,rBÞº ûÁÜUe—b|ð%¶†ñ SMÝØ7—l2vÖâGjörwUŠ{„øa’‡Ò°bolôç„œ,U§ù´Ä4«¤6(&Î7×D5Hh…;“T4l êûÝ̬=32°çfèR“³aÃÜ·+H3¢gœœ1Æøps‚™ùÐ&©Žr £¶iÉne[ýöpÊ­Üš¶»‚_afæn+¨Ép—ElH÷ŽQzêþ¬Ü¤åˆ«“ïª ßššÛ·Ä¯v±‰å[bájBÜî<«æÈ lÈÉñðŽ`aC“\£´Ü-iÅ‹Åö”ÎQ\Œ[CI·ªÀ<Ø$ùî °pc„üþØÈ©?·Âs¯¦®Éˆq®/Ä$6‚†’y¸ÀÄÿ° {¿®pðÌ•zg™˜ãef{†^}ll‚ÎVš¡Ÿ~dàRS ÅGújjn>_¶ý4ó9gK Ö§‹ÜJzú¼O–¾ú(½¨üÁÁ,9ÓÛ¨ï"EnÊY¹+âº7›2|ØÖö…un—‡?U†Z%&I Òò™•äo,Ù”WGÚú1!9!bGMÎqO“RVGÊà \ø‘Q
+=J6bOũߦ”ÚûП[²Ãé9÷d„ì'«Ãhc°.ñ@ÏÀ¯ðÙÇfN à`ÿ"Ÿ³£c5iEžqBåSsµ¾ó]†G^Ÿ¢çg‡¹‡áO5@}#µÒ­lÌxèµ{™ôÅLGo«ˆ6icÒJ7*Ö£Äeï«)UÇzõÜ*ÿp‹vÔÍyû:JÅé
+·õÜÂå_¬ò$ß]‚öoèÌÆçüáhkûº&êÿφHz §Â/ìñÂ÷§zzÍú:ÑÕŒ^“6ÄmŒ`ÝrLòÙ
+ecÜJVõÑ|;w{’_ëUR V¥5¬=ÐðÕ^èmÏ(!Ý%cšzÈIÎRê¾VD´£MÐuóû²ÐII¨o
+Ÿw¨gaü Bžß,îXQ³–Gˆéû bà_>ÿnù0rjJN—Ù-»z|áÀm3H-‚ÝÛž`C×ä´b›ŸbTÿº!Å&¸G(y®AlŠQR¶)gÁv4]|€Ï`{³Ø4k?ä®­·üö¦¬6î`Ž^µ;ÏE;‡qI>
+!D;È̘ë!<wÈØàåVÑŒ9NÃÜŸ¤ácô\’½—”íè"&›;¢´<Ì#%~Ý$yw_Æ„Ýã•&^Qâó¯ý¿o«c¤œiV¥wVÔ´6ÛÅÔÓ»qo®iyUq'&zKYo­yìCź•è—ûjb}ª¿MÿéC];º"̧¢¼ÛW£ÒÖG*š:ËnŒâÒ¥¨Œ+òæÜ[/2΢`A1¥i! ·¯ýíCMfØÞDS®¥³ì¾Y˜{SEˆ¿Â= èAgkø¨ˆ¥>n2+þRìù#¿®…öÝÉåzeðg¶e¡º¶¼Ÿ;Q—Œ°8ƒÞPø,˜ Šûû·eV˹©µñÔЊܚÄd^…Ê£j/°wU<›hƒÆ×§Þ œb–$›8¸C
+7¢ÛÕpûZáÄ@¯¿°
+ùI#±Ê.€Y¾s¸Úñ¯‹ þþåáÈóÓ0@¯„fWŽZSDˆîòß^ÅçL¿%T[ˆ[󎿪!cOë½÷,Ī{üÑ*ùû¼‰ñò" >Ö›š‡; pˆ'û¬AÔ6Ó]o.»Á±“¨r9‡È ¼Kʬ‡”³‡Jí',¿k.ÖÛ‚•ñ:ïâ¿ÏXåÔ²v9¸Eíð,ã÷¹ä¢œ,{rÁ1ó®äæ“Š.Ç­¤ÞõÏNµT=j8mâ“ÀÎŒI€Kšx¸”ELK94âc÷‚2n’Þ/QËÖZļ¡ÉØ%dl_¸¼…ó¶ì)§!)tq¤ZiDdÊZP4}™ÔN£òÙ’$×Âb.„ 7DÎú„ŒJX Ѥøê@Gï*"
+nÎôW0o(:BÎÆÈÙäjX³V>˜ß-…”šŒ­‡5«#ÅB-Ì¡`(´ÁyÖqZb¦é—¨oføSRýá÷ä¼ã:¡Þ¾ŒÁ›ga¹¦R(jXÙ’ Dweƒi<^Ef¯c³†¼…Þ~¼K{Y£<:ÐR›R;“¿œøÒ’†Nƒê¥z`Õ“t,ªòV9¥äP0ã Þ®šøη0ùcT7ý$oålsbÔ¹¬­ùe¼ªŸÖVróÞemŠ[K~àôýã÷ W}sk¨W»S@–¶#æyvÈ ¦ÖDm±Š!ßÔš\ôÕÜ
+vÜö•Ô›Î…©{G;|\Æ3}ËÌæÙ'ÉØ|ì\âÕ¡LQñI¹%¯„7μŠéY-y»d¬ìUËQD-.ì+At_<Yõ@Ó#¡õ£­™gfå¿V)¥9f€)¡5¨-k‚!ÁdÅ'áæö%%¿pòSÞ:É5¿p¼PÏ×Kæëã"ÀÀ¡6rKÆ0ҜܽǺUÎ.Ÿöëø=»<Ü)æ˜'$6¨ÍYÃøÃÃuÒdŽð-Ö1~¬{xÄ:–Ïé=ð]b—‡Ï[˜äË0‡|ãÓ/Ž¤ŠœGÊLÛØ?Ÿ‘¥ÍŒ[»œ°ÃŒŒ 
+.)ï< ñ°0˜ÒSgÌÌ%'Hª!B:ê˜éMëÇš±î÷¤ê"þ|¼ÛõÏŒqô~Õ‰y$ÖµªX^£~X\.î=2>Š1 ëe]Q§õØÈí? )Tç‡óÿç¸<Û8ï8þ¢è»èBƒ (.8ép‹
+³‹nÞó¬…ÿ4¡LflÈ’p}ö²•‡7›.€7Ê ºAJÄŽ÷þ0-âžú+%›äuÖ"}Õ±'⊕Ïê^êÃ&~°Ðô¤Î±x¿hƒ— n;ëRl.ÎÓ&.eT|¨R*~áfÊ@ýúÄÎ[­z4¢¼çÈrlÂhQ…ôIîbô¼°í6"u\–Ý»S |éË÷9æúeؽÏ÷~V9òßÖÓ§.îÜE„·6J°Iýk¥Ö 'gºêl4rnÑÂz•/߸pIçÁ^71º1p»u ¬¬©ÜØÍûQ‰ä*«´^å!Þ Ë¡6#ï^vbàN7&—Õ½RfH Mø´ò'I‡bëôXžGQ´€¶ ùn\ºôigÌõVýˆù*$~s#¯ß¼}EH ƒ‚T×Ë9íˆh¯I­
+Ÿb{½c C4_«)ôAó]ûH%ÆPÙE¢Ö|â¢\
+¢Æ)XÿÀ%6>‘Ѐ?Ò_ÎþÄÎY»}Õb½¨Þv5ºs.”ÆLdŸÖ´xl6¬elRƼꔦCzß^Ês$ì³ßà[E®EL`}Ä\cÝdÍS?U-
+ÜY˜¡ÿnñ!õãíѯ)ó‚Ÿ[¹Š‰ó˜ÑÞO«Ì\Î[ ïB˜~éÿÃåõ–fž€Ñ¶'™TMÔ$Xl$Š½¡Æ
+ŠlQ±ˆT Í|tø
+,?ñûvuY2W,èî¾k™àfm« ‚jˆç™RàΖµÂ«ÐÔ‰uŠøÅ!h94 ŸÚ¤ýÛËÙ¦Ã@ß\6Ò=úù:·z¶pÅ`l6«WZ!è G4£Ø}â)WSµ(÷&Ø–<
+‘óœXG^[9ø Ljé$ÐPr¦,oE­ÃWaéɺZúýÚÛ¾mq;ÏÜÌú£Åð¶ÇÂ÷˜WY*™Ÿ,¸dG£Œç-d±7P}#®§¤Ž…Û8q¾Q–˜T’ŸE´Ùwùƒ¢$HV€¡¾cŸFx¾¾ ¼Ú”ˆ¾¬rÚO}‚ö –»\¡ÑhoÓ(í8ÚÛ®˜qa§8–‚i¶ê-›Æ/ê"¾l㥵7°^jiOIø¶v?%Î,ŸÊµIçÉŽ3wÛ±¤ýý²ÿví%ÿKçþw!7ÿ<‘ü}yÿõIë¿Ú×ÚÎv Åþ† rY—X0·égTE­ÇavŽ[P•]±Pî3ÏÕûLâÚcïïß5ŸÇ‹ð×íyyÀíQöiµåbZ¶ø•Êœ ¾é°ÆŒÒàØ¡^ý=BíøŠRZTcýìmB|3%g<yÑ’ƒ¡F¶Ô3nÍŒÍ&ºTÊú#'Äü´fµn®ÜvÇ®fѲ;*ZüÒ$Pü$NÎþYÅàì¿t'p†ÜȉÏsæ€9¦§Ë@jU="·èïZ4‘Éh2xþ¢T”óÂhCþxhá°çhÕc¹Ú†4Ÿ<²C×ÜðŽ–Ú–7¥bÝYcú<w”w–Ñ9¸ƒožjnÕß!µÛ£›šõèœaD² ó™ˆÌ.ˆGQAÔít€Éî‰lk‘ÞcÑŒÏ\JMõY@6~±-á¬YçÈ°j¹U%Ûjœ¯Nð63D²ïõ¢é¯UrÙ1a†ï)s–ó§&W²™Ãp|3AxŸ’™PjsIÑ­õL$§›öªVÇN<Þ¥oJÙåÚó¯ ´|´ù½>§Þ°?FTÚ£HwÅÕh¿qæeëµrÑJ¡”çÏåp·Ð”ñöf¥‰õ¸®r$2-ûKCI3Rdzàô¢D+‡ke”D˜F«ýnŸ 6¯sôÆà¨TsD~xMí5ÇwõÙ‘ þqvg¯ãú55$=½5‚Ъˆn é#QTð(¶
+ÄÄã@–ˆ}Ù Þä‡3ÇüéFÓ.7è]Ò^|PëN×gGwÜÉ’=8¯2žsò³º>FàYqùäôŒÎl)óæ;’õVm“!,¿ŒHJí˜b"·r›Ôhh"¯Eõ ¬>°­iÖEøKµP³gá÷m¼š ›®Ñ0f”çm“ò£òA–Ùܾð˜Òïˆíî÷=,ÇrBSQï1 AJdf_ã[AD ‘Õj¯=¦¬Z|+ E‰®©eÇY”&ʽEÕÏOsÆ?>Ì
+ŽÝò÷Û.Öë
+Ë»:£åã4dù]"”ÔÑÙ«IŒ1G2grû5µ‘F¥jŸæPnÕÕMÜ©ªì¹_WA@ÂsÓí?ü
+ÖÅÖÔèŠ檌¾NÑâi#Kr^ÜË=JÆwY£±õ“!u¼PLé HÈxnÞÏ)ɇåƒä,ˆAâAõ[z„hšnU9È;Kê-“ï·Û8°50!Óîwp—µ#âoxGÉô퇕M󡘢!PZÉ ©ÁÉBó+ø Ý ~½f=NliY£àU6 ñ
+Ck§Èè•«Ä Ø2|îƒf.üèÀ§“®Ø•d½ÅÜ2 Ô‘YÇOßÑ>Ü!ô¢±MšðÔb¸÷Œ ^¼ì…å"DZ>Ä¥€è[%CdW*BPù vêöðõW rMÍÙ†zþóºL¼ãÕMÉ„ÊL‘ȉÎî ±÷’êÛÌ‘ÙXQ8¯ŠªnP†§¤v€ô7}!eU¢°œq'¾܌ƀ¸Ä6’;îÇ“˜Ö渻ÌmÚUö¹6Î×4ÊC߂йdšQlÕˆ4—T¾áïžñwt÷Ønü{ú^Bç{ß=B³&¢¨b&¢
+Ï.C<|ò ÐT”NŸÖ 0y´Á¸ iàñR6æF³¯"šK§hÐBŽf‰ò ‰)ÿ^zÍ|]E†¢
+Ù7Pùcl­<¤²NšUÈ}„lBdæÑe×[v <I&ƒÔ6â-e-×½õˈàcžò‘6—u#ß÷4ößNµ|·Mi:ãò•WÍ#3§ÙCœ`bw¿çAI};3§?üI" ñ,­$¢{AbFÈ(b‡g”‹Ãóð†p\—ïÁÐäaºdá#Ѥݡ–³‡åÜ'¢aN9,þŸ¡òàJû@
+PeOÙ{oø󗀊+j]ÑôÚ$½³w÷îÝ{÷eÎï𥎳L _ïdªŽ`îÉ“·ƒî+¯[ÙgÎeÕÖ ¡eÕtHÁƒ1ÈýôY먧ÍRH^%-+§÷MA5«îü¶ã}:ñÞQ¤—:á ç(4Ózd!RN5Ú(uµ)””Hµƒ ³3O±”íü‘éÍ;ïpñk­0Í7ÅeDÈãjZVYÝôZN3äÊõ:h}§øB]ù;œ’·0Q÷c2ºÏn˜í‹DµmsZ¤Ý~7/Ûo!q7ž}7ºÅ—j[ç²+›hçn¾…^¼ZÉ/=ã“CŠ¯Ð†vÍâxÒogD¥¤M³#ãžïLzøo½.ׄڲŽç*RÍTŽ·˜@nâwk:$g/Ï*x ƒÀz.t¢ì—^ÂTÙ¥/é_6s¯cFùŒYóC·ZXµáâ´ìí'1öÈaD2½aƒvA‹Ý¬îÐÚc8¾v£ÊŽ”S˜ÑRc©¸ã¾QÞ0éaö\]ý|C—ð|kŸàûÞ1ý$ÉyG æ³™©ZÑÒ[PN2Öat²}Í3ù* (‰&µy@«Ž«ÌÉ1ž,Ù86åùéÍ[Å_*ùÕP¡+ñg< ¡wŠz!å/©ÐòFäÖ£1HÎÙ£J¥úwÉ ÈñpÁ<?ÑtÇåG1­bÃ;SŸràËV
+dÛ=“ÌT%
+«¿ÛÖýŽDPÜš¡¹ò Óλš¿V"òÀº
+K”Ü'3Í¥}tÀèòôy½–ÁhÂoE5”ÓM>í¿ÎÿŽÊÿì ¥
+Dš”à ÛÀ…ŒÞ±´Å4°ãÒá>ù”„¿/©þ\“«¾¦8¸=7½ëÈ37|š
+¾¤yS;‹œþ´[Œ éØ- z®>(./΢7¶{4ÒƘKŠLúµ„”OOÜZ$Çió$)cü–^˜;]—ÿ½%ý#5ƒØ6÷lé~þ´H‡íz­¿„è­‚¼âåôìDe´5k£¼ª‰ûNéÄ]Ÿjª(b“Ãã>ãD&ÀíY÷ÑCjá—%{ä4-áüky¦ç8¶ödq²î€ôz/Äý¸l ¥,À‡ ljÕÇïKº¹ð¨ýÿ •ÙWˆÆŸúÖžyhO{2Ód’I:™¤‰¦cœÄ,¢Ä¸K\ €‚(ˆìû¾hDMbDEdY(Èê";(:g’Nÿ“ò|ï¹ßý~ß7;°öa$ŸU%ÍC*Uu›Å5ÕSêïi é·\áûÇSskíó‹Ë-+ò÷{&ÕÜÙ®r;e›n2 ÷ì\Õ/[¯úÂ3+ºÝ+ú—íä–ei²Þ¬Z!ªäüBÍ«Òåñ9…Ë-×#Gˆ_€Ñ_щ¬Hf§¨«0³JÃðë—ÙqïuÖA{“µÓaçn>!¢€7ÚÅ€¿;çÛUÂônˆ R:üÞúï&Ÿ÷-,ímO¡vÖÙm[rzÓÆ;zÃö*¯ÙeœAÄo¹É
+\CpüÜý‘ð(¬§ƒ¢rWTOœÙèo2V
+(¹‰}–ÐažGkÚ‡•˜ºƒUÜ£†ÝQ^lDPÉÉDäw˜Ãe7 žµ‘j¾£ ]\|ÒÊMèyà Šòj_Iî¬ãŸûä£|r죀ŠúbÏ(Åy5²qRÜÒ²:RÖ«Ç–yÉ©ŽJ ñ•®›9ÓdOÉ5Ž؛ýx*/¼\Bщ~•X~”³ñ‰E÷”¨èŠSV,n¡†,óŒ€ù=Gûf–áF7ˆÓm!&e¢ÂM˜îœƒ.8ÉCE']»·z×XgÇuøçž…ž;'°ê'B n6öÌÊÛW ø— uå}Ž¸à玵¾YÇ£JTSÁA_ø訢‹?ÙF?>ÞÂ>«øÙÄ«0›•±£nYרVw¨C6æmø¾ÏLR.È¥ï™}~ »¯y»’2N4G– ßïKÁ·vÄ}7ÂKð R_ÅÍLi0ó4蛨a”¶³F"kˆ»AÅÀmÿ‡Þa9èÖÔ±ÿð§?Ò’‡‚ªìó8—ÑÇùÞ”ìH7Ü”u]÷ËzoÄÔoþ]Ù£¢«ñ镬ƒ;±H¿'§ÕW!¡4m Ä×ú+;oÄ4àûÕOد{ôñßx¢¬W Œm1“öxq_º^ /X‹3ŠŒOÈ;±1†ƒzü‹== tâ‘­$v¢v6aŒmóE7›pác’ÏÝôɲ8tá&C.¤Ö¸ò]FOn«eÏ<pÝ@뾓Û`ôžj -Y#¹3¡&5Ç×&Ÿ„”¨‡&N÷×£›5óñ'zrÇÁ»ñ†Ü&±«â õ—w='ëðÿØgÚ®I ÷þ`çöÞÿâò¯¼Ä¡”öõ?+×ÒÈOYódkÞÁA"jr‹?2L4g¬¤Þ¼:TÜÁw& º¸jð§“ÚâZìã„žØzîaŽ•4xÞÆ|“6¡[ã*d]piô‡Ãµ‰ÆŒ™ÚW zdàë =¾%´Šj=ÝÀRT}p¾û¯‘EÐ×áÛ‘ÚNBKZ/ÿt¸Žx‘µrÐéå©ž
+v:Ä»:4ð;¿‹)O"Ë#-iíÈ“² Õ\°Àž¥6»¾ô`E'¥¿äd–wÄœœ}J¶ñ±14 >ÌÚ­y—œñÈ1öé±ÝR°S ÇÚÑÆÀüÀMŸ´çfn ñ8¿j,ÛI}µ2iÀÓÛ¤ö¬“…HlàÚK.²` 3fR_zû¢hÁ
+
+x¾#¨lÈAMy;n¤êçñ.}ä±³ÍágéMX}p ý4åàáÎƒÓ Ÿc’µJH4õ ØY7‡sÏ?ÑFóNÚpîÿ—‰7ÛkÇÏœ3Ó¹ÓV[íu«ímokêVQ W„†k‰ Yd•MK”–nT-)Y$!"$–lö%Œ‰„ÈB,5ν§3ÿÈüæxÏ{¾Ïû|?Ÿ×ÐHß›âc<ëï}–w=»ºÆª-YMÜŽíacœòêXïTmÖöÖ÷‡jx¬º,ªyñ”|}¢s4ÑŠóŒó‹|Ú†ÒÍAbÔÖöÙŽ i—Sw•¼Ü¥⋵>Jœ_ÓN»0¼å~ºØ­­˜ßeÞ™k‡-½Ë¿¿¯däyt-$§ššbFEÚe•ÏýZ40ÃÈÝ•R7‡ªÁk"4!6`à¦F’}—`‚‡Ú¤ÅOöÕXGW÷ë¹ï-Ë©"<ÚfÀáZ({*:Ô*!¾
+rêˆ~OD_·Škr.:;Oõ KiÄÂÇW¡ëý©¡þL©‰™г+}:6Â9ÁÎw϶1tmÌË…ÎÅŠ€â›ããݳ¼òÃiVvÀT‡Ø’b£7D؈=E#Ö£Tf蹇ã؇¬:våsñ#ë`u°/eþi¼½äÞ¦”
+ÙˆS§òr‡û¾ÑX01Q›âÊp×µà|ùm§GW_¹¯¢¥ï ãã]#˜Ø59õÔÜD:Ö×c·‡ª@dzŒ\ ³âÝaRºo’–³%*‹u*«¡612Ö=ŠŸè]9lU>=1·6™š¨ÇsŒb¯‘sj0`«óòpŠ‘{4Ç)÷›¸È9.ît•M
+Ú®Šqi¨P€cü£¥fŽM t††ö»–]}<ËEé¹åG@'Yå(è¿ð¥8t¾»òá–˜ ”ì)™["B´g¬áaÃœ#@®r\”CIMZíÇG:w·iI'3­4»”šâÓ±àþÙº¢}=ïBO+º0â §J¦MEOÝÄGê¸ãi.Â!öT‚7½ÍV²âþ6Ó’v=0Å)òz1.AÊ/¸}2ÅC].p‹¬²#)Ý1‚Œ\ï.øiýCÑ6 -yMÌËXýBx¶¯`¦Ù¤””] æ¡@rr‚¥õdµ§*Ö¥¨‡[ú(‰øß®H ™×g›K¬|*yb— Ÿ{µ´lç$6ÑYöTLË>3½ì*è¯?Ý]ü˜qÓ Ì¸9ßžÏ!ÁÄLÓ³,€G¨x°«sŠÃΖê k_‘rvÆ5 ÷ÆtkÎ 15éêÎ ú…g’Uä˜êPÖ¦ÙŒB×âÖ0‹U|ð 5?&ØܺµÙ›ßg`Âݺ†ŠÍ¯Ì<ÛWìûš¶ÒH?Ô“a5À[YaÔÖ`Ù£ÕäC§ê5É*¦¿ÚìG<²IÃèó›ì› Ð`À•Ÿ[Å•q¦÷ðÛ6쟠wd
+· ¡@k ¬?¶„o}ºº¢}]‰`éÒçœ{[’’žY8tï<fedδ2|ÆŽ®ÓEaÛ,M¼Ëy^­K‚ìŠ ÃW>f…|ßî<ZìèÛŸâ“f^õÉ í31Ñ~£ôb…:33K¶ÅåÑë½esM¬ó%!;`þ¿³p‹}Ó¬ÜÍâG>=«àp†]àždf¨ñ {ʪhó‡Œ;Ü´«v);oµŸòrécÕ/æwȆ·ÈЕ\¸UÌ
+Ð×p§¢:Òü&ëîFoQ”CNØU†­}AÇŸ»{\ªîö¥.h®“ ¶ôá“Ïôôüãibš}³Ò[.¢¥ßê#Co_.··ž.´°\Úšß´oï*YÑ·v‡²C½5É{¬,^ÑÓ¿NuÆžÌ×c\2d” bg…ÿƒ“þw%õûÿ(.ó·$°5Ž?ÏÜ©Æ›–F©mXZZš.¸àn"¹‚‚n‚â‚–û‚¹á‚¢(Š(¨¸¡!.(¢ânÎÌû4wþ’{úÎùáœ÷ý~?1t
+c¢"È1P;ƒˆö°û•./yOŠõZkuQW…: ²ü~›kŒw¶(Š“‚´‡ÚfÊ›km½àJSJ:Vƒ6Eñ.2®ŸÝ”
+úù­±‹ì±ÜŠ{¦«K~òûjë
+ô™®‹à?QùÁéûbeé¿UÕÚT䈽á7ËPF yp2‘s®ÉŽþŸ©œ§n'Æ–î,õÓ‘ÿÙú2{µR×b›bc¶%X7MUÄöÌWö3õ(¯«åÒüjÂû¥NbØ•¶œú]Ë'MßÏrp ß1§_ â¶0й&ÄmÃó13À~]’ÙÆIt¯ xì Çš$ðõ^ª·¦ç¦þŒvÑT£ï­6“ž›G9Ñ3ÍžººTW›ªèÞ¤(Ê")=›dF€Ù„[G©þ w_-µ`]Às&¸‰û²¸¡ Y¨»=[óï¥ÏIp&ri ;¨¿0ÂNߘ
+ÿSÇÅ_Δ’Œ½¹¡óµÄ§Ç£B¾±³0îh,'êPžé¿,Lu[b^숩`ÇyÉ.×~>œd§ìHsƒ¶Åœhë #èX†¹;öb£ç¾ÚAð¶*øtëDuÎ\ÅÝØ÷“3¹hC{¶Ÿ¶í¸Ü”á®oùè¥oŸͷ¥y©x™¶ 6z£‡øâ[wº÷F7nì$½úÖ~b‘`Ü7ûÓüõÍ©Œ"2ìb¦Š¥©û®L¸rüØqÿÝafØÎ`Nð!p‹•¢w'3üÞîhÊ:ÎIæ¢ 5ß_z(Á7Ç‹ct ÖzÈÁÛr.¦‘éZŽ}ù¯ëùòÉtqÚJwl²"æ¡ZoF ™$@Î5Ø
+‚¿£ˆëu .!Y”%)³U‰\Àøeˆ;ºúµ
+ãÎfø£ˆäcìÄù˜:ÓývûIq óƒ—[S=Ö»“]ÿ1—•žéXƒ(Ýýp‚}½Ø,¼Ö76ü0Vº\ÈO
+z¡ædLaO'‘'J:â`”
+þ*q¾í¦iÆ>jc~€³‘n§€+
+ðÐÉTaêbS¼õósu Ñ2\eâ ËÍ +ÍñŽ³ŸCn/5'Üßnc–ÄÖ‘Bk!ßÛʉ¹Ô§œ]lžÒÔÄ?7tOé¡OUæìÉ‹“FJ?8‰Ùȇó­dø†ˆd–Ða«Ýd˜ºà+¤‡9tæ#_Ì•Q7‡éÁŠrDZ~×(Ló²J²ü­CYþ–±lxqWY›}¨nI‹¢´5¸§35ho};>l£Ÿ
+'ó`23è@E„oI½6úÒÜϵܬk}ûxŠ“:}®-˲ªØÈS ?ãRW]x¡æ§¶Fî eÃÖE¤—¦œ7)+æhºš¶%ç!wÆòÂ׺³|–„Dw³47Ä6YŠµ(óà x·Ýþ,ØïËMíÇ“•™ÛâÜÀÙz”ól}Œ³y€³)8‰×3‚¼ñ¢¤©†TWã@^œY^’¤ù„qRrc야XÇ¡"¸½ŒtCSñÞÉÜËŒ8VUà'ɬJvò¡ªŒ`êg…í÷
+
+ˆ(Š`CE ‚`7&›=ù(û?û Þ÷ÜûÜû»¶)fª(:“ˆñf¹›¢i%E›zp‘û"È!¡½Ûçgé{‰‘[Ãä·Wº¶&‡‚–꘡$È„X·¤p8JŽÝ¡ƒÍ#È­¡ò0S9îX.$mѲ:
+ä€x¦§Á>OV&ئè‰.yMöÅŠ
+^!àË(§”“åÕðÑ'K¬e‚ïRР®Y\ü¥Š’êš#%m åš?›‡1¡^Mm±}¾&ww„²MT¿üìWf湚‘å²f£¦¬ƒÜÝÄ&Þ®¶5¹Uôtçlõ[—”1u•†˘0†ƒ:QÕ"d\˜é#ÚÿóêQ
+JöGq/wz‹ŸÚ³þÐÁwE•ñÿÐ6Âü<
+nÑçéš,ó0ùåõzãLÏÃxµ,¸[IN;_¤dÛ2Í9K»çš°¦v¢u’¹Ñ IçZfÞ×u.öF_‡wH«’'JŸ;$ ÷z$åÂFèY,cŒ4ç<'Ç.©|mŸÄ¾¸Z’Ž
+>ngZP½3J‡¬~ÀEHkóÊka>ÆNì‹SmÈ_èçbìÕªvmü«áDUSh•†+ fدqL""¿mБÿÝã³7Ť ×Açj!áÖÐÒp»ù¾þë&{ºDJ;œDEXE%a×+õt·¦–,oÈ tÊ™n çV³Qg: euúr7 ´ØžjÈ ¸^£¡¯ô4¤yfã2.uuÔ3­€è]â=k T¨"jþMY_ø§C.(>˜¬Š_ëBjšsüwÑ‘
+6Ò½X µMaÁVqQ°w>•‡/¶æø­Ç¦b„—4Ùˆˆ:˜«ûÅåý—ôºÀñßÏ·®u:eã6¯¥fŽP*Ór‹¤a*K‘!{ÈŲs²á^dÈFq ((
+"ÈÐìÜÎëþpÿûí/x¾ÏóýŒ÷6v‘C^ƒk“7Ï.¹¢h/ü5¬§BbnKbO84ÓkF݉GZÙ„Ÿ
+ti“{÷Ü9A-4%쩘Èo!Û3*pB®Ä–Ûq‘µÎΘ£ç]`EHw©MÁÅŸÛ¥­8¦£T½Wí$dîÊp7>Õ¦Ê19»S­Ùæ¾²®ýQõ«g¢åQÜ,&†-b¢o‘]Ò
+Q;„G~!ß;ƒ|¸Ü_–¢ï.ÿ‡^Rðîå©­Wr ¤¿ k8µQ·Î?A.Ï·×Å‚V¿’Q±ñ¥9mõCõƒÙv¨k’Vf~ »´õø+Ûù±%oõ÷–,Ï0>/¢âÔ{e”¢ÝÑÖŒ¤MH<T  Ø\Ÿ“}°fÜ@¯Ixˆ°FˆÐýŽ¸»ÒÏ>Ör‹”—I+À,:&do†Þ—µæG—XuA%­dÈs¸úü±‡Ù›cBuRXêêŸõ©¾YÈ#Ç>²!ï/¿‡ÝM˜yè½YFÙŽœV·I8Q»Á'î;Ï /2j"Z!Ò?×÷+Eû8¼9B‡tïºÃú÷}I«´ã‡«³3a¡ÕúdÍÙîaDšOÉxíPŠ°£’¦t™°îZX#ÆúÌꀜR^ –ùÄâýYBþÁ"¥èØÚÅrLÒ¡¶­7‰E+˜b÷ªøÄD¨ùÏ\£!¬ŸÞ\w|mÇ=âC˜tdlo8µspA¡tžUã!§61ݳÀx=H-9Ô°Q¹úâ6!%fä4ìÈ° O.+ÅS}3°Ìÿ튄ß×ù”#©èÄÖÁ?^P61õÈÀoÚW³*ÝsmŽÄ¿=Ó8ðŽ¢½Âö™˜å‘‹à „È’°%ª£UïËñùÎÑ–€v²âf.:¤ïh³~Äd¬üÑx#ºD­vèh þ±ÆÆF,”Ÿ
+ÞŸÅç…M\ä.à-}OýS45ª#{€QZ¢¾J¬q)áUm ¹9Ùј»%"·'¹µ¯äG–w-wTüWWŒÒº´³uùo—¤û»C@÷)•^µò¿>É»ï[îá©Ôô¡ `} ÏA‘eî Ø$«Þ>ˆHsOa@¹ßŸ8¤CÍ5KxâRÀ:cãSEÊþxÍÕ½ñÆ[>9Puvgø0ï<·æH h[N~Z$% äò3 µö§§””!S7Ã>„¹çj¸¶=^Ã9\û`†T׶×3s'«’v¿FØÒv¶ÛåÙÊ{è³´*ew
+‘é›Çƒ\c¸‡ÀÖ)õÈ8Õ†~ÄmcìRX%†GTbTD)Fø&iECmàsïÏ©¹/R¼cÍb&1Á1ŒIw4]ý<g]H;]aÀbzr…k²9cÈþyì–sˆ˜£î¨½x¤¡V&­lÂ$@' í°ÀB[¡g¾>P{ÑÜýâ—–VdR½¾—f¦€j$¢cB&>: ${ˆ\@G¹þEfÝ™]* jv\ >õʈ ßt[nRÇm85IhaaýŒ¸¹5M|[˜ŒZ?Ï$¬}¢ov  ,ôWq-µ2®eÂŽnØ_â5¯ŒãÁÎ)JåéJ·À?‹ùd¨Œý‰Æ;ÛCM÷¢:€ÛÖÅôøŠ„¥íÇܵ| €ƒj)°!%øoVAKÌHªpMÔßÝ“ÁÓ#Z2älEÄ;˜~s‚TàW3!?¶$§k<bÂDª (‘«_ªï˜ÿ¬L]ûÒxóǶ¤Ï¯c"uïšî8FÈà„UÂ?¶ðYq —V“Ë|rd^d±­8¬¢—‡­½â¹Þº[š·0PD/ÄÇÌ"ü±‰?Yå‘¿mˆy^%½lsŸ[–öÄWß5<à_3kŽT¬`ï¾NšÙˆ¿7ظ˜Ý¸1Œy¸1Òœ³ð Jaùõ¯èGç6úëïìN¡2ƒZRebU@Knôöz
+é©~e+à˦ôý9j¹ß Æ¥ÜYAƒB ÿÍ4@ËwÏu7ïÍpËN
+šР†øÜ7OÛ…^õNc²¢ª^bL×/ñ+;±Ž1V鎼z ¡Wyç‰`÷(6Ã5ŠJ3õ½úÍØS™¸.i•Ð÷fY5_1÷–ûáW§Yå)c”’Kë™ÁYZ©±§á††=ïþ‚Nÿní´¤*çXímïö‰GÖšµ?E~æž •ÙG  ¥žW¢F6&¹ÚÉuµä8?A.Úú¡—}ŠÖœªí鱉x(ŸŠ óÌs«´½”€Aˆö-`AÛãM·5]å)KÒ²¹'[ ÃZîl£KÐJƒ€– Œ2“vÙÔóòÜÞ$ü¾W˳Áï›oNÖôˆß5tâËbªk™aí«¾`í­½dî)¿à¨º‘Ô«¦.êæ0¥Àð¶îð}YÑ%zõ¡
+.à ½Šæl¯ŸT3+Z1Úø‰þ€+þåÿ—éWÚW‡_M{:3'¶i›LÓ,g²™Ä8&*MâncŒŠ7”M@ÙwÙDTpÁÔ51&P@%ʪ( ÙdW$&Ú3þ!ó˼¾÷ÍýÞ{?ŸçÙ_¢”Ÿ
+xQ€/våˆÌÀªÀ¿ˆ*tÍ6dî/µþzlâ N]‚¿šX­ÿ rÉ+£VFµí-€¯""z£G+
+-“ÁI-v¼ÁcÞqisœº‹«Â¦ü¸ºƒèšiYFŸýdÝtsOψhÐyŸl<ÐK/S["±[A}’Úîñ*™­ð4­°ì¬¸îâ ÔG_;&ÙIkï¨KÝÍ]€^²Nb@qàHë\ZbK‹¯q)q=·Ídоš92÷tï*˜à
+([²ÃËÐ{¦ÑÒŸv$uiQ AzQûŽ”œû~ Ú•’Ÿzd”²¸ŽR{jç2Œl¼_Š~´..;¿óºù6À‡
+¹j“°ÊgéEgVøOÎGôªÈ<>7 ÝÉ…>)&ÿ]WÕëCÍ—ü€s™^·åéEÏ.ùdО¬ñVp~7(Ç–¸fqù†áæëËšó¾%Ö3ï"l}ÕxÕ2ÖpÕ2Íx/¢+¸šc‹xl‚@~:•„ü°®¿+¢å t¤š„–Tå”4¥ï«ÑEEKŽ±5ÿƒ‰…Ùž‚§¹°…G[|F|¼Ã–z¦!w¶Çk®˜Fª~(éU‡ëÂŽÀ2³Q-zö£WF(ˆ©éõ15¶äw+ ùy.
+_Ó3›Ž6™ÐOvAWjKØç˜$ Âþz²óù_Pkj
+8²‚- «Å1-½.²Ö+ hÇ^™ßЊ,“¨Û€oþô‰¢C §Ží|^ÂÄÀXX¤ÔV'eîÒ=oÉ”ó+.„4äÆ?¼]CŸœ=G66+¤Ã÷õÔú˜‰ÏLÙzú?Ú%fí`«x%µ%~3ŠÅam71¢¦<«±QU[np¡-àÙ|ÿ<,#¼D*K­uÓJb¾cyKó¼ìœN”÷µg‘öȃ  ±Æ'„5½,ß"¯Ñ0T{y†”ý…^˜ûoÖý6èXÓ\JfOÁoØ“rª]üÃ÷@û¦éA9ä•ãŸºç»áÎùA¼K!&íÉ;<s”¼ÝYRžy ’n®»å™@¦Þ 2|3è¿—rö°º³5¨$™Gw§h禩 ïw߀om2`>€‡¼‹ÌŠÈ*^eÁv¦¡×=ó°tïBsº}|Í/oÎ:22¡Q£€ê˜§=‰j‡†Ã«]DÀ}ò’fzsXM.?‰¼cŸl—¨u» ì*à æj@Ù^ÐsÐÁ%\¡_‰É1–Ÿ³ >¹à›…fGµ<”c–U®7Þv¼Â”j;ñ¾YT¦Vðäì#ïïaé·úÞ²z%!5±2ŒH×ô6g¦ÖYèÐ2d);7C/üÇxkÎw
+fáw¶ñæa=»ñ†ðpšõôÃôvLç$|jÊÔA‰i(uÎÙ–¼=À-½ÚvHÈ08`~ƒ-Xé­¸ xÖã
+ NšXdà¢ËÈÒ¨¦µÂ#ƒÝÌ[gat#Õ7ôbðÏÞ9$È'CæGÞªS›|FÊ"à
+$fhG–‰¿np§á`r“‰˜-Àûíi©°]EËã°WÖPë#.fGŠ+Š( Ç63®gÂ’6æÃÜ<
+ä©OAhÚ§C¬¯[2øÍ##ú_ÏÀÜÇm6)ibÀN<Þ‰IIêÉ
+.°žÆ¬¿¼8t ME =¤€¦ãþ±›KI¯ò:ƒzFÅÎTÇý}½Ô­¤ÞuÓóC \dl"øçé%I D
+kèeÁ9\ðÞç¯Å—ƒªæëq£2iéãÃEß8¿äk‹¢áÆž’ŽˆjȌ֔¶2
+ÈeQÒ¬;r@„˜YÐî×ðêTœš•W˜¬EEë9³¬ùÆ^Ä…áö#€¿kög)*vÕ¾Š]½ôâü‚8ÿ+‹ìÑŸV@®ì¾oÌL†
+¨Åhß”¨Ö€ï,þöþâBLÛ
+‚ Œ
+x‘‡Iظ¿†R”°rа™ƒŽèUìtäñ2“¼¯á´¬¾¦‚>Ϊþ°Ñ?´«¥Õ®µæ¸Fê/{Þ´ÜHؘ-»ˆr`wzføµkJa½W#cnÍt5™^"ó–†Ñ·\¯WüsØI©úß;=ý1°Ÿ½ ñÀ h;\‘ÉcV¶t=õªy{3ØܘÙtâé‘|öö¿ÙSw×-¾Ä]õÎC˜wßÀ¼’§«Ýø˜ºõ§½×ÅßÚûÊ.nŒáKV™Ôú–Zd”#/}t‹ûþç—žz’O®njÒˆ© ÏÖÝÜ®ÿ>¾qÜÏÆÜÓäZ•¬âìǽ¾áÏ{Šé¾4f$”í¾GgǬ|\Ì"ćõTPÇAìLÒz”ÝE›J^ùÖ¸á×ô⣖^ZØa<“„×G_Þ å´ü† úÃõòÐ=aá¡VTQˆ0à,ß4íADÍGøæ({è/=êfHÕ–   Û—wäcbZÊ;+ØCþœ'ÇÌfsݧ¢>ôƒ}ìFçú';ž: ÊÉJ7lê®÷Œoygé…neÛ••Ug`UgATM~thd"2öj¼1¬fTÄ´ÌÚ—ˆÔuÜ ªAŸ0 ãVZ5l¦#Ž–8¤O€1b‹<tHÏoM8ûeq§L7÷t¦l<ì}3¨ÃæùT¸‚˜žT“Yå“CfmÒÊj:]åÉ%1]Go¦õfÜ@­HYXèC‹„Ÿ²=é'åyUìú/YÖq¢|AÌC`}{ë÷æ+›˜ýq]þòÐ)f‡ôŒràŦ¨¡PòÃjìc౓¢æÚÚHãµ}ÐÛýFÖ:^PM«›À¬¬° §ëB~ÒƦz”迪57n¥#>¬Ë~M,õË~Úù¥Cãúæs>mp©‡.%jâ`CF^Ëö«¶)Bf95‹ðQ»Ñ?ßQÔv¥ È NÂÊl k)%©E!èeRIÜL­<˜EÝv •~·öºõjÜ" ¸ûëbÁì@ýß-¯
+§;ëäõì/Òÿ}O¨²(Zþd–×ÿ!òs)4Ÿ¡Îµ•sH‡Î=S^`*.VWÆH©èˆW‚¿PªbY÷¶¦)׶gH5e‡„˜7±Pe;»+³Djˆ¾ë¸¼7ý.¡'×½)eb5'—™M9‡[ôÏõ~
+J>Öãø<á`­ÎâÆï2«ÚÛ󂎲o”±SJ^fÇçMõdC4œ³³{KÃw++ltÉ#
+}À|oÖ6ý%±ø¸¦ìç•ÊœGÆ*x`þQ@©(xøØ´‰r/k¦6î|¸›ºžwÃìJ@ó¼àQ@)£›1AŽ|rþM4·°;²fFs|~ vkêQbf¡N×Ôã‡kJyÎÁE' œþè ¡>2…«>^“pRá¶ópåéZxQLN{4Ä?ç€Gò^¥´¸¡¯„dò¤G!öÏBŽ_ןk³ní³ÒºF[ðÉY+¿{v°îpU4r¼)«^,¤lr^hw)¬#ß,oŽ=OÚxýˆ“׋89Ý)+·»
+¨à•±ÒvN{ÞÉlɘ¨ )·S@ƒiÔ—±B˜"xÁØõŸQÝþÐq!>Ýya÷c×¥ŒÞ”6
+{Ó6).e¡'LKnéºÿŸC“0n$– 7RË´†¢KÆ©µ¯S˜Yà5}0;¾„¯/ö‹ØH@c´úí?zìå²G0]„zC³ÔÛ Ç±E3ê>ù¨;õAtfð‡ÐcÎs¢fÙðÜØÀ…½y^GÁÉÇæLÄ;±ì̺ðæ­¸ž×œµ(˜e߸vgžuKG¬B¬BBÆ"‚7AWd¢ÒªöYÒ®âï/+H9‡V±· AG7϶#ö‘®üÛ–: AlÄIïŒÎVÇh‡jMim”›wòû
+vá`Î:Ò‘³ñÝcâ“
+òº”·‰‡‚³¼¦¤…ÓýûŽvò,¦™(x!bÁ3Êø†GOwO?…epe]@Ü·Ò£ ô&mBÊáú“WåU
+ì 8ceõìX]97wQsžQº{‚~oq¼÷û´}„zñs6\-b¥·”WD¸ŠGL.»€÷ÎVÕêãuµºäSðÁîyzO¾²3ÇnH™a<bÑsö'ÊŒó‰2¾¬ œ³Ïæ¯=bºÞ¯³FKÊÂnÍXø]G~ÿ4¨ýù$¨Ö–|JÎ.ì@#V /aÐŒ¤,
+rÆ,~”1²š³f^ÈóÖįµâ„™Çë*p—
+(í‚R6%;k{*Ošd„}¯}ßÀlÚú0|5®gÜM›ù]à\Gø=¾j{šT“1³Ûr6Ÿc5F¦(77'q—§ýpU­BV”Â]_`ÝOÛ$øŒ ð
+Z›`ÞJ. {O‚ã/Ž6$¼”aèÆî\ß7{óت¨S2·U@×ä]rqÞ% –=2î'ÀJY+¥>i¾}‹ÿÁû¼ý‹Íÿs\^Om¥g¾Êdov&“ñÎì$ñØëµ×ØÙ8k\( ¢E2M .¡~tTA`ƒ—uÅØ^lšAõŽ$DH¨ëD1qò‡äó^|·§Ìû~¿ßó¼&”ÄÔMI“œézËnPuÿûëI^]IÊ ìÉY-½ž‡¶Ýž£ß^›"]C]bÆŽVÒ˜ üë  €‹þ!eÎ)åúxýy+ ]h¿úÐrnEõ`ÓÈmW?j>5ñ9à>@ ãØ1Ž¼¼Þõɶ³Æ{u߬ Õ"Þ‡ï#`ßv¸d¢¸àUÀyŸ¢ø_H¦*zmá9Â…}ÀY‡Ê‘ì² +iáµ™cÔ(î¤]{^˜•µ 0V^·Ýô½lý¾¸"‡ +’/õr¤ëa£ñŠhÀI&‹9še×$ÍbÚñæØT1(‘ AgXìܚg—ïê¹ Ùe!ísDª:Þ‰¢ú¾†œ NF.ËZh•¿°ç1µè–óÐ/»b`Շ畈E€‹©U; —Q «1ë“KÒ®˜FØ´³H.óM´œÜ|ßq¾àbc|ø£
+<ëïÙ%nSRÏÅmÍ3«#€o¢K*6pîM „ i½Ái%u}FÞî ØU>14Kmˆ„„„UAÏ.«vtnu–QÒpë¢K¢»»5só6NàžÎ´Aв–ŽX•œˆj÷»5Mû)®ÀÄt¬ŸQ+Ô½»Èk Í0JQ“¤;o•ô÷(]}Ývvó ñRBÛ{1SnÜìË{ÅTÐ#íyŒUðŠ)À¡ÊºÎÒ‚OHDøiÆ͇”óiã¡:í‚y3¤Ë»ZFCÆ*e<#ÃÅ€j4±ÄÅ™ÇêO›F1§ÀÝìL嬕w½?lϳjRF#¦á‹ Çó.…he²ÿGãöo Â['Ö&º«ÁlÚ|­ Ï÷ßÌ[Å}{sß 3>eð§5¹r× h_ûm 2ç”fìRÚÎ<½,e¢c ¿+cÑ—e2ÐaÒN˜3óñèòýg{¾{ãEDÊ[Y ¨™\šêù1¶(ÀçÃ{þ{#:Љ1}ϯ
+V•ª½€Žhmžmÿð<ÅÚU³j‚rX7Öpfs®çç‚W@:ÀœÏ>~_ÖÀê ¼"Ô¾Ü:'n<ù‡¼MFË;xw÷ܼ»¨•^šÅŸ‹ëÉ·P;` 7Lͺ ^ðÿø¬]Ø7’Ë£š®ëG^~OÑ#茊óÇ
+~©`GCü)c¡Ô§münÄ)ae݃
+ÄóäcÎ?þYó2nvë¾x4mà·_¶¯Ê¾²Tc­¹°ú[qäqÿ~•™•´‰û7Fž# +¢¸³ñ¡÷ªçeË™õ™®Ê”‘‰Š+¸¡¾EUÍ_‚SÄÛ+ðÀ¾‹GHM·—X«¿}L¼ò•¢õÊ×ú®ŸYÁ@@–§¬\,bã
+È’…²„›…„YÈz³ÜllõœþýùþûâÞïïyžïçw»ï k1¢jÞÓ¸ª‹©©Â
+èIôŠ´IÎNZÆF÷ ‡ÁYê­õ˜ Í
+؇nˆœ¶0›vfˆWÃZêƒS¿b1A­¾øËþ)LÑÞ\G9øG6蟃¹õ±‰Ôšj,´”Ø펼zeyÞ¯ÍqêÏÂ㔟2À«9DH[¹,t½¿ko±ûVÔÄÅ&­|Rx‰RêyÓøƒmâ—3`¦5·Îxœ±ÑªQpOa1ãØ58pèR*âþS÷á/k/}oV•·ùkíù“ óÔ'—Ÿø岜SЕYã“2F^[ÆØW_&Þ̵ý´7Cú9²Ü[‘ZãbO7¥ÂSŸLZpJzÓ¶þ–ð
+ázd…]‹
+,ÑÊt’ö¨a˜³¯æaü¿QJ“jVmBͬÙ~‡»fÞ<³ýs9‹{sÊ‘´st<¶¦€‚+ôÊíü˜žZÕtÝ‹h(wú®ª½¥žª°QÎŒYÇ ¤à‘ B:z•ŠX¼7ÛS윲°€GY˜}­,²ÂiÌÛùy;„‹­ôÜwÿúøœûUÝň¶ãnÚ5Ç-2FH/ÂíÎsk=ïYeÎIÒåнÌ=ÙTdVÖ^´Õž‰æ.Àbbæã6?¯¹_×_N;« FcDÏxÖ±1ˆß–XU0–g£É5qwHÓ]暬û“Vtë÷ŸÚnæ"ZÚÎïD,ýDÐí/ š³OýÿaÞŽjÈ×3딨½·. KÙ(ÈžÂÆÐ è ª”CÆ êEÛŸØsôÒ†r3¤í(¬Òj"fFí¡OÌ;ô?Ï8¥Ü´ƒGκ¤œ¤EÒ3ððQ»!¤¡WÄLÔšœKÈ<ò©^çý/–NsÌ5£v~OÖΧ„z*œ¯°7¬õÿØ[¤ÕÅM㣃Jì|K«ö~$ß9ÝyŸ°‰iI³€xd½ÄÞ…l‡Ea +ëû5ïRŽ!¦Þ'!]Çí}5¹$¤£”%Ì”²Cëéñ6vÜj;n–çùGÞd
+‚
+²CȾ‘…¬7¹¹ AëLutŽ­¢bI dß²A a YÈž
+ðÛY•9ô…­Yô°—-À]³v„’±‰) „Š¨ÈÍ`ž·€+öí,.^æuFTð`Z?É,2[<ÏQÕËÚÿ¬`W}YÀ\?z´&œ²O? (àÞ] <˜²HyYÀ'€GP;‹˜ïËCWvT„†¬ëÑÓŒkúŸ13LÉØ%¬ˆZBÜSB²&1%mäöî-á*÷WˆÕq=­9n/câ$u´Æ¸–Öþ@lÈÙ}9óÈÝ„[Ñ{v—‘þ°BÔ|;òƒçöoËâ–Ï—á¦3/û/íÌ%„OFÛvT´0?tÚ4ÒSc«ïзߓª3^wøNÎ657I¨Áß7oI5¡w¸ºœŽq/ofu%
+n1ZÄUî®ÐZ“V˜“w ¨Q-¾6ªÇßHþÚÕá¯í©I åuép#iÊ.ÄeZy]<qèYy3½;c¥w%rÔ)¢Äl9¦çõì+Gj7^õëúùþ?¼3Ã÷"Ú‘Þ„…Û7"xß í†á æ|Â&æ—²é¢]B/XùCi £¥ì€H'^ TrÀÄ¢SHˆÙ1Ã8m_=I}
+öŠx#ïÆÕú]G>¡äFFËΩ鴎ߟֳ;²zÁP^'!¥VFïo¾~ðE^ù§À¿Û¾N˜íE7Â,{'åy»¿¥ÄTz_ßû"¾<p9o¡ß);!lÉ%¦§Ì0:¡—¢àNÀ{Ô¤ë%ØÉeFcTÍnM¡¼sBP\äíù@Ïé ¼Á_Ž©™w
+„VpŒ±ìÏP_ÙS*‚¯‘®Äxu´kûC÷åˆUÕŽ‘vW¡¬EŽd­Sã[J¨0€°ä“ˆKkˆàpMÄ\&HØ üÚñjT!ÆlÌ!Cúɾ¿l½¡4äOÏÀî9t GNû±è³v™õq5³¹àQrV„œžU¯ï, ¯
+þé´–Ö|´
+áO@OWNG4‚À<ó&p<úŽÚ˜1cšáë¦'õg·˜ê„‘Õ{`á ö’ÄLœ®¨Þœ¶súr6hx)*c=eÃ×ïkðµyÌ<
+<~zXÓ±ÚÊ1ÿd²&F·÷iÿÅÐËίý¿¶Ÿ‹ë(w>nÊžžåÏ“&»«dµ9öÚù蜕‰©I­ÛŠþš]%¥>ndu½òòÚÔÓ àžøoqãAÑ- Ä
+½É;ƒ¹š±
+±iàza¾*¸€¾z?\±³H¸QtÊå</tÙU)×±ÛOs¤hâ’jtåö|Û—ë/›Ï
+ÈäÏrN˜ v³nOÅlÎÚƘY‡ˆê{ƒ­È8Eôÿ†¤?ù /¹˜ßýccÇëãÔ;·—È5[JFãÚ[È öý¼YH9q³zÿðóÈy´¼ƒØ²þªûÜÚ éjÒŠŒXÅìà,¹J#»{ö=¿úsß/ýßå¬ÊÇM©üSP2‘s@Ø=¥®äAXÇkb!p³ÿ³_fOMei
+ö½!CcÚúpÙæï{Áóq§V½÷bàÏYF•¥oŒaõ`¯ªùáeÿßß,(°­ :um¸ùŒwŒ‘k98Wü—Øy˃Ì̵FÑ®K«û~Q«O€=\p¶³ÐõÕåßÎ [Þëâ‹*™ß ¬Ü™à_µÓo½ú¿îÏË ;Fù¸ºpqÚ7-®ÛuÑâ½ÚOK)¿°Ý*øw¨>ý‡1ûßÿï ü¯@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰ÀFÇöBùáW))E"V!CÆ 5]!¥ä—d·ƒß5 ©Œ‘*H)íiù˜¬Ï”ñÅ"¦&ç¦ôÔŒ,rZ ›!$Ÿ|IAäJŒÏå‹@c-“!dŸ"Ÿ¡gÁAJ'SÈ釯&5é •šEM¥dS²r(éé´Ü,2•–EKÍHÏ¥RshTZf¹;DËLͤR3r2(”ì,ÚûAÂdPöa[.øŸìœô D=ìEɤRhY”¬wAï ÷³‚ÞŽGj$‰HÕ¤\òÉSä¦F`•Ï&I~’£$åóW̲óÞ}-`ƒŒü7™ ˆ¹LJ+d+øLö¥òæ2rSéZ²w2¡ïÞÈ?Y%3sR©9d:9#B¦¤ç$3Èûy=’Sú±×Ÿ4r˜…v8ã,r²o:)%åp’É+ LËÎÈ ’šJ~œ•^íZahFË÷OË|O%õ[FqsÈ*nŽ{„ ¸…]˜æ\™»«CvŒŽ»Ô=;&IËú$7ß÷LP²ýT\™UtF­jnÑŠB3JFhVv-`–6,Áú”¢Ù;&ª\~È)Zážò
+CÏxE¸‰_40Î6?h¹u'`ýË]¿µ_ã{¦¤o‹J^?aåùÇ;sƒÏXÂS¢2|ZÕ³ê$/¾–® ÷T…-Rºošyqc²#gm¤%-8Í)‹º„õ¡iÎ¥Èlw-á±¢NMOÄ,®…WFa©w˜IÅ =MžÞÞ˜]Λ¤ôˆE-Šy4Ò°]ÆØœê*\i£lq.îLˆ+³¦;dTvžjy›#ª6Üp]éŸ
+Ë´©©+ã_ÀNßÔŽmgtjŠuœÑj¥ "È"²H !!d»dß7‚qêZj)" ;d½77;¢„@öÜ%÷&8?H/Îœ·gþÿßïyN3á•Þ=Š€]EMê´[ѹ» ¬Ú]’ÔçP½`{^X^QuÀlUa°–—ÝÝ̬ ë“.{kV{C#8âp¤\À¿•þ+y÷Ò'¸Ixë ^Y{Ê%ïL»µœBÀnʹ ¼Äœ¨jû/ö‰×ÜŸ2+¼Jb]ÚJ¹ y¯úÞ¬íƱàvp‘ùõ©ÕþÊWM'óßã>qSÑrIØ á³G,r¼‰D,
+1I0Ÿªó@ͪbQ5‡[ôåMË0Zµ€!eÖ­a¥W5Ý„O×þ’KƒUÛÓgB¿µŸ^³µŸHÎX Âº]‘\„nìO³+ÞM¶üð’q_Öa’¦Œ[z{áAÝqÏXó·øª–G¡ur™{móEã—ÛSm§·°Žp‹êI5$jQ⃨€èø8lQP¨Ít·Saƒ !À‚‚Ž9œtÌ~Ÿ
+(zé ¢—Bä]…uMoÑoQþ¡NÌ%n¢QÀ¢P!£´ˆšdÅ UW ;Æ0Ä¢J» ÜœÇ
+áÈ°–ŒZŒDÌ ÀBFYpþÙ,¬äÄfï]Øz͹DÇíŠ[#Šqë0½iŸ(½}NE,Æb@Í)"ÒvÌ/¨£ÃÂV:*îÊ"œê”—[CGÍ:5ˆ19yŸ¼›J;éÈP7–1ò^QCb‰{­àÑ J¨óE9âœ"B:QÚ µl®ºµ˜ß <Ú}Ü5c:¨}ZŒ8§ñðƒçÔ
+>-€í†ŒÇÈO.™¸Ç,$<VâMßÅȳºãgÃÁ ÆéܼU”ZÖ³ög¥µ3ÐõÔŒ¤_“4&æzÎG&[¾yû’Yá{Òu*6ɾJúŒò<ÈþÄÂ`ÕÎëJj‘WGÁ’VÊϯ!ÉQ±“ ß
+à¿P@Œb ¶H1pý›VõÖ6NmÚF©¨YKDõÒkâÆCÐ+À'½e›
+H‰”—Kv\9DW =h‚Ÿ±{è]øœÙûŸöÅ#©²ò1Õj»\¶¤HÀ›†öG÷2ªITëïÿÒê¥t1—ñþóí?oòþï7 ãá:| þW'²¹÷V»÷ÞëûŸ¦<LŠ•æ¥Ûû…àŸ­rB‰˜ˆÆa­×acB¤‰–ÚG>!m˜I¯Æ_õý×Ûuñ!ÒMò£š xˆ5×°ò~AjÕ^ªU‰‰ÐR2æ°Ú‚Ç—¡>”ÛÝ!ë$)´™i=Çi®î<µ ïë2ÎU†MH!¼’Xc>ªKõüˆŠ÷o'P%[V¢©ü”j\ mµiçŒoƒì¡¢5+U}ƒªR–è!¡_€
+i­½–±"•"^ŤZéï¿'¨nJ.ÇG¤FÔ®«ð¤ÑÈ9äÍ"ŽÉ(dIW9¼ðôî¢~€¬rÔÕ£W}g(Ì(µR§u›*£ºWnp¤˜=(W^UuÔ>OºQÕ5yjAFÈêïö Jðwå<=!è/…²P½¢û /;+{PgƃŠZ'a£fÖ‰×bö`<BÍøÚ¸ès!<Ú(Mz«A P>fÔ 1º­µb-V)j‹(!d¹Ä¼7PaâAkNP×’05›q”¾å,ù6±ÎíãÆDa«¨éD¸iW˜#Þ}6oT˜'yŒèç -(O÷€P¿}kYè‹£ÕèÛZŸZ0Û
+¬áÇô.Ó®­«`
+Â$ A¢êûŒ¨6Š,_À»/Úgõ|³¯{ÌtÂëàMÞ®Ù‰}ä·“£¦è€}ˆs#í#v]§wzÊS¶¬§ô0ðPÒ•½AS¢â¥×ýª']?ißךr{É€3V„K|);5™<®M©”ïà[[è>…!Щ&V°i¿êŨÆ9<!)btS6«Ž•ô)vŽ“"W&\öÀzÒ6À½(Ìr¯ýÐÁ?ÞN T;t5ŸY·÷tšFè`VÇ·A'ïÉq˜jK­õ/@ÏO‹UÐ<¸7éÁûcPS ùÆhí¼LÓú–ù~„T‰‚žÇYM©d&Ù9´_©2UÅ
+}†¸ŽÙúÂ8
+íŒI¸¾GÁ“‘Hnˆø õ“q”Ü|c¦¾—ÆQ*•Œ‹ú8G ý*3Þêq`_¥êŒÕñÒ9*Ùo©lÚË ôóèîYn5£ð ^ÀV¡QÎÎQ=[á^9GÉmJ .azvŽ’[b\IH{Q«/+þ—s¢~
+ý8šÂEM¡ÒÆÈ>Iãªi¥ú«›sRɇf"_G ×ÆmDö\ùl@0!‘´€Œãq0Ž'ÐÍ8ž@wæÊjUœzÅ5ßÍ2ÚY¶B=ÙÆL^UË@±ƒ<ÙF*€À1D°1ã,,‡šß
+
+rwȃ>ê9T§FV³—³mTdŽªyK«óÂ7êubä²Ø^"Ÿ|£2·µJ:ê-ϾQ'=§TÇ+ßx
+‘£mÄ+ë½x§§µr¶Ž
+árU£!µž­ã'ï/òhDf[ü›u$NF¦À¶]ê³u¼ ûÝ:þ/ÖQ:æ23l”KSMˆ‹Kkï ƒí_¹»”\OJù?@Ü©BøšÎb‚Ç}D¶‰ß‡‰mq
+ÃÓ¼¿O〖CÒ×ú9±j•É4!5Í
+íÎtÇ[‹-bxK­î%(Ýþ>ßAKH³rÇ^R2Wá²Z@;>9ÇØÞ2ŒtRå›»Ï]”ç-y½¾ú²N³­|ÒH û¥Ÿ|ôŸÂg¦«Ø}‘ý=1ÌEÌ$sãcÔC>º›âMNÏ0|_ß’oôPp¢ìýØ•×Ëì¿Œ—=’d·„O ;ЦÑ ðk*ÖWG7kòþü(ô²_ã-[#¨™< *+3ë¡1z]nFê7ÈOÙcö¹Œt&˜œl¥.ôPr óþTVxQ‰öf Ö˜ÖÀwçTºçV{ó*ƒ\ˆÍ¼–[%•³ÏçùbçI2ì²ûÐ ‡â­:Ä$ydïäÐ Ëäšð+梿RâI8þˆƒ¨)MWYƒ¦æû«n†¶ (Þ´J¯!Þ…Š´ ÈFÌZ/›Cø5…W½vFF†+6žžŸFOîÇ;jÃë[¹m:ô|öj§UË[®O´@Ñ-¶»;¦ÔÂì¡ú›ëÜEð¸Ë‚d|¥P<»`µÒpþSû/¢wLEê;€Ï¬N]’¸ Æw¤áw1 ÕT’,ÃE=Õ©:á¼;-Þ@§–köÐÚhÄàö1È$*l°<—5gñP„¹=·íh+`(R_j@Tâ–¨Zç:§uéÒ´ÄCj A§žNùêÚ‚µh^gbOÓv•< Švê&^œI/J¨uñ tªk [l²LøtQæïOäû¹ñP¢Gg Dñä½Õý#þ\˜Zxveeª§+…³ˆI¨§„éëZÔrÝ Žp‹,lÙ @˜]t!7ï>½Œ4Cb›TDYjh;+v¬œÝjªÂ„d>fŠ ¶ Æ&•¦xæjIxÐ ˜–¡H:€¾n@Ѓ1ëTkW;<Œ›‚ÚÖ)R2Ú™ÙÉX™EƒeĶBóvyMl ‚Ò…i¡ß}À¼Ùi¶ã+î`*+ÛN^ç‰.Äßôê·ÿ±“»ò¸l©Ú!þ89(£‘Mj=&­ÕÞ #B©Nlƒ¡äÚ$œúE*±Ê ¯pC¼§Î&U^äè…·Z²‘±€í­oLHÓ>×k±æ{,Ù—Îdj™Ž)»Â&``Å#ˆjÂÝ8…Hm?‚®DÔ F.íÕïuk™GÊ!8î·óÆkÊŸõ3Å„a­Ÿv…5ƒÌ‰4Œcö9¶ý=mÀBZ@(H:§ F¶¡‰ˆïM¢ Ž×q{†þ¥Nµ¦]•]š‹²AN8µ;³˜+84²î‚bL”âo˜‰ïV—貫ž5ã#÷§\$òVêâňü‘ȶ}fA¹Ï­Å× C ð7niN $‡HÉ_=¹6D9ÜÀÖè…Œ¨ãÎ#ºÆ‚YûX!ªâDóÕC1‘ICÇBDëÔ¸¤ôŽð^–Ž qâÃ)‹7M«(—ÞWAÄ‘¢Ÿ»ʸXWbòC*.‰ÿR¹øäÖÛ°š½BB5êLu²9ÿPNÆW†Ï¬W$¦?ûÊÞoü£¾ø/–‚¤§þS~b>GeW¶ ÆÊ$[Ù¡ 7éÉí(?8–L…æÔç³^¥ý$¿—ѿܘµ•ÔÖJ:hÊT܆°6 cÙñô½@|˜D]šû®N‰ƒb–ZÙá©âu(9N´ ÍH MÞæ“ ð$¥³áòÔPø”Ø!‰ÇÉ…T­uSÜÙðàËν¡jg¢«ýÂ¥@ªÐãØŸº€”tø¹JÉ×ýUCÙʇ µ A̱C(ƒiÚÚ8A~[q}Ò¯¯_fÚˆqoyKÕ?=åçÍy/ ¯#è='
+_êA ÓÇ øœ)^U%ò¯#Ý. §çv–«»Z/ ï]´JžóS"†ÐõŽãrKŠ†I)Ô±ž¦Kê*#œìrëÕ–Žb4Öú:™á8þÚÃHßZzØ>Ø7Ûr"Û„ïÈƺå瘖—@D³ºWTX¢éFT„qB
+5U>d7UzGªÞ_B²K‘£»­Ä˜”ãðëâµa'Á ¶á«ZRÂTö`³]õÅáPTϳEQZ! ]ô`#.ù÷3‰ñecRÙ‡œ'Ðu¸Ýî¨ç òý ã5þùøïÿÿóç_K1¹GÐ’Èó‘&þªÉ80ð„3¤44'G¼c¯,‰ÔD9&„C¸dsåjŠÄ\ö"þ°/HTpFQ0zA¢R(ŒÅ§YS¦%¢è%*楾@$¸:1ÄuN†ÔxMk3} ãŒ_e€ a]£½‘ ×Ä?EµÈñ¡ªëÇsЗ:Mæ"û•ù‡½‹„† .—ˆÕNDˆE±Rµ‘û½ªÕ )³ÞLu›c…5Å™è å$íÎëYx¡ñS ›ˆ€‹bêä¹Ìû˯ùkç¼öA—cŒ-Õ4¾÷³ÎœÅàXiì/½ÒB¯6ª46s^ÈU•¢¤0xNˆí¡ÆD¡ÂQ£g;Ïä§9”ˆˆñºA1WŠÐyDð¢õ‚ìûó„áZÈ™>Jf;"ŽŽÒfñZ'C7Pø¹þш[ â¶eèDS´änêäìX‘Ìm] "¡€1¿Þ JѳÚ_¤’íòìNuÊÏû*έØ}lÊ“-BÖâgçs¦¥¶³ >—É€ñ£IË™y-&Ir× á H$O\ ›DM ª
+§áis~u,H™¹ùu™i¦ë¸ù¥þ)F¦ì·‰øH›ÎšüSoÁÁ™Ý†}&åôŒ„ Žz2ù·sõµ“7ë:Ê"T`]ð¡ÐæÇVòÆM)
+"ÄøÆ·¥MjŒ.RÌà÷!W CYéHÐÂË¿Å5e 2&³™‰é2S€¢tcòãRJ*ð[¶þ tä"ÊË»‡ÖMû¦É@ Š3šÈ
+9 åö¶Q ßGПO§eôYy=nw’W%²_ØC˜´²"¥Ÿ]å­÷'w‚Šœ¦eVùáæ¾Qžm¨Ü„u‰i†, BçXŒ˜¿/#¬Ýˆoi UŒÚ6´ì¨5Œ¤‡Pž–ÞÁp~KY·ø m´ƒaå‚K)AÛ[†ö\F°ç5ïÐ%–¼ ǧV*ƒ_Ž¾yqÅ4ý”§JÈÿp¡iq’Â)—v‘ðQ6LÌœ|hy“AÕ­Õ!çð¡ŒÌôðl-åi°«{`ãéî_äC®f½ö­Oh÷€ÿ1ÂYÁ(V-Gµæ»cHªŒ&ܨ¶“¼Dα6¸ÊHø! ß!IöB> eûSüDÉL’ã>SÍwye\ILÅéiè7èe>PƒÑrÈ]6+ˆ„È6üåI«m#fÑó-ðŒ!JÌØšK õ2cÇl'?ĈÓRÉ. -]’Z zÙ‡3}ìDe…b8*“ZV¯Ãõ½@=¢ð’êqFC¥h²mB]hÇIB9P­‡éF1
+m…Çã«úC©¹*ÕõsÚÂ("GœÓ¼ÆPŽÒ[“Îýøµö¤¶×If+ʃ͢-·¹‚NÉž:Q/tût]ã¾o@Yë£Õ‘—muƒý‰·µ¾ƒ˜I‰ø7DÔ!<›ÙÅìúûÚå%$;©©$ÔvÎ…´BdgãÔõ ™Õaã÷Ä@Š†hÚÓí9ùoÆËÉŽä¢'À Жû"Ò@‘*O
+Ìr^LŸ¹6‡ª0Úè€@~£a¾)©A*öN¡wξ ìè”1 ;Â*f5…ÂCÁ5å´¢ÄE5ˆ‰håÐ ¨œ¼ãVÌ ¼‘ðgˆé‚£ŒkŽ–=± QK—0à«)ˆ‘ÂàÁ©Éª7—†Ã SÐKw@/«O&+Y,t@
+ÿàÁ5\!‰àÊe§¬„?5½b™:%º .H¶ö™šÉuÂìÍRÊÍxÊi‚„@Ѓ} õ'ÄÐò„RV¥"Cé
+ÒåxE«3Ú1¥3\8¤6¶7är)òœDÆYÝãgŽ‰²/!µIŠ%ß³ƒüòiQµÚ µ]=ÒÓþÿIþL”tþ¸õx'¸Ó¦ý±ñèä'jÐËÞÙ —¸Æa5!áD[1ßÆN~©Ë0DAdY¼¶íåY0‚Ë»§¬÷Dæzò¿ÄzI8Kä£o™0F+hJq »ÔPŽjœþ9éòÃk`iíK#2 þ4­¦¦ÃöÊý„ !К.okJ˜}
+©›6ˆ^Ÿú˜9H:‚úçöQ¼&Íeº&ûRÅ[ÍüþÔõPà£1eÓÅǨ÷Ü{s
+UëÝ©aEØõdKL,ä`üZó^
+V1™ó¹Ø^Á€äÝ9`F¼‚##3jÍd£á¢²íÈ á’Óô¯Ð)–¾@4…Ñ—ìÖͨ“Ÿ+Çäm¢ø=Œ¯õ¶A¨±dÛ#ÔY¦*SÂÏ1fJðÿ£${9Ä•o« :¿³XÑô;‚¸T4›ô¦n¢£VbŸâÿ°Þ2ééœ3c·<“––ºÉ Aæ_äc¿
+KÍôâsaþN`77ÂD4tž¶§Ñ
+âs]|PÆÚ*¡’Pk· ¬»
+T•0á,¼¸ñbØ-ؼ„PÆÂC;ÞEÑY“§˜»†A;hçD&— ¬ZºÑÛVÑkzÃN`U1ôc<¥@ÚN'ð$)î¸y=GáË HïUf/:FÅ|
+¶D䄺„2¤„D†²Ú©LÒ–fiÝa›x$Sí¼Ze¿šV× ,ÄØdžÜÅä
+r›Ž¿IÚÞo=†Ò+ÈÍ›_­©¶ù¯ã¾ÿñŸý÷‡ìëÇݲÈ'>,¶@”Æ
+Ü}M^ Cò!z‚LÙMŸŒ<Œ/;Ü…]ð§e%/o¯ÁðqüW*þ^§þ!Þ…ŒXþ}á©$}uÔ.jÀefgÏÔ¼Èë‹nL&E Ñã§Ý¿ô9Í`¥µÖÇŽ;eP×Î
+Â+hŽå…ž CÛ âáNmaé™ cÇÏ]ÁŒQfšÓØ+›å½˜]‚£ôØY7L‹íÜg¬l“u“ÔÙgïˆß6˜Ù¤·®ž©øwÕ©¼]ÝÅG ’Ø™n{v¹]¨È±ì£8ä›o˾ܙ¥™‰tû£-4ã¸x'ØP D™ª­pëúìÊSÞõç‚ 0už ‚£`Ö)Z=8Æ€ÀÜ®aJ`5v)ØÞà¹$õð×(ªh÷æù»D¶ >ò“a>²Ov/ÖvXÖ¤Ã×I@sQùr| SÁT`@)R:‡[3—ƒd.S0¢ˆ\žËMxoÂÜÁu¼ºañüÚ
+ó!Ì_=œùïïL±>¬§?Ø"ïr5—Óþ8Ã%#\f{~ y††Ãë»Î94D²‡%(>je§Æ9%8“aOïk—±©1í|Š©<Zm­š6™%‚Û|ÿÛr»=ùX£pw-¸!v `Õˆò˜aŸ‚x+a‡'Œ…Àµ¶Ê”Mòè'¼Œ3' íYo9 ½XDó8,CüÓDíß]Yìå‚oV–æZÓ a&ÃíØͶ ”´¹+² ÄÖW¬-fcqeÈP-¶kÈCK$©Åi ŽXÐΞòúÔPSAòÌËö%"""FK±E¹`=p%:Í‚9AE%¬ð霥“Ñácû†õH@ hDg·ì\Ø>•ÑmŠÓå…ê<'ƒ26o'…ZºŠ¦"PLÆrrø‚ÄNÀÐviÊ,b œò˦]Á$Ç·V?g³gX„¿HÜÚnɺ‰êí&Ñ6"jŠ³hÐ Ì <“K«L†H!)X¿i!mð9†Ëž}#@¬{Ä­Å´w]U¼å·cAø‡0Šc1)ª¬ y5Åì@öî‰
+‚rT}:ç"EÜKÙηê©z… :fÛïN¸-¥ÆÏ5g;Q(òL¶ûP~ìõ=g½¢±Žßâ¸ä#ØLFÄ»ŠÖÕ[*A±fƒo·¢ÁöPƒ:ì \p‘SS°uš¥+ì¯Ãh²`JšËR_éí
+±nžiÒ=çĵº  B+¥z” $³ÊZVlmÚ_:3? ˆ€$%X㧫0 B®¬Õ’;÷=YoÊ”ÿþZ§Vì‹ó£C[Ô“d3bŸº„A¨úÎç“m€ < §Ã®O 
+E&f? ñb>c„Ú–ñ6ÅÖgœkt!{ž¥WèßØ€ç
+?ü˜¬Î¢ÕlÑó7’8ˆ!“%»¹GlY&²gûâ=ô ¢¢4ÁîÊòL«cq/Ìûo>¾Ca±=,¶WB‚3d*íú¹¿O]Áˆx”ÎÝÞ&„txs[$UõX­YæyAˆ
+8
+´tz¸BeÂÌvý~ùsgxA8$Ï!9ãŸÛpM@'•¼$©9\Ä3-qNdË:ö»d³~¾ eËS1ë!ß=
+ª«VhÎg=> •
+‡&Qo O­úøÆpjŒÿø‹Iþó?þø¿ÌT×':cŸ²¿1²
+<‡GQËÆžŒÆ>ØG]ŠÓ¬(’  =¾KŒÊ»(ø¥UO?J–/ñ‹TòÝwØBx!‰I»» ä#øÉ÷Úý«ÊÆÝ×F~˜c$Ú]…/S§. kÇ_îreÍåEò]
+såð¥¼×Iøz¢<Y)Ú±ÊÓè6—ÍÈ‹ÿÂp™›Æ('íèŽUÍÆ•x*JëH†˜2÷a¹hƇdU0Çùx|ÂeàhØ“n$ZBø”4‹7 kBZAƒC”¬cdRØ2€ J]šq°5?×Q=q#NËî¬SM^€Q[È…ÂCH”Öº~>5TRÉÚKÉ£[—…eEQ~›©ì¹ý¨ æœ‚„Îì¢ÛH´ÕSÅÂ`3T­ùÃ;óqRˆ ‘è%Å"îÔÇÀðBQ2¦ì^EK‚R Kç$'@‚òX¨ŠV[)JƱ{k:@ü¨"3ÏË-ß}GY«!­¸aêÒJ¨Þð8´¢ `pH’Ft"mu7,P`‹iB³ç6Ö¨Ö85Ü%°1ªH\ðúñÒ#K¼¤t©8lÂɯLâà2ŒÉ‘èó”x
+½HhQ(?ÊÞñ±]viv>3~x‹ÔùmBPleá»×“Lò`²#Œ Š5¼ bƒH©×Ž?¤öô’äÓ~ÐoaPE||.ûõyÞEgT\ eg+²l?$J¡¢hOŸÛ8/ƒÏRÂnç\6+ ‘1þ<ðt³úòêÄc/ã`‡#dö*"ÛÊBd´‚â·ëâ¤`ÙãÒX³Óp¿S­qÃÆÐF‡°ã6,Å­qÈ[ÇŸ‡´?‚£«¿AÚ'q4$´ûŒÓ쨓ãô¢UYÞµA]ÐíÁ·#ý(0+ltY…`R¨xäªX÷Ë‘ìPÖX¯fš6OÄþ5—À8]ÀÀsÇq”÷«
+crñï°IàÓ—Y¡Ø4 6Ôº½ìëŠÌº0“VpŽv·j³Ö\ëhÀi/†én:,Û·‰DÌšgÚ:Ñ™W5ùþÌÂ:ž9/‚Kgqè’È_ Ô¨¯lÇòŽßÁÌ(Š¦¨9ÄaUt69\l÷  YÕÝ÷ÅPÁ°³1Œ
+–%¤0œbX]¦5öàRGÄ©’,‰Ô¦kÑ´ññB.d8G6%ül~Ô áñSùÚL)G#Fö$6ÒKû¨¯§ë׿‹'pSË^·‡ø¯ÏAŒïÊ(¸L²«JGêØq$œ­ãëÐ(þR³øž†ä?öë•ÃÀ&^÷ŽÊ1l^cô6­Ò–Ï’Û陲$÷» y#ݫߥÊôQèín\@m
+ýQ…Êb+—n”G´¯é
+…rS^l…J
+$Œ³ó,4†U-¯æß!*u1ºd0Då§Ê0gõn“åÃúã¨WÓáC \Ë¿ƒÞÀLèéà=@ YŒ Á*âzD{XÝ~1L`š·YÕ©LO˹Ãöãå’dÇ­ÑxZA¿ 9–‡Ú…§WûŸú$ ^©«XÑÏñ"ÜÆe‘@"?òÖ!”3þ¾‚ñ_ñ6êR¨>2’NdæˆG0äï²$âHA”( áyø9Ai"«ÑQ Â,Œ‘O\çǹÈ7û”û‡8‘åeùë
+LùD}Ý€æ JUIli(@ÇclsÎ`yYèº|œà"7WÏSp¹”°ßŒ}í™/ýüçTÓf,ëHt‘r½¼H|haÝúI°4âŒýùÃfc°¤®1w7Ì¢'-¦;à„)Kbd¥d
+˜œ_ç^ô·¶¾¾#ÀÓÖ»4²èÄÑÂôð2°›2­ø¤Àã1ù¼*þ‚}… Úz>E ^‚»Ðig/øiW–œ—ðQ0^Uaà Jðš$Í5xŠr¢X·3R“ŒIÀÔÓ€º“&ªâ¼j«œC’1Õ°iRÖŒMÂë¯Mø`ÀS[ÙE×s:Ðdæ‘^Ηé0^ç¿*J¥§G ¶¨°èŽû9ãc64Ýe7üØP(Yô¯$ ¹ã#ÝVaµ Ÿ{Ñ!BR½¤!ã)gÞ‹ø\·HÖ …Pãë¡( s¬Ì²_;Â:ÌÕuv’-Œ
+r67%Ä^pS°æñ°É>-…3À]ða³Š(¶ôõ©®ŸÁŸØÐSŸ%–qPc=–ø§Š¶"C
+žÎý‹8ê ÷†ÕÁ ÂÁK”20R
+ þª¬@
+vÖËeÛX1Dͬ{ Ï+…òÂP+mqó½èÔI »fíÛEj~}ƒ¾ä3þTö0Â(ã`ñ'ËÖ’-Q¯»ÏšE"“ýlçÎÏ™F•É<Ö±èIÉqÚ¡—ß©(ºàAÌa†¸ 0'ßen‰¦pwªTO : ¶JðSA«µŽÙI¤cíÌš?è"LYyÕàXB1Ò~V¼?Zf[%h+n¨àèçðØÄh@þ}¤{ÅüPµT±ót
+ž•¨7~7ò¶q• /˜
+èyßó)”rÖÂØ¡$„Úáy†ÂžüòËÀøÂoÎÕ‹’ð–'¨åµŠpòÀ'±Æy)š%8.ÈÍg9¤ ©ôéYÔ<8O÷–”ðU¬.¡-Ú{QCë$X/‚¸Q;D™FV%¨¤©7_ý¸9ŽU"GÍÔx¹ßfº ·l’tmrõOá{;/5­Ànù‡Î+o' óv¢*N ç§üj¹|ñ~Uñ ËZv~î A²ˆ
+§»çxÌ€mÆ?e—Äð¡ô…?g4î¨HLú÷Á‚ú9rn,9ŸGÊh‹X?;©‹>E“µŠÐ¿cÑýœª0Eï^~ˆM~Nñt•`Ÿ52Çå7y±c q|¸÷"DŠb0Ç°¦¦ñ†HBm«¨•HóÄÈÉÖǤ¼†lÀ¸žD¨Á¾Ó-«þtþB³jÉåPâ]Žro±Fq;ç>JÈ èh>Žmj`àÆI—ÜŸº
+EeEW<Ùê?cN˜Áî—]ah6žÿX%Yhè¨~u·ü`|(Éh`•òö÷§®EµvˆWõücšãꢪq•`¥2¯Ù.¡K5bˆ©°ý©e¤1zy ¡úPû)=j`à þž4÷+(ÿ| ¥º@¢ûƒY@†VÓ0¨cË|µ’S|¬ä^¦Ôm9¬4ÞJŸ*”I:‚,ª?vebKÙ¨Ç>³+©½Uåþ¥¢ézk‚¡9#{H^éIx49ƒ cÔó9†#S¯m”fÞÚhQ ­”ë,lm‚¤òËõÆj˜r ¼ŸÎýÙ&#¹¬sO¹ÉIÙn
+²?¦–®v¢u¿ 0„ðQ«8R”Œ‡EËÉæÎH@TR¶Ä7°2ì¸åc N;X…Âœ[M›Ž«ÅŸÍ u—˜e­Èög17ÿ| ‹
+Ê
+”|¬ „½QðQ7äX$8öÂõ1BÏÞÒ|N+r$>)˜·MX–¹ ë]ÔºÎQùåæºê· ²¨ «C@£µý©—ÆA.À¥ìœ—öBªÖ2ü0¬Ùp:¡ËQíÏÞù¹—ÿET”ùÍÚw7H|E–O7FæI2:f/Ÿ¾B ~:S«Ñ.âß kÄ·BX±(t^§±xpÌ£ï›ó¥!/…t6¢v  ÷§ÕŸÈJœ7hY8mCOâFêQ 5J@÷ ãî• 4±Ý¦ã1ôâ aw=
+è´bEÕ(©;p¯òlzŽ­ liÑU!‚2E6ªÐõݘ‚´ ?È}¡Ø+昰·‚^\ÃÆ/׳aœ…¸1C`²ÀÊØÙ„Ï¿<jáùåQÓóœ£3^,Þë0þ¦i—a] ÖûS/*p‰\‘)ŸvÌ1¨ŒÆãíw!òBí±ˆýsà­/Þaž‰¤CÆÍ1hCB“k¨|÷B”Bødîýc h&ëÒ"ÄxIäB1½c¥œ".rby<%†,¶à!‘09@3Ö1XÞO*§}i°ZâDˆôÚ$“vËÒiº>œˆëC YâPFoéî^”´ôÍj¾nʆíu +‘6ˆÅÖ% ÏlF/NÐ7Iü¸ $‡Ø÷á·ì"¬Õ\nDý¯`®†uÒÊ0™ —ªŠÌ2¥ýôÊ!JÁÕ|{OØ`«åBè›íã5ÉÞsÔê<æµàç]Ž¾ñÞtôß™§Ó_3ì„|«ÔŸ7&æ‹ÁŇ.¥ácÞqg™x:ÛMµ¡ú06â±·©Qçmâ«’‘}þ„½,;ªºoŽöHØÆÞB^U•r”* ÆØ»¯†gáOQ¥Å„†¨áÜ"3l×öjðz™Ìöš5¦¥Žr÷¨üÅDüªT»1‚•Ž†v¬s89_u2Ì—©yšae˜ 9“ð¶õ’1êòd2ÝÖ;A_ ÿáÆÀ+ô2Bد⭠³} ­É N€cÐ1ÚÃÓàQ̹ Ïd5+×L–§€ f¼0îìAW°[¼æ-`Ô>€‹˜ñZLVŽž8¡³8#0¶@å¾ “.
+ãáÖ¡¬b€m‘‘­G•^àss¯t:gR+Î2PÃWáÈ)Yç®í#ÁeaЪqÉÈ/¤ô)Ú udêçá¼<¥zèG$jã‰åç
+"Åä™Îì}a-)Dºáê°ð`z“¼¬<E7Â8×-ôÚCÔNÈŸõ)uv@æs›wçp –§f_.‚Ô1 'mø‰T2°
+ÖöCG$'äúrÑtá
+ý§eDC\6 hŽ Q–å74yiõÚ<ù#¿ëçCü‹`
+iÉ}õóÓ(ž{;È‘{}DG—–;OPïœÒÔ¾µƒŠQEËiObӌ֌\6mV¨¦ÔÀ¬4Òç¸Ftõ~U£’2”+Oj­ðE² ýÔ棯zJ#±aÏæÈå0’êöF×€cRÁ¸Út½Š”ÍÂpã—?|h4Û{½ ¹<5”/·ý©§ë‹ûÞR
+\¢o þºó}® 0õâ.^!™ö£>¼´i+¦©E6i…ÈD‚,ºÕþ•ÖdÒÂ<bº¬4 ;myÔg؃"›Ç:…`Àˆß…¾02Ägý ø®${}¹ƒ&fÍFyŒI³ÊÊ{t‚~ßÑ·CÈÄWW³¤g"HƒÝ ”¸7QÎ
+[‹V¹aOŸ…7´…´B:Ê»†Ül—ÆBçVv •G+¢4÷‰“\íZÁÆuùÿÙ-Ûðkõ¯šÿ÷W¦ eG
+G„£8W4Zv­©Ó|„'D¦$;aÇ)qÕ
+¹ zQ|Î,Ø=]êà“w¼Šªè‚ÌgŠ7Ü«ÎAعNy†
+>oƒøwK, `øú[­*aÖ±ƒŽ;} úxçâ×û€(šN]Ã_öîxᧂ’Ÿ!î(Tö<U!ø©”q ¯3‚åOMF^ÈŸõ)4bÞü…{²UUH4æ‘ömÐv!Ê;æ
+ÑœD܃ ¯ o“&¦y}‰Q¦ žÒöLRÀ¨6Ì딲‹ð4S‘*Üqðz‚wbN`@Ïš6/èlZ=\:‰D‘Þꨮ¬(ÃòTy7:Tixû®VÒ~ËÒR‚ÐTãšøG]!È+
+Ï[÷2û-Ī5¢Œ£FlÚ?‡]›Zvƒo2ÆÛ\=˜¸a@Ô‚©#Ásôp'ñòò3Yg›€a¿Q#'èšësõjBžï}¼3<Ïí§Lñëú¹~7ýO7†fâò²l#ýºBÈYè¶`©jIžAV„¥
+Áä°á1ú6â‹(îÞVáàp(1¢!¶A²õ–/"R´ŒÂ[ž—ÙÐü‹Ö
+s¾{áµq‘¼<©§pN½‰’$B˜Ì4ÃÒ¿ð ˜îPM01eÌ{×<ø)ÊýÙù/Cøa˜åW‡J±o<¸ 3ûÂ.€å‚×íá r#:á¢Ü
+XŒxùæ¯Æ`ô¤ùÞ¶œ‰á6uæÃ_È$s…¯éÑ7-—fzæç2×ï0Zd¸²í=ÖmØuØsòð ¡ñBèýäš©à ÄÝrŽ
+ÓŠ±2ÄØ•mDl{¿Iø¬Mݸ‰ì1öçÆÙ)ÅÂBݘ¨_ïl§9ÑüमجéÌ+‹Ç²õ6ÏçPåS˜æjÚ·1§õ§È ¥¶‘î@ù¡dC£™æ¸[éoöš'¦ÑyV!ÃâT–€§'¿ òŽ#'…åœð=·O 1䂳o;K¾€¾Þ)¦jþ×ßðçþùñ׿ۤ É.&ÍiÔÙŽ0)Òñ¸ZÀßQ“Vªª›ŠútA jEP ”`%3(ì’Éän7Ö¯¸8Ó—Y´@ÉB<ÀÍç†(ú±ŠÜ&­徭Å€XÉ)ÒäÚs‡Ì$ÃAäí)•`Fó(ñÃVl@IÄŽB²šÜ˶;îwØæ™c¦Êíî6Š…³Ü¿*HyáÿmTŒ ŠI‹ëÏÝPÌ6üÌlœs”¶cùŸ7zþqdVçö„&IPôW'_¾-ìºRéT~•x¬ãz&eI=×
+6ìH|š_¾tu”HÙ!vÅ rf-A¸Óš¾ö
+W—fu §é ïPQß}G:B®@»ŠÝ¸‰ˆ2*½;‰¬Œ‘G·ÝƒÜÜ9rŠ§ØÒˆsª9ØwC„yÕ`‹•ÖÁ¨ÚÓáUÔ
+µJ¨6è,òŠaš²õ!B:k*€ô)fàü\ÜSù-SdºuÃ`êÄN -„Z±E`ÙÛYsJš9ªA([”KNÕĈŠÃTLÀÜCqÑ–Œ¨:B+Ë'­;£¦¬PÞ!ì=ã¬Hvˆp1„<±"Ëâ/AXÞ cDY,.Ç vC$´:1ž„bP.&ŽqKÛЉ‰pkÞÓ9 ¿»Á Ìn–ÖEj Á@5!¨ÕÚ‰]
+%Ôà!ì v‰‡KÓÝW…ÛTkÆ\ö;º]œjŽV=ù®Èë‘ nÓZ·’Þ=¯Ä×ÿßš•!–K õd)¹ÀÅ›zä¿àèÙq`{¿®ü%¢ruÈ€Z)Tsy0nY¶B¤C&oøЊŸqî>/!5ÎUí($ ‹/7\«A`3f,~{nž=49”­ì~
+–6{8¦Jåiæi^Gž
+wÂÙ›ï¯ tFTÅr¿î}JÔ’>”¯ÑgAd¡1Ç€(w='ѬsPš´¹.Ù%Øõí3‘œÛYb‹jpRå¬~m-ß@˜Qî3¦ÝH”pŒv—AÞ¼€®2Ì bpËÕ¿$
+¸/w²—
+É•ŒúÙV΢µó»ÄrZ¬3h
+ñ'åª0Úþ Ô§`G½‰Y—eñô,)¸BÏ<"ßècUEh-˜Yäl%愶é,2Ü1›Ð# üQÂc&;ISÎ31˜Ç¿‘|KGûƒÉÙeS¸\Èld¡ 3Þ@Cü©&ШÞî¾€¾n@Ç…Y#R–` ‘eäosög$ÙÈ_â&
+hwG¼Z>En%Ê߶6­‚XΈX†¬éÝÕXÙ,v+_]Ãê®Z
+k¬óê<øyLaI¸ÅC·¯ßX?ìbâ#Ó^Ôd¶”â4 ©îÝì Š81þ³çŠñfou"¾Zä_ß9ÛðzØ[§ÝôÇüô¹÷¼C<#¾±GÛÏ0å>ñ0­–½çP€]1-!Aï4oC¨/77„}b?¢0nH™â…úØN2hÜ”ùôNFeΪ˜
+Ïhþâ ‡,òV@ªÕ° Í~¡°4D‡!.“‡ÞÝ·ïhÈÞ8§±ô²i¾¶¦Õç±ûŽ({• 3Tê¹:'1Ge“¨o•˜¬ªóšgÿÏÕã×ÝGx†ÆfzÛrcúŠ Š™
+H‰Œ—Mn%9„Oà;xÝÀ$$Š¤¤e£fémŸ 1³êZÎýçcJzÝ/SiT¡þü–D2 M³H+-¹êç×Ç?òç¿?²v;²©×¢9'ÿüWnåPëÒjµ®êŸ?@|Ñ¥Ôdžò瀴jU“hïÒK͵‹hku@Äjvm&E¢Gr‘^T¼µÏ?ÇM¥(¿[mU†3Z³¦-•qŠZîR¥wŽ=RšˆÔ\$õñÔ»JÎVû2oª®<®Z*íéœÖrËD‘m=¦U-^Jí>ÃîÎOj—S­ÅÍ{ukŸ?>v 9Ü2ØRKêòù×Çî29L<ÕÄóªËƒåPO¤Xª¶ ü™_¸;çR…û[.¥¼D4/ºPBŽÚR"Ì
+Å&%.Ä’£7Ž,ÞKk²…î&D
+A ^1}Ïâ |„×£ð¨Ò”¿6ÇýÜax¸[ÊÖÉ|>©¡ä¬ä¼­L§Ôͺ÷x ñø(×Âé’Ý*„î©Ž‡ëÑ8Ö‹§4™8’ræ*ž5¬yMB^Ú€DGôJ9ŒOF½*¼¨]¹Ì¹º)Bã>Ó`‚Ô`ªÍ×¤Ö 2?R¢8™B/šfÉÄMטÔAx@%˜ˆÚÀ—óœÆëˆÃà0Õ81UÉ4+¿† UÿÔ°¿6h)v¥êÞÆk©’5è$u
+~?çF›ûknôÛEu£ñ=;·f¸çøû¦ú±(”â@ZÜAŸ†ˆcÒ„<ú/,6S‘y†òo@'„baŽ[W_çð_QO€"ŒÐ˜m&ÂDzz ÀåPb²)ÓÞ3ÜH> É礘›I;©]Æ<&S
+—û‚PªÔë\çb¯QITûVÊY¤Ý1_O˜(|F’í4q' ¥ìŒ¸õV0h©¥³GËY†ÀÁÌ 
+5=]Ð`#ÇØk\Û<…íæ¦p³39ŒU>‘óà™d’ó´Å"R_©ØS©¾/ùµm´fW”ÅÆ̧IRD\e3G)b÷jX‰Ù<Ü S5(-Á<;„Q˜ëŽ®gìgÆF”cÈKç˜íx!¦Xç ›8’=oJÂÝ̧[ÔQŽ(
+‰i6xqÇÀßœ¸•\ÔQó+æÎ@žË¾…_(kähþg:ƒVœkFZ{,€ÉDŠpO«à >2û9|­WTÉJW¶ú yl8§ëZ´©÷}½:ùÇÖÆrÇö¶]ÓN¢ãëxyAææk2e?o,Æ­ÅVˆkUebc {2¯Šm[D´³
+.3²:Ž¡åùŒY²ô†NÆla ¹pB8™È ¼Ž2eŒ©”°î«Åo E ÒÙzX쯽VüȘP-²…ŠVl@ô¦*¿û¹g y [ÑΙCpR ‚YPŒ0Võã5òoÖŒ©‡DÖ´fõЈ·>Î1¶ÂD‹èZ@µ†97~­«j’ý’ù±Ì«8ô~
+°?'¸c•Ï/ˆÁÊ„¹˜qSØ[4E¬'V3t•Ùºˆ!ŒX4¾LJŒEÙ&ÑM£%êËÈiw!`ëmËA=×5L)Ä1osÊ}_‘ãXw"ùu »Æ®Ã ž£å¢ÅqUˆÎ+¬wa¿«ß7zŽáPl¹j¤­~ˆ£_éþ2t6¡Ï¬2ÈÊ”‹’bñãpN0H™íZÖ”f¡ŠTá·lBâÕ”‰h9ËØšNÍLuN" dI‘í¼¥Ã‚ä4•²iá;ÐÅ5} PN™£²ŽÔ_!¯x
+Ö+úì…8cÄ\¿Á\ŸMœŒeJÖÒ$F¢¨ˆ •
+YÖÐODcŒ4rH°Š—ë¶WSJ­Ûéíœy]m9?°:™ÛTöÆZqbÓˆsúÁ¥ìs‚´0]³BÖ5ßðëe¥áºÙ
+7µíœàÏRÆ“Ñüu¨Ó̓
+.kTé6‰qsŒ@H]¢Ä¸Á²uŒwÞcÃQ®)Ë…ÜcÒEÿ•,[Çx×ö»
+Å ¯&´4ÃèÒ]º?nj
+Ñ”0d[Ÿ4©îË·¯Ânj°É19Ömª¥²?†¦pôZªÌuŽ$ÂàÛ Â8'sWŒ LŠê”0y ‹‚q±|”ûvŸe6Щójc1Ò›wØln:.ŠÌ œo}ގîäYÍC;ÍúÓ œ‚JÞ¢Ór_¶Ñ1oÏ è›cG+è?¹AWgKò>1T½°«³O›ýøf ¾–zD!/osܳX?¼OÎ{;äKH"‡ž®†ÝÃ{•…Î'e–q…Ú)ýòG¬"™H&£¤‰¢gÇ«^åkXƨ¿?†oÁ#è`²³sQ"åà”æÒ¯…`$œ³]!;>ÔBáð©0|¢ÈK-Lª5 3ã2MËñ¯RY¯ž@»6 C¹] ="\…²¬µÅ°Œ›D¶ütÏbåã¸ÌZæ×Ò"³†Â(e91ç¤$€­­àâ­z7ŽŽà´cç$ößž{DÔ—Æm½\õ…NÆàUäKº<Ç£9EW1Üý%Ú«zx+͇»VB%æÅag ¥l<½³X7k%°â½·FîÌ\к^ªÈsùŒ:ïиnùØF7h¹yþXWœ@»‚û‘Z¨ÊÚ> :,Ò· ¾$I¢49ÑIJqT˜»6`$è
+Óç$»qéJ³¤¾ƒ¼3q*#‚¼
+Å‚¼‚z•bÝÊ{'…†wBF¹yßBتe'7÷ùÓ´Iï2C¾ãï\¾ÚgÐ#-¿=A݃ào?é4|b“F ³íváýĆ“=LÌH¥côBç–CŽÂú UÒ½Ax •ŽLË
+ ù:¶# YFE
+¬~&"çYPžNwÉÏÕ2iŽêÀüom÷ñ¢\OÂirÊC*…ï¹É˜[ßXés1“©ìG¼ ²ü°Xù±)Žg0{Ž¥‡ é¶u)tE‰»An2 ¸—ùü•Õ{IrÞ}ÈSkœdÇðickÇÎäò3û›N[-|š¹Šòî>æ™ _ 'æœôõÕ€äôn Ú’‘A™Z#²X"¨Ñë@|uY!W4Já¨#}[7W •Z”j¿«øó1ô­$‘·TQx©òÅmò DLTŸaˬÓO ÉPnÖÀBÌWÊð :Š‘Íwµ¶¸øsºöƒm}3Åã¹¥º;Á!}wrŽözf«9&«lÙË*Áµ&½vIpT4Ò1tw —çÀ ”ãQ— OïP¾b\#/çÊn!’˜ EO-V¤{p¶ª(„=5Æê7Åxß‚äRQqÙs6ýÈ4ò›Ãë[-õiЦd§hÏa(¬gê­¬€*O¥‹Ó½æªR[²Šx%R‹Ÿ÷ÁP!zO¾¯„Ÿà’¼AÐL[ɵl§,Å¡.¬?0 Q>!‹…µsZfè(‡3ƒIH²ƒ9ÿ6šÞKJÆB¨¸¼Úd°œïlã´ ±Â÷°R›Â&ÓîÐbù%Ÿ\¸Òƒ,þ1 §c ‰P`msCþŹ¤ö–Œ™³³‰Õ»Ö&o‡Ú,k‚PˆÒW’9=™£¯óå•5+·ý2“ÜO•ÖŒ!íÏaƒH½ ¡nWÆ#²NrJüršãltà$² èÔƒyô S‡æºêåf7§`OƒúöÌ4kèø2óë'÷"y]CCSNHF?B·X´ñ#”¤~ïonüýí¿xÀŸùõ›zªtÍ
+îwúòéßÜÿÇß?ýðŸÙÏ6«ZH¾Ù¾oÞÕÕ9
+úBË‚:î(tÞ¼%‡¦À0påpŸ-U 3kme‡¡fSKí²Ýy|mø<ÄuÚÞž™®aþ(0”IT%eŽmYH
+“,½OɆVƒì§²
+{;|ôðø’7° ´û©Ì}“ø’&Ú@F¨Ê‹zXÚÅ_#Y̔޶+û 1–
+ŒcŽo!^Î[­ös¶Šï¯Ù:çôQ[îÉÙúxOñ!ä[©þý›¡™~Óç+h“Fýù8œ÷õK&¥Èd¶®?vaˆôB %œ+…‘óT E™Óʆ\92¸®È'®UÇô™5ôo´yL‡Œ[SÏ2u¨ü.wôÔfÐ>#6¶Nc:¿¦ñ•±óá1t{ø¨þ"w0?‹¸ø‹ÑH>1_q妿ø£T´!¼5€’¬IŽ…*
+Õ²÷§–Fз:#€'e@:þÒ‹¨u‰€¯=7¢Tí ¼Ö‡Ë ¥ 2ß4è5æjéã4Oª› =ÉX­«þ÷d:ÑáÇh¥¤Õž¯jŵàÿõ7_[Ð HÊ«RW:ÿG´†U¶WÄvן"ò«"µÒ©Œ×ìÓCзm<CªacŠ¸É‹A9k4Uœ!89ù‘JÇûºN@Ð
+³¤Ý˲¡ &wÐRæ1`dÍÒ‡é}pöq¡NBêàîÔs½_I OAŸAWÓ°bLó:˜yOº<{‹Ì.¤É£B›!I¿dôCpýâƒÒ¢y¼s,œšbG,º> —i„yŒ|h­:Å® —ñ”ÁçPÍo!Þ8ì?´ *íé€!¤KÇzM”H «ÃM(’6"
+û§À‡—RBK"Ru &‡…l²¼”ðC…¢ìo¤;N=Àƒp9Å àû|Ó´zLJ¤JCBÌ «·MBßR¬%Le£ÿ1`«%Œ•Õ*Úß–Š
+»¥Ú,0*ß05.ltšB¸¿ùU:#úfÓ´ ûÝšŽ5öþ{ðzä$Ô"únÀP¡7 ’Õ5UôŸ@ã7ßTt0盥<D É&{Æ(¼G| ‚%"”B1?tš:ÙÝcå5¢{«KL #ú“ä6+q%7 ÖºJí
+tÉp>}zO`Ùvw»€’è†U0„3hI E ÉŸ3 Ž„B•ºæ®€¬Pœ]˜ìι’ødÜn—O‹@aYc‡.£b©{B‚Ñ°“æcw¯å·ß{ÏD©3•ËÃ!ô ¡Œ9 ‘ø© >+ÿ ¶P‹Iß{ïI Ã\ÅHñi6Æl$‡ á)ä«^4þñŸ±ŽŽ4áo\“„ðšO­·¯sŒ¦%]ìÑE
+z·>œÍŽÛÉâ_Òè9JO˜[!´é„ûN4pÈñN‡ZιU|ÍÖ9§¯Ú:pÏνï>E|+Ô¿10r‹aVRÓbžOuÿzú“òˆ¤ù*\ÓCÛÂèÁgÉ\Ò¢
+ÕP8k‹Åá¬^…cìýî.NC£c00
+Ìæ»—¸¾7oõ+y™ÈÁ:7€±
+¡)Ç‚fÁ¨Y„Ÿ[Z7Ýú‚/g×å. 0?æÖ]à ‹…1 O!’ V/ª0ÏŸ[ù·ÍÕ(ã`èùã±ìáF…‡ "RÂÏòOžïbÈù¾,Uö™Fzb}–Õ­PsäûY K ¨Š~…_§«Êµó—ObG*¢…à©+L%ëzW_è÷Ú“kS=©IiÓÓ9læTþKy™$GrAðó‡y ûræ•¿˜+ùÿ«<°4ÉBÙ’™Ì4£hK¤§p;¯O‘ßÂxSY›%ÏÒƆqyZnJžÚZ0´ê!z“(“!P;i~</ÒÁ"•Ø6|öSÄç`õŒ,Ö4Q•%ÖÞÆ‘¨Ö®Ú£ ñ,§Á²YDÉ„ ÚTçó™`E¨7_^)‚\­A=ùÔï(B9eÍàßè-ÑdÇe¼€vù‹%W¡1CŠf}ñ(îæˆ:ËÑþÒ¥L±1ÒYe¿|í¯Uìà%È~šù^»‘h[`d5…cêçÎú2zhs¦$4ŸX_ xþ˜"˜˜YÖ”9›°£Œ‰1ïyÊ2iüQÈÚR¦ϦÇ|pusTóƒA™À« y™ î8~0$À–TažÃc ©meZ´`6ê(OÞïµæ°æŽMS2´ð«Ù² ¢²ÔfˆÞnD”·jòu A„ø «WÞZ •yD5„)wøº0éúz–7{ RË’pÛ ¦ß(*´`ˆ°Âƒq{-T^í;ÌÜ¿+”ú<‡9îˆaNv²~Kúë§I#űvê>fÎ(i¢]h¡?SB4åãÚ*eg|œ±S^YÍÄ=ð´ºñ7ºÈû#ÓcWE€…ð
+V:e8tÒØæÖöÖTD4Sð»,:K_Ò"ë~®AïóÌ1é´¡ëÎBÐYM
+ËÜ„]p½±:{ŒŽIêÓÃÜ In”}Ùñc ×aÈs}ág.¬JXSPÑ17¬¼Pþ. oPjm/œKÊ“H$ít
+¨ o&oïÛ´´ô€€ÝÏt¶~:ß/*«EEðç(kw«Ïß… ЀôÇáôÔdueu:£k.þ;$ðV("°9>5qj
+>æ¶Ì¡Ô;³d…ˆð9!sÙ’«r2[JpÀîf¦Ó–{ÙqzõwçtŸªîÁ°\Æ?«_
+ ®ê¨Â]ÏqÑôΑnÎ,ì>˜Øˆ$T]*ÓÁý¹ªЃư…•‰²o"‘Lp´$:ž"-ÑYŠL1øƒŽ eé«ÃêåJÜ×»MŠÖѽ†q1éq4`g^”ºüâ.VÞ—R%®[>6‹ó C´ÈìÂwõ7èè@–C¶ˆº üŠŽŽ¡#¿tݨΨM#n¾?ÐñTxúÅ“¢-чÅù ÏZ®è(§LŽµì2?Ðq´lžµ¢5‘PW:‡ZLäS‡s :ôñ7è(ßqj:Z8Ú:ê†!ˆW¹=Ü/è(äU^SÒ :Ãͬ†ØÉŽß‘T¼ÕUð²mºüŽŽg ZìHªaÈJr{½²£Nêžl‹®›ìxºûé¿ùè„Gì„6JŽµ Ηy®-|‰$º®a|ˆ øAT"¢ªú"F*Æåhí·›oý¦ÐQø‡ÞÁ ¶M"U2
+ 7…éÉEPŸÊôfR1@a#øS4 ¹­Û¨¯¡Ó…öm5êÒ`7Û¢€*•éÇõ%jµKäo£ª¼&%5i*˜©µ¨vj;ë;hQõ³·ã”,„¤«|(vCñÿ~ÙW3Gh^ñMMé ¾¦õ&
+XË&ëW|#(Üäj-ÙEJìˆ?^Ÿ³³$ëá®èÿ³"úfŸSðòÔ³²¯~ˆŠÕ/{žnW:wã‚S«3G´Åeå…%’ßñÝ4²€ªXÍsJÁ“(ò½":2[iko$ß
+ôõ%:ê¼P|üŒD¤ú¤àl8ã3Oˆ.&`wÔEô…ðx១ղ1Û>nD”ÅÜØ‹Ææ„ä©%”£V½cà´%¡…Ù.ãÞhóóK~ÕºRF [êð§/órG’:Áž6J³$~ê¬Á6¾$ÆÑRôÕmC¥Q1A%±n PQÐôX
+`­¨îÖÌÍåZjâDŒC†Ð´$¦±ú™°SCì}ÐK*ëKô/»o x<ß PxÚºŒ§5å}I”Jž$õdzƒZ„:la×ò×åoH<¾Àç…-æŽpø"Y©üž'ë˜K²Ï»\kÆzÒQzghŽ>lH¾åéß3Ýòö¹éçP9}1Îû¸E°dl½~Ž¾HL@8Í¬î— ;ÐØÚLõZ{EàâÂvÆghÍ^È‘&¸©§b㽑÷sG Â2Idèf³k¨˜Åž0k²/“y‚2L5û›'eœ¡²AÒeÑGÇP _/s„Æ{³Zþ9ÃëñSzØ»åÿì_˜"õRî–„StÉúûHàϵ´Ì7už&çes{’SëÑLÁ¢m±­ÑèŠUòg“¡H’ƒœ •˜kóœP’âËáí)lXä9ÍŠ°Î =¹O¤ZömX¼2UWÑЭŽ€!Û°[`Ct‰•u™UÞÈ•+ù[±»½D/e6B­Šeÿr.Ã<×vW¯\ÚIߎ•¿Ä°CÞŸú¹1ß&m»†lDÕ ¤?–ˆ±Jß±%ÛìûÔh¿sXnÛ£”Ñ¥ Ãß!¨”C¦‡˜ì†ñš+„êïý®ñ¼°.ëoýþÒy_aÙe)«a±Ú]åí1N  †dÒlÛæ f9XÕêþR¯ÜU.µo|=&¼0>:
+Cû2alFx0ó(Þ=*²1¬ˆfò7çD%FÕÝRÀFaü ôU:½l¡º¼m–/‚I9…Ùå‡èÂÒ¿ð…Žz.þYfÛdfÐraÅ©a r €<ŠYÈ+Ðx?wF³'@• Õ¼$ Õ íÑÀT(l¶ül•»œL´AX¹jt°ƒdD)óVÚÌK:‹‘ó!!8ÌØHUÜ–9¯xˆKŠîÎ!füo—µügõP m*p²è L¬kRa
+w$‡1B4Þ—Hst" ëùŒ ,Å zæµC,´½×nÂì¸4.Zúò]ºÜ0„®[š¡ÀêSãÙ¹àÚ8‘槄‡bþŒëUÈ*àñVœy†ðª1‚Sû¸q¤2iå§EO~Ƹ?—ŸÚ^DQšp½nÌøʨSD’'Ÿœ³DC"r§N{­Ùcµ’ÆŒ²Ñ¶´1ÎÐcÌͲFÔ:ŒßÊE#’0¥
+óJ>«h´MŒQGõ8mAýì0­œ,@ô.M·#”Ø'†»tCófk²L50¤x#Ú<]øâjÛ¦pb耥4ƒ#¦Å×]öÊ%"eJ0äÃõ¦ç1¦KFØ6Â
+$­ör½=ÏTýœò×ÁK
+ä…üåeŽ$GŽDÑð-·†}ÇJ¥:' Vûþó~
+HAâŠa
+ŒÕBið,ðâÊ.¯ÄJE—BCݲà?t ´ÅY@¿/'{/{ÑY-Êx³@(Ý(è<A±u9`™8÷ÓÅÎ
+&d¡Â³Dû.‡û|¸TÙ¯âö×íY&/µ <(¶ ]ýH2,CXšÈ !¯D­éb™±žÑ›¤Å-·˜äìÏBFõ0¢´m>3O$§5Ï.Ôñp¸«“¼x1yóÜ~§À?QéÇ•¹@R¨ QÍDÐxI49·²€pQѱs½ÐX
+X'®-ØÄŸÕ>™.“
+)-.Ek¼W\£(²£íõ2Û)$Ϙ£ÜöÒ­W~+˜)5‹Ô‹±Å߬"Ëûä‡öAΪ”úvÑæ}ÆuÌ,ƒ—¯Š^Ÿ bÆ\ÕžP*?ÁÁû÷J~ÅI5žé)ÎŒðs!8ò›¦º+iè©;3SÔ0ôruÍ>\d"ÔRã:ásá
+.~”ÈHI´¤YñâD…š–Ü| Âï«éqQ-)„}9a5áe+øqÝÆ*$Zäù»L”ÁÅ|!ó;|Àj|Èê£â¬y
+/5ðöy¡ù†Uì]qìHÏNXÝŠ,*‘ò@‹LÇ»E ¹.½Wͱ}QôúLV£rÃ@®¢;Vi¢¾Å(èŠ ²$õvQ¦UòcL w]nµÏ^fÚ§hƒ•id"þå|ëŸPø[ØñY\/ ­ávˆŸ´Ä‰ANaÇ_Šà›Ìú“:_ÃŽÓܺlÀ)ìø‡Ü<bWúP–=ìx¦Î¥±¯»ÃT d-¤ØNa'<
+†¯±ãÞŽ;Gšªsnál;þAijØS}^ãŽÇs M\> ëë¡ÿw(åÅ°\±žÂ£‡„Áçrâ»Òk,¬ƒÌ{´ÃŽÃÌÉpÝNaÇ=
+Sf<±, x;î‘•ó8©y;ëì{Ô¹×XIg¯¹ÃP«Í[ÙNÀÉ òšb¥ER÷¤ã0`WR8-¯«÷SÐ1F¾Sè*øæXB>pyHìoéÍ Ãw 
+I¢¤ç7/)ØI68¸C/-ÙIgà•t@äå!j;%àÚµùý”t6n·øï+9§:y¾êCÔøvÃòs±ö€€Ëb4 %èZäAX4·¸ß
+ÂlWĬ˜³.º¡!€ldwìcE÷"kæ ô°µd[ÌÃÛEp,à/AÁ‡7¯»}¾³¦# #§É V€0Xò U¤ôÓ,2‚ E¹sFÂûð/ŠHy—ãªØîúSER:ä€ÆpŒ×7Š¤JT`Ÿ’€J
+¹b{&Ax £eçG kÂ^dÞbq/IÕã\#°0£ˆldž‚Ñ\Sí—Á¢BzÏÍòBKuºœ% ™F½‚v¯ß æ5—õ µ=)~;ö@´á§§H¾ŠÛÞÿ×gý0gtêì/QÔûZT;Å"À¨(“¯~ö—)€ÀZÙµ®øŽébnÅ(™/®hžÌs8¦`Ë‚v›÷Y¾½ñ \¼œÖ(aAJ†hJš7b±„¬C^4XmÐÅÓÜ£Îtnº„‚ŒuÇâx?" ipJõ¡äŸ߬¢ æåÂ'·gýsžqÆEoÝXÀ^©[ÑÅ(W+‰æâVX-7ûóP$æ ØRtæÚ?>á™À·òçŒI8
+C™%Bçgíùq“|ñ{6}V§“-=„qû³Õ‘]XÆðÕ;MÚö°Ã±<hQbdé*·9ÛƒRÂWgÁæK\t§„Øò¬À}bŒøl>/a g1éîoV±¢8í)²7}ŠUÈhˆ{*ó2VP64ŽN4– $?Ÿ‡áåD÷ÐÊe30¸ú ï­7ô\õyá•4EW)+mœÃo¾oyQOUè{º5ùV2çy–uÎmäûÓÜ‘c½ÔÀ½9Œ÷%/£úñÎÎh½ÜX/}="¨7㼟‡¢ˆ¹T‡dÍÿºJF!J¹j4Ï “\¡Ê²\ŒŠ;¸ãóÁyj>â²eõá¹(±ëss`]u¬ÆÚl«oÌ}·ú@ŒMêâ Tís@3 K«NO“y#›šâá2!ßÈ©}>úM=BZ†”d »
+gx£¿¸Bm²wSÜ%£Eš<Ç$#ÐCqjÄ‚[Ñm쟇"z‰ß Ý—0^
+ïΠPñ6+пê˜gœ»We¹›Rì²p7v‚¿~í°àç‚c^+¼„Œ« >"pãœ@BtdMÀf˱A”»ªo˜°Î Ù’ÛRþ¨O¼&U'lhJoêMœŠíØz QwÑ0o®£ó$Ä)È-9Ì :A›P¡ã.m‹³„‡ªøÁ$þeí“îfU ¡›³®úz3§ëö\‡™¢E­šüsÖ´Â~ÆâRÛaú9jí¨©¥Øh•V/èUÙù
+䎫'r,<ñÄÉ>ü+ø¡"1Ì4º]’W"nñûÌ[‰ǹ`ù}|³Š¬œt%BF€¬úøv×Å’õ™-4Šð«¤ÊèɆe4à˜µïK)rmÎ1™šg‰ÇZ¢ õɱ°é¶e/ãy1Ì÷25)yT"
+)±cÄf%×Ћf´ÞÊ!QdJ‚JWAôÁ‹-"â‘ÈHkœ"ê¸Cfϵq%À¨+­Æ¹7ü“íó jyæ­Èº>%±ÖÀ¹¿]t[ÀÏ7–ô—ÝÍ]9+Fqë`¡HŸ
+©~€ž¯uv°…¸˜CÝk¾sèe¹b¢Æ•4¨bøK
+3Ùþé²ßl‚{„€Ðøà§7VŽr^hë¹rc8XQ!%5ßGž @çPò¡§ÉW m9u<¹ !K3C Y>‹Icï«3Ç^ô6íQÄt&‡ÏŸ®£l:rÚñ®b>ßi“:ú÷iêþ÷íï~E\³ËÀ¨Êxv2maE8±Ñýò÷¢Ý…ª¢°LÒí¼Ìÿµ%ïç‰ïw4¾YbZd7Ce֠׊.º¶=Ä›´8K"fã‡Cž^O¨?0û¨ÀãÂP]¬žÇnEjq!²Á
+V“”’ѨßC󇆩8<ÉÐaíK®r$h*Co‚c˜M³ ]?cÕ\׆ èûtíç…íº1­ëƒU‘äì#\ZêÆn[B–`S¨”¢¼Ù>ýu¦² Ä\6 ºjíjkÈ:˜ Bo˜7~8ÆÃË%öĈ,y{
+$‡ÑóhŠÃMYt߇I­`,¤UþLKSÈ$24”Ûþ³²©ZÓö}î ?öû'6<×Åcr,F‚k‡©9\ŠÐPVö?Ó™µ.,eVÆÑç:Ÿp+&ìÐ4Ú÷zp í\Ê£Ûɉi)NU~Ãà¹i^³Áu YE~ ʹ°ÃBšâæ)¾o·¹‡½ ¾-Ú¡‘vH‡´=®sbÒ
+ÀAi» 5 ‡é Âßø®ª»£dP±Í/Á)vQ(ÇB3(0£Ï9¿&D… L±Óu·>žìÓXé¤uy‚nç§àW|›!cÑÜŸjˆG¥¶“p€c´ž.³•†ƒS~ÐpŠ Õ¼š/#b\ñý©+f+š²@LÔ«‚"b@¾ÌgTËcš©ájD
+2êë5þó—+é°Æ¨~2
+£0>1¯äÜgCè~*ÁxÙ»RV&­n{$ä…!cÑÇ'¤ÑJ”+®H~ ·~ž‚LÐç¥.\žÎAxš:ò9V]¥âÏ4±tNò>­CÌ ¢B€y›LUPÀ†w{€  …8žØ›¥7* -14d“jHçÒ¦ Ä–…
+{Ò95#pÑ“2“Ê“¹3]}«d1ñXzæ ¦–-ˆ’?§°Þh=ððµ¡r•±¯EÂq¤¨{PÐAõ#ëJÜÓ@ié9Ú¶F¢6Ì•ž}ªH]Âà×%3 Ö­d¦ ‡1„Çœîº_uJ¤Ÿ{ubîXqX2lE”É50‰x {ðH»jÕ/9I#]{Z>B¦,ÖP3¼`ê"?1@†D¿2œIAq.¸—SVd]ß&7ZG %$ùBHä# Á¬ñ®ß¤„–ƒItÙ½!@#2ZÃgj 7HãXÎë©êÅR §Ë œÇ5—ÎÀ"Â0.XŸÎ¡Û‘\Sù¤ßF¶‡°íŒ°”‘þ4[Y?áÎ|¿ÿa1¤Bl»ìcÐP­°Ò}w3Íà 4ÛJTËfLPÓ„’¦ØLÛK#¹1ƒÑ-Säè{ƒÄ2fßa‰¦®PéGÅ0DñůÓÅZ"ñxO&|Õ6h¶IÁ'°à®døPã{š Z¦˜+¶—îçTi¨ùíxŸÝüDø‡zšL*½Õ‡S` :K/‡wJ9 ]ÑNá Å“*¹qˆL‰ÁëüåÍPU˜‡Ö}›û0Ür&ý0iMÚòßãê×Lµ²dhýé—¯3|ƒ<“B™¡ö„AY›´Wç(œF'XŒæ»€à,ˆÒ—:ùG> ¬ògƒB×°gÑpÑ oL’úeÒ`DªHŠîeƒ´°²_y6¡¹„JbÇ¿ŽJ±¦ÂQù
+i–ÎÿmêÐ="aŽå
+Âyl˜w˜ïâà
+U!î—£XÕݪ.ªØ6ChT«õUãLaqª!×ó4§kH°$·äŸÅ”¸—D†n'阢a ÿ´¶õ;Da£a½ÿrþ)´ cpUÅ$Q^V¡’ö§:jX•öò‰8CQÑÛÏÁVM$d?Õ€*Ü&»D}Î Mˆ´…°šsÊqµ3“³˜¼¬¤• Ÿ™kc- )¦µ£§a‰—GTñë0u…ÿÏÿg¼Ì‘ã8‚(z‚¹lˆÚSA™tu†<ÐÔýõ~WV“˜Î††ÀäÔ–?ÿ²Ç
+Ë'¸ïKñpY)TÌó‘ˆ-©—Sl“·£W¸ª¦›µ­ûXR½û`(Ün>•Øpê$PGÈwë td³Ýö´?v†1.g âH¿’n«
+(}FG$®¦è|YPŸ‘©/²Ä»W‘rJ0íñ¦Dv »EkÆÎÏ5š`5 ‡ÌRyç¥îr²Õ*J¦ˆ#RiœxMhöhûæ²z 2+F9©Gð€Æ(ïGNd6“EÛlV¡k•ÿn²=ªõBP~'¨TpÃ2É_ø)ÅѸ²9²*–€kÞ¸ïñ¥%R•`§)LGa Mõ'F\Ë·Ôaí¥}—¢ÀJ…ì0«±j€)È/î¬
+øëìa³tãY3®ƒ—²u‚Ryeû<\(>‰6çƒ7êû”å$W0ªNÉõ„¬Q¶Ì+:J0,p=ö'6ÿ,”
+>HT[ëT\–‘
+£ZE8RhÈéí…žûÅм’¤Ñ›ŸëTÊŒyê‹ð¯óS±§%
+@W¢±{3¤¨uû˜YÁH®Ì[4yIr˜˜™}Bû?^…æÌY® Ä5PØÖAÇ9›ý¼)â÷ë÷Y¸Æ÷U„<Â=¤…˜_.zR¢ïîv¯‰G‘Ò¢8“›¢8ˆ¹3L‹
+pÀ{AûÝ*š у¬¢(˜ðǶ+ Ìä»P7ó£
+ Ic嶮þ\äb0a\Òч9æËEÞ\\¶{±èÓ¬º.ú¾æž—ãc@§û¶3„S´‹!Šf›h7‰-/曢(°£4»HÓ×pN¹n¬«€,À»YýX[ñn(ÕPìò×AÒ©ÙiU5&gc@ÙÂÞ­2Ó_¢ FGîWÑ©Y4¼*D°AÖ}Æ‚ôˆsª}-¾‡€£Œ¹ c«[¢³<!‹$¾=œ"·§ÌH|‚ßÇxµHÛ‘IŠnÊä¯Æ_Š86©’›7ÝÕ:A÷æDNõ™ƒ~*‚e‘d¾c T Û€2°ãn<°¦ž3X³€mL-áßæAAê0Ó•Åh£Š,—ˆ{æ˜6‡ÏEîû$òW²°eÞNÏs‘àÃo­Ì€w»êˆjwvŠ>^™_áµË6â9lëäÜŸ7EŸ³NãôV*!LbêLÛl6¹ÀˆVÂ0ÀpÏðòr Ìï3Šr×üõ]„e =2¢pÇZGé6såÞ·õe_­‹fÎU‚I,2%5T§äØŠðEÝ>üyŽØ„{ô`ü‰ci šzÈ£¤s©ªh:,  xõã”XÇo§è™Ñ?ÎfÏ2îùj8/W÷Œëå Ýu>7ââ¢/ýô̸Š8J×RÙv‚ ø|ˆ¯  ñ¹(Ïȃ8‘૪\èP}@ÖÖ÷V_CY¨êa><°TCc•V®í!j&3•—"8R&ð¨e‰K€ÁPû„ nçU`#žæe-/¹lœœ ã°šœTÜG—sG jØч6—ìñ$}=íž}SËPŽ¨ÜÒ<÷a[aºÉ—yò-Š€ÀQMí<ͯ°ðœqîà¥ï¤ú#üqºâ z]ˆ(¬>ëó±ƒ)O¯ÕŽg"1|[E—A|©ˆIàE¡Ý¡à»‘´+ëIçƒó\ ¡@Ù”&È1v#ôŒÁ…¾š×çëÆ箺ëÀ¨ø›À<³bÕxiÅÆlƒó9ƒhlG2` B]kÛU@$@»2ç;×déßbhÖ29ôeۤɇÀÝ(ÁlÅenôlƒæϽÏדµÜ]o°:Íïš™@< ¿B?%-ÙqŠ+ì´D ßVÉŒnì‘áJ9ö\%¼/øe(̓&¤kÀ-ðíJ´!º!ëy¦‚mäâPLYëÀ%$¦°–VBa& y ¢3ë±HƒWñcaY™ƒ…çÝ*™E©Õ.ÿ,]ÌUÓ£Ã÷ºwêï"¬•Šÿ6ÕÆâ
+H‰Œ—Án¤¹ „Ÿ`ÞÁç¶!J”H“ã\ó‹ädóþùø‹jÄ¿Õ³ëvµD‘ÅbñOéþZLj—Q´¿É°ñp¥Io>õíÏ RZ1>¬ÖtAÌdŠÏøxAzmRK™êÓÞþþqÆ”ÂÿÔVê©vnfV}3º¶:z™}.HÓQµiq¾üR«Çn]ëÛÏÿŽËì¡fJxÝF6ä1môA™o;"çYáv
+½E§ÔóáZáº\\ Hë\¥Ý)Ö‚P¥!œ2l ×Ã3‹M«sÈùœFayÄœµ28X„ö1Â!¯39Aü¡ š¬—ß0G¶óˆQGÊÅ¿ :uÅý¶ïa>õé‘îwPö|…ò}¸CW|¼ÅÇ /½¦p}ˆ1Xø©ïÉYzÌhÆ‚ˆ%D®Ém³Œ57ædh!­è}¶U É4ÄmqPkÕ¯žã*+Ä“Æw`y]gèMÖï-^È(Œ\
+/ ) é% æ
+‡ÐâÞ’¥DOc—&X~†±¼{ô«= ŸùR§’˜œ¦Ð†„ÇÕz@ÄH†äåjü·¯˜Oü[¡Ð¼ŠŽ¹%¤ sbóΫòÉZ{ 1©Éb‰ß:C‚Nz¦®‚GÌhòÈX,®s04Ì1ºM÷Ⱦ 6y8™a’M_§œäw‡s*
+ûÂÓ[{Meì •5dZ^½ª’?æ;õ—þ";ÁSL®ö âÊñ¤òLÙ1–]=@<~T
+Y]zBøUœÂ…Ðû ÐhA¥Š ÷¾.+¾wèÐ]ó0êZõ3[Z3¿P~HÔæ`4aJuŽµñy:u ø–FÛÑß¾ D„nöÐíì c­ŠÎ}´ [p–ç{uø^q,ÉæFKÙJŠ©¼ß@¼æ ˆŠ
+¢áéLª¼Û—t#$tHÏì0Ù¹¡¨#™<ظswËWE¿O
+`<¨´Ò
+´ZjW»úx™®Èã¶Ì‚ÅE 5Ç"ŤÝË„´’â$õŠOÃ$n’Bþy̓šÊöj3’Ÿd×pz“í+!üm ãc÷Ík(jhø‚D¥ðŽ€ø¾ŠÕ ¥ãQ[eþ\óUJòo2~ÉXÍIŽÂð`z1ºÍÕðÅHÄóª;(<1m¿§ËÅc|„̺ áoŒ]±è2å÷.âçŽÍáy&ð“Z„‰éŽË 0uwùmÖ}õwí:ú"¤19´Ù«St0ÿè{ù™$û·[+ƒ»¶çâÉQm´õy;~J‘žt’¡a{&øH]/¥!û\¿3ÌpéŠæÉÌéP(°0ŸgBpl t4z~Y¦„˜e¸fµd˜æ9±Ñ2ÃYOòUQ ëb{†|²
+?³}Ã>$T9€Þ7#[$Ò˜ô¢tÑBB6Éž†˜¥Á£®ÌuèU·HuæúzõIø.þYÌOâ µl±@µT
+ã‡2¤òS<Ä·f,‘ãXæÊ~ø'HRôæ’¾žs°cl\Åz¯/ ŸlÝÏËý 4:F%‚È”K“X„h‘uJüñDizŒÇÐ÷· ‚wb™Ð}A؆8d"rÝ‚A¥ë妺áŒ
+'Rˆp篛l0t¸Žo-kx¢ê:Æ©f o-!PMŸað„>šl<l3v€$•aQp©„ŒŸÎ±°jd½DûÍc4@Kû|Ó¯
+P´Çˆù Çä
+ù† –A×@=v³p©,ƒáBt9ˆ
+ÿÊÇH¾óòþãpWøirK×Y[ƒ´`EÁ$öíîÿ×—¯—³ãðPÌ1£7Ü Jñó9”a†Å¡˜3!@‹6“b áGtòÌ€ÃØ1¾[峜ìSÆ$k#1ŽüÁø´·#µ
+™BÑc!ò3¢1äc[‚JíyÏïHt/‹î埘Ò4õpÜÇ »ÂÕ–Z;†÷í‚0È=¬¯í§Ýö(‚¢—7E†rñƒþ%̘g‚¸™Îœ–ªÉÒËš(“Ø5Ôc‘AÇk*ͧˆbl²#ê¢eíõ
+µ|‡*
+W8X‚©t˜4bÇ"­=ô; £f"ìèv6êo@ FnCF–oF½&µi¬A»càñ,Ÿ[5É÷ÓbÙ`$CèlïqøRŽÓNq¯êñ˜Ï̸‚q¦OA¦ôįx“Çš¦˄O¯MÓèƒíTµ`]éž¿¨ŒËa¸œjj7ã­Á— =u ƒ’º "øó}Õï[+ºðÒˆýëÇÿù‘?BHc‘ó½ªÜg€ÚDt
+í&áPKyNNºîÌÜ„°?ñ ÞS2yÁ^–9fÎ/“ì:rˆ®À{¨èÛ±§žJûŸþl^ée"]:Õ¸¬‚™$ˆ&ŠJÜ"ÖÕb.•U°51§(dì½63º>†/áB¥ç¾õ¬ËÑQýp•$N¦éÀØ’§aràëáÞÛÖ…œ;SâM¦”ªþÁzטë‚Hfä~¨i1¯Å×’=Ë÷I¹ç¼Û½Ì;j¼7]Áç´ÆðµÁNÉÛ þüd_^2Í 1ÂØá!ç¼/¯H2‹‚;ÏŠu*áŸŠÏ Òí%Ót0²X˜åY2^&¾”Ü¢|"“rºd€çßÓî$ÆÂQߘâ˜Çü•ìé«3÷»NOˆ!d|$õ—»¿œ#0ÃCnþ]HD]àhÿÓ›:&{VÀÈñŠ˜V7!{'õH6#“¯&Ëìì’„­g¢ànJ$LÿáÐ×ߊÞÿé!Ce怆“ØÈŒ
+³Ñ÷‡,:ËÖcÞ%L?šÂ‰m½CÙF° ûóp!À^'[«Åõíî:†ø€E|>{í0å]篠pO‰³ÁÜ:½dþ»ç˜
+3Ú^—ÛÚ)…`CdiçSßÍ©*›Ù
+Ô³9ü´W؈4`Å+v:Qø9aÝ)#›„7L3Ê(Êç1{ƒµºf£µî•Ì[ú
+—ŽÁs‹NÁ$Z¨X}²(âNT$×´?…÷
+â.ÉJ'ˆUZ¾‰–†ìÑ’Ösl°4w=Ÿú; 7Z m›\vø¹4ñkõT2»e¾Ñêç*¾¯ÎÆ Ÿž ÷ß+h¤ùæ?ßPa“µg í©™EIù<ÿœü!ìÀ˜©¥“ØôéÙÈn«'GŒÃ÷ä=ö
+ð…`W K‘™.´Þœ’ý%ZÊ®Aæ‡Èîçdæ‚رq‡È*‡úû¾YÂ#£Mu;Ù C)­
+¯G¥¹<©¶ö@d†í+4Šå²}NíÌM.(Í*áÝ8Pà”
+c˜ñ6z÷ã%ÀÖÈHÀS<sŠîñÌ)º¦J :ݦ<DÞ¤Õƨ¤³a·$§/-¾œÂ%ÅÖ?V ç;×Luí¯›Í®crιÛ¹Ë4Þ“®Øs:ã øÚ^§äÎþk]þ gKà¶Dšc†!/œÝŠîáŒ8 # {ÔìÓTø稑OA†Ï£ìVò!œÉÇÂŒÍ\WâÞS\-6ߟκ?SÑZL'ÿ6ñ£Ãâcr„=½Š¬ ˜ A‡|–°‡œù×ñ#×|FIÓòwVãÌê–Ïœ¢{>»ÝóW&AßÖjôóOÇóö¬†Cq×|Ærb™.úùŒƒ™R‰5ó“Õ*½|vÅ…sN˜i‘K Çù\(ÅS 5'=%4¡#«iP…›Ð´3mÊ64knBóvï–ÐþsA—ç ýÝSÌÇ1ùZ5‚é£Ð#OL¨àŒ6’lù6}ŽùXi«BÜÍÿß{暉1P¦øj!*‹µbÙEX↠‰­§GàÅï€Ê¾.Q¦»€äFð'{ s˜±F‡'ÖÃäš)a:EŸ»ˆ»±ŠPø~µ% 7†é\ß!43s§ì¾£/âB=©&ïd=™þûêV1€ig(‘ K½Û› Zûµ 7„Þçè ¢8£-¡bŠŸsNØxt ãå"o$‰ðôU¡€È2âZÐå
+thÌûMM.[+í‡Ã5|Äð;Ó0Q‚ËÆbWˆàx*¬ ‰Ñõ+À¹ĉæìY:Eoäöé‰$1 tS®y¬DMº)'ø~
+ëáA”(HT'Fí‡@IjòÑ!™0Þ¤­Q‘×pÜÃ^÷]>nÃôdH `Púr䟣m1‰Í®G†Ð™e€6B›&>òš·•Ô–R¿š¥:IdZÜ„tÛ94hv³äø·u—зWó²¿N2ÔÆŒÂ/]þgC` ±´¶W‚Å¿ÊW2d{1b°Çà ð!Ó\ç!N^3|4¯¨Õ c7r¼Áû8CIÃSöj/;p;&òF’žºÁ:4q›œV ‚‰Md–»¢ôæ fë/„òg€@8ÝK¡rQì¬øÀ»p功ھ È$ä#äQOIb[»´æ”dlii„ÎnÞŠúŽÇg˜ã¬æ¥.æ6EÅô¶ý-ŒB-ÈMÚ ‰ @~R´wo! iÛ…,@£<#¿èÏ¥¶ù¦@ö
+-êë3y”ÌOMÿ%—¿—¨æd…ne/Æ~í"öÁb'A|7€Ÿ«ˆ+bƒè®‘¤DÊÁ20­>É40ë´g:)¤«ŒÓ/sÎ1åƒÝÀãÔÎ8¸JøFQð {ï°‚QBLñû—S$ipøƒeÖdÍÙþjõÇE[\‹ø®è†eR¤Z%™<סî1N r,„Íéï(­da·Æº(ðR@L[+gÿ®<É(à |hÞTj 0é·ÁUBì9¸:%0Zà™l:óéV‚WìfôÉ+Ï¡“'£S‚'CIe­ŸO1¬N×±kÕh*ÁýäúR‘ÊŸ€ÍD>m•$Ûøv.ŒYÅr7˜ÒþýË)r‡n>0F E?-º®àçOötzä•w!‹ˆ-¢Õœtôå׌L`èèP_4%ƒ8æÙ¸£íy°D°8Kžw ^Ž´lùx`eDv*ØLF‚""ÆYÚº¸ žÀð÷â25Å8' '.—)Ì1ùˆ_=aî{ÅüPâG±5þl§ ©˜1ÿ¦UA~¡¯èÁ.ÁN* ÙTZ5OŽ±‹LúÊŸ^Ñ[^ù|(B²Øø×½>ÆR@‡¶‚pF£jæ Vv +äVK:Ë>£§ ëy7säiûq<¹3%ž$c7K
+ÿuPÎ9×q;·¹ÂÆ{Ô}Nkî¾ö÷^ñ6¦??X­U8käÈØÐâ÷åIT"Ç 6Ý*¢É|¦?p¸´\q §9z{nŽ1Êìëm\€'×U{ðÝ·¹{‚ Ä"ªnJ®‚+,u¶©‡‡Û$«.×QŸ^e,#3&Õ£]2ô]ð¶9VS¸5ļŒcñÑ
+©SŒvL+'Ø™±IPþ¨/öZ„jBX~JÌzs_Ôý)ê€^¡­Ÿ\ã‚pf -4ÝÂÑð@ˆè#ÏHGÙq.ˆa»^uze ð©ú•¹œ†¹
+}>(1Ô“…DŽE–»s¶‚6€[ˆ¨Î§sð
+Q*†äª¼v)Nƒ@Q“–7¡¿»èµt¨ Á³µý&þù¼?ì¥k6g¥â·a8ù®WömÞ ("1SváÚïˆ!ma}æô+ȸ¼íÊ¥e5%^·aÛ;êÒš#¸)†6ÍË&"%2»c^iÞ…Xجé+ÅwY£nÖã=­Jž$·ñ­ÖL`%Àass?²F™‡Ñ¶í`~'³ÂV21
+²é‹ÖY´l¯ïäBåøˆÿ`Èhë!›ë â|‚ÄóN|H8Gƒ£ˆhkÚ’Ç`€°[1JUÉ šC:Î"1d— ÿz
+WòúùOA ùÀ+ ô‹òÛ@Øù$Í£@ ÂpÙ gíŸÄõùáh+V˜iè“©-Ûi×å>QøâEÊp·Ü’v~»Àª¸5ºJ»ƒ¿ba¶pXÕíYE–ØY&.Ž“k)6¯•vÀ-üeý
+¦õÚTD™¹£ýV›‚HQ9ź¾}+†ƒõ77;¦•${Äzö§F}ÝpYس!r£ ø®´>RºÙ¯»«aiÊ…‡îäK†–]5ˆÂ ›)ëîzylj÷h(Æ/¹þX3Ä…àÚ!C|g/Ó¿צjšâ‚÷JŠª‰²5›‹Fõa©× qœÀRåS¡ù+7½‘*o.Yc”fÛ­â·I#ò~óÀÑa¶ð;á2^Å?³ON¤ äBgZ¹µüDOL S$ݶ÷stÄMÒÍý6¤/ÕB1à 8&VÄ~X$Ö£óö©
+-ó7eÄ~§4®'%VÚ9eÎD¹ðíÅYˆ*)niÈ}[Ÿ¢¼n­Ón_ò¨±ª1]<Ú’èâ{ ,ûÄjÉÚ¿ŽlÛ˜ˆ±üÛ' )¸šêžƒŽ@Cï3ú‚ŠsÈ0¡oÓƒWY®æ+»[ü G°¶cȇ\\¤PvŽ¼
+W¥çf8eDøq-Y=ûä"EÇ«4ˆȂD©êÜCS¥˜Ð[Škº øúLKfÜQ$“Û$«ì^=ÍŸêÛeûpš}[†‹©ÕÁH˾1D›™ÉÞ7©³ÞY³_½¾J™I´^­•Ÿ9ý@|_Ó§F~íèt‡€Òض^ádªmÕX6-®ÒÁ¼­ÃÍìdF‚I_6e>§Ó›¬z<0Þª¯ã~Ëžœ;ˆLJÕai~¬OŸG/+Ý®§‹¿¾S¹•?þD”þñïüç5—ùc²1— j: Æï-i³ƒÝК®.Cpipä%ð=v#¦ wù Ô4‰p,W¯yÅ"‰è DfãOóè뤹^1h?À¬kC§–åfÁu3À²›J‚¨jh€®mn'ã73õ‘c§=¥f < ‚«JhÜê·`Uð“öxŒ/`ÊQLYA {È>˜ ¾AÖ§*“W”¦Jëç NäA4óÛ(·’ÉàjxeAÄ^Šn\È!ìQc£šxðãÇ”E,úOûX[GçQý>™¤P1‰xÍf­àeÌ$¹(Æ1 «O@rO°t ÿN+ª.dˆ±¬x Å ËšwuðÞ°&ÚŸÎðŒ‘¥i •â3†Ì»¿J4еÍÓ§‰Âå@vœˆ´¸R E ÍŠÃ\ iÖøò@(!È=íâH‹´•òi˜yWi…õSb8€^ßY° Ö›@n“?gývZ
+òK´v …/
+SŽÛ,kCP‡\”dͼ‚Yò€#Î+‚btáZ fÜ€õÅiOÙäÊR|Êœ ¢L0˜û^æò—Í…ìö£^#ý~Ž\WFykð+¿á8ÙŒaµ#ƒoHGŠÙS2Úþ§üÁüzù°|‡:S;¦2è|LŒíåcÖ‡N®¶î@=:ÃÖû—0NÈ$5L×ÇèÔóÌô(í`Bû6è´Ì·¥k™•ø°¼2T9Èæ ç¢Ø§Èb²w¾Ð¸¥€ƒÁaåÙ !ÉLB]°P"ŽÀÖÇl‚×TÈ­ÙO!ðåV$’Öœ36%l¦˜€¢¼1?0êvM*´[”oT5áÑl+j-H“¿Æu˜Bõ_ƒ²EÉTÙM9€\¸<M†“'ï‚^±æ1›¿^*›fíÞ ´ I”ˆÏ÷+³€qÅÌ™Ðfi<é0$5¬T–Þ> ªQ3MÌ4®VÆ
+EüCßHæ-rBÕׯËÉ¿ZüÎ ‘XÜ|±v«Vy>w͆°±×C:éOÇMùò„Ä)†B^Þ<ä‰ã–¶o%þ%ˬÆT@{;çFºÍЛ–æ„>F‰Óö3w{ð /%5öÎ0xç@¤-òCeû/^²ä,»½”<Ý 2ÃjÖ©èó¥è>ÌÖ¹1p¬äY*_`Ó_^Éý2&f×?ô ëê4awÃlr&_)
+Mò5Ц“¡„åÇ ¼òc½ƒÖ`e¢¬ìRÃ~²}¬NŽ>‰œ& ’3Ÿ%ü9DÜÚ;,Fä=D•Ö_ÎéR3\ݸ2/J3àE¥·ì%ðCFyÕ¶ :Ê|EûB`“KÁ¶µ
+a)ýZxÁŠßÎ+<QðL°•ÈÏ…íZÿUÂN³½\èË…PõíVX©¡ÛjÙG]çH¸ø¥Œ»Jä†2¬;ga
+‹ø׿‘E¸~f}Éè`µšÉÍ"Ø— ‚Ë‹ ñEg¨I+FÐPlÆ7QWhäH¸ÉwXþç°|}‰Œ¢mļ‹Xa
+||Kž¿}uù2ÓÑø—xz*JÊr‰$J^%pI’‡CwÕ’)xlH‰E«r³Ž¾ bUcÄ7»Å†¯J¸”àÍì+FÕþT/M
+©Ç\yæ#ŸR$…Á ÀâãíDœy3ßZÞåLf¶ó½»ñÒ“£"Y¾ Šâš²],[­ 0%î«"ɚ¤wØ’ü›ÛK }„~L‹ß¯/Ý‹DâÒÏ>‡'>|Èœ‘píob1Ù'lr?¸²M­ªY쳟ÃM…5UùNµ¨u²7ð3¹ÂøßU2àÜ>
+
+–NE³DÞ?Â:ßFÑÉ€f•Ð6 bÊoF<è©8’,k{ôóá£ã 2.²˜h
+‘b®o%˜$ OΘ˜ý¥GŽ†œ¤mŒÉÏÁG†>5Ê¡œAL$¹U2Št
+æÁǤ²\bøWXé%Á-×}Ò8 ul
+ØÀØÍ¿ƒñA– ­aóôW#6!\Öppæìj³€Uˆ•Ú ]Œêç¯süÓÕ,˜ƒC×`Ü1FÎ Ú:ªÓc]8Ÿ\Ñ¥úž*
+ši‚rSNÚ—•‚øÊ=™ 58êrHµ‚{*”ÊŠu/Œ<EgDSlÇáã‘h HZ¡rOD£´œÍyJbJù!Ž»“|‰ASA]‘8]É çÑ$'v?à߉Tœ ¬²VÜ€¯çáII9/6¬Eaóþ,ÐÈÔ>×&Ù\ëÙ(¢ÒAq ©–dŒr·'¿á E=ž;n6a‚¢Vˆ4+O ò¡Gt™ý‘ŸŽ+tÈC{:]ûõ“|±.‰2@}ÐI~Ï-9JCÅغY«L i¦9,»{ß@kCª¡›u/ Ì•UMEÌ ;£3¤ÏÒ·ì4¼úàf\ Íåò5m¢dÌ\AÇ×wŠ¦êæ¯CÉx¥Ë#-HUAj¬õC~ÿ:ƒ´HF=0^o.9›1MÛº~½¼>8»@ˆvÍ:s[Ö^ŠÉ!ÈÐ °ÖÆ‚XWAØ‚¶)Ñè2ç3]N!‰à剸¥¸“N Öõ™"~ÃðÜ…¨hN¦…”ú ÂÐÀ£Àµ¶Oº:²È q1­ïÔ9¯Ò¸¸¹µŠ7+åGUž„ÿf‰âˆü(%99m{“ž~(ümñÚ4Q?“¯Žø_“üÅåy©d«‡uô¡¯òV0ÛÎèNÇwóüe¶@ 2˜Ñ–Ç‘^;îvÌ>óTkËn?–eŃòczÏ|Q.Ä~åútTÑ‘ŽâGWrLâfr¯ïã
+ ¸>Xl~†„E*ÓtÓhŸ ˆ ­ƒ4¬!X/Q]C µ™G@0S–e‹óº13Ð祗ç¿X’õ(%— êëÂÒ§ò®ŸÄ@Y¢Ô呺zŘ ÁÕ.ý®üîN«©˜sV¬n²ÄÂÁ¨ür¦§ èóôÍÖ½@×ܧ²z¨EɽìÜ”`b(ÃÆBðn~
+¹Úö˜D5,?†»üËÞArÙW'aÈââhý¤ˆJ`¥ÔwŽVY…§|ì节ˆw¢k×p9£œ@'ÒÐm†…OjNŒhñÑ2Uë 6WŽx÷ü;…Q3“peAhR0d¼z‰„0¦×ïùR0C1å¨z¬–3¥Eå*(@<€^ k7oÓÎ属,_9â{ø |ò® ÒˆªÈ\¾§’1ïÕÃeTªÌÙ¢¯R$÷, †BÓ(@IÇ—+ä@þ™4IJ¿
+©ÏöQ0}–XœS"]dý7ŠÛåøüùÿùÏziY,ïDñƒÏ,ˆ’‡l› 
+Éo{VEÐ<1¯ú¶.aªžì“+ž±"t‡ ¹º¥º¥‘j” !ôëœ@_ÅuSÒ_x:T^ø!÷ ãRÆ=€½M›%­_BÐy•™ç7ŸÔJìd§þ™y+ü^ÞfÿÒô€"Wp½: O${ñ82…Ó„MÄE©¾† #3z”l!` \kÒ|;)FƒÊТèU¼N‰!¢—™†#t§J1Æ,íAÕÕ׃Àò ¬S‰qU·‚¦(v:$á×à%öاi ¶s£pûä"ÄØõ!ÀÈRb0Ë¤æ ¢÷
+ÚKi/ 7ŽÌ!5ön°vlI; ü$>™pÅt¢>}FŽ&ó(&Õ/”/#þ"ùef¿!ZöâA¿,÷Äöo®­ÚŠÜÖf… Å&#†å)Fˆµ’(J]ïƒ~‡;èÐo@ä(Â`vû1¨P‹ŽýÌÚã¾A—ã. ×O6ÔYã22¼‹œ²í7Ä/_ Thµ|kºa70abhõ,jeĬ—~ ¦Eôë‰ÔØŽ‹d ’¨-•H+$ ÀHlrÙvAªþ-RlÛ~š¦8ØÉY³ƒû~]^qÌq´xZx(»ËºoÖ¸-<  gÀ´ ¯^ˆ+µ†ÙŽ›
+„mÏÄÃ~¯¶ƒ2m™å§ï°•­O-Ç×x5q\`^Óy㻪ÅvÆ\n+¯Q<šH· |Ýx~!,ZçE–ž6þ:µ¼N/‚þÁÞåǠ˾~²¥s™ÿùŸ EŽ2³x±y®%ç2=$Û‘|Ÿ ·Ð4oHõ
+=?Mà_ù7#öÈÏ;QÞãaÏ|æÅ«h$yÇOĉòKtHçØ*Š`ní²¢lLAª¹!ì)ae[”áPY,ŒØ{æ“:ÏX×ôtæ*¬ð¶ý”‚rÉ°d_‹@d úü6‘ß {| …"lâù;8zÆÞ[ÞÅ!Œ•"µ›‘¹V—¨V‡àH
+YÙý3l€ªÓ††˜+±‚3¬!ÒW.Öx(»º † ¥„IÖw ‹8ä$²*¯††Ú]‚û`ºˆ‚Á!Aqã´–÷NtP2£Íå «UŒóî;1T¥¦m(û¨MÞÑ›™¹!Ím- gvº€>@¨æè¤ÕB7;Ý@©2*°.¦qó%‹ß¡[sj$—b6WŒïGÜúÏ€BQÓò&ïÒûœ-?ÄÝÿÏx¹$É‘ã@ôºƒNü“ëšeßBÛÒý·óœs¤ ¢'­7ê*ƒþá?É¥½_%KÞÖà Np_ÃÚå™@-Õ)±O]âs [ìÑ?çà ÝfèQ=¡ûF¹…•“ã~÷a£7•Ci “7Ž#Må²ùÆ4˜==”œ&”Šƒ‡\xwŠ¾ŠîÓ<Ð^Eˆ ÃHÕ®ÌÆð,žþ›ŠÑŽjî~yÆ?eìx~›bâº&#_,8ÿÞ^­)êt½æè>½H¿±xp¶ý­
+ZY›UŒ²|ý¡€ß’T°·¤¶…œ£!žN5Gœr(êÒ]p·Ê"Öê//³Ûhõb­¬–É 9NŽÇ{u+áÝ5MbXò€N5ˆIar_ç¨ùS_¿?aË›È(lÓuÙYæqŠYÑß«Vš,䧈DV€w„XôןƗ¾Ä¦Ï¿Yz$€?Îr4¾ =(.òÁdÆÕ&ú…Õ°OïDlÀ™Õëà
+$'Sóô7ŠÒ Üy~8G;ƒÑÆÕ…²欴†çfCi¹#e9¯bËJ1ûS-òÎÉX&Ö, Ë‘yW°OÝvæTëcõ*ü^@ݲ{?o;|+²…\Ñð(¼¬"´‰X"æmÃh‡6à'0ÌFð@”êÆj%N¶H²“3óÝe¹zúÒŠ½ ²€©˜3Ný§Ç“§irzš¯ÀKF?˜8a'V巵šÐÀp,o n¦ô´qü‡­²ï W$# ÓmˆA=ÉÀ¥µÀ¿†+…àâ*)ˆÙ%§Ê€7W@(@°o;Ô b
+¸£~L&xS áŠ$ÇñÍX ¡(¡Ø‡°S Õ÷âKÞÿ“ί©° æ,¸ |;h6ž„Ä_ž
+KQ‚š°UÚ–}âžÜs˜µÉM4ÚA·bsY»©Ø žÆYìO#u½©ý<ÿ.Aci÷mþÖ¢&îB>²–4"Rëu¦ÝÄ‘¸6¯­s.äLy9"ÞÀ$Én<<ćY¢ÅDaÂë¬açª0ÛSžiN/ç¨(|\DÚíô%–ÄhÇ+º¹JªÀàô±}A¦Xì‹Ö®’‘$ƒ8Ë”œ’_ ê’Ã}û¸û90³r§µ9ÆjPD‰?jÛÈ4‘&¤’¤s_?¼"¯?ã%ÀѸÑòqÑa>×çªl--H^Ñ÷'Û3mìÏÿ¨T©‚(…y ц%9HÏ€ô÷C‘
+éÅ^Žv!›ÜagD@ÐdüÞ§ƒô¸*Gö¼Š²ú ĹkXMš€ä¶'ïã0åé}\œç€.HE¨%÷}Ú[âc•T.cƒQ>{É@d¦m”2%X‚—u.ÒBÆÉrÁbŸ®šŽ_Ý¡ÇÀ£èÄq?½µbVÄ4¼ëüõÉ‚þ³Œh ˜ý¨[íE¤I|„ÌÜãôJ%Ȳ­k³’܈­½*@\Ê}|Ð+Jl7p¡»ÝMQ#ú5žMˆ
+^JTAŠ7‰+nŽ
+ýðŠ¼×ÙRg‹øÜÌOXJ,øBEI>!M ú©T ¯·–rÅÖ‹sƒßã:ïؾP½’_‹™ôïe§fÅ"±Ô\‘ŒgM¾Øš(ÐöZV‰òÖVךÌe±M û}ýðŠ¼ö.­i6>ž?.ºó÷'ì=ÝÕ̦»Ú:ÛÂ\6MaÉ
+‚ ¥+‹èNìJhÕ)š%9€y°cÁóܱDc³klùW|…h¡3$†’Xð$IÖŠ¬5 ê²5xÞª}çç}ÃHaøG#•
+¶¾¡ 4aFÔJ$hW¢ÍÅ(س‘«ƒ³&8îP؈£EF$§u<OßéÂ…p¢Ž!kõ©„ÓÂΪį^Q]ª+’òÊ1^Ñ‹Q~Ö
+ÎìOϵ“7ÄêŸä%†RÃö`ë”û0@ ŒÅ±‡ö)š 
+´‡pÿÏ°QÓžz žÉÃÞŸKŸñ2#í TŠb’7îGÌ&ú°«5¬´”Ͼ_ŠP6ŽÑ>ÍXvz½3Çw‹”yÞ&ÕúùP„ºLöcXÉt4‘v£Q­úÚÞMšðµ§´ÈÜ«r
+mX ÿà‘?É´Y¦òNkáqÅì
+ŸRv´ïC¾µ:%¦KüÜÁê÷s€"5Ðw1nƒ¸\÷ËÖÅ%š,EÖ 
+ëÉ7kb½óvQ<¾ Þ¢WÚÇ@àªB†uJ¬ÇZæ ¼×Ãô*QÄCÚ±2}Má¡ï
+™ >²n_*±Ð?iJJçK%°:ŠvÓHL·ãÛ¼-Ï}Ù 4ÎP"I&É-¹`ôë‡Wä¡=,6­eù•¢û:ŸûFÑ…üåºýw3Lù,ä†Í#ÁèX Ù÷¾Æ)jH5f—:—‘¢„¾bØ Æ~4.ËkñÜÒw‰´»`Qr°u¯€9qMäªïцOv ƒzÍFÔEFC!Mâœ:i¤,*"`—AefØÈXwnÆUYr‹rƒoy;Ø¢‰FQzo¦üaGËJ)T$.¦ÒV¸®=QL¤v´H€"›Ó@¯±"QW;_A¶b„™cAJBAò­B|Ÿ À–b—¬dà,á°(w»KÅéäï·ä±¾D ä ÅY-S!Bø?¾³¢U$ñD!%Ò?é¡$¯´ÅæÀŸ¯/]‹ð¼h¥åÂô¦€›õrllÂüFò*š»›'½d'ùs¨eh
+YΧ}ôšA‚÷%ñ–%ZL2ŠeŸÃÝ Ó‡ëP)!M)ùÉé¼¢‚«ìmŒSQ–òäs\F^´Rw úUŠìÂ<¯L€_Ê­µó©Æ:ËL¦pèòjÐ"Ϙû&,ÃáEë1¿Ÿ5Ž‡Š7/üe‹‹8ÐE§èçCQR¾˜“h¯.
+“/^6\=sA„©tS«7Ï·ƒ³!Ý`wsz:§ðnùeèÁJ@§ ¬øpX‚GéÉØÊÀ¹çKy%Õº ½›W.kö¦¤ƒ ´5Z´IÊVc%ûCIæòŠy4~¼ÖáZ$r$D`„‹Ã§»`)o¼—œëá+àól·¡ öàav_Îd¥ž³½è±À8 tœ"®„ªl­ªŒ§†ó©žãT€ÍÌ&Íâ™Xø·¡/ J\8 €À™ÍŽY♄“E´Ú*Împ<Ø5²†)Ál´¸Käÿ§¨š*@hQÃÒ‡ùZ¼!¨½S ,$‚
+âY¼ŠóÐ}“1mBI¥óŽúPµõ KŠO=Ÿºá?H
+›ðMeÖÌQqSŒ#I²|
+”œRÌ ‰3iˆ<lB9yOí3­T£{ø…Ù=ïv0Áíz‘Ì•§sŒ¤)v™L¿i2h+…}^¡øØ£x`,YÌK b@S1¯žC¦‚ç7&“¤&¡.nñKh#6Eý£U_¶RÂÐXì|¯ùyjÀfÚ4£H;!>®— 話`šw·&ËBz9kǸñ%ôʉ±O¾Ämßå2=žd ¸ÔÖ?¤‡Y¾•Ø—¦((¹¸ç,Eä
+~•êp,“Ñ7ûß3%ë­µàK¢©ß½,èÌòž)!€&Ã8Î,¯ç”O¥WƧùø™òÆ^¨,ÈŽ:J¥º¡’
+(±f\Ñ•NÉ=T:E×PIIFp¦)ŸÄx •Eµ?…J^¾Ð4+?TÒ¿YeJ›©Õ=TVhæ‘NŸhz •BÅ Cï©R;EžÈ˸g7UVmAvì{šNªÔÞM 绩R[Õ$ÓºåpS¥WrK•÷¢›™Ömˆ*…aŒÒ¼T©wó_]¹í)TR³@©2ñ¸…J­«œ¡z? ²T¢æ7s‹O©P4A/7ÿ˜"§gËš*!ý(Û‹«ù!TJ^°AºžÝPYØ1Þ£ÜvlÀ5T:%÷PYô@ͪ•ú”*UÄ~ÈÃg?TRÑÁø‡ÉüPY’#t@ÜRå½Ã×P ±)m š=¨wI•Î,ï©¢Í •ǵÞÏÁ…ó
+žo©R«Ë]{Ù@o±r™CRgCõÏu®›àXÌK¬üW«ªXùÛ?ÂÇþüñÛÿ~€ÚR€¼ÜP—OHR ð‰Uüë¡H6EÖ¡Ç]2K–1l‹|TBºc"#,*TI“œË¬»¤I .ilIRM‚žø|´&Ù`ïÉQû˜Îƒì}¨öŒBÇd7VÅLV®>U§Ä>„_áИdwÝs1ð-æ6Ç’üË…
+àî‚x J䜉 6Ù”MãÉs®z$Çúj{+ H<]ÎZ¡€½¿:Çqœrø¸2îÁplÙID*¸¼ß.Üh-*^sÉ3”I2 ’‘Ñ?ŒšMi¤©Æ9æð‹®ábîÊ&mB½@ü!éçÛ
+´¢{98XLÕHÿ
+H‰Œ—Ar%7DOà;èó$@X{–}‹Ž˜•úþÛy(¢¾[ªª™v„Ö”E@"‘Ðfãã_mÙËZo¡­5ïíãï¿ÚjúÒ¥ÝB´@úZ2§úp‹>?~lP“‹?Œµn@Ÿ ê/_}Eï]ít@¦1ÚsEA†ëê:EÆFŒ!Mº¸ÚøP0õ9íãç¾Èzëc,ñ Š¡M¦DÞ´Ñ­Gס±C—˜ ™Íe=@ºËÝ}ÄyÕdæîª}ôauŽ{5 É7„—ÑÔégØfühDÒªÓ–®ÃZ[çU£)?úŒX´ºó¤Æ“µ¢š„)Ö£µ^i±D×è³¢Z$‹CcÎÄßýç/ùø÷®i>ª‡<èx5›Ñ¦ð7Ñ_jsXWµ° o2–?%Ü
+óÓµUŠ¸/|.Š±FAœp5HKÅïlšÀ½¿¾zkÆ›xA¿¿ª¿ž:ŸÙ,H§ «Íó´âï.ë ~q…œB÷ÅÓ9°X0Õ‚4íÞx@X´ ɞЮáÞ*pŸ2B æ¨«Ú+´ ”"‰ÅW/…÷9 Š“-²¬Î™™§Ø0ö¢°®õIŽG;¯º€$9¶x¡ísböF;­µéAÿÅÙÇRîÛQ5ÎÐ5cŽj®™ja# ô›«)—O«¦Ÿ:ˆ)ª-æ N’ä)5 “kÒüPsmZÐç¶Úš~xR§/  lš1)˜Kluº€+ ˆ5 ÂXÑ”/z6î~d¢‘ú¤máÁ† ;†”èFL£ž´>m4O"“ú5ˆ½:†üg­Õ£n:JÒ›Õ9Cé;ã¦U(â+ÔÌHÕa¡¦Ncõ§s¾iF¾&õ˜b+ò£!ÃG燊Š˜áÍJ£=óUÃòn´Teì%O„l½¨¹*ÅpgÑ‹üv>@è™ðå³ïšCÒÌ:j
+ǯ˜ÏÌ÷j^¸Îƒ0i8Øø
+½ß?ê
+Y$í§]Ïɳ;݆d–ê´³nxçåƒ/£  šÊ2˜Ï½‰X3áté>'å.ÒI®Ú/xWš
+ÄáG§–žuÚ ä¸J_ØZ4V;{ñzÎщ)ùç~yÇÔ²,øšFG9qOU#Üo›#7 ìüœ#¢ø.Ú !Á9êµz½Ç`³Ü[y ŒýE½Ì
+‚ÇæÏ8Ó™]éÚû?¡³‡x™ÓáñM¨ü'*.µÖñtü{gÆï²|ãׯպžs©ùõ5îÜEuáà5;7tÿžãÈ—Zýü“¶É“Ýa˜rÚ@ñÐçæ¼_ ñt- ÛŒý. a³%—5z©rÕS@×FdáÁD¢Ý8Ï3Ix:ÍåèœÎ]5ïø(«z0¿qòl§ÞñÊw•ÿî¸Áà qž Ý¥§ ÅÜÌOQüþ,%>‘Ù—÷)$Ôk2Ò½{î:$ÅYzÍÝî,¨Ì rSù¹£­ÜŽVAb¨~4ÿ±Ã9KScÔ¼¼€¾ÔýóÄ/˜åÖÓÙÜ—±[PªÙ¥“ÄÓ)´v¯îŠÕqÉTjÔðÁ9Àäiò[#C[Míç¾r?¸˜ƒÝî'À5Ê$î}Ë¥œwþçŠë9É-gÙ”EYÏ×0à(8ѵ ÑT•Œ¼f€ÓçCðÌíÉh¹b|»Wä#ÒO·ã»_ÈÊHK{&‡ÿgp2Ÿ÷-å˜Mr‡v
+êÿ5gùPÜê‹_¯*—2ÜZŸ9àÚÙÃXØæyú“7ÃZÅÊ„ŽÉ£º<wO˜¿ÓHAIPî:± t> ¦à™I@UÞaóÔÌ¿¹–Wbé}ÇQ¬2õa‰ôæ2r3à׶ÝóÜn =sÇý¹¯bF"Ç@7Ÿ±x+¬Ç–p¡sÓ‘aïúF`W©­
+Çe;ŽÞÞ˜HÂ.GªÞEàÏÄEºÄo@„³²v,ç]‰"0<´WòÃ)}kT‡2Äh.‘ô¹ï"Ð(–#° bMÍ}‰1±Ï¡¬]ØQ{««XUÅ°!¬Ÿu
+Éä0+ÅIЯ­qp‚t&ÛLô\crEI A­*;¬”+é¤ÏD&Œ$íwÛéIäÿÙ[G ÎXiÎ(2©³Rò!µ‡ÔeÞbhvv§¼T
+¡\hºÔ@I%¦$Ë6¥ëó±4»Î;)!ZœÕH›ÍØjÙÁXZecJ·bObËAŽ2Ma âêÂ:Äz‚’õ»s<.š¯t3ø9鳕èŠarû8 "=Ô¶!sXdmÂSu‚O¢^íõôŸ+—>¢ŠÝæ °§°÷{pøŽÄ2:cîs öynQ§\RL’ËŸHOA&ˆ—éébU8•Yý¼(÷²¾òŸ¸3aÁ“!Äk^³¼q®2’õ‚Û ÊA¦wú. (ÜÈ#½Rý‰Ò´2MƒˆacG2Îì¥q%-ŠÆ:`°fµã½Å
+›ã{õòU ÿÁP9FÖ[p^Êâô§_ÑÁˆ´¯I€wÃ|õUõþËx™$‡qAðþƒ^ À¾œ©£žBýÿê, {(Î`_Dº‰ÁÒ]•Õ…³‰'[…viB›¼ãÓC¡Ï6“#H1G?R£éqUiì/ M+’‚€:*Ò!IÄ­O MŒ
+Îåb
+?Ã×áý1Xµú§à‘¬y-‚Íñ9ïIEÒƈSÅ·u”†(Ïv\{Tw Ê4à&
+LÈ#âB6oQö‘ue^Àz¶Œ.e*Ô³¡'ƒ7¶+ÆC¡i ®Î—q4Ö…–\cw«ÉKÌFø2ì¡Cý¬-Y:È~ØÜ‘QD"Ýu Ó€¬•{ò¼ô±¯0
+´Çxùò×—˜OÜšÜá¤Kv˜áÓ^&U ™©´L@’!r
+rý¥Dü2pâ¦ÿ0sÁ64feÄCѧAŒ8ì%9„ ‘
+KØGÛë°h°%4zxqñ½}ðÐfª€~6\MbQ¤híêÕ$a’‚w£´,Ç®Je6 ›Ü˜_Û—cë|)%A
+öŒ–” Û|²è4襲’.¼—01û˜E>l`â‚Q ?k>_jî¯ùèt6ŒF£H•o• åš_ )BŠ‹ä_Ο@™}t‹WŠ#X(f¾“BQÅ$„Ój=!’æ.ñâ‘_Ž5.Úå4´Pg¢ú°Ý0r`ÔË)MÙÐbdVXk%<Zm ô³'¸áÉ÷
+WÙ³è0ú±o« ù*dÊ8ø*ÄCZ•µ³é9òÙ0n<ØJ4–$<Nÿ@È-š} s”kßë0M :—›¿¿Bõõ–Ç’¬ŒÆ7gW‰[}–¢<û%½i0ÆŒî¯7L® †«•æñ‰«k!;8âþ@òæ¶À'Ñ¢eH¾›gÑßÆúùÜ÷·Ð”Ÿ™qÈ=àfž#þØ?/5¬‰ÜÑÛ¦Q"Î6á¾a$È¿ª˜Óܼ±´
+²­‚Íq×1—dOÂùh˜ ŠŸ4.p•Ç^‡ÐZ‘™Ïpñu¬W­>âäA»C;Ü©@D`Œ¾ñ?0ˆ ˆQ‹;U}À_Œ9î §Hèï]"ù£7xöVýK%6%:鹟i„®Â¹ö:²X%7«*\7ǹcÞ%x>Ú[öU¸Ð RMþ!± ßžÝö‹46¯•løž¶@+ÜÊ`)ì„KùX‚>b6€ñ®Ë{a ò Â“Ý ¶•èRƒÉÖJ™døÛJð6–eélâ—«hG=¯Ë£÷°>Fu­@r‘,]Æ^\æÌEWzœˆ„òÿdRí lÞyt’H;¤—Uš:zJf½bêÃLpo6£ "é¡Ú‡`t†
+
+‚ç\£ µÿÒ÷š¼´ælKtm3°.T³q3·Ðh8Ⱑ㟠ù¥šîñãl2}`æÒFZ Ó‘*˜’ §jåëLl>A78‚})Óø\`Àâ¬#’.br]Ã:TlÃL%„öNµã«ÈK‰ö%AR—sÅa‚ÏpôÂwWóø €)»^R
+GÕk˜¹ =¼‚Ä¡èÓŠRÊj¿l7 (§Á‡øS;6lÚå“Va(µNŸ»¦‹*ËòwKp\F,Šý冟©Áã±QctÔJþ?äTÊñ1o%Ö7 ËJ`ÚÛ:„Yô%ÚVQðÌÏL–ùõ“ÿð;Æ
+ʬ{ºånõ¼=êÌŒV?=çÿxq5Gðæ Pòä€V?H˜õFÁE€kÒñйõ‚îÑ”PjËY­*:–ÄøhLƒ%è‘=ꊢ¾£kÜ{ÉÉFLŽ@JxJÛë4%¥…në@
+V¢Èz,Áý¸Q¬Ä`”?þ9éŽ`WP”O}¾ÝQ[N‰7f¢­E(’ú¾ÛÑy1Z¹¨¿W "[ƒ^ô± ¿ê3j’2O*>}dÆg?ùéÝï\¯.Ä…'Êý÷@ ±zﬕÃk‘8‹u;È
+õ¡¦Ü'*’`
+Ð1ÿ|½Ò½-9s …±×Aî95ŒÖÆÇW
+v¶5F›š¸<~bhCóAýËx™$Ç‘ì@ôºOPó°Ö_ò½%ï¿ýÏ3)U&RÆ–µ$–P1Áá 5
+¥Lª­ÓÉœ@:±®ãÅ@›Ö.®riÊ3’Áó郊X†ØaÝâu]Gà]¿æYònDD„øTrFâ)¬$ÀfÜÜÁçàÜ3؈ûßÄÿ()2«
+„?fy8‹r. –Uy¾„ƒ^ yø/Ó„%@“îupʃ9
+ `>”àÐ& ®0¹·º‘ïø‘°ˆÙüðOÃÔG)°]’æÜ
+4`2#’æÞêö8ÈÜ©…²­s}a BìcÖ€ØC£ÞJ6"® ¿¯sÍõ,öîwr0|}g’2 ˜9é¡äm¢ÎÁCÝ úÊ D1.±uØh³:ê
+Sßj,?.òzÆvD•‰–Íþ¯¢÷3á}€>‚;s<
+>-ý)¸:Eomÿr‹
+b¢©@-k3¸|¢¶ä§¾³dPL̼¾•DÀ]#b±lRy)V–Í1
+÷•0šVc3 qž2ƒÅ¯æ²C9àz1i z'ÏÞŠÙcˆñš3ù%´1²DcÁæáxtdQI×=¦Sô女Ö3Ÿ5£ .B¤|‡i´óèÁ›ÕUNg‘
+Ë'ÑÜÚùVbÀaf™ø¬úë
+$?JHÏÎix¯É7bäÉRÖáN¨%*<Æ*cyRhfÙi•4BØ©K§)Áü¢d°·$ ggÆ#¹%¡ù¼“¬€¢k¡Ž¼ N§(â$!LÀ’ ¤vÒÜóIÛ @Û±O
+¨í=°N,Ä=˜nÀ‰¢PÌ93±'ÙÝ,¦*Õ0{ `ô‡hp <×tù­¨!P†æHƒ4ѬithåêóV14¹K³Ï¹J¦r/ÿ£žCu€"£¢ëýPyfƒ«Ã;Æ
+bÆ¿q÷ÜÜ8§V
+…+ úÙ)ú²"$)GyÏ„9¹íi­ÚË2¡ùúÖ:ŠÚ!ÿRç½ ÿ1X8ƒWfújá™^¹è[C4
+瓤}MÏèzn3 ß(ü±'Ðbø*A–ŠyJ‘'"ò
+ŽöM
+šPYezzQy‘wÐÔíª9ÁojXb"À¶F¬UÏ•"h\!œJ<ËØ*€>„K&®ì)>‚.Uÿma’XÂ;´&̕䨓d…d0-²˜a+„~àïùó¹5¿|7< $ƒtDl)N„pÍó¢$Âë³R<Ó—&óM6ˆÅ´€æÞç1".rƒ^÷e¸=Õæq~õ' i@Q6È1å@-ÚÖ ršà¢HĆÄ*%¯„®O³œŠÅíä­fœ›®i9ê¤Ü¿æÏO—Œ/Y{’
+Fnƒ¡U†¤}‹zFF>³œrlž£bMeêÖI®™yDa0ãw«¹´páˆ,i,+vH¢!¥ggÍûSîä‹uI2Vöªh¡á-HØ ìܵe©Ô&­m-­ÉÚBS­žç!ÚÌZhëq¢
+«I´i»ú wTª`i`êë“ÜÑœ> Ô*éáúË:!TYûŒèå<E™µÛ™$¥¡ëÅŇžGÕY>«—*¹D=¤lÈ8–¡"MýýRŽKúh=doü9?'³À˜cªý9(ÁgÀ=ê°†U‚Ër¦Uª¾±°¶ÈZ¶¾`ËØyÄ
+õçzÿøÔgÇÿÚp‰^-r[­)áŒZ¨L%Å‹±ÖÎ)•’áFŽîL# )d£ GãÖ稟räß­1Þ…öe¦u5 ;ឯ##Û梄¯»·š! n¯>ÂŽ¼Î°‚”£ˆÚ€wÄ­ ³ ysì= õÝÜ©ôr;kIõaQiþàÕgÝÞd(Cä«L©HÐ1£ð‡Ò†QpÌ1ÕËÙì´(=ˆïõçÔ?ÕŽjd©ôjSMhu-U["ßàÈ~6_Çõ ×æ1‚ÚF"ö³Ñ@ д""™Å’/OK•t“B[/¾$»Åí:fô·FÖÙW³CÐH)®W##ø*(’ûÒ —ärôIÞ }òÛm×ÊÓ']‹fÙ
+²åá‰i•œ.!õÌZ6a2ãJ8+PØ÷=Iá„ÐÓ9 ­/^çWª
+Ba{4†øÇ7+(³yh/Á•Àñ9|õÕ‘} ¯_2à“ÏŦ]HÜUžƒnwBÃ=7¤5ëM®€sÀ"=l,ìE³B‘ÒL
+*5Ò†š„õ10Í.FŸ-ÂCê•Ò
+8h½˜!ÀÅ™*(:ÔÈë~zâá…õò½Æ]ÖÂBmh¥,U±×‡#Ë·UÐ[µÎsŽšŸ·9zÇzÕуgvŒv¿çعÔê×WÆæç{sÊý…êaGì.ç}<¹†VA-lo}Þ ÄÇè»í½ôPöQ—‚Z ¸ºÄ01xÍÞœº>RZZÍÞŒÊû=wƒôØ_z8ĘpŸðpÏ*ÃbˆĽœ/âà =¤”åÞA …¤4Ð0„©÷A
+mW`€i׶٣˜æ`šØÿú €“ie3iôFèãSükø)#FhîÂ7Q‚àN­4l“âÓàïÙw"x.òf®Í ÐUÓz½\Äçè÷P.#±m`tpˆß#ì¤z\«FsXÛdK +Fh/*º]î\º {ðñ¹y‰¡kûä»üs!ŠË¡Ùç2 i’øø³0v…üP›~9¦&°à7¶~.òÚÓÖÁ ~µæ“ï¯÷¯$T ÈÔQ´ìÕŸ»aEº œé»YJRÇÐ+øÛ_1â€fâ¡fäF.Üua㲌檻b+…¢ìc
+y5ù4êM)¼³‰K£o0Ù£Î[éhiÉ)Yƒ@¤"hã*³ÿ~Rb‰ Õ§79*ñí=YóÛ¿¿yEžóI7ayk¢ÙËEOÖgŽôSatøð7Eßxo(¶êâ,VñlÇe&“e«(Ên
+Ó> ƒy!5ŸÝÁæŠ —ãÑbœ²¶pÊ4¥I¤…$-Χ>FÏОó™ß#PÎñ¯0ÿûö×o Xj%ôËά¿P¨•bùˆw¬ŠÐ3ÄlŠ…Y’]êRÉ&¹ï¶Jö ¿WöAx!pù¬hB¤Ãê.':[Þº€L‰¶CY’Ùà(ù|[E šˆX0R^Ñ*!eæ‘v»M–ïÃè Ã»„Æàó'hûQQæ{u
+¢‡X±(¸áŸ‡£„
+-…<sÙ ¤¼hßg¶•3™’ÉgVƒQ2 Ñ© ”IÂ
+üqjû&EÂé14gä«mƒx™¸WaÂévqé˜W§$˜'*Þn¤ë*Sú‹;*c—`*Ú^+•$5çK <ñDâ«}
+m¥¥4]E‡ ði÷­ˆ"6Àúv™‡ŠË•Tí)e‚’,ƒ8îýÍ)rçÕœ"JýÕ"}Éi°0ü7(dÇÑ/=cXQÎä€ÆqQ{=gáàæ9ç±Ëy]:tÁÄÄçæ%ú@²…¢/ŽGB:8£9m­ˆ.
+2Éœ¸Ùì> ÙÂÈ r*ô eë_Ä—%œâœ!zøS‹q}‚¥ÓÁ>’¸ ¸h dnAíÐòvç»ìK,ù0Wa.’ŸZ\væ¿?ò¨(ètÂTfëáÓãZ`DLj,º¥Á»òá‚"óØÄ+?VÉL$*Uòw8Œ”IÓAfòèbѾ!ÃvhI çpÃÐåéÇ—7àf?=Áø p‰"[oŽë3¶ýVãynMÊ_€¢òj‘¾Æi©"CÙ+ê2B§²Ó»?E¦œŸ‚Oãmý:j$ÇJ"Ї˜Qã¯ÞBÖµÆi!¶‰ä3{ùÒãï’­QDù+ÎúÒãï>”’
+zRÞçà“xúõ65|©t¤š¨à–\Ðùí§ÈÅyúPR¤t¦/y»wûÜ‹>øÀ]«kÑâ–•ÎBÂv÷®PÞlþ{û¹‹°ÌOÌ÷½¢UB0*ce›·FU4\œ—Ú.Œ<„9Ô7á3X™\‚Õo’í8P–ìP>t€û”Xìc"ìϨäuý¼¤,<á0‚šm¤
+í N¹Éý¯’ÉÖ²Ü ë|Ü÷«#]ÖpÕ'ÑÈêÒÁ£Q„œ±?«„Ô qÂ÷Ãv,hÕKhÈg³°š5…ðåÜ"IO,ü[jcÏKcÝf,ÄäýØEMæ öèËk]‹VÉrå<+dó1“]L»D F‹ªœÃö4¬WLGÖŸñÂãè”—¬LƒŸ1߲ωšJ¦}
+ÍSIY›èVŒ†0bO x¾t)A.HC ;gèÉ<ô8}2-²›´õönŒ ~€'y~H·}*ÇM~|ÀÃ` JˆÜUø 5<b< ð8~§ ÜÅek-ù ÎŸV$Ýð‡É‰})KiZŠ†²É3„ÖM™8
+FŸ‡ÿ¥!2Å©³ Ó£àý|Ü ]/Ù3<’Æ£q¡§c‚6‚•`U¬„>ÈGeÃWGC’\¥£•é7¶OuZÊA̲²—ÇBiRš¼Ï0ÓPòyTD?RÁ Ân {œ2\ŸX,ØÏ>u+‚ô„&~Ríx¦AºSv¼ü*Aºpãlé<;ŒÍ€}EÙ󽢌©üf‹ƒ0!j,¶ß€Ê&¡‚èuÛj0nœÜ¢ö®¤Q¦¬^‰ËN×¢ïEèÇCQA‚k%Â)iíØ ŽD™ÎÁS‘·aà÷‚R°]’DÀ. D n_¶jœþ°æPoËÍK
+e,b`-N ´VÌôª b“‘ȺIæÆÉ¡D§Ä>ĦÁ§ˆÉ¢÷œOÆP ÍáMÊ~V‚¤Tø—Y lÅOØú~-̓©7,x×ÀЖ¨ð.žÌ`qH ©…,¿¤Jé }5EêKfÅ|íØ¢E×q^á®wií
+žl‰1Ðà)óÇ´ØfI±ÉÍvP,«Ö[6º*·£ïHŠ‹àØay÷á”è–¡Œ†¤¨ß—S4ãƒàþ­DI€?ÍÎi’ >†J˜€~º”kÉa[Ü…nöÃïçÐ-…žCÊVÒ{`
+øG™öUR€9?Lë W‹]Öõˆšf‡Õ¾ªu8ôØ#olýIZØ ™Éx¨¨ƒ±ÎÆÇ{=<ñQ#•fÁd; õ7´&!ØT/ý¨„DZXTY#u„ùE«§µ…·¡
+-†#wMÓ¦5‘_=×¹]cÉßø7wZñMÈ0Û¨áÜ™ûçCQ‡ª˜F›qÏ´¬‹|\:v¿b<ƒeØ%Œ )Ç=,úßþÀÄ3Çß?AטIû±ÈÕæèe@ºƒƒ b[î[ïëCí•[ân}ñ™Šž‹+ ý4ÇZ­’&SóxôÁ×"ÊÊÁ_!„øzªÑÑwªÜ7æãApnϲ0¤žZ«÷ðk‰µø³Î9×)è6éÆK¯páLS´Ý–ÅÐÖr>õ‰
+mª<y›ö¯­.€cN›Âæˆ^‰LVõšµýÆß¹Óú¢éä"Y¶KcX«›"”úÓ«‘îhÎìf2(}ʇŒáÓKÅ&DÞoü ‰Á'ñ‘¿nž”7ÃÅÈ`3„ƒ‰Y«ùÆ $vŸR›.ŒÈöœë37‰<“¾7
+¹áD¢RHŽ˜øh²Ç©‡yÌÕ­¨¿ŠL ›Äkó6WEÄÙHDËkîD¶>âF’_y.¢‡ð8?œ pvûÅc&²èz?LýÞ!Ö‹Á¿LÂÍ T»¤œY¦ °(Z?h來&›€zÀ… ‡q齬aéMY~iU:´Oi*!dʼnؽ,Nö©ÛB â òïIå¡Díƒ"¸ ÷ûï ‹õ›èœ"LÁz(VðZ³ˆaâ
+‡!þŠ
+ir—;ÃU×Ý@¬+¿À²¥þTrGü_pü›èô_!JÏãÉf9E×E,/rJ2IkïWÖ¨»å¥ÐF¬áã‰Ê+᪠oë³ ½#WõèSTæw¤š›Sas_ã§y–§c"‚Iˆ€èØ^Æ
+‡Âçüž„7)ë˜
+¬J"è b"NŸê ‹%sNª]4v¹aæ1O~|ãšb6ƒ-Nµ$óëÁù²u½âC˜3"è“Èy L3§×zV´R,gTK•Ü…©Dk,„¶•ñÞ1ö{z"A°Á4ÅU4@ ÎbU<Çñ&cɤe-Cdœé”iq]IW
+óâÎÜaÑM˜§µ¶úÇp.
+
+o@$ˆ¸·=5\2«›Q×~føº‰; ž;i„k~aZ!'ò˜ìºl÷H‚Ì¡y?Ô«.3v]4yA ’Tªæ¥W!Ø׉]ÛµÇñÁh`ÎcËþÓøPm:~fnEc¬eÈ(hƒšÑñtarbù|ì}˜%ä ·§E8ÏʧÅ&=ÁXÕ¾;E¸ö‡W)ψé8„ü}¶=zã31Ý}ß QÍ+~¦ÐÃØ šÑî¥>ÍhEb¨ ¬3jjxä¡óËYGº!5µõé0ïCvÚ†N
+G³Y:!˜QêST™8™¹_Í*ûN?£,½3æVæÏè×µLKʽAñxk¼× P؆¸06|³Û™L˜¤‡ˆ«[q£È;óŸá€~>€:ÅA=e1âê!@1EåpêûDÜú„P§>Ahœ€s\¯¼ ~Rù§-ü+÷Ø5ËØf¤ˆ¦›¥ÃGEâ*ï×ëydÊÞÑFšd-£˜©²U·Tÿ,ù¯¬3íp¹AUÉ ÝDN…>÷ÕnÖ,á5AÛoTå-zåˆ Â ¦Fk§ãGMe†_C{ºZƒq´½U’q¨Rò´Û/Ñ
+´jýX$6­0ÃÙ ¦­avi³=<2IÌeàv–"Ü@n/NlˆÆ8iæ•Zuõ(„×®n$Òû!r™'¦Hn°R÷²-LKòÉåVw¤è0u¢â¡=-3ˆc•K—Ïr¡kgšr‚á„¥H)õL¨ø•Q)îÚ'Nj¦C0ð{"R¨+‰¨”µ:HÎÅ“œ‘þá.ˆOϽd¼ŸßÆx1CúEA¥–¸˜*îÆ/Õv«+åÉ×1Ý«‹K$^åÝÔ5 ì@;®æ“×ÿO­x, E-Ûˆ2d¤NÉP/«XJCüYýcêƘ龉Ç𘼠íæ öNЉ!&…à÷´ äY°½¶³ÎÅ‚óH_¢Ò‰®8 ¦Yý?·ê£cKªxFœ *8i^9®²Khð ¼ó¦
+îGŒUè Ñé@}¾Ð+ó}ìŠRDTt‘ãÚ'ÖI¤)Íâv-]$=<À®UµÐáþV‹¹äî¸ôñèOAfš²ºŽ/«¨¯ ö·‡QGá8XÑ[~_Êj¨G°Mò&æD}iêº t$Ö(Ϲ™ û¯•Bh«€¢<-L0p¬„x
+æarFÚä@H ÑaÎ2Uñ@ØÈܪAƒ¢IL_hOë(qÒta[â4Y[-Uó‚¤À£uMï>p¡ÈFö0Ê"÷+H¤V`•Êb>úµ@p1™)—Ó®LâÛêòÍóÈQY”ê&/™êæ­`µÑz‹wľ8A‹.,¹¹k\k0ɳ6}
+3—â¡–J½‹5nWÏØ`vh¨ ¿íÚ‡!òlŒL:ÓîÐAX•Z'‹ŸÐ}lõ> \Ut1õä„ÄŽÖñݨ‡ày ²Ú1ÆiwüÝŠ›¡=¤®V.®Ñõ÷A,Íe¶dóœŸÞt½zq=g
+
+ Þ¨g§¿ÏÖW låaÌÔÉ‹·c6à>âC´ä—›À%û!Ðx´Ê‰øåhÎ-n·Æ6%v7s
+‡
+5|ˆ.6,—âSØ¥èÒ‚2ãÐ{ãiŽ^p4
+H‰Œ—AŽ\9DOPw¨ LB)Š\{–¾…Yyî¿Ç/*{œõÓ(4ІËQ’>ŒŽÏnKËÝûlás|þ«¯x4Ñðµfk¦‰
+ÿ¨Íòl¯ÏE«h×òzK>+¹¢^å["m4 £¢›¯ Û–÷¬N[1:ßñ]P^G)Lù,o²yÑ/:Ó
+õ*³ÀÈå&>ìzuX7ãMÖÏ1¯Eîùf5é×]~í›àQ¬¥}ù›¦·‡s×ÑŠ:cZ[ЫÄÊšNfÏ
+Á½É¦x·]ÂWÐmuo6]¾:¯þ.h9%Œ†1ê±KÈu|“2<­é èçÇ>úç¿?²Š—Þ'³F1€¿ºG̵V|þwƒlÌæâŒÝ€.ˆÂ²·t­Û"krÛT•ºJFfb°À¯ž
+yÞL¼%=Ã`C÷g¯œ>§°yÓ}óÕFLÔã]£þÞðp£=¹Á
+ڸȱ+g—q¡¾ iæG±wÝ¥¾ÐR&
+¾69n@T)Üí»ó¯ &擳ßÒ[ç±¾Ÿ­†´œ¶z÷^Ì0l’ùœ‰$3¿Ám{œ7ƒ!4çŒ_ÖJKX™XX9òAã¶ï/ð5lƒ+rNr—r¦ zAîIûƒGù†LèŸuOÙŸ„“‹ñ¼ieÁõÒ;aái ( I! ÿ•iÂõ<Iè,Ì»KÔŽFÎTu?sþD¡âŒéÐÊŸ7Šñ]2èÎÍþ~Ê‹ñªOŠì„ Æ{2%ô'G3CŠOþG…lC’~‹柇³¡ë]{NÑž‡@¼ˆ:ª^£g˘â&4mC„ñáȾ6$š¦ ó½T»Úä‰Ô‘¼?'¹e'o´*#ÝuY©Sš××ypwIóŸýyUºL¦±¾Ê+3"¢hJPÉ'üöôR â_Gö ]ëwÌ$û²`Ï¡†"˜$_OfªàÈù;únÂÐ3ÔÛ‘R½´‘.øÜ©·«×Ò”ÝK/¸œ`qVN‹£C™‚²c@cq?<q6,Sï Ù²Ãc/~ü„ü™1 t©GhoóRŠ “ZK»?Ǩ¡¤?gã^áŒô±x,2¨q3ÉW/^Awé„
+b¸ÒÛ¦Ÿ}0áæo ×‡cǸ:z9o~P€•éˆ¬®~ÔY³>–eصFÓ¸'mrÞËêÊIàÅÉè;HµƒÁ3s]{N²¥šVò¼ÓPehØä"É;jx–"”¹$K<oú;nº*‰„(šÞ£¶ÏÂ%ßD|ÓõôµÔšóŠ}e\ßAGP¹µÐËußeNàÑ452ìïÎ~!3ýÚŒ.!þÜ‚>N&óÎé“™Úª¨¹ ¢L‹•eG®­­ü]8aXˆ{íˆkFt*'tƒÎ&aÉŸG¤' OVY½ï'ä‡Î¯ˆº¥€1ž:õå”™É4—´ÖïEüµ ¯¯ýqש×A­ú*g&Qæ8šˆYe¶‰æõÙ6Œ)B|ÏVù"¬š¹AÚ‚žÖo õá¹aöœŠ5ïÏl®b!í8c+erY¾!¹¾b<¬õ^6:‘Œoüò¾J˜70‡ðx:äÞsE²Ï±ü­žnTWåMŠÇ-ä…Ÿ?>n@·L—$ ®Óø6ènú¾\÷MЊp;X¯ R—ñ ‡Ò1˜5Ï抆‘3n²Õå䆳41¸«o@Ø<pt {F2n&ÀàûcC0±ü!M$ƒÍÄû26þÚWI®lÒ‡\› ØÉI¼÷ª7˜ÿNÆ0D·ö†^Ç.øÀk~úƒ#=#…ŸõÁ ¢¥‰àìtV&9/–TyFŒÛç>†×#$úpËPnH4>²!0ïÈèt–Œ®yN̹
+þi9f«sà`»¨F—ö9CRœo^y>ÒÚèY+‡Cé IÈI(Žƒ©eHª«¨6µ!ÇžUgLl¹ mCzÚiõPšÊ !Ü57{ÉßO)ñóyÕ+h;Yn ±— $–¦cê:[JP>¢Ãœ§€È²Kf©Vc‡ø-N[ÏUÌ®±(0Ç'Iº‚´‡éÇ>zb¦þf«W¾î¹óûûjÿðVaÆð7öÕ)éÈä$„•‚Šk1±!¼…"#gÖSôX-uœsð™òr¨àÓ³ ø<ЭP ñDôê8dµ²£o _s/ …<IãÐï7 òUšKŠ¯Ô#$~öªŠ¼ììG©Ùkmk }ÁlûÙ9`²$=ë LŽ9Grâ;ԕ̨«˜RoxbAúäè@†VÜöóRÌÉÍk¦ã™ÜŸ“>Õl‡ÇÓì>bRóð§VäWÈÙÌQýIRª‡æci'¤cQØV
+uqK`v®yõbÍOim¼ HsA
+Ïqx¥Hf'reÛç‘Ƈª~)¶\C¹¶EaîÒ6Ø‚ ¼’ ?URS< FÉ ÁV5ª¼áÃ8ViHé©ü$ /N0d"ÏœGÛ\2 ÓÝ8œÈíf¦,xiäå¡°tÎq¦Š†À"[%µÄXÎk6‚eÉéðMJøÉZü»]™ÿ ž7CˆŽ~nФ
+jÌ°Õcè*‹[©¾C [ÍcþOLyÁ¤¦/@“¶†·p7 ¯´¨­óWom/㫱ùŽCb­•ˆf”@þ™(²`ä=Û>2Y‘3ˆ 쯣Α‰thºÕ¼ä3&ŠÿÁâ´½ËyüŒ<›Ž¸Ú›œÇGàÊ<
+5ñßcf)“ˆ¯'æ!†#ƒ¨÷ÿ3ÍýѦÅ}PîÈ_i$g}è_(ªÇ-„ð"™¼,ñš¨lk³øô»@¬&|+FXŸÅÒšþ¶V6}ClÂ.rV«'wYór¯3véMxOBa®
+E‡Åš¨ .ÙÄLInH“;•kçq/À;!ÄxJP¿ìSü»:È
+¢žkon S^áéAêÔq‡kÙ¿Òçê„De‘¦y”;ˆ J âüfÍ ù8+²\E5È{óŽžÊ Æ‹–ÿŽðn©Î’µøt
+Ò:0…ü}¯1 섯åéA\Åš· pFÜ“#ó\AÇÄ'…¢ÐC¦·}¤ i€‚ÈÚá6¿åñ'ÐçwvDzcíü•´E $ÑAÃQ%ÔÕW¬³Ä
+ÕÒ\ÆôóZ’ŒÆuŒ†úúù
+þrµ“PÅÇ4  |,ï ¢U´É˜$Áþô)Éú¾zY#6G ªï~_†"C †E"`w|0iŠÓ 49¸jÍBÙ‚|ðÖÕ¯Wßá?/ œAª¡Ù <¨75+×xËù}ÖúU.­Iƒßë
+L÷ÅC`G¤)gÈeÍ`ÏJäì(²4ÄO"1ð÷QeÍmÏ "]"ÐÂ’mÍ{ýhÊ©
+D¥ù¥•Ý`=d{C:šˆ)ÉƉ@¢~[ÀìñHŸ¯Yv½[…0»ià˜Òí¤‹V$a?ɤر: /p}k:@|Àæ’IPú;ŸD*•:ŠÛηik‹#¶}¤ÇW!þüy#<T§©îqj¯çC/½ÚŸº‚²Tt¨ åý|vµˆ#"üð(ÜhÇ¥³ ùõ¨Kq
+oÜV‰X„óæÉ>Š)’1ڹݹ/ù ²­§ãÛdÉ<_Ûp+!¡c‹©ôç¸x@Juø§™Ë±ÈyñîçÜfë~›ÛŒuõ{mnûr¯ð réÔyïÆGÕ’6°ðçÅ‚æ b
+’óø(<Ny5wü¥Œ—⌽æxš+¼—xÈ…ãÍPŒÊ±UÈyõNç\ç~›Ûü^u›ã{u+s­ñï¶Ê—/}Ìåàèòž¯ÐGÒ-_ÍTòb`æ|Õ3)O¡nz'Ž¢z½<a2Î ¬rÔý~ <bdF4‡Á•ô\E¬l.W¤r±ï 1& €3™Û™ª4£Ë˜ÁM˜ lt“qóªf¤¨ i…NzÅ™luÆ&3K"¼³¹¦„qF– ØiŸT"«ç4¢ø]ÐI^Ppã
+Œþ“jÜA|ŽiÆAPÜùç(Êus­„}³kcçQ`ô†±¹Üo"$£„µÐ>¡î†àdS_%Œ]âÛGŠŽÁtOY=ÖÓO ˜º)² Þõ;ðlzCåèýl;½~ü²Á€°%ÇØ·øtN¥6¯¸º~Ô—å_[É, ˜÷“0[Z°p3þ!¼¡8õö œ¼Ek1šv´#›
+‹ˆù†ÿ»…>*®žKÑ¿lç`[1SŒ_ò¡g~_“Ýv‰ˆ¾ªH;Æ_ôê,­=`„3ÆlÌèçˆ@È`-&ÐÊ ˆ‘‘€ýB<K²¢‘ó7±
+RÍ°ŸE€è¢¦Ú!ÐüPXÚË1f‚%^ñ˾Ä*\I[H¼…y.¹ä{«þwË•@Ãk:*Cƒ½/á$â>䆎¾•ÞûQj…NÃôц±¹7û*3» ’fÊÓ\¢DíIi¿âže› CPÝibäxú‚ˆ8ª¸!¦íîùFRÂØ$–€c¼@m‰‰ÂruëûsD‘¤¢gŽ)«cl_Ÿµô‡wU°sL¨gµ¬+c¦Ä5Ò<«Ü¡ówµdÊÌ:7í5½ž£á¥KYÊâÌà晇j¨ QÆÇú6¦¿DV¬6T3VdvYŸxö¨B6…Ñ·c˜<{;·âÕXî?‹ÐèÑ„ì ªêu6¢¶^0 UARøn‹~gŒßB›<芼Sù/ãå²i‘Dá'ð;Ô +ï—%ò¬ľ5°W¡y{¾“YÝ®?ÿrÑ \ö©¼EŬŽ¸…H³aq§yµÔ©!³l:G‚Líšuðàx±Öh&%=ó¯Làh9üÖ–WÅH7Mb Fƒ•‰ºÞrNÏNER”°žIðt
+ø Ķ*r ùŒéluf¦oÂò1Ÿ Spå,ñ·£(üKîk+ÄmÅÂ,Œ'«tѯ‹Öz|®<z,–ËøÌze„¸k=ÜÁ5aŽž¯-ˆzå²–à>Þ §!Ö4{)(K´Áî$‰–¶´~¤¾Ï(ôeúêHÆâÌ4*þhCïä;¡GÑ׉nh4Ð<òi‰ìÓ÷'
+L¹OÑü2*˜hf¨ðhñlÿTGWäu˜–ð8Ø°>Ù/0!r<ðM3£ëˆ_$lyžPI€å»•‹[ó6sÆP„.û±ÆŠ;ÂÙšØO >ªÒŽ4‹~
+ÂN˜×QÖªòø¶øðÈàî9ÓpËé:ãNtK­& 3•)ĘÒ'•€Eà`[á™p7ØœÊìH‰)Ýâ´‚±¼Å]ë²!0‰‚#ƒòÜK’MžÄï<êîæ7»ùÍ Ö9”DÍк0˜îXLd
+âA àH˪yÓÈÃGq)"c¸l›QÞ#odŽ=¢ðD&
+ã®ms¯…_¯æ >¯!¿¼Vkò¸ÄRøfuûĽ x€1Ûñ[Å7Y–p÷$Š¡ND”
+þC¬d±ß&ˆœÚpøëDNu„<xHl2ƒ”ÅÖ÷ /ïIU}BpÔ¡ç°È…Œ‰‚Yý U`ÈS%eYèõÌ$1¦‚AHvwÚ½Smžy§:Ÿ×üåCƒÄô@dë»ñ±ö€jÁ ½ìÇGé²»9·ºæJ§,Ù@´Â†Æý A³àNiñ5´šÏLÈ\Z!VÒþn™D:?Áíè¡ÉÆ*€£_žv 2©AI…;ÁÜv¢L7Ñ.Í›òt¯†Æâ ìâòopVÆhœ‘ÁÌÉ´„ë\`R°p=ù¾Ä w‡¢öÆÈŒ„)
+y+*›Â´I-Ö!]JÎÑöË4Ù‚LÒãvˆ¸Ê®Pý„dÕDýT!…&/“ZÂÚªè+4}l¶NC‘ah«ÛÜ0w.â~¬·Š8V‘áKZK½fD)ãý*á0¸¾fý€ùèIö¤ñ9†Öa)
+Ôjl°ÁÄÂH!¢EaLyÁ½ºe¾3ñÉË®…¶•‘6æÃV©¸À„•„bÓêŠD8KšÑfë4Òwg`ƒÑYå«_®š¡‚ÿfö§EØ8tÑJMâfZr²ÎéœZilAFŒ›EèÅhЩä°ÅÚŠÃbäxDæfµ¦¦ Æ¥®l¾BCZÚŹŒYqð¶¤Epƒ‚SÜv a
+ɶ´¥÷Þ§R±î`^ÙÓOœkÑwãÁÅ;LÆ•/ï95æd.Ê&ðLÌ“U` JÚõÝ%È)I:†‰ðÑ¿e9Õ{èÈ¡¤í* Y£Îd‡=ÁeeªÓÄ”›~yÚÔh¢šõ9~™/¦ uWÂà™Õvr–¸i*pôñL7±ám‚è2Þ˜Ñô~x=d¢¸]&˜åòÉ¢°ê§Ô ˆ’¦zÂðtÕhCÖ:Tª95u3½INŠXexíê´³8Þu«ûmø:»52vXø†ÉÚä} b-Ç=ì©#!ÂbçRÇk¶ÙìPÂC3¾^v-˜º Ä/ [!ç B7å òÑ—Q¸b5Ò„0ƒ•xË:!ðO_à'DA=aêÅã ¿™Ü$=A„PyuŽ4…0ñ°±2h©ÍŒ’Èd¬qLÓù¥gEX@V}±­d- Iä0­ƒïà)jŒqm•pH„
+åø:7’Öò=¯82@È7“BæaŒ‘ñ€&ã|£òŒÓN&rCøAà¿œ^,´q–JÊ¢ "gcffÇzªázEßR,Ðë ÈsèJXu
+?o"RnŠ¨ .=Bõ”CƒÚû‘ÐM–u5„,TØ‹ÛÚóTWd-YÙžÇK‘y-”Þ—õÊ:T¤—÷Ï’
+íÎJu¿ä/CVwp[Ü}+a<eÇ·d'o =ùÕY!2sãÙ7 +I«øÕFâQ šV5,1xˆ¥å ù‡òrI®#Ç¡è
+¼¯@AüŽÕCïÂS÷þ§}n|¶^2îꨈj "“ÀÅýÐç× øcQÚ˜(T…®”Šæ*uù‘Ö•¼u•±ê‹_¹ŽXƒ $•cIÿ`»=,aCOEêÍO¯qÍþ^ôÆë‹sg"Ü83lÒÈ&Œг3ˆžŒ”•½øÄî
+”UI@´v>'«íô¢k¨ñ5íZ ‘ó¤A†Ž½OÑš‹áv–}ScÕ'€ÞÝábh]y$
+@=óüñíLÿTÔ³ø A.V¸ …! ÈD[Ì‚ot>Áûª@Â2»ú ÊÄ|q1f:àŽ¯Åì ¸}#ƒ !Ýñ·9Îá¡Š>“¡ÅUìšÇ2—X?>K.
+‹çšâ`Ïe/0ÿŒ†~ãÙ÷5ð$ÂÑîx6üÄÁÍ#îÁãš)Ü-DI¡Ùdý@ú«=¤›*|¡Â:œêclO³ð7¬ïܳ̈Pfa°
+ÔOBŒM q$øþJźðGc^gÁ¸Í±–ïxǼ“Í.ƽüÌqxÓHÿ¢’Ô‹*FZãtÝ?Ñn©—ÃK+OEFÓ ßW ‡ê;SŽÌ¸ã;ç NM¬Ž´Â÷ð+”%ì‚‘¶¥eeáðe5††N`†»R@œ‰p#2=÷Zdg™øa¿”›SÖVþZ‹?Kâ*“ã>î:‡[‡ÁG¥Úi1î“xûÞÏã¸Þ,tˆ’s»,U
+¤¹ËÁbuGˆ$LK€yÓ¯ê¶kH]²J~®yÂN3Öy1Âýd2ñ0J7ë“Ç4ÄŸ|•ˆ˜óýŠf’PMì—}•FÀ³,„”ƒ3ìÊ߀•¹Îá¯a 4mr¡Oìק§’/(ýüv*:㢿™¿,ŭ较‡ëþ¡èÎëõVôcq ò8à*¾ ’& ¥EpcR‹cEßÝ°ÂyI•àÏ S´9š i#ñ¨µ]ÄH Ш¦sÆ\Z)a(<†«~®«8‘õ˜`6‹Ró7mÙ¨ÎÉ8ºX)I†xò³^£Åè&ýå
+aÈr¢]ú_Þ(Â_
+QX­¹K°ö%!Oõ@®›°g]ÝêfeÕÀ,UZ‰];HEQªÅfm„aªt/Ä5í¡d$G·:^7½ш„õÁ¡Ô_,ËÃÿ…^ÂÌÊ'²c¼>º‡sÃ6ÂhC`ƒÚ3íöêèo¬„+.èYV×1üöÚk~qt©BÄœ9·+ð WÈ~>‡ñfñ)`k‘ÍRÑÂQ!Э½è&£Ë«¨¨û*8:Uâ­oK j²YÇð½#¨Ø7;¶’ÕôùPñÅÆîö¸¦òì¡è×C¦P‚Û´x¸Ü>´jQ‚›Ä%‹·®èÊ÷䀄áÞqZ IõØ5
+´ÏE¼Ã9ÚáXp2…|b~Ì­÷q¾•p`M”ErXžÎ ð2¾ Æ'ód0ë„šüý@TL´:ˆT_7á8x¥°þZvî.PÈ€m(ÌîžÌHƒÄÜúCIŸìÖ$m &¯‹Þ‹Dr¬KerôB®\°³±àM”)„8j.¹+Ø‘)‚&Læý쩽èçëÀ‚ìECßpù3ÎÁV1'+º{QR“è Ö5…h²ýJÁ²Y §¦=(°E€Ep_qÕ¥Ÿpx‘c[KÅǹx4Å'31JH;Œc•@ÕÚ9j‚üx›
+阿àÇOô,à"„Ðd¨+`‹mFÞ,¾F.(ºÜŽ%¨&q" ,?ôºê½¨\éJ¡=ëœ>åaÎy Tð%ˆô¨± 4Ôäú.áîà¡¿—
+ѽRì•JºHž)ú—á5,]¯gwƨà^I[꧒*X œ?{:„Ö‰ ï_ ™ãÛùF¸s•ˆœŽÍ.‡öF忆æÑó–Ï6¹Âd¯ ;;@YGÆ˹"Á,%ž(l4_[ø¬t¯ù5 ÑäKKÙN·ƒU+b븨qŠ×¶Chö¤€™¶)-ÃážwÉŸQöÜß{TÑÖ52314´Ã½ÅÚq”o%‰:A„µ÷ó9 ¦cú*ª¾¶<l­Á>e€Ð¬^ó—ìÎÅ »=mÂÍb2ÅQQ”ò’—¿[ÕO9ÚïÿÑ…B™l#TPöûÿLaÿ]E¼²¡g­ŸâÜUÁË;è&ìY¤©}Ú¾*X)ÀÅ~n„Uñâ¦Üºž_>º"*„UÅó1<¼žS–”—qA®‹JÏϧd Mèiªù)®ÜŠD“ÿ6Eá£Mõ¬Š~E ‡émX¡SÑU×ÙT¸LÁ—Á¢E9Þ%¬"¦ÒžŒ¸LÚåì èÑÐ;‹ÜŦœþت¤VõùT¢ÂNC3
+Jûª[‘Écè#¥db ˜â†*6 qn+«
+h†Øi\`/%ôn/J³s_õV$'Ѻ
+Ÿ²
+hê0ˆœùÓ9oœ¡¯q½‰ñŒ(!ˆb·aΉG«Ì&ð¸mßøë+ù=Ž fc' 2ÒÅ÷µ¾—
+5ãZêN__j~kÞ'yC:ObUÁtïÄúÖ)=1Ô(£ø
+üCÞ”÷ „Y0¬/æ‚‘`"Œ‘¼„ZEÞpæ‰ñˆZ¡@œº.LYTFS`Œ8IJlW¼¸àœH-HxHËßßÁ‘>”6% sž`øs]…@ ¸—¥Ž÷sò‡¤ÊVB(#üʬ¯
+ör±üœÝ©{Ió‡ â2 îOçÀoxHnêùü5†ñC¼Ô¾å'O¯2˜¦ò)8¿ôð*Ã\ô&, Ûsõ“J‹VnrðÔ¬\ÜÄ ÀÁç·SÍ WQ™Ž<+(Ì."Á±Æ¤,RŠ¬z+bÇh†ßň뫕OøW¬¬+;xdUc¿¢C™ÞÛ.GMæªÅ°Švo¢?Ÿ‹˜³×ÃЯy’Š†ŒiÙªvÂ.^÷°§¢… TÄm¤´¡£Wf~Ow¢DÔ§K/u”²gœá4ÿ AÈ/IPÆ–¤Ìfã+sŠs0¥+‚hÝÊfI©(+ FICŒ´ç‹>&œÍ
+f¹†ž®þ 9à‰~þGy™äÆ™+Aø} ÀyXûÝÄ[ùþÛ÷™,»Šd» )ÅŸÌŒŒá\2ð^&ë½(#±¨¥•ß±[…
+²oâ^s‘1Ó(žïÆI ¾F‹l]é7çëê0ÎÇãm³‹Çø¸óC)ⶈ/õçJIpÚþ7¯Ë@!. ¼D«Y2–VÍ[û:\˜çÙ}&¶šÂM_±ševãU*„–Å`Õ>ÅÌ:Ÿ'Ìyo%äÇ Å -סhx>ï0»Ívb+*
+«L‚µì#êÊ´% –ƒ÷ ,6 rœbG?Ö{E$W$% :Ï«É€„í+Îfþæ½çˆ»$üãÙòìó<˜áB¦ßËW_ÎÑÆ0?P×—KcQ/H9»Ò"À¡:+„™ìA>鹜üÒÑ­žêQ’)]F ¤o³å±}ñ`ä_§gúÚÎ×~«±íÌ•Ö4q[U7HχD…eMËó` ² ók% –†a‚žîáω¯§˜É뜤$ú™¶vŽ0ëÀ†ïã|+1äîŒBæÚNE†‡áá’)í%±$ä+9ìÀ®Ÿö%Ü#·ºf 5xïçvRâ¹0 T]FŠ÷qTê‹+ú‰By3zaäNþ¹›ã<)
+ÌÚ^þ"8­¢r[=l^T»Ìì’÷7éü1–ÄÊOØD˜Èr¯~ öty*ì¡(2.ï¤áߪèÓ°Z,`’™žLñ$-çÏI
+
+d¯þ½Nê­Â¾ô:ïý” 4û]àÛß´Cø½5‡=ðZw^Ðñ—’—}ú½v©a$XGJ©¿lž©Éé”›×~­Ø<ôéC'7þ·Û˜´VŽ%FÊ¿fÄ_C%ÕÛ½·"ð„:q1 ^«èísoEߟÜi\ýë* XËZk!ºM¹¨C† ååFu‡¢—ü7Kز$ÖÃ!NnÈ­|zÆ›"ÈÔw 5ñð0¸Í
+V²Íd‡œxyIóÜ N éåÿªÇÓd‚ 1Å…KŒ¤¨HòŠLØõD©+Êðs1rÛ†s˜ªãÑN.ØXùUÒD.JHØñ)¦[*èå ø[Sãc^ê…qËzÿ¶ìʬ^¢÷ÎpT¼´ŒÈ[§æé+kÌîûC‰uÙaƒJV´­çs¶A ¸Üøz)ǶiZ¡`°QקހÁÓisoøVüë /ùucLu(xñ"??A²@ïèéPPuyÅ“¤íˆà™Ÿü:sd)™
+˜íÊÝK?éwZ˜gKJÆ-®$IÿXÒzCzb>ãÑÍšÛóþßÇž Uÿ¡èDŒÛŠ}TTfä,ì|áâkIgÝ‹o²©,…˜²±Îx¡´€È`}on›Æ)nSÝÏÙ°±ÇÒ c§x»aõ-4•ÁQZ!FÝŽ%Uæ=cÊJãßúÔ¿¯×TÎÒL˜W8ÖÔŒ†Iº1‹€ó×,1Ñk–f1ÉÓà¡ ë¯QKbPqî iJ\Á¹…ñÓËj”q3/&Š<Ÿ²ô>¤• 1D„VÖ2+R"º`™|0J¨5kyš¼ÅúRƒ]ð‰Ù¸“ ^Ù Úæ92t4ž±µòSkÀÜùTi²oÙÓeoº³ÕÌ5¼ƒL°šæì£"¾l
+òŠ\ìó¤FüD/³Ÿ 8IµïG3¦qxi9ýžîù`êÆXi4 XëÙÔ ð‘çÏ·¿zºcO·cu‹§;âyB•Oï.òýc ·CÖ±-·^Ö§þ}½ÌÓahá‘š=|b7Šåqλæm ±´"¾âGçíù"f b÷5JX&ÃȧFSü–u*xz›E|ÀDø´œÎFÛVD7:=Jä àHàáM%I …Ç3žÿœŠN,B‰Ý]ÀVÄç¸t…Z ù},b°Ž¾Êà„à絡o< pèÖg8
+à”ª•ðÛÿ¥Ü©Àš(7S¥²RS"QåYª²†ÍԚ׋â’
+¤±>J~Nh´X‡DôÞnçTÀ‰j‹•Àà ¦L9Ì’1u%‚ç^ü)îö)Ä”@W™²50w€Ÿ‰
+tcž“XbÐ
+Å|\tH¹ûçÞŠ¾?ÙT[h§T‘Ôž"ïéA‚Ep¿.EpâÑ(Rïtq˜·°”Š >UŲ6K›)>ëÞ¶Ln9sEFóçü´%†f»¡ŒÚ8ƒi—yNá0°±dÔSÀ]Hï,Á´d$‚E·OIÁÇ?Î^ŸÂë Ñο'I  åN
+RSCeÓ¬jž°ëØ0J* Ôh­•$^_ÇlÃ(‚þ à8+"„Ò©T‰ºó˜‚›]uVhG™ëÛ­„ÝÏĸuˆÀó@º­/É™Vé`²"Ì~® Ã+¨„
+H°¬ŒÌ®ˆ’¤ ®J1a´p~D˜Á™%.ß<PŠ¶
+B "{Üs€äR’0ÓMOyéóËn3&ß7^¢j)k!–¬N}Ý’½®ÎwxmÞ*nJìêÒÖÎB)ù»uNÑdÕ°ÝJ1n”™ê:>>ˆÏÖV'`ˆîñ–ÄB"…ìáÕev#ªšn
+
+€FŸºÆnnô=$ ôn>ºÒ¬¬—4Bd^ÊèOEM§aäáÀ³BfˆI4*2€ÀxftµÌ’
+V–„l1Ï¥<†ì`‹n0ŸE‚0…³[t–`áŽqÇpõ'k¶ÜÉf0 y„Œx+er’ä“­„$¬{$ºæc
+e=ELÇJØC®RÆÎ&ú̲ºãj©†möÔ ®¼~Ù”Øc°b%„v³ {wF‡½
+%•NCzi1%ør€åÛÑ¿ÓSJl«Ó“ïÖ9gwš
+Òþ*:ž%>¡kÄr[åý:ý•;8;rê¸R…Fk– XD¤ à–úàZ¤•èsês-*¯µ`«éF`¿ñj×ÍŠf³…OjyØ‘¡
+«Á:ä]¦Ãµ1Ë”¨a-G(ÞÞ«5Yfú6%kT“—“J®7ë(Q1qñV‚I( 댜[Çs¡¼:0Þ
+#Pøp ¸ƒíP¿æÕ¹AÎ
+áy]ÍbÑ Ôª~Á«ŽÄ`%„!ؘ–! ëžê–BRæéwþŸÏÉ‘+—ŒÈqMöVxºÌ››‰¤ÔÄx*Œ@ ˜—ÔÄhTŠiSbÏ ¸Cð@nÝê¼/N["cd†ØgPˆ`ou
+3í•nÜ>ë )ÈŽ†Üêþ4XœnõÉè´#[UÏÖá±½zC§‹Ñ)ŒEÈ×YB„°*X™º´anm5þx‹•ß¥w)ÁO"_ð¤˜Zf;?0–ÚõA¿òÌøúñ+¯¬_~Á1}xøÇß|”Q
+¬l>þ{¤Çolp™XŽ>&@w,‰<+­­Ž‘HCW–öÐ%Ý°yIE2àIv5X¢ÅCÄ1Åc¤™æ7¢¬¥>@“Å(LhRËâ\çöbß^þà~¿s¿u*òž¿âÀü¼ñ¼ù?”~x8®æ¹‹wºÌã¯/cœW¢wøb6ÈI:¦ Á§-¥lì‹FÄNE !†ÇÿŸò2G®+9¢è
+°Ú25¦퉖¶À%ÐRhÿ:·2ë“ø¯^ë7EC ^TÕË᳇Wx¡ÀRn#ƒÒXÚ›7‹1(T.fo¼…5D¸ãó•™Ìÿóµ/ÿÇ›® ‰ü.M›¢Gëfø½
+ÀKkoã/
+WCÚÅ@^MØ»é§4™UxÃE<ûE:“œ¢Ajãß £©p± ƒAÆK+„=ÄÞ wÈŽ®¨k¸ i9o¬Çg5ÚÎAýEÄ´¦¹/¡Mü•0ƒ !½°ÒqŸBËeí{£æûª
+¡V gpPä9ƒŽöÇU(,í¯NŠ#PFåyb}Õ3¨%&±Ð,ˆÃÏ!åg‰¹›0 \ÖžöHVmZÇ QÏÜ ‚^2gô›éÚW1AUÍëuGA®•Wššmk'l†°à"OiqAh#ýÇW¶~'%Ì_Vø”yÙ ÷Ô)Fs)n‹¸e„ƒ ìZb8¨-ÇR÷WÁ)ps¼¯Šïg»ó×%‰S&AC³s
+þòʵÔÍ_¬YÅâ•àN)öÀ¦â÷WûLh s^1Ÿ7fòe³*ŒÙúöŽG î¡,LV/ì±Aøw0¸©´IyTü³ Oï7…ÉÒ˜Ý-
+‹¸™žÂÞñypUßÜ…/&ÑÁÕ³¥›v~øUÌ'BÌvwK^WöÚ
+Àçž+#ý«ž¸‚¯â£\¬AÜ7é"X·ÝÁ÷ñ{m<¶aÙõÊ÷+?dñ& éŒÀÍÉ–A&á±vϬ´X
+l/‰}j”² # .ŠT2ƒ>ÂìE¼×é^‡ÈšäãAÅÌ”*׊©8.•M¬¾tt¾ËÏöìdRÿ·Vñ±Ï’2=ⵄ¶vØ[ñ‹°ˆX5eó£òêE–­¾Pø6<ÞílÒ#øPÀJ£@øiÀ6ø{;+šiZ+{ôØ95–=MŽs™¢,Ž¾O<C‘e® &‘¢Ï´2Êiß@ôÏUeǾébœ šÔM±sXâ¨1šÆYã}™ˆm†ì·«0Ä\ƒÐý¤l[zì“FâG9·¯1S`
+»÷’O@)VZB£¦Ó•¢ïóŽ¿ÍšùÙ3ƒ+ÝÊ>ÎY’Hz(’ɱ­ìÀÂÌþPÍ65œøÚ²¿ŠÕA8èi.¿ô…­]fÀýSÅ ð«£ú9pßdJV/ ‘ùšSîìÅH,%cŠ×NA¨
+˜ÏiC¯Â0d?•ãbô×°M²­JØkêd0jÆ1x™O
+†Ž‘0üA&"Cšnj̇—ŒG•“,%âç`XFêŒÁ:¶ó ²™˜09Lä¸;;xÉ’CHpü ?AQf GQÑí«H©òñ—°¢"ZÐüÀ<­ÃÁcÜïÈA¼û‚Wý¥­“_”‚3„Ý}¦ $ÇC? ƒ“Cž+´²3qOüÕ‚)›ßóò泜6Èr–¡ÁˆÁ†ôº4a¸^áî>ç'MŠ{g“¤P› evp"Üæ+Cf´;ißD­]Nw·׆-R8Ð)ˆ…ØÑI»2Y_D òÂVBÃZN‹;.©Ë ½·+¤¬Ï–ɘU{ÿ2¿\à›\ÑçC‹‚…2šµJÎœy‹äÊéåiXLJñÐdJU‹ÜV-È›
+Ê̇bWÆÍ9”ŒufQ_óŠG!¼Ö ëˆBU}L™&nèN:Щ<Na=ôãx[è èš+×uª+ªÊ|@Ÿ/,ÎJŒßþ° ÀÇÃÙìãô¾Ñ¤Á›8‘ ñó¤4…èr'*UÕÛÙÔÁg„z…Ø|!ø B†´n ŒV#Œl÷¶UI½ˆ˜ÿëŽJ€XS=üh6jçrÜ6#€F1Ê–°ê;|íŠ[ãâWAÕXÕµçs
+ß„™ èã5¸(´ )JÙ ó Cèî(Gå%&êò5!Fò*GÀ¯Ö²ËeŒcïÒ@¼Û70<ÁÀÆ´Gú ǯï.}T<@üÃa9ܪ
+ßïÎyj%“¬œÓ©›ÚÚ„<“žú¾éëLˆä1çèV¾Ë` 3Y…òÔqDÀÔ”1C³¦Hÿ„5íaO;›ÚÀ~¾vý*ßÓþ‚L:Ž:¡a=M{–› q©7‹àÑ<{3HRáð¶Énq>>Z¶ïñt,YÉÌÍåä?2V?½Ð]ÅÔëYuèh¢è8zòíxÕ÷û‡RŸ7"Èf‡Ù ([JIe‘àÐLx£ÜÕß:;Ï{ÁæPî 4~v ËR±ÍõUБ6 Y•’Gl÷ X“2ÅŽgóuWPmø=8ñ±ƒVþfßfE%F&³hG ¤6Úá´ç£î]ßß¼,‹‘Ú¾ê ÄÛA—£~Ö[;ŸÓ”å–u ÖøkH<¨Õ2ÏÇ!kP_•$!í!{Vh Á'žF^Æ@J4g °ÏÖ4ñtèqÕŸî×wóu‰x 1(6´ƒ¸ª)V_à èÔÖ„¬ó‘æô@…P
+âGPXˆ_Ep9ðr¹=‡Ym¨!b ËSлtâéÁÇvï—âÈÆ:H  ¢k
+âOØúõæ3ãQ,é¡Ô®Ì™VŽƒ(Ha7¾?BÛDCJäš:Õ+¨|6ƒÐ‡”Ká×¼:0
+®/õ®8ÐJ§Æ¼'y…{Ö02âcÞ@˜}R˜ìÜöI'PÈ\¢Õ6}:¼Fj™³ª êá«A*Aþ,[ÂÕ‘0Xßòsžj,ã„.£÷Œa?µê á7=uütÌen®¹Ìß飞ÇøZ›Ë.PÊI"jeÎtù²SÕcÞñ8Ð÷Ï=;˜:f*Çxu/ k\®{}¾ò¦Å+" ™}vE&¯—›ˆp‘[¨w„½JZÁ>$yÉêž î¿ÉCÅ"ƒ=>á‡ÝT¢º0H ®Ø,Í~TcìœJ…èeÅ¥È@c˜®Ç•‰àfùáã1óò"ÔÌ[ðs K\`¡C9$ûä¥{2“à>|,õ¹‚Ú;ÁC\dgLᯗa[‘sôò”—»nTAD¿`ÿáƬy?BDf!!‘#"Äïsj¦gmí½»ÉÁzÕ;3Ý]U]Ý¡_ÛìÉ£ pÕzÏ™˜(üm­É2ÇÚp.9
+±"SSŒ4[Tð×ÎaOË,k²¼öš;¯v`Ô:#>œ¿j.³à«˜¹‘Ý!¬›‚ø®+˜íoP7•ŽT;Æ7ÝëJ˜
+H‰”—M’\¹ „OÐw¨ ¸‚?
+óc OYÖF©¨Ykù±ÞMäs[#€‚î”n i!'âY ÿ]cÂG±9LÎvtSH È]ÁǮߴó $™ƒVôê^?’9hºt†v›#§tØ‚t£„Ã¥Nχ3*1‹£¢Ï›¤&ä±^r—0#xÚÛÖ’VÕ´>©aîÄ — EÌ=ºžƒÇ?Ãt(E[ ½aè#lðÕЈ0çx–Î¥dÌ>ööÔ~k¼›€ 4‡w«ª—Ðp¥[\Ê#Ùl TR
+O­e{^ ÷ÂtÛ>2*±•-ì_-ïÆù¹ìµ> JC‹úrj´ s(ØÄýuqAÎVÇ.ËoÏ®4ƒj‹~ãá¹݆¾ tøØ7@Tšq$’$[úãÄbßp},ypP’ °w
+.8«ïuÊêeÐ8?Õ"¾)yc‡ä"FjÆé A!byIâ Ü#x[ ß›«¹åB;cé±y
+ùm«þ¾åá;å7;HøWôö0ÕÉŽHQÛêëmô¯~h¬<hDžCöR¤
+¯a¿ËÆ7ADm6¢5YÄ8€X"”_KHΗhÃ
+>Ì`ò§ºýø« ÞÛðõ²ŸÇV86Bü†W‰ Î0þ:óÙ“ÀÚˆûÙ¯ætÅ_$õñy€\s”™}¥Ëû9$da]Ñ«1y›¹0ÂÜh ‹*¤Èy›6É,Ä@ߟBÝÉE8õŽvÐ6Ç‹óSˆ ‰n›6±(„úøxùÂÐÏèÄuY$šâMÿ
+aÓÁ4¢M$4YÒrÀ™6Zpæ± ÆÅÿêþZ¬eƒ[!Ò$V,¶–mîÃH¡ØŽV_ûúòõÓ°@3Óam’ÉŠ;Û× [á3Àÿ2^îHŽÝH]Aï¡mHüaN´L¹³‚ŠñJæìÎT“¯†-EHM&ñË›÷“W‰Œ‚y;”øVY}Æ÷ZîÖI•,$=Ýò~= ‰™7åþRQQ)vœK½{ÉY€
+øº)êb'VâQË:t : þxV´®T3ŒÞynL®]bÑ><c๨9ʸQ·Ð±®«HHEë*ïå³3ªÔ%†±¬!w}Î["©ø˜"e¥‹nú¡d©2Ikðv¾[Q‡rË4ë4­›îÅCTÓ8
+}éÁw*ÒÃbˆX
+9“¦Öµ€¾¢ê±7{‰c"8Å:…l†CCv^'Hwb¶ZP¿žæ‚‹—[9ºÄ 47m®¢Ø?ˆºÉÃþt®…&y´â’¥Ò<8q„º+žØ˜ðšé áVÞlïô=¯Kþø7*ð¯ÿüøã¿ü¢èŽÆ…`(}b ®bI´ÀZS T„6¡Z½Í¶Ì"ƒÝèS¹”’.˜ ý³½*!Ô)2ÅX%˜ÿƀЙz(ù\[ N¢d°ÆÝ:¼¡Ü|´*ª$NÒŒ#] 2SÈï‚ïD>…ŒƒÜrŸ€¼%ô=G®kÃ;LòÃÊö·k 'È3ý³F›‘OiKL6¾)z=vâM£Öt{r” ÇúJÏÖ*E*þ@ A»±òÄÿ Éئh°gõj L@r9•x/dÈP>|dº[g
+W7l&"ì
+¬$¥‹úvÑi
+ÙŽDW —ßN,óõÆäü“‚PK’ïfOðmX4šPx™ºS륨Ê$btÆNQ¯Ä Æž u’€˜¢¸LnûàÐ}0±™™Þž%+³BùÌßÔ¢k:qÃ0ÓËQ"»Ñ?_¦‰ÉɈxØS‰»‡øèu»NŠ‚/^sŸT‰§­.é°Æd7&•HÕÇ áí^ŠÖÐäá0â­<üÃófCœDÐn‡udfŠ'*yƒ:C'Y!1ï«—ñtÊ%~u fÃ0#ìÇu®à4 e€J<õsèýø£lèæü‚Š.ðjVt÷ôó­>áÌo8K¼© I [À'×ôØè[ÿcÍ)í0L$¤êCÍ_¬e@Ci#þR¤‰GWèNë\—ò ØÀ@Î ‹bË°9Ò¦Ëëå|*‡
+gÐzBd:²‡J
+ÀËØ[w¶–5MºVK;ÊÀâü†^Å´b
+BÌŸ´Cn„¶Ø;DWºÂÀ‰×ë¿`±óÕ7é'ƒáÑfwë$ºUþGy™ìÈyAø æú¬C£öåhÌM¢a€†ß_,ê$~}Q™Õôô_M!PöD×–™±´PšÊÏ»1{=,»òŸA
+R Ë×±{«Ø”:2éüKrEå³uÈ}M]ížÂòÔ¥ïo'Щ߫5!\6·Áùè4ƒ—í>zâ…óx=œcªÄãA‘†GÑ‘1}JQ59Ç@3e9¨㲶|’ÈVØ‘X6BG&× ‰„!( Ãgržd™ññâ^Y@p˜ð)ÉIÍŒý3Õaë¨ á`Ђöu®Æm§A¸"³Š½ËÄÚ©Ü'ÿâ´8³r^¦ÜµBkHYŠçÃ
+Hðe%ÊfÙÐÓ¶ òEePäXõ(Ú霾ø©8|ý±5rœóœ (=® ÇÀÇ«$hݲ…áU;­³&Ý+óE.Ì"ËÀÓÖÒ!_î®”‘ËKTî•À^¹½ä¼ HöWµàxG›Ÿgñ ç†GOyf×1õkcòJ7@šU kùð ¼91$:‚^bH!»ò~AôªÓôÜ®¨1Æ4£Y|*€+ ý®PÊ*Êå «1…8•ß*¨oxé¾*zX‡9å—$^Œœ#Ø;ôº˜ÖYW™iâ(!?O–¥ÿÂ|4 b!qèÝOƒïÅáÃæ÷œi”¯fT›1¦›xuƒ$Ž1ñBmø*(@PRhz™wߊv G)gzÇã\È™)Z=Ë
+E*c×W¬Ad†¶î{1\3Š6â½Ô³3æÄ߸FCÛö½®ëÐÙx±Žcxpùw{ä ÃOf†dȃr‚*0e0P?.ÓnÐ8ŠŠÓ<Ú¹†¤­|/ž%¢ÎIïB§¤¥™á’OiÐ(•†ÐYBkçÃ<·Åó¥|'yWÑÀØz Ûfå<Å['ðÞš¶Y¼{¥'tÍ:}§'>ì„Tw±Rz4ò™]"ðÓßЯoA–¡<± }Ĥá \Ãú߬~Á+ý|‹·òÔ©âÈÒLÌ¿oåög6®Hªq¢JË»¥åS™ÚЗ¤tÚº¡FŒVX ”'ß‚êxŠ¥êQƒ; Æ«¸|@à~AjÊ3Ÿ²,¢š5A ÏäÎÁ%CTàý+ü Ü÷èb#û±ôìÊvõ߀þ| Ü-r™t›Ûß¿rÚ!1cü`KºìÃ…`Ÿ<Ë”ƒ€u‰¹Þ»W\¾JÔ@_,úæÀTþdÀ– `!Zš†(¸ºdŒZ@À}ÝžU^ÿ†~¯áøŸ'2‰ÿú¹›ÿ yÐÄ)êh^Îðᆶ/>ó툨Sï?z
+Š¿Ÿ@¬Øâ9*a fy¢\EO¡sÌÚ Â9‰–£4GÄŽˆL˜H^ôWÛ gÁ¿ð ê݇š àN‘aDH•5”‰‚§A¨~!Ï‚$¤d¨÷޸‚ðLiÃ.3Jk«Âqæ\1kv%R%o8>Eëä»Ïðá;×:–!6p (ƒ¹ %$~Á€„b,¿5—û]Az|[óTIÊ.AA ”àn Òeu!ÍÚ Aׂú Q™T4VG˜êÞè#ÆC}•BcG;-n‘B±ªÒ“ ) ˜„˜R1¿Ã6±¿•I‚ŒëÅ Ñ\i¿º¯êâÃ0Ì1 †¯P›ÛBhˆ¦lǺ³aª?˜÷\¬HüDɧ Þ䌭Å$xZG¯YpfL~ÀșŠ„Ì…¾)»eë*ó÷Ô1Å}«.Ç:"E±×SÒĦq#rÈz½¡pÔH#Gƒ0søAæ: BªåùpüÍÞ7Oi!.®í~ R^™œRw” (™È–ib¾¦ X¬ÜüÍdëQÇ ž7i+vš2Sëm(ýd<znз •¨¸0Hµ7æžlbÔªA"®þÄtûüWl.ÙH„í—⎙a†¤Ò£š˜Ãˆí¦± „¡ÎèKªò«B`¥”<h,G(÷ÂÛHû¬Þ7xKò%­•µïµT˜“â«u4Ãt1ÜQü0˜|0oš£51ª…)ÃU4/'wÁ=ð:@÷,H{yì*J˜CèÇfŠ•éG"S¦O½œè ó•—Èjf.Â9{« hh|èêÛ´uºÈÓiT¢JAºc1’Ýjª´§D™ ³wŒÇB E.f ŽÊšYBÚíés˜8ij ùs<8¿öy,g[0† ù¼»Q`ì`ç6B¡©!ñÒš 0çƒØÄàÎÃL3cÞØÍG³Ñø Õe‘yäÒpõЧ{ð ,7ü¶•£)[²™Æê8USffÈ('ˆ£¯‹*<füEšWuŸß’3)?¿¦.Å;âÚʲíÖ3V¬?=¸Ê DŽ–·Äþ׶i‹ ˆ¼Å ˆŽ1ÁvŠÕ·6‹³
+aÁ!ʵ-<ötQÎþ<Y> vK ç^„}X†Í šz4ÏÂCó>QQ‡A= ÀÛ¡Rü™ÕæåГÁ3ˆiI”*G'Hƒ‚žbè£=M#…ex¥{›£È¼†ÉHá¤D!ñÍH[2¡bEÔº‹2)Är©BLi¶ \ˆ¼”1¨g"f怠e¸>Tõ$ZæÝÍFY~d&ëñ'зHÅh!J-NÒîYVð¤ô%vM7u`R{˜*ÌáNÎ$S–‰þ2]Mòû"k¶SeŒB£Ò _ÌÕÞv/r›Iž»æ áÓ 3"¿H[èçe´C|p3-Ðê È›úŽ¿i‹&KD/øf\:N­3£YóÈ(âx#h hpõ¤$Š¸-ûT©qnÞ¦Òx9š^»N¥0LÕ5–áB˜Ãà’· Ì^
+çŒA+‡B/€±‹æqœª .C’hÔyš»µ¡GQ‚
+Òçe0•4I›PÃÞ‰Ö Q€<R!¤ ¡÷±JòDˆ"b[¡oì9¬['Hé6(°‰‰D¢¨Y¬!¢Aoí»‹Çp œ:ª©¹‚H5Ð0–ƒ—H+î@t%©/ÏLŒb×âô3v¦»«@|‰ rú«0?Rl:Ï]t£eÑÖ]7GK](¿g ÿ0^&É‘ä8=AÝ!O ãëì¥n‘ÛÈûoû}®4EÐMÑÖeÕ-ANøø>²ARØHµÈ› ÔAEÂ䌣aMÒ*|1v
+5º1¾ ŒÔ%¦o§¢]Âo5Ò…8† 7yت¹gDZ’/Bz˜yqëäD3ßPöÒ48Å.WFn£X×YöúA¬ F’Ãÿ%‚6 ãS£äûêÉxä­¨#ì·¿ŠA#O‰8=bi0ç¼ Ä„¡ÂtÍ)8§ê/Gù¥8øËŽÊÂv=bEs¸Ë0…Þ?ªÝ3Œî³\þŽãƒQ HÔ‡…Í„C°-áÕYÜŽÒ䌅‰íÔzü\*‡’XÏžq‘Œ°]'=‡‚².TöH u“àƒ¼ƒMºD ·¼êÝ
+»cFùÝ„ƒ„ìSÀOãöÈÀ"J`ȪX[.ã&¶’mŸæK®G=ù‹ù4_ŠÀ@׈Å_‡+‹ÜW–ÏdÌiÆËábvXpÙCs%.ñ«Å$@\#xšá¸t¤ˆ~Zt˜³
+z9Z\Š œd…¹c ÃÄã–džN%€Ž¤±ˆ5ß}Gœ8Äþ+žÄ(§ôøOG(®äˆ4£‰#á%yñv-L…¿;›.KðMÌ?OEÌAT!;„'ñš¹8” Z·f
+PdMÀZ„¨{ýx˜®•t-T[ -¡ËN®4JšÄ „¼ÿÕçàÁ©£ÎiŠ>½Hü00
++ :ö]“'™ÁÐÓ
+ɶˢ¿q{Âk‚ª¾³9f­#!S¯> é¬)ßÉ㮨~à€°žXèqsœZ ÆÌZêãPôx§O{Òz¡ %}ñŸni¨Ñœµz; ™eÃ]Bô­èóTtìÎF¥hH¿¢ÊÏE§ã&ÎEæÙX+¿]ôÚóC žŠïôéßâL' ÚËúéúOEâ!|ï‚o«”r_ŸæXÂ¥N¶xŠt³Y1$À
+K·<<íÔ´h ÓWò@Í*—KE ¬…«©PáEäˆòÀÚóùíeÙ&y{ò,tZêITB‚è᫪b™r, +Ò£B¦
+ýÆã.U.
+¡[â×CBƒ)2®-UúKNšL«4³rí¾²M…‘VÚ¥º‹ÿ=ùOµîOŸ
+‹(Úá%•©Hí÷f_Š˜:)Ü$›Å$óô¯ˆRLpk1ôíÊW¤º[\ô¦NO»pñçð|1!« E¶iÊyõ´pOE§ ÎjÊÇ¢À™!À õdž‹v³.Üd¥z”È t‰°\-%øS€ ýçUƒJä`å¸+·ö&((X‘G¼ ×tûŒ/UT`Œà‰&¡®!*S_ àÏ(‘y©¬­{²§Œ²aöR$[™6b†?ü¥f} Ùà"4:¤¹0.œÀ‘ƒ‘Y0š h´¼"Ó¸fÖ“gE„׸†æJ8¼ö«)ÚµòŽgJ·
+tUˆ¸A
+.˜¤ó‘á‚Ÿ¨EÄW‚UÒÞ‘›ªC”9„­ ÇÇÒE§9N‘õÈFmƇ„I=H"2GÁò0äŸ!Žoéi;ú®í|€ì›Š’Ogs½ŸcÛ$Û¤©!]¬ ®+çÃe20E¥÷wóêc´’‡¸§ì•¨ïNVUÑÔü–W–‚ð% Kñ‹9†µ†ÜzñQ§¿ ‹ÞUcoçˆHDýFRUóv>d\'Ün™ŒwáÂH†ÈÖ
+§”1¨J ³y?E÷,çZeÇEr¿Ñç)v^Îäã75K'61IVþ÷f¥Î$‰N(J¨“Î"~eÛHLN‚Ãù Qîý†+5Í,´~SÌÕ[<¼È Fš
+]Dz‘Yå›;?S5Ý‘qï[+©ñóúå xLJec¼rÀT°²¶P
+½ôF¥Jl?šöröÚ÷AÀ¥¨¯®[r-H*tÐnãùþ:ýés"B0 hÿØ攆Qµ¢Úó¬¹+š YHÃ3ûôî ‰!ÃyæuñY01Õ–þõMÍ@ 1McFxÂ(`0 ßãÒ ®å,4zsK_KxqîGO”mÖ`C Ø|~S™¡P%1V—…†^@²ˆ Oe}\õ
+Æ,Rb´±0€šhÂIoý…ŸÍr©6­ÛëÇŸ„Œw)
+]@0®øEšÃ@Â=,©ÂZ/ 2è†Ù¹ê4䬘ÌA›ÍÏIRgöyî´É¢'L uîìvõWQ:Ù¸fàãñ~HŠæþÍoš²~Z©Ã}ô}Š“É(Î3Ìá0ZQúyÁE_⸋UYQ¼¬3E4ÞŠ £#È(ÿ$t¶ õQ/ })µì²ÃÆÊ´!xDƆÐQbý¥LYI Í~…4q& ÖÂùí¶Ý@<‚‘BÎè“Ÿ£\•”¶NâÁ.Còr)®
+÷‡h­š"L&q å¦EØ\/>|ÎZÇ‹ò.ò”oíù=uâFF_µË-Þ3 F!!¦‚e/ADù[Æ c&1¿üùü×PÖè;=‚¢óLuñ$ùòZó}…ê§<•aJS~[ѱb‰%愇 Œ°ýÑü"Çeç2¹iú#û¶²w^Q
+â½(S¸iþ³eEËf7ûAúÃW꘶>®ú š(U¦ŒËzöOV×qñΨ²Ü LD
+u1iÙ¨—FüþF³>†‚BaÒ³4,¿°îÏ +ç?Mä…óÕ=KìÎùIa+S'ŠF K¦
+¶íݹÿÜÄ ü‰Ý©šL•ÁxõøàÂ:²_xÒ£bŒZ"Ýé ù[vÍ`
+U¹¡SÕ£ì52ê˜AÞ@úÆl¥ù)Î
+9Ò¸ªFÌ`,{;|BЀ®.ŠÎEŸA"êÓ®1zœƒï̈¹Â ù§bŒhñ1°?66íÉÀ—S¶• b=[Î%}[!ºˆ£R°`‹${”èîM—o4ù
+dx·V“TC6¨0v9jª<™"jT¨!sÌÜñ£·¿n t")#Q»Li÷G×-§Œ7ßÿ”#±äø"ÃÆ%‘ù˜þ² Hk³¨[¡™?6„ŒØ›¨·ùÛ³Öš·mŸ¢(æPXJí±ŽY`Âz÷Îò·=TøO¿Içf‘\—uôs0cRÁ’:D’‰U‚ ¥_7þmÏÇ\?!NAYnón$‘çLXùŸˆ%cõ k‰$Wµl*ÕZÓxØÙC4H|D´’‡Æð!
+2à‘Å¢½_,ô¾ŠÄÉDuéâÑÒM-IŽÓöU9Ä A"ZeE¦Ù¶µŸ(`îºæ½-S
+î~Qã/Kc*²Ž¬[ñ.HªUý”LgLëHGQ´$7Dcnx™•™lç¢ÏNÀðÇ–ý|gOTg8+3Ô,=/bñ)K> 'œóX„+±~t×NíÚþ]’<mŒmÝ-cx ´w¸(ê.ÓH?–XYJ¹û˜r²_˜ï äÌßÊ0—½â—c˜"ª¹‰}E¤
+3‘ùÀ,‡°4„‘öÑd{úÀ„'5ÖoZŒ½ÜúPôr ‹É]Mžr/޽ȅ˜Ž Ò,jÖ<.GÒšrøò²ø~-eiX↕=NvŒ·kì8úÇfc¤uetÆ,œ‡.EÿáÖ6_@>åš·ØÚ–k¥ye¶ èý¹K"
+tªÅjë¡U»LCP{rKÍfHªf~뇵 #LAŸÍ>z ñ¢nÐH¸2àÅv4?†ÑÇ:Ëõ¢á0ä“(k‹©a‰qW+åz웆T˜ùœ2H÷sÆOì66?þOÄ-KgsŒðTŽ„óÏ×Lý¤:mعŠFq5)ÉÉÕT?‡O¥ð·º¢ 6Ê­—^¹B0Çù\õšD™Tøb„ÊÏOO‰×:v”NMI«øhC¨øŽ©‡×„oë’¾Ò«s½†¨yéáY¥µ,ôçàæºÂT=ç[¦&Ò#Ø¥ØÚ‰(`"™¦âW!²æä$*Šm§»LOiYSÊ“Ýœˆ…¥»V;·Åfª jÇHf Só=VÎâ^çèFÇÐc³ªÖêºU¸M´šM]6Öu7%—0*3Ac,vüšòrËcúìÔF˜ÍŠˆ‘*!‡lc2; àÜò„QÍ)—×OñFá¥"çÑÃ2‡x¤%J?ºŒ‹”ÁŸ‘ÅÙI´BQù”‚‰åîuœ‘@®ºZ¾ŸC{qhÆ0Žp­T…
+c²ÿÿ
+•ã(Ù;æ7™ù‡ pD „èR†ÇAôÖvôr~Ò؃,E-áÄåæñPcÄ”³bÐZ’&í‰
+ᜪŒ-ÒNÙågp"¨#ïØ<išø<<üt¼.#c%߃¹ÝWbN`ú% L·¿*T>’-Û\ϲè£äà P— v§(reX˜%ƒA<Lä
+rŠÓa* Ë.©ÁCe\
+8ˤ:V,"ØôÛ !‹’Ž1ˆq¿]=Ь3l:ã†)8t–‘×ÙGpÇÉì:ðƒ ‘;ÈW¶ÙõñÊÖ±z„•­¢é8Z1Ï,*M×(ÓÖ’›°2§²fØU(Œ†ê¡~a[#´kO­ý¸ðÌpch²Ø§îý›¡™†]ã$öb`øMõÒúµµ›ÆFùÔï°:çb˜ü<ñ@r¨k(Ç°ËààÑòâ£Ebü=/Ç<<sòoÛ|†f@Cc»Èü cÄè¢!ÓK˜˜ÎL' è먮tWJ<„2ç35?„ø$OühÆ„%¯ç
+¦RšÜ†gÕqæP€¡4êäÎRËÙ)Ì#èÃló /Éô…î%ÖzGcPr; U—NœÅ'FŸäïÆÓ$^)w¤T⫤ár"’[J,‚ß-ˆæ:j7õ Gþ®žÊXQ®¥<£¡Wžžq¥ºB
+õ¡ŸiØØDÑ£hŽ@c«I’šF'þóÌè¾Þ}[!¤Š`´K:_´Æ‚ÕþlŒ¡þÌ‹{«ùxNžwŠ7º°1Ò‘T²ÊŠ\ìÕB®ÜDLX#ÉŠmýHq,CŠMàYÜD`¡Â¯ò½õ2î‚ •SÖ_ƒÀFvHÀ¼¦øÄÐk¬`°´O¦oZRj˜•GIÅ&%a™#‹`/ï!ô¹È>KÑv—*ˆvªH܃˜a½(©y³>°ã™=U<1ýäÄ).œ§ uè°7)(j8ŸÇ˜žM©%{ ÝÌ(^¸(m”ÍU!»ä jM@kWv@1ØöÉRGMPS¶µs˜™ÎE*Zr¥†tªiV©“d!‚¶X®MaÌ=K “Ó2ª¼"VðmôCЧq=c=à1×Õ/Qy8)YêØŠy¢PÆÖôÌ$#|°Vï W¨<MOgh™e-ja-ƒ pë
+ÀÜ\Bí|^®ÖMg^Ùn„;ió¡tÒ®±@„’¡Êc3SEpÐL6€ ð÷VLZtî…nOz9®sð%}
+2p1U°:KÖÅ°˜«òEŠ?£BNn¸‚h!N!©yAF)V’ö1š¡&›Ìÿ$é\ÌdT]RGéŒA7Ÿ Ý¿¥ë—Íøã•õi[6“ÇèEëfæ­õnŠÓ{ó½§ Qÿ"ücä(ª,R*ìY¯!RÇE ¤“”/ÔQVU†pwô4åÉô´%Ï#Ú5ÓˆÂfÇ
+6Á’-Èå¦åÛé¥ \éQ¢øÛºFD\yµA’C0J±×¼L__ s| Ñ2ˆxyÁž® ¶uƒŒ…E…p3 ;ˆ ®þäýíDFõ¡‰¤K ª©/ªçQj^œAFlFŸBXš•÷è‰Kf!wfêÆ ¥ò 5–Šì Cð3
+XÒê<‰€²hpó—6<vÝÚ©ÀD—í6¿„üx=ÚOÿk#Ô}¯7V!s1bÒ ´­4ëYŸý
+üùoÿ¾I)q½h` û¡¡ÿÜÊ헷ȲNUÎÃÀ½¡³Sj¬Ñ§B-
+®—¤«»A§ ÉSÇ"Bb5~¼MÝ] ^ÙY o× ã¯+ÇtÊ’ògžU?üFüÏ·@–‘¬bPZI¢Ii C7èkks(«ö•Vwú˜
+ÉpôóqˆiCÎMn]<ÒŠ¹\öôùÊ‹ŒIþò¯·ŸþàÿöÏo|±M(YÌ~N‹æ †Žúf9®L®18IGáZš¥‚þ
+ÐoƒËrü?‚„nŠ )–ðéºïƒ´Æå«Ð3¨íÅbÇ *Ä$46 ¤e!S°BäïÉøªôè€QH’=§d d#Í–òXA ;*ŸõèAIƒM‡˜¿hçÀ¹Œú@ö~C­bVÁë
+I@ª¨"’kˆ_¡¬
+†â0iIפØTrÍèøä2² ‚y…h8PBó**wérî!´b«…$j‹Ë€Æ$Siv@†AãæTbCÎB0L~¸!â:?9/DÑ*€_“Óîˆý!þÖp¢áÓ)ì]¼Œæòtd*óïØžŸ4>N¬4¿4ÔŸŸÖ[Ýò^—&ý~s1 —Ë\¨îë^n"ç@ç˜Ò[v_c+¿ž±Måþý¢Keº"dÑ1”›¼òv~€¦ ¢E·KÈþÔ¥ÙÞ9Êx·¹PÏ{ÕÂ÷ê8Ãr­ñwóô¾ÆŽVXÇf49m•¤žE™6Y>c7ôl-kµ9 ?=+×Ï]A?¹ÓV ¹‹
+BšòŽú{c:.Á¯çi€J¡C¤èNp^ßKl¸!+f}ƒÐå(×R
+ÿ5!=©ž:œ¤Á˜´cÖ6{›iõÖ@…î@fo•~Yµ"?CÖ¡ñú-»xcï2ú¾˜Fj&¢Þ,9B%¢z«åp}ªèé²E„þñ²Xáe=ÿ2†±ÂÄN¦K2–Ì%#úçÔOÂ'ÔÃý)T‡&·@ED÷ÀjZË {MSNÔ|.¼<&ri,ýC‹éz‘)»Y,º‹ˆJpSßWN=ZµÈ /ˆÒ®K3û«Ì(€jƒÜî—QUH‰ÿO»Fˆ±?a⪖DÉÊ”×9¸fÀ8˜íìôÇöbCÈ*Ì
+/r%"Q¨ú1¿«ÂßA^XXº†³ÕöèÚw£ñè"ò‚0×ÚlJò H‘¢ŒÖF¨Oí” %~oWC1³)Z"¤~™Y
+ê òôS·P&6
+ü€d–Ê8ïþZ>@ÄñNÔ­qçÂk’–#…†&‡-®N^ þlÝÏZ|ƒbªï߆g"\\…¹)O¯‚éÄ]¶
+XÚýâÜæE¶©E¢öHòm´j$tü÷O†sm'9b¿ˆò·dŽIÌ&¢D¨Dlb}'tšV†¨± ˜4ù ·Ìu#ðM"é\Væ­É`$t0@å÷uë\=zV[r@.(½¢"Lˆô+öU"ÚÀu :» Mìa¿d‡¨u߇ÇtÖ #Øe³–TCTÇ®ax©SƒÇ#”»#ÕlKÞüK£3G<e¦.š9Fy ! ¦qªaù„{õ%e,f“ ’!F†y‚‰yٻéMÂZHÒôÂÒ"! ¹Ù²PYÛ­Ê%Ê‚$Ÿë*¥OàܦÏÙtYöB³ ¡×¤>Hž·Z˜VˆÀŠUVV»ƒ¨9
+Q?_ί2ë8òFE¡h`iìéT5^ä±=–ia¤Æ´Ú  wfü7¤’°GQå|Šá”]ScÃð“8!íÜÍj=c©ð4{¾øë|Çaÿøaù_½ýñ¿7æÅ¡ü¢8š:Â9`P`ý1 =üá¿oDûþâ/¹"“iÎQäòßAüƒˆ2ftÛN'¨d)„Ê“¯ªˆ„RGÖ|ýùð¹ïA3 ðס¼G?@Ì)ªK˜kò‚ð'yE©»“Ì ÿ@­‘7$åP 6úÎ%‡$Z2÷™‰¡¾A2ö1† ÎfšÖ1:V‹m_f’\¾ûB”XqhiwÀþL)ËΩ>RQ|STXF— (K•];]$ä€äK Þ½F)“àù¬‹¢a>å­ÚIiŠ}Fã€åƨm0ï'qa¥{ ©8f]¤Òêe’ýî@xwø)<‡ö"#[ÕØ–£
+´_Ù7-H‹!³Îd“·Õ3øQv‰Û !WØí œZMnÃûZ^çÐ\’4
+–vK”¦”©l/ü|s@.Ó«¼ÝÁûÚãÌ8 ûô9Ÿûè¢î`]A3ö“¯ØíHT6Ùá"nEù%Ñc©‹) InJÃMˆÁ*Ô–!ûœ.Î ™%+¤Áºh(uÙCª†cç®ÎÝNÿð×ù+€dË>f$Œ‰>SâÝ6¥Ñkœ63!òDv.ȸc«fBöÞG]­/ò%ö!›úØ ¬Mس¬‹õ
+öË0YÃÂÊçË!RMµŸqŽ`drfy×øÿŒ—K®1 EWÀz­üŒaÈRûŸrnâ´è®0@H-¿Jb_ßf€üñyÁ.Ó<-j‘ß,X0êü™‚/«AÔ)ì€Q )…q[^%A HÑ ½‚´Ñ 9úyRVG‡ÅI>ñ&ßÍÒÅÇ,iò n™VüÓœKn•Pr²ïé¥i-2Õu÷•8Ô$(+F¶oõ8ܘý³¸Ñ´˜ýôò8ýŠ±°–¥Ýã÷ÊÓƒÐÆ÷S{\Çp ×i†ŠZÈD½ŠwèwßÁŸ°¤û£ò#ÊG¾Q)¶½@-¶‡åG5ùÍ Æ®;[µIÍ]Ì¡þ¾/a^7…¤öÇiëäYꄽ5ÌËþ~}yQ¬ˆŸ¡Çnmá"ÆšÿÖÃé."‹n//•w ˜O¤§ß¡{nI¯ò’ — Hûá
+ÆôË×Mx̬èè-úaÌJi[²ñ-I TD:B¯Åþ»‚ & «²ö}?}2A˸3
+ÆÑèíèàx™ ˆ¬>Óf é·ÓúûÔ°bq}/å“*;@’ ¾È(·³t3ÈË4Ìœ·tœØ±Ã*ê
+,˜q±Ä"w+–oY¼µ]€¤
+·Évþ
+oÚòâ‹þmám´êU³P} ÝáêFó"È«b2 ¿³¾I#Ê–u•°…ÈJ„ɳ¤cöq®<–Ì£ú$á™`Šwß¡éD^ßÏ—é0˜¤ÁúÝ£úSž±§Úν¡¢vˆ›Ñ¦tîð¡ämRû¤÷"CF¶¬ØÇãpƒ<8#ãFЕ㛠šD:ˆdÙöIï­1ðU„OB•ÿ>ô— AÓrÅóœ>Jó¾~ç‚š?nó[€
+H‰Œ—Ar›¹„O;è£"H‚Ö™en‘ª·²ï¿?AM,QoœTR‰Ü"A Ñhè°›T÷{-ZÅ}4o·¿ºÌûT•f&ÓF½ýþ±@£©«.²@ÖFéRªêÔ<ÇÛè͵v_¯V«zéªv„ؽ´®M‡v>½ýüñ¿ãe~/sZák¦}Ü>O Úî:¤MïÝz¿}1
+™!_óRgkH>½¹óô9g•wçô9¥”â=“Ì«¦×á}V[ˆ•ª x¸ÊÉ‘mÛˆš7‘àbÞÉõÂpih]û:fȬ<JM|A(oõÞ¬Ô:Þ@”ô’;QÓ⯘gÙõ úl¾ýÐyÒ^»qÅU²r7£1¦õìø|ê¢
+Q•ã®›äÍ„vÞ|AŠiëEÍ©w@Æhfá`ºaEqɇ‹+‘ãNÙáO¡53ôᤠ’ʲÎQx2û,änA:;}2{•!n‡à;_ ×Uz§ætf§Ôó|Žr9o%² s’ƒZg„½ Ê-6úÔY×Ã¥q¨4àcUíTï°}ðJþ?eUíù2@d"6r¼¸!;”h¦yÌ¥,ÎÖó弊b}ÆDæ8Š"VÇ(ãÝ1´¤Mþ¯]¤ÃpTb4Ë*Ð$SŹ(c©D è¸Ý°BÅ}âTz4#Â"Þn'n }!£!–|UÞ@øБ+éeW󿈜/›ó“Ƥש¥æy¼‚˜>7ç_@qžLApÇXq Bp)Ì‚)I
+ÍÚÌW¹Âv‹ì$BEGG\ziÉþ²:ƃñ=èNe‚Æ(£-šTm”PçU›ÑÔ‹|ÄôæÂ"2„€~ì{ ^˜ X
+%û» g§Õa~
+,¼â‹+ÓÁêI¾Bü–¼–?¦ƒ’C1ž!YŒ¯E=œóLˆf@rúIrj<S yŸná‘ö$|¦ê%dè.Æçv;¾ÜkH¯S\—7Ò€RW0)¿¿Ó] ¹‡“õ® fw›†Ó¨riŸø¤’Lò%¯BÅA%êb5Ý«ZÉ39‹ŒòxCŒ¶† N4¦Ç<R;ªßêXŒòžÔç¹èžuŠ´ JRg ßÖë&ˆn•À#è‚´.ŒòoUàÜÆ0«×„`¥˜¸üÊTó¸p«_@OÂøñúKL»ö ¤C—r‘sn@Eí0IˆtFñ°f‹dÞð3ôCÚÎ1šLX¦w‚F¤8z¾Í•@¬S«Œ#Z8ÛtÃâŠ92Ç¥r&F¬÷$obNâ=§ë»s¾ü5˜gÞßô¿×ܼ°ø%ÁÄ—:ýþNÇü1åʵ?(ùXöÏ3÷qÇÖ1VTR‚¦#š)Mƒ ¼ão2EÍBì‡Ôíë¾Èê|„Ñeúlг¬R t° Ú}lkû$‡‡º¿Ê*  è,Û×=Ÿ™ù+äôïhà„Øÿú‚
+Vf†‰ÌWE| GT[v“Ø–ÖÊt‰¯®ý¢ÇÉp„w–¢:G‹Ý
+#ÑѱÍÖ
+ퟅê>ÎV¿`žÊþñd×ÖIË–ÑÖ£¨K@zñt~|C(ë~·„Æn»=4øN³ýnÀ6;"mGò°­Ra“Ôã ï³ÓxQÊ“cÊž„Vªúù¸Å£!hy(%´F<x™õR>ۆɡÉÜæ¨÷Y5æ˜éçbúÅÉQßÕ0Ü
+„ r»BÒ¾Öo‡žâj,>$![I›ÿhÍkBÒr¸IƒZ£ŽÓ÷™˜YQ¯¾›óÅÐ
+­?OÍï2‹%°àokl
+zy¸DH¾OÁ9‡k“¾ØW)ã³…yغÍÀ`ÿ¬h¡&;G[ô…g† Ý€û»òP‡]ƒ?§ìÏ'Щäáñ£r(œÛwAÏý÷ñý×ëZø'šÛ0e/M}ÖP— Ÿ CûÌk?t{Å\ƵŒ.Ù¾± ø!µ²
+V)Žbøµžô³uEv}AÞ ;4_õx!²ñ\b¡ôL5AÓûô.C LŽp¿ÆÊÒ  YyÀ {ò2^9q’-fw@Â~Jh-Nv,ó•ŸzGkPk”Ë3#TB†Ú:ç
+™WöUŒ²VQ:´m¾Ð9%æ?V©î«ž@¸|^\:5ω’z=ä8´#`²ó®×o=l"AŸÉ¾*–H]˜{»qdÌø=WñËÌ^ïú†€ÿ—Æ?þ“ÊÃ1¥;̃žšì3Aä¥0•sº¿Œî€ÐæpYÍ åÀk@5M²R¡ïÆcoÃ&#¬x{¤s“ž9‰«¥Úö,ÊÌÏæ _Oïæè5€ ß±Ùª­Ö9x–(5&\‰yu<‡´ÙjÛ¹!0”&ñú± A œ8î2{ƒ0ƒª4î«8”w(2¹Ÿþ§]» ÅâßP¸'ðn\[c™Gˆ\e(‘Ñ›âáÃEˆÉT: ï
+|o'˜7´í§ò ò%›¢~ò‚Ò÷Gþè×`2¥ å±.…gñlõ@ÌOšfÞƒ/‹±3R P=Ž‰]é€8«Óè×PE™:8e_âôŒ‰áoÞh8¹VŒaò
+Š¼g` Sƒ…Ü/]FgèÃ!Tl¢05­´q¨Š¯Ç=çLŠ“:çÓÙ@TÃfíìëæjÊ´mÝÖñOH^E÷Ä®PPÞwçÃLÆÌÚ–7@3G÷I–ÜcJ³D¶ ¡EcÇñè˜â„ Ô,CYISÑ2xÛØAgäpý!¤ o. ±J^qø9€>N «œ<éÑ G“QnÙ£Ÿ6…,Uk60?«¡wsW
+kÅ:ȤÓÇË‘~RʬÛ×Øf"ç=ß…a–˜Þ’\1D‚X”ŒÆ0KH ËTžry# ùû`З
+[2†'--(ÓÈÀ¢¬ºõìàÛÕÁKMõÝ&
+Îì]¥Ã´õÿ‚ †™yõŽô;a–;ß繃>KìÇ+:,úýOTû·¿¾}ÿ‡¿íÏß|·²J´]³‡Ë’-ZLƒ‰. 4Ð9šQÌ` .‹d÷(CÄmÕ§"0|Ë»‰”Kš¨áDÝ\²u‡¨Ö€XC>µ•Ü×é°¾'©M†oEi`šÝ ”@Ãs‡ +XIÑw1cùî2uø­º:/É2”dë0ÿR^HG4‰¶Âú¢g½Ÿ„)ÑgÓ‹xÃ4D”2ƒSì[¤ÇÓ¯­™{ „­Æ!¢¬E,¿ºáÂÒ'D*d/¿GÑ|±ÿλA5½NDF˜Y60"IØ ´IM œ.Æ®n
+SÁëÝ׉=¿“'³Cp”¶¡¿Ý»&G¦æËõ
+^bÙ«?-çC ¢‚
+諲'æªD·Zž”è:
+÷eè+\”d§M‡À¤4xr¬£öœzt
+8¢/¢ÉÍÁä¸û“ƒt>‚O]­È`Œ*:ÓÇx;ÍŠR*}#ï×çH—*Ä&÷`^¦÷úxª’\Íë£þ,<Qr`h«\:g%ÊŒ2nÒ ûEM™?2-P¬Fé[Fò"EW[‡×…µ–×pš:+í†\{=³|fî±Êgˆo%«?Ô­¹­Û1g¹žžUÌPrôZoE³As¯ƒÅZÐ 8dïÒÄ 5+>2`ÜoêïGÏÐl]soT j¦nõ¼4D“æBU¡ «ç$Û€SgBòͲ¾ˆNà 3G.È[Õ‡Fc†¯ ¹—cDoíÔ…áô•«¢ë†Á‡¦Èâ8ñ´°HO`E_†g(ªTÙv03¹L)¥ñT7=ž8H!•:íN|0H™?XÏ–áôbÉ0ËI_ÍßM]#­3¨ ’ö gHžgÅp6SW@j.,òhn'™p1"X!"òňò¥0wp …$Å[‡Ž?  c#sœig±ŒHÖxê£E¯Àx8$÷¼ŽFKUsØÖ B§ˆMqºq%;··AÏ™(ðH[". “!ÒMKM“a_±>”þ ëª³Ó„«Wªâ¦Fx!Råà«{H!#úp?‹C¨&ÔM±â¶ö\ÙhØýˆì â:Ÿ±SbŽ™àYóx°ÓÅœFe8jŒÛL'ÿúî­A%ô{1ñ;­ƒ}™š
+~üÈËÓ<-gƒP‡†á"}+òYåy(UÿýÛ t*E€G·‚†›p½ºÌÅÇ+óìÛ@ëàB“;¡s»åÌÓh.¸Àº–<. Ìx’Úv–”ˆþhU¾Ð –21Óu¯‚ùë¸Ë´¨.Uådœd>Fé*®>D¼Å3„¾!ºYêU^Ë ()®R­ÛTSú%©wVQî ‹ûi LÝâÄ#@þˆƒÁ84+ï´ j tÛs"a&†,9Ôÿc£à¯¸m~¥çhtR5’4dÖ‹ò®›C=Ô…;¬ÃÖ! òh…wv‚€ÛÅ×ãTŽ‹Uõ­n tš‘®ª³o5S%Îm>kŒ_7˜ø>ì^ì ) £yb5ïEn¥ÎQÆ1Y¨®˜½ÐkMÉ%á€n‰
+qÃ!¸^\°8Ê%zL5çÝŠË_ã’CK‚ʇ Þg: Ý­CÌý‰!-MÜC£Ì(ÖÑ'Ïâ[-Ÿ—”tAO_èÒ 6u!H[>Ƕ’óå=é†Þ]ámæ'ã.WÅÚú@Î…vñŠ) )oÄ%™'H د€ª÷ôØé¢U‡l7\’£oÅÓaÑ6S y)ô:(é„h—Š<9½BÛEƒÐ"ˆ*j0Üýbpèÿιo s™—Yë±á1á3ÎD1‰Ž»–ƒþ—«ñ
+…)xè$gûß]Cç8DÖókͪ0Å£¬©
+úÄš¡ñðñ!`˜ÒI‹\¶B$ô–ÁÙè<$ØçÝ°ŽË»JšÌOV_¾801f~/FV #1¬-|ÜŠ” q= ï9{-zÅ¢ŽL×ñUó”`2 R¨"Ž«K(a‹z÷ ™Å`<aª¯¤¦lÎ 8]RÈïì{éü+‹²iÈ£0Þ®Ïs`!…©§ó6ÆfÖüÁuøÂxN rùÍÁKî28á R¸EøØLø»µ½Ž+‰µ).%Ƶ-‹¢B{ÑPòðS´+›˜Æö—"L*ž‹X 5jkÚ__ø›E¼U“AÀ½µ ‹KO*ŠHß6A†ÂöN€@v¶¬A—
+¨ÒÁÇ™&H°ÙAF$ãD¡;†Ö+‚„½ŽÝÃÄÌÈ-)/çî§êøÍ<åKIà¼9\CÇÚ»uÄöŸ Ùû4Xæ ízTØŠf¶„¥K=p˜R;[9<rþŒ9äÐÄ͹îu
+æ‘¿Ÿuxfˆ~¥^¯ä}%H&²õ2ðxóA•f4²ñ€Ÿ»(cªxQ¤M_Œ0¸¬ò…qÈÌ”iÆ-"~ƒKE»´}Ç?qjw/2Hœ1Ér.Êõ:f®Òœ“Ãghí’ª¾¡:>øƒ¹œß,ïŠ:VÞ”¼øO½:…UT'ã.6í]<‹vÁŽšñwÛAê=Ñ((«ú¥èó;Oðǽ¸ :Ì]õJÌlãe±hû±  €P‘ž™ókͪÀ».dÀâ@…Ö´²K7u
+ð´›“Ç&®ðò{ïÄ#j#¾€’*òNÞû^g2þhz¸IÇÜÊ:YÓ€-¼ìÿËõþ¸(ð~\´î×®B+Y®û¥ès•Ú€£‘ýRD‰ŒÜOn›[›‰&Š2œÙ…÷UM%o=Õr ˆÀÙæ0#ÇIq~ ÐÊž#Jx
+r†¤mWàfH[rJ‹A^*H/ºÎEšû<—ðƒ¼íJgµÓKÀ¬»llÙ%šè*WROÇÿö±“ôš!iäƒ& %™Üxf‚Ǭëwøý+ˆÿø1ǃ€bœ¬Ý|}à}™ÍÚ § šŒâƒ]ÔÈ< f82š5[Ü µœ‹g‹
+(qEK?Cƒ>‘ry±”ÞÅ® ë[‡ýD«éŒÄÁP$éõÕbˆÐ5hÁ ÐKÉ,&wžv0x]§¬­Lº{J2§K¥Œ¾Ý1%,1å•¡ê¸78@uÉrVÏVt> ‡Ø&X[eŒ
+kš |È7¿%T7êþ¦$K>xΆ¦ï­^ŠìÃÁ±&ÖqšûÈ\ I2Ǻ$PÜ•›f€¹Ê¿3Š1¿Ù5¼| ‹ñ˜À„QÁ8E’a˜ò¡Œ’§ŠµÝ`¸ˆ’2,LÛaÒÌÚD;–7<•Ü™é©è×›"ÈðÅ<vˆé¥†QçÄÍ-ÐEkÕ—šÃA2{ [‚p1ä2É°lØYV¨€›¿´ö
+’:ì ^W×hi]P%œŽ/¡ Á'lãÐ<°nA/EØ!±†ˆ ä\ŠºE)Ü›!2´“‡ÏáàЪé]LšâÈHì’9Bf²˜‰—0Çq^j¾èëçwDxÙÓÚ‡$(-‰ŒWdí…ç‰÷“V÷)û‹õeü/[˜sº……èùÚvJx‡QÕH'7øOŒ…•Ú©&gžÓuXs[Ñàïéâèí&±—0‹iÅ¡‚§¢[T Jö¥”ûüí"HZâíPe»—"ô³?D÷µ*íbtÚÑ,IžéßØN/á
+2Кò!fÞ’©æb}œDue1Å5Á¹ÈßÁ,rk„4Yâ ©Î]ˆ ø†—KÉï Š†ŽÐ`r+Z%83dמƒxà;%µl¯#ha æòÜ
+{G"N™¢Œš_lµ¶gg08âRØšÿQ^&éuäH >AÝA'xça­^ú(O÷ßö2˜.%™.¹wµ %É€h %ÔåDF'aòljéqX‰ˆµ7bì CÏaÌ$ t°=-š;ÈQ/”š@]o t;îzÿdDm’áy+ÅSéãbû514·ƒ‹¡4ó=Al¤8Ûm˜£vÅ“‘°Š²ê—àÐ:ë>îIS‡ °¹'·(l<lT^NÚ¾ƒˆÆ1h}<Ì»
+Ö,¶«ÎA¹®¦˜Ä¢ÅÔšNLäNyy=Š¼ŸäC,!‹<)kÅÓŠfõ ø?vÕRz`àyü ãÝd¼‘'%Rì1'Wý2vˆî$—zÅc–ØÜqÄj,Ki¸Sêv"CÂoÐåž Á' ³!5«aÅY߈·Î_”'—$Þ¯“¼‡Lžš¬érÌKÞ®¢,“ N\gŠ¯{%Ê
+Àd‘§SiT|:~‡®S¿,ônG!ù¨1ÛáTV Lz&'~jφ%®jMq±g¨"Y\Šû;¿†
+3p{UÏRr4®]G9hÐÄmk_‚ƒƒ•$Ú«ˆT
+‘kyE”›”Ð[Ï8\¥#tÜïÓˆ\à ì¯=@ï‡ËÜYþ¼ØJ!ˆ–h©ºê ˆ1´’ˤ«ÈÌ›\•)-²“WAe2³‹nã7Õ¤ ©áY`ä¿YÑëU&±ã
+G(0A{•÷–A (vˆÀjÆ6N«)’rŽïDm­õªéi €ÇÐ<rtlýü?6IîC¼°ïà¯d‡¢†„ÂâoÈz4Hb~"*ÉÏã:
+'E]C8î?M ôUûõ‰Ý´×Ñ×nŠ$Ò«´´‚SøQ‚çùÒŠˆ<TäK˜0¦¿t`é -:~äJ4ó$éFËU8UW£Å ,PÜ2gÂÞÐ(jU²øüçŒùæ®ïŸXðÜ4s¥öœ]smÝ®DyP.˜ŒÚ§$K À.ý7f ŠÖº„ Äóã  Œ‡žºÂiBÈW‚–s"ê«ÃsúÈO,§àW$’všþ D GEq½E"é,v}²è“u–³g⪙B¬ §™RÐØÚX¤ÊÜï˜÷Ä`“’ú`)Ö\JæA´ k<­Èû³›v-„‚}¯ž‡a‹ô³þ~6“×å!“ËtƒáoEŽ <[Y¬ç`±µòPrA£1G,Å·óg
+9šüÀ:HÍí.Œ.ub°s›JÍ8m‘†ÕVÓš2>OâgœÃ>[Ëš¢?s•r¼ eXäÆÒ¹eä€P{]bÐÈa³‘wLTV¢þ‰¤èÚläŽ_ˆë[žÞn+I/$vŒp~tHÉ|c+«¸·Ò¡Ä^–ˆ–©ç÷úoò¸·ñ ³bƒv°Î2+Nq‘Æl‰Cfa^%äóhoÜÄäk}……¬µÖ³nÕy”8žÂ˜Œå Äf­ cªÁ·9ÙÌy¶HÂE©8"¬‰¼Mí ô¶IQ¸¦k¹­DƪÓÿy‰=#l(:ayB-ái𼵈n“D¬
+~×8Ð=Ïžd¯<ã´)t«1u(ƒVWúž’NíÜÒ¬ å‘Y­ÊÃw yÑÓ²µÚÀ>ã¹·ïuBø'Ô”(NAÊŽ":í9|b‚uÇ»´ïš¤±ƒUÝ|BÀ3ÿQK͉çJéMŒlJM³ˆáͬ˜üÛ;”ûý
+9Ê»1¥™¤Kl2EÄ#‹A¸`¢hmZfá$U† -D¿ ”LJ¥"U°[ Q†,´¡+kÖú ‡ì¥êƒIòB_;1‹ tãÚ@Øàk-­“Š‹†¤™}8mŠ"Gµ—ð¸ á’Ð0;ÏpÓ› Å³L ›Ý'H¢ú,¿eàr­ÌVxÌŒêÕUæ?uôwß›,Zñ{ ºI«ÕPWão .È]0#ÒD­ó´ª×¶Aoª͹7 A¨ “!é4ÍÐ ¡Ãæìé …bá@ ý
+É,ž
+žüÇé.Ì#b×ÆÒãñM@¼ƒ‡®âTiu« SIHttö¦[}ÝKÍgŸKÝ—tlÓ ²ñ½Û§ïÜ8³]f£ÞéMƒñQ|Er_?Îsà~Á°y©òÓç5t.ðpä¦åƒrY?3.æ '=@¿Ž ƒ^ìÇÝ@ïŸÜÉô"bDH:ÿiá4¾ Ä@¤ I™‰ØA‡úÆË%ÉŽ\‡¡+è=x7ô¥¤±{Ø»ð´¼ÿièsíÊdÚñ:âùVJ$€ÂÚr&@b–Cµ¶jýÈAQ¶L2Φ%ôöõôbIál–$Hº øR€ö|ÀƒÌÞÖ¿$#\Á&ÿb‰<ÒÏÚBþãë_̶¤@L²±¢'¸fx@d¹²8Šž‡ÙZA#†ù—±‘d°æv~}zT#˜|Ð
+óáŸÓˆ%FfY³²ú`kS¯¶!ɬ‘V ÿ
+¡ÄQ~àp¾>Mðò¨Ø˜çÁÂÖXìË <™Qp%òuZÌp@°BûHoµÅumþVù‰[í$/¹éyÍ3ÖPDm8dˆííe‰Â+…3/»ˆöê‘"D;ëòÇT ‰ô¯­S¨¯a˜–ÎVö©ëR¤N¨Rõ‰#ŸzÂÏA“LSRÌÜzÍ7!wé@ÈO~‘HKÿÅAù ů»61uœ´·uNËJJHUhΖ³ "Lc¯S•…¶²ÂŠ w×s¦À™ˆÃ;‰| ÄçxAë¸dCÚ>@èÝŒ÷¥¥dkèC![öë\^ò%NŽ÷ ³ˆ…OUÒ+ÄËýÊ´‡ Ìßi郫¡g䄱ÎúYsf"%r!®"Þ@^}x)Qíq¥/ƒ<¡ÿ›‚/¡Ï û£ßdžø¤ó¹i¦9Píñ ó€Re*!¥ìê<º—+ÚËâJYV Àédу¨iYÆ,#­õDìë9Ær0æê
+4>ã¯}¢=ñ#¼"QÐÐ}/ÒwR¶z[ÊUŸ‰z5á%DÏ—æ—X(5ݨ¢äŸ“P†›ëÂY_çi’«´÷|ÝtþòHFÈå6¬Zydâ ä鼺ê<aÙxf)ÌFóuž´Ìäp4SØŸt^»©þ]`ê¶^”^Y¸Ñwë5y2>!­à'\2ÅøÌŒŠwEF:ŸùºŸ“±.%ß·5]•>¬W Äq¯›Ò‹„ÏC놫ôl h—Á¥ä+=1 ³ñ$ôWŒÓsæ†Å†½€n¦¯ƒ÷@"{Sð'[»UF2ZƘÉá¨Ê­[€´K N˜ßu Ñ"¼5èQ]—BÚBöšâèüäÕ‡TG¯Špá)jÝAŽÎÿMÀ§ÌûWÈòbaT
+¥š+ &ÓT¢V]Zï€z" §)möÈHâG›¯ð=袵² $ šýò@xÊ(E$ƬOÉœšr…å2ˆN£ÙoÛþTËÌ‹ â@Y —áÝ%ζ˜õŠ@w1¿D¦‡u†§<ŸBB}XR—
+Oúaꔘ¨-‹Ô¹¹Ö¨k? |JÎR”W7 ¿Q”ô°Ÿ:}¿g1Fâ£9a§Úû19)«áÃçK¤†‹=IÄ^ìKA‘Nˆ2¨DŒëS†"1aËÙñÊ aǸZ?†mlc…jm4©üMλÀ<’=‚¥£×ß@—¾<€Æ`†šbÍj7Fí€4<u©
+§ ƒÐv¿Í㺾!M
+’uD+uûј«cÒ×: &\2zRÇ;æccÒP"fšöµ]5Ez}{Bl°PPõ§wbgh\
+.³'à´_ïF³"ª4ÆáÀ¢:ò.±|m#øù,½uÓc{D\+,Ö•Î /]×”Áò¾L‘T¹ö…ÈŠG€ú!iGð°’¤fóKDxäµå¯6)X\zk^&=* ÔŒU‡äÖ›ìÜ]»çŠŸw\GÑ
+•E:Yö§à©ì1ˆûSÔ!’É’Û‰¨,…ôX¹ ùlgûK7 ‚82ª¿Ž)ü¤¨©1ñ"4
++(°‘BwÓk%p—,¿?…šÑbnXN*«»¢°ÎaJ0'èdÅeà_xüý½Ò®>}U'lǦ@‡r—q(ÏjÅrÕs~’ƒ2ÙGÜDÄÆ1 ÒÇ”\!X•ªì$œ¹A´” }ñMùŒde¿ÜQ_"&2Oìgºð¬À‚¿øEe 8ÌC¤šÍàjX/“ùt¢‘™KÆådªQkÀ9¬[ °#¦“%ÑyÅ®ŠFäõ)8GÁUG®æ¬N›IIÁ¦F€=Q"keOì32Lý|êâxŒ—(+ŒuõMò/­
+MëÈTZŒÀñowÌcŠ¥­¹;%Xú Vuf"Gõ™?¢ÌÔþ0|† ÏŽ{d > UK^ÀX^C´™™¬ØCÑ°‹s“þ94‚"PåNiŠ·AÁ2Å¥û¼;õwL5³)qpŠñ`î³ÔÝI*‹%)RéêfJB&¬
+rÁ\M‡¸ä#´=ÍèãÄÖ£;óˆxè^â„cçxÖ6ë"µD˜PÓ+¦øÒdKçé LÔ¨2½(7ŽsÞ…n#:(Jß›ÿUMÄhïê .P+¦•hX4[fD âÛ
+ª…ãøÖ1ø¢:ó?åe’]G®Ñh^ûf¬jžÊûŸþ$øJz‰,¿_ÝqÙ!& "lqgððI~Š©©Ó s 'd‚#YnBỉÅ„w7DX‚íoÞ8 &AÑú‰î!{SKB8UÉ’”»2vA” €ÌBømèÊv 懿~½bŸ„§ò ã£ö°£Þ$jBi%Õå×#aAd,Úzž “D‘نŠ8MkÛþÂ{PÜD4ý“-‘ÿÂóæilà¦èŒ¸úˆè‘„8”<zÚçÐ)¤¤¦vÑÐTUyÎA¬/‘ôµ@h”oN‰ïµ‰ÌÉioâhL¶4› ÿ`ÐÑÉ•o>ˆHF¡øv¶–]0ì¼EzŸÑ¶Ç·¨î˘Gâ.òuæ©n `¨™íš×Îœ P8A5Ÿ`+b£€P{×O{ƒ¤±çâ[€62F­ÈlïœDžԧyãH—~{—yâó¨+ÿ.Ź²øRbèß;õû•‰ù|„a Œíˆp¨_ûãb2r‡·PW¶W»U^,NŠ
+~íûÕ`+Ë gÿÈ,gÖfǸ”;4‘¥lˆ=Tß³Süˆ! •Ì³?•àÛ£"Oyä+^„¦&hókAR"BK±â©0¯$qa¢!F#çXIÝh7¥Šl(oè©ï_7 ã—1®VéÆS‹ÓdLþb¡˜„È€oE®º ¢Ïj£ƒ00ÉÞîC« óÀëNò¬”Þj+
+lBöx„{Œ¨¥÷‘Üøµ»Ñ‰¢“x†¶Eꆎ¤â桨4xP­0Ì%©$œ@ÉÈû¥\l‡[qË„n4÷yž;)¿ÉZð†ÿœÍe‘Ì“Vdä£íMU®1_fdðÖ?.H, ™„È:C”ûÜ Â\†ó'²¼ú‘?o>÷HË'~ ÿÈQ{ñ@-0jö±„6ÉÒÃŽÏ"ÙÀ| }ihLÓœ´RÅÆøÑ–]íñwf:©"X _ö)‰øÄ`”Ý…–œéœÈð
+QáºéŠ°ïÐ"f‡çÍ)5"(’°s[¸³`”n§.
+ÍV`±Ëp*«s#’†^äzöDž¤‡
+#‰LÓ+ÔïÚð£šïÎQˆPNÅ yØ@è¦qÄtÒ4™Ú†œ+©Z åÐ"ÀœV)W?3Î.U%ËÑzIøB40eæbt+´ì³ˆ@/ÊZÛƒdm„('‹}mOH{ŸeÞ”¤–=Z‘'ʳ„aøø7!ýXy?°Ç %¤…8§ÌKo¡¨¥°<ÒkØ‚H!Ç o•yò®ò
+b]cQœ0Ê* WèF’)û\/Èöû¹2SÚàJ5zÈó‘‡h#¹ShBÝ<z‹9ð')­WŒÉ­ž8H{'«ä’LÄ.ÃÔ1ç…ÈTîÞ¶òÀÈ[¦l\M”Íλa 2È_!\¸.ÜžPJ,¶‹v~ϯÍt‚sÐƉÖBͳ¸>3Þ¹|b>G;Ôá}AbdžO˜z$Ø›†6¤ŒBã}ûK,D¸(³šÆ ¦^;Á0å}N"#-«°Ð¨´ùŠ@%y^(øŽæãÍ=iò× ˆ®H)#ÉvþònŒA ròœü‡£›Ô“ø•G;Ï~*ŸÌª“USf[h¿œ\ïÚJÏ9Ã;gV0ú|ãÀò‹*>Î
+ݧȑ\Af=ñ¤
+¡ÇM+rÀˆ®Fxw#°×ÏM.Âp{Žì Ñ-X¦?ÓºÂCû†Å³ÖZDÚŽžj¥Á*ûùTˆZhÚ¤J„Às‚¢b>2PG×ÞºE¡ª­튾N0uLM˜ýŒæ¨¬\ÁŒ3A±žÑ¼€$‘…¤[i¿*`䙜zv#ŒŽ.Ë)‘›£m•½†x,øg„¹¶ábwµMsWüíM
+[múø>=TÉå®AÇüÑ t¨ñ#m¸SÉh^¡3ŸÔ0ù¢ÒȲÈtW6õ «@1QpåŠg9x>§3ZÜDäð›n
+êI#S¢ZȬ·•º’ÏH‚Á´‰’äjk
+e$5"ÿgV#Í…~ŠšŸFÉüDíó}L‡+Ô4ëIÁî³nƒÃ„p{Ê´!%…Âd*N›tUÖ
+¤éŠù¼Áh$˜0îܬØê7«Nƒ2'„qÿ‰dÚ¯I+öÒËhL‹†(c!ÑmÕ"A±Ûœ6Á¾¬õë÷þ]Ñ—PÇð™ÏïðÇò™Ñï:õß—Ї9zæï@H?Rõ=Y9¢ ‚žV7¢[Ë࿦/=â7nNg£ÔlA¨º\Œ4eNG=©<­­G…âûrY6ÄÑÊÙkë &á¹îsäe’€cñ;*¿7%ÞîC
+[[¤­ ‰è6±(°ªÒŤu·þò¨˜j#“Dã°‰ÖdžNàå„ÈÀ¿»¥=ý‰Ò·¤‹ùÖÚ軜Óú«[rÁJ„"½*üs´f@„2>¬›{ˇ^¹ˆfÏG€ù(ÝE~^íK´r Ò§Ò 2Gƒí‡:©¸kÞ"dɆà#ÃÓ\%ö'‘ 'EÍXÊRÝÓ~)ñ‹ ºW)9õðâšÚÊ
+LÑTlÀt|d”Ý­køK
+¹’B@·áOÓ‡MÕ°ûá/Ñ-,Dé¹þò2G#7¢è xÙct`_Lùãê
+y¤©ûë} Ñ3,d‘-ÓäïËÏ—Ù-IVÐõÜæ'‰¥"®%Uÿ;ÔÔ@ãÕÆ™D…æǯ‰¾y!~A•ÖlýÙÄÿmð²PNa°ÔȉN>}¾HoŽ–öÊfR
+üeŸ!Ê„‡tá~n a\škØSû, í…ø{ '|d»¨’ á3‡Ç«qíbG Ã
+t_?×ÑÂ’¹tï¾þÎK¯ƒz_ûÆHÖ|2Ix.Ê:l ]Õiš¬Ä­u]‡Mq°§â(õ(9H4Ãk+¸¼F5‘É¿÷IT¶ÎÍÝϤÉÖ5µÆç^¨N‘û4¡­c2«Mkr«•kÂ"ËlÑM>Ñ<™8P+/xäˆ.òÙÇ5]“mw”_ˆ~¾ ¶‹&cB•±žO#W@eFˆ«Ã&ôÒ3ÉèãK¾•’)Ö æÌåAÝÀ[û›%S7<€Ð—Vì&LsæïêÕîò,­'M¤Âùœô«òûäÆ4Ù=*}Ï[i(Ÿ%‚ýz†˜Ëð`DŠ¬M$b=l”c fzÁF‰Xïøµ.{u°Q
+ bM½»Ø¸$ôi*u¦]ld/YKdO8hùç‹æ‰.Ü‹³ºØ¸$±Pá—w%Éì'|ö:Áy`£D SMÚ…’»0I´Rôšø¿Oø'6J
+ÉPtXë\76JT”ÂÌ.Õ\lÜ)ÃÕ'U=\lÄ߉,ÐØÑó%â³,¹SAñ°Qª€^àW“G·â†FOódÆ[s—!· ÌÞ˜£‹ŒëÙj¾=Bò‘Ñ)ç2®î#䌎ܓg+nÖŸö´Š¿#à´–ks«%˜F•²‚b”Ï©Y Ü}`
+?"´g/..JÀ¹,ð{¸¸c˜ˆ¿à¢#ºIÐ ïEO\tE?ãâò7\€-#†äÑâ²É¦,ño)ú´¸šBê¥kÕôhQ’¶†3E;³Gy«¿±tx,„òB‹Ê'­Ì¢2쇺IúaÍG%aA¨ û‚‹«¸ø0Oº’‡‹’ôªðÉ“]^¼ ðÆÅUp]ÏËOü¸¨qlkÒç...g¹È¤“Ç‚_ðÐ…‹¼˜ñÈÁxv¡è‡+º[êܺ7ÕÁªTš©§Á$)vë/ŠnrrŽû‚híL@#x)Oþþ"¢L©»!ò_‚Dõœ'x”¶_'Ú+gÝ-HL©GàeªJ¯ u»†°ê
+X³‰­X+"§o˽D´èº!Y¾,rŒãäà !Dí]ô¸F&ÂÊ0¼¾Ç
+'7ï ¥d™¶Ïe×ë%bµ›\0(2¹ ˆK÷jζ»þ!únáYMš6^?DK¡qhÎvXÖRÀ;Å/‰h:j†D–›`Új™ÚJHTÎ&d™v&# }{’ue8,ž£ V¬V°ºlü–°#
+…àGMn;éÒð ! ç$eTLwf“~šŠUßIWÙM 9LòÙQô †ÿô3xXFrª¹ãߨÍúR¿¬ã¿°˜ÄËw1V¦¼gT?Làa£ÖiÌ‹3QÍ“Úë»Ê"1L–|&Ô2׌šÕ4@繩ž¾2bñ”<ñ™sÕæ8'Ѫ<—}ËÞVñ€Nw<!I©­Ù4ª‡ñ§zZ¦ZÀ½æÍ°%!î¥7¡žIØ
+5÷ZR¨*ph&‹õ:Ð0FBÉç¨ÈK)Í'{:xÈ„Ó~cG…¨2¨A« ¨‹ I¿Ù_$QÔ9 ŽsÔCT(]æ+^²ÞbÝ+ê€4­CÉV«åuZ1‹›eô\sK¢²¯V£P>›Weê@[6+éz`hSŒÖéR„Ôö»Ioh¬FD/LSˆúHy©ÅQøæôýù"bb³^É|Êö&GÃ?IÚwËqË<U7©E·ŒulÚÅö•Åk95lØò¦]ŸVú,䔈¶MÔ;8 ÒѸݞÞ‚nÏî~+3-)I¦fw$vªé0Þ·ï€5khe‰¹*OaËÊÇq›| ñ‘ŒY•=6>«KäH ÐÖ.™…‘™6ÇN‹.œ›Å˜Up¾‘{{‘dr9ÑtȦYõŒgÕVÑ÷­;‹fí]q* f³èÌ…<ø…i=Ì¿,÷äŠf
+&Íün/*5*QZ¹k
+{ßA‚ãpeJ«egõBwÛ7Bo£ ÔD8"R)Ýš{_\W&kŒ=*áeótDyu#ö×aq×…#bròTì7±7Y|B‡QØÓ¬+2Rá„%êgÒxjgæÂ#A/*O¶´“2¬éûŽ f±r®vTg
+³ùÐJFÜò!>)uK(ƒ(*«{©zHì¨,vdÛ{¤zßIX»ô×½ö?ÆË%7r¢'˜«4D},ymŽÒ÷_ç•H5[ÆdklK,ÖgÒ‹{"Ø“ÀãoÐbì
+ÚÆ a~‰OuÚ)Æ,Úóâüú‚ŠÉ©Èy!6æK1ÙÐ?˜Ø8:³·lùžŠ|¬Ù«Ÿ-V q§Sñ¹€4>/± "ªÅµ#¨ì""a´*êf‡àºCРY²*r0‹å‡
+H‰Œ—AŽœ·„Oà;ÌÒE‰¤ÖÎÒ·xÀ[Mî¿ÍÇ_üð´Ú ìîjI$‹Å¢Ïþñ×ÐþX¡!æ­·ÿû!Cõá!n#F·u
+ü•ˆF[|`¦ú2tÉèÑÇšúñ3¯ê~Ò‡pòôùÔÌçÈÿ‚0.ˆåsõàG׋ݺ®nÒl?§õ©kvžc2»Ož¨<ÜïÈûòN˜áR‘÷àŽ¶8ÅêÖƒµ."Æ—Ü>Z› </,tX“äºj<85b¸téïÎák“¤Öë5ݺø”92-z4íÓ¤ St oâƒT\WÍßçm¾´ÞãCDZóXûªù³‘á²!™bõ˜acèÄU* «k•óÄÕÔd c—ó
+I׷磎99Æïפñ1\Úþ·ºLóU§@BÔ¬Áýû*8Üd)­Zɱž‘77« šÝŸ9+È´ w–¸úò§¢ÿ„íæ‡M¾ÓŽ2­¢Ìâ>iÓÇæ/GcV6ú¦öÚ·.yC[(\Q;åÏ`öÔ¹!™éÖbènèœ~ÅH2AFµ×Ü H13ÿ)u³}åHIuß‚¦¡È¸!‰æ24f FW§@„iº8Ñd7þ+ˆl7¸à"F;.ivt~¼nv4äbÁרì ×ºR±ÕÌœn¡r&Ü¡Û„å©ö³tQµçkÝuH¥ND8Îú Ÿç\“h]õŽÓ¥“^ëD2ßè¸<`OcA¾ºJ¡.ÌHÛ
+‰ÇñTNž~_´:´…æ`k*[¸#\(öl3”ÔõgoÁ¤bM_T•Sx[ìB5G‡m!v§¦ò h"NÌŠŒKÃH1¥$T´rž/ Ï[2Ññ)Æ›6("âBÚ´W!<˜ts¬¢`z "i9ÔvÏ ìÔŠÑWÀ-'5µ‰§°%ôg*š=“숻¹×U# uô]s"B½Ð,-™ƒJ°M°ä®9Õ„¡³s>GRæxõ±ºŠçMãSš{lB›*ÚÅÐ LÖ=Â(4KLÚÁL2Fï9ŒZ‚'Õ9Lú!ŸN¯«xk†…Ì—2µùè‘-Š¤³ô}|Q¢“6¨Ñ2žL¥øä1J¼à
+-´Ê1W3¶ió£~C'SR/¹À}´¤NÓÒ‚| A;\¥]r
+ˆÏÁ«‡ñv Ó)¢˜Ók¸¾‚.ã—Zà¤.Ó(†O² Ag²2¸Êš¤„29v뼂^ì {`f$;ŽÔ~„ûS(ÅÄjá¶A\×ÒÈ%û@Ú:ZAßÉA1+G™CM-Q;)&©fPw„„ƒNrxA6ËeaÎ7‚ &'÷Âë˜@»òW³àn‰§ˆTvÔ¬Ç8c­#ª¥©WIQÈE`÷UfЯM4l"†~\d¿’ÍM¿ŠÖÏ^0§D[êow¶ y>ê¿A_ Яãuß $™àBÝ3¼Ï#hâ“gCJiµ¸¸dä¶ Öo9C”ð}(õ6š AS²AlÜr†¤#I[nŸ©lí` ”ž`]÷1ìUù«Y{ÚLÓÒê!þ¶!<¿Ó­x¤8@ê¦4´Œ¶ó9©p!È–ñF¤$úNÄF½ÚÓëøá½?œ@È"™ˆ–)|¦x¥LòC›åטÁK
+¯…æ5ðW×7ssK¶äü
+Ž5ÌB‡Yå“0vÊ*‹¾Ù>§c±ïÌ©‚8ï`e3lØuѧ‰›¶!ÖøÃZßK¨™­áývdä5÷NbêsÓua†Ö)=Ç$ëÍ^5t8r¨l[2®ÄL˜n)¡Ç 0Ô4GͬuÉ¡Žÿã‚Ð%ér!ëÜô>s([î „m —¢é\žW½€F™G§óíûœô
+òÀž9sõ:Ï7~Ô…C1´ÇX”ÈÂ]©ë:ŸC”™pb(_“¯p­ÇðË]é¨8.“LÔ)l\Ly´’$î«02©;saÕÜbÇÈù Kdìs®VÀhœÒ¯–ç¸g›ÜªòÒ0_@Ÿo@4eg—äºB–OŒ=[Ô¨'c•ø^({e9ËÉÄÇ4 •ehôø˜ãÚ½œF‹u/^Ò¡FZc­Æ£w‘~’ÁbôÏn ½­*.¥êŠ6¹ˆ
+Ãb•BÆ*ß‘Aéo ¹L²u± ÝÜ9€~Ër½0ÌF«–+•6Ão9™¹=¶Ü
+ÊðBjï0iý™@Í‹é¿V ,R²É\«Vhä0µB ŽÐŸEÇ¢R<[!xéJ IT;eHî›–r¹¢Dn%ÛÕmk$lï q·’.ôgÀó†WC(‰Õ‡sKº4k@‡ ÜúlˆÔ eö:‡+WEÀ¶£ØìTüh±ÚÊÂŒ‘HúZOé:€F.ŸäFéÇ©=q,„Œ|ÄiűÍúHE36#—¸¯ú"ée(3lNcôqWñ"^'ŸuP"è3pãçsØIó…ÊÎå·•ù}—L­IÁ¼ZovÒTídW.w£–)V"¡_ag‡q¥»Ñd)K¹è1ﳩk<ˆ^ÿV©2XÎ Î~Žv$\ ×m*©Z¬Q^:ÕS›ÊIK.ƒÎ<
+ [Mˆk
+BÀh½HWÐWªä [Â,¥M\‹U‡–óšÄ@Ð!L þpI-£HÄÌ5v3,AiTÅqg¥ÿ=‹†á¿†¬² ƒ¹îB…&ÍÈ/|#R²™í¹½"®kâ1&š½r“YïŽûÎ;Ƭ‹D/Uç1QzÓéèìãšD ’0p™,ò/ãå’GÑè:‚ÿÏZ[ßÂ[éþ[¿$Á¶Õ…’1›‘ÑUEÈ|¹fùZ¤1Í´Fî8÷,«HàŠ(×oŒ!m¬ª«ËCÖ£ }'9ÒN´÷®ÙŽ‚G€‘pJìäc(g7súÏ¡Iã¢`;ý¯¡ÝÌZ}4çPÖÊÈ–@Õu m&ˆýäVgn¯þôuq\Ãœö›’\ÙXj¸­v^õý+ó„5ðÈû«D¢8ûôkž3MxÓÑׇžbk£0sÞ±¿{*„0d|yÖäÊcB³r9ß fF!wØICúΠóÿ¸uÝÏÁÌäL®‘ÐR温ëŒCºl…÷Q±ôPP·?†ÖñT´Çxl¯Là%Ôq3€Y™U16vHÞÌiò§ERТ$9„i»hpa²PÔgôM‘U‘¯XLh’Ô*´þØAÌ7uz.4YEÊ—³`iy‹~²Q/h©y¸áÇÚ.RuÌüçL!"Œ {¶n_ƒ( ¨bÕž0bNÂx!d;UŽòk>ñø5uèóœ~¢«ÈÅhBР÷惔›x \ÇvÖAZY ¼÷¼èÛ­Òþñ%l þ èbÐ…|î¼ò4›BæC㈔°ŽÜ
+MFEº?Ëà*“ÌÓx¾‡_« eÑKÔ¹y„0
+-ò°çÀü­ !ïÁ|[m–­Œ‘:¾…{ìW‰`ÓdµF
+&ð´[¾NY¬e˜¸3û©ö­0Å(ZSðJ@n%.(X»ðRämD!=0"ëñÇE¼.Ã'ä‡TÃØks)ªŠ ØENs'6JV¤Ê)y¶ž\Eë#ŽZשׂ ~ÖFñŸÃË›¤ƒÄ{(–Ybœ¡>Ül—äÎð2y½x¡äýÅ+òî§iœÛ”Þßô¥ˆ!‹z6íAêwañ¨r:E?Øž_¨ òxØ`ÌÛ{ž"ÞçMQ¡^•—¿®’©päÆ6’äOV¡ò”®‘ â5’"pÎì6‡"ãÀ¡WãT4a"š›þâ ¼\ð‰ÙöcÄÓÐUpÜÖRRJoµ–RûÍ„PÓ*š !ò„
+Nðàyû ‚¦/ª»„üÅvs€|ˆí+={E°±F»æ´Õ±ëË`HþŠÉ ¿q¾:_|áÐ˹œ]×Ç^чOÅAÄA
+—£§Wo®òã/,$OV¿ä ×~ÿdˆÿ2]èlÉÀÂ!ÍÈže!(äÓ-âËÙÊÑɶŒŽ’YáU” D5»lƒ;àßò¿0–þÒVéF M`N‡Iˆý|zž™;©´l˜yÓ‚H–¸Ÿ¢ÈÉMrÙ
+^½Y&…jpêäëf¿øðV;“¼|nŽ­cû’ò£Q=ÿ¸ÈSÄ€®0/Ä™1êM‘¾IOâùMàôg Z6€ç 
+xl¨+[x{ÃIÂdë¼êûåúÇw–¬³æˆx4­‚é¥ìXkî{Û›¨0‘DÊÎ/E¿nŠ¸°hl‰)îÎ:E¾2ZUÉóu—HŽš#Ó«$U†Ñí£9|
+£#,‘^ø‹½ŠdÐæûÙå´¤“pZÜ¡ï ýçŠÉ´îF
+Â{ÃR`#þrŠ>Ü"ÞÕHfK΂V ²Õ"™w3M&''Ü_ƒã`XX~1’þ%T õ×6ªóG³élµÁŽ•¾Ðg¬IŸÏ#=oãº`X&Šm²‡ô¿wØð,3H³øÏ× ìÍm<šþYTÂaÐæèÔ±¨FÒ%\)yÄ¢’]—
+Åìbÿ.!`Ôåö¿¿øE¸WIì^þ屃v×Ç`àûexî˜ÓÈæÿSœ'ýhŸœcQç8[ÚZÐ>ÒïÏÑ'ë‡"×<Ë6ie±fe%« G´î¡†ÓÏ+²8ëp}Î\#Ip #aaÚ#
+ÎÔëÉ'_•z5dî´æѼ ´ÚÉŽ0€ —¹l=ß‹±ãî9,fÖÜÌsðë×@õäž ÝÄÄ5r3,¾œç(±„3
+?vI„ܪÈǾ†ýŽ²Î¹ïŠ
+-æçU¨8ވͳZT4pAöyrHÚðï?ƹ(K™• Ɇ2Çth(F´ñ ®5›År
+X¼}-Z%¨_¼ ×JÐ-¶O(vIÑ<òƒÒΫeºÀV϶O/Á'{‚ˆø‚¾E¹ñ‰{˜‰Ü þÙæéEË°jr]Òø ؉égS羿85O&ñk×èa.î;:EvƒÁ@H¬ñOE«$äH‰È½[I[g°©k
+™ËT #G·<5*–ÚƒRûv
+r£­ÝMÅ·Ã¥1 g »V©ÀAõ0Æ¿Ôhs؉tžY󑇦2mQÇÝDÔaA™¿+)‰¢Ó†YÌ$H‹óc +Ü.âËØ¡R›1àœÙ€Û¢‡NÎypUÖ4óæRʸ{ ^3¥ý|ËÊ°M!Ï:ŠÆ…¹ÇÙΤã4Ëßo‚èQ§™$uÖ É¢~Ķ½®
+ œB ¦„‹Ïþ0^æHšY>AÝ¡åÊb_D µUNІD!r>÷âÇ*3²©™a ÿ#cñç ¹€•Ú$5VŽÙÓëKÐ%ÝÛf s?ƨ[ß©¬Aé+D;”ÈÓi¢{GˆD‰É¼rþ¾¯—`(ä¡ÙÍ$Ì%¨³Ö#ÛÙB3oÉ}ì§ìƒF@Õ2E¹@ŽzqÁü8cs¸Šl­¸ªÁëÒãKÝ*3CB n±Ì׫ÞUrÑH $iÞŸýÅ`Cø‚}§&WJ…‡;©Û=ºÖUñší
+Mæ¯a^Ka‚$ØÓ6GuwܦùÔSUí’ªjt:j¢õ“6:Y]p½IÀxyA?VQÒ—¸¶sØVCÌPš)šõ)´ß´ï¾Z‡,à’<òíðͦ°X[A\:âˆ!ôà´ÉDuûTã¤ä°ÒŸ_–Ãœã ûSQâ8e£î+Îm„£ù‡2/ÀÐdΨ®,†‘#ÃKØa1TèN\õ‡¨‘ zÝú‡xÔ4fš‘ž:·ú}ößÎ óÉ„>¾âT?”­Ä°¹Sw¿[gos*G534n­³ÍPÐËCæ^ßãò%”.SÁ|AØ2ß–A¼by^Ù"c31?¤Zù§/Øt"OO;ÿ t; ˆ' b ç鎎˜´d¤*¥1 †IDÒ6ˆ-!•þ  I'êžžÖu4’(½oH±ßG“²º×à`3Úé™{ú+'ëó<<RiðžKLýi¹z˜‰C!Öéi}¢ß⊎
+fL§ûÓ¾TxÆ©¶2=Ê"fXãIÞ°uˆÊôªt‡ö² —×?Cŧ‚0š ‹`tLOëŒ
+g±”œv°+ëutƒ`ö¬B~*å°áßßN G42è_w UBÉÑÇ™,Ås‰&®·“ß*Ï
+×;Ô'È4q½tÜ]v0ÊØ%GÐ@"Ù;¨§¤iÊb+¦–Áà!ißõ0?¤Y!ŒB?DÅôâ‘b³\¦/ÙT&^™(¬ÕÖA¥ÑTjÆ*l@ÐIˆ„ÐðMA&Ïøð,)Œd}jrã™ –—¨×ÕºÌf!aíf°!‘2bò$ Ͷ}*Ü> (n0¤­q…#}Hé¶Ò0›KÁkmø!Xû³)3㉦A(›l ÷MµO~ø%_nð¾Îí½çåTþ© /È]QÏ®
+´™ó™\ Ë€ZLñÌýˆèN
+JѳZyù"kìÚ>¤„Wj‡Õþ>@Ø4zÃYiGqí©ëIy-ÂjºÂD7Àl(gé‰IÒŸKK 1P­‡âÖÔ<Ú‡¨ h6ÚÙDçå ÇXmnL¯¢‘ìd7ãN>èlÊ!Ý2;Zç»Âø,k¡ó:p°†!
+¦•Â´›Êåxe>©¼ ‘G÷U:âOì "Nx=7ê?¬+éS
+£ :1!R7dó½*r’ `Ñ—Al‘Ûƒ
+]+ïAh(ƒ’ô†"+M2™V‹©™ds±§ ë]â’À†Tø3I„D÷´/q$•ˆZz¼x'ÊÅXŠ+LD’É&¾ÎdÖP±ÆZÅ%lð3nÙþr”Âã:äBˆíÊÞ ^.Ù­äMƒ` E¢‘Ö
+ÜÙãZWmlÝÞJUSN‡i!›F‚5Í¥í­h
+uP3}*x¦;öµe hŠÆ}µæºÓ ZØl?Q¼9ƾåJµm¼9¨éùÂŒ+?¬‡”¥®³ƒÄ€÷£.ȼ .€2êûSŸ {
+òYÐJ]¶
+¢:¼i_ÙnAð³ÄvKÊ®¦hä‡À¾”‘£4uÎhë`?$R
+6×ÅdÞ ÁAMò~QŽ f1 R”Xœö§. xšçº¬ªCØnh´V)¨O †¢\WËv*èXÿ8ÓN𨰢0a¹îû«Š™\Q±ÐØyL4¤Œ1ÿ#ߦÏ$&<²8!2†:×Ûq֋Ϙ˜ù 6´-7Чä×h¯ÀîQ™DĹÞÝi~Qʦw`̹h¢Þ‹ÄÈ'¡
+fµ•Ö*(2ϵù§h@è+É™†fë, ¥øõ¶é‡Ô õ\.ø0¸óÁ¿u¾0ü
+žù.BzepãÃ"p‚¹â½“ªÁÚƒ;#qåݘâa¯._Ìœ|†úwäÖ¯e|µkÞ<„FÅãÑÃÞÎåÔ·Ø̃Õs{‰›¡Î0Ò¨ÇU"µ`mñU¤œªiþ9¥E€ã~"5£ñ=‚ûŠ¹3/r¿‰ßPí¾
+hÁËu«¡0ñ7î`·Éìª2`ìî¬'jÈÒ4Æí–4ècnó½ÚG ˆûám‚'…Òîä<o,%
+ã †<KYÕx–ŒIÉóTÑù"ˆ€b7¿;V ® _
+³-ï{÷šÕhèÜ‹JGàLåLo5àÁõÓºí¾ä*•Ô ,ØciùœÏT¤3ñ!CÜ‚!øgu’ô¾ ÓšsJ
+ïl3—ÜI)
+!JüSbÊ‚Ç{ÁnçHc˜yj¹`v….F
+D6l÷¦KÑÅ`{—ag%•£Ã=j+I¢+þ.(>U9­y„+ýkPQ›|±?¦Ò›Bèz
+)ÚáŠ{-2Ê‚ÓÔ§JÂÄ*+¡ðŒ,"MšR þúE#㸵<»Å€ év™"5nÜ6çvPkð©^J¾`ôû·SÑíAw\a ªIÿPtÛÀÓçþ¢èÂ
+çåºýòýAÉzá$O¸ÿi%2Ÿ£àL¶ø\@”öœBà ±«„—€y»¹ ô”X¬6´áöÎ…å%e§rÙ\¹Á
+è*’ûØÊ"Ù'ô ¿a”(—¥‘íœuã†ïOnJdxgQÀÈõX‚5}Á` ”ö§nEÒüÆî+?F“ÆïYá
+ä›OnÄ{q›{ÞQrà˽¥à›ÃÐi*¹is`´N¥ÊRÎ ½fA&g«_Ë9æ~W@—A(„ܽD:ÚØýÍE=Bsá»ý!ÜcH.c÷gãß`é(Ra£5`ƒar½žJ4X´‹ ÷"ÈSLƒé•i·ÁÓiÓvû±‘/g|®‚¬Üd I¿ßË€/À¯ÃLÓJ”ÀX›BŸC”Ý„È ·nñDv3f©Phs„¶ª`c,p)9ÓÒ¥èÇC Ë‹¹°ŸEIɶ‹L—hW+Ú…é‘Vf…t8°ènTJ¬Ùƒ-KE E–”ï6„ 4ƒdWÀD\fgd`BR)þ%’m‡ëäša7«Pì%å@mùPâ_Âdhv u>‡v1n®Ç•Ë¾ {¨³Ùs+È
+Á2›„
+#l;ÎçßÞëÒ˜¨~éz®ÓÄ\Vé`5­$ËâÀ ~(),;4ZìˆA15xî¸}>ñ*j&¯J~Ÿ€b-G`DJ…srÐ^_Âɯâ/§W²S¸Ÿ F¤F þ)Ü¢ÎaÑ|žøƒ ³wÑŸžÄ ýv<¥#B¤­†ªíO $N‚NüS¹áohZ‹ö*QÛ”:ð©á%ìrä H+Ñ3.6+J¶³ÀøN‘_éçsPŸtæ ˆÀ×Orªi$„‡¨Ñœ~ᶴ³gy4ûJ‰W9™âW¦ßˆ‹Tj²s@çC†Ìlj3“Íb†£{I#QÊ2ìq2[l#íîEüÿ'Á‹Á¡Hù´+Jåißbë1tgg‰
+l7¬¹mÕ„“ê0÷±os/ºF’?êïò¦ìî‚`Ò»éïÅÀþ<”œÜrÅÙ€¹Œè–üEwûØK©÷^rɼÐr™_Õ3¯JY0—vV½e^Šº¦‹kà´cæ¥D°•sfÌgSU^Êf¬(&¡=d^Ú·xGôšΩd¿ÈÈùü¾ò%óþOy™$בëPtµ‡¿‚ì›±§Þ…§Òþ§ÿ\|¶2™eÕ QÄ$Üî>„C潌ûTÙ ~¾óì×È«—3Ù‚²Wðb…Šrˆtûöd¨XIÄ'¡?µÍ‡S’˜"Ãi©ž#oyEÖq ÍÜ.òyË+ ÛU+žoÆ$`>F‡­öN\ï¡äŠnE§=ϲ…<+ÖÊ(º#ïð¹oï_è⼘Z$ÆAÈ a¯ Vl³Š&OƒøÉ@«áá%¬a5Òèö”4D’ep#Y‰‡&(°9Ú5T ÷NêÆE|š¯=ɹnøúT”Œ½Pòº¡Œ
+àzV¨´’eâýõ¥ˆ G‘*$pª¡"‘cqˆ]Ú!ªô¤›%½y îkŠTz<¤ÔµD‡"bR›äIÄÂv+•ɨ‘üZêö-,Ö—Å6_$#’zã~cy¿ÆMCw¥'¹n~ŠÐQb¦,©¯«Cýì–SãKP:šGÙ»¯¤Å
+ lɇ
+ÿ¿DÖ˜Õ6"×cnó¾_æ¶7§GÝöïÞœÛß:|Xó/ƒúõÀ[AØ‚À`uì-£Îû<É$wÀÕyl³JÈ,¸Š$}Úæs.È\Ù®Ž©ÆÇB–e<ÖnRbÇ »ˆ’±-ôº;‡í!ïLv·…÷ÉßÝ%;–gÉÎÑ‚Ú:Cbµ°ÌÛ€ÒX°܇osQ
+àb»s¯L3!$zäM(Ö im÷Fk&Ž–ìRŸÌø`ëÜ8bÎåçÜd$ŽP­ÁœhÛà+…бQ|+ú2÷cÑà$%¿Äy)ÚLj*a}“; QäMròÖ(×ð‚Vw M/[8öÃvZbEò4¥Dò¡y4LÆæ@2„‰&åGE¾åAÖƒÄ š.„s<„µb©XÈÆ®úM§V¦½×“_²¿©FçþI÷†¶ g®/­5•Ùs£°;“6JŽ€…5°S6£L—~9rl6’<AÇ2È•Ó½öþÔ¿#s dž ¿Çž(lµ“]þô"ö×ö˜ãz*êL‹{sÚÓ z*ÑÿÉ@È<aF%â¼)ŸÏÐËSM[ès>3V£gàÃAŽÎÁŸx¯h—¢áÙ3Ž«.ØÕ
+áÐÅÿœŠnÑeÁH]ìÌB»ˆÏñ%úäK­‡‡"\>ÊÁ>B7vmX'Òû1š“i’EnU†qX b@°äJ§¸ÁV<—ÝCP3fæ7;’lHHçQ@œ±³½M¿*°œ5@%ŽCÉ/Û €C°Dˆb:‡tPŒ;ÝözA@9·[¿’x»¸Ÿ¬HZãþRDNø›0»·8*šfþ­vW4ž´b(h©dŒÚÚv½ …¤­C_DöÇ?÷’Ó´9Ñ«˜æ·‹.Øûø@Ç™µÈpV.Ä'g½– Ç‹ô'gÆ…¤•†‡‰gg¦Ž;ß½âÔ•U$fª"òj¤—­û9üKÐéíŒcJè!S„ÛâŽoE§y©)LŒïÖœP|¯¹‚¸ÈCÓviz9ƒ¸,Éa1GŽíÄdÞÉ0!õvñaWô‘5A8ò"Ê|Bq–FÊaÀ…õ|NFö;MÈi†|DqF§¡ÂvÙ!'³€Ð2¤RÎ(Î/ô¶² 8q†1«Î«4ØM±O@¾&ž^ Ù¯¨¶þí¢”ÿ†Ñwfew^0ÔLø!EKËn¸°,ëØIŸV„*Cf¡CÑ*á>¸Ýyµ—DºÖ¦v+Q:€8iÃΣ|˜EdÛnC!Ä(Ž³™f4T´ };G Àl±°Zñsøœ ;­ÒRbÉضY§•ˆòQs<^I6”[ê
+ ɶƒêý©[DA\ëRÿ–Ì$Þ±‡õů’©ÍãvÓ÷‹¿ÉYË„Û_Ìæ*<Ö—‡(Y'ŒÂ"Xÿ-@Å´´ò´‚ÿ¾Ê?~§ÉøŠq$!‹7 öéû¡c¯J:zñUÂZ$¾¢§‚˜y!ñŠ¢!ŽÓvë ã9ü^zœ²A+ÆÎYÙ­7æâøˆêñtüÖ†Ñe ]Oí@l“ësÉ|
+ ª@"?!zEF%õ„,•Y%š•¸´5÷%LeˆLñ½§1B#·nÖð’»VIˆŒ†¾xêb{*1 ì£Õé¡D#€Y€D™ûS—¢Î:±„AFpv;‡æIhòœÂ"'ÍN+)r†¬XïŽ6Gz†½1…$øÝ´“Çá7vgs\è…‹«\ à.„iª[6I2.^ŒQžJÎÜt)úùPDtƒƒ€„dÜt(JÚµ.FÝܪ„úë^”øðõÃéË:Øõ)sA•Â°Í¾ì5îË?Ѷ±¥wC4ZïÜ ¨ÃÈ€K÷èlSˆlONV’ÑM¾çŽK‰Jy@T4ÇÓ1hDªŒâíùØ©ŠÈÊyÈÑÕ”]ݾ !Åö† §§
+"ȃž¹Û`m°CØ*züP‚ FP&‰kèZd-”TÞ™àÏ¢‡"ž%âäYÉïÃ(1ßtdß8ÈË…I@ò—ãæDÆ\ö¤@7}ñÍÿ¹7Í»¤=*jð/ñÎ uuð΀9Êa2ÝoàˆÝæw¤FDWÞM†D«ê©ˆ
+‘„V§7üE
+êáö4 Â`Õ}Êl2Ѓ¿ÂöŽ×c¢8<K`çðc0ˆdPR¦ó~–“ˆxVÒ–€bïpXö¥øb‹ÐÏÞçε_RÐ*a…:F!†÷¨’LiÕJv/áI].Þ+´(AÛf‹¯5C$Ž Bí7\k°B´rÈÓ>„,`ZñºÝ™?à1*Ì:a«Ü4½²U“#æ.ÿ±ïr/úSZ?¾£¿–5Gƒp ˜(ì‚.΃=b{°K¦ïEt¨ö^Èn cí`S–+Ð×9ÃXôŠ&“­óÚR¾ñm¿iZ¡‡¬dÜòVf&z‰ŸIV#x¦I¬„û²k5»}%ñ„ˆ¢´>÷—&º[ ó”8á€VÞ¨3¡Nh’•pë®>ðËb%B¹g3£2âàlìE1ÒYÎoQÏlE".v¶(ÑH‡a
+÷c%ìY¸3å4äÒÜ+WFɬ™¿¼0Ü,«züå(ß aG;´t(ªÕö~yÁ}0¼‘%àã„Kïï“гÔ
+7#ú³0
+·pPÏÖ‡’/ë¹ú{+:íy‚Ôá6 ü€9Ý¡÷ÆË$KŽ¢'Ðúzœ‡µ¼Ô-¼mßë$XíÊDʽñ“K“Äñá|îA5pçêc‚òáÌì|(ÙiCF@‚?Y@¶°­¤iYo.N!,j"ÊT^³õFhD°ù"öÈg³ÙBù#\ER»® )ûÞt5ÀHt}’ö9Xȳi]³ª5¶ËQWÇï¢õ ³AGÌùU¬¹Aã6D‘ìµ81Ÿû:s²@qi+®Cò*ó–ñÍý-ø’‘ȸÇ|x8.€Ùº¡i¯‚m!G}=gòÕ3]Y\Ï$/vNàÖX «œ3€^1Õ&B³n…C¸ï§ô¹Ü‘2’‹à­Uâ$“Ú!4=YÀ'ë±g64Ô4®/ĉÈMú׆´I’™Zë>‡sI¨˜ÍŠ°‘
+c©I½œy²qFkc×Ò Òš2HaA´V1ïABm¬·€¬FÞ•訇á與ûô*<Ç.
+ÊlwŒš<-dÓeÊŒ@¦l<§îüÊâµ6È} VHÅ|6PÒÇDfm |ŸÆ›7êQ÷ÈÝ'ã»îìY•.@ƒóÛ‹a©JV Éï•N1Yý^Ö·•Ê$“MAû!½‹!¢l²"Õ)ì˜d>í²RÄQ1Ú§@!ŠÕPÅÜ%KDPü`²ŒdË€å¾Éôà‰„=êÿ
+®Þ¯Þè5kíîÆÖê‰AFf‰®c«\Qâ]¬^rláxmÁ!úÄÀq.£j:^L˜D³-D¿ÓÁÌTÖölKBF°©í ÿ)Ï9íê ŠÆƖݾ@þêHÚ­á¡2ܤ2һ{reýoGúKé‚´<(ötŽ&yPƒ#µÎÞSdCg&.íŒE
+(sÅ{±éƒ%¦4uznì×ýfØê0ÔZ+'`ÿtʹO°s„
+S»al‹œ Õ<¦ S²¶ =¢Âü w®OAÆà8¾Ï}lŽ4aϨv:]˜ˆ³6Ý,Y7pw1/g^6éÀÕ¯~P¥
+Q]öüÛÍò7ƒx{hL.Õ0³¸a³C—™4Æ£aX¶Nß-¤–)J Éõxp‹5~Œ‘fg×.ac`B`ÇîÛ%e„m¹éÙ…µ†:™‰EåÓÓ9ú(ëRrB*½Ï3C ¡e;ËšÖ »06ÎV3|çS4ÔÛ#í|Fç‘3ˆàWIƒ”
+‰–§Ñ ;»,-Êö§…p¨¨œ6·J:AjPí˜SÆ󹃘¢€Çr²Ý‡‡kÓÂ[fµû”!#”ÛXîÏÑGÞ<Gô#»¾GØÇš˜©J©=œ2É7˜•‹¨äS)ïvºhNV³›ð—€ŒIE}­ ˆñÂùúé/4ƒ†â»)ï`f#õ¥&o!jæŒb(;Á· Kº½u úýµÕ%Ü0i’3fX4s›
+!ñ'ï„ÀŸªNÈúTI‰‹Á¦ÇsÀpˆ‘ÙÐS‚¡êN
+a
+˜VÏdHÔðkž>xj§VÕnA0mðÃh‚­ÏÞÇà8–!í£}Ø•ù}ä^¢ñcú±œdO¯TY{¸é-IJŒÑ XÙ“…h9éSC«Æ ŒÅ‡Ý
+
+Em-›e@«ä@¬gý|©b¬@v,'& ½"nœ¢sé.-s
+ª?G’`k d–miÈ‹Õ¨ÛYG¤T)ÅNÐêè®Îðß[!.!VòK=ïçܺ‚Ët4 yDU‹Û]•- í¤ܲ¼t©TŒ@zfÞŸºõ:KeE±WÆuº!Œ§|&aëß߬/çiK ë|eî­Ë™A/ˆîƒ,gQg­09×Ðt˜Ä´³Ðͼ•(¢ÐT%žHT&š…ëæQèÉÇAîäê!=´àô‘²»Ø6]¼ËyótBÖ§ø|ÁÕnÐ
+T¾¯7Ø•¡åæ_Ž«v:·/ò“$Ò'ÐXïr¯_?ܘ7Eüôbðv 9ÞšúþPGi‡Ì|oµ8\‡ŒS£²]
+Qî^¬}’”‹“„SçÙ¥Š¸3óµ7ð,†ËÞ3AÿS&b3ÚÙ¤ÖÔ³s¶Õ¹!/¼†X1™Ž¥½G|:çRïûmn}ã½êÖ÷ìÜšø–b§Íß
+õ÷wæËßZÓæ@=°ï…µÕ݃Uì W$J^©jd€>†dÈÖmܼä2j@~ÈèÉêIRFœr‚.²JHÕª‡øCV®z¥¿éªºŒÏÃQtcòÏ¡¡IFЦy𸠮‡F1Ù3g’1ö|ú02×AÔçþvÿ²6‘‰“œµ<ðÆ–ö1àœ|±`“ƒn#)éï;¢Æ
+ÿéA+t2kŠ09î‘C…Œ hø &žwËóð¡¿gY8!ük6™4'ÅØcy9ˆ $Ð3‰>Y›ê®ïL,[€…Çw¥¼ÓŠz‚¦! Jy8…Dæ˜Å'/pŠQ MyOƒrWŒ”£LJ'é£à°÷´®Qa¦ ™v<©Iå†DÌåô+²Òá®–`þÔòœ¶A8cG70q…ôµ×§þ<›Ë"Ù1£¤g²éy;Ü?”;KY õi°?°I Ý¾´öŠBµZÍþÐ@u×Ø/d½-¯-aëÿ2^&Éuä:]÷à8Ø7cM½ OåýOë\Ìò{É´õDE•%Iàâ6¡?|'+ipeÖëdX¬bDv³N~|H›ßNEóHGXš¬dýrÇU…Ð&â÷Š
+‚ÕéÒ12`¾^ã>‰Xƒ,DñÞ°iAdÉ?åÊ4ãků![ R€…çÿ96+AÿȵÈM/©ýøv*:Í\6Ÿ]¢éÉûRÑÛ~~eMÿ7¼ÙQv:Åœ«$;?þmES¶%°Ã!Š(£a”š¬¸í#Æ>…Æ=–)¦D–dIOð’ž°Vè[VlÑP(¢;ø››Ð§•…ã‹}G쥈bšŒså+A}Aƒ­¢êº(²’<ЀõšÉ½¨ y¸(DìúÓŠ"Q†µƒã¡èÓÚE‘5¡î:E«„W´P÷ðà ¥Zpü,fµQ!.䦃wþeG¥!l£9­¨÷.2ÇŒlß!(–
+#42ALg
+>ôš~ >Ð!}©ÛXÃ[`†‰õAöŒŠÆ)çu/9Ó[Ñχ"¦†÷g¯2&æó¡÷‹ò‹M˜æJ8Aa`&‡´K¯6Š3²ó Om1Íüù™o§Ñ…ˆØÃ-¾CV¿Õœ•Ùr ÍÇúe=Bôäd-œÚB·†t(ñQtÎÌëÓwðe«”/˧lËPAjs¬cóq4x˜îÒ=sg’$à¸VˆÐýÐÙ5‹<!ókÎ¥úȵÆæÝ‘ÐbrP(%Hs,<}6u`ª¤DzñEŸE<‹ AKÕáÞ`1ǀʕÕ°^[ð%Ö´¡1èd[,‚–$Ä´7k†ZFéÍCž[Cc þt©;d‚¦+Q’Ô·$‡þt$&ûŠi/\M·Á½xt?‰ …ÊÈɤºé êX¼£…t
+ö Eßd-
+vvoñ­èeðŸÇ¢Á—”*ßKÑk‚§ã$+Q|^ê­Éz7€–“¾R8ž%Ò™¢/5ED%ŠJ]ÑFEè
+“šAîµ:@åäøb­“?]‡¼7¹NÐqt,—·&5^ Ù®ü·ÖeªÓé´-»×hyNK Nkdùh_»Žúûnš¥ã§ýQâ,–ݺÈg†ŽñMÅ6øVC
+Kµ xZ× @ˆ<\ݽjšLà\ÖY1ŒÖ°àSSÇr–]È»b·FÆgUD_†!®Š„ê³°³vnŒhoËV2’T*qjùbÅä _A7VId8Ћ‚ÑäU‚ûÊü•²Dñ£ð l45<óÓjè7ž-{ ZMG¹ÙlGqÙ¬äfŸ,5ûÁŸÔfך¼õô’Õ>Šh@Üg‚e*³8zˆ` x‘&{µÏðÓÿgéî*¡%€‚—•î%“èH–jO%7©¾¡HM>† Ù%eÿä,×5V†é’>M×–ÒÉ2RÕú
+>{8*¼™ŠQö¢q]q,jXE,8¸è
+®Å¾Ã¶#˜Æ´z“,ܵú~Òë(©€ãá}Î&²îy¿‚‚"xYhM÷óî¨جb€|_Ëu–æM©[¾É
+ œ§BWbûÞqôà=JrsEÖ­ïÇeƒKÂ/`((w
+;Øà{ƒz vx.,À#G%Ê!¿Nü=lü¬YIã·ˆŸÔ}0ì;ôدO4º6mrOð¯>˜¾žÉº^‚—ÅYE†1“•0”ŠÄ2Áq(YGeâÍbw˜äñ;ÃÈ”‘Ðæ· Š§ ˆõVIYjÄ(±åþ(rKƒñ—bz/j+»°L
+ªÃ0ÆaCÀCá·Û ‹ä
+?2r¹Á‡Évñ{óX‚à œM2+¶úûn¹ÃŒxg\ ’„ÈÆWPú0U59¤iøÍâßýNl[B.,§&åBËø+Q•+—JÖµÿc¼\’ãˆaz‚\Å¥ÿgmŽ2¾ÿ6Ù‰»5Nª²‰ #µH
+cãN"%ÏËCò†¬W‘ÏFk¦Ÿ)Å‹hÄÏ'Щy ^ÄÖ{ùoÓ<øfÆDQhsžëp+l–&ôo@_¿é¦S¯ Â*ùV¼Ñ©@êÑ~ kb>ë ©›ØX‡¶ r€‹Xv™Q)bzw{*€Ø.­‰(rçÞ7’Õe¼„½¡ò«¾gâ¯+-e‚3þõh‘A‚LÌkÊùŸ£A¬Ð3Íz
+é/qÓnÒÿjò,>œž0Ziõ ÈÛ@›'šþ›È?¯¨DãY8Q•î¤ƒ}¨`,øžç6ÅËY5n›eQe9³yxTÂ@ ÐC,qʆfd€‹°pQ3bT¦¤{3 œ¢+çï·a©X/Ú¹ôîªYÛù1Ï1s>{ój†þ‹¯¶x<¥í{µ‘5gr ¹õ‚`Ó•»¿i€É¤‰x±4¨ߧûî—H² Ú>ÙÿÄãÓÃzÇñ=½¨È
+Q¦_uQMVD­;s‡q ið0M=B]lhEXyMC hc°‘¡kXgÔ*ãÓ€¹ð
+V(WuœPÓ¤ì¥ØÛ›ä£4]½ L*zAÕ¸ )ì ܺ•Ä®J˜
+#éïÎa%§T¯e"ÐÓ•,LmùæRZiB!QTB/½†­/^ƒIq˜m¶~Û\I§VÚ¾®^X“(0I1”72(_hrDZ{Þ6wt»÷á èõÄ«À°²‘zëìnü$ÇMÓ&á,bz…»6À` ´Ä[…òbq _¼ÄŸ¿£©–n)ƒ8OKÈ‚]…‚‰,=D‹ ˆGÐYÜnC>‚¶U4&ÄC¸²~Ò;©‚âñ
+™Š(§ÏÅmòÁ«Î›¢¨3×lµ+ë>˜XÊè^AoΑb®|KŒþÉ82þIŸ&ûD‘®l<µeŸ=+}þF;:hIŽ Á0üRÓ>c‘D¸³Ï¿QŠ‘„0ŒuRvk'–ÆrDK ¬®7y(6x"æþ‘‚Í$Ó‰²)v•?[0í£É p˸˜9ª‰Ît÷MT©Ž@ükž ¿Íõõ?üW>Mk(ü°ÀS»8E•”?ODˆaÕaõ "Aãkë–Yò:67úJù™å6J™ÈÞ~Cw'™Sg¤G!¥øtQ3”€2©û}ÃÀP‘¢ÂZ¡¿ò¤ !²)5‰1Ⱥª|HÒF`žÏ)ò+žPE¨g¶Z2ù}žyæਭBžä¯ Bóq¶Ôméè%¢¦` gƾ,ã9…|ãëf``•còÚ%ŸO¿A¬Ê·žÎ¹5â¶ßœú)iäPÞÏÓ,=ÜiÑÖ°Kî«…‡;µ”Šñ}¢(ä¨'„¢>#€Ë#ŸFœô[€
+H‰”—M’\¹ „O ;ôüüÀµ¼Ô-áUëþ[xËÓU,E{313õH‰ÌÄ5ëÓ.¥|ü«w»† ­:†ôÑ>~ýøÏùø÷ÒZ½ª™·6›/h™Mç¨âSëÇŸ#H¯á:K¯S‡~܈æ6ël6u.D©Ò„+Y-cÌÚ{)= ã²iZ§um¿×Iνͪ·ž n]¸Œ«È}T»Šyé&Ò­.H+µå KàÑ›K×r€ÜGµkrˆxáýü~Ub>Ûã6RÇœs ï Ò§‰•b”,_å•êðÖbýãç¨]V{íC|”êôи¸Gu÷^jÿ6h^êyi"ôhú5†¶ªÖFŸ•J'Œ/Ý5òkö:jS™“ª}.PþÛ[+c}ˆßÓõ®ª­¬÷‹úÎè٠á‚svzØýØŒ¯ˆìEñl²1ýøˆ¡ÃáA¥åUÜ»7º3“`Ü–û ìÉYUÁÍ}l‚qU¾QTF‚´O)ÀèlõÄvg$ªñwôy„ì6;¥bÂöQ­˜ÂÒ*ÍñZŽ÷¹yè Šú4ξ'0@Óš4‹Â/ÓàÉ Ì§>HÞ‚£IWüj «tÂg¸dEN“Áµ²Ì
+ÉÞ O¹„O*Üíü™rÑ@ŠQû,vŒiS•×##K4¬R·1joªkv^@'2 RÒ(-ß_šð-ÇqŠ%•!XŒ?€:*:ZŒ]­«@ç¬ÙJªa­œ££À__Æch÷ÞÚBȤ¢fGH~¯ƒFæUç«><Sju5—ú¦“iƒSÆx·)ZÊ…ëy’·)ï:~Z8ŠÐ³Ÿ4Ä…¹Õ„dŒn-Ó¹|±Qî“
+5ч-HØIÕó± Vë=,‹­–(ªN›hìr0äŽ Žp>ä*>c—ЊiHtM¹Ã¦DǤ%¤§ôÆt×iŽTúOVG^@Æä´‰¡P]|è׺d‘[´¶&~}.S‚â:âbÐ A’œÊ‹ó0|'ðiž· ¡ê“Ç™Ðcçf{4**ÚfgÀÃ"ˆð:3¨a"7„#ืd–BˆdJ ?>Büªd†2ð=zõê\þ2¿û$úÒÜ“9*EPD±êBP`ó,»ãÂ-Ü ‰Ž}®GsLÍf¹Ð“‰üJkY½ s! ˜¿ãßßyüó!i<¬l½"}Š’L/N­´rû7/Çœ,œxAzP±ÓÒ×üb`t‰`U´ë–ÃW´± ¾*ÆC~î)@Ý¡; ¹£.9\Ó舲öSÜ
+ñjî-‘I`ÿ&û? ’ÃveRöèáxcj[)Öî°×béÝÝÄÓŠG.Ç1¶^<<ä ßEõRð" 6/¶«TºO;#é¦ø‹Ãve—h‡¶`,–ÏmœTaøJ û6¯ ºëçw,8WÍFÂPÄÈGŠS%-2?…"¨ü9‚^Â. ¾lK¡ù91_ÆâכþzŠŸŸo@^ SúqCÂ@+ª±#M,\¢j#Ìù€ªug'v=ò!SÉ꺨Ø"tp˜·Gx*
+
+ˆ¾Ìˆù“¸vh¸bóÕ=„¡žûZÁä|7槻Yƒ_ï*Gš¹ö<í7]Š‚ L@âð•‘™q{5;ƒ ³ú†¹àÑÔ=å ÔMDÜ^Ë<në­ÊU}z¦:"c½ Ñ‹±^è,˜Iá9œú ã1ˆ.rƒÞô7Oa–•ƒšK(GIøDÂksv+ë8 Øü¬ .Ї»
+NòãˆmuÏ™4€ ÝÅã†éÑ¥¹jlÀ=ÝW®Á>"À¾sÊ( ]cøR=öò¥Ä§ý¯B Øâù9LnFk©îœ‚x‹Û.‘,æÉõ]ø"“‰Üä[H ù83&óÛ˜Séæ¬ú¹6W—¦ü€¿˜UøÄLþ:]D”’Äû‘_`zS=
+ŠqÀÝ*øJzÀVû½Ðú—½ ‘ð¬Ö­BCC.Hüí9Ü'_ËàL«à•à*ßÄ¡ã*àòèµU2šG„Œl½½ˆÛàÏ`ÚÐ-¾Èr뽂Q'{Ì‚6‚H1 ‘‰6"qk¬>PÂö¢s¬µy8‰ cøà-ÖóÓ^5Du¼HV±ŠXiø.¢±kgô²,vä^)ÇdÍ%óÕ^°2HzõŒ©Êaö
+óáð¶Šøú0dåMsßðy†…ÐÎ[=ç#Ñ¥}ðW{™ì…0[”­„•á‹¼Â«ÈþóŸS 3ºd©°Ï¾Ôèøê
+Vx790KÇœ<É·Šÿ+V°GþÙb´B'F4‘‰Ë¨²Ì0~7¼iô¦¾ò_Ήö2fB)ðØÎ)À€
+hZ £%ü;þ8ô’’†š³»\PÝý~ŠŠ.àÂ`EûÑD¢Q”ù—l•apW.S¿9-G¤éþ*x£qjV=[…VC‡é½[I®ôžÒ¢z¿
+§Î°GnB¦ä‡†
+×íæl¬’èš-/Ç’•XbòñCïË7ùueV¸“F.AIËÈY`p~Y“’NJ`Õ{4äBP
+#—ÁKïa¥ÛÜT&u
+˜ÙP“¼‘\*ÛðY&P)³®Q´Çp;=&®fî—Ä!%„b­H |CGS¦„M¬:½+VÛYX–JRßC<ý<±7È š“ úd³\ ¶;ùrI„´ÆXöê<%ÂÙGqENˆ
+¼ÄÉg²ÝM®="ç—½IÔ89ˆ_T͓ƪA=wêo×p„=™ÅÌÓ¯Dä³¼á ,UM×ëzåC‚Ó* ÷,&姕°lU¬8a! K"µ‡&¹v¾=ºM¼Æ*éüOÄ…L8¸j2èä”%P4O‚jvÕfãPD¦“W˜Âû±è:‡:rê Xc'È·²Ä…ì_Î^Vv<Íã’`WQÓÞŠçy®X ˆüИâë7¥_ÀBðõã‡èF¡Õ½½éûS‰OéÑÂ^f;?GN7ÔlTÇŸSšêcX¬µJ’ì}èˆ~šæ+Cdw¶ÂªGÌ1ÊG=z*Ð0;ð²?Ö溰ñâ÷Çk×¼i/¼„ Ï‚¯îG7§5)lîÞõKQ屉ì‚ÔfÛõ«h|§Fѳ1Þô¢Ú\¼Ö úxXQœãn­YO÷Öà\Àhn˜¡Ø"×Ú”2L;]¡çAÆÖ7}iõ@yËX¾Sð…áÈÖôÀ°Ádf»ñ3kÉ!Q0·š‡Ÿ uœëȺ‹7ÏÑ0ÊÖCÙ%‹û:°½d(ç€H¿C ÑVêŽGÌWÒˆ>÷šgðUº\|,
+¥”šŽ%(äxFk¾S§¢'=¾‹PI,PÖÈžŽŒ;¬ƒƒQ‡ã§ã2 ð€~„cûÓ_®·b ö©ðqhÃÅò®Í<YgàÂ3°úüÆ‚µ. zÜL9 7­–PŽ…É^ÂGòÑ=IÌ6
+!?Ð&‘*m|í×e£csŠ¸ËÇiç¯N<IiéóŸÓö¾ݽH‚<ƒΊäé]žR,£vVofôç.©!º!2GMRÇùÊÈoo`íà’Ê?uã 3Ì›µ­Á5 È!ñ4ÐýHÀ§v>•ørbÎŒ Þß>')$b^.#ÛpÇ´PY% 8üÉq`©Ë
+–||ˆ%ÜúÕšÍÆ¥alÃ_^ÞshZȺgÖ.»è §"¾cýŸIOvCì „ªu7ÄtÀä‘81¬D!
+‡æÕ ¦¢º\ògµ<µb*~”S¶6RúÎqã¡Äß„<$px9?†qâèº2fÅK *¥Dstº‡8ãñHãþ¢?sÑ—O ɉ#Ë€]W:«†Ô$M·Mò\¡¶8ó‘Œ6~IÙ2•
+7ƒ¸°¢
+m¹§86*Ô9ŒÒ
+¬L?eTìëÄ”¢Q€¡dK‘=4(–É·õ¥+¾ÎÎÝeØþ«ˆ-_“¶0
+ÛÅjp³ ²U~µxŽe·i·ŽèN>Ÿ‰BÑ?¼-º‘Ïú·—k@”#úuO8Ø
+Á£Æylµƒ1¿†èS£SÍWjœÍÇŒy[ôlÅ}ô«=Í4¾ƒ‰æèÚÜlò¨$S{Ð( ¦àGÍgFÚëÃ` è]b”
+"JxªØ9Ð3Ï\)² ½ƒúp0ï ˜öG*Ù’°$†;·æH¦érg 3JªñÇu¢îµCO¬×1
+ GÃ/‰æNÒ¬[ÝÙV˜µ8;agÛto¢´h»ÊGÝÈùm‘ƒd]MÅ™ˆê³è²'Œl´*Çç«i èE¿ ±Q–A¾꺀•ã&Ã,º¶ªç ›2#Ÿ™gàû9äm8?õiþ:GR•1‚,³CBHfÅ$OùÅ+2$ü<ÞLEðñoïTâß Ù›`È{;þkj
+b žOÝDD>hÜ ÞËèE…v'ŸÔ¼4‚®¬.‰¬na{Ñ!¿ý%ZƒÆÄ„Ú©/êh”Âú¸¿
+ào$ž›Á T‡ÀÍ–¨&xèÖŽ3× üõË‚È݆!ó·dÔ>w†ˆ84žmkй…®åOõˆ‰Zˆ=ÎBKäq>u5è†"$#Ò¶Ö‘ÏòTØnPð£hj`ŒK"x–Õ°»†ÊIɔů†ÀUÉp+'¦Bí3)B¬ëÔŒœ<‡aëüÚ…,ìãµóJî‹Âw¦‹è냨`…"à…ër&G4PlX΄bùXmï:!8(6»´Dlûʺáªq„1‡ÆÐØpŠ˜A«h$²ʃƒ «F›}v8Xü?ZrG\YifsTúÍ‘ìOÙz9ÄžÖaZ*I™u&ʸåñhõí·U®„¸îÇ¢€†°³è§c²¼©Ó¿çvènkb†zÌ´cÔÚpÚ·CÕD‚9"?H2n™)!ϪeT;®Ù}E]EÅ„kÜûáÙŠ&-½ÍÚ2‰©û**ËÕÂüOÊ6Ï[Yá*Œî³—ÿkˆÔ-Ÿñ¶±*
+ž5ìOQ'¦x+"
+öÁwX Ø]Þ‰0°¾€÷ÂÚª ‡ÀRÐáe¼®"´l{ãß¹?ŠéÃÒla6Ë«–' £Liè&^ª†gŠâ ³Í YwÀ$ˆmãç?¬ö†ëLP|s\ŸÊ
+WÀÑ—ig2µ«Ç{j{žb$K¥/9»¹‹~Ž×ïïd° õÏÿØÿúë?ÿ7y5|Yÿý—Ÿ¯ð}Ò@USþ‚[¡Ç1;vÿÃU†=üˆa«Š<§“"Ê:_æw3®¢ïKÔU%´Lš s!Ჸ…!VŽuI8žN‰ÛÚ–ÒüLÂAÆ’ðãM<°hˆp1$Ú½BŸèì`ªr»Š^¤`îg']º¯ ™ƒÈv1þÿq×þÜLóà€¤ö¹¯rKÏ„?ñ¾÷’hUŠ|”‰fH8 [)ÂäM[øÎÐÆï—„^…¢Žìǡ莶ErVáBÕuz\ó m¾n
+¤Ù–~8¬PPÉPCHf^Õ¢XU£&ËËM“–½2Õ‘™ä’OÚäŽÍ¸éb‘‘Ê)•ª•õô;(¾3(É@‹†…k†ò¯W›1‘ë:–ПNL-Hš¾ éBêž3…’ Î}XSehƒ4‰Éy;4âÚwkh‘0SÒt߯ÌF-Ú¨)P¢{jUªD$ì°èµb Ò
+
+WЇØíE[1HëØ’ô×,±þõ´ÿí@e;°×|-®¢¦LÆR»ή Ÿ,Pé
+Ó$£’çRRLc)Ÿ6áâÀ>EI°9Ñcp¯Ð(YŽòUÐ鸂Fcq
+Iª>6ø
+:õîvÜk C[n%x t:îÚ–“þþµwKÞ;‘®IX‚Rö?鯘Û)CÇ_ehø †¡}—¯BãfëÃcÈ0Ÿ0„&cPNhpn „®€Ò"›° ¨|}æ‡t¥+ô2‡ ör‚fh¯ˆßvÓŒp3âxú Ñ@Qvˆòõª4! B¤™ZQÂgñûö$%à;#,ŸyFK ”œ¨ú‹V92sá¶CHA¤GÈõÊ°ÅŒÒ&K£°žN¼cµu÷GHc‘à LV,«€3‰ÃÍf•õhhWŽii©2Þj ÞœPÉýñ; %:µ ›Cp¯px(c#~ˆ^¬ hÿ>éϸ(,gÄ—JÑag‚Dtˆ]¡eÔLžàO趇ÝSb.É”ÏÖU ,CgJè´&.ÕŠž
+„Ÿ â“=Ʊn=S‚"µbìž>ƒÏŠËÙç6 ð (¬YŽÊ]*ºïvêC¶?¦í:„Œ¼y9$*͵¤dßA–ù¢Bþ®¨8Ô”‰C BP+ J‰î0“/DÌ­Äs˜S.¨kpé_BèÊ4Jfºg·A¾czcÚ;#–¬te¹qŒï­œ`éë ÏQ)EÙv€ü¶yˆsù£ÖöŽ_¿ÃÈaqJ"m» µfô;7F®mô4 =Ó·øŠ#± ߸ª_ÿÜ1‡º RL4?Ê==Àr¤¹Zv¨véÙ÷ý}[¾<~NÍ#½DBÊ
+oˆ5ä@* E a-ÖDWâÆ3Ì0 E“„‘Î_VsÊßÔÒÚˆ´ÖñqlˆÆ-ð.ÖWÏ
+Ôßû÷â^#árÚÁ·…Àaòé8Ðímz°D+"æi7Gç:¾)çé Yþê#|Ý|ZìÁ9ò«CÕ†›­.†ëR¤»îßÛ~ðIÅ—¸ Š_ÎþY.ð%Ž§Ï¾/+0@„Eš°í÷gc^ñD8úl$Þ_åƒ6ˆöÐÔ%³ï Ìä!ÝÀ ÜWÏ"mrH+,7Œl ¶^Ä 0Ý^ãèGã? øŽw'ìñØkôw¤ˆ-uI°`P(üzyÖ"ÃìÆ´÷Üù“­Ý/òX=MïÁk’º5‹7Væ¿•š>å@•7»£±èÒH”§ÔógØ?ŒU›twë)vr«{DcÇ››?ªñï]Þf(£^‚ÔÔkƒ–UüŸo¾/SM˜W$àšz~;,!óÕÊ>êÏËiÖ3áÍ3_—+ë[Ù†L[mŠ|¶Ã¤, }ç{q~Ê;Ç‘,±øŠaç¤ÀQÌz YLýz<&Ô¾Pü¼æK×a&¸¥›†)*z†fQŽ·é8) XªnŽ©èÿ†A¸ÈÐð^ƒ z¨é"%ïE}×dð‚¶^^PÈVÙ =¤Ùw8©ÁA¹ ÒléÏDÌ(AÙÈ~€¬ƒ
+MçÙrž&ŧïh•ró»pržÚ=ˆÏ •˜ÅUô3‡@
+«Ö óŽYˆÌ;X„ÈØ-nÄÓªoÍæ:¼A^°tðFàÑ”½ÝeT ED®ƒŸª~ÖÊn‹ØMÉ%†f±» $þïÃÝ`R­àÒ¢E[ˆKÖ”œz9>@â"ú’d¨? S¯wñ«A‘…Ìëå@âà;,ûö`Ø`I(áiLƒˆÞ#ì=ÁèŸibôâD¦4SÁô]«pÅ%Lr51ÔÊcJmúc³}{«ðN÷ìTò<h’²±¹4ù¤nÖ \G‡} —‚ ûF¹¿àkƒ¹çN!&9JÁ °ÕÑ-¼Qæ ÛG0%L6zm™–Šb´³ŒÙ”E‹6±7þ:(y¤—|,˜FÜ@÷\W&BŒsC.–Rï–=¤ïyOòÝR^fùn)%ªôS,T6\,å­ç'K‰ÈG@lYÈÖ”uà wb“ý2˜ˆª`+‹A¸ZÚ†Ûd
+dÇ0ôŠöÛQø’„•!¸~´‡!"”°Jo Â7ƒ¼aδ’Úé&@h,A ¦Ï–òºd‰#_¼‚!Ò7ID4kzÃPRš‹Q¨MÛø߀†™æ駉bàÔ¦IŠ&SyW¹Nê9÷HæX{UÌ!,~7W‰º}„e‚Œ 7ñ½D Š I>@–;=
+F€~bµg>‡GBI02Χ8D¿ƒ–m«‘[‡D”²ïæÄ<aÙXší©Q±…"á6 ƒ7#|œÌtFˆ+;8»º¤uZ™9¦|?Õ—ŸÊà =òÌu«ûÆp~ÕÇ97­³ËI~|nß^D(âIní´§¡Ð8žÈïúk9àÈŽ,öÀ„dç
+íÇŠ§TqQ- þ•n ÃÙ"Žeá%<ðmA(3·;ûã Óñ‡4B:Z< ~ÛI’Ö&îýé3(ÜÄh¡R_ìÆRÏÄÖŶ}=Jì‹Û*og=ñ‹m¯ ù!öœ”†¨ú0ßTmXŠñ*èîšt¯ÂÒbÛÓ#äzé€áaÖse£Áã5êdÇŒn8 B‘¸^ vHVQ²„q´}›ŸdªN0ÌŒqâmo¯O9HYsÑPRØ£G¿ JQ‰pÄe¼@ÐÚÕìþ>éÏ3øí£Ü'×Ñï·|ö‹‘Ä &¦ŸÛžŒžN²‚ßãÉ/¨B3îË{ü"~Ÿ©ŒÃ3ßÍ/
+‚ òÖú Õ…Ñ{d(—s=ùŨX85!L^|ð‹'ÐÕ/
+ÃÉw@<úE Ìÿñ´tö‹@˜d,#übTó“yö‹Q ”Ç÷ª_Úô—fûE!ò‹ õÁ/ªéV„ÇcŽGÊ\ý"ö±uoûÅ/–w5jˆ÷ÛŒ—Ir7DOà;ð ÌÃÚ[ÞÂ[úþ[¿
+_úÝÕÃrHL¢rx0Œ™
+r߆ßéÉ7Œ™ù#f½R{0Œ7ˆgÐÝ0^AÎråíû °ÃÈÓêÒAÜüâm˜o†Q=(­Æ«Œnº5Ýã&EÙQ¤óè¼s N.\KŸ‡)ß cllˆˆŸœÇƒaÄÌBoLx®aR&kÄå³3äÂFø5îÉ0z ›ôãG ›e¼ƒnžQ ÑDŠ ®gU&‰s¤|Ó5kóÚTÃ(®kL.2µèš=ŠG“'ÃÆ>™F@…F0]tÄ7šT8SztÊ5"AM–vd>™F‘©F…}-ÇO_\# EÀ!KFçœsq·ô\£¬7¸ÖO®Q'±Ò%7™G×5ê]yªŠð]㟘Ô\£fŽ˜pÚð¼Ê"Üe°S{òàU˜8TŒªÑ¢îïqÔ.-­JZ©$tºõÀ*I…'û°!̓ 8¤Pøȶ!´Ÿo‘Z¦© „¹‘Çf¯ צ•\èTœ~
+Òç° ¬šÆî©jØl+>ûÚEÐ(ì„Dô€Q¬e—pjØ¡–V… ¢¤|°k¨ø«áçv<¼y¢k#ø¹âd7vC rLe§\ÃØwDî<'5´ I{Å>â+|óÀl ”—Í›Ócåö”ò;±Ï"Ñ7Yã܈
+LõcA†œžœ¢Í1ÕV“ ï-Ψ3ÅX¼Œ¶¼Çâ
+r;Ž!c‡; 2Š©¤ð4œÐær£ èû'º<5?ÿ„
+a£÷Ùœ‚e’lïE4‰~mé{x ]™h=¡µO2>‘~Q<üôþ@Â"Q ÒÓ~bI@ÌçI€oñnàÌ”(cãWŸ‚§6A ôBš²-¢ïÅšìþJëûT ±sÏ£Œe¼%µÈ?5…qËAL¹úŽE)~Šäá¬pºñ+’ý‚|ÿ¤¡¿²T้%P›3"—,&ez’smžIÚ ¬?²ˆÛrx
+Þ’NõI¬p¥€8ØÑæwêýþ§@–åñ6±?ä)Š=zªÉõjGBvÉ ˆ¨§@@ÙܦW,“›§ÒD_ôø||Ö[ž„7Wf«zyÊÜó” ºæ©;è6‡ûÊ¢ ®7Oéåøl¸y¦3Í·@u›ç[ ÒöápØTËB÷¶;JÆžÉ\O7u­qǸqcÈyD7PÉJwL²<•¨
+HÂÄB€Ô£ºaê6^˜ÒTÀ‘©DÜȹò5LâçP+§¦ô.R­‚-;ààŸxt¥)ØEֻʭî”ù_a6_™E„^/YŽö¨‡nÇRPøø° y6ü/ž¡—‚ZêÂ\•M¸¬>å`8ÖLîB+–`ë«áè¯<ÞP~“к¸aCðh"5¶aèêîW=l›²LWªéŽ1²%*ˆ@¨¾ÍE§+¸ÑYT­ ˆ#ÌÛã¢û³YŽI¤Zë¦v°ˆ9æõì¦!À:+ø°[M˹*¼â®ºxýt{ù†øg÷;Pq]výá˜%‹³¶½’N.$Ǧ5AL1åX”À3ˆH8:Û—0ÁX±.­j¶ƒ©•iz‚df¡|p3ÂAØE²»H
+C=+U.»›è‘¿=вLN/’‘úá\YÚÉDà'Ú—œ—Ke†’E¢/ì½Õø½€R+L™”)INnšwë¦#k(˜®1´àJð^<¦¼lµ»°¹h>/f›P¼®„eùI ¥H‡`Äã„CD¥ñÎß­ö}[Vœ­eÆ¡oÎÒ™Ê-ì7\5v/¯Û{}Û¾ÐEÔ7„9Mê9fÄö¥&²(‘sœû¦ŠÑb¡Z÷In€|Ã(é|1ëʯ0%í<zê1ðÁiçð—‚ÃÂ’dÇþÞ{ù°¡!NñÆäOýC2ò"ŸÇØMˆŠ­0¯3ÙX%i µÍyÌRÖ'Uo¯/üºÉÆ[£jUØ¡ëÓŽadxL'}Ùƒð†P5sUšËa•›»•ˆB|"’n–öŠ;;R(»M ÐxÒ~Æ3.ëÙ gÃSkö|˪–AFä=ßro€£rË¿O¾¥ŸRÞ¦hÑŽBX $:§­«΂ñÐÔ“ZŒŸ`ÎóI&
+‹˜†úÞ ¨&RmêHæ¡
+mË˪K¸2ÃhÆ é®†×<ÈóbÁ¡îâäOÍ@À>¿@S–-‰rßÇ@Ý„(ÍOÜoçCd®Šîåø
+6‰£Ë
+dT šáY¼ØáB¼F^O•‹Æ%œ/õ†q ðÀf v+G§DÅ·ByùllmžñÉQÑŽ”$@—ÆÓ1úRâý@ØVŠéGÞˆHäõ^[ÞEƒJÞêûKéëÄS¥åön(Q í›¸Óç(!úZ°Pþ!A¯ÓG¨Šs3”z¾sÅÐ"T‘ÆÚ)“à!.äÖ¶yÚht½öiËIàýkÜn¡èˆ#›íµzôˆÎé?“‘¡ì Ò*Û²ÃðòîR`õHY$Hæ4{†•¢dÙ<1ÓèËÉ#â­4ŠCªðý
+jØ„æÁì(·W@´W˜½ÿ>W™úPÿéÑß! ¸¥'åp9~¾·ó jݲ¢W>9L;œˆd¸7‡n"¶ˆnlÄóŒfá&
+nÀVMl×¹y5”m#¹ Hýî.‡U„f»ŸÓš"î•}² ”c.‰Wuw7„/6©³\ÍEž1j2z·&Y,Æ)Ê'P]µsL§ìq[tôž‚õæ³~Ù™œ)ð2÷¾¿ôçÅúMwðeüÓ*¶ub›)ÕFA~ˆ®bPµÆ´ šê4{ÜŽ!"JpW)æ u
+9|M's‚;‚aœ§ðn+‹¶_¬26CÏ“ÿ—íÁʲ‰NwŒär¾rFA3‹†3YUáYÛ‹ (°¸UîÌøûÈËöóuÂK«õ
+¡Uô:mÙéˆ( µÏàóS;NZ)Nå‚ †­îµÖ³2íùiÒD%Û“6-H¥‰ðVp§Õ¶„•h@h*šlËôUÇ'hk%ž})Šú°V3ì——¦x@ãg²sÂè”bã¦%Šeèó DMhšƒPKqÑ$è¡À-ÉÏõÒ,Žö…ÎŽšTXƒK ROðŒ`y ›ÇR´<kd 8&…0ƾêBÄ©+
+>þEO X3UÙ!NJÉ?†Ráiy¿ó:¢(ÊŽa®ýF(›k”Ï6"=h_ç
+ú]]?^‘`9Ô¯#Øýóöõß·˜qáÒ&®®H "”Ðë€hgߊËÃ=×tÌy¢ í‚ç<# Úu H}­kƒÒQü[ÒÝ_·¡mL9³„l‚¿\R‰Sˆ*IHŠRÖ) Ä5ƒÀ…r>Ëu]!?ô%Ã
+›"¥ä#ôÉÊhDŸŠ6Þ,Š Eq1´ ô„ÍRC‚X#lDðFà8è<>n;ÜæJ»Mü÷n_Êìªì`ò±æA
+þÄÒ^þ tö¶"àjØwÈ«ÛÓs'±ƒ âIVAâêC•oäI nR°wðÁÝ1(,k×´ýê¬t¶GèÑøhh{ÏW—,Ê¡›úßÑY¦¨”åfÂVc°¡Ç7‹Ôv¹› Ù3†•ÕšÂ…µÉ¬?ŽN–2X6‰’xö±O‘æWñioȺ7FS€ñÌN×s
+..‹‡áõ ÂKUÒ¹M¼?ŽuÞ\x
+?Cž;õþv}êüÇ¿­•†Ï‡ŒäáÊ¢™ ?̧§7å5¼[®Jdyùs¡õ˜I¼ŒùåÐ9 þ€Ã©aG¶|‚—ëpŽa®œuœo#‹Ï#®´!×WiØEñXˆ’ÏÕ¹ì•jL8g6ì°z=ÈnÁÞé±^O7G#VK/X6Ãn1N5#°Ø_@WƒÀ;xÕœ-†_„<Ñå÷ã§^©DUŽ¸1,ÅFõ€ÔÔÅÊà­ŽEùÝD6I ºAäTqAaAF¡éâgR¬:†öæ­|]k‘Ôž­¶„«€¿`ìüY¼¢
+C¯SM¶cx¢X ÷+v<á¨=íCnÉŽ¢HAðeOç´Î1†¼Ùõ2ýÅXåÈèr|zE\¹†(¬'Ñé[©Á`BW#Ù×à—áØ 
+^Ñš\võˆ½¸\I„jn1î9sÎø/ç~iy—AêõÝäŽù•iㆮheÑ1 S?‡™ì½Þí"zÿ'žß¥±ÿèƒùµHiщÜI4€y1x¦³ä lvoäÑE?Š"”¯N1¡iS»­äÛ!¸ZY^p-Âo0^V
+6´àoÓý®…ÆÁG@_!â{%·,DQöyy
+dY™¨ßv_Bâè€h9ëã9ęέ…6¿MÅ
+p·ÙøTôks&bÚؼêôšÄC*[÷(`¨¼ò¤—@ØÒaÕÙ‡_Í l…«Š”HΑ]yS Mã©ìÀЕ€Pµc°H²¿ø€×ÏÑ ±î^ˆO{æð)ÏŽó1uÑ\YÍßùÖÁÿÕЬtÑ>ÜHðõÔ ¨Nç{ò§m*
+ÙžK¹£¼nÉÞãÊçAÉлC "?ȆLÝB^êŽ/Yî–“su˜ùÜ [nÖ %à­ÑÕA„=ƒfÖh‡©hF*QrÅɸœe‡SRúQ~·\Ëh‰®†æl€U$¥AÐèûo«‰EÆF’8ú¶$âc ‹XÚ¡h•`0ÉA@ÜJ*q%Ð5c0G:Åf¶²]²Çlä÷o‡"5¨ËÙ,-²Í /šÃ Äòó«EbÞØ” ›•DåM†›·§‚ùÂíDõ_ºôþà®;ŒYŽlÙìÅji¤–sø’&„õúÄ– .C¸Ø6ŸŽ) Ø¡äulÒeíù.³+f) fH›Në²/„K6è¯Ðù±
+àÓaí-MÁ/é!ÿ€\Oš²>¥G³t-ω/µâý×<« ïä ¾¤¼vPÓïßEbêÒ¥N‰ÔáÚÃÇ0¦t¾Ö'Yz¡ 8ŒiCwØï w³h\Ÿ~Ÿk ç|Äá6×^_åŸú ‘xÄ@.ItããŒAV½Àp{*ù„èŸ_Áòýá+,Êk‡'ý¹ÝõG}ƒ‡þÊQø ¶°¨Û£æýÑÉ ÔÞÊqÕŸ€a@þüTOâb3Ó§¶ºÞƒþ„7KêlÒ1‰ó'ˆFµË3b%E¡W#` P tÊö€y¤D 0ëwÑ à5:cM_,¹
+˯cQFöq€zÉya(±ÌÐ:çF¯(¸—„Îñ'Ù£&á0@äÅ„'¿zft‡‡M„+l%†cÅ+Ø69BTDåýc™øïfÇ”—.“% ÓƒÌi0ÎåÊ®%g.<ÄŠRØñ’ë„z8´Ô•FRÊ~ߨlƒÎ4w‰ Ù,©LíÌörPÎã§,D1CŒ˜Äjxÿ’(?$£ëPËc.›•4š‡•æè×wÿV¬U… 'cb…d‰<9QÄ°ÆEgù8å,ªÆÈþ.ÄŸ£$½V¢îD¦Íÿz D¤SàÂj%ð”2AN~+GÎ`ôîOGpÝJ¶¨NtÐ)ˤ'W–¡†?ê¢ÄJGø0ïO}*Ù«—Zœ]X9¹(ñ*Z÷eÀ¹l??³‚(c:K¾ŸÍ!Ҍʯ8DáMäÃî^âÙ"f™à“l¥—,Z;ñ¥ñ¡¤éñ9,{V6mYÑ0Ü¥¾• xò#¸Õý\Õ>RdzÝ°/pG
+
+BÚ$ rï$t
+ ÓØÒ-Æ:ÐVe
+Ó¡&¶K;ÇF.©Ø!°åÀ<ß
+H‰Œ—Mrd¹ „O ;Ôz"æ $¸œh/gëtØ+iéûûÔV_[Ñ µTY$  ËÞjM—ËV{IÕíñ÷[.V.MI\Ìr—
+ØL6•HÖˆ¬O‰Ïf5éBTm™†…cŸ//¹À›òÉR0Ò !¥X7™7¥)ÑÅR@Zâݽž’ü YW‰ÐTÎ+õöœä’xu»‰F‘q8ë–ëÝ«h¾Ðøª•rNΡŸS|€|)ÕÏï´rt}š]_»LS¥œ»Îû8"tV‘vout} ¤®ÉÕW–JãºÎç2eSG®[iIÊA€Vä–ykFnËQH5Õ赞uã¥î'ý¡Ss&÷ ¿ÑŸz•äI[b~é1–zY×άðÚO’:nª—s âHÔsbŠ¯¦¿ä&½Ïç2ýx;ƒ¾”ýýÊÁ8ùNnâCÈ¡<ðNS»ÃÓ\¹dL‚¹1töÓ¿fòí©rÞ–§/u
+9B½I¾é€ˆŸ‰ÑDËÉø(‘òŠ¥…°jK;C«¦{ûbð…~ÁÝA¡Ê¥Uú›•áÔ8œ¾E÷˜óǧqN©aLÈ9A@zˆ¦¡yÑÚ0ŒŽ1/ê (¯õ]éz½‡¦éëEïß©è§k…²¸ÄŽ½ÍTvz\"uTAôGñ_AðÏ¡<.9$ç1 ^âSv£¶¯ìÝÂÃðFŸ"F@S"³KùÀ'Ρ†õ 
+0»SERoKû:c,|+ÃÌR†y"¤AH*†ÃÂÔ.‹¶j”‚űîÊŒ«*‰ÌØÝÞÓ4 ̽R+CÊWÉÂV²¡£½.á°"0elÑÍÐx@v,&ëÄ×'”ŒQ?ªöŠµ&ÏPw¸•aKuR¶ý¥¥:Å—pÕóUÎÚÃÃù·ì¦Æ!\Ê>°ŸN—Õ˜ yƒ2ÅIÑô=SÈÀ¤é!jÞæ„ÏBèLËJr|ƒré^cÿ±Ê ·8§ö%‡S¾üÊsãŸôÜ€‡Ô<·ñ!Á:|)ÔÏïpæÓ”ÓÛh²–ç}œ@ÃÙ³Öç‹:B_:š Û)"æ¤#9³b@bødÌ'u;›É‘$ÌT³Äí'<ÊÑèp|v:™ßSÙ_ýñh0äÒRv>‡^®l¹Ä\†ŽXB±m '¤)å#`$q½)[’˜Æ=Û¼* iø2%lå¦ðÿŽ°Ü>„qŠŽ&®¼îŠ±òqån(1N‰B!\ŠÌ„ÔM@_
+ÿ~!ódé×(ó¼Œí5 Ú¶AÆçRÿ|¹Å®ƒLV2Ò…7‚J²“ "œàíËŽºA$˜; ’d¬R¯ídkRùlGW`í<crìä¡gkIæ"ï±ÐénÑ Jîi›uÜ©ã¢k)¿ZTÂJ‘x­;7%UK$‚f,¾°nT¶A[¼"®((ÔÊãÌ;v°I@ªOÿžœcLj´Àt‡ù:“?[-qø^Ò¾ï}‚HÍ%™ÉmÀ2à} Ô+k0Y)¼AAåõŽ}~´vbÆIžÏ)#ÍDCÓMD'ÜKÇ2Ô…hŠ™«Ä´xÕÃxäØ1rŸŒxÝÍ"ðÏØFRý.(®ƒÀ•
+ÞÊ$¢5(6¹T°v,
+–¤y =¡ì_ ·?!E(Ro)ë„H0¿Àâ=õ¿ÌÙoбⱠáHÙIìwA¯n÷ÿðsn¹½Am )e[bc…ŠQ+ÂXsO ø›ðýè{Œ’¡ÙP fËRwB¦ãB¤¨ò€DÃùX`úrˬ~<!‡ñÿ9oBÿ%­Í†>ßÓA›êRŠ\PRŸê3„/µ<j¨Ø-üéÂW• Ð_ôªOm„4(Ê‹‰›ç$œ^WÔ—WSA¦¤I@ìBµÍ0OV4h;½µ÷2¯²+:Ú0Rt j˜…ZðÔb@H.fO“¡Upµ¼@C¼Çð´.2!9È©LG+¶¯âóäµ"E¾½» ²ÛLužáÉ^‰-[Ub‡m.L»H…x=æ*ûóªg›©‘¦r£AÂYåEâèÙ–=ÌxY Äz!SD¹ìŽ ÃÀå’w­ÂIp?ÔßžHÏÜç± H¼QœË¢/Ú.¶S= »qhÈ“€<ŸÓ.Ú‘~‹TÈŽ†h•«ùeB*Vœ‘V ßzÉ
+ÛÄQû&*‰#cÔúNN˜Yò@‡-Võ˜îp¸ÿò·åƒÍ¯Ôra†AøÓGƒF}qÎèÿô~¢c؆0[i
+CÁ©ÑN+žÏæ(¯+dtÑC•aÄYq„CaS„ë{††`e’ö¼ŠEŠi~ä+÷Æt(çËž#xüWèÅÍ)¥q32¸B‰IP™ÂÏ ªy‘RÕZâåû¦lBïC³M˜·TÜŠ>áIÑíº¨‰u ¾ 2En 54{.äó¦g»$f‰©µ­sXUH{¨ø¢xõÈ-ûÙ/›IŽ?Á¼CŸu(ä“™y4¤“¼†~Áyu½¾?VU¯4I–z.siô.;=•E™Áà©‘%c¨Ì|ûÒ÷g_q×Hè7Ížù”̪iô(gÈ»à xðS•(¥u?†è|UÇsÑ¡L–yj±™Ã£ûïAtŒ®y=wGA »½Ž“Î;Ep‰ _Ó Qg3bÁKçÍDPmLâƒïÂ5IÒ{KpaP9
+<Uîóƒ?¬[˜µ¹÷Ä°=„‡ö¥Ã¬ Ô$Å¸Ò ˆ˜«¡•˜¶'}h·’°¥gRŒULÌÉsÍ@õDÊ]ïs<-)Gƒ‚¨Ì [)PÕ{NbšYÑ’»‘&—™²:î×òý`]7•½L<€dqËñƳ_Z Yc—øœÆ<fL0EG¹{ßbí7ö¥õ€Ø0ÁSO¹û TæàÔþmhÚ†Fç0Ïp¸o|&yë£búŠã(N_‚ëž tôQê„Y>ýÄ[Ny$bš†é‡A¨èë âݬ£ÌÒq€Lº5~ªçµÃë j˜¬qžc«gqÃÓ)kÜ:ä›ÇœÇ g´‡âˆ9¹o$W|QËIO2á•n—æ~íˆbwº³ƒáæBXÉõ|9_5f.g’Ÿc‘‘ÃNtïðÌâ†É™çX`ŒHµN8sbDàó*¾Uî-j¦œ¾Q¬ÓyHˆÏ\øzÞg3;†žm]¡\ Þ¹ÕïS+¶QÒ‚ÍÓ¶Î}0ñÁÜ1bS€°gÌš°Ž HãfÒ¢¢K'jº²½.ÔhœVDZQ2Ô9Ó¦q7Ö:ÞÄ…š¢×½œâD&”d=Îaê7LrÌ^tÁ.U2c9NHÆrà›RI-€ì¯ªÛÜ­A±©‚þ…ÏTÊŒƒ©¶zÙtcFëUR©å¢5Ó“Äè5Íé1Áñ®N÷-“64Ô–Ûz‹C!—n®³q/RbàŠ3;xÛ7òVj¸¬©aBr ùͲ[/.¸^•éäìWnŽkš5˜ ù|R¾‡ñÐ|`ÁÕCŒƒ‹À4c|€æryW¨?î›iíøò™ƒÍõ—³ž“£é±¡ë­­»kð²÷¯‰ç a÷Ÿ·tû×[â…:z»áhO\¸ýˆ“Ñý³¢Õ~÷÷·|ûñ–o?ßl]¤ápÙì8ÚøûMnÿxCˆXµ§¶K¶Õ.W“©Á‹…5bçAdl¶ðkIû渂¾D %IÂ÷6[g ä_·‚¾~$¦/o¿î$”ˆ8ûFDÓÃb²¬emp27ËlJ¨:F¯ª]Wt‡Y`Î
+ùš"|¹ÁJ´Fã!KVû« ÉÄ7ÿÛlÙrÑ0à( Þ‡ðldå öª•èœ…ã š‡µ2‰ý ¡
+nèBÅ~Æ\0
+
+8^£ *µfV|e':gá8ˆæa­>C^ òT òI°m‡Vaýb_É÷ Çq§Ïdö*5]Ì
+®$*Í*cÆg«bõbe#õcL¸`<$(ø
+
+(^£
+µf|!'8fe8æa¥>C^íñDíñ9˜}Ó†¸mC¯<¡9Šg#ïlíWI-,[¬iÖ©¥G=aÔ̼R³¶ÚqYñ9ô¶¤liëˆ î!AÁWPÀñMP©5«°à+;Ñ9 ÇQ4jõ‚ðj§jÇ’ ¥n­vaýÈÑBõKZ9Ö7³L¢a5³Ú’“µ't,_
+^íñDíñX¦Ó(eÐ5jwÙoôŠE)[½nHPæÅ´On7 t»J•švùêcÓÔ¨Rš³fâmæ¹øžOf®Ô4mÛ
+qÁ5÷­‹”Öpc%lÞº%ÌUÏ» ƒñ_ð
+kÑ ²&5Çò@‚r{cØGãêä’
+ªí¨ñ§8~},êôbðjŽ§iŽS
+hŽò¡öˆÅ¢lÈŸ`‡*ŒòV¦¾k¡ŽÁG?
+Åûú‘À¿ì·çoÿ}ûî7îÅÒL–­Bëè‚´:jÝê,<Ç&9ë‘t€‘>QföÍÜäv@èš&CçÎ Ö$VsªmœY[!~ž; Œ$)%÷Ö貟ŽWÙf™+ìíû¥úÀÅ2MRîÇ9r ªD{¬']Ö&fWOc*‹ ¡‚áåÛ.½VZfç6K¥7F©äKiôålÄÓóI¾ƒzÎiT=/?78u’½œ"Ç!q@23žžù<¥ˆ`„'ïjߘ™•ÉËåÈr!ÛEéäÍ/vÄ(I[Gìò!O-3}ƒh/³¤\‡ˆ{ š’¨´^/q QJ¡ÜY½€ü¹¯EÿáP¾>3¿ëÿg¼Ì‘ì8’ zÜ2…o¹/â(Bå hezĹÿ<ψl³_•4H‚½øÏŒŒÅÝ£äì%= Tø‡&ø¯FžFŠd×A¸b3Æ¢x-tø%Wª+éÉÕB
+ä½k
+›C:V…¾ÒÚÑ6S(”½§Oo@=|„LÓ »ªçÄÆÃ¥=|žŠÒÊó ’X:”8!Ä}Õ”ؤðhq²º¥j猘ÃHL6Û=
+—ä´>RiæÍÝ ~‡$ÈEÿlH¶‰cÄb ƒ@FµP´ã'~#Ù{ì>yŠŽ(Ë”]!»OK )7Z»ÃÈÀç
+´ÃÆ*)aû+ËÂÚ™|DN(Éjî¿mô¨[ÒäøE£Ò„Ô<Œ`Oä‹ 5to¬˜‘Ÿ–+ò« æ?ª•6O\1'¢`–G‘6Œy
+63÷Ën¤ÇÉ÷ ’œFsíJ† ‚ –ËÒn‹ç¦CÐÇWÄJ^kÁ
+ý…Š@Ò«3A*«µaúÖ´Ç)SÆIýt r2Dq-ŠŽø–ºãˆ@iu5Ôûý×1yèÔæÅØCòJgÅr²=áÐHçpL·A/|–-¡¨UHŒR’eQ]Az>¨÷Ôq›9sÒñtSšvÒÀËuÙÌ¢nYŒïÚØÏ Å„íæ ¤ T¤¹xPft­$××O«3iôNÎôô#ׇõcÈèš‡× ½Ñ(JTMç<é$$´Ë0©GФ˜T*"íi+̯k÷Ù—Ð; ^
+IÉ­£ôˆ,MF=ä-íªv¦k!ev4?'‰häP­ú¨£8¼£…É^…â â3/ö^Ô'Ÿ7
+ï4Ú]B> „¥%[aÜÞ_(iLL2RîwZ¬2Sœfw-Ï…*kUUĤ«ãz°=@à`U‰ ßI¾è¼òw±’”ôP
+O2ÓÊÎÁÖÜC\ZGuÖ‘%מ~k èÜ©m¹ %ÊPËÃ`|t¿î6¬ÐyðoÓóïoüõ-
+;ÏY„›ž¸®Mñàs©LšÅ
+!bFRä1"î'ò0aA¸|’/Fb »ŒáâÆιµÑ°°ñ/Væü}wýXù§ÝG8…eeÔ"îb^™óŽIöFLXx²ƒxaeÛyRdÉñ6“™ S©ÎᲦÛc,zC.¿
+–¢`l¤l¿?q&ý°dË*?¤ !”ü*–6:˜Õbì±üÃ"VŽ¥ˆ™(aV´m_uAva°sØe©¡ÉâÞ>‘£ÄˆÕäˆBÑ€MþÎ hSƒ¸qˆß„a [Nðüt2ÊÁOC=2 ¹ä(z3
+ä3âæ9r @Ü율sPKfÔÇ´=ßY7;Å9Ðêîã7’;CÞÀoºhÚ†x‘ÉTýQ‹'éÐ}(A'fX¡$q+ë
+©˜(å ´EQ/ë62AØ-Έ³ÞYɲâÞav¸ ãþ\·à»œ4æÍAY—aRÜ®™X©4n4„>•o‚Hb9B(i$É%‹°ãçUWPïLQ††óø?ãe’+émáôÞZ‹çai´¯à4¼rkéûû %™¿õÔ€¤®ŠâÌŒaÝ
+ÓÊþ4U]­Q°0ÞHëâì=ÂÃì@ðNò=<Ö²˽,A{gåŠ:@¿¿39ï#§QŽ@k
+–pzÝ9¢-™%µ1`L4ƒ3œfðÞ
+åH™ê¬; HOw˜þÙ[}ÖÁ·EßùûÞê³Â‚@Œ¼¹Ï/Mý|©²È§yWÉ<T¶?­C!pŹ–ÌÓHT#_ë’n(ctô–æ¨ëšXÖæôÛµÞ#L]§™ˆ"ÐH¯â~( r<÷u”»i®+YÍw•ænâ«À÷(üíHý|0àj7”„"öÆ5Ìì EiÁÚúWò̹™m'РŒ¯Î4†}!T$W†=N|0ÊDj*%ÎÃc0‰.¸D™*Ûìi•H–tz¯£‰p—ļèjlm"x5x¸H@–\¹ Øš¼TòaZPwÚŒ¡žËT½¢†pxI òêN?Ìå ÒÙ$óŒ1×½Ó‘P#h8eê´–i”?w^»Ä á.”.Öø4ºÍA>ø+\u˜˜Ÿ?––Â
+‹0¬í ZšÌƒPøüfov
+œqä bÜÜÀœ¼s?Ãu)ë9¹CRüêX‹fw†…0”Ùªê­ZÈ7¥ºÒ›ùõFVÏ»ÝóÇzôBãô¤Û‹B”…d› Wh@pþU^°äµ‘&*ŸP‚² bîÖ†²æ©°!)B~›­\n5=G±¬¯eX¤ÐZ“S›ë/ˆ\ªS½íii!:°¥ érq®ãÁ3ÌjÑÕˆ:¿“çºnÅ‹6¨DÁv64ôà\OD³ƒ Þ†ü‰Ín-?Rž×¸Ñ5Žxk{©´rHE8ð`­@Ú F úùZ”ÝùJï8jšÑdT=êâ)Tây°\ëµ2¤ê">µ5…(ZÃY0 3¨fVf(©)oºJH·1/ÊÑ„0„ç5†Þï­.PurÒ4S{oEÙ Íô×lpGÍ7®­ŽW¬„z
+¸ IÀ êÕqsëZ0œ8/†Ù:l†cnx­5ÁR.8:ÖÁ–›á ÐÉ”œY/LƒÏ|Ë­š\²Ë)<ѶÙ höèMÿüŒoÉt^Y×z2U:—š“ùõÅzÊ€p¿À;»Õ=dÀde@ CZ–atáé\¾KA<SêC¤w%7ôóeæ;—]°×œp BÀanp›µ{;R ‡òËÖxR““Ú› –»–².U†É­ù‘¨ÌkM!![PˆŒM‚N²|õRÄÌ€æ:GÐ9sç%ãNwî*ƒýs,‘›PVפZ+b¼”:^ü>ÍÙ6÷¥Œî»ksuñ]ákþv¦B "Âîoxä!²–LI
+¯õÁ} tlgÆÀ´X_†)ʲÓ£"ô¨qÔõÐÉm‡Å¢¾‡Ñ4€ÒKJJ=³\à;Ãœ 4š„$¢*Ë:Š"u¨îó„F¸4ŸCŸŸÞßûIv KCüÀ‡UZdËÎq·dÇø"¨ñâ<¿,Ÿ]Êo ‰¾ùbý Âc¹/^x<p˜ZdÖHfŸòøXƒU€(ÅïhD¶Í~zI‚ÑlF-e‚ÔÏ–ô`öå‰t<™Ôd­%bï]ˆkÔ¸$šÌßË
+]Xž±tÖ&4RŸ?'¦å ¿Òfj)RqÌ1¥î5©Ur|¡Ï4®,ë€à»œØ3Ëš÷ ‰•Xš[š~é„è²€ø ‰Š7ö: ¡w]”[û>q¦òï†õX¦©FŠbœŽÀ9$TSrƒ*T„Í,Ëy
+µ„ïãÛ ¶ãA ”–q³0Uy2Æg†Y Â-ù ;´EC%@i"ð£¨;Á®OD¹Ú±u~U017 vŽ~•G> „ô
+ÖK4‡
+wJºÊ£”ˆÕ…¨óz,žšîePR6 k+í[ËÃ2Lzo:~\}hžåÚà÷¸ü\ã:- #ëA]SýçÐWZ¹ Ÿ¥ÊàÅ+[Ѻa®Si[ü0þbmUŠÂCã—ÝéôP©ŽéšMq¬÷2ŸR‡h}•ò˜A2ŽqcŽÍÐïo è G™©g²šú-‡y¦\¹=B$öçÄtÜ/4F3´f©Q÷Hv0EøqÚrBÈæaÔ×@$†
+:—ýóÁ<GÔ|æÈQ'U€/x ?èMW‚?ã:°Gy‚,<w]äføɲÎK< ¹D]çƒyèÎ9”)°@¿ˆQ„óãôv€“pª&gnV*Å“ ©õéê|Ä‘“Ÿ ÇÛØ)ç÷Õ1|.ÉêfŒ_8“ØV‘³ò„6TUnÏA¨0vº^ó€üšÏIŽ¨t'¶@âÙÄ‘ 8ò<Œeî‘»¡¼©¿T„D›â»'DzOA%¤s
+ÊÄ¥p@k
+œ«ÂHÎÍ„”Y­Š :Þi¼§
+<®oD‹>GâÆä—&{†ƒÎùkŽ5¥`På¼¹. 6lÝБUdÙ‰ÊG¢à)}ˆ^†ÇS×Ò;ÐqD,˜¬Ú’u½§%}>d;éùÖa"È2N¨ÿ:ã•’Þ¥.¹G¾¨¯'ÏÌOKÚÄ·½ç³Ð8±ÖêƒF\#¡²&›ë×5ŸÇè}ÊÞÏc† iÔ­Åž•1ó
+Ó¦­'F‘\œ4ïõu@tˆŽpècA=,A±5†(F"„¦9ÍÃ!56ÕœÅËÌ
+æj>ÌM pèJFPdžÓÜá!øˆˆë*’¿IÝœûñ76ùÃ¥LAÊ™ íàï±um ÔbF­þ’+(‘ÖÙÇɃQ*ÌJX´þ]óþG9ÿújMv¢ËÀŽÙc–“¿
+ø2•ðŠ¬BÎu.3onø¼Äi§þZt”4
+!`ÝƤÞKÑ»w'µÂ@¯Ê¦è(1Œ‘˜¾—uŽ|äÇÒ,ÉA
+dJ]PJDÙdÝêj‰¡VËC‘訡wÜ%°yÃJr_=‘V麰JûhÛÙÓN€”;¯ºAÛ wÂ2ú9Ì.௹ÞÊ‹6.N' ‡Bå–“Zí#7ƒ|:]m]%b~Øß² !±?JAÞœÎßYBïŸð÷3Ž¥]aB1gjmŒ›5ûpt‰
+Úž8vº÷û9°DO$Þºz¨F²Y óIö&ÑÀ¡„X|x €Z:sZ5<W•Í1Wd½[Ñ*ÊEù9Ø"fw-1÷°Àì¸,ÿ¾„E0Dƒc<¯ú^TøƒQÄG×<Ïô¹¨G¾{ð'»ÈÚz8^€6
+ÉÑüøA5ƒßU+d±¿R¯]L­ ¯òììBr’Ã7Âu›X@ËîkÜc«±#»´0ÉKF2Á!ۦįB¯«:}Z‘Û1ß ƒŸaù˜Î×*A1µáVÖ³ïáÆ_—Ì1aÁ
+6Î.Ûp¸+¹;L«2V­Ž]ÑûCÑuœ7¸ËÌ)bðs6/áÿ3¥QWoŠÒ2¶­ÙÑY ½q3L…?EgB…,“&†dÝÒû ªMÂBϳÌè=œ̽
+K.´¡-mñ߇õM}yž‹y¿9”]Îr•4ã!K°Ó1nÚ[%¸J
+¹ÿ*~Äxókìæüôøv4zÈ-¥Í§%Ð a
+š:¥€Ñ7îçc–T].öleSâWÁÄÄ!<žCa©ÐlÁC—½Ä÷/Á [ÔN±²ö€jÍéîV„+Ä~ñND.O(ê.1›<§¹¹¤½06n¼¤ßþň}–A_ŒxI¼<á&ÀŽ}Sâ/ï¼
+™‡e¬#Ô0Àr£,M²°çòÛ8všpëýœ+6.nt1…ß7¨™ÃÛºéUlŒeÔ¾eÀ#ô™
+·¢ÝÀ«ºç†sx¹èbR,†
+¤À+âB•yÀD|EŸL€´Á<w7‹£dô&¸ Û«ŠXÕòÉÊ×sø@D$‹(óˆ%Îáš'
+mVI›¢wïi‡^ÃU»¢£„Ó Üº.“AÀwÃÒi–yôÐÿÜA³÷œaѽ!50 ÆM3Â&࣠»<ADü`ºÍ’Ž1‹âp[‚mn0,AK„r^u-âV óɶ®2™Ç’òÊ:¹bTC=îš%XÁi¾“­Äñ Vd]…T.àtf¦T”-ÖXÍIO°ºÎ¹#ðg ÿ9ÃSÅ`ªÓúÎ÷~8>0¡%…Šo#>¹¶¨xiŽ2–ŒKí$ƆÜÉ‚ðiÊÐßy´À¥Eß⣤‹<{ÜÞÃn‘i+v©mðøÈœ3’…úq+ø¼1¿;‡8
+»û€ÁËðì
+CL–dñ¹¥^S É[°À,ºMU®pq±¢Å±Ü*Ò—S?„ƒžlÜòã2QV‰¢óƒUG¿â4÷¢âúû+
+üÁ fºQ'1æ?“þqÒÿìTƒxë8ÓAþ‘DfUÆ8Èé-&L2a ‘JNÂϸ
+ojâ•àobŒcŒé3mÈÕ¶—™™`Kž#29-O»ì*¬Q¼$IJ/æ& ñ¿ØŸû~È ê˜hê2E¿žŠ<œÿ®ýØYæŸðXÀ°©.Í-˜¥­[”
+‹ uY¬µïÏAš ô„ªnL[5c>³šªH†ý+í¼Î… ñvym„^2
+J CëÓ:27¨“rò™Ãï}x;ðÏc³)Eç!ÞÁ¡¼hM¬V¿:@àtØÂkîìrvu‘%ÿÛ U0ºœx^‡<F8¹HDœ¦Êr¢í­)éÊ4 } Ž2Œå~ÁÏ·"¡ XºéWgjÊ•#¬øV »I¶UÎq`0_,“;ûCÉG”þüvª9 }\ÄfqÝõÕšÓøÝ6ûbÑ'J8×[‘Ó ~»Éaà —CZÞlTÌtÙôr(J²…*]êC .¹WY•f¬éàqÚ½Kˆ%ØÿA
+½j‰­ŒÌÂ%ÉžÁ" r›Än¯3/RV?_‰–…`é×|(¹;>ŽŒvVî,§l÷û¡CZå·Fï>¼´’ÏRñfò*«Ã.Å+˜íe׉ÂêBÌn¿néOtù¸åUebnÍ1R0Ie"Ÿ¾ÆD9úfö[;ßJ8CžäÚã2<¹bÅ+ƤsQ7«ÝH‚;54›À(PÆF­ Ú‹ª¼FšçŠHãü
+:œv%7UC@7ä– (±–8O+<úk§[‘Ø—™Wé¾éþ*«ú‰Å²œ®‹¼h’/nn<—^A¹T˜ z$VZ“Í)4É1€êÔ‚´† ¥#në\aoæžMþÂPpgÚÍv£€¬UgÆϘ(¸v=y)^DžZõ¹ëÃQaꜬùÏÔÚ¸±]•Ñ[9 /²×Ä"R¯¹•Ðªe®AX
+ã)ˆ2Ùa<eÐVË~šË 4„¥c šÙù‚{lu+’übŽ/è[!\,JŠ
+ ƒÔç ãØüÞ¨0n.# n…ÆÄA
+ý­+‘¤‰¸ù$ ˘5,ÇnÙKÖ0Ã… ¹±§I¸L\&7é%ŸŒêOùÙïÿhÊgfõóž¾å&„ˆS¯íioEÒ° ²ÿ}—€A¾-çR“¿1¨‘wÉLUNΈ¨Nþš¹;j?@%ó°ý˜¬îâɨÉ0ÔØ÷:xFFF=BÛànY’]B¬£[ø¬a‡ߊ=xç!#{^§p+n.%i2®¦ á»ê«ÀÓ/½Âéq­ZÔknnEÌ©“\R݃sÛìžÁtdx§±æ<⛣M<­ªhÖ¢Ò„àÛ*o]¸"$‰È€©c75Ã¥AÒÄ”Ub§7Tˆ
+de¸´1ßÄ-<Y Ò_q(|±ÌõŸcTÇß›8½ØŒÉ8qP<ûˆ†a´å‰Ôí]RÄEY±ÂEIùAš”y‰ào^5mÝŒg×g»!H[JFœ;IÎU3÷(ÁI·ü‘p‹€/µó2 °ixDCÎD)•âUÊGÈs)Fñ¥
+ÒC*0Dí:§§ÿkÞ%#°5†¯6G£!ù·ñ¦¨ ßb‡*ËÌ6Š+xj£0ö ?YÚŠ¼þÁ7ÏPt±@¸œ?bÖSøaÈv“eN§œµ¹Žl ^Z7;ŸÃjdžýb^%L;ì>­_…D6¸ai{ÅžV"ãÚ÷«žaŒÊ©"†q.‘U¦«=^õ]"eÆ£PGÄ¢…/Ægêi-¡8íÆåˆ, ´—ë˜ä‰=J…[gò¥T ®¯+p†`ÊœJæµ»@¢#ÓÔ˜®Î Óh3T7ñòk¬BRfÐ,l
+„³Œ©³™ )Áåá?éXv‚p)_eñø?¤É_¬ÊÜ„‰À×¢$ø숭ÆUñaØ¡¹²ÅóR„ $Å­$ºf…åd$h[xˆ~ÄHv³¬ä¾Ó8”¬YÑ-t“áØÅ9lNŽBIÏ{½äŽ›¸­{``'ˆ‰¢qùÀXãDQ•Ÿ¯SÍ©=F6‰Ž6}ZÓ¦Ê!&Ìh´]u“ášv¨¹G—ÕU¾÷AQÿïô»Šô¬6ËÕ
+1Æèzû§ErƃoòiÞÁCMS8 i©ÏÐiŠ,'ÛJÜYÖ¸³¼t ;—½$!ÃbÐ0Ö°²0%YåÝBYE踤¸,6Q
+
+uäChò¸pÏ m'»Ê8æó9쌡“öv D‰´ f.¥¤©†(µüõzS5ÎÒÄÕ‚˜”¼#<~ ¿ÐŸ [&Íæj@%%/‘lü 0
+H‰•wSùÆÿÌd2™ÉäÏä|7¹ËÅßårä\&€qlŠ1ÆTêeÕV½a!ûÙ±“›øð70EIhµZ5$šÁ4¡•´}%™ä^HÖoà÷ýÍS>ORÓ[Y¥tT∢㠬î8 ™GÞÌ7è¤oòëNÇÏ[&
+uëXà¡pmw^TŸzÒõÇí§ÊV6õ0p0cì|7-=“ŸSþƒYXËcÆa
+LÂc§ã^2>fÆc.m9l$Ã6! Ù$DÌ,Ú jÚ—ïõÈÏ™ø$äÖ—#æ4
+t£AY Õݨ"cN6鶖b¦á½MËûu³JæÁn,hâá!]Õ”#@›%DÒcÝx©¨O?|¾3­¾°1\Ø›.–†ž£yuóጲñhNÓRúpÈx#;-¬Û}´1ßÏ&Ç\dÒ8Œ†eXÛKÅõ×)Ø4Ì&]&&îÒ‘1·–€¼ÖZÚiü3Œb1y+×—"Ê,
+t‘°i¤´ò A ÿpÁØ“ŸÓµÚ+ù9IÃþË¡¯ó¯¥g‰ iàý«ÑºŠsGoõ—ß¿Ò5£¯Í[S'Wî]üÍ꿺>Á–A>¹M â±î€öbÈ.©¥|÷*œ‡Kâó[Ó]Ÿî¼¹~24æƒÀ•R$ãV¶b°ˆËÌ$=ÎjúæM6ã´PIPL%¬r<R°UR‰;ÔNË*tWÒòv5æº>ÇCª*f“oÏ
+¿+ÌKk§ùxÍí>沂Áz^9jÆa§‡œ*‡±˜¾Ÿ@Ìb*ã
+MŽVÜ
+ëº8?ÔU.lÃàÀG¶T3fy•»ÍfÍ*:e–“ˆaä´¦c~"ŸŸÓNAÅÍb2¥å¯ÛÍÕ(eWu‚ý üÒþ[C×qÚÿxÃá:^×
+hè4ª+,‰Î–#Šf1ðé„EƦ ›s ÿm25ùˆLúïPÈMG™Ëi1¦íFcº^<e3Y›ŽYu€LÎ{›H{ýdÒi`ÒGmÍ3v¼áñÕÖ–êºÃÆf¹Î¦¬*šË2“ðßb’·1«÷g+ÙñûÕu—“]³)NS®<
+1‰hÄ®%ãvu51q¿Âu€€ìJ,jèç¸ÔP^Q]Áa]!¬n#[1jÂÚN:åг«+9ßÃê†Ç_Ût³kf€Èê™ ›…Zsر¤EI§'³™»Ï·¼˜¬ÓZä¶ãnÕ²ãªÙ›“—âã_W<¦àX9DBF~)¤¸„†T­eHs Côœc•¾õozõÎtñØ
++61ƒø&þ›öNü¼æö!MoâÇž8tëÆù‡C YDdam14Ò°ýºïOÛ3¼/w8v¿[ðˆSÊóÏħKA}76ðð°e Øwæ´­¹Ã_oLñ¿¨¤mÀû`ì¥`ðñ]R¶cQYK1<|fëyï§ïž‹þZ^¶Ž ‡a÷ò|)d¸^ sÌáôÝŸŸÛ|&üKüÑõÏ(ÏübÉÙw[tŽæuWÑ9y#:/m,Ômh@ÝšŸ“þ«;‹!y[yè¡“v
+—O*e’à°ú*ú?ÇeþÔÆyÆñ¿"mÓv&3i3u2ncìÚéØi|Ù\‰KHHÚÕ ÒJ«ƒÛIðQg§M° ˜‚Œ@×ê`À\ iµ«½vþ‘>ê;š‘VÒ»ïû<ßÏçé¾¼9#þKf½#$]TB×ËÆô²rTÛ ü—q„VL…T"2¨ª-Å4÷ø´¾» 9ÉÅ'ŸÒ‘ñï©ÈÈ0Á `RÖ§¾u@ꎡWŽüŠX£üô î¢!_+}pº;ËilKÂzÒ¸MX³;ár”¢U1~ÃÙøè·Lzü“
+µ¨I8ã"ì;Mh[‹Aí½Rƒ÷ íù ÚÈ'1—Æq.m·ó¸½ ù
+7H?ÚPðwÿsûåÝÖŸÕ~x°Øs%»b¿õ˜ÅÞ‡m"´)® uæ½h}Σ­;˜×~³5£¼œúIöÙÁ¬êúIÊ†í¹‘[nm-ìÜžë<Ÿq·Í.µ~–óJª$UEo¥†lh<+ñƒìoŒß9T‚õCHÓÆtãGñ5üŽ¸ßðAx¬é÷ÙŸµà;hcäQýÇ»ÓÒ '„±› ÚiXçþœôó-Ÿ¼}Þügà@uå²n¤† õò±¡.jê)
+žÎZœ …°®5ž¹9ßq6YRN›TB3ñÉA%A[騶IÚdÔ‚2qÜÊ'‡'™¸ÍÈÆLr.>Ÿ° ÓCtÔØU~c6òëCF.eÑñ)lŠê»Š„¸n5±àŠ¿F ifÀys~¤>ëQVçýÈÝb%“#V´
+ÎãFÞ¯o¡‚Æz§ð?ŽËô©‰<ã/öínmm•ï¶ÊÚCgg«Ö™µfÑ©¯Gt‘Q„„
+ O2ñLØ8QŠ#8AÚ«ìÉyÞRÄ8ûQUð`¢‚[‡VÀ*1TÂDÄ,&,j¯¬JºË«¨ü8NŒÃ>Õ»€h §uÀK†ÑãUœ(GPQ1 W—Ž×ô#Åa¦kÜ\]ðàrƇöÃ=·g¡àAœ áš"¼‡ße½qÞ«SQAóÄÖ"ÊÚ™ݤ}òž£U5r }øySc.¯"â|8$ Óæýìi`ì0RK+QTr\cµˆ/qUÚÍkL.².=jy98<Y
+èõi§´5ãÖ£ÊþjÜð¸
+ùX‰bhÁóx‰öŽ½d‚Qi‘,0+ wRZ3þ|¼ALTÖ0MÎ)åT°`ç^£=â¶
+œa%b™¬FG¦ËQÂR〬a,‰+!‘v¡âÝ%+ã² §ÉG\?/¤}ã3TSº›v®ZÑ*ö=&ËÚGø®NÃÕ²ìT:þäP²iÉx¸¢—%íhGÉ£¥€F ¹.`˜jÒÕ2iü˜Y”ÜÉ’„v罬!³} gSôáhÎ
+zNÁxäìÄtó™äBϵ¼WÒ±ùªï;ÚnPÂì!¥ &/tš‚W¥VÞĻ޺ÝÜ‹»³Cõ“½ç³óbpž¡–¬W_ö‰ÚÀC‹nkãÍÝ3ëÏïþ•vJXið˜—÷þ²7ÝöÏì÷‡2ÜyÕ‹ð(¨sû}ë7‰_¥lâ–)ö¹Ô ÿZÙ­’ œD!oƒ˜,ç’ÜÝží:Ÿ²ñn€‹JwgÊ•èxz
+föé5ðÑ8Z#µ]\j
+ìØ“¸~¬V²žÞë5¶eHŒrh…4©Cj½u´n‚Ü2Xè_{Òh=tªûòÛs ¼È[Ñ•¼ƒP”ؼS
+hŸ¬3åj.Æür\!dÂZ ´>M’rVÚ§–ÂÞÕ"àJµì *úr^%ØRV
+"ü©•&—ÐƧNJAƒ• ÆjTg<ZW¡Õ¸ÖÈ„•ÂœWµX&rópÖƒK‹aÂ\^3>.Ç­o‹ÀJ•5ÜXC>G4ÈÖ\oý8ï¿à3‘Ñ÷ù i¢4•¢˜²÷] ãšãˆJ
+ïzŸÞ¯s>à\9p=ZØqL¼ØX2Èv/ ¬7ÈTRʧ§ÝŠîà3ö—”OÖ•÷ËX9¿‚Cù1 åU¼æ‘ä²^º5'¾U¬GqÜ\]G…02wc‰ö.KZr.´wÏ)érµÝŸw¯‚ÿ„ fàlãÁ†²+C·×¦îžMÙø Û‹Ü3ŠN—GFi?Aä|Jn†TteHùýOK’¦Ä;nÝþœðºoªëÛ_”õ§ö§ùG¼ÖËÒôiÞ;ز7ßqÞ7qù÷ñgM¢â¶Ã塦×ÝuÌ
+Ú_ò¨sΡæܲàVr¾»nýeÓ™í©û_GÞw_}*¾t*6Ñ}ã84ü
+aB
+> Ž–qˆ[R+œ‹Éo üvøYëÙŠWÂ>ÙÄp&"ëè¤ý2öþ"÷ÊÞ,÷rÁ
+’ÞÁ.×HËéÝ—ª~Êýd‰öƒ8ä¬Z¦eÍähãiÒôãéÃýõ‰EáÍ¢ù Êf„½òp®~“qÉÙµ\ɺDw¶æ»¿?° ›Ø‡ËªÞŒüã3… õIÞo°æjÙáCxi»ôNÆŽqÓƒ:ñX’ÄDGQbì(FŒ}ÞÄ-0'’BXØšñô^÷d§*þÖ/‚x®½~[ŠŽ¼/F¬S¥¨q¼–%¥°„“%9—( ‘v©ùÔ
+.bÜÃÀl¦ÇÕ8n¡ís~a€­høÎùU¢Í9QÃê”èR™4›Êa•„׃ü伈 Zc›uLCƒŸ$ÝRÎdiÚÉ“¤²7é5åýˆ
+µrS+7GjðYÆ+cmÎu×µŸI΋;ª«Äh†ÔfH\òë¾ó€÷¬ Ý4©âÁÝœEëÿÀ¬T‡„óÉ.jȸ¤­à,÷³Ny{Þ…p²¬ßó¼§îµèêo/»oV"˜’òÕ|ItïÀÉ»¾7×ó]üyÛ¹ô¼ðvÙ§S^• ýAv»ê4j“‹rVüMÏ¿h¸•KÞÅÒάm¨Áñð§¿áíç~c#šÿ]ê¤<âÖœWÆ.ú4òjH§c‚œ«)[û×);çÂÞtëUr ±´*îùäì»F‘E æ™v£} ÝEŸZ”$Û3Üÿs\æÏMœwÿ:Ît¦3i§é”&Ò@&R%$Äø¾±­û–V«{%Y–0Ì1¦qÀl°1¶îkwu ¶‘-ií®7I¿êïo»û¾û¾Ïû<Ÿ§ƒ 8,x懀ü;Ðâ v"TЩ«l¡“Ô+ãD5`š
+­Œ®ë;ñ«£M»™#o=)…lêâªìlöDH9€{¾VÖj¦àŽýôTyž
+Zd,a3W"6U%æ0P1³¤ ^ óª8àêâÆô¥¥ë§~Ãl›U­ÌÜ&ˆø*g d†%ô×ëT#¤­Há?’ Ñ ¿ŽÞ¼ú—fná)C.®U“7”ã=°œ·•Çæ[Àg\R?^ j{™°iš[ä|Z3ÞȘtt tÆe¼Ë |?,½øö¥ô\iË8ØÄ]n&¡¹ÆFY+ë¹/¤0 n/¢Úhßí(;ß¼^8Š ¢üºì<ÇL\Ö»ÂçÀÛnŠ4««IÃÔa@Ý]\ýò`Ó4ÚöL.ã¾Aãž.kø¬sžÏØí¬Y>¡ØÝ@ûß¼Ðw×3v ½‹Ï{W„‚Ó ýEÅCö×3Vcn¿µzVŽ¿ó‹.ÕHïC6}/F‹k07Æe¤ÝTLÙLj²Ð\ƒõÓY‡ƒÉ¸nÔ2¾óYÏ=6eÃúd\Úêüïk«C 4ÐSíh+7¿ ™ìã!+«qãu:‰
+
+¨ºè˜][‰Zd{›º®Šß~ š„¼i¥TCuBråýF÷Ç»:÷îñð ¦ÍaýÕö±˜C×î)LÒ$H£´•Ö‰ê¤nª zpW@O{~uw-Šö·áýM_9 ½ønmèã½õ‘/˜€vì8ƒ¹˜ª‡ «º©0¡^õýµ´vù÷GÏGOÖ"ºki˜áêA6¬êg£†iÆïs1—¾UôW;ÿp¸5 wC1ÐH¤¶GÆô#Õ¨IÚzXÏc>!£e“³_3qÑ%.¡©“6¤žÅ<h:©ƒ
+™¦Ž6 Ô+ËLuçpE¿QPqý8ÄLmï‚ý‘WVv[9äTH|±²3u†žäH£¤–0I˜2I…Ô½\¢h³YÂàòò­F(h\= =R˧VËI·šôÝ«&înq„ït*y7Š„¤n‚
+k9ÐMº0.å¹#@ÖsÙÿëén«`s´2±@(úk¸äûVN5vœUÕpq×QTz¥µ…´¼• „E ÷MÃ%4clL5ÀF ô…¡ƒÀT“²9˜ìÜR…4+I›‘‚`ó3EŠ/ó¤fŒK®óiðû”ç¶sÌ1¸aŠMjG9\5@Eä]{ÛÓÿ|ï—^b³˜¯V@u<Ãg_±éû àÄ'ÀV>!ï¾ÝÈ{5@«Í‚k‘ÏÙ16c˜mí.Ø œÁÜñk°’IÛ*ZÐã×(ºÕ×$ç@+½5Òé9$
+&Aß .È¡›BÞê¨P“¼ ~Ú,ÎÿÐxíy,·Ð‚¬:.úžëCײ BÖf­·×’Ã<|U2¤z¤…>™¶[E úˆÕ¼¥¯ÆµC|Ênm
+8 åS݇õÞJëý§jAù`7)ظz ´9qú`íÚ'å øR)ð?’Ëô¹‰ûãÿI'Ówe¦Ìš6a†&! ¡!>0`Y¶%Y÷±·¤ÕaK> `HB ¸P<ƒ/IÖ¹ÚÕeٴƇ¬cµ—´’Ióô«é‹}£Ñüö·ßãyž2¶÷Üü-ƒ~¿Ò|)n!úvqS®±FÔ>TÝ4]ä¨ú4W» ;;nw víIÁ¦éæ‘ÉnµÈœe¸ž˜øº3÷‰ B_]ÅGê¯ImmÍÚ'fF¿h³fÈ&¸´pN,Rîýˆµ¯²JMH‰ùÇBÊ‹­L^¨,/)I¦²„«™°TÖF>9|}ë#î,¥¼D3NNJD'$-C"cûín‘@ºEO ÌÒHú¼ •àO>Ÿš¥<*çñµ²P‡jjdœÈcZµˆ#íòôS)\9Ú§æ(êý6Žž”óIÉelliή_?u¸~ó4ÔÿêQÔðm5‚éåÔìm6üC+ãu×¢ÈÍã(>v°î8Xý¬‘ö‡I۶䳳ODÈwÊŽ'̳š/{gˆÌ™È*÷8•Ã­„íšF í‚'Ø-“^¥hŸàYs¿\pLˆEó ˜Á°ÌÝ‹Šù{q´_)úçÕÐó“·¡—jÙ–ó"°˜Eþÿ>2ãÔvòtè×ÀC‘1JY»¦•7˘®ï¾;[Ù4}眣o7ôç› 6©CO•bø™Tð¥<î8‡¶ÉêŒyL-Ê9¯Oú€wü$Hò`mð´Ü ,5%‚K¬×{Ñ›K/ìF¸] ±ëÁá›Æ£&$Æ¥=¾tJÞ)‰ÁM½9lÁ9j‘¢»eß]9OQÕ¸î«Ãí_ ó
+ã¥`þ®6b¦«BÌ¥—þ95ºßÉú‚BÒ5úö_Ú3•5k¿  |:ÑŒS–zÌ‹óI7Ù²ŸëÖ10ÀADwþ`ÅtÎ?¬nøÚ1r¼l¿Úxm¾*lÁLì¿Ñ«­ŽŸm3Æ.Ì'ÆùMIÉÀ¬œ „ÈkRÚ5Ú„Ý#ú¯Ûyóðò_ýáñ÷?)17®rnRJ£úf?
+zòß2bWÝ¥jD÷Ÿ´)Ó!iøFI;µµ„yè1ñéï~qžû€ßÄÝmwèý¶Û~Žűñ£„íFaeüÓ£-ÌPe`¶"Îr<lÅ矨™àL=fº¼¿2úñÞÒЩÊêè'bš²í¯bÇoÐ~2F·Xèä2:Ì85bÆ¥áOA'ýõ4fªDìƒÐÀ6ë!•^¶ÌºÑ£¸éR5¥¿(ÁÌt¶Æv™t7`VøÌÔv)´¨n“T#e׸A-íœÓö[E!SŽ—oœÚYÆGŽ¡îÇQøH Pßšš®ÅîüÜHÜYŸ™:NQÖÝ(v{wÝÑ'q³ÏxÈFœÇ+pî`“ Ì‹l`FÎM÷ô^fÍ×+‘É Í4îT·§ —©Î¿«gŒ—k ÃÅf~òŠ˜#ÉVanEÝ^lýsm˜ÛÎîÔbk‡¤[pw>ë¥ëÚ-B¿dÖ©kl}®06ØÜÞÊ:´*‡ZÞ—0ô·2Au Þ›ÙzÜ|ùݺö¯bÊy[ÍÓAà TbݸÀï)Å©GJ1ôKî-r„ƒgp£”óÒ0ËDò ” ©vïàÿ•®«%qèÙO€…ªÀajÖ?­rþ°š÷ú»yoqè¤ï•AãÅÔÌýV1ø ½í¤p~²ªyÂ¥é0ŸÀá¸AÍÚ¹ð“v60£fÓrʃIÀBÂ5"¦l#BÂ2Ј/Êq?Í'æ~:ŽÒÎjœF*äx3ŽÚPs™C­Õ¤c¼ü¼gÉÚßM‡(iÌx´j¼p¸f¾,Ù3õ4Q- †x1øû÷1Ýß ÆäŒW!ZYËP#:þùÑêØÙâ“+üçÑÍÕM·X­ÀnHi/Õæ¦PAU&ô@Éx°&ܳ·öØ$Æ
+¾àÐÀÙ÷©ù§­¼Ç#g±IÈG·ß>=³5ûÝï“óƒ”RÚdzúêù´©lúFÓÆ탇)D³ŸrÝÜ[³õu ¶àQ%ƒêd†p¨ÜÞ›ÿý7æ {«–
+xfvQà¾ßh²ÁY!çq y©f#G›ÇÓè¤\
+dY!áv4¢ØŸÀ´|š²ŠY l6!§œ#à©ÃbÂã£^{#‚Ö¢ȶ˜A„Ì,1˜Q{ÚÌR.9G"궛!¹àvóÜØL¡ZX£“ö Ð«ÈPìÜu ô­ÍúCMп·/õgË‹š÷–4†ü|SJz\ÕfØcï«m†üµ ?ÒŒcÀ—NÍ0Àî«[Õ7m×Z[^¢›£ýÅ>X±Ýh&}Të—éWiÅÿñÌ·î±çœ9szæh;­3=(Êâ4( ûZ©¤R$daSDaº]Z=Ú®,„,•­R!1¬BI*©¤Ö´OÿóÄõ¥*©zë­çÞû»q§½ =LÙ,å2øyÕÂæîÍ9sÓÁ²­‡¶çÃhO)¨m.ÔWK°µÏÐÎúMeàh)1d.…Ì]ÐC< ë ÚõbllRˆÍH«îÛ•f¦ e#PÕ÷È«óÅÄÜ1CÁTLÀ*+,Ûv^ÉÏìÎ*j8RÝÆ„õåòˆÔlìÖƒBWg‚¦Ž0¾ûÄ’6ðmLßøæ—c)ó
+3VÖ\Sâ†ënyÃ=-¦Ý÷`FF*ëž™ÃM÷OâšÝUZÅ‘ýC3äñGMçI‡™ŽŽŒòÉ[ÏÁoWH&/Åœ˜@¹GDè]EÒ3^ Æg˜ø½ù"55Ë%=÷ŠÐa«zäSSzîúßwêlàIŒŽýLäâžI઻p¿qi<¸LJ:ìÐ=œ%Ê=–‹< JÔÄÃ0äÓ¾_$M=YßàùœOÞP ›n}pz~_wO%í°¸‰;Ç„ôÈCÈ£‰²Ê ¤‹o€þ;Å$Ž‰)÷T.f–åH`žôí— OÐæðp• %ð!*sï M™}=½Œ´3À¼EÂ*c ýUzI}>»¨k`ƒ˜,3o¼´÷FýÃÁ¬æl%a·Ãb<hz›¢@: A¤ffððÃdƒM“ã*:4¤)A Ä°Qá.l¨þ.÷¥}Æv&d‘í¯˜Z·æL·ßö½ê ,¡mÉ­è[²~CÛo^}cfÑ©^{¡®Ý|Ýÿ¯,øO¼<ç3vVbÃx%â²ó1t00séØάþ6çýU%æv}N»ï¥m ³œ! m ¡K™ES3ã3¶°ACëaªûüô3,¬ƒ^Ö7^:ŸYƆŒ¥ îÇ좪Y¶ÜÈ;Ïøø`
+ÈÕ(Ò[èZ‹„öª@j[¹˜ª¥Fû8Òé Èÿb@ßxéÏñG僽gب¢A
+xÍQ^Ö¦˜@Ù"ËÑî<YKÞ'jf…KŽý\€óeQ°”±»U\)†X!ò¿w49õb8†m‰é±ÇÂçh!Œô²”MÇ%‡Àƒ«â÷Ka·«œ£Ó áÂÙ¸ÍÏä£F™ƒL-¯"ŠJÂf>ºUY¿³(mO¼-ox~ª¤p ®ÉJQã5&bhçHcW)jèϺ¶ïÇ5,9:#§
+°N112)€¤äÈ”˜²ã Yí,¦î,|èUE
+º싨æÃ#¥íÃeìáIÝ\Ìp½ÏâýY`ˆéÀJ‰ñû%wÅ”U#¥,ÚOk6 Bº²ÀÒ¿ù¬½›ó†F&4>ÉD=c{KHÇÖ+ùw<…iàçjï¢W´MûÐIÙ(®¥}XßöÚ"'–c„ˆUž[ÔÔ•U#Gh›EÂÜÍ°152Í¥Fîq«ÊÆžH°ç|W—B¸’ö®îÎöØ|ÕbãåÀÉŒWQËÅ-*†t |b?„œ¯Pè Ö·å—Tµ™u 5#;¾õXy2³ «ß~!?™[PËyѶ×êšõ§=߬N·ýiûI×ñœW][
+j(-”wlâ`üz¯†]bÒ¢s×$!Å%kºÕ1KM‚¦¨.EçYQ=‚õª8Å1}'9fè$›:¡ˆNŒÌ‰ <‘ë’“R”äôQ6³ªÑ%¤öhI€~¬pë–x Ÿ,]mg«Ò¶Cs]h#ž¯IEGKÔs‹Šçu<£Ðy¤&jàbÁ;ÕDÀ—$ºðTˆ.>—øõmõ¨¹ƒzhn'…çØåþYFa؈Ô8P¶K çùA/ ÏókC:^õÉ‚ÿûzã?àszGIËô*™Ô Ä]XŸþx:ä銄ƒÎ·µ zîØ©ò©à<·xß,¯:2'xž•‚ÚNÒ¡IBED¯)ñ¨˜ˆU~fi§£ó‚úCsùðç!˜§Øz—0 ˜ ´(aÄ–»¹Èz2²ªÁ?FÁ½Ç̼ú£eAó7Eô‹51ìƒ3·“W¸à«Ê1åGÌBúï»ÏÞ|²Š¹G«¼æÓÍvøÈÚF‹,ñ|àlìrNþÎ
+¯R\ëo¼%_³ö&Ùù
+^™M.¨Xø¥ñ_û>j5ù´œ2 óRç-Ó=ÉÄø•¼RŸ‚[ìÕ å–âí¥Wø+Aàõ'f!.®3‡Œv7®ÝAÚíý!R:GÍGç)ù«Ò«•Ò:) Ü%0Ë)³Ë™Žq8û@cŽm¸³E1Õ¯bgxÕ!- ô|?8MÍòLâ“]ã-· <ŠÖ»»Âüúá?£ø´‹¥FDÏ®rÆ×Lânz„T°&# ¼:ª—Ì à O¢ú§¬ î)ìѶ7lPnYúZw†(É[ƒ¤›n”s¶$¢œ¯u0ÎKÂz^ÍÉŠˆ|²*fDŒíøSÐoÏ7zºÎ-Rññr;åb»» °­
+æ?dÖôp¹}Š˜æUŒVó«ÝSÜŠý1f®W%¨ºèÀCÊüN©_”y¦è9î Zúö áÆÞ%Å1 gEÕ‚GX»D ³)9^µ_yNâß1£„
+Ê]ékLXÛx9<WÄ=¹[ééDÍ=ü༘蔳 ¬o(W-½øã³ú/•¼Ê¿m¾mLƒìð«*ÔÈÆþaãQNVàª`v»’ž½ü¾á›­¸k^”ã×°JNV%ȉ¥[^’ÐZQ]
+:Á9ŠpN93wk€”´ùks‚õMSBHÍ~pl·†ÈÅÁi]xõÊ7#!ì2€7³ïYßá¿3H«>SóKÿ²ý—¸Ü‹¿;Ä.ýr_FÊû´ÉjòM7ßñ(ˆ©n ÌVã5Ó‹ÊKö‘æ›1#ïÉ™åù‹ðb'Ãg“S¼ÒíþÖïíí©{[nš~Ä~±þúÉ7.”±ÑOÏQHåøÕLìÑŠ:^Ñb‹H]È@Ÿ&š“vû½ö¦ú ÷TkæÊ»æÛÁ~ýÉZ·0¨ã׺Ɖ·vë–þ]ùŠËÄѽãï™sÌb¼ÆÌd&ÛK3vƒ±ÌØP¢H -’R¡…±$Û„Av e )»ˆ¨h¢EÖÁlçÌ{ÿå}üÏuýžë{ßŸÏ šfÇÜ ÚM~„=‘”#@Æ!j¤UÊŒÓ ‰Þún‚—ID Õ
+‰¾‹¼ô'²ŠGc?)⫼*÷ëBmþ™¼egDY'(ášNŒË– l‘ƒ'*íWëQÞ×SUð«ùj¤IRñþË=B/,ò´‰ó}Ly^§£Åá§Rj¤¾§àå×…Fê÷µöÆÓù9ÆQ’¿ösöS­
+ò¸§óyêûãéÒÈÀmM|ATä§"ÝÔ0—/¢¯«%ZÚÿvª¿­ÕOçÇŠƒ }_ËxI¨í–‘ãÔp +þ'Ò²Äorü¸ÍÓɲ˜cIÉà ÎËðDÕšýÌ&c¦\¬ðKO—ê‹.ÕõôÛmùMEO7>¹Ö„z¼Õ‚ï´ç¹ªšàO÷„…Þ":Üç›ÀÓÎdŒ$M[¾ûn?ÉH³—ÐÓìgjÎ#ÌXódE’¶—às¥¬Ì¸\`¦Ü(™N§JÂV›RTŸ²]v»ò=¼4ÐÉè<Æ™Œ¿”s2Íãäà]!Öu•tZkÈy¼ñ ë2LI¹§¨Íõ:+K¸VÖâÎæY°£1RÀÑй­@>…˜†‘"à=Ï¿É«ñ¶…–jíHyœu–›ýMÕÖf›­ÊÙéľ\`}¸;W™æ0@Žw,Oxñã(.éd¶Úð`¿?Çãû:õÃÍ
+ðBÄÍ"5áoUê÷qµÈE~Wñ+¯U ¬ƒ¡ÊhÃ`i°a
+c'†˜GîI !_†óýÖúYÆÓÜ7 O,s¥Ð¯*.þr‘…8´×Kñ3‰ÊÂõ=ůÔp`_†¨Úó<msëA‡bB˜u²2EÛ_°Õ^è¾×K„%)'ò¹·žµÞšùØ,-Ôö‘ü´=€Sˉ»=…ÛížÆbÿï·Ì‘#Vøù>êb€YÊkÚ ^Æ‘òh¸ÂÇ€v;‹<i¡ë-Þ\X ¦ðÞ)fŠ¼6Óy–•ò`wÚíÀºëð~çò’˜› ÔZW–¯ˆm§î&Øfì1'u¥µÀGÆÊt‘U @Ë5N³•±ÿ5K ïÍÓ¸À³¹2è/µ ý_]ß<šç»\g¿ÊOv4ô£\ ÂðA_áKƒˆ²Ú^²ØŒ¸^n*ÿ¥ª§Þ,3¡§RÒ[«˜bìá
+½æøÙO;KãÔä¼ûeCRúFR‘ÀJx
+k"ºŠýOÆ©G#åé¶IZ‚y`§Åluãƒja‘÷»p)/îë(À¶ ÎÚnƒyìu ½¬c„ð; #ݪ¢$4!NìÜàû{#„¤3€£w;0>‡]è—û] ùêD§K'óvQH=Ÿ`n–DÔóE1Ë,gÆŽºÑVä¹"Ìú}§5ûÙ±å{©åÌ6çzìô°sìSÝÓ¨¾óç¯mAy͋оêÏ¿ ^~àÑ
+là<±w ”øoKè¹
+©ë­tÒâæ0oíJ>Ö<ÆN8,?§‚Ïäô؀і„0c9þ|º¦f>=‘‘NåUˆã!:ôzšžö¿­*â7yØK1v
+|TضŒšXžáçëü~r²û_ëʵšWpðœaí{Г ÚïÎó9CÍCV`ß_gĘÀ`kYîºæ GÏ–þˆuœ«zïdÃÆ\ªÙY“ÕéîkÍEÁÿÒ 9?õµÌ‹vúV´+)öS s]+Ó~•Ý·¿¡ËM%¡wZ>ãr†‘m:áJÅN;WR (ß>:ÔQÊNq1)˜Ù‹Me€ßBÏ<øí q1OL6áB·»á/·ºQ>W‹|üÎ8=awˆ}9ÇGÛ<ØÉ0.ôlr¡ÄGžn»;LŒµ)EœkugƒyBT¸-a¼«C‚F©¶šK¼oTÜÌë>×ü¹Þ$´+Å«¢ž`aÇï”øíI«õEnªêl‡a2ôžá !ʪàÂÖZq¾GÒÊ4³¬êã0-æž‚“ä¸ÙRè©ëÄ
+râ.À¿¦\ðZCŽ«œÿ›œ“欓‡\ÙÅ>’²r<‰ªïÖ9óòÍ©‚›s>]‡ûªþ\ñuYD?™¢Ç[§YWKõÔSyE–}‚µ')òÓ·Â=®ƒüÔÕ1n55%–^æî r§io¼YsìJV굊k›*{w§Å%\©‰S ¿o4ÖžÞ»'­IÞëæ@ûÑ;˜ÀÝN´ŸuŒ³ÓSúêPFŽ2LTfÌwàÂÆ+rwÛËÃl“ŸÒì
+NªUN7—E[åXà=Ñ{]HmâìT)Â;´"FxkÅ(ïu1ÒÝ>BOºU °‡2F²®‹iW|f­~a‚{ÉûRRÔoë9–I
+øf‰¹]ÎJ-éb•ûcSÀüs¯¹ój½©Î¢ªÊß }0ôÑ¢ÌRz”áÿ—iSšY†çËTWu÷tÆ,šî˜ulMǘ˜EclDMÜ5
+(»€ ð¢·%îŠ"›€ ( à¾ Ê* ˆ&éLMÍ_™7ŸOÕ©§Î¹îå&¤G”HŸ‚S¹((¹¼5T›P3àÜé+]ˆ+K@Y¢MŠ¾»7Ñú'ÈTmÜÜÉúº!âk@klåÇ »¤sw
+Ÿm‘T‚ •_³wA~qËÐ`Ö2CºNªKN-XûXìžE¥ÇÍäB—çÁù¶Ò°@;ëR oÓ¢Z-jæ]Š†!#«: ²RR Î4ÄÜè"d—V|4Gzåž%åœ-ó[l}巵܂¶dÕ)˽ȌAÙÝÝ‘úôss;~]N†¿oÊ»b”àÊ?›»Û]#è”­o“,¢‚K 9 î䃨žø:ªgUÌÑ¡Ý„ÜS=ät™Û°6XŸªhƒ$-Š‹ÿµ)«ù}ûSŽ°Ž\èÅdkDÅÉ!úk–œñâ'#ðêÊÞd}š[Õ”åV4=;Y AüªÖð_R]ÓìʸµGì×skvFL4eD¥7ÔlÈ¥€®¥ ê«Ó d#ô~ÂætSaÜÆm 鯚’çU‘òö&êR…$ûÕµ˜MÈ0 `_Í1`‰¾Y
+<lè¦ FÐ
+j>ygÿÂ1Þ’=D‡%̶–]÷È…È€FP¿>Ü”®½¹¶À‡_vô—§Ž¡²¼³ÄG9Õ.£pMÖð{lI@h(Íá77”¬ÜKz èÚ5ç‡)jîÏ® v…¾Ÿšõ‘ýi¹õôÜÌ%}u°PŸ­,dØ@)Ú›BÝß,¿ó}Ö¯Îv|@ßZ6ˆ§+RàHÉ-·¿¯MÖ² ÖñŠoéù°ÄqòÓ¿kxùW£øÌQôš@üæ‘SAó øÖ¸‹e€[âb0OK-EQk'j–p=jÞ»ýIÌ5Fxy<†zøÙHEDu4¸{š˜å™k~íSÒ¡a=€ókÙ%ÖþÒ¤õa|¦OÃ.ñi˜ð˜Y@ˆÛ»ø!3ïÖR
+Ãk}#‘ÕáùÈŸp4ìüX{ËÖ[¼)kÈ/¶Õ€9ùÎÒS~kQX|5²ÈƵÜÚ£BöÆ`åÍm2Í­dÂUí5!½¤Õ¯âÖlËðO<sÔRPg#ãmÄ@)<˜D¥¯ô”Ý1tÂ/ûuHt M\ÌÖ1'b3ÃKŸ†ÛŸ@=9žiÈ8šE?
+É°Ó5..
+™%œ°©—²Ú|¢c•º'ÎAäv.{OÁ€ïNÓ ß¶;»‚:z¹kŒøx[ÖœîQÒ ¡%zÙ‰‘Væ7PKNì
+°›Ã¸<çHm¦c´úá¡žwiY˜]9}a{?t¾Ù7ø®K7Ïkâ¢bAû×®žóu±h_Å(5¾JÒ `ªÎv%Ÿ¼ Ÿ¸g@¯×´{uÄ"ŸžV{bd¢¿lŠ—èG§L´bŸšQ5÷w ÿç¸:ÿÙ~×ðBS¥fE«VjÕª±ÅÖ(IXÙ‘E„$2ìÕ FÍ›4ö*iŨÖ<8Ÿß8oÎr¾¿óâyû|®ûºï뾯«¥údRŒÝ롇AVÑuGr^²|Ö„l¿.Åg°gßOÀYNÓˆºæÚ„ÓAAÆ~/9T?A‰:©>›åA}nÿ^läTÒRÝhmú·vÔ‚þ])HÐ)êâ×›ØÒÂ8£J¾Í†”<lÀœ Ögítð¢–jÉߤUÈkU+ûH%-SwRƒwzXq—êöŽuøSÈïÜ©¥UÛ]xMy–­¡¯È÷jJØß»!g!;j"Ú1'= ¹W£M'}²ÂYcÒ<ƒã²^ÍðùÙ+Å‹p'c”ˆ‹©ê¼ëE ÷JÓ;q¥n®»\ä㵟JüÔ’']Ÿäƒ~´›©h,8ìæÇñ‡
+Iöa·
+¢:õ½1;÷½qB°HñyiÔ]‘ïÒIÎtZ–‘G#µ™?gꈺAv‚n€m}Øg,ȹÈ!.Ž—Qù¬¥˜ðX• §Æ£ŒJÂÞ„)ñpI
+ÁN‡çÂâ½^(wGûê9ÀGú”$̳Ce]æO•Œôk¡Ep4'%oq¢7 -ïö¶|\iiˆ¬¥» Ñ‹­Geö“¦ï(ä6L-qè"`¬¨0#ßçöà…µ%@½t3ªÍŽ2Á/r_í DCwƒw«å3¯·k(gËÕÅ[ÊÊ åV¬·AÅ+½]k’N>ÌËêI/V¤¿5)5쫈ð© ýT§%E™¤øyQQ9)‘V|¸©¶–v£æ3.Wô«ÍÁÑ< m˜¥ÅTäHÈ7%žÎ‹w¼¨µÖê°yiMÈt}¥ÏPÆU†M°%Æ›âÞEx8/;+óÒŨ&=ÎNÉÌ|©Dé>S
+n4µW+"ú-¤ç?ïXÅA_i„´»M1çBS] í/E~•¹l´ÐCÖ›ªÂ±™æø°`12 ÆII2¡&„›°Ò£Wg& ÜðXç°Ü 55öiKaÂ0Ê͸ÔH8+r¾A%ÂéG¨ÑÚ~|Ø÷Irâf/%hï3µÙÇ——{ôóÍ•UL?hV¬¤…YˆšœT³$¤—±ã“GÀzvæàak °ï¦Ó’’˜?´Ò®ÿèëeW+ ÌÁH~Кí÷°W[}³-o<SC»dŠ›þk©
+}³Ê'üV IÇ*N?Î+^l%¡ä¥nu¹IM…è'ôäw–ØÈ
+åy“êØE­°äc²á¸÷0t@ ÀlH@ûû¤“#ð„øxfi L»µÅ?\
+Üïg†OËÉ/y嶭xs: ^†z>†½©þ^
+‰)©y
+CP*5Ä,æ¢QÒ²M‘DdÑns‘*¥Š˜w«­±¥U>íÙº/ÎÅù_Î{Îź]Ïz¿¿á]ËÉ®}˜v½Õ'ûçNÿs§¯óúMŸy´\,wV͈i냢„¡6¦¯ ×E[àX“H’Dc½!#(hw/òÄ
+ª©é@ÂC²²c¢!%"ÑÍaâ… qy$¢SGY¦÷(¿‚° hŒ>˜é*y3.¾»f‘7Ì=)ÖÞŒq9;Ð*æúb^}=šO‚ b,¤†GB8ä;‰HgFûâ€àí .h ÜÄÀÝàHhɦ»±QoøYÇS’âãéÞ²Ý)-㥱'uBÊ%èYuè~ö£›ým-šû(Z<Âý ñY¼¿Ü €xzC ¢çt‡?ä‘c¡§¶µ¢á„Ÿ­ÊÏÖ¤ ‡6uoôQÜùJWõñ¢òáæ3yñ ³<Ù(ú?¢:Ý#!%4JHdˆÅãÆh¸åâá8`=0åG€‚ÄTÇÂøg}ÝwDH»X–0NçDyŸl¢¬½ÁÝáλ¶^~È„L¤c3=h9åwR€ž˜äxˆ%àÁÃnn?H
+%‚·Ç-¤/<FŒt¨ÍLöœSÔ‘®ìÒ?þÞí“ÿëCŸâ×[¥øjKÎ>Yè(Y`…¯XÄ Fn³3;Ó¹*)ÊHqH–ý‹dëzÉ!áî XwðCÎA>@$C}f–‹¨¼
+YÊBŸŒz0e–ž¯ œ.¨˜ÿ[g¯Õ­ç¯Õ-ŸæuìwÏ-ÂõQMÉœ±;ÑÒà >þWŸ_†ª æ;RãÓŒ!ÞÅ} ¢IP™UâįhòíRÎåçvyËùŠ¤î×òzpá÷aøï½þ¡Óuwk¦#omJ^4¡è
+{"nQ3ã:kë¼ø÷ï{²**1ÙÉ鮩Q±GI…¢ô{ŽtjŠ–’æÀ¬ªBK[› ¯-]YÇ65çû+“ìû²¾ãlEÙ|:/¯ÛyÚFÞì)>Z2u®Œ¨KM¢&~U!º$r£––åʽO¿UEË@LJCRxŒs] WZ\êYSLGIš½4\^Ä„RLÙlKYî«õÝgQÿù ³üÞWiû!ãbCÓ~¶mêÿh7J—‡e™¦®àAk^ÅFŠFv><̧;¤¥£âàŒJqä1*ÐF!;Ô¦’伟Ñq¿.«Û~m)ø—o”Ú/kOž|F.0'K–ž£…ñÞ3iþÎS1eNÉïh
+=ù“sbSž¾êþþÚÔóÃnè=[Ñ
+¾¾R5]nT—o‡×~ì­g›*Þ—%ëÛºVüqUÍ9²«9ûKFá‡çƒœíÑÞÜ¿ÆTôƒ…1ÝÑÜTÿñü¸zÏfåN[ØŸ§{Š®WUœk$_WÛzù…]Úðý•‚}µd1^­LÌŸ­[NV´ÂÏvƒüt¹¿ýp²·ôxR÷`ÊÒ¼19ôxkvZcö¬ë¥e¨t^gÉ—JcL,&æi[[ðk}ÕÇYkû¥Ýjüi·Z~®™4¿6Uÿ¥¸L¿ÒLÏ0þô´=gú¡t>ôœL;é$³¤™‰c3YÝkŒ"Be ‹ì;¯ì(ˆ¢¸¡ ‚ÀË.;ˆ€
+ˆ€ì¨ÉLÛü)}çÃóõ9Ϲ¯ëº¯ßƒ¯ú¹„š`•ÝBö‘ŽÑ^™}–ÜáŒî‰¦c:ѸC#íµ¯*߂˪“b±Ã*—·íÏqïë¨ø¿9Uò~³RñjÏýnC,zjÙš—%ÔœÉ쉴‰Þw´:ý}ÎHìû%¦0V¸ä¸ûäÈLèöìpz·æÙ-;Já3½Z=d\ZRÌân,³-›"M‡–-øÖµÈzÛ’#;‚‘s¡·à¡#KA¡¨è—) ¾yMާܨ†dš+èXóÑ&²rOÂDh³-N}m[¦>Ö-0;°ƒ¯Çšœü\+^x®æóîë¤s?©y¼.ðÄßì«ØâÆE ç^wT#ŠrP,¯ÆŒµCùv9(RT‚|Vá€5vÐG äN/Ô¶EÞ3‡’û(´%GŒZ‰O'Eº´”Ç:VϾŠÑº q°V€ûʾªAï/ »<:þ@Ü"Ä•|
+ä¡"f!
+\öí/ÍuZeÝV5û‘}S1åØ]áx÷”øˆ‘ÖÕ϶…ućÑMÌ^€–µò±1í̽Œýàã©ÌTñ/ÛªÅjÑ#fBÊ9Äé=R—MŽø^øùU+Wvó(©çÌü¾=³‡{V°CY©Ce7] Š¥çNÎTÉ åηd®Ô¦f@¡©GIJ¬‹ƒÉ€”£të±ñ}{Ò„„X~·DPvÌCÞeMÔfGj>
+"^;˜ˆ²{^•ß— Jv€”uð±Ç>6k•°Jn«`"÷å¶g;N6‰OŽ¶(§&ï:‰] útO†?6
+
+ØŸŽ…ÿr…Í“XpQÞ½B)"’A;Ÿ~f“>Dø¦—¿
+20%ž5³FmÒ™¹ÿÐ3# Ǫ>hNÅì´á¨•KXyÓU?ŸÛ ÏïÔÃbeÞC­……KØ’¿•®äœôqˆ¹ØõÄ]¶nêÂ- œîQûãk¨»y;UöÉ—Ž6q‚êÑ/tÓüº÷m ɸŒ´GÄÚôߌê‰h¨“vë!±ªîDwU­“ æÉŸOô3‹VêPÝÅEWÔeXÀ©øÙ„´ùÃéÖÛ;qMï_ýŠ×7Aî“?'V‡[ª>‘<¨ç3Ì
+ăŒI¬ª ¿H\ö2± ûV“àùAaÇçyÛôàuR´VË´åh뙄ú%.Yk†€¹šŸOƒöÌDʈzèWY»sáq/åº4ôŽ¢Kª†
+NÎLÄ@‡ëy#wÓ;¸º_®©x”«%×ÂbÁÁÅÆ4ƒ_¯Án§·g~J›(ý6)¶Ë#Ç æ­B”!f3ȧ]†Èð†0œÛƒ·F4í)¹°º_ª:w´ƒÕ™–¼“©x„¼ªG*¯¹„̲ƒ<Ñc:ÓfÔãF
+‹kÑò{³}5?W‰óx”x­©r^ܪìùêP›ªDdÚ m´`™~zå##¯‚´™f‰-Ù0/Ž7†ZÒºñ×¼TDÎÊ€”o¾ðJ^}_¾Yv½ï)‚˜¾Ë
+y7¡ÃWž8w3}"YaG6
+9xƒE0»÷ÇÀ*ZKÜLkMZY=Çjü³Ã%TÅù®|:㛊$ø¸ŽÙzºˆþ-¡&>+XEä˜^Š7OSÊçÈ—O°O‹[RQÊ,Šlò@«=!-¹-fba’6 ËoãŒ`®ü®¹ÒŠùa£„5S£¦¡Ê˜ _Ô¢Ëêža¥éPËjÙY ? mJ8Ùí ä–‡õlDG‡‡Ô˜Òƒ·7N×:nŬ”fÇ,â×½×ØòŒu|æ"¦,RZÔÈíšY¾"f$Õ¥läø¦ŸìþìvsÖ)–äv‡'‹ûBaÎMé*¸©½y³ß÷u;®çáÏv&TQ‹€à× 1»\±ÈE!“€ô9Yðˆy>1k
+;b!ää¢&*<i§´eìLTÁ5:s€ÿ¶êTˆ×TïÍàùyÞÅ'·ÙØܽ;ëd"#ôŽ –ÖœóˆXç{Y`ó4h$À³¾—f§6ãV怽ë·àê ä¦!r{ jÞ+”ôœž£uFsv{x*fçãÁ]èY«ˆ”sˆ``ÚFhÌ8Miµ}™]wYËo¿ÓñúBZJcH« ™IMa3©!j%Õf\LTvgø%ÐÝbÐ,a&ívÁ#d<ÌþœƒÔâ‡_]¦Ýþ‹EVùcÎÁHZÅ´“ZmÌ@ï9ßc‘3<< Á<ö)qw‰eGëCå'lEØ@ëÈxԔћÚMº¦Ô™wŠ7¯ìe~<wdc1»²¯$–%̌μ›;xºN©Ê:¤ü´]ÊKo hÏÈÜ™ot½x(™H:XÈ„…Ìî͸žÉ%¿Qˆ YÝ}læ“úÅÇaÿy þó@<4p±c„ŠË 8ØÕs‹l´òsJ‡~YîøépºùûÝñÖ½BÜ
+¬+U²¶ë’Ö{—’«¬Ž¼i˜Y´‹(ðt¨!ªGü\m¾þÁÍÆ\ð6ngž{EŠ´› éIuA=¹6n#7@6J[ÊHzá_!ü÷tEŠò/r_ÄÖµ)-]%ÁN—Hbð®&= (ûnÌ·þ+¨Æ<ŒéY/Ž”Ôö„†‹<ÕÐá«Ø’‹¬ »“TÒ éYí»—wM©Î@®Én‰@W¤7tÔºc Ü`-È!—X
+9yô¸‘‰ìâ%m̾¸‰T—¶UeíƒUI3v¸Üuïàmß½”I@M:^.„Í\[Huy+µ 21q¹9a¡ÂÛ¤gþ5b¹sªë‡Ãb­}¶¿fÿMßý”‰ÝT㟜*û ƒ¼—Ù Öçm,TÖÁ¾È×¼[Ê/‚®Ûb£Ã:üSàÃwOõäÆ£ Z£{hMÃ%õ¢ƒÑ¹6ë2foDOiM˜X¨Ø³5ª!×
+¹Ç_AÛ#ŠžVÔR뎰÷lüê¯Rk„Æ3ç°" ˜”°±¢znt™´YHÈÚxCa=¡1°IlŽ˜X=y÷ÈØ—…ò㻑é⮈Ÿù?Ååù•fš†ñsfÏÉîžÝälvw²“™d3IŒÎ$Ñ5c\#±lA¤W¥DŠ ÖØ{ŠQ&ˆŠ‘Þ¤ šü3ûæÓóåýp?ïsÝ×ïºÌ}Ýç:T±WÕ“ïÜèyá‘£
+¼rÌÿ
+beXIkòo’*‚ŸûZ¢ÀÝ‚;´V· Wé£ÞD ú¡Œ§4çÝ8\&6]ÙÆåÖ¡©s³7°C¬õ¨h €!€ü6èᔓÁg2j£KÞßæf7O³ÍÃ-Ý è¼“é¶gž5LE@Ik9^Eÿئ´]ÚÇSÇïÙ13ÖáÁÞíöºõeÊJjÿv̦ûµ}°ý¥®·Žì G£@¶OÙD³W'C³q y®C—w<³ÌÁÒ#ÆNÌ$JZ8Ì «/¨'¡S6öÀ•­Ÿ7P;\RtÑÑBçS·’Úx(¡¾5Íp õìtD;&Œøô„¦Ò0:|rJeHÎhJh¸xï'BIPJ­O¨Xí§s™îÕÎ,Û<<s[Pñ/ ¿ìºe¸ù™cƒÚ`œéɶ͠²\²>„[ñéÛâ ãÀ¾}3Š„.¹Y5\ÿèË
+¦.ð0hÕ¿ý̳݀-Õ 5fX§:_è&»hyq˽H¬u¯ÐË üÖ´]NÃMÍûúî 5 k¾”JÜÒ~ÈG.,ôüöœº¼ŸÎÕ\²GÍ@9d½n9²àh¹á¿–™ª;þ­ž×ö5d‘VT÷ëùgRÇ7 ÷DõØšà"×ý ’a_(ÿI/*¼q2Mª^%u´ŠËjú`—z1©dÕô—ÿÚ)ÁæZ©p|D>W C3¨o³þ&¡€nÇvydDZäd‘y¶ý-°Þ™å·üv²ZŸqa ¼‹jYX¿WtúLG¸611+¹ëÂÖOŒì
+é™XÓBW–]‚«ŠïqÈ) _¸Õ}PÛ'ükß&±Ì+í-p|ì.ÐêšÇš…UÌÖ0Y¢&áhÌ"´Þæà
+lSˆÃu?†×P ϧžb§¤#/iá}vXÚföÚ—º
+=kï@ÇËmK­éÁm*üêpd9µ/šN˜x¬ËáXÒʦµ½%>eïkŸ
+ªÐE1-¡:pÔ·Þ“ØÀ¼
+n±à{|fTÏ|w¾CjpJЯNÄ8Ðþ2¶`¡¿á©CÊ$GÌcóÝ
+Wf_‚eê•?ž,ugÛ¦»ž{VЯ\K¨Î`¦ànqWn`‡Ü°2ú6³·1óÏ|õý”et>¤á“Û¨0À§3)ôEŒÌ;[ëÉIèù¿A0
+
+älƒTc_æ×óØaÃÈLøW'‹í̲ë#íÙ?èà_‚JZë¦ôÁ<©83ªçŠNשµ&⾉ù·‰_wSÑ_øw1!ãšiô3 cŒSÆ‚;VÉe¾5<¨fBJJ}D-ÿjîG'÷Xø¸‘ÏJîñ9WVÀõ dÂDë¸0Ñ»âZ*$ô¬ÀZw®}¼1Í9¹—ØÆW¤}…
+d[hy ÁþEI/¹Q‘»"Fǧ`4Çw˜È”K¿2 °Km>)2÷p
+œ&ù?Åuþ•t¾Æ|fš™Û8S·;3My«)k²¼¹¦KÚ¤¹+n¸±*‚€,‚ì‹k¹¦fj&‹¢"ˆ(*¢‚¢²ŠâV§sÏýGîw~øüøù<ç|Îó¼Ïë¡$^6a¢W‡jž_#î‹Ù0¿º«ë`š_ÄÆŠI Y Ø¯DàØïåÔÜ›;cä E/1µ›qe½úyQÀöÈ)%K¯aᆎ²HÇ乩·ü¾¸©ìîòÛ†‚c½v¹7ÿæÆø‰KU—e“U'íˆQ/üsL졆GTwV†Îò‹ÿðëxüóµŽÑãE6q{—¸5VŸ¸ó±!ãTË"yULœ´ tGÍ-sI…óÛúøzѯ2"èš‚Q¬€o›»+·‡ëó7¥ôò’ø;ßPóã‚mct‘¡µ}s¬4ÔØšqÍÔžûËŽ}ºÌ¦ûô<î»ði6áúm¢!¿‚Ú#cVÛ‹ohš2škJûÑÚ¾w¢¢V, Öe—'ܽ$B$=¼"ðêšë¬ï:QöÏ}ÕA
+nòM¿žŽ÷{Ä«bF¹]N_,·vž-
+8Kôç4:eùÌ.©Š<ÌH Ë>Æ9äôbŸšYmªŽ1ˆ@Ávö5 >þ»qìÓog˜/oÚe¨‚/6A¿˜“ùÐü’Xb‘O€¹>YâñFàiÌ_sÜ—WWúÁ Íõ—äP6÷¦(¹. «ÎoàPOŒ4Ôg
+:`i÷™oöh0׳øzw–PàÑ‘*+lŠsu¨šáž…Ö«˜;6ˆø`/ó«¹õ“¬Ê] 9kmùX+‚ÜÛ“Ðá; éúP°©tÛ2Pö@ÁÏø·0†s
+›z¼Èçڜڴ,—’[ë×rð'KôºóUzÃùƒX&@¶¥ˆ¨iCºOË&¬Ëê‹Œ}Øô½IÞ)üj>Ñ)'å³ðû #óÊ %ó‡í)j¡„ ~0'(uIXUûröjoÕ#ë "â@V÷Bÿºä®Z˜w}m
+¯†Ûx¨¦A<3Äâí÷è$Ë 4|©rÏ2„K’0!Q"èó`Ëp]ŠCʨtH…8÷$9Ç-!änãóç[*úñÿ˜V¤ø¾GÁ@j„éÿZì,y´5‚J:35á \ªSͪë…%H89¡›Ë¥¦Böe »ãø<ÀWé;
+EÖñÚg6)1OÉÆ(øõ>µÔ§ªIÝ„Æ-vܱ7¤›Ú{O—D¢%`±ÑÚø=!ß-o,rʧ*ˆy;¸ë`M侘œå’2×ßc¢ì2Je‚^¨Àn)YÅ¿¨˜Å¿Íó¡a#¤¬ë’¦Ü–‘Ú«C˜øyQÑYNaˆ„z´Ñ? pn`áôl¢i õ§¶»(â|…/<_t8€\±Ã#mÈØ àðitÒ™™ÇóX„=¥Ä£a FQûþ4.u}ªdeý¤kÍýy¹öxm¬1½“ŒNyüýÚ;LÐë”eSÕ®£mË»ýùô*¿(ì;%#ð¥Ü«âÓß`7±>u3&`ྫྷó[ÏMœ&¯†Záš#åù44ˆkžGµI 9n£úØ%z:ê@Ž~¶Ú¹ß_ýò29ý¯E¡—–Û@O>› »2B¦WE)õkˆ€Ž×èÕ6‚] Ø»0× &)`ä²ÏW:‡½jFÍæHY˜SÉšñ@-8p§Æ9CƒlŽÖ%LÓ¡@®ÖHà[@Œ….v!b¬CÐP×dU‚{
+ój_ŠÎ°¾ƒÆ©8ÉW=³5eçf:ik ¾5Zû¨A=[æÐŽôMèÏkö'‹°Ë5ÇÀ躡!{JÑiÐûÌý ~à_Ýr,ÐÏð=1*Å«"Cÿgg0Ï—aÞylîÉ:·ãhãµ<°Â$,]ú?Åeþ”V–GñšéfÒÕ“mzœ,Óš=;MÒɨ±³¸kÜDƒ(ȾŠ€Å$îŠ-Š7Ùƒˆ. ‹¸uR3óŸÌ˯nÕ­zUßûî;çs»Ìq•ËtHdSHGo0½‡ÜßZ`–Qé5³)a”ð3äüÏCÕÿ؇Ü(áiäà±(²½`ÇÙšB?kˆÅàw‚„ ípŸ³ðèq+@‹˜Ùøȸ›|`ÙöÃ/î÷ã Îeà>Ó­@gÄ–Y´ûGÙ‘EÒuê&ºÈ„îÎ’r·äøLŒ’m•’3´¢Úêo–Qªb—žŸ¦\ZàTßt^m)Yu»3Lxt±­1²ÊnñL²·A¿ ­u²÷õm ;jhªk$ÿokÝo.j¸™ç"zjÍ¡E
+÷«†š«Ô_ŸÕÝÜT±›}ºNÜj/ò¾‚”óý@cÆw½Ùß±Küa‘_’¼z½éÙèüŸ/xǨoÃZ."®§W…T-YŽ¡Š{ó¼WצڳÏGW1eÿÝu‰>FIå›ãMÿò)cËTè‰à&\<àÈÎ¥í-ËÁÌ‚®rÁ¼…ùÅ+G?Ýc_íj(•ûzzõæ,µ`µqOM*º°;->4·¡WúËR ݹ—ÌCµ÷ä´ÜŸÆ o®,(;&Í,ýſyð½gä]Æ6
+»uc¢ù¹c~×&…ÝpŽÖþ´¯kzvfðc&kŽ =0p0ÇvQ÷—uQOÜ؆´öBn/óª“ì}õ·vÔ”â]ÐlÆ?:ÛÁ;a{Fêÿ9
+JÍ™õ£ôÌ
+°N-dرWéŸ>¶]· %¹Äý¶ÕÎ’D˜cÕغ¤Ç& íÔÒA9\f!":t®o¢æᎪñÅïë¾=cWw@Ë'Æ­â÷ 'ÀŠèа–˜ÄdMs’$UÎí®
+9›jFYh™ÕZâ |ŸˆÙ›à¿´5Þœæ…ÞòI«n†T„ò=¶2Ðxgs²²«ãá·TäÂm¹`k–ðÒ?‡{¹g •˜¹ø¨©W· ¨ãÖ^ű£GqâŠÏœBpÆÀ9ãv0K.Ók‚KlÄþŠðÍ¿õ“LG?*ÕÙ×|×(ªº²>üî™
+WäE§Ù‡/|Š.¤W.AnNòàÞiF±̃%&Ó­¬71á!^ »~WÇED–$@È
+lnlžXœXd7EuœÖ ¢Ðõ[å·´"ùÈÌfš;^*kcrÇ«¬¾g¨¾»=Õ”õÅMÀzêY@Á¯y"¥W¤ž¸z¦lâ‡+tpª5ÓÙS›²",¼¾©hÈüêéì·c_k%¨—¡µî±#O·z_ßÞà‘ã2ÁžúÄ£ EWظˆÏYÚq ¹=KÎ÷OQò*fÅú(2ÍÙWsÝ5P™¼È}ÙÞW™â•a‡ç„8‡¬5O/)»êU`³é°yâ` jlM(ˆ›E’C«¸+aéê>uöÎœ9%ý§ŽNá¡MØ7KÃfZ]ÐÈ€'ˆ¯Æå×v¦XÐm%³TÏ®JêkHÿcôÑwzfÎ¥íÑÆŸƒ*f¹y‚Y2Ȭ¹9Ö–{Å;‰{y°  F zÔH‡š©õ;‹`/#foÏ­Aý¡}¢­Ø=I/öÏÓk  .ÌøÒß]LÔ§ óØ*égl­I:­Ý£ kׇ¸ ‡ ”š ¦)+ i~½7× iù-›Ó¬rycŒ¹=‚)¼:Ú’ûÃ0æÍ¥Þ¦ì “´‚$[Wmrh¶½FóžÞO*ø«GΨÛ׊i¾O”üíÔóðêEpþ(ºF¨ˆYØ€ŽCH¸>ÈÃævô‰ñ.b$V…dØþ*—¶k`“¢>°¥¥”lMs<
+Z£UJ¨Ý›ç·ìi¾ÍC. k(¥%>{}Œ¥•Ôß6f\Øãó6'©ùn:=¨&m(ð¯²ú‡Óˆïh‰E ék#˜WúØùgŸßËAmÆŒF¿“PãrüêÿS\Þ_izïé=§M{ns›£·émÓhFkÓs5ƸÒÆ Ü7Q¢‚ÈF6((. . .† ‘! Côâ`OÁ‘4iþ”ûæ·÷ýé}Îûý~Ÿçó _x”„f«Uìéí¨‘ÉŒ›Ù,¿ª¯Ú*ª»¥b”&)ú+¾!^6OT¥4$¨„Nõ%ŸìÔ÷wú^u{ØÜ…†‰v­‘jŽ7è0 zgo¡¯"
+äwXOGŸèh]G¯»rwÇš®k)O¿™îúï·¦W/³FÑ%)·“>3‹sß;=AekᾸ*]/¨½Áßû‚N»t¼Ô”ýñ€5êÛÄ·J»Ÿ
+,¢çW·'žÿh_€<ÒŽ7äÆäÝåí1v×Ð%ꉶ»µûÌ*˜?³’0 3¢Ê¯¬Ëôg|)Áe~íZ‚üñÖÁØ—ô•XÄ}O½›¬þ ë„âoëÐÌG'ÿÎÑ ou=·Ï´Ý÷¯‘ëƒRf{XÍÁžÚGWÏ#+§æAgSlƒÜL˜X@é|äÖ¥îM7ÜöIQOÍÂö4ûúAh‰‡ÜŸD?Ò±jÓ@ßOvf]fק}E¯ùåBXæ¥ÃטÊÃU|©_ÑWÑšC[ÄöSg0aÇvxˆ9³0i^¶2¦¥õýƒraeü:6þ@Áì8^ãÂ÷¦èÅ2Fë ~ëãK’ÞÚB+øÑlw®c¢>Õ((N² ëoì/õäÚfYG¯ù ­Û¿†+÷KÉà† (QÏýŠ®"÷:,_3T|]=X–|ôæeö…•Bòh‰PŸ²û™o^Tu‚âôƒ•„ø°CD¼Ý¡ N¶i(ß¹ñ?‡´rT7$ð«88Ë«®\qoÙ•WÏ’4œÖÛS¨²­EÉÖÑ^ë ¡Ö.‚ÿîœAfO£^|·N-ý) Ç7zålôÎ,µtW‘¤jËó)9„€¶ßÆ·˜0-#
+ØËÞ²€¾0кcþD|‡ÃO
+©)^¦tï5,W?ÚjŸïÎ÷l^&¬¬¡C%¦ÉºØ
+9ƒ'ÞLÐÀè š ñ
+[ãS¡Ê•°¢YG‰c¡)Ó¯D×FÍlÛ"½n{ûØ·Åf¾wŽ*b[äOÌ™¨½ +w$¶= HØ1-Ñ°Ð1€A#z?¤aôxÕ´ß± ø/HßöEhXj¼«=OMãU)Æ¡úT›ˆX4}~yº¢æÕÝ\¥V\_Bƒ’Ö %ÉÖ±æ;Ç«ØbÝüžmªÒrIn%50Éq«`!dâ »5äÖCÀãw‘ù«½ ¿2%$ǵE·‡…¾MvŸGŠ+3 7E°Ç_³>W0j®’곿"”þvÉ2Ò’TôU6ÐÅÞ²ãX¡}º6CÇ+ø·’œóO§°9õ‚¯Pñ«¯xu©»SРçƶúÀïxø™ Ót°ÞœÔ kß>ýÎ1¼ôÁÆæFåЂàJËïQe糸‘Šþ?Ååõ•v–Àñ9»sΞ³;Ùd’LÌ11™$SÌMÓKìÑØÅ*¢ôÞ‹ˆ`‰ÑØlØ©"U X
+n"ߨðeÛ£˜üϘÒ_5ýÙ‡süFÛP{² Ÿwuö檜’{K/(¸éžiMê™øÍ‘†ÔÎú——½s\DTÛßw¢ãSc@χ4Tph›>sr˜ f—ãßO±*î ¢²n]8úç£Ö!Eв‹TãšnNö,vd”äj`áÖ{!¿ãó¯íNÁ3m=ºÆ'§‚<Àñª8p›”} £4„Ýï.ÓaŽY\Õ‰VÄé…¼KˆXºû‚Û@·iùØc Ð,¬
+è.½.¨°7XM[½m¿ObÊoJñïoÛ‡!6…嶇ÀÙ{ Ø !÷†WÑQ3²ð±.1fá°£V&3¬%@½‹ß{”R3÷|ŽìôLx6XÄ ÀQ~çQ2À‡JÂlŽ°©§èïÏ{rV“k‘^µo…ÍcúSÛˆ>lþ,™z>†ô\”àÅ/
+DÑÛúX? ¼­ö¦Ð !%s´D.ôÌ¢S]SÐÇŽáÆ»*AáÏJaMÂDgËí¬Ç×C2n{—ðEûóè Ó§ÖDûìñž¤åÙá<øI`…\ðαBŒQsn7È­Aýè’W5>ïÝè é»;ÏÍâþS£o›%8fi@ªýT°w—ïíx¹Îm|¸Ì
+¹n¡êÈ9ÏZÓÏrKNXËÁFw„]V)ºLΩ¸nG§ûubñ©M<±wõ¾ï$ ƒ.ʼ@/Ù_æ¶ú5TôþZGuÔ"8³ŽèþpöLœYy4Ÿž‰ÛØ„€ÞêÛ¢ÖÆi„²ç©Ålß*»Á+£»¥è´/ÃðG–Èíä£I68®5'ñÇ9jù3÷"®@)ªŒßä—]µA÷¤-I9<ãLËE…´Ý4­ý¾½$áoöAXEH'5tQó§™s[—(fæ3¿ï×*«F"‚¥t6f]· µ—G”̦ãb{†VdÁ½ÚùˆzºÎ‚Æ«x°DϽÖ<ˆH1ôÕÄÊé 4Å1 yá‘ኜy¸„ÊÙƒ%oŠÀ¿Y95j!>÷p™qf ´(™ˆãüb›…¿ØáÑŽ7°ÅžUDê©š\Ú TýÛÈ&ýé¾Øò]MmÜ“¶¦y—P¥{ËÄÒ\N¼Œ]ûÐ9IʶÀžèDq
+VÑ5@WÄMi—ç0Ew,°äiJÅ]PÊ­œ£ÐÌÈÆw†Áä»Ñ9»ãˆäƒyHÒÑø¹{¹ùy`Õ´+i/[²ñ`“f¤áÎÍÌ™™†¸°1Pÿ²ÑÑ5¦Ä·ÞžóÕÜ)ˆêE¢sSßØw¿œêYèS ¹þh©-Ã:R÷P)(¾ùE‚~g›!ÕHÙ剪>P’{_9ϨLš§UÝø©(¢ä£ý
+b4i{{Oïr¿ýíýñy?ç™Õ]Gyú¾âŸ]À…LóáÛ£ ˆå=:|’™ý½¾ÚSúf’pÈká#´ÌéÚt¿=Y5èx†úΩ$§k(YvþÝö$-ËÜOOf†ØÇ8L÷J—Î¹Ô 8rýl¹–pb°æÙ{ S.!ƒZËÓ‚Ö;¨ŠS±Ö`ëÇÅX:Ðáz4À",¼}ðøf}”‘ÎG&¼Ç¼½s¬ãb€ÿ”jðÉ«ƒ%¿ŽqÒn 1!þ–>xÄŽ’ Êò_…^ŸbƒÃþ°µ*ÜF‰øðo
+³ žèZ²nÙ>”=vé¹UÝ„¤ïšai_–šú/ÌBÁÙvª£å9•”¬Üëånô£Ù–Ê°NRNÐ-ûæÑ¿ÊÖÜ¥“³"%¿Úìc¤háZ‘ñ×%…O¾² -°SSÁ=ç >Å¥e”»4´\¯Yra¢øÞ˜:aaÆ6XÄ‘‚O¹\j’î+)9«#ȧ' „œ39Ï»DEüµÉáü±Æ¯wjê‰gú.Ù±¦©Î¡®-ÝDD­vB²ïÅY?¹fi¥šÎ²×¨”Ð+bØ‹oÝFiµ—¿òóh½  púé?ùìÔrPN]=aMŽNQ4‚ïîÏ S?[ .ò(®Å:æébû÷ ‘ôxž˜¿1VùTÆÍ Á½{ôÃþ6Ík¦UnUÄï£^yõøœÑò8skN° ÷ok’Æȉö›áçþì11qg‹ Ô™‰ÞåNÀ‡ì³ôÜ}½à@ß@ßRràÇzÍ=/`^,óè¿¯Õ /9x/ }Û0ì‘¡5ç'cgUì0;ÿá 6ùZ;,îJ[q¬O<ÞßÚ…xåžå`m£Ä|­ªæçÝÚ›®)r,åPAÌu¨IP‡†Y
+8À–@:UøôC=zl`£?[$ÒSç1óY[“œJ­õÒ¥«AxLµ„íøD£¸ðž¦!ý†›8DN¾kÆ$
+£ÏÉ%çËÁ§I—Ç\Çðšxç‹b‰ÇØXãÖ×"]JVÁátmáb*|±©*RÉGFIÑàë]o¾½ö§úÏ7ÜwŒUÇ;•´Âq\æö$>Í1Ë«tΉjÝFò`“ eÞRðSü—»KBìã$ˆÛÀ§ýäHÇø ¢ºÓyÝ© ¥*/vä¹!–^ |À.Œõ AÀÚ›bŸÎ اsbÞîDMÎæ>v¡ ¥#ÂÍí•ëƒÄØ­qlNÿEÅÏ»3ÍÉ _î†G
+~=âª|µ®ªx~0ËÅï¿6@uRôç–Šœêgæ¹GèP‡’‘·=.ÄÚ”Ô÷ÆæÊÇv%3s”“{½µôõÙ©†ü[sM¨§J2œ åв‹»ñïÑ AØw÷Ït‘’ÿòéIÐ uY”k¼:ko†GqÄ"ÿ¢´ýd hýµ |ü߯ޮDƒü ”þ\í0ýX•}:²‰€CŸù}‘Q¶£Ã'ïMÑ~#»¬Ä¦{&˜•>ЋÌBèÍžÊسm%Ïÿ£e¦üáѲŠrDhùý9
+{0ÓTîøRy0E„/òkëø#]ž©:¤G/@û§$Ìýio{¢îVÓR*Ú{ÈWKíøˆv|úEqaÜ-2ü+³óXσ]5¡–æ‚G )tŒ<×Rpß?^_åÑÔW;T„wÞJÆz?&n£¿ä¹g¨äÙF/ô¹W]¿k¦dï›ø˜ïf)Ï«eÁü2äÄÄ@jrv& °íqjÎÉ,ïÓ3 WÁN ²f­oªIèæÂV>cŸOŠ³.Õeìô綆ødÌ,M=òæL3þ…KÝ€\n+¢¢¦\’—Dží¬|dU”?°¢&l
+Öûjß%÷ âŒÛ;\òê
+¢Ï3ò"/õRoÎuT†/uÕd¸uþ–¨Y¦æΉd'ËÒ¦ƒy€çe䈰)EY˜å3æå®I$9µµ(Ä<¿‰[îç¡c<ô¦š•ãå#7´Â
+cVÐàû3m½ö¯„Ä!jæÅžªä߇9!Km%á»úš4ðŸQkÃ|&åõùvBú‹ãiõçJß«'C¦iè“…:ê‘E@ö…Tç¿bB†~ºÜ[ù×Ñ<v¼Ì%ùLÔìMuÍçî•K…} úч…^Z9'þ,57ê’m
+·â–]ÉÎ>0s«öôUñ¾!TäÚ°”hj¬¼;%‚ëW â² mÕa«ýÔ·9êN_5äB&ïÆ(»äþBkåCk'&Ü9€{í£æ.õ‘Þª°»«ýµ©>æÒÐ [zÞ†VPêž”ÐdR¯QL^ׂÝs„Ý–;3r¡[Ã+¶uãc†i—šÏ~ï­yw^Ë-îåV\ »ù bïÀÆÛ;ÑÏf¥9×[¡góC©ïÎIá‘JRJ°N„¥Bž®BÅÿÏpÝÿ£½þq
+ß¹'P Gpé;ZJ¦[ǯüiµøù ›‚›ë™­Å{´ÔŸ‘K9_4Ÿ˜ùl÷,-{O <àZÇ$³Ð¡d"¶'€\Û0-É6B€hÙ姆¦Æ}mÝ9ÉG;œ­‘–ªÕ~nš¥‡øAÁÉ
+”Ñà÷ÖˆqN¿xºãkrO/ îÏ77íÎñX;f[EÉZÀEîé
+l©—øqª*@&Þµv•G[ºKÃ@Ýê#Bo«9Ÿïÿ½6·ã¢×$ô$m!Šž¹ÁËþ°7I-ßD…:d˜·V âÜ{ÿÞŠ}[É‹Áz䳤7Oý©Ñ·W$ÕšÑ/ÔLÄoc”¼Ç¼Ì¨«ˆw—2^>¸Tòþ™Ÿ“põËÇ~„ä0?ëWð;Q¡›ý¨WÆŽì§c¬¸€fäKÿ¡š·×Ý
+<t}œ’)ÒƒŒíÅ}?€ê£9.Ù­dL3rìýůԼ¸»–.ä;—’S4@/Œ°ásV0ñ–4dSJŒö‚‡Ø’a“¦Òwb>\± p¿Ì¥e.vâR\#„ø)><Pʼn¹»Þ›r²P™æÕÒÖ¤ AKã¼s 4£”ž¿3ECª<130?-ìê3snáÎIR&h˜g›rbú†Gp“Òǘñ÷ÔìŒ`›´â&@¶é0—¢…l—5 ]
+Þ¥jamÊëËl2v¶]Nƒ³,|½Ÿëe¤îÏŠAz–¤ö«².ÿÓ©YØj—’<:Ðu¦ŽwƒuLâãvpªŽŒbÑú)ì hh¢s”ž½Õ_>ßšò|{´<Ú¥¢~^S9 Ir]j }„ë3ñÈçk`ÝOÒóWqÎ Ô­ ¢fØØ‹Mq÷ñR-¬¹bÏ,»Ô·È®qi•>SƒàlI(81Ö3ŽŒµ4Ç4«ðÈ(›…ÂóeqË¿–zÞÙ€ñj—‚ñyì'k¢oB??˜e 4ݬd}1rcˆ“½$©ù°1€}³5‚ _ľ7JÐ!¦o¡kƒ¢"³´aýÎn×m UFÚzKCCøˆIR–k†˜²<‚~c”–¿?2‰°'\Z>Σ½zà3·õžZ:d§–¶þ]ãPÕÀÝZn馜™é£Á§Eˆ@ v½³"媖_¼Þ[—³ð•œ(£}yhhÁGN ñõm„HÇ3Ý©âoŒVÇ‚ˆ±ôVAœJvž¡ ûZ+,}flêÎ8)Õ£â¹A;­ôpS/#A×ZùÖøµ:æ@SO™o+ ²vc߬ô”GÈiO¼‚ˆQ^×d'_ï«Bþ¾$eÂäœÂG¢üD?nzìe:<â2 q¾Ù˜»×G. $gADÅq2vú“ú‚ðÿ)èñ·&Xñ×Èñÿ¦§<ž¼r*X…ziU)åÕ•\ÒË‹%aÛ™QØàT02#ø(kgæ/š:ø/¶aJþò;{`èÂäœ,p*|`vZ;KÃõ¢ìç}UQݘð+JZüÍõV•ü•ðòö8ëËë=%5k{¸r0IHØ›@%*¸1Ï;JC¯ix©¿¹AG1ócnɘS¸Xå×]X€*ß%×£®L›&?•Ó¡OV†ð‰}@Æ›Arò%/5xŠ›óçV_ ì`†ŽØ–“ ë`@iksCìýU™»j!°ØMÊÜ”ðýÒíòZÔ®ªëV2²­Ý¸„Q*ô
+Î`dï|CÝŽ†Gœç¿°‘ÓÌRR‰®`—a£u Ð_·ú‘aûÓàþzfÑ–œz(ik ulp.aùNˆ2uV¼µË«¡ž)Fñáÿ).罹Ó-Œ_wvWïèxõŽ.¸î¸6+¢ *ÅJ‡P! BzïPzz ¤‘F’BI¥†ˆH,ëÝýSî×ßÞÎÌ;ï{ÎyžÏ£çQb¡ä»O<°©jÍÙÑ’+—¹Œ˜+øºÂ“~ó
+Û¾¸…â#§€óÉÍç^÷ÿµ.–íê)°¯.¡äÑÛC= ¾6„Jõö×ß\•¡“×GšS}ãéGË4¤mŒUâ›dÕì/¶qó‚ºÀ$%oE¿îèü¾Ò ¹èÅeyåÌs)Í3@ÌØшñ®aTªYZrÎö¶4Î(-Ž )°9{zVcÄ(âEl’öå ü ë>kÇ(þ¥køƒýÝÈ¡C$=´² [zlÁ6À~Mk‰J ½žaCVÇ©ENö±IXsi]ÖüÀ7BÎñ6?6IKã7ÆYÐMu§
+|÷¿*!èñ7tàóª´?æbÓ÷M­à°º%o©·&aŠšÊ;M($@2â
+ï^üÙ3Š‚D”ÈŒÀhUBP»µ1 Nèo¸}\Zqõ¸NøüÚXš|Ÿ{£.û³UÄÿb—´Ål"AÔÌlÙ5àÀÛjÄ£|,NÌÃSkÒo£‹î]ßTÓ¨1WúÈÓ­ù;ܾðOP ;²µÀ6&*¶õ$d?6ïæµô¶o“»!ozœiÍÙÓ‘ÀF|É7¥6¤"ljø^½cˆòlK-jÞÑ
+f…WNÎ1 ÏJ®.ˆà‰áib‘µ³æ†‚ž¯å–_³t¶¤¹‡èE®1^éÒ ¸Ä8(.v3
+LlRl…Ïó)IE«Ó¸\¿‚RZà ÂZ!1dP€ûËí2äƒÐ2=¶Lkr Ô&8{j.{GS§é%Æ°ùg ØŸy|®ÿ¤•^-…fl.!v↽yÇ=Øœ¾:BÉu¾'ç„5Ì7»&rm—VkbnäÀÊÄDô­Å«ãµ·ÜÀþ¯O£ŸlÌÑË­raùœ“2A¯JÜ\ Â<Uxº!—Lü¼3Aáys,ÙÑ OñÏ’ e;®ú£ùüàQ•¹[›@?ÜR“ÊCj2Ø;ƒÍÝ8&âˆÌrLaSÉý“Õ™Ivµ4òß>a÷wü}W xîsGwÙåånh’yˆ*O»ùë›ìÛq;KôŽ/>ñСƒA Í#ž;ºò/¨‰™'?X9„-‹„ãžåT/É‚jnýmÍ3*€Þ?ü‘§æXK!,)8C«\žbA© *1ünÔÀXŠŽðÏb²ÂóÄBŸ‚VáS·ÓCú®·Acß{ׂLbíC;¦Æ;½ŠÑÆ £Ì")¼~;8Uwkûju–W3Ì]ëjÎ9³Ü…JYÆ=sõ#nyÞƒ£‹è¢]#¸ªä#V¦˜å›F1{Û"¤lêé°° ökðn9òaXÙò*jÄCÂzB™gÿÒ=GÊŸ–VߣäÇ}в뷕lø¶Šß3³1
+Íý0ÌôL«ÖdÆåv)ßùä?³sÿû©ÛõE“ì'¹;i7øªp áœ¸ù2™%ŠqºQN,¢îÄkNGbƒõSa_<xdû¢[@ÖÒZQò®_0˜· ú8Af µÑ!yŸjîį¥\+’iu
+·ìÈb{Ëôœ]5ëçîM¼ž†G<^óC̨Yút‘÷ü¯ªñŠ­ úJѳÄÆØ7‡º®¿¤·ÿAxÅ£"(õÊjD«&0ù|ÙÑ*‡
+v%¨˜+yJÕÇ}RvÐn—¼¬îJ˜ŽX=Ð\÷—¼ž›Ÿ4 jÓ§áV=µL•Ð
+~š÷*h5[Gˆ®“óÉl‘6ò¹3T:ÕÆd³9¯‚fO]‚§ÉÍùŽ‚¤œ*zÈ@ûÓ}_¥|O²ö÷Ù½ñ›%çì£:*Ä·˜økúÝväÕü¢±ÄæÄõøFϵ«(åÖÛ8õÁyLĮ㋯O|FCxG>–²H_¼K/9~Ïèv¯ ¸š}ÒD§ïµPöÀ.o…D”äý»’ƒóø*&¢·1ÞófP0|NžòF ÷ÂPjk¶£
+(4—ó
+žQÉ$U
+ >á@É=ýŸ+|iû*nô¾M¬/ã+ö¼[9Ø•v[j` îGh/> /s{ÚaXÞ
+Éy„[F%ÜüÞ*É7u/§¿Z^
+îʇAF×/o~ùÉ™KÊ»Ln$ÎbFg)KRû²Á˜Iþ3*àf„¢¢ üY?ñèZÑÊþÇq™7µq˜q¸G¦é43í´“´“fb·I3vœ¶ö4qìÆ‚/0DØ` dŒ1!V·´«c¥]I»Z­î[«] û@ @È„äcÚIgÒïR¥_àýç}ŸßûüºYØtº¥àwz¿“Q
+%`¸–-qÆÕ”zòã&Åk§¥¼º—û(ª||¶€=»øº¨ÆOKjÓqV.8Œ+–1½æŽì˜7^ôØêæ@uc\‹Ww(DžqZ„y/8SûáÖ{³Ë!ˆY ¡Ýa k¤w%càÚqv÷2¿[ÓQÝšÆÙÊÉ„½›«øEÇPø²Ì_<-¬NínÀ²RØŒÖㄧ™Â|•  áÆI»ÆxÓnÄ1d"¢Xû´d¶’Ëiqv
+Z¢S‘Ʀq\sNË°«S1¤ºu$Ù.‹€zˆyg7¸LklÊx%JJOÂþ„Yp£èX½Õê1|¸)]ÞOH•€r¹W^VÑX»¢ 6³Z²± ‚5jíÁÞ†xé0«3¶{\·ÿïRÞ‹Ôêx{kùA·
+‚±ã …[¸üvX ü-…€}U‹áIÕéV¥]nU SÀ•Ï³ÿM^ïë`²™V®g=Âá ›ü^ÌN.û1×< ®£Ì•³±àÛC/¢î$aÍIJ¶¸TέúÉlô+±[¥y@Å}´2:òS§`öâIÂH'0Ç‹„Ù—÷ÀŒ ’`ÄDÌ‚Ïæ¼µß`yâÖàCQ,¸¼åJºÒΰ%„‡%6…g‚”Úîpgyò.~ŒŒ‹.ºVˆoB*×d1MÕ]àâwE™ôûª\õ]YµdÚª›Œé­ãqÄ62j†wBâGUû›}Š÷¨•eÍ×9̤4¢+”˜{>‡‰FBÐ|_ÅÌíÔìJÉnÄ!é«¢ÖöŸš.ÔcŒu_ºÿ¦¢@~ØÑAJ¯Ù²‹Gý*ö’Ëù £3ÛaDÚJku‡QÉ\– ¥ Ü›;váh£çºQãBçÝ.ìÁš“{ÿ$ Éq»VÌW}Zæ–C5]õK&÷7”KûQ!ý ´<P ˆÆÒ.ÝlÈæb;´$-bBÆ’¤i.Œa3aÒ#¦0¿$` ‹žù+˜©ø$M™»a=ÐH*Ø{i¿×sØý„ðI++bµz]â0-fî§z®–
+SGt¢›å †W¤4À¦K8²WI;e‰l?¾ò°ê_{p‡õíœ)ÜÊhàjBÉ*ÅQY*´ðŒÜ
+çQyjX ø¾€Ùøy ×ñµC6‰¼ÿ\[2üqnœÿ`Zq ]wÜE…¦Á°^;˜±€´º_Ã:Ê©Ý$áÌG̪ n³¤µ$7 ùy%àºHž”Š¢G´.z6ÞOâî&ðGoÉxá+ã´•÷&n/þ~a\ñæCéÛ6@×”4;ÛÄR£´@Ê*û6JbsnÌÏ$ñ›ÀÒl•4Яày.©E±[¨ªôL%Þå¯:/NÒÖߢ­¿óUßÒÏϧýø¯ï÷ýdàïßžš?wÈ“qÔ¾pH­t3¬Ñv–3QŠ§Ü:ó &ö]GÎÏâ²÷L{ã`ÚŠ.ÔBVC>èBCñÄ&×]±rµTÏYÏõ]û…Ÿ±M:凃æ8ƒX_W´½Žö<3—ÂÌ¡—y YKyËq§[ê5 4;]s(L)çV¨B™UYB·(Ô_33¿"¹¼KI=:¬_àÿÁ/Q l»ŒÂF•TlZÝ <ÈãŒ/«vå\Òåãn|)î2/ù1Í"¿ÀÍ#$0Š¢9ºhÅznõ©øÏJž¡†üÃÜUòò$mí íÎÒ™Û}Ïû`€õ»ÇÃóç 6x9€˜ÓA«²C”=oÛOðoûØw #é'~K£#ã
+À{ÜC ?t•³ºqî)ÓûÞà í/‡Äo  ßúÓ‡×~ôÑ™ül”¶ú?Ëü ÿãïBŽÍlf›{“c®Är3+„‘£›äì’Rt¨ÝE')GÊ=WcŒí3óù|¯Ï÷±_¾Ïׯ¯^?½ž¯×ë9H˜¤ÊʼMÿ¥[+¸Ý]<ìK…ß·8m×îé¶ãm‹ÜéÜ,/Û[µJ{­V½Ý¦;ѳs‘ˆ­ªdÓø%]C©C­¼Œ¶:V*±†C&´F²:bMòÙ|ÛÜűhâ:Vuù4ö?É/çæÿw'7ý¾Kÿ¾ÑlÿëJçy8ÓÙnŽ4Š³=›Æi]eY4–VƒXUâÐÛz¦u¦Y¡ÁÚ‹·iï^™©¹Üžçýç‹ÖûûrÉòãpAîum¨-¶#‘Ú|'’-ý 2¦Žsz­‘£4K½×N¬a–—ÑBêñ’§ÄVKpJÞ0ˆN¨ƒæåRƒšñãOÄ’D§J‰÷­kW;Vëþ¦×ewœh—Ì'£Â¥ï ÓŠßS’bÇ9g©C®xÝŸÙxÇ4EötãÉ-ª×”fÃsb³6(Iïê@9†çO£{â4‹=¾­ óáF{µ!¼pι,s¶µý¹ýMû¸è6”wƒèÚ‰$BH-†0r›=´d€!ó†!±ð:€,d@²Š¸ .
+Bžf‚\TOPkó\8‹¶üÖ©ÔVÝxeÌ»C)gÇ:O±¨ÖZT²ƒzÑÌnñ$o!”ýÄ E?°rÙ%QÌß(šá¬ÎNm¢Ö8Q ˇfŃš|rh žçtó‘Ûª­‰¯Û«ì)e¿vfÿ>Ö¬ý:¶û<ÛîuƒñŒ©1ž¨t¾Aþ«ÕýÁYíÖÈ…›Ås<O>‡{€ìhž l«c„t4°Þà*F‚2RÐÏê>’àzž¹Ö0)L´r(QÖñ!_WF«<.÷ŠÅ´Ë1,Îi}d¦às6µ××Õo‡ó/Q½Žd6š™Ù#¶(BëȆ 4¼ŽÂ‚ȸZ‡æC¢Þwƒœf
+d_qcì­¤ñ G2gê0{Œµ—A¥ê¢‹ó:žàp“aØŠž—¸r"Ü2/j»÷(Xw³£›ë®jÙÝ)\º®gIoK{¹¾dB—5Ÿ‚–ãx~¹eC Ñ‚_~
+ë2¦‰ö9ŒØë E7hÒKy ü-$¼ïÅ!$£ bÓAräceT…šV(€å¢gŸ?²j”k«oöÔ ßve3GÛúY™@™%®ç
+$G%töi¾Õ„B ÐUHU2 %½dæôC?a…þkå°8<ͱ‰­ %¼Œ#Cr‹Xa ¦ë“kåDýÍíÜ»ÝÑ*/Ü‹‚õU]¬8¨jQùÆ¿{g~"»'NâúÆNã;ûÜáD’VR.†akU¨¢ È«èJ†¤‚¤Ìð*¦’[@ƒN3WàF/eo~õàÒœzENã I•ç ùϲGÏl,Åò*­˜˜V8A×È¡8¹ß‡âq¿×ðHVÁäÓ£†úÈÑÉþ‘ ©ìØÕv<™þR Ó¸Éûkú‘Ÿ§ZûŸ×ºÏn¯Ë¦\¹áò•¤ñ5ŠÎ9Nì؈h «Ÿfå D'’!o3z@"²$"z
+ûº- “,cº])λ° °ß\“5÷Þiê÷¹±«-Iß–Y„5•8µÙ;0«¾ 1§wr<‚>¹ÿ¾®Ë‘ƒ™
+IÏëó/©à`jÅU$Ñ‹Æ^mLóˆ5z€±•0Í;,RKöë7´Núýž‚ÿ†êD«©Ä
+{òBù—{ÕÂÃ7J©¹×‹=`sÏ\i˹ûˆš{·Z”sêÆ@ÎɃGݪ–åT¾Lî 泄±ç~WT±Ñ)Üäʤpguº\EfžÁ-+XŠùcÇ+îêÍî¡ð‰.r¬¥:òÓÅöœSy9WnrÎôäœ<ÛœSô`4·¾YþcJ}T„îÇAmçÂÇlE8XDC-ØA‚ àbo±@³ÚÎоo%ŠVaÆkí}†ÓØ!Ë/ƒøB-ÆøË•â‘¢
+RîÓ顪͉ºeþÓs—/‹ÖX¬ JÑÖc “üÏË
+K&¨e@ž˜LëÌr„†94Gä+e±m×ÙBçíº½ sÀZЈQæÕw.5u
+/’øáR¼ R>*”Hä®G°è‰Áfâ lYiÄ+>vËšŸÞ’õÒ2~»C#«Œö£eº[$‡JhG!‰¼û¼<WöLõüRöOöåÞyL=T‰Òþ½ GÏœG³g¯¶‘¡«Ã‚`« šå¥B.ö2ÌhZúïï„©Ý2S’lPË…ái•Þ•æL™>–|ö1I°^ÚG½ˆžl| —{ôxmÎÙüÞ›_ýPRG8‹W©À“”]…ýc’6BŠ‰„Ï«ÀÓjSA
+ñA|·Ç5fì(ÀèíÔë‚âI¸„Ê Iuþظé*•OÉ •j£¡\k²<u<Ø„fl@ö^RÎü×<b'>Ö²æà1é÷ZB~·Ì[Gt:e…Xh¼Ia¯÷S"WkQºåµÂkÛ”*®$*©ÿCe«ô‡'(Åá*´òX3ÎqOOþÓdšŸˆ#!å¬KŠßKq‰o£TôÛ½÷Ó¼ˆ½×0œ ¬^¯××)Kýš*L—Œp7±ÂÒFÁßN^lÊ=“ךs¯l$÷a ù(ªKz–D´HØðƒÁÒõb5¾vî¿ 2_nøø=›9eÎ7Õí4p‹-V¸_oŽõê ¡v½5ا¼]
+µ½J¦÷Ôsµ©&<ÝyG’^¢°["C¸Ëä„ÚÝnAÌ„lX;¾¿(¤ýgÇû߶HõûšX¶
+Äïæz¬¦—^Wo7jjA³©°"XÀáÛ­ž^ƒA™œ 8d¹as¯WK¾·ŒðPë‘©±ÍèÔøvdr$©#ÝÛ±ûwg¤üw³2ñJ\ÅKú”8¯™Uežì?kQ®h-P+W‰T Óíù8^°õ刦š@7WÐáJº"øX¨CžHôî
+­Ý‹%¢õy£éc†=ô.J¨ù¤âö—Žý}b{ÖèŽm:Èc%[ìN¬Æ·©,ó‘&ÝÌ”§ž\méVŸ®jã<ÇNÞ!q¤e: ÄBzúœk¢~Qƒ*
+²æm‡h¨9'•õJ‰ ˆ–œñ(úmwÒãb]K)`WÙ­j¤Ã¬B0.£0¹R ¹kÙ©ÇïxTÄ_ãê©/s
+õ§$¿ê¢7mBÌžý¤X´—Œ.Oó:S.IG@Ï­E4Ìʰϧr8ÝxÐ`j€´²êˆSÖ‘ðêˆI´¥Û)%ûmBÎØMM1÷ç¥â¯YÉäoI2ê5ЙŸÕw]Þ™¦7¯@Œº7>16PRfܼÖå°œ¶ÒpÒ°íV]rȆÎ{Ô£WBVEKÔcšõñ[ç=´ê€F¼·…öSRÞ¿ÓäÖÏQ\ÙÛéáò7ðÈãÕ
+:”{i{h7&oO31_3
+å÷Eûû¢P´%4Îèk1=ærÄ4RvX) 2¦=hU&‡”– ‰%‹ú)Çú»Çöy¹Bz×i±õ†s9›^>ês‚Þý9Îèý–×,؇ñɧgR꺟Óvì9DÜ£b`o³°µ'榬ͭi)$Lº9 €Qè¥!¿zÆ/{µ‘â½Ïr¿½‘¨¿mLß×ü¯óãèß³_³ôáÏé ̪öb%­Õd€l%¢ýº WíÍ(å»)çCZLÞMs¶¢Ì—›VçjX>ú&
+ùÅ’ õ«h-~%|bb¾NéÐÈþeh[éÏc Æï¯_xåì6Ÿ”ü*m¢@³V"µÍ‚çlŒ¤‰šÿ;zwf‡KŠy¨’×ë‘ÐÜbZ§oTéø€úÝñÿÂ%©ÚSÏíò…á]ñd«W1Ö5®¬… ó³13Rõ—W[nZB[vgŠ{¾÷fåÒ5'>·sÉ+¦!´˜ø”Œmj"cŸŒÉ° a¤Ûk˜uéßñ·Ó0ý©Ä¿)@Ätãبvv¨Å¶joÚJëÉX¹˜ü¼µs×džëlxpƒT¹»ØvïX…¨»pRúÓ66þÄ86ä!~w.÷çܜɴ“C«ó~³Nªó‹Ñ ´…Ò{î`¢3;Œc¦,¬Á¿<u²)W채Sg[h)ðKaŇÈÒ”‰ùºÏ¢.sOÍ‚8ålÈ…oz5ªÆUû–ûï»çzïlOB
+–~K¨S—R3`z!ô'¿
+›Ç}2D¡GÔu×ù¾½ààôÎ]#CÿÛç ¿zsÙ]:úÔÍã\ú-g{S GðRÏBËÏÎ…ö‚€ôõãÓ½ÌEP¸štpˆgÞ1ê_¡ôÊ;>×b»‚²Îgâæ‚€¼·èâöy9üeŸ7‘´ ÆVwÄÂθçÖ3‹ÆÌþŒ(áç›Fá%©jOÉ€ï.¬†>..úÍøQ0t¤ã!266ùÜÁ¢Ù˜„œ™Òsn£õ]̓¢¾_JZ}^ûÛæ‰àŸUŒÖ{Àæh{LA®IªiÍ!)µ:(#”{Åè-§õVZ=Oêù¤c%­iÿíðs`‹Òrj¡væ¶ÉmÇëOÍ3õ7gûýÍÌm/úæç_Ù)=QEǯ¡Õ¦›qUßäžP›²pëÄHD3:p¤ÂU'ŒÔö”y¤'³MjŽ¨úŠƒ’î‡Çùø²’R{¶ËÊY)ëu\‹© JÅžeÔƒC®4¡§CÝ"dÉîBïÏ!%©Æ»†®m’ÀQ9ú‰g¾õGßôÖÁû¾»¾|OHA­3^]?\GT%ŒOD}S’ë‚«¨âض<«'Ö¦uÄšÀ²Ð8Ùò½˜¾–Pt\9&¹6.¦ÁÕd­#½€žÚœT“Ài3³Ï¾6T§â7ÿ#Ê}Öăå9º:m€½Œnµ>»ta+½3kCå¶'9€yJ7ññ "8¢ê.Išµ)—–ØÐ*|EX©I›éýaªÔ5ßuÛ1×vÐ ÊR:tiÎL…äkȈŠX×Q“Ö1Dh“ؘÝa /\£˜„ž
+‰oâ«2,ø›‹Žþæã
+òCOŒSŒŒ}v°rq)µ7¢Å´xpDƒfRÓ‰_Qc*‚rDÑ…sŒœÝ¡öf­˜Ö”QŸÛ&µ&´Ø*À@iþ¼79}éŸ l ??RêÒ+Ï[|BE
+ r®
+gÑ/®iX²Øb¶¦u ÈÁŸ¨ßü¢_Cøƒ° Sr´N¯w,¢
+Ý2êqÜçíIÚU^‹:DÙÎ4ø{ÛxÕuÇtÓ­ø:¡ÐsÐÑMÌai¤ûßtj"ÔÉå¢þg<NÈǧÖÑÁÓ,txþ$ j¸q(nûW|sà) '7d,´Ž”WU¢Ÿ:v>Ãq†#J<(°†*ÌçÅ‚7ÍŽÅŽz?ôßu®Àî¿Ç”w,Á
+|…SØô£†º¶N(û»ŠºþIØrëãTÃwþ $ÈüV&€ÈéÙp@=‰)†AG⎮¹W·­ãÕ?ì
+ëþ±7ßYT3:åØæP~çØÖÐj#2TILŠ.ˇÊ܆›+˜g׫ˆW_ìBá¹…ó¾‡Ü·¿©¼áy_~#c‚=9ÿHl:µPºÓzJGTCiJ˜„½€øÅΡþŸâ2oKzOÃø5siÚ¬´ãѦ²U³d¤T´Ä%ð¸/à
+(ˆ
+(›,
+¢˜[çXšin,Jˆ¸²‰¸ 2B(‚‚ₘ§™®Î¼‘ùÍøþñ<ßûó¹ŸóU.É»ÈÁ»Ø%ž9zƉ®¾xS„m á"w¤M¸}%·üdž’å™ÄÅ:ÄàÕˆ0ëHE—¢Ã%.a²yË"ª†ø¹Õ_—Ú8Þéúœ}1u
+›´#-‰s}¦¤îÍñkvµmt³„’l“ã“u0¯Ž‹?P³0v &Á2Rúp}°ôžiõàÌÄ¥m+ð/ŸÑ/œ“ 'ZFñ©†Yöm‰]ùc£©áDGC[eᮩڼ³•Î·û3 e»rrêÎ>Æ5Žï)ˆI§záXÝ€³–CŽ¨YÀÌ[c„T¯Šœ¹9TvÊ*’íØ=‹?ÕR1[L¼y¨ì±OßÊ;Òñj©ˆm Ì©ÄÆ[EØžYjÖÑ"³äPÇB}‘T>?]cTÞk<_Ór å’첚DÓ@Q¸{
+Ÿä]ä¿ñ­µñ¿®s)~#ç7wõš>Î|ÝèUü×ÚÚõÕ@.Ü–c!¶±òh—²6ðçh¹™iW
+Ù!'øæ[ÉÛ¢ÚDï =ûp¡¾`WNÉ9W“ ε•ÐS )Í.§$mŒà£NÕÜÊã9V±CäTˆ ×uÂeôç›oI¹z2Ë,Ø×6¢Ô‚ÊX'/È7ËF[b¢Îô¢# !Õ1Žz¶Þ›÷ËzwÁÏv!ù¥IÀ†¯}¬~²+¥¥ØE¤Ä-)æ'Ab¬¹ý`­¯ì’6d›HqƒøWDÕiWš‘·Wß#l QO¦ÉN6õ¶è±€ ôë:¸[RÊkãû‚ã;øu ~ÝО{Ë!Ä>÷ÍQÒÍ@³a—»ýË Õ¦aT¤„¿¢äf]›kͼ&¨M¸üeóÏ}½À 8Õ!«KÙ–Ró]ŸÙ$·’†sâ¯)8ÑúvÈ Kî?¼Z¶{¦±Ô2L˱`?L|Žµ‰âQaà[q~ÔæHQØZê®Sþ†`P^[>‡Ù…Å÷c˜H}GÆõ©Æä@ +?µ
+Êžë~ϲ‹kâ縘/bÀÒê›°d-{â’–FŸ˜ÀMÔP¾.ÂÇMðrnì(™ˆ¯ö@Ó€²Šñ/VÞgÜuJÑ ·¬<êp¶z¶ÂÆkzò"ŒŸJ¡ßVxTŸŽšüµâ#M]¦]Œxl̹ã/ù ä"]Ò^¹{¤fTßàó¿¯7Õ¨êàNàÀ÷9%$Ч’
+f–UT£iË»©ª‡^±a®S^ˆ¸%wjàOþ¢j, ÷M7¡<Šª„•žì`u{Êõé¦Ô뺮Ò»“<Ô™¡ƒ·7ÉÌv+Ù“ÍhŒáŸá”8%„„é攀qäÂâÛÜ``ïm9ª}›¸;AJxí”V<Ów¤‡lôD9¤˜XËPÙCÓGLŒ_ÛÛç’÷¶/÷P!‹o‰ñæüK¿š’{<W“²=Ž^í/ "§Þ &}[io=]j¡»¦«^MwÆ…Èè [£¡SU/w¦èéì‚Ç?Ívåƒ}†¬KI„N°aŒôðKÌ´ð¿KII·XÀ) ¤e ÅÂÂ.™±‰ßîîÓÕå9¤…OÖûà!óüW"ø¢þ·ô_vÌ\EKÑ-m7ä×vµøË÷Xˆu0=d‚¾4Ó’qCÙœqÓ.­JÚŸ©Ë4¼C<òéY>Ðÿdø¸-€ÿN)*Á£Ä¤{³BåxÈ¡¦¾hSŠ{eú˜w{GŠ;ÄDȶ„¿?GJßš¬M¦dÜh/Œý ÈrŽg¦®Ø2ZaAGØ€=»ÕPÏ4à7ᚪj:²‚×òÁÚ8œ¯&ðs´e
+€sæøYÖé"˜¡7´Ñç¼&Lù÷V#
+¼IÜfD qîéÚ¨¯Íö‡ÈáÛÝ觓‚d—6
+Ô¹“vRÕeºè/gÞ£ýfEh_ó ¶ÕX~—á±ÝóTÓŽy2)@Þï£Áïh?”@ä ˜àÉÚDÐþ r:SŒ<Ÿæ¤Û•UhëLYæÙ,9þpt9¸i„›0ÍÏñÛ—2S¯«ðû2ø`j'ýq6Ay¹òé:Í…ÝÿuoæA"Ø(É÷-Iò¦C]·š‘^
+쇆»Y­&Jb×›‰OSâc/y>S”t&/ŒÔ‹sý¶ÅèçG2pã‚ìúæƳùòœ£‰’¨CYE²m„å˜Æ cŸ›¤ùŸ%„0›¢†i›k(ÒvSŒƒ¿z&?ÏÐÃx¥æ¹éÛ toCt]h¿õnlð¹JPhŸ+Ï3I‰Ï÷úða¦>
+ÌØKz±'É{bEìa#u¹ŒýdÈõê;îʇ·¾‹õ€G
+#$Dºõ—#BN—«HeUÎÚ»,Oèøµðß5-).Ë"Ø=Û >᧩†cSqW«5c?)ÜØ›nîÅ¿:"!ÏÖwåîöe{ÿ︶úJÃ%úñgsÌäÛÍñ­®­õ§±áýÍFY6À
+T7§xY&h1»Ãܼú|Èïü$ßëå
+êÅ"s9ÇJ¼P2á§S¬X`WYë=y~+˜GÝÅo|ÇËÓý.^tûX §`·+•ù—s•oý´˜ÙêT—S%/닼Š¨SÂu­8ßí.bº9÷ÉŽ$?øЇ.8¹›íX_`Öþ_—«ˆ–qv’Aœï«ïxÚîHs[kŒqÞêÈð8ØæXÎF| Åþ«“ýàh¬(åFS‰|±Ù…y±Ò”æoøpŠ”u,Ô}‘W¢&«ß¸ËÊÓ}Ö»È0S?+êx” ùÜG†,·ÀbfœKoüñµ¶–¶?ÎŒVÔe‚&Øñ®F16Ä6J´Q#-3 X+ îªlÆ'Ÿ©Eœƒ ^’º)ÿéjS^˜®g"@Φ QmrH?÷j؇ÓìûR ðDÏ2äÎlYÜÝÏ-¨Çº(}{ö³s.âï}A±yŠž$o@y+XÖ…fžE%¤~Ûä“.5ÅÉVåÕ©Š ¿Þ,'þenê¼ÔT1¿(ð«µ
+Üß&QÝ÷íŠÂÓù¨Sv0šbÄ|Uó©·ºw厅
+‘y_ÕµT›ª<ýr¥¦àFÓÀ¹^®ÁÝ:ýhŒÙí'™‡‹þ°LpSÎèrAúÑLiüN5|KL 8ž(‰±ÏWc,ʲxC/Îïdˆ
+ù¾½Ç1__x(+®µdz­µ¤xÓ!vEEÖíª¨ôh–‡ZhÍõ6—"åU¨•÷hw%?å¾R€pãÁîOWD9­SÝŠ*!n^”íiS–gŸ©j æ!nœ`€;£urðæÇÂg'ã¼d túnŒº«"iéó•^L 7ÐÁÖIv¢i¤6Cû‘nègÅ~[ki°Î±“­S%p»\H>ŸцXó
+¼­áMˆyæ´ôþC+ _(à«Y,øRù¬nLx0UñÛV[î“‹•ZÊî
+òf…ûö»¶Žy³*dXä¬øuqŽçþ8 ffÄ ÐLæ&Ï5¢ÎÔ"Ü }ØÇbYšuŽ—¾ÞCößêcDØæ8(Ë0.hGœåó¹ý
+H‰Œ—M’[9„OÐw¨ Œ‚ X»—¾ÅDÌʾÿv>ˆ <¥÷4ÑŽŽr»”âÈLʘcŒ¯é´Çr5oÚeÍõõã/1™õ!ò¬G¨é²FÄúú™{ð¿S{LwýÇ }ÄC×X}|ýªÝ D´e7 Ÿýç/ùú;¡úæѽ/Y¾7µÙm­n6l~ýÞ ]Óz›Ñ£ß€žóic„X›áfÒغ 3ælj|© :eZÖTåëß{+oç¬fR[P é.\y¯ã.-«"VˆÖ¥µÙW!‚=¥ªz‹üa¥Ý‘ f>8FÏ‚[ãÀ?7hÎ¥1L[è èWUgôÑ|¸=!ü³M®&q63¯²”{>!Â|M©×†L“` …6»8ãÁ%VÌ6g÷¡v«71s_ë(tîüDpÅ%:ºA¬G[Nuš©6}mô†é(Zo6ôì£Ó7¢9Õ…¹œK>wÚ7¢#üÝšÒ+·s&A{[§ßm ÝëcÊá_‡‘ ½:•ñàpÓÖYçʾÿÏâ¾m§bBÂéH¿™²"<¬hM sÔ(úò¹D½O-Šq$éb>ÖF¬é|ǵ±
+B‰¢µÎN³‹ë’iþºþšAI8N62h"Ï›9çz:g>är3Et4ärêÜÂÑ M÷ë:ã±¢çpFTÓc4®¨ÃÚ(êÌA;bÐêFË45‡a~¸Ã1lÆ€sv® §ô^ëÀqjC«‹‚¼ôµù Âhg;BúÚê;¶ÎßtHŸÅÓÔ­¤éðU“¬2'«çÞü‚1[¥–ȧ€€$Á™^1úbH\´«3õ´–¡7«>lÑüœFÆ/P'©f.zÙ… È'eúúy ²‡ƒRwÓdü¯ z휸÷y†ËƒÂ®ÖºÇ†0ìÙmª¹3ºÅ"&¢jÏoÑ{ydÇ9äªu؉äÜp¦ô_lµŽ M-H÷Ñ¥3£½¨¬3÷Z2Ú¼ÔVCrˆh‡}ZæM1€ )i5V(º¡íi3bc‹làŸ¡ù¦`–Ã8Âè"T:8…_5ÑV§™Y?n±ääFZl*ª†Å è×Ð{?/|ç^Ù}a%HWSL0ÀbíUcCµ:~ªvØN·±$Q
+ƒ¿®Ž€$ç÷*ÎÏ…+cj’Ì|âÞ?\¼Q•4>8úª±p&­™•Us¦ RÎuq‡¢³ŸÌÓºLx>ðõ¯»„ò9u¢lN»]‡ve£È»Äá"VÒ¢ 熌"¢]g<Õ¹¤æPS²ž¡¯ZTï®D¡ŽølÕ¡/‚îdh‘ZÆ0&ôËùé ‹Ì
+CÎGe
+_îŸ"Ì3¦¾ÈŠ;u1ßbÖ½œŠšàl< “
+ZŠ
+Â\èDò?C#‘©µN˜"ƒO^\´ŒŠàÄ•;ÞBnN¦‘Ó‡}€¤ö¤†NBÈ}§¾2sÄð+æ×Á±²Uø#F—-ÓrMÝx†}úYâù Ò49…Ñt ËøU}º!´Þû_3$S—ù‚<È^¡Fh]>«n[ùQE€# ·«À™†¥:·Š™Ï€„KÛkH?LF@ñ*Œ’Å‚¼ÚþLwJ*¨8§¹ÎÁ%`œ¥çS§/ÿ
+’ŽAb›KRIÿ˜øÐCbÚÐ%’³4ç;ùVk}¯CrÁ,¸Û¨­Wù´ Î$ÄI~¨ùLYï7çVþð)xèÓ
+>­ÃDMÞÐzÜŸªAÓ‘ýt+@<F)þªù½VÇP8‡‘0Wëôô}¢ê¢ Ÿf*böülu5˜EG30ÞŸ¥er&&æöNƒ`*<Õx¼äæR@,Ü2µ!Ï_wHÅc?{>6¯úŽ(J¼õû²Ê…4×£\Èww¥¿—æ: -·S ª~ø6O¯©“¼rHÆÓ›Çæïêeð9Ž8¢¿½Hï@ú¼Üã }lÐe»7Яr¦Ÿûè™#(ÏVÚz£+¿?€ààD>’ÅsrxÒ÷žO­`•£aä
+Ù.ÆS
+Šµze2ÓIl«ø1ó‘䄲ŸmP&<#!$ö:0Ë×f½‚¢›I³'„@GÙŸCV2ã’ï<•òò–¦%µrÎSß19Ši{¦ØÏ‘¢v‚:X9ŽªQmðüÀº BÒ Brvê9ŸyW/LgŽpJáyå{¼‘…¹˜žœHµ ã|Í$mWÞ:)š±1Ïg'¼ØˆCžÐÊ€‡˜?Ÿ62#Ÿª¯¡ÿËx¹ìHvAô új­EƒïÇÒèÔ†
+¯šãD’Ó$:Šp]/ço^n7Qi,$[·s¼ö³Ê‘÷ÎõÔÌC„ Ü€VtŒ&Ÿ€¯¼Ü-†©J­áÐÂm·$x:™"°%ÌéhBi'*o¸µ
+̺˜É¨£°{€+ÖuŒÔæ‡0£‹Ç"#Ó\ZÇ3ójšÄ´>cš_©x›Àar@ŽtU(œ»„\±]I$;biX!Fp®'Ñ7ˆ˜ÛË’¸\àZ´ ™4‰¬cݺVµ)H 3ŒSºBшhÒÃûs€G†Þv"š™äÚüÄ¥›±!ð…º3'ƒ"A‰ãAâ z©°B§q™/0(IGD|Ì)0M(e6G@Qä‹ï­íǼ‰{
+…FáÃF±»
+hG)œÒBŸ£;…Áa´‡<˜<>aIs¤¯Ì"Ÿ9Nè |j{ÓÉå,.PÁÞº&i?æäç]ˆ]ÛÉbº´ôËñœ(Q­vá÷ÙDí»¤-ÛÕ£ðlÊNhœ–÷Å‘­kŒ>¢§X¨LJ‰I!Àq£1ÂÙºêä­*‰°N9ær´yÇ'íÜâ©2g×yªïɺžÚôa]:t{sÊadv©Fo÷¤Í+³‚Ó]˜'Äj»yÔw+ ŠãÆn~[sƒ”Ñ=‡­'ÝÜíçöºCÐý•œ>gêå þa[V“< ‚Âj5Û¡Ï(e僚È0¨(âW¡Â;[ /„•ÒÁ>t!¤€{ óÙ…Dø'i»óÕΪŒˆÔ(ïŠ `,'­§#e
+Î(2¤‡
+Ñ„pùõ ®—°¢.Ívlbž|åøM1YTbV~Q£’…IÒʶ±n/c`lA.1M꬞à.­Å\¸+‚ñQÙº…~Dh1é=lBì¦
+èOd÷;©ýJj+™Î<ÿS~&;“þƒÐŸoŽ¬<ix§<nÿüMÉtœfèˆÌ¢4É´£xðömñ<…r»ávéSü(:E€yvðÌ8UÆ3ø(ÿ3‚r•˜a6@k3‘Á/±¡ ùu%'uf¶å…·ÿBX€‹ŽEøŒÙ
+÷T¾m¥|݆ðfcÅþ—@q±UhÐ_U
+07è‹öU¿ôåû
+\¦ôùö'Ùÿíßo?ý׆àÕùüãíkJhXS¸†»âا©³uäeP‹sSlch驇ÈÎ!lüÒØÌ%ÍÞÑXAvÔBhQCðõñjBà,G[8<õŲ.y© ÖˆÛiæGeÆÎ'¿B`tngë h}Šzê&Ä®Š’H]ù‚‹ssé‹ é*¤nfAÇ”ëõ«<,nß]¾¨Ž>‰ˆ•¸¯ñ&äûV­›N1=°ô¼y¹í“Ah@˜Ðu–ïÛ>ªëٙݘ×E§Ò0ôÌ;ŒU$šÏÎŒ £.|ƒØ6êbW=7|{ÎilŽÙlÆïø¦Í+³ƒË¡¾?@Ô°
+%”÷²ø¨ä±S…øç
+¤ð*mäï1–mVú0ßFH€™©@Ía„O
+Mû&Íä.‡ àê&žNåP…²O» B;ö±a ·Œ«rt/ÙÀš5F¶ö!' é?B¯RC§~¼í‚­úœoê ú!ÔúrPÖÎ+ÔÐX£› *¦@1â4ýì)!ü<D=CpzÂ\üh„ðnÞIG‹…0H^@Œßœ5ägÚüÕk?(kä‹Óë­`)@%gÿú·‚(¦îNŠub7¡%³ëüåÂÕ9@†É@àT I>"}ˆÓÅ°‘ [ÈæUir뼩È0•ZrìÞ•!W
+J?'˜“´%0A¬eX\Uw
+±­
+CGåaÇóC1áiÓÚ-`B´^JµKE¡l‘Éؘ à|Ûtóò¯Ùò 9k#¯Î‘örZv­œgàåð»I൉`f9ÕeÍȸhè¾Jß
+½‰®˜Á¼±'©{NVÆnaý9jµ¡Í`Ž”ÓžN VC³°˜5ï·½<„ØØTŒ¯j®ÎÍî‘M”ÿì þ´>u!VMûb>Д¨N]W14üNŸYÃÁæ-˪ÙøT5,K9t-ÈÀ#snkOd-_æ*ÏNA
+ì§DÂô÷ãm4ÚÂ*»î¾as ]’ú,ó®#š”­5†¡¥ÿе=ŽaG‡rež( Ë:¢E[[L•À³^š½­¿Ê¾f@]¬}Ïw?âMÓÊž•Ã#`å>«\†®d‘F¸:Nz%ˆÊ ìê ŸÉàÿŒ—In9D¯âœ“\»—>Štÿm¿ “%éKÐÚVˆEæTŠ¬
+%
+’§ß6°“‰Y1!á§!u|n\|'‚ÄSó…iÌé‰P2'÷n˜IÜñœL/˜(iuêgG×|ŠfÙ“£¾Nž(ÁÒ¼=±ÿtâŒèî¨@§5U¶=žuÂmÁ¦ôpl¹“Fz7Á LÑYª¦
+Yôá$*Ȇᎆ¯ÀYú/8F‘þ‹ueV _l|‹{€1ïbÞ"ö]z=†EU¿5Ÿ*šÙ9¹¹cÄ`YL<›Ûâ]à:4*•ËÂßÏya
+®¬!O5ŒO¢-t—‰n;ÝܬÁ¹^BÇ‚ÄŒcÒùS:<!nÖ@#ŠãMÉv¬ûz
+cF Ôu?|¨;Šø9–qGHÇmÏÁx"SéˆØŒØ«ÑðòÝAbA}I]°u ÂÃ6¶RS>ù|6 æPNˆü]ËÌüçFˆ0'¹ûfqL¡Œ¤åS:«`˜¿‹.\˜utÍ;ÉÒѬÌñDL_1ÿŽ˜—,ûþ
+cCjÌÆþP“hÈ£îµçbÚh’moT–‡àR¼ãŒèp0v†änÎß1cÙXÄMvd“
+å™,úYLACaxâX .jzŠÌDÐÈa¶ìdKƒ&Ÿ¨Qö}Šö£Eã-K9±ð†”÷òJ79lܲΤ-VŸ¼´ýRŠ#µ4ûeîµÖdò¢L?Úæ<A•åK's4„†•»íΣ*ØGqÜZtžD‡"—ŒÇ—s04{„r·}añˆ–r]i¿\Þ›/Ûåñc xg[úJa2dÎSCî¶âÖÍ“=ø®¡s9ŸíI0†9f5*ë6Ñ%pu$’–!èò?ãe’É­Ñè< ó°¦–}‹Þ–î¿Õs PdeFµµ¬Mÿ‹D1¸¿°‘XÍõ¾í!ñžØ׎ô$ÄJddŸ£U•J@wÄ)¡Ëî†h’3Ѫéå‚TA2ùpƒ˜uàŠÖ‰ªú‡]™ùåÛãXùeïÕÓÉÑvƒL0S]Òy:݇ â¡9š¼ËÀõòTÞº+“÷átäv^h²Þ#3âÝò#»<9 ¥4ÁnRAYCdq0{ [®eä0 ÚBJ,;ôhd-âÙhó21W§¾ÞêG€5+ÉêyáKШ ;™i« )DcSÝ/GKè¸
+ös1úÏQÁ8Žšäu§Ê´ýÑÕî„©m¬ ÷Ý!ŠÏŒ ³k~U5j¨ßÜϯ `WCuÓž@% {.ûÊK®‘ˆ0üçWL € òåℬO5nØÓUžL_ÏiŒR”·ÐÒö*þ*€Ï‚8…ˆ¢ÁæXL –Ù,l2´wb´"¢9ÔuÚ;AìcС-Ùur[ªˆššEð ÝFŸÜÀ‰`g䳊PL¹åd|¾›\
+WÓY÷Š–9ÍdîqçOkP¢,mx[˜—bO1n¥òιܻͥq¼WÝðžœ{_|x©Ó￘¡kØ“U>±¨ºvªPœãþsƒ
+ž–õ¨˜;fª73!üÛ³%­sóôqúZO˯}ážÃùÒzÉÁr ¾
+ÍC¸C?Ÿúó|ÚZx*ÏBpJô6Ú5Æðf°*p~vÌËFë‘û²8 îçì_îç.A¿¹Ó¯o¼œ¯ÅÙÎ
+ˆ…½ÈÏó¾}
+€VMÚéáL.µÛ¦iCbÁ d³ÎB×°Í1Þ:°u´µEŒÿ»1žB ñÝæq ®¬FO²ÿ#¶Ü¥çÈvᢩ#¼´ÏË‹x «µ×"‰ÚwSPIÜm7F…WJ¾Åñ&‚µ¤Mƒ­ýouñ
+Ç^9êó[@sÀ¢Ãü"‰º¥ý¯q&ndzòXÅØûAÓðdœe¥d¡Þ<ŸÊ}F0¥7±ã¤ZD
+p»2/à‚ì'&`y5 ýëI ]ŒºØ}™t¼R5aúŸ_RÿÑhs;ŠÑ$ €Ì´GÑG‹ñÒæ‚«î¨)
+^(ouqã}T¯{7âB¶Ù)æ›
+µ¯uúÜ‚ <Œœ² .%.Ða»èþÊ™>çÑv‡¡‘6¼òuòNò®Ð7µ-àÚ§€š,ÕW tø]Ò€7—ãdÝ-/#…daE{¶pƒ“aa¢<AE¯Ž  Â\‡ÁÕ8…î-5JÞ±dݘ&$¨b*Õ[ Ô“ñ 16µñ7¬-.Í#™æw”¸ 6âŸß„ÜAÌi&6Ä\þ
+Á«µêD`
+#lÁ”r™bƒ`•ª‚\Oa=s‘̾ñap™xÚÚª2à‰ŽXœeuiõÀ5æ2TÖQ+(ÓvÊPŠÑüœÔ©H±ØÒ¥}ñ
+9"»Í cüyïÐ,UUcO¡Óq‘ÁJƒŒ-Waºì1&c®Ã[WÒV,Ëoàñ-°UŸ<¼<ÅÛ¹UÅNrÝ€îâ 0,¦Ý‹6¢Í¼á¾à±T—ÝŠ¼^–M6—Œ€Äj[APcAÃæô\e]]¯I4ã“ͼÍô Ê>ºßó±ÍHßàÀýÕ:EôHº¡½í0 T«›È‡i€’ì—ÕŠãÔpž¸—Ç|Ãäl…P]LÃÆbê
+ŒO‘]@ž¬ª™q™PQŽýγ]€BƒÉHJÑa@)–†üë>é9ôcR”ëV0=F@^áªp_¿ž±P£BE}Ïçôên’
+ÃטÎÛ(B¬µµ‹U‚MO:?…uwÑg¥âõ•Tq„ÒÛïJâ 4\äÚU¬|!i9My÷­N X«Gÿ4·nu<Í!é.µ Œ§âœƒç©Âçøzz¨Ķ:¼÷nC×ìNsè¾ý­N]|*Îy^°üÊI¯ ?Gê{òD^˜Kj´‰œ_«w¯ÌJqŸKw CÂýÜnw
+í=rVÜÏ[˜ÝñÅÒ†4©ƒJ÷„âRæ¸5IÊ0BNן=ud¿lù)ÖÈÒydÌ !Ø¡T$¦•æž ¶Uβ‡\mWëP5hŒ ˜êÊ=ÄAÈ Ykðê…È"¿o0Ð_¸¶
+¸JZO+*½”â$ä³™«W ®„)…˜!|Ëû¶ß›œ»ªîùÍÇn&voÎÊ°RU‚[&âP¾Ñº·G™ ãnÌý…)µYÖ׌Ô0—y©)F6GPˆ1ÌhMTn¢‚߀>· Ýíò/¡òR—£qÄì6K㑺ˆzÉØ 8Ÿ­˜šôQîÃýŸ1÷Wj¤RþùçͽÿõïÛŸÿÞ˜¹Ã(1’úàþ«ü6a,"ÇtÌ×ÄD±O¨Ãú 
+\Ui­£Òóå7 NÈ{Oþªï’èÀ®F´:“³Æ Âvq"²ïL ¥Ú §
+ž×9½‡ÁÀ*SB"yûž Ûˆ.JihðµÕs[pqµrê-4oÏyè­tÃ×
+*‡‰é2Œ¥Á2×µÓï,Îs³çË­‘:D³\ÆÖ1"ñAfûë¤4…ƒ§eñe sf‡€¨ <!IV¾Ï£ca2F:R»YêÖvÄðéqrX Çq¤‰) wˆ%Ç Ø2¹PZˆ~­’ñT\ε¶æ¯I9e†z!Öò¾ôœÅ½õb\
+Š;#£AJäh$f´n[ž;á…²ø'´tÑòá›2M¬F°Ädô $ì"ëeÜ[@ÔÚ+1b‚ØŽ't¡ë=~Q(dviŠÈË2‰!”ŸUˆuF
+Œ«@Ò½Ûõ1朦Êñ™|a`ѯÖkÞâaÎ3Îùè›UºL”äJv#"›:—dCêi¥¤Î ëF0• p'HΊ ‹äÎwgë`HÑ!Æ=ÎN­ž‡ÉØlq»€P<¿4&¬­~Ÿ«ÏoÙÉ⦼©Ç7äû5A¨bAæ˜é ¦Gˆ L\§Xø‡Úèåã„”¯¨a 3î„5d´ÖÑ‘«>úŵG•¨tŠR”±Wi*/›¢é
+¢€†5„/Šd›r%X£È)³$:t†eÔôuƒ¹[y¤û2DËÿ<$ˆqæÓ)€PxG—OýÊÒWÅ3¸ßQP)â´FuxGÞ•ôC1¥i ^|LÞ¤Ò{œÈ,x¨„ºï!*œ—Y5§ÇVGP£wIÁq['ñ f§8»Š‚ijS2{Cu$ƾn{sWˆÔ¦­Y÷)°rûu«W芹±]þÞÉߺ“uª˜Hš%Å IYÓKT ¼NÚ3ŒSÝâNl,­ËApÈ‘qHŠmCv«éq„èwOkóV=ž¢ògOvô‹Æ3 ³¥^•vAÇëa¿HÂ-"£TÒ/®ãYy«†ñ³þ*r26ݬô„°:Ú;µÕ<š4,ïh Mî‘ê8ÛI~4Æÿ/“$¹ŠžÀWQä<¬½í[h[ºÿÖï3v™´ÚŠpØ-t2øøƒræI‡+„âàm¶%ô KÎÚÓÞ6ÏO]‹¸]Ñf;t*è}ãHÖ»Ax<F«l/øÀd÷Äå Q}KF«q9bÆ€$±(„9]9 Þ³L™Ô.A`S²ç¦KÑ×C‘⊀Ø'§[-¤ ·‘“*R¬ÊÞ.ûÙFAÛÀ
+EKÚlû!× $âñb•UßSBÂËÝðäaùþª”³Àš„;q÷ˆ‹ ˜GeÆJ!Íšk¢Kô1c-œQÇl|ùªmC8% VvÕ6°²FiÒùåP X³GY‹ê´ÒÅÒ‘ŽHiÿ]G‹#Z1Z›¢¯§"¹c‚ ¬âê¶ö @ ŸÃ.Tª¦¿ÄåŸ2ž HRéº'J@7›ÓÍH(´Ü˜•Ø­;ÔÓø9Õé]fkIs,Ú:†ÛFL$MûQýmäofŽRf®n÷í1lð„H`€…êÖ÷h`ü‡uj‡‘€MV æ<1y°á!W§Ô‘¸¥C#p š=ã°ˆG)Éó±šSX[U‘'KÓ|Ͻ‚ùbqèQqAãZ#/ŽË.8f ý^s!djåQ3âÕè窈ÇØÜì£z§0{´Ô†eÖŒáIš²«ì‰} A­PDaë÷¬»ù½—ì(ìb>mw ‘ÊËHÒ›¤ÌÉ5Ci(¢è
+’&l0€ÂVÀ™ø§}ò›«¹½C¢‚¶ÜÕÊŤ{Ú°UR; “ÐP³Œ˜Îé£éþgÍÅž~ý³ã‹ŸCy²†L/TlŠÐ}(Ï?¾ QR 3´þÃ[`kŒ(ù¦è“Šg¿Î¹¸"(ÖÑà6Š·ôwèù Ù—pqAŠ„±´
+ÚÔÑ\Œt‚¬<vٔذÐ'40uüpµ#Wêù0*p`²|] a¬¸ŸB§’ìzùæ7†5y¯M›H» Ø‘c
+ £y-þ¸H6†ë¦§¯‡¢Ì;¡iÉìjä„6öÏ”:TA…ÉDk2ãDvF†ºÚ lZr «!9€AR–æPûV©¤`à&]†J™×é¢è'Kv%ö%¤7Œ€ýé–…xBt©é€à§8V‰&dbæxÊkŸ‘;Üò^Ô(!ÒYhë(8(ÄPÿîXßäµ{Ñfæñ¦ pÐ~\IÓFF3j÷¢Ëç.E¯,é·«ŽýBÆçbJ'Ùñ<›)ŒµÏM8
+OB0Ö1­ å´´|ëö'ÿåï´ÀT¹–ÏêfÿXQ®WÒfqMUŸ ×ï gøTN!3¸¦fY:v-ð[V‚ÔÐþÔi<n‹Ò”ôIÖ
+2‡T–i_q7 Úµö7E¯‡¢ë8ohç„ )3«gT½ì´Ë{ƒàþ#ÒW8zPþ1«¤PÑ:úc=45ˆîVIp(è=l_NàD(’Ýý¤ÂhƒÝÜéS“D!Й³¯bÇË. ìLÊ&.Çd¦AvŠsîÏ°ùTY&+TÕbœæd /ÛÝì²ÏEŽCÙüK}V‰#¾Å¾4Ák‚ºÊ8Ò%ü‡t¢´9±)¬";žç¾Öe-»|Œ3Åg
+¬\5 íÀãpðMDïúŠL
+¥!ì7ü^Ÿ¢†XWºH0Ä€ŸbÚ×9÷:&ÈeoÏÓ@ñÉåLÂ';&ª^¢{Œ ÄfÅVÕn÷e£qgèÊJm”p /„í¦qΔ$Nµ¥ùwjÖ/°UÍkxÈÖíKâºÁ"‚(= Ö‘:Ë"uJhÆÛ)=åL~e]ãù)tÞ$Ì9d/iW_ÇpH
+¾J èêðùk žæ“~ršv_ašå”¬ÕÄNC˜äˆiÍcç³—ŸØŠ7õZ£žÑ?Õ 8x#ò1EÑÇÖ°kØAÜŒ­•cÞòÄ‚3¤S³?ñ­ÀÈR vEG üˆÇŠz¹ßÀêìÖ*¡Gw0;ö*¦ŠÖ)òúP«=ñn©Šu§Æ)?5ç2‰@P
+0ˆ;ÑõiW™áØ(`à%ü$…N[òù$t€+öV 艴‹) ,Iáö(L2NÊa@6“oCÎEv”[wg6Ùe‡|B‚—;pkNð'ä
+nËa‡d¡Y$ȟȤP"¾ì&dtøÑ=í*[©zþÜL¤äÇg²'%*,(2ÊÚÞúZ¤ä…›FZ>Îa£Zoù--]_Iž=+ë Hëq ˆeàT[Nûhn•‹@ŒNœó …¶sª<%Á¦ÄìfRÐ8æhPvoOÉvXè˜Yµáž×¢¼û˜I<~š½3}¡ÛA¶3÷yL&€Lšú'}!:âžÍ€ã¥øJ z¼•×æ1ø9%ihy(i²RÀ{17=rc-u¦\š_§fT(cÖƒ°K?ÙxÙ‰]B³I H¥ás6žˆ«'ÕœýçIX¿Þ7€%0ùJñÜS‹¯.RIñ;†n%`ä‘u"Þ†ù¥Ä`2›Èþô§s¤ØY¶®Bi‚èˆ}MÃ87¦Ç8Ù«ðLmˆÁÓ¡=‹*ˆ·ÝßÇaâV~Jbºo8Õmhâfqcì¹Ê’¨Y  üv‹²l'NtB®¸Õ»hàV#N5þ¢/)ïÇÃç¾QTUB!äl)Ã)b˜·T¹Ç¢'Jh¶‚ N\7Ò\NIGöà‡rvð>´„3D8QA¼¨EJ@3æ­}`¢ø4̶K
+h°Øßœw ªÈ6±^Á”†kkóµ£&±Àñ÷ìÏ8Ä ÛŠc-;3Q¢í"Zæ|pƒû:šÝá[É¢yEØãB ÌVåÝÐùÜ7Š.¤àïÖ¥èÇ&˜ˆ³ÃÒáFúVƒ´ ¾L¶bó‹SC8
+¬!ÈZù…ÉÉu磴¼©vp–m¯â+l)ªáPK@ÐåÊt ñò ±ZÜ•ðAäÖµ:l!3bHçFÊâb7†/ÅééX_
+¸œI—š ÝÓ1(–d”z|ºLƒÑùkžžEQDhd¹6ZïÍ /EÒ"wº ¡:Ì#°| e<”€€6Déd¯( =B}¬öªÛm ÀÀUR@t_µ˜±f<Ád/ß üÚ´òe›üPÜæèÖc0‰áB
+{‹g_‰^%|7FÏÏ‚]‰þ^äÁ¬Â/˜™Î"¦Ç¥¿yDï]‰ž’"In³?ð|åëªãÍWl#‡ðàœhžQÔ$Ÿ­ö¦¾JèNâñô„‹Š~D…Þ<1ظ"_¾^†ÏóåÅñd@£×'žö.ó6}ž//LR0f­ÉçyJ°°8ù\ÝyþVä\Û§dÎç%ÇSPÈ?4 øOK±W%•ž4™mD£OŒÏã¬òâ‚ÉPßžï~á>‡/ÛôI, ®%vÿ€øFò·"¯;i]:óï“÷Š<’ÿ/ö^$ÿ÷?* /®”Û¨z¹J닶B.ÄXrà&z§¨òLu`V§Â‹x ‘ÃZwI—Y†Ü¤é»¤âHHB©õ]1µ·˜ÜÑäÏý%V ¨#¬ÍzÀÌ@|šd…} ÷H`6#}Æ 2¨¼ÉŽÄWñØXþ4‡S²>!…¬ž´´Ý±wŽL@/`­Û£X|
+
+
+y;çý~("Û™˜(#³>‰ƒ*I ìèIKi%û¼*)ú•¬ÑXÜÄëÊèïipGL®Ç¯áM’Ä´Îd-"¢ðG®Vßl8s¿›–HªÜX/éñœÆvѵøÖÓëmâk¹Yx‡ðô¨ô
+ãÅOþK8pÇQVoóÁ`"+1¤f?²×˜’ÈñÈwÜ%p~BgF¿öߊ.ƒÿõP„OÄ­Às!ïoq}F%H[ÅÄy˜î¾1‡ªƒ2Œa< ZO3NÁà¨LŒÆrøDÔ"b/cvBNˆÿî’±ìw~ ÊÏ› (n§
+$~•bS_ñy…\h÷Îð,`p6ÿé“Xp ª™g¨Ž­užøA"3‹Ö°#h"ÌpwyðhzÅAª;Ï ÞU“øSÒ·§SCE§éxÔNås•$ßÒ‰»„0Fê|Éž$c
+¿Ãߢ|òooa03´‹ÂØ'[˜ËâáSBå"È;r¶ó²Â—"ÛNåFš,?zL™¡ =Ïý±Ð{
+UFÈJÈ^×Íž9LbT\oÎÈ­É«Oä˜ã­Õì  N›s¡É«tçy5•*â‡ÇÎýœ¤I* ¥tæ
+S]~»"¤.Ër»qÍ
+Šéæçáçµæû¡—ŽÉiÅ‚F, æϸmßfÊ®ÖkE-'kŽEJ¿§ ÜÅ° üë0›Ãl-ï„‹º0·@¶ÿ:Iǧ8Ìò¥$>5™
+H‰Œ—Arœ» „O ;ÌúUEE@._y«mNàJVò2÷Ï’¿bÍðOÙY£ÆhïªÖ›Ôbýñfí=D5øUFõÇç[µñŠÑZU´G·'ª¾{‘Òbt ³÷Í[i£Åã× È9¤¨—* 1¯.fCJÝR gI­Ñ¤õÆÌÍuALkóRz ‰ÇÏuU+jFè1d]k‚Ò}Ž˜/*ÒmAÔ­mô± …Dtï¸âQ‰Ä›v]7•÷ðÖ3G<nª—^k«Í`áÜš×´(œ(nXû‚´(«gJg’!”‘iñë±Z‹Ö6&k¢¤Çê¾IºV£Xû˜.R[ËëkÝ]†„œJ¾¯ª][ Žª¶@.œÀ·úˆuLõÚ%HÎÅ
+ÎìüV¢Ë B£ØÐÈ Ý󌩒ɯî*cÇkE¬Žu¾Z߇@‘!m\ɯ¢<aA(²…f©G½®"yYÌŒa’‹Pºµš.>Špµé‚XF×[é]7*Á8©‹ˆdSO—U£€ýîWhÄÅäãŠ&«¯&ROH‡DR;ÌÞ2Ëל3Ä®«F×1Ô»tÙÙ!W=ÿòy/!ÔÞê•cotNIšÜA…®`²õ?ÖUÆ¡½˜¶âÐç H†6³ÆØ ®.¼‰NÓOvwÙ/ûé­i´CÕ}JµšCòKW– æ˜SRßOçï3c5 ®à­eJ6¤µ–j3ªÙMA¿A6w†Y/ô<šr>îPK¸4hó é & ¬»:ïI'xÏFÓ¾hJAµÒ%Wr¨x#£ÉåuŒ·¬V~º{\ø:dB˜Z» 4¨MGMô«÷^@(¹ h§x BÈeØ@Çw“sÀèhx;šÐ††òqAhU¡[¹ðëQ1Z¡ÛPÒºå¤AOžïåzÕPÄY=T÷ÃCKGoЦ«!RV!Ž¶è—¸ñM^F²¾X©áQĸ µ#hí-û‘Ï¢÷‘­×ÕB1d¡>ÚtôvO™)vìë*ºFkRqß“J;ÉBClU\ W¥Üµèˆ=¨¬å/åJñÂqÞú\.å9c‰o®_]^<•Œ®Ñ-óWÐÅý(K–ùôQféV‹våÏÓw åeƒ¬¡A”›)½Ï4% ­ˆ ´§ÏèDZ[œQM´É﯈͉Λ`c–æx
+c³…¡Šðo#æ˜Gíxåž1©MC°_M·{a–Jü6c°
+…^]FÀÑâRRî&¤@»æ”í2C“ó)&Þn T‰<ÐîŒÅ%‘Dl½s:®ÐçQ5MïT÷I°1a¦§X}G|ÃÁ¼ÏŽUQ!οúâCIâ¹(AÆ–‹‰`·9®mjdŠÌÖ-:“£h”ºÏ©êUú¬ÞTódóF’d!ŠÝã[ÏŽ½ê`#iP;™EYÈ`eNïGå<Κ|1tö;Åœ›á`31 [K©­7ovõÇtµ}ääVîÖ5^eÎ`Phø¯3ûRàñå«
+5)Ýó“-1($rÚ†ÊØjF£ûÄÃ/!¸¥O£=A´žAòFÖ¶„0”˜Ù¬tA¨ÍA{T³#äIÈþ‰Ú¬¡`ÈDaØü—ò2Ç®#W‚è
+´®€ó`³ÍÞ…\qÿî¿$ÞoUe‰”×ýP9ÄPC³±(%‰%lÙ·&\0*´‚ ÓĤWùE]B£99òü¦¢òCO"ÎmÌý®¯1‰-#­„²Ôø—ç›Ø>Y„¢)ž¶vŒï…(,[°K1Î&ò­W(S­j¦à€
+‰¬Z‡É3â-mä§Üö9Çóhb7Èšÿ6ØXƒ ¥ô4¦é@ì*Ö(ŒH™¿ŸÃœÆŠòíè"ÓE[ñÃøæ÷&\Þûá5JžM„M+s˧Â]fµ¨"•˜€º[;ù þè/w˜åÒæ\¬Æ k…l™Q¸@ìÃy`•°NOçdI
+¯x[Ùò†vËUïgsJ%³°KsC°ï J“Ew ?w‚<"7‰eÝs
+tÝ%d¶A f„ o©¤C!hŽÉq£?<ÐeyþÝ_Îìâßøqæoƒ¸n[þ1înÜ0÷»!ج®AßšƒÀ±*»ˆÕ2H×kqãýh t‹!c¹“Iiæ7tµB’§@h¨ºš¦yqõ‚“°ü™A-ÌÛSó†à?é:=ÍÙØU$6Äa>ƒq?8ŠXóuÿ ଂMF
+3oH¡M­ò0¸'™!Û@´L70oЊ4<]ŸË«Ð ö ,Þ„Ç €hp¯BÉs xuÕ (YDmk~:§ã2{O¯AÔY›€™O_ˆÍFe7üê ²†¤±§vcS×eÁ=Ž}šAVýÜtñ:„¶ÜfyóC+yT§DçT$"*X ‘¯ò]k#-Eç¼Êw­p {(¥²Ûú/Â.ºõûrŠ34ק8Ãwû"g†¯…q6AÁ²Å==@~Û¨×â’'"E+œôisÏ(ÚβÒ万ûuЯï¼É8cà°;æ; !' q¶óô(÷+ é€Bå/W›gxO/ Mæ÷Øt"Ró¶ÊÝß'LH}Sèݬ¶õwÌ~Ž~ϧcˆýgŒ¬B ìÈúôåU€ÐâIáyÝ\ndÇÎK¹î ¯í¼†¨óùc|äõŒëÐvÅ
+Ë ßßt±Ö¿6¨Jl±u)yÎZ5¤ )Á ¹¸ŽXíbÿᕹnÎZ ¡„RŽÇsT^[ë f¢[$šfã )Ëûn‚Ø6É&9Wýy
+mX Ó¡Óz žûܘÂÊâ´È»&ïT|Dˆu°¶»†…ル¶V&ÀæÅCÜŬNZ³s ZjAÏd«6D¼ÀX-BÝe«T‡L¹Ç쎩
+*9×Éý»íõ]xšñW }üÓƒêöwÝ@YVp΂«ë;ßUæŽt
+¹+Ñyåé:H 
+cä%ž^<ùª£Æ¯üécÅÐeµ¤§RTª«'Û^At0ñÈò ªü„¥gTÛ9×}”Þñ#$J^?lL%í@g”-­BªnkCÙáën5³j•BæËŠ”² bÛÁ
+a «SÈŒ‡¦a £¸ÁÉA܃“º'ç1×|à|Ô-f8µ¹¦§Â—Èã4ꜜ~ßN¹ Íý)·ás¾è6Ã÷Â\áš›¾X§×ÒÁg%á(»—ˆ>ÏÔTÜ>åNׂß@_Ü®óbÓWoZ|ñöÏH+0N„™lÖ%¬iŽ‹øç
+N‚@¤$%v! d¼cAGÂiÍŸ1ìð
+¶m#†Ì²”2X(Âdµ4ÂÌi/—@c‘´TN
+;d«…&A ‘™A·§S†»•6<—h’.Èv€|…žaYô«`ÑØ;ßr¤8olGôsšÞ‡½ø9¹ÀCH^áyÐPô`…¡@«MÓ)_á?µII§ë;!Ô¨×1ÊsÒœñ­ Áå¡Lî=!ì²$Âiyq ¤˜f7¼puBËqÙ¡#Y,½nœ+e†÷«00´CHÒ0£¾XÀ"øÿRp>ÐÅIö^s¤aXmáÐÑGeç%›vqhTvŽ&Ä—§cšÌ¸á”ó8-BÉ!Ót(…µMÉï˜Táƒã V"“ãsÎS÷4ùƒt;º1œ/ê5‰IÖèŒA*Ì° al“m¼ÁnJ2”}-’“æ³)¥Œ@’ ·î’Q
+¥Á8ÒûNÿG¥??¹½ }–#þí’Ü(
+Þ°†’ßJp£ùÆ(°«âK¹ã¬ÉíÅÌÄJ÷¤Éí¥=ÐØaè³&BEjÁ¾ä~æBBÀ{N({-Ošl¯j#¥Ÿ4ùt×d“Ò™’VB7M&„õÍhðˆí¬Éö¢ó
+ŠûY—›ô}€NFnYú›¢z¿+30£\ª—ñpŽIeEáìƒí¤Ì
+†’.,geVu*Œ¤å¾*³Bºb:Mí¤ÌW0Ŧj)8+ó1hÝs|+hWæ-hWfBxZ—ˆÍíbWfZÊêÙ%° ð»23€)³F6nŒge†Â±6]‚–^Þ$µ½Pn”MªùâýBؘxm²ó9 gÄ‘1 ír“fèĬ‚|ÒfQ/3ÒÙ7óšõ›6̨lÐÓI›(ܵù‚F *NÁ¹œµù: ªíZ)§4íÚ¼Ñü‰ ÿ̨—6£Þ€ì4uet‰SP˜¼{ˆI†8PýìºÜ.Ɗ˼DŒ SÊn3y·Ðf©e>Y¬‰Àðå>¦åÕh1/§ÓÞ |³Ÿ9e2B„ü/lÌUpд‰!ˆBÛ1&…û–£­ é¡ØRS¿*`D×m¸7 Ñù¿à“ù €V‰"®†€E(O‹3„/]žeÌÑŠ×ÜÀO`ª¾®Ê/`p‰Rn§Q­v²èµÍsª8Ä´Ì,ÏDÃ8a›OΤS3Kq·~S»[—9ž1)†ŸÎÑ3Ö6Õœ;—*hnˆÉƒ4 C%æ=‚Né}Õ=ÈbVW¨‡yÂø´ ’EßðÔ¨Éwzù‚Þ€°'W€L{µ‡ELºIøÃÇxÖeIërLczA8¸ÈäåÏB&ì_-O‚$ø$‹OB|:g~šòZ!Ìn!e´Ä¦‡.ù)¥6¿*“¿ˆ³«4î0h Ë mËëf
+ÈÚSà¼yNæx Æœ–¢Z joç›fýôù·ÕJó!è÷1(\rˆk®ŒäCN•Ù? :4Mb«>äLö&Æä®°™ðÉÛR®ï*§†Ë¢wz’yÑ»9Ü
+yáÊÜå¡¡_Bü*ÆŠ«¡û91V¡I[Wñ«Ø…røÃoTÁ«
+®¹ íԅRFºuqrZk‡¤
+XðG»Ær¨XÝSF… sˆR~õ"’Y© ¦[ ep‹%?‡7ÐÕ‹÷Á¨–E Åg†T±Ò’B
+ÉWïÜ5e¼‡´x…ÅH.ÿ8qWfD¦ÜʺI¯A§™¦E¤üŒ5WEF”®·ÅØ!*"hu†0¹òÖymð0UHî<HŠQ¤:‹ÿ8R~ÅX ÝÚ Z4=Ô}÷¦ˆ
+#Ãö"ÊBî˜Äß‚4º½"Ä6›
+qñ-üÛ…ÁšçˆJÜÁ t±–Cˆ·ëª4lýáÀC7‘zl»# ¦GÎðc˜Á‚edKê—êP…Ùµ°8õlA§ú°vàš¡§˜cûvÒ2 a|VP‚t¡Ožo‡ ßß™ž_² ‚@œÛX;‰Aå¼Ô–v Ú$4 t²ƒÉtŽ¶C1O«VŸåô
+©Ûˆ¾×Õ¹
+Ek[Ô¶7áox.Õ%¬’ÆÀp6Yµ1ÏapxY á‘òz~Œž‡¨“ xk@¯ª?s £.«cMŒL›‡@”UF2æêúó¯s’‚ò‘W×NA·ÕF6CK™›ajÓ•2Uf„$Ö`Ô÷ʦ~^ö/÷²^Î=z¡ÃÒâ49Tm ÂT¬ËâõÝ{‘þ}oÖ~ÎÖò=› :§WmÜ«s@û½Ä‡/­úç;S£ sÀÎ(f¦ÅÈãpÞ¿A¡³™œ$…›y±ÇR&Ón»vÉ”„T¸Î«0L'¥î(ðÝèïÔkÁòàuq"µdÁÃxC8%ÁµzcYŠNâîñÐû/! f9Ü~<§qCÈÎÐÎÙôCÏ»¶š§W ÄVËfî¸Â5¹B>,OID2X$siÕ¸UYÂX¦SW"OsHÆ·ÂnÅ4έK5· /½ÿ} Âbxm—e˜—ÉC@¿œ†/S÷~í’z:_òÊÀyœ–S®kïafùHyÀ´}¹ |HÅwWTažÃv¡¥¦óò°7ôà‚v`ìç
+¦qAjL²Cbê¢<cß1Áø €¨äÜ~Í$Ñjƒ3Xª¶€lçÙEdtiÙ‚`véº§Æ áÏ„3šÄ®bÚÐã嶮Îé’~zc–q@;b‡b¾yux
+øÝ-ýζ©ñGÊó‘dJÜX^ÊâÂbÏÁ‚õ>–ñл ¼.«”µj Zv0 ![È(`-\–“±§cØ0ãŒAžWç`ù+ XÚtQlH”î`¾n(û¼‹e#B®gbá&a´Aœ aÞ˜^%7L-Þg3²ü>¤?¬ä˜ö/¬xyhÒ÷7tîv2KCÑ/FÂ= {Ý  'Np§ë ³–ÅòBÈd¬–à>õËh­Š^\P®¼·ÖKl*©ÇýG»N 3_„ñÁ"QWä®f;9ÊG‘Z…Z±PäL'¨ª_ßNµ÷1X{ 9åŠÓ Úxrv›À´sRÇ `×h/ ¥ÍäŸÁ^Ä^#oWqð×д¥Œ~ýžŠÀ¨ÁÃeV^¶(”`©T
+"RÂõHZ½=CìªÇB»ç<¶‹Ís×å4ïsrœ bÄsχQzÿœ8¹ÏE‹ÝaíoVO…—¨aè‹ ò¹âéº'ÐÇ+1Y¨™]_[·Ö>õÍ@“üB{m `\ÄCpH¬UëBö½ŽÓ&aä|ƒ¢
+%®aÕ–å2jg$y3ïtN*­ÿØ7Û†ôˆŽöͬâ6ž‚GD>Jÿœ¦‰B¢Yn·ºM©ê˜ ó¸!…¦Šaè!3òˆMný¸
+ÝgoM8|uËöG|ñ¨®ÆBéã(5]=ªß;“Ç ýÝ?gÜ—7˜œÓ-ÅŠ+X! ƒ°ÞïVwÇ&}”Ù ‰÷7äµb‡?™÷Âl¯ƒ¸Ž ±%‡ÝZñŠdLÉ<î©ågZ„I7„­$NÖFÒ<6„?C’8/;„+ØxZ¯ßh9ä<l<¾çH-c Ê³ÛMôdf!¤ ^Õ$ä ¡‹tÆuc RAŽ–.Îiwì)­Cê¡.¢éˆ.n/‚C³ÞáQL£XŠ·ï-8ÚP0Ǹ÷Ôª²Œ2÷9tNäåT¨X†ÎÊ24hM'_ÜK¡J‚Œ£¨,í¾xÆxo;9“Û[zÄm•72Ú1E# @‰fÇïÖd¬ÂŒ4mT¼·l6`—b&26«øI®;f8ºuòë榄f2E1‡‹bA_ZƒQ¶,s5^¨h}ÙÚˆkTsCÐf4e[æÂÈK«%ßÉüƺù2ÈcùÑ·±|\jdÒ <³<ŸeB™ ðÑŒg–gÁ¥eØ6ôuŸåÉ-òƒ×–|£«žŽ—|HËŒ:!-<ÐÅ9éž#„P²vLŸœÁðLö
+šðª=’61¬7Vð‚ãÓ1Q“W?˜ ±07ÓðÅRÃGÎJáâz6dV†åïLòÚ÷Ø¿P“Yñ:‘ü ä5G|B ¯ƒ<’?Î$„Y,2ŸšüLòù^: HÇŒYžn–åÛ1K>ËS ììÀ‰¤ì•}A¨­£ª]ÊZn”`+þ9´n_\gé>Í'­…òâñ¨ËÓƒl^™U#þÌòéNs(rpÊ3ËiyT¾Ó6‹y4yEO;=¬=ü¼ òxžÏ;T‚í/hžwàʉ/§q‘å¸c®zŠùu“Sƒ¶3·å¢êk´SÄ#ÄV\š5Ò”ø|–­+š?¼ì„u4­0Lš^y4ÿ#þ^4ûÓ›ÜÓ‰Ãd³ØnÚ¤± $à”„þ悆
+{º:d/†æ µ8ç}sAñ®ecÒ|diµ}¿“.~ØÆaü³HR÷F›Œ˜ÐO䲧°Öà„3:Rògè´Y.½}zè˜:ß‹"¶ñ¡í‘ÐåõçÚ;ÖEm†ž1/MetÏ¡£.Z•‡ ?š‰bPKÈ©Ì‹WÁ‰!ÀÑ-²‚™ÐLSÑ:·‰ò›EnØ9RWjß^"èòƒÄÙ´PW‘«„åè›ÈÐCí?\PæL¥</Ù—Q¶e"ñ&F†è™þ"㲞Îí73êÁ?ĺX<µÏ,kÙãå”®šã ˆî±f)Oá Ô™ür®ì^ŽnAŸ 5?6†w¾Œ¤ùŸ#…W(aÖ¦%SOÜ©!˜Ïˆ˜á^Ú Û3ŸâÐ|K>6îIæ~Ï Â"7Z6©..Nðg¯C{rålwõ¸êè^ˆ›.>‚˜ñiž¦|Û Ýmw¡MšNÙsÖ”dNW&Ó†ð79S\‰U&¦™
+_¡Æ6úË“É’#œY ²÷óCËšÁFYЬqÁn³´´f
+ÊÈŽ¹Ö÷}<Q1óaõs‹0¥¤ŠÒã Æ£d¶C(ÌxgUl­qÊ<Fb$ž(¯óé9M\ì0RH4aú¾Ø'7qêŒÙïÓS9½~ot*IVGò©HÌht;›-" ¨¯Xf¤ Q ð·U“E–Ö®:×X¹®-†Ú1%–?1TµÚdPï™Wéöz80’¥I£ÿ׺t†Ðï–e#˜q@$÷á‚âZkhJ$xk²Bæ“lüPm&‘>W/¢!¾°ýâÅÍÜüÃtPTŽ—š±`ì(ÓD‹¼.z.$êDÎà÷ÝÇg9•ÓÓ£¶ö®ÌŠ‡|øçhd
+ˆ×‹95O't V†?}_°´óXÜ^¡­ë$=8m1ê¡ÚíF`EBÓÏÑþT0š¥'†¼ûÁ[/O¾²Qv,Ë9O 8”ÁjÕ ®*ï 0b\Ýèé4mBÑÎ y±êÖ*Ê„ÇÂeùj'jÚJÁ=üGÁ¢l#¢  ’,÷fªÇˆÔ.)²Õ¼¤÷\†+Òá×D[²±ÀÏïMN†‘X$gc„<Y¶a/§OÌ îòÑxW@tŠóܡBת0xUd2÷9 #&à[OŠ÷gåü¹Vg맨É3^GÇi&Æeæ-°Ò‘' Lù
+òQØLG”ôù¹+èõ;-o°}>ÃI vÆ}äGï(L¿oÏÎTŒQA8k=€Ï¡`Á;䢒Ì_/¢¬Œ )Ô!ù+zÀnK>ä%q¡Óû{1PPzíxs„2› L#íŸÚ A˜c2Xˆ°s±Spæ#‚_ÚS{aˆñ&]‘¯Dë —.UþS¾ô
+ùaô:Ú†Ý-¤Þý©ˆ˜Šš‰œ²]aí²4Ñ}ã5
+jÚ¤ ÓJ$’Kž!D%"F¼ƒÅ= 1õŠ8p.3}þ”‚!þ­Êß<˜:fLyÆ,õd}€°›èœÛÙû9YÀÒIíÌb†õF¶¿ìG…/Y(E®ä+ÃD­ù}fŸŒAÁQ¢ÑÖTrQ ÈÃÎAhp‚Qݶá`E'îbŒö¡gìÉ ÄœkŸº€Ò¶}VlÕ¶‚¸ ;Qžæ;:¸ºlÞ~7Ïf\1[cCø·|
+¢öwýH/¬!Æ@9a¥E=ÓçPÖŠ9U"Ø&¹*)òüaݤ8)³]áy"¨Ð?GôpI´lzp
+úÍçc*ÕïZ îãÊ«G”¹KìB{ªÂŠÏü‡Aÿù×s*tùÂ1…%…òزèVèýµÊ±‹5ü t¹v‘¨+yg[ ½~Jt4&;P¥|´/–ƒ8ϲC'´oTžz’@ä1™ÖÚŸÎ’JÅ/FžÀNjqç(ÖôïOñw²U¹Ä8=uø§þ<„kVü-h‚á($Zk>„Ê_GPfô
+¢Ò‰-UfJ!,+GíËÒHývt
+SU AR:fÕI Ó
+†+îs”ÕkÕÆl¶È@Q‡Xl™«Î”š¶‚Ë/¢¯S1Ë–ùúèýëbTBÆ%·åý16
+ׄ"w&x{:«Sq
+äÌ"ÆcC/gæÏÁ¸ŸÃxAöì*óý6ƒÖ•ÆüSÚMå{0ûÉ^w|¶Ó…¯8F¥‚<­8ä™`?Öä‡í -(áå¬+1Šλ×s¦KCN¯ú§þ¼ fê—¬2†U~ãà6~ˆ‡‘ÿ8á´¯ *ܱIwtùÀHaEùSœÈysø…ñÆ°§POõ61)Ñ@v/=ø â &°©±o‡Ä?x„Ô¡„Ú3yâ»C¾øù× tòuœYñҭG‡xñ9",åè¨uÛE¼s6Ú§Ü÷µå¯»Ä²yÜ „l[GáQK*M¹ÊGS&1{ Pùûë‘gâÈH
+‰ðè½%©¹È]Oø’“ϬŒðœÂ—–"SÇÚÊÓ9ºÙqÔzÌ 7.,↨锔òWAN!l>ôOA<@r¯óANÂkŒÃaÝ^µ‚|Dçdž$)3AÙ/<=˜4-z̽Mo“ñÁqSæ–Ó·A§öÿõ·ñmíkÖåê™·‰lUõºo#ôѲïZ<¸€$b•àˆÅ!ý¯ðó†(Öᜭ*$ξ0y÷Š
+ŽFÑéxZ.4ÚÚÐloˆxV5T:B¾
+s£×ôÏ?uA,J]ÿc¼\’3¹q |•>‚à›kmûÞªï¿/I°ì¿Šå±Ç£èpCEHäÍN~å"þ-êQ<
+¸J°°Ó\SgïÄÛ™‰ì£PÈÙ)ƒ©@ü˜ÅÍÕR%óêqãŽÁÿ‡åïË’jöܪ6âÛÂÐm×ö¥¢]¯g[¡šVؼGħBÒ½ñ3ã^ñÍÁö°mQÌövìÊiw©Gç!51#|¹.|p0êu ²+¾Cª/’“—`à׫ê‹ÅÈB»»C®Õl¼ÏHû¨ VÄ>:<È•DeäÿŽÖ– XO;éê—PßÞë[ ’ƒíô8ôë¨Ï"@:¯°Z|/ŒwóʺÔÍ´®wËA¥Ë¸JÃ+:€Ðµ”ãF‹^Ä`é8Ü36™o¯ «µJ€r‘¿ãõ}ó%<>’¬ÿ¡ä¡>Š~‹0õa ´†]æ?/E²(*-é~£ŒBÕ1ãÛ+`ÊBÔoN¤„GLb—Y B
+uÎ ef<Œ¸Ìtĺ¹H!¡‘x™]Ë -øÖ î]Pj—Yøg‰‘°¥µÅÏï<hC·ésRDI'ÃÚq
+D¢íN6™;•á Ẻ5ìdñwÏýÌJ­ì£è l6Z°vD”ô/­G·Iz.N
+ûM€„ÅàŠÃ•/‹ÿ”奈â\L¸>ƒ×a–2Ë嬇’“ò½EŠ °ªµ½9™C$\êô¹øŠãJ’±ŽÉ u¢´\íËÕº,äÖ4´„d‰ÑÕc¦Ý
+­Hd¡öR":‚3MÒ³Oº×ˆ!› XóþÌä,®²=y˜‹š¡¯ê0Yì‚ON8¼“™hãâH Áð¸áÖŸÒ0‘ë;È=ß–×sR2I»x3˜OJ+ߥbeSq
++sëû¤®Ð&mó\HIWFp¬-f–ÙÀ}³D€+˲R2­Ö8ïcÞà¬Â({ï@ƒô¦»EåÉ3ü“š­ïä ´bD¼ J×f®]8”ð˜&"“'õ“5@ªÈGöý$nWh¨n´3,ù±ÉÍåÍ%Ry Äƺg¨Yñ”–.œOH(¬î)X’¶éª9?2\K{R>§SæĪ²ýÜð5å².ðšLW
+õüúYÌ‚Q,ÛàIÁqPzY%˜aƒ$mÓ(eû[ȸl`"1»ŸÀnD!†epé m{¬ã*ñŠŒ&í|)1þ$
+UZYü(
+¦ùƒRs}ø1´³è›˜üó7GC(ãí
+“A)¦µ÷÷ð×üýØQñÙ¦Ò €´Œ¡:‹t’q †t®ˆMtÅŒÒuÌ­†¨Ä~Uømo1=_6#Ïù9 ª’Qª]–ùÞ–0¾„/¶¦X?è³µ*Q’J{¥œt/ñ£>Ç|üÎ'XŽ·¹cîô¨tŸÍy,
+n dÆÑ6ê)"q摶_%%4–«áßaS +¶¡í04Æ‘¾•tŒ¾”!Çt½êQTV•‡Ñ²èW–Š3§²ó–h)@æÝÀXØ1°’=¿1C3MAÛ£šðCò0Ã;qb Da5°%Ðåƒ'â1·Rà iÙ¨‡?ªC•p1¨Hçï E
+±þÝäââð“&/Ù^rØÞÔÆdÇàúœç­ÄÊ%NæQéí;T‹ùâ”Ë€Høîÿuâ
+‰]äMÂfJáLžÕê¦n«À^áá×wÔDx²º±¥ÅèXã¿ÁÚK‰iLÎë×I÷qdëæZ˜Ž~"‚Õ3§¸–{h~¾‘&AŠÕè%d¤q °×Å’¨>±Ãª³13w;H7ÿ ¨íØ\(ÚKÀ#k• œG'Ul¤Ín•m8ž½À^;Cß½ÏA PÙ±®Ab0În´”Ø"ß¼¡q+‘ù£ MžÝyÆ(û¨’À´"Ys¶¥ý2Õ°©ÙúΓc·\«ÜfZͽ ^ƒya9jÛ'=j>M¿î"väÖ<;4€(³Ù-o6AW±Gˆg&–¿Gö «÷IüÀ¹ƒ”¼C'tˆ¥Äù9øØyØJ±tL®”è›ùzº•8$ЭÈú ;ùí;x1¶~ ÅÙuS»+Ä9Æ/EgÖÍ@Ù@AM¿tiLŽ=¨LñQ izÄ,AÚWwyD&å6yôóXðm¿×‹³°|mgÒš¯a×ñЀáq¼`ôWK Ñ$Žs ”i
+r>%V@*BLãZoõ«?öu›ð0™Y¿ÀH½¤÷_Íê²ãŒ\ÛŸ‡®V®ýÏKQ’¥ÄäH°t\ý“|¦ÿñ2I®ëF¢è
+¸=` ItÃ
+ÖÓZÂ3jèý×¹@‚Ö_ñí°#D^xÙÜæô„0‰Ã÷nÈ5.
+“€­Mñ;°Îhî"ù{tЦ׃áwù à×TÓ¶ÈCÄóH”GçryöŽ’EÂ\Ÿ ƒÛ«5=«‹óð•R˜]\Ľrn^Îï
+d ÜŒí«dŠYPc/Žš¹¤^mcrð™<U‚f¢¥"†Ôœç½sk4Mià㸧¦gÆNÌXý%n¯€`3n#ÖÄ0mÚçéº+èë•Eõ}–Q|D9lër„[“緵Ϥç#óz
+wF%‰‰A ‹„œoœ÷ Äš’Ñ´º0\lrø6‚TrHkYÇ}Õ!oâÍò!°DÙ¸ö
+âÒǪõ"ò‚)»ÌöHUB‚ª½zݧQOΚzü[:³ ŸN«K !µtLß y—˜äËY1i;ª ÔòIO½?)f›|ÊÌ\ëùœÆ
+nwêx;YUÕ {ùõ$g¾,è$g;LC®ìp\™ZÖ»ÃîÎv
+.r€}Ï;f8PAVËúž ¢@D[¨¡',2?ȬSó«J¥fÔôߨR†,§U†Î'Iã}¼& ~Õ ›dˆµ;Ÿ£á¡$Nù†<„•Éuˆ‚Í·;D¿bpÈduòaaêØÞtF²`ºâ3Ø•C˜Œî5*{{0.3H‰™ 2E"OÙ÷d°Q¾…wRLHË}³< 5­mg§¢µ.8K[ý¼²ü‘ÿ̧Ëe•Ÿ<NÝ$.i‚Ò X´ ˆ=’íéyÓ8óC”B
+ÑÈj5Ý!£ç(Üb áýÔ½q€ÌVÌÌøÌ,ô䣄]žGÕ=“œaBp™D2÷°ÆoÐÑÙ;ñÔ¢Z‘ž¥+èŒæmìU§øñd¦>O/‹±H![”0¯á0µ•µˆ‘Š87l#ªÐTUDøŒÄSÈlF×Â"UãqLx,ü(¥ÂÍŒ*ÊH¬Cæ‰2®wÅdÏü» «‡®þùÊ þf#†g°›|üðELÐD‡…˜u[ãšp¥MÄ#´hBu±SåÛ‘"4Êwàò‚ô¶¦¤„â´`Ѩ©Æù0Aþ†õ25Öy üK{'Iå÷@ÿÈM‘°ã¼Àbâð
+«Š@ø³>·†í# ­ ´P¡Þ9¯Ðd²€vÓ’5B¼‡šø˜z
+M¾ÉYciDl·z S€Ý ™CT#<Yk^
+í{èñkM¯±x¾Šõ‚#ä…]8ë#fòxÚ­r=AMvÿþÌïGØ.ç¹í»\ÜY6~ÒÊ‚ð«`’žý`ãP*Ê»³»€øŽMømÌÛŠð@Ù }N¶ÎQ’"Ö=@CÂL"eYˆÀ¢û¢+F¦g'ÆX§À †¡ÃÆ׃5œ Ëø¥¯–(“‘Ø+!ã†8tݩҳΙø[B…d‹J¹ßí¦™§okߨAN'Ä™š. Ï' Qþzº¦W ÊE¸¬ÝÅCd 2EHËz2`ñrÛñ–(Ã1ØüÛ. ·ä@)'Þ2üŠb-06ÁÎñ¡I¸;C´r€øU«òÇÒZ|vÎ#_Ì×d*EnIä½ïCà>ºÇ <ò{[èKÖ(0qÿÊm@è¿ÉÊ&wrxãÞ&¿Å'ê:ä¸Ø÷±q),…a2 ¯' k3o“Þå×ÃÒ§è»[µ€!‹ÆWi I¸wþ·W
+FKÌ ìé6¤ËRÕ£s
+ðʨÇÕà©hTZ”ÄK!ˆj¿[ç¥ : CÈd¡}VWÍ”¹H6Æ–Í.˜àµqlÏ96º «¤2N`Ën*dñ²¼&éÜé_=Üéccâä‹/ÌÊÏMÁrèDðóp+0=“"ÉcàÏS P¤Ë¸²ò4§\é--óä
+YjŸ|šSþå(àŒ§NÌ
+0
+H‰Œ—K’9DO ;Ô&@‚\«—º…ÌfUºÿv‚`J•ÉèQ¬?éA‚€Ãáè£hiöñŸVý1µk“î£÷þñó[µYeªÆ/­h<j·RFë2l|\ê£JQiͤiZæÔy™­Xo2ÝÚóªÐ|ˆ[•øÚ°ßþû­~ütô‡zëݤN½Î+eº´)Czûøµ@µÏn>£@¤Ô/#Ø}Î(Âã†kíiY§ñWi3!R›Ûlµ‰Ù: !¥÷R¿H÷Ô®j^×9Î=ÚÝ«ŽÌR‘l²=%!Í;™˜¥é‚Œ.sb%£ß׫¦jÓ)…? 7 µ â”þñ™ ZÇ$Û¼8#ªÔ¾‘™}ågð 6Üym¾«á;ꦺ îd“gú~—”Ò'ßÀ]ùé>LdÚ €Ì3éóÙÇ´uN㎩<»jý{Õ¿C² BÉTÁm)D
+å‰WÝéJý
+:×VÍ‹E'"žï.
+o–¹iñÂ@2L"ªÂ‹¤$/øŠnDŸ}¿<„ Ê¢Ùçœ4BgzYƒ)ôKÙìSlØIã*øÿÔ4„˜> «™Ó‚Þ2f
+¬Ga€aëO»¢O 4³ä)øì=ÿË$9Šï¡à†@ÍÌp,xh )§žB™1Êø©ÏyóïÝyJbsS@Fö;¹(8&ô ýuâß‚:®ôxP—™É윌œLGÓç@Y|NKñŽAëÈZ@ÌWÈj˜ë}$(<(t";ºŽA–&„ñ[ƒÄô,½W ¦µB%­6s+¯ª=¶»šÂ ×¥£ã&]ŠcØ…pÈì5Ü–71§ccâÉÚ$þ Gó'„=â suõ/œ‹€JúKÞÍ;‰³ù:&Ȇt0B_`i`¶Î ‰4JߦÏÉg`c)Ì«Ø4¡
+#rÖã±ó ô$¥‚æ¥p4g™7?­÷¾éC‡aIYðÊ5"bú`„ÿö\_¢l&‹•,Ó'ƒFðn%¥–<b@£Ü£îB…ùFâø4|}ÆʳòçÔŽø(V9ª1ÌE[ í7ªÈ'¼š²W÷sh
+¬ö  kˆCæ¯<†×@Sê:3F1©qÝnGË(á~äš'ˆ…-f¹›˜ƒKÕ£1‰'(úôy¢äNÏápwÿN"cYaGØûEÕ)5€ö‰½÷òÅ‹ˆ„Ä¢ÒÓ_D’‘6a)C÷†S£áYÛz60„˜œnÅëÑX½×ó`Ð
+º4Rƒ¹cŸCèZ:yZK‚ÕJfê±|UÉÌ°<"GLì&ÏGUæMF“磜FàL<JKR„®RK&F¦}ÝbrWÛ•
+©0<·nq£`–åwçMa >¶*©ì±¯¬G‘ÑŠ‚L9F!9ˈQ²õ¸˜|yôÐy,FÓàpž¶÷cÒ2Í8½å1·Ø1µ%¤Wî ¶¸ðòe'Ì›¾‚bÖvÂôš q˜Õ¨ÍEG†œñ¬Ô¦s4 Lº) WI 0^¶¯*˜*ÔçWÑp¥Ä<Ë’‡‘Ãk²]èÑâD©¨¸ ß”;«à[tõù&'½AúZ!Â*ÍXFÉb»6ݘŠ¨€Š2ÕÒ+E¤f ao¿ç_ñT1 riSÈ ÉÂð…ƒd¤g‰§BûÁº$¼ BZCÕ¿¯«®ƒäõ§éúô¹A5køþm•æˆ%Eb¡ÉÖ#WC£W<ápà‡o=ÁòQQâÉBTAõgƒÖn³cS
+&›š½G¶hXÝ<e+ˆ@4ßô $¯Â¨ 8ö?ÊËeG–Û¢_ÐÿÐk-|&É¥1;i2 xoxcÍ]†ß'ø¨ë®fI-–Ç£˜b2Af¸üœ”B#ËzŠÇz›ôÒ`Ò 8Obh’³~Lyn"võÝ$nâÅh6Ö+)=VðïÖùÚPKzHªáhrì
+LS÷¶™Ù (_Ñl@‚îÓ‘Ùõ܉ׯ„…†ýáiWéÒ’”jÆýóƒªùÙÁŒžÍ?þÄÁ H•÷÷B­2»¥ÕÅBèÆ
+%JÖÁç H#ÃjÃPÒ¶Ï
+š‹` £¾}ÍâJµ+ÜîCQ»ÝæKa~&¨ãø8¹žo³ô©¥+KžzjÅÂÓuvöéÁÿaoh$§ìâÎð…ÆŠŽé‚ðd¹«vqj0PÛÛ¹áþ)Æ#ÅŠ+y Ësˆ¥æYpMõj¹9Ę}[è(¥j[Çy)-¦©”râßñX”wƒò™¢°ltƒ¨2å™e7ô‡8)(p ´YƒÈÞr´‘[&Ëø-‹1Âfí -BéYùS뢋¢;žÔ•ƒóˆ±#Ûл«£<vÂð8¾]l(–a
+j‡Z\m&½IAáWßÁÍ+Ç·ÕH­…«eÈszHKxãšãڷЋKÝËD$\"±Ns–ƒGcD¶zs"Žá•÷,ó$eBjU·…he°¥´.Ïø³Ñ ´JòÓÚ¨Øq'”©]óǖԺϹN.„<B i¯$Í&§tŒ¤ Òo›ž›Ø+­2=ò$* ÈÓ°63 v¨ÈÆ·6œ/™sXû9n {i:>/@¯©eŠ2ò*Pfs@”@``^VølÌahÕŽNºû•ÁG*›nÔîsBî~ƒÈ[¬LwJ¬¹/®c»J-p"ãIóþ;gÉèÕD4æW?!¤BT€ß'ÎÑfŒ~*ß'æÿl“1„¢¯ú¡øf-]áü@_[•ÁQË?渰Q äßo@_ ók¾0KuSë¬ûp’µ:ƒPb¥ú×µ‘iC “Q¢ôHÂ>"o®ö!hK=Ù€ˆürc\?í.‚F*Àðñc¥aILø½nœ
+|}™'EV^äðè¯ß15°Œ¥\UCH²æ›rëõ¥ä×ø;üѹ9@2ñ »œFҤŴÀË•[;& Zƒë¨3ˆ¤ÇøÀy$ô¾«zÖ¤¸ÁÚöV6Xh ¶Ž:uÇr'NæÙ‡û®Å rÒ­«“ûß=Õ ²Hñüä»ï<ó楖öí®ôÂâ×ÖlfûŽhã†øß ÈÓL}£Ç&Š†ÏÙºøoó=­"$&ù»0$/ ½h<ŸvÂ|½S‘4ㇿÝÜý×›{БÖ{ªŠ¨ÊÄɸ~th?ütó÷ïþþ/~…XÀj‰šŸÿÞÓýç›Ï.>t(]‹z
+ŠÃ¤Fš^™5Õ r)ú…k…JµÓÖq–1Ñ$•âþJe¿PÙ* °yòøÑôB½ÖQóo@¼;ŠòTáʸÿýWSðµ<&4VܱŽ«ˆäéJµmâ”BàÇNlN€'œK³¼®?ÃÅrªÏm–Ë"ð ¡ÃU×É°?¬vvN ×%¹ ˆÜìó¯7
+Ò£ŒyãÞ?ãÑÒSè$n¢ÌWpO×gö³sœ†ÝÆsá©þìýµƒ(ü{bÈ­i
+þl®Kú¼ý›òÿòÏÛÿ™ x—›¿Ý¾??ô^P-©juµˆÓC;ûÍÙÛø6ĉe ÝÛ’IÀzÁ5’I%­Ø©}ˆ[›, q¯e[™Öß#4(9§{ÑŽH¸ø=ø=㘰Dé·ˆwÆEÒ Ù¨O¾@9áôàø6Æ÷«C8L+^åkÁ^H¸á›³QsÒ“läè ·²ˆÅtl)?/žø É=lµãÙ¸ˆ¹ž>Š^>ª£ÇEb— K~|ÅC"ì £4õ"ÈÌE© Þÿø1»ë¥È[Æá#’VL:ÿÏŠ}Û‚*s…÷¡:d}vÅÂY"Q ExÔºð²Èµ¦:÷ˆW­¦eK×ÐëÅìÌì~Ô7tšÉÌÛÕ×IUü‹Ã0¤£ÙáD£' ž Â pÿGuë„Œã8'ˆÃrõX¦ù%me’Ç€` N–Û„zY鵬¹yBÌ“(„ ¯v˜Ñe-˜”ÕuƒÇ é õ¾y‡sµ»Çá[¡QÒWó«Å4E± V—2rˆ”UR7ïTîôyV,wX¸ænÖ2¥…æ}Ý æ½¿ª÷Ÿa–ÊGÈ-ÕVż%R˜õ©ðZ©µmd¥%1J«Œ`¦)è=±…xØÆgLµ…Ösëô9°äÄÐÛ´åº×´7Î`é\Ä h7/ǽ:iÂv´Î Ï¡/ýΠº9Q¾‘{3Ö%ΕòŠ!×¢³lNØwÂâWÍ„°7ae›þ¢ 74îÅúăʸ4^VÏ÷ÍdÔ¼¢ tY‰øäâÊ@ðoQÂèˆNTG¬õa™'%®×ˆZ0ß(hQ±®j‰…&Ü]ú;aÔ£"«öª3¸D°4'1Úâ+„,R“^Äöý¤3¨°ô­ gï‹ávô³à6ZÜ^JI‹&ažÅz´ï©7Êt,yh•r²û®Á@‚y0±…`Û‡:AæIÏï½û̉4»bž¨·¿Ñ _;sž”-;i¯O{ÀÓ(}#âXsu#Úßkø;úSýó¹Ál•â|ØôõNEC)
+1²ç"EžÝ·¾ Væ°Yò¬JªLoü5@"÷¤ɲz††sieÁ¾cí,â²Ó”Áÿ1^&Ù‘Ý:]÷P+Èþû½ OUûŸþ$˜¶^‚å¸9ÄGhÖPzwIÅÙa¢3nÎ)ÑX)èxN™^%Úk~´R¤QA4òŠc—$ÞDì3ŸP‘ˆŽ¹Kö¥1ÀT‰õÜøó˜Ö Llmô/ƒn„
+™ìaÀÏGMR*ak*Í\ÏáCˆ: èÜ+sõYǩЀžtK‹È)Œ’k.ñû(òpHQÖKñÀýw‹ø\#|9èaÙ@ôŠ
+”‹e“OnûÚËÎÃQ¹˜'¼•¼VèÉ^–Ĺs˜‰1ö+óÛ‰ßN§‡äÎ#~Ë: Í×”d³(r˜²FÍûš¨ú“Äæ9Ý‘Ññ$ܯä~=§*²‰>Z<·)#óõu«$`MyEÝ^ÕÉ¥ÜO“ß Œ9‘íɽ°s2LÙQ—¸%€ÃYQže²’Âm &¡X R_xìÜÐx¹Sgýðßòà›â«HЈŠp™l:¶›Q9tE{>6¸BüØ›¼ù
+P«(Š?î»dˆtØôšíº¼(³¡C–íÏ?œ·9MmÆ‘4™“ß-òXþ?éÛX>-!¤‘­GÏ¥þ´"~!PC7–'ßÒE–›î>Íç¼ÂâI†OdùU{,Ê範˜a·„e(¹øç”WÌ4zã…ž —Á¡«G­ßÐQ^Eábãv Qo*êÕxyT})¼@Z`bÞUô„õDÜ×s†¢ÚûÆš§„¥ÁFîy£ùg‘ ²ŠÀÁ2 õªÏ"—æ¢'Í×—ÜX†{φ=Y¾¾”EÙQÙÛËSCƒ¦³…O’gÜŸ¦2OOÛ×@é\‹ 4X þ’À7=jY%§Â—íÌ'Ç—WÅ9)"†Tn+žŸûX®ûäøòZ‡h!úO ;gñÆñÏ"wä™öÔX3a°\åÿYär<E8pÝ”•w9>¿”®’"L¾t9¿²‚IDœ«$ç͵)eùç¤
+Iß'”u\ăäaAòM†NAÞ…äŸ5nwÒºsœôçbÀ"—äÿ‹½ÉÿøŸJƒÞ3p!lù /Œ ·paǯHMÜòDG×Q2B,lY[ÜKÉðe óK°l=ÃІ€ÿgù#
+ðk¼‚œ”g£1z8Ý]2ñ" ”˜÷™¨ \!‹ïaÀ>¢Þ˜uŒœ$ó`³
+@2,¶!ŒB=yI %`g·$3 ük>Ó<p§HÞ£pI8ò¨(¦ÄÂAûº·t 2-æép!ŽwÚ§düYÑt¬Umd¬qÁ)N’Ñ
+9E_V´Æ…Ô§j×KaS v3jJ{iÙý—Ãø ˜K‚xƒFî]KÊnsîy9Ï‘Ó¦A£6 0ö!E{#[óñîóï­ƒÑ/ë^Ñ*i£à³Ñ€x žáÄMÌ]‚qÁŽ³£Ç6³öq²Á)×ó%ÅH$Æi_b_xcT‚‹ûÆÆð:2›;zü¥„´ƒaÅ_‘÷8ŸEOŠûºa«Ð¥p»?–ä¶|ÛI'H(¬Ï9›½ÃÄf5.qéL3wxä<Ö€•é_1ŸÂäJaû±ð­Rjw5äcšž­Í:†Ž]ÏR„/» ´Ð¡ÓÑ‹¡5æ±™¬fו—&AM£NIüIŸ14ÓiI8á»\úg_É•
+Z Ï#s¡y“ÿ¹‹À]ZÆxò…¶á¡P•–Û™|ÔtøjI»D‰@Q‚Ö»Ø
+ ,ÃôY®`RÀጚۨ~=ò?ÿ Q”EjZ¦'ë†IYÒvÌ‹fk d)Ü8‚%Ã×.ÖVI¦ýX­)4Ɇéadܦٙ›/ÆÆÖŸåÀ[¶¬ æ>†¦Â›Š?Ç£ŽD,h±ÏÍ@NÉ’ © ÀFdzHš c š¬½i¶Sô„bSL\ñó#§:T$·É~F-50L|g5ÙlÚ@Ú‚\äž|­uÿ©Ù` ‚d(pRñÏ©hmîðx9þwŠš´³í¢­bßÏm°ŽŒWôcÁê«hP=„ Ð‚ˆ&û>Ga‘]IÖ?¶‘þðÐ@Lv:»õ‘Ÿ5øø×güfÈjT¼SÁO]Š…C²ÝY1[‡>K,ÊôÅ¢,æ¹B¹‰›%8ÐlŸ®XÀܸý.ü‡î‹E!ÄñW,[¯É)±/ñHp÷¤v;5Ü9+ÑA³ñê„*›fn$‹0G> Ht)³®9›w3‘ÁËÝ@Ä‹…¹òIk
+L96
+º‰£¨;º¤W…Ž¹2•çáá>°ŽÈÍü³H.ƒ `òf{îÏo9䤫/¬F0R†&+{w§h/—cHt÷J¬É²‘ðH©9úç0‡®ô‚ÁÇXw¼&c@ø6Ï®nŽI ª’0oçKôUA²²E3d¹Œ”ìL‹Œ·fä–×%QÐkCÎwü‰r©§{Ú‰öþÔ¯€ü×?&ÜeÅÿ/“,IN ˆž@W©8㺷} mS÷ßê8¡Ê ¢UšÕmE€6° Å/…ÿÎòúÔµ­™?€ŠÒ~5ðkñ0Y1–Ó  ª²Ø’„®-Š•md^‰N×ò¾9뎎å7ˆLaJ|½*}MÉU¤»HFN†¥mñIl“ZDŽ‰ÁƒØ2‡4PÎtKmGFøÍÈÈ/
+ù}Q~\Zµ±‘|“8€x†óM ‚™ìªkÜ=ƒþ`
+1ŒÞ„ò±»ø¦âª¿Bl˜¢ÔÊBžJØÚ–TZñpðmk©,9T êG'üν ¢A>#ðT†žeiÇŽŒè¾‹’±TæF‹Ï!ZT§Fíÿ W^Í W¼d©N?0k2<MÍûÕĹ"£hiûf¦ bªñV ZŒ¨;/mGj9õüFQ/æß I,çÓ9xpߦÁðýÈQ]RÕ¶éø€tž“D͵í — ±»|™´ˆB
+‹Ô,mýzSžáåXÔÍx£àmõ D·…7Ð-¾(㇠μKfYSq
+Ç°ÜOÀãOøEÊäÛPØ'&ôA“~9ÊÚE–œˆÿ…Ò»ÛmDZaÓÅÀLZø¤SN/æs‹èÜu߈7‹bà
+q öxŽ1ü>:à,ä˜ð}ÚÄj° NÓ¯ ûq¢º|}‰â¡YvSÃlcûGs‚›!½ž¿&áŽí4ªðHz(e­í öŒÒµ±Éed´LM›Û»¦/ьഄ±¥µªYh'n?ðߟYô×4ÅL¨žÉ¦æA˜3ÕtØ­×aˆAlèvŸ;jì‰iµ¼‡MÏØG)_ GŒ‘ePèÊO  éêßkÁ?…á ’Fö´/ˆbE,(HÞÊce ÂäŸ*_ŒDNkËÃ9…\ÁY¥4êù6…â
+ÿšJ&¹,‡iH þ„¥T3f‘òðn$5¹¡C€X‚‚-];ˆjM¿üK9Þ ­Æ†¬s¬V0 W¤@ØÛÖú
+HQpÛ\K©÷¤ht‚PNäFSãþÔ ”»¬v`›’!㦡܇Ì5¥Eª ‡uß+Z?`Q”<åk#DÒ!WJÄ¡âK{Ë!²ÎÎÀ ¢ O(‰Y<I•XkÚ#5}‚~?€Þéë]@FÚÙ«Õä¶ÅËlT\!ö™öùãà&[Qª_: xÎRÞcLo!¹xy€ ÝØ¡]½œ…!n½¥s„0(o8Ä´omŠõ
+KB+Gz[ÃÇ]%æ Ôùæ±(LI‰óÒ6F¸ H
+¤®Å]¯‚nu)ÊŒ`€˜¦¾©`K^áyÒShƒ‘.if#ÔÆ£,/îä—-/”e™\z°0'C”˜fÌ7[ß.úEl†õ×uf¢ÀÙv+²„¬uÔg‰=㋼1¢ñÚºÎ46ýÒEM¾’šk¶½l6õKß²¸YS;oF,@…±/<*<“Pvl-2|ôSKœL~îQLòÙÜ Â{
+b낾aéÕ‰5Ä7Ö)Ë85üúÔ¢þ¡b—vLÔ‚1oÉn «"R¥AôÑÌ4¥E¸i„½Òïήv¬¸=Š¸â$gIº8‹×á fØ…º(‰Õ†¬;Û`­du»ÜHµ‘@ùaKdù_»à4*¥r‹D¶¡36éH Ηñ/& ø1D1©WaBxq
+®c$Q.» ¿‘n'¶ú Âe1mü‰\ŸÚ@Ê^|˜ÝÅ]Ø«°mUÝõKÈå&Qá%šDE.Ë4F³ XAìÒûú”¦‚ÓQÜe(­Aì]±ÕÄò36p™?ÇRvŠHgó×Çt«±è…ñI5ŧs¼†ÞK‡ÌóXÓ†&4E?,²ù ´CÍ~¶) vƒ­+ÝÂxlb‹‰&Ì–ÒÌN°ëÒDŠêòAð3Y$඾tîЧ˜VG
+À—Xà ¥Õür¾ŠF(à´ˆ´.aTtƒ”ŠB",oŒ³ckj},ðf#ÇÞõáâ¼·sŸ Zø¶zù±O±¹Œ#¶Öŧs˜z58KæmØL„@=7EtpQn<µY£|¨ƒ÷1KeMè¨r %­ûÜ—áf2ɹÕé?)…È‹Yý5=m~Ç«$ì:$Y§
+V
+â”1ÙÔ8µOºBA1xr3p%"mص´>åyI KgÆmÉMªíåeŽè ÁÄqäèĸ šÀ#qôŒn^–“_eíÁšx@á%º,ëÊH1 k[ç *²ðan:.9H‡ŠãL ° z^5áúÔ⯭+ÕÁ#ö*'Òk2ê–BD_M.ÝOÄÚ"¿¨Ñ²`sl¢ßÝ]勹a ü²†=BAÔ>ÍlA±~ôçøê©(¥ÄSúRköPðaÈÂùƼÑJ$Ù]û‰÷éø£JS'„!©\î¬W‰3rAGJ^ŸªøVÞª¬ˆÖ4‰´Q‡Ü|ô2û+:1â-‘ÓJ|@¼$Ü_¶» A¡€@Ÿ F?i$]œÞŸí%©8Md³!¥R¤ÃŽîV¯D'Äd§øá‡p..Æ«ÈÄ]´„—¯dIX xžf“Ó‰cLãü_ý|Øè°˜ìy¬Ò݇sDŠþ)vc¼kW¶jLöÛ)a¥ÿõ%D†ËP^ëÀF‡ ®aQQrtxÕŽ@I²BéUÄ—q(å¸>µƒ´` â¨ñÞØ[ØØv,é»Ax‚²d;¤hOO³ÊÅ‘¢6v˜I5P«NŽ ƒ¥Çk¿Ž‡¡ìÞEÖKÒB$ÃæáýÅm%ˆá¡›tmC ЉàýâQ&„}è\ÀfîƈɌŒ9•AS¯ÂD HAö<›x„ÖÕ#¨¿„kíPr)›hÇ$¼QV"ƒMl©¸©^”ÝR©N±© ×{‚pUãÐק6ÐÝùsD›Q,Wäã®°b_š©@‹{S
+ô«ô½qÐ%M ϵCÜüZ3ãuÉèQl™ô
+ËÓÓ}fdd’ ¡BGr+«K‚u”Šl œ q§Œ1 €q(úC ÔdÔøToçÈ^zÏ´Ò˜e¡®ªDQz5HÅ`ÀË
+ª$Ì¿Û”¾èØ6lŸ'š9_~
+Ò猘lÜòÑè%ZNÁ„ÙG@Ì/òF$ÑOCghôX6Õ›ÙÆñ
+Ã.-¦bº‚a™§ 8õò}|šÿ’—Έ‰´®­pzÃP'b'ÏSl´S¶›
+ïŠÈЙÔÎÅBê…k«é\âäÔ©hoøí6·¡¹=ê0z{iN¼Õ÷´ÿ½NÓXVs=Ž=ÕâÜ:.„Ö dHÜ ô±: “¥\³“pJ‰˜ÿÚ&H³ MöxÍôù“;M"nDPÎ åÊË%Ë͇Á+è=ÜøèE‰§‡ÙEO“ýOûƒD¹S¶œS=º·þÀzÀ¢jnXÃüI±÷}ô ˆ­FÂògóõ˜@˜ÊVë(¿Ÿy"‚¶ƒxO~÷tY…c˜òNÃ`pr(g*Z2müß&G~¸qh„²¨Ë·©|¥°æRó ¿cà ÁÙiÛQ»Ç¨ ÊaÈšJÛ‰cô‘h˜±•B¨“ÛŒ­ºœB
+n—’õ6Ø„ðe{TÊ2 ê¢@lýhÄ&‘òù <卵ƒÆäç3 ÚÅ7·Y6/í‹+¹_ OÒ#šÚn Íô!XÚÏiÆì=sˆ.Ã%‘˜üŸâV,›0o¦Ÿ#Pô! ÛÆÎe<³%›ïeàêJHÞÆ£Œ$—Ñæ(WHß]Žt"+&êÀU[ÃvS}Åüºb(9©¾µÍ?Ú×™K1»Æy]N¢ï¯+¨Žs^ÔÁ/pÒÎ Û„7Lh3ÇNÖ—Z Œög~#cëžÃBчb~ÄVˆÿäª\Ê>­“¼se&7[Ñ-Éòǹø‹R
+Þ’‡âP¼šÉŽÿ<E²Î\
+-;c+F¾,§Ðûq0rdÁ“Ubc‡‘r¼:7!–Ѷ¹¦Ÿ4°ð³¯]ÉMSWØ)3ÐZ4U¦&´š¯MŠ]µ­´)Õ É%xz\Š”¡0ÄeÓ3ì”ÁôE™#ûbcXGpò½?´þFžXÆdŸ Uƒ"ã-ÜŸg£7D‚ÍÈadÐÁAù¹x $Ä%¤ÞBp“•9qÎ^zOKŠ
+ JàJôÔè)5Ë@Uû¼€~(£â\³Q‰A—dÑ–&n­J‘®ã8Ä(¤¥ÑwL†<Ç9³ÒË•¸vöo¬œVp2’D4Àaу9êRžx(·ÝÊù9rL+ÀLg|Z§ï+èÙã4^PI¼ý sà¸T?¥B×0H§Ç“¤ÄJÄ—N™÷~¸M^sãÖ÷ò%°îH>&‚Ö#3´‡¢ÇTgÑo+×¾ƒpûé° ÷‰§¯´ Hn%áþ®LË+ëÐÛvß
+BÞ+Ìt{ËJE8ÊŒ[ñX®˜PG¿R|Cò“¤(ä0n>œf%`4Kã(<™¥`#l‰T9“8A ²«Àß4ðïZúc‡ìʽ*.ɱZ´V'¾e<O²%·ÒÄ¡¤ü{Œ
+–pòü¤ÔG¤+ÊÑ{š™Èã¸\'çfä}Æiˆ{¼àÈö¬çŸn[­‡äÔÜy­fH}¯Å} ¿­~¤àß©üG6¦ôNyPÝlù¢D¿D9•|¹Â? öh¥y‹Q9¨íd‡¹Ðß݈ ¨j@äøªQdZu;ê ‡G{’ž_@!6Ê÷’â éCØ}ÿnRI)1ãù}^ ‡@Ø.Iþ¿”—I’9EOÐwÐ ÂH‚ãZÛº…¶Y÷ßöû$èR„ÓeY*“¬2Á ãM~Z'(*H…ÊÖr6E[-("#C8å¶-•%É­j¸ÑF”0RÎê˜_œ‰
+•Í)ù"U#’„õ
+
+<ÏÁÆ­?”ðhÊúþ´wºqHå­j]ëp;°À3÷rH‡ëÖ¡…NPïÍ9,Í$Ê*Ƶëõ°=\¹÷¼§S¬ŠlŒ†TçD&Å'áÖ|+Û7Ñ€™&ª5ç}”<©Ó[Ñ?EZó×±æ>¿¢¨m&C®¶³<¹”,FÈ0GºìÝéD±²¸‰Ðuá\3ÿਾNÕ£š\ :A^àíÕžóŒ*¹jÖ«J¼X ÙGÌýiÉP¨ ªçgG:O
+
+ å:νèO{ýúŽÿ©µ3¬
+ïiö ¤¨[è&•iק¢Ù3†Zžò¢+b()î¶r¿ÑÈFðbU úIa$îÞ# @ᛲݯµËÈ¢Ã\]‰D0`ÙV̦D:.Šø¢4âúQ\–ædÕ%s#ž_¿ÈìT4K¤µ)ËióAÂf a‘»STÉ3…šµ´¤çVDÚc]‚
+InÒ¤µçuè4žwðª}Òª
+Í2FÖÇ*a@"º ºÍ'&°Z$ß×ÑíÅ>õ¶~¹¸qšÁPç#e°˜X˜[=+g~qsƒÝ’JæNE¥•’{?¯S˜™dh­öd t1u/á:)HõÜKð„­ô„ë."ŠÞ„ñë¡(eåÁÚò×½Fe.ÅÎ=RTíêÌsb¶
+ÓPoÏE´`2èìëéÔÑ4FÔU4" 5À¬§…‹×
+è—Æ\ Y%¾QêÈO1”7=­#Ù „:f.? ü…b†‚{>Žûóج~|ùy¡&à–1 ¡üƒXÔýâŒ9Š¡9Ä£ÊêùˆxQ93J~­nÎá3ý´r^§¿lM¨_Í‘•qàh½®gn9À}?p0ŒŠ¹Š–Ö½RÁò\x½2äU!Õ¥µf€‘mwJKÞðPò†ÑŸÿ;Ñ—½Éàÿ›[чí¾Qô¡
+gr}¹ÂJHfÒÑÂ~ð
+•x.­­²|“å¿âë`\ J´fIÏ`HÍKrF""Ù»X%<ä/ïç[YÆ›°Ç"æúV-£4i8N*™1^ïá¹”Vcü…u󩂦qýŠtصÑG à Ó[!BX÷+At0À—·Oyr"®Z§{)_È1HÝN¿!Á?! Wø±gv„eô`ÀÕ› ÎÊ“ÇäO y’“á+)}NB_éDNXjí¼¯3]"rÛ§™)Ÿ´
+5Ä6[%,¦“ä·yÆŽí·BÐÈîk
+1D™¸·jœ¿ð@@?KÞÃÖO'oH>]ˆý^óu¬á%˜HÖ,^¼ƒ+Э¸ç¸®³¸Þü²Pã0^‚HñäÊC­\OŒ‰Ö¦ÂwªQqçäôWvÍ•qgBC@ëC7ßJ|+`ÄŸ®Xû´j8„\Øç§ :æÓª—|è%Êêx&œ¸n$FH@7ïyã†8Ü(áQirø‘{ád@9.%ôT`«ã7F?Šxv
+äîA‘–…cØÌÞ‹jò”Q´¶Y=B-pDÑ%Z¨ ^¶˜±Ööx¬ àŽWixøã9v«g€_-Â:OÛIÀO­ê´?'Á…£ã_qW¯] 5iô’è„úb&Ø;møÎM>ÕÒŽò«Ðo0ºõ½B#SN0Ù1—%ú¾Pí
+õôûts/·6žm A·B.¶…ê˯¾:Ú tUžo¸ù»ƒ>Œb(àçÓ0ðí¬#Ð
+Þ»•ü( šNî…n×2•ìǃ º‰†¼b‰ŽÉ"º‘1“[íî¶x·Ló!@ågdÞ4ìî3éŠ!6›¹¯î¢Â“vc•inCšFf‚&‘Vµ¥”®UK§²ê&è¹ º?ç­ÛI™mCç¤T=>3¡×ì*·ˆSm5WÕ?½€õƒÎf0VPV2ìà!S f¤_olç@KÀ8—ªÃPq3×q»_E3Qa *Û£_­Õô©e¸ÀQ
+(cF³š±„·Wiê׎U>Bœ2¼Î©Õ?ý~¦7â„Ä×–rB{{ä?XȤÿ$’ÙJC·K£‡;Äò€U$l6/²`‰ë¶¯ wóËh]ð _€F»‘$Þ¶þ`
+H‰”—ÁŽ\¹ E¿ ÿá­H%RZžÕŒƒ
+çÔK/a>š¬xì¦V\Ýml@Ÿ_þ•0É°E¼¸—–°vk¦ ë6Úñu‚,fV˯^‡ZŒ~œˆæáµ(ï[­Rƒ½2!½Õæôn ¿æMzãÚ"c”;Р᯵c\çÔa¥•ZŠÔ…È÷TQϧûMGïâ£öau&ñôŸ¤:ŒKÄ> ò[!:ï„CX³ vã‘ÑÍ\³°bH‹Ì²¶7ÁŒ½›Nˆ´á¢Buú„DéÕ‚r˜ÅÌ¡Àªî<¸»Æ9åãä*ÖǪ…«
+5l²ÊÕºg8”ªÂ(‡Ôº¬«hŠB=e”ñìœC)»Ó>+š¢:jõ¡e1ÃzÕA@0dA™i<£êÛU<´6H8|%0‚»¡AÕ²HH÷•ÞÞ/.“ƒJ@YöuÕÝý&å F£„Nj<€6U¯þXF]îAvƒmM¤ÐÑuÒçá¶÷˜×télæzê’k—¬JÂ*ÉG:
+…‡#g3?‚ZvGŒpðvŽ2 í^:Ìú¢H?)Ä/í=\÷PÜjí @QŸ3 ;Póæ4š©FöGBÐÚ¯D%DuN’§ AÈ"K§5ÅÍ;)ž-ÈM›5ÛŠ9ÕF„¤¬wõ°ê± c ØhÖuYW¥°ï®}vó¡ xäW0ƒî¤uì*qï§m¹6c#ÉZBHb ¢?¥æÞ׳\ öõ%Š¥*EbÕÆ„v”‹Þ“Ø@ÖËk«‘®EŸƒúä0cú]M{Á1'„ÑØÑ@Ú~]%æâˆ%5õë*†n¼÷Ù?<“ŽôBùŠ•yŽ4¤×çzeYê8³…€=¼céÊòhÇwŸ$ìhb|h׃×}
+õÿˆE‰‡zß²!Í}(òÝ?hCáû´lÁ
+wbü0&öò®¡>½õ)Tbäììê׋5Zš¦j=5. bÜßv‡yý@D©?üýEŽ_^ð’øCÏû$§²Rò#¾ †…qˇŸ_ôøéÐãßGn\†d—„ýøßQ¿powú¥¢Üð$¯å»u¯ Tú3
+¡äÚ€Ü'I„}…QT1½”+Cûqý•¸®ëƒ!ÔæGÏn8#úÓ!„¤Ä ’Aÿø%¯!ƨ©ØQJrD<w“ž–€šEi€1¤x…^’%Bž›[Nµ•ont`/Ù=™L©9 bp^› >nâÁÑ(Åi¸âUxÌãu¯‰)Ë2kò·ÿü’¶;›Ýd«òû)0ï Ö«3Ü“+â[
+Ò<ÑïMµí;SðÓç—ÿ¬ò”–_^¾Õ>
+ûŠáë{J
+‘b°
+Œj\
+~¾ù•‘³ÔN#…ÐÜL“ÌÒÏç °DÀšTðßõ| Ð|wÝ 2Ú…¨øç%ˆîdU¢qH¨ŸúŸÜøó?_~ø/ÿÎ?_ò‹âI5* >¿XŒ…­frúׂbHnö<]o¹„ïçhy}bdÑ¡¬<xàã„œŽ[i™¾Ó@ä*Çl¾Y@c5×Ø i)åR"mF:1jOõ˜•¼UÆØ€>oAÂÓ79½ÿç4fQ²…ê!\Å¡£·œD‚êÔ†­O¯01¸ø6ÒH{\éÁOÔ¨ˆèuÔeðjZÞÈ·7Ì\Ëf²~“Ëâ‡õ-„Wš!“¼½Ÿc‹Á†çE¼åiBÒûU"Ä—L«)¥c«`„O£³]ÓŽObàüSm£æUÄžÓY?NãµÅ¸µK–£÷T"ø0ð5l?_· q#cÌP(‘\ Ñ=9±çêð
+ËD².j)?ž»iî®[ˆåžÓ¨HÇ­«@(
+ÏïgdAfÚ5MßÄdkÐÕ<Hç)¸Etb8Z5Šù6~Ý3ºóù´XJæ9­ Ÿ–¼÷THìNÑ èb3—S·QŸÜ•þŸäóÅ°"À!¼`þ½LA
+Þ2{&$Òˆ¡G2yƒˆÐ‹¸Ö,æ¢:ûŠ`¾ª´K2 G³I›çäBGZåäì,8fO ùtI¿µÖ·-ˆ!:ðfAW[å‚pÚ1R[ ›@[7±®TžÊ„^IFÀQ´, ,YÁÐ,ŠÁÞUs2Az´¦÷x J@]Ÿ¹Nr”–ê¨KN0÷tÄâóa䔣CvŠ“MŒCà>_ŽrŠÙÍÖÌbˆ±ñÀ@Ž?öâæ¸"–EžVŸ Þiä¹Of_E®§%>s·ôãp›ªâÒ¿>ºš{ÈÆø
+ŠF±‚£»;ñ%ÈýS· ½€~|eß׶ßv¢¾˜lò }{ÛÅ™ê般WÐZ@­ V„ƒ ¢dZk‘W2È„B™ ²!( ä‰o›ãQ.ÖŸWô±¶«BÅ(Y‰Šxii)ç¢ex«õ²J´häR°#Ñ43ÊY Bêâ 2
+Èý© ýisðw–ŸúŒÄIÅâ$,Ir: {¯JŽó”H&‚²•P~=?EÚh]¿/Ô–”†vµ´D’äŠ?Îú»m§äI€â¹–8øª…Vâ»|ï"˜Ä >Áqd X¸/0Ç|(M{8”WÄ+±ü4{œ Ïjç0†Sâ›Bq3))%#wúÚ‰ÿÂ0Ò»XÌÞ}o·©¯åÑcB
+bpð_lMÛ±íƒÖb1æ4)˜i'cÕ{bk®"ó¢ÿŒ½ü›Ã‹†1*ZãaQé‘Ñã×h….*ꎢ"¥(ë=’ÙQ””#2 À=Ãu#³ÂŽìO‘{0¸åBäæ:T« ‰Ìüu.9ã íÑô»&NfÆÆZrJ;õǨywAŠŠ÷ÝÐK/‘ꙡ Âþ¤db³Ápe&©V;Þí»ŠB.›9¬ûS°fK¿.ܨUëG04ûSZQ?‡Ù¬£–Xsœ·YO8›Lxc@³Íºæ<3èìél®Q\:Cq æe˜4¬2ñâ°æî9gSó”ìÊD1)Þe˜¡f*º4!§%O
+‚4‡ˆÓ'‹›Á¸SŽ¹S—R\ÂeB(yûlÆpà·Ö
+•¤óB”°9› ¿’“rښƚÁv§XÐÓœ²'Ä(Å6ãy¦CÛ^ç¢ÊQ\(A~Šƒ­¡t¶ž˜ nˆ6_Aƒ*JØä“$˜ìö\»Ç¡Q3À¡(Æ~Qn㘦¼Œg|Á²ü¸Dw‡ã«I”H1x)›MN@~æ°‡
+ÍÄÅN·3XÜ•-‚
+ì+,ÍÁ{c:ÍÈàƉ|e,ëŃ`yzÍt“wÀ*= ‚aŒåIFØ›å@˜Ú˜­rV (Ç׳‹½ÄëÂm›+. +ŠQ!N‰|{(ÂX|-u˜²¢kó€`£ˆ–²æ¶qÌxÈÓ>$ —Š9<e¤AcÎi^ø°“\˜©Åa7òø÷§® Ö4Ii¡<óÒY>&Û¬D'ÛÄØÛ³MTÙJÍXxº€6W‡ýa™wxbš œRFr@§ÀúP­e¾âêºú7¢½_A.éšUÛ:¨ýŽ…Ú/ŸÚ—ª4d ÄÏUöîGÝûÓȇ<>uݹOªÁ ÛdôˆŽb£š¬åÄPfÄW÷U.zØ!Å
+{î­IvØÈÂÕ©Œá´êäå×R6옋Hº+bÌ7>@¾oÁŸÈÔ?†2£'ˆlA»1 ©ÜF®Mà ô(U¹Úd§Pbf´Ë<Ï}Ÿ›
+}
+­0n=£±µ†wè‡wÔD­9 ôjO³ ¤ŽêG|Ã0™jQhÞüôÉs‚>¤: 9`̘0ùÄ
+ ¿‹³ Ò+ûYø5ÆPà(mÏËÃUE½jÍâžT¸æÖ IS2[µ –]Ï,×ÅdcÏNê¹’ Ÿc¦ìÊ
+c¤EI‹º]zèg‡¾˜ƒ0¤Ô“ÅI­žZB^9G{:Ä8åÞ¨i}l×ç &Ú ÜÈíìi@\)÷LÅ=eö UtÚ9M„^á“àÃœ½¦rZ©YW¨~S‚á¨cçuL¼Ò*ÊΧz­.d@W]6 Õo* xæBX];G‘—ÏcšÅrQ¤Ñ“@¬©$Áˆk¬‡YB9¹£öÝßÁ+ÈïWGO¸ É+sƒmç>Ð÷òãs†— üö×·€âb…á·
+áiI8lÑVï¨7åÜ@’Y…OÄÀ.A¤E2Jh9~„‰rS6·Ž†ÖcŸ,qañtLÊ1/Pì¬6²Ìèõ%,p÷å0¢‚ðÌ…œ‹ÅqhŒL:4Õƒ™áß:‘bö@6¯È^3ñùèLº<H#S:aWfh1ÏÈYØnµÉ}±áÕ^…‰èòƒϸe)bë!öc TBYgª
+Y¯cˆ‘µ)báVÄxZæÑþ6Ü¢:ÓÿzzÖèý)(!²é·ƒÍGç1–%R
+’MZÀÄ«Å· ‚ñÒÖ>Ý®k¨
+Áì £ùu8Î×& ñÔ½¯‚Lf,"Áe´ÊC÷¾
+:]w«ùWA§Ü®û*èÒ–×7½Ûâü¯šôÿêÔBÊ"Ù\ä1kÃÎ㥌Žå÷CÆfÁˆ’=+L侧#D¢\B6ŒR¨/IÏ°Cz,LO…ëlòA ´C‚®”ß)VJ×û:G¸Bóe…ІQX‚¨0ÔyÈ'!ó*¬:ÂðO]Râ|N‹Ôë¸|…¶ᘅE¨¦ÁÍc=<#
+Šbã.Á¶ʤ!Š$[÷l(
+b<œ÷ûD§™°ÂMT©­¼/¥¹ˆºì%€à+ò×ñXÈRJ^
+é2NèHöi!å uN¨èÑw«B]m]ÅƪTBH"­^%&ÒØx[Ä~eû)
+¦›qC[ ‘·\É è²S½14Û$ãã—X[PCÓWÞ%ŽðÌGãýÝXð¦@
+þ—¥iè7F=
+[H–ùo˜13¤Lê’µÝÍDN¾T ÈjÅ<ÄC*]³€ëòa ý¦Kt/&hBi?p
+'$–v¡ßÌ€´ÅÖ›aÎ õIñ‚za©„¢A˜þË9&¢¥±ÍìGÐ˃ ׈Š
+æï’ê@#ÓŸ'cäá8ÂóA²‚f¼,yÓÉœ2èÉ‘j)Ô~¤Ç*£’@/fý¸Wo0<HÒâçHI}ÀMé¡¡!~•t†à¸¥ÇsºŒ+Z‡Ï<DŠbÜÆI!‰âê}HE2-)åæSÊ7tgׇ}¸(M*<˜ 4<
+;Ídî–
+Zq À\"Ñ*}é+¤€3A¾s™º»“P‹`rV5ד#QˆIƒw@}øU•¡ÚLDÃC¿ƒ²a©ÒǾêDµ”Òáèa?Å:¤7]ÄÄ1UÒg…ä„毂| 4¿Ö¾ÑÍ‹ôeÙš¿Šß#)(bí§üçYÖ²½Úš¥1méycƒèyÀ&?UA{Lß­>h4•¿š­_®´.ôÃ{0b
+A¹#ì½EZöª~ÒÓBM۞Š_Y87 €€dé-É'°dþáíTN!gxºýùts[‡˜©S“TKë>£Z
+ŒêÙàðvkšÏzrR LCŒ¤ˆál¶æ¤’tµ?s åÂN¢N4Ž¸L½p³o™)
+º«ÌïuùÄ/œ”%iMŽâ“5 ŽTt^š†³>¸U¾%t ‘o’J€§:>Çx Á-$Þ!èõôÙÍðó®N!B¦4;uéÊìîâˆá(V¯짋°©«…µWez))Zv5œM!m…Ð=µš³ãˉ@Ä–‰tñÍjì"O`W'
+pŒ§¹½¤¼±m „¾•êç@=ØÎ";åy‘)—-0DçüM‹«¯çLrÀÃõ"å!<xªd>Ä ?ÄržÌÜMÞXG}K&otÀCÅš5FÝãLø²Îé¼»‹õÌv§Z—ÂÖòCuC:¨^tʱâUãA”"š¬lèÐN@w£fÏ*ŸVu´ŒóuiÅz“‰¾×]è¡krDVÇæÜ
+)àY›ZÔá`XÑ Àrz÷ªYh…ÒëF0Éâ¡ i|žÂbý;‚‘Ÿ1¢â¬~°5æC`ƧSP­ð\ Íaâý—ñ2GŽäX‚è pÈ`¹/" *Tž`Œ òþ|^Ù3]eÖ_€ñc•™±x<GÖ+?“EÌ"²ämÚ“ÊdH†5:}i±÷ ¸²w,£vâQïGH
+ˆœ€èf7^#þä“O›]Ü-,&—aÜîÔgÀ—½ ¢¢‹ð€ÓЂŽ<f ËŠð]˜OÇ8‘t3²¨¯îô’Ì©QÃNŠIt`·á…`bã¤oàã\HaÔiZC¼,Ü°eézA˜ËSŽH\¹ìâ•üMU²¾;xXMÓö îE¥_ÿ›tžO(Iu ’<j=5rfWÆéÑD4“eG2Ëve1n]ZÈf*&6]L=ù0#½šøQ´™ñ¶£†tzÑÖíÄ[1S–@bG
+p‹ÍÆ|£™ô¡-;Q˜Ö_³£jÓô'üÞd!tÖÂ0';B<ÙÖâɘ͹P€‡£iÈOŠnèBGKDƒýs¥'5!BÎNз Jwñ{ºˆóÊoAAUt©µû°ðr¹2‰E½kWNxAt›åg–<F¯Ãr¨ âd!ü§ÀŸ™n
+ú²90Í$Œµw÷ùº*ðßíuÀÄ!pKs˜àP´·èõý*xú¢wGŒ9Š¢jv♤xÒ¸öNqCN¼ú¹°¶|4ÆÈo/ 
+wTÖ^‚*ØRQ©ùû
+¡V)`¶/¢ ‹¦"]ˆ‚ÖQ-¤#VÜ®{n"Ü”2öÀ=$vcqmùâ3úK )äÙ¡V„
+BŸ u
+8ïŸéý!Rê]j¸èC2“…PÇ0µEÀ7„B^
+±£N÷¾sjï6§öó_ujc/;—‰¸@ö§Ü„< ÕïÙCÄ¥Ÿµã<v÷à`Ù¹5&'èË ªëmìó.›ôåw
+ú~åN_ëêá^*»¢6G]~n‚¨ š‰Ù«(
+êµn †`cIhÎ;„%ÁÎkÈŸ]:“Ékš©sDíñxˆèŠö
+%«Ã¾ÂÐÒ1œV z ºÂVj€'3™zE1.Ò¦C⊶Bh -VÖ(pÊ®]Ð@0Ù…* Ár¬°
+JÃzðOs% ¬Œþ³-:8H@_hðtÛGÓ³"ÔОì…ÈÒZÙ‘¼Z©2õÕ·±‘.Å•ä„ìþÃŒ©]Þî¾Cû‘Œ"Ód·¡ié~¦#$AAˆZ£˜9Ú«@2dD0`­ž@2Ʈϻ†È¶ ¬ë3¤3K÷H‡y¨,Ü/“rò§Ùý´ñÕÜWà²_C¾ýî^Ò´rxkzi?|$ãiµ¤gHÁ,!ÄÑeFbå*Å‚¡EnÔzuçòÃ]ÄV-†Ñ?Æ0ÐÛ6àŸt$Ô·±—JžBì$ü4Bt÷~hžª}†‰"ñŠf·9ÉJ„S7Ї–¨tU¨°_ÎÒ-V6½Ì
+fà69¨ŽyoB˜®€nuY¸Ç(œƒ$¹ë6 M¹M€êùjÜ4t§4è¡i¨ø—Új\L%è¹"ŠgÝíG1ÉŒ }›¶Õâ+ü¼¾“8ííeÅAè%K”éµR!ñ¡àj«åobÔržµ1 û¨¡ò£¢¶ÑåÙSýp6uõyÐ_°ÀóÖtÊ‹¥(„õ¹Bp
+RYêm»£@gÈ'Æ"æÇÐõü–ûÙB7¸VØwŠ­•¬®U†+ÂP‰;òM¢gŽRç>êt¡.£Å@#®æ3Ø
+Ú0qf“
+§„À2“!>Džõl›ƒ'"$¤msŽ†!=)º!'—û¹Žbth`}V'èÛ‚˜ÕFã.*Ŧñ?v:¿]ZÂ`Jº‘IJ“s¢¯Ø‰¬;6<)ã:V†¤f ­¶èm’¡#”‹>¦¿÷ØÑjY}{-çS„T!èx´õ¼ûŒ¶¾&sìÞ:@ⶭ6t•Ã•ìÞJZžì÷ƒáö€“c0™î(\sHxÙi\¥´â†œPuÁø˜à"Xá0û„OãÕÎŒÚê+„B—Þ\·G‹@M¼¬Ü¸FqGr¦^vÝ'! T€øY= K™ŠãA@FØAG;"Ú2JþWèMŠ•ÛK®WpGbE²“æw¶¸XÀK cÊ„ ô,5Ô1L-´× ¡PM”Õ Ó½ Ê!܆G{÷ns±z×W9–ñš‹õ¼¦øb`¯•:…ØQ§‚{ß9µw›Sûù¯:µ±—ËÄp7vx¡ä­Ü„< ÕïÙc%“düŽçqmö²È ©8A_nÐÉã~¹Ç‚¾_¹Ó׺º¼›ÍN›áäg'›lˆ(
+ݘùc;7•Üeˆ`П`r‰®bÌ ¿ Ûn|È
+‘Ué¬óxì¾sȯ·#ˆ Û.:ñ¿ƒä*Mº¿Ã„ !††ÆB  Ø«ü†B2…<rkýâóÍ‹©ºÛY³™Žœë:MÃʨåÐ_â´ÚáôeAU7ˆó3
+5•r)„Z’çåç]@<`%› <"ø×(˜i…
+l/~3{¾ùNe/rhµ?ŽªrŠü_ñàÂÝhâ1ÛîdP`•½®£Y›Ak–0E~‰Çõ”ÑaV›æ.Jïò$éÝkwª€0×(o„ü|º3Ὢ|˜C˜k¤—ƒ0‹”+ˆgÄ`_îq§ ïWæôPIæSùW£Ëó) ¢“¡Þ¢wkœ3 (` ùfw‚)€bTmóªZ'H…^F,y…D(,§…g¤ ÷ö¹ªV>@V!úˆè+"ÀgR’ÿ/“äJn$ˆž@w¨Ð0ëêeÝB[Öý·ýˆø$3‘Mm&5éD1ø€‚oK‰÷/<fC*F‚DŃ0k|+õÜ6DæQËÊ£»kœ¬I³Ã<€Þ ùN†M¦}¬)'Qp
+›öÇÐtDU¢TŸžNÉ3”ÈZåÝ{ÅZ:/%(¯§/cÝð–6 ¼J{X Ÿß`LÜ`?Û áÞyCÌ¢ëSC/wC3V s%}:‡’„¨à°sp_¡a!ÁIÊü`¤æ‚hèiiï/1uøw¨”¿´vb‡xÓÇ>…j’Ê0€ çŠÞ«'
+wÕís
+ç)N½žµî¿4ó÷–Öð¦É™ÅÉÖ¬ %r[J¦¬áM{ÏE]¬
+€a–Gnçs³C£ÅÍ×ÕM¡ÁâÞ:Ýß^Ýô s²CñMzÓÛ£å¾N”q
+ãÌ_dzŽoTG&!ùnÝ#¾Až‹çUÒŠh0&o«âÝÇ·ˆ5À‹ÀjõÉMÓuD%ˆºg<ŸÍ13Ęڙ¾Âjy’8¹ß¾›iF°–(å¨Î•W3Í(³ý”CÐÏfHìr©Ý}2Ó7ЩçéA¬ÙY¤êÇ ¯ øþƒ%].ù×ÿþáéãM¹tŒF‚1N#S-­³0,2õ`Ñr=€DÞ-Å'Ý¢ 0!T'ñr–’—á¶üvõ^ Ť2É\Ý(‘*Äžû‹§Øó7pL7¸Ò¿©´qC]¤n2³›X;ô 6</?€¤x‰¨Á`Õ²Bɺ4#%Ý .Ü_@ïAÃØ)Ï´_@ BXoV;Gp´
+ëIPáêê¤]‘pøáC‰uB@øÀú§ð*:˜‡Ÿô õV&°²Ð U›O3øí(K¿Âú©Ká³ÇŒ{„.{ö×@Q¦9ã_<*[DJ´ÖçŒ!, évamC]Ä H‘mÚÓäÔ³’ç««s92¢Ÿsý;Ȩ à8„cFZï'Ãà7hõCFÚý(ú·lçs:£9XRd&$)v4íîL¢Ã³‰aìá2“9ÖÕŽ‚bôPƒ1l áLv)C ÅΩu‰¬nn±¢(*]y€TJ®Å âDÿÔ ¤ ÔTf*º/SI+²%Û6" —I«BH¥x”"Ú³žbQtq~l¬±0^14$4¾€0á¤píþR3ÉGY ³a.'‚úúó
+øJÜ¡¬‘¼>´{V)­‡7mE¸bÖãq#ü :>‚E¨S9’Yh'ˆ}©°ïȲLíÓ9Òà2%1&&·ë2ÓÌ5„5Î>á@b—ô±ç‚HFj¡Êî>åÄ#àêÖ”bCqœt°@ï k7oÓΫH#©II§-é®C&²ZV›Ô”+p‚eú^I³#T=Ü#YøF$mÆLEïö)zB}Ñ;œýñ鸑ѹ_ÕÖ¼8%Á³ÅAL0·Çäõ4Zs‚Ë“ƒEq'°*÷8°Y™IŽá+ùWž iOüü¢”•Ì‡@˜²†0²Ýf­'64jAþÝ›‡gF¾!ËqJB BEFµ°sFÏ4ŠVàBÆ¢QQ<>ÿr¶¸`kAcƒ‰ïÂn°üÏ(9 ÇÐ ’ìßšëÏXbö[·I”Õ4Y*Qü6] Ú”1ÉrÏ~Ÿè‹Æ¾ÿDˆ—QÕŒå¢lÈy¾bæ$²£n½î4‘ˆ ÷™§Ä¼î ÅÖÂUŸgÏ ŒegО&~Q„ÀË¿>CVWÛÄàöšÛëë9 ½žLt˜æž®ÑBzAZã†u¨û÷?'Ð=ćÇÓÇsO k&Ø_ë0ömŒo0_oÄHÃîJ_‘þìÆ6u†¨µ=AJt$X…8”ö%…óÒ ÙfÞ‚oÚfšJA‹œHyØfÆÊ'<ÄÏQ9ÞœHæ"0Á÷E?%)RÿÒ÷cø)V±4x£[µíè° cýŸcOìÄ%2Î
+O²–Q~KBˆÍUE‘I¬Ì´ ª„š“íâXx¤ÝÈÕTYV˜-Òk`?ß|A ôœt‡àr8{Û†@䆿÷Ä>%ÓMN@|ωRÏÀ;›AèS ¡¶¼:†;› ‹¹w´ÝH"mÄ)Ãfk7n .˜¥*EÇ(1óßRz3‹9!¼Ú±vÛêÊA–X:wÎœÜP îÑ=w}ØÓ»:“dŸO äÔ’TÓÕA
+l(ÇÂÀ‘„Ùloƒ¡Þ¨¢KùƯÓt•E
+©¢ècÛ„Ã
++|]6ìG ‹Çñ5TúÝ»Ÿ¢æ ¢Àf ÛBÖ‡x(Ãtæñ[+NzpkéýœÛ`\ éiÀxTZã]›ê2¨"²¡ouŠ^~W"ñˆ5´H“e&õO}¿Zæ”"·¬ 9¡g+Ý¥õ7/ÇxjéNÖìÏtjýísÐûOîô!›»\™Œ#†ùÀ 7Ï­|#ŸéD£E4ˆ\S‘VaCÈKò
+QZœºÏ!çЊÌ,Y¡ŽyJñ0ˆjp€Ñ‘kœnŸv
+¯dj( boö ”‰)ê ÈÁ®·.B‚!aÌèÝê_cô{8´22dõ1L¨ÑNâ7$I•:¼?-N 6ƒb°®£{ydEˆHøkWÄóMY1"Æ>'ˆ±œ/R“8ó{&<•Hám–+Çק® Âi|¼û§¤ê¼ªTÓÖža<ï¶O)RNH(x?GTôIH®þ©HåÈ-i3µfÙ¯¨O^œ1
+ÙKQŸ&ðûIþý¡šA—be0±£>í,ãÑ‹ÒÝ0W kUõˆ²
+ò¶‘ïÜ+‹Ê´gÆ5i/Â0ª>RÈL=U^†bYñ¼ò™ïg' ]D !Þa™Î~½v‚*)̾L2uJE"|ýd:Ð-¨(oHe:ÔÊÜ‹Aà+D é­'È;}ýy
+8SÍíXiý°
+¦P»¹ùÉøù'ù9Üi0à¬'Ï(Kom‘3U®ÚzÌ«KÙxUu(_™óºùÙ«_l‰ÜŸC2¶^0ô„Ð #ñTq5ñ1œ¦·
+óV€J®ôbDÕV‡þèÒ¹4BdQ_§S[\£ëBWhH³M¹Æ¹â„Aª¤Ê£·±h
+JÝ`DipËÔ9ûSãöeàb¹ìp5<§AV³Œ—Eã:AÆü©QüàþÊ\‡ÒU(ýX’E6¤cjw<E¹ùV ^THY|ÀuxÙÉ¥ Èù "ÄôÐ]{‰P6n?‡”Žãi#ùê…57µuF%²[µ7•€9{y\½7æ «¸
+Æ­ñ)h“;¯Ls‘Õ5{wq©çÎ¥´{‡à¨G~ávÚ] §kÜÃ:M-ä7^‚‡žÈËp
+ ƒ{:D*X†¦Ë·Š™.1i™? ¿ÿ”©bŸëà-` >Kn>1Û¥
+º„ ÄB³ ¼ —¥ƒz™›1¶\úeçTUÃÿT‡dØ5K
+y¸#Î1+|­‹#ÝZÊ^ó3¢ .ZÊžt/åÜ‘®†o¨ µÚxµµe‰D´s{A >
+™CðÈ<;%¿6[èå¢a_¢ŒŒ‡-Ï D~J?È¡ÏÃidÎb}¼Œ
+«–Æ@šŽÄ;Jb>p¦±l,ÕêP¢›„.¦U¨ƒ¢Çáõ tR6¡z ÔHg"¯Dtµ‡cG~Ù¦BÚk0/ …4NE'k0O è¸$žS„ã÷ÍxE«Qó³ ¾Œ”1Ç qÀ&ašt“gi¿8Ñêð1cN—ãÌ%ÌF°ÇÓ!’z!y+çÏØÛŸµaf}–îŠf»xô} "eA­™Æ¹[ò5Ì6cs;™Bp™E3ÃqðB W¥ÕNo`>U¶îÜTÑ&çÛ<gñ
+毊 %c¤~›ƒƒÀð:èÝ!ˆ›Ìú\‡/àf‹ì³%+M oåAÄ•cl«P?Z4ä è©ðo[C RÁ"Ðòen†wÀá |eI·ˆ€Š#èÝ…&Vfã!bÇ1äaëæʵI†u¬H†+/Šzpÿ,Uï,„ó _~®æΖŸ{⺊‹2ж\9"¯«Ã”CÐß¾²ÿ®Ó†‡É÷H¥´1êoòôHN£¸ÖðU}4úråš„’öÛn¢ P O©©k«gÓs(íD`Edü7qõûe¨'ɾ¥¾Í´;P¼¥Cl,õ* tÚîzûÌ™¾>Þƽp3´úSŒäâ°XØÞï${…Ì„c: j’Ì|RÂm˜¤â 0ëyA4?Ì=Õš
+HV¿G+Œm ž²A¥,¿!WÌÖŠpôu@çxÓH»ß@§™!2@vݽ€DÜø{††F™ï­CÃ.%’æ0ž@oþ>‰åÁcÙˆ…^ç¹]œ¹'ôÖåòã„Ü…“Ớ嘾÷àÝ5öx$ˆ1Íu”àÒ¡—4µãß:Ê Y‰
+æ \[AÈ*2Lù¸ž¯C<ê3®õ‰ªá1wŒ«êÄÔª¯][ÅF8ã ÔÕ=t%mg<ä\‡&é½jÁ[ùçwÝ óPYNb¼Z@8M#´çO …îaüødBà0:
+1“)œ\es'kvÎT­®çO86(ì¶Ò€åŽ±JÝñì1Æ
+¸ Ù ôöt®æ¥Û¹Î&5)éX1°<ÄÜì.”4ȈŽ5V xÈY^׋YõI`¡Ú\æh
+-œ2!EV‹ [·¸½9N§ÛV ̓‘æ†=”Æ8(j†’š¶-~3“³LO¡áêT6áC-Šª40äÈ}ø¨*ψ
+ô&|à"R¾tO”ÌßFÓiDÆ܃¼§(FßÚ$”Á·Œ6BhBö¢LTBb¸ƒÐ­éðdˆº¸â„A¬ñ#ÈóKc; TN›ðeuš9 Æ ­²öbZ"©l.¯£_F‘ÓíH&4S]šÇã’9×q. '}ûŒ
+˦þôw4ûÛ—p·AB#ÝÂQ1<„~åðôáñ+­¤_~ÅÙþr‹·ñŽL¶Ê
+ÿ¹•Û_¾u¤ìôE±ê2–Ðp˜Jf‡éµh÷¦°™ÕÇ€[h¡«x•Óý£ý•£­ˆ ëü•Åã<ì<ôï@¹N9F :Çíß´3¢¶yºF2Ò#<,ùYP³õÄÔ¥O«Âœ$%`?^Ç‚²÷9‰ÆHÅ!¹tܪP¯ —åà᲋74IeùŸúzبoŸ»÷¯À¶¥äœ=¥v³ á‡ë‹Aô®ö¾]€drƒÞþïûã$pšéÇëã/ô¿××ÿè@_¿üÁáÿôÏ/?ýÛëÿÙÖüýË{ñ›‚ÙG—äyk2Ü4O2‚ÃL^QnƒP‹d&HcFM“€yêbOÂg ‹˟àÒË„t£ã%µl +ªx\œqorûu°,xW%½.ˆž´Ãô§ Á»1¾pÜpHV>š*weFe_oƦÉ“‰l˜´Ï‚84®‹Ig`]f†c;*è{á#ÐóÁ¥CGéPx›ª€Tiì¼MP£ÔK9þËx¹#gr#AøsÚk0ð~Ø”)wO@—¼¿»_Ʋ»ZúC¡Ðˆ“DUYY™q ¡ÄÍñVk À$É
+[Ç·V×b혌!æ üsD$)²É•,ק—œ ‚m‡PY¤ÜOO˜¶ÀYáXÚc¢&žZËOiõÐWC0ب¼V\Þ¡ ê˜,N1Ø ÿ
+“(Á9Ë¿DéËêë¨Ü· ¿Í@“öÇœÓèÖþ ,Ô'úçÐ2VJ¡/Úë.”«D-ñ¸:7 c!tvcÜk<‚Ýœ º¿AˆÆjL¬™}H¦.Å8WáeÁ}cÙfãRÆZ8h"váãr˜
+¨w¼®bú¼
+ò÷²L:–:'[vÜùÓ´Ô ‰ƒ+ Úʤ~ŒhPèjUC{†#À«‚ô†„DÖíG£ùb^÷Š|…X?7Ë=çwËÝÛ\¨ã½êÊÀ{qn4¾—ØüjÕç+3³-ë¯òNö!Ê fvÎû~
+e©’Äa$Å°ãhûÙLø·P•þªQ²wr
+—B\‰#»I)Í9ïûĨVLBqQ1½wÅôÔت:¢\8¸‚½Ø-<X’Õ&Z~9b›g7gŽÞF2{,báu
+ÛF´
+OAâ“xÜZï‰lÕ&£eè·†–ëÒòO¼Þ¦¼SbäcÝÛÓ«Ê»|Ö‘ÐýêTEJ®¢mÝ;ˆ_úøã.ÿz
+0<3Æãûg¤:—ÏÃ5|ü` ¶èS_åM!?Mh$ñ`÷qL9&ÿ˜øž %¼”tc_Âòʆ§Ú/$o°¡¥×ÃlÁ“DtÙ/¿<þ‘¥ˆ¦Œ6#6^y#qûÜK Ë”º|¿`Ö¼cžÄT§èZº£@ßûæ 9
+5Q‚ÀYŒórŒÆ,Mãulš:[± ³ú…–áíÌÍsi ‚"ÄòàÒV§*3SY.å¬_ÎH¾¼ )-z»ñ…õù¡-ûö—>ØÞeµ™Šnt¬—¥Æ&Šø-Ãà#Y‘të€þ~
+
+H‰Œ—Mr9DO ;èó ?\»—} GÌʾÿvŠ,ÙªbÔî…ee‘ @&´I 1•÷j£½ø±ù0—Þßÿ£b/íí˜{è˜&M¢F”ªÒmøâZß¾ ^J½G4› 6ªóG%ΫD]ÍcAT¤˜xµ² Öjt¯}4Ù@ÖU®6¬6­­>CìZ‹ÔâþÍè¥WéÜ7ž^Õ_¥ºŠ1ûì
+µD
+ÕƒcϪ¦ªÐt½®7q± ´Sÿ¸ª.1Ap+ \MÎ_¸ÌkŒy€ŒF­`ˆSäçwˆœœ/“óåÕƒ)ÀyÁý¼ß ]´gE숋®òÃ4{oB
+ãŸàÎ9 I¸8Lêl×ÐŒjréc†^^£¨zšcõ4ü/C:ö,Îó^&¼kÅ”ælaøÛªÔHœ ß@V–.ÙAt„Ýdi…Ò£j|Çé[Òû‹1À0@ªÒ#Bò\þQúgòBN´µâ» íô”W…e©¿êò BøÝ ò·‰‘ÊÓÞ(€}ôa–#È’ê)Š)ß®1S„Ø _‚¬«¸–×’1ë»b\!«Ÿ‹º9çJ#ÆF-•)£;Šå£2RH׉“b96˜ Pïv”ê*Úº¨š)w^ÚŠ¯ žˆóé2á/=A1*¥#´ûª»þˆO@>2¿+U6=ûûÔ™€ÊÄŠ©, é$‘ËY¡syíÍ°DmB”¡œˆ¯9Í7Ì4r0#O ™e’“XãÞ¸9j²hžÂ_¼c)c´­f
+÷`nż<j]u!Ü4ÏÁ¿ÉºêB-´Ç¬.]Ûx€Ü´çKÿÑž` hšÍlØiÙïÐE{€ ´¤›`GÝjO¤¡Å¥àIÔƒöDzgEÒûV{€`ë¸Û„£¶š„ÈŒ0ýåA{
+%ái±¬«r@HÜùþ˜Ë€OuÆ/02ËÒÒ‡+g4µóMÛE=«œü¬i/ÑÔ¦ Ä gÓäÅd§[@<ÍcÖrèBaPÅÜ1Æ^ÙÒ÷0í`±:…í˜nŒfñU̬“Ó²éç–lá€:¶ˆ}lkÂnÓmgænSr{ÎçY{w–·™}u¨?ßv³û€UKecþøûNApDt%¼ÅcLû°P6Ä^Ký¸êÿËÕaæ 4E–ÀÂÙŒÒß ”{¿„Ô·C²a¥¬öSZ %¼ýÔ£ªŒVɾx? _:Y\1ÝuD_š‰f]À9?é‘Qã þßiÖh9rFܳcq/$eA”e¡ˆ¾\“‰¯´ÑÊ‹ªWÐn7ɘáá€iHïwAéXØ0ào§OýÌá „qDDkmLï• NŠÙpZ—ÓA¡ù˜æjAH2Ò<–à œ
+L{ÿ'£B
+K£MÐSEØ)Wà¯(MlÛ€èÒJ®‰Â"1ŒQ4ÈÌ–³v“\5sýÔ4¬@ö*ÃÓªúrdɉ±¯”nOçtœo½v®Aɦ‹ðíXMqOÅÂÜŒ "g“´~h,«‚ùé=ÿÇxÙ#É•ã@øºƒì1*ø ’æ†\¹s‚Žõ$sï¿_’`iªßt;«ž,>Hd&^6¥_7 Ä°ÌðÀtíû„"¾¦»‹‘í«Œ)ÕÉÚ13+y‡w”oîRíÏÓÙáfîm'óA9‰09¯s ‚ø‰ÜïØ­ne¾G>Vù ò±
+é´ÅÆN ÕP¦KwI­Ÿo£­Je©ÚÝ£ Î”ˤ¶sq.캖ø
+ +§~¾CžóEMÉà£æó9°K4æiaî$‡ÛÀR,¿„‚ÙÝ«`;¶›d¨ãFâ/##ó„V—|Ôn /½úøÊ|®¨Ô
+)(N_Û,PÃDY;Yì|Œ±æÊð`oÃB}H éÐtR¿ G‹ã.À”õÀ
+ØcËmÊ)lIU±öèÂÑ‘ŒÐJ3üsl ü;ð#«·¡J;Z"1e[ Ø”h´”Š¸KÃœÄ&ÁÒîAy°Ìu§×CÅD¬%WEòN¿;©`‰¹ëæ¹ò°8}Ú`ñ௿Qõÿü÷Û_ÿ[™»æ%c$2\Y5)¨
+\Ø7ó~ Ä ç™êµÍ@ÒDʆ4ά„™é&ºÓ–1ܤü‚ÁƒGÅ6…P ÚùY%¬Ç…éŒâÐ"„¯ch\VÅÚÌÒV0°ÖŒµgAØ”ø‘x² tSQœ4?Š„P/'ô)™Çúôó”Ø•èU³¤ÊO’Á0ðëâ7‚häÚŸÚ÷Iª¹*íwÐ<ôÇÊ‚ðÿF´)& bÝhaA0b[åAó9t¤D»Ì$UêLGâ:'K
+åܬϚþc»ÿ„ös>2¤*ê‹}Ñ´ð¤išPx
+9÷ÜpMHCŽPÌ«s™ÆqÙBvs
+‚oè±b¼?*hÎé¥òðüÒxã{òR?J:¸@ŸoÂ횢à êù›†6‡¢§VGôœ¸7jÞ'Y
+Ç07mõò
+çÊhOfhb™OœÔ¯ ãÄk[Ý'c99¦îÚGªؘ[ÀAݯø‰ŽJqùƒéaÚÆ
+Õ’wâ
+{.ù8/µåŽ˜ŒªHªFöƒ$Làû„ @—\¸G«ÊKj >.4Eß.1£§ÈÇút p“ÏÝCß0ZÑÊYá Y4h3°ÕvâßNÊ«áá(¯á«<= C„^¾ŠAšä×W ÄÇ
+é•5¥–½ßšHÌ¿’±-ø¨ózV/
+'[—Éï4‘³Óz=öÞbȘÏzJ+fòž Þ
+F›Gac!Þ1œä‹€ ÈSÀµØŠCÐiƒ`f[º5üÛ•ø?¾@¹O¬p _£s÷mÛ¼.á1³jx”áÆ4!M)F>YžD{
+ø¼a ›¯×ð˜†ðe;§G È
+hLë»­ïêš¹!9°w"ÏîØ%?ÂhœÓroÏ­á=?Ò¬ ÑŠq§S‡Î_uJŸ—°CüÞîÎi¬²ˆ];¿åGøMúD{‰‡›c—ü¨r¡t5B»vÌõAÎRìγJKÐòÜåÇè ¢ñ%Ð5?^@—üÈ@T­š`©çü˜§òðŠ”ì.>Òr¾“åž­žã#Ú ¹AQ;Ç>5[6¬ÞÅÇÂr§X­)ÞSð[ÃN¹é9>*è@,d Õ½ý]â£4·ËU²s|„€Ú’ÐJkù/ <ÅGˆ‘åôúû×—øç$Ž$E÷¤k||—÷«~¦¤#É&,‡?„–ß „ÂgÍyH;¼…–H/ÐÈêM؈4p ¾$ÚqZv7Ø&ù£ÝˆAzÀãDP Ûù1,ÐLß0sQ5Sdî·†Fq é&.x'.˜C6L,BóÀ•ï2æ£Ù'NÁŠ> ZL|ãBÆÌ­;K+J¢ð!¹psCFu^ãQ±o­9Ý!dhõ€u×|nâÉF•I>ÏþêØô_eqp¸ETD jªË""ô’žÌA?@üKÚÞ”¸rJwç „Ô¾wR p=ð(ºç¬ á¡+ËìèˆM‚Qb{>Š¼œé^©{­xw5 ¤Þ-ºt6–GæÜüS®+þ ]9bò‹СçîÐ`am高Dc:—Q@…¦?{ýúʈÎtm££èìgƒG7pÅKÒ‚ ±ã÷ (•€X£QE‰fBn©XºÍ$JÇBW0œø…M±¤˜Cøo ñµ€æ©g䋨±û
+¨±I%JÓá>mT"v1N«åd
+1çvÁ¿¤.QJ6‚%è&ðÜÞö§ eä:¡!û>sHcÕ˜_žÖ’T¾4žÜ£ØT!û?ûe·ÇqDá'Øw˜’c­úÿ'A.Dd¯DŽ`A@P"­0Z’E;ÑÛç«îž%w¦Ç^9ÎMVËÃîêªS§NÒ— ©QGÈß:*SŽ%ñm)±,É“w·
+¢2K+qŒØªë²etXÙ}ªßŒwç±ú,‹s§!hj,6¤ÁX*iÛ1²ø(±ø17S§2ƒBjçй¬—¸ô‹• óØj&rB游¬¦¡‚ˆ è2iu? ÌsÍ]1š¥Wá“Éf
+¡f a1|j cd~˜
+’72ÀÊÉÁà gÒ¬hØ;”žõµ{ÖÓT^Ëælé‡ÎaGoWç—Ã.àÉp¿8°# ŒïröXÖBÎÂ=Šm¤õÓeÚwÎ:0.gõÚ¥H&,+¢]Šë„ŽzšÌQxÆn?_"©GG«G§ÏžßÞ}qùöîòæúìöãð[ùêåÙ‡÷å“žÝÜl‡Ç›Ëë÷çO†ÏùR_~qùáìÍvüz‡=Þ^~ÿýåõ» úÅõ·w#úä÷åg4í£Óä·Ç~ 4ˆ¶ÝõCÄ°í²¡Gµ¤I¬ì 6=Tk@Y’L•*jvå ¶=,29^•Ì»Uëíj5ŽÝ‹w}Ø®6Ê&!‹Ú'·+æÚie×®9v˜TÛõÑéO4,“>åŒáÚõ…xoãî9#©0Ä+ýÊóvŒ8?åû‡Ýw,û.×ÍOƒ€N'­aihG5´½žÍ ‡=KØßüÂaFÖš–Þ£ÿs\Œì¤íºÖ' íBξ+]³Ø¸ÿ=<ûúæîÕÅÛ›ÛsèX:ô×c׳WgÛ—gw·—ÿâ`zþù‹?4Âÿù»›Û«ú£Úê¿??¿ysqúüE>%èoî>n/NïCm(þ”DZ”ýdþÓ~ª_·¿KC´ì…áñ“áä[>½æOÞýëä7å=„y<a‹"Ö/~õ/ÜÿRŽ«EÒmm:ù8óKÃ_þª†óº¶rPŒ^)>ňL+Qê0VaQc{mªý=ƒ6U~,–ÍÑEÿПMuÿ´=$¢Íêo«oW׫?­Fú”yöÙ5Ål+æ }…ÍUdÉ#ÒÖ™ì½XmÙ]Æ)„»ì#E»²G“˜÷VW׎ Æd [@Ž_pZ€­ úØâÀÓªùlR‰8ÊFDæŠÃ/冈h©Š–ËaÖ1pZ—<ÆýxÕYQ«T¾HX+Ìy~p0$Ê›-åTÕ¥ÌèÍ–Ř¡B ›†%Îâ¹åQ1Y2•KêKÀLmÙ.ÈŸSã±òLrçÊ&& d³˜'•b«?cÉá¥d¿žC†á—‹¢çµ9Dcø½À–y@=e©æÖDK6H|,2¯¹G£]®”âj”M’ÞÊ TÓèa–õ¬HJŠ| H«5_Š't™ŒWÐôºÃ@²UB)—­¬ŒÛŒð—ÝÓã^ Ï€¤ ¨*£Ñ4~$§å›«ÉF)’ ÆÙ*ı@B#«œ
+µh€pt«º]Å£RÙÁ´zL²¢µL[òÃ<ƒ7ÂÔ£azˆvfŠÑíuò±Œ_£ê9EÙQÄXë”C skÀIöÃ=îÖʬÅ0 $ZÆØì´mÆ×'5ÆL[8˜Ê[q{õá*™$î¢A¢Áá¢=èXKq‚oV¬Jè@ÚË=ÎJ€K‡”IŒËÁÂ4ˆ’¼ r¸†
+±€I¾ì^SUi÷ÐG˜ :ºØ @9Iõðx*Ôsx˜oJÞR#wóðh°Y ‡ü<^õ0=¢ÛB?\—q ¨Û|Óëí B·±¦ M‹óG§’‡2©Â‘-Úvµ
+B ‚!ˆ©çE>䦃„™hF¦pÈC#ufÄw í*Å7.rQèÎ9ÐêšÄ‚ÓlØ`•—ÞÄ¢ÒÚ±.:óÜ
+£Œ)‡ƒþÍx¹¤Éqã@ø¾ƒNPßµf©[xÛsÿíüA‚9®J¤»7²ÔŽf’@ "@¹JHÆÝʦ¡êYÌÁnô“!ò+ÄžaŽ žþ2ˆu‰H`a¹*ƒcŒƒ…HÏ`"lÍF[ Y.¶sŽ4
+KUȬSF¹™9ˆ]ŒVÊ}@<7ík§Àøa€”À-sÔœ$Œ‡¡¶‡v"¶ë§C´|*uæîÌÚ2rDCòF0ò$ÝÉÎ A7°ý.×Û£u9µ‰c]_JWjþãéûwÂmò^!Rå=¥?é; ÁrË~Õï
+Ï‚[±\d¯”á+|}ulÆX>4,¶W $ešY¤,¢þ6ÚÓ9Œ0‰$¡‡Ùf }’¥f†^õ‰­†W1o#'ÿœÎ¨³)ÑìzÒ‚°…jÿ"Äyð÷î)ZAÚT
+ñH‘qWQÁ—xA@•÷?Hüäq¬ãm“ÀȆ§A¾ƒ‰÷@„`Ö”¸ª+ñ‚ðX¤1Žù ð`‡¦OÅṡÂ!Ò3€•Žzm_•n´Ë&ïÌ íZ ø8ÚótNÄ·:„&²¹
+„r¢I;Êò©ð"áÈL»Vº#„ï/S+A/%º/H˜³°ô¦'…¿a¼–·]Q‰4O1õr^ ,½•ª+¸„PÉPpÃñPåÊçÜ©å²®{·*zPÄ'?E}1œ9Ö§Ö}‘GgK›Ú“Èß@^}ʺ4«Þ
+reþ;_:ÿë?‚¦6%V£M}¥€8 (8ÉÖzÄi¡ÀáØ×
+¤äOnX&Œ[_ÛuC`ö]Ô¹ iMæG€°¦ðæÂ
+;[oÍŠP&:KÙ¨:G[£8cጧRè/f#25"’0ͬ/e¦B¢:±?35Ë4?u{“d½rÁŒV/åf‘,°+ÃÜ"ë·6ë¾JüņŠõ¹{v1#TTj¥nCŸÑ©>JõšGU vÄƇT%©(—«ºü”Žï1^Æ\æ¶A˜ù:îsxO’FS#£Š[ãˆ}é½U÷cný¾_æÆïM7þÝkscñ½ÂÑß:õ÷OFFÓÎt‘æÛ)=iÎyÿuAØ3šÇª„{!ZFâ œÄ#Š¬»±"9mH¡
+›ÍñUt$•µ
+;SÌ
+Æ1$RQ®vvŽ«¥-2M#ÌÍx|ž0-Iá-èË@Êå´6g{WªD÷®œòþòÝÖ¢7ÚÓÓkePê$ñrLî#;Áõt¢>y‚}HÆrDÞ6Y•‰÷L6™}ª: ½S^*/^0FG›[_Á¹ÒEy‚Iòªı øe3ª|€ÌñSÛP$ÌKÿ¬¡èt ò„^u ¦€„©|(ŸQ§„S.„Šv&þÒÈj¨ú§Ì}¹êÿ”’’²Ý˜©f7›Õ ‚+ƒÛµy9L«]IÔ¤«J• ]9Äj'L!#ì°/ ಊ \jìcŠ¬6Õùl§ëG>Pˆ#„öωš5–ZæÒSÝZ¡U\òÌUe~{R§ýªØVõrAFΧ:µ€ûèÊYd^΄ÖþË–ò¼¶‘œ‡ÍçÇè‘VØùÔC:ãù1Ãï›N\O»$ò\N0âr$¬|ŒlšSøû°e1¢Í&L<®år™ì?~jL³ä^An`1¥ï¿<‘¼çÁÛlz±’ja¯˜£¤úéœÒ«†ŽW¥ÓvŸÆÊ’º1”d8µ¥ä­šö)J))MW£Ð"c¬sæR"?Rƒm\pòFUD3ÈrÂÞÛ8SpÊ‚õB‘ßù™YRL Äý¯œAGÏ·¤OÒÍz¤Ò^­ØÏuÖÆU í.ŸòƒÇðšk6‹”—üÁL²i®Ò²ÄiŸƒb=ðÁó¾óÎßËbCç\ŽÄŸ.±%>NìËb¡­h0ªrÆè
+ƒe‡ÃsU¢˜ôþ¹OÐ×Oî´ÒÁŠù¡¾ÈmŒê@ÅÏJJk¡I&ï„
+{o¢¿èóÙm{- Å•t‹B§ À(3“жÜ0(‹'îÐõ¶«Í«¸ëÚÅbw@_VšŠâ?¶æ€„@/ n2¿’Ф:Ò~U}鑈/Ñ:Yìc¼~í‘T–!ÓÈl¡¦3áÔ˜þä¼ÏAëè(üÞ€Á1H³¸6“O
+ p=­¸¿ÊSì¤Côõ
+~®"3ÂÈ©:oý„WÈWï›ZÅxlÛ ¶&2)úBîHÅ{ùçŽdŸâ”"Ê£þ¡’¸jœGঠCmîtK(Þ"%*s0ó€Kù‹” ¢e réYë*íGñfè. Ž5PdKð,ðDE=ÎÖh_«%É$Kµ¬²î]ì´‹¯5œÛÎÁûÑÌš`óx€Ô';Í°Mê¯IQë †}¤€dÁ˜ðØ8M”CÄY]ì[”®*|ŸˆJÿáMá¹Án ‘&#’Ž°kÏC>’â¹Í'æÍ`¿~âÂv²./IeOk.w/Ô "\Š£[4u@Úw %uÏ›HoŒ{ÈÙ Ôj‹ f­˜¨{miC4(]ÃÐÍn¢$0¤…¥}ŽÊõàG¶s–¬àÞlM+Ÿå—´•Ï׺ôô²>•iÊ`÷ÜžÎùãå’#IŽÄÐôêý?ëœe¥êþÛyt™b:ÜM“ 4È,¦$7£‘4ÚÕ3 RxŸs{M†-‘ ¤‰xú*@„ð m3W¿:@p DÕcØä/h±*}@D̾dÆûxßt1öY±=-+~¼e\‚‘þéûM@x/5Àçx—ï³6c ::Åíœ[…H¨ßä<ÜNÝ FŠ[ÇŸç<xó|̓ÞW=xü¬Ž3 JlS V]Î÷ÝT}½‡}ãð:ç)(«]ìÈ.•>mµwUQb/ t»íŽùóƒ™h Z]®E^J69ˆ>‘6’øà¿ÐóMy•[: º»éoﺟ>wÖ?L’³#üÄíkV@š4eð{5ƒl‘Ðdd]õ
+¡)'ârð~ÝOA¢à×d_TDbÆÿÒ)•8 {*Â.R®Máà»@H^“²žü[Û*/VªîÓÏ@Ôe™þûi‚u–u®g¬·çS*aM]¾Î²ÕOç°ô1¾‰™Ï§×0”º‹Ø}ÌZá…jð脶t¿:@ŠY9Ã.àg*qÏTâ€n©äñ–§ï>¾ÉóïGmž9àQágšxtÊK%Ž{çÜxã½æÆ?ÿ«n<öªó˜†{*ùnª¾ÞÃ×Êà"Â’—8þnî |8CèÅýö@®nܯssÉwoºtƒ½T`) LLrÎúk `€}+¸Þ×Æ?Q½ÌãK«H¯ÄÆ÷a ó‚$ÎÀ¸/ûÜtŒ4‰fua³XÑQ…EtA2:é+o„¥tóºì2(0\—їþ©y^¶ñ
+¨‚ò”·ÒÄ€JTñà*Þ%TƒÖ-¶Ò0Ó„£.TÊdª÷{;óO4+A—ý㼆¨C ™dŠúS3ÊßϨr!ô|OB-ýàa>À… ¨#ÑìíYœ Àñ¢A³o• †·4~»ò‡å
+Ri*7k“gÜ‚@Œ$ýÁƒØU¹Gü)Æêé1JYrˆ§×(ô’)ÇŒç‚í­Ë&R9‡×òg5îúýÛÿ]ÄÝÿ]Їÿ;o¹;Üó“žFéTæn¸N}ï¶íôéiÿN¿½sn¬ñ^scŸÿU7{Ź§û;P_ï±c›ãÑ1oÎmîí¸g£©é$wcþÏë\ÅøîM¦ùEMøq6@®"ÙxýÉüË‹˜ÁO9Í¿°KѤ’péì›AŽ+šNp9˜?òÍ°°-ÆÈĸæ/HÑÚIf+Û$?Íÿ‚0j$Ì¢úæ¦•×Ìc6íºgó€ów0wó/´‚iI¹ûæÏ¿£ %ÿ?æ_^Š·AÁNæO“ÞPÑZ|Ó.Ê×u*ïŸÍŸ–ó'Í¿¼RBa¬|ó/ê8Ýžílþð¯ÔÔÑ°’†oþ™h=PÌ9øÏ7 JωãäýwŒ×îÌ Ò1z­Çi¿ƒÜAþnB¯AþõA#ɯ¶Hb É^ÆV7ªŒqØ0G^ÏvrKÉHzÑ°Ä)vƒv1JÀ„Ͷ pµ-,–Š);çªRÅšŸä{°jvÇ"ú×$ n3­s$ì¸V$ÝdqÑ®AåÉs45ÛK"
+2¦5§/VDf§|&¨HÒð›ÞSû¦&A‘.˵ñÒ ´ËLåòµ£! HÑ‚N0&hSŽ´ÉAa¼¯*-˜FÛ¼á1uR(XõÃMQٹϾïX,Ó ‹ði½ŠP—“ñ‚ho³+¡-¢•(@1£NÌ4Z0¬F쎼RŽÕ ,'½È‚q•}… ­ý„ÍD›g÷obú:‹$߶Ë8;ÃH°#û|6ª —s‰dW:·Š¢cõtNJ†0a?FÝ QßiMe©Å8DiûjøG•¡e7¢bSLgUÖšm7À¾@³/|%>Vó¤†FÄ›¢^¥ ‘Ͳ2ìï›nø¸f\1ØnBÐLÄ{ú064…/HV`òƒâm²Áʌޜ2™ö.Ÿbt¥\sˆ(Ó*ƒK96QK Éb¹^Ì1YÙdi
+êÆ®Ôñ„œÔéôÛa;ƒîþ¹¿,ur@ ¹íŠÎµ#©D„çÄdñ Œ‹Ú!› ÂÍ
+é#o‡+äzÍѼ¼yLY º­¥mí™=ƒ3Fjv•žp`óä4):
+EõcŒØP®w ÖŠÎý hk§SnŠÁ[x>ö5pNeò‚šEyæ)<Lù€ÑÅ®edÑ‚3!;KŽE†Ԗ̠)¾øg@€¦aåOæ€þ@÷v>èÎw1õ™tø–e’é`W`hvq®K\êé]â(‹JŠ’Æ
+h¥LÖ6±À›b÷° 5d¾T¼/W!™@&íï"W*Owˆ –ð5ÍH$+jœ[ß*ƒÆÃp(î «õRÉüsPþzBûö¢TµÀ1C·€wÆ =¶';7«Ø½¯"ƒst.i:ëÏ…°„æ¨í˜Ò³òq‚Ìã
+"CöŠ;¤j‡Ö“i)Á.ÊW¢¡Qeﲤv4V¦nÇ@µÊŠ‚
+/˜Šþˆ×8@>¶Ý}ÕT†ºÀÀæ`çP9ÙY.{<û`úø_Û¯©j‹gh&“ì› žªãï«Dܜȶµ‹0Å2  ×Ú èÃßXzj¤ÖŠj”ù ]âœ\bžÎ!Ë€šî×ÐpÜ‘,„`ĪØýUd
+¤Ô­€Y¾SÙñ_$çd2œí:£ù(_6¾‰•‘åb YT{÷íùU‚&ùÕdù¤fˆDÁ¾Gœ4Z4ßmûÚL¢£†ô}—Âp9nÿvFªU䯔ÈFœsÑ’ýpÃÇ£›^„„&Ü”7Î8¬¸áªDuÕS(œ`ÝYw"ø x¢ñÐì|É>¼Š%œ4Ã<A¨\ÃL1•öòHIEYæªC¼#–wý$µœî×h Žî‹J¦ßZ›Úû£XRΉ13U™Ul€ú¬>Hž!A%¤„k¤(kÕ¥¼6ìõA)'e„in5†]”—b´²ž¯¼D.âòdƒ‡à_[“njÌVØHO_Ó 2þ—ñ2GŽä¢è æ´eL`_LŸtu…<ÒÔýõ>¨QW=AcØÌF¹ü¥â«ÐÈü’n‘ÄF\‹W\hô5amq³Í¶JÓE„v¤R€ë«fcoÊcò)ò'®T @Â#WÉveÍ=ü»,$3yè;.Z;º‡ÁÍa¹Aæ¡Á/˜Ü¼ê§¡(2qÉù[Vù6ŠÎh;`Ÿ±Dë5ÕVq*‚!°È‹«©H.Že`òà×e˜_x‡ÚßI.;*?-ˆ”€•7¶Þ~s Å€RðVÆ‘Õõ2£óœ
+×–H„ü()Ç8oÜÚè3Áe¬4`>»‹r­*ü*#µuÍKÌQÒAmYJ½óÑÊ—…Å^â
+*>e5¹÷ öðŽÀP¶˜ù¨hËÖjÕTž ;c’“Äå_ña34/têU…
+! Öå©fxÖ#
+n6_zÈËt"
+â’^ñ§w„f1šhƒ÷ˆ×‰Ì*YJblÍÕ”^a2@4ÅÃI¨”Ô½é7¢›­›™ÐpwÎ5;…×ð>ݨ§yŠT^³û¼¤"vPPg„´yÆM;ƒ}¥À4ËוFŽ!‹š4ùKZ}†Òš¾†êHD F_îµI³²àH;„°ByÕ„<+Õ#ˆ›ÑŠ9KÅ3H¶d ‰S1<h×uŽÕ«y–™Ä25j÷•Š±bH˜¼¾†ÁÅúÕn= “èq&0°w¯j¦.LU¾é²†ŠÂŸr:¦ÓF<$±îB³Ñ¨½‡!ÌëxPïâÖ›€%t cS­S ‘ ÷€ð7׉dðRnpMœç€Û’Ô$Y®ÉãIõDLÏ!äÅßþ²ÕE!´´› ¯CP¡AC‘ÖòâØ8¾j³ÅÝq«2k¿ÕÄ\Û˜3螌y![%¦~9É*xËDu
+¹çm¿±?(+TÉœ›{½wób©$çà´žÏçð <2>fqjN›†Y!¯(Á«|B$ꞯT²‡<Èkbó
+'ç9¡ÊªÌ\@³¤àXW¯(V¦ìƒA´±B8ºqAq˲m%7K„ÜE˜Q»4ËÇZ16Èqr[^²2tf† HLã²Ad>†mvKŽ³yòl5m6òˆŒ¢ Æv®U”¦º‘9¡3€ã¦
+woD….# ÝWÍ7¡DÒCÈÿíí¯™©1w…;ÖRž1_¶t¢
+8!™¹KпdB¹¸_„º”‰p²:¾"^DªÉp´"CgQù°Që߇  c¦‡‚13„¾Ð'8<ïMžÿ9øœíõ1ŸÌ¢°…dÜ»¦åKo¦ŒÂ븬ÜË—Þ¬+²”cè:+7!–
+™B3á”VOç°; ‘(:œnCE„Ì çWQPöJôROÕ{`Z~ê*`”¿,ªô!ÂÉC¢§+Ó=¨‰š LÜÇî.O—÷xÓÎ->jót
+?­ë£S·KuëøîœÛÜìns›¿ý«ns¼«Îc¨%P
+‰BH½®NmNçV‹@Al]ª3$€±¸”R
+ëM^í纖„£àœ‹|‹ö$€„Ï âõ3¤S\ÉÉnC't†µ¡úÓ!=\à‡† ñÇ…Y ˆŒÁ › ¯Cþ¨}ðõZÞ̬£¸’µ›"i£‚ž2#20Sî®u›ØQL+–UbA¼x¹UBenP*~>˜K•°Úx„òLšyßÌ—Ë„+I"–~8†±D#c?†îĸ”¸8›áJÒ;úCB´÷ Jœ® .Ìs´
+(õQ°æ9µ TînØ•¯ ü*bÉv ]<Ø3B«]»p>º öp¶Sà²ùè õR(›$ÆPFö*˜¨­[§2'ú“@q+†KâYp{Un4­”’-ƒËà•-/LBžaxèiµV5 »Žnò Ø4C
+î
+>bg¸A1…#²T\µSh8'„4D'sgŽºft.B2
+ñk!°-Œdcãv¥!C:XX®)èp‘ƒ°v×1@Ðlþ2ï(±@({«3
+V
+7ñ&Œ<ž<™ þ¦*2z‰@öTo(°‰~ùbÔÖÊ•"ìOi [»;‡Ös –úI¥Èí¥™¡,Hà'<äWñBT‡²÷æ~AŽg2­ˆŸ“To4²¬c"N<Fkq3ŽþÁ †qB¼ÍçÇ?ÌiÐ1ÓŠøúíõ‚¹nÞõ®ÿǼ2Áy¥^1¿§”oÍJ…l×á¶?¢1èn{f¦ñ\×þ¦Ñ…™‡›ÍŠ•Œe_ëY1¬ä?aˆ7é£Ê»£Mï.ÅÔLŒ‡ÚùÃlúHM²Fë‹ ø/Ú»†HCaÜÒÜi·3:”6í¶¿Þ6ë÷úÅÆ®Jý1ˆëJŽ¸€ˆßñ=€^‚é‚ âRÝÚÆ’²›5J\L7ô†,ùÃP±™8dBw ±Êü³øôýEOh~ؽh³@'Ê!Ã:¤È)~v-Ã'Á]à+Â/¢|ÌDÃ[)É,PÒÖÔ#Ì/ƒÇdÍb2J­ûƒ0íIž´»Ã.̶ô³@¤n±1ضƒBVÀ’05ð«™û}…eŲ<sÎqtŠÅ‹›s‹DúÔ}&. S» ƒÓ°x”ôSÌÛæ}þd=çÿúWÐÄûðtTÚ:Â? TåÈYƒVN  É&_„ŠöÝÝYŸR^„Ob0Г=b]ª¦<•\þ;H¥•¯Š—“$ç¦-\Ç ÜÑ(>ô$WBE½I,+#
+¨ŒxìÂG@5]Mìk<. 6«„4Kžòjýô>‰úd nøË ï gÏø ]ôµ#'Œs·xÕI-`ÊÙ8ëÛ¡õWdÈ Ö‡œx£·ùÁ(ó¿;»½9ȆPQ¶uA()} ~‚€[¤0}ß3 ‘˜Ëu^F­fˆÊ[‡hrÑ›½¾}
+èÑÔïiV†bQÝ,¯â5\"â÷Üõ7PGýà“& mîÞ9ãg}1ÃnGsÅLŶI„›4 –ja÷Pt,^Xé/-æa*ëêT—c ¼æ±=üD³ÓNšV(' Œü®cª5Ì;ã™I^ó,#1XÅk¹þ¿†ÅáìX;`|bàbVwl»óGò-Üqxæ¹PÂy7ï‰@:(Ù¦í¬6Øš«0kðpíQ£›#ÐYØ mý:xTv$âÖ k´‡aÛà㱿ª‚Œ£Å¾I}úÄœ±p[­qã̫ɆIýÄ{ßóçÇ2Ó‘WQ[pÁ¶t6v! ÞÞË‚‰,:–§DßnÚŽ%$*sáó8½Ì;mTþͶ°˜N}R%^Ë‹ó2ÛCŸ‡üÔ›c%Ìz3ÞΤp 8ÖzXÝN “ß HâÄÇ`&ÚA§ôÃu)ëa¢Ûo@¯ß§Œk¦Øœ¾Æ#(ëáº^¼Ù84ü å]sHË\ &öĵ«H
+¾Ä{KÇtµÂe²r)Ý“|Ç
+CXi– ˜¹ ÉA1+‰ôçíl"ó¯úvŸ¦‘ÍPá+ÅŒÍ Ñ‚òk=M£@Æút±OЄp_œD„ÒÇEND¯Œ50ìE ÚIˆ’ÙŸiA âv=Ì›èD@Œ!¬sø[OF˜njB(†$!Çé&¤3UHNÚ*A—œ…Øå)%Gˆ,PFå+ÍÐüèÄ6å—q
+J;>o@ï½|tTqRø”FöÝÕï,PwY$E¸`Ùâ½QSªQ«ºý'¿ŠŒGm~ͬ=…`É@vƒ ŸâðõѦ%ezXÓڟć£ÒÈZû¶2 B£zi³¡Šeœ<ÓÁœ\½×`5ZèÖÏ爚»| ½g¶š©/¹µ/<‚6ü¢©¤Ÿ}Ë.œRö„½Û›*ÇÚhèžù9.@N°ï>áþEª%Þ rM4’„Ng‰7,VDZ0ŒÃò&
+H‰Œ—AŽ\9DOà;Ô&!RE­Ý˾E³*ß;_TÚ®¯ßcÀ6\•LI$ƒA¢½ëǺøKF×éM¤º}üóMtÚ˦tû4^n6j•¦ªûï·òñWD¾mÓKâ’Ç5~ìVl~üx2±^Ì[1³+¤L­2tT+dÚT^Ux˜~\šj£Ë´á×£æKKkæ³1û壦}ð€¾‚ZÖëpÅ×9œÞ¼”ê^WHïC›i™ÞÇ
+ÑbÒ«©ír]Õ_³Hm³—Òæùœþ2›“"6·|rkƒzw›QÙ+¤Sª¦SZÉרðu’ÒAm¾;iyÕ8šOhÈTjQocô? ’¨FÔgX­sñÈVË”9ì9ˆÄûœµkm+È»’|ü\?>WŒ‘ºËt›5r×ÑèK[ù»[å º±"ÕÑVÜ[;ËÔ¢³ßý’}ï ³"Ò}:gX`€Ë
+ùΨ@œÝÏ#'¾@›ÌðuÕ ªü¢öÖ¤2!'ÀW´BÄ
+ôâª]E½ß7ýûpÅÊšCyuJ=*_ÞósŸÃC(£l ½øõry™V—R[ó¾B𮌋µi+d4px¡ó
+pjƒT׬[P<n™9¥¡Ï"®×!ú*ê¿ÒÓœ ©B³ø_o+BÕœ‚ p½G\)ø‚–è)O§ð:£QÖJæe®´_åSf©
+‰Nêp†ÏúNœ”Í,knÖÉÃ}ì©pSY=
+›j¶Â# ÆÑèôÐõ;‚¯¨*iqûÓ9ü•âɦÇÇÔÌ1§”ZŸrª/4úmVJM0nC¹˜Ò¾Î©Ì*4‹§iBÍù©µ L1±À%ÅULÊSdÍð-èKã?‚cê…ëbwžŒjôbdÈd@
+¯Ž9ÉÔ!$£Š¦ªKäÃÉ
+v‘½;Iáoªl¯ÂØ4=L¾àrF]Gàäè1<y÷*7\ÜÏ pñaLÆ|;'Þ§>!ö@쎦…eV¸/À]ô÷Uáp½õšÕiA¤­ŒÀAŽÌ ç…1ªÛâ
+:^
+Âú8M½‚]¶”ßWýëx^òÈóP#ˆðÜÌ šs„™és°ú¹‚.F ¹‘v‚<q@¦TÏóG~¨ WÇi´®ÌÊ‹TKÀ.~uœ?ÜèÐÒ=]¿z•á„AÝ£u%Þùx ]Cñ5†®Sà³kmY½ò
+7å÷ö¶±ÿ?ˆÛð篠ykºA Å U}=ZàûÊOe¼ÕBvÞÁÚ
+%›OÚÞ¶ ¬…E—®RçK~b‡ÙN—²ÓqÔ˜e+Þ
+äu8Iö
+J•@?ñ¶ŽÁ¹±já™gu识…áŸz"þ =  ®º=c1ŠÔܶ!ïÈ/øîM­­
+ì’fèæV(‘èœØch±n$Û³ÆbÌzxOØå!³‹?œ!Kþ¾êkP0#šmÍ꼎Y0—méáW:ƒÝšù,mPgà:)éGF¬¼ùÊ°[ÁÿS3+$ž >jyœ¯6ûæ#G‘˜zY_ÚÉjŠ•ÉÝ”Ö
+Ötÿˆ;‚[0böXcpä±ø1Ž9x±ȃ‘ÍÚ NyfoÝü5"q#ÁdÌ8O6½—p°Ý³Q×æÓëÉú¼œ®yíiTK@ÑÀôø9uQ^º ÚëÓ(ܬ%³y+úò›EýNö㯸°B(~m´LK ËMá—ËËÖþŒ%î’~ºBB­v€d‡‹–×+ zÔû. šF‘p9+ûʶ‹
+ªÄ™ٷhpcõY8®Áwð(EZ
+|Ïž^Á[ˆdöVC¦xc•…ãCÉ`§Ð8eGø{r*Ûã8}® èŽ<Šž‚®¥ÃT|g¤Ý!ŒÐÎ &@¼x¦?ðJÉJtÁ’™îF^)$ Tuä9 ÄÒ}Ìô²«‡Øs‰ÁÉkvâF¼’6òœ1ØùXNj&…EçA….ÈóàÚ”KéO#LâH2zÐÆÞßÂ<ü¸%vàq êô®O
+¾f¸€a܈&؉Ñ#M,m âžÇNÈ‹‹ÀËI:£ãéXŒ5©˜[*G-—GÉÔÒèàÚ (`‡#KÂ&§èý´4¬ÞM_Ö˜b1P!|Y·-9CšÔ
+ìÖ¥³"Y2–°Ù «'èÎ)™E;g@¦Ã’@¹GzÝó
+Ó“âSøß
+5e¨š¬¢!°ã!¼^SáÔ¯T(ç­ ÅG~]™'Û£%SÁßé”/“ÛÉ"81lþ4Ä ê%«€°´0ßœ)Eäûâj3@ ósnÓε2æ턤µÂ\õ…SÖ;ˆ‘ñEzLûìšdQ'Hq„$”©C¡€:'Ži†Ëí‡D<_ûa÷…0’Ûƒ½<'¯ÎÏ'H¶
+Yoìº 9è§7 d »x‘ùr2j—kc€ö4>÷è™ìÕzÆ,vátÌò‚«J Åc9íö“^ Kʼ@ãi¸Ab9ÕUö¾z² Ô>¼–ÓÂÉѵIJ½H^€WÏcûB°’è_ŽóœØDëhA]]Š"Ñ&$ §mZv²>Eï08úììçˆëµÊŧ«jPø>;¥ë[9Yjv²N¹6šã¡¤åá'‚1áé¸h‰ªaâ$¨äë—Π‚%ˆzÞóf3òDJDŒÔ..…]ÆžQrHö{3lœ—“éýfuˆDƒïJ3ê±>tzoã”mhöR¶á3n´ðÞ˜mðWUö‡ì§…úôØ;RHwwb¹Î/ÇÜÀi°V5ÙýÝY„±}îúx¥&QÆÿzs·ßÞ°}Ü5Ê7,"=mdVþˆ«ÛÆ¡9ýáç7ûéæoÿåGÌ #Ýý¨øöç-Ý~ч ƒŽœG—㘓ˆWâ³=‹ˆëèEAIp|äW¶B¯ãÚ]6:#ð•u¤¸RÙ¯TvP¥§ó„Z?k5ÿô§›£(OÞ©ŒÛ¿S-¸bÇ­qNÛá°zdfg-5Ü¥GYWúaH0-žT;:N½c¾-%ÍreÈ2Æv¼
+åÔÖ<³BÐʼnD6oKùx¡ =Ê|‘îý30ø-É.Ôà±ëÜ7·Ï¸f¢}ó5—`´Zü7×W2å>å»×¯€sêß¿ÒûÍÀtuÿï•ôþöÇßÇОÕwÚÚ?ß Z+Îw)~˜ÈpâWT–Øtób
+j|n€"ÔQBŒ|,úÙÄ
+•°%ž})⯠ý¹ýã?o?üo-È«½úýíëv`ë0â˜gÕ ‹P:Ü îŽ[ỿ\€dƒ
+w¨/J‚`qk©,H%íŠíùç ÉÅc®åWÛ„ÀˆXg”Ÿ']§]Q6X}ÃÌ5ü#N·¯s
+C¡ nQ6Ä»ƒneñÁáB³ˆA™ÆõöøÔá?µ—ã”çZ",Ž
+Á®Æ«çŒÜ ¼«[1`œØj­ÞG»;@0S2•gkðþïû+í˜óco…ì#³]ȽSc¬>µ×Z„ï/Ô°T·'kÄá>4. ¨‡{+·ÇÔñ`s÷ –X±­Õ)í á¤Ö6!ðïÎñË·A´ª¡ˆ¸>ÅkCè•Õiˆ­+ëbCš˜X2µ©Õë”gó²>ð;¾¦ôl#”R*ŒŸâõÙâAˆ½kø(Y]œ »Ç¦râ„Tyw~–x± I!¡²0OÔÇ£$¥¨ÍC’
+[ éè` òØɹ¶xRž‘Dô›¿6ˆÍ”è<[5ûlm·²fTÝÁHfk·g·3 ¨ùb/ oÅñ¡°9ϯ,ç…3õmdtùIŒ‡:ž}Ód¸/A*Áý/Уå—Ì<Õ.6 ¶3$³CzòNÑÅ ÄÛ¢|ÅB™:¤7G-L/›b;S¦ŠA3« ‡ ~®¦kMb¢rA3oÕésÄ¿ ØuÛ›
+D½5bžjL–7’¤©D0ÜS±LÏbzSóœg fTs&SóVgKhtçä+ŸÖKí˜óƒo¥ìc³]É¿Sk¬1>5ØZ†ï/Õ§¹{çðè+  *¨¥¤ÿ‹ *÷JâÁ, xŸÄ,
+c½ô„¸pHN¾6B|ÿg¼Ü‘ãÌ‘ |Ýö < €¹!—îœ@±eîý÷K Ðœîÿˆ¡ˆ’G!++³ ;ØÕ)ÖÛxƒéؼ[©®sE
+:‰ c· §Ë}~§‘zòQ¶U°™Ü~Nˆ‘“ƒ(>ª]4˜ã.Á ¨R“ ÛÉ  s9“qf~ò³Ý‚f¥©lÐÒçÝ2ýÑû(ƒ×íÄJ™ËsCk«J¸¾Šô×ߘ²ÿü÷Ç_ÿóØÓšz±7Ù‰é˜aÓÉ“0¥0¿ˆRÂbD  )5ÎÐÀXÄÔw FìQ-˜*j°©œ¯©?¥'¿ÖV̼.§^’càStàƒëZ¦\ùÊ};#µt>à1¿QÃrüñ‚È·gå‘+âê.S^û¬ºŽœ ^ìõŠùô
+K:̨ÌDDÔ!¢_6O| ¾È¤4{l"îm!àR•*ö±­Ò˜‚›Öȇ]ž ,ÓÃÁ`Z£+䨉ÔrK8™n rjÄ'^×ÊÞêÊP<@ЊkDÓ€¼M)ÑCÑ’^®Ñ7¤&YK#í÷î˜'þ„²ç½7 Êñ¸É6oxZ¬~,ÓÀQ>«pFÛ~eß¿³øç#
+
+‰ç†°4óüL&ë—œt×ëP±å©é¦D·ˆdØG¾ /›T«=]
+ôÝæÄ’:ßK&N:% Í ŒŒ“t¡`LIºK°·z1Ùàjdxê k¬,¼éZŽ9&p%úVUm©ÄcÞPð
+aq¬!.¡ó€Æg§(ºÉ£G¯¸·"0~hŠ¶u ´È4Ô㶵Ž~ÇåæºçÉ~-ˆÈ™³c¿”
+È¿˜ˆÞäГ=ýøqV‹o:sªWYRWÝ ¦¢fL 7ëo®oÌtú©Dwïô%Ôe 朧S®ªŠ¨ ÐãÌ×B_Ñ‚芬ælkþÊ:hx0lQ¹12TºK©BV3–}à'È–€á$xÔó:"‘k­#ùip3ä%*s ”Àã*‚Ò(^×¢xV=¾dÙ A”~bX}Ã4†(Š!9gRcôv4È„\lf y¬›$~,÷B l+{á€p®ù0" ‚'é¶ó"íWýû7 •ØŠïARŸxð´SÔ<
+ÔbM¥ ˜=Ƚlç¾€ü1<Én,–^@—Á?AGŸàÚ·AñAØ5Àå›í¾*S gJ}¬
+h ÊØóù¨u‚EÁº÷`âLYØB¹ÃÄ÷‚“ ä}7œuC¢”|!ù”‹ë]"¼O ã£[£õ‡—›F ‡l±X–8QÜ$h]èUmÒ#H‰ªÀPTùmAš, NkÅ.BxÄØ÷ÌžA8š:Ó„“‹nj†_k£ª{ˆƒ+¡hž¡Ç˜LäËÖ*ôfd0˜÷*cÈ„£ÜægÕ(*s€øVLyŸ¸âÝ:4\¡WÍáé4¸ „ŠæííîN±Å`§+Щ2J9"Â;‡ l˜%õt¹ð+òYø[üÚê´{Áë×·óiÌú€®óæN1 „š\]zOµ™C¹‰%Åou©0—¢ÓxáòÍK=A6)^_üºÎ…7×Ó\ØwºÕ…Å×ê\zQ̪tvyê©ŸÖ3™$¢žÑ[?j ÷kôà
+:ÈÏu»ÐçwÎäªÑ¡pâŠÃR9j½½óqR JwG¿€:}Ýîôù3ùѱ-á飒“`ÂZ†m…„¿o@=F&u™_åÄ KÎÎn9ªR mÛZ, ¢Œµ–q𹈣¡«
+ÖbYN@‡Q†QîÞdÀ Û¥¯uJ×iãî±²óÒP‰õãXœ]ÏnÛ'v¦[bFVP1fÞ¤¬`æó¿Ì¯¦È0wâkh“Ôƒ.H‘þ5Yò ˆIé˜IºË!X¢ÜK,_;Õ,ª¾ðe
+!€Uéá6Ö2ä Ù²Î-÷0D xƒ¼Â— ¡¦#m¥"®o…å+§¢R]n³Ö™¾á…£Š³òž[#€x7ËGiudT0Tž×[½‚Œž ²µc‰ƒŒ>‚5ãwÈ-2’£ZÞ§C©„\Æ‚ ’˜4®€wÜ[ÉcYÛ·â'Ȩ Òýȃz‹FjÇP'`AJ´dÓI”l©Þ­_()­Ö}^A‚ƃ-È@4©‹ùVòÁÔ'e+˜)“Á%&ìð†×ÁÓÌÜ· ‘ˆæêÛN©}¨”{yÊ_?½kÇÒÁ ÏPWËxê8Þ ÌJÂLÃó)x\r‚Ý›“ÜñâœÇ!sÓô¦\ýQešÒâ—|+ús¬¡Õ«796Ù 9ú½¸ôP”¢ÒãæAŸ ¾•83xSFÛÝ:p– ÁÀìQCÓÃ
+£¢Þ²Tuá W™Ÿë¤LâAü;ø|&ÓìeúŽ­Øþ˜í÷øàKü#º^3(ñ&hmµÕ ÁŒÎdx&W¿¦´RZF,vÛá® 
+^ oÆúCíÂSrÿÓn!Aë²dÚŽ°BtdÞ¼ ÝCål§ºú›ZY´B:
+Šµ,{£è ¼šËbEžY/õ‡†ÍütàyŽþøÜ·ŠØZ/ãDSÊ*Âi€^¨nsdÉ¿8¸nÅóŠBl]Á·Åéðö¯YD4IÝ1èO-º7ÈßvCÛÇ7FwÅ•¿þGeUž”'#K´]‰YxE.ûšòYÄ”‘L"¢—pJ_˜†g¶x’øU!·Å¹ìè¦2 ˜t˿חp‡º61ÁŠ ,•j±]#¼”%“ˆ$OÛ‚X¥hqŒ§­ /Í)h_ûv?'
+ÌQôPÇ&FXh<l.¿CI&])HÔ椣Ÿ?ü¢"Ų\tôã¡èž6þ¡¢üÐMµÀ!Û6%b«ZG݈½»Dó”(³–ýëé`†ª·I«òNÈ
+-ü¨n+Š ¥ÈXëhùËt.ÿº•¿)d´b‚×–îS!¢S!ûKÆPƒÀî*d%@Ÿq­~ùð»Bv‚ Ü]·M|WHJH8ü‘”W]e£¤G–ñKãA ëxE §þ5Œû1ƒ”Õ3æÞ]TÉЂM9ÐôŠôŠîä=ddiF'M_xÕ¤¼bä'%DT¥ÌŠ¯Hý5ãåïzªÑ×µ{y<Gås¸w™p¼Gø;{ã@ýÞa§ä”Ç[™ßä±á3r‚¾Ì‰xêxÖÜűA÷Š«Dµùÿ&Ž Á(X7äïI¹8Zµâ¤+GÇÅÒy_Óœ¹Ò(„A<Qü7¦̸¨ØÂ.ÿ.vðÜ_ òð¦ñÒG@;l¼Ã»6R2ÓœStµQ%(`OÛÁޥѩ9”ñ¨9„Q÷eÒ¢ø´%à]õìB_Rf÷61ÝuQíㆉ7¦±ÃÔ›.jü?4 r­ž¦ÝyÈâ†ó\+KŠÙ.ÝWEp9"tîÄs¨bš¤«/‹ìˆT»Uúv7]<×èÅÙÆK! ;v·…=q"èç*"¡BGÜ095¿¼šHx™LëÄnïšÛÇnEß¹‘]<¿2ŸýĈ«Ié•eâK–eÎëâG«IŸ'‹ï×­ Ø #Ç^Q¾ÿPTi±BÚ.:>÷¢
+º$…)cWœ¢7û°JèIqÞ@ª´Á¥6+Á¡'c& Ú‡7L¸âµÁhK4ÌÂ[Ù°“×1UHÏ‚ß6{á
+0¬ûR-x(@Kc¢SÙ)±Oñ:ØÛhñé ?Uš÷¢ ßB»=ê6‰Û…ºãºÈšÜÐâ(i3V‘Ó)|½Ç}œ;`D8}«L÷*2…ï_ÔÒJ~%v‘MÎ!ÿˆ/ñ³ÞºÁKa
+(&‹c˜?Ý¢Šm­$ì]Z0ÅfH OùkzËVIÏÒbYŽ7ø{}ŠwW~ƒ»oégø ÏÉqÇ0ØpKÆMŽ a4² XqÀû ëM[D’VžÎi˜O4šoÙ•‰ªô0zÊ\%T€|¡¤æ @S&Âáèã’ž³(3ãÿ!"IB>~x“9AÑ'‘#_òº2áKb_,²ž€é`*ÛÓy+ª½{:/À©dðýsDA
+·±X«¡ÆWëêÙ&ЈÛ2xH1“ÀŠžÏZ¶›`-æÄCòó5yÝ·*Õ®>ÞJ6èár K+Oç\« „!_» ]Qœ_ ‡$C¬e€­¤dñp Ü2mÐYRòkÝ ? DEûà9üFŒß.º‘ïÇCQ!Bbɹe0††Tx‡ødU€dL‹h±·OA¨OâˆôŽˆGJfõz¨”?c\уÎãàÅäÜÙ¶8MmK]»ÌZbèaè%^ÔÈ"®|¯ØkŠVà¹qûÝ=…-enùZ§0¶8mu¶¤%ëÑ|¸Ä"¤§ÖòþXü˜VÌ3ȲT~á˜Õ@ØyÐîÌ`×0Ø3$»Œv…cø¯• ÆŸ•œ´€?קØü(¢Ë«H~&*LpÏ5K.­†3ã¾#Âði6;!²›}+ÿ(bÊŠS6‚¨T fõ;g‰u¸˜š}ûUç9„±LS; ±JðCœàj
+Þ«ŸÂ]ŦéÉ—c¥b
+œ4Ùw.$:Ñ)Ñ«•$N¡¦å»d`Ô<æý¥Ô5ÛïÐ×p~‰~ˆ—­9d~…£~Ãß?Âøç·Ù©×¥Øñ¥“K¾ 8ˆÚmâAõÁœ$Ì Æ²c\¸ºÝ ÆÉM-Z²¶_vVíðÙ€ÆkÉ÷óå D„¥O÷K A¼‚®iËý=¯É p{È'í&GÙœVjÞ2sìCH7ŵ„ˆÎõW (/U)¢šû§µY™b°÷ëC8>ÞƒkåæAp"²NZç È™OíS*¸Á¹Nä?¹ôK›#K˜ïÏü^¢&ò\D_רĖì¸ßõd^†ñÇ1Ø6]lLëAâ¸÷
+Ûóÿ*¼Jj5ÒB+Ø ìAóJìK¸ä¢2´×s<ÁedJ¡HL¬ñ³3ÍÎ"Z n?h¿Rø¦¤OÞ¢9£³>|´ûáXôë•«Å
+ÐÙŒ¢ÄžÝ¬Ké®0—ÚÛŽ±Ï¬K‘#ÞH"íf]J½€.ww³®Jø®•Ÿ½Ìô¨9“®Þ$;Ìux–Ÿt©É ÃüYÊz&]*$™´"—´OùLº”„ŒÉƨ˜—¤›Ç¡šœ±ÕøHº”ðJò›4Š›t½’#ézEŸIW¶©ßd7éê¾UÙÊKÐU Öñä]µ<ã8꬯Øûg']J¹T@ù“¥'骈ЇèГ­ÇùSà!Go©7éªD*¡Å:_’®vbJ†‰Ò_¾„ÿ‘1gÝ!S=’”ÇódÛÝeN+ NÔ)èЊȳ«›t)©?çËJ½$]Š
+·çþ\(¹I—}"vd³½àŸY×+9ÒîYtä]ݦh5ø{ŒnÞ¥¤q|í䤗¼+¬ƒ«^سšÝ¼+òrù·Ø¼œºÈ„)v gi/y×!¥#î5NÚujáH±CLÈôln؃Ã
+ùŽ°+WÌJ%ˆâ†]½®Æ.”;‚>RªX”máJc/iWH‡0rlT;çAGÚ×ânha¬½¤Ýƒ»Ž°+PŒ.LLò™v½’#ì
+¤V¢eÜqî»^ÑsžØ°«F
+(Ê¥y6Æ^®Ô3.—˜Ý°+¶(D'~TV|:®÷ô#ìj8ÏT3”èfÝ‹Ûäu”S>3êÓ˜8IW8fpHÁ+ZŒ8e
+ûö]Ϥ~D-/ÌÅgÒ ?È*±©e·ø‘tƒˆ+PÔô“®Sr&Ý£ÈKºA°¿Ý¶ì‘uµzhL ¥ûI7€­JŒ"Ä´-GÒõŠžä_åw%ÝÜØt(DÔ·°ŽôVÑî½¥x¹µŽ9¼h™Až4Y‹"
+FË/KõIs6¢Äªy춱'G#Øo¹zžÊ»Òeáû6Œ€ÍLk•0øÒC­eó¤Jxmiªé‘UÙÜ…ihLL¡¯cºÜ“¢KڦᡋNɇ=ùi«›d,ÝM·§‡qŠ°SþѲÁQU? Ã^Îñ¶,vuõo”û˜ž5’$sÉŠ {œ`œÝˆHæضc$j‚lÃûÀâ¶Ïyšgž§‰Qè¥ñŠd³¾Šà<Ô6COKuð„gÍ Í¤%5¤ä[<p,D@CŒ5PãÝÌâ­š MëɽÏ"Q$ÑNoµst(n3fT!¦ÅT ¹Y n—Ð'(F¨?‹Î+Ëý*x*ËÚ†jü½³hlöÜ»G;aV2™ñ׉p$—²aŠ(Èu¤LŽ`ÛfHÀÕ°4±9”d—¬”ÛæÁ=“ ÄôìÂÛÒ†ï*ás²S#c´/ëÆOà‹{ñ¿82Ìöã2˜6ýë+=´…-¼#nÀà7è¥n8*ØECøí&“£F¬•1‰ð­­T+à—ÁÔb%Ø
+€˜¦Ut|îQôëOîô÷ºzÄåðf:‡s¨å륾Ÿâj!믫„±Ö¨•
+[MHhèàÅJ å‚ÅŠËÄÔ˘%ÔÝŒknªS’á’;˜ÊàQÃ0ËXçà_ñâعa¦
+KFtIr—³ ?˜¨A§³þÆËG’‰¢'¨;¤ÜB€‹q9bªs‚FkYbßÞ'QHwÞªò—“´å/2œÃVÚð  a„¨vðïˆâ³œ÷®ï`Äå.áÓ.ÒÁ¢2z˶&¢Qé‚Mõ¯p üsë%åçùÛá‘Q‘ÇÑ(Ùú ³ªÑ'æ´í ýäg£ä™V! æ'Qᎈ2û¨XRb·U|b™Á3 l¿¾aÀÜ gùwR%«x„°Ý´/1XKG@(<lG$kŸ ¾ µ†…v™æfv>-10ÈGŸå„q4D’ã~”eY'”pŸÄ!è(”»ë…q¥²ç_Ã"—ç¤Sý
+˜lAxŒj†+­iE®’S²ÞýéÖ˜
+ò#â~ú3‘¼aÄxÀ|Ÿ1˜ü 'F?Š_Då^ãë„KËÝw“*)
+èù…±s@ÌVß'Cñ{tÏëÇ´Ö!vR\X+ÎbòNx¸žÃë¡›? >8p
+:D„4MóÍCbq)t8;c§¢'Cn^¾1dSD]cS§07AÛ3vã¾#{Ó"Ç´Óøç­Uu€£Z󃪆Q)3ß;|6—Å7%‡ ü§‘rÇsí¤Zž…AlQù[K¾S<˜–À0îÚy`©Tªl¸!HHQ“Ïäìƒn˜«÷ç.–yp‡°]$Q!6Í!UßÄ%eGàÄ°àIžL"J½ ¾Åµžÿ%H6H_Ll‹›øü)¿Ò¥"}Ça®púðJ] K€îßàçYÇs÷@¾rÉй¢2awV=WA¤SØÚø£—«Òy<OV˜›¡@D#\¡¦Êvq˜§&D™€”Szøo?×AÐRSñaýèÛ×-ÈKꟾCM_[ÞÉN°=þ³+ ¶à1~A˜-> …®)cææá(l¡:ü±çºÑg–¾çrή·V^ ›„§„õ‹ï03h3¶ÅÝüK¬á‹#æÀJŽá™;˳aïçfÓX€£â«5¸ùKt†‹êxyùáS—§ë¹à7 bk¿þû÷È—¸ÍT1!JÎÛã3ÄýÌy@x&aL´›iQ,–¡QK6n™HDoÖÇMú-¹a9°ìOLÈ<ª?ø5"Åнú ¨æì|›3Båò,¹Ÿß$‰Yp_–Ï•™£àJ’'/<!S%• ö}!NxK@Ï£® ”çP£,µ|œos y§7ÝÂâ©6—Ðyªð%ºž:õâCqëøý;·¹¹Þæ0}÷W¦øZû*$â§% •>#~,ÔŸµK„A„9Åx›¿÷ì°¼ä²|Â|0—dûu<ìú~çF_ëâÀúãŸkÞ1àÀñ¡J¯ëâõjB4hrInø¥L1¨,³LÒâì±
+ÄlAæ("¸@÷
+üÀ|¿Q%Õò¯ÿý
+ÿùç×_ÿþR4%ÇŒ†7B8æŸ@ Ydùz¯ÌºÞAƒ 5¬á Ð¥u½Öe·žPÛ» ¥Ƙ ‡õUÛqï”ȘHÜ=,]g9î jV¢x"š6CÆaÈ!Ķ /+ŠV5‡ ú<c¦9d0<È<ùt9^†~·Ð
+:íßÜ´€\Á´¢@$*ÍlÔ`Î ·j ÁÝdœK­å
+t»”!KFu“S üZ™t;Ù3,!‚º¥çIJL ¾½-að"aÔ‡ˆtIó¹b,¾>•¡Nˆ/KÔ7% ÄØjkO+fk‰¹î¶©Eó! h!9„p…Kg;â‚ÌI¤±ÍعOoO³Šé
+6¦t/2”`ØÖ©w/2ûÍ"òƒrÞ°C;ï›Zðe5ȳäúò;©ÞÈmo|UΉ1¡º#æ¼ù¾gÕ%È["Þó(ôœË2q†;¸É 5ðÕˆaY/…N^ £2:Á€ˆñùëŒ 0pGQ o ð(t|¤NkªŸÕ)DL“ìÄñ΄±uan­7f¶® 00’¸D
+Ä·ú®1a% p.‹:ÿãËÔ¼¾S”mlNŠïzâpžÅíÔÎ+Ä'‡šóPIÊ‹ïhi+¤XhÕoƒÑC™Ñ}He™Û’ü¼ƺžÆî£pÓ)+†.vˆæ‘½öa¿¬L€›É´<³óý¼¬^x´ˆ>Só¨âûyYâ È÷§h²V}i 0¢•k×Ä(0WëLíðë$#*„Àýœ»MÊ!QŒCËýåQõ“õݶ'ü{×”îõüÉ’ˆ@Ñw!/ËÇv^!>9U)±ÊãÕw”¢dwãpQŠ©‰(ÚïC
+ß`0ÐùèVÑ€«(Ï>ŠhÉɽÞ#¶À.=jÒªÕâ·!HRˆ‰Åß\‘ 3|îV¿¡à…GòÍjç Ä[©ð hqµó
+ÂÆ„²æã
+®»\àáí|¦Ka
+"¦\ÿHO…h‹Í=ÿx €CFãqÂù¡k/s³lª…'³Až!‰:d67 ©‚mÌ-rõ‚<þ½kTŽunMi‡aæÏÊJ Eg¬‹&ªçŸ!ÎÈ—&w2Ni]'ìGEšx—ñoøîþC‚pÉpœä
+¦›ü{9o§¡l$³fì*ïÂϵŸIýÞk‰5þ{WïëÜ°¡Ãp/ä“ ÙËëÓ´xœ.H šæYˆb±¤^ðκÖeX1jn –Cæbž­þ=\ŸÒÓ—ÃèVP²‰3‡^´Ïû0Hb¯URPxIr³k@¯Ñ²]R•]ÑmYïÍU2e(@#5îéh¨Ô…» §ˆëi#ÅĸלsÒšŸp[ã–¾a¶›S²vêo
+Šµ ói&¡ãQKÙˆsBØb¼6³•€Ž3mÄâb—J‹Tþ÷/¿è+-¾»54e'Š
+âÃö"á"æR(“9Ìy‰¹ïœ¢;M$ Éà€»bu“桨¯‹g,ÿä–hŸ/ïýMà™8fCŠ
+aÏ0㹸o|)±n~oÕ}™[¿ï‡¹áÆ»Ô ··¹øþÀοöéïOæSãZÅÒƒ™úì,÷áÖ@È)è‚Ú–òR2!±!t®†2ó[SÌš÷Á#× ò½õب¯¤zNN96Ÿ
+EÁxÞ¸tôKd+ÓíÀI0d¼‘-C¬Áö¥Æ]|«áPåݲ\aᬸ NÆÑ>.B>™;â%H'¦E¾¢˲&©,fÄöÅàO¯f}"NxÖ8ö<HZ «ò1…C(1mbnƒÇ¤wœˆf¦œþ9K#Q_¼|ÀpÕᄸ«ü¦±Nx–&±Ý H˜é>Þ³¼HçL|÷½%J´MÆÓôå7¹ã4.ª¿N!C¡v™€<DzK˜)zŽGÉNà\3q+òÌc-C‰R+?.‚ž£Ì+iÒ~D§è[Ð[%èw‘éˆc3xe cˆKÄÙ³ŽcÄì ù^V “)`”h"‰r¥Ö‹˜ôÄ£<d× iBhV­N‰m¥ ¹—§ehSÍtÁFß3ì8ÆV®ô&ó¹8£œã2~vÙ¯¿A"ðà‡*/BÂ`ƒ.ƒÁ3qm)t±’
+cª³ÚÐÃ}BÂH`Sì9 0` ÆÿåߟFT ÂK#«7 hˆi>ø J ƒÑÄó
+§e&Ç©®ÒŽy€b4°%z;ñàÌoœJZimQ=¸Å|´ÇS€šõa#M0td6§ë¯^â/îWRüVâÓÓ¥èÏCÝÇç3rÓ,‚Sƒ_Ñ[ÒFLÅGœºé`žÊŠXÁYŒu!™Àx"ÓSùͪÄ9^àš¤Ö ÔŽ£îp¾‹ù0r’ùÅÆÔa‚í,ëE7™ö)ugŒ¢SbÑXü®i<­s! ¥þÏãƘx¨9æ£G¸pþX,‡17F`AÓ|VD;ÎLÝJnA@–ÎҙЊSôþPtiçíºhT¾ÄÔÚ
+8¹‰õ8}|Ä‹ˆ‡Ýš¬nîÙ·ðW›âFåX­‹ƒ*^^Øâ O‹mÝJ8ûˆcÝwIžpÇI”[È4ÏNÌáT˜éÇ0 A«D€ŒšØÓ&Ä•¹
+ekù­
+³ÃÇ1à–2ñv8"iñk9 ¡‹E*FØ‘/s1gYADŒ§ï@ŽÙ¾
+H‰Œ—Av[;DWð÷à ´8Îf9§GÎþ§}ñH*É•v&väI
+7=€¾Ÿ@ÔÌ
+làäûéº;èó+oºžþñoBÛCºk½>o3ÊR½d†µùúL¹Ò%¥×^ÆU> Ò¡œ…V™h–ä±ÆÅR¦µ¡áU`öàÇäR} Ï(Î <‚kRµ_´­·1„Üê„Ú.Ø„)é’Öd]¥¤¬I㪷çH‘ZÀ­×hòÞˆ²ÈI–ò¥nÑÔVÜš=5²ó/Ú¾€`R šMŠBèY³ú(£9MBÆfÉÞ5#=b+r sƒ µ-)oå¨6žÉ1+TY®Î{…¬ÈÂ¥DôwçÜê
+؃l„=uMùNnR’zŠÕÓHí²ó.<#ÌÛqóþû¢;Ȳ­ùçÌù\V¼\|Æ8èá Za ][Fú´7è´ñìMÇÍ©m¹ëayqGˆ{Ö °ÛUÔiÛâ|1ÚDÍŸ•Äh#=)ZÈY›n ïo@Œ
+f–‹8?ß`Ø:Q³ —žRúŠC«±ö
+ú5÷6ñԽ²£ÑWPw½:xòAÕ'‹Õ¹•`l¿R &Ó¬’Ô2òx´F „JXnö”™ÀÖ£¡È*D\žr5{wÎM0ò1æð[m¾¤ çM—±*¬¸‘QÏ~‡¿¤éOý"9®9±¬Ð K­ã&G_Õ ±U*¼êà«xvV«ÞI✰Áˆs9€>߀îµ|azò'Å.8’Õ»¥ÒC9ewfr{ð”&õ
+[RȯI³lŒ°–ý­±®ªY^FhB½†#Þºøkàwß´.ºÖ/¶Zmù¯´˜c³ÕRKMtvhwrtzÐ3¸µV³ó9(‡„ЖxÍýâÒƒ|¦•Y&O™)F¦ÖUiKÙä¡ûª¨È5ŽÈêÉã^ì
+7¥¯ÜnP#-Í(ÍÆ
+šŒ¦˜O¸aô²ƒØ0KÛ€>ÑâèêØë¨AbÌqMk<!t]ƒë¾'¯Àq#Ö1#çdø‘îD·\3äÒ3;ˆ|m¯ ß'ëçWÆï/{
+å!VäuŸüy‚dç7΢$åÚÜZ0Š)#ÂêaT‚&/ü­Ö Á&0¨#‹»ÊÁôcåÉ¥kŽi@hE§+1DKà„ŽÈÑ]§ BqaØ _ýGûbs;VdØ„4¶–Êt£ȺJ 'ûçò^¯Ç8œ·äoöäzqá) vVÛ C,ÇÑ‚ uù<ܬ´©</ țϻڲÈT.C±3…ü\£šn¤qØ1lEž¦ÁÀ”ŸZFž€ºçw^!+r
+—£Ž´ŸlÜ©š›Î”kÒ¼Öbv2Šs@Èû¾êÆ‹«›HkºÞø8ÒéRÜ[d=#rF6ÄŒV’íPþFâïO_
+yÅØ0­¥|ÊÏ7 ÃŽåj>ªa³`XŸëè嬘~˜ÎÜ:&„öWö]‰Ü5 Žcr?›&.¬¬O(NòAݸ>< ºnG7 ÄòˆAÓ~2œ«òöÛã|dhtJ^ê‚àk$Û“ÖÅ÷à l!*í
+’Û× °G®|(+lØš©%ïÁ\ˆÌs¬§‘åÿ±WLEãRjâ=SÆ8ß­ü亪ãùœ .½T?ŸÓQpDl¤¹]‚‰-öÔ:¼ÄB@Sí4Ɔä[aÒÏ'ßþ9ƒ~WÄÏ#Hw›Pxª1ïª\Ë ¨±çq5¢•‰À bùé ]{ËUL,Tn.ϸkˋо8uƒ¾š»ÏxzøÛÂñšâÃâòZªÓ9·‚Ÿ^s#Î)ª;ÿ^róÂá×hþ{~|¥_~o !)ý²q8îçƒcÂ.,ì¹Ù€° ½¬{ÇéäUF
+k¡äRŠºßÝÔÓ%Ê–¹»¢ÚÅöHŸQÛY ïU?**+)¡çS r´–R8J?¿¥3íÈ3êîïbê ·Êz#èÌVTXò?ÆËä8–[‰¢ÈZÀÀ<¬©¥¼Ð–Ïÿí?È,‘Õ(~*BÁÇf6€œî0 Œˆ]%‹
+€sÜTÐ+xã†]ÅNãxqÕÛÄU‰_…^¾Â/AßÚþù„èŽHI„d4DÁ,DéÆ ™‚œ'[øêi¡4 ռħ4!=õ<)˜¤õ´ ¦ËS‡|í>p[íœEÆ'_ÅÊ},ŽçÀ`9°£±ùcš´/V6û„–…¥ù’E¼¬#)fÊ“žþ“šŽÃxÒ4jo¶/#¦ÒØþîj›µùi¦·óÚI&!C´1ůúq7A*ª„L® ¦Oá7p²ÎÂ!ЛB[IŒ
+Yöà­>Z°C€ÀÈ@bõÂCp
+I°uY¯Î XS݆y>'#MV¶3Täw¤ À¯õ²#`Wö…ìçvµ¯1'A˜Ü TþÜ$0À­=QôhAMQMûø)èÛ›$±fFÜÞxǽ€$7ÀKÍäA4õ ìåaùOÛ­qD°Ý
+ +s.öê äh)ò‹3=žCQ …)w<-rlu”¾á€`5AZÒ1ŒÒhK¿êç)ü¢æ¤–h~’ï;x¯?LjšòJðxÕ(­®1ŒL„k“"W&F}‡°à]õŸÑBX@Ñ1ŸV£¨Z«$RÁþpìÊ„»û…ÈÄ"›‚©±‰ 6ÆfðÕºC$»ynJãbW¡iPÓs˜˜;[K›ÅŨ֡ÕЫ¿&ˆ†x]viDgÙ&¹·ã%Hi&Ρ{ÝüËË]”tJ‹ ,­Èz1`ˆˆ7õIâS%…,Ým¾ªXáaÄj|:æÖ…5
+;éF¿éÇ)þ"± "^?Њ“ÑùóJ Ý8s™ûYd› ¨ÒÊÈ0µ ªÛÞ,½S ~cå.ÛÒ‡bÑÑóªŒRø(ÿ‡œ( ð†Û§U
+±Æ£‡ÓDΡžÎIb¸
++‡óÄ“Õ€*Š€öÃÀçwyMÚŽÏrªá8ú—1Gœ—ø*Eã8~ŠÉLEeÕ€dlFbÔkÊ®¶9ˆBà‡%/ô®‰ñìíŒá´"U^”ÑIó‰ ÔRÀe2¨`me—T ÓÕ7n…OXOó%‚ƒ+óÃÞ¿é6§|€Ø0±¾¦ªS; ‹úCÈòµØ m½_õójm‰”䤣‘~ÃQ2”jÚXx
+êsDXŽŽU¥OÎd 5~ÔÒ&9?v‹ñFI5 ÁZJÈsPÙIAh,¶˜†˜ NwÕ¶ÏAžsTnÔ{‡ÀãüàzH!ý̓«Ùœn¯e׋K°
+„*µ3Óþdz§Rkz÷1¹Kà‚àÙŽÁÆQFšV:"W©=šß°[T†Ðõ›"N†ž%Û‹ƒö‘ëbMû˜€ö‚ƒZwÊf‰™éí-ß“ñ'É&Š“é‘…ª[kd,Ì(<‡ïN[-,ˆÅŽÐû·Âû¡±V‹M:‡ÃaGJ|Á
+0aŒæºê”2óáuÖÛžÌ Ñ°#Ù¢z0NeÜ­Ä *}qÓÊ2'u
+!•½~RPZœ^Üp*sdoºú@Ó´ŽÔ/—˜L›Ü­ªXÀpA‰,{<Oâqo;EÙ)ùk„y…™]|DH£Ÿèàݳ'0….ûM´1>h.y*Ť©‹ÆWKÝ&O¦ËVâN½Ð{PÂx¹ûí¦œç®¯1Ÿ1âAÜÌúÐVœKàqöÙÁ Ž¨°V´/”"ƒƒÖo Ì#šÛUá*õ‚^in¦ä¶PÒs–†\Uè^ “Ú²‰ÇNÞClh
+&\¿Eô·í$:«R;äèQhªK±‹t¡ïò Xµ,h x
+¤4èP¶}¿îwAôŽwáQ¦àó$gá~eSÞv@†oó2 8ú^F"†.Æ‹¤ò4”õ²^Šv¶qÞ4B8ÏQì)i,s/ûI
+âZLÉ—ª%Ž¶dfð|sÚ9èwîa,úð¡`"ØgiðcÈmB?þ:f½¼#íÐ; _õ¸5÷ ãþݯû]Ð Ž«u2|),\_š…±LÀ ìÊœo|9QzŒÛB¾MŽ­ÚÑŸ¨Â"³ ˆŒSeê""gq =²æGåìÎfQ-BcÌj)´bÖØìsëÃÄŒ­êB!°ª³Rv2V R‡»JÌ7¥hŸÏ¡W:°½õé5µ£;Ëò&ÏY ÷<8`>§ê[L&ÿ[…á²]Bï!$,ЧNñ›îA<š¦û9ßs#žSRHÓQ±8ȶڮ¤nÅ‘~ÕºƒmØ9·£ê߀ãEŒÌc«n!vÕ­å§snƒszÍm
+zÀï×݃>ó¦…o+”CÀ¤0¦a5&ŒÝ˜Ð÷µì炨.Õ«û6>„0E½aË8X¬‚$­ÐÖ(—`ü*þÝWAr­¸ÀƒPoºh÷9$Ë@ ÌS;«à€³LRÒ™MzRÓñ­fÙ¾ó9RË(»LôÇrÙ"øWAnËÂìúñV¼]*êÇÇ_ç €á ”jÙ={‰ ï4œë ^Ãî
+YËÚ綟ª{)Jܸ@J˜ZÒ»G¬nØ£+ïÐI%¸Ø˜ŽÅE2Q+S7Qð„aØcÐ QRžM*ÇßB¬›·Vι5üôšÛàœ²zÀ—â¼Lñk…ƒþµQÿþfc´\a-» pÉ=¢¹}ÿsŒ¡°AH‰»¥lo+X@Û¥<µÚ p•w›†¿”¯›Vè€r!AÌW¿^
+DýçTȃ]¶²Õ•}2`,†Á‹ „
+U%whü=Äg ¾H$QR:Ÿ£²äêl_¼Ú÷×D˜ßƒ„cÊ*"QQEƒô{²ê°BÔ@AÙUCæzªì+„žóG`bäXaÞMAÇÿ1^&ÉqÜ@=ï 00'°¦—¾…·äý·~HT°»P–áÔ!ñ§œ[¥Ì*HØæñ­èåé¿ŠHYJ2•F—ÐoF‡È’®„¼]…¡'×þçO¢vß"WdaÉbÜ™„íÉ
+†TJ¹‡Ÿ=1j›=‡»õô*7pÒœR°· ¾#ùYøŽ‘b·ôPñ2 î^‹0úL&ïðAâ{_{ŽË¨%±yÑ[&ÄÐbóœTÐÒÉ&³›gJ7Îe/jØwQ‘ÚMuD1³„sNÒ;ñÖD0’P{ÈI¨¬&«çuk\
+ÄŽ$ÉóG 2>LjÛ휊¶Ç‰xÄx*™ª¤ž(5E ÏëT´
+1ƲJøÞ¸'óÕè^ÂKÞÆÈáÂÎÕ£ŒIë~þu*‚¢¤6ªäÛÉ`…ptþã","fåO"Lðlq(zI“³Cƒü‘”è“?F–\ôàÛ*é3e3v\qSðÇ׈)nYño(de»#s
+RùôŠ­?K|«éh¢rOëd 7Äú¯¤Ãü@@&PôU«ØÖm‡z65ãIí²P¨«³í²©UÔ òô‘×:üÚ…Èœä T")züZ€×¶HQM;*2y"P³[ÑéÕ3ø!ÂEhþ§Eï$üú¦Îõ×ß*Åî5LTyªsáÀ¼åðãû¡g?ŒC¶,•î;ø[í`Þ³¤@¦º·Ž“ƒ>D©×›0•ÊŠ)ó¶'ôVC&µq­BLlÜbg£»¶FÍ A^pñ®Ñi¦aZi¶·z_'}Tù;Þ2Ò6§Á:èk ”¨Ïó¼yUcVQŸ?ÿ:•Ñ ÆÄrÚûu¨¸Ï–ñŠ"9^TYA>ÅUò{sŒÍtž1µ¬@½¯ÌüÅì„Ct' ,ac 7µ²Ö‰²õ‚¥”xÎö·îžf„Û+Öy}êûaÞsºÓ w·ÖÜÀ{ïïß?éß?!Š8§Ø hLÑ<vXîûXƒ£0›+t8L $ôKEðå¾2Â]ŸX˜b]¨êÉm÷ÉÁN"l[Mg·éIˆg“|õ£Ù0Úž4+X;¯”y_œ„i0ŸCø¤qž°Öüt©ô¡Na¿y×$IÄT¹0+¬7~Ó†£çK™ÇŠº”ð%Ù)»ÙĹ}¨îÖî[ÑËÃ=1òEÞeÀJW“1e—;ˆà±Õˆ=»šÑŒ"Ø·µãËy£^ÆoÎ?VA¢¹pA´ÂF$Þèë$="ܺú}ž8hä}.yƒÅqƇ07¿†¤À ÅG8×Æ(=ÁwàñìшâlÝÖN`½ýÜñ}?1 CÏ`U™~1¦*@‰âƒ({„1ÇpÆ܈wŸUÃÎVØ ÊùUÃMñ!$¾h:ú¥ÈþýP4“#žÖcŽëHQ€ÒðúÂ;~¦ˆÈ~æ XHNÈ5Ð0WÆ}h°"8§_¤ ƒµ²÷'s‘fĽŽ2‡iš’H°Epä¯J´E€XŸÔñ´ŽiˆC
+KËÇÓ¨$N¸vy¸EEp
+G£€Æ~mõ^”¸b‚–eoNÓ¹À“@èxáè§álÈõmeÒC1Äæ½ð™3vÛP':¥ëH-] íœä„‰%VŸ¡Ð×ÕL=ÚÓ2z(N/wÙ‡)eâuG:Ž‡ÇÜBIÅ<9 _úNëÈ?¤·‘ö¼ÁTÉ‹rW7-:Ç¥QÞæïÍÛ¦FÊúä\¢i ù
+ú¿·y-™º¨Üлs@?`ëõÑJ@¸ïä÷ÁåÁœ„{–Lf—.k ?
+6®!¢µ©qÉ%8ÈÆÉ¡£å­E0‡ãò#[%Sè8‘}+ZÁÄñWu?ÒŒ—K®œ¹ …W=Üq.$ê= ÜCO³‚FÏÜÃì?ß©²]¥Š rÍÒ/‘‡ç‰çŒûo%6áD! LWk±LRílÀó°´U[]\íüÅLÒ-ýÅDkSL,Ó¶%t͘“ÅNåÜc™*^•SœÃ¿ ô"‹vî˜g›Ê^„ÁÇ—~.2Ù¤ŠË”ó%†q±7=JH@n%ýÁßÄ}ñ!˜²H;HÝ{DcNó« UÍ0i0OL]•LIîA×D¤9IÌ’ ð.X 4¯<—kÏë94y7Ëi¦(é"ÿ.ˆ‡ªhõÑûVG¤E'ʉE`ºßU¥6fK*‹´ˆ£ $ÈÏA€‹š:s4X©‚MÒi-×ܱ,9]À÷%d…˜`U«õRô-ŠÕ†£…t/T¸(ëÙ:»›èy)eÜG)M8(J°ÆâË™£9™±Aø6Ô—&›è‹þ@#½[rð[ w·7óü©$ ÃŠL1µå·ç ùeV·aã06­œÅƒgè<\8Ì ­‹L¸Âzl8¾£.;t_‡wIÿŠºI
+èÞÁkÍ-~èc¸ÖÒ$Ùëÿýt#Š¶zÊé™/Wײku»Í8‰Å÷ó³rfQùN’’&»;2f!s”ãV³àdÒkškëçõ@ïÙóø
+šŽ
+…;y}ü©Òú™¹ïG ŽùѤlÀ¢ŸbJ»íŠb÷Ò±hî òŠ…/8r  a˜‘Ö+Í(‰Æ™”ã' ÏÈv ñc b& “¥GzHüÑs?¦†•‘ð³«Ì+Ž×úÉÝ`þ÷XÅk¾¾©ù)ï|‹"8rØ‚LÏr`à~ «±Tk
+ÞóˆcÀF5°—HÁ•UÚÃÜò–¼æp® ž".4ZÌ“îu9œ]›`˜Î»Aýjà"²t°at•ÝQù¢¹ ¹.öѼ©w̃ÀÊ4Ô€$/á-ñ
+Üë 6Jj*<†©'„n^ðp•Å—€
+¡ÛùÃÎ:¬³å?Õ]³ì½ÖlÓ¼ÁÖ €j0lK<Å.Ž*’貊wÏ…qbsˆs8ô¾˜+Á§I8wl/„1w/ôþÊ€¿bÒpàd.žüy>áÖ8V6©À&\:OOé æþÎüÀ@¥Cý]‘zÒ; ÆAo=Þ3õšß%
+Pd‰›[/ŠX¾‘Ø„¾Ï©Ið…<A¾@µK˜KOM
+ÇÖq-áfbqQù¥¦£8GïÌçåOEô V;Q›1Ђƒ2qñð ™[^Û(‘;SÑr¡§gÛN©¨¯ótûDbIM^>ŽÐ‘Ö˜U2Y® 㱟ý<LJDÛècžXÅóîŸPQd=YÁI×cà]^Ni²yÑTPdÓä,ýM`'Q”[ýS³¯A'rJ1o˜à[ew¼„ÆÒ•Jošµ6à,Í|;ºì!–*ËÖ‘€
+eü§ùš¿q~iò/‚Ážµ¼ÁTÝ’A"ü¤ýì^ÁÂ[ËÕƒ,Q 64M¼&(÷N7J y(ƒ¡`ªú9ÚgºÜ‡ ïüœ4KßÁý7/ T‰R^—’ø”ÒDȧÊýuOY-¯vnÃÂ\ä‡mû)Á_W@ £5 G^0΄Ò~(å©ÄeÚœ‡»›×1V^ˆŽ“;?\ö@
+Ò¼„72s•0O7†ž˜Þ¼•ÄÓ±³¨X ßÃ
+ËŸkÜ¥q¹ñH;iœ…‡€ÿ°ýsÎ!sÀ4Ë£Çr°8LXãáU1Žƒ߇cKÙ«
++A:Šx`Dñ§Ü1
+gÉÌÀD–ýS¿ò×Gþ¢´Î5Á}\xîŸ7E‚ i
+ÉmèñÔüE2T/%1°Q˜ ~?§ïMÆôÕÐ-nS•½¤zö"+ȉÛÁM¨<œõy€Þ€
+žÜ[|ª/I«»w¡¨ãQaSt½üv2ɤ«v>ëóRDǬ³Œò
+O°e °<òýìOzã6ʧ:‚Ž&%›¤Ùå%†¥™ òy#V°ðb,0Zq 
+k“[ÃBA¼¼¯G“:ÄîóÇU½¤F³
+£H;Ñ¡øR4>·©Þá°.wFü‰eSÛýZô-ÚcéR‹ú¥h·¯þ€«sD)HÒÍ^2tL76íÝ`íSŽŽõ§(€FWð+ÊKNt”Ð, ¦Í€ÙΚt1eA—F•åZˆwß?õ\„(T™1´ Ä9ä:ãQd–˜^ˆÀ³d‚½D <dë™'4d%’ñ)œSyÎÁÙN û–f4‡Ïâ‰Ð©|Gà¯üå»÷)~+c«ƒ_±Ø:ȯ~0¿}Mã þB
+.C‘7`F3Š°W§—ðTÚ•±³Ÿ$7TzøkØdJ¼ôÛæû–,ó»/eB—Éͯ|§)†´ °* ¼¡;ƒQºòŽ6MXh"ûâm¤9ÿ£¼L’+Éq z‚ºC@ÆyXk›·è­tÿm?'ÁŸR¢RYf¹¨oÉ
+ƒÐöÄZº%¤?<»Ü5Î=ñ´VÇ §èÃ-ºóuž¼h íÉÉ><…,^Å3žÞPA²ÖiœO'@V­PM'±Å‰¥E!Z²— Ç mK6ü€¦A}ó,ø­;™ÌU¬Š»;Wõ(aÂ)õƘCŠˆ­n”!ë+ü¼vs(^î¤âPÈzV¯h+r¦uˆã”Lؼ-V2ÿÊ>BšeiÁü,ç*™F
+AÝ ñ¸¸¸dÍ
+ÐÃ%’;‚B$‘=ÏLç&À+ÃÁùí_o—÷¾»ãò€Æ{†’¸/–ÝÈ è_ 1Ú—ÓT~b¯ ýÀñ0FsJìÓ‰‘°˜žÓ?'¼Íئ1Í¢%ô(Å21i—h¼yàó‚œ[ðNU̶®
+P¦ˆ@gý›Ó –¾â>Æ
+]’·Ùyˆµªb+Þ¯>”ÜCÒ­ÈÃ{y#KYÿ¦è¾ƒÎu?(ºð‚¿^—¢Å1+ 4´wù¿“¼¾úÃO·ˆX…µI)©I&½É€a9Û…Ç®BÕ€ø·]åU™±’³óÿÛ75ZZŒÐ1ö%‰ò2à”ȳI“æðvq•€É 8ò“]…Yä¼°ø[ß°Óô_ñç5_Ü*! 42^¡?
+MqøÄß:kñ¹G†‚¨E±¿;š–N@ENbk„:Ú _íÅ/|`0Ç>Å",T(:¤Œ<›¦m°5IÒæ4”‘\¼ëY0An@@IæÂ/áÎA:
+ð²ý¬šÂrá\¦üºK²ñ6áè6Ʀ@¼ÅÐ%ƒ"‹jàb+ø—OìäÔwÿТ‰{žNÑÅ‘øÌñ³¢Ž…#Ìtq)Bñx+èÓ¡©AêÂ4•cÂù"K—c²‰ІI¨î³ckH®3ÎñL;†
+ø1Í]‚M²ÑÁdÛ¦¤­ÂĦ.ÿ]2|ç4à §åo«Ã¿ÆA@‹B'-³ìX>±R¥=÷VÔ·!«ZÝo]:Ÿ5N§èÃúÖiù”ÿ¾QÞP$’WösN¯/®jq—pQ Q‚“ÌQ(9☣y¥
+'GiKÇu
+¾ZÅE2ÄZžHšqL"@(¥?ua¡Ó‚n¼t1–]’y Râ<z€Um2ÚÐU>Wôø¢ÉìçùtÚÀË“îs˜·hMGà 7Ø
+*o3¥ªdçé÷wp5â>óSd"ëmi“¥RX4h²Ý†¿à²ð/ÏWTEe
+Vfžjci;ˆϱty·Ï 柡ÅiCr!°<ôÆ25ó'«¾0ãÀä¦ÄÙ=:‰?$e§NJ›}äÍ|ÜH• 3<×Ò5eIëCé8„^ÄO(M:×¹A×zò7^1ÉÁ*“™;aö‡q¢3¥Í¡ïÂ@WœwNû;æ-~ú,4CDvŸº*ÂHX%$s t=æ¡|Ô+ä|Ð߸‚%š¿†TèvHaÚ+z„ºK9ù-›WLÄŽªÈNšèFò—™½u¤bç¨ÆeHܸýòúðváÜf9S¦ûp" 묘ÿT2Gí]ä6ü nT7©€S̪±§3•èÜ©ñé;XRÅ&zc·‘p2ñ½òR2Y1\ „
+¡ä'¥œ}MFskÒ X}f6"˜­9Øi3¶§W_¹šÖåêäÚ“n7î,³p¼þ6þ ›Îq?
+Š'îAêJzQ~>Æpv×þåƒ83i‰Ê|z7ÍYHµäî± $†ŒV®NLêC©Ÿïêð*šÜ’6Nè 2G‘=ê<=c@¤^¨t2m‚NŠm+CÇÇpZMPnμ«ìäÕ»[÷w®žß·¹fÇ{Õ5ƒwuœq¯±ùÖ«ß?¡6Ã*¡YAnÖ“a¾}îaÉËzmf¡ûµüŽÐ…ͳ¨TI Ã’²Õ(6’cçã'û~\çÞ,l¨e®'B“OYð$¼ÓºÁqX¦¶X7$z¿Â¦fŒÑwNô©3§Åüìߦ³áŽ*<èáQãƒ3
+<žæßÔ†j¶Þ4Bc˽ˆÅÈŲS
+&ípäHÄ^F–HÔƒ¿çA¿†^ÏÖY™c¼e20qCHt¬~”¬gÖË©R`·ÚŒ¸@×&³.]v8@‹êAû
+ÚPeŸ  ¡£m¹Ô¶+4²•{­¤JžÊ¸ ‘°  ÐYq“\‡…Ì‚1t|#œ2w/\¡ñcDù†1Ô˜›±Õ¤dй¡AP¥Ü
+¡·6bGå,*iòâÓw¸ÌŠÀñåÅ“4‰¼e˜±!Q4c´²I ^!9™ñ¥“L`⥭*Oû’±™ µT:c>KújZ²xĆ jß#F\¬¤MËÖ“wŒ×ïÌäëòXöSÐû>BQcrü‚¦®AÉOL”ô¼çôÀäøÁ-ILÇ‘ã
+éI«Ÿ€é‰7xAz"rZm-õèÊý¢ªXì|ƒ`[ê ÅHí‰ÇÈëFBgãÃ2xlà ÇèÇ@°üζšˆ½ó8±ANNïQžè39aǃÄ\™£è3™VTv m”Ó£éjèÕa"ñˆÊÍdºŽiIsCÊOßimè•|ò™WÏÇRÕôDåøð—s '½S™IƯ¹/²2}*ÉT®Ò—/×ô /e¥SùÇ Ìcéwבˆ©´XŽ] KͪË"4 œ‰l£e´ …€D5Ù,O(½!:4)Í áfš
+apâ¢aìê-rŒ)[ÌÓ
+Û™|N‰lƒ]í<¼k•A×ëØŸ‘BáG'†3쨬ÖÌH)pÏÜèuô@ú=
+þ†¬,ñÚe¾. BŸËü˜Ã÷$Éú‹š©Ôe>¶ß"D,„‡v&½±—²/Ó×C‘T› èJ<bC
+äIœ¾þý
+Q2jæw¼@4†U„©ÅØöÏâ>´‚–šG¶>‚Ê@ÿ÷a‹}IQμQu¤aMŽ$â05'Ç ô¢HÇùÿ¹Î új±Ÿ?ñá½sM&Z©J7S9Ö.è‡jYýó
+gbe£€h¯ù™ÔÃ ÍÑ@R
+é±b„
+ž-r˦Ý0EÍq…Ý}¸P§ÇãFj'u¤I3ñ v; ]>®7ýË-ò¼åy&Ñ™8ù2Cm…=Ø»”ñ'mÍ(‚ìÏÎ˼nÁ–0n׸U AÒïÜdH«yXèþy¬ëØ
+ÑXÊ=õt I¬Æ™a? Sç“D‰¥‡?Ÿ×1õ[/˜s›F£Ö’@w ÈÉáx=‹”™íài„4tý )iáá#É#’‡Â‘-‘ÎVÛîPfóÝrß‘2«K,%È~JO2l;Ã)lurY±öŒ£}ZG'ë¬=]:)å4n¸'.m­Cb¥½BqÍ­T;g¡Nf4²HјÇ÷êlÆíbaά#[®¬Uã»™þPcfB£èUŠj?¾•raŽKS=®Ãíæ‘rÍ)/H—;Pó¶=B–hL‹YÑÞ]«>‚´$Þ]2f¦É­äITÐ9ñèô‘Äà =wD‹+¥÷}ª8=ƒ÷ˆö56ê¢ß«6@{ܲöâ¨KLKMBí7BréNã¬Û3b‰ÉÀD¬Ò16ú³£UP/ËxŽUŽƒ·&€£ù@¶D–ÒtÝ:jíYÏ«uÓ!¨¶"Yr¼=F<&µ;õ :ö’„A DˆÊðRã{Ç_·ÞúV»2ày¾°Œôñ$[µÕ=iùlëH•bÑvÖé|#êpRjg8“I %…XcÒ°“–­>€˜ š0 ^-!êá4¬bD˜'wsÕ§[} ™-
+éjËÄîטôLÌ5 †Àè!ù)ý-ñ€ù:×Þž|&Ý¢G¶½éÅëéñ‰®Ó!²1½Iý©[ÉDø}Ë·ºëÕ0É_$íÊVË4[ê…– ÙÊÊ?ÜÂÌíÏëD…J÷h™°Á©ìašòì4!§Êšœ/ïrLQ_©Ži*QÍО­=.ÑÄš`‰Äã!Kü>Y
+4 Çå%ÍÚQÛ€`ùG®ÛhQ Ó*ã*ñýTŸ>M×::wÚA”‡­‹~Žêp9ÉmÛÚ¾vžµ³Ž›]Û-°:{»ÍfŸÎ+öí› fK¶Òe×qÎéTP+o ª³uÎÐddæ{¶d¯ZK= ³uz}pìZί­³§Yç²Ì‹ÖjP]a›³™=Áóš!uã#®Œõ{—^fâøÚ<|ÑÇg²àµé=fm@$4ÔZïÑ‹%HÂ=Z»wÒ\- L4#ŒpøØæmæ\1†-ý>!žu¬NpÌKKÁ%9¥L:YÓúÜ¥uîÖ)K§Ž¶Ÿ´ÆBÕ䇵ܭ“{ùàv•Œ²mwëDà‰‹~³í¾amö^S^ç\z¿­Ã+vPrL[X¡ Ò“Ö¦W®?tÓ ÜÞÖ dÓŽ<&lëìÒy<òù95í$°´8‡º·"+ëÅx½Ê@B]%(Xø(·@$̪~VQk3%Òñín¥¡M„¯Þj ¹sy'¸Ïå![]¹{\Ê~L.mµÇº½MA—5 þ8·kÐö"õèkÞª†ívÿJ»iÛ7ò,ë9ųy—d«c«c•=µkÔ·ÌkãÄ­tEºR“\hîhÒŒâ‘66Äç-‹QiÝ–ëGÌTEEù‹l'³Ó<JQ¼xV¾¹Ý‡q1”ßãÆ&Ï;•Y`ÚA'ëûhÖX–®Cf¬po¹g3ÊÛµvz¡–b^HºMõÕÒÌ’?ψ›nÔ.ɯµã+«Ä*\|Çþ¾J®hQ<Qžˆ-7Ú®¼=ßÝÞ•Š<örñÚ³2Láæá""“C}á¿Œ—Kr$7 D¯¢LÿäZ^ö-fkßë—P’ºÙ/vÈh²$òƒ·ì-f-»É*sÖXöRÛq}ü¥—5oèÂæÉ úX`¦¸PÈm»T$w‡£ò1ôÔ°Åær¨
+ˆêXn¨x°¤‚‰•åÛ€Ýbˆ–aèë%£ÁUw‰ò•œ£² Z»Ç„mä‡ý…Eû%xá0lœZò|pQ+mR8ôK%F~e#纭àËýãdÎ[XÁé„ ucj¤Âtô”¯_5á±2¢P{´’ Äᙓ<#y
+âºS“¢¢n%4;×Â_P3ÇG»0
+%ß!$PDŽ$î’G:†„—T`eiD¸o,Ó˜Rœyùèùi#“wí¦ñKŒi=oò¢íçe†³±Ù‰~Ô|»W J¶’•”´s Û)G ï€þW-ÂQòQñ!ì
+„:—ƒÃ’Zm¸ÃÅGÌmÓëôW%±6RÚ±|úUÀ¹u¸$9Ai5 È8yÖ(J'œ|ä_y
+ ÊG3œ1ø,ŸšÊ|5—»‚ Ʋ³·ð
+
+y‰Ë†øT9–o)ÒUP3¶‡`gz›Ìì`ËŠŠ²GvôÁö"3jòOíP³+Äë$"8ÔK
+ÊXÒàˆ²ˆì1§Œî¯âã~‰®¾ÅBëóK$) £Ä¨hDÈÊÿH¬ÏjBP!/†9I©Gl÷TÖuÕ-BXAÛ`mùá³ç7%?"íç›ïA1È€‰¸!ÖÊ.¦ôÂ(øº_ý2Œ÷0з;÷~#;ß³ðOLJš™ÒèTGÎç$:FT‘%ß‚`ÒZ¦]"úkHým­Á_†;jî“OÐƱ3žNÛƒ
+Hèd°³Ú‡êãFú×UaðçìË>„&#šaÇtÙ7x˜ûr)R\;«d+AYÇ ”y ·À—ུýû‰UŠø–§`1[ó]ÇÂá!ÖX¾ÇÚNÀ™Nt&iÚ0Ò¡$(ŒǪ˚ŠVĆ"ˆ¬Ôr=Éo²™%í&ºóŒäU!µ¼Iµèé"è\í“ÏŠ=YŠw'W+Ÿ72Ëœ®ùîŽÀINæ%jñ~Äð…Bòßr¿Âovt캯*ÀˆXnkøXr%›Ã»üZ!$¹y‚”Ù2$ÈkÞÆh°ÂþêŽ'«>ÝÒÖ%¦§¢¿½ˆø 8bξ†œ[ÕõþnŒ¾|G—2t1r†Øý“a÷Ä`pXëfT¢ÒÚ„cÖuœ“C°‹…€¶|òdªOÚwòÝ B©
+H‰Œ—Mrc7 „O;èQ‘ ëÉrn1UY9÷ßæÃ#¨ÄöS*3^Ø*ˆÄO£»ùø]{{úœ½¯&¡ßø]ža}Y+Öx|úùÛŸ„™ó©ÑFŒ ¬ù“Me™v·Ç_Ϧ¡Ë;±=ƒÖ3ÄÜmØ´¹WÈh">½­¥;Ä[‡Î1#vˆšÙðæb³BºúRm}qù¯}Õ²>VwéÍo‚®èÝc(Yžs8Ò¨l´ÈlV{f2±„z|WÕHÌ]— “ë*‚ºD§i"Uº(ùgwÙçt®¦]½Ï!±thÓéêŽ÷.$lk‡¨ô9ZÚ3ÎMÒøc„qð2Îè"áCúã>d­n¦Ö‚Zß\å¢ÎImÊãÇî_&ܧx¿ûV4:Ñ9ÃÞ-æ9e3­ëÖ¾ÂhN̤mRqÕeÑG'ŸXvñ&f¸†™0‘¦{ &JBô½[µy„0 ™rfþ9ã ‘B'ÔÂv¤‰ˆÃo‚6’Aʺç¦A8 晃ȅ0 7Tu™jæÃeí.çpÀ “.()üçš3¨ž>¾ërnhšìš¾EÂE;ýJÉž¤¸¢ñEZvFÑgcýhÜ7AÿgÛ‹ôi­Ï\ÓØIMª ˆRÖ&}²”F«s©o‚®÷©.ÞÇëœ 4ÃWÛûCH8Û’­ªŸn¨[²æjσu‘¬ë£Šµè6?uO~ƒ=i ¶B¤GÎ}€¬aKàY½ÕòÐ2e¿ÍuÝ}TP.Ú¸öe={¬
+ÃÌ
+õh§é<ì™7Û)ì½®R¡Öº\&^ds…4¨<aâ/ öª1[BžùäwØ]uD¥kæìd®J+‚ŸOº þZòóÒ>â‚;\¼Ï¡/‹ C‡ÔÀ!ŒFß'¯þ†Îûª¢Z¦Ûdr½œ›Œkøa³tOÓ¯Ôìâû¼šÝFë|ÜÜDÄÅï €±P«ÇÁ¹b;ˆš6$¢R<HMpŠØôÚ= f%iQW9“cúh] ýl e[ÍdO!W}ÎBOשS(ÜÙò3ÍIvB;ö9ëRLxaÂÂܼb;d\?¤M5ð JºrGÀ†ã 4oñœ{;²Bu| ÔZ†‹9ax;É€f@Ì0Ö ¦à¢SS‹p9•Å¹ha.
+íà›t‘bÓhêBý8ÒYú¡ÿy Rht.õÀÔ ÿç¦é¥§ÅûHôLÖ{9õÚz )Ö’'’(aMRzü¡ú±õ Š!9ò`C½è"™Œ­a‚[ΈY©±ììEYS7l"r•ÎጓýœípÁš]
+¬¤¤Ì:ð'ó» ľÆe¿î)— ¥O–ö>S±±´ºJg=8É¢c§Ë4+×|–TSú¢ ¸5°_µŸ v¶ñôæ‹ûgœP¦ÐÃæzR ÿ@7UÝ=56rðVhëX2"iFqŠ¢âŠ¹1YGiFRKö”Ž†¤šÃO>ß÷á Žó} cC¦¶¦WHB€Ž¿ÚÄåpÁ|íºã—DÕê]´ŸJÏTç CÈ.`®”™4¦9ËX§*kh
+Ë'Eþ´3’þsriæ`(®‚Ø2<{ÏÄr².à0JŽÒ-ZDÉ ¦-¥:^b„:¤ zŽùnin} †[ +væÞN’ ¾xå2–'ÍoD8¥0
+©ƒ\*/û
+Âuï× ÊÃŒB¾8öoòðݱ'•®K1;'›|ÑòÂá‰=át—7¯‚xåæƒÇá¾Yž4<ö1Ü€MdÃÚ1 ”y c†€ú‹ñ;O«y‚ÒEÈ’£÷ï1%‹ÊÉT
+m—)èàOèŽA.|^¬FaA‰ù&<+“Œ#iñdÝ¿:‰Y‰êô­~Þ·¨4Ï
+a:š*ö¸.££8„î²ÙíG3
+íèfò H.kÆS¤QAZY[’=m?´[Ý‘”û=R)Ç ·1g±²oÀ†ŒüÐëyŠ}çß:ÎUÀ<r]ç(¿™¬ƒæ
+Ä#û^¡i¬ñL5ªýJãÒOȧS¾Â§®âÀ”J˃÷@Á)È)—ÐdU¸û´©÷¨øOl%
+ÛF!è@tÙZ$qVÆ$,Æ ýb²òAçýPwö(€~£ÚÈ.ÑhVìP.ÖŸiq,æùMÈ'•øñ&L|…lüÁX®6VoÊ!…©€ÞY—é‚}ÐkóF'6~Pd+eï¤$gß\ü÷ç(â’vŒ¯£6×{!½Bx‰€Š¯Ò©‚µ ˜öU¼×؇ċÍS:ý¾¼îs²*¬ Ë=ÏÃk¨FÔ
+Iÿ !Ž£Ù¾‹ ±h¯«ðQH-B{ÜO ÒeÈ}UÊ3Ù _fµšÏ <̶'Rø; %Ž;*„kòч¾¨Ÿ~+AÉ!°
+ªZ³ÊgCz«#¹:L…ñâƒpK¸¿ )ì°Ç‹Û€Üº úYA”ŠÆ}cÝ JjI¢kç€Í] (E«¬â‹Ÿ/N8O:P!ž§ß„ü:|‰ê¤ïµõîãiÆA~ëî³…‰ÀÖñÄ|óJHVÀÒ!ŒÐþëõÃ|Óeýµø‰…yzjäva&ýMy™$Wrä@ô*uZÌÃZ[Þ¢¶äý·züªŸi¢º­ej̘
+
+fV NRTÿ«©Aéq_”jüŸ›±]£òòÄ\Ʋ*• S#gºO#¨Ý ¦ñPÑõÍ‚¬ÓKØN Ñi¹5TGÞŸabáz0fºöño$ºÎ_?x¬?\”Hª
+§T>ñ…:ûW¤œ>í ¤Y酻ߟÌcÕör¯SÉÊdŒø¿ŸSçDB@ëâÌL`J Ñ^³~ÙÏÄ(“bX¥5v %ΚKÉî5_Vƒ%G™Ë´!yɪýøæ ô,‚côS|(ѹhÐùO[¼)Õðœ} «¯´\NIÍf&ÁÌSá]!\ɲŽ9l¬ø&­;AðO2ÁDš¬ˆwJeJ •R¢®È-í‘Q‘
+Aû&°jWÌõ¡/ò¿”vI”ÊòL.‘o
+´3ÈF©>³y&ÙCÉeK]Q6"µE%$™rFÙÿz­¿P–78Ã1µ'”åM¢(­ó̲l£½22Ë‘e3ȉIO¿–¥HFΨ²‡3ËRÂcÑêbªS
+\Y˜¿àÎ$«ùeÀÉd%ö'’ [ I =õ3ɆÕ7â%ï(K‡a³“ðjy@Ù°Fj’Ú<£lÀVÀb•Î(K§#n½€“Ó¿rCÙˆ‘C¨(ä9àŠ²Q-ÉóŒ²W8¢,AG '±8ÕÜP–¹Šœ¤’g”e-IFjãe9Ԕμ¾Òe¹ÂOOSÍtfY~ágÁw9êÜa³lFAvnš™qøÖ¾wQ Hƒ $—¢£¨K!¬é²Pñ]ä–kØEd7¤“váZòSQåë˜7Àm?-W )<*w9Ž·åe„$*ïh9‚–ø–Q¢p+B`QQ5Z.J‚6‘#XõùãîW€ðÖB5Í{‘Ô–Ó§ói±±r­‚ãCÑ×O^îs?0-'ïŒÀ8å½ï]”ÀZ‚ë“D{Iuh=Í5%4ßäbî#‰9ìÖ¬„Aïš·\ô[¬'ﱦyɺIÈÉfŽáXpB´•`tm‘Ëž¤êTQ—°2›ôµÒµ3ˆ
+ä}¥ëf:›XNô÷p¦ŽýB<rí¾ÔínÞžâ|ü$Þ$¨Žé×ù¥ÞJl©€ýó?6éE¿×%þ»„¡/IáÒ§•m€ ¼C_†0ÏÌ+Ï7 ?›úŒ] HQ±ëóë‹ÜI ñþÉ…)Ž| IŽò\Þ¹„U˜7E‡òZéZ£mx*³â+]7óæçC½9†ßßõr®z¸â‹­üçPmqM +=ˆ&Ò2<f¬Y }üÇñcô`yI¹ÔÐsrK ÂcÒ‡H. '6ýQ–0V”\@ÿñÊy‡"®:Å™Ú0Ód-ä‘ÝôXDH™V%–Æ¥Q*â‡øšèôÂÕn±‰¬SI$ñ—³ÝÍwUA®)Æ{Åï½æ×ÃW2Pš§lß×Í
+‚°Ä]RDÜYœëm˜q²^JëU÷wˆ|PUõä…_¾’¬R9N©NUÄ&ˆèkJá&È É¶Ÿ JúAíý+ƒ糸Ì>9DÝ—15‡Ž·šU¸pT¤Úïo$i\gÓ®€PqøµÃÛB•ëŒMJmE|”aã†Û2
+Ò%#mÙ‘OpÑmk²NbÂ"ζïº*Ü vM°"5=€"¡°~®Ànä>׃MÔ2ï¢_J'‹b]:„¸UÁ„³6Ô¶ßb®Ü907°i7YUÀåHÀ¶3dï€/õÂðꥡµWGõª€™N-Vb7uó´Ð =#ÚêB37-À©‡6µ,\@ìxóæûeúˆ³ÕY53“]êœúAwƒêU´¾KïÉ4’òk)äÎq±úK,Ç„¾'NMK
+
+V")¡ÇlÏÄHíÄCÉïÝ6U"È÷[RQ.fŸã[Ot‘
+lÑ>E¢4¶ðw•ù6×ç¡U¹¾!µß+udjceZÙ.‡´É¸ùýê~Í'¦4ì©
+ß…AøËì%ô§ ì˜5m¬ìm48—¾9}y²St÷Îç 4i—geŸ Ve|ØPiçýpt4Ë‘Æú.Á©¤ ÿR^.Érå6ÝŠV  ~ÇíaïBSiÿSŸ$Ûªz,¹áA»„ÇK‰üXƒ
+£É<KN¡¡]UPv¬ˆ†¥Ð™¹÷¨ÀÎhcN ìJ G×q)ñO©?Â;Zñé`\,á×K|Š¸1 Eì¯&­iô"{ ¦úúPH T”dÿÎvÞâ Èô¸H´–ìÛô’ú* ¯ÅîµJÏó„ ê³QŠ®ù–OÇÄ•5‹_ N«Žf-ßÂF¼ª/ñ*–Þ'±òœÃ£h…c§jaE¥)h ‘ Ye}=°£weÜ/?:6éZÒ;‹ð–`j'[)#’EöÉ·­«)†žÔJ̤!~\¦{ÑN•d=Îò/ÉÕ½½F¶Ð’H²Å¬ï ks­OrÝ•«ºþ|„Oa7Ð"^æWÆ
+¶p®w£1p͸ ·wb¶.róVpG­5“9lI@©­ˆrù»,bR6™þ)Q¶¥À:›
+S ¶Î¨Ÿ³^¢£ÃpaàåJksP«Gô•PѼH'o‡@³?ŸÙ0-dU4s/y‘ˆ¿>ÜGèE[ }ŠcÁê°@ô¨Qp•:ýcÄT:q/8ð©0#¥~•¦!û§qþýœŒŸ¥ˆÅX󉲤0$¿uJºäÞ 7Oܾ‚¤@´Áªa(B¯Q`Oå=§`aÐ ™™`1J\ ÎÕvJ´Y'ÓXét$•ýçS†%åQx´Hiˆ¬ZEhñ 7,|#ŒX-Vf¢O3_š‡úpö<«—²mFû^‘J—Y¼õlÍ…Tæñ€ºöÞ0ni:ìµÁ—|)qˤ×ëõ{Ñß^Ä6²Onªn®Ý6­à/`ñðÛ
+{µÀÜYZŽI7“ü#¨Ð6v`~<E®<§•ï¾ž"5eÒ Ñ?åUl•——9Q
+(eí躛M÷ï Ê\|öIcdw)j Là¼JXkÂY‘ñ-ÿ¢…2КÇÁ›¼)¥û}&’ñÎÝ.ç¢$}ø§¶(GîÕ½ƒPÏ@¥fh:,²BìÜ<%2H­\³£u OÄB0ǧØjhÃÓãéP*ô‰L?‡F´ª¸©ˆí Œ´ß¦÷!k€y*¡¢ú{Ø„ŸbEûàÙ«èh€Åöí@äjá§ûèJ^0úãS¥ŠLéæ{~±)%)vˆùѧ>¾Ý'ÿfÂ(‘ë|JŒ•”I4Åç[”)Œ@[$É}¹äÄ’ €ý^é<h6xçëc~,Ì•¼4£åÜ*ÓZVÔÀ´ ¥?ës¯‘2$ÁÓ÷'+Ã抢Ï÷‹ëÐ6v¨ƒ–Ð!ùfùÎLD£$ŸâF⋬Œh–³±O²"I­Ðf”â*+”Lµƒ€5³?kqê"yŠÈa&Œ £ª,G<
+¹À1±Õ¥2óô0$dÃ=Vôœ£øÂËtíÌÑåÍ3¿ß½KXeú÷xšÄMW4âS…Å`—ñ5q ¶kŽÉâ2`Ùc ½b/ªÉŠ(]CÁ¤Ä‚‘WÒˆŸ'a’Ô´‡ÙePøBàˆÖ4öÛ<]x›ÐØI¤·´[è¸ÎÁM+ˆ"/ŒXî½Ñ2´R±·:"h->£<„톔{o©úÿy9ÿúG–ï%‰C B~7¿NÍæ#O\]B–m†çè×!\ü;ôDL›õ”°f:@-îÕ€ì±Õ,ÜYcô´Â ²HgùÄÖ è%Í7«ò«ý?Eš{V”Ÿ§„ñ\kÏÁˆK nç¬D@íåª~kr' =ا‚’Z8PoŸ‚Ïé{“yÑ \t·†&UÐßÊ|ûßÃúžå‰€KÅý=3
+ÀåÅb1ÝËó T@™hã7Ckô”.Sr {=´¯øH'–°á"⺿Wø‡4¹ÛRÂ9ï~2L8Ñ9,3^\—Ed²CÃc6.<ñªF†Ã
+M0¤9®’,ã1Äœ.aÈô–Àp\圡Éɳ€öã×iýî’÷E±¡ïwwsC) ?­©Ä<üŽÝ‡îåÁá¸Gú¶8°3ëáí[Ó„PÝ|(²­ ¦Ô‹¨ð XÌGZò#´¼K˜C”çwþ%`Là†½`UgŠ‘¤ŽVAJí<æfÝiÚæ8¿Ž‡| ë?}‘Œå£|„@Yb½BÞ¡á„u4ôü²6M„<ž®ñZ®17,_ȇ5þù—]ß”´&¸6@7Rƒ8#ÉùåÅÔÀ®pY¹¬ü‡/¶x
+„Ú.ÁÈ2¿¢t&=ñ[v›µ!mßÐi3¤ÕV³£¢a’+ªuyaCÈu†s$x^„ŒÙ­¢ÌmB•˜q£„±ø¥pJVEr´LĪP^ÂyBHËNP‚ã …CîdMu¢2˜¹R…*s<)u jî°YC–]Ò*?î|ÒSÿóåc­ чE0hq߷˯*y$×AžáɬP‘$æúay$&Ú
+~}
+% ØëÆÜkµMÊ{îOɄ›³™ÄÝc« œb9ϸôÿl©á»ÒX–öl)‘7$·9FÑ5ÒM—Uî«Àb†4²ïîquyÁœþ!„(±ü4Ð[ƒíà?д¦@(ÃáÈ;Oï^)DHí%Aô
+Oº§Aˆ% 4Õ Ð –…8ýì"¶W<œÓ>‰‹ÐÜ?¯YXíoÖ·ûˆ7YN@°KÐ)BÒ B:^„@aq€¨Ø8»Ön
+è±î7ÿÄ0é={‘[no>,Z{ àqMâ~¸6\Xu«Æ§mQYÎUÂ<qN ”ºU'Váº\Ÿæ)ïoËp±,º Óî’¬°@†™—A/¬Yð@¹–õ@rÓÅÛƒ7f1eñ¥"a ™Ä&zο‚¯¾Ù%þ¤-Á—$åœãÈú2—5Î]$‰ o¢):rì]Ä£iŠ$þ«­¡ä·a¥6oéT+YD±ðñ´4"u¢rëš9 NëÀù»qhŽç¬ý©Š,°²h÷–ü
+#îÐ`I¤ˆ™4¶çò¦Õ ¡£\&…°
+þ Š°Ó¥éxÈ«ºl%½â¦ø²% JX˜K«D¼Ì Xéy(ÙŸ"q.àY¬~:-ˬ<K3Þn àZLFT—+CJ.ÞÑؽ9vûàtv¡nç¿Â¬Ÿèlz àÁz†:“òOÝ‹èCP¾~cÁ×€AY%ZÛ€Û›ÚªÓ£úq^E”ªWÿ>›cù¼#Ê\ýëÔânŽË@{ð§QÝJp˜,t»‹:Æ¥±®­ìsøTD¾»„u¡ ô¦8D¥áèGõÛðf\pf/pË¿êÄA¼\Ëþt˳È0ø*ÁEibÝ^3ÒÅ´FÕ×?lÕµ|r0¡@3¹‘P:~¿»–'!†MçSѯ}R7Ù2ýHi6 ˜sꊱ>k~þåB¿Ö½‹²-X²!CšÕ !£O!M%øÈ©—\Ç)¤©À_‘"<7ÝCPædþÊï%žCšŠš‚ÿƒ“SHS‰Â^aÚb–{HS ¾!Ê·ÌvNiVƒ¢3#N7h¦4Ud¦Å‹ª¾|HiV‚.Bå8”~NiV4M¬ tÇ”f—‰ [â~ò3¤Yc°ø,`öÝ3šu¸Ëo¢Ç«Œ¦I±X°Ãí;e4•dÖŠñ&ôÒT„‡š °;…4• ÜdQ/Æ8Å4û<´aÎ1ME
+¼]Ç‚ÂNÇTd%Xz
+Šó9¥éÆ&YPÓ짢/ÂSaW §”f%lN©ÈçûÐ?gÅcÆ=¥­å4 ºçSJ³åJ$ÂìÙé#^Ù<ÙT^ÙãÓlƒÕʽÓ'6VxŠi*(ìL`æ9¦ ¥3ͬIÄ6ßÖáä{N³7ãm¦‡Ï{N³"ø¨D5O9ÍJ .£×Ÿrš½
+‘˜ìÐ<ç4›²ÚVîóœÓÖJð.Eµ|Ši¶€˜ÖCÈýÓžKsˆiú®“½áÒÅ3ÄGL³Ë@´è~÷”ñ™Ò ¤äÎtÚ9¥'ihÈõ”Ò–<TNfÆ-ŸRš¢gy#žSÚS)͈T8*’'¿ÍGJS /Œtüù«n)MEöJå˜ì!áÏ”¦
+\k’ÏE!Í*¬ÝGÒ!ÍÄŒ wøkíÔ#£YIÖ‘¢®q
+iKáÖB{ü¶Ÿ!ÍzGA2剎!M5 “GHM?Bš­Ka¼|:›jÞã•Mr H“dôÒŒJŠLÏx<È ³x<aàÒž¼vi†N<jNì`ö1}„4Û\¢’N!í¯NjûD,håRi¢gH‹æÇ‹þ·`yi” ¤ZÞâLÕ)(ÃŽ™Î©(ZÆ óXµvÎV‘_øa
+•§·?…3P¦DÓ^Ï™ÑReÒ¿Ü‚šRˆýåQ ª³TZºZó™Ñ()" !{Å-¢©BŠ
+¨K{‹h‡¢{D£$"—|ˆ•8G´Ç›NíÑ›gD{tøÑ“:E4&>ð8DÂâ9¢Q±)¿Î¹E´(º™¾î~›gDбָàÔ½;·ˆF Í‹’CÏC÷ˆö·ºV/cOÍ;Ž·„Æo}–¬òÐ8k‚:„öÐ…LÛ@VB~ h¹ÎÊgÿEuz¥”ñ°“mº¸¥Ù­)BË°
+ÛDSMT@ýÍDS!71¥Ë¯ËbñXà°JP;-L´ß9¢ÎŒ5J14°1i>^²‚ÂÜEVB,L£ðÑFÂc@O‹™(Áþa°ØÈèA%ôNôƒ[òOI€JFJ¶éŠ©òb–«^%èuÂ`
+·Ûœˆ`Õ¶õ¼Kè'O~šˆYç†$)ÿæÔ±¹¹¸M:hÍÀš}KºŒSÅŒùò%(ÓÁA8ISrjQŒ¦÷|1:¡³éNy+šjrNÌqÓ*OÝŸW RÀœhÉ<äΟ—"Ò*„DRéFCN–=–q-ž VE²)oì|^ù„AV@Ùº9Æ9v™å/;Ì`+(×¾Jè)È
+A‚Cì¢A…mÄ,Š~þeÝ7+´on&ÓÅqÅ…”!)7+ÔošÍLXÎýZç(TÀ•ÊDï5Èwá?lCØq÷²vL.&jøàNEìwÿŸò2G®ë¢è
+´ÆX˜‡Ð%‡L½•32ôþ}.ºñlþ'Ñ
+Tš@÷í;
+þ‚ûÄmÈ
+rOO ­X÷Ið#‹Þ/Šþoj] £ZXU
+vÜH
+Q*Ö»”«¢uAeÆyFÏ¡ä!垎zŠ¹•Y¥hìÛÉ;YQÀSÉ ÎÑî‹ôH $PÝÇ=ä\ÕÐih–Oaïý¦è!çR‚ǤµøðØŽ9÷éΧœ
+1ÂST’.Yþœs…e,ç³Qã˜sµH7 ÜäÜ®üÆø&:/³š€¾xðz'&PP°18˱ÍÚs_j‚!ýoýy•ß¿°îÿ±« À"L—`¬˜–(.߶˜!s/=j_rùXôfEÈŒ”êì~/(I$ tšù²_÷v™¾‡oÇàl ”áþGÔåó̱äCѺ™,‹ÜdoÛíKdºÛ’ ‚4}Fß’Y ¼ ‚BâxQ«p?ò4ŒûOEµBÕ{Â5ÝŸ†f«Ú)7aô—MR?û“–þþ×·ßþ¶¡ó_˜–¡ïj<úôPH,ZD ýjí*¢„Fœ~²¢Â÷³ÎâÀ—UÒE7R¥O”0ÖÉX°ÃÍ+
+º^ªO»䎟1†£„yyÿÄo{NDC^Q¬5%{ÞII`¢•û;Ì`rE,ņ æl,ÌôÛDÀLȘrj S‰/cñó~tÂê
+0l؇Åe"('?—r·Qþj»/ŽU)sA|ÉXcŸ)sÎËpZ›@Ŭâ‚à#±™%(3ØÕeÊ@mx‹ ÊFNàÞŇK!¾R MÖzÙéÙdøŠ÷‚eÀ.DXÂïÀâ%2ì þšþ;6:;AÜêKcV ‡ËLÒƒ ì ËßBîŽ +æïcŽãúQB ÀnFy×—µ5¼š7bØ«ßf­ßÄñ96»8°#y9ÔFÇ&ö™EL…5¨uïjA׉»ÅJˆ +‡5ʾ~4Œ%˵}P 2€
+²Dw±áü±gû ± d‡ÓÆ!›Æ_D:ÉO¨¸afu '–ø«¬´i'°êi¸(%OÜಋ€d² -…2üU„f-¿\jĤß!áiÓœF‚òó=Œkš 8h „=^DÓ»¼”MSÏbûU±@Œx²`Å=J‚H0g&at‚P¿“'6˜RLŠž™
+¹ã„òŒ^‚l%-ä¥D)Òoš £¶}¤³r\Hþp¤Œ˜@ò ®®Y¦ˆÎ•¤VxrRè >4Mz‚f“µöA)pÊNûWô,Sž›/xT¼ •±ºº&"U-pq? ÈAèë<@âÇøfùRì6çó BBtNβJhõ`ôÃj
+CèðkLíÀ'ïV„ežÚ¿|’)2 eñRD³²z&ôÈ‚’„µàYM7¿£“¤`Cx`žù†N¯â†ÈFªýL'‰°¤`L¢ÞÑI¢eœÃ:æy¦“$¿ËôÍ‘›^ågðŽ¼É?Rîò“HȆ¨¹·ˆâo—sI’«€¶ZéwG‰ãaù
+¡U#rl ®ôXß8>ég°)fcv-É>ͲWøT àÚ õãÙ?£^E2p±²rÖÈe´üÛ4ô<ÑF¡ ºèiwÕE#¨r3Óáª(m&ëO—>9•H02ŠežX.Øâ•_FvœØÄ‹U”Qq™}d@q•ša}Ž4C ,*mþfE⶛8w;Œ@ºÃª¦žÝxžŠø¶w,
+-‡U~ÿº_a•&¿ÂÖYÄ4÷²~jÓ‡aÌñy,Þv†Q?YhT®2kb8äÁo^àÖ èt+‡WIÏîSN"õ$ÓknŒ"¤³ÊÖÁn¬‰x@[ñÖ –µ{øê*Áü_E]E öà´lf£/6ÃA¦Uò®0 ëÊ úµ ª#ÛŽµº)a›Q[%œjzú|”:„Ò``yÿŽˆ¥fÏçú¿×÷ˈjh
+$âM1ö쿵ÄùTÃÑkP˜‹Âç.yðß;¾E‚ÛIRa‡&æWÑgÁ¤„'bxšöËoà ”(Á:
+†#*výñ¢8FÞýU}E†T-ôîpXSÕ¾“ˆˆUÈhu¢HXet£Ý”<ɘŠÂÊÍ6æùíxŸ§ôæܪ Å©Æ‘ARí‹ÝíC4u¼‚¸¥¤ãسø‡òrI²ë6‚è
+¼5èÀ…¡ƒSM½†gÍ¡÷ï“@á‰ý. Ò¢D‘Õ¿¬ÌSÔ³¥Ð¶JŸ%£"Š÷a˜®ŒGQ55;kÕEȲ̄ ‚pŒ¤ é^¶˜‡ïfA0‡S¤!sL¢”àˆ( b…Є©Ór˵ȟ‚·ž„QJ/¹ùf â,x¼/í äÆç¿Èøç× :« ΃¡iŒ_ë;9ãñ•,‰û;]nh¬v—»¸ÉÄÐû+?A¸¯Ä$Gf@Å…ì¯<jÚvªX`6[Øò˜„ž!a ¬Ò¢÷²ÈëVðK¬é»iš-¥’øJÂ`ˆjûÈË#>”'3v¿@„D 1]v·I,Ç#9§m9Ìu¶yaÇ´#¬—„„ûä­F·T·#gòŠ&•Y¹¥°9W%tÞÿñðm¹`Núa[¢A‘-âÉ+E(
+mlk“ÔW:ÎY’£…íÜ"ǪXfÌi‡
+䉕7k‡’yöLGˆù‡Zçö4ÌeÑJÙi…–Æo[‚ì|3‰C°×”ÖKÈ?²4#ÇzöD>îSk£¡?–vÃg}¨ÊªAM³#­]#fì+:a‰¦*­BÚº7w\Ð"Êk¶”˜áòÑxÝwâ$D„Ãëü°AwcÏ;ïÃséqañ8|Gš½ÒämГ2äÊxóŒôìhüf•ð‹Ä§Á^s™ 8á.}©_Éð'¢ ¥IŽ˜}ÜÙô ÚÀ¢ °t}ÉG¢Mƒ)&’F¬‰–ìC¬é•:¢U'KC¾9n°Àg¼™xLö=ˆ–"ŸÏÄõ™h)±¦6WŸ‰öPò$ÚÇRO¢ \}T û¾í¯žë'¢åÑV’E¿­î›ˆÂ°[ò ÑRQi~qËg¢Mjãî™.àB´Ñω0áüýH´*Aì´DsÙ>ˆv~%4lXuG¢¥$qp¾ ?­JèíÄ)âØoú•h))Ò÷ììçúJ´”°³A[´·¿¼-EÖ1K‹õL´<”ðCëÙ`xv¡ÑûÕ`Â|K´ÛK¿mÀƒRâÛ {;mä€^ƒ½ú•h㇜„Þ èðL´‘6aªà§8ú™h%O¢¥yâ%£ö+Ñ>ös Z½Wk(™OõS‘K£vˆ?¤Hû|÷ÒKÞ‘öTô†´*1û.G¢Uk€¡¦¼Åw¤•å…°¥œï„´2p”Á)ð¨]ò†´èÊÑë¸"mX­•:‡º mø §‰TùÁiÃvD‰<oH>@¾ ÖqDZù)N‘Š"þŒ´”8!m$NÕÚóÞðii-Ìšûe;é;Ò²_™W6Úi9ìÌuÑûvCZÜ€ñ ˆË±¦3Òò ƒë£³ÍÆi• itöoåEýXEL‰A…c8ÛŽIZÈS` ˆðYˆdi»‘m¢‹©åŽõýbÚ~Ÿ©eY5G%ûË-¿2%³Öµîï`MÙq´ù”0QI?²ôU2é€çb{)>cËn¶Î•ƒé<µÙúN/Ü1)†éY^%„W#Ua7B“îcNubSèô²¼€»à“QŽÊýó\’„2ð[¼-õ0TÝ7ÈÄ žl‰½S8¯EÓò„"–z+b†ZAð}0 7°kú§µô*ᨗ«Œ½@ËÜ$}ûyªá•¡< mé÷-2Äð6›°ßÐè¹å74ú¾ÎÕ¡àŒ/ߊf .ŒŽ±¶Ÿ´Tñƒö\V‰^¦ÐCÑÒÎ6„‚àëš%KñƒSj‡ƒÏË© ‘îr¹^s¥ÝéÆÔÒýÁÑ–„ qj¾b?Êä"TÌ»H&¦7²ÅroEŸ¿Óð._`º‚Ù> Õ°6}i#µ*Ú³ÓûœFQƒèˆ é—"Í©¤z7‹·KÈ0ÖM>­­ŸŠÒq’8h¾}Ž³³(~(8`Êañ6ÏR$î$nÉœëÃÀ/ô­–³vU)2™!“!^/ÌéR®6¿qÙ©€¢|.êd÷Ë„{õƒ¨ñ´Üçï<°tðÇ¿™6þõŸüñ_~b;™‡ŽÓ}H#‹zH@˜¡ß$¯/UÙ”óE̓?㥱ˆÒÃMÍ¿ÜÓTó˜ êµL¤ó©1ÑÂ$mmÿP¤Ù/çB‹àe€ tQgÉ6vÔª‰2“â^B—òN„€DÁý4g-«¢¦Ì¡ª%ŒÚ‹ˆ#š3–éxT4¡j*B;$%+š€ÆÞ7ÎbØX¾Ó¦U©ˆ}(ØxcÍ:O½î‘–æý¼jhZôÊ/òÚךªŠ"j•ÔT¡/4\ðRRXø*BNûà"<4`¥M
+õ“ƃÝ«Og@oe8c¢h«„ଊø¸áË
+È OoåûX7qÏâs¶ýîåe!Ñ܋ˆ‰ ËÀ‚ÒÄ„£ÐCê™ú„_ö‚à0¬¶‘ ªŸ•ÎÛà~žM¬"Üoˆ:ÚÕ·Ùt$x4h:#Ú3$÷Jî«„ß‘ï¨%ùR<eÁ+˜c¶|IÁʼnúƒg/<t÷„P_ß©œ†6²òzS˜²Ãm½-¥RâA öŠxÎC㉴«­Æ9qaYg/êýO/‚1éðW Ò1ôD#uêZ «5Ðõá Ôãhh.$?ºD†ŒE½Œ Œ£¸#^·Ä¬Ë­™V‡§×—5Ô©™—Tç†Õ­mæÇ©Ðá­5³~òa,Ø"ƒèFóþÃU±kþŒQª.õ©n غWdN¤ÉJG˜%¸C'ÿ$Q¤Å¬Ä@Ùÿ’s ‰æàƒ
+áÆu?ÁåLNM \Îa3ÿxãÈð£&’’ÝKƒ:8h¦|-%ÄcêÓ¶<ðàSžˆW —£¡3™¿e’,†°´K’ÆtBO3ýæ+YÑ Ÿa:Sƒ\‡•Ù®">ycŒ›÷E@\)yL”p—쇄xÙ—“t?x£¶Í¬Ù¼+TÕ„9ЄœíÓè2´zœÞ¤
+¥2ÊîöåÀ Ç\7okœ4¼|1Ç5àb–±Éyàÿ²ÜÂx@&`8f¶û³s}¨š÷cŠÃ;fÎ p(š%-Òã£hÊõÅ8¢Ü«¤Ð08j{µð—æÛZ*VÞœ6*7´>AƒQ„ë÷"šVg×aßã;i×¼EçÚÆ ‡©!Ú ¼fVÝ,Û@`snâÉü†·Ns’¹µ QëtB]"¢ûûåKàѬä´*ä­ú©žöv4S’‡œq
+H5 .į6tÒ[ íäãõ™AÌ¡r®ä1¥_tµRùPò}é‡V‚…³’äö„ŠàW/$-`iüf•dñ`¢í¹¤ò!~Š¯¥þ^ªk²yV’ͬUz~ ¥‚ óÇ*wàxßõQ›Ò£ïôÙm
+ ¨cE»·þ…k¼ˆ!X¹»Õ6ˆD»ûæ„1²µCW×¢ZtTx§‡**šl‰‡…Éx 9uFŃbONª]`3dQÑÄ–9gQ†íâ_J¾¯s…‰ ¡äzý ÿ°òrz¢Ÿ‚i•DÙ:ac—`‚S‡]-ÿ¸Á·üúôí`ò|«Ü³Œ)m߶áïU~Œ¸â!Ng H¸_Hæ–¸U4W4F´qJ\*+MÁÙÓ® ¯¹T¤˜Û*AË¥Ë cY%s,Mðî•xë¢î‰y'n¨ƒÍqÈâß <ðˆ˜èžD»¨“#€]^u3ùêÇ\6IÝÏ(¾­•h:*ȹÞUŠïj†
+vkrIeÙeä#c·"Rž¬N|í›áÖð_Õ-ÛOÑ]
+¢,(È>‰Hrä;{æ‘ RBS2¸›T„×j@“õ¾¨—Ø´rñèÇ>ÿê‹Þþ£´Ëº5dL,ij_²FüY¤p¨Á3f:IC‹•
+X¢‹ao£à*uPFV2 wŒ=pvc|«·ø§5‘Ž$­%ÿÛ-zvò-XPØ}[ZL”›“®Ë`ôzuç]’I/CLá&aé
+FO6
+)‚'H¼øÆüš‹>­$cìPî¼3Dÿ£í á04öQ‡Ä´q¶fÊjbYM|o¾)yÀŸ½û¥H8Cök§½
+ÁH²n5ÙÃReäXò]‚FV1,z§=L„zTö0ºƒÆ
+·,Ũ!ù¨?r^šžXJRDmW .5M¾ôM?o‘75? ºèb
+é?*!’õ½AÝPTý±þ†
+ÙDp(’Ù(
+Ü%ƒžÏ黄K?;»§%rRŒ*©ì%‘4Ö™{wIW¬nK:q£f$‚-ëA9J„;$?n¸&Ã÷]¤QÀžµ7w•„:œ˜ûXEŒ‚‡C¼PEº+âK8 P€‚8}ŸŽ
+›îÑEÀ¼ÓøxCEò{ ­LÇ0¡Édp‚1tNxðiº3ë¯È…>¿V|Ø» ÌA­­ß|è(W˜Íg¡Ä„G…iò.éü5ÕÁg¥ÒÊWSl~R—éà*|ÏŠðU<°Î°´M  ‰°Q¢¹¹G©Ãh4+Ñ.C*s:=uhFX–•ù»ç$>˜~Þ TBcÒäVn‹°rS8Ù9ãÊŸÿ
+ BR9ùeøäÞöðÛ²­…lªô2k]TŸ6Uÿàñ2¾ð5À|îÍ ŸÕã8$öCÙÇ>«KÐv×nIt©¸·ó¹âöìçš' ë²MÈYÎcOzÛÚ2¿”ÅqIé³ÞR$Eæàf‰öEo× è9©ºŸÃ~&Éa'aíˆ. §Ùu›Ògóá} ËŠ ¡ ÄúÒÀ0  —âßÁŠ–•Sæn/%8aÌÀ´ @§Ê5·ë$L$ÔƒEѱ®ÐAOoo7kŽ©r_âW$HIÃrôˆ¥ í ^_6Q^EŽE«\âÕ«‰’ȸ¾˜¬Uí!J…fÜ%ˆ+GÉ›ße’GÌøòte²¹ÌØ°¢!g©„$úþ6;‘eb¼FúHÞ*A¥¦tÓñPdÊbîÚ† šCÀÊz;~q[(±¨;Ÿ6Û¢ %•MSWŠìÙŠHñ€’=]Cä¥qØPµO了۹P3µr,®ç6ÚpŠÂH1LpañÇØtù"µÌ’Ù²|"µ
+
+C²“¨ ÒV”ñPbG ‚!hÄy´»ïLÑ~¬öj·AÃ9*ŽÌJF”yïv“ÆÃ@ühT–g+"ïæ­é8L„3ŒÍ¢Ï&ƒË¼«=[£íZ{䦤ÊÙW`ÄÀý¨—"ù¬"ëÜ7Ñn3X"‰ K=¾ 2$¾‘üM‡Î0lÌ*Z.pX…pf•
+K¤GË㬰„‰èà<”ØQð0ÚÞÄwßQ·ªØ¤úmxÁÄœæ²ÅƒõäŸØqØͦ=‰&Cr+úQ0›È&×i«¥l&jã
+vÔ ¨ÖN§ÄØoJ
+Ôœ±¶úYF0–ô©Ù6oÛÀÉl§„+nˆeçŒFñ­¬2x)Í¡:è”»Î:Œ)HK¢I¾Neä@ø¬”þW?`(§¤(‰ ¸‡­>MëÏ´¶Ã>½zÉ*D¥•é–…>“dyt6Ë»Q¼G•ð¢*
+Ž 9Z‰ìÕõªhW^¢NÁ²AôÐ<[5 ÈšD«ZËÓ’Œ
+J˜S"‚ªz¨cª©:PϹ:~p
+-ƒ“;IR1 ícɯ\ôÍ·ÂwÊà õôñRM4e¾3 í€=woýëÿ;!JÑ30²È`Rµ‹«þˆ›4u„nE”T>ŽlEAËÃß%˜0EìËY¢Q|Öìï¯/&Ƈo¥[ç•s÷ª²ÂIM¸ ¶ÌR\Q6¯`Eš
+δ»QÃèàÁNwU8ˆP‰5@cgÙXwCäo¢0(aí^©X”àžÀ\¢/Cc~æ±:Ö<#ÍNî‘딞 «¯RÑç5f£âÁ8:sç[aGh‰É6/MÝ%(]ê?ôÐ?œkãø’˜&Žàï@ñALÎÜWdLd >£"ûvñ7C|’©öB9Æ­Fúˆªf¯½‹$("&ÂFÉQOE¡Ó©CÃ+’ê¨x¢%tê€E043ÁÝ7Wp5¿kfçΔàÄp8 íóú(áî–"P—ê[™àÇ€È\ Dc($ñª}ÝKÔ4¤‚'9ËÃV¸º×%EüôU~ÇRÞ{¤ñŸ
+H‰”—A’\7DOÐw¨µ#æI
+¦C*͘>Áo±9öwª³%fù‘9¸«öÆÙóóßÏ«°huÍù<ä'ûÆ-B•B8nŠ€Ö"¬T+žÞ^æ3óozò2ü»ÎE†„ØM{ÜMèòמCïm´æ䳶AÝ,ø†À+­w{ˆN•¢á}BX:ÕëyN²1zŠÎ›÷ÆQjÈDßß­bt£ç½‘=e 4Tý&‡ÒÔ^Çó¨WP)¶öÍíyÔÏÕÀ‡†|ë|{)h×m̳{?µ•:öÈOó~îïËšüÂ&-‰zü3Îk—²‘l3òŸ†AùL! ‹‹}KФŠÉ°÷Âó=îÚ!^+mÝ­q7+-­žmqŠ '(´oÈ Y…“"~³0ëþr¦7ðF9ËûºUÂNð£0?GuÆ
+ß 0eOáq4è\6¼ÄzUºvܲE/¨‹ÿÙt¦‹EƒFzÇ hÏ‚ ‘ƒ>lˆJ™Esƒ×ˆI³&¤3yè„"mÖàWb£?Å\×À §Æ­Ð EÂOD ›¢®ôéˆãB)ø9roß‹¯&t–é̹*l…¢RËp{ØŠ×p·$# ‘ú7®²WµKïá­6N‹52l›{rÁ{ÚÊGî²ÒáÛ­Ê~—s3&‹° lp†+˜#›kA*]rVtCÈ=nêé^^Ý£÷Ï“H
+^W°=Ê!áwƒ¬â)RÌóe7Kr ÷ ÄSõ D
+›M ö<ꄽ¡HˆE×sé¸7,mÈ·a@B~÷@H´•aÕ’;¶ªé.áç¨ZãÕÐ#&n ?Bqöwp=GŒgËm¦X]è{ÿ<ŽŠGo6ó ­6.mOÕxÙÃŽöÕx@ ?Ÿù”œŸ«ïԆ䜚ú÷+úÝœ˜á„nÂ*0çÔ)<¸Ðþ±2t€xôFåˆn[ Z=D¢æ^àCŽ`¸5ƒßÒ)ò?J
+•¯ªfÈoXÜ—}T©â!^ ÄÙ+EtÚëEÎ.aͼúÆÿª,$H\¢íÔ`*/5¬mƒB„²­f_¿×5…kàYæ¬ë;S§¡d¶zFûé¨èP<ÛXÄ:ò;uE^w£c!ÑF¸J}ü¼¾ uLÍp­6¯7Ê£….†PKæŽWeX)#+\–DÀãAا„â20K$¨”³ãâFêÜ÷-—)á['à¼Ä.~b\ƒÔALÆR#ä˜>ò#4ª’ õužxÌb3(Ô„ Âöú~_úJf(¦!¥kì‘1ZD7z>ÏL+‹Ë^¡Ò’w
+Û_!,§/;†„´%Ÿ±/Äq’ƒrË:É¡Œ‰VÉVŽhgìÉ%÷BÃQ#ÊÀýcàL˜Y¯,\pD–¯3§ŽvÇ$GBÆÊÏL¡‚­Q*½z‚âRk%÷kª\¡Þxv™ÓÖ~Ûѯ\ðš\@âñ .<˜„ˆÏ»zŽ‚¶‚¾ŒvrT˜)ˆsí‡L‰!rçeJ¶˜Ð5V–B<Þ@„TØyâ6¶íP'm‘ΘqŽ3‚qÕHg×_êAçi:†ât ²+_VTºÞ€¾nBÚñ†6F¦)DÞQ~DÛíÜÃœ"çy¤¾ƒ5ÂQ"j|ÙXk‘šd§Jã%JïOòu•šQÈ™Õð¤k{á-4' àö¥=I
+€Ð%†€¿Îöæq(6«Gú+çb(ˆRÆþåHki•ŸÞ1Áw^'¡áùŽùá¡—dË S¬Ñ”žz[N™HÄ0ʾÆ}2‰—,’dt±i¼NÁÔ>BMùØqDrÔ|ÓoÒÑ^PÎX(;9\¹‡:Ðö,¯£îø£C§"²Øì§Ç¡á=‚ãÌ †ýpsÖ}ZjŠÇçÖíFûÁVHh±þØVtD=›ˆ@ ŽroÄŠ*ÚírÇ÷1t ¶ðÕýAâÐW:ocC$>@Ü9¬˜Axj¼¶º­’/=—æ×ð0 ñëÆľNä‘0¸1ðka€:ÝíêA¯pž[UY\…flRB–"ºt-wØ®xåH¸„§„)Š@Ål‡¦À£íé5|¡ðÚ°v(3Ïp‰
+N1~ÓPò‘
+H ‹Åë½:ÉŠŽó
+óËÛ˜Õa@¤Ø=¤§*‚-Ž~ÙƒÀ”Eß™E9r:ÁŽ¬=(Zö—þ
+*E1jõA{McúcÆ(Ð\·¯i †`,™+·8Ð J9Çs r©¼=+f¤cÂs8*p³ölj¢Ö¡§_â ÿÒÞÒŽqLÌ({/Ì!Ž Àõ!IÒK‘ætÒ_?Ðua†£á©‚›á×8¦Ê@
+ã6¥~¾åc¥@ÈU]âØZN‚¨WÏÇ8¦åÔ¨3¡Ÿ²Ö‚äÉe!ìÙ;)iŒ;Ü=N =8™ 3¥ûŒ¾¦1 0—ÆO9ôÇ4¢¬6ª»·ósÞ¦ø#ŽûP1ž.qL îER¾oqLGq;Ì0mwxczÔYÍ»Ä1µ†a F ùÇ
+µ(Í/óêrð:M'j¿Ä1@x˜6¤UálT€ˆŽeµ¸ö1Ž ¢ÉÁ£/qLWî\YçÈK³…„°’ó˜Ç–,²à BÎÇ8B”Ó×RÇKS=)"T·V¯qLk%u(ˆöðâ¼Æ(µ“&áZ¸`»Ä1
+ÒÁ®Q¡rûF±óNyÑcû`·Ï4&=£ûÈ^öF½f1Í0ªÚð8ŽYìÿš©•Å¾þ¥ã0z(³J;ÏÏ ‚Ó›Ò(ù<ayE6†µ>ëóÒ/ EÒ7$ ¢]\âÉB„†­ +Š šfË8n²ö¬-¶£„Îbõ!°VÝéŒY¡«*½‡0?:2Õ¯tÀìGa•aÅŠÛ6áeÉ؆ðàR0¡SÓnIKOq™C/6O"¯Á ŒðþŽ´µ‰ˆš92Þ´ö.V?ȶÈöÍzHlv¢AA‘÷j'©2ìÆÔŠ¼ÛÄʇxxþÊËüøQdN$¦¶ûðR æzTµAf~Š¿.ÍaØsÈp¼e„±Yâ§Ä‡41ìMª¶Ž©‰>£H!ZÜH2Ä´©á=C^”â÷Ë}Lt½Áü¥ûáÑèbQ†³ÃF•rU~eÕnëÏœ·ƒVì‚zf“/K79¡!ˆaR(œ—ï"cÛm¬„üC)ÿ4ˆÌ ÿ›Ç{:„ŽÏÕ’YïÓóÚBFðBõ§c­TtÄhg½j`m}:¢ú²¤ÀYŒÚ!yãƒ
+A|ˆYýë$>Zõ7Ùø0UU!Ú}É8’î S„•!JÍ'žc64Çv”md¤ÐÅ‹÷6ªxÕ<e ßlÓŒ ^ɲbí ÿEÔÃ.qNÀжq€8}ñe&RÉÐ÷IE0‰ô<Þœ; ¹±¢Ú˜;ZZÐÝ&¡ã>Yfô삳äLØqsÓyÏœÜG¹}FFÆPý'äÍÚge!HšI~߈
+Fº(í=“Ûqwø¥öóëb•sÙéxM9fønº|Ië²"œŒ©½Nb:‘×._R=öJíã
+˜VŠd2œÐT–î>E^€ášËÓ¾Cl"h%sƒŒì 65u$…àq”K¾ŒÌó2|ÔI‚=ÔR?ê}†EKì\£híë¼Ü'9µ_ / ¥¬²ÞNØ‹‹ò:Ln õD?§ÂönJ†ô» ïý%e ò•Ì¿@¤ ¤Ï0t¬ƒ&iM)K«ßA?~åNßûêˆ1Ÿ‚Qä÷ !âÈDZ<øíŸ"£2Isîã-¸fì,ÃÕÖØK÷WÄÉÛµë8<<$OòliC¸)¢Oý³ ©íI1.ˆ?`fð0»·M6GVÀÊûixžÆKƒV~Cà‡¦!у¿bdJ,MÜ”d`­Y7‚ëe†A®{C¸A!båö e­I#€Zï®e©&ÿMâØßao1LlÚˆ7ÏJ‘k°“6©ÊP,Ÿ„Ä€´$_àQnZÚrq`$kҙ˴ x
+9'.Ó $ÑM¸43yC83kWÆ°Ë„Æ°Idóþ´ÿ—ñ2I²ã†èUt‚ÎÃZ^êÞÊ÷ßú%”Ôýù-‡í¹…f‘@"‡&#­ƒVÚ±;
+bã|<“Q‚“´?°Zž©l(¤/šœh›ÜœªÙF+v)¡
+ÿQªÕ»h¨·x ¹ù9,n-Üf·Ò0D'|É/ƒœ«ËðÝÌŽ
+žÔ&…èwæ^W|‰»A´ýœ¶gPg;µC’ɎßÍÈxr¯àÔµAÈ©üÉŠ°¢„5¡­rê‘h:Ó´B¯E®‰P(»'êòûYabhž!Š›'…°DB`åÊÅ9©ª§  ¬î¬ -v9ÛóYQ¸ŒEwÌÆ0@‡MŸ"6À×T-ÉdmýR„“OX©¾;§ŸµK2jq‚wÁJ×+·M‘L U|¨M™•&ýK`¼Ã&ëYò©Œ*Cž¼ÁD´ÙI<Í}韼Ôq‹ßþÒ÷€V’m¨>xö
+KÀdÅÌvQÄ<9’׬¼ÐæË?[ ÜФÅÕŸŸåÑÑ]¼Ì°HÁ-Š ~q65¤g²&K½d˜Š)Ž
+Iî%×Ä$‚ Ñâ6dŠNÜ€°Ü¶ò¿¨
+#Ï* çHÂÖüÜ !&: –„-& Iˆe®áˆH_Ug(òe¹i.hǺ1$ˆ#퇀&9èz]vÈ4®s¸~n»Ø«)ÓÚwÔ…)— þþÓüx<Jåfݬf
+kõ»ÅýÇŠ
+~ϲßßÜE^ 'Êg×~Ö‡LïwÁÇ 5tvŽ@X!œ!Ë#<î'’3ð¬AG†6þ jxz^kN¦žüºö‚«2‚òϲ9ý-DKŠ4¥KýK´‚ Õ—wÄ'íäçÀkø£±ŽŒ¢ÔÀ/éÎ"†õ¤C ϧ 9Zgw^ ,g2gçÀIúƒqóÍÂm1¡ É)!vÉeB‰^R«‚˜~m•øÔ¤Ži`„ÙŽE—šm²Mò>kƒ‡YOIÇ…)ꌱ/%Á_KˆE´n5N_tfvr•ó¾UœðkÒý]"%­4º›<§#±™ó-ÂÀ†ÉûlÛug8Ÿû`šÞÙöC•%ËïúöŽØÛ4ÒK
+´ÕÛàB¾öâÿrå›ÞT"¡ÈØ]o€2¸l0!žÙGŠ’K‚]†•ú?Õ’`’n唬Íõb*râ)ôæ÷wÿ°Îh’˜>nkzŠÊ‡~4aÄtÝe?‰;BÃÊ/±j( j%xšÝ3Ð皟ÿg×H¾<• ãJýBTÿX‘<j%›´Ù.E§Ńìè¥#‘­òúR³
+|·!°äK´ûÛ>4·ÈWÙ*2EW,a2­§d¡«,¶xÃó_l%õ6ö„íƒÝÕö%Þœ¡f¾ždøVê—šŸ^?‰ÈÇhÁ8%`KnTx÷p,ýcÕÍð
+,G,3Àj%€,PÄ•s_‰éh‚O|ºêm‰µŽhÓéÔ‹©R9˜ä5÷î J•˜BVáf‚§…äk¥a¹ŸMð ób‚çþSÆ5¤t¢Œ÷'(cM”l¸|3ÁšeF›ªáJìh‚µ„N®0…g ÉÕ^‰Ïу!8Ö „­åïUƘÊ÷a¨ñ7'U>SÛ«üõ“}_7ÆO¾4B‘ m]0𑤼ó× Ä~k°õ™ Òŵlær<$>†¦z«Öñ“O¢äÆœ_€Égð×ê Ià› ‚àDÚ:~@à £+
+~›qbŒ×öÎORùm³4‘aõµÊA4lrÁÜåÇ;ѵÝWôRNƒÇšƒLrKÖVÓ¨ Ó¥uós*"! †iAÿThÕc¹VV_b…!\+iÔ•_#¾ñýãèa¼¬br³>h†6°•)MóV4+z$¨N–xŒ§Ê*myníÄQȈ=Y+" ÚžEDŒŸ®ÏŠü4ôÁÛ‘kÕGk¢ò‚ðR“4ûïu“<6Æ—®µ™F³¥á›A ÙíÏöÆÏÑU­×Nó?‘òÙck‚(×ܹpöئö‰Ç[Ý!SÚŒwsȘîµäº“+jALEHÚòcÖ?eðyPhv³%µ¤¿cÒe¹åVYcÛV«ˆ”¸hä äÍõ
+dê¥Do¦&‹ʬ¯U{M¹‰v6~d TÕ¾"^ôåyùXPa…²@€X ÒÆ’A,¹›®ÀÈ«ÁÉ3Ç ´‰l§¸¾ƒ$Æí(¯†ÎÏ¡3Ð!éÀ!Š1!³1iT'UÒ^´Õk¯x*uä=¡<F6Cß÷«ÜƒãVÕ¦›ãÿñ³¹? nð:È¿ŠÆ„&úâ#8[‡’v„Ń0pU²?!b!¼‹$ËÏé„N冘ò‚ˆ Ú*:_¢âÉSÛW¡0YÞ mŠ#ÖÅyu3*l)‹f~Œf‰vÉÂùM/*Áë`7¸*‡2)u‚æoA½<^¬À÷ú×-%M¦Iû¹%ž¯d¢©ûä—Å–Y@dÈ!\£àÇgš-"QñÌÛɽTgÌÝÚ®S‚âÇ æ¡EÔ•*ù—#+‘Qgc7³ÿ›þ ËÞ1†’&1Är¢M©¸FF°¥#Y’áG%r¿@$
+ÚZTð(âB…{B¸ú$@±Å6™rÅD/b„š¶­=ÝÆFOGòãôõƒ¬ðeÆÙ^Åë—ðu=‡/AsµÎKLß— ¨}Pi±KV$e8`±ú)ò
+îÄg gÇð±–5Õ… ÉØ ´Í”ô•Ü˜X±iq• ‘̈‡ÖíפuãHö|0˜h´øw¬3Ì"[¶#¥²ï†;½4Úb îÉt—IHe¸‚
+ñã|ÈÓpy‘#A¢2aã6¦(ÍV¦­ÿÂ/hÓïÄø¶9"g¾•ˆ!?|á7r‚êJa/Ûüõƒw^ˆŸŠµ
+‰°ŠË/4†;Ç"†ˆà ÀYcPH-PrB‹ÈA–§åóÚ”æL•€_X'aªXo{œ7ÖûîÙóë°[¬Þt󥟸ê—cdIpëÝvïÿNð£xs‹ªvÈ|te#]3‰VAú(H—63«\ç—a2i†µ™°$ÅÞ;@æhÌK ‡-¢=ñâ"§¬sòÐh1J~p­š›Æ @ªÝ+°ÖŠƒä³×y_…IN×Öâ‹·•VÕ·‘Ö9mÞœ”J„õ(ºG†Ñ!¿Zq¶/²Ç
+O…¯FÑcæN _x·èCK™êÆL²%*³0IòUµRÞ¿Û«Œ/ã–j1Ÿj‚ú'ͳÊl×£Œí“¤`‰zÚä¯lûä„?…,“Ð""Ãh Z‘R\5† íqb
+c§ šìQñmêÀ»É16 tkÓ‡}NLHŠï½T®Mç%¤ˆÖ9 t+ÑÚvZ³cøò"€Åé=(°ÿ†¥4rçzÜuÕv³Î9õ<¾buŠw{/žv™ú€û‹“z§ñ
+FŸ-`JœÐ<)V¹†3ô•]ÒB¹¡WŠŽ³úÀAÜ'£qàÏßÁé/z› ­,oŽß!-§8d¸m©ëC¤%W•µ±ïöQ±ïî°ÊØ—"‡4±jó$É„ÃS5‰ÊO/ñô°$8_Ã.¯U¸JS¬DV¶L5G+±Y£ãô¯Ï•#Qn<]è2¡ ðÓŠcd1¹aÝc_ÄÒN
+2&tGü"ri¦ž¡úíË“–¬D¯é.JnL·î‹²—ƒ2q÷ê¾Ì¨=ëeQ>4 ‰
+nÜGð©a XQ_4Uuh´Ç¼ŒýNQ_J~þ|„´?ÿ ØþõŸ?þüû,êÄÔA#*¯fUüX†FäF‡^ŠúÉ#[%ëi%ÈöÉã” MK…›J–š…‚—Uð£L¨ùŸjr›Ä!t.Ž=ñ±t2Õ)ö‰&Pd± Èf’*`œ†'ÉóX¡§
+ÿ…Yª«¼:E ‚ cBk\£†£—þ"¶-ªzƒ `ò±ãK¨<º(l7ïŒîŽ®g¶§Ó@·Z÷û""L™´ôZ+)B皆³ï5CBzþ8ηÛUÖ„ƒ`ظ¡q}%pF—6Œ_zjÒ–¢!ö4w¿ðSåºù_4lñç똞JüS…ˆ&viÏïçÀ½I„©_݆Ígq¥l/_%wE^¢¨_×'H¢Ío÷mÉ0Ö(è7âò¢äËBýÑV5ÎÌÃ'Ê R‚¸²Rî_þø¤uw£]ŠÞ}ØyØD>]EIÇU£©ôµYÑ~Hf;f„z)úø;½ÛÕaJ¸ £Öæ1Óœ5ˆ{GßÚ²«cG ÖÌ.Ž*lÊÚàM™Ê(v«yÆB¢+Å&w+Âút±;ërœEp6#ç%ˆ~aq¡^(uç×Ed<`‰-çC h­Ò_ýJ VCAE™ýXTí½BKĤTßì©óè¹ÇγjØá–ê²ò‹‰W3¾”ü´±El£NÚ¯Îéî—Ä÷¢¤ÉAM÷ÊVž¡AL{ħ6a 2;FðÒA^€yh‡œ[¼›@'°sŽeDøqKýðw#mœ‚Ÿ—Ð&JÚS¬„^ÂðlÿÉtÏ%çå÷QWo±÷DRz±tçS"ˆvÛo\ bd¬V"ê9Næ_NÏ;>Õ‡`×_ßX6¸ Ϲ6;§c:pDv†FÚd¦W4yü¶?îá-æG S´ï—H“ ³‘Ï×E'ªb‹ŠÉämqh!¶!iªëÿl—P@‚mïË2üÎÆ;1`¼H£r÷«;!qŒµ2åÄp)’7'l¶Šz³ Ì_y]¶’–Ž3o»M«@é&ÿÑA§³¢4·²#; 7·êVs
+&Š 7ÛZ[nÁ#‡psÔµd+a `4Æ7Cˆˆ “Å›ð²}ŠP?GU—™´¤Tmp!Û)pÕÒ°2C¼ÜqbS¬3 j+kX/žÁ?µ×¤(èú9ÐoÎÄØ&¦¤°óüI¡Ä±—o4”;ï{…XR–½Ö¾Ìze¡8)LíT/Eïׇ3Næ]×,Ænù{Äd°®>ÏLs¹ñ>6˧'ô¡„@,ˆei€#¿± %“/Ä‘ÆÄÑ»*Û“ÃœHû€pÁ¿SЮ…¬yÝ›“t=ö™Õnϲ¯J°7?\h ¿]ÓqŒïÞÿ¥¼\r$»(º‚ÚCŽ¨Áÿgh”fnA€
+Øÿsà ü’Æ“$¿ÀÆ0ˆVŽk~y•¯ðâêköQÑ\´6±ÝV‚ûàØHa²×÷EeÄ&€‘—xÑw +$‚áõ½æùž`9‡ÇOš=ÐC_@Íh§ õ»ÕTéÎBô"(*¬!-ÿO 9kí%R`—u0a“YÉ^‚h,Ûw~A;x,h…û>í¨T”ýxÛôTœgM¼Rµýæ$A% GÑ¥èéEä“"á®:…s€Ò`¡×þh]šSÊÙGRtkŒÀ,ö®C [ÂëJºnûCQÁaèÓýöñبÌ]’û¼¨,å.qò~s²¨aÏLÛ
+ˆìÝ2,G!W_Ä¿‡
+ÇÐÜbY"ÊÓYzÊin¼J \t^«ÐwkÀ|†“
+HÆeìïȾ@Ññ% ¢­CpISne£RÅ…’<çžVÔ¤°n}ktù,g(Ûö˵áûÉí(ãiv“¹ó'S¦/´uXÂŒ4È:`%—ݬƒôÓfeT¿,qF2O+Â^aj`•¹Dþs…‚<%¬ <ïÞ%—&R’`ý¢U÷5†ØÀ2k.«û›¤˜AÔí¸9µ/?ž$ÅIE@ò˜¬š­oÀF)ìÍ×öZåÏäªÆq)ÁÚUe.áä„L+ä´a%EPmî“BØ-)+Í}éœB™åˆåª„ƒÕä¯ÆΞÚ=HÆ7¯àL=1ÕÃœ>í VJ²ŽZ®”KoŠË’9ÉSQû¶åðØж“Á¥ð–má˜ñJ’É3ò ä‘åÞ5>¥)‹mZd+Ò+ÄÊ’¨Šy**²~#ÆØ<È4¹mPl)âW¸Bùí€Iþ“×:U†9i-½$Ê:[óËÛ’­ ¬*èÁ%k÷ý(ÈJ¶KécËlŽ²²àÝ¿ƒ&3Ý"Uòæ}:ËY¼„éu­q·&ÚB
+í¦J_¨fßØ;þ;-¬î\‹¾‹°Uæñ‡Äq½¡"H3>ŠžV¤¸Ã„¹ØéK«Dã,2¯#{ƒF"u”ÑJà,ØYé^ÑCаRh»‡J¹A8­^‡RHBÑZ±‹•>-ˆ
+„5“½iàpÛuÉöB b‡IÁ’„xžÖµè8-~têÛk6­·­¸=¿guÄfññ“¯¼tOSc½Õ¥¦ÁJÕ ñçM|ý´7^ŠV 3SЃaë&a?s‡äÅß熟S†žÄÊ©ˆMæglj]›¨Ûà'*î‘­c5Jä±½ûmä,q´Ò¿­²TÔçÊ‚06àÈ­"›Zø8ä £ÇóPd»ÑËJ°Um÷!²eEÏñÚ ¬mÖŠú£„;ÒŸùG¬G­ë¬H#›œCI²Uád¤h%ü¸\íh7%oM>%Ao
+³oä_o£ëCk<ªÎ󣤺µóšo'½õF¾kY;ªÞ›K‡ßÀõ>©KÉ/—$9nˆžÀwÐ &ø'¸–—s m¥ûoý’[î*ÖÄض£ ±H ‘Ÿ_ßA²@6èoçE]BI(&—®Nh ±\>Š¼Š xÞì¯ãëˆ1/Ô/‹îI…4%¤è¯+B×RvÝó`D\&âQlA ô%@_Nò£×§’l sÉyô·"ùuúÄqÁéõr:áÞŽñø*’I)Ò,h;ýÙ[s0bt…CLæúÔa"P#LY+ü+')ùyœü¥è÷"îÿ•ŒãÇ(Á’È@²“åtc*>¢ñhçg‹Å±¾°ìÞÝã¦I.à©gp*¹så­H˜àcPÛ4~œn²ºT•ã¥§7ÐÆd«‰|ò+ô:8•HÉxÖ1$ÂY¡ôóº6ô­ä×wvsjÚË-¡ž™"'vÊÇÀc#î0YðNÈ^•õ¢-éP4Kð͈¥Ø½DJNâi•`¾õp~Ÿªê˜áaÝê®%} /ü £.´Ë:Ãzà4gë3®ù”U‚_ƒ3T°ØâPü2ÌðÊëKך¢»é§ýÆ·Ë€c~ ¶ÜÚáQ5©A2ë_ºô†°—QŽ€°øãÔ`* €"ÍöW_u)ñ/á†a˜ÉúÓ9ˆþèˆ:ñpŠ6=*OBøÐ"þœyóªÉ1gÂR©ë˜À?x9ó÷öŠ°e°êCÁaF×’û¤o¹ãåöžî.m9á÷ÒÜÓ|½M?çÒÝ*3À0‹¶"Oÿs¬á(b†Zv#>¸Øo½í÷7n´ÈBŽQB‰¿h³‘™ý`ìœ[/+Â-øûP´(.ó%bÉHû,L
+WºÅ…fd¡Åé K·‰ØÑM¦’`-ä²D™5“e°Àê:ƛ⩗!ÙÜ`
+“îpc¯`%ÕWI†g²²ú²D82>.3ÚW‚¬cÃ&dÀRèó;°²Ž&´æîŒë‡ªž´J †MÛßA2xtˆ}^¨R«ªZõLKÀ„kø!®’_ÕlUVÔ“;Ö!%§CÉÙºÞÏÁœ$’*Ôö«`)’é ¶7n|<ÄQ’¿
+1CìqçË•‘v%¹1û9ì'9CÂŽ+ðá`ðõç’Ê^±´&õ`ÊnØ»_¦Ðˆ„PW¾µ(ýö¨ò![,dáÞ¿téMáá3é˜È?¸2LßMˆª…Òcw?Î(eíèh>”¸+»ñ( ­h*ÝI˜c6´zv(Z·8Èq ÝA,+À3¬WJ@|jè.g#çZ¤ýÂ)5í¯ŸSfB,~¬t¿ºƒ}¢©­F/ )`„Ù‡üú”L £Ÿ£5¨ ¤ø>Œ^ Xsa½$¡TƒlÔ|9Í,0ïí^ðk·¸S€
+8p6/d9žY’
+ðDÖî*I*ýd‰Š—p1nÖI(¾ ¼ü×fbz yfÇ1$†ñ\ÕݱRËÎŒUÒ8ÎQ­XÊ‘aâÆ©Ä?•8¡qÍŸŽY’ÆEý2ï7¥-7ÏD²n¡Ÿ7\ŠˆöcŠÊëᢸ熻ÔJn´u*‚˜iE&¥ìçÈõp—\2Ù@~é-§I׺0gÂB¡›3:ï¥UI‚÷§ð¼©«;k]ä_YS
+W†Ü9Ví
+å5)¢`]ÿ¡0ò͉C¢­cŠD
+Âcõ¼,6þêµÀ»U¸”üüçì'¢Œ
+
+7±äkO¯Âûüy(°欚ä|m=¦€½§wi}Ç*YKh¦gCèO
+Æcã*Ü“.[ûËöÅ”VÙ
+iH(d¼Ðß±¹‡#ç£E^y5ߪiõù1¶îÌÍ«H¼ld,ñ”*¤#}à9œ‡’7š\¦ŽÇcˆÁWþBe탅Î8và ÙÒÚ«±é"âm8IÃÙý
+º’¢ŸSe#ŒÀÍ?eô
+1˜ÕMVÒ¡Å·Î*$9~]S7-)¥‘B4Oȃ➠U£M¼Iø”,¾•ëW%Öêžô¶…šÅ9,ˆ7é_ŽÇÏg’‹©!ˆ¤_Š\‘¡D?_P«eBw.-²%„Sõ¡3ýˬ3´Ž„ãò5èRUÄ
+ àÇ¥G(²¨²/KˆÎ“ËØó‡
+6$ó7æ‚ú`“ 3DbÑ‘+1m+µ^Sº‰È²•Æ4c*ýrß…Yª›wÁ
+E¬¶½ØA¯ìÍZ/l4¥ke˜üR߉À3–5…6× Îj CÞÆlì )HdQäuAÈ pKÂ Ä ©ÌD)*µ ´+¶€ò_í,+³®¸5AØ~Ç&¹¢á½Ñ}ìïDÔ
+Àºú¥°t€±¦eBÈ
+^'ã–ñ(Ð4)žUŠïh:R‰PqmÞ@v•"÷Á1d<mÔ‹ üxó]!Ìç ¤KI;×Å™ý‰Üj9+Ý•Ü×û‘* ¼b[×ôŽ§"iÀôã
+ˆˬ‰_¯s’C³tqAø³HR«iAx_”ÿ.i æ¶åÌ´~€ßOд·?'Ô;eRJ; ž¬€h5B
+6-]œƒûç“,æ´ah@”9´KHö„DºÅ ôæ¤d âZÏWHz€Xl¤‚»b"æ9d«(%”t
+?†ëÙ© òó•IÖлkè·óèbæ¦å"X™JÆLþĬ*z1‡¯ÓÄò’ìetš+}U1b“F‡XLs)ð(ÈM …Æ̵ "n@‚R¬&Áð6ÚÀ‡½T«ñˆQ•+"ظfl» Æ,°øAÀe™¯ÂcǸ+Y-û_3ö¥:ú„Ŧ¶eQ%&ˆw{ö¸æbv~ƒü0;¿–ø¶V)=tóc@`P¯ Pb2¯ ÏÊ£€µÃË¥r0{‚°T³‚@0*8Éàe‘­><![ ‘`—`߬ÛÈ(’Pv׬7Ñ *˜PÜ-3ëJ]’R&qƒ$af`ýTÍ…yôé+âç «y[$FêäÓ#÷eîWG¾¤»¼êÓò€°"ùOX®r0\
+H‰Œ—Ar\9DOà;èS®=Kßb"f¥¾ÿv>ɲ­úåqtD·ZÊO@"‘h}øÇ?ßZú11Ë9zÿøWùð6úlª™‘@Úc†Fk.cAºN ‘àËXLïŸB炘ÏÞÇËhÿù¶@ÒݦFú>GSS2<æ:'¼K5ó…hÓr¸ª  ÑMx‹9o^™Þ¤…„v?7µðl³ÏX/™#½…Ùð+(yL~12uι 9ÃçLáø…ÈÑL[¤¨mDãçڌu“<F4å~›ShXº‡~©ëœÈÞ,»µ±!ár}4uˆE³Hs_åÞÌ[ŸxnòtþÞF;¦îd“¼'ׯc®{£·&± ě czi1ºZ3ëÏ«~Åcºg©çþýboÊá<f.Èp1IÞA¥b¤ÚÓ„œ83àÓ©”H¨•MûÁ­™9Å|EÕªÔ‰8­å‚4ROáF_°}RNQ¿ì«ÆsÂˈyÍ`T‰RåÜN;ßAÛ Q™Âu2Ú¾*Ze˜‚zŽu•>¬‚ö!ÚwT¤IÄu„ë\çôa SÅvŽ¹Â%z7×[Èx¨,½‘Õï+ªJVð?߀>߀šR+hѺõÝä³Zö ¡¿ƒ¶²›3;è>eŽýä†ÁRÿ™e/Z¡·¢ÇêIÙ-Õ-ð]ã$§”ƒòöö¦œ¿"ö=æ1èÄÞO5_NQ¨ÄÕÒs?7[£åB²}Ñ 0/’ü'CVäðÐxB!å—(Ûf(W@R! »Ãų‹¶fMü ¤%u&L58W}!:é¢ÈØeòm)´ïGhQÒíû*2KwÂ$É--É>ý‘O…$j§Å¨ÄšXÝd_Å áHDæˆÀG(ðÎBÁ[†÷~”Ö¡…‚Œž›¦RkdtùCb£# Fs-¢óW‡¡cû&B‚$a—þ­¶Cx9õ8²Ï1 h«¦§íºé¤È9³0$J½
+6÷9P«SIÛTÈ'G Ó72Œ¨yæÏ1^z/µcŠìö•JÌD¯N˜°bÐÒˆ~¬±ITU¶¦”gCą̀$í\%ü«{Î=?惨Q2‚eHïΤŸÑ!æņT[p?]ô,UC“bD¥ë²k¥tœáÚý1ŒÍ~µÚ°!èŸÀ’È )ÐJû¢Ö„êZ)ô6å—èç1r¼
+ ¼yÓo Ï¿yø+¾¨Ž#Ù^ŽìŒOL)X
+¯W|€è¯?Ò¨Ì9-uABô9‘s±"ƒP<cC(sN4ï Uܠผ "÷çPKþÖ¢4þ H¡Ö,܆rÀder¼s2¯ Âä øÆÒVAJÑ«ç0µÃÿDi©4ë‰&mPPžî½èþÐïoÂe'jKB>¨\°X¹7=L’Þ™)[¿r][…_Š}k=«L<ƒýƒ­ºóVÖ³qûz
+û ¾ 2Øñ¸!Óq•×¬(†é@¹® dCp6÷gôçM¦áÅÖ´Œ?r¡k]|1bÿ, R?°ÃHî­åÁ†Ð]ü˜|nb»$ÏýR Jkj±jûÉRKåzxàk(̬5ïø,ÜÈ@v)E¬sèxeôP‘y¶$QÄ
+Õ:Ô •¶v[Ì2÷5´˜ÑSâ\õ…Y_amY’|P+/›Î¸kŽÞ@`(<\¦žWý‘Ç?~º /ÓÄJÉÃŽ0üª¾ÿ¼Á0E±Âh…éXã´„5¹€.8Û,S’a…C±‘¤°5–9ž5òÒG„CÏËù©Þ<53 ¬œÎɵ2³Ã Ã}ÃSÓK5ÒÃç d_$ØjT$ö†özŽ•ùðò’CöUl¡ÉAT9n?×\b¹˜Ý3>F±Xеµ”¬ g´
+v‰‡]ëÅß`nW/ÃyÑMd>ÛÛ%ŽQ8´žù¾›½øMQM¦=[5¦6V\æñ>()&
+Rß„Eþïåüë ù”.i/
+Ø&ìì`#½ °—Jq3åÑã¯ëC·Q9§Üæí]å†ïIwø90|ö×)ù2§ÿý`_>©[@?‘ìsiÞqŠ’X¥²¿èÓ¼ï'>b<mø2À6Ð'èÈã)OÒ"´<êSµúyÒ.¼³…ààs¡3ö“Sí%*?–á”õÆ"mþeâKßÉLÜÉõ|TDq9ž×už&¦u­8rî9,¾D¢±|UNj݆á%,2d¶'uÄÉ£èëà?jÐuèÞ®À1¿UC ²íƒl3QDzŒõòfp7n»ìÔU k„Ïçìýr«hBŸzÛnÅpº “¾ÎYá§y‡‰?}›á0åiWXœç
+Â4Áh`u6H åjmgw' ­xù8Ï™bÅéÔò6„L?d%ŵȷ¥êק7éÍâþÔ÷ëyid.ã¥P»ðv…làæ}n1E
+¯`Šî;5¼Ü@áñ$ ¡íšÛÇnE?¹Ñ?q/íÕãÐ U; øï¬aú½(Å%‡È¯
+˜ l»
+I+áœîh–(ÊÀ¤Z¼½‡Àƒ‰À±³ãExÝ¢äkG4pÞ°U©^\§þT‹~Õµ¬Ì ‡ÔW‰Eá
+n_ù©mÉ°¾„‘kYËÌ.µ^£Sóǯ»øš¬P9[M ®0Hk[ºB¸’É“5ÿ5K¬ÄÎ^Ž´·ĉ­¦³£(²Æ©.²ƒ­Ó• ¡ÙœŽ¿îhÚËAò`~.6¦ÏsX„RѪ?LêÛyÿ~‹“*[ †–’³Ó ù: ­Kþâ_Ó¨l,ãq¶MË6fIˆÄ-l]xeþ‰yÀ€}¦ÆÂk±>MrÐ7-Ž‘ùãuû_W dèö&WÔþú#†¡?”°*t£Šhf$:‹ÚÕ$e€QÏÉ;Ew reœ 0¸ å鲞ªª{wSƒÂP’¹J¨1¨} Ѩ®œzs?TYkÌâ¨æ«;%D DÇp¢«$‹ ƒáÐbœ%2öRÇN:$œWÁðÖý).Œ*ãT·‘j?VkóÖ®£ND«µÆ±(àÉn¼wVV.ÁÚƒ*5w7æÆÏjeþ¿“™˜pŠEó¹ÉZ‡-³
+¶E• „
+.eƒ„ÆÁfyøtá"Â{ò§Ö‚`ž OJ6E²a+•. 7ØPñh-6%@uÚÄ—$ÌA¼êf/š™5É0ÿ£"/ËØË2lG`ÖoŠLYÐöMVä¹€ò½…äOØù
+v6Þä'gËÃFˆŽÇ8n%›çðŠ¡GúÝs ‚ZÇÎq•éÈû\U*X1>DAØ2 “j ª`¸0ÆÂi™ßV¶èïkGwß• Wìc{( ˜Âh1z±?õýzýË‚Y¸Òw 춟ø÷ï¬é<¾Ö‚GSÝ…j„¶ÕGKÒaÎ*€˜°b%°Qyr—"A(s5(2­0¶øƒ§„6ÃÓqž£@QPÜe@)n<4¹ÈU«ž3¶ø gQ›–„80fÔ•ƒ(&íé(ù˜Á`{ ‹{–\¨.
+èípÂÝõnîO*)ˆVžû EÂ7x7öñ½JeW¶\K\e¸å˜©\8¥Y|[¿f‰ôÜðÃ*ù‘cû°/%O´ô¥èÏC `I•Ò@ÊÇCQ nÑC`×7”YSÂËf°•?Ó K³&P "t-›A/e:Ó(u#Yf9~¤¯cº(€¬·+üuýí²W­)×&®b ëxTâSŽgÅšC-¹³4N¹Ñ…®¢h‘eˆW ïÁUóïG2ø{a¾²5PRœs*‹J$’ïkà&ªnJ™ü’[…)ÀÆÍ—KZÐVœ¢ÿ3^&ÉqÜ@=î t`ÖòÒ·ð–ºÿÖ€
+µŽ™D™?ÿðõPtŸåuaP7ïÃg‘VÃj=gÚX\waÝE>¯›²=6ô|uˆ„f£aŒ´à”x|ùÍ¿.ö‡$ü{ûøÁ‘#aÃ_±œ Ã~tÊ»398n8°ˆO{pî¢ý"Qn0´•°íèÝ hcuÄ6H›(³gk5ùw…ק`®…õ ¢A­Ãéò<ó’díëÊ#÷Ȳ•
+˜7áè"j¾¨âV„J¬ dã°X€ë°t„wù2øY·îÁV\Öáv~Y{/;R׬‚Tôj—ûgÐ]ýºÏ^ô]^¿>ÑàoÆ43¬¯v98c¨u€W<aŽ·”vd„ðªì1cV ô< À³Ó/†aÞé,YÛ®QþˆÝ(!¼d½`KÖVÕ/I –yŽ„zÌÚÏŠÈpŸ¢õ±Šº] Å„$ ÉТ—Êþšïæô€†žŠþ}(ÊÙ#ŸI<6{qF1‘ x¯…
+6:Ï
+à Π̉²cÎ*³§“  `çüâ_ZÃx³RÓÆ3£]ͧ3ûF¿³8“Icêi&¿Ó95+šë6
+¼R92KúŒ`×nSZêÒr4é%ÝD/ª74w…K.¨ÈÏQÁJøÌxçÁ‚¡Tå¼°Š+ÃF‹¡[Ñ»Cý÷lj5>«!‘áF|Ð,\lE)j¤]Y+A°9¡sÌEÍ’ýFnŒ.±ò0]vÚµ¦< ‘.P4}Z;š‘«”mßš$ÒpÞ¢a¶ ’ô,á"îÐW{+1\ ÃR[+—ìç€,žê´þv›.\T¡ÎpQXs–„Tg¯‚I“Òi/î‚ RËÉD_©v”`•!âO±‡Ó>ÎýÄÎÁ&Â;ð0'-`x¶ua-¯+{¨›eºÔ”“H$885X$ë0z—5›ÓÚ~§Á¿Ñ©˜—2÷žc½ Imˆbe:y²®Ãùy œ0K˜8.Š5ΤëV²
+ÂSÄdÒÿõ¥{ùHTÊ;Ûºñý2ðfQI>?<j
+׬$âŸ
+¶@%—¼"]Àô¹ôPâe¾R`‹cº>õ^D4@\‹K0œ¯vÝâÂ}‹MäÆKÏb˜ísËj;H
+“El¥„«}AÄ"Îcms`Dþ.Îsð Xhå¢vLD”@}h_k§ðu/YÄÌSüš8¯z±¶X /eX²Ó,éD’h†½J|§B‡êú”öÂ'm!NB
+yeèó™ìˆÄÒ k±âýä—áXrK_¿l{ °xz|v<}=Áù4¾tuÏö·Q ÷= €5ÂÉVÉžÍöiIQr–dÅEÀ
+²ÍåQP 
+${ƒØêÓ!ÖÎ}•"ÜÿTra¢VÒ
+­¸šuÿ,÷ÐLéKÌ T›-«Ð¿Lü#.ÞÓ.i¯ 8…’pK·³ê×ÀÖ²!œÂ?mx s Ëä0ß|¾úIŽ¬1*â¿z(ú²"äQëj“„±Á¨¯q Jä?„2Ûu€y@²3­C«aVÍ/@ èˆ3ýèþ¡Å<=Ð~‹líxnƒ7Ôà| C<Ç’Ãmšo%‹‡½¼
+tíÎ6‘”b·Á"á‡<Ta%ž
+ݛ٧?ÖÑJëÀ@¼âQ Çàvü
+'í"§B¦ ¨ê•
+(ËDÂ…D3KqFô³Çaµ3²½^âl^“u h%yÀÿ)/“¬Jr%ˆ® öÀ
+8ê›1Sv‘SØÿ´®…\ž¤ ÉÁ¯œännM|9­Õ!ÆüuA¿édy͸¦F¦Í!ßèä¡fÕIJ8 7í¹ÊY'Á8]¬LeŠÎ¦“Ð$^?ˆÂó4ö‹NŠ$*2…áug_*èÀ’æP-t’"\ Påö;8x§©²ÐGTIAo=XôõF'÷¢ƒNžŠVçÏ”””ÏB©WU§”A\)wªR_åý™§sV'˜¤óPT§æI¼Ï·5ù ”Û¬¶¯l߯²çô¤ €{k6ï >”ìBù—•ù&”…_tÑ ^ì4÷Ï›¢U(Ë+ÿÁsJŠg¡”Á/z¬ŸÆfÊ"'Ñ¢ÒÏBYyÑVÏ¡ž…ò0øM(1~™]¾ûÎEˆˆC çËÐXéÒÜýÝ£ ßG Èñ¬“”pI"ä’Ž2©
+™1XËœÔ*“{Ñ.“§šE&ua„,Êi=„àY&õðS¯L¢MnZeRý¨2ízÓ“Lj
+ôAÿòÑŸåíÀ•»Ln¨Ø¿#lu‡ ÆžãY&@‰bØÐ[¼“I°ž«+öÓD,2ÉÆp8L±â,“ÛRdòoëyÉd
+\Â=0VŽóeߣéµÅé•G´œ‰±Ü„ܽ(óXžï1p¥~?·}üæNvõ
+øAâ—÷‚?£ƒ˜C6ŸÅ.íæ#ª–}6…/õøÊw†—Ó–˾gµ èî“=!uqî}È‘7ÔÌôíQ‘­ñ˜äA®;5ЙÓÃ0±ßRLx­ô%6Ÿ Ë»ïÀ°-ž¬Ï\
+µN݃·Ê5oGeî¼-;?CÆ¥ ay”vƒv–Ò¡ÄŽBmYèàqÔöÚ3£¶Ûˆ† ˆå‡Wñ3l‹œ‹¿ëN—q—©ëw-^+ž&5Zj°‰!HÜÙí—ÓU(5»*9>=I¦ãؘXý£{Ï­Yðuj0\Æ!ÐB—?j)±£–Ÿ¾³Àæt›~çW-0>ug[†½Ç?/Õ›vïÊFóU05d¸8÷æ»™þ¼©ÁÛbîñCñrÊ”È:WÜò Ãõ…®ÆqíÂ-;”ì ïrp ÆIÑᨠÉ\Í/¢*Ø@$µ§6¾“.YÉæKWe¦€Nø3Çßè;5L³AïœÓ_Á\S'šÔ™š<4ÁèÒ8*4[”—uµ*úÐwÚ)Ï^ÄwÙ'œ>Dð?þ;Åõ*ê])ÑúÛÎÚw±ƒE°Þªn]ý(!§$6`%öðĹa ­ß}ö@íYšd·áÅ\%ñŒl·áþ¤Æ†*ãUN'{Œ?ͱ“|`Fø¹î‚½­“›#5ÖÃI溰°[]“(íå=²×Ð_;Gýˆa)hϯà
+ËÔa<DÏ›¢€HóY4<\`F†Å¼8 ©ùpí¦tXÃ(irZ‘tàÍ_ó9Á$UÆ<oî˜qÈ4ܾü4_‰ÅÆcjÚ
+9‹]—p¶qÁ!ã‰R
+ÿ{hwŸqbâ–-Øë“ \,  áÆ@Fspn8†n[vÂçé£ V|[%ùºF†à¹)2Kyõ3·¿™ž¤¼‚%<X„ÏQƒwlÜ€ûB¢Ê®]»‹Ø™±| ”²"2ýΗ'ä˜P«}„öÆØÙá8%±ø Û@CÌ7åPj‘ú» sB|…ÉŠÞµÑø­4
+Š[-ÔÀ,‡ùÎñ8—}‰ê±™R<òä
+×»)‘Ûàžèð8i­‘à%Y"mÇ(gŠÿÊi(¥°_ç»q“`+öi›ŽtÝÏ“”¤øTlse0’RðÈÐÀyD¤=ƒK̮݀îGä¾}Y¤+Aœ"ÉrH$rùÜ'O~PÄ RÊ(‰ -ç(,¸á½8‘ÍÞJÐPL^³;Iá HW¿!=ã…XWëÝIAá'éÒõLKàKÂØÿÄ\s¤³LmYäôTÙî¤)MzSbp\
+m½Ùw püŽâg<¾œseQÐô™’<ù§ÝµìÅ¿™»lø˜w°Ð×~Ng«9…<Љyã#Uퟩr 2Œ3x1Ì@¢kϦG‰fɵ•`¢Â~ÓmÉ mQ³‰ò Î(oDsmî
+CI‰d
+Jn‡Ž%`S™Ðä+YlXŠp@tŒU§ÉÁbC”ø>4ZjbvŒíÑ­{0£é5<&ÄY{¢b×F 2êIwâoÓCX€}Ę½è»²~üF~/7Šò¢yHhrƒs0ÌØ°]!A?Tz+bf4š½¥ \ü}aÑqÙÍüEO‹ñ~sÜ/ŠÓùqST«“ŽTj.°Q’af|[ˆõ(aü]? Ó8ÑG|=rØLôî/(“•rž !!c6!æéSžÝUx•ueåAûÁ]ÙAØÜÙȇ›¯m?ÛT¦_” fåŠvóå4‡å¶oÇapÆu®|Z¾¢yË]:rÏfþ©$=íl‡hÞÿŒ—I–¹ „¯¢Ôã<¬µõ-¼-Ýë/H0«ÿL¤[¯Ýý¬*ˆLБ,.Ñ)±‡#/ØU|]} ŸI­¨Tˆ¿ÏÇ0Ð ¿lAÄ9L,/ÏI²$Ÿmž<—³pË?¶¨A˜sÑèv .«Èô3n€4=ö—Šg*zyPÂÌFëÿUÑsýœëþ¢èF þfÝŠ~Ân`çöÿ®(â†]jF×{:ž¤y ¦ Äw•|íéGÜ ü œTV*h§àžw)"\Ü$ö³¸yW%îàüM7ïRØ‚åì/£}=¯>™¢‡üt¸‰WÉ’B,I–¬‚¦H“ë5ÝÄK‰´µoí¿Gâ¥(ËïË‹Ôè&^•dàÆŒnâõJî‰×«¹%^•0FƒÀKÿ*ˆ²ý‡¶ï‰WEXg;—é&^õfì
+KCdq3/%øq²UÀhe¸g^·èžEþU€wæ •5'
+u03ÍBĂq`d·N;E•­1¤¼M:X^¤œîò ó %ñÕô’iVè*%¡›AúÁ[Wº0Ú©x$,…Hîœ# ”qeÓç>G܆Éwgõ+rnâÇ‘¢‡Ë+°+…°dóŠÚüØ ¼ºÞTV惄â˜ä¶¡Wx¼|(¨£®0y±°W&>Û¹†>ŽÂùß.¸•ÅÇyo›]¾pftg­„byd꧄k°å9Sªº„XðºsSZÙj2Y,
+ †Û›j‰<î¼ècš6«¥«}=Ò-2g¯'ËñL #±W±sbHÛçœ
+‘< í-èÉˬ„ôvŽ8lE~nCaEÈ,Y ³o_y1 X¥Áœ]‡¹©†~©ìL¿x*Èt…§_»ŠDSøIñeñQr³&¿mwÕ¨$£ï}¿E¸c‚
+ð_l{Ùy™Š:ÍQ±Û å! [L†Ç±üçˆ}œ²"@´^M. KÈ,Ç¥Œ`Sɺ[^½LãÌáæ<œq>üKÅ
+ÇŒ¸¦ør
+°Ö4b†4}+u£ÇÏ9…ævÌÀÍÖQ®Cƒ¨pµÎIß’D;Ô—’8˜r–t´y]u/Jʵ’ÆbçÐr8K|cû Åfô…±ØUp¸!^”Ãú£¤´µ‹I°\OÔ«æ‚M‡ÁSƒá|࿱Ié˜gÌ`æórˆ¦ ‹%h^èéÐö†ñ·ÙÚ™SÃb²/˜n+‚_I9ƒ šó>Áb0UæèºÈ÷(5ô±WºD#±4G}tÄø͵u4
+Aå7fƒ†,.C:¸FI) KÓÌÂÒ¶À„£šqhz#Ú0ñ†¸¢*'ݶ)`Où¸nVŠÏí Œ¥Îi:EßV„ÿoŒ³lyÆ#‰
+iÚM¸3½
+C&Î]B÷hú,«aÿÝ7ag±I|sÎVD$ö„ »©ÔP$ÆÝn‚uåvi7I‚à.åI§ÄnbÅyxšùíœHþDõú·ÒLŠööñþ(æ•ñIoÍ%¸$ÐÜþ=‰g_
+¶¤i½™ø¹ç^\¼­¯¿ü/™CÎ
+ÛY_^Ä¢UmXÃ?\/ºwˆ'¹âYûå÷ƒ×¸™Øó2§»ê>îç1Ì<?æ=ïQw?{ó؃*ñ2{1ÿm~¶. Ð
+f|£jNÉÁ_ŸÀ «óv %·( Ø« )¶¥Ák©$âlcd!y„½J¤ÁɯÃrü"¹,Š>N>ìW"NMsŸ#˜ð× $ûÜ4+„¯!¹%·tûÛöW&/ÖíÎnEß/E$
+¸“­.¹Ø’óçeÓöýfY,úfKƒfá©ZêįDbÐÔ
+׬}=»°4ëmíø ŸJF
+ÖÝP1Ðà,Õ—*ë ÷›îE$H_£Ùú2Db j}ˆBD‹òaQªam€¹ªtÕx"š/ñkã4/2U¢n©s‘5šJÂ5@ 0©Ö¹ÌÃIì&:‡hí+š=RQæÅlyèjkç*X4Ñ6Ó¡QELâ sÞ@40ã# ck´ŽÍ‹þöB)e¥6ës
+U
+ÒV,ÂÚ¶áXAä«T9y;
+mè…E*õî3°z
+M’Öï.ñ"á÷ÉžÞ£ð«Ž6ë]md˜ÂØó)3Ý’Èö¡¨×´äôüt1ýÈÞVÙ‡{—×°ç>é=3º¥yÍž^}_¬Û¦ˆti÷õ;—¡¹^æ2|Þ£.C|-ÎeªÌ
+"[ôÿ;,¤Â#{‚ŽüßîD*X¤Ÿˆ 1ì0fîÖOŒ ëƒtÅâ@þØsÁ/r¬ˆy÷Vß™XØyb6AÂ¶Í QË9ˆ5;âÔ([Ó9 s™‡_ÒI¸+Ö²)èìÏpn_²>OÚ@­s+Äþð†Š‹a.¬™Ù¢ Èë8”žZÈü´=Ï'˜Jì"ä0–Mûv{ý|²£¶Ê%öEÎì™|! 4(ËÆìU._$Uh^n'; oä=/+¿D±nêcw\bÖðÊ}˜Ûÿ Ã~ñåd³OÁ/xý|R%ó·ý
+üû×oÿÁÃõoahGćk-G“ßYRÍ‚«®1⃊9Ú‚m«"öj:[ ¤#XÞ ö›]"øؤP©ªGƒ(…)òU9ð}¶ £f}ÐQµRaê'x㟿|LV”cÄô¨^¥'‘âÀ©“àúÿê­/ràÅu?ÌIÓ
+s‡¤r͇R‘[r[ÿゼaT‡ëRm¢¡Ä¤øÓ^ôЄÇëÖŽbeÈá¼z¤TMv`A˜RŠ'딊­`Œ <¬-HÂ<‘£RŸñúþ±ca!3n½)èú¨Ã‹<i·ûòu‰0ÖòFà<ÐÉÊ/6§Ô
+ ™„I,³ÚV)¸<ÛXs+÷ÁŠÙ`0µ¬ýl2I›uí)+Äõx¦Pæ-‰ÀBÞß¡ ðôÕO5ŽY–nHƒÄežÊ™v?‚)ÓJCnìŠß‡²²Õ‚–àZh¦Øað>•T¤Ü*2hF¢‚E,{÷tÖ"b0ŽÎ@ز)–x¾€£"rY§dW#/bEÆF6 Êðzêõó ²Žê_ %h¬NÛºwýN×òCEðZ>·‰"h*ßcÚZ¹òS°•`^À➣cì õ,:Äf!+D’T¡&\â ;ÿ [M=ÓEÀ™„Ú脉çóBË?æâšÖuS`­åM;‘hØû]ó†à±êc4ˆH¡Ë ó®ólˆ ÃE<õ
+®ÂÒ#»m†Ù‹Ê_d%Wô®½tÄS3AÖoaÞ}‡MÍA!$™?C÷Y ?ÉRƒÚaíæQ©@2h1ìñ!¸ë‹7+x]\ç䌆¬˜÷x[»‚X e³4Ývó²Ào ÛÊGxÁ»Ø¥û,8j¬=ŸÎ–u9…­W܇ÃT/£%Ü–ûD§å§Êy8Õ¾SDA4»n;þF’|mèÄF‡#RQªêG³ƒ³d{ëÓ“Kt.J"² Ü+êUŽMDm{jìâsŽÕ"¡°0ª4lŽ4lCãÆÍã6ù(&
+8›ø:ƒµjÉ’ÉÞ†È5eYtyJ»ŸPÕ62sšË<í:EÜ[ôï®ÑËË&¦ÒÒQyËPÊzÊÕ«Q+;‹cnDï¯ÅÓ4¶,êdµbÙefEYN$ñº¹5|Œ
+õŽVÚ—¤<C
+厞½@ñbSfrˆpCðHÕ˜lz0H8+º?q¿b¼!T‰ic|¾÷ÃnŒ)l~ò8ãbä™
+f9Víí† ³š™8m+ýí8B-؆°1<UåÉÄû¨Þ ‘ïÃot©V4Õ¬ž%EŽ¨)£Áòn½ªÔNÄNBmPµÆ•ûÝwpÀ5Tó(Ò;1]r-“Ú<9ŽAšä?àyÔÒ( æâ‡KmmÖ…lõëŒÜ‚­½)‹Dz´ÐN²×ìì½òäõ¼, I{HzýcÐÛþý|²¤ßÚåÇï‚f=¡!jœMý_‚^ëœ"òÝÎc: aÕ¡9s<óÑeøj¨Bm{"íYµåLø•gÚœ%ö¦Î̤™PêQú¬¸¿Ã}ßÀÇì¨ÉŠâAyŽtŽ_ƃ,`…bfÜ=YÇ÷¬lwrí èû„õå«\'&‹¬yñmc^!fß‹­ñƒGا„ÒT '7oö6xÉFÒTLÕé;H6ibˆ#εû{˜×èc¼½ð»õ7MÕ‡çx#QæLtܵÍGÃúG´.Þ8yõLU¡ô3ÚWÖ¡ñƒDÄF+lîÊ‹¹(~'›Ù_“jP¸ÐqàPá’£û" pŠ¦A(3N™Q{<Hù"Р(eÜ©HT~$mÕ ºÎ¢®œ[ }©>Éðt¬+{>kuäÒC’áÁÉF_.iCUÕ•%<-tï©%‡+4ÒÒÖnL8O Á‹Z¼Ó˜5
+«Õò†íX%¸ªœdÁ†°•˜™ù<J´E3Íö,2õ¥>Û
+`™ sš%†$G~Ó‘{™ÇTåy b#hJ
+†d®\ð£ÂúmÍS>Ê‹mp·ç~©HÜéìâñ‡Æ¢Çסդ‰ìŠH‡è Ÿù9‹/ŠÚ=÷:ˆK©òr¯
+ü¬r=&Ó^·7lJ…²¦Ý
+FÚëPñ¸þHc؉„ w¢‡´Ñï HDœ™{‡6€?ÒOÔö;Áéf…jðJéF†è€CË6>= ò9œ›o„oI&AÊ0qCÆEÎ{#H½:õOªs…Ñ$H$˜™?_ÈOm‰EÒ樃 e>Ž r1Ý–!]w•B“ å„"E.#× @×e;á!í6H$ü}¥¶ñ&@žh
+$ž‰kÉè–½ËíÅ©ô`Ûôø"zç-PÕ
+H
+W5/Ò ÈD˜ÁÖ*!âÏNØW°¢4Ü žñ e33´išA9&‰?D_S$\g²C4$2qZ:/ìOÃ]9Pû”m•µ^A×à¹Ê­bÔû~¨åNYƒ Á3pn7ÏŽ‚"+9‰ÝóT`ÄN„éE¼–„xàjø”S ï/¢ wxRÅ(IS÷z²1í”+
+jJ|?ºÇshÁÍß4XŸÒtk¤dÒ..å×å`79
+Êz!o× È'L¹qGò—½ïBÏ×J¹þzT-\‰~HÃü’Z°‘äk†”Î0)n®„nHvý$­w:Ÿë4ê…;ºyß#÷ÊãŒ=…)©Œ¹ÒÚ;x¡qŽæ$ˆeøq;4utž´Ã=­uhY¢‹ox‘ÇãEâ § xÀý¥‡&ù
+èXsëôÒN’òW¡X’õ%κ’í­ÝÖy;vŒ#à5†´¥råcî5¼1DrdGá–ô§aÞ LáZ+›CÓ×:P?ƒ'šÖ€7jD}F_$=îF騧þ] CÌf´ïÑìMôu=Þó¬u¶D ¡â u500Òø‰ïëvhÿ
+ŸÚ‹äÕ6ÿK¯Oéµù“æ˼Â6ÀXñäËZVaZrLl¯x×ux-í™_¨ *1-9·É‡C3t=ì¶7hÂ(ÓN~:9Åà0•2#‹SyÍ£Ÿ†ómÚ ¯×– Çe¨è§$RlL‰Á¯*ÝYh=‡1ü7[¨ƒÏ³.u]²@Oùý!q?µRvÃdnœlâ¾ØŠh2/C&•–U¼kò(ýBæ`kÛ@.JBfµ¼ÃQI]
+0Vö§àcl‰ú^¦^…4tÃ^§„uë@¶wê–¡kÜÛ9EßÃõ듈
+0cM€ Il†ûÿžF@J² ã9 ¤J ó;ª=£M¾‡YªãŒkem\;¢Ïx&b¼NMÉ8·ŠÍÎQ˜L;M8%M^5ö•4%<N
+H‰ •gWš …÷ã̉™Ñ˜Óm±ÆG$0"Š¥#Ò{Çh4QÓ4vÅ‚ €ˆ
+"Á¨("¼‚=Ùdg?ìÿXÂsî}î¦äZ[ª¯˜”¨xKâŠwŠU²;Ã{'ÈOæxÈ3Gkó霜ºÜ…Jdå]˜’Ǭ b‡MBôÙbkóÙâ+床”ìÓ
+ª¬oš€%ÑÝÓ9ÛAJ2¼Á't³Ê¶´csŽæx(¯yÏ3ZywCU“¾3JÎö ã’öõ”× &uR^3Ì{2°qGVvåfô†ãUv¬û}ñíLúæ
+J8±6”­uVÞ\iƒÄ®´W^Yl}ñçj7ònÈÌ©VÂn•'^ú‡òèç=€p0/©÷cR×z¡7—ÞB.­|D^ÿ2DNY£=ýÈ.¸ô–úëÞ$1ˆàÓŒ¢€Ÿ·>„H÷kj3£J~tž µ}"ähÅðؽIvé‘•Z|h©qæàÎVÄT¯†ý|î &A#(¿Ð q;zN±GKËuÖ%ª…WUÐí€
+9YâsNœâf÷8©pT
+úë#Wþ½­lù¾!åûtøg–VPŒãCY®óôÐÖPº<„Ï$€ü$GgÜýú¥µï»[Ùöm±¾2j%æ¯UÝZê‡ß_ÇfyôB˜gä×ðK
+ Î÷¨øíQ8l"×ð]jn,¾.¹l’åÿÒGÉø§E ‰ ëÕÛc¼wOýãc‡‚Yb
+ýSü2ß8%Í7‚}x4+#ïè¸{'ò¶š]øÓEˆ Ls+¶Îv Œçkƒ¤Ü¥wø¤ÕN|ê¾E„ ™%Ï(+/dc£¶FþμØE*“V¦;>â‹¿»ä²¿Ý2É©O¦éë*B²W/Á»Æ¤4×
+ðE57í_»#ðGß–™U?¾H¸+ƒä”Õn|btFF<[lVœ­¼”]ác÷gÉ…[#ˆž>dÂñ¼œ4‰êu
+èu¿–U4 ðÁ."bžgI+š{ I4¿.¸ãì*¾zlg ŽUîîªÏ þù¡ULX¤$`VFÛtk.éSCþ¯9ìÚ®NZ³9BÍ°wÀ¯›šÊâ]ŸPÌ\øU„ñëÙe†ÖŠkæx*`–â<*zŽg öŽ½½üê¤0ïòÊ{è­3; uº*á, òu
+øõ€ž[4ÓÀÞQl¾g°:3d æ¸úëî›[Êâ]5³×+,½9¢„'mNŠ1€õ%-l!Öûñi“|Pì//°0 Ñ!îÈ)¦æY{ lØÉg+bãSNbFxš‰ÜP‘òÝÃŒ¼ùnZÖ¶ƒ9sIù[£µYîHlÔÎ#í;•Ê¨«¹moQÊZ7HA=5/d¨/ˆšé¥¡v™«‡òØ3FJ_鬈 ¨ñ©žBÊ|+ø¢ó]iÌÖ0.ùp^NäTŸž_ž‘ÖnS’ý”ÌqL¢½ü»¥éùo–ÆÒËÛC¤ÂSÇ«Æ]«0
+*"!Ì?LË&y°C«„à×±‹W>aï-½EÄïjxåë*&xþ5üòZWÝÃs¿RÜpiKopI[}ä´}ƒ jgŒþÌ3@xtìRÓÔ¼½)⟟2sgYÐãY0JÑæ7è;‹¨”ƒÐÓ mœJÀÌlS³½c„̈‰ ê˜ ÏùžåefEøm-§ÜÜ[z_çÓP2¶ÔÄäϽ˜ûövø£yQݶ† ÞT3 ‚ÈáSŸó“Ÿ
+â‘ÐjŽå@o‹°OB˜¢S ¶,eÀÕ}×”OÚ[Ò -ÓÖé¦_Ò­3 7|+˜¢%¥á³žÚz¶Í¥ø¬ær Ë%ÄlƒÏëù•iŽ%Ø]¯²Í#ïy…„§Z¥™€ÝÐŽ@.†ehDÆG¤˜W€/šîÉ?7»wNN+NóÌ·gÆtœîý™Î,ëd˯±ïçì²ðÉ-"$¦Æ–ÛíÙ³€÷KØëÖitŽ¼¿î±W‘02§:fÛ©† ¬÷:—Û²v'ë.臊 )ñ5€'5¨'·-3½ù9¹:¢"OuŒ¶€óÌ)†å}”ëß ½LYøü ’P~´Ž~â¢AÞåžÜ„ŠÖ”Ôqñaf| »v°Œ.ˆ™&QãÛ•Sãû³È¡öD‰«8Q’ q Ü›ôö­T¾u©·"¹5Äô¯"@^!<Û½Ø|ópº%#ª¸m—C8Ùâ’•· ¯ºóƒr>)¤à¢>™1-¦Ü¶ØpË%„fE”ØêÔ›žÜØ[Ääùå¤êoÜþä}ªÃT¤­yÛïªnê_Wü{ç]óµo‡Ü¿ŠÔªk¹¹?‹Í?5rqƒ|b aÂrl©WÔú0²Ñó,,#”…<ÎïåuÅ(Q³P1=×Q¡gÛtìg3‡î‘J÷P91ød{|2¨ Mª9–‘kB
+r}BßûË܇ŒiúšÍ3÷̳íÙ1£[Ê*ûõ}Ûƒs扆›Ž%øÝ SqºÍÄ'Ì<žè•ÝyÒÓïzm±1!5®Ü/ítÙ’å^Õù5ä±–Oû´Êls —t“øGöµ¡v×
+­ôLß×Ü@íÏ´Üõn ŠŽuxpÂ:ôêÄ20h| ÏÑŒÂn
+iˆ°iýIŒ+òɉ•Ac<±Ç¡»ÄäŽ%b‘\h
+pPÀG51Àb&,j¤Â<âgäEkfT
+ÍŠ+¢J\YTÝòK¨U!5µ9¾ËÂuDpP~ê]Þv̯x–;ïGe<tL5ÁõKûóä’O"
+ا Tz$è|û"Û6¿­©½¤®H \—0r ®Urù}g†ize™\–6ßûââî«Ö»ÁU|‰v¸)]AÿÓþ®-ëOã; ÄTZçënxÖ®ûî%l}_j™ëmמjû:Û4Û|GŽuªúÂÇ ðÏ^qWNHÖó$®!TG5t¸WFƒ8%´*Ÿ’×аڼëÐáBË Å`YÚ&¿ôª]ÐUVR‘)ó ; À”^
+¬Ku|ÈϺáçç\èùÐ2 ½×Ïv˜„ˆ†ÑÕ tŸ˜88Ûbk¶q¤ê¼‘WwQ?\vÞ:Y™žP¡+Ouƒ¸½™Þ<ÍèËóÀýîG7 UG"(¸Ž*ôˆÛÿã¡rƒrRE@ÉiÓN¡ó_!Ÿý”«¿ú‰nbÊ€7<J±`ÛBc†}ü‹o­ôç—øÂ%éÈ9’!žù7ÅasNPÖ]šÚéç~sp'ýjÜKý¡W½b8¢¥uEMÔŽ°Òâ^Ç<?–jÚ¾¶ÔG6õHÉ"¯0ÓU<XQLÍÁ9–»óÌS+s°;Iûƒ°ùôË>»È¥I ܹNªHZG'½R*Ôò¾=SË«¼0Þ˜þ÷5RÁ¹ÃÙPbodÊ¡b©ÆàW÷fQy10#™X丑EŽYĘžÕã<(¨¦@Ïv‡‡\ëÔÚ#¡<eéëþzÀìûv8ü:¹ËaþŸ‚Ñt âÔ®Ž÷ZÜF§ˆV•ÜfcÚã»CÃ_ìcË_ã’Ó}ǯ%78×)•Ç›#ý!5oð»öÏfjOrŸwKºŠ¢®±-,eà¹
+e¾5&Ì-ŸslN¹e<ŒCD)Ùÿ€Èü8¾°A-þ‡Š]ò¯„ª·6ù‘ÏuJF1fAEÀ,·-Ñ^¸W(ÏÝ˸ÍHåÅYÄÿé˜àËÇKè'q5ù¥CŒ+°¯ôÕ«P@–Æ«/½Aeþð®ç·ôüÇ?%-,bÀз
+0yna÷£‚TdXÞ‘´ ’Ãsw¦Ê¯Ø™ àAz>Í&"~ZAç¹D„*·˜XÓ¾°¨g[ ¬_„,0W^¶Íµf|°!üÚÞÛŽœ¸šÞúÉâ:b­s¹%ßü¦<ýàäf
+LŽ¾µà“…Ûš@^ù(Ã¥Öù¬ø
+YÄ—»%ÐÌ­Ñúó–áÚ3%³vß$ì ,
+,ËöW;H_Âä7p~;C::bGyÖJÃZzCÄÈÅmÏŠãFZãÁ&ˆ
+óÔ§ Áv¤LèÆ;ÌuÇ$ò×”™ ÿŸ»æp‹CIZXˆ¯NïË&@K¨ kÓ1þyR•é%ìü–¤ån|•CHØØÄè
+’°2°#šÜä÷|r„I+‡30‘®9ܸ@}5·þ»ýl,iyú4¬ïcûUÌ
+¿›½ñæÉÙ Ú#”KG–ø·ÿìzÍL™»Aðèã­Ð#§–ìʱ·"RÔµÀ,4këUÉ?⼿§ÖxäÀr/Õþž]²ýž^èƒg&È»ß-Tèï6Å¡DÝÒ ×ž·¾†_©{‰^y7ä×
+ïî沨ü¸m¤)#ª~Æ‹ªGž”Œ{ªíîÎ1'¨æÀºnòG9ðÐÐý·APýc@‚»_ WîXTK©Ú'äïª-ëïŽqB¶ëì‚OŠ½×±A7âS÷LB¶_ͬñHQY‘eby|¥“¸«¦W9'ê.:ÇþZ€eYu1ãÀ`Ò<2^î"T­y‡v6)µÎÁ—h®ÙÖ<‚úÀ.!ß±OQïí.²b.ί¤–î鹄ŠZ”csÀÝ»¼ú¼øbPÍŠki•{zaGDß/œê(=ªï‡\Û‘PGU´†ƒJSÊHƒ€ÞT±þqÉ-c5†—€ËxGQýO‹½° š§L7ØÅ)M;*a`5;fð9+¯Wí8o}} s')mãRöVH•»óć<η=âÏ›=¢Í)b‘¦šÖð)ã&.Þ¯À,t—žZì-8êW¢ïnñÁwa4L0ËŽÉ»ª~™]7¼ÜVä”4_sN 2ݳ¸› # ‘²õtoOîØFЙÑ
+ zíåãÓ‹‚{Gô¢’¬`¯|œlÌ
+<e-7=ÓÏÆ à¼TŸ¶Œ•Ð¾xð/û"7¶,üó]HÏ£xWÅm-qaa5­:ªaC]3ø{2Þ½¿M®ü% m-O­
+ùc/°#ë¨ ‚ßå¤ïâêÞ"©l}äáYSÉ? mE!-££®¬¶å%u\4˜ÉØÿS\ž_i¦iÿºûiÏž9ÙrÎæÃN6lj‰&nŠËj,Qƒ U©R^x_ªŠh²)'}¢±Œ
+J/¢ A@z[Ü™ÿc_ÿç9羯ë¾~×ñ—µÒ;b†‘ºÍwmW쯯Ä5¸æ¼cBÓÑC*rÑbðNÞ0ÒzlƒH 0”6²ûvÞ÷ÝPAÕßd­´Þ¸…Ý\£ÕÇ5´Ö„†þ8kó›5ª¥5…ÔÔº
+èN›§¥¾%Òƒ¹Á’õ‰–ïfÉ•M¨‡/\"AÁ&áóísÍ‘îÅôF\/ )H÷ögZ¯.2oþq‰}÷JXN‚;Þ³I×,»Öú
+÷õþÚ‘V@ð|A•À¯Äû¹ýïûŸZ®&4ÃÍyÄ8vÈ^WéÍÞ呚ŽdüÄ=&ËZ9ýY¥5¸ÜûãÞÌã?G×iMð„ŒM&‹ÇXûËÜŽ˜N†×Çiq=ˆÊZy„#-±6¬%T‡×Íq“¶p1!¥6mÐq€Njé­E+½çd‹I>TýÛ¨0³ÛŠ®É7jZûÎì@©ã}×5ϧþ›i ³?fRzñ°glß™tùURæÞò(Rÿº§ró-ú¶ã]Ïõ[Ñ´ýâM&áûì×JH-0g—Ê’f:níô+•¾elyÒÂDžzD’ ÿä'Ÿ’õÔô÷½Âœº'¦~õK^žo³ðIåÀ]߇ºo­MW]³ø†#³tÜüµV'ëùÇ™[<ñ[H:sîáK¾:XÔŒÓ]yZv°ÔõÏ”âÜÏfÝKäv…´ùÊ™oâí…ozéÌŽ'u„¦ƒ/è’¤Ä%|TÃG…×
+½Æ=7xÝþ²õ›¸b¸*¡$×çtLıUDÍÃ;Ž+ÍI5³ýÔ!d‡×ˆ÷ÃJ˜'t¤š”™Ö7Ð…M`ä+Ü1’&:¢Ò¶IiÊ&ODÃY P¢ËÂkØÊ W•ÔŒ<9ÞÉ-³=cf#Ï·AxÆä†ä±*¸<P–ÒR›³F6:g”€YË‹×îù‘J¿‚Óu™¥Ñ5nwB…¯ŠÂz8ZBWì}ì»~lsÎœ²×9›˜Ñ0Ã^D&´,Ä‘‚ð0ªÄþöØýô“;ï{oÂÜÒñ°˜ñÂJZ{JkÅÎ!œ;`Æ¡zæÐ !Å@yÊLGÒÿ¤7'¥9›ÌÛ.÷((ï-ýêâRs\JB`#:^ÿ¾œÝ·LOE 2(aâzNoh•XVk³
+g³‘;2òñ!Do¼|³£|,La˱M¥¿w|aÂìÀÿ0í,lD…¼Ÿ¶0ºã—^5Ȥaý³©Ýyê}ÿ*³5»%äf·¸Ä¢SÀ-8! ½É!„uÌx&˜¨U:ß@ #åqLM¬Š©qrBKÎδC:ÛIwå`÷¡‚…øÅ'ýpâ¹¾Etip}sçMç-ÿÂÓêÿ¿}8Ý@ur»k]Ÿ¶ABûGTíÁ"fLsq›Žgʉé…ø€
+BÂï5¹g‰·k¼î Fˆ:ÒJˆI«„3‰)a=ȧ Öx‰ånXÇÞEòCÿ
+«Ñ;µÄÔ¤¼mŒç° ¢g´Âw¹:°Â¨ Éù]!¥ 7 ç4åôÚÐ2¡<³ÁèL˜™O”#U_†Juâ¦?hE5¿ó~F^Oà ¬
+ldÑB눯k¼ Ø›îOÄRßíáÞ"ñ¶ÿg\EÁbÓZKÁHpµ3m7B‹ý%a9¾2·ÉÅE5´ÆˆšRŸ2ý9gðRïg.ˆß¢õCw`Öz’s>›‰;d/ý
+v[a‹OIµùMJÛ… ;BÊHV7舼 ³!¶"¥Á?ˆ©†îXxøÿ9'Ÿ_ا_å­ Ìrm£`øP'Þ[ Þ-c+çQe°Oþ•]g Š–q k’á¬D†Ô£ME»Lš¸(ߺl÷sçU÷LOɪ¸æO;‘·â:>aW \_F2™´°%Ï[™È¤~¸~wùƒVVÿmXÙWQ°³0'n‰$eÒ26ëÄ%gl¬þ˜†ø0¡%Õ. ”ù1åi«€^tI_dlbntìk¸½'["VÖ
+öHzSåœã“Å]¡(bóìËÜ'¦ŸÈåÊileÂ*{žß‘Ê2["vBÏêñ/V;
+œ—­Šñ9Ð
+É`Ì€‚³ôkk¿Nº$ž\yN9žáóV/à¨î‚SÁ+-+¼KÎNZ¸-9³!e \¾–
+<Ö¼öwr}’TYäRþ9pí“)þí¿fMÃy+Ô•ÑA͇°ÿÐ#ç—œ(eæuœÄi³R°=3ð`šF¯KºÄŒòªBuy´¥ø!l`´¦ÈpiU9·
+RvF³žú½~èJpzÙ¥Rí®Œ=º’Ü
+qÖõôUamìÙÁ†LR°B3ó^DÏmŽë¡;»ËBÆA@ù {Jø"c…û?nÉÕ~À[~™<ï•ðÃfñ}ß,|'nâvüg{üÕÇ-õ‹¼ ¡æ—¤Ð¾_&=ÚV<Ú÷Ëe»^5ŠÑkƒóŒ[q³p¸â}ø¼ìQ+AÏ¥0vgTÇnÏ:x¤Êšˆ–]’2/ÿXÃ}“´°h·Dü¬™t%ƒ1ÊNiwI<X¶ úö€÷>zTª¯JUr+ø ÷Ôm¼<¿=ǹ0ÊÈ“Z˜µ<MÙŽ†µ
+Ê1û¬¿ì<±5û"m‚&NcÊÄoß[–ó|ã?úTã%7:œµñ@]L ÇtjV¤LÅ=)=»>m„[Až7ÆáÆ]›ŒyàU‚·T I;BL˜G9i󣑸AN‰êà–¨ŽygóÍÐ…°ºž4òÛÁ¹VÿkrUèíÀ÷)#§)kFzÃsìÚÀÔðåõW¤³·¥âQ)3ÎQaDÇkϳï&Í(9e<
+Lφ¦ Ui=¯9ƒ p%»„±çxô¤ä@i@ïM`·âò®QQÆ*¥úøäÃ5˜X\‚»×QîO9–s(‘•Ì«ñE!îÐ7öto…ºû—"sݧwÞª‚Ó½ú¡¦]°kröqÎ. –—ä¼}ÀJiløf\7t=8I>çzÒòÉúK¹ø"ç^Ê,gº^±[”äKœ„›Î¥ÂûŒÕ–Õ1nå@Ï˶ݞcÜÜœ¢^Í»ÄPX'!­M ·¿6‚VV(ŠKRî,Ùè ±ùî¯Co;¿Üó)b¼îŧíŸÇÌ|.ð²¿,>¬"С¦ø'q§°±¦?ûßÒsž'Ó;@oI:2Q\öŒ %ï
+vÊÈC2eeÂEæ_ïÎ:(Ô…)e…»çÃÄ}£”´ëAY+ò½ÄÝðþÒõ÷ŠOŽ–}Îñ—÷p{#:굈jÎyDtÐI&+Ër44Ë®OYÄôÁGS•u‰$ïôG´ÌÚ- ûVÔÀk)¬é¿íH•¶D¢˜a ¥hG@'º^°Òkÿ€Î÷bZÅ-‡óÇZ1²š#¨6gàíNø=áBÞÊj+,‰©I“”×
+ï…†®{':? N÷)»Øû^NÏá›XvCø(oÏ‹B cÒ²˜„”2Â;‹ìú„íK<¸ŸTqJi¿÷ éÙ˜è;›ª-8…ļ[<”sŽ«âÆg#:5èD€ùŒrêά°që5ãÊÒ§Ö'zN¥ ÂNÀ%`×!¸mÚ½ôoÚyûSÂgà_§ &Þ½”‡ßÒ0ëv
+šÿ|xh•O ÍÒZâF!!iaVTÊ°ÁoÌBÕ!-¯)fõFµÜ†¼…ÛV²sq€{ú3FA'ÈZFΦàîh‘vàÛŽ­úå„žÓ׳îæm9º
+ÞQ/DuPKÖ&…ÊËjUeMù0iâá-š?7?lø x³?ƒÉY¾×”o¶5¬ú46ŵ¢ž
+Èñ’kDä›üSwœ˜Öüis‚\fƒóNtý-¢¼Q²‰v2æž…ŽÖeèѦ\5
+º7ßpj‹žQiÖ!¥‡5Œëi3£!g哲–ÿS\¦OM¤yµµVmmÕÖîlíìT1ã0医«Ž.53Š rÈHä¾ï¤ 7 㢎£²¢“û !ATH9:ÝIçtÜÚÿc_ô»TWçy~¿ï÷ó‘1j5è°¹ŒWIKØÅ„l`ún185_òÈ©y'·5k§Õí.Q.$ôBÞ;¡-†¦~Ž›@'z¬bhä ¥u’«³^f¶Ái?0Q/íxD,8}p× R0#3ónˆ2°®Gàϼzˆ;³ÄÿöOó¤ŠßoÞ¿~îCL{3„„àîhfKaHz¸­/†UÊÄ:³Û¿Øý¡÷‹}·ù02¢4ͶV¾ÑQ®6%Ô÷a¥àCP<”[çRˆÍO$5§í¿Ë»Ô̼GÔWô‰ú²NVÃî ©¤™V“u–ð)¹ ù øÿ„œ[z#i¥Õ.oŠ)%¿¤0j¸ÿ&à³q=·%µ&h’Ós.z}jýÆÙ„‰ôí1è°÷Û¤²$ka$ÍÜƸö#l“L  ¼ë˜CA¹:”6Kq¡EÒÙýzæö–C’AÔ#§Î#ï[Ôƒ)§–w`Õ²â:yëÛÇœjëdÓ§ÑåÞ33»ùà%¯9aUõ¿]¥n¯ŒöﮨH¨UN>г®„öŸ~µ4x¼kvò÷L¢–=“Ÿ´Ì{ÿE—´ÍO"î1qìåpµïNWEÒ(èÀ<£üRxŠYe„7&YOÊ3>‚lŒ©RvùØ*ãÒëÇ„SÀ3Î V% ÌœŽ7H;›†—4H:s616³›ö–¨Õo—hU»ËÌ‹’„94Üø ^}äñôë'¤s¨Õ˜µ3ë3VZãÁÚ@mLÏ©O9&Uˆgj,f`ÖEu~™^ã5§×¤øÄ
+»öæRð~gæWxC-Ú3ŠZ‰ÑÌÆì<⟽‰øÆTñ5N£ÿ.î_áG½WrN/®× ûï±Îî>—u$MzÒÄjÆ\L\ÊĨ/’*Ðõ??^þ$x—øýÞª°Ñ<Ûù àà&Ô)Œ.S«ÓFVâätÂ..iwecŠ¬b"j¶F_rêJ[š±bH3’ۀĨKDË:xxÄ.ê¼JF12¯+…§n‡ÇgC#꣠Zšµ«è˜båÁ½fl¬ö§¤Ói§ˆXA’¨‘ôâ ·d\b2êUqs>í(꿽Š…æÿƒ"ÄÇ×C3™uqWä~ÏWFyõ ÷dÃ_]3ÿÜþWw ÿ›x
+›e]·r¨äá ¡à–“³vÉ Ì-¥7LÄ!%dÝ2RÊ*w$ïÎ9Õ 0TÚ®$më˜?ºú*Ó&A[Ù§d¦@&\cÒ‚W ܓבõÐë'½ö£€>mÏydä¤]œEÒŠÚ !Ø"%fíœæÀîdàfÏ—Àwˆ7p›† ;Æ%¨CËCj!âÜì“Ps^V[Â4øÃpÙ¼ON?
+ÌÞÎzº³¿®ßQAÏÑ»·§ÍôÚ×Køs[N¦Á}ƒ>™ÌG¦ï8U´ý\‹ÒKoÀza[ZϨM†€'±®æì>꜉¯›bËŒï6~ÆU„ná+
+òz>í‡ßôtÆ¥„×m°œŸCBŠ¯ŠÛÞ>×æQztEÖñêIwEôEß׻φÏï-‹›b/”„Ìš|
+ŽM–#ã3¥-•´¼5µh ̯d—‚
+!æSp1б/»eßLùw ÌÉÁ*·6¦cTïë}9§RðÛ–vîoeŠ‚ÖÓóÛ#Où5[WÏÜbÒ­ 6k…mÊaH5ñá¤Ê˜%=«ü« #óòî
+õê÷äGÄÁm‰êˆgwž ^Š,³ðoÌ2:êÓŽ—Á¬gÝœfÄÉm:;pЪÞ5ª’âD ì«;Ký_¿yL¨L<£TåmŒ¶ã‚õ.¢Q¿ÛÖŒƒ
+F°.l$_ȬÑ."f æã‹ÅPÌ3æ–÷çç”ja90"Ä<RbÆμ;d}i›†‘qL*’æ)jWç=™ [¥ÝY§´/ïQP‹¾>öÝw¿í ½âü ß䵿äÜ|ñR2êP2Ó6ù@Â$ïI¼„ˆû:~CøNûçQð­I³:k–à0à©¥ ¥°‘b% äSâ÷ä|ìö´P•6ãOÇt}§sÀŠ¾qMÁ;?avu}µý÷„|¹äöŸìÝz-¼Ì¨Ú3ˆqÀÕ&36%es‘øÍÖbGeÁÉí8ôɇsá@tu¸1òœÙsî•„e\
+₲$ìûF€°…@ö}Ê_2o?œSÏ}?ÏuQ2>Õ!> ~Ø_h»R odm\|Ö15›²Êe1ƒ
+áüò/ ÕGu"|Ú.“äþ̳G2ÕÃýQ=[Øžœ/nKÇO·ùœ#%ô‹k¦ërÞ$
+ 17 "×{çQ×} uAÀÎI#t”Ž8R‘jBjV[Þ
+cóVfwDM¸ç~õè‚ûeË7!%¶*edvDbJ`ß}ð™Ý䙣×8gÐeEr{¦ý²AÖôu²é\0wÁ. Å p÷Ö{T¹û÷Ö²”n°¡à ´…Ö)ƒ*"¦ûâZ)%n|6‘0 ð¾Æ5Óò%ÿÖWÇïû*ó>)e…cÆaðÇæ£nûáÇ‘^ïÿh·Ã
+LEÆÝO[©-Y»ˆ‘»§°16
+¼AžtˆÙþuþàÞ{Fóá"¹: €*JlmHKj (MÅm§¸ùô׌SÄN98˜¬KÄJ…øˆ†ÓV1ž予žØ˜sñh¥mùïyïó•C «#¢gv¤­0!k…¡À¡ÎùyÃ<Ýú“o™ÔÕOM„4ró5©aóæÎÙþø\Ü" % \TÉ
+¼ÄŠC
+©jçuW™óEË¿7æú›jRgÔ4ÜÑ °ž9ò]ÝêRÔ"d—¼£ãy«ˆ’3³{“ëÔ†’ƒ;ÛqŠ6”·sCú¡Ö°NB®QÀ=¹‚îf]˜š‚‡Š8ñ°‹.½d—'5쮤v¨5­…{³.¡¢·ì¾iûÞ$«ø›wæÑ¿:Úã¼K@+mŒÉ²V.ö`U±ñ¦é|DÑýSÖDi,Ù9è¢SHIyQ­=‰ï‰­áîVþOq™~5™žqø£_Zí™ã9Õ33ŽNµ£âX)cDeLØ!,$d!û›äÍN
+3vj_ÆJéð¨(u3ƒ%ÞE*¢‡É¶‘_“VË 2}WN*÷,R@¯£cf˜º6Þ]Q3Û²º!vZÃÃd‚3vÄuøŠ¨sÇ5‡Ê‹‚~ò(™Íû
+zOE­ÏÙ!üïÛlVn“†Iš)ín)úúÖ{ôÕ‚Ñ^ä¢}2r­Wny‰øA-*?¹ùºíàgTÖ
+έç
+ˆ¡£N•SïÇÔð@Â0:\ ¿'Eÿâ‘Ê|‹œö¬ýÁ«Œ]òÏ$Ø£°‰Ù_ev'Ž\HÃîI-C}¡ErýÎ4ð:ù`Ý¡] Žéi+›x`cBZF[PÅîHáð±fk¢õ¯1 ¾þ³E,Hh…ôíé¾B÷‡ŽK]y›Ó¤[[3PO)&:g¡û¡Ð##ßÝŸ%Vå,lÒ<8kåÐ O| ´ú°ZÂJê‡ùÎqb¡JÒþ”}ï$`É¿' Cœ´E,Š­ðÈÛrBÙÚ»®üˆŽÕ®·-í-ؘF]ÞœêÌsD'M’1Ë¿•ÑÛ§¤ÔåHRËê *P×\“µßÙ^Vœ>TâSÑQ#ìÊè‹°AÀð(Éuþ%
+"fäÑ2ñƒŒCò<fb“ÀnÞÜ•‘*¢z>)jäà¬cÝy‡ðeS0|h‡@^Òº~·óùY“xä®9LáÖ,±tý?h”{ñe6g&7}µÓ1_7èø¸±¯Òöyfý]~Pt\ÊÆ8¦`A\}jŠqý¸õUËÕ˜Â~v
+$‡<QÌÈìÞ•ao¦-09»Îe7«#œ?¦ºy:¤ÀV:¹pv“/ö(qµ¶ñÖó±UκŸjÝÃïÂ:B›†—\%(?é’”øÕ´û= æ{cožŽL¯ñèY›·!ah»í+ýñ5îÇcnióO>áÎŽÓw gu˜ ž”žÑž2jFliL‹)‰¯ÐÚ6?Š]3œ¿ê±8½&`æ>A„„ˆô.÷\_y<å™&U¦ub¦íýÀ­9~ÙéøòQ¯Ðï‡å¬Æ,è¾´™„H˜5^9®Ø§ #@v‰£€«#KÄJÇûŠo¬¯KoOµ_Žè TÊÊg ŒKN½ûi†P´=E.ûl†úÿçdRfB£WÝteKZ}ä\~ÎΤ,<šoPéx‡8ëšjºSÒ‘½–6‹8ÞefKÌ( ¤ÍŸ¥­£OC6Æ-m¿¸9ÞtÖ'¸“4ˆ ¬Mò$f„q1ŒË}?H™YØ Çù¶ñ‚Jrý+Oªþä “531_Àüz
+"ãàB‡[‚á´EÄë9}q=­}WŠºúÊ?fzZþ}ÚÀêóÏÓïùik²å/ûóýE€3z³+bvÎ,€<ªÁÚ­9\aHË#$M¼Á¸ÑÒPê7¦P7'Û.d,Ƭ…Þ*Ôµ;G«ö^ ©8¨˜îI@vÈ a- p?³9c‹½¿£¬
+h8蘑KȬ‰xIð= ç ì«Èu;2jupFùå´ºýÙîkÎ1ä÷›ï;/¥,,\Î)NÛÅÃq ]• ´Ã¿aˆ•pŒ<O;$À™vØâ=9æFT'bÄM;:¦Š"¦!QÆ)õ«{ÊtÃ'v¦º®4èAþîѼíRj•öù—Ù(?m}2™
+
+„EbL$å
+…"Qa‰@(Æ4ÿ1¨Hœ/à—ˆDÅbð[1Fä‚„‡s%|¾XXÌ€ Ñ᪂BQ¸¨ è¿}çvÄ÷ɉà)y×xZ^¯;~“\ãµ|ã
+ÔMœ«[™ÑbRÖËÌ $Ú£É2Ôä[ÓÇ[´Z™ïÃg10 Å'@•.‚CbË]à<Ë·«ÆǪÀ…LÙ°>V‹utñ±¾\|c.ûa.cáQ劊óEÅGãšoŽ Ï5_Çw|¯ý÷íÁÉðu¹,Û㲊ÀK.åþÕ+  æ
+ïl9nUÉñKµmÕ˜¤Ž×Öž9l˜£o0:,¶Pxô(„X&à
+"ž¤ò‡‰ESpɧ*æ6·Fg -»óº¶d@×–‰­Œ¯Œ»W’>MC’4v1a»~ßkh1Ý1:7P¹7««£­½é€}€ñ;µÉ›,¹hîˆûLmq¿càÅŒµmë±öê³/&åñYeyrNYÁxU• ìT%ü·ïÆw>ˆFÑ9[×ò‹OûJcS½%‰¹¾·S3ÚjÆ=ØÆ OU›ôõ)¿©+ê–_Øž–oN´ŸM¸Õé0Ñ’t+.Ñ‹š&6dîKS=íÓµ$牚ø<QµõH.b<zÉ«ÈÈGZð”×ÔEûíZ.â0¥H³lgF]¾1Ñ]°ûXqaÿ‰®Žõ94Éy›4>ëTîL v3ž1[ì ð— ^ŒKÞ|þàêÏö&»„켡%íµ+¸€]ý|²Gøì~kÞæŸ:Ïl~ÜY¸ñ°é×±'²RàÐÀ-:ˆ¸GWŸ˜7^cü6E&4leîјwÄu«wf¬»O éÐè(ȧ—j%YÊ¢‡’ Zä)¯®)1?P“X0¶îytµÉEC Ô4ÿe²ùäæc‰€ 46ÕˈÚ ;Í x&[Ÿö—mO©«èÀ™ [¢ Õ»neEÔˆ‹XˆÌšÝš
+Ù”ÛžúµÛÏ2>žð›{^¢>³j·|æºË-[‰tĬ`Ãæ~nÉ$Oú”樂—Ù€ªšñ«¯¾Z÷‹ êÒa³œ¡ôÝ)Êнï¨Mø5 鈩&µ-eUg—†,iÏÅ$ãÂV%²» ªªâr*`ìI/{_.þºîºû
+Ü÷`uØ•^qÅüƒê]¯Yõ;sýGSÃC1ÐkÌÒ÷3Ë·d£×Ó!§)MYUÙ“‚¡ˆfšÒµ³a£,»b7¥ÁšÄ¼®‘ ØTìÒß²‡1»ê<Xc#FyŠT^e(eS2Ø_½?¯¬Ø™QÖy~ÓP¹‡—ï{¯³¤¡{ÏWl}‚Ÿß".³!PË•á!fÙÔÇRš¦”·¿Šõ-/—Á³Z±c^]ãæ”âüæ´ª<AYñÇŠ·b³†vôHpÈ@Ͳø|žGQ]°t>4×ÒäØ(CŽí-€ý7ÛSœ\žKûñ
+Ö×Wžòiê÷´1¯¹;rÙ^›lçÏýç7tžØ™
+÷ge¥¬‡¨Éxm½IŸ±cräÒ±ðï:O½(§ÖÞ½¹žÒçŸT¾±ó¸áMƯ¬äH³4t¨™ƒ`H§–9¥I§Ž%í*ÚoÐ‹êª eèä(cw6â´~¹îf©!3è!}ÜkjÍ™šY¿¥‡ýî;¿1Þ·ô‡ú_ºGê}äT§<.]ô‰úÒöxç©g¬{cë£Ö“Œ[QFÏ«*÷½DíÔ»e?Z¼YõsfÎ,ËPNctFZ²þá•o<¬ûëU”±Þþò4xפ)§ž ÙúS¤Eκ 5b¹æº•‰Ø4thP zA—]»“]uÝ`C`?ûô- ¾‘vë›2Ac{&¤“dÃ:I†Ô6¥<& pØÀ@#½ ¬ÌR H+žY$8Ê®áÂC.2v“&†˜×&M,©rØœ^q²«6½4¨á@ŸçöÑw¯Nvä?è.Ì®¹Þ㞎¾Ç­ g×]·>»~/³ìäBÆnŽ$êé
+9ŒPÛ̺ë6ȶgÚó_L_;¹=ÓvzQU¹:ÞVz³ïÔ±r\fOM¥i¿žš›®¾š¥©¶ÚrªÛéÒi[§AѲ˜˜
+³óV.´Kˆ w03p>ùãõÏ¢[?=bŸ.M;¥¹3oJÕt8©hÌMÊðeyÛÁιäË[§6~æ~zÞóåÚËþËdȪ)öÌŠêwÞò.å憮S°ü6^##òN<õgA¾- ™ÔÈ“tÚki›…ˆjØð ™p˜˜OZYs Ã
+ú±Ê0Ø¡ÂÀ—Hö¯;=ÔÆÈCj}ÄK¥ìF"eV}\–µ\yLgF÷€Ù}V͸Çé”÷)ðž–Œhz‹~A êQxèZ6Àý.à×]ÊŠ`b–â=ŸJjúȘ´ƒŠÉ
+qšv—”íE¿e¸–ðŒWÁ>Ž–‡®nO°þ´ûáîÙìÒp}Ö§¼Y
+A\"tÕÀ£»Š;mLrdd¶‘ŒCCdÌ$Æ`ˆKFLÃÕ¨U^³deg5)n'Áúìë4ÈŽhmg†ÿ]~~¸¾úÅɆÃq´‚-—Cú~,bSaa› ŠÂšô2e…èM÷SrÝf"×ìrÍå¾xHÄ¢rh!mÕ×2w-c³ÑëVcø§¶1öšN¹ÜÄ$.úL̬®¥Í“6é˜u“†JB‚B@yªÒ€~cP«¦ŠXÕdDËÁâf
+Ñ TAÞäƒZv!0j"¢£.:nÓUb%±Hpå4€"ÆáÚ–û½a³Q 1[ÞWô†f°´*i;îÅeO5 IòauÏæ´ìêöŒª…NÞÿÇñºÃQ[·èé¤ÃNT¢Ê.<Äû~¦ïüþ¬¤Š›•Ìºç9™}PIyƉ䣷Túþ‹˜mrbàëÜ’¤‘
+«º™„MOgÜOr G
+AйƒjV1 k), ê(XÕ²œƒÃPß‘O~cÑÀÉùÇ\ùU‡ èzQ¿øzv‘qwZ\w´¨ï.†¬’ÙÔ«;$A ÉˆEZ\Ñõ”‚ Vem¥ ŠEÀš»‡‹¢†­Ÿ‡/ >óÐqÜã©DÃ¥âv> k-U·ögä 3Š¦ô{ÁE³àëß¼×´ýsŠ0ŸCR^Tw—D {€ïÛ“¼¿l¼áÉ/h:×tø/:s0 ^)ú8ß–V¸ßWø—ŠË«(làï­@¼ýIÓ1è‘ ;Êj)½ìí^nYT_X^)ûÄ ÛSü¿Íz[?<cSYuéË«ÚžƒYþ…­7½_n½î>µõSÏŸ¦$õ[Ó¢ºQù…ßfž*nÔ`·­˜A#ŠNptçýÒÖÃis. Ì3Ë™Oýtb
+¸å*@$!ÉL®äw¶«¢nwµzpU´á’˜ äžî 3“ÉLf& âöé/}1‡sLž™çù}¿ŸOsƧ¨¦Ã0ˆ3>¤ŽZÔ…ÄèÃâÊðýÒšcäpÝ>THTtmçÖLýLÊbÎEÀ‰bc ño¦6œSÀdF+s:xXrèn)98FÃ9 ËkMX-àt­•yLËÏze×qo_‰)oåBjYÀÄÆÿÅ&&Ÿ“!‡)ã7~Œ9Gÿ›²; LÙþwç6þϲ¯Rô#ô9ƒ„\i&¼Â‹[¯šþ´õºåÄ6d÷朣;üT~yíY÷_³‹êzðÇÊ×ß/˜ÚªV~ì<½ö”¼3©vç´õûà(̲EU¾/…Éoæ½7_çùéÆßoN‹Ïc¶hvgä—³^Í™ïwÏÕ}aý™èóàÔcwåçæ¶6}–›·vefÑZÜ%½„Ïö\"Õø‚¢êÀÕówʧ¨#¼ÒjÒ¯j`#fîUñög%æÅ°Òꌧ÷êžKT‘^Tzð~‡}sóÓ£˜®ç0®“иGY“^èû
+w+ªðùµÍ×âsá'üS;¯åÕy๬æ܇6ânùÝÁŸ×Ÿ6?x#»²õBrþ¾âÂÏã÷:+€…!+ä
++'·jƒÏ¹øø&6ß70Q\/B÷Á5Î_±À–¹F@›öç{.ÑK¦^6l×QKšV* †³]îVCøØ@!1ò
+/d2·¡•2É‘©|tx²Û@C6ìuç˃IÀe”õ\܈T
+)çdد˜²Ú˜„AZî™ÃTÙ‘‡Ø耕žå0+Iû
+¼týEÛIb±÷:
+ÏÒ*n©÷: }[Š‚£ûdÜôô ˜e“‚ƒì+ÅJ.Šòß{:*^L- ØÒ³(ú³9ëщi®•ñiȤÜ[¤éÝœøâÆËöӌߌ0˜]Kû-ò|À"¥ýÀLa“⧔Ýq´b66«Ó~ÕíײÊ\ÀŠp‰±ïè`¿,ã.÷ŠúµdRrk ³bwÀÙT± »5‡¬¶[é¢ô·Ÿ%1Q%”ó¨(r§™[J_¬ ?(ÆFáÿ¾Ÿ|VZéGŠ«ŠæRR| n=TâÎá|ÐÑž£/­”óÓÚC~xÐ[ùÊÿ
+S ûCX3ž…žá¢•nè.$ú^–Á…€ÿ„%„«ôdÒDT:®4vÑÀ¨¥0ZÂâ(%á™e#*Äq„ áÊb•‡^‰ÎLõUý®qŒ, ¾ãŽ‘|DÕ´5#¿œšBníÎÙ.úpZ
+Øãç…¬  ¹ACÿm|húnmR|jo¶ë&O(G:z
+අE´™õ˜»i"f<º†Ü¼ä|êuÍwÞ5|“™‘^¤]hëúTë™ Ñ?9“ýXgÞ%½º9YûÕÊó›¿Ï¼k9C/hnó£¼è…®Y@Ä<8En¡ý|Æy÷ëüìùù†S´§â[H§
+K¨î0i<µ»€˜`¦-ÀKö¡Ã%Â&ÄP%6ªÊÀK‡ËÖA.bë¥+Ü\Í„Ž ¢pÎ÷s°àA‚&¾ÃÂs¹€IUX°"Õ;º6‹Š6f”7è ®í`ɈÂ~ùdê–U [Ì…Ðã… 0v˜Œ5¥8ª>¬°ZÌB0eüÒš½YÑ÷iÔ ÔÀ¶Z3ÍݬX¬6t–“ö'eèÇRGòÉ~ÉR61#9`V΄_vü|¸j--ã¦ìSÚ£Aæ^¢IÕ½ìa)Ö7VŽNq[_…rnD¼;§ª)E,¶ŒUm: ¢¬·o ã{ün×û³3™*FplßÛU»á|puÇeÖo“=}Ëïµà»Kµ d*M?¹ bšìq컬ڽ´‘(6 ×ål×Àš,†Ü›—_ËΪoå|6óƤ¶:; s {à ´{3§gI85QwlÏÙv©P7~úµãzÁn€»‡ð®ãÃ"°´ iO½m?»ù^òýæÇîªÕ±öS¹8Ow}n^Z%•÷ÀC9¿V´úúö±•_nÿ…ö¨Eð˜Õ—w¾Úš¸÷÷œSrE€3/iÖ¹>y÷dêEã·éyUýê¸øoé)Ù%Á©>Ç¡o)\›÷ªo¯l>•ž—^Õl~쪘ò3ð8Ì
+1ÐÅ4ÜA:h5ÁÏÌ… ´œ° V²˜¡LH 2ösÒ:ÌGõòÙ~µÂ¶¬Ï¢-ºÍ
+ÚgA*³u°Ò½eï£ÿ?“Žþ}±£&ð-7*½Q^(¸mz!Šë„„ANµMiow—ÐË„¤^ÁFÍ&šê¶çÓ‰2A£r×ÊÄÀ•*ÝCé;òC °¥–§ã3köæÐ66hÅ9ÊÞφmŽrÜâ8XÁÐrÒì`£E> „µôæÉÞIh¸¨­WXv<’ýo8`¥Ò2á('¡Ÿc&dmº½jDúÍo€dllh²@õŒ2TÏ0Ç %8ïr”0Æ0 |[xÖpÖó°å®÷±sÃ=úbuήÝtãÒp°^Æ4Å ¦Êøõ­Ôsñ×Å ¶¹ÒŠò!}K1„«‹c7èÜ[´jÖ¦U?–cöþƒ$Ñ[^AõLyPð㊼mÛ_T×ç½hû–GÝì¾w|{JÕt¸þ±÷g÷°$Þsuÿwyüöñô¼¬z}Vr}Jå·88D‡l¶|Ð ÉúôÍYŸ®agN]›z+9»=­¸o>ýÁPud{BVs#*³¬áaN ®ú­™ÆSÁÑó¿M>¯ýíVÝÛ_ì®ÝxÕz–u¡<‰uå=ÝuùEù{3­gW^Ö[oø.6Ùzñ™êÜ‘ÄhëõÃÈÀ¯ÀøÑËhp´¬[UŸvµ|_ôɪ‹!µ8úüîñR@-þü 'ؘ¶< ‰iÅÛ³’ [%ç?*ß t5{ën¾Ä:‹þ§st<À­U:0=«­ó Õõõ\;ºÿ¾³*5«¸á´Õýµ8ï€\y4ÍÅ^g½:q¥Wr^å­µ™ÖÿìÎ+jYÈŒýE¬=ëÁLhdŠ¡úŸBöþ|¥;‚ˆ4³ ¹•]À%·Ý˜z ,éÕqÛðAÂ6üåÑ÷DÍDw³dûypOqƃÉÖ>È«á½ûåÄÀ>>8ÉÅúÇù¸c¤Ò%|TÝ’óµœ+’òÚk”]„’õ
+~ÕÀÁ+Éw刲§è‘Ý £ª*p/¬UoÎB¯Sa£äد8ØBZ¡Ãç꯭S•×:Þu<ï3“¸YJŸ#êÁ·#gˆ…Þ?å^*ûkûl·Lq«ê—¼ ƒ÷øt# nÁ™]_×_ûŒõ9 Ç~àУh*‘.p–¾R@ÓKu‚’ß4~8zæ‰âÒ¯ß-\¯&MZ:Úð%EçQ@tecô›ìÞ…—²æJÔ¨¤#iaMÝ\ LYr¯4ƒÙ•ÑÓŒÜ*¨fqd ´-oòÏwÿÅÚ{âWÛö›ÿ¬¥lsù°²‹Œ¨‡¸¨YSK`\Êo÷žÊ{g÷Ÿv}^Ã%-|F9z^¦q3ÊÃ>3!½:a„‹9\Úò~uü<íµyÈÌ#¯ô:Ìb°¡¤Ï†7uÃä+íPÉ«ø—O#í_~–ß°Øø§9¿Y‘]‘ü«
+OÁùÔ³Zù§¬JTOk&« `±°ÃZÏ=bBókLôÎr)¨ì¿:ýæÉðÉôòäùØý‰ lÂæ,FŒª0cEßÂ
+¿÷”%N*†ŽyÇΓAÑ·T@vƒ'tb*¦ê+ÛÂÎi
+7IË!³"·…´ç ÏöÖ{NenýíaÒWKòAí0]&‰ÙGLƒÏâî(ÃŽ6•Íû«â‹Í<¡—•B˜¶ž¡×<Ìë)p &
+™˜ª¿TK!£LLx-ç¹õeWôTv3tãÛ„]n6Ñð¼žA%µ”zœÁÇ¿û¸Úùy~KÚg£`bº ÞËL/•ÕòàstÒb,DµÂÝçß@.ë*ð{:nBf›²À¬p$y-¡SàkG>È™*¢âw_²YÛ¹£Ÿ¤³†9 ³¦î²ÉwÁÙÒQ³š!\÷saÌüѧl«e¦æ«k×q·›0i¨ˆ¬ƒ*+ÉéÅ\ÈŠúmz†¸óŒKO݃¼wWÒz5·£WÑqTtŒO\ã’F#—5Û?†$ßײÎÅúÞíM>ë˜gwŒ .TTãv'tìƒjÚ6UIÙ\Õ ÝmÔsiÕ8·—ˆø´NÁïš,Ü.æ$Cúq&bA©ˆE ó¦¨ï&Š ïo ÿóÊ[¨EUÄ’ý ¤¥èÑC^ÃLBßÔòî
+!jþ¸ÞúÕ›ÇM¿ûð¤çKºÁu7Ùȱix
+Õ‹ø¸V\ßA„•8"¨GÂ<8Š0OûEkÇ4›hÏÁ¶²½à_ù°ÚýÕþZß?h¯jàS³Ó~y·’~`~¿nŒ|Õþ×ÜêßçŸ÷Ÿ,‘^>®c#Š.& ï`pÍ(íqÛi¯]]Äe7³+M8Þ†ÝuVqµ‘‘!u_ ×K`¶–*ÌÍ'ýLtü[:,¼ÊFT}•¸­¤0'3 Nj%ýzA~]ÓI¾2Ž•¶tƒlLÖÁ'å}dX=HE1}#»àûH‹^“
+8LÀRÒ?q¥¸-8ÇO²q­¨Ñ‹è:Lú7ÙˆQÖ`1º„ŽIÛ(è·21ÙIÅ=à‘*ngf¥uºJQ÷ýRda“%ÜÀ©¤µ˜VÈG‘!2 ìbaÞ¨¸cÎ{<t=›úežê»fk=©™à YG9&ú¾žþ?ÉeÖœFzFá¿‘«ÔT*¹ˆ“¸*5v9{Æ•ñŒ-+ÞÇ’,K#!„
+¾˜CM,e¹^AŸ¤ ¤]öA!<À[ŽFÂ6 f½„ |'ÓààC”þÜÇ¥±ãl®µö—^Li,úì`+mbã£_×–úþ°3ö·Õ…‘oØ5Ó€˜Fõ[ó*È[ÓU)ã÷‡¬Eí}••É3µ•ño1Ó) ž_ðú«)ãÕÒ«Ñ?sÑ —&fîÉÊ›Ñc…ç~õ¯—}‡ù Ëp‹ÂLÕÅѣ̚æp*fqûÞëË¿«¾¾ú%¿fèo¥1#—°ôUߌ©¼üc}M{ººêÿ0oºÐÜ€û~£úž¹tr·ã¦œã̺m°¾fìm&-ÜÓl=îv¶tŠ6õ§¢UÕ)¸&;EÄ,æÌCÄÄÙƆéŸpëêKØhã-®>X¶\â3c§dÚl‚™Á gùáÙZ.Õ–ˆ !1÷„K‘Xuq²§¶`8/%ÝV…v;Ù„e ¶<úÕÞÛ¿”àÌBŠt³q|Rȸ´\Ò<ÈS–!ž²tJnW§ä @ga’~¼òÉïW²„« %çõ·²0‡<bd2ð˜Z)a.ykú¹PÞs>¿’'ˆÏ›ò©ì2}*; LLuro¥ïÐÞÊð˜ÿ•êºþB=ŠêÄÔÌ™ŽüÔÊžƒu×ðþ:6^Y±T–ÇþƤ½ø^Ò‡îB·lfgžòÀwÒ¶7Ò¤UßwŸÁgP0‘EìöÔfaÖ«\ÑËEo°³…“RÉ6ѤMýbÑ>Á—L×Aƒ1w{/ÜŽóàýRijNÙÏzþYÙòFÄ‚ÛÅѨYüÿ>à"åP· ¾ð¶÷yÊtMÈÚT­‚ºŒ±oççñµ5ã¹fÊ1ö~U÷K¡“J)ü\*E^
+E2(0'—³«YZ?РLãJ9x_Ì“~ æÙñP,âxeùÚz't©>,Фö¢«Kv#"q´E{1øOèQåT‚{i—É@aÆ®[ð¥Dø:[þ[b êq홽Eõ_€ùÝE ¿+̆ñ
+·áÔ‰‰©Y%¾ÓÎúƒ\Ò9öþŸê£µeK¿ ýt‚æƉ5“7°)°ŸóÇ}è
+øZcÍ6 $a64 sñzr/ÕGð“ÿn¹l¥=_jO5“¶.e¼Ì%õ礴C}0 >qÿõ3ÇÉ/šk˜½³é Þôø!Ï]Õ8ª©&¬×‹‹šãÕª¯S ­¨ãº[ñ¹§J&x£±a¼¸»8vìÃëÁCµ¥±¯ø4aÝ]B‡öÞ!ý0F§¸Û.8&D
+|˜r¨øŒSÕ„~
+>9ÕH£ÆZÔv­ (Ó^\ê²eÖƒTãÆóõ”®W
+=’Jág 87ŸsÛ›fò¤´ìf€…„»ÿ~_K`Úƒ$¦ÿÁ;)t¡:ô0%;5­ä¦"Jœêp¬•C&yx¯ϧnÜi•‚÷äM/ä#qðäE)¸JÉi&0x¦WòÁ»r>òTÎn(™À´˜ò¢t .áåSÖQ.a`¢†^1>åk&fî¯ûõ¸ÏU[Å5lÓË0s1‡XêI»fëdÏkK'¹'¥QCuÉг·l:] Ø5v=Q)ºô-ÈbÈ÷T6|Èî*1.fHLÑÊš™uÍ·Õ¥ñ¥§—¿ø÷£ácJÂçkÑ8RƒÝÒ$!ç¦RÀ*|OÊxQÎÉÄ-ýÐG¬e\°«àÙw˜ÔÜóVÁë³è$ðÑÈûù±£±™~“œ»ö'!åFXªë߈®™ö¸j1·ŽI£º½¸íÚ^Ê¥ÚM9‡?,[/µa¶mè£RÑŠ”Û®1[Wÿ»ïL=–̇>r±uôµhð·¬/
+á'B.òX*†îAoô@¶#øÍ΂¾·üü—ÙWÛfÆÿœ¹ëœ9s3§g&³4ít’4iHÓ.ûfÆ6¶ey‘e˶löBh“LšÓLÚP ,ï‹,/1†P(`,KÖjBOÿŽyÉÅw#Ùòg}ïó<¿§ÿïµ49.fìú“‚Ý\ËÛL§ÂWßÂ1à„O9­rÆ7¥d=$päxõ‚œŸ^’Þ’3d$Ÿ2¶… *Ή£=®ý7h«š¯$f8jbž£H/›ð:+°/>C¸Ú…Kiz–ßÏQÓ/øôÃ
+=:ÍgÜŸuÚJ)´ïhx<a×I9ÈÓ Ò±»jk-Ç&çEzæ•”ž|^Ûž\¨mùçkß#)cÓŸæ½Ó°!—òO3Ô|HÈO-piÿì^È©;Œâ‰¶Þ«gŒ]õ4¢>X×^;NtºzŸu 5Ú¢æbH[ |gyèòáŠæŠ’2÷Õ2N;œ—V‚¾ÂÓÂ|šœ,S.+ YPË“3rÎMÂòBŒËÀ²|”@Ù ÖÇE±.á2 )Ü
+œg½€Ãu‡ Y;©çŸDfÚÿøtO9‡Ao1÷Ji𘹓‚ËsöÃÞoÛ­JÊ:¬ÐRßòÎ(ÛÞµmò¡’'gaFüõ‚oîd‡œW¶\^>ƒ£‡›¦FÈã1‘{X¦ “ðJÙÉàSç T&ç“L¦I¿½«BùÆYz|ŽKÍ.Wè™E1ë›­@‡=×£”³™óKÙ{MèŽCß'™ä·‘RÊ7 \õ
+ mõ¤¯Ç½.)i Íݺ°·hlgÂnK9HhëIÒ{–'ïŸæ Ë1µ€†n×Ì\
+ÈÕÚˆ Í•ÈÈ]™i“Ú¦JÌÚ/RB†,!ÿ+!c[êÉ­?¤ž~u¡îýTHhdð!NXù˜Ï+@_¨Ä&%MàÀ`~`u-è¿G¦ *)aîd£Î!ié;ç]žòz ëB&›º™è½«‡ÁÁ+
+e½§@fÉ6Y£(ÛôÈésß­ïàÄù{f sö– ×õW/x§Бw¡Rzâ¿rž| ç,Úú–±ç–’¹«$L­5ðl)¡'‚¬FŸ=ƒõ´
+ž®ä¡×ä)ªèKeèjÍGáÑQ!ã‡Ì±h׈Ú°6M`r’¨º¶v—iÈZê;Š£ç6ÅìØ·,\i›F ‘îJBs»#06þh…¡f^îLj -%?öL~ëech¯@; bÖ  \•šøŽ‘ÞZnô¡’}ÈE¼¸r ð[ƒRQËIÈàÐZÕÔÓøh²^¸¿VÝø¹¶í›¯çp î©ùÒÎÅM*‘B:ù„©<«ý0ˆëjt®
+ž ‡‹)ð®´]¯ÐHWquøò»ï½úd÷'ýåçš–ב;åõ‘îW´—ùq௻/ºÿ¼ó}ÛGrTß(ÇÌBÄÚ-A`ƒ†Û\ÐÔ(DÐv9fS—×­­Ì¦KÃEÝ!î±q¬¯üm9ZÑ_caÎ˦;¥uý—ÅUݕÕáÿ®Y›w7ö5—Ø€öK& »Q†¹.‡Œ«Ú«±Çèg­í/Á÷C„ž cýÐmõ|ÈÒÉ4W‹ëý—„˜Au¼ij:zmþº!Œ§ÐE«àÏBúDí(­ê¯/kþ]ZÕ|.FMª“¬ú©Ã^Ë:í•°]}¼fúº4«êÀöRÔÜv°Ô{qû‡Î?±+h³v£àÛ¸’r}ȧ÷YÂÇŒ-ÌÚÀ%n³çâiÎn¬gý³2ý!wF`.¿8øY{þcËÙ–Õ\ËZ¥$ÒÁõ·Š«ú/%ʳÇ0ÝD¦À
+’ÆÖb@ý¹-E,]çÚ)ņýU´©¹-&õÐy M'´}X‰[{Å(Ú,ì[€YÍð,¢š'\µÿS\fOieyŸ§é—™©®éôdºÓIº£mÚ- šqßbB\‚¬¸\öÅ-‰•d*‰Ýi“¸
+‚€ ‹¨Qp
+ÈvÙ.«˜îLÿ!sx ¨¢.uÎ=ç÷ý~?_§BÙ”ÑV!Þõ3`‚Ü–Œ™Ý“
+vÊ¡`ŬÈ}ÿ
+¥ìd‰Zv¬¢•£&>³+°qÖ5ªøäü'§˜žÞäâk]e×8¼ì™f•£«p£WÉ(®BµQƒ˜pºÌ®qÏ’®ìM¾ôÎô_ŽØøà0Ô6œ§WÆ œÆô¿;æ:nuW¸x¿†{Õ
+Û|‹\Ô$zè_áÖÌ0®£:¸.iåÝ‹€ù÷-RKÞ/NQ¯*˜‰Û’Úàvø´ÔJÏ2µ<j†[âVv3fƒ;«pƒ_Ô;'‰ß…WxÍY›Œ’¶Á÷’fúí¸‘\ÑQÊâzzUÆÆmOYø„ ŽƒwÏÐKkR¢OI,ò-ô\JYu› 'a‚ê°uvS·5ÜÚ¤IANšäŒMAeýñ51 áõ˜7}ªb¿š\53ñ1;»sÀi‡ ø9=»Å#¤HקÝ!ÑÙöˆ(ebaV)ÿaDœvH©ù]1½×…ŒÂÖᶄ™×Þ©-örÈé˜uLˆm<‘‡M"f—RSvÉ@lÓ60ïÄ,p›g™^éSCUaÐKbëüΨiÍZÅýŸw¤ÌßÁ' zM@M+ªY8Ì,ì=w==2·%çbë¢G˜‰×‘4Á-! TuªÞg@Zãk‚û1ƒœÑ+R69³HIA-Œh˜¸¸™Ýx¶+a`ëBÊ.%ç
+R@'hOX8Í¡UÚŽ\1KÑ5ÚíÂïA­üÑÙÎøsÀSõ€¿I€5ÚÿÕ¸ß1KP5çÎé"½ôx†VPÁµ¾QSP-ëôÎ j¯Éߺ~&^ô+9Õþe¤Þ­4Y_tXæÜÅ,âžž[t^ç]¤•Ÿ*Y¸°© -ójƒ:¸q÷-éšã?ý_G€×gíÂÞ‚Î< ´ë}½§];ž¸‚­Q«±uJ5jd7õ#4Ô8BwEA Üú“f¹gªô« \Æ"ê=ÛQÃæíÈ*ÒÕóÛžïFV¨eÿUßü£ï= ˃×=N“ýeÇß<³ý%ç)3nâ´x Ï({/—‰Åà™²ðê„INJÛLà ¦1vÄ8ô’®½·”ïw']<œ¢\Ý?pùTC¿qæSò¤, s|Ì„´e·Ää춌·Hús ßæ£CùÝYfSB9?l+H€ùZ„Ô5ž,‘J‚ÑZ~ëé¯éxŽu3¨t&ÌC à!õa·.¬…ëKŒ§‹´Òƒ÷Äï>ÎPŠ<ÓPEB+¸÷§kH 2›‚®!­aT:)|'-r*
+þ{¼0Xa{ñૃ_®†•ìªãiÊîé«î)rQ@ÉÀíÿJ¾zø*:!ªå];¿@%ۯȗŽ¦?ö$cVaWÜ$ ` ÿ£za§WÃÅGLrjfëñhnwX–Ì€ ‡tô[(`t°Æ­Ì§¢ßŠ™ø@ƒ’ž¤EAMÙ¤ä¤ ô3 ýæÖD÷…×Ý_ÅV¡¦‚Çc ÷“[bà#£
+Ì>ʬËH^ç¶ëå›ÝŸû/Xþ¡Fšÿ²÷ºûB dGX·`NûŸn„’Ý‚Z¢€ÙOÔŒÊÍ7]ÿÜŸìý6¸L¿Ö±ñÙm9œÝÆrª?@Á|Dô‚{!-Ò|4C»¾÷Ûà•Óô÷"¿!iA2üÞˆQ“°²Û²ÛHàèvŸ’^q<Å(ó«8ͨŽUƒY$Dàý]Q£ð>˜ý;áeæÍà½Ì3G-vN<úfóyß7'3¢ºãiYãá$§Ìý*;~•½¡{gáªÃIz± -ñêB W$L _t¢ÎFØXâÞ9™üqoªç’þIÓúáŽ/œoŠ<s´[ I ' ÜÉ<ûÆáÛ¾ûfüË9ÑðwÏ ‡†¨^µôaÜ<DEânÏ"çv ½³¤¢¸–q'¡cÞ=üðµg‘qÓ£„ñA5Ò‚jyM§ ¤k¡%òOè
+¥ÕÑ*]ÓÔ놱ö¿zgqŸ6‡‘Â,¹çú®x‰E¡zUHËÀ…®QÒY…[NU´ÊÄÚ+a~¦êGÝsüÚ£÷HõÁ$óG×ñ¢{ŠòCx™S7ø— êŒ]ýŸã2ÿf3]àø½wN;Ók´¢}o­QLUÑ(µ4±$È.‹ÙDj ª±Ç±$„&$¨5¤BA"–¢è2÷žû¯Ü×üþœ÷<ïs¾ËçûíïåüÍ@¦"ÿv 5°ç`?·jW•øË5`·©˜7Àù} !Ô8€õÝé+ 6 ‘Ÿ˜†(O¬côØàÍNg8éW‹Uù糕pÇ3Þ1E±#Âù}ûDßMðµËØЛM¼‹µº¢c Ÿo
+1>]oÍéºQ^vYÉŸQi¼‚ˆ?S1r¾k«K~lUÓn4œü‹•âDû>d_”ç¹-ÌɩϵøˆóùŠ‚ŸÛ¼óÅ
+¤}’¶?šïó¹ š®ˆ¿;X~GV{(-Ëí‰JŸL°“ôBr€¡‡äoÓ£tBrÐBCæC9ê¼7@‰ý¢¬Êÿ¢ªÅ+ß!-VÜÁ$-FÛ…vß À{bjädy²Ór=2àbª
+~>[°H9 »#ÌXƒ°Ð×>Š ´ ¢ücE1Ž‰Òç†^¼ßUSéÕJ{“c¶&ooŒªëÌuÕ ²êÛ }G.h”<ÏÏk»iI–±ªTÓ
+ðYœeœi
+ÄZ.
+ãö‹AˆJüù¹…¿3NKžoÎ|t0 >šB™ú³A–LÐõJeÁ;ññ™œ‰ú ð¦}†5“´=9àÍn˜ÛjóÛë”—°$ñ.lô=û=Å8R¼Ð
+jœ¢%<ÍKŸ/4Ð
+Ä2Zfá
+æZ
+B¶úI›u.`‘(óÚÛ*Æ?ÙjGë{IÑ7‹M¼kukÍé\Î>Ã'Ú¦Ê^Y&(ÆA|€¶í¥d´mÙn—Ë•˜½IJ”a 8R×ÃMXÐB¶{(Æ>êSã +fªï‰óüçÿ.´ÿ=ŽŸí7?ÛØØÜbÈe˜Ë˜KÍ5I¢‹B¦D¥¤_‰˜rcÈeHQ.¡”h®%·’ûeì÷û휳Çã<ç9ß?áóy<^ïçó© bnµBꮤ*N+Jþ]+‚þ¾Ð”þHÍËr0‹)!º.R
+Ô‘ ¶3´•gÿ2´
+.Ô• ²wÁ¦n¢—V€~:^•f÷¥ç1Õ„•$ÿ¶,"„Ø&™éúv¼ïz!ÈÐó–³Sßõ@B ¾g‘bü¢‚ ñZÄ‹S%~<Y°…úØFÄî ÑW/Êy:ÉŒ ¡DÜ׋à Ï5HI :Ør_ˆÛ©?ž`ÄÉé±[ý”Wrf¢½¼b¸v¨YV pöíù,c›`§)«a·ºjø•êÜŸkÕ¨½Q\î=êÅÙL3ådº¿/eB·È!_‡Èa¶1:äPZѶ€9¹¯ì'yÈ—×s¼¼ÕÖtÇŽlWS¬âf:ZÒÝOÇr#lRlü…†÷UAzu­á⮶Øzr«¤ä¢&Øp{]Cžóõ -áZÇÊRw yy­r>ÆØψ]lÁ¸-µ \NÆ©ñÇ
+züî`iÀn?)`”µ3D‰Ù¢Æß-¶ýq©•ÚÌÄA"Ø<DôWòNE14BÔ‹Âs‰
+xôvw øk/Ñg··
+¸Š€´ÑUâ»ù‘à½×G
+\iF>ÙîÄ{Y%äp«‚÷¡æ È‹}²ÕMGý2¶6žÎPãöGËÃG€ŸQC—š
+ÜŒ]ïüåŒ{f8èž²6'Ø¢`#䵧Arìo*vªý;ýáçòû9n¢ãÉ 5¡©êòžøÚáRÍÆY´˜A co!ÈÔWä»ö±ÐG×€|¾,Ìó²1R>RÓœèY
+v3S•õ¿M>õ¿<Ò•Š
+ÝÆøý¥o¤]ý¶-¡GZd,è‚š¯Ç<ß—Þjoo—YH½RŸúðë7k­³"BÅG=Ñ7£@‡ÃïB=§ZåÕ©ßÔÔŠ0Š vGJc.Õ͇ãõÙ‡ãLÈÁxyôŒ
+x<-y¥‹e‘ÑRGëУÜ\ð±òì¥6¢ºí¤mD9Ï ÐîÓ¼°²a’Òß–#¢’ü\þ1T™æñc­v³À+¸|Î,!úí  @»}yà Ð <\-!Á›àw®µ$h ôê‡CG6°£ìg‹ï¡Žóü8'›œ¹Z`çLÖÁ=V;¡ÿ2
+9?¬Ë96|³ Úù/QnµY÷G¨o=Lý•°¥öâð»eójŽ‰<nµŠu1Kƒ­÷àý>WÁGØ鮇JR×þèJø…’‡¹]¬Æ^j¨i‡ò²pSÆg³¾Ö È[Šª”íQjìÕ¼€x®ä¡de᧤°ËYrôжÛ2*ôlVĹYèi³Nˆ
+Mbæëù&hŒ™ñh³£Ø÷›Š‹ø·¡¥þŸkíÂóÙVʱ²™bc'ïûž+ÍwUÒAV {`î§Ä+¹èÕÎ2¿ý‘Ú,«”Ÿ)c@(9©Ž ½ =%€÷AæazÂí¼°ò¯e!Ã&¯Œ¹š«#œÏ Šö)¯ŒÏ%!Î]#Àº¨ëð`Ë
+(‹  þAr7Å]CvYEÉ\B”UV—jºçžû¯Üß}ñ}û=ÏyžÏj`×Ûå¤7Æ^äcuWUÈCdÈÈBøõ¬Ú À9À[:fÑ<2\*¨OñÖ ÌÓÍIÌsËd]Þ¡’R¥Jî¯ á±ÎÙút¯ —Òã‹ÏWèØ‹UÜ«nÌP²ò.Û§q¹ç+ì¿šòÖ%!¤®K¯™ù×WøÑ:nÁ§´þíò
+íÁg_]DBB+,Ìú2AÚ‰Ñó‹ÿµ#ª~`¯¸Ðò7&Ñé*^q¬‰Žõ5ML˸d
+@ˆYõÇjtŠ×Øü.¼*h›„,§’óá`¶¥À>…Ï<žB=ýf …5-…ŽyBšSÒôÆ-§BZ
+÷æ[ ?mÝŸ†ZnŸ"<·‰šòˆßH-;1´”yt”’ @éªLá!R¯ºå¬¶óÍ1åÉZ¿ÐeêÄÚ¤ì=1>=¸
+re›ÏûªbÀó]ÿGsAVÈÒ?æ2𨳸ç{ÓÈøÃ…êûÖ©²¸ÍѲ‡?­üÞ‹-AÿØË´Š‹ÞÈjO{ÈÈ#„ô@½GÎ*=^hƒîÎÓsÇ©°[fNó‹ d„áóû"Zº}‚–圥C½rfYÐÄ#œ¬uQvÀÎ æÓôS=€ó/rP-Yæ’üKl¤ß@/wÉ©¹nêT
+.æ`®ýÝz6Þ>C+pk…-»f‘¹s×Ô‹NÚ›j) ,Ä¿×z:OÉУ¹ºä݉ª»»ÓõI!ƒ€r,&eo Áo+ÛŠþÔ±+ï,óQ©¶Q"Ø™ú¨Š.BÂ0ºàŠm”†øÏvïPx©)ïDYŸ|,­v(k‚ø4´òƳåVøöüþá »Ô£áí}æ”­
+·AS¡Q‹íEѺΊ„1üÛõqj‰¨ËýÅeþ”öµÆa4™Þ4w2‰ÍMÒÔ$š4½jj½Qc­{*h4n¨U@ÙT6ÙÙ5Šâ‚€ÅÁ7P‘M%`›´ÿÌýæç3sæÌœ÷ù|ž÷Îߛd9*Ë«oJ?P#^z xH` ¬s[N;þ.ìë„L< àÂéidÆéGDêné÷øapŽ\^éæ챊
+ÃÓC=aƒ¸Ý¥iNsŒ ^ûgÛ`WÛòáȶ\¶t‰ÏMlÔ™‰QÞ‰.Í”+K'É¿€ÎuM6¾ÙSÁš¦_Žt¤âž&Ȇ0U±f!ùg†Ó^TˆÏ§%-‡JJŽwŒ^t©—‘œ£¨ ic¢w’ñÏ K,Dhº\–,IòÎuæÞq¨q¿†V]_9¤AÂ/ÎÚ»ÀñN¦ˆyG:j¾c¸ýí*™°Ú‰üïv/%ßÚßšb„%X>@v†ëÿÙáK¿8ûLÈ'<óä»›í_—ð.mó‘=ÙHd—M9ÒãÞ®öÖ>³ªêž[!Ú¢+»´Ïoà7»´´Ò¶úƾl|PÞR œ-4Ù‹žíª«~ò¯´B"vÅ©%æîsËM2zššŽ¾;Dj¸%n(û±(ç:ëMTÕËPæ³xPܽ[ Û7®ƒbo}J{|7Šúîõ·‹Ü†Øüϯã˯û¨[!¦.ö·¿2ȉ B|܇øx˜ˆþN‚€Þlî¤Cò¢x%à(9´ò^øòÛ5jåo×
+SŸ‚À‰?F÷cjõáË.‰qÉÇÚŽ2ߧÉ1F-tŒR²]¼jÛ(¿tQBKVÓñCDì=ù-û}ùõ–‚üèÆô×Q\pÁuaEþ jQÆõšÔWQ?=eÅÊ}þ–™5ŒƒÞ;æ”ùôbÜ颌}¼ Â[ÕíÙ›
+H‰Œ—Kr9DO ;Ô& ñÁo­Yêm6+êþÛy‘
+„Óoo|â2 ¹K³R=§üúçljižÄ•ä604+5Kt¹ùX¦@\h|÷±*5z%U&Ä3GÊ™óê2·ÒÄ%¸4{Z‡æ×L5 Ô<±¤nνÌj@äh½ä’r×:!µYõÞ$³ôàí “:ÿg÷NËGÓØLLÍù-2@Î]¨©Ö×<27Ê­HŸ}¥à=Õž<7›WgÍÚ…± d^½0H½+¹>­si©Ù¼dîV¶ýÌ“ZK¢^ÇV7^äƒ5Y‚‹v+¯=ƒ­Æü?@JNL}6f]ß[ý•ÊÁú´XãÁ¬st›ë©¤TBW[¬¿€ò‘¨k-½0oó\ÜÍÇP›UbúPÕ&RÄT,ªçsôá¯ÕN.¶Ž.Y½4 2ÒÅ!fG0Egµ…!@‹(øÈðbfða@r£ï]/EûJëªÚóÓ:ÉX¢8›ÇiZüCSN>iÁ™“4”’s¯µ¿iÏøY°äIõäpñ
+³¼~´“ÆÛ”} DÄ‚²Ü¤½'ÉñØÞ¦ÂÄű¨®Sp' „ΪL1¾%†¤¶íÆ2»qéê}787T¨qà\¶3ŽgZ[ÉV59vájˆYóÂlàöÚ1ÞôÑ«x˜êÂ)rש§¼wúûtý±ëG­j!Á¢¶Q¢ßÔl¬0pMÙU®Ò¾PEö, BÝÐÚHéšZ7í}Î*VŠ*s6å*ê]š.Ïg–3ÔËVÜÇ:Ú ñk…¼ 2HÎy´,!<Tåbó4œ\01hŒoüü±áÚ¦¹ˆN2tå^´ÚaDò æc–·ÏÌêPü æDð3Ô”¼3…/5¦—Ñ°>Yé×®:7ŠHU«ô„ݜńdÓf¦¢Í¾2ë1+i\<˜ÆÏXè”rò :Þ%¼ƒà…Ô…­’“÷NW$óZ™ë¬E©è2:LšÆýì8—ŽAºÔßf"Â}|må±(_²·a¢ß‚ÌSÀYv‡Ói#;þý+þñžt¿jKÉOr´ŽÜrŠ¼’n*½Ÿ³ÚË$.„À¡çsKnÚC=lA’EV)} l“jHÆ'åÉ`ÎXäðõÝVÜŽ< &EðÚzFÔˆM¨¡ÙOÞµV3&2?XM!Î5Ê9ƒŒr¶8Òì*ˆŸÓä·;‰ðP„S‡¡Z;‚—RåusŠ rìhG3¶†Ýš|y7AÃ
+nŠói¸Æ„´Î,´é«€…³2XV¼¦ bMD¬º1¥!ÊÓ÷ˆ&Š¶Áœ˜¨Ÿ“ÄÀ«Æº}<€.í¼1ó*²BÁ‹ö:§—ÜtÖ"­ü…2óØõ|4Ä8ÈY›¹mäzÍäRøU¿¨L´b@D#%$iârñ/¹iîA Õ
+›ù‹+¸Âã§øA¾Þi}A¾F´{@ÙE=x™ÈB¬ê>êa <#¶ù¢(š€(q×9 (Xx5R¼î³Y¼p÷¹U<@¹-koBî‰hFokxÐ\&£Ìh*#.åB:ˆ¡ÇÞ—T\@¸0”&p3A¥. |ì,!õÓé ï@-k3ŽÌ±úuâ6OÆxãå9¿H³fÜC–ßż° rVmçúl®ßqàOù´â|ô‚,€šlÞ”¿· ;z µ¤
+4á5 ¢$0vÏSY›LnTáÏih;ü.¹¯aïƒæÙX0¤¯3–5`áô€25Ëä§$rÄxöž/%%{FB?!ÔH*Öß2÷2·Š)±ÌÒò°Ná-ÕÂÑ©àoaÀxM9ê€TþšB—óï
+`'¥æ¡>76˨ÓDD® 397KÁÆlÅwq\´À¿úšGxäo.^c7w_"Œï^óæñˆ-Á!ñ§u.}
+ƒÝ‡ç@3J]ÉÒ£9¼¨Z¼eOˆôè1þVNcûš\Þa7Åÿ#×̆À´–âEÎ ¡ñ
+ÃÃ6òrîă:J”‡u8 #ªLÑ$‘~â]3ÎÑSJAÕY;¸Fá ‘Üèç=è‹ ~<€x›@71
+?7#AòM÷H!Ãâ:DJ˜,â1 X&ó½šzIjŽ„óçÞ)[‰Ÿå¶÷vž–‘âu$O[‰wO˜[«vë\ú½;Í…7»[]éw/ΆÃ×o _ZõÏwæ“ÁEØå%nþ”ên RNÐC.k>Gž ;F‘Õ¾Þg‰LÂ9š÷iÔ#¯¯v|RÔupž¨…ÛµìÕá[–¨ýV oßi*ÓDÄ.d¿ŒOx]ØËŠý×Ó0ú ÏCfˆ*—"¿+¨HiVó‡m÷n"cÂ-BUxÝ®FhwÈ3Z<!^ kVã9É]"­A¾¾4þc ŠqsÞMd4nl†µÒ*^uB¬Ç;EðHŸƒÌËp#hNm¢zÿg¼L’븕(ºï+P oÆüCíÂSjÿÓ.x«²/¶ÂâeÈæ61¬a¾9‹b"Ž4Ñ& Í¥&ÓÞLcB|§ï3nýôüÊm.¼ï°± CÙÈ_Ç<…Þ2ö%mİη‚Iä.¾=÷ALzÅÍ`p0HfåRª2QÄþLÕêÁ•!›— ƒ’· ì:>ü½ÓQr¥&ùõór.lÔ€Ó»G—¶Ã©6b¤™x1<,ò¹f;嶇¼Z?|!2ÉbmRˆÄ`8=ȺvÚ‹Þeç‹ÿ„·¥ã}"Fѧ‹ÄßÐ
+J¢öí`ÚãÔrÜó~Ãx¦0áÆ!Ö6Ç÷AÐD@Âc4¸jãQ(rï?®÷N¤˜4ɲú_ÔÙt<„ÏU”ÜdHûùcàë{9”pç'šAÅRÊÚð'ž4i(ÚÍ›Ÿ¾“°‹!úñ—¥Ò¬ˆçéqC(KžZ®0 ‚çÕeöûõóþeé"é¢7EÀìŽ?>†µeáòc!Ô´©|I
+͘ª¦{½-С:È š ,9t`§G ¬‹jÍvB#­Œg
+ioF€Âñû0ra4ßÕ)Z/G» UlÈ”Çóøo;(±äœÝgñ@ ‚ dÊÏ3l4
+ÇÎÊÞ1/@Ò¤öä2¨õåä—ŸšX:Š¸VãÂœ5&~Ê¢a7¾þñƒËðAôyð©üaWF'$ìóÄÒ‹åéL.l {ô'/«9?V¡<}çÒi qŽ}ki{±[;Q˜4#14þ¹OÌ…È £÷áΖ(yá2qúɃ ;ÏtúqŠÿ²Yñ6‚C:s>E˜Ùkh³‘NíV•ßMêk³§A™Ä7DJä² 2†&]†ëÐzÜGÌç≰˜[`®ÒQBFV§j§Òq*µ/§ÅJš\Lm|’å€Öwz5à}ñgÂFhÕž= ´~ÊîÀó&‚ݘJ¸½= |'=ÉšAõŠ˜¿7ˆPŒ‰SMoƒš×q´õ„žó Š›À:Éáï}gýñ@¥Km ‘`c…ÍBædb­ÛÀ`Ͷ©åNáô½¥h(zr Ö x™ùŽ,ÚƒAÀTžËUy`gkóæ õò©Ò×ræë2¦È;o„irÏÙÆô2ì’w:æ`j‡ )J·{F‰Z稟7k{¤Ô)<4Ãz‡nL—äÜùµ*ûçáæÖäăn0(µ³¢üm–cʽ"ti ›[Jix1”uk@Ì<§Ë&l JKáéÃÔR¤öK³§3^fýz‡ßÃпA>ÅF³6ÝÎåL€Íº€
+Ê gøpƒjÇïr.]û;ŸŠ¶‰7„‹tzG#ª]†òrr_è0.Dƒûå~Žb[©ú›â_8¸êýû;„•fM§A(ÇÓˆ_v}¢Ë‰YšÉ<’NöQ+Eàc¥vvå€1 žÐ—R÷w°OäSÉ‹)6yXŒi¶ëÁv’`ÈB稈¥PgÄ!Ø;·g=Ø{Ûq<Ð()?±ú)ñ06pƆP~~Kí~•O¾–fAgó",pÔ8åCe”Œ{ñ÷ÈHu"—çɦÅyš-Rýï° ,‚Ö:çzn34G}°R•€Ø”pÒBœ•ÉçM3Ÿ“è§0ŽÝ GéÙw¼ 5:9  Ð3(þrͺ.ö4OÞ4Ì׆œÇnCS¤V[]R)ƒÄ‹¬•U‰Õ¬¡níA¦&—±ETÌ´žå´Ô ˆ¢DÕãmŽÝ˜Ê‘±XŸ ‡‚Q Z}á†÷`7—Õ¹·ò±©AÜáXäk”§ïT~ÎH–wE)ƒÖ®EÛï Kpa%UR¾!¾¨„‚b‘¢ÙNoà"ŠQI-à«á$3Û‡T…—(ýÒY.¬+Tb6Ù‰ 8 œïL’˜I‹m¿a™(^bƒíUP{`ï 2c‰À†! “€pŽªøK
+c_\›©&• É5yNôŒþJÀ קï F+5…q.ÛQ ɯIê"
+i—«( R‚2ñŇz¥³pmï=ÆE'Ѿ^L-°ØA‚Þ†yüµÃ2¶Qì3 Áý0囜—ªJ™Ù¦‚È¡ûcößFôóãÍ:Çp"n ×»oû®g½‡ùÆþZ]@Æ-|^b…¤fßR^!\eõhs‹JJså‚5ª@4
+jžp¸Œ<™;‚€Ü$ÑÏ+ÏâäÏì$­È7¦¾S# ŠXY[ä©ïÏð#ì¿j‰1Ó!}Ú\Ô H)7t v._éI&ëé;2É*`}çv^D
+î¦E9/ë×FÙÄŒs—Qf6k ¢æS9(¹5¾@ì¨K¯¼ï\îÝæ28þ«¾Ïß­8Þ_JìA¾µêßw6FËÎr²¶¦é÷ïýqA2KqÇéö%L@j[œÃ4;‚ì'>S £¹!—êô2ð‡p(a¼.>ä4"¿hudà3Ü1RLVê„­b¹`f®Îp̲r÷Æ_ gƘ•žlë~g3ÓL—øǽ ­"l«kûÓDì2ŸÉªG1YXx4o½ø«„æˆVíÝcʦŽ=¶MïW _ÁiÖ(.Á¢lþYäè[ã¿\:A@¨{¶EnÊ 8a8Úˆp*0ÿg¼Ü‘+¹• º‚ÙÃØ2ø
+WÃÛhŠÆÓӌꪬüðŸýåž$ÏçäD÷žö &Üƈ14/Â/ ™ÒWgÂar÷Ls ÁŽ_çyHW\\Ï]H.÷‡ý(t¬™”2m„2ÁOåoÂq$¹"&¿1ª¸ÉB@®\bé(áƬsJcþY‘ÑK
+ÓÅVjÐÝ‘25¨XYŠn(/SaÛ°¨«DZ€[fò^R,Ã)T/‰ÊKta¨W~•RA<ÚY(ÁŠ"Æp‘+ gâ$§1´›|b+1Àú¸ê¹HDÏcMû‹Ùwš‡aÞôÕõ 8c‚úG#XuÓ–ÍÚÏà4îGû¾yÕpåŠÈ¶pÖn_‡5yF$ñœi½á~³ jD•;Z}°ïãî¡tI³÷×?ðÝ8[%À5ñ'ùsÄÒœ½o›D`mqÛ <.´Š£uMœq ¢+û"ÀÚÒ4“£Ü”(çb³<I]‹9è?ýë%^kÞoj–Ôºô¹ù³Tß•àjFÄáö&"÷õ†Æ U/¶+l„¥›Ò)â{Y8ñ™ßæ!’ŒÅob
+gú×Ttªá'ɯâIøƒ«ì^Á$0ךù'cB"„ÂCŠ =DÈð€Bò¯á©¢¥òcÉSô«.E%ë ´÷-i’Ûù¬ m½ã…}4gÀ‡€»ì6 s”ð‘pô[€Ü
+ÅeÞMêë‰ áM½ÃáÁæà ëˆS\าœæÑñ`$îÄ @ýHZª^‚]¨l Œ‚³\à!E—â³!dÖËÏa"`RÀ¬s¦cŽ6|y&°d¥‰c ‚–ôùSäl¡ãP¤.áq:˜jk𗢑•$q©q&øba0«Û[¥¹k 1Kd öËY’(9|h ”þa¸c*{1Žph_þøêÄ/£?9z@¦t&¶X.æzK€¾‘ضxAþY¥y•@“…ó6x2\Æ·º¿®"úit´"´m‘°ÄÉaøM
+f0f…¾®^|U£YÄ’|Pt„¢Xæ½ë—"tõLÚѼvýÀß+Ú6­wÉOEUÎe°þðtØ$¦?Dæ^œâÓ s÷·ÓT"úGøs´#/ %ŽÜö´ J™-Zèç0=¹y“‘‘‘˜Ægár‰ƒXNÚæW5…J“{¼ÑQaG"Ž÷Ø%Ï6„
+^®ûfÑ'Z8o×SÑÛÃ÷‡Žé}À;ÙI?nŠ0œ„Ž†¹­”L¢LÁû‰Ð€â´ «Ôa&2âU+ÿkÝŒøèÊŒ÷š%<“¤e.PÌ_ö -Ìí¨>”°IQ^ÁÒŠQÔ0c¸NHÿ¬b” Úô`”(0Z2ÖÏ9×¢¶`y,à_Sk˜hNZ0:ey8Y%lÎÙ¡(Ðï•!Õ?§(åkîeóX:D)§U"ѣÇA˜&üÈG§ß^Že§&Ò—Û"ÚðKx¡—uOìFå®M~*ñ«ž†u=ç2ñë×\szÕ3
+G!ʃ­D[rd`gk˜eæY‚ÌâïÏí8
+Ⱦ"ø¼þ8f^AÏ\mÉð·‹žðý;[ªeþã/tùÏ¿üñ¿hÊ—ü‚a›Â10ɘf!ؼ̽>ò ^M'ÿœ%Þ‘Š²*˜¢*ý«Ñ+ é&ßÞVÅ¿Œ—Iv·DWà=p<hÀXr)Ôþ§¾$žüªP’ÿ™ ¢€l¢q¨/KÞòÚX0 µ‚ÒÔqÅÉX(CðcðÓÝy§¦mïDÉh‚ˆp"lfŒi»ÿ~~F§Ân‹:Yú~‰'¢açkð>è¹(Õæ§×¨JüY¯Éò¹& räø£ý¡kaOÿ7hé
+ÒJ4}¬ã]ø¬Ëdb9Ï/‚ǧŽ¶àlp/‹L†Až­Ú®Û{eô™(hë¡=€Á¥Ç§Sn“r½Ë}àîºíµ,‡Ù¿”öO+ôc*h ¨Z+R¹¾4B†žìéP²å‚ï 2¼ž¹2”—&ðÁ¨tEã² ²,o•Æ¾öG »-͸Zåq¾@4¢«ŠjÙÇ‚èŸQZë= ”)¡YX_‡Ð˜Ö{Ê^;Ú=Fã6Í;ˆ¢ñXÌŽOéj`¯2^³­s4+E?IÉ!82Ní(A]ˆZD‰;nˆŠjp£À{ÊùœˆÄ"ÕüÊd<ÆPr³C#®þqêÖuœ½Êb)lc
+ÁïƒyÆr Çm¦;=½A2HqH>‹XjT‰Eô*£ch{q‚øÓP Ù%Ôöt6QV®Õºo m!¢dR¿LT_òíŠKæ‰×ï/%ä’è‚?ðE&ïuZõ_çDã£ìmÚS!‘•eœí
+¸LènàÛ`úˆ„0Ê sÂÜ7ïö©?B.Lp\©+hùrëŸÌ¯òòÑ£HTÄÅb{.¾º$…ƒhB½ ¤Ô^SŠ¦
+¸§v^!ÞNìhêÏçs´06ȵ½nCæáïÈJ{BÓ\]fÎüMU>L–ë¯Õ¤JZ8 Zã‰ï-ûK—}-½
+€Øˡᯧ<#`ò–#9yÎò˜ÉòF‘‚pdR{Ã^à[;ß þ©<HŸLXi'КJJ9X¨î}€o ¿lÕ…>R>˜´ú»ašÁ¾ãkrÛ_âM\¦ñ0ïD”‡úþÆæYÛlðö‚H¯¡.¾\6u®&¿â{W3ƒgéÞ»yÁD…Z–«BÍönÞ@²ìܧC£¾·é›ûæ}~ËŸ˜DÍ7G9S0Ûü§ÑÀ#@NØ©j0ÜÙ›‰*Áˆpä6w½û½nNÓþñ¯>hœÏr¦ÙCK§ÄÖO±-¹.׈]ú^á\™bÈl÷=åÎÔý1E#! ¬
+I„°iPÿåAÉ!úA¹¤IÒ°lðrZ­7Øœu7N‹'Ð׋’>ÞçéT{Më•»(‰ñ–’8S.$8ú-(ßhË‚PžÄ¼VÄy•Ç>‰±†`dÒ™æìS6An¡¾rÏ¥Yjºæ#¼æäKä‚tòÊ>ÑTÕ€TϪ­¦#{]¶ùô†÷¨K»*NÍÁ`³Ò‡¨éóÁßw&
+nðûö¥c³Î)ºŒ9FhíYCW)-Ëiù‡ Z#»xl¹ƒÂ¬7‚ÿÇ
+‡'Ð}Y=lˆ²Y¼V–˜ @…üé‘BÍ䧖ÄÁ 4>ª×ûéè]ÐÉ+dJ) Õc:I¢Í·ß“%;µœõöÐû»n3f\¦ÒõêÓ9-’­éÚ~›¤p˜¥r>fì@êX TË_%+Ì” q›‘`%R*:¸ÇÒáæ’û9¾-ù>^¬W’80¶-ŸÆ”½¡Ã‡åTÿŽéƒ\u§ŠÑֺ߈ãoAl¾±™ÔÕö`Ü@xB¶õÃûN *CÎæA2 L) u·±ÇU2*¶g¯^\Q 軌~DNr°¦Öy¹J:@|*`6…¯ötNY×g• ¢i*¤œÝ™PœºïSÔh¯§±¿DØ`"ìéǼ’úÝZóá⛵(eÆè¥i"
+
+Þ×Ü\çOFÚ!Î9å5°tè(NØÅO§1Äno‚‡ßø•L±ƒø!»Ù‡’ŽçÀ?qé•z âÁ¡¨©Rkƒ}ƒß*üUt£Ÿ dDzÃ{ÇÔ[ T¹À±¸¨ʪØ–Ö!°YàB!»³“"ƒ©\½
+é\™ ÐaI«W—퀴Táz*ØAæœUÜ_¹BxOÇê”aû®—k`*”bˆ1Ç·tv§°¤öãUµ÷š
+vßA×ùûþ›!ýú•çøú‹ÍF…Ü éU«5ÜÅšçˆó‚˜¶ñ>„Í,ó„ €d2æ 6^óGË‚g‘(ILø ,ê*dÒÜ u)x¦ ˆÚ`ª¹ŽÁöN׿ÆGa„Å ¦Õ„_%´žûæÄ¿„Âr©ðp!óÏŠØ9-Èdpç¹’‚7È62êƇØKyØËÕ´(Š[ÂtGïû½@ønÌë[ª€/â÷,K~eÚˆE5»÷`Ìð]Ÿô§3–8žŸºö‘6sZ{:ç½ ˜þ‡t¤qlgœêSd*61ߦB• tYŸ_¼ÍV„º´ÀÚ„°ªÄwå×àü~¿~eT-¹ «½:5í?@p
+¾ÿ”è/X_ðøãå´ Èy:È°ýŒª‡m±C^“ŸKlž‡å~çiäÞmž ã¼ê
+ÁKw®@¾´Ø+ù0ªoŸÙšZW—¯
+êW¾|eÒâwsLË®HL¶®‰:±óí‹Wä8HŠJ¦Ó8PÚ§‹`gpüs§¸SšWô!ï­9-¬1ß~h «T©¬V’¨GÄgŽŽ³¶økÁÊt ¨
+“5–h6”kð XóÀf¨¼(CÓÜŒË4·$¾²
+¢yLîˆç¨KQÑØa?ŒUÜß“Dp˜¼Œ²J0>p9m¯”¼Zç4Ùµ³u„Iºo¶X¡‹“ÒØ2X±± ƒÇšO`#pÖô 3¦”YË´±E§Ä'§§¢?nŠ
+4ñcdÕ÷›¢”ÔDÿÁrl %òGÙþN‡a¹g¼*Á‹ i‚º@€eL¥<°Œ²Ãùh“é2²Ç›ä[Ã)t°U€[ üläi»œ•ö€qJì$È~
+K£ç»ï|¤ud»éŽÅJWr‡çÝW—p¡1šÓ´C‰®²éÂKM6tÆ
+Yö›&2.ÚlVæRƒã@„Ð6&éÝŠ”$³ŒR? ÉP·ÈvÖå)µ¯øÕ™ÝÌ»‚Œ&‰]8ÁvTvšá´skÑÏûþ!¶À©U‚Ð`]®ª&ø úÄfâ/ÛÖëKÑÅﮢŽFYË|Lþ3E?/Æ7§ý²æÙ¾ßñz†ß`©´`O ÿ·(ŠÅã™WÐv±Ä$â± V1d×ð” E%CaŠœW‹?{`Up¨E<z ü“Í"ó(xCäç”ØQ,1ä |»çÅV ´\!Ý´)^%¸ ȘöâÌáùÂoÞ°\œqK…†+RÙoö#ÙÓé¹Üí7ÆHTœ2Ž†Øç•ØÓy¾£¸~GÚ¯D[âãÊ´4¿è§€& DÂÅ×Ø…QÃ$;%f[GQ4¸}—á+Ç[bŠ'H
+öwD«á?ËÆ2¨£ZÜ'']Š¸S#«‘ÖÞoÖÆ«¹ìŸwØ'Šž8Á]®ç¢Å/+”.ëÎzxŽ…üà¿ßUàØæÚ /%”ÃùŠIǯ6aÉA—v e´CRæ¬ü·}ð@0ÁÚqóQ®)6¯Ûwä<¹BvÊGð[cÍ
+¯Œ¦ÓŽÊ{Y½".ÆWÐ:.d÷¡}`/lzÈ^.ƒôö¼KÐ+.Øp¢V’ð1)Ìe×O—)š ²0—´\
+j ÐìédÊ̶ Q¾[}n²gV/³r>s™øõ2äxº ðÚœ Ž¯-vJ>ŒêÛg–Fûö~5|¹—ÚÏ}÷kÊl
+¥Ç}§À?¼“{2
+¶E˘!+á°f­MÇoÅI¬Áßoî[ñ¢@³Nƒxxc„¾EtÆ~qšÔÀáø:×¹~ˆr8„;³X‹&t%Cˆ×GM˜FdÄ=Œ‚¦(¤ävTEÚkB7Õ ‹¢VM6l—Ðb ç+ÌC/¦ÖÔà©èiìï7Elëà?¸bû0 Ñ rÑx@
+k€+íå#2aLÃñ«„~a. ¬Óä¶(¶1ÓAþÅèóÜßA=¢BÓÕUe‡*/â~A…ó°…©á KŠºKÚØÖòh°àÈö&LúÁys¹£AMä.“m!ˆñö¹)¹½Î
+H‰”é;Û ÇOd$²‰D‚‘ˆ}‚ –¢–Ä®BVRB„Ð!´z{ÑV/ÚÚÚªª¥EZmu}\zuzïóÌÌ3óbþ“ùͼ8oÏ÷œÏ÷|Ï—ãí=ÛÅU{KÀZ[ÐÖN´3¤Ox: úå’Ò×Vš‰1ä¦bóD}V’Wg^2Þ,OĽš0œoXMg[6ã÷Ý+¶ãGfåÇùÇemòñýË9_Víõ/nXRwÜÝ †zbWú[…³œau6­IƒmÌLñHâ³A@'CFH GOa&}¦½8dy°:õ莮ú|³Wó}kÀøcÇaù¼l-ÿ´Ü©ü¾i7üÜì<Ýì®~u³Yüt¨.ðù˜1ö™«#qD]L¨MˆA5%' :ór±úìD¬¹0Ý«»8Ì‘ñY¨²èH¼>_æ;¦*Àœï*:Ûttžn9­—jÞÍéÓ_ݬMx¿¨ÍÙÒE¿¾cÉÚ6Å?r¶ðÕVf:ÚEÎú
+òªßSšï+ ™>žà€>ê1nÅÑñ÷WC“ÿz×?ü}ËTq8W½sC)úëuo÷ù¾säëÆPçû¥®Â“õåù¶µñ÷ {Û§e[Í»»–úÇî¶,gs·¯,—èR)}ŒŠ’:9Š0\§`®ôù=¼È=š3–þóhpü?'à ‡«öÚû.£`ª»‰êÔªñÖºjL]n6dEE@(“2 ÂüÀó£€7¾D ù JFÝД‘·Uœ?w,5g»Ö¦ý{¦Ô½»ýEëSŽœ{#]¢kFÁÑXŽ·U—bšr3P™<…r Dª„XH å‚î ŠÈ`ˆ  ò0Ô¦„£Fkr}ö®ë¥ÛW[BÚ¶F a¯§ûs×'ìÉ3–.N_]“·¶äª*=Jbã!‰Ï™qþþÉã@ ›T‚' €â‰Eæ&@,› ˆè¶zÎÁŒ)þh¡Wy´4jØœ,XqõDÝ0éX¿¶ë™“z ÉZqS“–„RFI¡":ԱѠŒ8€ Á‰84xâÿXÀ ZÛœÐF¯é³¿më ¥{׫9§Ö†ÏŽöçs¶¢¥QsÄ5C³oWy!¦-'Õ&K]V
+d ¹ aÑ!’é¼7=1L#@ ²,œÐÈéϯ7„y¨Ïz·Ôûeµ=÷ÍL›ôàf{âŠSrÛÒBskj eæRVT%H ?R
+i¦êíxŒ°‰8°îÒ
+E"
+î÷řڙ …yèt‰âùPŠ% b±Æ^à‹ÃA 4"ÂüÙ ˆNðÈ‹’bFj ý¦Ûå'k=ªãÅŽœ÷ ²×Ó¦Äý©®”…~#ﶭëÔÔ“jåY¨’Äx(ŒŽ…$
+Dl1XðöFxøC _T¢/ò/H 
+P•q¤E{•øìI勵í;ú~¼rØÏ÷ÌgÛ}š+Êõ‰–ÇWÍÒ1]3£>3SÅâH$ËL !Y¦y⑲î~@ó!‚?2wx
+$ñ9!7ì^®å\|Ïíõõ_¿ÊÌü² ý ¤ði>$ýN:äÚR!¥O_%#;n’Ú»Œ0¨Ï ljîʘ„±É¤[
+e@šò‹!ï‹+/•dç$gÜûò&73 ÝT•"êHË<䮕…:ö20±&wÇÙß¿
+â¾ELŽ:„ô=»kÚb![T;£>‘|Ï wlÌö°‹‹[·³k"huû'eˆÙAúëù! rÅ0Ä[›„f†Y­¹:vl“ËÎì
+›•­óþ®b±ÊY s?ãåª^ÉxF'~p,îJO‚rýéŠt2î€Ç>6=:GGì8)Í+Ó„²ÙÄÿÙ”¬"§È_šµúõѼVüÉ­!ÌiéUæ~ê‹ñk8ØŽáA”YÍ/÷jÙõ«>öhžËøÝÏåý¾ÌœyI¨­Ìë_¸5 4‡­BbÂSRÀHoôip9“„Ò câ»D ·µ„×WfÐKcŒ†¹©i¥E£‚™xeî1áǽ‚¶Ë.Nïæ$ªÄ%(»¾¥ƒe}^8ëJ_|‘Òuà$5Çf9Ä ;«ËoâwÙGùµf5¯Ì¢”@­*F¾cìtÎ 1g Š¿ž Lõ/kñyqô¯‡³\rØÊÆ5Èg»Ó¨—ÿÚ–l'‹àpÌ+æ‡|â~‚ v ½å6)ü)uõÄ
+`ãq×Ø[µgê.Ù5`‹¢ŽDWí¤Æ¸‡:ñ Åû.fç‘+Ñ»yµélQe<_ÏüBIØÍDïÚ‰õk:Ô‹M}wɺ±§!Á
+­‹|î±Gĉ;e‰ìÒÛÎæúšOç‰ð];îí¾“{dýf çÈÁí ;Ù˜M ¶ŠèG=j$ÔF&ûJ·Æñ…kÄÒmc?k+¡ÐŒµmôlNsÛÖÇÉ•Q»óÔÍ£XqMÿXâK/UÖóÙè‘›Ž>òÒÚì4Øž €ïLŽ=|ê±WÀŒºØ=G^±8b—rœRÞ®ùi]/ÞÐãŠÃbÍ®¡±Kc@upXеZŽW$ÚØ<“Èj]ÔI„Ålè’íÉ–ôCæÍož0æaã#1¾(VE—ÀÑu¯L½éQl¸G6Rº’X˜â¶„LbÌŽƒ;õQ±ç‹\æ…Ãøï&WþŸm€î§á£nâûØ,wäH›Oùm™ÑûO?µç|–ØzᣢÜÔÖ°‰Þb#ÚÊþªå"›CÎá)Ÿ…ßt߬TXÈÊBœ,°óe™îlY¨8ðâZN—ùêÏAõÂY@<qQ>&˜‹q¶”ஹ„·zס‡ƒÛ6êVGº2´®ø¼T½6ŽÍ÷©þGq•}5•¤ñ×yœ93v;ÎC{qFlÛnŠJ+ƒˆ‚ì›bÙHÈJnö´‘M‚ ²ï Cزçf»¹I€nçØÿÉ\žªN=|ç«ú~[ |{ %<òiÇ"îhÌ!åF]üUiçõ=„xÒF)¨Ô”줦âÎH]Æ8òøDG¬wØ=%‡ˆT´ ñ•]©°àТzìÝÓµ×5á…¶ú¦Û¯[DO¯D{ï½ê€NÂ5NcŬ<zé“Æû&”y7Ñ×nI_üÕ¢hü:m&tŸN,gv§–¶çjfþ÷ðär9(“C> €èÌ›#¾Î÷¶÷?¡•ÁšlH,Ê~Rk£H WÄ(e’œO0Zô³Ñy'½+k¡µDuÃw×1?žÊç!·æCÎú~-g{·’ui–òN6kfµÅ Œç! ¶&e¦wz1¿x4Ø1=³)i¢5åœìÁœƒÙ š©ÍEw¤à`÷Áîàà[+ì×.9PI… çø@ÌL3Ò^¤,¬^›¦çƶ´ñÖYhròÏ®æìJ^ÚÈhŽë1wܽÄ&­)cCzZ'î¿ýHé*ùÔ ×ÜbÎ1;Ÿ±‰Èû Ýß/£nF׉¢[ãf¹É¥¦t§w
+®šZ:Ù2Û„úª—…­
+áÙ¹O&.ØE£éÁ**i¤õ€6
+UÞ?g(}š\*Èg+ad ɧ@·ˆ^«ËÙÇ_W<Rl“þöÈE·Œ_ô ™¥àÄÂYDµy~$Î{
+¹è=+µ7ª£ü7¦“a£«¼^pc¼¹`d·gôÔúØê#p Ù¨ñµ7·»ÿ•0à‚f ÷xÑ—Ûâab[¬öC=¥þp^ŸÞ 淅ؼƒOJš¾KîUüsÚ3$×À>1òWduÄMŒ–“­±úÄ6€`yB ù%B$sp@—ròNªªö›ª›Ò Ù±µ)#þaÔ@zÕkS*ðŠË¼+Í3ÃçÌ¿=“Ó$§K¦í9í¤mFÛ&¦VS5*šE¨·ˆŠ‚²ï›€ ˜Æ=nàÊŽ.¸!‚l/« Í$™2O¾Áûœ÷þ]÷uOQ·¸1Û<jà ÀkHOï¥Tí)ˆùrBÉ£"2嚈Ûe#•Ù2t¿
+Åȼo.åÁefùþ_è{Îá¦ï첆ow$µ_9$U·.”ÙA«Î8ƒ5—d|6ß]ñsPÍëlŠ(c>fä´6p¥‡su÷|*t~Ü&`†·ØxÀ.AÄÌnë)ð˜‰P–´t—E ØÒ“EdÎñl{NT/¤G¬Íù |à-xÊDoˆëY¨°žú2Ò~-å¹g• ³½Aþp²L®´L`^¸fÚEõÜß±ø\yྗÐЫRf:iå~òkQÊÞßs ¶Âå6·Ë¿A|zø÷sµæT豃¬©øTŸŽGöhXÈ ðÚ¤CÄ‚Œì¶€ŽVé9è†]TQ_€¾.‹lò0—6S›¤?n—OÆÙ…ŽQåSÓá§s¸sOÅçÑURÍ•mPL‚ÌÜΠŽ¹[&f‘’fÁ¯#Õxµä—=§5e— ÿïX¦x »Þ÷$ìltÄŒ-½ÐvæyV;³}*lÁ…ªûqPM)‡4ÌúÀ:µ,´ÉnŒ·…ôÌfï±Ü£À–D­"Öþ IoȽ±?K©¿v¨.w%£3ÔSª|Z&ð¨øÛ(È×£¢ÁÏÖµç*N‹|»ý ò¡]Öø³w—{<Örß·Ô]Ô0çq·è-W®þ‘ôa n§µBfüb«­À§k~”Þ¥¶½;°Fö+çLÇ^5uÇ€Û§]Ò‰ëcÉDÂÁÄD̸ç'
+Ô}Çä«{Q›Pߤ"Þ¥ƒÏY¨¸´K ¼vqh +u¾‚+>˜nÿÍ«aÔî/2^n‹ê‚º‰±¨Q>˜°ö±’2là¢ü*zyXÅ­OĤ %ùIh…Q“ÔòÛN'Û3½óíY®)dæÖ@ÙmCß³:d ÷Ý« „m¼ó¡k›u¾Ænõª{0þ .îíM:x®¦5he5?ÌuW§A†AV[Ÿ¸
+f?8Æ+¾ lt-aŠÒ껑M*ê𞘅PÚhÍñ*ë2Ž¦Ÿß±H oO6Ý‹i¹¨ {0OÌ‹د®,"JÊÜÏéXç+¤"Ï"!d¥Ì½€y •5e0^f}¶H‡}7õÒÜK”'ÇS­™go›~ .·gy¿Ï×d\Zé]1#ŸP+â ÷áMVëùzwY|—ÖqéâP »Âa{º#ëh‘X‘ØÑÒ`_xuì&—’Tä_§<»XÁ¸Ð†êíòúŸ -¯ÎÛŽ;úAÖ[Ü‹Ä×hë¿÷dÕ_@KX˜OÙYêYDå¦}`ÏÊÃ[<üÑLG¡o© v8Û’±7Ó|/´Å@^ïͦұäv/ÿjoPžÚÐCFü¿_äW`!-®8n$¿õ/wþ\íÎmð‘—;}¼˜…×ÑSžE\þ±‚sÎ
+¦9ˆßÜ+<ZÔ.Ÿ
+š…”Sðý~°"f.åŒ\:å³îuJùé:½2K$aÛðëS5iyÉs¾FçWhe§³íýkا€'eGŠ®œ‹ JuÒÁ¥'d|›X5Ó‘ÉJóÕ½óo‹
+ºâ;b~Ú5ø:` WBF"< !½ëMq{oßÑ2±4jæSÒã–Ô¡l.¾Ë§ $ø¹ŠüÔ½H(9UtÃBz6Ò9ƒÍµO²àyz2È!£˜ÕsšV!%fdÖ…´ÄgG3¯2-å_Ï ºÆ:øæpùç3ØlÏ4¸‡Qd†WÑ‘ÔÓsÿ§¸Ì¿’N÷8>gšÛ4ÍÔ©{gZlÑkåÒªe™–“{nhŠ²(ŠàÈ.« ûânî[¦)ˆ"¢"((".È&‚(‚V§Ó¹ÿÉýöÓ÷—ç|Ïsžçy^¯w[É}lñý_ljy'ƶ¯ZDñÌój}
+kö–::=‹œ=eÔ7Km#o @c½
+lêÁ,>Í>Q¿ÔœyÍÔS~jbQ>¯7÷jI¥žÙÚ7>51ß1¾Ô2Ð=*LöŽ÷àV‘söGŸ uⱎƒ< í›'å»”¸ì1t‘ŽÏ ®õéü«ÍíGæÆ®£µÆ>¿`©žMtβoÈw«ð™îy*اó=ù@/¯6uúÔÂzÏ %Ç)#¼1tÀî™:¡w¥üÜ«´w1gWÞ#2RJž¡õÝ KoY´ýêŬ$÷ŠRœwÉØ^zódM
+Ûݳ´BÀ× ´)°éîèG&>¯ …–qzŽ®£:Ê=M‡…V 69õíö6as°úáZ;äîÎ0ò‰uœô÷¸
+VY`¡}²,`œÎd©Â-C?7w„ӒΙkâÖÿyjhGÞÛ“r+ê÷ï=3ÄÆ
+Z‰±½â¡¾µì±kþÊÔ ‰”Ö—…¯ö ƒzfíjwÁµÍðï<æí®¼:Ù!EýX`c4‚:u,jVX|7°,~±´~ ®pëlŸðI;£¸$Ç81ëTË!ûçÙøÉzÐ-5’à“Ì}¸D%óÝŸò:Ð% r]+ß4wBÚ†qÛ“LHIâ­Ÿé ×wG™¤c}SËöhi”¡)ë’©%ï?ŽIDÜé*—y¨ðG¸EO»°/ÿúß®d0 §¡ÜRdüzKñUM}ö õé¿ou#NæéPã
+2øëjSÛçï«‘ùÏþ :eo
+ùÜ.«Œ|ÈH#Ç®äV¸ÌâC5»zk°:^/]×ps.ɉÿú„}ú‹’zÍ.G~ßõJyÙ÷ÌCð”‘C=r}bCà?35oø©×zÁ1GúÜÁ2Ÿìšk@ìLÓò¼& çÑO Ô7µ"¸Ä±†øÎ7G(õ.ÑÐ!k˧C³¨Ã½Ä¨ð.±jÜ€_;gI…ËdXhKÛ_âb]ó ˆƒ¥¦Æ-)ÔK„! —Ô|œgŠsʨo-È­á–1Ž b¦®tÝÔ
+ºií/»£f…YÇ؟ƦW„|»ŠW»9Îxëã×´<‰‘‰ù²Î$~±°¡UÜ6YõÄ1IÌ<ÔrIrÜ;C6Ó=%"ì+Ĥ€ZX·¯ çY¸­de_PÒ²³MÓ‹d|ð$Ê+ãTîɨ9ëÝ•Ñ[U<rÌߺö’pµ8ÿ/K?2þTßØâœÆg˜zaQæ¡Ê¸µþª¸žê„=À·Ž5”2û4%×.e–|5µ|37÷úµ (Sùõþ²pKø6àK1žFÉ®”š«oEFÚ†Éiþ9Ê.Å¿ÞÁ¾˜#þÛL?/‚$žÁ¿½âc€d"ÄZQü¹Åføë†Umè,ºÑ…~r¦>çöOÓÄøó®Qècï4r
+t‡
+_¤ë*Ž éù¬S³¤Å§][_\æà½*jéÎòq@Ë!~]oêøŒ-ê×ð)Gjü@YWlû€N¶”?4¶Á#¬ƒødþDRþêºu“âšdÁ\“b¼oŠšë“‘òlÄ‚ÅFXlgU⯠bhÊ¡J$<P±qæ啶’èTògS=!¨çÓ÷՜څ2^nÔçòªéð=91Ëù‰øU¦c ð˜áꇆ¦ÊØ)zþUèñ/uéÑß×¹þZéÎǪöqäsÏ4±ðH‹/únãüŸâò~J3[ãøäfî½Ifc¶Åì®IîšbÊjLÖ˜¨1ɵÅÞÅÒ,HGzQ,«ÑX£k‹
+PŠ Eì‚ (¢¨Ä½wî?rÏþðÎ;sfÞ9ïœó<ŸïçáÌÀifðék“øâó•n鱎Cs~ªŒÝͿ阂Æا`±A#ãÕ63œ³ÌR¯Š_ëQ°«?âÒ/·Ç_Á¦\š¥§^Û€ÄÕtøksÛ©•Ëe—Üý›J”“pfaQ}ZbÅæ2îPO,uÏÕf.õ@€ßÒP_€?¹TÍtM6z}†šç5÷Hk}ú/k|¡OŽIÚ,ŠX(Š°öEzµ¸B¿žŠØ£<z
+"°Þ9}¶)l?ß´ž¯ šŒl¼G˨
+Ï<)ׯ¡WyÔš]LÈö*˜5'`– ê¨=:~¥¯êNÍ¿/QÒï]D]´´çÿrndœÂ;ŸŠZÐ0ÁEÙ§%—ztu;2à\Ÿ1IA#ŸZîñ-0±[£Gû
+LFÐÀƒ½ààìþgzÕÖXmâžœ\ÅÎ0K#
+9ÙäQP¡†È»Š^©éC?uÎÒ‘Ç‹m|§˜˜±Þ_rsgr×9 ]í)¾½;ݘåÓó·ÀŒcŸÆ¾ðʳÁ9A<j&¬|jÀÄ£ø–X >qñÀ
+·˜÷Ï7Uût,ÌÆ4>yðÒ£ofí+› ®ÙòÇ+C×õ])_Ë9‰—}Jré‘Qò\¼6Š{¹
+d|È"D»ŒU~ fãN,¢®ó5ÑûÀbÊÔ ¹«á—„[zªî¸fIÙn¯Æ8ÐçS73Á°6†ªõ£/ ½4G{óO-/ûk´.ë¿6aŸc¡Õ‹¹ê˜ •†Lƒ!qf$Vž,Õ9ÄåÏL}Yæþ¬ðÐJë‡s¯ÔµÐ&rʧæöÁ¯éÌÈ%iŸ›f/vI«_}YkéÙ[lïrÎ ¦ÖŽc+áWS˽sø ç.i¼15¼­8æ²[×ÂÞž¥å{4ŒÏmûÔ˜¼ jÉ>Zk.¿c,ŽôHñ.%µRÛ[}o{Š q+ø v)1sGJ|g—àß8dõoöÔ„œƒ%NƒßÐ=0öÎLÝ'Ëï'N—[ZCÖæðn9ÿ°
+Ø1#‡œÜä²âf6ýÐ@oñiå–ñº[eTѾ–ÑXà ´£ò¸~T ¼¦;¤°kÆAÐy1:ãg×$:×&k/|‹/8íxÓt7¾Î£G×xôðJ ƒ „o [alùs¥–òÉÂáŸlq{<jÀ[¿äc—õÁ$?ÖpÆ3G,rIZÓKÌfR@3Žà{éЈžK‰®tµ©8¨Ã….„oŠRõ€.JiÅCN»SÚ^¡î©ºàSË
+R¹W†ËÝšÀg«„õ×ðÌS.1®Ð=M.²·¥ûÚK\R\¾e¼þ¶k¦é‘_E(øH]‡z¢î¯K<¿ûƒã¥8 póHOƒ{PÙÞlgó̧¦7X& yÀœ^x¼˜©›X&W[F!¿.sËΨ:Á§G1~4 W¥h™9š6Šz6d· ¾ àGù5-aÀ -èžg>÷,qÚ>Ä-§„ Ž
+«ð‡÷Ø{§7Þ6Ý ‚R_?ó•i´.û“Žªó\“UizQÍ/<è  ©ßydõ?ïðöu´ÆÝE\Ðíæ|°òŸ\]]$䶼:×1 }è¡žxq3Ÿ^çÓ×yÔ˜ž×‘\á2ÿ±²¨Ÿ] BÒJÅ8$ˆGn)µÐVvX/ ë{» Â×GÆ^ax…Ï8PP€ŒÄˆ¬’j‚*,È+oËôÎàr€œ}×ðºñªU„¸céoüm]
+B ‚P–)j¨¡bcgâ»?å¾þagvÙÙÙ}ûÞçE Hx—ÚþÁÂHÚEÒØž`,jäà?Ú¸,ÿ&±*¦c€þÁødçÐNõ<òÑæYïC¼›b—nrZÒ…-ùßÊp ŸÂr>âd•åmH3IJSc éî%t–c¦óéÉrgNRÏB®“*Nôú°–ÕTaŠO·º ½ð­¨ô¦f üÒÉJ[æ';ƒæÓQ;*TQ`‘R#KFRÇ;­ó˵óó7³0mzÓW~ëøô3½Hrªæ“lÝYR\ùÅ dQŠ–ßòï)Lù5IKá%û®äp…ÚŸD<:˜éʜƼ¹²Á,»T’›üJvo–Y6MªLQ‹ ÙŸÔõ f24nÁ7GM@—¸ò ¡4²P1›p4±Ç&^NÚ‡×bVñxÌ*>Þß1בah½ooè”âA#¨œ«
+nÅí œÕè•b²}¥)vÁEÛPí]—ž5O.IÙäƒÄÎçSâªìÓ­÷ís­÷60¯"»t(Ð3Ø1³pêüP¢ŠØ84Ÿ†ôoKxQý¤â|¿o0l`À³vDó&Í\ZÌÂrTæ ÃàdXÀù·ðeï–áY†¡Æ´ýyTŽo›Ö–´÷ŠŽUx°}]6ñâNÁLÈÈÁ…L¼OCoõ'a ›E#½Hp¬ê„u^Â$<·ô‹BZv—KN¯Ü—qê]Ë=Çëäjë$ìZ̓Ša/R#Z2:´ƒ­>Qt¾t-@ŸZÇêÓO”Ä·‚qʘí
+ :³öó‹ì:ó1?°Ëãþu0´Û¥e¦Ž˜…‰KÚûcæ~IÒÔÏ‹é¸Ø¨¶4jÃZÚ¯a¡ÛT(p/]uâ›ðµ:¨¤Ôú×Я-#Õ·L¢†4Ç$µp¸£ø‡E|ÉE îç5fåÍ%lIÊåí%û0ä®gXªF<pLáJº>šWÅì:3ré »d!lˆ½ZzË1àñ®Å®œ£5\‰ZXu+¬$AÏÌâ±ÀàSÊ-’ÖŸ'áù—4=ýf‹S›JkÈü¥ì·omƒÍ9¡-BUp[ÜÆ•{äð¼ýiP†^{YEþÝÁ$ãPN®T k®ui®©Žç@ÏÍ‹íêÿr’-xðÑ$#¤Å‚þ|úO§x鋃×w¦ìÈ É›©E <ӣĔYF ·Odt°w“ƒÕ!ž @ /C²ï|7ДÁ&lK?œ‚Ý÷¬¢^Ocr¿Ÿí~™r0I(òË{s°ÇìÊŸ4Bð‡õ\;ØõtOJkèD>-ìWâ*CZ.aâöœïñú’62ñÌ€ïï¡Á(¤£w&÷%+g{C!Ÿy°Ò]â^iyR!òÃZl¹qU8Š*KÓ‰‘yÇKœ&ÇHçC¶àljöç?*H¯®z__=Y€fÆ t¬j¼1“Ûðä‚w‰Õß‹Âz9ä|DGGŒpò€IZ)íNöÍ,£2}¸+÷Ú¹K¼·(〖O5„Z÷\ËCÏ*2'¨&Ö
+ºVŠ{uÅ«DÖ$, lÂÌÂ'ö˜=q;ÝŵyW¿æ(©2aŒž™Óžm>ô¨S=ãQÓÀÇjf‡`Ž¨U ò{ô½‚Ñì^¥6Äícš¨mÒsŒ¢¶QEÂ*D ¬.'Ðß)™¥zÏ0ì­ã‹M³Ý5å“‹<‹Ý™îÙ¶{®±¦[ÚÞ¢‹j~íinëõÜ{—þ6
+AIDqu;§çÜä~ýöùüö|>çy¿žçåÑ6ÖøÁ¼m™ß$“­ S³õ}¤T^Ú´1ëQraw:áÏ/÷ÀSÿ㪹4Í êHUƒô72ãnþKÙ^™sbiâ†×$÷<µdäÜžªöñžžylà“«’f«œP0Ç/º¶þšê5Êd!›¬?èhnóïIzƒzÞ<ÉÎßV
+ê½za[ÕP¶ˆ;#Ö~ãgëPÄ*<Ëècê3±ë=K¬2Ókt
+µ éë€NÆóÌòª÷Ì<·œ²Ù‡ŠµtÂî¬tbc‡y57ê3c.Œ³
+ÜSä´8J-*¸âDĸäðxßêiÄ À -€ANxË¿ý¥£Y0Ê×|Æ¡Å°¹kìÔÖ,=1‹Ï÷Ž­YnÙ¨™ØM¿féÁ5µ‡“´÷
+r¸#§íNã3íãȇjiÍ}K7¿L'¡díŽaŸFLLÌ‘¦}8ÃA­p)g«Bàp‘”·7‹þ+¤cT©%×xôϲ޳Uï:Ô%¯OÙŸÆÿíRÒþî$gF)xœÃô G?2Î(-º1Ï…\ÛⲬ6å»q"䎥ùp„Yôsyâ­8iÁÅs‡!f»§™ö×è‡;°øUM’[Y—ä[àÖÚGq w&‰Ù‘5€|jf#f
+…»oëSæÙY×W¤EÑ[#˜ä ¨
+ê8ð…–šèõ>Zªw¾ê] Û†Ik=µ÷œcˆx·
+ûä@î=Ôòþe‰À5Ë©óš…þ‰Ð£3bà7œ8„àGpÏs*<
+ô„Sf_Îʵôá)hﱩW»PÉQ²º‡·»ÑßvSËÓ­
+Ég`£Ý*zÅÁ’ ÁkÑ‚&6Ê«!•ú4,˜WÃ*·ÊÑ)ÆnØýµ7¤¬Ã‰Ì­gÒÜzÒ·.íé¹·–‡Ú3¶ˆŽAï;Ðà
+6&*ãü:6É&gQÞÛÅmŸv$}Çf.íÔΡøuÄ¢mêÏí)<DÛ¹¯:÷~ÅÁ¬¨ng‚±vc~QE—°9_ â2þ­$ç\9Tpë6dzܗ—öäøŒ£9z¾žúâHM+ô¨ˆù;JFáê03s ¨|à™ä![ŸÖonƒsýÄÂ%›ÄŸ^ÂÙS·ŠVjF<ïGg^à!7úê ÉÖÌ/:^ÅkKå̓·ä,ç륨þÙ•nìó[GZ>|Ì«&f;Fj~äå] ß[‡êâvÄ ¹ž^ö$æ›NNì'W‡*´Ü*;ù6СÖ7ˆDí«Âë®·µ:>®Ÿ”yy¥š÷ÁÜ6|¶Ú">ÑР–Q꟧Zú1ñšWèØ^Jñ㌢|Ó"œkü—^R²m€údkˆ•»$©»×QŸöMkeâ?=$ÈÊ+hœ©­üŽŽ˜XbÁjFIÄ
+L®J’óÓþæÅ{;˜Á5-°&‚kBÎÇMéÀ‘‘\¶9‰N’óKòãoìÏ4äEVè­Qdš{ õ$¢#ïNÀSW;Š£åÄÔ/탄”ÚËXVñï—æD%?‡M
+âÚ8NEêUSìeÄÖ9u°È®qÊRw„çA
+l2LŠJ¬i,Ý÷@‰ÏrªØˆ¹æÊ × äž`ì«)`·†qkXÕZrJ˜é»Ù‡L:7 k.pO²÷ Ô‚ï&6úO«ôË‘‰÷X¬­±Z”NŠŒ>Ðs*=³ÜÛ !Ñ,)|2%ʾû—è×ONýus
+Ï—¤KãtVÄ?›“H=æα‹8P³ò÷ƹ…smÈ—sMØPµ
+zú}CCîßg>UÄìkxµgý†Œ&ø±&j¾°
+xn dFDnÈ+Î̬Øÿú
+è2–n\¼ÛX‹>²Ô<ólü)ýžzéñ £|¡»*\ßT¸9L†^¬Ie. ³pk°:n£˜¼ÚKNÓŠJ_ Ô€üf%…/÷'YðfZî¯U©a>º\þ¶Š׊A¾“‚„ÇS©K=£O¦´“‰x[ÇAS‹ÞÝäfEþX6xfüÔ’³íòª×ÖÎÂ'Q†ŸA”õlkœY@„ÆÜ¥äÄ=uZML”¥òl„wK”|µ­8äÊÊ'HÀ±‘QîPó‘¹ïŸ_VƇœ˜X´¬‹ZÏæYxû0<d­öÛÞ6ùÂ*nsiy¤Ñ:Ès£úübA(:4sÑß ,ø¶‚’åW¿–¿Xë­ ÝP0³YÐhobæ«ë3e‰nÀO7åèTë—’(SKaà×ÐMôõU%7åS‰LÝÕ3íJdô¡±è[­}çËZ`f§V~-“
+š4¢üp·ŠXþÝ.ï.
+Ì%ÃįŽ–=4aìS¼*)£*Â1JÍ>Z` fÑàÝIdìú”€ oCEÎó`¡³øMmKù–®ÐÚ0ðZ+BުɼÚÉûcšZvÏÒ‰º¿Ò‹yä«~åS
+X:" »¥~}CžGQûæë.q[|ðM8l @»ui܃ßUýݾ¯¥ ¶¤˜”QØ%¯KÙ›o*òÏs ö! [JËû¯cøNßÑ2ÅA½X#AÎax¬oà‘êÛZ קãÔü°óÛMœ&§œY¸;Ï®ÝÕKŽÍLàÜÁk;±rè¾yRþž†Ü Ôn))ð-枦:ÇHï¸Ä-}j ÷µ-d’S¹%ïflN´×­ 1¡¶~ÂK9#/bœ¹é&¤x䜲} µ¸É=¸¿Ø&Úѳ¨ÛsM(·
+Èû:\°§£Uú¹¤o öÍL'[›É‡f>{GÏ#i™]+,z‚˜}]Ó\tkMRùp_ 䞘¸Ôi#Ä2T“¸3G+u)H™{zRO‡ÏrHk_»Õä‚ÃÁ§¦”œY['6gßLÇýtñû~¸DcßÞq²ÂámÍÒk]³즜žïQÑaß7:¦÷L<Ö‘•Ï?·‹eç+í_έ­œS î7 ¸N#×­àtƒ]në©ŒYéG/µ ¸Çz!*z– [j+¾íÄ>[Ÿl‚X—:jcÖú…°UIwÕj?3}c¼)c¹§,Ê".
+߬{ylàÔû ´J§—².%@ÎlÂŽoËBáÁ’ yßÈmÜž£û—x”C“Hph÷û—lï «Ì«d•9GH[wå!¥}º%!•ßd—|–>E©›K"{1ÉAc à qUÂÅ/¸÷Ásüòk±<@HšiGxˆÔöt²­·âq€~$¤‡¨Y¡ üÒGVqu²£ü^Û‰{E.H f忾¶§$¢×G ­qÔs{ì~u\Ð
+\CeÕ¬”¶D¬WÁ(&Ããí£µkè·¶ÎÊÄ !Ùhbsó^Óò1¼õò’s˜™³ØEÊ5uWðNàÞÎp *øÆ×¼˜“¥¨_‹ƒ:$%­Hñë[Hf ¹x{†„ 0P͉µ õÃFÇŸY(¨s‡æQ6äæðñ`Ž…óýŸâ:ñf{Mã
+°sežY:ìl‰ŽuÏ‘gËþ…EÈ?7óȧf6Ñ¥¥–œšùüo+BáÕª¸å§•ÇºX¤Á½zZ‘{ŒüqÈ“­o”T=>ž%ÏtSÓŒí 1C ˆEŠÝ¬ sªàáyí³´êÅrg]ˆC.*]ék*¶õ0@;£œÒ!dÌf?ì…k½7‰»u ™«ªª0s_Í›Óeßd­ç"<~ƒ×ȧ­´õ·¶+¿[Ûd&
+Ü¥Áfìë™°-5%ow„˜¡JÒouÔeþ¢çÖ­÷s
+¿âS•ÄOL-¨˜i!ê­±­>Æ5BÉÙÕ0Ë6†1‰€Þ[ûÑñ»ôϦ¶ÚP½ö¸±Y{£¸,†YºØÉÞOËXî'§Z‘毘÷Ç3<ÂB쉭»6ÌÞ[­$gÿ>Æ*zaÒ’ä"B(’vk
+ÿÛ)ùÎ85ÙwŸüW)óïs⢗»cÔc:—ùòf/âCðµEØva
+vÇȹ.*ÎÖ‘÷p†“ñpSA€®ªè…ðô?S¼à|‘QwÜN[,Ü(‚<@ÇùuÃÃoN“o¯«¨`VeZ@eJð¯£ÔO¡‡à2þx²>åp¼2uŒùþq;,Äw†•õ}ÀQèû;JJnüwƒv½Æå\[iè³9B¡g
+™=-Lû]M؇P©´Ü09>íþ++hšYðÚ9€M?Ö‘ŠwÔ8Ð:p ôìÂÛ2tÞÁ”¶ÔËÛRÒŠùål«Ù•NíþbëF¤ 7‚héA>
+<èÕª ¶÷×Ä訓eûÄÌi:Þ» àìÍ°tbèÓí|öJ®ÂÐZ•²­¬M0@þNYù«#-ð¾‘RêTקúàÇ}3Ón ¡ØÚS·ÜQ±­Æ€<Óä²ri6ýÛ)îÝ›ÆezfÈÅçË\Æå
+Wp½Ê“üt›¯ìBñ…UÀùnç󬋥ÿÚ÷(å×6aÓÖDmçlc
+â?ÍrÜÕ¼–Õê× \ÑO/Ú¡®o|Ú­È
+Ðz¸!~æ]žhw-O´nñ[ÎÜ ûÝDœ2±[ãZzcZ?Þ3‹y»Œ•ƒãÈ÷»B ¤›¹mÒwAÞðá"öÛý¹¯cëC?köK£M…¥˜dŒBœAø§"7}nUöýRØc¡B „+¡¤"Ë#wâkãûKœ†¼Mð¼â’Ž^Ñó&ÖÀ»ˆTqæPcmñ-ìwäþ'´”{IÐåß”u”ÔÛ
+YÕ/ n·
+²NGvwôQÅÎê¬À€{Šò£O5\_°(˜)„‰­ñ’Z~Gd‡ûԷƲ¿-Bå]*ñ¡VÐ Ï2n% 3„q^’1ɱÑ-öã#§ùãžœúe¿˜[°!½Ô ÷ø„¢ÀÕRa–1;2tÌ(Ba¥CTXÚÿeáRÖÈîÍ›Øe'€ÍZ¹¨„a¼#¼ <MX%ä¼kRyžZM;@RÆÊ'¼ªÖÞÄ• ŸZܙ㴓D×:óÁÞü`]t‹I}8¹ø>&‘Ÿ¨=UËHCÙÌè.X¸øØö%´öblG<ú‹cšÚõ!"_(#Œ‹o`°IZþ¦c=ü‹ŽÕp¡‚ÌÉNrÒ =OÌ#Ó$=‹°$g‘§vÅØ£m 9¼È¼^»“Üå÷· g¾°+¦ƒÈUò®Û“ÐqŸåMÐ ÂbÙ«žLØä£Gvp`š¸Ö_üÛk¢Àæ,%®“ú§F¤×Ù­e _´‹G|[Âv »÷¢ßøià%÷YFt†—pW÷Ô _DVz®ä­|LÒ6ÉM;§d¯j"ï–г619·'eäœÒÑŒÄl¬¶¬ž|?ckÎyÀ!ï·U<Ú|‰ÝZÿ§²QL¬Ø¬‚EL,»Á‘<, 'í1a•’‹ð¤²œT—ÂaÅ#=†…¤š[D;õJØU—˜–2qÛ÷—é·“;6iS\ÓäoÝj\]èöâÚ×b›øúŒÖT‚…ÿÊÈ#ÿ"å?5Ä.Ú\xyðzh©õ_ç>ìÝ×!bãI
+ívѨÒð”±ËYU¿JsRÍ@ò»bg÷eMÌöŒ]ø<iRŒ=šßÆH«s{?·Ç§ä\àXÑP²Öñ‚CHÎÙ•<‡jä†GCTvNM& blìØ‘Ô‹Ð1½
+„^˜M¹Ìr€Vœ¶–0ì?t‰p_yx¸»yŒKjÙNöÜe’EèÆÀÃZÜiÇ9„ÿT`ÃU^ éÆ9øAB,$T®iñ:Aœm!?Vm«w‹¦µÛ¬[Õ¼Jü”ñÂLŸý¤tŠùæ ç'[w/lødtë·#²7ŸaãëA˜¾2Àò¹â`.œ›é‹u7iã}Šºñ!ÏØ»Îò Z˜Gªx!Œs)ÐŒ¨ôÕ òøÜ «˜ÖðèW)6ã`¾dÝúWÕEzÜö3×ûÎ^Ë·?]±nÞźßX/°ò÷²ùù_ªöµïz~Ì#±®ÕÃòº‡ÎÙ‘©æ3Z؆õ²ç¹SêxÙ~ør˜„DW¹£Ó^Þ„7ü¡â¢­w"aë âÔÏ
+v;ÊÜn¹»ƒŒ2xîäl¬ÔYì<œNBÀeø¢wYñI?-ÖT#œ’ïàçZ„¾Ò:©U?uºŽl~”\g”¾÷¨*|•QØ+¸‰OòVx5¨ƒ^À믗“ž^Æ!î Ê%·Öj ¡Lµ1¿éúˆ/:Q¹,làβqãŸ.|ó§ß= òUVƒSJW= ÑsÖLê”û,¦%Mð"o}ú÷â剛5›5JJ >}ˆýûÃ3>³ê–ó&á›Ô©h>|LÊ–?¯¸5äp®ææ¬4‚ƒœ ZÊ:Žé)›lµ1žÕ¥èV‚%¿ (zx› á‡s3kµäTò3Nûi@§'…e¢§f¥ëRr-\Aœ‚coòYüî]šº~gï]aùÞO©Cí0w«b=œªÚ˜¯{!ÖÚ(F_éGhËu?{µ2¦J`£æ•0s§´Ç)ÉÒž—¹ÒÄN¤t"œíz€½–³Ò&Ó˜›÷ÃBáUJn¸Ê€¬AŠA¨…ö_5#œÝND"®¸DT¿w«$Oã®8’¶ÃRiÓ n£ü½¨hñV¡¼îÇ
+da3û:ïd/tÏ ÖeŽNdöß”ƒR0ëÕUB:[3ªväÝÚ#Fµh«aåXH­Xðñ¾Ìj9¯:åÉ ¥03Ý0)êq¾›áP9©e˜WFGE¤Ÿc3+ž'e7ñE=Ò²°ѱÆÃZÆjÚÜÁnÄâE˜‹Ï»Ä~Z¨¼,úy™»™”èëq*"/«AÎn#)Sõ1®ûÿw€ÖŠîÏõÄ—£ “<ÊJ-=T‰äíÏÊnúÚ #³ PX1Ì
+Â|“¶RÆKáÕAI»®rå­$›ÚN@À° ÒÒ°x˜¥nŽP¥•K“F5!¢³Q¼*Ï®Q¤b¢óÿ–Qêð%“ž•Ðð&¼"æí¨zP8Q®ÌVaÌbÔ¢iDþ6 ïŒ_§åŽaFªoƇIëyÐÀ{0ê‰Nµ§‡•?íoø8ŒŸv…h‘Š{QPv –ÑSù¢Oªt
+ÔmíS!‰ýùÕôoÌŒµ;½°Jß «M­°Ö‘¶IñA½ïVé'šµ”Ý$r*OV­bÍ”_í&&,KÌì=ñh¼\߶  O¨k´›´YÂ-ÅûŽ…¬{èZ#Šã7 Døˆ‚À <áÇ }ìT¶ŸÎ…†¥¨Jü¼äá̤‡m¾À&ÆuÊ&ÝAÄìïõ¯)5{Úãä̲KD*!R=ïP‰áßE™cl¯Ú¹Îó¿¾Q-*'ŒœWN!é =•òç°æhùÜ«
+UÔF°¢(.P©ƒQAF BÙ#„²÷"óÍÎûfïY  ÔU­×ßõ¯»ïr|‹çñp°{‹Â©½0ÿÖ?Ý´åeu&ìŠßüf;Ñ*ƒÐ!­ª3i‡‚:]r²:Ç« ²Uà ù„àV¨™Ø *û1q7!¢ív/Êx¿Ì"¼?ºÄA‚=±?²ZTDJº=ƈ#-¹½Æ0dÀëdPµXh®`Qõ7IÃê ¤!q¶®ç»ÙB™U.í˸Å#û‹¢™·KÂV„ÖûyCþµ¥N~Ý»?®q؇¯ùs{+JÕZTNy43>ì š´¸Hƒ_hØé ^…tc1£yfQo-B iÙ)š^óQ0+6bÝaDLý˜Ôº?¤ŒÉ/Uè´ þs] þg[¨ùë “û¿ßyúÿî‹,o‹M_6x¼÷)>k'®R¬tó!«nÝ'¥¬¤Œ%;³-‘p?¯sx{‘™®M¹ý "W~ZÑß'¥òͨŽ€¼¸Ï{õI>,_ùq”Œá~,'êošæ¬ÕV–¥e¹ž“§Ôˆ! í
+½Op¤ZA¦TÊI“ýÆ#%¼[6ÆwbFÛjÈ ñÙô£fØ;Ái¾\2ì÷ †³* àC2;>*×¼ŠécN©·ËÈ¿äQ‚O0虋=õ“ùcÁ¹‰.n®™¡¨}3Ø>%U¦­€Œ‡y­aH7äÐy& }”hÔ%ˆ®·V@qÞŸg-¼%éa 7ÜH›µÝâ©phêù5S'Šo¢³J jŽ5=ìÍe Ê­ü(6ZÆä¶/IBǧeROÊÇÆ9¤Ží®é¶r ÇøÈ©wö9UdǶü°zÕgýF〙¯xÏÉJ$£„Ÿ‡jžå˜ ãUÈÆ?©¥’*øß²£c0è»ÎþGï‚-®J¡­¸k=bsp]jm‹ŸYÚ¦¸9 _õÂâ€A²lUáM‚ù§Œþþ³Ðå~L ¢”c´KŽ¤)c×0÷} gÃÌA¿q0ÚWõãO7-¡˜Ý!Š8ôS»aÊ£“6¸Uü
+¯ÞÐfRy;@pÏš‹gÙ×…u­\ìAÍÍB°hòUtÃÔÕúšÑÚ›y½¨‘b1QôÈ«Ò">X¸± ¾=rÛ^”Ò›qS^ÉxÌÑ=&I)
+a W³ˆáhÑü¤ÙÅâÁ ×Ůٙ–&öqd óøµÂg@ÑÕ_²;гg飬B-Kô< ƒƒé€š¹½ cF¬7>r÷f€ÑðÃb—ËÃ4¬j…µI­òwKx®JzÂUþ:;Bÿ¥=zc´“v«»™tU?r± Ù™7;„½dsÊ-B)Æ!3Qb›æ]
+´þ~ä’/iáüß;bÓ×Mžàsô\ׄ—5–L”,/Z@—ÙF‚A¸SÇSþâÐZð“s’€µV‰¬*`šo Ùõiÿ<í5uøkZïƤâ°Û£‚-q®
+ÚáŠä»8"5qo`Øœ79ç è¾GÕÏœ¨~>’ÓÒÈ?ƒê„O]/~ü©ùXÙCÜÉöFÂiÞ4ÿšK©hL9AâzÐlŽxÃn«ãµZ½ždÉß´2$_ÑTþ‡ZyùÆô¨û2y̘›ð\%›òð}úË]Êó˜vÝYT»údQ)¸úS3P]E;>2*
+!-TN¦DK±íìÝÍÄl+éBCÍÄÉ[×+ÿÙü¬ ¡¥AMº9Ö53“o&<ËÙ0NÖ…Ü#lZ ë ‰I¡:Õ5Ã\º‹0ô ZzúAï€ã‡Ò»¸c%%Ù¨Ins—îdñmp>¿È+hÊ*éYù?÷÷Íüƒ8*1_S~›fgI¥Ý\àOÆÝzÍš* Ûd†8Ó0H_yZE=Qr»7»ò9ñÔ«.óéúVýñÇ/èY…7{‡OÉYå5ܬüÂ>
+:€ìl€@Ôeá¬y/êا ‹±¹/ëg/A
+#v?)W~]Ÿ7¼_â3Ònño1—Gãw…õûk­Zã‚Ð;S´Ñ05ë+$L;ŠÈÔØÝ)Òâ-NócEötCÃÜ·µ5øs Õ(<ÏíÞIH;QÎ¤× S”†@/K¾ÙBl? ¤Š}æÜÊFê±êÚ7Ÿ—o¿N»\/B*é@Ñý ê~ÊfÍ™KÌJ&nS GdÈj!ÃæðœH³ÜC‘ÔÿŸ¡òúJ3A ø÷aPSMuKbï[D#¬T°aC¨€ ÒAD:R¥‰"5–˜Œ%‰™9“Ìî™Ý‡={Îþ3ëë}¸O¿û»þmÉë,cpf?¶±[V'o -+aU làeÑðì®ë×44W: dÃæÀì²™{C’˜u…·'h6‘¯|jѵ_¯>ñéÄ[6nÝ´Ñ»¨>iÃÍ}LÀ£ºÆÝhn ˆ~…R²F€êz˜Wµ
+à hA=Z‰ÒC;ð¾ÒâiþòÚyQ{@ l:—N+CW>Þð{3³Þ,ሹ–báêI;KþEâݾ°=yÓ, ¿Û¤!‹+˜@zÎ8š=rç::PÔ$’a H\!,½”©ï²Üo;HÄŒ™35K×up‰ûe{¶ùÆËÆì[#:‰µE¶vŒ]’ÂP9ÇE´‹”ᙳ„~ÂîÓf”2,«˜ äW0 Åðði\ð$"Ú—#àõÔGØ.Zò{÷:ý* e:Lëx½i‹¬Ø8›YR]ô/J¯ÞiÑ Ùñ¼¾sùAuóB8¬‘q/&ã-U ¤ŒÞñÆ… z—Ÿ0HÚ×&åÖdÈiš¿öK(gvVÛ±m±=d¼Ý´Z§†"O¶ßDçnf‘¨®øAÒîËÊ6ñÝMBÒóQ
+=N¥ñNÊõ¡q¾4Ô0>mOª{+{\Ú ˆÈ«¤ƒ¹0â#`BfPRCK„À‹¼q îî£
+$
+íÁÈ./ØË÷ ʾ+oàk€=âQÐ˶͢ɰMà‹œe|Å%z^ùG7IxY30ãÉAã5ñ#SÆç“T×+ä€öyf)VK»Äáõ½Š˜æ!YúÔ‚£„'Ùm2n„¨Gñ:(ÿqºÄûǧUã‘O9ow%Jë9[ ù€e Ý•‹,S.K`-¤0Í}ãí²”–^~jGŸ ™ÌóWøø4»bYj«qmꇂ®uÒ¡KC¹*DŸÝ¬®+/³ëÜÉh=rðÐ…©×š{µÆ­A¡Ô^Áà[²É<_I'ÞTݶöKz%ë~|6,~CG`”ÏXC4†vøË:Îꙵg½ãûºuöcîÁŽõÉ5ßñÙ>ZþÝOüóheùܧä¸\[kjÛ{öŠþ¿(=~Cæ_WâéÇÉÓ»q¨ASdZÁø0 $¦ß¹y RÑLL$Ðä0ùª¾îÀ¼6òWp|³³:r;ÕÛ®-¹Íî¢ôž!…:Ø'–Úxb_5}Þ‘M¦Ùsð$ó«&´è ¼‘ÛÖ-~5BXÏñœ­Í.ëÄé5 é?DÔcÃTi` ›úÁ4 ;r/zœšFmì`3ù) ÿW¡ô¢ûk†Ð¼li›xD!É^Ì2¬)¤ËKÃY-^²w™Õn¼oC‡»ñ³qÎþëhnàÇ.µó‹{®å«‡Ñv §WøM+ÛÎÛtØf6ôfŒ^ëìS«|½¢%W}ÑM¦ÊÓghº,:S_¶"Ñ äZ \©36Z5Ž‘Å5³m`ýÜ—.üû„7÷}o¦ëÊÏ%ù Ú}¯Ó¸ãÝ”8\•JV+hó©‹¾ÜQj ‰QÅÀ‘(²Gö  1^ÑD»‡èCê0«õXYjÂM`îét's{žÙ±MLøyÀ!ÝîÒ±·æð_'BÖÇ=Å¼Õ iQ«ÕÍ2ËÇ9¾úK¼¯ pBù“óÁìÊvþÓ¸ä0!¥(­¦€¯›fbpâD2É”±ÌrUíhŒ¸ ûbû……X~ë[è¿q󆾤Ôî•A«†SfÜpª×ƒÃ*ÍZ½áÃËõNܪÜ\/Q;Z8ʃÓZ>A§RYú¡ÆÓYíèÍMÃ@À³N<÷+iŸ ÿ½ærÿ÷M¸öŸ+‘äÔÅî
+¸l×µêñnrÿg¨¼ºÒ@Ð0|µ×s³çìÉlÎ$“¶³3“lb2›˜Lc¢qìbEAEEªô&(R•*½÷. E,`k²gfË?YþÀwñ~ïû<*iŸN"þ¬^6)dÒ¹Ê
+–k,“2•´²bí•j…ÍI2˜e “U—¶Òzóž%dÁ»„Ùñ,ÌÅðºy¬¼Î¦ïG9¬ŒŸO Z–'L2ÒÙÂØíú‘H©ï¦.[?LãÕ&hÎîYa# /{‹ç¹ð<ç»y±õý¢ÄðV¤6 º^G~cUz!Oí{aŸ‚؉JŠ§©d$èªÁï\ë*”R­ ¥æ¾rƆۉÜÐGX«]Âo?ôÐv€žÂ)ì×b½ísIð1ÝÜç„°÷¹“\ÿýŽ ×Óz“&öLÜ*BEŒ¼Ñ€F4ÔK&|ñˆn™ûJÍW·ªÖ>ß
+Ô­Z&R$¤ ¥µ’É=#æÔ/XªÆx‚ã m2«Ã·ôÄ‘JÅ<
+1i­?¤[ìsH¨MV!±Ám±ð5ZäbEÚ¢q=ZN_À$ž Wà·‚½Z&︄rh‰XÙ`³.“‹ gAT寮ÿAR2tφoÏè Í[8éXF¯hÝi7—r )aóâ A0uOÙúÎ(@<r©x]^£t*j¡woqÛ!ëȿ檄ش‹0ªûÄ;ñz×6ýfË<û.ë Ã
+{!¨0§ì\DÄ8èè]î5^¿nUó´ÍÜyý NóLs>ǵ?à‰üïiÌ•7‹ËºVXñI%_iO95ËGaµ«ìã°vlDàe„·|“nóÌ#/¬5&ü»O¼ï‘Î>wkTh܃ÕºÊW§–•8‰¥xC36û†€&Ç屟i•k ·FOL˜”‚‰Þ{ÀŸø  «•S‚ëý ÿ4ÿ%¬ÿ³²Føð¸éÏ1ƒTˆ‹¤)×"4h ´8ä„·V á¥kÞ¸nçŒo'—h¥$v¶µ(¸Ú^’ßäôË Ìàurr™ÄOŸ„ç€Y ®3 #n9'ã3Oã\þÑú2·âPÂ,T9L…½Ä‚‡ÔŸus[~¹<äQK1&¾¢ó`ßOïyÑ€¢ ú:§<©®“_s{”½i4年$õˆ÷Û6ȯ;>âHÊÃÇÖœÕ
+©/>ͪÇ4ßé×àÞÌ‹½QŸŸñ³`9sâ(Éá^å¸Úã0n¸ìÇ´Ÿ§ôë‚8z‘“„Ï6ÙâmcƧfõûÔ´îx͇Q‡Œîµˆ`Vd²šTF'M«±áÍz Âmæ ®»­¥M6·’`±·õc/cܶ[1Iÿ»œ~ô©“¸[F7ô®…ÌiáõQaß½„xäÑŽy¸mãÂ#ZZ¿–}|ì\\Æ„ª› :áÀ
+y]Ô€~,êAKvxá Ûš3£ÛŒ¤®ûY)¤å÷v€·œz1'^L×y„
+ê:NÂ^E³~ôàu ü²Ë÷”ÀÇflKý]ÄÇ;|ç£S+±ûÀˆútbǶµ˜Æ‚nö׌úÂIíü¶bgÁOÜ ä¾Û¶%™zyjCw\0}AT×¾ü‹ŸÓr‹ |ò'?­ûé¿ãLÆ×z°lìý[QÕvëÐüùÄ=Ût Bª!®ä ‚w-3Ç^L÷™7X "ÛK`]A3ðó~íG#üMÑŒnºŠ’&/Bxð™4|è„54º´tâÇÝLý±{°©„¼ˆ
+‡îÍÈO™5hÓù±¬‡>O þÏp™%½æqü§¹gν¶x›Û­îÔ”“eii®•¢!€Ê" ;ˆÈ¢b˜Z¶Y™¹€,n  ln¸@
+Š¬Â—Í}ºÝéÌ?2ߟŸÏyÎsÞçó~¿ÞOÅåí5׶ëc·Á™]»@#*<g—7Aýóäív¡g’U°3JHöLP²ÂjF~pŽ‘gÃ?˜ï)¿$f¢|Ê6ø©©GYì zTÔ¼°¡ ¨Ùe~%ÔòêWÆš ¦ž—ݶ‰›²¶‡qy^.+²@Ì jÐ9ô 2pjÆvBDßÓ h{EÞ…ç4ÛæšB¤úµMù“°Õ·,jµMѲ
+r^PËit(OÖßÕŘÞVÆ
+ß,
+hZÊÎ6z^žXº_Ù&H{“ô‚ N$
+ñÎrj2´À(þsG<]â5ÌÓ‹¶?•ßqŒÕ&Y?WßÙ«Žýïnç‹S³@àRÑ
+lã”’®Z¤U¯SàG&2Ê£&•ûuÌÊÈ
+sjuùf™¥æDÆú—H@ËÀ¯uu˜Z›÷g°9ÞtŠYJÎv뺇æ¾?mýÒË‹>ÿš¨Ã¿ÔÉt,Äá*—0p±€^@sixhÀÜû*hy9°§4X%MI»bt‚S‚yè–6&4”üWþi|ºwŠYjzÐO|¥À†U]X`†WœãVo â­#˜»»ì}‡”œº'ç™6?‘’BÊn꙾§õÌbß\SæâKØ¥¥nè9ÓËÒk^9½PwÝÓ䧎qÔ}‡q/4G„,Ћö¤ø¬­‘ÆôÍ¡zP'|â;0
+ˆŽ lŠm¤øŠ]\yË;IÔ¬â®ÐPóÝ“Ä4`®ìp$×$ b#<
+.n¼«½éWrkí2JÙ.øfÏL3˜£ç»¤„TÏ81Ù!kÎR‹¯‘Ó£l£M…ßVúúŽt\´e°:nåõ³+æÁ¬+¡tÊ‘‘Qz kAÕ-p·ª¥Ô§Ñ÷Õ"Æ·•NöÙ:Ÿ\âá|ZN­žYp`dÁ­bLÂ×!LœK&À
+ܳS º3HžjDܬÏ~hŸàŒ}!æj¨eˆ~¤*ŠÙûD
+2‹ÈOMeÐÁj¤«˜‘à¸)¢í*Ʀ¬D˜¤´ð¹ÆL¦â¸#Îó^ûX&}óˆ–ðM5Îïr¼oWR£W鮳M°;ã5 w´­¹Žññß›ù¶±Êô#;ýd¬–`æ¦^Mðr†Š¢ÇkaN#œ¨ o_¹w²(èù·ˆ»‡rF×é²ü@]sŠûFwVО,/Â(Æy¯vå…_Í ;­
+a“^Pµð–^ï¡Ä\Í–½:Ÿ¢Á,rBˆ¡;×O\šàÜCwùg¥©áëRË:No‰tf;ïö§zœ¨©1ûjV
+7Ë÷Úd+2ôr±ŠdUÑ!rn’;ÅÏ¡îwSÆ€þn‘€Na£Ÿ(R’§Ãªˆ÷`÷KmIæž °Ö™ì>]ë$e†þ®kKqÛWV¾RÖeߟo'_Í·Ö]ÎT•¤(“(Å]Î u˜¨KuVÕ¦‚vdTèñDIÚbÊçRÇÉ¿\øo˜¹ äÿ mWåÁ–…/Ùýl®"{KFŽ]íÊ|¸/#‡î Ò£,CEàã)FÊîX1ücYªs&âàå ûD ÖØŸãoì#ø›;) !öñ" ¿KQV5 2׌pý܃ ýiæñþZåP€~1ö|VÞcŸh[2}ûüšy ô™¶®®M¿ûu¹¶úõÍv )~oéµÿ)/rWJˆ8Q$}™)Lüw«†;ÝIH®!DÜÖ÷ÓRÿ6˜»4´tØ'*ÐÛƒ¯™úøÛùOoͶ".WªKðÁ·ôÝ„¸ËùÊ×yñLMzñeŽú}:U7 =uï Ž"ºÿoFÄ­õÁ˜á¤âã}v†*“·ËÀë)3-X¯éF”ûLÊås;ñÏÝvâl{ž¿¶%Ëîa¥ï ²öËbÏÆñ€6ÁÖJ8лOõ÷C%°sÔœŒyxµ;´Øç8ÇOúCßø
+|ÓFõ—Ç;,µD
+àqnæ÷•æFÛxrGVµ-a'Z¥ô¨cy®Ÿe ÛwSŒõþÜ…´*y4«º¡H'${û~q&µÚY:ÏG9¯¼Ëó^êÈ XzöZfûi¸ùvujSLðÝèÅnö’ÁÆnâÓ.Ô£ýA´·©?;|©=ëQD
+;Ÿ­gδäxNÖ¦ÝrüàñpË#nGZm¶…ALìf@],#e«Š1ÄA€šÐÏ“£o¨*“œ´@>¬‰IÑÛ£tæQƒñûïÕBéd²2ÛЛ6ÎOº?]—â2[— š«K}™)Ãðñá΢Šä€Ãé*âþXr®>ÃUÉÿ5䶶5Éiºì`UàâlòÊ­šrøÙ,lƒŒÝØ ­n\¨¥Ÿ2?zå}–Ïzo¦ÇÏÝ×ÕgZ&~U„ó¶©i‰WËí‚«¥¶·?Œ o.K3
+ß)‡ ÖfÞ—ów†™):A^€s½»0òÚzs” »©kB¹D|DSaò6jŒó‘Š[p0†MQ lZäŠå¹Ö…¼oÌõ±kèñ‹=Ôg˜¨ë my`sê?`ü¶û
+Ÿ¬tä¿Ð '˜&‘ Ç2æÇ:µì¿ÆFÊþ%ùLÙˆ:Ñ“‹ý&%'üc³ æ­ë„ùé{sžŠ‹£~Y˜•æy|Š¨ öèPLÊ:–¿§ÛdMØëõº²sme걸<òDFL¼\g ~˜;úϵõÄ# !ñ³º¦ø—©…}k¨AŸ,¡cNd¨Ø½h°é#ÂÿBS‡½Ñµ2œò°‘ùÖ.c@ÎU¥WÚ6êårc ÀÖƒYhg¼,ÐÌ«xi›£¥*Úð{"&ä@X´=† Ûà üçªâΖ
+lrÒÖH±¯u úªïr.5£÷§ª¢Õ]Ùª»Òþ<äáAgâèÍJKõ”“wç=ÚåU§Šêaªwðû’º´{&Ø}–{o¡&措)ãþ!·2Ù)k*^jÉñ´K9 iž¤%îxçÝ0&h}ýÌʧ§Ú€¡Ósiqšše'1RÏÁ†™¹øðcå/Ó4+ku¶5NŠ¿Vwµ/RRç«ÏDM˜SA þ`’2s‘þÆ Äó­1t žCŠ>‘6·x”¬à]¸ø×ÿ2"ý·‡K‚V{P¡¦ÉÊD“€š>Uº"ÓÓ)£A?ugºëûÓÜvGr¼ ÝÉÖ©œS!.óËj3]Ë­ˆîǧ¸)ÚŠ_ýÔw»”älã0Ü{k ÏÓ<ŽòÛ@‡nŒ—GÈúJýÐ/îÀÏ|[­-¹Ñ0à®%lÂÞÇÜ`Im¬Û&à7eÛ»61)±§ü÷쎲öm•‰q‰‘៥ˆðsIQ¸¦=ùùª¨¿oôäù¸T¬ò.1Ü8‹²K(9Î%*üLJÍu*(P 'Éú‰ÒЉjðÝá
+ðCÝ >|¦1ûÉö$&Ú:GLߟ)O¿RÑŠnWÙÄ«•&‚MDJZãäzZø¤X3°ÇÅ›LK]l‡=²À÷·ÆÁN9óx‘YÂømŒ¢ì‹T˜W¸ÍzmöÁïŽ=±-^]hÞ×4â #eg
+zΗöÖ˵VÆé"9ÕÎ/ Ýþð—ßöàÛ
+ôp¶"Öʧ&8¤µÙ—Z6îDR“á’3²OD@ÇÍá"/ÔÌk³È±ÈBìÏÕ¦\ªÚª¬zÚæ‡2_ãÚß<
+XíKóXëHõ<©JºT¶¶‡°¡–1|äU3Á%§ANÈ v!%Y7ÿl_€4ñP6a5ر܌Ñô?Rw<6pJCö§pQÇÓ”d×B=âLHÍ5 …è9…¾çŠšü3y]žU@Ž?˜¯LÐ`^júÑ/vF Q7š6ò¨±`kŒ:šëÄ™-pË,5Î2‹\ïÏl™, µût*¥§_iʎ汯ŽøĤsygq¼.w‹ƒXiÏ÷Õ'ÝW4¥¹_-wЪΚ­éÚ yk¡ÇéBMÖÀ¥—Úº¢“¥ŠDug¡·²é£j/ò¶JhYÇ
+j†ulsmÉízWÇWMs¥a1EKr—4A<w§0/ÊzÔ­®«ãV÷¾Õ<Cˆ´‰˜ˆKuO»KÑŠ7ŽWÅ®ô"d=ØÀ1ZÎ]U`áZ¤u¾$xŸ²ÌGç³M—Gœ/V¤ìL•‚ÄÍ9žÓÀÿ¸¶üZM‡ïqaÏÖßÇy?@ž[&K£÷&QáÎÅJ°K]WÄc¦»ËÚ __¬°ð_´ŒB—œ þÄùð1ÿ‘°!UÝ~š>L„¼\šúüž€ ¼QW½Ý†ûmöþ?ÛÞº®ÜGÛc%AgËŒÂéÖ|ÿœp¿± awR&îRË"Ù¥ Ý ˜ßÆ
+[Å%
+陚žÒ@!3ÇýDZ_~´È€ì‹ªSèðùú”'3LðóY%ôf£Žq£oì0
+i|6ø¡UJ„ÿ:líüia×٤ؿT]`ÏõÁ·Ç¢Òø«µªl‹‡þ»ôÍÔÅœ¦÷»Ž¿Pã“·yÿ!»€#ëó\FØ…uY.% y¶ÂÆXfÈ1ŽEJ†cs%¯Juˆ‰‰[c0_ãÇ|ãxi€KÙPl“ÔÂv'ˆáÖ™êx €¿Ò!
+ZÖ)Ð5†á¢@Y#øži“p»ÖÕv*§¦ÛªãBXß
+9Ñ¡Ne]Ñ™¼q$bBõ>‡|Z†SÆ.1Ob_î ?ûÅeú–ôž†ñ·3çš™NM§™œvm,Í=“S©i(‚¦A. €²/²/’©õTf¹äŠhÊ*ÈŠ‚¡ "Šl.Ó™y1ÿÇüú ¾×s}Ÿû¹?ÛkÈ¥%Qñ_F …ÒwÂRBJ΋mYÄ=Ü~/i—Уf.*¬g>ªÚËb:p˲À­8¸ E¯Œ°z§éåÇf>¦gÔœÙ9èc3¿uožøÈ7…Lß›ÅdhàÝyl„9¿9A/ ,²¡þYB‘Xáwö¶¾oL•3Ê~8Òs÷Õ¬Z0ÞŠZ±1Ž{äxƒÍ^Í‹èy˜C
+tiãæ®Ø=I}lúH.ÚV1Q§.!Û7Ó\ä†]ŽY;p‘•ÎΘëUßMH÷¨Aåwwi/é(U@ïA]Ä{^î¾ó}mʾ›çnÍ5õ‚ϯ¼©úÉ7‰É‰›ÄÄ°YLô+ÙU!­°yk’˜#<ØEeYÀô]ÓK«
+zYX骸uIrQx¡£.n´ŒJçtºã×ú+{ó5ž)Øô~ic¨%ÈW®û¦Àñ &Û7Š/ˆ¨8ˆ]¥Äû©53i÷ÕÄÇ‹mù~96÷`Íø2½:¹ÌC†5B¤îdšm )÷HË©+)eI À,:&l{–Ú‘µ>ˆ.±ê‚
+Z¹¸ç1ôÇ£evû3³F×Oq¼E¤øç …>y[ÎÚꎵž–0ñZ¶çà-9­4n—r¢Fv½_Ì;‰Ï+Õ­ø,i
+(Dȵ "h}Œ
+éúºÂúþÞ¤¥GòÍÓÙ™0Ójý2tîæ(2ݯ`<w)DmŸ¤2aÝ•°FܘcBä”òð"ìŸ#–îÌì))%G–—,×½Æþ®õþú0±Ä6ˆ-Ýk.=6ªß ·$WhHËûW]1 ¸á•x_#&:êOÖ8¸ Žðtg¶)/ªÆÃNìbºo‘ñ|˜ZþcPÃnþÍÓ;· )1§~KÖV´(ùùŸ
+ñýÿ,üÞÿ¼"á×U>åPE*9¶KøGV5aS—ù;jdós{‘k ùoß ´5×Qi"fû䢦½ÅNBdIˆ‰êhÐ9þûæ.°;Ùq·%¤—´[Þa3mo®E—¨PÀÓ
+çÛ@ßVØm3¥Þ¯Âvæña#å²¥…¸l|Y“Õ‘`Ô…–¨Ï+\JØ!¢ sa7¦$ ùr!ê˶ò‘œcîäªøÏ.zêÒÏVyä?<Ò®¯.=` @vuTÈýÒ¾¯îþ"é© èC#Àú@ŸF¬Üà$# ±6ŒLßœÆþ¶ùzôØÕó.dÐ<ó„Ÿ=s$OÖ’é|_yag¢ú_Û 7ýr.ì@õ’àåÃw¸Õ‡j`·åä‡!%©$±L®83Sk¿gjOAy2v1ÖF°·Ý#õW¾L ®¹G·öfI%qmâÔÄA;¤°!¤ílÜå‘­¿æ咽ª Þiä=ÿ¾Ð3ŽË\ç©OÆ. ozá—Â*qSD%nŽ(ÄHÿ­d¤t®™uNÍ}raw}7f\£Ø ÷`ãÕØ÷wV…´Ó“+=SèÌ1àö/ˆà7Ý#Ä<µ¤ö⡆
+IZm £ %±Ü?Xlì›iÉX¬½hêzòCHK«nB?€K_¥€Ô,XDǬIù-
+R©o™ìQ~@ɬ;[ëé jv\$>Ü• ý3íùI·þÄ(¥…u|¤eycc†ø(fœŠZ†f–^ÑéÐfú³¸–
+‰k™ð# v–xhÛ䞦@Nl]‚À|[¡_Öœ¹3Ùúe¤ñvTpÛª˜·IYÚlšùW(¨îRŠ?µ01©Ò3‰HÛ–5eD´dØ™MÄ;˜~}’TP3aß6¤’“1a$A¨"Çhªé-$eåCÃo_¤½¥ëkLu‘A ‹”dæ³âf.)¬&ƒýrTADÙ^VÑ+–nñçš×ðˆ^ˆ™Dø##§éØÁ#Ÿ:ż]¼>Ï‹Y{^ÅýƒA økfõ¡ŠU øîó¤‰üÃÉÆÅ–Ù ÎQl–s 3ó
+aÅÕ-9眈Tïtó½ –I8´¤³»ÛìÊê8³ø{Þc6)¤§V­@.ÿÏq™%½æqü§¹÷œ™9·;Õ-»5u§©[ZÖµRº¥¨¹á‚¹‚ ( ¬"«ˆ
+Zèuiq+\ØET6ÁRpAdß0$[î9sæ™oóó÷ùáû|Îóy¿_¯ ·œTâ×q1a½y c7H¹ðó†qrŽ]ÞßâZd<ZBƒw¦`·½KXpØ@®JîöY{ûL/Yº¡¦«À\ûŽÞuwŸìrø
+Ø/§–;8¨à£Ê8Gq3³)fb4y¤ÙžE4Јô˜
+ž‘Рsb+¤’˜–‡õ+å-£1±Õƒ(U! .Ï»¿á˜©ºè™o½S pñµa¾_Õ‹Þ™¥HhU>M'Ä£À=´Ï oíÏ o¡çõe§C
+sbéã5ø¢ð–‚JJ±AX›f(<åÃoz¤˜ûÖIøÍ„‘R¼Éî<Ôu·Åt½íGf.iÿ-â–i°üŒIP}Î8Prfwr9¹†ƒ|0ô‘¶§ˆ ÝÐÓ3Àÿ݉­v–$èì›ë‘¶üæ‘`ï…–»Ê‚+Üfýîá&ÿ¯!5¥â °?±U| p‡ÂŠPµÿ¦þºýMÕ%Ÿ•ýi›Ï9øÂ¥@eÔè|ÿºÀ±Ð˜R·Ÿlòø_üq¿–ôÔøüŠWÚUu¨g¶¾ŠŠ®Ó`n%¾0¼Ü MêéÍ'ï8ŒÀJu‘]yMÐŽk¹$Ç|;È2Q{qo¦é¦GÑr7ªÃä}ÞáÐ^z²
+ENeWYjwhÜ«bÀ­¯ZÒõÈYQýåïä]OÙ¦QÙÉíÁ ‡¶¿gí9òÊö47
+iiðã­~—’
+Ž­<zÜDEFWp¥î9øíÝ—5ÿÚ¯þg@E«þ`pË ˜VX{Þ+%æÇ´´†˜Wôç6µõÛ\
+[úAßKð- ³ôü²³rzÞß×¥§ƒŸ½rbQXÛƒZCeêY)3 ^nmŽCÒæi?¾lÏùÅåùÖdšÅáÏî^»³Ž3ëŽë ®Øqlˆ"UiA”L •˜ÞH‚0J ”@I õ%%BBé (º3³ÿǾóéùò^×s½ç9çwîû„š]xbµ¯áRØ""ØÉ÷ÜŠãÀ›Æ«1£ˆžœ1ö[è1#½nc“· º¥×Ä…€—]Kƒ„‚َʳ g•„t„ªä"—ö!6 E—FÏ+=JDVd†˜•‹3÷<½dé®úÙ;†¾ëS¢ó#3ä§û "Ö¾½²Äü
+v6¤ãÀ}“dÈ›Çgýã·b`vÆ ¼Æ]@@Ø·K_ì­Èº]!l]%‚»Ô¢[2Ã5Œ¾ÒÑa1€ƒ
+h)öl|ò‘Sú2¹ÀÃÅ6ìß–‰p«1%añaØÈ€F
+óÔ§&ÃÝÍ°•˜ëÎaäåÔ|sãïž®±Ã55¹ÈBm…_VÙô¤…²6ãŸ&C¬ïàçÖM÷â61aç’¢sŒºÄ¨°äª¨ý“³Mš\âÑb–f¤K‰Ë‰Ï³QGóâ×_ $;;Ãæ®_Ó\îWao¬üZ›&§ß<&§^<6+ºó×,35ßÊ jØ·”ô‚m­xG…½™@eÆa×ÖÞÿtßÿ{jAH è%4Ç·x}ˆQàhÌL¨‘÷þX¤Á~sr©Î)ÔmCÏ“sKý×C: É«#@~-÷*q·ô²Òïí½Ï2¢º¨®÷U`ª ãÁßs²ƒ:<lS¶Tì2Kì?–¶Ê
+Ü͸–Q±°áQ#²3MÌÛѵ5-1óƒÄ®ðtßöVÜÄÝHDÛµJ¹~]sÕöêZDO*ϵvt Ȇüé…Á²ó!-üFp†õ4¼|“œïë[‰Íóû‡.9µÌ#géå®ñç÷·Õ´‡‚’ã¡åîÌp¡1‹
+ Wo B3’v!3±ÚñveŒôh~ .}ožŽä’õvJÞîY™ ÿ3ª
+Èã¬ÊOk½[ZjÕÊPC¦½¿ö‚ó<+1Ç„‡­"²×ØFpNð«V-µŒ¹1Áxfìf/ô!oØßC/ùU˜¼]#±ò7WkG Ìg^Œ÷ê…É%Yg à!#fÆS—ížÀÜŒÍ1Ÿ}v¶Š¿y:>¸§›«-=ØËž)ê³CúâwøÍÑrsSlºáŽ{ èG«´ôÌÚPÓÃ
+œŠæÂu¯lc¤­Æ¯ioŠšÛ©!³
+ ¢¨ÈfXb!+!+Ùnr³ÜBDGtœ©t\Ø„„²ï@a !d¿ÙYu¦=ý7ú㸿{Îó|—ϳò¡2:CûöÔ 3ý
+(aáwn¼¸žçÔ¯Oö^ò?úmJG¯Më3sèR°Š`Ç)=÷aÆÀk;^‘ bK´š˜ð„y u°[RV¦ä ž#câ& >ëU£µ
+µ*èy§˜Ñ®Æ–HÕû:rmÆ4Øz¸*aÄ—ym9‡
+nˆ™¶ˆHq³·¥t¦œcOãV œ¶Ê)i‹°'º@»3ÐònQ?è QÖÁëI˜wóvà2•µ²š#ó}U+¯›þ¼öµIÙÇë£/³D:ÿ´ó¶Á}gEXY"ȹ@7ۤ䛔µÂÔ˜{üõšþ¹hú)©’t¿â‹•¼^Àn"ðÞ¡ŸOŠ/bk²NnWêÜ«V:fyö44Ū /ðå}r(ïƒhå€ *`QÖ+¤Æ̼v0“þ„Ký4åVÂióAÒ@«MÈ·
+NjstgvE=–tkÔ!­¤kOÇÇü¼«~s”@»3„ŠýIÂ×k¯;®…§×}ñyÿ=~s¼)ƒ#FF[pšÐ˜õÀrÿD_ÃÎ °KÿÃò*§ÌT˜´È)‘E Þ»¿þžv-²$îÚ7Éû–•´ŒKÉKÚfÌ"%îêXõÛ3´Êu ãíÆíð<¿i{
+nNFŠža!èaYÜÂ}r¹.2ϽÕJ;£zYOD+|¸¯å4D稕97·#íà=>ÐÖî| V˜‘û¿^VÔÿjû-öRh42/j>×j‡ûÏ=Sr °e'»=e¤ÕoO“¾^ÿ7­b÷#ûöæ ízx–\U²KHY»¹dã´§É ;ïZ®Dgp_Å´”ê‚"'L즸وÚE¸‚GØ{®÷“ ,(úØO":â ÀZ­…À³w©ÍxX'h)ù¤œ”m ¡èe¶|Ú œDTÔÆy[¤Þ*»9˜¢G
+AÚÂïÏõÖ®ˆX'Šárp|!aUðBïû.mMSþYÚ~·òº3^gÆÁíHX†:rÀ'‡›š‰bè…6l‚ÉqãNÉ'¢an9 æ
+Õ¾‘Ùî|‰ÿ2i”²N9tº%ùe’åÜ,\ÒzÙÏíþ´-C¬ßîA_8^w?ø¼§úñqÎX†ÚAž ¤íjIÊŠ€¼ðiLŒ[Ÿ‚ü
+~Ü"%˜à'1“¿§·oNÝ k¹Œ¢Îл&ô¡¯"2G¬FmrÚ°Ò6))¹,ëOYTÜ´m|/ZwgÁ]wÁ”˜I‚Û™£ß ¾é»è{‰¿¸ú=ñrd–sÎ[XÀQ]y72Tô#PÎ+g'mÜGYó~Ê4
+íû.Oÿ~ÎÅ{y¾oÎ÷¾ïó<?¹¬â¢fjÃÞ2­>nW}?”½c*À²”™ÕÕ±Ú³.¡êQ².)Íñšvge})ieR~Ýñ³ÂÍŒ™†(;E„ŠK<T¶ zÁîýê=öŽ–Ö”|À=¶f†.,±$Œ2bÆ4&ÌZ'FR¶‰‘V9ð)ûÞt~¾?‡>—6Ñ ™2ñÛ=rþ‡Í'/O6GŸ”Öàá¬Uˆ3Е1ñbº1f¤JÅØ”žÕ”6ò¾zŽŒixÈŠMÆ8ö©À[*¡¤Â',#ì´eR7È¢:*ªc4nÿB¾RÓë’F~;øîûbMðÝൔ‘Ýœµ@=¡%VÃîìðÀ[Â×UõŽª2ÎaXÇm -³&-01ey
+’&3ðP1#§9aâµnÍÓnÄ üÖÒšœS]‡(©¶3¡ÌW±•ë»ó¤« «%xjG uz¦ßÁ½“€°;‹ÿ:8‡«Ië¹-³
+¸äSÀÀSÿ ÊTU^Â}srÖñ®r¬°. ¤VyÝŸú3‹{ÃF)¡â…™…U6}ó ú®ï§®T7åpySÂþTy/§'¬#}6Ò[r^0ÉtÕ#‡ƒ‹¬¦”UL=Ý›œ­$’¼SÐÖ2öÕ¬ú¨‹*¬ ©#RÕé¾H3 ¢Šv0¹®°Jmø­Àœ9Ŕꚜ—ÿ4+FfKXMoÈ­
+0 3¥1ôw%¿Êl-¸Ä¤¤IJˆk…m¡roªóÌÞ\ïŲ›ÕqäccOü,|yŽ9Ý‚¹‡>˜“’5K)#¯#¢a5%ŒpoÂ
+Ð’¼UNI˜M ùvÚábzàVsqUÜtu0evl¼Â×FÕÊ..æpC0sBdóˆQ“l iaÅÌ#´È„Ú˨3!þšÇÔd tdl™…Œ›%}ûFiK­è ª%øœ"ÆVh÷ýS}·g®‚³Óû@Ïk9ÐC] ÓSÐ÷7K ˳±¬ã1?¼<T·öC癄–ÓVt*ØU¿B6‹°{zQwÒ©’gÝ%I+Ô~O¹³û{pFMÖ ãÁÌAYÀ°=kQ²A{ÁÂïJ舃YRÝþ,ùZpžz+k‚ñÅU%3²Èj ¼Æ]ÜÁ_ÎYhMy+µ1c&7Åt„úð
+£1¹:&É:LJ5Ôéö¿§t,dJ'슫éõ½0çtgâç´[Æ;ÐòZ²îQEÆ=ù,ë™|‘]{,‰èMž——üÓ˜û›„YQy~¤ÕDm ½r8¡§!‹vjGROiô¿ÂŸÓJ›ÿ´À½û™ï%îæÁ{nÓÿ).ó§&²ŽÿjÕÔþ°µnéZ匣;®3Uºã¬¢È £ á6
+JDØŒY0“´.ãRÐrîn‘6g\2jÒ&~D¸$Ù%93a—4']RJlQÞHÖr¨ °¡P›‚ždÞv½h9.ªóˆ‚™œqê$
+°'ïaÒM/N8èEÇL
+¶g-)‡Bð{Pûø_¤ò Òýps–_zÏ/ Ž
+î%Ü_ïºä`›=À¬âö½€ÒðÇ*¬Œ›Ä;Óü’¨‘yw}ªóîµïÇ„{?2I¾üi¬ãNhœU¿f’ÒqD«Ï­']œª„ƒ[¹<pàÓ*ýeÎ s"sì’OZ/®6ŸŽµßH[Õ9ë0¤Q†5=Y¿œ[3R¯Åç»o%,ÌBÂ.ªÏºå´4Ð3á’µ¦çä}*(ïë·„·1Ë0»´µjq{¯|×Ô'Ämª®´fb ³±EICÒ!iI»åY¤‡Ÿ
+fStäã{amÿaX!Ù2Ò*}Ï›.æœJE†¡ ¢J¹a!jÕC)·AExtÚ½ ZûǺî)ð=²4a—¶®ªÕ„OÆŠ» &°U«ã‹‚º¼_Ú}”ñ÷üP{ÊÁ­û2Öö}ÔÈ(:ZÕÅmPSøCËw«£¤‹…àù€?uÙ¥Áᤳoph)èQî¯÷¿zå­¼i»<!ª9… Ñ®¤W³nˆ’r‰yÄ’°kcºûfÌ&nÄ]Rjt†vge¤ök÷pñIp¦)»Ä¾Ÿv3Ë Ð§{ˆ’}àÓõìù ZÌ!}ä¥üÝùôç?Ûû
+¿
+½ª:sPpÂÍáªF]–u¥RjÚ*!§­‚l¡ízd‚|ecŒúÏÝNIÒ)n<
+©äGaµ*¿ sRnaCÔL¹ºkæWÅÁ6Ï/kä¹e,¿ ô Øä*ï7ó~NÙx5¸jŒ-J?O2Šƒ£”Ÿ¶?2Js^QÛæ,åÇлƋ‘æÝíy¸5fémÍJH«¿Òîà³¼ªÄ,·bíMóe‹êÇ“koIße%'0ô§–†P§Ú4³JצZ®£&Ful®«`wŽv;:ßU¶1C/‹Z5\Ô¡“¥½:8¿¢–í̳ÊVGÛ®mŒÓK¢`;'<àQikžyw×,ªÍy¤9ÔŒšé÷ü¯îŸö¿¬þf×Øq;å€þOq™0µuaødÚiÇ3É43qÜÔ­]Ûµ`
+˜;çB81+Ü»½HºµñSÛÕ¤™Ösó… ¼Ö³UQGÌÏM£¼sÛÓ'ñu„Ô1qÞç¿^Q–}qºH,ͺ•œ$
+Ób¶AðÇæ€VÞyüóPŸÿ?‚ò°Žr'å¤?ÀPþôK-ÂÀíÉùÆÇ€7L&Üòƒ’¶¿(j>þÈ­
+êè¥ÁjuÈÄi YyMùÊo=þ!åQË’nˆ’öª¥q›Š1B„ðª¨=¨ãÖF,ìÆŒW!(ìLþ”õÿK{l”vE,’. …Yi¦—Xµž—øïí³m74œ‡QËÌ“qñ¼æ4l½§T\LÍŸ;NÜ*'Pà%(£°s©„iïô¿³Þ‰é˜…ß\¥––)%ÁU:îÜJÇå7…}Å]ˆwf¦–Ÿ¬PKËCÿÔ\
+°UÖ)êɹ]1³°½°±‹»ê¡üÞã—aÂ=Ò+{Ïôbf>¶L©rÏÔ}e­þÃöÛ®k§k<À¹òþƒO‚Fû þOþ·äªà
+½½è†r !f ¢¥ÞMÙå¤:"͹'F¢v¨cUâzE¿í™ë½ú‰ûu·`VaSÁÃ0©à~ò4cLY;‚KÄ»¯ WOè%çzAfáÖ\2RΫä¥JfÆ­f
+üö=íÖÉâÀµÃ÷×À\6
+BÂ4>\4l¼ þÙ4ÖúG¸ä‹àGRŸ¡Å­‰é8:õدQt
+B|}XŠ><B<Y"Ý>Zê»u²B«Æ<Of“ž©Â6+‰ªEA½š~ª…aV+aì>]¦Ü9[¥—ELÜú4ؽ¤UÖ{näÖDÖ¸ GŸèÕ˜EŒOÙ8ÿŒêÉåAAƒ¼+ CzŽ4ÊÎýwœï7ž‘¿Ó©~§SÔ^Úëù{<Ê{ jÔ,l9YáVƒþ $¬œæ°ž\æ_¸~ü3£,ivf邏“«š½ÿ ÿ¾ÿ£ü`R•2ò›Ò6QGpË‘†r÷TÇ®J˜åäÀ'¸Ëþ”r%´Â®^ÇÍoAœÂÄ?·0#F~;†ÊÈq§Š“pͼ<[ùæ9{è%%R)†
+ð9Ÿ”•Û”ò“ÎAʸ©‡ZzeÊ;¤Û`Ò‘†Q\fVÆ×m¯˜üë¹ØUG°äØ"jK¸Ôƒ`'ÁÞRvß‘o»_àoùˆÕI à&Û°jï#»뻶»Än.îÿhŒÛ‡eØ÷æ ×ÃË„À™ZÒ¥àóþã—¯Jq°D¹Xå6ŸÛ’´f‡Ö¨•!õ^ðWÀH-=Õ3ª ÛãÀ†ã¨œ‚9ana[5šÛPˆÒ6^gÒÎë8wÂÌKÉ
+;fØ$í:Ór*w_÷\õüøðo¾ùþ¦à§;º>ر ÔÍyî=ó éJÔ¡’ücSYTÍËØe} ¿¡à„>5”w*èY—œ²ˆÛÂæî™~œòÌý^ÚKÁå6ùÅM)-ïE„×äTÂ(ëI˜Äm˜ îKÕŒøªðáÞ›GYŸ¸ó{ÿó–oãfAkÖ‹
+¾ñ‰4*§jIw|oš¾Œèzo¦×yDÎ{T¼¸M15©!°'1POLϨjÁLêø5!½¸9jzÓ®Q8ëÓ(ÂŒ™$íþ·Ô›a½ 1ãD¸ç}F¼ì˜fÝ؃tD—!À«ÂŽãO7ƒZbyhmˆXEú°õ ³OŽj¡vÀ
+DÄ,*fF˜{¯•KʺoVG®ì¼V‚7œÙ”´¯Æó¬ëJÂÄoκûòRjѱÎtÔ×lÇ•7ìrÌ5‚¤|CpÊ+#'¬¼fð§÷kÜú¢¢^€œÌºO p¯ÿƒà>p<^ÌÈ®IÚh5aC…u÷Õ±†Tµˆºcë’~À^ê°UÒ2óꨟr@ýÀK‰I䔃Š;3P+Ó… èŸ~ rx,lµ6T²‹m¬üNßlÏõƒ¹öow^µ~1²ÿ»76{±?ñâܪ ´¢ÐÇ®*HÙe¤°žÑ|¬é)hY¸ˆEÔ™õN¶&g“€{‚àß"fþ£¬f…“Fu¤’ *êÅ<
+f«d§6TÒ èiÜQbAg~G
+ÆùÇ(e‹ ×É[K6,|᪎^‹šX¤53ò«Úª}JVmDÇ!&HÅó¯o}ƒL¶”­…ü°ªƒ¾0€Ër Õ³¿nHw /ÌÂÅn…d†Ë-(kYF)ú8L*HYäß­\NÜĦyŸ¸ßÑÊã"fD#æÙ^‘²”¢š½RVé.%¿h;Ù¨Q(@¦¹”9”;û¼!#¨f6@×[ÞδÔwHêÒ]o°9½¨1þ¢Xæ³Ü
+jÉ–DTLŒo´þ”³ÿê^óã¼] w+i«:.Ø•îG-¿mYA)ñLP+—³
+{bVÑCDÏ"ƒÝ<·$#ç­jxäU›`zјÔ³¡O¾xÿ¤5üaáñâfi«w:GðYóäËs¿a_PKCSÌæ„rcÓBÇoÚé-!.ßÜW¹îyS†OÍiõªÛ©öWøÌwÂÂÝ’¶3ÛMOªN"j¸yÝÆmعDÇh\’5ŸCJ|® ºYá èð6uç¹=þÑæ¼ [;'îà —„«æW·#3NÜõ@µá?¨¡êInÆv%ÿÊ.§”xÞ3N»Ô0±`¾g?¼¥W¢³\zÜÌ"Œ[ÖV_´½¸|ð)áà6—ôæ· º´¦fãÖ4ÌÆ5=Œ‰jÚj¢:¨8¬k¾Œ¨ðçCÓ´jÇ”ãdWy”÷…è,Ÿ‘xCa ©re
+sfúQåîår>ª2Ì/‰Fx¹{BS[w…~- g^ƒÛ‡Èa=T¼"'ä¸GéÀ»„« W'HùÖ—y;MO/o_Ôªáú¨‰ÇôjÙx§üNÑûA({ABÉ]7ÀMÚPÔ
+}cœzœVòq¸ñ”íEå>Ç˺cQ#“° ĨE(¹m«3¢N¯JÌöh;™ak×CÔ*º
+H‰ì”éSçÀ¿w¬™~kfÒvÔNIâ sKžÄ`À`n°¸%„®ÕêZ­n„Àv×MêºIfœ8áÔ}¬Vˆ+v‹1` ¡c/ Ûéÿ‘.¤É‡N›ÉÇ­gš]í>ïîó{Þç}¿ûî'ÿôãÿX¦ÁŠ0 V„i°"Lƒa¬Ó`E˜+Â4X¦ÁŠ0 V„i°"Lƒa¬Ó`E˜+Â4X¦ÁŠ0 V„i°"Lƒa¬Ó`E˜+Â4X¦ÁŠ0 V„i°"Lƒa¬Ó`E˜+Â4X¦ÁŠ0 V„i¼9"o-Ü7„SEEõÚÑ: ,ሮqŠj„•Ãôÿ‰–AœNÑð…®SIa•N+lÜjúVIqY÷B‡Lrßû>’Kq[!•B¥¥ovJ% ì4÷z‘>8%ÜRnÉÉOdãœçñ*ùÅåü²ª2~EE ¿ŠËãó+Š«Jø<A_À+/åjþk¯¼¸²²¤¼²¼¤²¤¬œ ñé{ŸÏ«¨ªª ƒ*NF ¥<A ÷?‚þãsàÏÉ ä(978ZN;GÀ}ï4Wtƒ¶ª‘q ?Ê•ËÔ¯Aʪ¿¿¬•Ñùw1éQtÌUÎ…:™Y%•]¹ÞÛĵpú¹'å<~Åù“+:Aå*ü
+î ·¬¤”[ZRu\@åÏpüð‡A?œùÜ
+:RþI¾Ü“7pŠŠNR<žÿj.¿²¬ŒÇ ‘ ûñ°Ìúª”îI.º^]o6¬ëÍÇÁ<$¦Ýò«Ù ¦=‹@ƒx̦?ôúv5ɵð`YׂÌ#dئÆCmÖg‘dp:hìM‡ìêÝ%sïó9mëÓ/åõ»3òºô²².»¢¬Çý*aÆ#¹HD¬ªLè£{éðÝû©ð„=¹bLÌk…/¾­NÍ2+£ä–´M¸Û:L…Ç O;Óú¶\È8˜tK/ï-Š«vfú.dÜò&2vgÝò+X@ÓIDáQµë± ®;ë›Ó^°ñù´”‡{ô¢×ññq
+1Ér~ã ²i©¸Ý˜C`ÉþP·=3Tš˜“_>\еA»&뵈ÓËåþŒu÷LZR ´Ï”´lwJôγ‡­¿9˜¬$¼†nÒo“Saðlv¸òég=E;_ œßùj |ûQ绩I5íÐNì`Ú£kËx¡xÈ"ÏGf"æšHùÇ I·Ø_2$ dtb‚Îg‹hEÔ¤¢%1Is~]gÆ«nÎø žîz6`è&"š®¿ÍvÙ™•QuûbQ½ŠÛÍ…˜Æé9yþ¢vohÄÂc0uš’>uS­¬OÓ5¢â&0¿i3ç¢åžGݶùUß<h‘eB°üÀ¶å7l¶£o]÷¨53HÆa9ƒÔªQš *¯å|Ê"¬jÂC@ëëuú{q«ŽŒÁRÕåPÃÐaP}=Ò´“qã(†h»)Ô V1Ñób”P1³’ˆšÁ„OÕ¸ï‘7äÂÐ0¹
+¼Z³þ±åº÷šþîцÓE®»R!+ðÃ’d8î? uŽ¥è^ÃWo}’_»ó0›¸IFF5«
+ëF9Ž‚]ªë#b¤°n3’ô˜ŒWׇ-*bõÖŸˆ¸*lØ­G[ÎI"Isˆ²G•Ùˆ¢éЫ¬ß_R¶xþØ^ «;ôßtˆaèÀ-«þXv)16Qº–ëÎ1|Í8J šÎœ_ÑHÁîWkô\­Û ”_×±3/¿´³¨ªË fÙöœüýÔ²¡Ð=3`X’ö*ê=òº¤Ï4ðd¾Ž!“829yà£×ßòpUÖ'¾H†dõDp´.Ô´ø´)?<”‹ºl¿E²ÿµâÒöÃÓû3âÊÃeI5á›ó~ËH6õÏŽ_9ûóÀYz-J±°yä`e¸úÙcáÛûsíïà!¥B`1±xÔâˆCKÐ9‘ˆCG 62ˆ°
+ âóË-‡“@Ç`º‡ôi¿±/µbì"B¦aœî¿äâè¥í©ö¢Õ¿¶ýÞ=ÞöVrÚä<.]r¸²75pöé§-o?ÿ²ç î–×b^•ðÐ^Ÿÿ°ö—Û¿ÅW`Iu@É%±`ëó«¿Ú~Ôò;Â/¯%üŠ:’ÞkHÔ¡Ç£E1IñˆC—GÇm¯6]wòq‹‹Zºt…É»… ×-"J¯ç ¾;ãÑw`n}g>õå£:Q!¦åmgÎcQa‡«;0ŸRX@é^@̲üª¤P›†Š™¨øäm qR~‹8pÄ “ë+±aÑa«V E÷ùñZÀâú¡Ùþâ'3Cå…M×ÇÔ“‰©Í1gaËõÑÑ·7ä×V*
+ Q؆…eµ…¸¼¹°®ìL#CïÅ— ëvŽZ”½çdCÚ."vÖÔ]…¸¦'T4ì/Š¹€Iv„ÞýüåÚÝGĪI‘
+Í/¼²~Y ¶È_mÝü ½vmÔ† 8Ú¼yŸZ»;…Ç?|pˆZ€,´dÂ@s1Š ›“ŸPk&®¡<wM’1׳ëÖ¡ÑþÝÖa€ŒY
+ˆ"9L†
+ML—‚ãc¸Ï,üê>Â'é*G¡Bè)
+¨G=Dø¬ºœ[?’~§ïÁÝF>áV±û/FŸÝø$0}óÓÐó|aÁ,ȬhÙó’ëGs£ ™9Q=¾.j9|Ëú*úâιäO=_zžv}Ñ÷éÑËŠý‡Kܺ÷¯Ù—3ËC7(Ÿ¨òŽ\#ý¢vÜô¥¡ß@K#•ØØX%j6V11ð 1i*gZ‰[ÆP¿ªŸðª*#òkP/> Kº«Qþ:2Ò’Yf]DWÊg·eÂ;ÙÕÁÚrD';I˜Œw,“tT¯ÀüZ>æ7«PàÔ¯b>“ƒ7…ìß2Û¨äØCjkÌNÅŒj"¦•|X¶C¯€G¦éÔØ<*;ãOÊ)ë ³?ïÉI¿¬;ï¬Å}ƒu¨oèZÚÕóMÚÅ©]
+ó°"¨à~%‡ŠÊzÉ •
+ !£þ±÷–y¡à³Ë¡©©ˆ}ºèÓòrn„]Ø”±°
+v”ãÈH)†° ¿õÈ%j=vªú?¬AV„m“ÿMôw•b, g¡^5§’õTCüÛägΣãÂ^Tlò91¡ó²nõKÙh@ÜóŠï<güåýKA3y²v´ hûÏìÈ·éeÁ5: è¡ý’n:¤è#ý›
+™u¥Ð¸•XT˜Ï()ºT=„KË)ý_ª=Çè­™Îsée%‹ðg¹Uý9¸=çà6‘é÷• Å@‡Mš‚OÙw
+jxyçhk%¤•VZ¤’Ð(*[Es.qLUðÈ€o4ƒ”_/+õRÒ/gá^n#Tô—¡o²n93ç×q 6(J!õëø8ô_1„ô£AõpuÇú„N pQeÇþ” 6ùM°ãn<(î*'~Ö+íÚ^^Ý]”4Ñщžl™LÕ-’ŽšŒä@) îÀ=ìo{/,ñë©°V\Ù²=%cãJ1Û zM%&žç`¶ÑÙþ/2küÊ+é¬D J:e}”Y™˹¹ÝRFÞ%lÊ­ÖR>It9 ÷!½ÇQóÁªŠ•qNZ²›&1æRt£NÞô*çÒÞ¯öxUÙ™÷èùïW„ͱîý„&ü I¿NßPtÜЛ–‚[ |²ûG«ÜúŸ†kP‡vè$l³•êá‚g´-ëÞ̹%wEõ‡‹£×o/i¿øÍYËïÑe3s˜øÅUik~[¿ù¾;Çþ<ùŠý×슬rMÁw)`æd 渒w°¾.lô|ßà\ί\E}*ÎþÂ>Xä_?ŽvVcJìíûÌ:·.·>r¥èàÕïÎsþ¾d¿ù©ÿ óËÒ¦EYÜ”w.qjv^u¶ó²óÜÎ]>œç×í,pkÇE5¿M=m®ú¬†dm‡› 3ëÜ<Ú4f\RÈ<­¨rÆÀyÂ<Ë–JLÅ«À·é„JXŠ¨xD:f]ò=~Mmv˜Ÿ ¨†ˆˆ¤û4¥SU¶:.e:x7—äŒÓ¨ýÑé¶Þxš’°qo樂£·&» ëæ7–‚rV)¤æÒQBG-F<dHD&Ÿaû LPæ}’öœOÚE”CTB+¥âzäŒ;ð¨ÕN„ r*jÖW“fËé¶ÙVMêÕ•”^K'À³¸‹@ËTÈþ€
+?xJÅ-–ã*)£Nj$Ìü×M•¥ NBt¢JhâQ<
+L¦É{Ï8î°ÄøãJblàgÿ7èá¦cBVö^«àäßÈ9…ÍEïè]4(ccdôÁ‹R|j¶4k³›ZàcÛįQëÄǤɚ÷Žv„þý?ŽË쫉4×s5—=KÏôxÎ8N{{ltl§qWè ²JØ’@HIª²’}a°»qûØ:ÚØ
+:a &*{
+­!|Ê3ÄT÷™Ü¬ú*1«¾²ëëþššW_Ë•WɶŽÛ´DP+Ú™’ŸÞ‘Á^(¯îç·}Ҋ쬼2x_ôGìÛêÏ>$Ýû)£œÆÀ…šªìlï%¯¾B̪.¬½”ˆ=Ù|©ºZžËaÎçÑz¯º¼5Ùö·•'w_õœ[.?yW}êש;•€…!+Õä
+ëàl Ýjîóðé¡ûÀvýyð›<æÐîÃ}é°±ƒœ7ˆ³3°® -F‡n“˜±¥û)ôBëò¡Î³kϯXz$úýöT×éì¼I¼î·Šw[âwZæ-¹
+£ ,nl/`p†f‘k î2PaSÛæ¤ôËؽšß¾}ÞY±3¡8•y 9FÌ]ûI§˜)díf1·©´Ðïæ¢Ò^Ëa½ßÀZT¥„]¿¿ä¹•Åô-«ð¿d8010ÊÁ¹(âÐ=s’Sùpˆ[5(™Ìнbbp²ÛLC6ìu‡Áäà2šZ.eAÈ8Úgù.ñ>ãî/ÅmÀRú6ð
+näàS£LÂibãf—žOÚÈi 3ÉÊo¬&~ÉbâÒ6=Ÿ¶÷Q1ƒ¬€ë¡×fX1ÒÖ@‡41À¼D©ÍúUr!äzAÈä¨ÍCÆgýʱQSw>Ïe¼ƒlÚéd’&åþŠÓ]Z¼+ô ¿Ô»ìWZr8™´Y)ôÌþ’àHÞ6áq°À³\f%ãò@?ê‹ £,÷Zß ¸Q>j–gCêšræ/9ü€ËŒ<âWúï3q›>FwÚ
+÷ö—b·ÈySG.ï
+zMß ^™_0¶y°Ò5½ ßì_íN©«È9S›°!9Ü"ß ¢×â?¶ÚxÖsŽ‰Z{²ÓŠóä,Z›Ÿvtf}ö¶Õg¶ÚÍI æy;ÕùÕƤäK
+¼tåyËáÜœâ"ŒéÛ~ɘTÌ9ÔÔløäÿÜžÔT±¡AWÎo“s†úíW½—ßMiköþ«©bº:&ho>—œÀÇD¿Ùï< óÑF4Õ4pÌ®¯ç\ö•¤‚˜–B·uU€év|ê‹ì‚C Ü纕‰Ø{ ¿òÂÞLëTPr’+¯pÅEú¶œ
+?“f%4woƒƒ¸ÛÁ$Ǧp6åíN@(ÜØJ†uõÌ,ep¯=T\Ø Èχ¡\løÞÿ}7fUo¶skLÜ®å3ƒcðÌÎô ›ºaô!ŸùO\hø_4*X¡'—]¡ã(ÜÐÀ¨¥ 0ZÆ;TZ†Ï,9l|Úi-F:
+·i¢?I¾š©þ¤œº»¿øí$—ºC&ôo§4—Ö&¬ ÙÙ+›óñÀYŨË\ˆŒØ8ûî'³Èé˜eR›öüã2}j"Oãø_°µo|³µ[S»³º¥U3;㺎ãÖ®×xŽ,"Ž‚ G€Är'Ýé¤s'ꨌâ(‚„œû€€á&äìNw'üKöɾ xC*ü~¿çù~?Ÿ²P„¥³¡Ä¡Ÿ`ÇÝüË[.þÅí¹Ákt
++»É0&%ƒš¡JÐ:’]4ÈÖ>J~¬'Löý4n­¯ ÊJ\ý ÀD%?Ò»·(m+ùÞ–WÚ»{l{Vrÿ` ü'f²g[h*º‡þ»<uûXÎ)hZŸï»6«¼|„Y¥ÂFc)¤ê+ø•]¿¢cgAÚ’yÓwfû£èJhªëô{Õõ#ÛÓ‚æýÞ˜e sZ¶mÍuž
+Mœÿ}úYËŸ)äîÞâPËÆo=gh72Àè`É;ÔZZþ˜ë9³ò¢åèúTÇ·‰™ž‹O%玤&z®Ć_ã«é˜R@£<’¶œ»û,é4‘a)7þìαZPÊ=ü„átB>
+˹Ûó}¶>ô¯a68Øåiûbó:@ž,Pað
+ÖÞ ›às÷ê©á×lrd†Iاؤy¼Ñ%l\Ú]ôwŸ# a3åÓH7.¦ÃÀl–Çõ4n£€íKa\M[Qð»FÅŸ>Š›–¦Äçª~«¥G¥4¸ô°T ë ¶YÁ´øI6 ë^ƒ.Í0EÖ¯âe½â–rX-„Å Ÿô=݇ÿ$Ad²óëœö&fŸ¤§`ÇÀ3†:Ù.¡ä“ñ=&a´mº0A惪vÄnòˆM‡™”ÑVKã&Ø]=¥Å².AÛ[Í¥ã¤Ëd-ÆlOv¡OɈe´š²<e“ö©¿®ÈLDzù%÷ûôoÿã¸<Ÿ›Hï8þ"oóæ&“W™ÉL2É%—Ë —„›#„’ƒÎ ãŠm,Y½¬z]I–% SÆßáp°qQ]I»«nѱ¬²E»«âÜ_’Ÿîž“¶<Ïïù~?Ÿ®2~cå]Øky³íVcnG#?·ÔÈÙå°vtoSöm-¡ª—\;Ú
+æÔþœwqðži‚Å,س‹kæ Ÿp!å líÔ•(2
+™ƒò‹0‹tÄe B.¤²a§6תA³(¼<v éýì“ÒºÃÂ%o>.†íêŠì_eX!åîqú[Y›…‚3öæGåi*d•±¤ÝR‰ÚU•˜SOÅ,’&d1<WÅWÖ&Ï/N|ùkfË¢jefo3 ƒ˜O™ä,i˜bIÝD=cÒi›aç¿’³Lg…ݸòçfnþG&¹°Zůß/Ç:`9_+ε€Ï8\7V i/3ó$·Êù´f¬‘1#8sÆe|K Ü/"=÷ö…ôTqÃ8Ð$Ü&¡æH£¬•õÞR¨“‡Û"ƒ{QíÀûmeÇëçÒ³¥¨Iœ*;MÇQ3—õ-óùyÈv—‡JZÔU\/:ª» +ÓÇ÷×Í#íÌä2žë4áç²v”˺æøŒÃÑÈZtŠWk¦¾×ÏuÝõŒÃ —ïâó¾eaÇåQñÐýõŒĮ́‡'n®ìG”cïâóµ¤ï›¾£È…Ux6Êe¤ÝTLÙË“1t¡¥ïOgN&ã¾^ËøòYï]6¥Ÿ÷“qi›ë/mNÔ€§:L­ÜÜt²Ÿ‡®¬Æ4n
+C4ø ÍbVI) éÙ_Wžæé*mi:èŽwOÅ'Þþ0ýe%`ªFuƒ¥mär5d9Ø6 ¼z¢8SÞ@zJY ?íXLmŽ€Á£pÙÁ]Üå áZq¹Rj{Ö¤§8`Ñj@7PÃlšÃ6»C×ï®MÈ/ ÿñíƒßŸˆþV
+FÕaA+iåôÊF
+X,îqÖâ ß³±Å§,~s¹5ŠvŸ_;öêÑø‘ܲò$qwæ—ry+ «ö=ÌX%tçÝ~Ì‘/M¦>§NRQÉ7tDqI MRšÐ^­FÛâÞ9³Ék1»º¸…ô!ÏÞ® Íß¹ò×CÒfn
+®*mVÒ¤]Ïè¬Ù[\¦ÍÀc°· n×±¤ïn1ŽÚ?„4=Íüìb£}íxnÕáâR6=Pôq¸z¬ž™[*Æœ†ý°ËÌ’7Ÿð¹ÙÛ÷þzάãÓf-“4H°™ |Æjå v÷‡˜ì»fÁ»Ôz{}C(x¹´µÍ…êFÒí…Ž½ßȹfëY—¯‘G¡»­f>§¦ip‰DÈ™ÔÂŽÍÁï ^*fžfpaÞÔ­´aqd|wKüïbPÕEÇÚ
+f•í®#]•€òfú¦•R ÖII燵îÏ_ýÐñ›÷†>cÚÜÑ Rí‹9‘¶§0¸Y"$ÒVדˆ¨ óà©À<íÔÝ5 Õïm†ö¶5½å ôÜûÕÁÏwŸ^ý;ÔŽfP7Võ0U7曦¨ÍÞ¿W/ý¶ôläH-Š Iý—P°U‹é'™€ßÍݺ
+¦¸\XéøÝÁÆ8œ E#¥—6ÚÓ]­bfÌÖƒzõ õ‹OÃÄÅ繄öj=i7Ô³¨W€™'uRa³¨´¦ï§6­SÕ-ÓG(ú„Œê*×Ñ8jng¬¼´‰éˆÇÌ!§Â3ç*Û¢ð$—4Jj ³„‰Æ©°ú2—°*Ú,ÆA—0„¼‡†~«‘Ê~šPGjùôüJ÷úª¸ÿn5qgƒ#ý÷Á©äMÂ(päÑ p0otÒr)ïmºžËþ<OwZ;vg+£ŸHE_|×Ê©F³êÑ1ÓU¤UÌnÒNÈR“L ­Z8o.¡ecª~ž4JÀ÷ƒ¢“LÊîd²³‹•¤Ey´)è6oµPÉ™K|R3Ê¥ô|ò>å½%䜳 ¡±¸v„#TýTTÞµ»5ùÏéy6‹úk;&„ƒßðÙ…M6}/œøØÊ/ä=·yßÿI.³ç¶­3Šÿ}êd:íCÝÖ3Øã6vãiœÄK¼ÇÚlG¢(Š¤¸o
+‚«(9µ%‘¢H Rnóôãô/ÎÅÅwÏ=çw^´@«JÁwGÊ‘4ŸqjÚEÒs°Â:›A`%ÌÖ.âhgunÅ׺Ӡ•kã VÄØ`Q` è 僚’óÕ, ÎFVÛ³µÒ{T)Ž?mmËM¶!«:Åð,°>t-Ü%g=D³»—”2¨©ÁXnV7 O²Ò.ÒÐGð–£³õIi’hßµXðð¡ZBñÃâðÉFÌ®îîu7bí-ÌŒ«-úÜÁfÒz«úGe±çOÛ³ç_žü¼±jê“’¨¾4«‚¼5ÝS^ª±÷ì.­,|^Û0]–Óàù9Ò[No^ý•H˜¸sí¾:‘{tñ7¿¼ê9*¬[n7ÌT^:^_ÕœNC¥4nß›»ö‡òÜO…UCo3‰ù˜¥§üfðØîë?WWµ_—W\#ïgM—¹u8ï7ª¯„ —®Õí¸ çH}Í6P]5^àbˆÎiªu;Û;y›ú0oUur®±N1Kó­Zlô|mÝtUˆ¹uÕEl¨ö3®>X²\RÃgZ¬ Ø3ƒN ³±\­,£blúŸ °òÂعʼá’w[ÖílÄ,}•¥¡Ïö~þîSö,&(w#Š‰)—–›ÆrK`lƒ‚ÛÕ)þè,õ¸—¯„|òz•4ájãdHo3 sÈ"ÆzÊ¡S+ÌÕ*¿sûR†ö*Y‚ø¸‰!‡E—é°è4Ô7T§÷–{Žì-ß>ó¿^^Ó_®FP”˜|ÔbÃ?6S”ç`Íu{ Ù]¶÷í. ÿ³ž$ñ½8î@·äÒ“Ïà;y‹ s¬ê«îB
+5
+ˆ9ÌÉgìê«ï«1¦¥x(e)¯ óìx,åq|w©ÿ˜½ºTP
+zÁÎ[íéƒEÍ©cè뀸¨[ÃEhBŒû'¥”?$¯‰Içpî†ÑoåL·æýãk÷×Gåu¦d<¸˜Dt¤K³ ¾V[µõ‰q˜ KÁ\HOæ•ú˜~òß’Ë&3ÚKÕˆö ·õñ ã5>®¿('ꃘià™ûäo_8N­böΦ'ôqÓã…<w•£¨¦³ÞÌ/hN–7P}•mE7¥¨?ÐŒN?WR‰ÚºñÊÎÂð‰÷sG*‹ÃŸ Iº³ˆÞÚ{‹ôÊÀ¼ÿ~;ç•ðaÆ¡RNý|ÒWK¢ÆJÄÖßl±$.wÙ2íAÊQã¥jBwAÍ´7†V ÷ÔA+\*x§U Í(›8QOXúxÆ©áSˆ¹•uX-!0åHaþæ‘­ylh澿FBŽøýµàøÁú§õØÈ™à~‚°l¯¡ƒÛËö«bfòlTÏ’ŸñŒZ`ýRv¼ëÿÓkê©DÆÎ5’˜CÙ >áóNc-cø¶–2\9ˆé/4rcׄ,Ž7óS ÊöøL«à›jnÛÛÁ™æN7aï\š¢k)Ú#ÀyI¬C[ßùBf¬pO0[3mW+Äü±ˆ"¿–ÜD'ïƺš­EMW>,«ÿ.$ƒJŽ@ß@DÖƒñlà®\>‘ ¡5Ø·qÛ93ˆYŠ-»ëÀƒbŒ&ZÝo€ÿWb˜ö ŽiÁðd
+]¨
+=LIûÆ•Œ/¬ä(_'‡cÍ 2&À{%ðx!1q¯Y<hm’ÆÃoC%çv*:ÌÅ0x¦W²û­løy+íŸPRþq)A¢"t >æÖ!>fî«G ¤¨æbS÷×hG5J»*+¸¦Åô-˜¹”A,Õ¸]Sz Ù3géí$Ãä$j(/Îí-™.A¶F]OTò.}²òý‡ÝuÙY!F¤…)Ð!šió@}MóEyqäTáùµOþýäö %FÓMG*p7Ä$E´2ã?*à‰
+z §H´û¬G-½ÐG¬"c\°«`í{õÄôËfŽ$¥4:|4ønvøøÆä·¿‹O÷ÿEL¸‘ÓõoDÇ%=®Ê†[WO¢º½¨­/áRí$œ·ß/Y¯¶a¶mè£r
+ÑJŒÛ®ä1[Wÿ;oMçÞ/š¿>r¥qô4Yð·4í†hl £RÒjæBÏÄLø©œ>€ÞèlGdð›íyý…âKÕq%㳈¡SBlJÑe9,y|íM7
+båÓ¸SÎú&š9Ê 9^=''祂wZ‚ŒäÓæþòºéFΉc)âò½WˆMÜo$¿Ÿã˜Ð}ŽñÒõ$7`_|ÖC
+,á–2^èY~?ÇLÎð™Vl`’Ï’>‡»ªiûPyx<‰ŒIyÈÓ5ëÀ/ÿã¸L»›¶ò0þA8ó®sæÌ›9=3œ™)íte ¥„„ìqï¶$Ë–eËk6B€@Y†rÊÐBC Iï‹,ÇÆqHâX–lm–ÒÓÏ1ó⾑léêÞû<ÏïY¶wW³÷Dfþ¹”}¢n;P7ƒ÷Ôbà®´a7|s°!Ÿ αô½ˆP¸ñ‚ÏoïD\†½8©•|¬±aéodÍîªþì!t¢cÐÕÇœS›†O =eð÷K-{¯µ§• :¬n¸°_z úJ¹­eý³ÚsjÁ?/ç=~>ÈiX¶§0.L óqb”O¹­B†4C7›’¶aÈÔ~!îÁ„°åB„† cÀ¶„^
+=Ì;íj?ÞÂÍ\Â6°ýÒveÍ9(ÛK |°5µU#†«µ°uÈÚ%„Ñ8ºžuÙjq[ô€LÃü¢n‹’žš“3S õ ÿFž°±1ÝE6¢¿
+ìg£HÂu§™à}ô“Ñ/bó×ÿøtO9O@oA‡¤,xœ¹Ã¢Û{ü– >n9p%ƒë†@›¾yeËwKÝòßQ
+þÛpF‚b`ápÛOÙtûj$¶·nmƒ<ž™©;š²±©à¤”›}
+þx£É@u`òZÚKÈŒ?¨@ïªÒiŽ™^à3·—ªÌü¢˜ Ü®B‡mêQÊÛÑÂËÞ¿ì¼¢ ‘ÒlúûX9˜®ºÏ›®ÁsËê9Ê ÝÃ[cüSåäƒX™yX|šÁ÷«4:x?[M´VhÿÑoà·¢þ(熵 Q!ã’ Á‡G uÆiU³Øˆ˜AúAÿ=JŽ$”¼¾œ¶iÊ40OáÆO OЦÇÓdÃ:x‹œÅuå_­WJ¯,Ø5¬‹æ­Æ!f¹Ê®Î¬˜[…(¡)-!—v_¾Þ_4žndÝ`XB@oÓr¬‡‹b=pfÆ߸ œÆJ‚Ô³q—±,Ç<ˆ'bÂ1Öü] žË†.>n×ì­£o_¢ß¾{amÝ[6œb¦öòº¥ý líü°l¹XZñ6Ÿ¾Ù~>úÏð¼¼BziÙHúÜR,\:±³h¹ÎF=¶J˜Ò7Ò~ßqÁó¨à­Ø&ø˜µ4t©´‚¶ñ!¤]ˆZ;³óïà§Ç0˜»f¹zéQ26GºkQóµƒ}+¿fï¯Ó7”27) WSØ1wTc¦«2mêÓúöji/%CÈÿÕˆ¥'óðÒ2¾=Q‰})¤´­2xŠ¤ðZ"à /T“.«’¥H`° °ºô?(Óæ.)…öqq—IHÛ†›¼[£}^èºÉÖ6>vf/<~Z¡ñ12K¦ dF íÐ#çš¾ÛØ&©æ:³9;Kæs{‹Æ3¿}s
+èHHº1);ó¹à¿%çmúƦeð†’6]URÖn<[*Øa‘ò7Š“€ƒÃxTOW
+Ðk
+.§”Å4
+ô¥
+t5È€Žýè䤰„̱é×(uX›¡9‹ iLSŽâ²–¾Oó̺˜›úžƒë"c×
+ 2PMi/WÁ%ï¾féùgïcDЖR˜z,¿ñNr lH`œf1ç®ÊÌܯ%ü>5?yGÉNÞác>RÈ8x׸”B4r28TÝÀ´¬>šmo®ÔßÍü¢nî5ò$÷4µrOZ»Dé«¥¬}àY×÷¤Q 'êà)Àp¤˜ïÊ:Œ
+ƒô—–u-oŸŽ~¾ó|â‹w?[¶ŸhÿQYE®TVM­,Ü+½Ö·üï§Ñ¿¾{:ðçíz>“ãÆ69ö 1|@‚þÀ…Í—ù°µMˆa×å„]SYÅ»Ùu·–{lBÒkçCÄpåW¬sÿµñ,ç¼²f½R^5ž/-Nï½Öý{oïx·h¹°³¨=Å…ôçÙáBÎu%biÛ]ÖŸI<h;Á<îþìýKø„2òQbº­±±õ±!í™ÒêÈ)!aî:X·¶ï¿B¿«Ç(ËtÑ:ø³…>ÃzËËÆsKÚ•—µ_‹qk×aÎýÔéPs.G5êЬX¿«†Ñ®°½G{v_Üú±ïOÜk¬CŠz0ðmRɸ?åÓÇå­E,ìÊè)~}ðäQÞaiä‚·eæSî˜à\~³û‹þ"|cçñ&Žª9Û¸”Fzù°ñRiÙx^¢=$sêxè&
+0­
+VÔ†Ü÷-PŠŽæ©E‡*Z1jdâÓ›Ûg¶G¿œÿ²%¦§Vy„Ø„GÙÕ[o®¹'XÅè"\ïQ2Š#‹PMÄ(&«ÙÕ®)RÎÎ(ákÏd﵈‘CíÃðçziÔÈ©O­ð;ã`®cQg`‹÷i¹wP°Å;ÇÁEÌ¢‡¾níÑ$ã&ª‡k6Þ½0˜ïµ`ï#ñÊþ8õú±’Q‚™¹MÉn›WG-u«©Å ܳ±1;ÜZ„ïú4HÝÖ{âÕЯ1c—QRvø^ÂB¯Œ™È%a=¥(f —§íÜÖ¤•Oè9x×$½ ¾$%z•Ä<ïlו¸‰U›¶ ºâf¨[f7ÄÍpk@Ë­I˜ä„EANÛô˜IÖ[’ýZxó–WÕŸïÓ #&>ê`7cN¸=åt?§gÖx„”éø²9(:]%ƒ,Ì&"}§œRêÙ¦˜žÍëlFaËpKÜÂkßÔïtÊé˜í‘[y"™EÌ!¥&’þè§>ddVE­p‹[M/õj òè%Ñe~{Ä„4glâÞß7¤ÌßÀ•½Æ¯¡4,fvŸo?úùtÈ“59[õaf^[ 7µPù±
+xŸiŽ- îGrbØ èOÚåTÌ*%t0Þ¯eâbvý馄- I‡”|ø3 æ)±1(Œ&ˆ¬È™‰Õ!jC@Öí+»œù} œ{ÂR«‚žÿîˆçŸdLð¼„CBN® ºÃ6v“{‘^s¿<~ýe[ÆK­#=';(µ-¢ÅœüŽ Ø›#·bÿT蚀q‡“BüÊ3â%r ä øýè2qàŸ˜•Q“tpZ‚F~Ëî<R½¯‚Êc&A'˜zpÆŸ¬Ë¡ÿí o€ç/‹»²lµÀ˜ãéã¨õ‰Ü5#nˆ˜‡Ù>½‚ä× ZãVNcp‘VÔ“ Ãb!ºD«ÌÞèä}§OŸžªüM¬Ñ
+ü¯Úõ‘Y€j8UÇsôÂÃIZ¾_×xgE ¬Ý3#¨ÙzCþ~ûñ’OÉ©ð©‘:—JÐ`{Ñùµ_͹ƒYÅ]A·è¼Ö3G+>V²p! RTójz¸~óéºó?½ÃÀë3awVgîYÚͬ>Æh×Çûs°%j¶L©@MìÆ€a˜†š†ià¬(¨‘[w4Ë,vÏ@¥>„K[Eݧ+2jHˬ /"Í¿èùNxZâWöæzgú~pƒð«nºµœÇ˶¿»§z ÎRfÌÌiòd×(»¯ÔÄ|°¦(
+¼:n–“RxÂøù;lzù ’Ž”6Gû.íSrwÇú¯kée§N1åì“”tŽš‘–Ìš˜œY—1cVIï è·g[#ƒg›Ã²ôª„r¾74ØVó±
+ ¨ª?š'´ £uüæãy^Ãá4ëV@+h[ÀCêBznmH×ùçeÇs´Â½1âÕÏ“”<÷T× îý±=(™MA—æª
+FÂåti
+p‡Xë+öd¦<{¥ •lÉ‚Û5.áTÁL:»ë8=É4J
+Ò¶ ]ô8wÓhq䊈¸ÓCKþKSü#wü…k”öºð»b¬Çõ,;ýïuÉ(¥@¯”¼Â[M[Û…ª–h’ÑlfÇíö¼—™Ž³©÷ö»q^›„p);l™ 'üo…½PâÃÎ$ȉ‚ h ã»&‚9YGñþ?V9¨ÿî|Ýhëè«5ä#9'u€l¡BLCþk¾åf›(~c(‰Qà1—³Œœ¿t¼â¿·x´Ú²üëå¢xÛ$>àp ïŶ8 t"§Fézñ!Wª
+ä¯ízîÕBÂ6N
+:Î÷Øh‡¦*bîõ•ÛM|„=••fƒJ¢ŽÆØ ;b²·¡“äe–ÐÃôb²¯šŸñT^–äpÐK‰üª¬Êÿ:[ƒ=SV#Ì£¬è£qZ„®í².B$ÔÐñòdû¥:„÷õdUæÕ n–•Åí1# âOÛ0ÖÇ܇ò:•FœŽ•Dºð/¿Î6”|_ni8ù˜w ¥êÛrô¢ì§;ÍpÐNk.èp˜à§ä‚u´³´
+jì%ûÉÈ!¶qrˆUFÒ÷½5MHç1ÎçDNO²Œ'üXp®çª‰ÊJ˜MÎJ|m¡†
+sÙ—äy]ÍÓÓÿ³UÏû¶\C>a½?) 5ö|­£Ea¶»Ž-‰
+÷<ÿ¦äm³Ÿxú¡Ò„#7÷›¦¹Ù¦¨ÊÛlǼœå¼¿7]žþ¨šèPšäñ¯)î݉¾TŸô`·7ÏýûJÉû›EÀ=¤ÈuIÒ_š2įu>ëJÍ…×ʯ5õœ½ò·ÆþâPcî¥^Œr7“ ßç+à«í¸0t„ó1*þ×F“`o„–¬jÌxv4…ñ;™Dû{²Aæ!Œïír%²ŸÿüRÎDýxÓ6ÍJ2‚t9à͘óJã‡'k"”›¸8þq;6ü¾mŒž²;Tà§æÃÍTeÚ+y9—PO•©¦‰g›2ŸÐSÜ_m@@.
+aÎñ³¼~.rÑG£dˆE
+ôÞ ²?ˆ º6À:šî¶&Jzj.Nûªá/Õœì3 ƒ¶»if #ÂÐUè0H‹
+¿ßu†„¹(Àúh[ÉA–±ÊL] Éë`¨ô­NL^ A[íž{ýô°•Oxo.,ØN× pï$ª¬ÉrVp Vø™Ž[­7C1à\Ys»ÆB,wäøJXoí´¤ ›BT±7\™º(ÄûÈ9Y.ò²lÐBõGEyü¿-c¤8Ë.ølš‘öS+jùg§^`‘¢|jì—)Æ^„«QœÞë!¼4Jh¥<DÝD º^h,ý©©+¹Y`§ŽQÞ S æá ÝÀåEɆar® °ÕC‚üXã  3õ£Ý-ü«­´ßN)üÇB÷V#üx1W‹³M ˆÖÉÒDó%d·ï­kE»iEY ]s¶óÍR%æ`œfè-
+ÕwrâÖD´€íNŠ÷n7Õ·1Ù„wÇD¿øm®:+âZUO5HoT‚ø{*Aì=5?å²2õ¡QLñŸo%x“??(Žrÿý|º*OUwœ©H±[¬M³×
+³Ïñ f*“›¤¤× ”ØGôÄWvŸ
+ÓÿÙhª>SÒÞJ‰¯¶Ú1®ªê‡QÆ;»åÆüç“|$ÿêÚß:ÁLYúŒôX¡ý6Dùn²ÿ3\&\I·yžÞ·Ž•––šŠK–[š+n¸"¢,ŠŠ"‹,¸  .¹¥â’Š¨ ˆ
+¢ îâž ŠkZ½S3óž3çÌ™ÿGx~Ϲïûº8©€sÝ—P^Ý3JÑ~rQ~àxüÅ™’³NVÛBy d#l
+º§¬Ê~e”sà²*¸ã
+á倭Êï[ðÞêZ”ã|=ÒiN€r›â烕u¸°m)=¥ aŸèëüAjšû¯µ:ÚÍ?ÿà¹C Þ÷  ÚïÍI
+!öˆï³Í%逡V?e»o¶½rôî™îc‚Ã\uŒ£EFˆ½ZàdOÖÀÜWÛ
+ƒÿerêY—³ØV'´+.ò["]«²þ¦¤¸o÷Q¡‹­Å¡wËæÕ,a:áZÅɺ˜¡A×»q¾_* Ãœt—c% ¡m}ø 9ôBÉGßêؘK %íXF ÝîE{oõâÀ×ZiG^‘¼;B‰¾šàÏ•|ÔÉ(1ôl‚r9CŠ¼
+½ØGŽ²*y¨Õ¢¯i¸*Ë,­ÎeÄÞWrS6Ú <7»K
+ÏgEä C[¡Ç¢ë¦`œÕ58°±Ÿ•y:õ©|g€›¼ÙIŠØl-òYmÉ{nhøÚdäÈ«…jìú
+O5¨~ZøÑ03Ö¦à¦Û¬ä³ ô|ªšøc±Žs­¯£´P .€WAÿÞk[æÙ
+Âu£“z(e%žLpÒm€[Ü,Ô‘LSÌT]{I°Jˆ{ä°äFW[|9_‹¿Yl(?þòXú>èLQ ôÓËÝAb˜aˆ±6Tšv4ÍÆÍ4äøo´b¼,RRœMQžr3OEüXâ‘®p162]›æ`’”§üXª+»T±Ó­´è¥VÔóåf´ËR3ÚM#‚-“¤týçbHKI¼½QÁ£ž)©)ÿ§¸J¿’Î×ø}33çži¦Û¢VS¶\³&³\’ÔÌDÉ} \
+¸‹K²
+¢ÕtÏ=÷_¹¿yñ}û=ÏyžÏºæ_ûîwsWAÔBã›è°›Ãž@îò*3þ9IE|â·[èÕiÙS·‚Qpºà"úNZÄÈ'ÈIo\‹TDÐ ht/Ð>)>Å:\ùp«·ä¨uémäð
+íT1óö¦éç+ýÝ!£îSR¡gKÄ ¯›t¾†Ë
+êÒÝ .^®wsÝ`ïµN²s¬éé‡cäÔÝÁ긽Áª‡. 1mw´&þp†r$e!´¸çó´‚+{ýõϽ²˜OA‡ºHoìóØT×ÜgUœu¨ì–WÚRèVòkA¼*,]–ÇZ„e1¾9Ò»ðs8CÎÙªKö):©¦Êk1!ý‡I|Ê–î‚ÛNñuÈÐ^6‚o•‹÷먨/ÊW럃A‹p.µíO4AŽÆšR“¤”£QBBPÎA»em¥:^Áõ½‘Ê8ï" ΰօ¼a
+o÷ôy ¬*»¤>É+ŧŽxr"«Hˆ¬QÞŸéYë
+h„³µÀ!g[þ¬¼«bå^Qsóc5ðè)rÊJnöÍÍ |Ò¥wœ
+Ò§`¼w]w¹
+ÃvíÉ6ÅkhÊ ™ºÚBFÛ¹Èýp4Ó ·MÒO&1Ï¿è©ÈºaŸ#¦9¥ïÜr, ê<*Vi
+Ð'?¬ôÇêøù7ƒ:VOÅ®t|"@v†KïYÅè'v9q¢h¯ðkD-»Â*Æ';¥Tȳ’ ž^ÔRr?Ï`Öz‹h;×=j
+4´P|ËlìÞ,1#h2~“[Ù ?šÆ$Ÿ|Â%:æk^xõdøٻΠ†<—Œ”}¦¦ ÂzÌ!©yeìÊ¿¥hËþUÍóÁ@Ùý‹åÂÒÓå7ŠÚË}\«§j&Ê>MHÜFÿn×AetÄÁ\3ô»µ³Ë§¦Û&‰/­âƧ¼ê7ЊNõÍE-µàÔЦye)tê5·œÝz±5ºxju69ó@B€M Wv„‚ÏJfiÎÓ›ÿèk‚gœYúF]zíhÿò`ª:îx¾âÑÞdQìÖHÑ“ï{žËí®¾]°Vè%—›=ýaSwÛ™A@<Óµ9»ðd¾¶?ÇÈ£¡î¬q›’ƒÒa¦["DŠéÛ8=Ã9Ày嬢 Q@<5ó¨»`çó)ä\àý*.& áÖ…Wødÿ2§Ú¯g»ä´l÷æT
+=ºaÁÁ'n‘©«ú¨ãÑ솪 ­í(‰ß%ü±1FËà 7úk²ÿõ}]Ä;ÓÓ@-¯Ït+ëÒg±‰.mS^ÀÔÖ0ñÈ>€õjx˜¿q^æÀ,œPÔB|rl²uuks
+<¸Ì®˜¹×"9Û.%@òÿ).¯¯4³6Š¿“!‰‰šbŠ5ÑXÂ$š¨QÇ^°&l€‚ ‚€Q,±`ÇÅ‚EÅAņ5âL237ßò½sq®ÎZg=kýÛÏÞñ?Dl}SiÀFÚËØŠ ÜWRòmKµë¯â[?!iA–íq4Áεéd²k½Tb™$¥î”:Q0ü³÷zg^ØEö'xw¬fêÚPÞêê÷ÀºÕMY’ä¹*ËÂLjˆ‹>ír¹–a僼£/g¸˜½~L F”â¨æÁÕ‚d×Õ–,ÿSuYÊ_+Œ¢ÿt¿?ÆÌ9›i¬9™y`g;ËÆi wDpgS?ÒïhŒ
+ÿ¥—JÏ&kJŽ†ØûJN˜Íá¦al¤^‘¸Ú‰€íLáÛãô¢ï•èkm]«Í ï´üÇÈ‚„{0ËF]®ò9?7Åõ¶5‘Ð8JI–D=Q5åüø.í8
+Ÿz0Y‘|8EÎ;™¡¡ÿ2ùV-»bµík™%§ŽQÒ/ækÕ ¬ýQaáF)ŒŠîÓLœ«–OõÓòi ‚_o9Ò¥ƒt'bŸë«ãz¹éßø`Ó!bs <èp’ þsƒðd¦ŽnV‰KŒƒÕiß{h›íŒÐí.n¼±·,£½ÄØ r¬ˆi°ÃÞZÄaoͧµffÄl5Þ뻘;WÉi»*qé|KeÐZ5æl¾©yg„‡:
+ñc™Ã¶š¯ MýV­ˆ©kCÿ¾Þ ûB†u^P¾ÑŽƒ}kF{îõba–ÁŠ¨³Q
+âr’‹:£ôrn˜©]’yþµ…qþµžtÐÇÍÐJóÝÁ<˜i[
+. ÊýE1}{š[hžæ ,süÒ‹ÅZêõ¢ˆv>Ã/3“£w»JßœÓR®çø£ aI{Eæ>|ŽóºŒ5ªhºRKèfeY°±»èƒeœ„º^“wÙÖäíW«u’ÓYnÉÉ,«ðjE,¾ÐÖP¯Wkª,S¥Ñæáâ›(?“ûvg°*¥ ›üHQžíªQÞ(yÅWÓ’ÓQiåv;5ê°—™t¡j¨2õÔ”,ËŠý‡«’-cUÉçsœ¢ËEnÉÅ\Eêœ4æÙDM´“±~©i¬û‘=° mtÞšºi{`ÆÛ!Çì Òb]ô
+ªXùšnrÐt+ýZNöá=Fxd÷.réiâ|“™¤Â!rDÖ-A&ÊJþã-ëñA^
+ó‡tá·ºxG* îxº»;%Æëûè‘+ Ë늆¼Å†ÚèÑjÒ‹>R‘ó ¹Ôe”^å?H½PV?ù‚A8abCìüŸ»
+¿>
+Â]Rÿtn÷ÑËðyäD½r³c§Å<êªÊx¥æGû ùšêr뢀t ò¼¯âdï©è©V Ÿx¥ÒO5¬|Cw lAŒv[i ½_–QBë
+3P!AlX„O¼Yz“šy›•ç¶özÉ
+|­LŽ¾ßP52ÒÏ4"ú颔cV ¦ÊHC7*d{Ÿ S7ú™±ºNr°ZZæÕÏuè¢T€Zq|‚²3“ï$Â|n<½ûð< €×CG 0vk\TõÓ þò¯©Fb]$#6rµí©×Õ¬ ½´îpô’1FÚñ,%õb‰ƒ9™çãöT\¤i˜Y4#ÇÅJKÒ_ð²ïÉ
+Rï’’Â ÃÞ:HÐIOÇ«Ó]f„9/Œ¤Ì¿ÂÆÿK†6'ø(°nø(XØR|!”ƒÎ·G'Ʊoýï§Î€»“#ðÚå1àéâ ÜÞÜîߢý¼€‚ˆ@H{y–Ó’4Ïݦe"Ït¬~„±:\“>«%ŒÔ1ZH"L6”›ŸiMŒ‚Äø{‚Zu>Áü‚÷@„÷ ÀïÑ} éÍKàýk7Àzˆ{ j9ÜRL¼»ÚZùn©¹Ì{Šy¶XG|½ñŽËôŸ lãO$±“H„Ø#’ ¨%¶PKb/J‰JÙDHˆ]$–No)¦•v(míUÝQ¦õiËô¶Ÿ™;í¼¸/îÿrÏÌ‹óö<Ïùþžßïœ3×ykj kÞÒͲ·¶û««ª1¹¹P%L‡Ì¨(`ƒ -4
+Ø,HeRèX
+³¡ˆ|’è!@òõ¢¢)ŽÎ“ŸÀÄO)ÅÔç7Û>?Öþº*~^7^~7¯J>ºc¼´6¦ŽY´((.eK NRŽ»Q(‚Æ >”%ÅN82 8D“ˆ@ö
+ =–tóALhPšœ„)æq°]•yäy}5sm 5áxÙ"y±`ÎÞq™ø{îÞô½Ùþœ»’5kÖ„NÌ4]s³ŸHÀƒ2bâ€M¡“ƒ23!„
+L2‚¼ý€H„ìÈ8è(ÜT6Å¿˜ÒçŸÞµ–ŸÞë¯|uw´éádoÆbŸ†9®hô›P¶Nh;‚ŒõWðbbèa…æL@§AvT,ÄÒ˜F  éŤÒQvС˜Ï…Þ†2üæˆ:æ|ËÞz¾Ó×òΣη%]lZêN×מþl/ÿÅmO›4émÒRl.ŸéQ,ðøÀe0c?öö†*(ADˆeBIJ†W© 7Þ" 9š3Š¿lX›ÎVLÅï=¦üÃ9Ã¥W·»³=ýzö¢Í1¦”ZÄ…˜ªKé MI¾
+òpµ©iPÉKB^¦y™âã‹|È@ÜC€¡¨ï„0DЀ̄æ¼|oSU õéTWæɽÁºÏë?þ{Çå<Z²”¼^´Šºro÷ÈÃÔ•b\~R $1è ŒŒ&âKñ @¾ B ò"ÁÏÿŸý“‘†¼ðhà0XÐZ˜ë5)«À.©%þgËúÒÿLþñÒ¦ÿr0Òóac¼ûÓžÛuòhT÷Ë°.z¤½ÉW#)Á–ñ¸˜Ò$b“<Vངê†A|8¢–•©—0= uD§úJÜ›;¢g³ ̃ûÕ+. oÕi®ºF —º8s ãN×@œSÝ©«V_/j
+ÐUµ“zš”¤eûtÙ†ëçŽ×Kó=gk³öã_ÑŸïës—óâ©{âdkÜ°½<Xµ6gËñLZù‹ý6S­aºTƬqÓÛdfÊËä$YiS ¹®ž`ª”úª«™wtÚ”½){Ý©§Sr²¤œÝ7ŠÏwÇm·}§k®Î·+³–gž)Õ˜¦ŠèV7‡?t;Ëçî"‡ÆÓÙ¤ vH¯úÉ
+ëñ‰<ˆ %¡Y9 *RDxKM ã¶Ö$|63Zw¸Ð[òqÝÑz¾1føüxL÷Æ3Ø2¦©¦ ÉÊý× ÜõQ[Úœ£?q²»/Êz]R)’àÒb¹ÀFÙÉ$¢|‹âA.?ÍKœ–ãU[Xã%«¸æëPÊÙþá\wÞ×Ýqëùc‡ìÛ–Cû}tòÇË ÷ïÎÑÏ{®Áýn¹gzV2ÚibµÕ×ûç …ØÂħ@bd"ˆPšš‡×4*éV…‘³4`K_¶‰Žïºû.6g¦ÎÖe¯ó'Cª‹'CïWÇ”¯ïOwçG$+“=)Ó½ºãõÔæË•øjÑe/‘ y<Í.ˆ~ÁÏâAM¾«¯n§Ï[†Êß=˜´_lÛ;.6­¼q|?šYûqì¼ýçáÄͳÝÍþrWñÎ]{Ùâ€%ú's'{HvÚÝÐHÒ_¹BPT× Ò²|2â¹P,Ì€²¬\/©¨/NÏÄÈjkýúTræ“iKþ©gHýÛ#—í·ñ®óM‡ülÕÞxpK˵Ô[~òØÕ½97Tá2µÓõµ¥~’L¡oƒ8ßGsE\+ÎñDGBjL"®QÒD­(¯ Ô—KñÖ9iD£‹]t˜…{3Úôá†Ã…请cÓ?ŽGÐû°éÿ—éS[ç‡ÓN;ÎdÚÌ´iÒv:î$mÝÄvC“@¨76ÇØlƳ „Ú¯®¤«]´«@BHh¹ZÑ. Ђо€!ËLó§ôæÃûõwÎ9Ïù=oÖÅ'Ÿy¥¢8"aX˜÷¤”É?ûzõøNÝÏ~vû½W÷›ß{Ýüô-õ ¿¼ý·ß{öߺŸã_=ÿµd
+sCÒ„7“)+Wö
+ê¹EJÏ!bö™ "¦ÌàpÞ-óG.(‘Ÿ¹A|ÒÆK;!RÜÎÅ!\ìþ®d*²9õjØ|KàÓƒ%Á‘aMÛÖr£:ùäá:ŒI¬ÓÛjv[Cù*x…¬,Â87˜Â.,)Ø–·Ïœ°ìÄM%1ëÔ*"®°;b+‚¾ý5xĵ¢òl­ó‘ÕÕY#¬èØÀMZã¦tlð÷W‹Pô"¾%'繤„Èá’CÊ/»ÁéŠ$U}»äG:fgxiæq|‹ó&±#ÂîëDoi'¢U¼¶/*»M°ªÕ*“5ï
+Àzmú†S)ë2+àoVyà¿×Ä¢G–ù±¸]BË <|Æ 'MŒ—GZìç#ùå÷û°±²Î̤ϧschØRëÕê^オž!þi€ÖEšÖe€Ç¥b7íoÈFb[üþs©3çaŒƒBQÞ?§Èùæ5YŸb­šÓ\¡Àª>œ¶Ì>™HÍ6Õø§¶EÚœÕJèyñ>ƒùhY,¢æqëuRÁ×j.÷_r®øö®¸{hTA÷ª£×JA±¬²/7Vd›¥ .yìÜ›t0ú ³m^4ÿl*îc‡¼Ú EŒËŸN:êZ¦>ØÓ±Ÿï*™ë¨/ó‰ÿD´Üî‚°Ý£ãuZ„Ä¢~ˆ@‚Â\QÍCÃnŶÈO `Ú*¦¢=¥EMìÞà2©áxs¶õØ„f—¤ç/ºÙG¿Ù:~Zzöô•eY=h^´¹ÖÄoŒ
+FÚ.e|ÊÉ&®Å)jûÝéÖà. ÿ*®
+–Œ‰¬ƒÖŸ÷ò©ÇvîDÄ,œ°¯
+_î.Ú,ª¹«¸¬ÃãŽí%ŽwG11Ò;¢ú™æ°ŽÜ]ÇUðBô´•GØ_žü"eÀÝý.1g*ûm•
+CW!TvKø%Ç<:»ìáÚÞLÕGIÙIßœ;X#%÷¼2»;Ç/"%íàN,<BÚ*aÝ"vÎ4û2³9ÓzºN~x´AmM˜”ÜSôĶ%¸ÄÎÜô‰Ž¯ÓŸæ̼ñªKÀÊZI¯Þ…„²«€ÚzéŸ_-ºØø¢‡5–µ³ÏÌàHÒ
+×Òî…Ôøª°ûKZG®?FràÄ5ŒùËË^¹v+Èì>„¤«XOSfV[Æ
+ô…4äûûK¸º“]p& RrJÇi;ZÄÿœÖPï—­b:¤—‘Í3ŒÚ9ú“3‡o‰÷*;2qÖ,Jl`­öÅ´ôvÈ26)6ŒóƈÍg_â_i%#q£”’43ž$MCõ‰ü ªÅ×D4}7ãF˾xê}K¿Û’ò
+žI5ìãz!1¡c£cBõÞ›®ŸŽÖº®BVF«ãæ?¾×ÄÚ¼ubö[ÌZd¬¤‘ߟ4ݹ2Ò³6ö
+èI+€p‡Oȹ„¬c¿XTÙña;7Ϫù›kØ^ñOë+¾oìæ’BÚ¾ßRÛ¤†´c¨¹äf`>Fç²Û"²‡Œä<Ó[i»ÌX…Äœ/ع¸Ä*¡:a’03Ï«ÞÏQ+(ËØœ–Ý’ÑÐ…æ0— D‹©á–=éÎïLÔŸÅW¾K!y›ÜäöªI7÷Ô½—ýêžËÞ„ÃKý¿|qˤ[Œ•ôÑù z áCP©*y%bÄ·ÏÒˆGö–1U‚»±‚Íÿ²¿¤Ü÷Ü<[z˜\a?ô½Àýäï<ïR´ÿÃ9Öò½{¬é»˜[•ü?Åeö”–Fñª~è©©ž¤&3Ó]éôt'žŽÁž$Ú‰¦“H4î(î â
+²ï«¢¨@Ôĸo!*"( › QDEÙwÔ¤¦jþ”¹yº/÷á»÷ž{Îï(˜õúi,´õ%ä›e\ù½€’Ûåß#ú¾Þ˜žôob –ê3¼
+Ô³¸™Ïm³zwñ#VgHK†Åvð¥I#®4¢Ãž¬ rŽ;s¢Ú~ZÄôvɧãn!ÃÒ;´Æ¸–ÙÒRêÂzZOM.q­“ æ ÄÏ'k¤Jã4ºÂ¾Ðù4ªå´yå„·ýÈx/¡¢U¥ lTÒÄùÂׂ´u¨ï
+t…”…ÓãÛ$ƒþÝ­¡ÀOUt¸u
+hMÁ£x5\’KÅD
+JÈëÒÈ2šECqëØL|O$9×Ы¼Jìt ›cè+ÿkt¿4HbÀ“ÂNw@ÃC§@—‰éĤ÷iˆpšTçײ‘i«hôÇéE“WúV*bÀž«»ó\ëݽ
+Lþ¹÷Ÿ€’\V1ü”Òà«)ÎÔ2Z=rB™KŠy5 ˜r&‚Ö˜{ý`‘ÜpeW¤öEï"no@K®òª5À€ßÞýq\
+*ìLN¯u+Øm0»u‘m•4ÝóÌas'ÛzWq¥£éhûG`›Övi¿8êãÇ­TdØ@„o·ç{5­O/ö)íŸøL¿žÕ²·ÐõÄ£$À¢{#£1ÀövñôÕ±h:ac #lɉ´ã¡m¦%#jîÄ-â´MÀMÙx¬ ‘‚½°óû¯ìljÂDïpË°‡s<*zíÁ
+½Î2%¨h¦'£ú±‘„IÈLê؈ŽÓáSÐÊB
+NCR7@<ÿ@*
+Êèð¤š×~:Ó™éYî̲Ï"2·‡Kÿ®_³I:×é5æ©îlû&Ë-g!=Ê>´oS€M€ÿöÙ,q+©j üד%\õÈÃЪû‹/
+>h¥ÔùýH-i†Ðë²¾Y¡AoÆw©ÎUrÑñ,2ól¾ù·ÀZg–KÚôÛñ2’2ÑzbzÞ¯ ”ÇîC[L¤{Wߧv¥ìlrdWÈÈÅ[溲+„òÄ®€zú…GÃj¶ ¾ðm‹Ïe½ùÎ÷¨|Ýpõ¿¬c ¿†ÕÜÖ0`–˜ed4nZos®òíïw?Jª¿ ¯b ÞÝ…®•ŽÜ´Mú¬d$´Ííu,t=÷®ö@Û Z3‚ÛtÄÕÁëÅ‹=ñdÒ2È»ü82–ÞçÓ‚úÞ"Ÿª÷…O‰‡ÕØ‚¸žT9ê[ë~XÇ= nò©]!7fäöD´”×
+öÙ±”
+~Óp?ð ú¤™Gº<{)£Ê>z°ÿ¶åÎÞxk†g­¿åpžS¢ ny¥Ô
+·”ZâyÏ;]è~`h¹½=Rûœ»>ÍÿÚ1Go?—‹©‡ï±yàíï{äXhx‹ÞÖ€ÌP³›æQYãHRË&:;¤M÷v†Š¯éú˯Û'š3زÈû´ž…tˉE– ä]—ŸÓ*}2z…k•QvºLøùˆÏñÊÈE'Ó¤\Û(ቒÛ
+±½îzâÚÄj†[~T2 ÿl–Üm sý먧>Yç³K3‡(¨¾O…Ý¿¦l†^ìú•yéÝö—‰ÝŽÂýEX†mªðû|ó/>%­þlRéXóùaÓ멸«ãùvÈ:·øÚëöì¯ Ã°Ÿ‚*FëÜ«»³”Â̘q@|ºF¯²ˆ‘w,ÂúX„Õ7”ìç‘’ _[F¡?ã\r¹L-ö­2A ŸRÑàQ ¾ä“•Mïòˆ ³—Þ
+®öÿ9褅ё²0»zz}ô¬À**Ç1^{Ë5[;¹M¬(hÇ ê^%6)eç
+r…k…Rä\Â=5KïÙgPÏÏ·øØ/~2 h)Û /nèëM›‡øA}?.²ÃŤmÃ’¨Y,:Svûãoú¾_Åm«iUÇ È‡G³Í²Qã_ºWÛ²âW7÷&a·/÷8Ô«CñLÌ@j¨1a±Ò£À•Ö½1 Á–8×p ·àÞÿS\§I¦{Àg¦™NãL3™i1m/³r+-˲IsEqÃÁYeäAV·Ì}ÉÌDEDQT\•EQÜêÓçü'çéÅýö~w]×÷7ñýžâqvu
+>;Û†H°‰j †–¬€¥î¼ûÖaÔ³ Aê¥q>äÂ\knàá“p0×Ð꘠dØÁ}ý¾&Þ!'@¿gâxË]RStïË‚cÔBß<§Î"%'¯ b¢VúËBZóï® ÃÍBÂk!ÈHxfWÁÆ[„„·¦®‚뺖ܫ†÷E·uM°«úfXÀr/ü¡S”¯I‰‹Õ`ùÐ"¼­qJ¡m´òÍRþm!>öÜ8ím g¢¦Ì£°2Fžwª}¨åÐŽ@³€Y‚;Ä触Îô«BÊ‹s¦þòˆÅþŠ'†Vä½- îUµµmËÄr&RD‚þ@#À"Ïʨ+›CäDE'1®“x~¹÷u–ÇrË(9s­ð}K^˜}¤è¥±3ÿ¶¨6ïÆ|>ã@O¯œïL¿²2{äTV%oHËb6E¨×Þ)&fOÍ!ªÞMp³ïzµîÉR˧ƒYÑ2Œ}±>TýbSˆO<Ò
+nâU3hŒ1LÜÁ,—mUÔW®iÉÎIv¥WS;œ£W,Òñ'K šožPd‘”†oJð »aYZeèÂ$8Fy¸ŸàUq‰;2ÌÂõqFÒùqJÒ¯–1j¦˜ »3ÅËrŠ’-19e±³äþj_i趴굮5熊Ÿvq©ùøHßÐlþ5v™>”D,ô–F,w•EmÞÚW×äYÇjR­"zΩ±¥ï«©©Û£©C{‹ï,öæÝXê‚]½¼-§ålˆÈ©úämË
+Âœcäü#ð&ØT`3uÙ|z6ãÈ$hv©@£kjáZ
+¿ô7TÅÚ%ŒB»„u’S]bÄ2€KŸn(|Ð^ýŸ)~A쮂Çu+H5?ῳïrbŽµ¸=›º£*§:áÏÅõ©AßÍåTQ‹¶¤øDÛ0. ôUÂægÐ1e!†Æ’£Ô´ËhØÏ#ÄøûßY ž)Jîú§ÒGV!òéö>cOƒÍüfê½FÐ4b\Âò0.ûËbÛèÁ @µ Ãס›#Å¡#ðpŸ¡pixtÛæR²«œ
+fIe¯üì§?TÄ£Åÿ¹Ò“ꛦ!NM¼Æ#‹Å̹ó“R
+úÉ¡äÑÔí!f1%Ýelõ.wéN—Ù\¼<f½7+ÀÜ“`êκåÒTfîê(¥kôs§Ž\ê5¿¯r›¾¬q¾˜9¼}çÔÐK¼`_íÍ° ’º‚[>#ŸëÓqj|Z.Å5ÁBÚ¥„8ë`qý*b_Å,·‰ˆq¸ÎÑšt·‚M±#EFnË@ÃNÑ
+W匼 9³È­avÔä\›îÒ¢^û³•)^ &ãpŽI½X;Ñ{0%€¿:Ðv‹| M}`ÏÚ¥U¯tÍÙþÓì´´|D~œ ¢üú± ·Ü£¬±þPHzé'¥Æœ×6¥^”oÏËéñXÊbŽ |ÁêpåÓ 1Ý1ÉÂl+¸Õ»Jj"Î6Z5Û–qÍ:Œ?16wÍ Û“ Å>UF;¤„t—¬&kG:UALÛÁÆ®öU„m‰ÈÉv )iùcy¸UJÉ0Ð3'yð€I ûo%3ûÒ4·øÁ )ù¢¸rÙ<Xùz±¿<zZuy¢>󖘽¿Òˆ> p¢pû:Ñ8„z¦iÏ
+=YàòO–x-v°W6†a#ÈÈM1èp9:æØÄáìê‚CAÉq«hŸAм%ÇÆ-÷Áƒ&äßµ¿æ»àÁKC5 ÍåtlðÙ¥åÿ§¸ÌŸÒLò0^³©­™ÚÍ$©]7“Ìĉ“˜Ä˜õÈQjŒI4Æ#ÞTTDDN¹ÄŒ÷A"Q11
+ r(7jPÀå•Mjvkÿ‘íù᭮ꪮî·ûûý<Ï“jº¯f4zgiºîW?õ5¥ŸVÜù³šUüXÒ}-™iÓýà
+»%j€È¿o
+%1 Ähi5þeò« –^ç×hÛ3Ä¢C«ùdÉèµ?‡~¸>\—0ÒüìÛŽ7¾—%ž³v—Þýbb½³Ä—%êë°–…Œ®
+(¥Ú¯o+Û™žk¡%+jâób¶Y`……qŽÃî¨Zò£6ìÕ
+0–hìG¦m½C$ú‡Ê–¼½tþÖâÁ”}þh‹Ù™d×d}²k¢5ìA;³BôÈýuâ}ù,ê÷/³ZV×wÕ\ÒEº´èÃà^çÚ@=×%ïN£ž–:ÿÛaqbfJ}@ÓV|²Éÿ-âè‹Ú8ä+ð¢fvðqGZ*ìh“_EEÞÀnm/ÑkµÃèT¯’Þt¼*á{gHùŸGª~Ú‡ýêjHÙ¨¼æ›&Ö ‚dœíiÌ£ÃyB¸'˜_ÃB‚ybÈħ†ÍPGÀÈn
+!ƒX°=dži8­'Vqï—Mq_x•2÷Ã~Õò«â¬u »JrÑž
+j6¶ßh:YàMØŽ·uw†’¿“¡Ó¿]¤åüE]ð+Ú
+ÿë{VÚP’ú{óÈÉÕ1s4f†˜g&Rí‰Wᙧ™‡ ¯ZF
+ãbë]ƒG–!ÅîŠDìcÖœZº¥'ˆqfâ‘#Z&2 Âä¹'«“wÿ½)Ø_íîõ.
+as×›c;Ä j¨ðÃE|¾÷ckÖ$!7NRyï»=½ˆãRÒJýZf³…ƒv d»@-m7§¸Çà niåu¿_¶«¦Öê†o¸>²`{*~û¶‚T°£ ½ÜžÅçxæp9ûbñ‘‘Û4ô˦!eØÜ/?±õÉOm¢®˜½SFQœ3l^RK­ö­°‘º.È=Cú#ŸdÚÑIö曫âÊË›£õ=Ó¸BÛ&Å:B{ì–w£œ”ë#¿Áù‰Vä~Ð1Õš¹1U—6Ðü@óìº=X‘@~ Ô¶3O)>X&`ƒ»„/ð(IåQ«¤ïH/ jøø€ô·ž‡­ó˜'F«w—k‘6Þø<Ïò,ð0®yÚ:Êʵð^Xºˆ÷–˜µÿTÒK/­tÃnij“ÞuœW øf˜0ûÛæ4SOù5ßTSÆú(<ÉÒ_ïù
+Ü9枥E¼ˆ¢œXùìc 0Л}Ë„›¬öÖþRkþÞ
+±¯„ZÂ+ÒéãUéø¼c
+•úYO0_žÀÞ¿´5ŽÍ^Ÿ"½Åç^ø<‚L9^ÐCzýHËd }*\©}¼9MÛÿjƒ„g›|±G ´õlà³cæ^ÙøÂ`g–˜¿5‰JÝW1›< "šqÿÄ3G‡Wù!-^„ZJ.Ú÷\¼(.¾"§½¼r¸ ‘rR©Z\ñ£oø
+ð±ñ9'|Þ à_ÐÐ%t+˜µ~ˆ¿;Ïkë™È}u{‘c™º=}ìšÀ¤ïÍ´?õL¶¾PrKœSíeŽ”ºY^õm5Tï‘cóB „¢ãevSPÅiÛ’# ÖßU¤oH˯ElZÄØ)tÊÑY[“°ΩªDëPÕÍ馬/­ ŸCf’ ªÓ¥Ôò¤Óõ¾OQKWODÇÁø¦Û2í}¯ãu¢‚«.9"ó«£sÐ2Ž}¶(Açø×zßG½Ê5 á˜Àe‚œšî ƒ:6.`pC&*l`¢vfIùžiò ¯‚^¾9†J±T_]ª¸¶,xvÉ:Pï”aÓçD8›¬í…ZRú£SŽÍ>Z¦ÖžZ¨¨S ±s(¡5‘0lK"æ®îcSw&f— žÙ:E °J(94vÔøViðÕIÂÓ÷´²+»ÓLøνDÍ®Œ@¤þ©~ž{qg¬ñ®OA/3NÒ‹‡éÕ×ß3ò.;?ârŽ–„”ÿS\ÞOiåkß¹wænîÎÝÍfÌd[61›ìÆuÓæjL‚¤ib°k;Šˆ r¨JW`ïbE£‘)ŠEzA4›ìæO¹'¿ïOç™yßç}>Ï‘– éšøVg­O ö‚<Ô­`a½*{ŽšïXò@eD úb Wô§‚ü`ãò’fÔH¼¸Y<•0 EÇ&Ö+¼ò&ˆGÞü, £V…””k™V²ÎiI!À¯O`^ý0…ÊýÏæù…&èù%RÞ%‹°òJè-½BÞ×poˆ—âœ%W•½¤ýEâK÷
+2+¼ÌöÊnÚK£zZ«g½«=a͆·è舑\ÕãËÃúŽšàf7ɯeŽLÖ’Xp°„ÏqΓæñöÊ€‚ƒ
+È?ëé(
+ˉEžtg¦¢ÔÞà#žß›Å½p-u¾tL£ïy×:`{ó¸gÖéÚ[{Ë|J< ô#`˜À<Uõפ™Ï{¿Û7›½y¬'#×0ÏZ[Îáö•WE©³JðOÀ=½qdäpâf7°ÑYn¯¼¾Á.LQv•|;Ž}ðy¸,=¨¡!¥,xúx{îÅÝBú®6P?ƧîjŽA_h8×*íõá: ¥"o:ç:KŽÀüŽèY„ãMf›{± ²3X{EÇxñõdÛ¿55f
+®>¾‘ò…y¼òÑAi©žìIÊÒõâŠkøíñáiçj³>ísüZrà —ö;poy¬÷6.ÿã^w÷‡]"êÒr讞µ»€Éõà.'bàR¢×sèI›ö·|Ú£â“6
+Þoõ
+":.5(ê@F¢½#V„”m)ú¡g— rö]㛆T›yÛÚßð»_rÕ9Ræ™ÅdìÏrä=ÅW§‰¿ÜFAOMLÒ™•‚Ž›:àà­ÎiHð(Ø3¢`fD7ÙÄ3‹x:iæsNÍ<fâó¿,”æÈ}]W³q’ô|…Qÿ›u¬ *çÔü1…ßè®ÊÒ²+ïÚÄ ËJ[îGÖ‰%&ÐKÃ$ØÅYÊïƒë@퉾OÑôP*R‰G†}ì• \ËøŸ(ß]"ê‘·Öè-GF8¦§6‡ÕÈÇõ&h@ÑZ
+õĵ„.ò¬¢söeÄ¢e.üú½ð×0}Z[N…{7舰‘CöjØm
+FOÃj5Žb³w¤à Lý/ø¨è5‡Ë„× æs™é\ÀÂÜ
+6J&ÂåR« ?ìÌ úM-ãyßm çýdŸC<Ò UC C5Oƒ
+\ñ'§wg•P nºåUvã’VñlÒJ#&̘²€ªòŽ´ëΗR ã+×âÙ{·wOÚY`‘t¾ði¹]gÖaåßVÑÔ§]ùOG'"ò®-Ï>Õt7°J¯
+É8Í5Ÿtbx›tô¯œ˜bï[b¾mñKÂÄ;Hë#ÇHeªs²ú†_†aiN³î‡„ؽQ£MnÅ
+&ìûÑÖÌoxUiÿf½þíŸ#¨Œs‹ÄÒƒ·ä€²³8ª§Ô…ßQ›OL|AÂ*’Ķ…#Q›˜´p˜>9©4¦cv‚ýƒqfeÓ›<ò¾’Ór¸ÚƒvN°òåì†k} OÏI;*~ ¯ðÑîiÄ1\•jç§ØFª®í-´ClS˜L÷"šØdâ«@q@F‡‡5LdP…Ï (Ûr=k(¨F”E-(ºè^jÌ:³2h^éWá^ú×ÑC­°¸@þe¥aþÚ¦bÞo3ðÇ[L¼^ó™ŸÃ:>ýhS$lðËXDÒQta¬õeŠ†ßpc_ô“¸!÷¢u æZ¢TØÇÑ÷v§°Y“øW—Öº *È5>°=Ý]8 ”¤lˆš²ý*>%¨ëBÇ·ÈMÇ&b}Ôˆ÷²£(¨ïB L\ÌÒ7ßæ÷%@^NXßÆÌoFcæ¾Á}pþ¶äó"Ý4Ú|×!!V á ßË8Å׃+ÌjŸmèÉOQ³r.X^ßÜ‘  ³dXŠœ_qûì|^EG©u‘nA¤ï¯áŸGÞÑ›ÀžÁŠmõMœºÄªˆ…MóªY­¾u1/º9.;µ÷ô‡õ ¨õ&¶8´˜‰æP¿ãúþñ°šÑâS ‹(ˆ~ :Õ>‹ƒz×i +Wt "ÖZçÛaa#_pìN… 쎑Môªé/è¸EÐÞ ù7EÂUW]X'äÅÂþSS¯(¤aawVè%v)¾³ØU|°J.3·< •ßþêMË£ï"r{HK(sË0Ïvæš2Í#ðkn©|OÆÇ;¤ÝÍ _R“–Þ>ÿéµ_|¨BåîË[
+sµÿ§¸¼¾ÒL×(>kÎY§f&“6f¥—‰&1'šfâXcÅØEQ0J¥}ˆiŠP4{/Š(ˆ)‚±‚ ½8ÎäÌœùSÎçÅsñÞ¼ÏÚÏÞ¿}¢
+.°Ác†Í=Ó^ƒPäP1?‚o›Á%HÉeWñ]¯œŠ lŠ\ëí”cµÀÐSó`¸.ío=U¯¿]i-‹j†%ü“žûcou²g…Rì^òܫ令.u{ú\+L¹¦`¾»°3€x¾/¡)»JomËïÙF0ïÀž›ÔP*~·Ò°¿áKˆç
+úôW«höK{g@ŽIñHªãŠ†ì°ž„@Ï´ËIù†~ÄÃ#1îXn´Ÿ°oÈÌkˆ¤è ‚ª´‹Æ®Úûû#è§öBÖ()å»q|ú•aJ¶S҆ݙ@¿\j)º±Ö¶LÞ©zq¯M“Í0—º[p¬¢Ãrr‘GÅ%‡õ<ö™©½3b¤5tx¯¦ é^§@=jf}d»g>`|òè;8;óxÈîüÇ×6Í«
+6†™Ÿ ù÷Ô¢†ÔÃÙÖ*K}œȸ<„zwYF}S×–uýh™Ò1Å`eöê¢c–‹ iDÝ^m+- æ¼OMƒû6(ðȇéÞ¢£¬2 wœUt¿—róÌ&š ™ûå!PË'k”²Ý‰qö…†d·²©d#ÒJâùye{ ²M­ ̧ŒµƒãPqЖ)rê˜Zéu¿-e l3¤¯†ßêÓu´øô\rÀÔÙíÙ
+?6…£öUV£ìQ'Ú’]Ù ?Tr0N9ü[B˜ßŸ÷d¬êÝ,dXó‡uAË Îoü, o {|:.Î
+öÅ/rBÞ…|¬
+DACG«e†œe›¡ƒùM.=Y¥Á‹¤Ì¡†W+ܪGÒfè]pW1¥©ßÑóß~¿ÙÎqI
+B.Øø¿Y„ÿZ¨è°_ìQâs‚ ¿ûÖšávIc¶mœ˜p8Ý\
+qKéÝ1*äÉÎ,®lwRb£$['H)»ó@ö‘ŒQúe’”ì\":ÔÊåÔ I;ìÍÉz—È»!y@­‚>YíT1áðÞ–G/lŒ‘3ÕÄD§šÏ³-²áRâNoMÆý5‰ÿÞ…ÿä•c³ëDØ0%7*ýÉõ¿H»+²Â&+dèàÉÉ%‡ ÏÙ—«ßÙ¥5é~ ‡Úìh7Oá dœ¢«[#øÄ­@´Öö.÷9'©ATsqÆyFÞ¾”‹<QÓðûË ¥!S[oÄ<¨ýº#˜[èNã·°Én=é\£•éG0oÉ/þå[°KìJ‡˜
+9šÂ¿ý2€Ž1õ"îlôbcÆØð(dzô_gi…ÏŽHYJ~ñ EkÁeëpmôÞTM¬[†NŠh¸8Ÿ¦“®™ÂçÖçÝþ»µUäÓNÜÚÑÕñÓô©¥6¶2Ϲcw‰U6ÉGÅóªR®šúë Jfµk¾1ëhšžc$½ÞìÁ=]aÕÞPµ ¢í³Œrc&^ß]vãPŒM´MÖÆÛÆ/íbdääC .}{§àÃ2õqÊÖ;€÷‡ÓؤˆžZçU21®ÅfôÙ 8Ûl¡»V‰û&!¸Þó­’Kþk`Sþ´ >Ÿm‚þ¾N«Ú›B¾uHpù{ÒÆü^Rú 1»üÑÎ%Õ:ˆz¢åEÉY9W@Yü ¨~{q–sÇÔ‹Š› Ý…Æßüfg¨69°zÞa™G øôíLÜÁ"öxþâHúñ…{…U½=Y_°zãÁ<!3b “NTBÄHÇœYšq¿Xx÷:!ϹRŸþ«‘×Òñù§[ÝÃç÷Ô±ðAuìXR—d¬x¤lƒ\ÿ2‰O³LSʦ؅Ѫnhì‘(žk.Ž£—<
+jÑÚ:0‰›=j~µo†WÔ4ÔÕ"ÚF/5y½HÜ• K–ZÑÑTh¨¡)ÿÞœ¸è¾FXpC/FܲõPV¸ç¸èm%)Í9ELßgp)™Û
+ o}ÈQË£X𸫦Ž²¬ã•öñ½YféºçPàRõ DÐH+ ÈEûÓôâÅ.Ôc‡œ ?6µ}
+ ¨)à(Õ‹sa¼<g’¦]_|ÿúÚ†¼"æPËÅ-¼k›ëÂA6FÈoOA½üÏÉkúËÉküÏ&ºL.ñëðKï‘Ñõùq¡ôì˜ÇG&öÓ‚€~¬á¢ýJ
+2XÆß$îÍž’Ue~‡Œp ÞiÜ£¢¼õÎRò¼³Ì:Ÿ†‰R 3¯ØPÉŸŒBÈUÞ|–[O…Xè/V‰ìÐÀÃÌ­ NV‚Šõéê‘›àÆ'ÅEwÔYWeؤkƒä´[ö!L"ÈZùø«Ë)‘aß1à/NA]ɮYlòž
+“èÓàn9XH[“”ÌÝVqÐØ"ñë4ÇX]ºOK+s}ÄAŒÍÙ7±/.ZK£ZIÙá¹±÷Ï~ ¾ !fž¬I8£c¦Ÿ×±¡¡Ã¸¸sÝhȵ¶êÄ0mcáC•ñòàê?PÉçÓ ÜÚ81yk¢&Þob`¶g)ù+#˜ßW'jý oÿëlhß!¤{fùÔãåÖþÀJÛè'{ûìé–¨÷³E>3jg¬úé¶ûÊÐQµ.'¤î“Ó·k^:åÕÇ@yÜ懒ßõ $»œfaõ“)n^øfœqw†ùöÒ!í¼‚tvšštn©9'l{
+?­H¤E>ýØÔÈ;1‹%cSý‘ž]åS1 ÝSì"s;꡹¥6Z%¨z,C§_V§\j.~u~ÿúò|SáÝý±º¯
+(ÚÇe;&ð™û^w®™ý†ËÃ/éE ã÷Ö½öiZ§:VvÒÔ2ÍÔÈ‘9Qq
+3n.v!ƒ·GÉ™GV!cà“=“ æÈ*k8œ3|“dðî$:Ö¥‚/õ
+Ò a5<hèb* È¡‹¬$ÂÑS“>ÝML”¡Áw>b ÁÛjNÍRweø´¼<xN}³ÖíŒãNgø„ãÙ&™Û$$4ã2ýtòÒ7?øÔŸ.ål ?›'äœÚ‰€ˬíèHNÑ;¿~Z^Ä¡]Üî BÖ»+BíˆG:>øFüÅZÆã JR¬ß¾žQ6"£I¾õ’Êwô”­¤€nª˜¯nÍ´¼ pö¡bvuœ:¯‘O`•ÆÜ׈ÀÑ¿W$M?—D ç6÷ÐL‡o©jç…áfQf ÏHƒ àÑýôü0ŸCØÖÒà^ñƒk¨:ÕÔ\ôZÅȸk–æÿ1ÛQÚÓó©Z92q©MøkEÒr¾$äûô„L×:fMYq8ÃÁ÷_":)|°©$¥y'y[c4¨kž·3)Ä:)L-èç #kœ{¯ {ÉØÿp¶¹üÅú )˜«Ü5Á*šïÁ½oB%øcß?½Ð]—üdW_]W•Ey&k²¿OsÉnƒXä›oì8[•´ýÚ|üß&WàDü lü¹Üiþ±,ë:qˆ$Gãxž^¶§Ã%7RK|&vq›áÕ2л€Y„Ðû½è¸Kí¥ÿž`¤ÜôN0‹Lò’À~Ò‡+Äë J4€¹2DȾg—V‡OÐsoXy97œŠÂGË=È=ô¤’ò×GY›Ãµ «/Í°¿¦›áa®/E‘‡F"ôtA"?[j~¤Ûk¬Gxõ|”Ï(eXeÜm}ñ–ŠšæVR?8
+7M¸§ØÅS@—±wã“|^õ¡G<ž¯'œÙ<Ë(;še¢º«^™šËƒ×G(°ó•¦Ám=«xc¸6qm€”â짤$eχê2ïØäÅ¡{:6²…žÿgUZ„¿±_è™ä ²Ì[:áûÀ©Æ´Èo=yoO¦…ô“Y¹ÌcäTÓJÞÜæfÇüZKm@~(9.eUÔÒ§âGf øŽY’´¡a‘` ÛÔÜÄ'>³Tq`eaí­Ð QZâuIaØÅ6Dø…åèÓ# åÖ
+0ùqÏ.‹+“ÂO¬lú?«ÅÙ<›àA†¯ ÀïNáRΗdmÛ>y¬úÌ"ƒ=;_Kf¸Õûf6Ò£¢f»•Ädg/êùJåË5+‡ {ëGÊzqyöSy²ðÓueuÚÒçÒXkkqpo]æ5!,ꢚ›zϫƤíèIY.5æí¥è[ŠŽ`gC§K“
+ähV$;[ll>´I¸îqF¡A^jT”…Ú?c^ï›EÒsGëàÉœ˜ë3s*¼“\”[ÍEm¨˜Ï8±>!¬Ôȱo,
+jñÁt{Ÿó+!q„’u­·*ù¿£tHÀB{iؾŸüsùê(†I‰½ÚAÈxujeS~~kàmëI™‡V*êl®žrbç“vMBŠ[Ë«ÔÊP/ûð™¿]-êÓENÝ®™’³¡Â¿uTÇx”Ø×€¥ÎõQA$Hü%JnÔuÇP5bgŠ Ó4$ùDé¶ÿ3\÷ïhïÀwíÌÎ\ç\g7¶kóu¶ÙÎ9f;¶9Úš™9Fn‡“›"IEEèV·*•äfF%„ÖH*‰(¥$r3l¶æûùýýÃëz_¯›Ç³ñ‡m
+àwI¹ÿì‘!SÏÌôšõˆ'–ö÷qÑ»kòº'? Ö&_Ò’³щJãóžÆ\€ƒb/EôÄúïv&ᇃEüfe5ê‰e~"Å%¯ŒŸ•"ã©¥×eؼkFFÉ- © fš½íÔý¡k-»œ“˜Îê´hvaJÔ¢ñØ (KäBŸF5g%D÷T¿úÍÀÊ‹Y“aSÄ쀦6{ñæÔʨ?¶Š9fØ»•‘ùæâ€ÿfc„
+R¡šQü?÷09c¾«9}Š‡þg±™èQŸ‡>#Ôì˜HE!3÷Í%’/óh »4<ÇŇMäŠc›tîtž8xÌÐ¥dÏÂ@†·~î×ÒaÛŸ¥žQJ¦gœ
+ä†[ILhxUûF.: ôäžE@Þ_ì”ìÎsZwfièm=©xCYÚ33P‘E>å Á¾Ø˜ÄcGõÐ&âîÎ ÈG&6Ö,„='^3¶•Ý\—£íHE'ËüÖUu Ä®Àwg•>-%wož ™óÝjüëmz¸ † ôŠ3‡P|âðömLÂwŸHöÍ'ý
+ÜŽ“UžÀ?ÃÄûfxu[fI@Ï„}õv}Þ[pŽ"ѹ«gò|UúéÜ!äÚ̈MÌ÷L²Š¶µ<`op«ý¨„Õ~øƒ•Ñv¤¥—ðd¾ý`†¿µÔY~Û=\÷ts‚±·€—ºð 냰5y_íÚ ;Ë;FË^鯊·÷”Åz‡^[xM åÑÒ7ÕDÈ™³£ëËJGÇÁ’¸mßÊoÙ™m-, è‡Ëñ¡­g0²$æ§9UA§Ê3B8ûP›%°85ã¿›rJõ nEN´œø_¼¡­âþ
+Oq©ðÐu%ú­³öʉ© ˆ­1l¦±½ ¶ýâ’GÉ~¿øR´ÜWŸ'¼æAâô¬´ëCÅ 'K¸¼ˆ‰ç–W
+Ï1kŽ—™MÁYîØÞ.8[
+Nl|Ú‘Kñ[áG6à‹C(<_K8ùœ³%:bbT5´ü`ž\ƒ-`‹uÿ`Ž›h}gé%‚6U¬’YÓ‹M%6ikýÔ=‚M¶ÉP öu‰îQ¥CÞs}bemOµUnªp ÏPM‚_…OÙÑ5gˆ9«ã¨$›“|d ÂÀLM¼ú°Y@ŒXŒcG÷Щ³wìÔ٭صÒÑ~}$db×xÕô¢À$bÁâdÄì+}u9—M<̃¡6èÒ‡–Ç(ïoY¥xдÿÊÒM
+H‰U÷7Š~zúžÞWÏmïépµ÷v*ÚâQJkïª Q!C"CöŽL´jÅ–ÑX™$F!F¨º´úÆýSž÷ûwÎwÎ7›’d¤âߦ˜ '†jƸ
+_ôoppGƒ-ÜÓ“jN–9ô3‡w¾Êÿpó;¾;ùÂS;ý‡³½ýÛšpà_Bɾ >wðEÿq >œ¨ ë#ðd÷
+O6‹7¬ªn/ˆ+nû§1…&2¸ à­¢Îe9>×2†ÏÜ[]èòiô‹íãØÉŠ@|baµ ˜Ò]#àÕaaíýíIhMF)·K0i&~ýïT’gŒTèF¥™ÄU›2f펶›·=˨ÝU£2V€÷ì}oîlJöç¨ïó¸kâÿŒ/ùnp¿,Rê·T¤r÷&Í>€ˆß”ã³öô´º#ôÅÒ!òÌ2êvtêAÌÞQ± ·iQ.
+&Î F§8úyÎn|ªµ£ã"+ðàfñûRFí}neò¦èúÿ}[ äoÌ0ë|ja뚺›¡$fö K®ë¸ïŽÍÔ&·¢)qe¼á‰sïQÀ_íihõévÃÐǦxM„_Ez¿§e¬Õ=2wUÝGg†J`YWdØ‚p{4Á&g‚•áIw®ÿåcCvÄîdk­«êUPpK…K¼Â<
+é…g‡iy°¨¥~N>47ñR{í‹ÇA}凋ÃñIÁÏí«nëÛ î‚Ä\2uÖ&%ðLDéó0 á¯ß-̶33½åÄH‡nM!³Gq¯ÃùobÿÖQâè®y>ÙJlN¿:ͨH92³ÑZ>fck•D Ã_]fWÆ„LR2oºGÀ‰4`Ü/~eÚ—xðÛšxàÌÁ¢š°Àmmkñb_}ÔgJI˜[ÙVÖJ¿]wç²k
+ª`é¾ñº¨-)øéæ(0j ùY¨øíÃÐ9~ΣýE2„P•p_ÍÔÕ˜õÍ"hÿnuœY¼#3£u߈îj›^þéa²Ï\l!¥>õ¢<þñŽ–J9sôjO]=ºÿnwªÿÜâIN­­àMùÛ¨]6€)Ž•Sªžyè¢MiËë­IláÁx¼€ü°’!~ ¡tG׎ô©¹Ð•rv@+@íéÚYjÕÆ(<ÑÖ]óPÅ~9Ž)ºêìmJÜRq =OhÂñzbf"N­,ÜÙ
+‡yhæ×gYµv°©3=s½ìÊN¸OßÛ4vpԔ꠪­â«‰ٟÖu„Êàœà”r–{à1óÌêƒMé?ÉP¹WuÔ¼k«Ÿ
+MÃmiS¢æ‡jACô¶’Pné®2M+ŽÐsª-u·¦8GhåŽ nÕâ°°0,¬pÈèeÇ&ñlµëQË×”¸"ï4ù­_ÍnÚÖó ~£ˆ|Á_m“À’ürXêÙ2µÅ1‰²÷Ößw#“•4À¯˜’ëFøžo–Ù0ÓÕ /¹©צïÌ@6IÓ[ì¹s•º6F.²‘
+·uŒ÷û&&l]+ÚVêÏœ<ѱ…°k2ÈSçEÿ7”ˆW›3´j‹”_=#F'ÊiuÑ;jØÕ]w×ÕúÝÔ^|ÍÈ+»iî'¬ô4$z§ˆ —ª׋«¼Û Ëùýâ£j¾^än]ŽxЫÿGq™=µu_qü©}I¦Óédâz2“i&MMë8ÅfL2®íØØ8`lÌ.°dH Іt%]]-W»X,vÄb$´/èjmhßÂ@ˆÝÉô_éõÛyù=œóýïç{ò6fo\?ùì4 OÂRØ»E~NzÙøÙàý†ëU›ù¿´xþ÷›ù{hzeî£ð|×ßBó Þ5Fg÷ÝþñÍÛ×*>@ù!-Y»sfò&ü£ð\Ûuýþgg>­ä—ò£{üAß­9gƒF>ysÞÄéAµor‰û¿1òú¾¶‹‡rzvh—7°Êê»n•ànÕ>åÄà3{äýyÚÀîIÛ@Þ5§Ê¹5+Ë’Ô£Õû۳qÃ2œÒsºüÒ.”¥¸›¹ÝáÆ’qòIbOðz?ôíÜDëŸCs„ÛÉõéŸ#‹øÅVzoÔ÷‰U·€ž0 ñG»Üî¢[–ý♢ * ÌÞŒÚÝk*˜)Oênj_ÁE늩-Q#£M'üas¦íÚ™)›A\ÙÊÃÕ¼ ù4$â•|2èPÏÇ9”øæ÷!!÷2&Q\Æ`ùeD(.»¸Ãy»;c`w¦u`_Æ"$„–§ïƵܹ=å­ÝrÌ×G‹ Ç[÷²Î/–pe'ˆ×ÃC7¬RÜ­D­ûôŠ“O<óÐúþ#c.ðϲֱ{ŸzH;4óÞ ^OxkúÁyXÀ¯ù¸ÓçaîÔÕ@zd¾);iu?{ø"(`%6)?åôÔ§e;µ#¦½Ö’ÌZ€þSv‚§³&NwÁÎ+»e˜K›E„ŒE0š4‚¸¼$Ö½<ÒÕ¡Xtð“˜Pò«çª©¸€Á¢G
+&l¼×9+³3·7ÙRuлªˆð¨ˆ?ø¤¦¢UJK¡¡ø»5¥ãtFwY¿ø78Owø}·BÑq{yêݤqŽ—4i„Y³ÍB 6¶Íxr¬g¶šÉY@Ì; XE;«ý+ã¥}¾äpõ ˆ—CÄÔà®7ñ1%„O,# ~•@¹”31ú fFWÅ`s6V_Ò8ÓÞ~IÚ„„‚[.; «Ö3Np,kãà)6.²¥¢_© í.0ƒ:ù¨[K»¿¯iˆmÓÈ"òåq!ü>Hî­Y'Z+jOÑʎ行o§v“ÿqªÉÝ£ðRe\bû½]øü/zú£ÏõôÖkUTOÔ; ) ô&e’ÒŽÍòéÊ’¼&¥÷Øã =¼Lk¯Lý˜Úãô†¶¡Wþ5°;®‡5?úÖÁêMêY¯
+fheÌhŧ”'íðä±C™¸níÛ±ÀÎ?¸5OLèá‘€jò~FËè¨X9Ã%‡`¿Í{9Ëèÿ+<üìOÁEÖ«¬è
+¯ànì+[¿Œ®õþ£`ã ¥ìrVÆ¥}
+IÁ#œÎÙ„ü¾ˆšw‰&³6[´Ó_ä „{YÛT{Þ ŽûvY‚Éö댎¦?TL‚Ѫ]J/Z£8Q@¸„”MÚD„"—ÕBre%(d¢láU½üÉ„7V÷ð)g>!£æPÒfÖËÃÕéæÔ.„Ma¢[MøΣÄ5¼ÅÞB³ö­øÖpSÖHi+#<|`m¢%°L|\÷CŒ’À…WGV:þ~îÇþtq0úì4PËÙåc»FíÝâŽvx—Ñ9ï1åæyâTÝ”UdüI ¡öœØÃ57€=\!~Ÿ5ОŸbÝÃ诺X˜3²Ï*XfúŽÖ(Í%'8~r¤XÊ (ë€éµ€böü@¡ùˆúwÕÁÈ™i/³Þ›”Y:QòÎ.ù7':\KØ{ù}1ï§Jn€˜³Í¼.:y„¼CÆv*&n{g§[*.•<i`ãïÀΔ‰¸˜„IDNØÔ¢cç‚"j€ñv5÷á‘is5l\W§¬b"ª&kb” é4
+¿·ê”±¢z°W¯˜¸a_˜¾{¸Íä,rºg‰Þ|°>ó8þ•ƒ¿<ÈʘSt‹QýÌ‹È.£%k—Ü*iQ‰rû
+°°/šª¸ fýP *x1ßæÌS£’ôo3LjŽèä¤À*Ô–6@ƒõ
+bKJ7ÚßkŒ¢·UÆ*ezµ¼î.æ‹„ÍhNˆ­O4§u”ûk„Fd÷zcÜ©¹¸øêÏÉ=ðul‡‹ÍšèÄ‹ ›xáQÏaYÆÓ“Vºç³¢<²¸‘rj–bV9-å9Ì}„¸è ýŸãòúmë<ãðEÑ»^´EÑ EàÀE
+sw‡yuøÔ ¾+Û鯈÷€Ý”H0Î %ï Rã 'ÕŸÅ@r5pø}#Æ\é¤ùô³ }¶‰m~y‘¯óêÀ¿pMô*¯tÖ1þ&nf=*ÙáÕ°z¯Ïý"© <¹LBÜaœKí`œÕF›¾ ø¦ØÖ+äQ3÷°5ñÑÂß~÷ó‘E½*èñQVíiF!fÑÅyž5qŸ&ԼɌ^¬ÏþRºüðfÃpÆD;L ™#bïÒBö™—·\²ˆßdM’×Q-s"._þ¬îÙyØÀŽæ^p¥-ÐRÁ¥efòmÜÁzÚÀ$´ŠWͯ弊O°™Òï|sjå¬VÝjaÞí4… (%*—<9·*i=l¹Žá:¦Ë®½©slñÎû}ý2 ì]ó}ÕE.¢ÜwuûÑô™ƒ=×pÖÆ æÊ ÆXnuÜΚ®úx ¿Œ]41deK7ú~öÊE„·ßóiÝ¸Ý kE;c*G°ù *_eæ«<Ÿ3̲v‘ƒ—í¸Ûɤu„Tð'¼Ù“¤M¾uR!QiûùÛ¸žt7.Yü´Ñæ £î¤¿ŠÞÞÈë6o_áý° Ñö2V+"ÜoÀ•¦Ÿ>[óïuˆn³$¢#ý#¤&Ý­bJAÚjääœÐr姬w}0¹âÓÀ5ŸAã—òsæ~[tB‡½"bé3\‘cãŠÔÚÍò9DfçJn`¡—†8—EæÎ0ð¦Føÿ±ª18Úq«ä=VùôšÅ©fòVß‹è” áá ÇàËnLmf•ÖQ^f!e3É!õò mXDl£’:>®+cƒ"À®ûÈjÞýgÍ$ŸQÀx+1g"z̺‡[t‰·’¼ýó(—Tòˆö9±ú²¬
+ Jro;#C›Iª`‡/ÎÂàn+#× ˆ^þÏ<F'~0?Hí¿çÙÔq±õq5Vr>­y™küÜ6Äaå(Ïc×1ö›œ6Q­ ËlÆõWÑÉ
+ðêËÙŸYYk·ûQ Ú‹ê,è±+ç@HaÔ@òj !£~-ë´HÜjãª]ªŸê¼û)[Ì–°ú>½Ÿk:Pžù}q“ñjçSå<pÛF5|çÛcJ훺 Úù€óy?–âE!<ÄehÂ$_ )Ló¥y)®‘ÎT}à벋òÝ9ÆxÝAü6Ñë¬!cRàK ¤ßÊê€Y¼u¿tÂÞ¨y$”*&¢4#0ï=.3ÿ§"÷ÛëEvŸ_—„ÊŸþ¨WHS'àK·˜ò9J§}Õ«–OýJ^7!“·‚Ü
+L'ÔôÉê çe“`Ý ˜ôy^Ïxˆ(S+ýy?ó›ˆ^ó ·Ê.9e/•ÝÜÅó°h÷<ÈYiøöŸT<À\Â&_ó™m‹ }Ð*çb¨vïÓ-ûQˆéÜ\Æ*Ç_#dág ƒ†\ó+Ø͘r–€˜gÞG9«Ý °×%\¢•
+‚5F¦édf³3›½Ø¿g½}/ÎÝû<çdš¸ŽrŸÌ×ë_\îÝ\R·uvŠcÁC0ð5BÖj±y2¶³v,†BÝ%¢Ì\yÕ ÞösWiüä[änÁ¦€I;`׺GMºMšA»M“ <U"–#kž·VÉŽe‚@=›aË$5O?"7O?,«¿žšÖŒ@ÅW†`rº¢¸ýú\ëÜ&i²P.|Lü?»Ô¶Ÿ{ôÎÃ>Ù.×cµ|âØrÕ³†<‡ÎÑíPª±Û0Dùä…U+Ë×`è3ÏÉ à©Å Ù0õù@eI¤™:R~j²Í]øU í*áÿ/^5—óÅÑ“÷cu´ñ? ¦O[Î6»À©‚š-Ri‰U¤Ál™}KXzìÓËö¬Ê^£h¾˜ÛÓsÇ4ÅÊ
+* œ‚ÂŽsÏÊ0§Ëê™ï+Ðì±y¶ù³Ûz )>±ˆ‚ËvɆ]7¾±¬wkê\ʹBN7*=m´ßË›„Sýü1KUµ$u㦦<R3óisÝøÓÚÊáû­jtn(UJ“äy”ìö
+,>^SŠ¿]yÛ×MVש‹Vè‘Ý&ŽÛ¸BWHüD×Ù áÚ%lo!±žÚ?êŒ%t.ÞjÄðð3a‰I% ùiQx[3ãg˜—¤áIJ0ÔäSÍœ­-Î\nˆxŸW˜'>nǶ–:î½ÞÚ®’[1*¥·C&t¢g™¦Cñ–1Ä)ên~1ÜÎNïh ¿ÀÕÅ°íÑŒRœQ:›k/í‹FVÐnSÿ8„¬\yÉ?GâùÿK¿N„¢¿O!ï¿>ªý—{jËé$Û[·@N³Cp»V¨(²k,½v£cÒ$ƒª¬²ÅrŸq¾Þgà×yçÙÿþ 
+ü:Z‚¿¤—[ [¶JÓ¹@²ô…L›ÞÎì4G Á1”>í]\íäõŠÒ¡ÈÆú¹[¸vøFÊ›ðøYCÈ›rDk=õ¦pb.Ñ©×: ÚÇU³yÃpYí;ª%ÓÎoésWö«yzî¯**sïÅÄ°+Iq!§FÝOiƒÆèÞn]¡Mñت½ƒkUE$£ˆàé³PQ΢øã¡ÅƒÞ÷é2©>º%ƒÎù‘-,¶,oˆùšÓNªà,wŒ}ú²kx5‹_ˆÄ·io:¬QMxm8êÍ"Ù
+G—Òn´Ì7k›taùeDRZÈ)f"r+ˆ˜¤n€DÑAq5²p%–Ë0§›—à§b±f×ÂÃîYØ5ëݸJ³OÊÏÚ§¥‡åÃt?ß±øˆ4`éð=(G3CÓR[Ñ
+AJ¼Ê¦€ßãÛ@x8 ‘ÕrŸ5º¬š3)•USˈ3É ¤ï»KŠ_çõ¾Ÿã¹¤ï‚N·Úë èìË;½éƒ
+©ÁHBó+8 Õ~¿b=Jăô¬1ð"›Žx‘3ÖA’Þ×ÊWš×aÓÈ™žûuоO#öXåD­ÉØ: í×éG±-CïoãúQè&Õµ´b6¸û„ž=ï…å<Dz> Ä¥ ‚g¨Q[%AdWÊBRógÂsг·F®¾ª“ªjNו ŸÖ$ü-¯fV2#Åã9rf涊(ŒÝ¤úvcD6šw UDV7ȯ¥¤u‚ŒÌþ²*^X FŠ¸_nD倸Äv’;îÅ9ôÛ´IW™Ë°£üäs®Ÿ­ªä¾Å‡ÍHÊ6kxª 2G÷wï ÿªgb'þÝønB×;ß]^^T! ¯Â(®eL <~ RQd”Abk9yC!ÜIRÇf§¬Ïe_xCN¬A Ùñ"ù~Mú­ôŠùºŠ?H-d\OÍŸ@ k¥!•uÒЬBjèCdâUÞ8¢ìjË.aƒÇÉDVÂ@¼%­†cz6å~ÈSC>ÂƲfôÛ®Êúljú½/à²È §,Žü?*<ɦ0·ÿÏP™x%™
+qPI·k–{ZUêuŒD³‡¢pן᩻y#ÓÛwÞ–n´ u?–’aÅUô´ÒÚ9ø¬&ص›µðúéÅZå*¦›š»´Ðb
+†ßiÁY d­c{Z¢ß}ÇR4Oñ·ž ‘ý·]ÒË5-sô ™oá—®—ÂòJFÏyã¥!ÑÒkt’é¥S·0‘8™‡QùÔ6Àn_÷²:€ð­Ïã™ÔÚ6‰|U²‰Æó’(`öiénu»ìüEiùÅH²wþbÆy¹©É(©c\ªkâßĺò˜³ÖÇ^­¸rËÃkþd¶Æ¸#GÙônH‚9EÍN«¶]ú­>7RFeGKHÌåÂ.œ7³ Áºü¸¡¶‘‘Žè§·ô‰~ê7ÞAO¹³ ÔP—|©—,¿…Œ é4®àEß·m
+x¯yÐtëaéRÅ+2¼±[–@k3[†Uù“sþ2¾|©ÉîHPÖ =ñ0.ÿº%áÿ}Oi_ 똀?.×yv8Bã†#
+V³ØÎÇl¡çéÃUÐ?î(èTåµ¢÷;û…¹d~´š ˆÕ~,VIÞ:ÐgŽƒÒ*hœ:ŒkÅûAv÷AˆÑ½ ·­ùù}n­¼Ùjr¡Mö…!‘¨¢ ÜEd~¸¬ gû¥¦]ýçüjöÙE8xékZF=J÷S=ÆvE]½‹a¯öÎ
+Òú&åRc¿ÇçÈGål"0,‚ /
+rN‘j$žwµ{H“èåta%¥dŽ¬FX)ñˆ±ámÝÖ¢*Âì—y':jÃÃDíd¤-H7³æWânÝP0àãný°W¥xáR»:mQCƒ^S°hö§l–=ðů&ýcY3ÿdžRó=É#|ò2:¹á³¤Xô-%ø°·ÀëOy¥èE·ÒÎÕGƒAµÛã#ØŒæ6@'oˆyäèD@OJúäý¨Mv’R±O
+æo©ù¹³M™øß;RÉ?“3¨]kþŽaàÁ—r`¶ü5(Æî,ª(«>^Ï^TAÿÑrV@)Ƨ™¼ï–OþŸ¡2ûJûÀâøӼ͜>tÎÌIÛÓd’Æé´M&1™cVQbÜ%nDP‘}ßA(¢I Ê&²(ëYÂ"Êê*;*±§Étþ“áùÞs¾÷ûù~¿×.àj,ò¹.@»<î5ðº?j©q“TtêPX
+®ny›ÐFlböô“O"&ÞHr{ŘØZчŒbœG+€8×x]VÅ\ßÚûˆlN  4÷©TÕM×|ÊPÿHcHþÁ¾k˜ž_k]XZi’ËÞµ‡ÌªùÓm¥%mŸío2 gž¹ùJ@º^ „§
+4b vîn/J=VÙì®mI˜ó‹%§îyqÚ5Ë>ÞÒÛd`ô'l̾ˆUŒÛsÈd‘­9NÊ'¤¥\<FÌÄ„9XðC€IêP£*äí¢›‰¿Œ.«™Á>t1‡cVÎPPƒ{ßD>Ú·3†B6 ¹Úé(µ”Sç´«ÞÛt‚N‡ŠÚàÔO¿òÚ¥´]‡µ GOƒ³âó¨XÙ¦¾N;Hí¥0Ÿw‘Xò–£o·ó;3Kq;®õÙ•Ünµ½¦<À°ˆÞX[‚é6ôSr­™«VmÒôΪŸp[è­©q! š‰kFê|â¶+¾·}O¢šá;fä;ß<±1©%ØШ[ï•ö^, ÕìëÐø¦ëQsûÔlÔ­ŒùÝ™O*¯|äÑ7“*ØOI ìVʈ}vb!·FõÄ6-³ëFdÙò».9±-9€Ô‘H]%•v1à1äÈ%™Ë\Ò‚|Z•ýÞC3æÉ €Ü4hÁÅ*zXÈø&¥5eãUü<Òe€«ø9“§
+êl‡Çù[tž‡¦¥ºáú°´ãj@Ú}-®~ýï³]IÌȳ~î|—Eú#5£¾ÜJN̘¾ÄZï¯eûµ¸fðNå#ö[ˆ>þûO”õ „q³?ådw$ëÅÈ"PÜ›]Íø…¼#;c8¬Ç?éi#¯TžÜ^\Œ9DØ$ ;°ðE›pág’Ï=ôɲƒ8pá!C?H€Ä*ô‡ŒžÜRÍþ®èªÖY“Û`tk MY#¹=©&5&Ö&í*Q÷ÍœÎo
+F6kåãô䶽·ãu¹MbÇ™“Ô[Þ"t­Ãÿã˜m¹"†Þþ“ƒÛ}ç‹_È¿ôÒÚWÿLÊÛ®œ ¿d­“Íy'Yt‰¨)~`˜hÌ
+:Ô§øí?Ä•ˆGÑ•‘¦íÈ£²ÕX°Áž¦7;ýä'ÀŠ.JoÉÅ-oMqrŽiÁ‰ëp ”¡ÿ~ÖhÎû¹äŒW@Ž°Oµè¦‚ƒ2t¨­.ô]÷Kº®çLˆ†¼U_vzª3dÊ€ŸXH­Y ‘ÜÀµ–Ü4d%È@g¬¤ž“ ìó¢ ú¤ ¾D¹‚ªÆS`šVô‰Å9w"o' ¦ŒcÍXPÊ„åø¶S+¶9eD?Ihw*¡ä& –\èÎ<€h)oá;3fÌóœØþ[hêͧ]ál\7^w Ÿœf•[lÆ0 ÊРœÝ”TÞ;ÐN<>1Szsvú@ÑŽ{ù9@G]zèCgÀdKô]GÍ¡¢÷Nl¹§&¡è¹ùG’#º 3™)WcZ‹NêhÑí©1¯.üèÁcëxGÖ‰ï*ûhðËðÿ).ó¯¤×<ŽŸùa:÷´[×›Õ­[NÞLMMMwsCY”EVÙDp´¤,ÍÌA@QqaSÁtDdSqÉqî=Íü#óà9ÏyžÏûõzÚ„Þif‘ežº:X ,0p§+Bá™™MÚŸÄfz&ÑÉ%Ã¥2Ž-¢žÛ:e!ë;‘o¥­Å·ØZï×ÖÃM\B@ÏÅúuM´½YÚoéø´~èÙÕ4UoIkw$è8§ï’Õ$fërþp?úTx°g¢º2Pû¼“øòêT,þpJˆõOòJ‚3ÜòÍ!BìÖ0æéŽãQRvœ|sáùú7râºz®{Ïþt±w¦büðúöb{Ö5ó‡¢{½Ð¯i%ºT”4Ç(2Æ!…?;˜!BCóôü]>}s¸¼>€rÂ'„ô|\ÈÐDtŒc“mÃvIéc
+òkê ´lX`–šãRAþ™ÀáZÉ{JÔ6Bxøâ#ã§âGæØCë`MÔÊ
+ø¸€–²ËP©›CU‘–þªßÖÏÖù ‡
+÷Ò9|ᚬI éY°]üb™ƒý±ÑÔ20›bx”{ŠR|¶ò^ä×4Â=JjæÞ(.É=†NØW‘ÒOŒÍÄ#m#f{¸t´@Ï2+Ý%f§©y[ .E Ô.F$xÇ1à=µ+C­ð'ÇFaó¡¡™r´H/ è Y.5l“ _øféù‡‹ •6bG†M<YcáîqÏ-o[z>Ù® ¤­«ˆòNáÒƒ‹‚·Çkm‚ï>íÔÄÂœZ;ºÖ¿j¾ot«þkv|_¢–;”hÐöhu¼[Mãš[ì* 3ÔÔ,°jŽØÈC-»òè$› ÝWc“–¿–F,uÃl‰IIûJ~Ùž‚‘½5€óO4Áœc¬,׫ ëTPR×úpѺÄ­!jêñ¼êPÒ‚fÁÁB}‰GI+<×RKÎõXȉŽüÚ®¤¥o ábO´|ìÑæ”{:‚Š2¼ÏS0ÿ:ßšq54ÛPâ×sZ16YÂ+¾u<ËA^,7 ÎMÌŠC1Ó9†ˆ±tÿbé,ùÙ>B}¹.æd¯}Å?õÈv 9mWNÌrŽ‘!N)ÙÚ‹|¼ÖSà–7X¿‘Súq¿$Á¿¾ºÐRvõsÙcÇâY`†šëš¦eM‰*žˆ©Ùa§†wü]9í•ésÉÓ§ì:Aö¥ö¢»Îtâñ-Ç
+x„’“uy±³4òÔ܈_DDËXÙWÔüüës¼ëbJêå!Ôsÿ4³Ä0Õ©¨ËpÈéoܲWÍ(UòÀ×U¼ø0c;èæfoѽ ŽQàÕp«6…öAbÖuïtµ‰æÓ’²œ*€·Ò7±[C×z\Ê·D›˜öj³öÐ>{äEEßåÞ˜âBÃ
+95Çf&*h` tôòóUòÔÈ(ÛWÆYz+…›™gf+dü¿³°KƒsÌüÍþÒ‡A-³Ø7Ï*öN3^ï«pÉ{Šê8cgöí)vÆe‡„U¸ÖG~aþTý›ñâ¾î="bµeÓÁ;&Ä>ÂH7´—ý¼ÑÙ£¦í+ëÒl#UOÌ]9·L¹·¶%ÈßÓÔWg Br@Í«Øã½*vžWMËô(q ûJø³à4:Ý.EÆ͵å‡Í
+*îû4<äæ6~{¤6Á!«Mòª‰/£èØí~L´m„–.å¾  g^ùg?ìSóÊ÷U ù6 !I×V|{ºrÅ>€Š´ Ò^Hø•¿²Ÿþeš :žiBøTµ©+]áÚöŒ3M™7 U=“͈³¥wÍû“ ^5« 0Ù‚ô)8¹§^¥KFLiɸ6Æ]Z…s9”$ˆ^”æ'§}]à’×ÄßåÜÙè-‰uÊQÉ›ðÈõ¯¨¤S}w[ÙÝnE$°õîå©–Vt4GÈpŒ#ãW{ÿGq¹ÿ£Ý÷qüñ¸îܛʹa§Ú6fÉ¡œ'Í©ÒRRJ
+‰9ÄœO™SFD¤ÈaE!J,9'äl®]÷îÇ®ûúKîïþ€Ïç‡Ïçý~½žO‚gO^¤S3Üù§¡¦ê»^X`›¤‡NÖÁ\‡ Þ8m÷¡ÜŽUô«ª ŽŸòòÚt}²ÿåB1Å6ÎDÈùÑŽÜ8O‡¢XÏ›2òÖŽ„t
+k¤!(Ñ`“˜ö `÷K]nÒ® ç½Ò㪮 uàøßškŒs±*‹•ÂÔGÚfê›+m½ðRSB>RR‚ÖÅq®rž¿Ã”å4^mÉèÈ£©Üø…ŒÇå/ãrà¿alÈÿ=1Ø>ž±Ø†vĸžÎ¦ZdÔPSGÒ«Œê¿+eí eþ²â¶Çrb¿°QN5¸ÀkÀ.'اrñæ¾^æ^’×ðχÊ,„}2Èï<ŒMÅ@ÌÖ¢.w%ûÿ³!ü0ñh@?¿5wQ< ­ø纺¤§.—r.>ÓuTî_¬(ùï·ÊZû%|w(bL‡mU™Ñgš¬¨ÿYÊøêvRL)ðîRõŸÏ³—ƺûT!vSŠƒh*Ãï¶g¼º3Sö¾4”äUßÜYê$…]jËhßµò©Šòîl–‹ú{ò•»ÑÏ5!n‹™^HXwV¥¹mÜÇñr¢ÇÖPQŒEʆ¯~¡ùhêðu5ÆUS…q^n&¿ØáFÍ4§{éêRÜìv¥‘V);ôt’Ì&Ü6B
+ô|¦’£©ûž®ˆ
+¶‘áV†óü×Õ|1åxº(ÕØ,~¤Æ9Ï“@³ÂxЙ†+'8‰ c¼÷ÕÅdëXqòleÂC%`üRÄ]]}´£Zw°¦…ÿZäÛ&òcOgT³˜ìkîÄûZ:Óüwúȱ@æZS<V»“ÜþÙ.-9Õqˆ&qšûŠuµØ,ºÒ76ü2W}ºXÈK
+à­Ë@ºOv`¶7ûrƒô"4D]êlhIE8Óg’û†,©kIó^“d‡¬Kh¾+"¬ëj3þén/Ýï@‘¹Ð(xa«m”@ûX^×€-¶ž–%?n¢"r‘n¦J€e$?BZ‘ôH.H~²5̉›¥ûŠè!×;³`׆8È›
+nÄ͹Œ«Q\Ž®ÉŠ¹×Hq:çg^
+<ô®Œ >Ws3§ p'*fÄñ±? þ*a¾Ñ4ã·±>€‡
+Q€+
+~¥)Ÿ¨Š>šÅ´ñ’÷Žûc »Šb’^DõÕ7ÀÆV’§¶6åéJÞëà¡ã©ü”ŦT0ðÖ/ÎÔÅ$ë;Ò$ƒ Í@Ææ8§ÙêÛKÍñ6·ÙV°cêÈ¡·Z(ÁÎ[ƒÙѺ¢T çc[q¯45q/L]€§ôÐaGSÙ»Š¢Äá’÷%…¨Gó­øš˜´-e@—»)PuÑOÄsìÌC<9Ÿ+¥­1‚•ehŒ¼g¥zÛ¤™¶ÁÌ
+"µ5øg35};!l­=ƒ~.æ“}°7åìpûWÈć_(Ðãya7—ëŸèë_š’ž* ïþ^ç³,#ôHEU¢Û¶’¿7U[`(ϼ\ä‘Ot¬¨=%Õ‚8_,$ý´Ô´œèŠ»cYˆÓY.áï5aÙ#7c2#h‚ß&x¯õ¦ºŸiy™WúÊ£)n"Б˜3mi¦m¢u¢¤_èªòÏÕ‚4€­Q[ƒYÐU1ÙÓÒŸýÆ*ãDNWÑ7|ÔÖh.r¥;ÓwIDrß–å„Ø'KpÖ±<¤©“
+|’bA‡Tƒo÷W‚ïé{ñ ÉºœÇÛ£˜XÛ 1Ã:Y‘q¡¡#ot|âÅ’€`—’W{òý-Ó¤xó8!ñ‹t2=MÞ »'æ}·†Ï=*J¦CÎÈZíÆÑN9 f/ Ýî>Øì„?Ü@>¶Ï^­
+³Ø«¤€×z`Ó̸IùY5íEA˘He{ùstZðz­%çîöÃ6?þßÛômù÷·‡JŸ/2‹' ŸæüþƃFÜvÉ8¸óÉ.BÇìŽÀ‚ ÝP¿Í¾ÒÀ½qbôîLuB#Õ¯ó›kž
+9UÔ/P3Ž$Ødó82Ö)~¦©L9[¡ÁVq‰€„\óŒœKmUÖ…†”³ÎÄÝlÖUÙÄŒÌåt¨˜“çs$ãVÊ™YVIu’i¬4ËM}<É)ª ×6óÚX×bÒ§ùà{6þó ±õ‡…϶˰o5m`ÿõÞÜP‡p±JÎÙÇÆãÀá¿Ö—Å=û¶×&6ÍÇïzüL‹OÙG<5|.
+Û)羌tŠÙÙ^5u¼ÄÇX&)q.9âšÃÆ](Éi®ybÒÖ,Ðô¹ð©iâU×–Ø¥5°Ý"È6Y
+Ì“¸(›œ‡Ý™áWïŒÒÀ«±áâÚü{²Z¨Ÿ¡óâDÓ@
+T7çì ¢"
+­*jä6Å×ùÄC£ÆodÖžlñS+ñħ[Úà˜À¿°Óò ß(yûJòÌ*luM¡òlUW£æ|x½½=jízï_2ìJAC`ñ§»4F5Ô
+`÷*­Ä ç4þÉæ—šäC&Ë9‰}lè)ù×úŸ¿ºÆ›Å bRÈ(&y[+‚j!joœøÈ''æzfͽ%I+¥ÿ\‘T
+bkŒÞ¦‚ƒš÷¡•= “´í‡½½=n¤×x§;†©^óU!ÂHÒ¦…µ×C*1Î'gUúeԢЭÄ+'ºg‰¹‹Ô‚cSÛ:Á€X>cŸl
+Vû0…ŽaT቞XýŸ:±NG˜¾¼½aýÖŽéºÄ‡*1ùH×RwºÅÁ4Äb÷ <+²D¨:µˆ®æ›ZÑÅ€ªõ—½§/fR£:NÝÞ4.o¡íé…øI²wšñ?§Hø}ƒO=R’ N,müc³€·ˆiGZ~ƒ{‰]î˜kγ#þíšÂƒ÷ä-e–~R¦K&‚,´Ã˦ˆ†^é–rm#M€ìdÆ \tp¥­Ùô“¾úgýÍÈ2­ð4ÐÑ,üc½6Rë¼J<Ø=KÈ é¹H'Э•.ØU}$9¢¡
+0ýö89ϷĪú±#i;]ç‘âzr¹_Ì[ûZyÇð©<yýký­»’Ÿ†…Ô¼o¸c¦€ã& ÿØÈgÇŒ\rh‰Râ•!s‹ͅ!%£4dêÏu×ÞV½ƒ‚Â+BBÔ "ë9ð“5ålSÌó(%Ûc„¬¨YÚ[ûÐPñ€ͪ>R²«ß}“0´"þÞlÅGµ­õ›C˜‡›ÃéQ#Ÿ¨–Þø†~ta³vÇ9‰Ê¨Éåñ5=±ÙÝí²²1Êzñ³ïÑU9¸B+ó)°@/ÒÜs´RŸVŒ?ÒI¹{³‚:¹þ›¾žë˜ëlÜŸá–œ
+ÛhMŠg÷Ô5ÍtORž;Æé%[#DÐr×ë_"ºVLb­kmʲ}©ºdé…\ñʱYAeó³c-£*¢å¡¼J.Ô5Ï­<PwSýZ!Ú»€íŽ5¤¨:J“–¥%¿;&°ù!5¾Ù!ò«ÈÅ Kþf‰^
+½¢ïzua~ß#Ççl Âï˜oOÖŒ°–hÛ‰1³øÿ—é[Òé†?™k末9sjššiª¹N›•5VJ‹»“á‚š²
+(›ì²‹
+.ظd©e¸€.)«¢€ @ ²Éª"Y:×Yþóë|~ßïú<÷Mv¾ƒÝZé*9½"®8kè|zzs|1¹ŒèÛɶQHÛ]yXßø¥$(ÇdîªpÙ;
+äo;rܽÝÚ³Ð{B7„ø›÷íî<µôø?ñ%ÂS`»jb¹s¼æªk¼ü‚•ùÉ&âÇ
+›e­ºa,½ºýw`⟎Ξ½U6*ªaVT¬Š]ÀY<è¶!è} ûÔ?Ëmð. õº—†z½ób‚[Î(²¿Æ¤­õ–Ÿ™cåÿu™_ôCr™I­IDe7Á*–mȸÅΉÖBï4£À;I~¢íŸÃÜÿ‹ž[~><¼§¡Wºä'®ifYx¹ ti¶QZúã
+>ï|»ð!À†ð˶—¨Œ= ö…ÉöôTˆg²þ¡u øâÖëçÿ ¹j—±K§§–„ÏÎÇTŒŠè 1;(CÜ+Ðù~9.÷}{ÅÙÕ~Ø¥
+´£¨¿šEÞ )ñEî)B®q
+}/öN@ooWýj¬ø%¨fT¬ŠÁV½FòüGŸ‚”×0jã|ÑŸ6:ú˹
+6Fà÷vÕäŠÃU'a’tÙg˜M99'±*b&L¬k
+ýÀ§Æå‰à¸Õp¸Î‚Þµ§6ÄÝ.€I‚‹øß_þ­zOC…D—ðù‘ERa\Ǩ‰®t‰ƒº—£–·ôëæà¤ÿú¥ªOŽ6ÉSG[Bþž™‰Û·²[RnÊÒÓ¯ј¡–]k)õÿöµ÷vµuÚ9ì°ž
+ÄÀ ñU6N?XyÝ …üâ›Fƒü
+tnô=©2µ.d¦¬".À¥úÐËáÅÖÿ,©t½¿ür@¿²3¡áÀ÷<|Ê*î9°I¤îy>Ô©6¸UBØÚ0ò¦[†~^¤BãÆVTpüûÁ—tâ÷%×9¸„‘Õ¼_AXGCl«ŸF4„¢ˆ–V5òpN9¡ a¤ÖÙù¬„…H9¸k\pWȧ~©a{†µ½i¼ë!ošðÿx{§?mrZ’f&âÄÅçÛYÔ¤°6­10G*]jøusû(±Æ!ìYÙÄØ
+­fÏBÇD-Ф]ØñÑ!'-JÜÀ@º¸‡  ubj%üËÙ3’4wwGô]ìÀ<Pb2l£ÕÇ©÷¾o¹öÍ’0ó[÷;èíCS=4Ï*÷(¨y;JJá®ó :ƒº”CÓ7_ž7JŸ:\瓃˔­wìBç;Úÿ(.Ó§&³,îššêé{œ±­±UDmÁ¦UE¦š„,„섬o€„[…¡U0ì„,d% „%‘-dÏ›•`@´zzþ¹~z¿¼U·î¹çwÎóä;Çš3br̽?× 8ytç"öŽnèñ¶Ñæ[Aµ„â^£¿"ܳÄ,­´ä[ûp}zXýTV?÷-Šp®IÒ½ýIJ¶_Ío Ä´½Néò@ÖEg|2bfTÕ^–0qÃzzy@A~P‹ë¬<ç[òíÝ7iž9|VÔÀnÔň[zy^uGÕÁö¬¥”DÍ”€º½|{üÉåí·¥—‚ªÆÛþ%ö“ˆé×—‡+Ã3!m7Ù§l½ìàQ“ëü6¿†‰Øi½ g;d´»ŽIFN`‰‡Š,CDï"£(n„ÈA%£Ä¿€ÏÙ»b}^xÙ/o¸Õ3ËâÆ^lè}m¨½¹/c ÃJ&*e¦×'MÌZàMˆõQô®yv]HÃA­ ƒ;J«¿[’4¦éúk3\`'u\ll™ÝäœnË6¿Â\wȨ ßúúb+Ô#;D›©e¥4µ!äÚéyv²Ù#Ýœ¤èÒC:!+ X1jÚ¼ò¶<•¸èÜ’$ïkïb˽ã­.Pj¼ãÑ7 Ýå—
+÷ &Ç«Æåy fWÓ…Ù˜á!ä]¨owÆ wS6þÏíÎΘž†8²0ê’&jõö[Tú¡]ÈŠmö nLS­ŒÕ¦%V8Dàp‚Ó]éè‰S2˜°°
+°íšÂçîL ®ûI…„}?N¸á‘Óò½`ÛG0™ÞéÖüÓˆzbëæõ5Î Òm×<#Ï!k¾b{Y~–·å†”‡‡:2eé¦%ÁÃ
+&"¢bUØ»Ø~uë=¿ð„Ž”5Ñ+`y´Ê%ŽYæcASÌÚ'Z¥’¨¡»-aæãÜ
+Ì-¿—í‘ãs#rej]@ hYUq»þt]
+.6£'Ö|öÒÚrVÚƒ™þ„€3^„»c”Îô]ØŸ‡Zsv>.c ÝÉH5 gÖ?²nEuPcÚ¤`•¼£ÚÝyöýí)RjS&ü ¶à’‹Š«Úçq«Š°¬ glZÅþ2B;ºx¶µµçå×m)|€9¨Ñ¶3Û_] ×n¨5Å5/kçwç¬ÂþŒy¨5cœ#â?"ÃøÇ».n½ÃWì-q‰ÿ"ßþó$¿î«Œ‰Úœ³1±è
+sèöŸ¬ÊùE—”™²ð°y׈8mQ vgÈ׃Ô$ÌôI˜QÚP¨OCÚ_NÊgQ£5å„©Å •6n’S&¸@ª
+. Vǧ—<juymô9Е4ïUˆ3ž—yÿèÇDš·1;P ëÑžž‹‰ë™ eŸqRýh~‰ûµAýga¹æ8x+ˆÈsëR~Ô"Øœ…â&nÇo»Ú‰³°f<çI9·Œù9ˆÈNwcŸƒr¤¼. ˜éõ; ŒÆ-BêáúÓ×¥U
+ô $efw¬°Û3NþÐ/¢eÜ2†sœqoq´ëû¤uˆvñ3|5jf4•\"|Ù-¦”‚'GÀ{g«jõñºZ]ô*ø ÷<| \ÙãÔ%Œ5i„ëSeÊþT]VÏÙgë—Î á©®oÒ&fSÂÄiN™øíG>9ÿtSûÓɦZ[ôJ¨«°h
+Ôr°ÂjØþ8x5ªcÞMùíà\kð¡"2M®J93¸7:Ç®MRonMà/£+Ü–ÃUµ
+u)…{+<Lt}?i‘RÀÀg!¯y}‚\¹5E¿z p=r>«¸IÐZ%ø¤ –˜‘û0a‚Ú3Œ›1¿­è•s×`ÚÖÏþýÔs)¶H¼š!U¢6&úTpîô½c]+€wQ“
+ ^Œ³®ÛÁ¦£ŠzG¢
+$Ôu¯*Ŭwÿ=·óõܹó¾ç÷{Å(bU@k/é7#J~ËÉúèä¡æDÕ=?Z¿ -á®ì¼kÿ>ª!ÖäA× ™±ð9»”}X)¡'Ý‹¨‰%;ÓÝEŽGuñ¿ÂE” qƒ”îxìStÿøù4§¦(¡ã÷¤õŒú”šZŠ
+.ZKpwùpÖñ¶|$½Æ늛8­ŸæÖ ;‚:qWÞ 3Ò¦Úú«–;žÍßÖ¥pn]4ðé nV{PM¸ÔÑj·€œdºà’ÂyfeÜ(¤œîŒÏü"jãuUôòÝEfé–]—^ãS>†ÄŠÓ] ¬í«ËX àdÄ’´‰Rþû{~d’ N)ý´+:Fmp‘VŽ˜xmQ=¹b w 51êÓv!!¶*ø û+ÄÏTÓ™w—r&æÈ3€=ñ1ñ9'­ítfz`pLrJ/êŠë8˜’YÕÁQ-Ô¾ßU°‰É¿û‰ÂnLu…—‰åi:…DÄö`8¢ûõ_Aõp"À|:)!4ϯÞ}K½aŒ¹àŸÂ^HhùM€K@×A-{j¸Õþœ|Õ2‰; îú6½ÊnˆkÙm»‹ôû!À7áU¸a׎
+ÂT²^ÿ¬œ¼5'íoì*˜'×Et|\Ì,£¦×†û¨mcžVP±k«‚ö«
+5²ê³V àžÎ¤Ž×²–Š˜å¬
+jï³;Kù)ª¨Šh?£f¨û`…S˜££QwÖ,êîQ¼ñªåÂÎküÕ˜º÷:b$ݹٗu É GZ³. #ç’€C•Æ4Å9+—Ô °û³´;G61ëÃö#eÒs¶ç×Ô´º”YL˹F† >ÅXl•Ýf¯=g«: ÞfgR/e¬¿íýno‘Q™ÐËh•
+X “Ó¨ü?6måwDõÄÒ°ªëÖ‰›ÛSpñ:£VùWŸm ­0jcjVý/DJ[H1]GQDƒ¿v:ìtCÌ˃,ARrT˨ )‰Å „ k@(™5“°äj_\ËÇxŸã‹Ie;»íÐËëElp^÷Áª¤7fd†õƒÔÐT·û†^¢©új¶íJRK«/3«#zQç®NFØX”uExDu‡W¨÷|S—6gz¿wõ%¬²= §vO5GW‚¹¿Zˆ&FRÖ!np¹¿Äùï¦3Q«!c“ |2qP/Àîh­1›Bšr ‰bF¨#¸D¾½ý{xÆ•”ƃƒRÀ1BJ~cÊ gF•¼Æ´ÛœÐÒªöf%»3ÄëYÊÍÔ*ŒÏ˜äŒÐ<³Âÿwiû-þ*b V¢FJERO¬ «»Jƒ+ôŠ˜iD”²•”2ÿtã×q5³:®æ7Gi¥Ý0ëtkìuÂ!áì©8µ)Ç°,éŸH¹Æ'SÎ!QHM¯t=Å|ç›n»—6‹˜¡y¿ëµ(0'hˆj䤨†Z±P01 ¹Â÷^%®ùóûΞ§¸{KìJíx〃«3¯w–PWQ«RfzcÂÂÄDܦ¤iHˆº†‡÷•ìºýezYa]>”÷Ê¥i‡˜‹X8DÔÄlN9M;LÎû'
+¾ÑÇ'>Åø‰W*9öHø¨QDÊXÅÔ,˜kÒ@­ßú )næàr^1o_…ÿ)e"Õ&-ÜnÄ.b¤ƒ2ÄõëRÆ;ñYrRNfó¡wx,©ã6ù_´~£‚J>³ŽÜÿÒ2Vyyã5¦ìØÏaÿ78ü22+nöo<C\"FH5ðËöûÞ®Mç·æºÊz:ph[Î õ­(*ÿêŸÁß;Z‡\b¶µÈ4xÿ«ÇøëŸÉš¯þ°ïÖ94dyÂÌÆ `ZyÆ"èÉØ!B`‰r;¤¤Ü¬0Ë^ãᚘõ?Ì>]sN6±ÊªKhM9+ÜW°IéȪ
+:f µŽŠbõ`Ê"ëÍ9¤td6²ÊoMÛø9‡¨§
+_õ¼©G,‚.Ä"&—3åØq@ÌßÓQîy¦ÛÊŠkE#“yX1™qHf”qŒÈ²ÎaåO®ümgøðYš´
+ºƒryÖ#d v¨ °*]xTò
+úŽ}BÎ"el¬GÛ³„£:ZõIP9‰Z ¶À‡Ž ÁlÙî<¹
+ü#ìÏáÂúÄTzM=ZJn IwÆÞ½²7ß.Ís›¾ƒ')—sÀ«Ô•±óØÙõÁÞÝ¥¾q Ÿ² ˆÑeJÅæë–ïS?gÚ
+ëÌ9½> úô
+Í =þ‡u3®%]É­Sª³Î~L–q² {Jªa°Ôi—œ6ˆ{¶>rî‡æ-åzDG®Š­ÒbVfãA@Â?ð>͹e¼Œ‹OÊ{dÜ”MÚ—0ñ;âzNsD˨IXh ˆuP¿*Ÿ-‡L\\Âá²N5ïP"‹Ô÷ üUûTÓ?v—èÄ293©%î_éõ¾¤['Ûco“ =e`—8{±€íðYXÊÎ{&^=#¨¥ÿaDO¾¹§!]‹è)•I+¥ò`“ýøè3Ÿ5“o†uäë€åù‡Á±×9ÀVÅuN[ fáP3»ùÐǧ}– |}‡%ŒÝq;à™ö¬™‡G5¤
+×díiƒ²ê[ÿ î⾑ 8Wصý‰Õ`ŸÄŸ Î+":Jó‘[Î/¹¡Ôd™\ž³ ¥ÿS\&\M¥gÿsÚsz<gæœÎ™Û±ÑŠƒQPAvÙIB²Þäæ&$@—v´ÚCGQq’°„ì;Ù BXB²Üì±ïÑ—oðÞû<ÏÿÿûÙåüºB·BCž9jµógòU×L˜gÝç-!«¼!¤–|0µ q¥õ'Ïræq^ÆÈë +1•[o.ìÏ‘«V3j¤7•œ\Á-fdbJn]2šÌ—41["k¤»µ-¿Î7)ÏÖO™%Ô£Uà·HWöç±w?`/‚½lî2€ÚjÚ&&%-&¼Bió¼ \±w pñ2¿#¼¤ô“¼°’Õìy…ù“~¢í*NõáEÜÍS¡å ÅÓ¤}úq@÷¬ÁI‹Œ><‚ÙW⮆”ƒWöWHõ¨ëÉ‹´kúQ3LMÛ¥ì°FJ>TCP“„š2òz—UG«ä1=½) n/mô'tô†˜–ÞúD®GœÞŒyäǸ_^u,#}!•¸kçýÈž—ø?/Kš· 7žÛšé»”·³]1nmÝ_¡×ƒùaS¦‘–¨#0‡­Øû8|#½ÆïÊßÉئ¦b&)mçæÀûášàávFÇ|5³;ã€[B*µÃeÚí”A„?ø$ì¶>#œ¬Ð×Ñ‹ÐHÉ1FÊý˜ŽÙÚø¤C2’r>}¤g{gGnn/«”¸ë¨Õ[ðò©Ÿ™vð!©»jrmÆ=GÍB\H5|=¼D©MjYí97ÿ9(FN·¤“q›»gd·§œR¸Ã)p·„­÷ø«ë¯z¯æ0õi#à&³L²½Hk´Ï ^ÜRÒZÊ;?é’V™ ¸@»œ¨ˆ. TgjͺĬ“ǯsn TªVé- +Ìͺ„´ˆ–XÑo¥
+>fgÙÇ'ÝÈhÉ95Ò úRzN;ªfuÒáäêèÃí·¾·(ª~øWëwI«-ïFX%ï¤"kwÕ¸*ïÛ_Æ–û/g-Œû%'„/º$Œ¤ÆÆõÒḓcðžcÍðÍ°ìä2³!¢á´ÄPÖ9.̯˅Y;B9Ös;£Öýœ¡çclûKÌ·¶§ÔÊ·Hg| ¼:Ú¹÷©ërX©‰hdžV‘AÔ¢@Pë”|W u
+¸=¬"]¬0cZñà‰ÿÉÊ~Å?ó`RN—uAøÜ™ ™`BÁ,$'µìöƒEàu«£mŸý² ÔÁÇ}0³´)b$-‚„ÆäÈHÊÈlÙïÿKÆDoÿÕ3!ËYäüÐ"¹fÿLÅÎ<®2¸Èªßý$l‰é&˜Ûja‡ÿ#£&²Â~p¤fÞ?õÀ¬ÿ¤È‰OÌ;y[㵧
+QÞöxlû³F¯üZ?<Xò‡¼}R\ôLŒgÖ¥ìÐ*£Ñ;‹»–¶Šð)àz!±zg{)øq¨r_Iº•w*žd<¯uèº Žé8mg9’·ˆ ¶jo¡õëÍ™¦sÀ‡êbz^'ê‚]™þwÊ.Dt춸‘Ó™qHyåÀÄ“r@ñ2ã„Y`7o®°šPÛ uˆi¾wøÊ´SÌøoPöø³_ò’‡ûÍ?6v² 1ϼso‰R³«f6l¼'¼à<ÌšEÔS÷ÿÙ/Û§¦®4€[¿lt§³j;íÜiË;¼äÕ$PWÁ
+vÿéÏÁÞÚÓÓ^Þ0Ò=t!o"ö?öºØP_õ)‡Íäï´™–îº?›ïæŒSÍ%û}ÿ8ÕR­’tJùݨaB݆ïAþ'Û©ÌÅ;»|¯²&Øã²Ú…â@;¿¡ î2+ÛÑy:ù­ —+ž¸Æ|ú˜÷ïÕå¼ûà®uÆC]n~©»’}ØRš~ïªI=Ýæ0?ît”¼Ö¼éVêÈ`}AôP]^Ôbåh¨‡÷*ûÉý«tê8¨×¦[„«­ð±œÝÞ²£3m4¨û+ô‹ÝU¶Åž*G
+®˜Ïr°WäàE"ÁWgT _ÆSnU¹¾ï¯rA7}Ó »õ÷´7oÔîÛ5ë)+^ùôü|¿ƒŸh¡2'É# vú|yÄý×P/_ì` A>ŽôÁcÆ®39  .ê?£÷Ÿœì7ÞSÉoŒÔçJŸ1Ä"„âí^Àÿ
+$H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6l Ø@"°D`‰À $H6lGX„ðÛ¨¨Ú¤Ãy\rü$*)]UþÎÆ9ž`%’¨¢ø$–בFždhœub à’4N®Äâ³ œÂ¢W#1„e²¤™¤ÁE½§ˆ=X Mß)&ä+_Ç’XµZ©ŽSÈ•2F~É1µF­ˆS©¤
+•Bª’ÊXy8H¥‰Shä{å¥RªÙû|Ò€kJ­V£Q+÷îU‚ åÊ,­V¦ÖJÕêÕ çÞNTÐsog‘äKhÉ1‰‹ÞƒÏ—ä®{ò¦ ç-™8K΃”Ð?%³(ƒ¡³X’æIÚ»z9™
+@Û ÆÊ#¥Az“ˆm‘VË‚$ÊjÁ#¥(ÝIp¼IGØH<¼ Õ9ÿ`
+H‰ÔWénܺ~½ƒú#@Ôs¹“2Š ˆZnf12NÐ -ŠGqÔÈÒ@£qš>}?R³Èã±=Nƒ‹[x,ñ,$ÏòñٟΧ'ñ¼ýTžð ƒgÏ’®,ú¶; ýhxV׫eß¹¡ço_„TO„â33[ ¾/»eÕ6§!#ꙹÓ~ž7Õ<Ì»¢ù‡¢²x>ÞEÕ×%¸ïšÞ½ÍOØ‹Íœ0’=˜œþBÉ/ŒP…éN™pEsS,—ÕÀ¦ŠŽ1Û®šyÕ\ÙöߧáIž(F¡ xû×êm¹¼O„M„fL %8“^<m/W×eÓŸwíe¹\&mÝvËÓ0ù^4á«â
+œ"üPÖuû-´uqù5€ ä,¯ê»½.úr·÷øŒ²™]UõüõêúS ?P-Ü0Ÿy“ï–°³îÝ ëÙÙ5F¦eßc™˜Ðù/yõáoãuÀÈšž|[^Uþ8à­¾X[îÚÅuÑ}uêØ"e¡ŽXHæEy½¨áV爫 O8qÿÆkYlÇËÝ5³óOyS•ßNÃ×mSNˆ»~:Œ„ ¿çíª.»wMÕ‹Ï¢Á ¯ÚyY#`¶úy]øÍ{¢»ßAà¢è®ÊÇÙÖ«Þ‡šÙÌ
+vÌÈà6„I-lΚ͒W£Ì!“WSì›NÚk«KW$\4 ™êöjàmß=ê«Åà‘!nÖç]Õ8›ÁkÏ1³ózÖo]»Zœ5ŸÛàùP_VM¼Ð3QâüXÑõßÚî+>Ïæe±«“¢Z¼xÐäôkÙ_~Ù7ºýq³ïËKjäè<|óé_øp6\Ý /ºâ[Å÷Væqs8†n³shúÏÍÿǵÓò3–½SF³æ¦¬ÛÅÈì ÷¸½óºhŠnë¬ÍÙÜ€Sà w`Ùÿý‹ÈˆÜámÜ>ŠGF¬#&*ú/h'e3_nmŸ»U» cÛKŠº®®ºbñ¥º m·Z~ /Ú¶ÞÚ>ÀßÎ3æy–Ó<"b/]Jv‡f»ÍÚN´þƒÍqîš7Íp&wgZ ìÏ„æ5èüAçÚêšÌÿ—9¦ß¯?µuµ¼Þýhäµ±º¬Ëé÷e_S3ÖÎæŠÞ=Iþ Ìô[šü²úô`ZºM~®š9"tºªúr—‰íõÂaÓpú¥X”~ÉéÖ ô |Ô‘NNÚfÔ¯~ëŠy…VçuS\£Â_­‡B ëCƒhÜ<´óàcð— V±ŒEÌcÓ˜Dy”Ei”D6ŠÑ{M¤A*’‘ˆ8:7ˆÉMfR“kbc´QFa¸a†è\g:Õ‰¶:†¶ÑZ+@¡¹fšj¢r•©T%ʪXEÊ( d#ĹbŠ*"s™ÉT&ÒÊXFÒRK%¥ÀDLRID.2‘ŠDX‹H¡â¥‚ &¨ <çOyÂ-y„‹‚æŠKÀ*Έœ°œe,e ³,f3L3à,&Àg€X„æ4£)M¨¥1¨¡š…PA@£”œd$% ±> ˆ!ØÁÒlðƒ_kM¬uïœ`šŸêÜ@šŸêÜß˵DÃg3ÛÁËIœD‰It¢™ˆ„',¡ÐÎmfS›XkcYcµUVZa¹e–ÂvgqÛ8Ž£áœü‘%IbÐ`§;ƒÞØ=Û}pÃØïá`Âç†øb{DïÐö±µë>dtqP¡Ä .oàCÏòì2ÀED2œ·î$„JïmI¹3r”°·Š‹&âÕ âº »è e&"^g²VÞ*Ž&ù]ÌiÓm…ÝÔÐ#Ëjt¨¬F®¬Š¡¬Ò€ >¾ûòƒ^ßÇfïSë»_ƒÖ¸UGøoÿÅîo[9Þš+Ü|¾Ã¯£LÃâ\ñ'öH„Û!<18_ᯢ.¼9õ÷Ò;~}ḊàVä`ä>%ŒÞtEsUþ9üPÖuû [Ý€9¹éÊHÿ#u˜‚Ñ««&¨b ªYfó„ º1T9j§Põ ª_Œ:˜&Y’§$¥)Ky*R™ªT§&Ò8µi’¦i–æÉhÆ2d"“™Êt6”>HOâq
+F¯¿Ž«ÿb¸#ñt
+î Ýò¯ßú±D_ÖèÏú´E¿NÑ·óˆ ‡3ts‰®®ÑÝ#ty‹nŸ¢ëç1`@ˆ@¡ã4Š # Ð9²8·}„¡Ÿô…þbÐgbk‡ê'‘zŒ‚ƒ£3Ùsò~ùißàCgø y×
+ Ë
+„@‰æbRr,fÌD+12xŒôÚ.s"’å$QÀÆ’JMŒäÑØð1âƒe¢œ(P½…üÌ„ OL>,·‰7ãšÍÌR¼Ã|
+Jš®eNÛÏ}hëâòkø¾ºjʾ/˜îça.þ_ö«e×q܈~ÿÁ›
+‘ƒÿTÖ¢/Ð{葉¦ 5‰@x…i©ìˆw+˜E.SC3]APP^BïõhˆTPäÇ:kØÄ äÊ–×x~Ë3’,AØôpaáê=¶¥Å¥„Ó!5@ú+Ä ùOúÔ¼JFfIÈ.Æà\²Ðéù¦Ç^¶¸yƒÜS#ÿ¸Lß³Pó©Ç.·L ‚S
+‡§Sɽ¿3×ÛœC”¼ëÆùŒ/9†äœL02$z9ïô̦ºÂ}š5íÿÔÙ¼¯h%g&ßÀ¸ÎV0¬p¹?®¼r’¾]Š°+y>ØQãåùj×ð‹µUîr6ütÖç&w:ѸÝDóÞ¤Ówæ½I§õæ½I'uæ½I'zóÞ¤Suæ½IÇz³$«ÄFÓ…,GƒUÙ&7ÿá«x¤£€êAÂu2®×Ắ?Aoîç)Ù5E:S–{
+ñDå­ðÌÏ^WÉàE2^¨Ã› xà bäðÆo±`v£Å®ôØÑO&‡óعˆLØÉŒí°³vx2*‡-÷Øûˆ€O¨G‹ºô¨Ï¶¨W…ºÔ¯FÔ³E]{Ôw„2ã}QsÚGô@2h†Œ¦èкdˆàÐ5ÝÑE Ý”ÑUºë)1ÏÉ­€##œÀ‚3¸À¡ºO„Ê V† `Ú„¡Æy Œ`b#3˜Ù¡˜:aðq`®ƒ#˜œÀè fw`ø
+}
+™…EÊ£™IäR˜H&¥“J(¥¤RZ ±”ZB.Ј Ç„eÂ3ešpMØ&|Æ ç„u»Ü™Œ,èÈBðЀŠBF¡c$%z4D&Z¢+è †‚qÁd–äeoàn¡‰Ê²Šk’Ú'¦M4²|öåz™h :Ü5òÊ?°“`Iló1‡T_NÃpmJN›'Ï»T|LÆk:Þ&äQ‡£›Ä¸‚¶‡ß!ìWhÛî‘hN‘Ï`–¯í#¬5ÜO«ÇÄ|“ŸÍé4óq ¾±ocöõ ÐLfþò$d¸…y¼d×÷až]ˆ¥§5üäñž nÚælöúþ§.¸Î_ÍçÔ1?™Ôƒøt–žÌÏÆ'vê;ó¬8°ygžÃ4?›g•ˆÏpöY±x¨,¯ÉØ]í{J<õõ~WŽüžÂµ¥ ’þc¼p˜Ó_§Ÿ‡§É~ – !*QS)Õ¢¡bdªFKåè¨=d Š@G%iF’¸à „E¤%P^"%FDFЙjÓRq:ªŽèÎ@íLT KŒÄtˆ‘§ ‰$EÊRÍÙN¦»Fû®.F<OûÁ—èw;#Œ·3Â< Ì2ôºÝ‘"ÃiàC)šÚ• <=‡Ág÷Z·/Õï³}«m}Y]Í^^Ï^i¥Êß8GêÜ(“£÷WÜÏ_v#åÙŸ9Çâúùb¯÷ƇÅSʇõ¹,—a5_± ›±ö…sxˆsÀŠØ.¬ðûü4Ñá¯ßÂò–Ö{¼pŽÞ#¸
+ Såj|øTGùˆu¾9 ‚¶¹É 'á.Ý`þÐþ%þ}üñíÏÿüåÛoßüKè¾ÿúÛù§?]þø×?~ùÇ÷o—_ËOW1§¿â¥ü¥û†"²-+4dVlц#°BóÕh¼-7¢Ý*tZÍ.ëÐa»+ ¯¤«:é(ô“tS”N2äºõ’’þ‘îQ:ˆ/«+‹'«#«Ïn\¼x5âc,z-ÝÆ¢d(Vm©a‘&W)%RÊT3‰I”•JX’¸ÄÀd˜™$5inj¨©-Ó“ä'IP’¡¢˜£l0+Æ) T¡Jb• ¦«† +«†"guÌZ=óÖÀÌ¥˜H_– LQx…A(SÄ‚zA*h6È Ú º†¹n‹G&yêfÓºq1÷!ªÇ0üðo@P˜ùË»ðÿ Þ`š}R)Á;°eZ$)Á »"J7²4Òö<…)ÑîÄì`u^'N§>§Æ­7Üloëèp;8ìÆs;7Ɔãà°Ž«lÍÂLÑ®TZ[bY dK«JÛ¦¯5wm2—9Ä­9k…EÝ69Kƒ`ѹ®h]OÁQ¶O¦ˆž-§ҧòç93*⢃ª…Š¦ dMyHCyTì™ì±œ±•˜iEt…“ÑÔmPàwfþ²CýÒÌùÏ;4¯À¼¶ü=̯À¼¶ü?í‚$Ì•sæÅ̺„ßkŽ)yÉqÈÎ^¾0)J>Õ,º|O,FÔsüC}qþ–ä»ÿq^uMIOµ»u›Ÿ?›ÓèØ|Ác‡Mtt»yæfš)³Ìù43ì§fÌÍ<³h¶cÚ:ÓÌSM™k–ɦÌ6fotf›§6Î8eÊÑ9G@²ªN;j-‡ÒžÔ—Ô„YOÜo‡—PŸ§¯·XÛRa{úÆ@ï€{Ð?à D%6bà$â%â&‘ŽRÓUþÍ}ÙìHŽAø ø}YÀ{iˆIQ†/ú5|6|òa±À,{ðl¿?™ÔO•ªJ==X,¬8ôL7KR‘™_´t–Lwéè0=]FU&k™+ÖÛE#:Êߪ¸­œ¢·l*§ÙðDU‘JTk‰UÕQ½I­îàh:—I èì)¾\µ©1S4%•ó¶_»7ïLýªÁ4®ÚÜ`…beGgY­ò;í¯æ pPܤe{T{§|ªîLnýgÿJÛî/ÿTõ 5Ίà±ÂÅMnÿŸ;¥Hk°uåu× ·r¯—êþ¥ÜÕ…Xzz†Ÿ¼¾Ï weã^TÕ‡õuCœ¡AÂ_Ü þø/è¯à¯ÔŸW⟌ö•ô)„ïøÑ~&Õ{Ò| Çg’û@^Ÿ éžXâ-±»'_ M 9ƒ•kòo$ª
+Óõ`ýqš¦yZ0°‘¯æfp¼OÉ!=RÀ8O¬TZófÌ›-SVK¦!›ӌ͉»Õ…5„y·æ°´&±a—Åü]ëyì6Ž¹»<vñ8´¯ôìÕañr²|lŒ=œ}—†§~½ßµG~ÏÁµoÁyú¾:¿áøírŸùðgo8\‘»¶ìð)0LµÈÉVԮʦíêMêÑT2Øì0U‹ª ç
+ 5¦`Š¦d"D;þȦò
+åL£i2ͦE5ñ
+s:ƒÏÚÔ˜‚)š’©`|6•ï^ž=¸A¯Ñ4™Ê>/ª‘¨­´«½]©pJ!óqÅñâ
+à» n·+]“©Ý.¨ÍÆÌ…‘›•‚“e¶lĪ¹M ‚±Koµkš]„S¿(1n r[”ÛÂœ:ˆxÈ!Ï9îp‰tj(ÇPnB]·†º±„º]¦kÜI¤ëÌrJœ›Íx4ÈÕßd_k²±BmËCØ`æÖ{Ë<[Þr»5ÀŒÜgÝãÚ­¬›Ûݹîkß=8¯;³Þó~Ø{ݹù2«~›û^£¡= m#ÿŸ®zSy(½5Í»”þ?ÿÝýðÓ£?ÿqÕ;ö¢{«Þ=ö ?*ì©ü¨šÎ–Çê­‰ï]ŒëÝ>ð>_2`AÌëê&®ï=þÚø·ßqˆõöˆ|Æžá¶1É›…ÐzüH¹7‚-ïrýCxÊ0¹ú¿ÅŸæ¯_þúüöË×ÿê/†_~ýíkùÕŸßþô¯_þ×/_Þ~µ_½ùîGwú[<_à Žè<4^ÐôLŽeöXË#=?×ùª¨9U(rÛ?™ΔNÕžÝåV›Í'†{o½{ GÓ{Æ«oÔB×8ŽG‘{íqvOÈÛÞ÷; ›\5®švšW-E»×Þ®z'ÎU·úbXݱ8d2Ÿ,Ê«¶R(`29?®šLóªEµÛÂò:ÛÕ˜‚"–Ðâ‘ ’œ•žŸ\™… "¥#EÕ°åÕ*÷ÌÞÙa½”‡ÃXÆìÆ€“ ×bçžùdÁGHÄ@HOÊ•0òáÄþ5w¢J lî08Ø„¼B†i$˜‡8j+ñ©»…¹L0™pÖᙘ>0!1ë–äcX”˜úÃêC'îÕâ.²&ÀÔjœKH˜aw ̯ƒ ¶0ÃSl°Y^Êü1Ã4G˜g]È0ÓS â¯5¶¸ÂÖÏ<'Õc“2¾MÂiÔ@ò¨P83ÊiD­õŒ~·ðŠrw‰88’jgCï°ÛºnÅlÞ“lzâÔ®d–¶ïx§°3¥K‹9ÜS•;/³Ùã‹úh^†Á­^s˜éÖ~j”×þl”ã·xNSFùŒÂRÄ4›qX3ƒE/;º@£ô!\m©lßRzRT¨7± …,­ä`H“ÜE/áf%xiµ
+ÔðH„InDu,Ÿ‘Àj-*'­„­´­ì­$®—bºÎHu*½3¾WÚ/ô¯Y@“A\%JX°(A£<ÁñAåÒÏ«ZS 7%ì„U%­É­™Éo£´ä*h^5­wÚ»ßÆíO¹ŽÖsô§£ƒÝúÜÞûæ™ÃžûñjØ7þØó9_‰Hq]Æ]Âœ£†gº¥¨ËZÚ Û$²7¤'*vÂȺïXë‰åÝ° +ðÄ‚íYŸ-k1°î¼”Wñ«‘U£•xÜÅ™*s¥‘Ž$ž”ÍŽ‚Ù‘§©îàEɼ¨1/'R/’^vVt4£¹8™# t$õ$u¥HWR_ò‚-ô¦ÍöZÇLÚ“r!Чԩ¼ DZ ¸êYÛS|KœK¼+Ñ¿ÄÁÄÚ¾ÐHåhebf3 mâÄÒ±ÒÓÚÄÜ2 N,.Ñæ"3d0x¨éx"—£õ-´¿Ù,p2FÑ)¥sB»I§@BPÁ"Ã$¥¦†®©*—!׊a˜m°¶!ÜŽëöÀwháƒÚƒŽP÷Rò 'jèmòŸéåu Û—/ë\òymÜ ÕŠÐÌ£QñBúUÚ ¶­¬@«Rê,D꣊Ÿõ”P]RejNjod-ÎR›xªg¡5,ßÈbnYÜ }hF‡º—úŸ¥°óR8R¡ û%Jk²‰2ç¿4–4ØÈf“¦[‚3”—–·Á3©1¢S;VVúWœPºYŒ^z[(­Îªê•‘”'òÃI.Œ‡I‘91ÔVÅÑeŽCg8]›4û³§!ó-8“ôÊNÈÚüO[iAp„|ÓÎ&™o‹Uµvœ¶tcâˆÉÑ‘ÒcN&Þ?—«oÁ„2Ü7ï(Ö´zš;½çÉö4ÃÞW÷"©~8žºG u§ñ‚Vªq;À¹¢ù•Üë%çbç®ùæÛƒ3Gpì<. Ö þ#êOÊ%ã}zéh|^0H=L¡AH™§¹å,@$T[Æ®õðæ=÷ æj ã è„*ÌعÎ>‚pçaÁüõð•ÞQô-_íJ¸AÒ8É!W3ÜtUBûHBÉÞçdÅcUݼcàdY½ÞöÒjÞ·Eüi;y<3´kªk}ÝM_/ýTdjÎSsLÆBÊ 9€R˲ÌÈL#’IXÏ #-`’Öðñj^æ1"³ô(¬Ìø¼$tUŒ[c–D¬ì˜­"#•Ä©Š1jbxêšÎ8pzL‚î5
+>áÀ[
+º7$
+ý)ûòSîSêË+ñ5J{N`OIoÛY)kœ=@­hŪ’2`9ÝÖóÓU,¸&â¡àŸaS9m+öõûlîâ·_¬:üîs…O+1J)ÖZŠŠí¸oÛ¶Y‰¦­D ˆ†„‚ƒÑo…­ö‡)"mßüÚ¶øVmz¯
+K¨,¡´„ÚŠK¨.}ô[˜1¸gj¬Ù%¡Ï„F:foØÄ ¹e3wlè-W¨>iH'y‚C±6\,Öz¾+íµ¥‚
+˜0€fHiÙ™rkʽ)7§ÜÖí©¡T LñÍ].R“ºE›šÕܪ¦fE‹‚5—¬²j- WY»Ô¢~±hêa³ÌFvWnO4=mŸÓýï„b
+8èxçÚÞNë£Ûð%*¥›ÅðtU\—Æ3rçDgwüê¦Ûí³wÓ_ê gúù–àP5xQ!E8¸G¶àÉÐ6Z.°´% S#* ÔF;@é
+yë’ú¤!iLòQ]"ÞÀœ”à³N’I*I'5IãmRþíù³;êâÖ' Iù:û¨žQ;Òn\Û™2§d2ï'Ï <cwÆm3Ñ535EmLÌœYNܤÎf±ÆÞ‚#¢ho5IYT¸˜¹ÆÍEn®rs™‹ 2dÑ爯p®t1P–¥N­J›J]ŸK]Ñé$íT:—"'×¹1O,ruÆ·p]kf㵆oÂ|F¾ô"uÅ—Üð厦çë¯qMÓŽ×mR÷ùÜ]$/íEo‘¼wg/í‡/wÕ—¥ï9*ah¶ü?Qu‰Ps‘òF ãíè«7Gowÿ¤êŠká.ÕUà:á¡Â5 •tiw]]ts5J4Óéî9†??SamïXK=}-𮫯¸õüw“>ŸV]nÂWSÊ<4¶V«ƒÌ凜?ŸÒ ôUû;ýf|z÷Ûoß=><}Œ/tïŸòK¿¹üêOOoÿþðîò>½„}M»¯âGá¼ÃMü–Ønö4ž’_‹xE-%š7YH%éIMa=šdxÙô¢ñµÉü¢ýE ëuŒ68a\»µŒ›¢ÂÃR^›b´Å¸°Kc,­QMÖs$,tvG^ìÙ}Zò³5ê„ܦ0Æl‹~ZþlŠ³!F˜ |Éà¥DØ5Z;{(GÐËeÖ¢ôÄnäVj u…úI<ƒ´Ä4b›¹ZÎÓrŽš”—<BTLÐ<?ëéYÎN=ÍŽJƒ£çÁ¡Év'gžç†"fDœ‘ŠP­&§]LÎXÆ锥9I[ZÏN¼õ¯Ûêµ(=‘©…t¡ýYJ#CÅÔlg¥˜‘i@–ÃQß@ZÌE1‹èV45.ˆJ”£@ËIØ Âv n’•ê(ÏÀfÔŠnXGé´²ŸÉé“nó _ÞjiAëø˜$oJHâVÍŽÌFv#G û³Ú•º…ú…†…Æ(JOü¬ÅÚ_{ûËŒ­V˜YÛÓä\@%ñý?‘J0XY«93Õ]î<ŽIà‹Í{`ÖÃc£mX ˜“¦ Ç;y©]>R·ùè.Ú‚Ýä 5•éeöˆVö,ÿ,úàŽwÑëSlé`´J1»“bþvŠ¥6È.›¨¬BvY…vËÐA¢Ð‡Öuè~FÚчhECû$´â ÅÜ­&‹že f•vÏ
+*c‹y(0TÌij ô q‡¢&2U¨ç,¤*,dä’ƒ¶ÀÉž;n¿>¸ý/¢ CGÌssvÀf6·af1ûüBè²A–’VvIejˆRÌüþ¼@?¤þÓVH /Ln¸ä¥_Ú‰€;Ö)eã+¢ˆ0úî<î/U׺V÷ñÒtÈ«xÉîá’ ´¤3-½¬Boë¾¼bì¬ÀO¢/'ürÂ/'¼ã„9XgZ-ËÔq¶„,šƒÁrôì÷ž¾fO×ìã–­–ML*ž %Ó‰f.±‘ôKD"‘ÎM&²hF
+G Zô˜Üdˆä6“û  %(ÍTÑ4²I “Xq2£ Ì)c„ÕÄ+UŠÉ˜_’½*®@Qé¼&o6É%µIݤ~Ò@èMQc!?k vYlê•páÉÊ…ÔŽô¡š­¨øÃœÖn½¹ÚÜѹÝÎëÿ÷„Ù'j¸Dðˆ:ÂjxCƒ‰ ®0Âjv'è‚ Àj^ü†~Ï >,w–9ñ#,ì°¤ÃrŽË8,_Ï‹6·Ë+sî ©/ðs±(/ÏË¡æoxäœm¡Îöv°£õ lÔ”¨9šW¼Á/t®uÔ»ÁÎ;þÄ(*[|¶Ö«m±gÛ¶Ô·4¶¾õÌè‚¡5®`]šM$¬l|v8w׎
+[ ÐÈòg°Tˆ—ûZ(_=[YÏN”ÌÁ²«¥_¢çóõyXc’š:ÃÔP‡z’œ¤hP“t¡¦YÉ®äJQzÒ¡~_´yé•áßø)EŸöt¿ Â'ªÜÕÀGOB«°ä•C?ÒÒV&<±Ý)%Ô¨f§ÂÝq Ê^‚š‹-®y>ÓÎ;yÿk£ñF#ÐㄸH…Ú¨Õâ¸ý=–ÇœTt©««¬Ù9~½Çt¼1ñGT˃æ—_Wåno”_ãÖ¹8Âé4<OÂûD[Á G,àÖâJ¦†uJX¨p•õ°ÔÁöñÎã`0¢ᢠ$BàvxÐä
+\?6Àî:ø¦,Áy8³‚m×°ó
+6?Â{DA‹l°ÈŠÙ¡$5r¥BÎŒðÕùÓ"Œ,‚©AH)ÄUèª96”{$[‹Œ³H»ãIpdz€¸kº :ƒÖÈa‰<¸íù< Ä:$©Ä·FŒKĹÀPx„û€¨ëõ‘oü
+Èv‘Æ^wÊU
+ÓƒB¾~ËÿÿøïøÇþÅOEúa?þ‡ÿü=žþõ¿¬—[oWUÅ¿Ê~ãÿt_fßô‰‚$^$Š1¦A
+¦lÒÔ¨ßÞßš}þ…´E}„BÏÙgö\Ö¬YÃÃ?HIø*üøS çëËgbËÛ>ܾ*¼ãÔ¿:¾¼ïÔ1ž/ï»è_Þkÿ7þž<ºº~rñêúâò·—W…ÏœðON//߆žîé>ûâüâúòêìN¡$gÏ/Þ¾>{öúÕõÃð)|ÎßÛIûÆ®Y2‡_P®[™ ],ùÜFH£o´ÀÊI‰Ûd„¢¡Ñ:&C6s Ô+úhuÄ ¾ºq2ÇYëH…¬£ÐÐÍÔéØhŒÅ\Âc·Û—]¾´š¾¢±øʘè¥ÇAvºwÝ\×Í%ÍÁ‰ÊÍ»¹DNŒ >Ù펭ç K”É:À»ºÙÀp-ôcOîËÑL0ùºOD8ËZO<øÎÍ” š ºJ“Oå߇N•BÕ‘ËÚ|þF&RZˆ%Å2BƵ¬@Êè“•Þ”¥ø–ˆ +å-óÛyÌbH‘¾ux‚M''ʹor+¸ˆ#†Xˆ$¸“¦ëÖ¦ÅiZÉ#¢F</9m-Bnm5÷&m؆¨¹¾¸¹Òv&ô;Ý_ñþöi•mÏI1•ØØ6²
+t)]D®<ÞÑDWÈL¦ ºÔ“…iÆì²CéáÖÃË25km,œP v²OW¸šO I\<Bh²]rjŒûÍ §„¯ý‘88oFí¨nqdÉ%$ƒEZ{ˆìöó^áÊ&ªx±×š,&cH KsˆØ`i
+Gƒ?AMXЦzI³'UêO¦
+^é&t££€ÎÁì$CH²’ZeZYvÝ h”QA ó…XQ;‰hŒSN€.á„ÉXç
+Ö /Bà!<­ÕH4+ø.‚àÊàÄLjÛÌE
+„ÂxôüØ¥tèrÔfŒ2I¶Æc¿Éf¦‰“Üy‘›Z%5à–®j@ ¾Ñ½$$Õ¾|I¹+è ®-ðœ Ò“ŸXT¯N›¶ßÄgÀ^3ðXS2t—ÛPX!í€;’ºB–
+r$.Tµd„»¤~±YàJ(£C„S>pÊH„NA§d ö§l”xL*'ÒFcG $0S
+ lŒÄ ¡Xìx°8‘º ÓÛÈLÏæ(3/£JQë‚89ÁÐ:%15*î !™ý¸Àà˜½P4’z÷ÀŠD¥Fý’M–‘Î)[,¥Ú@¯‘º)zî3ÆÈ8Lb'H
+ nDòi¬~þO‘ÁÌQÀ„˜#éD–៑4 '?ýß";ô¢I²gB0sGçÈŽ±"{ãòôôô~í{òáïáäëËkdîåÕ9(úÔAõ°8yöúåÛ¯^^_]üÉáðàñ£§Ç¥çù›Ë«wëÕ.šÑ×ç—?¿>{ôtžáÈ·×!ªß_KZ§ðâüíü?@ûøÓq¼ç£…ËïßKùO~÷˜ŸøD ™
+tùÔìNêChÆC²Bª&…Bð“V%748u¡Q»Œ´E 1PY0à !ßÔê·A-ÉgŠ‹Ìº+ðÅLÐ4
+T`
+ý Pv¡ifH‘
+l.݃ås¤w‘\b•]>°ü`L‹F¤¡ÓO¦”vïj2»·)§™¨K8ó¦4³Ïtú>@˜4š4bY ‚%
+YÑÝn >?/GFYj ê¥~ì’°?ò÷§˜7…QÉøÐøÔZ£ú»¨.¢BÖZ›á¹h3u2b‹14Œ4‘Óé±Ã‘J»t ¥“ë˜:{543˜!(5ÌKapq>Rtw¦išQéêsÕÎ…d@ h`kªc¶v á‚¥†œ0_ÓŽ4Æ
+l‚3°QæN
+‰\c ¤¦doÌ{˪±dlÓ7í&81`-Ú£ˆÀñŠ¦L›T9ò°h/Hº—¨l¯#ݧÿÓ{¦ÿ¥óÁXIâ*µŒ÷“£G Ûºk¾É¸Æ+‘dsÑ ²ÊÓY"p²ÉWèˆY~ا<LtF빟í~B¼èN*£&¡Ç£7£„¬Mt"igpúÕ`E:©¥$ fÒg‹ñJÆCÑc˜â
+ŸEŸ¸ƒÀZ€-·v3+
+ˆÑB—²ŒÜ&é‚âQ^4ik_¬³Ðz'ËzÑ÷=H„ˆlÙ,É®ÚÒ'€©l•CÅ¥H!WBÖ(êâpwŠjh½ÂÉx¤tV§n–V.dݤ~ú&ªf&½áî
+‹~ݵ"ŠiÔ~T\ÛøüL,¸M¡²ÂíÊ/øÐ¥íjd³0‰•fœÀ¬–PøPs/5ë>×r•uª³÷™O4Ŧ‰¦R±2U5‚vˆÄŒ"'U—(ŸälAÛËYœ[ŸÇþ‡Á<àËš0Æv:ª
+ÀX%TprŽÆ$ò÷óˆMr£rƒZ†}ŽØFLk YgŠ‹5Q C¢ÆËI§ V²Æ}B-$ùF節 Brj÷­{ôq•P6*b"æ¾ÆˆZ«âv3Ž¦ùÉ$$Å¡¤•^v}Iئ¼(´Èí
+ E!S«ŸWâf–JƒdY_MÝÎu&Xš‰_‚Z\ºäÎ `Š"Û#¸ýryKËÒ” L¢Î—‡“²þ7Ýåv-)®DAWÚ‚Zz L¹ßãõ"R@W3_=§@¤ò±w$!± ¦~\Aå…7'Ðëq˜MåqPŒžÇßÿüÒãÇó"¤š¡7»Ü¥¤¬›w(›LÏØ’ƒù“®c®:¢Ý^IF ¨!w»ý«|ßù}rðU?®vœ%éç¿¥îèÇúAR¸ÂOµð †pþôNƺêaÈñߎ3—Ø'ËPbb8æìQìà¸?Pƒèýv6IÈúI
+ Ìk¸9Z¡cwoÕ~$ë/Z@Æ—ztµ'ŽÕ•Ko:®šc pr¡þ£R¹õ6rÜëÒ¶ÿu‘
+Ýc±DÛi¨»«:moaËdøkM€q<Œ¬`קyøÕY\qÇøáËrðu˜§Ñ—ä|ç§QÁ黂xiÔËÚènª‡»;jß>è[E5œ‰ï98Ë<»Hš1z^ÃBÚå¶æ«j6ºeéíÇ£»‘ü&ŠËÙûIïˆ0V/èVíä•^LÚ«šSãG†lœ´ÁŸ(ƒ‚%Tò|"Êÿ#††bˆð“îw˸é ô®m~ ÐŠz®¨±n²·ìç3vÓ.Øbœ?Òs¨8èÌç&Õo±‘}1]G`|é­¹3]$ØÝ(´,Ô{ÛK¾ØññÃ7q>~<9•e†%lWuf(ÕI¶êNì^$‚Í¢ý`°¡W‘¡s^Æt¶jÙý¿PüüÛÉíN)%EBÉõ>þ’¾y+ "éìºÊ÷~`
+šja ø…W…|üÙh¿¤Q¥-ÜÌQãßÇ 9…ŽçeÖ Õo-‰ôA:tÌ‚ô„¥–¥5fâSå̃˃‘»—¥ªUôpô&!Ú{{̺´0cøÓ.fg”õÙ=q/jMî†íMc¬ò6)šúÑ,4laPN(Ž¥{‡sÓŒ¬˜Ä]çL <= aØsW[Œ.‘ˆºHè!ìL²BÎ×¹üš"ã9*«Hï;ôÅÙÀgð.FK§Ø÷Pe0-¥Ñ*‹½sÚ4»‘µ K™ƒñ‹Ç—ît³ÛÌ.‚à/·
+€ƒ[€Ê‚æH¢
+£<¡ 7º˜›
+ÌÈÏ\Ýõ|™~x|¹H5m½ËEÏ€*OäDÇ Þ3A³S<ö$S¹ -ã/+æ 0Úÿü/¨ìV >óÙqm—?þbÀ÷Šú!lÉÑÓí¥3¼REâ ‡%n?:ãÞa,˪ÅEo˜ŒGsb@q±Næþ¥·}¢‚#D›š5øçœìïÑÈ͈á?n”øâŸÇt!-üƒ onlùeHIÚãÜ¿m×þ»¿j‰ß)ðkmáõb–ê¥%¬c„PÒF;N~uùå._ó¼„Ûæ‹äáôè6]Ži£tí©}´
+!Nn:¸]=Þã—ÆãCL¶Ùݼ*͈ØI[f²CgqôÅ@Lû懆dÉ™eß·º4%f`$,Të“„~ä©ËWð儸5Oí:(¹HPü0™Yw…—Š†Sw œ±b¸9¯cýµ†“èãš}]ÓcšCâvmuw+»kˆãQì"žP9|¢¥rÄU©Ý<GÎBˆ“n¤eS9*„ßQ8þÞégHB° %U’ÝŽØ'ÿ0k1³H|É
+Vj·øÏœñ“Ýõü]_ì
+ï ¤å’Çbñ26³*Æ¡H]”ãA‚<>DYQ@ˆ ÞÙF (B”A(N„ºÌ& ‚>¢©âÉ|$˜¥x°7baØùÀpJ k±w /Dó6¦…K\€À¹Imö9=1–œÓz›rN=24Hƒ@2û -ª‡2J{Щžß>@”ÚÁ’Á5ÓƱUF¶ȇ¨n2¦fÍ&€€ÛÀf¹E$L3•Ù•<àH°Ñ–›:ã.KÃ%®„¡:v.%º#íG3¸Sì±Q­+A–+—mÁüE(X䑱éBÞ%4šǤ(OíIê"! ™IËhz9©ª~€)[¯R ÐlA<¢úxà CÊ"{·KYàh§pÐ4 ÎÄ«ä÷›Þêü±Ôy²±äÍqÜŒF&»5X«~²œÀH¤ó"Þ×%¡íÜ£ò«µ¦–»¾¿Eõ—6ûºqoê OÎàÀ)iÙ fín†Ò„@B±Åù ›ÓƈpÒqˆÓñ¶ºÄ‘öj ’G;Î<‚S$‚Ý(: }®õÔm0r%5‰ºK¥æõ ŠqR™bœöQLiq»ã²Y~yBB¨˜¦¸ËJØã©û…-D,dÏQîô6L†H ŒüNvrF5ÝþЩ°#[´S·zÌ E>ÉìÎØã¥÷cpPÖ^Z´1gÂÅ#çoz>õHŒd¬ï7|g¸™<’«.vºµ¹¸¹E¾ •êWeú« ÑEì+ÛKSšóÚS4²§Ýûq°þ”gû±àoŽGÉÆë0Ä‘\v!ée¤° <]`Ú½>§Õøø.™ d«÷éÞ?E ÑÿE´C†¾ow¯Zž²2‚U½Š¶åÿ'’ø©xÝVÑÎ,Cße TcI ÆS*ñՕȧÎRáhE,¢Ôô&×¼ñÑÛ6Çãy*>[öÂ_·ñîq]÷¯[K{Ðhä[–­œ=Úe]ÆèÖÀ¿³ òî|c²SÙ↳xwPЦʈ½_Ÿæ‹kHòaC5ßÞ Á/´ªc¾X?“Í ¤"Üûc¸¬ýËxÙãLAô*{‚•ÿ=ι1D¤œŸ÷Úó­ÄÌ AÀzl·»ªº
+Ç‘ÊÌýÙ†0óÞ-sÀ ƒ{µ3c
+¦ò1@–¦/µ´…ô<û'¿Ê m¤\|Ø0}`[®®×ørp¶Àl*L&”yöC'Ìã€Õ¾Bº¯>rî{”óÊFë@xr7®qos©x£',¬ßË°¤£|~4ÞmóŸ¸V&•¡ou.ç»ý`}ïÕ¹GE¸½¨HÎ<
+î_ÖŽ{sWq_…:3;YÅF9|Q.ÅäÂòf}&2²ƒ‚LÙ»¶ý,)a¯¿ïi3À ¿¹¨Ž‡]Íjðm³"´Üsª‘^
+¨½b`(F<"X¬ˆù°ŒEÔsœÎXNÞ€Íw ü&:âÅ®ž›¡3ÉÚ6òêàÍùÔBLY‘G¸Ò™õPp¸å¶?Û40Èì°Ý½‚ÃVÁ‘ €À°âô NôWM‘¬¨íÞ×Ð1‡¸‡ÔÑšK& ê6v‡êе‚³6Î}¡ñ nR#þ
+æU|¹¡ú;Må|
+ï!P}@ÂuªXÖ¹DõðÍ]Nbo.ÞÎ#îÂ1ûvÙ•NÞ…Çkñš#•6ægb²ˆSHçxkÂ/4m$à Þó*{h¦ðΘBö‡*p
+Œ‚õ è·qè(u!ä*8‚ÜÆslEˆPÌÕÒ¼t„súâqc¶jT¸Ác
+ëYQ:JGj‘!ú‚Àƒ5td¤Ø°/ˆlØGï—ÎKÓêðß5çh€Oi*@JÇðµR‹2à=¾)€/:ꌮN/â¼]ÕDÚ˸V{Aâ—;±GÑû‚÷‰lâ=õ‹ü!0ÅIÅ}8‰ÿ¡|I iÝ!QaÝ>qæð‰IIÜ\?G›.n؆Þb¦‡âCÿ„D4''AmaO #~jhKÈQÔ@AÚúh«#ŠÂ؇yiG¥Â$¥˜€5€+¡0iÒ²Â]@µ
+À͇&q½ªUùñø „"„fÙ‘–žÓ9I„CMlny}}.máz8ôk7“ö)R àŽwLP‘­¢2ÌOL\ •Td–"Ä“¨öº¡S!fŠßÁͼSé/ÜXÞN(ßh•q¡Œå%«¹Þ=yg”‚ŠnõüÉßôúÿþŠa¨@ã8KùÕòßñã<>Ô[ª ÷0—ι˜"§‘Âîi¨ŠèäLøW€
+ endstream endobj 208 0 obj <</Filter[/FlateDecode]/Length 3675>>stream
+H‰ì—ßnÛ¸‡Ÿ ïà›´@³þ“x{°¾Šã­Q`uOqîšYl)RK‘ŽÝ§_Y¶c9ÙFô¦(W š(ÎŒÍ3ó›*˜jÅļõzs‘ýz¶þ&H oZo_ Z¯SŒ`ÁˆfR¬_jÿò×ufÒúãU{Äèúe¢VÙoƒý[½çÌhHsK¥È# 1(Èß±×júð
+²ò¼V˜@³°*ê^†a
+º¼2ê[ý79aýêž³y¤³“žQÉ¥úã>b°1JõŠ—´žÜÚyîõÑ8F…„”ª§F$~€%M€Þ˜’ú
+᫋οŒßp¢®¤H5x´§Žî!;¶”.)à”{GŒØû¢³g½Nö{Ißзò­‚`Ÿõ-PðãbÓâY×&,ø‰qUá(r+™Ð­û:~9ÊA¦ÛŠýk;cÔo:²lI¾wزóíIdŠh‰^d Ω¦Ò(
+—<‰>P=‹HõNxªH£®dœÈ´t×ðQÐÝâkÉäWˆÇÚÔßÁô+¾X¾VQ+øˆ\àA.¼ŽÈ9ä¼2I+’DŒžJ÷$Šé('ªSµu|ï œé[ÂÊ̦…â"àK÷•a˜‚rSRT? +Åsj뛬ßHd[j'’ã£Ú30®ÝÇÚ#[ƒWíéçñûÜÑ#év;ÿÁæé= t„NÕ­µû©ñO›Gø&·3wÎtùáîJJ>T
+HË*@,ªj…&Yẙ7bù[É”_ŸIÀ >Kwæ­¦—<‰È©,¦ã¤l ¨çRŠÑ‹—7_ò^†Ì¹ ÊYª$’\ÎÑèÏÚr‚êÖhš÷š6;5MC5šæblÅF#ÀO­ÁÊë´ò\ÊfÝ“«x4Q]Jþ& S@ß¼?å*B5á×’¥èf¹qFFe÷Iþf˜ïÕŸ1kRý},0ñM– ÀÎEç`ü>0#)¼Wð·Añ}ô‘—s@!§šia)ÓÜúã<ðñ¸ÚêÒ™>53<K´fûÓÎщF Ç÷¤­uÓ”\”‰(1=@éUÁ‚βº4X<‘µˆ9&ùˆY^EDàSà@µTh¬§ŽÎ!/±«—B>u¬¬XšpB!¡'$©_OŠIöVËSÚ•:­ÝWëÉc÷àK?áG¾¹ÏBé{C¾8¹&†&ªË$~%¹T„|ø({áð¼TÐCxªƒ,YÈb}-:¹¯šþÉé
+à[É QÆ9ú*6ÆÎÊ/®=×ÃÇ_çÇG>òrNª xÑAÀ4[Xð=88GRà¹(5±áD[\œ³~OV%3ìšj¢¬ätcïoû •Œñ*’;‡áL
+¨–
+õÔÑ9ä%rõRȧŽ•µ£KN(Ä ô„$õëI1ÉÞjyJÛR§µûÚþè~ïK?áG¾¹ÏBé{C¾8¹&†&ªË$~%¹T„|ø({áð¼TÐCxªƒ,YÈb}-:¹¯šþÉé
+<¥&6œ”es‘¬àâœð{²B§¦‚T¥-ênkïœ+M8ÃGN'Ù+h°½ƒs²n®ÀB"ƒJÄÏ2ƒ9Ãô[k÷Áiu½N«ÛϾwZÙÿAöœýdhaQó'ü,³3¯hm!‚År÷LHÇj*¢pÉ“ˆ ‡g|V’~ï° ‡ /éÐÉ9ÜŒ—ÉÙA£2ܪOåæ^3å›2ù‚Êä „11iʈ–²ù¨
+Vñ\Êη'‘ (¢%~\Ø;8§ÚèöX‘$bªžE¬z`Q'2enL‰pý™«H®vŒõÓ…6&_»èx¬Mݨh|Å|­¢`ð¹Àƒ\x‘s<ÈyM$Ì÷JÓQ ßjê×K-w :uRtmýÊ­´Á—.ü¢ÐZKLE!€šÃú2ñ!=Þ9ª=ãÐÚAؼjO?ßçŽ%l¿Ó꣕ öÇõÇ"óµàá\f/?Üݲ%ð[NVw%=¡PŒÁ›~±
+päì P=”FÙÙ†riµÌTÛÆmëƒ_òYA,%âpp5ÜæNxqg"€‰ÒNZ zdð^ÁÃ9RTöxŸ} y9ÇÌ®˜ŠŽ! ¦YY&ùœ£ )ð\”šØp‚Ÿ \ܯWüž¬Ð©™MÝš(«)}c3¤D ö\”p:‘øÁ¯ààœ¬ÛA‡+°È qijÌ`n±Gl­óäãæ ×iõ:«Ñ3ÂÏ/;óŠv%"Xl!qÏ„Áq|¦Ò(
+—<‰ÈѶWÇHï° ©F°`d} 4Ò¡“s¸/“°ƒæd¸UoÊͽfÊ·5dò•ÉAcbÒ”1,eóQºè†ÊÂФp%E6×|®=ñs?5ØUÑ4pðÓÐs´p.ï±|œÍ#ýýŒJ.ñãÑc7÷“Zæ
+³EÌ.‚‡N~K¢çýx[ë§Öµ,±ŽÐ¸|é
+b¹@_DÈ8G_ÅÆØyl™ d‚•MôŬM€è‘Å~YðpÎg£ù…%gw€ëœÉùÈË9kvÏ<Sr4d0ÍÊÒ¹È÷ààMHç¢ÔĆ[,©.ÎÙ¿'«Ô¢ih¢¬ºÆƾ‚Ò@Ð%—t’½‚Û;¸Ÿ¨ûèp:T¢4Ë æØÖº¢`0W
+rÛ†à ú‡^{J›E
+8u½ŒHYl(QH'ÎëKÉv-µ84`’rr³Œ]`‡»;3‹6„ óÑ–÷¤—áŸ-®``ä…Zå¸níâÍZÆ&ÜÈK©f­ÍÂGîH¥tgÊlëøÆ·
+¦s2%²&å&”î—å•zÀwሱ9ÂUKJ^8p_<·ìgØc3a>Úràžô2ü³Å Œ¼ðB«×­]|_®?\i°ÅÉØ„w—×ÌŽ­¿?G)e±½“f[ÿ:¾uÞ]zsÏ ¾Ð¤E"+Ve;8zxö ÞQ-¤®Sv-ªœà:wèÔy
+H‰ìWínÛ:}¿ƒþh€ZÖ·ìæWœÜ]4ÉÅu6ÛÅbб•E•¢’ø>ý’rËi¶µ>\!H"ÓjgæÌeôϘxÔÇsÎH|§¼\F±ø8”b´ÄGʇÁ±òžÄO›ï‹ŒãT~1:a ­”Å^H"ŸáÜPWFŸb¾{:_%ùéÊèµwߣ(Ë¿~à Ìîð¥X<R`î~Ø£Íú@"ŽÔ…Ñ”ãÁh~3;Ï E€F_.>Ë«”gÄã„ƈ­~öÅ›ƒnüè{ÝÒÞýÇõñy½Ùý¡vD&QˆÉ]ÈÁ6ÛkÇtòév"Ÿ>ÜPhÄ?z=‹‹lâ7€….¾bOiû·)}„"ZØÊSpÄvlj‡:Ômp*–\CÓcË‘¬ÀHV°fÖ®œÓŒyø$JB®Â|ëÖ^3$
+$åþ¾'H:†´kT;¸E”•ôè->†Ó,‚w‚ÍöÆ´Ø eiJP<-ÅX”Bm©¤14,>œý&xΊþªåµÒvR£Ab.+ƒa¿]w®r¤¿ þ»Ó„ßÜÊiÛPPóñVÞh÷x}­f %!ñú¸ìÑf}ȧ*¨ £3(ǃÑüfvž¶ˆMN>ÝιЧðdõ^¿b–úM4½ÅWìñ)Íb_ø6¥%Âée0ÅVž‚ëpǦK#\ÀÇQtIIZRBÛ Zofó¦šqyú¡ˆ7ÏØ»X«‘®AÅÙòJ¤ç=†WbѤvdšjC¡-PŠÏþžá؃B/¬jÓ9'Ü ¡(Ó|÷5‰*DpǦÅå–w> qøÝê
+ /é=¸ˆæUé”Qa%±"ú3à Fü,˺¢Eíø*ÐÉâóû/s@˜/¬j‡*®9B8ŽÈ÷ 'eÙ\Ä÷lÐ
+µåo™[$4Á q
+¯Ö­Aí¨æ4c>‰’ÁUÓHo€Oé2¡i©ºÞw“ˆcp2ŒÎp Fó›ÙynØ¢L×´wÐ,y >Áyò´»öô¯‚(Ää.„k¿ÍöÚ1|ºÝvŒ[\ÖþÑë™\,g¿<tñ{|J³Ø¾Mé#XØæõ#¶òUÔ·E›ú8K.¡€è±Õ8V`+X#i Wr h+HâÜ*ŠŸÞÓ
+üÅ¥ÓOýø»O`ÐOýØO¿ì
+†„ü.)Iû9°ås u¸s Z?öc`8£û1ð—N?þâ¡É>¸1°¢ÿ¢Ô¿cNYý Øü 8Ôe
+¬‚ä`çÀug1”„Ä;ž*RîŸá{‚¤CwѨvp‹(+iÓÝ›p«`Ê›
+3:…­+d—†È§§‡Hy§t™Ð”ppmíéàq‚A–
+¨qÊQ Ï·ìjO<0ÄuiÌ=TAíØÔí!,Mº-¼ˆÜ…\|?´º—fµƒ´ÁJ6còpµ
+9‚gäf{z@Û»ÚL˜¦ŠößÔ›#Ê.
+X×
+j†²4%(–X»WQCpþù`¾È·ÖžkC°´õázÖoBľ¥jÚNsWAbþ;ðÁi÷˜ ²òi{Ê•ù·EB̧`ú.t¨HlT˜Ñ
+OäëÇ®©üˆätloOWu×qyÅšjYòòtá­96ÝU][~t'öD¹Ýµ&n4™.OY¯˜šîÚbESuÇaXœ‰mŠ— §%KçÚbÁÐywò~Ä{òÿ† ÅÚD\˜ ŸÀ«iÂuÃR Ãrä{4ǶLå÷%&JÇï±åˆº¸XLWÕ,]DI,ÉËÖX±d[â
+釴â–ÔGU7lX®v\‚®µ®°œT%'Dζ7Z£í^ Ø–Weúð9}–Ó
+W³t")i"ÔŸ~.òqbú熅„‰ÔV0Yrékwc—Ò?,ñ껀ƒ‰¨a½Lq ‘ ¥tâ¶Â«¼»ÖÁ jYjd(Àø#±¯ÚXÜòû‚©ºu/'§R ÉÂè9J±-¯ÐSë9sòÞvgBå¸í†«*,&Õ¯ÖÞåðp^<ì…Ù¯›hþï8ÆŒ²ÿpK7Ku3]ÎìÝôÑèž_¤­ü,«Ê<Mµ“‹pωŒ—]Ò<ú]A·wÞþ7n_Ú˜gÒcΩ%årÌ*–î
+u(yQ@‘ÞWdÄZl
+,ǵqÝ ý¸r— ˜ vås_|iÞ(¥Ãýð‰ÿfˆ¼TÐ4û¢¹@Û^]œîèë˜pÁäÁæ×Ï_p{å-‡\ßMN¯u,Xãäd`ú=é¿ËÓÐû7É¢(ßTÝÒ`˜7©?£ _ýv 6½_
+׸´i»7ÌJ4‡RôÕ9·^C›*©pˆµ§?Y½ÄÓ ©¯¨(>7sŒzrQ †KÙ$–¡äú;ö²¢g&º€>·aõ ÷Û‚y­Ñ`XõXû…ÉL¶O3rº±¾í6aÅQ =¥åü^ôM;ûeÿÏØ’Auú˜Õ=í-nS»)°*”ú'¿”¯¬I 2@rJYì?r~ý‡ÿ 4eÕZ¸¥• ‘'Þ.Xσ䈫•"í§þÙöµ0.Ød—™&Âë¤845’ Ùôw enï72ü­Aµ”X—3§[t¥§Ž)Uë€RˆÛ7-YEÉ°?h:ºÛg¼’…í"[¦~a¿ÀßVå‡,ÿ-Ò‡$`§^«ú 
+À%ÅÊ‚_צ ãpÎ.üBýþÛ/—¹'qûÿøÝyoDÔÊÚº*[S-l9j:åÞU®‘–ÄXÛg;…
+ò[©!†ýªXÌb5(ØO‡z¯õ5s¾Ð…¼ëýø&]®}·²68qô²¥r4{t>¿HQšÌ-̾´dAe TÂÜŠ²m ‡x¢¥QvÅJ†]">Çä£mÀŠ!]ëžëû·7Än¨¾£~rh2ú(#g7ÁAý¼
+¼¬ã×}ˆÁçkMŠÜpÿëPDç9'¹‰¦P)ƸôëÐ}\„bÉûuåP(lI<võ—Ö¥iNü]ɾï‰Ä_ðtÕ‘¾×Ô·î=Ã\Ðøɨð,ÐDÜ[%TM‡zIDÈË*RŠÆ&4ßGëÄ36%¬dÈvP! ÁÏÂ=Ãø¢»nìyývãØÛç<ü+Ã_üÆ54
+¤¥€|ŒþƒïDZiñu¢~  ”»¨ÕФ°.æö–öN ´í ‘Uü’_ž45WWƒ<³o $óÖŒŽ4¥_*dVÍpIã¼Vµð¢ñ¿E_¡³ýªgD‡4oµúRÑU‘åUTYeÔÏ+{;óM RÊòrV½°¼A¯“¹‹ÆÈPîÌC6›YÓ]PÔj¿ÊV¢ÀÜÆJkTx{˜uzvÍO
+Ýs¬iéܘÁ:ÒÝ«VíYe‡Y“ëöb*¯×­1o˜,¯_Ø?×WZŸwQx)ÒÓ {±(êNT´þù-pyŠW6’Ú¯eîö(Tíáéo˜ïlT…A¿/ùa=°ûNQFiÅÊÑ=¤ ò2Ûa(¿É!èUÏ'¢O²“ä]ƒæ!‡/Ù"
+:®eg0]+-CBfG:%á§ÝfŠƒ_ ÄE¯ˆûñ).¯íøýø£mŤo‰WÖ˜žêjÉâÝ"þ#–EÍ‘êÖœ›å@–ô³¢*ÔÑ°fN³ jN3ÈñÀP»/Äùá^mgÞý¾îmåüâÀ9&g§¶‡­Â€½œXѼì³gc•™Z0„¥L¦{Æœ­Ëšó. ïÿoUD6žô ϯˆA_ï¹Ò´ÔoäàÐ=8Üç=^ón»eë7Vüµ¸¾xµz³—>EJFŸä¦cøLËqvfk†ºï¬ç¬MôͯqxÙk}GÅ­;ŸÅ«`Í
+0)¡Mª,ü+F-FæòùRj>H[’uÏ Ø
+ Y£’}!·ìø£x¸jeôáo5R­9K8c$ÿ·Ø¿ ?û×&{Ó/ƒ¸Ïê6Ñg¨)’O-Øk¿¯ŠZ瀤°²Éò‘dz/Wɸi­§#¢úÔðï«jeöéƒ ÄFE¥>b-ñüijø
+yÍ«ðÏí}ÑDý'ŒÑjÈ•¬¬øÓÛ¢vvýlÆ‘§:LMcµØVüRyÏEr¶í5…Ÿíƒ‘Kö²⊗¯U„W‚H1­ˆp‰|ÊÔ™H^ÌÇ…cCPÿ5$ÿ­¯>N·±Põ¹P¶S…•m;)F¤RãR°ô‚]y°é^…F SÁf=~d€Ô8/òÆ‹EkuÊX…B п‘ùK.­#‚|´1[³.} nùØúÛp®$Û
+Nâ;5¯Y
+¯³ˆN™'§„OÃwð_N‘íé#ú·ô³Ê®Ü7l_¿ÙþL\WÕ˵4}«”0•ë ›Øs¨6]6+T-n‹\”oì}96r
+¯’‰V¥ð3d1ÿ-«å_ßþ|ÜOx»ÃõžÆöÈØ{¨Ôæ~5\VÁî§p’¶¢bB­U,Qö03ÃëœÅg´-öŒõŒäΓSí¯áUH™ôáQ¢µÅPÞIâ•>Ë J¯¡r×6ýÝk§Â|ʘšÎQ*–µ}Ò ‹¿9TYb_N@ËÚ[Ù<'Ù¹üë•ß²§#0ЮíƒXi¥&ÎSj{Ÿd¨úýPALû)x…ä=4›J
+$µZ³-#¶Â@­ÏW„úÍ<ýnêj c·‰®šâ‹ÀÞnMzµs³,–ž‡ó!ÖŒŸˆD÷ëÁJé(™ÃKû8ayÛß®]òT²‘q÷ðÚ;,›í]?Í혹§VmU|$%úÒí[šó^U‘)˶ë sµâõŒ¡ú1±ÑɾšBš9ˆ\ÛDBŒ;ðýð2Ö‘ã‚èÜ?\h 8€rHN(@á
+™Y9U¨ÈêæÜ>”l;Æp€QÂr©6™å ùbÁ«ºo´:¤Ñâ Ê­Š¼kÊ™ír{g"Kud™K€r ÊöFb »Û !†…CPƒݹW Õf´õÜbEOè\Ô½ª{13T`óÁêÁkÚ›pß›ÒÆ“Bqp1³T›Ì)ŸàEyžø ¢OÕaOëç¨UŒ†ÃÛì«má šÄ·°'dî'—-,}Æp+»sèe";hj(‡Øáj¨^ÍBÉz4™ª÷ƒ7¨GI¬òR+ò€Ãý
+Õ0ú‰ Õl1©PÖ­®3¾4x¦zK[§ †ªh¯ñé"[Ó^/J‚ó ’h2× ŽE"ÉÈ2›ÇÁHµWÉG¿-‚µ‰Hžªló»‰Þ°ðOÐé [–ª\”ZDÛŠ¤ÃÉÉ›¡<FÇéCsÓWaªÇÃÓð›Ê•Ï¦“BxóÛXñá´/š9™ÞÅäJ†éŒ`ºKÂt#›lvhÁ}3»ÛÓva«V oLfFvšrÂËNîP-˜
+†p0<n‰ø)"i¤\ÛÔ&î†~s„·+rؼ9ܤ@’5ˆ÷Õ¸„³½Ì=0Ò.Z`—Ó¼I™Ïœ"Ïšöýq2vQÝ{ôøt«êÈm"#¶äQek›³a+SBðëwötSy,ì¼Gé,é@×klžàÞMÀ@S¹~ÔëPCDº«8©'Hf„‡láÅí½)øqŸ%z?^û”wz?˜5µ[l)¢3*ë
+ ÃIÌ°“6Né±)ô—¤E¼Y•îèR>(¤7˜F‹n”¦ó¤S]( ™3¿“k*[6×ÜôÁøþMd¯ÛÑp ©P ‡·X¹˜8Œ)¿ÿ˜³ nÜ‚8 ÁJ—·{|ô˜»6óÙ†>Y1h;à(ksHs³Àm ßÎä³õa
+}4GÏæéÙ V>Z‡'gñ$`oV´À=lÌîIM1áD•è“,edzp}¶UÂ1p½J ¹\¢©éÍo—JíœQ®Ù ñ“R7Ñýw­¸,íh²ŒÃjÞ,ˆÞCñ_b„'<QÏaâ-n øÉŒ£À,ÒŸôÑMRêË÷PSˆ©¯ÿèïo€xÀÍèê1ÚÔEØ€áÁœ'ÿ¿0iQ}€cS}N­xú+}¦Á¯À=²‡Õ<¥_vS¹Ôå,NÁb÷ü×ÖMðTD´fÈH’;…§ÐÙDt2dtm$sØ>t‹üS€2ÌO±n•UGÞ“W‰Wmäúô¸'T¢VïEŒ‡zÕ \ÀÉÌÓ-À'ÎÍfQ$
+’Ídç1Ë4iÉÖ"”V"ÔlW*c•ù,icîœBAÝi÷5¯ŠÃZ˱øSTU·ø¨Þ„j2 Zëúé¡è*|räGГ;16çZy$¥%4ˆyL.++íKUrnJ:dìewé4UKœ%Ðxqï¢G\C00Õ'×…Éæõ–MÝ çñ%¸³[’’Dm%G'„
+µBÞt„ðù ¨X±ïö
+?þxi¯¿üúúãwýüM(Ê%wKÇd‡Vv@C[x¯á
+|Œ@÷~˜TOPªhÉ6ñsOÒŠXvÓk‘ å5ë”+0¿ãÙÆFé–¤qê*Äñ½+%Ç}®Ðºð:çéE·°¨¼<ï£x,ìnxºï¯«¯¬@™ä¢17ðiu2ÙýV+âY(Öïû}Q™=µN¹÷¬ˆ,K Æ) AÝ·=f`…˜bq¡+´n|ôô¦[Êþ
+G–mà˜4Tsýb }Þ—é=8B*8äÉ=Ô¦X{Øx­ºÎ?òMp¬vrfêdÍ÷4̾í¼ÖðVÆg5k½{dŸcê¾k­Y'ŸŸ¾u/Z4\ÎŒQÐàáÑþÕ”t-C+Ùd®ýaÃúïuìùùK|‡ñÊÎ^©Ì©w06ÙÓpƒµR¤ÔFn>s›~ô1fI›7ß×]_9úmßi[m&ÒÇUp#ezÁ(L´Çù°Ný­†Û‚€Ûní!´åmÚ´µu-[_8òÕÿ
+H‰ìWmoÇþ÷î‹
+Œs¬´ëXó)jƒ±±³L9ãiH‚ëDÆl3æSEüt_c2æ&p>FNcÛ˜<°­1&v±Í§Lë—]wÑÃå-9»ÅWéÓ}…É
+j ±k#iJVî$zYçk9¥[ãcH
+TkwiªUª³¬çìÓ}bα‘j…«’7Xyš,²HåK¶IGw’]°Ù´¢w›æ#)=ä-ÇzÓµbŒn­¥d '4LA¶Ç \J$δ?# ¨Y(¸]Íä·(žƒr•3EòtOWÖ¯…(þ°ñ |ˆA)QV“Y»cÐȧD>åè IÎ+CCÆþT/:¢S,¹ªb}zV¿þo'×OÏÖÏîfë»ÕòæáSýWþ†¿þÌÉùsR×ß,o_}Z¼YݧÏöz2{·ì(§Ïïï?.î–7ëùmýÝÃÍòíü¬Rõ9þ^ÿ*/óêcyP}þïßá÷“~_‡àå_ ýZ“ª_Ö?ýSÕ·ÂY¼Vœ|P’Š^âÖѸE=R!=î$Ó!ª{™IàÀNJ³ËÚìËWO,ì›—ò•Ú¼Îªb@OÚ0¦fòñ8½Z.oˆÜÛ‡›Û»ùr][}–%F9JåÿämäðÙj[M(„:È3t*?°Ú³>x݆hë׋=Y(½þ8YÖÁ?âz21ŒPûX{ň?Ö¼ Ì‚µ×Ê)§ÙDü.%ø6 ”vº'û¾RPk£”[Žúp¹Ap沇ô˜Üãc‘#1”qBD$Q9ëÉäO˜ù¤¾f~—ùhè>úà¼9¦ý¤—Ä„ä"6‡'yDs3Œö¦¬ÿ'¹4s¿é¾œ9å›Cs¾)Iß|6ôˆ=’É°„ÞgcFÓ¾)y¨¸ý†0Jâ×i¼ÕȵìG›”Rs­u] „‘!3™U‘<uI*’"ä8ÝñAI÷Úa¢ QÙñVÑF5«HGÁÅ¢-¶,[ØFtôôe¢ÇÐpÄz’ž2cåß_v'­†(LÌ‹J*˜!ÚʳIßȾV}_T…–럭‹”Dw‚åòvQ5»_6»Ü͘ŠfÏfÏÖ'Õ·€Ö³j
+Û×±á¼üOÔó~oüegõ$$²kQ r¥Ë®a®ì’z8ìÈ™Vß"›žm‘aâb›ÓÙ$–|ÉùCA £mJ‚@OÙ6©¬ö™+ɱ&r=ô#.†Î&J†ƒ»PDªï #RŸ»ÒP(ÃPô„d‘Ù9¸“‹Ý MqO®° m
+ýE[”aÒÔRT È
+¬¶V`I`è“ë÷ÊÉ&¥½G›±-¼Óu„}ÑC9£ÄÂÀYÅX”‚÷¢]«kqÉ!a$  P#Ádƒ 0E-Xð4ðJi´z!YÁ-¤†í˜Ü¢•z¥ Ú(Š¬W€¶(Âì[BŠ¨„-¨€Ù‡ad#aÓâžDY˜ÌÆù|/!Œ’¹x”ŽêX9›uáÚ( .C),„ cd@ìk—µÊ×(œÂ§1­X™a´à<JÏ×­ÑÒ<`¶×2‹ †l¤è‹o£ øN£]¶Fy éâ)äd3)Ž;Ù7«>ŒÃïôÅYýôÕúánù¶>}õîæý|r?_Þeï6
+_F‘IÁø¢ ·ÑhÑ`b‚0#º
+ÐL%3¢ÎâòÈêEÞé\U
+7 vO@„›Bý ÿá¼j{³:Žè/ð¸_"ÙU!;³ïí'£ôCZP+ 
+R!œàd!þ}Ï9³?ÄR9\_ïÝ9sÎõøÄAÆôäaÑö9B¯ÁÛ2b®ÍzQ‰éŠNF(JÎBpsÒ-€V{Öí §À6røiâÑÖ:YàcΘDÌG ¬ä¹h‘dÕzÁ»À8ÉM€Æ-"C5PÌ=˜ÅLUqˆS•[2t‰hr‚};XalÔËĶRƒ mÕ~gUCbkGbyüY)j(ȨIo²œÐ®@xT
+ÆÞ £ä0^8§ádf†LQcÏ (‚¿¹Àf–vd%†ô0ì^ÓP8ŒÝ+Ø•ª~νMX9#’àôK5@Åàm{jW®
+
+‡¬zQñuƒyªm`™íd\
+$Á B¹$ÇõÄ}…3ͪ=Eéš]æˆôÂ$²[aÄ 
+šä®ÞPOlHã…âðcBl&W#2ä³$ôtö*ò nª‰
+›@ïLV ¼T]¤oêGEôðt\9­¥ÎøäUØIak.Ô5à8œ…”pØÆh–ÄÆ%ñp ƒl†ifµQ‡×É·<Û’Z&ÅIFb% v}†eiÖ˜³bjZ64ìFgVa<¤Ø‹´¸ä]}ÆPÃÁÁÚ‚ 䧧ü÷ÕÕ)¡nêĆ¥¤ûË•pä¡"’âÙDz•gv‚L:°È.¾ƒùèB,.žÃþº¦4vbÒ¡r`^
+á§›Ú R–’4sÙ€pà.™¤Æ)Ëyd˜iÚCZ©åùgÛåÀJ²Wm`p•1L3Ø(¯›´3† J/êEnbF¢ÇIDár¨QkÚ¾ÉÄ Q‰ö¼Q¬%ý†CÃZö4W NÈ™[´OI㌆I£-B–]ª™F^eª4Ëã Φˆ1I[C’y‰tc×/¢HbŸÉœÑ,k€µØ6Óùò ©‰´ÙBµ”æ–˜õÓG®Á‘òsØ ‹Îñ.üq(ë%Õ€1Q0Öôpx„ž5´¾j2z[\Uèõ
+1
+‰2Ê<
+ì´ M…EP :Dd«ñ™kLÿ̘ŽÔs$täOïh<1ªlѱ”@©ÁúÄTÑÄç9ÍçΑ9Ä^Ñk˜VB) GêrS*Y‰^êÝlM}F6ö˜Çï‡æ/µ-üG‘ÿ
+í ŸÍçœS4âÕM
+8ÜÄ=I÷ë;€ÞUúïÿ1ª?c‹a1 6»-+ì*(ËJ×ÀÐÞM[ bWzIË£¦ù~åQ627Pp’^^ŠtÝâ”{Ÿò~×lcð»$—w°'}1qk)ߌtç•Ïß&§qÈîy€MÚ)ÐàœÓ®¤ÊgTԇÔ»šAPç$ uÂrÀîSÁÍæx3ô¬!5C Â·¡.ÇT ßõß•¹àð2Q¹èƒžïdõȬ–MFY5Lºj?ò¨æ î#v¹a«Pƒ´T
+eýƯiÍS³.Ë*ó@¨Âê¥t×33e„÷ZÛ,ýqUw‘•EÒæÌú2" ÝŸú@Ss£ ÚLâ?ò²sæªx”Ѽk›Ë+{<JÊ=(9e5þNGvÏ4þ|¦. Å£²ç¶ÎHUÙ•¯Ëe~ç³Ô33p[cÌ·‰þRªÿ×ö
+§ÿ¾Ã4¬âµËÿJP&ÀM?³à ´Apƒ1£A´ÎÀñyd £ÏG+= N šd\Ðî඘¦pëïa®-ßå–ƒ)ØÉM™Û”6= f2&‚Mšðk¬1÷qÌÚ”„J.ET™ˆÂƒ˜<õ°e€ðè
+0 î+¡
+¡ÔØÚÛ MJ
+’Ñž‚uøêÔbã W¢7¨ÿ§¯*9ãÑ2+ŸÙ†ô¶gfëÔl³" y—‡!|êí@(®u ”*·ùÆ×ßûçù¤*µ£YF`Ò1[h$´Ú| Û÷Jmg„æÚC‰2S…v¸ç°úù¤%{íNÞpm¾û¤z2ô>¬4ôåÐÌ7|Ú«Y7}t0Kkñpgÿô,—®˜ÁžLkˆr$¥õ„J²Håtס<ZÇÏwTŸ»(\°7Û~“x=¦‘ 8 é
+½]¢ ÊÎn+ò)N{´Ö§†AãW­« Äߎ0ý¼úÓLÆçíê°ôWí9¼Ûd¿ \,OW+h„Ͳ:Ü‚r{¿1É€/×ý×LRˆä]
+iŸf‚Fù¤/‰ën&U.¶õ¼¹m¾3XA_™pÖd4Lu…˜Çgô›1É¢‡"œ4o&Ù!?tsC›•n0ÉË.KZÃÙ£ ¤|I·ÎýئìèC«¨ôàpò“l£>‡{áõ¡}ë/'Ÿds_Á±EÞZÚ°þtÿ«^þ ¥Z JMÃ!•’É~ª‚€zº~꿯Ô~û ÔL”iH¤huÒ€P a0 :g@zV–õŽAëÈ,qô°“ˆq¤Aò¹$ÔËœXƒpYñÍïó¨o<à+WšTl¨’Mpu>ŠDîïPttX­]O(4ûc‰î$ÅÊ鲘s¹üº z5Î'qbio¾!7ÓJÿ¤F¡Úå“@«ýÑts°¿¾˜ >“ÉTí)ýŒ|^Л`4»ë>ƒ0œƒ ÐýõÌ S‘.XÌ…Â/tÓgÇ´rÂQH*æzLMƒh¯¨“ò¤¸¤ˆÑ”Æ ïÝSÌr@¿ž8˜Þ·Uä'Øl†~nl…Ò–=$i¥y§&HzÞ··Yê}¥³Íº‚rÀ<‘'ÄúVý‘ŠV©zã“T®åÊóž¸ò³L*E)z‚¡%'JbˆõðgŸ§M—u™§
+lƒææÅõ˜Ñ1ÄÜ\‚4su«P_Xà¤:wÈ}Ÿ¬4øÉ£BKŽ@”• U°¤Ì°ölVZ'Dô˜Æ#<¨›r—®lŽ”µy#Ô«‚Šåw½•ˆZÙ²´ÚyYëùN2¸¡X™i†«œ\É"n9ùUm…qä[GPZÚK¶²€çêˆ'´Ç&ø0ƒÙÖ¹ÇQ—Ø{‹064pbÛXy”….vý‚ühWãóÕó]Ó
+£ugá|!¹Z¦…Á¥Àº5Œpyh¥Ô,ÊX.
+‚ ¶«¶‹Òõ0À¤;gí'²Ÿ0ãw”šp´¦u£ÖX¤£Eö·|A BHu«ØxŸ@¸šqÙs-‚9•z´w‚pôc,·{VhãZGŠû|ÏùxŠ N±u˜f»ºå´á<ÜÅ”¹åÁ9^‹Fé˜Í^:q6#›)ŦŠÌ=þ{o6œw®wf² ä,éè
+K<äŒbSv‚b-zž'2{­§û†BYlÐËë¢# ²Ã.kb%â¬rç®Ø‚IÌÝu]ú¬r}³×³¤Å–B=±=¸(¤a‚¬Ö¹od˜5Ž†7ecè‹dçnžŽÄk Q¶uV¤{¬R[cWG¨ºÓ»?M+]ç3;'a; ê¾A}ûÈ«)›G…z]1zRë|Ö8¦Þ«3‘”TJzšNÎ
+ƒžU!_UU_ÖPjê
+0¡TQ×Í QP­œvŽµ¡
+Δ68š]Q‹q—g,/ö›!e¾ÄØUæK‰Žv•ùvJ”gm”ö ˜Çädž[¡ôQçôQúÖv–>êˆçÇŽyCî”zBÕj\\]LÐÐ÷ïPÁÒ6vÈ*µÅͧò‚ºr‚Ä£Vñh©v扂$Üæ3HœïºXxÅa°½žÚü÷k¡å~ùdCÁwôšUò´"1ì4´æÙ;
+¯)0„œµJ¡¸ÁØËŽé¦ã/?Òcý¦%˜èsæ¹ÇI®X?‰™}Ñá'ÊMtŒüýôÛ‡¿þþôúí¿?¾}ú÷Ÿß¦“ýø.½ÔÇÛ^ðê#:=Þ÷¢Wúú%ÍüFµ%“œÀE»Æ —î‹ *]ÅWê«–;MAAÎ6ÝY§Ö³6ÙÊTÛ ÝY1ã½.™mÖÆË,y›¡;ËHUͽT˜á¢Os´"Žè[ ,ÕwfAÐÞYzVç¶m*ÈJ !Öš.‰ÐÜ.Ú ûT®èò‚ v—K>+ôå%—4äQ#^Kâ5c õy\æ¼Ö5u®´OF†šÁŒÀvN`AA¯RÒ jIíèIí…Ålu+¹R®ÀbéM‰,24XÏ­âÿºu­p]7Íå’‹§¦Zd¢ÖnÝë¬W Õ‡¥hB#&9©ÈÊûëÌršÜ 2²¶â[ äj°Ó)ε »²PnMZV, „fjFÕ‚]Ó—æ
+y°Á¼Ù»‘\üU9\µ.àÊKä)×r5e;­H½ ˜Ü]y¤r &L¬pÚ¸J`Ò@…ÃLˆ†K£`u㶰åÏ’”Óéj*rªßÃ:e\^›Gµ$÷+F׶M¹L¦4¦,? dÒÖûaæÌu)^>_¶åvÄ-;b{É°!×W¸±/'ƹó%ÈDf&Æ`—sr}»ÍOåÄd|ÖáfÌy5c›a/µ‹ºNë~y%£®GÈi}G³+œ#f똇– ¯£ ÉHêBh¸´@·¦:Ú†þ<²E IÌQÚ3(¨íT'êfWX¤kAÍÛIQ«6¡–Ѷ–6õ±cÞPÐô„¢Ô4ôýQt¶„õA¥Ö#°hÓ·±³
+êÊ |)ZÅ£Q1™óÄ”“¿ì³_+%‰æ–òô šŒ×k¡)¡Þ>ÙPðÝ•¡¢ŽYŽÄ°Ó°kŽ­|¶­ ,~GÖŸ°ãÏLúN¨Ô{¾’ÇÔãvå¼v éÓp­éX6/y›íÉ^ÑUî:¶|Šû$‚ÞÈí0sp¸sÇ!Øfñ"h\}öÞé^S‚”•Ì~¦·7{Ù1Ý~‘ñ'J4=>oåjÒVEìúÙ]Îö¨q×éñỂöã»ôRYÒþÀñ¯>žÓãýw¯y¥¯oBAZaý‚2æ¿*ˆÌlQ²S &õ(‚U¡;(®tù°šÓM³ÔƃeŒáò·A¶3-Ír¬+}V=¬)"ǪÆ¢*êq®Pª\Zçh7T†_ê`©¾³‚öN–s 9ÃFPr·uY <öVƒævÑkQÒW*Ë—•tvXSopµÛÎÞÅ2fÄk ëÒ0uVõŒèΖ ^ËZIci 55ƒÙ€mm²
+*¥n=Ë@9c†zws›Pre±¸tT_âœÿ³_-«–7ô î?œ¡3ð¥^ªÇ8‘MÀžB¡ Á¸oiCÿ¾¥Ò£Tµ÷±»“IüàNö]§^’–¤%ÕÊ£òiÓÏ*©k–¸LÕBÐPóSEÑó‚æÖª‚ǯËS”Í+¸þVn,ôŽ¢î”â”mèDˆ¥X î ¬/ 3‹g.Èi0™÷*ÓÝÉkrÓ&O‹<Ρ/“\ ôa¡&†–² ñÑhì}[‡g«
+ ÿ$…ýºR*S&4¶¹lÄ2;+¶fáGî]¨ÛgÓDd^qPÏÝX/ •£ÂËZ–.zWH/ú)su”¥Hvטäq„F.Ga˜æKí}[‡Pè\ðª•ì^´µ`¬o=É:hš0QOËÅ*ªo¥ïX2hKþ’f<„¡Sÿ¾É13,Õ†!öx6OÒ|”ĨXt¶(⢥ûi¸j²ÎTäM:¤©ÈÅAªÀs€ê2ÅÛ¶¹NU:¯Ð±VmÂ6!ÑÏ*d’Ê«í@oã„ȶUA©}t¿Uö¨—é½›µKÇ*ÍØ$å¾Ô&šûTYÚ/$œØ{J@ÒÙ‰CÖªX+ç;ƒ Iiœ •‘P\u×yKfOm^6Òd˜!~ÈS0 ®t˜ ÍÂ?#@¶9¥FÝÉjŒ –}T\†EZ+AÊ0¨nÝ{¥±t2ÝŒ°Ã®@H}@ç»®«ÜQNcAé¡èȾÀtB˜›ˆô¡HEK!EÛ©åVccI·X¸Ì$®¢-úCRG‘¢>IUZÞ¬V
+JCÁØAâ[ÒøƒÔù²„ä”ôÍ9wY»<­H±(غ†ƒd«Uu·®I×ieHW…œíVS2ëu¨¸Õ†*6@ª)ÖòÚUyy¯ôRÚá¾ò*¾Ø1S ‘¨µ"o‘JAÑöæ% TM®ÊL•OÈQÊÀE=xAµìoD®
+o^âƒÿ¾øë7ÿøçw~øÏßøîßÿúÛð‚¿|û^áññºåƒõêðøóOÞöý(SÄ,gaïM”ÀTù†ÚðC‚ÆPÆ2©á0e%=Ó2z'ÖØHH%"8ù5 0Õ»gX¦èŠúÝ6w˜Ç‘~ק6õ;­—½ÜÓÉë CQ‚Á祑†ÌNh8LÅT'ªâlv²}?öÁóTŒ¡Œ6¦öYÊm)U]Ùø£Ö¾!»tÓÓ–[ûÔc§û{ŽñŒSìOôœÇÔ s^8ùPÙ¼0R
+†ncØKZ ?28¡š»ºµL±M£-ÔåÂÊ[1²óWœ“í̢눘4Ió_ÄÑfCB©µ33V¸­ â,©êÐÉ…TUOb)¨©4¸mñ5+òÆhz ²`%ðy±¤ê³†ìêê7ƒ¼ùF%žÐf–d™oqìñf…øˆLêùŒŸ¨Þù
+s_ËN­Tƒ2 ù¥Âçp„zGÎu{BœPëݱ€Aûh:G–è‡Å5!‚PŠ½æ!ïT{ã†H§It£êAé†>’Y¥ßŠ8B W^ Sd§Ðs
+»'¶ß[êTH‹¶¤sç]žÝ¤tÙM–YºáJ•…,ã¶ä6kft$KØ)¶c:•UâøÀ€xÍ…°º„"áãB(LÆYGI¹[1
+YÐÔšùfyDÍ®ãVØYÝÚ!›Ðï1ÌòÖ‚èGÄê€ÙÉÞ 3”xL Ð^Éc¯·ä¯ÈX„®CL–¨X뉹GîŒ}[f[™ ‚ŽJ†¹Î±Pë0t ·éÕ‡½œ+[²®Þ†X®G $¶ußVÕsºÜ[(,íÜ^‹\žE‰!T“ÜŒùnÆÔÆÝ(à…MhÍ_¤KqÄZàЄ>í’ÝítD“·xÏbÇåšr°‰sn¶…óDËNåç@mD=&^âÞXˬoŒõ™PSöjMÎÜ®d®i„>ïi½Z£O(Á™¤Ÿ¹W¼"ŽHgu¢™–ýy šåñ5PUF̾7ýƘ|õòíË'¤•¾í—1H™‚þ¨ÿòï¸0ÃE%b
+èù¨UÇ‘fдB;Ü#¶7¸'l/ØpÞÿäúç·?óU$_Eìá謦·¸y²nço¢$Àþ%‹æ×»©ÜUÌŸ´&Èrþ—óh~¯åî+뙫ølûããr.ÿ#N ˆ×õjN¹&‘â+ÛÓ|ôSq–ÎoxÍeNc¥’w)Ê#äœî¶œÿKç÷Zn_ºh~Í3ýåq¹G,ý9Ö'½~+Aüôñð—ê?ýt‘‹[¸õ–7gªw\XŽÃŸgEà­¯·—=Ü#¬.-´ÑÇʱEVâlW¥D]àÉfá¥å>ð7›ñvÇ››ëiÁF;{ûÌ å R—s6â8—íq•`D¼¹`«å6öp´• ßž3ȨúØYeA å>Â7›ñvG›ëiÁÆ/{û'„ßl˜?­>üJÈð%ÃD;Àù1˜÷r!Á¯"<Úˆ ÇBi2±]IVVœ€¯9Aßx®&ôÜIžãréFrg`ªAÚXdË(9 aÍõÍênK¼»ãÎÖËnû·R}oR¶cÙÇ•²Ì}0Š2·2u8…ŒC·DÙüq ¾Þl™L½Ü1Ñ»a;y“Ø^Ó–*e{m†¾è@}û¼ÛRnï¸ñÑç ©ßùþÿÁ÷ÏÐz¿gÅ•Ÿ«9Ÿ»ùiÀÏjÀOWõæ&Ÿ*q&Ö}"^ôÉuÛJó[î³â¹$Êõà3Áž¤äÕËÆ»ä¿u‘ê¥[ßÝd«öÿ›¤|š“OXp›5s ú“Œ»è¤›7¹}ËDÖ`—Œ¸¹â ê“Ü»1êGöËe·®ãˆ¢_pÿáL HAîQ?ª_ÉH¦ÃI –¦® &e0,þ}Ö®>|[@ dyŠÝ]ÕU»ví~¸ñ—ºüóö«äüoñÿÙÿý‹ã·¾ùŸõͯ|ù|2óŸªÿã©õÉ¡õÝî³7Ïž_\~qz"ïÇWË0•åÙWç—Ë“ç_Åüòøòòpq~7Èo¯~:<]~ÏÂ8ÚŠ$³QBˆ$4e®ÃJÓôÑRh´áÒP
+˳oÇg__^œ~dç½ÃÇ«³Ó“ë“ã³Óó¿¼8}ûçÃÕtÀ¶Ïß¿?óÕååéÇÃÙËÃŇ“ËWWï¾öâüøû³Ã¿®WÇÇq¿øøÓû‹Ë;ß9ðfŸòå‡Ó·‡ÏUäg7–'O—×Û}ÆÒðæÅùÛéÓ¿íÍç$ä|³<ù+½¿XýÝá)å}ÎÏëŸõÇa÷aþúÝﶰüÈOôŒ°æŠ?ñÇ?1ý¼Øòõò÷„å-èxýôè
+N×½®­×ê$¬m ÓúX­ù«·¬–Á¤,1tÉ(
+¤±¤Å¦5-âj”…
+çóZ¥$¦õ^à¾v†¨ ̵áj"iSž¨I÷4fú æRÉ÷®+ç¡v7¥ÚªÖÔ§!0q–Gá)fA/Åìm÷Fb–i¨¥Àz>KK[Ì2TKËí&ÐO;íH 2¡l"«th÷áúqÑ?Z*n(+æ!.K’I»‰‡~ó½ZdjZGšY¯Un*…ï¢-VÔÈ]jYÙÜ2ÊX
+ýç_}ÄÈŽ¢Ëç˜z¹Ð…¹ÎÐ3 Í÷HB:ýLsµˆ.oY”·â¤ÄPÒw-Mzð-|í„ì¦"Ô·¹'Œì†Ô„3]jXš›Æ‚Pœ‘Q¯ê&Ø1‰e(I¡rI³ZW‘6µâ»ªä6&K¡Å
+'èRV=2²®¶ÀBy8³­MJtÏá!q(GM"+xm3edjˆ$ *t4Š
+æ2F)ÃÝœ™pÖe8Eƒ±@ðª]ö¤qK+8ô–®ýÊ‹š«¨38Þ<ÝDÉ:ƒ’Yλ¦’zVDÚB´]y-`E›æÔÂ`½OÑ{õêÈ67U³â¦>‚LÏOCê÷ð¼5:cOg¶¤B#yjˆ>®HIi„²G¡‡$*Ñ…Cni^bÖ“žaÝ4§Òàc’³’éÐœü¦ehß#ÞŸLNL\zðþl À'í̤ªŸÄAʆó2ØóšB”%Aì"¨[„rr:k9òdÚDþ'çûÌÔ2ØSÓ¨à;´ê]Á}ØŸi,ÃÌL>1àºÆ3¦$Ô> HÆw€tÕŒ*Õ6®`‹ÁõšÔJJ§"28r•7w,ÔcÚÕýX¹óœ¤æ–ÁË%Ö*‰ÑN5äÂl÷=Fµ™é½#P#;4W ²s‰Äi: °[±è[ÐÀ&?’*?
+Ê(…ÕÙx2ŒAØĹ‚
+î"}½ü­ËqAòL½¤#Ò£Ž}Ì÷¥¨¦7%‹Í×”Ñ<4x¶»¡If;OEï7Ž%Õ¾&ºÄ·ÉɺŽ*õ0È!“\©vWßi Ž#ýnÒ ó;¡iõ=4†ôÍ I@âõ¦#/&àg"T€k íùLb^%µé™’Tk4HßFõ Ræ]êµ›¦ÌêÙð7çö²ä}—ÕSi¬w‘v|èC~Ãòü°û I5Ä.U,÷îŽ)αÿ—ŽNŸ¼V2¥(7,–&¢oeh¼Ý3ŸŸ”O’™n!ˆáKèïÁª1à+&˜‹hIó‹p\£ÒAIlälh݇¸G­ÈÉÅ;툂,®vˆ2«_K˜r™/CŠYS‹BìdªzïdU,o’BÌˈÛEéî9!B¨$’ë,½èO:‹ÉÙg‹ L´gQ8U%¡*…9ß%@£RFK#DxY¯Õ8뙫GÛšS_’Jur1m¢
+†Þ†_±že…t…Ë·ä¹Ã÷ÆƬñP»rìOÍet§Þà/vH¸‹2”´üYZÔü9ÍP`›\ýÆ’¦AeEν”²÷4LKá¯Ñ!K¼l%d ‡“jBöXm»ù ¶MÞ °0¦ƒ m4Ok«¹Æ\YŠu‚R[ÌEE QˆsÝhS‰9¯‹ ÆÄÒ”`v oÓ,¨S…s—µºõ¾ú8шö‘·ô}Ñr»»¢XWE£^YG˜þ‡î2ב¬Ê¢èÄ?< ZÜy
+Áî t×ñlyÊ[3Ø
+¢ý2šÛP¦sª§1~òMvÈuùœ®j£Û2Êó\µOÿ¨±VVô™½uaº›­kžÑÙ\²œÀ“‘øØfŽjJaì6ê„9YJvo¼‹ê0Ÿ-™:ü¨ãz#²¨œ’óŽclÓ2\Š¤ut׌ŒØ`D×c£§i,@æQö]º÷ØõÌq®Ç©ÄWY7»–º“ª¦O#{'¡€77o?&IÁ.±l†n#s­ölCcI%¬ÚÃâÈKU£âN9ŒQç´º–qFÅë6çû)=|{äŒmû¦
++R)’\ÑnÚ½9Î2d;((ŽÊ WŒ¥aü™çq@5&Š‘…5ã8yP×
+63+¤úݸ\­#uÓ¼ŒÃ^"|üeª³K2v<e;L“*¤ÂÚ`îS€p(ëN†<Eö]S=p&³Ö„”)Ù¬P…³òLe¸‡n¼§Û©Çëf*S„@¹â2† \ºÉË«ˆ,Õ\Ï×Eî–¬ÁN¤LL™Œ ®ƒÂxÙÊÌ@VÄD¥5²¨£²Ȩ«”ñR <’fämk³nFòÇ:1~@”ô •ÀS„Eôùp‘z™´-—RÑ©5:#9â+!ÖPà×TIï²})E<Å,ÕªN‘âØ‹bÕ£çÄx•`‹«+ÒEíUƒ¦Â€ö[èI«„;TWÕße«‹ƒ@ÓU/çp´,S¹7
+!ã€Ì%*ĺÏYÖYw8Ò3W(9Ó¢µ`iYqj$o®©~)«PÈ©
+±‚µÕö½Èâ;jS
+å DT6n4EPaÙî)ëVÄN h(¤ºÙ€!ï·Ž†ëâ¾*PÚ6×#¢+ÛÂ×P±>ƒ¢T #I…Ó¨t1Z «X©3Ũ;ô µÆÆÍ>P
+†¯%É&*UZ…l€.Ñ’zKˆ7WŠûúâbuû\ÇU.OV3PEVv`ôiUSCÈHšÛå|O Mke¶,ïT
+ ¨ð§€K*AØA‹«Iň‡Ø¡óÕmúàþ0’1Ư{ˆ晕±ö4„@+
+¡Ï%1Q¬©%¶P¶¦L%ß·î9Ñ‹‘ÁgÅ–i—a`×æK–ÒxÚʉˆTž5ªù]}
+Á,q· A\ˆ±d »‰~ÞÁdIÓ0 ®qA…®®ÑiÇ-·ú^ñ¶àêÇJ¡¼ôÖ±”å tmY­„*h·[è¾s….ÃÆ©º—ÒÚ²ÑWuŸù¶<¥š"¡oµøà÷©Ø±\¶pƒvpÅã#X\#ªfÓõ9O¥
+ r i\Ñ•|/‚¿£ê¤ÁW—‘e¾"-¤ó ¶®ŒbJÃW*þœÿ²¿¼¤Áˆç—Ð>Ú±JR½Ø—Ý€;ÍW;C™@Õ/ÀFW9 nó… *fÍG$ –ȲR.7Êuaç„šîRëè:r&˜pUᤪq •í«Š6t‚­®Ç ªEj:/YÏÖwÅj;™>.„“\Ê©F`
+-·w5¶Ñѹ÷FJ­¾ÝºR8Ø-6ÖüN)Ttsòvæ&ͧ:õ‹f XWoܪO¾w§Å§}>tu›_Ĉšß#FB$%^1݈;‰È8Áž‰6´³VØ¡²î®oÙÅ~H”âzÈm9%‹%vC°È]õ¡Kã+¨¨Çw±ÁÓÅ6·8ƒ\DQ¤°…êÏGðs/·G L„Ø…Ò×÷ÐNÍáY÷D7Ìf„‰~gAÇN˜hT ¿Y!Tê<›|hžTkM‘a™äø&ú¬}a?N)JÂÞ©03þSŸè0ø_¨ñ°Ê9®ìÐzê&n¡KG^Ø3¸ÒU£úž=Ž¨qfÒAíxkÑz\líò$èÝ; ¬(ßÞzòFz¥ù^ÑCÐn˜¸t›8uBÓ0ßÀǤ¥Ñ&8Ð] 6]zCŽ ûÕv®è"ÊQý/È´è’pª°™#+ÐF|ƒú;ô£ŠVQ—'*þ¬#ÒÐ9Q{WK›*;ÊŤà§Ï­ ¿ ”r0b4˜­ÈD£ a2*‰L¢(£ØŽèOcP)+VG+/jø<ld¸“ÊøïÁôÞHÊ‚)ˆJRúò°ÖÚÂj¹‡†Äô’}›¸LL?¬¿tCc¢ØƒîRE+Øź¶èè©áóKu!©¥á®J.a¨+äßnð|Rä0–Ò$æmuСY¯¾L²"Ûb=f¨s¨Ì(]R ^eX&k`çq?èÁxg¼V² Õž —Vµ>¬ÛI‹ÚȼØ2ÝFÎ(tæ@4t¿?—a@ûÛdÔ± fØ]ÞãþäÓvî†äWJaí÷¤‰UTæ–•_ ï
+^7ÁðôSj7øI±VRr©Û»ëúVAï2'É°vô]MŸIƒµ›ªjK ²_³ò
+h’PÏ$€ÿ[¤’WXŸPÞ=c\¥‰e {®^Ä;ÞO¡–Å|KñôÖ–…×Y8Ú€YÌF¢w4"=Z“Òñ$Œ- ®¨D-„HvOú&ï‘#KPdh–Å:¬O,Ã6ñZŽ©/øØ/ÒË$ÕZ†¼s¦EN~΃^ºâÉèPtÈìüxàjßвÐÕ¿ Ê€væO9DÅ4û@“3ÖÑ ÅõgY²)<0Ã5O VȧßZó±ˆèȧŅ’d(„‡¹˜ý1û¿xcÖ6*ï2l=³<z¢œƒ
+H‰ìWkSÛº½ ÿA_Îd¦8~)vÚááÑÃá•!=\˜žãÄJâƒcgd
+¿þnI~ÈÁ)&-´.SH¤¥­­­­µ×n_x‘7ô z×h÷œÈx~L(zßhî¸á ALÂÎÆãˆÄ-¤!µSÌ»††ÚÅútÁ¡ï'QL8¤Š3Ÿ·P»ï'/€EäÔ™qdÉ(:ön ÚÉ6hòbnNçûøg4ƒƒ{Þ(öÂÀ¡÷è=jŸ†ñ9…Ô%®p¥†>Jm Ó‡A乤ŶÌ>¶aÞ &¨¹çEsß¹_¤-Vâ{Øÿ=œó¼¡"ñsŸ´ôóå=|ù >ü‹4ÅFw0q‚>Q‘ ã—ç †»tÁY2FP#sˆ›eÛ|@üèC"ãи"ò=Ÿ.ŽˆÑßzIà¿—67VJU:aÇPËç(PXXâ¿|”Ýp6“À-N³;u¼à§YK‚5ÅBÍräïˆìß’àÌuùų-Š-¹ìŠ1 ÜuoÙÚü; ½]4¡Žë‘ F¶ðt«Ë`Âbö»7µ½žä܇Ÿž#:䈵ÑùenXS…«¹á·S_cX~O®ˆg½€F{ÿ+%Ì>Á×6ÚGAx¤;C `Vëb(Ui¹lï€Û·YaZÅ>¶0ˆÑÍËIz7 ÜÄ‹WYºÑbñ¶4‹ú„f9Kâ—ˆÅÂy™¯P.¤²ªè²¿)é’qI-\6]Ö:µr©“DJ’¬êM«ó¥ÛJålQüêóo¢Ðÿ‚šN×j©[•úµÒ}±Òí‘[׎ã7¥uSm·òúgŠÂGŠnå<ýnñö ÿÍ#Tmõ›VÎ*ÎÀŒ4U×™D× ¬¨ªªw;ØîvÔÒÝÂÖÚ)V4:ÉÃÃ=:q¢›uµEĉ§,øyôÍPC¤ÚçÄXŸC4’ègh.q¸´‘ëƒûëŠEÉäjñ°²p„ã(n ¶8„iþÄsöÝÌ
+)í§`ƒ«1V dáJÍõ kS[Ä4Gf1å5ÒxÁîòsæ!3J{§ÊÛ[r<l¾­ª˜9ÎV³ÝMhÉ*Œ™ã0ßG‡‘Pü(ü¶
+”–@Í÷dç<a¸ÚÆŽ!u,~I8‘n:Cê¸8®&9WdeùÎ$ V+1†Hq÷aÖ ,߇ðP:EŽÃåÔäÇÕKOU/%ÎNkæ©Ç6ÖÒ”‘fz_JDŽ5™Ug{ã"MùÿŽEœ?¡Ž*ÇgÀŽðB€™fg7Kîš)Xr×Ì¡yDS,9¤¹ V)¢Fæ‚-ùkä.Ø%³ÝÅ»ãÐÁÙ“6d°•ÞW´ŠW†¥—ͳÝ拈Y¹–€ö† ÇŸ¿C‚ÁQÆéR³ ¿{Hƒ^¯Q6ΨL`<%õ§¬,À ; ^ƒ ÜÜ?iC‚¦ë³Õ›2SƒdúbŽcá-ºð&+‚OZ^¾’mÔ<}ŠJ«dDü|–,~AóËöA|;n˜Ð•–hÁ%ÍÔ¹çMF"W^ñ3€TëµbwáŸù×-Ã,@Ùöc˜]†u˜5ë1Ì*Ã0ƒU8×Y€=Fà2ÂXâ½Y†éK¼7Ê0m‰÷z¦.ñ^[€™Õ›âì¾zÔ›LãâÆ
+Y´]ð좰„‚…+@Y’-H©*W×d©”%%-c‹%¥“R”
+ÿ ­øÀ×t*–ô{¹$Í”g¡Ee‘Ú8 9Ëg2A{ âSVÙæ çh0…÷
+IJw×F|,]õi}o;Ìèà ó @Z#é¦ä>傱6•J9I¯ÛNÅ)È7¦/j"Ÿ(
+>*pÌ:¦ÐEÆÄA,ô¨é˜«ÊËýŒ×ÏbÓ¢¦ba¤¸–žx1XÜU,{D‘¥ " ¤\…©÷ëm°Æd¹ZäJ¥/oßѱ屾ЦŽîÏ‘5 @f‘¥¿PZ-e±ÑÙÇúö=}^ûPF‚~)ø&ò™Ÿˆ±ÕÑa~À-<FŽn¿ŸŽÉ TjÞ¯ËRE.•—î»;c¢\*.ø'é«–£G¨Tþ•Ù„ýèKD¶æîÇs¬g.ô1£ºÃÕ_?Ì몱h[¬ga˜$!4(† ‚S‰șB1Xïä‡ 9kþ"]zqáئc§¼YžÁ!5##¸üÍ…Ô(úú_Ou:Bz›ŽÎ½ñ¶óˆJõ8=
+CŽKÇqɦåbêÁ (Á„ ’ç$7U˜0ÈsSÇw0È€Eæ—ó ”ϧ:‰9–Þ`w²5At+GñˆV©¥aíáòßRû1Ÿ}4<„°-2rlÌù%Úkµùµ`h
+ ÄæÂH
+¤ljÑ9nAo1ı:~3ì~Œ×âRZqðDõ}Ó±‹š‘ gM Ю<ä]j¡ó»ÙšÝKC6?‰4|®PZ×A~µ¯pØ÷R)-Yaš:sh,e+8ËÌ
+ ¨°"añN“2 ôê:eÀ4EµèÙ]t}>ð¸ü°¼>ÈØPÄ£|Te@`:ä,„;†
+àà - []'pÛ¢3ÌÖn—Uïl8\Y*¥ÎÜ ïÖÇU#ºRוŸ÷\R·ó‰0åå?¨ÖÙ]µ‚²K¬…âf‹)4œõŽƒlÙYéßÚõ:pF¼Ô°û<1YêÍ -—{Á.Ûã\^À„d Ù^Â]`0¸µ‘ÒñžK¼qázNùÈ@Dgé«"ÖMa¼+GF6 Ð}hýT-@ ¢Ç_äÃ{3ÃØ/PÙ€+&>õ›«ðVˈTÀ…  Å}OFÈQøöÎÞVÛÛÞÍ€m-'¬1šüà >µˆöEqö€\.”6ˆÖ*ƒf§S)µ0÷†”^]lÜ;¾z±s„>쟷/uët>­MŒçgíçû»;M‚rl³üþõIy£Vúò\zUë~Ú9¯[Ž*·O
+ç•-Q’6òyÖúÖšìç7ëGŸsÖ÷MVgo
+‡Ï¶êGÝç–/tf7&¯ßuëÇ4É‹—j+—Û™<0ÕÕþöäV{«&ÿuj·¾]7¤¿ö•9í2¥3°§{/ËN»%m^5¾é;W϶ZãüÙh¥²M¹:–/ß}ü¤ ›¹Ëd£Q¹ÚuýxÖ¾®×Xn¾×ÚßrÚ»§ÚøÙ–›¬öWXI­ñõ•ÜÐëú‡Ú¸1µ›Sù/q)_·[ªØý^?~µsåé—Yóóä3…_Ûß[­ó¼qPù¶© 6 χHsžmU¿íî©'jéÝns*}9:V¶ŠÛ{·û_÷êÍ÷í&vþ|yy¶1=RU4ã¿ÈÞɸ;õ,‹ùC$[dók|>ÓúÖ«kï“£tÛß¹ÿÔΦÅg[å£Ëëºb¨;ó½çG‡òüÓ "ˇl\T,µ#îÍjb Qm±KH›¼ƒå«b^«‘¿¹¯Îõd±-|^„QŒŠŒ‚(*Ý[ÔXbÁ^£÷vQ@cf¾9s~?<À^{•wu9üüK—SÞ``&mØê[Ð)‚œç]m5Àž_ò=Æ•fåý€ä[Ó^"2võÛôŽ
+ˆ "qÔø%EïdZã¯o|[-br9 Ž0³H'’¡ûA‘é´ˆ(™yêʧâ“ÓÕû
+çËOÓµápsÄÇÉycU™S‘ÏBíN‰ydÄgJûÞPð#^S@tø¤´ç¾ºGÖîØ›#pädß„°ÅŽS¡”‡E O’#ÍkC"Å7ºŸ{ÀcEîá`.:7Ó¼žƒO^0úZ%K!ÍŠT
+#T
+§Iƒ½eÏÏp‡ÏTä«s/=4Å7ӭ˜–é¬\à[š*dé5'2tÀ+gÌi놃°(Ñ_ø)ÁR(£‹è“©—"5xšÂ·odøOÂÌD>¨±± ¢G†Ýƒ
+IxD ?aª'A¢w‹–åݳLåÓæ$"ÚT‘J¦¹"RÄA 3±ÁP)¶š+7à¿$bfIA,!º©ü(}šüêW)æmt' Ï‘ JDŽÔÃ(îæÉš AáiJá\¼ï¨'®ý/ñà†«|‚eñFf œîáH!«¬OÄÌ”œ}¢ƒðÔ–ôß»«ŒÞLŽXèý' Pù Œ_×L«¬Py
+:
+¶ý¬Rb$W?C&Vôý.MÝ]@VFnÇ‘p‡{qvR*Hµª
+*KÌFžÙ§\5,mB: ˜ÖwhìpXâ{3ÞW‡¢”Ÿ;…£4ðškaƨºŒ›
+^Ÿ 7fî£IbN¬ALp/IÃ]_<MTÿVI?cØÛôÕa( „–û|6%—€‹çNæ\ÒtɱF(xwôê¤ò½,eaöW¨r¢¼2ŒÄòµV#]@Ô”NÎÿÌ´;ºé ’ý}ïó¯ˆZ~ö}’¬<$gßZž/þCy•®'ˆ%Ñ'È;»FÜpWP@£¢F¨(²t÷Ìyö)ÓÓó'_êVÝ[uêÔ)J/¼nŠ"Uf `ÆL‚fèyã`„–ÝîÌ0?PäÕa–t·'!SÅÞõ×ú¸ú ˜·€íñdóq
+,ta»V€¢·í?}éIæÝz)5¤î!RÉ6š0_Ž³âôR¨%¥½z­èØùññš1]¾+Ø‹I>ø^xÝí]â~0˜‡–ëDgè?ºÚõHXÛkí
+=ùF€Åé±vèU2?ñÒ÷ÀøÚÏ,¥TLçÒ>jLœ\3,{èúàêÀ–>_6|ôc×¼ ƒ\+ÚóÀæajnBõVÉûN××ÕÔµ}œýÅY謓ĢŸÞw"šÌì·d£â@bä1l†ñ3ŠÏô‹Û"lñk¹ü´8âJ kU0#×Ð6~ð¡©p0”ËhÅ ¨¢ÆŽè%j)‚W÷x©¢zݽx–AðweÕ£3¬2JSD&Öî[D‘øÅ6²/º “ÌÝ™ä¡;2Åedˆ{ÝIV€È»4Ô`úF(Ä:x?
+d¾ûÍEÔº)t#)˜¤Œÿñuê ìOuù øa/Ùž3)G hbF&Ðd•î¨–(ÅMµ ‰7ÕãÙ8ªX‚;©1Å%BæG]DÊ/³Wv`êÌkR²W ÈSÞ‚õÆð °iñÅ [—´ËKA&ÅBÕÓŽØm†Ðš$©KKäîaÌ5¶WÔ
+-Ü
+Þñˆ>+ƒõÖ Û¢%JÇ £½#VJ{órk÷|±£ø)΃Ía“yÔh*7h~Çæ û[੸>øý¸fî°«º£
+ÄT#zí& p3µò¼¹RßÃAø(²eÛkœÚùh*¯ˆÔâ};‰Mï˜/±Qïcç0L‡þüÕ72Š
+Åa¡L­
+‰Y¹Õ‡Í¢–žÅÜš,aîwé
+Þ9VÏq[!DÊÝb<{ø,‹Ùl\_§k%"ûB—Èä¿"T7’‡®‹D¡‡â*Mp5ò«Oùõñ”ßL´ܬ™¶vA0[ΰîøŸÊƒï«ˆ‰V"‰¼(+¤§¢"ëÉl‰Âׯ)Š„ßMWÎÿšÈ eV=™d³Ù oÉÆGCñßÂÉ*HðÊJ@ÞHyÓ]0žwÊJ9™…øÐÐDysÇÃ+{.Wq‚äRá̃
+HüU1§h¼ÀÛŸQÇIœ5 âÊ4=.Ê{I7ž9Å”““SNHKÞ#Q7Ëièìà—„l•?›"/|Ëä™ýCèš’!ª’@hÆJa5^w¬Ò×Ä%¾?ªÃÊ“Ý@ÎÕtrtì;¬nœ}µHÇYî6XÉÉ"ÑBË‹–GVæ‘® o‘ +’i%èt(…þ8Ôeçö!FøËøqè6R¡Þ†ÿw¨Ó©ëšÈ<"+ˆò‡ ©€e„Sª¢‹Æc]̓ËÉü;¶à´ÅdAsèÂz–À,[B6D„DV?ntòW—D©ÃU$á/½9AÚn›Æ
+Jb`š
+TªEQÒa6)€]O+¼×¨ÄBÜ?ìZÞê2–Å¢¶ û¹•íJ
+‚Ø=,ô(Çl~”ñêé4¤d’ÛœQÞ–§VÔ±µsù×Y“àx"Ù§@Ù…?퀗D+KÌð «!Ó¶‚—ŠûÕÍ,°‹”«(ª °—º5ØOž6‚²â%¥Ø†守¬¦ì@“©_ íY:.½æ¤¨rí+‡©—(È6ÑQÇ‘
+vƒ•Þ Ò-­£‚¤Œyô™Ø"Ø·‚ôŸ†JbPêüf/ÜëgÔ¶øtÌž6‹+šcŒ½®â~ì}+‘[ýÅ—Š†YñÀ®ˆ!Ff¹ã\…÷扣ÅëqÐq6gWÛÃÓ°cÙ©®‚ò_îwB…k*m^Œß¬%/³òñ*E©$¶!CW†´s-²éÆW0+Éc,D™cX8«\IpüPŠìï…F*p‚´¤%`“ÇóyâZw9Ë'.]Þô˜_ Aæ1~6ÝuìÉ1P·hÃËÑ5g/yF”6o§w
+ –Æ}Œ£r £?ŒÍ0KV™Tm!<ó «áÇæ̤¼`áô
+ì«FÁÁvÃÛïÅÅ”ÝhÃ5´´AØÒðÁØDI1ò¾!xx¶cb»£Ò(ÿWŒ.OƒêÑP(É:ÈÇ’ä'bäGáô.(Ÿ>ûlp@}ûÙ#a[³¥¤y™¥9–§ŠŽ
+âÊFUT
+X𠀃¨–×?[VfŒ éÿè0kö°wö1¼ò*;Ù$ðM®÷µ9ð+›ŠÒ0°¦dƬª+MD]Có¼ Û=JUÔµGÄj2Zù«-ÃAºþÜè—ãYçÄß<;cT*‘ŽP9Š$“À•çÜN€'c"*M£<ÈàŸí±”<2Ò³ÚÿÝÙ &P·Æ«ðÎé/·b1ç‹2å9ËÜYcWf_©§â’Nøx=ìá”ç|aÿe¸FÚ0+ótp@ÿ0øÚbEŽ)Kpl¦¥5(sÜ€Qž°F!Ü9o»‚ÜgV‚´†±TrxÊ­ìü_D5Z¦•c±y6R~ï«asÜ*¿ŸþâÌ9o˃j«•IÖ%ôÊ®¡br‘•Þ–·äm¨ •¶r\ùD%ò½\ühè?©†»xi(Wj›lc×¼éèÚ†V*T觶D Þ,‘[_ßá!BÅ]žtÇY"¼e¡i±‰¡Cn@„;jD„ìPämaäWé“D5ÐK
+tàáj»DéyQŒW2ñLjœÚó‘E]H¿Çɵi%›s¦êðHR±°,‡ÅîãýS3Íü{´.ŒoÒdLÖÆõÑ°^(V±`9Íë,qw/Tû¯Â¹«9·³n± PHÒB’p‹¼M 47Ì“L]~£fÂÎK®½1•ùÙÜVš’ ÷ÎBÅáUWóRÛEoA¦‘È|ß}Á¯ ®ÕÎI'Ò´3é¡IéY2ךE‘¤3w·íðXhMÒûÔð Ió„_1„&}‰M.*4I“ø†4¹õӾњ4˜„òÔ'ÚSW}–pxn^E®ò•¬Wžî1¤©7/
+ÒÑŒ¬oº“úrBÛ òÞØã|ˆ$m4©!6¼Ô¸8\¨¤°—ç9}‡•<9Û
+mà2«þQüƒ‹ iB¸(¥6YÑI_|> i2¹Ÿ‹&©YÉí\šnù!†ôžN¥ëî’tVZôq¤M‡Ç÷ßMоæ‰)¸Ù2}4i¯(~»÷mièŽËMR˜—óR
+–Æ÷ÏhÒÄhDÖ³dIꪦÝý=õŒ"uxÈútZÇøšºñìøNGJ“ðâ MÚ Ë>ÆŸ©¤5ÀòM¸¤“Ž"~K€KÝDAïÃdW?#çÉv:SHƒR‡¸šŒêÓ
+Ò¦Eký¶?Æ4†4u“Þ­ë3iì§Y•T©1‹¯­ïÜý—Ôí#Iw¾8–ô‰ÄI©¢ü‰9˜\(_ë©Ét?Æ?’ôÍÇbIÛÞb©’:<—¾~4È·'1&mÇ=¯õû|M*>ºP¤P“Ú·yÃ%cüñL¾—žjhÒNña>ëM§HÒé󮥒*ýåÒׯQr]ÇN2äp4i÷KÜ?g3q )dQiç­ °ä„ éè•|`÷HÒL7â"c6÷m½4‡yz®“.ãË¥ ŒÛtP%¥|EóÜÓ6¹æÊ
+iØ$…,
+-ÜöëÛý‚d%‚· “Ê¥ÅSbB4ÒÊ(Ö:°^‹d´ÑKUjET_!iU¾Â¦+¥‘–bO‹†ÅDWë4qw®ú¤’*,:­[’è%¯’RI*3‚Q¿„…$¿rz§)åzQKx]³:ôù¡ç©óC5_½3!‡µŽz±ýκ%› ÏÁ´^(?”Þš³æÀªçúbÒk*›u+Øe.neŠßºþ ìš(¾´r¬5Cû ¼uKÏ|Lj]ÚË”w¼ÂZÛ!¾Dá­ËçÍiµD,uóìZ-›˜Õ™¦·™šuãûÎZÖ¾†Xc8ÝÄ\¹‹ˆ½.<Ê®©\-´“°ÖwÏò–À['µ\Áˆ¾ðÌÒn¬õK~›XëîªôLëEÄö»zq†[ Ô»Kb­T¢ø1ÏŠ_Ú¸Õ^ÂÛš±Ö‡rgÉ`­TÉÃG¬LPî@cM6ɇBÐð9翳XCÃ(éÖj4o½•Íá¢,›öH„ûç/°*¹ö}TuýYx8Å*êÏÑú<©IOE
+üÿ3»Y’Ý°³Ù`¿ìããæòÎÌ;ïÀ[:äÒá&b¯óþɆ;v°jM[àï÷‡À_fïÏÑ]Ø)í¾,oòoº}Ifè–^:ˆÓƒ ¹s.äN÷‡ç¨SX"NDñ¹ÒKuJVÞ/ÌéˆwºßÛ2LÎíû¯µçtdY™Ð)UÿÓR^¢þƒL½ÿ§Ù!½^y·ÀGEÔ)UÿˆS¸Aý_…N!!×KÔ)
+¬Þ$ꬰ¯„ìw–÷ó99òRÜAÜœ³N~5òÅÚy¹¥ÆÝÀÁ©'»N=W®H=%”zŒ¸Öu6_x
+Ó¥žÇ÷sÃD“†f ?5s±@×’a&©¯G0i$WO²Ùy"OKÀêLÃÅ«iQm¡»òäŒÅ£#é $\ЭJg˜z,o©…Eœæ*ød*…E‚úyÝIhÊïäWë=iÄS©eL\(Iãò™?06T1I’äUÝ«»~ù‹ˆ‰Œ–0.º_Bª¸y÷²v¸.*ó Õq+xx²c´Ò]¬ îS1<Æ lùT.ªé¿6•µÁóV¼Ü7}DÑÙùtçJ*ÙØ­:í/!6†¹ :ô¥{|Ú2ŠNe¬eÕ‘F5…\6"¤ —ø2Uæê/ëäõPbX@+ˆ¶Ü “˜±ç!ˆí÷v‘õo£ðû8¡9H¤ÏŸ¹Ôüö{d‹ÁoŠ—ó»<‰âkO“ô¼›ÀÁþTŠ(Á”BÃHé¦Zvf’ 2€È–dä.¦z-ë@3$
+¿ôPÃoH˜
+îJY ­Næxó6½ðB1-Hé˨”†>H¡'kˆi‚r;*¥“`çë1©ŽG­„9ìªýwv¤#¸[,Ñ"lG”ðfórxšU§ßçípùûétUŸÏÚѽ‡%D4 ÚéÐ;âÒÛ„
+ö ›ª‹Ü¶¨8Éð©I#ãäúNŽ<>À/†u ÿSÇ ÃŒCòü/À
+H‰¬Wy_ÚLýù,a K´O¡
+–]‘*(n(¯X
+UÈ÷g&ÛL˜™LˆÿLýQ8w=÷žÛ(•oíùÔ¯YS©.> ßBõäEñ¨_Ö”Ú‡^W_bZV©gRð¯«ôåbõâ©¢vž5IVϾÉØSY'^®•Ê¬,ê…h!:_WçËyJ«Ý–ÎÔ\ñª
+ÑŸÝsñqÄÍØþ$Æsø³Í¨ä4Ëéd7ƒìtâð;B’ÍžrJ°#öÈendƒºÎ>«|ë½S/ ³çúv«éB™÷þ4”=6C™_…ÇPæ¬Ô [‡MBéÅò> cðÓV(ÝÔás“-œ—¢vAc.—Í¿¦Û0 ðeF9|_lõ¼¼Å·X¤?;{ËæôbX+—`õ­,çz?Þãjîx¼ÛnÂ1`ïõȶIM‚c'ÜÜîž²¼_£Ê‘ö§ï2Šf22û=®ÿÝÑ'·L£z¸µÌ3ª­×ßWäõŠÅú=ËŸMÿÇ2ú‚-^E1£?~×®0£/±Xrß2«GsG&\ ®l¢ov¤í„Ñä„i¤÷X#T4‹ÇZR3Œ~DoÚ#ÃèäÁeT’‰ëE¶ÑvúiÄ4Ÿ'þ[Ð…@'³nÇ™F·ú°b <d®quÌÎK–è/³ÉúíËQ}Æ÷woïüïX íÍcs|á‰ømaö)&k æû_è{Ä}J¼9Ó¾,Z»-{HÙëßíP̙禺0Ù”@
+Æ `èÖ”€8Uké–ê³\Ñl+`[Ìûž0Nê"@.¹Ô¯Û% IPÿÞ5Èmï¯tÆÅg9Uî÷¾"O?SD»Âê»=ô'a«'v ¨óå|ÄÏ““%ã~áé«›ÀñI2Œ0ãŠðÐøÀ½;áòE<é9ÿ.»rß©zöîÀ<‘³&‰P`,Ö´MrÖPd³È¬iBËmÇMP}ú ™Œ¡î ‹åIú“/I]£jb¸b5hÞB,eŠÆƒl?#´Ô¯Y“œ0û7­ßÐ2~hÖ-6¿ÌÆX(ÈrûòÍ;ª˜¥ÏQ'ƒš»ÈwP­ÖäJ`^Iín “ä{ej°ïSç ¡¥›–01Ó­Ðiêu†4ÑÍæ@sÌØò)¿vD„™»™¶qÅ|àL~nù:z+Ygï¬)|ÌYs3fòs¸—»äLÏÀ÷ʆ_ÚF«Ý–Î\AÚz,]y:î¥ÍÇL‡‚gÌÔzÖ²ú±Cï®î¸~;$ÚÇ “ýʉ6Sv‚] (¾&0¥DÖ÷¥í)»\YdUwõc¬xì$žn%’5 Ñ’…R%¡ /†^ÎÓ©R˜é_J·˜éÔ"ÂrêÄ.Pýìio³roçÆ]dÖ¯P]÷øõ“dÁ
+nmùy@3`:Ùóº1„;k›OÕ–-eÍä `Þ/ž±üs4ÞùªŒ‘³6PÎD;(cÆ:µM«'غ¿í&î@ª4 =v˜ÀØ/;&ÙIª@,tˆ{^¸ 
+ÔÜE¾C:Û‘S¼@*XÔ;èiwcÌU^ˆ–6E~Í]iÁ¦%™˜éÖ;1{
+( ÂÜ@Âyw2äm#“b¹P½þTI±ÜuIJ5ÇÜÄÄr¡:T"Í É®v ^wÉCƒ2
+Ae@tàÏ”$ NÚ«ú‚8™ Þz ád¿$ªçJÊ->”c«‹¼' IC /|º–0·ëq/çpŸGC ½Rn‚Ï<
+á"!v¿¸h8
+BC«Ù'kd
+²28 IC9Ƥád-JCŽ‡ùöw/R­yßpOŠô¡^¯ç èPrb[ÌGðFöŒ<Ÿ+?W‚•1ó7OÉ4úR¥Ô‰.m怶CM®”Ná#c’I¸F†9aÔ×|˜:ü%ð9<Þy\¯l¡³6 û’9@[²„
+8@dQÄe@q@.z¹£‚¼ÿíÎÖ ÝIGøÓßL¬åTÕ©Sû¸+A-SNðžw%A¨`#;Ä}à³?aCBaz¡{‚’†^°–:܆µ0Qž4ä•và& q*
+€£ZÞµZ*êóZwéãÑ[£+¡‚ݾ±@™êiÿÀlÃbÍ,ädÏóÃõRSÛÈý’ „]‘2AYn‰„IöË«ÏÅ6ò‹æ|›^5i>–?eN•ÓÙ•“\P¤𱆯+ËÌš¾;
+e
+Tßùÿôl‘(mGVüb7
+2ÞûˆcŠØcñ]^{Î> ¤ÆoJpà öhÞbŒÇS. ! T‘PÁ2ƒÚý
+„cä\I—ôq'¯$„¼›zÌ>¯¿<†
+ ÁÐ($“Ç„ƒ¢B¥K»5üú!NÈ)D E^[;àD^˜ž ã‚SöySmù¢L¸ÅÈ‹¨¢4×ÔE” .‰½ï8`Gx¼äÜ• Ý…îÊ×n*Âãä&<.±¿u&Ú¸’Lr”äŸM±ÜbÎãÉ0JKÞ=µJñ¦cü-¨ ™¡HbcH947äs²Ò[‘@˜»ÅAÍ¿²{¨•>X’l¹Õ;QFL¶ä•‰^ä X"À3ËÑí$£°±4 D§ €µÏü–8Úã‹ ©G2LñæÀÝ’Pð¯ãì–-’<s„²øÍP¹WÜ‹()É{% Ÿ'Jr›ó‚z¢E‹nv‹³E7•’ܦ‚åwº[j¥˜hbzŒ…VÞ?\Pq%^ý«awåÏ
+çZ3à>º­­]ùƒÁ]¶€—·bôÑ (sÜ4D‡ÐÜNæÌ/©eàY’a.WNV5ðoÑOnΰÀª³j¥w?¹sç^I›ùj¦:ôb1Ý
+M¥‘)K)1>îzËìÞc5ƒûvì±þ-ÌཱིJ²2«/hg°üa4èJ2ìì<†ò„uØ’²#9»$áfŒT«ìLS&ÆH©YÖoçÔæ3þÍkK'Õ¤ùÀ^¦eÎC`Ì·Âãh~#Z2ÃwÊ;”Ìºà‡¤‹ˆˆÅOʧÐ_ÇË=ä•
+õ˜)–‰ speOüú݃‰(9ˆíJlâÁ¤¹7_Il›joD´% ç ƒÆº»Ïþ=Rðþe€}%AÚ]N˜VÖï¿]«/hçMþêûŠÞ•,;ž2^({]9nVl;×7N%½í~k ¬Sø‹Ìë÷=¨‹µdE´|,ÁÃÖRC‹Œ!(Dj—« x1Ú]^ïi Õá=9ž<ÆCu8î¬ÆÞ;Œ¡Ýhƒ¥1„„Rú©í1ô{½v|!k‹A;»!´BîBg'û’ö¦±Zê‰eŒkʼ+-c¦„¶[
+|§úôÈr­¾þ¯óÂ8?èñž\µøNUõ^3œÚ¸ ÁÿO:¿Ì/W›x)MVŸó]®–á~g̾í{/þ°˜(.¾U´&A΃<-:í_² äd|yÖеMHö¢ãžOäÉ@=ŠAÞ¼Â4LÞÍôU*¶›íÒ׉N–©”Œ§g* uø®1h½Nk^×`'󃪭¨ È$‚Ê—ì½×¬aî Bi]w¥ Bjo¸!ÂLœâ¿&*'j³ –ÎÒ0DP·Á½à´@+Ý®>½ÔA<˜˜ä·€~;w ÉD) ÖOúÃb—ü óÛ*êóZgŸþò#u7c^„A'9Â#¤@Ø-¨Éz?}
+%yÅ9qav¿ÈßÃуGL£ìµ@õ"Ê€Ù·Â KpR]‚<f'Pâ´b±%à"MT!>4{9aêŒåÌ©È%BÕÂ%…”½òÇ8Ÿ¤™ûVNÙ‰— IN%°NIÓ¶~ËeηܯÚrÅJv²çâ¶ÜÚƒ,¶\Ð4Hù}¨wLW„,_÷ê‚T`³ZñÌ‹zSŒOK Jʤ@*—ŽeÖ _ Chª‚UiÁ&4Ú:4ñ²lC¯efw‘g( í/û¨|bTæU r®ï^€˜I¶lªôƒà¼rÐE¨ûPU½ò¼Û¢Þä.v90ç°Hƒ2Œ0¥gƒxt =‹©dغfº@‘ìè›h¶{äô‚½nìÆdRÕ8N²G³½ çÊbØÖ5<†#€RH¼Háòßæ‘z¢°™”æ‹z±&aZŠ¾<4Xcºàô¢Û¦B1Ž¼!*e¾8¯P˜óþ梀ÄÞ7f‹{§vCëâ´kÜÔÁϼTù *¥“pиht ¶Ä«^ˆÎü`¦{c¡ŽN[BŽ4ê_tŽºk]M1æ²\žgrø'˜Y'«ØLŠ CÁ¡R¢¦ñÆ ºÕ¼,ÎV]³ƒÆÎçÝe5ÑV[ïÇ5÷]¿â²M ƒ³´Ãòåk¨
+ÈCU°ÃžñnÛñýY-¼\¬$±¿§|Ý)€HÁÆ6XcQï]½q]ÏÖQy­P-€
+¼ X[q9µºÆ‹l'ü+Q™¶´‹]b‡Åo¹«M0-×–„Ì7 Ô ú6ŒhÊÓÒtM7YJÄ´WÀ؆àžM"¬ÛPiÂ>jBI)ý¼ G[rÞE:kC`ì’644¡²‹ÉÆ®oC€¶q[ü/
+ðì‰Y{
+VâÊ‹qX¬ËtC3îgï&|& ŒÍ0V…¡¸½b¥áÈj?ÓR:P,&r$?\¸ £p±ƒ²ÀDíôØ™1ðáŒÅˆ)QÃHÆÚ…3–p`Ì‘†‰äGnŸýÔôØ31ȤÐþ2˜ˆáŒaús~°yæÃA£.LœzÙ9ªÛá Î}Öd<<[i´Çj\ñ
+ãâî88…¶±Ö|ÖüŸ%<ÄC¹Û%9ïrc‰·7zɾ5´­]P
+BR 532 ²pr‘ÅçòrXô,N¥Â_¡3L´
+ wŽ=Þ×rìaqWNzÖíÒ }°}g„Ïþðå¡’ æÀN'›%?‘šáƒë
+äjôš»k{ÕÃ×VòC}0˜Kýaþ·?ÁðŸzÀXê>ðæh6'Ž=Ò ø;9ŠLÌÆJþÅÓ ¯B4@äæü½ @ œêüM‡Ãx(0ÅÆ{p£ÀÈ–Ùr)Ò¼f+\°"OeºR­ò!jõUz[ï’›¹ñ#G¡sù@¿+ü7cyð Ô<˜y i)?*„ÕG‘üs/+?¨*çյ煽Օ LË(c³æ…†{¦Ü ª/»H^xeÞ”Gaa›âà=VÒGñøÛö}ñ•D#
+àÂÑl
+ËT=e
+àH<‡‹ììÞ—ú÷MõÊ÷¥Ø‡†#ņOZik¶EIàæwÁ`ÔE^m6BeÜEÙ¬¿vû”¼Ê9jÙ-ViBb~á—óÛ¨y‰ø?'©žF³˜ œrLJµ1 £²—ŠxÞc¥²Ÿ9~Þ¨úÁ¯1ï’ô ;d’ÕÈÛüÒ«t+q¦ _÷`”%dYd ¢ìÊ& £Q‘íœ÷×wí_U§³À8Ž:rºÓIUu-O=5ѬÕ,DDÐ  "1@ˆ(l»ý׸“­ÕP±­y€nw•"Öà]Œ™æ:2ÚHqwu\]yé]Œ×3RçZ6ªEó!÷˜ûu¦ßd_¢§Q×´˜Òw/„éš²ô.ƒ‡ïm¥Sš6Û¡¬• «‰b­fªóîÜoûúÍšð¶säÕy.•ˆt'U}\õÆ0ªYÍ7ÚDäË¢†X Î(þtåÓ¿
+|ÄÊ`CYäz(  –ŸUìÍwÕÉ*`áfˆupÓ†:¿Ú¦Vòüy.à–#!?úWd‹¬/Õ[MÈôg˜ <²cœoj¢:Ø%ËÒSwDC|Ò“Ý YæI.H·yî\W~‹>µAKdýÌ’8@Xæ
+ê‚Œær7ÉŒ]P'„$Ç há8 
+Áo‰8Ì1Û-?Î1·'~˜cnOüzYwÅï%È^Ží‰ø-M]åú]WÒû×hpŸû²‹ÏE| ?@zå‡ÿÕhpN4þØDà.ŸZñ}ü=€á´<ö³k8áü¼bôÿ`ÅŸ€ëË6|©#3þü}ŠP7Ã}ü·êÓìÈÝCv^þІƒ>äB˯z‚wgåÏÐ’ÿB^~Ú‡ø²òžüI^òj¶§”]ÛÁ6~él5O<WÁQ!‡s,rÕ’è Žê渌õ™@4Ðg„i°Äˆ7œˆ$0‹ÃìÌKA:©Wg¶a¨ò|›‰vhO¢Ã½‰oây½×GÜ].Ü “‘êiDÆG`Å
+ʹßàlLø–üö
+†}Z6[ÞœO•‘\y2y‘ ©¡ôG‰Ì7hÏ=™sé”›
+±zA­¢ÉŽjš)ƶëºÁ}p%L¼öQÀ}4ŒÌíGß@ó$–¾Åq \Òû"KÅsÞy§ð9%A>Ƹs¢ëhâÉS9År+bÞ½ÐY)Ï;8h(èA)¥8«Ôy—… ÝRÍAqê‰ÄaJ2‡±º$‘V¦sœóêàþšïU™¦ËèºBÿ©·‰Xxœ&OQ3¨­½f$¹@G€–Ë-*—Î$L- „œFö%¸à/jÓÇ«|¥Ì4AÄešŒf%Ãè™CaÞý‡ 2Ó3U®¤'„ÂôúïbëWç¶Ølx[óÌ‹ƒ~tqÑÜyž
+ýYM&³XK±ã;V»a†Æ¥7Ô¬|j…\Nȧ#S+7nÄ•j¾žÃ2¼•¬´¿•aÖp¸RP‹™º·*¿Ä•f¯B¦ˆBâõ ¶}*q4Õ18}»rú²½R\ÖŒÕ᱆ܥ¯¹ò†§05V1Y2žÃÖ]ú´ÎeΕ՗¦· Ƹa&!w<ÄÇ֘ɾ˜ÙlÐ Yë.
+èˆD×®ÔuÁZªæ÷“lú_ê(‰…#ÑhøXjì^æë›õÓâéíX<JIeEi¿Í–úz>oÍÿÛæ—ÓÝëüm{?–.š¹r9ÎϧËÙü˜´ðØÉŠ
+M—”;J÷³lPž7.µI^o[¬lJdzç¿W¬¶tË•¿W¬0»á!ð·Š8‹W®¸ÅÊ]ƒ”¼S*ßÄb'
+{­„óªs”yB€Ó@Œ<}Œ]nkô"àŸsÊ€l.Ð
+¸@ßMêâl“}©#‚"gù?jjžÙ©ù}i™:U¸Ø3¡ân­ 7/›à%?3°‹?c/e˜M­ÙòÈ(uLviø ‚«èÒƒ8¥~
+"óªKíD·í*F™éã¯(ñ³g„žßÚ]Ó3Áµß  5Ü>3Â4õì!¯ûŒ f‚·® uc¢ÍtcRëËš
+
+i³3±˜°¥^~]„9'»]FºWã¬^tá§ò9Ö2–z¸IÕÏßÃÄO‘«;ËL¼½·°²mãä y3ŽRg×¤ë³ ÷ß«®æÅÃ-u/ê\‚¨IëñÔ=ú¥ÏY4§²OêÈKæÝšs]ÅÌíQÃä£Y[ã£u·G4,hEiÐç#íÎ$
+ç7%g´Îì‡rþH¯–!\¢¢#S„¶uõlATµzŠ“Y©o²®”À‚òj„q¤úœؘĘw÷Ïbd xwi=£<Ìg"ÎS—Å·)ÞÉ•T¢4ŠHÏš ¯1¶´ªæÈÝ8Ô~‚©RˆÅDðXAÉÚåF‡@ùXÂÅ›e%íSJ„‚ÈùpÐq@TE ê4aNŠðd Êò˜¢Ñ4çßlš#êçälžrd”“g¶ª"sxœ£±Ï93´JÎQæ Á¢ö¼¿Çü€Y¶¼ƒúè!ËxŒ„KÀc2£0ÆwÏi•rÙ%yQ)ÜN&4«
+h¦ /&™­ …(]ÀR 6Pà (ˆ}õ`±rîG«û§ù`ñ;&Sz­]ué
+ŒÉ-A¶vàQ
+ƒßý•±e*–ž‚ ÓJjÌò·Ó3öuMøIå¦Aä62m]×-Ã…};Ï·±4Ì‘Éhö>§vˆ­áFˆ!ØßTHZ†¢›(©' +¥ÓLKA`Š¤¡Ù6\Óã»kL%‰m”Ò-¬HÐ%ifÒ2MÝL²%†¡†™‚•l…¦ëØ0‘‘ĦÆVhVÒÔ’º‰MdFt'lé¦ihØNÚ&[Ûàÿ^É•ä =ÁÜÁ'¨ ¬{®3÷ßΣTÕí/Ïlöü’Ì”<MˆÚ ³¢ @e#æUÉ£4ô®dW–O›ê
+¥ŽahBV=äà!±2¶:­¦65_ŠÓQ,úð¯“Æ×¥ª½…Ÿwªÿ6Ë6Úß [Ûj( ±ßÇ F‹ö¾Ü¾:Øø’ñÞ‡_2{øJ>xIZÆG¥¦»ñ{ÂÙÓŸu`( ¹ÿ4N6Y:Û˜ 7¥ò¬#EuS35‡œÿ ²5ö]ª‡sè4&L¨}»™%Xˆˆ^%cz«¡^ˆz†Â`ËY¯;>¨S¡ë( Å ”QÖ6Ãméø» Œÿè9_‡¶ÄØŒhÍÛÄŸ ¸Õ±Š¥Ú²ÛÆŸ ¿Ú
+qÈ„ ßÁ„²:φ½,‹Ä\],^,ÅÕ]?ÑÔÞÌnµ4l#Æ‚µŸròÈ#yZ'nG„ØñÄ…¨•…3I›™”báy•? …Ö¹šW«bÄþÏ\3ÔËÛŠi!j3Š¹qø¢šW|L8&ቸŽžRÿcÏv2«»ºE¬äzlãƒoeªôõûÏc$9Tt$tG¼9w –OÎ…vÖ¢ƒuCrÌë…R½Ëì¼ä7||7=ð/–5§þ>‡'haº¢KÉ5xÈ Ÿi>?/à ¾DùG8Ÿ×„\F/.ʘçSf‡ááÏx$—'aË~˜HnóÏ$!ï-{ˆV+Ÿs{âºE+»>[Óx\òøcE»ÌwHx"Hö½åyêcñ'ïƒÔ˜‡g\_0 šO¡O^ÊûZ¿»ÔwrÉGn¯„^zÜÑ7È.Õöê딜ã9èEé÷ ;äØ͸þ`
+H‰Œ—M’\¹ „OÐw¨õD¸‚
+ÙdÌFê¿}Üò)Ñ ÒZó°Ç7 ïÃx_D¦îˈÛg'¯q CÈ„Ê$ñsA„Fàîz î“·.ÑFz^‘RÁs7FýGµÂuÓïû3[nˆŽ–.šc'Ç…& ÕN“Ý@öUô%}ÜýÍ9¯%ç5sR—–.·­CP½YNép%ÎU³Ik´Ÿz‹“'¢RÛ=
+CF´éfó¤˜4nKNÜuz>•©ƒ’Ÿ+ªÿO™ï«-rMèÝ)ho”Löqv–1’_~¾Qq¸>²óß‚¤y’•¡:OY%;ç3õ… ]‘úä ‡ñÔ‡ÚàÐõðùÔtë­Úeæ¥?”±£.ÍæmŽæÊ5ÒAf!Ê]å_ ;G¯½úåœ$(TŠöÕŠò¼Æ¦ÔWtõ‚ÔUÖyŽ·wšÔ,NÖUT(ƒŽn4QØmÁ7×b®9©ß).Öi…]û5ÂKç䈓b~©£.¢Ó“¿‚bµGr9½Ø“¿‚üéH2R¢±eƒ'£:&ñê½Îå“LQ~3RÑ·>уž7ªl't$žWêœÓ‘>š }ÊC³¨1æ9†÷Ô†¿‘ËOÓó¦)Üìóöº+ÆæêãRæ—×̺ʋ7‰Î¾ ŠfgÐ’:=Žì¶„pLîëœAx¶Ž,ô“b‹B•¶í«^YP†‡ñ'o窿àgQ9kR%uX}8òfœþ|êÈ9e-U¨71>$3È£x^²jI÷Àk¹ éŽí‘Lþ•×³ ¶)ÿ C½æ´“Äv_ð™5myAþ4
+‘L
+š6C.Ÿñ:*fFø"ùÝ)(}ѧý7ê—§TÞi?¼ò8ß„Ak
++ã'¤¯1mç8çôÎðൃÖ8 vk¨D—ñò©Pûª¯ Ïöéö5Ÿ=ØmT¯^î6;45‰.°¾ õšb„²•–y¹ ½­Ô d—*hÛd€ÃãþææÒ˜½y rƒû¢þ&¨2Ablê¹MÎ^Rü—œú¶¨××ä«›0,ç²INyN#­cQO1¤°ÆšÛ¸}¿È,î+VMddÉs©!G{–@ÑÎ!ø²{Ћuü¾N ’‚<‚IR…GAç½F÷úñ+)X"e¥e©EŽýÆFT¦Újbu˜Gægð+BˆyÉâ‰Oñõ8–0å èëI_sΛ¨¯ÀÛæó'uc0ü,ﯫ“Ø4ºáÀï@?~%•©ßþ9ûû??~û÷‡²'ÕæÜFñŽß3Ã8d(eך‰Å/œÚo@ß9©£*½&~SI«'[‘ñWζ@5¥ŒcÏmU^A?~åM…îî]þ¶eÁäIƒ£<•¨×ÓkâȤAbhørŽfŒº°¤cvVâÛe u««<9.]Kí´¤¤¡-í@xuÌ[š$Ê g_Îç‚L-‰Ç ZÖHa½
+ \}Âù8¨µñ°â¦€È:±Â¦–oJ%=M«ëÇ•Æ—R,GŽ"¢Ï4È]^°’J
+RÂ
+Aƒ©Žu'`-DÔ-Pz{”hð’P\Õ’ ™|‡‹zòU‘œïT‡l§úáy:íiæ‡I‡¹nÃw$²\ØZ¶§G ÛÓ|Uvâ\T€  ÏõRô²$Æü0‹`ÄQÍÆÆÓ‡_xm¼K:-AÉÈ·¿šP$¢ò€ƒ 5ç’ÔICXÄÉï÷"MgW¼ 䤋þ‰»âÌ¥§¥è€"±úÛKäy
+ß^ï¢܈ÉûÛÚ„ö™µÊ\'GKŽ‚j¦f‰²µ†Xÿ$Bð‰ÅP
+—É ‹à)Zì¯Sú
+=É8B ÙϽ©„=]Òg(+ù`W?§Á³ñrVŽwÓ²µ_Äü?Ö—/ªÛ‰v–§ÈÏLKÄÀ¡>Ÿ:’¼»h‰7‚˜§ SjÑÚP¶Æ2þþ¸òkæ ˜Oa¾ö‡@öñ7;ø“u-¾®šžÄzìkýw’kƒ­™ëO˜C’”ú *o¶ÜzÙÇZ¦xzZ|%Þå2ÙZt _›Í¹”Gö%†
+XôÍ‚È×ôùN?äÀßñ¥­UäaæêN*$9µèù:^¤±5öÒžnŸ‹êp׎m™·R~æÊ»8å˜ð å&ꤓ¶ñ¢2îwÜ©—Wœä~¨•ç9[Uê‚J½–÷³Ÿ¥†ïôuÌ[ƒ¹å;!²*ïü®(ÛbaÅ¥ìîæá¤Àœ¦ÖÊu¼î³
+*×)æ,Š„C)gd.C`ù L8;Ûü3 U牺ÑHþ×1ð"©(™F2Yc=ciÉ"%uþžD
+#b,›±Óø"´KÒmÇ¿3`ÿ%ÿ_V– ô3‚ä{ ÀŽ3—Qƒ&)`½¢Ÿy'ð ,.tž²Ì_ y
+Û§€F‚\Áù*X˜fH”—L§~,BúKD2Pš’Æ椈;Í]g}m)s¶„¢à]ºW¶ÜÉLìõÿ'ß?Ãó?áé
+œÆoÙ¨ä é7"?ûvßGÑ@äÕëL2;œÏ\±-UKØed³ r]&¹§Àì,Ùö’Ã$0X÷¬<b ±*Í‘L˜¬L¦iŸïÀØvÍ”/“úm˜+IŸö¨E^„æÎÁ£x_2^ïò©¨\sUݘޑRÒ˜ôû^aî§[u[PäBµ ^ÂÝô•…aØ>2DXF …
+Æi…Ò%È~ u#|îòH9 Å!رlðã‘ìý¾_±‚?^ºÓ½²ôó‰çcW8ÌÅ:\!EÅÄì™=."0œ´‡UŬçŠ.SIÏ Z0lä–yH„¹õ$Ä-ËgY±È
+Ÿª›Œ#—N,¨%Ñd”Å“ÐÆo¾Ã!b'¬æNJ€-=Êဨî’=é}ðûóeî½ãMõ]toü¼"ÀiXÁC£mþZ`Ëv‡…
+ è|ÄvI88–\Àäê03`8&Jay·)¦œ2.>Ý UUõ¸2Þ’-2þ\ÓrYàkþùÙ rz¡0|Ö~™ $]=êc!A´}I†b%Š‰™±k'Ò”7‡dVf,òWÖùRü%ÇGÖ1˜ò<ýŒ¢­ÅÂÂHQ¤5jÅÉpß"¢›7ˆLËëþÌÒ's¨?ñNÒLWVUaÔ*6¹Ü}¢¤Oâl°v— ê
+tWùE>ø›"zÐÇÁSr×M«ÈV²KŲH!AÁá/ŠH“|‚)cÞJÚšº=Êm¢Ý¢Ñ%‹(Ýõ¡–(軉Ù?¢¨‹D¿}ÒðO9>e’ÃÅU"¾Àšñ—-m–‡WPB€‹úßપÌôPòË2bUÂÌÍÒ½üåÐÞüñ<š´iDûP4±¼ˆÒðj!Î/—äºnˆ® {x+Püsœ ³ OåýOs•ø‰7åTªËÉ 4úÿcèPÇ:¶uLÅB›Bd TÐ8äà÷—Ë4œ·Ž%Ù ¦ÒÛÕü³°¬ô¢K¯Æ†¢@ŸüJ›g­ß E Êf‹?A†Ä‰éž Ö:ó@ÖžHÁÝç5ü>9z-ÞdlŒä
+ÖMEnYU
+8¬*§ º>·ä×d]PšK‘Jƾȶ¤~ÔDAIEf•ƒ$"G°‹L©¢ŶaÁTÖsrÄ8žLGs[Fx‘Aáß| 7<XeÌ»«Ýg1Læ¦çi•…$Դ¼„r0E@¬3Òk
+s7˜µÅ‘pâ’ d@gZ^Á_1àLÆÙ«²ÝS/ö˜¡Q"If0{Þ¸,t ^†_^wHðçfß|(ø3ÚŒ$xi¢]™†
+@–Fc*Á£áð0ÍKpXy@ šþ.kñkÌžMHE°d{a 9VŒeíà Qž©ÜVìo?‰mÂ=gQÊ- |zQ®¢)rSEãC/SXê*þl¾’ ÄηZ¦b©A¤t»Úþx§¥Üð\m—ÀMa(Ë7üøã2ì;=§]üg™ãCçÈLc„l—(âÉÒUA^“¥eĹ&k¯_Y¹>΀èžRf¸¢H‰`fØÑÏÉ0=¤Ìˌ׬Áb!߆q¢“i]b„vӌȡ_5÷²‚}m£>piÆNr¥’šEve上-aÑîËKä=š|sŽC
+Àäuüô2¨
+ l5®ªîQ XÌÓŸÅ®ƒöUÔi79Šƒä÷)ÁСnˆÖÜ— æ
+J»É þ¦¢†œR¢ù+qý!†
+® r3!Î(špl+j
+‹/…ØÚfBö¸ðUl8´U(B
+Ô!»E 'ðˆâ½]J^
+¸½FðDC¾"¯ËGºè'3_ƒ(Âã,·—.†Ë$xÉŠ`ÁÚÒXÚi€äÂÌÖ Ã&þG÷8%nú^„©ÆÒY×¹iºãC1ƒSaTÌŽþ™¯Û.XÑÄ«¨Ñ¶¨‚q•ðŸò¦‚oÌâà$çC Ù¯˜,Óœ¡X¨Ñ:ûÂ,O
+­”àþø%ÅÏ>¦Aˆê%ú— $àS[^Ž½Bf…(†ZL@VSA_ò¨JjY}°ð©µKžˆ‚)
+>°ß¹è{‡ ²Ú2Šþ–*}Ì¢(©Ëõ—¥ìÈW³éxæ4R”8C–EýÜÝ“'ÉPÉ<™À e1¯ŸÒ9—qËÖ§ÍÎÐhÂôX¨)e~\º¾›÷V²/‚ôÊX"5®ç4p›,Jç*ì(€eʼ"÷¡k³iíËnßww¦Â‘‹ñ}Y€¦‡«p¸%¿ìÓY;dŠMШíˆ$^à1UTïkG¦cºxÎѺ•»Y²mÑ3–
+?цEC¡þ Ã` ®ä‰ëR/Ε¿¼ô÷"F ”q¹ý
+5†R˵`€óƒà—2¤L
+eò邶yÉ›¾TR  †R¤@,¿ßXp~ä]Fç’¦ôÚä0œQ7¿*Ë…‚fF>r¾Ûº¶®*Щ„…"(ò¢Pü„ñk­7,íÊ¡ˆZþÓ?ª ÂðÜ­<
+¯ÛMHã‡î²˜—ÀžÐ
+–èxå¹IbɼÉYð€!Æ"™΄‹À¶¼oª´”Ëé*š< Ög™FW¬K¦ý(Å
+
+áÎì~~2ßaŸO|?06MF5oïx©1Ï¡@)#•±4“V@ÆŽlŠJŒÚ™2Ú­^Š>‡1D,öú‹F¡,,& ÁüÂk‘3VR.ñ絈ñÓjzn AÈ'd¡Õ˜#ìÀ‘C%ÔYÐ<¦—
+9aÀ㈀šÁ:0½5ƒ r§—)˦PûYâv¸ Y „©ØÃÍš˜“èC[˜¢•Rö2‘IŒç£º”+´ü\§|-ì—|ØzŸ›!€ß•yÝ%MŽ;Z8úž‹ªéº¬·Ÿ£¢ yV}™Ñ¡AD ÏØ­`óVdGV$è——O‘S€\¦CȲèÒžüWO*]çtÌ|²…°ŠK¨
+w8#È ™ÒêcE(Eø\̽†?ð'Fâ(y›þ ì°÷˜ÁSÃ(úˆÞÍfu˜1÷±È·ÐÁ¯õûƒqÎ^~“¥x¯¾y±’f}sÍw{¬>z×u_kUÒã]ødyâf½¡m{ •Jb³·´æ8æ9ñtö榶ã6ºÅ
+öãqEqÞ¾éáø»z.²Z¦ð;Õ7µEl•`VpdåÎmB&¹ÄòåÿŒ’2tÃzž/DúG}Š|{Q l95¹ÀUkãÒÑñ•Lô(Z‹‘pXï
+ÊOÆîÏ÷ß1'W³¬7éñˆ/æÅâ¡cÎse­JnÚ'ì$Öâ¶Ëƒ˜NáÄ1»±Mðz!:ÂWß~O|ÉlLBÔ×êÍÃÌ™7ˆ½ù(¶Ha±éãi˜üœØ‡R`\W6{퓸çS—îV¬¼|Çg[tùÊ99G»e ~‰»¤3I®ûÃî–ÉAÂÞê#í Ó oZ³}úçW–æ|)ŸÚ"åÜ®<E¡ävÕ´õ]®ÝöptÇ#k ƒË/†F J‰®„uÎ#ŽÅò&ÞûM¨ qݦ¿0‚µÑa×ùQEè˜>‘0dòbúRÒ9Ô+¤MÀ_œLÝmý–ÔùÁÊhO«¸ çãS²2Æ°ÇÙ^IBÇ«•1Îò»²õ“•QȦµÅ°2¿ÝÀÿ})[f&¹…%…Fü«:I•è7·í³èŠá8oŽ£Ú¤]ÊÁ€“Á)+ßa›…NEÖ¹q¬ƒ%['\‰Ž$¹{¹9™¸¹•~­¯¾9}ïaŽ-&æêŠÄÛ‡9ÚÜyOàÕÉj:eúßœ›ÔcS¯Eµ®l»pé6ù£ÌaZŸÙq¢ïÆË¿Î`€€]²<î
+©­² ÷:
+‚x €Ûé²bÞ
+Z™þQ²†vís<
+\«µe,fó4\€äõ¤ÎáCVˆ¾þL.‡ÅÝ£„n•Ö¹`¨•ìLùùWˆb÷4`b&îhMUWö_@n½‰ÿt,W<`ó±{|*FÖŒa}ƒ»ÓïÒÙ¿Øue«5ÉX4ÂÄ—`e,êMF¡_%»ÖB ²?®oo¤ó¯’A_¶J’ÊÆ þUúJÎ0êð|2 jÂ…çS”8e3 §‹wÀFÅû[‘æn‹¼ ÕݪÂÓ5Cc8¼¶s ãeÉvtÕì¯ó¥dî;]qoÓ§)ÚÉnܤ´XçˆÖ¿Oæv—#†G
+zݳ/:ES5n½ÜùòD»3fXeë_ ÇYG¢ÚãÍZ=™=wt=Éx¹uY~¿x³’4ëpÆvî—üÓ›eü.s·óZÏu~òf)9}ŽÒå £ ÞÓ˜eË
+Uðþ倫S²Çöå4%|ƒÜÛÛæQÉea÷ðÉL%/û»Õ,é䶗)|EÁa¿ì(Ú§’ŸÐTQòJZñG¯êsCý0ÎÄ–0_#_Š÷hJRGô-%hi«tB]fò@ú­t;«d§ÓË-[µ}&Í£i8‡r«á8¼ÁÕ À´ gÌò)?ê±N–ÿ¦K5@0Ý°IÞŽ˜ï˶½”Ì6˹æ]ªñáÿ^·Ë.ïO jžÐý¤=‡Äëºó³a
+ZÃJÞÇ”ËI)uo/%c'¸J•daÒ×sŽº1)Ž“|>µDsÊÞ®×íó*†:{ìÙüÔ¿·¸×4Y&Ô°ßßÞc¤ŸÂzJ~ÂÔ€úhñSæ|-OÜ¢ÚE&סwŠ8•=2´µ¹‚‹TCeêÙŒí+L¿Í
+E .®;k\Ä¥˜]%]ôv½¢£hüJœ‹±[ ô‹ðZ…‹^\ñ¹ö¾ÏOU^¼«ð@™üšýŒ“í£„u» ³Û\Ó5>•¬Ñ•ÉÙ74pãšÔ:Ǻ¹Ãù(‘˜(DgKâÆñíy}â!ÒD´ûù”&84Ùr™çÄ.'­žÅ\k ¥ÜÃÓ:\§ÉÛx6§©wà°´'Ûä‰iAúð!áÆ—¤ʾÜãMÁsÛŽ©®-ÆŸy²ÎUBŒõ*³Ý—Ž*šñLÊ}¬Jc=¦að€;ºÈDlsâø1ò¾ßíÿŒ—Kr]7 DW=x*þIŒ¡v‘©¼ÿiNƒ b?ñÚ©òD2ÄKþÜâÍ~_â 5À-ùÜ|‹7‹ñ±œÀSKܸ1„©©Ð/^tW$ºxó’Ù(šÚ`6©Ãô'ú%Xˆ=NqÎÒV)'d ? ` £±Ò)!Ç Xg
+¦‘Ò ñ©¨À«`ñ|Õ/5»FŒnR'‹¢Ytægxÿ8úaZ©*P¹x½tyI5åT
+Ol¼¦cËæ˜Ï ’ª63‹ƒâI0³l
+N‡v3‰P®&²°™EÁ–â=©[ ¦Š¶Wmqs~T,⨅ãp÷öKCÓ?v‰{û´måö.«SHF˜‚ˆT?K$æã͵ït.k2çÂH
+¬LIµsãÎ5*Ùih¹4MŽ•b2m+MçlÖÛß &–~J)åãçeŒ?Ü™‹¿û
+î³<a÷OB‘¥­#–Q³È6ö`<…±‘<#…[Y쟌9ïØŒ*î’íU1‚øÀK›ß»ž®9l]õ
+ó”$
+FFÿOJº5Ñ*›ìapû” “<›È,Š<.pB[Á¿@“Je0¾€rà8H:IÃC»àÍ\ÆrþmÊ&H
+HÌÆÈZÁâe—Ð-ÐÊÇ®/%ÿø§àkÒƾÔ#* .` éT÷sLGÇ ÷yJX“ ¤¢m—у³rǸÀJ¸Èºó}÷î/ü(“´Šâ H’H³_.ú‚ Á_?œ?H@‚[íë(Ъ‰ô–~‘J
+cæã\jµ»ŸÄ'~£r‚úƪû`“Žtø»?«7Yá.³üP ‹·KÄF¤MÜH=Ÿ’>ƒ}aF‘Ü ÿ/?¶üœDXá¾pØwYzÆFûÃ)¡Í×p* n)¬)\z—¹2Ô’ÅbkÚR[EÅüD=}½ôe{^FÐSNÊE¢2fˆ ¿-ôÇÿÙúw'¢oG—“ ù@¤¸¡deËÚ.å.Œ÷³OVæcÑ¢#õ£lÌ^E^a’]N²>ª¥bÒ¡áÙ¸Á¥ÍN½=P5¬&ÐAOÈz¡†,;¬ÔñŸ¤ñB ($­ò†Zþ@ YnœMc‡I7WjPš“5ªªÃf/ÔÀ0¸FVèê…²”‚
+9àõf7kK NRñç³çúÅÚãhò'\ófm)a‚¹ƒ®s.“À vÔ€Œ—°ZŠŽÅ}f%;k‚ó*ûS†öŽYÄk†â+ø Ú ªAŒ’ÖÆ&Dš:'€*]žôZ²Ô–‹Þ{¢Q‘ì.+¶wù›"1¶¶uñ®"kÉJÊ«øeð
+4,ÿ¦h%Å"’WÞ¦½*e¡5@9%`UûÂÇÂ’ëOde«Xëc‰ì¹Áø
+ŸÂD£xI†ƒM¤±QW€ïÄ¢¯DŠð/rÔ•]D¸Òß(󘣌K³«tþÔ@Þi*ìßB³Ü½8»Ã—(g<Ý+P€Ì—j !Ý@•Ùµ-/û
+·Çil¸à°£@ØoÞY󷎱¡õ”1–¯šÉ+7Xfë¨{ÎÔèý#û± —Zf OsÇÒº—,FÉ–V@÷E¢:̾æú”òªá‹xÚ<Ì@@cãû þIŠ;ÁŸ½F¿¯—,;r +ðjî“ÉO~Æžz=Uïê$³Ž¥ÎgK#U’$€@DÀ¤_3ÄÐc7ôSuÈCÆO\gÝbhœ8{»ú¤xøÿÇÏŒUÚPö5êl
+D¬F«Î»@ÑÝû³õ°¥«ÛÞ¦BxŒöG%Ú³3álÞ1í+ñ°°ÊŠx>&Á ˆ(á…QÀ‘UÄñÜVÅ#aÖk†üÚBð„Ä
+Ú»K§{+p3,2c)œÃâp¢%ÛÃ~®s¼¬Bö¨3Aq\[ÒØ_BrúÝ#ßßϵ„ Þ3š¥wœ=¨§,£Ä`Ǫu?GˆòÇ]ãúª «Ùbd`¤Æ%€`…5ìj¶¬Û¯kš&È.ää^¯¶¿Á±Æ(œiÁu´ÇÝ`:Å ¬¶™¸|*°™»¾?¸?QYà¢Wâò Ä×4ƒ€MÕ$œm´
+ïé>¬S¾<8˜×9¿]Ý„ÖädÂù–œ[ûê5}‚"¤æ¬ÆsÇÃ)È1Ðú!´n­ZçÖañ¼ú‹öü5‚ˆ® ºB,ËyᢥÖX£>Âu ébŽÖ€ Ÿhd¢É#$»çNŠÊq.-,Ù$cçÍjlžŽl{Š÷‘­'AÊ:¸iæcÿľñ`¹oÜÿO ) ¥UG–ÿž3M¤ð²jƒâª¸,Ð>ÖšpޑȘ¯i¶ýó䯘¬j\jȹ$ä`ï¹e…îƒ<“Õ6Q’gl
+ÉÀ”'$öSŸ{YóÌ ¸† >êsŒî8•`¶pf”ãŒ>Ç°rpÂ^ÞÛ>BbzÙ!ô^)¡Œ
+€Áñ¸Éd^¼“é4ùki
+ÆÛÞeœr˜mn§©ÇâtECR+¢ÇžÑÐ2¡¨"
+±,׺ÈW&ÅúÈ&MPûÿç­ô×8§W¦–x_ §”`îYcòGJÇ–„vù-ººdØí/ÏUo)Ù,¶¥@ z• Ðh­P¾Ñòc¡ ‚´mî#„%é˵Þíƒ×"Hëe•»Ž¤mè÷~ »Žw!U6Êõ¾ŠÓ¤ª1_¿1ŸcÛ­¶Ð» =Fdz=VadŒ_ƒ`DíUg`T¢¾£(ÊxÍ–*kX¿}„謹Vžc˜­,J:µq ä8©P"TcYÿ{ÆàS®wˆÿ+!v×ÌF8¡Îtef1à5B œjöx ™Wy…?S³¶rÚ:G}cŸyŒô®öõ'íB!uœ·Ô„PŽÔ™Ÿ˜ö:9 0úg”ÆMö—â7­/ë9ì±°»)ò×¹† §¦çJê€-,¼eëúb$ `­+ô\õsÐØ{3˜…ðëå5Bêh”ܾ%%f´aJ[åû¥8‚809b÷¯¿W8·9FˆF¨¾5ê×yc›•”µœQHKOÁöÙr›=·¼îmÓȹQEêzgˆ»Xæ
+³\n•mã¹Úzø-G`üO[äîÔNª5î G† ­=¶ic,¹é3¶FHkèŽDœ{Yæòrbjö6BÀJƒ³ijÆÚsÁ¶â8û-ì#bAÛõ¡è]yŽýT‡ÑÚ£«¥¿Uòßÿx{ökP­QZÛ*6™AJˆ’™Üf”š2¼`4\F7†"!GÏGÒ½¶µ›ªýÖb»JŸed6È»“®uÞQe&<“ûç¸É7gˆXr똢ðâx¶lt5‹ F(gB²Óxvdo„HjÓÄJþÏ—?:˜â¸
+[Ú‡sb¶-þ¡ÞoFEˆq&+~ÜÖÇïö±ÔûXWýo°NH§ðq La[s/‰äé¡Öˆ¿F ,ŸY¼ÈGÿÐS+K˜‹ènOú/AŽõ÷;⼬QCØÜŠØ&ÎöxNCcï» rh=ct¯ÄôøBk+:ZAC…¡ckµinG}FtXÐÅ ÞYÁ¦ÕJ实Ñ<çWB€¤Æ³…GgÈpé—S!±Ía?LT^BþWM“F¡Ö "¦–`}œÓ7·ÈÿjÛwQãê®@&!ÇÅeÐûŒ`4N©“´cL½cz6 H赜ìΰS^me®8`Þkù^>¦WkV«{>‡Ïb¶Ž,„ç‹Úë)ŒC¶±¶ã%$™“PLbÜa¯,²Sö͇eœ³ïêbL·Ç±ùœî²ù55.Qü4< î½X*œº³B%¦ÊM‚N³“•Je—pHÛ´ ˜ÿ(äØóÍÀŸ«ÆðV0ðÉ;!¤Ìƒ¶,
+Bú~ŽsÜvRB]<fÖ ¡â†!V(ÜgÍüãåŽœ× Cá­h’à³vJïB­½ÿ6ß@Môëj♑ ]’
+™7Î%yΛ¾AfEÑ$½»gg8*µG!÷Ùæ¨Ñ]QUn`}w5oK½§Ù×ƪe S¥"
+æÚè¡ò¯1—#š6ÙPT‚ƒéñXgy;QØŸŠ¹_+ÞãYÓ}ëúüÙ[ÁVö¼¤ßêÒ|ÙÓ-Ù¥b"a®%ï%£ ±M½‰“
+TÀ£íØíÓ® Ìñ’ý‚ŒQЖxéTÚ Kã3iºZÍÔNð&»³"¤é”Û/âi䢧)Ç5@ø9#/×°kÀ§Ö(Á¥p#¼IÏ+-ù„Âxùk«6„V@Ìîï*
+ ü8-M û @TǶ¹ŠALS|÷ Pï>O&[!<ÕfÙ>~Ë÷cXÇY|¸Ž,a}±^ì~£9O(ˆ÷{aÄ
+bGã×=Š8ͳOùô‚µe¹÷òÏpDf'ûàs$i
+z±\º(ö% m˜zË«AÍ“´9$1±±Å<»²+âÄs×Ûó²/PŒ?g ëw%Ÿð Ô[:ÍWÞy?×·ÿGÃþxìûÄ~ö+…Àˆ çL±¦Áõ[YŒ¡íD4¾EªM3TÉp¦Ò¼ìê2ºÄhä>ãýÆPùÌ‚KÞ¬¬?”¨‘ibZ'µÂ…çåpv¿×X¡%SƳKãÏý°ÝÌŒ=K {*š…gq—)?õ;Šð¹
+F<m@f?Eèˆiqti(EßïIUx`5Z8LºSX,YŸóá#§‡üÑö’!<áû¾ByW9*:2î<ñ|¬5O Õ©D…‰Ý‚ž†ÛµcíG3ûD ÊÀAðR,æ¶"v{?ø¢b¡
+-Xo)|Åæ»oy“ýhp×j7h~iJ¦Ð„_„ÇÏQ+&aÅîË%[
+:â¶àZ4´"ñŒ‡7øÖ%'£ÄÁH—Köf™X‹̯pÖ'+´wŽ
+1Š—ৡ§e×l“U.Ñß.oŠ+° Ð¸ÞLòè×äæÝ_ ²‰5JÕCHy:íiÇS¨$2V1i²¨ ê©—ào!$¦«3œ09ô¹^NR˜©f&Æò[šzfqÔTÞ"@¼íêh‡‹%Œo!1®Dã€h¹‡‚‚âá/âAS‚ëMP°lƒíc<Ñ d˜¥IÔW¢Â´DWjJ 1¶™çîÙ!*Ër
+||ü¢|C®YñÉ ÑA® ôî¯Ð&SFªF¢O»ƒ!Æ÷9@¥ÿüš%®éÌ9@`¡¥`lyf¨º
+n·pã|­xwá;‹¢¢=Ö©¢E€qY…³¸¬TÛ^B
+À¦²´¥ä$†0‹-jÍ{
+0~Áöùá=7Œøˆ`•Zö×Gª XNÄÁ¥ BûMžq¯ûÓ‚˜›fÙÓÑøº.F‰ëó&@’ô÷@ÑD˜óPôÛ‹àþC7Э,¢‘<ŠÍ’<QR8ˆÎ’”Ìt·¡i½‹þ@dST¾OÆYL_!dN_XŠ0€l<Öú¾ŠÁýù'ºjþ’„üé¥äQ¤«Â €*Ì•¦Ý<Ö”(ùyœhÏSá±I"8ÀíûcÑgû®”ð˜üè̱ó|–œx—£¾P{=FŽ‹ Ë5(.ìûrVb,Qé‡:I+W/òþM+1U ²ì‡ û‹%”€ßWî #Ø¥ìe<€$öùV´…Â-ó
+®†a¡j?jñò1q·çtËàXv¤)¢5ÿŒ‰"Ø2‹%ætòyÖ?ŽêvP àp}cgWÀÔzú› m˦p{¥ž-ÒŒ5'tp½
+{]æ¢ï
+!4|k¬ùAi¦BkYÄs«ï7þz¼òй.QæôV "Tu%BtG:Ë~ί—,±i
+ +`½‚KþH3eL;ûŸrŸ,÷ ‰s
+ñY¿‘Ð’¨˜ñŘ­.Ò³¦·¹ù.¡C½Vœ·¡1g;
+D¦¿ÝP¸D]tqµ§Ûì´2KdûZܧ¯þœ!hž¶ÙŸíâSÝÇ“û€1îÂÍùô ^éFœòùÀjF›’þÔ1+Œ;§:ÎUgM—<ö»#l¼ŽÆCnõ-™¦wëè¶ôÁ‰¡©-÷~•$l!GARœÙUïƒþCd¡Š5˜ç€:$5åeßÿ{Œ0þÔý2Žåœ¬
+ñ½J¼ù‡\ Òïnk5¢K–F*5ö½-¾q¶Ð”ÜIÍ"¯æÓWðagèÎb—]ue”æ=÷—¢ÏY!û&‘ ìEOøø¶<ÌÄGJZZmÖfºJxY#ŽÏWßKTˆÇ!3eWg‘ÿÅ•Whþy»Q3Å=VÉ•wöøÏãzƒÅÿÄ{u õ`Æ–òœ/òöýµ•? ”{à’ôjYœEÚxCº®Äì#¯^Š6ðÕ4¦‡5Õ¤ë'ŽòúåŽèOH ÅZç+Å 
+¸'Ôå¹æ-">
+ÏߣÈõKÿðyt3n$yC‘‹à) ¡k…ȲMž ó;&Å„Êù+BR›-4‹MkJè•9³Oð±ä¯'wég.óç<É@Y’|ÜYá`_ZÅ›“ï²c Í–4adc$ð¦à–<¼rD\·¡¯Ie¨Ve÷%d£"~5±ê?ö õ¨ñì°¯Z‹8Y»r=.ÿ¬Á?§tLüa ÚŒ¶üò›Ú0†C.Ä帧𳪜Ž1ÄýEÏ1âŬÑ8Ε\â’¢pZz¦uS;|E rîγ 9Ší7Q…MÜÎåˆltÏÚØÞ!ù˜ö%€ñ
++N—]M[úª ·~Xðµ[{dçÀPòSš$±_7%ãÐ÷àáë2',à…¬Ø•Ýr0À@ V_\ð¸q
+ø÷ìÇ{‚T„µ:FÓM\¼öÁÞ¹à>K.
+Ûµ,ÿÍ>L^§D Ë=æïµ®Œ
+ŸËÂÆ'°gykK òŒÚàïv®Ût¾ }ñžÆ®ˆLÃPBmÿͬ®œ=*Øø¥³½5*z„<¹’íÑëáøóÿ€ý¯eõ;?;nI³Ž¥5"Ì'Š}-“¹D²„v~(Aêén™Þý‘œ¼F¤P‰xbi±-oÄLø€Òß>ew‘dn·[ö\;EÎz,áÙãïþ¥ò){Âñ$…˨]¤1â°÷))Ê;¹Ä†-VQõIk„«ZC?á`K6ñüc¡Õ^â &/:˜
+vµX> ÙÊ]B*]ˆÌ¹ñkÉ¿@ø÷oE5q¥ÀTzhg?Á~Óˆß{o4.L{²¯ÑD·a8}2Åaö8Ëìܳ?Ÿù1¸õØZ<ãk[kOÚû¶Ù ´“i¢êAR.(i߃\ïï,7ÅøòRÌ"òf³ŒîÌW<Þí‹ôt¯JŒBx~–x’­—0ŽåÓ,¶„ÄöQîsæÖ´Pa¿‹bl1Mâ”;|¼/º¹/£Î'ÿ¦ä_XxÒV?îuçŽö ñWýúž‰–h§;±!Û#ƒ?Úf%ù£ˆw ‡Ñ^<¼’MWÆUV‰Žú×â£è¼ÿú–ЄñÀä,k^…¢má0üPbÄ-ÞÍ Çš×•’teëg‰¹…fcý–›e %„c>5°y-‚Eh3±d§Ïyµ”)D"êeUa6¹rC£
+0#A*àh £R‰ãµôFžˆ ÙÞ{5~ʇ5Ö¯Z}‰‚!¼‹.o«Æt‹•m
+Â
+°JÒª ÜNΓ»
+(ŸCäA#ÓVÔŽ›ƒMTaÍ+~åK1ˆ@{ ³+U"F‹p þŸ&X<;‚<ukqëM¯ú8ÃÛN|")"-´üP2nSg'o’Çóïä’åm÷§"³!v~:%΀ΠU¬y“½öx³£1›×§þÕ;Y4ñ<­ŒðêõÁŠƒµáSÖé­w¾üË‹+Éõ„+m­È?†­,ƒ‹–A?#¶¾Ö¨õ?—4Š8³I©RÈÏ4M™7¾Kïû%xŠÞFà‘>aª -u?l˜¿‚™”„x|0æ趒ÈÕ;èÙ¿( ýž(…–'@(Ô¹µžÞ_e§Žf%Ï#¨`ê°`¿ƒÈß÷,}”ük·±7¥Ñ•<"=”ýÁ
+þ~凢#ZÎå->æœn1‚Má÷‘Þ”DÖ*ç¶O“DZk
+©Ë†y¡Š@ç©×éosuéÒ
+ÎíŒG óÇ¢ÕåÁ'+é€Nh¬ôiò€æÒoÉ)jqèHÅ´vEs¸ÊšÙæwF3 ߀¯¦­ã¦ß2J˜ª~‰'¸JÖœ¸QÕtó_>Eæ€\%qKoe5”¥ib(çEÒ®‹ê¢ÇD¹/ÃÑ…†Žá îµ:g–†Iž•MÞÁgËÍe쨞¬ò2gŒÜOôÿ¼ïþ¯T°Íî+ªuÓæ_>Æ–è¯õF=î´÷‡h6›m;ûC <âuK< À÷Û2ìU¯g|WJÀÝâp173KH¾ àsµÆÛ*Â…žX†ò®Ñ0/Žssg~FëØgŽ@ÏáùIÒàÐû(9£J‚F°5ññUì
+¿¾dø1u¼ÂL.{¦ T½JIä?[à\I*gR Ü„ 4Ý
+_ò¢åb­¤A7¸ªÞ¯E Õûð.`= kHn#Ç)r³<Å’ÔVnâjøtŽ"K7ÿQ^¼ÐO€$m݆F¸ïU³…õ¶«˜ÐóKõzz‚Ǩ2Z“H©¶…€æQÇlj³éK¿=(C1ú(øíG!ÂG·h‘WÕ @Ïjì¦S‡ÖsÓÎq–õ ³
+$³{}-73},ŠÙ
+‡É—é† Ý«Ã架N?€ $jPâÅk(ùõ¢(AdÏËîš?Ír0Ú2iZšÍ0
+ ·‡Ðo%Ë@ûa~µ]%áÐ$!d¹õ×Lh‰‘uÚIÈ­}IÂXmL)ß6‚DÖëÔÇákZÇ«(Úš@—ë»>ža Ü\F‰%¨·ê²Ý*›G²
+k$éz8~h<Š…Np샷ˆ†‡l1E[vAFåí’… ±=c™Œ¸¥õá/„5¦¹Rá)òh®ÙÞÅÚ`øö
+H‰Œ—Ar¹DO ;ô¦ƒ ‚Xû/çŽø+ùþÛyÉby!—#4^ŒÔÊ& ‘HDvóÞGõ×?óÝçŒê˪Ò^?>|½}òãUÝú-kÃÍFšÕëßÏ·ÍÞÖìѺÍõ Zï0®Yµ8( ¯wóâîྨÞe­z†UÄZe½ŒóÚÊÐuöÎZ6‡½>…™5¦åÌ>3ç…q›
+¶2<^ÑÞ-šqMŸœ}!úè³–·œîB8IoÎýmÜ‚ÁýõóL³ZMŸÑ&-s¾øÒïe5,Ú̵ü¼e´$šƒMè$j¯¶îSr6ZËäS]Å'³F>m¬±ÓÜß$¢÷ÎñTÄš¡¯•B 1y¿©¿ cÎn¹âªúà}œL½Túë&ÞNÔæ#WÛ ·ì¹T-“V­—[e«uA’|ÙLó°ØAqDõœ«— ñF™ë²sÕHò™„
+WúZTRDá©sàßâêeIÂQ+’ÚÁ‰–ª¬ŠiúJ™]~o>(§ùºJEø¥Êsùoš:óíKçœÐ™lÝJÇ5Šµõ³xZ ¿Vˆéˆîå´Íÿ?ìõ¿÷·wáKiqÅ–Õ‰5´ü%Ðœs4_zAúI@£z­Qæ/ <¦Yõf„{ÓL=ÇÓV¾<Þ­¨ ‚qrí¼ÑI]ÍLöéÐ>< køU3¯².Þ4Õ]œD áO’È O=:¡%€0ôÔÑëÕ|Ó£c¶ÆuÜ–ëœãl‘ºï¸F¹1Œ¹ Q 3© ÞgóÖ[ƒÍ‡‹Üâib(|
+`ÕùÒ¬0=¸ì( Ò¥"ÎNU^@&òByœatI+#(ç‹b@Rk%Ê£è<¿y01ê\…¬L˜§sPt"5\çÐÀ:g-õä!£¡¤3SJQQ8¥¦µ[Æ»bUàªZ^ùKl‚¨Â³¯® ¥Ì JÞ4q7U‡Ôˆ©×{;ƒm aÛ@}Ÿ¤ç d‹“—áßi{
+†CáÆ)¥Ñ<гϵ¯*YZ\ÅYãØ¡ ]5Õ¿?t•Ú†2Z;óˆúþVDƒH0€Z@£]LOœn`V©â€°å©ŒÃ•#¥òý²sý¼ù»”„€i ©¾ÓcЗQ†ó»÷…ܦŒž%àóf¸º?Ì™Ïï £™[£x
+”Z+©îÏì…NM¦—b'ë8O™—´‚¬ÈñHYT ¦0ïR_¬BI™©x Eç
+ž± j™®(¨±Çj¢õŠ!¸f ¬~TÚ=ô
+vqË¡Sdf¦œb¿#Ò¶©ÙT»s°¾•eز¢ˆ nè?vÝ„© E©‹óÊŒÄ_¾†뜒ï×"7Gsdö䦱ÿH5öCÞ¿àì¹ê ˆš‚†·^jž×Ãk€PF ¥êA ²a;&¿nú’0’OшØþš`A
+—B‹ÒpãÏ2}l>”Ʃܨuh„Å ¤#¥MPÆøþ$ ñûÇ¡ËL\ÑŽªS8^ëí±WDí×â%úvòMÈúôd%´³p&Òýzèå…^ª7ãà­ôãê8tx,™6úâÉIÐqx{œ*3ŒÝqݦ쫓À†ó#Ž„´gõg± Rì'cí=ôq°~çM[,ì EàRW¦ÆY,GBDÑ”Ü_á<î(ËcÍ0†ìW‚µA¹G¿¬ŸÁyM¦–¢Q¡Ð΄ dĵ,¬hµ|4&’6š1[2Ö®a £.š˜OT‡§x‘$ðbž¼!¬Ü˧ßϾäR“)ïr{
+Ð×mpQ0Á†6Âë*¹$V.‡Å·åñò*œä\E”âóÀÝØYã‚(R¥eå9:€µà?Ú˵·Î£ŠÂ¿àü‡÷K¤¤Ð“¹_@|ˆ 9T
+Dµ„åÄN0=¶‹ëù÷}Ö̼Nê3Ž ”Viã}æ²g­µ×r:Aë 1õvˆXñJEº9ßäz؃ýÕ©H!ôu`5áé%¸ºHx¦[±
+‡È¿Sf´û5‡³{KâšÓƒû GGQ$(ñ­€èÁÇ{³†4D–Ñá§Äµu%¸ ÁJÛ; uL……ó˜­È ¬ÞÔ‚¤f¥H•Á[Weç…ð(Õ)ðÞËØpòÑ‹I™xU0Fk%ÂœÕ V¼ ÿ0vAÿs+‰P<R>|'š]s`h%b8#6ÞÎá;œÙµ"™#€ ƒ{ 8:¹µØ°Ž¹gM ôê=åíœ6¶
+~„± ‰ì„Aª&ˆ„?åNFÖÞƒ§8Ô¢í¬‹k<P ´Š
+ä¾z³1˳³Í?øs‘ÈU½&¼§‰
+󚺱ü©:Ñà¾KM S[‡qÉ…6ì%Lã¨ó¥•°‚o¤Êƒsp41Ôž>](B>Ù
+V­·ú™’ìzQõ/… ØAM‡d-†Y¬m3´½àQÕqs‡d
+µ™¹?å)ñ’€Ã-¦^ˆÀ„gÝ=žƒ"`ÏœKÕM½ %ò;Ò~Z:u@*Éše9öñ<±[1oAž¸ ËÝ"§0ù ¤`ä¤A¦7Ñ…ÁD+$¸}úz¹wÉ>ÆétH[
+6"êù±×(`J?1^/ÉnãH@Wðö  ”‚ ‚»†ž×zRƒWµÿaŸ PÝa;ü“S@f"ó~&§O
+)Ï€‰ÑZ®A}Ÿ° F­†çnÒA!µ<¦s gŠÏt#P9(â ßîˆîòI¨£q+f« ªA¥ÎðEPÓgB@:ÉàçP%Öži_B05š1ÆeD„þu*ÆnÜd+ãBº!©ºH,÷Mjsç"Uäcjƒõ`ÑŠ]ÍŒ
+ôù„âl#c@ÂqO÷Ü!ô6``®Bï™~ÚôV¤Vh Û@-cÚ¯ÂI¦Ô¬Syw:…P¾½PÝäªõå/x
+hìëxùªà“c@mâKkôd— t*§&‚èë}Scq%D&Þ+~-f†³M×ú1Ö‹šÇlíb)üìÆvö€pÄÓš‰{´“rð Ή"\–’¾–]u®p’ŽxIC_ûmqÙ¡Leë!ª
+U%w6e|Z–í¾j]$¬É+ÈEWöʾô(†—¥¹Ý­q 0Ôòë!šrhq¿L)A{P¿áðò2p«Å‹Š L$âòÁHÚænßÒ7žQØu«K:$ƪÁ’rÓ!šjVµý¾ ¿ø#¾Q?%iì=G÷¨O°U`G¹¹cwŠuRÈrŽ#:ðçBNÇF쌇¢ìÀªí”Ôþ¢xÍŶ-*e¢T#BS â<êÂß5;EpÜƵe·;’$?¾þþúëë߯õñçšÉ&ÉFŠ"g1ÜâI¶Ç?Ÿ1]ä˜.+ë²=~~äg;Bpà³t8 Ì+&ÊCK$AX"kfàêö‹“Þƒ>Rúþ´ÞEz}౦CZ`Ég~M£99
+þ¶”Ìá$æ»ÇlÁó€ }%d ¹lS1Ѻ‰2g#Ää+è2þ:Cšà:¤MЀù²]K„ñ|.‘nø öôs¿Yñ=û)eÃ;yó‘ ôѱåЀYÈþhg˜—|5¢“˜ïyL€6ÄE0ÔÇ4™b—ErtÜ4‚ÑÈŠ †xië3ÊÁ»»Tf$H¯H“+búÖ±±18Þ/÷¼OÖGˆ‹*,6ßÙz¯ÍΩ«Õ‰ÂÔ¿î^$c|°‚îÔ¼\BàSé–( †”Öq`•ÄîW‘ [L™:—³ô ×xX¨£+ó4x•Nʼn‘7 1è¾Î\%^BHøÚÜÓ†¼]òeyóüó•ÊMÇùŒ;””kÆ1Ö%"’ºr݃*4|º‰,²2B9TP$ÔÝN—“³ÉfAœ”´vv!jò{Q‚Ð[¾øSÔÙžtÄ–Õˆ”’r#øTg—d‹ Êá*6²ÛUþ5ÚaûŸ“ˆ¸ÕbÔbÍ¢Z§Ç Iÿ¤‹ƒ¶ž ª
+yÞHIÍ[J©Ô^S± œ%©K²q Ü6ú M‚¾‡Øå
+j-erÌ‹ð×½si×4a{›)%µµiá].PÁÔe#ôÞl€Ã1ð'ûýžL"x×%伕àȤp5Â‰à•°ÉžŽ ŠTìLëÂø#2ù„bƒvrÈ9…z‰YÚ¢iÙ“ÿ²]'ÉÑ«X€·â T†B õÔôí\ß¹"ž“Áß8}¸Ýiø±Oœ)1HøÌýýžcƒõÉ Ý%Ýjmîƒ_Y’йœ3¹ËA¶yéD†'ŒFÿ´+f„Õ»G7‡¾ˆvÁœO®ºbG êí´† âD…ô¨©q†!·ºËèoœhðë7ÎWFq£¼i—»iÕ+Êøj» Böc٠תÜ`ßWÄA·1ÿÂámÄ¿°|ñ‡5èë2 5_[ÚØýû®mÈ¿¼ ù›÷WÐwù
+?ˆÒUwŒèŒg¸JbV×·îZ ôü˜nËœ¥$pÑÔÄ#€|¶ÁUxÄý#ÅÏyÞýÕeõÀ]+GÁoRä¡áª“4ëÓÀ¨´
+“Uq
+•á;Ƥ¿YXòÄ—ÆÚNÅR klÑ'ßù=ÂSÒ–œbDÓ”üèA½Fìñ#bëoÀâÝn #ÒôÝ!×'.ï„Yäh}qGlïSVHTKC†ÎÎŒŽàÂM+ÛŒ§ ÷¥Ÿ› fPweʘ¥±»¬ª'iÛz8%ÞäëÅ´M#òÁÖ“Vmó.`^Æn†½!ÿžR(›ÀƒÒº¨ïž"„Ù<Ÿ'Xñ³Ë»€-jñPÁs¶i—w¡´<fÁÕžuY˜-hÏyYá,ŽbŸŸ8†è[`< Ô§û+êûc-Ýœ3s%~΀#®xz‚Ø ¸•ÜZ¢®Zïñg§œ•qFÙ…ß–?$ ¹ÆØÔ©B‰9 úíÞ\LA@ßÀ¶ñÔPŒsú]kÖA¶WŸâÞ³=î?§äýקx§Ñã5Ö'c¸=ÆFAW¼O–k=µè¨%³ymmæyh06ógIë+~QÉÑñÿ— S7A\¼&ù–€Çmw|,ö]oãëîÉav—9´¦¬uEÚæu{LÅA(È#îõ8,ë ´4xÒÈxñʤ,ÁõTñ(arF ‹ï×”ëW<«UÛ%õ7bqG„‰²jmwLÚ€6ýºöIÐyŒú¶š,[I›¤¨ŽJ˜ë™ž#—rší(šï V5õãðúî² qOÊ3Ö˜~=ùk7™Û‡;1V¬—±=Qu¼NŽiòW¾C6íÜ©W0à¶Kå\ç,6È»¾B¦h”óiÞé±s£ÂÈO ÛÄë`ÜÎyʽKi4‹½ùXº€Îî'<&,Ÿ•Ñ•Ûˆÿ #IgϳZGÈᑸË;xÁ—àzè7Ô´N”ô;€, Þ,„V
+êK{3¤Ò‹GÌ㯠œ¯Ó}j¿|๗’Î9Z!ÅØã;q¾WH;0[ã¥zÉŒ
+Ýñ5Qpë&6Ôæ°îÿÅ|sö!Žâ½d‹öŽö†˜µ3¤ÍehT
+é™’ÿtü^厹ã¸oÔDB².l˯ò†(9F2„o3•ÜH´,Äâžl/Wx½7™ˆ³Ùêç«7ºÿ*è³`@ˆyÍ6˜’W·@ˆ˜@ãXã'iìLÂ#²£ìMþL1qÅCÄ ýg*gïTQ×t$ª8dñ™ÚæM (Pj”þ_÷¼ø?ZaÒH“šj-‚µ‘ý¸¯%}Î)czŠaÀ »p%eS ÐqŠO8¿G†ü°a†úŽÂ'¢úŠÐµ|›¾:§œ¨aÝ®¹÷=Ÿ‹m®hÔ0Ýá½ÝÊEÝ;DzΰÌRÎÈÐ3Ù °²^^}¹ÚÕ¨*Õ¥ùæUf¤GÃr9%µC7¶¸G ÈhɃ„âzö9ÝŽmaỂ0 p»Û•sÉaÐþ Õ<³º‹Ïòêîº,$¤W%tL¯y¸¢ƒ¦`N#uGÅÌzL´[Ýæ½2€z\c¹¤ˆÀ’!V¿‰G‚ÀI\Så“ëØ›H
+=ž>!kÆI<¡›H>µѕ31}²Z(i.:É·*m(çGMˆuÚÈ¡0‡±ÄÐÈžý»A®)‰š(?JÂf‘92
+MgcÔ>/´Á ãœ#UÊ7Áj‘v—õn’‹9¤l¦pŽ Áû4
++Ûjœl^!ºªIÚÔ°lE"ÞœI1jSs¯ ˆlèŠÖ›SOˆÔŽíóTÅì¶^Âh!‰&´
+Î7˜ÉýŠ Æ“±†ÿR«î2V³ÊN dú‘ZWÊ׊»ÒA
+$SÊóðÓž#²eÑ
+Y¬¥î‘¬=!Ìù–öT‚'D
+âG–KÚ¶¹Ë" #Š’RºS¾1ÅT>xAž„ãŠ?‡yí"ëå³aÞ&¿CøÊo/d*÷VP!ÌTÅݵ¡Sù Ž5œí•1ô^ã,XÑÖÁT¾÷‡u8!¦;§c ™<TX•$vMWŽ†E¬²n’ÿû§Cb o µìKÇ|*`¡ê‘Ùý\ÆÍ;6{´ç2øny®èD¥ñRúÔ‚²Å“£[< 3"ˆR:N†çšYz/ž°‚Ó ½Æ»ÈÚRø`­pLLpÑB WÙÌ®,iÖÞ-Þ£,^¹€O²Ž½À*0Hbƒrê]ARÉ·òëØLDÆÅ?ÔòQkywŒÎ$Ó‡-ËwýÐ-4eO-0{+þ>Ñ0¥¤H_‘ŸzØ"â¦ý"”M€1õHº yxVìÐâOûvZdýuˆ¿»Ëdß*3ƒ±šr]WÂ7jÝe~è¨$O.îídÝGt‘™ÈºU}Ëè,_6ï‚¢v1>ÌË´e3^Ô%+™°­õ bŠÒN2EùK!C„ÊV=/SxóH„¿„T(±ëODeƒšiÒF{|¹lаI¢SXÓ% ˆ®ðbß<•Aj±èÎÙ=D•CïÜÌz‚ð,Ï`í_RÙÓV&¤¯‹@YC³e Ÿ¶sT¹"Y4L Î"üÒËQå*šßDš°¬Üz‡8GuŸs³Ý•Œ ¹k²A?ŸærT÷û^ŽÊ5“/(õ.§SÏÄ«™¤´å.Ù]¦v‹=Ãnl›² y—n‚ÞO´ M)‡wÐÉ5âÈ»ŸÌ:›H×a‚:í)lk+ÜìC¶¹Ü!wM6hÀ©z; âs›ÒifÙßoÓ“–|J|ž"zâ騡
+»xŽQZLǤǃcê¹RG»CANµäF¨}J@D5«~˜øAöÅ.íNÆ]«T€S?„ Ñù**#×áÕOá‰W´¬mNÿŠ .ÕGµ('2™ÁrR1ÀK¹âHK–måœî%/Ÿ‚‘!é ‚å$/Jy¾!C+ü‚{Be·Zr÷¥Ç©Àð0£Åy!Jï€G¼ÖšS‡@0!­³àU vPu édì[Ǭ"ϵ§D¿dÔ>ȸ#!÷a{Œ%˜
+dM¿ègdàÚxóUCKÅÄ'퓹:bB°¡fž¬šIµé&±Ó\" }ص«¹!ó9†ñcôûÚ©AgÓvùKÈ q\Ð ¹:@›V’òËÚ}ƒ ƒ§ÌmÉ!¹ ¤¥?Œ =v¶ÇÐ…ŒÂ+Xÿî’1!wQ6èÅ(dªù@?«þûýþ ªìÎRvwâ…•è‹ý¬-ñWKŠ'( éºàÕÝ¥.©ˆÎ‡Ë؆¼+7AïÜÒ[ôQ¿'rHRG„ã´úE7±eø—Ñ;[Ôs’xÂgàäwº“q!WQ>èÇãD
+XDNNÅ<q„$9 ¢j/¶Ÿ\Š2¯»ÃoHf'k=AüRdņ9Âê‚v`ó,.eò.ݽŸì¯Èë.¼®Y.´:¹!{9É÷“ê> ¦ßÙ*!¬é‡[(ßå]2&ä.Ê á˜8ed"s‰$´úó8Ð:?ã†À!BÊGxÂÂ=ûÛÆ€?t¿Qg\…J@¯n´1 z¼cd÷bþG㪒ˆEÌY|ØÅñS!"ByTÖ¹êfÞöÃÚ5™Î^‡e$—H†*8†1¶`Í㛦‘#6%•@± 2ÂŒ/­#Å¡3¦Jæ/dWm=Wïõ:žsÐjˆa“ÿâÝ`&Ðv’œÛÐ4Zžæ“H¬ |³ýí“¥+È™7ôÇÐÁÂáS»aû!¥þcÕ²=ô™ú|¬­%¶l?OŠĤҋí$CÄíHcÞTzÈ°ý”Rl<Íf5 TÛÏ™%«ÅÑÒ‡¯¨kJ€¢«++<u7®±Dµz¦‹ƒ…òl9•Î"r‡]IÀxä:â¹flhê1¥¤©Ø0 µ?Óõ$ÑûœSø‘9Zm}£s$;:G>âµz.èµÂ¨}1#o‰^¡Jß@0C“fI–¼j÷|öct$j¨<wŽg¦aU1ÃÙ&rWeƒ~ ­Bj¥bjûv¸Bö‰`²16¿áäE³1üSÊŒ÷;Êö¦‡„¯ÊgiÜ]
+‘œÃƒÀIŽ|È»pô~@àM̨ohV\ZÿÈ®†t­ÓA¿®ü©Ž)€®–
+É‚òÜOÒ¡PXnK¥žFÖ ×—(‘GýÊ
+ªgíÈ©£íEu¨òµXUž‚?ÀV
+©´¯PˆˆpdݾL6YÓ„åѺ´œ©êr¦4¡¶ÚF§! í9`æ@ô³©´'P¸‘
+®”µ"ý†ÕE-Þ©ÅHõZOúNÞÃí»Ä€ÇÙ`BÎr_ïtho ²dœ¡¬»Ã(i
+’×µ¹Îðbì®Ã”!@š\°åa¶åRzð6jn±.ýÚ©B¿P\;ÅFÕ9üs–ÐfpX_‡± QD“™%žÄ½‹¥ëYbòØÐîm½¶Z¢R>‚Ä׳ȶŒàmE'¡“SwBë_Ía+{Üo|é,I~¡x…ƒÚÏù£Ãí¨$҈ͭp…äºë¹¾^³®+ìyøÄl§ƒ²ò„j}Ám$FèêµÕv„‹s©—+ÛÉp¼Û¡æÚj>•ïMx=‹å3ð-ÕWÐû¸Ãâ&,
+Ë9&r;"02ŵ‘|„°©1—‘4»†¹ØîøF®í±ì”d¯íŽÙ²“[BOž
+~5ØÑ-#‚¯
+Ös¶$«HWŠ­‚Ø`wCG¿!!§¥Óšµ% C×]†/ì&.°É¤ë$¿M˜¤(k¤ã5[qù AËfs­X./±ÂámÑáy–= ÜñÑú"‰#aXš¬qmåâ=ãw‘Z–zó˜1RC·ºJÀ¶dŠ<ðY‚Ñ,ЯùdnÂjQÓåÚ*™Ì”r [}‰/¹iñË\ÆXŽPh4ïâᆸæÑ×óÖšKëY#Ó}CùçN!oäg«öÒ—é½ @Ÿ[%ʉ‘É å¥c¦Ã mÎx•Èu{ÃbíeL=!²ãd‰ÔØ%ø-/å`»Œ,YàÃÎed=êÝHk¿Jjø ÌVw§ï•w}žY´”íj_˜®€PÀP¶¹÷xˆk xù
+ËÓÜ;BÛcíÌíî9ÇšaûÔÍù¯˜îycYË4"qpGŒT¯pgjö6ÝËÁ.%
+x"pÛVašÖ¾×- ð­äËCsãÿ14ÀQ·ë0W ïµÁTÒÓûiÓ7L²òôôöæ3jÝ3ºq‘·7Ÿ5Ä Ó&º¶»½üQ€4éïNìõÖ¸>?Ü{{qîBÆá¸cZð÷e@Ø`ÓLXh&q¡‹ӯ̒ugâæt¹ÔZ Nõˆ˜±Ï­ª`![×Ê¥Ü2­$sËL­0êRÓÎò[±AXˆÄ'B¶Z¯¬jp&ÅãÞ ò¶5îLˆrõiTY£çãƒxê%_" Žv’möPƒ13%Ë3“®8拇l©Ý’@šöd«Í›Nm;ž0a$`[[Ë”ÌVSƒš;–Ü’›ÈjüÌÂ|~üóñëã?ŸþúøËc|üáûõQ>~öõg_çŸ= ÄÌBk²m[t
+\Â~@óý[ ‡€ðÛ&c¾×ürÖ4Bë×vé+Ñ&5^(E=i-Ž^þùÁ"X‹IDÉ9€†ür{«‘¶ýå¦èëÜí—Ç?ôâOüôßÇ¢7|//Ùi WϹ$±‘ÿI¸<GÚaœ½e^å‰x±·"V…²H^û:Iu#zÛ>‡Ö³ç,ãZÜ£œhI%HpiIQŸYǸ»jz¶üdÙ• o¢j®imúí†à…Ú&NèëRñÿ—ü‘#c!~´:ãB4’­ E#ü%´e2ã¦ä‹­àÖŤî\Ëí:ÌYÕ2­kNstÉ¡Ä]·»Fä”çÍßjÈÚH’Bs^ú}d•û<²ãuàB¦„kËi€ å9šà;YO 9 ³ýËÜ|Š,~XîJ¾ÌÃxÓªöq·N8m §-6ÏÀžåÍI¬÷6w%âWL ’ÞÌK|óMÑñÌï#|kR¯ž}×hÖG•&ôDÚªW·]ã{âà{É—9îæ urSÇLïëðM2”‰mkN3žŽÊ˜Ën¹•Æš N„·Eä>¼‚›×IFÒ˜Á2*{tãú4ÔÞ¡ïý‹ò€‘k3Éü‰‰÷"À(¬00K4ÿoàHÚJƒoaüéàódŸo敶S¤7 ”¥Næ8¸@fqçØÚd-\Ç2ëÌ’)Þæ4†VZâ_;yÍ#F‡ Ï2*²ìËL>ò^’— e;{¼íCp³µ*žõÔ—ÏŽ¤· ÄŸ`ÞaÎüAõ÷hði^Àg#¹¸÷˜÷ϯøu6Þ7£ ›C¼†·§Í‘(²X¬‚¢ôÓ“Á‚j”ÆÔ¶
+B*]Ä99¢Çs4^‚aìi½‰=›—ÓêÔ‹ÁÖ…•}"÷¥ò7Ç=DWÉóÚ0;+¿„çÀ •¦² ð €nd;‚, ăY;>…Ÿ÷S˃íúçýK_Vg. ­†/a¦ÐŒÂAïîŠT*ºìRÒuï†Îæ­¿ÿö¶Ç Ì¤ï´päV• á¥( 3')lnIY|]J4°|7†Ã 8P¼ 4ŠZX2@ÕgnJ{MÀT¥'›H®AÀbäÎÃ2k{vªÊÎNIXª!Ž—¾‰«dŒ.Ä2lÔ»Eçdô.?r Åëž5òèªõ€ZƒšÞ\)r¤ëò«¶ Ê$).¸EÙ€ë„ð $¾í÷Ž~
+Œ_Þ~u&QhŸÈ”ÐîÑmèm Ãjf9?1àé«í“s¿ R­+?˾‚ÿrS
+àG&“Êu/ÓR@”û@/\y šæ“Ô¿=‚þÁç€'ArJ†¡Êei%E!rdßN­¦á÷¼1ü‰UÜÙ•ËaJ½™`ŠOLŠði#Õs($|¦J*iæz{¤%š(«¤4‚åa… 61ŽR‹-ÊVå¢à:fš'é`KJ°ãR¢("ð‹Òkx„©Áp¤vµƒ–ôÐÆü"4ÀBçHBBñ »Iˆ2,Dc_$X MÑG“³ â=a@ÁÇ%Fr¥ª‡·Æ C¿á鼆@Å…s˜+–×ÀZXtN‡eÉ£Ž«þp¬ðœ.°‡°L:úª`yÙÅɯڂ ª€( -S@’¨øÖlJ…G’©8’\M céEqÒ’”ľøÖÜÏ¡›£.Ú»doÒÂò\Ô ¹@H¿
+Á—Dá9SîñÏî@ÏD,ê“=ÌC¹$4Ùí%ß!X%Én§>Ìàô
+Ÿ‡Ç cÇÒL8wk[À_ˆ%¹Ùª»Æ%¯2€Mlî!ŒHp˜ÅCJå&”*ŽÈ‚½þ¬x΂»{xÈ0Š\ÚVo§’³&ËÁ¶­Qˆ¹™•BE–øk »»“]~kÎ[u΂îñåîká#ºî5èq
+(LFœß~òækÎbdž±µÇTö é$8’ªÕð]4%iÍ’•9e¾¶¬+¶
+5‹vÀÒMÑÇb’Æê$`GµðaZº¸Uz‹ ¬¥„I—ÎÃ(3^†ÝÚƒ®>ÍósQÉXlKCÇp`OUþ,‡b}hL¸j²äb¢mH #ö jRî€;Å’£oäÍJêå —#ˆ Úñ9KVaM„EmûA¬088……¤·|Gè´Ýâ{ó|^_õ9äÝ/^I¥×¼86øÛÿKWêü?NÞõåGñqVÅ7êG鞌Äÿ£t+bô™ïâ|æ{pÍ—ÿ1Ò‘&V‘yÌíBúãl‚ÿvÀ£
+ýÿ‘ÜïÇ%úï°~4šËïG€ýàš/ËZyF€;þ쟳ñ!Ø´ÃØ*tÔÒñx;"Ÿª™Qôûq‰ÞþëÛ_þ.ùƨLsaçxIòL‰¿Hz5+TÄhSF6™Ú"“¹}DA¼…]Åø<ˆ©Ä}åŒí­+Ÿ×á’Œ’РamÆ`“l® %äeš¨¨
+"ÇÔÅú+kR>ÂŽ|ó8éš7×Ê•™ôÊÀפQš[Œ!h# ¶`õ:Í݃‚¾™]íe¥:Úwš»ËëZbÀÚ™½ã:hºñ/œgå8hïÝ^‚£ ½ÁAW¶Þ9op®ËÍXgkvò×:Ñà è`9NZ7a‡ÅÜ‚$é ¾J"¯ßÊ´_WÖ¸¤†fG|Ý?
+
+z·_w´·%¨ÀALpÙÞ”€ž?ïœÌà\Y‰Oô§V"#Õ.i•–á5’úÆ¢½ø“‹@Ô°p›RPí;qŽ‹ÙÉ,{ѹ«åQFéwé®éõÑ;Œ†(¨åŠ°j~Ysjaï3ˆºj\ÒÂRTÖŽ3ÅŸÒ'©¦> ! g‚`Bh!FÖ¢-¬CɧøUðeÌ7y>)8dT\ˆ—I‚ñ6‘2¼«L$ŸŠ:Ô€c™™2³Mëâx!¥0[ˆÒM鲪#xj“tTã[¦WÊx,|Ò¤µE`"lD5ÖÑ|Mº°"ÝîÀE<š³7O¦É¤õìÌ~Bí_fªMUg&ãQðõ]!¤N/‹ò«èæT2½qACP EO(ðá˜Ï«^ƒŠk `—Ý)—12[ÖÝ<}±ODhENæ]¥©CÒq³¦׸0UT ö»QÙ±eäÈmõ†»ê™ w¶’^Ò¤†¯Ê)¤!"ù¼ʹ—ž| DW-0Íbïâ›ðœ‰5¨"å|û,¾RQ«J‡Ë…t A…;b²¤¨oݧš©É€Q&FñŸ¯Và×Ùd㽬|…`òZ¹¹`^ù3
+ü˜|
+'dÞ­ßï
+Sß•}Ðq¿Ê(:¢-õ~”ÕQÀ%7ø‡F-™¨¡˜Õ{ÓÑpR)¸›V[Zaˆ–’¼ð`Æë0ÅkÐ# BàTÉ$àrWºÇMUë­DØ"„Þ3sT½ù)<]üƒE2ñÅ“NJk<¢Òú¯Šx2ã\‹t§“ì­’ +ϦaV6à;„ÎKˆ®ªWí¦¨é«x9Ç<‚¦î_eUÑ.ÇÝtgy£<€Ÿ¥çÈàhZI¨Ì™»_EoªH *¨iÞÕ1±äL*ÅÐÌô…«…‚ÞDcö:r_Rÿ- )('¼CFJë9{¯AØ^Š¼phxzÊ "Œ,9=Ÿ”ÓO¥‰×Óýªiê&0žýf.ãµSÖö•Ž¶$¹nLÓ*Û tæ¯ ´Ê¯çæƒ%’,ú
+îÊš™Ù™Ø„•c#ßLËp€œ“Îùx\&3+ޔɗþÁxÙäH’ÛPøy‡\Ï" QK£fÙ[ŸÀðjrëûû{Td•ÁB c
+Õ¬D>¾u¢ô &FU ò­ ˆª°Demo'g¶„Óä[chLÃuf„)ûêùÃi•U>å äzØ´¾Wš£à¸¼}ˆkÁék.a 0dÝ°Ú<·ü³dwRë/)Î<‡õÅo˜ãXöª`ûVýU ÄgÛzw×@
+¬4{×r/{§$K&5k®¾çJI”˜ÌÌo“þý+PÛ’vw‡Ûä¦6Ë‹¬-Ð2sÒ¨œ÷ƒØ@Ú ŽÚo§{žé:O‘¤µ‡Ö›K™¾‹s¿o[G5^ÇpûZæª)
+#,ùnÿdÖys‡@èÿ=Hˆ¸õ¾{ü— Ñãñ¨ƒ²‹ãCyt¾[Çß±K‚«×í¢Kö‰ZpÉ>7ú¤ì“Ÿ?ÐTÄ…`A›g*ÁSAÂf©¥Ì¢Â¸5ˆsFEÜ f7s¥‡•¨òHAæcêf>N7Kº!) ÑÜ`{}•PNTÒ²n¦ aÆ€õä¹ó³ˆ›¡¶¸<a6)ÉÄs¬ZaÔ¤.(üMÙŸEH\ÇIàYäg<"ô‹ËCøq­~XÄÇ›Fí'•Žƒ,áÛ¦´ðu§OtAÀ#«|¯nI—2`C’‹í̘-¤ùf†Þ©Ï¢_Q‘˜6g‹k‘R;Yhx¢8Œy·(hBŠH]öi¥u© :Ž‘$6°Y“®¤hsâ¢ë¢^p«¬b…ÑG’Œö+œÉ÷š×¹ùx3ù€;³£@|Fg¼‰m™\§³¹þÀz×¢ë˜ll6ùàí!ï]Ãv1,¿ î]gw«è:– 7j4]‡õŸ'çd-åTS2êãôÉ êC­ Çß°Šðl
+E$7r°CQ Á´…¨“¤òCŠÍ2žºL'E(xR2Ëõ„É°½»Ž-€w&ϘI©b.ø»ß«›åý`Ò1ü3¦™áÌqÂ3°¯pÓÑ•U—çÏÈÓnåU> +w—žÏLÌX‡þ±˜È8\ŒŽÁ¡MÝo^(–þÍû¤zàaO1£¿)ó%‹à0 *ÁŽ`£0ô£š,zémªŸ¨FTB_øõPkHn8™ ˆ ‡[D-P2°
+^:piëܼk ’[«Ëò´ŠžÍ¡à“™7!ƒ¨&4M+3a'1—îo?K›vp/k,Oö_?#=CËëxs9©Û1¬îøä%¼i™ûM"Ú©\t0|p_½…F€CZeÊŒZlÜÈ› g¸þ¹ &ý,]m¨'bb=xŒ¦É…9ד°“›.>Š^^„)k:=:ï”<j˜üÕà,{F=M!'ïaN p„X ã%ü3O4„¥Îý(›j:+G*8;ÜÀd5þÁ÷[â$—KôF "ìh׈r§Èõž
+Zp1+7úäí$ÔU-'Ø[5£2ŒxjCªøC* Š‚›_N .þÇù½aŒQA;ä³Öéêj
+¹—Gí}&Ä ˆ—n%P–RxÆ­Ý/Ç*á*²ŒÎ¹ÒX0D¬!˜[Q :
+!ÅÖ¶f2’mJèPÃäìÎv%1BwÄ/8!Â,˱;âpTœ˜38:¯è;<0ʈgÅÔ}öƒYƒ½I>Îà Æ‚]S À£5ôÏLGÑ-S2]BIÆV挛ä¬íè8˜¹d‡§ï:æÍF‹QoÛ%ß÷…_@U,š §fÉu‡D Ø®æ[ÉËKðvXQð ÑíI&);™ÕõÜ6Gò7e™æéLK‰…)+\'ý‘I“K™ÚðÝß:A+KÅ欷9¢ªT<sÑw&þÄ.ih$‹Lfàƒ× ‡«7b/ÁÔLÂÏý‚ýd9Í-¡ÞÀu!þÔöîÂ3<вXûݵI˘$_>ái­À8ÎòöFØ-ÞÃKpT ¦ÃJ†iî)h3` Œõ,^Bkh8¬AjÛK—Ä*xԤÿA‘¼ß톃Â8kžAÞh©½•ð@ô+wE6½Ã­·¼É܃••ýépF—-äµíôNGî>Üií°dÌËjµ·7´žâèß!pdå¡Å[BnsžmS7dëC磣æv:Êæiöú¥À m°â²G Ä–h—,E |m!Yžö¨T¹Fh{î E(:Ô°x$Ú_a`ž1Ã,,Q"!›»6Ìf»[T OK„1”<›wÙôš× õ”ÆþõïGëþŒW¼ÎY1`ø"7~øçÑEµC¸†Û‡‡ 1 ÂŽ+fy‰9úŒ±Ä€°Œý
+Š@–– ³êŒlªèzÜ¥èuçNzizþë¿¿þÇ÷ÿþy
+tttbÉ»[$u§ÁDiB”ý;4¼'ÙTÔ·îƒw±è¬ß.aœRo-à.aš>÷0Ê×#(Z üB”]üÉ}^a‘;Q¥RX½ì§óU&^úi1Æñ¤H@Ö¢§SÒx(n%ó§ý|úG )Ò´ù3|»Cƒh"òµ¦»Žÿ3^'ÇÃ@@Sq*·&•Éÿ:ïÐEĔ쓤jDwÿí3Œ„Ý!xkŸ'ü‘2,ÓXú£¢oNž²òD¼Õô)vËÂ`_B¼ïWaUÊ}FlûŽ
+Ùë-Û¸Á2:GïýJwwh‘¶µŽç,#
+H‰Œ—Mrc7 „Oà;èyE‚Äד¥o‘ª¬œûoó¤¼ð£ÊªÙÈ£ 6€F£âMK•õñW »ÌFtåÏbúøüø÷£>þþp‰«‹úhÝj :Ê5Lºšïaÿ>¼•«ºªöQBš@Ÿœ4.­ªôÒ%1á—‰šD©ut|½s[V20îÔ0‚.ÍBGBå"‚nV[híç•Ú[?`2.¿´ò¹Jµš ~u#¨ÖÊ褀¬x)D¢ýˆè{‰æEšF^z•a¼Ú*Ÿ“TYQ~×M{;cÚU«¶üÞ†Úù²V¯^E£÷hí
+îqß)øýmßí^œà¤u¸—qn÷׈¦½ˆQyqnw@µÔ%{YÏíþûmßí^Æ©ÊÃ<<^´{±+DÌ:Ïonç~Ô`U º
+*T;W˜©Ÿ@Ÿ'Ð)ôr‘bt¨¡TòB<ï ûu<ëÍZV)|tOË[ÛüÎR’i¼eŒÂLcˆX]”3&™ÎpyÀTš¡Ñ“­Ñ%푈@¥‹yˆ­ÉW¯–ª]Ѥ;LíJ©æ AV›Rdt²?þ™õ‘·RH­ôÝQÌê«Ó{6Ï!aBëw ÝÝëTV^í|ß.Ëž®Š54™"œ0P ^5æ=B0Ö™B®dlø<Æpæ ç9Ð …Ymš‚è^éOR“O‚…’¯fdÆò!˜„°lMÊ—åU¼’Ä Tœ³ƒâ
+F ôò_yOü”Œ´.MI,¦c¾ùŽ`ê#€$©ºYÌ‹î »|„ ª²Î€Éi*¢Tõ©”>Õ 6‚”Ñ0Þêð(
+ŽAJhÞy
+*t2%¹t„èX™«åx“‹àbe*f]eÝkúËEÞyb<®{‚öaÌ×™§¢)…clÁJA¦Dè¯&ÇTâ$bO!Ù-WÃÃÄÆÑ·~z–ãHçÕfçš+xH´%Ö0e0Gñ¬®Ì9ôÕíÂxL'õ9t·œ!å)bhÍÞÌqÊ>ã©ÇŽJ¹ìÔ ²¡KÔN8lj±±4í«~‚È\Â!èÖjo½ö¡øÆYZÉ9ÅÙR[°ûÂFç¹C¥„MÄ %1b-{lÑÇÀÂjŽü·—J£DþËcÀ=R¶u™åJUËê„žãž<õ±*"}…Ï’çcxcÇsR9ü½6xrŒÏš`jÌ•pð²Øñ
+©¥RŒ½cìq™žqËóá|‹s–­±Ûó•å¶ ^«YûüpB(VšÙPk B…P‹üu¸f ‹€µº×
+£+
+Uƒgisî2ñY@ȱômÈkn¬4h¾!•n1–L
+wÿYvƒòLkÆÁ'ÐׄÉB§fáÐ&ƒ97¸Î}ûcº®ñˆ\²– ¼èÙZôýxÔJgõ,·Lè^I#¬u–M®™K&xÛ©bž÷é³íÆ©n~ –æ›nBôø‹cnö“g{rFÅÔ¬ß êŸ´±Óy±ŸQƒIÇÇæ
+Чô£“§¾ÓÛ–TuÖ§§FÖÜ@ ΠÓI<¡ŸæSäÓt³û§î˜ûÛR¾ø¶eV©±#èpÐÉÛnñÛs·ˆzeë7ôå?íª$ÅÇ•æ Ư÷ózu
+¯Dw¡öÿM JË})ýù9¶ñ
+dø‘ˆBÈÊ¢•uV$wv
+¨ Éø·¤üÙ'#•_Ñrg5&#6„}Š¥¶ç´e±ª œ¤íżW6ÝTiÒÊ¥§s;Wɲ* ʦ÷¢ÜÀˆ-2!™ë”š†nõÇ;{î¬Ü/ó*¿RÉ5·(9 #Í•¤b¦8¶nÏ¥ŽãÎ%µû02`ù7}dš;ü™3|@Žà;& )%­m×Y_–ãms6ïâäJ9sÛ[—t22Û¼:U—gzŒØMÁP¢˜IÖ PÌþ3eÓzn¤¼o’;Þƒ¤-af§ŸµÎ Ä!P,ÔhæÒ³d;á.Áûɲ©Ì—fYÊíL÷²ä°1õ­ø’†1Ó‚_5œƒ;£)cw•çêÁ,¥i²³øÙÀõãá¶øЩyìx󄤈a͸u‹Ü—h§’>Æ|ºn®1ƒõbËtf3Xî\7¯©ùÊ®jÙ»-_Òg¨^®xpƒ;)¤¥õM ÑÐÞWÑà«¥ðâøÖžKÝ‹Ý6ûnx¤gÁîì­”í0ù+1Ï¡($°Ãæmsƒýg1úÃJ'•”ë Ç8@òªrñ êt …CVy úÌb¸+”My\•¥˜g‚‘obè”’+ñÊ–
+#ª‡ìŸï„m 3T]¤Û¯¨´qâ<x§Tf…!o^$feÉ3+`“Ž?S„a”ÃEî`‚äú°ç—¢÷ÏH°+u09U%Gz³º]V u¿v,J„./b»sq·¨Ù–±§ÆŒÕ¢9•€9¸ jSÕ@kƒ
+A[zøk1Y#o{†SCI
+ŸSáÛååÁä„{Pü3ÑÂÇlj‚ß ¹. öVe0«º×€·ì÷wÿ^²ŒŸc_k̇"Ý _™ÅÙaJÁm™LfÆF(*2•£#¹œyÎ_ê•|3²Åë¹M4¤˜Æ`WÆÃaö¦%ÇÈéÚ¡èý3MR?ú–þòÇ—Ÿþò¡Óq¬}a[(©ó·\×í Æ¡_ÿübè
+Ia°ü,'ÙágÇóEF†ÙMpª)î.—ˆRs/‘£–·ÆÌÞ­”¥v/cÄô6ÖYB%iA>²Ã*Áúš¤M*©2ìÄ <¥þ¼]h¯µY‚7剖#JMI~ƒµÓpov(ÑQ,Kƒ¬BÎ/ýðŽIZð0/\õe´iFó()òTø9D·¾žM4Pð’«g OE N€4–îÊã÷4œc)Š²²C†·{ÿ¸1—ÃÃâÐTÁ]`‘ÊýR;¾üR2›|íàí;÷9°:ÌšûÉÙqšòs]´`6<sœPÁE'?QF°Åý¢˜ƒ‘+¼ž!*è õ9ÖýªÿÇñæsc')‚“Õõ9ˆ#°Jµäê¿©'0?Š!ø—J˜0± w³G–¯(ÊOÑK°®!桛͊ZŠb c¶.N\MAo¶>%: ä[™ `K’ĶE‡ LŒ¥>‰U½‚)pÑЩCÉ„|¡ùpð²züN–;E»`µ1Öàå’Ò¨F–ÈGÈó(³6òò¡sD§Á$K«"èQ„‹‡\øçŠXBè~Dpý«ˆì5*³úXƒp*è"‹7ðåL󼽄l¶„õÅ?²4äDÜGŒÞ!̯å‡Bõû4Œ\"x­ Ò×a\JæØ="’œ>ãfˆ#æg¸ qŠ4 €šM|IˆX§(ÀÍGAºÐÖ ùýËÄ©‰úÉ•q?k~çŠv¬¡‹$‘À±DXq,ü·Nú`³´„F
+‰4þ¬·(ˆ<F°Žî%lÛeªk‡’ßý¨€3ã¯Ëù;¤N®:ð‚ (ì]'ƒ6©Å¨ÑK`+DÝÒd Ö˜ýboúÐZjî@­fHÖÁÍÿt‘¼ikHâ/"² (’ˆKEMÏ=Œ²¶­euÙz-]ú‡}êÐ&¨ /V¥VÞfð'äÿÆ™¡Ü®
+q¸ðŠ‰V/:~Þæð‚“úÊÓÆfº– 0Ø­ô:£+(a2ÀOY}
++£dÙ†òÅ4Æ0¸µÎW5aZÖ¦K¸AðC(Ý&©× ´óbš¨°±¤²éìG…R ÀHeœ×­h³V)y¢L(Téþ1ðŽî¦G[¨'|’®ÞVXç!Û<çw´›/ãO’J¸/‹*:,é¬Ì> £TêÃw±€•ì%Œµ¨xðË¿÷¢íW¥÷ÜÍmðpûîwÙlFcªy[ >$ÐOU€ÅP\[Ú„€©™EíýXQÔ)‘Ê\ó:è^Dt„ˆH¨(¼Ô²ÆÀÁö®ÓLŽb¼ÛK ƒA°eýÂRwñPÃ*ò/yï:™…Õ<é™LBDÈySŸ;ÁjöÄEc å¯äÁoW¦«ŒÁ ò*ÙÂ"ÕèéRôí¡HíáNK»éé^Ö¸A ᙜZe£<î2ÆL’(ÈŒê%Ú{°œÓ2ÆÅ.¡f¬/#„2@K;|yûh><?ßáȼˆ¬g Áñ°:¡S–çCÉ…~4 H:~Î@4MÁ"ÿ#ƒ|·b@Æ„z¥Ó!èõ*.W[& •òÏÊüÈaÉ5Wl$9™P0†G.kêü¡w´~ć4N–_'–MTÒäÚû¡èý¡ˆž+¾Á|a‡§ÕÓëÖ@c$'°”˜g/!xàÞjÍÿ6ÐEx9YbíoäxÞš·yÄž° îšÄLG£Ö„“WE‰}Îg0ª»²|Š]³É ˆEm˜±mlL¾¾MΩ~—ÎÚGÌìÉ> 9ü¡aU@ò©d‘­Á;`>Ú0鎃®ôcá8Àe-H1fEÖfKøu­¡²ÄÃE•T‡ƒ7Øa’µd*YÁÆ¿c˜œÄ÷ °2ò*M©çT& , -8÷"M²_y&³sàPK*½áÌê6iF{Y6Èr†·P᢫¤ÉÄ`äL„¦eÖ"§‹,yÐQA¶mìûŠþ«°ïŸ‘á}*ÞÜ98Çf^ì € ±½ß5ÅŸ—d&‹-x›ð,Ð$:” §ÝÅ~Ãm*æ™HiW'{I.â“æ
+ -à³CPЈýoÊËI² ¢'˜;ð4ì‹Ü#òT«ï¯òyD Éú‰46G³.z±øœ8vB#Û´|rA¼3 d!Àµ­òa+. bá “>1­‚76½5ENe1AX¾ŽHcƒêÛÄüàNªÕO®Š©-bëë~—¢ò ‘O´j6‹KîêÞ‡*£Í]ûPËwmÂÊÀlhù®qj9..'Ùí~ÕJ‡BÝXù|>zg#ž†àÑx3ÞÑw?ŠŒvÉ‹l7,€†9Œ<Ðx9»ÏR2Iò@ÐFØg;)×β“~åž‹(@àíÑVòÄáV3~Ð¥åxJ&ƒÉ‰7sxÅ<vïëWôe 3Å¥'…äJVam— åfb¨]øõ†ºSvEe*Çmaˆà¬‡9 $kРðc›l„kkj‘RY8(Êld• :8‚ÔÁtÆMEÆ[µŇ²ˆ7¾mOƒL²¥ŒœJ¾Ž}d~õCÓ¸ ˆË!'=ýðbC,¿)bè èË«“8U^38“Ð× l²
+×!¶ééåÀ^ÀŽ¬¸ªJ¾ l{ô
+
+öÁya‰¤ûüeË68€ø'0ÑÒ#‘п@xZ·Ëæ°ËþRF‡ y\v»drBV˜Ø9xÝ®"ç5Aü{. Jìׯè°eͺ9‹TÉ‚K„TÆ÷4GøÓwлße!ÇÉÜU½þÀ^’•W®×ýˆ²ˆ‡¤Åšrm…¬ÎûŠvLÖ“ÄÖ(¦|rí ½p5ƒ>£h±ªòšG@UÑ–,Ç¡aomÒ+;g£.©‹ Ú‘?g÷ÀidÓïŠ5Ų㸠þ´âG{’â ÍŽ1{À÷i´Cš,&y‹øÛ­yýáíbZù 5+óäñ=h¾F2lr<ä²T@Ì? ÉU®ÆHåÓ÷*ÑQ®»ÁÔ!0"Ÿ>¨}8GN…9À"–“ñY|Y¢h1Οv¤@ÉìÂS1ëâGxB•gžC[¢j—ÁÂÏYËy®ù–¶Þþû{Dz".C¾9:ø çâ½ßõ/ˆ' \·é rJ¸4ÊÜ‹K(o‡Í±•3%ÿùò,V’™²_Š²iFÂçipwŽ˜ò9ÜÏØ‚t‚›µrÀºìF@Xñp°Øª¦ä }¤"5M°$%¤OjÁ­,¨kåükÃýÛôL—ûÁËÌWÎ1æí¹Ù)E)5¡,cØàMxMc 0Ù ‡ÉÍÔ%GT…Q’¦®ˆŽtS3_@ý ‘í¤‘ýÂ
+TBÁ›aŠyâ"ò ˆÆ›w3K›e“k÷o‘3LØ >VÚí!©R`=D-=m ¨Fˆ‘ÁÞ¶€t›
+4¤çWˆ6?¶z®z€²}CM‰‹Ÿ#“‚ìµåcx0´¡¢‚â¯2cÎ+c#Id«D›°P ²*åè” ْ@Z¬ XTZ‹•|31L bPé¦ß|ŽO¡Åë~:‡/[“uÇLjƒÇXêÉHwô)Ó¼vÜbÄ¿pÈ@tÕ„e[¤¿‰Šèø=þ2íÌq—¼ Róæ´¯n‘낶‘_LµØRP.¸Z«B`X±ÜEy•,Ùwä&èS»Ðe! 24òCÞ'Ÿü KƒO¥êëÔ˜_5mü«å =œ´È³ùvë9¢­Ò¯¡èÒÎ÷TÄT°f 'öñzƒ3g’sݹÈ·ˆf,!Ê+¬4îQP“¿c&É7ùº Ý_…ba#8eÙKn®ù@.ºÈhCvqÑ;qD(—ú-*^ô+ÿÂBá¿Ël“B>we Ÿ|A¸h óÒ—UΛ¸§È#à·ƒ`y \Bö«øƒÂõxqW#åòVÅ:…bmHm’3œË²ð5ŸÅÓR0£‰eMí†Qñ”ƒƒê,‘5‡ô¥ø¹ÑRƒ´Ê‹™RúWq®i½ms!71¡JŽP>Y|©D?r…»… p™ÄÃÞÙ¤ß QHæƒ ßAh…æ(3€9ù£è-!¯#l>ˆ= ÂÎÔ ‚ˆ4ŽQ óƒ¥BøÐ}n¢L,Ø‚¡Gø]sÍÔnµâ¼ýoˆ v­wG7d£¡–ÅPÜ>¼‘D©ÿ¹’½ƒ¯¢úXfâ•cˆ³“qµëÛb2[|rч…ZPÓÑí£“£S vϲúlls}©S"Š€ú#-°ëÜÒ•ö>@ LnÑS©èW˜Ä”$¬brÿ
+OîÁÜá(œÀhTþžÞòª âM¿‚ÑOë2¹CŠRHG/,0¡Œ,˜Í·ÎГº©ì©µ0 "I© ùâÍÎ!YQ–,
+¢ž›º"‘2O¾ÌÖ(ÄT$ãGýâme]AãÒU~d( 'Ë›D‰ÉÆBò>†á®zžY]‡E)D«W1 žüå ¤”” ãæ•ñ9U| /²¾ £-£óu¡þýª£’\¦Žà“ø3¸
+£ÎØè;pßÉù.·xy“!)OÍ£ð˜(ëE¦(ΰ¦ß@[ÙS‰”¡Ÿ4É°;C½È5äy~iÐs…ç(k°ªò|½,©iC¤áâØ5â3ÄT
+}¦.·Oö@ÖÐâe“ÌÿÇA†`•hmoƒØÙJUÓyòØ#Ø«èÙÊšþfZ¥Ä„hížæO^L’auA¨½‚z4£&–ãž¡PŠî¹ÆšÚ›º,y´+‰•Y”n»4 „$P¯t¥UTt¶¼›…,¼0Ë
+9‘Œ43¯ôŸPvéS7¶/IÖ„ÀS’ªß„¡âOSí+±§i+!e Ö M†TY¹µ}Ñé´€LS´ñð§Åt¼Èޓ܆o©ï‚’–ñm$rÖJ=®»ƒ¾ÞÑ?>É°oFí´¢J£: -à8Až6G ÎRøTâ'7ï§Ã_BÓÔ@"ø?@U²Ðô¼î P•v
+ úr†|NßM‹í¥áqT!ŒWµ§^ŸµˆÕdõ¹²¿†”Eúä fÆNqоSCÄÊ·¯âõ¢L†<ÀΖi¢B~JMm®väF+´0„X:°dÚÜÙD½ÈOPÀwÌ»³x¦{¿ö®·@Œ®ût×…p¼î ׄÐ3—•%ÍÏ ¿7ÚeòÝ6o½jX—¶Š ÄÑ1QðÔò•(ˆ4¸IV«“¢WòöŽ{€2b¼q&TãDa# ›*¬õ#ïb›à@íkA „ae‚ Ã‚@ ž¦Àƒáf¥äãõÂ9Ð3P„ ŸRØ+“\ψÝ.b4®¶Ñã@ˆD[_g›ÊQ P#ia°p‹â‚ üac×jLµÅ‰BA—d‡ÉSÆ\Âs”]$º¢ÕE
+ ü{í®«÷8Rˆy‘]«¥9ÂL!oHÈ*æ;Zµ(TH&’œxRö-˜ÏP€‚®W¸lÃm}åWüy‚nó÷õÎîY.DRjBhç}<f%©§°ËóBÂ]ÙNÙu»XÂø§Ø1æA,mhüæšyC’¾<§¢}n`Âhñu•¦n8 “C')ÖAx©ßáÕ‚0µ&œ«éìÔ!!Ôc‡°7eùNØȾŠ¢Gl’'Nç } ûi›åÅk˜I®©2ðùò«ð!ä¦f\¡žÕ‚ÅÀpbÂ_}þQâñ£Sç¦ñ4$‚¤žëGü…wf5nÍ裀hç«%7V¥u®ºPf€—Ò¾]œæ k)ˆÑaEÎ&KŸ”Z$  ‘jpÎ
+ û*ü@™.‰„/ÎáÇ)<ýî+~̓€ÑWED¾U'š†[ªŸáKÈ',MX|¦ý{·C¤È¼ª¼Ò'虂ëÝøõMÒ ûøÏ¿FJ©4'™õsîç3çètóÛA(~e Vé%ÀP,^ÃBøë CUâMüÞt!ŸÕ‡±ìýÈýšø=45µ’xGõVÎG¨™oõÁ0ºáç”ÖrRYÛ­ÕÏû5¾,B+òêÚAEˆˆY•â4i9û{™BëºLõú‚ )’Åø§;¸@(]g<DÞ ªž¾Pˆ†¹TI@Ç]Ð ÔƒûrDRêÇ¥–RÚ.7ámåvU!ã&9âUæ‚ésT'é2¬°®¹ÂfýÚs1;z ‚È4ùIûÛ3Ü{Óh)ÎÍ»-•
+É‚Õ¼Qµ (lréÛ>EéÐË6%zAoTÊ07ͦ½*­±'îcGÌåV$ìpLo8á‘BrǨ°le
+.›¨‰›Œ-¦7ÈflT §ÝýÅ1ÊÒ8#qä/Uב$g®€Š4ÂMgºsÇN:*ÉnsQÐÅþkº›vþoÓ©ŒõCz|nJE+¡78oê‘JÂò»]õ…†X›×:”`HŠ@I/Gþñâ-ì(ËÁ8ðT‡ÿT¢}c¿Çp¾FÞ%€„Ò÷«„zŽH°ÙÃ4à{qŽH…¤UùZXÁ×Ä9â Ò !8dŽ›=®»ƒ¾ÞyÓÿr„ÌŠÀ…@0£íÌ!"ŒðùÌòwRÊGëŠ.
+
+_ÇÉa?Íâ;ÄTâBÈ(Pó Æâ”ä/®g¾ØÅ´i±6NGðFŠ‡gÛQ;MÂùG÷Í4‰þD«„¸¥u¤Œp”ê’ÛöÝ ‚7Ù”è2s¤A‚•‡Áã)Ù&{[¨/g×ÒãsÒg& ±5õcü<¦›Âg­Ívb:[¹]Vࡳ~Ø>µ‹-ˆR,^ab0™º‚?äXÐSƒáz t j{
+k¾Â>óÕ³AN£§¬Š¤ f,åð˜Ñi…V*Lê<¦@4/1™›`eÁp™a»¾ SdQA& K›a$ŒEJãÆíƒR°Ñá:ã’ëGÄv’–ݹŠÌ<BóÕy)Õ ù\õÏ“u9æOXÈ|œeþí jÌg2Òŵ AdÖØo¯ŒDÖäcm\P6‚üòïαRnú!ìƒw“hÙ8ãsQà“Kà3q–Æ/"Œ!•G$ðHõ#Ø4ª;Áµhf"-´“ÉK‘b¾}2ž òÙê£md“ž7|C
+èyvD¢ù¾bc;RˆÁAE>ö¿Œ—I’7_ ?è4ìË™W>¥õÿ«<@sº*ËfL2“8ÌAaÉŒðX%P>ã\¹ü¬9ÚŒÁ
+Š:¢­RW·ë!ÉÀóTºÛ‡Ì¿ˆ¾„*ѨµÀÿÉ ­$ÈĦAÒÈ%ŠˆÜŽ3ËþÔ½ˆŽ×à 2{’'¯Îàô1ˆTIWfS«„¹í Åæi&Üq
+ᆲªlÛm’µ×"Ë(v !¦¢f™ÿ ‘oßÑÛg¦TCƒ;g­(©)ÞUµ~õ+aer*¡´ì]u€£Ø³Òfpmï- ö{ÎTI I´fÐø:ó‹TË÷§ˆ’ 5^I˜Í(l¹X†5™âa,ã½YÏzZ™.Bq†»×Ö “A_>nÐR«~UÀˆLPS—sî!w¢çÇj ÚEê§w†)÷ÉuHýQEç÷áFüö˜ÓWßö*
+\‘φÕ¼?t/§0xÞ ö³u0ê„)æ§xIngb¨ˆÑ­D²Åd{>ó'G"VÑ»{
+ÎÐJyÏ̧ˆA܈ôÙ–Võ-”N$‹ôSO’œñ¡„ ÅÄŒø‘½º4–[í.<æ£èõP¤™§Ãsa7|ˆú[hÿð`ÛéÃ$æµýÐÇY 6þ^ §ìBÓ™ç>:CNðá¡ãÜ°(/ð«4µ-SL?“qàþŒšk—«8€»
+È)êÙ0‘ ÆœM8w.ºöŠ¤$–RëSaö[aļ+?6%?H«„~Æü¥\µ®®¦éŠRÁ
+ ïP” qÓ€&mݽ]E
+
+¥ =®‡Bk†°²­.¥ÇÆ€°×;!ŸˆBPFpJÖKñÝLq3=¬Ã]#ª¹r÷O›¹tŸw(¯‹/—ãÂ势©ß{òpD¸Ê¹=ˆÏ…ìÁº%ífwDã^ÄÕÓ°
+&š¹[ h!væ5·Iõ>äuÌ ”ôû§&sk8s“dG»mj2`è*³[ô²+œ|9 Ë;Š@hxÖúí_•Ð6´©(®•U ±Qn%BEð*²ŸUÁ7€þÅír ìT ÕÈÜÎ¥‘ hv\låBÞŠÂãÏòÌ:Á°ù¡„ó®Ž1›eê^H)NDõÛZG1ËÜÜNíRH“A㪠£?|º„vÜ96±—âZûSˆBåïy÷º»‹Hȃ¢µ¬S I Š¯·¿mdk°žÇfé&‹‘è>¯öÀˆÑµdЊx@È7mwÁ32qG ÅS®³¡d«ç%þUMô&!Îõpmyé=E˜%[üÚÜãJ¶˜·ÇÞÖ: Ž
+FtlWž.5ü];ÊÂS'§†À>O‚‘ŒNzŠÿ&µ6’´£‡¥§¡ë2UV¨jH<ñjÇW²Z •Éíc·qòö³Å¬šÝÒȯ“ãÉœ ·­ƒQ×…rrqÜ´•à]€6§d½!A‚OäMÙ]É †wÑé÷:,„µF)í’\~…`¨{=§"Ø.üž˜O KäŇEŸKz’’^'òpÒ}Çüfê y|()h¢‡È%Ë2jdä×z®­îº½Šp T*  aw;$NLZn¿g¤­ù)îk€C¨ì/÷0ö“sw•óÐ- £(ê¦Kû[–$@ãÊAEZaIF‡–M†|_ T<X¦ë_OD#û¥bfw8 ×sÆŠ‹â´?ÞP£ñ¨<DÙâÆ8Õ©I/e:ŒbÆ<Øjœfê•Ôæ”l©íÈ_KèÙ¨ëÈ£Õ´ažq (0óX½7TŠU$Ö©ãl—æãòYIš©/œÖÀ9Å>¸†’=òTµÛ:´‡¢°ç±KPå(Náþ¢[BKð@&äWßÜ‹
+¸‘7±1¿‚¼ú=öaÿ„¥qWWŸr{BÚ¸PõÆñÁ(Oš3ë»äÊñwir@Þ+º‘¼WtAù Ä¥RU£‹òReˆ'$qÒÊ^IRÒ¢‹ò˜„¦¥s’¾&æŠàÒRaôˆ¸`}@yŠÐ@¸·në ]L¾¢d÷H^²Ó 㺄Ç'yGÀ®$¯¶àúBŒ#nqº’¼[r%y )â…n¢OònÑ'É«Ûy5NX²z(OIåqéQ½á‰T”×LD¤«憃òT€P´Zk
+gÊëäfܽ ñÅGyy.Ȇ¸°Ó^(¯çL@ÔÕGù  ¨‰çæ'”§‘sBz15 ñaÉ_ò~èsQ>êyeEœâl÷†òÜU¥LìÐOw]PžŒR™Rbê.ÊßK”¿9(O¾Å–¢à£<=€àum›~sQž!BHxO^j¦–w‹®0ÿ­ ÿET¤“ÅpЛžiÿÏŠð' ¥ÿŸñ2IŽì†è ú:‚ó°î­o¡­tÿ­_‚d…U„¬ŠðÂQ ñ“@"äË¥8”ô…Q"øä hÌ l&~i ƒÜËc´"^™|ˆ"pVÕ‘ºl"°âF«Ài Æ”@<®•_Ó.B™[ä½iÝFd³’nÚ†OÙ0É]qšqB1hoÍÓ)úÇ-â¥p
+ø6ŠØðùiw‡e¶¹©ëö!ìÌ!¸§[&YŸÆ˜È/¨+ÚÂ|ÈùiV¿Îüaç(…p
+.K<%;õ5wÖ—ÛÊk/l8%,êZàõL®šdÍ‚Ï8¨FûÅ”ÔÓhYyVÄ¢¬W3a¦ŽχÉpðAbì•x3$)í€ÕÉ'moË)šÏ‹WŸ©×úMá—˜N>¢eÿ‚hFH„ŒÇ¦p-4‘kÀ
+<RŒíé ­ëÒÕféAMì8n/[s0^© 5fYLp×Ð/¨³qR(ÑF¥4’Ñô¼Yì'‹;dÞâÂD§]XJXfÙXš…U@q”#çFÅóÒñNN@‚ñï}ØÈM8jÊùïeQoÉKD´P¥È”à FwJ>ŒFãÃMy#»Ç ³áÂÜ YÞ%e h×÷e’ºÀŸQQ6þXnd7¬¨³×›m‡_³NVI K~çi†L’–…?¹:yÏ9¶f³Ù0ÐÀ«•¹Q>Â. =‡â\yûÈh'Æᤡì"f_Mæ]SÑ«âû—5ºÈïWýûcLRœ\¿lŒ%‘fÕlÅ—[”–ÿΊ½âRüŽ•{Ó¨¸7‡Ð+|%‡Þæû`§uïi1ßØ
+vØ™æA‘¸5vá°íM„H"åè´ƒ~“ƒÜر» îÒ<¢ÐAIÀçÐ&öof§DŸ"÷ÂÔ á½¹çàϱ¨aŸ„3¦†„ê°Š¨iÉeí,•ðôú8`/5y§Àh¤EÑ „Zƒ|˜Õ¬‰……C
+$ØýæÝ/·¦¼Wίˆ,…ݪ˹È<1—=-z_å‹Æ°
+ltmèÏ.
+þ´£–´»³Ä@ÀL©¢f¯ûSϘ@Åbƒa<w# 7£P$*¤Ñn‰¬íÔÖ³Xõ¼êÿQü`Ö`¹É?©áÉd9E8wË#¦ž…§ N­\ßã`„1‚^­µ@Lh˜»Õ$½!KÎVö}s6´²ãªÛÖGxAE‹´Ù:
+ª³pÉ÷=Ǩm“5"]QçžCó1¼,ƤE×”
+"Öä‹ÌS•˜aÓËE|.°À… ÂÉÚä½"m´[Ä•6 hþtŽéùJšé \Qu<û À£ Ã=¦!­tyâLNœù>AàßeæqÌÖCìß?^‘ÓžÎàCž¹ ç«5Îò±F¯0
+Þ‘oa£lføvX¸sF-‡‡0Lµ!¬t·sæÐ6 ÀcöIÒn¹ªßsÎʥظ1œ}JÂÑL‘åÐÜsð=†1\Aøbºˆ:,|Ø%™<€@Æêµì-|/7ù÷_#
+ŒVk#»‹ðgVˆM¾®ÚÇô.°FÓRÙ.!FJ²p”Dœ â–»EÕU²¢™½÷O &5`!·éD{Å¢°yÖA¦ÇläEëfÑ'žœ( oY“ã4[Æd’S²æI– ëLFu¹'~]æFŽó¨Won_öÖáÛ¤>^Ø™‡.ó½`_€)wKÕ9îË-Âöð,¤¾K‘ÞTR{ÃI¶Ÿb†Ñc½±«:BCrÜ>±%Üæ8—F±Ž=—ÃQ±d‘'´³­Q`c˜M™ž³õ†~›_ŠPØ©Ù#½Îa6|†±²^'÷`l'†©×…䤉E¾/\ë`ùSäٗ2Iˆ~Êgör’îlâ)Ù~CÃs&ùr,€—/TR`K¦™ûbýA
+éÀ/VÛa¯è¿CÿtkP{š éA̾%ÛLŒ)ò'ÛŠKðÀq¤²ñYlLÙKódž{åÆ¿Fj=6â»yÖšó¿t¤®æ\~å&JÇ÷
+%K
+¤[è3i’!ã.r†ŽiƒzàW~›ãÕ"'NÞŸsâäï[º–9³8I`뛞5œÁ 8ºiËìI&Áð”¤PC¡àF!aÑ<†E™!”U‘“" <ÍÚŽù_ÆË$·Ž#¢'Ðt¢æaÍ­Žòuÿ­_dfæïjˆ6`Ùt°ª+‡`üi+ù|Ô¼«ÅÂÜ"ᵤÈÌuÖ@8“ªulÍ®ÁuIº…c)6@E¾~éy¹Ž¾ÙO]$Oˆbƒæ ’Xªâbvœs˜)‚)¬òÇpò§)ÊŠÁ¹!ÍLlãJ”èÄä“-¸âj¶O*=ã{[˜ºNY!äÿ\Œ½› $Q]’ÓÞÛ?¦T%Ò¬]‰9„ñ7\Y‘Ðo’ÛC(˜Þ4/3×Ù¬RV¼$© |Þ*AZ²„Ï‘å9B$kD1V‡¶ÄUo lߣl9Vñsd¦‰CK¾-vC+Š8†`Õ¸•ÉZ5OEQU+\O‰›à óÎêFH¤›4ž¤fÊËle
+ˆ“ƒB_¶2-Q¬ÌÌôA‡Hi\ùõn¹nÉ_DW1×¼25ùÌy¥
+: ”%÷wü0X­K‚!cÁŸ‡¤ó²Kkh$ëÐþÒ ¥¡§c—F ej̦²Âtüb÷Œ=d†»³r‘GlQekò) ºyÏYMÏäë E‹‡sJWDb–K½4k3HòV݈ ¢ÈôVڧģdŸ¼Ä²ÅŒÂ60!“žg~ØtwØ7ç/­Ñáõ‚íòó-_Ý@M!‡þmwz@:U·ìËÇÈYKf4jzRêü÷ÆE*J9M "ú¤
+‚ĸÎE!ø
+Á4J›B÷"jÂuÔ¡‰­ÁL’|3Ù[(61“3[öh)‰»Ö‡Î;ù¾Ö.Å ± ‚q1¹‚0@­ª”É!¼¡A˜Cµ]HÌQæ¨|$Pœ
+H„¨×Ö!`ýt‡xȧÒ[(²Ø1‰oH’Š)> >²˜õ2ä}Ô‡yaÛ!´“”±Ä²£8:ã°ÿò|dEç’…KU(È%N¡±¬e“¿®Q?àÌÎk†
+À?ƒ/ÎZY¡£ÛÇÔÓÌìÂÂuv鸧Í.€wISŽ©‡é£F[ù÷Å›lœ
+Žƒ½ùª¯JY‹|nXD¼¡L8ü^}wEÃIÞcFBÁÃÿ¼·ò âWmX¡+ŸñÇs¤†Ü‹¡[d“€Ð2,ݦ{:öB®W‰”a lÈž1žJ-”®iõŽ›p2˜\AûÚÄìåÈ7£ú)?[•a§<-ÉoèšI˜48O ‰xåïm”cVbVᱨûÏ1Ø©(šìÀÜ/û¨i‘rÏJ&)k [ôÎë
+´høº&œx±\Zš,‘!5+btWê8÷ P½r5¼œO“¤™ÇEÐ×€ŒWf2—Ì&Qeø!Dõ
+ŽÃÝQqØÈ È‰º—tÑOTiå[pÍð3›³ ¶w=Ÿ3ÜÄ¢â6[”ŽC
+°"Å>fˆ;«â.!J {0 
+„ÓP›ƒÑ‘ßl±¦°>§+©t;‡Èè96¿^ƒ³eñ¹ºï3¤‰<Õ^<
+ýýüu¦¯dRkOqÀÜÖïtÙ¿AïŒp\­wÐcÑ:ΑxÒËrÔL6hÎvr9`–f‰½ÁtPG--7!ß©…ƒØ"]kãW BƒS’±Ê>„‰aÙðhAÒÝaDíQRbqPf‡0 Å+v6ÿA—ÿv“CcCT Ò•9²¤mÕįªJ_ ül»<œS¾0À#\ûák²ñ÷Æ£]ñêð*…æNCÕª“ØÂec[ö¹ÄwÄ·FùMw¾M¡œÔì÷ùcš)PAÐö:=
+ûˆ $Z`…㪷â
+ºA/Êÿîÿ~÷e †ž4u
+{c€Cˆ,PhÖkíÙvëÒB•ôe|ÿì‚Ü—&ÐïÆ[‰-æ4K;3¢Cæ†ð}Á?+¤¡ïqB'ñªÂµÓÍ¿Ò8~©†øuõŒ~²îÛ7Š<‹UÆD˜
+éV=® ò…/ìB)èü9a`ÑÅiQsidHù:œt³Š)zTezó«î þóú‰ršÀ–¦¹d¶¥n–ùIÅP#þ’ÀA¼½P†ˆ »É2[ô[b|࣑ôQŽè¥$Û<šJÄ ö!ÕŒ
+<9ñY|g;íÂøJ€µ‚`Nè
+|ß 1~dÁt±ôévÖJþ[.—ÅZ‡Hrª™ðGgõ¯G ÞÓ”7§‰ ÊÌ LÛnX9ÅÉESí–`S|Û°\‰²¬®`5 ÜÂc®—q—6sáµäÁ0¿wÌëƒkÕ¶À"+\~OÁ¢_£­køÑðÍ$ñK1èQl¯Ö®à”ÖUb'1·n7‹ÀP/Ü|á
+¦¡Áq‘FH,¶J@(êÒ¢™â
+R 5L8kXcn˜Øå2®7U˜£r,ž#@RMV
+‰’»æ>¡á‘Hi•¨¨Z|Úq²$°E“ùÑÐ8Ù€8XüÁô×pûÂ¥•È…À„›tˆšwí$t‰¦Ì0”ÍÏ!åÌÏQ˜ ÿA|˜›Á-b©þ¢¤gQg1,cHžÌMvu9(ú<ED9ô˜g778° =˘Q–­>$@¸‚¬ÚqX|pF#*)ó&ƒY Ñdo†6‹¡AÓÙ_z·9§Rå
+Wó7X½y|]Bøi§kà%K)ÙbO¿ oÅnOð_g¸ ‘Éüf²`Vßåió‚ÂaŽ©(3±•Hu!|Räi)‚’h+ä™–ç/0¹a*òÉbÞ⎨ü2œÕ³‚‰çI” ÚxVš,š%ò²B.K¹_8µ¦53x©'><œù&<Ǹãê”ñ™§ ×-%Z.Òc›…„Á…õgë¹eL•N‡EC$E¼ ‹ö‹ÐS ¼q9 ‹0> žg[„‘€Š^Γ à”Lå6URHDEB~Y®a$}9iš¬(…cîuù¬à;™MmN?ƒhºœî­îç(ËŠ½R.gV0$ô«yµ”|,eK¶¥¼8c§À2X£ë5U˜Äø4¯v:?ÆŠÍ Åt
+¢SÊ¡2iœŠ³*$ïAQ
+±oí÷Æßb‚FwAɺ¯ðœç̯yb'øª'Ýy"ùÑãh!¾Íêã[£ýJ¶_ü1B8Äܽ§}E5å‰$ÂXbmUКΰv3d*Ý/ Úyľwb}°èj¼7ËDè‰_Š‹`¬©2¯f1ˆlʪ}‡JŒý©¸B“ÀK«±9<0K„øN;”ÿwYÛ„)*Oña[ƒ©£aW4T¬’…ªŠ˜0f"!Þí¥m…Ù1ùvJ urjî"õ3¥Êx‘
+d±ÛGEÎü3¬iÈíÜ« aöQ`vˆŠáËÈŠL*"e;ßÜpäVÜ!ðçø_>„(à+AÂöê~Ä1å¬"lR¬ó$ÁœèrÏQ>s‰011Çe
+°Qx1d$Ұ麃ц­Nâ
+œ=™FäÍ=3Þ©C¨›r‰&£­ BÁôä f±…›\½ä¾sZ‚,å.Ù/úïµüǼnáǦشÕdÊÒ&!e€œmoT¤iãE詺S&RR2u9[ƒô1ÀSBÿÐt¾l7ß;`‹ŸÂ`µzÅydQÐÞx¦ÛaV†PèAևЯå¨%Âå%ôs'™§U­DiÇØ™âV¢«ä¹`6½ÔÔ_œÃãÄ7E³ñ–KØ µñGe‰.;­T›wØJëŒ}c±kÔá¨âÛ ü¦G¤ fvÛ¼è1­“@§E½|UCc²4uûM·ÖPÄúì† ‡\Ok°00ÒîXíì%Pá*2ï)ŸA¥¦
+H‰”—A’[7 DO0wÐò‹
+V'EiŒ‰@Ñ-ÈO_gÀ¸Ì¹º/N>zŽÛ1ýr‚]¥¨é%pSØ¢~;°²ö1‹7ãK0œ“a´Ÿ@“ƒLA/ô-ê±oƒ ×t¬±¯YÔB>^ E%…¦‹ðÙ}L¹¼R=êÑ |³laYƒçLú‹4[ÐW‘õ©‡/ÝõˆNF½(ßP¨¶;0¤ð+†WR-¦D‰Õµ0ËkÚ„%ô|Éx)9UâkñYeG$0-H2I;ÆFÒ C°ÅÌÄ©àô[] K•A„µ?v¢ãÝS(‰{Ÿ\¦jöúRÖd§gÔn'ºë…‚ ZÖMÛrêm3£¹Ðè>ê¿[+›Ðc\x(²HwË|XúÁ2=?‰\Ï ¨@”νÆ-öÆ2À2 ,pp»6~B7Šb1a—¤²HyXÞy˜eNíÈá‘+Ø‘Cö l}7ãàøà&3™¹¼!4)~™1Ì&D«¡5ÔHu,"fúübÇ8ýšŸN_Ý·)°»´n÷ªÎüÇöàëÀvÌNGE ´€I?¥ø„ø¥Rë¤W§Ó8QR;_ÆÒ¿`U™Ñãô¨ÜÇÚˆ$ß}ÔSr
+Y¥",o‚Éò. t:¥rÇxs™'þuâñSrNÍð”âß6ÕÝ{ùàåä/ÿYÔa3td°ŒÇÜzæWЋ›=œö„ùþàFK3úU:?ynèSH~ ?Ð>/Þ°5ÝF¦mýNó!ìP…ýë–Bk¦Š=s_©7gŒS›Énä s×Òrº ÷yTnZØo©Õì'ßÁ±tfš€Áíq=<D.6 a•„‡Ñdy5d×0…,y9~| ‡Z°SQx6HEícС`œÆgÂæó.81h¼§kO ·†žPp6cÕ_Qƒ©–‘#ÌZ—ü+1³Œd
+a@; ÀYÚ /…©¬çA: Ê„ÊœÖôMú+δÏ5dAZŽ¤½ÉUJÊünÌ­û' \De2ëXh1uKÝ ÿÉ“ÞÖp{òlf6ÁC·wÅ8XiZúgÆ{LÐé¸@XõìZ…ZË,ì 3ÓàZ
+V ÃÃz¬1](VwA0)8T2«ÿ…Å옳>Ó]§ƒléB¶/†Ì4Íf|C;,Þa‹Im•E·›ËqJPðCrc…8Ʊ‹ÑA䉈ò†VŒqÎ ·S%€”>¸\G•|v³ÍŽ‰‘ëk?têJ2„o¸ ÌÕÊ ýämjyß DÈ!´Èi#˜ÉaeÈþ¡ã¸$ˆ€Rð«VOABh˜‚ÆãY˜GæÛ˜k
+eEà&,_Tj/ ´$MÞ—aA£ KÕ´®;ƒÌO¾Ç%C3Â=J‰\.vrkäi—Ð#’öôl‚·ýãë:Ð]’iÆjÆÿ
+ØT` t&óVJ`íLàuh[›§Å Ñ:F—;‚ЛËøàÍl‰¹êå¼é8°ê8¨¶Âi¾•8ÂAo"R@Z;x§Ü{:Èq£#¸ x(ï’¢9Vþsa‚›Ò'cž£(ÈÒo,é Ñ°ö«8¥®œ?'9ðqø-?hÑ¢VFÍç {}b'?1?H¶Üv”éy °Åùc –ÄQoJ’<Èl§DÙK7Ñ*]‰K˜©TÏI›•‰“0ŒŸÔ
+°m«ëño¸qœ
+î‚Wõ³eas(á½&ýíQ‡£ŠOƒ:'ÝŠÆøZ©?âË S6›å‰E b
+?i¦&£­m=|†‹ôÄKá‹^æ¶~Ñ£¢5~kN„…·‰© z[tøJ9´BV x¢°|¢²ze2:ƒ¢_abhü»¶/ð¢ûqoEß¹Ó/®žüŸR—”E+e+‚3!´íí¾ý½‡
+6)ó —Ïmžî«Uv8ZòW4:,±—6o©ÍÙsì—k¬…O{á³,ü0ø@Þù|n*K&"Zî¿ñ`¼Îý–xSÒõR¬ìù{
+<Ûd¿$ÓÀú( WžG¡§ZºÎ@â•O¸I–ajòÄžMe I!­â”Ûö6ß*Š˜“ã³Ç^¯õ\„“†EP²rH‘að4—^0Ô+ŠRb-^DÒòƦÝë./ˆP¤Ó€[$¸#He/‚!ö!þN‘üi 5ûÃdì™k°C
+Ú1,Êj\V¸ƒ‰Š)½ýÞëÌ1øÊ
+¦F¹¼è¯òÚâßyßøªŒºX]Dúlü9£‡öBåœôºD‚¸­æŽ¸Ò~Ç—ì~°/AN ã¥ý!Š’烎 6vöÊå±fìmTÖÁ"4¿”¢ÆPçjûû!ÌÁ
+ghÃv ÄhÝ š¿V2`—·<Gá^¼•þÌOY–‚„$&þ¾<<Vz
+ß`<k)~|~йœ¨ë‹vÚGZ‚~þ/*
+fŠ‡ ‚O˜¶oqÜáãxÖÇC‘D–Í÷I@Ò“½Oè`‰,%M®ghkD®ë÷Þ ¡Ñ?j†…ßauø3Ws;$ ‰®…-ÜÈæSß9Î,UÜ
+’ïÞ‹‚ö 8.dX–òí¢
+ç6HÁ¢ŽªœÀvMH„¨Õ¡‰vÅäQ‰nyìÓ
+òóŠZ³ëéXç‚<
+ãû;ø 9é×N1˜X
+öÂÝ%Ø"Ø}öË,1äu„ö­h5‚¢`êe&‡qñaãAœƒ¢»\~R3¯tCA/»^/¥«¡Ò‰ æ¨h*ô ,ˆè]Á‡‡¶£l¿@m¤™5,^‚[CÕ€Jr+Xà¼
+W¬Û.vÑ-¿ÃÖ¡˜ïRê1™­ˆD%|Û kdN Ît%-Ç´]È»R¨:(ÑQˆ¨A?WláwÇ,¡©ò/=¼ Ô®Q´Êº- ^UŸÄš‚Ÿv‡hœèS8¢GŸ&uNºÁàY™½¯?âËð“ÈcÍèQ”`„qŸ¥1ÃuŽzkExæEÊD)³7g)Áá>ñë”àéqB¬SÊ>ª"Ùæ J|+PñLÐß&–è;Ö¤ÃeàMÂËÜö/zT´Ço͉ÀðÖâ/Aõó`¯"¸‚räï ë| ˜ðÅ¿â} „{ÑÝ'ÜO Hã«9gdrUMŠ‚<ûÜ ñ€¡ß‚êâ& aB]¦ýz(º½.½d®à2 3"ïiºUcžð‰w»ø{ÑÇw^·›ÐÒ ³VD³º#ïZƒRq 5U?B*ømlάA·j(k«C™@v6¾zcÇ8¹Ã™Ö_ýë;ùÕÕ>Í\
+ž“õY¨…‘,%wÈÍÛš"BRˆ;Ó‚ôùB^O ƒ ÑÎ53þg§å¼3 ŽB]°÷pÈzø‡ñrG®äØè
+¸Ú2nÔÿc*æ™rµ‚ y”©ýë$
+=ñxï„ 'Y]$2˜
+·#<GÞJìƒ
+œ×ÂËðJòäÔÏÒµ‰ÝÅ:OJZ×ê
+‹Ãu2&Þ{IËs…#ħFýx @‘®|+GWû\£ ¯èJ#ùkHo[I¬æ !#Uî:çÙDÓ‹ G
+@¹¸
+L×Ø.vS­î[¨ (çö=S÷F¦D-¶öE/?AÎ\B¢•™mâM Ï)JÒ¨Øámùm6Q=ËZP
+ÜEZgpËüÕÌk_ìd-jÆy0 cÛUÒb,gŸ‚åk±¬,oAYÈ8%ü8¤Vì½}éA*È£ ]±VÞAJ€K:F´nÍŒ@zjIJräàÊ„Äa^¿ú<—ÖÀ=6®íZÃ7&F}·WXªW9Ë잘Sa5š&HÄ«•Å*j:"a 46¾J”DÓˆ<;>DA²cýŒY6Ê3¢ðVT¦ÈOgâiiRã`4ÕlñBCçáqtùx'¥ßM;Ä«8+¿B»é’AîþóñŠkš¹Ö  âUL#¡åÌä@FxÙfU$Y§BeF
+DÏÆ<—¼ñ@ÔQ<—ÿÐ`Õ°ÐO¶28\L¥pÌÿЇAFaÖ7¾îâ´3Ácäæm~qÌÚ&§Ìá%<˜ÒÊ{È­”08Ã×æßùoþ‰¥ùý ‰L$þZ˜‹r†gb¹”ŒŠ4cêÓlâ;¥’ AwLñMˇy †_ÅÿªAª5ÁÜÝO‘Z2Øèü6õÔi\†vR:ÂÆhʹÜ\Œ(uj_«Ûæç=Õa— >»ÅDŸÂÅ°*A¯%<‡g²9³ãʌڳn‘P’#àÊÍ[s=å3ƒèH33icžÍ$
+?Éð!kL•ˆéaB _vÚ±p'lwëÛ Œ€‚àŒÆt@ »"{ñṡŒ%>bÄucAAYt¡ñDÙ!±Íb»Ÿ|
+ah’%ÆwZ¼¸ŸƒThê”ÝKwÈ¢(ˆ/ÎF•a ÄŽªrƒ¤¢°IíKÛ×·ƒPQ•ÕÀH%
+nû¾µ,ƒTÒ~Ç6q'€œOexƒ
+"Mk†ç­¯lØ™Tü܆¡k°gN»â†§ä_ˆ,¦4\] þyHƼ–Ö^9CFÈï
+vLÓj qˆ¾œõuþÇ ui»Ò€
+fSA¶ &Þhq-Çr$Õð›—AOøñÊ”j®çHÌEcJ „õ¼ŽäÀ¦
+o ‘Ìjˆ¢YÙÐö±[bPÝEY¸ªs³·‡„8B¹Ì1¦ˆ«Z] —j- TÄ£)‘-R\‡^â3\zlݧI(ó‘ªû-Þ§w²mß^dnÜûå;ö)ìNžâðNê´ùC~ÿå”\c¦¦ƒ$PEœÃN}Ûqñ:]ä€ñÜŒÌfË(<ª\SîÍÝ–“câÊc)v”€NËl$øý¢&
+OžvGZàÇ6[
+чŽÕ|<'™°^¼‰0+ ¼E6¥M͵v’àG¢?#k|i™"¯.Ì8PZ>R®4ö)“èá •“z%ÕQ6“Š”®ìCñÌç"m*[é¿Ú»i³I¡•½£Pöý9\‹bü–«£tá9Ì ¦µ$#,”›%1“"‹A¸‘/Iu=2F½ç^Y—¶}J!HÅBÊ^4®T'»ý—ñrÉ‘+Ç¡è
+z^¡/%=­]xZµÿiŸKò%àÒ@£Ð•ÅžÈËûÁº#FœC„Åâ5F
+ûœÜ­*x2WSJL¾Ï>sT¤j„RŸç{Á˜Î6ÏX·.ÿeQ•&XÕäÝÉ_‹ì|Dw+¸UØ@ÜfÂ]ùŠSŠºt à&ïæñ%„Ä™ãN`¼®žÉÐ|HQô©ÓÍŽ«'Á<Æð Zèe{ Ò½«àSƒ/% ÛÇ¢5ΑAnGe ²áµ‚”:0Øßã+]B™ùàÙ˜ÈÇf£‰?'k1-­9QíA=~Í„”YœB6§ÉÄÃ*½ér
+~ÿ –ÿù2xt«‚"¯‰¯záßÿ>mÙ| ?;üCìÒ1çÙéÁzR VÏð’.DQ
+Ñ&ý”S; ¹_öiÄŠ±Ù˜Ð/,³YZBpñsÉÑáí´ùiž±‹]Ö@nPº i ¾ÃpÚ‡¡BK]© £¸û'x à ’#sÞ•¸PnŸ<ß„¤ìN‡ZŒ¾…¢Ž>}:+¢Ï`³Ôß(á{ 2(å^ÂêWe2Z½£„›– f FÄ­*'€¡È§
+uãÂäK-Þ_¡.™_!‹óßµèòU„ºøùiýôé·¢£INvüDÍí¶ïk°³áJiùÊÜŠp0ŒHfß49J¶èU*8´ˆ1ŒÖð
+á.,yÍa¨£ŒªsauÁ§¯L‚@Š“`b X J0> ç$-‹…z/Œ½VIca™‹Æu¢Ä{ŒdÓ¬™WU è‚Ž¬•¼
+R¦…T™Ì
+é‘Æq1³ÎÇì¥~¦—À%X.›Ï×üá tÕÆôtVG^zaÀˆ6Ü$—á%ÐhÜ×üÒ©#²pDî×SRåªçª·¢? „2<¥5²/æÞ69SåýCtë°,c«âQeÕ Îþ ÙÑ.‹ÓRH€Fw£b;æ
+*CºQg y=GÀ9
+
+‘ÖŸ$E”n·¶ðý|,ä¿Ÿ Qé8\ÖIŒBrØøëv³Ó*!tÒ Éûɇƒ¥<g©JŒ½<bš‘W½áj@,\;Î×ht <˜þŸ‹èÑŒW!ÛL>î3™ÁÁòÂضó*ñXe
+¢àgÀ;ŠÜE4}#™½Â" k¤±Üb[å¬O%x„!©œL'¯z-Â84°HEÃÏÙX†±g"ÐðœèàqŽ-ŸÒQicŠ¹ý<ôANµcsó*O”©¯ŠÓ™²®È^ÌŠA¢ÿÏx™$Éyë@øºƒN à<¬õ–u oK÷ß¾/A°íîB…ÚŽe5ÄŸ945BßÀ^Af °8„ü¥Äaû±R– ¢sÄÙ O#Çãþ£Ÿ$*VöìL¥Éͦ¬àˆÙUœx£9üY&•
+-òë4«8Xf›èáœC
+žKÀ¿áXˆO†x8tõŽàŠxQì‘ôg Í<HŠ™¼¹{Æ\°
+ÏΠڥ
+<«F¤ñÇŠ@ N'úZ!%dŒ‚ºÅçÅ·–ÁÅu´íTäŠ0Wý2œ,våäàt›,.~ÐÑ~žóiÛ
+nÝÑdÉqCgØÌ}J`Ʀ–×SÁ>°6<Ác7 GÜn1l(0š²KAÑãMnxeßÖmVá'ŽñË ÓŒ'×>EIÂeÂ"4ÚU@$Þ2%[f¶œKgGn¯| R™ù$%­7·a²ÖzoráÒ’õb„YðQÒ$²ÂnÃcäÿš¹ŒcÑ Zòõ¯¦æ £êêµb]úf¶ŠØ…×]rqŽU°?P뮌»9~´“Ò ¶ÄÑÁ
+Çå`¥Šù>¦FZ·V§yùˆ,ÞزK*Ñ% UúîË…ìÁ½Iª|Jpýäj¸/ýÌ!#¯cØ@_‹ØÊÒ‚›5øø#ìò7‹2ÍÑè³Cãµ·ÔªoX¤¶å‘Š<Ä~ ÉÒ
+—;^—éBÉØø¹ì¾›ZÞòß7Þa¶HŠþÛ@^Ó…›(ôµä
+ü+•ŠugÞ¿ Ãá»´@ºaܼ`8GÔ7WdadT¡B-“Œè#üÜ—¢çwîôa'²Es±a<É¥m†(:ÿ²ÑÞÅ%‰ÄvPR凅”^\Se·Ô a|T’Y9™~”ËÓ|êÔi˜¥H=€\ðeå*¦NàóäO ”;bÝ×–Ëæh0|iY ·Ù[ÕsP¢O5L—Bïüÿ~9‡h
+Gyšù²ö6χ2XÏ1”ÁŠj>O–.NR/Jp(Í
+³»¨Ûàak|ïz†ECPòàyZ8ŒCxɘWt`ÐŽÔeó„ÀÉVM¡b%ÿ.” ÀÊÀj÷7çl.WµÃë¸ݯ)W²ûtÈ hÆ ,ÇÞ1?’ù£
+~ÿˆŠ‚þ,åÆ}`Àõí¢W˜ä
+Å!ÃQŠPd‚UWˆŒòÁ¹ä‘A|Ú¬á.Š­ÓÃâB¤ÜŽUH®8î
+õ4¯Ñ 
+j¡d)ˆIÞàÛáäH Ýõ{ö®VŸY¢úSÂC*ˆ¿°’V¡:…‡r“W˜^4R¤3¢¢Ç£sq4³…Ë«~»è6ßÓ3Íã5ÍÀ¸ê]Zä*êø
+wÂK÷Ïhø«&ÄQ"G •ds4×ùi>7GPQfôîæ„ß‘Ó톥M½I@ä*á‡\6¦B—„D®¢Š[™ø ¾8¼JJY[³”Ò
+üTÐÈ+1m…H=ÞB'Çð»Äsû~ÑÉ£¢’«Jo°$âm•ÌÚéa]ÚuöºèÙ‘%Òã¢YR„äÖ<#K'qžqÙßHnM‘Ð:lò
+7$Š‚û!òæfûuÆ]CÆrHXˆ
+æHÈ?¾3=²üó?”®¡bbÎ=Žœ÷Å‹L…)²A{a1hÝÆDc›*a>2½M™­¤£•øªÖÝV‚id™Ó•ºe°;4ÔùŸ-ÅþzãêðÏ`s°ݾ‡š?¿bÕ™šÅU„*œ_rº¦É-Vsáøä¿Òñ“ø;‰ËÞüÐÙ?¥4ÃYéš•­¤óš ·ËÛ5‰ævÓvzäYtk¤+šl˜Ó'榆 ©-Ó³8$mv5'¯—ÝÀâ7á läQ®Ï£ßã3W`Z á,‹B1ÂÇd概JŒYû<æ½)ÀR.OÊÇé‰{gu®pÀ¹+Ï÷¦Õèùà+ý£ÕñÉ:ž¦`Á¼ã{•W­Ù:>*ÂmÑh¸o ½|="Œ~±WתŠòcÇs’/lë2~r—´ýü@ÀÌB"Í·.Of¨ÑÉî-¾ yqž£L%(öØv Ý'¿&_ÁZ›×W.–_˜¼6q2vCp
+ÊØ
+†9»e×! MÞòˆ49‹ÙW¾ šž*¿Zž!E€ ¹v壛˜-H¹«€ôŽ™ôY’”ßÊ"ßh&‰ÏéúV =t£¦[ÙG$À™ã‘\Ë[“ð€5uŸ´s–,>_²GOñ ÿó¤Ì;ñGà~‡Æ€Ø³È¬u7 èP
+,¡Ïþb]B \Á`K#u‘‘#°
+RMFaðYîê¨%)ߺ¿Æp³:Æ\Ñg6’8þŠ*nõëGPs{qÍ:‡ª “¡Úgë[E¬&ôM8_1þ k˜ò]–ÛåÈe˜. núå 1—¬fÀcµQÇè\Þñ<ä©æòùP ø“yzе̊Þ»P^ÞŸ—÷ÕjƒŽxZ6Å"XmJ…\LI㎆`žøîf¾†&ƒçÌÞ‰6ïO°‰Ý¢‹™PÒÈ0heæft¹QÅßOôëGTs{ëxº !9z´>Flð¡ZCàm!˜&'h;¦+ìaݨHÍ%OG+”á]¾`åÏa¤ï¶þÏ=ÙÖç|‰~«þ,Bƒß§ˆÐ”DýueWù†î"ƒy×\ˆFÒL’­Î‚¸͆ÏLôû©’Æ/!¦D–CÅì
+¥bçš›µ9.‚Í„Ÿá®1Ö½t%ûʌБ=K¦Êé…VN
+*ÓMIiŒ~å”èÕ¯QÑ“ØO“̓‹5•7±)(j¤Ñ*™¥«ýE°<ÇÌ0@C•Hîy癎¨byê†hjVŸ.bÿŽMŸæ¡¡ Ÿ¦ˆy õ§²\óœ£6s1ç-&)oÏ|5ÿ=ŸP ¶T·“Ø3çP$AŸRŽ~áÈã;\2-·Gñc7% ^ùËwx”˜GÙªŒÐÌ8¹A_Š÷‘Ž¦‡!æÁO†ˆeÊFI®rú|w刬,Ñ$jwY•öôÝ‘i„ɳH¯>hçÖeÈ…wýó,ºàÇwæT㜕/f{¡¥ð¿‚Rwš`ãÝÁ7ÜPD£ÒÚ€!rP¤’}ÝTsÉÇòVb-q*°-P “ÍÜ,‚™½ O`Ôí£®ùÌ7ÿ’SšžDKd4Tv¬+É‘s 4ݺ^«òãB77…4¦þ!v‘Hô"Ï"--ö*PQ®vÙl™±}†ê`ï­èÊ
+&…”eSñ¢5iMØÒÑ þx§/ÆM¬Äÿ² «`–²¢Êé&Ä$q D†É²Ë¡÷–@1ó´«»SÑPôV‡lÏwP 1Òà(Á×–w…„°‚À²H‚ÆÝ}¡[ ®ª’­è_iJK òR?‡*š €›Q Õr~¾wÛjú·úBäž•q1zizÛpH­écöˆÄh×ÕÞôÞ?zø힬Ýû·%²»²é—ûmEœ ºîÌoßQÆUI —åŸt£
+Ý|`‹—a‘¶´¼U•!ÐßìüÜÌPJ•òÙnø3z<ËqEá&Šš†èÿ6})úoX„†p=Úm´)*Âè¡!)H§OÑËG"pQi!N\Jÿy4²å P¶ƒF†Ò³")ä<Q¯¤”R¶ûMÍÚYãVŽEpW\4ô1œ³Ð¥ÂÁ@üqšHÖ¸wä&(9K%.žo Lø^‚¶XuÝ
+H‰cØTw<ùÅð¸Vµ©ò£J)°
+QË”Y‘súf6Žsé`Éóó[¸(ûT³yQ6–Ó[ Jô)Þ‰u˜˜ a3<'½˜°š_VJ5¼M²À5 DXz~ñªô’±(Ú®2JÔ*Ð\¹á#ìð³ä6©ó¥GÑBªf.'Û®çex?¨k2wÿ|Tr‰0Ö~½éÞ›ô‚_)“›½7›”‡‘A°óÁ–¥ˆÚW*>©Nbco`Ë Â1¢ŠDåé_dX*0çø2Oø=ßôñ£5ѾÜüíJ½Íz‰ÔE$û ›¬;B†€ پȫϢG>„ÚoïôjÇb¸08XLÜ@õ­ÏÎ?ÈÌß°¨šG ö@ÃR0Jø%t‘©ÔKqÇÆδUÞ‘’!&ÅS‘-½Â¤Ê„KÍåSƒ¯$®ž{=DŽ„@›2:è´J
+ÂE£æGM!‘*sžc2ÊÁå
+æãÓ=ý[¢/MÜ]ã¥ðÖóçd’B–­ÀマeÉÃÒ6¡¸*†næqúß­p³ºY–  ÙrºþsË¢ÕFîä±WJ$Ñ=–jäi×ÁÂ$Ù•¡œì†‘@”Åçåø´&5[njn%Þc¹VbʤG;<ç9TžÃT EÇhšˆ)dº*9=_ºƒÍ£{›p@&ü!«Ó?Ä–—³®í‹36~#¥×—¾ñû•kÇ~)pá0n\8Nb_d§³!>*¦ÒÉKÆ™ŽN2cy¶3Jè) •Ý<M¼[ì Ò‚võMK8Ï™0m'Ø"`
+üð"FŠ+åû<ÝòNK4ó[%€–FIJæQTäÝM°¿i7#yÊJ¤²},>¥x’‹·§½Ð öIÝðÔC— ·ÝÛ š#ÎÁ w᤾øÛ'O#¤£È
+U¶£dåàH,' S¸ M3ƒŸ[/\<BCâóBÓ³W5«çtÄŽ¿Âô²šÕ½ÿÈVZr'úw}P
+‚Ù³œ¤—;Œ•ø¡—dÆÌ ‡o¿¢– #Õ`
+ò€Å ˆæ›Ï‡M”ƒTÂN=ŠDû óÍÜ1FÆ<‰a3ñAy|èkqGôƒ‹ÜLG°ò’ÔF<ÍýÁ‡MJ3º6Bž#UGŠ®|Š&pg‚Dµg·—ˆ·ÉÝNÍÅ·ãdبm_¢ë
+X°
+“:í“_Ä5CRU_êØj®›Ä»g'fà¦*‘f¯(·|Ñ:sB¡·²žÜ×Û¯ æÁ…Q‡X”FFÖÔ]»¢"D U‘Ýt&ä‰pý(ë`=á™8’ÏŽŠU7Vôç~^É_Òò|Ìócí°‡è+!p’v— œjæ›…TIàÄ-zšÅé7‹
+Ò÷6{Pâ_Ñ°
+¡À¢Õ\‘o¡# ßèq9*ÑÓ2ÀÔN«E5<¦(ØÐãŲ­CRs¾=QÎKø ÑIz„>1´¸~9ì¨èÑD]ÖàÑ0ÆXíÇEX
+œÞêHô KÑ…ñ+ jΣi„> "/×C9Ò(Ñg‰brZL¿;´=4ÜJüSº¼>–Ë5¯Ç9IJC&Böšx&ø*Ü|m-ÆÂZ1.+Jc°vê >E‚  š92ó”ð ΉéJÍ<œŸs ïUEM<QÈC ÑÊ û¼ýŠŠžSo 8#¹ÝNŽ—0*ºíàÇöÔ·ybû•fÄ)Å 
+Ç>à€ª¶Í A%Qâ–aWT6„ ÿ¸Äq Y­ qåÍøµsFm
+$¼fzTáâgò¥ªÑ¥bˆ›C3r"Y—ð&Ü V²/ÿT6W^”L{P¢O 6Î@NR«á1bCîŠ.à¤jx™ñ¢Áü·MŸÒ|E0AnBkkQoð¨pžŒ0‚¾Âþ>Kncò/=‹°¹rY„ßÑeXY;ÄDÁ£2Ԉ͑E:_º÷åÓèVf„Þ@ݺ å)AûÀI‘Þú °UYK1JJ0
+J
+=—þà±ñoÞÒÞè6Oø=õÀð£5Á®ÜúûÝ6½];G{‘oµGjü×q£ÿ<•¿ ‚gÑcÉ£ÏlñíÞ¯<ÓDá°#ÍF{Úag˜º)ñí‘íú‰lc£¸t'Eã ±‘ú·‘ç¼e±?ء쀰cüâáf6›Á"ÍÉb,5ØŒ-{ÔöÅòL&M*¥ÐŽÁâ±¹OÉ’–™Õ+*î¬+ëŽìZ[I‚PÓeü•
+=Vu»)¾½QT„TÖÚÚÖlŠøO¢ý€ô(ÊÄ¡TEP E”ðj")B„?¢Ã ,;ÚJ¤ˆ©¾ËéŸ}
+Å“ÖæÀþÍ!w?e’KCbL[2 ÌeJË¢£Tî“ú~âoW¡ŸÈ¢ÐÙ}ëÉ$Ý\Í¥*7»b*$4(²‰ÀÞHÀ™ÃKäIS Á+²Ò¡l@ÛÇaóW;I–«³oE)•j3òÎNÒEýñ5Âû)‘!ÙŽUGw3Ïèd|ô|JÊŒ—Í: „+H[ÁBÿϹ¾R6ý_ß7½lY A›–Dr8œë–tÞ¹!•%Uä?Š‡$ã`øß7ú•Ä…¹Àm¢y›ß…Â[üeEŒ• eNÖü€èí8$Á¬¢;Yw.ž!º£¡}&¤¶c
+šÃ{½œË¾@šA ý‘UE2zbÀ»³t·œjòâËï<Ä(9‹¨Ç2å  ü~‹LZøF&±Ÿ¶wFÿ¦RW]š€V¿=W]ªŽ4à¾r1ñ0’ˆ<“kñ˜×Ëž‚¾~ò¢o-Ñ5.’´aNÅÓæ¦Lb |DaáH“ÓpŒO ÕæÐzºèið©‡ìí☘Äà뛤½ñÉr(gÈGÆ1¢ê1dzÙÞŠ`ç áÛ3ó¦½óô HçœÜâ3q¯‹s‘—YBØÌæ–¼ŒÇðøN)I‹Ý ²Tk
+zµX™±K(ÁˆÑH‚¤¼YÆZ_ã$²-æÐØt,Î~Ñø¢Ú ¶³|Ï£ËRµE74LPõŽá«jSWRkµR¬ŸôéʺD´5ΈøÛ»g
+1’Ȇڪ1Œ×<³JÑ<˜lëL~wœÀ77ÉúarÂë±Ñåmñ7ç#ˆC ^”ë7˜ÿ1ZšÂ%ª@<ëª*ðÚ¯?7ëUÔÔv¢@"ýÎéΛâ%1¤ú5ºvTi“ã{µ“‚1nVeÎTKStÊJbj«˜+=ç`=‹Éj
+÷ñ–"œÄB;7…Žœ^ÌúÚùj"ˆ¡¡‹øæÓæDkiU¦
+4‚º¾i×òkaK¼i;ré€N¬ÿeejÞàéjibßCwÖå D Z(!èú2 JêÞXZê÷\bþQÐ-ý„»¿Ý_¬Š¨³½Ñf™½üãAbMávN¤ÃÉ4PÍ!9æQõNsDvŸÃ­=0ë5¶ÝbAì"›GÈ45e=®ê<‘¿¸¦±ý(£ ò¤’B²q 2sj—›‡LùÒJiq2^Ä,ß¿’˜ñIwÈ“²µêzDOfä•õžc&A_^æio!J‹ ³ûAüRœêÕx f±ÑvC µ T“GÐçÊŠCÀØÊlIÒa@\^œªáÖα"cIù”â|(â¬ÉÏ
+H‰Œ—K’œ7„O ;ô¦$ˆך¥náˆYI÷ßÎ’¿Âîb…¥…eUe‘Ä+3a™‘cä°ïýSuZtSíóã?½û§ëœi.f!¼Y‹!j7È÷oÿû&ÿýæ]?=ÓÌZïºñÙ3ºh÷Þ¼üÚõìMGt j)³ó§ñÍÇ‚ˆÎ.¡ªc#D¤çð1b,€¥Iïn£í'ÅÐaÚL|ÆÇ_ë¢>³yNã¦s[Ž9Ml¶ÙÔÖ׈FÛ s³i3e?²ÛÐà²~ƒÔMýS´‰Ï òë9íÓ#»†ð?'56zèÔÙl!F½µ9ƒd?QGŠn„»njŸ-ÒgËÔ1ŸÀ‰Ï+t±¶Î •™ÞsgÏ?u&iDË~…™ƒ#BÍƹêTïÖ¬ë:‡y-UÓæ"]SªÄsA
+þ…®æžñéT¤ÓN>tCàŽ^$v’L+A?\ÞPuV•ˆÜå¬×ÓÇÜœ×0
+QÉ ù‚d‘’CÑôÿ²›´Ú=€®æ¼žCÖ[P*èeÆ 'Žê@ƒKIqä†R2/Ð<ºnb^QJvz4as:Œ¬Ê0àÞY³
+eÏÓ½² ½¾Omé(NÓÕ9¯ ÿD¡²ÇlÒ´­Î”•ÒôzÄ‘¨hnÒ%Kaá²A=­(NrC&åu2O¬û=6¸FŒÆyd]õ2iý¼çú»ºþüþVSK¡Ñ$#rx°1Z4‹·¢+„ºM(Í8€Ú3­§b|›£ ‹,m©x•7µ¼ƒ8I òihµ·£%4Ð29ÎAÈYbÄSî/_OÂv@Iè]‹7 ‚c C’0õ.‰"ò
+¦n8"Š7Èöæ$´•M³xÿ¤„ïh*Þ6a{p”
+”_Ñ7™¬9á¡´F¿€~þI᪾­¼X¤9备þÎ濈›1øBÚM
+"eÕÙæ3+ÂË8[!ô³,IOdA쑺RÕ h8³˜‚ŽÈ#vïuDW‘ëfÍD5sNô“«ÃÂ`h…H`çÜáó8¼" ˆDל4cN†MȲ Ð7 ÒÉá«?7ˆÏÆXÚå‡&¬$Šg/ 2ŠË»Zœ°À—iä«-G“‘B ËÃG\…jÅ[é1D2¨myQ9ç,¬âÖK)Ä\¥/±µö¦VÿRóßV(ZVýEÃe\<ënÉ ubà «›±­¢Ù,W(í˜uFnBšÛF|ýž€Ûñ¬|5ñAX†±t±ñNƒÐ¶v\kÉ_‰ƒúŠ¡,#Y9šqLk+y­Ši;ˆVv5Éöñ£å§…û«¿»aàÕY.õÆX…U7„ %B'Ö]ôÁ@öXŠŸSŽ´(še;p_Éú 4śТ˜íŒÅ׎FÉi)Æ}êθ‘”¬}¨ÝÄõZ÷¯"]-• jÊõûdÊ™?zqŒ£®÷‰~á‚VÜ•t†ï6мc`Š#ñ°¬VË´Âwô(&ÞÐÇ™q v—Y~®@ÙØÇf3!(«”éÚx@k]g-0Ì
+Í621#ß¿Ý@ì %Üt 樭iÍò‚ZÙxÊ/½¿‚|âHŠðb³¡gñ»Kn¢óš"/v„¦wc$V¼ 0¿ò³¢i™BþÛ×Ny=Œ…ãF>2—«êUwr"yŽÁ†ðX+YÛ½UÊgdþÙWA+5T}‹—sà0!¬’¯g<]!t<&9¤¬f¨Ã‚¨ÇU·ØéARÓŒÑâkÆ|ˆ±rÐTsST]¦‚¨žýµãáÈy“nÕ ¨’Bœ¨˜Ó²XX]{ú?Ø©mi—o‚‡ð—ÎØŽC‚œ1{üN2ÏŒÔv
+±<Ò|.g.>‚u¦V¹ßšýwµ Ù`F‘“í\^Î æ-Ø÷jÜÌ’k%ül¦ ¢,½ô:æ4+DZÙJgIÎ ôê¤ÄkýÔ*»oüÌ«‘ªËhAæ^È›‘: ¥¹i=lù¡1
+kŽ›¿¹æÈ¡f²\ Å'êõº«ø3¨G+y !cˆUðÝ¥DŒqŸQÀ’]ë™eÔÌ)~úIFMk•ˆQMÊÈŒ†Ù†žJõþÏx¹äFrÄ@ô¾ƒN äÿ³/糕î¿õ &ËÀT±¡ö°¡ÌJ2Œ°³ð02P™øˆ¦ŠŠR]þ°P°Cçó·9»ô ©15¼"%ÄÀwRS|SL ˆ ì[9ßu¼Ä ô;=<­Uí?Ê8ß%b.£¢é4”ÑýH7<@¼Êf§úâ29ÒøÃß¡ÁY§É~‡÷¨å óõF½Õ•¾3ÌS¼$ŠÃÔ•lg[["ÿ£„‹¹ÎëC‘HcZõ¡®òPM±Ä p±kQñ£CØ„¾-Fq¶Ð¬giÂÛU©SŒšsNAÏ
+BÀßÖVŸÁPû G££0Ç$-ÐÝZ|
+ÝrWˆeyñ)ès(3séËãILôÂGc>óî=, ‚ŒôiYZ9,pù«Qçª'h+&™;ÇE_ƒ¾ ¿KQD¹#z½æ䄪Hq®ºWç6`Q‰§•˜ÝÕµÃNÝ çª{Ãç<ióøš'ý‚W=iü¨N4 ·ÿ8TʺØÐÏ•$òl:ㆿ¶&1’»´ÙK"f!åkঠq׃¸±b×íÚI³(BM¾çàØYßÕ°oÊ8VRòh°°Ÿ¹n-ç®×$L4Yu¬á{y]Wla S÷*Ù£hËÒ„jsëЧ’G5~á(ò@¡¨õ⯠jídv ýþ¬
+£¼:dƒ°åÚàÑšDW‘æpPð´k©¿8G©ÎðÏp§-su
+’gâ¼K„ˆ¼ÌŠbŒ“;ܧŸ›y8<cñg9ˉ"³úXQmÖÙÈé'v
+LEMûd$‚?bÖ×ÿáÎ3
+LÇ0¢ñPÂ]uíÁdwN…ûÒ0\#¦>þÛ3MÆZÉŸÊY‘E£"ˆ­{¨?ÑM+”ËÄ•Û¢HHQ"‘ Ó¢¨†€ùU™',sÞœ£o‹'Cy´„iC#‹Q¼&9AÜBÜ#i\ÕØ–µlç‡M v'¡Ë§Æ+'Ì ÔQ½¢¡B™)/•!æÃÇÓ¼ÝÜü˜Qg&ŸéËœ¥ðˆæ°‡4Å(°%‰Ÿðôk¿´.†
+·Ý• Þõ3f`
+@”†ŒÊ^Žä‡T3ÓŠítU’M®Dk+=8 -åžø€jÓY…ÈLptÈJ{à{¥ã@†9gŠFi5€è*-4hÖ-É•çÈJ³-äjø5 e'à&Z*¿
+Úñfö{¸®¨8 pSL §ÔVø ¹uÊoz€d¤‰t©,­öàcÒ'’†ÿB\ŸðQ ›VO’ßt¯M‚qEv+k}Ù1œK'<‚CÐ4œß Ñ“.ô|¬Ñ rHÕãþ‚1¼u;GÆÙf¸P„~Í“ÏG=Yü¨M40·
+ÿ8S¿®Ñã IÕé3ïïÓÐ=ø3©¬
+¼æfD5v".—å¼Dùy ¡äsö!ÑúMü¦‚:¯‚
+˜eT+ßWù€´Ý¨+¤&sVˆ‚ÅX:yíâ®n²pdC1ºJ9‚ü(S3íÛ§-IL{a8íªò9xèÐ2u¸;¤­‚9Û9Š'˜g¾u­©X <|wƒ´¦H³ñGÅ!Ec“j.Tѯ"Àð 6¹Í+™¶"‹Ëc—à Q\æ4_‘Zb!ÆܶJ™dÕŦêízÒcßÒùhÌÈùÜ¡ô¥ÝyŠG­Ôh\C¯Aɶâìõ)—:š‹;=`÷æ†OWF„rˆ"…§5Ö&‚à žŒÞ£ˆdá$œQqÒ„YuY=…½è”f¼b_Z~M§ˆ _`­ØIŽ£Åx¹š±3þ&ȹ´¿ÑE&‘lÀÆå1jQ°£BeÙ9]%HòG”)0390¤5Y›"» o’ÓóËÞ4ÄÅ…ξÐ× ŽGV‚ëòù¦ôOÛúÀ
+_GŸ«OœDcGÊr‡ŽÈàÕ8§¬Ýí%Û#Ó‡£|~4ú[X`É.#è•|®·¸À>c«‰eõsu…8%#+At Ì÷V¿ï¬ƒ7h²k;–„ ñš´Äšî½ysúZ7—šx¥Ò,,RŽƒŸ¸°UôƬ”4,"¢æ£Êø{`¼$^ï¿¢¢§Ñ¼(â€Kù…‡Mû„ëk ýxþiU|ìîÐbù.Ô¡8A Üœql‡ÅÈ%׶ÝÂE¬K1;‡.™èšb;æÝÖh/äùi©((< ^ˆ(%Ñú9@;AGd²ê[Ê“ªÞ¨víWaX³´â²O 9 àwäzt5/Š DÇårŸŽ‘v‰ûe)Ù4å-‚ÜL{é ßn÷{€þþË®¿Æ``±ç¬^Š0 ›
+žãóEÑ%+]4î)B!@>Ò¹Äaª¿c =Õ’£àýò¼T0I‹á#õ6{Š€
+Hm+ì\¯°Q+‘­˜Xâå!¸‹çü‰oX^L¶Û*tzÀ–µ”èKø’"~–}î%< c(YÀž•Yy
+èºj«xÜ1…ã„R9DÈHXJÀºjPôûEË€-*ÉXâÊ`MÂÜúñâðÂÜhªØ5=ÀÄöÃ>W´M»µ’•Ô)äëM}
+c§ö`Ӄˌ
+<н‡4
+^-ü¥á"=8¤+|;¢þ`æï_}=´/¼ž˜joG®2</åÎlflvAcúVÑY»
+`?ˆb$»VgÐ-l˜Ûmçìpj›1É”<´oÆTCÐÕR·Ïwða»!¼ŠðÛ1Ì“7`fÚI<OF“<ˆaÌÐ.Dö¯¦‡h
+Œ³ÌlÂW<¥—ðpÑ*,;&÷ Çfw?8
+ž­³´¬ w
+|ÝE‹
+Ù"§Ú|+X> Ls!üsá+R®ëØlŽ;Æ~•Ç»Å¼jâq âü9ºï˜Tœ+JA`ñó,8ªä®O+"¬’|A_.n$.‰êà5ã¸,•%Jå…”¤ÃGä>+AixÖ"\+(ïaçÒ^CÁ/ÎÊ1ú°ÃpIªßeÁ­«—.‘6]r h›¡³?É÷^4v)¨?GöºŒ×jÄEˆ·þ¸ènCõ9‚)‚FS‰©ÑEy¼jš]F\ã6¨ˆ¸ â]ñá¸ÃÅáîžv‡FãvÑ‘­gFÕ0¸8‹g3³†Ê¦ Q9¹ƒ
+Îé$hÄ#%(ÄP;€Ý[´b”Ìu±.Px9Ÿúfå Yä‡ò„GÔêÏ]ÄYè \æMp#:Õ+ÿ¢&ÖÁéÿ¶$žGßÀ:Á¦`‰¨¨üNŸFhÔ)Yâ‹$GY_þ:…z$ŠÎA×SQ6¬’ ¿ èŠ
+lbNÖÚqO­¸S”ÈG6‡eV2ùsPL'ŽQ£U<¼‰¤lZ¡'8ÉÉByë¿Œ&LxDJy3WE’èB_k¬…eŒœktN•}À“EÈS‡ááêVA×ÃSÒüBa£Rœ´˜C–”åμ2jÝ̉è€)¢Œ?;¬îßKmý”eø®èføýWTtŸ¹À'Ü$³ÀóÇE|Žë är™ÁRÚ6yÃ9#5÷T‹¤Å`Ì,6O÷ÝúŒ¼á^°²Fùż¸"øH¸Òþb\ý¡üÇ|mæãca7) Ra%P7g-H.¹Ú²€¸%Ƭ ðþ+*
+ÚƒM”7© ™ÛŸ=óÇOØ[$Ÿ”+kŠ€H‡ïþŽŒŸV+tÚ‚aè#(¢Ç`ÊÉK£
+órU,Ý©[<ÓHäæ±-ˆühÍ™Š}Àaª)´s0ÛIDE“}&|;ó6.WvE*ð‚Ük+›€ØèêJ‘ÛÑHø©ë€|Û×ù?åå’[É‘CÑÔ4îÿÏ°¡©§½‚FÏÊûŸú\2RöËäCUF’©È’÷w+úãM‘a1I$ ÖŸ^„,ã‰AõrAݨ Çt#þjtÒ¼üµ$l˜<H [ QhÈPfï ì…}X»4/*SØ€,(‘’‰6ñµýá¬~9sYÖtÖ£W ¡koänÿ´¡á›Ìµ_^òn’mhÐ"¿Á*Q’h¡\V1-ô
+Ö¬PÕŒ P4ù‡‘B­—,!i)t£”´ÆÌ“À\.SÇ«qCèy±
+öE¾°k£)‘„bdÚöÒú§F7Øuô°+(ièõªúƒAÞ9U?ZG4‹û•½Wǯè„K3“J›)dŒ9ùõ#*âÛX×ÄÖ(¾ÚŸ=þÍ",>\«4µ]Ä¢¢8WÞt6äŽUób»OÌe ¤sæì{1]Lñ¨GŸÆ'6PÇ¿GžÐ¨Ž¿³raEÞ@n
+Æ©žcÐ'.Ûu=çŒ>hÉ)õVâ܃Tb=âsØœJI(òZ§„É#$ZË™»f5ÍƒàŽ•&1‚’ÑÏ*ì ¼ò)ý‚yK}ðü¬èEIÆŽœ\ϧj$Þ5;题*¨h_¯Ê(>ODì×^4
+U“rWpc'xVÛ’VA yU¢ ޛ̳ø‡åÝŠV!þ’J¿,yá4TÌîÀ‹Ö Œ2T‹L yã>‹ž!—"¼KرXÿG·iÀκjž_ûu ûÜ¡ÐÞæñ%Q£'óf>?´B”lì*HÁ@3zR³æ<åE`øk…Òb~¸Êz¨%ñTöjÁXךÄv31ôqØ1°'~îëZVœ>4'X.+Á‡4%”zÈûVâ_R6•MBüæ­àzÿØ2r&fC/Ñx~†
+8ël+ç°+˜ZIG`½ÇØi¨YÊ·ž5Ã.6¥$Ùe{ýg›ÓZ3ÜUº§ëu…‡­³?†l`0lKtN%î`Aá 9Ã+/R€
+øŸ A,^‡ÉN!ø¼D^¸Äœ­¤i¿ä™0ÆG’!zn&t˜ªz'N ³P1· Ä1ü%Lž%¸UOû_ Ì­3Ì/÷Y!•À4UF <Ñ€³ž&_iîUbTê)RâE¡³ÂPuÃIƒF9žÍ#@PÂ÷ßß± ‚ü.Èþ) Áƒ:Ü8Î9Ì»Èl9Ü™\1…¦5/“*ï ‰lFTÂñæH"iŸŠŠ^Š]¹èïðúýÊKÃ΃Åñ
+ Æ‹HGéØXQ2#û_Ï£°nô
+'Q¾¡3*ÈZlòò‘Ã] ®õ»Dµº¯˜yÁkÇ^W ȾnÂÔ\5*24T¬$×X¬s­'[ƒ¤8fYMKSÚ>·¬èÂU•ü×!É:’ý7iÈâãñ7ýP B¯4—Ö6 4i£ŠJfEl’dsgã0ÐÉ:L,!&pE?ßAÉMε’Mí]E½á¦@ût(…4IPs˜ã|$Küí¾<<Õâdö@'-šœ}Ž/°¦ x6(ä 
+Rk8Ï—ÿÁ@HÿR{s4
+qÏE¶tÙ),ï4óâÚŠ@Áe ÄÏö$\@Áš#'%%9EØãšSR¬Â/ç²qG.âe݃!‘e¦×.Ž,Ê}"iÇJ`D܆æ`“¸˜ºh•×t‰$v©]DÚˆm„Zþí¶ZÖsžê9G‰`` ‰ËJàj˜{x6 B]’†&¦uàe™±iâê^¤¨@—²²§¡J³]jÛ'mq-¸{W±åly®?ºÕ°„;h,)¼Ø) »¢VŠR¼DÔ¾H€§s £´È‡§õ’,²XÏÝO÷ø.÷/ß;1ÝH3>íÃ¥U¾+)JÅîÌ£"?"ÑNÆO—§
+ueÓü­0ÀnÉ Ž5¢øH …LˆQ¥ŸËP)L1¾~–
+…4wÕú朼i2c‡e»— „Ù•¬$Áã(O›½OÑD^@–·Òg¡ªŽž”ûÐ2E¦?˜
+žÞ­t(2¶I<v+R ž_À(–ÃÂ&¨í<ßÅø{ü>³ÂžO:ÁfìzºCDÂþÈ»Ì"
+Cd-ÿ6+Ĉ—ívJXRŒIhúV
+&pÇEºM[¿P5'}N´r)ˆôò._òt°‰+ìo‹Ÿ¿0Ë—LÈŸ²ƒ9Ž—Ïš{º¤BÛÄ×`K±_Ò¥vØÅß9N—Z)YЮüt‰ökº–@–²–¢GéR«¯âÕdyÃtIþƒ8É©9•(]j†ðý$|Š¢tI‰,ÐÒIß%÷tY¤ýte¢#-J—Rh Nf‘»¬].mYt!±¸|Ü-]R„°u=V¥K•@iü(+¼¢t–ÜÓeXôê¦ÍâpY”Êuô–-ùÿ
+¢¾RØ-[j
+
+±Œ‘‘ÖâlI‰¶%QFÍq¶£ øb:˜vxζÝb©ùyï([R‚òf ç8²dËÄ šZGtkWìyÍ–ÉXºj·wÚa¶ŒJîÙRØäd6'%—Çg´Œj^“¥*H_[bH—£d)ôvbŽÈ£Õ'KõFœî~}é5Y
+áNXr£a° ‡y–RL1jÎa°4¦g×$úÄŠ(XŠ&8€ÉëK‰ƒ¥¬„bR®†ÁR%–Gê¹FÉ2,¹GË°è5[ª[W´kÜ2Ê–Ò¯1ñ"t<½Ë–z:4‚€°¨}FÙR=§yâ#W ³¥ì§iТ«äž-%MZä;N‹²¥P%bä¬aœ-îƒ ,¾~A´L
+ë¨a¶ŒJáòYô°Ó°ÀÖXðsEáRÏ–óà™X‚‡K‘h’öÀ=
+—‚±MU¸®0\
+U¤¹,SfTr–sÂï4Ôž3±0Òˆ|ºÝÃ¥AʲÉÛçý —uFQgÅáR…&ÔmtFá2(y†K]y±ö^ìq´TÉæ4–iËÈ>£¥„
+ý”¯ï»¼$KÁ_ƒYæ QÂd©Ãÿh8ƒk3J–JØpèÄæ(³|FKí̸ i
+#ª)â”èwC|-…]±å’©-Í¢[Îwö ‘}M–¿´ª
+–ÿúÏôñïÿýÅx™äˆ‘QôºƒÖ^œ¹î^ê½-/}¿d
+®ÌH—h@*ýb’1üáÇ¿þó£õõœíNòòºš²"9vBz—›ÛD.KK©Ó!‹BIJ¶É!²õÌÞo:B@.³š²Ø|®Ùㆭ÷À ^æÏ]UYÐÊ€ÀVƒÏéA³Tà4À6^åàmX.€ð%@ЦFSrd/çðw˜œZ—Å—!dðÏ8xÖ2|VŸqå‡Ýö½0 Ñó<Ãê†/]Ú_z‚P>Ƨkßç</Ó‘iÈSm/Ob˺bÀPÎÜ_ºW¡a
+Ñ­7%)©@˳Õó©;¨@9Ä»ž ¬êçTK)lV½6«ué ª¼¿D“›OOJg­’,ŸHgl÷¨iç~ìl»•§É÷Á“GñrSÆ…o#Ê›a³AÂëðRöËÐÈÆ?Â
+Ô°HÉŒÂû_˜é+æ× †G¢ôŒII[ž ¶„‡hØ•Ü7—JõúäcC3•Iu2Â/wrôv¬uM*MY\êš`,D-r…;¨¦‰´Í…&…»JåkäÑW¡I®²G˜¤Ù LÁ¬d·‹À¹È$“ùrzƒÓV0*çÆY&/¥²•ùrŸûrZf†ð#µÿÞ–/ÜŹƒ¦QöÚœcPTQQßS_èï…Q¬ !krwªŽœVo8ûÂcæ‰I€>_@£0Ÿ}® 1ø»„ qM?ýæú C»”µ÷7káiÂálžÉŽóCxüÚ)ŽEÅXóÜ™‚¢°|9×ý%¦qŠ´l®u"/òZ—Óê–<ãD
+vÇ!~éNÏ|ÜÈi×P,“eôZ> †]‚XXJ¼ÁîEe“em\ZSµjÔqäåRE—ÚÏ<{
+ä1Æ·Á·ãÒ ‘,|•±ÙÔF‡ìð^SB¡Én#v± mA,o`Xû¥Ì‹M-»UÒ:Ì:®,‚ìOUV‰#XÛ}åàîKIQ=V÷å6÷ _õä[u¢u¸ÕøÛµúëlŸòÔ×+ÞÓ®š7¤³œy8á z¬{ô¹€8¾½Ó&Ž$OÙÐm—x?l
+BÖ—M¨2!$ü‚}g Üë…êÝhv5ü»
+>AÇKÞÎñu}À·$I•è²Ø´òîâ1Q<˜Ü5BU¦ãRßÖ߈œŒ+#¿õ’Sa¤â]gã¹Pfèrc•€VT^*î!›R·œ1qÿ¬BŽ;l—OÌ¥:Ć‹G§­"°‡^ÃÌÔÊÝÿú`ž%®XÅ%#ž%þ#ÐSõ5œ"³Œp-{ñì΂Ï1í
+¦|¬à\frÕó)äBËÑ(/-x‘eÚêMÐ õF™2—$¯H ÿñŽ#¢˜KÓ\„j¬¡ÀöUþ.ÞˆjJTÜRví9Z ðüP q>ôÿÇOCj?ÿ ³MR þÀd§÷¢Z´5IãžTmæ‚2˜Ãk‚¾`„àMXD>‘#¨¢o ž3·³Ë(¿ƒ…Ä{øµ!ð^É0Hɚ瘱4VË€ 2ð*•Û²Ãª&̹x‚ÿ2^îHvÜH]÷@[F˜S1iÎþçÜŠTWå >Éؼòsó¤m ÷ iŒ¹¼$¢^1/Ö†¡ã40ƒ§¢¿_ˆ|Xhyl>iÑ€i&ïe¯1D‹( ÎVó I›/O»Æ ÃØ®¾sIm«±÷P®µÇc ‘¬™·Ê½O‹ªžŠK’¨Pjòï™úCÂÅ×éÔOãP†ZÞeO#t< äNih–š˜w^ˆýOÍÎó¢L„ù(4pøºSûYÕ4협־½ѹYŒôZOqXVŠÙoo‹ñ02Æçäˆ0½ Ì™·ôe)ašo’ð Ͷފ±s›©̤>•Qû˜</gFÊîÇ ¥Êæ;ÔVü]¸M'³<­_E?4y·˜?¬Âdùêƒ`0)Šh–óô[Eóö%¦ûT“{ViM]Ít~è,ÏÄEQ¬ìDvµðº€ÁžØÿêÅù˜£ivuwg¿¥þ†õzjâãÐ°Ñ  ŽÄO½ÄÆ5ÿ­‘Ü”¬ ?"ä>×·ïhìlþ­Ë+§kY˜Ð)Ðè[¾Ó}ÈÓùuÊ’ ƒÝÓ›ýã÷4â«®*HË+Ò@çÄNxn»Ú'ÁÕ®”óÞ˜x*b–P
+Ö(Ëb.z|í • ð/cJÛ' ~íÏÖ¡¢ (
+¬™aŒ“PJ¶òš-añò‰ÈvQ,؆a0wÝŒ†u%E¶\Ý ‹Ì¹ŸhqY¤ŽÓ^'ãUY@V˾Š@šEŽ~ÞŽ}“|÷ð%¶JÊž‰kxNvp§¶9fµ«¸:uÛ”Öú5J¦a€¢ÅèL"~\õ„ŠÀ¼'ì‰+t5À5ƒsF:ZÉr“¯s>—)ñ£¡ø8ûG
+yǸàt0%_· ”¥0ò†×6q-Zô
+žP#ȧ_–¼°¦Ò׌_ŠYÔäÌþ OÆDOÆEŸ_7&­w¹b‰_/ï:Z¡É9fü žï¯4Ðá™$BÖ!œ‰B¾jl‰«i8DÇtŽ¡Òƒ8n‹Ÿ¨'yêËC¾¨ 6"îä‹ùus·A¾$|bjb@ª1ä¢'ÀA~StÇüPô™óÕbVÊ6]çë©P¡¯šè1çÝwç|YÖ«Õ«Æ|®>×—¹2?ŽV]ªƒN‚ùÑ2à.FJy.ü})>a>ŠÆõ´•{…ù²Ô®»Ž"_
+0_ñDÎÀf‹0_5Há·?CÌW]dVm[-¸ð™ªÍŒ«®€óå!RÁÔ# ü“‘:ç—Èbi­Ç °ó¢5‚Þûéš•ô¤z›§û²MÚc¬z¹Qäž²v°‰ h>e¿0‚,f^øв²"‘ô,ÂÇÃìº/YaSIGa™Q—tfµâMåóåt¹í]tãØ¿ý:ü;$L±Z~[ÄæiŒtòCZww>E@œFzY’Ò%²ž œvV»Ê^¸prs‰Y¡DA"6¼ã^¢BÏHØÇÆ
+žcFvùå?û՘{4\ÁIf`Ÿš¥çº„¢¨‚,—y
+T¦ë&š„Þïx¦‰ÑKHŠøyô#JŽu˜ èç”Ñ»ð>¥<(€Ï’ÜGŠ¢è‚à:öð>4A3®Ù¸µ­ó¨·DAß=¾öŽææqCÝDr•¿þÃVö¯ÿ~ùë_þñÕ&Ø:rtB<ª ø?É`$jÜmd
+ܶhßÀ½;ß–gß 57f"¢‰èªŒ¸úN€ü´f­k.¡Ï¡jáå’.B*øýÌ‘äû:‹‰Äž6˜Èkôð£Ðyv¡æ¨«¾èzÑ!Oq‰Pšð˜xK—qÜ°—ìŸb<30wäç
+Ïp V˜Y[ámʇÓ.]O'Ú‹WiµÁ·†_:Ÿç0EkŠéò¡¡Dà•ƒmˆo—-áŒBBMÖ†d©C NÂ|¡ºÑ NH$I•ÃuêD—¦ÅºÒÜ6ݽ%âsDŒ:¨âe‘R$ÂÉ MIq'I×ÌUõµ-aûH ´ây®iAÖ8é**%ª›Ûé*5e‰Õ³0­“S ÃÐ~Xü^ s­-H»2J ú—¤—•Å1½‡0µ¿8§ÒøËšš¿l ÍÈ8K´C^êJ¥E"|¾éUÄ„2Ñ°ù]„P«“…ã
+»DÓŠMø9¼G!¥…|©z† †ø)EÇc¥
+
+
+oj,$¬ž—C1ø€N;œ›¶DU–W«[”Ó¹ eÂ
+Ã,MyͲʩÐâŠÈè”à‡ôD3X :–®ÿåªGa2‰%qBÓh,Ë(x½JôÔ2]91 е7Müß;chÓZ· ÍbÖÚŠÜúW’ñem—:§«ÐUY
+–EæÐïþ6è޲ื@ðZd6%!¿'O%¡£ÏÉ:{…^}’±ïô#ñ`û GäÀ„*Lƒ`Âa/mæÆþÉ'QAJÉò«Þ€ˆ˜glçå€d»—ÔhuûžàæÉ{ç‡MÕaÉÖ,}*AMCñ1€¨µésðÐ>Q¯Y>Ós lR>¢ #óAòß2ˆüK†u? WƒÏqbHbDZºáέžú%Ö€ÏsÛÎ: [t^É¥KvÙÎbx¹Zá;ÛÈ SŸ™Úƒ vmæêH#€œw§nugOˆ}ý%–Œ61âJ~aÔ¿Wq4%„à£e¤µƒ¤F=<ݨ…gáÚ‡æ°Õ‡9 @ÁZÜ{ôºª¿ßÙç³ö'ÓHíájÞiHUóÇ1š(ØCV¡RÑ!ÞøP©
+(Èâu™osCŒè©ï¾¾ƒf̉4˜!¿Œ¼œÂ
+‚#ÄæŒG‚¼÷è…ÂÝz‚Á0%¥mAw"ÆÅcžs¢á‡`"&5ï!&^š¡b넆rròãZZÚ[âMmAh²ë%wx§•G%*6 ?e ï¬S”„ÝkÄÕm7Š%ÐB´ÀùóùF™ë@hõ”@·¾j
+Öï~Ü[ »+ù™7D/ùã¿@J[3±–Ûnc: E°¿ TºÔŒ½ªWù$AŒ|¿Öz•Š@MYBëeðYŒL´â¯•¾3#ŸªŸ˜u~ÉÌNvÐèÈÚÄÎm[ ÊÀ–%ÓPØãHyY­¯™)i cwZ§Ü[nf-Ü,ÃT­»XöL—ö@RF @=€ ¤²ñ產Z¹I,KejIΛ¸ƒ­»Ø›ƒ`U˜ÁE€Z‚gš0´ywƒðq¤•í¥bG!iüvêCB ¶ä^g¨Ž*sMmòt 1p¡UšõCÓ¥œÉçchm‘æ•ÿ0È?ûÌG’/†tïtº6XÜX2‡ ý=hŒªAôO3¤ü狽tÇNaŽÇ+ .Äj4—§C¼,ÎXU+€ÌŸ†<[Ïž1å¡øÔq ]»ùî6†M܆VOÅ¡Ùm@n z?y‚%0;¿@èKÞ§mÒ:Øz_sãÑzš=÷y9ŽB•ç3>@µ
+—óü5|5^‡zÒFnÜùdx–0“AÑ ÎwwËãUsØúˆOë”eôv?ƒ1D§aT¹Ûå—ª¶°÷3òk ȾRê_¡#©åÒGqeƱ­LÌÈÂú$òÜa{9¹A*&›‘F›»ïÎqóï²ý@89 ¼éÑBšÀ°Áx¬J:d~ÃÐòŒÙ„1Øðfï^ãwA5kÿVÓØ\D fT©f+‡u]ý&vt*âÄIJØaL>ÉÒ¬!ºA¡ãÅ{A¿­‚÷ÝÃ,²-‰ú F_Ù »Q–+©Š<ôŠu2È4?žÑÕ ÎIüÐḦ«0ß✖/s£8€ñý@u SÇIõ27º;ŸaÜ}þXÒ äùCÕ e–¸–fŸ™¢—¦°‘çU¼K/³ m'ò?/׋-úOêÕÇÒ›Ø úWFpãCðVÚ!o¸ à;ð;ŒqøãBò(m‡ ø#“þºD9ïÏL+ö
+Ô` ¿@S! /Å ³¥‚°78[¬Çö"1ÌË ù„ŠV¶Iѱx%¯"‰z‚! 9XzRš‹t—W§ö\¸
+€è%t¾¼asû†(g j}boŠz¯MßAI Wb
+ŠaÅQýé“Y•9/µSe„¾L,a+ÑwÔ–dÙnt_ŠHFÊYOCÇ¢ $öÙ†lÍTnYÙ¢ãInΫ¬§ãp óÜ*Q+/8]ãŠÊ<@ˆa$¶šh®£þ}–ÿº¼FV–ƒ;àödÒ.½­ ]PÛWÁiì0„Uü!.½ l}¬ËF1ò R5 Bó <Ê>÷F™Ö.ÔV<YýÞ7bº¶œ†+ÿg6®Î¤ÙûsÀÀ”9S¨“@0 |vhå"È™yxEe£=–m‰[I]YW¿ šå”!µ·™·àRyzñÙHç²qñ™ŒO®¹¬3«AÆÈÒWB U¯Kÿ RœƒP¼©Y·í¹ƒê§þ.¯EDÊvmþMQoh†HŸ$<l‚p "ɦÄhÏÿ¯—ã0n  8‡°?,€³¯EÊÿê×
+ô‘ÇêÍ’S¬EšÏÜÓÍvSnvþúZ±ÖÔ¾gè½NUþtJ¶Ô¸U:ŸS4]"Ám–˜[3s_ZÚzív'æò(Û\`{Ë@r–
+<f¼5T‰OëB¡í{œ0e]W¡O<ƒ”l4=ÔÏ~‚fX—y6oš%>Ú§—»ßCE
+7É÷=0;ÅTQ?@º'¼NóØ(drJŸ¨Þã,fIÈ
+Íð³‘%©å@»ïë”mjµV*2†òµÈÉç^Ð: Ú[úí=j[hø~)ú1ŠŒè¶F[lõ*"öšÍZ>žzû?öƒ9êò×@Ep’ô®QŸnVÓÛ²àøWo«9ý®"‰Šn–aPg--PgÁzJð¦Ðøí¹*~°­ëö] Á=c‡ÊsÑç-äªÅO{Ni£åWèt…ÒôÙUC%&µõXÿëcæÿóªšOPû@í4)±g5oœöÇQ¿Áßÿàø¯ü¢«–=\‰?/{ösùl2Ök9ÿyAŠW'å—ÚyÊýØë,ñĪIä7#MIÉ/„%•÷¯>°Ñ? ßBþœ =®B’ä©î1A)a‰8Œ6"Ò›çPÂÒŸac×ïÞeŒ Hxõ@J QFC3¥<µá~aÜTòØãô‰ø¶n
+5÷›j¶¿ÔœsÞ ~Içbïq 5V_ÝZunCDû®„(w;Òö¹êsÑÆO3ÕÁ£s¨§©a˼¶Ïìb `Çw3Å ˆüv[rœ³ÓùÚŸ¥Èo9»³¨t£r¬3—¢g˜:d}–P•Æ û먓+%=çë:åŠä¢å¯ìô¹èï×¢6ü¡mïTƒ·"¾îoq›“Q5ªè—̵Äú@¹Â_Œfx™Ãä¶î;f YAÌrÎCN:¶é‚lá‡K.Á1‘¤iâ=ÎѺ=&½–±4ñ‡5F³ÅÊM°ó×  ,ß]^JæU0yÆõZÀözNZˆ§~L€YŠOG ¼(××d¯³¦ë«œûRnùØ™_(lØ„«äƒ®x”Q¹‰À|ØI2~Iî«ß”X0GPÇP¤ Ù­¸Zôz>ꗢߡhŒã*ý˜p¿{OŠº?™º±ÃÞÇõzTyFE¿’èò|ú4®ò-#äñ&‰—€eŸt¡¹~†N¯
+H‰¼—Ýn]I…Ÿ€wØ7H Âþ«ên¸Šƒ„rB¹³<ö™!"Ž‘1óö|Õ{ŸsìÝíÉ0$Š­œSÝ]?«V­*¾Å uÑ¢®ÖXjlÞ‹Kâ‚×ÔÊfqó‹_^iM®5õF_½ŒVZ‹kRø¢¸*žˆQK\´%§1ñEuµèò®_w4n®¶$M[ 1¥e|'F§Y2>”'þî-º¿%8Ï5ù\sœÝ£Åy_JM!ó¢báþx,T$)Ñ¥–sMQU|}ÁwüóË÷ü”ƹ¤Šo¾H[¢o|PrË%H.m¹›[eç5cUy¢•EÈSh¸,>dá™è½ãôð9ÿYO}()ö€_>­.†\jn©¦\fï;ÿ×û÷V!¹"R[VOc÷³i·hÂy,ÄUpÐH{ɱN"$åÊ’LŠC陕]ŽM±J©éôž}.G_¾V“›ç5ŒùTÝ^-p™ªj¨-Ô:‚Cp*ásÕVya0žvh ]U^
+FÓ¤©²_OL{é|j«ó;{¼mÞÝL[ß8®„e‚t×B “–/.¤P·þúìxjŸ¢Ó;/äîØšª‹àÚ@ ¾—îÜ ŠuŠÕ×"•,§/5dJQã‘,AТU )¾À±>¹
+ä@l™œeªÍ([.yÜ $ #PÂ@ÖšMªb­Ôš+‰âå^vÊŠ}ÌTG4•dÄl>V:6™EØHŽª¤ n€%Ø´¨Õ,þ$'ɬ¸å8¸D¢Áì4|ÉÚ™
+#s{3Z§ïØÈlI(, Ý ÁÃ<ܬMÖ#k6‹
+B²mD·ûI+<Í—9vJûµ6Ñ©‚cHØpÁ¦’§qøm5â5_ÎI`ïɦ:ÙG§NÒÇÀd34 ¦@~ž|#¦­M|æͬt¶oM nÛ¤üˆ`
+‚b.>Ÿ´ÑóDôý!å¾?Ä.Lv¹´]á„bIé®ì
+Á ð•æim.C=m¡¹mI&ö pM[¯É¶IMð„«¦p«(g@=ƒ%›Z1÷í­&ü  × ¸Ì¢Yä“Î`Ï0ÁbÃ9¾´ï°ñž}“Ž¾ì{}Óž)ÆÜìÙfHïž«¦U8o(öÀ›^þánäñ=~'`×û¹ñn¹/ìxaysyÿ™QøþOׇ‡/WïÿyõáðÃõãáÖÆâ³1ØžŒ«·~ý˦!òË£yQ3©zV|w>£h”Eè2^&¨W©L³‹!ã?ä—YŠ-ÇE§’ß]¢o>-{ }ãxþ^ëDÜï=‹*„  $Q73£9Í>kwij °¢QÁÀ&Ÿ‚ 8$°C6ýñUP@¤ÕYž ©’nJ³,ÿ÷X\>ç›å»ˆÜbšn/ŒœŸâ3
+sõïNª2÷I¤øÃVycæ9m%z˜¢e"œG(~Ê`
+üÈä¥ ñ“"áŽ[4LQ ‹‚‡±õšRs`jÍs”Vôc^€ÃŽôj´a!¯ëîÔH5­;¦Œº
+Æ¢Pi,ßG˜:ŒFÖ>Å[Ôòò­’5<ÞvéÎð&ݮ똗2YG3BŠË¤2&Ѹ`¬ËiÂÐ ¥[ùÝeP©´j`Y`M®Û9”!µ?c&ñG¦†æÊ’4õ4öoœPÎœžuVv¨è/ÔÙr¦”how:ëà¢2¨L/Ôˉ€¹%ãÞHi‰Œe ciå{¥~ ýÒafÜWkÞˆéU2Y×õ±™rZQŽ%΋2êÏ>m½/Ù1›b#©zRŽ%ˆ#q/úu}ÕœecZ×ô®83˜¦f„S­Á
+[á뉦Á@r‡Ÿ(Tpm<‰Eö~š}ؾ
+äZu<´' SÄ‚QuDáÌ0)ÕÈÃ\ɵ'åñ"· –ľ¶zÊÍĔݔ]ŠD&æhŽqa6ð=µ›çárT'èDá¤Ê`Ð}ír”õbÒòÃÈòeÁö­^_öGÝn¢
+V§®e¶ã¥µN’Ä4úÿ°eŸ—ø—LÚköóÒ«„M+°ÀOòAh.¢âÆÈKÌàjw2NªB±˜1Q1
+ .‰¼™Ï² Uo™Å¥@Íœ»¡‰W8 ¯U鳶ô´â«#ôh5cçAÙÏTRû§?¸s?;×ì'TøLð`OlääF¹ÃÇ©"p‚ïJ©Ã’¹ç°äœ9^9§L7÷†Î<Æ~ÃÁòü-Ö¡‚JHYýdn±|«Ü÷7ã÷Ë­Iî¼
+aºx“9)ßñV1š£SäN
+=AÁ1%åÆù¢sÇ¿É840 7ÂQ"È£´2]‡*À²T9ÎQðsü%#Þ€Í á·-y£`0æ¹ñ—w•Z<ŠpôŸÖY¯‰óò ¬à(s›ßæ ‚h"þõL¼»î¼™C"AåXw(¶96£ˆh#çrñM7Dy»¨ƒ_!7´
+!Ø^ 'ŠÃͦÏ|ô .±+I#´QK°%)ÅﬨÉJ±¶ ÷ǧû°h‘Ôü$yV€5
+ó*‡îCltÑ(q²û‚F»"8€pe%ø
+y£‡ŒTÚp€ç>=ú°I-NÃv“6í˜6zÒƒ½r»éçy0åªUBhšU‹AIļYÕ{DMžªpüÇŸ´Ç›ÿ=ü)oˆZôCÎl$”ªQ(
+_rš³É·3º!,
+ÃUŸ.2šèw‡€¤f>þæÐ’¶Îö:É:D£{OH ¼ø|ÿ ðiÛQìiˆvNÍÛ‹nà7âÅÐ lþæK®ÚŸŸ¨’L!oœówm†>q-ÆC±ûv?Xùý—
+!¤50J‚ È
+98‹aŽ¦+õɳ«[Ìñ#h¸ø Vdí‘ã 9n.òí*%*ûÉ9üËZèԆЩ˜úûæ+úkŒ½¦
+ó‘QÔ”‹@ít€OÓ±Ó“´=Ìe†w|„^O1¦89[xPf˾„HDéwª‰)
+émˆ)×q¡Aè‰Ã°4#{÷Wï.½L[m¯•ŸUy ^Ï{R£ía
+eˆ+‡z6=:Å11§Ó^bŽYeп(¾¹}‘¡i‹u+u½ØÕnºŠ§LêWé“âg£Á†=Œ*ÝL“JMí?žçrQ—
+€[—8Âû÷1/ö!ÆÅCíÌùÆ>B¾LˆDúÀ&ÞZ¨ÑSh׿
+¹ù+
+/NrP*ø3Úo[˜
+
+v•66)>L¤j>b.ý!!ü4 ‡/¥=Âh=rÜB«ž|9ͤÿ)Ïu½ëä}ÿYgi²ÏLfî!bœJ[ÍÎB¦û/÷ ”פ¸f­j ÊK¾6¦–Öfº•€%Æ{g”ß‚ä’FCjA>\ œîj+äØU„ÝòzŒªˆí ÝnÿÉÄ@‰eôØ[^­zµÚRcöx ªK~ ”
+ÕÀÖ¡O=P°;9Vm‹3`X“gØqÑLCD9`·O!@&Qg4À™›¢7†zcr :§ÚÙꌪ"šP•~è'ò“\ I³–T”Ú†»Q=¿†wÝngm?×ûŠŠX4=M'ÿ©¢šäØ0’ÙñPå³vš4ˆjdï<0Ž¨'Ya&óQ$Ì£5¼ê®àK°Œ¡êê¥/Køf!,Ë5k«]·Ó® cÃ
+lìé¹æUµî²ýá‰61Ôt€„i>ðÐúåv;j
+S†¶íp܇˜Øþ9ÅÆ´2IŽ#þ!UØUNFÉR߶4äÔ7¸·Í¬Ìiõ¡ƒÈ‚P@9´úÑL€%°.ŠÙËšÛ)°±¸
+7pu™”/I¶Úÿ3–²¿HûaD–︂À{|@>
+Sõm%Ü@ãÖàjƒŠoFÄ“ÕÚ9YºRÅÝÇd&½GÓØH¡w d´d±?Ɖ$ÒýÐæ æaœîvK´Y>ï…6"}ÝœZÖî\º)ônôwüß᩾|âË{n“ÅÅÚäÎ$º<ñ¼Co[ú˜Ý¶¼ZU:O'(¿ãÈTÊÓ¿\¸Ùh9f'˜0.}0ÓÃ…K*Ò?ï™SÆyø Äfk>PbÏX[àP]Ûá>¿æ
+ém“Ñu(¡¤³¤lb’–PïæVéÝZ mϪh‡×®ªÑŠ¤˜ì@Ë1µCiŠ¯¬0z â(:qW®á?ýjÙ!›Be¥
+vÕE«9\Û"í¿3i¡&j˜Ü¬ô¦ƾr¼¾+ƒ&Ww‰®…eÜ "G»‚À ¿¡«—˜z­¨X{t@©£Ô 5P¼µµ ¢Ëç°0o~¹ƒ5•
+eEØfEY9xÕµRR"uí)‹¥ÂN|b7|í
+ç]Q n< äЮ~É0>+<ÕC³ $8bþl@AFxȽµ±ÅLÔ§ŽîÍ,pÄ_Å{€rìóÍj°·Œ‚8Ó8vªù2'-V†6àöni|œ–ÑH®,â½9"Ï[jsC°z‘1ÃBÙËÊkè¸ÉîÝvE¼mŽ(ñ_7myÅOÀˆÁï¹\.OŸŠÁ<|ªØ¾›Äµ9„Øp³p53þxL©/Ô‘¦©»[ÔÿF}m¥¨žF_À»]ËI€Ä/Ÿ9Έ›3¯^¶e±SE€€ ö´ç2nÌ¿“…ÆqÞo‹/ûm0ŸÄ
+û ¯é9FøHœõ‘ CbOkXlÀá¾Ökó„þñrëë8‚ð_9R¯ÎÜg’'Ñ=$qžZ\+BD1` þ÷ùªçì’{fÖ°e‰àî\zº««ª·©%K¼jMð~ã·†6–ª8:{¬Rô:q/ÛkôÆ»-èaä7f9Y®1JØ(7!–M„ž•Â¼zš¬_tšV"éì%X…(G@',Š JÒŠD’x¡.ÚZâA%¤³¾4ç(ÇFÉLÊ\Û¢ŒªR²^5%ÄÏ–U‚¢÷é(8y¯V8¥ÁÍð¥oè—ˆÁD„Ð=|^
+ Ô¢\ø 0"+ L:HU>=Ø* \ê]&ÎIÈnEô‚ÄIÓ®èU/‚‚Äìx¨¨dC‹#âo¤]qJI+#ÝÖtŠÈ1 3#VÀY$L~|ìF²ÈÎÁñ¦)b?XNìO¾±[VYåTÈõ’TZ _SèÍp\ ;ƒÁ€¶«•„z•«æbôJLx27­?¢N€¢»@„¨z쬡±È‰&s±Az §Åš¾` ü¨<-ã=këæ˜rGIÅ9L0o C²F[ïooÃÜ‹ °ÁÈ­¹å}j@"£ç:•Ê/ÿ0³½K1¼¯q¨ Ãçm
+NÈšÄI?9ΩD7i6 Š£¬0Ut
+ü™¿ëòýûi#ª‚Óƒ¨¢ع²÷Vuóì1„U íàH„Ëjœ@ü•+ÐØ
+ â“Ý¥Œ ÕîM‰œF¨0®°g‡râ}MM09‡žòx™P ‚è8Ú­Xä€Y!¥høzÉîKˆŽ›™>oœýŽx¸AILŠ‹“‡ìWtÐî2œ3$•ˆg•‹Ã™ƒœk5y. J'Ç`“›à)Ÿe€øýÅ—ˆG–¼cÂÁ¸Ñ8Ü,-kÈVwL¥ŸË'ÙN&¬˜ïõên¦H¹B|+W×ٽÊ]Øýüý*È…‰ æ»xÓ¹àmE©üoÙõEÙÂòÔÉ I0ç­â ˆ¼09ÞR•uª5MÏÙR8†p%ñ/êS„(Mº/:
+Ó‹ª»±æ¡|šÀrÅ
+>Iu6(ðyå|Ž†‡F–‘°e¤mý·,cT\BŠsмzÚFz™³Z›¢i+[Óí{ª{í컑Z¯äÄ ª45X÷0…+'úØ‘C‹¸·³Ï¯ n¡–‰³-XÄ3]ÚyÓžÏ× t:r åáµ£ŸÓ³×‹¤22Ôd¢¡¬ƒ¤MÑ1H〱A^tžÔy
+îA܇& ÂÐhÃc§ ;–¡ï¯z1½Üüdγÿ¹ù§.X^ýðõëíýñnùôx{÷ùøõi©áõ¶„¸íç7˜ChC„4RHQ ‚D¹åæÓÖí°ÉNMZÙ7ï?üÑ©Ö·z €9ÎYi^QÖUâzs¶Ç77üøÝüóæíãÓwŸ?>}~øzûøËò}ôò÷åÍ_žÞ?><ÞñŒßÛ«~5ê7ï·_þ|ûôøù,^^}ûöÝŸ¶ÇÿóÃã}ÿêµõÇåÕÛ»‡ŸŽÞ¾käoO¿|9~x¾~[Å_‹Ú-?ÞMíS¢OÿZV·äåÕëåÇ¿÷ª(Kþeàˆ‡RèHt§„²óÀ 0‰ª>bæ î >xÜ•˜R¾VdycO‘³dhˆ¢n¶‡Øl ëD
+·˜r»Ì9ÛûXÆ^öC÷P»1'ÅŽß 8DÚæ¦há³ÔÂ|8H°ÈUr|;Ã×T¹=@õìšN¶f· ÷èp1eåJYÞábØF-bÛb<:<€Á2‡ùW/—z¶ã—‰0Ö¦KÄÚ§Ñç"—!‰Nqhƒ…²¯„óÝr—jkí\’ËŠŠøc6±'PpjH;&æ D«)pѪ“âUq†ÄÒâÀ½–Úõf¨Çbè œÂŠFÚ‰%nÚµØx̾IÇPö½>ÒFCFnÙ'u`¦imNÄ6”vàÄ·NQ6pô€Ö‘Ý÷ˆ¿&ßn
+{EYÝòææáá ª÷OOÇǯÞýçÃûã¿·OÇ;)à…âµâôöÈÿò7ៃ׀A/‘è…j‘1º7$¨
+ð«ôÒ MS••Y—%6O
+a«ph;[ ˜N´„B¨bÐHÌ ØB‘€#¤Nìwuìzë!ó“Öïx³œÂГ³tn'ßÖ²»"»(„<¶Âù”2;á±TL Uººþw‹—érÍóg¿¾…|?‰Ôï©üYZnØÕtu+dÊà(~#E‰bvv xØêVÆ"+PaH«YW ÓÔó¨^Œø7†^ õh-g ?-{µd{|.ù^g«ùäÖcOD¸Æ…eEŠ|c °‹ ¸BGl µ>ïEve§|gs;á|‰³×­Õ=Ö¯–ä°Õ1-z¾b¹$wNÕæS„êÎqSmpE•‰¦Ñø³6ð›]¹vhe»SaÂ-ñöp–k 1Yõ3¥*e(.òpÚ)D²&¸–—ÝÔ“oŽÇ_U ù¯,§ð»ú$ŒÇ“aB̯$£)O ÑBþ ÀeÀ€fý‹uMÅ9VHµ6`>{ze·ÒhVG_dþäÂ\ºægd]cçˆmˆÙÝBlYïmg/ŒAÌÞ¶eµ=iZ0Á–?ö}nyV@`{=ĉŠ ŠD.óñFý€±Û¦Öø´yûËo^ùk,qÅóïàå•—¼^¡.WüŠ@‚Fx,n\³iâFTܾ>°m "œ9&¿·­~>ñ9Ô2zìÝ1ãM”¬ïæœ!=”j´‡ûÁÛãiºæ.Ov}hJþÖ-èoÔ/ï8@,̤>N†ûÆ8àÚVÜb‡dKL­…HeVï8’Ìm¸L–âá;Vƒ/°rà¦#Ø÷U‚†dÑT3Ž¿’}·ÜjµŸÛ,qdûÈ)uôŽÜ´CÑ-²µp$KDi c¦«ç$5U ””Àîš ïœ$Ò‰îៅ?{Ê®¼«?)¯™µí¸*„‹Qr‚9ÏÒH0ï†cõsK®…¸­‰trf@=g ûµ·¨'´¹ÀÕ¥›–Éjö'êÞ¸ÿ%³ ÎiMï“÷ƒNJ£òç­ý‡3c¿_2Xêï¥2osŽ¢Áæèb÷¨áyFwöì•Š4tü“d[v'èê,W¢èm
+
+ò‹Š^OPa¡‡8vÿ~èåÐþT“³©a+@6ì„—oå¢U¾¥ké5ÝYUí}ûÚ{sY´‡µò[ý9ÒQ”éQ,^9¨£E©·õìŒÒ¼žhÑn(£âû=ˆ/§Œk)«2£q:¡œ\s…u·GöódçNýD)¸¸ˆ ¨A°ÇÝtØ/äÁ>G ó•aaÿš|?c°E
+ûV'¦|gÈ1Ò¬Ñ s˜ÚŠâ÷ªÆ%›Sh¤úZö6*(YÕëaì,¾°ËoÄÚv³;ŠP
+öÛ%?¾)F{d4[¼øÞ¿6›ä¤Ù­»Í)>l]cÇqÛ±‹Š”*dS¬J•s3¨yic]Ë#G¾MFÅè ïFeÚÌR5+Ëž:,©ïÊ[°P(q)Æꈭ_u¢zÞ\‚€g (B"™Ü\tiñÁu$Ð_R’…"91ÐÑÅüú#×—´–æ´h›L®l^±Tp)¶{ÿÒ[÷Xv|)Ðz
+|Бv0Õ#Óõ~èxöaƒ!¨ò¨{ígüf®1ÿð@º?ÞOíK7£mmvÂA#ëEYȨEb©(â>è[âË`ÇRVVt
+(¡Â²àˆšûl Üðð(¤O¾/ËÕ³ežûl@@¯2½‹·eÃùiJK}ÍH œÒ©A/(–ko&9PÌOÓ†W׿떻²=)RlO™u‹™`dŒ±•l²´]Þšá1/…­äHŠT¤R4Š3‚¬HâÊêR¥
+S¿0ŸÀ[¦_Z`Ê
+BöÈÏ°òiyÉÂÇrÜÂÔ=ëÉõÌ+lÊ^³p\2´Þ;⯯i1GäøëJٳ̈¯±…³PÉ×>$“:æÄ6w}<‹#Ù¢£">‘ä™ÐQeÕñ‰­˜æó˜÷gËCq”µuIu‹E¤ã«jÛm,=‰¯Zžä£RÃ¥Ú *³y^)\uR:zk±“¤X\oxüúB•z¹žw-xþM-®ŒBǵ¢íÆe›QõLŠAía5ÊÏàÅW³•Ïˉ“ýضl|¦§]³/§>¦ L-6¥—t/'xýZ/ed¼‰v½ad ËTŽkÚHÖš¿ôw^Þ~˼¹Æ´³\® •÷óM(/'Þ¥ôzêci”BäÈJR$ÿ¡¼6"fn5ÅV”ž}æ}›n:¢.+оi÷W c„
+3)ô«Éö%@Äy0Ûr½šõUÄŠñaÖx­WÓŸ)ÅL1U)^‡™õ1³FBﶎ ع&TË£<^wìøÌZ#oÏeeý¸ÿ„HUd ‘ö˜åwkÔàº6´•Í®ÿ›ðr»²7‚ +mÁ=Ä0Eßëƒì߈oÏ.Aýh5Ýh(TeF–1CÖ‰\šçà'æÈo犷x®zæÉF®X -kG&UñA‡n]ñßúšh½n€•Ô$’•ï·aLÔ¦Ôó¿Žê‘nÿ~aùÝ}¿ƒŒ}
+®†¸¤BGÇႼòâ®XØTQ2H`é“<P¾8 %öá±â¯ûÔZ2§¾•ö²Âtå¦ ñÚ´Jÿ\h ÌËyÒÌ¥ìÓ5ü+˜‰Bº²ÊˆÒýÒ¹ôÕfå!ZºŠC‡­§=m^ <·®žerë¬`üI4ÿQºÏ³¢È°ßa±*Dúu•|ꉙ¶¦Q%‘?Ÿ|-»Ž9gfÓºÐ^"Ž r°D×Î( ÉåIi_Ìì~r¾N q`va²˜¦åØR3dhíç«koŸ5ÅÑPâ©—R¹½{á>”²¶¯"¡b•yçþ^ùï–ÞͱoÌ
+òêÔý›¥ç+ ïoÔ• øNÓƒs¥‹OÔ†`Þnê‚<(_\àÒ¦iÑ×òá
+:mã'Á ­ÖêÉCJŒg×ÜÐeFç>&~Ö
+ín)Z‹¢Á|œµ^ñ.kIsBm© 51F j›‚ÕDo&{cãFìÆt`CSC¤h¨Û½ák@ '²Ûi“"²á3Øüô£)¢¼ýU6ˆ÷€\ÝBl‰FßHØ‹ÓÙ@%)àM¥h4a'‹EôKŽz«ÞÔ1Í4$}©’€-é43Þ)ìd§-/C 'NÁå:l¿wÏõnÞå Á¿ˆ{nêè¸)\1CùVZ¼¬€a.<5rÚׂAYn‹s˜{ûç.t;ä®c¥§ã)„£ÆSsø17e­®TøN%Àoy²»&l›ËždÁÙázÜ£ÃKC)t4ÍÚéM
+]|JEà…ÎW^ú)J/àš1Înñ&Ò,7É3ÝÍòlºU6u\¢&Ós4n¿EÌIy)ñì{W Åô¦z'ŸçôÔKR¥m—b,¿=pÖÏ…‹kSÜ¢½Ì1•ÓøXÁ¾èÐ*UâQéeoo!Ïù/OEP <ýW~¨8'kì‹-åï¶Oõªü Ó†\SZíTO?`8D“Ïpu®;ú»lÒR ­·¢tÎS·«.Ìëó[<sê Fïqh0ãÜžÎQ PmšƒÎ(Ð<«0Î
+éÓ¼ª3*ÚÚo2ü— Öä1/ŽÉ¯’„ûôÑÊÍøJ3Îu7~º°7E‚â¦é+©‡™Wp0§Á“šòØöÉ$+è  pü wÌh_ãÉ".B#~Se$¹ƒ—ûN‚«±è4¾Ì¨ÆáŸHeµÑ)
+ŽFQm³Yd‡‰ÚéÒO‚cb;ã»´©ù%¸':“´8“)¢ì&¹9 ˜Yab™ù„Ò*”.Æ“‚pþûK7ÓÆ8јSŽ5ž4\/þ½²]Orh/Lu^Z}¢èëfê'š«„ñÅNb{â½]Iñù¿1Ò¼* ­üPæëžðgܨLSRvüåÏY J¢›Æ¿ãNGô¡ÕˆhªòU¿yä™ ¢ò½•—ŽVÍ‚t” ™ÇÈgš;Vüu'Ÿ¥ÂsŸ;Pú´íò®IçžÿøE§‘T ì‹·ÿW ­xG¦gQsæ÷ç½ç
+°*]Åa2}I–þÖëjû}u˜ØÞ ²N‡â¤Ý m{š“ù᯦½
+Þ²‚ƒYËOŠá±"œ^䘂{ULkÇLœV,ˆyïקW2« Y¥Qø);•È>\úÒ”yÎYC­BIù ÔÜÃa4!ã@C¶PgäA?Ó^FÚ„£UV SÅR+è[hÊF¸ _ªH4jKI˜¯ûK¤BË˪ih`Õü
+Ü]ŒYq$Ê^n3Ä¿I˜gë±ËƒnJS€Ì>¾
+y;Ä„Ÿk41¸nˆã4Ì@ò4¶;j`%#-íý/`E&¤ò-š6ô
+Ûñžý]Õ†ÊüÓ ž'Xæ­ЯØìÙ|ÝlöÒ¶~%Øœ¯X¹÷î]‘Põ&ÏëmŠv)ƒðXÈk9'ÅøøÇ”€Ç—´Þ'›“n„
+µ2+ ®Á}rÝ”ËÌ:ÃÏßo#ÂUþf¼ìz몱0üWöe‹&§þö6sÕ€4â.˜A”«*$©D)ÊÅðïçy×öIŠí’‚ZHŽíµü®÷ÃÉ h‘"Í´‹c†aTciúHEØ !”çÃ
+ÿIKÁ€¤ÌYÉvGÃ? #S»ë|ÕÉæN#p ‘õUJïu°ŸÑñ%³–äyd´Ÿ”>اz„Ôž=GÄ›I ‡•[â@0äQI¨>p4ýG@NÌ×7«ÜÉ.(‰hø5>¢‡t4Ø¥ATª$•ãÅb‘£x®ôýi
+r“Db/€fÅ]¸ãM-OÊž"P]é o£®\Jôl¨ßRöɘ*Nf@K
+<¤´H¯É>NQ¨=<¦µiçÚÉ}ÚèÍÍ4ëÅ„³ØË £ÑÌ‚f³48,,
+”õÐ+°`Ò÷è–€ öa½­fK´Un¤GˆL'³¹3C~„ „Ý”œçdü±´":¨‹îéaÉù|P¤p04²1«øS3‚†"=“ŸV­Ñ†ƒ¨ˆÃ‹0¶ÉÙÃ"Š0··ñLŠ Œg=ÛA¥O¹[šyØ{§º½K ûu’GÃ
+gÏ£k„9—Î
+®ÝÞ² XãäM8¨­ œ|Ì<ÁØë !RpŠ¾…ÍÀW×½½p;ìM“Cx.Ý+±Ç ü“¹À”4ôr+È›&‹A0ÐÇâöíÀ8bÍàœ’N¦¥œr¨Ð¼rVÈ»(ˆÍwQ*m/fÆJ`7Œ*¥Öˆ åN#cCŠòœf¯J¿ë¢¯$„„}æ ¨€_übûŒïi78”餽ÏðÂÂ'Z sð2m
+Š2w”Äœ h+bhžW$“‚ˆx
+R€;Xï7þ¸í[ûûw;8œðÖˆôMd˜qqg«jÏ | &\ÀšÌRÚ]U¼TˆRÍ”=õËik¥Ä-H\ÁÇù+]c?¥¨`!ïé[ ägVqh`¼Díób:€†7¹*Ô{›·ßazÁXä6§Û+´ŽÅMûÌ jELq#Ú¼ÜèççÑÃdŠ| P‡ý€H…KÕ±{ ÜÜ­W!@¤,:l\9Ë;+P
+¦•ÌŒSÁ
+7Â#/þ$ ª²ŠÍsvÑ+ZÆãw.(Ò[Þ~WΓ‡Û«®§ÂêrZðôdÕµKªn¼16Ô¯»¯U9;í³Ç#V —ÿ}ñîºBMÀ8 –®-á£ý),kÿªÞM(ÌÊÍ=3Õ.Î@&Ç’ÐÉLJŒi9Y!׉ÇèO3{u SFè2õ1-^¦zçòV3œQqì6ßÎ)e EÀ7@îɺN^ðIN\M×/žö™† +Œˆ_ÞgºKÕÍ¥Ætt<—"wb—Äzä–z)c#Ì¡ ~|_òÐSñ¬±.`o¦gdQlå ²V¶EAQõ…ž¼À郵…€Ðë8YÒ‰[òŽ@¸m3’‹§aÀÝq ¾|$ÏX+ é
+}ÿq°²
+©À­ÑØ(ÄŒ#Y,ŸŸ™$v¸
+|$yµï{Œ?-¡M© Ø·‰6bv'ˆÉðj-3ýßN.óF>×Ò³Âþ…Äû D8_eäÓe!÷òg";wÖeãgÞoV¢³ ­4 ãˆÄY\0Ͻ‰Yáû@MÖ`ŹÔÕHÏNed„/p;/º&±Óå¯f/ÿË~{õÓÇWw·7ÛïW7n?>n{|Ý—xþÕ/¼ô„j9
+« õ ® ¹…P,S­sUb•y ±8ü‡ÿäÐTõÈXYs cY’䨂l~-GБ§¦#îUº„iƒ‰dÒɈ¤ƒ»@I ñ,×£ƒØ€ÏNÙ7çÁ^‘f̯· ¬SòtL3Œêù(òè»yRñ„ >Íø'Ü„¾Õ
+´9µÏþEˆ /Ï™¢”
+f€Þƒ&/=™Ñ4ò$’ß%²K£/wÒ£„}ºLº'
+ˆ‡?)aXPrUæÞ2:HiP.¥M”éM JqÛõáEØÚcÕV³f÷«Ã"·'WË3h8­ÿ:í'rWç$"çÔCs–e…À˜sÆí‚‚
+Ð)ˆkl ª€YF®âç¨è‘]L£÷²Ä´j› g(ô^•Üö›
+$á³,òœŒ:Œ!ëB«~,Ïû‡¿Wª˜÷"Œ="õ©"BA)> "êd5åãž‚ìKzo,€«Ì!‘U Z?åCv‡aQD™&$¢& Ŷ ±ä‹ü­ÍÈ8•¨‚©Y›x\ÄÈǬÔI0Ññý¨\f¯I—û‚÷ŠJ
+¥àéR,©÷rÂZ¨ž‚j….»ÁZ¨ €ŽÈ1N:a-¼„kÛ¹
+ZY qʸÁ|
+F<vÉ›3º"2zC~–ÓSOteÐUk…ÈIéq¥«—GçÕÜõ8³Cö¢Š¯Ôus="m§âØ’@k˜ z|à$n°Ö³}‰Ž¦gtôs#"_¿ýª lP ,Ú¯2©Ö&¤ßÁX¯
+ë„­þ°þT¢úõÆùæ)¤¿a0º°ŠVIØÉÊd”쓦
+và@5ñà gª‰… •UFÃå JÕioüfØé­üû
+ÀSg
+8‰FÞƒðŒ3ÐînÚÛS?mOÑ@¬ ³À_Áoœ!ª˜Pk•h­ÎÃ
+ùÒ3¹) Ç¸‰À<Ýu] 4"2‰\[By$ZŸRõ„Êœ¼Ó®qAD(ôÇ…’SVT3ôQkNwG]¹1V£Æ¨èh‰N>y?êi2Þï]#‹L3ýb˜¸ñÁvoõ˜ã=¬wŸéºÙ6{¹Éïá6`µÌE{Ú~”¾0m9`ËN6VZW" ^`\ì
+ÐhL.‚?jŸœÙ9΄)“?8â0RVœw°sDòÄ´}çç á‹èÖMù#¬¦UA&F™x!Ã?º7IúB„1m¸{WhAÎŒéxï«Æt7 „œ«d>>ªã¬xb À›y£3؆L‘"!MEÖ°Ça²t8Uñ¹¯Ky¡&:Ü $ú¢é€{w“”,OÇ#'çæ#ä®D B1L6È$Ç™ur÷ª+ª¡2Ø]§¥Iè–âõ®I±K³Q÷ ÑâÙïhôìµK–^®@Dº¾Tªâ­Ûò¤jWÝ _’nn²d®9‚7h¬È4:¤ÄŽ6h6Ž ÆQ$5†Â$C “Òߘ„Ù¡´žü
+É6ïŒNz b c•zÎ芣uÃ?--XT,}A§E»}‡u 6¹§³:¸l¼ß)«²³:×q ç’ô¤]hcut‚æ„cJÔ«ÇÌäXsTÛY]/ 3ó-Ç’3Ù0¡ ¡CLýÕ%£ °|‹;«ëÞ¢VKöò.VTÏTˆew8õŒÕc1—Xø˜Ðº>XÌÅ8* ­Éð¯´þ¹Â.ÂõEc ²§edjb§õcàzÕ²¤‰’¸Î¸JFe‚ · ˜@j“%ò_ZôÞ¼\Ι̠GAL°rÂ:ÅL²Ë„õµ¿’[nU°Œ¼Ý©s€‹8ü‹×ÔÓ×jÝÒ}ó»i ²=`ö¬N{ªYÞÂì“ÕqI DW¿ Ò›u  ó$ux·IêׄÆk$riRöê2áOú þÁ¿ËéÐ’+w
+H‰Œ—KŽ\;DWà=äúB_~Æ~CïÂ@ªö?íCJ×hçUá•®¬HI$ƒÁ`ý§‰^Z¤ˆXÝçK‹]³«Úú& 2U«óQuõ×ïR3UµV¬.̘­¨ÎênqŒ_…ŒcÕ{_nÒ*ßë.5!Ý´ÄWÔÇ 7ùe>jŸÅ§ë8SËÕ¬KiS›ÍûÅuÖÆæ‘‚ñ^}
+ßq³Œ)΋ã*@V;G´^§'È®R‡t‚ìSZžãcø˜µÌ¡mAº5ò%•lè3ÎRíõóǤõêÂUzí]^ 4Û 4Ƈ²@:[«E›¶á\V¯RÔ½4!?QmW1©DÛ|D@jUÌ}Aòª6§ ×|Ï 4Fon<ç~O›côQ÷Éôëǩ؃ú 6•Ï6JE½Èko¯ÏͪTM¬tëP@S8ß\J½Ã75²f­jBŒŠOeÎÅéeòU$ä*³4ˆPëð}ÓˆŠµÆu7ÉUã"‡Rmšª|’ ʬ= ¤°E\•´:#‰`¼AÖ©¤Hëó±0TŠAš¶kÚùu¯p²åUÚÕárÔqG¥Ãà ù²¦ ¡]F„íc—]¢ýÜy¦Þ;pŽÍÚ.<¤—^hŠæ+ÅÐÀ½6õÍžIêh
+«¥öש˜o¸i\·#jYÇsù›E‚>Ùçp(šbt„bôËFë:Hí²âvzš\ôhÚ¸©CS™ÁÓM-::j EÜóŠ„.Dgµ¾Šà°
+ë³h-¦Eç÷,å$~qÕª£8Åý8‚ ‡Pa{=ßr1¥Ò>:u´SÔ(BWkœüï¿e/Ά†.RÛ‘ Ói È?zK-»¡òüµ"_è-””:ŸÐÏçÐ ®5Zm‹)‚SâéÝ? ¨ÓSälbZE =Ó o~æM¬+9$K:ê}ÈëÐ-@¤FŠ“ŽÕ—o=G¡ªzÓNŠÝ˜oÍûZù&¡ÛÕHÄd<ùºŒJ*AɈÀûN~zü»‰`Œc¹Ñ¿sÌkiwRvG®1­H”Êsf«™ÔÍ÷UEECj2ËùYÌ …”6ÑÙiÇs‚9äÅ{Ì¢…h¥¢7Éœ–ˆÁ‰áØçi|0©Œ^꾉©y­´ ’èÝ„- PÞAH„µŠP˜%ž‚ŸD"ºŽWÑ•(÷ü<õBô/騭bž@/ÜLÌËRÙ âö>––4&ò Ž¶¶Âîü
+ <fÁðÅ
+D×I i>€ÂÛê­#¿d¼EÏè÷ \…Û
+­£õq€¼“¨B-iBñ6»È,¥CêûX䉟£ÓÇò;9Q¨ŸÆí'P@ vžâÔ¡/n P ŠºmRé–4Ó÷
+I¡ôùH“-ÄÃ_a·˜'…‘²sU
+6ÙÁ5áG–çpªÔ8¥úI_(:@›„’©ËWÍ Ý†aWúùœIï’´-6»ýäÆ·áZ‚#Ö ì±––XÏÐÉз$É„гp⠵͚U;¨´“œÍšÏa]ÉjWÑ{d7èG­&†! J3”þØ›ÑO¢zZ3ð˘P¸cZ잀-~@²Ô2•B5­Êi„ÜQq–ë8W©Š¥¿±ã4>üñ˜oN1=ø÷L̓Åüž•é÷w:&š«ds…ôÓÓxzŒX;œ÷ù³i‘]…~¯€Ì¨WŒžq;ìŠbÖ@è‚Xê>&†°3£¥u°±&w’˜÷´›0ào[ö6éRV
+”™ƒu`Qºíù_vøTù‡­&iÈòUçÀæîji÷kØlö×Åf-»çì¨Â¾øˆ©±H†Kå¿‘Ž¢Ü?Iœ.a
+^KŸoÙ!LŸ…©^Q;ûFos«°+4ÛÚÕ1ŒRFÜÓCqqjL†ªY&c"Ñ,Iv6=O™<˜*™nØOÎ(y%8Â0/èâ~MÇ$ÂbOzzÍÕáevzˆº2—ò¦»-Þ’M¥—­Ú±%b¢ ›ØòœÐ00Ó—ý$½\=ƒ326äï~
+A†ñtJÜ}Õ¿tæ¯4 |“¯÷¶ˆº^DgB%V$ž Ì k˜~–WP¿pG„ëÈÖ ô¦à[…tçÑ‹
+‹]<ˆ;  ‰Ëõ 뮂1·^aË(ÄVç\“¸£ Œb<^‰O0Æ—aüQf? ìŒ,T†®Y @÷Qa軜,tÁŽ46Z/]Äœ%a¬è±|’ð„Ðß9a›¬’¾Aâ&z‘"SQò¸Èü81—›0Ï5SsYÁ<ò³ˆÄ\¤¡Ü~M¦…ƒˆ>€bþЛð.l}Ô!.C9<6Ö^viFÒjÎy=*qˆì ~層Aè±/ŠÜ²"+…Ì؆SuC&&öƒÁuWJ˜XPÛá¼PÀ)È÷º
+Û:Q·Ü
+¿Ÿ$¸„£ú– §`!’1rf 1±ÁZ¹Ùce$*ùNïEóxå’§—#0´Á;ÊQí€ùøNweZø4¢Çºû­ç`æOcÓ„6.V>¦D.Q¶sD‡Gµ£õ³fH"{§!w¶qKP¢ŒH$Þˆ»èFнâ!IÙ,,« ᨘg®÷†Ç‹‰€JSþÌãT—M7Zû2G“ç ¡w è7@-5cß¹-3yÅØ6‚ÒóÙó‚?B«ÐEu!45¼t„ÉAà kŒ2xg¾Z¸ä"&Ç?ÁƒûÎ3ƒÌÃkÅçyN¸ŒªKܦ4|Æe!(*¯oTô罟q$# `·øù™ Úá(5ë®'“Ši8'ûH ±º 8Î0ód(¦L¦OÒµ7„ÈXõB~k. €¢9ˆ‹U ’6qì1€€ âETð;Ùhi躱ÆÒ–ø4qÊ~´ daT
+yº¬þÀ)"×ѤØ~^F]ƇÛ~°xeƒñ´ý8­§þÏöØCÞxèbììκoöäÛ¦6e‹¡%í÷ÉÃh$P(É‹héÈk˜a¬©ExaòÓ‘,~ÉÄÈβ3 ÁœÙˆY @°ÎàNk@xÈâ2›÷¡3Œª™åæ%Œ[ƒÛ,?°'ÝBå¥F†Gq¾ãj°47qÉÚíKÚ
+ç´ºƒÃ{¶Ð»ñ\°™··s¶8¬ôg¶0 ÊôòC&8 þš-XD2LÃ÷û\Îü!?4²’‡Ü“á«° €°Öµñªà‰MÆu•Lôe5ƒ™_x<È“ä(Gg¢€0§éA2L°XÙW•Ø0ùµ|k]Í/Hh_ØB®ak@ÒX‚ñQsM•‡ÿ¶H„$€äçMŠG@b³i!•´®Ù·U’TCkžt íêƒ(ƒ Õ·âxü AâG²‘Ë0YO—Jv‹Þ0©™W ”Ų0I>”,æAl! ›{;žÃâ°0Ððc:Àe¶(O]¸¢ cz^‚Ì·c·à[xîKi#œs$µ ÍŽ©SþŒU[ØÚ\e€¢oæ•Pøš—X˜¼f_Š¨ñ Y î8\x‘;æ±J~ðæN€IžRä_¬WÉÜC»ÉõöB?Ò¨¤ëXB—úÊMþ¶¢¦” ¹o›ßÞ'A –2 å8€ê/c‡´Äò+%ØšLØñpÕUL ×¢F³èJÖ6ê©/ Åö©÷8U}
+®Ç‡!;WT&@
+Ùa•À×IY¡t4ÀCôO·ÙSP ™]†þZó´Û…fö{ðîðÄ’£€‹}JN ´&ÁÖ.ßÎB1…n%¸v9,™97áÏŒ#fbߧƒI–£&w|¬=ŽnĵXu7²™(µ¦~çG‰>ÕñEÂ4.÷ã9¢èPmû2ü(šéÈ<ôö@ê “(,ù»-}È_“Nj°æŠ8•§yû@Œ†Ù6ì˜Â"yŶll0 FIÁÞMA’Ñ ™)ÛŠDÈ}ÈÅÉ5G浈L˜ž¥Ã¾–:àù(±¤Ó»¥2½hx<=Tïð{û$V¸g–ÀŠPlÔ,oy’<Í2ñ÷êW°ärNá. æ,P8K§Àa ÙäÈoS´
+¿ cPt’™C· p@ïgÑ‚æ H¦‰±½ýä“—ð!R=w%gã³Ùxþ½8‰ß%1WÛîömà»Ç°¶d£6÷Éì 4§øµ²ˆµç˜ÛP½9àÃ4?JÖÞ@r9€rqÃñ'Èrë¾C ¶ÂÁèÄ:¬„ Éèöòå
+™FõY ¼V¶( ‚0<×Ü9$Èfê.ðñ$º@ƒ1ÕˆU´Y^ˆôBk”iÖ(5ƒoÓ_hµ¯/-/r&[\ÿo]ôfìV {šÆÙ¼P„„L†+ÚNæÅ&€W–ÅíõNê~Ì¥¬õ!­H
+‘ M®ñ-Ö_`>ymVÑeŠOÕÛ†BCÛæÂï¥õ(îÔêäf®¶çÇÄñòÚÉòÛ"ˆ‰¥–é‚ŠpÚÖçrà"¢9î‹
+§•î£hûiES?`˜rIGÞ¦‹Ôq,G…qä[¦·“Kš:wÃÛLuŠr2'ßœ3e,
+•ÂH3ˆoše*ö¬ÃÕÊxú”y,iÁ/°~N ͺõ;¦*B&…‘i2d#~R
+]%(ãd_qÊc±˜£ãTøœXáPtZUÒ9{ž†éÿm?QOùãàaÏcÑÅL,8[*¹‰Ù ý•ÍMíN¢UÔ8JþÚf°9ØÞ ¡zsÎ CÍ<€áÿßl8ŒÂú6ý[Áñ–F/—¢Sƒóóø‘ï:}):qÀв`¯
+ÎTÁ¼Ÿ§
+˜ Ÿ,Ó»»™%ó€äƒá¬5¬Lƒ8YɶÏÑÛ)ƒýg”ˆÇ‡ò4‘< ¸›|iEŠ}
+2Õ¬9VîêxŽ|'>i °/…Ήuåå ¿²JÈ°+¡v·8?Y}öÏ$èZôzÊâ‚LW-‹õ¤9YˆUørÛd²0ìñÀ3[I› p•wdP8ÈôýåŒññ w¿d¾”¬Èׇƒ}чك­òïÎO=Æ<r»Ò!¢³ƒ´Y)Arø<;ÈëÄ/—¹,ÎéMŸûwmÍe‰/ý=U¼éïo
+Ò+׬[3‹´wÂ<Ä+Ép[æ«ÏžéÚ x2^Š:êt}†iÉ02˜a!'r(I‚P[Ž†â0ø«¡Ð*’–B GC¡}9ìð(yß&ê9¥”l°6 Á(>ù Ä®Ó2$ì…>6}ôfLB©J8vR9“äWý‰a5k‹¬„ Ãå ñ¹£ æš^äÙ->\‹>gÿ¼)‚0H è7™Ñ>†ïììpÎ+¼‰¬2×ËñÙ#Ô¬÷¯
+$QV¼
+v£8‹Kµ2°Æì‚ÛJ+Ì—ÂtlꉈƠ0%¨¯Ké4rV”ù´ý²iZ%¤ƒjéSŒ§ÀÀJ¤©¬š ÞæÕ!GmfÆ“,2=6àpy!]5Öe±ËTh¡5K³&¸Wã:#ú‡’B7ýÛØÍ ¸Sµc"€v€zñ’Zh¦Ìu6 *ã²yŸB°FÄk“mW8êFF·LuœùØöZ£| 6ÞKØG”RˆÇ@Ñ‚’oûS—"» .™ZWnŒ
+Ä:u…b!"h¥­¿&n“+X$‰GÀvEŒ
+gó‡«Vö„¬{SÒiñ‘¯—¶¿t)šYDÌ:-SÂv2<bw³šÚÎɤV2z y'vg•Ðš Æîisd0b+zæ¢ L¦ƒâvkŸ…|n*øš™Ý°’ª`p™•sò¸ŠÜ­°_˜œá<Ú‰W
+‹Á\SÜÈ[Ü7ÝA6}ÛÛ›dàÑä*íß âYÚº’7Ý°`xÐ˺mtÊ«:É.ìÚQ¦!„Í= jüqÝ¥I‰
+ÂçÉ«z
+UX#o÷s¨:!Ö„®ëÕy+Z4Á )úžZ¸ð2«d¹&“ÿþ€ÄôqIì*
+þƒP|<½ÂZ~
+z×ÄŸ4eÁ7ÿl2qýšé4î¢A]6ÐkÝ5^ZQ½Ë(¬(Sžv^5¤ðfZÇ’ed³Ä¶jQ¤=øÖ ë‚ðK'\¹œ‡Á0´”k0ЗÃsúKBÐÚyqa6itF’1XG´äïtf?ÂÜ‘wë±v,"'c#DÓ‚°/‹¦òfa…p7ˆ©½ Ô?å)LyóûW
+¼œ ì.ecZ»Ô ¨R¤¹E<F÷ùŸ¹ç£„¼ÿiòº~ê³V
+}‰åŸ’Ûð×p/넨Ìéî8_Å,E¨RÂs€@#¸eHŠãÎ'CËݺ¬¥ ðp‡9ÉPvì2Û’ª×\P|9“ KAÀ¼óç„&ÃÈEÛŸãÏJ²cm„Í£ Ñ[[õ #û/+Œ×®*”gb#j¼Å8Ì@ 0ìʘ^@ÓÆñl%>ÕX²C¶ÅŒ89z¯l(ö½6ìíœ[ÇosœèM·¼Õæ>ÆïôéßOVFÛ•|»k«`óìËÛy¿@¸˜)æÖJô¼kØiŠ!k*¹ÙÒ-T
+í±N¥‰›ì¶`ÚU$î•I‚Aú±c„‘Ù9‡l¹Û?¯Ê¥|Aä8‚Îß‹fŒxºp’ùй0ð™*ð««\_ƒ,»?ë{œ ¼›ô·Ö5? Æ`GXC²žy(qJÿÂíŸêÈ™Ê`ÃÛ_‚Ð!E»Óåëp¡beŒBæ`oaÉCš,Á€´µÉ‹)ûþ€Þ{ÿó
+™J,óÒ¼
+/E?sߺ†’h¦áó¼¦$¤†ûM
+¶â~Ý' ÷Mýùd}ë+¡´«ŒüÁCð8]Í·í3ï˜ûëê"´i¨²ÙCÐýu÷Û>À 9§Ñ5\}í$a˜ÌIJ¶Æd9`²ÎL;}b%¼Ùðèl„¬]•Y&¡»¬ÑÉ”VåU <«"*‚…%ŸÈ8Ì©÷Üü~„zçö+/f9=dµÊx
+¬+=Œ˜.ã,Ý=ž
+•Nchy¨íw¡r=Œãë|3€ª_mh¥)„ÍØHÒ÷uÇÜ|x?9÷VwÜX}²ÿb<¼ƒZ
+¶ï~ÝG 7BˆV볩§Ä
+tÓ¸a[1I¦éüý
+“íñQøÒ¹ ø¥†ÅB~`âÓÄk…%Ž ´ê\u%,·É¤ú} 5Ç/‘‰º©…¢æ|hê-1ŒíÜô^Ù,¶kPDþk—XŽœ™[@¦ër¾ä©ý$›‹R¢’vÙ°? û¢IÀåSŸÒÎÁª°[,o9ü–ûôoºOñ­6Ñ.üYà¿®Ô÷µy)+ž Ýb+¯faAW×$äç¯;(âŒÛuï ŸO¾i“.’¨ÙVƒ֋[S#~µQ1=þé²R¸Jüt½wÉÀVW>¿®¾Lã_9œïë0ºZ5¶CÅ,r÷
+m\#MA¡Ð8’u_!#LDO—¤Ü(\á!v 6cIxJr@”‹VËúƒH˜îƒk9Òc€©aì%á) ãS°ò‹Ò #-GK!ø8¢iÄ«(Þgh½ä‡£)S€Á !ÿÁZ¹
+ Äÿ2D³0”?
+%åS:bBw‘Ö‹õ¼ŸÍš4t™®r§cia㎠§—ªÓåe†›£2L¾¥ckÊ÷¸àhäŠÈî°">(
+¥D½>!…˜W*:ŽcTk?)‘O£:LÚ&ÜúblÀ3ÓádRY)Ë~úMd7è¿Ë-­‹Ü‰WvTµˆÿY…&ºåg
+vòIïÿ¥¼LräÜq |•w‚‚f‰k÷òÝÂÛºÿº¿ ¨l8%\ ¶++REÆ茙ÂÛsZ€`uÒC@¢¯~ùVä2ðHOOýÚóB%ÕèjÛ¼A<åDgh¨TüZ('zAÝ[ŽkaÙø„äOŒTi+)@32dÂ_ÓZíUd^ŽçB ¬_tMfcb©1a«DW°3—dz½µžïùÙ£ w3lrw]_ÆIJÛ~úC@LSO꣙éb}‘O±
+Õë&Buì.%2p¢¤€¤Gßà‰Ü72Š†NÐ;£œŒÄ-3†•Ü97ëÜã#«oŒ1I&>œ\èí\¢=@~–f[Åd.''A #r¶oÎÎôfÇ‚•
+´ R¡~øJȬ4ëÕCÐ42ÔÁtö[¼¹Qí…
+NŽfÈÓŠU¤S²A
+ò¡-JÚå´ótˆÌ:þ/9Ëeö÷VÐ,¶—d”rûò!ÅI»z²Œ&¡êm]Ûï¯m¬$’¼ã3ª’¨·Jþ0°4`DÄA5ü²)4qß݆R%$õ„t4©ø›J ¨
+µäË…+ú
+ C[sõWÚ£»`“Vm?C!k1,‰‰ˆUè,eZyæ3U,R¥²°äÚ l,<&Oú©Ãï•eˆR½¦*ª\´:‡ëÀ}l~Vù\«î§(—}X‡)ƒ“òhœ9ÃþL®ªÆ)Ì/#-g{³­º½|'h“–¡uÈhùô:uÃ(_gd%^Ü Ö&^j)¯Ž¬àñ"†×2NNÐ{4øh’_09!VNX ¬âÞLÉkˆŽ<ˆÈÐ\¬÷îÁå~zȃo%oÕÂXÝ@ì÷OTØÝu¦²2{è:Ó;wÖ¥\ŒŒƒ3—X_@4Ðl²!RO4y}u. =0à– b2¬Ø
+ëP¸ƒžÛý$•èh4œŒ»Çùj2²sái
+[*MŸ.ƒPR©—ÛÑ[غCÞSÒtköÁ ›´OÙ×úè2€Ïí~z#…ël½ƒ6ÁtXYÑSg=Ú>+“I'˜'ˆÀÄÜÐ?iHB±¾tÈ(&jï OF‚#È ‡0° ˜1˜;Æ kH#æ€P”÷ø´¯
+éA
+ÚéýÜÔ[t-Ç!MÀð
+:]|,±uˆ=QËL.Ǭ%cèV’W>Õƒ—Qô/ïËàwy[¹’ÙÉ.yqoûDà\DWµS›´}tõÅ
+`§Êó½1"6T”Ñ
+Ï^ðÔ°|[;A0—| ÓŸ•"ã4,‘Å51Ý]œ…#d8öi ê\Ñ‚Sá™øA³q*uÓ0£89„©C ÈVñÚïoùÙC×d¿pmüóa <>–÷Ñ $*égßI8 ,yQãùKö°àªL“0î£ð4¢•d†'C¼ xÕ_²´ :D ›¤£_6–6ÁˆiêÞ Z
+ Ú>JisK" æe"èÔ
+‡÷ @lbÂÌÔ
+!Êóêà L.‚"{ºÕ'ã<‰°%IíºŽ Î_Þw´Âthš6„à´•OÞbuÞ
+ö(¶ t½ÇFE ¯)‡)6.ø;HÛÉ!2FÐð¶O’†WæPjjÿe¼Ì±äÈa z•>A?î‹­1çr¥û»ód¶TIätyR "“@ ë¤/@•sl]%ê {™V³E¤i
+Y{J_ÄŠÉK×…†¡àôhÎ¥é*
+•MåÖ¾‹”rzÜí4
+žB (=| ¶iàx9§¤ÇG%HŽøW1ný¡9ÕB›á› £Å^É˨ÖUg‘`Òôbá}L¥ç
+™ªm¶°…Íiy,DQ³(ÀÇæ·Mâ«â”…ìô"1\iàÅæ¡Ë]»%º*)ñÂ4dÔÝs’RÚÀ(cö£2Ú9“òžr:b4•ÓÎI‰]Šf(ËKœEŠm)žªÉ`¿ŠðkpÛÈÚLû½*A­£m­˜¦$ø©YIdÿ†‹ÒË#tå$X¨/r‰Ÿ2ß“A²[»aC #xïÃúÇÜ2á€PÑ· ]å4%Z-Öbèà†S²¦‰°âÏhó—tßÎ9~|ÌïM7ô9|´×«x™ÒÏw¶E‹X,bà!Ö°…x±ìœ÷û¡(D¬*Yã2?TRi^É…¦,¦¿Y•`A;kÂXwŸ£¸ovéâÕ"(“F"¸–Í̧c/ IìÍj|:}ÃÞÇæùkgî§O§U”Q¯°{ X&ûåM¼¶H¶ƒäF†¢Axþκ)Ct$/ÚÊ›6|ÔË‹
+x{ÁÆÞKÅ)…¶BEUùFqv’ Ö¾
+I-Û5÷9XdèVCï>œ9¨¤²W¡í2á‰!€LGL‡ã±V‹ù4’Ôi×p/a½e_íUn¢úÒÈûzY÷0®„ÎI,îšÖÙ-r%,6@÷ÖT% ærÌþb=JÛmš]TÔ"tô‹4"†˜@…â"6ë*¨.-Úçm¯¦îFm:¢ 
+“pB8º@¡¡`åQòøP’Ôt6•«»õø(ò°N>a‰§8w=ë½"gÿÎëÞ*ºq‚»Z÷¢Ë€×Ù1·Û1þÕ5à~QW"bl9XC1Ü-±!X‡yéJlê‡X Š‰Š”öàÀeå3Qf‹i**³Ë:¡›„ÛP¸G¨‰#1 }f’›¿d F¢ðË
+@¡~àÇ‹ŽsøW< ®c_Õb
+©@¥ëkdh ¶:¹€ô7Õ HGQ
+hײê2>›%‘­¢ls¤Å}Oe;\Xã lÉîÓï%«É·žçÜç c ìr1ÚÌÞ8匥Œ ƒ·Å}Ó u²&ˆxŽòဠ1Ô·WQGLý¡¤±MµB€­]W}ƒä?æXŽý!â.ýÅó[$™Å0ÖÜŒ›J¸
+þ-Is܃$Gl±÷Ñ­¤M\>¾¶ÆKx唕²B‹<1C±ÿ¸="vÓÁåXý²:Àù :÷¥ò/¦V%¼t
+MælñAGò `jWf°õ—š›Ç8È °ia-ƒF½” gbysƒ»¬-ž´+ŸÍ‡+Ã)‘§Ô}Õÿ¯Ö—òŒþ©=ˆØïNéNX]uDÇÈ[~[†¯ÎÒÉ,׊½©„.',:Rt¹V:Àñj#X).àùò¬ ¿†Ž‚Ïõá£ñ\õ¶³ˆmß$°Dò`ž’JÆHÌL®kŽIЕéÈQØ[%lûTCj+«ÄH1 ^ŸÝ¼U14&]¢Š¤ûs¥Ð,¸RÔÉMR¿¼™HŠ–&˜} œ†•ð)x5oí© uÈ.EÉ
+%@nb yìŒq¸g{VÔ²ˆ¢#Ea\š¡e£Ê 5>cçè-·‡a}3ôI%ö‚kàÔÙ…b-;FHã@o‰ljØJ±CÛTò••Í"|55Ô°³2±{‡Ä‡HÐljsRÌP¾¼@íƒ×Ð’hÇ †].ÍÛ»ˆgÜÆÈ舕Ô*…
+tö ×~Að&ïm?ýjÖròÉpÜÖÒ|ËÔmž®8£?õI(kÂÎQèÃb`pªòQÍ—@Ñ^maµwã†v_n9°¤ÉßZZ¡"l ¶Š·B——ÝÒƱ!O;§Éz NØÍ¡“(pÐ+ë*‘Èñ÷b±GƤáUÖx™¿£ä´%N‡ß-’ulAÝ°8‹€èL¤(D|añÒ|Ó¼|/ăI<t S>jBÝÛF9“
+Zæúén®2¥ §Ç.­sZa‡9¶ä²Ï ‰ë”ì0LÌ4€à6ŽsJ(0y2éL<<#MÇ£X9ï¯awY›œV °2ZŒyì-óS›N7öM´²Ë J<¯%çàTe,4N@…}êøç’¯æ°àòÎ5nüY0ª|NÛ%æï ˆ!oXæœ/¿?œ/þeEJ{Ìxb3·‘@'8ˆ‡@‹ß¡1™I.¯.~ˤ?¾D9Ó$OGôbò(ü·Ñ
+V‚ D/]žÇju–«KcuU9}SÐ’ì¼BѡʘQB&#AØÅUåÊ@<\M¦ áMH!>hrA à)Û¯:šìšL˜Y—ø'8¼jr1ÀWSb®&ŒlÆéauG“ DG‹Ù ,ûö^59Zb ª<Ìê›™ÿÑäÌ8°>,,W4/®1ÖÀkFsÉH*!Ic–Ëؾj²|7ÿerV
+O¢ÌOhÙdLaƒÿe¨s¨à–gˆ®(«†äÛpÉéA”"O”½¢›(ë¹#Œ£ú¢ÌÓCË
+*sQŒ'Ê'ªQÖBΘñ˜’Ù;¢\°>”*ŸÖè©„dÕÿc¼\’ë¸u0¼‚ìÁ+8Å'HŽ=õ.4•ö?Í÷ƒ¤u³Ë'÷V*–`6 ü/àtÄúÑ• ¶“¤Uü-{råú’O៕¡]™‘Õ /£÷[Z.®\_Š{äô±÷Ê»-ßjN–{kò»E7_>]|­Kò„žúÉ—QL쀅sþ³ž‹/ßÙwóåÌæ‰fF,eô|ôSˆṄë§_OLÈò@qÕrz<Ø°_’
+Ïïés RšðåÉá{á«ÃhPüÚN jÒº5 ¼ˆ<ÈÛÔ5‘/s@‡%}9rvù%Q1j5‘"MB$hT;?…*Ï$X?Ç´¦`
+c«Ñ,½‰” S¨)½®åP1¿D[gnm§•Ë)Ä8*ùùXl Dé~ø]èà+È6¨\§
+ÿìeP£DÕ˜±¥ÒÏ×C‘¤<¢æX­ý§§F­;o`¼ ©†³S‚
+–Èá¾Ë{±¦‚Yî3jÙbÓJ¨MZ+ðýéW¯žóÐIh?
+ýjdŽsÈÊ©KƒÁûØ¢û3ªB58T%…¬
+‡Èûá¤iü8•ú ×K|/Tüœd Çk ƒH# ËB³„¨
+'˜¡-êÚ®ì4äIâKÑuîŸE4¬½!Ûd±¨ÁQ¤¥¬=²Jˆøg¾ˆÑ(Ò×Ö7…IòAI}½;À’›LoÓHqx-‰–’–!M FÁ±õ~ZPî"y©˜ Æžµ_ÆZp:ÆÛÉx!5DZ_b/2¥<žî¬AˆM Q´„J¢TˆÙؼRÂ÷ê+2ý^Fä ŠÈÓ<©³ü¡
+1VvÅ&I‹ Ü@уÁúÌ_8é9Δ¿ˆ•'`ê@7Îê–µÙ¢î½èBŒ³pª1‚㩨+²#]h°`òçø¹wŠè$Àç÷ÒºfE€Is=TXBP 5¶åŽ$ œGž+Ö¨d´‚έCŒ€SmÙ¹M!ˆˆ¹ÛB›y
+a–ÅÁhGGãb]> t¢höÁVÄŒÝpö^òá-$€!)$’nýáÍ»ÙÂ!ûŸ)GÐöë0•@~ ®¡Éï9-¶Ì…bíc?ç}€3»Üÿ¦Â.±"Úš?’KðP‹|ä¨úg‚'vŒÙ=”]
+®–fJ FH §õ¤ù“¨Î`Éôù´hü ߈
+b¡žü_%ÈæG¶#Âв@„Côó,ñl<Ç CSÆ›fh¾vE.mÄõÜ"Z¶Î{‘>ïI=(FŸM¼õ—Í¥xO(Þ!“Ú)lLKõ ZdS¨J^Bj‹¦•f­ýÅþeC¨çT*ü½=ÒN×ð‰_dÔu¬a(2×JOÛ‚ñeOgÉGÚ5*xƒEÆCɇCØÇ® …<œÃGé<4Úz²… É‡ß†u
+*IS@ªsÖëCQ˜†¢‡x½ß\šý«¤ÈSEŸ^Ä…y?_ L¯"’/ÍFÍú|<8“4[-~9¢F,°µVø¨µX¿•‡:hÈrwÐRÄ" Oy_‡èÂ,Z(9š}¡®ÜIºïආ§’6Öƒº¿s-¡ƒÌOúˆ}bAÊàê;“u0_=7/!Ý ”¨Dê{äÚtÈøÙòþëlSºÈЂ‹“2[È Ž"8°.=àï/8þ½—Ü*€ÀšD:qñkÖø&J%-Ù+‚wÒÃÄgR˜PvúàÇ4 ˜ûD)áBA&Öãº
+=c!ÞØPÙ‰SóÞ{¸3{mä “\ ¬)……rÜi„¯VÅ¥ãqí™nxpž0Óñ’b#|0æš]9ÕúÊ”¡V8eÍd‚n½®OIšµÿ‘Š=vUB2kúÇò2Ú^ ¡1íK2•`A¡
+¨RÙ‰=K¡˜D*Á”ùsŒvêZ¤MRz%æ°ª„7Ãb§oö5…t%äùpâ¬Ff ÷ô\þ›sȨ6äC¾
+ßü¯ÇYDº
+ËKrù/­tu=P(€Ä±¾tÓ0 ¥êA%k=,
+œÐeø°OèŒ"»2\UÊýŹP—5.âBÛ«¶PxFd]#ˆŠæ‰’è•J ˆ$a£Zá!D Ç\'ƒ|
+‚¾b,ss˜°`œ[¯ÁLið÷à ¬y:ÿæ£ó90åÝùh&ˆ’Ø\m9Ù#Ÿ¬%;’$ƒWB`bù¤¨Å€‚‘¢›“1d™`’*Ò´”E°A«˜ämkXõä$Œ‡ó]˜$s®Ì2v};UcÓë†|ß(qrïÚ×\¿êÇÝtjdYL-I‰L[ØMyâ…Wô(b¤PþdÈa¤ôàŒ¹Hzniöà¸[Œ$Qê07Æ>!k>S¦/¸e‘Ø™Ã6²%œv}RxþªDp¸˜¶K6y9„B¢$Þë'Æ'7EªT£‰Ê뙧1s`¬4 /5÷͈.šÅZ 4˜<Vr ‡08eŠ«r(ú Dq.‡PãÎÓÇÃ9´‰!*ü¾ŠÑ¡N”Ïekˆb5)èûM6Ã-ÉDh‰ cvYN vÅm–.c/g2–ë ¼3¢8Å!渿ì$}!xðÞ¶/«,.›0jâ®Ü±•s·ÓP-+‡uÕg5ø]ÜH{M?;nÞÐÔ\Ëçèn‡À‘Y’(Óïè²ä­år˜«AæOYª'@Ú¾±Ht÷&³ÛÈ1#4cLM)fÕ&3?‚§†ƒÐUDs'È×;ÛåKXØL|QaãÕÿƒëý÷‚åIO:Z™¶ê›
+hb¡t¨aósˆ§¨Sñ9{×%Éø©,=  #øôDWᔳ,#"ý<Ÿ£1ÃGáþ×þdhDþÛ¦Cp<ÒpCu_…Y#Z züÔG56s{û"µaµŠOšVÙsˆîw‘å$8Þ7ó»Pž%Óµz°8í!´È®„ÕhM=†B(ò`å ÎMýx6½Š_z–‘Ü-¯Úqæ…¯<BâHßä ù tÛCŬn
+ºL{}t_ŒÃuï€õçŽÅçxøPm¸”%” ¥jdz ­G«¢Â:ÙÝ®{}½óMúô?þ&üù¿_üÿW—É™cðJ0ó„™W’¶uX_)àˆÉ•e…õiû‚ Jyyc* y†aqäÙà\©½ <»öwÆx
+„F°-„îIДU .¨ù@l·éÙD5
+lðÌBPòÃ9M;ÁÎóÌ|þY *;ÒÎOò$P“©~* ì^/btëóTà#ä[Ÿâª;HdʦÓ‹sî_ÓäSã½ÇWɘäY~î«^‹ƒ¿€T{“)"ÙœJl^bb‚+è±U/¸ê¥ã¯Ç¦æõcÓwÔqŠ¿ç´
+ß+üãBýÅv…‰¬tkúÂ=:UöV>¢ùÞ@R2ý¥ÃÈÓ窔ˆ,ƒÀô€d/ ,+A°G‹ÿ°q®Ï@àè¹H—ô®øÓf°Ð«b‹_ ”©îV£€Œû9Ä;:Q$^ ä#ÌbÚ“ppXºãÞMéòA|óÈ#ä5^¤åv¿-ú ¦REFo’¦#HÃ"»HȪ0ÝtHÎrahmñ „|ƒ/UšÍ¾8¸å[”‡”[ âåK“0 ‚L•aÉR–iEî
+Å2d
+±á;´À†V£'£^+~}sm"ÒcvA´ËÎ:9#)™ÐI¶•Ì¿e­l’—}•Q€ûÒ¶.Ì·Ô9áwÌ{À‡™¡Ùˆ~’dÒs@2¿C!Ñf;Aö@tR<Ë
+õã9ý³HѲRK¹¾fˆ³&}ûñrI®ë†è
+²¯@Å?‰q2Ô.2•ö?ÍitJïR¶S•TdC¼ü
+Δíéí47¯ÃÆ~€“ÜìäÁÔå%2G X|…–á㵟Kæã°K­Òa*ú³,ÛÓÕ!úRžÁ†YBgüÒyïçc~)Ù3ÇfíÇèº~¬3d@$;Š—JS™·‚Ѷæ[Ç Q|;ñ…»8&Žæ[VÕJÛØ}ž€‰veƒ%%õß”|UñlþñßËN¾G j#L‘S%e€úç­†!g\Õ(<»ºî„A:ƒËÛ僨ýóÝõœtëys^¥A/sgÂÁˆÛ½÷sEðä”-If\’Œu¶m$MÕpQ/ŠTæ'oœÁúR¢OAøÜú‹”æY‡ Haê¥Ò#±áY«9ÀY²â€þǹIˆ«Ó» ™OÍ£HÀ ¬K…tÄ:^Ôi—A†wòà£ßþ±‚¿IXAÛðh¨EIÒ‡ÛÁ_Kö¿\àm—gÐf
+J«ƒïÑz<§GF…0˜Œóß¿nmK7ý@ ¹ÿ¸ôVrÄ)’Iåˆkc&íÇGGŽýº‹ßé÷äýžÄjà²Ëî±O©Ž±"Bì ÿ,ºþ)BßmPÉ—"«1¹
+ ÀÌ删*’ø|—
+áÅT>ùkÃ/¬àR&…/úˆ+ÿ¡®kˆ¨‘ʸ£1|kÈPj^RŠ,>+fÐ]ÅÐy­†ãRÄ#–„êd 滧BÅQàºú¥èË*¼ÄUÀ.Û)ç¸5*UBäíê*‹+Di{:S.‚þâÄ`ú‡ôtEVèâOZÓ"³QÄ,bkC©-ôU ©IP :_‡W@•Löf—iØ\Èx­ ËøNkJšñgI§…ÁF+9-“ZŒ¦Šà/“Ìk当h™î¡õ~'G[Ïm:üO÷ÖšçìûSü¿Â +×[“þ¶‹ÿ>®Ã«ëݲ U»hÔçn ¾ F+ õÛÀ«„ ãʈƒ9J˜?`D(þ d˜
+át±+l‘ÀÀµ–høõ¦.Fì•„æQ:äŠW vïtK~ºYòU¡(¡¹ÉMÀN½Eœh%Ã,Ò]è(ß'ÿ¢ñGS!iÚöRk¨$y2)ÎÄDÓǤ4Ì¿ìƒ4†I«åâà~°.æ­eÎD¿ª±×nvZ£
+ò›/ÚL0-˜û™<Ä`ZQÝæg’‹ ¢L^¡b) R*ŠàH™¥ÒÖĺÂFh§Ûžðæƒ~Û"Þ.fÕåü$‘Ä¿‰´ÀÏ’Ì]Ñ&HýF–^ŠÞ¿)R&j '$±eéQ"ÐER
+­%fÝ—A,–šÓŠØûlFÈ‹ãH[iyxŽq°s()}4çŸãòE¼„Õü­Â—”µT’!ê%˳q‚dÀ…1¶n%z(^Êv"ºx k¸\ïRôq+ò×Ìz`<)Zoóä–|^³IH3q¥í Oè5óŽž<Á]-äƒÀÚŽøgaÛf
+â輶G"1áV
+2R”àPÁ~ÍãÖöÑ‹nñkë°!þ|\!‘Ôèíô øÎn Â!Æl-~в:gׂ¾dç³ uKh–<9üp]‡^Â"ž·éiÇ¢:æ‘Þš®í uØR„-þ.I,Ð ˆÁ­h&ÎÖO_ÔDW ABvŠKUsÆOí„Ì—Øò(QWàpžY€;'ügQQÑ 3ŒÑÇ.â˸Ê´ÂóRË>žÄ‹M•Hx€Я»„NKÐ"¶c{?@~e‰Øºò«’½ŸgÑ{ýøþŸNADI:çöЗ<ùùMzqê=D
+H‰Œ—An\=„O;øó J")­3ËÜb€Y9÷ßÎGI/ÿ¤v€ q»Z¢Èb±8Ú‡ÕvÉT«¥L­:?þU}^£Ø(M«t¯ÿùaµ^sºTífm Q.)R½Z£ÚÇ‚”ZæhÃ}Ô ÑÒG³jÎQ)D®"2¦‰©ªÜW=@}Ôj:´ÐǯÿýQ>þ½ e¶âÖ¤VrÞïT®ÊkÌ«º¹ââ½T-6¥íxÆoJßã õ9¦JßN%ª^zv‡n¥“7å˺kŒAäµ4^µ²Ýk«£‰ùÜÒS¸—s, ý*¥ym}ˆ¥¸ª_:¤iá âLÏÑ«[Ô¶¿£iFEÇ‚´¡f]
+©“ýª:k­2:dðu•^ÞFï|ÿûÙ4ç|Éö9Sfñ1IÅГc?ÕZ—¨³K‡RÕ‚3lß ÑÇÏè¥ôŸ)ˆò uêQkë2WÒ×'¡vßme¦D/Å©¨õ>)ÚU‹Ì^Ä!J=/ªê(56ȤM¨1«Ê\µÒ©<‡ØlB± _!ÑÒv9eÀ6ˆÑJM q´h£Ñ($>-r¹v'çÝTN4U4ˆR»nŽÖêE`~-z%F+yë>ïN®î½nEvnl˜ÁIˆ»Î‘RtÈDûž Óúâú yvÕ6@Nw;|ÑžÑÉ<á*.[ĹÃöh]œóÕÅðÉZ#ÀF‚ù•`Üh ƒüPƒš-Ìã²WÐçw"Z‹Er
+é´¤Å>ˆO¼ ™é2²V¿ZEY
+\$Œ¬á jß\N{.ÂGÓ'"®ÉOÏ™­EåFœ•® Òëè¢ÐU}ÈVLÚ™Pgë‹8휀žE#žFnè€ }¾â:‹N(
+·’˜(ùPw„ü# ¼¥¡®}ê¦H̺­ Ti.郣´…<j"1a1=|’È°¨œK
+
+zÝ'€ú¤ápÿ¬o%¼jFÊËÄJYË·@N‹¡s~*Ø(LEwþ(=‡#5îGî›)ºLÚY‰vŠ•â`bM»˜ü–âzHqKyÔû̃7Ù‹žü{dæÉáG~3š¿îk_7ÌŸ}­ Œ=¼
+ˆ‡Åòâ;×/ûZd äXþ?³}-
+ÂИð}Žt]{Vþiº‚`-¦T˜.OÍ[pß‚yÁŠfÁ3j1“¬[ù¶ˆq$Â{›fÛ"b«Áªßõ׶ž¢j¬~ù¶–€žÛZzÝÖˆ•ÆŒT]-ÙÖâáxÒÀ2QF¾­…^¥`5´l[‹2Plò çŠd[V*•¯ÛZ§Û™ò¢)hQ Bb!äß»ãÿokÁPÄ„Š`‚Dòm-˜ÞWR¾ÍßcY‹v!·ì<˜ßò÷²–¶Ýë²öuoþ3"e™
+JBïÚ~ €E±òRJ˜ÕqüMÇÌÀ…zÌÞx+Š¹Wš’NÒ|ͦ¨<9Á©ûŒÓÛ17…‰=£\˜ÞÁ4Ð]<G–@â*>j›ŠŒ IÏÁ³±p ©hçá¬Z$’öñ±¬E Åá”MCÞMßUþóG
+«8VU´pÙš¸+ä-z Ÿ©£ÔœE릭»bSéÎúP=]žOÖgŸç<ÊÑ°Ý”xøX^åQÎnLbØ÷#|—ó•ìq,l\Äv"›¯äbD_k4 »ëî1èL9W}ÁäföØ"ýŒj8ýÁ!0^ûž]OЫ²–»xžŠžÝS 1VàÍÍL*ýÌw†æ–Úûè'p‚FŠc‹jg  ^Ö¹›Ãö9xDç$Yí›ÍhW¬Mh§‘Avåa¯0>Z,zé9—5Š³8Ñž=\HJùx/Æ1à8XZsʳ'Òå5
+Œ¯ ¤c؉0“‘Ä]AfÞ°Àb—· ”&<‡‹'£g·!iÌÇàE-=d1\;¾òÑó¨F6zh²‚P”áaé96®0ñ[,
+÷è¡7zsV× ‰- ·ÏÊÕÎ)µUÒQX·‘€©bËwÖÞÎä¦T¡ª”luü“î!B8¸Ñ)’BÙDô z_õEoE"× Æø2’¸DÙçF¦š PH/rGÕÖ†ú
+úh^Ý¢—0NT:HäH 3?–ˆ¼JcfÇmt¥ èó;1íÐcàB†½Ÿm²¿7ˆRbí¨Ýªm4‰ ¤Èĺ0Úº…`P§å…¢×+\"…¡˜¸€ ÝD¹‚¤cBpì°ÍÒ¾Çà0ÇJh
+*¤#=…~Æ “ánØ4§Ž·>7d¶F·`§ÿØÑ—áÂÄB c#èé9ý‚!ì°
+ƒ’F½¡QÕë|ó*|ºÄFK·hz rBcÃMFR·TõØÜXc ‚ÞÂ×Ê™-ÌFÊ]Àj–ò1Ó:\Œ ì‹yšÔØX?sÌ_ë^ ˜tt§‡µÚkcƒÀ¬¨…,HxÁ"š‡Sˆ:Ä*µ¨¨ VÕŒó7($œv†Þ…˜±Ä`}÷~ñZó(gx‡ÝÅË{Z0¾s÷7ç(‡+Œôqæ
+Ý-a·…ÀÔÐUèqß· U§ÉΣ‚€•Õ€ýLow3`dµð¿eŸ#ä)`ür¦)R‰…6þlMpsaë©‹OPRrzOB(¼3óù• Ž uL6y]Ä
+{Ÿ±Þ4êø]Ы*~GºïÅH‡]ƒ˜˜)î5]Œr¯Cù,T?
+©Ñ¼$È¢!KQEtÿÕ„½£ÎK–
+x¹N‰ƒ5÷—1~)‡• dÏ“ ÅÖÃêyLüy™r¢G=øèÍÅG@›Ô³27©TÞH´ ^VpÞ¯°ˆÆÉ6Šp¶ü^ï`¥Áè¡DôÃo9}/Á$t|,ý½¢QI¬&Ý-Ón— =9Ö•o›Óäáp;Å› cbç’¹jä6‚É?M EÒÚYÏ\ç
+1ÒÊko Ü‘ä ìv¸CÙ&©o·äs똶Q4λ
+XMOAz) k²Èˆ9 mpû™0(˜¬ç¡–`×dž°Iï’$Ÿ%O'³­èT4ùT¹é•Î`6Ýß³ Tð-j0®¿Ñ#2‡1.ÍÏ¡«6eŒËíÄA@£qÍöm [PÂïÌttcfr ˜ò ø¼‘
+Bg’Oy©‚±ä‘t½+´ÒÖ%/˯³°ÔvP'²ãÌûlÜ ¶S|;Ìø±„ôpœÛú9…!F£Q>°ÎàXŸÁ±¯=<‡†‹¸0º².Öµð,0¤ì%ÚàÁºBØçSäE0 lÙYÿTj–~ÀÝy“@¦b‰:‡ÉÊòËÚðr¡Î¶¶&
+Åk TŽZ;DŽbÛ—^Ùk| ‹à%fù3åŒx¦?;|us6ÁZGƒ²‰{Q Û¿Ô÷— 8ôìf:ø "rhÃsQ¨
+*ëþÒ美¨†ìÐð¹½äss?Š¾~D 34ègÂÑ®C
+Â0àÆæ%Šˆ¨ð:R0Ò•qD .wB—,. °9/=ô—Ú‡*U¤Æ¢uaäÇh8©a&¥dÊ;Ž½¦d‘™4-X*ÁðCkÓêÕ­W =à%ìOš"U©}w#²°<6¼Í@h‚§‰eß¼jzÖqKX{ü*fÓ [öy:Þ£(çÊ Lé¸oBÏ¥ÕàÐ"4»Ù4fõϨèªÿþÐ¥W_@´ÔëÃÿ¢ˆ}Ãx´5ùoŒø×7Eô¯ilK1ïKâ#Ik^÷CdÜ;Tâ8BŽ#®Ç0¡oj ~Ï{80Þ8~¥Ñ¡£ø aÉB›â%ÚØNôk©SWì…Z©}ÿ
+FÎöÁXP%Ý©ßçY.8ÒfÀqA‘ü¼Ènöó|¦²dþyk÷=ÎŒ«!S8¶¹ý$(`lxL4Oµ°‘
+×a6âaaSVn²PØÙãAט 'k^2ø ÷
+Ô\ù3rrT¬
+3 nÚuBÐÚi
+ÀP…ePŸëMqtÈ–ÒZX@ê+ÈÔEžúçGTD§ kyˆç4É°ˆ^ã£r­½‚ë~fØàÙY±<Ú•ßið{÷dvD[|_¾ãµGÁG©ö»äCÎeMÖþ°s[À;›´z²Ÿ¬z±²º×9Là*†Ãpä OnKß+ZYö£hºÂÊxêS0_jW=‡Yax©ìcYd{e˜åW´t¼Óð¯|Žÿ5ßÌçú~}ýˆh@²6
+üÏX›)HŸüo™ýú2Ó¬V÷bæDÆ@„¡ÑLü6™éö²(=íÒ+!}mzlDØ
+± é $Uj*0‡¼oî§9ž*e¡³y  æåpYóQR’”&x¸Øó&‡`µð£
+…ƒ'(0*Ñ䉹’¸ª]·zæ§JÔ|È)‘så–×>T±¿@Nôµ+ÛâZxq{Ö‹"átž‰,§[*°wbƒ_
+jl 5Ûƒ-”@1§ÉóØSùçy 0<Æ!œåš<4Ù¸• xxêÀìáøš±1cEH=µÓûCØB•˜—°’niwÌ߇ø›Ì>Ê ¢õÉ›+F¦)G°Ê¤¶¦ ö©B$'Ó
+I_R…;äà7u‹ØPq ÉÙèY„̃¶ ø•Ø—4VÅ¥—J“ '×á…*°êB0Tï%` .…9û`¨†¡h͉A(¯&™ÐÌm©¦x”f)&
+? v™š7™ D ØUÔµST¤’21˜È&€<‡™¢,Ücf ±Ä³Á&딀JH&£ìåÒ†Y
+Íʲ¹É¨%ªÁ5&€{[àG Öž>Og3ð6àGž„/©„áÕ£Ø|/™9;âÞˆ¢
+ùaƒqÖYº4”#Ç¿—é%»(è@iy BXñrˆª§€[‰¶âG ÝìÐu©Û2lÍq}Íÿžøv®€
+6ÍÍöd\Š%u\&¡®°9S©€)€y°Pa‹£’//å[=‹ ÁV+âñi@Ö`HÁIêonE³Q×UÉcãlõìN’e BAOÛÖa`E×hÛk"øjÂðɪøS1µkˆ®A‰£ÓÇ»X0XoÖ­`e—µ+:MÀÇ­" ߺLíÅßÍÔ¯kòHTœ@1¥ÜýDZ“¡£!*¹ôæKÑï¨("Çv÷¢ÏŸœI¤‘?þ¢fg
+™CL^;y /Spg<Sóã?‹Š‰X€(
+J¼Á`¥(ÑÁþf™wH
+~ÖPLÒÀUmopVB`û¨g<©JR¾|+0AÈIòù§Á(~Û¼¹™ÿˆ•^M±N„¿)A 'íDY÷ïŸÀXˆOñXrŽ=èvj^šâ’YèõŸ°CÓõ;jïAšâiPÙ§I¼žº{™òàxÄ«M$^¼‚qÍ…‰3içàıÇ…ÒñûD2³¸¬µ¼×²ŠÐ¦|°— >)'"°þbp¨Œ4hYA…ç莖s¸ vw¸{_ö!—.'O‡»¬OÓ<Î cìºeôAßø…¬
+OÌ %«:ã¦)6¸ÙðæiœpSº¦ž)P}íç¼ü_ϬxTÈRW¼ûƒ¥*…)ȇþºkdÜKéÒ%øk;,ê ‚ÀŠˆfù#Øq$與=&¤±%Ü”l´KsLZáׂ'+P!IÌé%âÙdÕù €ò&HþíYâÉCCÕt•¹×é°Z mîÝK€TÚ24$€j
+Á<@´'€{Ìqè=±.ã—®°É2ñ4gÁ¯À˜v¢jQ,{ð@îD'".Ȝ׋Š “—F·‘Šø0@b!|Á^Ñ¥ZØŠ–°¼Ž|v©ÈØÉéJÚ‹^Á™'»‘t&—âÈzÒugK#7kø–Ó=%!?°j*v> ç[¢q¶ó7©!~å#ß H´”_S—‚
+¤ Å÷#L<b¯•B`!œPGÑ‹Ë´ìúE.”ˆ9øjÒ®ò
+T¦ü°­D
+
+ß-4(ÑVÈ ËÀsb@Âu³‡ÂUZ+>Í€WÙ…oÍqmõ¸â&›héZ »C _h[Í+îqTòå­ÎV"ú‰VàQ¿ø4¨I^X¾ÙßÜj-ˆ`°d¯|«gwø¤¹¤«A‡e/épÆÃKWƒ‡ºU8$nï¬òÍ—ƒDÐ{\'‚ð­-Ñ Üšûí@ýŠçNìLdaöaý‘ã¹£Ôá£+W+5š;E$x™/>r^py¶RÅ/l—j<wÆQä*¢±S8Äè­#2ákÞJ±{®ó
+”êrsÊn“ýÚùUdö-æ”e% ËÎ #yVÉ7ì¿Ì>ù5’ü(ZØÜ~¦aEñš§­tøe–—=% cO‰­·EÁ;ùYÒ‹£BmÔyã ðpI»y£AÔ“ Š¶Né£È17ÕKðј1rXOõMIZ¤µT»qvúZ#ö×q¦Â[õe Ÿ$˜ëòQqàÇ\š9¬„oڦћÇS`ÕØåÆËG²c‡¢+¨=”-#ó`
+’Ùî_Aã{%Sû×¹ ¾*B BµnEÄ#y²*®åW1Ãü!‹·ã"ë±’ö?¤+¯Óð*’5(J1„—PNb^Aa!Ù=¹[Ÿž>{lm<ˆ¦¬Sm´X$UòóX²³ÎzŠ¶XØϼŽ³E(aÑÐ`7í`º¨io`Ã"–óh/fœmÕ®vNƒ†È(ÿ®Ëke Ù¡5ö¡Oö~ Á8ÛœxY¹æX€¾"BCÛP)ŸëðxEŽQ¿ìOžåÅÑ^kôfÄOrÂ}|$Ršð–ÍK * ÍÜý€èj}g>™†ápäªÍè|2+R³\qëåâЛ$”,´vE AZ"Q23ãÐRo©¥%Rf¯®$’8†ukíñƒ ó:Š¸š_Å|  ¨ß¯ïþN…¢¤ÌEh0G{æjXtæÓD¤“h©ؘßtZBtÓX4;'ç¢fRÎäC!ÚN™vz>ŠŽ°0Õ™ºC¸"Ñp¤zV¿Šê%)¤b·K šÇ¬wrÃù*ìùëetIöˆ 6ȸ B˜ªN?Ä”¬±S sR\´ë•çméÂñÍŒÞ`.C^iJ\5Ž£nt¹Ø*(±ÖPIÕ脱!ƒU¡ˆCøf¸Œ+HdñÐJÞÛvY—w Rˆä»ÏÃ)$-¥LGwêÌ3ëe{‰¨öv›ªßs}Om¼ãéhúÊž¶&–Ä_á¼GàŸâ2„êó2yºße.^:\‡aà÷ö8ãÊ[(Cý<~
+³[¥GÌ+/áeK3O—É`lðë
+€¾3†¹…ç0ƒh )p+Ëøkª– v ~;w}•ü´CZ–‘ß<4Bù8æ˜ÁV‚¹ÇH³ãMÌÔ—ª\<zh’[`’íEõ5f’IÁÚaê¼v(Ç)Û84Übiíòuei¦
+¦“Ž®à\ŠÛ„•Z£•G½`ŽÈÃXÜÙãî«“zºql-sxZxÚ˜ˆ~ þšœ*¤AË`Àø(&’¸íªG8Æ”âárº„P,7‹ÁmëÒö0Ù—’§d,”qƒì&îÚÄouyœQþ=±ÄAˆý šD ˜°íºIsØ’¤;4ùƒ‚¦vÜSÜ!Ñ¥óýo%ªÄú©M`8èùºw@’32
+¤G8ÓQñ¤ˆ¹žL;T¤i3DDR ËlÞÙ»÷mê/+x
+
+€ÐHB¹¯^tþ–~@´)©ÙJ.W>Úh¢Ø>Ú 6c-öªtÉÏ7ȹiV¬“$åç`µE±“vö+…âäR%tù3h„ L¥¶ÇÙ»ELªJ»Jösä+Cn?ÆôZeY˜Û’C™
+x¿î-Г(„Üzá“«‰éÛÖ‚H`î àU´`m‚?+o+åmPP©ûuï€"‰@O„ ÐÅÙUW$0ü
+ß‚ÛPÍ4_ŒÎçVRʉ†cSÎò¤`
+çbr²XˆÔ^Œ6%¢$ ¥wxçÖ&ñf(0·FDhêïXåf-/F·¯,-‘,†Ãwe òmÍ¡0ˆy+
+‡ÏWûñ{EíþêÖ¬,qaús¤/ù±ÙéøjRÛ®±¾dTŠøÓ»Ur¨/Y…(úîºöõ%‚ÜôåŠfü0¦8Ÿ¯zðï~Ý[ @_~+¦/e>”
+3Ûbš¯ ‚¶Ð`µXC‹¯Iišª>C~Ú` ÛTVÙÅíå¶^’BΕFEÝ¥–í¡á}æ¯ÜÙHæšRÇÑü¦¬#B=7ÎÔÄjÇ°Ø1ª¶¡ë‚Ê‘š¨ÎóL»¶Ê0z<ªQ>
+tâT°½
+øw¿î-Г&ÄÜzIjòçß@ êÔšývŸæ2l„{B ÊÔ»iL"* ¿‹Ijm Ñ#Y¸˜âÔE)ç(,š¤A˜ùÄ •ƒ¦¨¹Škb‰&¥ù{*”bΙ?¿ªÃ äd*k»r@
+¤–A¦ÀAW™‚ ÁMñª’ëì€uZ ‚n|ì7§Ùn Ä[`­vø#’úä Ç}7¾åaÂë…<?w`ÁP \ÄcS_Õ0ãi0ØjÁgh)B>Ô,s•+
+¢ÛAÕÃí1w¨r½š°£DÞ]b¸º´V²=MÔ‹”Jß÷4M×c)Xe¤í%?}+z¿•71Áâó“-s~ºÉ•˜,ìaø+y¢­¡tJ,D&—¦»[kr`»DÏðØ<ü”Äí$¹þ-†@®‰½W_~Ž 5-çé6¶Ê)ØÈ“`³7[²=©JklkÀCɆ2C{Äv½:Æ°KºWû‘ïLÎ, fî̃$ñ#ÐÃÍr"/>áCºVH;™“ olæ¦95ÈÒi1HA5Ñï¤!%vÈŒÜAJfª"Ør.ü­èãEœ[À2”ìH ÔÀl2MØ¿‚Ôq¡¾Â2˜8çòÕ=|$ ÕXð%FY"4 –UãFu¯§`&ú' †ei˜ø àPx9Çmâ^ãKô—~'9›r@–¹s4sçñ@æ–û˜Æ2²ø¢ó¾KJ“Ùf^˜÷ Þ [÷±_J¡ÌÓV·y=‡WN%Î)³—á&$Ê‘Ž‚o!sÐnÎ7]ó/ $Ì gÕGûð_¸žY{÷s )l¿ÂBë1¨º<òLß–[ÅÀP+ÈNÏE
+˜¡Š ‹Ä?v„ •Ù-Rèß¡w0ÝRã)»Zøªâ†¹ð ‰©ôëh‡¾Â˜+žv_ä¹ä)”ü(¾Çœ"Èçú
+?—ÿôé(ßÞÈB%‰žé--Å:ÀË佊‹O%coà”uø²eØÙÜ|Ð/›S­“î"¸e`[_+ºZ!º–hIþÓCù 8¶ý)Ù˜ŽVøÁ[@:4ÊD9EL0Þˆ,ÿèãRôþ¢c½’¬3ªîtAÈdûaÿ
+S$xx¡"¹ÞÆá/¡Ò•mb'Ñ ½C3¨\Šbû RéÆ¡Óo„{úo ^´³Rq•–¯Ç ì~U;W$Kj>¤[|=Ì´Àé®
+µd,g§4Ø…Iáنŧ8’EÊãl85¦ØV†Ÿ3†|Á ¬ø˜…HÈq‰ÐÄ»ô«äÓ`´"7km,8@±BCårå/B©†Œ
+Ê*š[KŠjk7yÈ2Xô8—uå¿YT„+¶0wC;l‘Ed¹F¨r²…«àQ–†®å 4Él“z䬈ŒÞ2O zÉÚpäL
+—-<.ÖýR¢a˜›AøX®/Î)S>^™f¬Cp<"E˼BWÍè
+/8núkîò®¿ÝŠžÌ
+Þ[Ô ì`’õÕ蟋°•UÖf㼈ϱÙp°žöºˆÎã3'ÿœëys–!_ãøÀ/Ðba ‘A†MžU*^¦7
+š_‘Æ¥—øæ/R¬l—ÐtÈJ¡±Î«ì±†K¼e¾€Ø‹…/Q4á,?G_.ÒÆ°Îød.)+*•½£È¤zY{ %±£~æUÔÔë9ì¨"‹r‚xc_ùw±Ä†N×ø± Ó†‰–hY±c#ÐÄÊ:°O_î«`Ol™S lu{bÀŸðªsŠ/l‘ G¤X1ú.¡{ˆªÛ&èS€ ¿=¥÷Á4÷Þ鈪K7wkHLÊ1-¤†A $gÓ\’r+u‡Í¡ü†i—ð ð‡r¦§Jö@eà±è™‡®Û9š¹V —Ï‚=aEœNö
+ÑgA:¦|â[̧Áÿ(7zょ’=rNÁ„ð¦ÚoEÎK0?–VÚóé. ‚Ö¨ÃvôóÛ“þuXX°Ï}°ôÅœió¨t„×˸o榴8¡uÿÜȸzPj`©g|éZÿczP_È¡ažåtC
+§hÍá,2”ï¹½¿(*2jCD‡2|ì"+2'²O9rð­d¥îp0] ÍuF¾-œ«¬›è~”0®Êá—„ò! 6îàðPXM_IGcÄ÷®Kš1#J£_GõãÈù,Šë3W©—åøô‘-ÐÆÐÅkfÔÄÈ®,,vLàDàs—è©X†^‹…Ùeâlع˜]=?ùU|”EBcƒ&Í`(¬¸7}%<€I§âߘ]ѼlE ‚T$ðgËJe¹íMÏÏE¬PVÎDËûfç燲Àþ©ÙŠr€–ú$Ê¡±wýýn:•štªþÇx™äÖqAôºƒN@Ô”5¬å¥o¡-yÿ­_Ô@øwç`‰VUçÃIf¸D™Â÷±—â:Î2#‚BjŒöþ5TBÒušz±F^ßoÖHqr~#ãÜcpœÙ°ìJ¦ÇòVi%Ž õ‘ýXNÆi¿ހƌ ËëÁZ¨ ©cª_->M©•u’Ã=ì7Ú[[ "^doê1ØÄšQƒ÷þüºCnVØ«îCPdÈy?Rƾ¡Ly£†qñä$¹™·R(³òøAZ#Ñ'0öÐÙuÓéÁB,zß#qY:²!j)çgunôÁ ªàX‡}Œzað£¼È¢òwY|Gr '(bªUô¡¹çäÙLH'Ôökº©bœÁ›è^JçÁ _3SÍZÃWäš‚üXˆg¿Á`xú6Ïa¹Øö!K´ETÚg
+lX»5´Í°îprÙ%.Ò>ªÊäž¹P‰ù)¿å¼xÑ:²Â´a®ÆZ†ŠÌ³©¢õUdˆ.QŽÖÂRÆùýÈ¡¢[~fÈLTÖ–"øBúš ¦"%n„ÿº“š¼g+
+µ”Ñå†Á“SdYõÒ­hx'Ì"oØ݈6AŽDša_…üÊrôùKÂËØ\zÜ·³XZ_™“Iµˆ¡oP _ÒfüD£ÈH+O1\FׇÂM=½chIs°xŒÍ,^‡0€q;>›ŽËEG&d¾˜™S€ÜZÍ|b0ïe2 F„beRÜ6õF`ªòÐîÄ`ø ~¸{q•ªa6­DTNa\šYWQ<ö¾±\u¸ç`>³,B7V”
+èE ïc7ÂTÂÃÉ°²—r]W
+#Éâ4ªäIš&y,Æ¥ÃR•$)·Z{ÃN¨Uä\@œsq9=¯:¤}ãCPdJ¹3zL&Kt½îú|²¥ÿÎeƶ3àt 00F¼[CèÀó\æ;(Â&¢Îo=Z*%¶ù1ˆæb øCÉñÈÿº×=©”O£åˆÎ¬‡!ƒé9Ù´U‚àŠ”6)b¨›ÈeZi ¬ÇÆ”²h}Ÿb·‰¹l¹> •áP$ýH3=CÖ0¹²¦jHªOÑù (tb"†œ³Y7 XÇL²ñÍ9¬Æ°²çbÔã
+ÙþöúÀOP$>$̱Ö'p0º  gb?×s(KmE=áVÖ—Âw
+‘j†„¤OGœ^•GÕ«´Š¹ÄïyÑöïì&Z™%z©-¢¿S ©®hËð£T"cÅbž´U½•3߯‘ÕdÜqÒ¾JÚÑÄ”½ïÒÄ•O} ‚Ë¿m[»1ÃäÓOßAàþÎ×Ú,ñâŒ:ÒÍ÷.
+vAÎúݯ{ºP‚¿ZФ—Ú>J”„¨16ç¯*rÚ„ €)Œ@a˱j†äìç8†‚s¢¤gYkŸ–«¶ç$”W'
+;œ•tWÕ·Èéy¥y˜ ÉöÆR8 Ë
+~>ÙÓµÎP?‚ÁÈ{½­­‡$!Ï6×ÙEkY(X>Kx¥¤ò£löHÍD&sÝ„MÒ°CB즈ñé6iº*Péò)cÄ X}¢—Ĉ8œP“fõ'Ä0XD)&³ÊºÛX¦þ¡‹°
+‚›7Š[ ó¶-?Ó;$øiÍ  ÍaÍí]0æJTÏçÍsHSKcªëÖme /ÄN¦ê tÓY§‘ IHîäÉ,Qû&ÞŠ?—JKR€àáoÜÑvЯYçÏ/“>¬F®ÒÆ¡Žé®ˆÆ 3 r4.Ã[á×û¼«DJ‰ùaS]1½ø]“úÝŽ¹wÇã|8yËí&rË
+ã[öUשH
+.ä„€î*ÜG+ëÀÐï}€²‡à9—-üq†5íaO;n…úH3j[Çq´`1Ê{ܯ „…„™‚f9®W©QIªL½·Ñ*®®Kv@@\˜pÜEÊØKÆ£×ýpò¡ mgu6Ì‹âj"i3rçxðXì5ȪøΙÉ—5lx‹ÅuÎ@ªâ¤¸6V<àùeÜù¨ÄŒB70åpç]ÔÊ`AœtÉœ4ˆŒÒ
+ÔAzòXS×1„ÈÅ~ãÓ…1Å6 ï¶Ö´5¬)?…!m¯ ÿ¾æ>IéuYÍõù8JhojIór®ÓŒ dí…¦‡“-×ÝsšRܨ®[ö!PŒœeÉ瀑wù¯‚˦Jz…râUø5`,»¨HØ,Wh(RMÃò:ç:í$È +¢AÅæw{4þÔŽ°¯úaµ¦Qµ ÑEÊÉÌýÜ)Í„ƒ Épæì’;%ŸLY芗;(N”
+eúÛƒ»aãÍÝ }……§[e®ðî1´‡Ö X’ôa^ì Ú/ÖÙÈgɉrªÀA÷Ø逮±SH ¶š=/v™nØð”»;1„ÜpóB§:QƒŒ/ѳùú†ïàï• †¢ò£é ¸–frÃ+9¤–Ù @$wÃXBM¸:™Ó
+%~Qjä…N èçdp˜;5íŠ+˜UZýØ逜ž'†¥Bßþtw?¯éÚæø*HÐc¶²Iã3)†æ6m{*©Œ›! D
+“§#‘Vé’F‡‹TÒbý}R
+bxËŒ ,ǵÀTÆ–u ªºî€î‘€ÏÊxR(ŠíŽñ1ˆë"`×3Š«®; yt¨sh[ºÍ3ka.Ž¿øfbReáþñß/C¥L–=Ðœ2-TàÛÚ…«`û‚Läo_``øYím¹fìwä\}혛sÝˈ¾hçÞd/ä¬+AÊ­h7%ÎèóÉÞ|´RdK™–ÓÈoš â‹Qã‚sDBÐ1i4ÖJY¡•‹ú5[Aé8ñýŽhð3¼.‡7Wän¨2Þ)Æv"šæ¾ßIãÊìE6ìew®Íz@ð] \äU ãœ1Ç–ˆ®ªL(f@|Ä4÷œúeˆ¹“òÊ^´.‘ã=ÓBúDjÎÛ½™sE^ºë€Èìa¡m•‚Íõð@•¡¤8ø”×{´äö0Õ¡†C“ÉDG8šA
+Cšhèþr2¿”ÐÔ² räÎŒ¥šç°ˆ0T˶ëÜ€;d²(Ï,2T¦ÈÛw2x¬~Ò€BƒêÍ9×–ß^sç«î#x-Î}Žoö /úûdi¾#aA *A|ÀÃ6ç¼/”ô¸=B—c½‹±„:ðDÂW/
+&ÑlžÇ.ø~X^ê(Gèxu]õãe’YnÑè< 3ëÚÖQÈûoõ<
+SÉb`É á×ðæ%·Ó)F ‹V³"ÛŸÿE ÿx¢`ÔWMýÆ( žNjN,æ>2ÂþòÅ$aã‹æþM•»È«ZaÐÂëÕ¿¥JµU"þu³ÞãòÈ–êòÜžóˆ<á1<RÝ–¯7\`þËغ÷¸1fÊ$œryÙðHKÖîv1áEÖÎ?îFLÐ*ã¶ÜG±00,Ð:Æ]ÀE¦™šöí¼Ì iä¯÷6üºîC[f¦T
+E÷3AcÃðâù¤Ç3ÎT¬Ž—1ßzüØK „ÍçƘ.‹g4ð £ ¢8†ûðåT<³
+A™Â„%-©˜±‚Ì u,«d‡&™„ÕWÞ
+
+©ÓŒ2·´+á1šñwmƒD ™¹7)° (-3ÓQö±Y!¥!Ö7BÞñ0ˆ;@쮲WFúàëùà˜ú®R±=¹rÔÀL>]A¶³šÏe0Ï“•Öè¹û+ÏCËʦ±#™ š€nß[¶´A¸³žÑb6ɨ`þ±¬ÛéÞ<ƒ»Icè`=„Kb2Q-]ã­i ðf+<Ýè$Câj›zè8=èšùâŠAˆ#¦EŸhŒ?ýŒ u&‰\Mà´—AÁV<~î%У;þ}›¿Üq…š´ÓWà^>Dœ]é²^IýnqªŒnå솋)×pa=+“Î,O‡$›KÚÝNHEÃxznNU{kÓžazꜘæ b—fÕdke!r .5s$zÌEV6$;Ñ"Ññ8u^ždÖ9…öÖبD†TZ
+rù:ÈE Å=ÑE9TÎ:Ñ „„¿“µ’ä ºÜÀ–h&Š_P^ùF;‘?綅¾¡ü˜jêñ<€ê;uÝ}qñ¶?b4ˆ¸Æ¡Ð¸GAó³Õ"O Â^že,÷ØÓóüÓÓú’F±iŒÈ‹†ÿéi ÆßycÔ9<GÆN3¬óÉM
+Q O[|AñuR‚Aè¤bçIükÃ%çq@M‚Ü#ëfÅá&½µú9”:ûÞÌ|–‚¤Ã–#WSr*ˆ†SÓèn{CvL CˆŽz/Aa&*ñ‹ vÎZ‚uÆâ4³Ö ]yyÓá
+‚Uò1€ÒNÊNr)Å»¥È "
+¼Á¡åxöáDßÞ­:íF RþF$š½9$¥Yä5EŽ¾?#A¡c³’C—lªÚEP›*o‰
+ïÎ#2™¢É¸²|6•Di KÀjpŒö»Èxw*xLÛ×äS ùî¦Ûª_u®
+HC?g
+´½ç¨ŸÈ‚É®Hm’~þ ^ÞD<y©óoÑpµw¹Ì«è¨<dÖ„ýLìƺŸúïIþûå5ª’’Ô Ñü€~?cŒ„©â%´‰oBd&Ãä£lž®’9Øx,RYûmkˆš%‡¥äÂèÎÐbœ‹£Æô¹A å:f³À8¥Ý¬Lg’µ®|—`‰›ÂR »ï—Côè2vá9L!{†u"îcç¹(,Û´,{ä!H–‚'5;ïÖŽó¸AÝ#ï+H …¥î•¿Ç¾ú.§šù J“_yr”ÝEa;,íbùsÃ^=ØŸ³†‹Ž2”>¶?”qHq‘¨iú–ÇvþG]]âvùŒØG‰DEuL\¿iˆ‰d†äV×^þƒ4Jýš:X%J‹éŸÒ¬¶‚NO¨v]&[2†I è-x cÜ”F²²î#ÿÃ'›îZÛùÎo–v‹Š(ªrOË×b~˱ŸÂìb¥—9Ž;Ä[`:ϼý qiøHiÇþ±ï ‰ ŒÈ
+ ÏãšxíËQ•tQ ¨ŒñÄ­–[¼½‰gÂs#ðgh¶û@ 9¾Oǘ» ðíV¤4¹Åä‚ÉA
+ö"Å%PÀ•õ»WA|Ž¾ jÜE|1v}4,HÕ2x…H"i™ñæ_ñP™žâ. g1ŒüòŒ˜v
+10ð
+F`x´^eï(ÌÐ
+"U¡Hf›9ˆA¬t •ä¬2Öá)?}ý AE³ÍÓ‘ø|Ä A—B„®îñÚÍ(T+ÇP8S¦èçÒØÌÄRx:õ›iÃkl~Ò$gц¬§#»‚ÜÅÞ6ÌÏÁÕáãÜ­³¦•B,ÒØl ѳjn@ö$ $ˆ±/OΡ =t¿å|oþb~Ç |·^åˆ7¼ Þãh¨ç.ÍwÃ'P]L9\˜6ù$Ñ0ÓâX‰fü™q)ë `Šöpóè›»­LZ”¥¸ZÐǤ[²Ô—ÿöI–– üÌûj¥„nRè~ÞL%#3M ˜gÚûjÌ:×¾Vlâì!InÔöz¹h/£MnHÇÒó3Jfçå\:¤%ŠB»Ät“
+)kÒ5çH
+¼±Ó}h§ëbþ$~U°kVÎQî"D™Âœµô Xq Å°bÅŒÔú&‚ó8÷\+žAQÅ(¡Ç\¢ôL,»e»€Fþµád™,â3..l9rŒ² Њr{
+k` å¢
+u¤45/è3È€>S¢ *» QÀk
+RÈš? £æl"v™[±õЧ@ñ¾¹Æ‰‰hyArÉâ¼I΀ª…$sÚÄ{xÍ´Õ”g4µ’öžÐ÷~ëŸ?°Ùüù÷ÄE¸d™ZPÓ<¤Š/H=ž,ZÃf¨~ ±KQ¥…= ~Ó¤°ÐÉíx­5Ø uË^x‚†¯ ^j§ª˜¨FŽmM2G 5©×™Š‰EI”½#И–Þª”ì+‡P÷ÄQÒhÎ?ƒ ŸßôÐk5A áÂ*hqÕ[`Å*OR@¦/s.q¹-‰5;!u§›†:P¨
+‡O˜± ±ÒFF!¥äت…,ÅFLÞ»Q‚àÂ:S[¸ Jõ5ù´Z±ƒŽ5 ÍõýÇ1Ë+Z„ÅîžE·1ÌyÛ!P=‰É<©Ÿ{ã]€ƒ-Öó(¬+|[×ñ®ßͽŸÃ¬
+0ò—ªF§à%SÞM8”/fÚn†1¤…žjã6roc-zã—¦Ó™;Ÿe•š¾Ží·
+H‰Œ—A’[9DOÐwÐFA ®Ý˾…#fU¾ÿv^’üž(é+ÚÕ½p© ’
+î½LnÎsŒ×18©DïsEx«½‡L?Ï­‘áÑçtrÒMþœuÌŒ,£WŸôVªyT[7ù“k¢ÎæÙJÛ!T`F©eT›+$¬Íž1ªû9¥‚8HŸ«,fµ3Œ<WPk6[¡`cÇ)N牭Vçª GBøVñRŒrO«dßwˆŠ^†[Pçu•=‰¦Èfjì ’ÝÃ
+݈öPˆŠnNæ+DŸÔ^Ò’ÄâCHï>Õõý\õ¤÷PÀ9ÍÛ~r8G’Ä)`¥êQGváfÐI8"ÒƈÒc¦µ ‡YO;ÇôN²vŽ¡%­”MĨå¥tî³›×b Ï»ÊÚc…Ð~k@dÒö›ƒ¿®H«g½ ZdhePåê´ô¼f6…üF:ˆ­Yf~r S®~íÉo£X‡Bí<x¶è6i‚¯êõÕ¹´J•Gî{'cÒàò)ÂÊQë*J fêã&èëCoª,Þüä5’çxoÄÌáÃVHáœ@ö<‡f7¯s¡§ÄdÈ›s (ÂÅ#Å ¨Â@^Å w1Š>ç‡n~ ÙWAæ"=Û‡sàIfÂtD伦–Ê›ÄÞ`¸ó—žv\AHE‚’zˆ$¢R,lXYãQë9‡r¨ ¡e~¡µY¨q«1¯«Þ‚²xÍ°Y7Ç}7¸ c+ Âà-­ØÏX! ›ÁßBöQ
+’ž!ÅWVüJ-,Hþ(¥k 48º
+‚É@"s‰¤uæèæ¨MÎF$K3∤ÄÙI¡ k‡’ðÇ—ó(WWÑ+ø+æ ^š
+Q[r:mƒ ðª¯*ôHò;tÐh£C´ ö§­H/é»W3]ÿ1÷”îH•3þ7¯Ò=™ö%7¯^B,Èé•ÿÛs4áD:žƒ>× Ø <%<\!‹ª£;ñÙzsþL"q 4Fb¢yûɃœh)f£í†7
+­©@Vž¸nò™Jmú<°€Íè$ëÿ¯²ˆÔ‡è“:ç3)¨oò‘¢{0‚Z¡n§å¯ý| Ù:9j"Í;¾9gRðå¸þœÎÁöˆÚÛæºø«ÂÏÉê2Þ”­ôƒR*
+Fh³œã=!^Ý&_Â%B
+
+ÀçE¿V¢ ÁíìVˆ f3fmˆÉ|ñTóF‚í }†+È»UyÌϳ˔³ Õ[‘WÄšHhXñ @ô@‡¶B  kXÈ
+JÞà«t/[Ò–â‹ Qƒ)µ»sÕ&ˉ¢œy»sî^i¦³Üžíìæœ×Ž¿½æ97Y½#ðµ8ï0~+ð]È·FýüΈ^åЋì©¤öv×ø_·A¬°2xŸÄ¯wYhu¨ŒÞÞ.#„عØg0hÁ;†"å­`<˜½ŠÄ¸¶ÈE=ZMh!ç´²‹-Êày6oœÀ}ë_ÅBY7ÄJ;j½ Zô2Ö¹~,åeoRã0>Qˆ,GIµ.GÁ&ƒ²â|¼®Â±k€Î؇S9P-Š˜ÐœMÚøá›
+—–{ûó.–7öTTþ.×!ØÞ£½€Œ’´ÀÅõÆïpr¬¼'XÁD^º<C+«à¼è%C ëÍÎ&3}ù(£%Níe:Ë©›vÊ/w¤B_é¸Z#@«þ…žËŠfÒ>tqf±˜7“ç||= ]q7N}•YÒÀÞen ÷¡caû
+AoÙr!qX¿1%?×Uì ,’æ¼óQ„àvgrAk)fcûì+d™×Ì)L5X:æ”\àËSí¤C(x©7A_;(ä·'e¼l—B°,løˆèÍA;O敇 9]_¯™Ëo,Wâ“/±uÂ;yw Ï.#÷—ÃöH1Â}CŽ,8Éw^þ®›ïx'¨ê‡¥¿#•¦~ÒHÀljñoM©g®^KIø½_Hý‘~²è-¸7PlšÙòìä¤f2ÈŠ’Â"Ïq:Áùnnù¯Û¢§à•Xl†šyô"q_·AG1“Ì òí»'£·,9,({¿Mýe½Ý©¿Tðe»]m€÷‰>¹]>äûQ;A ;žöÛ~?Œ ¡Bž±ýÞ]¿Ÿ¸ öA›Ø™WLJfcKÀo£)­ÿc¼\räHr zÝ!Oðÿg]³Ô-´-Ý;ÏœŒTJ »!U[…»“F3#d7[W™R4ÔçhJ:G±šÑ äa…r!f™¶·ýæu]3míŠýˆÿ}ìˆ*h+É“+­“*ƒ~}ÿŠ„ œ‡¢8è6!à_‚E›”Ù!˜ü“5—¥ªrºÐ· ã^ä9ñrU«^/GÈd{ù‡‰äyŠ~í’?ó`ØÏ×\ ™¤uhLZa®uºvÎÒˆØmÈ“L‚_Iéç†^0²]i9óÝÉÛ‹Y, ” éY{ŠX«ä¨[æáJ°Ò’Uø ÑTR‚½3½>Õ\ª€ŠrÐW4 Zšª ¦X7 ëb^Íj'öTð®Ý'Í㺰۞=ÔqD†[ºÁv=¨*d^&'²ˆàFWúà‡ô.ý–é(ÏEd1øÒZhxÿ4ίã¯
+ꤞ“\Mr^-·ZœÆOþ† ü¬ø?qq(òãÕØ*¼hIÛ†ÆòcÌÀtø‡~0û¿Ãã>á²b£à§e'×u*Z¤ž!°IÜ=qÞˤ+î&Žålâr0ð¯th4;dæ°Ó32-²’îK]W¾À…ñÐݵá£õ0¾:d25ƒ=©é®ÐŽëŒ#=€ü95„ƒC;
+iª½ùû.JŸI™D¨øˆ¡TÀ>°R^Ùˆ@Œx"}¢5++S›5]l1§È\ÛÐè#Ö—§³8Hv“è.i£T–Q­5L°ªŸö%ö‘´Â°lEV–žZOY‘Þ|‡°ʉ‘ IŸKfÙ¡ ™bâ,ÅoCh!ÖrŲ,ïHÓˆµJYCŠ–4Uל* Þ¸Ã;¥OpÔ¤ç Èžá…ò„î2ÊÇ `
+f1nƒ>ˆ†]&)/H!`ÄÁAsSD4…qgV𬭠;)Xt•©Q´Ê²Ìµ?I *Z•œ¨4@pö:´‡/E!?eGýqµ'¢ÁÒ['ÊO·Û2µˆ@I3x• ¿"µöØåGý¬Ž@,§üFe²‹Ug¡¯|
+CïäÌùjH±wêH*ÿ=Ä8¡¨K=Ý•ß|fI#ð]æƾèQ7ß‹s›…{‰ÿ=S_×è‘ÔšJí‰ïE¾ÿyðLe¥êdýAjÜ{}r§£“€³‰ÉLÑÙ£†’óf6Ýsõõ”ÜP…ÒX
+ÜÃXʶZÒ{דÄUµ“²¤ÎîïËhd™ë¤) ^¦Q wE¶2£›´’âÿsÂ{š¬Éß9gEäÜ)ñ®|9O&®cFâÙlOàÆ= ZŽ
+eXr*Ñ×Í)k–š¶ä<@Xyp#¤±¸Úñ/âvnu.ƒ HŽzã©Í’1<]J¨E÷ûúÈéÊä£böÙ[g‰Á3þTÎaU:ÈnÉvµ²ÊÓ„Xf·Û•abAÑLÆ8…¡9¤}«qRŒá$Šª‹¢þŽÍÜ£a¦›Ðr Ä…Ú±(À
+^TÔÒ$ª²»¡’Úe’v@X¯(‘Éy“m|TùWæìeªºž…ç#=Õw;iì‘­ó¶±Çˆ»—–Aƒ°}°ŸðÖ¬•*˜™ü”•LD9ýœLCDÕ™†€lf†´¦éËûÉÝ("Ùìé1ö(×"-*LS+*!ðs÷."çWµÊZ•3ëS¢X½\øÃàÔ-‚wS¬žíòÀB¸ó¥-ðÖÐĨƒlÙ+é’YJÎJ—¯F0SM%:=×¾Æí¦RüÕˆ©X/𣠱Պæ!‹jA$*_±ÃcŠ’Æé9•>p ˜L$×jr Hê¨ÃòЧ•Ñ‚0N?ï Š11ÔÁºzúyµ§6ô–öõnÜ´ÌB\ÖæÚ÷’<®eßpr[üz­Uúk§N+±'1%ÊÄôŠ…¢é48¸£õ%PÛ@ ±š{Pµø;“¬Ç*GB¦ãn}jÉ
+â‘ʇ¥AN”ßÖÎ{È4´ÐÐ4kyó†i/¸G“»A
+¾Ë±ïz o]™©Wä#$"É 3ZÅBù7r·)±WP¶N“AŒ9Èxš(É]„P.#¦Ëý¿~ˆ€è$m®ù“>݇ï~Ú'˜9ˆÇêdÒ‚GTÉ/Ž\œ÷×@C²EÛjr3\0e l™àt”rÙ.R™<”`>ºâªðø!Èb…#‹q™æ“Ã3[éý/™ŠÄ$»¾@Ôx|·'b²røÄž±!²84¿ÌB±9ZÃ3„¿ÖÄ;ª?ªÊ†‘bÔ,Ž ûöÅ×£Qí½Þ¬hˆã¨Òh#?ŸÞAM’Ò³ E/!xà=ðó¿èB¦@„KãßØß5&ÎQ]Ú¸´”‹±}²Æ<´8œ^ãK%R¤ËQ(iÚ ÑãɃFÑ®Ìöùs˜±)é‰,:{øV³ÒEV|µú«™ d·A¸6/VÜî›·%]c«Æj"9µ'Àû«*Ì–q Öå|+Òâà4ɺjŒŽÉÚ²ˆc )ð6ðMÔ»uó¡ÆtµjŒŽ  &¶V}z™ÁïOUóœÿš¥‚CÑŒËù¸Rû*µgjÖ™i†s9fÔŒD§FpÊÙs==\~ýr”œØÌî%O`yôZÒTjTwU^SO’¬™F’‹³¢ÿªœœ„¶ÁÄÿg1󬤣ðuÈÔ¾ƒ&ãAžÞ*FÌ㢶Gy2UªÛ-¿`~Çþ´eV\zžZƒ!%bLTr6N9.%Ý* thZjñµf­'ÑŒYqïå«h]îþ&¤e£³Ò@ÎŽé‰öTè¢=, Ú’ŒûùÕ<i“—ï7úG¿¿ FrjpÚùYew6j(–'ƒ¢hháêAùM£z €YSŽ„#QXâéîÜîËœ5îiÜà*üÉãÒ•ìÑŽftÙFMꀇhC!Ùê³+ºUÔ1Äâ@ýb¾½|*ÊHÐ 9ÑíÀ—;#¤Æ3¨UÙ©
+ÏLHe,0<œÒpû«Üz©ƒ, …ÈßýJgæmNRD²ñ#-0'l®WW_\.jüÍ,afÂÃôÃòÆty$ï V)»Ð71
+µ`‘ÄofO×íMz’±Ë%Ð Û¶Ò5.Q°<z
+¦Œ@¶0b¸‚Ï/Ã0J„+~^̀ǺÌÚ­7þ> f·a,ä‰í Hf*Ò÷ÿ2^.¹•ÜH]AíAc’Áï°ÑSM{†g¥¡÷ßçò#”2#¡Ø°¥Šb’ñ¹qîJ2
+Ï™ D°7ô£”JuÁJÁCÚ°LøœA‘Z2ùLœe'èà JC.‰Ä¢)dr}ÿÜ-è÷+wšÞ €š— àù³‡«ãËú: ®~ r¯~ýœ{õïô…A=¢ÁpÌiò)ˆ »•ÍÌ~ñ¶kAÆÀåÀÀw "„p”°ƒ¦ùƒ*l=>5šÞã "ˆH×XÚVˆ+)êã"ߢÏA D"Œq>¹AWRìÁ(„ñx’ˆãNâ¾¼Æw"$rš¦Å™}¢ü¿rŠ ôê‘!­RL,&ǘ[«ŸJþBDR=Ì DÛp¦÷s–Œ Á "1»*0KV›”¬·=*"8Ù)[x
+Õ’BN?_AHˆååhxêxÌï
+eOÙv©y!Ïáä`$ŒjÁ_¡Ao`0ܾä‹Û@ \g§ó€8^!¨WcCð–ݦóSt;â·¹¯Ê°Ñ¾Ìì›71¬'( ËËŽR';“gPe{I,ò8Çó>× 5ž)À òàâHù’õIY”ÄP–¼U©ñž¨qìÅ©øÉ1×u`;ÎášgŒÜÒÛÒȬ¥†TƒÝj~ Y#‘¤)U*î9ýÜäCl¨aíxŸ.Þ7Æ4ͬÓÐ× ¤mŒ…Ï ³ÄCÙŸ²,”¤ìèÙö>Ô••9Ìñ¤á=X—Z0ô£Œ$åŠÜ×’”*È#Îñ¼Á1RMF†Å6«éU6œÞ@¬M…CëY±ß(Fçj|š‹Ý§%NŽB"¤C1³ ìhžNvÞ‹síWn™±jÙ8„»U϶(x^ûµ2œÆ^©yÏDG?²rQJ¡ 1´è#žfw‚x #HìÛFPr¾-ÃR¿NâÓ‰´|Ë;ðLs&!Ì ÂidÄPÇCŒh†ô‘òª$5%U‘)H^”o#z†?œd<æ’dÊ‚ØÏ  ½Ú¸^zx'1ü¥²;K~JSf
+BÓ2U gÑ2‰íÈgV™´¹Ìs´b 3(4e·T?–\ãvwÕ$ɇOsÿI\«;ؘèO%Î|¼£ *4;PH³± Ç—Š–©dËØS\$Êöåcw¨¹8£òਤŠ’gþMãØDÓiŽBžçÀô
+Aš‰`rÛ!¬Aš VG·vgÐ+QfU<cN”¨•=«ÎÊù¢ÕÐÇÎM®Bó
+‚ÅÕƒ410Ù$á;Ãh bHCÛѵš™%˜¹;7^OV¡_ßØ‹pvZ9Ž20j¦†g`_Ó‰]†YàÂ`?ƒáÒÒç bF
+·nÍ× #±ƒA!iiÛÓÛ9¤-`˜"JqÁ¸-<hß‚J@ËF@þ‚2”‡¥Ë÷ ;oqiTNÎhˆ<_ºû4'£Ægx­A33Cø¼Œª ¶Äó'ˆ1šSҌ࿠7 &'ߧó(3Ž¥a·°Žj ªñ,eÀÔ`ƒêÙíM*BÏ  I°ØE-LÛè„ü=û¢« Œ.¶°ºç°ëY:Š¾@Ã$ÅtÓX¸163rdÇ|rY”—MG{í/™NL Z:©ù¾ýÀ)#fÉù^áLZû¥Ö"1eéN4Þ% –ì6Ûå¿¿¼ §âr’àhŸ-<
+GË×ÌB±Ž…ò¬J÷éÝ:HA44\1¸£ßeNŒ˜õ L2¡eÇ\?öRʦeÞÛ¬L²á±ùb° ØŒú6CߨVKe²‘ga¥³ Vˆ¨yô/ñЉ-á2¹’˜WAZÓSٶܕw­*Æá‚ib ³œl-fB†ü™ä%ŒRß¡ñ^Èút&G‰žøpŽþ‡Aàk‘p.‚J´“ÆP«ƒ½
+¦‚ÂS4 I%e ˜÷¸aÉÄQâ‘ÿ®D4%¿#˜Õ­dÃâ÷™ßϹ!ç~›£WÝ|ïN¸4¿÷øÿ÷J¹ÉG
+ /‚yÚ-4Ðy¨ÒÒ€¼ËÃämØäaA‰>µœDG 0*òbªñ£<sÍ'·Ñîaϸ>ž¼
+ Ä~u.50vaw">ô8*ùmVß_@²0Ÿó0ÌÔ11µF£ÿ•w@ØdßE±o*ÁˆâX ñ.ëê#†Ä°Y½›ß¾c=¥s³mª›!ÿrÝsóX1¥@m <jHÿ9%uï5¼ SpLí§BŒ<‰ÄƒÉ?”l±Qb%Õ¤øÒ%Ñ ú”ʇË‚Yo8ö‹¿©(Ò*EüuÔ›¢&7Ïp „«ÃMXeÉÃ&5þ“ëͼe0*úuða¼6R¿$t¶ü[`ä ÌëpO…Ñ9ÞýòA$šSX9¼:é Ñ
+uõÓ¢t‘rÉ•2€šêkSÞî=¾Ãg· ùÔZPòý ü $ÿüçËÿ~™£ Ñt\@A*¯85zÅ®ÃÀ.££‰M™&u3­XG~
+<š~PÔéÈÅÚE“lfT¥:Ç.bK•µÙ²Ÿ•áoü"Ÿ¶KÆD7†Ì4ã/¯ré
+Rý5ŠAÙÒD­ð2Ȇæ§ÄUé%g¥)ã­³õ)}:„ÇE¡qA(P«êBòk%VNÊeèÍpi‰$Sa¢ñ„T¶GQáÎTóçª4‘•ŠG¬á)È[àñÚWË\)C!J„mÙOÚÏî —B÷Ï} ¤:Ñd* ³dÉešo°JSbÅ÷{+kiE÷‘•ú
+"ìƒ
+Be«Wy¨
+Pƒõ¬Iü„Ž›± Æ=ýß’zq£ùî!…ï´¼X­žÈ~ßÊ~7š™QGdµ-LpŠì©Ä6Á“ÉÇZîÊߊŒ.æ䫪p"àvÈ^q «™Ø<nÃÖJvÏ9û}Üæäó¨“÷Úœ$>êëA>õ韯LÌGˆ)d„ˆKØ^nçýrA,= "^)|„÷²²Â šSÐ
+‘“…°d3ÏîÜÖßmS$cG”Ç”Ý×ãØÌWàÖû‡m"Y4+˜ FŒŽ
+Ŷ‰w«2´™Ü;?EVÖËÓˆtcûf£^Õþ‡khIeYy•˜HÓ‰)/© ö™±V«à˜(ÇŠTf^:A·Öÿ|
+Ɔ
+b!qÃÌqùZe¾”éLþ&*†8}JYK)JÎ(ºç°XÅ€®(þÔý*F:Š2úã„pžùSß#ˆPújyÒ|9 £~ÖÝKóHAJtd¢·)³ð%~ÕÒºO“``…¿.OŒÌ˜ì¼®§3Ý „ì*›Ž`oãòÿãœ{#XEäÿ$·òJðGt‡EѦp¯"vuþ›G.Á$†j‘ ©Å ¤ûKo˜üÛ¾"„’V†ÞVRpÒ¡’ÌgEæ1=>ðÄ3Ù®–%„L« ª8!fñç"Ç‚t|ˆ’TÍƾzÒßÈ Lð–ŽA—’2ÅêGÇ)0,¯º›l‘býÊ+Üœ‚…d½4}º¤õv¬§ñÉB C\êËþÈõ¨PX¬G³øITm/ÁSÎÑœ°álÖG’4ªmòˆý©02"ËøÙ@í¥Œ„¶$´„Acºèwä½i‚îCö%Б&Ö$bÜá™^šPvTŽjn pÚq¦‰ÙÖÖ’B?ŠíÆ È1ªÒºÐw*)%w…êÔûšk^é9æý(dMëGñDfÀ0D‚þ²É¯ŠY Îß<Æ“Pá1ŸÖ[« ÁYX#‹.›ËþÒ›éÒ þù7³ø׿üùß2¢šé1Éœ5±C-~ Ù„cì²³fr!ÕB]Ãp@ðø…za‰ÄÊÉRbL\)´–Œ7-!±ëø|œY•†ò'ˆ •")!·yNVþ×æ’¦âÄùPÀó†4âÏÐÖ4Mr¾Í) Jec“áÐä€~<€:\ž>Ã1W 0ËN›L
+Ðò‚àY¨†zV6äã¨ñ,‘b6$3I'=N !šÉÅP,8þó$ÑæAü‡.Î «´ˆ{˜J«®’Ph<±ƒAïÆ/ÉZ3N.wGdª|}ÏÅшÉ€ MÌÁž”Ðr_MÐ~#â0±Ñíû ²)Ö¤hˆhŒî1åUB%%tö™¼iZG‚nT'‚…E–Ì< ÛO’!LÏi)©uøÍB®_e
+#.1–/ËêmAÈAøƒ±ÿ´iŠHKÂ.«¤z?Ü #oÒ ”¤¾˜GÒ—v§¸ˆ[øwÅå¸% ±ÜOñji%}¬L±)\Ùå ¤î'îSWù‹´‹mcædz‰*öˆŒûâ.+¿[Z )|§ ´½;Å µn’‡sè¦Ü\›è~ Uoš‹V•NC«¢4»XÁ…«j\Ä0Û¬ ‘¢á)Ûê5àì©3¶øG
+`\øœµ}#$RÔÉóüMÛ%Îl‹’
+/R1˜œʹñÒöFžÐºÀ<Œ%Jes Á³Èh{ŒxL/¡¹ê÷VE¿_«¡±JTg˜É|G6‚XÉrŠ)š§ñ@°æ<X„ ž#›-èAš¨üÒwd@è&Q ™až<GÂ1AZ IðYˆ‚B¢U#Ç‘µµ šW?Cv0‡ƒAŠ3ŸÑócª<@iµ•å5w?Ò!et¼¥ú~ ÖÐF­(ž)Ø\æ‚ÍMOï{þáǶÒ5ù§
+ä„úðh`ÕÄÔê÷–\+Œ‡¤d„ÁîØè %góx~¯0ÛÀä`K‘>±íÑ/ Vyóåc¯ÝÒ <”õ·A˜ÛšÐËâ<í ŽS’ؼF ¨lÉv^œ\L„^›ý®ˆž/ðvØ6Ã.À^iw"h…¤žC*U'˜þÒäW”v'8˜éžøæi/¾3f’–dó¼ŠqÀÿYÿh¸x7cÌñ]?>'ý0¿}PI°—bÒ@iƒV<B6ƒj0¾\Ü­
+yÕb`yÊx´ì~Ü› wã°à!êkƒÊÀz0þmkv¦ÜÚ/ê‚û/ à=R;Üè;cÃ7‹CpM#E
+ßt„ߨd»±ås±É£°¿³Õ Â&V—ªK+T BdÂgŒÝWqñI~¸ø†a%©ùPT:þó ¢“ÈKôZ«‹€Xøö²§¥Õ,u×´Òy¤¼:]+ä2–gS÷EdW…jùS_f<믓¬öSé.å7aÚ/IO(&¤ÙË) Î
+‚Á&·áïZMõζ‰
+¶w¤ Æl°ÍLÒ¶w
+@wí18¸+”&HaÆUŒ@÷öë¸Ö;š@¡Í^ƒ0š9ói,s¨Qz?ëm ‹ºf4JEd{3Éj‹š±ø#š%±‹5
+¸akés˜>4µP|ô¹Ô ò¯Dóu—¨Ÿ‡P³š•^­|*ÒUäW†ûné/0Ü™î+Ã%˜ÿô€F©ì$Ê®[á3:E£þ$+Ž…Žëo®y6ì¿ß\{Ððµˆk»¬6 h´u²®Ë¿Ó©Ñ¯[gd´,<=ädÊ3±a7c ›&…]ˆ?Ó `è›T’N0Å"“@׎ªõ aE©‰rstsÍ0fioÆ„“pŠ:m»èÄE & UÐô¬¼¨_Mé¸MfI ÓŽêˆ1U):QÖ!r€œ0;9úyxC«$$¸ôË´>ÍÃ0PÍ¿Ùi¢­zˆÞsxcÊúÜ5ÆNM¥H©Ú²ÛITE ˆè‹ï<wüv›û䯺Oàsqîc|«pùÖ©?o¬Œ–+åJ½“Dd:F>÷xRDTäcËöÍ‘Ä©‰É.†Æ'i ÒÜr'\ly;¯Ï£ÎY:3½®
+U™ÜÊ^ž3±EÜu†ºŠŽCŽŒvÔõ€çL{ÍŒÊÉÔø;L{æ…˜=fþ/é¢åÚ½µ7 '«åc/y'ù(Á‡É- ö BÃÖ·y†Çù’ âM³gJ̺–«Ø)¯e‘ææü<{¿¶éȦ 1ai÷?ƒ¾wý+Ä( B(¿Ÿ…F@N8Vº»!h{ÍupØûå(2¢˜q’ LJ[À ž—ãúæ"ìô£lãSm¤æ\½d_âÖ×cp.‚)%! ¼+$Ê'È^blÆ@Ëá”ÛgäÑ'6ONs]wA0y;½Ø‹ÅÀÒÙ¦Wœƒ˜Üžè0·¼«0ÎÌùPMOmèA Þ'A0‡
+\N…sÉÅ47ÓäiŸ äR”ŒúXåœôÃbº™«Ë­4CE-²4t·˜€`%ef®½º[šà¸7AÏÎA³Ɇª‡ÈTãÝC–0ÃYÒ61ð™6$‹C Ú©>å–í Bˆ!_£ö>
+_–E‹âr¤µ8Ä‚“i"¨) q@l–ÂﯚQˆÂttÖzŽÃ„÷'“
+[¢UW>{q:ŒÃQ8¸v%«o!NW®³ÐÀhQø‚UBÃK­‡8•PÑ+\ø¤3ú3+9qââBÐÍ{órq<öÊ ßA÷½Ž{ tq?/ô_c\ñ [ Å"9Wrü׺<ÄV1ö ýM­ŸScD»¡øuüqfæ?B–CôÛ‰›…bÏû{\“~–Ýß
+gX½œ®1e^y@a{–›{l¹Xñ1µÖ«6
+ø˜`’-ÜsÜléD&ºUÓ+ºÀ²ñodŠùX5üŽ\&üÆ·&îã\˜ÙF0å)„¾àq¦k¡o±V
+oÊ·¿þ‹07S£;+@ÖDl:g½‚+$ÄÿL=ý
+A¤Hkbwt5í øAJ4Û aîXý%-5‡pé¹AûúaBÙ9 £`»†x:Æ—éU™.U7*3Ìèîé”,LéÑ‘WÍ<Ó!È$6€ŸÔAþø\`õ1/.h7ú&³6<$Ü/‘0xu ‡@NÌ?1¯˜gq$‚”¾'LÛí°^­‘ôQÄ*ònÊmŠúR!$¶û(b[.ìn&36ðÊ© h4нëejœ®Ãö6èi¿ÞYÓß¾ÍÃyÚcæI!ÌJÏ‘2k CS¦ô«Jâ€d:ÔUþwÒ jÀf³Fu:„}c‘™_ÛµÖÒ-MïR.ò†
++A¾ujuå5`±q®í#¾MjH†
+ %Ð
+4¸’VÇKðº:|…W`ýù,`W…÷Ì:É;ZªAÑ÷7E&úôƒÝ6§ª"°0^D¸uŠ’”Ù
+þ*<‹
+wÝG£w ?D9%ܲDe^EÝižÂ‘âuçˆSÌ…”ìuÖ<I^.1Z˜ÒGƒlÔPT{¿É0Ê"ù¢Æw
+Á9kO(˜ÍÍuxc[x6H$0MfU¼ñ‡¯¡7Á«;&PìÛtÖ]o¤Ð°eçp
+kö߯‚i ñ©YƒŒ®pcf.}ñ£^äù—u19fÉ&ÂÝ‚+j‡U±”òõ!­jÅ€®u9K >…¼ÑBÿðWßB!\ÅÆÞ•’œ
+Š¾‡E‰Æ"
+Uq¯hù/+j˜ü*2o7:üQ¤üAɲsû2 Zd‡–o[ £gŒT”|6•#Ù” ssÁë+wºG™6Æ„=S‰–i»ËµC¤¼Äç
+ý?%¬1LÇòîå¤Pq}4ˆcàµÏ¢ì IœwTº¤û€;†š”ÀjPôÓŠ H-N×$çz1}¹NU‘P–ŽY<&È WÕ0\]Õ²féuql²ŒÉ­¦Ì*ÂráqÅeŸ"œãÙqZµszb0[<ï§ðrØ_r ï K†åE­Ÿû§žE­lzÁË0€çœ>µ´¾ïÀPÚ…ådUxõð
+“²Éõ¾ŠFh|¼?U—9þcðjs·sø2º"©¬oø$ÿó©<¸JL?–LxlÇĘCÄ€*VJ€im‘¨œƒ +eH$ä­¤@Ü“— ÷œ‰W¨vóô .çO»_§á>Š~L«ìq ÝsìiJ°£VyƒJMcŒ
+"”ŸÖHð3»Ø§&ÛΪg^}t1ô-û¦åFÀxÖÅÆÿIw\Qz„°Gö/½uø™”aLù:» 0dMJjnüjCö(’e2Eí Þg¸0\Ÿ;ÑÅÄ[!®ò%ž,ŽåQ»žM׿øa»ôÌMufœ§
+ahÜ~ÕßôA
+ÌÓùÒbC°†Ìâ8ëŸÍY±D7Ü%Ã-ò¼‘Ý3Ü ·˜ ÒÚ!®‡ÝûãeŽ[G Ñ«è6¸/±SÝBé÷ýS¿êæöL ’ABäôR‹Q-_,Yχ?ìâàº@áóÈôòÍØ©± ¢ÖBõ˜Zȯ»!÷` 3s7´ù—ÏMø~š7íª W$y&ì[=&zãë‘Ff‚#·ØlO¶OŽ¨hUésü:nÅká9/y¤sLrc\ªßʼnŒíÖ;eÓ
+.‹Ù*¢ÿ)ð P×yüMÖ§Ëþ>!ºªÚša
+9?µðœús¡R„QE_±MÍÊÄ(Ùà«
+/œ­©@ÈwX7Åvb) ¯Å“êx6¸J(¯«&ÉeSƽ}qlÚ1x0uu ¥Ø /”§O)£HwJ ^ieºÅ³¶Âë¬f—œ¢©„GRA! Ad}¥fXèr}øbši™Œ$WªÚÛ‚yßϧ$ZC…–Ø9÷ŒÄAííPÊ„Ô“Ú|nz"øOSö çæsÓ„ùª*'¹±³sDáêqöKÐ\‘ˆ TešÊD± þBÔÖµLÌÆoiûãÙ2‘(–Ì')£„¨´ÂS+' £5sBgDÅ$ÉûñG€
+H‰Œ—K’e· DW =Ôü‚àÀXjŽð¨´ÿ©O’¼e÷+VHÒ@­×yù™Éfý5¢gš÷Hß›i/9jë…ÿôµwП?€ÊmÎQ¢[ýø¼‚Ú˧EïQJ¶þ!ÈŒÌV‡ÇÈ£–飔(mAXw”™9kŸÒk³¬½D ÿø÷Úªó—…_»wÛ šYê˜eÖ™kZ32lŽq ­VK0mòÊ9ÇË2.
+þ¡íªòXëôp™.°zåßßòøw(_åÛ‹Þ ‡ƒ¸ ã_›!v¥ß Î'ðBøÜ,تÓ~ͬ¢Ac#HP^½mg'ØÝ Mxº»ß»#°h ¬>;97Jʘ¹'§x*c`Ñ~d–†R쎌š¯ûcÞ£’ÊiÆäðG‡ëÓ¤z]‘­ðŠ!Ñ‹oø  "G@ò§æö¹8z:h™<H[Ã.ïÅœæÙ‰æ:”¢$ÓÖ2µð·-Û°vDÅKò#l>}G0ö]™£µ³Ñ;hPõPìä²­®uBŒ°ÂrƯ
+Pcù_,bdvfXqž¸E|ÍÄT@êLiiÛY½ó;älÌØžt¾I38òw"¼¨4ýÛ2©Æ3ÃN©. ý®Lßcüôã/ ÷/5í©ÜAW¢Ýb¼4Y΄.©®÷/uv®Êyé·¯Ëgi¤dm^c¼œ†œÚ¡g@…{×=%+½÷Ò~X‡D‚¤ÍñKŽ—ÞbU¸¦Vñ{Ž¿È×{Ž¤HèjyÖù5Ç_ —ùlt\¤”ŸrüwзoËGDnÜz\s<.Ê€º3å§ÏëQHåòõ-Ç£ci(c´•â=ÇëæƦŒ%ü!Ç«<‘Ž9 v— ¯~kªÄœö„ç_¸xCŠDd™÷/¡•G=+Ê5ÇË©@²ðžv òÑ×óâ<òžäµdéåñ=oIÈຌÖ“O£~IòÈ÷$}Kò º˜fÔ¯I^(ÊóÄ8DÜ’<,¢Á«
+ÊÚi¬hÍ&:}@X°ž©D nHÉdñøOG" „‹ RE
+IõÜ&Ü*îtÂœ­¸#É®|ùþÁóù,«8|…ÔàŽÜ~C°vAà«,HðÂ)\#>ô“C®À®­pK—ó¬TÄs JÇ×CeAg
+Èh¥LÝÚ—~¹ZJ|-ikØ©f¡³Þ”Z(%`>ø•µ»‚¹ÞKä•ÑP¤’×u|Ñ ²OñÿœÚ”õkìqè®ó¢äQÎVz>ÀŒ/–‚Z™—“ò11Aæ—ŒµNª^ëH{¬ÞÝñéËE Š–’ Yc×ÅÛ1/ Ï+a€xý`yÆœ_ñòG?G­¹-öÆ%ÖK©ð
+%Èãú4 „-ÿ«2i­Ë'æ“Ð|ò1Ó}O94Åì–Rnýüd
+<T~ÌœýÁ¢‘Yˆ½ôqlŽ=Köß@ŸDG\w­û‘A½`(³ˆ‚)é˜6«~bcׇ‚ó¤ÆHÛ9ŽëÑÄ öh_%FæÊÄÞÎÍå$·äe¹OJ,]ñäM‚öb‘íÞÍ7ÈÖG…RþeÚüa¢—™óÆèýÄ_~‹uçØl2yij‹ É#ÎÁ ן'ܱŽiF~˜†ïIóÿ“ö?¬¿¯\+›ä…Kº·ÿ2^.Ù™Ý6ÞJ¯@‡oãdØ»è©zÿÓ|Ňâÿ¿%DZO[%^(TfÙÚRA¤âi}æZTJ£}„<  ‡l´4~ôAÅeA)pLˆ’kïuA8€¯IŠvX‡¢Ž²T;ÂnJŽõ›®-Á(HÚŽÐ@”ßñßlû6&iN›ÝÊSˆ>EÒ`Ð2ŽGgÍ=‡-’ð+eB2̽ [¤)Y"È«¡Î£À$¨js‹“ÔaîŒý’½Ý{—V­OÝ ØGÁÐ4³’wF¹Š’!óè!]%ÅÛ_zÖ&ð__Æ’·­3äL+"Û>‡A b ÿ²Õ)†5‚†è†,Rpéz¶òŸsLÑ*5ÅÂÜËÜôsåÑøQoþv¨Îìñs¾„Â#ÝÎêùwq‡àDLÄöÛ¯ýÔÁ6&þll#y‚îÏ=@Ÿ?¹Óo]½a¬?榭S]´¡°=Í«7ÄŽt¥¾¶ö*±£DFDé· ºßˆx­rrxòoåÁ{1áÚHBþç~Âbè‘Ô ZsØ/øixSófBÁ@r]"ý`¡˜§Ø ~JºZ§dòë$ˆCêl_>æ”``‘ȧlµ;ÏŸtN Ž¿þ 4Ô”ârAÏú†2Dz1|Ÿ=ö0l€Ô…è­L#H&_¡Gq« €B0sFAIŽË¼ØƬ۟ù%ÐÊóŒãØ.Í<CU’8o✌×÷AèÁð£gä‚Ð0žÏÃïù^ß´( ,h…ëõ@Š"¥ÒRûÊEA9R|í>2äóè_Z*Jê!\…Nóa“•*×`Ãbµ$ÅÏžy .Bè«$•ù±LfÈ*-áb#jù5™þDÌû+Y„ PúJ$>bõÜ.¨½¢J^7Å&y¨i!jsÍá-“³ÆMšÒò›þ¾9{´xµás7üºÌMçQÿž•¹9|•×ƒ¼´éÏæE“ædáíÜm IrŽûûDÖ̓ÑaÄts²Ë+̤N+ç ßà‘†o,ˆ)|bNñ´+bÄ"~ÍN J#,m‰T×2ÁB¹Uhì°â°,£+VPzÉ‘ º]@6ÁôBø?ÒºÎudÇæѽŽQl›Ræª=[ƒÕ ㈵cÝ›±Rf“·‡™î;o Ý!Í£®Ð oŸ?‡tAŒ(ÕlôlX››ɱ.ûdÐ*CÛÊEòÒ
+ÃðѳZÅAÄ›Q6à1Q(rbrPùniç›ÑüòÇÚI]ˆiäîÁõGóê@¦AÓB:T=ƒ¬ã#ˆ“d]tkø (2QFÈÎnùb@ø$¡}t-‰Ž¯PF¦ˆ…I¾?Vc’èîð<Tâ aƒj„Ø={„-ŽJeëí4ãaè¶Gôªß@`@W‡?Ù1(¤îÛ!}ñ­P)™Ò‘²i«gI@…IÐÛõGÕ8“÷Ðäû# bjÞóì÷u™›7Σnþ=ks“ø*°yä·óeB%%î’š‹þ¾½$_ †ÔW°¯ã/ Ö¼%´å¸É«Cêâ†8ÉpZÏ!ÕšSç=gsú~;¤(†ìqE"hvRdfÒè}BЇçáC¦Á®jygì= ‘åF㋃ ‹À`µºþ‚÷fº yÚ¼A·?ޘ˹ðà.²ì>Ž¼ú#ïNò5t`úmªÞ(Eê‰Ø˜gêÍä M\{ttò¶GqB* ÏÚ ¿ÎaøB)„òÄS’g"(Úø«çyÕÃE"oÒqɳÇ9.+G眊gÎD]ùýhþß ùeÆbxÔ¢A¼¶¿o@þÁ~Ùtã°ŸÐX7Ÿ§ÏFaó˜&ö"ò£¥£™›tù3?C1d/Õ¶bÒw
+;±²eNÃ*.u6 D<Ìð~´ É _•°Nè~èS’P (WÑ=†ûUô‚ÜÄv³¥‰ôBeQùª ¥lŽy¿ÖöNâ
+iZä jÔOæÌ‹›ŸÄ
+Äèž™q–¹}k6(>U³ø²ÅSrI*»ê
+òQÞjXŒ°Û\οºÑ»·ªzá#ê Æpîã;ÿ:W_®SúÔº¨ ËÑçFç¿Å:ƒú1.Ä€+,Ÿs€#ÿ×¢ÚJ“ ±&"¡±ŸxIç EÊär]O…ªÜrض&:òŒjEù.t­Hʘï$ˤRU›• ‘ ƒ€Ö½Ÿ dÐCb–Ü›Ñ(ìw5¦ÈÐÊp@¿ß€æ®I×؆È*ÆJ$QwžgÂ)5ÿŒÚ[ÝR…zÞE¢R¶æ“R_ YÛ'>H
+Û†«OñÝduY% žªÀÅr°kˆOö(­ÃHf'(«7ÁÞ4뛦ÿç4P
+—è<lv¤jñƒ2ŒDÎîÊ¢BŽ¢ñÌùÉ‘•J˜¤”NOÄ0äIN;GFy{e:µèùø$BTéÒÒúŽLnÄ™_‚à‘:o$¡©¨ce!pNN²mÝÓQI„l=„²1ÉqcÊfJ¹)zöÝi yÿ@(æ…™À¬‰ÿŠZ¯;Oƒ?R¯‚¸º;•Cè§Î ¤LAoÕÓ:¯ï·Â0ZFˆ!Œ«{ÎàøÇF4ê>–Èq¹Q›
+˜ïÎAS ÔuMÛ±ðed Öê4!|G« 2¿%=¯Z¢ÙŠsÇO@Q÷ʼn ùíÇ ¶0dó)E ûtAlsÔ*èº5y™Ç*³ È!¢b2É2k¨Kk3£µGØ¡ j¦­’]ÈÌ…Ôñ„"ŠÊàòwÇ4Îã¶bÌ;‹Î#· áòüZE7Bv ëK‘Ùc³Ö­¸ç$NSˆºøO<Ù!0ôÁú„ ¨]e°éÄ‹ÒPlíšm#IŒÈ$»ki0‹îú«úuûãe’G®Ñè<AæaÍ-o¡-uÿm?
+Ha¯»Ü{®ÂÖD¬;H¶ûmWjXìè¬G†àeIƒ/a-%®Lu鎌 Bh”sŸ´¹øJˆgl¡êZ†{Žh—ýC¨dÞ×@Ðr€zKL/FmÀ÷õˆâ¥8@¨6„…þ.È­Ää©UûªÈ¨O9ê}ÕåkPß(Q‚ Ëp¥TA2ão-Þ7]ksÙ ¯Â}UX ØévêYºvÜ9ç:7Î×\çÏyÕ}ŽoÅñ¶áRâ·JÖ$*f˜°³»I›™¶ÑÆ}V.èbûç`rÆ‹[ÆÁö£ü Ã;·öf€ßŒè0A»‡ãô­½˜åŠA&Mƒ
+´Ö8¢_êò¬…ŒÇ!'‘CZÏÒ!85þ0SÏ/#VöË1$žO8¼óY/§ 1Ì+¿÷ÎqÚ
+þ¤GJ”°O³®gÉÑ9ʳ™Fk¸{†‰o\_•’Ù¬¥Æí‚X)Ø›ã°ìn RÂ+Œ´Ch§xŠ› Y=Ož,‡ƒ#uÏaÚs“ÄÁîoi‘¢2Û
+;£QΦaÎÑT€k„˜Ñ²—¬HÝ´Ô®Òœ"ÅОvsnAqX~žšó‡7설{B*ÔŒÑÔá„–´¯ú÷b™ÝK,w!ÏB$‹âÛ=@­qo =-ý¿P
+‹xþ æÓß‹Bÿraz0B£¼Ø „,ŠÇxLõÏááJ-`Íó1ZþŒ µ¤CH],×Ï8³Õò °p6–,Eö ÓçoÖÕ´T𾃣 ‡Bø\5ª›
+Y ÂœNêNKǪ2 +kÉ°kï dÖ›H‹´¡._ör˜ŠÇW—•ßq1·J‚ˆ¥¶:w” À}°/a½Àúàg†ÆsºÒUP%¥8ô8¼I#¯“Iž¨ºçÔ¦6†XÓ6\!ecMTrîÕÑãÏ_È©Gg”Š!dÈ_UúrØŽëØ~ôKù¨: ïw¶çëغ âY»«ÏÁù³1ÈiÔµhÁ¤Ñ–ªI¾ƒaÅÙÊ]ùt.„¥h¦ qä׈þÇ \#  ³Yåd÷°-¥Ð´{‚˜‹U«cœ aÓH,NÌ PhT]Ú#ó2ÿHPˆÖ¹;¨>ØD ÷±VïøÀ’ÀM©Ez}[}`Ü©Ïlrù „ùÄ¡M h ꈮ6¼Y/!ó˜Ud² ƒÄ
+u‡®ÿú‚3½… °|oÄ“ñòÙ *mQ$(e÷ó
+Ahs âSœÅÞÅ2IŠ<:r!:‰Œ0!ï«î B\²ÕA™Rö;ÅÖÔ_sIÞe0ˆ>õëq@oL¢®‹]ž‡GÒäÈFz¹3I 鶘¢œhzÁFùó¸;º*ãÄas²g,þ¬ù€.¢¾ž2x¶KŠ-#f„ºÝRŽÀåê£uDÖ˜e\tß7áã`ÜŠp…¶‡Ç
+$ĪSö¿KÑlyå³8½Ð©2xµUǼ«–e3aa¶`û¬Š/ß$-d¬ö$¢9ù‹Ù{ANÐ× P`"ù‘“ã
+Œw>„A‚uB@¶£ŠØåRÚ–GbS3(´AäÏHzP\ß6²@ç31ë›ÄðCŒ5ï|•Ì/QòÆD¯·OD"Uvj!œ¬8¡E„=©o~\p,Ê(nE©G/ŽaúÓ‰0´}»*î²_Ƹ|=OÆÖãz«B¥UËY˜ ebBÐdÀ)2ü‚àbÐøÓý1¦ S@ðq!t*ƨDGQÍJhŒÑX$A.²9 oÄeب‘§vbÏz5&`‰âî8µ„Ý‘£YÖŽg^íý抬ÉçÖ*“¸ŸŽ¡¥:¬
+½@R²¹`Q妽,}Ùù©šUéõr8›/„NŽÉ|Š(«›±óPù•Í'ø#þH>9Æ57,=&ç9ÜPÅÜÀû3=D?T‰j;6/*¿?œ!¬)0ñ´ý¨Ö¡2’þÅ Óîxbå0%×£¿”°
+óœ‘2ñ§»ÊêikvGgƒK š æ¿ÚÕÙ i,CË3?¼K‹Þ¡AºQ÷M?LáV¢)åbø±Å`+” ¤!=ùã‚œ:cê™q±y²wARÛXº¸>c=¾ÜëÞ—¸/Àâek¬ªú4Íf)…@þ†l‚ãÙvN‚`„ðTLΞ-ìS†\„öJáí·½ŒBÄ!éäMhÓŽ®sd5P<hú¤îÁ®4ëAØRøŒIȺ
+;ÈH Z1öç(jY¾¹Ä HÁ
+SzPüðZqa×ÏÕ/H (@-z§E}Ž…ÙŸ(ÝÎÊdò5ÔxÚ]„"„Znã@ž<õ›Y)C-®/’
+!õÆË¡£kU±€€ÖÚçèŸ+#I»†£È‰d{óD8w–³ÍúG $¸.²¬kاÈìÓën¯¤‰Çø²a'^Ø0’t2ÊÎa½°›$Ÿ³sh˜É*"µ”=
+ñE¼
+_KŠ]2ÑЕA”iÚ4³6êº6ˆ–·³ÄÈãå»fèå†hãÇ (áƒ<¡ã™ƒ0ðàíУ›LEb¢—,q¡‚ØÆóöåd”†ß†sø/JV›BB\Øèò™)Š>_Iõ‡);›
+E‹%óPôú‹’oRã;þ\$]€!+щnƒÏ‚;ˆ¢)Ò’61+,¥W-T¦‹,ÂïÊ|u¬Ú “öLÇUvܪ§›9`“º¥9× Ñ!ßÇîÕ½}œ…C§”ŽñPf‡;*p)¢
+ÃíXÀävZ! @–B:J®õ€~‚–®^Í […ff0öÅTj«ä+8‰›^¿“˜ ži€¤SÐZÅÓîí‹ÇeZV­áLa¼À}‰–£ÀÖ¶ÎËñ.ÙVST+‰&‡lQ-ÖÎK´lKKv%´Ÿ‚:ÁÝvJ¡Á
+yuÆQINI~…½‰Q ú“´ ì* {”ð¦ ,›Î óµD`*
+g u@UK¾*J
+2ê°L!~.õA"¸”ü1\ ¸YÂw¤ïé;õ å†ßûÜõˆ¹.?Îa
+8CVó+©°x1YöáXù5 ½ÅÃÉküidUûNÑŽ²„³œöÁ8U ›Üè
+wð
+=ÏEû÷ù“%ý`—óïÿý
+|à‡!6
+'°p»°Œ=|È«™-Êücš¨ï~„4j‰·åIJbÝW³€žËK]dî6÷‹\1|5ûǬ^È.1¦Å
+Žð˜ÜE–>éB"è уÆE0kË ¨W,8r,ãøQ[0HãE
+9NþÎwŒ+‘Zîl…aühû2YPÇÄóÚ=Ä)•t N¡lÌØDžB¬“å6ÏP,s­q)qP(©
+´d“ÛwŒ¿’è'ÊÞEIOx ބɶ’Îõ±€ëDÄ5Ôä&„õ8ŠÂÔú5φwq+ƽøQH ;Ä–cÀ!˜D”ZÀƒf ukE-á¡DÁ |¶
+£wñíÊŸV„»ÖápL´\ËĶ…V±»Ž†ì`½òß?yôýK’¡"Mæ– ~Ón=hF¹˜Z6‹L$^Ë—¢O+Âþw:€ –y)RÉ*d(¿ "üEB†Š•ƒjž ýÕLAh²dÞ¹—öÛ%…x4ëQð•*)Y@¢ð‹—$~ @NïøÀ1;MÈOz.¢› Ö¨Ò0û‚\ÃO#.–7qâ‰M9e‘e¦ld7dp†Ì…Õ-–kBŠ!Ð4(2|ûŽø4‘H‹ÙKdµ*üCºò£Š’*ôؼ}ï×™?}þ²u·²¦yU³±(£áç¹è;>ìý¯N\¨
+»—"bFV2W¢˜ÕÉò]»¬´F†üò©È
+¡ùŽžxï+ì.ÔQÆ•|‘¤Ñ1ld²”â(‰±¾Â¢ÅÀ iá¨r‰~Ÿ€„p‚í„”‡DľqnãÊ,æôò( ÷Ë€†9q!üR<D),
+îÖäSkÍ/J–Bû8Ý_ô/ø~‰Ú’s“.ÜãQÚÍuà·%Qÿ{¯ÉrÍ€Oôo—bõXÚ¦¤T
+‡3½Z1³ÅJ:4•”{.s
+̃Ð×¢XâWÆÞdqáåd ÊÇ”Y]bƒ«(8$ó¡jQ´Œå¾RËYcØJÜÈxSÏÌUôt¾õ“š—õûüÉŽ~ÙƱ`Ñ%Ÿƒ<$÷s¼„$<mß¾‘¢ÉvtgÕÚ‚"•4™fõ‰¬æ%òãpâÀaì+8Í`™³Ô€ ¡¿2ê \ME80‹{öû`õ" ž•")ù€Gûcâsø‰ ûô‘¤¹’s ™;ºÛf:Ëú,ꤔW(†©KúqJ;·+x}î¢ÌÆMÞêÝ‹XM½]Š*rm³–û-‚Ò˜<µ“´v ɤ(ÂÒF§h–™ÍFßê܈¥÷A’‘Õ®æI. !–’*á
+'µžÙ-Ô‰ª—ÀýMÁÍbªêYÄÇ™ /K•Œ¬+à Ý|XüiU‡ôVpü@IçöýÐŒû*`¾Üø%·èX?úSÔæ¾Ïû èÿ¨ã ÿ
+ê?ë‡.`$>JÙ?Rã²2¯¾$GbìZÊ‘etŠO^´ÎnjV„¤ìi‡› ßZU*V6õ¿ù!ôh­¬Áù†œ^Šþ~SÄ ‚4±N[ žER0 qÞí0j'-觚+XWtãžÎ¥™‹²®× åY[;/;Áð<—€ Wßî[ä‘;×·غÑfÜø7›»„så»`ƒÚƒ’3 ZlI߇oÂs¦||ÂF½%•ö°O°Êá/)G
+7„ÓL8³A,cƺr¼QgòSÚ#¡÷‚o@úÊô`¾Âzh@•‚™ä”ÅÅXg×Ew™·¨ƒ™îE^Z—ÖFP¢OaHpoEÖ°Z Ï!ÃL10½Àëyè‚X™‰1Û%¤†©Ø8Ó!×.CY¡¸ºaüAQ“cg•šìY×ÐÂ".Ò$ݳÉ!é>‚+”‡—pÀ*hNÙ˜Q÷Ëå¬+ É÷3ÌÚ!˜Ãü嬿–«œ}9ªrž}Ÿ#Å×BFd蘃PÈwIþ-4†{XD6R„ðÿæœ×‘?nó„Nðª'_›óÄñ£ÃQÉ·Iýóƒùû&[*Ñ“9å¿=8îß7EòxØ
+Ì(,«Çï¼3
+>Í Vñ/ýa9·å5UšZµn=Ð í0’4C f,JØEŒ
+ùÍöâÐd®×À·¨Ïy€y¾ÌnŒŠh%Td ó¾¡M .NÁýí¦Ô±´Ò½.dˆd¤ b¨
+sFG((È6®¸íi¾–°ºàtRröY9 7ùþ•%Í€5Úé
+9…)7áóH oæR06ºÿŸñ2É$É¡è ê:`ó°V/óڪ÷iôD§;¨. :@¹™‘Ÿ(—é@Ô5 6íb½6ø~A‘’sù¡«¡¼– kˆƒÒBû«^~]á³oÚ°ªìP¾„¡³¨ºõ:ö˜"†É¥–4oE*aø8¨3ÑK`
+Ûó.7 7ðež1NÑÀck«ùZµï â´}ÙŠž’Õð;„2.—Üôìõ,‚8YI^ÆCì;]ÒÊÛ¸LŽ–±@È&^*QÀ†¬æ¡ˆ¡÷-«½%î~Ôd¹øŒ8;¤ñôô§¨Íݾ£à¤%€@ê þÊ_¿CaúÄïm½—ѺGa»ˆ9S›ÍPR·S€¯"`b1!sÃààSl>”sJ*ƒµ_¤Úu (!!D•¶Ý”'F^•¼pëê¥O4†qï/Â4’fÙëD¸ZŸôÂÿ d̆ÍG·ë [e!”11{¡­¢„i‰Ëõ¸Ëä òŒCƒ·•`ñ*“§^æ¬fYw†¶»ï“…LJB€D‚rE( WiɇJÔËÆîµÎ,ˆ‹  í¤¨¢ °/„8²Ÿt/ê$<š
+…@<˜)sí”é+9è¼—¡Õž=AÐçÒ¤JB!tÕSB±€M„t‚Úp ¾V`ãü(¬t‰¡É£M6S›JhDD¨L«ÃØ¢Ð؈P”Ùº,•—½º}‡I¸8iÉ4¸¡=¬”ì³Up·%µ"Žþ(€ž–^ÕÛïù“ÄnnÅ`1û¤}µXäUà}Þ–0ªœ‰7³DÓ¿ÎΈ‘k Š~Â"ã«K¿ôìpGM S2%“äaÉLÞ?(¦ÊhàüéÐX%å·ïnú ãUÏËYQ,èïž`ÖcTuú¼|öj ¿ÏIØ%R$Âiëj²¹^@Z´FV4P¶!“•phîA§¥0IÈ©ði¿Õ8ÈÐiH4 $ ó › @!û]¤N¸µ1„bJˆ`–ÈR9v‡»íhd&¦˜›KŸüÄΰçûÇA™ë%øýµ‹™úkPô‰U“ó-a ˜(‡!á/ÅÙâ^„+‚”’v‘¹àƒpf#<®ËDZöI¦ Ô[ŽMü'ÿ@M—ÌРss+ØJúêò “påsº¹
+‹þ_aÞ‘á_(6`üÄ3‘8à¦y±È™
+yHÝ2
+7bn×–x”yåYÆ¥x…úPÉÒÌø)-Œõ)eßÚ®RÖ ü @„L8>È!€ú°|8di°~e?)6 §„­.ô‡yº³ÈÖ´.5éÎÌo€1Údo=kPTe»ÊFÕ¹÷i…Ç2±íá}Ý ]oVÁP`F:qá=®RÓ×DÉôºæ„ÊJÂE
+yˆ ¢Y’”äUî-(E0BkÅ’+HØŠ çI)§‰(í°fÞ 9êÛI••`·3ƒ$ìåÜŸæˆM¬$³– ¤A«Þd¤h•$'ÑÇ)J¨€£ib(J°½I´€'Îô”ä…q˱y2qŒÍQÉŽü Nõ#üNV/Èš9e¿r*Ö~pÊ”cBaÂz*°{+cn7 €§“UŽ ËŸ#ž `ìü­ DK³K5ú\=¼\Ç!˜P™1K–1«…vávò³æ笌l7ÝË
+IÌÑ.{•b¦šÉmw|'ëÈxÙ+ R;p}=œPÔx šÔóÕb„Ø—— 5Ä8d"˯0(é9d,¤lZL4
+ŸGA88¿Ê íå/'V ½ðTºh@‰@WMçåÐ<>VÜP¸
+¶ïyÜ[E7>wëVs˜E²ŽDbñACA-Ï¢àR˜þˆæB¤}¿]4êyÜ[E¿DE7‚QZÈl¬Ëh!Á˜Ea{‹²O}A0æ†%^!Áà†0\H¹ìE™AÞ%P+‘V~[s‚¡r"‰öPP)ÁåzlL æ9‰€a(J“ñ­¯ Ã}p£3†¨†ë$Ÿ»qz·Ô¾Ææ¢ÄÐCÄA§Ç²$S?a­_|‡Ü¤WÊDÎaðë¹ÊÎºÊ ‚Ál༰èŒø:éF0 šç²ü^1¿zyÔ<¡^qPͯÜf½]¬ßã´wjnù+kˆ\òÇÿ¸ö
+¤&†2òù^§ÝèÎù¿„5ø6ì(˜êJ:”Ì4PDþíËE¾ÞÑP¬Â¯$“+lœeçŽ
+*Mª„ Aû¨G2»Ùðd$Etüú'*z¦‚°ˆ‹°ëJ}¥Ùad!öŸ½†›?"F”x8«UtünœŒwVY‚]‰­$Ÿ:‡¿ç›4'œ"øS
+1k—¢//š81³õç^= ÁygÇFÿ³=ÞgI‡[våœ<ÛyÆR3 áø’eFgž´ìÜ|ð7cŒ 8ΣgGԼɀËL,]ú|Ï à‹Î‡ì ¿Í/ë”Oe»„¥ÂqÆL4¸XdZ c¬ÙÚòJª­ðÕK*œìãxô"
+ÞÚ]ÌN¼)rÔCï@³ÖȲñoJºt—¬‚òõ¼ì=ŸE/,÷õ¦•E0Z— ú¸™¦ât–ÍÝæõêp7vˆ¬ r4ùµƒ¢p ÛûÅ˧Lƒ¬–Ô¯ÂÎ ³C:,ý¦2\•<Y«çë2Ìÿ&<r¸r„±%8AíÛ§“ÔŠ«f)Öò+‘$²íg©°<5`¢÷¦Ãò(É6¿ ìغÃd÷Ûà}ðˆ®5E{Íç¿}}»QF³¥,5 ¥M93‹°áÐø€‚vÒ‰i@ïH‹ØñòàÑå$-Ä•kBüê„f¶ìiþK4 lxJÆøfïy±”Õx²¸»r8Ë”æãkG‰xˆY ŽŠØ_€¸¸wJ è-½h`TÞŽÞ”¾W”ƒ¶#V¦Æ;
+µœæœ9O
+“E'àí²m™ÊOÜÖ3à>Ö¦&yŽl%EÁ
+*’!䪿)ÄX¦.göE›’J0äÃI’;M9´¹ƒ™TLHÒ5»•P!á¤éAnÐ!Ÿ,z•æ:–„ñÙÖÍs³efÃèñ°:Ç0A Ÿ%½fVï0c{Í ZáÙe8W€BìÜ´QÇh=åºm&&}b±Þ˜&(îâð|6`©1B…8œO–TÏ<ŒK4ºëÈrªo³ôêÉ–Y\® '\èœq¼)áþ3Ô#8=‹pÐ/sÄ2ÅX&±ðgÕ«±HŸmBż <q°ATM>4n…}£sSHªáo–žüO×zfjZn‹Ó¼Æ8J˜UEJÐ÷=ÚP¡fLïp_lñHªa«+Ép8ÔWGºœ@ÎjüŠÄysU¦µå@:´ÑÝ4c+Œ$â©ÑÙJ¤Ñç$²-g+ AQ^<[A‘hOÆ&G¨”HËþ‡E‘“Ï0ñV±Çš21º%ÐþXQ œ¢†;arv¾–tº@[Ro¾Û­ˆV±©£Üj¾n5Ú
+¥%íQ*)ìÂ%@œ— ì-hsX%¦tÉ!ì¿­-)€GVÙyN±nø¶
+šíjY‡ÁÓâÆòWëËk¢”SrÅ>n‚7ÈMŠ~W;êµäň˯ÿõ¯oéãÿùö×=!1t¼³¹Î_,XCM¼ûóZ„õ”ã’U2þÃb]ĺ<ýüã¢&
+8œ›ný¸îö'5L^§]s“¯"n=‹ q½ûåÄ­_¢ ˜òªL4£çQ wÜ–½°äUMiEÜô™ûRêEô[Ž‰ÀÑ|«)åguxoF ßD\¯’mÖ­Ø=Ÿÿ¶&Ÿß˜·z]æìT‚—ƒpx.·wg`¨YvIÙ¿ûcaÒ°õPJ-^´Ö$‰ÊNo2çaœ!A ;c3@,rã2‡eÑÉüòI²1R ›¤¹ä
+Æ©©¸¡GzYFN: B¤Wܬ®0 dÕ
+ÃQV¢k>ëÆeõˆ •Ì›hV¢_o ø(K_“¡c
+ ©¨l{ Èã$mêSe‚I(Œ66¯¡|
+¦c+°ˆ"Ÿ.³Xy€ ~#W! T×IBoHú+ŸëHªé‰ø O­ÑÃÐÔ,rVxƒÍÍõhçÖœwá£ñCGU‘ÿŠ{Óoh:É(Û:ü7.IÂèOöÚïJzmHç?ã
+ÇÀóš¦l 0Šu¹RÆÇ3ú|`§[Ñχ"˜!áA2dœ;yE OùºÌ?”:"³K$š½\…å§S lŸ o±Š-ãyMåÉ —2vÇ`‹ÄßR¸8>˾lk$XË(l%‰^ЊÀR®ö@)U6LNÉFråë,r3ŸÎÉ”¢-8ÆK¼ W*D›sõ(\VÝöªÂ¸1üµ47
+£;ƒXI(Dè÷JLúŽ0 šo#e#bRÏüC šBäJ¨‡e¹ˆEBØÇNÑÛC={ø;ºUWâ])•½ç™ßòÒ„úëNêÀ˜hË4¼—ÔÒPPn]†»yK[²_€Ç(Qº®”…ÎQòýrô™ìK¬T­`¶½šÌM+ã£tåOÌG°IL¢\:x’ bÝÚG DŠ¹N²Z«d fÂA;%æ1˜g“5èç@ý0ú²"h‡Y1°3 –}Uh‡±W˜Ô$‰QåiL|çFŒ3­÷Zû018Û¤Ì:W(ÌM÷Ň«Ã|c`ŠéëÌn  à}¹@üݯ®Šò{Ð)²¿|¡ŠºŽ^™áC°P GOàÉx›ßVTþ„´JX´2pØ Sšõ©"®û8E·xò¹ ˧~ûÑþãï—oÿ¾|hš¸þú„gÌSÒl"K¿~Ä!è‡^šV™—%‘w4B‡
+ªˆqH~­¬ $F?~;˜•é$óîÜÀ+:®‰˜ð`6cTe·öå¢ô˜%Y•‰üÏ}©%‚Ç Ám_\’ÁC£…ê&
+®ÕKë¡©STq(,Tž÷Áɲ‚h…§¨ÙEðÁbÆùЂ{)zfðfþö…Ñ]ùü.d©DSÑJyh⺦ì1e‘I#³ª}*é`¾XH#Óœ†¶¤±hX„Uƒñn²ð%ÙŠ±§,Jð’À–FÄŽ„ÃÄÌ>E: á°"еJ2É!×XÞQÏŠ2=ìÉ»OÍeZÖ5}*Â%LçŸp0¢X>Èvš5Â!³qXµÀ^®‚/Bƒ…—žðˆ“€w¼ø5±¡™8EKY3;‹êwéFR¢éì¤J:> Hƒiâ.a˜(t(¡R ~1Óß-“¦Å2ðÜÚíå€uö‚ ÝDg #$&ƒÝTɲ&ØMˆé’·ÿûƒÕä$«„Žµ]Ÿ±†Å°>`¡—‡sî#¿ßÆÎù*‚÷î8@¾÷Ø]ˆ³úë+[óÓ¢R‹âvÀ€!W.uÎûåUÀ)–C~°ƒ¯*A@ˆyøíp¹1ùâg ”uuLjU<ú4MÅæs+øXÀ®ÞgTD‘¡07è
+ÒP¡î=œõZÞî™
+%ö”˜YI ºøBIw$}`XBt…’l ìÈózžÀÅFl'¹<%Eò3*"Èùçt~9lRJÑJJ¸:‹+RÛ3;„Ò­¹ ¥Stx_ÏJJ¯—–Þ¤€Gñ‡4èA()âò¤ÝŒÏÕ•¦†àòÚB¶©¹yG yBhœuQî](5¬*Ofbî9çÈÛœÐq^uBðèÎ ä£ÇÞBBùéÖ¼ %ùo¬æâýI(Ï¢C(%lŠÆÿ\¡Äcéi}¨OBI—`ý ‹+”9BI ¼+]éÊ`žÂ9³?•’"ü\©"‘]¥¢¹h.|ÝUJJêâqé^q•’†Q5|Dò”J$%°LÐMÃSJ•àŠ§hC}p•Ò-º+¥StWJ]¹ 9Ò¯9=¥Ô»G'¨IƃRª´)BŽØçè)¥¦ÀM
+×]}óÈò®“„0„”öâ-d1·¢m³Ý…ò?
+H‰Œ—Kr&7„O ;ü˜
+âEkÏÒ·pĬäûoçC‘Ç´Š˽p?R,ÈL$"^]üš-g9›ú|ýËt^6Ô‡yDfýõQ ïÞE=ÿ/и²‹ÍÙ¬{Ìûƒ m¡sA\šÿäâcCî?[ó¡é×ã;SUö§ž éÃfLnþúóã?òú7P½Æh&£µ1†lèT×tŸîñúûŠ+
+$Êÿ„Õ6¦šXAä:8Rrhö¤>%—‰g¿xÛ7ç´Ö©a϶o3Æœœ==R€4:ßhÎ6WÇâRí48dD÷×'Р|ô£©š½>o—‘чN‹±8ÔÌGŸÞÍtÝÇÚðáÂ"DÃ'4šµŸî"a=Z“dUNA²žn"Çsž˜W¼÷ÉoÃŽ ݯwÁiÑý©¯Ä¿OïÍÝ_'j
+(I·gïã éœ* “{t{èx\”o7åí
+.¬ÍÒaß>ÏƈˆÓô¦üTR´Y_ñèÅh»ÜÂv&Ú‚Æ ó7zC$ l4þ~ÃD1:Û¾ú¤®~¿÷Æ ÐÃ[zÛœ/„ Û_’ûò)'ón|ú¨çCg=Aç]£õÞÿ}wN³ò,AÚ÷mø™¨öEy
+¬–#”}TiiMù±±)_„aTÂAaP Oúó£·¤\j0Š/yþ$ˆ2\Dd:(ìG ²d¥ú · yáVÞ˜v{oR ¶*äÕ´Þ(xAŒoµ@=Z=<6ã dµÝ§AËRÝñ˜‘Ò©­BÅÜ·Q œç±† ÷*P_¼çQˆ™ô„{÷§Š©)S(‹šn#›Ý§
+Hü:ñ=a< æÓ¸§ÓI7XNéÈlêÄU:ì­_Ø3Â-r #/,5yÊ@:½4¨j˜ÜTj†<AÅy}qDˆR ì-ë\ǹø"‘Ph*<¨¥@Ÿ?¹Óºz»J>T³ÜEWßRh";½í£@ˆƒƒ²ÛbÐ
+€õ\–ÍN0™nïö
+ÝÓÆç°¾†ã#z¤q⬉ 0TOt¤+ ;#¶ŠdíxJŸZ¥–|«£ .ÜG=Ç ÉágžÇj3¼À¨4Ó„B±kX›”Úýx™
+05¿¡«EÿæQLZìQðDNÓã9Az@í˜0>´¯lÕÑ
+k²
+EWRfv
+ôF¤.›îÌvš~[“í‡FIPmŸ31Dø‡âlа6« ´õ8ÒôÔÎÝõ*Q¡@ÆM‘RZà‘3÷—2²²âí q§Úø›än¸7Ä(MÏÅv(NÕÎe¼[—Fm&£•5µ¢¿ )=ø„—3yÍ o +!jãÓì7ÕÍè7‹û<‚Ð#4K#ÖÓ¿^Ã¥P¼Šåóøtl›sm+¡ì§-!4À<H×
+£VŠT–,q‰ËA¾µÉ?uñÒ~eTÿÔí60C®UZãáUïŒ@x¹ù§^«Ã
+…¦ä¶ØÒbMm"S¾¨CŽ VZU ’×N$°:(ÉwBaUˆ&ñ—[O'½¾ª/èó'™Ù‘rá!Äß[Vì8èÛ9°8½”yËŒ˜_fraéââ-´
+ðçÏ‘¥fP=™dïe¨Hº.
+ϲj¯³ÕgWl*Vqµz™Í\RÐ^”S€¬$‹Ÿd.³™É>8³µ½[h%S+»‹s½Ÿâ,™hÙ_kþcè×Yô—® ’{“[©úëq0dPY ‰…Çt–¯ÁèÀ›5r@øi4yù)n.F\ÿÁ¦Áà[Ý{šè':œ§•¯Ë}bÜH½õËÞ}¯0?`ç1r;§ìvž:
+(¿isqÇ-g÷&V¸›‡žoƒø[­Þpv¶¹
+œ0yL?&?N´KéÀJxÕÉD+–¯0„È·A/¼üùyÿÇÏq/°D‘ÌÀý5â‚ÝdmW'³Ë_VùjW¾H•ÒO´È.lŸ¡°‚ôµœv$Y
+¼s! ÷¯kleÝF|Úv“ŒXÃ½éˆ þ•Cñœ™e™iYÄ ­ôá»ð÷2ËÖ2*ùQ¬¢ç
+ˇµ%séx“®9•AÅ%ø¢ËíA!j(LÌ""͇wÔ=l«XKðó>bs6\ìýëG "°rËÂœ©¡ŠdÁXr\`_Ñ•_Òlôt( ÿµq}°Ø©òk 1EÚ0
+{] ElÑÌûƒìýC>-IMÊ!êÏZôTüÎ[Ö ¤l"aˆ®I>Wû0‹µÄxü‰WE@@é…]Ù†÷0iûËåÿ˜}F×Ù÷IïøT½»Æb# :¶ÂÎi–lr’á:͢ƊC—4H&* Hz¦Ã’ã'N,£ŽÄ¸Ã€MF7ù¦{Ï{CÞ¯ Å~BðÁóñ±ý˜Êƒ°šƒâƒ (Ç“p@~
+ô¼ÈCþhþ©¤Á¦Lovò¯i(Ç!º1”HP@¥Å'Î9„R$Ó™ªuÕÆ;\Ìu³!ˆÞ@”
+WÂp“#±"jµiçPË“ëàä-«®K½Ö¤à=6\B ‹êô’ìUI3S¸èQ‚ØU¡uÊGiZeÚ®˜ZÔ°‚óê±ÿ¬<’תëÇf)Y8qŸ.@™Ywƒ‘W(U<
+Cç4àâH-܆£ŽK/ B½—Xƒc³XSå…øcņÙàŽ‹j9 ³KÖó¥…$.¸C¦/àÈ‚a`-cFǨMlW’[/íù|€ðÿeC}ï³s{°ß°ìXÉíã
+t75Ÿø‡&²žÓ?õ@ûäØÖæAEs£Ÿsøú
+T>Ð(Ì]{€ çFJžOÝA´›Çc>';þÒ@„ÌC
+ÿ"TˆæÌçÝ(5fßQ<wQ顨
+û6ÿŒÔ‘†Aëüx‘ÖJ› søË:¹_j- UbxÚ‹*íô”Í åHæŸi‹3lf»·ìÀ•'¢÷íTMl3É:Üfû£4Q–™J»ÔE‹7åï/Tª¶ImïG]d C¸*{…ñ%k_dybHƒË‡bÅ1ŠäÊÔª(@ò'èó€0<|¹U¦ÎmNÑO_à¢qd¨’ßg7f’ö¸Õ277<qbåe¹éW•[Jx-$­z}`,F g¦˜˜Êy»°ŸßgñhÚ»4÷uÁ1|b#‹LR×¢ìÕ„ª£A[粫R% Ñkù²ˆ ÀSËÃ:Ü]æ‚
+¶ïT®Á1‡›à­!„Õõ—ŒDòëGº»:¬I†æýYï`îÛ|ì-Ð #Ä«õ:샫Yú¤’‚“èÔ²h
+¼‡ZÁ v`3–‡^Š´ÿé?—`–ý2ñ¾a…¥”$‹Û`1݈¯ÂÏa—ZêRyï ÌÀ“zà¸÷7ªxÓ9çQÔ”Ôjb £«Ô†ÅÇ¿pë‚ôX33ýÀϹ÷¥¼ÀØ^9³/ï® ¤ÓÛhÖX‰oEœ£!uÒn¾æñ,ðñWnÅZ?ÒÈŒ+µ=d|“'îîŠÐûÞ–hn­ýí&ý8 ‡MÃJÁɘ€³ù$ 9Iþ´]„üVôOXpÅó¸[ÑÏ?¹“¸"ýM)ðNC2EÜ')!w4cTͯÿ,[¼ì7¶s—`¬QgÌÀèOÇÆò_6còÂë#Üe)ð!¦;¸Q„&°í
+£ïåyÖ¬»\Å£mÿhL‚A·>YDͳâÉNCeó.ÁC/À›IÕK0õLtŸÁ„g›Û‡¢f¯AšlóÆÜ*F3l,ï²¹ÎIäZ Re3l KZY>ÔZPâkJ
+€ô¾~•ßA¢ˆ—üÎòùrr=ÒîôÉl|¾. ¹6üLÁr_lHæ¶BÓk e.˜jZæñ/ø`©vzŽr‡á’]¸‘êÁǯw\Ec›‘€~Nbs‰%DœêsÚ—XàɇÉIè7»í~Xivbá^ק9Y\wZ#V!Úåô$ѦæhA‰O“‹@ªEh…ßaàPžt¬À…~¨ÉfÙŽÉBX['° –êyú³èq\€E
+y+À'iÒ¾zåsÐoˆ}‡$#mVâ¦v¢{Övƒ,5õ øߦ¢-Ø.øJ7·"qÓÞ6ôp&1þ1¬d24[Œ?wQ~5±“4—E?ÿäN~u™ÄQ¤g½µþáêýE£é s+cÆW×ûø Ò(뇫ëV©.ØÙרèçŸÜɯ.¨}Âæ}³¿>ádzÒS‘/§B¾Î\Oû‚u)¸9U%8(4Øæ]Bf$ºñ¯"ìJÏzÉ¥ÌöÖ´[sG懚VR^ßUN+¤‘²K08;ŠÜ·¨DGáöäa³&yŒ¾ƒô¬> ö%Á ocð-Œ¬uŠ^…G™;¥ (µ†ÝA+&²£+óÛ"¾µ8ªx›”Ÿô,‚´uäÈî'=/C»‰jXÊ+|”a}ˆ0èÚ’cò£îÍ¡£¼‡Óå\ì+j±íƒ\%–Fu+qTÜG|çœÇmž
+Û‚é“I祴Á/¼>¯Âi¡£††k1É=_Ç*ùR— %°y”ûÏᘽN6èôFw!ï`´±ŠÃúš^‚£S–<ãTœáî bØÎR2}$’%¾­%ïïps´-¥~¼Ž½„5¢ ·“¥Dv—Æ+Æ)!ôñd…•C&D°†¹­“PèEä/Æ9µ"cG3à_‘üÊlbÇ1qÝ4ÒüPRÈ:˜õ¡~\GÝŠšß‡¬ ;ùQXqh§5/{ÒÈ;ä3Zä' Æ$ß9v:ܦ‚¤25¼3(âíÄè™ç›ú"|R ­ºÊ”’¿Ä‰ÓK H©™°÷]2§º «u+9èCšt¿”[T´WX8ŸYÝpr›Q÷ÈѼJ‰-× ’‚.«¤YóHS‰Žª/B
+ ‚½WÆ=&§TÀì&C²@JZÊ4§ÃsÒPbåLq œÉ^61…¤u/&°éˆôÞ%??”ààÂ’„\_oö¼‹h×·SÚ1”ÜÄûw vžÜ¥ÈÛ X‹2p'àâ{¿,Ë ³Ë °e«Á­n_Øo`i}ç:Ã’ròJ}{Šç0o%ŽȺÁ
+eûðfJ
+í:jnݬò—Óë-NWœðW±…½q]ÚvŽÊ2VbÃnBÖ›x¡bß8›°‘ÊrÎa K—!1ºdàô(/K€Šï7%t–•Ã]õ 8Á¤€h¤—À…Ë°`ýt–HÌu‰«ùîáG& ¸LaÊ‹ ‡"˜×xÕa¦
+ÄC’iŒp"—Éà
+‹ BlÌ¡T û0µšôm…þËÊ*©°Œ½Õ©º£_ÛêõM„;Ùž6/µØ0pK2¶|†vðgbø—¦Y÷Ý.áç€Ãtù©·…©6¹1óånßÁ/wÁXþ©Á@«Áf»bEi °™ûg8™L'7dÚœ h o‹…ª³hs‚ÃÀ<yrÈç(ìã– ½Òá
+õò^6ÿKHb4ï *D“#ÈkNn‹D­tJPN ‹
+‹v̪úBEÞ:4F…Löh,ÝÁIPV —ª˜ØD¦œŠV{éˆÂ'žB¬vOQê*UFPöá‡âè„C¶•,è‰&s©à̯‚MÌf%MC§Yí¸ÔøtÜy]á3¥›ßrIÎ"¬Ïà˜Ñæ¦Ä8*š$·»Oú»DD¦ë]#F»—従âÂýjáOätµàx³†é¶ñ¤
+'æ~Ú ‚s ®ÌñRr4¦Ì4ãö6!ÝAM:¼9c|½‹yBð>ü€®æCè«[Èjuǽ3›ùl„O(+Ëe¹Bß.09–—u%¡¬1Å¥žÈ:©uQœéC2^|GVŠ´Âêñ¢bóÌ6Rû{xÔL€¤Ifdj¾ \×æÓÛ3ªEÉ mÚEF
+1\rûÎxHÊdžâz–¼c'‚»jGùDsf´îùôõò(¢3Ö ¨çøK†·¤Sò‡Ü.Ž,¶Iµ’L؈þÔÿ:Hœ uAüáà<‰,”dW•ZÏIŸÖ… ìRÆz„üñ(.‚ñb}ÖP\3Ê2(€lù¬ñtv¬èœ#ž…
+\mÁ|‰ÆUÍ¢«-wˆ£-wÐ}Úƒ\ í¤¥ûYO0ûî‡=9Úò+јÒbä®ÖÐ~Ü–Œb²¶$-Öævׄð‹[\Õ”…æ„ 8¶VeWB½Z?SàÂÿ§BìJ¾ƒ4¼ €]z6S8Ô ¡kÖ…q³š^5wAV©Ã¾oëWä:¬ñ!LÜ×/èBêÌ+?" É·¤Ç Â¦6%ÌîP/\ àïÌC– „4¦múìí¯ 5ã± APb«Ô"´íž;„J°góä ¬¿Y…"K}&Úh«Mæö‡|†•%Eý˜1k³ŸH£VwîfÉ|Ÿs¡‡'å½ß‘æÓ:ŒøT
+µ`=줃;+R <HTÔ»;]@©©2N‘J|õá´¼2ËÅ’›íí1ˆ c¬½}˜mÐå´ æÇŠŠÈüûK eŒÆ›Ä¬?2.Ú¹ü‰Jòß¿¾TI\fZÄÝxÜ]Î5æH-øù„x¥*Àò›У)M–ãèíú•R'„–$1Œ±7G‚¿Ï£:Î6ijÛz ÓíƒXŽ ß/T¹­Éç'XÝ–ä»ÉK Œ™–F¼#tÔ0í]ì.ÀÿJ®,/ö0dîU:#®á&ŸŽÓûÛ“ 3;—Ž!#º¥é“€ª7ºå؃|jÔ:êbÿøT´‡ßüÛ4èÆëš¼Ó‹W‰¤¬¹Ø¼ºVç2ö^‰û,±3vý®úŒXqm÷õ+÷™¹]Å›½Û“¼¾”ÆcÂ¥À¿dÔW(Þ# _*6"®lîýGV€‰£°³¤ŠÁä•>–‡BȉQÅP$04ØIz£ÌxD=%ÜÅôÍn…_Šø®.ápŽ»‚~<¹“$(¼ýùŸ/üýX‡,È“³…˜IS<L…Ÿ`!°LCfB:ƒûçì ῾½
+Ü?-þéaðœTƈ†¬+{‹pÑ@J9©õ t9íǃ­{ß>uÝIºø`¬èµ¦Èrt@ß^€>qL Š8Wæ&2¿ºw}Þ#ÌÝÓp⬲80û¿ºú,§”Ð'õV[âÛ¿€æwN„×–c ”Nd¥ÚX­ñ@²R„´KËC0p®]„tAÄ"÷ñ³ ‰8lƒg!†A‹ƒoǵÊ‚€Õ)D¿!!‘͙ߑ®±líf ‚*w­É¬LãÆw•GÔ/ã­çQö>¥ÑB$PîÎI˜ÞÒpTo‚ðUæTÖÃ¥QжåùÑ„P%)5bSuYÞ™õ\lŹ di V…Ga…Æü1 „ÈÂÁ aETS#G›¢ë* àZÈ:ŠÚÑ Š?Žº~‡“ðšô
+ÚÌ’ìê˜üö3!tP~ œšÙ!-)„À ä¹c…ÈÃ{´7&›ß‰JhœCÉvÙ «ŒÎäu–(ªÊ¿ëûÁ~W¶ã!§W¡ÐòåŽ{,‚$ OÓ§ßOrÂø-öfoCà“B³XL €°ç1oKf™ÀŒ‰l–—ï(2¦Ñ^šIÆ»§Œ­fñÍïtVæ…‚äºùPz€[,/4݇h¸"V8¥Ý«;gd¼«4µ®\äz5¹ìä€/ï™e‘5Û^YS¨‹*D<ÌG¯"ë ™h+\È 2õÐ/Íú¿Æ×›À d…A,A=ÜQÐùq#`Ã>N*7S¤ ÑÚì'S<ê C‰ä4Ë}Ÿ„q ~V×Çg«VFc¤°ðJr ˜N\ÆͲ+3HyÞ"¾té*:i¶üÊË%·Î†Â+ȼ‚@oJcw˜]èÈÙÿ´ß)¹Wq€¶H\ú—Dž ˜
+*ø*6ÛÄÀo7BÆ
+ÓuÆÉð@ÿˆý H[d/ŽF¤¦Ò®u[Æ*gÔdk
+p3gsÊ!ÓŠyÅz—’ z”€ ¬8^|‰Ë ‹(ÑÏQt93ã¶#Šè„?&EL³Û…Ã5ð$0\”Ù¢óÆ°€qö£àY
+Þ ÈY—ÖÏAOE
+œJ|ð²h€¿s‹§±â“ѳÚã$Ô·³s˜"QB¡ˆÄ?}l!['7Ùã(¸ñ AvŒ‹ö
+
+ˆ/t{úeš‹ÞÙâ2ø"’Uâ¥ƻ4<Wj-^relyœþñt4åuáùóî‡Ø}J6--–UE÷ËŽh8a õÏóVò¯ÃZàA=2ÏÓg¨`h\á Ɔ7‰êTÞî8_Î1^Æ+â *öœÀZ9¯RÔN`Ìû;m ­øüW t¢Ë<‘Ìu—tðC!´HGoL`è3Ž‚ P˱Fõ<C>˪VjƒÂ¾Ëù¬Y\†=¡QtyöÐ„&x¼V¡)ùI„[ûEX MWõv'Š*ǽ[g?—ìÕFkèç9é„#nJ››@\
+V$ ´H  ªÖh˜X|¦hêðc8í´gÞaq®3n†ó îäNŤ5µZntŸÂ€@‡,“ÂḀk€Š„׸ úf=ͱiB„¦e3Žy-å]ŽØðeb_ŸÅ‹hmÎlñt[Å"Vz#³a—o
+<3ï‘£kœe¸‘jv©ýÄ@@Xд^|F¦ŠH?z¼ ŠÀ¬åNçú¦D*ƒîŒÓc@Ò·µ È:F»|a"é´.„¤é¾œ" 6„•ølZƒáRý˜ªGsÁ8zÄáœù§ð"•£0Jº¡ÝKŸW«²@ܱ§ãJMN­“ÃŒ"QÎßÝÝyvCÀµvêX~*–´ùÅþªqH”ƒ¿;»1+Ö._ù²0W á>c¿;™O‹’¥ëe†s¦5ÿ§¼\’½m(¼ï¡7ß Ç=í]¸Ê£xÿS‡
+s‰ùÄ^"rA’ù‘…£Äâó a_—’»%úøÎ|8ÉÓ,]ÐoÃ>&õi»DG-E ±â+èÍ©há+æ£C\¸“*+VÔbÁ*d¹?ÃF6¹L!_–ýVòFü?»a;QÒ&'hsïè­†­4…[÷³°À]cÂXÖѾ”€Á©n•6‡¢Š…ÀÒ ;–œè´@þ–k$ö‡]ýÜ^Z‹aí¸ö€@ÑD
+äXFŸˆ›ÒQ,‚ù•‘uà °µ­_Ó
+%ø¬]1þ%
+Ì%¶”<Øå²R¾TÄ’¾-ûG&š;}Nâ “ø0¬ˆW¡ŠX.ÜÕjzóss^Jü(ˆ–† ËÉMßÙ‡JªËzÆ/LÿNcý6h0|n”Ç>£•3©ìÆ *,qöÓdÔÌ7ÐPwœÆÄÿ†ïÿÈM¶'…1Ç"䳤 ÜSÒxÝ!OÉ°çr½…ûmš¼óÜä¼c‡@²{R¸ëF'ãĈԆîe7U¨:ùH?²ØQ¾GÃp:â“»'¥ˆ@ àɹÜp˜Y\©ŒäI áq²¿üÝn^›,ßÝ
+ëWÎËÁJ™hÚðï(Ðð8óT[:iŸxsÖ–¿ ‹‡Á@؃uÝ›OG ç`±Yï(Á‘ász‰·’Ÿ¿ýÁYåQ †k¹xú?)"Ù1&è.·ìRôëKQ–k
+`’ô»8òÕŒTƧè`v$ç¥ÀZIü¬Co_i:?…Ü×ú,Á·¡Š¨iÙ% ÿMBâ_.×ý}ï ôL3°íÜ›ÁÒš²¡t oÃD‡M?$ÞØ.Qš¤Wð¦–
+·¢_ÚfùÏ¢7®û¯cbÉß³ééWð˘f¿,¿ââÖæ·Â«è®ÐŽ”¾;‘ö§öölÜÄðWÌó>ÌúêDš<¢Üóù2,8(2ŒqîójE¹¢WVxM§äÕŠTÜ'6XÑ¡u)ñÕ¡5J]5zõ"úUåp³=ÝÓ«ÐP†` 
+³y³¼7X]¾“—UÙ"¤çË£ wÖ±H‘ŸS°ÅºÓáê*¹³äB”)Ç s4ÚηÓÒ£6yÀ@ÈèsT}ç[|ÅÜ
+Â5ÓþÏX-†
+÷»)aø0@ÇD‹q@ZJÞ(‡¢ù2ºaØ’,êªi'U*Œƒ´ÖÓ¾ \D¿íÜ¥šÒ,²ÝÏG>’ Ep MàE_
+ZcØÑyuè¦Ú—
+؉¹ñihþœôÏ&ã×ö"ü‘7e•ò÷8-óF‚]¥±Û‹ÜŠL¾5‘+1Û?v abÂü8à~ ´‰Õ),›­QëðŠœYÌ!—øû>
+Rx$_Ö%fº°n˜X~éG‰öÙf?ÚY–2_ÎoéQ‹Lu/Ϩ RÅ9Ý8 ËÝeÌëAù˶«‚u„”0Ï…ûP:dùS?€ ö¥ÑgYð+“ªÚÅ»êçQƒ”Î@#^R%ò>¢l xhòïÎQ%êSÓª/»Tè$Ò#»™Ñ“IÞ>C¦ãú°+NuÜØM%}2#ŠøÇ{|T˜QE³]^®^gÂx†W®ý#ŒéUܘá¬+çôEÒÒú&.tFż z·ÚL9D©'¿½Ð‰Jp/
+ªFºÁ·m­¶’ü¾Ä²—`õ“œg­Ao2õVáÐÕ®Ò'|ùL‡v+¤eç2Ú{Ýe-§GÜ"ÿJŠ–½D—oX®sîraÒ¬y¾ƒ\gÅ͉áÝ]Ó¾‚ò”cì¼ 犽£{$Øl°Xžaã·ö‚ßú¼L(¨îÀâi
+‹84¬BYjc -<G²;ê^*Ž eÉi(">®ŸA õ„¿(/—Ü8o Ÿ@wð |6ɵ²ô- d¥Ü›¯Èæ$óX9’jØd?ªªá¨RŽ5I"¨gôô=Poéƒndy§mïžhEúîßíÊÜ1¤ŠæhòÉ #‚¾*êXÎIÿEziÚš(kXwîo]]ãý))ç£Uï F‚Jö.§f<2Lm³£¡a(qcpÒp’™ìÏa£@Ãà0|l9Ã÷Haže¾W ëNˆF &B+yS»³Lz4Te&ÊÄ·å¡ S˜N)nÍÕ1±2 }[¨¡ôõÀÓ=38(©.P÷ ôkW©ãs¸‹Fƒä!Z«÷PÉJ/âÂe <lž¬ŒG2~±DÙrÇ@‚4šôx1
+ƒ2<‰Þ“j0º«áPhQ­]Qgå‘‘GVÝi7k„ ÓÁOªÖˆÊA¥´XàÂoÌs¤bøD(»× !ËUcˆF<5Æ‘Æâ"ø§IÁÿÜyv£{GÚ¹ÇÒóœ&³¡Þ/Ã7@§±•au΃¼(͇}~üM©*ãL'Ñ<"‹µ“›½
+MÓ2^ñY‡§»€.‘%)Γ"Qt£oc\wáAÈlSš}®ä«›]Ù” `ØkíÌ'åÁ£á Àüõé³ÇÞ@Tƒ1"×eB>?N K#ÿ”yWS‡vÛoƒÈrÊÀ5øºum™(2ͪkŸ ¡÷Øž1î WÏp©ñÌ`Þ¡b¤4S~ÜGÊŽÒýè•-ˆØÍ“Ñp7jœ™”·n;Ô;j2Ì4ÝÍ9—=æ7ˆ—æ.’˜€Šàîbº9çÅÌß@þ¿xg0ƒ¥ª× ¯Ês»˜¾Óæ0!(„Ádï9¿\ù¹>ýzÍà q)Ãr-§@ìh4sì
+X\¸µ ž@Ruî2ßAZF[dcÛ‘LI®HþÓü¼®ÜQ)ž¼ Þ?ÿtÙ¢v(Q_7šôrò&²'kå
+5«~͈eõ ýK÷ \ÖšDMã²BÑ  µd›õæœÁ£a4^ºk…˃fÞ WÙCr€•¤6Û®¿´ºB¶)©‚)óP“1ÿ6zŸçdþ ‘´à¡ä€Q9Ô¨Mÿ;¦‹f¼IåJõ¥…wE’Ç{7ò:`q¦¢<[‘´ŠÑ)ºµÐÖÄ£™h/Mˆ¬.3-<w+k[µ¨y(£°F"»+N$Õ£ºŸz…€BÜ@
+í1VkøùqÂ\„æì˜ÞÔHMv°í\^ VØF¹ÚœûÜ´hï  Y{V£SÙd 5²NˆßÅ\‚}}çFëâ×£x^ˆìž5K°˜î¤ä­hµ:b~ž1WÑîZK ¨¸R¹Qö#è-ÚÛ㾺ØÂŽµMŠBG†ö ú8å¤`¿Ëö¬I×F‚$(âI!/w¢(~ÆÃL*o'ïüóŒ¹^[ÁøúÄÎl3
+Ÿ ¯4d?ÔÓ&½(”2ÖZ¯9ˆ}OYéƺ –J’‹ò¢É ¸Æ¡Fˆ;b1tšŽ˜MjG¯DÏ1Ÿ·ÂÖéÉå¤gK O`­h9T¶ËÉúƒ­ •xÅ*ù5|NèûUVdø:®0 ¥:˜Z'>C1U†[ÏsÈU1£{9qE¡á¹˜£8!,³‹9 +V1ƒ òò9€è’ÐæMÊÐA‡4JÙFÉÝã
+żTyø†Ã8@V¨ëX½Ã¼¢]G«ùø*Ö¬Óê¤ñ ©kÈ £™eÝÜ'ªæ ¥(ŒÅ'¤'ÄÇÂFPIQR¤¿'‚†.‰þZK&DôdŒ]š=ÒE'¢óðÔ9vùD Þ k{~¾[.¿¹…b‰Ý…ÕÀ!Qš!Ir@ü Óʯˆ¶Žâ®Ú"[¶#쩣ꯂ¡‚<gÙ·ÁÐ÷¼ÃïB#Bš…¬›y €ðˆY³Åãø‚`<ƬBÙŠÆšÊ$‡\$ýÂ׿¦\O¿äÝp´Æb„š8]Ì;§Žõæ4«y!3 òXRÇ1}sþoì…\Hüñ×k8p UÝòiZ!™%­—r"¡3°P±ÑN A ­ye¤½†¼ì§‚<6YÛû‹[1ñÀ(È@teÔ UtC3›uçÄTåï8{c0Ž”mA(ÔÜ®¸¡ï!=Ù‡äÖˆfëd_‹ˆ¯<Ê—¢ÃÄižC¦+2ÑÁì«
+Y-9ĘŠC´ÓA‹Y[¤€ºq&ÎCqÔœk§îçÀ˜¼«°+;4.c„‚…Å:µÝ)*8B
+Ã9w †D¤ ’L•äøPÃwÌ× WßæÖ(>›úâW|¿/ ÀﳚìÆÈ#Š¼Ý/ƒ¢¹”tÒº ‘mL[c:-z@f-¥y¶4 î⛈¬)¬¼³^)® žPŠ7TH¶ZÔ×<†åŠ¸¼rH¡ƒ`Ó„ÄÆÕTÑí=²”hZ‘dó@AÎFÏÕËz€+`m‰ë,!éÍýfV~3sŸŒg¸OØŸ¦0ÆœDO½ƒ—!Î〙óÀü6Òž|û*°êcV¥><ÆBÊ͹L@ò>¢J9ÐRb09§7ÉLkžDŠh'{3ûÐ4£–I&ˆÉ Ñò$›×«¤ {ÖW¤†˜kè ½ž¶}Ã)…ŠusÈÛzé—¥Û¤ +ËD íBà‹ø3%ÏÑR`Æ[ÆÊ‘¦XYþ ÷½ h°׋+R”
+Y-óÜ9JN6!oD« 4<£ìPo -øaþƒ
+]ä‰rÍçIÂ"Uï
+2ñ-†Â˜V1œç­ÖàYGõR VŒƒuG1aTEè Ç.´Œ–o¸g;ùaÍLœÀd„½F‘0 $)(‰¦OHLÿ,Œ.AÙSòæ°tÊ®@µÃÞP€÷KáN–<ØFá³
+]åÙCÞÇ–œsàòŽ…P&ÏÁÖA\‡Äm Àšô
+{HñÐì’{‹ø9H’
+£óÛ6*…“©0‚ãz1RÿãÇþ|’Ug·xàâÝýc *äëù¥ëv+â•8¶?âé_¬5t€iðA§p°qr®VRh«œ×Ø‘4ñ>OúW•GÐòÀknØ5ŽÇ"Ù9x'º-®Éûb-Tr^Ve–†‹È/òi\Þü*Lþ€åèÚų«$WH#ÉŒHh? JgkE¿Ì‡—»^1ÓÆ/
+–ôk9Q‚ÎÊÈ'Ü3§Ô/]€o0sXÉ’ÒÔS ïQåEË
+&ýco²tó܃ b»È›ý"|#>÷ÌsŽ7/%Ôm’Nô¿Lù¸Îz|=zÊ^•n²Õg«â¢ÆÎð:ÔÀJ˜ªLó ¢ø u•$‡ìø÷”,ƒÛ•^¶•Ð¶*³–ŠU$" b5åÎÔ\
+šºÊ}*Oí’×å³À#ÔêÝ«r¤|¶­å(a½1Þ„ˆm7ë$FK£™¡ºK‰«+ˆX ŸÐs¾ž\ñ!Úè#JxaEÌ^¬d‰Øšm\ì”3¤M¢ˆT”"%¸‡Oè-R
+Ïøp¢˜Õ ÍV1ñrž
+Ùq)ÞHîà“Þpœ`{ŒyGÕì¶<æ)á IeHFòÙdlßз^)§®å'Ø_º.)‡¦º~ªáü¤Á‚ðˆŒÃÏ©^]ç­¾æ"VׇÝ~Át èƒå\^ΩK–P®×«PM¨¶
+Ùíõª"GCÿÐûéç°S97{ š„æ gõ'Vùoì9-ûM¿‹° ¨;ôÀÑ/çüH¨wVÿ!‹®õ(ú¾šèG2ÙW˜•µêWèÞVÎ)üîÕØVm*0u_l9 mzuUìaÉøî2mËù‹É@½#’ÑÒA@ç¡h[@nÑ'ü.qøPË9,Œ‚-­-Øf’—„¿ŒÔ‰FXP숻D9ãì©XýËI]BáÑân§}¯Š(ßMnWy`
+r,H~¢g!°1Ã……ÏIØüÈ4½ã[¸–ßÍK¦(qáØ)OàGÔ‹¿ÝzŒÍ,ôy‡É!gº“\‹Âf’…ÇøYsè?ÿ*b·
+pðÀ²Ã·’‹³½áÈrÓfõaÍy¾Ff¼Ë¯Ã“®zϯ"
+¹8eûEP]¦’HìbmK±Ö(Ú`'HiGò‚l'#H޽΢TÝT#2¡$À‰9¥ð Y:6ÚÏC‘;¨uÔDúƒh¬ ?äƒÍð.¬]‘ K~SaŸ`}±ó
+„âI6÷0Ø$G˜W¨ó|F[‰±CÞó!•ÿÞò£á “†û‚©n%®Ñ
+b›ï_i½œŽøCœÄìþÝÒt=¨7·óólE”‘ò4'¹6>,¿ÄŠØî¦ÍÓ´*Þ’2ãÉìÓFËø“. D£ª•›Ü°?¹(åÀAŠFh%„‡›Wh
+H‰”—OÎ\7ÄOà;ô¦!R”H­¥oa`Vöý·ó£Ä׃ô÷:pÀIª%ñ_Uq„Ž™ÿèhOÕ¡>§X ŸÏ¾Z³VˆÞGæ}
+ýd 5‹nc.{|¿½ª?ùßÃlÉX_@N„êÝú\u™ Ž0 9µÖÙÔCÔ'hû{JLçkˆ{¯ÐUZÞ.Í—P÷&£õ¾bÊ>GÚ\œ9áH†Õu¹ñÈx.ªÐÃeö™¦W­1–î«Æ3ºðEÛèÍȧ •%ÝçLþô¡kõ.S…ÄÑdCÌeõ¦3c;¥b”««¸ÔU;UcšˆÔ{V¸F£Qd'·¡]ȳ|Bø¢8Ú’©™@î–ÆüÓKvùþí¿ß¨ì“cçl±úÔ}vžhø¢õÇï[ÄÙyx¬üòƒgË“0›Uçσޮûõ'oú±Ÿ>(
+.e´Hc— ¡«³*»±>h~
+&Š{éZŽ¾xø#!ÒrŠÑó©•@ìqJ(Ù Sñ`
+v9›å¹"uUR"2ŒÕ«æ-²!yeØ>'Ó,>y|‚’¬¶Ö¦ Š¦MŒ|]µw;Be’ºŠ)AåÒRã•6![‹¸Î98Õ`‘…Ïg½†ÞËíŒÖÝäô"س…t’a8þºê Èré G§äÊyódyÊÊõ‰bÏC²9%gX¢yF€ælË×Ut:–RÓàøa[’øÒOØ©9õ„>Ù çE¾g«“¡ŒìBzeÝ@N‘3 ¦Îɾ¢ÑY!´žì¹JÀi¦Vs…»à&~wFøÀÎUÐ(Ãä·ö)ªž¼…EéâuN*„âªüt ¦²¤lU dΘk\ɺ¢âçú&¥Q»$F[ÒYXj„ß©‹Zí—¨±@Œ[
+Úí8?Ü–ÖUïTJ[`¡$U<Š¿p6éÍ.á1÷ˆ¿±úwø¿mþ—=´“¹¢Eôfû}@Ô&EW“!¶´.NŠ,<j‚”Ó±d1¯-µ}­µÝ½’KÆPPdMõ°bò=)¤qÎaãÚZ¨sH0Fîúü„À‘ ö>ªµ®›’·ykW§Bœø,íœv—F¿ææé1 [&„yŸqUô“­,Y¯ÕoËb—d"4kÜ#<[wÈœŸ%";' Ârúu@p3
+ë\CL!gú}ÕRj^‡!6öÑVA’H¡»Ê­Ä’ÝæšÚk>U‘äO­ùd?Ä<Jx¾ðŒðÊèܯjã-Òk Ók#žÙÕ5(<ÀxÆ•bLÊÒ! ¯¨ 8ˆ®åûa¯‘»%)HŸ9)™g®XwÈÃß_=Ìü¶ÁS¢_Æ!¤ÑØÑ°âq¬€|cº—ÒksËì¬ìàÎ:G!J!ÞÕó RºGR‘j&ìÃ9i‹s Œ{AóÙü™ Ù\Wr'Š^ ÏAàur]åÉN÷_öÓœ¨@UìEîh"ÿ¨Ç@,:X¾ŽAê¼j¡ú7½•7$HMX…hŸKh8Cö²÷F£©6w΢ä4õiôtÇ $BF.‹%³àzÙ-p®Â@Ç°;ò\|ËêA«qÕ&J 1’o"—¥C¥\ÇIðŸû=â‹”Áä‘Ø‘»NHïOÙ­$§,j܆”ZJ¯ Œ]7½§&û±!4²{îq—àô¤ˆÓj¼Èí¶P؆9×DnVK¼|;ÜpdïœÖBvÒÚ¶Ë!Ó<¥·åÉ»ûÒ rJÒCmŸÛ '3Í«ûhMÞÏøÚˆZuM‘ *…¼+$+÷X\µ—Cf¿`@ÒÉy ÉÄ£g~9㛫;VÑåöœt „~L|Žöó;r\\B¡ U’ÃÌ‚§yÒVõå„riÄ:úº^L_™<g`!LBLú¸^ÜxrÛiœô¨ðZPOŠ÷!ô•mÁ„T=ÃÎR›2t›ÂÒ2„m•a,&‡oXCûÐð0ééªÞ;È$›ÿ/hHÞ¬±ñ8Bæ’©òFz,Iª2L³!Küôò/oBõ¶¥|¿SÍ7̯{ |”+®Z~Eà_©¾Òo¼o¯Ç–Lyy2Æ.Ùþ?ÆË$I®#¢'àxYÌÚZêmÖ+òþ[=)Vfün™L’±ˆú8|8À*²·
+$ýéE|ƒ £]8¾‡$¤ªKhA{™¿"PIê"Íþð4`aRŸJ„Um[s?…5¢Žýݳ<|§o“ÓÓI_áîK֋胟#tìï™ã•P*̺]žÿUQr_«ü6 ·¢©t„¯ƒ{‹ùC0m7¤è%TÀ‚ôÀE»•é:em•#¬X :Ü6ca!?‹À|ǘäáÖîVR`í©"%_,øþOÿ KŸeéûÂg+þ!çÉÍ™ð›ÐrüNÂØü²" °a={FÁ„^nyµ’")„a'R^ŠTB’Úø5da¯(©Njš_§DŽ*ç[åU¢áv1o×û(ÊÚ%ÔQ„Ed
+CIMÇñNM¶À }’
+ÜpÏ0óXW«´°0?´žŽ+å/Y!~m­xºq»R½;ï~#Ö,ëG‰Þ`)
+›èY‡/%w?ôñý‡^n¾`œ7‹
+LqFÍJðm
+¸øÀ³œÍ”؆§ØДR, Î$bá­ ¼ªePJ–t«C(«Ö Ôo%oÄÿãÛ­ÏÉ¢6Ù@TðçC  `ÕX>÷·’Ù5&\eí¡ÇÈ,Ih%,E8ü$Ž¹Ž!':± Â}‡ÖðÜ_Œuµõ”T¹óNb8žý#P$æWf¬ù$¶ˆ£ÈEÓ¯,Žà›ìZº†•Pq‘ Þ!k"00lÒÞ0C¥"þ9cS$#f$ãWÊùRÂßL ËóO.â.µÔ-c?¼dh­Ð‡–o%~T!ì´Ìš¿BØÇwªpå`Ïe¶
+?g2V4€‚±_ùÝŒþøv­éK.‘õ¥ßŽj°|M†•
+y™ÞaÑÂføzœ„þRU4‹gçaqØÆI¨~#(ð:o_Áoaød¢†Äu1âÃÜeÚ@{;ñ¯‚0@¦i¿† `A
+·ž§h ×!ôÃED%`$OJúäjÐ_.Ñ‘ˆºñJ:¿—Dß þ‡X¨éHîŸ)@·`^h"ûAjë1à¨y)ñƒ0Îm2»Lƒ¾SEI3Éiœ71<[Æ>û]6ä!Ÿv¦ý{¢ò“²ŽÙژɌ¡Ly·ièSIVJ›ý(ÃG\Ú>(.ƒDÆ}é7
+ikñT(¹©AÙK’<XQjOª“øQ“``éÆ86™š—r:üÒ¤ïHɉV|Ç
+<«„-„; I¢<¹qÜnCYΊl­!WsŒ£Ô&<n~S%Íd2O
+ĆacÐRo5‘²ˆÂ^ò(R„(V÷02VFë륡¿ ¤!ž+KË?î«öݶïKI%²\ä“YæÛwDÈ'§ëŽùvJH;;-Ø÷WQ†¨½Ï[o( gu™ŸýšÔ×S’®@šñ8)ŠÈ[\Y^„òeà ë…¨ål(!R^
+ãíŠ>j¦\òвµ|±JôÙ.ÒÌó¶
+—’ ÷ߊ†ÒVå½}Nÿ¦ToNm[ƒ Òiš7Ñ£Ô)¶3ßܱßÞq”¹BW3^&¹uä@=ïàdr^kë[xë¾ÿ¶_pøò¯Ê‚…†–"‹9ÄpÔ~d9€ÌRŠ0Tá ?K|Ôž¯ˆ¹rUYÚI4Ãïë+ó@ôòÐ2ž³ïNñµò¡üõÒ†VÕä,èc)#$‚ÿTNËÛ¤ÀÇÊbÃÝéS¶kš&9žé»Nñ
+nòG
+KÑøä&ÇÂþdµ¢§m MΆ?céæ@ÖN5-&UD²{ô§h#ÔVݯÁï!ä0S›‹ÿ*‰Ää¦î5¯:šz‚@ÐÂX¼‹ÿh6£ƒs·è÷j¥âkg•ªÛs±q#ñÀ „
+wrDê)`X‘i^ïOàd— î$»:ô¾ÿ–ª—÷ÏQ'm
+£ø^æOÂ0ÍËçÊ°)“]=sØ n/x›´\é2Y‘]Ç›tϵ †È‡Kæ»@P% ›1FFÍæ ³E€dø}šË‰"—D“‹æ[íÿ*ñ{0ÑEÉ”9õÒ•T–6óC 6­‡i•s÷ž 3:qÒQˆ÷ÄBÍŠ¯¹ºO(_ËàæääW! œªßkÎ"ðm¼H*û€ÀƒD¹ÙLmÎEÞ•M¡èØØç >ˆÌ³m2ÔÖ Ö¸L¦zR™RD¶ýx£ñ8#è·ž6ÁÚ«±ëOx€_
+Í«ÝD«!ÉÇ%¾¥MÕ¿£±?Íh>ç80å¸ÄÛ9t÷e»œTô–g´P²«IÌy à5Ïh1•ì¸CZ…Uv“ðÏ»áÈ€? Š/K`…h¶HœÈX!]؆"§éƒ¸®ºr~ !¥y°Gö¹Ï;ò€²í«®ÊTÀ"$!9³}Î…ˆ¡zXXÛ‹‰k.ä)ÑäÆž“èX.yÊmoT
+7ÊSþ[ äq
+O
+³¯„Ê.x‹ÞŒ£}+ ¢??˜ÿv‚#sÔEnÇÓ_ Nq*› »7¤Ô±£n½çó
+ÿ…ú¢ZH–åñ„²céȬÍ6eG@Qi7÷3[EÊ98]Vw-ýñ¢VòñB„ YÙ2ÞòAÁYÅ™þx•˜•A*ô
+¥«]›—ò„Ê\òÖëãU9tZɶZz8†Í©SŸ>›FOÂd‡Ç›È«³ÎÁWWÍðxk[ÍÈߣþé]•û‡–Š4 ÀÏ_ÓPõ
+M æŸõ]È¿&åU öŸ\"Ñ2oÌo¯ÓšMoÊÄA²[§ã „?~Ó¤‹ÿld+Žú½n‚í¨âó¥ŒtŠ A­S˜m6«×ŽÁ«2]”°Œü
+v‰Gõ=²'aíñØ7µÒabìj/„¾ªäDÊ°nês¶Pbîž›,?É‹êQ]5Œ|¶€iÔ¶€–ÏUø£Ê`°üåŒú{À¢Ð*Egâ]'èAè‚"Áˆïûüá.û)’K= <&í»ÍqW?±›Œ¤w(¸Ú”t1X_ö“®7ÁhQ=2Q—f‚d7˜‚“Ó”:*Íè“ÓlG!š'e£3AÕ0Aç’lWH›n=LyŠïgâq¹ Š,;Còp€¤íAíu¬/wžJtIc×£G@ø$?3yˆ7Æ^ó€n3ªø®–ªú³@Ð]ÓÍ» DNJH0ñð?HÒrqU||¸ºÔσ\šàB®½ô@üÑ'iÈÛ:‡R $/oOªÄhÒñÒây¸d .vJCZLÄô¥ÔsÕ ‘`àD¤Ç=‡¿¡-­¨©~‚¤ø ~÷IB«X™áV?åçá¾ÊˆmÞgH˜Ç.ÅM¢I9Þ…w ©†ZÚ8lñ5
+‘¢Ð¡¬³t¹ÎsÐ'™<ÚËcàG}ž-€LólvÝ2E½qTµ.jÃ…s<‡Ð„gW ™A,ò¨[î`iŠS’ò
+&7b²1sT(Êoº F4âðYB M
+¢¶/³”è'†ÔDiBYÑn(ïPe¨‹«°£Êˆ!¤¶äÚÑ\_„?ººÊžvŠô¨dÍË„@Uè &4Öc뉷†œÇ}•µ¢e—û?#6˜‰Q1#Ísè±åE
+‰ìð!ˆœË-´1‘Oñ)Ð_ÄÓ¶ý[°‡éAQG‡³uQe³Œ)å]5FÇ-MÉ/ð¦â „ ¼©&†äãÕß‚Ìç*ߢtŠèžÅ£Æð9T9uF¬0ç íËg›â*t&Ó
+„›¢¬­fBMTTùÈŒ©žˆ«ˆ²ìÙÒzLC.s¼*Œœ˜X1°î«
+]„m=FÚ‘.€] ·Ìî±3G§/aG
+ÑÒÇGÍvp\ht+ó±9Ð #gaâ¿`üÚbJ8–H'§ó]ã¨Í,·‚ÁëqäÐŒSb¼Ø]l7\ ·E 4ÀïpËu1ú&Ÿ*â\>Š)ëæ¡!ÿ6”áâ;^ëkAUw­^Uè±ÖȆ¬ÉºG}+b㈡T"—ñLÖÀÂz7³µe
+Q•ùááúìüTf«Ø!—ücçqFqÈÿ¬x¿•!xv7øÅ$,
+]?ò—W€YUÉ1‚Ûw ðØ6ÛׯEÔùøR‹£¤‰}ïïWhDÕPÊ=ÊÄc"`›™:A>üq˜G Vþª—ÙÏ
+XŠMÀeõ .ɉhl\6©JDôBI%©ÌWÈ8Ù>ˆ¹FÀv ÊñÞˆÎÀS–°>ì ßþÄ †fö¢7]1ãÂeæ‹
+¤jáá.áOUm((ÜcÞJ0ÝKúˆƒh©ˆ[Nµ›ÉÖÅÿ†W,¹y•Ð"So..´sQ0’x5<®­DÀF9ئÓf‘ø0¶¯î´½r •þAçqL…u¾!6dªœ£ÞÁSî h@;an”#y7ÎòÆœŒûAêÛLÉì°ɨ˜5\c’@ãÉ.ȹ:ÅN–ëæáYJö4HZ.ú,ž(*ϲüMRÄ»H½•ÄÞb˜A´EÆüð¨kj½IY­ðŠÜåœàÆ]<—áˆÛxɱS»Ž£œë~‡ò*F=™Þ½5ý30@|ƒ¦—Фʫ—¨.'U0y»ˆvÚxI/8Yëë>_À%‘ ½Äþ­ ¦¥]P,5#Æ¡u»"R7.É-4ÑÄ NPŽ"=ÞA "¤pÜÎz 9EÆ`°»#² x%öü·’K°qH&^ÏÝ!ý“>o‚¹²èä°JÆ›ÛCŠùÉ×5¯û%ˆ:2®þROþÈ&‹µ€?î°GÅ´býU£h„I`j¯W1•KPé_@æ?·ägÆâ¡c´B‰ìÞ‡ã]÷ÒÚ§dzo0¿œÔ‚òy)h|é®æ‡%r$±È.”^C2k?q;Óî˜ÎÔÂSpø…½êøD'ìCW£{¨TAMd·I,œëá—M9û'Ð@\˜Ž²>¬ÞKÉu¢ôa!êûŒÇï,™"-&½ø
+Y¬[‹¨‡7e´r^’y>\¨â(œÀd§œ<‡³HG Ga å¦Ùþ\±&%\pdz±‚ºÌûÚkuOˆtò ¦ y¶Ì yëÓÿ ±!U&]ëÚtÚÝÄÈ'5‰¨ Ç_%–Q©ÃÊ4qïï¿=]¯C†Ÿ—.òâKlòeŒB‘ö
+G
+!€äªF{…¸¥i>)Kƒ¦Z„&”"=Õ ¡hÐ5`/`Uû…<^ =€‰A(A…¶Ž™×å¼Xè8¨I÷XqÞzM4ð<Zô£Tê%Ю‡¼QÁ0ö¹JÞÑcÍk°R‰˜”'—sM¿È—­geW>JAÒí׫ Ûtíl_ÍãOYñ“îD.åQÚ<Š
+TXH¤§J¸Ÿ<­ ‰R¹}Ý7ÙvÞ$òë÷¤u´,Î5š"%–{,_^m
+¡¤qmÛÁ³±h¨LlÁÛ6ɶù°¤r›÷+÷'›YRh¸%D‰— Zäã6•µŽ\Õ¯EôDb ØAjƒC¤
+É(•mb•q¼*ZkY²ùfC á‡ªxˆ_„“ÛÛO_
+žý P“6Â`¸:®ÎÒëàw1PÐb}^[„¼€Ò<·4Ž|’ÕdxO¢Ì¸¯|1y{÷f\N1'‡t!"ŠYÂàЋíå—"/‘±g÷t|†µ³5GÆÝcÉT³ÊùíëÚé$/ÕüÖ}úöŽ’|µßCŸ*¦pJq¸YÁ2td½ÛŠËleè Ÿ%q‡Ÿ#åÀÿgŠ…¾±-ŽÂpÔù¬g&PHžyšX¦?šJz9:ÎøÅ÷U®˜¼ü—»€=jñgdXá+h5M-î à¤.Ã
+ ­{™¦A¹µ^“XA Äzâq£ÈbÅØàáß¡» >áJQRð\
+’Z9Yd¼ŠÑó†
+BÇ"ä;-DÞÔЬéƒ …´kâjdŒ‡
+y `ÚH<G7ñQ¶ó´KócÇìñþ|Ú i‚Ì[ÆM\»ÇhÙt#²rX2- ÜÝð…œ£P\¬ÐJ{…ÁÀ$U,©¸û a@¥]2½\>•^ªÍئäl²±·%©ÝoÛIÞêåì µarz{!oÉp±ô $zÍŠKÏ,É®¤¿y 캡®—!•Š-)Ùvº*”½+Fìÿe¾ŸÓeƒø¤­ ¸a˜$'ΡªîÛlÄ3ï²kí2˜7AgÏ?Ù<9´C pIl ³—D´ml:¢Iô€¥Ä‡†2ÿhX´º*•kkP–>hcØEG]Ð.Ž9•<ækmjdÔÁ`”Yöõ¢†¥>YŠÍ{h~öŽÀ}p
+NKÔJƒ³Ý.{úz#£Ož™ÿüãÇoÿåÿñ/i- É¢j ½‹ËEüT ˜žr ™J°ïí¡`Ê¥dÕô{îZL ~ô­t¾æô¥VdKDaÿᦫAÔ‹‹ä›£Þ%Ir&æ¡<(ª¡BOiÓÐïÿ š‡$t/ýÍ™Z!Lm´3Õoªò}ìkÂÌí Är²ˆÅP7«Lë, «z:.£âRð7@é!(ìQVŠ_…‚£1= 5ý*EŒÈëÒä¬ÄÃý¸ ÓÄÓZ„`¥4ºÇL¦tڜº¸ç9d|ÈÒàÎúc;­",}¨øi˜0[R,zõV ȶ¡µäY1p~U?7Ëñ/‚’„#Лš7ª~ ¹ÐP 4>!c¤ÝT5ŠB'T>ç–CÄžˆ—ÎBÞo‡c”ŒÍëäÚ"øì‡¬^ѱÌfï=ò52§•]XäW
+Z˜¥dØ~yå_U܃º0 à93Ô1}†‰@óø—˜
+D¥^™‡º‹½œ¥Öu‰±áKf}YçTæý §JªD2“EHË!‘ ]Š®…BæQ  n• $°ßI cÛ“O”ÂòVU$‡\ €k­(1wt),ÌzR…`YmXû¦{ ÁöjyQ¹ž Oà…ìo›‚æd‚Ç,ˆîx”ºÏÆw¥‚VB Á­:¢HÐpLh²Ä½K¶P'«Â
+ háÏ(B©‹ÁXÌ}ÓsPaÁ¥L‹Ÿc¨M~ÊñîŠ[’ø2VhUTÈzñ…±"Bà]Ô_xlCVêBáŽvjÀ ©{p.›™üIŽ,°ËZ6Aà0[XŠ€+ÍCÈ TS’H#­ã9 h’ƒ §f;ú²ô¤R}  AIMœ ‹6µ*û™ò˜›]ŠxWÊy­O—Ed5…€Ù‰ UìæV
+Œ>«"Ögh«‡€=ˆ6ZÞÊî‚jÉÒöü¡yò3‹ š b@ÍàL„4žé\ãá\.ZDLk½ñ æú÷PV”¡i–A_/‚æpGCK@—á0 uŒ”õNB茺a@Ä
+å¡ÁÖáUÑPi/U#ó'‚˜+aqb&+RM¢ê’d úsï±ã4&Ó
+qÎá”»À$_ì^8Psbø¥P•——ërNY½\˜Èö0$x½å·£g«X\üŠZœõôy ¢<6ªÊõ5ÉC1?( ¨{¾ÃR@œšÖ¸¬±\j7­4Þ ’Îa „—ˆuÝÆxp#àç°ñ^ÜÇ;eª<Ú±
+€Ã÷-¾J DȘîXcÌ}_nY¿³>`L[Âw¦lßõTÆç ¯wjí-É,¢e!i^¶)‹£Pp*j AÔ ¯Y%+ru±ÝÔ{!̤¢;r‡é‚ˆ`ÁZªIÆ!BrZäe;ÄJžØ6ÑËt9³Æˆb Yò–(U¨HÒ)ë$C3d–ÎNB$óL©<¾2³BtSÂGPv>…ê铢텉æï+7a&¤p…— }Ý1§ËÌ%D²Ô(î¨Mg@sAØ9ynGª%$5^ÎË(×–XÏN£!K\•*L#£Ð«S$9+R÷ £!IyºÊ8Íȹ$Ù€ºó)p/¨€-˜1¨Mª©Ì4P
+˜Äà °|;„ Ð**âƒÒÚÙ0œ’,dUF¬QCXì OG¯Åj y§­P(o'ᴿγ÷ƒÃü¢{1›ÍµÊýœÂb¡“ í®OɆÌØŽË”˜ÐÒÞ&¿!´±à$XôW± ]jŸQ.â8²›|á©š@† Kq¡¿F¾\auaÎ>¶þ8¬{5®F·‘xV;yÕ?Å#Ô9íô;#³Ë®á8·1,Ú1¾iˆ__š&`y]ðJÅ&
+‚ u Äxßòi <¥ÊÝÏa-»©ãr ù;ÔMJ¬™üG>„è*ãU“Þ
+¤˜ÏªïtPO÷Œ1ߟ…h™!_˜æ=àtÂsñJö—ö6¤ùÁb'šˆH+'g@Rç#fðò²W5¼¿_wÈü×9)u{ü‹PvÚ”®pœBKY9|aGŒ)} 
+ræC.¥ Å$`œPâOA
+á—•X[&MÐD1_=¹F&¨€M©à`–R`
+ŒYöþûÜ °2C(Á€-W…Hf|î=AЀ%ŠÆ¯·CƸΓ—xÆÏjî, >û(BŠr?l†Ü,u_uÂ:?ª½ðH?‡qIIÈQvEn,ïùs †ì†µ ä#ªÄ=èó'åRe“WVPHVHu_[³ IÇà©^×çi· _aУ™X7øB‘¶è±ÄÃE Îüc!K±Âñݽ†hbÆ@¹–Žg;Ó Æc?˜‰žì91³•i-¡µB€ Yéí|6¾ñÂä0wzIp¦€à`VªË¿Ü) ÚÌb I\@u%Õ È­ù‚<,ÏœyZb/ÿh©Éõ\u¢ù鵤í€-ÁÏ!4dÉe¶S*¦e„Áó×ðá$@µòÙöÜcôMîÍÆJyV¹lÉN½´(A™lC^‰0æ.Z奂ÁŽ}þ¾/5Ê‹ßåÅBÚIÎÖbI»xBÏüýMPâBAÉjï
+IÖ3@Ä5È`êW`ST‚ú 9à]Úí±^*<NþR©î‚Ók;#ÆÔYX ÀÀ )°ît±©ˆ3'! KÑ&¥-â‚é/!º¨ª¨š0>²Çç
+‚ž_÷X2ƒ è¤G2Ÿ8ûƒŒïÂ0FüeTá·8Ð2ICÛ ¯K’û1œ†d•±ƒF¡Þà´P.|‡¸À¼üÎ+ÞntÞÜ´Ç’kÁéy?'3¨Y—«—¦Þ ¾Ñþ5ÙÚAU0ËÃu:GkèäÈ”‰4qÒêUŠ ,™±¶ÎñbgÄLŸƒÒCbX*õèç5‚b™H8=HÂø4¯ôq
+¹
+#ìŽ1ù£†v‰uR;81¥´E .[%„q§YÖ8Î+eÃf;:8wå³Ò9f­ÀV?Þ‚ M,`FþåW"ç(ˆÉ' “†ùeX3ÔR1º 1¡#äФþ±Ds &82T}ZmÌ]…ôP¾K»Ç¶ð,®ÑÔ &œ¿DšH=ù•!Ñ+½ÌX[é r
+éâV™ðžßÞ(ä* #ÏÁ#Í<¥ñn÷J̓™Ä´Š"ƒ ÏTµrº¿þv öô¡!1²WD¬žó’!aúk‡´Ç1´ÿ©ø0b¹XÖ•dD.Wšwž÷Pæ{ÉšWßÅÂê±´`]+ÏÓá}Œ¾Ë‰UÕQÑ“5ž{:ùK€{Àc­G§ ñ
+ñš×Ìh„Á0žtÎa_­ZX rÐ (£+ê`Ú<ˆFÕÖƒ¬UíÇA°+¢^‘þÞÜ°&{‹ùü»]Ïâ) ©*¥û†£&GÍdZÉï7õ
+Hãy]@¿“?}Pȃ oƵ‡ ô‰êéaæ
+Î0 ™Pˆn[r>´ûôRå=t`ä<ĦÞXE¦§'Y_ˆô íÔ“ƒƒŠ‰+_+ž4šF«f̉!À(«sØØ Ð8B  YB”J²¯ÂIqFÔV ™aO¨Ú¾ò%ès~óUX.-Š4¡Ê=‡Ùk·²0
+ÀËM½öËÀ*¶ÖÆß‚ ]ú*x`‘HAžmà
+‚Óœ(TÚ£ ¤ìš%)*1É[J-fˆ|ýÔž"'¾¥\P‘µN 14ÊŽ|=ªPåPÕW»ù›¡
+Š·Ò^÷Á?0C÷ú|ÕÉ)’˜£YREX¿Â~)±H6k‘±Yr·³Ê ’;4ÓŒ¬ž#º*R“þWëÐöÑ“UÂÀ£Ç¸§ëz-9lÔf ጠfz °éœ-s…·¢Ÿ¯Eƒº õî2ª’˜|$?s®rÒ—8ðDJX9lŒ$?gžwš!*á›EƒâtµhõÇt´Ì<)V’"ö?®}Â5(\Œâ¸Mè’
+}'¡\èBwÑëðj¸ÙAt°ÞD€Jß$(Š‘¯ Ÿ}
+"x[™?¯Þ–Iy+Eq•=Ð&¬Wæ–vH.64ð½^Š²:nx%%ÖéSÂOÁXa »klü gCË•„tv”ß&žeF Œ<j›E[C§LÃ!Zä;[æiÆl×> 9Öbž¡•;½Ü‡ññæ¡ÂJºm:"yê?õiÜè…Yd@±´Œ Ø)¯rTל%畽ñ/î’Šúµ^Û*)\~râ|ΣfÎE˜g[G¹Ý]â=];ßÝÉÓ’cn—ð.÷Åß”ð82Å5|À<ê¥
+ð¡Ãã6æVr§{_²º ]þc¼ŠO4y¯ìkG¯­Ê©ÅØŸ£Ò8˜f¢ïg×Ø“ñÖý|ŒÉôD…y¾y‹:#ÞJÂïNÙ¯äY§~ˆs–,6»w£ÙqðÚós‡…8Π/pgGrÉDW¼#¯¹“S¡¢ÿŠ$&³Äý Fr°¹ó¤«‘¾à ™uÉ\2ÔSmð…Üîý^_±¬1’QÄÁŸ½vÐq¿TŒEÅ­Å4ÓÎóý3
+³ñ¸н·³|¯ùù›š$F+Vïw™>bµLAü
+Ò¢ix¿~ÄôÀ¯mÜöþªeŠºÉ'Õ‹¤MÕ–c¶~ç;=6«Ý{ˆôZr‡`b·£$ɤµ»Ýš%
+çI Í3Ã{_E!Ò¼ÄÆQWœéÉìAÐrµà@ivo>êò¿\~êoCéùë³·‰Œ¹°zqü/5?G¸)õáÍvŸSOh ãµÔ·)ÓýÅî§"¿·£-îWý ^Dxn0ˆ="€}Û_š“’åñݸóUr×d¶Srß\ZœÙÑ·aDl§¹äyTœœ˜çÐwY=$~K¶“/ÿÕëÆ(¶ö üKZºf‹5On¸Vv+Â-WQu+wA9 é•Fr ¼Ì7ø"ÜB]m†«_£`s¨£ÃWP²4O*u5%cžÓ¼†0P-;t¾q6æXïxßç:éOl>÷ñF_ž%3ÚFm<ܵnh¼˜ÒöÒ½ñnâBüÿ,£€;ßý’÷(Ó)^Úúkñ:¹(¬NŒË+nȃÉÛ*¯÷
+g¶’ØŠ$u
+žÚV-½jy0ÌelEHÕÔ¡D¥¿-–¾½ìÂ8
+Ô¨}2Ñ]Ÿ ØJ˜¡„ ò1oLåÊeÉ’Wݼ ¤¿fÒ¡ÑçöÙû¾|çEé•X¢=†J°zu 1º„ýÆ(e[ŽÁŒ`E±åy•‡‹ƒíâ_nfp軟ïø)Ëäœo>è{ÿl–þæ©öÿþÕ¥6£*–”ÞÍE>j{H;á-¾êµ¨Dαê^JŽDXÙFVhÜ®\ìˆy¹F ÁÚÄûs‘¹B’`kë5rÄrTlµ>MÓà\¶Ê-ß‘“î3Æ|®ïw&÷
+ÔÄj—áðŽç+.Ÿ_É›}gø$`ÛýÜ–Û=¾m[è;мßÜB?÷UTpghãò/Ù3;:áw—õ7k×æJ5[`ÉH/ïÍËjZ_°>GE¥ÆžðLG@ÿ¶àæÄ‘@,ëz´W¢8ˆØ•0Ò³õó¨ á渽.NúN[YÀÊ^(j¿)ù‚¾¤Ï-LyŽ[ ïz-RýΔ¯E´Z
+·ˆô#ƦH 9»¾§gþš¬hëFÜ4¥ÇÎ=\¸F–XòG½G+ò!>K¾œ’è³}Œ/Ù&.D‰M:¢óDþ¿^ù/ÓŽâ1c1\i{,Ú±*)±éñDçq>œq.ñ܇
+þT.öv˜ýK‹·¯ÖÚÅAþ¤¤eÑyÛgÏùwŽð(ËŒ–çQ?èt‹)æ­ !ÙâqÌî’WúIƒ~tš~¨[®¥‡,/k–Ïà¤Z4/–p”øÑ‚Œ‡¶¥YØ„!—#žI¡…ãŒ)w?
+g‰¨vŸô‰‡œì²Ê;ßY­ÄÚÃØcAÌÌZñõo%~dóŸ5¶C¶ˆqòô~T´‹òž ¹fÁä|(­9Üm‰õ/è`8ÆôEAR\뇒@K«2ï1ªIQ°_C<KÍàB+zž8ƒ‚#-.óþùzË»Ë@ºQ퀗Èwž}â[ k }fÚAṨy‰q”›ÁìÎñ÷RÒŽ2%a%äã?óïàz·‘râ^FII«žo]³T[â ÞǨ¶¸äÅŽ»áUûþ–,5 nµŽ¢=€8kRʯي“Ťâ?ÐéŒ*””›](=¨M('³)1%x4x#.^Z«Ò±:%ÀúuÃ9!‡s¼jB¤W!Q…9ÛÎÆnï\ì®vúÓå˜Wü`ô¿qÿîßòmz™ç•þ²ƒlÅÜÚrn!ã¹¹ô$7¬×¤([Â!\yÁõd¸pIß¿z2F(Âtž£Œ5â ¸lø±8 5š4Ž"XhƼùNM¦°ÜqÕQRÒ±ƒý2æ3³Fõ*e\¦ÆL[œ¦éNªI¥Ø×—-öÙ‹Ôp[NkäTû»-â.¥—”Èg[]+aªts=@_x¨éÚx·£0ÌzeÏËðƒ;æ1dð†#ßiTÉ)”2ôw3A+wmí(ÊSNh¹{‰…½«†ómkìÖàì6„KjëE»ù_ñÜæo°E|jœPÛøÒeO¢
+뱦GÖò ”²„šî¨Ïý\í‡R4±QŠ+ªh(u^îÚYG{ókü4†ædPu+ JÑü‚(—a^õE§íFñÄîOlã‡ÓÎÇÁ¹Çq1¼î Ò‘ –¨—@5®Ã¸>ÕºÎö([ËPý¬ ÔX:s‚üºÝSÒÙÛÖ]ƒrèω¤¥•{.w´;j|ûJ&g'F‰« M=ôË%@¤ŸÎGMŠh"¾òˆ²O¿cº° †ù³ãCI– ùÀåþpTÈNuu'y­¸â5«[$þ=ù®—¼©Š%)èÚ~Ð{Md¯$¾ùW H ˆ¥YÎJâßô£è÷Ô?»±t£Ãžy5¥ZQF³Êdp4xÙ‰öl3S›õaöàac†ÒkÝFX«xz Rá*Ž,”>˜7¢SØd”`WU.y7Qà†â€\¢LKºÿ{UŽ÷ïhÌŽës›xLÜŒà›_7݇3öý|E´®xÚØËNÕgˆwnñê£9³ÓzP¤G¼uâ •¦‚—áyÒ½^SUÐ Y¬¶Gôµ«-1òvˆ©<þs"…f<šÔyî#ïÊûpe®]½¼KÔŠ0<:¹Ž“`o lµeÓÅŸ¸ñÚ:“DD—à_Öö”¬tÀ ’SqíHÍ÷V>”¼šÚiÝ»xy,¶ô£^o¾Ä]ÏôUkäÚ0×±U¥V±ËM°ëHÀ±/q}MUëÎ>Ãd„ßX<›u¬]ããi ¾yˆ<ƒ ýaø¼{€ÛD‡n(Ê×}óš9‡·Îwd¸º÷©W/ »oaÈ­k¼Øè6zFúk/+ože(™ÕÔv8ÖQ´ØO¿z3R¾¶nS»À›¶!$(]»×
+ŸØˆ¸èkVÒµÙ>è©a$¯M¿cwöt+3®S΀Ɋµ…” uñïQž¯h¯ r®Aÿ<x\䆷Z,8Ù;“Å{J,7Uóô-½÷/Äeî,Û˜C@v¬ž]Ÿ×yy‰?ÝÇ4K†Y»Õšè³Š+aø(åÐç“ÑcÍ]ê(9Ù:ñ±ÖæCQ6£X0»&—QRŽä0?¹Ï2Õ²5Ž÷ŠÍ”vÿžIÌKÍï5ìz"äuõTõ^ÁéHb"i}¨à>äÉòë#wQŠÂAÎóìÿÐ]9
+ùú(ÂëW„.ŸÅd
+–¥O
+H‰”—KŽ7DO0wè ¸@&?™\—º…¯¤ûoý‚d R7j/dÈ­(2¿ÁnÅÞ®ÖSŽµ—aõñWmùò4RËQj |~x¿j.µTs+ÍËô}‚’åZ«•È}lPŒÑjªÆUý*#EÏÞ¬µ´O)%«=¬¥6!QJ1•ë„°+ÕîÑZéVëãŸ#&I$kµz>¥Ü°ˆ‘Ýψ–=·f–¢Œ•ô3¦]‰ïKj}DÔ•ó+†BQ,Ë%æMµ÷j-Ò±!­ë™Z ,’œ³Yu[
+˜æÎÍ#îa-+”Z ‚úXçÇZûaœçM RПøk'Rá¿’çF4Ðo•é)(ý÷fhÇÇ¿BþÑHä m(cù℃x ^9nˆÊ0P‚Þé$¢Ñ¡Ý×xçMÞ{Æ×$È*võÚ$‰Ö‘¼yL@·hj§yw+qD¬Yí ¶W2䎾Â÷M¥N,(eA_N XSóaM29çÓ¯!²à³¡à7¨Êy5mÈ,2ëÛK` ‚û”O1gŒ„ ¸xœŽVdgCh5i#¯ÑwêZé&±…W7((pÆêY+«‚µ2)X°šêMô´¤ª8fVÏÐÔ0D†Áá,ÉžWU'Eò§ô·`˜¨¤:ã¡sJ°©Ì+^À7Âð3°6¡ÏÉa²ØyÖN#ð
+™71|Mþ²3¾qƒ†¦[uªe(èTï`˜¢¡>m. {¢f?™Ád®ÃËV½žÃÌ4,‰÷ÑOYáC8d0J=/-WAé–{f”ªßSÑÚ0›ÀÉ&ø3PÍ5·÷90›ªÎÝ‚¯.Ô»ÉÄn·Š¬Rq6¨ì¤žÄHÆm jÞÆ$Š” ÓW²‰[±©Qïs¶)á!éÀ‡ï{ð¸U“¡Ñ5Mœ?0íʼnz›¸…•f
+í¾.òRa]| Üç`Zd duŽúƒuÇ=¦ÉLòuªpпïJ€Xd^U#Á4MR\ÏÇH˜±j”&ßÕ£bKY§>!ô±bLjå6ç"‡êþºGÏ33¼égùÉ<Dæ¡SPvÖ­Ó'»I ‚®X¯!!$ ü/asî#2T×ð>y· FÖ$ºLÌ>8–Ȩ¡Gsé»<ÓUâš.»Î$ó/ˆš>a~l_5ß?U~|º ´’h#r¸|CÌ¥ÿËŸ>ÏAÅ8JG¯Å*Œ>b=£C|gˆK^‡n—}Õ+¿¯õÀ÷¯¤œI
+yÜn‰Jðjí*q>×…Òk†CóÏ1G9™‰Ax?_ ¿½:Ôpj•5Làm‰õ»§é{‚Ì«szEñó9¼äâIsÜþ³,Š`Fm"ØÉnaØîxÃBì›B„¯˜Ü[›«ô
+@ª'†X=ë7ԡø©ÎsºO›‘Èð M‚³šJŠNÐ[ƒ VþŠbo:kŸ¥dï;)“5•Mr⣿fõ«¯ ÅqË@˜˜›Ë˜s7]E c×/éA†¨ñ"ØI¯êýf€ºŒzj@&§dÌ*¯ ô~‘@)*DÔyÑlP=$Ûµ®D£€PÞç$Jcê]F<í'Õ,šÿ@05VDD#WºO
+ÙeH1»IN¤ÙõÖÈw<RÇžØR1ÿËÀ‚¨Ñ+ ïÚÑ¢±ÔC1JÚç°Zr ÿ4Ï™m'§Ñvö„'+·/Þ¡”õB ï†ÿŠX´£•† i>`Ä)ücÐOö=îP }q
+elSÉ+ÛÈÄŠüí² H™A¯¨ÓýæÂ_©UH<oA†XÖƒäê.  ±f=Ä&ï4µŠeãxN±Q±Ð|ÃЉÄúm.vÌÐá"–‚g¬ÃØ>Wy҇ȥŽ#ï´¹vt†x³ŽŒœü
+»/*hΓ¥ ,‚Ôƒ
+Õ}¡ð£ˆdú„ €aEä¶s:û¦§9žóô=Cž^¤_€p+ “^å"‡MììÒà…‘JÛg*Â\aÑ‚±â
+"+b`oöâ™Ì2=æ˱ZèSùãõ’$6nôºƒN  ×ã¥ná¯Æ÷ßú%
+ê—Šø–re­‚´øŽÚž"`<¸ÅfèÃù[8C×öY" ¥Øâm”€•)ÄyûòKÉÔhf ŽX¦úþ–c¢î1,«„yÁBcšwÈcT^ë᳉+××ó¼
+.Zˆ'Ö:2ME±«e]³RÒ’9ô5]ã¥Ï ÄoJŠ«kÇzÂį\Y½¦Lߊ³ô´5Î’iÖé¹^TZueç×EþcÇÎ Nèdíìêð£‘àÛ!wòÀärngF åUÑ`Ô l}‘ÿG…øPô÷WE•]±í×þª4îÃ~`2«Ò¿,6–,åI+'˜ã· Pîçéet™3úiu¬’)«¤Ä뱆H÷µD—9øB\'Y_ËLîÛ³z ^4 æIŒHhÿ
+Ǭ‰J ;Üð%Å$ÄQ\p¬£îr#NŒx­š+¶·ŽT2fÅ<'ØÐÉò £’—…!au´ÏÅá7Æ¢<oúmÿúvn0À>»#ÛÙî—¢¿G¿rÅew×XEí ÿ}ÿž=ͨn»^Z“b…C‹û¦ÿä÷Ø*§{÷{Ql!í!îöÅw˜67.F·ÞKŽ)q»p¯‡,¢Á¾PÃ~=üs‘lY“álJ¶¡':}–å:ÏBxîlÉsi¥·ÉÅ èÏô÷[\±»î}ñµñAˆfb+Ö„‚8[yrÜ×Ä“©
+|^ñeë¤CgjŒ˜¤™Ïo ‚8ï8W‡ß1,+/ )‘R :[ÛŸkÖƒì—þúöV„:0fKB+çÀ( _oتÁÀÑž$Úüô°"™•û ñTn]¢bAÏܙ췔Dbø²auž¯˜µg#ó~®§S«O|>‰¶kpI<=êì ‰.@ Ÿ+si%ƒsˆ(!-Z·‰:ñ q¼mUN®”E½¾˜uŸÄ/õ«ç;fM®¼ûX†X‰Úãª9Ñd:Êr=tñ¡d€´óVmĸm?Ÿ"ÑŽÒ9alZØx§ŠBææ) רL ç{q¤þÞK{)G}Ú«Ïß1]Nã‚¿úßÛüþ*ÝÚug
+ã`£w_p¡‡Qm¯EA¨î^!•†3Ömà)öÈ÷7 +1 p£|¥Ï£(,ÒÞlS/¿|e‰cò–|Ç ì~çêë¨ÆˆY]Ð9FÉQæàN ¾†€Óv¢Á¥ÕyÔÞ1+Nd{(Cº2ÎF~K¾CˆzRG¨»<¾£fx:6Jú`ÉözFµÅv-Cºó¨‹0
+¶†î°…?ˆgB|íû;WØ!NK[î/J®AìŽ)®£þç„ž¶ÐùCîj_룒ÃHÞ1Ù{è©ä—?œ ™×K‘’x½8_p«¿ ÂÿjÁÅî³ÅÜÛcž>¨maW’ ó;÷u¿™°ò#RÖaÈ¿Ÿï$ôš4o¾s4MLŸm;ÚZòÆ{qjæÀ8J¸:í£=t¥¾Ýy¤Ãü-û™ï 
+G™Ù-$xÊâð·Ù@G9míWN&È×<ê¼C4›ìc>b§ÊÎxˆmpךl°¯ÛÔ‹g3Ë«]ý‹»~ŽŽ…ÁæQŸ‹6$É£Ï+o‘í»‘Æ³ÁEèÝp±Ù¥‹,íˆT×î÷j à‚¿Áš+^0kaJçˆàD 91¾º¸È˜5š Ö0NÀÜb4,ë"o7«Ði« —ÑÔôô:—Þ„ï-%î.ʧ±¢×’’Ý`îí¦nyU k°j-¾ìZ+È%QÔšr/òþÀÌj”ƒÍžà6(un fnc©u[–HžÙ_)µF8rÞ-©äuMùNò«1?_‹v(½Ñ©Ea¦7x)" h®à>‰ƒº"Æ-À%äø¶æqƒÞÀ´­Ï’á0< µÝ‹15å½W‘ݳ›ÔxÌË"â ÚgŽY’¡Û:µ¦|"øß_ªkjD»Kr¨ÀÍÝœQôƒËJI¯£§n¯‡£™ñ+S*ŒŠ)j|®ÐÉÉ™GrJ®,¢­Šv[ÜMc†ní‡æ‰¥=­q äópŽ«°èNö3\u¿”L½i<?1~üþ¾€êŽ­qÊ"ÄG™ïWO"*œ”?—ÿRt1Á×äl‘pOŽ
+­Š:ì6¾ÚpD¹‡{Än•´øK5¿¹1Ÿ,†"Ñ+²%M3ˆÂx3+píëÆ”Xd‰~ßï~1*//žé×Rë¿ÁÙÞŸ‡qd 3—ó“%‚Gu=§¯ºÂ…—ðÆË%GŒ‰‚'ÐtƒÅ?ך¥oa`Vöý·‘E6Üj
+@6¬vv+™ù^¼´OB´3r"7ˆóP6t–Õ™Oѧeùûê222é²ì…ú¥¡™e„ÆÈ0kÔëTݘFì;tñõ*‘H´Í ú´ãøÒ6»9ýªÇŽté‘ÑKH5ÚÄS¨¢5¸…n£y’>Å»Ôï«HnÜ“ºE„Qo)¯Š²îç‘m¢„ž.R:þNÖ ¢É]á˜ukú—™ð÷2 Î0h8Äö®ïL -öWƒ(Ywš€¹Ž§”KɈ~~ÊkÊ”±
+}A²ûþô\k×»þ¹‹”ó‡K%¥Ë(m`0ò~²ÊYJç6Þ‹¨L}6"|;ðŒ,ÒTê¾PƒÝ£Ö)BJqÌrÄ‘‡Í!±6ì«œ$ÃÊ«€¹‡VD”ãB1Óu©x÷áçiÿ9•Ÿ°[ôqàÂõwæ®k¦’”q©ˆè–\‡–µz+¢æÃH$òÜåtŠaz.4äÆ@³|.êS¨
+Åö¹XsA«'VÑɬ™»‡L±ÅUsnn‰)¿º]àÚ÷þ’¢ÄÞ/xÙxFðêZÍëRôb¯W…›
+g‡§”„4aÌÁ¢ÄœŠLˆÝçÁ¦@|žò:Ž®cÃSë#v\ o'¤üEùòêÞ9¥%–åáÀ8¥H³;H˜¯*àÛ¾¡Ì[‡ a&nåL wVª.´£Ȇ›Zjº­”¶¹&^Á‹ÊÖ믫€u-¦Q
+LTŸc´Äarö:ö•Ó²"GBJ)ñ—ɳAvðš‹_ÔÒñm‚½ êÞV”¥ˆjFúN Òü ß°òœÇ;‘Ä<Ä9o‰ÈÔáþm—d$$B¥ú¾ª£ÏR´ÞÉÐU
+°F/!Áž¶ äÕO½öz»¨|SÅbÆ>ÿ98ÉÞ¶7r¡x à¤þ‹ƒÃGûÖ|_õ¥¨¾Þº£±Â^&sˆËó{‡öÉ!`¯ÚwÝîÏÑ<>Á4
+e<—’¸HþVÈÚõv¢Æ `uåºýU¹ZÈbk¿Ïcu¹ÔÞ0@
+F
+|®¾ áfÎ¥Þ´˜£F8 .6ÿá¶ÈR:̹ZF42L\¸GßÓ(G`3\_™Hú×Áª€_¢)OJ~;žr^,ºÇ†rªuЙ
+na‹:²C¶ßÑuú˜Ë¦¶&w ‹ªšÏ‰{Àÿ‘ʾ_…ÿpö¶—(©\
+;ÃOW߯j„ ½”À‹¼54И‡!ËC”$<+Ž\i¨k8œ» CZBŸ»)Gc¼2dV*F~”ú…!£„뮲~= ü7ƒß+xêÕ?¬H;oú©ˆ|N:Šµ½oBY{f!§MeHäZ¬bRã»9*þõÆŠ
+#'e«­Ó>¸˜í¬óÐê'µðvç{!
+™@𙥮÷w ËérÌPk0䇔ÞÍ۬ƒ̉QÛ£ÙQ˜­‚(Ï£vaIÁ&QŸ|Gc’ ¯\{Ê5ºÓ­¥QÆAJqß4cÉ•GIÆ!q]ÿAFÐM1¯QdWЊ£ )eé¥ô·MG¿„‰Í²”˜’t¹OÓ_Dˆ<`‰^Ž£l“µu:ŒÊG£:›d<‹ ÞïÏ0x¬W¶·\%Ì¢}(™
+˜'½{Ô=Ú j>]†“¾BH5‰´<>ê“õŒbM·£¨=è=–Þ>| V ÖsT„à}¡E2ú¤Êšàyyæ9/çWµ\넺u©ÿµ„{ÉSw&a{wÛJ½Ø:ë0n¬ÄZ¡7"8Üâ‹If.©Í©# Ç™ôAf•Nßp<ˆëDiæ“üÒæm(¢îQÎUd}b
+9’()kñ•oè„8ÃÎa<’òãÑYüfч㞳î§k'ëZ+JÍ$­cm>=>¦êJSíkÙîKjbì™ÔJ݈ò2(Î0´Õqjé¾”LQûxûéP’n9 }纻Ÿ$KÜüÆ=CØD%'§ùÔ:Žâu[[+¢»±Lwv(tÉuÍg²ǏָÅÂÀDMëæoyƒô³Y›G¸Çk®•±MK»Ö&>Ú`jêù ÌltˆßõhpJŒˆãÈ—òøº4êC©kiwœÛ³ãTÇyFQ#U¥ w=â¢;fXµ÷n£*óÊi •M€¬öVÉ–ÛBsoÜ-ŠsD=7!
+úu’´_û¯dø¡(%>˜ø(ç›ý¶%Ùo{(éÆ>&%4‡‰Ëñæ;¢”Š`yùu›Ë_á &%%è ôÆýÿ:Êj'®äf•,Y´«ÝZâxÃ…aËXš4(At«š›ëHéM~˜'ÂïÔ‰siªZØû9ó.êä–hP¾œÀ^áo4ÿ–5®æò²ÂºÍéfAoqôï"åÞg£$MÁ¯§È&LØ?Ñ£e £™*Ö»„Oˆ1,#¥Å¾Æ£ÂÒô9L"n]Ó繡þX6È>fO^<’T®a?‡ÜòÍÿL² ǵ7%ɳ&,m1ÚÿùòÇ¿¾,_ÿñï/ü÷ ?ÁÐ4mîfzVµ›ƒ<gûú×(²¶Äª;jP‘lhHŸD§
+ö¶€wÁagäÿ¥äg Æ:¶üýÍwê•ã¾Ë6¯ (´ÞˆyÐ^’W»ÐG<_UmÍþ·6Úi¤à’$°Ž¢ÎÎU]ø¶‡X{)óF‰Å½"¶lV¯@K:¶øÃ9Ïc§0Î1pñZô0r_(ñb‹Iúí¢OÛ÷çï¬èK …ÙÇèQ²F/{r¡Øø×›¢zÄùïY¡ëkJr§°`üý(9i25²D­Ž*G Jþ%À²àÄr¥‘?ûQf¶Bž·ù«E|V2RÇQ"#*ÝÛ5^#zñŠÇ6
+6™ƒbF§?VŒs0/kïu9Ÿ¿¤{R$ó³>ÞÄOü|ÍJ¬×›E›Æ
+½‡G½BØÂã6¢p H¼îb^*{]ÞT|˜Ó÷¹uÇÉWkÄãiÍÿÈÁÑK@ÐÞ0Áç’—:ê,þö>,êñmÝøjvŽåî*J[´†ñ3kïýëMÑÎ¥tWÙÛ”¸r¹µQ§à‡5®Û|Ä«ƒN¬£díq†BÏ™˜"œ¼$¢.ˬY@ï‚齆ÇÊ
+íÝg²ä(Ùêd?M%[2ðï×(!Y<Bѣͣ¬L™{hSûZ+À”—սǙܠ¬·¢SØ¿á ¬þsI%ÿ&!4Ö}õZÄ]dV¥óCÕbÒT) £eŒÌzõ’CÌ †õX怫æÖe%`K\ó6­^L‹4]}æ(Kžëÿ­ÞG7XãˆHƒÄ…%ÙýåVŸŠú6„N¬Ä—y›²H:Û\Z1®œ´Í-Ž%¯J‘ý rvæ_ŲM ²È¸ÿev[ÿLL4vÃç4D–
+s;’ Ö8Žd~å"Çä„eÐã'Yuö~ÆHRѱNv]·¡j°Ñ‡‚ ­éV¶r<|KǶ4T¹Üw=b8¬è~CX4$´ÞN³%›µw&r?~ !7ñ5& QË2
+â¾_ƒ6#.3Lý¬9€ÆÜ üž "a^ĹmoùÃ8,¹®ÊJú’ßèâG ÙD¶†­Çˆ}^AHd’5z,:‡¨oŒ#‘†ý%Ÿ±¹›)ÁWÂÁº„`MLÔwõ˜
+–ƒE{=ËÀFQ,´?/ÿ\ŠSÚ]þòëà 'Ž¾Ü”m¦ cŦŽ~—?æF$%Ô§J-ñÇl$õ‰Ø‘Úð’ÌbJT‚½‹“˜^„ ·SWl/dΡäíÖ%±jÆDÏ~äþ•ü˜ì6}ÊGÒ”$–²˜
+ü|#ûµ¾äÚ$™{Û¼™|R¢–yZŠGáð厇”t?Ši›ŠˆÒ…ú¾¡Oâ6ýG2M=ŸÛ°*K¯côIJ€’þ}ŽëúuÁ²ð‹Œ4£“FÐßÇ=@Ÿ?¹Ó/¿zuZ#9 àýÍÕ;ŠDb…äl¬Ë͈ËÈ<àPÍ_×Ã^@Ÿ?¹Ñ¾8²PfÊÃÔÞñæâOj‹ùå'?¸oU}"!MÂÞ±,?
+qc¼·†vú!C]LÙ¬Ž|áVaú€p1]‹²X‡Q&„°-¦ &ba*Àwš8w@ ÜjC”rñ ¶QbmUµ B'5~„_&’q+{óͽRÜbéÍV ©ôš,qèÌ;ã÷E×uòÈ6 ã¤ne—ÃtHcóI;ÃýÜÂ4…±lQ`Ò#ïCÌé,÷ôrSÀ€@ ð ŸÁwr+®Üˆn
+´(–Œy.ô#7õ)ÅIX¢«„§ÎË«/­l2¤˜†¬¡)È.°¬ Sjo>IÈîÀœfç2Ì'Ž†ßsDa¨Q©|$Žd<°@½ÇAHRu Ñ’]ÇSã0¨LTo!‘x‘š-+DÖ‡1I"¨¥øzìÕQˆøü–ÛA—=÷#Ð ÷|þ„ œÇlb­ðœ†7°ñ†€©m,Šœéíx€nÚñ8î¦ßßi_}Ð-4—/wöæês-ô0}onþ‚¹^üõ°ëÅ¿½‘_\¹Èìƒ}„ JQ¼8Ž[ 7.¥À?*ô™_ÄÑÔ| B\²6¾œH†UÈ}اî¦TA¤«Á5pn1ã’¦ùJ2Œp)ìÕ„¯š l¢,‚`ZØf61­pŒì6Ž•È»fuÈ"‚±‰r+uC`±¥ h¿ÿý熩°Á «Þ¹¦—›_«èOk^@Ÿ»<¥Â/ø¹Qn l1KÏå:O–ÚgMñ&uÈÄð©xÆ€¨¯ ¾Iº¡ÊÇa1r@ÒþnÐâ¤Aqȭ˿ÕyªF ï.³Êªvçç;Ä°|
+qþðÄïÐÖþµçb;ýÚýšsíØÌÁÊr\¬ÖžRLÉXœõ$€1ÃðpÏ›'! vJœŸˆwÌôÿ˜_WL‘®Cú8–²,ˆé â5&‰a%öÔ裦͈™dÊJg«|ÖØ|;L®œÑÉÇ0o®°Î3À•íÄ”]aì»´†Ü’?œZËüQภ$ÆÉô“et©µ)Îj5/}R"ÉQŠ¡¥»~‡.`®—Á2,ZܦV¯àÀ9„ú'ˆ¡‹ºâM-‹‚PŸÚϺ°¶ü
+Â3-j#óY&o›Á¤˜v6·ˆ+¢Â8£î´·yB:–‚e©£-FŇ…ÖóyÇØÒ›áÌ6öm`ÍÂA´% 3„]joxÖÌ¢9¦b=Ý~xÑþœD:)^¢TLÂëß!N™èyZþ /@×Z !ê":L˜$cC:’‡ÁÇGcÍöQwYü–•Ùê] ®MlÆ+ùnÓ¢‚Ú^ÏMŽZ_}ÄÎmþ‚„µ@`Ø‘†]×ïHvùœ†Â@3m dJru„,:‡ŸsCÈ€âŽj2½hgǹô¨2~?Í®9õ¯4¬¿";‰ppëilDS:bŽc-•,é'#þûÏ"D…2ÍCM|f^!<Pj<äªÌ%
++K9Ô~•²bÞ»h™õúõ$Š«CŠK#?7ˆ>óã5ÎÃDz챑?Q#H{NÒ%*ˆqe‰ðŠÚGAøƒ(…™vÕßÞ tß¡FVN¦”c
+º bÖÞe2I?#J^ˆ,{š­PY8,åù °ýì×a†+q}$OX~Ž½}QÊì'ÂqcÜKÏŸÌ­ñÂ|ˆd ¯ßü*Ê¥Ò¹ Œ)¹ÊÜÛ!tˆ„Ê@Í@`NfÌ¡t7®`ƔՂ2r‹$¢Râ¬\þæzM
+gã8&Š©˜D³ö]…ð
+æX¡b3;žãôVu™|SŠ Û‰aÆó’õ¾£Pq-e”I†ìY¸í"¿À ùq¦„,®e¾‚ýçR
+I`r6Ä·J‰wRú ¢6`í‘aX96_¿Ã†À'ƒ¾XJõ®·âàØa‡ÈÊí#`vVX‚…šó7â z¸ö áÃ*›h®Ý|Ï}M%*žÁR¸†Ò˜æç=èqqEÖ¨I}8niX6¶òê»Uk‚ÊHžÃ ^ z`zª~Ú®ÜýÛ;o²V #0×»ï`ùh舭óø:³ìf®²Ï!Sn3ÆJ‚¿2‰_æ±,t¨Ìé•ÖüsC‹Ü'mš'ˆ,‹i©¢štmµ•çp­UÓ ÔÁjAäãþ~ó%±Ë,#Ø‚:pd‹ 4¤&\†»–CRóX¢$ B--}YùW:Ì”I#!ö7´úãe’WÄÐø>Aça­­oá­|ÿm?p°õ?ó·ÊÑ·J(’9HOŔǤ¥om ò24Kn0ý„PN§¶ºå-yZ”!F«ætX |&KÂ}š&AÁlÂÙó®¨î®ÒèíÏ1ÐQ›%zóËŽ {ÿµÞð!R Û¼#œX?šsi]E Ø—hAòÄvÂrÇ,ÚË)."Z\LíÞ€Ìjú80_±»§sî¿=Æè›3&£ýî¹1šøžas¾ê÷;óko ‰<ù±öÇý11P~àlÔ1„]
+r#eû¬«B/#ƒÐvóŲ Ê4&jc5-¨{yçee[v¶s<«nýÅK6±*óZØ4hq?&ÓŽ?u J!Ñ8Iæ~÷b&_ÎÛ×M”œ„yaXàX×`DüÉOɉëÅÉ7®îª#“(ŽëíÆÜ=+ñ`@-Ð¥èŸ&]€¡!Z<öU/ 6«Ú‰Êö„™ñpª¶G¡/8/-zóIŸ±6“+pŒ;_BÿÒ&/JšÖy•C5\$µžÆ É/¿Ÿã;4ÏÑédõ<B=…“êJï·³j™ÿÊ°r³1 ëJ”œV¿¢)Ãÿª¸aµ&À
+šFÖ£WT¥ð}/míë*êÄO{߶½g`[t$Ð%ôA ´Å¡jƦrcÏccµnHëÇ d”œ+MUlµ¼ ºßç#úÏßpBÏ(½s{ä{‚X°èŒ&¤!Ÿó!ƒ^åÅQ4'¾¿ ¼z#do‘”–8ªªh¦
+“:” Ó²{–¿Ò4o¼Ð±¾,cŠ
+BY"Z=&2Íl6‹ß{6 ºŠýÇËœΩ4 –¨êŠ»»
+C9:a„³ïQñm‚F#3ùñÃÀ„Q VH¶!ú÷s`jTä¨ iô« 4+Bˆ©qÉ°3pcÀH ’Äd—oæ9ê`±½¥¤ü¼›Ù 2aëš{Gpª‡´/ø”Z}¥õ–°1 Pk2!HR ´T Yb÷û&þç9%2p~dìíÁsž¹"8 ï…gPü«â£ªFªîZH«X˜ø`t‚`ZžÊ1Ûs:â¢Ñœèf¾œœ‘ ­,a»N¬¾“! f®›6º¢ºZ§²ËQ’YÒ,g:û׸¢a¬Þ´jBŠ­%B߶³kB²Þ°ú]–
+oYlçxÿð+4ýnxì/rJW¥nòZW®wÀI=½ Âbñ~Œ6…ý4Að‹"c“Æ›a®½¦9†&„F̲àÎñJcvô÷['„R¶Nê«26“H_j9ª ò©Øn €V†ä®¡ ø1Æ ¡ÂʦÏÏ<CeÐn£McxƒÌÖ(ãÝÇ ™çЖIªØ¦Ëå1Vcዳyë¤qòan˜hpç~s,» ½~ L’¶@n¬8èg3GVLV‚LG¯äT@d] c$˜7Þƒåm" Q4A}Fc„ádÙY2M·+E§$7šrR!o¦)±}H‹ßµÊǬÀ)¥¶¬€I›8ϸ✠üA•ϱYN 5qÐa¶@‚°X°Pñž¸_ÃL“Y‘‚¥™ºàBAHbµ5
+ÿK±hÍq¢ûœº±r6žšUÈA£ÕZ[D(Z°<•ý+ —3èí-Ã.‡²ßT¡M$%™ýG!!‰ˆt{ü«1…ÝI¯¦çam!2'gֆΈ^ÆÚ¢*MDRosMïq!¾>ö–ïH}î-îÕ™Q…9¦Ä²â¡zê5\lÜoh_r™ÊX„ë!>Ús²¤S;rwèZܤæNßàü\ÈøR~´(~¯Ä­™T°=n§.ë6¯*£‘˜à„À
+䲩‘šÃ@üyNÂ1/ŠXØ®%ÈϦ9OQÏQL\o}zŠœ~Gö[ ±²âéŸúøh±hÀèßôßr©Óºê
+“¼‰Eocù˜7¹Ag‹*l7LŠ•`þ§àÁøúP§ d^u–û8çlšã5góQMŒ°K´™ôÕæÇ$ÀƒP·¾—B}ì¹#/N[êÚÖnkÍœ;'„[Â@=ì>'È° çuÆ‚ôí›ex+ˆL!£Ú¬µíÏ
+£À0Ä*†ý§¸íA©JDܦ>Ø’àåá[Åm³2Õä‚c“#Jx}ö«GPeÁdhËm'%·V ¬¢±¡vR)¦ûñ¶úÒ¨ÅÐÆYv|_ÞÙòŸÇ¢Òýb¬˜˜¶ZX/FãI2O vdzÂc œ [FrH|ˆt»íLÒúÉlGªiA a*­%°@Bà†r`uŒ»­Dú˜Ÿ»Ÿ ÅØ€6P!sJtÈFX1ÄBÛj4ˆ†%09ÉqLÄZ&þö°Ò!+ØkIƒ}à–¸)â™W;(½ö‚yœUBbõËmAÔäÌ&ÛdþiU!k4±³0mÁt|˜¥ºÆÌqaXñ*ðlŠuwrîó0ã·çÒ[
+Ûäˆ-cxgn ©š(TÙ¹ù˜aãë+IÇ×v@ðL¥óˆJJ±ìYÇ|u­’> UR“E:&¬"Dj‘¬ß?
+ò¯ÝùñÃÂ]ÞÛ«CªTEïñímÐ1xÖuo®T`NÕ"JáIØ×RRÂW0)¦µA«$ˆ#÷x$¡X† HHYóˆAvu{»«q„ï²Óíïêwñ \w­ËY•°W¿¯&²Ã¸%.<1zˉa«Ãà0 ükO„èaœær´^R_
+bV‰Šæµ$=î¸
++1õ•iÙÔ
+bË•VzMÈ͉ڠ‹µ_sq¢vT7×fgçâþì_,¤Y¨›µË}=Æê™ë[ÌÖ»Çd¶ðW'jÏÁW'úÆ8}Ì© '¯GÁ2¶U!Òc@§!šŽÇla\wx7Þ4£¼¾ç±3’¨IªJÛöe<«Æ) )‹¼eë«ø}
+ä=ËaÖ(Ârt|Oª½.…’¿$2)Ω&\M÷t),ºo)ˆ<¡ÜÞ©j=¾0Œ(‘Ù.û2{ê‘Ad²Ð'bD<Ësæ…1'rtQd*4ãD7Ô™ô*)äšS×9ÿ1^7É‘ãF€Oà;è
+Ip=^ö-f«û¯ý=51­B‡å‡=š,ÈÌ÷gKq¯ÖhÕ”›Ò887%[åÌIÝõhÄãkwKn ÿúϪ¨ŠQNFïlOEÅÉfpRËž”Ën}Ã
+31¸F¯éà®ÆÓ›¢ÕK&m݇˜+‹þ<Îl›_Ž­´OŠŸ¯Ô®À®„£®;út¬ƒ©•€¤=Ö†ž«"%Á˜ã¨ó6õ1³ð\å‡Ýs}²3:ðÂ)uúÁåùÔÉ †Þø‹òO¬Ì.áMB‚éµ@ðl~@ TÓÀ`Š
+@LÎ^ü诼j<ݶñ'½/Š¾F@¹ {ú}hV™–vjO¿ºÉ9ªß×üXÂÉÕ‘ÐÍÐSVzm®7íÅl\çù¬òØlp•Ë¼„ áßw¸¼äÈ=‚ kÿè}f›‡hØÀ&4Iòè‹ŠN‹áhc–õ1t&æö˜™y?Ú¤8¿ë)ÙKeP&Zæ<FԎ󵣚‹ÞíúþzøŽ9@‹·½+ñ+lC<fV#;1”ÔÃ9)‰^Y8^¡y¾uG8Üûç[Ñ>àÙa¢ñb_¨¹÷ëN¤Ðò±ºñ·š—Ã;%ãhmÊääFïë‘uM®¹Ý”ôž”½7<3Äs6)¦¡õz%šóÛši¿S ›^/¤Óø›ã*«s¬•­ Ü„çeðaÞ8¼Ž5÷¿rB¾ûäÑÎÈce8¦5`dƼhìŽþµ,Â¥ýlÙ¸Í;¥9þ]§B?¬©4Ïfá´’ÑüZ
+ã·¢¯Ÿ¨gD¶|ü÷?ý¤”ሾoOª^ºÔñ’¡UÔŠ>ÑèãžøùV¤„?è!ëÊL®l?‚S¡µþ d
+s§(E<ö µævEoO‘OÞ$þ6Þý£§Ý@—ðÂó2rÔI¹<3IÉ%û™bcYö§Û5]Åq²ifÛ¯ÏáŸî ðx,ß·¢_(²1ú÷´;EäD´dÀ®gó+çêR‘ðT>”ì<=fáÉkýÒ"žÒŠ=%Õkò&m˜OÏ=âó'§?×':²m¹`}M¢ ðª&Ë“>÷Ín ûj¢?züãöì¿ËlÔü±´¿Ùš±Ôÿº¼Mªÿ­$ÓБa7ûëåÀìå}nè)ø¼Ê>ˆ³O‰¹qÍ´¡Ög74f³/%¼}/¶ºG/°<àÖÇÄ«—zíã9—%Š•¶#„j]5™†c$É5ÉwUô¾ˆa›ƒ¬ó,×l4óóDÑòt{=ãfç,ð¨xdÏÃßÖ™ÅÒÔ#}í˜!À ûZBçôÞ%¹îŒ
+\W9VçØvƒ³q’åQæe®øÞj÷žŠ«æuÅ{iOIÌa5Àj[Ÿ/Y€½°Û|w<ÍUbÍ· eáèìMÔÄU5ò{‹œŠÛW°Îp4%v!V(9ЗEvâænz<M€¾¤ŒQÑø½vëѳoEô둈ìû@ƒC%@wœkJ¤wíç•i /Ú¤ý^ÈÓßz:ÍÒëa?¦.ß™N2§¿æwô;Ù_±VðC\ë5¾Ä1„vÚ¸(_Ò ¦¢àÀqqŽÕ9‡ÉBŒ[{©ò
+ì¸XÑ –@—C¦ U{Ñ@!a»{‹w…\E'~I Š¼Nô9UÙF2Oè±-7Pƒ™þQР6÷‚§¾hRÂàä6ÊR/›¼E7·2&jÙë‹
+•]í3LG¢t—R>’w
+ó®´ÛÔéÑêžxB‚öþì¨\þ5 Í™kŽ` öÿ°Ën~„RËäÿvÆ;s85k¼,9ÅRÂàn/8¼a]LâËI:¹Íž|âJZ™Çø|°õ>Tx ¡Þ3Qùik&ôî“CßTéÔ™Z7Øê¶CgŽ©¸
+YúyŸôh*Ú-JtcLŸ¨Q“ˆkhÈ®{°
+Lø‘ß~*ö©ˆQcŸ/Uj—f†m
+}£ZÛǸßTõ·(™1`¸¤”úV0Ö!ÿï>pM­¯T÷ï3²„ë¨}¥Ìø ·CWhk¶ŠyJ*Ô£4
+{¼„%“ôî÷œï1bä±ÜÇ~k¿‘ÅŒ§¹fJ¼¤€ë‘°E À$VÔHl¨Ñ•ãzú}µEÍר!_ømÛ®¤–QtFný–Xﶖ&û|<€ëôØ‘'Ý Dy«h&Ûÿ”Ü?§²õ~¼ZlÍ­0Nºf‹ å°¹u{ðävnŽ¼šì÷Ìr ÊŽ?Ìò·’ñ)sD¯Öãn«šì̉£éXøu™¶F¶ÆraÛÌ0óQ9÷@˜ýñ>1†äü°ç½îs=
+ŠÀLècí.Q/}Ú9ÿÀ¥þ3˼ã”Ìó8_A' µá0Ó‰•]”0 I‹ÈÃJ¾¦Äúï,/¯Ž*ÿåŒ×KrܺÐxZƒ$
+ª¦\õ˜½ØÃg°B! |îÅî!TTmjƒù–;íl[Nƒ|¹ ]©{F맓Sd(_¼‡vZ.’Qj–qvÜ™uæ²wÖóîvÊ´å¬p„ ÍG]ó1¹$±ÕNt½÷Ûa´yÞiÀ¾÷Ú 8ü^àa/üùNÿ}§_ÒZSZkåOL—Biï™ôºÜ¯!fGç¸Ýicà>@HÆ*,ÉwË]
+OkòWñ<Sc?å
+›A0½<ûçT­ÄÌ-ùƒCçÈÎçpKÁ ÛÉ ¨(€ yoWonP2Óç.oJ±'ƒÅæ_%&~{œ‚¥·>¥ÞmÞ2ô-œ6Ÿí¥¢iÛcEòe ‡¼•0ªníx.Tz·¤=¹‰ã>Fó5z¶‹še-çÐ=ÊÖmŽáe*‰“s=¯ƒâˆÇÞG'j/œ*2m³ù·}œ¢ŸÍÂc+G/\‘Q-Î?ÝG¿í|Ý1S;^[ý¥)Ó¾çï*KîàÇÐm0aÝb"rnS¾ zˆÑvß½ûí1æÅo²Mj‚KÊ4òÛ-fo‰ààòq[‚7ÃÝNN2 ¦w`¸AP¥n5,[oŽ½8eDYãV³÷CÇ „<h ÁÐñ:® Ð}©S8îÑK<,÷ôæ¹³n¶#&MèxîÜ ùÈŒ ±ÝÞýÏöR¾ÃÁ"Ø9lQ–Ú•¶•L*êh l•Ê®¦å<°ÜͳMûLðHÿ~?Ò›åÚ27êÌÀ×åÄÓÎ3R¨KXî1äÕr0OžsŸN¢ý]cúmþzöÞ`»oÞ-÷ß%#ÂRw¦Š)òwƒ¶8zÉ©–(Ë„¦.™Zý$d3?'N¨” âÕ5’6¡Ùà•ñlEn릒fƒ¾ä]rÜ⪞¾tЄu7â2 ,¼1Ç$i^Š©Ñd[“Ùœü¨[,:V“W¯Cå2ÎZÙ㥻`«N=[nÉ:îìNÈ(cn¤YZi¼àvB´ÑS×–Ä›ëGηM}+¦`%ú<)—eÒ÷sæI]s…µ.…¥˜×1n*Ìä½ 3ÃDytë|²ÅÜЖlµþtræSÑç®;±•EïçfV„
+(è®n¢u¼œŠÝ/ kõtÝ«kí[=AŒCËõ§ëÈz§ßW½k,o)_®·ƒÈpw^ìr­9s¹Êg zt&†“ê14Þme:lÍ:hH1̲8ðxhc¢Çª­Õ:#7yÑŸ›–}_v?Œ×‰[“¤£Þ Þ…(ÏÕ G
+~TW½ú”—‰öN±íõ8Bá3éûô${¡‰ÇT,s¬ÞRß\oŸ--{Òfûm;¦è|IÚË'!R7d÷}
+|£ç|>¬Ø·ö
+H‰Œ—AŽd7DOPwȵ'$Q¢Èå gÙ[Ÿ 1³ª^Îýç…¾pU*á´ £«:¾$’Á`°º×î#[»ý9J»—9¬Œ>=G»õyŸÍf™Ó£ûR[†Ï’³º âuö2jŽu\>ª%#›ÅíטQ¦YÔÞkØƘà‡â1»Žéc ÎÌ:cÖ Ò=ŸõÞF¼€¸Y\4G±ëªhz+½V~¹Î!ÜУ[k󂄇u#ìuјÔ‰Ô¼_€>gO¾ïqn´’V8#…±{á€9k7ë+3Ñ«•¢“ÈÖ©½´ÑÆ0òü<yZ+ÍÚu“Ý›UúÌ•½¸{ãboÃ{©vúh£÷$åÍtNŽQm$QmhÌŠsëy¯V“˜øÌÚ†Dã…sæèº*ïVfBˆÙCÔ量k…o]çXºõÖ/ôqAH§ósÚÌWQH7¹Éaû¦gŒª]àH˹ŽßZï0v 2¢Í½9)¤4RÐÉsÞ1›ºV˶ó7Ó¬e‡ƒ½äÝ¢D#´Ê_Ÿ0=à¹§í«¼Ï–¥–1(d‰¬ï‹ù¸AႦȚåpƼóv§Nš¶ßÑÒSí5T¤Iÿ™Ú ZÙŽ‡—SåP®V‘üÎ%bë€eŸCgfQAq¿óúsðo«ýÊîì^(a¥›ë ˆŒçÉA·Š v˜8‹ÛŒèsx½¨GÕ äÄœÔ!Ãlµ\%n'‰ÉÇû=p¦¶Ùô¤ÕtÖ£<onêe­¤–19=k‡¹×{½ÑÎЯ[’´F–ÂëF©Ñ‰Ç‹b~,ÖæY²ŸÏénT¨ ¡ €ÉUI œçץ玪×icLî÷ÝàYP«ÙоC‡3š[««¿‘žZɼ´pCñ&âÐû È@b\™HõüïGïTš”ƒô­[ˆç„J¾ß~ ÄPB;üòÍ ”Š"(b»ý<‚ óŸ;”¢=ÙºNÈ…oƒzæŸPô‰§¯{ ¤èi~‡ö‘c†~£vW æj²zg†P
+#n ö0ô&®søƒôÉ!/M[î$Ò]ë<b_…¨ éjIš¤Þ1L@¦ýÈ<Aè
+6tf9õ&%ëòoÛ«Aáʘ«¥µc-€ ‚ÔÓ°1Uê¼­acùõ
+FC]w-*º,¢¶N
+Lnô$CºÔÇ°Šà¥ØyŒÏFž–0#q
+ªMˆ>Ø}¬cæ]óo±½RÛ‚‡tß ncáÐŽÁü‹¥šPôµxøµ*Þ²34OeT|8ÿ)'öŒx%LÇü<bÚõušŒ ™2XgzuÐç`.-uxÒ"ÙóùMA‹¼žLµ™Ö|Ö¢lÿÐV•Ð_‹‹ÀAµåh‰íbÓ˜AÀáÉ·Kü9Ž}‚Jdo{Jâ¦å™Æc5yŸH$‰É€ ë* ÚO¡[kã|žÇay ÿaX¡”‰«â©iˆ}‘(oû°œ5ý®1¶ûeâ6èã°m­0z¡,%]ÐæJÓWÑÖ`0¿Ôc£½€0Pú:´Ô5Š|ñ†S‹§0Ÿg 9'bLƘë5ä#u ¡Ú†@â©<ñ¨ÕàZ%T[$¼mF »P‘–iÖ®°iUHÁ<aR?œ°Iøv¶ÕSrñ%[f+—6b3yíd1Ñ+VàжS·½àà\0c¥W™ðUÌ¢±0eõ´e^>ÑϸSZQõv^‡úÃÁºË¨}e)‹Tê´ˆÆô÷b1~/Nç„HÓ+ñÙ·µr”„
+eD2!Ɖ`ôý[´*·¢E,`Ö
+œ÷
+:cˆYŠ´:–ñ'±þ(0,†<t½ï©Ó¯Ët9\o#ƒ/J¾(œ}êYô¥Åví>‰¯m¿ÍˆH’•¬„¸† ‰ì§n!­ÏÉ‘§9p6ðí Ì…‹Yºs“(Ÿ)4¹xO©ñì?æšþÔ-·YBWüQDáÁßB°¬%°÷Q­n.öŦ®N]~Ü çM…p+\m%´|i¶Üfœ¾$ÇUZ÷ËX™é» à™;ª6muaЄ i‡@s„tˆºk$B~W4z
+â
+Ÿå„˜Ëõ0ÊƺU.¿2ØYÀÜæ* œ­güÐ
+kbFs«È¿Û¸ïø¾üö_æ9ðBd%ò ÄUø5¹œ8 ©J4~\ASʱaa¨qsòj²ó]~`ÈcÞkT[’ÀüL(bƒ‰ð<nߧ%ã–ç„ ë9‡üU7kã,!«†zütLü¼.±ÏQ‰}‰¯ÈÆHøû‹sÐ9öÆò—°u‹­euXáµÌ¢ÔX¯hÉ£à nO-9¨A¤6vË ãø>ÎuØÔ!K5¤[¦‡ð
+J
+öp]žñ€Ã‹áQU¢W©ø}¤ €/9Ô­Žý£x%ƒÏL¦y"d¬møÊ.£ 3®m AÒkûò¹ŒK¦µwWllTU¨$>­²¦Ðª\T¸{d·Mr/¹çœð$Ú›“Ž»+A­±Ç9Ú?ƒÒ€3d\&¦Ò Û©/iøOº)ùòB©viT=cŒTh‚Ì¡MF?Η~)U?>m\}7"`t£½pq˜˜D~ÂxSEå?9‚_~ç«%â\û€‰­ë…µ"ü…?ÎÏ¡#æ.ã}yA b\cY~¢0­åt\ßé/¸ÇÇ_Tô¥E/Š’DÜ?+zwQ».ÇÒT@/Ê°˜“‰’…r}áxšxª7Pµ»@ÈoP’ó¬únyoLÙ=÷  ÖGnb…=¾•üy¦©ìÈâñæ|qÿ^j#¬2ãË°¶NÅÏZÖÞn¤ PxýaoøÝT<\ܯ@ü‰+ïYï€RÈuý…‰{qo€6ñmLY€§e8*hX-Ÿi%¤ÂåKþá”è´˜ýn> ÖXĺ5ˆca ÇÔ>¹iÂ1>œÜ‰ < }£g ¶Cëõœz`á
+:b%à|I•UN‰D‰™"<£%¾£3‹=É!©…ç°£üm‘ ’ºü6z"mGÄ :Ü'SkuìˆÓØþ™(2.
+­›1\O—á¾N|º¹.ã‚J¢
+P|žs3B)ÈlÇ"1çP)@gC´gïXžL.."ªM–`5°0ãð£ôj‰Hù`‡­5×sàY,(}Ô²_(bÚà4ï’ Y3 Žê¬÷Xý?Ÿ‹÷é „ÍqÆp]rçkñf^ä›n¹âe%ô¬Aû"⤕
+
+"¦jÔ´…#Ié8`—aá\žY0îEÎÌ P „ÂÚ<áâVsY¿×Ovt­2©©
+<½- ô8ùBUÙ¹9Œ‚#1û¾ÍsçxúY­tym67áŠ<ÓËL•à`Ë›ož)ªâN„J-ß¡ úKò¹MA‡bÕšxh¾ØkòeNSåL‡"vÏønjöK-üa‘Ëœ
+rç΀„CA\ß½9²ðMüËZ†•4¹ƒM´øÓJ*ðIqå$DòÛÄΧ¹t&é&häPæÚ¸£Q¥Â¯°aßÉ
+7 ‚þšgó#Ä™òªµVú ‰‹oîŠÙÎÝjî\.& nÉ?.â4Ø–Q#ÔHù롈OÀ
+D´vÀKmRMÙRJ¶®Ôpý QÛZü^òZ%ø¦Z•™fù!bf8ªh’rd ¾­м¦œuú$¥G÷©Ä@ÕiÞ;Ù©¿è0ÝjAº¦;ÈwÒ¥$èÌ©ëNç8\TMa3Ë·îq£!¥ÉÅJrc\€Ÿcö7¢<V˜ØLKÔà¤{O±„¸ÙR>$`ùúÖ°,ÊÈL{@¼“™!XtË-ÁQŠó°©-kïåšA¤`lŒ÷"´EâÒÖ*çÆFæ³Aœ¤,ÿÝÛµ~íߎÝ'pu€ÎïNR`˜ $’3ªïHmKêd+ÀwÜß‘`²è}A“|wŽ”ãö£üE‘øCžyC „ýCFÊZHâ
+æ ƒlöB^÷eŽÄ6.DÓvÉû¶¡_ìs笊`¬…¼.í{ÍÚÈ„ñ=tŸÄcRÓ<Ù ;)f> SŒp'#<y…ÕêòñŠ™Áž@"Žï'V£cüã‰ie ´¤e­d§âŒœêŽ Ô++àíÝ÷i^JÖQ|F‹^à°ê~§¶J{£\ß – eMEB+AÉC4¯öQ™Aȸ„Q7B{À&,v<ÓdP×Kqq@æÉ°We#WÅ`ºì Ô‡KÙú‡q©¯â½¹Ö—[TŒ‰B[‡a¢sÁâÎosF§øu@µûåY±’–—,võ,ü<¤$@BbçÀU4™ÍãËazspTîjùËDU*Ðd—!×¼/Ke’7ÕúI°¡o™Àz’çÕÊ@^¥ašÄ Ƈ¢ú‰‡ËŽZ䄱«³«*H†ö—x+ŒÊä‡$§;áÉ ´ØB{¬‘u¯R#¾:i=葹«‘•kì8Kýé0.9`+9†ZŸNcÚäØåæÒÃÓ2ˆ¨Ç(~{h’ü^Õ‡YòèÙË×O'Ó?þ¦4²J<=‰Ý{r¶öËŠ
+^&´€VsW;b(é›zvù5¢NØTm6ÃÛ›Í?¡v
+Kú#Æ#Ì´ Ë`‰Â×<ýhw‘Ú(% ÏVÂ>v%ÑÙ¾»Ã5TÃ;–ó¡§$•@ÇÀéVxÅF«òçź ÏtéUu>Œê#×ö‡ƒŽŽˆB/üA¼y­…$*‚Œ‘$ÏŽSyžI(ýØ1<RM©ŠN“k'nw©᧲mI ‰`#뤺ùqòÑLX­·ï0*´¨”¸KŠ2î%Ž$›{çÜÀH¢AÃ+‚”‘(î Îmî÷"‡p¢tŠÈæ3^‡ŽãšÉI2LÕÌQ”yì‡_ÑŒ@ DëûÚ>Ú½>yòÝIݧî82àÔäÔ oüî~‡} \…ga²Úq‡Á ETDS‰”ŽN5þn¬&1ì²sû(1$­©·ýðzŒ‚¡” Ò‚Ù¢8«,¬0¯jüÈ¡´7uÇ8ÿúË­ah ï„­µ=÷ãGEÈ7âE(×¢"Ž]ý¶µ€"œXŸ”\/
+Q JÍç"Mí!/ú¸/õF3™îÝÛYJºG=K¿Ü%žU.‹1 1 Þó
+øïjV«L-ö›™ÚÇE1Éì–PqGL`¸„„ÑÑvQšB0ƒW6èË5ØO¤HP
+4Q„Ü2Ìãì\‡ÞñwilìáÈ’$ƒ´¡7{ø+H¬ÕѶ&ê<£¥LËÎóª~÷ehØpõŽ`™+‘RºF˜=Ī%Åøh9 )vGhNT6̘Pí”BïdåÅÏT<wS¾ÊàQ?þzËŸç²àQS¾</κ-—%NlU:T1©!#ÉÎc6¦–xrG6aíyÛ)™Ì³ §¼¬Ã­ÉÀÞ#üôR¼{:Ž‚Âé‹C‰ÜM—‘eÇ¢ý†µ€7 &&„<ÓÄ!ÙƒŠ7Á¥äÄGšt‘Î,c¦))nší yŽÜ4…òÉŸ|’‚˜;—ZçõøÔI9P<<ûÃ@S9ŠÊî1¼Dm2tS–@|‘•ˆ¨1–˜Hv6>ÚÐz•ÙÞb²zggg¢AÑç3`Hç–9’½ìÞPX¿ Ñ–üII’IŠ.¶Û{Î4m®Ÿðéá^êY³F¥ÕoEQ¦üÍwèy“DŠNÒ5’ ÞkVèN‹ó¾ê­š³%Ýfù4,dÒ
+5V׌ԬE‰˜!2lUdBðÌE$e$5,kêXÌ¥±Ó <̦‰#›Æ‡a»ðÓûvÖèP˜§õMÖÞª¼3ûQCMk~û(f{Œ&]’… ‚ÐïÔÙÖp+ÛL0〗6"GºÛxª "®pðÇ0.oÝ-\Füƒ?µ ºy—!Ãc“ŽV¿ZÞ¬–pØwºq Ë
+5FÑÈ"mQFš-Ž8þ¢Ò˜'v$Â1‘q.#7àÏVd¶¿q ”¡S½xDTV!ÁÎrD Ï"þ1Ñâ|œŠøÉÎzCÎo97Va[ÆjÌ›èÇ›¦í#oÞÝ<Â^«Lâ›vuZÐþk •Û@Д…ï2ï¾9ø×$Âc¬N‰¥Ì¹‰tk
+‚Mô'½Âc1×Ò«s ÁR’ŽÎmß©x´ñ:·!~4Ô6eÙä˜$?ÎÌÀðØ;¤¶–~CwÀòcÄF¹÷`ÈänÊôÑÔ+šVŠßƒÅYÔ‰9oÅ—ª›:®Zwõ@ƒ'ÔŒµÞÓ¾S Í yQñê"Òˆ©¼­C^ö ³“2'úQÿ½”ÇÈUL;…ch§5
+ï!ù*ý1 aL0РŒƒŠ¯Àû)½ƒ¾÷#P“Ù‡©?™êñ f6ÅÂÒÁiÑ"ê6ë¶C®…)«nNуîçáBÍd=a“tj H«Àv ½÷
+…ú-úÒì;p4³*‡¸p 2þôeB$´Ä'ð»ä%æ‡ Ai¶ö &õaµHˆ#“y‹!Úì’SŒÙ<Tžlì)òmc?yÖÝ®’¤ƒ,q:%l’ÉãØÁvQ›$ÍÌæá†V^°HÐAm¯K\r‘A=Øó|…Qõ´!E[
+…ᬙëºÁÛ˜yòžòBþòy<ýa‚9ÏoÞƒ(rÁÿìÞëq?=ÓA¼VϘC,DR$,KÖ®Àvþ6›„,´²çmüoštI<½È¹­»ZŒÛ’4×eGb´ˆ.GP)'Û¦œµIŒ~¶ÎÍ´^SN´¢†Ì{)ö™]$˜H]iÞ2Ö¬2Qk¦vl3u'8[ C¡§`M¥¦
+6|e®ö
+y¾”!„÷‡[ÿ‰ô„,Øôdˆš;T _íá›3ZÙøV€ñ€@¤þhÝ…‰q"`M7÷Ñ>îSÞÙU;œê.Á^E}|ÞͤÀGøYºnô
+~XÈ+\NÖ<4mþ“‰]!šƒ­àÆ¢Àª.† '§ôAŒÅ¥¡ÐùÜ׆ª‹1Ù²øÝ,3,‡…Óp/ð×úeÙJìl#Ï´àʼn¾62p´lrjüg%y®üÍwpÜÅYÙ…^†‚.à°ë<Ã)Ç ß'nåâ½NaZ«•ñ\E,«|se^ò)ÕH¶‘¯›"~’/–qïyÞ7² `Ó¦“J_¶öò8¹s—;óÈÄ[ðar´û,Êj¤7zÂíZÎ*ضà&ж'ÊÙ´rÛ_M(c€)p;'5­}" õ›m¿„É×^¾†RfUØ<ÿÒÃP
+dlœOâÒLÖí±‰ßTˆ2F§˜Abo([K)ŒÖý(ñ¸¢žÃŸN_&–VæCMZK3ñDÿFÓº0
+ÃcúØåÁ€g
+~Eÿw™A³—Ó߉Õ#ï›a¨LiæêýåÔç]RgÝ PÍ-ÆÇ3¼E}Dîbù MÍoÿp ‰¨ BãsAWˆµaÛ`´›¿t~M–ê
+UÝ zÞ> ‰zt5gëç ðTÖcìýÍéؘÚWÎSùdç7‘¿Z)([|- í‚d‘:ÆL
+ER¡ƒ¦#TÐÄ?`µ†c©–ﺈ‰%gÉz±÷Èâ¥"ˉA±lX<ù3žRõµ $Až§•”]€'ù[Ôo”üD&´s̳«…'1Þ«D{/¦˜0H¬nÒnã³À[ïK‰*ÖA¯_ôQ´÷›ªd‚vVƒŒ’Ì”vÆ›Å|@,låÃéÀÏÇ…L Ë"øíÞ±
+†G`ÃX˸…aÁ”m\†"FáB(|Mß!"µ#e•À‰Øû¢±€‰[F苾ÊÜ @”ˆ,˜0™“·ÜˆYâËF­5_ýšÊZ@ B¬*)¥ØÎÁr5ØC’$!”` Ó€H“약ià¦ò at““5ÁÏ``OkpÅl’c¼AQ*
+â,®B¯ëÚE6,ˆƒUEŠmA"à âÿ
+DÎŽ½‰3¢çy• èY–ÙÐÛÉ‚ %ÒDF\Òª¡?!q>bõ™Œ Š|ô8;‰ý
+ äUІîÙ¶#—“ì*ß7öwŠ‚„LR³£&Á*‰z˜ÇpJ X
+yqäªÇæ}ìhìÂ{‰14à”ý`UŒñøh=Fµä}ž(R+Àv¬£ªû»Ï~
+œbe99fνì”O®W…p{¤Ç
+N ;ÄäôCD4kõ `<‚¸’}˜íå"N[ÑŽ5Å_ü|R„öÈ.±•ŒX%²]ä=T©l'ëñÒ|R –Ô‡ahZÜÎ
+á[dVšZ˜”?ñëØ/ÂÜÑë¿‘3 E˜…€ ì2üîÀ3!ós8vôTzÀgS÷>5;
+þv=‰`¾|#¡ý÷ãíËï·HRk†C$§e)ußyEîl¥‚ßøE^Í¥›J‚à³0[#¾‡"le³È•¸$ØN‰B.}‹8Z©E¬+Ö4ÉæÍÝü2YA~
+¨’}K}; W/yä2$7H5Ë vòÔÿŒ—Kn$9 DOà;Ì
+úRÒÚ³ô-zkß;/ôiLe²àènOXJ‘ÁˆàÁE™ñÇK“ÏP¹§ð8nda@¡‚kÙ=‘’:!%q ÿ'Xí"ÐpÓž¡¨ýùá ]&$©ži%Ôe $_Vr鉥À¡pÔe3bÐRéÌv«8£!Ì © ÂDàÜTåëd–˜Ï ž89ª–¼sè"Œ`Éœ‚û5­" Ét^5¯*1êTEöÖ÷>#ea(Å÷¸EÁ4ì±+QÄÞ€‚q8ñh_õ ‘Eÿ ÎWEO¾›—E缟 &IåÓ0ÍÕù Ôoœv$ö~0r]
+Å“X“;aDˆÐ™r’»’
+\S·ÈŇ ¹4Ïè÷êHÌ@2Ž qêgE@Iy-­R9xŽÞjK ”-a$n ~oÒ”ÊÖx§BÙŸw†Jó'Ï©ÄèÇb±â
+y¬Dpv…ÎB¨ù D
+ÙŽñTi±:‰ÅáÒŒ*ŒAk÷9M±´±þaE‚`kLê eÅļ‰v‰j îé€(óÐdøxV2¶º«àt¤³wöKRåq[MOŸûT‘ˆ«]0Ÿ–W®GÁ
+ƒh 9t©ÍìvZÀ&$3$´žåù5¿5ýóès=Y»öiúÑWasèpŠY“쀀P¾$AcnÃÞ:ï¥øºv~8œ§‡Y½=+IeÄ"Æ•É¥*
+³«Š…ažc‹V[SôÚìÃ$FÐØÆ9謃Œ]žÉc‡5L5ðï®}e¶€h‰ÇóE­Û—˜h6½&Ö/ebX¤m d/:JßrAJbðR꼊ÑÉüHÂÓÝJ·ÃŒRœÃË„²ÃZ[f˜[Þ£ÀÞwaÔ„ÒX9‰~« 8¼XÃcCë>¬‘—
+ÑmÇlD%]-: ‚9f ;ג“{h®Ùz ›ÙR¾,íUSb­®Ùjºx&¶7ä<žÛ2 u‹Ìi2‹žÝ"ˆ•œdK®ßJV€3?H]éÅñ[u“»˜Y
+¶W—‹OJäp3V.ÖÖn~K«8éÂ(͆gÊ@Ø3©8Äêã·@ˆÈ‘µ6P¢vþl¸jhQîìZFt WÄ@¡EîDÇpÅ.*ˆŠ•@SÍ1\q´ko£=h¹† ˆ‹MÄRÒÍpå6l.܉ÙêÕUÜ~IéiM´Ø’~N;ÛæªIâ+j$9*˜‡èqh…ÙSÝ™Yä‹WdeÉ?|9þUÊ» 0›†Gâ_÷H¾C`Ó61°ŒÕG÷4G]¡]³yPÆjÚ—±Õ‡1aÏऽÆmMƒÑ|“Éø*ÇÎJÉaj“¶’"ÀQ–@ôÁ¯Ržo‹˜ŒKèûö~‰å„ &åmoDR͆’T¢Xà‚®Ï“ü#  (§·A—pòå^÷èÂÌï †)‘ù¨Ã‚¡àG9Ÿœƒà3ˆ¯!mUêˆA ÂBtüàÁŠæ ÓȹÍR9ŠEV`¥bêÊ<¥ó°ÓEGù¨G2²$ŠMÖfyeGÍdÞ$ î
+8xòΡÈ&eç¶Ó0H€ 4]ˆ(ƒùµ¤¿ÿïç‡2›(ÙJuU˜ AJ©mc™|•\búy
+«ûò«d ¶Ö›Ábs >
+{#mc,£2©ƒQ>±k­=»6$ΔEc‚ÄƦB8Lõïö)&­'Ü燺âžRü.æ6Þeo‚žÆÔgû¤‘/x'þ"À®ŽÈ
+ä >jËòADÚ^ÙAˆ@jPº"©3>uA/îÊP iü.²(v¥± ÿ3í0ôJ}Ũ£È„Ÿo)L™ŠAàr ½H—cGÚ’)q8…¯D&Ýá7€7ÂUÊo„zN*¨/ÎáëÉZ“uïk:¿‚Ù°«Õü×¥®¯ê\¤j­!7L.ÞªdDI8'+Ð95v!O½ZW]@ô=2ÆL
+:Ekœ¯‚§ (¬ì½
+ƒ¥$%«}Õµ:€JÓ
+ßÊw9ÅéÁåCî¼<gÞs#Dz`挣Ë3ÿñh•X熣És>¤£2cБbûªß8,ºÑÚ²2£Æ!fç¸C±XCIK…„ÑáŽEH¨N¢åUìg©Q#A:Tbeå,š± ìŽ9N!›_1»‰‚éD„ŽQ$‰W›(Ú]˜dqœÀAaû $XÍÒ= Wâ@&Õ¥:³wì# "¦x±©[¾—Òç44%ih(õÉ,SèºteS½1©8+ôñ"#ÙCÁ¸ë`-uoƒnk‘ª¢ãk8 ²c 1ê¡_ yzcA XÙJ€¹iÂÝ’›ðLDZ÷Ì
+°*8ºjÈÍÐ3.ŒàïÐŒøªr*ÝüòrGÒã†ð |‡l‘Ÿ¡K©RŸ@åL
+}MbTÞüÖÚ.Ë% Ë!F?à^Æ©hÍŠü,]Ý=NUÞšCP–”Ä x?§ÊXåOÁu}JIŒ/[ÚkC2íŠ<X÷ì‡ð$€)œ
+îêB1€EØÕôÑOEÉq&’ÀdÂyVRè²²ÝK t
+gŒ2–+yÿŠô‚†RŠ÷cpp6 ãE Y
+/ÁÁK/ô ºo$øŠ¦¸ž‰ )” +Ù÷·”T`j‚)zµàcäÉyôž™JP\èKÒæžg%"™b©2o|΃më Šl"Î!eƒ¦¾4©Á¥õ%1?#á‘,›õSCû24š½×1$»Æ˸;!à_÷KÊ[ 0ÈU㇡È¿œýB¨vöËܯ¡³t“Ù¹N•ç´Œ~›Ï°¨È"LÖ {ýF >·i.¹ C¡ŒO,‰ÀÄÖYÜÃKØ"æï­“’ð §Wô‘\ϘÑa¦sèÓ°Q¥œˆASáCðP-ÐÅ f±ÈJøTUQÒ³u˜0>§Þ-è.X÷Û”w11¢£5¾@zUy—Õ©«ìu¬Qw(IOîF&ÌQÃ’³:ŸzÁâˆ,lE¼Å·Wô Ù^¼jBuœ]¡ó©Gwä mì(·dꃳW¥²F˜.‹ÌêV²a±V†æ3)®\ÔôñS«­ìµE— xTäÍ ×ác‹½VŠ~¿ÿ‰§üã¯ß~ÿ{G)î-–:ÑôÙÝòMÝ-£$ƒ]T ;^DOSíY£
+ȃlHÞpÞdÍqrx\ô]ΖÖ@£3»ûìJQP=^F¨h½¦ûÔåþsÉ\óƒ—÷9øÆ!^ÕxÝè* …žy>5­d=ÆuJ¨¥$
+ID~ÞÌ‹hB•6ñÔ¼‡@Ž‘vÐвâ1ý÷°¿xäP%² S€õ9#‘;¸Èü(Û¶´ÕAÑžWZ6 äÐÆ8,uk6¦£Ä|€ZP:/hg\"1bZu`B®jL7úØ*eN|`™é
+1LSWÚ5U¥Wh2³éçåX[>ÏÍã ûŽ?&WX¾vâŽf2SlÅšTélC¨Z¯vHæ–”¢É?×ƘZL\ÔƒÃsÀû⺠K‹á·a«Kö]Ò‹\ªU@î%
+Æ‘S3G"#
+ðËå”
+Qæïó©.qÏ䪔œO‘*
+¬L²Ê¹D5)ŽO)÷<5´°¨Ò†,aQ.äÆ ß5PÙk8´—5 ä\o’HÃb4=»ôL=
+(ãzËy88`…–¦ºõãÆš:RÈ2R¢¡ñµe2ºOÛ¡‹ˆ;(س$'‰ù« ]‚C‚ißo æù¢
+P,J }‰º´}a{n/Ñ@ ·!ÙE„;ÌýáJ0&ÓÓ|\æóòjŠIcóº‹A¶êšS›Œùq‚ã?áSPŵåÆØÀ‰N¹ÙB<üéÙ väAΤ뜮^¼
+÷gÀcR–t—(ë‘ÄäVkèJ¢¢Ûä¿¿(’ßH° lèchœiÀLárÆÁNêmûå]ÑÙÊ,œŠFBà•ôÖi²,æBÝ(Δ¨YÎÙ´»›/X^e·•ä: ‹˜òVrHYí¢§HH‹Š„­‚ŸÝ­°êW†aHl&Z´1J·‘üf2Vþª¾vÌÒ~Ó6Œ®l4ÒÈér6
+ÿ³ŒC;ñ]^ŠM_méÖiJØøgài\LÁ‡Ó6K*{¡=±ÅçÀøC¬<:¦2bv3‹Dä\NQèD–ÖpTðטð=—J¸ ÎlYÊÈAÉþ#çMžg|NI›™ fק`­½²Y%,ˆEìJÉAæú&ˆ2_þi8^³‹€þ±-K½Âæ¦ ¸!ä§Øʵ”@åΠß$ôK¸ Ï™qÈH8V þÙ¢Àì>?˜Ý_l¨¶x.ø> :CÎÉE{.iÈÆòS?T§(ü°Ë¬Œ¯f Ú*])¡ªD/d©°Î+,â$N^É$^­Æû3'cK¶S.¦o¼*ÊÐÔ„2'0×®¢‡ñW*˜µŒ$Ë«"íñIm@Wy/Ð~±Ó³A'C,fʃ/^ב"ºK@Æ?ièV4ȹdÀ•."²­¨h m¬S$µb¨MÒøâsÖkâ¼M2 Š¾fÀ îQVDîôwq>Nj m8Hïĉa¡†?lµé·¢Z}ŠO…=dÇ‹_§Úñ ¾ßô1¢+=gx²»mÆ×â®Ö©ñ2uF²?„áÅÊaM†¨•¦B-bðÈ L–fþEC¦tÑ”rž±çÈœh¶J’…#Ÿ« á‰ÔS¤’®¦¢ü¤ ì/篆ɫ éïTCø“âÇp]†€Ö)á^Iv[<éƒà£´"ƒ*”âý¢¹'\ì
+Ò7èÌýQD„µ-²ÞÞÆ"ÒViÔH½»åbÏEðjPVŠâõ,¥[%n5ÉWæí ôrGpëñSø0ô Ksû|¡ÂGñÀrJø¯©ñÁP D˜ñd€>ýí,JYäÄØU¾9ÒH¹°]Tibtˆ©Sć» .4siÐ^åÆ;ŸÓ»0¤X²”õ«ã½É®ÖÉiÐ%%E¬Ë“˜ÍeHÕ?|\Ð×y:™‹6ú5²‘m ‘ë˜À¥s°™²‰UCvêà·Påî³…%Èÿ?Œ×=’ä6àôÚ–1A$š³æ¸{‚ y#S÷ßï(E¨
+Ó–T9 ùòý”¶l^yFë¿Ì‹þåð~M‹Ö´&™Ô>t†6z '@? ÞodIUöî šOe³Fà1H …ÄD÷‰¢;|!àv¨.“ÉTlÙU`]¤ZâáºW‡Oßü»cûœŒ3Š˜4β÷ÌŸ± jßå¾é¹Nú]l!°Œeü1Q­,¿ÙîX[|84šy@èMæHo"†ªn
+È”šê3o v{$qÝÝñTJý·¤³ÝC"üŒ$‰rK_Øá¬úúùßjS´° `Ll÷0n(ßµíjÊâ)ÌbÈ~­Q±.Îÿ­ž×«U¬v$r` ë‡ã<ñ"{;ÆÒ½54…!Ö 2ÜB_¥¤F¶Öðs¼lCÜ{µ]FÐK8Š5£ @# žÊQc¥;à« ¦åÞdÃZ›ýxSÄ6HÁrÄ-5KüRpùQ³â¾gw>•¸ ™[ §Œî˜Äb ù’’
+¢¢ò\‰–ÝÝô˪qñ®Š
+Î!`D:±ÀÆ€nm2ËßO;¸Xn\œz‰Ž0ß:ɱ+f\’³û>)Ê4ÎØ( FvÛCN”ž²)¡¡É`ä«Žptï±Ve¿‘aiŠéq({º°U‡¶¥fÎüƪm(Rk‘kÏ(g}S}FÒD‹‡ûþ1+Úº;*­£îÈЧEÏ(ÜÜ«áƒ,åD?PX|—’DÆZ!Òµ*-Iä2ý±/hƨ¶†´<éÞ—Æo˜æ,C"Ÿ²îlîO%7À ’¾Èòüh¯q $ÓC÷@sT%çR £cÐåÌžmÂuµÞŸJ` -3Uû> kJ¸+Z‹ñõ$$í?Õ–d 5¤b«H­%Ž?ÆÙc9~æ©oúKQùÆbðMG…ÚÄ‚Y—¿Xd=ë~bõýæƒiQ<Ãè­ã³#fàK àý¿·H`£C—ÝÒóDŒá-žpú^ûRdûX·S±ÈqY“ÓFŠ¡¥ï{ü×iC¨cû¢ +Á’®1í‘özLJú§²näï³s '‡x%G¾<>•Œ¸XÐ=†Í™­÷¬É×Xc(Oµ>í†FÙ‘W,1+ÿÏ®#Á+ÌL»”ìÉö$vsÄA2ìþ,|NQ¨Ç åoþº74öÊŽ€üq7=c’7Vì&xN†ˆ_ºÊ(ÒLʆ2£üfw ¤Ìhð÷tæ­rLc¬k¹fñ묿çË褭mO'Eq?y*Ï·Å„þ˜}î¹è×WX· [±édv+W§{«dŠßÈßGÏWŸ½DÝÙç^¢îŸ¼%±2–„‹Ÿ^}VD~5*†>¥&À\Ò¾s¬ÛWj^^7ûØ‹,¹;]:|®×JÜúè“Ÿ,ü…0›’Xm\«¼_Øž ,H4ä’’Ò©~Ïm%§e3©vD…~æSBU8YØö
+Œé›r§ž…Br±n|<Lô+H£…YÖc€­&©xÞßß”Øûƒ‡Ø’ÕÒåiÑ3¹èO$+2½–òÕšçUŸ}ë+5OÜóë+ûã¬ûþüøãïíXÓozZs,á\')ð¯7E x ¢ÛgJD­ƒÉ8ïe‹ãÝi9œŠ¶©°‹¦*·ŽÀ•ðã©ÜÝz±{ƒ»³FDoô¢1Ñgß›r~¦¤Ea궦PD*ጺn½â¤ú†‰bŽIÅý!‹_ØÑRù)\°Ëc&‡=ò!nðo’5;æ²®3^„“ΘÏدï“šûö=¡Vÿ îþ”…µöäáT<ŽãâGõ»ß—°&ðêËÄXOÖPÙ›þl¼^M§¬“’ûÝ 8™ÏHË6='’‰3ˆr¾¹ûÆtï¼öìU??n@˜œT„—3…#"S¯Ï¬
+ÆãÚV­ðÅ7%ëZ‰DÛøÔo0üc$ÖumB"ÏÀšø˜œ÷×›¢jA0»071C‹¶ÐÓ1LÃƤÊ9W6¾_]“ 1£äeôšö1JÜär]ãêu]ÃQQÝ8Ç7-¿­å;û9B〼÷‰+ì{ƒvÌÿ\gÞòžýó^LΡżþ‡AÐâŽuå£6ÃüŽ›Hþn))Îó’å’må³òCÉñM»KÒ¦<¿ZóB°ý[œK]â®÷59ˆÔl÷b å3)A©ÛXA–È‘àôH¬¡5ØQê½^ëên4]LäÞ>~Ú=š7©¸çMãs('î{-ú¿
+H‰Œ—Knd¹EW{Èq~ #‚¿¡!5õ
+ö¨<ôþ}.É'T§˜°ª&UÒ}üEÜO<snW1ªuϹÔçßJÎWêyDîáý)ÈÈfu¤>"Å‚ø«=%ç>rîW¶è=ÿeaê©KQ²=1µxægÖ±+³¹óÝÈ©-HŒ1JÍÎϤ§â­¥R»õ½Õ+ȯœ}xÉÑ›Ùóóño`é*ÎbôQS,]c4k\³§°ç¨r˜jc‰«U µEköü51m´¨9iñû…7˃—Kƒsç‹/’U/Õ‡í½ÌûàU{„Mˆ®™Jí]-_`*§1ýð¸ŽÞ¬ç<’·¯zñxf©{ø„¸Õ\«åˆÚjd+:âÜÉ.½_4
+e¼Y¦w®N‘s>Æ/^XoÏ^îo.WJ•Ñ,½Öã:qy鹔γûî°Hƒß·û÷4J®<š¯¾ð6,¯Ôáùñ8aâ=yuÚ"s£ÏÇ<p$ëÆvföc]á­Pì‘ÇìŠȲר½;—[¯“¼pʈÖû‚¤ÂrŒ² FsíV®‹â‰EŠG]µ¢½|L~ÝMÚ¹§rÐ…s> ê—àÆFÀ›Úø“WÛ ¸ÉÆìœêwÄê µ”S§‘ÂÏ«”F‡ë¥Âò>®Ë£[›8H ÓåeäMrj%…8<Kõt¨<&ïWíÇV.V ù ³Nˆåè}Øób&­§šHàŽeB S¥Ýâõ¦Lù2íkT E9– Žõ:¬Èxª‚ ª@I>¹_øµN‰ekƒAHkm§U¬ì @F`w%GÊn–ç ©©"Ih²­£pëŽèÑkhÔâÔwÐ÷"¤”¸±jo’蟂^Ôø×O${*{êÈYªþ±â†%kÞf‚¿HÙ —µb+¯F¶üSLÆÒ†¢5ôìó¸Ù@~µJ…Z®´Ì”•#hd¸¥RÓÒÑÔu=ÑÛzîd ^ð^Ahß1< ªlHw:zh:¶@0°"–Ðs¯Cµ²Xêj`A×ÊÝi»!2Aš
+± i4´uõc‹dmÕ[¡0rÙîoÖ™"‚®
+KZ§]½&}`iÜ%ôõ’)‚-Haeò¶ÂÒ> Tz‰Nüœ´Dž¨EwM7HÉ€$…lË­:åky†z®¾=OVëŠy! ÇnM¤Ü9»AR¢Ýìs§zÕ¦|F%Æ×ÉÄ„ú´Ëg€%6®±«kÐ&úä:Ál!hÿFR†îeAäÐ$Ô¨µ½4†ÿ$„žwPŠ”´,—êk'gšà°µrû8¥ËlÅjý $žßébzpoõ
+¢ÿ]I^ü´}äL7"²¹ºó3­#ˆ7!M MO$h¼ ü,(lbm…>`¯tDœj5l±•Yq›Ëÿz;ågA4DE^‹½™ÔxzÐÏcÄ
+?\ÜɃB"Œ's!
+¿§˜-îÉbÎ!ãXàγ‹
+Ø—”(kâÎ÷"™ál*潊Áñ dý.8l&éæ™®æ:³±ÖØî‰
+¬˜–š©@ÓPWAøÝxĉÞÐñv;˜¶¸ámëZ-HB--ã`XÄzÒP]¶PC¾NN£Ž i”©uÌŽKlÁ÷™èÌ-#0Ž¤š$ËÒjG4t õÛ|êÚ¤Éonsô”,ƒÅd‡6Y(nr¿‚â’áI$ŸëLKxPºG7ˆƒ‹‰C±NC®1œÛÄÍQŽkPƒî»ßÏç‰
+µÜãÝï9ídª¯ÑI|Â×óExÁÔ¤ãÔ¤6ë•¿­ƒTÓv‘¸+’¸O£üAkÔe™xÚш±35ñ‹øŠÐfo##Â’Ó©™åští<×Á´`ÄewŠe <“NiãaP@”•U–>å‘#CTŽç†i@¿Èå¶Üž ‹”0™aW‹yÔáÀë¾üŒå|T|"ëô¡´ó)ŽIÓ6é~þzd•ºcÊ{¬ˆ À“±VáW|§¯ì1‘¥äŽËùY;I44» Zv\‡Î)Yµ£³Ó}˜àu$ts˜æ£&©p˜½•A˜WÅÊâ&y'QR^”h¯Ã`‘õè,¼Öy ™<9hîoGÈKXýÐTûü²<O”5Áä[‘×~ÅZ>F[Mú“Oþà¢9±ù—?4T]Ü1ÕÐÀ¡k”Û£©R•Ê­È *ª¶h_ù….AÆ¥*-Ïuðqx‹·ºÅ”Y†ÿ!ŒÖ×V®Á€ )²m5%r“€L9k63lkL3]3®0ŸgL .ÁÙ8æl÷%!rŒ”ïÄÊøH¿É’ªê—’2wDåvpÃ3“«ÎÅ' ©R¦,`§€øç܈ ñän_,š•åQùŽ’;)‡h2Ž6þÿŠ­¶Hw[èI žì$s«/®Rg¤Ýq”9•«(W—¡ ¶dNò®¿ešŸó`’]"H”{E=døÝ-p:ÒUw^żAùïšwµ|~Z´7†™:È >'›#–ìžéÍ2Ë~½t¡ÆZ‡$„;”"V¥’¥úêeôŽ±·™›mN0Ú LµžoN¼v31Ö(ËŽžVÃÃMÓø(r³ø÷Ê2=ïÞÑ*|6öàùm
+¬nç¸7!')[xšhA°Lq ¶§Ýð+éˆ{þ*ôAK¦né€!vABrS˱nÃôWA º¸u—sîy‘%„ ºxËÇãâÝ™}r)H>‰âóøÊ?)2¢$š­'ûù†À×WÇ3Ðf"F._fiÁu"5 µä’a%·g¡s»TsìÖè(c.zĺÉEj«$ò‘³îø3vÈL›oâ¼XLb°"F2†ÅÉ%Wkàbätæ5úáÍ:u¯õÒ팗I’[GCOà;茚‡µ¶} o©ûoý•Õÿ_ßMEH"Á²
+ªYòˆZ'P.;¢3p…ÒvÿßÔø9ý ¯(LRA•‡d•ßA—í® ÷'gú²£‡—/Hù|ô>¡D “F˜G?€Ð!^ª£¹áë¸ÝôþäLvôN
+ÌF9"–‡£ßA80…BUÕÉvò.bf=+9k~Š¹ßî°Ù‡ »«Ö1ú
+T”8¨V};²qÿé<¹FM‹£Uz>3gDíc‘‡ÐèøXU.DçvÂkòrtµ³i¨-ͽ4€ Ñ˜&3àA«y½¥v  Ä_˜Rg?@þ6Š0-`“·ñ¸„œ§™ðšCHxIŽ“dœ@èjâ —(qDhÉ€¹ûV\ Óã-øyˆÙ©¡×Ü6Û:r6-W(Â
+<päó0 Ò™0à¬Ã!ƒuð™WöÐ:hc$RÒpnäô]åLü˜ñ^çÁ8³…Òä›1V Y<ÆÓÕ/J•éç`“ÇœÈzPøF“ÐÇø°‘˜(W5±Ä}þ7”Ä3ƒp`q{˜Î@ÙÖ Q«gß*[ô&ÐÅ4…^ OM| BfD&P9—¬¯XA
+]«ò të-êSÐk]6.Ñûto÷ûnŸ`.úóþD¤––‘;q\Š;”°´Œ´J”²¹³7ü:rßîà ?Ÿi=¾`1V¹”æáèv¤ÁIwôþ¹ Ðéè·íNGÿñLvtÍLm¼”cá^bºP#ÒñÚ$BC9Ú( ²B¿ç=ÇI‰ãÑEU‚„Qg©R _… ‰×ŒYK"¨—Ή8’?2©]ó£ò^´uo¤ÜH¬,ž’˜I4δÅ
+Ó_Ôr’ÃÐðîc QƒHN
+®úçŠoS;>Æ )i~€ C6Ê‘÷NW)”À 3FÚXë`-òëÄø³“>:#•áŠvܪù"%Ëy ´¾S“â…Õ4Q±”D0æ´uT[“PŒÔûÂJ©ÁÙ>4—cÚ÷†×fˆÀˤ:§ðìÌÒ„Is.
+Ò d®O׆^d Ô*,H(¦ôüÔ!UCùŸn¶×¦¸Ë…çð:*vTÊHc$[Aè$/j´þÓ©ƒª4Cüžˆó§Bcî®öamUH=¥ô-µ×˜µžU¬$ŒÐKÓL P¡”,|´F•éΞ§B£_*©%Dâ_‚(àóåHþf‰ìp“Ðâ‘×+¨„eðļ Ë+³’­3¥¶öþ$ª®‰×°×.Å‹¤Z
+yªØS%âÀàŽ¢PþîŠÊ}f‘ "cä ØUÓê.biŽe¢%* •óNNXèž%“ t°™†[sµé‚ù:cb)$,ZB»4]0ñ%7“ˆCãÕ:–ž£â]h 7©^Ô<»ø×4‘²{g]VJ ô½Þ7Å,R2º¼óBFÛw¶,ëæU j 2
+áfj «4ÈŒÐiwÄz&¢fžr‚˜\ Ž+*Ák?HQÓ±¨®h¢ƒ*&‰ D¿PE€Tzw Ý„-R ‡¤Î¯
+ái¢(çå¥h/”¡éýayE9È~¿W³dÚ­ÓÜÝ&‡+è}é)É¡CÎ4ÏÕDw=Š?AW ÐIW¯æ•P6â rOuëâ²RîÙxM¯s ´c»VÖÅÙ!cí#Öâ;ÿ;Tê¦Qvs„Mb¿èåCD:òßzò­ 5ÊR¤IÙÓj´Íǹ²ÑqÈx¬}CB R‹ŸL ™6”õÄù‰p`Ç°¢Õ‡uìh/MPø‘‰4Ð
+3ŠºUîë,UÃÄÛ¶-0šÀ$
+ν b7QC£FY<Z„Ä)ÊO«3'B"].õúÁ`µ–V ;ù‡àDÓï¬Ú%Èâªv€ÈBˆ «—íNä¼GñóøWk¹h›
+º@U4s…–ηèf+Mz1 ¨ª¼ hG£OG×*ÉΫÏÈ(cAjopQ¥„Éþ˜6î® = ì!ÙtáZG©îØ1?îQô2[â´P!ËÞ¶»ý'ÿm¥SnÜ;çÓ‹éÌ0Wé˜2†€û?Pï
+ÿþŽ:ƒtdJÖO}øgQ£É¦†y¹[wÂÆÉÝ–j…@›Z{7K‘Åh…¥-¥·›Ÿ»‚&†ë‚ßd'²TT"Œ¾#‹Ü¾H¼Òj+¤ZŽÈöý5²?A0nœ#‹^ƒî aÈ]Óqd[ [Ú3žÆ@ZZk¯7­f˜§W¦·@qQèï;eJ+ÃE,ÿa¼\’3Éq|‚ºƒOàГ’ÖžeÝ¢·®ûoçÅô´3õO¹;¢.X)‘ ŒƒY„œQbXu™%
+rôÀ Ž˜™<*”`nÝ3ƒµ”S«èsä:Jê×@¬ s‘tð±;ÈAPµ—­nÄ—\aêŒeL`š± În&î€ß‘¿iÈY˜n ß/@Êx„JíÂô±¼¨câsßZŠ³B¥ùW ÈPšÅ—BøâDÄA¿
+H
+D{azY#yq¹¥k³ã‹PrÖôlûñt­nüŽP9–h4C$•mUwÔ³>û;ØÑ‚åâg«œO)Ü™E0¬Øu“¦±“%J[k“tˆõˆøÆU¬ä€%#^ÅÀp}9SÄ?@ƒ3Dn“Ká±K|W ÿJ—lŠç "ðœÈ^$&UO\¢1Äá>dŽ™ Ï#H½D£99·\gĤ[¨Ôåf´iù>ž¬9¥DX¹ýòVTΡ€=ÞùAj‰%s½ªB†Ó©»•£Ç¸Ž~yʬ]Q+Ê=âá”’¶A¿Â¨º hŽÍÄPÈ@#ír)¸ªÎ«§ì–÷Þµ…ø·~¹ïνúüÊo£ ýE€Ät£‘G/çsX E‚YÂÀpåÓyb²eŸ÷6y¿ˆ‘ww’)èŒ/‰X~ÌÀ<$A¦Ôœ3ê‹ZìÆ¥ñîeÏ¥\¦—®k¾y ÒÏW ¥¸ƒgv+Ål:†<A øTHasïûÀ$ ƒ™]+VS«c_gMh‡1‡IW®‘Û¦0Ú娠ïÖÏŸ,`w¦ gù&Ñ°ÄY7ýgO·Áâ#>rÈhG—Ë¡Ö´³÷HIí"—Ô¯Ú¥1ÕÇ_7וz„úïÆbÅ'rÀˆ1^²)õ2-[w’2T¹hº¦:D,+l9¼JßXÇ\¬‘ê#Øô[8ŒVæáDO—tÌ$Y´/È•Îx$Ûp¥¯ü¸o’.%\o}ªShv¦ãÏ_œWÜbâoV@LNalþªŽ•—0©K H‘ô0¾‰®~ü:º·~°±<KývFŒ¿:_´T1ERçƒèÜ`ï—ÝÓHö4÷ÓÅËõ1¦¥ã]uC¤ðÈJ[1ðþKcjÞÙ(Ñ
+m0ü "ˆ1Äo¶(N`yGÙ,¸(~”-x!O‹|f‘Ó!X æ_N~ÛEj±ŠTƒ¥žvyÌÙŒq#ë—ýbfŸ¦ïòdl #¶ú²W”ï²æ
+­¥Ìæó'¶çõ@#8Ú—X&£!Ö¡ö>‡M¶½£v_ÊÉõdZ ú(!(H:¥Í5¼%)Æ«vòMâJ™XæŒA9@þñO±³‰†a.v<+b°ƒ£÷„º'ÞpüpÈÔj¯dJô#Ôµh°!˜ïÇôP*²·ÍCö :4M)ŒÝ™²ž÷J§žwb¹$b¢³ûªDK¨ ƘvX¼‚°²0ŽX_Šømª¦ÙÅâ®ÍŽçNUOéh“œ¥½8‡¢c\I@‰°”,—ÍŒ;Àà}ŠþžÇ§þBCg«^]X.Æ&%Û‚FùÙ›YjÅx‰±G†î 7loY•$oVZ¢ûp¯Æ—%÷é]òŒ £±W
+ä·sêŠUm lzWþ£kÐ:–",ãiø®Ô²Ó‰¸gÙãkC¼
+âQç ™O>ž@”Ú† :¬µÕ"“*Ø$ƒ¾’/'†|„ÞA_žÎÐ}¾
+Yn†÷ijL‹Oýe>}MÖùŽ+†‡뺶2d’aalæãèîG
+jM>Œs ;ç&pˆØÔÖw†±±pñF9’Û”;B º,î‚'ð¢rÚä¿N ›ƒú|Êô[ë\ýx;]?‡—ê¥âUO'Ì-² ¹°Ær¿×s©We‘©íß{p·¨‡^>­®8©À÷@¤Q^œ3q¤ÀÚ¸" CG3Gþ"“Vü˜ËÖÂÄ2M„$ÜqÞ½„æ ¶GNñ’AÜ Öh!o§YÁGÒ+1wƨÜG»J(`Ø;mÏåsxo Ï ÒBåÝ,£0ÌdF3n‡8n÷ë,¾õÕÉoÑí¤H4›*(ÚVµ¯
+3òìkVT€:æ™ß£Â¶ë‡åÏ8‡~H£§^>ò*”˜<à1¹œN4 ™cåä÷M¦Š9f­¶¹‰R.º‰±´‹¾,J þ¾Æ‡$甓Jä«QV¯L|9柪%í`„!r!R?¬³_W?HDñÒÕ.«Fú8Ls›­ñiìfÿó‘2â•y¢2¡.Ë&õƒóµX¸%†€å£qHÃéSZˆ™Öhäó0G"CˆqÅh$<C§± 2 ¿]Þ{kî/ëòC[õí?|Þ°¸á>ß°)þ8†À,.n‘Ö©å™u³äY˜Ýr;╺كYj8cÛV|R–!_ð¼x¢ã0›CnÙQ~ÂIö¶‹ŽW¶9<g<ö—+Š}`®7„Ît1m0ï¹(¹¦LlØ<`~Ÿ1TñÄ>ϱ‚”FPpKq,²wÙTG¹ŸÎÄ|/$˜Uzu€ø€™“.EeÐýÕX@¼2Ȇ‚.åå+ï˜á´É
+
+n˜-. JÎQ2\‹%à
+‘šz0Â3éÄ•«†LëÙÌŠ³"Y¶€Í.Û•­îyy¬C£leùym:,¸ÅËiØwÚÆá$¼u¹ò™%¤s†(¤Ç@‹cçñ’ ñ¯y=îŸââ«óû@wp$s§îïZ¼p‰2Çy¿œ5¯Òà/éÔä¸lÊ2à€ÓY'ß è x¤_ëRÔ•nÀ
+Øó"tAŒPJÕR"%a]R.ýò_Jl´hŠ¨ ì{“ÉØAÉ7;I÷ØR›áWÖç8Yói_ŸAD£ŠÔT¢'A=¥õËJó·´
+E×f3‰ • ç
+NHiÕþQÃhAƼÿv‘lÆTB—Å‹p‡™¤¨½×Ü®¤eÂá,5ý»U,芭5û¾ÂSÇ\¢SK¾] ÄãZÏØŸˆR5ª4!GŒ£»ƒ'5käp<^"Ýat¸lìD=KA ©–ÉóG„.JpZ¨" ô£~Á¿ ©ø*²ÀãÛ<íO4€6Ž]!\C꣈Ó…æJ öÃJ˜!ëu* ]ho‹®!–2ØRÌ Ùf²o§„~ÒPå\öM·¦(M=4Må +‚ Nrᔇ}G.ŽsÔr• ¢U,Œ!±0ÛÈtT£ºì 6^Û/ßÁªð, Kp›&'­éÍòú(BªúY©­/ÍÁᘪX$êð£ä>)?êQ$l˵)´}·¡d‚= ‰³
+(|ØNËöîÂWØoßmÂbŒ8«Uà2°¦‰$»Ë³Baà¡VŒÝâ¯4Ò
+–U%,!6š–aœŠðSÈŠ®W@üh–¸i°
+3惾ŽÙ ÔÀ§"ó kÛÛž
+@‹-¢xÛ­%M®v°`†Ø‰¡ÝÌ EÜ]!_j3H*AHg4ÒqJØ©ÁK<ÏQˆŽ à}#™—QV4‹»¡"BÎ"¶ˆŠ²XÃVaüB´P‰¦Þ`Xzáö…àŸ¤ÿR‚ã»s‘®²u/ÊZÄ B¨­ÚwP/RM¥Ù3¨…¾HCîyx¥SR¹ÝÜ÷ö‰Æ0Õþ”a'’öêaf4Ûˆ[nš’…Câ’º ¨?ó+ãlBI…cGyáià…<XÖ()þgnƒßQžóÛ@Œ¨ÄÌ˼o A$[»¨·‹Q£aû°'E GÓj»Vº²@b¿²}FÛ@[F7§Å
+6¯íÄÍ•l¹1û¤’êÜÍW\$‰ƒHìÕ%}EÝXªåDÁdÙŽg·ám夶¸Ý(Ÿ˜zÈ…Þ­~–¡t “Sö’M4ìxS.Ó¬¤™“›íNêb“ ‚¯~-~´Èê(B"Í"¥ÕøB¨ÃØ €“èzm/% RꂱLò9ê^ÔL®ª
+É(›;'Ò$¡¯ 爭[ªsΙ¶SÉW*áèñ¹ _êí¹ê™Ü§e¨åÏühxhýãeŽÈDÑð´et`_̉–IW'èGšºÿ¼ŸHp¦«P"¥…ÈÎF™?ÿÂVk F[a"g8ò§Ñ£B'\vOAßbGðÝNNà|)Ö‡’^”H1¿!Š!uc¾MÈåd¾y(z(9‰Í–âQ‚&ó-ö z±}*' ɤ:¸Ò_ ñaš<½TìfæµÁah FOeÒhþ¸…*- ³³JÖ’C¢²\lÞlç\\7æ*L«‚‹qª¸x»Ÿ8@f)ÃRK©=ôáŸâp"‚Ä!|<.-#âQJðE Q{ß-Ô $a-ZÉðÛ õ8Jé9°Ô~Öd]î´p`ÝijXý¹ ´ƒv–WÃ9E%äÕñ'iŒ3騨¡yTIÑÃ9]_ÆHÃäéá6ƒ`Õ‰i9íI^…[ÎÚbî5ÝðREA°’£ÕcO%¿Íjm轊V(Z{=ß//—-ñ®¯bGðØ(9Ò篺vòêÚDFC–{=õ¸ŠÁ(
+Ê»Ž³º”¬O!Œ„Z
+µç`ÆÑ.˜u†~¼Í ·W|éÎi.=þr¯ÖúØÝ%Á$w:òO§0¢aëGî€Q²œ9oEÏ7¥oV„“ŪM”=·å’y„D ë@+*?01 làÐWìÁO°pP¼6vž‚®²„Z¯€HA÷#2EŒU™}\A›ßsýoôºFàüc˜Y2
+Ò¾Y·!ËÜá*üÚ%v›·µâE°3/¥)ºeä mÄV–èW¢'S–fôÏVÍ•;î5P/öJ.±¸•WØ)ðÉE½î%!Ál¹§:ñB;JÙßÆs*—tË)x <hò‘
+¯QéK5\7Õ
+›LøˆÀ‹ÙvXOÉ9<}ăõZäÎÏ—ƒìúA£ÂkbK©=«@]4ž~lëú;~ɤ"ÝT™1+ͦaÆÖöA”ƒ‘"½ÈI6ÂÍUè»<Æ¡æíPƒéÊpEÒˆ„ÕÜ>v-zÿÎÞìâ8–@°ë»Û·{#xìcçG Ð.~/‚8miêŸݾv©yÿÆÖ½kVÅÖ<ñ3˜\/~/BuéRŒ‘ºª+•)H¶Öà‚TÛwko»ì›E¬WÒ²€KQTbÁ‹±U„Ÿàï‘ï«J"ÊMZÀÚwðdA,ãV|R‰Ä‹dV‹ÌÁÝ¿ÿ²OÕÖa¢a»ãPZH‰/~G%Hƒ«ø‡ ÄjêÎʾjòiˆþ2Ë4¿ 0CÁA5å݇cRM¬ë~’<eAÄJ¸$.Z¢3~[âôåÐüSC¼OP¼6/jp ,DŠñS€°‚9ìÖpœÈ{V‰ýd{¦ï˜˜‚ah7œƒíÑÃÚÜÉÛˆ“Ž0J™6MMù²ø}pù8Fl˨OO¿L†Ç2qäv¨X/@¿i¸PÞ(Ø+DƒI1÷e0:üTûš7÷…Leh÷£º²ÚLsÜ°é
+D6k[c›õ^¡Iü‰R$0˜ Nág„¶‚G,á¾ÊoÉ£U’¡þêÙ°3œ¬ßü|9‘M
+‰ƒ<M#ÓÇh›8è©ÏžÆ!‰±q&ûƒ!ðhå„!µ’ð…óøðKÉjñµ‡s®Sà6„!ü&
+tpÅšJ”.97¤Ý?õÅnýOzdi=Þd!­Â;•‘øŇ•„
+kæ´žî5ä?W¥PÏ[3{ïð^£X¯¨G Ò ÖdLÚ]lahù%"Sº'{™ƒ mvNFŠ®C§ü¿#¥ëS¬®²¡É/ŒE)´™§(Z(¨ çÛë¡äíXR‹“"*¾¯Š†[Lغ¹‰5B™ÚV£_Õ;sÓ7™`ƒç‘6OÌV×G³Ö}7æÿéë—M CyUÚöþò¿¨¤ÍØÎaÉ_'œëaH_Múç'3Ö_ÅκÍÓn}ج°?¹v­WͧűYá´&SƳ:!ïú—m²6 ¢^J*n¤ZÌL_±ÀJ´é' ׊7@ Y‘ŽÉž¾7ørÖåiý¡Dˆ 2á}ÙùC{“‘`¸¡­šú±èA®<ð}2µí¡òÇ2ÌÐdh•6‡8Â.&´ ךã^ˆ+”À¯ †V‚³ÕC)`eX9ËÉmðUacŽÅg'éÚ ®lhšŽ0“ûcÏ‚ - Âþ 1ð  >Þ-žQ–Yj S7Y0à<2º¾ÎéQ­ŒŠ[ö!ñÀ³ÍS%¢ÜÖ¡¹TÜŽ -U¹•2ßòkÑÍŽœšüÍ"öYè%–ÅÇ"ò*ªŒ¾I¬½áN•Ó]LÐ1V—ùE ®QeáO³”-ùNˆá‡”ßãȘ6ÞQª¹Œ3ßÆ"yí;CZµ%ŸÞzÚŒK—˱MÏ  'BÛê¡dA£1¬‘¿´ïó_ÆË$»Ž†¢+Èþ
+|Ø7cgè]d*íšû@ðÛªb9É ²! <¼æv:¯…ã{m—46S&絈¸¦ô®È“?FûxÍpd°?Źy?Š1@bÈÐlìDÇ>€4½ã‹hyÉÐÄ„´dÑaýbÚÈ€…§–[<³nÌ™Œ«¦³ó{ñu ¸}ˆ$L” 6Ö³X&ƒ
+;â'x¨_F¼#“¬¤".PM5¿é¼dKhèµ¹ŠÈŠ¹Ckð*„n{|ÿ8 ̘Ÿ™{z­+ƒRh
+½Ýˆÿ3¸êå`0³“qÜ+ÖÃù¨âDb/æéfÐ[o@<ù†‚†‡I"çYIÖT A.³®>Œ*µ=Ë’å:fCù»u$¿:³ |Àn W˜<T\½ç÷~G@d’X
+Ø[¦²)«„ášõ¸˜WI…Ég$å4§ vgˆ‹!$9ÿÒω¯²c0íø@>+,Ëc‘-(Ù’œ·}(ѧD"MØhQy8‡
+Ö«ÎÓ«(irTYXÅ?uéE5b+-Äs¾=VIÅxÉ Oc°Û¬®% —™ßϹ!ç~›+
+=*ÑÔ2vÊû3º~ZQn¼-à§YÙS¾¥DYRL‡â»Ï*ü 4JÌ­ÃJÈ£6™Ù™¡°Iܱ”¦kS¤0Éõùgˆ(YáÞB°c—åÍ®ËîÓÁ)Î6¡ãV†òSÇŒx„AÖñ@™'T ˜©²ÐýPô±Šº‚ùµVw˜Øá‰U—&íŠW¢ØŒuʧ‡SA&â™A,è´ÑdT*¾5- Ôù87Âäô½9èdÖ»ƒXS=µZYîrtÖ‡qÞu„>Ruã‡sé`ûZl›VÙü¦ˆ&Z [ÅR L©;r°8@ô,cŸêh±&,Ó_…¬êÚì
+¥”à¿0ë [ò»Ò 
+~ ¾:—$"Ic(üüçTĶÉôÁ0¸áÇCÑWëx¸2TH$¬AÅÈãÓo>öÐÁ«>ÌáʇyÞYE¸uK˜Z>º€ÞäÚ£Å}›Ê 84%CiD;9(ÌÅM<
+J!7ÐF§a½ô\$}+‰nµÂ¯ÓÂDî’DÁjÖë´w°«„‰PÐy¥ ó¾Á—¢UÔdDe…ŽÊE¦×Ù»é
+
+‹²æêÜ>D] €µžŽ´Ä¼É I™ëón1_³žxw…“ÆÙÖrfPÂÈ1Ñ1Q8dN.=úq–_+jFE•Ñ´¯ûõ0뤜¶–s°¼+¿e%ÌÓ™
+ ”ýC¢tTfÀM>Iz„æ@óÈúâ
+¹ûF6ÍÁ¦F Æ—Ó¶TÉíC#Sxü<×\èŠ-WŒP‰®Tä/ÕcêJ±­`ÉZ²U jv…KÕÅíGÐ2ó"x×O©ù&ÅëpÅ“"kè—U’•rBâ3u•Tìùª_ž”a2¤QÕeç¾JxƒÚÔ Ôü†=Àyu¨„œDŽ¸br"6Gy¾UtFÀUáÔMÕBJH Æ)‘½ÅNá
+Ž”8™Mhèq[%XZ–9]Ê0+SB’»S‹A{¯ëS´sòðÁ(à>#§‡’ *A–FiêReMä `¿äW¦çØSŒêf-~aÈF£ÒÙJHS]én„ÚW ñ¨æ¤öOM|δWçî‚Yîb•j#ïœF⃼TbT†uY=ÈTŤ2¹È6Ûñ>1E‘¥Þä°ü_Ù’
+DxÊÓš(­G±&„F•†C½ÃélwqnÚ3,Ú1·#Ë*ÃwàÈSÉ=ïè¹U:ÇÎ×x(ú8EÅ]¢,†@ö—~ÓÖ¡ù8Gà Ô :ÃJðÀü+ü%o¾…L74®º{, žiáØÎÄ°.ÏßR¶Yaˆ@S¢¯ï@ôEcîÓ¼ UûÊ<kîxq=ª±±ÁÜȯ‘œ•D™xe³òÎ( ¤iî/±ÿˆ ã†{é×\{'de‰ß÷î±,K–¶KØ;iàÁ“ͽ ·"Æ!nhézncbÚ‰
+ë-N1)* , b˜Øa]b\ ~;§ü½!½
+ÁmÒ¶¾©TôA Ʊ–1¨üÜ똇¢_E"žIËÀÀ3S…ÉÛ´F…¸*Ejš%I‰•ØÑDƒ$×
+º‚+„6ŃEüÏ>Tå¥8“WýÏxÙ+grë@ô ü"Tÿºœ*õlÝLÞ÷÷i£²fð•d¶v¡™!tŸ¾Â"£–…óˆ¡?§&9)kÓZèãßµ[ƒ‘Î`°4eÁ'LplO†͆*ü­Påè¾
+é$0x-êòôÚk@À*u! EË2ÎjßÐ.*ã -’â"K9둃!´R] ùï?¢"¾PÔŒ_âeæ»þ¼å ±3€æ(BT„<@˜Ü›{â¡Ô$Íϧ_˜$÷CKÿ)[0a:Îγ7j1¡•cõŒÆ¨
+L\"sL‡Ð’³reK33ˆÑšr-Îͺ˜Ü` t3_Xô°À¬Dš¥jÑ‹ç rÄ^ö‚A;%¨Ù 4k[¡x†Á~b.–«Yß·_…i !…~årí::¸¤ÍüšZ*¢§ûE„,RÖ?Â)äŽ-®Xð›¯ÑŠ`ݘðåÚ‘~a•Xý‡ex*£!âè]4áð)Ñd8¶Ä›œlÌÄj†Bø­ ~š² ìÛùv‘Ê^à gá„Ü¿ü;,Rï7ó²%>°M£+Ü­¨ˆóÙ|jèBi¤E*k÷­s†ÃgdŠ×+D³´™éØ>s@‹MŒ–‡wÖÖÕŽc0n=«Œ±ÌÄW”Œ
+’T"» µé,œ°±ŠX„…%yc(²J„*jlP„éj^I€S õñ¢ífÀ¨!õ/&0Paع[1¡sÊ0²®=@æz´·«Ç \û#ŸT¨ÞR;Â
+n#€lfèÏ)Ù Â:ƒž{3™vÒ©V9(ùõ“Ùy¿,ÞH‚ »;p¬y"E¬N¬ú¥Åš/Ì~“ûœ1ûúe÷¢=ft
+/ä9xLP¤|ÊPr9%ŒlÄ'u)> )•¼ÛëÎñÀÊ!ÕgÊ3dĪ×Uº'dÀŒËñ§
+J‘_°cⶉ³Xvgó_°ã³(`GŠøž20Ç|íÆWt¤‚p_ÆÂXˆŽ ë„P-ÇèÈ5Ó
+TMïFê tLÜyó!3{ØèÈ©ó·‚´+Ô¿oeÔÑÑšø
+u¼+—4áéÙ½ûCÆm±‚ö³lgÉ'"Úeê0/a¼õ9@;¼Si¦
+$µÏ¢ò14Æ&—˜›?¬áe0 KC›{óÝŒŠ*~[Љ´_Nƒ8Rõêè©(!Õ´²¼„ÁûæCrˆºé9ÔëÜŸ˜’#hZ!EmбF†Vvo%34( a J~ùHP²ôŒj{ñ>»æ>VÍ!8ù{rd)òÒ§ïW$J@Z»ŽÎ«Ñ.¢iþvo[Üé© ÅpÇ‹]§ ќӄ%[×ÞM_Ï èÙò
+g±|Ý áÅX<j ‹‡¸ºˆƒïÑËîE?YÏwßb«$:1ëB„±šf¦of*©L7`Ønqz¡h"r|>UõEâuÈ6! õÎÃâ; ñ5#9²Ö¸tI¹ÆÈ›O5œåÉø‹ò¢'Èý‡š«¬)¬IxÁ•ydÒ«Ó‹DœŠþe¼NŽìˆ €Z@hK¯gê(/t¥ü¿ê%€&Å™ž.ŒàšP••Ëž3¿,²Þqìwii­ôÞ÷zZç´ûþ³ŠŸ¾T©UˆÈs³ÂŸ‹~ý?³(Ç|Š^ƒx¦{dÉ9òDȼÉmëÄâh„´(q›¦Õ°°ÿöÍ |ñŒ¦ac\ÆzMaÞTÿ\&Í‘ÝÙZEgK'‘ÇŒLø@Ô
+VÚ-çkɇõv­ g3Éx]zÈÐù&g˜|[þV”bêbEÏÈaò²wtÖž-Æ&tü'b
+é)‘aÚ“(ܧbÙgÉ;Ó[Ãa‡•r`ø´~iä=¾ÓÑþ–§ÎYq&Ò²(FÑFE‰v7_êÝâL²Zá6»ì¯¸ø=ÎfÓJTBÔ'·P³¯)Ùò:‹V¯s]Æìîò¸QB( ÉÈóé2qå…2ÀºŽŠYCñÐÔ×Q0ðãv{ß1ÝÓþW¶£Ï’‹œ8³m µ7›ÊcØ„’~pðqàã¨ü´Gÿ„‹s)`²Êž´)%"Hóñ¶ø˜jZÓâ(Ö{û¢„a¿£¾ƒ×QŸŠ° kÅCZôyåV8ÕDÝÛˆïù.až:JH£‘ÅM–eûX(¤äï<k5Ð>ì µlŸ
+H‰”—Ar\9DO ;Ô¦‚
+’` ž¯ šÅ‹a•ƒú¦2![Ò *“?ª™'Eqê«“<í©¤ŠŸS,_|¯îô
+s+íÌé¾yuoµ&ùE—}ùg™®®C
+u8»éò{^U™L9Ö€É7çÐDetƒ<ÖV4°¼8 `«…ðé¯=h¶=ókOe Oá>îÕhYèF3³òSÌ¡dWT^ÑLSˆÎ {DÌ é< £Œ³®<?Û Tî¯mŸ4* iô > !|i0úùäâ,u±1‰[UO.n–”ih¸'ÆoœO•R)ªÇ$ÝŒáR´$î•_@.¦ ZßÊóN®>þšÓ–_ÇD]$z1XŸß€RLH?ϵŒkR‹SÓI´5µÓ1ÎB®¶^ÛÐÑ@;Úöf¥QH×_…ܵ‹m4"é‘ñw™çà!sìSÈ"'ï$DGï4GÏÞIÏć8ã…ï§(õ"xÔ'¤Û­0°
+»IA0
+ß|bù—BýþI¿dk•ÙZȈála7ä‡ó> yâÔ¹ªYÍ$±Ûª0‹6äÒçÑ®”iÄ„DÚ\¨Œ^_õ`Û€*1›+KÔÞ2·Øuµ‚ùg¬1yt¦ÀRù5Wœóºzªü«ÐÁ±: ñ‰VÚIwvUÃŒ »z…‚¥0ÒŠÕRqÐý§KlÊgŠ©Q_OBcX`¹Ší%vO»M;Tç9ÆÍ4„¤Ú .XÏ ·Ú_L$‘·Õ3èKÝ?Ž vL\%Z‰fL:×9±Lž•¸È"øÊŠ˜úz99f°¹]‹ÊÇ^ƒ:Ñ;Çd¢ñŽ,ݤ¸?+™Fúi&ol9Oúß;ÒËtÊÈâ ‹mG,i¸k®ßÎú/_ŠéÜç0OQ¸Nc.Š:ÓTsøî7Ay,¾Ã×Pfa¬g+´½¬â¨_N¶Z«]òŒ%Ê ‚O" …–¬ú8·{* ­8©×}ÕzsNÈÀºŽt®=ôÚ9Ø6ðó(Ò2¡wÐÁŠ
+kXga;q>”±¥WN¯XVÍŸ¥S$f2ÓkwF…3š>‘]TgBk\æ[Àb¸ÁYX²’9+ü@ „:«$ãR(š‰Pé„XŽ˜Ê°)ÒO÷'©âM…î)»3ÊHÍBé‘üùt‚–\hhnÄk÷FãèkG[Y&5¤ZúiVžÊyèTÖ@ìömjÝñöMŠIþ°Q×Ä¥T$]eO˜-”)톖}
+/ ‰ÜSÇ´GÑž#;ÙÔ‡¯§£ß´FZcÉO‚¨è
+\è›J5aIË™{€0Pñõ½¤Õð¥†'Ðõqá-ðL|ä·èã2ÞB|Nå6‡ÒýåX ŠCíÒ¨{éd
+F²¯öuÎk^\ê¡œ7¯›¬à8Ò
+¡äŠÓÙC«5²´
+£Ã×´ËïìGw”ε‘`%×À¡x¤ë…0$Ð)˜sQ£„¼PÖ~÷÷ÅRmßõ¨é8(}KË7=¢ŒoÙÌrWþ¬ƒŠ×"l;wÂ;£Z)û R•£c¿G¡Pð x:;ƒçüF*Šs£É¶qCÍ¿RÁ+&MÅ© \¡T°äø”ˆÝùwÅFE2Ì£¤D§€›&¢RÀé§ý”â(Çæ¦
+ÀƒœµN‰‘›è2Ú’/þ ö£Õf¿+^d– åÒüunðíÌ“Ÿ$ý
+Ð"=†
+=$°ðWÚ
+ã@=Žˆ{Ob€TCLP·Ã‚Q…€Òî)3×M´¨þÁ‹ìkÀ;—/}Á_‚ªMtÂàƒ,‹¢ åõƒ¤3˜_÷óMM3¾†ZÓ1®¤˜xÉzìŠ.´ …/f£
+Ä] ‰,6Î!¤I€@»Šph¨ l´'J›,º¸M04 Ûä³lÆ)a”E´Ìâz Ô€m†¶‡oÖk‰>e?Ð(möDY.ÇzsF¾ÚÊÒëùæQ
+³(ºÜrÛýl%LÛŠ&6ãG%ê|éQ#‡Ån£¿¾èÑe2½6`ÒÆ›G!è° ™†:Ÿz6K5ed¸ùAƒ'AÑ' &X4œÓKÉþÐë¼çD¨ù÷.øO
+0œ¥)ÚsDD‹Àã²þAß•ü=¦Ÿgë0örnŒ8 ÀÏ=̆„¨¤Q1M>k|~ì¥è÷7nôëM“nNع ³më3,ªžÊä‡çJขLc ÒÜ%U¸ìPz–é¡a‡Êð=×ó0‹"@Í{¹ßƳzô<&ͦ.-ªÙQ°B»1$õ|gjAñ)+µYÿ‘Id8 ä¯úPÇx0e¹“hƒc@h’_e7®1ú;ÖPA*Íê ¼~†·5¬üâ×\CŸEÙéÛãÿ|Z|É$CL°ü!wCŸ°Y>N×­p?Nn¦#|@`U|Ñ£_*vw_{÷8æ1€™ ¦˜±#†Õº¥|>ô
+†¸*Ç4Å«
+>Ù—Bêè‹ÙH¯òéPj9¾üõà‡ÌÀxêRotäÔÞ+–ÐB¤ó¤”Ùï!æ¶ép»‹0^^úãô5~¨Û M
+ã»EYòµåѦ‚¼ò¶ûFÆ­ÆmO@âÎ0NQì¸ÛGþÃ+±è¸³³M„ŽÜüù•˜ÀX˜Ð½Ï¿Y"ÅKÉ¡7¢†Ù‹œƒé› Á*_ËMêâ·xj³/C¥—ÞŸON…o
+~äœÔÌÈžÒVFIe”¨® ž
+ƒðõ ³xo¸ ­$V¶í ·ÃOt%N´$X
+/i)“¹
+Jö§š¬O¾9§t”¡ó6Ìü)±*E„ÛÙ•ð$å¹ÄbnÇWpÙH™jjפ€´õ
+`^ð«ÅöÐ@J°ð6èítG$Lã1æ2–*¡
+°Cº”°ôoîÀ¶ŠHðgz‰ô$9ÿ´˜‘ÿÿ“â%ÎÆ?€?Øoä˜ê]ŸÇ¬¾˜ùÏk´8 -Dz@=TË €Æob“±&‘úÐlÊç8ÂÌ5e#ùì)¨fËIîÛOã$‡’úJw§E m5 Øx
+ˆuXµåfì(á*V®¯ø,ž%…ù•š4ÃÑÇâ‘ "©a9|îÏ¢¹°l¼Æ.EƒKÀ$³#û¬w§Ì#ïXRî­xôC)cŸ¦œE ”ÑÔ_„RøBê±F\à:ÙOë S`rÙ®Á$fèÝKžyØìX*üàRRwG ÿ’±çP
+óFýzVÊEWª,ÔÙªŸV;®fWànØü_:kÎ;ò¢¨·×<*zI1a|«ˆX 7àyQÇij¨¢DØ4ä¬áM}¥žÆØW¹QZh2¸:/ðaAh÷x,ŸñCx‾%I<s¶a¢ÜB Ú37RÓ«ëkìeÂ0«­J£Zp<E³íY°AÁâ,æg]?
+QŽÄWÛemnb)¬ulZâu\‹žÎyL¬¡ 4¹Bƒ~&klx}»û­È#J.ÍÏá×ÓîD>/b à5­‡ø£¿MAë—Æb"Ô³Ÿ+w¡ sO&'éá´r<ì'w<E”Ãê~W‘ƒL~rß—úÓ}5týÁ})¥­ ¬É½–o¨NµÊ Ü«:lÎS”„58°(Õµ¡.bãqö±àè–v‰ v±¨+3¨øð/ñ(º!~ÎïŽÕJ@ صKnìÙHÍKx”Æ Ì£ç<¹òvVôÌâYôô=åsw)(Ûwk¶Ç?¦¶C"Ð\_ôr#H™³3{B³7:ªœ´L*ðÇŠ¡ñ¸Vy˜åÏK£Ö©º">Õ@ì0@ *ö(Ð;䉘”®{=e‘f•Fç$¹¸m¢C[]_ÑE Ê…©#¤Ñ>¾Á?¾±ã¯±wÔ®©ŒùU·X€d ÷"”àú„&–.ê%®É¾e‘%\Tk‰Á±C]íÒe—j
+ê°oÆå¡w¡iÕþ§ÿHê[H+'R
+~$@4‡®Ï¨
+2œÚ—½ñÃì¯5FªÐ.—ƒÏ²S¢&Ó%ìÙu–ßi ²ƒ(r÷|
+ŠØ ø­²?½:–HÍf{qE›BÀO†AN‚œºø`[/&Ö;Þ’LjÜÌui¤àR
+
+„
+$D@ì—÷QwsÖ®¥¯7=;<„’,F £&wR’uÒsâÎw.Ø<ïrƒÏyÒ b\-Zƒö ‰{d1ôIòUÉ¿çôyöŽMðnèÙ¡÷µw9$WºúBœš;.8‡=Š¾Þ¸Ñâ‹dÞ§ÂMĨìhÿ·¡7$^~!¢¸ÚŸ>F& Úâ;OüE׉Ä*BÙ®øSÃßx@ æů u¨>M™°:Ên%ƒw¢ f—Y¢éãø;E÷,РÂ5Xëµï”pT—Âo²G6§ß‹f²¤|ÎZÖOà­ym#KÖ‹Xmš§•”.û0,‡lG‚¨y·/ƒtt6|ÿöÌÄE<$â/¦ð°Õd&F¹ŠtÝsÞ? |—æA!÷;éKÄþ²å#ì¨ ) ¤La¡FžÀlm?¸//â"eU¸0K!ìî`£ø[ìQyG%9“ñ¨átc@—ÉLÝÖø:cà{­*5áOwî‘ã°¡†ñ eaôOõ¯Ã<Õÿã~Ú²š4ig›¬€kÄSÑ*m15ؾ^!ç|—¨`L±pi[¬!S[Së-¢°™= S±•4cRjŠ6°
+¤On7hA?Ogïg`{ÇqÞˆGWtÂÊ–Õeþ© ª)»æÜ™æmÎ)
+llļîw°Õ$¼‚M΄…}–qH<sZIfe Ûùo~fdÃ̆P±?yÀE%íi5DQë##Ïw:*)P’
+d}b5gêÌ49¿Äî"ÐŒåM¢j‹5: ÀAFJÛ2ö.C‹õŒvØkP) ®Åuž}[\§}ÎwžCà2ê?Ó²\áÍòJNN2«qõÄ$ˆ?Éö¿ªÁ7°àÒJ÷âr_n K<i D.ü#Šå'Ã<4-Ûe¥W)ž #øRѺÿ,‚\ e",v°$»èAØØ@g󤂒­„&K‚ð|rßo.w]á¥ñ•¯yû=…j¼/í{J¬(–¸ €ÚŠt+1þG#‰ÍK²‚ añHÉõ7ŽÞ»J…òÁ²¤ª{·èŽ$: H& „Ù_ÖTSkun€JD; Žüµ€¬¡Šiׇèhe.ðw,ö|^Ålñ­ø÷ž„cq³yÀôî~<kû!¦¹¹‰û4¿¸è™Iúá8(K%ËÆVq‘ Šž°Ó4Å¥
+1Ò¹^”On¨MnÉC!¾³V–èòdk
+­'ÇŒe`A™ÌKz‚Ð÷*ʘp¨†ö3‚üõ¬•ÍaQÔ`J¾ÌÓ6×!®¹Å:š"cŸð8N£=÷Yt6r%á3ù‹#ËÅý‹þúÉÙ¹JIð\uÖÂJFªAÄ»GÏ™‡4p Ûœ»èÆ2_Î(êÄ!KïÖÜÕ-"_á"Õ»u‡p‡
+0zµóN•tx‡·Óš¼U) î+§)óþsáÏ<,þgþò±¼!ÉôÜí;5CÛl0»²E]Û1¥™Ø;+)zÑ@Ö¹âƒh öÿüË+º†žñ–C •PF}·æ±|_ïlè?Ç;æföHQÄÛªsEµ–ðcËÜ>dA55~¯N‘JÚ$–-p¶iŠaX³z»J(†N0à,üvbl›d‡ù ŠçÁà`Ʊ)±iŒ®Ã¶|§g9téÌÖ‚
+aÓ¨YxŒ•ðzX/!ú–Î)JÊ­t³B¶~·>Ía‚À–œ¢/+*ìE"õS¯H%p~dt|ëp+ZFáCíJd±
+" ùÞ²ïUC`€k±ŠåÐ8鶃:¦¸!6Pª¡-«[oq®ãøW ÂIÚ¨lË{AÉ3éõà=ëU
+3*ÇÆÞìՔ׫
+½µÄjÂ:DWcψ~›,ÞA¸!¬5 䔃ž Ô^gA˜ÿý1\F–0sAìØ{ŽÄEÇtˆÑmˆ^¢x”³`šþYŽ«º€þ!¯Ÿ Á;gÂäÐ<@×Û-Žþ}Q#7cÙ¢Jc2“Ç3¶äš*Ù@I´‹¼/~Y=Å# ñ«Œ‹;Ülò?•‹C<nˆª6“JIR5éüíìÑy•ÐæŽyýÐü1Øø¹¥¦áˆ`QHw8O‚Aûá.ì%„ˆ€5¦?>`@0k(Žš[/•Ô}Ä‚q׆T<V æÞ©¤A·H®¼f'ÿ¬|Á¥“FˆIùtP)ÉóW3nÅ5™Ã¦îúaY‹òVQ¶r -[ö9€š¡0–bõ` Ãód2* 'ÉIìïÕj f·¶¨pKÚÄ;™æ³µPÌêÓ!”¦ý³ÄçâÙ1¿øÄl±Ïª$²mýTáRêÎ-x»Cb²Šë¸×%ÙCf@˜³&ç¸Úv,Žf½J?Oǹ4î’IÚÔÔ‘X¢ƒuƒ"vÇõÎ
+9A
+ôÞx'‘"i¼Aô¥éef<0ù&ÇRŠiåLCHgU2Ö];¸ÎÄË…f9ÖBã|IJH-LóÞˆùc‡®J¼q/Ô¡ƒPgÙàfòm¢í¤òpXÝ‹~$ñnk‘u!¶nSA<ˆõÿF›â ؽÅv)êr+íêð÷`@#æw|ÀT‘2Šs´.³Œá¶³8°+£“ (ìÃ?…øpx–+§ Ÿª œDk”‡°Iynµùí0ãáZÆ3ò¯ æóOÖË…ï%þ‹euJë £óØöʬuõ–w¥bp3~e‚‚€ÀaîË/?§ùQe;6DŸ%A½_ ?ýS’6M_üî=Y¾q‰ëΑ±´Øy=“šCt+¶íÇåÓb Õœy8û^@ïÎ'k¨2,•ÒçB. ó€£GÍRÿôvŒ*MÉŠˆ–÷f 
+Å—ë{Í~æ93Ï+=GïQ™÷ñ}T÷9ß_zôó÷[¢uJ¾N|wS”lòv–ÏPwe¶<Áª:ü€s±Â¥d~ƒáÖá+‚C”&%<©n1D²£hƯƒ“dÐoæ6E#ȤX­Ô‘š²Á@ÀüZ£|¡¥[Ëß¹ý˜šPX“¥º¿W1äΡè|Ž’]¸V_ž¹s+„¯yf‹u.¤ÇD|õ/áŸÂ#ç9cœ5õø„%Î;ð&΃ôE±k”G_6“ ]ææ»M2BÀ73ƒôñ×ó¥ëŸ7L•ÅħLáSÌq5E:_ƒåE°Ø:o6׆'ñ&U,|„›JÊ6,C˜ü§!2ÉôÖÍb1Õ°åænè%™o:Ñ7¾¥Ç¬ŸV¾Ñã$x˜mBÙÏn×÷P.ãüšˆVÃÉ
+1%¸¨´ç-ÔŒ!¬vnÅb9x+Wøé«ÀDÈFtQ?³Zý=X‘*_K+ H
+n¾yS¯äÒ]Ò¡˜QRñºµBº‹Ç¬¢–¼Iá új_w‘'7ÆÉa¹ËˆýA;³T6r-¢fmùºbÔoROløÌë›MML0Ößç¸a„Àøi#ÙÁÐÕ1Àe±<Œ·$^#"ä` D/CC5¦|sÄk1¿–0FïØݬÀD¤j‘»jô›ž
+ÆõÎo _Fôã¯è9ì€ÒhãÐÄõz_Àëç~z'…ën½ƒœ`òü`Ãá‰[×útŸ;)£c>‹£‘f1†ÏÏÎÕ…´8DÅ8&^s¶ ÈÒhôÆ.æ#dŒÊ„ÀVþ˜¬®®ñŒ‰cî3¼;j… rBXÖ!"cȵ@GÁ°Ø(tðÃ?5K“Ò‘×ó¹1ÜÁX¸Ì@Æq
+ɳa‘ý[|66#ÏûÍñ£’šÂXL}24Øš2Þ<7Çøp”‰ž
+GpFE¹¶‹Œ°7My®7µ¼u󹦙à7­VïÞ]Pțà I®)NÃè²;°î0‡”Šxɺ­ÃÉÿŒeSýSý‡¶2ÃÓãX‰aÕ-1U;»ŒÄ0J8QÑÐSzIšÃ×Hr/ô4!ÞI^c:^A_<Ô甯ʆ…X²×åÄعìN§5 ÒåÞ9¼‰C"A=ëÇÓ·Ú‰\ìuë›E½4óat5³`Ê­ÖnvÙ÷%¿/þ:ŒÂ7±Ø¼y–= Ž 3ü9T!äøâݵb@v½Š2‡æ¿3c 9ôõº­Kæ0‰cóÚ«ù¶u™ °Êð
+c2^ÆëX\iu'èà §eøS$.Ôø¹‚JBšƒä)îŒ ÕŒšu £ôÙ;ÎÌ£
+Z°Ñ©"PäÐ×h||Êö"敲vË°k@óãl:<.µ øØ9ÕPiüðwý§–k8Â9aÇX~óÂÄk:”$ÄË\ùp¡kz&pjÂÖvl@,$H½{ëQ…#Bloº†ñÕ…ñÉÊÖ²Au{Ž. %zé«õY<ÈQ±–­¼I«Ø ëÅ=$™Ðƒ#Á)nzÿÇ"ù‰~ Eûýé9£hK
+Ø„Å\t•u§±)§9Öò{JùþÓ®ÓQc®ûá×‰Æ ¢ðÄò±þІ*Í`.];m¿²5ó%–d/Bò)4¿RƒÃÄò]¯ÆíXÄÀ½±Å ðÜ÷0B-ª
+ó·îÒ¤¡«ˆ¶W†!Y*ômìX”:"ÄRŠGé0ò &©+„ûøÇØ,m{ÑTKx݃ÌÀ¡È{Eĸõ}1HO€ (ûO7ÝNRq2éùÚ††G×¥2]¢0‰I4
+C¢Î'
+N#p#Ò½>QØ·GPÛâ¿GüÛLq¨dMÏby¦¸w
+sÃþÑoÎÊ;_¸"ƒšL[[¸Ä\±½Ì;[ÐÊn û&\š6Ñ„çÙï$½%Ý윮V‚pÊg—& (³Å
+¹HõÎ
+ŽŒ%νGÉÎ6Fp¢
+ÌÞ‹CHÆQ"|d"Ý{!ß;µoºÅÐ:Î Û¢»É€1ì “ÉÆûo‚Ðq µoº—95¤bHüÍ«/mð‘4U$æöé²FâÚïÛ9ÞÔüŒ7|·7yCŒfaØ™ná­~!ê/g? ùÞ§÷³wPùÃñùÈèoøµº9À…&–ì>N:1wt.»}¾ÑÇñ™¡‚„+z …õåeódŒØlf^ÒŒ2ó¹ÕŸ@è0s”ê!„.kœŠ"X!4£ÁÙ€óZ¯úÐÿ™WJpbúPÚ‚»íI8AÑQ£E%„(Ó[бPõtBtUC!ãg90ä%ds:£›H³TÇ×\f4)u©ûÖ¤X±.áEÃx”c«aÜOš¼)Ñ;fê–SºØ‘yZló~œXH(4÷Ù—UákùnçÜ›p¡¯—è’ÔGäf÷U×™ˆ¨M¹Kä¥L–3X*®\èçé†`&fצ‡‰¬=7ýûkàƒ <N©²ÓEÛwPÕÞº›màïAQj–g`W³ô!è´MÒǶEþ‘ BwÉr2õΘåq<µ²R7‹‹ ÂG­‚“ÃcÑ3zj…;ÞB«Ù—+LÈ5¸œºr…ˆ¦!Ua²#Á„³-ž[wç=Hú’~†wÞAycã
+Ž§£½ƒ$eÊBh~ŽÁªûQ`rj€ó~Ù ëL°Šòƒ[±³ )ÛúæÍ:67F®5oÝ´|oàõ¾ê‡ÅúC:áÅ)à‚¨õ,U2p‚œ/ ‚¸¸¸yÐì¯ …¸€Û
+‚ þ I’;•’¦ßÕùb¿ì*À¨*†ÊçJM#fnŸVeëEJéi¯~èùûoˆ-0E¨ÕY5ì2ÿ€ž^¢d=‹`*
+KÁš£71!)r‚õòi|K&6‡^ñô
+-CÕÌ*m¼obqK\è{äY<2ý±LœÃJ2 ZÇ‘ËÙ-L
+,ÀF]!MÇXŒv4’¯UNk×½ ‹*ñQ㥠ÄNäƒÌŸ¹æâ”hº!µW*k) ƒì)Â%[­¦•#Ó^Š$`ƒ…Ü„!¨Ÿƒ}ÈËÆi÷4üªÂn錒ÖüHÙ9‘—$FC>äY°HŸu“ô-W`'Ýïë"’¥ODR\ï§H(IÍÍ^ðRf eç¹.Ae9îÝ„¢ÉŽõÌñÌ_!  }üñ&Ì7G
+—S±c²Â0užÊ  Z˜úNuú ~€)g*ð©3ÁâÉœ©2®.¾Eù6¼wñŠ 5¶q #¥ò6Ú‚÷D_ å²+"~RaîDÔ ñ@e¦£S¿¤7
+àù™<JÈ b\¹/ñìt‚s(¤^œ5sº‰fRé9§Œ‡9“&]Ç®ï"T!!úyÙÙòÎœq„¤œ¡­tUŽdÓ#+¥ÑÎ=¶Wc¸ŒÏØXQÿù$¨èîñ½«:’ßÀ1»ùšúƒé0;KD¯¥™t'liÀÞAÖà
+q9QAÉ:ªÈïÃ_&˜©AúmºfR" GÒ}xç`l;ˆØ”£R/êQ)øË,”$ÛUô=ŒLÓ[öÓÁ
+ Œ}ªR´,?žgÖ‹ÌoZvq à[2ªvu¥âô˳Š¼%[#GoýŽSb
+rŠvŒñ KÄ„truÑ×áåÂ"| dTþ¾çò„ $2°ˆìR uáow °¼d¦Á;ÄͤvG5€|è(&Zÿà_mÖø;<ÈH6Tç C؇Œ[©=æÒ«\$ TÛºÊÒÖ=»q‹@Wʃä'´‹?]-´æÖ36¤ì*[4^‚®7ºÈ§*M1Ã<ö;ðlÜ("ÃQd›Iv‘å¢]t’~¡Æ}†N]—òӱɼYøæ3¬Íµö£ØZ…Æ
+F¯¾^€(«$C&¨>æxOPÑ'ÙâØ}y®…4c-@”T¸Öý([3Øf17oW@ÜX”,¯ZÏœ15ºþÈú7¡›I^Ö‚ä‚i'ÓŠ{ “ÓÄÂ&u’åòš9ø
+†sÙH3ÊE[+e8xï¨Ä .×3[ÒvêTÅál\¬Lt BÍL¶“ù@™Ae.Ðk˜Ü¯CIda÷”,pfz áçNÈPcQY~b¨?›âò,‰#sOœ3´™L„s‰†Ë3÷úMÙ*E+ƒéU`<ìC±à¬
+Ìö˜î…|BÆ“ŸŸ³ì¼&6 ò[S£WÈeÎg܃†´ýdß5‚ãfª™O!ìÎgøAŠçÝ€S¹HOºçŸâ¾?³œ&ŒÍ3§…ÞFð&b§åèf?Y¹CþEŽŽÁdi°ø¬^>ý|MSMæþ¤‰N4Ýdt á® z(GÇüÀJ$XÍ#¯ÜºÅw)åR¶t󘓿ë°bå=u7øZÆ0=µý3U¾$ƒÇc›†™K¬ešp,§ý¦.9Ë”Ñôr¢)
+†øµ¶=1¼7°’Å“£Æ”?é–’¹U{Þ“Ê(/ 6 ¢L*ÍÅÓî1¢É› Ä"â)wŒ@-Îf„2}/gÔK´ Ñâ€cB1ñU†
+K«_‡+â¾´Ž‡ó“G¼óÀe ä÷eKT—)êÍÄßMÀÒ†J<küY×ëEr²ûÍn  ¸"‚0\IÍåì|v€¹ ¯’F:aÕ3šüt]Šð¸€ž÷4÷[òø÷ÍÔf6€/ÁG¨„é˜UÉÁ—@ ê»ÞjÙvXòeEIWwŠ@×óÌŽÓ"|æ
+ÓP€Ñ,_Pµ";ö#¤Î“sµ²R-ô¨#¹²Ü˜£¸sþ'j×µ½ˆ/±…Z<nÑm—ðqxÄÂö—(.Ï+eðÞ¸K‘ ¥E5g-æó'}óöÚ;¢êÒUJ#Ú;ˆ6U"°4õ§+Å^AVÙ?Gþ²Ÿ|Ž@ó¤i1i™ÉŠ :Šx¤/Æ«çø;%<SW`ýô8hk‡˜qc4”ÅfÙª…_ þz‹@·J£ÀxO,ÅàÀw#ÀÜ{Ïa”N/y¼]o”Ôj;fBú¨ˆôàK áYF”,ñstrÒ*x4>ð”Ä?§ÚÅСG Äê'õV,Z+JIëà+ > ¿Ìì×q":
+ž ¹ÊòØÌ}lz9Nƒ‚±Öü&âjŽÀÀi¼•y€¿CÓ?°ª‰—qĨyÌÀ­2:‚0£¬k cqÇby!ÛøBLB–±Ü byñ2{—K¥>¸¤c#Œå©·s›ßB‹ˆ]â¼µŽ§5ç æˆm 5hc®8ê5äó'­õ `Ñ[μÃ3ð«_Âd¨…d“êˆ+yÇÜãSpØôùƒù½ÉAr§ßâòðÚh¤gˆˆ}³ðÚwLpíÛYÁ­¿»_Z]¢=Ì£uŒª¾´Þë 䱦¨®nbšeñjC•êcȧ¦Í.…3H'ð$‘.«êŒ|Ô™«y ˜pµmÇN"$I. Çåˆÿç¾²žÄ" âq¹²çîËøÓ×¢C°7®"«øA׺+äã­/%î¢=«=À€À!Q–\·ëõ2@T[ŠL¥ÍÃÎýQñ3üáZ+( íN_xvH½Ž!öÉ
+0è0Æ…\¥ÜÞ…žtq=z8^<‰ð´p6R€zœ”FfHƆ‹?‚òŠ!ð˜¾\Â.] >·nß¿ÌÌŸw 'ïò¢x€ó•eäí«` ¤õúåyëûmúuìœ:4ÕP¿1…Ú
+%…·T€èÃÜ%ŠS¼?%™J¤Â/É6¹%Ðq¯Ú@7Äæ5:Ç`52ã»ÒIO”lŒ¡ªD¾ ؃ÃÙOÚà˧¹KDE¶†Æ÷ñNôâ]EtƒO”ðBMÌÆõšv¹ê]j#%=_³Ÿ'¢0_þT²^n40“4jߪð\¤9=øPÒoÏš%€!%'ïd,“Pèàžç ²D\‘s*3°eÞ4™ €1Fi¤ø>}ÊþÒO@æ“0/ªyr/çù-/aD3É•açѺú
+ÌH¡‘@iìuG‘`93;r²ö”{¹æÌ‚’5.¦¥%tzQ’þ@“8ô”$¹!ä…M\ÌìJjIÀ<‡°²Œ<k ~(*Ÿö£ï"|’$„Di XF,/dÐœ¹ìÆe­ìFÙ5˜¢ì
+>DF2ª
+p(¶êárl °óp¾1¤ª8ˆ¥ì\ž`·Zó bµœÃ4ÐQK×—¾ÃJOø—N`«Î‡,!TLe‹€Þë,W)9—Îßh@ÁÑË>ähãô8[ÄüMaXøÖ…-ŸËA)þ¹ž‘Š»=L:ãÕu³õ dMœÿ ññ¶lôs0!©K’„ê\uÐ&0­u “†Â§³í/Aâ\–µ*­uÉ{f}ùóWq-Tˆ;¢©i³ä“_‰9Öð†¬Ýü±Kª#«ìò‚rOÀ;*‚;ÚG{ú'Íi¡áÌ@Pý!kh|Òœù¢Ë"Ô³QZ?8óE‚x*4qçÜÚøkÇÏ¢¸¹xÄÜÔàäY*¹ÈOùrÊ@yVèqç‘ÀˆÉŠ´Õe’ »åwýhq¯´¡Vž·—¨èÙs™
+NÕVåÐÐ:G5| JÁ´1W_í ‹h8]0µ½«9L “À\ªt46£œi­ þü–™)£ÿãÈ0ì™ÒfÙ ³°!kú< D´Ùá6*»þ;Ú"Gg`S\5T:§?R fp!Èâs衉A€Žòi¯%X¾ø–4‰´ ’·µËýkק@;rf$0»Œ¥A™­` »ÎGeag¡§;Œ–.ƒ%‰¥JØ!¯ü„´'·Ö³U ‹ÿx{‰ŠnC‡:™0Û‚½QûeÍ-ÀübC×"ò ËA¥Ð JÂz¡Œtù¸°†+TÞ¦xˆ“­ûJzRÜ% ˆz'ˆ[%2t8VU»„hÐe·r]bBQ!aEHt§†SëbD
+ ±²(ä‚ æ¶æÑÇî„ñã_4‡á4')§©v]3xò
+eWQ¢¿ÌZ¼Ö,×X}ʱ|ªñ$ôe ÈBjæi°½œœA 3ËÌ̳À0½çoÎÁaç@"Fè<€=[[[œyTÓRÔ4vH §""Õ.†}{ jn³ð’~,ä'Åó
+jîóÊü?ÃVŒ–òwEÏ­
+Ç ¸Á
+ƒS^)I$Up€h÷m¬å¸WYOIÅÔ³ÛYçÌ]ÒC·ÔÜTŠš"h…äÄvQ‚†îï¦s4?Ø^Ñ`\ù&~$§1ãgM‹ôEÏ{B-èFZòN·S&ïD
+Š€«á?~TS 5
+C@ä¬üNƒ[Ûm=Ènr#Òä„AB
+œ@cJZ¬Ü•µ&Z!x nIy«äD!>:Î{‹¬l„šXÆZ¢QH‘îœÌñr¯z2<ê“
+`¦!U0ëôÞ(+„ÿl¾Æª¾šR›hDc´k "Ö–Lã`Km¦#>æ k$¶–3Dû—e™ý᪌x
+¬Iºåï:Wål»ÿ°d€”´ý60F.†F¸“ÒèÚ(¸jÝ!<,- ¸eŸ-³ÔDÚÔö­p\HÞÉ!¶tPwþÌšB9bU“”5tmbzÈÞ.•èÞ(駊m
+Ë|°Ž’ú€Jÿ>„Ô çwü $…ÐO È,ž¼+缦%³½† Í‚õ)c.‘^¹Õç`9ÒÛÏl€Vp•;uÁ0Pœ*ìÁRµ>…B7¸w¯;Ÿjà9ìEdós¸¢,’_–Êb` ªÈý×Å(øÇÞ-ʧ±tžÎN×üf¬_Ä<²ºHìZdÑ<¬òŠÂ°s Y@!  ƒ~劺?°Â¤ƒå¸`nÛ|¡S¦f•c`eøoâ´HÉØîËh±÷¡
+à{‚û&A83"­ùx‹ƒ H¦kŠ>«wó5ˆvîìSâ=«_‹­¯Ñ '.¨928õð{¨¨à÷y® M¥+§Ö£usšG\!Yû'À
+H‰´—ÍŽ–Ç…¯ ÷ЛH…¦«ú§º“ƒ¥È‹da'Y! c„b@B)¾û<§û'1È(–bÐ|Ó_ýž:u*b̹Z­ÕRµ‘×è±l-Hoõëç­z¶>m®2=Æà‘ç:çXneÖ5SóÈÅ¢O>ñá3ÕÒr/Ÿk‰Þ/Föʯø„{òÕ²Ž¼h³¥§òä=Û(xò¾zßfZ.3«4¬Ô\j”¹ªUãK;’6»"YÍ,5›¹x¯ÊgÅâÁÌCù ·˜—›–k7…k‘eµžËÄ„—VêôîhE/œ(HvkÅѪÍ<}³í¬L´¼²ÄI…^•QbŽÔjä?Sß¿aæ;™êv=ÄÚ8‘§2+Q¢Žz:ÑKn£Lj¯H17,ÏXê j=µÖs Ú2)*©D‚²Ƀ:3ÁÄ¢Ò‹ö–ͦ-+½‘í©P3µàY£ãÇ‘Y©8
+úL+¡ÐXʼn%ìŽS‹¹¢t>¨Q3ÁÛ*½72ßf)]#[3¥&Œ(&Ú^=ˆ²mâEɃ†-¥Z”V-yš“’ÙÅÓ{žcGm¥X½ÌÔ¨±ZÃ?eÇ3iŽ]°Jy†<÷A(B/iÐÒØ85
+ÀÁÄÅ.oJ)µée›`ºËÈU]᎙àsî!ƒêöPÐ?pÒJ ¯:s&C@|ž©åÐ ù öÞn09d®âY
++8úbØÿÏÞþÔå²ôˆ,»`%z+øÿì©Q¥Né”zÕÒÍËO$…+x<W1š¹_à*ºØß;$;éÏJ7ÿ·¬4Ÿ™• \bïd%Š-éæ†?ùp÷Å›—woÞ¿{ñá‡ô;}ôŸ?§Çz÷ÕíË÷^—ßnôü,uûâû?¾¸ûðæŸ<Nž>ùòÈþüÝûoϯnS¿Ož¼zÿííó'_®çòõÝßß>ÿ·ûëvÀ–ž½ºÇ°ÿ" ßÿ}
+Gm=ÊàÛ‰Še¬-loyè‚×QÑp–Q'º÷Ós+®xcbëÜ],;\3véxÎæ‘ßìoî VÌŠIª6=í®Ï–ó^>³K¯—2~“¡ã Q”èÜÎ!‹fm+1žvWBú1-4FœZ ºJ¨û¾Jª³k`ÙìcÓIÄHLÔ¨iEiaIé÷±kà,MÓ¨š&‚•ñ½2BŒq¹×vDZ"~NksQ{MõªRŠ-à <ÜÊQ·§ʼë6©"9y‰+´ 4¹±ï¦¢2D¹s2²ËÁD…Ú>cÔ>R1M¿‹.‡Èéé=@Ù=Ÿøz]ãêògú\('ZÃIó´qicJŽšK5B]¤ƒæZ—ƒuµKˆPžRQ³7FÀhl\‚á›JUÅBë—bi£vénÅQö{åRመ®p~Õ¾Õ'úõ´œß‚Fì è€ ¥ú&O4S2|¡ÎP†–§Ü Xؾ.L˜OÑ#(¤ýq6±$oÛ²™FÄþ6‚€k©"‹>l(4OG+°ˆyË<)˜¥hU²8§å–Zæ¤ uJŒë"2Cqó‡Ë@¯8À)É)(þO>rc$ …­ÎNŸŽ§?rü“[þùÍ?6m±?y½õ›ÎÄ
+\lJTuäÞáµ}ÆuÝNEŠÄ vÔ.‚ˆs«mAõ˜=Qzr'‰à=¦¹¼ Ø#ôBZ“‰”Ûæ5pµlýCîˆ8)ud-U‡ÊØZµ¦YFþŸzÆÙ‡ê,z°¶]Þ4#— íÁ–ò7
+À@1-9ãããè#; ×xÌr¯}¨ii
+GÑT…”‡!b%áÄ”úÞüuÖÎÁþ
+²zS­òJ¦†Œ~¡j#e³hÝ"ŸåÑåøtKu)ò¶’=ÿ!À…¾ï:~ÕîAÕ9&}µ~ž(Î,»ç4RAüë&êÇ©þ##ÂWؼtE#–Ú“°^NP›ËvÈ?Á²ã¶Ë櫓âNcL@è{4ÙÕK@¦‰†É±TsþƒµöÌ3 "{G%Ç)TLÕ½Âèò³(Û±¯îY&§i[‚qšX¹[_¾J7Ð`º¬Âl"ìÐ1<“¤ÌlÑñðý_œØ’Îݨ2'–1¦)lÀqYËmP"¦ê V¶ŒMã¯@¨jaÀ«
+ÔÀàâû0
+ôÓñ¤øÔx°ä4åô"æ¹úÒUöƒj¥dtjòÓ¼J·¦eOk̤IÝ’¸°í´^WSñ2 LQªˆ‰LìòW®%àBtÇN¨t4ïÜàæ@þ.Ã÷3¾3¦ÒÌËb`gê¦ÀÍá%}Ñån籫
+n{™’ЖdäôN7N¹ûžð<f¶e-ârQ0Ðå~Ü=Ä;©‹a²ã½æ
+Ç]aíï‘Á2w­ O‘þŒ<qÓ "ªæ3¾È‹ *É‹Ílù¿é‡„Í/Á:Áçõžä0ýLžN¯ÿæ®óÞbÛqê›úp—?
+ã²tAÏÀX/}åªtð‹õž;©nM·eéñ™g|eÄaNØ:~=þ¯PFæZü
+úë†Ê§¬Þ ñÀ*€…ï€&p!MÊ°‡ªùCLVz7U Ë‰ÍóD܉€F@Y ^_¾¡ ÷:)¢/άCÞâ1$yAÅ‚}°Aì7ÜCÐSï¦á@GH%½kì"$§‚Ñ+¨zÞ¨Ç/i!û(ÔÄ *æ ;)ˆ?Âêra1Hë‹.mÛ&| ã)– oíði¢Ã/Y¢*lYe3 U|OW@À‘:ìÜ0`2e´i÷Ôlg²|×JPŠRá³#H
+QŒà‘pã»ÀKŒ>ÑQìH»¥BåbJ¼ƒŒDEô/k!ضÂI•}áN¤FY­OüF¡gä@‡oð
+X6ëÅ8D«h
+ †PHÏmþšP=²&Ôø4â^•¼{îÞ㿈}¬%üÐÛžQ.†œ‚p<°íO6§Bž²]ToXþÃ÷b’—æÖÌÃòœ¾¦²h­o–ÆÿܱÈÞ¡ôXŒÎ÷{Ïø.y¿j ÷x
+ªÈù‰²éOŽ’
+dq‚Œ6×Í#Îzj§`ä]Ï/ nð€p® DÎÏâ+1‘Z“f09úÛsÒ]"°êº–±åDŒ±ƒÄ—îµ~¯KàVMQ 'ybwh õ
+è}ÕH;
+)å¥ Éßxè—n
+
+ð”y,2(™ºÊ÷κӇeàÛ8‚åpÓ.´K¡I)®E3‚y3n,¯R4Äáñ‰\ €ÊbLðœåÛ „?áwQî¡ò
+À Ö<cY#i›“¹âôó½*Q
+Ÿ@8ã¬gÌ´q–¯¥L?zÝ~T©a"ç
+J.aÔ±HΩàmœ
+fúJ;¹Òòz‹'«VX™¯ÃX0ôrý×/^£ûúå‘Ü
+­ Ç9üsÞ×À§¬A'} 9 ïŒ@o¿‘w~`G†œÑ@ÁNªªÒóè03Õ¨šÌ'Ýᣣ˜Ö/ÉÆo¬YÑ+é>ÀÓBÝ6éòÖG+NÙ±Ž»MSŒ#b²„À•ßé>Òä5Ó„­Ѩ,E¨jTÛÅáSwxbwæM¶€Mp Iæ†æ="ü`‹¸…•ªœzIº8Íi
+v’ Ô½õÈ0$ Ã[~ßÑañh2–ÿœkCe!¤Š„9oãq¬xžüµÒßWèþ¤x‰¸ÂÎ?V¡š{ü@–ÈCó÷\§Ž¯kÙyê¹ÝiêQz:¨n/ "Ô=™](‘áHÞM$`èCžLž «(¨´’†@Dãeé=O\-:à†i*7uœ§àðØè œ ÇÐS3Ä/¼GÿÂy=ñóΠè!°uÌÔËçŒì' ÿ$…T3à!¤
+¸Fwae¯XTÑå4r3 qrj­ÛJãøù‘r©îpj
+êÁ?DfXl70È,@#€“´˜á܇ŪŒ!¶ -¶4’.»hPç­hèQ•°Ða‡PQâåÇj§þ‹®»qQÍ=ÕF@()gÕ›FAŸú‚ïaÈf,Eœ^·¸];PÆÅÄ2R*V¡„H{ÐEP37N»¾l-RÅæИZûBuHj'‚/O²þ,¶¼…0€?ïz'te¢™ õäV¬$'2<ÝB]÷d`,ᔂ [;jäªoå…)Á~€Þ¨‘}e>‘<¢“Øz¸§8p¹ík|¹ ¾„¨ñÜŠÄ(Â7|Øqtãè7㊳$¥Þ˜:QiuÖG…ئ*äVÜ3XB½¯Éõ?‹U`Šávƒ
+¥W C©>i(q“p=/a(ãš‘)Ma„»u4 \ãÛù}Ô9L
+ýr'ÒfVXÖÜñ£eþB¹bB"Õ0ÕÁðÜ:nq£ÃÁph”@ë £Ô‡›Í)|ø‘ÃOGKx¬ôÍ*ÏÊ
+?ýÀæÅç϶@„Î=Sÿ?ÎÖò
+ÇûŽX1c€?þž¿~ûÙãª;Ëþ
+†žºÉP5>Ç{çߣŽëC§¶ÒÀÛÐsÖ›‡uÁ<0 œ@‡»«âWˆ2M&ôœáŠ­ÊþŽÊá ~{ûúY·#‚?•ÿçïÝJ †Ï`÷ò:l.6(f¥ÁLÚì'F”þä†(Š¯eîqÔšÆ`D^phXÿD¬ÿ%=œ÷À¼«y¨k]縨ƒ˜-¥˜3}¼‚õ¼å1ºg Ïé?z#‘gYŽ[ÎÂ>Xìýy ß¡hæŠTÙMdîé;Nu—I@£’±h}£4ƒ¦°„Þû¯_
+ažeß7³
+N­¾´À¦+H&¶|n‚ŠÀŸ‹ÿºƒAÄS5šÃ T—6wæ"·A´`gúg–»Ð„‚hw¶+,/Yt ù¸—S²G[ V Îq(š÷r€ ‰˜È ä¼?€ÜC·D$²Æ̧Ëo™A¶È¢¨=`k?ÈGÒLj«ÃˆlÛ—Cò‡ZqOÑR¸¹¤XŽÆLr‚NÛKûÖ­¹Å¨à5ÆYnžàrdó ¢ƒi#"•e ÙÇiûmâIˆÜ<ð)°Q²¢Òñõyq°h¥9ù
+«ÎÝîJ¢<à5³`™ú¢Ëúåý‡§FýªíE44À –’|,¸buWïÅþÕ?þðšÿ¼xyÿðÕ»ŸÞÝ}¸¾ÿÅýI}úo÷âÛ»‡ïnº»¿!?ZV¿õ‹ïn¯þËõÃý»ÿpØ={õò›¯·äøÇÝýûõWÏíª?»g/oî~¼}ûò›ñ–@¾øåçÛ·ŸßNñ×¢îÍÍa¡ã“
+}þ¯Uu«AuÏž»7ÓK+ë¡%ºK;§†w¢r0;…[4k `Å[AIyUØ)š:ï‚¿V« ]0†•„GcÔsl,Y“cübwA
++éž ýeú"(ÞsB ÅÆùˆ¹àz
+7(”
+3Å}a’2blȈµ¬¡’QÏçW2-$£$õù–„)R°Uÿ;€¶Pð8,û
+V< “˜^B¢¬¨7lé’„5?eŽÀåÆ1¤W]¢Óž‡Xª0xˆP€‹3F‰<öBmeÿ¬ﱩœPƒ
+e<RKƒèªÍ'5¸3Yêlx+”\ÑU+Ê"†¼&¶g%M)>w Õ9c@K»×Þƒ{Àw]
+QÒZ)7^‚eÄ,IÃ/༠¨"ƒ¾™äH$œ÷1‹BC£÷m3#£oómAÒ·«±äæaoؕذìÃÄäH…%i‚¡hU2õÅ*F)<NˆÇy°z
+‹P°DÈŽvEF”žÝÃ(E6œ¡&2VšÌ˜êÀO€áòr¼úd²—ºÅ„‹ÊŽØaÖÇy‹HlÐf»{iÌ€nõ:QTO)ÝlRW;„f¤³s¨l’¥:±†”Cd1À%ä‚”o–Db-Õ"…@ï;<-S&Ô`)¨)äÏÆ8–ŒL´gÀ(NÀ€g¨
+¡Æl!p+É2G”di÷&[2:²H}Ò4T~2Önª‡{Š¨ X5%G–ZøKÀUÖ©u Ú'‹mÅp| mT¢Û†­S
+¦UPm«)v/rq•<o÷4@X¥¼ TinFEDA—¬x|
+NIïJå«xBÞ3¼!êc·ÚW~ÄÕ µ_rKàÆ”"úI"$ÅËÇÜ’5ßÝ]ÜÈWgms¤SyîN~ñP8“à ј/f ü`-x¦
+¢Ißn4gïa5 Ä•ù˜æ€Œd˜iŽº.<!ÚòâÌrˆJâmò¡ÝK“²œ’ë’hXÄÞÝEÉÌx*ÉøHwúªµ8á¤~Èv|oAê9œ,õ=±}†ÌØ7”[–žØ³ÚpQ"4•PÙ."ÚŠtªH3Ûé!
+„%Š*­Â|y™ÖÀ2Á¡)€œ«@vò]XÄ
+Ê)w…½’^J°¦ª’~¥¾\v,9ª(ú+wh:‰wd mø†ÈB Ì„þÖ:‘ÕåºYM·ZX[n«º"#ÎcŸ½÷)´™r³÷'z‘ë~ Ö‹:±½FJ­X`p@W® ï T€Ó…†9hMÇR“tEþ©·Ûêe_Ñkm1oòËCÄ—E)¬„©Ú÷(¥Þ¬:göe<T9J³çÚì'mzoH¾Jz
+qUÆo1ƒtÁ¬/ÛÕ‡7>ºúóh¬&Ÿä†ÏßüS
+Š —NFV¢Ø€Æ›Ixþˆç&žï ¦°)Ôu"9@â`"]"ËVœ7_’çéåÈ:~fX)ý›žšÄãlN|G¶/ÑyþÔª ëºÍ~ã{ZQTžÂ™cv.áùÕòs^QY½â¨°ÿµMë³ùŸ÷ý5‚ãqÖCw<1$¸C¦:»ñ`Íq° ÀÎ]h«”£…¶sØa$zñc0•f½VJ˜+œtÃߺŸÖcÑ\x…6A¹~:0ñœX® ›$ ¨à X
+„0,îuë3ˆÂ•ñ¨–:Ïw© ¡
+vy‡²ô6¤ÙèDQ(‰„‚~÷¶·oNÂï}p¥"º†‹Ua…Lú
+|×¢€‚íü±*¼Ž¯á]ÈK:*R‘£`&5»‰”+“¶W/L3•‰k&¥àú©êóî²~0×
+¸ÙÚa¿­¦Œk¯×Ks·ï[Ëe0ûF|&+L?RëPö²–’ÂJ2<_”—iÂÛCLs$€ ‘žš>¯+§S“›È ²žòœ÷„λPŠ¼ .b~a¤¸§^ƒQ­0ûâè|ózôb«%&Sw§¢±³²•·Ýh¯F™Ž.³Ôl'ŸàIÔb”ÇmßÐÊ3”‡dÏ$ÞÊß¡Þ6·<à«Ñ›/K“=WÉ™¡*‰Y/ßöÚ˜„kàʳdûº7‡ûb{ѹ–²Ðußš}n2Ux\{ѯ•Ü¤GŸ˜ô Çó5[Ñw¨~Sè~óøÝŸXa~þéñÝ?|ÿãÿñÇþ믞ÝëÊö§ÿŒ¡ÿëí)Ôë·#É?Äßü3]¨¾ZD÷JYáòÑ›lÚ{”}=¡²=Ü´†
+ÔPém!¾v“=Jh9Ö‚ÞrfÕ²àP4“´É«(ÄQ º…ÅywðKÌàåªíÐà@iH'4‘a¨(ZÃ?i¦¹¾. !h"er9ì‡ðçPÓyÉ×ýÛzn Ê=þˆÛû^)š¡ÀÅhÈá
+²°QݤŠks?TîQ‚æ²ÒÜ› ©–À->ýs'ÂgI÷ d069ꜹ:#`Å£¾3 `¹8­síÎwšEî »`ièH:›æj+ŸV.].”|äs9 kSËhç ruî¯ÒǪ,Yk UZ4¶²½Ñ²¤C"ÂÜ+@¡®`Š
+ÄÂÅpªº‹Ê ­“`¹Ÿø1(2í‚1Ìó~ Ý¢ö¤ÊbH4'
+>h½s‚E‚’BÒx½s;4õÅ¥k·àCÒ¢‹:ê|?€kÛf›Ò#ØÔäÜ6™Òêÿ?a¨ˆ“6Ì:kj0Ã*-×M¿•KÛë/¤d0!°ßá¿ü2»Mu'795º0u²ŸmÀ¿ GÒ3ò1N"ApÜÝ '  pwŸŸµqÇ^1¥8µh.˜»¦3Ó—Üù¬Å‰ˆ=q:MÚRÖPNÆH-è}DʧâµGœ‰À‘p¦`iAnÆ— €ƒôÅÃŽ…è0ÊÐ&ˆƒT˜õÉ;èâØ3²Ñ_ÂS>ƒT¶«áê·ûÌ–¶Îè3KõHø$¬ã’¹I3V¨vº +^bwM¾TqJ¿\xוnE °iÈÃ
+‹êi…edµ)R Jçwœ€9Úf}ÝVTY¨Kí`éæ¬8xJjöVÑL gÜ kèÅ]¡>|Ž’²Bé{b%åg}ÄË%š$~Ö%zá}XbÂûô÷œè—øí¿š9&7"Iƒüv èJÀ¡†*1:Š‚9®¼C™VH˵®ºyæ“dR½`g€5Sˆ®–÷@ím—&ꊜS‚} 7”4lfÀgvFºíú0ã”+(ã8éSù¹‚Íö9l<s3‚›á˜y‚(Ç™eVÑŒ„ÇNHâ´¾A¢åÑû .lÔ²_ŠîiK¦†‚Ö -¯ú™ô Ð ¼K'ô†Ôƒbf¸nç©¢TWÚ>‘küêJÈjßÏ\A¹+
+?}­æª(R –yp(NšnÌÞŧÌW¾Œ6ƒRWl,i†ÃÌ‘ DA‹¹®q´]^„ǖD®·gÃ`× ªBSwÍþMx™IŽ#QT• ؉óH1çÑ¡åŸ÷dT‰²¾´Ug€XÜ¿ÿåµêù^»Çd°ô5«: jq틘=V¨·TÐ2±¼ºÍå3±9€ÿFA]®}U¹6û Û¸jV@ð=V<ïòZ±}ÓkÕ«6Ú¯ì«=;ˆùYߪós(+Úsοõé"«N÷‡ÆüÕo\щM˜…S¦¾¡Â%ñ‚|ʬK#[œ1(kÿÁK%í^‰TkÅ<¨uÚ`i+Oú+†°íè8‘d@2vlFö5‘ø˜]\Òª$ÖZªù ¤Ü²!”m}Ž5£Bzµ 3Rï{.C5‰%íÍ„ƒëµùæP°‡úÏ=ŸEÿ;Þ žÔï“t0*C¬Z]äŸc=‘#ŒÉ™Ûü‹vøAå[ÎñVòžß¢Å¸GßK]s¡¥¥$³1oíéç(=mºùäQÞúuŽ¹1ÃŒÖöv¢k›Êùv#HOÍ×cfój;ÄWtic£ôîÃ!}:0ê6¨ëÖ¿áµõ/óÇ©?¤Pöɤ²\7|&3×A­@ÙˈF·ñÛ%V%]íÜÚƒõL¨Ã™{Úâ¼%)ä¾]µ&6Õ!w­™§nø.+DP+e {ƘaÿÞ;V@8ÌÐp°¶uŸNu?æ¯H½?s oÒÐkÅ6L½V½²˜·1E¨h8ÌMŒƒ‘aÜCFîêÙ6 òÊn&{FIÊö`˜f ;ÿ¾`i_‹4ø©SAÇ:Íàt¦±;Ùì&óP ¦CÛáA *=Ä n^ëõ\±x½@þ`()#oö)„ªSæoÑfÊAP`òaÛR§w¡UøvÐ:17”[T_…ª?Y.é`6JKÅSÎãÔ.Ø;f²(¾•Ìƒ2á”ñ³ÌÅ]ÚŽqÕw,èô'SP,ÁKðH¹yOÆ©izˆ=Z°oÃP&v¦ÕH ÞBr¥"U¿ó23)`Ô×9\ïÐÊt¢«hÈÓ4MÚö™&©&pæÚŸOˆõgeIYì’¨Á ˆíˆÕq­vÖÎé¸RUëÄìR^v‚ qÞƒP‡éð±bY$G ‡²’m—Yi·Æ-Ùg
+·¢Wo˃%îÞ¸õ™~dž½®@Ù<p2Êu Î\ÂÃ='´C )ºBÇÁ…¸
+SÕ£Àðw',~¦ vTOÖ¨0µ¨#”l8%âÃñˆ5À1»’±
+O9ÓØðH³®@ÆŠ+´ŸÙÌ–µHÿ 8ïg¨Öþü–“Ó‹‰ K{iá`dÝœ»å¨Ρ °QþŸªmÊß¾’uÁŽ©†¸ ?ÀÊ*63uÒ‡3VàðzÄuzNÃx„`§!ª<á,˜w ¢©¸…õ$¿öÖ’( §Ó8©µ¯A6nß·]·Ã"Hñcˆóª2#pösJ # Íà-|'CA]2ùŠaÖ;4NŽM(Rgìû€ä®°^˜¦/¦óïYÞâïÅÍc7®«gà ¤²ž|Y—e€Š?];Û˜*ÿ_¼Ð˜œÎK‡?õKÏ´9~T"ÍaÆ¢ÕŸ>x%EäG(NB‰q°«Lå¬KÐüÛ‚?ZÖ 0 àÙ‡…öàÀ@@ÁXX¿~«.Ü "ò±Î VRk À.-8Ö¬îVÀíK$îÒl;M,S(J;ÆÆ1g÷Ì "¸l€ò>ã£|:Z<>×H Ìá*:’V¢K5¯»g(0²Æ¸Ž«9Žã5 ÅÎyPÉùÅ€ˆ Ö¥z,ßF†la† 9}s}Zõ·iy!§òA8/Ép@ÏŽ趨D¶rÙX$×s¿ê”­o‘3¦
+áhÐ_¢q5š^ÑV<%;¤ë(æ¡àLT_ý‚DNxÏ­#c€€.ž3¶êx'T¶A·<Ë,ˆ8 ÎÌͶ|„·ÉéÔã2CÎÃZ¯ý‹Õ£Î-Pž¼
+=Ìþ—yçø¦G œMVzÑ%Šm`¦¢l‡l¥w™­,^ÙñÖD’¡Æ‰£µóÅ~ýðȽ"=y3“ÿ¢¶¸$úØ÷¬ 0¸?cMäáíMÚø'>ü1:œ]¾4K`ßÊ’ÀÓ&ÅŠ§‘Å6‚Bôˆ`ù”"/2p'^$…ì”,ç‹Œ0ŒD aÆè
+ÃMÿ©¡¹dl^LGÁ”Խ㾖Ózêv†o ¡áظòV|ï@XP
+ˆG
+È“w ”~S(ÀV:T¤£ì°–˜~a$tä:ˆf³ê­c¤ulƒ »þÑ=^ùžÌ]tMp'áÙ;z–.áN‹èPòL¡¡
+t Ñ´ôy2å„3bwlaÀ ¿«Æ
+@ ïò¯ä\Þ¹±€bÓsò1ðñ
+xÖuêú•·}k¿ß*+I¾ H*Ó¹ü­LnL
+q"ÙÍ„¹ j²Y­È¶ÕÌìWlÇŽËAâu¹¼>¹yúâo¿à¼ªhH¤(ÕañžÑ-[QjÐ Æè4Dar‘ˆ}ñR *5±¥¹{K†»"åÍ®HËsFîæžÉø»Ï
+S„©¶¯;ˆ3Ñy?—Ÿ3KH ¸xD¿^fJ¿„¦
+ör†<I’‡Z£ëqWﲇãG;>Äò¤wŸ=¾ðD ÏæåŒë0ADá®Ï_6…´(>ƒ©-z™Ç²ZÑlëà‹í½øŽ¿Gtïžt]¨hiE£³TpL=gÐa#l>‘­‹æ $¥Pb•cÐ=µÀtÊ”¹)Žgr{ •ºä°K>×a¯a'H‘yý€ø!F‰‘(㸡]€©¹"^¡ߪÑ(;YëΔ]¿"“V=fZ(M?I3«óvŽ·1+bdÈ>bÍ`X y¢™,¢›ÌdâÒ³!¹—¢–%Ïôón‹ø‘=ñÀ•c‚èò#0~Ù
+‘+‚Ü+ö>¸e¢Ã¯™gK^>=ö#ü鑤••hØ¥Uº(h,ã":JöŽùÂóöÅúT+0&‘>qµùá–È{S îyÜ„uÈÚ®—PF`Áâ$ñ *ü dŠUŒ„>g4Ë …×côÍWv"b’q­c¡ ù šÀœl‡Æ€²Æ!–1”LSÕà@’|6“ÕJN>%Kß"ì2FcœÄ]îcÍé\¥%ËDzUø1$•^~ð™÷ìr»¸ÓLUAÑ ,)ÈåúD‘Å{SmpU":'î_VU4C1¢˜‘AÖ¢L)ú"³ö­iÈjkPE‘ñ‘ØI=€,f?GuóVx´>Q¬›ƒyqÏ8ï¬.“@‘AyU
+Äê<÷âùqËŽ¼¨`B3̕゘3÷¦¿¯ÈÅÆ=žQöÈça^aüåsΡB©ÒNÆ[Äx~‚ÅŠ‚õ|ÓbvÅ㘚¢Š~ÎhÁ–U=V“Æ\W)mÄÕ»\ùt;
+ð—cÂå4š`€Vë"=6_dÒ;qÀr\@w3µoW»µÑnÂ+Ç÷›+=ÅÍ-bJÏIøš
+^€-ÁR(.¥Ÿ†UÓùü?áñ¬P -Nˆ?á;† =
+—X)Ìí‹ùüyÀ3„:!Cc‚tœaÒË,ó¡h}ÇK5[€^øšÙŠL@œ[P¦’#Cј€L†Ù±‚é ’%¤D!©{‚°nÙåðÈN´SSð•›HÛ¾W%—sØA´¨´%&#hbæV·ˆ8Eœ«ÝGz0 P+T² ¨ü«Ì@'º
+¥7Vh0Ò¤ƒ&l²PÃdQ÷¦7aƒJãè%=³Q G‡2 ªÕ©bê*=Ð$¸\öéîÂAððVµKõà+nü=
+ÕCeh ËYb}Ùma8ŒwݱØ?ß]?
+ä,É);͈ì2’hÃ>-’ZãpÉŽ–á—+µÁ—³w-¸¦”okóÓ³uA4•ù4-a‚]ö¶Ž¡YÄ)Üx3X$ÿp¶?þZK„%î*¹[
+RéNîÙË‚÷–4-"%ZÄÑÄt»¨:¹h!CÊZHœ-0G[HÁ¾`6šÎm7¬_÷N²tìeH ëê%ây¢„¹bp²h!cê¤~ õC‹RôjƒÕa— ‰!bûv†ç‚óA8çJP”9Féê ¨(ɱ ĤZĹÂÌӈৱz£8öçH—ÖjJXK-â´T¹¸"Ž³I¨5‰Y„© ÷Qß*¢úª`tÜ‘’íu PçÄt‰×b&-f±ÂfâHŠrÆ]Bb¼ü‹ʸLj4݈“\Æoí¶AÎ…áE®™Öáâ$ålËmt•R‰æJˆ4fÃá_úp¹Ã\ÔCÅ; 90ÿ䊯)³›1ËÎYöœÚ<›p+Ù˜}^XÁ %
+vDQ
+Š-`<ú;Á‡þqt‘MÅ¡ªŽÞ©êˆ1jÍˈñÎ>riàÛš~±ipÅú¨ÞóÒðn EäÛ´…£>Í\i`nÂsrÅ#šŲ¢â0ËžY‰ PÖÆ6<Ë‚RŠëôã ¥ÃÜ•¹uýš‰åö˜è½-5Yå"ÒäYH±¹ñ‹0"@¥EÀƒÈd£?°0"°íŠ;Ÿ`Fm—-Ù,üƒ|
+¶ãø¨E1XÉ#¢ÐþŠp2Š
+âÂ6±bñswE¥Søôäà>êÑ…œ,Í·ã’ÈbV²›–b%Ãz#¶¯gêÞ“îÎùÈ2vÒ“Ö3EDÙzƒYj½ì©¿q©Eí¥÷,U]Ö,V4y·ȳû·`\À…» B{%.8V†ƒ’Þßá–±ˆˆò¸FÙi‹YU³åÊ
+ÔX[ÌgE”+ø-Vpb¬‚Sò¥¬OãKsµëK܃DóˆzvnÐè#£‹b#þϱF:yBŸËDÿO#üsõ 6"†k’9ÞRðyXÕÓ)€8Ù%ȧ îŽè»Ë’ cE€ò… Ì6
+êF õÙEE'$©
+Äôqã ®¬v‡…#è]Þe!05M­!±qüiýXy‚Ý©<@œ‚}§Èu9Ÿï]Xx&ÿfPƒ=Œ tìMq–˜0v3cJ¯¾ív:eF$œ;+Çïö¿³Ë£>A”ÌϽõ{Û->¿N›P‡s"1èê´.%Ÿ]KÌÃ6vû
+€'×qð›`u0„:Ï%hÃø›»Çß°ßÅðz}y<•«8vÔ
+Œ‹õþ ðÆw­}€Jâ<e9ìñ”ZƒŽ^JS¦½“Ö¥½€xaAÐv…!IâkWAäO§Å
+å¦AÊÿÀ'£ù—À}‚d3A‹ìÂ¥dï†Ý®òkE?5X4°æ™*Ð/5F áÈDZ³xœúí®][4#‡Ë߯ÄÔDpô9X:õ ŒÈÅqHd7ó2äÕ±Væ -VA'›U•}b|¹¯Ö^/Ǿ×Ö%Åq€Ž²€mø¤}wÂï’ª¨s²—ìØ"³i«•7bNZ÷¹õ!]Þ:g€(t$]g5Å¥Y[ ø¾Ú€ï5æJ{\u€…ðÆ·pe±kIË
+GF|`M”¬¥6k<«ìø]߶^‚GÚƒtG´
+F«r Ò"Ø0^gWôKÌeS æ¨†­$™Š¹«:_ÀÍ„nbW! KRoÞ .“÷1
+H‰Œ—Mr%7„O ;èSAâ亽ì[tĬÔ÷ßÎ’5¶^Qn…ívØ/‹$€D"1ÞÿãR./jÅky«x1µ¨aZDµÊR‹ÆDxTq·b½ÇñëM¼]%Jê"%Æé”vq¼y¸E×°¡e„ñc­ 1Ž²â Òk´*¦:jß7 ­£ªºñ[‚êUuhïR‡™rNç1Z‚«Â$·è„ Ò,O©½®¨å«C¥pwèû·3¨Yµa:š¿ÿ|›ù+BöšrÃ8€>HH{Ë€}ž¤WQµÅˆÖÛªCW^=”(lA„ú6“1!<Ryœ´ ÄÃFQù‰Kk-ÒJ)}´rB¯y‘wŸçØ(­òW »!ÃZínMªMHSþÛbT©r€ì¢·TŒ‡=žÓ. Xѹ_t_eŠÂ¨(á«êÅÜ]]}N)¬fìw)j8Ïu¾ßÓIsu4êÌr»zo­T…Ý:ShIî¢E«{kó2c}X¡< ANI^å|Y4Ù"¶á ¡T¸)\¨åæ Tá!‹vÁoåHëLà yQü1ÝÒ·ûî«íÅ„Æ;B~¼ý—ËŒœV+{c“Ùj§–Å£Ôñþ{b„*ç_ íƒ×õRÚLŽ^#‚à³wSü’€Öœ&ñJ¤º
+’`rdý}=¸ziP>noúÂÕgàÎ?x8çµ ×<ËyðƒV¤ÍYÖ÷³ßl]ìMÇý„xšÁ?Qøo/HlJ¼‘OåpÚï#&•Îzéa–lGZ@o¯6Ú†Ðp¨„³ßÀ/R†Ø5»E>ØJ Ѫëᩳ¨úHѶIÃ^–ÁM´Åj,úhþ,ÑiœÈ¼)Ð:Ž> V†85§¬½ŸePAg_Œ{øñ…ð@>-«ˆ&G—æ \šÁx« ²¾Én­ç %ÊÛ™¾(öv1~‹Öoƒ>‹áÇÖ³Lô÷„Lj3êC-!ÎnCásáðôƒ¼™n¦cèfÊ‚x:‡‚7`oÙzÑði#g¿¬Þò‹rÃT\Nk‰AòZ¬éæä½ä[FˆÌB ݽ„ų
+ÎÑZ8„ê©ì¨ÐÊ«pr¬Í/h&ìÌ*W>¸'±™¸¥Ý T–<ÌnÛÄð«QKHš?Øb²GíA*Ü7$Xos5$Ú6! o(]`i ‚ eŸWhÖ&3¸Š·ó}âÛ #¯‘k»ætï–î=7Fi²U™‚æ^Ÿ0ð+tÞ+˜'ħïsØM£tÚó§üWHßô]ˆB“±…²izÛYΛ¡LXšÁPg¼×ÕZÐBràÆ‚TI‰f¬KXøœüZíË{~FìæC&r݃¬z:EgHÑ%µo³‚Ä öH~èâÕ(ð¶­j5 í$‡÷ÍQì?mŒ"Ó´;{šÖ†]Ò¼–QÞä¨Y~FÙâý<ÒŒ÷zAÿ4¹¶&ZÅáòHl»’Jp-ü)+½i•¢zvÜk¨@J"ZÚ}Êc?JÄDZöá¬äxÎSiy ù§A°I¥]hhRË°ÝŠƒ+—[¦Ú^"V$87Ï‘ ;X.a°ß^ˆQÁOx™Õ/Ï1äÔ2[• R÷Uÿ>«æ†¤,~„ˆËU6_]GÖæ`nr¾ÿþõ¸Š>N‰IH®±%ˆ-Y£ËˆkdЫHN(¢“9¬ì‹ƒŠT箕¯ÄJ‰1|c@¤Br ϵ !HM£¥(ýi ™NÄÈ!A…U‚²àÆLá+"óS˜Xø%…dåËcˆÇ4÷Wôìô 4‘ e­ë¬ú!(@žzDñ0wõ” OL_Èà9¦øùTªuÕ䨚"ylZëÉÏ×°Âä£ùû1(ÊÒ›§H!ˆ¾ozMN&¹3¬˜^3<®n Ï/Ž…zA,J¼Öûõ˜k^Þr ß3¤#‰?eæØ ŸÓûçŽú‘—U -iÇ9Ažödõ[.}˘ëí`a~1/ô¼ì°ôýéEK0ú¥¸•ÿ1^î8šäF>ÁÜ¡í5~ðý0…Ys\` oÇÔýõE’ÕÀ_ÌR÷BíöDÉÌȈȦ(3/n5¦àð´Ñ.~€œKš¤(Ä÷7w@De<¾“Ö¾_îqß¡«mTŒŸ®=Ê¡—MfJÆv+¶]
+¿¹ ò$ÒbW^*#Á΂ý40iÉ0“ßá_zþ\2Ùv\*n¬4„ᙃ•m^â4¾IÆ_wé„Í#þp"ÖA三ù#KþW°>À4´ø‰ zks5Ë
+¥¢9Œ†sÛŸ?<ª»Z`ÖšUalµñ¦œiõ6["TQZ.³¹ï¾û±ú¤HA2 [>øzî/)!’„N÷; RBþ›Èv%ꀬ!Ÿ=-­@óÓŸ\E\ Ùž~‚6êL(ž$Œ'šž g*Î㾺OªKø;hM=;ÏTƤ&DÙ‡˜àn1¡¾´½¦¢rÝéˆþ¯ L僣æj+N‰Ø
+ê1RAìȆè‘'UH›sÍê -è’Ôi]œ×’f˜WÚ‘úè)°+1Böþ)¤žÃµAÐœ4SšB'á.,X¹œˆEx¶«Ééýð>¢y„¢hI.ñÊ,ø'ÑpJ• R9
+ÄÙ< ™¥¥€P›½Û
+Æž?ÁÔÊ F£ìsA¸j“”Ö>Z“Êf 1rl„F—§x§dseå Áú²â_O²]õ>ˆ«fÜ„_ÞŽè°–ô=áþ£5t©Xî;‰iGl#þFÿüÏy5ô/ÃÊRm›¹§GõžÙ 8Ècô‹£„– ‰_î•Ø…¼µÊŽr@•?d,ðo=´Þ4I.þ«ð)ý"?0ó«Ó§v¦#`~!,%eÕoÕÂNr:~ÿŒÃ›ûeþÝåòø½8î4¼—øë©ÒF–ºFLƒJÒ"-{Ö rd §Y-lüy
+Z*!=y§-LA}ùÖ"ÄS7nXŽ(µdF òjL±?÷á7ˆ=ü(àù£ òƥ摅Ñm'&œ™8¾; ¤Žrh^C› ‚ýøðÈ_zCQj¦jÔî™w;¿f²’_X¤g=T:’–tð½d–Xéq‘Þ5ŸE;‰î‘„>úäÙ¶6XÈh'a2Ò³xŽ¬Ÿõ°›J¹bÀ¾Å»}sV6XÇZ£ü…møWJ<ÐF ümž,6Tv-(ó%áT–£Œˆ»†bÍXë{m„…AâJ/÷ï`ꞦbF½öá¹è¬Þ$+péLß]çS½úœOã…"Ùüý%(U̲V±o‚L\Hµb(‹ŒY<)=ºYëaUs·ÍÑL*ˈAGD†€´‹i\l)¨bê©2öªÅˆÐ>/-ço ÔÀ
+AG‘7
+À+¸{ð
+Q$õgê¥tö?½ß¡(#ÝM#F
+Od“ë+ƒ_Ïi‚DìŸ1÷7æò] 0A© "Ü3$gËß\Àm˜ºŽÔIsáé³ß8öך;s£ ÂJÎÁ5#Dó滷Тwñæ+jqH€ñù
+—äþÝ­ÈŸSÝ"çlÔÁ»iíýà ­^½ññ›’­œ%“üÝ™ç¢è 3,‹ìöe…ŠFB7&¡\sa8—/ÿ‡ÆË¿vðk¬ZÌá«ô-æù]Bƒ Íme€¾Râ±3]Æi¾_SdåZ>
+b2àòºeŒ[Ï“Ñ‚%1™x9â/Nõí1ÈlÛñ&q1hgâ3«áž[¬ºŠ-‹õüR2>eËoó<báÛúœ 7·õì7·T Õœ {€ô–ÈšÖD÷ç zZUõ¨×Ò0{Pm‚óƱ6qwƒX"n D}šÎë8‡éé’+ÊèŸC°écpE_‚äÇoŠB”‰l¢ÊE¯èô•ÙON¦[³„z^æ•¿;¬œ4CÍÖ½&䳓YÌá»ìý—|Fe‹lÓi”Yõ£$Sý²Nfã¦ñZÒ‚_÷'qõ{M÷Í î½½Ô-‰Eí 2ê18u„2WÚ'/Çr –ªç»¥ßÂÄ-óërö(rNëþ,iÚ>=ãínfóˆ’®9 ʤå+æÆò¯ þ{MÁ<X8VÆ庈ÝN|e]}¤9Æ…6]´¨QqÄ´á¼7+FÀpbÜ‘ŠÏÆåÇlžÈãNÇû€Æù6ª´ÑSŸ¢= ·¬>çœqᶅØúSáÉø6é( \U|µñ¡ï5š'5©9%—pu_>ÊkÊ÷Áy5n²CyÍÎË
+;~=èj ^„ö¸_Àvp«<%¼—{DNæþõ(þ …­N¬C¶3‚Êë~?•§_ÿuµ§haã»WŽÈø:ˆ\ÐwÏløÝYe^T²"Ôb}Îø”qÄJ½Ý9m,SwäG®7“ ¨öî½°Mݘ[žO`§<ȳ·ÚçÄŒW{9
+w‡…cˆc< lm Ù"ÆËŠ}LÁpÚ2¿´(ªÑÌzF {Îê†nÐýð Õª" mçx”ÿ¶…%!ëš*ÁM­=Ï Tÿ-³[¼æñJ‰P‘€T¾Eª!ºç[R™ö3¿¬Ç`@jå5pZUü†–>Õü\ÕðŠ¶
+Ýh=3ñ±®‰Íg BžƒF¥›ÝÏÄÔÞ§w¯»Xò—’è—…áûõ(]j­ªîŠ¡¦R"Î_:™’¤Z" Õï&š«¡Ç¾4&щ>ÖX…µõ84Úb#×çôLŠRÇ"íïm<ë5Ã’Ó°P{½Ž±¬´ëåZ;uIEÆÛV.%%ò ö96nå(—%|£pøßƲܷÉý_Š>~S”¨7hFc«0<ì|ÑxV¨cAî”$¸éDvöõ¥5Š7ÍÏZI‘H·¡³>ï§ÆˆÖômP…Azèã˜ÇXÞîŽ,æù¼\ìdCåœóž)?5ÌŽGñÔh2§‹„Ú®7 xb66+;Ø óöØ¡ó\ÅŒxûûøˆòFˆ¯I’wÄ‘LeîÕ9ÔŠŽ˜òödˆD,ΟÝ>ˆÿâÊ`tãѶ÷ÝÔ'pD9Íߨ«å’{º1ÄU4QŽšÝ1«õ˜öÖ
+ãèò¤:´E÷ó6X[ÕP^BHç \}ŠÕß¾ë|NZ]øKNZ=üKÉx8ÜEOnÀ´Vç…bNÈíý+CýÝÃKcœ¾r¶çë‹L†wÔÙŸqŽ$U'MÇ´FŒ%‰e°ˆâi>ÞgZMºépýÇ
+~ñ'z"uhà
+¯^)eÊ—YÝe–´âŸÃ¡ç ©„RöÙ+ñÇYf4+ñž2SËÐCI>Æ:k§LùÅD™k”°$—Žûøþ8
+o·´‡w”D>ŸJ(Etqh¼ðÌoŒs¨‹vÞ&‘¦RHȵ̈çIfR™D/H¨aŒ{›\ç÷ع>Ô[þ‚lu¦£c÷œèV±Î\9'VÙÀã‹®™ß•ðÇø¨‰Êoš>3àµ-2UÒ¿çCÛn¯<0ïɼÌÚã‹å³ ”6ßqJÏÞôAh(¹Í²„ Hwøò©eÑ'/š7Q
+Z«-‡‰½ãëØ“‘ÖD0P]]Œ´>'NYs7nà ö½CŽ #K¶CÆÔ¸g>*ƒº°Ky”ÿaê
+w¶hZž-i´”@=çø;¸c¶¬ÿÌþ%×%~|&`ÝÙÌ¿ó*TÒ[e9ý².Š>~St§§è%zèWœÈñœÿ€‹æʼnØqµà
+ 9TêΨÐû÷¹ÕýhðM4–Ah¨šþÕ­û cØ_7‡zI‰P•Å2dJnøyÇE´ DêÇvÞKb§¤L›e•ÎE G1—ß³óœ×a=†5M%ùê
+ý÷Ê趦Ú·µË4¢d” yÐ\Á.Å_]%³#MUZ
+Üj˜çÁ\~clæG×3´%ÄíÑ›
+H®éË»xÂ7&¿¸æŠÙes$D_V˜QÓæYŠw,!Œ¤ ØzÀCΡà8Èœ ­zæÃ
+4‚6iò,œ¤Á2c_
+
+óERS¾ÞîÍ2>Ãv¯L
+\O“‡ÒŠ»˜®ÙOÙñØÎ{Øì
+dÅʼîë0™#éx \„O.…2ö9‚ù› ÉûDÚ@K!‡æ{ÈõJ$æ~_\œƒè.}}ôš<Œö§?©øbY¿ËÙ"¹Y™p¥ƒêù®³‚Ñé¢í_OŠ(™Ð4ߊËæ8Ÿ8a‰¦
+nkH:JÖ2 Öd>xm,2"p$u¥€Jƒr Ρ$¶Rš1FOVAú»8§¸ìÔý,•npxŸýÙJ6$‘œŸ„Ã"°ö~zÞCÁ—&ÅF‡"éÚtaÏÂg×y2Äx¼¡Žw9‚©¯­Þ†¢LoE­Ây|ß©…¹V8véV=tûqÌ<žå½Ó•–r,ŒðÛy
+ ÌSÐñxRò¥Q:m†]f¶-çÍ¢?Â…­,<èdèÔ n×À%-CÑS‘|v¶$a‡–mÝ·»}¼r¦=¿ýõ­4Z€þ›h8™ ã/%äˆ(‚©Í¹ŠJ}ÇG)õâ• Éí*!8÷Ñy• $¬“ø‘¶Ç—u$;ìEð.ë LýÓUF˜Y¤k4 Å·†\ÛÎsüXà4,JtU|}&?¶í¿”å”`$8ß¿•þ²Ò2”uüPôãIQaÎ2B&ö5ÈÝ@+`“+lÈ
+Mƒ½Eµw¶¨¨ (%ßÂåOõˆ†!c>¯”¯’š!µ”
+KdŸ%\»°q(›WZÚ¨ó©¶kî8¤UFC¯se$\°B o–U‚7%-œGÝ=5îˆ‹í©¸ã™©÷Œ9§mI×:¨
+°Y–¤0ºh%äDŸ®šKù€÷Rªé©çv— Í¥zÀ?pÁ4ÄgiæÖ•à”iÉaj:CÈíÁ­°ƒ×Õ”Ys^¨µ+sÜ<³ˆ…^5 5 åd¬èNP‘renÀg<qšÃÅ"V5N9wÌ4I_vx^A©$÷Ë僵Ä/x0I‰"Á´±½]!3›œ8ba~D O,ʆ€ñظàÀC{É__.ŸóC¯äˆ¹èÃà°Y^“E‘ŠgôZà”ñ’VmÐNø{* ;¡ä„ã.B\9Û { âp(©øÙN<á_䋱³¼+oôã´Ý½èã•3ÉNå[5&_Ó©8zBŸXg ŽóZ‡¢ƒzÜîà„~&]ƶ ÃpZ=?ú¡í%âÆ«~°5Â%£„®n¯=Þîq·k`ƒ–¡„.*R ÿ¯qe5upÀ?õM&ÁÞ©0&¶Ýr2³Ž©PXÒtrbæk^¾^î,¬`l)bŠ '¦®Í+³ðò)–Û¬jL<PÏCfgC†C>a,èòçBÝi<sÅBv(ù)|À¤ƒH‚„.Í<¬Ãù±¤%þß·JîlŒ™èbF´Æŧ†æ¹¡/FƤ:öNܽt‘AÝË`[R«ÎÎ"Œ>€…y÷¶V÷fzŒÆU§J€ ¡ «²K|ÂEœ¿•à¢ô.ã‡KÔ½z»’#Î…Eä_„g܃#{\­bDöy9“ïHÝŸ\üK;¥Äü7xèHá$ÚI,c
+Iyð„QÛ…OX¿¬(뀋Spñs †"OY±G>Ý™ ÂJ•1iíŚà?nöZÑ{>^à'±ØcÚÿüçÛÿ~kÄ1°—§{ÅEr>Þéy5ýa!´†¢÷V1°j‡FM5t–é˜J—ßiŠ i+3<šŠ:㑤DÉ{&’«Ëaa £hjp”“°\k¥pd&·ŒÁS‘a©‡‚É ¬ltI—fR5ì 
+óhsá íÔR?}¼
+~W ÞŒñ%š
+ÕQ45Þ\JÔæ*rQ~Ö!?J¿¼ðk®›AÜ 3$.NÓ@û»ÅÝÌU—±žôºµ¹¶ø)ò¶Š€Ñ”q†Â§Ö1…ÏSw»„zš±»E%¬‹°LIyo…$Õ¢‡Òh«.}CÆ´ÝÜECm•:xª’
+%_©‹úE D‹a…ÖÆ¡"v*""A›æœŠjGh1«LÎ.aî‚LSß*!DòÄ9aáZ¤i, :g§¦ÑÀö’^Fd¸‡‹×ˆDI$ƒL÷y=Ÿ¬R.dN
+%QÔód-yJöÿšsIÊ@û£1@cº]&ŪºÑ¢kŽÁyŒ½ªÁfÍD$cß·g=Îú§4i§Î—3\íÁüɦ9ç4“{éõ,Ñ0kpø¤¶ÞŠ~SÍü ÅÖ&U|“•¹îtp?¿?†x¡Á÷H º¬rBè½¼­±`t…„&›dýî˜Êë>Öi÷£€Ö»æ9}eunó ܪ¥,×\¿(´
+ §P=¯ú,²25Žbc…û9š¹QÏ;x%|LÀ{€V/Ù般Û(AæW"—7쳨 5Ä,ÌšD%4à­#dДlNs'¶è·ŸÍí³lÉÈ‚~O±àlmÞä_
+žWxq¼&œ±—^R÷ÞÕýÙçט Žµ”“”’wð+0_ãÙ®feƒú`ü’›Ìzß¿”}œ*¾žyÕg‘v“7
+-ù½ó|ûH/n¶¢/ÔÚµ¡ø·Â
+Á}Ej‡ÌHæ-W]1¼Q üí.¸B.WwÎt¬‘fÎì§èð6˜Ï¡wzàsFóŒ±VâªyŽéã™èrtÃ14ªXjùÊ7Ñ’NÖüÚ™ù3=×üº¶½µS–d, _ÛL©~ˆz•sÿé|Î8 “vd bvw9YÁ*¸ÅH)bìM’Í>?š©)‚ìaêRÊŠhéndŽA$.v槛ó“æÊúïèOJWÄôvt„†øKzåí ‹‰\øÌþp»g­Ä-«5/:x– 8nÙsú€–½úò̇‡Öv.ÙWwË‘XV²ìúóBÎ@
+ç,¼«Å…í‘WmÉ",s¿¯±§(n•¡—#è”ÑET{$(a[\š–ì¯6¿­Ä¹éD¼LM°³59cÕrU¸!.Hk¶o‚;ù/ìÃG?'ªèdìÑî—úQøS,¼ª¨aþÖïk:"¸½ÇMhØ»›…ÛÚË’5^z‚
+¼QGvzîkÅ£¹Yà °R‡UI¿©$ÆCu[Ÿ³…{Ù³߸ø˜=Ý;I±]jå—7Åõ ‚äBlRW½ÙuÏ1Nlo[5X Äw…î׃ÚYy^wàŒRWW"Z-ÒXVk³ÿ žŸø%ÿ…úì÷e1HqYl±
+gÞé+ïºB²ä/D›>‹þN]‹Á(ÝÝ,…
+p´®L:^´æÌ ¶¾÷ìÝ»®üRBá(±ò+UZú öŠ§Zç)T‚ðnW˜}\u³ßvˆ›²£èÞºŠ\QkC‚ÈâÓÌï-ñ vD»®åúd›²ûÅý±b±ÅŠNÚNd|wË¢3Aø¯…¡GTRÓ áìIûlìÙ´ûè¾õY”ô«ˆ uæÃβ>çàŸ_óA«WýäÍ1=Ùf+‘bfÓð™ê öLdõO'¿š\X‚ä|Ôåbø'Ÿ¥ž5ŽÀ..ì8åäÿƒ¾ÎçBÆ“Í~%–·ì-9ì%ðo7^¾ÌSü,A+†ú7é¿ÉÒÏ'yoꙀtf/Ó›¥º
+$;ÜÛŒGÏ…ñ&‰D½WíG›èð¿v1QûxOÿ Ŷ£Ž¢»vÕŽ»Ì‹NT;ðtæ©åÙüôÉfÌ>ã56
+í~ˆêÄ&™øs¬×¼áX”#Øþ`9Ìr`Iœ¼•®RtFD³âäé LIôFI)É«æözlË–¥Þk|ϲ™Yú[ë¼è£FwÝk]áûkõ)°qÆ·YÀ;pZ<ÉâÓØC´ÁIsN­±Õ„ºøS¿V ¶°œÁyçÕµ.çD(X»,#K=½ÂǼí¬hÖùsQçCã9Ÿº
+íé[CŸœÙ ÐP…¾ÔºßèþýL[bóŽx|«ûŒ"¦|r!¯-õPÉN‘n[½u"ùÈ›W ä‚Ðêó¯ö,Oùàˆ¨5Nšd';
+× ‚oœOJ,³4~íe>ŒŸLå£ö9(|Y«ïù±Ô–XAÐlÚ¬KÂÏ’5}#†™„O b¹ËÄ÷šÿ®kŽÿ3^/»vÛf€ŸÀïàq)QÒ°H‡™öÚQ:ìû÷[åÀ{s#>^G$ÿ˺Ä="é{‚
+ùúóªý‡ž|¹—˜cCp-û„è4e5k§z'zmw†|¿Ôjå£RÅaõ-o bj] s˜¶G~:íÏ_¹RnþÛ¿Èî?þýí·ÿõDyâ›g˜Î³[É£°$;§´Góȵñ±Y6SªÈAB"eµÛÖZ Þ¯#èFö¶åùˆŠªL;±Åã«4 ·´T™¥sËÿ¨ädÐÓºã;áÈXvtˆ¢ß„üêVdQãȱƶû(ÚªZ¥vóv,äDáK[²ù¤¤.3†rCtud“A¹4ÊÍÚMK ²âô×VË}’<ráI÷±ðÄG"4iáêÅ©EcuøùÎReÕä…¥¿ÛÌǪ-Ê~nH?Šöql⦠³ï”Ѐ.®P÷Іcg„R
+wçЂêMZ­Ô9ŽBß>AhÅ™qŸbéö¤×¥íá»ö€Ù,ϳcáÊv¡lè™´Ö}ôÀ8ã E Žûœ³õ¾†èÏuŒ„J‰;Wt¾ö Ë1ï ±ÍXàA—)¥—¤£8û­[î›Ò_”Á$µþ ]{ñ&TŽâ/LõÙŽŸdê ”çöÅè©@Êû\Ѧ ‘Õz,öÈÌ¢ãLcھȺR°…Ëè7$1òÞõÜÊøåÖº¾tJÙ¾¶¤3SG¿L\s½Îfm¡!$xù¬å›uE?\.Ž¤f=,U=ÿÚÒciÕvÜ'• ŽqJt¹1{qCµß®½_ûèbLë6F£7 Óhü6m»ª¬G8ºî?´“…ŒŽ,dzúÒ }•©2fèbõÌåÙÊ-zjÉðÈd×1ô£HGW¯èû6ÿÎ’ðeæÈÁ3ðš(¥ÇvÑS]
+ìÞûó*Ã
+&Înå¡4ÄvÙ¶ë‰UòDYb²2†É‡Ú¸1›hPL‰—tzówª(…°óú¤KÏj[ÐFq¤o\p×Þ5oQ/’qºhÜR””JôG‘2Üxne\&78­h'ÕVZb“YÏwÑ[üÑ5Djá{½Fù¬Ô˜¡Ò¦‚¦/‡¹ßâÖÝú´……01)ПŸ@v<L’
+ÁN¤øüáßi®Ï`¬~dÅÍu‚¹×|ÕÜxÚ?ÞM/®HÔu›d&ß)‘Æã9‹v„„zÞú
+Š|œH=°ÇÂŒñç¼Çá“]ÀÅÅëtVýÃp̵[l|.yÀw4˜¨‚âé8³~ Æ`Y|š]Î í'#¤|V³--·+«hí !œ =Ø%ÊzÒér¸Ú°EMîûQ¨%Ü%Ú^.ì ©gc­ëërvÙ´´õ5aØ3-Rê|y˜dz#ڣ̵‹H¸,ß»Üvñh±Ìk£ YMyçÐ.=Jüª—ízŸó2oOŽL Ø…‘õ²›/3©9L=©¾{è§#}.L`lA–„FªË¶ÐOƒŒ6$,<‘”U÷sž.‰Y&‰¦ÏÒÅÀ¬g:
+Èñ(x »nz™þüÁ\WWü¨2¶AtÈìÜ
+1CèvƒAœt?%Vx2qÞSÂüB2L¼Îë¦Þô#XÎ8
+B‡¡^p6ý˜¦ÝàŠ>{Ã
+Hzˆq7%ê7é¥+Í´(*¨ju®?ÚÉðÉD]=¦„½›ÈËŇ»ò÷Óz&­'sÅìlÝk“3f.gH\þç›"(°°ƒ/¹Fù {¶ó'2†!Öâ½âS*á5Ø2غn,Û¬žw^„ŠDÁ<´¬fÁE[“Ar ¶kdHÁÐ8$«‡@ñ˜Š>rÙuQrç8àÒ®šŒºòA˜‰ä§ð%ð6\ò‰e’%+Ÿ­)‹L>Þ@¯sŸKN)²o×Ux¬ÊÿË.ð'„!t:ÀŠ:¹iËå»hÐ>‡U¥¡•ƒ³ùL3Ì
+j³i‚ï‡X^¬=”Rk‰Ï¡4âjkŸçH©é•4,*ZU
+er*×M/À?Âg´6Ê°8š*õI#Ú>: w!'íå‡ –ÙFDÕøZè‚“|Í-qíÄÚpڞ夦HPÍ7BDW ðÁíûÕ,ØТLóؤ"ª@„퇋Ï(DZ™¡”¦<T>1 >b¨Ÿsœ’$½1ÄàaJñÂG+‘žW%ÈU–§s©Šø­`ýYœýÑ°&f!#Y¸åP^–:<÷ÀKÈÕÀ ”ñó§ú9©*âá­—S¤&Óï¼l›I¡†ÓIA:ÄI‡³šúþöIQ@JS‰“[…C·=ÚÆa
+Z¿[Œ& ` ë*'`©½ú­ãÔtr> Ml,BóaÒ·Ò‡–|W,t
+— D.ÃÑ@üμIôW¹´L• ÀÀÒp—}¼)€‹ÆÇ(Sƒ1Ø'ù±(Ò'ÜíÐ)äI÷«”žÀd(³M4JÀ6g}ù¤›ûMl¤¤´¶¸à+EðÈÜð'±,¬³;Û”O{!
+xXŽKq
+GL/ïa`øÜ’ä5²·Fæ
+^“ PD~ZtØbÙCPÚ>Ïùˆ¾c
+–WœéßKÂÄCLQ^dÜ8¹$S°}»v.± `Äy|Ž~ñ€ü7Gy1Ü„{Î —î™›¨ÃøΟ£h†ðWqja,9F(€PÒÏ£qQÔˆËæEòÿ%Š‹Ð4¸º¶až½¼ÇE5ËÊr?¼ÍÖ+/r,ç’*¥×ŸÔwË‹:¶uœJ™yFy‘c)Dz ©Æ‘¢¬4Ã
+ÞÒb`üÞiQ”^õRr•Õ(.Šü,‚àè6N¬¸ÅEQ(Œ<™’íUx¥EQqC7`ÐéÁïÅùÄ
+1$“©¤Å@:^a‘ Œ&>‚°¨l¢kúÄ„Ũä¿)z„Å¿ÚKeEÝšó.cæõX^¥µ¥âBi<eíM;½í¡/_Áæ´ÂQ‚
+!å&êÀÁÛ‡rY¿J”ã¢Çm¿þ±Åm ½¢;,rûiÂ%ÀbêL|ÝŠÄaY '
+˜þ¥6ažô^ ýÆ+!¥+,À±ú$~…Ë1çê<Oy‚Š¶]Ål”ä±jÍ×I<¢Fp•%èÀïŸLN6VJ2&
+ÿGÑ­ß]÷ƒ"”uè­pI’¹DEGK FÈe×q¯e ,cx(€Eà#|ÁøtßætÈ:‰BU‚Sãç™èÇçóï2¼¦Në³î0è8.ô”—I’\9DOPwÈÈ8ƒ\W/ë½UßÛÏA0-õ?Ó,k!™à
+%’WHíšlÄ“GÃ
+£·eТݰ‚%e©Kò=4·+|Æ,˜A&†Q(mïÄœˆË¡™~Šš›=Òm‚UDú¼$\
+bj­oìÝ*
+ËÌf(­ž—¢½þþ‰T¹¢¡Ž$C–‡…²…|À
+ß3Ø4ùêë*¯Ù~ŸäÅï]¸¦J¢¸‚“¡ÓQƺñŽyá¡âÒY&‘×/½97âW•F²3îÄlá¨ÉT2›¾UY BC-X‚÷n+áÏÆëŠc9ª\"|³Z€FÖËg>gs—R
+/:|òYlEè){+PU§ 2ÕVÔ¤¬ïG¾èsËI–U@ðMÉ×ú1K3Hr™Î`¸ö0»fR>Ìz°ÿ9KŠôÉ®uîˆó¨¼`hÌÕv‡((4ÆÁ§#Þb0y6Fg—HY!¬å8¡m &NC¯ŠêðÂããÆmí×rMo.'.cÜQq@
+ãF Ã"á/{Ê9*£Iâši;ÈÆàN"vÝ3<T`Ü°,»„׈`0ï­$ÍxWr" l—`Š!.Ñ4ßJ—¡œXCíEŒðÕk†)ÀÐñ¥A¶ˆ­<.ÁÇKR]ä¦:-¸UMòã:m¼©š³ò´©Ì8ÊÚ5\±,Iwú}%™DXI
+27£0ž\ÀÄÅÉ€çÊ­ éPÂîU¢5!y<&ŠA/˜°Ó-ÁäË›Sø $Ý9ôŒuLºžTýãF)’p l>øxrÂû:<–™zÔ¸8™™j†y–í_€hÚM߶ÒSdÐレBʘX0¬Â,&›ŽÆ:ZäÜìlÀ+À¦@1Š[_ÆŸ€9 o§ê¼_þ
+Ò†Ü!2Ùíó«¿D!€*y„sòí™HTr‘uh<Ú÷zí$‹*œs˜á;½T>¡Æó.;-?ÑHeUS«‚¾ãÓ›²ÊZWœWn¿([À8ßDÀ¢
+H‰Œ—MŽd7„OPw¨ LB¤DRZ·—¾…¯Ê÷ßÎzz t×K =°§*+R?d0"4>ÿ7ª½¼ª—Uõóµ¬"kÔÈe~Az\Ã-ú²ÏX¯¶ú˜±V˜¹]ÝgfÏöùÏ ¯ÖǨ>MßÚ Èá«ÆZ#\댳ÍÈlYg«²ÉgÖGë!H´_|4zÕYÎ.ÙݬÏÏÚ*\ u[}Íx
+ñ½S‚½ ó@œ¿çÍö>leãÜ~¦&$îÁÇiU6:^e>gœ@±º5Îþf¶]î1=W½üÂãw;ÍÉ̱Zôf•Ãà…kˆ!öjToL0>O;¿ú«šÁÙBÐà¯GÐxœä³1!¼¸ÃæÙÖ°Ë$èBqû›ì5ZÙ`ïê#.Úá-Åb&[ççÅlS08s2›ÍÖÈ©eŒE `NƱ×Á@ “”jÐþ²ŽB=@~|üûãåÁ a¤Ç2»î>¼æ»êÙç Ñ„Î Ðy¤ñïàŒ™´Ií[ó?ý¶ÝןœéoŽ>p‚@²fo6â^°dö“m'´çè…ÇžÁìµU*±MA1}¥N唓ZÎ`‚ Ç#hH0Ž¹?l÷õ'gÚG¯$iƤ3ƒq”¢ ꨎþ ¢§œ‚:p¶OAš~^¢NŽk;÷ù‡œ‚Ì>G±¨½¸ žxž8X áÀT-74ÂÅ
+nZØØø}zVaVôwchûµÕ6¦¢ÅísÅà8ŸYh*ÇÈØÔêŒãS¢y¾w<Õ›R¡b¡UÁòÔ¤|3zi±È›E]úÇ•–Ê—¯bEÍcW}ºw_Ê
+ v
+ÈIaÒRÚ5^ûûøƒ<ã@†I· àµÕR¼K>ªyKů3žTpÅrFæô(¤H—dª¾yÌaÙ~M³¶Û™/5Ÿn…è}nŽJÑbWdß;Fb€¢$Ûq³‚S—NÍçÁû±wÊ€w8}›?•â÷eD™(UÐî©RÈŸ<*æÖ
+ýá`bÓ!cê¢6° !ˆîÄE ¨^Š2& ¥™¨§’9Î*ƒG'|„*÷N É­é¼RÔß Ä\rw0tFäbÖø…Ô~.%oCðëºÌ­Ë•ÎðA½9
+£¸c·ˆÎy[Kl¯]±¡QÞÀˆÌ=Rü€õb¦u.O”H‡$Qˆ.ÊÒq43Î"Œ.R‡êu¶w2~h@\ïç8„Ʀ,\G%('÷ È=±Ñ$‡µ¶ yNŒ ð†ÙÈmêÓíhr®š¢Pㇹ£ìŒßÛ2Kü2ø©HqÖá¡]óh¡§ÛÊ0>R\Fñ®€Ï¸!5[:Ö±2†’ò1­u¿ö[ûWS·•Æp8Á¹·a¬ ¡ârÔÞÊñ9Óc›¬ ‚¸ð¼ä©#®­¾ù‚¨8ë"ˆÛ 4,—gŸ­ÆPF%<ZƒpRE‡9sœ~ýöúÑ¥HüK½T?Î[Œs 2ÀxŒ< Q 1 øiS0Ñ•Æ5¼S™'JQÎÚ¬`jð×ÈR¢¡d¡F:¨'Eÿ ±é§õ¼èªù¼Ê7½‚¶ã
+*4€í!ưĶ«°Ž&r}‹Ìcæhnl;[]UüyšüeŠÃ5¾òáVrÁ²zÊ¥ýLúóu~ éÈ÷®è‰):n^Xq« 3è8*“VFÔrJÐ[)œÄ±DÀ¨í–]¢çÈ_4Žÿº¸ÑV" ófxÒKä~Žh‚’ŸCuvz×üL]Áa
+Ÿ$9‰í;=/&™ ÈŠºoõzœ"žVï`ó}Þ(8±T…/y ݨ¹Š¡¢“WæÜã`YDRŠæÝ;[ñNœmb¨
+iWÎ;$JHlÀ[vÛµœÐ22XüÚC+TÅ d¸ rÕ w¼b“ÈÁ2pUëýwD(’ãDâ4N y¯0!zœ’~xIR6i
+O¦ö<̆5+£úyI% zÉì»zr
+EjmV(Î) Mï7s¹í¦‹<9=ÌFIŽB$úâ÷»C¹ê_îïÔÞizÛ}+¢=æÔ­`˜IIZzáG¿ šARŠ Ä8|`-ÇDFU°8L'8ª+ô
+‚ߨì^‰çd>Fª"eçí^/ÃGx(ty9²ñz^r\ÆËÑõ’Ã6‘§äAѦÿg«W»ˆ¨Â¹‚mõÒ÷„•B’žkX'‡Ú0ïS}§ª©Õ@w$àŽmá•qw’Ó/Æ96à GôÁ;QóGÉ™\e|–œÃŽ×y‘§AÒ =hpI`ù„Š–¼WPtq¼nvâƒñ•§8Vö"&¾ˆÄÎi¡Ðò"Ebdb+*´± ŠÀù–>º Ϩã9¨ù;®Á
+Ñ'œX×|‡pÖzËüÅOØD5øfD’Üþ°Ê€œ¶Rˆ+:½Å:53g%©Á¶5] £Ì?ÝgôlØi`£ÁR¾ðl2¿$%óXq ÖXéµ”“·(’mÀVÉ;úy€7ÆM…òQ‚‚³J±œÛ.hàßZ‹4êS ÂEân«K·”N`
+J~`øÓNØ6žhËt~XGœ.Š¦7,É& ¸l³n;ŸEˆsBZ˜çFǼÏ"Ü#é Åhvm+À€H:ééFº@”+ìÐc¶¨™»ç (³¤y§ÞÈÄÅeîGáÑO Æd¢)ËÜX‘GDJØý¦ÌGýS ýë×åßÿùU·f Î$åÁ½¹z¢r
+AK¹çŸ°EØÙ?)Ì©„_ä–É,ÓåsÆUãÊ(aªÀk,rüu–Ò¤ÒøHpVïÑeÿa`—a|01—6ZGw°;J1¼„lÅø1
+KH F´Œ¡Œwù­-ƒ ÌêúâWiÈæŽÿ£lÒyxyãa¨@H-ì¦oÂ…Î 4g¨Iꊽ
+
+W 
+¡ž"Sg§¡¼Ò‰L6o®"Š9IÊ›µNçö’¿h£\VFň:¼áV‰ü ’Ï{®TØNœ„ï9Ž+iÏeĵŒiÎñY°ã\ªBùZ‚ç&ŽÝäyq9­Do3I’úšžõ‚ž_ã FÁ2͸O±sŸ7ty¿7îI©Vć†óa«Ãý]ná78ùlÅHHCðУú*¦#|À€µ³USúÉ ½¸y#´‚,Æ+¨¢jä:Zèº6|̤ϰâ1Qg£wC†gäcéwtE¥‚™˜îÞ‰Œ…Äð`$óØÑÛ@!"!ËOÙ¶z½pÕHÔºöv
+oD,ÌzfÜåÙêÕpïRe™¦­
+wR oºoþÐMÚ`³7™×Üø£[.QãöÙ…Ð<°†xx4CÑšü\™g29Y×y0yý†!fËL/²•ÔAæe¸f Ä¶’;@>Ù)Œ×²ð:<2Ìø4“Ž£¸æw¢')©£ï= ”…jšqÒeô¾ó½û0è³ß?Qðù}.<QJÐÏYø³JIÐÒ4M\à”ù“ EÉÿEø‹=
+jEÔðÎjçeHV„JÀfïL›P£oXñ”ÞîÜpx¢Üž£ˆR(ÉS•Îf0ÒU?U<-(B^9®žbêµtb¢ÍÊ
+*û*0Ô–,Ä>‚†ŠÂÉâU¹ænÈ´d¹ÒT/ E$ÀWn®Ä±'šÇËîß±9‘§( ¼ÍÝb7õ£â8Dä¥á‹² -ÃLɃԥëÏè, oÏ2e¼y:è{ßi+ h.¡yLuô6g=_øÏÜ'š¬í3BDš«¬+4XõžYˆ±‡";kę<Áš5(úåõã¹nøï;0ôN_êÎÖ¡ïï¯(j°3Cû’ÁbQ?‘ì®®ªþó$²¬6§=[_@L!."øƒëü  ¯ÇYºvs¿¥{™]|
+:Ûßé9KA­@<±Å›ýQóö@‹mÛ8îk1¿‚~þNÅÿÔ˜Mÿ`3hZ›•ó¿/ OïV ]¢ŠXjm\¿
+çHáÔ‚ÒU¸ë‚½Ï@ÎLkŒØ.ì,ô¾¹ikÒߥ}Çüüåâû—Øðÿüñ·ÿý±ŠGR±ßNEYú@Wbú„I`ºö¥‘@‰`ÿˆ Aª/Ý•µð²xÓÙ!,’p†vü¼½­Ù8…
+ºŒC—uKDÀãçfÎ&I€2¤’í2 Ñx¹ƒÁ‰aPL»¨Vë8É£èq]üf¿âè»»mB_¯)ψE*ÆQóëß™šr¡c½!r°t,OºÆCH” ½BìIìá8 ©+”þ*FEø(‡”›S")b˜2ʵöï0?&OQseVˆ]q) íHŽ$ åº#éÒ’o­7 ¾†)m]iÏá\¹ £"ÙüIý,HEŠ¨Ï£¶x«¿G—p ˤ.8žÐzƒ„sŒËÌ­?Ž•­—"Ò¢ösa9KœlQñ«#¬XRšŽw´Ò„Ãåɸu"H76¨µ”vï”LÎS“©AÒ'j³—4$ÙÄ×[‡ˆT13qžã¾õrÛ•vaî}Ô™Ha¦–õì÷Ac¯6>"Å’NÌá(t¦[– 9¦‹‰/ýPÁ„ÕµŒ£¾ƒ☦`n!ê·g1Û_x\¹$Iõ‘ZÉiÕâ{âÁÙ—(­Ûwí²ŸtYÌ„e>±tnÁ„ãK¢µôG]-ëÝêZoHzöbÔþ( W‹•=Mƒ˜yÅ<ýé "ýŒþÂq¶sY0¬¥gF2ÜrˆÐ÷ÈSûáîk=2¸“nV$6Þš¸`_[χ~ÛE6d |<ÇQÈÔ(ô‹™…%Rá¢ó(_‹
+[7D}٥嶞]·éè©Á
+£î"þD; ú°Ùü¡‡ô´#y¡FëG¹q¥rv¦Ëu8ŽÃWÞÄ®ò]G™
+"1vÆÚópÍV$z×X¨°·­¿ J„[,3|Ì bL@‚W†ŸLoýeˆí
+ˆqŠE®ë¬z½ÿ ¤ú'çV[»gfÍÚ¦?$¿½@º{’Î3qòŸÌ@_ôÏ?öS×uÂÁXØ©ó[ ÔÇÄd‘MZÉOA‡b\ñÝ–¾O!{rŠm¢$S¾@ìµ¾bQÿ~9Ê‚¢IëÕ¿³óíŒq]Ï17+c ÔÆFˆÁÃ&º¿8ú (ŠÅ:¯2ýN…‰†¿A>Çà̯1øòk žC¾Ä`Ì0î¶VŒY'˜Ÿ/˜_Cðñ)€ÌŸsLXº¤G:§›û¨^b²é€Jg;7Š”ú‘R”«†Õ)D›t(S-´“&˜_û=‡XÉ…œ.o'}’·û(±Â Ó)ý\‡(9[[’¡&øòÅf€ÊÕË£˜¬]s¤xûÉΞÐnÃÐGz÷Èô
+Óº kc’
+}„îkÙ÷×û%Fz˜Q2•¬2br÷?t.cØȆWÁ½}+Þš[CÍfW¤œŠ…x˜¡iÙJõ(L¾ªP”ù %6A:€oQa®r'ô%âP³¢åòv WáéuQßIlÅoè ˜î"bØHöjp¶˜;fÞ¸v¤;Ô®S¾CÁJ)ªÃðÔÇñnìÆfp‰ …êÒEØÚq9D¾„ð¹8ŸÄµ Éíþz 0!〠D‡ù¥?½f¯WÐKö$Écs°kð¾G/ù<“´™ÜøCôZnÖ)²†ø+ã~‹^ÊYœûÏ"È÷è¥Ò@+‚}ÕáNJ|B+UäVâÊLß’c1¼åˆ‚§%™¬#^[ðâqSO®«ÿ¼œê 6ÆŠ·
+ƒ—X
+r¨c&lekúCYI€L£Hµ14 î*É÷a× dù™ƒ Ép•0’ù6°A.Ó´äSv‡øF’ê]© 7‹×A0™'¶’/”E{RÑ"Ç@QN\Ä°²T¦ôá$Õ0/¦âA®ÃÎÅ)'2Ã3OÄ
+óSR’ØÃ4ç—-È"¯Rµ Æ_ÚʽR¾ª@ I>O
+:â×[*YÙñ&¹ú“ÍxL|~‘g= •¦§šb¾YsrVbàò‡[ç"ÓìOŒR¥˜Q/­-l#?sj dïúU<î ‚ ‘匔è•Îµ1#M¶ë@\’¸V9µä]ð ‹[$ê»m8ă4íá %Tù÷ö㱸ŠÝ¿NÃ¥‡<Œr…šP¿"ã\›£v´néë ƒX0#ìœá¡-ˆ·Tß0T yÆà¥ò¡"EŽ»1f@šÂA&3¢Ó+€øVY³±‡&JŽÖ±îX9?íÕœ¹ý™&ÎF— ¼/=¢Ã™'5Õ/;2:Û0ä>ä¶é9 îöZ¯ƒõ ¾u­SY2a˜Iœ«Cp&´Š: ¿ðë-
+fg̺)î…Õ¢iîàRKBÂÔïÐ.gÉh?)V€E8æ«i^®ï6èœ@`Óšü‚’‘ À,ÜØì ¡• JSdT;ö]mÈ,QÙ³L‡Wè qª²´å8Ë°'í0»~)D×Iõ~6W€ðZbö’UÔ§á2%3þ*L²áD“¤0³x¨j{ ™I¢”¾7b žcÙûy9i_3¤Nˆ+9ÞÆZœ£ ‰ÔƒÒ1…ïµÉ@ËHÊh׳
+>Wá•ÖK~¥†9C)Y7ÚÖ.5Oýc-ØBë07²#â“O7 ÆU°R2\‚½°ó“à44—òî­`™Ç ¿ÏÓU\4u«~dpî#n°Óã:¼~"3…õ¡f5¹«SŒQGwŽ$SægQÓÑ®ôHÝ9ž†Ëæ’¬^
+ŽÜΈP0}ØW57
+ÂGø™‰AšÁ4ݾÓm&ƒU(:bJRæ¹}ÊÛ¢”ÔÄô ²EÏ­´skµ uæòs3A’„ð¸Ã¹O<|!»ú:™oYï9<ÜèyÑT–ÎNE°dÃçJÙ¯Óà™m÷§å°k#à2æñ•K·(²<x,svåÙyít·Ðq£kPÓe5$rÚstyJ\p­{«A ÚBP\`ú #UAç`Ž…Ò-œM¹©Ó/4-IÈ ú„ ø6²
+0Rˆ)Ga–i0ŽÂ`‚§Ÿ ™–C¾¡Æç­ÄtˆÊ0kgÌ´ƒyÝâ¢:ec“HQD7A0¢ñcÄ ²Õ*îˆV<[ \Æ_ºû=€¸Íð…®f1BÄ€eI§§7]¶µ
+=)õÈGð fɤnõê@Ø‹3NþÓ:£¨ÄðTïA³ËZH X‡µ®y@:¹7ÒßÛÞŠú*C¤Ì¼PFh´=çÞQDÊ' ]êâÙi®Ù¥;ÄÙÍu½,ójÝ¿+’êÕöNw+©JqÁÚ‰°ù:wCú20w[«®aëÆ(â׎¾›cu¨q!.°äª"‹{‡«1¶®ÌЪOJiªÒ´U`ù'4ud¶²þ§ ‘A´LÆíò’Uªa/I~œq ó«ÿ_“<”ºJ
+Åau%g8œLF{&ö8öo±]#¤ Ì¸ü°?X²;åz/≹4&ÅÔB~N*¸
+¡>Pê7%˜œ¯¬DŸWy ß`„ÛzÏÐ"_…lú8ç†/¿ê1Q¨û…]I]íàj®-›>?;Äíè8‰ç&úFðc›¨Ä,Š>…]÷2Ž±õ%./ÁcÎ2Žb6|Óm؃³ï¾x´²tãÌ0çM÷¦ÎS˜„9Jdó}|'…)ᯖ?NŸøt,hMô]
+.ìjéo<³æ™È†¯ÈF% K Ñþ,êU±%õ¹Æ«Œß …)æ\Ĥ%gÄ”9”ñ=¨É™¬MH‰ÅôPí:Îw ¿}\%<î#«î¯k§$Ùb/á©B^Ö7E½“Òþ×Pz±]>ëoT¢ƒCû¿ÏŒ”"‚ÑF<èßàåŸqÑ,N;ßøQÉN€9êg! à.Å“¯Ñü@#{8Ρä6¦Å°/iàð€€öC)‘.¬ 蓆õøk<W3Ðèä(+|Ò¡sÌ*ÊþOšùPôów¬âßå¡÷•Q$óÍV«:ÙYè`(_k> ]%w îEíC*g’â“(áÑã[¾‚ð?¹‰ÍÙPb8y­„ q±¦ ¨Jà1I®êv™W‰!W4^*þ°Tlø”\6ˆÏáVú\…IgMÚ]ÔÎìçrÄë
+\LSAOó5°m€1å$ã`¢ú® ÄÛ²­.ïÇpXïÎUÌðröºö©F^‹AJŒús0ƒþíGÂÜ\.F•MmK‰ÎTã;cìêKɸ
+ÔÉLüo¿Ž÷s’ŒÜâ¾ãXWEüÍÝúpYIæ !ú¦_ ‚¹£tÛµ#©K
+TÛj_œ©•µê¼õq£qîÀrÎs
+ÏBxÀåä„;ð õ—²˜ô?Nÿ°èùnd@ïïa9' ê…&{áÍK‘Ö@ ,ŸËm‹
+8Ì;;~H‹Eï1Îum6²»ÃF‰ý…ênÞemÞœÝÁÛ5§i{aIúXÛ©½ð´óþ
+ê–Ï‘sû=!aÎ×6Ôݦ¾É ^­SC¢~«ÃZ{îq Oà#óeÁë
+C?&_±®
+›Êâr™.“0½dÚj,õ*²²9uŽ¯ìIRÑ“:L¢ ëŽISvœÓç!Ì-ŒUî÷sÓgS„¹v=ž«tÑòUúØÖclD£@ø¦äCn{Ôòs‘®ÇÊfmÏ©ºÂƒègIÏ>­*žn±kýœýûè
+…Ç4¼X(FÆ“GoТñ’ŸëI‡*ü…ÕÏp_™üsÉÃä-' íïç|¢éTìjNW§¢u“§Û–góç¿ÒÚè9}8Ç9õ251á~ü'\nñszö”H¦wÅìo´§yî>©ÊЙÇéʱØÇž×µ:œy]wžDñŠCEèó.ÛÏXT¿†ë¾Séø|ž‘ï£HXxyÜ{4îöÏ“8ÔìõVÍ1^) ­Í~LÄ´èã!*èBOÊPÓ‰_ÿ™dág¯˜§þW‰ω΢O69lÚcž¢=90S¢Ä\ݤ[>οD›k!4Ç€ÂFIiãF±«7Z€$b-fƒ¿p¹Ä
+5pèàÆ"Á¿·muµÆx¡ zØ>jìy¢êzKRžv‡ 'Ûã2„å–_é7ÜlûÖ‡MƒÎFµÑoZ(§;\pI>ã6°ä^?¶¶éó,-ý ó¨ðº×ÀÇ{©÷sâÔZœ½-púÖÞ#÷ë5±|.y !'M¦=¿µþvN}ä ¶ÕòÙ¾ñŒ¡~+ˆn爥“E¾Ér8A¶Ž8ò™hTøÆJé× r£ÿ¶®Mxæý¼õv/›@[bÊœ:ïáñì‚“¶Ç¶3²Ñ°Ÿ+מTÔÛ1x1öÝ_ã¾Eä#:Å•ÏU‘_Ý*q’u]ñ±åWl;õª‘‰²Ï
+`rðoü Né2=@¸Õw]i a;ŒÎßñ²[X® 7}Fèíu’Ð*i[%W˜=hƒFø½SãJνB õ¹¢a–R°ù`|‘ð&Ú¸)ùô
+oO LI§8­î5¾IÂ+ [ú¼jä+Ï3Í}ù4áÐ4ù.KºƒÖ´Ãg¾ì0ØD/T¾†øB¯ñôð>Ózš¶áD)ËŒ
+ÐÆEFfÑç9Á^r:dÄê…9{Œæ‹ƒ}­¨Gƒñøš
+j5àÿ …Ö°v!häì¡÷¯sIfý‰z2lµ»nñ‘ñ¹ž“þHôøRýæ×þû‘zk¡‹Œ†|ÜàZXL$©GÉã' úPPÁ•!,µ9 /„Ÿx¨¡ÄZìZ_ ëçÐóÎôÒðùç?ü¯ÿð˜*첶RVýâ("%'»Ëc*H.@ØèÄ=kÁü-å‡qi[”¸*ì*:û僼“Òc.æ Ç÷îsˆ
+¹|’?¿¹Ò‰9ßw l=EiÙ9•¼`žwª­6ÅÏ¿>RÊ
+ )iv£TunÄüMUcÐLz¦8«U‚…CoGWØ¢+&i¿Y0”ÏŠÜòm,,æ¤,HäÞÔW¦]Ò$P×xJx¨
+ÔøéÀTl† puuË‚(ë@#Ú÷D¾¨ãEO¶ì@ô)rt„÷7ç`ÉK‚8L–lߦȼô¹
+IƒÚ®ÿW\Ž‘Xç9¥€PW‰áå6®Ö É#‰2°B©-U ê";$×!nÞtÉ_Ô|8 4Š«@ÛhjócUÝÓ3¬ÎHfbTùoÁa9ORäD½¹Õ]åK ©Û4±ƒA# bþC–1yð zAßsŒ«ŸpÎIbŸNÄšœljáøúða´ðíÀÞµ×Mðù$0E£5¢Ø/„ܪ…¼_„©“P׋$A0;eÀûîs0¸Jo dhsè<NW&¬¥]ßÞqÎLPÞˆß7Jm‚BdÑãŒ"7Vs‘-[\á’ªA@QÆâ»K׺ ˆ“UÍ”+-­á!W-s ÃÇ]#ÄŸK »‚èV«ÐCؽÀ®ѯv_½7 j;l,JÚ{zĈ•Uë,;N.¥/ª†2æI*gÛ/µ[¸ ="•{sÏiL‰%)#Æ}›
+ç"£TÖGaкú¼^%«€™cVç ¢ ÊÀ–ì71¯~—
+|
+RÕe1O›æILrÐ`ïB¸Á*h"»W¹ë0@Ìs‹ŒZ-Ñ%F:•¹Å'ˆÈhYmìËüT$ÌÈx$7uÈ&3™c>œ.tYœ-¤@©ÔöÕM,)F1ˆ£n}ËTšCöiqC
+q³
+hÄ„bSÿ7ó ëÌ®u(¸;ÅÿZÅ‚Ê«Ô•=ô1™aا¿ë˜+6y*¦Ë=ïTéÿ­
+[À¢xÙ¶ØBJ(]Xª"Œ ½Õ æê€ÔÄ„çìŠcÆ’w’ Ê\ïqA2«ÿñM '–” §Mfƒ)@!©€P7”#ßÿœØ³@‘m}‰C˜á½½lˆœ/*$ïßß@;Æm2¾¸ìO]AXk" Um8Î4Ï©rœð=7òÖJõãÅ45þª:^#.˜çfýÚ>I Êá¡j\& à%‚ÍtÑg_¯Ç]1_æœÉHùS€›i¢ß BQ{¬Æ®ø4F½»áåñ!=眔²r$vÈwÇSR k2;^!$’7…åµ€`j¸3k“¦<ƒ¡± gjÙ^É4"¦FtƒGŸnF¶z‡~¡ÉºàÍß@•.|h ûSWÐ —V ®µž²Ís²ì"ίmÏf³TcÇz7Ã2Æíª ‚aM¥sÌ(Äš²3ÝÒ+ ÇRNœø¦½tÐá†GÂ4#¿a¢ïçp*xT`m5FÈ¡Ç|6õ§‡á¨ÚGÅØpy,ôsJ…’‡’\q˜g$å$1 eùìÄÚ#àÙ&„1QµWÀ0õ†)2ÄKM‹U~³Ëz`qw%É,Eª•¡ ©ÚŒˆ…áå‚È ¶<ò]´ÈÏ1/P…+ƒùç¤G&EÁÐe`’½ËˆÄÕ‹Ž;®ož4íf§kÕ³ª4ØSD¦1t ÊlXaŠÎ1¯‹³EêV}Êܾe¢9<A'ð(“ŽQg71×=]Pz„NZSz(L¸ Zk¤! âû]ì:£DæÁ¨ :ÑA£îC0¹‘þóÚw«2DLPa(—a„ Ô‚„kZS ƒ¢§±Ø’þF2_Ãé»Cñ;Bb¢f¼iS£Š!¢ì.,g÷*›QÅ(ŒHožT¡"Ò$
+8•©ŒÍhÄ%\Uص DÎ.Â…6¤Ža¨¯µE—åýñá€Núÿ–)&Ÿt‘ÁÊ3Zí9.¸A¤EÔ;ý+(Æf Jio7
+D4k¬tø` XíÍØFÁž`,Å% äYå³`m"Ó2¸pbn*`Àñêº| 2}å~<–Úÿë•6Ö·VoÃC{À´(Q†Ï_"{eŠ€ÐåX`Ö‚Ìî9óÛè:öRÞW–%g’B¥Áüè!þÌ\†×ÙªLÈŠ$!”ÉA¤i°$³#%ã•Œ%ÂœWà/¨Ñ^ìÞ&*@ÆåŽY.LÔe›µØÁÔd“‰ –4'§¦'¨ºÏ¹p@”]%6¼9²9Ÿ• Ob(òt¤¶Hcö›w‹’•G-
+Zaü3»É€°P8¾âᲇMnQ†+ºçàYYè óÛâ¦H<R(rœ Ñ]“núŽ= <M±>=µ–!H©]_O׆S<Y-œŒv*ˆIZ–_x™é5Òúç¹vWKýcm¢ÄÀ }8 çQNâÚPÑú–~ØeÛ0+seJç ¦°É‚ë°ád[íøÚ*„‰ Nìç°õ(cÌy&²²‹Ì×> !Ì/sÎ冷†Õ,T¼á;º->y"">R· á²Ø.¶<±–SžÆÆÞš¹ÇTÅôaµ±}N×å?i«F+×"¤Ðu¿Z.6uÝ7íIÄŠ
+/îÔBs¹~³ÅÉh: #”öu]hlö`NÒ„ðG„³–åﳺȃ㠳›Hž³KT`+…¼§ Š¢õhT+(°W–„(ObÚ"¸õt&Gš¶n*O¶Y Éšëé&·ôÒ_ÔŽ¿"€…‘æ9 1ö›R”Ü夙H8|Éöáh«>Ä$YEMØÅ;¥BabS*ÝIn¨¸4t}Aà ®…LŽuÒ Í‡ŽøAˆ{a&×d-HkŽH¹ _©òÈéµYAj£<kwAd¥ˆ.Û0Rœ cÑ/˜ç ^JÍ$$9[ì˜vM??(2“*S* €úò@§[<¿v:Œïo¤‹#æØgŠ'Ñdƒ·rIj‹6Ì‹3ù]»°99 /ä¼Nú{¨ë¤”» £L胅Ƀ¨ºÆÛÃÀ‰³+ÛŠbú×¾ð ž *@!Ë¥¼‚žwê½ÚB„V¼7óÄñÐ'¡ $*¿˜"gÞ_NÓ*R–9R˜uAÞIY±‰)É#ß}q®tbη)ÕñÓ,_Ö²r†à¨ä±+7ª=›òùŸþe¼Lrãº0|ÝAë
+š\!<²€NÒ¼Å1(©‚÷GD)“ì)¬ éÊÂzßö9Åh ‡"Tæ®0jip:ã
+´JÉÊ~:œ%Ñ4DAü
+ƒÕ†Ð¦½ž‹0( ' (£*C'¶0Ž l‡,äæ6\‚¯ÈÖliCiPB’ý¼B4úÐ4” †®„×ånã¡V:µ9l”ú$GÙAIó«~¹›ÎÅ£=²{’VêOL®rBÀ&?œ‡ "g› Qíù‚\æ/z€e¶‡D( n·XOŠ‰—ð18¢îã.*?Ãh 蔵6eàM¯ %IVU»ô½4UøÊ)ôZM/òª(Ȧý;ígY!K»6J:-¹®êfÍ2Å_ãxN†²D‰.®ëªÏxÒ±5t’‡’ܵÁO°Ô!£Á½çvQÀ'x#¤-C㶠¼¡M&‰š0ý§˜¥7cL`G$â··C¸ZCËØ-Wi‡ ñ@³%˜Qºûe@œ®°DV+Lâ~vg%¨ Ã…àR†…þ |ØÀ5)F·ðÀ/ft–Ï Mè¬ׂDí¸Á‚2&‹-•¹/03Áî"cÈ8í›nÇTc¯öpB²ôUŒêJ‡æ²Ý¨nï@¦Z^ÝÄ 
+[Û}´x¸|, ªqÔ1l òdGÇ8S_TB¦jôrtÉu1cªØj!lB&ÂØìpÌIã î)k=}ì ŽFòÂ1ÛæÒL€7®I¸’åÑKA”·K
+NÊAõó=EÎ ¼
+æk—`’ojU3ªÙR«—‰eê~7îþ­ðÅ*1£½ð†
+Ä„Ù@j©%ïåç€$‚7®ßŒ7™uÒ)èqõA! Ìj1B#+]Ê ,€vßÞAÚà±
+ú†Zm!$˜ûél"5f;¯i#ãžÚÚ¢Èà·ÜA—¸P ” Ç«^dz€Î·XΛB¾íM"3U®¥ ÇKn§qôÔ¬Áb!ÿÕçI­Øš„aI- _€|“5&à"TC®tѧ4)×V|H$ë ú¡4¥~+ï-™I²÷~ Ä¿_aM'×Xà(ÁEÐö2„@t+œŠ\»´~å_†;­‡˜ï‡x½’Yzæ‹Ž+†±`$´ŒyoÖKA‡Û0ƒÁ¡2‡öz¸·¤KålwÚ÷÷Žùx¥F^ÊdÑ!Fœm¯ãð:JÙ@tÌÖ‚nç܃î‰ßo»'þëŒvâðË\}ÑÇÚã/Â
+V`0óä2è{ƒõ!VDþ8MȦkp[¦ÚÔšÍàÿ8F€oV‰Ãw… h»1+èû~ÒE)’%à!×Y_aH°ÁmØéC¿K%¸ëçU )RaÝ‹‡ Qi&\
+Õˆ;I”–ÌAÜ€l)q¤¸ýX$'™cØêGˆƒ(ÿ!PC`É:˜Iµ±CŠä@Öl>Ãp4]`Ýcëô,°”1ÔG1ë]`°ºFÇ œÅax'ʼÎzXt¡J«H×ðùIÑM—H’&|¡°˜
+^ç§(¿Ó1Ì_IYÈŽœZ!°øÈ$N¦í•Nr‘u9:YVH›­ÚvW«€³d ±ß-@¦r88iåVÃ*ü¡9 ^'CÍ8õñ~X:Q6š”¢1õR»°(/ÎRŸ‚>vÉÓ$l 1§_B’ð ģ߰O][¿Óa½E2,ø^)CK_xGèVùÀ.0QabXnÈ}ï‹€Pß*_ÚBdRïZüƒƒ.R3i"Uæ³¾b(Cƒ¥Ô<D˜BÁèi^°J(B¼
+ëÊŒ=X”¯3÷ll'EÎL —¸ˆwYb.€©h•ñëAú^O…°þªºL]l2•¨ož;„;
+ÏYÎÁ‘ÜÀ$:Ã:GJÒZ<aÊŸB-—¼¢w]ÐÀx¯¤¿³‚\·ƒ2"ìDd dÖ6}7}ÅÜ$vˆÐ`Š¨µ. çl
+ú7¶¾ØÁäÄ0¬‘­2gÙ”¯…‘Í‘qìéaˆ-Tº­U§Î{SË-M£b³{Pò
+2Å ë¾î• ¹˜RŠ‰°…B²Ý7ý)èãvª¶„!d/"2×Pª
+M5a`VŠ–&a@J—öö$ —öÍ ±«ÜÔBFfŽcè½”“ £ZÓt9^zÌ‚h¡å¢›„ç|´êç€ëÒòõ˜M‘½’{@=|õ&X±H¢¨cmd TÚY–c…O!Ÿ:µ¯ºá¢JÉòI8¹S6¨ª %#‰¬ÄUO’Dah’}Eùžkƒª¼Šðe4N6Ïpª’ÇN=…ì«’– õceajš{Sæ>Ï+!Ê!÷óôñ¨Öaì|ë1}ÏSLmÐ(ð9À›ö ßwA‡sB›ÚŸõM›WÇ£(]
+-grV÷mC_ʘÖÑ‹gu‚qªÐ|e˜my p‚©S¥VwŒD\MtýõEP‡º&
+äÇcz”W¤:D Þ2ù„)cȬ²ôô¸ç W* ˆúí¯·ôà#š ''’êõ×%7Â_ßÿ}+²PÌ":ÅÍæý^ÒbÔjŽÂC
+
+y ̿a³Ü Þ‚¾Ÿ‚Jy`1è0ŸÀ)t»î9èã•œôÄôþÇ?o¿ýÇ;þ2oۥȮEúÉK6›b21dÉgVxÌ„C“³–>‚½é[Yíô݃€
+ø€dD Xz€Db7Ý¡&2¥yúx!%½“‰dOq:’T]ÈrÈ\Adm á½Ûñ,Ý·¤"Y9 ¡|‘95‡Ðµ[Ú™ÿ2''Ê÷?)íœì‡Zšw AÓ ¬g×wÙ»üÞ\¸ÚŠ%=yH’µk²oQ+À/aÓ܉Eàv€!k›»CAŒh—r¯½]냠pû¢bt­ùŸñrKÒãÖað
+¼‡YAJ÷Ës²–óäìÿõ| ©‰Ý¿:•TR1R‹1ÞÒ,òÉ‹Rð$Ro°„¢ó]T?¥sºÉ¶Ê¬¶"Ô+°RµnÇ$XÔ•·öùÞGÖ@øo‡$âk•Pרý%>/ea¿©Cò¦ƒ6ãsdò*4K‚ÄDqÛŒn!,ø*ÏìM7‰v²h®fKížVcýžTòúùbs뼩˾A{±ÊRøx¹~†Õ ¹dcFút› Ÿ=e+öné5|¯Kµ‰ŽOZǽXévÍÔE,néL£ W®Q3)ù WF‰Ù¸hnf\ÅÀtÒ
+ÏXâ³ÔtùÐØ«&¿‡ð8„ÿÃ÷«ëêa Ä4¶ì‘ÔÌ™U*®""–“¯,ñ=¤å£ÉRª1oVä2ìì#ƒ ’5-Uw9nW9ü±«n vÑÓ>9UEd¦„½&¤€o…ÄhŠ·*['·~ä„ç’}[»Îx-Bñ(t/1{’Њç÷q¶)R ë˜d«’¨G1ˆD 9 Ì*Lš|ÑÊqLÅ·….±765&Õ½ì¾bH»•¸ÒrF䶪+)l¤0ä±B×
+ f»-1|º|ˆÓüéÄÔò3¯»ùí2-VÖ©€yœÄwÏ!ÑÂcþâ7Ñ·ì¥_Ïû©´­…b•{Lºƒc† œ¸ ÜŽGÑmÔ‚dæÐ.F†Í‰ÛêI4 gâ‹¢¥£Õïè[”˜ŸN=5-ÙŒF‘⮣)Õbk ;‚]µ úK2ùš‡L.ÆÞC(Q,Øe¯d&ºMŠ=˜(lH º92¥¢ëqݧ.ɈT§{OŠfÇÐÛ—Y^vø 2ñ‡¸ž_ jýfä©×2Ó»‚Q&Ñ1Ë÷Ò7&
+ª‰5n‘Ô
+÷N&‚2ë9ÌnJ>è,¹×spû¼Qüë‰/¦6è/:Z|¨¬~L·<]Ž’žSÑÔ:ÈîCëN‰¡\ #ІHB[¯õ1š
+Õ/]«
+€«<”Aa’<pZPÎMb ¾€¤k)ŸKòDÑvÿrHR–Ÿ-”T5›¶»Ì ó\R‘>AáTbHÌK%€ÚsþÏ &{…¢…röϯ’u6†MºA“šÐ*…ø7È“ƒ71|@uÕ¼œ£KYÿÊ„>± p'Á\Aä
+„Z3ìÇÔj2ÉLœÅ ™¥ñy‹ªÏì’óX â¦OæQš+¿ƒ(W9„E˜bá`X»rl9Ä€p ‚ëîù
+©‡Rû“`dÕvПeçøòˆ ãgvÏèQoû úH&×"“Z¹S û†¿½^0žÿ¨º>‡…gÄ×T  Ý_Ebk~á „cÄAXðiu58ª°§Øùk{x™dg?<¯‚J#“ó15‘¿ÃU|÷á(1c+ýv›ÞJÈ
+?X\<ÚÊ”bqk4¹X›øiˆêIXë9¬å zðؽåé¼dÿßüõ×"ÈòÞ^j¯+Å¥ÞÒ° âëouNƒäFE©e«æ¶uËëV¦©c\0BÈÎê@z’ÇAˆ!KL«aQeBøÉÎ`_Ú… «Um0—BWH3{h<Cú9ý¦$>e­?gF¹YDªI¤! (B˜59×QŠX¡þkÒX&"Óý±B}0Ìή1¹]ç
+H‰Œ—MŽ¹„OÐwè LA$¥u¿¥o1À¬Ú÷ß¾/$f ¦+ 0¼±Ë‘¢D2‚A³ùùW«þˆµ¦­Ù£ ÿ¬s>F‹Åc¬¶ÑÜbš·±!V-Ê*u–ÞÒÌz+µØçß‚x-ÑjŒVK=ç€âÖÌÃö)¾ª·¾Æš5RmxíQê:w +sõéÄÉS¬3}8fÍP³zôY­Îš a1jÌÁOkŸ3}ŽémÖù¦îkÊe¬Ä¬ðQº-ã‘BZ±9ÃÖÈP«ù,3V«Õ¨z_Üe8yÜçpê´Ö­Ï’ç+eñ
+'(³3²+êZ*ó åÊß\5Ý’“äp;jÆÃ
+PqZ~Qþ~1§Ð1T„OcŸ3gW{wn´0‹
+/ꨥ¿ôý¨êhêDkǤ™)ñ8WæfÜšFË.«ö­¥ñÝÌ(%l; 4ƒ«8yW­MX©"A†Q®Úæ)Ęk™ºñ@œàµÏÁËë)§Û ƒ?~ Ö©'Ü Õój‹™P ï=z ~/°§½º(k<Û…vSú)§/2Ó
+ 6gBÆ€$¡
+® 5I;|„¸žWîôÅàg(pćD–Ñø™v:©ÃÒíŽÐÐÍ/ky•~v$«~~½¡(wƒSeQôEïAý@¯êPæû4KI&§ôûÑ\RÀ?!þ¼’~ôÙùè¨%å`äªr8F³Ç¥–è6Áþ ' ·RB?©Qö&i6B¤r)÷¼Ì„bª.ñý@ôy¡cmTÏHá3HŸC¬<â‹ÙÌ}o%b–ÆmÚ ÞØVç©9kèWúÑ*
+“§@yž@òš_¡Í›D‡ÚÔè%‹ßÎÌB褤9S¬AëÁ«3÷‚NçÈ…¸½
+žhµ¯ŠbòU†¢’ݦ<=Ï1c T4Ÿ¢®0
+"ÎA½öý©&ò8:³NÔ=J<ˆ ¿Â´m Ǿ$PL¤‘¹á?§\Ü<³‰î¨}#5ù(¨ÒÔ5#™fñ¢œ3{‹²0cyê'Ã4ye¨»(.HgúYšÎ%‡ü?ˆ ý6"ÿ%øמüì‰þ=$.}™NHSöæ£4ÊÛ UìtÜS¢ Õórïƒ^ÎY(3Û
+ƒ3½ÉÜ×
+«!õÌ ¥ëò>H6„z3cax½Î1íê.)Ôÿ_øÕn'R•B®ûóQ/Ç°5(¸i{žu‰Ìtð Áv¬½²¬kmøýú¸ÉYOD¸úö
+Æ£òÇäµuU›y¼¯º©zA0ëlÔ0_ŽÄQ²D» rÐU>0ŸNñC(]MûÝ)î" ;ÉíŠÎ5„óÚ =òBÃÅÈZVíHíº '4d
+¢×«žè
+ X”+”vï4\r|±ÒG\iôk©èÞVðÓÆÚß8ñ«ë’løÑë/{œ
+CmŽV
+RÑàvQæ? ´»Hµ(¢bd³»v0ΆµÇËÃÊèâ‘‚³œºa€èïã¤XÄp L!âåòǘ£³‡úò ÕÓ Zs9î8Lr@1Í®.Æ£r¨/Óêtç/o²Ì8ãVDú«_Å‚AKc‰›tN?¶î©¶ƒÄ¦l¿óž ¬LXbw}×7¯¦«!IÆ­ãmXzœÓh¿ƒ|}üC,ŒÓªK;’ö‘‹¿M.'Â<û ¨?°)ÑЬñ|µ&ß+Ë6Ÿ––ÅFì«ê¥‚Š¹–Q*rëžû#te™¶¹Þì7U— –éSæý)»÷XN‚yu€.L{$q<ãÀJæf\‹ 4¤–·­x”†¶Ô–Æâ'L;+ï„ágP /`ýŠ„ÿAÜäkš
+‰¦zÛvg¡x”´ÐĹÏÊ[áõèýRß@°.U}=…<£¢Änž 0þSx– òHL¼d®½SŒUÂúÅ;±Ô
+ÂÔ>Õ³€b¥¥…ˆR¾Tm]\!pêþ
+º-샨;-úÇ Íñý'
+öKBl=° Ù,²£)
+”±EÙro×VY‡Œå»j/£ý>Á׸ŠáÏ-×Iã^s´Ÿ
+‘v÷ëöW׈Ôú{‡˜CdžJÇõ4ÁµsícضxÚ¶[—²‹2³Ù,‘$A*þ¼0
+ÂH¡0FÇ`@¿ÂØÇ È†ìsPb#¢ÛñhöÍtŠY4ï!jW:¤ªEœЫUƒÀ~Éøƒ~Œ‚ï[2=Ù+ÖÆ°;i³ × à/8Ü´ÉÆ še¹ós
+Ôæÿ/“äºnŠ® {ð
+T$Ávœ ³ O•ýOs.ª¬ÿùÊ®$eGÂ#H4·‘lÌb¸ëV©}Ú©©öœ)%\Îä!CÎÂÓ 7Ôs&çÚ#R©[&<?
+a˜»ÖHfM!š\,‡å³Y¸SöÖvQ!X;ƒ ³j03¦ÆN*Ƙe0Î9 tAd>âʪ×+r]äDþ7Hz¸JÎÿ¦C#†€`ˆ¥“1U{,šŠÎ…¨`sÍøäÍœSÝbÍÀÊ“
+#qjÍÎG€Ð¤¢Ö"„Í2™íäÕ”^ë%P ™ÎLfšzº)2–¯á´|)ÎA·OHV:B$2&¢KÁ*~“ùÌèûh”©w6î©ä>+Z÷HJÁ¥\Ù¶,
+
+’7ó»´ gJ€’
+€
+Mv×Ù"É^‡Dé.`ù¾ð3Äb—OU˜¸r_Õy;Ç1¬ ÔÇÃmಥ‰¢©jZ#$ xU®Å)
+^C¾÷áòÖÏ÷ Q·œÀ
+¥qP&p7©Åe'»@ï¸ütáÎÓ‘˜äçkìsÐ5KFs¤G›ÐSÛfü„£Ñ!r,5–†Xê°­®\¢ë fc±òéú2¾º’3!L5‹Gõ×áØ¥nÓÔ¹u{ j¯b¿|«kü {F*,
++üQû>á&É ê{ ©3ÁÑËà!è@EšÑÖ%D©ÆÌtÛªÙ®çŒÍo ËUlÄ9¨)œ`'›¦ô+½•¹+Âì´T‘ï±ðT =‘Mñ(†
+™ „u›_fâí´z˜¸£Ñ¾ã“ÆLãz`î¸9ÏÔ%1YWÔQÜÈånÌ$ à“$EOæÿàœæœïLúA!¼cƒéù¡~,'ÛÁ€9" ³ìÞù1¯N§2s%@›Ã®Hñî.ˆÃLÐ#Žž¶á·˜fÔ¶ãyù6~•iCåÚ·â­ÝÔ;TÞ‚ø â_Qð½ÙôŠhŠÃÝ2.MâÃC`8ÓÚÔÊz€;F¼"mm` Ò¨6ËéÇJüzB®eîs¸à`6øëÑ+Ô^<etvR¶À£å Iz"£½µ²Öãˆ|ð_…tûHêGJ¢fƒÖa]~¯•Œ^°5ôÅè'©ÇRc)íô+n02Ùd!É$ñä\N£$†ÙnZå²|ïšGK¬ª‰Ó®ç€XÐL\ãz¾¡ , új<=Jâýl˜Ê2®µQ=-‚²k%3ë–(˧>H6ñ4îÝÖÞobâÊm]gF{“m¿iõaöø
+[:œÁp]gXà€^_A<×M¸…¼aÿ-È$gÁ²R6Lê6…´YlKŒ°‚rQžKâJ¯bdlRtYUžF9—ÜA¤B­ hK
+îì܆B—áà/
+²d¦º:ŸéíS}¥ÑSAd¶£$¬­9›S¾„øò6‘G!úJõzô%—‡y¸Íú`@PþxxÔb(eåë×Ú
+€Ò…(ÌK r -ŠˆrW>k#3œGÎt RºCÒ$¡1A¦
+Jº–<#âv@R9p?íÜ Ì©Ø‰UìQXŨP§ö&CK*mHïÜÜŽÉkššOC+„¿uAx=Øõm™<UÍ!_N?Ń“`¾Êy#U:Z âK‚"•lT<ê=„EjS€.õ’ #Äíª]‚>‚2üÛ`ä¦VÂ"‘‹§Œc]ùž³Àê°(t;%Qñª$qºß]d«jE×A>qÊÿŒ—I’¹DOÀ;ð4ÌúµÔ9´jÝ«ç/v~¤usE«ò1ø@DbŸ ~ ”£^²›y#‹@ ñ†CÒà Æh¶¥
+pPT\Õè/@Tgœ©h51ê‹)mvý®L¿kÑpÒÅL¸Ú± Bbâ’2Õ½€€ÇÖƒZqîÌš"ÑÁ°˜d¥ù£ZÖ
+VÌ–pÞÈ;ŸC×ÃÐgÙî&·“‡AïXPd/\öFFðF8äav –ó.'ä\;V—>¯lÇЄNÉ.Š8ÔFÇ6^§`ÄG†}Ò£pëSÆ—ûk«*Ryh[q‰çÐ?*jǨ2Œ¥=Õwý€@óžò äiÖ¯ BR›ÐŽ<Ÿ3ä¡àÚ1ì'>•)†T¶¿©Éa!^¥ÆòñZ´ªÙÄMÌ?¢[ØÆtH°'/Ä,Îù gÖ³Œ€X°]Š Ýß-R‡=(ÃaI&ŒcU.šàW©8ß:r‰%N;Ëœ¶s?†®¥‚sä3öâ̪HDE·ácÁ19‰g…›7.¤ºKŒŸLkSû(@ä÷ãs1¡.CÐB Ýzrxºlm-N«}»»uq3^tyÑ—sX9à$¯([UÅ©9£`öãµðë'ƒÐmvYÉ&ÇU»*?ðì]ÔŽŠQZ^ŒL&–æÇQbxhŠã Ób#ï× §cØ!%NXrñvU¥Áü‚‹úG¯<Y¡<öV’ÌÓÕê؉žüªŽ `C’ÜXȃ|vV¾­%®BA¤ÚÔ¯äu…˜b¥jǨo:›?‹I7F† *oL\Î.¢Èõ/ X”§&:¶Ìñ¡Ÿì„0Ö¼€þ|ñÁ°ÒH-¯:<~A³”¯+Õú°« ŒcÖš:ê•`°}éL²ø†4Ûº)ü„’w94'lȃ1‚{ƒj¡.Y“¡Ü6üc$VJÞ7!C˪;;£1å QIâM Ä œÙ€`¤s¼xÓLL)V›¶ºéšÔ»)âÆT
+º‰Ñ·sÈ
+ˆ7¡‘ÃÂJªføzâߤþb $ÖæøzyE™‹Ê­ãeÃÙëâž@³ke¼PÔ†å5“›ØS¼1âIÌÀK§ î…Ø&)\?gtȘC0¶©^Yà!B>ÅTõ'ÚЮ
+¦žsÜÚÕKý­'“}C
+O›Z¾g0x 'W D¹e9AJ“ÇC®ç5¡í[ƒè×}Ï•a„Èžäš}ÜÿoéT
+Mò‘# (ÁRsÛIWì
+û¡ÊÝLcña8‰ÕÞçå4båú™uŒ9ß"O_Ã{$®‰s.F‡_?¾–¿›è@ùÜT˜kî#D6ås;}kÌ“Ÿ“¹“R@•Å!XK>Ò™67™2(I "¾½€Ëùoe¨žjÊ×­ j¢"&”,Æ6Úß@¼JꉟÝ^¡oHÃ줌ÈO9¯;ä9ƒ7V|0é°7Â|‡Ñ
+›§öcúfñÕ…sÛLÕø'92[•Í]óÿ=ÄMß †…ÒÍ•ßÎa“ª4bÎ"˜‰ðÁ@Kén¹– èÏtGÌö¬Ÿ/Y%\
+¯Ïj‰ÌHÜôÍÅ9^Ƥ®Îâœ9ÞZ_‚ß©î7¤Á}°(–­ç˜'è1Åþ¸éïרÿçGþù/ ¬ÿ /h,³<ñü…QY æÏÿªz "5p¥3µ‚ôˆºØ•¹”¦èÕ®± C!²-ÅÑy 0ù’ ·lF@3(s“¢Qà+ááe宺Åî0xó\… `æØ’éW5 j"#ŸÌE ㌜´bv_÷µH§«Ó{ Ý:Pi,ÞB|ßÆ99œ–:²S L0f@¶&é“ÉsHêtB›ÔL&Æ/h2i«0ý窌]÷×5Äï„°òùbL'ÿ&»î¤,||Þ_ ò±,›5ë¹ê Ô”ªŠö ú'SÛ®šR­æŠ
+ n¶ž×¶s²ìEù€ ÔP\‘Yœ!*"îQ`%‹¡Ý-®R7ycEJÏlabù–Æ\ûUOª&Å*ƒR¯–¡&p­í¸¿¥±S2vbÆPT‰Êøþ>j–‘$ÔñéÎNðÜVŠXRÖoÂÃJnɭІ‰ÒÒx#èò?ÆË$W®c¢+ð¼!ûfì©wñäýO}"ITu³ðeÀ€ôÄ—™— F?TÌÜÖ#Ú…3õôòMÏŠªÓŸSܘ?ËTÎ1xeÐÃÙyùcÀ|1"§„5ç×
+(Ì^’e5Q"å.¿©nœ^Ò›ºa/*!Aß4Ï9r
+,#åÛ°’"b‹›Nû´\-ùÞàód
+뉨v9¤Ë-êb¹ÒU#…Ónq±¨Æ±*DŸ[>þþR‹á ªºYâ¢ÏrD€Sµ1•ª y=
+ÀTóÊ£LáYtgã¶oøï/Eà‡ée‰¹gXMf•z3¾@
+!£ŒÞŠCgëø˜y¿Œ’­¾c#Óñò·-7?¤¦-ÿt²¥‚ÿß‘QØq!.ŸlF…"k<qÝÝ<ú'§¦-Ë52Ì{& CHS‹`1”<h¬o§ [{‡œ\ãÉÑsÍŽÛ~˜HÚª†_…3 ¤cJx:žKwùÊ]ízÎ7ä(YY>¦ß§7Œû
+ÇD’ÊCöœ€¶N…b+v“Žîö¬0jÃCJšÄôvÜ2Y¥Ÿ/¿éU‡Š°2„0¬KŒ²‹”tþÅ
+—’¿þø‡»û]Ïm8‘ØïY¦¸ jþ{-ÒB¯Ù™Çï3õ[·a§Ðìrö7iËp&SûàÞxáC
+´Gø
+cÈ3ü™¸Ú»¿s=& bÓJ`kvZT/ﵫXË— @䶧@ZËÔsŒd¨HÐ"·q
+S
+€*€nDÎÌÓ‡@òPµâ‹@ï±"4·Il=H²k2PD<Š{L`%ƒÔ´˜,õ¢£C2b¤gÍ‚ÿ¦fa9ª †Ã¦ý[EÖïç·¢_ÃÒµB¤&¿]ŽÕº–|¦ŠkÑ»YU Ù,1¬éÀ"¡9Y,9¬ ‘«Z.Éïz±‘Kb_ßÎÁk£x 9§/%%É™"1³}¹
+ö‡ž;pi×s(âh-L¿•ðÛòÌ'Ñ0°cÐx£ùõRôÓ‹”AñXðéõ2Jr§ñ
+•-‰Åd q•§žÁ °>µÔûü)¢Ó ØÁZÃ×ëÏÝ{Óp)aß?€ ^VñÍMRÒd©¸)aU¿¸òCn›(Ä÷öoçˆèˆ{˜çž¿|U‚¹Îô°Èo&OWeÖ›tÃÑÐغ¡š¸€hŠ&i.V›
+®è¼„Íh¾ƒ[6*ÞE¿ £ˆŠbòmê
+TÅ…
+óÜ6ÌU0É˲‡ÿ^'I–ÜF†O ;ô h™
+·CÛ®uŽåÆT÷s /æçkýtFüöÉ|¼‘ÐKÿ—:Uýú÷¿²?ÈÒ§^׋ßdù?=ȺKuÈàþ¼%„l!׫T~lu ³’¢ñç¥ÿÍš
+q=«/@‚J¿%ö´f
+zj$$p./i3¢yÝû¼J™€©®¸îÅØ̮ǚŸ»ŸCÊH+­½–Ëþ®ÛÒ·X²ùÌÂ\:Ž=e¹ÕŽh¿Î ,Ÿ…šIìp!Šäñ[i×rÇßì<3¥9Ó5ZíD 4 ÂÒíz=‡2iĽv©šF -Ýo¡x†NÝ$’wÌo
+Øì¨$ÅÚ°’p}°ÔÀO˜¥îÓ~ Žä‚~Ü·ã çèÒC¸Ž¹+ÙCfˆá×1ÇͲÖUà\‡íY±–/þ®“O|‡×²´º
+n5U}qôYxLþ¼¥p[RšZžöPרÔcSñݾÉܳÑ5NHõÎ┈#óe¤hDtEÎ"óÑaÁOÓ;pü=ˆŒÒì†:½ÿŒ
+@&w{ úûCSÎ(—o.œY4d5ȾN÷|zdï¼Ï©IŒÓÁ–Ñ$põÔZ´é©©8~ËÈ7«g›Ó +èØ=°Â$†JÈ”<G„]BmÎøÑIÇR¾’›m ν…–Ô}*-:ëc¿/, ½ÜФµc>Ø {]QŽkG£8¼‚Ï㧶Àý¼ªA #Apž:õ:ž›tþ¯A‘šëʼîó ð:ã®ã«´,YpÛñ±L˜-¤,DåÃ1ò 5wš/–ðMEË$%Ò cå¨v»íX“uÓœS)dϘÑ>6s“„‚&þ¾f„n4 ¦bßê›|s Ñþ&¸h^îùCWÓgz€› ?@Q¿»Œ4Iú]Ò'c4};T°ò´yÙo®GDóNú”€Î+y{geŒ¦P팻‘¶Ù~ÎDé ílSpJ´Dnî}Øü–ýLõïóêw™fb#ÃcÕÓŽ¥ä×|HYè¦*öŸ°q„Sò;Y®leý«¢¸ï{¦0[B«Ù¾$3’Q¡º Ú;Z¯±½T\Ïê#ƒÀ‘çØÝ^VsTk+ä;Jý’…ÞbãQp}nŽ·™ç¿Ê
+Âè‰Ô9á%_uzö3:ô ¿>unfŠŒPú¸Žq¡è"™š/-ùî¿™…ÇúBæYÔ ²ÀFµ÷az XÂû_+Å6²þu¶çuì¾…Œ«è»î²n_PñýœÔ[d„Ž¯Iò¿>~ÕSúª!…÷þšzcG|Bêzð5ÅB.âL0]ù©Tè]Còóª—‚ï-'
+ ÖÑ´äÙ_Èn\d¥ˆÐ¶ìo¯È KÉhí–üæAo!𧷛µaÌÏ ì%w0µnSÓùgÔ"œh|á4@«¨Ï‡G OøŠx´écJÖÇM¥öò¡ ;Ù‹s»1TF—ŽÞìx ¯×Æ™Xû×P¡ x?}ÀÏLˆŸ½jô{Ξ£ÆuÖA+ª‚æ
+<õ&>-j”J×Î÷ê+™lë&Ì|ÏøÌ=—'ÙC¹Æ •o€œaäOáh¬AwÍù=m
+ö½ªt¦CK!X­~!ÌW7ð ˼êG'fðqãá— =³C ¯ÀÚ¶Ì'6´5jR꜖'Å)Èå’¬-Os[vZTfÅ¢jׯ·•".–žÿ/ãe’ç’áø:ó°v/}‹z%ßÛ_$Iáù¯,<{aH¥¨˜d Œ7&î)@ ±XGš$ ?CE—€6AÒ” #i
+-0#Ъi˜.ÅÔÃ…™ÑAvUe{ozY#&
+HѬC.‘x
+Iµj:vØF€ð`8 ±<
+«b1|•¡"aJæNÛw¢ôÜ)Ó@[èUŸðIžâA¶’'=7"Óò =¢±vòÜä•+nÛ
+e¬f•³³–™†ƒšGšoNsl6Gæ··’î.r-?ÞTg%ÒVGVZvŽ>BÁ§¸MudKý4$ˆ5UR<•èƒÍðb »$¥7ÛŠpˆSÙܤùÅÅJ42>m«­ä)¹žcÅ`ñ£â=y4}bÐ1“F‰}„Z JŒ™éÓY ëP)Å(Þˆž¶ )iãŸ?n Ç|þ: œ×PM£¢ÎÌ ˆÇÆ(}½‘„ap÷ú¸CHvJ©Lñ~ƒÀí'UF^í0
+LL#œñ_ûÞé bÔP3œ<öQ„QÜôîý4ÙC%v¦¿ÇVxÓĈ`ͦ*­ž5k8|[‡V£•yéZ¢)c”xõò¢Ø£ý™o .Ù¦Ä[ÿdÏy fVÈ%o6#JRMJú^çydºè˜¨€)õõꯠ—^ χx…\ô ¢1 ñŸRíV0_Ã
+,o‚Þ“ú‚®cK‡”¤[Ñýj“ŽK÷êð2°Ê”aœglÂÞ}äºVÁP­
+º7›KŸ’hÍMd‡Ó2™‹ÖžW603êSÑÌ,çwQve¨g|ðqP7PRbZY’ÓÜ<Aís«ÊH3 oœ+Úúæ6hRbªµ¾Œ-ÂVç™-Ûqh,Úfp  wÀõ† [5Â0^ÀíѨZ!–“?K—µ€©Âf};‰ó¶ å–id –O æ,ˈÕnd Šá‡\ÂA4¼¤À½]i°zÅ´ÈH MOír+#v"Ñl ,:›Äe˜l£w&#«Ñà¤Ø
+
+HÜy¯VÞÞ'%¸ãà«b#X ÷eí‰âñ öõ@„ÐMúâ0ü6ôÕZœúã¨éyËá£òô€ oŽµU#ýU \¹ùªwáàUc”þ´ˆ Æʺ¢,^X¦ ë­ éI_3+]üq3Aô7tÀ(wêmü0Qã®Õä;AˆP'Ž•©;C ¥A–X_./ýÇœ|]…ƒópbNt£XýÄ;ˆ¤üâÐdÅAlÞäaÒ]–wo)^fàŠÆvúN”sa|s)ÇÀŒÇ‘/F5ÚF©GŽ‹¹tá•{^j¦í‡‘>¥¢¶È¾S•È¢˜<Ž×q ‹xÞ­Æmá`ŠÊ>-^Ѽ©Iš“+üÓ…×Sâ¬ñoòì¢,ÆdÑV+¹ ‚ $#Xš8ÛÄ»‘ —gT*š0×r7A/ë¬Ozwj®©¹oEo’)¤>‡eÖÙŠ&¢ìÑ ÐsoÔâ1»¿à¦E_±IË߉¡*ÃWó¬3P‹ª¬·>¿ TÿóÇ£wãסBšûºc¯Í¼¡góºˆM°”ƒLÇÆ_i¼¶üÒ‡¨ùmzÕ«è>¬Ù*MËfˆÝÄàÅÆ/sÎS‰çž@Px; ñF‰ƒ²Àôg @ª“1›÷x„w®Œ:".­Ð•4Ú^Ng€Ð->å[ó€ËgŒË®g¼ºÌ?fnÌ€†¦úªZÃàt˜ÜÃNë±­Ð(c9âÞuö§ºPga¿×aj'.¯
+B—–Rž‹x²%@¬ññ…Ú þ†Ö–`«L1a_Xü7Û@/ Ã$—êÔõŠœ§“.„¦Éâ…ïÛƒ’_ýû¯üñ/î+ˆ"l@”òè—LPˆëãŸ75CöŠ®Ø+-”B1úÎÜôý…ºÕ3+!ª’«øâðÁúà››òëétáåBÈ
+W’¹´Ö ƒ€ñwo„±zK§O#}=™D•ÿªÊ`óbݯ¨eèÛ¶ÞGü?`—v°º©ÊŠð ¸¶þµ8"+è“ïov‘IÂÜ|o,Ë°¦ÉH°9Ig¡m%ç*0‘Š€ÖË~s
+f ÏKÔ%8·¶ƒ6EÁ ^ÞõYq.bÀ<¶Csŧd} ïCŽ¾R/‘½/õ 2oµs LzÉÎ…5…¿l¦l*’ó¼Á È”©˜e3 Dj±ÍeyF¿G²³hKHYYWáÞ'°ô6F×Ç!‘)“H½f±X{@¯Ú¹é•³
+×éHçQÎÓ
+rÄNW8my ´M×¾R®uÉ”¾½¨a÷iy®ê
+•²°nÿ¥¹d^d…xÒCi|)q-'CÒN@Ïi¢aä¹o½û«’¢×7Ýú»‚"ocî{´;*)<Š’4;à öíÁö’V–t%œªiׯ+àã0×4à1èË6þ—<µ%áÁêû1ê¶B""{ž¢UfxlBq˜oÂm€KœI(šuœ·»ŽH׫´Ž_<çl0,ÈÞËÕˆ_šäMTö]ÉÃÞEE(Ï2Vûœ3ºÙˆäÚ ×à±r+~Sfƒ#¶jÜä@âY]Óó‹0òH‰¯\…é“Ø Î†\˜uWÔšœÅM§“E…²Ð5šJûºZ$Ž¶Yo¿ŠÉ±X9Œ+ÞRœØµsäÓ UÜæö WdÑ:à"JZ‰í%âV1U^ ·& 3ùU‰ü”8w¹‚\yÖGfd£°•àosŒÎK‰Ç þë“F¹ÒÖ
+ƒDXû~ó+-"D‡&Š¬É«ŸYRÂd3¿ÔÖ—/u Ì}?*qtfCÓ §UäI½„Y¢ìáŒôj&ƒþA¢³Q5ê¸MæA¹’-ªùèÖ
+Ǭ
+a"t›öoeÒ%-‹SÐP¼»•TÝ3Ù%˜Øƒ2H¶EÆ:W ĉË*×]Ç4ª ®ç*¾®“ûdo¾äùž 6á0JôŒÌôT¯9gÃPM¬p®aH¿ƒãñ÷ Ž•åàÿX0Jª²àýt­"­„Y$ZÃJè7”5dúÒ
+J¸jVîòDðÚˆÎY§;üAÞÀŽˆ)©6¹qJØÖ®×%–ÀÇ0 `‰Ã¹ªÛRJ¡Hÿ*ng)+fL \\M®[Þ§ÿ Y>Ai¼)ù@»êQ4>ëÀFÏÑk¤éÊ_dˆ!ü*$*EhGeÏUî@††À¨ly"õXÞ‹«ÐÚ[ÂÛ€%ÖÃe³bÖE­‚ÂwPb°€f8hÉ蟹 Œ@¿ÿš‡L=¨-†À¶ð†ö§ûàÏMát8»æ2.Ý‹”øp$c؇†W¶Ò_>%£
+꬞ÔD{}ðdPÛÎÊ<HauɆJ_OkÃÕ|ÛG´åÌŠ•Æ.0¿YC¶ÀÄAÈTfù bq„‚çUöä#æ.2nÁ1úkþþ‚.t‰OáYÄI>$­ ôŸ7E˜V¼&ÿÛÆô”à‹äö=îUa-Ê)Ïà&š y qÚBÜèÇã'¦üÒ–ÏöyìÄOÉTÉä+!¸»Dçe—Îà9PG7+‘V†“Æ-yxÔôY“…:D¤]ÅýéQÓ§|ZÕÀîõFß=*­ÞZa4›Ýš±ÇÑ©*oDGàQ…y½tÐÆxËh•EŠC¹-9:Î(r÷ôaã ¢Ç@N¹Š ¤P³Åµ eYø•õf}&¥9Ñ0ÌA‡÷R•eAK8(jH$ܼ8‚SŸñ‘èU5@ üôNÅÒ~^À§"îLPÓ¾Ùæˆ)Ážò‘YV!.yåÿ°¨ÚEÅ3½CÝF“ö­dP•àL¹f;
+lÌ “ü^¾€ø9&£Ø$ªÁs:G¼LÛ¨:á¸åDÚ.d¬„¢¬˜¼2Bè™*¶xÜãøöÇÚòcþ@£$g›c˜Ã¼F«—'¾Ûœ?vçC $À­œƒ¢ßaÑ#nìú©¦J“™õŠ‚K\ô¼#:ewZSÝ/“äJrˆž ï ¤3¹Î^Ö9z¥ºÿ¶Ÿˆj“>¿•V9"€Ã‡öã¢ߊގì}ܦ>™ìòî?«P†Ëd*™ŠJ.müVôù“^ÿȈnî­6‰èÔ R°ÈRädÇ=]‹ðÇ
+—BC¬Ãº‚ï[‰Ã¼Rùuõ[‘6U›`oÏcoci­½äTI­xcå)Ï|†FYã&E'ƒL еŸì› ¢òÛzã:é
+I(`!ëZ^õB7ÓŽ-Ek}u_8‹AáJ—x¼i–¯Ô7Õ+ ¶w…—¹2(?ßr@\îßtáa9b^²EUoJ¾ðù.5S°YP€à£Ø˜$O(©b~ é¤m¥CøVãk®^ *8–%|¨É! ˆŽäÝåiLÇäM¨S—Õ`±kÀÔ-%T_·Ž"í‹lâÀaSRP}²†È«ç9ƒ .xÂR…R%HHê³6™E>Î5fc÷0'4v`DQÔkC-":Oïì‹~¼¿ó.´Œ§¡ *ÁÚË;‘=“÷·,š¶¼ =â·€)µù`ÃœÒLžÉ7©DËGî$|
+9© ¦ìš—ÀQ4}KêÂï³dK
+´AŽäðŒÃãY¨uíz) Æ"
+?×ó¼]X’vÌ…SÐåËAÏÊæ ª‡Y Ê…‚}¦-˜uc‹·0M;]¤Ê-\«L&øDÚº”.ì)a†gA¥ðкQÎr¢…žð8I9|îl©Y^Äc°²äH 6†×`uRŠh&JØ|†3Újd£{ MC€ g”W½¶Bΰð‡·B› +ŠG÷͈½ä«ZsÕ„Há–%“Ðjœ³hKÏ+S†Ä BI±q'R‚3j\é$(nEU<«eýÍ9_¼ø½ä‹(þyóU_Š>¯ö™ïp Mœþ÷J5o¸‚láF¨ønýêiÝÆpzÎÕS’%‚¬QBRÒ‡VƒN=>X.™€$.Ñ(KÆ“›K[Èh<„S.%a–¾ëÇë9BŽÂ€¢=_#Žé¡|²2£ùñ5¦ èï™Êp„ë
+»–á‹x¶‹Íwi`×e°ˆù^ƒ
+» i¤©%¡‰>¨ƒSÌû)ø$ZNد‚žéK>Xg¹ù;)‘9©BN“•Fmí¤­€}—}SòÕÓÞk€#BÇpj÷î½¼E.\"ÁÍP»—oÂîÓEZ¤2¿ ÁÄÄK;53
+ ÉÒ  JpÁ¾ï[I‡ÉÁwLgøk”tØÁñ§G «CŠ`R~¿
+thùî¾3[HBÑyÜ÷p53*‰æ´šO6¥!qäc™x÷"™Ším¿LÕ’Ã/iðÄ †Åcû¢ˆkÀß_Âmhˆ•²Æ±QT jbm
+Ëp+1CLÊü¿ŽA@Å-1ý|y9ˆ8ë9Â2LÏÁŨ‹ÙA˜¡A¤8ãù®V8¼fDóªÑÁÖ[¦6gŇËÌsYˆôZ¢IÖ¦<´Vµœg´~­
+î'í<¬pPOÚîçˆüÀéQͨ.±T‰ApöVâ°‘á¢mØâ
+.n+Ø tøÂ!_‹S‡¢ùöÊWüõk3›Z1KXGš?~Zóí²ÏŸ¼è/=¼hýMöcʹÞ~)zy”ä}bH›ý]?­y}ø¿¿H/ÿùµÆoÈW¦‹á”øÂm%(uðök ù³`s›öñƒŠ‹Ã‹ÔóåbèAUG¼áĸ™ô §bB06»ì*cN†q`,ÈlÒ¯¸–_’ÞOÓDûG;DVf%Ί׀ahEFbë…?ü*,¢6•'õžvÎ ¨RÍÕ98¥‹” ²¢À1JÈ?*1 ™‘÷BfÜÄÐP(-b©¥r÷ejõë‡JØ[~Š!ãf „„º³ [ÞYÓ1™¯~ÑâÃaÿÅwöu=…$)ðs¦RP d Iar®ùI—„€V¸©Ó&úÊ«¹•?åÃX¬&%Mž·Ê1Ì’%øÄ—¥G«½¤Kp48%GÙË0Q&;WU¾K-¥IþŸ‰9 Sñ?pÖt'j@$ˆã*ÀÒdq»áO•däæšr\~­…~Q2ܳ£ò³"M’œÎfF‰’.K8ü°÷’¯Éæ^#žÅÖ‹8×"øYÐ;Œx
+vä<£bø²ÿð…%ÓinƒÍ'Î|°ÊÊ2úȈŽHÁ±³늾o%~ÓVüÃâW)êýl
+ѾÛzs&dƒ‹BûÈî}ò*¶{¬QÖØ]„¦Ó<4¥eM²¬|ÙA/ëMØ]Ò-dÿ”;MÐ>؆~£µ–W½ÐÍôÓK\ý¸qƒ"à‰ nÍòÂ}SÜ‚*sÆMåç[q¶ÀW"Þ&Ýeš°{Å:—ÔšˆŸiO[ÕíÒE•ÿVîÓ:Ì„M­ûR$|±…J;|ã?êÞLärº¬zj%ðÿÿ`
+H‰Œ—An];DWà=¼ ´!‘¢$Ž3Í.>Ð#gÿÓ>%éø¶Œ¶ƒŽ]O¤¨b±Ø^ÿ±:ß{Fó6|´f¯ÞF{/}ÖÑb¶É×EËéÝ=f¼†¿§ÍlÖgò×7$Ò²ò©Q†³V¯kÍ(­õ:õÁ¦@Nh1fëÙslP3H%PuJdãS¥äµn„ÛŒ^½ò½/HíIœ´Ù7Äjሙµø‰ÔÚ°éU´Ø ’î£{î+y¦×b£6/ëJã=»>×¹â\Ùø(µvÎVrC¦ÍÂ2ª½~©xÀ;ÉTnæ}c†E Gï¯aàÜŒXs´sN¯¥D­“_‚ Ïj¥Ìì}<ÙLëf}ÌB™ñ ¥ ÷SšÒ²*åè%öSFŸ½Åh£•qŠS½÷VKoÆãÑ!œTZéyΩ³ÌðÂ[× +³xmÀ
+¤L*¬éÞǸïºLuØVþ>Tår¤˜ü 2sÖÎ
+j—t úPêJYfB•¡rx€Õ§žĉÄ`gñ ‘1=G›ƒœO} ?âáV$¨#Ú´R‡ÁÍj >
+’"”I¨už’KrCw/+™sÙÎÙcC ¼Ïap8r‡j=ê€Ôæö°lFrM.’ëœÙg‰V)ͪÁXYÚ7¸WRÂ8¡¾‚ø> Å‹’ñÍ9Õ$Ò¿è‘hLnµÛîêï™âbéÝDt TtxS5¢ˆ'Š^b…ŠR§ÊãALÞȉ†æR\0VÛà+¡öýH×OnôØ¡wêü®¥Úûz£>o¦z\±9ά!Rö•¡jˆóP‹nA¢8œ'_ÍЋd—ªŽÃPÔCÑ@ßmGí”Rû9†FÔµÒm+ -†RrK6DŠö‘1%)iˆcã^WÈ
+¥ž¢s „¼ŸCîdËø·'›¬õA„ÖPàõÕ-bQ=
+¯̹%Ih>zJ0uXÌ !?t® /=?ƒ;3 ›ŸP(e«cò^Ãw(k°aÀR´ ¢±Å€\’Âh12¤Ð¶îL…è<.$°ï hœH«ù|}ÅÐ+L9K\åû’ d“°•VïQ÷åNâZ£¦µP‡s'&<ç­IÑ7Eb)P¶5a–
+ÎŒÌM- ®¾3õg¬l <WíRmCø
+¤èUþd.õ“‘)t´ÛˆI5™A¨œ×̓cf¸癚PÃPDº^7È^ 1¡ö{NyǘàJ©â  @é‚yeRDÙjˆfoáÿ<>a>¾Á`Aƒ“¹zB$\…Ž±ÝÂQ
+†±ö¿œ)í¯Èµ‹=·®Éô¦7c½3?‰Ë…¨Î©"‚ùÅ^´þÄ‘’ 3;¡Ì©g‡5ÑŸžâÖ²ñÛÓY®¹Â0üdC ª33¼]°FÓ#[ÛÖx2¦aå7µÃ…Kñšk îDâBþš´ËBtuÒâ¹µïSëò’7qF-GýúX ØÙ­˜»žÓyîÔ¬5
+ˆÆ‹æˆ«ø­6@&/ij^?iIˆëNÿ¼]1Xh†¢ñÞ˜à+BDÕ`eÇêwFëe…ÊüºÅaôb²L¤§9÷­¿bB-
+Éy™X‘Z—ï’ôÌA”³…˜ëÒÚˆR•´ö×5»<(=ÆËí[Ã:æ#Sù; $½Du9*vñ  ‡Ê;gwQñÇ6ª-sI¦VÑ£žSœfffbÆiœ²•’›vÖYšGT2:gy$6%š,Îzˆ€­rVK$´n¡l4b;[‹ž,‡ÄÆ1Rs­gU‚òUšÄ¨ß[Ý€o…÷ñlAèÙ¬ˆãdM eMõjíùõöß·QౚŠŒ˜÷±)
+©‰ƒ²Ž1_® c脘Ìø‚þ¿ßFÅDZ¿hÆpýü1èS¸Ÿäô[©_ÎúdÆIÝÞÑ22 _Æôû
+
+ƒy5XW¿õd"©¤Œ¶RÃ~ úîr¿äXNUR^}‡kÇí ÜAŸ¤ê»‡ù¬gÿ¿âz˜ŽEÖdäíi×>.Yý¹‚¾–3¤ÌØI—š?Å|-ý2‘ UVô›:]@_êt‰ö3 «€ê!g”òŠA·p˜
+ó—
+mAð2[ÑÑb&̵øl/HjAÄ i¨o<4\Ð|ñ¥WÙ>…Âì¾]#]R«ß#g.¡Eo¶³6Œ!îA[S Í@&c x©ijΦ] è\ \ér›†öŽcb“µå»@`ÂåÎ}|«#ŪÂZì
+‚Ýg›ê÷ySà_o7¿3ß™@4&^ìü ÕÂC2jÁ(a™²c‰é‡X#VªG¬‹ÔÂÜÞÂp4ƒØÐØv1ÉaÚ§= \&GÉÉ2aIqŒ' %ÆÚ÷UãfMÌb ·GâþY¡x€¡ðøËû9{`â"á¥]R¨l S½=¡à=}Æs¶J„Á,T\EžÞ¬'ë+;¡/ŽrqŠL­8þt¯ól E[¹`Jðì/D˙ÞXË‹d”Ò¥ îú‘ ‡|ƒ¼G] L6TG>Ÿ7jKûã­!  H9:™ðX2øx'Á^æ …Q,°ûú0‡å5ÒI¾:’¾;#æRŒ‘læôYfñq˜J¹[îšßèõôIù?~2°Ö\[Õ‡…éÎvuG® Ì)ÁDíÛx±Àž} ç5¶};9MD¶j{¥ãºúo|èƒÀ^2›Q,¯ µgCjèyù‹¡ zò%|uÄžÝGPg»]ü‚ôIYÃó!RMë ¬$VïÇÄjiòv–<Èõ4\¤§81‡¬iœuä%ßIé²MIÐùjð^ë˜ëD›ÅVËDvYf€BÝDÛÌ< ¦^•„P“ª2Ÿ.ÒQB^zõ”Ë›+Táuµô~ÊÆ
+öŠ
+tý9ÔjØL‹ÕÏΙ­‹ ëÞá°Q£)áU’F.îy^ê7½,ÑôQ±ôžºŽkZ7¤xå‘‘«?ÆQ{ð;q¼¶‹¹bpùƒCQbßïÝÄJÝ^¹º‚É.jhÎ(áfÈóÆ¢- ž¢ ŠË0æ»0Cç… Üð¨YñsâßeôV£¤dKØؾ¹â+ÂiB¨¡f‰ñ0ʺ¯X÷‰³Ä­ø?aHIßä× ™yÖ±Pj%> ‘ÞÛy-Å\QÚ¤Ç^kU¤dt½bñ׶¢­Ný™¯ž
+qÕ ª~í÷ èÃÄ´ÐN’RÑVôOß^û”6=Vÿ”X7Y÷Ø8›öš3;,…ŠÖ%¿
+ïSq4?±ýÃÊø¥d¬Eq—„¹ðtYg#4{t¤Ü÷ê2ÝBÞY¯6˜|ý¨n,™¯ÓÎesœ\¢¨¾¡Ë÷!ë,ÆÞÏöø¦ÏIõ¸øÝ/¤Ë[[MÜwc‡ˆ³¿ºW°ñY»V,a´ïZÂ/Eä—†¶8”¶€±’DÈ‹%ó­sµ ë’¯ü¿,‚íJÔ Wæ6È`!ò‹Ó=lEC´’†2H #|M©Ž9å·oPš£Ú²LHd˜Œé
+’.}€Wº’´„Ħ~<nÔIµIñ¿E¹¢5Óx±´¦u×ç ãÁfþ‰NÁOä9hüýH£-9]=1l]0igáˆÞ±ß—À8`þÃ5íäÆ6ŸAy˜”ñýgÈý«÷ÏB‘veßß |Dˆ!F]AOœÙVEч*ç…Úëý3Ïür›[§` Bøˆï…mø?;#Mà/½¹É¶%~Ú }Ñ_̧Á8ô¹g9%z×áŒ2³ Û4ˆ‹®7‰ü˜°y2*HVy ¼¨Â0+*½ê^
+ ‘ø­ŽxÙÚ> ý*àŒ¶ñ « ´’¬;©8ºÇ¶°k¿çIWG új婢*|FØÊ?G‘}æ¼Âu}6ÅÛ,W¸¤åMV…¾
+š$u0ÌÂ †
+„‚—sÉ5F&K„  VU Ñ6Òo{nbàYTP?›*Y­-³ ñ³tšg¼ ÐÇr(dëñk¬šŸç43?Š€‚9¡S›ƒÄÅnbÿ `cÕ/»+kÆÏ€,¾ê`Ö¿uàÉŸ Ú/2`lRŠ9ð bG»tÔ´sàoÙZ²?x®fƒ_Ú¯ú&¤HN-½fæ¸êÿ(‹¿%@2…¨À „‚ [Z„AüEO, KPî8 `zªèµ°Söb…rûúGAO×}ÿÉ›ìé—³žøÈ´S*Ú-´$ž#_‚þ¾=-A ¬f¤Ž‚ê]ôzÝ%¿?
+z•4KÛä
+Kí¨ l¸%ðÐ9  œ.úPdÖ¾nÍÈø}z·–]äãæô«>$¥Úâ*†l¥H¦Î†5NÔÑ«P$š Èà…p¸¥ÈR#„®¯…È~Ü’TQ̈÷PÒ) ‘cDù¤ôâœa%3F”Î
+ØÉTÄ1ð ï™ÈŸðSPjÌU]]ë'ú-}V4v´
+¹Çm’Hc½:ÍWÑð<aò›â½ ßSˆMêA
+„Á(¿ÍÉã94¶˜t]?üŒÜ´(‹Ø5ƒË`#+!Tìêdñ‹è”‰„D:‡Z€˜¾»%^¶[ƈ —BÚùŽT°id‰JŠ«^ÐfX¿áø NÞ ë‰æoÐ7Z8f“
+²E'­ã†"¤±¯xR6Ñ0ÁÊZ´7 †ŸR×)ÿds* (»B¤¸ÉšXµ8«ëifé+RÎq~Ž¢u NEXNZ/á„Ð’sKî1Ö<KÓTAyõ & ùîYÖ…ï
+ø?…FÀ¢v™ ή§0¬G°DÉ´q•Á½q•HÐcÒfäX—R"BÂr Wh};˜»ii¶ȆAU8‘áÔ
+MÁ‰¢ÀO®Æ ‹ èꢰ\B\+=‘Çë1f#àva@d‘‡Ošÿa@¨0ÿD
+Árî IkÐBR÷¨)(‰¤ ²n¼@/PRÐÿ8èæ™C®èr…ìy
+zþ„Àñ G·fõ«¨ ò¡Jì¶Ôý6Ô®3e>Z÷‰èñ>¦2•w!OŠöö@s(‡B–··Hƒ'ÉkàÚÉî%'Ä>–€Çr–M4PÚ›bê™{ê³›‘óB±(AnLašXt‹n»=F6§Jtºç!€'Ƈ_€‹1*Õ’ºbN=(ƒ¬p<6¦›!`ØéïMàI²BBÈî¢hÁYh$²O_ý%¡Sd‰àpùÓšÔöÄ5ŒégZ2‘"Û-»!üĘ0*>è %JUÇ6_˧ghdQÌ?ìvŽ(š¢#EÉ5súР룆€Ñª*ì8§L!S6ö% ˆÇAÿE
+ß(º(¯
+êä![иô¦±¯ˆôZ@)AªqÒ ½’1ÃJH£˜ð‹®n¥ƒ#ÅÍõæ&n M¿‡Ô?(‹d†–LB ,Áƒ=bƸ%c¦~€»€¶%† êr$så2F´1fY*¼üºÌSÐ÷» !|•uéwâ=Ô‹~!˜]1ÜBäEÐ7¿Y\h/t³%ŠÌMð·!Ÿí‰C% #‚jkG½ãMæX
+˜LÛ9ÝÌ'kîŽ
+‘°»7‚ … R;Bê,¶†ev¿ª˜(ÏB¡a.SKn[W'­˜9l/:Cîb­å£žcnçuÇö¢
+ÄàXA&ÅU©)s:ãáª)ÉñŒ"“ÐÔùD€²äÒŽ¦ÃÝhÌÒ]zùúøïGþüë£fl›&¥Ñjè–RÚôùÏ=FðÝ•Â×d«ÅXÂ(º" Uyˆ¬4 é›<`‚Þ½‚È%¦]Zî bc¦üQ©3bÒ¬JFÛ¢cØ+& cà⹚ƒBé‚è ªèö!BÚÂÐ5—n‰/U]„ÐÌNhH!ùÀ"ÖìÅÏ‚<N–8gÈÇ Ì ¦­YéD<h=!‚ŠZÄ•1$áJF¨¡é—¨Jʇ”z‰*•Ì~dETIQ:’{Ýñ\¼ì€÷×Ç-æÕZðø¿´p´#z9TL4Õ?¨4-Å©s§0J›¶Ù«0bw[ôµzo‘â›%z P<¦bÖ8§!©ÊBŽé6WUVs
+mÄUŽs_Üfˆ²Ù®aÔJ¯:œz®RE6~ƇuËMŸ?¼šb-jRQkß!C¿SqŒ0µ!Ôk3A´rÝÔ3j¼ÆíéÐn_ÔÛ¡Ýl¼=
+ÝÅ,¹ ü“®!UU Úu_õÄ]€º$ºŸ,Fs3?,)$Ã
+  ÓÈ'q}CÇ™Œgg°ë7”
+=­Ò'N£‚tGwB4¤„ ç;„9¨ô¥W®8ȆÀùXF\UAöÅŽ20ÇE„!åÙƒnz‹GmiÎ&ŒŽFéI\kC!ÞÏ€xTC}õ®&ê9”=Ȭ>â,6Ç9ÒÂÅd‚d›Ö`K1)œÜ–Ë_lcš‰2³LõÝ ÑȇAT]´þi>þb
+ª1@ÞJ ®Ríymǵ˜ð¡†ÙW)¯Ë:_߃
+'vž
+2„.èÿúêw
+—²$¦·£žtÅ_ úqݳ×}y¶^—­‚¥‘fùlÍKòJ*´,Â+·Ä})¢â¬ƒ@=©­h*ŠK q3¶T„£`zµ}¨B.˜ 1„˜ÜºU˜:*ÕŽö­ê‡Í2JN„
+E”C±ÕþáœÑ(zr–Ó÷k.Ñ4­ „76åû* P©x8WQQ攪å ‚ [”ºý. ¬î¤²+ºµ »Âñã빡豊 uCF, ®5åM§3üî¥%êì
+ioƒ ó/?XMÆG Ø´mÁø  #”›é ¾lÃõ¹ªSðM/PjP,ܬ„R¶:ìŒNÏHÿ‘ïˆ
+Õ,çe7g¢ñ誑!ÛXÎE"?“³ ÙP1ì&—B£¿ž,nGÖPJã6CP¾i¡A•ÇHÔ?~ýû×oÿü•¾þþ¯_¿ýï©c2fˆæ:æC SoÊkØدÿ~rS“å,LÑ™ …Ú“yBP,rjáP :Lø¥Ý#$g3Iz’JWFð`ž®Ô­SÅ`X÷9‹Q½Hÿ„x.ôװ6ì;Œyö5Ä«°€®pmÄy:®Lû¹J„òøB˜ uD>Êè)+&OÁŸ6+¿W‡<€¼ýcŸB~ô*®z *¿óø¦k0ò×Ók
+œStY¦<fEˆ¢¦G§¶¸êµ:…µûY¹V”a5.¸ª¡úÀäÖöØ«—3¯=8çurÞ^ó>Y½Oò[uÞ÷á­ÆºWn"mE4¥7‚cDKeSj:ðRu¯ßSPe¹–?›× ŠˆV—ƒÇÖt")*W…]TʲPAJøª^'|À)ê{'?€~@³¾µDï†4+ç*Ápeq™'€*'Rfž”#@;Pü¢Àéxš«¤mz;à¥@rIÒæ÷Í3Ü16Lá†%ý'€‰‚7Ûu];¢©hÔ¶¹DÚ±‹ZaMÛ}Þ
+Ä1PK²Cº…ÀEÄ~ƒ¦ Žq¸NVu¬ skÎ@ Nw¢~'7³j‡„ˆ<QuñàÓpÀ& 8à݈$ÀÚzÒ–{5,P³œ ¬9õ»Q‹WÐȪé e—p–®Ãt²ARušÄŽ>!sj;›)‰ZƒÚRðÑëœý±vºZF’G—mÆE™çÙ©,\m£,
+=FäÃádšYê¸é=–c}g‹6‹Q yVNñ\öPpL['mŒ&–o­ß,?`4·úáÜ«%!ûq«Žª@t¨òק7È#@óZ¬«Š_Õi”‡‰¢`oF~<[òiñš óº«ÇÖUúÜ•IŠ™#Lß–&ìóÓ¼
+ëY¤%þ„»"múu:ï†<s]{º[¿GÞ¤™^úZ;þ#H™è uv>²yïŽC6€c=~@> B@mý8Η’µx•õÑQ5‡»se‹ÓHIF¿ìæ"c#ÄÜñƒó$È¿
+éÆóú8¾B •X<Îûuö˜ ¼I‹Õ²›’'¯*KýËÙ%ÜgÑÆ]¬5. Ú7`BNŸ7E¨Ü„Sp’Ihê%R €¸?‰ýЦ™Dv’+j‘-ŸÒ$Máj¼V Ð31¼Å{MêœÇaçЉ")Áì™á>@nÕ?VÂâ
+)f¡º/”KÅ0÷{™§´+R<(É>uÁ…ÔÊJü]¶½¸€K"K•"ÀíT¢\@‡³SvKÿ-’?
+Aír3 Å5[Á«H|3¤F ¨ð\ö
+v©·èÃ9I>(HœÆ¾ÌPüµÏ²¨P© eâ¯s\%'Éä³Á†2ñS«¨y*âe kMù `Ã)ÜÚ›ƒ’ó€6£!ü+ô³’ %0ô _†t‘*Ÿ˜«{5˜qÊœ%Ùã‡==„
+Û-âu“í¥Ó´5E°èç «š”P-*Šž´Bd³[¬Ô¹=B!K@<% âá™VC,ÂsbÎ0´×š‡Õðθi-]kÔd<œâfdøÓ%™¬Àï ‰ %¸hžŽA€õ}ªÊØ8kÎ`WgârjÄÑ®<ÑPHô_%°‰ÓÀºJØ,6‡GP<–
+xcPÌš½/71 ,B‡¢ÇMÜ.Ò5]X`Ç`àÖ1tÅç0!nÀå!¬Dd‰µâqæÌÕxnÈ]a¾üéØÿ$1½9,ÖÏã²$J°­,QKjg
+D‚OÕrÊk²(ÀJe8;Œ½æ>á8âX«âii7çÈêÉ5ѱ}eo!7ð3üVŠØÒàc6ÐÙú‚§fÀ ‚×—ñuCÞà-òØ9ÒÒÂÊý:¶ÐFb³¥UÌ‚Œ¯_X&JýÂ*“)ÍP_ŠÌÅø8,C}*bCdE›–Ô>†˜“²0ôi«ìĸHöštÑJèí“%q#œÐz¬Lè½E¿Ï¡èw}}üD„eQ™wbmJÆ^­ýëØ6„‰}‹+’Àùðoû°Í‘j¥È6ÿ¢èi1>n>÷}Ñ«}Üa1È’™s†¶Œt÷[ñ’ÝE²ja`祣”Èe°Ëœîî¼eå& Í¥ úíM)CÖ,—í x8;'ž,v Š®®²Âýä TÂ.Éî­JÖ—Ä?xB@§"•£äD€Ù%¸¤É»á¥öë0‰×<ñ~× iÜGóá¤mW²Ä *ÖYïÒÏl±/¾ˆþ)rÐ@ðrç¯Ú["Ê4ÙÜýÍ9"ÞIøòW4cp,úNI}ÃÄœñ3žû«ŠFÃó…±OUi+k€ÿØßàiH+ÉŽéÂLƒÌà—· tö[0¸)¹Ä¤KÑ îõ 
+H2À‹ñ/Š.+xúÜŠ^há¸]¯E¢˜h‘€…M`¸£‡šà¥Î\’¾c4s*"÷M–À%õ\qŒ_,lØÑTî9k©ÉVBo£r]ÀöûÎ [ ±ÉµýgŸšÄ¯Á"¥¹‰MpL[(ÁÓ"O“÷
+¡dMñ¬[øàÜ\Ìø}­Ðï%ú5$&øãy>F¡†FöØ›3õs†S©Œ×°DK{O0ºÉ&IÏÁ/â¿Ü*ߡ̇˜œ§<:„CêöªÛuÀ//Ú'>£u¥¥xzøkÉzøsÿ®Ç\† LOØ)Àgå4LÙ1œr•«@ÿ|˜/ ˆË8AB¹¬c^EÍM@[æKNàƒRº^È:¥ý¥?ÃXˆB|&¹ ûX,ÂSóL$K‡ÎÅ?O5
+C¯Å;bhíZA®ßÏc¶?~ò0”`?QbT·K؆^3tPýæü¡£H¼·þÉ)Z¯1¿Þ6w<[7@‡µ ¸7\ÌÉÝ­±“vèI࿦»sØ1IK_®UC¯7i?À© ß ‰#Ã@eèœ+i€ \}[ûIÑÕ,©ˆe4¢!»ý¶(Êбۊo®Ò«Ž‡xX˜(AÆë$怆ÑàaâBI½Ðq®´£Ò]ÆqRÌ ÕBÑéþôxÐÐêDú Ëúmäc%-$¬1ÙÈF‘¿J¦šC¼½†`Á—Ì/'E}Ý7sÚ‰ÈNðÐ jùX†q;þWè¢y­owˬmƼ _@úB÷¹Š \@Vå#V„Êá@Ë°²ù »ÂžR‚¶`Ôñ{eaŸÉ{UäËîö™ÏëCø‚I}mÌã9JbŒ°Š÷*?톕ðƒ–dPmêâ;ÅŒÜYöQµ>×¢ž³"'¿ Àƛ͸ésý?åe’]WnÑÔ´ôHŒ9Õ.jJíê‰mþw$×ÀEƒÀC6ÑÀZ“ÁÛE¼ª&FÒ79/.Ú†ôã³yk–ñjJc‚@ÛqŒE‰€,Ûàe§†°ÿ$)O‡q ^Õë‡^|W':jMCÁª&» Ø¿>pAQiÇÃ1½%RØà!í7¢#n\Ÿ´! ’¸†øe‚òΫKÜD¥(̺Vhó-K‹2RcA2ü…òÁã)TN#ËÍEBëV­A} —/²š0Üz”E>ðÁxÝzŽùSÁCÏu8ìÏ ×üü›=ý²‘µa•äÈI%,É%úüvP6ˆ©®d_¦û{>Bùû„‡ÙÔù~Á6ˆ~æÔÆq÷´„ŠêMiDº!_‰‚Z8À^p!¸"äôsÞB;ñ3¥’|E¯þk"\Ã,`ÒîÖî‚QJƒ”à[/uS°…hç¼c>we²Ñ¦„¤óŽÁ"<‰E]é‹~ù‰Úpla;áBœš¹!¿gKZ²µºRÉ{=|“«LZÁƒkMqS‚ ø¸EÂÚƲ#î‹ñÁTRfco
+.w 9§±Ï¡€NÏå(SSÔDƒáìî(ef)Åx7áû&Ìtæ‘|êIÔϤœû&Üëp~Ê£=ÌÞføãËCÒwåÔ¶ðë¾Ð¯;¶Ç€O0
+¹Íc&† ùR‡=dX~ÞÒ±Yáø`ÏÚõ^KÕ#b`Çú¬´¾Šqïø‚Rô§îu:…èˆî–Ÿƒ¥¡7ôˆ÷Ýü5’í¯¸Õín[ÔŽÉkÈ
+mÇè¦D¼a¿Ž2Qœfyín$ƒú`—ºEYïnEýáûê¾
+ïg £ÏjEÊoŽMÉÒðtškù8æÍ4Þ
+*Wõ'WéÜ7½bY ‘Ó̹Y¥3
+™O±ØP:Çî™RÚ^QÖˆ’“àF M
+­ð¸³ì¸-
+'ðY<]ºèâ½gøs$™52¾Èü­ã/ŸíÔtb¥ ®(1¤N¢Þ»TJïèš5ã¦ÉÔ0o,EŽFà
+‚yœFG%·!IGçðÒñ=ÐÿJìçßè°N=rÑטBI\¿ØË©˜ñûôÍÓÂË”I 9aŒ›4›Äÿ:¢Ya (PîáÁLkÁ,XL]UõÕtDjÐñŠ†Œâ>Ë>GËÄÃ4¹…"IÏ•ka'AD¹Ó¾ì—«Q­ävÏg
+ãHyôåa,;xkgu¶¥ü‘Y™Gµ¸ÙyÑ x–<•î*ýTva×;öb_4©„\Œ¬Íˆ‹ 06–bÊÁ‚ÀÞ6½zõ´#½ iâÐG‡PMcûñûG²ä^©EÒ훦ØÔ±VL3î[•í¤‚@ÏuU‰j@HŒøA8¢É\!üŒ+à&œÌ¹ê ô}·ôÉÒáÙ‹&džJóØó5뮡waǶd@£– ¹¡9¢QDÆ­mÖÛŒÁ#@e‚3â·p#Q4éÆæÆK1{®#ìÎœ÷s(ggí0¯uûv}ŠJ7dgdñÔq8½¸¸x¼Ê\™œ‹“SBg‡lU?ï–“ÇRÑrO5ÌÌE7]+Wi¼Ad¨³Ò‹.²P{Ñà¢pÙJ»€>@RsÆ«):B2„l°Bñ5&A²l’+wGEOùHlÈH qk•S⤽åO¶¹ÕªV¦Aôfs—8KÓlhÖnåÞÍWãÿ‰%e1yÙÕ Ipð,²r³TN|m™…–Ï»5“,'‚ ä†û¾Z<IÓ€,¥»X*jeQƺBàWèã(êÛW½ƒ<6pkŠ û­²ƒ<E«°Ô¸ ÆšérHsá$Ï°wÁÍ·º~P„ÐÑÒªDê ø³':ÊïsÄ7ÈÄ‘Ž'g2–• õn24ûPÓã5[ˆi1bâ‰zð3X”Gr­b“>Z8[(¾ k·SDVÍ·š³wCbaAúp µ`ÖÉ”{ñØVòÙ ¥àôá|ƒeœ€,08¤º¶J#eâî6yú"C†bP­ç¦7B—uÎÚ!d¬´”jÜâ« …ÅJƒBÄ“’, C¹2Ê7˜ð1ļ%Â)–Nqƒ í[_eõ5šj® éb3eîW3!®U¦óßíéò’Ãé嘂‹0°o[`Ü%`kŸa'úÏ>—`}&3â4om·€’ß“³Í>èËA
+[ôªµÍûIéCJ~EíSä9+nrôE:/ïÊPòØÂùn2iø+BsÖä›YýpO;4“6•·>LȈ°ó‘Ã=í;Hv+áwü5þX ”¨*¨å½¿}‹{¿®û3Hïc÷ùö”€ú$Þ@/cD®•/c†¨fÁÑ"¢ÑàÕæC׆ZK€(ylƒá³ÈÄÐ{ÙE?‡:ð|ê:í ZCEkC~æþ>mÉ¢ëýÙW¡E0(I#÷p¾ËTjøk›¨ˆ•°Þ[!,õCLÌ÷ýôŸô=‹™‡œ•Y‚é$è–äöeÛ>Efv%C
+(=Äòbaû©“Õ YTèäáΠ“ÿ&>åM'KíøÍO…b)­‰C “Ué¨óVŠþsÊ Ðgä$ú‰ׄU0—3ÜÒ)”Žè<ªcq°‡oSúñÏt›÷Ž‡„]jŠGÿèmo×ýè…®ëõ
+úåÓä-2ÄMqJÔ4›²Š¿@l!ÖƒŠÒì‚0!~_}€ ò›=6„oD‘B!6'åñ]båý*÷P5†!Îr™Uœtò<Ö¨_†}´bñ2f‡×v|C2±%³ 2DȾ*ñ÷xfåÎ|=§²b?™Œ•©×¯©?y€Iü˜Z{x ÜRcˆÙ„y­NE²LfI4»ÖøùÖ«}Õ;ã(¥ÝœVo_ƒ€)‰Â¾Ö¯¯‚m¹)ŸÚFÜôZ˜m*5V¹fûq+1æÃ"Xs¾¶ê²[õÚòË9¯ƒsùš×|ÔmŽS…TÔó<÷¿oC/òÿ“ñ
+é*DÅW‘ÎPt†Z1àÓAˆ"‘‡ÙÏIµCìL~aú~p „âó¹ƒã[×r¾#Æ–løAÔYb
+
+‘çdóv ŠS3]mCH„Q¨‡ÀMú~^äC)wÁßÆ=6=8¨÷SÐ6"+8XÊ×oA­ w~OY’=< }]XíÌßõœd%Û*ñ‹&(Gà=PÞ¹!ˆ‰ÿãer^×­áœ#¸æa-g¢-•ÿöýÕ
+Å&arëEYÓ„_,ûœŽ¡ŒÐšVb×XÁŠ$œÌb‹h†Û½
+Òµ¢
+;ˆ/Mÿô0Ó:Ôï¦1´ïPíM›$±,³ ¯¯G£,™ÎâÍ6å 8T ;Ì5"€ð鸔‚á4°TÉê[3Õe$b®cAl§+¨.>vHò ²fÆ~€Ó÷U)ÔÆRc» å#ªqË5Á,|ÏZóÇ~NR™­ñḏܰ<le*F1’¢·
+ÒíÛ%î3'‘tÝ›pï\ÕÄ(qÙý©ÿ^LË@¡áàÑL<{øe Î¹,l­a¹žwBÙ43¡‰òQ uÏ3(®f·IÏ”•Þ”uëNæÀX±^9ªea=´vN‚åF؈$îeFT
+g¯5[Ã6ð°qâe€ieV»·E1¿vÿp¶NYÇë½úYÍ·ý}­ÕT)2ñHÊ–¡Ýx²Û×ɘ¥æì˜*‡“ÔñPujø»ÆIY—WÃ0ÌN |£ëîŠí]gé® ôÚy™ÉöÒkxjƒ<œc¶Ýiá˜
+HåáÜÇf
+Îô‰áÇJ ’<Ëî5J¯rKu»hå< ÕÉkÔåDt˜ ©hÓ`Ç5NM
+äq¯æ;—°¿Ê›²Î½œ7HYeiR\Ã/ÊBCÒs ##­fÒ«ÔÖËYz\CfÈú1¸°¤R¬LdŒA>¢Ö±9UV
+”3…MÍÆd{v•g`²cÿ„Ñm“_åÏì8 Ó<CHáÁ¨#O”¹2Ïsïpâk’€pDYÿvl“ ÜžË!CÚKÏ~ d×Ñ+e^n'Hűâxúß^/ÁT(ÄÆ aŒÈuùÎ`ž¿FÃjc/nÌ=§zx„Š³3À·@úZJÜ™bÕfôæ§Aô»×½ƒ ;Â…»R^5„Ž²ŽTë1{_ ?m.¢ÒÎȪìtÏat tY)ù%'Ø‚Á•Û¾MRÓÉŒ-†l5¼@Þ²¨.Ä% _~ÐM@ö99 H ¹: Ïï쎭ØÇ¿@‹~»q<Î<ŠØà[„*‹»=€Ðt¸ ç„kü/J±™¦¼ .vŸX­8l=
+ò"ì!Í,Òyù:tÕW^­Ñ$¹¥²K.bb¥Q7²¬ ˜äŠijMï½Õ.N¼s%|‚É!z‚¨O¡á!-bÓ=yŽ¬ f¤Œ­½8Q¦c9K\%0^÷F‡µ!Œ‚NM›¤ª¿0¬gFFsƒÐ09Ú³&ͧ\¿R·ÍDÐL|F˜jòPª@‚Ÿ¹^ŽpCœ0ûÿíó`ˆ5C0±Ãe¦LyÊÃs‘Vdm/Ý‘7w]¤5‹‘&lmrϹZ~ßæïUïxçã»ÄÞ¤ÿÙ©ŸßYmW°íÊPyEúpp#Ïû¸_FÞÁÇ<M‘ù‡ ]|?l êñAXïÀwG\o£Æe³F›Ëq„d Ù´=§D1Ș¤ÓvÂnÆD]½¶¶ÂxYÐoû››° Ó4C°ìÂÓ9ŒW¨dP,G:·)ü·Þ5³íxá‰ù˜-å
+.¢½1Ë©”ûгeÅɹçÐY¤S éôë6$sH}VoA'.âL
+ôªª5ºæ¸:”N§âü±)ƒ{+c¦0W)×= MN‘e‡QØ!Ð/<šWo쪚¦“ ^±?õõl. 7°1´íå
+"!ƒµ–èíºQAcýÁ†aVë0.ÄÄÓÊÉìA!3 eå¾)DCHK¼ !} Iÿ×l!…êqNiLú
+i!Ô
+êòLH´S‚²ÃƒPæ¹i*ƒÊÐÁ Œ72¤ÙÏ}&¬#ÛmÃÚy
+ÙN¥pA\èŽ`0³F/„l!šƒd-Ú¾+3‡H>Z¨¿ùA‚§¹–T´"ð±&Úf^{Ý×a}I|¡ä¸ªÀ»bž„¼åì)Dû m+⤂Œ#H\-ÚŒ0û€¥v)qÔÉ®"`øƒà>&ð  
+šÅŽIÌPo!1rÛx@~È+ Î\[blôÚÕZ²qYúšëR8š‚m
+zfœ‹©
+‰j²Ó:Þ!Õ ¹Cªt‡T]¸ëë²£1y”ª‡ƒƒ|¡ŸÃ§T¥O6 ñõ±ôëN©ªÚ”á#‡Å£KG*OJUO$‰‡Ä]ÚUgñ_ì쨇x”ª%çøÞQvÈI©êtú•lñ¨æAªÆ…(Ÿ´Fñ Õº;¤~;š¶$¨3ô9)/c‹
+“q€ϵ Ï sqtüÝÀÜý8dh<pf ñ>õ}Po×PjçóEõBg3ç én~§¼Ð!ÂìjÔ\XÈ’‡¾BÐmý*Â_{aK†¢J˜Vªñ'Qˆ,Ðâ{ÕònÌJ—QxSŽUÔœ ”]xdºœf’4hØ:Ï YŸR2¸Y
+ðâ°„(#q_i“‡ç(*¾BŒÈÀ2’Kî!Ép·K\³ö¹îb&
+×lIŠF@ãêúÓU€K2‘ÜÁ"w9áýŸÏýÅ€Ê<°ÄHßhÙµ‘„Ô¥š)°x÷ÑCS½Ê]
+–ü0÷mÄÃ*0¼P÷§˜EÊ\?ÆO0ˆˆ£ë&;‚q”‚¯Ñ;ȔȆÚÌü"ä©Cÿr‚¼^¶scn§ÅýEÐ1}Þç~tÓw´îA
+¼)„™XãÎg‰1k Y†aH5¤ýÚ;•…K3¢É6>ÛÒHî¼Dᙿ 1~cþã¼f™_`f«þµF”x’KrBô¥É¼ÈÄe6ÂcȞσL4˜˜Å©ºLÐFnxb­HŸO¬FôÛè Jú6†éÃþ~$ÏÙ°žl§m«èrúZÏ´ò+›$‹qÝ8×¢}Sƒã½ü²rüœ@ï˜[ÔÓ °¼
+]P¼j
+‰Ùüfq[¸ªùÜ"bVeBƒrY§ÜK@œ# N¢4·÷øb—Ì6¹¢Ÿtñƒˆ»<üä¾úç â3FÀ6䩘<!§Bè~Ãú4«5¦ŒÄh"²$–_nÓà ?À×õºyœ¤¾ Uwªå2Ë¢Æ6Uw£‡²]¦†ù•Ã“G»«ãI,&'v‰‡ÍÖ…´á¤}@áy݆2X\Ñky½
+´œ ¼lÔ‹–o(ðÈH[+,Q ,£ øi›ÖÆÜ×ÜûÙà ö^áK´ Ô‘üýi÷‹L}‡‡´J¢­â. u7æY gcª¨†·9Í[«Ö¸<8œ[ox¦-ø2‹%ÊX¨Ã¡ËLl¡CKûà ÞB:µËaˆÒÊïA~öÍ ˆcDBQ –Ý oÀ?…$Îý¥¯Gë¿­ŠãÞøÒ—­vÝT ÒãE6… 8Ì,IBI‡DíÞÉObèZö9ƒ—‚7Ì¢$yKŒœ#7 ¥…¦ ìì0ÓV3nOH—’®sÔÌpä>·Ï« *Á1õi…€8Qˆ—jhK¦F‰`5› ¬ŒªÎÄi©;A^!ü/KAð.Ÿ+(Ó¥¦y_‘,™è¾)•§Y²&“HÓ*ˆ=m
+©l Ö[€VKñtìÃ*Af@š@³î$—1€K¥:Ú)쳌ˆ csKõmÉ?$ZFýºzР¬dìKÊò 5î
+H‰”—Ar9DOÐwÐæ$±ö,u GÌJ¾ÿvHVǸŠ¬îÞXvªH‰ÌDýø—š¼j‹^£¨•Ò>>ÿ*½¾|ˆ›ÕR¢.Œû(@iÝ?¾Î˜ ñ24ÚGš÷Ök×àˉ(/“ZúèU«.È(Þù¯jcAj ®R‡ŒÖ?~΃bæM$¦¤Ú¨­òiÑ.ݹJ« Ò{·V‚ä3 qiZêhuÄ8@ò$u)#8Ç”Jœ¿ÃíÅEjõ~}§rõ¨î¤½D¥Gójüî¾p¯£˜PCžGµWiÃ¥öè¶åAµx®Pèù ^ªnåªÍ¾l¼²-çŽ)´ßmªbÞ£¶¦?æI½‹÷^iSì6€æ­Ífò¥Q=ʨœ¯ ÒW±ÞšD+Ãc=Ëkã—ÿÙ‚¥áÑ!]m§rV“>x› ?þú‡•—´>¤šÅ¬²ŽWãß¡Žyÿø51¦B©£jeŒòP=ùzªZùªŽ±»D¥™Ö6F›TZ¡4BùÉ;$[¡¯®#óÒÄŽß±—P85þ¾2Õ jo¶ §Ý¡% ‰W£›fÉöá³Ò¦=¶áuŸ¾j*õï‚ò8z.J¤×ÙÕˆÕÞ¤xÞ)!F‰ù瀠º ¥@¸0"&DGðµûz|Ð_g֨Ѯ! £íÁ´/ÌàÔÂàÑxÓ hªŒy0@ev”ö:ŸfNçpÝ!?'3šsÆ'F/o¾ƒ ` Ïn× °>ï´ °Ûè®Àž}T¸4Ex¶î£¸+ôÂhìâHk%GBèíüN©‰hÞªÛ‚(¡†jhæÇî1™ }(:•øq‰SËÑiEA}çÛ mÐZ Ü¡÷çi
+&¸$,+1dk]u ÈZægè ^°ÏزÊ©¨L@
+KB*óhí†H1h éivv Pë‚rN×èkxO7„ÞµožäU-Ð%Žuñ#Ú¯ç‡GÞ/eLÝ®Ä3ѬÂ8!Å?çI(STg¬ƒà?0ré/³~óIÝÛ8É÷¡™7HÔ_äÍ\øü™NÉQÒÞy]^2¼YÚµ·2! eb\µnF ?3 ¬ó¤öBK¨#\çôeÇQŒ!“Ó$
+‡-ØX¨GòÇÔ‚„\ÅQ`˜Í<ø ¦> v¶òaŒQW¼n࿺ns»pj
+žŠìhªîéáw#Y%¾pzbFÇ-çóÙ…‡e=›y°¾»™êЫÚßX0aË×ø2"É͸Ÿ¯yQR^ÃHlìË@VkÉNç•‹Ÿ<É©Ÿ¦pŸ â9,SJùfň€‡¡»»ÞšÌÛø¬q>Ki*±uÛ¸œ²GQfé|ÉÖĤ5’½"ìt‘‡&=q–¿‚JHì¶+ƒÕP¬QëüŠ§}1¹‘¦¼
+õ _Ñë›nþYG ¤ÂîÖãwà ñ²:2O„½Ú`h(ç>’æd$kå—¶éQˆ*-Ýe V&mÔ«W|VͨŽõ-¸&¡“íb—Ï=)2O÷“R
+«y‰1-–]+Ykä"r
+‘.å: s'§è·åŽ¤¶ŠØeŽ‹ÌÄŠ”©r„lŽQ åíøD›Î’œ*WØÌ®,c™cÇ„8¬á•Eìb:%§.D6ʾ‚1iç8x\$¥6>Òó£ÎïDÔIýíêfšc¤%''·‚Û žíñ…®Y zÄV‹h"áBwßBð±:(ÓrJ×aŒ<ܦqÛ®²àé{-1WPÁ-ˆ(ˆÞØö€Õç<§Eíe÷‰ù_{ýúŽf<m%Æäz›¦® b” @Ó©:J(Î%ÚTJžUS¨½è?
+F½I&Ð*‚¹:ÚÕv„)ÝŸû!vÜ‚\Éý÷pENÈ´¹bó×:)…‡@ôŠåï0D!ƒHA¬JôœÿÉÁGî{ÄS«Nã0E5Ж°+¨XšÚÈßôu˜¦aÅÄ’zLD'ë“y
+M?g7¡:ÛQ„htü­¡Ó #Ø^cA¼£¢eNBø@ϲî(£Ÿ;‚Ó!îë¨Üp)"ãsÕ‡„ÊNË’È0Íï`÷Ìlú†Ûµg)¢,¼%ì ä±=@'²KŠÆî'þn"N ÇüŽûè& §áºc¦¸x$;‡ AÏêr
+ü:óg"4جJä&#,ÎHµTcÍ
+c]wÉPëÈ?Ê-‰à)bÄ?¢Gë¨{qRÖô þv›¤ŸyeÍ»¤L%=ö½(êñª 1Q‘ÂÜch
+0`LCôrª£ñÖ<–+LÁ¿bŸá`‚\=æý Â@=ÔÙ ŽÔ²ç˾.æÀaËhì*’¤+“µuá D0œª€°YŠnL$E„·J LEX1ôæ¨$«ÆFí\zÖ<ÖsTkðFžPåÂN—ƒžlm.®¼v5¡9czÌ»úÌŒÑ/EŸ+'Šd~ëoA)r—úò\3¯6Žò‰G°øÄÕ
+~rÞØÉÌ»5¸i– Œ‘›bO1¸×÷Ä‚ óä‚~¶œ»M¬#_/%ô?Ú']k0rvH–ì3¹' ÌvI©B#7žVÒ
+¢l
+‰Q’¯C8’uP 6}Îß!Uƒ1ï»$ȃ'øë.0SÂ7ÙjêMøL Œ³.,&^„õ5RWgпBX9-Ü°¸À*Ó ”඲ÖEÂ÷P®¦2”H˜ Åà!€bÂtŠ¾Š`uÑ­«.¨óâÂ.¢çY $G˜|]¹p;Ëv/ -¨cÔgS½U„èR7‹)ª.Èž¥Ýb!sŠêËé ¸âº•dý‚QéÏõƒŒ¼×qòÍ"Š!}mÉ"AŠ^¢Àiœà¤´màô–8hh-ñß­9›faÐÈväì~a ½t2²/e_XÈ/è@hÃJ*.$iÈ*©ä
+¶¿ê×û(ºÆ¿ðÊyšuHëg±ïp¨ú0¡”³tˆâúgK«„^Ö¬ªåÄäúH/››(.EæƒðõÌ;p/"ô$–¦iCí0„Žó¡Ñy¬Û¤]Ò¼&Q”wEÈYo%Ö´Õ#É£ÔÛ}Ü¢K ùQ€ÿ¸RÃÇñ‰×x„ðmEŽëˆ„]Bðò
+—Ã[{-ð±Q”³¹Tƒýœ¦„u¯)Æ÷9Dš,^ ´à yÙŒ@›)!4ò;ûÚ)Uö]ý‘«0<L{• Eø
+Þ ­Q¤0G%ð³%É2òÁ‹„ÜLvûËF1F,ë[7ò‡"V—nGûŽŒ*NÚL‘;­§þÇËQJ;Ø
+Úºci¾mjSW’› ÅO·š`$—Ê„ 90Ùñ±0”!ª¤TÒNtüAÕÖÀ 4rÒ47ö»Ûe\lZàtèuÚž±ž˜]MÃ)áz|³àe´„·<“ðú­Y,¹y` Øj,çL‡‰jl1·èÂË#3àä0{røl½ü
+jȳƒ(’ŠyI›WÉiÕ§`oô7*Š3»2°sþwDM1 äŠX™Žö
+¥,ò$[ሢ7î‡ûþ²Õià.tê¦9ãB†œ«‰Ð[Åü>íƒNŒ¹™ï¡DÂsƒ°¸F%j™ÂÅØÛ~/¡ÿ8¤“ÙضßZüjš!:ÑÝ¢¯h²é¡Fl@³Y5ÄŸV
+ÒÂë ƒËüD; 1I´p-öKCúà–,pØÌ!ÔŽWà2e—ôn$‚Ù7@2À²Ií@vJ,è×…¸êôŠ 9¼Iù¡;%EÙ5Z™ *䑸w¢dûÂJ’„dn\Ħ%bÑÚÎ.ŠQälžŠ±è@ˆ¨}¿Š¾ SYšÛ=ªÇ„^.¿…R72š ±¨Ÿ$i'³k›âùtãëb’C_YgŠÑçz”‹Heyl—$Ó#Ì×#
+fj1Ö¤1F!öíÞkØ 3YÂÊ©^2g‡ÜJ¨ËK\¶JhƒhûV&’âNöÛd‡³Ò”rB²ó•ÆÑÊwE¾ãò<4™³ºd--¸6¼÷‰³:%: ë¾ p,üãw¢90V#¶d/†Pƒ/ÊJØôQe—Û‡R`Ç VÁߊ<‘jj¦ËFð·ÃD˜³ÓuÑ·ue¶§3[èx²´Ú…y=¸
+ˆŽ¡ž­€hXáû$ÉðԖβ¹'éßk¼ó›?"‚ìöñP;éìVœû¤Ãøó¢X‡øÔrs¸÷Û-Š|Ž†‰kÙ´=˜ÇÄÆ·´[Í» “^”{í棧
+9g.OD xCwƒx•`*‹BÂÌ}•à·GãßÍfþ²£RÚa“QŒKT4€ëÅ—°å™äS¡ùÌc$Jô|pŒÃ­«ÿ HÛxËpJ8Š"˜NТãÃwø] ©=ÝŽ‡‰pÔééUa>4T|B›^wTÒJ‹üej}z=vKþšÕ>êVåí 4u¼ù· D@ÌfGîÍyUS×Ùí¦ô¸Oº6§9fsƾ®“®-¦„p@t”1º£º”¬£®#¿~ÇÎõ6
+œÜqú¼¡Q„1pÉ(R}ÐÄ{‘”iG8¢Y~¼w-úzåNÆãÛ‘Ø…º-,\Žg,R&]}Д ä{~ZQÇ/D+Ž‡"l,éÅ`‚Ïf*ÂÆ ¿MâàG›°ëü\¶ªq|ìh/¥VTqÍì(àAªXvìNÃæ„ñÀç¹ð+¥5FÍùáqE‰F–Š>„ôPTar ¿¡Ûd…’š|
+Êé£¿Ô ÍÃÉcPŠ{|†Ur>Ù6Äw9†u™Ágaÿ™zKÈ: Îv} ‘*/¾3¹— ’ç).ûÊôâÒ Ap÷²Öæç¿€Vdy*@&M;ƒ$ãJÇ*:4:ånKý–RTŠEHáâWÈz9½#HföÆË‹ïp³š1Ü,ÕN;¨qéhiÇè )†$ã{ï Ö©8gO¾ßÞHLxvö¾±0ŒuŠþB‚G>SÔ;‚””;âÓˆþóãs›uƒ©H¤/^ð7 §ý»œö5残õ
+vùùïx)5¨jR¸t›é?R`’†@Û×™RM „Æ´™þÿÀ
+"çÄ•hÑn‰¼²[
+ƒ „8‰žöujÒK&éb"ø¦ÁˆìÕp¢dJ0¢ÜáˆÔ›sÝÇÎ>cŒaí0½cx¦J­ ó²*:0ýóä—Ûµ¹ýàFó‚{€žæùŠ[Sqˆ¢0ºçIShZZˆÊ?7¾bÐ_ó<OzM›,ØIÅ´eG×byˆw¨`[Š”äÓL˃°’¨[ÆžÎQO ’­\ãcm}Ç;G#sÉ7ÝJü7© œŸªæ»M”ÓË¢t»øƒ†}¬l¦áÿ})ú(¨;/x1y_L°Ì Å°O˜d°J“*‡~ ¡@ €•¶@ˆOa {¥
+k¾¦ì +ME6Ged=Ý-ˆ@Ö!ý|=e’ÇvŠLIÀŒí¨à» !¾¦‡’d»1"õFª*€ÉÜÃ5Õ^stƒï²YÔ*_¿Óã(‚ŠÍìÇ–¹Q&]ÌÕE*¹ÍùÁÍ,'3Aåœd1¢Mvf÷ƒÔG—øó®\P5cäÛÅ©N ǨÊV/ ÔŸÐaØšÓgŒÉìâ¬xbW˜Ðe&Üâ觽æp"‹'õ-3 …?òÑ,™YëúwlW>+A|C63—ËÖή4‡²jEcÏ
+È1ôæ}A8º'Òö‘V3ÉGÐAÓñ ¹Ó#æ×ãYs↛™ž0IŠjòóÑD–Ë’¥±ocd<™vtq-[À¨“daö¥HèòìòIneO1ç
+™›Ç2/ õccÖÃkiP/<ÙÚV¶ÞdÛØþBcðs¢,Ú²Ž‚:@ºûá­¾c´Œ±
+ù\ŸUy4”»8Í„ó¤«m‡†I4 ÜA㬋l Ÿàݧ6¬‡c•òšfZ ÍTÛ.0#T´-Ò½¶ç/ -ú͈F:ƒ\S°ÿ#èý^áj±ßú7ø¬íg± LrR¨ˆ¨™X½8•õô"GøB{ÔèLIúWcE—MÆd—Scæ¤î§8#ÍbÑöà°fø¼.wØýÉNƒMü¤·†Aàzs¬`&ܱ!:éËڜƗyŠZ/ʹȸ¬)4ïÓºÖ¢ëûÝÒmÌ|a(G@r—ï‚ÎæÜ%®ôE¥3q«8%÷XeæpÏ(µ©rò‚4¸†Màb§›’Æ9•[^ñ'gìn2³1Èì[£ó
++Äi74¶xà"ôdÖ¦iK×a¬ž„mÛ
+Ï.a¦ ÒEA°×ôc6m›{ª “šu¥¡uŸ è!|-ÂaN³¿/@Ö:µõ=†/V "®¡ÕŽûRshÑÊuc&ÄCt iµ3¬w´ºÊÊ'rz1AõMíÂhIî“ØÃ.û*ÙØFËé9•9‘ÞVïiYÊhe.˜Lf3Êøz®îã2b6/·ðwé8©¥d š†Ü@…dÚ™šª; Rpc„·M?È‚2—8; Xƒ)7#ßôƒp1ϼìÔfħñ¿n»§C.¡Á\%ÜÜ­³A–%JÁS2ÓxÑ“’>!~Ç`´Œ”z°õÅg<Ü2ŽåÚ—au
+\Õ‹ À¨ä³Ÿ„ʺ„lμO"Ž±M,t9¥1h_‚‘ôø ’:;$€E­G)@$îWëzöã´Ïdî–¹ÁZ÷'ЭåñÀ×Á+˜“ñ]3Ç ‘§ZDÖË@ïßYÓµÍøD†$?ÖK ýsð`=T¶h-î”ߤó$Û,Âý Ð'™ûu?íKÌcŽ|¿c\fœÙƒqÕ[øBÂû•7»Öʺ!—mb (.ëÆÂä•ŠÆ‚áF1hDDXy‹m‡ÇÆ"÷°“‚d|≛ùš–pý`ݪQ.¿@ÖQÐ%©‘uX«qûNee%šÂêLí\
+UyjCÄ;Y#¯3ý ºH÷éd^Š<¦^dS‡ô¼®Ãp¬pïNåo­Šœ?ñÅoIJ/G÷X^Š“øô=nEÕ'=G ­ž˜„ÉûÎP…õ™““ðálIõÄw×Q.Ebæ…Ý,ñ)¡=é9¾SàI<C{r¾ÉÅIu–O3úÏ è6ì Ÿ {‘þô´·ã¾z „Ûr=b‚\~þûƒÄÀÔà!g~ L"/N¹MÂ];¼ð9=
+Á‡­^Ìíï8
+gÃðmé„•Ïâ Dã‚u}îÚ­¯šþãa{<04I\ _Öz´É<Š}æu÷£gÖ1¸Í&+.ˆGè$‚±ˆqÖ^}ó+OP Á§>©REè‹-¢‡ïU#:¤þ‚†+6ê=›E9nÃp+ÄÞ ΕÙm.¯ .9±Èô‚½{¿ž'‘ÓäAìÈÏnªÌ¡9Os½œ©"&M;Îíÿ¹gíÅãDcØù&¶‘f£ &ÓHý(þÍhßúþè×Á`XQÝÀîŸa)Ð#&6锆åˆ<ce¡î¶òv$;˺îïØ­ZÔ=»å‹M„¹¢Ûp) ¿7+&ØrZêb0Ï €®N‰V¦ÆbìM†PþŽö0)xõ¹ÂßaD$'ª[tp‘ëDh¼£E«°+üF϶öcÿ†ÂL^ˆ††x±B88 u%)~ßú
+ @’Zætdn tN ù›è8Ü9´hœtlKgëPbµ' ²"S.5~„¬©à'Ël[õhãÎìpü1?\…(CDª‹âXŸ,ƒ¿s.–A*Š¶6÷XìxÒ-ïÛö6 Æ6õè9«hS¤Çé»:èv‡ØÎ~›@Õ8gêI!œÁàÏƯ8v1cïâïH”pSn›‘4ÂàTV™‹Ýµ XŸ¾}è~E¤¢Ü” 'Sf§i\ÀŸ‚`4¢gß Â)õª(¯!„öxLå’ý½£µ,3Ízüÿ]aWµ)·M‡ƒ‘+µ˜_06‰$ìa6ìF+‚Ô 36(ÅΕÝDìL 6 #Ãñ4•7Ñ"QåƯß!¢uIÞXsy»MAiùbže'ÏË£œ½6kÔN¬zýŒ/n׈ÍÓLRgÞù}_OY…ï[ šs!7†ð t ž€òœ8^Ù¥Å)ßqƒÁ<±%ZÛ÷ Bo0ÐÍ׃ànÙ¾!üÈàp )1”I1˜Œ²‹ e¸Ð\»JHh¯ˆ-ˆÃñ„… ÀYCO@”÷àžviº -Ü”)â•—sÁPËò™:^|G&Á“±Þû3A·P^í?ÊË$KŽ ¢'àú|˜‡5·u n“÷ßê›ÈfFx¾JJ ±JÆ
+¬+¶æâòZίºœ }˜åcŽƒMsVÆ" DÚÄgA»ð¯];Î’„œ5v«¬œÅŠ9×7ÝRÒby#ãw+¢áAb2zËÓõˆ*gù‘‰“'h$ Ü Áj5¼a¼ò„Ÿ©öB~)Û›º2óãú~殀fåˆH‡C=j‰gÈe 7CL,PA a®; ÂÂ6%˜žmxðÔÖѾAÝ+̤mÜ¢K
+'ɼÄv¥³°!&ÄdQžW؆¦µAUvÀ”„dØO(Qqñ$4
+ç)1uâRÓôõTÚ”å@`«Ç2‡YÓ%E`ö‹ Çqøè`Þb1.ƒˆ-IJ3™[i7½ Ä!ŸAÎS„5ãÓB ¥ *ÊÎì·ƒŸCò)Ê*ƒÌ´fA¦­ ivî±9Õ ¶ié2 éÍwÚGèÀG¤}ôOñpµA˜ì¹õxµ)_Ƨðþge(:Î;Ìpœ9ØdŠÅƒ3ÑlõUQ6JýÞ@!.?ãѬåÍF‡¢28±9 ÇÔ.ÎÕnÕ5í,ô¬cŠÔŽ#æ7ÆÆ'ÍȲˆ·£æ9kÒ©zö—Ø‹å£8ŠŸ+Sà,‹fXŒ ðÿй:w5šhë”Ffs ßN~å OÈUßf÷’Ó?)©)ä¯-iìšibmn
+.DA¹» bFp§âçÖÖa7s6WÎÁ̚˹%r”$¸´…Í¢ÿ¡÷÷uÐßÒúøDÍ›Å-Ʋ˜et쟅Á"eÎj5y•~hˆš0úQvèõfsx\ÙÓ,óœþˆx43¶#ÁÿùÍŒUžmQ.+sÿNÅ°1ÍU„Ò6„»rYø•m[F‰QÆ[„椤_?“
+êÝI'ø# GF"dL&¦cYÁÝcŽîÑDÓ @$²?ÃbŠ‰"A½p á'AVÓR™4.ˆiž]ZB}Õø;è6ŠºqÔËø³
+9‡ÂÃá[½œiZ°Yн¦-Æm¤/‚©>@Ú1Q`HÍSC¿õÁÔ”Q­uB宪N-
+JÇL£ GSù.û/&Ñ"Õ*E]âo•ÍÀâVÁ¨p_1pà TJâ™Ñ¡iAàb|„ÀÛönÙèÀÁÍ ^M¶Jb¸Õ*ËÔ\ ã_Bý‰=üZ¦ýË+ñ§ „ Bs";#4Y!ÎJ©Z¢
+Á­ª¢{QÏ؇ìD2HM]Y…ÐÏ$+£1çp{ëk,¢Òƒ
+¸$#Ê‚Dùv͆>Ãhê øÊpöŠK±Ÿlä«n&6ªà¶
+ƒ+•Hž¹¶£2é•ÑÑg<ã "%M7Þ£|óÌeŸ‰¢¹ùΙ)E¨¢V›;ì8g $Æ…È;1\œ³7ë7ç쀜Ž“ØlH&A—ÁwÎ(5†pà.&Ë„Ð}¹ûw=>YRùë™`Ä~"Ì,ÛÜa ¾”‹ˆ­i—ÐÍ«ÏLYPļ¯>Ç ü†ñ#‘
+ãö=N¤”|‚ª¸ $—Yƒí “ÿd¨Lš¸z¸µÖŒ9êü† ¢½Áé)[ ²&‚áJ5 5@¶;ºÖ6CóìÀlɯé+s4ékÄVÝÁ[A’hwÉ,s YcÕžùñ[DÉj
+œß’ÿjN¼a$J5+w‹{„PJeìž&úŽ¹ÌêC쎠{ê3“èÙQZ13ÿó@^4‡Â «-Ó
+‹gßqéÑS¢‡Ð¢ççRÃÛ*AºÅž÷÷áRŒ¢‡Anë.i]Æ1 }@\
+ÚÜj?d_ö㱤üŠ_ô_OKöÛ·ç¢ ¹™†!*z€aÇΈ_(Q<ÇšøIp@á/ÇÈÐÎ{´ã^-XXYP믗3®Ü¾õæwÁ0s°YNòÿ‰BäA±¥e³òÏ£ÿ¡ä™)$®–uùdªÎwobµ‰K»ÓQc¡JDlp>m•3=똇$=ú±,¹ïŸáD6|ãم̺÷‡ ª¤&?ôÚ ªÜ«5 #enK%He<â»~*¤rÖÑòß¾=—èÿna¤^ýÚâŸ-u«ù‡èÖ E6@v¬4a BoÑÅë>%¾;ˆu¿”’têlAÿ§Êù —ÁGlÒÅ•¦¬ÉFÌûíJ—°9ÇT¤¨Õå;ÒÀ«]Qï)9³:sú¥õ¡dÀ=t°³èÚÓ9ãÛ¦»°)ëgÀr_ï ˆáA(ç~c»Ö‡ÔŠ.–óúÔÊÃí¡uKãpz…—ëø”íA 8ëø,ÑÛðÃu›W¦ÇK4jOF\&2(i_ˆ¯=¾AïR.ú²ZX­NújÔ7ŒiµÇ»Äž^á€SÙžHðŸÉ4¼{Ht R³ d=&xt†]Ý#úáÜÇ"óe¿ö’~¤DJ[7¿Z–!\sgxWüÐm’X.ضOc\ p¼õŒrgÂc]º5él“ iKŒ'?sßnäCÃ/hØ@š ‰¬|5&t13ú4e4xgH>5¸ÜÉ&hí*âõÈ:—4Ñ+p˜¿LÊ’”@GÙS¢Y&ÓšpU&åBºâ÷á.ŸJKý/[ÙgQžnéŒ_sJ41z…¬5qj61*`!l)Ù¶£{=âÅ/™=®±Ç7WžP2Œ%çÄwà nç„ÆmÓŽ5®Û<”x³í¹ÒÇCÑtüŸ:i^™³A‡¾~éÈóRãc$RB•Éâr¬³„ßá8„TrÊŒ= àÐÁxÕfÄÇÖ8‚·fktÏ_H¥^%@7åæ1+ñöE2Úyâ­­%€x>_Ôöçsô¶dþ:\ú¼M¥`x†c8R"UP¨Jl·ÛÇImq`û%}ÍV/:®ïWQ•hàÑÉ>J4—.êªÞßîûléNõK=%Ô•3}d@¦Âð@LÒõ¥õÍ€mQÖ樨ڙ4W3ªö+ÕÛî‰ç Rë‚Bz}W¨{5@چߞ¸âµèû›"öz8õ’³ÓÁsMo°2H†²$à2ø=»ÑÙ/ÈÉCçæùŽ9Í<\$Ýâof‹×±s>¶ï›Tk‡v²U$é!_¤ÜÛ#n^JDÁ÷¡‚Ò×ýéå:ÉBdsTDЛ³ÁÁMÛÈÂ/ñáÏÃLn”EÑ3á+ÁÐÓYãcf†V×þ4UÝímoJ~ãSEiŸlѾq›Ø)ƒ¡v[r[âʵ™Y¯Ï˜†;̙ۻ]ÖÖ¯/}Y_ä&p;þã>uØ‚øÍÄ·çj;ƒš}»°ä¾|LKl,3>a„)ŒÔŽ ˆ0tÎ7a‡hW/émlža†UûÌ„áS¥a3ÓÂÈÖg:EˆÅi¨}®•LNÙϦ˜aÇØË^â,ìÔåG:ÜNã8¸CÇc=VäC;ÿÒ¡ëúÐk åâõÒæIãñÑmâï°ÔmÝ΃ªjù1Ö{éñ×]‘íU’îÚ&ã>ö«y‰H¡^LP¦ó@¦¡¼eèóªÁlqëç“óÈFA)´©\èCɵQ"\Ëà݇sbQÍp¶hò¼Í*Ùïmàzôšm]ʧ›*€cÓ„¹©Ï|e9(S½íÝ×VÂSŸ½[ÃtÙfÿÄ+ ­^º
+ÏHÙ–6K%if¦x"_Ù`Úo—ÈVphÃkòDFà¹J(MÞC÷å±ÄýÒláKôq¬Õ ÐRŠ¾?ùO™$äìq­á€N ‰K[âgxéa]ü{ؤEpAªÜyÛc(¦6årÙZÁiɽlOnkXñZb"öí£²Æ´&:Qïx±³_[¶n¡
+Ù<†÷P’–^œ•ØGɆwbŽ»¢'Àå¡Ñƒ?Ç—ì¡ÇeÀƒ›QAûEí’:G‰
+÷nËRí¨÷9¼_:Øâ…RÂK'€øÒ¼LX
+NåúgÆÊûgY‡’ëK¨I[˜û|sf×u]DŸ³$õäЭ¯I-;:Ó¨»o£3ÇH EÂœPµô¾ÌûH·Ãg‰•ã˜ÒJ>‡ÞgRaäÆ0WÉÆ7wÐœ· Q´Êa÷—¨mÒ„ß¹#_¹c~^Ýó£ïDîgSésÕ æcT ±ò%D>_½%Khw‹ ^*¡-ërãÜlW*¼ *%#ÚѣΠ7ë!c– ‰××+aÕ†l¯§¢w3|Ñ¢Ô¬§ ”‘™~ªFíÂ’îÅü~S”´˜”ŠúÛÇsÉ â#¾ú¨ïJ^øµˆbò+'¶(Ûã9â# ÿzQÞ•ì1(þ){ÿ)©ï8^!vžcº+ŒÕU"û@*bì³D?—Xç½W‰'XiÍpoŠo¼À¼,Û»s¸jÖâjS⯱$2iç2€N–ÃfñÊ×î½}SÄŸä]‡üt~¼)á lß–ˆkQ ]Χ-ÂÐ=GI{Ç›sÐKO¤<%Å7%n¡ ‰hÇùù©¤ÊF•×þ0õ±6€ùÇöLìt«W¶£oJ¼'šÛ«FÞ›þµhM€tßóØÏ iË´ ów% ²9;^¼7¯ö ©¢Õ‡¢KamZ†Œåê} ÃA!žíâ8' ™F5ö‰åìªKž±h<€Ý›…ãœk`:Ù´Û}OÐ’‰Â=•÷›ÿ5ªï CëÅÊ*ˆ.ûnK/eÌG$ˆÆ]±P8VEü8n1Â\
+í›5,¢$Ôúµ3
+¶ ¨-ÅäÛìŒ`ÚìïE:TÏŤù\ÃË:Üúš­¦ÍË<¦¯K¤àÖëKR)ÀûyCh:œ­{µ8?\_9ò÷u]ÜÎX ‘ÐÓ*!£ÝwõAÏÅTÿg¼Lr#Íq(|‚¼ƒO`ˆšµ®ZÖ-èUÖý·ý=Š2(‹„fh"ß´¼êrÛŠiئ}Ë)pCòC6’ZàF@w/E <G[iÛi"j¢y
+h‰¹b>Ê;ÀgòqÖ÷aø#V|(ÌÍ‚@âÙ$ßé°âÄNñ¸\+íuh*¬=±¥õÌf“bLSûw Å0D'-¹£O ®ÇVRp 3–yM”·–æuåX‡Ö1óCÉ‹Ëüë×­èÝÖÑoÎCBXظKt»×\¢Û­èÝT‚l@~s@õˆn” ç}ˆ¦Ï*oÙ" _i2ù×ìF /«èÃ#'‘!0Ò´{tã- iÙªöSóšÜ¨`]¾Uœ oÉí¢é)g•·äÀe|>$úšÜ(©²Vó™®GâjÞpé`ɹ~0Îï„tYGq4ã»N†y‰e^BÒ,0 |—"º ˜(û᾿ÍqÛY.(ÕÖ™šªÍéÝ°k e̺
+ ó
+þÍê%’‚ Œ¶bä¹ wÌ2^5¶ÒdcÕ!žzZÅùàeÌóš¾Nž²¦èXH,SÁP
+|*ÞK¸·z/Ú‡S´(_·ÃTèÊ\ü`¤v½Î4 ‘ŠGçýžÓ<ï‚ëå†ÚŸKˆ%E±7I8íÓ¬¹ÁÂP=JöT`p2Ü1H뺎É
+˼$+ô4½SH’¬=b+lÎ$k%ÛEÐ ¹ˆï¶Íÿ
+
+&'ÙZ€ æGrpjÖö¤
+7¦w.%&Œ5FÁy²j>¤,uf1y÷P.‚ˆÞ£å±É]d€õº²@„"’XšWü=J‚*¡h-Ú¶µº,W𚘑Þíz˜äf‹†&àÃ¥Ò¦à%ozäøõqlí8l¶F¾¼0â2 5üÙç½Qu¼±’†Ú¼5œ’Ê•BœÐá¦×±QIQÆò‘ó>~Éî,æv¬Vnc¬ÎÖ%@^Àp-yÒÿ¥¨pž¤1€›l/á©
+n„! ý¤ iiøpß
+wàÎsD…
+3‘Öx`zU4‡>\I.^VuåH—Š>ô7C`×ñËNÒËô#iÏ)†hm“ ožQÓΉ­L¹¬KÎ5÷{´xŒüV°‘‹¸0™ÚŸm^×h;3q¼~= øÈX½ß&sž`=íö&ƒ`'h\6ƒ½ÁWÔ$»7HäYY&uE†vk´(¸Â÷5ãwÒu\ÄöC«\ ¾ŠºG?2]·é½Ë(i ‡±=/ÏëB|ÿz¬Â æ oü$G*x0D{ñ²=˜†‘––{EÖa²ˆ¯…ú ó3‰ÈˆeÖEéqf@·F”Wô°K ƒ+ïNæ ú”»w : S‚æ¬hF‡YNPÈôˆK¶¨Wúkv“ÈÀ|fÑË^§â‡Ts• ÇŒCb‘AŒi»„‰Dxk]v´Qèú’¥[1}1:“ŽCJÁܯä¯#_©ƒ9Z‡dÙÓÔ`p%ÏkI SI}âRÄ`ÀËY§ô¯Ûi/¹„õë­Uƒ£tùÆ^Î&æ”ß¡ŒÈ1 •ÇáÝåaUB¦gø?ª¦ ê·áæN½jBùgôàƒLÑ݈­àÍ1£
+:‡Í¾ÛPOr´ñ1MdÖ.@]Öˆ®ç<óa$Åæuº!WIcÀ°Æv‡•` .\ÆF"y8«™lÃ.b¨„SÓSªdÁ}ÒA2Íy9¦‰§Oú %ó[9Š yŒuž:4Ôßj~‹‹ÕZLj;Í>n™Îîë dUzyZ=…-Ž\—J€:ZËc×tä´te“ ¢Jl¥ÇB;F#3x£ÃP´n¹,SÂ昊Îà·†
+Ì°©€>ùÒ*Ù×a8ðyœÒ¥êéñZd‚µ·¢1e_4ÙE¶öVx9Ùá„”†
+/eÛÕŽý8&=äEå‹B#YF‚8ê°%/ì µ¤å,„gxàRôûCàG¦pe‚Jcê»þ ܤ#¨d3v LS\9Šýð·É jkÐMÇŠ0n¨6úïëÐOs''ãNd1“ îÊÙK¼¡ò¼¡ï²®"„)ð¬'jÞ°³Hy÷e0v–b[,ó?ÆË$¹’¢'ðú
+à´ö¶oá­î¿öK­ÿY¶¢ÃÝ2DD±‰˜À¶KÀ8| Í‚ýo³}nJjãèXHyËpP{X B…'ô‰óaeþ{óD,é¾£ïéroXF©0?åFeZ‹©Má«g‰t QÉV$¥‚ØÜ»:©ÞX…ŽÍ«
+×Û»áö~ë]>!éñ081h›¬4‚ä+?C§ð u—tr}–Ývoñ.Avü&ÖfêU
+0;Ԣøø€ÆŽ2 ¯`ƒ
+šÃÛÏ?p:€˜½Ä¤¤è Ö`ÕÓŒ!î\‰êŒ~ümÚf'/å³ ?RQåòh7¨q¸
+°±²œ˜–å”tÅSòظ8{·K€Æ€'7y–«àÆüöà7ù .Ç✥œ˜öÖ¹É)€­7°e+òÊyÂUL¿ªB à‚žƒ¢IUüM˜<ŸŒçiV¤u/áë™ef5ݹ5·›ŒP^ò&†Ï çÆâ³ÿ²T)âˆ5á´±ÏÁF¢>üjË
+¿å é&4%dÐ.%®¿&A° ×¼"©ÇuV¶àF(¤›0jÖLÏþªðîîè))xAY»a!õ†²ˆ;úx¡N:ț윣$Éå ÁÅŽÜ«k÷ë•^È¡ÌÏ‘_AÌÏÑJU2Û(Õúñw´´BM> ü쎌×ÕΆ"úLªZÞœ"K‹’¬¼ãLÐÅÔc¡û1&‰¬³ÜG;(Êr˜svß_Þ oǪ¥qhg(aa'z\ÕåËáD8~9sá
+ØW8ÅK†‡£pÞcXºk·¸(ëµ¹r‡L™\½¿{ÎFS™©zº'Å•|S¨™ð2ó×L(Þ×Bñ32ßÍmý¿Û‘0&“½‡šúº,\[gù†L)ËÖn›õû¡è{˜ãI˜ŠÔNà¦üóš·Ë FÚ×’dÎÚ‹ò‡â! åsN`5Úë‹`GéÒQÝEo|©ùüA·ÿI¼ VáRÔ>-L+l
+Bš|g³ë'ØḞ‚Í8Ý°L'x<–\*12úU,EpƒÃ!êÕÂì1 á>˜€† |˜ýkdp ÉëÇ¿?â(°(I\Jü„ò®
+
+ΑÞº‹Tå_“:>§ é,UKh-¢ÌGù“¥&S ÅU†%$Šé®ùð*]/@ É :J J†F9%E êD½D–‹q/D&X ì/ý ü–/oÄŬí%§X Û·¯.rç¦WUZ¨m;Îé¤ôÎÆ;ý ¶XñùPòÕ%Ê)€Ö°ÕìVÉÜ <Y+!
+™¿ˆ¼ÈC“Ýf*ø‡R"mìŸ4;RíI’5b €G½¶±|LcÆœÝÁïC úÝ3Yê ¥¹a£¡1\ÚÃPY
+X
+U#¡¦è½ÅõZ*!â³HÒYc ëíÇí°™lîQ3¾C)ŠÓ}ÎZiú&£OEâ×^bv¹
+¡ž–5r–Å#Œñ¸qJúBd°/!F(äc0»…aT6s
+ÙäwŒ Ñæ7–º¿ÏAv IÊÚ9vHFúÜO —P>¨Y—t{®J?N¸¨V~µPYtŸ“6YQö•òx ¢ë"é«øÁÂ7!ïR¢«´“ 'J{^×s¤
+ ¬Eñ¼Ä.<*™à”à,³máZ3—0‘ͯ’çSÄŒì‘M_Ç@ó¹g§Ã¹8¢úÀ!Ú‘äs §¤B0Ø rVŒ;ú„û°÷ ©Å
+ $˜b8ž °ãyaÄêó§ð¦81MXkë„›³ ’#1uŒÿÅ<Y@ä5óE´MÈ“]X¥ÉŸ:€*-ÇjžÎÓÿS×Ègû` ºd—Db'bÿý
+‰âu!F]D°eìÄDÅ«ŽjþÃ9lÒèû´·`ùÑ\ îþŒÝ uhãÐtG¨æÈÎ,[…• •>Ê|€ð\>®Š:¾¾tqY6lš õ"ÊÆpRŸŒ ~‚tˆÅ±‚ŸŒ~™HÁÀ#§Èû‰—z!ªyo û2û€wzPbÚD Q<VÁ0ÝÚa± °ƫD‘]¤›MSºÉtH^‡JUÖ€ m“¿|>,Œ‹¬a‡`/6I©É冖%6°‚€Fñˆ4¶<˜XU÷âQØ2.F.{;©[’Ñêd‘½3ü™¨9été$×€=ê¶ffÈå•™lDD{-ÖJ/ÔÔžv…+”nC”Uñx«x7‹óö»…?ps岧#úe,šÃ ö]±3<™™™X„^-lÓÄ(TE™QÝÉò(‹õðRØ=½¨u/ºEòÓ&,c_ueýð0€P¼ ¾b·é„âÙθ„ËÑÅ0Pî·¦Hž‘‚ð¢K<gk]Çd1øÂyÄÜÑcéÈtn½ÐH
+ýÍm³ÄÅ\²?î]웟=dÚ& v ›‚¹Ãx8„Èד&ÁqˆÌi¬j|H<³T°}:¯a‚ˆ_cSóbuÊ^[;NUî!‘¡˜Çq®Ì€W ÷î7Uã™Èm~]G¶Ÿ‡‡Ly#ÏÄŠ²§¥&ËàÓ&Lñì<†KV¸a{%?ÎP«9ÇjÛßàOÕc¢á’sYLÈtSÃÛ‹õ@SΊù£êb!Èiù¾k†w`vMÍføLÙ
+ ä,ó…)€ZÂüÈŽÑ'–®Ç§
+»4°DI-9e
+ v 9;ç`[GêkaÆäÍdþüqÝ]éß2‰Š!õÜñ7˜Cp;>’›ÉA/ïØià#¹Ážqaædç¿[rDz èrrjÇäfNH<UUfà/'„ßçCr3]&ÄÏ0!Çä$‹!!þŠeïÉÍ$$USL2kÉ øÙ*MÎÇà‚Êao0*}ó¸ y䨠@ÎùNI‡s´XŒ© zHe‹Aq10r¦ññî,.„œiËf¿O.¾2«H`hy›{pö5OGH¥~F1YjäjMûô>É®/Òû9ìÿþH_¿
+©pÁÀÚ†ÈoI…óB°×MÉ5iOÃ]jºj ˆ˜·nÉÈhÊŒòž¹ÜTþ†PÍØGN†õ»ji©år ™¦^
+˜MŽDÉX²ôQ‹]Ø`LŽØ.ÚƒdÙ‘j~æœô=±tÈ8êÔ»C…\19„IÐÈJ:„Ûsw µXŸÄge†Åâ>ŒÒ,3hëv¢*vac£ ÒQv¯ª…†˜X—õð/}b¡&yX¡úŠ§ëÌ"»†nñÔx÷²í›R@Pvjä›íúqX.öÉA…§_ðå‰F1‘En_° &SŽ hËÒfLUÊ@üÿR²Æøë¶wâvL•‡€wôθ æ“­íxZ¿Ìçœ7)œ± åñ)¶–£/Xõx9vJŒÏBÖuŽáìù6þ-…¡ÿJTBª˜ä9ÜÈ=O¨ÌÊëQvpÐã7& j3ãçÀS$<žÚý24Üð‡𰕺¤Þ\ÃÈÆø ™廹¿¤n~Ï)ųtDQÁÄ^'¢è²F—<^ËV²¼FÇszùþH8â¦kqã#dzXïÚFˆo$¶~,bb–i*ù1!&Œt…r@—O &ïG­“ïUƒ’à mÑ,}i`(ÈEôåqL¦™b†±¬^ú†mÙ,Ñ/1[.‰)¾Ò¢)S7â Z€d­Åw˜üdn;v‘@Y‘_KÓé;@rÔpY|ˆ€e±lûC$€«CýWÏ{ª&‹QÓÝíEâèEÙ›¼‡ýÂñf™‰5¥@ˆ$ú 'Eeàå
+U©¼åa
+ÿv’ê5X 1ÑlýEy2C ­Äì‰)˜NŠµM2µ¢RÈdw§c@¤:òd¥~5䌂gsžqŽ¼'C¢Á®'aŠHNs}ßÍÿûϨ‹vSÁ“„ÿ8³ˆ¯¢e—+0B*¼ro&V4¢®ÅÆkäu†\Ìÿ
+H‰Œ—Kn¤9„OPwðÆEŠ’Ö=˺E³rßÛ_è‘Ó°ÿÄL
+(§#õ ƒ¡þéÝæH«Ó>þüe³~ÎV<¢Î1Šü«ªÃ›{Ô1">Ä:
+—ÁÎå\¼9«=8òdoY­x-Ôh¼Y'Já³QÒû›Ãxðý9fm¾½T“uz¦»S›”yfβ‹SÃfÏ:̳È Á£Sı!6JT³³ŠÑÎÊ
+v·2ªânÕ»Žµ2fÈRv«¬–¬uævXQjºUÞ—*³´ÒÍ»×Ë›1``·vËWrŒ#ŠÕÓ)¯½wŠ£…ö:͇å„ó&
+k¤ûœ¬¼ ÑkÔMë9 ·Î½Ìhþñ[™ØƧÖ9uàhVNÇ_ä-[¯|ÒOy¸R/ÆØ„«@:Óâ*D¹Åé|ƒóÒBt-ifõÕñïí´OèA}¢°
+Ä U¯Û¢µºÖYÄ)­&|Þ:kЈ‰± ”cL&Æ|C¨DwÈÝr̳Uzc®œò€²NÕ˜[¥¯uºÖ,eP›i­hã\Î^é:ÔU¾b\æ|£ìo™•e
+„Úd¯-‘Ž>³ž[5Žì­ŸÑLΑ£òûÐ6Ô³Ô2ùÍà®kÐ5‡@*1âu†¦A·fmA´ (–õ4 7Ÿe0^µÏ³§,b‘ÏMb)&´ƒ9° ¯u&Ó!Wž?C®4ó ÛÆâçOPPq–a¿ÖÑÙ¯GPCL VJÏ›™È¥Ê±Eô ‚^3‚–Þ8WŸ’ˆf\Õ汪ÚE9W÷†”ÑߺHÇ7\í¿Woùô&膸ÊÃ÷èLÜ­èówî:L/,R}–´gzj›(äتöR°‡Ü@¼S–‚o1r»=v€†—âÓÚ8—
+ÍÞ…,ôµŒ¹–b‘»²Ìg’“½GÖðÃœf½Qe¨=o¬ã•pD˜ùZ'Ð’9`¶oÈÄT‹d¾ÜUøã½C湕í 4e^§|_„îcU ŽLHÖ`ã¸öbš¢¢ž^íG5Àù>1‚V1Ï üOA‡Æ¹¹M /óY^ ¤ýå(û®`KH‚ŽV; Ç·zîVJlË =t°žn7Èh ͹ÝìÂâãóÒ´0´‹lV8ÌNü‚ÅI¨„ÎJü¸Åæ(ù…šÂó¨ÃÑo\S‡¸ØC®u¸ÀD@¢:g¯@³2T§¨?{£6°Ÿá*N^%° ÊD“Ûíwª×P§û†¸ÜNÇÉÛî ëN¨O=;U Uç
+Œõu¹>¦m›X›KÊ\'8Å5S¥iQ¿±ð™wÀ›Æ”+rö£mâA
+QÎö
+S ¯7 |9äš]Ò ÚXô¬÷^ Æá1ÌLD¶ŽIz´×Õ;ÿçîI××Õá}šÀw›ÎˤêÅös­³ú EÛ‹cY ÖÕòÉËÎXDIò@ˆòˆ5·Êz¨CúSšçkóœ‡W¢@m3‡
+„Ÿù#O!qô¦èa¢3›ÌyòV8f9S¿w^wG%îÌ54Ø…ª¡f±šg+R¡æ Z´A,ÍQÙûZ†ò"¿û@DªÆ-T¡M?´²%›·¡€0Ã4€éßág¬g@«N´ÁK“f©yÝÙL$Xï&„í@xî
+½Ï5Rq”²@Pö¾–¤SÖ#?”®Ý‚çR5F²xq6åð?WÃÜÐ
+Ú $¬HunwÌCÖL(éDjH+3ßùN‹âöøtÛN´‘¡É',¸y!Ý5M(/|â£é"4ŒR–z¶i.ªM„
+ó+õ?–ì×wÄJhy?´\ÜèÑL ¯{»q¤É
+$`ŒÚ‹ ¦âB4Šs/«àTžÈ•ùr”,MïR_o»áš±ìõò2š¬33"ÏDÈKc…Q§>d%¿•/ µ¥YU4‚yhnd{PC‰7ÇQƒE²¾ó
+2$%ÈnR‚U 6mË׬0e?Bší´h?¼#È×G¥- ®À×Áx€­¶ðšd³.6—ë¾Vü=ßè£flˆ+¼¯2”×NJè/ûhú3ñµ"2±Òf¯n×<¬=â
+¿â9œ³;ùG‰ƒœl;÷ ˆãeî£9À›”—Ã@åý¤L²_¯D°½LÎ\ñ4©Ï#&å˜Mæ Ò¶­Þ‹ïaÃM¶’ oZÜm‚s] ß Ä“
+_•Ã Žœ!á&¥Â}×',EC[Sq.â¹'ŠÄ¥z0,Ï&oÇc䈑D'Ü3HÛûE3ô( Qebæ[IEXwï˜ÂØ ¼
+Ò'ˆqR‡»Ä£h»më$Fœ+tÆÐh€˜ÆVÓäê›ñËî…SÞ4Ëž­²˜uhÊÓ•ò—Ȇ—úZaùJ°¶g ²F™Â>-gn<D´SP·}èoÿ•ÙŠ{H´gïRÎ2ì£ö±qØçAîY:¿<®J¡µ!k¾_Jl+­ü6²µÇË:N²9ž©Á`6PùS¢¹?š“تÀ&È J[A¡HÜ&º³"†ž¤iúìÉs^þ}q¸…ˆ‚9uÁ/"‚ÆhùÅ{N±ÄDpï*n誾P7 îC¹‚ÏðŠ^ÂÚÍìz=5³+¯€öúF(òÜûp^'r~“pƒH:±/%š¨“•ÿ¶ú(* >¦ †«oëüPè{ÉŸJõ¶2°tOtãm‡þ¬Ò^Kž3u-ÒpC§ðä|[ç©œ—’?£â½WÈëÆÍbŒÑí;³S²dB‹üókÉŸnê—+0Æ âs.5¿ÿº%ÇóÜ4˜f!õÈî9÷›”®RkÓ˜µ`‘dy‚ƒ”!_™ŒO°
+½q¢î¼Ä“hε~¾˜ÇÀØF˜Y¬Óq%ŒÓ”²À v^ù߆MïX/éLƱù¼<#(â`ù…G]€eÂMý`œãþ üy¥OwŠÄhr~L92³_(ò8c:Õáä2æmJ0uš,7f£b,i Hí…¶("¤€z½N¸È6FÒ ðs–‘C¤CàªÜºDNoÁQhyŸ86¹ŽÁßA‰`êkçõ¾qu"›ÕuÖ©„³,Ÿg^&ê”4iê»oY…K
+âøŽS•|)ú篛Û}¼±‘Ø2±Ý¥èé¿ç·!%ÒCäÄ9ŸªF ušÏ/„V‚~JÄ
+†àö b" 6³ç$E2¤Xi¯GÚ°A'«ºý¦ˆ÷a3¦}9éÃN€EX˜*¦ðRsíH†âGÌa~vâC^T#½_Âw 3V°ºmšrò7,ýêè>K:xFçõŒ²îE4[£¶o‡‘AWÔc²v[/Yê‡ðúV·¡ˆU8uå?†÷ç £ò >ó¨pÚ¯º8ÃOL|dà— Ð7åæF€?0™T«Ù—²Äð›Åi`78–fÄ?øPƒ4²ç¥è” F•Òèù­  ÅPZo¯[ÕÑØÑÃ-¿¬ÓôÀR…¨ä11¯|cÐf¦Ý°`\ê#“eƒŠp2Ÿ‘^&¾Ÿ…ÒÜgÜÒ‡‰OµÜ!:ô[Z+°Ù´¬š@• pÀoÊ
+ "™øÜ{ ÇqÍbz¥L?S€BP…£Ÿ«fa€ŒY„éãÓ™Õ[m+
+" ÈYŽkƒ(E××|]žùÛRŒm¢ù²®¨¹¬ÈΨ#ºž!²Ÿ-BÄZF[´î!–T=F.¹Ëw¼ƒÿà (ñ)žŽ*á^'‚’ú¨‰hÜ‚Ð ô?7:8¾>#|¬)}Þ¯Ñ眉˚´{Æ*ô <˜‚P_ªúnÒHYz8_òפ÷`.§–fhû¶¦ÍUâˆa‚uóuMˆým™ƒ¶Xg¢ ì¼° 5n¥_-³#YºAÔ+ZA;ge.÷sÓ³à 4Z4¨v6 Oºh,‰…óþDðépô²Dò @ÂÌÄ{v1?ûaëý1¶hpåÆöuŸñª5¯"|¹Ú¦¬Kˆ_ÅK¹é v·sä™ðû•T#}jW•–q]û´Ãgþ$mx7LÂœ¥r*…"±µeô–Ð{áÈdT\…1`«Z¹¹ MŽž‚6]¦’PX¾´F‹/ùË"„SßK ð²nAùJ$²,Λ;„FÈI¡Y- y‡Zͯêà-NûÅM°på#˜‡æ75üthùdŠ‚ÐçRIUü%äâcŸA¦ÏL”…[Köo|g—cb=DOea¸aþݬEHÔ|ç
+F.8^³;ïþˆwéU&ÀGô|=æFè"¯¥l ?Ë{Uåµz p`áôñY!K‘øñⳠС@<#•CDág’« ¶ÄJA¿ÒÔJœ²W)£VEî}ñÊXô^>%®Z¬tPÈ8›µâ‚@cl(¢¡a0·ž "›¦°“ò9¢³d„‹áþMP2˜"õXÔ%HQ•ÂRû~>î¿ŠÍ]ŒÎg­/?Èßý›–€½€7ü%kS ü¦ú EÃË×®Aõá"(¤¯^‚p7ÉýôK!(yª¢¤»RXG;íŒCÄ ‚ç˨bXBº’N¿«š¸x2‹ AôI|ë¢Ä4Ql¼÷2Eä# š)tô£|_4ufZÖR:È«‘œhÿÚbÒt3¦€Vh/Ú!h9f쫶Þ`P·)ͯ9ÞRx:ÐDÁû*‚F¡v].ïa]wË*9!ôpaî$+BÈÉTã»!úAÖr2Ê%į2ÕnrvÑË9Ì kÈ¢wçzy ÄÌܾý$ðòU’Þ*ÁÉÇÙ!7”†ÖMöcV#­XôÎøOÛ Üã5`BÇvX/k‹«g0œÂÞè‰Õô:¬ù1»à<²U'Šº×
+ÜÌ6íH¢¢®%4¨ž‡[Hã&æAÁ<!ÛTÇè=“%γ
+¿ÜÔÉÆP´« Å'š³è5,¶Ðxr`=ÛyG{.½Üµ=e?‡d‘=ŽýΟÌ
+‚n‹,vO<">ºÏ/zÄh£Er¥}_·—0ûM -¦×·b<¿‘AC±ÔºÇEŸ™AXz©UÊvÏ¢¾Ÿ‹Vc£ Å`£*}KgòJ"lc¨s;êÿ#ÄË„°Ypü Áã–³„³ôü'újÑ/˜•õoü\2èPv§ÍÑþ'ªæ^ÓŽê}Ôêïh‘cŽÈ+ÃÌX´ü-­d½¼$ ™Ñ¬ù–Ï‚0,‡_õ9ßÔJ†ª=´Ô«L\Èÿ)7UƒHß9õà›fUnGÐäͬÿ¨}¿‘É2©G±ªÁœÓ^eòTIò§-ϯ¤“èMÐa‰§|™¥uØF+Æäq„&ìÕŒüïÏ7¤‹q¦|lvvˆ¤U’nYþýFN8S{¼mä¬ÑÙй•Çiï‹CúUrÑ‘’>#
+ߧœŸ÷m‘jòÓ–YØf¸¢§dXª™g:Ã¥g"áŒEÉ
+å:ÞI‡PtìÊ‚Ï ¦T¦6Üç4N×–‡à«
+êÌæóxÁççÚ“jrÿêÁt+?»æ±¢ÿ©4k]óJÝÉ-Nñ½.å¡Ð!gèy^ËŒLÃn 2RïÍBÈ
+.·ØKÓÑÍH»’¾¡Íû´•Çü£=ÉÉrŸLJJ3…±æeM%Bˆ n#¨|OÑÇæAB+Açâß—B”±Dms¢óG( 1ƒ}‡àZ]ÈQ‰SúŃÕóLÛ!¿oü({ ëÄ’¼ª›ö1T›D
+Aùir|*­8§d€˜¶¦«m‡ n@zÞ\÷A\M³Î„ÅU= šn˃L}Íô(AûfZÐ:â‚YKLªä¶`Ô´_"ü&Š $Ò;Ós|9†t³­
+4ÿKy™$ÉyãPøºƒNPÁ$×êeÝÂÛºÿº¿G€j;“¿UŠ°¥L$ bxƒð-ÉPÌ%H!°Õh{YG½>Ûd÷“&º‹ÜÂ`e„•&ƒ{ â“&ág¦.ƒ«ËXÐRY&.‚@˜²eyõth ^ŽR0=©üÊC aIÇ?_"üáq8mëî[Ю ²½³4ü·>…ÈêQõÕc¯WM*LÂÛƒœ(øzØ[…x̽F;_vj‡
+Þ¸†øM¯-;‡¹`à*çz
+aWA3–Ñž®bDá8¤¾ Ÿ=œ£—!ù<Û&C:%9}3‹± Tl®¾.#á7ñÙ¶jžN¾®Â ©BÄ”¯·ðè"’ûP}_&ŸˆK' /°N®qÀ‹÷hKÐArxÄö9(10¾ 9Z„´&ñkx
+ýgÑupw ÷ÓÄóô—.÷ÂïV5mfT6€=N,®"CĘv*h£t^g‚ [#Œý–[‚ÃtÜ$â\˜cˆç!Ø¤Ò ±«‡ˆ áä¦Îã1õð•áÉJ¤C  AÞ”œ8ÑÚtŒÛñ 7„Áí\Þ õMÍa)Æ¿”¸H‹7
+¤×Wi㛈*¿‰eà*€½"Ø@dàán|$Ç8…zÊšð' ²¥ÃÔÜÕ@ÿHò&].ÍìJã]ÀC´¶õÓÖPÖ²¤¡kˆ0• lG/+ÖnÐ÷*T]é%‡ZBšÒa=£ß„Ô)}˦/_S…JMGØ6Ž¡é%µ?W½½Ÿ# `#zyJÆè :‘åËÏâŸu°ž­=ÕFÐÂLC–f¦h_–¶¬õØ)v£Æœí8C±qÐ yÅUÀ
+*P®“’†MùfÐ?®ûúNNžúûY—‰ªàžp ¸ÿôùô:- Å
+IVpZßy»ìýuß
+ ™ ¤P,®’F”m^¿ôŽ®—R¾ÃëŸë½Ûòó??pqð‰dR‚^í"p¨ìi¨Ì„ÞFÒ–A\  x¯ëN‚&ŸËrKõ¤:d×yBæÖhEÊ„€ùØòzù;%þµoÂv€aÛ3Þ\øA¯Ó>!†©…ÛR;ìËɆÈ
+)ûá6ëô‰7´…q´ Œo–…X½ø»- Ü+r·7»„øUÂÀ¬<œ„±p)“Ù
+“eb"›×9ï[m5Fx
+È5Ybç­‘:(\îfä~j§Ò˜íê±óF~äY— Ù0sA9 g<JÖƒ2£3‰‰›ð”B24uõsào¦Ñh§ÅœófòieJG팒LÐœG83Z#fí\Äîð1IäÆhÙF4ÐqÌŸW˜°®S›{Ükè3e:°{í\ô¯`#XJ–˜†ô¤¦‹*º@ë’0p—ò@.Â4tÕ.A
+”ZÇkHZF>‰0ºYƒa¤v€\ã.”¸}p
+,—Pßôë}Œ1'H9°+¸BÈVTZàmG
+ÿèT€$*ŒõƧ™r» à °åC- ?Zц"‰c¾9“¼Ä=²)²Ñ‚Á‹Ôlõ¸
+H`RÔÌÚ."n‡Ð–Ýa‘ -` âzäÀ!P=jÃ'(ˆ>M ’a*€ÖŒ*§Ff]5®NV…õ`?Ð&af Tö†3 \%‰Uû%$˜sÏ Õ±ZÎ¥ )(ÑêɈDsaH{¶ÇG™ZËl÷zŠŒÆ¡ÌÜ XìsºØ’ûmPi=h#!1#‚ìïpÕQÒt³p@EÓÅM¯²‚ †ˆék¦1Ù!hÙ@4ß
+8(¢ä›D$‚Oý-Ù^úÀŸÇ°ßP=°ÏA?o2ˆI‡Ô‘
+Ÿ°ù{7…l,ãN‘í²1_Wþ Ÿ àP±ìš
+‡ÞÑYïõÊ„ƒFz÷l®DÊו²¯É!0T«>{¤e‚èÏVvˆ "ï],£_1ùqÍ‹S9SÎ^!GJ²ˆé¨&#ºÐ„=tFbTÏ„©…MPicKcš pö ÆX&R&H”üQ0Œ@ :·YH‚{)2÷ÉçpÁ™ÅâlãKð_݇Þ@&ÒÙ©Xv˜ul–
+¼D¢·'¸„X*¨@m‘VŽçM¤Öo»~YÅ:Tâ>ø‰7
+V§/šˆX<‘t*‰Pq—©ìèȺ€†"BE‚[¥ i»±ø'¤“ŽW&‚"ËÈOfÙÞ NĆ0²º
+¡’¼E4OÀ1™%N¡ï¸~{j¸§ì*-hÓ3 šû1]TdàDE—êÕΩÀ[ÆñL(oê 4„ÜŽ!ÙŽÇBZ#lûÂÚ÷ ënþZ1œ([ ù¿äÛ˜,ÝÈîP> yíï)(2kƒ*Š‹~œCnžôrŸ¿÷ ù%ê
+”ç¬bÐp°"†§Œ'ÊV÷òœJ˃/` ªu Ý œõ‚(v¿™Œ,´ÞP¶P‚8žnð$Èê*Mêžê„þBñÏÓ9 RM¢½<„ð5( ˜Àº>Ð &BùôõTQ¼kQÂCj$
+»‹÷ºŒ.ïZ o
+õf ÕDÆVàdµ(r^Sƒ
+Ÿr8|Ôöð÷ ÙµåÄ6pŽ4 ï~M‚b«½ˆIb~M‚jF¡ÊQ_>›€©Ü³Ÿ,™„¬ÚÒ¾”™¤•Y
+|TbãE:œàøÛ®‡ƒK‚ ¥oöæK‘3ÕTKAJÓcD^„=šÅݪ…n8(Ü!Ð2+’%-O†­fa\ÇFrƒŠgBÆ:¦TR㮥!áÙÃf!ìH7:{Óp8]‹¼èûQx—(%«÷ "‰Ê"_Ö9ØRZ
+þÆûè)M…„“ aé.³ngÔÕj^Ü%ö’ "y!’d®@,ˆÎÐgíðdK„:fÔzÛóË2FIJôžÌ"RUÞr}!mòŠÍ®ÂS¤>ù >°€ì$òKœÓF2·êIÁN©°0&
+Ý!réì$é¢ ÁèpZ~ܤ–e<„\”åÏSл”£×ÜH¨¬Öøטƒ];Ýì!l1x>•£[#"w•
+H˜íÁ­„
+'ï⯣[+†:…®‹Ìô¾à‚âä»F Âõ3`ŽvV¾ºïj׊
+±¬ÍÈu[›»]#ˆñŠúOäd׉B«„{ég»ŒÐБ¥ §3ÎÑZsÒïÛüß-ø”aI軶M†ºdãã ùn8ü6ÉD„"n¡Y}O27c÷`Š!RaUË Á°¿qË=è:È_'Ö½ûïøã¿ÀœƒwY&žß%ú³‚º¨•M€[Žu$ ˜F§ƒã%Æ.]%D0ØBf!Ÿ§ƒ  )–û.õuÆÀ4þÞjÞw¹NªB9 ‹<·z¼O<A‰Oè³T}Ú]-ÚsXCìKè™ms×&ËW‚=ü±T4⤩ø›P Û¹uöTu
+a*ˤÒ36/ ³
+ ‰šÑ'AŒ)—n:ÏKˆtJUâyi·@Cù–(„—0ÁûÜ/sõd!@‚ 2›J"‡»ptòTx&’/{ï:š`ƒ0Ú:1;Y\û¶ë„`¼Twõë Ü,SÀz&‰Þº‹pVð­YS®h=2¶‡ùfó~²¤áaIÛgȈ@”•¾¤(Ô8º.š¹­Å
+ˆ€k›£œ Ÿå‚~CöTÀ=É€ïK$›²!´uN¨'yWüÒɆ¨U,gR'^²žòiº9ÌôAÂvªÁúÅ“»ÒØLÕœöÕP.m¡r@ÜȈ¼ X•5AXPã¸9‚A#XÇÖä¸4k=ž6íº´Hj_ª•Â €S·¶å
+* ÿ"XŒ_kbÀØ—ËÀ ‡ ¯‡ à–I£Bsœ9©ÊöP Õk
+-µŒZBÊŽ=Áˆº<kDùÔ°"
+*”=òOþ®(ÓÅd°¦Õv˜±A™À‚Ù¹7IÔ6,•üÊ
+Ù³Ä8û)…N2°”q‡Ðˆ$Yk.[!hD#‡ ÝPGà\9e{äÈB
+Aö}·Uâ­é¨$ɤ(î,†¶(>Ž–>6&;©o™´[ÌkT•¡¸6ìÏ1(HTZŠ>š ¿ʵ‡å&û?ǼÙÂoï³la–BÀMÅ!Ííwÿ2^'É‘#G@O ;Ô d
+ò<Jîø¨<Á^ã ±á5@ª$ân?¾:ˆ‡Ïß·žw§¾/˜~+%6É×|äÄèž¿9LÚ< 1—¾˜Ðö–=çF8QG³Ý²ôw4³·ß}ÜIA@âie¹Næë&‰9p/Ì̯W}¦¿52KzYþKìø4¸âÛžÔYÖV2ÜÎЋ`ó4gÜ!¾3Ò½dç‡N{º‹¸ín¼ 5ž°€“8~f­ó­è÷,Š’ÜÙïk3Rx'˜ìgÇ (ÆR®m_1ä{RQˆ3¶óV‚‚2+­É<wÏŒKïãåyz ÖJH g–D¡ÊÉíu”´ü?®Ù*6!Çh=k+‘Oö{Lÿïì{&ø~Æ¥Á3iTšxÍy{ÞøvKŠxOleÈ7³[ô±œiåQ—)·#~HBÝƉ d·+*r-²…¾°Ê›‡©âš[Æš,eDÛ-%XwNùÕùÃø]í¥è÷¤‚;¹o S¯ó\¸ÞcH¯ä•‰ðÛɶßuÙ÷-†[×ßÞo2©¨–9â3g³<WdØ@ÆT¾0²~"ä–áÊðu^7 þdÎBŽŽ”pðó•_ÏÐ'ØŽž$³X t>KJ,¨ÃíC‰6–Î̶—iš·oˆ.ÎÓÐ1?_ᇫ/Sðæ4ij¤H¤—œâŠ'œÍ,´ë‚±õAȌս¾®ñ)ó)hñYòÝu½Ú%<_
+œÿSƒmVl`“¿ „¡8ì¡ÿªÚ1…b\pÔ‰›»¶Ò—Ìl ÎݹhøãPÙ¢;TþøüÍØ%§m?LèG¤Jñ[lËåïߢa†âÄ?ÐtÖ¯ú€aÜÎÄ`’Za²k•x­„¶}‚9>ÑÝ¥û™Ô¯à6êÑɆ³èâ¨h® $/;v"M%#C¦Mf×Vh­s'–†¢ví™Ç©àCVXœkl¦ËñˆÝ,ñŸvZÜt÷¤zæ$ :*@Ǫà~'x¸&XPb–nx¦ÊQâÙ„QN•€iŒ3gà
+ݶ%´ß­køöè)¶ÜÛ³ŒCq‡Ò¶/@µìdžÂf­~6^kŒq(‰'aÛÒÈÿ‘ôŒp5Çu¾Ú‚P¸0Cüpm/k % †V1dý²ýæH_¨Y§HHdÖhÏ9¯ãàú±E&mÑã#ö7T4Ž 2†óLËB‚ÊÙ׳
+\ÿéuõš–Q”Æ3­P1(¤r±BÜñL²‘@€ž
+ÇŒA€Š;feŸœmëS7$fÃÝZ|EŠFÉ>¾ÅæO''pa¼ø>Y–y×Oe{†*Ä»˜f¤{NX4Od:—o#ë-rBƒg ÜÝ'8¾|›8‚¨·èE™\42s¿†‹„.B±L…óÅû!¿}Êâ]ðƒÝηŠ¥¿Îç1€¶ü°LüóëZû,lôÚ(îÛß@ÜPÝZSáI¾®Õ51píUs®÷Ãœ%™ò®eÚ ø’rÏ´¨ [€BvË |èÏ€±û¸º‰>Ž"ãÀ<Q±:ªˆ$hŒI_OlÝš§0£ç˜N·çž:7KŒ%ÎЦ×<M€·s=Ÿ>ò 4j[¾íAaÍ · @ò ÛFýÚÚ¤œ#lâmîYÂúŸæ=m°Zߺë¾FÌã‚¿
+˵Í@F5.³å!Õ=ó€jú2õL
+½ïûë%6Ïð?ÓDf(uî*ËqP›˜N/‡øÑr*TïQÎ:HÎõ¥j]%Ÿ6êoìXœ[4\ä䱉æ9xÒóš†Ö?Tõ×k‘O
+g»ì{D ¯±‘êC¿f·8ù=â8 Ã8mPkÂOB?KÎèfI“ï¹QtT1Û¹š™¡yg>ºC4#y½7”~]pœ=Gø£$[ÑÁrpØp»Þºž’Ýmø¯Èá:xÄoß¡%šŒâ¶}´î}C¹Ëûl ”¶û¤ÆÑÆêýyó” PÖ+pqöÇûÅ‹CúvÀüŸM;ãÜÉ›ÇIÿÖ™Ûöe~í mË3',7ÌH+½D+ËÉÒí_ÀNï]Ø«¹•¶Æ ò²wæÄÔ—èÕb@²¤3æ¾t¹g¤ Qa–W 7ûÙ|пÆó¡¤p^™'®ý%¿'TÔ™TX×Xþ¯×Ù$¾¶eÐÊèUóÝîiŽ/^Æèâ%~ŒÇ¡¤ü•¸Tž9/È£k½jóæâÊ]CB‚Öë½À‚务
+Z^¤(ØJ’FhÄ¢?$X?Hq°Ç,Ò_©AŸû­`Tœmäã+˜iÛÙìî§0T4ò u|‘ɧgGžo+«¨ò4aî¡zÅÇÝâög¢J Red»Cx¼’±ÕÅ5ˆC­œ_¾âs´¼`±ôüá4iUΆíÇ[=Ù¥`³¶íãu0N9f _ŸXI;Œ”ç§VUCc:¯XÝÅ‘-W’tÂ&¿/)ßwí4s`a¸E'ÏþâîÆNbE¸¶&¾½š%ü”l£S«U*ôVB1³
+>Ã8s«¢HÞ^›•BçV§ÐêX é›°kâÃõzï®K‡IÚy¿j¤ Ìyé÷>()<9­žŽ
+ã‹J™‰cúxGÖÐ홨°"Èl´2×Á
+Èö¨$½ÉÙòS(çdê¡Š¾în¢‘å+£†n¾cp;|G6Í…>+ß¡ðÇ4 =2Ö©‚ª
+–žâ•{¼©
+T…îöî!©ˆ”ÐûmϦ¯ðäbÈ Æ|œ!åã•,ËÉ…¾pÖs£ã ñ| ÝkÃOèû4’g $ëÈÎÏ]­”hÍrˆ3sNLôûÐ;<ÓdÆ[T™)-ܬ{÷ÍU²dð“í\“mÆqT¹N?G~Õ…a].U86jÎD §¬Bmÿs6òµð.×Pª[˜Üí¾oÉÍ¥†¸r꛵ïf`¨æ£–&a˜#¦‰‘Ì#@ÓAq0ݦ…MŠ¯Nq™J`ìÇ’ÏëÖ½AÜ„jµQ9t¸<œÃ(V]‘ŠîëtÕc¤ÿ<ÿ¡|Ì-µ…¹å_‹Bd]ϳ
+‰ĪA·<€ÕÄôkñ¡÷D¡á—ðæ ¥SáB
+™¼LŒó@}X4vÈU™¼Ä‚ӧ$"Ø
+ªålÄâj©ú”ó8&¯@p.o
+=÷gêÃ9”!i¸öùp›cÑèl®QâË«¤¾Ìš›c¸TgiŠzÇ[ä5¦/òÜI&]MtKý6\Žjû[÷Oáôe±!Éá£#Ém²nçŒÍ6ß F‰©@Ïf ²e;´ÅcÅfÑ6¨u+£iCRÊãqθºr´@:Ù/³~#f=„6Ñåyà B2`’Y@vèø²OÐÛ~þu@›ÊHª¦ÇÑßQgÚGKårÖqx7ZKÇ2ú×¢í¥b)Êy¡`Ù} eè‰^øVôóÌc'¶C¿0×¼an‡@ÀH·æ¡<@”Ĺ P®¯O½ƒ¦ ßÁ</”ãaj{@ –8_y’ZâC¤Ë´ «1|P¥;t5IõíxR¡MòåÛÇtóoÈÀdVùy*¼|.2d”À+ó‚±fÓ" åáS8j|^Ǽ_˜¦ðHìÚîýñ០÷ú]o=øD\Zù ‚¹àC¦´ÚÐ0‹øÜ/’êˆCÓ–œ|®ØTIJÂA€d/X€áBbÉ?@ôž-Ä“Ž•Î!n’$TÂõô)XæR<œ3–œÖAÉ”£Ž è‡ñUÇaLW, Ü!ÏzVóPi&PN8/µ¨ñÜ’OèÓY;¥g@ óÞM  %Æ6µü9\GŠ¨ÛÛˆçÆN,õ0ɨI¨qÁ3Òx¸”K€!S¥•ë9ë{Te#NªÍß-Ã`ôS=ž4
+ˆÍù/ÇÜS¯j¤ ¨*"Ò Úó—* áéÓ,Ú$ÍÈuE‰Eþ¨¤m†©êægX¼2CUhÐÑx})É™ðÜmtªÊ}@ü&z@JQ¤è;†‹"0³Œª5aÀ“F™a,4ãxºcõfiVv÷ÉaöŽi_&å@dúÛìˆè±=EáÐôõùŘýùÇ ô&2wø¡Dr¿†#•Ÿ‹‘éT]ÌJ!÷ó×óï9’p ª»ÂÔ¢|Â||+Ó2&Ž—µ”oÖêÊßKT„a”=8 1<ܵð6®¿ ¢¨©A æêŸ{«ãæÇoÔútäë?”a~OUÌ9 q1$?ˆÉbÝÒ’_s>»*šÈÖÿ8 Ü7¼Ä9©• H¬+¿æº#-MQ,$/DÒ.pHqK„_!ôCUihÑI=ØâÕܯi‘8–frAA¸Ô!MÛî³ZM˜¶¹±õ}ùø“츈½µù‡0¬D'vb·È@ýâc»œs†Øfdµ[¾¾TK2í´« $^Ç™q¬W±ŽP“ ŠáÔ% ”G F†çœ~è^é„}eÉ¿Ì'dsyö“Ä›Þ1°"ÄäuQ©Ý­Å^.?¡;Pff¤„°ÑY{býE½¼|>—[Ï-OOƒº§}‚þø5¾®ãÉé[±w³°>¿öºð/ÿÚð¡‚”‹èâ톘 žê îÚo3¹?™bgt™s ¶‘—P`´OAÈ>ÄÕ¯P÷¹0´cCƒíœƒ¶1nô&‡à–<&‘JõåàM‹ª#2÷šçç×£ðŠmüœw
+<V$Òç˜M<C7½áè&{Q¥i‰Þ±á8T?" CÊíø`"4ø±—ì+I j_wž¢¥4¼—ˆ$$!Õû?|#jJ‡š)åõ_ýâ~Ú8Ã}ˆA%gܼ)§ Iv?D2D Ø€Í=‚«fý°Þýâ}l˜³l)‡YSh'K ï§$†iòïÊ=ŸAÌb7qqú¿„‡âéz•˜6FÈçó9†)nÆÚ¸Û˜Ea+õø¿>¨ {µb#š¦‹$°võOá™êÕ^¹.ÉPâ¶û™Ô~è‹g!·!¸¡†ÑRë¢kÌ憥w|ê1%í}*§~@ÀKAø'AlÙöì¼ìUЄ˜”Êœ‘§ö“Uc/¢€{ ö5c¬1+¨‹wë?Ábsçmm(a4ÏìԤ؇´à.EZQ
+¥°—‹]E‹` Ä…Š\°]yPÑ©ì’¶XH•ËƒKÓèÕçþ# YÆk:ÀýS·C"ÞX¦sñ&êÕ\Ô •©ðaö±~/ù¢‹Ý®l
+„•D¾ª¾ôçuGÁ`9>,æÞÇ?Ý@ 2Z)º¥Ñ+]ßðMKÛXCan–Ö !椣YÂfR6"W/Úʈwã`uÃo¨Ç.ç\
+¯ öpW€¢oÆÅ] ½Q±ÿéûŽíH¸™ñ ª‚Lg4Ÿ®¸9)QÒ¤(}âCËè6Ü_št>ŠhÞƒãUoMebéx‡”ÎV8“´<渨5õ
+]ff‹LÔÙŠAïпðïCŠìñ~¼¾Æ™[Ìî%Àgè!êuX Å -€Ì»» ˆwÛÚYcb%µ`Gc&€<=ǬիO¤Œ!Id©þò‰ ]‡ù ‘1ŸtiÒ­óu³BLyb Ž¿µ—àøBI±WfZ‰e cÿÓ© ƒƒÓâB罿¡å×U@8â'nÍn(ÙYi µÈ¢t”jBW
+†ì ­@ôÊÓSB‘˜'vâÁgÄCþðÔ¥ßÙ­8gçfb»ì%–{ u±¤«¯´Áóz |1Y“±^‡ý*¼&×Z¶mœÕb.5ü©žSͨL¯P²ÈJ>-xöÝŠS’¬y8àq„þûDfÚ‹âMg,…-èŸ8O‰P*ü8
+Ž¬mÚçú(¸Û ·5ÝîS¸ÆaÄ&Üã6zîV¢mxŽÂ§Ñ}Ò•iÂ’¢j£Å—Ü"Ý_%ž¨±­ttÚ:½¨¾z›ðkÅ7ªÿùãRÓLÂ,ULØõ×­CË 0„nßîH;Ó\“3祊–cÁd–—6V
+™®SÁ·ß1Ç<q ¯%š¼‘`Mè˛̢ … ¬'+ŒE^bŒ“Ž¤ëûz,³üpÇ£v¸æõž8_bÉ'ŽnA£Øåm­¤Áœ¸“¹Ý«)¹TÅ p‹1Vrƒ«:º|)‰!}›õ´,jBE2x#JÐl¤~ꥈ^¸GŸ¾ºd6jl÷Uàï^ŒÕ|ÑVޚљñN ‘Ÿ&#µKŠrþäÊ4“)¨%º™‰Þ,gaxäJ O¶yËHÓJ30R –oI9|¸á¥HÀÔ
+£ÛLähâAÛƱw7Ÿ1%#%'Œ·éoî ±pÀ¾³5Ñ~™Üݧ=¼woxñ–Ì"ƃqÛq dÁäc7ÜʽËk‹‘4.!–í (´$ÌÚÛ,áÂUòL³\0eb¹ x‘"úö€ÌܪC1í¸ÌÖÍÉ)ža;GÅP÷Oø4ÔŠÕ/%?üïGþúýd²ô™P‘g¤?¦VF†¯¿ŠÄ_¸RÎÔô¨”@J¢Xýš£‰YQ<^ÈKY5vÒÞˆ½[“`¢<ø§mÅð3Lãâ©çÀÐL%B+1 µiJw'“ ƒXŒaXÒÆ”ÂÛ=H£ .IHã(V!ú ¶B¥š­#Ñ&V?Uh
+úä3³³U{ãCQ¹ÒöÚJ^›’Zé/àl0ÄC²|µuè ˜AÛ±–Vh@ªQ‡"…¶u)ñ­˜ŠT4jýðöÇ:4ÿlš¼òp+š§ÐvÎ3‹¢ÖÓ\§oµ”ädîèx«¢nb²ºy‹*h˜z-1X^ªª(í^A3µ
+€q\ìp›ëa¬ 1oèÑ+ó"¹éG©aýv<ü@ΘÛAï²f¡róĶ*ú9OÀ>jV º¥
+#ÿkMšL7ÿîö¦·’¥/yõiè¼UІ— @»~¶|Ðä÷Wêê6)pXöu`YäÍ`ɪX•NÌ4ÝJtý^
+)+Oxá, Úè ’H¨'ÀÆ3Ì‘c°+"f¼£DËpÁçÇJò`?;÷¹Tg¸³Vœj2! ‹%K}J`¬!Ét3ði%/ý+šZªHÖ"Â-Ì«rå€`+c‘@}ìÌŒçóJæ<7fN¸š)™ø]ÑÈo9‡;B‚´PÇ1i­[Üð
+û²<]ãt³_Å™ˆ'l°ó4ŽŽ½‰Ý[ѯ§"°Ò7Îz7œœ݃šèsØèK ½!pdËÞ5pUXu¾Ù@è]çZß<¢U$ºÇLãv£9s×$°®ÓäKÑ"ë@$²eët$Ó¼x¯yPÍ -Š’"ƒa%«3äâø}Äù³bÑè@oá¤ð KÓMÞ9ö™ÀѦ\Ã%ñŒd8$¨Ö³Ê?\øŸÿÁ©ÿ§Ïxú½Õ•ÄO´ûšQø‚õÿz(%E÷C~Ö—J4.©§o‘X0áH6…`Z ¬9qŒÑ
+9bz‰qè:¸àä*"òba†ÀsÔ¦Y”L&Q”L:’AE¦G¡Y´ËKùÌJ¥UK¢"…ºhFü}«a.<8  VQJdkÛ ~éd>TmŸhI̼z¦AU%:š
+×Óº”°Õb –^ªÙ'~_gys˜9ä|DÿPÏ Kð¡dÕXؤFÿ`ÜZ]šo%:%qäµç¹U‡ÌÙXFpiÄÙF¤œ8 ¶¦ š3¯%oh[]ŠCBTaïúu;L3v‡**Dx»j0?¸ŸýûhzŽDu˜¸”jÍ1çÅVèì)ÁÂÿò?É^“û—" þûRbSÁ±H´ãÿBV`
+H‰Œ—1n¬9„Oà;øÛHŠ’â·áÜb‰ÞÜ?¯$ý Ø-/pÐîj‰"‹Å¢âÅ?ç|ùèæÍ£y«ýó?íå3óÖY7$FΘuð]±imŽVj³ÈÏÿ}
+*É훟ίл¡ÇÉ_†ÕD:Jz@6c /üt®W<—; ܺä/µ—I†è×U(j
+òÕ¶ªm/TjØjSÿÅ8…â
+•ÌÉ`™ñì»ðÏÇL2Ø
+¤­‚ ìsû<5˜FãÔöUÇ|áª1Ú‰ËRå‘Èžé_Ƀ4­O/"õüŒR—\¢—ÓlaåLßvl Žµæ¾Ê$ôç’˜ b`P4 c_ç`j— F\Ï9%I¸¾¼i}qãZ®Áj^ \U`¹¹²&I½C¿Ê3–GÏù£Hðé¡ü!“€y&ºÏ»=õ³’®u9éT&±
+Ïdã†×悦c2‚¢ÉcåO†%8ˆ8 Ñ$5~´ô§–A`žìþ¾Ö`£ÙOj$µ3ðm¬€¬ÌwòLY|½4Ýyö‚¨Ü¸³!…>ô› o »ê¾*0Bu,Ô|ƒ·³.`^ÚÊ^ð5š0»]0.üË–û¹C˜ƒ cÜíÍè
+¢í¡Êr¯s¨¿RßÄéM¢\žÊåײôЬ€£ú<•*ÔŸa4ů³aa%HŒÀF‰™T׌»íYæ¨owMðÌ+ý¾AÓ«“õŽQ±HÍPeQa4ß ©`Íj˹‚£“qdšös
+?a
+¼7)þòEÒŸÍR5E"ø¾-wÇ·¯.—§]3jà9É–÷c¾aVŸ“õFyÇÜ<í2C]^‚}d5:îD¶¦è˜sÓ"6š2rœ–X~Š½¨v@rbŒ³@dt6 —ì¯8ç HЖÄWÓ¬'A(èºÝˆÂÏ»(7Õçˇód:…ó(ZKE‘ÍO œtŒ
+:Ó®òœJ5´‰akjK Ö€åhð@4*p•Ltò¢:¹úŽÁoùÍVJg JDZ't w:•ø­)ΞÆ( B:m5r/Œ¤q…4ül#Y°’‘®zQÃý(_?Óå¤hj¸ð‚¦áxxSõ/Â
+¹÷Àpf¬ä.ä›6jKž$.µ?]@Ve)hÿá˜/nüù2ýð¤/ ßWÍ« •Â¥–Ï2KA‹k„ü±ò™·ûÝÕ¶ÕP°bå©eÕ2ËŠ8Ï3€Ùfè…‚[ëËV@".f@³t¶³†™ØÊû½, `hP
+ÅÀ„ÆÅAœ%ŘØhU†d?`- ˜Pùl:˳1j™¿«RŒ öˆ¹XcÄ+VWH0Ǿ zOù/*ºÇ‹½º–GŒrËuà ×sÎöφæÓѬ’ø¢eL ƳjDíö5DñƒÆ ÜCt^M.(8—DwQ~F¬–å_„•5Þ-Ÿ‹àQu!Ð6
+äcc–ôÝ|"ÚÀš ·KØI”*¢ê
+G ˆë­•ÂzYéK×Öb¿A=L¼,5—ñ~¶—n
+Ä¿Ì$Ÿ­CзA  •(½]Þ.Ì ƒB †2Q×-ðïV›| ‹a®¿{G:luPNÿ|œƒdÌÐÜ,-²íR¡!Qˆ2{Â/Cf ÔË‘ù=þ€†p€#ó{bT ÃuH|{Í+¹‹~=Œ6¨0Æ«Hs¢ÊÁêìÈ¥WÁÍ-¾Â²Dê€ÙJæQÒ
+¡>¼´gV¶Îú1§äP.ýs65@B%–¼y¤0Š ä@‡[ö«$ÀŸ9€…+Í2k½ƒ6^[Α‚"$jX[˜O(œP’Ø4(0{5l+‡2îˆpøܹÅUÂsŒMi¼ †¡­` Hsâ1h«,ùŒ`·Ç¨qT®éûñùñïÇŠÒIŠs¸$ Gf‹}cÉŽ1õ/‰MGRíÇ×Çšh9>E×Ècöwcþ¼ëû÷|éÕzãwÐC«{|õ!æéEüÐ)UY™U}7æéÕÿù½:?þþ˜(ù$·aO¢†¨GBõ“1ÿ9Å À8­HyˆŠš¿Áy %ÞmØ €ãº…ÐéU¤ÊÖ¥œÐùŒŽ0TAFH;ÉxÅ9ÿ ‰K‹ªÎáÈ®aƒ¸W¼"Æ`ªX…h€§v p+ä°«0*ÚQîo×€”lUƒÈ˜ºá;#”[[PFB¥Ø>Ч{€1nB<£(
+ò"äæjÎAì5cz·lõ+Àœ¥?—‡°†›<Õ®z, ‰ÜÊŒèH¦•Õ)Ô£4"íIá
+™iˆ"‰ ¦ —¨33‘a‘2“:ËlçïbWÑ&u®m=žƒDá5yéêë50ËÕY• KM¥{â*(•4ÞÓ÷—$(ŒR#ˆ™ò—Láã´â|%{J§fG¤àòL숾qPº#A”Î}ßÞnQŠÅƒqBIý=áÛÊg‹ÏàŸÀ¿'e*¶(¶Ê,>ŽxM‘J‰Ï ºx6 ûyb6¯EvyHûGA(ÏÔ†”Eret Ò€!'X3Úº~©½AÛ,s7Ùeãª"M¸ôØÓƒ,ÇCYâ*u dÊ*ÝcB< $ȹèQb«]KN–AY¿R…,Ùi†ŸíÖEÚ|•‚uãèéA©€äC&SÇXÚl¨sH0·!KY,ÁE|K„ËR\@nÍͯ’ˆ\ÚrM¥évð”%MçNB͹‰¿\XÌ=^BÅF楥„èÅŠOH½FSUã!Ù‹ÔVâ=ÜI©¨*Ç£ „€:¹qÛr¡ÆI£0„zÅ´vÊ_b™ :–ãz†´™Ó²ŽOåÍX±{QJÁÖ v»·“Fº2¡ñžYåxØ6€Ïp‡CŠ=ŽE]( Ìh!dÌdk½Ú!À –—€2ø‹1çé-¬ ‰»Óo¯äå¶.NÕQ‡æŠÙiGÛsu¨°Rb¯Ú€8SØ%F2L¡þÙnÄM@”35S{PÕÚùëG€è¬=G\¨À«Póxö¤Ó¿]G7, ;I†
+´æx¿fy¶PgØÞÁlŒq]:@²”)×
+“¤{v½½•Ñ8]K(V@6iUhða"¬Î&º{u¬žÀ³I©Gê0+§ Œ†„u×âîKQP-Æ>”â9±Íë¼ Ñ†~Á
+ø „]’X‰v"µ¡1T{hß$äÅãó/ÂAÁÁvVÐ&“´ôñåMaʹÀñpÕ¥±Hb5‡dGâj¦n5þ‘º {°
+©`n6Êö Ét`îI ýÛ–šfX<dt6 ƒš(VxK™ÕC:34™cV:&Å$G]¹Ä{ð=ácH¶»49òšêäBHc`Ë&«l!BöA
+zBœš·V5Ñ „Òù´Q·9×õÒ4ÖÉk!· ŒÐ<õ/_§€a|†p¿ÜÇ“% H
+‘¶RÝP‰ÎÊhí"„êŠB™õ¶O ô\?Ö£‰•++ëã}`ÌÞÇäþŠT<ÛZ‹›Bv–†¡ç<’3 Go29ôx„NgÂa¾„Q
+6µ–—^Já‹`ÒB•ë!èÛ‚˜Ï»BLc_³ÎÍ3{Á©t,Ù¦z(‹Vmo×NÑHÊ)ñSŒ™Ja¬~ˆ¨Pè'e)²s‹1@v…uÿ<Ýõ‚¹ò*ã<é{
+TÔÂ]¬¡¿…Ñ€!WBsFˆ(²ÊŠ¤ÊvÉâ1ñ¥ýJfD¬ˆjÌYSÄ<Åòà_(Ù <@ªsÐo™k¼Œ?€Ž(Ìm3 {2÷©ÈàÆ)ì}ejb6YTÜÚ›~–Çi® .¶L%¡)35ëayßµ„¶qi^ h«2WBñXºE‰
+ÇÎe!›QP>Hg‚¥T ðè.L¦ÙJ‰fdº±€0ƒÓ]öòùñïÇLŒ±–ŠÁ÷öèLG™Ÿ&©ÖçãçE~"ñ"º„âúú˜ŠyÉþþ?‚þ¸îû7}é釳nrœ§‹çº
+ÉÆѪCÐ׋ -¸¥PÁöõ16Œ¤ŠBmúðý˜ûe‡ìÞ
+b]á'Ð ò*ïq€ŠÔ$¥ñvÐ §Îmy³ÿ®·Ú2X¯$ZÜ’ c^õs zª&?ärW  õfÈsج (˜$C_TéôT¥ÃmïÅV0ɪö$owŒá^€Ì@ÿ(
+ mI›Y›
+›³¼~oÇ:jL¶FÎLÑâ :âBÅw™¼tÜFvÈ¢ü¼ÚcˆsÅÓâO«%\e|%TŽÀ"˽Äç0š’ŒV¤[’ù‰Ë*ák‘è|}OŠ/6)B“ßäªù¥h&²&Ýöá«0†XÌšÃüBç¨%Í¿ßmSÉ“±+ª‚™4¡«ŸïüûóG\dعDÙÀ ŸÑ]8üÜ$BtNw%9ŸÄ®Ì>NHCvÖ?Ü÷‰È“’Ú)Á‹cGp—:ŠL$€áØg¹EEhÊt©è‘2ÝB$ÍîU@d ¤,JšZ¹PÄÑ.´žJü*
+¤”~Ýhâ‰RT“U‘·?Wñ°„“ÊÌõhDBEüyD¬þÂ~qI…ë ”
+ ¤aÍ+ÑÎ/žÑq7˜ýŒ½pÿï¿,×5Ì°(±àÌfí]þ„5:Âà#&é¾°h =Út‘ÍC%@^ª —ó$AïÆFkÝy"AJÎðð-ácqØŒÚÔ̈ŽVAŒmÿ¹¿ ‡BÓ%µ b¹’$ÄW‹>žà(1ø°ð
+J¸iǵơS1 <—ŽÑEuã™’ªÝ«¼ÁTA^H|&³š}Ïõ‹ôZbŠŽæ:GFéÎ@,D·jž%2”p¡k²3sçB€, 9×½oÊÐo#tdlQB˜#Ô9‰Tœã„ÉŠŠïÐ>”Ì?Š9³‰´Ò8Ó¶„B@#+o N‚?–áøÎ9ðfqPýÅr ‹ ™ðÅ1„t"⥎°¤í$H…wj·‚¢7W•¿x­“¦‹­nPC“³h4ÌN\„ÖÁÔ ”†}¥áK¨ûP’>’0hÃáoL42?‡öVT¥™ÇvAˆø0ôÁÞ%bö^¦„ì”d²KÔûöUïE& «¾ÂùÃ9Œ¡+‚æö±¢Hd†¢Ýç›drLSùôàž•]Ù’5?ÝôÄ“?u+)ú«ò25ôÿ*BfP=„m®_†`°{¸8‚Ë
+_T¼xTôÜ¿ âuAÉû0åÊЇ$$åKN6‘‘îZ…ïA@±&a Í W Mвs•¡ö°,ö{vsú€ D‚~!¤¶%ûQëÙ„ÑøŽžÇ§ŠÿSäž&¾¬u 5§E¿ÝHªXoX};Ÿ0QðE²M¤YqÕÇWœ&—&K„“—g% $¹³(·9 i¼ –†lQÇhpjyžCZ•?¶n.‡þVÈ®v8ú­À•AÝ“kGŽ íC™iäÑnÁp×;ÓļS‚@°J¸L~è"õ 4€õ"ÜŒ±°Ké æÖ.áôøûvZqV Jrí^•ôŒoÁ¥ SøJg´&I`ÑRW*<mã
+=¿ùþ©„é)¨¡7uY€ÄÁ§«‚ÿuî¬è^¹b „\Ø ì›hÔ‰׫,$9ù¶*õc
+¶f«:Ãõ{)ñ(¼Eé'îÌù~î –®è1ö…Th·†ì\‹?Ê0D±nKk…ͱ/i;ß ÅÖ5Ø\ÏaÏlnÃ9,Ž 'Ðä4¢yRPeþ1D÷˜gÐ &ÛÒ²do†àS›œÜ
+Î4°ˆÇ»2JdžƠ“È'þžïZx#UyY¿P' YopMV—Ñ XïŠ4· ×Ì+"jÄücVeÿ±+í(‹¨¿M…V*ªÀ‰d Àï¡F”F‹ ›L'aeEbL‘¹?¡jdVØT.£A”h¯g“kKvtÃ0+àzNgae
+*K¯QøfX‚íŸPkàNx˜¶ªæ•”ŠŸb/u€¦]éjÕ|‹ø¤èq2%néé‹„úô 4‰ûñ] 1aŠsmëv_ÌJÀ¾É²ÿ©ÿíܦÝãÐa—Ç‘©˜GÙh`Ô<„$‚U$P¢R!Š_= šåzá0ªî›cdÍÔÜÙH9K•–; ‘Sž¾<
+ö ÇþCíÂSyÿÓ.ÊÎ|Ìp…»p
+ ÑÜF¿C*~ˆü­”=T
+‘ü¤«lhÛ* ”Ív*t‹_ÕŸß+m,ˆÈ­‹ÎéìwB!ŒCÚ/ÒrS4˜=„ù¡ÙhzdE„tp’ õâÔE…(â¨lü;‚€P e‹Wñ'²jIT°ÙØ'Ä O(²2µS©F»„ü¥É€‹œã˜ÝÎ1°ÕWQ¥>„Qa<l%ðBI ¡/@Gé H.— {*à€oPe ÃoÆÀÙŽŠ™£5§è‘4¹GÔ˜7ây°ûmÔXü­hí¤¤Lb€Â ÷ ›öKAÃÑ„!+ëTÅÌêR²É
+ê>¨8EQúðèåÀ® ·™øeqª ïQÁŠô3žˆQ²ÁdŽ(¡„-®Ï]BZJöf¡i$a7^@CšBp&Ò«ç6r%4ºË4z*<!Ó€Ëae‘„ΡÙÂtú•j¾ ´fC£é¶ƒo!>¦(%kšÛ‘6u+h‰]irÞc*)*ÁTê^>BP”EŽfzdƒrÆá±öŒðD½zœw™#P©ÿ\åõIè3fMÈ<Ž`YÆè@“÷N$1Â'òŸ=€`IáÊ]¡J¾¼oD;TÏ#:—ÖË-*5ÓÕÈ;Ú„òg¬X*—Á7Žà'Ö™Dô¼êb:Tq8#_GW™mOƒèMh£ž<zïܬ5W§b¬ÉŸãd%ÔãÕ(XžA¸urƒÉtŠ¹ŽD9éß²<DÆÅʱ‹_”Š§BJÈXh$[¿-¯|Rœ¥yµ^áÑßàz§rllÒŒ9Šƒ&ëØo˜ÎÏÇeo)ÀÆtW&I¢GÙ‡hv¯5Á‘ê?ÆO ‘ÌÂ@V ý‹”+›Ï% g‹0¥b¨™Í!¢Ø¢iJ|©Jm3ûÍ —t¿¾s'¿úå¬7Jr •Š¶+¡ìО— Ÿ‚þ½bĤ¦™9
+‹9¿óžìòºo=EMÆÛ6–dC¹·øôÔY—R¾ýúN½Õ–üõ¿?ê„lpR@EÛ*§CU
+Eä§/…
+bW{ÑúŠ 8d¤µØr²ÂÙ±ÕBý! ¦ü8Ÿ´C²lÉœõ¹C
+ŸˆQñ-y§[„H²b-R•,…ŠŒWáF'ê(Vx‚5oƒ"yˆS]"K ƒÌY`ºSÁàHÅŒ¸O¢ðÀ’BÐ9>A”¯öÒãÑPÜùÖkÌ!ú$£ ª]B<ÀG*=<*¿Ÿ#v.4ì³"-Ce–èâ«|γ3¿ iý¨
+°I¡;”ÉÜÏ Ù¦J}^nV:J9Ö1X¹…c…Ú
+R¢n±]”œ›RÛ\f˜õè¥'$mÚO"ˆµE~ñCh¿ú£UEW–:b !Hê*æžCœtVífk‘ˆñ[ì<Ìtåjp Ìć¹Fõ˜HžÃ•¹³Ÿ“IôµIé¨'$ëÐiø=éXTsZ1"Õ3¨O•4ZöúqûÄ…Œ­%èCšÒ|pêµ€ð¬¼æ([Ñ)„Ù´kc^܇fB‚ŠL›ßDïmüÞB|ü*Í_•Y³y?{‚”ò‡¡Ñ¡¨df÷mx9 9£ÑWdâ}°c’r¶‰FÁY€ Å
+Ö¸‚Îð­ƒ×˜á:t€â$›Â‚D*pMF ÷37¤°Ío£@Å«Ä Œ4¯!˜RšËlA2µDªgèS¸"õlŽù·+½G¼¹Ò{X2.Ä“p‘ˆ²Uì0TÒ¤hÊîäGå@ñ‘Ô’e<‰.AŒ3çÐQûpÌ‹¿‡¼p⟞ôô몟y“G§
+¨ä÷Q
+³>jÔOZîâéŽÃ(LäÞ¨Þ”†fW°6ÇÓš5æÃmN¥Ù°ØôÂu“„&@»fLDÀ/èXØìA€ãU yvKPD³”jYœ38’é’æuQR×ï‰Üâ”n@ŸÆŸ ùƒê¤¿Ssì ¾3{Ìñr# Í¢k‘°Ò0>“…«svfTËpqµ÷ò-d³3"¡©bŒj½#z–«‘~›éœÓ¯èlž9†‰_ð$B #Úé¶>X‚¡ƒù/o
+bpD•)u@ƒüŽ~‹®n¥™<56h½³3ýt!X6âMÿg¼Lr,¹­(º‚ÚC5(° ’C£<¬©W`X#ièýûœGFÚù$,À¶ ôýAò5· „€Ë9h¶È"(ùŒƒ&a 7çÎpá .ë3v>yctF´™7¡¬ÎNý|˜Ð_˜“ÔÓÎ7÷aaIóY©€`ꜷLŽàÉ÷Ò¤<O²/ßÒz±ò¥k b¯ðgñÃ/z5}e»£$åMÚTüíG3q6˜J¨$\ÝÒxé.PÚ»™¬boSs7¾€¨¸"#ω’4ŽNÅœ–Ó$Ä »ŸNì¢=y½÷ÇÆz!dE&„^f$Ðò°¼4sÁú 5#I}Ë|
+^ f¦jHâÏÇ£P_ÜÄ% ðÁóòWNT}á¸1ZÜg°D†Aˆb'ªÙATh—† H‹íLµ¦cö1ðKµº˜¦ýô«kT2n¡Ü¦—¨Ay`4 ~G÷Úd¶=éîbмi¨Â^À,Mßq߆]‚§j˜÷Qe¬ˆƒä°½Vˆ
+Ï"1uH)JˆQ24v“îɽÐV
+¦bn£„M£Î7EÔ:ëä‚Ô»|@ÁËÉJ/Ÿß~ÿ–¿ÿý[ÍpÌäÝS»[Ž³àhTgËq>c.è9ØIÇŸbµè\µŽ³€ãÕ[¸:mÝ¥ìZßláÊB«Ž¤%¥Ûašê86’ûÈgËÏL§š˜
+ûÇŸŸÆÓA. ÆKzOnµn¿”ýFˆeÉLq‘“õvüuµ[ð᪸$n ø†ÚäۚÙÅÈI…°°ôÑ_äÛ(Ò"¶‹_¤åx‚Baéh=Á6t?R‘ ¿mà/¨ÑuŽÂ±?D¼'a$é«/ÄàYŒm1ÞÆIÚÈvñCnxûM¨rÖ1¾ƒ,6½$_¿!Œ:aMŸ—AÕ'I’¯Ï²ni”w˜fÅ<0(f´C·/ö- tÛëÕv×˜s²ìè_@4 Hò‚ñÆ}Ô;(¼…dßãUÜfoHZ9
+ aJb„9npèÉ%yŒQ?±ê7Öi- J7·1‘dKÈŽäÛ¼áë‘N/1ˆl=r±Wü%§œ™`h©$t¾ƒƒag`õµöl±xÇúên’ð–ÔÜ eØ -|ÛÚ „uÈ•ßâ(Sg­|Ù: ¸ÜÐ|Ä•i.K€ECÛ”ÝÐeÞrE§Þ–Ê|6-Ô5d“þ•ÍûÅ‚¦­4³<.Ïwˆe戸õµ¥æÄJPZ¯žº”0 ndž¤X
+ÖtªY·¯áç3mÏsb†Ýñ+Ûçû+Ü•Jc0’4àw.öˆÒ3 L̆௧¹ÎA9ØhÖZÓ±p-ƒG‡º8 ¾kH¼:¾sDÿ¡ãýØž<Çæ1 thå›QY,˜2mA…©âîî R=öKëÙ1H—¡”¨¿6=@+û+xŽ‹‡j ¶–åÓyÔ²”îñÔË<
+ úÍ4ËzÄ»gµÁ£Ñf
+?Mý~=ÎÇ_qœ¡‡×Õ>‘-gSŒÍ¬„Ò‡á%L¥EG„‰23èjRx•]ušd¬•b™ÚÇW>Yؘi”¯kÒÕmfNæ¿“Ç~/Æ ÉšÿŒyÆÐKß ’A¶3\4Ës>rÉ÷Ù Rb{4{~ƒ¦Î` Ë úT—ß¹°ˆD<>³î)%×ÒÞÄø–8Jg ^©Þ²ÏJ]ŽÎç¨fѹ/6-ß[ƒ÷Âî8ºÙï°„P0}Ž‹“‚“Ž-Í.bö6ªk>ê±õ[MÊ2Ô½ÃÌ\’›źûIm.ÖK»+¥e4Úų üb\åà r(î`¹¸ÑNÝ4v-«Uç#0
+ q^ëh šEb;ûMLðWkͺ=*>à‡ ù"€¼¢iüsš©3ÆÔãè#ó­™Å–䀹¨ÌK°7"ÃÕ ‡qÔú³Í{ÞºOnñçãj6Ô;ñ—³›‹(5¼?Ùê $„.{ŸýT¢n[û¸í09)ë@ ÌÂXˆ¾ßm¦¹ª¤Mš8 ²½$tíò1ó4@¢_ã _¸ÊÃÎÆ]¦Ï8\8
+*a€ñÖäÈ5¦ÀöÍ}e£%ߢâyóMÑê ´“þèvê)ù!òÕã ÞV¡ªqFÂc´ŠºøïÓ[µ6‘¹kΖ‘ ¼ÍæÆru ãWÓžPH‰Ó^²íÚ 8Öø1[Ø<K¸j¬k9—i2žª°žAI¸]‹Œ[È·½ÐÝ”üå>Š7"JH7;s<Ã9#„¢#Á[†)èƒîï3Cã\ŒJû
+Â:)fÄØy^õ
+󮦸ì _Ow>ú´7Úh´õ2£Ž3ÌEb,êQwÛqÚU8jS2_ØI
+Ñ¥ñª*œ@çà³6}÷šZ©iHPГeÙC!®oÙ¬+j×tTõ\¹tS¡¬ ?N‰|Ô±ÉlKûÂÞÖòó .îSÕêÅAí#~ áCLÔÝëøÒŠ÷«Æ¿ò
+Zâ8Š
+ÈpÅQ:¬á8Vò /°Œ~%Ž’ü›blL(ã4ªPŠç˜ GÀRdGåçžÆ'ðV†ô÷o¿ýã[úþ·}ûíßßx;A È„Ë'«pÑIÒ$†À@~b@½‡™)^@½D± cCœ_¤¡q2W,^?R]sÐ`ZU¥k%4:sƒä Lâ™öI“QåMø‚zmH’ýäUÖ†}G2÷À¾B<ŠÈ‘’Épö#òþÄ2Åuu åñ6@†¼·mwôô*ÓBeø*¹2dù­8 `µ
+e–UÇc…Ÿ Ÿ:uNzaŽM¨e¾Œn w]–çGiÕ¹
+‹Ûó9ê½8ÆörЉ=–x%˜ˆÌ¹úúÀžˆ÷v¿|ã}d^îñ4zoïyšà—º<íÁKuÿï>¹¤zZ
+ˆÕA­&zY2ó—¯Øº' ÄÍy8gÑ)ßdÇ/ÓDÙôˆò$Y*“Ó3ñg¬ `1Þˆä\aå
+ÊqL“ÄäÕHXï2­/°+qªÔ–&zíÚΧO…k%åë9L„¶7õË#
+Ù X+ Õ9ÂΣØw°ƒ^µ³ Ð#^þí :@ˆ\Cµ³
+?-[„YVñ™ÿm’rå(ã*ÝÄxU»‡ð@‡EÙñh
+´ryÓÏßB|÷Fé2ôozwä¼¥”ö÷V¹äU2ì(08xÌ7Ö¼#Þk¤‚Á›ØB»|L ¼QÉðØÇEiª€IÐ
+ÖLÊÐþ(µàᮄ[vøÙÄ&‚­(õ Î¬:ðã©X¦¢ÁZ¥]B”ª’ Ád
+
+"q
+UðÞùFº—OlFA—¡´Û-ÈFpj!$v¤R“!^™bÞW—–Fwî£<Ñ$Ü™N÷ó¾óµÍ̬ÈÄ7DÛˆ£A7Ä9(ŠRd™ˆŸ‰I“äWRW×z³Y¤n,ÓíÎÀ4õ‘ KÓC~¸ Ù…±²íÍÔâ.3²ƒ ‡KÐ_(õ€A‰»L+R²y EA/S®6=ËàlyÅ8¦‰Z—¿ñ^¡üw3mçLñ+J @OcÜåJÏ ÅKë܆ãjñY¦öxvRZÌqö"ëV‡vû,)×ÉU0C=Hž´jvyŠOòâÆÚnÅáKB8,‡P^ˆèqcC‰™u ð
+ˆùX
+Ý!QI±»T );€ß°~rt‚x ýœÀ§0CÒŸ¾%²Ñ®XÖ’à³î/B.ëÕ˜—ïg–ÀOε/çÐòÖ: ¹¥Ö<² îìäJýF
+ËUܱÔ…°¼ÄEÔˆ­Î ¥?Eý„ í FQôê) ÕÐV=Õ”š ÿȶî’"“IÅ8’}· ™D
+ÍÇqln— ²]¹‡h膸¤•Èô`J³ôÁªú1(9N³À„*ÄXHÓÔ°Ȫz70õ‰Ú£¼ä
+”&%ê7,SÐI@¨|²Ÿ ž§Êz­þæbƒÿ“ûI“8"̓Á*¤¾k°Â
+ö˜DÐóZîi!ÒOkd´m
+;ŽkF˜ÿvÐmNËAg!ÜUe(ݵüñK‚Ô@‡jFÿAÔ={£Nfv¹¤u ñN`A`]XÑ~=‡Nd)%&jçynS¡l.Ws¶-Æùfë©WAÏðTIíráù…ˆžîºçv y&¾#%ŽW;Åap&ÂH
+à]ˆ Å2¯4œ’¦ÍNÍÒ s]‚o‚&ÿ:ÀC]²£2$,d âYÔ
+ö¥4\¢¯xœésÛ¸jûP$$v<¼ ˜¶y½`a£¢£­Mš·0A¤†¢ýáEÔ½@?åÉ×y"u‘V¤
+t†’
+ 5|×÷ŠÕDVR™çx1mÒ)èuzGÁöº„Ø"¤jÛå­îçHðI;çÊI†•ZŸwˆ®b‰SêG)ïæj.຦-ã¡Ð]ÌhÉl ÏÙ¢B€šÖñ@§™Ej§–pþlÆ…éíæHõë¯K‰"@R„Å蚬¾±šƒ¢›ÌP€Ñ‰«‡h¹ ßB¹$!B(/åcgv
+ª‰ÿ*%º„x&áÏÎ[
+y„“5KÁÐÀwá°ØCø§È{ê$c}¦hÓ‚†qjHÿúJdT»ìÄ% ;, +9%¥{ÑíæÖ E†NRúvÑaïÇ}«èŽËõ^$†Iž è/.ÙŽ~í¸ƒÓ‘dï¦9ËœŠÀ×hï/!ôvnìéT •}&ÿ a•ð¼å8ÂŒâCñ 5ì!{„Þƶbà`t0Ê«ùN¢™°;_ËÞ €§èõ®^¯Ëø=7èk‰NJ¸63±{óü™Œ©¤‘=qð&ê—§rÏa‰Bz8ºaTà 0Žñ„Ú^tI!¡-’hð/›A….0ðþ¬ÛuÀ§-ô7+/õ|zø[Åz÷kûn¹@ˆÆ´Šd›;ÌÛ$eÅ FÌ$²>FLòÝÝǘXˆxÇ•ŒU&†a¿íS«Ž" 6mî“þÂû%¸—‰C&Y¢·È PTrõWôúý¡ˆq²Îh½Ü1*+lsüâ_ªHû2q28 «”e¦ˆ©¸¾}uÃèuï—èÍF;p—¥mæxµm‰hQ!ùÆýçÉÙ­© IxÈ$ú΄QMù¶ç§c…úÑ\ôªáΛºFo„(q†»A¿Ì¦™f§±ŒRâãP–¿Ys÷I*¢C:[âEHP꺡í]¯xBô‚&³JïóD˜y¿ ìE\”‡.¥Õ¡†Ç‡óÄlí(r·aœÄ’+v¦q‹ãw
+¼vú p0»S<±’ÙˆWuQ_XmÓ‡ Ùb–óöPÀ`?_óø…0Bš‰·YxY /Qšåím]t—”*§¨Ã#Ľè
+“ºªPB y- µ¥ÉA®·ö¼Þ ¥t Q…é|ÕxM%"@ci'ap$[or²?¥+oèûŠ==$“WFÅ£AvزxÂZîÒŽ©].”5EY/I”ldÝ@3Ÿ©BȺãTÁQY,´VP6œð6ø=ÃoãýâMâ;)”ð7Üöó¾“á GrýÊbsŸôî[4ˆŸõuÍ>|F']0%¶•I9°ºì4/¹F‡šz÷m¬/Ú$<~æoÈ]@á+=¼[6• ö§3Üß!£ØTŽC‰ÊÚ8ã¨{Z5nžü;P5-'‡Ön/!€`Ê2z87͘ºÚb‰qÁCûØ^
+–|Lœ(ÖvYJB¹–‚g3:z-ŸŽüò ,ÒÓÒ"ÊÄõ8ØÛ¡ä=½ýý¡ÈñÏÒ=½!ÿ¸äJ?Ðƺøúä÷¨…EÔs¦nÈq;+³¸‚ocË@‚ ¹èÙ´0µpp»<»4Æz<.,õ„¤néÆu1¨2§U’è æ‘q(Y£À¨&%ÍwûŒJ|lÑ$··)(ö„¿óæ”&|Æ·K ”ä«>9—¯ l ¸o–¨Ä¾ TöT”møO A‡?•L‘ .¿èCgi Tƒ¹T>=>uð2 ÀN»ÀûÊÂ+1¸ñÆÞ_sáô
+?•çÓÌ]Ô`³P©ÝdŸ˜¸ïGAøüž·ÖÅZ*R•ÌV̳DÇ€^k‰
+¯à¯â$xS<Ɇ
+î”ào«Þ ƒD•Û¨Rã<¢„ëãÙ¦ká’,Bi[„2¯£ºÈÁL+%(Ôò” ’Ó¿ÓQ ì}Q’ˆŒz™é;§’î–“hû¨[ÑëféÊDäØ0&[E ZÓâ0o Œ]n%8 Çîvyi 1[Ï?‹o¡ èkˆ›þƒ=X¾‚ËK'ΡD¡ÆV͉–~®#ìNïçï° Ö58_öeVK™˜Ñâ%Bfâ©ø¸xb`Req—§®Òä«l?¼@¦d¦«<'Â1òQï%44ÑpÁšÎŠ,´šhpVÀ¬¹ŠÇ"¦‡yÃŒ ]u­¸dˆ“±áq,Ž hôØq: ©·$$Œ
+Áþ£Ç£DàÊ‚ÁÕâÊøHRlKt¤|(Á::FCç>ê½HK”³ÈMaq}Ò¤£ò3vœYB¼8«—TN±G
+:ÁMÏ!å?Ž¬5YçÇœµŠè7‰ÅtøꎧóÑT7â#
+!$¡2kœ f´g·í£M…*¢ãŽ‚@E=˜”¦S“Më;Ljb
+±z8y—Ww)Q9LB@-¿Š6þ¤1*zß¡8K|ªÿã ®·€š—j9g+§’€…øÖĉΧïHž§LŽm+$érÁ¾zý§{®‡îÔm…ªœí56,X‘Ê1ƒÓMƒÑjÈÜB:þ€mú³n¾žrf(,ÎÌN%Œ
+ùH*H짳$7i³"e.ßúVôˆ"ÜÕæ+ŠP~Ž¹`ˆ;ÌŠI“'T]‡ze)‚åÚN4šÏjuLJˆœúvi÷ÕFMñ-m/Éz‚®²ž¥Á`€ä¶›| dÄqÔ}œ/% 94¾Ã·§qþȱ&Âaè)X„rvŸ§/Õ¡ ‡Òóý*n,¸.`où«‡0®ýtÆϳ°,6ÖQoVS ÄFâ… ‚ÖN%oŽõ—ûÚÆ ÿe¼\’+Éuº‚ÚƒWpCÿϸްvÑS{ÿÓ> ”îç3ê:¢Ë†• ‚
+2%ªõ¥ÁØœûæ*`LØé+cU\G…HÈÔH»¬|ºÃCqóv
+¾ E#²”¶ŸK€~(äèÙƒÜIúû—rè^_˜ˆP•[|äŒàù¹·@7Yp§ëúcS0È:öÞï³p$ZŒ'FØ$ÆMÚ*·5‹u­ˆ ˜~Ÿm|ªZÍ Ð"Ì×B€gß·DWÄÒ`p!u¶àn[–1Ä.ìc`qVk_ ¬g¦9q¿­wI£ÌdâA“A"Á…M”"ÈúTàï±’ÿèž“¡o"ØÁ‡Ð³{›ÌÄ5\Á^–ñð*@AYeÈ]u¯8ùÕ-‚Öán…=ÈN­/ í~ÜZÁ×åï2U>ŸqDÙØQî£*êس¢ ±îOÝ‹Sá
+¶êY­º·Ü9çNç6w:¯:yLuX8¸ˆL@XT?§¡§‰ˆIüÌIüuª~ïá3mÕQqGÀ¿yù1KYíºcþ8O5ŽÝAŸïÜÈT#âÛ’8•¥•É9ëk2JÃ’Ë9îÔ™J'àqY<â§aضH{éæì ôQ²!,dŒJ6ƒ_²øêeoðÓÔZ潂=>´ÒˆD•KË,JìçèÞ1 ,)vq:uß@l­ŒÒ„_´Ý¯A
+­ûÖzÚŸÝRƒ:á_JºQ©a„è^%¾øó:´ùÆËo… ˜äaÜsr•l±PÔ–~]zê º¦Ïì÷8W$%&Òsdc/Áãq0½¬cŒ3ƒ8Þñ5i#ǯku n¤`ÙQDS¨ô
+Øä¬ñ®e·o†¦á¡²Í`|)`±iNÛF•Î’{Èf$Q«a„¶Œ-áã°?¥D"åËÑ,KD@ù6Ãõ¥³ëB”pöAñvú¼ éñâ=c¦æŸ©T‘ò,¦«hiÍs£Ÿ=©kBÝOb:X#ì’8/¶®Zò‚ºAZKÓ\‡CR‹±JÓÂU`äw€–÷õ©nê cÚ´@Èá 'óXãÅòZÎà± ñµ2+Ov€ô9ÊÙi³Jo¼8A<_qDw–\ÑŠÊîí^³@”J!(%ñ‡L„70n˜pN/©dJqyù¡T]Óg®jb¾(ívŽÒÈVqÝPI<ANq
+FÒh×\üTf SK´ò7ùAàc…—Ä0ihêî)¸ŠFÜ©Ê&³¸wÁþ:§Q/)=¼‰ä–)h€+}øçt&‚ð1âêÂ}$›¡©èeqõýÄx SÔ©™
+Öô.ÈÑwt×wA2ì Ýr¥äé;ÌéÌ_œ´Ý×÷¨HÚߘäé»zA®Ä›™À{ÃŽw‹Z׊úÄ
+~ÝÕ(æ`„`
+&RFÅ“w ð¼c§e0†/ïâCù¯4OÞÈ9ð29ƒìÉ» L)®¼×¾N9åýygò˜9‰AŽOvÇ8â~bm'@ˆœ ÎÈÙ²Ô/°!Z ΊýÇ$ ’I’‚å™ÎC’DTN2L_ÙѾIeaЈ—³8”ý
+±˜´>„ëƒ ÇÿáP¬
+\Ýž=À.ˆ*„˜f =†Yk¦¼p"æ:„Y$¨ €fw<üY¤A *µcÛ÷ñªèèôZÇvn²":ÍÅOÌMÖ ÷¸OáöGv¥½J<?¨¼¶ìÉDÍF9ÆLvN-Y»ï1’Óº-ˆsž”Ḳ„QÙŸúËdZþ ÿN4äLVt–Ä—zŸT“2…Ë÷üÜ$Aa'ò3óªÝå]³žum²Ã€lµ,Ý[#”’®kcé™ òEG ®%Ze7;ZXª“B¹„6•a0’ Ð Ágó;È˨Çø)¸J¢ègIü‘œÆè€> 4HIHK'o)Dé;#Sìë: u‰cÞB{x9­°Òû+MÈÃJí䯗´ºrUû¶GpŸ¢óºÙV‘Ù5°!C»ä2ÕëçÁxR@ç&œ×Ÿî9x|n‚»IJ&›ñì” ǘ†AxºÕKO×ë<
+¥ÊÚ—pÕL-]±µZÎW %uÙNa΃hÉŠÚd†Iof¹= È‘Ö5œuóý¸Ï cWä’F™çue‡‰ŠZ†•øå<Z
+Óð´¥ýè[í¤ü 4â ‘?œûã줳‡Ä†…ïÙÂpÏá‚H8BNÖ){Ÿ)¢&-ØÒâ'‰‘Vïöúeß4ÈÈãæ¦g—ex׸Â%åSVì{0ïÃÂOBe1<>¼¡Ã¢i.Jc{2ïã{­ÉL}ô"¯ü¨¯yd¯Êz9!dÊh9¶Ê‘$5 ÃóÖ¶©P“Â.e^–—gHÌc=®©ë<V ¥^מítüd{é9ÌÔ'ßOÂþ+Bu®àW´QÖÿš]$8Ó(ŒÇ¸Ì1›Å’ÅQ“ô*¿T·‘Æ81–Q^£Ú10vè0iÛ6œUå#÷è©%B,w7öhÞAÌ/á(…϶góI"ٻą±t ›k&s•¹7£YÛÊŠë2z9¢‹mÈ°¬_îc"¬li@—úq Ì6Ó+‚©›ˆmÀ`~üþ`
+H‰Œ—M’›9DOPwÐFA @®ÝKߢ"få¾ÿvHJ1ÒG…Ëá.•Sü2ÉÚÄoÿ/wîÑêè½ÊMŠß›7:ÜÌëòëë¿_RäÞkDëņvÖân#jH¯ýöïÄ„ U+^º<01´¶Ön q-Í {©Ö ñ^ŠéÐ6&¢ ÿÞj­~E|ÏjjÖuD?-Rï£YmcŒ°n Á‚Öšy · É ¹V1IH¿—Z£Y+ü׸÷Dq:­k"õö{‚jTsöSþSPó{Ï#‹ôèæÔ‹:*4 }½<î2¤íÕÕÆíÏA;k-Um-4¼Û°Ñ› ™u U)1ŠÄÞ«¸ÓL/#|µ¢‰eGÔx‡¬^xƒÜÔ4üÃ:t¡6TÖ7Bº–ÞªÄ:Ë;¹@¸ÑrÑ‹ï0ð7<­cpfSíÞµ\ÍhÔÆá4_7ˆš Õ¬=I²@ùÖŽn )b>F¡Œc¯34 YO­~÷
+Ý5|çñý•±Ñ eo}´ã2q¨£(äåÿ›>Ô›{{Ñy˜àtÉ% *ûR>¼¢6î¤s«XÇ-ðrmè ]ðg®ÓjO1Vï‹¡Hƒõr´ ©Ð¿åͺT jÓùͯ¯æÂÒ¼xm49`ú1ˆÝ
+Œ¡À*]ç:½k ìXàaÎÕÑ`ÚÀ+Û×Ó(Ì­C.©ˆKÇuœA‚“#Ö}Ë,Qù¶· V»‰º ¤äðÀÔ}qä
+ÂrØÃJºN³i'ìÐ4Œ*\#<è¾A’Þ{[Ή÷4Cù°MHŒÊ
+"ÛœL‰¦Ä¾b»Æ\š q켤
+š—]5¦àS‰WHû[Nfy‰?Ðb@5pt´×ZŒuF‘Þ ÓpF»§"'E-={*ºS8S«ˆ“KÛX·‚ꞟé(܃Y¨ˆÊTü¥D ‘=mx68žÒ0½$…/ó¨‰ùhiº·ú‹8W­œÈè39\·ÚóbÆTBFK‚e‘#Z%Ÿ²”òv £Ñ¯VýˆgÔvAm’£¥ø.ùÇ8Q§sTA¢ÔŠi¢:óÜ-!NÎÒÆ2p—LÂÃ3 c,Â;ŠiÇ[Šë,ˆ$›i(:YŽQï’o~æmȪ´^ahm•O1t;ðç± È¶¢l2wÉ´EHAäcä3$â
+g6ˆs\=‚^ã*’¾kêg?eßó*ôþh¥´vΫÐƳqhzÕ#³¿æU ¤sj>º­2]r&|îmÈ”Ï7Í[^íO7ˆO
+„<ØðæÚú9°f‘Ø™o¥½×Sd¥5k—6í§<zjý5²&Ëi±r 9.CŸ Ÿ0÷·.y€Ó8‚oÓgßßójÏ.—\¶—5v/yµSÍ’Ó AÛ)¯&„¥ƒ$èe¬—¼zÀ\óêôšWóÄ‘))ߦUN5oN2tÁžp£s`Íúå“€¢ýaa¯5›<E~ÑNAóè–ï5I!Qç,÷z ¾I-âs›1]Ÿ§ùÿÀšen“M»=!ï5©Î‚P…Kù)¯¦\ ˆä†òêAQ×¼úwmÎAÙ• ›û‘“t=OÓ,“`Ë?%|]§w7ŒÁ a¼ý’!dºPlÖý}ÜêG AŽ‚„V\fP¸‚’ˆ¤UÉâ&õÎ/é Î\ÛíÀ’œ1CdB°î†Afö*¢ž9ØJ¬ZóV©¹u%h—MDMçâuÃ<Këµ{†mâ—4Æò†ð+iØú„+ш6íY[%é_füOëhzˆ1*±‚9¤?ð‚i·K#P8©¥À(įk¯2Ôæ,‡CxV˜f#1ðõ­.Às@Hzë,T' -ä¨,j§ù€(£Ó¿
+Õ0záz|RáAÝ™!uìuPâ|Bê|Ã
+|œÐ®çêIãûSÛðî<èõþ0YÿI>›ÇŠ^œé`Bx/,Ä°!ćCò¸½q(Š½ i•ôˆ®Å×)øk±î Z úÙˆŠ™]ÁIœ‹ÜÅ;zRèèH¢'g³Á˜Ix
+ÐoüT¥œhì_ë/ƒo"ˆîGF‘0°È{¶J+˜>ͲxlÕ[þ÷E+¥¡N¡üÍi‚~YËÐJÚ@5Ç RË`¤6ñ†yO< <Pj.MG KØ)Û†QçzͱÁ×G§´•£ €d®1Œ8×t ãQA#š™?U0£¤LZhиÕÀ$QJ$ӫ釚1†¨ çÞP\DÔ`BôëÖPó²(u[„ÄÀ$Ŭe²ï`F˜4ž “=ÖDL Ù…RóIsNM?ÈèÂ$T¤›Çu/^9qc«·,…Y—êª(fx±žÝÓ@0µ€;–ïi¨Ò—ÂvOUôgF¼º­#»B&£1a+œ©³q!Z‰…·€{ãæ9\6:Ï€ ókÍÍžøg5åˆÜ"KÞ皊Í`oÒl\oÌA‡­q_îQ'j8™à°JžRPÓ¸7‡âe%ÓÍ“$@vÖNl¬”‡¼øD@
+çž<bム%+A¾R,üplƒÿ­d¢ƒ‡iŠíaøUÒ Üëð_±×±è/Aò X~\P‡a½ê˜@æ*üKø•ØAÞRT_lT
+6vŠÒ37 ‡(4ReÒó(»Y˜Š!‰E~œÐLÌZ›Ýû
+J4—tFþܼë¡hZ:`)Ø„Tù~(E& ZUfÔ‘IØVÀ²× ¢«ÓÝd?)[>.AÄž–Ù5§$roÄIðþ œf“?þaÃ"/CF€9ö¡ I‚ @LiI,) ûÏÑÌ:E*aX€¬Ls`„±ëèF¶
+ø;¸•¨à;i^ñ]£bׯ/¨‹–sá—‘EÝÃ*a&RŒ2Ê´ÈõŠDÅø!ÁÅ$’˜&¬Kìž
+1˜13ßxÎf¿1“(,PŒB¬¬ŒýŸ’Ã2ӡ轞GFKL—Wï/E*aT`-$·í1Ÿb$u¬Œ´f(ƒ’nWI$CÉs"9ë}
+WM|$Cy%”Š
+¸‹ºÅ–h"”ûåÔ‚tN>‡Ã;#¤ÈÂùÕ“Ð/Ž C‹/È+ÌI‡¬¼ãÃØïzXC¬dFRÿå ÛÔâ$Xäéì©/"ær$¨ç5Y%ãäŒÚ,Ç4t†4o%R|ŠT¬¶ã:ð¶"dOƒ½dÂ×80pa‚^yQä† yCy‚7i²od$èHýÎb t~r™Úˆ_ÉÁ;¼0ƒMðÏÉK@-óÊäívŠl™p°^²óÅ¥È\<)É‚œ
+îEÌ&‚À­²['îÁçŠE B­Ö`LÐD°ÄW]Š1ü¥
+‘_#x®ÊVw`ÌAc•ˆÅ0ß4£­öÈþ²\|´cDŠ°ëÐ ÿlöj7&#’]ê.)Š˜™G o%+‰?*iŸt×eÀ«m-
+Oì@ô Ú>Ã&'GÐëQïO ~ìÁÀNÂÉ Y9-{¾ìs!ƒ\ íßãrr=„å"•
+M†<ã†TÔwEüMJÛèš}Ð@HŽØH¿0güòötÈL„Ü·’‚¶2”R\b ~»åᔬƒJÀ&ʼn«éùá;üµIzÁÛmpyM’Öƒ ̘°Nu7¦–ß1÷¯'ávXÕÁ‡}/†ÕÍ‹IY]þ
+b{Û754
+"Ìå©v‘–J3–UNÇæ¦SôñP„êê¹ã[°Î‹Kƒ.áóý,Œ²ÎÅÚ€aÛîå¡…u~K}6á[E)AÒ\½ÉÛ£às»Ç@š^=¬°¢ä
+½ÇðiþØ`Ñ:äa M–=Ø!k“á4•}§*ÅQeß^Qc ¸õ%l”ȖЭ¸ ¬„$â#’ÙÞ¥ óǶWütÍÉ)z(™¬B€Þ9ìcÉ…`Õõ–Ž ÿÍŸþâ¸ZØDŠ›Yäo†Y2¡1MÃÏd°ÿ³QÐQÌa‹ò›»ÏeFìà2ïeØ‚j¶ÈÖSÿçæ(í"/ srLͧMmêJòs¡øWSƒ¶É%‚²a, †€dQ£•dxʺßN|ÁL!·¼v§— >N€æÆîØ]ÆõW`%›Ôi»>á–DH®G?‹)†S4þB]K$—whônÑF¼Æ:¶6n#Ò3ž 5®—GÖ‡gCÉ¡³õôWP›#`'ùÛJ8êzƒfæ¹wø½Ãä ¬êÓÐ[Hþw’ÚžF†™Òò|’ž ƒÌUQP6”²È“l…c’¬-Þc,qJì;b`Щ›Zøõ‚ ›Ò(û¢'6à»i·Ÿ°[Ü2ÄUÊE¢ÜÜ Øø¬$‹;öº¿Ö`R;ù¢Ióç
+£wYÄ?ðÖI¯(Æ ^M'=`MÁ‚M ¬u~k"b»Œ &«V ¸¥óPN;y! h¬bSgbàû0J8Ê’d£V¶,nR•¸MÙ%
+#Þƒ$rwÀI²JB:lã -6Íl§` Ÿ"`)’",²È.Ö¾ßD `ÃAfÈÅ…à”NÑ&¾ªÃ70Ä0Laô“¥`dúP¤3uS<2ÜPa~³úÊ}3†uXãCcÌ”K‚?’éÛæ‚Ɇ’¡jI; Q±Œ8lˆåó¡(E9H!¨q+J &sv8Àm§dâÐOþ(0Q‚a<÷Tð–]¥~Âo£è_1§ÈÐs”™ì3l%¹­3ÆS’Õü¬!‚äaø5€8ë]¡ƒôˆF¾bFfD¡á8pRb¿LD1Bã¬$vî˜}º@óÉ„cñû]4Ù6„$)Du£÷ë,Á%GH²âŸuaNîL¶Ÿ Øa0Ô»‹þ9[ªS²:¬ë+α†ÑýÎPê‚$6s•$»‰‘·–×,g: éì9ꣵË[ß'I„§6t–Í;d†€àà$ÚW„yOõT"f‚ÑðB£í“~
+Ž?µ½» ö›šç³·pž¡A°Õs>j"âšBNžc¹‡á8Ÿ{¨÷w.h˜óA°YÅÚx“õ•‰„$·ÍtTÅcCfxJS–•}Å»‚ÅÀc‘Ë|¨hò¨];ßæ>é‡Ý²<X²+U ì¡C+˜H)ù—_mãW‘3ùë´—šßÜh]\–ò•Ê§ãJ®‹G) !Æò±‡‹_EÎůӜ‹ÿx£uqyš)¯Q °¸>52KŽ
+\(Á‰¼ l 'Ò
+P‹÷W»8³ø3þ©² ¦*}ïç&ØÚ”“•`îdÑì:œŽ¢ÆÃÖÓ¡ãÃwX:ô<¤öt›Z±1rÑééUá84Q,Z›NsT!·Ì…ÈšÓë°[òmRû¤«
+'xâ¿"fØ»LýKÂ…YPä Çæ<ªò9‡ +„ºzmN•CUßɸqõïµÅ”dÙ3’JÅ{£z)YG½Žüõ;p^oã
+oåâsˆ1”þ ›[•;–õÍrIoW¿Ȇ£¦©¼1«¸™3Ac•0ž,Ÿ2,ÕŒÇ!±R+:1ÞŒ š±DcˆþísߟôwqH“I©5Öï
+^•7¬j2ûWÐ+²‹¼
+)sò¨•ãÒü!Cj…4·ï­Ñê>Ç-ž¨8™56ïýräAa¨ª½9'wãj•°š"æP*ƺLÌötH¢“üDI3ú 4ôeXYÿ¿ÄÐN­}?SQ`ŠL¦c€'0©e–3ü”çC
+’Äë„þý×suLî u¡OŸƒ.ë÷øÚ'˜B¸îÕ+ÈÉå?ÿý GIÓÐeÛHÿã E%I¬}i üýAbúÏHÿÿ¼
+"ëÄf¨œ–pÉ’F-ËÓ ±H›â:ü°*Õ`%†³P Œ¾â =_EšõZÛ_âA°)Zå×½tö‰áFÚåº ¯Ë”nÌÉLnÅ¿Õ èÛAèïT¿%| ’ªÉ‹¢°ûÒ
+ ¥öï2>@Ï
+é>•8£˜‹ñ½å¿+HŸ«ä=Iºöéû
+bÒLŠŽwÑaËÄi+¿ýàEdÝ‹ë_“9Xò3¬kpK›´Y9vv„Fòþ5RœSQ¤‹ý¨íÚ ¶g±¹rEcwå廃พ€ß>x?¤X*ç2¯‡Tw_t ›Ô• ¼ƒ›åúeü.FÙ'„iêb"¡\¶ÙNa8CT‰$~ ¢@ ‹ÚŽNdIIbD÷›Ÿ³Ž+kÒÔ›ûT<@·†Û郑d¦f›Ÿ‚ˆˆì½\/œ0ó¼ÒŸ˜ïOVtor’(:< öHÿ¹aX._£‚È¡É¿Ó©š|Ö« z‘¸_·¯}‚yɑߌؖ1+¢Ru»FÞc·Ê:^×DÐ$·Ã@P[rb®I\J€x˜-)…@H¹X96u
+‡yV–MÍ~ —˽бø/Y©°ÅµÖÔ’æøÙŸ‚*©'nh/ÆíœfY›eÒË€
+Îñýî—6x¶ã®U‹êu=ä‚û ’K²ÒÛÉ€¼X)I¡4Þ…6Qs¶k¸¡~ ¤|þØê IµïøƆ ]A‚ÈgÐsgj'$5FšbÌ5Â7TasRZʶ[ ÌŠDJ‡T­ŽìP2Ühû&­ÊÛòé^ÇðAÂsØ ò:¢ÿu]f=¹9Â!ˆþòÇ Ëú=?÷è…n»õŠqnÉâÉDl ?b£Ø/ì?ìâ–+f@Ï]v‘ ë”›hO£’²?‰Jú!vŽÝQ>­@µ¤Åd“û‡
+†ÿïõRüìP›«l1~‹EFà–Å]X
+Þ䀆o]F›“q©@(xr
+çÕDÀô°'Ò+ÛÃGëª8Ù>£0°M(U¸É×b3ƒt¸,’æ2éI=»>‰¼tã,÷žå¹æô} miti´ª¿KI³±¾íØAR‚MÖ×ä¯|ÁU…ªá³yw{*rHÌõ1HaV"Qíç;
+jküŒMwUfJò¼öó²?ÅX`ÒdªÏ}^Îérép*/IõP{QãñsnY•~"—41^¥x‹Åüže”ÓRùâ £°#}ÉÐÔðs˜$M-ðœÃ  ²Šì¯½,ÕÏøáó©WÐÔÒqÛ¤Î~uC.††£ÅŠÓ5\“*V7„æÁèŠó’j‚"Ùh×4ËQ‰žìÛn„‡¶'iZ°’¡
+dZty·SÁÇ:´ÚGnÓÄ#È"G¯ÄƦÔÉH’
+‡A´q{«>ã©+ ³&,æŸSe^H2ýE_¬ÛÄAaHD_“7'é
+É{HÞQS „"éY¡sõBÄú¿Éµ™&è;rV}®õbî\›Ù*†ÀMîÝ=®Í$ ¡å&†À½pmþh
+³þg5»\›‘R­FX¦us¹ÖèÆÝ.v½¶ö#»¤4„°uò¶†”RX-€e«ÿlÑ‹¬S™k§ø¶‰&½V®mŸÙ&~‹&A§ˆì’-;,b\ð‹¼¶"‡l bªÔþÀvsÉ6ëܯB¥¡u—lχl½ Ù&-±”o4y–K¶é32+y¶zñúɶԡ#;+QñØ–b‚+7sגˤ™‰Aµ’¢²ú×æv2]€ç,ðàÚÉG(ÃeÛÌŒ#£´º ì…m ÃjÊ•RÝ‹Án ™àˆ¨°°w‡;Üž!Ü2œ À1Ñú·gз„0S˜¢‡]ñ;Ü2åRt¹g«/l›?4DRɲ¹l˃GÈM|&=ëé°­Aa¶Z_Î{™XL¬ÕMÚw¶¥»‚èyÀ¼_ØÖð“³?õ`[›Ý¥'*#.Ûz!ÛžAÛBÑhHìro¸¶5€S6)Þ[*žlËÓE Å芽=ØÖDY ^)Ô´¹lk¨)åÛ4Ëxa[º ÏUd.Ú2WhvŽ˜nhÍEÛ,a@ëš‚â Ú2zLoä‚XrѶLÉÁ³•°â¢­r ít¢m™†&Z¢zÝEÛ f•œ_jåm¦F« «â!—m{=™Ô.¯¢µjžËŒÇ¶t &L´+ËÞ9Qm=¬Ãe[laáARFza[ íb>¼:n4»³m‚=@
+røF?ØöñØ–+'9']k^̯+FÙc1b…Ê.Úš(5¨·¹È¾Îm= δy¦>^ÐVIÖ)ŒcÚˆü@[FÏÄj]Žá#©WÏm‘[¤]蘋t9ãD,Ö§°osG[´èHÀ$ùmó¬eÇ$ªK¶lždû'fdûã/>Ç(у`sÝÍ1€ß3ˆ+
+µ"BÖ<—T9ã´²ÆØ–Û°n‘R*Ú€…ðCö0(nï«3Œ¡ þÑó Òv ÐU1o—MR tEî•2ñvÞ|†ŒýL€KHÁ²lrÔ†Eš|9i…ÙÍñ#—;ÂÛœ Ÿ/Ag9¾õë
+Ê3ùðiYnƒT'uÕÓ‰˜äPàR$tþˆ47ÆPnƒ@0é
+"eÿž…èÐ-jOÇöbýhP‹Â<ti‘´¸ŽþÇš«=ÂnòÁõÒŠ'd¿g͘U<`Œ<µ›5ƒtá²±ÖÆVáFã\p-u€øÖø@4=¦®Ö‡rMFf‹óM(6]Ø©íQÖ’Å¥bœV;›Ð‰àZµuùhÖd81-´Å—ó¬¹sô``àMc±áT /žüªGUºœ­N¦IC†–0òëÙ^§uãh| r‘³Ô2‡ìBùYrîé.-Ÿ4°ÞÝsâŠjRXvǵG ~˜ID)W#M†„xõŸ|Nâiºïßsb´Aš,=s0X!]s—hëÖ¾˜˜E‡i”x…@ ˆIcsÝk#[„Œ;\SþŒQ]‚ä|pÉ¢)?Sü½˜É‹à7!y6…ÃF:½”Iha:fj­k µmÀBAÆK&aý„èóŸ-ï$‚qàoò–I¦B+ ¬Ë’Pù¥í.AÁ;§£òìz8±z6NÀõ¬IÕ^É(Þðr ¶8/g¹õ P)½_ú%ØG¹^û©nËMMžt¢¢]÷਴,Ÿ/ úÈ1?™Ç0  *’6ê « ¶€Â²Û}dŠi
+,SU›ÒÕb_¶SÄgÓ"ÌíibÉÄ%êê¾’Ñçº]–IM" d_Ï>¬
+Ÿ·h=(ïú95aâh•&ïY²>ôPÙçô`…hÅa§ kE ¦‰ëií±´ Þ
+\‰·ó6R ¸¨ë˜cOGUNVݸròõXõ„183`-^Ðì
+NÂi̶CûÐ'«ýpZ‹€* ÑÚSÊúжßÏM‹A«,ÐoÌŽPþöc„Uü)ËÌ–I¶5w”M§0 -…bå¢õ“Y'mI#5@Š®-‹ ¡ž“™ÂñKV$wI–QAî?tK[DÎ[â*HNöû” Bàv“¾Ñ ѧ²Üˆ‡rÇPÜcØR‘´
+Ô•Õí0D‹55:\¯ùš |ÎŽî\ç‘|…·°(¸ºC¢{b¥˜­âr õ4Ä«©¦”ãEw2ï¬l’¡5–Î@4iÇDÑ7•²…bÐY7·ÛyUSá±BïøÝÎß
+‘'¿™Î䘻ŸˆŸÙà‹Ã5®Ìï3-ò Ï-Ž‡;žsäÏ9åQ„ÃsŽZžžãõÄÃs¼Îº›Žñpotñ¶B®´Šp·äÚŽt³Bxg˜ Û¾–›ïÂþŠj¥¡¢®ïèêBÌ]]f¾ïH&z¢øtõ}‡M³iÿC%ð–ßAÙ¦ öRYáÜsèAAR<¾ÎyøŽ×ˇïœA§§¤OŒ,…P CýfŒç)ñóðn¶†noÆÃÐ좀{1ž¯jò)ŽŠ®ñD¾d-B•\Ã8ŠáE…ûw)Ž9Aj ú±–"0Éû6ÿ6BªÖMtÌÄ‘®ñ¨U«“̲‘Çx¼†¿ñ0žo ×—ñÐVeš3§÷½8@¦AøØ’Ãö‰)
+Ý\J‹ÌŠŒÚ¯¡C˜EŚΡ伎Tóºå;!v£8ÉŸjÒ¶ÄM ßí‚w3ð‚:•§s¹qâ'$‰ $\J"ü+à ú¥ ò’¥‚´Ú5Р¡gq5 8ŠºÊ:F©ÀY…Ðɼ?à[ëÒj™AÂ~”Ÿúá‰CúŠ¿¤Ä>k$(Ò³órŒ‹àö%…Õ!ܽPbÂaýø{RÉéú’ÃÝd…–43+šù­øEdJœæ»Q0$$ É]秘‰Ò©:ÿN{í ±˜É‡ç›þÏx™äÆrÄ@ô¾ƒOÐÈyXk«[ümÿûoý‚ɤ.6,À€m‰ÊÊ$ƒ1€N‹MyB%ÿÈ—ô ùÐœƒüÚÃ<e˜ŸkÕY ËJr:Á…ç£e†2`ŠŽ0¾2ýÞMº)ò.¶³)?^“³ß»•bÜ‚vÇ|®Ì…k3ÃËØÂ[YF@=Òze)Éh爤
+îÍ#Ù—x oÙÝ[‚…Á\\Ap q‰S†iQ7àÀwÅh0(z¾)¢SÜŽÔ›D·@½âLàœ®ImÈ—UíÌW% óÌ2°Wk5.¤×;,裖•.^ǵ^Ž/l‡(šdGŒÙ‹S­bdzœ½œMÅV‰ºç—‹ýæ©íSèüÆ"Hز›á‘¹ ®kVÛ*Ѐ[$ƒ±¢=ðËr(•]V$K'nÝl·P ¶ÜH;²ÝR,¦ŸÕ/£¤À„„œkŠ
+$FKæ¨ê{ÃÏIÜ¥ŠïgÚör :êÄe7ðÍ—îm»Ô¬%é0Šïx¾‹ó…iŽ¢Ã‡Œ†ƒ±Ëöàñj(‹xiÙÏ%\¯µƒ’ƒQî…ŠðòZ£c¶µÇ‰/¯ØpufÉ´Á]²##³†w&#TŒ$ñÀ âƒÏaiÛòɱüÿ–~lVûTà`/áæàO¸À))æ€ðÎųl78çKÿEæ‡àH©¢ Ü„Eì%ΆGUµ˜’Îk©,·C©&Ï l©y88Ð| :Ê?âŽv®Õ|—×ço4XueÈ Pb· ¦Â¯f÷¯m†³„Ô¶œvè dŽL
+ðÄYM¾Å¥³Ÿ3a'᣹t• ÀJ÷óÊ7°&ö©Tå Åõ(>ƒb7Š¦D4.îEÁÈÙ½Éf¢úöþu‘>—%j[äÛ„‹ HJ&ˆËž-Îr“¥éym.ó”L](¨ø#b’x¢˜˜¶œ‚E§Â¤<!|Š ®çìx×…‘H•€µŒ9¬=çìMMrp›LÜ‹‚æ䬿 ©~{³~÷¢WV~þ†ºá‰[BTÁƒs ,ñä~î‡yž¬ðR-”¼}¾)ªôÛÅ5³]=üÜ¢çog²ä”IÂ0¥Ê׈üì_I\¯·ƒ¢IkðÅì¶{~¹U¸`Jt• SM¶Ÿ7³C²5¼LRE2uvvËê ¨Ž„* ØeÓÙZ9.¼dçEþ »•\¶J 6ðÇ·Ñ=¢€™NÑ&ð4‰`@qܼµ èóM.|h’=­¾dÓM‹<|A¤`c4ôx8‰’|#@`Á¡£)Bh*1ÇX0ÖÕžïnê&1!ÆÌ8œ·™ &¼’çÒ1]6À¿j4Ñ_ ý|$Ç0ª¬îªžry>bͬ(ðWC[êb‰<õ F3ûñ:I²ÇÁ
+òÓ“ü…eïšÚoÅ<PØôÐüoN®ý„°ö¥,%²x„qçì÷î4íI£Ûj”t¶<¹’’ŒÐ# ™¸þxëë18¤ërÅyeŠ(1Nk\Ä©ÐAi´D6 ü·V0¼qðtø‹í( ï KF¹Š|‚PfÒ3_‰‡0·±wkì+LÞêØzŠ@ F© ’âŠ.ä›`ý´Æ™qì¹XúÞN/jàîV/yz†^
+ë]ÚÀƒƒýGÉ4ðÏRúëqeQGRôŠj®ó ^/®\>fôr¢»'§B_æúà”¤e®\üÝ~¥u­ŽÇ¸_]Y`âxæÅ„ºÌ• r÷ÐÙXfϤP‰ »-—¥î¹+×#6*v¶–{rRòêÈ’+Ù ï=Ã9CVÑ6“ŸG¤¢Ì+´Û;Û”¡?-ûÕ+¢S:Û‹XfÈ2±älú·mÙ0¿1ïOCŽ4­N‚2ÐÒrC.AèÍì˜c^î4ùúÕÌ‘ýÈ¢.5žUöÅ‘ƒµ c ZÍy û~´8,sä´äÕ‘“¢wGN‹¾Âp‹4DjT™#{ùêiüÀñPëÅ‘8¿:²9øÊXgoýÌœ4ý»#×QÃ{€ìÈŠ‚<£„›zUK¹±Û éõ®cæŽlo~owô®Ì‘Û)¤ö±©hFæÈJ\m‹m t=uä¤æÝl“³èÕ‘Ó¢¯ŽLå*0qd9ÎÌ‘©%Üöo!õ¹#ûvÆ£‡“ãfŽ\L‹ÅIP2Íl™•"úÔ6’+n#wdÚ"!Ò®óå_Îa¬-RÄX6ñbÉt.X¦©bãzvbÉà%ÏÔøŽø83K®àæbWòëÈ,9à»%žŠOåmgnÉÍ€7®“rMÉ,™Â‹4çÞøT›=Á?‹ièîδzÅ E&bÿEˆD
+… æÙ2EÓêÏ]Ž@Q0äÇ‘ÉÁyÄ\€Œm5•ƒý”$ ÐŒ£f)^‰®X£ìÔîV0­¢”£Ë‚u•¸ ·¦—k—w¨Úí–ƒ_?ÞkôG„Á
+J—ÿ¥NxzC<ò§Š#,mxÞo>(°ÄÃêø™#=’-{"]´àWF‡dàõ÷Ø(8ëg@ÿsÑ0ÈÉVcçUóv¥¯5‡ ¤œd[8ÀörE»©\K)‚ÇIrâ[­D[q©ÝTk,Uc¹„ì[E&K‘XøJ¥ŸûVÑÉA(þ´)ÅöøwVúk· 4BãUÒés Üî¡Ø¥#.ÌêV Æ ­†ÜnÀbB)á³ÖÆsuâêù¤¾H¹{ìk.眑UÂU¢íœÃUöÈò?õ'üÛ
+⟔üŽ&"·@Cc÷ÈÏñÄÝ1{ì^Ù—Œ"JlEüM×ÈƯl^¯ý{ÝÇž7‹|>˃‹•¶Ê׳jä0êLqÎÄT¢;ÛGh]æL«Ç¤žÄjq§æLjt°')÷…]5žÀ (1ÆCÔ¬^nPø.Wبîy}IV!EÍ<Ÿ:)XŒÀÒ:E=bZ6'"È/ÂERò
+Ñ_?²¢w°w:rºõµZ}»è€Éç¾Uô"
+)·^‹–ÀÔŒdºkf‰À$Eï·ì`?ÊI¯÷o½w*ùÜ·ŠÞæ½èM`ÊÇ¢N+í©¾¨¡Ë-ö=×—”:›”ØÃ3}±7m¾ïÛOtAºãšl¿²¸–IÇïè¡»Ci!Âl~™8ââƘéËû$}I‹^õÅÇZ,Œh_j=3ñtOð6­<L4Pp5²g´º,ô‡HyÇ•¥’säƒr]•-S[Sx‰€õT`ŠMᜑgD˜G>¾*Œ’¶tNPûô‚¯
+ó^’(LRôŽöÍJ´#ÛΣú·‹Þ˜|î[Eï
+ógé…)?ÿóCpŽíN3‘a?ol{´x •QDòíQÕ?sÅ—¢àÓ«ÏùY$+)Ý”«à@†;Uv7”4p¤( ¯sú38tçJšp6ŒÛŸ"ÚƒGŸçÿ‹[ˆ•}¸æ*©œŠ¢Ó†½^%v1.hH~ý0𱕹ˢ%¶Ü¤è¯)’S€dkÈuê·"ŸŠŸcã½ÞEaú±Çã•”B0ú~cIVf;~'
+Õ›ÉN;ɬnË1y~tù€S’m4LߢbŒ~“«œuîvS‹î*Ñ%Ԫ綕«b‡¶ê²c 0'%§¬FHw¤cmÁõ¤Áß,*ÔØÜçÑ/T$EºPà}˜ùšÃ¶M“£\s8?Ba  ï*)ì¢9)²ý‘(J Fë%aˆ!Øì°7Ž½ïe7Ap QóG b?q"ö18 ½ 6ǼJÇ^“’… ½¤Íž”ì°©•Å_÷~>ù‡¨cŽ#
+ìVØAªÄßqßÖ3@.\[COÚç&RB}LÜ–TÒDí:’r¿ixªW¶KMß৿èp´£>¨°å™`õš¥±¥î>tn;q=G–¦À2C,™ÕR÷>FÞgò÷g Å]6}Ò6¤ÖûÖáIú×)H µM‹¨&¥<×LÂRˆDR­æóÁ;y‘Få£Q‚bfe"’å푧da”¯<ôw|J‡}Ø«¨×íµ¦K¼¶pÉ#Î.r„Ù•ûÿ¥32¯ìòst/Ýè>o“ƒn‘ŒÌdcSZøêxï”àŒ›ßE­eŸ!C3Α?Ûä†*wIƒ¼ÈÁ³G €¶Ë0·K—0@ØhVO5I¹ƒ<áÖƒ[‘YjmøªEÎi”^.°ñm»·Ë¹®”+Ï´x%?ÙBižS¾h×úÔàîÜì~RØl Ã7Žæÿ¡>2¯gÆURz°Ì>‚fY-ÆùËXAj}&)òOt‚\hϺ®äEˆÂö˜aD,tUBNÁ”d{3åkl¡óìלPP¼àsÞ+®.Y ŽèÏ÷kÑŠ£ÜU“¦*f14[ä£ycÛjR²À×Ȉ—HZYQ|Úþãì—Þ·‘QûÒ‡¶ðÉÆ,a+žK!ý,c4°o—æìÔ£Óc¨ò°ûS¢7LŒÿ2^.¹qÝ@]Aï¡Ç4H>ò‘ÈPÓ,Á#kêýçœ";€‰ A’Uâ§xë~tE¬sCT´ ½¥Ó}lSm˜cì éi —¡ÉaÚ¯
+¼¯­`Z}bB7—càï0GDXL¦và~iŠø‰;îà &Pày*è`œG““R w{ ‰/EŸÇ"¶*r­rEû¸²œˆ]Ç¥×\2Q§h§Æ…˜i
+„98_lu(Rëé4V6/¦0üu­`ZF/ ¶%f|Ù$XÑß6Þ`8 ?íZ äM‹‰ð·¾í^L»çT½Â?™'sÒ´®P¥“Ùs…ÞÑø8-S_ÞåI¹Ü>Œ°É¦+°>Ì´†ÿùÇKIצ¾6—˜™MtÇçÜnöý€IQ$˜ ”º}$cšòœM 
+¯ºÖ‹œ×¶§¥G4H^e«3–ŠQ˜rÿU÷KñÞX
+ö»c'¸Kß!Ím+OòXIY:—@fèÑn”kpðÃÊ ‡¢Ïßa9FBI±¸õP®ñ>qâÚÔ£Ç|©MTyÔ·9Fpˆgtz¿¦
+~<i뢈%)3Ë*b`kX2]ëÐÐQ+)"ÏUÂ(¥0‚èm” ¡ ˜k—°u=s> •÷{+½º3홫ˆ0úÆÒšcôÂ"…âzV SlG’h “pwaµÌ¡%Yë’ F{'Óª­(I ow)UŸË0`HämØk»ÄL‹Éìœ!JÊ5HµA¼æ¡Ä­ˆE#”3Ç\Mί¿
+H‰”—K’œ7„O ;ô¦‚$€XÛKß³²ï¿|(ÔUüg<rX¡®ÎI<2Ö{¨¨{úUÍ_>šk³ÑÄãë_ÍëË{·1¢[/:!fZ­4«­mHÈüÏÕu|ýù#AÚ]ÍG/e$¦½j3+"¢}†éc´ðhnAz«Åµôâ% £•áîÜçôŽ‘Wë½µcð¯Ë]„‚û‹ñj»>I^.„ÞTGÛ'½§F^QƒGÙêcÆ fnµW“„ôW©utUkÕ2ÁãUC†¶®Öùà’G—ÔAdÓüü!N—æ1ÀÕý¨)½ÖZJ¡RÕë(¼S7D›F)Åjœ“Œ7IæÖdƒ
+ÇtmUÌf/£K¯¢";ÃC·)ÑWþ€ðj5¾^ò7ø¥ç/—}’‰w-¢ôÅ
+“¥uºÍ*•[•ªM„{!¡Á¿#cí(B&IÏîºOŠ6hÅ^5ú~y£Bæ¥ó¿Ï8Ñ ;ÂÝdCŠÐ\ªDêr…|ï¾ß~üûGùúób^ªEƒ"ÿ¾b80è:²Á_9X@¬òëA_î·qa¦B‚4Lˆ#Ô…¤Rœ•!/Ö»ÔB’æóqJ탵SááLW¡}fªítÿ,}—ݵªJÖlAø—ðklu`m$Ä3óLç:tålrº»´ ç#-LÄzRõÚÈ…g,Hç_ù½œÏ•ffÆm0v Ü•á­t¦ìŠ:ýgÒuÈ>‰ÑpëµØzÑHâ­ÐÊ{ô¬Q<Ëy}½o5å‘Y£0…h×8°…W§2&üzXI#óþô(5&¯
+Òüš•Q|î[äš`£I;ôÐ
+
+Õ–TœS¯%‡ˆédc\ÎçwŒD¨?4`
+C@è>rHn}|S ·aø_"•r†iy!·"°YÒÝyUgH*u€5SËiëÐ NÃàáR`ƒ â8œLk)ð U þPþ1'§¼Ò1þ´gë4iü`cÅÿ9èrœ¦¡`Õ`Íú€>Rp;î€dÉ@Pº$ç¿ž@ J¶6ëëŽP\@Ë÷Ë´Ó7ˆµX^©ÕeÁÃ7˜6vƒèM&¯4õ&)ŠïIØëR™_—(µ7Õdö`€eŸÔÓ;¦,m –jƒ¢]ºÕ˜7œ¤ UõXnz:ý¾ÏêÈ ­ÑÜðãÊß].ÂÊÉBZî+ÓJ^°“=ãШßáÏ1ÔT·¨¯w§ ÀDþ‰X1ýéX{>ê%p»d¼œ«|7îŠ"Ïp//—Ûþ9Z©|F7Á¦Û38h]qPa<?u;qí”Zš¢NHO¡Š¡'Á""_1Ì+Ü@$ ß2®Tç90­SÔ0©Ô“BȺ÷+JcÑ4½Â°tÁFײpèšôž³¯¹Ú‰5Ý/‡„ ÛÜ1Œ˜Ž`i„hÖÃ1o$&ÕØÏ]ØbÛ4WMWŽé4˜Ï0w7G¹ªé­r Ó&xÜYYÍÇ/(fç˜/ˆj”܆ÐÉ`’P9E¿AÖ9ù¼’ èã9¡MÝÍOf{’§A ãA
+=¦m^CÇo¸)&Ë}ì¡{›¨K6Mx­K>^5û‘»e6─”ð{Ô¦¬£˜fD“™«¤r b‘23Žƒf‘d‰8[FÒšñXã‚Áëlf,ŠcGáÇR’Ø»û>ê]! 
+—p¡m¸ÂLñz™q®£?•Wî¢ä³ rºŽbþ¾ÀÈÉŽÂ1ÁÌ c^Åé=˜Ev´¸ÎoÊ5þ¬eÕ±q¯dšAöQoŠþF×û(<2¯êZã°>êÈé¶â°ÑL„׋ý¿CÈ:‘…>ˆ~Žúïã4"ÊúÉ‚BöY™ÆE„ÿ¾aX$z ãÂA–’—nL^MÃ'© Áv*Y¬¶„!!ÁI"f Bû§w"’ï‡áT¡“tósŠ„NÂvôÔ8
+L¿O]AfœŽ›ÙŽS ®Ó“n'„5Œ¾Èœìj‘>´=Ù'ak˜Ñ l§¤\?‚—$Ϩd’!8
+Ü-ã¤+˜‡rh<¸u­> pJиÜ
+&m'‡!äp´U§gø„´W65¦Ý§.Cð º8dV&hËÈßöäµ?Aää¾µLgqÃŒ¤[ÀSî7¦ü„\cÆ­Å®Ì]g¹XÇZû•ÿo¹Þôè
+áªð=Ìþ
+¥=}‡äQZCԃ݂ä4š“­ªw»#FjEÐÃÐΙòÁû²Ú:n UKÖ·ô0øR9OJ•ö5œ%„4x¨œGçE¶¡¥Áœ¤ 'D¥¤ƒ5GýõÒZS­øB®_zö H;™N½²ØRåq9HƲ…¼µìh ¤pp6 Œs—_7¹ %e³ÈÝŒ5”ý°¬–Ññb¢Ø赺1ZG2$+àW"CQ¡³LæÇaÐ>!îšvŽÆ“Ã6hbÆè}m‹€,EŽ@ýtÁ<KŽó„Òäe
+ðm1‚ÉÆ¿°~ÅÑ,P.« %veEUèg'egÁ圔ۃí81¥{uìTàœ`±Æ2ø
+·µcå| R€.W†vËTH"*³n!¢j0U™Á½–P¿°tw83ðcãtÚÀ,;ovð¾åå0‹RU+‚~Ù´ Ùœ‡Ð`À)ó4W\£.ئcvçÁû¤ŒÖÎS J<é_Âèè ›®´
+éK\)fôñ2\AøŒ«èU)A±ŸåGA´)ž'ðŒ ¸ÛÁöÝfÓv«“ï!]ò0ØW`ÖsÕKÐí3êIóc!£äGˇ@˜C‰$´Ò‡¸‚`q^ÄÒ¯Bº*£Á¹7Î:‚lT¯ÓÎf¨½5ç3…C·1„Þ8³ì‹ ïá­ªÆE_SZ÷S¶o*¥Õ@÷Ñ°ôMÑK^½ª«™Ä•GÛE™“æ’ð~ú†¾ã&a„li2¡¹õV½žpÅb³0€vð]ûÌœÑ5±é)|h‰«ø…᜴WCÍ0‰¦¾Ív “RØX«ì4ÊD#Q HõÍ„þg4ŸôŒ»;¢Ö㦦EaáÿëÁmÀ¦1Å9ORQ]t²}>ê5D’Û˜Ÿ%BÿÏw$Gê”Lè°Á E)X7wûmAø‘ô¢)¿WïêògvG¤¼¶@+ÛËèï)“Q]ÃC+DÓÊÅjõôöX0%ý\ZÇà¨0Zž¦ñkmLKî_æ¼ØºBÌþZ¢ áþio~-Pi\\=N1Ö¡®Ó}©iª©"-ÆjîÆ~‚¡!cXˆeÇéÁÀIa¥9g‹àYÔœDÇ'dZã/jƒéž}¥Ëû&âšú›~Зüæ w 2‡´çÕxqÑò®¯)jJ쯻I²Ù-7íN=C‘ òOŽuÒ‡Ž³£.²| ‚t37úÌéUà+=ÂaB‹²Ä2[éÔ’‰ä;
+ÿ6Üh # ä>WAç`HZ=Z´< VÞˆµÛú¤ÖÂßø7„°O‘8?ñ#[]QÀ Ÿ„íu5SZN÷½J2AHÛ¦“×®ÚüTÊc !
+Ôé–·š÷Ž2|h4|»ØŠªñ
+‡
+ó&–‡fÇH1ð'Üì´ÄÓ²4Lò©w±mχ’ºlf’´æÀbî-È3Ô“Ë%(ˆ—â3¨Eö­@?kàlIñKú-Þ#“¥/º‹ J9e™ÇYÙæjB„¤#wåÓ‘9Œ }Œ«°C<ì-ŸÙABv.ÚA®Ÿóšäõe~:Åd—L¨6²-õÞ‚“œ/ce
+>‡To¯Bhô†ÙÕ¨7BŽ‘n ã-¤< %ÁC†fËÂ/A1èÚ=QZ c—ñí ’ ©T-¾ã\cÖÈL&NZÊà-ŸÓÖK˜5ÎkÈë:`AüB:àÜ
+,F°OƒJý™xZcZ}ÖÙ ¶=ï‹ArÈÚ€¯q`ž ª1 T8ùUègSß<>[‹Sl¹iËCøs9l:ZBAº:ÂWU-:ZQ˜âpFž3Á -4²¾™qX|YPîÕ) õåMÈ,ûùãtaÎÿ “ÆœuóìŒþÃxyIrQÔú0t@‹óòH/xåúåû@¢c»*+fbuo6DâçÏ ¾~»ErH´´³ nléZPûA/!RVá§Ew²üþLûè :A‰ö ïÜj]1†q‚$ⶹÔÀM5 $R¥$°%Ú‡ &Ú ‹Ô—+ŠÊí¥0†ááÍçY…O`ì)
+V*'âÜy•3€‹mÅütÂ%¹c‹Šx¯zç­Ó.’Œ– G„fë ¥¾]BN| ž‡«!ûü<P†‡pe¹œ­p*ñâÑìȉy™HN¾Ö¡å£êH!ØGݘ£=VüÚ°±¿3ï†ÎÆ€§Ïá«hÉ i¯ØG-žŽ¶`˜4ˆF„h±¿ˆNq&Øw—ðGÉX%»Îä`[±™XjL ÍéÎ*QÍ‚X¶³¨£Ò£î’,ŸA¾LÙž³@gƒ×!Ež -e?ìŸíT œ sWÜP åŒ/|
+‹h=ËÀu†T{¹Û‡õH01ðá
+2i¼ðGOÛ*J5qüèˆ+h¬‹Ú*K”·yÃ£î ‰}W
+)€£ÑLˆÞ»Q~uÁltFa$ëݵ1YïÖ¶Žö±–; ¨ðs˜¹ƒÅ†B+P´Wa[q8%{«.¸TnúN;·uxgø”LPÃ9ÍGF¬½‚®fÐ.ÁÈ¡ØQC„¼h%+Èû[Q`ŒÃÚ„-îÅg}Ô“u3 tÙýcœ¢¬X¡áó©K ‰¯äﶲ_j=×½Îu¸yª(ŠÇLmcw㈌7 iÐ)åሠՀ ž¨!¯üÚsyå« _®ºòFvK>Ñ÷ ‚ ‹ ³ÎÄîL_ Ó'È{MyTE~Ó\Q
+ˆ0 Ÿ¶n#aÀu`öâ:p–cíÛ$EHœ‰&bÖ‡éû(ÙÓ‡£Àc­™ÖÁ(ÑqÒNMÅ=MÐu­ÓÞí»Ý*È}ŠZ#£»Ý /%šÚEÒn@…¾šÆ¯Æ‡—
+¯$5‘ä§7|¾x@H§üS¢EÅÅMÀG$ú0ûȇ nø£ˆß¸=Îp$Çi¾HŸÄMÜo9cü9 NÉüÝ™ý,c½S‡Á_
+¸ß§[Á9$?DBŠïw'Éê&l%$X›Ûcø¿3ÇäžÞJ¨ˆ2O\܇‹'¤EcdI]ÜÀédÔáÛjïà Šm(ˆÎm#¯(ö4è2
+ßÉ”-¦Ó²2ä’‰°Žmø½/ÃÄ ÌZöŠþq‹Ê" ÂT‘ʯ¢Æ|2½uÆâçEÎv",Á“Åùã¢ði«%òì¶].ô&†W\Ô&bEòb%éþã5óZôßO:þŽb îÇp&¦"Žž\§áÖ|D1U
+nKJ«ä#šÏÜ'á”1†rOµø9æ³V«ûäK)ù!£?}à@õÏ:p¨ ì€H%2#F€ëâ˜
+8ê³™²; ÷’;÷{5ŸtErÂWgóÒ˜®T¤ÈøyžÁwÃj ö¢ìÈSó\µ,…ƒ°©©ž7×CÁÙP
+N„áEzc¦ dU;³a±kì
+T!ÙK”è'ÕL*Š}‡ „t…•@C‚;ÂÁˆ@ÇKÕv‰œ$Ì nèw>ë eÈBX^W^Ò:´$Ûñ_W–¼¬Õ+KRD°`.lè~óˆ‚Þõiá)i cÙ†ž0aeX#r”hêP?Ì 91`øºÉ¸2ßÄzyn““þBÖm‡¯Ê!R`Ruš(P% ï0³ÄÆ׊1à/  e%ŽêK‹Ži5Žªà¯£º0ÑþÌL©p8¬™‚$›±$3/™ÚNÞ>,Z3dMÇö¹qÐÚ÷Ço¦ÀŒ
+l¡â™m‹=FSŽ€겸MP¤É€ÇK¸Û±ó`°1†&ñÕ)èß'áWqêÐ k S²fD4Ÿ^„> “Üâ0F5{Ùܵ_EQŒÁ;bR æ)1•UÒ¼åCzfgãQ©#ê$%,Ià†½dÁ´TéªC”p3"%Yi!Ƕ-ä ]^¶K±z<ª]*ü¤ÜdÃ=´ñæ3@fɯ~¾ƒòS¤ dß»%Òîƒ}‚>Óö6cÅ'ƒEì„‚cÌÐ
++¼ e»î/nY
+`Ú2".‚,K½Qx%ù+n@v„9[mâÊ!p ;O(àS©­âð{†±¤
+H±(¾á"n“ãúPÕE `m¯Ã&Dy‹¥}ó¢cÞCáâóÛm±”¦±¾ÈÃZû0>Ðèxý%ºbù”PZ›~ E"2xýÜG®U’•xºL?KVûìšèŠí<OßT«Áóµ0poåG܆VMXCprà¶<l@öq\Ý wR&³ãÝ´ÂoLLCë¤f'+Òó.‡AÛu!& Êv9â
+ÀèL/?ÅQ
+âÈ6­C2I!…€ÖäbTÂê%“¥oqæe°HI'zÔOèßу­îhœ6â$„`*ÒÎÜbh
+|ºÖscú¨+bºRÀ‘FŠ„õµò¦ÆÁEÐKõZ„Åá‘@×äWÆg`pìÙn¹D­IÒSV=¦ùÊœ*R8[ŠFîàŸÅgŠ<ÆÁ«ÙA+Èá4dìŠ%}bUú㾿VüãðKøiµkžÿþPÌÿ"WlË<_}Ä:éAÿ¸`DºÀ
+†âã—ĸŠQnvÑ ³Ñfr›Øõeœrg²=ö/aù
+Fì0*ûq³B€\+'zA†¼¤AÔÔ‰†bU ¾–Ô‡½™P2‰Xð"Ë—eù¼ê‡)ècÎóIQ²¬1nÎËbyÛò¥ƒ"É}Cª—ßÍŽDl8Š‰8©Ñ¹)
+(Ñ@.Úx‚z`#–Cd¤üåYZ®†î{ Sá«â;GˆM¶OMÎrë4Œ5÷&cÃƺK€E÷ýÖ®^Üv‹a}Fš¨y‚ ÎÛÄðÃQN£H¬âÙ]$ €Áb&šUІ1/%w/ôò‰b3¶žŠ£`À¹Ä—Õ+t’Ô‘ ÐWÎ¥álÐ>i"ÐWQ7gÌ’”á™ý´ˆ Ë æ—Š'ºÿþíRS¶ŽiúCïó^Ãåy”똷“¨X‚.Îê›
+üÊHŠ²QâÑ›€ÇxX_È‹MÛi¦‚UV
+ñÍW7?Ý@ã’R
+¨¡÷ ‚ÿNú`ÙQa6¥ù}¹ì†_[ãs¨PršLï"—ÈAŽƒ4õ¼p¼üÓ±ts¯dkÛîNvaE®œ%íc
+0·8Qfg?w_.¢¸\OQ]É9ú”ÔÍ U‘¢{ û$gt0k¬$kÖ⺭©gòë¹W#ÈK,^r´]…a€å_ M¼ œ¤°å’c
+„—µÚj91vü ŽèÝ>àæmåR¬õW3G €…÷¡Ðåò>0=t4Í5Û¥äû·¿ÙÇßHJãQKjDþK‡¥ÅŠ|ü¼W;<¬#)Qk³Ìá~PI HKÄí%ÖL¸47‚/ô€™¥À|wˆ–Yø
+šm)!mš1¡88R’êP¡«*NAÅ$ôt¶5eçÐP“
+'Š`*(;g‹Íwé6ö­`* °¸–0=9lÎÞ¯zÉ+Ash¬‰,%<¸É"â NIã­•<ŠQn¿¢ãܸhj|ÅuBƇ˜£”¸Æ9Mü‡×,¢ìvMBŸýÌ3‡œÎN±ñXœÂšA:W°Nô +º§ee+¹–k>*š0”"Ç TbΩ]Frô\À},o %pänºýk˜öšÄûæÈ÷¨),B
+f|ƒ—TÑìôÅ´i ç]ƒ’sU‘ºïˆ)ëÃ9Y_T!¼^Á
+Æ¿\Vð€‡Nõ|i8n={ØK9‰%É7~À{àu9c $ɼ•æ”«X[ O'[¸cÙ¤¯â{
+©OÿòTÑ-úŽ·=Ø1åf=
+PRdNTè^å©‘o%®êƒd€óßÎ*sðI¬ãâ«\0¬ŒÍa£ZÿÔ@2Q[;öhwTÐüд¦!̈”q9 ½g2ü‰è²²U°@°¯æ]Ë<`ê÷„õØv +0?ØÙÎÁ•2Š™l—7N&âký»åfÖ’׊ Iã)˜àš~ÕRŽáêÞ¶U
+À8#ìçÌÕ •½—« ‰<Ð懊‡Ó{±µ²¥eß\Ï9“@IZ%ºå®U¦ƒ5jõ4xé¤%\O
+ÃQà$ln÷sæ”Zni0*ßšF¹N™%Ã`N±òÀ°„°“6–ŒvoY†í7a‡€BÉÓaaÍ
+Rfd_L™™°¹Œ Ä£Š‚ËmG!Ó
+eRlD÷Ö@–€›Ÿ‚{Ó³f¡«v?žœ·osä½^رM|šP\—M†¼Dk&!˜ýf‘gQ/$!nËgšàPÜ]G^×Cw’¶òÎÜS¾zñ¿bØÿàí3Þ~o6ÓËõUæßWœ>……µk°oKUÜòKR&©=Êâ¹L(ol#J•tZŠ4±2k{´‘ß”Ù(†bÕB´CȹBÃú× 9[Κ–€ ÖûØ%R”Bh×¢YIÕ=8YHÙ£ì–_I Áû\?C0©rݵKÀÌ“
+
+î­j£[CÜÁö’Ém;\>ñMg"nM3XbJ¸’¿! å
+ •<È?¬éäE†'/³ìÅYoÒ½ÕŸÁSxÒ,Ýn»†ùU­‘eÆ"O'ó` {Zƒâvë T3ù!í;´$£!Á¾^±1)ÉBQ2F®õ¯ˆ‰Î2vg!X)øX˜’ST±Ð»¸L¥™@…ó˜îBñ262 î^&ý! Wù“sÕc‡óo…?èRm‡Å£uà&õu^B"–1ËCÙÐ*ÿ$­%ÇænÁ$ÄÏ`âsŒ×ª©Ï^MðC%ÿðUF7!»8›tÇH«‰¶¤)걑ß̼Í»†³2šM¢<§óÄ·™]ËÍ”N‚Ø%X+ᇠz¹6çÛš2Àøã¸ö÷=Ž%è ÿ­RÒ³ÝËDŒôÞ­^¦øj°ýƒß+ì5pmŠþ„E¨±¥ï ^&
+N‹BK\ô¼®ýFí°;øU9²1j}/»)ã~®SŸ’ìòj?+ÊD|BFUòlä÷¢~ÒíW“—»Z¨Ð.ÎTs
+å|ÒL6˜°3¢[<”eÖÐ@Ü]Âàm³±E°5€R’½gBdTIj¤Å€5ñ/'_D€‰K.ø:Jä¤À¬´¡8QqÉ ÝJPFhê¼ø Öò¨kŸ«
+w0áÞv?»Š®»ÒÖ¹ˆŽL¢\P.•A˜HÌ€(å…“åÀ&b®¼1}ˆ Ó­¼†_¤Vb£ÞY~Ë}US8&1¦ï„hC¯@cç ä‰Ö$¸Wn[o•þ¢Ò§DãÆ¥-´¯ßƾ³*ÿg¼\rô¸u(¼ï¡WÐÐ[Ô8fwäìšïP”WéÇ5‚ q7-©ÈÃóXå|dlÙ• 2üˆÚåT‘4\¯Jø5^º6¾?Î ÆrN¼ï^"d1r>pÄW½Šê7ÊRŒZâ"6D{Ó‚z`Öƒã;ný«êºS•mˆQ%°‰£YÂW$-4ñƒ… Ë–•ŽˆœP—VEÍx‘­+ü%Žt¼(ÆéíV¤e@ÖÆDÀؽóšÕJ÷6gGºæŠð3ب(A”²zÕS\¥ø”à“ŒoµóUÖ€?›¿™âµáxO˜hŒ<¯LA 1T÷U/ÆA:Ÿ\ä›_õâ-F…PÑ2„tµýa`Ù~ 2z0Å‹D
+cÚƒîäͳO*Æå"×s`ä!þ€ÒOÄÔT» |ó¶Þý{ §)>,W‘?àÀ}‚Û:Ã$<j|Ó ûÎO™d™\ÄáX¶&[“tLÜĪ<M‚f6RÝXâÌ
+¢Q¢¨3_ìg“‘~"‡è«Å9L‡¸‰×a™UâßTz`”° ` t¡±µi†›™«ÅW±¤D0¤„$"²`ÿN
+Áò×6áË©’)ÙÙ@Šó›•1P@zî«
+ÉlD¯çÓË‚Q Ð0"C–€Ž®7ïÈÂé‹}÷
+È/+
+¦ ¯ ÒrþÎ4SÑ=¯Bψ9´o@8ÐþwYjú¦°ÈâÄ9£&ú§Ô§‰‘Øcg/ç1Ð ²ú4Ñu†Û®¹Æ{q’xtÂvZ±4öfóEïl$Ip*õ`7É+N?X*…ÝáhOBþ
+€cd‹Fj‘fê¬ÒU·»žÁψ13¶Z"ÄIw´ÓrŽ?[ƒqK¨zDA©—i–@7l‚ ³aâ)R„¦±ªe;.6?,ƒÜâ£ø¸B»Š+¨‚é¤í[—’­ÑMwáÝ°¢å~ªJÂbÕ/¦× eói.Ò¨tß9ÅÓBÒLJh@Ѫ¸Òg$\º‹+jÍÍe4Uc I¿‘Ò»}-7/,~k1N? l³ó¨B r[µGsR¼Q®͹ì  nrH§;%>
+ø”qUž†¦dOÛz/a“1|³T­y]v[?M# ÑB4öMi ì}´o˜pøVûTRÝARSŽ=™,dm2µ1*ÀÆÊv9¿M*Ô‚O­¦È«|(ñU<Œ+€ƒrAބLjyÔ´ Ø”'lë¸Ï›¾¤q@Œ*Ll?žþñaWæ“þ½w—i–ïÑ¥¿dSŒš]Š~zQãÑ3Kãz|8»‚UîYÃøRIUí2a‡þ~ï %°< \ÿZ;€­9,]}øµ¨ 2²C¼áÃ9ìûÍåRßk+™%¬—_þ,BµabZ_þ.š²XsVÛ/C=Jƒø¾ÆC‰3±è Ãë%ꎾƒã²Q}¼¹Ôa8£E'S°uaç3¼ šŸ»¢¾¡¤… —­·™J8Д†8¬G¸
+댃FŠ˜SKƒx Ý©c‚ãŒ0ÓN×9ÄÎ.ÔI
+cܬYÎδO.®ßÒYàÁm·Š£Kq“¸Ÿye\|^¶›|ÿ«"^;d%» b‰@Uå“~¹¨¢
+×lF,á\vIÏçÁ9CŠ´2ÏxTˆÃZ:J°!&Äàò4 <¡i‚E¾rNÛK'ðW z“”³aEÔY#%M©ÌRrÚÜeÄHœÎä(ìádâèOÿP¢æ‰ÏÄÿqÕ»±w߆9›Îb.%¸…“|—¨w,"M®ããUú Í×Wo ¶Ïüñ2IŽ#W‚è xž€†ˆµþRçèUéþÛÿˆ¤µ2£¬iê…Lí cð6ÀÑÍ!UiùÄAET
+EAH,Þbï}& œjCsk5¦l“ÁHLI¶ðž›(þ
+¿èz…šob8a¦iwÜ”’*g~Ê'ë"‡°Îƒï®Vœ
+“äƒæãzt»%´d‹Mg_*¡%;„Í`
+È–XȪG8±a§Ý*¹ZŒ%sEÜõC°k+î:‡0Ì„‡+Ê ’Uضï–ówæ
+]§ë@0 ììÖ‚ Á ñ8‡çÞnG½$Ÿº2òëÇ@™ÚÔÊq[˜¬¶°¢|üðÐÉÿã„-Ï[ÂÅ' €|ôŠ G MÅLIÒ³âs°G}:eõ'#w8Aøº–ý kÄ#_§@&Uñ±]ü€Œ `nºS›„>vF‹ç$:šÔò€•žåC$›¤™ÐïT) åÍEN^S]NÉgþ¤v£Ä
+M¿GgyY>âHôd‰¤ÈüçVˆ’ç]¬ÄV˜:`fÄ?¥ìtw´ªçP˲-è\mÎüwu¸a^o0 oh "á-²2¼•,Fà\²1p¹ÔÎ*=Ã18ªÒLŠ3Î=$Dq¼xKÓ ‡b®ßA궎`Š×-ç#3Ó¯ñ, ,mD¿érÍD(­‡?†Æk/Å\å $cl¡dDgùÔДŽ}œYäá (Uçh8¥Ðx(œ^ç°˜X:œó<¯Ú:2EµÒ‚Õ¥—Úg+$]T¹ ×%eæ‘ñ)fSˆ"Þ4ZßÄœC>;‘DÕ²èeÑòÖ«p%0e\}y„éù 9g|
+ÑÕ†5%½€mÌU¤ËÎWß1Hï@Ê´þlÏùè'fÊó%Sn=7µêw¦Õ–C3Œ6AˆÊ_|dûöÍðe¦¶óhþÿ”ÈC~nœ±¿˜P)Ò)ý€údžç•]ô`âÓTö Û”Ö%1Ýe¿é=j Qšó Ó,öÛ´ueQT‚¸P¤éŠ2Ip_Ú="_øÄÆÖñÖJ[MC}áìÜœ©ùbR".W„ô¨dìVgå‡eÀw¶…,úW)â%ÎÇgl6‡Ð #{É«_ÿ|ÌÄk§xj¿Í?ÉòÀé´"þ‰@à ®FI3ÌóùûC{Ïš†Ï·Ÿ‚î×½~ò¦ßzzpÖÍóôBté*¤L‹õ
+QDÞüŸâÏIbt¬UÞ¿ÚœŽ¼V?¶%Y˜È„ ™bŠˆ¦Òw EtB; ‡ ̘ô¶Jó•Ì
+'2îä A`´*®¥ÕÇZ§í [B8iŠn‚ŒáT|^@;º÷ߔڠU,XÓ9D¬×ïØ“Do#Ÿ ÎuC:|=yöé(s¥E˜çª)oS­OÅ §XTÑ Ã_t]d&¹q{A¤†xSD}}ŽÉƒÅÖdKæ ®š06üLD›÷Bç y·¢X3ÓÉòuô‰ˆ ßK¡ u;­÷°ƒnÏû«Ö·LQ6Qñ*àĶ ÏxtŠ³èËB¥ž®ÏÆ÷aòˆ\8`‰N˜V~E$üë#
+ò3æ‹É°jÔX»º7Š¡S…žä»[•0¡£)‚hÿÙd–oŠ™'rX>›À¼+ÞЪbIhß"íÐ9ü†‘M^W‰»*Üø‡Æì R°Èƒ§†^â1{’+oͯÊ|N*›]S£" Y‘L0‰8ýÆ|1*,IlñÒ€†rgÙõ¥ÔG”£ŽÙÞÐuú‹ø_?Ñ«-kÞ„§JBÜ Wý AJc‹)
+i£-a
+>iJPìõ`
+PæïU*¬Údî\EùâÊ"9 ÒXžzÚÔ9iÁrü58ŒÁ¯´Övþ}m†É¨¬áÍfÚõå¨kò« %di/«Ÿ£Ð¸Ö6úmŸ ñ#ªAròÉ‚zq‰¼ÓêÁ†fŠ(†–+
+?Ödœd¿ÞœƒP•†™X‘7¿Øò—®b3E‚Uf¦†!@ ¦U×à {|— ÄÇQY‹Ÿ@žŸn% ÷FgCeÌÐ ©øÈñ`0]C³$3íÞ›™~5m´7ŽeŒ3Ú|Lm2ùÐmï¢éZ]Ì·rmçÉ$‘Ê/Ø’êÀ.£ACUùWž~âÎDŠ¯|úÚ$G^­Y‡´®¥ÉØ1¾ ¹9tª¬0DZÍK#ãd%#@y3._§Ø»4dþÝ Éà^ÜÔ1`‘Dü5\ XwÄ€Jä+ò2S s@Cå0êy-/£/i-z°ïZX'ŠUãc¨ýPxB}Úá_ yPò?°†ä7—p§öMÌså¿É0®kÐe´ª|±úT±_¬IEDå•¡”p0ZÔ7‚4ÆU×o*Á2¥„}亨IØ·›ÕïñDÍ°‚çÏF¡‡ c‰¹FO&©œš3­èÊ-è%Pe[¶sSVËðí?Æäoµ;0‚`šª\ªÞäšÇ0»SNë ¤bÃ:b?`!Mg
+æÜóº×“ꔊТ.oçjâýWDÉÈY¢ç˜ïksþŠÅßA0ûv-×/E¤×Ïø’üÖç+ñ€ýbSÕV`X•|ÿ²F¸¸Ðt¬Ì5n&ÐÌž¹OÛÄ‹‘AÍb݈>¡§cjUá×âØ|ïéÞ(ݽ{1ý
+µŒÌøñã£Ð ówžw†Þ÷Usî_À¯íD¦Ü_éë·#?=F£u¤žõ¨î(.6æÖįÅÄÃÄÐ&„à£ëíßokF§Ñ$ZZmûzý¢ VKT!xôs‹×ôþ^¥¢g šœ.Ó˜¾ ¿ÿ¤h‡çëû½¢Q%þ™@+_/E^ÃgðpdI·¬ÜxNŠd¸¡Ðá#MNhŒqïáô}’:{w
+¤Ú’mýéß8¦+ ýÏm¯0l§-KÿJƤ_“G¹Ýü"ôŒJû±ºÍ5Î =f挩nÇó%£‹;Û¶$¬‰`›ŽPfs”œ{š@Ò’1_°\ÚQKɵ_åÄ&N¹î5ŸØc»ú™”×V´ ˆÖH<¼•kEn P}’d•-šSÒç%IVþÎ]b¾È¶œ•¯÷¼»áQ–iµGu²R)Ñgõ2>Õ$Aä!Í
+/uù}œw±ƒÁoûU[}— æ}î},¨ÂnrÍÚÎö'Kêc?Ÿ/¹WþèyûWmŸ8aº5ÇTyï
+ã½+ѮӪQK’š’;‘ÅZqð“Eeèn‰”Ïwh³ÿ´!‘½¹{´VjMè(z׿Sæ’£CÁE‡-Î(Ц©íñ*“Š¹°ømM91dúËwÕLÚ_°{”'œÿüsq>WõÊćù1·ãÓmåMÀ2’c‹Ú?ŽRÄËñ3ǘv´¤‘K¼x"7æk§ËýlÍÅq
+žß÷@v±]¸Q%¹î1‘眂^)£µ½,»ÿ¢Ô÷j€½þPòÍ>.j°F,?áhëxi´˜¨}öÎJÉÄÜ–%¶¿•ŒÞcd[WŽõ9öe@Ž™ìîsËàÆ.Ñ„ïI×ÙSb¯ ƒá°(8sºx¾ÉQ"Ö1Vïc…#QåÆc ¼ló+0ç(wÜ_’9 ªÞ´@^lý%Àp&Ï–OzIxJ¤øó+P%2èûYà;S²Iu¦Ã˜Ô7x4É%­]ã>º×Y4gßÇTC,nÞÀ è>Oç«Z㢑3*m{#dΧ‡cµ9Ð,sžÞpÒ+ÿ“Û4½Žy­Âp
+ˆõf0Å8õ³âlvØ“¯Ùâ”n%
+ÓæÚߊ# ^)a"Ì^,ÙgªüvÛñ%LpÄ!¡ú%GØcãrNÜ™#Mß&ÌÍÖ ü¤5J(ì•ÕÆ~/É'/ùvTçÆ «¢o!íÏñ5V+¹Û›äöÝàøährt“*4u¼\‡=Ú¼*¨«’Öþâ÷ïM›/×IîÀtY‘YtÆSŊΚfK
+Ë,s ñ>’›ñ @¡ëc¬_÷;úi`–°*=i†‹LI„®œø;éoQ2>õ©ÏsÈÃA¢÷ã½Í÷Wqf»æ ³óÕ£Ëß[Çm~*F¤#~¶öüˆO·“ä³ÛÙvYå-anJd4‰'†´QâV-ƒ Ü󾜞ÚãÊîAŒQ€N%vÖãvƒ¦ðË1×æoþ+À
+H‰Œ—Knœ9„O ;èS`>øZ»—}‹zå¹ÿv¾ ù hØnØîR“ÌGD¤»›µáŸY_-{ÎêÓZôñùïùòæV¼õêÑ>ÿû¨~>mòAæÙÈÙ,jŸ:§&Çt›£ŒæRúðR²´ºBUíQÓÂæ†fZá3ú•£µ°2Ç°¬ÅkÆÈV‰Å-uN¶,Õ²XÚ6dTâ˜eë±Be%f+á^›mHoÖ¬[|í„J>à¬n}ƒ¿lô1Z¶uNzZåâcÎs›Ú IàŠVóHw'¸Õ™'Ô÷ûØ«—ô2-‚[|þýñÏGÚ‹ì%ù¬Å8A°Âcg!;­•bŸÿ»Ú|õu–ÆëzrVúkL/ÝW¦jÿSÐ÷p¿ÿäNëê—³¨[u–Òl®««Ü4ÅõÅ. ¿¯ \9otZf Ê«S̳ûheü1è=Üå}z« RsÍ
+—ÎÎâÃNkÑ Ó‡ë¤‚–ÔSOƒ¢Ä.3ë¥rÓÍni ý&ìn”ÖŸW5GˆC{Åi?ø<­„1¬sìÕÉa­–º!¹ ¼Ñýˆ:‹’óÀv^õ
+„Ó2RêçœNÓóv«>ÿ$ V›£^ó‡NArÌUt¦è”ªÀ'…Þ×Q‚ѹL­ž gT§=LW&¹B9ùìÚ~ß «ý"Æ@¯¹¡Û¸Ÿ“šfhÈÆ—BϤé•e[
+¥Äl“î;‘"OwZ¯åD¢Nˆ&iJþ<j¤U6Q¼ ¸”~Šöé ëW¢ Cßèa<w‡z#”~Â$ðÿ­Po´õMéoôd«jDèŒz˜âEÕ£­èdìóÆÅñ*¸ÓºrWÈ7Nÿý—MÿÎg z§|¥±¾&sU’0c›þñOt™L•º€Ö¬ó¬!!ÂHjØ(.Ô±º'Æ-¬ä8‘(Dx×ÏÜ­*9|ZN¤AóS Ž¦c¢£i<V°s4ŠÚMõ(‡Šy c»úu²L' M_ÄÑ´’"ÊÖ9ëÝΕç¡Œ*W+‘¤y ätfÒcçÂüdšñ&®¡PM >HÄPµ7†~ -dgu d†µ)s͙؋q_
+0kÅõ®
+ðSy?Íò€õ¯.‡$T r_Ükˆ:GŽË…·eУÑRæ¥=¯¢}ÄÑs½ª * |ϯ¶rhH+&ggG ¡Ð ; Öøq
+]~l6˜”†u°4H.–}bQ¶Ôµê  O%eM²v¥œÜ#C×x
+E¯S;¬¼Ah Ž6­Ž
+EbdYìhÇ-9ÿ«3}³Ì¦Âgí"›Î¶C÷Ð7È»¯½‚0æ³PÌfý¼Ý+>”<
+ÑG\_…çïÆ/¦|”GW™ù¦U«>kz´¦h”?j[eùˆ¸µe$íhÚ¥JMI. <ªÕƬ®Ú힀h™Œ¢Ê B/Ôûðm]{xŽ6‚Pd?¯ Ù¡/Ë‚ èM oÌë9…bÖ¡R¢ÇaA(8¿éÝ *òU„[-×bC­¡äô@R',…^"ÎÊ%œ ƒ6/­ÑPj¶·s$Òx ì(ò_žsĘ1H¤S‚â
+<¨„–Ò<|ü¶
+ªê‘&þ‡sh{oÑ€ß ¤2ŠÇB~]#¡¾ØI”ÜÎËßA]lW¦–׌°¨±4ù¬9ÈzaÌØ\>$ •cø™—ßë[¨æ A‚W ³àåxlZ£Òä
+n™ÏBÊöÁÚàY×9ø$d¢±Òͳ aÄ àc[lL%·AŒ¿_¨Ó ”<£Ù5bÔ—6íçœÉ<@ˆݳéÈ'aVHóƒ`­À‚³½Z°¸¢ÎTß¿Ö¥A~}üóaŸ}­Ë9 ·ߦAÎcy Oýßãò«¶žVÖh±â‘B%°
+1dÑbE~yó~°¶jÊ*A«ŒY–kíÄ¿Ü«—ža…ˆpš®6/ż’µ‚<™ö:¸‘kJ\“/Z•†>O’ï’CEÉwªÙn?ðe Â\G­j¾8hJ»¤ Snã¼xÖŒ¢Ô‘+ì;Ãèf"ŠL¡9=ç ΃ó!4 ÓÔî'#ÀÄdTAúçu°é¼aåÎl(ÁáC·"ÕGN˜·«Ö9ëÛØ25&&¥ínBd<¸èy/Ó**ÍxTœ …GÀd#{Vý B9]À;pu­ÙâWR ÀúÓ"ù¶ûÎ×n(×ø0€27%øDYKÖøyÓ8&á+|”Ð9úˆÎ ³e—0Ð 1¤quŠ )±¼V»h
+ýc>œ…ÎÓbÑý6“ÜàñZ絟FŠŠ&E©.S¿1w?Ï°”fTi<Aù&@Ìyð
+ÄÀ%5‡¢rÁ·‰“cÊ%„¡ÎðIgÚÍ*3p‹dfK°üÇ7Ãæ/”H^œû·[­U8?µ Âb¥ &E×iÐ6ð`ŒÕiT7ðW`ŽŒ4@n˜‚à@Ž˜WI4„®ñçZ[A¬F
+zRVŒ(öRÆÚ\ðƒfð|í VÄ7n¡¹ê¼wª0<|Óv_=–ɵ,Ê嚢… ø³Ç Z™mìþÔ,Nwe­áö¥ªë:Ä}¥¦h¤ªã1ÕRSµ@,Â6 ?Áã[El×ýÆ-AC-¤Q0Mb¬¿.F(”~àÅ–šb8åê»D<w(äÉr:kàËç*_ìÄt4(aÂ-mE}F
+8£:’¨[¾Eq·œÖND`~qDÙ|M º‡ {Êá:p ¸‰,{jªÞUä\an+{“XG5öý`<$M~ºg8œ*s÷ÕO6…e³ê÷JI[23T™ê—Š“ˆùû4ƒæH¬8w+“.uÓæ!&.íE r æ8Ô­$¡B‡„ÊÛ™Ò«rY?©zÑ£xìv? ã
+ƒÅ‚úVêw»¯øçq6ÓGÓeHðÒì%O;‹ÉºNE–Ð…Æ\·»À®5¢ìP|&Ý 0Ò
+8°rgŸÌ•,ÕŒ û,Õ1™ùl…jÁëû™¶ÏyšŠ¸®0 $×áY€7Ÿqêy¤Ï¢gõmä£âx¼J°nD.ðw¥º(
+f0‚X‚U”±ŸRøž H‘t‹ßaî#gs&oìÝÉ Äà·8åö¤¸ŸÎ4ÁÞý®Aièb¥É}:›T¨Ø¯’èÊè¬cr¤JèA@LH™åL%Í´%q w$I¼ÀÁU|«èq»sÊ}8¶)—©Bý
+ÎœGæØ%ÞVUÇfñ?{ÕæÛ­TÞGƂǦeCGg«c&ÜšÅ$¼p¶‘©º†i'+ÓW‘öîPø¥ÈŠÆŒ[QR˜ßË”LJ¦§×ŸÈr‘¨/ÖÁæR!–Ã}\| Ác à—Þ0þŸ[Õ¬U‚Ýï­
+ì>¦A‡Ð[³ÞB'ÓwÒ«|¿Ñ2ØÖXny†¤ð w°cPÒ‘ì’ê—‰kMVwÆu… ºJÀvN€ûr•¹•Üvïºm &'ûU(t.sH¸xÆhæï/Jð×Éhˆí¥ÿ¼ýñß·ðþŸÿ½ýñï—G;‘rÛ‹ð G>`2F?Œ÷ŸÇ"ÔHåY°;DnIÇZk`òä)Jð„Ìx’}ë,ÑÚ ² îïV—©©úˆÀèeø°UC
+𤓹_ø¹ä¡S{«§¢¤ç
++É‘KCyw‰ MÎ#­’„q™”î¹øR²îmÀ*D¬@>®Óè N¿QØ 'VŒ¦r’fIC󢎱Æ}à$áÇ¿ÌdA‘qà¦{a¢äKà`ç2¬G^ xÁkó2Ù
+ ¦gòË>?+œ£Ù8nŽ{«_H,¾‡ÍwnÆ ð·¶\ëñ ¾o¾ßAÞµ!GOóX|GÒ†2÷]
+õsŠÜ¸ÄurÚ¸¡oH^·chA,·³}rzT&2çgO¬Õh‹Ì}ôåÉjŠîä ^ü*Þ1oo&X`<¥,r+—y눂MÿÌ{7ç0“owré> K§¨‹‰’Ž•9ÓhtþhSÇ>9²©íB¤¤>[óA
+¼à tÄp„©]äÃ-äÅ.Òˆ@@B¤c3Y¦©6»*$M ×b[œ"î„&)…›Ëð¼.)¬’ctã‘ÓµùƒŒËæñÃÍ®_ÛjÚº‚íçp’vœµgÿ¸ § …ÆO VÇ»l逞ØÙî#Ð36º kl„¹•áì¶Ð¬c$‡â¦FA˜™ð´%쌗´ªŸ2{;KÚ¢S¥Ü:þ׃¬­ü¥G<!ö¶Næ ‡aÅËü75:u¸§Fòô5Ú‰Áy&ϱê—Ô¨K‘£2u£<-œ¯G?«¹Jˆž¢®{—9…šNåEKA8 ¥ ´`vS#nHñ)`ªÅO¥2ÖI¨ÜÉ—ÔaŸÞ½Û±¼×ÔèBî©Ñ9T¯u¼)´Œ)~ rÚï¹ÝG gjü]7–¼ØKbÈ( t%ù©±”âdX«—äF!^«nñË´.at)ìxC"¢eSë"ã´™»£®)·Ë=4‚ ‹™ý|4äé8´b>¼Ì_ÂKÐ’›Ð÷Ì(6Œè€(äègFä5ãÑè]£½“•AÏÌè‚®™ÈŠ4Tô¡ªE1é.´¤D?29¶Y¬¾Y…MÉ=èå4_#£ˆL#b¨‘E?2: çuìáõÆYâú1èµ”µL;–ßH믭ó36 ²>Ç™¾±Ð¨OQ£«0Z˜AÄÅ‚Ê!.›‘™á›ìωݒ÷¤;ã™P"~Ãhn×0 /”CÃP~ä L› ’¥Ó\¥FpD ±6NâQ;ÿÕ—Ô
+^n*X2"õeu0Bà€²‰=m"„£Y R^H&7ÁNëéT˜8ø9Ô›ôð@—4ó킪Æ9nBb%çfœ7cà›ÜÞ‚Bw!~c˜TM8Þ|,'~‘·ãï}ñhš•|†£»‰‰¡Žù|x< ª–Çn÷Á”†ã˜ÝV× C÷¦LY«ƒX•Ä½F=^"ˆ¼Yæ^lç07Î<¯äPïþ4ïì6Â¥Pÿû¤[~²)PÂ$Šó‡³ÞTi$R%Ú Â¥b—DZóÉRcXptzY£©ã úÔnNÚ¯”°¤ÁÌú^â±5Ä g0Ž’ý7Èxu¿¥!ñ õ¤C"ZÖ½E`ò(™ã"NÉÎAðG2¡d¿ Á;n
++gx?›Mì.eÿvAA
+|Ïs3Î ³û(š$ô«1…Ǻ:•F¶K{0 -ÄEÖ‹Ü´Ìh=nÁ•ýnëcQ2Tîkž£ñdÒ1=P•%U"kgÜ\×±¸ Û®˜(ÃWéë ù&?«2Ö'”íуâD¡)3,~òqr#½Ÿ05 kmÏô |:®máýqìuõð³çĬ ˆO{Ÿ_úrºÏ\Ѧ<8.a©v?Ü: çX„f0¡"›| zútg»@Ïpë‚®áZ ¾+þ¸áVDŠ´&:d7݂ɱš‰/Ѽp : ±>·cç.™„1¦1G„Fdm„è„ 9\Є ‰b×ÌN¶uÊp϶äiÀ´q”å'ádŽK¶Õµ9 '‹X4¯ýæÛá6ôªû
+HV upcI¶WÇë ÈÈ I×l ¤Ñyh7Õ϶€ÈZì¤+„ád[Fº‰Lbš¬zÙօܳ­r˜ŽƒG‰uÔ/´AN÷=·ûô̶¿ËÆR—ò’ÇçPeô%ó…d¡™”.–ëö@FLƒ¬ZfÙ€¤”0+BV5!&$µœx^Ž7„š5üE¯-.s
+HgU%¸¼†¼ƒ›i[ˆ¶"à5ä3BmoÖa®%< ábT÷4@¢)ñfKWœ[7õ
+·¸ ÷u€ðz-Íš%÷=È¥V{«ˆÞ…C•._þaH=F’±°|œw)^ל¢Ž½wz>Ž§=t†/ÿ‰ÝÅ%\éM©.ÅŠgÉoëxĹÆ#àãV‘ù´Wj ²~ùíÀºÄZT?OMýµ­þ>Ý'9âs¶÷$üϪ(Ò<ðƒ*9 \#Ïín ïOδ„#½hu:ê=-±»Ùô¾s=1ZTVâÏ\¾ŒÂï ’™‚~Æ{þøòš]Ï™bƒcÃk2w´qgªn`ª¾QDÁHv©Ðfî:i¼!AC ¿-" 5ÐQÁ”W¡•¸bì[銤€®ÍÙ
+xˆ•ä)öûXÓùù…¹œë@»>Lí·”ÅÆ»s÷Ö[›Úòˆnjx!Þæï¿<~HÖ‚j`©§Ñr@#ô Af®{æ”í¨LØÒú¤Ò䟓žÏµÌ<"5 %,X«Ú÷Íq)f‘{‡œ]} \»C £ƒ³ÛÏÔ“/0Eý.àxK¸RpÅCÞ<s èZ¶tÄNЫê)*‹+™û4b¤< 3|é*T˜ŸJ¨gd­0*PQKsž
+mÇVé î
+êÆ/¯iLÙ3†ÝE*¦åˆÜ\N YÌÖÐ÷!æLvj]WgÙ‹ðDW©¡[^7'Ô ”žÌ·7ck½‘w[ñp´;ìÞ*¥t›”cZ¶óÆJO]¼ÝÓÐÔ!Ñçñç2싱PÜÍgºÅ@É›œ”­jæªÜ˜ xv «;x‰.`çñfšl8½ŸiKéꌺ} A %¢]3˜îKñ¢”“ Û¥Ä*K9Ÿ†;àÉiÄÓ ·Ô#bmK•L"ÄÃäjŒƒáµZ[mÛFFo±LZÞÍyÝRÞ÷PO¦G‡šG—®'&r5+¦.ê8\œÃÒUºçÖ¤ñ,Žý#éf;w°Ÿœ$[Ü¥ÊkøDpaäšqó¤ö©‚‘’ŒTtErÒ/SqŠ;ô: ëò-t3.
+ÏIÀ~HíN‡êeõ`@Vý~Ĉ¡Ñˆ{a¾'!mY3š©‚“ûg>Ϙé‡`§ €*ÕãÕÑ—Œ×˜ g£ß@ߟŒÐ9i“;&Þ˜ôŒ Ë0æäIóïcÐÓI8Û} 0t¢Üñ¢þDÓ³5ñ/4i ¨l–®Kñ¶ßR3Qím ’𞔟‘÷Çtv`:É7é¹ûdîÔ˜/=ÓQƒ˜hßL»ù¨Ô i‚¦D5QAŠ&æ[€níT_RæIÅ6Ä7ëÐ(oâÌé¬ÃFQ;ô‰èaÈÈײéˆ5#
+–LKõŽJ¶>çÙ6ÙJÏÀÊ¢SÎ1`À#Ík‘Ô£Y¡nB C4-1Gå×1ÕHô(?Þ 㪴Ø;$DAK<?÷-Ñ­MãjàÚ~üÔð*ˆ]NJÆ‹ÁN(ñl›¯Ÿ
+ûö9¦­©*b׈£3ÛZ™nÆw¼INÃô‰š&›…ç˜Ø$ê©æz݆SY‹Ö¹S¢C(¤¯]²Í®¸ˆH™^¢yáò‚ð´|c0:âê#Æàq$}ðËcÌ™¦-²Ÿ.~öŠ«ÐÜ?ôEon6<„ý¡ TZÉÁPÿ}Dð›i#ÓAó}l‘J’+.0²DÍgãe¤a2M[_+èÍú4lQPvŸ ² _Ùš+jaZ·¨lÅÜ,¦v^¢
+‰#™ Ñ•Ý)CãsRC<$²Ðu1Wì†(ù»>xq½NoŒ‰4dƒÂçmžÐQá¸#sƒgùˆ0£ç(8ÍJI‰ÖU7n‘Èœ“AîTÆ==Tyðª>Ód,rª& sÀ,¢E–سm å;4X¨›HΧà—Á$ã,û~Ž&,K«µk6Ö.J HÔ ”•:ƒ«0ÉÝ
+×ê
+…o–ˆsM EÇZ=NßOÎÕÕ!ã¶4OG- ARQÆ3µQæ`Ø0d)šB5“j•É•jºHt³2ô™kVÜ8Í2åp“cȹ¦\ ÉüçJ"¶šúc¯1熢;ç¢;çêÊœÛr—©×ˆsõòÆjH}ãQ+æ\E.Ñ
+#QÙåŒÇH¤µ”VƒPª®„¤^æå–PÞ1Nä¶%‰Lp@?„Py8Ôèûa'çH˜-µyv?Dpñhz×.¯ôbØ2~èÑyvÙ
+¢b<­‹%b'Â_r ѧҋU&¡¸ª¼9Ä:Å“ê›ÛÐï´¹Îg© ^• ÅOñ½Æ \dz4‘,Œp$ù”)ÿÔCTLXÙ¹âú_FK ˜;¯eڻ؞tmÿÒ38¢n@a?Ÿz†8iÛXC9ÌoRõIr>õLù휨pn·y–ßãMQçBk’%õGÜ
+Ü‹£WëɧDý¼:'gv<3€áß'8Û^”Ëb~Š0|- ê¯n´#aj<£u ®á¡fç$¶X:)ÑÅÑóNˆðdUþ?D]®"S¤dÎE÷Ï}K49L—Zª Ü7¢ÎfQ¦¸Ó–àØxhÖ~àμ­ÌöEKl‰ÑRÌÅ5¬^O 
+:n 0g¯Ò
+çÃ6iÙ,²:HjFÍ6é®ØaŒ¶çï5ïS=é
+Û= í S~C“:”
+öë2ÛG¸1¶˜ë{ÉÉy¼IíS4T¯Ô iǺã¼»kñÌ5}ìØLùÏo)ºPõ ¨;ìÓTGÑnIÕr9ú¹á̪%ƒQÌkwxÅVÀ&a>¿)+ÖH
+Ú Ñ W!òŽ³‰§óTv1I(ÉüA›c¾ù§ž"vDÂÕâ.û%T³–ª°hõ@"rG²ö%ÏçâÙ *®ùzI
+ Á¤¢¹mkǤݹV2ÝkŠ¶NI
+B\L2F~K—ukFÑH]~~Ö¦þj¤3!Œ «s½ê謔èóMD°çL"“ü3f:ÉÉÂÀðIæàÌoÎ!«IÃN%é·É{Úà²%Ç¥Á˜Pp—ylØ4ç(¬iW¿ ÇSfà¨KÕUw9.ˆËË…Á0;ŠªHÙŒèüFÒ0¼…+V­óºk‡ñ:AЈ>ÞˆÔ•”(Qß–K¥“sP‰ýh^ÏÒïëåÛ§ÔõT饼rzŠ”«ðÙ®<U”Ð*´ÏÃoù”‰jìH(~b‘ƒv¬dH• Âõ#!xb5fÂ5h¸…âk–åÌhtkê¬3;G²_N/¿v"Ü'>VëV«<+
+7Y­ à
+¬ŠÕÏd$ï+¸\Ì0£TcWTå\À×›©àñƒ‰ºÚî]‡@E69Œ±–Â+‚óz©< =]
+£ :Ä:÷ÉQk ‚’%¥D'‚"Àœöð<îøA™¶ì¸x¸µ K›êC\É{¡ˆê9(µx(~ð).íÅÚ=KšKC§bá|óë¸o·¹ÃÆyÕ~·îÜA|ë±· §Yý|²1Z®°–+?r#ÓÁÏxæê|ïŸ[Ô5 ä‡u$]=?VmV´Á²Ëh8»³m ®·¯ý–¬ 'ÙÕ[(°>ÚÔØ­ŒÒsûhat6—|È剢¼É_‰ŒñÉøI©Éû
+hfé9’Í㸠 +¬uY%sö<=ºk\H׺:Ålº D?fÁ³ŽÌ˜ iÚ@’kúNƒAI¦à¯bBÚ$ˆä$‘•d„ƒnCÇœŠH¸ÎR]‹ìæþt‹ÐßLu >¯Ãø7ô'±% "üRi0WI hÁoùPÈ DSÓµ#LðkÔ×]$ЧþôÝÀÕ¨R¤I»½¤.¦gŒ|%ÊK‰12–>ck{|óÌKihM0âá2})8™mãs 1MËö(¼#fß5ƒÎÅ<P(2lÄ åêXîŹlKÏ*)
+i~öÄ”þY;È$‹§Û*t¥VŒNÉ>
+Ùè ¸qõß÷í6wØ8¯ºÃïÚÄ×»Ëp•öß7æ%íüÃÛâ¦17Å—v§è*íÚ6hZH²/ž´‹áĽäúJ'Wi_
+ú7%§7/ç¢Ð8p(݃ƶS b/—'JÄÄ9 ;à¶M A&Ì»É=¨ƒÌoºÔ™¦ƒu1U«µëÊþÊTv‘›à°x§mÝf080ÈŒ;=h ¤@'`ƒzÔ`- [ë Â’°Váßn-æ×0C·È¸<t‰HwùÙ :!V·V‘]`[hÿþãøíéÖ¶gˆ1}yWü’&e4pé>®cƹ-ÿÅn=¾vö‡æ#ô=´/g Wºó®CJQS&†ÞGx!f­Ëã[ã"4E>«À2Ÿ
+>²‚âŸý @Óma?ŒNé)`BÕ&n.cø³6ó¶,mßþAí7o]wŽ³1t\0©ä÷o{/EÏ]#©VC L"Bc¿[`^ûÒƒç©<FÒ¬“¼óåÕà*4@8ÕŒ &vM:\êúNLìãá.&ÐåÍLÖÕÁÆ7“U‰£çE0Ù®ŒWï³b×£ªlSBmàˆÐ#t±ÂŽ$>
+}í0[Ø`Œ=¸Ë ¿€ÿ“í.X6}xDrkè.ItµtfPʦW$cÎßÀëót‹Ù¥.³ä¸N9íÃ=äd4ËE¢C]4ÁÑ㌯N)¶Ãt°çÀ­HŽ÷Ë%èj‰åÈDˆ~À_€ûÎu
+W±ûM2¿_Ñ3 U‘;ÈWÄáLôß*b#²4m´-÷·±‡.ˆOc= wù²¨WKãX-½¯Œí­kð·ÄDÂHÐ
+ìÁó?š
+[ÀB«$.'‡|sשrnaRþk
+h&™p¨«ÖúCƒl‚«úkf€g*½¬)ÛõWXs•TrRS^J‡¢ãâY7¥¼8 
+Š0/öfz¼2ñæ?JbU.Ë“é5ª§e­¹%˜SÜ!¯Ä*¯ýð‹pùD4Ö·÷Å N‘F2"s•Ñ=xêœûjWA¾ù•ÑýÜè€ú–D{¦-w´ÖýU°¿ÏuøL©ÞW"+‰4s4„dÆ
+Ý ºéÇÒœõ"Í°/9ã#³£(û r'È úu#VÔ¾x‰ÇW@
+jPkÛ<˜•ƒ”«‰äè˜ b$‚o¨È£â8,Ùûðãe’YŽÑ«Ô Ò8ëÚê¹UÞ[ÏAPe²T÷¦;ú$‡~ÑÅùL´¦”¾ßÎÐ&î³eB
+;^½…UŽHœ2ïI/)€
+„ˆunÈîŸ/#×4A_GÝÊ.ßbCžäs¡ÊþÍWÇY©tãÃ×*²{V¡álñ]7”…Ñ)Þø ŽÊ›¡³kà'%ʶÏÈ8J)zÍ~¬ÔKíi%üh©e¿éRy†¦Ã½fLƒ¢`Þ@þC Õò(žE‡ ‚%“¬¦"*ÔÙ{VɶÁÀÈÌï½âY59Ht ßÓjTòÛH kÛÙ’Í=£‘ª’TÔêÿ»ûWK þ“Mj±àD’ÅLX¶&>‹‚þT Ámà[ èÇEoœüùâþ¸‰k*L¡|‘ÌÀdü ‹ Å ¦€Um@ÕñÉ4|Ìå8jž
+äˆ(£…ËœÍÀ%„÷ç%>çl$wÞM~d— (¢•³Š<…Œ4¶}ÓV•|—»:/‰M fgðL¸¾•œIBVÒ$çºÿ·ï<‡ý¸Í4Á«Ø{4ç àG‹£ExÕïŸlËÇͪÏM˜,yÔ–÷
+¾÷',BBP2¢Wçá\’Uù>Œˆ7ja`ÃtkñØë:§G^ãæÑd`üí I/yMã:taÉÒˆŒF4øw»‚ÓÅ€‰A9,´+€™ñ@ŸLl]¥ ­‰¸›•ˆuùè£_„‘ÕPg~[‡+
+T(“«¨W¡TÅTétÝyùÅsò‚AöÌ‚LZj¥Çq-,zûgX„ŸúªK²øvc¤Ÿ’:sþŠPD,½rày9“žÆ$.©6‰z£GÃ.G !°9½y ~òš´Û&5“}tµÜkž4ùVát¼à².wZ£"[>t _ÅOù^>o {‚9øÔ>O˜‹%)ÒdøJ¦ç·­øh¯ õxûf–˜D¹¶-¨ÉPÊžŠœóšjÚ?ñÒ YØ e<?è/‹iâ8Ð/Ј3…kšFªÁtÑõ½²íoTT¶Ш²C?Ȫ5öºzÆÔrvÅARI¦º&Eü%¯p"Ü&YC?2p–ø9—6×q£¢öL 1S¹ƒãrù&Á¬LW¶’.¼‘\‘æ÷‡nÀ¸z*×Û§¦k—û6$ð¬ðÊÌ 9Òø¬xò>‚Œýûª­M˜|‚Çóïµó'
+Hù˜rŠ °©W¯¯ë¢À {<fÙÙ¾#Áü@õgঊ³ª«i÷OIN¬!
+çønÈ(Â@BËI„;J0lñ¨™ýÂllg{O{ ÊÊ6KìîžMT
+rø¢Nj;(úø¦ö‡ˆçÜ)¢­¹ÈóLW4ˆ‹ŽÝK—®L¾á"Жv…œ–raþj%$߆»C5søÛæÀV/1âÊÛbò¬8ìP,4aؤæõͬþ2r¡#]t¬\k™/A½pØA
+›„<§‘ÁÔJxix "À [2º*+É›zFrÒ%ˆ±°#<âè ‹h7Ò°û
+A%Ø©¢„bl~ô†ÛM¸“1ÂÄouZ-ýÇ FE•]'Yà‚¤Ï°(€"ÜÑhLÉ__
+/‘]:Wnüb¿O“™‡R º‚‡?ýÒ]ù§Êcp)ßAí?äAž‘&ÿnéÁ˜àñBf-úJU#0HÐÌn(;ìÄíÁAªVÂr`Q¤AÅQ(äC4#]¯¿H'ýÜ7½8`•@ij…ôŠ£æ‰4°5«ØR+¸¯·›±n°X+Ý7ýQ3†Ö
+Ö/@ë#ìï‹X7,ö
+ì x/‚ö1N[+”ÆÁaŠF&LJ:ø ?Àýe à™ò¥YŠ;Z|äÎ>*sl[ \~bŽ°Jgí;ÓžeŠ¨è¡luH`‡æÔñ´ž¹®+(¨Y̬Sà±”ŠFqÜ佌 —àÿÐO—6Œ Ì<— o‘˜TwÁ{ß
+&øAûLãÀÍËng&,0ûÎËOzGíÍ[†ç:ªƒ‚‘@›Ia¥_Ï„¼ Åu;±K¨eÏôAç-ER˜²m%¯âoÕ°ß_IÔÔx$ìN_JÈ›JMàD¸¶ó“¬24*AÑGXÄc¹‡*Ã2†åçqoEŸ?¹ÓÇ5ƒÔ…zÉý‘6qñbM6±ë?‹S03³SÌ »B^*Û¬ƒ;
+… –÷¹"Å!Ðî­°¥^bãð®KlwÖ™¶"3›Ë9ˆmf9ëÚ¸¥ÀÏF“ë2v(?;„p=Œ9J‹1uúõ?.sÔe»!¥9”lv † åߣ#¹†ƒ·¬MhèÐâUGµ³mUEDÍàš]Ò}܆
+ÑÐÿ¶«@Ⱥ¤Œ¹ØŠ1Š„»y±Ù ¹rDV@äg0,zl‰£-q'í N Œ¶ÃNQá<ñº `SÜ®h˜IŒDŠŠU¾òÒN…7"d¹3ñ|LÇÒt×y•øã°þèß÷ëæŬäY“$(;™5•¹DÖ2›tâͬ }ýÊûPp2BÇL–fM«E(cç®×!ƒ#gfÕ‡ôuÔqk­£Ì<²æBx%LŽud)ÜÖüz¯þüÂ*-KlÇv¨l>í°Ä1`Ƨ‚׺ìÀO AΆvTøä ˆ\œy¶¿—Û¡Jøô|ñŠ—ý,…¼ª •o«ÎÂЀÑMqÚWJÝ’Vèò.¹Á„4‘ÔàŽÂÃñÇ‹™™•Ú2‹E•I£Œ»¸™XÕ€žÒ«ÉO8æ`„Ð’’ÊõB¬Ä…·ê(2”MÀÞy¬`.ùsg(xYvÄvÌa²íj «ã”ÛXªI·ï˜šðÙE#üíج>‹=ù
+§¿ïs,8#ØZðRÉ´Ð9¶cÍ
++í<ÍìòôͺßIaÏ‹VìP9”Â!™½F
+,\ÊaÜ0çì9yà°|Âp­«²°“
+ébêˆJOüwÌ€9$ëtߪv*o`¹p)ä»>K¢D~ØõÃw0…Ódÿʹ BÏê›)4ˆÌ±œûr YQˊѮ稊ÝÃó÷~|+Ë©$‰2w°ïdéËF¼ÚU‘gãȇÏh–#
+p ôbHšòèy§sjðoÿùõ±EÜ»ÿ+áÉ TƒDPH}öÌ’þáo‹:”Å䲋t‚õbرÈ* R˜xô ‹=e@†É;èŠbVN]2?è¸WÐóÎÿýßo¿ýÿïÿ§Çð^m6Ñ¿Å(ñ¶B!•Ò!zL_tDRÅ:C”è‚&ƒ‹$Á}x ÃpOXm˜Y’Ϲ‡ ÃÈÉ»E…Û ÄÙ¶à7ÄÀÊûV€WÐóN•TÞb —`B´ƒ0…š %N’
+h$ü‚Gy‘™-l?Ø‹ %'oÖ"àTEÓåýÜ_› £>y¯¶óg„¯¦¾ÒúfjÁWä
+Ø ²ïhîªf¶ûQxx$u
+¿Ê‰ö¢±¡¤0OV+všáw”­ðÿXÅÒú‘Ž*³^dûçô åZè‹ÖÎQ>µ‚Οí+KaÚy¸vèÝOûÊöñªe™S–ôÁÜdß–¡Ð
+“¥sÝ9ü~C2MX›¾ ;“áÅ,Ú´É‚ÓUì-:Ša[µì
+,Qðü[ Ì$R>ÑŸã^[òzÞ雵·¢£P&ë
+rÏà}´·ƒ6¨#4˜¸ï Æ2iõæ6Ù­Bò
+#v(
+MAdþÔëŽ%ÇùÆË%¹¢Wñ TÃ/Èu²Ì-¼•ï¿Íkr*\öÆU² hô‡DeãÊ·"Ó([±œNá/¬áq¦Gidž‡<['JŠOGNid.M¯rr4-²ÒÒç´@¯,z¯ŸÓLQ¢!!Å+
+ÏÐê‰,N…¸fˆzñ§þ¦v3&jè"Ä'5Q¢„¹È‡ä
+‚‘Ó„‰AæJKƉ#{mVD¯K{‘É9JiIÑ=-š>ð!§1·Î3ñ ™íhÁ3m&M~¥_Š°\L˜«öqtdW øl°ã:-韘tbîv@ç›…ÐÞ”·ß”"Æ Vçîf±¬Ýø­kÍýH‹ çÆm@ttH~CƒeÃþ›°ÕH:k¢tÙž„ö‡`ý6(×Ñ+b‡Ë1­†Y(è"ØE9T4 ƒ÷ÊháI@"׃óV¡¢Þq°µIOJô"<1ÀØÖò«ç`© €åŸ¹1‰ p6°Âȃ·Ðå/FFŽ$¢×±ßÉ35VëÚáB·Õ1&€–ú©¢oÌ-˜Ô‘dáeS«{Y•hKJé¨d ÂTp©2õ» óIæ¦/2ÛØ„ù´Yê¿Cà;癨îèŒóò w±p³ÖôÞHüÐ4qÅÇÔ%íÃÚD NÁÜŽ·3üc“¨©µÑ0—f_öÕí k#Pð‚§¥;*`a£IqX %îJùjÊÖ;@%§(h…%—gØáJ€^dvE#h…~â2Qą?G/-Ê™{†+q')ª¿ì¬ËóFAŸÊ¢9ÚŒW}³›ZãË×,Œ7w¦VV2ø´ú#JÒ‘îáB%ì,A‡ß•=vX
+iì2%Øn¹2²â3Û²å3EöâèK´¯Ìyj@”ÁbHVÒ{ÝÄ¥˜KW2ùWéˆb—Š2§’>aå‚Úi–݇!F€:EAJæ$5Û
+v9Ü£:Xm¦=ÂWE~štš êW‡¨†ö²aüvT}5qB0Ø¥f‰k÷#(¼þa½[0!2dúk%(pfû±º)/?•äà1`XŽ ŽHž#p5yE¦y¼Šä…ص-j; …̘
+ÄâÕ­&ü`
+c¤¨wß"(
+råJZȤŸçñ28”®ÐmreÐrCu‹ ,)º{Ñ
+ÓE~¾;ª€¼ÿìâŸêëóžŠþ¤E/©
+Ä]ðí` Jž_k’<¤^á>qy8½Ç¦ýWt?Ó‡"eÖY£ç"•l$Œ!›î'´±ú ¶Ïþ
+0
+H‰l—M’$9
+…O0wðÍlÝ„„Z÷AzÑw˜óÏ÷GduDZY™E:!~ÙÚmm]‘÷œ¹.ßívÈXÖº#h÷üþ:ç=û×?ÿùïß_BowÚòkbÛ{ZƘiýêãÞ}æ5mÝö\{Æèû-µÙ1íFkΟScÜÞ<r¡Ò–]=ï†ûö±®1ïÖú1Â'ni¹F´Q²mYºË3,®î˜Í^‚à^ |Ísbz¼ÍåÜuÊöæ”ß{»Õ©ÝÖ’ ¥é»ü®ïü¨ï{JóåÛÄ]Ç7;®nO¸FÜÛxêr3Ëyáj Çšû½2ß—O½m®¾ÿTæWÊW³±Ö¼Ìî¿œÌ}
+ã¶Ñ6§âŽm¤Ãø×Çeûî9SÑñ“[Þ" ÒOÀäqk¼±{ïî%$ãuŠäu |Y búî;½¾§™¾s«½­ùî«„Êxow3ZÔ5œ–ÀÈÞ1=çv¬ī¦áƒRMid¯ìAi
+â}•Í#÷Üš :¡¾[ÄcóZû )6€&š’Ì<Єo¶–H˜¥d„MJ“ãeHkÒ
+b0‚Ò5ˆÛ.…ÝÚmý\„VBt¤B¼´F™«.÷âHÝc•ñ)“'N*Óä,­J+W€ii¤º³nš5¥gi™†&Z§d0¢¡_7í¦›‚q%ªb9)Ø•“kœ Eu”àþ$»HÒÙÆ
+hˆäÃU) WP¬¢\67ZcW±~j4†¢¢Ä
+8áÁð­!;ë0KZoˆÝ*Wpº
+.T½2jBt2º9ü“‚„x¡è¥Õ;†Vˆ“xÍåaÒ°XãhPÅÒ`˜yix%_øöÜԩвßßtx
+lB?UÕ´/á0DóɩT)k )MÀ£«HS³ƺ4_‚ƒÜæ}
+ü¡ÒÔmLÛˆq‘OÁɬ?™}5ë—–6Œ­Ç€Pòû¸[[
+
+¯:iƒ6£Ó¡Í
+¬/<î€üð_4–QžÈÜ ÍëM_Z¬ô*;(ARø9Ä4á³Fã²1
+*§†ÌøÒ@/Pþ%øç¡E=ä:KývšÒ]Z¶ªEeACT$f†ü¢!<8æ¿dÌø0+ WKÖ€ZB¦Ð`øÁwMÁŠh׉%Ä$„.¤Aƒú[ã
+ø€@SðPá‹Ëã<ä™:Ñ.¸™±…»¦V’4‡p×Ę`¼ƒq¦³,!CEºÊbZ²Óà¯ÂbxI_»F[‚˜bÍÏã©-@šeŸÎŽÚKŠƒsš
+Òr`‰ž¹zá³Ç4 õ¥ S5µ¾5©Á„ÁÎû®›>µ3…%¡k_
+¥Z¸ÏÕš§j©ù›†ö– ´ÃÌü+ßZC„ȼ¶¨º ŒEº%Žö°n1¢ê)^ëøÇÔ- {5<Â%ºµU2ûš– vV*´ŽŸj ù\Éže9¥‘àþÑ NJþT¹
+R°nÙÐ{ç/þüä¥yÇXÖ~Oõg_#•£±1×A²‘¨,ô3î7J‹‡ÞÅã
+8·¢~[ªÚ tˆÃ]Êñ¥»Ú¼‹¼V!•··¢ïcŠü@gžú눧¡Ê¬j¨„ã/ì³8ÉÜÐU{“ýBwzm(Þ_E|νG«~`A26ˆ¶Ž
+ÄÍF1neéìh)¤a[ƒô6ÅÔgæ›±ãõj†Ä£@¿W» ‡øƒ¼bˆunDÓ•^œ¦WÚ~»œW‹2?Ç ]öäy ÞkŽ¹ÏòÝ84Îà™UÞ;tÄÔAŸ¸ºÎd±z©b1nì]#Ømà@@sØøEÐ4_({.48st û_üþÃßù#]o~ôï¿Äßßòg¾Ò£\)~ýã·»/ N_Ê´voš»S ØìëÔ à7ýÖ"ˆ³³A¸¼C`ë1GP.Ø—°y=ƒ„4IF4$Ò‰H[jEž;æäaƒÐdeÆ” Û†$> ×ÃÝç:”ïzâõ¥D›g'Í-v£wYj¡#]GgFTX»$Ÿþ¬"
+Óê:‡O×ѧ AÄÞE‘B'K|·û,#"ô–ÏsªJˆ(ŒX?Qâ‡QÊN#ÐÇ݈jÇÄMp_.¨ãb!Ÿgðw/ˆîœ÷ö™p{v7È5 …ú
+×bRÜ´W8#lŸ¢$ƒ >yJd ˆã&Á¨_Ësî³R
+z„"µïòDa~™
+,,,ÇŒWY—­@¬{S©²m&“/⚯ºi ÐŒÃU‹Ü‚Áçnëÿ,eØ4
+ wN¢»QT✄,*rNj` ÜAÄ
+p“¸±>)_º¾ñĸ,ÐÄZ­pµkü;>Bÿ,’/¬b  *&ÔÄ–cYH/Óï"€¨^ñ<ü÷èæŠcWmÇ‚}tš "q×®$¢ÂåŒ
+Ú¼ò €Bðƒ
+ùUÆnIlGc¢¥² ôøt¡Z³X``\ÐO@õˆ©{3Œâ¬J»©EqQnŠoV¨Ñ20q
+ü:Ïf«ÅÝ q·ù°8#J-É<ŽHl!2W‰ü(“Ú„«¹¯ÿÚ`ßÿ{5ˆ²`×­&3Î/ÝÓÿÑ!£
+Qt¢¬™QÖ‰ˆx€H;û ¢wˆí¦Æ€…]
+ 6j[o£Ha5*6g˜&ö’,EZbù\eœ“ ¨@¶{Ðbó(YàTÍ°ªhVœ8í1àLgдKSÄ#–&© Y —ÆvÍž‘S»®
+8È)Ï-$UÝ¡W¢ ËHÞ#Qp=y„¤°môbñ! Y²/jqÄ1 v¼ÍªªV¬V)Ïz Ng0wQdsá9À¥Ê °Å>Œ#½Ø«™±¡d‡}€<
+
+xúÄ»0%vÙ÷‹ºç|çùHÆóR
+äKÁ统É®— tyi6œÞ¦ý Ëè™……èVÑݲv/FYiÈŸ®–L6|ñ¼~•¸2>+5®¡{ Š8 ²%m a7#øD@ëTÂÉ,³ÌÊü¾mÖ.àiÆ *©æOÓ2V¶$z³dšœBj
+Ï°òýu ï€ªW`Ò6'±ç¡,æ[lÄOŒ9Ä,i` È¡7ëí
+f³c§’§FI×Ê©ÎÈY„ï &¨¾j ž=<Dx'†õ‹u.pyÞÌ:õeõ·Ìz¿#`ý}±a)~)ñfÂÞ ”äßõg³¡u"éTuîãÞ·þ•Ù™/@/3ðf0‘‘Ñ+’±ÎÊÎÊ#Žý£P‡’PºÛÈ0fäbæÿX²ý4,}UöŽ Ž¾Ì€Ï{d uó°„K3fE2/±Q…=õ:ëªW:Ò0´°½RJ_C«
+fóãCxíÈÚ(/³±N¯ º•×2Îßéf¡MV_`ïßecU‚\.b·)Ñ·5·ÞïaFüý®ôAçx‡Åe3£(“éÁŒÐz„Ž9ZC
+u"Ý”TMélê¨kNè:ê,aƒ>WÎ)6×Rðky:îÖ³0ÀœwF ‹| ÉŠ­X‡Á„ó̺Þ
+j¢CÉéÕ…ì±½Óœ™TõÄ¢ ÄT+Q/ ­tÂ(.þ¿×Sfj» Éûu24½f\‘„Œ„ãB‘
+ÂfÄÏ2¢?SºüuØ 6ru8@¤µ5Lõ€ÎÐm܃j#TpD´`¢Œ\yÁK‘·zX2š%Ü%𦒼÷¨bB;AÁ]hÖ‰ÛVUž†ø7ÍP‘°›Á
+ŒIm@“J©©Ž„’†ðŽ Æy¥‘fgÍ¡ÏAj‘”&—•@Zt—ëz*ËÓ9î4Xw⸈/ÕeqsSÝšQÁ§â 7¥ºóݨlð4”€&©ßÝé6Ça
+ȵŽ­ Í§teÕK*_vŽzÁ,m'Oª‚¦‘|¶Ò¢éýã:hÖ¡€Ø`§–•G £Ë}þ2A´Ul `<*æa‡»ŒŒM {·ý_ÖåÊÊ$jyÇïÁ$d1Î5²¸Ö4k0ֱΤ!Ìà1Nf¤ÜbXlì­<ýÉ8õíÄ-Ö eßNÌ/N%Pae‡Ú¬0²­é…ÆJÈ"h4¥RŠÎã)“Nâ,GÕþ ­nmmAŒ”ŸW‘ ¡AôÍRÐæ¼vU§#²2e옌€ô•«,hj…Aé´ƒ„£þd :Èc…dIšêHnnÒf’~Óå‰%ü¿ék\•â…°b$<ŧà(í~‘{ßûû/ûÅ !²dj@k*l ÚUù¼*¹'pþÖËdW¯ê£¯r†€’Ëîf”(R2DVY Y&oŸµjŸÿ·“«4Œm|OÕF…¹¢BZ×R3ÛC$ujE‘TìÍ©u ?¹ÂAÐç‘-0G4í°oCŠÍó##³Ý*{¿dÞàZš aé r¥Y…v“AYó±Ÿh`Y#|œ—! ‘Ûbž‚–3ƒ<Q‰ê“•sdTÙªöOdôcsjžÏ…ÚŽ…xgKÀ$5ˆrB7iâ<2²'‹Œ\büA?g³¼gÏ‘Ò€Œ™o0c0TŠÂ¤*
+¦Y³M]iiŠÉmJê\ ÷Yê8}9Õñô%5ÕÏ >PƒW‚oN5ŽäÙ©“$ñî€Ñº•M)=bž”ÀÃ6Ÿ}ÇÞ¸/™f0Y¸=®Šßº´äÜmvÄÝŠBIÖX£i§í®jÇѱÕÂõæÈ
+à=ævÀu“(i-µ5â©*Qì$œh·“¼ÓsÌÁ! ôÚ!öƒ:ÍVK«HIqÎCÿ´øå !’­Ï»¡ö:‚vØÖà÷îR ªMw/À£5 o–$N"âù4HêžcÆÇök€¦…£!ã[
+u·óƒ_L&vwáõ®I?ØFðÃm*Së[ä?äPhÇ*®üRRU7 !eB‹³9Î_/•%:ÍD5e&РVßôE/vˆ¾c9Ì4ÊÒ¥< ¥Ë¸”"ò‡@Ja‡@½PTżþ|»?Ž!Ç¢†•C/2 +€PKÍ)©c…BÍüèXò¹Ïr'»á¨y«!n FX’ب£ÃÐ+ À
+V"–:
+ËA'ƳklìÎ4Šzˆ
+}S”¨·ýÙb
+¥v‡c"8Âo˜”©&¢_ºú:ði!¼
+BÖèOÞh
+M@þ“QJèêò }*ÚiB]—,‡$-r!×—WQoÁU€à­À“L³"k¯Ãhká2ÈJ°aÌS¨-3Ú؇ SÁ–qÇÿ“uÿ«È@˜ €öÿ« rˆ
+.Ù§º ŽL\ß`F1HöEY”l¯xµÓ)ŸƒˆzLg#<§£’A„C `(â%îøž‘ÈÖë »)xÙ K‚UòH¥ïŠôµ‹ÿðÎd«ªk9‘NE²]c7+³Ü2[ô$
+7+a7B~ŠE8³ê&vÊNǦ°¦­Æ*À…áà”ú}u5 Kóeõ£Q’¸h l×V=E
+á'cI&€]³tòŸ*ëÛ_±ˆ`Æqü]Æ™æ¯ìdwìàvêÜÚ²xûYÍÖ#«)9OT àBd €°§ø‡­6!#kAÈèŠÁÈ ͘«È‹,d YUü4‹#‹ÊÍ1O­ÜÈbù]ÚÑsd Y‰ŒP–dLZéd¤‘b%àå6Q˜‚îBm^YÕ:·TfaÔaMSÇñ襶atvÕ&Þ4Ó*mB÷«Œ#Lq~(9›Ÿ8_g)=©ê¥ô´®Ùï’òצÇ#b+ª ¯lÛ2‡¥';OBÁ½’Y[°ñþÆŒ,¦if†`7#z”Œ¡$6ƒæ«f,‘A/*Ô&³´“¡m$ƒ´ü\©‡q©ì!é *›…ºŽyB
+¾©S›l`Ã[G`y€¢:
+wD£¶g{4û¸x§@Ø!¶Öù³¡#„£­ò¤â·ÔãþÈ™Î9àïOD¼û„\oV<Ø”ýU'þæiQ§ qÞÁ¹Ÿó¾?Ný
+1o¡Æ)¿–‘ùµŒŽYÅ£3ªÐ)i»† \¼9Ã@+ƒè)ôÉÚï @u¼ªy[¢h‚&ÊÆËuŒ;"$r 7ª%ÅxJÇrr
+ïÊŠð‡áxãˆV…uXºÊ’ã%§ŽßT£ÁIŒÝòLöŠW¢s¹
+uÚOíù¿Ý’ðbtA×›[³`ÉýÆÆt8ýdeá©èIxý~Y¦hHx¾]á*Ás¥ûå†lñú&«™*ø‰Ò„´åÛrsæŽ4‹3ÂÉÜœ7Z›Ú—]{'Þ]ÉÆÈÈe ÎÏ…~Uø³¦Bºåiî„yöuÖóü0;rbx@Ú•FïߤPæ­j·ÖÅG|?VåRÙÝâqy£é…Kd»ƒ¤ ø›ÁköøùRcø¹¦ÇC"VbQ1%q„™ÂÊ
+X‰gP2rY s0Í–‚šfXd
+½W"¦”
+Q§†M=áEŒ^u¢—ˆ©Æäeæ7Ï¢ÙფÌ7\yZÏ# ) ¹(Jà0íðĬRQ‡ÄÏe‘#8ÿyýoôÿŒ(3,J³¸çJn˜/J„2*·TøߢåqÅjlUdpnQ){˜Ÿc™‚<õ9t‡EÞhÙnŠ„+ T¼¢ÛÔ ÊlQ¬¨bb”²'ŠþˆuhÉš6üæFIlD €á#
+•‰¹ë)ºà3‚ª&¸šÅWÄ*Î ßámÛ ¡< ’´ûHD70Úíå‘ç@]ÞH´(†IYÉãÂwRít—ê?Ý™¡cãˆAÜ
+ f@² ›y䯯S?|³ô”°QÁˆÍ@M¢Ž>²O`õxÅpÇÏ™@)¢Ôõ¸@íÝñúk$¢–Ø
+D„0y
+P\EáÅ…‰uXĸz‹mX²ðX|nœˆÛK °Õ k¶ˆíˆ¼wâ]N ¦n ”êlã”|›ö¶ºÍ³THLÞ®>c5Ôö–w¢J  ç§fðQ„"ƒ]€íŽ. Ç81‰æ„cÚ)v:¤f•up·CèìQŽIûcÃs§&fŸ,rÎ
+‰/ÓB<*f5p( aYG£ÁØôOÄog·¥Á¤ê<o¢fP„È~Ì&pÖhlj éÅÓîèKѲ»wîD]N¼
+Ñ+~´kT–±’|˜×ø¹àœã²®Å¿·i…)Ÿr5œ•éåô–1¨ª¼'ÇZ/Z3--ÿ°{˜%œJcÃ@A/F‘à„ .5Q ×ÐáK °¬iß‹"Qä£(ÊRD‘·N¬ã;‚€ç|Ö¹GM²¤b©"Hc¿ÈØ~Ëâñ‘ˆcà êë+¢GÚNãÈ ¼…h–
+Ó9yMV±œ¯uiy©¶(Gës|A ·å£›Ì–¨(ØíÅwø0úF ´oH‚í…|qœ'læ•°EõçËI7pf÷5môkcOsiØøh8|
+ÍÆ›UNˆ Nà—¿oßÍG,\ó#ìÆGboüŠ&-$V÷Cu´ÿËz¹õØuQø¯ìG'‚q_vwï†'›  Jaž,+qñDf‚äÏ÷UïsÆÌ1o(±=³«úV—µVíŠGÎÕqOåÜÊ •q.Œ-uᜦTd¹†ð×€Ë2DhÕ1ÝSLón`¯ønF¶3”É.‘×ÑI2_ãäq
+NIÖ#*Ê·µFJ i„Ç2¶µjOv¹ „’À F C=¼
+ )ž‰ 5úן+ÓÒ¢6&¥ð¥Äã<‹-~/Å‹(˜ª†j§]n
+è<sGÛo=VŸÊâf¹ÅYd!A0êÑIDeD¶w‹é©Gäãºï#óM¶×µöóZ@-¥ƒ‚& I~Ž_ ŒìÀ'*ïD`ó«arÇ»A\€L¢ fÌméA
+RLñ,8=Ë
+%`ßJQhQ.7x…ö>·{jd{M\#%\àˆfºCQx»v10ßÌv5¬íÊbð¬*W!Yd½Þ`P¹ž2`1ä5fí NÞAŽ"ÓHO„±2±hDÔm<Ï‚ö¸{ 
+£¤ÀÅôñãï…0÷ú8nò lÒHd È4škC0¡¶XmþÅêÉÝ·t•vt"œ.µw®CçgE˜ð†yR¾Bo¬j¤ëš BzÂÍZv&a;©ÐØ¢âí~Û~·ýC  eJ¢É:
+ÔÝ2õ2;œM=Mº~ƒ˜©‘`W”N¦sˆ@G—ðT:ØP1¦ÜÑÅcªz]”1ò¢Ã‰ôÂ@<­Ú†r„RæºRE¥áRÆ$<H1•Sí.î·:þGþ<ñáá«wß?¼»ÿæÃÇí7|zökÀžÏòÅöüÛ‡ïÞÿ}{öòå‹ï¿ÿå§oîÞèûÅö+<Ëþÿò;þúîÿ´Y¢AˆjŠÿ^}\_¶W?ðCßž}±½úëÿ>,oÏ_Þßÿs{öâ뿼yxxûáýë¯ÿõúOoÞ½ý»ûŸ?¾¾ÿñõïx÷ð‡÷¿ü¼ýüŠoÞþüöÍÃÛ^sÄ]n^o¶oùç—Ç¿\?ÿÙ¿);Óùs¬Gý#lùÓÇx¢ ’f²ëÛdN`ÎO§Q
+¶ðàz $JxdÔ”ˆâ}yðSì±¾z”yîÏ-ÐkÚzŒsØè„Âj´ý£‡L§ß\»xÔT—Ç@;áAoÓ &¨uhçL5k¢
+b¢ÂYÙbHª 埭Ží%$ ƒ
+ÞØ:lüc%Ž~ùq5#ס óu ðÏ Hˆê¤Ä÷
+^Ò¯Ç(ë4Qµ '•cP‘¹¸:Þä%‘bû DaÓ#)AÂã rð
+
+˜š$¡Rz(ð
+Ød“AcŸ"^€m×k]D|ôÚëî€ã&N¡ñwÊXÇÛ‡ý"¾ÂÉü┓ˆš8¼Ä÷Ø<ÐÐ1úzP†z8Zè@¥'ÄéuÃglAÆ΃:=Æ´Íå
++*SqÌÌò¢]5[P!¤ÎÝ%ã^šF!Ï>1xfâSÖ`~ny!Kè1áh®+%äJZr…ö®K­¤“zµÁTa(Bã8YiÈ°­ñ(TÛþCx™$9’ä@ð+|AŠïîqž?ÍûGˆ «™Õ2u©d˜ï
+ò`É#j¬ãô¨6¥\ m¡™ô |žŽ xËá( ÛO6…„Ì!ΰAf}Is<3V½ºtzPžðXÕÃ~»±ÑhDˆL×F"‘*yp¿)«¤Ì
+} ˵+
+Ûò|O'Pá0XŽðRí3I¶›Ô>¹s¥bÅÉÀß$•†Då!AØŸz} Z@äî´
+±³i{ün]Qó5ˆÁ%Uöòs]œ`¨
+´gÉ7F9),â}Lwßg¡d)Ð`ôàF
+˜)üžÐ¯pyaûg͆S³OE§&“_C¹Š…[v%Ò»q×I}Ì'SÅ,Óê[й‡[`±8‰ZÀ™Œ
+ªVÚÔWp‹N —Ü'¤ƒn®Æá¢cæjdc3¥È:MÔ^…iéä¼ñ®zðÔt’àĨ‹™u1p/§Žk<§·Å}fÙ„!±j‘öPCƒËþ ½ Œîå¾Aøx.Þ{È3KzÉ®e6 ”!]y‘—o ï8õ,€4¨ûY¨0H"ài|ÊNiê#¾$ Dó<Ù¸mR¶s›+ŒÃgŠœ»íÇ¿ ÐâH>ßÀ“»¦Ò{pãÈ47EÕbùùû«jI•K|ƒC%`"±vF—þ“® m0­EzÕeÁPrØä9ŠÕTtBíúÌZúÝ!³  úÊ®«Ø-¤4R ”§ÿ4ú
+¡/¤Êˆ×I!€—Ÿpè‡|>€ý
+Å¢ZÁY¢ äo£Óÿà×op©ž³PÆSãe£E¿ŽÖÆhT\q£Ë‘*¨+´Ãì·›âö?Ukà 'ˆ´î^º4o…gÁà’
+›'«iaT°8à ‚A†ß¾aÄËo-Ø6±è%ªÛ¦c`w מïÿ wØ“u Ç(o¬-ÚÚ梔×QC–P‹zPb}ž|E È+ç6®-ÙSŒÂIlÕœŽV€äZ7%0Ð/À1žå”“‚”4M…ÇïD"
+ÕÊykR9×R˜£d%JåØs.»3]2¥Cß<1 p‘Ä˺u õ»ÙU‹®
+‚[îDpÇ,`÷\‘¶K;
+5€§j[/õ,Vtíî•@I€;  ÎX<‹
+3â0iP”¼"?çÛaÏHerý@Æ nÒ-ĺh)4¼³ÓÐþà®e4ãPß š‹iè/îSíÆ/ˆØçƒà4ô!mCƒ']v %ACÃq¥¥~+ûN”¥AprØ©bWcƒµQÆ®O%“Ý ì¶Xô?a?$1ÏÀ+§vvà‰ÁÐÆE?GäE„lb¥ƒÐ<ËA“é±L*ä—öË“h1‚<1BÖv‘k” y͈…}ï4 ¿£ ÿ±]æ:ŽW}Æ
+µ/¡‡N ȱag
+mÀoïsn9IHõtÝ¿–»|˾×Éñ¹â0#þUh›Xhyhjˆ³YÒr·C4Ñ/D9Ûí(ìØm¡òý1KúA-&)¤a:¸E
+7½ÏÞj Ðly^‹
+®p·#•É~Ý ‡„¢ÇF›nË3QXüÞ~ Q¨ýsAnåf¼½Œþr˜QäTñ"rÉÖ£f‰³Ñl[ îG‹£X^YJ¥ÕÔI  t
+ÚI«ÝŒ‚ÉN”þƒ¨¼T1D
+¨™^¾Ep뛪5Ì„¯r—<ˆ¶lÊ.š>‰Bx‘Ю€·l7«.ÀΑß#^ÀKÚaÍèÛÏ}xàu®‡4¦‚k“KQ]@-¢ó#Ëídä¿‚
+A[‹oåÍè…wÈH¥—p©}@XÌ;쟋4‹E9R¢|ÁiºØÐÝÚ×ߟÍæÑv G)ûÛÇ oëqƒ¤e¤qʺ£¾ç‘Y¼ó–.¼Ì™¢xR5
+?W>£þvo&1±ŒØ:ܱÛQ×jøìµ8¨%Gv(Ò»ü3…oy/–NæçœÏ( S½O¢@lŠŽ¿Ið.jK›`¯ãƒ8ýËV*´Ïõ§‘ÊØ?Ô¿ôU5>f¦ %«´^H0óÞIÂ@t}¼3nœóNÁ,¶@¶Žêk!îr=$øu —tŽóAÁŒ+ïùe‰ùÇ’"~ŽF?z4*Ò´¶
+L1Õ¾Dî8¬%ÚܾOî¿léTž‰#—Ü­uN2*,J̇0EÔZ± ‡O×ÚòxÊ*ô“ôYJDµ +h˜}]'8k„žòyNU
+)t2CŠ€øéÿ`õ¡K XH´¼‹ÞØüKò14äPCC Æ9%\|À
+&¾ÍÅ:Q´Û<û Ï] æºRf“•\§
+2Héb,Ú£­ÎÐ9³à[-qÇ`c«ìƒ†á%ȸòG··°9ŒÂ6D$@È!CÑm×Èò š#½3† „ç„Í©äº`Í
+«T‡€>’àäá2»r9éw—ó…EÑhmàOþû òksÙ‹I0ŠþÊF5Ò¨Ì
+5ÌÊÿY/“\Kn#Šn%‡`%›$™öHÍ
+ì504ÊïÞç\æ{å‰í‰€„ÿ"’M0â6Wßpo2ŽK$ úìö=irn±«AÖ–‹\¯ÃÀ‚fìeF÷ÒHž¥G-âÕï´Õ6ƒÔdÀÉÀ•yÞ£=³0ôº˜˜³íAé _ëpQÖñsÑIBƒ’tyïõË)ø3›\/ãÏd:köÚƒn׹ȽÛY¡Ì€)ܽ ªxèìÉ"Íàlð—윯k‹œncføPýÐ%ë-ïÔK¼ ÁòŸ°c¤Ñµ¿žL€V·{ø_
+ì¥è¾ûþó—Ÿ~ùôå—ßûùó¿Ž¿ðÓ7fÚÀýZ¿=¾ûû—Ï¿üöã›~øþÓ§þú·ß¿ülî·ÇŸÈü+ÿEþýôh’0RA¬Ü¼[è-õ`û9¯Â‰zï‡Ö,ÍÍa:|††8†nÀëÍîƒÉÚM:9lßñÔ„(“7¼ãï|„úY¹&¢'"œhwËbà|Í~‚ µËô=Éÿ!ô™¿iæîv€ZOÀÚ †™²D9ÄëÖÌ r†ÎŒ²;™èãZ¢r‹+ÂWZ¥N3/Ï¢_{ÜPSLDÐ ÔBž½=ú´«÷ 8šØ}@ѵ8/ÕVY¸F
+ÚÞŒŒ
+¦Ü®’œãÙ‰7}Ò_U[
+({£ªrd W½ÚÇÈÈímÐaŒŠºi
+õ±žVO™Sïþ˜tÞª3˜Ýë½oš…¦Û6˜§SlY…Að$Xµo¤ÐÍþŽ´û}QÞÇ‹^å‘–¥ÈèÁ.lhŽ¦Ó ¶û€¶ TCž AM¯JE>êå±xT-®~Zés´×Ùq 032h.5~¡u ‹<< c’€48: dˆ)ža:cç<ß°¹™ÆÿX·pGŽøA+?Õò»eÛØ2lµ¢ž;?Tˆ7ãµ( M­½Í.43\èNF;ÏóUGMñNEv)U—Y8/ >ã)ü6-‚ˆ†ØTUþ©ê¸´œ’’©S9N]ànªw;¡Ü±åTÊѢøºÏwníß”uŸEšsòú‡g 7‰¦Ï] Ö;Fp=;)œš÷E9
+­‚hat
+dz¨åÙ‰
+ 4•¦¨aBti3ÀQÜNW2‚ʧÅÈó¯¾î„³‚–¹8oF©å(„çA„4üö`´!ÀE[Ì°°™¸ŠèÈ!;=NÓî])¾óz¿ é›t$Iתûc'•ßÁð–@·ÉøZXJoßÍp¦“ÑÞ“Üž–ñ-ê^Wç¶{
+ƒÝ-FSMD™¶|u9pW÷Ñ•«'€W3@SÑÞÄR´³5Á.é|"’—
+”eÊŽNW} Mĸ—·|^ƒ¿
+E#ðzïÀò5 Ü5–À_WA»)j¹ÌÜÁ«•|…&=  Yîì}¯}($JIàÙâ|§‰—‰¬R“bgb_«ÅÓiwG¥7Àà[J¼*ÆTk^ÿT¥¶^ßÜ}2oPÑ4Tl,áÞ>¢|i††õYþ¶
+
++ÕÄGÉó#û÷j÷ÜðÉ\bEûqi_ðüB½C8¿9õ‹œž»®ÎÐYÞÅáy±¡ÀxœŸ4LõZ¯Å»zÆ›’r~oŠËƒYG>®Ík(6y ÅKåX ”yèË
+fàãÀX)˜Þ4.-ÈÐ)µ1¤ÏL¿„´ŠÉ6¨9šz`·2ræŽã8 àµ”Í+C >£ý’Ôƒ}ˆá
+7X•ìrÎÃj ¥\hv)¡žGFñRœÂ@á$÷{µÍ
+ƒ©â[Î"Q«p@£ÐVÓ(¦ø‰udr# ¶[kñèC·Ê¼6uj¼Xë}Ýм,­åWOc
+![±ËÌŽiŒ(*‡áj _ Çêî^8“þ ;< /
+|™Þ˜Ž#àHwIÊÀç=Ü´Ó°0í…®FBLŽsÉÔñ‡Ô0œàÁ£ «S $‚Eñ»t©x'€ Šƒ ÃVB"ìÔ¦àÎNˆž
+
+w‰W,§xƒëÐ:M [âÉ5—¯`Î(™èDÒîhµêÑFm.h]‡%‡Ë”‘ˆŒ˜ÒOÆNhÔ؉ÁìN#¨P;;1”Àå£ôå*ES6ˆp)DÌ.foøËÇLëÌÇx?vG.Oã¡M²íú$¤2RÕ‹0eHÞŽ)|æ4©ô¹mø˜‚oõ´DLsHÊfsè‰ç½”]V
+£ðrݨuÇu«:O{J8!Ä5HÝŒUŸÜmFDçŤj¾(Gªø²´Ü`¨¾D¬lž¦áÏaöØyJCAÔ€Š€yÖïg¨{·ÓO© ÈMôà¸ú]¸E¢ïú8ÔMLPò®‚†î9Š1&B·•Ë†NcOø:&=Ü!LÀ!À˜Õ¨Q(cÞÁip"`ÈT9ã
+JÁÑï pOñ˜¸~ÐÅã`‚«CΩýM¾æúàl˦ *ìíMŠŠI*·ƒ=VŒ
+7¡ð$õ®^—뼄/aÈØ` ¬eeEqK™ù⎆;ÛúËB
+%ùŒ÷õ“Fè¸*LKAK„ôXgÚsM뀆Žˆ\ÌŠm]È¢SåN<h±­}v¡·eƒXàðœïªpkš¡^Ž”ë®Ñv!¥h»{¤“< º}h«ìbº ¯¢ îLä:£É)6×£qÆÎÔÌ!b„“zç2(VøtÄ‚8K¬
+kXö’p—R®‹\85R\vss9lxÜÁó•Z&Ù 1sz£2DY#–iø"œÓŒˆV$¢›(#zÐ*BÐLÀë°Ä`݇‹) RLÔô“F ÎÐÂõŒ¨-–Á!̌ȳÊôy£D¬úB2Ô¾® Îðiu”ЉqU WU,«^ûI_Ú±ÑåÑM)¯}ü^,G©CK1H§°w¯ˆ""<Ǽo©Dg!:ë[fµˆ•)Âl2ž! ÏºÊH1hsŽ¤R¨U²w9 ·% íÁ²¢È.É„4ë
+[B‹3ÚM¶é&X05ðɱÓÃ*’pµ§Œd—Æ*¦ŸuÂØáaÃÖ’eÚ`CR <»Þ°x½3÷¤Ú@ ìUZMCJ è‡}H [6/†/Å/ô¸ÈÔc!Í“Å‘Eµàº¹<QK´¶×Vø]Ôžr˧3ÖÙJj²±¤¹Î#ŒùÝ>)ODp°š¨e"È9p¸«ƒQÀ_îÔº ÙM8d„ JDßÂ\ Ö×VîÄô´Ìˆ:±v9ϼ#Qº€
+<H|êÄÄö#þï™6”ŒÁd®¶@
+•ïÄ2ß!Y®?Æ»É=VÞ8{ìG”Q<÷ˆ7ãx€­<ã2 Ä2ò
+h¿%ÑAôὤÕ<?,sƒ3ŠÂI„›Ãº> †ø€’¼/žûÓ`mª»µ¤ X_°Oos9gðè š—úºe
+˜ªM¯ˆ×*8Š]s<Šæ
+£–®
+;.:Péá…ÀŽ‹cÐñ+Q@x“†Q…,. xKn=„TU\ƒ¥´ùˆëúë1~…2ðU½-(=¸CNâ¿°CÚ-d*•å=„_ÄòEOÕÊr/8Âh”¶ Po „HÓ7ïË)
+H‰Œ—AŽ\9DOà;ä惔(Š\»—¾E³*ß;Ò/c_F•m4º²"%‘ ƒÚõÕZ¿lÄh9Äeô׺äÕ†ûÔ–üõ׿?
+”Óc6™£‚¦…È&:ƒsìj}š› @T.³“Ÿ†‡!zu3u>RÑXW=AíâšY€H.¯×Ìà£lmön!—è"M=H»"†«¤kòÁ‚¤K÷Ðüêõs]5Ôà Ô{ŽÍk&¡sl7Nþõ(—b,L»,›N1žôÄ|€ÑKæÚ|ˆÅ:Ç®Š‘Í3ê²ÿþÐ×??[Ÿ“¿ãMo1==ûë7 ’@jHïìSúÄ¥\!©#<ufž@áûgŒÙv¾S©ê¼Ó6dxÚŒdelˆ» ¤i6«n€¦oç/‡oujËŸJ\!ÈQƘí¹’ÐZ)ô:Bôò>}¤õ4‰û¦'H}6H2,´½öcÄÈ6Y¶ÉX•”zÇ4 §U³±£A²4h?Ç}S#Û¨È{lPãŠ$ãܾƒzÏ°\*£ Ó¨ôkUÊa]vˆÔ|C$y‡™´­¸x¨ùèã;Ä(É_8DÄ„b´O$™ü}=¸]i¡¬>½WŸ "Q©L‹j#[mô¤ë;hÓÕsH—çȨé¸LDm*µm÷9¡t¨ !&¹ 9Ô4EÌ{ÛbEÓ÷j¶Aó¯ÒúeIIDĹaƒr„uop_QF jÀ½ÑxÓM"®1%~ñ3¾¶æ0¢ò:ï«ÞA(ZçĈÉ×á5ä«yëˆm¶è~ŠªRJôA:еuÙQ±ôÙ£[dz›Ôw8é±×!ÇïÄXµBHI»©ê‰;ÿ~‡`ôLª‡e -1hŸ¯
+7D_ê(.JG‡’µy
+‹ÜÚªä„--å|·UʈuÉA즾NÆ:FZðJ¹ (÷ Ÿ±ŸŸ¨U~JÍ‹8nbKÐÜnÂÙÑÌƾ+òZ|P¶ÒtCpÞ5¼ û½ÓG)„—5±d“…²VÆ»yÏi¥o~zm7•_ìùMáZF.Ö‡Ï×@F¼kÔúâŸíBØJ?°ÎÝW•ãc ê•kš?A›±XJ{©âî»ï`ÚZUI…ÕÛÖáÙäo o á5ªÃiÙ²P MÕÃÔgT')œ¤»ÚñäfB¯`(Ã1²²§š^(kùd„Ï¿€Í2ÞÔ‚õ“V<®ÌÓò.;ž¡•#.¥©!:ó :ôHäs¥ù:Û«&3ÊÑŒ´Lžœ!5™PÙQÞôè˜Ã»Ÿ—Þýå‹ö»íJLq ##;k=›™áÅ(1 _GÐ366.¿\Œü1~_cž—9¦„X»dþž}2ùþ)_dèsIb¬Öâˆ×Æ·v»e jöÚ2T³’éð²¦ËŠß ®ôàö9³VΨ ñ@JKtlC&ŒD‚sC°Æ´Ûü"¸õ¬u¬%°@“-ƒpËÞ«mÙùÙÐ8Ñu
+”eŽØBï¹·ÌT]Wž!ÿoNöEï¦0‚ºlË:¦„]&±G¢UúPÇÚ[¨¬?ux}|§XŸ+fÝIœ¥]Õ‘÷,ÆØ°q±Ae¬ÕèpÞè×ôh
+‰ŸÛýz*çM÷µ},Ó¶! žûrŒé¦$ukòÁSZÖ¥ü*:Ú˜Ú.}–ý( ÏGr¤ÿâ“ç9çüÖóÌg\ìò°Å­X½,Ðw~87¸îtWM3â×kžÌ”¹ê[{ñ ¦p+íÒGS 
+ýÝDͱcG¶ìãžgØè“u®ÌÝNÃ9÷±•sRÎÿc~Ä´1]Xso]ʇ!Kòà1Ÿ¹`¶¤«.m:Óîï2Í…;ï™fq3â¾ôd”cBø¥Cw°—‘OòÑ€+
+¾"\
+cånʼK V¥hH·,«í¡6jà1¼E kÉò1ǼŒ­t/% m?Br§ÈŠnY;½c¶ø„MóöPÍXƾ±º¬õ¹òC~Š-Ô½×¼T,~2hmËõk‰ž´{e•?Fýø~ý–ºœkl©;õ§±£k£–¦œV{Û3[n§Â(xyo
+õ;dnå,ÊÌDO­þº_#Mšùfïþ²ìyugíD—0 N»Ÿ  o|6Ü-ºs×=Ê»®î,}ö‹ìÔ8¬˜â\s@Z×#W²ŒsĸøÛ¾NŽšîƒ¬ŽÌç#Æs²¿=Ý÷c Ë]‹¯÷:°¬j5«Ä·BHõ椵íõM‰#§¥$D¤\¤L‚q 9b¾Ñ³É2iâßyX€8…åD—ä-†¤ÖUa‰£[ÏÜLŒÇu¶Zc¦GùÜFC:LŒb
+{(²~P$®ö„ˆ^<§á¬k£/˜œ_’S¿alµˆdë­<ÛñJ_±üf<!l@âkÝ’CqQ°:±rFo×ÚI©ZÍï' ˆVzOÈ<÷Y½k«g6[
+¼;P°ØX¢ð¼–ØE—Œ7AA³¹Pç—“”vpYôoølA¶ÔÃw5ïŒþÝ\(6ß5e.Óx¥»d¬ÉÑ:?8~?¸ez`ƒWÎÀ5ÓÎ’_ Ôå ™žÒWøwW
+R,ï%d«¢A
+Ù-¼Y‹‰áÇÉm¤UuÖ½ƒ£ñ³% ·ÀÊ>hâ\->†Ùáiˆ‘3å8œ|§Ø ºëSàä¿ç‹‹4ʸªEžþ”í‘»×*Ÿ꿃˜"—ˆ÷NÍqÕTŠ˜97oU{U^{‚¤¨ ÇÝL‹êWÏ@#¡<(i At«rùzEåXIñ¡É} B,æWšG$åF…Ûà>« ߘw^x® ŽŸ6ìÖÖalyÈNÌ8uˆ¬¸%†£ÄV‰:€š$kóˆaTr/¯þs}Ëi²A”¦È}适tþ#ƧyÒþg—¢A-e£['Â?ZºQn9]*Ê@vϲŸߊZKSTj<aà±U´ájÎNCé¶Jõ
+èÌë±
+¿.}^ðUpüeBÓk\ùþL=‰{ăü
+³\6q’û%öi,IÜ)¼UðŠm°
+íL½s{%âûGnæJ¦)fÚ_ B±
+®^ÆäÉâèMôbUæÆë¨õ§Âs2 ûPèŠ'ܺØ:%ÉOÓxIgô.; äGÞ*¤qX
+G§j J¡oUùƒŸ‘,Z(öÂP¢äê¶o ‡;…åÊAAˆœ”˜ª:’ï4-j,M÷HûË:ÈIgdLÏÌHÎT‘tÇ@šÒ¨¼/÷­¶Þ’2ÇwÐRÖ£³(Ódçj$I%.°é²–&¹«'fåôDCà›M?PN™¦½2öPó ÜuÔÇÖ!$BírŸØ3Ã9úmnpåB⭔˚
+ œ­–â½Éëé oÕˆä¯ëBi¯‘G!–@˜ðúyK›¿ýr]Ò^ëQU¤”–ÿ º4¸+è­Á Á3éôÊÔn Î î*pÌ+ȳÁ dT–º—vkp‚4zË*_¯JÀ{ÃŒ’ï NsƒmË»ó¿·'@ßâNV^»58AðQ´•‚plï³ÁiÐQæ¶Ï“}ßœ ›hš¥|«gÑ“7õ”Vî úªL n{/åuŸÁz+ha<•>J‡È$QÜÀTFèí§(RY:Mpw­Q­˜OŽˆã–V¾@”®—ôb§ùÀ|òØæÉŸdÿó—üãw õ˜æ:½²'¿ÌåÓ4h¾¤¸¿ „!c‘IYjˆ.ÃèÈÓ6í¨³"¿ú
+ |´zŠStɇ¬T‡pSUòÅfóÈö›& #ÓYú? b³,†0FÄElÓ$`­ºnRª€K–æå'rYÄxèG
+þã ÈJ+Qn´²#<A¸tã9jÿ Õ»Y™Tn5‚Ìg8›¦Â!X2z¬•#oC W"¸úQJµy~ƒÁ) ZÌäŠk×8il¥÷×è.ÿ@ù’¦U £ó¦†¼ýŠôÕ5r¾Ôƒð×µú}Úã"îby¼èð¯a¸‡ê#ÍïKX–1Í›»ÀÉC²H˜7#âÚÞª^”çK¯Þâ´lBly ö ûÜøf³•¿¬£g£wˆqíË×äI’“ØŸ…ë¡r)²ž³W9yde['ÍžKgÄ:›U+>!—3!.õ•Gj7¢†^uY>0ÙB !üAtÑóØ8Ä>é5ag´Ã‚û#'^ò?4D‘Š xèG­úÙ^ž¨>¢˜™Ot½ä€ÀØÑEËØJÞ©‘¡±U›N” ¯ú‡ò2É#¢è tÀàLÆZ[ßÂ@¯äûoû}2Xpe2nÀ2 Õ/Áˆ?À¡Èn®‡‰ù>é‡ÎƒÿÃÌšJ´è+Sç†î;!è*ÅØ/ð˜]OÚâÓs‘™SM°cx;HÆËiíDãÉ9ô‚èë:œ¢éÒ_"A×3Esåc4ƒLLòx'®Ð¥ÒAiØ·Eqý\§µ´có€}a ²É8šq hóâÞ²·º¶EÍ“ÕàÀ´©”=ØWÕ7¤ÒXY#d‹NàµQYILu€œíÐm† Qq^¼íʇXÍ.¾«1w’ËxÑ
+Rw,ª²’ØÌ·2…6Þ‘HÛß Y·$¿9סGaV}ÑöV˜ |bÀ¾éÍ;T*®ÃŠ4눶DUä-Ôö°´PÃ<eÞjάÕ546 Œ)£ÌáäG¿>Î HŠ¢³=5œ­Ã^¼ŠÉ´nc £7íƒz¯Ã(íhî KG°ïÏwlAè, FoïEæ´â# Ê}oø^¥Wö2P+ )YŒ«ÂCóÞ©½¢ƒ,ƒˆ³¯„M_„íøå+V³â_o©má.ºÇAÈYj¢[Éý§Útw³[ÿq%oØ&«PZ9@|n~ÉCMÁS\òÄ}äR”#•’Ö½ +qF¿ÖˆµÅ.Ö”iÇÃ*´Œô•1ªc†"d>YìGvM°·ÌÅ!S­¤ ’&tÇC…y|.ê2†$1f(Z·c`ª“ö¥ˆ1ìÁT4픽Ò)n™Ñªï1hã
+¥Ã&9f^ƒÅƒTq@P¡KŒ¶š$v†Gç"°!—¦Zal{üžQâ5¨lm»©7ÈÚ*c4¹BÓÃ::9Ám~õKEFW5ôîøvÞµ9"éy¢9¨Ât2ü¨â\Jfh¢.¿wTÅÉ/Íé0Y¢´ÍIÂ7ßà±€÷UÈžIv›d!É>gS¬)gL[ÆâÂ0ñSúŠCâ)ì…,<ÆÆ_›jAAzqó­¤û²!òw Ôªd÷‹}-Ãád‡ûõeº</Ê“N½&‰ãˆ_е‹~ÐÐ<£™i‰¹ÀïÌ[fº#h
+ì>Ì¡ÓpM'Ì»]BÏDªH‹õ‰ÄFÚ:Ò'¡m° ÅãÛ%|øN7Îã¿—Î
+9§ŠòkĪZd&¼×ic×Ö­èNÌ –þ9ùÅA=–‹¼C.éCC‘6 ™ž õ ïÌ_àõä›1x2÷Y
+ºX’cÛ¨Ú>"½1s6F‡XŸ¬¢¾à[5Çø¨o &H5Ù7*keÓm\FTÄÚȯ¥­Ø™2èyÛdpY')f1r6uoÓSQä¢MD&Î&P¡éÐëk§"#ŒƒÂòûÅqÆ4¤ 7EÞê2ÚÙG†p?…¾)U‰©’2àøy.oR¬
+^/¾Qgv¹4‡JÞÈÒI~Å‹¯ ?$ÒLûFüÓ º ’;ºOðâb%¤(Sê;a€hb|o® *ãàG#:סºbX± É
+P«ð׈„ÁÓãâœèª1G£t"œY๢<3šÖw'¿™œYˆ
+38N=Õ“Óùù
+D‹ý>ás¾hÊî5FüiÙœ¼ d[¢Sƒ!YÖD69зº(…@I›Pü6 ˆ‘hŒÊ„øCÐz¦›éoA‚V I¹wAä–渢h¾Uº$×ÔÏ×ٱɔhX™ëhÈ1›(àð øI¡M
+Ru….ù‚ÑÜrâ"PMÊ•ÓÚŠ9Õ.l×v<ÔÕ»‘×:òQ C¡ÂýëuO~€¸&Wä©}=¯Ó4LŽcØ=ù™z )£¼bªííß“ Íé iíSô r#4Ï0·ýĬ*
+'óäD2ú¥¨ ÍÎÃ…<¨¤<Ý°tHùP§-ø2ž}®ƒC#0â~I`íYîdA¨lX£Ÿœ¸¨„ë&RNŽ¥X,zøÃa>ÅŒq® ˆTmYTóÔdˆ1-÷Ñ6øf_¥!5].ìKäí©&"mKCB—° ™ɘ™‚
+cƪ²@ñ(5F+Œ@,±* íc*™«N ÿ\£âIYª€öz>ŸãÂðY¨½ @Šúì'øl=â&´‹C©uû
+ȾfýØŒ·ÓË`α(J Fpcù﹕Š½fƒ§yöW vbåHD¹,Í(PDLA䔯ÀÒÄDšm>%°7n%8Îp©€ÂZ¾®3Þ¬Þd:¢XBȃ-øéÌæ¾gõÑáçTe‰S®Õ Sá¹s}èm’ª`ŒÖ²]Û&Žø¬uU±`‘¡§eO2.Á=Æ9IMžzŸ¶óYjz_­Ò<ÍÁd³‰Æ°¾ÎÆ&cÒž¾îK0P@Z‰5evºøhÉþhì~aÿòÛ??f—†ªl4Ø\ÀEú‚‚{ÖvpÄÝšµ6æ¡h66Ž§"Ëàü^ôO>2e§%œ@I~ÎèL; .…Õ 9 a1§  ŒªM\ŠçÀ#¨¼2°UB6lšÌÅ
+>óz}.ÝQ¿ž{tÆ ¬`^!CÎ. KÍC#xn¶Ñ7u%†^tqOSc†ž%mÎ~!þ¸©1μø¿¶¨ ¸óý.ˆÞ± ð¤T7rÛÆLš°é­|Sò¿S4˜±'¿çPôû›"ÖAÆüš,éÞuã«ê
+e„MÀÙ³ìZLêYæÙ(vÈuƒ÷íôÖi&õóYÏ9šyÌGcKùeÞ ©Èë=†ÙÕ8
+Y#Í
+êL5^åO:n¬mäŸl¼Þ¿†Ÿ„”/qzü¸G[šd%p©Mšô»˜õ¨`k±ÄA«;¤õ%…ôý›’ÏöãP„ß`û†VnŠÆS•.{ÏÍ)MôÌR/Ï›¦ë—ïÅ:Ú$ÊüÜ4ŒV, ‹ÚŽÞ¬â
+4¾ÄÊp"6­äøVL3Cèà÷ÑxK}Àý¤em}›…~–XaþkBˆ¦sä×hœX‚z, 7(1u;ÂrŸÏj ƒñÛ{`¤XTîaÉ1Œ¸ààO^âW ÷5‘=‰.éÚ–àÃP ºN "Ô™þȬ1Ø.ZÏLK@)¼…¹Vp'¸)H¹×þ\ŃÀÁ躟~bÉíb¬HÅ[lΆÇ;
+Íÿ¢º³mü8䦸
+‡1}ÄÕGŠDpHùòzÙµ ƒ“—Ë ³„lßÇ’‹~ž]½sÝæ_ñO|à¿nµ»bŠð}(úýM~ê)ŠèY©5ñ¸,èƒy„ˬˆØe<÷Á"?žàbvÜ솭¦çÈס±É<‘­WŸ°zxC¾`¦À•³bÓõwÞÆDÂ3󷄶@XRõKXõ¦nÀIè̲ÚЉ›Ù§°n0„õ©sÑ¬Ú AóŠ´Œ+ _§7²5qvÐ- ‡xHZå!Ù¶¼3ÑG¿.:uည¤°eê¥ä‘`Zž!ùÿǯO§
+>\§‰:,±ÃÏðý¤¯‘CÈï,,/ƵF æ§(WPŽ4ðªAÏ# ¨y­p(ö˜¿vCÁ
+Èþ}ÍËe¿ÿæ‹þõÿåëÜT K)ÿjòO!üÅjÒE
+Ò0½NEô)À‚"ÜõT„ùc™ûµ('Û¸-U¹:¯Ì\ ¦Ÿîr{Š°<Äeå>C’¬l×5\!3”0O"% cÀÈ:ó-°Y[‡ ¥¦zù¨=Ÿ‡‡#€­¶Œ¢9Œ`­a›ç(LT3£¢üÄ›â^®°­ov¸4~8³ê±K&ÌŠ·£ÓqSòëÌb É-«ÈXÂþ±©ú
+­²^jÂoÒüSµ—;zÇMÕŽ` ”˜òµ".BÇø;,^*§+RfЩ:¬
+Í
+ñ'ŒTì«P>zÁ¯nÅŒ¢ëJ,ŽÛ*ʼtúR+%<gp-ä;®º§
+j‘6²ë*,½ä§½>´îÓ±°Òö-óL³)ZËæ”ùµÅVЃjÎá#×  l
+ŒAwº¡3I ïš.VzÖ‘Ÿ«¾ [Áœ_ç¾·3¯!ã„©0Rí›’—ý3¬$YòŽ¯b¿ IýÌ’Ç"(À¸g”€0¨næôLÞ×aÍa3Ô~XA‚dß0š£]+èêdPW›|ÿ sa³è
+FÔT.À½KôªXMôZ¡ÚCµ^›ÕJƒ7¹÷B^Ç<ý$<ô꣪‚‘„»lÑÂÇÁ-¬ˆYºBÕ’VÙ–ÕîDoHY=ý;A
+ÇBÞZ3nËüšÀx)YCë±~>B^‚Àt­û(‹ÌôúeO$¨µu÷ÚJ¾mÞ¢zíÑ kºOKz˜W°’{[ÎØÝ
+¤¯5‘àe©.Oyìô_œ«G1Ãå²çuù×EiNíÙpsàúñëMÑŽ|–‡h’=®ß.d$c‹Ñ×u•5[×KŒ­hk¸À²,:‘O­ àƒÕ¶Ù^)áÙI!Õí+S:Ù aý(œYQÐb]1yK¤ H‡õ>©ÏÊRðqû Þ6$è )ñwH"ësÒ±Ú:£äÊk/IŠÈbÝ%<˽Ü%˜q!˜&?Þ„Ìæ9Þ ŒW¾é¬gŸ{Ù•ï‘Ýd¼ÈØ`Ó†£¢G«H ñØCW7*ð·õ i˜|›ýº‹ÎÐ"š>¶GÉʉ¨Ð` £ŠGl×¾Œ³ö¾œT¦ç¤5º¦ó•éÛÀ`yS TÎ:éMJÜÌqFçº>RÄœë¼Ð
+Zxk¾–Çp°WD…áGÔTÚ²<Wv81Î{«\ŸµÝ²éÏI-¡Éò½DƒwJ½6VœŸá7Ê8ˆp‚0GÀ&Ï4o»§‰hAkŸ±2¦ “e}—s[bbi”wc«Ãû1v•$Ê™õ˜ýº‰
+?q£ÆflœÈôU´FE)LËf’Ë«lÈ>o²£H²»kJR‹˜ì®xM–¡•5hÑQÈßéÜÇV-më2”‰‹.¼öþÉwïF\«…fLÜeæûIÔš¿ÙQ›æEééj6èpŸ6u JLÊ]y³3ãõŠµò™å±¤Ú
+ñØRÑmÅŠ˜ü÷¸+âu78¨¡×¿sRÉ^‡z0À9‰r-FBñ:®¹Óßq5-ëGy&:ÁÛÚ³^2£X%/¸®û(é+ý0XÛ>Š®º˜Qͯç;ñã.½TSÚï,yöE~½Ä{ Æ­u|%á
+cnöÞŒ¢(†… ‹uMBH"dÀ±ðÕd,@¬7gI¡~çWða Þ"/£Ë§fÅa1Ont_ð;üS;..®½g‘¤’Qb³Ãó-²g̲(æÿ?žÏ´(â1•ŒGã…’òØiRl»Á6„Ù£³I‹ÕûÝUNôóÕ†à‘ÜZƒÊ¯öó;GŸàSŽúõ¨Üê›QJΨ»Cí×»9×ë:uG]ˆÝI¡«òìçïà¤ï‰3ºç_¨``Ø¢Eü”Ø‚TZòš`‹=]PŒ—ð%î…5%¤+Žn?Ô‰™“³–q¨ r@B¡Føx±•ül÷Q 0B—°1D4à’cBýUÕè¯ì º®wI‹ i4¢¼)y‘ôyQa“]-Óñ1»ÍÕoƒK ž½êŠdÞ†Ft§\–dÏ=IìµÇݘî±tŽ2½É¬”øÁ‹:»ú9sEwØ],²Õºc#á’ ¶/wÉS…=Únf9-HKˆºKZL‰%kqÒð˜gÎÛq^QE’»ýò ¶¬üÁ°·B«þp{ß¼øwK}˜ÜV_+úA+Î#ÎéGH¥™±V¹Ë¨  5©c¹ÍEºÂ Áîó ˜ìðY)W7¬xÜ/Eîí÷¤Ò[[Ìã’,b,£1T¨í¸ºGñËþXÅ”åùÊÏÞ5bFµ'vŒÞuù;ÈA»U±bÇ>½³NÆX7ðë‚=°3¸ëËÓ[Ü^Ïú•ÑœÚîÎú܇`»%jz[C¯4L÷FƒK¼:ßU:k½
+ÕÏœòçL8ýzW”-8ö
+î¾všJt8’Õ7(A%<_àÄýz” §!Ê 1×ÃGG†OFâ˜Lù£¦È ee§,Ó篃ÉP©¨Æ<lXB0¹UH–ŒDqºÑ9°mYÓ‹ØìÁ§k-JyÈèìØGŽM/N¹`œGC@ÓOC<­8X¢ ,Â"`>HV²Qš³¥±Å¸³©ÓoÌÄM¸¬­ó²ÙlÙ†áaT²˜‹*Èœhж¥0cdܶš4Œ— 8Ãgu/ˆGÈ»‚x:T…ºÿoAñxA‡&
+0eƒ`$¸[«j‚ŠèO·–~Ò
+4-Ÿ»UREea ,iÈ{#ø| àp:ÁE&n£ÛÒQ\"@C ëp¬ƒHtÐoáˆCd˜7éÞìo5˜}£ÀÓ#·P]›OÈOd½¶ªºµ¾ZJ&“ vd‚¦—ÄgÖ ¡øLrUxxÊq‹¹‹ü÷Vß óéÉœ04tqUnZ¾IJÑ…>߀N6­e(q ø"À FìwA/Û}Ü9“ýºÖY‘8z¡BEd¡´’úýô2`€"—Az‹#R€ÎÛ]ïw äšHèÅ+`^ »ÎËy˜èãNÅõ0u"²´¦äEvèZ¨OCÀŠ  sðSñÒPQPº|€P!Œaè;§á¡°Bˆ%$!‡-ÌÖT‹ ¢!B¢š™ "lÅe‡¦fç=œ29Ž¯·ö¨<J”ƒQ‚)ó¹Ô@ ’ D—38¶â
+6Êa<ÒÈi¹1¹ü»!É.D[xSYG\ƒ‰uql[Á)Øf™œ[åáð¢˜$­ƒ¾¡4ˆSã@»: Q$–$š€Æxó±k,+ŒI˜¡ï­`ìp\aWËÌÐfÙG;rà8¯AŠs¯S°üE3oÅ-M9œÞf¿¸äáàÕE×ÉÁòXŽÔˆe1½`·eŠôL>€•‚"”fê“ß ô=‹hx1tx@wÂPð_Êð=H!߀/輇ºÝQCYÔ<<‡¬¿Kß F¯Òfú$´UA„ï‘WHÛ:<V•Š_êÑqgT$*,H x?´mƒ6à4lfäAÈ£2½ƒpä®Q%«jµ8’  q «Ì7›`V2F˜óf "}†éAŽ}À¿Ìg½0pá´v:#y¢G†–À#ÄanÏÙj0Ù³Ñä×\/í€Î¥ó §ò;ë#ʇá±x¢'·¦ÊÇÍÓ.„œz²ºÅGȈ¦ªÉÏÇ`#,=+eÅt8úÚÖšÝ €cÄâ`÷Y&îŠúqHVö 'f\£­±†Ó`ýêñý‡qÞ…0YàڲߜQ‹ø=Ýe.ú ÒêÞÂ&s©QH|³˜šŒ@ŒE Ãýœê
+ÜrÍ1¾…—öÒõ3L€
+fFš&TëDK,è)5ÜÒÌÀ)@ñ€‡‘L5Уè¬,žEºª†¬<å¬Pæ&K]“ïƒNþ½Vt¾ÓÊJÂøÐÉäß8“ýºÖÕäW‚-ä„'àY‚oK]ÌY§ÓSDŸðÅ^Ú@çÝ®·»ºº÷šŸ©ÀB¼/ÝÞîcNß{”«‚}_m=J©Ï‚‘ƒñ0ŸP­s¨Ï_¥@Ä ¨a–Tü“û ÓÑٮ²Š»5bÁß8c,b^œýXýį0VE ò¨=³ñʈðKœ·A×í y"{Õ8æ0縛Ý
+&ÚÆ$ô¹¶‚Ö—`âŠ%3udHRvvd‚‘¾6dlÆdÈÄ> Ò3"Á‚äèôµmE>H
+((|ÝdšøÖP„²#ó?Ê“ôX­{>pH¡ÔŠ0 dFj‡L" ½8?ê LÖ#sjdÍ[§ë|§¨R—~@òÙ“Ö¢ ÍÁààXÇ9°mU0F:H!÷VQÉFV#«:ô)œÈ‰”ÚQã¦'§‚U$§J ‘§(eo9 Öˆ¡Ì
+L>ÛœzDŒ9–Ú´«qi/ž.Í£Ê]d"òoeÚyZ’¥=Pè i*=lƒëR—-÷Æ‹ð$4‚¸ºTf—†~G¹ë ÂGk³Ì¢Ue+}JácÏăx"AÔ³%lÀ؇Qü‡èòzk«Ø¦=ÈÝ6A;e…7­Ã[d¹v]æÌ ³Sv¾\Vë d¼cÐ7aœ!Ö¥ƒTVd&Z8(ZB­×V—N“Ü6°¦û=‡+þÌü·z1D~ø“bq ¶•3Xçu2n—Dÿ} ÆùVpÜRyŶRÁ906¸×z<¹)Ë~~ÂPQV4Ý·ÂJ¶®Ö
+h$½oah©ƒ«u*üV¹ɱíDzÖ'Í
+Ñl£‰‰x&EÜ|,Vé¼_"HÆâ[^¬cys¾/ðûZ2à·ÇÍDÚ9®ûý…›: –D'´ø£‘ Faæ¨÷õ%¦]+v >Љj2“<tŒñ¹ñŒo ©G]dÅ„¯D¡„©¬áÊZÖ£¾NÏÀ EÇxÔ7Úú"õoô×ù-7óë`g~êH4’Wæ•_ÿ@„~õûCÅœþýo“þktñŠÍºêèbýGœp;B„G™ô 3Y­úR46Ý«žX´J®¹öŸ÷\‘‡-Æó¥Øiâie"5>°ñ}èt[_2?HÅ$-ÇÄ:‹-[µÎÙ¨0ð¿u\f<ª’öºÂ•æ¯º%(É~ãS%ÞØT-߾Ϣûˆ'€ßÛ˜÷k¾;I«OöWrùû›²çw$¯fÐüâ>[SD? íxàó©„IÎÁGÀ;j죠à¡4-ÇÜmõàç^wÉÒ„†’ØR@qLT®;Çž«Ð$FÓ`Q„‘† J gã¶Ûó$†ÔÒ:kI~¢c³^D£®ƒîØÛ͵Y’®ìä¶ç2’«­Þ2ÿJëCa™{–4ÄßóŠ 䔓Þhd–p]ïÌ"Ï64/#èÇÁ÷.²Ð<Žá¦›¶k¬p]ìΨ/%“t®à%J
+ðïçpÙâºÎݦ·pëÂBŽ¤XÄ­¶™¾\xz†¼ºóhö¼Š©4©¶ß“ßD’D/ãü ,¾Îå}¬ÓЕO¡‡²*èCø…ºË%Ä´¨øo︪1mAÔõÈ*ŸQº.ª
+Ž7Î "Hñ³¯öuT¼%§\iÖ(²Å„ •6ÞĬéÏéË}ÆÂFò^+’߈‘ õtï­ˆ á%-pùtΩô½äK*ýP³-–{=J¬‘§ëѪˆ4J
+­oLš’\oK®K^Š˜ú=ÁÓo>ó‡!ÿPòߺøóëþ(úõf¡sé4³o£…qK Oœ4u¹x|ØnmÃ[›QmhàT”²¯¢Ÿ;¼•u ؃JÛ4¾øõ#’êèU‚CM¡‡ºß n¶ï9[})™né‹z|?ÆG 9ÂýÇ*!Sqê×БYk±ëQØú’o[ÂÐc{­}ßW÷Žc‹­@1ç Màp ‹ ¬zÀÌmGixÓ˜°1}p«ÑM]ˆ½¶B¦Ò~;*²‰)¶AJ§˜U»ÀvN“Ý”Úãñmú( Û»
+Oÿ”XoÁÖ[)óS:“¯{ê>=ó[7v(ž9ç ·™BjW‰ïˆ#ñjK¾úÚ—"|‡|PŒ»Gw¾ÝÞb0\Çv¾>
+Ø`¬Gº®ZWÿ0#0J¹ç
+yÈÜ$Zѱ)›…u_™Ê0Ì0Sh2^~IÉ:¸÷¦d÷*IÜ £xûrÐlMôùÞC”³(æSâÈ øñ2lŒù[üu@ïW(òˆY
+#:3ã6¡õ½‹T¶ê˜Ö‡ˆTœ*â}fyL]µh÷”Ö‘tÃÄõé_ƒXûb¯ûSIÂå °ç$Eø ".Ⱦ×Ƚø¦áZ¹Êzgä÷u¸F¬Þ`“‘³ôY„€¾/¯aÊŽç°WÀ'½ýEíNìÜ i¶J‡+d—8÷‘A‘ÝÖ6Ïmý­9#¦¥¬oÝ™°´ ‹ôÇÃ_k.ô±g§pç‡cTÔjèÞùZ‚;êÕ÷„‘9_Š¢¿ÂÜ{Y/ÿ^Ä@ˆn§¥-ãYî§RÙ^V[çkö1HjuGÆ<ó2ü;ǿņ`ˆùtôíô
+Ø—ó5p´Wz§37ÜsB‚‘®lT‚[ÝR¢O¢E‹óxnÃXbóŽ•œŸ²;¤Šßô¿Ú“I#D(¢mcž¬’<çvç´m‚­] ag^5
+îïÇ*‘’˜‚—ù¡+þ62I‰ïU´UX¡`¥7™þÎ ‡&ë:ÇpBéC&ÎX¢ÝøÄÓqOÉ?¥OÅ#[ý%Nfû%£œ©‚aƒ·Ëç á©2ÒãžØ²GìÉÁÞ—YŠ‘<‰%)ñâ{ÑK¼pŸ=ÛjÉÚ²¶ÜêÞ¶á²ÒsÑŸüÙòÇy¹âq"JclúòµÑ­qce S7µ,®)¸Áö@uRšcÞpEÌT0øHù½âtÇÕ·uo¥~ì¦Èò5çœ@\Cɱv*{{ô~ ÿSXWª?jŒ·l4Áö8³-ü‘Ôéa)aäÍ‘ÊÙ¥¯ÌƬÓOW„†Âšè¾$¢¥ ªÓÁ”d @`ÈçúT¤>rÁ³ŒÛ0’5‹Ù=קJ–.¶•6?‰ï˜YOYo7GoÜz*¨b‹•ôë§Â]=¬9î2®Ž-Š}Xíã è”è¥ÕóCÐ@ó’Ñ&Ù~5o£Ä°#,œØzSR”Ó}ŽU{/É*žÚÜ®«<ŸúVÄ
+H‰Œ—KŽœ9„OPwÈu“ERåÀ[oçF÷Ê^Îýç IiÀùÿ…)ÃÝ®G¤D‘Á`У<Û%gwŸÃ?>ê|š÷¬¥ŒR²øã_.Þ3Ú˜5u<çðê“¿}øÜlÖ<,ù¿ 2rXµˆiéç”huôÓÚ]5ž½êë3ZÆyrRïaüFç´ÚÚœ•cfžsjím–ÓÇ‚dÌÌ>{xñ±!¥OÞÓg‚ÜW|KæÃÖ
+HÉ Š:‡ Þ«¢q#„±!™|“5rÎ||[ œ9‚GL¯Å6(Jd¸ó¨Ÿ‘›ÖŒ€HÚë¤â^ª7#+A¢Žiµz?J"xy³…h³Ö< ×sHŠ~zŸ6w=úõ*óõ(Ê9£Göl:Å³ð„¢°ÛIÍðQkÍ^³„ QÈ)¶9ê9e–(ÉïgÄ!N4ΈҭB•j•»¼“Ž©sÒ#õ¼fyJea$”DǺŠç–RKO·C-x8Ìbõó¤®Ò¶!®ýMãÔh½¬ªX§âY{-mCÌZã»2ë~Ôl…”:Ê|bâ‘;±kóäP:Àb¨>w-U>(åhó#EhôUŽ|ýÞ¬ŽJ­ÛªÁ$ ¥ôÒàE=‹ëЯïg*РHSÇP:„®eÂõì|KŸŠu š|ÚÉN1é:që²®jƒË¼7ÞÞ^çèj‘­åª%ìã,ÔìDÓdiF-fuxpÔãM7/#ºëcûªK'ÐôFÅÖ1ä~ûPÔ±Éïxéê])4¡ÖD¼!1Pº¡Õ­(BkºuêÏI=í¾Îé°qŒ€Ú¥ž`hƒNgÀÀ\êX”d¯”pCÌŒª 1°o_• 9ª³©eVÉ;Lr·Q0û’-zw9ÿîsú³÷I›Yt½ó£¡o¡Î¯WÝ€<{JŒR—ßDcЙGF] ½yU§ï¨05h½£×,e0èR¨Iˆ9*z²²Ã•ð
+í/tç†ôÑ](.ÖªVêöAå`In}!Ã4.6+D3£àÀ°B˜£¢}Q«5-TìDCP:ôoöMÀJš¦œh46˜“ÖÛ7ј­Zãý/L$ÔA£ÄHé ‚IŠ¹þÖº‹H<äNL)1)£T÷b’Ï©—ÌCÁÇ÷g2$B1®Æ—Ao×ý(]s½Ž¬jkØ@ÔWÌi‡š»oÞ„¶ÓPNfþï—ý‘C7ªÅ¼¸9Ê8%Í ±AýÜ눉½Û¹‰¡ Þƒ¼ÌI ˆ¥«´";ÜA :7 ¸uCC›ç&(hkãö¡cÔ áPŒ¦>Õâp¹ø%ã›9JÁ˜[¶’_ cð
+çä‹´·kNO.’ë» ÂAÝNïŸ@‚öåá©1qù±®J9'òÎÈOΑ!ÚP¹¼¢Q‘mÑb!ЉÒðû&)=®ûX÷Mô4ÅÚïQ#‡…½¤%NËøL•v~ j£ÂÜ0_I¾”s(Ì8”›axz†#‹æzæ°
+Ã3 T~á ˜D0^ÄÑ>þúÏGyüûï¿þûAÉŸJ#](1A2q0 D¤k<~Ý‚ê“q‡®Wæ.ýüP}ʈNÆò6¿ 2FÊ„ t½î‹ è¾9¦Î¿I `†CíR‚`úpŽYe6$Q"tŽ¡ ‚аLyü‡/‹ „aѤ0¥‰) ‚L(¸Ÿ‹¸`ˆ©Ä³å¬cØ1|.¿v65rщ:c
+½ÊKPKé;Û0ĵ`tèdÔ¯O@XÚj ûêG LJ1Ûžˆi¥ªêm\ú‚0+9rÕƒÍÖB5¤Ü9æˆÅõm+HhcÁ¦^Ó×9œÚ
+êÝÚ d_…xæ`:"HöÉ9\KùC×~ ã 祖‰þÉ«ÆÒ4ÕxZ»ÍÐìÆiÎ%h×ßAþ¨Õ¹êŠÍr\Šäê6›Ë0æÄDÝ¿ŠÕ3dcØ.í$ð=;€ñ„Çuîš¿ç81.&/ÄŽ™·¥ú±/z¯øõ”wÚÜ„r¥ßÍ“®4¾¤æÚ o þ¿MõmõGSCÞ‡€ë9Й:õ^#¢‚(«¢õ“ù]µYÈÖ±b¡ÝŸ€ô\‚bøHÁpth]ImKð¡ó,¬Dv
+›—«Õ ÏM/Yƒ~u©&A±œ_V*HüØ8[R&»Z⻈¤‚ÉdÈ
+4¦Û°Q!Ùg€ð`”꼨wae v¼ß)‘Bô3Rs ÙA÷°Ýd.&Ñ% Û1Ï^CúX)ðCy¹žSÂ3z‡¡v¨ÅeŸèQz:ùÔ²Iµ1 OÏz69*z™üWXÔ”7v›2Oš•nÌÁ°ÚÐl<æM¦éìvö—K
+Q§8ärª«Õ› áãÉÚæaž.¶ô^ +å#ÝßËìQJÓnÔý†+_Jl‘¡qlUÇ‘¯ž¶ƒ—÷ë20 ãðf
+ZÃÇü2E\Õ¦ty:DÜØðU\Gˆƒˆ‹ƒ«GÚb:½Á'núÀŸxÉc鸞·€ÁÝþ¥?,çÉLBjpØXyë2³áþSpEú‹ž†$ÏÏÎÓ'÷NéaÚñŽAžJZ‡ŸûQú“bŠ0Îû 5,Zš;Ž-T3)Ùj7q®ô‰LýàÃ\}èÒð;ÄËczr ±žƒ×áìœ|ˆ±º$CÖ²K.y¬óÿ™[ßçŒNkTy‰$O”ÀN ê
+9Á^ÌÈ?Å¢Ð
+8’>û>sÊdÊt`sªZÞa΂7ò%…ÁgÅ ¤¿5Úyen3Ž²øøÚOjnœ¯Ö­ÈøE°+¹eMV…r±|äLüÍ¡— †&¥F@ÜÚ@!GNŒ$Øe+‘
+ñï\ôŒ½Ÿ8ÈŽžß¬dK“ú@/«ùoŠ°Q¸ƒźþ3܆ÑSYØ`ûù¡ å4†!Çœõžò1uÌpI¥PwNJô)ò‹?éR¾7çt™é¥ØTßÝñÇ)r£i¢¼
+nCAƒ,/>ºC !¿Â)m‡=ŽJ^FåŸzuÅ}k^‰nÓPòÝ:A£7¯ª þ¤3üoû§îÝ¡ˆ×T£Øõõ˜ÔC‚·÷¨#‡³º•Ø§î3œóDÎã6¯ŠŒ?g
+bSúG¸tNÄÝ=jþi©~_«—æ.´œ¿ ¨ûÛæ q3>_"~ÿ;,
+8ãñµ[Í×nd”{Å8ó V¥x2%¢%°xOvñ¨ˆ9C¼Y!VëUX0ÈŒV¶6ñ„CÂŒ-@|J&=äŸðöSðQš193™”OlUÊØpÁÁŠŠ„Œ?cŽùœÓd¤‘œ²–ŸÃ3hlrîö)ÒæfqVö<˜6E
+ðÈU3W÷ 4Ösa"öù…]Ä]a+f‹+æ`yIÕ­wAM#<`ÜeÙuOßø›KC!˜Š–Ï}8µ*ÞàÚæÅì i¯%HR±umÛ`ÿCŽƒòè´ P.à—‘Ss&úM¼#TÚž9SÐc¬8¸$ê|sÎóâcf¹B/™ƒAƒò%4œFó g\»¹É [Ù ôŸO1}8‚Ïå¦+¤6KjìœNø#ˆðWÕÈK BSyS²
+l°—‹¿x3 6W¬aš-³9•¯×m A”Òe“¼XbÏ©ܲ­7¨îð Fï¯h.<ƒ½êÍÏ!ŒAÈ"×:%U)èìKqÂb™€lâQcûøë ôju†eã[IJ]ÇÖómt…±~†Ž¬¡ôu[©ažœr1ãòP›ÖýC"\’
+,â >?¿)a!š\Éñ ö©gÑ&ÒâÛPŠ¼Ï9GÃð"qâFà˜½ìÕ‡d`GÖÇcS'" aµø—ï(3"ñóؤSvjÌÆWå.¨§ „âRI:æMµ‰Òµ-sB.W€5’Jÿ¿ž«(˜`‡Z<ÄqÓÆhØ”]/mÁ®&¢ð"Þ8!–y•#Q|C×Z;ÛçR9˜Âö¦¤"f.Ø›CŽ¨ ¢Çª"(Æj·¢/+bw°ï£Ð[Ï•…;mÉtŸÀ6V­ú9âû5åE‡©(Z 1Ç¢øÈ“˜Ȥý×dö §¹¼ƒú¶¼{ÃìÚâ8›’Æåë\¥Ñèo^oæùRbÐá#HtŸ¥ø˜Qá˽¼ã%I½ˆNÌÒÁ&Ð16çQ4
+æÂ-¶ËÒ1Ö;i=¯æ€Å®4Ñœ*žV¦Ÿ¬oêóMÉ‹Q=>üã¯_øZ­eÒûeìDÐS’n‡Eͽ ,Æ´CTÂ7îµ1oÁ²«ó´À¸Òý⨠‰ 0æ¥Ñt<Óô©ÂLS&f1(©üŠ¢a-s‰ÆAÉÍ
+ð#u±‡pÅ®¨
+HJæKóðA¶°áðìK?¬çÇK+ylpðÛŽÌÙd¯è[hP Áuê£ù‹§þN0* å¸f9,Ž}¨a1û&¡JDP6Düë¡zö–ˆY‚ÜÖ:GÑøbìê‰QîˆqEJ$Âäž®% É)ѧèmNb‘"6¿9Þ!=u¥*+*Êaòî}•h÷΢ï’ÖeаuØÈ¥•×"4WäL«¡­¾´òú1™)™À”ä‡w勼_Ÿîص05?¹± ¢VKLSmy¹\‡/[%k•œÖaÆXL0eÜ™«Ø‘Œª%<5 :­¤#tÁ–Sœ”G=±k¢Ÿ }ø
+&YN²÷sõEy@º™‘Ø„ØFê»KE˜ŠúQóùnyo‚‰.½ˆ¶Qgv]Q枣öƒK6ܽÍÐo¯òQ/
+T.˜ `wÂsže€èåzöÂÿªIEt[
+3ÜäP¨Mw`•^pÿ‚Ï0»™9Ý%'ö²Îáp–¾cTCó¥N0a b}ø›#
+`‰®†±×ëK"°!­q?óøÛJÿYE=À×Ì–«k·Ã“„Á)C<š€·fóBo ÕA`Ȭ:\.#”¹'|´çžm´<Ô<L8ä]©L†eSÙh\^à$´Ÿ¶ÁW=ìQ0e$¼jïx#¦\y¿¶ƒÕ
+Ɔ$Ç+ú¸)B+«„cB) ®,p 0ˆ=lÈ HYæÔ`£â[“ ­/Ô³_XÉÚ°#c•ŒÊ¢°ùÕÌð!¿¹»ðh ‚"§#«°¡;²c똪€òa‹âÎêÇ‘ÿûâiJ™°oÈŠGzÖÌFÇ@4™Èí.ŒÆ@°ºðâÐßÆhô«1ç°V ‹2uùlªÉ¼±+=à\¥esK7hÑRSÄT3Æ•ÊxDm.ôÕ‹é<Nœfq.1æ¦$DA›ÑÐq^ïeY%Á”¿Kbì^Ñ×q¸4OÍÍW¼››iM`Šy“¯®€¾"S@d±wŠx.‘]çî"k1š;²÷ƒè£8ÓÞ}!ĤMbv5P/„È
+p{<O°(ŠÆèä’Ö1úv‚±·¢þ’Ø>÷—H!ò¸¨{öÓ•WóƆ>eüª/Æ3K2Âbƒk1-Ö‰¡fÁŸ*I«Ù"Y"a妱eìL~àåÌÙ(¸@2…‹QWféνì¾@B[CÕE®
+è»ÉÊU3ÀH•Ë€?2©HÂÈ’~¨ÿó‰_pj‚\ˆp)ÂdÑt2„yŠPèPFUâ¡"N!°iOƒ¥ÁÃók2
+‹y-ä”À¸ãÉz‚J¾“òpÏOÉ#®]<‡¤vW%ëQ3 ËÙ§h]á–´,ÊuŠˆ¡èªÃ!žfGÿØ °”{=&üEªÏ->«â±o‹®gè2\&þTO—k
+.Ÿ
+V§r[À™frëúÒú# äx°C3$ö|Bçhºœ‰=H!züÉÇÔ3UÀÅ8WÖßY9/±‹æ_Òˆ3a,¸Cöaá ²…ÅJ0—pj+jø*áû©Ã³ùØ~Líágl‡aS%xm¾¾…FäÕreõ‘¯1CG9…/|Ƈ
+þ]²æ
+Ô}zq?ˆœ™cÌŠÒf¿)á@ú24Ñ%GaAÒd§èó¦HB1 µœ-´³­‚7Á£7šÀ?ñ*¬u@m»Zˆ(œH CK|Àl›{«äJyÿÎÇÓšN`2£
+±<XCˆª™(Ú/\0¬7Peã÷
+߆ԳD¹£–<´[˜‚’JˆV'TÂ{,Žf&’b^Öï6Ç)ÂÁ‰E¼ü§q̻ɀa\} N
+Š’T¸oþ¯Öi%Ÿ"@°Cá£ôŒ!àRGxÁû…«SEÞÁÒ´õ+Œ]
+"¦ "Šh`¬QñÖ®óVô¹Š`®¨™õ¬§²XnJðë‘à&ö¹y8·
+ê;{|¼Åª€üñÝÞé¦D世†¨Øv‡ñµ8P|_rœ…?Ì7‡Ba ḧŒ…êC¶%DJº¹`<·AVØØ&£¥’";x7Zh%Ò/À<ëM°À´
+s÷²,0ìɨ”ˆ‘Öâ3eÇ%;XvÙ|x‘X³ôj
+© ÐHBÖ(¯5ìy'Œâ´3hzx×…-xq^^­w÷Ùpd5K€ôö³/í»â›Q&>^ÞÞXÒæ…m
+E$pAªÞ9Z²KGßãˆE ˜¼Ã(zpTœr$8€÷†Þ!‹e”'Èþãæ^OO
+…äËÞöf^¶åM§½­ËOÙŒPIŠk5/ûûV´V¸±!Mi›<8O-+÷õ-d»œê®€új€H±'1ò@ü+Ú´­ÉC
+ 0JýŸñrIŽc¢è
+¼¯ H`¬7ô.<•ö?}çR”¬®J…zâ+²)ÈϽ'÷1l<M«'?®ªÕÈ *ꈨ+ªæºZ Sø¯».çàáuÔRØ ç8« Z± Êdõ( 'Šü1-
+
+>w zåNºz*Xt|k¶NÀdYs su‚ÔFFvÉæžC&Uû°¬;u±zCð„i5—8h<˜AfŽž Q<¼S‡ï²LS ùö]=ÂvÉtÒ<{¦Y€jš&K®‚˜"è—GpáQxM*¹aIÒÐ|Äð½"à†å-~ÐŽ åù#!1ÜVÖÎÙçDLèŠ<FƒìØdÆ7¹ìknÙ6†RЃ ÷WªûgÃ^7ñ&gð]êyñ‡‚h¦ŽvÁ>"ã'„Äá9áa3X¶º ž‰Ëq÷-ÈH)ºc-ƒ (³’@sX’,õ–q"…Ð)XÍàó'† ŠÀB‚Ó
+IZ3+ ¿{€ñ;¦:i«œ¨v/Þ‰æÊ‘zôç› £kJÓöÊ7A0+œßT—D »IÇ êoBÐÁP¶ÔM+˜Š ÝÛPAõ§›,|ç‡}GmÉ
+°òà éOö‡še@;ÏÚãJ™Ð9™}ÂÐVL.Ä«ªþ¶YE¡³uE|Žå“¾¯þ¨Ë{p<Ð'
+ZE“#SØÄ)ƒ Ãa ýü»Ã,aRS£!”|’ Vª`$]ÓIúø%ÎÀÕ9,½ U‚D¸Ñî~ð†ß mMß„0¼…‚à7EÛPTŽ)‚I
+S郠k3rå¥îpðs®·_)MO/°&±™ç€=SZÔÔLS!ÖW)¡}’Ü^E¥¿ašºŒŽGA1°":G–MƒA•4o?eÚ8f‚‹B ÒÆà2eî‡'ð]l…ók»ëÂxŠ‹vJ<NÒG±_ÒF– ‘­›V,6ßãE¾'Ö—”
+…äAi2T‡…˜]=–Ž—‚& †¼Ãšu©p„èKE®”âIÌ
+¢¶º$|;J ï
+,~T=å¶Ýà¢>ú4ãbÁ´¢•lÏxz‰U#=8—L<¨FB¾G†K¨Iö•A5øœ¼¡ñ
+Á\”÷2µJÊõ8ÈU° è†o\š­‚” Œ—ƒø¸×:*eGƒ @’6º•Ç)CÌ(Ó%còÃ1xy2ì(P»KëÍ¥¾v<{œl
+KûpH ßÕƒìd9‹Ž˜³}tÑD“VÕ1¬,Ìm‚£ìg3å$ÃÙµ;%ar|ÐÒ}º¦†Õ–…PÝkÞPÚ±'†ÿ(èFÚ/ÌéIÚŽ|Àt- S¯-$mÇUQŒpR¾6ƒ B((É*øDý¬ÉSƒ`}&²œˆù'p<ƒ¶kÇ…© m
+Ó-
+’6ùæ(¼ ÔBûXU#gxÖÚz@ÚNñ-’@ŠzI› —}g‹ýÄñ'Ò&¤añÒ+š5{@ÚqÈ…´£ iGA×näÊSvÜŒ ¤­§WÀˆÄÈcÒŽºúê™Ü303j¹‰ëê™Aíož©6Pt¤&ŠŒ•˜„\±?4Òv
+RÛÜ{!m‚èî
+”RéH5!UØߨ#VÒV=0‡h"yÚaÌ¡cåx)èÂÙQÐ…³¡Éy+é„À¯œ-Íä7‰£§c&nœ½ÆÏ ¬Ÿâ% 8›TÌÙæè)ëhkÒ‹%qÄÓ-m‚
+]A ú˜¾9¦Î1«$t¨œ-1¤M–aè5äl‰*¶’š‘#Îve‚p‘$Ý\ÎŽZðÆÙI¸‹Kvhû³ ¸Š©ü[fž8[ïb-P&«l$•/¨éâìüÁ±‚e-
+|]¦Û‘œÝP!¯ƒ–ÉÌk|ÜÉ_ÑÊòü9‹P†)Òœ|o‡`É°,;žÄéÃÖqf@ ®Ž_CÖ§¸*ýI£Ÿ3pIò ÔdAŸE¥ „<a›4r]ý=âʲ8Š‰Â†ì³ „Æ¥PÒ•eæ€gá–’>“Ê ÊƒõtLZ¸ÍäKµÂsRbüÔ|ò¯Hâ…1 8œä[7ä×¾Ì~®z2&]Y÷u F™~®uŽ‹»DíHí©…É\›{réÒ·_QнßÓ#9
++ÒÉ_¹O`ð±—‚.ª×%èDÿ6H —¥lôĘAÇ~((ó,ÑJ§ ?"DP»ÆÊ‚ž%¤’z˜b–ÙÊ®3 ;Ï©âÚÌE“ëãQµá2¯ƒoÙÔ9m"¡
+™†ÜÊA0íPyŒ+Bpè €k·°¶Q+ƺ¸$†$éεԂÉ_<z_é¡<"}aa‚Z€NS`Ò›¥äƹ'üXp¯½)‹êÇÎ @ñ?ãõŽœGŽøºí1F¿`nÌšr÷Šõ$sï¿_¢Ñ‘CŒgþ)¢ª¬|~YJŽÞÒžó+ßb½K;Ë1ÌÂ"«HéìÇäƫϳw×;âDt÷¶~P‚¬†Ì¥÷—&E,
+Qåq³B)É@W®Ï¬JË(Ü…îç¾à{Úo~ñ•SïŸÂùùeÛ“ìØŸX Ýû)XêV9
+œë­Zùt}¯2)éŸjlü.>:¯ÍŽñå᤽üxÔ𸲡æ—ØæOO,ËÂnùe‰¾„"0ä ŒÍÈ¥%—¢õϦ %ü\;«Fr0Hçì:šǾœÆ6Å wºi<ô<ÁÔñKŒÐ1®¬ûz3ëÍòmãÅ1J „ÐwåaR%aʃæÓP/€3§ ÄÇ­fÂÛÄÅN»”ñ8jrÁ.Yš¥"#ù EbØ ÚQ×c»3ŠæפÍüÒ%˜įe[ÖcRò=rjj®}v&JT©éìùĆDBŠXËš’D¡’&ȳÇÉŽºÜ’ðŒbRôλ(*p„0?]äp&_bÚÚ^¬°$Yž½¿8™Ðed›òãKw©
+­¡›eð†’•;¢0ÚOøǧDOÉÏPÛâÏÊ„É”\XáA.ƒìlèš½ÈbLK(eüŒáüj gE@g½=ªÈ>áÃY X¾ûº—~eL¼¦EDå.A6d[zÙse9wÉìÌw½‘³°Î¬G“Çåå:¨VÔžÖ… Ú"û’™Ö ¨=Á+—[
+¸D¦´œ±Yã2«QŹUÚ•Žíwgõ¡„­ÊfÁz‰K'ˆÞ164P9ä?ü~a
+FE9§)¸àËlï¬´é £ª¿ó¦c‰ôûCï¿&E?úÊœ™U¢åv <ÐGL½—÷Õ³–§¿)ZóÌ»ËM_ÝRÒͻމç Ü ºùp×Ñc;ØT «s´Çº _v­U뎧ÿ8Æ}qØË+ËñÁ0_•ôÝ\#ü•+ÚÎ:+‚¬–P¦É®<¾Ô˜Ñ-‘5Ó>y"\ÏZ½h=³ávWÝøܳc1qà ^÷ð3Ð!
+vœCŠ£½šâ#H€½œ–عUO@<!áï/³šÚ÷’›GœHáÇ´ˆ‰t5%ñ§_˜ 5ãßžô³ô ïÝßMðB²<d%þ¾DgÖg-Å-ÚæËþ}¢4ã7˜1[]bž ûñ‚w]"ún7§ÙW%}”â›—
+RLŠ >÷¦èÇgú”Ζ—+å°¼÷3²\,½ÜPÇÝÒã+ßfÜg\ï1)úöAQò
+‹ ’lk¿Ùû¢µÏš¢­¾ô°àg#!ƒ°)f<œ•KɺÆÊTRú¸ö˜7Ìæ>YÏÔ”ˆª¸VG®–Zö„AêÇăXè³ Ä€ž!<ÄØÊKÚsÅ<øߥ]mRò½÷“ï
+÷óž-Ù‚àv²PCsL
+ãYY,ìß
+w8} $øýž5²7\@‚ÞPe¡ðrê'oé^Ò ’’E+VÑë’ÀŒvW-þ_„W›ß*©„çЉ =£R
+8zGpfLªœ
+ÂÅ2ü!â/+Q|²˜x°ŒÐ’O“2‹¶üÇ_QK͸ ”«Ä;<‘c1y~»¨J$´m¹au Ï"觘h³Úé\Š+ãË–“.&”pÀF Û.XX¦ëÂVt÷W}wm:èµ–eÐ%]×
+ýUA¿•Øõ´üå5ivôŒ‹ æL Ðo›¿”dqà 8vÔžx–dÞmÙ·fÌø^Ûô(Ü9 b+~
+ò6ŠbÍÞ‡ŸžEÁ$XœÞå+ñ
+­»ˆq‡–|? Ç÷øµH㪠:ÁŒ¥y¶´°ü†²Ž+™•¬"ÕÑr<®$¢K’…mF8.(ckžŒ|¦¶™¬tv'çëÍã‚œÐnø‰5ÜQ‘Hnt(a½.GÒøøÆ,T²Ð"Qºê­™¢S>ŒM²í
+Šž“ËuËÿÖñÝšfþü}ÿJUYÉ8 ,1Êìê.Ðz0ŒéÑT
+ïS±¾¨
+KuK°¹DÏR f›È³¨ÚÝè'üÙþ´"ÜãÆ„ò?©EŸ§‡/B'JŠTÂß$„ø¡æ—m‰ˆfŸG£@Dû\ýËA ˜º™‡·‡ŽŠ[°äÛýO]¹ÛÄå*1 fª8‘ëú@# lèô|S‚‡çŸ=ZéW=‹¦,
+Ž©žs
+Î…q&LÝ;\ü_¿ÜÀL!¢ä®±v¤?ÿY
+v¢ò17!E´´ñ÷VB6”ãÅâû¦õÂ¥v¹åS‚!éâ•}Ñ0 E¹
+eIŒä`*˜Õ™ûYñŽ3~¯ùÖ[uôƒýÕ)ãY¤²ñ °“œ¦À¹ò9F;é%÷¥ƒ¿W²D¿ès
+!ÀàU ‰üáþ¼„5ƒ(‹ê”ÅŠl:¡i1ÅÛÖÏňéꤸtͼ„MÈŸº}W‘OolHü¾u™3³H¯¾Ë9>Ι–íˆ¤¿¦MÍÈ“IÀ<hG• I?…„Qíêj3é)ª¦Iôâ-¬PÆ-¥iW-¨”‡¼´ÛcŒš\ùP, Kv¯ìôä°}üѳ¨p¡~zÑÌ„Eý¨kK”sS€èó¾·/ÆÌ4¬‚9i}_ü¢ ç5Ϛߥèórek° ¡O¼`Y‘ûÏŠ: '—éÁ´X˜å=„–Ñí¾b#¤û–öü¤[VøQ?˃TÇWÓ0fº{
+4°õms¾<Mk¡Ï ÏÁ0Èž›å¹¾âÁ꣆¸éS‚ï/< pBE‚Á]<»uúYx^ ;Ð>´¾3ÏÏ¢gê Š^S§Jh0+PÄ›QŒ¡¤Òâ)³»o¸x‰1CÂ
+‚Ì·Ç–¬¯¡…çMÉ{4¨lÒ¼®% Šg±îFÒø>ä<E¯Š‹$©SÊŒªãc~†×}«H{5¦.YM‹`î‰P°îȼJ”¾$.LØ8Þ[Kj±@%\?
+‘@éÄ 7|Á$PN‘ºÙ1P)à?m— Ä©=Ü™ G¸ýæ3çQ$ˆÓB‡çzsDSÉ$åqKðÕ•ŸµIÀ‘i réÍ(_ü㯨¨›øm:{1C¦Gƒ'ctÝ‚3`åD°…ŸþêÒ5ª9Å­›}jÔ HÙÐ{xŒÄ¹ù¯zK°ö]<zN£Dåíæ062§œŒ>üY@ƒÃXEÍüNŸEÁZ<¯ûVÑ˪ƈ):k´CŒ8‘¾v½"\2° Œlëé-NXfg]ž5ªÀ-ó0¨q;æÁ\®fÓ+˜Ìf³Àº&„¶@ÍÆg£h*Á¤GEŸoŠÔiþ‘+Q¢oúÅ›ˆÄ‚'óØJ”(G´ÆVŒkrp7Ø– °ÚYeÙM\l»€þâsô]z ü¯o‹‘H‹™V]4\½A4ñ«1#„´ž:Ñaê÷1|P“Í&ÏX®¤jêÕŠp京8.Ço‚  ^®Úyz4åÒ‰"1.ÝD€äOkã4úð’¶É^ˆ5}o§„‡†0‹gJqOcwZê–0Â"Ä‹cÙoê3,RÖ£Ð2˘í»RìÛ¢P¤O‡1ºí|óˆ–AZ&£U½ç|:
+3ÎèÆL•S¿°z8¢ë›:?™ôŽU¬Þb~Ó4ù« ë OI_öVpÏÎêWŒ“ðc¼oú°”™Wß›Vbz*âyŸ%¸Gœä’®Cƒ1!7êÊ9… Ôˆg_*þY7H]ÿþ‡ëò‡:Lj{Ö§Z›é-e±1b&±¬ìO
+É è盢"º•p2سHù[¼ Sú·•tÀ„oì%Ípý
+tÊOzåW¹ÅP¥ÆwuвCÄCW&Þ„†îM†(É—xT½ðÍúeùH¦Nê¿àõˆVOœâÜûš)Žÿ¬ë” !lgmù¾nàCµ:cøUjmtl´ÿ/w$;ŽŠ®€{ -ãEþ?æ]º³ÅxMsö?çY’X…7Ý!J¡h¢+3‹û©ç>Ëø´a›Êøî]
+©†?4µÏ"<ó»ß¿0tá#>èZ˧0û%Rþ_~+x‘¢·#öÍ™å`yqû’½Ðà
+è¯ü¢æO‡ÿDSc^RÊx0]R=ÖgÞTþí˜ëã¾âQE¥:ÌX7‰X%v­ðG:Qº}ã¸`n«vS°~p2¬Åt™O¿Ü
+[IäÊþpá.ﮌ—®tPµxÅâ…$¤´Ä¾ã<]b
+¾ÓÄ„4Bd /³_]*¦ÓV/oµ_(&Î ªÊθæP‚!D~:ÒÂG%¿Mêõ()‰˜ô8¼ #72Vü(¸ž=ƒIZÓeü¤{s2Ía¹zT¢ç[±±‘êVâ ØUy–-µ„ßY=‹V6\z…ÕÛmžø ^ÀøÖœh~ïð§+å›Ç :“"ÞÀGÔ&š†¦Ë›• ŠHŒJV¼¦55ÑÀÜÈÄj÷m¢ºR6[o0ö4ݲ¿G(д0{dô|ˆ€p›eŸ‰cã8# á¡h“Yƒ ‹çétW3   h¬+ÊÔ­¤Áx”b¢£Cú,¬î»ï \E^<sn#iÛÚ˜š’‰—ç$ éHîN)©ÄÊ*)7òWÍDöÐ@$à¼zá0Ù ÿ‡]eK¸_\J=EøŠÖÊ2ºèðqûkh/Œ·?VR˜
+‹vov+ñ—7Y³¥êo¿3’Œá"ºx"¼?Ë¥ÊaIk&9bä/ÛÓŸE7
+¶vÊ?b_ÏWLŠáE²jõðTy|O®ùx/À†ëD›Yíߢ"¬žÜ¶ˆ{AoŠJÚ,ñRê˜~üaén×ãNù¶dñá÷™
+ƒÀs_/'ŒÉ¤1¼“Wñ@²˜èìOgU‚È:æïñÙâgΤrÈbƺ¢,j£„uĸiïÞ¥¿H¡CKÿJ#7vŸ†á½ÃÞDà»u8*ùmR~Ä?¯(Ú0ÇìM’¾ÁÁç~…EÜ\ªÏê”:„w€‰Ô2YuéJ¿å1.…e#hAÈfœÇ(Ò_ÒEÅêQÓã¶ÔN6gÍ(G=ÿŒc‚*›EÜšHøˆÛÂÔžj†—™Æt£aFê~2)P˜^v¹^h¾5g*|‘Øô<Ã?*nƒ::p¯ùçØ?¢’FD!=¡1C¿Êý¶ µVv˜ÐÂWw’Åè`^^çÕ÷î)&@J˜­³Á÷ Üýÿs·Šx¯Ÿ!å`m|… µƒ«€Mõq2×uåŒÇƒ„ql³Üؘ5lÌsSÔÞ¤1‘|FKbÌ2åûõÉZš`Q†åƒ¹2/¯}l¡¸HÿÙg{É’b4¼Q9ä„¿œ°ÅQQ™9f§Ìëú³FoÊ—ãB,ƒÁàóöTd…;<£ …ÉÞlÚk}uNø1ó×zööÕĈŒU¿\ôu;Î4Hkãÿiô˜¬ÞÓ
+–Ø[$Á¢;8¿ÔB2`´ko ÈÜ!¥4 ßæÏÂç´õ†š „ñQRELM ¯Ë”‘Xð¢8ÌG&Î`·•èéd8Æ8®£˜©xaç¨Oô·3Jâ ôÜóÁdÇ}áèP™j`‹Š$öNãJ9”(UÊ©Îrq]êiâÜe?­„à×±„ÈÀ%8³qP…¥”iþ´£ºbNÕúõ£'}e%à5²}§‰ƒ0Ê+§«a‚ì|[VRÜ®WÏA‰\H’%OT÷ïÔýfb»Î¯-ƒº°Ê°H·ŠÙÉ;8y
+ÎeÀI†#˜åÔv50^|¦ÁI
+;ßA/J’÷€D%xfºˆbV® bS1˜Q",ÀìéÖ8ß‘È­BÓ2f%Ù÷Ûœ‰2¼Ãõ’—,̤ a³“´ymHkÅé¬2æ'W‚¢ŸoŠJ›p KNþËð‡âVôÚ&SxWlßU¢Å`Å°Í­\.«6ºbÙ²ê '9íô‡<!Ç; eÖÀÈä™ûî©QàŸH=ׄRŸÁRÊFp3¿™Ö'SÿqÄÏJ‘ÑO‹ÄÄR€*ŽúçHN|j̵_4åH4#‡‡d+¡A‹¬‹ÝXç(f®DšÌ<9@x((TVÍG”°ßæì…RûçbÌGhó¸ÎBÛ›ÔË\iºäY‡µ,¬ª#Åfÿ,
+ÐX§ùØ=ö8æ†siZâOK#¶\0Šþô;ªoÑÃACDSÚaôˆfÿPƒÙ„©³¢f¢‡JðŒ+kéȺä¦Éx u½Z >F5J;¢M¨ ¢ZH}~ÒÆ#!€\gžÀªÐ™,?YøÌD£:ñ®çQ/oNûY,Ο»zIî´¡YœðQ‰[€¤™›gTÄx’^ÎÂGÔñ¥¢ ¾`6)î>ȸ)ŠA#„GÙr_Š)g)ìyL#C)µ’ônÆš•5a;ùç¸|l†»LæÔ|ÿDS4q¹²‰l“ÀÎéuØw€dE ئko ]¬X‘“tdðÜ –êÜæVâóâ³Ð_Ù¦ÐÑw†¸DS§onD¸Íøãå’×ÃÐxYQÿÏØÓì"S{ÿÓJºm»»6ð
+9¥°Ç¤1L]” VÁLéQ߈¨ô˜ù*[ÉZ{ÛššÙY<iÎæt=”H„KÔ‹ãB$$åøt c­\
+ô#Þr%è[Æ‘®”CÎRùÜêqQ,’ã…õ*¹|~êû9¤¸Í·“l{ħE¸¬ejgƒ°ÅV펄ȀȰGÖ¶{|
+Õ!1>´#ä‡Sð
+Fx-;9®< «¹by]ÙÚÛÆrGÒ·—è@wf©Ó:ª)ë¯A‡|üܯ@w²pž®;­UJwìSFÎI'òˆ½G¾¯
+(œG+Baà;ɾ̸x0˜&UU}
+…”óÏò¢‡ô‰åŸzñ†¡%/Û BK›üvܺ•·z!"Lƒ”¡¦Ö\ó-®û§øe!ÄJíÛÅFHS„°D‰4¤èÁ¾qðg..5ôžúµ­u£·ƒ€~8ˆ5Dé´Qšð{<ªë²pωV¸PÒjQBj1•v´êIHf¡T(é+.^اÖ3$hÏ Ž,;e­ëSY !AÚÛ˜D‡Jù¨°@àmë.7¼WjµÃvYÒÛhëxÎâS„¥Éü¶/¥fK^ÔÂ!0}‚QW»
+eÇ F8±Ÿ¡+±ÈñÅ@-fÍ
+ØMÊê†Çò—ö/ž5¯‰`¡Ëk( Ä7ÒðB÷HËiÊ¡ÅØ(‚C
+Ã2y>ŒwÎ0П,­
+Ò‘2 zFØX|·LùÃyA“ŸM—É¢E
+Ã8-3\"/9£Å¤¢ ƒ  Î"@€y4â³Kˆ‹ÏÂ"•íÆËgSMqR VH!±G´¥HÊ„ÔéϵŒš…Ò˜&LRPâ΋Ç@Ÿ€¤uè9²ÃD½()ÈLÈv´±ð²©ÈeEoFZ„cSÄÆRtçA¸#'t©Â­:_ ûÏ¡%ƒTÄûwP¶LßoY¦ƒ2±äñ~Ê|ìÕ=¿e9 pYTšì‡Dóa-£üø^ÝŽ oYa®ØEGaXÑÙÔÑÒl4k0ó¸ ýæ³D*ü2È!vÕ‰ .‡»¼‹ë#“‰·¸©ãj²2$´fœ *ýÈš}cÇ#¨B*¯­ñ ¹ mÅY d´u2+Šõi:¤,¹)ÃUjæO¿§tF)‰z¨–ãD6+C:ŽBþØùG!´*'“•K>ÃTG›þC„CÇiþ ¥¤fñs²pá2 Ÿž—$ žFÀ/ö¥ÉÁÈ(¡z¥(Ÿx«äÈ0;F±·ñÙ}3\ªÏÑÔ}nÿÒÔâ‡Ü}b‹”¬W÷fZbØAr4Ô‰ÖÅ°ŸdãW  ½”Äñb †ËÖèq
+b 2Rê D¨uÊ
+H‰”—Anœ¹„O;ô¦A‘)­½Í-¼•sÿíû(©ðÿ«aÉ8íjI$‹Åâ?ÖÚSEÍ«öê¥?´ú³†E©¡]m<þ¹AþüJ{¯•øÿ“ˆ^KãÍ,|AŠ÷&e×(2z×áU¢”¶ ÂGÜ£k›7ųX“êÒ[Ý -Q‹¸6«„p^mµX-e^UŸCj©Ýù¬/ˆÁWxß\à¥Òø†E<>æUaS‘÷yi½4Õ!fýñ9A•ïÔjEú+ôæ%L´s¸ÏËx jã³¢.ªu­ät&¹òbîjÞˆ_ÿã®úLP´b^J$LŸ¦ÒÃÕ½éxü ®Í8
+Ã[KJÍjpP0©«/?³îÚƒwCÌìù‹F.Ú/HSJ1š‰Ç‰b@z„bâûªoxø{ÒUž>Ü > äI9þNÐ(BxÁ÷ÅŽÒQžÒM=
+†"ùÓ16híÉ$ãuâ2è-FFŸæd…OLê†ð‰@d%fD€ ¶02ðá‘5ó‘S(æ9…Y’
+¤oAš¢€$9?BŒ9v‚6bóª^ 2²˜"ªôˆu}œžŒµqŽÔˆcè(ŠEˆª·ú5…(
+oVƒ“}êû­7!¸—ó (°‚øF6¡Ï¡ÐûÊèÝê~ŒÓÉ$ÖÅP:E™m4¤® õävu_Õ™Òœñš7üL2‘ÍÉÀ{¿(Ú
+ëÓTøTÒ{ßÙS˜P 7
+†Æ¦Ù 0PÊc=/Õžjeq4ÂÞœSÈœ (¡­¾ê@Á™U8¡2!’ÓŠ¶¦[û+>e@è&iŠ:Åbý§7!“ bÛRàÔ€¿"öU4gmA Ò:Gµ õ4™ñÝžwÌÀ: dô–ùnÏ+¨N•Ô4@dÝ5¨Â_£ê6„éx„晑óÈ‘]ŸÊºD‡«<ùWQº¯®ßòU2õuóÇ©·‰÷íäœ~Ð{%}d5 þÁñ0`­=`–ѽí î¶è
+±Œ±GÉÆCT©*³gY‹³£¹z±Ö žCõ˜ãäK­öU7‡ôy{œ_S±Ã˜Gbiþ&*œž•îÇQî«®Ù‘\ÎrXJÚÌÇ)ÇÌsÇò1Sr¬Õ²®ºÖüvÎ9·×œx‹êÄddjKéhs?:ä“ïàEß@¾ÔêãÕ~Ìü6r¸¬àÊA½FÓ¨ü‰úN9î ûšw¸îúüÉ›–rrIw×sÌmKÊ„$Ó-­Î|ya\˜±åGíÍ£øˆ"ÊôÝ߀²pt îù¬Ä b"²¥5.í Ò‘žÁÛ˜Ëm;þÅ2“ã°.P†i®¨°âµEÜNê9Æœw¥ƒo@ñÔÊÎ)¹îÖ7)
+YJ=Æ;¾<få%*Ë r(~¤=(üq¬Ö¥=VŽ1×X,ì¬ÝCH4v“(HÛŠª¥ÅÎù† ÐDZ´ÚÎð>þÁÌ-ÈŠ_BJq÷öæFÌmÅÉû~2Ë]˜äNS&D(ŒÓ$§-Ô i¬rý\-gžTÛOVNa g÷f
+0wjËÆ}KG®hœë~„\Húñë:ÐÝP*¨ “X.ûA‡¼_÷#ÐEÎÝu-‰Ád ­0%Îþô„ùj!„‹¦·“ûâ÷­"œø5ÈùÆ›jú&<QŠk¯Go
+ÅBózåä)à`XSáÏ&ÐÝ›*iS
+’àÖßœ#­ÞR•ÛŽ¯)ÏÑÓ–äþåo¼i‚”e‘Ûp€~ÌNÉ}’îfÖñc‚O«7=‚¾zÓãk¾zÓcTWwÈÎÕ r|õ”‡Zݽé¡æ‡s®Ì9¼æÎÀCT5¦§FøêK¿m§Ýtô.éæAá~¶¥Y‡
+ÏR˜‰êý¹ƒzq¿îîH¾ÓÒ‹$o àDf¯!()ùh³&þNP¦µêà?ñÃüœ ,)¶­°JÅ*ÍӢ𦴖Áô‚™#MRÔÓH¢Î·,_YÇB]K'’¦Õ39üË9˜MüiÎìD¼^ƒí €Ê ‚‹¤·dÆ&¼f;9SIo‹•Ï«âÙ¢Aü–ÛÍËÔHîg2;%ôò.c$BsŠó˜´¨Bx“NÞ$Ä šKzzÓr2@”žÌ"ýsþ@¹tã7Ìû¶Âb” ¦/U[S nJ…Ìó¾"Ÿë*ö‰ìl #ŸÈ
+šÙm'y6»¡Ÿ;ò«´
+î*a|d·r *ƒ
+‡,©}/›AOCÁ:gÿn+ü&ùÿ”—Ir$·DOÐwà Ê0ëÖ²oÁ-yÿ­že]™‘bÉLÿ›H9d >”qsÄR⨻ÆÌ(¿šÙÒÏ·ñ,ZXëYLØÔtï±`ÃX颼¹Ö3P„0³̬ÉÁ‰~8 wÏartöç¿É™«­AFÙAäuFº—äÅø#Îp0lÅÙîY \RPM©Iµs`ŤbÂãûÖ~òĤ(² d<¥E u­bÙy‹¶ŸWP¶ý¬Ú¥Äê}¹ "eeñ†ßæ=ùLíÓ‡öpŠ âv²8f–1§½3TpˆÎ朇¦û'J;d Í éªFÞ|²½0 ËÉ’&ñ@6€6l0ÏÞ¯™kŘŒ´¦8BvpsÄ»üø{BZ1Ûä¯ d›!?þ_ES¿+(#ób¡¬£ ¤/#÷'Æç€XE6±OŒPÞ×wýúzGEMl#Â
+è1º_óˆëX6XÂ5>TƒL{ö¬‹Ö§_¦ K
+¢ÚüF~©1~Ë©RÚù²'mñaÁÐÜÜ¿Kˆ¶ŠMíº
+Á_ ÕMšÇUÿ=Ãb¹`㎣¼šEè‡ãý[,¿]P’ YpýZAE6¨;e;·2£#J2Ý|úUdÇñŠBËu#æ¤Õ yx­Æ`m^¾YF
+Š0¢•
+³ÃD˾‰‘Ñtå-¤J›ˆŽa‹0Ö;ë@°µüC¥„¥>¹(ñéÌ^DuÂ.‘x‰h+£±«„­ÌoÙNÛ0U\FOeÙ$]‰<tœ ;†’ÈÍ$ùƒM l?vÞ B÷øhØt75?d §ÖJrÎv„É"bìh=ÌPПP¢Í)>ùZ ¢@Ê•ƒcñYìû;åê©à„¨ÕdÌó¦j%š¦…Jà p’lY‡W“ÃeŸÖ ¾ˆáã{Û!
+ŒÜ$ÿÁ‰’K Ô çšK†æÜfýØôßS *£>´vÁ[¢oë<Hëõé_—H]£’Q~ó¹¯7ûàAÅ L?é‡_ çH<LЗ/ BBŠ´w¶ãÕ €нÉBXöŽ%bn“‚a·nxÒg¦!H¿ùý˱DÊ­¨FÓå_.è2Œz1^$“»B>úÚF’…c}9d‹™ò»üÔd`ž½ç¡Æhðítö”WGiLpò´ÅiýU£4eÜš$ôÏ=g"¢MK©ó©QÅD€ Â2¸6²±¾*À—D±Ü±ƒ)ž.‡j–;ú“ìœN4ã“èt¬WA¡D¥¬¡qs˜ÏHÛŸÀ-¥É³÷¼þ
+:ùŸ:ÞdÙã ž6WÜã6OQ Ø?Š;ߪš{ÀØmÒ”I©N’×V j܈F#,Sß1+ wbŠ·‡GR¡Š&[ÛáÏšØOþn﬛!7š×dðR}F¼z5˜SÂûyùÍ9ˆâÐ=¤¥Ãö·¡¥=.6ÄþÐñÃÝ0Å3Y/¨{2Ô\––²·q¬ºŠ)~ÎÅÚžn˜¥ŒÉÝzÉôA{P á!%æ¥"pÑóž vC<¸­l…|CM‹Y7ËãZŠT4ý"bÏ<µ]Ef?p`IYÒõ$?Óé3†Ì;™‹,›:ª Ð90/Ý‘Šz‰{‡2Cò#a€!á3†¨gš{„@8³Mû{w3aœéæΙ:ˆeƒWs¯î9Öâ‘xwº¡0H<~ĸê
+Ð%ê®"+̨Ja¯¡P†Šd!™j7Í9~ …NsÎm8§B§—XxŠs*tÇï5ºs*üyŒŸ©hÉMŒ] 7©ÐS!›ÙÈÔÜT„G±‰*t<òÓ)¢†¡C AòR¡jM;« ápåTƒþùSŸr“
+Q0…‰¢‡»çT<bú:½TèNó9: kà4ñ»"pFm¼ º>]߆tåM*³Šgá¤|“
+U
+Eó6|¾¶ãš
+µe TÓÅ8î9 f+˦r|üK*Ô¡ÔÝò® 5¬Ð ëÍ?Õ…ÎÈŸc¡»:çXøó~i9ô¡øÓ
+§db½o¦¯\7l =†­2#„ ©f
+½?ÀúC^%ÆhçY^$>‡±×ÇÜ«¼V´ÌƒYž¢ð(îDW±Q )“ÊAsÞœƒT•0æÔÝ×
+¢öhŠÜ{êy‘øõºèë7‰9✲ 2OÁŸ‘çÅrééŠï`§s½y»Àü ­xýÐ]®kÜÝ!IjèK‹@±'–™êíÔ‰o¦?¨Ìðß„h<H9Ýæ6äpªÔ%ø³ >Fÿ\¶µ/®¿w0²ˆ#Ç !̸l™.|ʶ¹`‹æÛOœzâÌ(k|ÑÉ‚6ôXiñ‘@¤&r nóÉþ¶÷BtbVòIs8ÙO)oJ×XÉè¦ù`œàêƒAÉ‹IûuT‘(Æäþj_ÌR"‰•¤òÒM±‚Šõû—zQ¼¯D骂b¶Ëð£Ã³×{ð˲U6"5ƒp)Ƨc¶ë®xD÷§—j¶›Ìz$‰H^Ó,b'ìÈ“2Te‘ vtƒ±È«È­Óò¨Àê V7é-âÃ\Üsjøå-×¹q>é:—Ò8ƒ~ª¯ƒxiÓçëräØ
+^âVÖ1‚&"7ß
+¤¯”ð6¥ ¡÷ÝÃmÙË
+s¶…½ÈÜÁ›³³öCh• eÊ׈c*!E’,ì%(¸
+†,õ¡äo߈AÓ``¤õb‚ #Ô2øÎU pÄsJŠ_ évclBSÂ\rUû½èàV¸gg‚ 0U&ùnÑÁÒ°ŒÉÅÂ×E·ƒ :}I(€ü‡¿v™ô•ó¸ÌXš-±ÿ2#r0k âiGr¢‚”)\ŽõFr´w3•\¸à‹uØÃJ–żì#¸¡¤»™¨˜îˆ@X±ÓÿGáo/§ìFFdª »òBrNEÉäg  „ç¡n¨Qœm¨i3 š¼|ó’%p¬hvô&¦€„j?9á£à®Io“rxÊ],§;E! –q·6Â"Ð/Ä >ÙD²Å»Âfú}(a«‰ § ÿÔr=©.¤²vMG…ˆÅbŠQ^‚ݧ{¢è8 <=¤( ǽS(ÁC
+·}¼¶Â7ã8Ø”ñ9øæ?ï?øXâ&â¢À€àž},mH†ÃË1qš51O • ºÛÙ°„°FßHm’7ÜœþPLà Ã1ä dÅZT¦ÆÕ/¬èăõü8£oáãð‡|ÇVlõŽ»,ÒeÆô˜ Šú"¿Ü‹ \=äÉu–Εßš†'.-2ÖƒcÃM(•ÈUcÒ§ˆg¬ð\ËÛšÅÑž «Q%ÁŠj3†2Û¯„Òó¡w;
+*=%qæÆÃÔu*ÙˆŸ„Œ)®+ó(Ì‚¡_“ÃÍõt˜;šŸîtD¼1Z\TèyKke”Ùx’ïÔÜi^{A˜C€×]4,ŒÖ©ünÌ qIv²µk¥Ï~—ƒà•plg£šÞ³ð BÉ'šßM‡¾ ´ÈòY ÀEŸ2mÖ.IÁâ Fv%ë†TEÇyä‹ÁÈC.å4'˜"´Nw3QYÅIZÖ|Î-¶ˆ± šc'Ôl¡¹?ÎCƒÃêòÕÂø}9Y®;“
+}%Žãdͪa_âÞntÊ5DQéâ;,¹kO8/ÃÂLæ
+.…îi5ošJ=vó©d ƒ}9ôìöbh…á¢^¾³1„šÛgd E©\ ‘™$É綋£ˆVÈžÕòkàk™òŠ¹òe,(
+5°-ÒÏSˆ~,«‰<°2=¸„ï S¸ú¾•è†>HY‚Ø/hŠpƱ¼ÄficíW ã$ p«±¢bîPq±UGÚ ºIq}¦*gã­Àl0¢"åÖÖ.™Y
+LÌ)èZŠÇ§Iᵄ…mĤÔð¸˜+|e|Ø3¥ìXpªûÚrÁ¼T]%\ÇŠ!Ã
+U
+-aY…)ãe«ó³ªÞÌ&5è‡üÂ('?hôíG¬ìÅ:I*‚!#RÅV4r¸¡.Ë)_£ËÓ \qmÚˆŒÊª—¸,-!a˱7da3Ï­ä&ñ2œT"ÌÎàÆrj/Jš±éÍé‘£I Gf¦Š>v‘;ŽªÌ+)“™|àÞÝsÍ•W$Kñý4hmmK
+½ÏyµßWOâ> R„막Ùf«CÃK´”ÓßÄ9Ö¥Ãç±ÏáS<+?˜¤fv^G{3uÌ ('NÓê›lÖ‡Ld’°²Ñ+¸iÀ\³GüÔ„ctæžúõ:À˜ãqSÛêqw™X4‰ÏÜãEÉ'·ú§›Z¨•Ë-™ì¶½8Ÿ\‹­ƒœæ6µ§"TJ‚ÁÜr·8†9ÅJÚ.Áá2õÌäp—çIŽ–#*»D\Èñmû)1þXÀ+:áꚤ8g_§nÐãbâwb´«'®AرÞ%{+ô‚ä Á·uð"•1©O?Ÿ†¨eã€ëæãí(b $âߎ£Th_ž)m~{âSɧVíîE,2}/Îõ8ª8Â.ù|q'4Šdþ™ÐØéùm(â2•ŠÞ§NÍlWÞÈÇN=•ìN=wü°Î3n§¹ãïv«Ž™ïÅ4jtûã< |P¾í®iߘªkø䎽_„§àÿ)ÿÒ¦"ˆÏ½(à€°†ÐÙ¡èDZàÊFJüG]^tÛí©æã'úÁ¹ÿøë-=þóÏÛÿ¾åÇß–è\"È˔𭚤¬GcNhüÏEl5&,F%ß\Hr2‘*QœQ9#mU Â(WÇéf*»¤0JMÑrxÜZP* ubVfŠuXQäP|P‚Kj᫤/úÓpp˜•teAì¡ÏΡķ⹱pŒÀ¯^f`BÁÖ•·v­Óa{&GEJ()ZÓ‰x8¼)Q‚ãáɃø”?ßNEL™Ž®bƇŠ°Lõî†ø€¼Ç1g¯¦kM&$ÁÒ%DJ|½’T=^üs…ßûö~·UnMà(E‘lᾚ›)NÑ2lH v:€ÊÀÀ˸—¸NÐƃÄVðXsw‘‡lPìôŒþ´·òµå†&ÇzʸxõÁ’ñ‡¢ªañ»¥sÁIƇõ-^
+i«ñУ”àTþ§ t-f#ëaNä2M ›g€Kõnp'R,p¿„0¦ù\ÂÙQ"\6µ|=[CÙ‹4 5þŒ•QV¯é]ÖÂ:²s¦¾¥1E”Ъ2¬Ó¯ã
+ µĨçÓGÒ‘Úê±Öï‡þ×KÓ„BdcŒØƒÇ~Ü~©iúy·Õej6Bj¬õ\"p¼’Wþ:Ï5ò‘'×z= ‚îhÿa‡Ú€6øHQ¼Ëµòœd…§+É£Ï-ê1/ؤe‰í½-9&¸†]XÙ"–DÊUʯÿø¢èßhte¾¡G Ýs²yCe%¶blµ§ŸÇ9*çñ®†…rHºæj|Du~äa7ÌšÏØÂ
+ˆW–|_
+Ðzö5*¾Qm©uå;O‘¯$§?So®Þ›ß7ÿËcH@¨ñ©8»mÏ"ïãC‡u¿¶(ÍuÆ€M·uëçõ9†ÁÐÕSs€gnL4ב GE ²¬OEˆ…/ðƒ8¯Š>Y“U‹ÿ´ˆXû‹-¬5¸Xé‹Ã
+ÏT"kg=[ìÛ~LÑàtk¹ãX®;G’@þuoíaûX¸“Uú¸jz8õô—÷±!²øHh¿¦Lé9ýsÍ¿ÒÐÀ Ót‹|Û¢_«Š
+ðÅFƉÀ¿8†LXd`—ßËXÛ‰…¶0!±Æ¥ž?eP£âƒÉÿy<ŸÂxLrtÒ´ó§ÁAßuž×à0Hâ-‚æþl•Öì;”Pæƒ^Pƒ¯ƒ·AÖ€÷àâßû§›Â¾÷sÎàX[3Íãûîdž?
+·Í¢¸³2~¹Ú5ë*«Š.<úŠÏ¥¡Ý&.:žÏ1ÅZçNì¹€<ë Ü‘Ùî’ä¤O–?“Qü¨Ç±ÿHQK‡Lßûßÿzd«/ l,¢$pÕýÜ­-6]‰Ë©,bH÷’1Ú¬‚±ºý'«sn¼·rÊ&/ÙÕüX„”\R•_1ê½´UnRsk/ê;u;WÇ´¬‘LÂmý Nÿ¾LKë2Jøª·@Ù+ï½:§{˜Ji/sÓý²‹
+×ÑR‚èú…D¶×à€;®Ž‘8}UôÉ;+¢”ÕßÀèlk¾**A†ÙŽPi±,
+24Þn?/ó
+ÛÃñïãÙ F˜µ ai"[ÜîXJˆ¦²Å¬íiÊV) árÇžõu†;†TsdaŒ#óÓÕü2vÖÁ?ú<€/*Ƈ|6zèq[Ÿ߸r\4aqoänÐr
+Ä-âþ‚ÇoD‡ÊYrn$tX)WX"ð78~£¤™ø)ï°ØköÏ(*¦“­½úñm5žkA8èx­îÆH/BÊfšÖú(añ"ÓÓ6ýZM#8[¤üÁ<IñN’G¦©§Jæž1{œÕ¦Ž §úàØò~öÉ®R¦ýP2:ÍÏunÅÊùµ=O8Xåìéük‡»¼$ÎB…ˆ–̆±¿ï¾O0a5$Ç|‰ d¿x›¢Ëûrt­ íÞÇ1zÒQX„ýœ±Ãö\†º~U’!H¤Fý|çS ¸ÇosÁBN  ©a]ÜZ>‡ó9%%ÜN—IPÄþÈdLûÖ“{cñæFx ª8ÇBÎx—dÇÔêV{Ω¸¥ÞÁ÷SRÛéʈ©ÝÉÜC£Ùc}%knúPôß/ŠhWϵëQ‚eÑfvV2ój¤VÓßavÛ§NÝž…‚”;%د À›¿^WÞr—Øñâد³é‰ÓÖH ÐŒtÈÄÑYVB¢cÓ¤fEo"‹ÑRkw\©ÚÎEɘ„…‰]¸íèq¬Ï9¨Dg'Î÷.D3‘™„+iîg‘ZÏ7é ÃÀ÷¹0…AØÝß
+¼Î2ÞÌÚÃL`vF0p›¨ØBÙº__””ýÐ’ËOïä­ ÜUŠª×¢æǺƉ.Ü’®êÀº•‹„ÞåþécjVàK•¹"`í-e>‰dºÑ’w=kuï¼&ýÙÆt”Öñø÷JKq›¯ÍaZKàÇ.×ÓêNÏ ¯½ÍÛaApáÚö¹U¬iø„)Vf¶é…áê’Tzu”éKg¹Vñ§dãÏŽEWIë¨pcïî8üeŒBûú]u,T5AÁ2@ß])a"®ždÁÎSŽ“C¬ä‰çSì:I,`"m…P¢–ßZ˜Ì“á­¯Gk!ˆKJ%¼žÍÛc
+¦W†ª°(`C>à,Šâ‡üÌÇ.9Ë"v
+Jö
+‹«¢Ïó¾#tžqä÷ X}Jš°¡c‘Cv&O
+>;¹R¨Fg¦Ò–'Ý[ÎÞdå®mu§ÿ椽è€ÅÌMâY¹Ä"ÍYÁãFT•!bÇ´ËT ƒË#D7oÞÍÞ'@Œc*UsPJJntbÎY”º?%çaE²”çÈ´Š˜+¶ØYç3Ê¡ÚäNÞµ Ç®{Б<OWBÖËFA‰}JôWuaº%ãK£Ëä>æ»®Îa?Aâöù(P¡CטÑRo^ã¬÷Â4‰©Ñ³eQt†
+hMÁîY“-ÁÀœúSd¸lYÏ’µ "/òKhÔÔcQ¤$^ÐðÁ1ï¼UèNAFqà–ÖHÞ3Λá—P£1O{ÒôŸÒfQ(õ@kP?ι¤ÓTë¾MJÍ,tñ¶qIƒe ùÜSrmÕϤ>"í9˜OdcÞ-tå
+sѬƒœ°)P×Xöt¾TvÆn¹?~¿]c9†Ù¯H/ï×ì7£Û“-‡”¢ÆÃLÖg‘錑[бn>ÔœÇK½\מ€hw¿Ý(Ÿ„†åËu”{Qb"ú#jÖëXŸÃSíæjèõ§C"ÎWQ·#ñ¶t‹5Œ(&×Å¡Y,Š>“Ýö=Bbû¡¹}AˆŸk>ÏËOâèêéyͯŠ>ÝH;ä«+x·äª×ßCX°oy…å_îùŽZqÆ®“ÃVÆ8³ˆ‰ãÉúÒ²d¦Ž`rk(reŒƒ ßD`v´¿3ÝFB¬qpõÛ`‥lW¢Æó¥ß€0Pýåõ’›7®DxÙƒW`ˆ’¨Ç8=Ì.2Mö?½ß¡(ÖO#¾4v™"«ê¼ÊÛ?¶ýÝwt©ö|ã’3D5£Ö¿_éêzÕþãM ¯1ùI²pÇÕŸ)aí›3^C±ÝA×`‰ÏaÒÝ\ÑæÝGúq®Ý¯çÃ(¾x¿Š3@µÏᇫÂë÷ð˜ WqXŽ„Ÿ[×Ë„>J|ˆ¡ÈxS\Ö<:‡qY <æ&O»JÒ˜˜uþ¥¶’}jÌO«jííC6x?îf†ŒQÊæ5ê<µ0cÇEQÑ$
+Ÿ+yWLgÍÎû10&J2±ÊÓ·­›k§†‘§xœëéÛ\´þÁ>þl 7ÃS`éú„‹DÜŽ拃œãzZ“uwͪ`ÛcPÒ¦É\z ^?hõó9¯^f°7¯oìß³7ƒ-~vxXòiR¿¿™ k
+º:™^±’¶¯ƒóþ~QÔâ
+ã—x6.b«­§|ÜÄ庅
+WŠ¸ŒP¥q¶œ‘BC,Å•çXr“©™ˆñ9¼›eMt¦G®‰Åeù÷üUJÔb’Çz“R¼OâÆmd»JB ´´%à]E¸¹ÂÙjŸ?Zr°êZé [N›¥cë%Ÿ–óçQÑëš[Wt#Q¹ºœÿGÑxÃÏ}£èAcT=Š­pKÔ˜¸¡¶‘ý›¢Ys‘säSCY¸⪺{£mÔL&H£Ñ.FØ 9nûÆhii¥á/£]Á”°á¿¨´“ˆÙÃàžs„§•í
+îÇÍÆôGD‰¦…SxU¼)çO[ÞþûQwJö[|£”m0Ó¿)ÚN# ñ-ݪ=Š”Èe%ZŒ÷–
+ÏøÃx %¶Ä#“ô–síý¾,sÜÒŸ3
+EyxÅÒ×L› üIùä5çx7>5T<3û7fžõ˜²gT~åŒpêq[„Æà\r·AQ›š‡ÍˆÎ·"¡Q|Q뎢໩NwL‹ ±´M¼mÄÑbÚ}šc%"HQ•T.~Öðm·Nd]n*Z„Ç%q{æ׸ëQª…öØH{{dôâÏËèÊ ’¥Fe|EŸ+&óSƒ=¯§r ªn)€³p µh q´ÔåÝ´Ø/+[Pæˆj Õw<ú‡pº–:±Ä‘oÃsì|ˆ¬v à6Z“¸ÛÉz—¸àÕoB`ÜwK3&ƒbÓfʦ~
+ïB¬P¥Ï1ÖGNX“éز‡ÛÌQxf^“§© ÌÕÏ£"4?‰ž3Ïèè×°Ãß-Òm·DÛq‘¾8ln1/ƒ°!˜-;Áaz9)÷ˆm:†‚Ñ°g¤\‚ßîë@0”dÿ &Ü=ƒ œ{’/ÏdÂePҶ…±˜uóù2>gåÌX Fkêoª H¨Ãrg<º¤«dë%²Ùž¼×Ó¾d·Nt²rOµ¿ielÁ­P˘³íûÚÏ¡ûG^G¨Ã¬5îÆÄgí‹¡•Aè±8ªß'1&‹<׌3'Õ´¿mW+«f¡–hÅÙXM“cŸ¬¿?ðßLÒŤf —Ú¨ÒaN1ãñhÃÚw{÷ÿ1xE3·8Ìæ‰ñmŒFoïGÅBg8ùÃ|75²*°QR²‡©ä‚96uPò;ŸBm'êd]ÊôÅ9¼;¶ãóMඩ>Zi›¾¥"Â:¨“Cè(^ù9aÌûãGFE4âŒ"ƒÖz4øÕwŸ´K%³Ý–oa'ÿªk®[mÒîs2QI²¸ï<(yÞ/£öøYûwë ¯3KÃïn7é–ÀSKÐhÍó×<ô@¡Ð1
+B™E“½ÁYÆ´l¤Çy`D{Ž‰TºPÉ,îè}Īoã›ÀÄJY°y¾tö_[ø+Ëíåyⓧ«
+T(xðÆ×ìê¨Æ¨@ÉàE¾˜MçÔ50@—ýJ·”p±{Ô#®¸\%K$S?ZÔýñc+`GÇõ*Ÿœ«Ä"園4ET-ýÑÏI_¼‘®¯í6H™‚{¾ X%>ÅPHy¡Aóè|¿fâàhŸŽÑm„V>-Fž XŽñ«’lý}ÝYDzͣî,IX5ÛÃã,û ÅÊOƒj_zᬓÙÛz\_z^&D\£à3N‹ýz}Tè<þæ؃ÓýúÔ³9)¢j¾Å¨È ÅáXÉ—«J€<G£z–´­xŽ|pÎsq^n󺀃W½.rºƒ>”ña~{CZÌõñGòŸúyoI<]=nË€ÿ¶ÕYZNÚ®³‡\9(z%ÂÁçE¾s§_w脼BßÉÅv™Ÿ‡ÓúûEQÚy²!sŒü„©‘KÖª—0O;ÙÙÒ+ãVš”g“ý£{ Â{»
+<mI¦Ì’³GyæÈPý[B›8ÒìË+Z·ÛÛœ‰…ÈöH‚+ s‚œýEÝ%>E<õƒïåS¶2>戓œÉé^jeÙQ´Á*:)Øóx 2ö1z:(šCôT¼ä
+mh>f|Ç’Ÿ3q·7Öæoîíù‰ö¯¼Kw
+y3y†_âaçÕ9ˆjÈl{ëK‚§eίM¸3ë$I ÐVÚË"LpJ8ƒ78ô%†QâKh¿%z²ãÁ:ìžÖÒ!4» unÇ9 “a|(¹™n;E“V¦ç@õ²d…¶‡ÇW©
+¼ {ï;ó@}æž¼ØâYZnèKAjqÓÂz–ö&Kiãå8hý6Ã;Ù&äËn˜_”¸ÜFl:·|a¹²‡ûi7mè™e:Çç®EZAüïÙrÇÀÙ¦z¸_oba€M=æE§éfÊF¿È3ŠbpxŒÅO–yžÙ7NDde!0eÄ'EdxÃÁÛ±ï×ü$ïm"¨‘4®w*¶5HwÛöùë¸El®cþd‡EQÚ(M ï~«ë'¶Z5ØPúëL…“:Î(Ø9?é€Ú_£þáù™ù€²ûe@Ü/P'†ã<ø&MT£ ‹Àå–´Í÷2)RÂBì:ozgpáι°©{)aÝJ×Nÿ§Tôl¤ë¶v’T$`ºbèx¿Sä{»^H´ž9§Äú¸Hv =g½k»Â
+½Äj-Ëú,B,1'³Ë…$=½-§¶½ÕIÑ÷Eˆ£^Bn¤ˆ€í£–×Ù¥‘«ö„Øy, 0\ã¨iŠŽ°”\\3ú¤3ç³Kïí¯|é¨vdômÃâÐÍÒ'¦î9f_¨="+ÉF³Yý~æÿ~äz}Û
+KNUY'4ÙááÖ0¥†×cR”쪪nGï}ÃJtöÀæ5%èºÄ@WGˆX<—1g¼¼¿„á8´ÞøÒK¨‚T6œŽÛý{`?~Æ;pËó’}MêÁåˆkœm}‡h¸Ò£ü¼èû½³e–2\HüÝwÙ¹=Á{êÔ),ŽÉK n"®g¹ã¤ Çné±Ù[ßA+˜u2ÇÇ8¾3óÑ0*Š0£,2lÇ‹s˜w?÷2-FEo0D×4VÓŒb"/l–àX³Åkÿ¯¹­îJ_ˆuyL÷E(- Ìç}YŽSräÏÊü]¡öŠ#âê3Ð3v=´bl@ šgEŸê¬Å_-ªÓš´‚m¼(Ò—DYÖyë,Æ-Ǥ]DѶ+Áå&ºãl¥‚F<Úp4‡”XÔæÙ§.µ.OCX”1JÄÀÎN›Í:ó§—Å:xøìy¢„3¢ù礤ãÂ{bMû<÷ù9îOÑØëŸXg[,(–÷Îð¢J«X½§bñkláJî/ÙWZPÁ²?{Naá„}ì!9ýêÎô±UtûkÙ¦Ôb8ðR.¢Þ‚LzÜGÞ݃äôâGŠ¶ÐMàÉÎQDå×hE뼫û¡^|½îeF‚¿'Óðn9ßm– P—vM$þï^¤—´p– g §‹åÎ/10­Î(CE±h}:ûp
+V] "n¿%šSÊ(UÌ­wœS-À^Ö½;žÇ¹¼ .)Á&ì¬0ñ“Nñ:–¸’mfEŸ¬›"X++Uµ°~¹ÈçòHJ$ÞöN‹VSØòÒÜ)%$ˆzs ¾p¿ “j!Ž®¦ÄTŽŒÚìËWÍÞ—¼lOÕéæ`âMñÏ'Ó¾óPóaÐ*iÒ–½Ä’'µ­íåɇ¿Vô!ØxÁeĦ§kØö¡*£¡„! t¨ë„¼ÿ
+LýϺš2þ¾FÅâ1¹Î9©¡5†æ“~ìÃÙà¸DŽyë%K)ÚOëÇ ’
+Cæ\LŠ>ü4º%©j¥ZË<ÌŠ>yì/lé㱋ç ^Rú¸ kH1†kO{²ÏÞc)®m“HÏŸ4þKŠP¿PFsëþT–+ >.A/N—‘ÙÐf<…›BX\QŒP¢'Úâ<™jû›sh?ÀW Ó–Aô<w¶¸—øO»X¹ÜÁ­Fjök–5#™½eu h·ÔéöÖÿ~Æ!µ'½+úÑÛ³rÊê0H½&E®Nà{Ýú^‚pán #²¥oS”ÆEG¸ Ý¶ø¤²¿¥?HiucÍíû]Äpøs«ÊÁäœ\9ì»<äýl˜ø £tøE ‹ ÑØlÃþýSŸŠ4ñìÑÇòÖûS)~±gmŒ¢ä=,^æš’Œ¼’Åè™)îŸâ-wN”¨–GQk2çá[ÎmÍ:.e
+Áß
+`_¸£+„»ÔœA¤XÖp8Á«WA°vupQ€õ0§ö
++ÚÈ\ë‚uŠsÈ$f6?Èqñ†ÊÈJó$
+ù\$îЬƸæ˜öÁÚa²×ÞK|¥e!ávø_ÆÆZŠëìÏ¿0Ú±¯Q¥¯hSgn¼Ü¢p¤dÝË0¥BÍJ ­;_”Üi³ÄDˆçE õNnÕûΟ‹gîc¹úàúÇ/’„…·ëq‰wŒ¥Üïv
+M0Œbï$â~øU“ '»‹|Üؼï¡bLo ÃϤ¯£Ùú_2àÿ)/“ì8r$ˆž îÀðaÖª%o¡mêþÛú8²È GjÕÕ”%ðÁ~Ô\u\ ˆÆ!楅9Ýs&d«x%C}Ôñ³  BL Ž²ƒ|Ë^•4I Ékq`)¢\p1£xÒ's…)Ñë“M3>´óy>86Îä’b³•5Lô“=ÔN$)쳈ÉKˆ‹Â¨ d±’ZßE~=Œ/+æµi`êÑ8¦›Ô*M]ûT© vfPvƒ¿À¿{Ii·¾£ÊNË4ì–„éh‡V²6_ä˜Lˆ&.Â×ÃÞc9z"ìóÌN>p²Žwqö“ç
+/O¼ ˆ NŒ •â†tù;(C¬.jC”µÆmœ6`™vá±]¦]úMºä'{/£¹1:Rü‚ãJ{¸–`è2ÝŽŒCЧBÙQá-ô¯ŸŽ±€€Ù]Ú„ÁÜ/‡€º!«¸’
+'T,­¡¾#Ãœ>þ€#E§Q¶ÒBM°{'ŒÄ¸ÎídM¥hfºu‹ýì9ÛTÀ¬bÎÇ­\¡§†jk©“§ÇáNKæwK*é.1MüÒÅŽ'ù\È€ÂRC*YÂpÏa(I{ÒÀ\ØmÒj %ÔƒP‰)šÇ¥¾,{¢/1ÊA?þ)º
+3K6Ÿ—*æŒäB—“ñ±ó¤öNW"oBŠýcðxSÓæäæp— …xx3ñɘŸÁºÊÑÕ¼4ä
+º˜Ûuç.kM¿Cý9ˆiBvöŒ5(³Ò˜²jHV (ƒoXŽˆŒÕZ*ºü‚Gfê4ZÃ3¡sŒø*! Ñ11°†qŒÓWoÅ
+h†E#(‚A ¨”·=8Ë4ðHÔp {,zEîÅÉ£ç›s•Ðt¡5&
+r:"À=²¢!G/Õø Ú¥ ÈŸ“ÂÐã”s„DLx­hÑ’hÃôñÒ/¬ËD`Ôžb¼é )QWžÝíWfÛJ›°óš!Hè0 k¹z­ø½¹‰òq¬r¶ôå¼&Ú”è’Iºx+à} ¤Ñ<-°l?Â.yÊõcsýãœVD*&-âîÚuÁ¼óã'ìýLTIªÒZ€ŽqCG•q¸%ÜYäÏ¡Uhg!zÌä€p"<žc0Ì06›L@Èu!¤ÿü"§r#Ȧªß—ÉD×p ¨{Žå؈!«Ñd<×9Œ“ƚ壧±O,;lÖžI~N, 'ɬ7ц¯-k¤Ìàòº„¡J©?H>œS;ÚXÐc×0…+§á;/½€Á¸ãqñ„5²©³BÐ[<‹“ù¹–) E.VÖ1ÂT" ÄjAÜ+ª@öIQÁ…
+ŽÒ,ãÈ´`ëW'<òÌj³ ­,‰s@ÔPsÇ*¤jPŽCPŸ\nÆâ›ñz¦7 <€$˜)£Gh{1RÚÕÜÓõˆeÄÎFZˆ»÷ƒ?UÔ:îïÁéŒ%dÁ _bA‡<™Y6% ¤gädu&ËŠT¿ÙwÈMä72÷ã'¶ ¦Dê=¯åÔç²ë, ÀåùRÝsðÖ¼‰™GŸö:sQaVz“¡‚ÀÜ ¼™m¹ÝTçUƒ74ñæådV
+APdKõ¡Ž †ˆÒ£9gbôÁ,ÏêC˜NrÉŠ º>ä0ÅU<ŸêAÜ:o®Eo·•)"cž)%„¦U…U¢Y¨MvŠ!¬ö$('‰jÓlˆ& ˆuwÚáb¬…Bqón²Ž®ñ³T„O/ ÐÚóÀÃúˆFWÜAÿ•,l¸:•šrÄø SÂU–¯CFÞ<¦”Lj´Ù-þã̱,ÃÒF8ƆÑïêg3Þye8b-\+…#•Ì]ž%ë¸ß’šÙŸŠ#Ñ1bK=¾ùrN“Vûf.ž–ºˆU“Î|Ïå}4<ÜŸÁ€*Jw@ÄÓq0#K(ÚÂH
+Ö™ hZªµ‡:ÈT6<Tò„+æ1à…-b#°žcœ!¤àRwöñÈmKœ€9U××»X=~ %ÏÈ«Ê _›Lõó€C€—@X$Ø>46ò\F«G—ÇÌQÆÚ-^uZN>«5èYд¼QxïHî`…˹0œ”Xþ®ÉÁc`¤ŒÏˆÇX³cÌ(\’ ¡SðZesˆÛ]Ah>ßè3a)Z[–ÆIùŠ.
+¥µGØoóþ‹vì»8 Ï*ôøR-w•ˆ/Ìpú?übÂþlP’y2Ãq Ä|ìÇ2߀x)3Š£ƒÚì$–Š¥î‰*½ "SEþ¤¶7óÕÞé $’QñÛùj< }h:jþ9]ÞFÿŸU² $Iõ‡­<K¡ÿj|Êí•¿²Dj-ñt5»ºs«¶Ê·Ý8btÍ›.èkÞ„QÁ¡Â†ÇÆÉ/ ¢<,#"ðó ‘^($³ÜüB·&F‚Uƒs«›_ÔРᆩ¹„Ÿ_
+븒5…/ú^Íž5„cžl ç‰Tn×jŒ
+ÓÛˆJ×)™U
+É£óxæË›
+ùW%“Ÿy
+¦:¬N.Ê<‰tËìá8o\bt ᔸ€¹×LîBuM¬¾oñW~2ôÁY­ Ì<Yus2Ô–h…:Í„ ìC›ÇÑEÈ'SÖPP†¶Mý9ç¢yßhç/IìÛ¿|pYD,$ɧŠC†6é|]*[ßUg”#ŸÆóqƒ!¥2ÞyÉÀê™ÊÔ-ª,Ý› t9VÂôö¯ŠN'5%…Ž›Q•ê
+ÕCZX¹g²•%àk!îá
+É 9ã yµÿ/“ã8bŠFàŠ¸œ}U¾Jù_ý>‰v•º1%ë¦ 7|ü%¿ýÜqa#96&úÎütÎù‡¯]½N9mÆ*4 Ý‚ƒá\j.§òÝ0ºèÑP˜Y—I»›ƒ
+„6a¸&ìæ ªFì4úQ<k)kèCÓéâ’Š}C˜[•ûúý+.bkn•†dhcùYôÙŽÕPQXuùca•.v¿8|
+er“éÜÌ„T0E„p«Ç2ü¢¦köa8qnaøТ¼J:`
+:JÉ/.Àã".«okU˜OÞrÎÅ_˜¯ûÔ†óT ‹«˜œ¥Wðà’K4À¦;½{Qe$˜Dd HæM_ Å”–­jÝ‚šÏ3~ 4ÁAÌ}%c]Y|ø!š=K¨ÎÄ€@´_à+ìL(_$r¹.&‹HÔ±n½û 7¸JB«
+×±wƒœi
+g4? é®á©±z»dà(-(¾æŽ6ÐærnE—.9†«Î:6¤˜ˆd¿ü6| ÅñY»öÔ€
+ž˜ð1$É)Yت{ÉAS…kHqɆ ¡/$CbáYð‹îô€ñóm¢¹½ðO3u&Oñš¹È“¹\É ¡j­»…yÁ”äbbö¹¹)*Bò²,=<âê÷¶ûH^";È£}§YÈ—BÖp6íÀ€/yF›­û-I=7›ë6þÃòn¢>áyëSâÖ’¶5>ðs j©%g'¼G¥ÃZz…ë¤s.Á[L/áí¦f³Käq8íI×*R Xxv—aŠ8 ޣМ•™!ˆSéõ¹‹`]‘„°¼ˆIhaÏmß }‚ÎQšä¤ŒkÄg;Îy*¤rîí8Å[ɹ9žêm/ÖAÅ!*ìÃj¾¯ždá¸~QR5©Ê)¯}ógÑ ƒ1¼nEWdKϨ¨ÉóΓ\0š
+ÈäÚ“ 1`x8)ì)•DEؽ±½ó¢/BaQ' ’
+C‚>™ÞQC÷Z£ öýžÇ„ÔÊÑ\„… Õƒ¶²-]*‡é0)âò`£¯þ"dJË`xu֞ðšþ
+0
+H‰”—AŽd¹ DOPwÈõ
+I@·Ñx§^µóî!ÐÐüÛ ¹„?¬QùÏy\5¯¨Œ¢úD¤Áª. äÀIGkPfeÈ4‚ܶ¨¥.P-¼­+ùµ˜ïo]´ÃiJÓ$yÙrõát[P$Éþ~…¬êg±xÍ¡rŒÓ/T¢Aš|´ ñ°âÍ,ød’ÃhøQ_‰ $2£.uõƒ&Ñ
+ê7Š‹ÉÙib¹2XÉ”ž1t̘Æý|LHj4c,
+6|# ¡Oó­øÊåó킂Ȇç4Hg IoçW˶ߚÆyÎÏgT~ëÌr3©ŸL7‘´e}Bȱ“Èt~€¬£°´h,E•QßÄiô úÉ›š½¹Mãý¤¼4—úöU”}È·='‡
+ã±-ˆV×rïê ‚çTʨx¿¥©ØçžÎ´Ð–±Að”|aYXfü4ìÕm
+ ´ÔÀ>Ûº #KaNgIJŽª HQ3_ËÜ!à zøö½‡Û ¬ja!òöQ©åü
+W…u¾6ÇBïtVYi3N¥ŠQ-?˜>rX±”jB¼2©aZY†—jæû¡:²çLàéÔwHÜçŽú æóŒ!¥ƒi€À Íl'†‘‡GXßW®½’8˜½—®]TÛ¾1÷
+0Lôt ÒZ Ÿ¹“C+³`acºb@ø_PŠ{i;ƒXXC«ÃõGI%æ{ºDó¥T_–<‰]6;P!8SœZ¶ƒXìÈL“·ôÙq
+ËGI˜UØ‘*6¨lŒ OxXË8»§~nÂ2Ñ–ó=PZ.ʤ[e1Sؤ¦ê!±ãt#ŽªSû_ ›fŠ¹!¹Œ?}gtŠÝðaú?‚ÏžæW³ä 3˜‹Þ÷å䲠׳‹e4;~”~/oaX»ÏÅ ñ˜aP±œçLìå7h.8ATØ! Ñ“4ˆH,Y§¬1i™Õ|é¥EŒý*S€)þ.h iÙ&¹™þ|"/«Ó»Îba/R )a<÷*|]íiZ·ÐQËÔBÚ¦ÈÞ™þËx™äV’Ã@ôuŸàC5¬ÿ¶bßÛ/DÉ€3•° h ]E§$2™ª¢‰j[¸¨ˆ/m¦ÄòÊÐ%¨ƒÈø›Iß$!ÆÂmÒJDUÿÊ`øå:ççIP›;”8.€ÃàÒ,@®ß1©„YjI;6™”ÞKWÅt¼˜ÙIMV«@ñë¤^”
+6FÈbÒYRé3œ W BCuª,L³ÇÓš•5ú4B™ª»7Š:Ííü(Z©r×èù0§ `rÖˆ("ûáM›%\þŒŒÑÆ¡DGIZejØÅX¾ƒsÇC—1Ÿe@´±‰mÙ@¼¦ÂCº<0|¸á.ùœ÷¸ìÄÞ‰•6IA‡…n“븲ÀÁÊ51±ërbAQž+xùÏ
+øµ×¯Ü§ÀUR¾ÃðÖí8MÒD†?˜:u}MóŠ
+‚@ASñ(‰Åû8a úYÝH y¨$.÷Y”ñ+Žÿûö°¶i}è~r”4fn¨×Ô1!*R* ¨{•bð[Æf±œ”n|bƒš”Ü}¦ÍÀ'oÈ— !ÃlJÛGsLNÑæwà>ÀJ²àB êºl…NËãApÅ8¶Ã'§úôH§õ¨ºoÓ|0Õéy^ë2|à‹÷8BÞD€C–ew½ª2F"²˜ýµèi(¢…ìM +?)˜é &-]z`qoa×ä1MÙ§á|qR…·Ù"ë5îgzŸÆ¥ÄÏ)¡ Ë4ïø°!­ Jq
+
+‚&!‹Cæýz(b|¬<'fI
+”3@ªâNïÝ+(Eÿƒ6¨z sbçhWXÉ…Åà‹&Í[+"Á
+E­x ä;ruz;?Öàå8œ_ÂD*é §^ü2•äKÀà¡ÄO’åUÛä¤¾к¶ :·Á õî±ÇGÉx“d<4'uúÍo2ãzîð½âÇ ü¤KkŒ½#1ðª¾Nº\†¶'G Ì äÛ£(iJˆG›ÜÖQ—æ¨Hý†Mðqh1%DÎÞ(ì4ªk‰uùé;?qsºÌ~çG]`¬æHèEWbÏÃ.¨ÅWò©°÷SÉuTï¹zqúÙ,—;F<0å—c‡«È_3Åò@§÷"¤‚?x•VªµUt=îRôù—;MÖÈj]Už$õÅß7N…B̵®~(‚É`ÌðS¨U’eØAy;5ÅàsI̘%ÀPüC/®“gA<Sš\G‘¡^\›­"Ôž]à/RôÏD9€€8º—”¡8[ ãæ— ¡F‚Çs5…VY&ôe>E–!ƒÀ¦Ï{Æ›mB^*ÁñMÖ#ä%à§èãÀ8ÎÒP‰(¡uÙPŒVÕõiÈ:‰e;0{µî” 
+^D?¥ÎxW)ŽJȤøØX»e”š åˆ%È ¬ƒE‡åöQ—¢«SÓ«ÇXR^vv:0Ö»£ÙŒIÎ2oy¹fþËôÀ•Ý½BE”=諆'u†ëŽù•F“
+cx2`Ya’Íf˜}=9ŠHM¦€)À=|§Vâœåº
+–NV§ÛÌlÐÎ|ð‡Ñ®¶dÅLèƒÞ9 ¢xA¯½¾ÃªòÕF‡gZ@{x]ÃRÆ°_ÝÕê.°¦cEzAX2,è6óí‹ b×Å…ýPôùPŠ`fD_ݎ߀„µ+™Ð,駌Õ,:À«`wÔÙ“¼@Äî1‹$³¼«ÇµJ6Ø Ta~Åå\…§°L7x"ÍBţćqþ(qØ (1ˆ‘Òçå;
+ÏÁvm±>V*T ëÚ=eƒÅSªK`°Õþàôæ›Å4øüŽ*–» ¼KPMZ5¯º×Q÷¢KDà6(vú«;hÈÁœVQ€ ê@Â\9£ÛÐ/(%ìµêø¹†¼eú­Ðyû(å㤫×\¤I¡R•äS×]îùJ+£¬¤Þ­œzûN‡Â5´!÷¼GQ¦‡ÎûÀu%Çõ¨(¥ÈÜ8Æ­0–šþhÀ^$N¨­MM–•7£&œä©6¹ÔŒ©OÇŒ °GU˨<à=ßÄTÀµÁáÃEŸ^Ôå¶!)-È<,fq¨BàXnHFïpŒ.‰EãeCXâîïÆIðI““_ˆsÀ6¥È±ï›žŒÌÔ´ÞmAKÏ–$ß_LV÷‡ 0çë øÔã4/%Že›Šß†øÛÃw@>‡`Èqië6,ó\!æéâ S„‘Ùë(,sÐåð£ÛØé ”T±fM‰’þEw+:í*Þ[—z(ùáV§ÏBt«o^ºä//Žl‰Æ¼ʤ×òiyŽèr`EÂòb&‚K…¢aEöÃ^î*ü3$;Ž¾Á^ʇ<bÛ®þæ?ê‹ÔâÝ>~§a0Ÿf¼n» ¶< Ÿœ')iÊjØpþrQ;ØYQwLìûß©èE(‚¼b”
+þô?]”äó¡(VÉUt8¯'¥2Êåw?uaÕž%AHÅvÔ%Z¨lTzÌA¾m6Ñ^ì„+6·Õ¡:¦£é“ŸNèrœže¾¨À(Ì SĸWøAȆwhì_‘°Ãô#mwVÂìØøÉÊp'¤Þ$Õ¹ŒÏD×ÙœâbŒ¾­ ”+–€>e9¨ŠïÇíààA|
+m¤YÒy¥mê9†æ‚nóþw*:ŒÞ#w 
+Èÿ½èQ_S¨Ä2 YG5yßdMi"DÀ˜!›Û|*â>˜XE*‘&xL.¿U¼
+€@3+ÐHd ¾Žþ|¹rÚM–>ÇA„7²´¶¸Fç#pÁ
+ÃübÌK—î0s‘¤„`ABU¦õ$
+i¥åt.â0äûh
+HÅïT”°A(šéâºmfcJD‹½{n7•±£ÄÀ;žn• Võ ì“,-%Ø æ­Qµî%˜h|)±s ŒP”ð¦XHäÓ][ŤDÚÚüNÈA 1@»J´
+¸ƒ¼”9-.¯%;–É>ÀÕ¿“Ìèÿ”—IŽd·EWà=ä
+ê›qyøwQÓ¬ýO}.Eˆ'œß€a¸Šñ$‘—· µ{ A‡™³-¸+ÉÚ¹
+·“»Ò¬?ÃðÌi¼­3®öÀRúû¨o+¶Š&ŠÌ_qv[Œïÿ^°µ…$E‡à+¨Ï X'GÑÛbFœPÐy)Z“Üô² EsõüƯ"ôˆ3ÛÖÉ^!PZ&•4œ!>ªËb]J~ÛQƒ‚yÊû]Çg¶Xtb®»6¦E0 Ρ™Ç07EóZN*I­*_"Ú(΢“)b§ùi‚%üqÑe`@jjýÑøi¨ç• ³
+ðh µYÞZ¡ŸáŽT€ÂJÇj"±ÊÉ#›ßsŠfAæ^*6ÝóŽf*ýÆÕ.ñá;#³ï ¢õ±Käå‰
+jJPV‘DE»dU²ÿd{?'ádÛb²ÛB›0Û°JLƒ"ÄØ—y+ÑQn…8‚ò@ï×ïðʪÀFìMÃKìrTS{]%}tX“1¼ü¾V.J¶gAŽÛâ­HJJ;ò&–ù%Ûƒ2²š.‘F%•ä™%52±+V¶\
+x#‰Ó¨>4ÅAà —-þ €\>˧²¢ZX 9{ÍD%ø–‚/ãy¼¤K3䀢U@òhF³±ë € q`Qûý8<RàÙo ƒT=wÖ¾nC ulžÐb|p=,íê/C6®MO¬N‘…å{^ý-3‹ÕÀy»­ õNÓÀ¹ã½æ‡- /Q ¡f.ãö8¥„J¢U›3p»­‘µz˜V‚hTcÊj¬§k`(nä’ƺ5„uãO¼¦£.úF`7í3ô3 ?Ì6yI)È à
+MQðZò†Ï_ÝŠN¤ÇA~MEnÚçÿ£è¹}×ã~Pô`„ûb=ŠŒ]¸ªS xU¹ áE¨b—kQ™j¶$7«ÈŠ.sX˜þbiš•(â½–îÁZåøp ÃK²Ñ*Ó¿PÑ|˜1HN)á& ’ W^ÂnEY”PÖmXÑdøp¦”訬xÖ@!¶ëwú«ƒ×!8óòëmº© _{ªíë:âÉ€áæ&áè%Ä· 5dØ·ß*Þ&å'½Õù"­ÊJs™}Òûe(X•¨
+‘©ÜE µÁt?êÑň/Ì(¡lÄÙbJš|”;RŸ·Q=K*#¿}çœó6
+ X)xZ$Ö×D-Ð*\%ĵğ!ÄÉÔ"=¨GšîG‘ÿ@ñÈX­õ¾0iÉfŒòÁЩDì[´VtP÷ÍwßFQ@ t´w›»„ð¡²Ø” BŽ(àÖ¦«$¨©Muöp¼'?· j!»ûC2É—Ðh'!YË`jCO½p>9š¢b…š*‡ñØUÒA4þ [ Ø©„>úA'Ë4“ŸÄ7ñü¬üØé”´¦¨ŠxDšÛ4·¾L!Ï5;q7ó¡$JÆ«¼Ï¬~гˆ¼Ä¡Ì±OY*{R X<L¹ %Ye'Žl¬ÑÀNÑ‹ dþÅME>ZvóˆE‘´vŽöÒåÌàÝ" bu9ñ)án@ˆŒUCºÇMa¯D^+X÷ûwØ„¢¨«£óôË`·”§¤âVBÒD|†¬¯?;Ëï¡üv1a5Aóì Å_ÎlvB(0µï
+¯1Ö‚³ª¬ùqz—››z~^ .°eéMñ’>ØMPx“Y(YTjf9‘Pp`kkÁuA†‹¦8nd¼Åù³Äã|+Y'hTêj{VÇw0Šè'(}>†WDAìÄ—æÂt¹¯G¤ÎûòI0»…QŽAÇ’~é÷Òf {…‚ ëWÍâ–æEZ~㵄Ń›`‰"ÜGE`-G” ™_¬ *Ú6Sä1á,!´uÔ¬cj_yœ3ÀŒŽŒ3Ú\Ìûx„Ù¦~Ft*qôÕö_«Çø’§5|q—-™ %Rä4Ù
+ZJÏÇ’Öã306Pë ©A'~
+Ã0ð.¸…8.Eß«ï=Œú÷,‘|€L; ‘µw 8ˆ%)õqóF^Ÿ­gOžÂ«ÑðäO*9±R¨<ûî0q½fêÞœŠcˆ[kÇRð×SÄê¨ÑÁ­ §Ýfù¨X'¡¼ ¿!ÑûÂÇgpý“…«b<¿ †Œ…žšæ¢}”C³ <Ô…¼5›¯sI³AàBZŒþpQYžk¿O'jnµ%»Ó‡’7³*;¿þæ¼€A…Çjdö–cãðW¬$w3_{+âN]JsãH
+*…¼’÷ËÙz„¸ŽºU¸3È2LÁ’}Fä BlWO@“Ùkx• µ}×á-ø#I2k QúÎsâÇmNä\^u"ðèΉã£Ç·’·YýþÉÒ(3߯$Ò@Dqµíò½?ŠÌ^Le7 …áU¤ª çÎIÑùP±’fPE=ïÙ¦`EõGÎ/\$G…­‚„bÕq>xö5~ÑEóLäjïo³?b~KNn§ïßù‡ñ2IŽ3×ð t@ÁyXkë[¼­|ÿíûíV*äŽèè– ñ'D ºIï”×Ú¾·¡/(\â)Á=Óå)•¾ªœE󘽪IÃE IIþ)tv+;HßU^úBš0>XYQlÇÒ…NIWÄâWº{+üYâ†P8‘ïó-¨y˜üW\ 2PX,òÕþ]R~,S!ò"HF­þn4‘¨&à_éÂë/E¨<½ÃÐ"E•OŸ%^zóÝvŒ=˜D…/*¡ yfËÀÎ(pvK,¸ÓØÎ
+@”TåEh¼ûe²ôƒ*šÐÏ]Ø©"Ç ®Æs…>4eè³tœ˜Ò£S&Šk,¨¨ûe¥Ù$“¸È(ÝJ¸=TÆ•âÔÌ.$ÁIJϷ¨µ%¿±Øøu¾ýõ}Lyád ZIzÜøQ؃‡?ûõç"!­‘3´)hŠ˜PÐz7+ ±¢íôxÂT„Ê¢^¯ a@h³ŽåŸ’yD¾Ð·}Í“d³YN;Ÿܬ)ˆonÁØÆú€¼ö:‹ öôOý€ã?Ò•6¯[BkI©·Ø†ExwBÂfI÷Úxî­\»Ó]hl­ôøS•ÐFžÏÆ;µžùÐþÝîÕeõ%ï¹{0 ¸&8(ïÓ¥±+Ô¸²%ņ*×_„70„°S€Zíf@!5iò
+‘³!ÆÓÉœÇOÖA½´s,xò‡mæëg;•`Ù:JLd)µöSBœO±¼iÚntvµdr_ÕrE¿^ÁœØNÔog`¥¨K†‡ƒ_š_£9ljñ®’ ‰C ÖwfL©ÊCºZ·Z— ñnŠrOÈ©Ùg´ µ^D?ùSŸ_sðýT ¡bqãVݾÃúaèŸhšÒMmK¸Ž¸êà#™e:_sHh£7e_Bc÷`J4-/+]8%±»û·É¾ëç©Õ<øÈ‹§Ëvçå\NlYè1ð›ÍÎI f—ç'¡€<z”Ë‹ЂJê½>ž‹ŠšOR"ÕG}TôÆŒcáù¬GÕž^iñòóRÃc¸
+@ýD‰ ‚q±rPÙ¥¡.‹fÿD‰ lá¸%¶Ãs:‘‚cÄšûæ¯ïœØy›Cß ;æÄŽ™L%é˜qíÌ)Ë´•M)ÙÄ’­RŽ\ˆI$\±=øukÃËÅÉê¹æ‰£ÿk‘2“掊ç"Y‡ÄzÓ$g±90ÓXI†”dW&ºØèÌ—™›ä Áô(ÓP7Ú=¡›Ò6w‰Ðå¬r“æ¡õ[>å’£¬Ðk6Ùn<îTZÈ/<kã D€†Œ­U^œ#<vÅ‚ÒÜACZOX~­Ã„ìwÒª·ë|¿ÙÚƒ‹"ž_h¥ßâÀkÂEØ";·6Ôtç%P C«©„|0ý| Q9òmàÉÈØgø
+2*èùJZÝÌÌPë’u&¬Ôö✮ø‡Öò”Ëð2b’Hxõ=†Žk@‚X+ÿÔ8¼.’<ò± ~(žÞ㮽…>(©š @ed› Æ÷hö(Tሒó]åu¬ØÍJ
+*NÒƒý®²¬˜ÄS*j
+»d,ò‹¯=‘ˆÿï;=ì"Ávu§-˽ œR !+á•U¬‰Kqò`ç¦,Š²Qj?äoÍ 7zd—ÎZ{ 3Š¾N{ Až+(RÉ€£õí.b%]—KHÕJôéUûªÓÄRVNÂ6Z[o7F¹Vؤò÷
+mçèÊlì— Y{­4¾àEɆy1çÊšÙ?õXD<E¯«<•0RèG{v§¥÷$ÂC­ˆ6àGôÀôOµŠþÊü‹ÎæÏÐâbç0_“¸×®ÃüÁŸ€|±†:¦a¤cÙxè÷)ªæ£­¾½(n´Sˆ±Á ÝK)3’º]19–ŸÔ1^k C,§$µûÃ[ä׆óü1/Ò,o´lAÑØuA•à±$„”Eê£×#^H±:¶£GO—€g‰mqO„ˆü8Ûų§¬Ë°S‚ê)×à>ÉU—IB¿\Ÿ`ŸBuÇÿaµÛ5Ä^3Ÿc:Ž¾KI®‰b
+i‰O E çú ”âÜ·tÛ;¼Ä#
+ÜBô‚×{öûEÿUÖ¯‘ß¿æÔÒ ísý|ØõßV>
+WåÕúý²Èk Æµ}ƒ±œñ±•°éZ'´¡ß‘àmðüX[IÂ3ÝÚÇŸ³„Øb`GóæF~)[âÓŠÓá
+ÄH•i ˆçÀda4ªK¾^ßgÄ&j5±”1ú$žH¬
+t|ƒ
+IpvÐ`é˜6œ®,Kv‹æäõªµ5[ršþU§f;jÈŸQ™‘Š±V1ÜדD,îwè »ŽFÃF5_É°j1!÷1ŒiAÆÜ°…„8…×BTJÞG‘$ûd°Âh;ÓðR)œ\ª1iÓ‡´1­í+«0w–òÆ“óäp ³zÞŽÃØe÷ŸZe„’˜;áƒ_aÂFßEÆMqiÄ,ö,Í'ã†{ê.ûýH¢â[H…õ¬C+/÷¸oÄuÅŵc.š„Ê ü+î¾€~¹ Cþu\`Â#ýš¥zFÂqF$£0È–@ƒ‡sæÌ©]¼¢8Å|@Ó­__b÷›x0–ø Ž§y\©Õ¾_W‡¼
+÷õN½—I^1ØT îì0$(5³ÒB¨«-ˆ2±ø7¢êBdI´dÁÒ­ÄšÂu+À-¬€¥M•€`9_d5wmçë Uu þyƒ(%`ø Ù"‰)$x­oÃ@³Vr
+¹?¾ªâ/‡;bñâ(…+§[añÒ¨}Ò+¨Îz†Žòªqôz £*aâî—É8¤+RªÕ÷Q·âDc8‹Ñ<§Ä@Úâš4+½VÝ!vÔ­åçwŽÁ9osŸ?ïQÇ«8äK ±Ëp:» SM<þFZðóN}^«‡@ óÕ7ŠúmeŽH_ÐQ|"»äÆyÜ ôõÎŒ5Ð$\g—†øtõü
+£5±—x°ÜÇPF$ ¢ù
+[³m}£V®p˜Å yæ/™½Â <L!e2³×íab Ó²ä2^ò—Ðø/¾°Ë«Ø;q1®usÕ ôµ@€Ø³%ø$e)(´bb6¾"† ‚]¯ˆQo½Óú¾!²kì>…À¡úÌš$Z·¿Å%óïØN\÷úŽ®ŒÙ™ú™A/hxÐÖFzšÄPù}ÔTV ÅZ•¬Œ¶n“=Fƒ mHÖ{ ©‹f¦JQ±ܲâì(ù)”tÚ5=,—žiñ ¹DQL«Y«3¥?ÏñwÎ¥óòàÜnŒóa],¦¢mÛÇæ
+#‰uÛ©+fȧ{«3g¢„ð³à~'¬“rQÖ©qô{MƒA蟘53.»ëtîÔü§«¥´&Ï —ngžp׌¯}%PZ¢ZŸD‚=;C‘…¡Êµ
+‰­×êÅ®“ÒÖ?=±ÇD3£&H†âðì³Ad{kB’µ9&ý5Y•[ˆOM7ЯP¢ûº¶viQ“
+h3VEíƧS«6ˆ]e !^¢Ç¯—`Ã5°]‘U 3»ˆÂò5Æ]>!ô©À»=ÅÚÛØwÅÊ)(äyN}) ž|ˆÅ'h;Ñ0ùß¡ç
+/ìµÅXݦ0Ž<…e4Â¥Û8;©Ô>ªS™©§Ò£«ë$É{·Ò¯êhhÃ(6<˜*dí›æÀ)±²RgôòÕ‰<à·ÞWÓee*¶d2NèëD²åÊ#…‘›Í»´ŠâÐèý,ÙBƒ+ïÝ[~[É+_b
+·ã±WdJÇ)xŠV „&\Rl'AÅHĉ57›ÙÈŠ×ÛÃåñಔ¶Åb:ƒ’S¼Öjª(“Âþ†–•iÜyljp#Æ“çn=Cr
+ö¦ÚÔ@HpË×ĦYö”ZŽ¢ût¡©1´P÷“ñ LÃÀ•£|”ÆìlxÛ_QÀÁßƆ䭓҇âÆÔžCâ{<!­ºÜO3‡Â€`ȹ¶XÊš$aUƒÌRb`S­xŒCê¢ÐŽ'È›(nY¡ˆA—p͹)àÀГžØŠa*E ŠâGÔõ2þ
+³Xã¾úSW¿ÞßåLûD?P~L%Tåd†ß>æ%$ж@T±Ž^ÒA¾&¿k~bíËÉC‹–#ïï/ø¦ô@É;€Î ÑDÞ~
+ˆL7¢´$›¬g-éeêX:þi\j–ñ_¬”}‡k`5ØJB“Ä@ küH.iÄai8B•Ô6#±*\ˆ²ëFhXQð´ ˆÓåÆ&?‡}Àµ|º:UIס¤«,ýÚɨ$e0R¶
+cº¬N-¥záIÕ#é4ö´ø!¬7ü#þ@æ îs¿Ó>:á
+3â|¸Ö’§)->¼ŠÁ£X<«òß­i6×RtÙG¯Ââð#èô#ÇeN?r<ÊSî£8§8J|úˆ£Už9Z~~çœó6Ç
+:7ÏGðö•Ôó^&­$FNSï´–ŽõA2úŠ¯ÈéšR%Z®éëá}Çõ¸vgÑEÚJëEw„máYb†™íeDíæ̺£ /ZŽ¢ï:ó]ŠžyY±]L¸&ª…³ïï@eÈM«Kä½f4<~Æá° _Iß6Ò‰°ƒ˜t²´Ùzõ¾SZÃTÕyM}ð2. óÌéEñ‚{DË·üetvŸÕžÄ¯\þ_b({˜yÇp‡õ™æ„ÊÖfÆ¢¾ž;–bA
+ÄÙÄ1M“y%ÇPܲÌÎç?ÈiyX´ QçÛ g›\ÓµÍ Ž‹ ü–à‚^ª-:•ÅNiôáÉœ øPX[öå²¹ÃØûtÉ÷‹ì©|dÊ·®hyh7ˆRõ†±bªï
+)Ÿéöô\­
+µÀhº‚·—»×›?&õ3niœYe@¡+<Û Ñ˨šHrüÃI’“á*ÓòDƒoû;iðó¿œ'¨Ô€•VÝñ.#ð„ÉbD"Èc„\˜ÛŸ‚â Û™Ï*/ž¨¿`bzœÆ¡hŒ}ØZˈ¥¶¥€Øÿ·1t+¥>˜úr¿~wžÃ­Áš6Üë*¼–ù ‰VÔe^ûñ„èªübÇ+P µ´öIçT«d‚&1¤í×äÄ¢ˆNM­2È̦…„²óa æhËs@l‹Cûd„pi6M—ÕÁ‚¢]«)meõD`m^V”!²†Òvó›¶Y²t†|Þ—ÿŽX5¾ð~ÊÙžÝÔÃ3ÌífD6‹Òt×÷M7R¤EAOPöV3ïÌâwZƒÃ=“øäÀ_÷Eß°X„“ð|‰ÚÃϨQÙç¡óT2²´|=€dÁx’e$î@HÐXµº
+
+LJu¡Â×*`à{•ÖŠ„¸Â·ùýH€‘ÏáÂõ |KAoj…ÛdK}†!XZî9
+rD¯ŒqÄr¥‡Hüƒ_P»,Žµ„Â~¯n5lú¥N1lMÄLôFazya .‘ï©,'á©Vì
+¨‡Ás•Lˆ¢ØÈ]!wx€¥’”Y½,üR ü¦éxh1wxHLu(ó7ùš3B`†Î²ç`°…wÌÂÖ𜠇Íx<z¤\«¿{ÄuÖ¯âìUC„¤Á¡6I2Ÿ­5T`ˆìVûRkWX!™ÂÌp]íB6Èùq-S슶†{`¹ô[¦GÈ
+dÍøÍ@Tˆ¦"ù§®gÞαɮ×ÜO‘Gó5$åËÀ. ¥L„.ËeÔ/ìV3r@¸>) ès´«4ÒXÌË›Î顲ck¶íŸÑ1&Û´bL²“c3GÚîTg EðW¼p@gË1åLzз<è¥:Sä·Cúwˆ,U9 …VÂ-W$c*‰IC“ð51Š¡Mò‰‰¥1ËY"P°wn*c}Ú¬dƒªüiÌìC˜Fâc˜r›Ì˜%_ûrÇÕ SM¤–y¿d¡êºÕ4²hÑ}d†!O·æ„ºQíê;”™ÖT˜éYìöŠüNQœÐç,”—°#¶¥9 ApEϘª}Žö¼ «}WÂÐòãR±Œ­Ñ qË]ÄO³>õ•˜Ê:m¾îG£müœI-c£'£ãhÀblfNCk•H/Kó!ä!˜!ûª;ˆ"2™Zgup¾F-eÆè×Õ-}äš¾NHŽòd4Q9øŒáë*&z1vÖâÅt‚GÞ0Ï©²c÷ø]!’–Š–ä BQóòÆ×"FLœA,Â¥Ðé5â
+¸HF½F@?ìº UϤ©V&rî„àˆB`ö¢|’]ìëUg˜„0ÑÕmMFªjAðâ†Ö[:*^üÔ2{ѹ(ÄEBç^¡Õ‚(p©”a¿µ£6]>2QVEfg¶á­úß’VCùÌŒÚÀù”ûõáÿŠkB ¸«Ü,îŽÒ¨Ç +Oõ€FÜ÷Ü!p©î6¼ìó”Fàÿ²£,^2¤O«c+ÓHÿ•UÐëzÍ]Ð<óÆœ—Æw^žhoh‹¡=«
+œu[±v¹¦£»-Ê𪮘|Mï âëÒ ôñ
+¼§ÎLµe`×]&¡h)á»öPqúÍ‚rµR“/ª‡¹€ Rv+™Iq[º¾›„‰SqÊõrT%s·œâ5TÚ|TõH½v,lüÍÅeÊŠß'm»»Ô9½H´ Íl‘-dxÃ<Ðô…J|A˜»î^Oæ{\
+Öü2ÝZ Vi3+¬îG;¾Š ï‰CÔ§:» Zøè¥ÂØŽp]:ÂBG6m ©9†Ö,šCŠd’PcXç²á€…")ƒðË@Oæx ’”æCó2ŠÎ?('J°5LÖg™²oxG‰Yf‡'ng€k´a¯q¿ÇýÛZ?ßñ_eÓ?þ‡[ÿçÏüõ#C¿ÙºÔk
+•GÍP¦©ˆïØãŒsä› çº7Aþ3ÝÄ‚¦Év@mà•S£s:„"ãHLzN+ R1¯ªÉ¡–]¥®=-D$ “ L¦3Á:Öås²û\w C¥ÝñgN3Ib9œ“Ä:­‘¤¡ø‚4†‘ýÀºé†à¦ôâOH+´·šýSºi®ŽŠì·P›Q|…€D)ˆ‚ÂÑR7‚…
+²eëLè/]¤O‚´_YsMeú”`¬aûüá݈4‰ LvÓ]&Ïcþ-þsN5q›„dë»<Y C³™N '¹Q":šüsXHéµ}JK ›!M@Â
+„`C5ñ+÷y}¤qñ’Ï:W'äN’R‘<
+D¥ém®Æfÿ/÷i!倶±a¤†C„ÙÕ-ÅÊ1-–ÔŸb^ÇEP ŠÙÚå N¾ñ%¦7J Œñ¹:gÿ1/ùGEí¹*¡Iˆ@_ áPìJ2ö:!½ÄqBMú•v‘“=~ç*Ãr¤6þV.»å°’cNÅqtä଄'Šé4êøzþÓxl%ògû¾xmh¼²ø4(ãáR,¾‘5fçs«O &Œ4jNZ÷@d§3AÃO·ˆ·åªerOo41Ÿ÷SAâJ`Ü®çS^ãÿÈ
+džc¬›Ž ù]eºMZ÷ë³õ[<…šÅSäéUìd’èîGRŒ¶E8 Ëõt>9Ò*±Àû6µéPs›q±ççè+Y7ˆbåÞêi45$gÇö̳ßå?¨sögó,U÷ÎÂlÇòœþƒÃnqâßæ”fLtÞÝÓ_Öý¶&>J¿-¡ê£ŸRÈÙ29\¡{Š…'ô±Ú-çØñ 7fœf‰w#!¤•vŸ¢ÂzgÄŽaTÓÉ$$òóÛ$Fí’AÖ˜¢¥l“)<˜ß#&œÙ/Û|=–‹às3ø¬²Ø
+^ÙÎ{"œW:BóÌÑràDý·?‰Ç쵸iÂy=غRe§éB!§\æà:Ø©Ÿ-©À÷æ´KZ"yá ÍIyúÂ%žg_@žž×râX­èçê˜>Xî¶Æh¹îóÆ÷¦RwœïÈöòjÍ,óÙI«ÃÒî÷Ó—Ôçã)¸…(7C1Jl,¤CT]c}®É®RµÊcºz5æsŸ„‚ÞØײo†,ÖïJÀLy^œ#F5z© ÷ón[¹ÇÇØ›?¤&ž™›mH€Cý“uð’»ý7K†Pip¹2LÄ°M²øäÿ ¨2?"­YP& µVÍ)Ñw˜fÍ÷›p„=çA2¨«$1±@ÒÙf~±/v8~ ´…õ½ä î'ŒåQ¨iYŸs 7U?s‡É<u /‘†@!ö3Ä2Ǥ™¡dÇË`‚Í»ÏìÏÑ’œ§äêægm3ÇŠ—Ž¿Ìr^k~#™ÚXô’$µ:‡s)ùûKÕhA^0.¢fãᥫ_¤éÄgˆ¼ÞnN¦_D.¿ë—A>gËÆe¸Söf ¢ŒED²~tųjõeÀzè”99⊌Á.%Isê• Ý÷§õãéò»Ú‹C§Vîj£¨ídïjÝ^Ö©FÁ&«¦¡Ù{Kì´ý\@Ƨ¢1[(­@™}Üÿ
+?ÌG5Á¼4Þ>ÊI±×Û|ÕHK,wøç·è­Y›m€Ž¤ v¤[ “—6ÒÏ3 RI€ÇÚkƒáùµ÷÷na¶Ði›:òÖ-dpG¶óÂí­-ºråwØ c¿ï‹*ƒ Bã:<¶ºìP/5k…Tέ¬Ï‘YÊðbÆg6´…Ž£Ö½ŽÛ Scèõœ“Ä»Øk\¤[+Ð{#·N%*ÞöáÜ/>óﯰ÷+T륺º½½·ç¶Æ¬†èû¢Ô“0ù¾/@
+]®´Q @ùã#zÌFOÀÉ9{&SVb!§¶Gæ÷p™×±st‚‡`BçU0&%ï[t8yÇÔq—=ÀîSë8Gʦñrϱý´íÔ­‰#ÏÎhFvIÃÊts,çªê:$±˜EÆÈE`æBñ~ A%¥¡î}c j¸ÿš×!âiiSœcÚ©ƒƒx±i±vi^Èô_ ¶´ÇK%÷üÔ;ˆ¯eÛ¤’vsBu$¹&«™øïÙz…7¥æ½jh:¢´ý1…jVX s}?©“› ¨6·zγ¤'ÛL8^ɲVlH´:CÛÃ6arb&cÜ·ºŸÏ&'ÅÖÄŽMvå³.…Úïçì„I=ò1-[y­U‰>AF©Ç4++Ix†ìý#êou§ŸSs[òëAÃyóï™QÃ} ª³~5VñPùçCx
+£nmû
+âͦš~ô«üIš6ÊHgzÄs÷÷ôæ¼hX ^©òóîsó[wœç}œ‰7˜¹Äü;eåàYÚ^dú!·øT‘IAe׋‘?å „N|å†? écnq, ¤^…r÷c•7‰NDƒla]E’ñ©Úl£WúÛs•2A6}Áë=n|ŶG4Qße?Ž²Ž- Ð[úýã¦Ë'ËxŽóË ÷ØòßK“ÝêçÂ>1;h*·-‘òîfp¬Öó~PèâŒòÚ‹/€T»Ôè±²àÐÿ-?÷%ÐÎgš–žÞí™×5¨ÉûÑÊ÷@lNì Q™Æ[Ÿ3Ϻo°‚PB~†S”öýW¾Tá‘Bú+h>Ê:¡þ3Ç쉬òYôâZ6jf±þíJËL„¦ÑˆxÁhÜësŽ$¦3êÔÏ×/Þ-Dg‚`u‘ó0ÆmqߟßV ,Ly˜­]O‰»µ‰Ýü;AÒ­zQ wùòO¦<5¶íü+Fn—澓®ÿïáìÈÊ܃„ÜMVœÆ+Ž0£|ןø ÜsmjÓ„5¼µ•èßÏo+ÐûvUFØ­ßÿ2¦ ÐûV,>÷ЧE]Îû'ÌXyzÏ‚ªe<ò>Çl˜9¸»²·v>1®Çñ‰eo H†]s¥özáà.þ D“`ŽJÛæ¬ê4q¨™ØÔºéˆùwêy½c~ÿÓ3â.gF¯ï ¹ï¡÷%»;}xö^Èm ÂYôÚ¯ns¡1†,1ú•w'ØÕ>,øË’|°6y•½¯ØÚWÚó>‚øï^‡U –N27ÎèësümÒ§6ÎÛX•g­ß{¥À´@®kÛ¤ÔD܇"öœqÖ@ÍB\!(1[•:1®ê‘säÛ"a]ŽšºÝïsìˆJÁÎVôx;gмC5ÔQTJ;— =ƒƒ5jZõ{ "ù´]èQº|Œ#åÕÈnÛê*Nåå5ŽÔ²8|:¨Öü’â»Îx¸òóÝa­öʈ\ËKÞžÛÔ—\gåùVí|ó|«uø|Žá²½Zºaêg,úE‘dGk= ¹LMjcÓ§-¤CÉõÆëæ8lˆpéÁh@ÀsrL)Nÿ×|$=‘ s²l¯`÷ým¾== `)’¼ÁÝœ-«UÖZómE‘i“ \ÎPDz¤~”²gùÊòV3Ï…¿}_%ç]Î2<Þ9I³9jôZëx#-'jÒ‡M‰…ѤµëÙí\¹Pcì¥çÝe°o,PŽÚÞüø)*AúAAZ`ð³0hìŸ ‚öñcãúoÉä'ÅÇ*«¶mU¤Ä-ÚÅyæ~®n£Ä]¡ !¹lëW‘‚šÀé:¥½¯’ùŽt¬#É„©År³&ä¼Ë€Mm¿wˆ»rv{˜GŸ¸ÏN•·éé‹’Ø>ÜFɾ/ ð-weä%’(I(½‰)x…àY§”ÄP‘×ÛÚ}Q¿s$eeæÞ>©r‚Œ=ŠÎ@¤àv÷–,ûSÞ_ÍŠT ã%¨š%¢<¥èxcLnVã[õémàÒ®n,v™§¨‡—¯´ù&v{‹×µµ¶2»ß›æŸ¼uûö×Çõ‘šÀÁôû~71’CµMœòW¤>;N{S¿añ¥èï_e_Mx&Õ=[!Ÿó™ß¨“éJÂ×dF•uE¾BL£p‰IõÕÛ¯0%FºïP
+mwzæ'“%ˆ_‘¦@K³µ–¨Áš|å#ÞE V{›ìÏßñ¨Àëå'Y ÝF:dS2ÚÞ­iÈóª`oÜœC–X~æÕñOØ›žîÙÿúSÌwF­9^×ëx>Ü…aÒB©àL=
+<þ ˆêSšô è˜è&ÆqÚKõÞûú©e++6…¥UgÝ’OKz Z–$ »ÉMÂ@½´
+ãϘM å\”Ì£2Gj·Û®U‘`›ñÊ_ÜF7[vYSÛúUã£ä9¾CË÷UsÆÇønǃŸZ¼,ù4ªyÒ¢¨E_Kôýš^ôóeðw.ÉGýÕ›fÚ—ÕGÝÿ`
+H‰Œ—A’\7DOÐw¨µ#æ €
+a_ººxãñT"And º²·´‘® BÍs™×d29²$ü2Ó.C%/srM™‡ö•bÛ3f˜œ!;eà:…‰d¿|©™P(^ѵ:ži}÷àe'„xm‚Öq äûéè‚5»@@ç~ûï›ÙÁÇc®¶œ†Ý)š£ k3?ù(Az(Ÿ´‡¦R7;è•FNäû„D›ÑÉ FB(‡™çWb®B*I
+Lvu!åR€Èœ]÷_°é„²@Ä ¹˜ Ñ‚ÐÀX?¯’c
+½¥s ÍT– ´ H?l• K" \ÝñøÙ¦ÙE;;ºIªb»v<ôö ¿¼u?µ#œÂ ÍÛíz:mš DW´Ô»!;ƒ(ÂDÕ` Wˆ$³ º»‰—I~€¼g=‰–
+ý›7m ƒôDküG•›’_3\B¾Têý&'é[’^Ø¢Uî­ªüG "ô€ñ( £
+;J§ƒšµëm9QšÑ¡ÈxBv¢;/xv6â+Œé§3n=æh>UÈ®Ä5 ½3Ó:ºØè¥Ì¢!€Ý™tz!Œ±éÑÐý¦eÙ ›`2: Êé@@˜¢´,z0fÌ8ÅŽûg0OòCzN®µš7Uf½H9‡|bÏÙ-
+ƾÑ+ ¼S t¹ ö”,W€ É8iêx÷ë* Ι-÷ã­áæ’0Ô)ux¸/K¯Ì@ ?A4”sÕ"ļ+¹²(ŸÓÔ£ðùp¡Ã•Y ö»ÑÜÉ( `Ëzu UÈÒ»´* þóºIñ¤]L–1ÞÄ r‰Ë0“æG¦
+TÌR(°òÚ–ì*!OÊú?QÃ˦Åòââ¸
+CàéØóò{‘<`<9)¹×" ÅðAÜWƽ8ºï`7ÓkòD
+Tçeóâ;^¹# ¿&å¤v;Õ¹6ÒGƒUÎSJŠ§ìaèh9O«¦x>UÁÊ1Šr‡¸¢ÁŸ¶•“š“+.ãJâÈ#]ƒï© φË<áÇYʼ„>ƒ¯‘•W1O"Ïeˆ¤ƒ™Bû~ SðX]—„ãÎaV†ž;[1¸²ÉdL»¬ìnfÞ—Aíâ! 6€ðB³¨sC ™«VM‹]÷¬%%ëIZ©ÎL&JÁ†pAa,Á9l W¾b«F\ÂöįG^üž<Æbq r‰d\òÄÒ‘kÞ˜T§•ì3òì’SF %‡9QvL*¥Á¸‰[<oúEWeÿ©óÕdh§p$Q=•D ;âä´Á©"€”í¨mýQb¯ŽÉÄ‘$>A×½ìoP—QßüÁÔ2µHTËWË« §'Ê´h³¾îôó•<e:ûEûý¯·ßþ÷†,C`Eèó¼{1\%û;]@ÙôÜ*ÐúHëŶ…^÷SêGVZYM;QnÐóu/ðìÝkÝh#5( %-`·@"-=îíd2Æ4Hllˆ·Ž Éa×!i;¡1Š˜z¼1Ž1¦”~b(¨²7å,;oÊùO»¸Üg,ŠI´ÌL¢ÜˆtÆ0ŒõD*ÈyÓ{XÁÃYŸd 5fcCßîEþrˆè­ªü–ÆRòã
+ôàºÏ¯Ü¦P»ÔÄ2¥¹KÑãã|wûBQð¹Å
+¥=EÕÞ7w°¹Þ‘+9´rË6I™H>½¥CÓÏ}×Ø[iyêA¬xEï”áîšØY¶}E47-žAEÅåàÌÀÐ3ú-žAÝ÷ó•ÍNîì@8¾#?–…+ú•Kš‚e…U—NýØ hÖ`CìsÆÿ”4™îbb^öÈ 2zn9Ì#½N‘É&e|«Ÿ
+‚MËØïÕ7ZŠ÷6úT÷1Xó©–+ß´ Ô]™” Á?#7‘uN]9þÏx™$ÉqÃPôºƒNÐÁyX·—¾…¶­ûoý>HÊQ™Huya‡+ÐIøøÈE„™
+&¬­—#08èµÜur­ÄÎWÔöMD›oÔäeHoìŽLØ5VkW&9M 2¥ï,1K‚,U2èæëç6RerÈL[Ü ã0îÐÊÑ‚‚V vHÉ*åjÊ™‘õl抵#„¢ðmï¹­çaØž{E?c¥¸öÜeŒ÷ŠhK¬²¢Ã¨×)’‡a‡ô´Q28ñ`)Yr`žnÞT) ÿ°Ü6óÕé3çºqQr’rÑÂ~B?ó—ÔEûŽÄ˜ã6Çi|¢™ÖR0°ñI5c1Ü¥báÀäŸöè}FÐá_
+ ðÄŠí´Î[I¨a‚ŽÊî’ÈÝfÇb·} ¸eËÞ` #´Ž¢E§eg0]8½a 3]è÷
+ˆdøÛŠ ž×¥0Ó3‘”€¼Ö ‘Ëg^áJIDZ¥•3p§×¿ì( Ï)øå«JðÉM dmØ5^Ð'†®’Þ¦
+[¸¿‚ÃÌIšB¤ÕÌòdYà9ÖÙ¼¢¯UÔÕüý{úModãˆpo–™Hhlûtr`<©‡Pü×¥%r+¥xÌ¡.“ò
+.=Žòz¥±Œ)Þ!5V÷;Ø„
+Å"Ò••)VlWí`ha¯)ÉV ×£ÞŠB¨ãŸ84d*èCýk= îÁ˜cØ;ÈzÁk,uKÈoüŒHaG¢ñ˜[ô’)¿Ü"œ‚ÖEï‰Eñ®¬8+{¦Ü‚1÷ž­E15`ɻŗþ‰õsR±€çÌà*µÎ,ï’-L@æDÀ×âÃw˜*ñ }ž‡à»^ÍJƒìPLuI¼)å-Ùð Û$iHæÕ èTEÖ¸žôè‰4ÚnÞ×Y•¢Ý\q­Ëuígh‰;'ù»ÏÞþ¾Ö|­ÎõOZ5•S8 Üïâ ~ð­UÂËÔ)Š.))]–"2ZV´U‹3~¯TÀŸwQèh@Œ´xD­z€Ö”íuýÇŒqxî0/% 5³H,ÿîg€MŽÚĪPz 3щ'bØ»•¨ÃÃLsöœ4Fv!…œÏIâtšJÝ1Þô¥ ’šuTæO«ÓrÞßé3ë ²%Åc VN»‚@a*š Ó©éZ% ±aÞj²ñc„ŽQêf'UQ4@>Àa%ç *¯ËT‰@’0ÍýTTÆÿÔÇrÃŒ;o¶Á#")júÞÌ˵ûF5?M\1¦wø‰ã³j¯"¦‡!’íÞ«Ç*ÓŽ.³öHñcÑš÷p¼;Z’—ù©ÒAä™ÄÑâqSe[-ÖA‚ò@'¢gô—Q"ŸN yòª¹éïìgÍM€ûìõ…J0~<sFƒÅ½ènu¸ @t
+çÛEw¶ðj*›Ä`Z+Óºø'.°Õ6ueÞ! ü|±¢¼4…ø@2Q´9Ê&x%Éû(É2ñºb‚—°AÑö¨ÖYœ’_†
+¾’†“4öôÊ{©
+¦Ú
+CB€¼¶–¬„éÒ|ØL›$!Mâ\¥y½ï£Ðj
+_fupË´ÖGƒô°„­¬";2f?ÚW’Ì(î|ïï þ˜l>=ëºKÄï}ş䔬£¯JÝБ÷4¡JuRÞt.C ˆâ¤Bô¥"ù~P.ôZC †¡Aȸß½»%/ƒZGÝ‹˜‰bÀqs÷6ô“™Y[üð* Îhè%Y²ì£nÍÑkDÚ%˜Žx]vD>kö‡I½”¬IÝ'~ûÎ7·Û\Ñw}’‹ah½7R%mýé/Õ,(úÕK^æô¹÷ŽŽ‹ÉPsO~/à
+$¿=DÀþôW ïaã=Ê=cçB]î\Žg•(?lÀ_‹ÈllŒÝn¥áÉÅ·t _€j,VQ“${*óɈ Þ3°©ûâ`-ã唳X~9(80ô¨ªNOKU(Úpý2%Ôb+ÉÕ÷Ë ÂVQ‹ž§Â¥|¨ÖÔÏeäÝk³Àá`ùÅP»poCZ A#L„1#ºþA@åUxµ’Þ.ò(“ãXFü³ÖîÁd©(ÉÞq­trRcF$vŒ³ü [I4;§Ñ¬LÌ„k·6ÃÍ 
+’&»ì³:§ÁÜd†êNâR²i.À2° ’;Üï€ †=švnÃÁv
+X.#Ÿ¢T‰©…FÆrÌÀ[þ£¼LŽ#É• *Áè@ Ê°#qæ•Zô•Ôÿ:ÏÈš®D¤±æÛüCÓ¼€D,¾ð„5_8a}¯c…HüÀP±Z¬óªë°“Rí2ÞÐ\s!Éò*3JáùËŸw6ë?Ñ‘yfÂ!
+(e™tÊ<šÍ9~MK("ˆðhMòüHÖ˜X¦éK^„I"(€LZpikQåüŠíÂ\ LxÄ7RT*ëÜGm, «‘íØíly_DEJÌ”TY$“$5ž I¦“"CW¦(/)Ô ³eèë¤áårÖÑÆH§ýGÒ‚¬êhbîÐb’äŒÃ[ÖÓ cIÀ"G™Ä%4XÑ)QÄguþ¦±?vù3AÆ(K^4’!øŸ²Û«_{þùäh3Ćj°³³s<ð÷¬b‡@Sr—ˆ†š‚‰m籺L¯l¡|½FZΫð_8¾ÀÝaNv #KŽ
+Nl8faÜ<ª*rJjOóã{q€0l vÛö~/±yiÕºjA(ã!Ïž?ü¯IÄ"\{îæ6ÜW‘Ž†Aý”uÕ^=GT‚xÓ­qà=m ìÃè7½zÌ^í=ßÎÙgû˜}þ¶Gysœd†JÓS>üe ^Â.˜¾~ yéÔç¹{YJJ“i´ÃR?³¡xlì§õ;¾ÛA;m8×]@ßï|Ó¤òàa2ºÛÛͧãt›ä—ÚOm¸~új‡ÖÆ0’‰
+c×U#ZöäYqž#‰ éLÑòd#"ˆ²h¨7\”1~Är]µƒ¨2Ïl²ïóªˆM¢€ª˜OÿŠ/μ½Ôùð¤ò‰sã4N—@7¯Ê2ÊÞÑÏá‘ŸÌXÍfÇPƒˆñ+O/½ à/ƒü ½ô”càGÒ¶þfe…C´•äIW¢U´bš\¢ ýˆ!^ K/³ìdŸn1C]×)JÈCRùœyv¦%óÌ‹Ø”h£Œ=׫hž8dôâ?5b·Ì Æô\ú¿!³Ò<R‡k‹î9Á®BÇ¢3L”>ô
+lÌ@YKuÀIVN¤jaæõ“Fcx#°>!=r*ÿ¬eÕXªáþ4&ifcgέb¼q+,ZŠËŽêMw*˜ç°ðü²ÆtB”*;mçÿsrj׆£4­8EµÒbmÉÑ?âo²üDSÆw™ÃcÆ*š1Do‡ˆ„QoËc*é Á²Æ¼iˆÑeFj(AŸÌ•,sÜ¿׈ª˜Uc\c1_J{kešW§øg‚˜[§€69;?¤è†-Èr ß7 šÙ`N2^"ÔG]Š±‰a~Obî±(ä¾¾dˆ:e±K>Ö÷ì yý~GƒÍ]¢_.ˆ%œa_}?>æ5/
+ÁÚ°²CŽÞK@PiÜÃ8ä1üô
+Hnh„
+xüG‚n§O\ŠÙ“v·ï@oΔüN$ëÔUµÈÞÒ'ÒTÙWkÖl2ÑEX3Ë4Õö$Qz*Á@¹^ÏDŠ’ôÓê+€üüK a ¦p…™# ½q¹9YÑ,ò~AÚÄáD ‚gø*•–ɪ|>× :‚¾S›ÊœAcÈ›ýÇ ÿoóâÿñ«Þ¬2®Î‹åÆ5~1î¸Wo îù[
+¾xÚN8Z¡²HæÔzuúhfù¤ó“-“©Yû$2-‘fQzÛ ®ä…Éð¥Âw”ÆCˆ±Ð !Ý †•ßôg.2m¢Ö<_±”æG¡Œ$Ä ŽÄ×Í6ˆ'“N'i4ÿ‹jÕF‹Ðç !óî³ÊpˆA.ü¬/ª³!ÚLmRå–ÏI 0Õ¡ƒk;]ܯÚàÏæU¦Æá8ø†@
+²=‘“ˆÿ
+:‡)®cûÆ$.ØBoò•Â&±”8†íGõ9‰Ýó |:‰’dˆ}Ô¢Ê+R²3ï¼j(IøÞò‚¹sl2[A$¢3Zh bw]æ-Z
+Y˜Å¤Õ«„ßaЙ
+$cÈCÑ<6˜lGër22ò|Þ„žÑ]¨—ûtuw*Ä¢†'øp¨i°øÁ¿ƒeÀ9†oõ“øŠR7„a*æDbÎO|„Pøc³W!µdæ+Æ &¼‘g[뛽‚b7l;‰¯bhC W8rò"U¹q•8©y‰+fŒ±±rÞŨÚ4
+›á³²k° ‘ „›h²Êl3ìæd¥ì‰«Uñïá;ô¨ÒNÿû@ä×ø, jq^tÞë+ =+[Èj—š$q)H¦|¢c½kZ,A› ,eñ$4ñ
+𸠵aÕ-óÒd_˜,p‘á†Q\Îø¨VU¿±?Üàù×eЃäNˆµîGAœ)…„|÷•ä($ËHQ`îÃù?6 a,óϸްÔ|„-‰Ð¹Tâú¾ hf”<2çÑPZ½(’.Õw-V’aÂ6ˆ!‘ñØ´g ž0Cг¯#´ˆ,uCHbƒ©z;¼š]ˆŒo¿šüˆìr¿Kh1\Yd;/ÿ ë-‚Ûq²¬]Œ)@zûÄ|‘Ñ
+™Æ9éRzb²‡Öü;’iÈR`ÞÚM“*6Àw꾉ÂK+RX ñ²¢žŸ£`Jb̲ ’3 ÅÀÚ¶R¼+–3 O)Ìà؃.šp2•oâÌ£W;ò~Æ>5GÜ®Zéç;¦œ°<VlH›ëuÔáMµˆ Ç_P5-ÿ:ih=Ë4ó¦ ,`ª¹¥¸2mz8>¿ì¡»ÔüÖ–ë#f×$ºp©1Ð× “BÙORT£ –O\pSxó¬†3ؼÿ];™0óò7ŽKç(ÀLÙ¯c(¢‚y¼ÕËâKÄ«µy[¤ûŒÜ© º#1ê­ÆÝ|A쓦²&jLVZŸ!NÐ8ÞÅ•Îep1Äfúºà£µÖ2$±”t:¥ŒIꃠóL(!‰€Ã‚PÛÉut…÷%-qÆ”€R)8¼Êä%«þðHK÷:oG(ȺQhÿõ
+‹J!…•x
+¢-WG4\š™Ó!«b©ò®Þ½.’M”ñáU„åž \®W½U¢i’rEXâòÒªsÔ TÉ£Ì(ÁlÔø6ˆ)UH^õ¶4ÆÕyY>㿬°q¯^ {,î=ûN49o·‰&ðöª`!ÒÐ~´RÿˆÉ€±+Û3—NýæžßY± ]&òß­ó×n(*Mx#kØ WÓô¶äþ÷úúÎþÑÕç„*X–"e¹^rO•æ¸WŸL0 39mÝknp«äѯÊò0´Äqø æ1Ô]?sèlè-¿QuuÝ ×±©Ø6H™¶$_é µŸ{vü(¤I:t*~Ì)IÁwnĬ gs#ÂkF
+Ûû²žÙm1äAÂóï@~ÈÃgÓ6/r["B1–GäÚæúŽ<¯4˜%ã?yÅ_ѲŠ®‹¿v®2‹²È²ÂåáÏ(ý¤J ”œ% †üø+5ï»ålÐ7{8¬)·™z¹@Ä=Y öÙ¼ ¼«j)‹[
+"˜g{@ µB-þÞ~_‹
+ËÂœ§€.oü"çFBE»v ¬¸û ÞA™im^ÁµÜ,¹"S0e­"⺕¿L,ˆjÊ<ñôÖ7„ºOƒÌœ˜¯§Ë.@Uk‡óYû˜hØŠ ˜ËtT:¡ Îjà.rVÔ«J#€ì~½qæ*Öð;÷–¿_æ>9Á›îx«Í}ŽoŽøðÒ©Ÿß!ø•œ_™ oUÖM|Ô‚ÏýŠ0ù“Í”‹Ók‚î‡rPÖ(ÞO0s€‹çêEϼ>c#GëX)°çüo3èÏíúìÑŸ.í'ã
+›¤ Õ#Í,Ù|(Q÷¾Rû‡ü ó%"<|WZz3Ö›¯Û¨°„Eå/&˜A@–+•êëñðêìb­“p}âàU§zH~Õ ¯÷¯P6²Ì¢a§À™}‘›ÎÛ†P², i[ãÄ÷*öÆ4Åè¥í_!hï8ä]ºøaôIì”úUL ñÓÚ†`û}qkÚ‰ mr·LšóÔ˜³y{W-Ž€¯"CÛ6¼ÆT˜¸H)°üÿ1^&ÉqÜ@=îÀ(0ky©[hKÞë÷ Ú]…²[¦¬f6Èáη(FfÜëõ+_B–›˜¨Lé|Ã5Q鼺8ð”%;JÖkF‡®cüU03âcòcM³žMCöÿ½²›ÀÅ ¾ EºC2—.Æ!ñ1SÞÓ–fÌ ”8‹_óß‹¹ä\àCº+cEh¼y†8ú:´ƒK+S!JväÝ ÐµÂc¿ÝA×ëÞ
+óElP-å\³z
+’kEeÕÉ" fV€âïZ£S3.¤âº…SŽfyœù%æ(d¦þ±‹É´¾è)RçÔ`®›°KUkÑ%:WˆT¹Ð
+Æ.}Ë]‘Öfðð¦4 æ%n§í³â¡ô'Šš `?Dõƒ)ÚÃ9!iØ9el^”ž‰dÀž%5J[ê1¯¾¯ÎPBÛOb˜ 2ŠÍO‰qùµD=ì”&37ÒÉ!pðÐA¢#=„¼Œç¯§ û ¤\°+a;Þº/ßáºw‚®€pÜ«kKÇÁ2A ¼Û
+£Õت”µd—cQÚ
+Š_u¯Žž#)‡žÊç³Pø”|ë½z Y½º÷üvÎ}rnÙÜ'ðöªÓ$#U{¢‡öq^ù ð‡Ñx yiÕ/߾ j3ÇÖ+/
+¡0äÑCÀ6´¬:Þ»…°l ´Úß–¹†®A™uu­þœ˜V †ÅÀw(.ñcVO*cŸ)Ò WÍ¥3rŒA[f!’ª¡du<«6‚bA6“M;¨¥ ¤…“¨¥p0ïK‚Š9Vˆ0ÓÊÓìÝ”èR˜EÛCÝ@Q÷ˆÎÄBL%¸,K*éSƒUü9'Íá[i™%D°˜VÁ†}…à!zªè†à ‹ž8éý&X €€ŠÓÛ–&¶BIÆfÇdaZh,Yð¶"L¸ˆ¥›!`Oh÷XöU· ¸žˆÀÊŲ2¦¾ŒL/î–:ÃøŽ ²E¬è$VˆnêUR ³®šq¦@ÅíÞ’d]Ñ”Öò”hÆ"-'“Hnr|)ͦP€^€Ã2ëñF}ÐO œš;µ¶öPVDÅÑ¥Æú‚‚rŒZfùâ"“Æä
+¹ª0“ÛšSP2Ñø ·­¹I’V«àœ&b¤Î¡R˜²AC<¡ e¼
+`ƒ—yú‚êNC XÏÑŸ^ò
+Õ5Ù)ha<—ÅØŽr€ä8‘MC^nAu“ê™2zÌÿÓ9׎_“¹ÎáI÷¼•æ6Å·úvá¥MÞØÙÅ`›… F•Ö´¾§ã¾ŽA‚¤ƒv™Ú-©fT_–Úí’+ÂH’xÌM7 Hè3»¨RÀõw/ÀiVx,ÁÅvëF-kEÅFEèÞr>Šú{Ûæ@ÖìAŒ±*gs@Õ䈪*sg#õ±Jæp´5¢ËÄÛ‡L‘¹ŒÑ˜vìC{BI’·˜äKˆ!*:ì˜":à[|àzG)x„8Z]!¤ `°.©àV¹È‘é”íð!æ¥ïŸ§˜ ÷¥©ÌÉn‚¸1,zZ º3JÀ}aºWH¢Æßh—ºvBcx…»ÜÀˆ•tð*< ‰Ÿžn. Ý1€“Îòã„“7ƒ®FERŽO§ -&vŸ›†•M1•}® •F7"‚ýQ3ȕ̪cÿØ. Y9ƒa„û½8X6†8!Û×.`ßXvÙúchÈû¢™ÓÖAƒ¹áöYü¦ÿYÍߛ۔Jï*¯ç->½z8Ä4eêK’~EƒrF©m‹X‡Ž} ¦ÁÀ‡˜NOg²ØÈ]d;ÖMœE†•QAP¶ä ‘¤\…„ÖMø:€ûÌÛ­¼„誮˜Øïi{¹Ë9ÙÇÑ¿]'GIÞ…i³…”´é QòýD1
+M*ð"cÅ Ÿe„ºšÉ–ý¬F4(:«ôýô)mŽE¥¬ Ù31]‹LQð`ì•ï1t ;…ád<\FÐ-„ÄhÀ”Í»F;ÕTÚrèË©¥tYý8¯M~S¦®
+ÿyg·þ!žòø¨ð´l ÿ·vþ²  &1ï7k¼
+lB"ï¬i)zA2A˜v«…ö†aø›ñrI®#×è
+¼¯@A‚ÿ±§Þ…§òþ§}’D©­[¨^G¼°eˆE‰üà½êõü)‚a§²O†c‘“S9f “…m²b¿iû…ì]{àÁUÕK)¾p‘Ûi
+ ãëaR_Lü×GW\#7ÖMëˆÎÈK<B¸'ÆÈk‡Ö›sÒoì»
+Ž {t•s¼9l#~
+e°0Ñ¢ø$X Ög#8ýØb#¦´¢~5kW@Ø-™/&ýS%ÈcoM¬<]q
+¬•ôQ©Í®%¦óK6~9¾ѠĦ`Ž0LÇ…JïxÕ]¶úí¢À…fÁeÔÑ
+Â¥ú—¾Àᶠ//îFþ ò€&…a¥yé\ÛÜÎÓhAîq«)ê-ºR‰‡yð9ú ¨ !ø$?E™‡Þ׫ÙAQÇ^Oàî$UÔÔK"!±y8 ÎcdƒÝži>Ô Íè?Zvç!Q$ÍEõÊÌE¢Æ ië¥:«b¸öhÁy¥ÉÎ=µ²¿‰ËÉ
+€9ECyÿÎx?‚DCt!NHsQ#çùwÁ¼ü„~ñÒÈž6Ø¡¢‹³Â«Q „Îr!“_Êq úÓ a´ JêÂw ´oÎ0H¨Ò_äCqh|ƒÇ55܉°èsP‰G¦Ëcj$hà‚ßa–©¤ J°Äk`«¸ñÕ— Aì­Ø‚æ[˜$(aõ“Ôò‹gõåÌ?¢¥E‰óíb%4{ ì¤×oɨ2§%¤Ýj·(JP6f¶ëS¯Q¢m‹(Ʋõ(JP2õo<oÍ:£(”Ü£DXô%‚¢ueȈͳZ%ôt¦LœÃ(@ú5Jhì΄¤YŽd&ý]®„²Ö÷¸g Ïó<)Ëã(J4¥>ÌÌ2,ê¥Ô¯Q¢mo×a«T‹EI‚Špd“(éQ’PÉ*ÆDZ~Éâ(½Æ„7¾Wô’%‚¢×,!ªÃÅ°Y˜•5¢,AÉH\Èkœ%öúÜ¥Že‰&;È&¦ª"€}-Ü‘ߣ„¸Cy(Û]Îá=äÜûC”f4^’‰a£„ð…³Ç
+¡&cEQ‚’,‰@9éêÍ¿Q"@à=JÑb—#ù%t’Œy墄…»Ä³3¼å„opéŽuÐ .Ìš¬<ÿ{j…EoŽ‹Ï„Áë|ãx×Úd¨" Q¥À¶¢=§bi÷Š¤aFΕaÕ%ßÌã!/®ž³ÞXC¡˜ùÅ; [i[õ‚ÁMáßeçÙÀºjz¯YEwÓFQ±í*.x}»ˆÏÑd® N£šÆ ³ö¥m08T±uÓƒ}cà bî.Á±æÈ
+àuö'N¦Œ³¯íA¤ì&?g{"ó ‚’(àÚs— 9¨*ã´ýRòg£¢A‰8ñ,ýpB.†)%2”°X,8«ˆl¼}>ųג Îî$V¯ŠÏç9‡Å¬Š&,¥w½'R%.Ä^ é|Å$—½xk@WFêV?¸¸#H‰öoe<áâ^t·×_n臻®p”dÊÇv±!‡3þ$<üÝE
++h?õ8¿—"•`B…4Ó¼(^l&‘Ál—°)Œ´á21&¬ÔДí)BÙ™˜¤j¸}‚–r‰Lï>g"Z,n€~Æ.*á[=ï’%ÄàÒ{)î°¶p妀ûëGTdb»%Û+°Û->æ…aì-*zßEˆÁ
+CœA‘JRBnL°ðc0àQthº3ìm™æg.Ú´œƒ(d›Õ~‹¿ñ‘ÎrºG@VgÕßk>çhèJïjÁ¦¦6eäS‹+Ä‹ÌE†|ú‡n5´˜Õ•_Hç;]zÎÏrΗð#ÑÌ…EµS!‘eŽÔ\ÿ×
+Ÿ Î$+ÑžkTÊ…Ä-t¼ïcPL¬~Žþ< ï ä…Z•µ HÑšÁ’À/É$«Ê³‡ÌxjˆÉd4 \+¾¬ÈGÝk CµKù-nå§`Š<„‰¸ðPÛÀüžk®Â@ÝÁVg·ò6çI|)·àÅbw$./(ñVóÐÅm¸ø Ï1>…M »Ééømø+9ÌRÝ%ð˜µ5~a˜»Å¾+œO%DÅ(¨¬w' µî%¹ïšî”!Ç£‡„…_Dzl%{ ]øÈ©QQÊÜ `낤^‰È0Ý
+šEímâÈ]$Q•î|Jj›E~¥!æ×ö‰õ™rbÔnãQÏÑ2çÎrÀ:p0/ÝN ª2 ÍSµ¾MOMó^ñDMÿÖükГ«j´3Ó½¸u¤üÐéÀC®Ðç¥ÿ ‰“/9Œ #iÈ‚º˜v>¡çq17 åŽGþ\”+ ír¾YƒDóp¿’ rY“+ÜTÊä)&0(9ŸB`P.eõðæ€ûÒ1é—E€Ð0äØŠ2áRþ¹/6Ð_5`,Á6¸FNBƒZØ‚«9r±¶©ü|©Û¶|`ü²F¼Ñ 9È»ØCIWˆB1HNGŽÀñ˜4~Ûß½?AÑE”;`'†L®YY¿2XŸ[7ÊÙrÑå eؼÌ*L{–ç:7ÂORܪÉ= ’ía÷Ça–Žþágñ\›Ü“,Õéq×Xç«€¿à!€W.×RMKvrCLpëo–‘l$=|蘹›}GŠ4©,a‰sm¡mƒà„{
+Ϧ8vLmon*êDÇêv–°.]ƒ¨’Wò¸ gÃûK°f#â¡(=]Ñæßø£
+b!ÿŒéÞÓ¤_1.é”
+¸~1¥´%MJ ÑŽ{$G¤'MŠŠÄ2q*QÒ ñš)AmáOyJþlXLX\¤õ¿b}>g`™øK £^‰õSÒ¤cœ$ 3=%M!ÕCŒ@³å(iR‚+Ö@~\rô)i
+étø?ÆË$IŽ†¢'ð|‚
+ÎÃZ^êÚÊ÷ßú}ìPe"ÝåpØVM2?@Óø¯§¤é9#oÀ?ÓúY’¨æÓ¢{Òü~E¿¢f†,à(¿Û‘ä?Uû_+Šr,õàJ{u‘2&5£w!x°Ç(Ñ¥}H’d\jÜ›Š„¦eux÷]
+8"ð‘ÞûËöF¹—
+ˆÐXíÆ·*2÷“Ñ/¿©„—e€’άnk çKk,tþ@"4˜Úf}ZA4¥çdYúpøëOßL_Ð`=BLzðÍ÷"Çú šC Äoö?.ºûf¯æÝ7Sw¬RhÓµÍü
+² 5Ê«rQÐ/y迼"êí%s¶Ðë:?.rÖï~Ý'EWJp7ëZ´èÓ…ÃV»¯ÚÌÐ"üô&va%Ë3l5™¶xƒ]Gw‡²%y±xY°Ç6ÂÚ>Wé.ÅZ »JpamGë[[%\¬íÆÛï]U•CðK6’{‘ì/&& ™¸f=âh–% 5eö‹Àe€†ª™º»E¥OÉZO){v~½ñ§¯ Âj–&}G&¬‹…Á,–¯þtþ¨”V ¥¡¨¤½…™«Ó8d®©ðÝX礤p²|E\%%âÖ„ÚiÜñÝHÍþý÷áȉ½ÀžD'£üëå!ð‰Êœª Ih™øæ3ëêLxÀ
+Œ½ {‡Ò¬<×Ù(¨ñ\õÿPÝ…z„y$Y÷y §Ž_`³ÚFýµˆð aBüƒÙ|kWÐÍ"â- ,jÂÚ*Æ+¡_0p"ä„êx¼õtt,-5ðò˜º ›(aàg½t¥’‚KMÁÃ`á &¾É=‡è¤¨EÒK[Åõ ¹¢tõoÍż㑑 ÞÁFMPgù¨Èsä‰HÀ¼°|E™ñUÒ(‡º”•ØÆØ{a%^ òÏuh-ª†U$¿Y£W!>øÍ麜û(·u9^²9/÷€QÑ¢Aûû)!uE[6-.€ñ–¬}™%Y\Œ%ÛоÈWš:¸ËÇAóZEY]7]ÑžY‰Ú•ä ºæ–°`R0
+qõ¸¯úfµ~퉘vÂlÏÊy›†@¡–‡56ç;ŒÛ˜ºÙªW¤nd󣸨í’9 œn»o%ïͦ"a3'ò^ØÊ áêé‰òØmº{ë"5lF¶sPaÒZb¶q/<Ö“5©*«$qwÄñó¶ ±QšŠ)ŸÃ"¬ qŠ~>ȃË6
+¬*)’Fø(zE™JB%œª~ÍYÒn`¡ëÆÿA:G Îrhf#õÓ?iì—]…g^`"®ƒ¯È ¤µdçd¬&ä[Œÿ½Y};ó‡¤Uª–¢xXsÖcÁƒõæCrà­™e<ú–çYè¤Â ,+ ð¢Ä–ÀßëE~¶-kD²
+!móî(£E'íX^:f°rKÒKK¨Ø‰1òt‹rÅ2A™›ý½è†EžŒ÷´…²áAÈ”ÁHsÆWÍrZ(wS¹ràèQóX‹q…4‚
+êC ´‰2¥
+üɘÓ+YÈÀLÉä#s}8GÍÂSãNâÃhCÑòÐæœÏúï(3%8k‰ÇºiÐõ¬1·pß‹ž©Ú´©2bak g»5q¨™©¦ä"þrš¸[Av×
+ÿOÀ „Ì®¥ä·Ža{r”ÀÓâvHv4?RD„ÙŠëÃ9(õ°ËÖºP_jê¿`×@èÝ¡.C
+-!˜¹POf,àšà¾9çÓïÐiáíœû ®Ðè=:À¸B]×@è¢ô¿‡òW Œ yµag„NÑ5r%²!©w/R’
+Ü€[Ñé~ ŒZÞð½Å} „†9!~72Ö$çÈÆMšý ¸…ƒ‡ràoõáœ)ÓÃŒl¸@_á½ÄÉza(y3Š›ÒC ¼9YO×188Ö\ësQ–=
+…=B}}N!t›jñ¡z˜ÄÁ¼´ëRð}÷D¨™F…\èäÜt9`L…½¨•é^ Àè±1Ôr²çŒ—InÄ0 ¿’Ú—s®ó”øÿ×);ˆM
+“ã âb²Ùýt„2¨\!. IÑs„θ?¡»6OGø~·ÔFñ6•¿8
+%þâÛarûPg(bYã%#˜?ül[Î2-:É^UPTU ÙRÎP¦9RÜmlÄŠd¨¦M؈‘Ü|§d²ßÛ¼c§Æ$c§ÏÔäMqBA£8kóÌ&À•ðè;Äí3}]['<
+š ‰A¡n²âšå å#Rœ>ˆ—àJu‡é÷ `M¹1‹î›F…þ)643çÜ•MJ¼„Ÿ¥"†ŠüF½ïÀë’õ¡ š¹Äð{Y-µªþó#À
+H‰Œ—AŽd7DOPwÈ LB)‰\·—¾E³ê¾ÿv^èë{P™*¸à¦ÝŽü’Ȉ`p xüǦ=kµZZ¯mäxÔâÏÙ£d´:í‚xËÙG›eéÏÚæhÞˬ3/ÈnÓ½ôäóC –VùV+íEfïÙ|Zñõ^Í8=mØÄŸ¥ÎÚëáÝdFä˜År¦ :j<­÷&öãwÆ“Ïœ¥Yæ~ø(çzò¿ ™Õ昭–9i^³ÏîÌã×ÇdÇ÷9£Äxü>‚ê3[_Ì´X‡õh4ª”d”Q(‡yŽº ÞjuÚ“Q÷Wl–ððäĺŸÞ†•œ£zÔvj ~t±­Ïç&´†Æ…(”sм=®"O>z3¿úð¹úÙ  —Š}ó×–¿^æ9‡7½3ð­6•šsøMã·¿!^útô†á.=«O¯Íü÷£<þZHîÜÊHÇŸûsÕçtja§ÌK\uÔ@m¿ŸšA”ÎÅ«-Èè³uƃÖëË3{Ò Yï^P!/^s¶Ý/Ì Vð-èø¥Q«ü1Ð wAÜQÄ’R±cÛ_ ›`fÒ']O;~º—Í!jËqßÆú(5ÑãÑ‹ë”h?Ê}¢À>"×IóY{ ޵ÛÎûLü×E‚X6c@•¬»ÂÔSWémô R'²ï4Ñ®F5îfüÊèÔñó¥ï¿O{¶9§ùT™Û:©N”E³bĸ “Z¡g¯¾µ‰ ©8TgvÂ13KÄ®ð¨=Ôß ãÈyR .¼g®ï4d³#” Â*÷À”û7FùriÒVJg£ø ´¨E‹¯óÌz߆
+•fCiPé{ûm·Yº5 šmŠJWŒ”Ú-öŒ˜mâY¶ZoÓÚ¦ Äjˆåó4'Ùa«q¬”ô}Ò¿hS2¦žO*˜.æ´[")“§clüY è'©ÀíʵÎÉ/Üz¨>&¡A¾3ÚöJ$o¤(5Ds…‰Éj=@>×IЋS¨ëhÇÏ4RMö˜ >$ð¹ z“‰uý|‡F7”i±ýDÓK&Wvü÷ÁAœ@‡[üTxjˆÏÓò µ. Ñ¾½Þ ?€ƒÎ ¢
+sI0øRc}‹¢9uÚ*uÓ0Áõ¡k;ºÝ€‰Ý*äÅ7.GW}hj%3w~ói°£Àú†ŒÁýÌø©?Nãé…’zø¿}Ò¿Ðp±u ^‹.†5'1fØŸ#¨>ƒjsû6ŠLSE3<R:ËMj¤Ëp…|ãB`-UUê~#L´#01ð¯aY8›wUß7«CÕœ‡Øú®|´ÖVîA¾#=ò<“×
+€zE¥äóC“|Ð  ÿ5^¾C`Eñ¼
+“ï—9ÑùåQgγ"Ø•®á‚¬>ãI,†Òrc¿Øú#ЛÕë¸F`K&¦e?ÏæZu*"™‡Ð{É0ðh(Î\é‡Ì ‚|!­ÂQòàâ@¨áħËi`\mw2‹œŒéœyaÆè^ÙcèûýôÔ* Ù[\®ªÔÞ³oHÂRrZÑ3×Q¸™‰ B Û7 Þ¨‹ËðB¡$ŸöŒWòj,½¥?Ϊiro­˜ªðçO¤%&±ÓNv6î½ÆÓ ª" • C@‡–vú§ñï½ç7ä8€pzB€²#\Ø ·ã~R Ã5 E‚}¢ÐD¿Ä`þ‡lläðÂX¯Aüƒrʘ1ñ¦üK ³­Cƒ” 힟:ˆØFµYdÈ4û3)SýöI}-xùLß6Æ¢Ãu«¨‚Œ„Ù”bÙ8@ÖQ¸h‡Bìyå’÷ï@-|H:ÿ¹±®f"+zœÄû±×õXÎq
+²`ëà?ôõ,Ä”#Pæ;k~ñ:ŠSÊrCRÒqáY/g¢ðnݽ£½žŒBÚ¡ÌeCœ)ÁúÃt@¸É¾S•„Gpi\~ßÕÁ¾  uÐjJ«úíÎŒHõC)}f*ö ³ll¹›]£Þ8‡Ÿ!_(úëãz';‡‹¯„czz§Ÿ€Þx8î' WS8jëtÛÄápqžÍ8´¬Ç1?ª±túQ*h±–MZ'$l8˜ ˆk€(8&âÛŠ§m…ª™0 Á¿7Ö:q'¾ ÉI,F90…œ^;ÒÏcÀc92W’Ÿ¼s¾Ã衼¤äh÷.öú™”.»42ê~7f£t öÔ üsf?Šúƒdr?0"Òô%vý>‚*{X_L™™î³¶"¸„‡ìm±\Q
+njð÷Mùö”ÊN n€:2X>7ÿæ3ÌéT\‡c‘÷eP­¦%¾ u‘ãµ÷àÝì{ûMîLT´Âo>¥›®‡ÖÄ™ÊØ# £wjCE´à]€¡y¸Žª«ô¦A('b/ Zä÷î;¸Es¶™€Wqéø
+9ƒßµá¡u¬\b€ÕÊS´±ì{Dò¹&ÍQuaŠ|Ÿ?‘¦T“1½0N8ì?Ál„êÔqÚ™ÀP¨H¨Ê£íEÀ8rÛáÜöþ¢cæ$Å`‡á§µêó#òirK¾
+wâ‚ë ÒWrõ Ðõk¨­ØrI&4ÊÙæL?ùõq½„пªyKi£Ü¹ï Ž£È\›9µ
+xÄ Uì˜b€PJ¤S,Ða
+J1%ÒW±Í‚•ÛHÉÕxŽR° P`¡UÍbñÛC+Cv²”~ìcMÀ­CßT<
++—8_ ë$Cs¬SVïÿc¼Ìq3IŽ(|ÞöÜSh™íêylS÷×÷"£øÿŠÂPf@ÎcVf,o‰Ïa¤h §y'H?CËÀßAà
+")*4»1¬Ã0%ÚU/㥼©°)®Êð÷l#@‡D_¢uxvØÊ×FÈŒ+þñC½KæX@©.Efud'"fgÖö$ B «Ö+Dª48¼È÷sPr¯à[(éuÜõê3I"µ¥ÌÍ sXöC&¢²Fe‰³Qicp`ÊɈHé½ rq s™u¾‚3+uDz]Þ–±-Š:¼!† ”¥‹ñ7æɌɷ—€¾@pE­zxë¶yš<'•o—¯ªž‹ä´¿ì)ÿY[¾jL¸™©`Ç2{I„r,°Èx¶ãdý*G!+êæ-d-ÅT ò_Ñ9 J.pRÒë2<å¤ÍÙvSÆ‹gÉ•{aØ\C°»“‰bœœ\–/õÔz“K‘0Û{&ý¼d«ha(GYŽiÖC‹¾ wŒ$nåÂ-ëî-ù‡r _q¦7é¨=íKd‹6±.-p‡‘iÄB 4£?‚ŒñþÛž´É1üŸ¦õêÊ=Š#[jQX
+d&«Óh¤å?Ð!M×aüœÒqŒ’ ™ÛžGÖæ:2‚1$XbAøk´V¶Y<¬ ¡¼cµó)zï$–w;h+HÀêè˜Îh©¶ò›ËeJõ„‡\D QãùF0Íó¥
+ÆîľÄÂ7ènÐÁöp¬J)2ó¸÷ˆ/£àUª(¦zà åcJ-9® (o1ŽýUXá
+¦¸°ñ£aŒÐÚp`耳çâ¥M×Öñ*Lé¥ÉÕÖ®ˆS3îá›q¼ @·è}î ôõ;ýâêýç#}þë¿ýïvù!g‚ÈIa5b@F§™sBà~?€†A  ´¤5ƒÐr¥oQ¥AURø3²øLìÿÀ^K8 BjÄl’
+Ë6iËLHãÚ`zçŒüôWxCˆ•GÈzœOÝ;~;ç>6·ÛÜÇïöª`ŠßŠ­Âk…ÿq£DXw·O¢âÏíÊd3Ü> ¯YÏâ5î%f<CiæWöy%Ø¢dHÙ¥14iSåd5Œ0M\/'1±ä"`%ƒ£ÁkRWÈÃn´(™Òß4~³ÒƳB…÷&H–äòñ‚tx<-|¡*)ˆzÓ[ž€šñ8îÎ_dœt³2Ë
+mJog#<0hÙ€óÖuÁM!o˜ÀåB¨²k+ ¡0„*É2Ô;â|ˆPºvñëåát'¡‚ÅôûŠ?ù +d«WñˆV¬L8«锊¢@4É!ë6¼ÿ²6ð1¬wÅM–“C*ŠZH¼Ï—ð{¨×®É§ð $HSÅ·YR?gáÃY#Ô²žKãOùÁLmÞ@ªd²¯Fð‚àéÇL¹û¥ÔH:4Äž|$k€×(vN••;„ïîL®m@AÙfÏè›Êc“<èWZ×úqc”o¡ØÃÊ£ @×äÎË1ÖÁ(3E¿ÜÄ•`¾¾³\¿l•Ý:c(85BS[…(°Vºùoaø
+‘†:¨Hz9ˆ
+3²ÆhÙüŒ–ŠNM¼5²¨æg=ÍÓµþÆËI²Ûˆ¢+è=”M£ÿ©™ãj Yì1µ›
+v‹ÅZøÂl%øÔŽ˜D‘ˆUt-ãm³œaY±-=¸!ñVÅLúí˜`Nƒþìh
+(* Ê£q¦Ó-ùŽÐ^õ;N ql³¿[ä­ßísï](Á]­kÑ¢¸‹L 7
+û>E–]™sÚÇ@ ‡
+]QÂÂTï’ŠÜÛªŽ´…Ëe-¹nƒZÎÐq|¼”só´sä0ŽTµ‡EBÀÙ$¸°µ%˜"^£ç5Ï{ÑÅD}¾(
+š¾î¯ïÊX:úIa ¤>ïéXC@“ Lñ~ú¥ƒÌÈ«ù猇3†»M½Ó³»ÀB–¬7߯¥)eìé8†Ù$¿Ò<'yIøßTÛׂMÁl÷ý)Š9J9嶥¿JŸc!
+rî+žò¨)Ñ­RÞæEu‚MH±ó5ç}‡/EŸkgXX‚…Ûij”YúËÞ¬‡“̸M1;Î#& ÇÐÊWQ¶†5‡âåÙHr·c”ʸ
+Ý‹›7ïËéäÖÄ›­0QÍÏ­
+ÉSÇãÐcå¬@Út.5˪]Š~¾(*üòÇe¢íºŠjÁ:ádçâ'¨`
+6äM ª9{b{½#ÚÙÀdÖ°E÷œ¢oƒ”<»6œ¡^¦‚ö¦P­„_€~(1[¯R$D¹ÉEóB/D
+»ÒùMór_ÈxWÚ9 Ή<µ—#Éêb¤j™•~Â͈©#:þêLJW!³E2
+4”- ‹uïE4(ˆq+\”ÖVT2U–«¹½[¤\q£t8”¹ä½=AR–EOõuÍõJ`!ãX «\lÝy?h ©iJýd*‰ÛÖó» Àsw“f!w›´IÙ+Y
+¨•ñ¨X_œ“²
+`‰ýð7^{J$GJ;Àôî7ÁUXð¾¿ôÏ üiP-OEH ·SMQ #PPID’¥_/j ƒ® ¼TÂì*>"õ°'X/êƒJ*›‹Õ
+žØàØ2{0É)Ó±Z¿^)£q<Í13O‰…Ý6¥ÌÛï‹Ô3­ÂY Ê”CF ÓÜMˆAŠOGë b„„A®]ÓÛ. pª ¨kd¿‰®ØÑÆ·û ,L$´aÈ(ÐØx/Xß!(¥*Kבø~}È1¼Öf¸!ÃÉ)E~®ek°byÌ£Ié½&É34ÜV€QÏñYìnO'žhÚ$¡² °‹ß+û¬.ée R&ì·G_*Ö«¯­sŽ¹à"8Þ ™~™—”5/²¼‚ŸÕ
+Ù€,yqVܯó³€äu»WB&¾ ¦Øvø·ÜÆ;X
+Ê$#¤´ÏËÐ÷Ä„P6ÜïEQYhˆºB±{|¸‚¶zŒV ­LÍ9¯×‘6n0FÇ°¬«cÝ&é’©…“ྺÖe‚§ÌstFÄÙ'µ`Œ:^9ÞÙ‹Rá(Ë¥ßÏIO¼5šˆ†¥Ïeh›_ñ<
+ˆ&9dø‡8%ÉØ2¾Ý‘±)¨…p7Wæ|«ÈãÍôÌÅ¢N°_뀞ϞúÞw>:$ÔöÚÔ-Aö½ò#‚HV°·¾Ö÷YPÕ "Û8®%kðäM¸¤¢æ­»ç€:È8Z@²oƒ2¢ÊysaL; ‚¤Z«¤BZ¹1EüÃþT¤yLjNqõâ]ÚŒ™´­O]ŸX‹lb8‘õ%ò®œvìOýf½´‰üçƒ+1žRüdpáóGÔ‚ò¿>§ …Až’YíÐ{A„¶µU¶J!VÁÕÒõè§STœ‚¶8®¢ëçnEŸïÜi‘Í¿þûñÇÿ>Ä”Í^†ĺþºˆþ„“áÿÿúˆ[°B¢‚Q V“Œ
+G£
+Ýüç/·ÏRø;q̧åSºåtš1Ê{E@‹-Â{õ,UxŸ{³ˆ¼#ʤaEã™; 57w¾]tiÁç;}2g“Šô”צ”Ú*#FÉB&e:NW;ÿOy¹d·ë@t½‡¬@‡_gÚ»ÈÔoÿÓw‹
+џ룀ˆëZ6žs›Jâaa¾¶é^ d…mÑðµE  ­ªö}rIùwWWc•Í±3äÆë«#Ó¡ T ‘à÷Aªê¹¯ŠÒè©*AØ
+é,i‰ 18;éÂFȳø6
+Ü8ÁÚg_ðšY5›;‰²WX‹Fò{»Î• ]®
+î3BåÚù Ø­;„_L8qôZ’dgùO*v"¾Á›xê~TC93w%¨ú1ê üCJÛÕ÷•ûŽßõGeY#Æ»§ðÖ’åŠ/F¨,ÃÁ+ ¶î ÌÒd¥dYÙÏÁQur®‹™Œ^ HÅzÕÍa€5¬`êgcàËù¦É—žgЇƒèˆ°–|2- I9ö>å9x>Œnú¼°¶$˜
+˜D’Ú0Ó¦ÆuÈ (ÎCâ#Á‹©0±!ÓKv6„Õä«%Ρ‘X, ‡6œP´´-v'º²F ŠgiksorI$ð,³ÁqQN1=B'Í}–õ«ºRäxMzF[å¤-6„#ƒV²¥±Ñ8Éy¨ %z µBøcÐÅe>Ù6›U‚Qb^d¦¸¬QTG˜fP„}Æ…3hYÑÿ©%ÆÊ>4JÅx)™i{žås™Y+z¿Ü¹K³1ÌrñÕƒü1Š¬tù”é[î¬" e*°o6ä × ·':¦”¤iÒ}´Sp:û)µ†Â×Û¦±†¶6B{‘ƒ49´H0?tFœ%ãT‘¤E±åìt®B6”
+ÐÇe<ÔR…h}Ú3NÏQ}Fœ…w 'cü5á3®L±w*¨BÙê%DÑVíˆk«|ódµ¼9åIæ…X°×So^Ì31ÚÀV³0䓘/\á</šä.¡Ï÷9L¬G,/ \„q£ñì€^s÷ï6i»©L h5±õsëogýï
+2=6Eûñ#»×o „¾4&‰ýOÜ´ÁHfò- ÈÀu˜~diÛm´Kl=}|íÉ¢$.W'luv”bbmBÊÙžÒfM®äçHéÐ)u¸º’QM †Ïp>I]ˆ[K ‚Hâ$i!K¸fÊ‹1Ȉ~Wz9‡qÍP
+e§UÅï$‡õ†ÐZÆÊ8/0}r,6«ïÜÈ|2†|±w›ƒ,NŠ)<wº@<RC¨²„a®òæ:'75
+¯ŠS
+¹ŽÚGI¯Ù³DõquÅVã«7W
+Q ~hŠ&þý× ô4ž2‰LÈ’ubòú9ˆ<“`耿àöw ÙyÞN•Ä¤šÆ•ÎÜìC4y=DO{ÁŸhéC¦M¼í˜^%ÂZ$KûåR e‡;„è[ÈßA¦”ÛÔáã3Ô3ÀÀæA ß…b)™&™YÛk^!Q<¾Pô@š“3X.½%wî¹aÈ4½ü•#7KŸ!jÓð
+ùºüÞ‘D‚\¸»{Âx1qÐâÐzò.$jâ[ĦÞ/Ìë’*¹ºå·ï¾€žó÷
+y©Â+äRÍ ói¬
+™Çø9(9$E;&Eò‰Kqhˆ!7™¬hQÅZÖòè’› ýÍ)U
+–¥NlåÇ K
+^ÃýdÊöYmËÅ?Þ€ odúÅ–þzñu$pÈx“úÊH×¹ûV Ý«7ØñÞL& Šûy®,Ú„»óæ¥1×4
+[gæ¤Ú§[!4|™¥¡øñhZ7#”6Z (ÄCq3*tRkP µð£DgsÛÕ:f˜@7Çì‘:zcì:M€û dÂÕíS l\¤j7Ñ›š{%{úd¡Ñ$-õ Ä#-„‘|¶–{z:gÂTÜBÄ
+„¦ÓÕµÛú#a3|EÍ"«q[¢Áü/~÷i"žV1A´Ö@˜iñ}~ÑëÙô5Êuî[H
+ c|0Ý$ý‰­=¦ˆÄQ‚u,_Dqü“m§¾ÕéÏv½s5=ì‚*Q4í-v"ý³Ãp#RiÀ1&$I«œ^ÿê>eDÊC¶A,™ùçfQ°ï‘½9R8G 0äJ÷6¤WÍf™pÞÙؾÒÛŸŠFâà‰QÐÙ†8§k‹ÛçdU†Â³Æψ0fx3ë’`<¿<ýºl
+
+•±ý ÙF ¤·—õÈq5™ÙC¶ØF†!J²Ž‚0¿™§iH¢¢dxè’Ü$B‰Ùqï¬
+vü%ÉÂÒ#E£ìs ÿ6†`–á
+‡†³ð`œß@
+Îú@ÝC_Aý!_•2Ìüëv¶&Tœºá`òõMñG«|^ˆJZ¥0ÊáN÷!æDr÷9ž5·Ö’&³F“Â÷Ã+U}~ë cJN䟬ȟ¨‹C
+!Çeó>uä>†«jGé9æª=¸: Õâ¼ý轚`&&#üæT C‘Ìz„B2%i… †}×Ë·¶ç®BÕÿS^/É•ÛJ@W =h’ bÜzžjÿãw
+ÿ-%ìWöí1œ… ZD9
+âŸ×™3íîð›Gf“÷%‘sï<ªuGMfÚ®»â÷ؽ_L‹`¶$¹ÜéL¦ÓÕ²vß»È5`”‡0DóÒö*ùhR/ÁÍ(ùˆš-¶S¬‘)unE<hÕ‘;ݱêw’L‰+eœ›n½£¿‘î°o*Ìè–þ­Ÿúñ%0˜¿„Ý·e‚8¸i±6h©¯suËÊêÌŸŽwÈ MýP‚Âñ[†¤;ÒÇ"~nñ€ëýŸ.¿GÌP
+Û¸Öךjôéˆ;Ñ›ýIÏøŒðnìyé0^Öæûèæ½È{ÜaŒà< A•Û(2̤TS“çú8pš8ñßÔGL¼1<0GÜ Z‹©6Z×íù+¸±<èbº©`ÔeŒ? ö|¡ýÖ‡ȉ Cg›[a¢“ÏvCu1%qâœW‚‹ CͯŽ‰Y°§ãñ¥s+ HÜ(ú‹‰Ü6Þw –úÝ­g4ñ㹪p”ý{ŘLHóF\˜§x\å`%jlz»÷I°S+LIGùº%ñ\SéJh>ù¹kì ³£–¯ÓÑA‹>d¶nƒWb=Z)Ÿ1ïñ3ø¼ž$ðU€†ë)Ñp°ž½ãKf
+?¶žª±¡°C¯×yæL"]àÛÚ^Y‰3A½ó0îR6Þl>*0›|Æxÿ[Ñ„´ƒ˜k&"-ÕŠ#ùgtÃ<àï˜Ú©ß(SPľµLÒ¢6ô[®–"‡`¿ÉO§Ž„™roåÀXÌä>Žrò¹Çœ–$L¸‡ÃjnãÖ ƒ¹]Ë×Tõôà/©*À⼬q67{2ZàÅbÛâäS™OOÏ!¬’K奰S±lE[Ð^x£+Äs®5êBËù}ÙîãOÎôÏ8ú·µ®ŽÖM€qô‡"YUFt¤5%±Cg™¨¯³$™jMŒƒ€”„nA#=§íéUÒ•w#-`'±7×ôµ„ðÇM¾ •ÈÁSÑQ"â´˜G¤×ã³+hIãRÑú«ñæfkx@2gH×®Ëë5‹H¥Ñµ„þ£•«…™ ›Ÿ£„z¸#ÒÞ»¢À\
+V 8¬åÑœ˜«Ä˜×k >GÑÅì})SŽ¢´RðE{ûjí/ Õrðv{uš-Îyoak--ë[mü¿ç% ½=0ýÒœ~j³e²z¹¿wXE¡Dv ›¶|(E-Áh£¶zp%uˆÕøþ5*ÎOœ³2%+쩱jÀ[zÆ|ᔤM.Ézœ«AX—|æýU W]s@®a¬Â ‚°÷Þ)š^Ùˆ:/»+ᇠãÓ¶w1î¿é|{v¢jBÔ`Ï)e‚¯/ž{¹®±S*Õ†ôïûþ3ûñu¼‰lÉ7uª<g˜+Øî–^ž­Ì¶£•Ôõã)ê'YÀâcÜ·ÉnçS"ÝXªu#V–Øc{ðÛnÖú|°÷ Ã2ØéWœñXgÐ(Vuû×JºS‡y=Å}õ€ÇYT<þ¦Ýæ2"$·®—´FX¡çXe75Y4R_Îõ° oC`€òõÇŸ[SDÈ=ÚvXü½õçc>ÙЈ6_}ùPaÐ>ò"—z/<4ܱ,L–—ù'lÂæà
+ûÍ:“¦c4F’4Ê|ìê—Ö[ê€; óŠÝ#¿ÇCnßøØSžEÜÒ&©Ç‹f öÊœ<âP=7/Ê;Üó< ;z—Û‡5ëXn÷0³~¡˜gðÁ1tèêíN‚qšk1
+c«
+Rü=Skéé¹.ú•‡È¬3žÛÄ;sÙÆ-âª÷
+ì¢Ö=/=Bç·Gh|^¯7¹¶Òc0Û³*äÒ|\ö§î€ÂË@·’{7™5£ÆmÍ’º UžªÄ2Ó$ÄÏfß+ltüG=Ø€¿«`ʈRcÓ¦=b”Óu¤¢D3I<Žßæ>h¹FOÜó~6‚G"ÉåD;g²] "%g|Âé[­³y²HþWA+%3»½žÕK^{4œ9ʳUõÎ=Ps¹yM`ÉœZ/²Ò·l„qÊÈ0 ´÷%nˆª(Ù|'Ðcg¸Ï]gûó´‹Ç¸÷¹¤10Ô¿g°Z¦qOO›hò`φf]òg˜yvúZæé;#‡}Ÿe¾¸/À$êiQsÅ+]žŽ,¦NGµxïó©d<*í™BD¿ÎÜ1³'7?’'Ëx¿Zeã™36%àž$¡l_T ˜k5 øº\¦dÜws€K·7Wé7Õñä‘ñL”€bjÏAÛ á|f× r´!·ÞV—RÛ"À|£×Ÿ.4vñΦ¡ë@„m€Ïy-¾Í ±¿¯Ë¯cÕ¹x",tïKæ‹õ¼‚èX•óÙéÅ™ÃéþÒ‚†5’çb«R~(ùDçÿÄüÛÃü-ûoc(JòŸ§¨'½rç+lnGƒAÏ’ÙÒËá&êþâ3ÕBîû‡’O¾u4ÀC! ïÅ•Ïó
+CÁzHydæ®?•0 ¹¾áJK»×XíªŸŠ~¢¡!\ô~öéd˜¼3èöíÏ5aÇäÞn‘YÓ«ca]
+l²·£Qá°9¤{$š·õ›évàŠ´(m×
+Ž¸.%Wxðgî5yl‰*>ù‘Ï`ó÷±²’ éünI‘¿S°*Å ëÒ’òÅX#ˆöûµ¤néhüv%zÖãiÎ;—=T¬¿¥ŽQ8]ܺ›ñÞV¶_7s¡.Àk¯‡Æ¾pÑ*G‰Ííí¿Çû
+£¦2À¸¹¨EæƒaôñAð»%Þ–í±éJ>0fFž‘BBìl7Àã)[i
+¯¥ÀÏõ:‘>|%ýY’a‘8[y¬Ò¦Àh{™ëh³„Î]a,ºù!lÌ)|Ü
+âpiy^ZŒéG”`v‡»I,`Ÿe™žÎ&"¹»®û‡?C‚¼ûl7ßÁXº$Ó°dQ£ð=/ xJø½Ë[щ”lºÓHyçaô<«°"}Æ¥õh ß]g cF Ùñø\êÑ š/^!Ù'€\IÔç€òÏ¥`~®ùýSÍþ_ÊëäÆŽ °ò¡,r%™gÍQ^40'ÿ×ù‚ɺ^ñ¡Õ€h‰J’ÿ ܾέ’ÛX²²EØÞA¢ðž>úm<dŸ"û<£nô
+ÁO e»µ”G«bxñé0/äa\Zb•OÙâ¾äSgóv"k4‡HP±3õz ø»ÿëO\úO†¾*<sÖ ¡pRÂÿMAáÅšzøÞVçå;×{·üÛú/@½FÅŠ%þÌqW¸ìÌŽ­åøcPC}G ˜ÁóÍû#ÐK ~ýIRÎU>ª‹(z¹-c·*ú0ÔÛ™”I+K*ºèùJs«Ï¬çöIJ璄œÒ>öþjöˆËb @¶xiûýþ›¶Ñ's
+âÇ·ºÞEÞéßܾb~½ÁT­‹±Òc´³Ñ’»<ósêp¡¦iq¹!B×%•vû3Îv°§ÆóìÏî£J/¥uTgOÊ1ƒòëšï¤" G
+Mé¯r”ñ:Çw¬¼
+½ŠýÂlèK¬c€Ê]äϘ_o0]-øÅ9¦
+\´¯%´y.[F»T³lI7“ì{f-~ð¸§8ui3Û2¾C:kËæ{¨Ë‰»ÍÐ1X‹zñOjÙºÊl»‚£ŽøA³†_¡‹a¨úŒ–XÛa¬äyu›åû6h&3µçÓud L¤Ù¬ý
+ÝÓA¼4â*² 5u¿OBÊà&«ml½¹-Áøt“è¢ø²Å~_Co„3oRt‘uÝ¿"†þ*”Å'Ç6ù
+¥×Y~"c½ Q™x½#Ôp[ŠÔÎAë# {8ØtÒõv/fFw/aËGZâÞ Flûy{ îC9û±àé
+DIóó<Ê–(ÙŠ)ûwÂt&¥u72Š³&L&¬aL1 x´ ¼æf È쨮´rÍâˆ)åûÖÌå>ÉTAPïÚû5¤•:qù¹û¤%'d”ë6Ö
+ÖjëQzZü[kæx†y™âŸßJìNô2}íAº ;³å¿Þ64ï™W¤~1õéUrÅö²DøÌS¶4œ€bzÍ×\량Æ­GË
+WÞAÖ5Îœ¡ç®~䨋„Ù¯ÌÇô«Ï™<BTeŸ¾ 'oVöÜÎÙ•§×§OAŸK8‡|jÄòÚP ˆ^ÂÚ¹fƒ.i2nh@**ibƒ)Ä”ÄPø:ŽrÿÐÝÎa=TÊ6ªʘ‚vçòÌ3??‡|¢É{ÕE¦D"_ßêÔºÆZP“¾÷˜ŸUµo*=G1ÚqF‘ùe*íÝÄ;¼ŽVݯš:ŠCËkü^ˆù>¼ÌÈÅÉÄ!S«MÂY-,7ÜG’ùòºÀ
+²ñÝ ÷Í¿£ %-Wó'|ÒWØÔá5nÕ)ôqí]|šõ~T%í¨ªdÎê$ ‚pËçË&·I äª, ÑU á IÏw;×Ò¢Î*Ù¢q~ÜJ4ʧ?@ð®¹Æ¯4¶<DÉòŸü»í› –bÁã± LiÛ”TWÏû(…"{bå±>Sª–.ÝËÖ|‡Ñ½6nP1ÚàdóÆR¾8Ì9¤Æ˜™ƒ¹fJ' ­™;¦/‚ôßoëǾ•ê!M²9ï!âÂIªñŒK›b²©eÏ`Gr@Dû}ÄŒ»ûIÕIJ ògÒïÅë£ê¦9òsòWNòÿÔ7m^Êð3'Ò¯5ú’x˜4%ŸŽZ3. vlôÙ!ü¾•ÝqÄþ¬û«ÕÊÑK|Ó¶?ýäŠvŒà¼Ø² ûpmõ¨§|”Á¶Ò2Œq>Ôcݸ_è¼¼}·×|=âÛÊ8¸û!?Ö¿R‹íZq'¾Z'r RÁZ¾Êãw^]8ªÌÐA×÷ã1_¯ß±áß5|ú˜WË…»³ý6ÖJ‹²ëíyvn[käæÞ-Ñç¸8Jœf¿ÇÃOæ²?5’|®äݺ'-üvh!>iñ_ÈçÑëM0EÏDÏÚöÙ]#ßÒ<2”0}b<UiÔz÷ò}©=Š™ì%4“
+—äL¤c÷Ê$9‚°ýöD¯YÓc.Óñ-¾/:צ 4cc@,YDs¬ù‘ë¦Ëu_'ánÍÝ÷­5U©Äòº§\Ü!X5È6NŠ×g ’8¯¾-k´›ð©ú8j³ï‘óµ„'÷>#È5”÷¸à×úíÉ–§ÙÛb¡ÈP©Û­Ùzk•áÂJ‚ã~Ni¢t$楊÷Q_ØÆTXï=ëxl}w'œÅÕm³Îå äïó—»„$ûR
+ÈÀl×”$§˜SÌI–Iôü
+å6D´ Eùlp±¤>³Ó}në9•¯lƒì¶]±•«y—¯2rˆ–o^úmi
+I?#8ÏJ,Wo#xSã7_žý}ƒ•ì¡ùÉ8c…ƒeÉñ/j’¸xR®Žk_1gߥûÇ>eÓ¿zφ êP“¶åI÷ù˜.–4ôvg_Gó¶€à Çxkíq±[xèßa¹4Êù”ò‘M7¬D<‡9ù‰æò©>-é…i1¿mÊfcYI2¸¯òøÖÍ›"*Ëw.¼:{Üt¿86Rq„»÷Ç–¬0¿në0½¥ìcËsc¼RêÊõ³Üt?T³Nu LÊÙßm™ÀûÖ:sï÷“‰~¿Íþ•>‰~®}\ëGí_<`»ísIäYõcÝ[Oœíºw®:¼Ëv÷âÚÞ֓ í6G?WGÄ•ŽÇÖÕÚ´
+T¤¬ß_ÂÉèf eáþàé¸þa8E¿þPô0:ö°~!2Öâa¥½×…žÐT¬ö9§ú£¢=&Šo-¡¨š"¿¶ÅC]œbÝÿMQ-çaɹ3òkÙÌ·¢¿Òñ±nÕw«„$Ϫ…&ý7E5ìq„±ü¢H‰{{„õ2ž§DÎÑ[ÆŠz
+³Ü²‘—>ÏöEñèánxt–ñ·ëOÿâö͉uîG¾C¯(™}/…O°‡b!ñR‚&Ž,>=¦
+^6=üœ/Ù5”RÜǼ]°’^g=%ý”ÉÝûko.àaVDŸ~-ÜA6‹Û‹š¶NËÈùÅÔµ¬ûØЊƒ!3­‹’q’¡•›ËÍv¯¿³EC w™vn(ÉöÝwP‡^1
+ŠKRZ`úØ߈èÃ'˜–ÅÎ0w}ä)’!ú €åÎD.-íçLNîžgÄÀ´à`¨&òö8ÓòB¥*V¦Ðè93r?ý¬÷ Ít½Bu1Co…œû‹?³
+\’³‰€)ê>>f+Kßõ¾½Nj8yÇ4²ÍY™“÷®Íw ±Æû¥ûq-czçøãs¸Y–•Nù‰òÿÝ^Œ‹ª{¾ju@Ncî t¿6çÓ‹ý“§‹'oÞ?ÊC% jcaýš%RÜË+‹¢_Ë¢0L’0®3E@ e Šw>‚÷YäÊ,/i³®—µ=B~mPDKÑñµç1b,OPî}—}e¹ºûãšÏ,ûQÑ‚ÖE8ľi-œüõ‡+^–JÜË’3plj[§ (EtîÌBÂËÜëªÄˆ½>%, 7Úm"1ÊQî^‘[M d“8ºÚ¶{¢¬r²K²•×Qña(ª{íƒèÊ‚Þ Ü{GQ !8^}‹ãã"qÎ(±?º
+ë¢ß2Úß)2Z»J¾ŸcæsïÌ¢×áÞR5|ÝÃvå¼³^~–’D Úà‘íé±¼*°œÑ|ù–ðgšk6ÕM$dq4Ð@»­8§—I öð2¿5zÌ’`ä£CO8Õ~gYœá(3«#ÇIȶ< ÖÎ’‡øÎ|Ǩ‘¾wwÚ¹ çlw“Tv>tñV2vTø9a#ìûRmE<^á¶{Ä+KzçúÏîO +@sõRò#q*Ö%ã¨X}~‡vY@þ¾ÍÛ«jïf>»ÂdŸñ71´¯£âEB›àØ'ô% µ¤„];/»ã<—t<1iE„M /Ó3*=÷FLÙ˜Jß¾ÇA ‘ˆ  £Hæ3g d˜h{Ÿ{ãR2C9-à¸L¤ÝfüÅIyÎj·óuT¿üü.IeòŽ-´%½X–|§‡íÎb^n:m Fï(Ç(ñÕ{¸ o:þ¯2NjxU“·¾
+H‰Œ—Mr¤¹ DO ;è®
+?Û>ª·JíÒ£G=Gñ†ôV{4ÅiÙ¢sý–ÊÚ.ÛðV¨SÌ9„ox4!=¤Fta×I•˜nP„¤žëXáÍÝÚ˜£+ÌâÉ ^{”¬ Tr®w¤PaJ1.u”à(½»<Iþ‡[
+äU¡ôל¼ÚrÌÞúóìFú{æ½ë$@Æ…ªL?Ÿƒ3æ'k›µÆ´YNx‡ð*Ë0AFŽnj„?O¥¨ÇX\³ÜGh®Ø‰S›Mh
+ÁǺrçÐÞ‚ç< åî4féªØ‚Poo<kC”L>µVŸWñÿ4Î_qò—­AÆAõV+´_ë­´Ý1ÅÕ¹…¿ˆZÑÉGlV\A½4+ æ~Õ(… SlH&mM㯸&p¼`~A TÓ8µ".§!¾ 8^^ël=&([¬à,JnTЭãDç“nW~ƒ,òÿœˆMt·¼Ç©è .–» ,Ãy¹_ý[gPµ¤U‹s_îÕ3x}ž“(T§ i<µâ‘¾‘6Ìëêß->àh8ŸtrW©
+ÏCÔ¢¾~ª€}É>‘À(«‰ƒ@“™©`O†¹XBÜr.Ëâ3µ;$ƒ®£xFßž
+‚4Å
+HpcÆOún ¦z+çoâ¨åEÿ¤…Þ@º„f0ko¢þšÂM´ã¤©Q'‰=&¢Ÿéý¸j)´™Ò¢õ˜É ˆÄ¥0Cž7q~3åo ÿ=½yÕo ¯«æÒp/761d™Pëjë“eÄ&“‰¯»š[jÅ`ôj§ !¦¤z¸Ø‰ƒV4†îÐj´º’sFZz ˆ(jÁ˜ŸK±Ú‹ØtIõ d[¦oäg] úOÉÿ±CS¶{Ù—¦“*K ‹ý( 6©§µåd Ã%-'}øߪ •F£AÉ
+\H÷å©ô6 !ð?áÏ`ÅF,‘³À ¦DñZ†“’˜ø~Š`‹ä\eîÑ0¤ö\„Q¼!œç¡«éÌhû(òB‚¹;Í<öƒHfwµü¦fÛ°ýtÉb¤(z…|·¶ûb“ýÃà¼>o·‘wm‘9¯¯ÂöÃäæ¸ôI Úˆ7gxV+FEk
+y`Ö+õðœßO
+žZÙú™i­’‡Ë÷£Ìì?øÛ6lCèxÚkÛniýñNòFÝù ²G4‰“êˆ[ÍhÆ FVž8SÍÐK˜±f´ èpH‚ È\:ÊLiþf5Ы’YÉvâ±W×»ݱþ@˜©xt\k«&ýL f)Yat2,ûÉ26/ÖÆ6.ùÓ°UµJ='©U€h\G´ê]6³h<3ZF  È~®Tml `¶ ,bÐì!WW!ÅEŠ‹¤7-q‡ªÀ£û߇Ã7Ð×;y
+”‰j×!Ã}XeÂ*ß!´É¸¦}?ÞnJï•2E>/Gr8QvONã.ý Hƒ«SÆ1ºD—TŽ³#¹EN5Ø¢ž¶+∼Ý&èdüJtØG>Õ®êLµêx‘$t™¸ÇpÑKh§‡Z½‰ÖS³iÅ¡Œl'ˆ&Ô>Äâu‰Çg°C0ð¢8ËôKïRËI¦˜S´&ùõ¼€¾íM-‚l¸ÿa:&|Ïtp¶ˆ\©]sö’ApäM½‹œ@3í{ ºBè’"Ñç¥ý¥!
+$\·7ÅÒEÄ:¯þÒØwŒ(ÀãÏ«¿ƒ$@4?ÕoÜH‡‘n-|ŽFÌcEµj²&¤­°ËV…³£«Ï^'žR¹õl
+›ly ,ïX„ÀÄOD Âmf©Õf{–
+Ú˜Z%ÁLa+ª ǾŠêO³R@¬¶réíÎ>²û‰m”¹ÂT*übå‡Tzaˆöm×ø’30õ½~˜‡så6 ô¿·^¤…0pÈŽÄ/r‚xÊ' Ægk¶­Ÿ¨y"Ébí˜PÈöÄød0k9‹Ò7ȯ??ìó0<ì"lJmÛR $U2Þ?ÿºcäLJj¶c³¬¦‚ùLeÒ4À% &I‚dо˜²¶Sˆ3ÁQ² ¶¹J
+ˆV‘‰€9ã`K´<³ª¬“h(øEިР£•qƒ¦²ûE… ë ßv üA…&|û¤!¶q8¸G.höN×£®0üÕ-H?a4 Öp q§¨™S±í|³R€:Z‡= bE3GC
+î°bÑ'‚07ì"u÷•¾ %Èü<÷…Ž!“¼’õëãú¹XpyA.Þx¯? œJáF¹Ÿ>4÷yøÚdb½œQå¼båرm7jôci¹1sžÙ;©JîÊŸ:é1Øx^<$N”éÿy¥–z„“FUóTTöÉBÿÍxýäøQ
+¢©ƒuÛ’ð‰g,óüÎ؆œ_»n¹ŸŸ¿—ù*3e·¡ú¨7)Ajû•x°~¡7ÌI‰A ï&ÿ_©q˜7š˜m¡ÒŒêµ'–ýÿ]0¼Pý%6§óeµšA¼ÎYb\„ß]ƒŸÖ$5æ}ÿÊÛ!½&¬.ÑÄwu£¢—I\5¸ß’4Îvƒ•ÑÑõ £§„‘X{CµçFpŽ‘bßrTü^>bèÜñFâèŽHà®ä;u«dº|Ý0å¤óú¥Ä¯8ܯ٦9-z%?zæQþγ¼ËÔìнèH?*‚aq¼ú*ÓÇq2ñŒ8öìG!æÜ&1¬¶GAN‰U CÝ;,™bšŒðlc zSíWî¶`Õö®‰kçïm§6rìöîOžt0ü Þ꘭íÜæG²ü4ߦ–žÕ¸–Û›ºôíñý#åXâRrëzó ¶ÑˬMyÔG%¦¨í…!˜¬kbÉmŽ?n^ââŽÌÊœ¯G¡0)¼\óJºÕ5€º¦ÝG¹—Av’5HǾL)3?a!™Þ»ìØ›x›~B<µüàü–úàóž÷îƒ_üãq9+õ³b–&YjÌòگ⟜x½žŠRb âæç7 DÏÞOëÀl÷IZ9ÛY¼b6ó¾ÆÃãW:
+Jeë:íE·· Ä…ûQ‡%Åš5K3=îf·ôÕ%:oÙÏ*‘”m´Ø,ÖùXËW%’ÓôÜø\$Wêhˆ|ïÔör›=2Æ
+¯õñUŸÌÿ ì%®¹Å‹,³(NoMGIÓcð„û5¶¬v!«0HŒAŸzÁ$ rÏc”4Ìt¥‘öEZÐSܳŽ¢ór„¬ºú[wÆ<Û,a+2oï¼mr ²M#ÒÂiMkÄÄje™f‘eô ¿¾w̦Vñ”• E³Äég(šx>òIéù ^ë—/ø¤°¸.âI°Q9‚'gñ‹EŽ{Ž»Ÿ®¸k¯ès¶ŽÅùôø˜*Üp räx_úScÏlÉ&Âm™ÑF âœ\µ›<÷l›ÅÜYædcΔ°©¬¾³u{–à”ÈØìKGíÌÀµóyÍ“`”ª1¯€˜Ïd-r™*~ ÊX¶qâ‚%ýtöþˆ÷8`ͽ:Ñ,W{Ø›øhâZ“QÏ ædGΖ.K*½Â`8†üŽj"_w“æhgMÒÝy ê®äÇDPV‹?šAölÄ›s&U{ãöØ©2ïKË8“P2¡~›—ßð¬ö…·Eq¡eöºû”(5‡Ôö÷dø¡(%>hû(ç%{‚W’™x(é¶>%,g0åøâ;¶^E¼Ü·!‚¬4Áªê!ƒ°ïÿ~T&øŸË<Jú‚R.p}—Šp%(©¯Œ§Ö¡•YRÃAk½wùeœ”f³ô‰Ltåvè_îçÌ»v ¼Í2°£7gvíŒäOY3a_i±¼=‚ɤ¬ë–õ¼µÑ7Øæò>ŽbÔvìv$%Îû@ÿã¿o¥çÊ`çX“#׻䂿- Àøu$gÁ­¼Å?§Ñ±š ²±ue¾*Ô;-®dÔ_û¾–³ÞžC.%×Þ$ÚV¾(šô¦$DìPúŸo¿ýëÛòöû¿¿ýößoZ›´
+ikä\ìú÷xÈ(å³þ÷E¢Ä%M¤Ä(5‘ŠN [È"KBÐî"É´Wh!—
+iQ‡¿úI8˜.£6Š"ɸŒö]ã;Z„6“kç,a‡I몈ìåûÂÏZÆï^%9*â`Éãç}þ‹ï˜}=<*òõp›bs„¾_øåUEöª:
+$çz=5G…&'œçc‡ŸJ>LjžôR¤“‹`·ÁÕüÎËe¨$£Qñ¤°øü¨ëÔ}4ã=Žzmâ¥$Ûe}ëÛs‹ÁÈdâЯý‹Q}(¨xù§ï<çÓmž
+{¬š,šÿc\)ª”öŠUMÎ\XeÅ<‘³ñ¥†ÒÇÍî6ÅÔQâ_}“³m”ø*áYðRéþQ ¼x[ôvÍ¢}`u êìßÙLuÄŠ¥Ž’=Öô¸ˆÚ9Jˆï.%}(qÔÎ@Æ×D"zVzýŽ‘‘†Kœ• vÂÚ®½„Éll„ÏÆ™v¹7ŽþãÛS·Öv9‚žZ¹Œ"‡ ¹à€
+«vÀ>Ñõ-€©5B•ýqK•s="‰ vÙéQº&$Ôë³_zPÂCW7ß¿ƒ¡ãD…¸é‚¨Ÿ[þ¨¯’\¨4?=P_^ËP:¤à€Ëz’òåz(ÉQ1òäkK ]×/¾sÙA“"´ùn¼ qËðŽD,4ÐDµ2J¤½åÜó™ÚW㵆ò%x'š+†OEˆ™5Öc9ÆY‹]¯Ãð±v¤^(f %>¸ù=//sòBGâ”'ûãå,88ºl× •'Ê€û83 ËSÅ®cò†ž#hý¼Æ˹ˆ °7ÛCÅg3´Ã6¯gy*z˜øëe^óô¨Ï
+¶Îƒs@š^Åõn®ÖðkÐjrêBÐ r™!ƶ:Ì¿!tzgw
+ì“éêFmá—ûøIÞ©*mª%>äK‰ó±2—f³ŽÇ°Um4¤
+ÆjÆæ·!=52§¶gK.KúþëPtZwññ(;Õ xÿÜŠ.´pD×µhQ A·Ce‚
+€ZeÀ `e€U]qñT4!†«aÕLJŠiÈhÁï‘„Ük´áA˜QžË!Ž2¢Âî«øa_âËU>J i±•…*üú´s@.;›îœÃÑIÌ>´lòæŽÍùP²¾$ù%“Â$ãT„ ¿‰CÐKLú8^F%ÒÄÌ(EW˜Å-ùÔ•`¼ÑÌ÷l§K^&基u+A"§nÓ‘XižŒ€êúªÎŒ£v·wÿÔµ;ÜlH€s¬A\{Üß
+ ‘íÙigu)Y³ºŒüpÌuo—¹îßýQ÷5¦7Ø
+zUð;é÷ À‘™R“þPò2©÷ =¢RÇ8r>ø×(?êrt¨Õ¡èÏ©èÄ·Ï]‹>r'c<äí1Ø0w^šu²^xvðS×ÕOE]¹–¥â_„0ðD“ ü¢¡¸_ 
+OøïíT‚
+NlØK…ûS·¢†ï$@…–ue¦‚{'LfWS£TBaÌJè觷ž=œ1)ô4 ø§&üÚI¥3ì}Ϙwè`¦^màl@"}²6{¿äD*Ä €(:¦M-_ð}žq:e˜Áë
+:5o÷]p¹ì#ii•T™ƒh ØýED·bÓþ§–BìÂ?yóh?WœC<gçLº” …½¿¤“Q˜Ð:•(‘f¥Z\u0s)`Nq?^m†r¯ù<Õð©GÏYeïÆÂ7 µƒÊáå×°t)µ…ožŒZ±{~HíݨŒ¸ À8V-vwUé Ä=âšã(¢2Å°à̓g±†|åa—’…í¢ŒE===œ´Èz¨š˜|]¦(âÊÈŽ´¶ŠMÐ6ÒŸTŒ°ÚÆvG§¢%æ}_§)Ž Ls}IkÅz&˜ÅÕ9xGþ¿>”0[ 2eî/½Öh’L#êïÓ%^ÙEêðé¦ÔE# ¼š×ðd³Jd“Õ/‰Î¦Æ"¡'“¸-‡F@cJ<m=JLÍFVÛ KÑBgZ%Ú•Â6GümDŒÐªÂÏZ ±0&«‰Á@Ć³íµ­Ý£é|%ºê×
+im*„i%Ò#t;(F9á3³øÆÆ[¡Üà|ŸMI9¥®c&“§3À»y ôTäFd€JˆF Sº‘§êZDÚ5rÃXç Ã~cKv¬KfI ÿY ¢sØð
+(9‚ýS™îeæ$<A±Ï<iJÆ—6_5õšç*ÉiœÝ9j+x˜×üðéˆ:r?& ÇW¿ 66ÑcLÞ"|Á—ˆ0t?E‚‘•dAÛ·¶ Ü.­~a¥Mø
+“ºdW{wd7SŒ‘éÂÅ)´c Ž@a©¡:îò¾”:
+s7ãPô¹ŠpÐ.OØög"6¸! G xØcô(±²ÝïòM˜'µ°©ñÓ 4 GϱYÞÄ Âf”ƒ^uìÍ^J¦…Ä,àÉHÄb ò’9‘>lJíaš/%ëS¸á”Ó÷•/ç(ÜÀl9™ùö†9ÂlJ¬‹ôñÊ›hÊþ*ºP¥é­ì5mBë~zÄYCÍ Ï¸vôj1i ¾‚­À­Öy,¹XU™ñøû>˜é5 + «¹µcH›†~}=.Q%9Ga„š-d_Óì*as‰eAÈöØUP¦ÁÃa((|f‰ý—KŒ,’ƒ!„»„ÿxGA¸¡åˆ[:·±Ðd"‡<ÈÃ9°r41¯Õ½ºøJÐá!ÑJè×£ù›ä$|˜SšAçVo‡)[ÉðÈÏs LE+Ù (aÝ…«q4qºPËìÉ)ã ‡•@‡x‘®„]>ae~ Õ“?¶-rýXP—a9㪸“m˜å͘ŒC9ùk0Ö$K¥NîcÍRÌ
+Ìé.BÝ ¿ŽÈ> îÅBñaœLý}ì÷hPÞx_]†¶´ó)ƒ…9«æòW4”+|!1”Ô791iX3«£)~vÙñºð[‘>E ˜1l/ÃE×b¶c0—MAør—'ʸeR¼D S`v¼Dº+’UòþëTt™ùçCii…*Ë°ošmbó²3WŒŒ±)˜F8¹¯IÍÓ–ë‚Cà¶1±¬Þb¡(g9Ñmwè¹ÀÛ.ãA/ËŸú<:GÞŒ +ÁmeTѨz<èaÔ>ŒqÙwÁ°ùTK®üJ4ïÖåUýM,á
+Z÷"IlÑLZ0´nß’)ÀÕ± $C9÷û…ïb~{øÉÐ@ÒAPûâƒ)` ›ÖyCûØq[¬Fóq¶CZ7(D8¸T-.«ÃVTc0^5ãviƒ}ÍzÚ°Åa»³¨º÷}VÂã±™5ÿ>­?¡-Ä¡¦MõOýe‘ÿÓ, Û¼…º³!<!145a®{áç±Àlc_WÏ30xPÇ‚EAxÙLÅ›„¦ÓçÎÚ÷é7‡« |¯&'2d
+BüÃ~yö DÁÀûÛN‚Rp(HA?Ƀ¡pE)|Z]&‘±K-‰“Fõÿ'þëð3•´bÔ(+\<¶[àÈŠO,‡¨É¥2Í,0#rS?‚I½¢)ö©Ó†cÓKÎQðÖÄ0Œœ7<2,Áô#Ýß់x
+nÖ1c
+™·2Iß4Æ®ÀËÄÕ4#Ä›i¤„n0 IVó<£Wq±Œ÷’»côj.ä¶Q Hwz†Qoî%OÑÊߥºÆ;’o†Q›¹L-Ù ¾ÎÌoÜDM§­&µÝå&á:s_ MIÙµŒ/²&¾ê–‘=K¤ÒÀqy–÷xФ&OáZFJd "î Öñ`ïEw7èpŇEËèÖ¼[F±ô”˜PïžQ WÚ¾å‘|Ïh+7µæ<ÏÈ4ƒ`6éj ®ÙÓvóÀn˜Ùƒg¡4}s™ÛÃgw’Ã’çÅnŽ¤7Û^xž1˜;å?à0¦ëÙ1(p›¥ÁwËxßgAEâ_!JsÚwµŒULÐH¬y–Q¼}”ÂBs™ï[5ËqøêolQ6ú °†ƒŒïŒhïEw»ëDBÅ ÚR>.BH¢"o€Ë°¢ûqŸÑo >´1,W~¹Eég¨ªIŸ@D ÏbЬ²ö,2—1×b%²Ã³cá“MÎàw ­Ôà1ÕÔqèxÔÒ®}†jŸAr $œ-/Ú%,WUÔës• ³Ø^À¸YãR²Žšl1†²ŒÍýy‹´¡‰2$gÎ$T’Ä|ÐT±õè^ôæ˜W“•ÜüÎ-N~«
+‹I@P Ôuäöé0[E®„]1v’~‹§$êÉîw¦Ü/}n²ðÝ» ))í„R}> xÜ/–#ï5†
+œLUàœ¹E/ɃwLŠ«¤(¦õÌ·6p¼rYmýõÃ+º˜ê­MÔ³âÀŒ}\ È^€–I»I³$ᄄ†fJ
+Ž˜ ‚ûÙàê-’nD…q•D1’I[Í Q9bwÅ¥«…´‡^
+yÉó©âyₘ*Ã$ÏÌR‚M+–fj›gÍ’™LÖuZ ºItcšµŸÉâ6ðvÀ}q8Â\VpEû38‹ÿ1^.É‘ä8=AÝ¡O ãÿ³®Yö-z+Ý;ÏAR¦L"M¹)3•\Ap<Ç!Ù``’=†§€×BÅ€Ž¬u©:š„ÇF®
+MR³b*ßT«I~Ã"C¤åhþõ5p$7 âa=vÙhh ¦Œë±µ#Ëa¾AR†šôáÔ£3|-¥3¬EͺOÃ$ü&
+øªÕ¯Å@ŸI, þ1nw&6á0UàlÄéVê׊ÿ=ÄZ\s…¥º<
+Ð1‰Ô"/Óƒ­J rÆdüº˜3µ Ó#0 xÜGÝ…{牷%­ÃÁ—jÛÐÑ
+‘/ÎäÑ2¹¢âåx2+EH^ªŸ_î~¾ò‹Ê ¥ñ½äáÔýF(: v£T {v©…nç>ùf4nÅ|^ûjU-뻣öÚ°ÐZ“ÞÌö‡’-À â‡NsGõ¸g¬õ“#wœÏ\ñ:2EÑÒ_ö€Î­„áçGtÅVÏ1Þ±~a­FŪ5…§a¶d_dÔ¾ ,sÕ£@ÄiI¸ÑH5ÙqË(Á\fƒ‚…=wtÈ€s
+ê» NîVé¢\vüK³ue«®W¯Â@·^©Ô¦ˆi5˜E±Ñž3ù‹)ŸÄEÓ€ °2/½úO÷Ë`0bY
+gå)BâœN&#ÁºªU+²R!®óšŽDµˆëzØR½¼zë’ôY»vâq7¾NF/ž“ÄN5Õ¹gƒâX¹± J+Å­q¸‡åÌ©™Öê‹xrinê±—‘¶ ¾Ì×¢Ç/’)3ÓE‰ÌgÍYµS«†|œ–gMÐN_†ŸÞt)•`VÁÖZò3 
+Ê3ø°ñBSPMf€Žn'$”€=Ö¼æòY UÆ^¯¿tà¡F&OÓÉAòv°ø‰%b FƒiO"¿S£0>'>0£$ t/DÈá´Ø3b¼¬¤©gà†§ã˜Ñ$ANÈÛ‰¢Îª–„‰"R
+~ Ñ¤ê©Èá .5ZéùC€—ÓeÏWÍ(sfÌO üÉŒ
+úÞSd¡†£ò´°‹Bw)DV‰
+ji «NÑ…ý×WƒÞn=Ló`œ«+.‘Wp|•½@iø†“÷~f–ßW—É&º¿éIôù΄KO5|Û*õˆëQ“[kŠ®“ûò4W`‘†abTpPüèã‰(+U„÷…-zzÛLS}:4@œÎ.àÒRª$–T´‚Ö|Ö8UÖжçŽÉñ•ôx1IæÍU«¢ìš%soÚŒ¥h†f"üœ]A¾ÄB+ìª=¥ 3tM÷6Uv!a‡%Ôç’T|§Åi’'Yojƒ_äji¼x»‘öIì€qVäÏUáTA’Dp Úgqå OôЭëŠ{$¿cyýÞKST
+À¹åäaŠ¦eG’#&#F‘AZèÚ€"´BË}ï%®|ˆd1Úi’Z@vv«ÞŽ\/‹h8%RÜÇ(ªÑÍ÷Àœ¼o!ˆÑÛlþ¦䟨1ØQ——صÝ{
+¼7åv4}ÿ?ng#jM„L}MŸá
+ãÀžÂ³ÅOGto7Dt,Ï Ëz±Ñ F·Hk¹ó/ó*éÁ$Q¿eTJÞ,ÒÕl:j–9q…3LîwŠzÒnŠxb”ŒòÑÐÐ
+,F÷1H0æ•¶Ë çâ‡bœØ›KRH ØÂ’Äå=fÕþÖ€\ìŸÂ€ÖzôDÙr› E
+¥UNÄyŒ[žQ>§6µ^B,
+BîSíei,<ƒzÁäÛ>8ÙöªðüÔë9 –—>&Ý Am± ZøèPƤ):Vfj?E‰,Z æ½jܘkÄŸ²ïQ?vV}ŽÄB+Þ›½öz%)Õ‚+Q„õüZµGòÇ´¶%s$bjd¬ˆ,eÊw¡Ÿö)u‹“~–ͣƩé HVFmG¥dvˆ«\9q· {ô©Ãd¿Éûx¿ÉÆúÒ_fÐG5? ù&%<µâÏ£éÉz(q%«ræñ”*Šq~ :ôëòµÏ0üñÄüš5ô€¥Êà +݉] C Ñ.—±Ð]á˜Æ‰¢-#ä1ÈÃÍUÑSK!±2}8hF‹ŒÃ@Il%†`vÑŽÁŠ°-íWî}]ñeff 9Ñ]Xî˜;¶9Ø}©>S‹'ñ“vwL¦
+•÷ÍÀ€ ¶\1‰Ê èg’`I£ß7)$ 8´±u¡ZGç* KíÒPrÿùuþ˜.ÇÉ:±ÛÇ©€…ïc—x’°šAYÜ„šNo/rW¾eúéÆñ˜ò`cQžI_ý†Í‰IN7Y0&´ÙËO Ëôñ,Rf ¦Þèt ÄõsŸ€Þ9zš÷«¤Âö6ù!ÌRÚ•+ܧþŒñ“ìî¤nÐj¹ÆÍó";ÅÄD|öÔƒ®Ø™3£
+øÃkyÉ1%,(1}Û—ôû˜3Æ¢6…^;&m³œ‘(íÂ25jœ„w2߬8Ä”Ò}jX¡ Œ¶rwN‡N“l­€BÀ›¡`˜Ð!šÄ§T.Ot@’ñk—¤‰=€²wžEh$Ô} Êë* Y ~¡¤ÖÖ©f ;@*Í$ K<lñ¤’ÏÈÿ4Ä! ¦b`(¥'LÚiÔÅù§Õ rCŒJ‘R»êø a¢5;§¤XIuða
+ïlÊöŒFvñˆ9¤ó)9¥ ´^áõË>%?MÊ«Üb1£*ÖO¤#ÕU^ˆN²1–ØÏÓ¬ÇW˜öN¼øù„aNDÔi èÝs;'ÄÖ²}š’•7O È@Ý00F+oÛ}Iv@ÔûÖbgØò9!¢¢=q7§›sP³å;¦ð\'ÿŸèUŒHa3<Ä+äýZC)_4³ÌOA§–ñµÆ¤!i½Üƒ^®tȇz<!½k­l3Ÿò¡JÝ! ¡>Ä?k(i•<_Æ1 ªç‰¸Ñµ^¥y:…шš:¿/ñ0]×€ø÷´IýýÐüˆÒQ‘–Ø?7 1»
+«Ð Š\˜«á_WXW‰ï-;B™Œ+ÅɽõŠ\˜ßÉ~s„ ÃTxŽÁ®”!ìÐkè÷윎4hfÌÅz,HR¶yÆB0eJð}ª ®í*4»Ï›sf"ËC2<$û«PÍ”¢øŽ¤Hp 6¾µ[2t™cšãŠy[-n@è(R6èc¨ö-ÉjW*Eм­6©dZöŠa²\ `BqH—uV.SXüå•Ÿ³<@‚9÷xĤŒµL;'“ïÚ;GÜÌ ²aÊÔß‹¬ QOˆ÷é/”“×~<çÒòëm.£szÕû^‹sãK‰O—V}ÂÑ+½ÒC«'v‰K>œ÷ç„TÃâ®õ[ÄN)ƒ°ËVhf<sšÙEz̬P<Rï»ÐEч&RÎ]"Š<­¼Z˜“”ú˜W7TAX]Æs}©k߯RWÔA¼Í)ˆrŸr[j¸¡hü@„¶"ÍÈT¢ì $Q….שÎa¶[úØ‚XEVYªÖ<äVK› Œ1.…Ä—sIüƒÅ–!_&Kô}A =Wäë× ôÖôŸPSº'¹+3Ø鿃[„¥^”
+:FíëåÊèÌ\Ú%&r׈)ÌUßávTÖNÐáGÑ…šXÅ«U¾$G`a1’†ªÏÔI%_ >2LAMSšÇs˜*’
+’ ɵŸ¦Áua¦Qóy#SÔ4¶…|BKrñ‘–-)å!¾ò
+# *&ÚJYÈ%cí§Q’Dê“<×þ’öIÚå¦ôªx½ˆxÊØy®lÈÈò›”)o„§ÈþKÔÚ¾[% ´Å ]=Sn傈
+æ°L¶Y8îæ®k»íe©6m>\0߇NŸ‘¦>zÞ=^²¬_+Z=ØS6бÄÐr/r³-kH”Æ‘bÍâÔڵל©Uw²#ª{ÄA ‰§³îô´¤ ˜ ©Ì š¹Ó•Ö7¶ÈÛÜ
+ “C^U‰)õÖ” fÓ3Ž·!ýúu
+˼`ñx›ARa5˜
+÷!žE¸vU–ÀR¤è ‚4
+üŽü¹Þ@˜fÅp¦q3î £VvêFðBg®ª+±¤s‘{Håºt¤û›”…B×—Éð( o,OiL„^¬&‡¶>Eѵݢ»Íˇî‰qšäuŽ&Q#¡`$D­Ó†h"˜ÈëêÔ7øO[ÝRІ²MÝOF-Je‚8A Š­FQ”}¿V ‚IH둱°lƒ€H´S/ž¤CëõÊ‚ ;, µT?sdS‹ÁnË¡ò”Â-k7ÈC/ey®/½ƒˆ­D-t轟#£<$´B‹ÊÙµ‡6ƒ°†Ð<Ûmvº›ÂâŒûQE®6!Ì
+“ø
+Ošrq§Ô»¥¾3QŠ¶HÊh7ñN3;a”b71 5ÿ’äóQã’§SŠ9’d2r;´Ê0²).º½E)^wûŠ¿Ì<‚5†ñ¥7xŸ’ÀÓîp€H sͶÀe¾ÜZ˜,Œ¥h8€~„X‚&W;QàF™ÀÑÝ¿›6¹Fèm= qO$lâíâ&£–ÍŸÑÞ•HQSÞÄ5Cz™Ë©VÚ»±£ð'û&4â—wø%6`&:vnç ÂyG–WäšûÆ—cP—BŸþe¼\r#Éa zßÁ'(èÿY»—¾…·îûoç¥tOe2áÂ` ;:%‘Á`vý GÊfÓŠ¸E€ÚYÛ‰Îé˼acc2#}_'*±© ÕÖ÷Õ`R>ö £âuº“Qý0?Û%:™dzm®°½(]Á‘Kj5?ë Ä„¼Ü×؃ªÕ™'<ÍÛP © _ÇXÛ@ɤ†´-E4C ᓽ½òa¶ìÇg '£Ë«èa•g$Â_ e»>šy· ®ü1‹ ©ݬ“hŠ0§õqóL¼=ß\†ˆT>êí›`$áF [›Ê¶ÅKKsįÂä©Së¨+ˆB”_nsu½ Ã(ÖÂVÚp_…mgEf-Ôju*Ě̈́!«|ç
+c_[@Ùôê4ÝN ë¤sÇïœhs¹Ì•}Λ®,¶HÃ’¤XÓÙ›Òןø—Øo O:F/ÈB4ù®°n ¸Å
+`ŠW”¤©(ÒÁTЧ’ÍZr­¨¯ºw}¿r§O»z͈88‘¶½%ÄÁW RoWgQ±»XCdÏ–ö0^-Àš¨)Úˆx]†6u/AäÞ_“ÇŠŸC
+)cHD=9YEf
+6Q¼,/ ‘8¢@=Ø1Gö$‰2Bø"Í”×
+ ‘á‡û¥Çb쮢\’Yw _Æ ­¤QFµývýKŒ!Ž‚Üzäî1åʦM<욡ȦÇò“5Nñ)=jC<‰ü«zdBøFãµí3Ÿ<šã{†j‰îÉK½ûT7ËÈ»|xóàµ<ÒÚ[0 !õWAh‡¶[` îm ëN ïWfÔFùýÏSó™‰lDÑq0ä)ýý5Pµ€I^Vë0èø¬ÊbñH„¥CWgQðÙ-QøÃ÷¡²jÍ2O©=·ÝýFs•×Ü–î„ü±jȤ‚ÁáépgùHS9q—)Ç&ŽW7¬EÙÌ}Þ€0… g+·@د
+\Ð<_áÆ-ÅÐب'¬WÂ…ˆ,¡XÚN+1a ÓÜ÷a´(­lû>…MËz`^븑¿_dÔbA!ÊHÜ*SšbÛ#¡Q Q©¦¶UÉmÛ╸QF^MPHV‰ÁAÛÏ.MhšAôÈCñ\Öÿ!_vRÂ3Ó
+¶Ë¼ùL¨2}Ü1þ7…Á(KáM…
+sÛ¼ÊgìdㇵŠ¯˜«=„¬±Û¦/bœ0ýNê3ù¯Ü‚N7:;¨o«|Ji@×-sç”$gURê(®uú°F0=N Z}õfSòecœ(´Ÿ3ûR8!`EÔZìqÝäÌ- £¡‹G—±Oú…€fKfnY!œÀC““rÿÞ€˜vn0«Æ¬ÜÃäâÙZëNÉ!ˆ69@x3K8æ‚`~Φµk“=*«æû) 2n£OûÁ wÃkRØÏ—o¦:2ª#‰c•GJ2ˆŽ’mIPLM-7ŸQ2AvÙï3º—!À°\àéÆÍ£p?2ÄœÃ-ŽBN*Zwí' œJìAžZµº€ÐXÛØØ’öîÝÆv‹†‡î£Î¯"7@ÆŒ”Úû>ê\.o̼¤ØñïN‰YtšV{ušn§NEŠsÇ/ß¹òær™+ÿœG]yLq`=£„Á™éÝŸ†®eÊq&ýòÔªcøX÷
+Ñg¸K?ÞÃ÷<©) Hã¨ñe3ƒ×ã^tÁ¯3È4&wå×)Åîizð¯Ðo< Ó‰ßög‡i®J^:PEÕamh»t©ÒÈ(™æŠˆØ#:á³ÌêGÏé!j‚æƒa_Z ÄõˆY—cä`á^SD,¥d‚9ÛLo†×³.—ཇç|õãÍ]—·ÁÊ@EºF|´+ë–Y(Ö°+ˆkOŒãB¬«<­^SÁ8´-¾=×XÌ9ä[TYˆq¤9–ó® [R Jù_RaqC„Àø¹
+™Š|´5o~i¢ì<ò´Õ•_ì ¦˜X v§PÝ„WËkA3*Ô1ãîd”\bÃZda?Þ<ŒL†6Pm뼺páª3‹ùPD!áïf\·a)mDVÉtöäzø‰Ò×=I0ZÈb&ÇtoÅ]]”PŒ6ÿŒ)ó¾RØ,î+u ‡ÛÀ V9ÑZ«A0kÄÔέž,éÊÝôf'Uf Š#—š^õY2ZÓÎË"÷,ŽCBxä\'±ž%ªTjn5¨d©³Gý ÂvÊS E™Ù¨_EãEŽ)jÉ@îMŠ+†á’‚¡$}kØŒ—Ar! DO;ø.ÃÖÙæÙÚ÷ßæ50Uùƒ¦ü³I*n3 u·Zg–¯~Æ
+„—¢4%‰é•T›l…ÚÒ]ÖLtM虇îpûÇ•÷‘"g2wÓe0œ‹\O¤“å“<®rf®Šñiÿ óSÄB 5ÅÎéîJq<ú^…_`:Ķiƒ¸:s‰d©·ÔOY¢Ÿ´`g¬¨üµ@ØÖB¡}z%$ ¿jº¯Wiû\Zvzc *Œ<È/¸…]¼h>,ŠPqÕïÿ•jš;ÖÒ˜|.~OFT–Yžë:¹:çÒ°ãw˜Ÿ}T– G>ù1ŽÂªå2ï¬Ýv1Xe·€#ÿÍ&šÚ¥?ÊŒ ’^Q…á †zá)¦"¡Èè†*{”‡Õ ¼Œo› ¹E‡
+±âg̘7Ì–—A Rt×µó•(/›,<ÓH2C>Ì€ªÅ.¥í1ŠDAîAˆ%)^Εj˜Õo.ÒÁäá)t?¬Úá˜LQp­*¿ûúÙ{—Á,aÈÒL:[\"GbJf2ÚÅåå÷¼¼ƒ¢†³RòÒ™=²b±Ó?Ï&yDÿ„Ê»¾Þ‘çHÕv’dœu‹ÿaÙœgub7v¡øPñÚº®žÔJ“üxDo‰•°øíŸ{„iŠ­M*â…£;Ȉ®Â˜<˜Q HU"&¹|>»ä-pö©°†L¦°z¹áß ;tDlìÀR¾‚%âÅ÷øåžê8ç¬1d¬%é‚0$YéNô0!Ú)4¼h›ù)îF¿È›=œÓ«ª¥åµÁ¸Z!郱ù<ôBÚYeŽŠÔˆki•°+ÀB|}V r=äׯF¼Ž…‚²¯W½@æÓ97Ë–áø"+©Ì—V¯OAgæ6±§i©02VÇ70Ô²ªÌxôñiΟbà’x`RE[«[ÜÔÀ)ÅÆ9ŒÒ®6(_.bö‚6øÓBȦ¿ ˆðLnªYeû“(6L Áýcon¶ªëš£Ð†‘CùƒâNä>$´’!úý
+H‰Œ—A®9DOà;Ô&!Q¤(®=KßÂÀ¬ìûoçQR¹áLúwv9RÉ`0è¥Öúú–qE©]£« ­¯ߪԫºy‘QšÉ8€~MP´0þ\,
+$u¯‡p´ŸêyC,Þ ¸)èž9`&³M+¦ËÛ0ÖS…'A““eÉ{†D´B[ÖÖeÞ”DïÌP$v´-qƒöå$¼ZáÖ-@Hd#Æ°m¯Sו+Õ6kBÁW1í{ÃüZå¢5̱ue)QÖVTˆã@º•² «ˆ“Ù¡.'EÊr£6éxxû“➥¤–s^gäüq8òºÎ©Œs/ZJ— Ä^ðê86ç²hƒªpó ííÃ1š²¯='ËÛ~ c€”Š!ÇaiœÆxsxÀi½È¾)Aèdq?'».Yà9û‡óœÄËî”+‡0ƒï¤…‚®#!Ög1ŸÚÚ`H“1‹y¤B¶ê”Üm^Eë0ºrºÄvÌÌz0°z5 ì Wƒ Ü:œ÷P\zöñ«dyvLÈkfX§—‡Oëu/ÁsÒýëÄüžƒõõßo5ËG~QŒå¨‡‚þž ™1^gòdO3 5ƒkLÈ_Í„¹€ô9zvÕ)&ÅÌ£ LÇe`ô]tl·i¾acŒ¥n@WßUçj´ ´fmA4‡³uo÷§‘øˆÈ¬ñZr,Òv v9n @Ê?tÒØ–j£ÑXyœŒ×}]v0°×Dð4,5ú&;te>Ôô£T5Å¡Â6UKÞÙiÜ‚àY:Ó“_‚×­ïB(ù¥Üªügž“»ˆŽJ
+ßWÝjõ¯5Oz”MÔs¢'³³ø÷,ZhÇ%¡éàN{ ú¡têæ>
+ÕXJv ÎrÛð×جá%•£¦‡d‘ ýÐÆß›+ƒÛU ’Se¥¿“L¹<5?…Ÿlgå ¡rðu3š4!O4S+pr—ø8 æ[­;ð¥³)KQÔ¥sÕÊ1
+ÿ×ÙoèŠàs°žÆuŠ,>jDùÇ9HÒ7c6!Ì™)¡ñv …HËćSêJºÙnéöý=µÌÓ§öäï<&'Í…>ôwcq¶“Bƺ)s™|›lÕjõ;¨Ò²p«uˆ…)ùqÊðWAsÄØ¡bÓ’14W*ãË—†1‚`õÀpµç¨H©dO@!h‘öaT̶ÃÐøú6¸O
+òÉÌJ!å%ý8*¦~hŽ¦Í{ ý ²8‘‹@¦‚iwš'“6J<²ä†`’r©Èi¶nLÅðÔ?ËU0»¤]M
+X‡¯²4{þIeµ„Gì5c–œ,aà|$•wrŠä@·(2NüË+¿Qtñ+x,'’úÝ:Î
+§P,mWɽ­Ž·p9ܪ©•}¿¥M?Imì¸÷ü»ŒÎŒœ y(;¯ô}´ŽgJ¦Úž@dQ)Â0gÝûÅp
+ܧ”OÙ‘¡Þr “arü„üU«uÕé¦bÆûëôÇ1ál®yǨ¡Ä“`ésðì«îÙ„s‰Üeb¬Þs샛ï2l‰c­nuÕ½æ‡sîÌ9¼æÎÀcTw&“<~Q% ò¹Ü’5X &ÝÈ_µZíwj
+ÎrŠëÛì<@¹}¥XKåa6w¿ˆÝ4%ÇÊÐÕ8c7ýOè‚èè£&5Ô}Bjªkn;5ˆL!ªð+©!k”<Ž1Ö¦´H8¦ö>†ÄŒœk2Ç‘‘0\³E«sDçd¢'¨]v+nƒ'¬À¹kÒ§ ‘ÖˆL ðÔ4/‹Šç-ÍèLÝšÜñŽÅ_g½h
+¼Tø²¨QssÀŽ~ÿpžk2•¸¯b!‚—)Ä›…L 㘹T,ˆçÐdùd>·³b$¨A¤ô­<Ùø ´fŸƒâ×Æà‚Ÿ ¹4ÔôRÌ‚™åèÎÁ#½î ö»Æ&ÂÖš²ÐÖ°¼¹šß@šZ’ oŸ ÝÒ×ö4_,‚{¢¦ÏðsÌOHË78Q“½5ä0ˆÆfÄEÌÓŸóª<  _ÚÛÀ4”T[ZŸç¤a*2ñýìZ¨4Ï"dU‘O®E/Kb¦9çúã9
+]S~ÅÛ²„0ðé¢Y& ã¡$‡·ãƒá®yLŸE{bðSƒ¯õ\~}
+ÿÜ¡þåÿŒ×KŽØ¶Эx†DŠ”4Î4»ÈÔoÿÓw.Eén6œw5)VÕýe¿¼ãXb—σ<»X§[»Q)¿Û=”`³é^uÁÜ»aœ6ÔË
+4Š~v£?ódèIP±-¯õŽ>¶ðº~nÒ±‡…°Õuú|ãEÑyÙ•s8>á®øš~MÓÌÈH³G±«õÇjÿ€VW6óʪ>Wýi“_—u‡mMç
+Äqè2Ŭ‹Ð±¸”&P%öâá8§©?w2¢D£”À²·\gô”1ŽJ£ƒèsn}ئ¦-14“74a
+-å*¿dÀ=‚Ñêõª)Âb<iKΑ%¸&ùì(÷jOÿÃ*ÿõÊNåkmg}ãÉcá~ÿ7öãNö`wüø\D£”Hl[fÜö7a:â™ïýYÅ®7Ћ³Unµ8Ë?së-¦!žg=ß­?™T`ýhH
+~<Æ*öÝßÔWà®<;Ie8¶ øYlËékæ»;;I[ñóÜÕšä7Þ½Ï&£ôŸY 1ç óü/­Ø_ùÚýðZ–%IdüEÇ>W-Š*Á¹¯mà.ç„}M­&t” À“cIâ`SÒÃEd“®ë±òm»²ˆ÷DàÞ]c…û[Óî8kÌ—U½]C;Êô!SCdí!KA2ñ+­´EÉwôô¡èïoŠ0ã`Ží =}-Š36(™ÒÓM+œšÈ™Å~å¸Ç;sçdRÀr
+cÔ¬ÝYýzJÐã4aÖ¼œIÂìG¶ï|ÝSlv³Þ¦
+5Ê»ûO<w*:_ÏÉ9.^
+lÐc¿¡wQSyÿÓÿ; ©(Ý EyP%9™'OC®pE.^î¡ŸŸ f7úoÕKÑø\à¹*7Ûxï&ß f2õ~Tò­7Õ²=MF™Y®HÉõp='!ϱy¶ œDã'm‰ÇCöÍ8¿”Œ/Ak¬?¬NŸãàšÍßž/™¥HVбf­k½¿þßÖ´rct2e&†»×¡j˜áW—w|q—[þÈôüNf™U¼xÔ_œìúÿÿã
+!P±³Ó¢šÓ鱕÷6%B¤riµÜ%4—A¢Kí’-ßzuøvßvbÐŒ}ìÍ®!À_V¤ði·C°9BÝDNI6ȱ¾÷9–5Pö>†A‰ÄþmVâS…7ÙcÑ´ÅÏÎ)?CM›Æ’gÿ¾ P چў”Rïwí•ÀðÙ›·šX² /vxkãSÌ÷‹³î#ˆÓ?JRŸ
+Å:ên¼Ô}LI¶4èë4y÷kÉhñkÿ&ç¼N!—‰!¶l%æi2ÍÈ´¿?ÉøCðÏ *"Ô\Â%çõÎ+¶(,õ:| zË7%ayF Á·ýþÔ
+bfåR’ƒ<[wW
+(­ýPz¯Œ ½…Èïî1ëˆAT …(¢@p¬÷9ÎŒ#¨q³’ j¡þyÁYF:1Üø LŠ>fEù‹Q£>ËØ™!5Ê_êc…m¯i›Òé—¥ílnÌÉÓœx&RXÐëåhaËŽÏP×R¦ý-7WôŒ*¼AkÖa„•<Ü“mV#ÝGøéKÐ!]ÑæíDÖz/Gl}¹èMCõ3Î|›øå% Œ¥Ð ÙLM·]ma Ô23æ¡}“®mÜv®Í“›“<.Þ8Æ#/IË›w=*”ÕåC$ù­¶Ý´ëroÂ>,œ¹Tº­$þzmúIa§E‰¸õ(å¶BŸ9á2 ž˜Å ‘b¿U ùñM¯†%ŠíˆcR¢{gë³·9¥tµ7<…@RR¹?ÃÓ½OÕ<dê`‰ãºÏ¬èßêúñ7üéO+–9Àioúw–üóMQ =°×T•ØT.u‡òzy$€ð1m8”˜Ž\ÂZ‘õ[Bí]`ç­¢Î8mÛKÃmCk6Óv5"¶0yÈSGÃúo±qh*%Æ`ØHtÎI‰OuÃ^°xììœÎu#8Â/±ŽÍ‰M³:^‘õYG‰‚†
+gÇñî*ý˜Õ¼ä…oŠ¬F\5›¶Ô| î"4ÜÞºÞ¼<¶ñ=®Ñ£tyïƒÛkWIôÛ@©KØÐû"èãþÂÌ\W >ö̳¹FÁõ[¸d&š¼¯2ìaŸ¯H2Qòƒ5&H˜œJÉûÈßnóÉ«Þ!øÖ _{<-ù2«þfk~ßv¸̺%„/Ã÷rÞŸoŠÖ˜+¢Ö‡q¬‰`ø‚­ØÇCx>Y¢y*v²²Ez}– +^7?Ó ÚZn^\œ¨ùM·¯y´ûÍ*Nh|6ùW1PCö3¿*­Næj‘–„¾>ZÛžHA8S’fÖ¸òaF¯%ï0v-¾r¬1v€ f Öân /-‘µžŒ«„ÝÖǤžv9XL¯ØÄ—ß(á¹³¶Ë~=IIò"a””Œ=~+zûÇ7E­‘Z{ÀXç[i2G¬õ˜%;//GnÖÃõw?Ρ`RD?3ÔšnÔÆsü i•­öY™2åk®ˆc×x+ñozŽå[פ)nýè“<x²ÄÍð)ay ÷²Ç ¬×—ˆ³Í‡‰4þnËcè¶ád»Â†@*œxçYD¡ƒklû~•¼®N®L…Ž(ŸúïÅÌ+ƒ-!ÇX›ÆÙ¾ÿä<A6ŽK£¦‚;)ZBèhÖFÊïÙç^‹>þæNãê B= äïŸ=F!6»é·©ýI  ‡ÙŒóñ˜_Hã#Eµf½ZTzŸqFŒ]œ,þõ7}²î<[ßd’¥Ä:å ÚÚ}{e˜œ[U,Z^Þûíü™ö°MmW T?.}]î¹nùYf G6yRô>0kK¤ <—šY­iµCîéK2DÚ÷V]ªMõÛ4‡%¶`G‘ïÜæ|¾­':O‰m‘ÿánšãƒÏÚ÷«%œmùyÃçùk(69§sP2–‰«Å&)2“¯Üw/û¤äŸ`BøsNK_gçX§f·…a¶[äÅ&Ÿv» ²,q¬ö÷Žï£ ëõ¥röL Yz"âÁ °ÅÊ11; šBëÞŸˆ¸W€x{%qe8˜Â<Aó‹;øõcRô>òÙÞ¬ta9Ž¿-z]¾¿XЬqë?»,´• ë-(Q†~ ¿³Ùãv
+á®Ïšg¹cu‘vdŽçš{Æ0}x <éêÔ
+4%í“dp™Ä9!Ÿ-ç’Æÿo’¥gÐG"Jà"«Í "¡„ô¾'Ìä{˜i8 ŠDÉ/…ïz £ÍÅAc©!âv@,$“€akbS5ÊQm·)¦‰!*`þâm ä,% æi!Â!ç ]Ûâ'’OÖ´ZöË )-g`Äœ/Jd8»"ߊþ÷ô|§µ/'¥2õü
+¶ä}²†« Nt]ÿ ¨c/˜©J0±ž€Ò õ‘˜Œ€1NÑLÀÖ7„Z£Ò?Óð©òfV6‚d9agW6–[ ~rGxìÖÍVXl‘ø“PÃÚªDdS²Ñ†ÐsL.7ó„Lº8MmÓš¿êX/ÍÍèˆyñ#£Ø§-UÒYI8ŠÒ)ì»ê·JE;™¬™r
+ô>Š"hóæQ„,˜ù”E9œ0‘“àæ ø„¹æ08ÖSâK©Õˆîl>ÞFˆ%€Ü¤T–yñì‰ÉÏ»™ƒ
+9‚®ÿ AN—õ¡#¤ˆï‚HÏû\õujdl+*Ç´ˆX³ŸªK Çn{}(L Y½ž£ObÂt¹nÇxRô&‘ѸìZg ü;û@y (¢üEÔõ do‹ÊµXZ>iPᆬÉyŽ\4C×kÒr¤Š-ôyœ;é3ÉáÜ£lÊ2Uã/ƒpj#ý“V-) P7Á•HÇ·³Ð„,*ÑÇ8ˆ{À:;§¨QLäh"´ùç·
+z ^ó¾ƒpqÌ›x¦§æ¯šh½ª¶/%îb–2_Ìê®ß€Eñ˜µlÃ?$£°×©pB³¦ÐJ=ªNñм^½•"9|†
+døÆŸÓ[–Ê#]é˜>™Ì€t|CXN ¹¯~ó$wÌÕ‹>CÐô|G(U¡ÐO,“8ç B$Æ¥Œ“öyËÄ&"Ÿ‚lvÕãrVó¹w‚@ðtø¡z$ž4“f–…-^â.
+àêkä#ð.d]Ë·  i\Á\üxgx>÷Öðë˜ûÚñ=’ÕŸæ¾#2F…ÙÇxäXTT€ª ‚ãFìï!Îy,Î:^:Р8câØ°mfZ˜2&ö®ÏsÿÜE *ÅïßE¡ÞeÄ씚"RÔ1T|A¬âÝeÏ—Åbpó΢n„ÞÄ‘mz8“O ÄyMO®wcH'œùô‹ßA7˘;¬Wö—±IèûÀמ3´®IkÙʾ8Ä3Y€q²b“ §‰Ý83†%f]±Õòn'¡[%suj|éµãj¦ŒkW>²òãÁq‹Ð&þç‰gS$ÝWÇÁéN¶å‚Õ¸–V¥è$¸
+©fg—°VäÖp$Œ8x:%x\ ·©‡§ï-”ê2mÍ®`h×@P9ü¬=IŠ(‹Wæwlžü>ŠºõÔ{ÅAŽ‚ ï¹ÂSx#~*˜Úu]à7Ðëb%P …ßæERÚ NÅÓõî_‚ÏQÎ\öv“€à
+›Ì³`Qkï´È¦©)f¬cÑi\'‘£HiCR~)9»5?|ÌØ8÷s´‘ÐL 9Öë6ëâàåƒQ`E#`”ìïfbc‚F$ñF
+"îûéU—¬dyžxŒyA~dÚ!Yþä[’+X{i½kDdÏï4écÒi¼¨Œ–“2cŠ½mK‹YíK$MÝ,I+s†ù7(âfi¾É'uµª~˜ß©d)Ì`‘¹½›RX+ƒdC”,Œzã>ËO©²X2øÔ¼ EµÊ¸Ñ––ü>©ÏÆZáÃF¶s
+‹ˆƒWæª~í^±ÆZž½DeqQQè²UŽC%¼ª —Qô<Ç®o: ¥’|TB¸špR€Å V.–ÑR…UÕÌ©ÉôZµ™V4äY¼8`ªºøCÓãôÅñÙ*ÐMÄRë"Õ?Jô%Òˆ<|)ï“sÀèÆDLÞÂËÀY[^ž7Ö,÷ú(9ØŠ?K^>êÐ!ñmú^ÃG%Lê|ê^´Ä謼¶ÑmÊбÓB²êõU ‰n
+B 7ðµ;ÍÈ€„"€ÏGÔãfWÖðH.-œÕKÉùTÂö¢’iñÜðñÞ¢yÈè’x<§„aåâGÒ¨B5ûpü½Â˜æLÌ%¿ZF:H¿/ÃRªdm·ßå‹úi«—Ùô‚ý.D¶œÁ¤w5ɈœÕ+
+“Û<q9QÑ“"à„Dó4À4sP¤’Zri¹ˆü‚Å,Î@>­„Ö¶oΨ`‰¥—3uâ.«‰Ðt˜"¥Z
+íV;†
+ ÂV¬eVû:´±¹Ò€Sê—ãšØ9|cj6'ÂLlò19„ºµp\q¦Íà¦| 4•Äûv;§qAÅÑ Ú«¿©ˆ•U‡VZ‰zÀ«±ªîÛØ+_ì°ÏÆñK¸[Á®[´“uÃò éX–rÐ*˜|Ó,ô±‡c•ãiê KØm±s¤¨Å¿ôZE ¬°Á=Ëλɾ*NW$ÛZ:ÓÇÈ[ \*u!Ðì‹Ñ2­šøÉ–>E³Äþlœ`|RFF *
+ByÅ-Å™-L"ø1ûŠF¢5Ÿ'÷Òˆ} Ê°pMd@ã5n½ªAt;ºÍëÓšÅ;™Q©•ã,b ƒ†/hÞO:‚&ì7æjlU×x;Ñòv­¸¾JqF½ÞHf 0ü”3IˆíçüÂĪ€@†0ÆfŒ8§jSà+³Ä†á7xì6¶}tâ9¸ëÝOºÆØZÀ ÑnL Ù¥·K—ü$láµòg> ^˜E˜EûkÐLXj–¡m?¨êméÞ~Òf5&Ó×Wi×ÏL5{.’¼ßeZ“x9•ùx ªßôK&——Ú°?¬¥&m­~-0KˆSË8WåkŸDPà×–^–K·ðZÙÒj?³ðn0fYrNYá6^=êJ²xíÁU\GÚæ™_TcFVQ¸D{
+coûUn&›Xú”?G}€*¼ŒNÀ
+Ékc^’ù…`ªCˆ
+–Øf‡È£S‹F»šCø;Or3à¯Bq)9ËPâ+Må¤\
+´qTw'“ðíËAÌ€¬j¥…®¼"}fgÉy:jžšs·^ÁÈTé5šG`§$³»ƒ8•±€¦Òt…n#
+ÅK¶ÛÙÙ]|]
+ñ]ã%àfÿ
+¤@
+Èbñiï ³æêå}¨M‹“¨¿tWUö†K»QŠ‡'aÙÐøÊÆ…£ãIKg[ѦMûq¶‰yÀ¤²º;üZ™‹hrñmj #â¾Ö1ÃÁÂÁÍîˆá3Ñ —á5h\G.WŽ£fîx|«­øŽ &åÚ´Ôé{†Jo4lÆmeeᪿ*ŸÊjw %é yšÁÁz¸ÖÂŒ­Öeü(§é
+TŽäRÑÓ:ŽŽQùø!á7žFïñ¬ 5ÎQUiI“5 O,å—U¥1"áÝŽ…R¯%_· Âi¡ Ðý?œUp  NÉåÇ?5õß+¨›*ˆ]T7S…F`ùè³
+o¬XíçÝ/©Â µ²Í>O
+z8`˜
+2í„x"¸Uñ?6ß]þ.(×<”=•:`õ9ƒÀ‹Y–Cö4²Ê¡
+IäÇòNw/oseÓA-•Ý&µ*ŸÊ[ëvH¡­U7ˆe”¿ðes\?“E˜™ËB$=nÌO*Ô] õm9úÜfiöÀçòhü³Ÿ”¿ØAFî5Á8L*%‡8í;=K’°aõ|/Âß
+u$,­éÕш🬵±Û]A¢lWÕä›ûùãz˜»õûP!¼&EÃ2eLÔ´Z$Å4*l1ÙÙ-¯©þ(¬mÍÐIpß@¿® OÙ´T£LЃ-@Øz´§ÌžÚ•°5[^å=ˆ£còñûO!—“öréwH3ýA—×÷G 
+ aFˆ˜Ùf=úN!‹ÑïÛÈT4íFþ8O¯úf)ë>—z&i‚6ý{Ðup{5¹Í~©</&Q¸9åMUãÌjY )r±ö¿ÝüñÃ
+£vFTk¥\Þ,Ó4JÂ^ fWu!íCÃÀq ïr^ßJYà-ŸáKH­rœ›ÔÎË”}F˳Î'LL½èÈPÉôÙïÑP4ÊÄå5 ϼF3”a/tŸÂfÜÿŽÎ³­¡€PFÇáø´ +¯°Àje”úå-dÒk<)P²Ÿ1¨`Йɤž›£¸å}ÈŠ§cM†ží;QUœŒÿIjôC‘,­Î‰‘ò:‡aòÒdȱ#–=éÙuõ7½ø²[RÆ%üm¦;)t”Í gáìšöFÞ—ߊN¤,xÌÕó Ò^ø/>nëP›¬Hòc+ðmËÉ»÷ݦ/иZ+¯Þ ÏMõº
+EFbÖBðÖõJ Q `©¡¯.â;U¦8’Å<uÈìC|:/ìRFQTJaŽIØh Ñ\³²L®@¼éœ%ëÁ5×ý¸™ƒÈ¶¦ÅÔÙº¯"‘ƒ—˜Mg¾-_lœé¯žœ<c­ŒŠb1`ê÷qG!èŒ „úâufRg[´qÀ÷yô’ ?ïDá_úÖ<ZõLúz9üÉ‹,R¿Þ\üI¥c#4òâ…Ò!Ú>%ÁVŸ¾Nã™òR®—¦vL\ß„@x…vä43Ÿ­þL‚üõBû¥hÖµˆ„vÓtF·´„[ö1ÄÛ©² 7AèÄFú¹`7Qb ¦/mØå¤VE‘;©>ˆ
+Z
+z‡Ú#Uq)¦‰ÍQP±­­^…‚*¦‘y4Þ{Ök½€Í&Š ±ÖÙêÏ‚#mJ¦MTÔÊaj ëgºÅ‚8ú/«fÚ† ¢ùàǧ …
+ÕŒôW‡ÕZòk²ðDʯ9ÒÍ gˆLK'êaáø|˜ åØê_GÈ
+]2_Æ2@#϶ZÓ7öšƒ¯», ÖèµË}0„8Šb™a‘ŠÑäÖ”7/3dFð u¾<„M`:bRõÇD¡ÅùIä!œN”¼Åø!ÜOªLU!í¤yÃùÈA¢e‹ÈB(}¸‹¢œW-%€)D(S0ÂooZ=¡¥$VˆÞ÷7Aì[ÀPŽ*¯AR,áÎ@FsÑà]+Ü0‡—_“ðKÔ8<$éM`[$ÒCD8,ÚæKè4¤H²Ç°j+%ž¥¯£ÉœsEˆTNN<†ë?ƒ"žÆè’|$Â3ø¹4Xóaì´¸
+ŠV±å aXΗUé­]øÂw"…ú¥¢ç8ˆÑfë@*jË ¹Ê:Z‰e0)òÇy
+ÒÙ½¢ˆÙ¦¶2òYè·Lñ½ÉcÒÈaÉzò 1Z¹†ÖM·C¾Hh7¶J8Sdû¼ ¯iüBHK××ãàLs‹qciàØ›
+~y
+=•ã
+±ü5ñ¡¤Ñ= r =ƒBÐ/¼Kºð‡§ç„øš<('æ‹¥šâ±é
+]ýºJ„ôl²æb/N}"[&œµK
+U«gq¡ßÍ‚CbêøÔ6 JšÜ®*"ë U‚ùÙ†×+R1¹
+üR7Z®‡B2éÆá#æ³M³Í dQ‹û:íýN…Ò^U
+ÐÉý
+Y*!²‘
+ýî€Vp>Ü}˦錖ïwLsð€$ïOE ?ŽT6Þ^Þ[–*VN:À«€’ˆFQœnY§@ó¤~ž±ÝŠwcYù4ŠFïð)§ßßÁ~÷±»y  [ HÄ6D½Nº| ÒLÌ6ɯ¦¤…³-<ê % gÄ/!7¼›CCX–9Š]¯/)_à¡r¹¿BFŒrâêÜ"‡â ±3ldMK!ÿþDÞ1$nOp„µà=¡«*3MÀÏñ´T O€°rÍ»5HuÑœ±Y4µ ;„—ÎJ#²!‚S¿tu }´¿³.M—æÐG žM7C˜‘s\•žE\:¢ÄÈ¢ÿ®rBŒfk,Ç]ó"`r*;²Zl» ÀGN 5G:JP'ˆAÿhNý°=
+rÕ¸©©êø=Ïc‹^:æ.â¡yü¹'H1Y¦>›K”$žš)‘Õ<}pI_sXíyx
+9§iéb|ôÙQ÷„RSÞhÍ=¢m 7JÊbD«¡ÌÜFÊŠbŒ‰ aœ›
+`¬*!tÎ@ËAj`“ßN&²8Õ4)ù"È%޵țq㎙ Êõ©(áO +ÖHK5„ž dÍl^„y£
+#˜†¿U3“™ƒ P,HÅÀæßVÍ7²K‹
+Å"Øÿ….Ð' Ëy´åÉQ¸ß
+ÏB`ñb- :Ã&ÞCê­Ä—’MÙOqDº|Õ7‚lœš‡ÑI·Y"Ž>C¨
+6/i²A<çåèÖ9@BÇÉ3i¤ǹ‚5%˜ù$´¸wY]€BÀk<FƒÒ¨ uÝXZ§³¯ ÐeÑÍ >Ç‘ Ï™4^<¸86áø ŒŸ .do
+Š%Viþ©¯a Ë<§¨¯„D;fN{3Ç]Ç›ú OIúc^B'`é-irõn!*Ñ~¥AÆæBÒe5,õÙ<}W™`dš¬o.¤h 0Е±¯Æ—ð-TÞ*ïë)%yâëÊ (áñè)Lµø~Ø¿H|(±s–^‹¼âYﲎ*âŽþ] _¦Gº‹wá¿šP%J¼Þe+ ÃdØKtoãÕÑEìŸ1‰l$†¤œãØ5á‚CïÉNÔ„eós±—á€ôÍž^4I­ýÿxòà&U@ž,³§Hø† 0y(‚YP»æ J’qÚw8üYá  Så s­ûWØ NËÔqSVwNàyë8Ÿø¸;òq¨JouÕc³ô%SÈ垥чûçBO·tI´ 
+c"¾‰à»/µÃEsf/±áL[EeãØp+{0¡9ÿpýJ^]²¿wgp`bkD8 Ïh%z,Ž{›ì£­Ç. µ0«±';Ýd$Ƈ»qÙ¹¼&— ÐÕvbªN´ms@|ð0-€Uä=6Ãix8à‚³†GTð Ó ra¡*žËc‰pæiA.šdÉ#ÎÚ˨m£—#oBg}!|ÈIJŠwüTï@3øBÊÉDƒ`$qÁ`,Á!Š;>üfÕ’´‰©Ù^÷¥&]‡"@ÄôD +d€–¢[ üÆ8’"»õPÂËéUxŠŠãÿ󾊇ޤ§:Œm bPÕþK‘ܦÖÉ…5ß·š·tþöÅÖý OÇc¸‹I‘M%m…`êR¼dr¤ª4’£À”\–Z¿$Q¦Ši#?mú³èrúÏåþ¨¨o:†Õ’…|}(Z{’@Ã$Ú½Äd‚:¾âà^‚÷ã)à¦xïïXjO¨k Ñ˜m•x•–¥ø]YÝâ;j æ5f¦:w ½–…K§—€*K6ã&·kˆÅd4Wï±ß0®‰òögXZ=@s§ø ÷Íý±ÄK4ìFâ·[e•1 ¬¯*¢s’šŒýÅÌÉïô_“Ëú¡°_‡aQ¢Æ×3Dîñ
+H‰Œ—Mr\7 „Oà;è™"@$ÖÎ2·HUVÊý·ù@â©¢™7UZX¶G=$ˆŸîƱúTó¿iÄ£Ùh¶¦«Ïþñ‡Š?fÄú%DVêaCl\o±Â|©mH×æËf[ë:„ÿ­9ÜÍ£n²áâºB.ˆç-"Öǹg¬5ƒ?&VkcŽ1Ub¦L¢ ÷Á7b}Ùò5?~ÿºÍG—naÂåfŸäÝ'@n4-Úä‹Ä,z¢á½™É…è±,ÚX¼a#Œ×ó5³ cÎæ"ä«_¯n2–‡ø°ë_ùÊ%3´’gf\:®`È ‡P”>7„àˆE—Äõ& -Æ/¥n’Ý´M˺P‰èÚcœˆÛœ1W[#Óµ!„²„hYÉõˆ¥od¥ô’W­¥:›¬qJ• ¼kq²Å>g†åI¼}U4&Ñ:]á ¥9=ÓÆ+â\4šðÉжîO¡*WÉü’7O"Œ³»ú¨ò«Ó§ª\OjžGNô«ÞÞ]$sæƒ_öhôå9å"›æ”6Úé,oÓë %É‚ð¾h>è²èûîV ‰N”I<ÇLÒ6Bmô^%ÞwçSû†p£ªò¡Œ:¤¹©0Mü°ºi0Æž#Jθ̶§4¦ûœa …"¥¤á0  ©§†üI!@7šÃ^¯›lu
+·©~7º H,$BÂC‡Ê`6ÈØAð\ò`Ä?‚^rÊ”õ]×E4<Oâ0_ÍÖ†‘yÎé¡Ôm2ºKV{P¦ÙFQ‚£ˆfÊuÕS<F3z—Yžøøë×?À:)Íi_¥îJô‡Nš•?:çúø÷¤‘ç@´Oç, c¨È%%»þcÐÓuŸ?‰é„þzUƒ3áíäº?šZB"ï@Ý‚ž&,A
+1;ç£>ûA7×½¾ïG —ú%¨s0;¡òcP,R'¡bÞÞæèó'ÏÂÈÇŸ@Û#Rj’Z˜×„ŽÇ€»Éo†sçûÈ@ÿdmå
+Ã-žB1÷)ÉkèRƒÑ–ä‹B·ÓZ±]u¥$ÉÖIÇu“1òDC±WM¸¥‡áh²¡û¾O.ÉŒáâ*hÅ$ªÐI£.Ê^ç"™c„pÐý)m!kpR}݃5IÅZ’kÅD(HkAH¨Öè¬í±$Mâ Ø’MªÆGNuö9;R’D_ Fxàxëšüd.RMù„U%±Aé…ÔǬ«T³Ž*}U!e„‚ r{ÎqÚ®Kêš$ &jeuUVÛÚÂ?žÔÐ|‘>ǧ˱ JDjÐ8 íô#VgáUÏ9ÔUIòu
+Nˆ‡n›"·€ÝUœb±®[žè[Ò q€,Ûò%DFKµ”©ÛÌ¡OéŠ1ˆ[}‰™õœËv‰ÍœVxÈv,´ƒÓ:YÌ£{×wOÓxiŽ,5ŸóßœCM©"Wí@0XFO'‹8^œ:{± >áÈ,¢ÖM¸bŒ2Œ@¡
+Ó¡¾*«øái°Ñ÷Io§3
+¹¥’ÇÃmx˜v]ôÌ2¾KMefŒ3³ÏTõ¤î7Œç,>ÁYÚžsÑ+qòû¤î‘óõqO¿ º ’Õé“7o4þÆoÅøø¬aûmgZðÐXdIkÓIüaüF½°28º¾n@{À2jewåi¾ìp‰ãÓ•„ãðâ1ÀФ¬êRŽì´>ÅY(‡¥çж„0tsãP–´ÌmnD¾ “G
+Îì@Jðû°#åeŽ g«cf#¡„ƒ'| …&àÆÏ2D Ò£l Z¯Z*tÊ;çµ”ð¢ÕC8Y¦’–š£2Ã…’¶—jå\AYÎØJ¦‘{òy8´k@“Pßj>É=‘&ÏÅ8H*„ÔÉk¿ãЛÓÜï@›NhMdX™ÆêOªJ¸ '8Ž#ü
+G CÆM¼åP¤Ô2V£+bB±»Æ¹ÉSΙvÚ:G¨I×¢ú—M€–­üRÆeìŽóË&¬¦éÎè¦y8&ûãR|BØ >¾Î¡‚‘ @¯Í;Ê™›fa;:xÄE91Ð'™›Jk‚fÒÃÅÅѳ°Û’º‰¹o¹x²—ÚˆîäÜæ_ìö‚Ð#Ãhe o ß×Ð;ÈËú
+bÇ$á CÖ:g1¹•Zm‡\B›œí•HÙC©ÝÊÉzõÄ H“gѲ(i}…|³à÷o’øûÍ«¾>o]3ïRäŠÙˆœ³Ý£(Œæ.곃qÁ ´‘e¿5³‚Ks+éÙ(5xŒäM¥¥H NʇeeY$ Ÿî—ã*æfQ&–û†EÖY’ï e”¾«ÇÍ1Ê$p ^¯‚Iï9-å€q3ïZ>un¶Æ­q÷òºG±¶T¯aj„T11p }iX,³xÑÙƒÿ ¾6ýV艑þ2(KKH‹¦ eò«+ä_Y›“€PÈM,£„eáh˜0Álñ¥…+,*o’¦^­«htv¯ÅVx¹†›)÷8¤¿ŽÏ s岎;a<ØÜÞAžýì†qAçpéXˆ»XÒ}·4ÖT¸ÄîùMØ|ìÁbë×õ&XÊzfµIÁ°p,
+;úLIÐ)ÏÍ­62æ’TBl‰§R¹ádxæµ.¤RCßD)—KOl©ªýº
+’~‚ÇÙX„äÚëZnAÉ‘£| ‘deç±HÞÇË(Îö2@6r¤ûNãtÞ´Ï—²Z³g/¦(ÎBŽ=Y~V£Ã%ì­Š/>õ)}¶A ô¶Îò÷|Ì–gKí¥‡×u Ó9]éÛ·<ã±htF¯p‰t¿¶x³
+.Ù~2 C‘£r–ÛéQßš; $•VpX´ÜA8òi41¾^Å/zžåvbþÊpÁä#l4ÇYéL<ͯ´ß„ÐpU˜R¨Ö×M/AÜ…´k“úWRa¹a„2—!Øÿ†O“”Ü’/î€ jË+–1.nÚZ(q̪H2‰åø†nSX´D1;­¤µÑúŪœ¦¨ªTÅ.ÎS ÚåI½¹˜3èL.pt?õzþ¦P‰KsÄ£ºœ ¿¦ÊëWÁMF&ÐÙéR&VµÀÃÎÑ%„)IaIS.Ÿ†ç©’G“3q­óª¿½ŸLhŠ}#‹Xä×jëÇÇ
+§¨ûæ5e¶ì9\–\¡DŽiYÂ\h!S>•¯÷‡Š%íì­;Å õü\aB¡8zV–B¡èýŒÌu!ägi*:ÿÔ“‡ÐÚîTuÇ)ÌÐ’Ëëeœ«ê`þÚ4&q¨án€y°•W±s² FM±„ð­
+òq (Â~¤M{à¶å’òJ!e¶iä”ëB¶ôáDFöRÚQJ§œü3tËÏ!Æ–Gê½5Ò0\,IF'žÇ”¢òðP^„YeÈôf ºfbÈ°ÂÛg$õÖ¢? Q‘k€„Á\Þ$N‚æÖIoªK[óº# a%t©ÊyÖ :ÃÂa$Vä3K:5}+_—! .›>Mh0Gl¸+£EAn;Û˜š`E’#9,2¶-w¯Ô¦j!¶qŽ×É{ÂÆ×ñìl@ìÎ’ j^OÚHl€:¹ª<Y¥®r4c}ƒè,XÄ5Žzfð‡I-"¤i"31r<} ÄÑNR<¬W«yÕÍþtúßV%ü!|ó‘UPᔉu—Ë%įâ@R»Õç@Ô"aÿ²_ÅE°><Å«h+h°HÑçz:¶1APäLâªç ¨ÈŒV¬ˆÏÓË”%ùÞ°Q MÂo`Èe‰œk?‡ÙàÿÐפ{•#8ú3mÁj\!Bì©Â 9vˆ)m·‡cè1íH5¹¶ šßKZœ"Â¥tômì"T ¥“{M}œamS=¹ÁíÂ¥a`£Î¯¯}¨šZiŽ7!´ȶµ|nzŠMµÄ%½{ÄMO#Hh8ÒUÓ›7=Ñèu2@Å{#Ô£¸ðkò˜M¤øEÍh!{’æ
+e±'I6hçô*<€ÖÑæщ|A\MaWà9pÍ?€V²Œ2°V€½@€QåÍe\1Š€ ¦` :dŠ[([¤Sビ‹If`g§,YÌëÌ…Á“šÓnÔi^¥¤˜?à ¶])o¤éd%µ$ˆ1x‹oývÝÝð¾|6†—µ$9ä×¼>¦Züƒ¸îMù'†¢¦_r$äZ*ârÏÇ?À }ª-}9ˆúp"M}9\QÃm;f¯Óè„mK†Ž“ô±nó1²¬_¾W›£"­¯ JÃØAûà+há!©!­=ŒÄ¥ÛÍÛö!ó‘ú¾ (šÞRk¤c.7“)Ãn¡ SòÏ~²|“lÈ$¯ÆŽ ?"õïð+G9W³o&R'Ëäi¥Qv¯€r¨¥3—žÀÞfËP«ve[B é°Cæ¶mÙ²ào¦Oüs4Å›±5É{ ²q¸›ß…4ú£~¬Kˆc½ô ž¤UZyw^"' I«'dkM¡¥©xj È€T#‚/WC
+â&MµÙ¬ EñGcR4ÂGÆ\Š<Îq²è,?ƒ6ekõ[9Á±ªÕµDFÞ]YŽ¦T1i\µåˆ:ê_Fóy†ÕŒMªHÚõÀ‰jæ ‰±Ãœ¥ˆ«Ý¸‹TW›:…¯nÌ[ìz®E†ÆÈ­žú† yÏ9­ì:½{ g*;Ö+šÈ6 0œý¨5Àð®ä-Ñ8NŤ´â*ðìpûƒª
+Š866Ø:îkPTƒèX‘æWbr¹€†áåƒ|wvý²úC6Àrb>»xØ='=”²ðò%5×TS®5Ü2î¼ß˜êK‘çŠhlÓ|ÑO‹<ÎðŠÞLµQ:äšjИ”ü>Ôç ÁàÕIÃÒך¾¥7‹73Œ¢2CÄÓ:¦š©s[¼ò››{a±!‚ç~nójªŠ6”WÁÙ©&$‚öŠ°M‡Y^M5«Ý¤´ê›jà.åf±Ô~cª/Eס3âÍhª@eâO‹ÞWðù“=•_Žp^dI\µm?ŒùTûø×ÁJ«Hê„Ѧ=µ;E*Ñs;m’Þ%A:Yä‰WIo( %×±yˆnÃ>3¨© EEXDÎarÚŸÂæ&–ëœ3r’ô8’]R¢îÇOee­DÛ•€™}‰ jÙûüåÑ7¸ ĺNk7wFG‚¨3/î|­y®îðw2 cÚýV£
+Y“ˆ kºßÒ ¥?ðLÖ£š4WRE¯ŽbHÄEÊaÌiÍ!QE.dyúþW 0»’£
+fª‹gÆÙEK0u£9ÙÃP!ÒFŽ°ÝI‰~ „mœ ˜s?×Æ`%ÙKìý,NÑó¦fåjµãFœ6ゼûžÞ»æ(²ØR-”C;vs
+ŒÃ°êÙ`¼RUôâøm ÓÅ;¥UÀµØÝ„ê•í±N²eQŠ]/g™'¾:ìIÉ f…`õ=NŽáHºÚÓ1+¢)BYÃE1Ž‡7ä FÂí:]v³€tj£¹`*Ý=ÊžeNÜißFQ”x¥X:¬„ùƒP
+P6¸ †9áÙÒªÙcUÅ™ ³e’(÷¾¸âµÆÌPà6°—,ËÓ¯éÚŸlXÑõ%ÖY~~¬IQ2qs’¼&MT ÄLƒgi5‹ÎccBïí0—Sôom}þD€eOq¬옞¸§E÷¸ºˆ¯4® ú
+[lšG°“h+¶¹*2GNÈ\mSêŒ bÊ˱ȱò!è ¥ôuq\f 0«š%‡Ô¹?=rK€µ¬g´
+¬(º
+£ùÝl½HƒAhº“ÚMÐ
+ 8ŒÏÃÖ,ÖRìV³"Ãlâ8|  Õî¾.«E>Ž,åᔬO5uÇÊQ¨Ë9(ÊøXäï” ³“wãÔÛ‡3 •„lhÄ ±‡Stµäú˜â—¾’®Ø,”Ää×ÓYö¢‚ŠGxj ¬)FLZ×ËI»AèÃ=¦=B ×JŽÝÔšððŒÐK1)Á ‰n’¾ÙŽ.Áƽ§&ÎC •îéî?ì0K”ì²­°Tw2BL8[~oæ›’Œ~þrŠ<´W´¸2‚”Òÿ(ºl ÷¹½±Âߌ—KŽ$9DO0wÈô—¸®íÜ¢¶Õ÷ßö3Jrg ˜ºs,äi43†Ãum…©ÄÍ&Ã&HŽHþÙ lK™¸ÚµGÜ-PÇŒq}|`Yä @øÁHâhÏ‘à3"äa[ÄÚÕgíDío,?ŠT ý÷U»0Bß^3|‡hS!ÔñÏâú¯ÿ À|¹*b݈~ïÄð=HŸk*<g½ˆ¨*œ¡gÝW üsã/ʘÉrc
+ý/†J;/ ¨å…EcN
+ÌÜ|¢ÅsŸ¿??Ò÷žº%Àêä†e^éÊO)¦éóLXD—ñOŠ[j
+Â4Þšÿ~hñ‘ÞÉï„9ÕOó0:“,]-‡ì¨6º§4Q6„K ¡e=«
+Ë%—¡C¸áiUbFXA©à:Ë© 5„ÞòZÈuK|×63»ŒÌ¢maX ΢Rˆý¥Å²Cï(ìüt kŠTDñ¢_Ë 7!J4Ä` a1q½IgSZxðWηC Œ<–nµrÖ82ÎÔìSj?§e^CË{ãáišÌzÌ€€ªpA6(;9ª^À
+ÆÉd9
+v«­ïg@ÌM?;¦6v<ﶟ%§¶iFÃeæ[%•è¶¹€ñô¤h’Ï`!šFuvGx;M> œA]C„Ñ]rÖ™Þ;O°æ($­ÅŠEÚ²
+
+Ö"}N›¤ç}|Ý.ŽŠñ&™…!íi§
+B:>öŠ¸Èþ…Õ<…Í?E\ìú×CEzûDØçÅÂR
+ª¾`G½r]QÓ¹sª^Â(ªNEëÔ™J~Š‰ä‡5d>¯vÜéúó“ÉÙ–¼UÞ;£xüÏÆ +c0W”Ìô¾`sS nJ¿#  _Úl¿h+%¼s9¹VAQQ_£‹¾5 Év5B?–hï½æ(“¾9‡•Ãúîa/áö
+„ŽD-x–Q©þ
+e¬ñ1¤ hÊŽIb/%
+’@²Æœî?gHõ¥ÉX:U°HoÑVbx™]v1wÄTæ±À«#zv
+æ…£ äV¸4úúzŽlŸg.ncétÓ ”ëƒ(Ð{
+ebPyÂ\çI˜eÛò.ñDa{-çÙêZ“‘Ìé’½CÁqÚÍxUØÉ0´öòч¸?9oò
+ÏaÐ4Ò0ä²vn¢Êâ À!‹ñÂÉ/»’ů\ÏüS k%Ã@=ØTNe¦’1)a‰@(,É“!Ìíª/ÿù/åe’$בÑôxYÌÃZ½¬[p[¼ÿ¶Ÿˆj)¤X’µÉš)0~àð¡±äÛß}+i\žE©J¹f©¸rÀLã5óZôù¦¨h-qÈŸõÆS%E¼âÉL4G!–¯wÅÔb÷D¦þ¨ äi¦h§œ&Ãæ¬&ªEßScMo2ï,Bq ƒLs 0¹Ö°½™çßJ6XŒŠ‡ÌÎ<çt%3Ñs=ΖöÃÙ¬a×¾\ÙH!Üí£'ZŸ"ÿ`w2gŸaÁ+9ÞKðJ<+h ’¤}Á±Íõ¦‚…Y{QŸêYÔdŹmXN¡„hVœΊƒl´ðe%øÑŠVn‰ó˜%O Þi ëUpIZ^â+*+½üÈ–/<Àaœ@¢WVÔ*ø*©
+Á[BEe‚Ш€rù ų (e$€Ráœ?™ÉØð%›d‹IŽZ SLX¾Å)Ê°Á$d›‡SNMr±F ãΦ©¾Vl8EÉËQ²¬ ÅÒ¾VdWñ-_êùÒ£œ‘èÚüCÒ±NßáŽðmŒC à˜æ;µ4œÁ(ëËJMå‰"=‰î!rìiJuŸ¢Råá‘óMUq/’P‹ƒÅQc"•´æ»\J,‡ù²¼Ç›s2ß¹ÈwKÆ
+±ât!—bC`ÄA‚'FÁHÅI_ú¦6<,Ë~•}S¿fᜒFºhêÎÞaF[ŠUø¼–àÀDx2ŠLJ³’.‘÷¥èÓ‹x:dÈÛGœСEØälÂ.)ÑòýŽ«â˜%+Þºï;5ä\èåÂ%›H4¦ÿôx2ãÆšsº Ÿ¤^êY;1=msGçäjY@ó:ΗGŽ(¿(ஓ˜^Î9°J¥;%HIÅÁâ4¸dsâ™p!fîpàSF=WðV6 írñ:OŽ‘—ýœ÷ãî2™8¾xA{¿)ù›[•©Í?þ €&X1êü/ý+ýÿ²"Üûbe¹z7èºB©1õКÅÛ°87%«fšÇ¤c» É®+ÑW÷fãÉ22vÀRóÑX)!ù9´Æ3êŽ7ËÀ˜HA­ï‰ýàç£ÃLY^v@÷õ‡ ·lÅF¿}¼)Z„ Ñ m5c "ÖÎÅÑœK+puHv˜jQ‚¤!8½œâ<
+³=™¡õŽWî¦ð@T
+iLõ”zç°¾ƒ¸ã)Úœíæ̯suø‚ØÖ0Ë2“};‡¨\õy ? ´€É:Þñ (ð/-Þ4°ø]÷§íXkHÚ{+/¬’UX6p—cÌ…º1,«™5îèöKEp„W$VÜÅWýYÂG1M|àÀ>nþn‘\#,'®s>¸mÖ<©õÖ@*Öí–NJ&±ž
+F—à@à·ZH;
+Íe›ïÒ¤ÍËD…¬îPÁ![AÐ6læ…f4™ªvVæïꥯòɸ‘Ýö¥Ä?ÅIm2·#02n™ °Æ1#Ü„ ÐdÃŒö& »¾”}Š6Ü°ÀË4iÐ[¢„L +'P‚Øb;ÿ>çðgòGU0™7ªÅ¥ .d*¤ê
+œÞ$kbñç
+òKCl ø» Ð;V]Wü‘ŠrÅ(̪Drd‘ÝEPp@Â2¾mÀ%e]{_Š>nEE°Ä@2cܵ¢Çç^‹>¿s§»zý|àN»’ÚíVºzaÜ
+ä­¬Go`¸/©‘cyl®ñ%þïP
+-]§q.3%@âçj%v_¼Aîç”ÊÔÈ|Íø€’
+&„e %Ætž®ÿY _%Š%nÏK‰Š-ÅÎðïõ¡XR†</!‚
+ðÙ‹ŒÊqôñÃb09áèQ£ :äà•ñ
+
+p<Äük·kñ)YgŒv¼!hl"¹Ï—‚𙚸sœ¡J±÷¢ÿ7GgŠœoá÷“yÒ³9KÄÊ…àÏ[ÎbbâyðÁ¿hè%¯QDë×
+r˜ vŸŠI쬭 ^j¬
+ísÜ*—"• °ô¢è‡î–\ÓP–‘m£¤IÖ{‘3 ~ÁC'ïN4æ`XÂ4Åí)ÁW¢
+ð?ÊË&¹–[‡Á+ȼ‚Sú¥¤q²“LíýOßJrùv«ãó’T*±qÕ  D
+»`<yŸê€þr’æ^è}Ð2ƒÈãƒÞOÒ^Gè¹@<’RЪ$ó9¼üÖO ?
+±ÑŤ "22q¸ùÜVæ–Â,±Ò’T=­6há
+Â7°qŠÕÞ:²;AšÕ"ÔÌM‰zdÜW`§PF¡vCˆeVýãÛpqØèˆ>Uhçð¥&Ô‡sÈÜŒ«$¡­`nm,GŸøò²,• ‘ìUdO¬…¬­žÜoöB–)«JªÚm?S¬±Y0Ï!´½_¦œƒ¯÷MJQf¼VPÜ51î \\nï¾Bf‰/õ;sé‚ Œÿ ´‹.§nò*\³‚+v7/¬
+D ëXa!³ ¡TT`M⇠2ÃÖ÷zäžcps‰D¤Ôñ}‡—ŸÉxúŽ­šË&Ùåm™±eÊÁöm”NtlÊntîžÉ’IŒÎgÊ›»2…pñt)«/~‰´Säì]ÐI9ùšäwÉåAïWqAâUÆÉ0zŒž¯1dØíÜ¡åy}€T AmÉŸ½ÇÎá‰ÈÊPKùÔ‹+d «TS±Y=ÎAÙX‚§h]FñY1’KL†Ù àvm,b-Æ@[ÒdûW 6Qæ:Ax#æF=Rû8Dd$0Ù ²|„0`Ø 1Ûq¬/ý÷h}ûŽ$Œ­®:¾sõFHl]‰Az‚cÒ®<+©[Ñ€Í5;
+/¥Ó1E•/¬Œ):U'š¢dàò „Û*–@4ëRù1òTÈŒÃGðúÙ‹ÏÆ+ß•*µ<éÂì9ƒ¸¬uÊ–„›šBAa•C°d#È,$b[C‰5®ô((R‰³rú^œ´®í`
+w ÙL×|^¢‡´žw/¨Õ0MÎ.ª6(7†¯d÷F?££á•ØßúÑ•Nm¿¹ ãîH*½¨Çp»@\&6ô¶/CÍz×È>ZI[TÄ×õ(ì€.óï>ÃòI@ ô¸–•Yô?Ê„¸áÇkÙ@‹6žXY¤èZœ_j¸š¦/”ºc uŠ—}ðòG×? ¦:wu\¶œüSUÙNþË’wô'ò7zVê|w'ëêOÅr0Çùl”h†E/ßÝéKW%'Rã“õTònŽ¢ÆÈVðµx:FÄâ›øš}ß„âETwœä4ÄƲ$eɨ¦› VxÊdg÷•Tq™=Ô–3’ˆ?¬zkЉ€Yf)Ǭn#¥s3ê7>ÎWE2*¡e¬/ý÷Tj~ùæ owDnw@û¹Ý}M2Š¢9¿X¬¸é£þ\úÓAƒ@lëý;Æñ¬ŽB§¢üš
+IœtThå</I¹
+ÉDôÈ1¡2ÕWä³Q¹©‡ó9•T%…d@F[‚LÑ‘QªH²tÙ6%éM?o.€:ÂÉŠû1æ;ÛÑ&Ûñ2„L÷_;ei}¨q.5¤x 6iè#R ˜¥98¢ž(‡b;†‰Ÿ+/Öå®ÎÆ6°júû÷_Ð!»Šô6ʵY¾ ’ÕÍD^&¿8/N ˜Iü¸¡ó¹½ÈoäÜ*YܾǦH½˜â%$
+?‘›?4Kë“ŒJ ˆ@Ò.´M6ŒwÚúb‡/B&®ä†·Ìâca]—>"þ†U¯à s*N†<œ2T)È› «,¾£Ý.ñ‘pŠ¤dÉϘ֖ÝÚ6HÄì|9ó!K›Õä V»`“_PzÉͧƒY0Gmµe];2í1ªo‚H…/˜1/²S'„ÔÈa+ )¡CGâsöJÞ1ćJÞ`æŒ{ð<~„®ñ®QþÐ N ‘6 yÑ’B[J¼LâK BB*IÏÆÎ|ËhV/aom™¢ª«ûZ :RÌyÌÔIÈ`É XS5‰Õ™†MíŠ9â0[ÁtÇìë™ÍþÒÒ¿÷Ú1¶Œ€/“Íúyí=‚p ¢+™¯ø„ÿÊD5xg+ݨF9#CîëW¢ÏS;4„D´žt¯rË·N‹/$ýrçÛù’X•Uÿ8¤…!,î óSüù¨­g•úvLÅ
+$*™Káø2(¸kCX›Cᓉ³¾ºÎÑdþD u_<Ê«™Þø½ÒžŽñbNsp†²åÐu
+4,C(í]´ ŸCäÿ(/“ìÊnˆ® öàè°oÆ9Õ.<UíZ7@PÇù¾SU5±¿!>DCÑ„ÁóÀÜÔ5ߧ«¨LW1d¤Òñz^ƒ6áñúñz&¼º6‰s>­hË‘Œ œÓþiQ ‡ÕBÆòHx\Ÿø“a¬$Jß Íw~Å£F‰R]ò
+¬ø±n(øœF)5U`ÀØ™Há9ôCŠ·'WB`Á-6÷Ã
+Ʀòé$ÞR W%O –uõ7ÓúÃÔ}“tÅ'\Ø^‡_·ã
+jè6AøÝ,;s@ñIrä9T–hôeD>ü&gÆç4Ò,ƒž’1¾å2)w0Z5¹°$Xoþ v‚cñêðR>ªÛÙ÷J"Y”:*à÷Q,Œ‘3B3ä`jÁÊ)û""˜|g;ˆe ËfT¼˜’ ¿?-ê@…Za ˆŠ°Qà?¯âS(Rüs?[¡l À.§¢Ñ”–¤qûšô½¸K[HÂtHtDGMÏõžƒñÊZÃÎÁDÌʬ*™ÃÝ5?°ç\rL%1—“j^K$,ÒJ†YÞœÓäªÈ§äCOƒ³iO;”oFTx„­$„~áiÂJÓöOñ(‹Ÿ“?]ÉC‰ 9ŸœÛ
+ƒ…ˆ¿rü¢‘øŽ(2Å;÷7Z‹~À\\†>¨ÈpØìñ®t•¿) áGÇ<ûŠàpá|-z%<R³ÙÇ|F°ÙTž²o~ÅY£K|~…c¹!’1ØeŒ×O‹Dg
+{ù9üþ)gˆþlj¶Ð9+‚åW&ðÂKZX­$µ†RÓgõ”T-ȾnC†>ÅÆ`µéëôQAú\*ˆ™×Õ¥‘sèÙþt¸O`E úù˜±sW»%Ю¹ø½ó!pþ
+(UœPÞœ+Ö…îVFj]!( kjŸÊÍ.yÍ"¨ÔƒPÔÉ`kÇ
+3•«_†•£Üg¤ƒåƒ3½YÉ‹ ^(J“ÖÃï\xãFñ'å G2¶ÙZÎa‰*oE hÎ(,¢áòƒ¬@çd–tĸJ½Y‰ˆIXüº½*Iª>¾îÈ“",Í¥üƒXìÁuŸÚx§Ø ØRLå øþ€á_ߦg*ÚâÆ—úëVðàœj3¸Sa‘á€g¿äƒD"ǘÕÝ d
+àŽi•#Eá‡ÇCô¼¬„X³Ø¸ë¾qI}2hXt9âa8TCVéê5!ˆÈZåA~&Øê|OÕB‚07£1/1¡ãGÑǤžÓùT)–ò]¢øµÀûÌa1
+7îb}½VÇO•ú¼º¿Ù°"S,jï¬ÖbóÁJñ‘u óƒQ £ú_É ¥‹Ô×dlòÏmŠüÎ1ðÄÅöÌ»~.
+NU`E=V>,ÉÛšaé=( ©éµèóMžGÖxÃ2 ‹²º”ööáÊ–µÒb¿W›YÞ¯æøœ™![¥ â‰Œi3 œêÅq‘å^4,»]fÿòÈRûÀ>wõØ+&Yv&¼Ã¶IÁ¥  Ê¦­²‚’crD%~½ûãÒÐf?{~VÛ‡øÕsì&nï÷2™]ã$ðt÷¥ 媢õÖ`ÖˆVCžö ŠO§f>z¥@Ë7µ.R¿7%ʨ ÂŒý:û"à`üÃ}½)‚ÙE¹Z¬~ ŽÂA®xÚ›hkÊYþáìo¶
+•Û)8 öPQ|Bæ¦ÿ$³ J ,×É5Hs§ÅЧ( Öòï@-ØYÆq”‘€DõGó&£ß’šPƒZ• õ×<âÄ'|n%0}k´ðœi.o1<áõ\8)j#ˆ³V1´I;vAmÓ”yÍ¿€°•
+Á °„gäJ4s€½üU©Ù!øFôOa§(¡]Éç©À™°2ƒteÇ`®·Sîç¦,ùÏÄàw}SòHG¢ìåc¢PFÂÐÿ¼æ±}ÑÇ~PôBÑj½ÖjѪLý_ßð@Qä‘0 üö?ÊË$GŽˆ¢'Ðê ÎúµÔ-z[ºÿVïƒ U'jÁ†!—ÃL2âDzqË©è%'©¤Bõ}ʘ‰À6’‡jSáŸV¢/3üæò¥@¦&á@…a+ÙªaÙL¹ûö°x=@D¤M릇)âË©yôC¿Kõt„
+=ðŒ6¡HÝ#$nËJÇÂb~Šò<¸¿ºn|‚ë¡Vv ]®\†L<ÎâõD8ˆ¹¾Ät+Þš¿ G&/o§/¡dòÆuÂÐL~ŒÑLFA;U1ÌÁµÛ6FS ‚È2Ý*éS$£ÁÁEæ!ù¬V É¿Ô8몛î61Q{LŒpdí¹_q¹•Ò,X¡:—d"7>Äû—®E/²mbBP\ÀÉ:”ÍHc! ºÕ>ÌÚ qA7øU$SÉ¢ù Ò(°9
+r3¿ÍºŒ'ƒÄ<`8"ží Xd[†bNÞW¹Ø" QÛ¸v>Û+Dq½*yñ«¨uMÉø ‚ÒÓLcK#C W>îØ &EØ @§Û´Þ#Þ bD)*It Qaövtܧ’¢„¥Æ^,&œÎö éPó<×(!€-¶£¯íÎ2~üâ¤_ìE,0Qg¬íŽš­ v2"‚~Á£%oqb
+älmu¯L¹,mèŒgm7¶™P¹AóšhNü$#XÃ\: T·‹¹œ#”õ­às1 h¡›Œ„ßó¾Ä=hƒ)tQaþt°¯±1,h¢£wö¬]Û"_3nJ W؈Óæ¬k $aN¡‹uDWºâÈtž€Ã¢t[/±’‚%-"Šý>ÓB‚ÆÂg1`¬;Þ”¬"è“?#j}¬sŒm†ì)ajš ´Ä5Ìe›£˜¶:¯
+ÆË£jqPˆˆ+±8=Š8Ø1íâü¡œd«çDœMYYDA_»€Á5H ¦À)Ÿy0b >ë^»Y%Á½ÞÒalCiæ%Ù)¨ÓÚ¶ºLÚÂ׫ŒÆ©È"«k¦ˆç_º±[Q/$|Ø1ÚâoÄ6»I† N)k£¦JÐ jÜqNœxV9*o^¼¥³uÑk’¼§(‡?NŠzñd<íŒ uNFG'oG?«¼2}òÏç$(¿VüxˆÐàÚ—¸hy™+hÛ¶–¬š;2E»Ú–± D–»š¶ƒÆÞGýÌURûd7¡¨xLç’ÐÉ£]TÆmÞ—¸àDžyI‡¢ç*ʉ£ÔÜöƒs¹+\FXdB®àŠ¬G
+þ.9nfɆã/lí`X Ïž,›4}Žésëhoo´Ì³c
+$[CMyo]V!®¹ŸÅæ0 á´õã<¿”,äà&šö"=Ã#YwÖÞßî¸gEîÜ-3ñÈÆÍä‘i7Ù èwÛ x6{QͬöªpÞãì0é&9gðÔ9oJ^œê;†–…‡²¡²
+§t3
+_šUÁÄ‹€5?!Í›“Ê›ÈC†’0EÏïŒWy6>~þàûoCŠ°è'ï÷ÛŠàþGV‘xr(RI‘™á„šmù¢à) +A£BaÏn¶P€*B‚J,”Q ¨ hѬŸö:ˆÇ*䱎aáâÆJ¸SY,ìHÙ¯r.]·­S"Câ¿MÌènt‡ïÀ퇢_7EPs•µ'Y·)"¡"z…åÌÊÂ
+2”&ê%Ü^Ô¬èé#v`Jî ’I•`yÆÀõ´´»ówFü°/)àâŸÐ™ès€B†‰)ó¶cø/ë6e?nFõ‘‹#‚£ƒ‡3³05ǃYèÀV‹qpqGGdij.«¬óòÞS$9xIoDÞ{ß&O'_*R<µÉØBü·-PE-x<9q×aîNE¯%Œ~ÒnÑüûs`¦«}´V“¿]¡ÈAÜPeÇv4…ÃÑ‚©ôª’>8WË!*Xó`ˆu°å¨¾/ÆH7z úB
+$žIÁ+lºÆY$<ËÐ`Γ)q!âÑ,\Ž§s„x(ÚÝî B5| ¼Ÿ`!{Ùèæ,ÅàñFP›®ûaì*áÀYÈ®ú…xM°·)^'O eUbs¾p¢Üa\%¶Wœ4-v2(ÑÊ*†­ú©„î—H³ U«~ið7ka8ñœ±Á©¦†«ñt>„(óÒdkÚYþ˜È þSbå*ª2 #·/ÁÃñcwDàhΟ·E'ùF<ƒU²€açðƒHÙH÷à[mÃÉDƒ›ðét(YàÏ Kòé¡F Ž˜—üé´qï$ŒYÀŒáàt0
+L>àá¼ã ,¦`]J9‰ËEytúïÌÅÉMd ,rOlrämpå ÿýƒGÍ–.&…™a\×þbJD·ç7~Àh·ý¹¯Îe„kê–œ3\)Øf\"™õZóG€
+H‰Œ—A’\7DOà;ôæ$¸Ö,} E̪uÿí<ü
+u}–Ý
+Ëí.g‘ H$ÆÇ´ê¥M{wi5Ü>þþ«F½†›ŽhRKoßi»†h”(ÞŠÇÙÕtôÖŠ4ñÔ®>J3ñ!îy›\Å{Q Ž?bNçôË\F˜¨qÝôy=ßö?`åÒâF५¬˜FcE±Ò?~1}¨ñ?¼L‰^kQ]ˆ6šùèeä]‰¨%F­î7„ÀŠ¢díã缨ö<SKµØçxµUëêó뽚òm a#x½ñü¶biÒù,´5õdÝÔ›©…Óßœ|ãMfo‚&+xTÔ7¢È;xZ÷crB¨;føùR¨}Õ H/)k
+<û8E£WÐÉéhC¯Bí†õÚ`ÔNàkv’^Í+4¥ON9VHÀ ]œíX«Ⱥê¥ä‡c^ysæ•ÇG½Ðø›—Vx$øßêG¶]ïä†ö¬>¼µ¯¤ÞÁÊëì;@O´¦èE%Sê†gý}=5¬£*­‰ðTAɾ BVšD­1Œb×óuß‹Ô]Âu*]‹—´:¸³ö ŠK;Má‚ôµõºAV„’5q©þ—ôN éÝ}•2œÇX³}R+•¥BÛú&q…çó½Œã
+$4ºRÚõ(4ÕÐ^ T÷kTiÑ;$ŒØF©x²TY€¤½¡ôí¹Ù”(ýÎ÷ãõ–]Ä@ëu%’/Ð8Þ)eŸ! %­ÔqCP #Ó5ó¿ S®¶ÖW4í[N>@§ús]±|}©®ï˜ô Ž!¼òDŸ¥%𱋙Ö* Tù=¤S[ p²ãÙÚ]V-­*¿º¾Êá†ðüà Yåh
+›25Žç@X 8²Ëª<Ì4?ðúq&À1aÖ¡ðÏï01 +ÿ* "èjŸLJn7†} GÚ$í Ô_ZR•L§¤{ÜO¹üª0!f®„Õ;ʸG°6ǵ,Òæ<-áà4¼íáÉä$rF¨Ä<'*Ûèjk{¾öž*6¤{ŽòŽŒ—FNhk<@ò*½è„¢´Þ³üq äÈ’v)£/HÊ1Ü#½& ¼»(À–l·*¥„ÑFc¶Ç T²ë+²Y¦}{bè³–#—.#û®b¼§hs0I²ÒÒ Ê2&¤× Î‡ÛîVz}ð$Æç½h¯Þ˜ð昀xÙˆÐdžS3»hj¾wAît …R$óÀ È*gÞJ:k19žó¨ø3šsN¯z0ð™Ÿ9>QýÏRýüNÏd{•Ù^¡áõ1+ŠV?ûuÂEàžÜÄw¡•á%Á
+Ü:mñÄËȘ `òùw¢%ûöÒ<wàYyf&d©ÎªtwñU Òˆ÷2“[~¿ŠÔ©î±ƒbœ7R{‹½;gHÐÔ=(³ßÁÑšï©õ"P¡8t²ßb6"ÿÆøåU5Ý¢ÒŒgß©¡àöÀº1ii­a *¹ËžßÉI3º¬›pùµVŒ¡/Ù¡NÆÄã;³‰O/Uÿ<‚ðßt$9ÏÉ«ó*ÊT«½½á\2_dÝ'DÉqOÞêÊ´8çÎé·z¥¹µÌV‰í’i+΀'4ÂÌ°cée0#ÉÇ6Òè›;¨>5ù)”/­É6i’©Çs`®‚«ÓnÅ Ä^ºŽÅaì!U¢.µì ‘=ž› ‚¹Ê
+AWl fk;rÔÙ½àÌf Œåz§·¢#¿³{áÙužœé SR»¯úçÖœ¦®¥\¨)b²'
+ þF×›h”¯dšÐõ IIsêZÖ•¯jø"T®ÝW!ä öÈ>Ú㕹@&Ç ƒ’Î-Òã´zÛzœŠ¦ßZW ÜbÅd¯[ËòC‹S2&û¿ Y±ó*ƒ…)oýåu
+ŸhŸÜs’ÑÆ!Ò@e¿,Û›ã Øo‡@¸
+Rn­Eœ6D Ô—”òë2—‡M#œkP•ÓÆ
+¤Pß–‚ÆBzÚ{3É ý w9½ «žÔ oKOqÔ›s^JþŒæAÓ«|fçAägŽO\ÿ³T?¿Ó4Ù_e÷J=($~÷ë„!*l
+8°”U–H‚‘©Â3ú R'{Ëáótž¡ÜLZÙó€x#ƒ D”‰BoM2ÝŒy
+„šŽÍ ö½®!»åS EÞz´RÐÑ–{>BÁÑöAF,3ʧ‚ |[ cê¹çÓrϧèžO`kà‰–Q>ÕÃ9€ÿ¯‰ ó©
+Ão¥7È÷|ª607ð•6hF¹2`Ëg>ÕXQ  ø†‹|OW”OLô~m3Χvv{ V¼¤EùÔVFsÝéßQ> 7ïžO¿\Ïãƒ+®a”Ç%'…¡îñâVÛâÄ–Ðä¢>‹[ÒQ7ð9×sÛæ‹¿‡KÚIð$dËŸpŠÔÊ°E…ü¢¯®›t
+ d¦\TòñrgC@y„A`XF†<Å¡\,HÃ4hºÎ«ª®FƒÑç bGNËU?Ü'õeçÚpë}bˈmssôTNœ>43iÙPCàJpüB)þ1š ôºªð¾ FÄ(+opÎÏÒÃd qF =ãMÓmiµð8º£A°3 ªƒŠ/ÃE4flýª)Y£æˆ‡Sñ0¾a!qlg«´Î|5+|)™TŠi‡ˆ[ <7=Aè\fÐ9ë\„¢áþÄmn”V±¡à”¶Î«eÝ›ŠWݘ€‡„I8¬_T”1·î¯nM
+ö¥Šu(òÏHTïG+…FÁ} Vk±QÒH ( Ÿ3rx;&¯J˜Kã$X¾†EeèÊ<úbÛKoÙC÷@˜dˆB*/Òç'©é¥`a#"’ésnžwÊ\¸ßÂ!9µ`R×Hš™dˆáÒDúâ-²˜“;èÏÑ ¾Q‚Š›2]É¥‹‚Y 3H1»7¼ámòn<ª´éì]Ò¡Ãvæ@†T³(µ¼Ö®lyŽÌÎø1dø$õR¯µ£ˆ1£t9͉¡¦œÌé˜a;os•í–ÃçýoΡ×[,Å\_}@lª<%¾öغŠØè[÷›NèD±}FÙ^ò®i\ïp%.7•Žn>=& GPö¹÷È'¯úK–¶K²$/ ÐÜ4
+ûò×0Œ
+¿„X ã%ùyåOœ+«§“¼X£®lÄÃÞé T,9ò›tˆÈÇÚ3üÎKÁ9üÐe!寥‘¿ååÙV˜ Ã5Œ%;1¯¥a…`ŒÑa.»ªñ=¨]!0bZ®Ù‘àIÑ5;ª3/Ò
+•¬²_õ)B£ PvŽœºQDo'¬gA
+b4˜¼¿æ’¶~À@D(y×›ul+|¿<è&é(V†oçj#d†ª ëâÃu€(¯­ À¢N‹•÷L #HeSoJ›8;zŸòÎ*e*ˆT0QnJh€ž³†}§÷=;ù¢ ÄücËð¶L¹î¢ÏdżмF4?f«zRdƒ™°´ |š³ùÞ½›¦Ôú@‚LÊqèê*aX˜¬ «gÝì‰ê@¢éíPr$§÷¢ß7E4¶Â”,Nø}SRL?ÿ/˜¢¸¼+ˆ‹~ .Ñåd¼­¤Ê$t8º” W¦Wç‰Cv$“Až¤{ÍPK ü¶L–—ÇØá“܉ƒ
+ÌÐÞ§÷f0qZ@›CÙ”QÄ “Rë;TFK¢  [¦Æ¥ÊC‡£É§RF»èÛ"Õ÷’µÓ-”© ñnIÚD…hçØëäÂxÓÛ(0^;ñ(>Oí: MçÁ
++t~æ›’÷ t-:á½!D t1OµÞ Å©è2ƒ§íþ¢èŽãõ^´8¦>žà½7ïñ Œú ”Æ1§¢—Ød%è^5Ÿ´BCxÌa‰„“7 byRÂæÒ󙞩ÈnWS)Š¬ñLhÛŽßδÖ!É«`÷‡'AI,(«"Ç9d:°Ó^Âÿ#/‹òC,R`pZ´ã-R<ùü}¨‚-'`ä’1»/fRm󲼙ɨr¢îwÉ‘ÞWnïÄÙŠ¦ðp6
+¸w[††!‡˜PîæšØ”!œ,® 0ïÛY9º`òxEªXÚwŠ3Êêb™¯Ó†"gë暬„0ËŸ¹I¯Š`AThÉãTܘk}}ou)zp¹ã1'>i¥DË+¸jÌlçïGª¤™¼ypÍ¡SS&D^Á·âW'6Úr[ÉAˆ !ÅM€­n/Ά|.R®ä8ÎIØ£-°#@*å¸N}¨õp)>!º³Ézº¦Àd$WY,mÒÝýJÐÀQ–îË&Š(“Ðîú|šÛ †a¢BùÂ:–‰¿¸ïS 9, žAê>×ðV!K”K>}ß)0
+ŒÐf"I^ë@k@¯¼3m­2Å,̺ßLxöUئaL÷ûDlû(ÉAƒÖ ÜÇj9„£ÑÃ'%;1 ˜¬Fñ|$OH¢;m  wª{#è
+ºcbž³éýª+\æA,šØ5ëL`A9̘WñÒŒó<´œôø4o"(Z¡»b(*ݬCZêj”4l›Ž’”-j]$‡3ÇÑTÝÝŒ
+¸Ú2˜S!™tµ‚Ž¶D™½›
+aÛ­T¤Žµ]˜÷€WÈr™6g™fÊð Ê_õ©ÌˆÏJ^B˜)!' Ç.ÿmN)Â`²B²)Lƒ'èÒ´_ë>mt&›cÙÆÁðavÈT]8Y.¢¸k?$ŒR 0¥4'ä?VС…‡ì1d÷œk^(Z;W.E€5üRP1îšö&HkO`b
+aIá o[!\
+È€T‡÷Í.e(¦ª(R;¢5Ó9‡¼#ÃÙTŠ­VYgØNcÐÕ) ýÎyù¿YìË‚h|‚ðaäkKåmRKåÂÔ±
+f°¡ºø£þöíÈ…<
+ã$!>±äò¡þÇ*éô4\!ìªÛY×=¹°_…=«zâÉùê”÷sßâZrþ`Å‘i×£+ƒ„`A\GåžX2 &œagþŸŸX²t³d‚øLמ t¹–̪ª©Ì]\[]KfQÅjûE‰çé7KfUå‘a¨öÑ·ä?Ñ;ô~%=)ÖEÿÇ“‹¶îÄš[ûKf[5ÎåÀ<ŠÛ‘T5*ÖÍÅ·dvŒýCïÚWKfÇ!P€®z–ÌvÕ‰bßÒ]K¾‡8–ìÝ,ùäõ"FP¥eæáZ2/¯Ò–I—Ÿéº:ò½¥oŽÌb ¨ò{¨ÿÑI½ºß™£!Y(ÈWò¹Î<*Ê#¦ëÈn¸båÊõ™%Wf©U
+Ãõä‚Ñþƒ¨v&æâ¥Ì8ëMDèÒ¬NÄúó
+“ rᘔ5»kÉÈ ­OºJÄ žX2ÝEðL¡×’+^ªí–'…|’óhÉ·ô<™Æ` ˜À¿иzreA9S¯æz2ïZ¯æuÙ“ÉŸ…T’‹¦Ó:µf@@ð³[‡ÄT¨Kljzë5ZD ¼ï]!4†Ý¨y=€Y:¿¥Y,„zRKzµÆ ª8-äÆL£©V Ò*‚Ç̳U°Åˆ>ð»eÚ9§btÁê æOcݹ؄,\cÉÐvÑ¡nÇÄ
+bD´¨dᎃB³¨X.›ÐÛÚehŽÐß1Ùn%%£ä#\-äر„GA½¢VÈgísôtĉƒ‹Ã?cŸ|zl]ç .CN÷X©ˉhš#^ëCôSÑ”ŒyŒ–A§U£G!\?f&
+=1&Ã’Fo);!«ý°*!C­üs4ø±Êªø|Ú—‰’Cà UYÀ¨­¶š@ó·aÒö§¦ü¸jŠãŠ¡¯Ï„ÈuSѱû€
+¡­,êá—S(ÅB-LM¼º ô€j—õ¦Ä"Êí§`gìËÐ`à¤+Ú1Ȭþƒòªw€' 8ô#Ôå<yòônâv“‰kÐד þÀI²îgéËìcÒut_2Ñl
+
+±EáÀÊ»gÄváƒ-÷ˆ:bÍöÄ!¾¹‹Œ×lËÄŬWŠô!C(Ú¢ì« ‡A¶¦¡±Ñ
+rh÷Œ)Íð€þ×Ô ¡É&€ÁìðóéÅTº‡MyhåZŸÞÇ®A_/Üè“÷…÷_ÿûöËÿx$6cÏâ÷a+û#"  ‘?ͦÿÿùFAE:Ó8º@D…Œ•âm+NPƒÇ¨p
+½¨> Èè2v¨èå • «jÄÑ-èþ¹ƒX™9j¹>Ççë”ïÊb_ º¤àë•<)ûñýwB3­ÄDjÔ§©­~"YÇ$AÛf)MÈ"—@ù ?/ÈBXfFB'›IŠB ’ g1›&øÉiNŠQPE´É, Qïd+'ídÝ‚>óúTÓ(iç61aÝÁP(ZŸ½´ ™6)„QÓ‚#UÝŸâT € EK¬  ÞÄÜYæ"bC"´€þMy™äF’+Aôu‡‚'\׶"ÝûŸqL(kÑ@WÊ‚Nú`fÞ&³Âô;=C±]e•$× 1ÙIV
+„ÖFùú®d¯sLsæZiǨÇñ&úÉ Ž<!üG5™k-«‚ l˜8fÀ÷m¸>¬Ê—Ôh…2£Wf,HÕÍdyY(eîFŸÆ™(”‡.$
+F曄À.K!Ÿ¡Ç
+‡Ge÷v7Ç‚y`®kuœ#gc›‘.A òª¿g>,'·óDìKœ²§æ°´‰›97ŠãóÕá¶ÉkuMÁ¢Êv
+ƒDE³…µI}#=š nxýá‘¡ºÑnhÊûb­Âø‡1xËæ˜h¬•É$…rŽ’m›{Z‚|­|µ­s8”ŠâRB_º
+]æ>ÄIMÕ#çŒw/VhÚ æh²%­{<@–k¥m©„½9‚ èç’×m`^Ê£j:}q„ÅeÞ€$þ)sç¶Õ^­Œ_áîžd7Ù<GTÿA;Ç´€*.éÍG&¸‰Ðìe® ýáK\÷ÑLT4Öu¶) 8'f¨·ï,K&"­Ñå6ÆSt- «‘¹±L-¹êqøMÚŸz£C2…»TÚj92lùÐŒ \
+ûÌ*VI2Œ{ “˜,ŒmVÆVdÈ·ñ¾¸K:…›ö/ „V»ùf@ˆÁâ–¾Dœ!ÊQËÌ_¢6±æ.Uk…ìRR”`V?Á<!®ã
+Rd<&Dä€ç×^63â,ùÈ}!¢î (y’1%eƒ¸0ÑÆz*g§cø÷
+ƒ¶Ú†p Ëž ×ÁDÐÑò•–…"YEúM4ïõÍ9­ ïº]ÝÚˆ!ÁUÕ‰uKck%‰›Q
+¨‚¹©3{4 ’‹Ã>§ÈŠ†J×û8³‹„GTý‡öqÑrÔ­,ˆì$Æ…Ë´ràà¿Î 8í‡ì³ýõçŒÕS «iþVf«—¥U®}Špï2ŸÎã£_·žÎ'›qá+Érl,V´”Ç J‰½~ð4’ÌåñR\É6"ž@“Z(!ÃŽ;¥E= 3kF׊ÀŸÊ›sÐ|2&¯:ÜX ;Å£44ÜN mÏ̘2:CMóÏžç}_ „CpŸ¡˜BΡtq7)Qp×®‡Ïw‹’öK6Îiz#.Û¸B±;¥.ªPÅi:,í<'(Ÿ„cf'݆N–˜­!NDûd#K]ÁqVI{ò¨R§ÀÑçäA[êPü Rú߉Úoôw†`9±PH1”ô÷¨ªÕæ¨%oÃ[>~‚nôÿõ‰j qKÃØRtùù:’„‡dpö4fûûzú ä¯Î¢c,–¹æø »c$Ö?aá-mÂüSi{6¸ØG„s°@·|}’¥‘ÌØØUqCvÓI—¿ ‡ ˆ\ŠN ,˜£?Ä<M
+«Ñ~úÕ'è銑õ†ºO¹ù@®Îb›Iyû¾«OˆZz0¤öÛ~ã"ðªUŠ¤³Ó¢?§ÜŒ‚ŠŽ=¢ý™Æ³á
+‚ä¢ò“S„y‚ŽÝT÷îoÃZƒ¬ûÉ)¨ýS8bîTK &:ÙåÃÛ;>>€žNáwÕšâf/ø9º WO†âûz
+W~ImŠ8Êç|}‚9èV†8ráq|«€OÐA·žá>=ÄíºŠˆ
+mÒ¤HJ=ŠŒN… ¦í‘ïò–™×Òˆ¬Ô~”7 8[‡ÆÙü(oö’£qÖ Ýwúflrô n«×dG}³—(¦hisøUÞìÅpËcñ¦zV7ãþ¨G©èEÛ‚rU7ãÑœH‚e⎲T`±.:âõ¼,´xæ鬒åÅêÀãÒ¾õäªníðŒ£ÉaÛ⛺O)h Ûµ¼ª[¬œ[fá e{ƒ«º¡‘¤.ÚÒö}oêvÝÕíìªnº²ãI¹§­8Wq+&«—”¢þr7X˜ É
+néGßP¦­wq³‘?ˆõâDóÎ 0ñí(n 0ÉŒUÞ~7@ .Ä~ÒÒQÜà[Î êɺWÜ«¶ ƒ¹«h°¼Ñ6Ѷ(©àjf’Úödö‡¶ wm;aLKr^YÔ—ƒÚö«hMm“Nª6"›¾½†Ö²Ã=>´íº/¸Q ðÊË&I΢Wé8øOpm/u‹Ùjî¿—Øá®5Q
+‡,uU6‚@fŠ^
+Ÿ
+Òç"8Ø1%À³;z‹ÊZÚ«é(OfAŠcª¹cMAE }屚‡¢†D¨Ùa¦™ïÊîíWÔµ¸ &\æ³Ó(ŽØÛ/Ó×!‡¿~x( vQƒ­CÏsØõ˜øÉ1Þd³ä;¹jˆ< sÅ9åÆ»JíÈŒcŽs{s+ÃPÎÑpC)Ô‰2¨íÊçΙcâl)½0. ïbb<–’¯oñT˜O\WúC÷õ¶L…Øç¢ô„ÌžM„99Ñ> ®Ã¬s÷õòƒÓÿ†þÓ¡v:ìg£½×S+ƒý›
+Â6e-j¤»©$?<bA•‘‚UGTzNÆHc#©Bc°‡ÈÐmŒˆ/ãím­¸þƒüœr¥É:êir¤zÓ7RçâˆÜê‰ô
+„¢ÉBô5¯ÝWAè-Ó¡ð„Ä6“Dk/Æ¡6îÝÔ³`šo‚hšÞpn|Ÿq+\Ô
+­Æ8c±uíò&%Š;™m^¿i-û¦XNKã̸h\ªùŠ]#![íôUIòžŒe¹²ùÅË_AÅ3TõÝ9ŒO‰ jw"*J,ìÀ|
+PV}iñòF£¸8 ooa.È“z¶VÂŒÞ@“âѵƈ¯7çàâávNKõ^¿²wI~bÁ+o#ñ/,šÖ“rUõŸ 7f~ùÑê_CevI|<\‚o«_¿6MFÑ»©| ‚€p‡fÕê÷õû ²C šFÀßB~´«WÊ1d1§¡¼¦Ú_j”Lgæã¤~8 Õ ÏIn︦Tà†ê§ÀÎ ›´â˜Cø¿«„,Vï ÿò˜ÿùÀ‡þ±2и"‹[3ó:*ÖEј ÿÒ#‚ä
+<Ã}BÁáÆ*Þgߥ߀‚#Ø´ì‚ù ¨ãéÛå¡—’D‘¯ƒdåñm±e¡áÈ
+< 1 ‡i¥µ°ý#NAšè*æi”é)RUEÚvq#’8¡ˆ´¢fmXLêigP~(
+^éqo/¶íB=¬¯u9RIi\pl?»!à0Ô‘Z`Ë!©gL©X@0þp4.ç¡dU3f¡I)n³ „Òv™Cy §Ï®½ÏHV_71aêExnÅi#86!CrU?åƒ?„¶÷Îa”‡¯ß@hÆA5±m=ŸPÿß\¸a©eiè{Ó-¡øûß ×ÍDlr½x=dBý4ôª¬¼ã@?Âýþ$'OýyÖ‹¹}ÂÛÕ:’7Я7 Ÿv­,L;™Ø+Qñú9æìy»@?Ãr¤Ø '{KþtñXÏR¾€~Ro=KþúhBm˜ÉÉ€ SÞ¡è°Ørosûiž ºmT)ƒ-m+„uU6¾8dò;, èÂߨ¡\w„V'“8§áF LåXýG9K‰÷@_“} ¢H0WsÄÁÆèZMáÜ)hqˆÈ]zÊž‘#ÒØýÀK L–}Â=ŠC…±¡OHÝ
+Hƈ1ãpLI6§ ¥ k ßd1iDje*DÒ¢zIFÙd(-ïsdBe]­¹ð58TÈ \&k&q³*þ„x(D‹* %õæ6^(ž*×ïPò"¸Ö Êg =o¸ ª7J@r…Ö2´Ïç
+%o1ã°EÆ"á,êÖY!¬éùÅ´Âés"®ºdô`#&î9®Ÿ…²±qÒÃÉï$ÕxÙõý ü–íæ ú®f<føºᶡf“g„çF”è×S¬& Í®ØÜÚTF…¸¿EÆ“ë29û98xušŸÒp<tbº É€g,EÓQÝw_ ÝÔQJ>{åHœÏ¶8› ‹é¡êµtèSÃÒÒ¸‚uÞHVI±Ó¸-»#€ÎrÐÙþíuo÷ñΓ?BaYmËzsNËÆ,Éê‡ Ð˜uoÎÆ•«fÇòY~ÈoÈ,ÈJz¨nt$”·
+òÉV@U»îËp£ð´&¯Ûg7† zŒ:N÷?ÆË$Ç †¢'Èr‚@#%­«—}‹
+d“ûTžs¯Cü‘@ub1«k6..a˜dì¢e+Û†L&š§¶Z³Ó–² ,—êŽSÂÓv¸ýlm¯C”å¹xÏ°³ö£ãRÉ´MÒ!y¢ðy®‘-žKΟU¾œ!sËxÛrOƒ€!¨<@Ôš#3f•ðyò¨
+»œEâ‡v¬zŽ£.%ò"® HI¦œÌ@‡.BûLt{æFI´.‚%×uÐ`ÉÞ0½úPì:ÐÁS—p›ã®ù^v–Š{Qäœê¬C[ðEÆüÃ:jÏ–é[Γ›¯Î:d¢…­„&×å¼a¤ýZ­óœÎuIŠœt¨'[Íiüqh4â`ŘL§z˜8@’‹æüttô¼ñAÉ#%.‘þÁÒN:G’¡)g"PÍ(m'zÝXÇöØ!h4q‡u2´/êm¯Ñi´µvN©ª
+?ÐYhL-g«7=T¤95¸ žÂè ñFo |/DÂi빿£ê¤”ˆéªrQÇW"%Šâ%8kâƒÇFï iT*WƇežLøò¤ˆ_®ôúuõÍÜŠ¾ÓoÐ’ŸG&©(‹Ú9ƒ¿rÛyªÞílßã„¥±ìÔîÄÁ™9G¸ÀŠ0
+ &¾•Â–fÅò1‘–å„É™DË‹
+é)ùª ô¢c(¿ØÑ‘‚lêmD0„°lq}a
+—B´F ë…–+Y„.ðÖ¸îXŽCD,¡Sæ©¢Íi.›[¸
+ä /º‡‡wÚF<Õ$n+øHN
+Ç5>@Þí;ˆ€ˆ‘C
+œ÷ÑÆ÷u }¶7¼q
+Œ£®_j9Ï/äÓ,ò®hdG§ m®ÍM¼Œ®ÞX/ÐfÞ3IŽ<ü½.³¥¹Iwéáy–醈¤ [šñX4:âRÎqWà–ø¯õ!
+ÒcZ¦ŒÝø
+Xù‡ÆIt™ì1/ήª™!}XL/QÏ:O _ÓôÁœ›ËºBÛ–ïÍ<˜ÄîÅóã‘©­Æ÷Œ÷aàpºÔÏowBYðӾåºNEƒ¤?\[ö"ñKÝø Eüºn…ú¢&`ÁsóNTòÂo ¿Ÿ‡>¶Ž—Ž¶BTò3³lë@‡âÂŽqæµ¥U ÿ¹ù
+Lþ;ê›Ø è^÷ŠÄ…3Ô‘îŒÆEñT  i[ŸEÆ9Â
+H‹¡IÔÆwBÐ) ‡›ö“´B—µ;Ñ’\½,M"„0,´–¯Mâ‘x¢r´nŸÖ¦ZÌ!‰IXk¥ãᲸŽG©b+L/U´í`þ4qŽ@ÂML¦ë ¸Àþ»’‘ã±Óh;Œ<å`}9ª f. s¨Øܺ•é—‚KxmËýØä·x
+ã4—\å Qƒçñ#O -Õ¡"ɉá°EÔܼV¯S¥&j˜6Ä(?ÿfôþeBS¶r‘Øjë'Ê2(ÄÆ{…༃˜‰E/d%¹M
+S¹°ŠMzPTÚSÁœNÉ ¡ jœ¶ûtõMÎ0<2ínX V4Dâ
+Šª´C
+¹4_GŽ žë,^—O…C~ÞCòNUW,ßf“-í­ÅNÕ@î)Sâ ‰rCÆlÎtY®“šÂÇáâØw5*÷™‚UŽU&+ˆŸÚJ^h›|Žƒ0Ñ>­ÇÁ]¹†x‰SV ¸Ó$O™š«˜8èfèEù 8x5*`øBÈÒ(ý­?þ
+Äv
+?Ü®ä~yO«QÐZ‚N”ÿÎL¡2£Z0&ó—ǘ½œeŠ|—`)(*·$®Æ
+c²ÚU# ™›<&Ýsä„z{F[ ÅPmÃ"»jÐB}uÀu`j˜|<˜´X O|8^¤¹rôEÁ¦Ð¸œ pb ‚ºY³›Û» Þ“iüºNgC
+D²εýèW_4jùWÕ³¯ï@çVˆòÕåö`r;h`ÙÊ+‘¬8„uoÉ2ÊôCŒi»}xXŒ&ŽÀW‘.þFæXʉ¬…5Á…Äwq_Ÿ`2û¾Ü ö×Ò(JâÌ¥e@)g¼Ær›&«Òž•L¥.‰?Ó¹®ºáä3ÅΣï_ƒ~±ÚÍ´`ÏãW½³ÿAÙÉB‚Ü|œÃ:Á Û‹S?ã2 µí­‰bÂ(Óbƒª%‰¶É¯^!’ EL´_Îÿ€46®:o±óSLFÎÝy«äŒâIR<eQ’´¼IdßNÙ" Ÿ½}›`´euj¼xdò‹Ú5ZÏ®V€¥ê®‹á1u–Œð‘M²EÒ£Ñ3› RD¶~ÈÀ¾­Å‹þ¹îœxoÏžàÿyüø¢}ÅF±Ò\÷¤‰ø3 ‹§æ‰9šGgÅQ¡»<,Cǹ3®%&ØIvÇJ
+Ädf¯ ˆ¸¬ ËwÊf!Îñœê!ӜݗŠäym’§˜r):qÝÂã
+EiÀ++Ô”*_W!”»+óªí¶±áê%
+à¶Ù¶¶`R±âпéÞ)åÂX(˜@ú¤„Æ™4E¦?Ÿ¾>ÒËçïO¿t@fŽÞZXý¢9¥±/ ÔË//"âá•JoC}èc;è$VG¤©"Hs°+Ün‚“d³1¼‰«€ñAPa¸ŒFBœ|ú”hí˜!ÈFÇ¡äÛCE(c²´ëñ°×mêÜþâ‚”‚ªf‹—ðéúHcé‹}žÁÙ(&½: tz³
+stˆÒ´{2N³`ì¨L© Üzˆ<4AtóÞ2ïß Ø\¸¿.¿œNAÎpôúâí;¤§4Üœ••âTBD*J<ÕIŸ„ýèšD7!¤Ux£¯Ñ .Ù  öfçà•ˆoòj¶õ”à-7KFš(ÝJŠ¨r›¹Ç){HŠXbÌ¿áâVt9ÖPª‰ÐÍöG‹Þ/àÏlé{_¾<ê^¯k2Ÿ,Ã5äxÉ'ÊÌý²"A É{·CÌýÊîˆ.yn=áC%,:ï–ͯ5úÝÚ’˜°0ŠŠ(&× •=ˆ
+€ô†ÊTTºY·‡rÅlp4׌
+1ä2‚É/bT¸îª“íó÷«Αï%0&†ž,<îEj,‰˜‘t›ü©è†EèË»äÓ/ša9YІSUI…ôvHôs æ
+H‰¬—_Žg7…WÀî R"ÁËUvÙð”LV
+§€LƒÜé
+¹»pErâ| EÃ¥lcèyã:΢1{-Òhº(ƒ)>©Ë¬} ×8ŒŒýÙjÐtmß›‘“ÁS‡Â@ç—aðÁN”–÷l•lýÝæÝØB`B¹ëfËãïôÆüù…Ñ5 É‘Ìnæãê$͇*±ÁšähŒæZÁûS·u ÏֶŅƫ‘ZÝŒ­¼i5Ão®ìo ÿÕ¿4ŽyO§o9"ˆÕÙŠÌxÚUCÚ «>°Ì}M¯ïžž²P]}M&¸
+rÑZw1í½Mh*ú
+Úd £Úè‡{B* ÷&ÙR4ª9x# ¼:Ý 3¤Í~14 šZ| q–عµñ¤NFŒ*ˆŒÖ2Î*°¾VéØât´ ‰~sð—;mž¾¡"ôÓ0é§A¿;HC,¨Ö–¡¿d;!GåiÔ'Æíê„lƒŠ‹#—Ú‡"ª¦ÿ£¯>üŠ- C’rµ
+ãë‚u{?ÃÊc’ky”ÌÄcҦǣÍV;ÍöH¢F{IüÄ!´5IÅäðC¤ ’h¤ÖëMÑ™µiH2$áØFA@#\x.æB¼ãQ¡k
+h–—Áþ¥*—Æå&¤)ŸÔóOWF€cšÀ¥Ô•²©§Ý¬vcãÔe¼õ8ÝÊ£„`€‰Êx'N Êǣǜ8Å@Ÿ¼5„^ȓɑγÁ/XðTm¹H*HQÊ£æmð?æ-ÍëHy16,IÝcÝ»^{‡!µùéÚ‰µê×ù”bGâÀÞB „²é9ô¶Ax÷ð<(Pp’ñ¥jÃÇì½ÏNh7½ÓTÝfkè #5á8ÈkUðU˧8†åKO%€–WI“äÖ².[ÄJ”ij^ †ëí쳃4)ê½-PÀÍd‡E]€Ì£êçÕ ¹„0@ ßj¦NC€b3žêÈÉ$>Q×zÏȉygÆÉóÖI¸/@¢û8‚à•å°E›8€YMŵ-Áìlùº<«ÓpA…¼–&,Æ’6,°òà÷.qyŒ¢˜Š!žRŒ´òà9²ä¡zœÈ§Þ*¼ü\ÏK)i()±)a
+^…'ÐCkßÉçwU=‹ÜIDB‡€Ú¤‡4ÂaÁ‚¬àZðLAÑͤœå´•ˆ ÀAârçïI›2U”fDqµfRÏ>ý|žU˜j€ªõ)@H\2šÂˆª°µ¬ç
+èŽCíNõ"èFkW+J'}µî¾Y¥Nã3×±>ÕõYˆ+"º²nQký~UÈÝ+´“#òôGÆ—„Œ1"0Á­×ý¤H°JY‚ ÔÈã±}Ô^ß.J©Xá°hAFÀ‘'/€vX—ˆH7âj)pèhµ
+YÕ'ª­Å~¯"æ6êO/á<j~ÊY¦d˜ÎÏ2´KRz;ï3(YLé©åè`} ­B0bKœÜVK@.J©LÖÕÛ:Ù¿ï&™â'y Ôþ²M]àöªfTjm­—71®¦ß: ŸKÑ rw_͉,QtŒ¿$úó¹£H„
+;µ,j©hécŒ´tâõo¨A+¯é“¨#GòhÌ üñ5U·È-ï¯üe^0®ëª-
+àMŸ…Ê€WОvÛö½¨¦-ÞêíuƵšb6›2€¹HôKä*£…Üø†Ïˆ÷Ü•x6ƒ“¾E}?ÁˆØE›
+†iUDsmT)û,@]|N{ÕAí³ÌÈýt)©<ùyÀïÆ>˜Ù³OÕ»y ñ؈/Á¦5Aˆ0*D€æ2Ú-ø2òË
+ƒË(Nb¯ó7JY6,„xlù%Éiʘ)”ÁGÏê„ð£^?‚j:|PJªÚ©äwXÚa*|òª>d3Ò¹c¨Žé&Úüîßß‚
+¦“a"w…ˆ‰}‚؈:ÓŠ”Ë2é²OÄ®)"Ú9ˆ®™@cë9híµ"¨
+]DÉÐDqËu:Ô œ«äø¯Íª;µ1ÖF·e„ôfg_MÆ`³qk§ ZJÑöØKw³BµñÒî£TñO¨„oÈ¡„Xî´?‚üžô7jÓµË
+’ðEÎÕ‡L»úP-A_ï}°IÑŒf°*¤×gÄÓÙh@;›þyï£Ð"ª:ê¤QçHWÜ8ád¼1ŒÍIL×Î
+'ëâ-™.ÔN
+‚|‘Qæàd¢è[ù-Œí¡lbîÜ¿¦Éþ&U¨YG€3z¥döacJÿ¹ð¯Ÿóî@zœ¦ªíËçP
+ØCY82Ü$²1 Í@Üð¤–zãgP¸Qy
+æ¼yÐ/x¸gÕÔn ÕŽ¨ë@’4£^J0"ñɘ7¢½#wFŒ÷AÊÈAtýD჌W¼/vMCe„œfÄJq"@Q#h“u"ÐSFlü}PUÌR’Íi ³L% b(þr[,e‰ßQˆþNrK¿»Ímæ¦hŸyìPN¤y2v “s´m‚^’•¦° cdÇ
+^_#jX¢ Á2Zq
+D‚¶Éõ(ªnU÷ñ5Âñåž¾¯øv-Ó,®Ñé-“%b+&,TTÑrÁܱÀ žÿá—[qÕ"³L0d™Ï"ï‰àÉ
+
+¼í7y:#Ö ×€—Q‰êÎ+’—NxC
+cÜu_þœŒVÌjF×xiÄ®±œ_ÏÿäÎ@y¹Àã¹I cqŽöðú€zwõùc8µ ™iN?‘:À¹)Q~ìψ“¸«+>>gNCŽp}` HM:… Ól¥¿Qgß?‹øó^«k¯ <n¯JÎñïº R4E 5‚¦Ü¸Ïg±8Ÿl !uIÑœQcÔˆ‡ ä™N@åâà ]5Çœ.ç†3³Ÿtè"Þ¥ñb{}Cnæ`àRÑÇ"úÚ|Dl¾¸pQN±HÉÚ !á~¦ºÀ1˜×•§Ñ¥óhæ¼WÒDEÇf²¼¼„F,.Š‰E˜rÆW3ÇWTM<¯¦öü>»83K%çüŽõÏnçÉÉB·°X!Žøj¥
+¿Rö^Ú‹~ÉOË›•zH
+þ,ÔDÙ&ª² D{„LCÀ‘˜YBVLûúã÷}x.6û»?§n”ËMv“O‘SüOŽr°ûèò­ú,<Ã…ƒBý~r#x¢â«í Ø‘~­-FѾŠä¼¥ÿÈ5.ZôXÌ1«ÒHd9wþ_Á}Ô…Æv¡®5Ÿ]€…GÈãáTgŽ¯K!vðX Sb¡;U™æDçÇ’â6À¾ÛEÀ>ò‡,P†ÒB<
+ãGÁ±Z¾ü>ÏÎVýˆGJÍæaÆÌ
+Z‘No=3ª1Ô øœó> §†t õ|‚ñµdEmÈ™Â>.5ðÙä½8Î$tƤpÌQ”öœ‰Nfü…ø<h~5çLê
+Š5ÝÇAP©Ê(ž¹p?<kíP Î AQÖîX‘Š?+‡6È´»ã-Š`vM4Þõe%.&C +ÐÕÊj'@
+Ì0Ñ/à]2à£`D.X7x PF©EdšíTG•ë°P›‰ª\ô߀l û:ô†MXøä
+éÎÔ˜1EÖt˜ª‰úòTmÃr
+}Óºx¸|±-æK•²õÁ;„IÚÍ!{+<¼ýÍÎÒ#v¾R| ÜžÁ¿Ä¥\(›g©x
+ÀBè%5‡º 0j3ÀœDG2Œx4PCËÚØ)[;Që:hEŽqP@²x Õe7#B@.!r%ú†‘qjÙ»ž%£¨»‘^µs¦)/Žzˆ*FàOE¤jÁÔ¦Ù¦Od­:NÞ/(bA ÅÙPѤ`•ƒuÏv²-¯‚E¤r" †&0)MItô®q•h(Vìð&³ýÛAÀÐIi žN!"]ȃ4,®„<©UŒRÞE{2×h/éÃ"E°‹ zÌAÅ
+ðÎÔŽ´gpk͆üŠ\ÚšM5z bBÄŠŠ$ÑáóÍsÁ;é$P·¤B Eº}p:ò¹_‚<ŽŒGåu-£î /
+Ú×ÉlËH@iJñË7o³hŒnSÝ%AŽàL!t"̈́ДĖ!¶²…„|h3˜1¢ˆ;™Ó„
+ÛÅ+ ÒiÖBÞŒZjˆ-K B‰:±Ç¾pÓ’OgÉïk~¬+)^]§11ve!ˆ>¬•`Ãa/.)Ÿ€£XppeÑ#Ý=‚
+pg½Êë;¶€q™·6¶’p°ÎmŽ‡oø4­‹¾šÈ:¹ÑÍ“dña½5v’´hß'ÐÖáoÛJÜßp¼ßYì7z¹·kLU‘цTŒy $¾;ÏÈwÖ?8c±ÆÔu¯î¥»5%êæ}òÛ7{»½ÍÙÆ¿àKìe”óc,šÆb]Ìù©»Ô’omÓGá|¼sËiܾ!ƒ°láƒÎ6n-´ÏÄ¢AŒ>è
+`nô
+Û‡^Iê
+g
+C!gòpSj`G#½‡ù„¬Ìr‡G‰ÒÀ6LN€4£¸ü¢Ü^ÓÍ€š,Æxß
+ø Ê—Jîㆊ §HÂÑúŽœ"É)‘qª!ƒµÃ¼«BpX8Ü-¸#E Ld€;‹@¶X›7W¦j@ö²ºf^‡®MÁ.ÕkòÉ•ºªÏ@ ío7æNÌìk‘zAµ˜n¦w?…çPÍý?t—Ù•äHU) æÅ}è°Z¬+ÿ˜!Hfw²öu}tˆ ‡Ã=¯½£ô—mí£Jâ=HS®0[¥ ÇŒõöˆL…õ¼ìŸ·GtÊÊðÁƒÌÔúÛ>Óª> vîGwmëP‘d. ¯„üŒ½Xö³ó©Ê@–·å5 £Ê“íÅû®t‘/Þ6¤»ÉQin7iz,1ŸÑC‡–ú‹ÇJ²h=7¤³úÛËt¥“ǼÕ-ì Èí·‡ ÖS »ÄA:º)ƒ
+™ê©$àRj©Ä@,™uA“šó6Ä5™þ¡?嶰ÀÏ*HM"MP_ŸßÔMË·´¼¿Ñ¨¡gv1±£*Ta¨=]£­s81;Ãë¡tèn<òg<kŒà0¼–X`›§+1²H/6¼Z£ý¨1Q
+µ*·Ç]îwg‹ïMhæ{£ç4€1ëÙ­"¢42vV“Õ€x™6qCMç˜lúzeÏs¹˜†g;ª­X2µ‰åÑdYi²À[Ä‹4“Zóç4@? €Çc8I
+×.ßFå¨Gj†Â¢¾Á4qäL#nÛ€So Ä–ÇpÞ(E‰r§aÓǘá
+96®ŽKnvTpzÐC:pÆìîи]½¡ú`…š.é1’,ÞSv´êbÂïç%§^ˆÔqÝ%Çòq¢ß“¬NàX!Š»"@YIfØ”åÚ5VU3•!#©´â –•y
+ä“ßÓÛÓ+™¹TìæóØ-9IKºß(ÎæCáú´|‡D¢GFde'ÊvdÞ¨°°ÅûóŒ|Þ¿ãìi„"<>€«ØMÿù¹CËœ¨Ô@cäß‚ê62´C M†S‘vEø&S¼ƒW𠨫éIÔR<oŠx9,ÚÛãDÞÑÒÈ7~ Ô/û˜øADŸAÇZnXojoý4A>¦ëô9krü·
+'5 òć43öyHŸ¨:˜»¡¥iŸÛpâþ5–ŸU`ÜÖT§F3PbÞ/ÃÉg»òù´Ô·—xûÂR£à¼Ö¸®ÕM0ÝÑà5¹¥mNå#¡<¿ŽP"¼¹‹:½va4÷J}
+
+$·pË• ÷( >Šƒ‰²ƒä®Bøè¤3^Cu@]tBšî'½¼`E0yxt_´p ¨Œý€è½LÊŽ3Õ}CŠy‰aþöøïER
+¹;¥µß^¨a¡ÎyB×eLÛç;tìDåþ‹-H?*¥óßµw]ý/²Ãœ
+d¨§HŒ"%e
+h8ÛHGñnÐÑ– ëM5Áó@ÃwnÃû@#÷ ln:õ$pø¢SþëìwM™áKƒEÝûª¯j£h®ß0H£"EJIútµ «‘Öz1ù?ßMwåTáuA2Ö~9™—&è7úAy‚ÐDRn•vtÃ/"¨µ2Ž.ÿÅ ŒtU cˆÝ%n(Cƒ\'DUç…;F§ù Òt!<,w™d\*0̳@MÍe×Z yoÃ&rôã ð‘³rz&ñŸüû»¿ñŸûúú{ÿþSüý½I¿ œëŽþþó©J4œU9Fò%m#•Ò­dïúx‚D4ƒè½ATl1hW=ˆËð×X¦ìŒ ¥Œ ý-Û(Ëšì6È/"(6#ªT‘ºPX¢7e;&±t%«–Ô²¨à6쉮¿Ý
+ÚZ³Êm´¡üž|y·äå÷@OäNFõƒC î“Ù³Z•–NÚ &<cƒ¢«ˆ¨kEµŽQÒ5¡ z-´$0iëD…%*Ô0¾Ûˆ;l'‘wø>lNO½—£ßÆñ=ò¹î­¯Íª°0
+¨ÜÉru'þ¼í$ŽE«¬Ü Õ>ŒzvÉB)ÑÊÌ:A@·NŽ+Ö(+ï&#8n{ÛéÉ꣕¼¹IÔ:xWÖQ¹ZWà=í
+}lÂÀndbùHš^wP»Rœ¨q²aõ&¢œtq¬ ³«‡½c¯Cº3ÙªC†Ì§âØ©;ƒ'­-vâyb'Àöä*ïøÔLÜ::tÔ"•Œ·IÑœÔÕw2îk<O$¢Q?&Bká¾¥m_z»/Wô¶˜2l*EŸ3"Ey‹–‹È$H‘ÙÅžìð-"ël%ø#µàðÏÖ¢‡É.Ë ÅxÔë߱̄›HAÁ–F‘Àbh’Y|néìFcõ\º‘IF’E’©œÜ&îIÑsÿ$¢Bl…=gyî8ËÌUÔÊŽsçѸ“€nÄÌåïÇk¢ñzÛgy½¨˜uÃ9lÙ7I~—Éxä¶GÃÈ\"Éh®[BCÜ«”šøw·iË=O=®¼MŽ¤†yƒü’ÉÅCnc/¦.
+ê%Ð õÂÍxûšÍs›MÙÍÆ Çº[ÐïXÛ|Mˆ-Τ‘;þh¦ðØ*”ÐŽpí*B²T¹ÛXO
+3Äu7¼9q¸ç>
+:žfY|t´FɦH€÷¨5Zˆ`x(îØ0,G_³éj~Qlþ™ºÜ%–Ÿm¯\ls`-šË/kGï*lÅÒðþ\­‡øÜ*¼1t3$†RÐ1ýj…ýù7,fTéRîË{˜Ògù‡×ù˜c?ƒ[¥Ã Âó ®b>%$‡ân~ïf
+o
+«Ä´›žc"…G,£E• -/Jƒ×ºe5’¦¸˜Å°P7g£¢Ôoï¹Ô~4ãR¥À#\•¦
+ålÄS±'#jÙñãø$lx¬ñ¶<î€ëjBJN„Іµ˜Lÿº=I8׸{.ŠÍMû¹¯âõF9TlvˆgUºíLûZ–O4K´ ã<áˆó0§ûòLÝ]ÖïÙ^§.4ÿs !÷DhÅîO L…àhÕ„µ.ÊRÔê$qD÷¥£lŸ´Ó­[Ô¿ð £Ö¡âÁ"JÆ¢ÈJ¥Þ- M!‰7æ
+üR‚`(}:`úýÞNïð‰·*ñM{Ùß-oÀh4Š7%é”WNV£2@-§Úvv`XJ)qî¦Qi®¶ãd¿F±¿¥&írÓH+ë&¥ïÏ’¯«qvÐëêMy7®'ÐœãFô;-—wòر›ŒðŒ_W—ÙÀ¸®$  {ÄùšRæ·³.¤—kXº];õä>[Ì™ZÖEÿ"%.~ãé8+å hhÈ•GíÙ)ƒw%oW¹µrlиØÛ§t«ÕÔ¹í>Ö‚oà·h"~Ó=~o2âo@*~Ïù6g-oeTŒÙf¬ë­ü gÄoLWÎÁŒ ²ÖVêL ¥¸O»³!7Z}%¿Hâ  ¥¿$<_…sW´ E³W_Õæg6o†×8k°v8‰)üRËC9i©VÛ\¡ñJ¡ÚSQ¦Ã H[/ÝÍÖv.½*LD[OÂNäÐéõ?t—Ç‘,GUi ¾%'ò¬îÙ °—모$AQDMA„CŸ_ ¤Ñ{àh%ÚôÉÐ4 d¡eVI3Pä§×?ªÁµâó(œS–j®Æ¨ Sá2Œ Bå3w´ƒªßš"€ñ…O%…ÿ8!ï¬-šºÈ5±Šeð‘&k9±ÔG°æ›Î°*MÚ³ø)—„1£=¦â“€™¥hån,·C££+?‡5”øö¶‘çþ=“ƒ”M~³•þ^~DÂÁgÏbXã›99%;ÊßxÁܸ¶KÉX2b^íP?¡dS·¨V{g2lá Q^‘­Ÿé_¹k”„.V0NX:Þ<‹q¦ Eè7!SyLm˨O›—t”KÔäÔ$¸/]ÕÊç jÄ´D¤ÿ%yÅ‘sd‚„Z-º!NU¦*%$šYÛ Do_1´¢8kisÄÇ µÒ§Ë3¶Ü0åGl–-Ø ÙðuáÒ®ö£v'IÉ­"ü´®ÊÔ)o~1v"?Mqœ=Ñ·ÓŸ´kT`æ ‡sQ²Á}ÄølLÍß+c:’Âß5–ìe~¿Á ÝXÞÞã`WHúm NèÐoÊ#%` -™vb@‰ªqØ z[3_ëøØ@Ç¥/:½ùÌ |ÍÐ
+:CØ}—é:ò' X %;`%‚a¨MÀ¸,ßèD̤hZùÝdüf¾ª¿ÃÅø[{ß ÕgU&ãÛ6F¬ÿÊß5ÞãBöïDPÒcÐ: NÚ
+j:qò!‹ÁÙ4mUÿ{4[ðOT`™1U­ØœÒ50 Î!óôì,Š~Dت0A‹a7\=ššIVë“màÿ°iJE¹®¨wèÎø_\F¼
+8#uUZŒ2Ò‚°ÏDÜê-H™€J+Døúüõ¼EcW©uu‘–`%æ^gK´è|'çTáÒÈCÛ‚È[5Åb^ÇyõJLÇSŠÍÝe&â¿¢N}Qý-Õ!@ßliÀÒuD„ q}Öƒéú·‘õ&
+Ö#ê0×F1Mo§{C¹NØ.Èt
+ZÚ(ÞeÐ*B¦Á¬F£P-0ï(SZaÇÍ€²ZtRÏmúw‚•ÁóxÊÛdP-M³4¸‚+@MWéÖßöFš€ ÞcxÏáå®â~óª¬Y •“©ÑBPù³bÑ,à-7-7¤e÷±>,„¶ñE"Ž­ü˶$)ý d5‡‡ Û Å¾˜ oËrnöÞË›‹ÌŒ³ÐçVˆÀḺ‘PžrtQ•”¿›:Þ^C
+èò[$æ5MÛÑšCP«|`Ó0Îwu$•È#‘RÛ‹ú˜ hª#,£ÿnr Xòƒ*Âêàãef–= óC묣yŸLfn´3lÿþÓ?tPSÿ
+ä_5<ùk¬sÐC¬Ì
+t͈NAï·@>p¦C/Ó¢E©€ùž ,h=ò7D£&’ˆÀÁq•§ÉÓkø³RLÚ1Ä4\}âvcrËâ5´é$oÃáã"AŠL͈¨nfƒI+)VŒÆjÿìä¹Ý)¨]=¶t B3µCâ/"‡ÊópÇ3
+Å&Ÿbûú÷¤Á¤wwWv ¨dⶭÞ–VFD y–UkÈ~ >C¶|ÁÍʃ^Z®©´ ×2
+qbTÛö,Ój¶¦5KWÓTy¤PqbY’¼ƒª<˜x&äµø{AÁ7 4D8VçÖ^\Î~WjB5J¸{’:‡ˆ
+–Z0ŽpÔDB{×þæé(¸zpÔ)
+E€±–ïxËdtü
+£åNuô„„j+2»”
+¤©#®ÞãÔè ãBgëÒþh»á ¤0¨ $¡Q ˆN§(¨(6¸ÒÛñÛ«5²ü¢nZGW\‹å !åVDO1÷ÐÜB‡Ç.ƒr,¯˜³óP"±C€ þ©î¾ë{
+I„"¸hW¹°F´L¡\Û
+<´ Y7WC˜ r‡œàd$>ŒJ#¡ œHÛË¡Æ6
+ÕšoÚí]Ç[m7VŠZ·#
+"ñÆõOȸ•#ð‡s OÕ';èÏgÛE9 Ty‹@Ro‹á±X*‰íù¼*Yx‰
+DgÒi£ª¡¤ï
+ªËíUl!ïrÖjrÉyVm<u 4DeV”eiþ£”#Ø›BøI¡É=ÍÉ”Ë@H«X…{7
+“Ñœ·³x ú±m»ÁøÅN¤åûN]÷Ò
+^‰ƒæ½Ž#AÊeìŒR5_¬{§aUºo”K&&€<ĉü-'¨r¦¶èL‹ºt ¬³f‡¾àƒ¥¦ÉêQpØŽ
+Ã8$j'JGÛÑŸÙõåkË‘¥rtµã)ZÚë–3ÃJž…±F;ÒBiÔp2àXU–䳿wzÈÒ&E§Q<4 ÖEkÀY8æ) Bœ‡320³^„7Öc`opî[Õ•ó|̲å²°iGÖˆ#„ Š@Ý&E¨‹ô H6¾äv+ogIˆ)ÌNK¦¶æÝ!§æ@…åšgªVf±@m5”5$"+$`£†Þ‘dõI¢–;+¬ YÜàØË„Q"C0cCT–z#!jÙÐÍŒ?  9¬`*hÜ7z"kLG™,ÎY©-nN|¡èù3Æn¯¤}%ÀŽe’øÃÚ©½A»Ú”žbÎwA¸ ÂÕ tUüA4®…òÒ'Ö´ôt÷»2ä9D‚5‚Ô®ÞÎBÃÑå|JçÑUúÞ€cjFÛž˜#úc ½-" ÈÆ hHc$<Í5s²]^Ÿ]µÃõj‡eÁvºÞC
+hür;= ˆ™YÄÁ½îÂX¶Mñ =h‘åòÎåºwPeT¿»­²—IÖߌÓWª<„´(½Ul”—xaçyß©;>—ãà„n4ÐöΛ1²Á¤ñFòl³…„"£ªfÀáײ õ´ ßD'”¤¹b,ðB|U>T*
+ TkÏ»ûÿZ½OWÁEª¡8@X Sˆ(¼¸²3ê‚a9#dꑺW¦žùÉÆáhSIzDG·¨¶%ÕAœ™ pe0ƒºÅ° J—ðc9<¸£D —ýÆÌ×Õ¬yD}l$WËâwzÙYίJŸÃç>oµì‹bÀŠ8˜Ãör”òdíã“'
+>±,tãr=÷ò|Óœ{²UÎj¥Ÿ3t›ÚIªVÑŸYÿPÈ¢¾®lm劷8–Šàtu
+2
+CÚdo ÿ%u¡H\ó øTlVa$Š½Ùl)d3_H9cD-¼\_°Ýap@{X0‰¢d¿•¿¡sÆ"fÕ€ª9
+U—öý•ô¬ô62ƒu#sÉüÿ
+¦pÏa¯²u€WIwá0WT/ò“X]z:ãpõ(¶Bš,#ªÃG¥ŠV)Uj@.hR>¶¶“½(úf}熰
+<`Çn„FRWåˆUóÈaúÊ‹
+Tñ51+8ªY¾#þ=éÖö™!Ò¸Š)eJi1v–‹4EˆkË4Óør]UüEŸ¬y>ÙÖƒÊTîAþ:¹UjйF<f H»œã ð÷‹d8âôHC:¬¼& :ð%òBõŽÛ#Vò1‡‚R*r½#kÄfƽ>¨7¢eõy°-y)€™u TbÓ ú–Õ'¦æR“tåØ3òàð|ªFT-:Ê,¸’øDµ…ãÅRIlLJHS¿‰Å–®œÒaÁ¢Òaž[
+uu¡­0!P/1;—-À(ç$õÎtNI@Ò¨º[PزŒ°×ÙK# Öé怾~Ö…2«f¡FÕ õë·ÓûÁKŸÑ®Óº2N
+¤ÃdkpÎF`:~'ùj îö{!S²–Ƶt®^>ˆëóÂk9¯šMΘChq
+‚º»Í¨¨©:EªÅbÐzâR<ÀÖî½Ê{VN”.µ€NAb4;ÇäGÛù¶ŒRþ]‰eëLpZåyîU1ãetÇ®´Ü12¤
+k…1HgSAœœâ¶'g ¸dUD4}cóbFá’ÖÄ .¹f)C@“Ϭås’#dËnn'ÍB‰(¯R´(ÍC'©Ú#öÑ؆u!UAâØuH‘]ߢ9tmØIíA²ÃØ•¼¡èEËÆr;…^vç‘}ññ­OO«ñÉ“rº
+ì³’r ÆG¯30£¡ äecªïÚ¹þÌÂÂrÊØD¤¹[V?Ïù†%#áÅÞŒB½²<zãTkì°]$R6Übk"X¶Öð¯ú>Êp­•?Š¡Â
+ Qb7E{¡7HWBbÒa,¶åMOÔ¦dÙb½a«fè3f»p5a ìÿX/·9*Šþ•~t"×½êƒ';A(”("<Y–í â‰Ì)ÿžµN}=ô@^H2»kw]Îeï}º\€Y[jŒJ¤õš‰Ô_F9<š“Ñ8tþÜ~'
+÷² (&Õ%åÐ
+ÎŽbÛ’î¨X[L„J]\ç[~ž¿øøðùû7ïï?¼þøãå7|ôì×Ø7–O.Ï¿zøøþÃ_/Ï^¾|ñæÍß}yÿðZì'—_ü-?ü÷é×üúúÿ´Y¢H‘Åÿ~óãþäòÍ[þ0.Ï>¹|óçÿ~X¾<yÿ÷˳_üéõÃû^}ñWxýþëÏî¿ÿñÕý·¯~÷öýÃï?Þÿðý>ôéo|ùîûw¯Þ½}Åÿq¹ãñéòâÿûá§?~ºÿüyüþ£¿©3øYûQ‹µüïkÒûЮcœÓKÑNT•ÄÆå»Uq K?Þ5?G‰€¤†r|)Y"šžUDw8ÑÊÞƒrÝÅœÔø¬iiÎðý
+;”£:àá0ÔfJN„æè.Øfä¢f¼Tqâ[Žxæ&f.jz‹\ƒ¶ È"Š©€ò¶sE2’:Eð=Œˆ¬.ÀäãÝ{ôˆrœûs DÒ5f¶kIƒo¯à¸|¿ { DŽæ:o€›M'¢µØC°O‚à f‚~4m!$Œ¡a®–ðVŠ9¬•Ä^B(‰79㘔“’ƒè1¸ƒ˜ø|®©hp¢g÷ÃwwÆ”´*…N×ïàŒ¶Uª&ÞpOùãp”˜ˆ Ë’d‚)È«lUa¶É?GÒøÃû°ÔŠ.ÈSKxDA²?v-R7уV¨
+š¢´ˆUœ!çÐKÆ>ZM9ûw
+‚>êãAP¡å(ç$
+4\L4&‚6"Õ"¢®Ò¢yb‘¾I.ö¹¯‰‚Áñ°²f
+ ëRñ^¬ÇEÎ<1'òl3ò˜F¶1Â"†RJW%Šq9²ŒvÖZŸŠHÑA­ý¥Uek
+ªoÄ8ÌmÁ¢´ˆÃ³AÐG æXQg¼f'(>Q©Gó‘_ˆY|êITRîž„ÀŒÝ4ì›A#‚lm¢m¡ÑR»úA/bÌ} p ZšÑ.dG+-ÒEjØÚ¹Å,è­9—’^ŽI¸ª”8›¯wÇ´M‘` ÃNÙ*;vGËÆ¢œÍ\Š¿JÝ{B9oõ¦=z0øÈæ… ¶gЫ×ÙïðsQ;X¶‹‘f #VÛþ5
+‚Hîo‡ZáÆp0±Î}»ë[è!a˜Øu¼Äã)Ti`ˆ§O£stÏ€š¦SÉSdÞ+LlnA„UyùÔƒPÕtŸ„‡ÁEDé:‚À½CÆV²;›À2'ëQæêùåûà³³m°¡B\“tò,¡èÐ:âµ;Îw‰â÷ Ô¢3@IפÛ}¬q=v­%4DÑ¡/´DP c
+€Fܤ$Šý¦¨A8E6u(p"øB®'¢@NŒ@ÔÐ2Á©î=ÈýŒ“¤ä8iHËP—sÌ€›Ýoiw)žÎÅaâ¡- ³[4† Zߧa¸û-"ÀdÐ;š¢©Üî#áv ’Ò f*È®3„ᢠJÌ<G³8.èæø ÊñÕ§Eߌ@™B@´Z
+…ëv3ëEBª¢áû»ãЗê¨0.èi€ËœÇLhÌëñ? T¥£E-ù„S ­ÅyÐáÜ©Ó}°HŸXöASé³kçÕè‘/©ã@qº”Š3ÍWåÁ½œpä,#Úd*鲈..BÚĬ2"xí¦¤“IpX¤‹îH¹—¾U¼\´ãˆ~‚¾¥k阡Rô§”K¡³˜g»Î»7(ÇÒÕõ›$bpÑlŽ’ù)#ÑÂ…I
+ç³ó$öR¬“^+ö9«€²ÕXË$Ŭ£êÞÖI…µô‡äùÐœ”<Î$AD­oy%úw2ÛrÔ¬N*B‘ztf&6*rŒY̱Ë4±Ï–#èär9Æ«åPÊ}ö«uȼš‰`í“@¡áÈn.B3¶†7?ƒš®9IÉ̉¬XUi°3³>
+õ=w'â>A)ÐF±°™á}9F­˜EoŠ¡SŒ~Ù9]GFxYì†ÀB¯°±ÏôðX¬à<‘'GNˆýŒA8(NDü±Qwa®Iܳ=V¦"@”2.ÝáSªD¨¹ˆdaþ‹í2±‘É¡ +mA#ïLÙ3>¬ýAªj0-a1ÀþÖ«¼H¾D¿í« ùIÐÙW¼(r‚èÔX¦8Œxå‰/Û±2°H8 <±KHCçÞh¥´FZ>¼ ¦ÂµíQÌ(¹Æïˆ?¡!ŽÃB i÷pu"*zï„›†ê@1Ú—÷¦©æv§Ñ¸,oÇÜ8",:Û©ÕJú¸•Ñƒ¨W ëU Uo€B#"¹@ûx˜b“‡¥‹\À@Ó\#¶AÚ˜?ÎÓ@ t©U¨)Wtæ;–ž…aûM¼¢æÆøƒ¢éW NpÍØ1ðhù÷+EW®Œ4? ‰ D¬ºcR€(0 ]JKé¶p‚„Ë` b5¥læþXÞ“÷(<ÞRhË
+7ü¶ØÑã.¤¢–œ®HL×åøú’´°õð½vø–¶Bç ±ý†èº’ý-Ä?Ns퉸E¹q¤3Çá·ø«“‚âpøAìgš
+=R‘zÿ"b›ÈUd8Þrmîs¢·æ}$¾«Ã¡“–áƒ÷¡Û¸6è OÎI„ÌÌíÏ)sˆÌò¤¬C=]–ê­ú™>kDÉ"bð–Ž–žBJG¯¢[.V³ó@ÄUº D×’½¸íe*Z²ær*­3¡<cEŸ„ζ_ÝH€Øc}ÈVÿv×Q¸ˆ+l6³ðÿÌŽ29
+>F„0X;B¾ýÇwoqš=üË@ìƒG ‰$8§ò¶
+'
+“D-`˜îÞžã MñîE;¦ÎÝûüE!(cÇ<•èr¥O%»Ñ&žšujÐâƛٖ¨¼ >í&qØn#Dç¹îj‘Ô¨
+R–ªÇu˜Kš uÔu¨”'ŒòT_3î®›…8óÔþv.(…)׉ùf­õÑ+ â0Àš,4U \¸E`Múw§njEèX‰²ïpÚ»ëp0ºVq'u.Ì ^¢åNaVA0­y§tRœ+zïÄ,Ùr‘5KKJaô¤b‡³„<;Ó1ÎÄ(7£“D³ï|¤œ÷öøžÛb퀜Dx,¢g£ZÔÙN Ï7ûEO3ß8„ªS¥ŒçÃ=AœÁ‹¤É ±Š œÄ›Î„ \qIø‚AÇ6¶ö÷s,ÍVÈ%¹G,ASáV¦lBºXq¾"BË“ñÞاbEPíý©ÈÕµºAÈìZyØkŠ`d±?‚n•Ça:šÙ¢öÍg¨&­'à3¢µ¼|ÇßÑ¡ˆâÈÌ—?›²PðHYü—ñ*:JG
+ÍmiM³B&V
+"bO½ –6’k`íDÌõU— ÙºbœS .N·¨rŸ«P¾—Sã3·eÜÄ r±
+3ßãœ[[‘l-G`½@ЬCDƒœî¢ø\ÊMĶñ›Žè®Ö|F¡‰qQ~,‡ë k‡òã­$E³ÙÒ'A‘y ^µ™|^Ô¯v¬ ¹\ãŸ;ƒýAѧ
+âĪ̕¬öÆ“i` ×Žy3ìÀ¾w‘$¿¾ ÌèFÓ°ÿþ
+ï(0sÄ©¸ùðTÌ%oØ¡êÈ?SîqwºÝZp $l$ìK#é- |ק÷(Nµ÷¬Šö„È®åKoÌ$F‡S{ª‘ä¹úyžˆàÄCœh¦Ðm‘Û…ùu”HUhÉîA´A3íß/ò÷u#çq*BÛX†4i|ÃN×%=39#ìOÛb¹‘,l$—¦kusZˆ†Þä1ù?ñv8¹_Ò Y¡Ð\ÇbÈêKb™M˜à­Ï©R0G…Ö¸g¬ÚløÛ‘'8’0ÆX˜­-C–®,ÒÀÚóLÛ;m:¾wûƒÄ`ˆãúûûáþˆ Î×júùþªcnh©W(p$ a𥷘¹_;‘ Æ
+’n—“OÆ*—wÿ|\ZÍŽqXùó=‚NOÄHþþFŒDø,(5
+½0Û"²ç>¨*sv¸(Ž&íYñzøàcbž…lÃåÐCLp)ü³oø°ØÕÐqÌ kqYÖûaÓ´–õ)LB‹¹=&Y >N‘¹5ˆô}–6wŽ_}‹®P¾ Ž Œþ<ì¾}àóPþV°c;Ú'¤ö™Ýc"˜$ø‘{t€ ƒzÁH•ó·E÷»ÁåËevhQ+×o\Au¦ßyñ“s]%þéÚ+‰D @\©K,V¬a35%ú6þ¢ˆMrrUyZQ´Z‹u ÿ"‚…{"0·"¦î@E…®¨¾Î3vò\ èªñÝiÒD¢PÎqŸ§ÆÏM-<žâÄ¿£Ù’Hãý½M C³—Ëá¼Ét6\Ž¢ãA¯†æ аh#…õ »’G¤·x?±+O t²JÊê¼Üìg÷™YèÞÉ.ÂðŠ‚Õ>¨Þub'¼,*‚†]ðDÆ,ÔÏÓÂð@ž•ò,åΑêsˆ;(›·‹æF”76Ú ¯ˆa¿yF…Ä3®žëC”ñS«û-AøÄ|Çt]õ·Év’[qôICÁ9w@¥oáú7>høÐ0~åÓMQ“sp™æ§f±!Næ‘£š/$›ù,«P”iÊ•Ûù×îý‰ˆù®YˆW~Ynåÿ
+H‰Œ—=r$;„O ;ô¶ƒ?
+GOíSi† ªJ¥9yü}ÒÙªOS­5ÒcOëEH¦ ÛYD«N²Q6ÄÝUjµî]€Œò,s6™œè¨URFdБ˨£Y5'WÊm¼flD7¦Ãë&ŽÚœV¼Íý˜+dßdÆË„4¶á/ΙB’ÛRíxL§˜IÔW­Êì¤Å+åìÇk9À‚æbz¢Iíµ÷ÑDW”NÑÉ¡¬sø#º-ZDj¼ˆh+µtIùG‚i¨  X'/¤ <î³øxçÖ 1§­jr!”"µ‰QQ’iÒHiÙ—ÕfâÕ{µz@f7’áVç°c!OÚŠxyûï[}üÃ…Bzеøî
+úZ<.$Ï@È÷ô©#„zÁJ’=l#d¡|ñZ]ˆ.ƒfpgè†ÄóîLþ­zȳU³áJÞä8Ç{£’”„>[çT’Æ×(‡ï¬¼¡ÑÙ4À܈¦ê}@³ýR+CQ©÷*‡>‰†¦¨ƒön èãH””ÙZ5/.ëÇuE
+ çcŤ–bT̬É‹O¬õÅ!}
+íæÅt¨õ#=5´ÉbÕŸuU¤S¹\Îs‚–sNëëœJWw£ošöµú¡æA²è¡ÏBTJT©Q¤Mmœr¡ =ed­ ¦Ltç! 4"UF軆¶vtDÉÇURm(ùÜÓ¢“Iã9Q=]à¢Èg[ÇðˆæBª´Ý‹”§V¯ÊªF©\n<—4ŒÅ;QªÑ¥×)»ôèBFžŒHxaÂô“ÑÌÍVa3ävät ¢Iªí_ÔáûL¾“ú6“éA×x$Mg:Kï¥Of2T,†lE±ò™¬OSB*A»2³™¬¼Ž
+£e1=_ÌäA»wîòFžÎä±E²1OèŸt*‘˜IÖϧò“ Ü{Ž ºMå t™ÊH]‰†(t«z:•‘LH¶À‘%™ÙTŽîcÌurH’Ò©,TËâ?¯™-§4:_Ÿ´•nîdcY™–cBÿÞ©â‹s„Îdʵˆ<ËúDàà;m/Ò^Œeè…a!ÞÑj:–•qJù˾&÷e,ß)˜Œeˆµšá6F=cºŽå Î
+M×B–ù=;µš
+³±èêÇQ(€/=? Æ C®gó •B´9ù°
+÷à8©ô˜µ— F.EÚrjCèWÑx<`G·Ì Ö¥¹Ï¹¤žf܆& ¿¿©ïGÐÀ-hw–aŸ›€ÆíÏу (‰Ï‚exüŽú·ŒE™Üƒ
+ËzSvÙ¯@±áÀ±‡5¯¿(šNK¨² „©ñó
+§ùmí–í~@‚"Ô(Ìñ¹#\v?@1"¸yp\¶ú .è
+!z·lí£šøÞÌà&ÉÖ5 1°Bet'Yû
+2…J˜^×ó¹—µ/Ä”úk˜µ©ÙÚ7Òã˜úuÎ÷µ/!à}í v…B¦Çæq²öÅI nÎ þŸÚõm틸4vU¬˜çú÷£Ž¾Md¶¬AÈ“5=DX•Ân¢òùÔ*éëÄÌy,H¡UÂøú9H} Átdw!{Êd ·½¥).R<p¬ ÖE5ŠŽ_ÔÙŽ4A%^؈„pFØb;måÄÿLt±-¾”ŸébÜÞÁÄ…Žud"?„ OB[;eÝ@ÍÈ)®ñA(DŒ`ïêzÄ̼!Ï·èKÙ¿!\Lh sö÷-¹+8HSǶëzÞŠ4XˆåÙ8inȸì°eÄë£û%ì¸c3=ä´§çPa2 ‡·ÓÏÒàŽåàö¾KÉ0 LáUæѬ»4’Q;jIñhú“Y{D.³0kðã‘Ñ
+éÂ{Pï ±^AB—”±Œ¸ó¦ÿÏá/‡ÑЧPg±%ºûùÄ@eŠbù—¡oÏRZTºÞeS¬È\˜ÑíÜè"c) ¨ûå~cÆýr•†áã›Zvø©“5åø|Ïa­aý³Ø%¹êžD™ŠnbÙNµXÈ»
+!¢s_%Ž0iŠb©caø‰>…d'yH²éè‘a,HqÄÕ¿©µl¼©ég^*áuw¸ØJØÌ•¥ óäLò
+ö>)D®ø”åš¡Ö(R¨4[Ñí.©Ö(äƧº²#ûÞÊò´œrŒ1ôF|lòE¨[|
+f§Ë­J¿ÂðFΆ¬mç0–1’üáñËàëe]çÜJdÔ?õ\ôW\µW)Ðò‡ž5G»¤ kb%r[<6sEIfà “‰‰Æ—ˆft³düÜŒÝc¡ké)Óc&£^rý5ïÛþ
+˜ ô“úmÃ~X€F'Ú¼Ã*°Ì
+ %¦À]JÂÕ­u³tºÃv§!yJxTuø €ƒ0/,S=™ÐªØÅR!Ô/ÀjãäFVyR z©P4ʶQ¨êÆý,&ò±ð½³×v)ú¼aQ—” Ú¾àȨ-÷q®CáVp1cÃñÞrϼ}]Òœ·xp¾€áŽ°yðžtf¥Ü}Rp*ëkZ·©£Ã€0$JræC…‰ã::vÚ~qL’ß´ÒÕ`llèrd±ì¸.˜w¤p®pFÁSuå¬
+´tOýê1eHr‡G«¹ÚÌFðOÙÌZiÆ×Êhâ Ë˪hÆoÎÙ/E/Š°¸ôùf3 ðkqE:t›pàE>zØÓË;0¦ØÒ’ãY¸âÿ…‡­D6>ÖžîüéÛ—Xl<4Ú F—èVO±vO,jÐÝüsäBG:è`Ì~)^HÌÑAÅÀm­ü/¯Ýd³Ï'D¬@'Ö…ûÄçh"F/ø}q#ÏsEOj#–`æU/¤ >€…×>·ˆEBe6µaÜJ2¤‹W@DÛ0t<!³YÁ‘ÅÚÍ+z€¢®l`À¨Õoo
+uc²»WNÊeøÄ·˜)SÀ8•f´ækñˆh¼ôáó·µ|/¸Ë’_Ep®†üyîcÄ&È™¯×c
+õ!Æ)¦¥
+c :cˆÑH\½Æß»¥h¢¶¸jM:ü‘‚˜ËVíœ-¾¢ßåØl¯°€ÂiƒÈÄO-dÍoög‰¾„ü‚n6 A)/ŽÑ}içÓÝÀA ïK+ëÌËhŒ¤É_Šp°ÜXä54y} -Æ°1·£Ù”nL6’ˆË6ust!\
+3 ÉÉ„Ô7ÁõÂi:xÝ&ÙmlpŽ2'?ñï<"‚/ÃU|Õ[ŽˆG\ñ€*QΫ÷WC²85…±ãKÿÀðÇ1cÀO0!;ƦÎ8Fi©ÃHè׋"òEâ+3e±%fzãûvO ꃽߜùÂøÓ¢‹Ew”›#jÆÍËÚ¬uʵq[j,}Û> &CZaÈ7…dêà…à[ÅHì"´µÑ%M÷â>ƒýÅ#ºÛã2PÖÎ@MwyªÍ}×<Ø©p'«Nâ2§;h~ÜYšt̃à.»52>¬œbñµÄ?+‡‘Z:Ö|Y> bÈ
+¶ä’ÞT
+³sËA´äí-òcR´‚ f°BýàrC¯I˜æW¶ïª¼GÒ9hÕŒ" µ¼pÀÓì{–‹ÆÂ`ÖH‘2
+8I­ú§
+M-¯,ÌIÉz•&õѧ(b1¹Öí¼÷á–©D [‰[›é\9Â[à›(y-Ѫ2†Â¶ÐÇiŸºñ¨!*áæqӰɸg‰á†×ãEëØ—SIuÜ°-› ŠegÕ (¨Tp3€Êh² ‘&µÜ¨ ³u^
+Ï­)6Ί{˜¬º>ÌÞW§äŽž^Š~Ý\%¶‰€™·K¸aV'QöšF…2<7ƒ¡…LC¹PíBƒ Ь?Å*³pLÝ6™ ²ît§×£ÍðÞ2ÆV
+p]çœí6x"ÉÇ%nzš\†pÉõî”ìIô$ÒËÊùæn`6¼iÔs›Ì’dLmŒ›t?§ÈˆÛ)µ ±E"{87+åÖÒ,o– (¸#Tþ
+Ò£BÚ~:.ž95]Ò¼>ÂÉ ÅÒ#X”¤´SÛqûMnm@"9ÃÁ‹"¢°HðjäYX+ñD܇Wb|KðèJ¸g÷Ø6åPÄóÊ´êd/–¤+îRåyή|!ňx•«P‡Y#Ý`óœ,¤–ÖÄ:Ï=%Ššð¯É*dú@>\f£Õ‚pV/}}8EËA4’¬
+ô¾oŠÀfӞ会L Ê!µ¡ˆ-΄LþÑWE–V’—ÒÞ I È¥…b·qjþ–ØïŸèð
+t¾%F^6eëPÒ(2BaI®—Ã=jÇüËŽeý*è­è·„Ãû
+>ÄÉsºY1) ñdžäúvÌ@ú ž(×3½4©
+ÅÜ¢èQ»Ÿ&¯EN"áÙx¯•üÓ"'M:EïiR¨´' è¬µ—&E|2ŠQËÐü4©_s¼à9zM“Î(Þ‚ þ%@ Ú›nš\t4©0ʉgï§
+°Ê•íå YÀŸÊ€ûU,E¡_TÎá%wœï‰ƒ¢ŒŸFï¸àôŠT‚ãÇ¢A«[W˜«Š\2 ‚éÇKIX£Ý¦@2rWHpYŸJrEÖA¤6;'òœÈ˜u¹ž´¤?lñ86+Þ˜ôj@vSÆ–ú¾ÄÖ)峬 °†éÕ€JZæ§sßåÒ0¦¼½îPry÷»”ì¿õoi³«™“Fþt¦p­Ë4=íc+ðµø1–g¸Ç0‚\FlãqNèŒK¾Ýð…Ù«<ÚHy³ôàm[!´ç|…·/9ô⊖wÓí îM÷¾><ü:âb˜.÷fÈí8s¾R 7l}YϪr×Ì/Y%sðM—¦.%¹°’'ŽôâÌ!ódOçG„,<éèë¨Ú’Ý?Úø’}œi^3EUF½¡[éØûË9Ç-nÇžË4ýw½Ôà‹Bãá`^áä#ÛQ¥Mt®™k’Q`o¤´ð•Ñ
+.ˆg¶%’>%ÖvïÚÁ*Î8÷v÷¹¡¬ÈJ@‹›ñÞj¾2^­øù¥ OF
+¡ó]ˆ0;úR’Ú$½^*˜t*?Ì)úDDo7v Ö=‹Í{±N#ÂE±¿ÐD5N»>p/¹Î53¯ˆ‘æÅ% \%œÝ¥f-tÇö«¨92¬juÔe÷˜/iìY1•’<Uí>ô»(8ä«ÏùêqP"þf_:4*2‘,eŠµÜó6ðëe.‹ã=꺀—æ\×øÒb¯äeT¿‚Á+,xÅÇ”Ó HBœóþÜE«"bž:QéCÏËí)ž„±–y.b¥’<dr3”CLì!åçÍÛn#a®«ãWdÆØÈ!ñU- ×^u€Z±†&ÐÄ?ÜcØfà5EÚv.7á<ï¼ 2dÏìI’ô_9Ä`LD`9°Âœn­!‹À’󈃆ÆñÏ2Ox‘ý¦!ߎ£vÃJ´ml"¢Ñ®Výëëyû÷Má ×(ö«0¸Y³ÑµŽ
+ó"þoÈÊ qjª™èªÒ½XåQå{:ÓRT†:Ƭ»éL›Å”aà<Ïe¸+‹Ë™{C™\SKçM5…Dðju[Ð(fP* Õ¬}‚rÓý\h+ä[ N1¥á«r›5õ¥W@Áʘ{ÎlÏúÔ?¡)#oü2QsŠv ™d
+°ß4¿?«(w ›çc•hEá‚Ø]›’ÆÃ3ªÝH­."‚¶2‰ú °”y»E3z,î1ñóšÛ1’“WI C2ÇD§(¡“i…a¾>œ"Ï"È€YN6ß¹¶K‘“œ"%¥£ëN*Á³q:Ìýd'RY€ðäy•pñ&/Þ{h~v¢Hþ¸ÉÀ”½ØxEW(k¸5Iûš'»¯¸Êuc¾Vü^kÿÁàéâ¹Ìû)‚hëÍ$ë*Š+q¥AJU¬7Ñ3>Ó .¯añLû— ~w:ÃRhoá¤w-²š8'Ù}¼Ä+AµXÇ]õ ýòðà <ì!ÐÖ\BÌ?-ªUA ’Að7¿\ô½Ö|ÿ¡ȤýªJëëÉr¸-ŒR‘í^@öŠ´©RRªF²yÁˆ…æ
+ùÒ'ü#¦£DVœ ²‚Å\„rn¡O¹¬¡ $¨íÛš„¨C‰¸ùu_éB^éP‰ebÝÓ¾M@‰QZÖ¦ÚJbI%ÊDí=¬Ñ'4‹ö©åQQ þ¦AQ Ö¬FÈ‘ðIœZ%± Ayf/!Kk–ó"ðŒ¸ÉÇîý49žxI-Ò9…`@ÙÓ8·Ôú³š¸¡Ì\\Ë[ë¸Ùùò¨s‚ÆÇc.£ü&ƒ@WI E{
+Ÿ@wÐÚ #æaÙ—Þö
+½S-ûþý=SîÊŸ‰’a ìA>¾îDÕÛ¶ø¹oØHfà`ya.XZ°|&ƒ§É‹ÈxµŸ¢N’+£GÅæ€M ù4E…rJàMYuzÑûAÝ…eZulÝJ
+
+>®8|žþ-…!V\CWu"ÚRçKýò‚4¿$µ›ü
+#Ãì"#‚rMeùâ5šhQ0׳.Ë©Æü<Ær¥»rb-AÒd4Tܸ>Z¼ê~¯‘Ýmæùò!
+`ÇóÝÜÜ~‰ ±5+Jª„4à HÞ®4€"{‰×L'"h1Û’h³½ncДlAÛaHI†¾Fª„@1nÐ<ãý bià<‡3÷©¤+1"J(—©
+¹$>¹_S~F¤êóò]ÓèTÒ¨K¢I\lüQ®§oüÌÐCóQÅ ¦y®r^YÐÕÑèl1L‚WPDÄ šZžÔhŒÅ”K¢›hÒæ}Œ³Úfáƒã|Š3A"4‘¼Ë}|SŒ+jBá±Ý¢eJ×!™Uƒ¢Ï‡"\G–¯m<‹Ç‘eßÒày £ivÉ;$
+hs‹ƒé)òg¸X"­µ¨*Ê|B§——ßsáÑ‹DP{Ö”úØ* 7® a¾FfÑ8Ý*hÉÆ®½ƒ’6 ,Ó–«$x·sèÍ€™ z/A¢ä¨d+UaGªÒñ&ªÐDÈνû—~ á,¿ÿõ†ñí|Ѹcï°
+ĺöó¡h*!Ý©ÿÄ8¨Zòè
+*7†sa)º•@·Xmíð%j¿”èê
+¦q$ã”è¿´ ÅrD7.<~_:ýNd>>}ÎKvùðVŒÁ Ú9>Bפï×nÙ'lÆ8·1²›Œ±”œOUivœcÚÃ98vð?•*½¢áÏ;£H¤MÂb-cj=¸îÇ[X³Ho²I€X‹¹ é
+8èq…èÀÍnóÛEÁV¼~î[E·M+:[nJläQÓ«GÔÖw S‰>Øœ>C?J ä†àÂöÓ{¹f—>p‡Ô¬‚?wçí[°ßZÿ}µ«Ê|
+;¸rPôùPD”
+öÆkñPú
+>ƒöokËÎ{²d–G³`~ø ]H};-‘Aa!!èæ`ÉáV3Oï,ù‚W)ô,% Ü̬šœ5D;‰¬D~‘g°!VîO„eé#R?[Ù±‚ ”­²žÎaeÁ4>Ûøp›JBƒx±òc<¼
+ÜͬOÖî€
+$;h‘…Š‚‰#ÔYN‹
+
+DAY¾¥¡W GõYÖ`d~ØOÿð¦U¯–½T‘Ì‚=€SÚ Ä‰àÝ€­Ùê1Œ’BA>~@’âSA2"6‚M?>w`?’v?L.µÎÿ6½°ìˆÛÏ,b¢2øÐhN‘J23‡Ø“‘]#û°8|•0‚œ´½8Œ¿cÙz)3 ÝkÔ ŠâÓEÎâ‹ÝÅòhöq‘XE!•Æ7 è×üˆŠ‡¡¤ç&„\uU®þ°5+ÛŒ“‘Þ©R-”h5ÁתGr”@]ü±¶?<Ä(†ª€ì±Åc”D”6€4^œ‹ÇÓpïY¡g‘g=­Ñ8Å"¸†ý¡¿ ç×Yaß¡"D‘œs/@œÔBsˆny‚Ì+ªiÃvI¡¿T=ÉÙKMú*JV£U²j¦|ˆYò¬‹èøQå;Ø« cÏks°\ÆHŶóáãúƾ­uSà"1á`ö§L’Ê&ö* aLÍ
+—‰qañ†WÄV(¤@yA££¤“=Ð=6ËŽû{X]LÍY(Š°³OÁŸq-¿x5É5“ uý±-út#P ,šæ90/hÅfVkžk1Ã`»¸Ÿp໊¤ÝIÁ=œƒëž8cÙ·¡7'Kö«³„“Ú¶º…
+Géõ©ÿÆò¯“o w«gì;X;áö7ýK&ñÏC’!Âà£È_*‘Çú¸ä½ C ¢Ïïû€A›ˆ¸:.ÝÊÀgçÔ¼`E‰µ*¯0øÑÙüwÇÁÌF—¢ã½=8
+!Ù3z¢¢˜DŸ:_w(â»äV0œÁ_Îéjá"ç€Ë÷T0‰Š0m\hÄè©)èÁÐxx†Ü±ŸI<¨tï/Ýi©sN“ôÌwõ¬ËDØÉͽéÅýéŽîÜ[èsƒs™ë0/Z_º‚B½ÐI£Û´’´,jÌü°=U¼ù÷'0~©NÂ&V
+ŠänÕW·è]un–·ÀasÚ<Õ¡„ýÀ†0øÞÍWŠx-
+uÿI î1żŒó¤kkô VŒ} {ƒ+$ÍJð¿¡XwN—Š…ˆë¸oÇÜAs»Ì|Î£î ¾öæ¾ ·þþu£þ‹ÇØL¤:šYuÖ|/^•(ðÓ>ÊÜŠ¼¬qûÜÿ/s,Éj Š® ÷Pvy4&§1qYA¼j“ýs_H*Èüñ©H^ëK1¼Á£ŒOïdœ%C= AhåúàDk+ÁåêÑÈO×ÞãέM¥ãÆu«+Èy_D¤Ñ
+â!ôÜnŠpQu/–…ªwÿk_Â Í ¡©,ëchj23c‰7¿tô™6h°ÿS2×] 2´±+ÄŒ0}N‚O˜L#žõ9>O‹pÆÔrŸ„àvVÝÉ«Òàu<-áVÀuççí]SÀŠuökòÀ±5sL~ædU·!`9¤ŸÐ
+´ÐYã±4dR’C=æ‚нB§úß}ut0,léÿçGSß~)ÅBG’"Åe‰á|Â#|e£æØ`tX——‡ÒYöHŠ=ý„!Bö™²
+Ùˆ<Z8·éâ•sAp“ĹÎu62jÜAd<~|ó@/ éýEþ qÑjACðÒ¨›±˜Ö²‹”“AêÀäá«r|ûÈÚK9¢ýô‚–óìB²Ù±¥Ë\gIô~:\a±”Õ;fšC0Qåðªü
+ùi C¼€Ë7çÄÇ …iá±Í½ =¯Ø„ÈìÕ˜n^%×ÌœU·:×ñºÔ؃<õêçWfYclìÑuÚA~ÀÏåäœ÷ëDÀ•mTpÒÕ±><˜ã#±¬c€dj1ˆjÍôDFÔc¡uu6µáJKjÕ%!OL8Ž|ã“ǵõ 1­´}J6bî9ùÌCœ æ™Å½ ¨¦ØREÓ›GýȦR’†[œ,w)¢G:_JìAžZõã›ziýû ¨¡VHdÔ,¾9WýŒ
+ÏÝïŒ,—cOÌ@9 kÑÇ*C»ÆÁTRnƒùá »q¤$ÆZ>´„'±“$àúðÄ*ñKý.§\š
+åÌ”už½<êÅC\›áx5•XØ¡g¦´ÜœEž¸7?uÌsfTˆmMÌÄÜ*{/‚?˜“BæíSLjbŸJi2 „W*ö,èçÍ›wJ1zÂnQ÷;„’& Ã¶¿ôÉn™i…Õ ‰>«²žzþZ Ä* ô³Küä›ß 4YAx óŽ3¯Oþ»ÕýBI*¸i
+R6eM²˜»»6/ežªÚ&ò sÉ®0[VC&±*îbcv¼«P8\ÆËVÌ$bc~Qž¹n,R•ÔKõzñªM‡ºN,ãb}'KÞ
+úTGõO‰hŒ,<³Ù÷MÐÙ‰_á¾Ã \lз?©Óªf1sö%?3’Bë©ÌspÂsùÓ÷}効€ß«!d¢8¨¤Y—c+”€Å®6×ðÂNÂø5ä¸ÉÏwT«¼,d4“Äcsu¢¸84yç—`®¥è^A‚ÀÆ“Ì„×m›ìs¨p©ÈͦŒ˜—°K9èûqTIzÎ\›šX:.ûÊImºÃQEèfîvLæH¤ŽçõÍÐP,KQôŒõ%ø¹È¼ÕÈ׳uWÜŸ‡†3²æðá~¨YÂ7ôä€Þ­<@Á\˜«; A;An£KŠ“)£qÎXDr‰f°-C¹#
+yø¬-)‚¤ Å]ùäm <v0§·yòùc7¬d·2
+›…t2„T¨xŽÄ¦‹'\!³Þ˜µ£‰vYÔî£r„ú°cg³,Óœf=84iGÛΧ±½•êØ—Ã>°\%o–µkÅ~ k(KıoLƒÔ¬Ël8)M(„,ª8÷§® Eê#yÑT;§6þ“ŒÑÚÚΩä
+ÉGû£Áêr?´lĽå#±žðìSÏêEÈrÓ¥,á¦j’ɵ<R ý=o"`ªX>Ì%[ݤL
+k¹È5=þ¸MjT‰Ãš® ĵC…Ìè->e^C)zÉv>pd(àÈ ‹r¡àÙñü´5®²å¸¥:èûžd”r*›Bh;[ÀírPa(U›bL¬ÍÙÒŽ±E˜•¸¸I:E=)Ò¬dµ¢iŒµó#§›sx¶¯˧ÛdNÜl\­ÐÕè£Ϋø®Æ ”¾¥])úîâÔ!P¤A‹Y_èj óxeˆË£vØcÞšÎ(c LZÓŠz¿uô©*g£]æ±ÂÉ‹7DCúäñ²AÙN3‰‡ã‡€:1YôpìÕ¢X–!¤Ý¬„«Ÿr¼¥l B49ª­jÝÀþò“‚Ìzºx”ÌC!ç ¥r’È@™GmXyq¬2Ž_iòi#SȵZ""AÅ<] ×S‡>{v ›o!qDÏÈ$‚2þ4¯íÛ°Öè·\ŸÉy"-ÈásHûá8ͤ ÄëS< åV9{!]x@fˆEÂí©›Â7ì9mX¼;ÿšÏXpQVœjð)ÍÎdžˆ³¥Y•Ý{¿±ˆ¯ÊVåÌ e/öþ¸8&IŽ3.ÈyjXœ­G$ØØ ʾŽƒù¯È¾E‰eR¿ÿ‰nÿö×·ï›_…IŠ6Z¬–Ç–fSš¨ÍNý(eVŸ äÍ ’3¬H%+3íøAJŒh9,‚oÈ?Œ—Ir¹D¯¢”aÖÚò½-ÝÛÏ’*ed·Yÿ/ʉbðÑÈ*þ0U5Ò Gàdz Y3°7¿&ˆQa ífª ù J†«¤É€q¶ŒïÑ°žAoBöe ©Ã»Ìz
+
+7Qœ¥tô’’žq¤™ïûb}•­©H"”èNEF{>ˆ¢‚ÖSóUá‰ç¼'N ±TzGL(´"²$vL
+8!Ø&õ=Ðoð\ ‹“ˆîS>wìOÝÎIr¾¢Èýæ kê„JÀ82þ!: SäYß™‹"ŒŸ6Fu¤¼”"hq-@QþçChÓä™é "©r³û‡î(Û‹ÝJ#Û)L"bEžQê{X4Ô“˜œÉýáøÔ–!£JmLQðkHqöò¢l@¬î"ù®Ä]23§9¦µ–/ðì|A’kÊ8æv€œùâúú
+²!sUÆæB"] OÜÐ
+ÃË©hûkl±õ4-Åʉ­UoC‰µgœ„¯jåˆÙåê '¼÷¨ˆ÷½C˜äÅòñœ
+ÃQš6ݵ` ¨‰bv 1‰²Ù³°}7ÈIÃ3Çn„š‰ùE¼òVIt´Q¦Å­Q@ýD¡ùñøÃõ`Qæ¥Ê|½Á ~N£U@‚© ) á ”¸ymÐj6ŒR}©êc­Œn½ÆXwy ÉÍó’D¥É†t?ßr¤è”‘‚ñ‚ì¦lÊ-JF=†„̨4á‡E¦|tÖqP¶§"·žÖí¬ß@5ćQÕžÔí¡{ AmAh§èDAÍ7Ç€»ÃAÕå6n˜»‘`H#a ÍSµøSxE®†àÛeÚ¿ìs}=&î0½ì㤓HOŒcÚÎï_º|ˆ;ó)ºS_.ó0ÓCãE/ر:ŽL—ØmæRc6Vî:0fzÀhò×,B?ck=”se<ˆ4¥:+L¦õr0”° ¸Z:øM²‘é› Ú¶"&Ÿ±Ô,NÜ‘JîÉ(Û±3SB5¡”H¼&:GÁ&TÅææÕ …öŒ2Eß@ÂÍù–ó‚JðH™µÇ°¤l)¦Î pë™ì·Ç”¡ÅþR%ªñ6È*·ìüdYð^fö
+ògL¿¬U²w©Z¡V˜ç*OhSlŸs:`„À¬Í&/J„BÆi°ø«:èm³¬—wxÀ¿C_D\­WG¶1$ )<@“—iç$Y¡ÉˆÕéuÆæR¯3„u‹PnW ÷§n ©aÍx-ù#»2#qå [ncñÍxm©vDšÌˆðº­@oìðg(f€˜íž| Á°à  äà³*ë
+³ÈbxžÃÔãù4RÏ9Es"
+õ;„
+L†[Ù¡Hâ¿·%»ÜΧ2íÇ DwœW¡¸¹0àÓ6wÃJ§ãTGZKð—óL;?ð#ÙÅÖ.1ŽQæGf×'ƒƒ ‡\‘ÅÚ˜_rnEÿóàý䊘—Ú@Îst[rËŒ™m†Bµ‹˜øu:Ž×…ù²ô^l9]‡ s;Œ0§09õzùPúÔÎX`êyA3†ZRÁê¶^Χڂ0¡t,yÞ’ÄܲåEþ.€œísŸ-±ËãÍ9PêdšÍ•„=2½²ÙÖ¤6å¢6Èó*)#‘•¼´ ÕŸ³Âå”/³ã¢fÆ7’- [É­VB‹G€£ŸŒÒ±²)ªò®˜«Õ#lN^ȳeOSãªÅÖ±Ôìb™aÀ¤ "déporª©V„ÊWÖ»cn\ †îHÐÅýg©Š|LmW>ûóks?ì<Y"Û4 o—!vk:O¢ Å æÄb¯qù¡Ôø'[ã#DKicÌŽØöó©oäJÊÆ7_p
+ %… ÑkÞRò‘ÿ(u‰‰’éªXwq®<±X¨Qã{õtµI#w…üDw'm+ÒôtÄ~iVaXGä­C‹QCæ†j_E|‚HDHPÒv…¬ùKºôŸƒ"íwfÕ!Ô1ƒÝ¸K©Ô‚c³Ï" ÁhpLíýʵtg’X-Ûý"îàîÐô–fdùÔR8Œ?9µ7ÆLÑŒ³/†}s "çk >¸"×ÔLK²Ò¾ ævwôYÙNHBA±üý+
+’o˜"#HI,¼ëÙ.c{ÁcL“1Um·£¿x(¿©œnsƒJ7ü'dl}ŸÃb/µ ù P
+êKoùžXаO7ÈîÓ­ÝÁ1÷™ .sŸ½àQϾ×æ±ú~·M¿}çèB嶽ÀÞ-Kþy®#/-&Š0ÿ˜ m<?‘Å·7Yde͉³-ÉNS ÄÒÏ7 2»ÜvÅL©mJn­&„¯·ËLT.%ÎÌÂ%!Yl–lCpËè]jîÀà  ™ÍÝz‚aW’ök?EÓ‚fWIÎ~[)§µˆ9˜fA¦ÂA
+ñú1‰A†Rå# &û¾|ò?òßòrÛRª
+Åà¯WyRÁÅiJ(„+Å$Å
+ËíÂNÞ2>Æ v¹ˆžÖòf䎿)™Œâ¶…¶X#ï/4òQÀè˜{—y6óö¨ý©ûPÌ—ó†Ið]~Ž^e6ä'Ç;Ä{ùç'c¬‰O>ñ8lÏ•ÐyZºŸ÷ùDðÀ)2ê‡ 4:AÎÌÑåûø ¥à’ƒß÷¤ çp·c —É(ÂK‡MK@2äÞkÌËÍòù÷êF¬ÀCIÏ£õóZú¯n¶ø§x„RU‰Ý, æ9)áÁ+<‡9Ä@¥¤ f—·Fƒ€˜­Cè1Öš”2çåÑ‘,q³UÎÐWå¡ =25"3xQÞÎâ!?ÝÉìïúW€
+H‰Œ—Mrž7„Oà;èóA€?X{–¹Eªf¥Ü;O“|=É'ªÆq$ËR‹F£áÑË(ÞÚÇ¿j·—RKÉðÚì#f{Õ˜žs”aÝ7dôÒ­M`BÄ+{·9}–ÔW©‘=9dæüøó‡0}dí9:¿?6¨fŸÖ"øðuL1Ú5JHx/-š_
+”œhÞ’#s˜.M H¸!È+eR…mðM,ðÒ(&îÿOQ jÃɹAòRÈömì«ÞE-ˆc*#w~Õ5mYµwÈ.Æ(g€ï4¿£~À*%àçQcj|Éž¿B&&§p4£©â¾¤È0w/ Ï+ˆË°rpŠ!›„ ^ÇËfÑôì•ZäÖ8“¡f¶pÐISµ…Ì–ÏË1iJ¨så
+ºSO‰±Å¯)€Í1_®óZC‹_=u¨®
+­Ç|<œ-kß°$¾_ÕÄQ%« »Ç/º¾ÅÁ± . ­h¢Ôœ¸tú 2¥ÉŒ‰HyÉÊ—~yÃ|Þ0PUËIŸôÅJ`—â˜Tû©»Š^Š»Ž\¨G±i æ~ì3‚©ß(c/s·s°.M£Oce¿tȾÐ$A˜3fB´KjöM¥³rl·œûGÆŠ†I´uÎb1m„=WMÚŽF+h`ý’ÒD$§È~«Þ@o9ÞшÀAöêY 峓ÓñëÝ®&FøVzS«Þ`6ÊëŸüI(ý¬O²ŠËsý溺YÎ
+n¥$ÀœÖ»š¬|;’Çw†Ú îa$êá(Šà-Hu;:Ð%̃%aÕ©‘«žáç&í`(§nw½žL‚*+ó¬±6¤Õ¨~<Ý€£ä¥â€L=mk+«Wª£ØÒGyébß 0¬ ßµgœ›Þ@Œj#2.,ŸñqiÍ®±àÊQý·_nÞ1<qOòþ)ËÅ }ïÉÅÇEn¾:‹/²us(ðu–ÿÀt·ë9γ‡ö:ú%3c.$‹Ý„)oš²Ìû‘E‘eí ª#ÆQH(FH>/o“>$Yž{PÕÙ²¦éú“½”É¢ óÌf!™Ãå Wû,*ïæ­HºleÕC£8µŽˆ»<4J&ÝÚúñЂ¢¬«£nºB2ØUÜŸëMŒÉ :î7Ðç7 Íx†¶4vŽ34Ë
+ZîW†½!£)œ«ÞèUžI6™oãûrÒÊZÄ(¢oZ ëcg<)„WÈ]ù/åuŽÉ‘Dô¸d
+°ÌŒ5Å1RluŽ0Aqî?Ï#"ÛȪ,ë®@õ¯X<ÜÿY` žƒU*¤ÈܪӔ˜ÍãÅ:~ìÙÓÿÒ×ýÎþýöïú›–®‘$ÖPø4Å1]?s€÷ £‰uRݨãç€4Ù]Q§ãj! „'@зº'G£^M¦%C3²¬äúÂ&Øþº=iðÊ1,s£ )­?Û2ÿhôÐÑ,–g_ºG6¢~é;ízž¢°y­]´ó°Îþ ‰WÜél·§¡&Í¥®·xºÔþ“HLÔ•ÓþYˆðXuãÏ®K=øò‡Z;=h[ê;%‰Ö½?ŒVC k—óÅ¥»ç({Oe_[=§ó*›…J“ûkÂÎpÙ÷õ7Àlˆçç~Xã©cŽq×xO×¹ë߇²ÜMÁCq9M1w!o‰±c”ƒA±×õã)oð/ÛçŸG#íxStSÖÌæPMÔ*nˆÉd×"­ÒÃÃ<‚~ÜÄC‚ 2¥NºÙîôýΙ"VnŸÿúÏÇoÿýˆ‡ªãf s–ù£8ÁÔO¨ÜÿÿüHýø'Ræ Jîñ@A•)LŸ5Q·›ÞRâ.U[§žX& zÝ2âË[ ÚGKáw]_Ò
+WÓ›`ˆ¨ÄY¹õ¡cB2³M!CÄ[;yø$|Lª W]³‰[±kš‹=»ÊWM:gvû˜ë bZv"MˆŠ÷Ö¨° êJáe¸”‘ú5ö$þ–Go@ß/@áܤ)ZþÏh²Y÷–¿Æsö!M‚ÓÛWu¼çoÉ«‡  Lñæ§<–ÒZÆu•©Ê€8ØF¬¿ŠÃ72[gÆ<Œ(ƒ\"xÖusñƒ h¸ž¹UDº¤•Ò×VÎïWÎ?xs@LŽšó‡“Þ¬¬¹ýžq<¹nˆÓD­×V©ùžwØÕ:: x9J¶Ð/V+(áX§©î`ZîÅÄübò†¼RF<¨$Ô" Í!Õ#†Ð 率™þs"<Ÿ¸D]¹þÙd’™‡@y}_ªª9BK€Ðé_, f=&ØKûp6k ÿŸ¢«F
+ˆaàXY(!dB˜vV'”áˆtÔÓCeÊ=AØAŒlÆ:D‡´šÛõª‡æÕSò ó4„ÖÜ
+[T?|+ÄVMlÁW‘vê<ò.gùeó´5z5 Yb2Û®²Oˆ5Œ(Vi¹½€ %¤»ªýµÕÈ”öôè‰ç*§Ò¡ÅH˜‹˜hp÷œŒ›2APм ¹‡EÀÁ4c•o7UÊë¥ E.»Sì6_<EaqÚ¶¸`üœ{ÂÒy>^b± -7%G=Õ˜ÁµÙÛ™úiÖÏÑmáÐ<¹ª'¤v•MÅìe2®Öh›/Ä¡Pµ4e)!ka(ßñEív/8‚ÚmEŒ¶ž÷ö‹—ì}N_Ù2R:œ¸Ü@æV¨=߶ónà¦ðƒÊ.ÖÕiuè“í“Kõ3*g°êZ¥zÄÔxvŒ´èK7Š
+êã]WýŠ'Š¶­}Ò`smãÀµ¦‰ÀÍÈÖîQ®@0Åv)_”¢7±€¢çÚ)•J/5z Êía„©}¾hÛèáŽu@vc—Zû±ˆ2Ýëó9R@Ú¤Ðó¥F>ʈBóc똯m
+¨•{A,n²×ôNÖZÄ—!|· Ýhö4ØpaÑ€½Ú¼Gâ½Á|¿ÀD—ã…ÞÔòVˆw­N¢¶ÌÛ”%ç²Lá¢<Âãê“Q♳bê¼)ó¾¸mKü€ÁÞO#|ÿðzm1­ùûÈ A>QeT²¸MOêS¶kfX99Š=-:Öû•˜½9yGÄ/ïREÆîÏ`ÑE;Mûæ·êW‘ê‚$–(þñÓ¾åH0(üÎâk¹èXz$a&¹iCÄ ­+±m„Ôäok4g4FÊ bI°<ËÚÿ¼_…gÛP–ãNëá¸Þ
+1œc@ºáäˆTÄÞ;xT ÙÃXyç•êΞ-YˆŒêõöaÞ€08d¥U#ÌQHJ¨Û€l
+Qb„ëudÊÌ£nÑ'sÂSP'¤Wc±e|‡M¾˜ÂΧšu ´àv#Ø-¡O¸DqÎu-ÌÕˆç²XË{ ¯¸VIKî¬p†:ï¡ tz¹Ìž€Î"Ó­3´‚Ï[Åa@Ð w¢²ÓJbJ7$Ûe^Ì …šzíã£)“žMeOs4½‘coÛ~¾pR¿pdaÞªGƒUùžofæ¯á4‹™„îA?nAŽ]‰»ÏŽ¬
+ŒýÉbmRØ» pf:ñO”¢½x˜Ð÷;SÛYØÅ
+=>è&ŸQÄèð:€!¸¼oþ¼EÖVSP\>ÒqBYYIA*u"D°¬Ýã]·j{ŽÄApc+m’ÃýûÖ¹]þ;ÿþqrv)ö[mtéÝfÚM+*.ŸóÄžhÝ0ŒN³6áfÔ®sp×SèR¾Õ9
+¨6Íå«€ì_>õ¹V˜ëýpÏÃ.ÈÞ™[¦&Ÿ³6;Ëq$cÆžß@æV£”=”{¥Ûu´…ÆáÞ£ÇÍ‘"wðÕš?Vçx_®°´af“6Ž)㣮#³n“Ë˳G=er|´8q,ºh‚qÀyßk8O~»…Ÿ;U^3,aš¶:Þ7éiŽÔâsÞÍ‚lPû‚°H>qÄ ‰Ûô™&)µÝ×(ã™×Nc¾°@=[›-îæ­É†óRÔþH€÷8M_F”½=±mXƒè¿ðÃiôÈÿßïÈÖT7ù
+0BE–õrÔ rØy¨ÛèÆ ÕüU‹·Üµùù€mcM‘6±] ÏÝßÓ@<x
+»Ý{ ‡|¿S§(çþùÇñú
+çeà0Øô_»†Aæ»ÎÔ×QÒ[™KŸÈr~:®:ÍÛŸóå¢È3¦²Ò*Ãcx§?Ë{I¡Ñâ
+á“F©ïê–rF€ª¾¸Ö©q1q[– ɵDìj²&d3{´ö QÿvîaêŒöÜŠj9§²Ž|E@ &cR‰„Õíüé !]£Ì!ßѦʬZÇ]wÏ’ß/¬éºyXV®·C-›x#ßÚ×¥vêåÙÌÏK0í…?ò dnÅ£mA•Yn×É"«€êq8¹t¦éW4RÛ„¨¶:rEÍëV;Ÿ \änn$Ô2äùç`c3NàŒ—I’\7 D¯¢tp&±ÖV·ð¶}ÿ­_`[]Å’¶£‘Ÿ@"8t7°}°£µjt-ó³bM«æ%l † Úà?1M<ˆ~ËyŽ¸J¡rïüq|²Òç¥ÒxJ€<ŽNÉ´„¥Å™>wÑ¥—´%2oô=®Bjx5êÖj«:ž„e´,P!Ê•}Hyú'÷ÝL?³/CÖCzZñ{3“cò¹èµ,`à&,œâžÌù›jqËÏø¾ðé÷ÍPR{èÌII¡ž[paKŸÏÎœP¤½áP`ë×v˜]Oò2^Ò“œ$MgzZZ>,ƒüK‰CO–SW÷úÜÏ©’@œÛyBŒ±'è?sñ«r9H) ¹Ä¿ “Ä>ÉŽ—r}Ë ÷
+ÍSyl”+IÐ<¼üÄtm·_õJ6Y&Ì!¸À¯ý¸QcjH.úŽ)¼S_†ÙK˜p¨ï•BA¼_—§•wîºFTÝz{(ùÆç?¡þt¨_[ªsÐÒK êǃ
+w"À¼”ó'ùf4çòp¶ÚÄßxº{ÿšöÀ¾,.zñðªö‘Eú
+-<ñQHÀ9U¨íòú,mðçó‘¼¾ˆeCp_$ª„E3=2lß>žÐ֤¨Ó!e†»Ët°JþH˜³óÔÙû· ȭĬøõ© (;1úM[•‘Ÿ ÞM
+<J\õ2q"ÎZp¯LÇ,½à†’Ia6ìÆj7üQƒdwª?Dn‰ñk‘
+¿=Þ4'ø+"ª‡sX9P%¹†˜½—|CúÏëU룘¸j›yýøEÑ"‰ f0ým‘¼k’ÁÂâN>jí[5ÃîI`WÉlµ81†UFÁæä•–— 0ÞìG=žáw#ô_
+5¢{}2M&Fé1З¢Ï‡¢% ™ÍÉ4ö<¡í&Ñ„Ü¢;XøÅž¢f{žX %NÔ:Íx”¶Y‹ç:³ŒIJ þ$Ža ÈX¶€NÝÇhœHu)_d½d·!ªé3çâ±{Mñr6 Þ€žSYqÕhäà–rÜĶ‰¾²Í»2/Éz|qð-!Óa,+›Ôª¢k ¸C.qS_`°èÄ)ÂÙà¯@Ûõ¬ï“Õðåaaþ°xÿÌ÷ÒóþïƘu0fjݼ;|UŠàÌ¢Öcð—Éâù}µðœ 
+Zlà"µ Ÿ&=}@³¸‹uÜ®
+®ô¡¤rÞ¶f8¼sÕKQR3ø65.’_`T¨}Ð`¢3™mS§“É£L,Ö8ŒdʘûEôD¢Xƒb'š`BºE'÷9k'‹ÌZä8³þWÅε=ÌMí8>fÞÓ°¬€hh4˜×Ë=øn .ƒoIó¸Ïï&ü!¾—5ºy¿IœlÈð˜w² Í˶Jv1¢+Ðp[«Üý}–d`ÜùJOþ6` ÷pÖÝàCMh* ,“v\ÓTJ˜bø]üÿàºA$|,¤<vM?$W3DNòØ=c{ø•à%ìHòµÇ-ƒNx
+ÞȯÂþ É¿9ÄšfÇ1öW!VŠÚ@:ÐiPí*ûœ%ÙŠI±0ìËFµÞ+„t¾«²»Æ[É$,Ù­myHk[õVÞk>j„qc‰HUvUa @z"&ˆì7Š,Ý¥›qˆ%‰RóÅ8EEKºÓƒÙð¦dNÆ
+;íåùPwVN]7x ‡0>2‚E\± dú“|z´¬ÖäÆ‚ËVÓÜ‘}w#ëÆAGž+¬Úá]¸Áwõ¡ä”\ΠôNìùÐs
+1aÈÉV䃶bwT”›T§qÛŵ«„[I·ü]dìjPæ€ÇZ'.tG IÛBç*ö Œ- ·œÔ(3W
+xȶ޿{UkˆDø‚Ùíîý)‚¥å§F™¥\ϸá¬ØDsÚI¬«8BÚ8¦5Ž
+‹Ò¶¥£óà&¬šõ+ÿk§x8ŸAH,HP@Ÿ~Žjz¸Lÿˆ"„æñÊ Šç†C÷¨¥+S<T-8égÂ){ât“•î~ã'k1©±C“8±^ÙAÀòQ¸%RÞ'‹æ:é£jÎIŸ\Œ‹á`còlôÿàtÑ
+úG¿
+jw‚`ty °Ê×zK±Bd›e­°=Ƹ4Ù¥(+гr„PÚ†&ʪ´y) ‘Å|q<ŸÊãå;ä¯2ÔÓUÏQ†­¤ªß¦"ìÆøŽË…ƒ{xéN›«žû‚˜œPü¨}2l”ói – o•¯~ÊXUbd?-– þPÉ€ÀÔ½Fÿè'³oˆŽ ÈVp@ÂcärËœÅAu]¹§nÎ…kîùP]„Äœ½†_”¡£5x™}Ϭ”íxqž—|0V § ×;ÎQE ª¼Jlj¼|‡ÛV „ /öRB ÊJÃÖëQ¹h­}½
+`%ðÏ¿¢G(ðîóN¨¤2¦
+±±£—"éQ–ùgÉO1õ^ò“<þúòªŸŠ~\ô3 ¤(¤U„£œb‘©´·]ímQžÄh7qÎ) 3E§ð£·ã´ B7 ‘M6z𛨔Iqcsb‡ÝæÆùøŠ>ÿ. /ø‘Ëw¶rß+.ƒ&!N vË.! N…7eÚ.y.ô¡“óæi»$í|Ëvh-佃-®êPdT•lÄÈGŠ ÇÊ×õ5LI’åUtõ‹8x…Òn¤ ’¯.T´:o:K\€'˜ æO=ΑG†
+D]GËí’Tä T;ý[&ߺrrýµõ]š‡£/æ’ˆq“7¡YÕ+è.¼?"*½ÎV@t`dx‰‘ÔXc´ÅŽh°y%ÉŠx ­SŽ“åp/µ`C6 ·(©Ê¬n
+»µ‰ÝÚzü¾®«á
+L¢OËñ*ÌNRBïW†LT‚L%ˆ5F߇~?ÔYÓ66™Gê¿‚ÁDl°À˼—€„²¨#(ŸÕZ`¢´H¦ t4~ÐB\žd<a4Rkh}0þSE?ÞŠ`±¼šõ«¼ppú›äâV¢GqW|¨,Ãi2{K?µ iF(À1–>#ð¨/¬&M<ß7¨8Ìec½•T E&áF'èM¸7‚œ¯aÉJ¡0k¼ªNI]—4œ‘›6ß;êK‰1PpPäúÏQ4Sã‘g‰"ÏÄñÜË¡ƒ³’Š²y¸`QY©hŠD®»§½¬¯æ9¹_âGx-³~)úáEhzÊZ”¯ë
+`hÜÜ%Âø cRVEζ.:K#Žâ9ŽB·FàJ½¼g²@Ž\Òª¯â"’ížãx§.Ö‘ÿ.Q‚j±xlãlã® ›
+`Iqù¬
+)Ì‘Nkð
+ ü_Ú"qþ¬QÅRXA‚@X €ºyá.Á`e±7ç´(OC
+¨ìŸalº]K.nòV”– yw‹ïÐ'ˆfÑõp¿˜#Ì=÷ÎÕ[¬Á£Ù 6¨C¼Ì¢Ë2ûQò Ýþ1ÚŠ»4åŠþ H{$&3JPb<=ÝÓQÁÒpÃ"‚½”øQX{¢*Üž³½|ÏŒ"~«ÛH ÿ¾v?‘N‰ë—D„ìuP¸lËœûy:¸Hæì.Ò(xÙ<ñ¬ßVøÚ%C|†+Nq™Bs•'´qRm %óÄ<ºÉ<Á­ÝŸ‘Éc}Y™'±ƒ]P³Íÿlxá·­€ðo'm$'íq%Cx>@ÀS‹ï|0Åàv8/&žúK‰!&KGÏÏQÿÉ8¿ÃMis½ß>
+ÉèIºæŒ£ K²º<a¥RxÉðNãý…_›è¢,a@^ì–q}+2ÆX=„4
+Vöº÷ãh¶é@š”í
+ñœŠó66žTñðrG:I
+ "Ùõµ2Ò[—û·Ñ/±&¼âÃ]PÔ0C$ XÙ™"Ƀ­"+Æ/dl‚N½zEpcú.ŸöÁ­‰m&7NâÏg;n ­.x*î‘›ì$ % :”Ý
+º<PçÞ”[‰y]&#/?
+wyà4•Ut¹u@°3tê“bülÓëùŒ\˜€³?¡~-xcý_?.5h ¥ÃÞöÐK ¡.&™öí$*Æ»žô¥‚±r%L# ÷'Wðªu +®H0Õ{3Ÿ°>†~Ü =%RZxBü’ ¨ÁI1þ¤DÑоÓ VM‰·É5ÑÏkHi0
+=Í3™š¬©t¡îH0oyÊ$em‰2”'ªZò
+{`fÚÒ@›—’_?þýƒŸ³Õ8'Zœ´^þ¿MQ0¨HEŽóRô×­H2Îs¹…¢Ö<EK/ÁNq“šò×¢º“ ®á l¬ŸƒØ°p\ýâE{àÝð$Ø–òÇEŸÇ5,Ý£êºÿ¸èÖ‚ãþ¬ëfdÇðÛM;t/ZpIGóºVúR‚ËÀ€N5@^ù^" $y¿jžRE(8î‹^ÇⳜ²ÎCgßç-á͛рÜѪrÈ¿ìT.6CGmÑ›†¤Tg³*O^xV Q<Ö¦ŒØêÔ ÃÐeXH+YMÄ½Ô ÙI¸(Q//‚Æ,ñŽ=jãqè$DI‚ðÞÌH•UdͤŸ Ö·;jtë‹vùÛN–Mëué<ºqÛ£`)Z¹·üþåÂò–WNrŽ«ÃàŠÜ•´4¾ƒ
+}§¤ˆâiÐ:ydé1Tì×íUÁĽ€Ç²^a ¦ÁÏ`V†½ú©éŸ¹PEýä 1ihq êàk\ÏJ
+SâÒù%0¸(æÊ•[*5¥S“A¤ý‡ñ×1"ƒeè|eÉý4X¿H½èú1"·"˜¥³øzÐ<ŒxœÖ"òî[˜¥„¹‘ÖháÛ:Ù:6¶nëEÐþÒ’·é}˜7’XÑÿ8ß)š9;ÂÄf@Î$ƒ–Ãn”¤¹5—&±q
+¢…WÕrJº¸wàXÛ+L¼ó(50=;Õ„Éà1¿œbïv ËH{ƺZËÚ0º
+
+(
+Éý|‡ÀKkP†úÿà•
+lQŽÿä
+U°e 8>Á*•Ô‡PY† IÖŸípv[>– Ç¿¬{ÕgÞÆÞA·þ¢ÇŽRÄPÄ‘¢ ÿV†ußkg¾ËjËa`±±«âµ!ÜßÓ"RÓÅ„•ë³Àðìr«ãe
+â%»"³O
+5Ô2pªz‘`ÍZqº}†‚(ôÅX†— 6¼3Ioõ,1ª
+c„ÿû„Ÿ:jî\%eEéÚhç¨Ø€0§íÓ銑Ž-“§cðÈyY G¨Y dnJ+-%Fªò!c½¼c.9ì¸2 Ô«‰h›Œ 0ÈjЦæí±‰ËP3¹|G„¡\reLcÓâ ÏŠv5‰CÛqŸqÀR”5ôãælLzQ\«‡X—¶”£†÷ÿ÷ƒËŽˆ!äië–fÂΡ¦¯¿]ÅukêQïôúMØ|§‘F5Ø¿>Å\.ûþ$¢ß+pJ©/¢ÏZâ¾aî!¥®å„ѪJwþäþo­¸ãÀþѱlfp—Ý·ö29†Æ„ÿí‚2Í ÆÑ0¢MAÅþ¥®fÓb`¶²º\·Aaù©°ØáSÐÕ‰¹×}
+‚¸qgZË‘ï”&v"Ù$EÙæäx:%¸3· õ¨Ú'Àà“´ì¦Ö›¿ö=2 -¦$µÁïAÁìREÆj0ÀYVµîE
+ Zc`Oˆ¥ÚU}™µBHÅ@<Q%@&¦6èÞ q˜R¾ªøš8EƒÔ  ÙA覉?Ïòº]ŽAb%—Ú©BŠ”UÊ[DÒ@äÄa\Y”Ÿx£ž ’^¼˜‚OæŽy®B„±çÒ,•äÞ´øå*;i¹¡Ð¤a:Ь^1Ó ÒZ˜—7ä‡]“,ÀFÖòšwﲡµŽ¥ÝYwѽ:n?/§p\EÚòNÎÐYƒÛÎ!¶LÞ™-É]­5Ys=á$­Hl¶s¸5¥ŽÎ"H!O²âsÙ•
+=iMÛÕÏ)ÿ„ì«’vSf,…ÓÉ·s0YlAð#­è„¼ (\áýiõÑhº&£ÓÏUd‡µûAu 4%æe-N«GqçC:‡µ
+NÅ nÆ ÙDpé –Æîq*‘É2¢€°1Å—ìs†¬&{ «š-ØüÉæ ¡¯Ûúèìu°Q¯QÖ¢œ›¤Å)øs;&iU@ö%'/‡Û¯ìçCø;Ë`£.„÷ËÓ‡jé@Ö,ú¬Ž'>¾ƒ®ôÿý‰nmy»pÇfÞäír”+)È*Šê[O?9ÊÅw¼•çqå£Þ@žrÝ®ût“7t‘7 ˆÙÌò¼¥¹òdöÜ#ž×¸Ì/½V1iiÓ8 P_WNp®À¦Ê
+<)\!­tÊ`ch©¸
+ÇþÅ ýr8ü"p,Nðlµ…ö oìM¬.øÙŸŽ¤¼ëû“á`¾Â¼ S%ã܉e+\÷ põ‹•ÿ‡|còu’UšÈ’Òyå]ß*×bˆRÄ0|}ŸS £œb¾ë[lð*Ù¬ì3õøƒw}Â]»buâ½è› ºê›{Ù›¾)buZÕ²u4ç]ÞðÄ‘ž—V´Ÿ3®òFþ*Û¡„ ÍäÊ c@ÚzgÊ’§]‚ä¦=jpÑô”k_•2G0ãÒ¡‡s"r¥“ c/òV1„h?i¥m^å­¬äàçP¯tây“7aè˜ÒÐWÞ
+ç‰3Ï€_å ƒ[Ë콌äÊŒÛiÒ¦”|W·¢e%%q %÷ÕM¼-Bª´îŒ®ºÝ¨ý®nä¦nèÆ´th[­-– ¼ƒ<uûWÙÚê&¥$±”æq¸Oåd©›z›@E¿˜-±ø€Áû
+Bü9õ±6C{UÄ'õ"Á?WѲžŸŽ³…СhTĈ@ñ —¦•`àt± È8šØ#XÉ dK9蔾›%ŸE.ƒu+˜Ó&Á‘E¹A0ÐMv % ™Ól8IÉÿ‚¨Þx©B…­ÿP[D3vµ«V« uˆöÀêEüSß!c_6cܳq“±hî
+ÿjâ¹ê¢=Œ…}Õà p2ûãîÑĪ€¾µè¾i.·'³\6€J…*KpÊD'´°´ö0NK%šLÑîä?Ë=Ñ ´FeÓdvóEhöÛ]_!ûª¢Aí5ƒßÎa׃x;?íŽ È|•7Ö SÅ9ÉãÜ}k®cËë3:ac…6¾¢…1Èì.M=.ª7FºÐMÂœˆì
+ßq„}Ö‡cX$Iæw¤'(•‘öqú”M‚ä5ÍŸEœfÓòJ†Ûîwnª²%KÏØq—+hIÍ=Eµõö3è8ö,¹<åª5-ÀŠ³)…Càò¾v Ð< C¾I‘ÂóQMï@Œù[-A’œ·ßÎaƒCZY¿ø3« \fç’˜Å×e XKÚ±LlŸÜŽD EëœDÒeÕùý¤˜¡à”®Z,~Hy‰t±‚„9 œ\ì&^]µî2³‡’+Xm:†õ¬1qˆ
+œfŠŒqï˜Z‚åUÇ^ЂŒš­flglbD3áÅl´Ã»G'›ü¶ÎaTÂëÆ9•%6±Ö‘çæ#nZæ`³ÓËË ˆ,$ò‡òg÷Ih¸˜¥ÑǪí¾éš<^+«bxyùÅá ¢7'ßá–)ð2¤½í4ߥÜ8í1˜ ¼AØNéÖ4@R(ŠùÁzÍ„Éó
+lþ¾Šµfà†,µ™z9™ÄÓjŒÛ LÉ/¥#ƒ$ˆBÃˇ۷ ‘߇ÙË@yŸŸõázÌexÉ÷--íÛ¯¢¬Ô³!Þ˘B[IHÖk4áì`ë 4#…£×¹ßü
+Dh‹”_p“£JIÈð É ‡ƒ(’T*
+¹=ÞÔ('ý—xØÓ£9Ì«DOùòÖé¿Ü«3$®Úz¯ß Dù+NW´”>ɵ†¬
+l¯ïL!}t%Ô¶Q’#­jÑÔÌ(ÑÕtî? dP`ú|âA•zó@J!˜Õ×Ôÿ{4‘,OÌQ[öEÜEÒÆ™= p–RØVºÚÌ¿>ñ¢¿±­ä‰]€
+òðÈ>`[gø?åe’iŽCáä|‚„DÍë¬e£W®ûoû{"e´ÿPÜ 8°_h È7`HLĈŒeÅópHzSS=çó÷£g}YoØgcÆö9PeQ®| ‚Øe&à¤TüY雌qG7hþF#„Ó€¯ƒ.ÛéWøîA1Õ3?Ý*ð²ÝÏ@™ÂAûäÌ{Òo@Œá‚n—¸áÁ®@wCÀH¼Èë%fŠ^–wr™ˆþ_Q‹®7µˆœ¬¯ÃßÕ&ÍuIu‚ÈSÂ9¼Ù
+˜šÇ¯ ðXgý†"04ÔÄs IŸ.ªmH1”³lB¹œ×w#wù_“ë]:éQ‘é¹Þ`lº±¼P*Ç„lÚWñh›„£Xò¯~Ý@b %rÃK5oQx½ô!‚žÁ™ˆºC2ƒýªî舢Øa´{#àáÊ}p(-RMa è`?׆þ&Dr9ëôÒ¦¸ùŽêU•|-kç0d wÁLþF$¸‡Š^Bï”;ªh’ØúšK^¹âçö2L~E§›çÄ0Žšœÿ¼A*Κz*»@¼Aé©Æe\|D>œ:Íѯ˜©íÐø§É˜Ñ¦Š|mˆ¢×䔩] ¾Õs¦.ë0Û4­ønæ7·2å^üîï&'¾UrQà<yêVèA·BH;h/œ~(àì•XÃm‹C8k¡½ ³¾˜î¤tÁ€ì­^Tß'3S'ußÕs™¨+ø†¬ÑƒEýâžÀ:[aÆZ¬¢¬4‡`½Ø
+ògÔ.G´;ÁÇž¹èØë4µÏ…±>ŸzÁÛ«9¢Ã.-eE‡ÐêpT†„Î¥(R`¯dƒëaOE*ã{A¢0 _ #u¿÷SÒ|[ÑÒ\ŠÕz0-WâW ù KlHY‹ÊC÷#1\qðÏÎVB5$UƒÇHålõïÃH¡2Ó¯Øk¡û°ëÉŠ,Ì?o@Ïg«2\œGÕ_þзí>r&?úËZOMr¥_±„¼þ¾‚^\›-z”Øšx'űƒ.Û½Üïg ‹¯q7èCVÆòOA7¯õú0ÐçO*®‡ÉM¤I†r ]È•÷¨ Œ3 ä}uÉN1¡D¤­ @øP‘üŠù Ñ>ø—£1]2 »ƒÆN‰ű'‡œ!/™<K9HŠ¨œ:ݸñ­©‘I¸°C°3,‡þ ÔX¤`ŠœHZ±Ä«rÃô50cÁ÷UÄlè’·[fSŒaRÅ Šƒ™<ï*‰‚qXèìTmè4KŽÏA°Ãf2|e¯#3Z²ÞѵJò=ù†ÐÈmÑ.\´^ ¾U&TÁ’IK½Y'ÁθLxýk«YR›¹N¬ÒŠ8Wq+T¯[@°i";%(R L/åÄb9ŠÌOS‹©ú{º…„YÒ
+Ë_¸²ÆQ *ÄO±ù:Ñ¡âõSÂŒ;c+„Uˆ:œ‡h¼„árü Òé<H›“ÒÉšY
+ÖåžáÁqNÔâÔ†Íy>4¸ºóÈH®”½#óR8*Œ ßóuðò6gT˜QXô1
+bZ”Ýz;[µÉ”!›šø
+¹Ê ²£CPÝ:3,Ó(ˆ.EFàÎ- Yžß½†K ypöi%¼1 º­I‡¸ï^öÁWf:¡Çiì‚3fÎ÷Vr:sNÒ‰pÁFM|ãùVÊ1X_8^ÑÊAŒÄO¯"œ{bm-H S[hݵ$º8$cÈéLæ" ´\Gað3mÇRÑA®Xã"‘‹RhcŒ ó'_ Í‘ÿ‡5âÈ8s}®NÈŒ–¢!þ>½ï¡%£”PÆ5‹ŠTR¿7`Q¬nÑÏJ¥²÷0gd&ˆ—‚d$q[«ô2¦óBJà|S±Ò‰z†—øf*H}Qp<©÷h³
+ýÉTcLGgòf
+™ÓOÓøæ¦\—ó†eP¯¡i¥Ìsâdz+Á|§Î{²loë£áõâQ,Ú‘‰Ž<órC~LP2Å<ôo¨šã$ªOºŽÁÑqžg>ÒèÌ s£²És^­žÒ!ÝL!=m§øFû— d±TÁÍe£%ˆ‰<ùbâ!Ž‚@!Ùg«U+Ñ cÞÞ¬óH¤7ÈK"½À÷C·_·š˜j9oƒ…Iô¶7.%‘N¹"©L?q{‡8Òâ½_!ß¼øòMÿ¼¹Õ7ÐçÕ>s/Õ ‹yy‡"2¦PÚGhõ«¼)fÝêÕÕòd<|sšt{J(¨á²âÕ‡.O ‚}³8_
+åÖ¢lÝqþrºAÚá(1È n黀\–á ¦çáÔq)¨YžWœÓ±œ…Öq"l !<›ÏN&bf‚ÑœH|SBÐåÞšû®-c±Z­ŸîZˆj[\µ®€i˜LM[>Ú@aLf”TrÞ“ÄĈ.ʿ׋¨Ax@Ã/á{ð…}_i*?Áq|¶§ÇÑ ’6}èó,Š'Ú„³ ’pô&Gµ!2›H:|rœ³ ­¾¼¸ÚH6IùÇí4òXê ”6…Ü=oE?RH†ƒÈ_ÊŠÃüA!‘©J“Êk
+³—H*ô:ó u–&õÍ_–Ù⬻(ü<ËPôÎT÷IœqYt '´s\à
+x(ñ¸§0t(<5Cœépö^Š%Q‰ t)Ÿ@ §’dzF€rI(DÒ¬àÉeñI@¦¢Ñf¡õ*Ïzì.÷ÉB Gže‘0I°KlÄq5+H¹öÎÝ pïI¬œþe:Ž¼<è\%·.6¨ˆýáü‡0<@Ÿï@IMϘð«ÀpŒi«âÄVn]Š³âàdN‰éhyò
+ž›“¡wÈ7«âX¯ iqx«õA ¬júú&×Ȩ֔6Ôx(ù(?wòmŠ1ÏswM‹ä¤¼@Mè­°MuðMA€Êb4È'kyÛ,ÔR%¶õcÂsÝL² :…²Ýà¼Ì®ž“ÜG¯á¿ää×ôé 1 íÄÕ{¥2Diì½&nYR>j×â’Õ!|éŒÝÚ}Õ†y÷Í_@ꊞ%G…KÔÛ:@X†¦9ôõ×MåN¼ÎÍ SL…)‚V;7€p£™ùäDüøÕñèc˸­0µ¤MŒÁ`æ—9§@‹âÝÁ6‰µŒù\qs½
+CÄèE#‹L–Æs$ŽûÇŽò¦5™X)²uB+\10ÈAZÁ£Ø$r®q„€tyÕ!ô­˜dl"ÒÎ¥¾Aþüúϯüñz… ËT>eþìé,+©¦
+ûW á?o@²[âj¤A‚O+àMEÜh:·‡YW>kÝ!ò#•ªBYÿe¼Ì‘ã0b(zÞ±VïKè’C¥>ÊŽ¨Ð÷÷û
+ËEXߺª"^OITiwì'ìÒlè 7‚;ž9¶j\K¦»1½ÿD˜àk1Jh1U/v‚ª-ìÆ,QSel°xh›Ÿ)]ü}*•E á=ñræ>0…òé_“ÖY峧¢(I…¹¾Æ‹Xþà¢Ëì&yÓ€˜ï÷†¡U Vß*dFàî*lF‡ÐÈÝÆ¥è1`è8ð.+ñ>§ú®J¦Êzö8äV¼¥áQÂÍ!'f½û Õ•µÜ×61Ìf7D7¹03Ê7Wøã,Æ>“rÓ[ò¿ÍoÔkÉ'Œ
+ΈøÊhV!aÃTâÍ lƒ²˜ß ŒiŠùÀN³¹z£7 ¥UàVhŽ’9b´¡ÐìSƒöÀ?²m—¸
+”ÿÜx㊘zàæJ¬³X9çÊ“E _§ÿUyÅKÄ<ù@ñÆ=}01.û S³€;Ð%d_F ÞX†?ÃÇÒ%ÞÐâ«zÉ[cí~‰*Uj2`G‹‹"Ò×ZS^ƒ,’1´±(G(”‰ª'.Ŧt
+j’©ó­ðM<X–+:šždq…äâ[-é,
+®Qwø" \ÓwUJ:I @#tã‰Úè¹b —È4þ† /%å¼.ÅåßÑ™d{]úœI(‘ÿL9Ø’é#†¬^Î:EzŽ<—˜SŒT…¡Íë†S(i¢Šãus™Ö’óaedTÑ
+ÔE»è‘Þƒ3 +Yç«ìÒÚi:G×éÈå#âp%ÜQ`'‰hdµÑ·bâ8‰Þyœ
+q‰aY‰§‰0Ìcr!ú$?9Jl·Ý<{ôÓØ
+T0ÀÙßçüQ(Šòs‡FŒ]ˆ,PÙJ´"º™ÕjÇû‚‡…²"äÎ
+t6e%öt‚¯pheyÒçÕé=NÞ'Çøý:žt¸‹&¬cÒ¼Ÿ[ÚܙĹǥÈJh5ÑEnø¬#/J;ws»#È ~ ;”¨@Ôp6i}¸¢Ó!\¶}j0CcR|™Eð0•ÄyF Û’¶ˆŒÛÏ"û*ÍÏåRâ[ Yåñk‰upÍSÏSá!o'ç#¿rîwât2 òÜ7¿ƒˆn¹(ˆ¾‰’^Ç">ÂRC4Ã]³‹$eÞpLl&‡¤„¤ì’·ÈÚ(,æ~è†i…^”EV¬APr?Xnç
+‰?ú¥H%¦„«¯vÃb98 Òi繑M[ùÀºO‘H#¢\^´T±ãJ ¶dÅ3ÝìN¨QhAï±±V¢×VJE–½dØIr4Òz2ß8ª«{¦ç·¢OŠdo˜zŒX{÷"ÆÂØ^”%íJ©Í«•¡D1CÓë@‚(&l‘˜+A@…kÅÇûà÷Du{Ÿ‰Tq­yÄx€þ|Kè3OÅ™j{Ò«õŸ
+H‰Œ—Qr7 DO ;èÙ" ¿•OÝÂUù²ïÿ›rfí`J²ËIiÕ;CF·N-½K“Qßÿ’Ñ­÷Ùµ–êVß?Þþy+ï¿i³Ç¬bƒßÕÚf@Ç£ˆÍYT‹©¾ÿY VEdÖ¦Õ[
+HÑ"C[óªŒQøRs³é·Î±ª‹ôº!}˜«5½´÷_oê¥öø
+ŸëÉæœp¬çh<±pœ. bÑùëcÎ9î â£6ïÞËp
+úcçϘ¥Myÿ}’×*E¹ÿzYã§N­×±«í­ST)ÓeŸ'^;Ìjévô£RÊZ‹7:ôk™‹‹rÞÚñ˜bS欵7é«Èê½ëàÃÝ}P§Ú'm¶b 1ôkç_ ñ¦¨(0é\È㋃xåEó8J¯­ªöV‹,ëô
+ú´P^Ê­›Äaã=ãֵÒ®Çc¬q%Þ4D¤QJT«ìòVªÆ?º!|Ýa®©û†¸6djôäã-Ù£uè•WóòÏ´Â?I›¥ÀÑ
+]+2 æÈU„sí˜JóÆä`1'õ⬢LH ÖKsqoÇÈTÁ4<XÑh ¯§†Ó6ˆ¶Ñã(äج`€èÍä0~Œ ¿¤€¥Sý ™­òkº‰ó‚ج¨j} È]wÓ/áÕeÒv¡c~œ¸Ñv†“Á·Í8ЉÕ~œ×øˆ~Ør¼ÊŠÖÉà BCN-‹5Pös\'jÁ,Z= Õc Zå#£ Óþ[:lòzò‚“é\|ŒãÄ´SùX™½ÕMG©¥NƒP§vAVÞƒXù¾}4F¯›ø
+äñÓØòÇ\££˜ŸD+á¡úÍ“0Z –C:o@FtÃ!°˜Çv×Ëñ V HÃpÌÑn@4!\ET—(¹ leë4XKq» _Ÿró¤ù ›pÌÐï´÷3H ˜äÁa*6)1_Œ …ïLÖŸÐåPhÓdàX*¸¶»“_Al>©Qcñ]_÷#ªfßqx{e& F(Œ\'¾ÄcòåÙì€Æ˜n¬¡ Â=£ÙBu·HEn4Ñ%$€Sö£Äf=§»ÀI*^[—õšÆÿ„Ò<µ&Sk6v¸êS4„ÑGn ä×*"oAb©*è›ç WèI„ý–hMÖ
+n…Ì0ð(:rþ‘öëeB~ïótay6ùN#Pž |F»ï«Ï¥|µÕL´¢€\͇~=S­]e¼UÜž;Ôvó˜»Ùc†N QÌ–ÄÖ”Ýs†{×­ïqH:Ôèxf ƒh´ëäN߉ƒõ:vC1g´;¬J9ª¬D—^"ÛVO!/4ýxË@ áùsÊYÒ}ü” áõu?}†|¾^@ŸgðSy°‹ÈT riσ ʯ„ 9‹@ȼÅÈ:mΖ? L7ßÊyêS|²"¹(oUoYìÂøSW~ê¥gn; 0µ†ÛÖ3¾æ> ¿3Â{1¯% ‡Ÿ7 ×ÜH£Ñ,¢Y¼eÁ/ ðo#<³à„[à^ØŽ3}@¢œ•r²ùkû¨0Jg†_e°ü¦Oßôûû€†1`cîv©¯¹P˜4¾Ê¦­™<ì–•ÑQY³Ü„täÞCòóàˆBÁåêYðR9e>²à—B^ƒ_º¿t¡"SCWKœR²àWçå˜
+šú™—à—Pú5ùE#ˆü(48lIï¯É–1®D‚•[š c,¼pdÒ·,û1ҥĦ©{7_³ T…•±›fÙOãÁĆÂËK·,ûÈE(Tóì—€®¹.©òA¯é/}¡uP¥‡ÞÙÐ,þ…`²â‘C‰”•Ç¿5€†‚m§_øÿ€¬lÅr[Û/Ém1ë´¸„WOcò’ÿB^ '!8Õž>öĦZ=#â—üJz×~:²×üÓ¸E¯*gÞúšÿ€t.ØäžÅ¿„„×øÌ`%ózNtf›×øÇ“EvS,IÒ_(<±·2‘øÿÜH}+§çR–ùdDF€¶#J!=˜
+a°ÙÑýØÈpå™j¡ºoˆùã¥î’¼}O^ ½ñ¡2ˆµ­—›œCÓT9‡éÁ©y34°Ï€ñÈ®±–Ðs‚>'ðõ9T‚•Ù—Ê“ƒèDä Ï¶ÛÉWè¯:™J+™›^ÿë¦#3ʯŽ›óNø‹ª„|'ÜB¿p#˜˜C›#B›„a²°ó¿~ÂãϧÙ(hE¢Q‰öþ¹¡ƒT%ŸJh 6# Z?MÂ×ÜX¨^¥:¥>ÛÑ»E›µãè‘V˜&˜øyÇ¡…­g|W©q]®‘Âfšy€ H#Çí=ƒlÎ#資µ¶íŒ/Ï¡¡°,Xxf§ÑÎÆ!÷X°MŒ„O¶“>äY 6ÆÜüä<ƒN‚EÏû%6²à„bÌ ¸ó$?‘Û@<–}NFìG ÿsÌa§ø4•!™ÿ‰2šE.
+Ÿ–úŸ¤Wÿ“´5yÎ+7¦`éB¬JÊ1ü›‘Ͳ]Ý•«HT`9Gœïã#\ðI|Äê‚¥êÌø4©z¼ê›ñŠI„-ôˆ%˜qßÇþ—ñ2I®ëØè
+þ´FõÍØSîÂSqÿSŸPïK÷C²ÃÖ­Ù<¨á뛢¹ËÄ‹+Ž°”XÑCº8sðÇ$Ô©‹rD*)ÓcL¡ðãƒóÐ%Œ kÃh˜Q½È½ï *Tfð ,¶é&RK€òP·©].]M46Ãä†~$%°x<[ït®>…-a»ÜÏ…eƒIáÑ<8ߎa-Á…µ5Ø Ã¦PÐõcJ
+ÁßJMÛ‰ ˆ©b]=ÆQ3ñ
+MÜGð³3Qƒiv _XCVa´øÈæ&š_ÙVe¹À–ÌKÚHE3wƒM¤W·)š{$,Im/R&ÔÎAèÜÞ¾~B ÈÀï´j›u+ùMNãS¢G‚µ+OíøÄ æ³éŠn̽ßf%Α‰¬D^H€œÑbR©eLÏX¬£Â:÷õ±QU
+–„Óòu‰±t™4”euÜ]È€ƒñ߸k,Õ-0©B¡–ýcË`°vŠ¼Ñ­d@ßP9”™Ã±ÐÉãš=¾DFÅKC"鼜ËwTmKŠ”à™¹‚ž4ŽUÓµ‘“Ñ­Dq ÃU«>¿MXN–Êååm^Š~^‹8»Hƒ¤ß}ú#GüÇ']Ýe±kßßVÙç:ÌRíK‹&ÃCò†À?ÌeÁõÔÄZïé;Žo‡@íŠL l›ñß#_Çù(qäôÌÖC±¥Ÿ¾¦a¶(ŒwP›0³U/)Â'6žÝŽ‘s·Åzm‰OÉ]A òÌ“bÕà žó àç0~|ɬ{CÙòZ·
+/+1B¤éµ Ï’,yJ·C@ÏãË‚)À3@“¹¿ˆÿ%‹2„¼UE«°¼ÿ'H°Æ‹x|‰OyZ×ø€7†ákëÄÇ Š(zš|äïµ¹qîFÄm¼hÒ'Ã`i'ª5XÊÇ/£;®À)×áI­†.`tœ«¯ÂDnK•ñ}‘>ÊŠ Ñä| ·¹ sÎV ñ€‚ ¢Üb¡åâjðBw¢/„5£1k~W‚+Cþ‘¨€Þ{ ¾ëÔ¥ý)öÉ” ãÀ2„ñê¹¢äI¿9•L ¹­'ÌÌ$5•6iÁªŠ³€6…¿%áÖÚBÜtõÍsV“)ä™UÎ7[ê˜
+ù™¯n×g¸,½‰Ûðœ®@”Ïb‚èRº¾äúÄœÚ:Ï IKçHÀÙJ·³DÛ,›Š9Ž‡Ã±ð6¸ªñ©ç0%þ©-̳Ë"ìë9àÆt wG3¢ "±•ãtU
+uïVOAÌ2¦×+1
+’¦†‰Ý>Wd* -Š·Nó‹¥D´õ¼d¤ ÓK'è7¨„}gb<¶@ÿêü;Åêë» ¬v
+Ó÷˜oa¤ùs§‹ýÆPº1â+CË¢„ÈG–
+ÃÊQŠ×5ì²x“fG¯õFÈus«ö•ö鶎Êñ¶êLàÝ–·Øø¼=¯šËÌZZ ÎõÝy×\ŠÐ€¿`»z+r$fÆŽ‚§u4wJ
+ŒMÁÕ)êý$éÀ«gß/åœ,1zväÐf‹\¬R⶙—.í
+^É^ 2Bn“ƒÂÀö-}/‚^Yí$t(C|Úu@-T:$;õ¯‹º´£ñE)œïû­ˆ-P Óˆ ž"wØ›¿ ô°¥FwÝJ°²OyF°hÂ/ö’P"ÞV¾Î Àk†ÔbA7¹Ù^û:‹7{T
+ï%Vä‘«Ë„ÀàuÈÉ0Þ‹.3ÏÞÄ, èokû÷óo–ôóĘ$-b/ðÅé Ÿ
+Š×øm­35ž}q¼¨õ·"•4ÉW)
+%iø¤uM(£dXWj£E öˆ©AëNx9•‡ù{'<͸²«v
+ÿ1^&ÉuÜJ]ö 0Ð7cz¨]hJíêsÄs¼ª,‹v„ÿ—˜D™7oÃï¨DV*€˜ÀbÑùC“‹b€â‘/É+Ì(ªÕ”( ÀŠüNë·’¯U’Š›î†Ro%*Ì.Ê\Ï`æÇ
+0E…¸(Dε-ÐÑ3mɘˆÀ¿«1c9ݬ•(ªxüt ÚQ;§ÊšðÍ2¦•°¡ˆ&žz¨@éÐì¤åÉö¥k‘¤>(cÑ«ØÖ9 ¿‹gx¦•Dí= jÕ*5È ö|9¹J§ì¥}
+gÕÈ
+
+œ‹;Á´ÐÒa„’àÚƒW“ô;%mM!ð
+p*|`¾¨‰^P¤x3³èrÀ±Èð´NYŒO|‰
+)¾íî1@PNüˆõ÷c
+ãU¥¶ÑÎm¼¥c•$Øšæác °/¤á2–H2¢Œ@…N
+œ³w¥kï÷}îEoûõ!^a3zOKØwi½V…©{e—^W¤ƒ6v‘TÊT"^$OÄ ,çÊXvœ\ò°!
+㊮ËÀßà h&!2y`á«£ÄnÓǬkú¤5즊äÕšB'¦tƇ“
+ÑÍ:b²^é èQ ŠÎƒ™u¶€?5~G(c T”GROØÂa£Ã€si& ª’¦w ä©Oâ*}¸Rö=;%úôXØIl!ºçÀÄ€¨ÃO/ñ³°™x°+@L3ýl°ÈõYyþeß¼"œ@‡¥Yfør®‰yEìÔÀœ,n^˜²na¸Ø~•ˆð—¶áOyÙ/Ï:$³f¥š0¬cýÑÇ:‡3¤Ìæqþj2?ÇC}%»Ú±‡[…HÕ§s®#¿ÝæçUwÞºsƒñ­ÅNÅÛ¤~ca´ZÁV+kX¬<Jí þÏC\¡{£h°ŽJ
+8̇öÒNÁ€˜‚\%ØJ;KøÖèÈdÐ&XîÕ"2jÅâ%Û lê2ñ‰î^¢T€E/:æÍüÍ.Œ¡ã¸.¤¼{&pÁ®ãïЄ0Îm*®,(Ð •Ì¼íÜìÂÈ~#RâoÃ^äü¡òd›=v):tV[ÝÉú-– Š&4 ëFjJyàäl8´8fýó‡Wt™ü×CQ×+ñù½ö´>ÆfÁÏÈOjéø–‚펩õýp0Æõ &Õ ¬ËçpvéÖãΉvSnÂB—eº˜Çº÷˜hS…V”K²ò¸ò–Ï*¸nƒ|ÆŽ Î1dM6¡ÇÏ]H‡¸'Ì\Üm8©*@œ¡Ž¬0
+#·åB’Au… ›¹€¤–”\×9ê.%ôgÓ&“6Üe,¶ ׂ–é1VÍó©¿l§y©î&ÄÔNYPz‘ðôDŠ?«ˆ¤‚$^^[¹€ÃÐ-±ZV•ò†¢Gó6‚…É:Nÿa³Æü@¢À7οíìt;g~p1f¥5«“W @•´“g؈6d[ç¼St÷H\¿Ìܱ²¡<ø6§èŸü¢ÀnSÃZ‡†ø —žÒ´]¸¡6%T¸d•ðÛØQÞQNºÄ'AúàŽLøÊXoÁÑŒÊ.ÉXĸJ”Uv§d
+¬$D}hîr cŠšªå.#šM‹/*0¥¾ÎhÊ }òùõ¡N4ÉPy©Ã¬uÎ!™­s*­Î› rB˜ˆDÌãÀý"£ŸîN83Ç
+v?e˜°Ø9€²@ÝÞ@œ¯ºÃVôf% O[ƒj÷@Í~J&FOm:.¨ó”(êh›<5¡UWXb›(ñ!•F.^Kš½ñë.Œ ŸÉâ¡èãZÄÇ §"ÞYÉßÅIžm¡\a@ ²¦;Ï ”
+›È¬®šMì H$v0Šì·Ä®¯H’ìºP”BCœRZßÜG{ä% §VŠWì<”ø§qü5Ë6W¾žLG£ƒ­ˆ¢íMñÄ×yÒE`¼ãzžæÉB` Õ™*ø[Q‹Î­õóÅÂNÛÄT£„ÍR©ÿ‹X‰00"O|걈g²¯´Œ_6ÿZ¼Ú6Ÿ?ŽÖ ½œÕqòþª¢4 á#µD3ÚþàCÉ;ÜŠX£Íí¨Jyu¹vx’‹ñfµ[‰]˜ e»â6»
+ôãG,Df‹ºÈ³‚·¹á¤}2"†.¶LbŒ±üô„üÐ&åXOPÃjNô(„sˆoSÉg÷¸|òÊñ)l0ùllcÿ!O‘›Bö`‡|bVô†6ó«’΂ ®r0NÉÏEË8™„’ÝðÙ¸<J0iÇŸB´¹’ ’“×
+05ä×4ì愼U‰dG à~|Š™ª[IÕe±(Ö4F®â.€Ï4Š/ÄCÑÇ‹¢IÀWš}£ ÿ2}â7/‘²6X”â‚ác}¦œ]
+D…‹î5b×ÌZð¹®°y( %‡IfïÀÒ—ór-Pà饾X†GÛÊ,±,ààCg,Ãûe–ú6¬˜S<[h­){¹D1/J¾Xñï8ö¿þó–Þÿõß·¿þ÷–ßÿý‚¾±«U Ïãì3–Bڼѿ_VÄí ®ÙТ})ªnMZI ùLî–Ë–åôMµiØ%Šûï¨ 3Ù©€ÃtWºc2 –
+\©£* \‡vœ(Ñm˜æ°’¥þ­k ¤q0bÒ’’î÷·{‘®C·8¬mŽÝ¹nYM\j—¢ïáÀh
+2©|qU(ƒ"^R.2â…²×aÇ&¹pù„åÖ¢'R«…h K qÿótÎúÖd¾1¶%îR»ßE¯>SF*Í•rìè”g&`ˆJ|)I/ʈwpÚÀ©›M½ÑÁReheÂ’ÑÝ<v-Œˆ7ÜUð7Ýj4\ÚÁÊ´3à ™cmdJi·òŠ•> {ðôÞg±W¨5›ãQݾ.q™á¢ ™ àÜÀ¬ð„€’F
+€Îç°ÅsH•å)’/Ä#ÍQž&é‰×5@:+9IóRâŸÊ£Ë§°£¿8²†àr—ý‹+½Aû߯%Ýì,4€úi£
+ÓG认YQK† jÓ*W®«JªŒ•–¥õŠ3ù• JÀÅÚÞü±>C@±Šs[iíh^ÂËÙ%[Å(a¦r{mDbú-„>ÍÝTH£MÊ5¹L²¤t™žm-&Ò,¢®v)RÉFÄ*
+”Ât¢¥¬8JR†d7v[·VT4RÉ0ⱋ'hˆ§AåKi+ÈlÊ6U9;EJªÇ2ùÃ0U¡ÌZ[I.6Æyí¼9_:¾'ÈþV¤ë0vCi~ØÃA¦÷§ øgч·p·Š“ƒÞ×­H%ºmem`àP–’̲·¥Q«¤£bÅÁ ù°5£…; üiŸjä@pzð–!ª 'ÿ¯fŽËJ1ñ©ŽoÚ9i|¯J&öÛ’„[ãKÔz²<dÌoœÛV¬„mû TüŸñ2I’ãØè þêm1kmy n»ï¿ýÏHÒ˜‰²n3I&Q¨ÈÀáÃ7èúë">‰Äɘ
+K$¢øUõÜßÕ(n&ÍŽ¶Ù1è.£UuG<Ë>Tù‰ónáæÁ&ºôð胫#{÷h2$ÎÀƒ
+]
+Ö•p(e){XÒ-yA8uxð¿ƒã"ãLƒÒû­è3,âc˜ýÞ‰ºì˜éÍš B~Ÿ¶_eÎ<™ƒÚîқȣÇ2¢(×þfÈ£%bM½¢NÑʤ7ÁéºÈ ¥_ÆBãcEÄù…xÒßÃB¤DpÊ©àE.?“Y„!mVˆ`ͦ&¿.3zìP¶OåœÈX’ŸÞ=LÒ.¨—fá/Ó‰jP›ýšeÊÚ1ø]½{V° ¬Ü_šòYƒÎ,0ó²øǤ5R½.!ÙÅ>TÙ"v¸—‹ücKX©s´Ô& ”ãÜåYô}þD¨þš+¦ÃúNV§ÿ ”d°‰8w”öëM‘¬ é _cÜŽŸZ
+gg(C"»y‰`Y*ŸÚgwØO†»%xûYSáýf‡5 _<4Ÿ$ËœNûÿØÉ‚Û`ÏÇuÕݘKæàÃ;d%i‘¥‚iÍ°sŠžÆ·°ÇÞ8nnåÝzPN–—>¤íWá8dVW5D
+Š¡:ë‚G$qEá Bn‚´¯H^«š›èO2üSß•4­Nb³ƒ±ñÃm4úeEE±¬ÎÉÍ/)ºpbßfc)Rà˜L/oÞ»h„]¶Hà- ;ü)gŽPJZgeë'‘ev|N!1%>üý•}ªœJC»‘zÑÓ¡mì\§CdSº`·»aõYôðÃviÆÉÀõçE˜t`™9Âá|¾)Ê$î=ˆÓ!z‹
+S½ÌS×­›'²ŒìÂü8MxÖ³*R‹ÔJÙê·Í oƒ¢gi³[9~Tõ˩𧦠™|º=ÜŠmA®tøp ãrrÈþ›shŒFÛæ×H¾R©ävnÕð‚ígùµ?›_Ú¼žEÏQŒ­¥«((lòã¢Ûf|þd}lËÆü`»‰kŒûÈDÐ2bjÓ·¤Õ
+ *%BZ ˜'Ö…¸S…BãIIê·}w·áÏò‘þ‚ñGJòŒ¨Ù!A6QìÓKÔ~È›R‚q'TM‰× Jô©¥üEH›èýñ¸sÐ>câ?ë oƒÒ3ì™aÄoB¨PYZ€%kqc–R'Ä8ÃöF%ÿLÉ?u+‚÷ñÖÄ Ö6¿¢Ë`‚ØÞ®´ZWŸ„•Â&’½Äæºw Ò0çLþ¥{±@’©,²Øñœn%÷yçÜQó¸ÍzÏ7=ühÍc îíýn•þ³…cëLs6Ü7Ž<ºõ¢=£¢_aÑ3gŸ{rÅ÷wWd¥Ì³‘»ë¼kŸáßé×›"E¦&òÃŽTÂ’r
+¾ãoå«ØО&Âo@,šBà är- 8Ë7å*Ð][9õ@qb‡’dl)‡PY3z6M^%(Ñ—ˆHüw7Ò?&"8‡ùƒûU€•·ñk[zÇC­ÂþsÇüçQEé`ã´M(‚É(!Pvw–e#ã[¥kŠTNW7“k8 q¥×¹q&%éikGÚ¼ü©‘Aç<Æð¸Ìsš·G/ÝAÇØrTK̵_´èÄÐÑäºß•üã/ÿÇ‚|2ÈÃ'yí¬ô†ý ÎûzS˃Dˆ¾È% ¥À©÷Ò´Ú¼IIY&:ø}ràâsoccÖ^î~už
+ÿbÖS½Üì¢c«‘›šu
+$‡^c ëÁ9V>%„MÿQFÝT’¬ Á sAlüé¥YÉbì<ÊÕ>pj%“Ò|Ž€3³c¸ÿCÜýÏsV¯`›ÌS Óý43ÝÇLìâôìøLÖç¦E³˜S:584U¬œ•ÂYÑ©-y ú¼°RPÍYL’C‘V^”øÖ%ÌXa„LjÙù*îs£¶ô ~Ç<{9õ@yú‰k¼I($d€í_Q‰Ï°˜ÅÏaüµŠ”™F(XØ?‚Œ’àn{„%,ûÎÄë(õ¦Mžg2Ö©¹¦‰¶¸…ÉÃi—Õb\ìÐ,úЙ,ê7¦/ZmPXàc"€ùR [Pr¦É>Mÿ(ïιÑìÔ&çZ…[s„O¶ÆŒêÀƳh’/L-s—Ù–ÓQQ0<øÅ# BQ¬ë_/_õ6µ¯H^àœ±È>üSßÕ_—?xq.8g|¯·ZÁ”EXEFäËŠXÿ&Ã&B ŠTÒhJ‘7˜sº+Bå¶Â˜?˜LD¤hÛ]LÑp.‹‘Ù%~$KýrNä1–­Š4ì˜IìµÑýƘIa}Êã”üŸñ2G®ä‚è xÚ2ØSA™ruyS÷×ËÀ»«EÎ0úÃúX
+Y¹0Ôä’2ê ÌUxÁj¤fHôŠtx›ÅÊ4 áÌA..¢DrŠ>V{²Ä»+ÏyE*‰²8(D k–40üq•°xÈ 'ì—…7‰+Ä8Èú4P•áÌÇO©¬¤¹ƒ
+2M5H·½ŒqÌÀ,>•ðþÕF¬´³Ñ¥†.×Î+€1çÎÉI¾j»ŠoÐõþi®"Îíñ§«¾¥!jZÔÓËÚ—(Éqô‰ZƒÕUÂíG€©è\]%'%ù¦SÑP1à”Ñ­zp¨Ù™ÂÆÜM*‚÷¹~Ý ct .šË÷9  ÏØ„&wð¦ácvzî‡'›òµz]o
+uw&´}`je®¦ò®¶ª͆™˜j_M燺.oJ‰ž¡ˆ¢iÒ™ãI1&šl¯¤»°díëRnMQ
+ ¼Ò–9¡Ú¨ÀÕ-ÁuUà>É z !ògdçh£9En›¡=!xÕí¤ U»„gI 
+ÿIéÇEöÂË#$+ºo÷£"<V@O¼°ùt·ˆ®`M’º­ß UCh«
+$BìHŸ¢Ät²è„—–(Y 99Oÿ_s©
+ì0Fü#ÿ¸ö“š,l* æù°
+RÃ'e|L,¤3Ô™Ú–„EAh8ìPv9í"°{Íëð'ⶠ,‰Ù™ë‚Æì"ðE´$ïMïÞW÷@‹‡ÌÂä‰à1ß…P¤î ¬%zãæ Ê¤à(°÷ àxóVb’£(:²•?C¸Ž••še%ƘCf‹€N‘ƒ?F[ve\äÌÄ}»}™Sî—¢5óí-ÈŽ¤Z9q@úÂ\Vׯ‡¡ÕÃIˆ8kå©[é²—iÇh¾N!)®Š€ž±L>JÆ53Çeù0LÃÀQáÒ½ï õ³]H¶JS( Ëy¬c‘SÅJrÑÁÈ»¨çŽ&¼:N»©¶vÂp<¦€æãçàbrWÊö‚«iÊ2—¼s©ÐËhoV‚é©2¤HÈ*±q4+ÒÞ
+W͵ÑTd[>¡‚GÞÑÖAÃI{œ…ß®’Üý™-O>”D‰¶Â&m<[]Š®ö‰é>ú+37<¤„è‰fÈô^¾êm•wCÉxLìböt»Êä€ýêš*ªR /ÁiŠo"„>‹À¹)»ëðÀè3º—~%LŒE¡-‰ Æ‘@.…­h«½ï­ £°§F`7aOüC2£Ëì…ÌýðmH~tFÝ+¡¨\=Ž`¯ÜæRóáÕÐt†#)Éa@­*ظQÓ¾¸Œz,â\Ã3"aøÄ>«S²wÃ0Ðâ2–ÉÀ±ms?7ÍÄ=gµx=&Ô¢çŽ]ºqïÌÚˆr~9j=ñT#cËÃ𗎽K”øxqΗª[B!\âp ¶8ÏN·¢/^‡‘PÓæíöÌÈWE•Õ`èÕPïæ^%iFkßÓ=^¨©bŒÔI džt#F•s «¤˜±¥á€×[ò |B9'öÁ-S‹IÚ
+ÈàþÄäηܴñ0CTÓiß…'äc6¡G6Ác››µ¸³–cQ@
+ôÒÛ­D 'ðLyÛLà[ÐIøK õÃÝyéíDó¤‹øk ©!2}ì"|õˆAsjëäu\BMù—ñrÇÍã‚ð tÅ
+~ÌûJ•ú‚3*ôýýÕLaîö‚4 ?ÄæÎLwUuÞm—ÀRšÕ¤U “ÄqI'Ý‹
+KÕ+6rÝxLÉÚ FÕ¼SL‹ÄF6ñ€ØÇ3Èÿðý¨Ù>k_¥zPƒabuÜòúNÖ€˜‚Ôö>ÁðOóz)RmêpÇþì"iìड़Ý<¾Fôe
+aŠÁò–N6\“HÇ „]§Ãûr2SR HTܳ<5ž=¦s‹9JíX#›Y[§È†V9vca ÏdˆÐ~׶ìPU0¸ ÑýNÞGÁ(kÔZI0'Yá±J +K9’x›
+—&¯EØ3ÁU¤Á
+•KN…œ ²æÀ¤øRo»„3q»x–“S²GQeuø,ãá;¤šÕ‘ü8ÏmPrD "mÒàDðwÌ´&ËK Wô)a¤A¬*=ãÁæŽÀ Rà)ýCyL9AFž åî%t g:"s
+O2`nu‚JP]M6M¦D/ ù’¶üvN¬0P Rrõq†±s«‘!BìõƘ·OqjJ`í pƒxI^wؾj]KXh5ª$2K¼Ym'Ô‚q8W¨ˆv—kɇõúö•,wúão6ö_ÿ|ûñï·’Â+5­*®ßßµ®¡’l¡þÏ*R€•Ñœ¹}4½$y~ OÍ,OEÂLšÚ·×éVÅó¥&BÓ|ÖûåëNè/-Ue~,ãÔhrWÛ”1 É2~µ¨(Î×rós–`wº)’Á¸ ØÕ*"¥.ÏÁ’!û¯ãK`¬èÚ(XôûÄ—
+g{˜pS%Ϩ1G " Ä&œë(úõPTF‘8¡eYZêá!½fŠ÷RIR¤ìÒÆwúý_“W »
+Q„ÓÕ8<k-“ĺÒ×ïuTÔîf· K5È6Z&c‚«Ýß âߺѴȊ.TÀ*=ßÕŸ‘øÏ€!ßNÉïÕDtŸ/àÌæ9ßÁ˜.øÿVÏeBËP¤Ræç×S4-åQÈŠ7œ„Ï MÉÓv¬‚ÍoÖï{ \•àH“­ý.Ü_•ï_˜ùñî*%…é4©qs ìŸUT¸DÀ¡É)·¢º8LY¿yò bÛƒ¶_¨«¢ úÌ5"Ü1ÿŠ ±ÓKÃϬ÷ó¶#›¯ÿÚð”2'}}†€ž*„E#6žEŠÏH%ô‹PQ—Æó½"vK“šc¸AÚB³W„ Ð8
+N™¶cúß!~°ØÔ82;C%)Í
+2PÕœÌ ¹á‰I]G xÌ„E°ÐwÙÅ*É™õýhõ‘2œ fG+)S_AO«™8Ã_Cö9­yÞ‹è²ØÀ`ßâ
+1ðß¼ I¯É)z3ŬSÛ¥©»ˆéãpCÜ£ÀM£Y<µÙvA=¢\nÓ*YœÁÌójFÚwÅÄ%è:žÆv|SÏüÑÀ"K´w!•W®Ï@[¼ŽðÓ’ù…”^-rZg­ÿÜ:i'ô("{E*Aäp–8Âa6CžTC‹¼+ˆ!p¨ö(¾6dÚ p¶A$dzTü}íÇeÄa0ùÕ·UR
+Qy‚W¨@ iørc”?£äŒ¶¹Ü¡–ÖÔ¤Ü /" gÕ¢ÏÁð’$ËÐÅf·É’V”ŒD—w ÀA¿ÁÅNU à¹Iœ¯‹;÷¢;˜¹4?Cgz(g¨Ÿ× ®xx¸¥Æ7ká­&Šû
+a§7W"\ü)Ÿ~.Ú)¬àC‡däd“<7Ú•†p"ŽyÆ1¸Ýxîç(Ƨw’büøwü×?ß~üû­²PX,– S+Vú
+½ïËà˜Lg¹8%:)[€+ôã§.߉/þ+V@Æ#º{Jz!/ÁŠ*’3ÉÙm%¯ÞH
+ñ¼êÒb¯äèì¨[QÇèù
+†Q ˜QÖ#Z9Sq?f ¼ÄY(ˆþî˜0ëð
+¥Ë"ò„ x»jx§¦.»Ç+‡¶”×à2}‹)™º!v«ÂÎK—ÂÖ¾æ W„³ž•èÈXU{E )ý% ázHÆBH /Gu˜°¢á×~DŠ˜ýåˆØD‡éc€uùŽÉY®[E÷A€ÚfÿZ"§Š0™­¹âÙ¬jã¹`["º?Ä‹q `®Ž˜-boÞ˜e`˜ž"¨¡ƒY›S+”ãÕ”1ˆ"1vÔ°-¿óo
+;pUÎw ‹jè×ùÒ«&7©8ž‚×ù1Ä«%•É-@Qdƒ¤{ ÝÕÄ£tyZœRE³¸^ø§l0Fa2 ÎÁÅÁ1¹Fûèmã~Ɖ/u¬%z6\©`UZi7ŒÙax’Úç¶%AýÈy0Ž<›Ýwh)ïV¬‡.| ó5¾N‹|kÁõ`ãqxt D´9¯³vLçF2Hú&æHz†ï7Õ ÔBH¨ónÇw%x¼ŒÍ“¿‹/=kÎP1H›Îù1[ËxzI‘Pzb!úßôl<%wûW\š²D½ÖB-»R}E(¡±®‚èÇMWqN1¡€gU§P˜«‚sÕ.%ð“ôÒ¸Î圬.ñõíyÞ†v+¢2 E#<¯ÉÓqÆP
+™"Ô…s®qh £’M×) >…‰Öè rˆkѲ®y-1% !þ¶ÍŽìFÒ«I\é]óËkXC¶É @çB¡bÃ2Ü.
+ir|*+D½Ük4ˆžícØ'¤ ee‘¢Ç¸:,Ïô’,JGÛ¼˜ô]jê'Ž©ç,¿ƒíü{טW¯þÇE¶ù¯*ÓÖ^ï"öŠÿoEfd_Znœ0};(EKhÄDv|šŠSS’Ü]>%?7*4%‘µ/ù›s€1ÛtæáZ, fÓê^
+H‰Œ—KŽ8DOà;Ô&AJ¤D­=˾…Y•ï¿G}Üp¦H4PíÊŠ¤$~"‚Ú¿þS¢?†×&cHí­|ýóÃ¥>¤»-µÐ è{¿õÖ½Œ²@M$ji&U‘þ÷ÃÆx´æÅTÖ&Œ/ªõEÝ¥ý~]\U­ÕèóVò(­×"R¥ ²ñ‹ö6š¶
+èvÜG ö-š‡›ä‹¿¯ þPn Eø|ÄׄD‹È<™ìŒ³2Ì¢™õ é5ZQsñºó]ùµzá:¾~Í£úhãª÷ᵆð~/3L„×(U¬ìƒ¬!ÓÅ"â+³#\½“}7ȯ™BZGt~ôxGûàWÞéõUlTžMÚíëVÞ4¼·
+¿~^«ui4+â­Se~&(x†z”Ágóåƒô‡Ñ #lCJË(•Õ•ˆF'¡ãY9vúÞµòïQÏqª‹Q(VÝÙÕ‚påÇ„(!y]›Û‚t˶‰QÉÑ>ŠQ‰àJ}ôÝ9ÞLIc¡]V=¹n–¸é°“B©L 6íWÈS“þüq]Ú=¦.ùY¬ƒ^Fðv܇ ¿há>]O ¤ýú/Ðö`‚zĽ4íï r/•[ŒÁ÷/ „ÐÓäRRÂKÛ&$¬J)C›D;eéµÊ7J›å¥ ÁôÐÄÌÉ>ª(g:›]Ò³&͸r;³¨A®³wÚ„Ð@Œ!ݯ^ßT÷’Ò‡íª3Ù<jX¨fH{|Oˆ1O…ò0ÙöI
+3ðÿÍÄ8$Å9c„ÙUf—,À3Ž¯ëú`¨m_7›¾R3'ã†èœKNœ2Ô}Ò3ˆ”rK @mÆ™¥ì 8½kžõ.Iã2è×AZbC8&y‹ÄXìj ÄÓâˆÙéf»t’ÖêLŽvƶ¥¬^ôƒþI»ËlwîÀ* ^¼çïÒJoð^Úp¢5­•Hèê°H…î!MUp Mim7a`ø^'Y›¨ˆá‘nÕN»›Ó…½C »ÝÃé :Á˜Þù:fY(#9j7ž=6¢ò0­±tðÂÑY$ï1Æë}åé?­4Fás 1þcKåöÃk5¦+ÛÃ÷QÅPmÈxï~:%õBb—tû¦pM!ìEº#ã¨DãÃ+¤Í2HvKüyÕ+m#
+£ÔbÁ³—a/ñ¡3áU§mLˆ'Qñ‰U›¯"[*Dè|ë<*RBhp‘ i-<m\þ•"²¤"º`A2 *pgYÅDNÊéqÜ™é ôÏB3ƒ—Xí>ÙédÐHQ¨‡)ë‹NÑ—D ÙsjÔ_e¢ÒÉ,Æeúál|Øâd{ mÌV-»“i”YRd·7t;­$±.*„©è6’z¤Ò³RšÚ¸ 0b.°\È:Š00¬eÑÚ›8—rŠ ÖáÜQú³·X·B2é&†íÛÐü±‘ )gh”g®aÓ;Ù1¤ˆ6ÄJ-Ne`ê¦tg'FÃÑq¡ˆq…8›œ\‰-ÃgÕ}v_S¥\@ßW‡AC$T(Úì%CÂc›þÁÂÁpµºfùuÉÍů‡»–läÎ;wvÒA·ô‹c=*©\Rö¶°ÝFæ2c$×»Jä½TúiAÄIN Ã%
+¤!w&º”ãr¢’_ú,:™ PŒÛ‚à(1–€Y[C3î;\ƲٖÍAðÛ´ä=,ÔFª½´íc¸ JHØžÆdB á&éhfÙO£=Ù„çQ=“9G\|7üÜ-g϶†°‚`a*ÓzRL‘J…7ùRYT“„†´M2­_MŠ^tñ JCÄG–¦­êf‚WñJÜVz{[‡Yö-Œ_…c##
+‘Ú˜B̨|ëÈmNÝ÷y=-'qZT
+•FJ‹Œvs²¿ß€^Œ³¥pñ<Ìx”ñ1èbæ_ûôdA¿¯ *‡æà¶â‘ÍVSòR7Ù¾ÎrZpÂŒ7|<¥”wð×
+5Gi[JiLƒgI%ì÷k…5Fï¬åÔ—I#!†Y\f³±¤$)ô7N‹ œqO'tƒüšILÒÙ72ÞÄár°QŽÃ ³ãáDdvãk)žŠŸ×z]ZàsÆæ¹¼ƇÎLÿp×zDª4Ùàê’rzz] ¼x¬š^Àýÿ{Ôsœ6Ydò{mÇc9”¹<­šS !š\c×ΠqXr´ÂÕê8Ç^²H‘F|Ïtûõ¡Yt^ìÌ<“+(íWÈeSz]¾<<…
+)û¨\p5žãLA¤7Ü!f‡<9b¼ÏaoÅÅaR¶UÀí°L,J«3K™¥¦qémÓ NÇ-`®Wü•iÂ:”8G=žõ;ïF¿°²5¡¢Êþ•Ž|åNÓÉÒntÖö8sº!'Åv•r/é”Ví˜P¡L3,G©³Li†y"%~u6ÂþBàôQ}¼sH´žnÈÒ‹^â0‰áa*Ç>¦ßl¥¸ó aïµ\ ½ãê¬üªï£FzkØ
+>àXxœEE¤ÆOfG#¥;
+£Ã.
+áÚÖHÁÊ Ö_·üYÀ"SuiKäZ˜+šÚ– È=}9)ÖÕzeš<,‰Ó} ë‹Ê4ý$#…D‘7¼l-!-ÑßA‚«‡"O»ó^18׊~ÁØ.¾ÂH®}ì‘cÛ<ŒÖêñT­ùnܶQÚ±0P“Q\Ùi¹g?äb‡¨)iïÙŸ_7I}1f2ŠëÒ1Þ< gQ+½<aC3†Âa–cÇ) ]ÄOã~S ³–cÚµq6-z“„ý£dä¹ö¶•7m4[S1—º° Ès»-/
+É7b-ºže5Ò¾5ò ‚Òi÷B"œ¼At²rá
+ŸÉ=Ù,š^ÜŸ Ìáa1Ù ¥hòNü¾îDwŒ^©˜œ¢²ˆGW¤•wC9Þ‹Dig«Ó…B0e¥ÇXù)§ YQßyø²~-àíœ{°ùÑ¡%Y„r¸Ý,ú¾fä$°Úye'“šà >”Ú—Ç-¼½@CH€á Òm¬˜:Wý"k ÆyVDÊ“µíÔ¶¿+¸¨•aqÞE³“èfµï"¼Y¬jã¼îêË g潊ÈiG<]£èJêþtØ
+#kSµÄ#¸y\]•¶ Bä³é¥úÕzfq(c‘ëÃ9$ô„°s:OÑ6T¾ÅäQž'±×à ô”`ãR>ñyáoBeš!ÌЦÊ$QÔò1‡äK‚l´‰ˆÈLåƒ<m>ƒ¢H óKØÒ¢‚Ñ¢ÑCèË…l½A£Aƒ$%¯K\:A)È¥ÕÇÖ^ÜãZ:Á®xíÇ £i=ÆýxxÈ`aQ0“‡¯Á’³
+I*‘¶ÜöMØâHò¸Ýtå°oWÄäOßò
+û[žæÛÇTµ™’ÈÈFV0MÞ¨c%)$*óÿyD³ HUÞ ïÖ¼5O"ÅBh£ãûÉ”g•}±í&ô8F)Pè{CŒˆÙð.$;&*vÈ?Üóƒ†ÿúQhÂ>b’¹ZjÎl,n`<z)Ù½ní lU-6%œÂö’fïB
+†°_E*×ÓÑðT·N)­kýA9âÏâ.¼ošÞeR^1П3]”®pÿä‘ú’Ï }ÙjG†¤¸º¦ýº¯Ê‚ˆƒ!ˆ½pÖ¨áT¬ÁÒ…¯fD\…Jaס% ÷ÌúYîK2¡ BRÕÕé…Ÿȸ©!õAÛÙh°&mƒXÛ¢Wb XT/Þâ¤Qç!)dÇê@3˜<]Ì…ýá5
+Tá'ÍkëTÿXZˆV*RÑÆ !¯2çdf´oê
+ÕË
+§:b㨄4;¥±òtö¯w}«"1¢šã¢:1•^ç1BݬÐ_Iô™YcÅ ãô›Á ”–ÈÂ{QÕA
+ÁŽDô3RÜAê|`Jð´jR ý‘%—‡A¹Ô8Á6ÇŒ>=õŠ%1FÕz¹{Væ¾Ï}UãwÃGo¦AÝdÊ&¼!³Ùå쯫„wŒSžðâQȆ¦þ}
+rþ¯Òm
+ßêkJ µ•“Ä®
+/“@¢,ÍWøÖHD i´èèϻĩIì&øÏîªaS–`“ј3_ ›Ü‹Ï•ª¦áJ<­‹ÈŒ¾:yÚ î@ ÈÎå1H7±ÜX“Ë qF÷˜ñ"j0Í£!?þÇŒ×à`8ÄÓR|xKñ"Â:ìg¢5­ªã´o0?ÂR'ò•$þº“Œ»2ZhrÔñ)è.ñ.è]âÎ’¤zøõ®ñ R@{`ôÎ]%¾i7¥ßØD#ƒyO'` ã/r©Þ¸Á’%rýÈ‹k’œ2gWâdþ*)Îx çMâØ£¦æ!6_â‚Q‘G¾4ï/
+€­Bñäj%QÒ’iBï;&ˆb_‚Äa"y–‡/%£JBdä†?ªsÛè`—",
+ð¯ÝÄ×Wø³M6:Á×
+ìmCÙ:F”j×’ŽD |ϵ„eÍð4­8]E”gP QÏî9(-ƒ0gJ”q+" ek’t/È/“#;n%ŠZ@Ú˜×Ôò{¡mÓÿí?Èb¨«²‚½`H|ÌÓÀa*ÍQ¹ž¼ç€ åÍÝþgPC§À ô­²vÓ¢ 2´(S©A…ŒhÓDàj†+MVÜ °uÊæÓ8¥t:Ùž:ã]YiÀ"_ˆ¨c4ijŽBL½# ®¾±±‘…û‡7tÉuΖ`§„œ†"I—¥};çÞóÇkž³dõÁGužƒü¨qò¥Wÿ~gk´`i/XþY•'¯ÄVöà¼ß/A~ñôB—D
+Yœ•2›’ò¥%ÁvÁ6·íi‹Ÿ<pº>®¬ PóO•ø^x
+\î@CcÈžœõQnàÒ EÖ3ê} ]36Р5¤m|-!. l‹%¦Çjp€"¹8¡n>ÑóŽºY½S{&e–ð„–© â÷²ÿ¥L…ÔÁð$HÒ‘³Ñ³)M’ ¼rB(/›všó!ä€ë«‚Ÿ²—ùtëýçKÃ=¼ô'/Õ˜!‰ãx(Îê3*㤎åLÚœÇø*¹Xj^å(îKR´9Jøa3Å:ËL*/Âð¡)q OŠ*TBȼ…8:c¾Ø20¿¾ÃÛM¯5O»^ÃÑ)—f>cZ…`5Aàž¸&o(–†2îhQ‰i`ȳ2m²˜
+™á%Dƒ³õ²2Û4q ,XW,úÜAœI]µ’—ÀIÒÛ¬Ùç}û[ÙFÞz”8!øBWæå2Hã,[ÒÊ(žx.8Ș„k|øg]êJŒ¢g/ñ¸%šÓ ÏyoØ‚Æ=yFA
+
+‡`öQ‡c¦´¡Â¬xLd- „pÑ(…(UäÜ6âAlñÔ¼)Ôp/hVBFVv.²îþ–†¤Ôi¥œ'3y[¹Œû"pK;;.
+Gõ1Ö ÀÊ2Tgƒq.[3SFáM ‚>¿±aGíJéóì.Œ)Ž¾Mº† ö"”^…µ‡<–¦I€©Í0n›ò!”»¬ }9¢ ’óVPFë©3ñe´(DýÀ3À­ÜK ÏÉ?¹U|+ªiŠÓ­Z¨ÈÖHýEù!oÝ\ÔÐÎíÒœ»Ï ±ç5Iöÿ»Ôðr± `÷úvPÐ4®›h]’$
+º½‰6‚?$wçÍð*
+jkêG4dE:¯
+“à†éWýe5°ùãŸÜÃ?0‹L?v©ßž ±ã÷û%¨u„žM¾öC!½“¾ f:mÎ ûeAxÞÓ6Tѵ~™/©Ì5ió&AKÚ¹T%줈âi2°â
+ΙŒ>஀-âMBàjFiHºi!ä¨H‡qêXá1ë'sÈﳓÄåÏÚ@!¢'˜¼‚Ì>éØrŸG™€ ij9‚ &Û5Œtl3fTˆÒÀ,°{_fSv )UZw5Ú$± ÊæUˆ6€N©¥¨Q¥.*Ù£A /é¶Ñ’î® ¨i)IAçiDÍonm¬Xkîfá‘Lºâsž-¼æ9:AV|ç9ÅG!_ZõïwVFÛ•öv¡q™ )ùל÷û%f×Óá$aW"’@ÝFr¿pg…¡¼§Hæ
+Ѷ!
+*ä)›åžA·Æ¾¡¶ä_éá8yaz AÐåËB° 0?Nêð D8³uÙ"äWž8c‹2–¬®„ˆÎŸ¡ÚYcê͘H€^Z=amØ,Õ§DXy‹8Ív:#ƒp´ø& ¥\ˆÈ~½¥ËkäÆâžùìmÌoÍW3‡jcH¬ù€&ð­JÎäŽÇ´Â"
+"c)¡‰'3(æ3 b™À$ë†YüPdAC9WÛh™i4»FqG0îVq»#»ÇNPÙ¹ýÒ.wCÈ$5–7&€o“IÓ³}Ž³€7@—{7SjmžÎ ’¾' BNgE:jÀL¾CtÐg—B0t'iàò#jIiCQ½lJÛÌ}Q´Ÿç98À®zm²‰¿“l?3€Ý)ùÜcÁ(tÃÓ¿ÌÁ¢68pHɼϪ»Étið‚cüA¤ÓpéTáêõƒA˜-x1¿)/‘ Íä3ßfô%Ÿ!Ýçi'Ñгx ó«°Q$‘ˆÊ# ¹Íè¯QP0íhòò¾ú·ƒ‚ |^÷Í /¨/×-è ÌÖï¨þIï7䙸Ó@<* .þŽb8‰&ãÂúÐv¼¥Û~ŒƒÔò‘eG`3/ªØËa’³[NZÝm%ÍWÑc%çmcÑèìf=‰Ê n<ê'„QÒ8 ɶ¤+2c戤„œ«*w#0!°V_ΡS¸‡–º`$~ É0‡™)·úš•…¿ŽÚ_Šc$ÄBzÓ_*|¹5ʯzMDzß7ÚGô£¤ƒyéLëÖFϬ ~E©bUäîWÝ«cÒK…
+/Àî³w&›‹ºäQ=ú_ Æóºÿ3^.ÙuÜH]÷À¼ƒ?cy¨]hJïê@âY¬B™r©Ûì$
+ÈOäKÐçŸÜi)F$'äUnÑU¶‘3$ë懘®,°EÀµ|dYÐZ¨©¹w¥>4ИGš!òj¨:Ë|ŸÂ"¨$˜%‹/ˆŽ>ÓVîm¡wÒ®qžgö’ä:Ÿë4“ÅúT˜¨
+Ç3¥?0[xU™Áv)Î4Aïä`ð<†FMȃ3Ý@ieò´©g@â–Ö¿Ñ<„$
+0;Èó8‘Ïųό¯eº¡=øLšÀ!¬ãr6šLOîr8=8?0)°?: åÌFïÉ1!8u“.åê«ù’ ÍôÒ~:ëC¾ ”›è”à^°7Ò׌‡ç˜áÀ­ˆøÒ1–¬™ Xʤý˜¯ãH´ñôŧ— ÏcP‘x²²äbj_#)UãuÙëÙ…rT&å8Ö„ZEPY¼R­€ÅäOJî9æ ¯Ê¨..HÅøfãì5àü”ée'ôm[¤ƒÛ[<–ó²:¤„‰ÙÞ\ñxÃ)¬¼›‰p^A«—¼"ª!qºrÛí‡d±ã"³÷ˆ³vjK”!m`ìQ>³+«cžSHe’`lVˆQëIóÑìÂä!¡R`}é'sÛŸZ×1­ BdÐg¾™`nœÛzÊàk­ W
+I…©+ßú¨¦æQ˜µåÄýBÀœyöŒ!¤;¨höËh­Ã'
+ê·}#rÀžíêÑÓV½y¹¸¶Ä8–KHKdQ%ãœ×0ÜÎa%¢æÈ'©¯þn¨ŽEÇßÅVÎÓPFh§†æ¥œÀ'UoÓzˆÍsC3o™4ÁÿONBqJžF´ËŽÖ$Š<„€-ˆ™Òh‘k» …”Šg¥tùô˜ÞQµŠ›ô¦#ÒŒxSÔz‘‚õÌNÔžuÖbøÙPwô±3Vµu÷ÁOÜX;±ØÎoAØÐÁ6–Æòlö7CèX"AÐÊöw£±ì}š*×z,å%du k†’ W{8GíIXLïî£JÈ YÛ
+ ^Ø×áïΉ|W“áÙ’ßZi™`Û“
+ùR§_212Ša‹Y·Î,ÖQÇýsŽÖuq¶¹ÖÀÓò쬘6Ù¢®ÀØðKˆŒÈÅŠlÝ3Í6ã ÑÞ9cUqÎEvyBœøéf$ìé;$uBúCÙïÖ@ V6$÷PÔñ“ª$3o²•`j“¸x†TQ‹°´nk7ËÂÌOÙKrÊä°8¶Åu Ú½­Ø<Gé¥
+­èÍ“
+ßdEÖd kY»ÂP·€?(žìc| ºTþó!¨k·€R½öõ.œÕ
+–ÔˆmöñâÓ8ɪ$Æ¢”Ò•­X<DèK™ Ø@WîÇ0DM/JaË
+…àA8›˜@“¶B­ºQŠ"‘˜ÓubÙ254 $ƒ{ûüëô±.ÜD³FTŸ}œ®|Ûë·—ð€2m*&ÕÎÇP,Ž´ñ ?‡*q=îæÒ)êxýÛ—7ôLý¯Û.'¤Va¶æÂ5üôeÈf©· 7$ TÜK§ñu6={¼¬Î¹¶êÔÙÞ4Ö¦ù§¾iäß—
+–³58½>‘á)ˆ—&[ËDø
+Mµ¥ÏŒ3åÁG’ü¼Ç3jÓÐ7O#õ¦ÌÔêÞ§—†vE¬. ¬CBÊ”Ur-¤a|2U:j[‘EŒQÒ8´.)ø”Kkîá¾Ð$v¬Êq¼Í©¿¼êÜóƒñJ™GÐ!C!4W·¬1ùCh^þ%Æ)ì;Á³….ŃãXUÌœ6ÓýÎk£p Iz/Ü FAŠÿ- ³ãúç…ýŸ!^\áFPQËL
+fÑãlÇYRˆÚ£:4§"ø!(;œÆÚ¼iA‘m¡V˜
+Œ?Ü%—#e¹ŠG}㨭†N2àùøÑÓ1x% =OÅŠe¤ñDæÛjœ"Řkž]_"Oð6]×¢`ÌÁÔФ̩͢‡0b¥5óØè;?â6‚‹c^`õ…¦¾EJAû•)gV²˜¾©ïqæ½q>_ܶ“fåCf_Ï’ÉQ–ÁŽ<÷”Jt%Ñ0{$ª°/¡ÂOÅå7N¬)%Ÿ>пÊJ¡Ç)zW7iû Ê#íuÌ/äTSœ¢, ¨
+(n?dHmq5߬
+ßoC„?“β—qÕ/&ñÝVU)1ýµ\ÒÞ/@˜(Vœ;›¸»¡åD?ö b ö€÷”rëê_vSçßÐÒ\GNH #ëQª©?Æ i€*¨Ù²:“Hx†‡LH»=·s1:Ô‚Ô‘EªôØH
+N ‹
+o¿ŠŒÄó¸£Q³Ó PI¡ oåT¶Èj¯~ÕÌ’@d|y0Ít«d™I)µmÎCR&î.ì…‘[˜Føa dJ”O‹ÎeƒùÛ3°J³ËäðZÒ?Ϙ„[™=ñäL¤šºšHâHe¥μ¢ÈJ…t[Ž/J]Si#¦*³4äv, KVvg §Ò$æ}ìº-BG‚h|nöé74Š°Ð§}ú3h˜,"¥øp+{r“Ä ;È¥õdÆžOóê
+†(ÏÐ@w3+ËÙ9‰U¨)5cƒ°‰÷AªÓÆÐHf#Zp„ jîv)Vqb2ãi”„fþ}dYœ¥~ËÞB:mò؈Sî~+ÀŠÆ¶@ðéÈ÷êLÀŽ›D¶pŽÙ,†ƒšì´jã|ÌHD\Íl2º#ºÏ®eúU@*
+_²½“EÐÇ
+5õ¡\©úI¼).Î?Í—áñš
+ƒ)Q £
+ ÆÚð[©šèµØ‡ªîHi;
+H¡/”zg;GÍÔ‹ð>172-”kj0¢|;‡²Á
+ˆÄaâ¬ikÌÍ6³8Go=º©©Á‰³ŠmÙT`)Ù%µ¯8GwàH*ÕG‹Ñ5LßbŒ#¡—6X
+0 †–`ŠÔÍ3è`g!±ÙvÊÈšF\Çêã‹uùÅÚýiÄxÛКxAáÉøÓÝ΂2$I²›
+ýˆ±•(œÏ>@eÝ´[<‹7jm–‹¦»#jšeƒ@<Kê-§î³Ì0Uy pŽéÄZ¶c;†5Ó PYSAdfÕ˜,ë oÁƒk¼VœR´Â´Ž1hqEFe,,I”Ñû’ùŠ5í$œ°\ ‚® ÒÆÃßëØ,9†n$üt ž4ÉÀÒyÛO@x
+yÃ:zq NÄÑŠ— Q_à®´âÞÁ=[ÂpAhÔ7L$öjA\ôR‡(;|PüÁ>4ŠîÕ„RaÍ‚Ù&ÃTÿ&¶•e•JœB!qßXÕE!gÜIÂŽLD˜½ß°!åÁ‚ïÏ1Å®aÔÜæôƲ0Ôž¢;—› ¸«"Eíz×ή[Se}álÆÒgBσ/ÆçcdnõøÏ{‰ßÂû`ºrŽµ‚”5-²IˆCÒ$±)©Æ‹39Œ•…ð{èG22|Ø¡~ø…b`­ÄZ(åA†Å*ˆ_%¶¥tjÞÏ)2Š èžHk|•2",/²hN¤xšƒ€Ÿ|k®DÇ5‚$ #y°vNá1¼v $K{ÙÞuD±-ÍÃV–AfUfŽ@¨fÒMêÅ·Z hI¹ê¦ª»^I²ˆ}OÐãŒi^*E¦ˆþ
+*ÞS¼çÉzLÉÚŒ D[gƒŽ„WLŽiÞ¤`%£fšXú¡foŠ3çôñ¯_²‰ä g‚œ³;ü§°ës “Ðâ9J
+x`BÑñ$zl·p칊z!*ÉÙ²0Û¯
+Þ¢»S†ÍrÈ$’±
+|GpQˆ‡¢cl3J{ywŠ,ú@P)¹Ybœé.ž7Ÿ’Öx"Ã"26y‘ÐSøžý*Îå÷Y‰~ÅU "iòj -SfÌ7Me`¥ù“ÒÙ ­š‹Ë)RÈ,ͬõ V:á2<Õø✒sR Ñd‘ËD—©l5„Mºô“¬ûg9mI5ìI´
+%výÅ—Aä±0Þ>%^Ë‚ Q.4ð¤B>Èò<h<0W€X%¨ÄúÙŒVP‰ÕâEÑ,ñ —[sÎá—85¾bµ3þ¥kVXáˆÙ•{O í9Ûy¼Ûâ/y©*Òô¾mIœsp…ЉœOT°2 0 þg­`®‚%a°…`ÒZ5ísU£`P'½:=×ÎìªzT¿
+³@#à¿q¼IDzN¦›òøUTBÃC1øqñ"j}hý¹éR“1¤öÝmâäd¾7°ÛÝKýG&ï† ÑÁß4¾ÔOKóÃCÀf³~Ðû 1ð­TÙ4LÇZ(3`Ç«ˆ§‹&;‚aXúöSå_‚nqw Â
+ó<|E%–ðuÌÓe_÷È"ýSN²HLÌÿ`}=ÿ7@ˆçb ¹û‹¶<€>^©·µ¥oØ ŠbGÇÜóòªÐ¥š_Äb5+Ì|ßà'Ì­}Âg³êûE˜¿z¬Óõº@ìbf*°õldU1¯  E]³¸‚ ‘Uq®Øø=è|Ÿ˜‚‘ê¬a˨”j‚ ˆÈ 9"]Ä6Iº·i…<œ2Él:Dã‡1&„Éï
+xaÊa›ÊÆà TÝÎÑ7Ê®Ê|:„ªl'þÐüZѧöŒP@Mâv¦…O(}]N«à5[ç<%Y‚ ¦!):»eJ‚ÀÀGã“W‹˜Ʊöæ P·Ä…ç(~Ó´€HÏg9êÀVãâ4†P™šÌmõ)~ á?¿Ý@ó­Mýö¬%'áç»
+øb²ËFÓoI£Ädáú·Œ Î!¸Z]õOç¬?&Zf|y­EF†ªç=X$Ö‹6ì’ÒBª±P³‚§m&…õk4ö—®ˆO v/¯[›¹ßÎÑL°©Ø>ÙÕ}y± Ð ÃŒŽ9éeŠš ’_ÅÏü~2´ý<Y­ZJNÛÏÑ︎Ñ?W ¢I³µÛÆ<¶<›ÉzÕ*³=ÛÙzGЬÔWa³5GØ<_©FíFU.ÊÑ©A2å/ôìê³k¤Xv: ÜŽážç£ô|ZâÅ¢8™'r÷þØﮘ°&HXCz=é–bq‡-Ç-}ÁÇWÐOôÿñŠj¹¸55ºü—ñrÉ‘cWbè
+¼¯  …þc¿4pGöþ§ï0$5\™J¸&îî2KRüHF&)Œ¿$ ¡N^<ûÏsòA¥BÁŒ,³ ç?8ªèîKa¿0í]Øþj‚¸…x¡Ô[ÛÔñºA— üþ KžËŒ×E-P‚ÀZæV¦5„¿úÌÌŸ²AóÐÀ` .ui J祩~†¹]öû“͇³VqJÈ&Ú‹¿‚ŽÂRA3(–f
+РãÓÿù&zÊ Z‰âÙœ¥DùÔ*#«ƒÎ~麎ž®£F"Öd Å‹ä÷n›œ`ߎ•†éúX DÄð"‰ÛÇ Ãu9µÜ$b#¼îè–‚Óuÿ©0% (2u¨xgLOø¢^":øó ©*¢â§Y X3Ã`líM y1œª³úÞÃqô*‘›çÐnÊmF &¤—B\²l24‚ȺUÈy Š‡Œæ`sò¾jÀü8QŒâX $3¶‚ÛÈ‘sX¼Š|œ<¯-‹ÑŠ†úé$‡D9^ *Ô‘[‡®*/7™¼¢æ D0¼8R^(HŠ(‚ËœìÝŒ\ÀKyä4!´tЇ%Ÿó&L%ÆÄ4Wñá˜ÆˆÖ!‰e¿˜o4\>69„Xˆ ÉvÃ}{ï—÷ oaKP½ÂX Òñ¡ä
+ôÓÛ4s Þ“.Ýç$£êptÊÂÅhs&ïßù Zã0"ã_?ΠJ•AèÕ»”Ë:žg”CYï©‹€“Äv—ù_<FEnû‚°á`ùáÕ6XÞŒ‚#«TnÀÈ9†+®,Ó
+¶ú‹¬Öž ¸f“æN °vu” A³ðÿZ
+¼Ó3û’»#æE÷©z?%¿h^¦Üýa?†”_­h•Âm–}H­¸z
+@ׄWü!ÐkÜQÓ|,ŽÊ/~Jq",²}$f£¼pK™´E‘ _¸­B*pqˆ¢‰¯ƒF”;Ñѵ•ö÷s ¿=PWO„‚a™d-˜!A¬µCéY‡$#Å,®›b"3Ì%fy%XKÑû\ôs˜K5:f?n °æØ U§z:­â ° kŒÚj^¥½ˆ,Ó’9§»JÀ
+J†‘â¢Lƒf-³ÉC j¸uè;‰ü
+»²+òxMã{ÅáŒÖJ‘ñ–Ôs©=û9 Š†3kfßòœR+ R-Ú˜cl+p£4l»aq¿H„X@:|•ý)ËÕs·#*_‰½‘vg`´ä¶¦! 
+UÐõë †ÎeL‰uQ• Gù~0ÇÌ›+0N¢Óùù®À‚`ŠüZiEÀ
+†e +7‡s PLÒñ5¼0S„<µ‡¨4l1ÃÉ`{9&È ²Ë<bg)—Rt‡ @ Z–Ë\§ˆ 4ÊÙº*E²ÌÊ…ñÙM€ÃǨÍs’¦§Ù%·¹qD¯Û˜Ù=YòÕÅe2ÆuSÆ/ L<a¬6f(é,z}òs¨¢lºWoaÇx°âîîAš[3¤‹¹¸‚ŽÙ^z ŒGÒÊ“‘¾‚<Í.Έ¶´æ3|¡`Hª¨ìwÈ+Oç
+EqîGî¶Y®$’Íå¨
+Øþí `©(šGÆöU7PèjÐEÁÓ²ÐåâûÂPAór7(TÛƒÎÍHÖ`Œì
+:^2¦ šJÿ焪Ð
+Ü æuHäpT*z
+R„†#H‡àž“R¼Lž ³Æ |s)¨–¦¶ùæ(7È(e8Ýæ9¦LŒT&$ÆqâW©ˆ”¹µÎ?½¯÷ÂEv,aÖMÄC+t—’™$êZ}Ø«Ísu¨§ÔÓ‚° @M8¾èH â&貶!,|DøSdqˆA¦YK
+ÐŽ²zÃË*„·íòÖ~óªwÐ4zM.­É?ïqÓ‰4‘ Tºw Ê—ÕÚ4"(Ôºé=7N‘×r•:ÐϤ«hž-n+å Îôq^%mgpèÙy:ùeÜ®àrmcŠœe 8ÒS“£ƒç´Qöq^4ÔÆAÞþ-jìàËEžNÄ&†ï-Bþ²L¥OH›BÛuìUî–@Í&å³ÜxÉ¢K~ÍáÅò<ç}½VZ^ºt?Ÿ˜âdÿoŒã]A6d¤PÄ™ÀwÞrûO6ëa‘Ò?øïTœ*»¿ªÖ
+6G8¨ïi$ÄÀX#¢š¬
+u²2L”Èòô~›0ž1yt…­Ÿa0-ð%Ç×q¼ìC™Ä;þŸñ2ÉŽ¬ˆ¡è
+؃WÀ‰¾CvÁÔìÊ}
+ÉÎøE ]úÑI¯ËÍ,E·¢ª‰!$Å”˜Ö×ÅyÄ|ÅšM.y(ªl—5v<¸Ó–©¬1IPiûB£Nà±Ü`½>Àç/´MÁ®fF4CÖ4 >«ïêö·ÕF¤f)]URD%–‰ØÆ^ˆT—ß#ˆPÈÈL6Ñj…HÊË)êÔcÔ(š³Ë‡´U§ñ[ŸÁë ¿JXO®VáÅå˜K­ª+±Ír 52<|:JÕ¯‘dÆ}'K9ܨ÷Ðl%’2Yüì$yO]vØpC@Kp0*ÁÒË®°à87o‚5!·øNÌ7¤Ð¬”áv™ÏN÷¶-“ bôë¸ä'bÚëû¿N[“õÁ*×Û|¯Vè
+kD5BÛŸVó&ÚF9XÙÌ͹Q[²úºqB…›ŒF'ãå˜)
+&(Õ‘t¬„•6Ô
+/
+|è
+Å•eB›þò>ÅG¢x<vc|Ú)²bð°eÆOŸÚ²¡A™Ê3„ä5ʦŠ•ˆŽï¨:[%š#ñz—’!¾» æ*ch˜¨:ØÄüì),PÝ-U °BÓY€+Á#UOÌãòt‹ÿt»•ˆÍuvï&ï$Ÿ
+Â!Þ³ s\Û€ûhq,À#eß*wøGŽög/RNÁÚ
+i½Šx·­ƒÉ&5¦0çÄüòÒté-Ÿ ]Lb‘wýF
+ ƒÑSs„¯Å¨Ó qø’cäp·k¸–PB~Zˆ *¹|šÉ,ð^¡«Á˜n«®<÷°uT±í£áÑ”³3 ¼Z–¤„O:˜ÒtjŒe,‘ßJž‡eäƒà=”3m] '+Gæj³+\¾
+Kùg‚{M}í,Yb‹Ÿ­¸Œ Œšï0Ü›,0ôÐÛ±o”p¡$ŠÒ7Š£g+vÐnÅaIŠ¶œ™ÜÆ:Ðë2†[Ùç+>½ÚvÐI|Á8´ÁÛ_J\…1$‚1,ëlä/Á‘{œfMÅ'šœëA0Vš!R"„± ¤‰WO1‰<Ëœ;Y†=rðY‰8¸vÄ4Þ˜$åñYÙåÂoJ䣜é&{§Ó%H Œ
+8ú´"ì\Ï`_"AAItJNí@´,A¢þëG†l¢<N yLy—¼ÛCŽí2c0j_\È3šÈý›ßœ9¥àŠ÷‚•Yºµœ¼ª”À´Åí@F+’èiÇNÀ‰ß˜äK !òñêõl5æMóÎß:ؘïc'Ùš/ïñé{ƺ5ý[ÆbºP=l =¸©Ÿ{2¹7áæ¼iûˆ—$"ë‘w8%îc:¹óÄš’"°}¨„¸QäHÛáüè0
+Ž„%¹ÂǼ£ðòL/ò:¨‘Ä*¡å ‚-w +ʱÐE
+¾¢’™àœëav¤D8¥¹YTn’È6»’£ó€‹Nû
+kžU
+)vm­B¯ðãÈÏ‹Õ>Òaå™Õ%èE
+Îßå&ä;¢Ü%õôe¸Ó~r0¶‚¦B®PôÇb0x .ÿ=×t@+"%¶Ï‘‚£ˆÉX0Ú !UB„]O:#™cy)2!Mï3¤â°H
+h†lN´¯”DF]6fû ­)i´"·R©Ÿ”äöû?®EM«c˜ÛI ™ÙÕHû\AóJGwÛ4äaòx„© x '7Ù+Óý J…"l®Þ*²§µÂ9F·ʘH2‚6í;4“¼ætðçÁd$¤(Ô‘ç…ô)‰t zS¦÷»($‹K8åtÑÚõz:•s`ï¼7gÑü¼dYÕ°Xi¨ (èÃÀU*èKëäU(|ÍÄÁJõf4©w'm1ß2ƒJ‚oþªÄ¾ÀúÅÁÁð$-0š&pÙª4ƒü¤&Br§¥O߯üdR  ëòÔ¦8š›'ˆ¾ŽÓ‰³ w†:H7ß™T’Á <3nÎ&Hq=*¤³;›êÝy{¿0H—Aïü„D$ð„Í™Ÿ‚Í)B¸A¿Ú&@Z½˜B€p›ÝBª¦âãBÏ"L”ºÐÂ}Ÿ„÷8q71øIUmÄÄC“ɾƒÔ%I"×Ù:‹ d´;k¬XŽŸp¯i<éÝëô/ïGq6Wá³u»¯ Ÿ’¿’3ê©nÒ£yG˜rè9úŠ ¸]Æ ïaàHD³È¹D[Ó]¯„"ûéÝÃ+™B¬¥ØU¼”ѹQ÷QU>þ[-+¯A­=…¾1‡/VÑTôÞš ;ˆ÷ Ói<.$¢^m3ä7É4é±î„Ö¥I«mº8Ê Ö½ƒDXlqÄdIµœ0Ò£BÖQˆ^×ìöºÉ÷ò <äºqwž‹IRÐ5uÌwPż/ˆ­ ¾[‰UÅ,W—¢£:fQ4o>á8½`7ˆ4®rŸB¼ÈÅôÝû$•Š Z›KΕ > ̧»*ƒ%Ê|0{‡ ¿„¬÷ƒÇ(.ÍÝöº»|ghõ ›™è;ˆ¶bi1Ôá$º°é~< /z«C*(&èëÛc´h¹ÊãÚÅkQñ|7å]-d„ÚµçÛœ{M +>®i`d ]1äR·œâÏi£ð>Të¤$gÁ]lƒhv1õ.~ß$o#V(KoH˜ñ¥GG÷“f7c‘TBÏ›ºéí€Ôü‰“d,Jeª}Çt6°‰fÛ(P“`¡ÉlYHKÈf£à!¨;•aQ†e@$‘ð¬è…ónÈ€=+¿×•Ñ Ž™2Ý2tic&ÑóèíX¥×pºa¯^¯"%âÌ$ †o˜š¥$²Î…í~CcÿÎtA~Té§<·ÊÖŠï^ñÏ -¶½}‰ù:ÇÐLYoÎç£Åè µÿÑ~ˆù6¨“ZžZÝÁd²ÿÔˆh¿ó×´ m§ ¸@™æÇA‡ãP‘A“Oîñîâ§ Ë œŽûÏ ëüÜÄ à—YUï<Î!¸\Àœüñ7!ª?mÇ(2Þ¿ü(ú^+>ÜuÖ:Cœ×w„³GÚÑGµÒDú²œÇ2n3òoJ–\},e–mb8ý¨B¿:ºÊm® 6Y4€ø+„È_ØJhÅÕ0êWµèK+µF˜§ê/i¬kõ£˜l¾’xqÀ 1£vi},WÆ@Ã
+l>œĠ_?Ö2ãn“Z? m~ÞdØ>£KÀ±„1?OàXÙâc[ž—û®“X¹±¡=ÏË kZ6”õ±š”5ÊŸwF`
+L"œYЙˆ_û|<PAm¤Oý8½˜´ßt¢îZ¢Åå‡ËÑYZvm¥ øa:dÀ¶×ËÜh­Z^x„’ŒS,¤L Ó%æiC|£Í(Œó F³ÁŒi–ê¯Wh.¨(mgÚt;àPØñX8Þ’Aœ;§ÎjÉRëáúÚÌx—²cTÿ*¾Në ^
+ê¤pmûÎ]¶¦añä”U©tYýIKÁùÁÚ¥YY­JëáP§/‘úh;g"?Ó×ÐAð¬*7öÒú±ºÔûW˜k:•.é#Þ¤Ä5¨ZbâÞFH‹E¶È¢_Ñ&PeLoO$žÖÂ[…ªÇëâ°ŠÎÊÝ3’ªM9jk!æKs£#›¥‚€èpˆ’ˆ_‡ý°ö0S
+×ó°Å—!HfdpAt$:à Ÿ™“ÄB™ A®A 3×7Ϫ}¢åZ˜+ž‡šÎÚÇAoÇýþäNëêïߺl£EOj)iˆ,Û!èë&èû)&¡Ì¸<Þ/å½k>‰¹öžÝGAGœÉ,¡Št0U˜‚ÿtA¬kYÞ‚~òÞ*K|üM¨fƒ‰ì…Š}™Áa£‚ßiÞU§ †(…ò
+H‰t—Krd¹CWà=xÏ¡%RãêtDª÷?퉪xvÞœTÙi¤>$@>ÚZŸm•¯Õú´µZ£}þO¿,ªµ(sz؆„é^ºÇ8ˆn­U>iËãóïéƒ%Vï,u@uÙ(ËlõÙö2Κì4Ø1×)>We™hµHíám¶f{‘øZÁ
+Åú½åNsŒÑ‡o–˜RV¸[ Î'Ĉ±*‹_ «Þ[Õç )úÆ*}”šÚÆì>zo#w²æ:Íüw@î#¼Î±ê^‡ï{¯TÆÖLÈ0J9–·~îÔøGœµ{€œ­8^D·¢¥Þ¬S¢ÎT¹ÿÙÊ×ìf½t•/Ö×ZVW§z³%$šOkî[ T{[µ°XÍ"ÇŒ2Bõó¶× ›£v>-äÔ¸;XCmÂ#ªOˆ²jd/{Ÿ£YïQkîä0K§[Ù8Ñjå.œpu&ÄëµØ5!M ¨|oZB¨‚•è´3·êÁßÖ¤4#r+.Ü©ˆu@ƒvšÊéÎ:•‹Sä»Ê\‘¶±Öxд29üd¥»ÍÿAzQo9ÚÁ%ü¬áªdW¹ÖpxÎ^›sí‡Ò BïÛ0ƒqï³Ê`F³ÌÁÜÚk¥6ëLe™Ì
+•¡!$«6dcësŽu·¢P
+A ¿ t¤h kŠÄ·é"&Ä‚¡¾¢½jÄ._@øiÁ©¯}—èv1hÇvñù*X»S>Q¬Âyæ«ì Ò'ž\#7ú®ž›Tj¨šÕ¾šziÓ¼úA VcÄ,p=@D=Ú C0ýó×Ç?å󯽌¶¯Vw}:4( ð)Rþ»A…~™ôpôx
+÷¤ÞÈ@OPU™åó¨ý­^+B6ó0³.ïÒþ… ›œbÅ„Nã@ø¥£€ÚZ ˆÊ[EÎX/¨‡iŠ ²½NPÞîF[n&ìàRë@ÐÒa
++}+®bý:
+YMQlºìÔxLæéó÷ÑkŽ\ø7¯Nè0ÒZ³WÃ<V¿ÇY¦a,åô{5ìƒnb`y†™Áb”$·Ìx#bI= *€ ÐB<oSu·ÁPbhŸoò(G*ÓïXMBón§¾ARt`Eg–HO ­(´×° Ô6kÃ'‹Òe‚ãâü ÇA%×Ãy3.à}ò3Œ)O '†™Í¡l(̺Åážôº-pEÅÄ$#Ú$ lE$΄­ÁÛZ–'h³{aðΠëE“ßî:P]Ðkù“èÔ=u›_Ñ©¨æ§ÚPLî„÷ôºŽº1ít
+1`áç¿o@˜NïøÌhjW4ŸE«ü™Q¨íg 5CÁd2n˜]´Ì'
+–¤Kdd›'ôŽrP{ËzZH"ªõìÊzdè7D‹£8
+FRäùöò¥"4‚Gõ
+Â96›åšbÔx"ªg¤’™ÿc¼ÌqäLr(|Ý¡ì6
+±/æ@n»s¡Çj™sÿùÉFùG¢K€
+Ii§
+nÓ/þ:U
+iL‹˜\”òã+£÷'šBrðí<Œ][PK“KºVùžŸoŠ`:G—‰P‰baå‡øˆ Ù™1ûPST2TaQN°ìLºln‡UÄÄò‚<@ZîWx, äÒ»­ƒe€B¼âüƒÜ³Ë†Ñ¡Þb%MreâZb`˜R³Òb+òY¢¯fJ¼ñ€†5ýÝo•åzp©ÉŒ¬JšR+˜5OgC¿1P¢« öUÁ‹n%] É„{[¸04td
+ðgÎñüb"ÆÕ=¢¤)rL¿hŽ—ð(H'šßh^ À4'Í oÿȋijøÏdyÔÄ PÃ$É^fÔˆŠz)Á˜Sqp®^Î2²ô¼TÄmMæMÚ¼8Y5¼b~é9v—ö§<ëH8!,, $zØ\\àåJöEQtµv½À!ƒ#§¶•|1‡+!}=à“%ÙKŒ”mH_¾•6ƒÓ É+A´Ú­D"ÉùЮ’Œ—,æ5°Û±ünŒŸ»7™Í™ŠLÁÇ·Âl¿÷ð
+ÄÝ›È`^ÄÂJøžÎCt®£ä VÀšññšYÝÅYÇ ÂÀ>vx½@Ž©{oV…PŤät³{ÏÁ{±ß¯¿Q@ DÔB z<'–7M—"+á
+yâ_ë`U
+Çü>V”±8¨^*ÙÓK’J
+¾ÍK„Ä a¦Qs?•œfä @w@´±$¸
+Îñ”] s€{æa‰R!{Œ">
+Îïl20¹p$Û “‚gæýRÑËv·Ì{96™—†~HöæõòE¦vË:‚˜ú¦‰ø3€ñÅÝäh"³Ê Šº<ófvål§5¢¶éŠ¸+˜Ûdf¯ƒ å£,¸µ[5±ßF…O–]R.mÅ-pOS§•k‰eô®a*hpPéà'¢Äì8`ÅôñÜס‘[‚F:ù’Þq–…ðÕðãG¦žŸ­q‰}n'µ»òZUºõLÇš¬„Õ¯=ž“ «Ã ½)¢ë4e"vßöVˆ¼5Ý*q-Þ£IŒúÊwk%Pú–mÀJ_Ý­HM Á8Åq4Ä«‚7ë—"+ôkqÛü®¤¡gÍȺ”¸±Áa)‘ÙVÞ­Ô2H‚øj” 2Vn˜b/  `¦øìÄW WÍ«¶Ø‰Kó܈+CEÜ`o@Ù|•i1'¸JhH&â
+
+×K·çKElW§ÌœH XoE¯‡6à~f±á]
+c|eôëYáÁð•°J¦8§y]_'i/x¬GÁâU5ºRW¯dsÅ5QÂŽH:Ê }ùáÄû+æáêÑS)Å(5ÖüçÔqºÇÒr¬mf«
+ØÛ@N*5?+|#® µ’•â®«€~´ fçÖ£H‹x{%¹Y,ŒX}Óf Ÿˆ/N»½ið³ä·‡ò­žEü‹Áâî~«çià¥$€{¬on…RAÄ$?
+ßêÙ<9|"œ|Ü[œµ;'ÉèÍKýVâ[=ü±Î6Ó<áw¹ÕÆÍÄcõï6 M’åúÂH}·Éc+d׸(­Û¨ÿt쥇.6ioøàYôõÛvÒøÇ39i$EE°ÉÛ#2*ƒm*b‡t"DÛŽ~+ª»Ê±‘pFEª)ãæAÕ+ÊÌSWD¯xÂÏ…3>ìÕK˜>ÆÂ&1éÑ(šK‚©³Ø "žƒ+ œÜÖáÑGÉ %pADu8¥<|«‚þu@Ò-Y¨5ÉÓp7mÀÚBS*§E0|^„î7ý²´*ê1)^¢,KÜåʸS•tæºÊWt—ÀöipQ6"ÞÄV˜øLá)΃ÅÂû…訒‚ãwVʲBOëS•E*AIáômÅ*…[Ž]b+~¯ EãR*Û!ÿ‰«ø:9c<äÜK(ñEÉìì|¯…„—_"£Ø赆'ë
+Ÿ3šG0è\±Îcªƒjñ“àYà&>3ì_2XtJ!Ì7’f7.•ö‰AJõœý¹¹šŒ̳o®Q%$MÌ̉g½K²eY BnWc Îå:&A¨9rÐ@
+‰5ŠøûÂ}lı €‹1ì.|9ä]Å®ïù{…oD¢ð©ñÂ}•D,bê
+Jûð_µ½}.ÍRZÏW4¦p½R$G@T߃–8,rØNÃÖ1lÛ‘ÏVfO;~×Þ”ð¯Ì59õ:c÷,š@ "kìç”´@JNsVœF!$3i^±ônn\.a$øQ—Wîn@¤†ÔBœœ #Î&"õ=%^YìOù?ÆË;²c¢[éðä<Øú&w!—½W7
+
+r.áoœ†˜Ô+j†´ÒäÚ¾‹"fù3~‚ߤ$×æßEu(À@HP×’éÒ<É:Š‘纔"¦ÄZó±ÚuÓ¢\–W‹#_œ5oNç%]”oc+4¶á;)q¨ ™¤Îé˜%i߶¤2–’ð˜…Ú0%³È1#­½•%0†7ÄYü¨#£Ž´
+XVrÈçé]ò–âýRNÐÖd9fM‚ÌÝšÑMØŠÐÕ_tóK‰OÖx.(¥ó‹u&s±ÁZÉ8‰ÿMKã)‡,Ê• ™Lu¼¾ªÁéíu˜[€ÏaÊÇçÕy߉⧿îsèt
+pBÀœª¼xþõ?öã˜jiÖM~[ÍjÖÎÁK»©S .–²Õ>êÊ”ãLªÒT·xkNËõ¸ ¢‚Ô`«ÛNXzå±µÕæÐÖÂÝ ”’}+ÂýÚÐ:ž¬‚ý"`N/ad˜
+Ü‘Ë/DŠ¥ vŽÂ™ ËÌ㖣܈WÏ—¢÷EDFƒfàX?¼Þ™PZÚ9™?‘r#±§$¡w¢Å½Ž¢1ýMOZš—0W Œp’ÚÅ$þm;Õ±y¥åbe8š-“1ÂX••®­úï† É ¡q‹‘{k7¢sltFPy”æ+Ñ©c´l˜² /sqÃ\EÊ„dwã´¾ÆVb=Y¦Ïµ¯Ù+V&Š)º‚*šœ.
+°ý,à’y¢7åVÁÎSšÂ£¡¾†ŒK œˆ"0áË<à³äAÒ!câ§*”cÀ$ļ»_éïf Ûqý G<e| ¾C=Q0úÂPnŸ½©+B–«¬«¥öübéiŠ€Ñ*
+?·u†üSÕÅÚòœÂixú©;´¬
+UW OšáØ `óa§[íH¢‹<àc ëƒGG¸(®ï¼heVÙ–r ò•µà100ªÌ'7ŸçVЈå3þµvN>q/­Ç½0] ¢©Ùé–ñeå$ßv·ydѵ8ó¸ž$¯T÷‰¿¯ErÝEÀ-g=%P•œþîSþ«žKÙJ "®!RëA¦ÄÑ¥‡åP²ŒŽÁUÌÔQ?ºF ãи†­³ÈX‡íç¾\3Jà4e+oEŠh¾”h«‚ÁçF¢µóÔe `Bê¦Ý}_ÑÓ@À+ÁZå(„Xg„ŠxçŸ58Œ™˜/ *SûaE‹‡œI;ûJOœ€ÃiŒ‡¸ô‚,t¿ö×
+`È@¶]´ßn«Ð‚Á¤š•\Bò—B«D~_;‰ÐHKÕ´ ‘ûFßQù×M„”®ÜxÁA›.rçDåZÒÌþ3öì=?·úo¿ ‚_Ç©cDš±F–O•)–áýY„ \"AÀ¿T²FÅ3‚Vâ‘À#ô–ᯄì¢o€l…¶a›¶n/{'0Ìåæ·ùˆ”«UmÜyk®B+Nq^%²h#ióÂL•R«²(
+¦K·"•(ðbÀ:ŽðÓdƸj†ÁJ6x 9û@Y—~ò_݆w>’LðþØ•êë€wñp1À¼[ƒÏŸ×ùs¸o‰ÁPøq«B!%æ3‹ììÌ]-aÖãá%DP´Ü4«_Kƒ3•€V‹’‰ëkÀ‰4ãêÖd…§èÔ'yBHÑ´p<CŒNÉzÃ%Ãà¹6/!‹àŠdüßJbHÙª ?ÛþX‡)¤(
+Xô|¸Þ dˆü%
+Q J;’дÎÛ %¹,S×3;e‘ú:·¬%{Ñ’±v‘z[‡@Û$Ûˆx®`ÔŸX
+€eS‰†Ö.ùšÿ¤u zžA‰% ûEx„· «Ì¬]Þ{n™Êh
+ÃáF¡k ­YCëjló(!_a¿³ø0¶¡•r¥ŒW¤T˜²y±!c¯³â@†¤íh
+¸@ž±„c÷a¦ÀF
+“­Ø*ÕF€êPL¾"ïþë„ú9„
+%;ËÂdD D©‡o
+·‰È4(Ûç`Äz:gt={YàÍSKOÞ_§ VlÏî9°5î€QáÛã "p{Áü¢$»„Mƒ !Øv—€8$†AÐÇõ)q<áxôc´ÿÌ<*à ú”ÁÆ1Èø8ñ‡eW@öšÅÒØ €%dè&„úñÃ+’ânL6ÖâÜ‹F¨[N@Ô¶?Ö¡ §IìàÒ%'4~UH °Âø—l$ª¤ŸŒêó:÷¢/ºúúŽøîlØ$¹…z’í Ìˆ(G ý½Šˆ]ÈNôV,çH§I\ îÔ#2ÎvqýôC#M¢EÇÖ>3êå…V:duΧ.É‘’!ß>õ¨öïEN
+Ê.e4Ì”eE×Ï]Š^ßéÂrêì8(!ur‰cÍ›\ XéËi¼ËfÉ>Îpœò—"•$™_û½W&£ ¨c‰â°DB­æˆmŠ¸ž¼ ó/':¢ôvÐ8×9MŽR·®'€¢G6?ƒév †ôÑ-µ€ Öå5 -Í÷ÄʤØ?sì—¢×.B7WRR3‹¬“s9å¸)A¤ æ)†ùðr|3¸Âë7ŒÃ›¤+Àíµœ—“=(o«‡Z‚NEd+V“{W!ÌÖ¼ˆàÎó5("ι<ÃŽ]à~0©‰
+ì$÷BsÔäöY%mALÓ>ű¬ðDÕÖ§xCÔËá‘T­Ë£NÅ„áOë‰?܃õqâ®S€í
+
+ÈÂÆ‹¨µF®ºM¼$%¦"Òé.)ݵqõÉgÂlj-0ÿ%Œë˜ˆùÃ݃Þä(g˜·
+á–1Èj–“j•}-$·Ü$| ñÈN8Ú_ÂóÜöл¨ªo ‰5Ø *)‡‹y¦CqñrÎp9¢¬ÞAƃP0Ö(½"Œµ¢?3š½UÍg­’6iÝ8T¦K^ÂëL(²êžûå¥`{
+ô¾€¡YæUQò(hÅ ¥7§D_RìäüÁqÑ=‡ýA]ìêñ2p‰¶¯¶Ò·ªI0b˪*«äMÿ÷"ö!ò¦‰Ë% ÛPëqŒ=Á {®\™yjƒËà˜Sçå7‹{ïßõû x
+ Ã΃;ÛÊhÇZø{b̦W‚?Ø-ÀÒ® €Mo€q࿧²*ä‡pEèx\»y$ú%ýâ?-þ3ˆR¢5bc†J\ Mâ#´½ÌÕ“ë=wȇ…J!Ä‘Ιå£ý -»Îà¾Ë‡á/ü=(P„
+ÐÓQh™ü¢{.¡(Iæ3ÐHù!¼èNLçqRµ"Ze bÆr@ˆÅÆA°ËÅŠ
+¡¿Ç¥ò\=⩬J%»öô>Çæ2ÖŠÉnøôCc—cØ ¹xØ
+†ÈíÍ[øºP%ÑØ8Ü’‹Tþúºv® ,¹–ñ´†Ù,`€/L’Õ©éAÁEÜÖוؙ‚RÇ3³*‚ ÏZ¢*Ìs–Ø.%Ü:N’˜€µûÖñÏc¨
+Ø ™ê·‹Ò»V^í´UÄçh³‡vÛsÍåJšz­""
+ƒ{—dŸúÿ5üyl’€GÔ!öä8*„¬ˆˆÚXMI-è  ÃèéÄ3˜…oÄivaÊ3ãˆeNWÉÀ.ó’%-V‚ÖöI%ƨŒ
+øn·tœ É¡&–¡l%,k˜´h[dìy
+œÎ;бÂƾmöãºúH9v–Ñî*vé%²éS ³}æ^ƒ£$×D ¢}‰Y†Ú3&¤ÞÀÖµ\Þ¶¿©øÚ±˜¿~lcÊl¥8Ÿþ›¥Ni/–TÁ‘Ê»÷—þ8&ICŸ2
+†ËR¿w Öª¡¥CÁĤR\SP•÷~±ÔMY® #2ˆ‚,Ë¥{<% n°ï†™È+&**þµ÷cë&7àÉÅL
+¢ŠÅ@^Ymðš#˜>˜@ú‡ìÛè†`dŸ4Ò”nêÞT–ÔŠjŸ¢J,X¬=¨.Îá3£lûôµâ–þ,ùé• ÇÐ@–tI)95xØR:¨Îul%«
+Õ½4ŠS,’Sôz(Âö‚3åsµç€KDFÌ1&°„²&‰÷Æ.ÎÜ`’Ø­;˜¼,×]LJP+©¯Ç4E±hUälëNÕ›{6ËÔá5´«ÊÛlîÇ·Ëo@
+Ÿ@w˜XÁ÷%tÉ¡RŸ@ålRßßßÁ±»É.•dôOxx‹itQ€Ãwòм`z<½¼cEÙfí˜î˜RÖCï’õôa^ u§5M/B(É(%+D96bj½šŠÛ<s"v YQWn·"• ê„ΪYlö.W¡æ4+ÿS„–Ê~±Eô
+q¡lðÛíÆ¢ö<¹
+
+øŒ†^À½†`pÕ¥f)úIôžjú¥¼(i<?ò¬ÛÞ¹³Åçô\8/Ùm¦µd•Ý1„P<ê"P’3J Ù‹yqz28·eÛ`ùkX,½RÂŽð;Mrs–‰ÏÐöúÒ1%ü‰#8óAËtÄ”ì‹
+–ÛèñM
+‘õ©î®7¼g†9§B‡!¤o¹^ ¥§¦>'*ÍI
+E—™÷¸'3AbªFJ\òõ;ÃòOVjÙ9a‹0>‹ñ‘°TÉa±»/ÑXyCá¥óS[†„7/9É•Á±.c jA1ÑÌìn«'ÐÆå@lzQ’iUbƒà€(rÔ•!š>2fa­øSч È3ÿ³vŒ†M(qúþÉ0Aýý>°¬Î622³haúN›“eUjÜ=&Š0
+˜yð‡Gþ ¢Â×®‚ƒ¥G4+Ï›÷»Žó¡dŠäÇ8j¼úôŸ`2;ÌKØST–Fäµw"“*ñó¸„YM²¸r<‹¶Ìré )ww´°#Œ£¬»öît˜4åB//JœªÚÐa¥H‚Oà%•Eö¡“q{ø
+Úk¹¹Ãˆ³[}ÙÏ^{ n•í,ú*—á^ÒÌ\TÙéJh¿´6w!Ãh(Ê.ÂÂM=‘Ñì;üÉ6yA0¾OÌu6‰(VÁ”ù†Øö¬ÐA +ÐÅ8ÈWºÔ¨‚ŠAFtÄûU(1ãœÅ\éÅ“X{*@|¾¶†âS熮 ¾•< Ê:Š p^ ”Y׫ÎÛàqIàÏŒÔõUP$Œ~€I?êìŽ<
+nŽþíÞâˆç²‚Ùð6©‡’…‰sàÇwNØ<Ýæ¿ãU7gT¹€ß¶Î](H‰â€òçVêsóØP¬™/9wáp‡‘ÝÝ6i¡¿²Ó¥èçµ(½ãÐ
+z´`@ã)€§±¥íÅš5¬v Šœ ² ³ã^E gl­¬±t|^Š¬Ù0<žDtY7ÇÂ>/Jmøü£2𹻶œ%a3á
+éo¨Ôý*Ë VZ:•Ð½Ž­Å\õz» ¡ã-A»”üúz„³ïµ?þþöýŸoEQDÉ âû^møPZ!qàV4p[P"VÝ£{"Ü¢*<jÉîÜ/E +×®.K¢U„ØU­(3Pú°¢@£ ªÝË*²½…}o!¾ËsÀ«°Lm1À°œˆ
+ŸÌw¾,âKpoÂÖÅCDиÔ[DŒ¥,tÛüÑè:ú8ÅÔö~Ö’L`»¸>`[pRä]`k%EçQTt)ùeG±Üìè‡m_|‡…‚²ÁÂíÏJS†%“ž8ôrЂ`n«­¨ƒ “ì¨ß"h;Ì" €_³Öò“4‘j4’7ØÎ"D*°ÃÔ^]*òç!È“º®+Ô‰Ôµn%´=Ó¦1ö‡]]ª€Ð ëg*éA"×µ“Ó/ƒ8uÚÒ*PŒ µà öšuø@áæló¯E’t;'ûÝŠPJè.D¼“åÀ6kW«qv²n|Ç
+”Ÿœ¹ì Ã_óÁ¨EÛ¯†ñ€L?f7U§Ê¤ÖbÝÃãÞО´gJPû—ñ2IŽ#Æ¡è | ‚çµz©[h+ßÛïspwe¢Bå…#,CLøøßAmbãÞàKÉ÷œ%¿Ÿ9žŽ¯Î¿çÄGX‘Ò¹ ÿÍ+ðü5–¯’½¨´¡£l=:½ñÀwé¯Wò4§ïw@|Œ.á­T{ÛáërÞ_·ˆ‹SYñ(ç‡*äê ü´ckòKy–T„‡¼
+¡ÃS¿&gŠøñþÔ/Û¹’AºØbÅL‹;úb®‡šfs‰ïEŽG°—zk:Þ.ºPæ—û¹·ŠÊ̈ÒQlPŸ€õŠ0IÖÀ=Dœ6ýoV !©žŒˆŽ8²0ˆ¢
+¬‚TVX½žÏ?^‰¾ ö"â¼
+¼½]ä,Äýso]–ÔÇú¥H Ÿ†¶ 0S ѳŽgÚ1Ý1 N‘J”Š W‹ ›Y`“f‰ì †û”Øx…H»í~àËóo!:E?/Š ÛŠÿ j²aÞ¦Çò”½8ŸÐʱϼn+à%¶±jꙬ¬ä{¾¥ü2„Íô¿Ôy°ž…©â_´á q6y¤š€}Ñ“&}ª?”#·¹øçÑDiÝ·iÌ:`dø5JÚcDÀÂ\ê\t‡.ð
+‰9®ᘱ%ÇÊrH‰yúEY±T®­fž^QB©ñIÖü˜>.#ÃhŽ¹Æ®g…¯§ãl»ö½ØöaƒmO2xÈìîrDY€‹Ù¦Ù‘a¸‚åÕåÒˆ&
+£{~ÎèÍz+ᜣl‰[":Ú.G•©·„¦,˜š ×®Ç]ŽÀú$Ú2ÉPp'©Â\!´ÍRø0òÿdi?¼!¹â¹bÔ»‡_Bh(lvˆ¥±9Ðû_Š~Ü¢$¥gÝ8š—ͧóQ4HݨÛË"©¨¦RÛ%
+¢/Ô¯Ûüÿô<Zs®b™ô/u<<£3¶ñuvß=§?Z‘b"ý¥u÷6sfŒ¦
+¶õÅ«(
+Èf#.ÆÝ ÔV@¿`Šéò»ÄÃU2’b‘ÎΫ"ÞSÓÛ¶‰Ç"@ƒCÁcÌyzESùÁqÿ¸Eå$áħÈÏ×ÇjÇ€å´Ù²UMiq
+%
+°ACùôþÝ€#sý(¡šuÀݤM’ >†òNgðÛÄ…0±Q¦ºø|ÇûwÁÕÒÄÊßÝ5?2 œ[`õcWž @§“'»Þ ˆ<¾@½h²kxmVK!‰vö=5åúÏo»%P‹øý­šøÇ+ÊÊVà²Ñð ç{ÑMOær
+¿Ž/rs¡÷Ók@\ /¢A7غ_^É}¸
+¶z!E¿"L|ÂX“ÇÑ>t3øºžD
+5”Wþ€M\4ÐCvax\¨
+›$·žã¦æŒÝ)¼°öŠµkLø¼X·’ˆï€}ØÓæ\NÔ
+µZvMŸW-µ-JtÏbæ½®ðH’QÙÿé©dO»¨; “ítÏ©QÕ 4;ŸŠ‰«€ØØg «*M•êl½Ñ‘Z’^Å;ø
+‹¼U:"çJ²,õµ¤
+§Ï.8Ͻ‰XœÈâ£Ýõ„Ã7ŠLÒFo‚¨²Î"~Ôå•`™¼ÖÔ-ÒIÌ”B»åê÷…ð>µE—(ù%þмåo '2"? öŠ(Ñp”Ù†RIó“a$1nt·ðKt[™Þ ­‡“K=´]ä9‚º•mêô| d}È"£Oyj?‡8- jdnû–
+€NNgy@ZcêŠ,;
+rŒwħGiuZ%Ch:wŸÂpQ1”2Ö‰’Þd˜±˜þÚn££B p¡î”èì~øØ£gÏB °ã06µùÞ‡""Ñ(X‹Ý^œ`$.*¦ý*Ü(W–ÉoPÒå‚hDÇf厙BÉWÓû8E—XûóÇûC$ì2z#­]¯ŒTa½p-è÷é—’õtSÚÈ«œs‚üüÁuvrÉU)
+ÏKNZ%Q(ÁÒ¼CkååÑpŒg~ªÊYÌL”øöð•ùeŃ”?| ¨|Q¬s^I ¥$ãßï`ùëDÛ®àX°
+©%¶ãç~6GÏu'ìïE\oYå£J(” ±é‡'>e˜®Û³¬ÁµøQˆ"ì>‚¥N¹y‘Š’#TÇì3æI,hÏXq߶¸U¼1þ¦{Eeèi‡Îm¸KÕñ
+R’#2‰2oÞ¯Ú%QLŒî Ì>R«ÂNc’yóÔ€pIZ¢j`†Uó¯qǸ¬xÓ$.%~ø{ÓuwµhîO}±\ÿÈÞ¨Ñ
+¥‡¸5”Ó…ÓÇ~UÊXû„áâ ò•ÁI;UßI@Eæaq*}~»è&*¿Ý"œQýdÙ¥,eí2M…FsÏ«¤ˆmdÉ*‚8S¦q«:‹w@_{Ú-är„#pÏ9«†qÓ
+Ö½Ô´Zó¢—fOQÈå-4-H¤± i¥¬[‰Ë„Î1¼wjv3¯Þ‹¥@s¯)ˆ…¥õÊ}Û)'å¤Ù7
+ˆK²
+ó¢Ä½§`—ýX\GÅ,L+´Šh¬Êt*Ö—F†!Ý\¬á#ãmËúÃ]`¬,cµôÇ7ñ )w.Çèô«M¤ø€×`¯âÓœö—Ž"â \$2”R¸—a?ðU0?«úð¨Éˆ†c–´¿tk .€uPz]º÷
+ò`j^ œ'†*›{™þ…ÐkÙÓî¿©¿&´<q…=]X¾µfÈW™ØPýþž·1ýüá}ûo·ˆÁ‡Ä_”ïÆNOüoï›Sîã! ˆ$bš÷Ëï d2tùp¦ÀZ#Ç!7ˆ³û4y+ñ7ë8h Ô„.÷2j»!ý£Ï‡G 錹XÜæœëB¶à‚³ÑchÂ-)t ¬ö©/wÓ RÃþÅ õ›ßY3*ÉÚ
+ÃX<#
+v±^Zz·?Sš™÷Õä™÷ÛI`l¢°óƒkIÜ âY¡LŒËc{>QÉ Ëo»YZκA1Ü"mAÝ
+顇«¤ È/ÞVê‰ rƘ?hÒ+:à¢û°e|®Á•é»5|,Õàά…N \W
+a‡$kOq°/üñŽ¨dòpЫ¤àÕ¤zó}
+N¡•±;˜SBTd‡WV¬šYæêO‚™Ó»*lÙÆɨ$!ƒž¿ ˜–Ìì\ºŸæqo‰ºº= F×A{²'ÑN˜¦ÀG„€í$fSѯ/e¬-㯲#cÌ0R4bˆ—7£‘öýWEk†FäÜ—†šÃX g‘3o$Dꑳ›~^óŠŽþƆ^‰„…(A6YŠ}‹:Ì’"†_Ù2ŸEõ¾e`ñÈ•ÈŠVûwÇ ùç8Ùf@%øE_‰oi‹,¼¶µ™$,Åu®¿“ͬ
+`¼º@G*™ û\aþÛ°Ç@—Ù'–AHdØV¢¿4œ}*¿´ëA¾Ä/* “6£]ù¨ÕNn(ü˜VX ÂEÅ´IþÌσëÀØÛü½¢ÜÇ„jòdymjg _&51&{vffØY¢æ`M%Dš¬Eó¯uASY"ï&óW|Š2¸¡ÈB¯·sŠ¼DjzæÅßUš”þÏx¹äÈy1ø¹Ãœ` ÷címn1Û™ûoýQU2Ü-5ÜHØØz‹E"@¶Ó'’å#牰jB$E¨Ž”ÄÛ*g½£œ¼¹\éäßñ4'‹Ÿß÷Ö eúz§cvúAR,P'Öp+ûϤØë èø‡8lì\òë:ÅjÄ„Y×ÉÅÜN£ñéš~ÖÁQj^™ŠÄ[úYÕ`NDšÓÝÇäYö˸åí1CUž4v]¶G˜Œ.smNAÑ©R½ ˆ7f.ü Z—’QÞ`å×êæºÖ€®u然d†6æc­‚
+Qå>½ðr¸MoÛ
+Æ„æ^1³n±æwñ z¨ü÷„ïG®gÏlž×^Ø*’/k
+tp0Å3õo¹,ÙǪ»…ÀVŠà’§âG˜z Û:]‘œ!¦.·jÅ{ëwJí—ÜrWʧü³h‘¥i‰\Ðïë@®Fþ`¶ÓÉ;ÿ,‹Afx`†+ ¾óOI° ®Á¦?Æ‚ þÞŠéWýÔPïóÑÆê–•1c,¹ÃHyͶ^øÖw ßH>šËž½ÓœêãRƒý²($­rF¬E¯ƒGóÕÕ ú ²`0‚há hœÆÀ¸hÒ³„°;/% íÉ œ1"§áÚhØThô0BŽêMR€¯Hs­S±ˆM>­Dß
+Q¨â±äSjaQedôDØF‹ýF•û*ÈF×SõÖ^œ„?NJK“™¾Þ(2‚è^n¯s¼Ì²,šCõ¼'ä¡J¶Õ Â5¢vzV»Ôyª2øJ£-nÝn•hJdçšou<Nìò´m=ýÇý¥‡sj®–ºÕéb;å>Ö9Isœæ$ßåR‡³2*û†d¯w´AÁ…,'Fãx¨Ò/ï8~À¾HhºÙ`ë8ÔŠîG)^xåtØàËn?ý¯-¥(+“™É­{@‚¦±òj…ÐÿÜ0ç‰ÊüÔ\djIÜÛ sP®›Jˆ:v{£„A‰ƒ ðuÿ¦+QÂMR
+ÿ§–)vbžŠ.÷÷£J™ˆ„gý$C(l
+”}§ÓÕeßIå$Ð̸+õ:t^z4E ‰WA!…”‹–DŽú©ñ_$-©mÚ$|¶:cV"RaÃáï–ºƒ|ÿ ±l@X|§ê3Ñ•î|åÒ4¨uÌ°uè1z’HÚŠ.UØ<¥ÄÒ!qò Û¢ UÌ$©„—‹Ùïaj{+ö-’J=’ l¡µy¢q×VÂŽÀ@¸‘oqÊÙ£
+Ñ:ºþ½®ƒˆ…Ž„¥ªyëG2ÇbƒPYôÊ;ØÁ´BðŽ¾âõ%s³0þék©¡éÆ“qs¯æ2óS¦·¢Š6™ã ¯]‰VQcêç*ñ ’ Šüf€¬®Ï =N–²ÙføkfXŸîLÄ„ØP˜Ö kÁjuŽ—}(ú%ù[€
+H‰Œ—M’U¹„WÀîú„-ɲ<æ ÙoTìÚŸl:¨ã Á_U^[–R©T£Õ××/f~ >¤GÕ×?Zýj͆hVJ}L ±â>ø; o_þL¯n£E3©C%azÕ
+ÿ4î+»¤hé4—óäk¹2e –׺Èñ ôí ¨Vs‘’œ™3) ]vÌ]¥Ã¼ÞéÞWB觑«pÒ‚˜+G´*b b]ŠÕšDÛÙ¡ýEéœú}^ãÍ’ãwŠ­Àçkó T„:–lö8×é÷Åþ
+-JÒ‚³
+ü§:$¾ÙêT—ÐÂ#觾yÑŠ@
+åu> Y‹JƒR$lŸc¤t4m!.@ââ£K÷U|ºtuRá’Aïd¤H%¨]èI…U­öyŽµžÍŒȆ¤vdHå±+:¼Q´Ôàé×/g‘dÓÞkxý€y°ˆÉõ¹…Õa&ŠTF[/׬ou§Kv9ªðhRÑýn‰OdFŸzJJÒÉúì>‚G‹#ùqÒ•CáúÁrDÊÏúT.HÄIŠqGCúÉEÊqLÈhNu»ù¸å{Ðù"LWpóªJ£3–Êš!ëåB ¸ô­–¾  ¹ß}…(ך£§.ÍÃe¶
+¥Jÿò‡ˆÙç'ý<•K–}~TŒ¿Y@úDÃ1ž veGä¤ç«%HFÖ*“¶ ³Ðxóº jˆ`ü%)íê™~‚*ÜZÙ
+Õi¤P—¨´kô.Mž¤óÊÉhe¼ßã¶PÚaÂfµÐ<X8tØžOE ԭѽG¼;&ÓC6Dsf ÝèåRAËä5ˆg ƒ=YIå÷UÌF"¶IŽÝç1rF8óœ {=¿FÉÉÃ-•Ø7}¦`»MÜ9n|C0€A׎Me"FL<«Â¯)îD¦à/]¹Ÿe ŸBHd}©nC¤0Ô\å$‚Ó¯?Çq¿R„Ãö}PºÃƒ¦JŽbÊî£×@›PðìÌÔ92‡üé{X8™A†FŽOßÇ”-itî™”>ƒ«ºÅÇF´NJè:£5<©ÍÂAy:IÄ‚ÜÂ+±e^'BêäŽ<yLÀš„ÎÙé*èÛ%ƒ~k%ØÉ:–¢.È4!d¥x¹Ýƒ¡¦†ô5e?€>qããËé2ķᯪËëñçž8<üÙ[™?ÆSżå×›sz
+!lq¸whœYMÜo’™bÞŠ€áZx¥/Å€ì ž»{”dà{¡Ž“[Äaòðš¢mQ‘ÚÑ2tÒ¿Ž ÅqcÛø¼oú-‹¿Ý>dJ”dö}éÏ'åýq5FSÊRVÓÁµ¥~â¨b·<{ V‰} áŒ?´ÍÛ·Ïèéùøí»#¯ÍI$^£ßzh$(˜üŒ_¹¦ÓhzåÜð:Eá-jñdU¾¤sC)þÇs("Þ€‰Â;¼£yòùӫΤÚK±S¬ 9§r¾1ÍxôTQ¾þô\¼¸‚õgº¶÷ „ÝXÑš>ÍÏêÃ^¨®6ß{4?d‘îA~³aKUVƒ@°â°§¾1?YU(Î ô`FÏ‘+^Îå"'ó“©lq{R>ÍOr{
+Ÿæ{†mòåeOÃÞä)Ã,^±¯úC{ý·VL k
+YêmÒfÛp´•È±ISBêœàùŸŠcØjÅ;KVêÏÐë5R?ŒÉOQ¶êåÀ¦9#—^Y LžÕÏLéöH˜¬²4x.A¸  r<&­û ±TÉÇ~7–ËMˆ2× ¸ÈY‰ßÚöP-]‚ƒX»Å ô‹2~A ø -sXeÞy<Ä"7‰5ß|Pz ›é¤™Õ™¡=ßÓ
+­«;ÇÓ“b¬™roŒ57z?WÓ™ÀžŽšB±Éi©$ŽÑÕ7ȱ.æ––a¬ÇSžå~„ò¤ÍáIOú=Ró$ñçüžzá—2}ÿ›†ùoÊaHÙÛ\gCßغ'ˆ’õ=)ÖWß`F3ËŽ7jÇ•’u']Ì ¡Æy¥$E¨ ú‚Š×£¨J~†Áê$ÒÇQ ŸU?ˆ*üRZ„õ ër>‡°¡Î샷b‚¦æ” g9!é°KQÔöU#G;¾3Ò`¬Á~„#l»iR¹T&›fÇôÜeÇø0}çSûBd#ðŒ$?o”GÐ/uÿ8‚°,Z›Gt¯Š\Þ°Ò-nÇÆc”]-Ü 6+ ©²­]Urø—'ÛïæÛP ãï·Ep:tù¸Yª‘Áwœ‘jÆI&†Vp.”øx<f5ls™´w0­-Xë"(‰gøOÆ@eí…JE7? €Ž¼íZÌ+±sÌæ‚g‰ A·#Ö^±FˆÏôžZ.¤ÔŽ,pûâçû2[˜ŠRYö¼”튡=8ðX4È7 èH*´ ¤D,‚ƇosÝ+„Eç±Ð’U*Ia‚›CÎæÞ1©È)uȵa£ÒÓ$q;r9Ï¡—9šÝ¾­‡¥/‹ô¾¯«JîlTEñ—ç «G#šÜ+ìæ:Ÿ±ürÚó•Ò×+íÚé¨aê H¼;GFB”þ Ÿñ¹ÝÄL§„\û*¶8Ò•Ÿ~Ær{)X"L-F
+!Á} sÒ8•$ç
+ÍzàÖëzÔ=h2Ý
+¢Ð[x3”²Ñý²K@ÂQävóL~”O±O"}PÀç倃5îÓò¼UŸñFu7Ú¨¤EK'wªÛŽßÚ~ªLcÛŽ¸LNÜ5ox6VäÈU ,Ž’¾ ºÖ s±Å½±zµÿ
+&ƒ:åSÌuð( •wsp1ž1qrtIk€”F5Ù´`Q{õÕ|ñ¾á[
+}Ôü@ßeÄQ÷ìÈdˆQÄóïçËaФh•Éß¡P~ҽܷÏÜ›æv™{óuoâ
+
+ÀœcìšíÏã8ê HhiNWûvP‚U±tc ݃îÇ=}~gDm’óFW…ž1 ;6“øeuÐ< rÆÐjŒ—¥‚~ƒîÏËSƒî²°ˆß:Õ«É0‘ü0ÿAlEõ(DÔLOxˆùüF–<—êR™0R¼¯Â~Ü «â1(©Þ•€Î1!EÌS¶m^€“(™…ݬסŸŒ =à7ôm2õ:oà[W½üÂ@¾‡™%5»9®.œ/ÁßÉd‚‰œ"u1Qœ®ŸBü¨É€F,/¾c|Á&`ðÆ‹Û,bèc^õÅÅ÷WaMÅþòjçälÈ*•…Q¼Lâs‚ïuŠƒnA—<`Œ%ñ8ÜσÀÔ Væô&Bho(³+óqÔsn4” ŽïÍSóœ`"ö’ƒ„¯h£S¡žBü¤ç‚¾óÜ6÷Ë<wßñMÏM|ÈÍ}nþÍD‰¤~üõ–ÞÿøûíÇ?o,3Þ’2ªsíƒåùuŽQŸÊÎv쬀]ª1òbJž–ƒ…°=Ù7”¾ô‘ÒòÃβ$TYË6ã¶Ñô`"ƒÁÂCòì;™Ä.+È‹%úùã@Àt”Ú£Êúµ–K÷Va–9È`¥Ee)#0r¿‚¸[Ó¿`œfß)…ÊòÄuԻ،g§^,‚u SÃmî!FÏ}Ú®µ“X#•s¨Ú©y›<ñ«†V<•Ü[Œî!hy£AH²Ì !“‰l U óÐ>°)8‰žÊØ%ŽLvÑ«€u\®ÒzÛ¿3@kHê(A+áL‹ç8‡ÀÀÆ]GÝ‚'®
+Xú¸ñ„Õxíâ£9,šž™[ž?~Àuaœï\þ!ȯfi%*U4J´£žS1ÿŸå)Of_ÁÕ¬D;,Ö׉f Ù˜|ª<܉›½ûT
+AIUiºµXK³I{¢äÌgZ‡ ÏA@ìc\y´¦ˆ·,vˆ+~ýìÃe R”™…P5?1¦’ÌQSèEÚ‚U‹8ˆã!>â´ØÌh¶øžSYh•v,çc„·ÍDÁxÊ•Ëù+
+ŒBô†¢aþJqÖŸ6™\÷bUÌ:†ÏÚ™4ÒÌô•›±iÁ¢öê/ªùâ}ƒ‚VlàUÖñ;4Nâ=ÒtŠ‡ÐCii>«Ï‰ ÖŠôíRD ‹nqTWÃ0e~=½ÑùæxxCÌ¡ÿ¹5¯³#@UdÛFF“Ð ~Hë7wžä Ó¶›æéƳ§ 3
+¬¨^Ý•…7Zá Ñá`ËVóó¶é!ÄÓj)Õ½â+ ]Ê/*o/ÏÞ‚[™vY"8D™Pβí;ˆ‹@™Ü{æi|µÑ,‚×JÆlž¾Ç?ˆÎ‚³H)}xþ
+ÃÓÙÜ­¿ºJB—§DÓ­ÌáE ¡šÖR?&†ˆ& äe–czO!eŠ“nA¼Óœ¸/S¶Ãe4êr‡Ë1êð$´­fs 9Wîn™A¢97š?§7WÑ:ìYMúNUzñ£îž}çÞ2·ÛÜ[ïðª{ W𯄲yGÜç€Qe–M÷oÇékêªÐ
+úüÎ~rñ½¥÷?þ~ûñÏ[~ÿÏ[OóCº%g»{x¬ t­-'ù‹…«!Ñò'5—{ÌÏ1v£ìB¬Í OAœftDÖú¨#Üœ,*F ÁÂüâ#4=‚»é9ÁÔÐ
+êÚ›™Þ\)ÌÆÿ/“ì¸rˆ® öàäaߌ=õ.j*ïúo
+ž»vìE;zú§»TÉL°U—wW3¡Ç©Àec¯!™ÍÚ(<^k0¢uãQ§LÃÇ­7mâÌp³‰d™„ä©ÈD£€&ŒkS@ã)j`œ%¾2•6üÑ+8ç8þôí‹I3O ¸ø‹27±n½¤i£X¬kw3É%Oš_O%Žc&ÎŽ±¬É£ÍË9û‘5íÂ
+Gl¡˜+É¢2(+ÁRV…{+Ñ»VEÇÒ·}j³Â ®ÒZדñdè§û1lFj
+^,BÏ-¦da·•˜Ý'þ0žÈYP  ,Ë[ &cax¬¸Qä`¬4v+úð"öJ—Lk’ØR–‘™-î"X÷IŒJÜ;» Ѐ™Ã×wMÈóñ#Þîc[”h˧/äç#¡„%x©X …:H'PaÎmh3ØýÕögW±§ÿ¶q”s®ëzÎvvãÞðÓé¦áß+Na±¤kKù‚†öòŽ—˜œÀÀÈD»„­n©6ù®bC%åÙZÅ"I±DÅpRgq|Aªú¥}d510?4¼cb Q)aà•H&£`7sܾ™‹S‰!•T/F
+ ®Ã:ûzÎë3<e×ÛsBuˆ,Ü\r¯#žó Üœ P–
+ŸÍW ¶É8Œ<ÛåV¶ÇO¼s¤‹42°t{\ØL%üRÞÚÛVî&L¬ÙCV,ü¥Äßb™ü70ÓùÍ9p!ÎusµV¯f®p % göÅÌ9âѬDM#¿áºl^@wóúë¢ !¾¬×ßÔ°J£?Ü>V‹Â3ø>ÆîïƒÃ)äêûEÁ˜I{®Š—˜¿‡…‡ÞX1^4t™7^×sDŸð+Âêœf¾Z1ðÕ$i™á§öÆŠSpK,ã_ÊaË/V °wÖ‰ÿÁÆšiÞƒ]6’»-MÝ vŽ ({|é›Í:’Cı˜8Òñúìt×dÑl–°íý MpUîr)²’½Œð˜\”°¤2/© ƯƃážnѼÞšlÆ¢ÍPQÁ]wmÎíBw*pªÑz;ñdT¼ÀÚrŒV2(Ã:õ #þüçVDDiòöK.µ2%kºàKÄi|þRôáC4}G‡ðŸ—"•€f8p­Þôwª„dÖ 1Ö&*6æ
+ãÉ©µÚxÖCaR¹3çtÈ,äàY4
+åq|";5Ò¤›íƒ„Œ;Ö•P“nd›Ñ‹¨¨²ëø-0¾9‡Þ¶÷7,+
+‡Õ×­HEŠ³0'ô *(Ð1LPìSóQð;$¡¶ÃI³ˆ)-îåç,0H[ÇÛp=ˆrëóZ€Ã¯|¤‡ñ™çh€%š°|m}T½4’6ÏòA¨µ‰x»·±ù»xŒÕa%` BÒƒ4vL Ǥ@MÜÄ‹˜v•'@n8†ˆ‹ ÕVÆsÄÀŸx@Yä:.%ïèâKѯkQîܳkF·ùÔ†5݇ášbôk{I—ÕäÀÔÜx²šÏªHTò|kEXf0ÅËŽc¾‡b(§ÌåLùÌo”&Îàás_°h5Iù¸”ø;È¡6Ù NºžÃ&€W.†w*ùXxö{HÛr}SòE
+ÂÅÙä/E*n4naœ5"ügÕ6©¤ŒM‡CÖ)¨ÇÃÊe[¯×¢—»14´‘ÄÉ‚•£4ß×v2L°ÆP¬†À‚ûA©Ñcã/E¥0Lù=¢mÄB”ÜPÕK³qœÓø,zÆ\¶m·‡\8@^mݬ¾Jx…–{$ôÍNí*x™d¸4®çððÄÏZIn%»Qô\ÝìæŠ ½ž›÷Aˆ€`x´øÔ7
+EìůëSŒ”0qÊËŸ™È [FÝ…Ð%Tx6ž|#W¤Îí%œÈ\­vì> AdWì5o5,´ìó‚܃³ ©µz)úð"Ò§Î/¾¨©–·¹ø;#Xbˆ‘\qMú6ÁXvÜeš S— b¹5ôóââ.ò!èÎubh‰(7Ø3`: ?¼‹ÄERÔÃqO+ÁÊ`§á/öèR/Þ20‰\oÎ1ŽãÚôËõÑ
+‰ÉR$$2¸·*.¤ÝžwH²È;Þȳ‘ × £lO•I½ÅFÌó1do3é(‚Š4 V  K R„Ƴ š—FÓ•pïó!B>?8ÇL<ÉjŠ›klš8ž…aâÀùP+.H˜û ¹v¥·oiRŒÊë>˜çÿ/“ܺ’#Š®€{à¸
+- äû½Ž@Ü·ávŽxœ}.˜>æi_V§çT¾®1&]q À’Ï£i‹G$µý)¢IÉÊjc[ø˜¿Ágµ”wg]~GŒzôpX‡Êp½ìËYÌëö^@ÄõŠ¬4‡g­1•")ý·ÃÌ$…Ó4¡Kxkv1ïS°T/<šÒÙƒÌðÉdÉ>¶±Êr²è;C6Ïé
+‘ÜŽŒèº³¤s¹>[ÖØ8./}äÕ‘Ç@$HªÄP›N•'—‰ü++*e{ú(®!]g(ò~T³¢96~š×„²4©è{ǘÑTŽèFð‘$Y9F›ˆ¹™i]¢ÔñײÏ+ùÜAußص²}ÕA\…Xoµj]z•ób“¤¸¤„y×>%:aÐÌzì‡G8
+cc³úMïþ(›?N*솳ë‘ø¯RS½t±
+;“Ã"6;Øóù£aIÞx¸ 6Š| [,„„´Ó®ª· ²zƒSÀô T"dE ÿî<FN“BŽæ- ®5X0oGÎ2üYªkí\ùrŽáUs—7“=·)8†¬)fi¢Ò® Ú÷ çÊþƒÝà%Éö®·azºx-Ïsh ´ƒ8Ñž½€ìd‘<%¹‚?vüljP*Ò†{!µ˜g=¿æ¥ˆ4VåîÁ5@pZ5²ȱ*ßÌAïÜ€’ ~#ß
+sªèðpAWAÑ•ù[…ãmt7"ééUÔ[5°ÙMZª`ÊøA|¹µê6 ‡(Œ’Rç᩺æ™ÛF˜Š¡zâ´£ ªf¬¨YrŽ!“”–j´+¤C„»Ê´`˜&å™í1ˆf¢»;É.°Ê,
+f{Ž«Ü"î)üîxïÚI<ZIGPv … ,Z—š$90nžЩ„bVÚ—û ß »ã­2ƒª§³ãPL„0àês~« –3´BBPëFeÈö»éC¦â«Ý¡bY}hS²‹<.œ¾=7‡²43xn*H7™²6iMήðz®g áÁí‰mK&àyIgçHðl=¨-î—óëH‘\[˜žº£5|oé*‚Œ3‡n>z%E¨ZóPá<>6Ú±¬ážqiÃå0¤øŽ¾;qѶÇ¡+“œ‚çÔH”µCOcÚØ;F7N Ð,dÛrC½Vzcµ&ÞAMc*ý4MóÜšúåªYØR’½ š…â±e› F ÓS˜1>Å̃(ú@
+î\¹ê‘b`¤OvùΕ”P™ûÈκIJ­â"F¬g\Ù>[´ž§ŽâC׃Ò¨q(·"èØÑŠ²Ñ»™ž˜qC’L¡‰&Ò–¾ŸžÏ9ci®fu0¿^ ¿Èä2p•ÂÈŠ0ÝÚ,!‹¢x@–|<m¥Ö.|dðŒ¤òt¯<¥ç¤½@0hôb¦ÞÅØ1©<±iÑ=‡|€f1›–Ø—A6 ÎA•ÊXH‡2Wlßq=ð—õD6ç8ßAΤJ€ fljiáu^7ÊUèfј’ta~1Ä™)^ÂÛ+øÝÓնƛÂö m`He›F…Á`Zn»ˆ”‡§b2Çñ!·«51¢¶¯×½8åƒ<fɬP|Ãrò?Íü\“Ag²ø¦<9‡£\4d›÷møæ…_‹yB
+œÙ„Á\ï‡W¦RÙ@9ñÇ›rZá}¸[æ.ÕW1—¥x¼²9sÁ‚Qä é4ŽMÅ“¢(üˆÌù5A"hÆ™Ûz€dUšf…ˆ¨m T%{ªÒЋ]<Ï:xÂC7
+¥@ܮѢ£AåÄ•‘}ŸÕJ)[«ˆûä¬#{ OìÙ¡†4†îQ£¸ øa´ ƒi'áP'–Ûñ‹³†w#¦a 3¼‚üx‹áƒî¦ÏÑD–
+ = +øîÂÈk}†Û¨ dĵáÎ9—©¹Þå>{Γî#|+·—ÿqŸ”PÈTQÁ ‰$ÊÌqþˆ$½‡÷_oIÙ:Ç ŒIYÿ!PÆ2DF¼6w3˲Á–rO5½‚>=P®2¹¡B«@Îç® Ç+w³„÷¿ÿûö×ÿx'i¾¬Û4§úcCªMoÄ ëÿ½‘IÿOyÝ$É‘*A
+!a9ûœDË‚~\ Ì¬Ÿï‚¾¦àó<EöÓÏ”B%s›Œ9¹[«Ò0³RZ.ÖÝ–HD~±=º6„ô´„Âbå Dç'kçWŽtnáõQ¸ªàÛ
+yB‚8#νsÜõØúðá|yø‹"UÖ)»ÄÎ Ž.Fœ½8ç95%eüñî›*u ˜Ù«Â÷9ã¢;;ÔSºå•Ç¥üžÅo-\Š7YÂÈÔ:§hþG–}/æ»Ì--ˆ»„ôR™QT4Ž;RŽâ
+Æ'Þ{]4aõ3±h(¸ÏðÙã¼üuî¦
+PªBh—sé ¦IÒŽeEsiÄ‘¡fu,ˆYd¹’Þ«Gõjå¹Ô²µ ‰ê²X9ß"aP†â]uz˜Lª'V(v‰3fì‰ a=jØÁ£Õ5’É} 5Òd {Aöy„ÓÍå›sªesË|»^?ˆžÆ"ËHp¬•é÷…¯–æTÅÙ¡ì™aO»y½¨OÇ"…8§!ÝD"Ð]«#då²­"ì É”š3má³v9[X‘+È/"ÁìËVèÙó–Ð?¸Ä¡.‡uF»™;Òu„ŠÆ4€A3ƒ*pî ß¾L¢†Ê;”'&ÔL†ZÞ­®uºú¥ØY&$$·…kABÿ.¤1ßAÂ8¤¦opsˆß3ÈþÔŽj®4à¯Ù:9|oaøž@Ÿß€8Ù wäý9Öéõ˜‰M×âRóK(àq-Bá«õHl énå%6·âm‚ˤ_åŽ!öHPÕP˜5Âzò$ÝvM¸.£X¸h¯²ùí,.b£Ò¬÷|Î6ñ‚¶i27]̧âÛyL8FBŒ„Ç ±q¹‡±¾Ö›$ıÌ}+›líLd%6̵"™LËNxŒû:ÎÄÐŽÉyù ¼æUﶛ×I& 'À¹4T”‘ñtO×bK±'“÷V¿9Fú¥V˜mA¦sAˆ˜â¶ ³×…A<÷}Û­kKÐú¶2á9S Í;9Le˜+"¶B…céØØXlÑD6PL¿\Ü+|Q¢ÕÈA€ôä»âØñ÷ä½Ó⇀\;^Á\§’ÂÝÌᑯ0û:JÁÙy‘ÅÉØH]½“G¤¥â åã¦
+Â.Jèî ‡XãÒUä˜ ÉÀÈû~/ûÆ‘ú16o¯¤±uå¶~EÐ^TúÍ#|>k× akØàeš.mÍ=²Æ·=A[8‰Æù·Šà&”J/ç¾ ê62¡·å6‹V<@èüœ<™Ö9DÛü· Öúè§þêË‘ÑCŸâ-ãz˜™°o-Ø•c*(-=`~=`âÒúÔÂÒC{&¦¨—úÙY#+ï‚ž¢± Të’~íoƒŽK Zd²¤Ž‰”¸Öú@ÕÎvªÚ´ e‚^SùôùN¾Ͳ„|Y COo»’ØgsŠq®U•ÌÃR̸€Ñag~ô%Úç7Z÷~=ê¡òGe6ûQË7Å{=ôJÈQ'Z±4½wAOá^ž÷†<G Þw,&9ö„q ãw1aÉ:ÊB³$èÛšüú|'Û³(áÔp›\¢û‡;ýïò”ÇÆ’’³#|{ô€ú‘~ëšÛÖ›˜×$={D)½³‡‚Ÿ‘ÉGP¹8UFüœ…ÍëFÄ&rŒ‹¨¦ a}y² lHJ±]Õ“é?æG™{̱²”5‘±‰È"!ló˜DªÂÕ´G+\ž>Ç®7–8Ö²î3qõÃä^Ÿ•¶@­Æ+—XÒ"> Ss‡"÷‡ïÅ^± `p -qȸæ&¥Ýî7ùKÖ”±1>"Gƒ~ΕŒ¯¸Â³0_ RÂíù³¡ìRÂù°`C#>@"’‘C¤|•ÔŒoÎ1[Q'O?îḸ‚¹ñĶÚ#}ìxÉ7$‡c± œy…²
+0 vj}Þt~ö0öêEb!2SÀ^ ÅÙ¢7•}©GVÜ€ØwˆðuxŽ~<¢“˜è¾LÁç§X0µÄ
+i=`Äæ…+.ð ó¸)šÙ2ÑØ<[ëú8«J²,¿O›§‘¾Ý8¥³Œ#jÇuÜ mU#ÛC¬[òï~±KíÅó˜Þ.ëŒ]qË«ihÜØQ +Ô†˜ùÖßã›s[…”ˆTn<WHv±¥7ŸÒ¤øšjÖ;’†M'”ïs˜9£Á¼žëœhå—õrË }ƒ»´3Žõîã4
+ü\?ö!É a³Û‘ö0˜wUèsWÙD!Žvv,á@cÅËLΉF~q=UZíÉ'ÝDnBB<:O•S¿C…y ÓYîûøÖ´¥æ‹“¾pÿò{Dx
++U“Ìíõ ó³±,Ò‚PZß|ô'õþ]®–¦%#œï›8ýf² b€Øyᦪ½€ÜO.׈Å`oƒ^]bαZÎ¥'šæ]‹Ç¶¡Gµë1Ü» ?RðùNž"éç¿?Œ‹6½j°¿¾o¼ôJý2æm…GPV+1Mø7d[OjõEb‹q–Ke­’ªù6KDZ4#+ï(ƒ”Í Þ“#µ¼[g“Ò‚]kžçÈÖF†§úX×åG.WŒ—NÓhų&aæmÚAä1t¦ÍP@å$¯¸­+#¦‹Õk GZçx^µGPîÛØ…éB&îPÇ„ØEKÔý>ôöŠÔ-rH3øl¿Ü4âõHsY‘èfA3Šv¦ ÁRæ…ô>!Òg¬ºÇ×ÄŠäºgÂ/Ä»|sŒ?¡UL!ä†àæÕ39qQ[è 2}_æ”’„nÚ*!.”µÛçK§sº‚ü'$«lzö>«§Øp½ýœEœ
+2H¶CýÈÙT(KäÚ&ÃMº j½¾i‰¨øÙã;UTQ›¤ïråQ‡Ö3!¶)ÛŠu–z¹ ñðF.
+Ç„`€“D-ú÷Þ|… ‰¥n-–-³Ì9üý{•2%{r!™°}Ôûȇg¤W´ýï¸Q”
+áHV¶×ü(öJGÙ³²`ø¶iYÄæó$©Ä¥‘ ¬ë{Ìσ;GÈÄçãWaC²Û¶n"6Lƒ1ÃKÉ^^Ķ k¯>|çÛòª€™%C,qÛ,7
+¢OS–™áI9&
+d0sbˆLd• 9!ÉõYÅ=
+‘šHžÈ·?
+(O¼óÍ\FCh_7ÎRx(P’ÜÃ:ç“ûæ¢ÇåvÇ‹û>9ën@ m
+ýäá6`ˆ¸a¶‡’o|þêOAýL“¾2Ò1úE%6õ³°h ü›OÈ]€Ã
+ÄÇÊœ­G‘+`Zר–!p¨mYÔhŽÑˆ—w³ØŸÝ³Ñ'n[ÝO†Ep„ø¢ì7îS ºF?;oÚÖdv¦ZÅ©#,m⣅…ÂÅÚ‹f§ a¯haz `o¶Æã2ì¡Àt¼›•±;ç_Ìüµ÷ÊíhÅxúŽLcÀ»vP‚FƯÍÇ£rBû4ïù S<Pô›œÇSI’ˆ¹(úù(ÔG™ÌãÞJDõÝ Êr üÜìá pCžëËÏ{o(ÁGA6ìozºË7”ÿ¼Õˆ’òÁ Ðë¯_ež)ƒ‹¤!]½°']2Ižùúç¡HØ1…ºÕ ,KºZÑŽWgâ˜
+q¸:ˆ/€ -Ö)ƒA£%»b™u("˜I££¤*¹%f“é”'ÔHÊàÚÓÅAzF×qJùȈ&3œÖ‘M(¼b1„Yþë8¡™‡Ê²êÜAÖê
+‹op==¶bæð¡’ú¶/¥¤K꣓WßÞnÉ·i¶t«I üLʰסé ž™)d=©Ý=‚Û¢­ä!ð] ÖtÛ{Z³ƒÈ˜ƒ^7DÌů2 Â@ëø ³<Ÿfôç·Æ ö(’!B£7{A«"==G€
+c ;Q¸Öš
+;]i5 u²&ïÎÁ‚^Ï$Ó1©ÇÍ6âæP/[©½w*`43ÿâÛæ< [ÌÃ$qíà
+Îl4 ò);Q}…çU¦òS‰*,&3¢Ø¢äQGŽŠÒ05¸w'Sž”3­TÂtÓTX´þìCuq•.)l›J°’bb‰BѼ0á’¢¸½yÌÓ£f ýfhä$£VrR!ç-âRÄa­ÞÇa¤L6ñ%H&þåUñû(àŽaשmnÎNÿ7…×Òɽ˒{“¯í€øùíŸo,nJ¡QÎ8Ч‹ÿ FáD¾ w2çòë¡*HRS¤Õ¼ÿò­–Ò "õ¼á½ 5OdÜ
+i”‹#9L@P$Ò
+<áÿf&z]Ok!nç=(X9 ßHá+a¯¾Åõ‡ÂÔu
+"ó¶4Úe+#Ô„Ÿ—Ñi! ¡eÌöu"'ß
+½I²0IŠ˜ KàQ"!"µÓ†ãd>‘JbÀærÉ»Cþ¶®Ð +ê1d' ª$IuÛ¶Br’µ`e™JA( ¿1Ƴ>ëÉv8“ì”÷'T*GŸ÷… |¸ »dÿÇ9±ª„èõpÊjø3‚—Xý
+c2.øŽçÀ¬Ø:$b6iŽ¡çêO¿¯Oy®ºsÐ-Á˜&™¥ÿ0»
+œ' ^O _'Ð}Ä*t ˜Ñ%BÔ¯‚NÛ=ï÷%ÐÓÈT‚ÄEûÀØ­¯bÎêT–O ¯¼·Ê’¿ÿ4)uP+Ô„QëºÒÄ$Fèè÷͂Ć¡Äß BAT.“–"¢#© ù£3ð‚0ƒh!¾Ð9žãúúTÝ÷€!ƒ2l4 Ÿ"_·e ímÕX[Ò a²§Ù= ×´XZA¾y€:Wì„jë¹á–æ™íUG/¾ŠH-Ëlò¾aš‰ZäSK Ëh²€)ÊÚrRTp±S+C§YŒÄo|¥n™8¨­#óÉITž"‡~ASyŸä‡á`—e)*ýDøFø4
+ÆÇcaèu7·Ô™%$ÌP
+g|¢®Tv®ÎÈüâ|Ð{
+· ¤B°
+?J2ÖÉøÿÂm.Ï ù‡ÂýÔ˜ÕmJÔ„5¤“7©‚siÌåËÈ3¬“ŠCpa®£3§F—bÛÑêæ‡9M«=¶’ïfŸ«†Ä€O)ÐðeH>ydE¸‡áoÒXÏj3 Ä–Î)p®ÌÚåZŸM£¢Á¶“b f
+ÙR’rBÚɉº³Ëeë°'÷$Ž¹Â[“ºHPÝ!y"ÐyÞfÓ¡ŠdkV
+Èqê1R‹ÐÁcQå
+èp¯/“$7lŠžÀwè#p&¸v–¾Eª²jß›÷ PeIT%N%i«¿8ø2”O¥ž A;S“ƩƸ6§p|\mQ2
+͈A…†“AçWxJhr]Î^C+=«Õ"…RrÕ
+Þœà˜6m…å)Qt”)°†+¡‡œÖ£%Œh…$uhã0©!|%+7E0Hx‰Üe.7„sIФ³ÎdÙƒ<˜íB;u;™ŒÄl>>´CƒpÅ®·qPSºÄ(@;N)U€à,ÇF´'ž:ò¡›&ií<–ä-6>¬óCß ·zúöø@9Ö1•Íš>pH‘fÏlï\ÊøàaGÒìÖS© (§Â?’¹þa'~‡<ÉâÏ·z}_3÷bV;å$àøÊ&eÑ1CŽÔŒ[,-Õ›ŸU!àwè®J·ÎÜ1qŠ‹ôTð
+9$³›8¨+Ða¼ñ:âÀwŠœhj§ž8“¤¹ß¤dÊ÷Æ°Kp&>ß3|'”ê%‹”c5ˆí2;2$YÚt—[x‹e89\Yª ‘ÙDÖa”0³^èaÖûy5µ7 ­ÆÓ ²Ÿøõ0Xp²(Py½^hèÄ&«Ÿçã\ÄOƾEœêš^^¾É…/œø±LfznÔfy¥s˜UΙ¡péÆCBÇ/’tµž Ez`xOƒì‰Py¶Œ46H$Ù³{u¾Á/ÕHË´GE‹ùÑÊ1Aø¡™8Àô¾PΔQ¤a·æÆKc£Y±Iy¿d¥Ôª÷Õ $ô¹©[k2€¯ËlynÒÞ!‚ HÓ”A\9;‰â©ÔçL9Ç¥NL‹µþ!
+íU‹3ψöy=(NÆ Kc7Bº¼ž@Ê©3ˆ‡¤¹¼äÜ7 ŸH…ø$ m¼3ë»<«Öd#Ä̘Ó|äY Ѫ £ƒ²®ƒ&Òpþz;úŠkŒW¹~ãæ[]¯RFw"ÅU¤…ÒQ†Ð÷ -wȈÇ_ xÀ,W;ÏyÞ Øy{=t=z×5î"Äñ¸:„¸øO!f£éuÊhÀâñ5+ʦt Û„9áq1凕&§i{+ôˆq–þE‡b5—KnêDyÅÉõp&Ƶ
+ K Påu¸“˜É×A.5Pb‹hbåM_‚_dœx Dñ1/ήʙéÚD6yùu};HLC×A;qu¸Ð$µ\ÞöfüŸòJìQ¼¼Ž 2šß$ŠS\¤ƒGÙW¿Ðs£PXóÑ>¬£âÁ¨¹Í¸;!EÆü\ý$…F*º>8W5™t‘f5¿Ï5·—£ô;oŽ¥?Õ!x9=kÇbãÈ^u÷ˆ›®Ióó›zRN†Æ²t¿9?p8)׌ȉµƒ„ªß¼m}çÈ0˜-FšØÆVtqŒvb+ N‚ʽšH!½ŽUï#‡
+Ë6|…ï›, Œ'Iž—ÅGD#ë½×–¡[‡$h‘dÌ‘â“wã€ÐB| R)Þë5üÌâýÜé òóÇ??ò×_èUÅ1ò¬î>X^¶Œ"öòõû
+Bt1 ¼<dÊšŨE±8ì0‡@±¼;ÌŒ•u‚#$è¹…AÁ
+Jt¼L§ŒšÒumC®QÏA ƒ¼ÕK}â\d¤þÞ[aA`½Êqq nMÃ\Ö^‡RPwÉ£î
+[WÕvL×%ÄŒrâ­jöu”*N†òaqd;JX’H}M4œŽ‰ƒåL l<nʼn¦‡ÜæOç<ÆŸæ8¢N#ãȘä¼>A('œ%}kžA˜ CýÐ%î[¤È!(Ðä®ãéª3.•Ü ”î>ù=€iÃç(ûƒÁ«Bž½”ϧi< ,.ŽaÜêhñºÇ0 ­_²ÊiŠªJ59°Ðb\(RH+ï¿!]èx•  P JFšy‹ÇxènÄ¥Ä(˜ù” ÆØ
+@`UõìÈ>;¯"ÑŠþ3é,JÿD’ÓÀBem_·©ÒEftía¸ÿ½_Lh
+Á1ùH¶Ú‚0Eä"Èy†à¼‚ ‰!aÿ]W Š…µëJýP ˳:2T‡í>]PfšÜÂlUTÇ¡¥õ)(Š¤r1Óö:% Owøg*
+áQ­ç±!p¥ô²®X…×á¡üSZiPdƒ)qÐÂ>ñ/*¸œê²\ãÜÈb|p˜1W?MRðcàø¤„ £‚8ß®äÄkt"¶ îmáÂJ„Ûa¦\¹¦ˆ‰H[÷`ÂË0bšäu“A°è£äµÝ5G^-17•´Î¯[ƒü_c‹´RbÜÎV/ºšO´Î“Í
+H‰”—KŽ¤·„OÐw¨µ
+š9äð­FÚ,¹·*$åg¿ì­+% ƒôgø†[À¤æ{+6Â=tðrnE÷º±9îFH3à[;«° hëÝ ñÏ7rPEk+uñúDXÃK݃kGDضT)®¦ï”‹Š9êˆËp†qç¨cª4Pæ¤Iy»
+oµÀ—6unÄèöNŸæ­—
+'íô¼Ða4ƒÄ]M§¡EÄq÷p
+ý¡Á…$è‡I[ø;I`¥b˜¹âb²Q¾Ï‚Ó]e¶•êä]Dç!»$IíK5Œ²¬
+ò“Œ‡ù6/Œ19ô>3›dj9Dö™Ëq¿‚¸gòKoPfþ’ÊA)ɾ Ò‹Nóºò¾uiôD­ädo¤°Ž­ìɨÇìâ8K‘•“ÞF±…@àFœ
+ãÇÁ:}µ]Bž}PŸcÄæ¼KFw@H¯ÂVÒ¤)Çyå´í²wªMse0€®¾¬ÃëÏ!ŒŽ7 ‚‚2ì”ñÝ™òÞ‰QØd0˜Ó5×|é©^‹1™4Sõèm§x0„ÙHè;®û¦±2-ÏÔÅŠ¯ ›†£l×Ãc*£Îï‚bêÁ˳†äÀƒè€^¶{}~G¤?––}¹¦»Rƒ`E¸)(ŠKË_AöDi¤)”N§bÄPHcpB˜ö7@X›ÞvÄжíÝvß
+ßêëAþ~¦_¯´‹:+Xgµ7å;ä‘êã¾Üï ¯ÃÁÕ˜ƒ äY©¦%È’0¡^”Ò4Æ
+3ï:CdÚ®+vœ“wºÓ•÷[Ä­ÎR×chøÀH¹ÙËéî»_ó¾ÆELã=rr×Ë\ø£„;Ïi¸>/ùýO™‰Ì ;pÊvâaÕt‰²²úÒ &"}bêc²ÛAsÒ¬³N¼¾ [§”Hʦm¹þ®qÃcÅ¢ÓÚ
+ïki2¶ÅB…ŠÄÌÉ)‡Ù­ƒ¹¼üÇÛn!I*ûÚ+““â3;l$ºÀCRpmëæš2¸Dþ„à%Ü­wÙÓ.r'eMk±ï’KGhÈõ ÊíLxš@<Cv”òn좢2t&Nô°„¥¥•äç^‡¸ƒäÜFRL#Â.ò<—"ÅU¨aZ$Õ¤³X»Úq/̤4…¯u¨e»Ä“òh<™ÿy]…(GhάˆmìþwZúÎ\”(’L®{nûÛÝ}.4ábvÿ
+±øAæ@dÀê¯ío¯Æ¿dŒÝo“¦Ûé µ¸!4D©y8#þMEÙ#õÍiF«*UÚQ¦øC~aî±uB TPqó3 Ò+\‚P v x4A©)R©cÞø r°†Z´Ú"—ƒq¸¨Æ+¼ù²yZ7§1¸
+"J›IiBøgZf':‘“¤˜ÆÖéÊmȺ±×)x:–E/Y&Åo…± a„¹ä—‘Dшkô¬6õ×)•ìf³Í Qlñ!Å0ž€TP5mÈäÒˆ4ªì­ºZ]«ìeX¤à„ØßH¶Ì€c´êbc |ÜUÕN ­é,ö^°6PfS¬ƒ° ™³.‰@»©$g)²Z{Oæ:„4w&%Ÿ¬äìŒJÅ»ùý=Ñž0,_b×ͬ÷œ¨7v¸xÑQhäk]‰T§á¯xòÝb—L'¬¯Âµ»¨‹àê+N—=°”;VÆY{!hî¬d‚ÿT¸ã†”‹#Xo§¯` R¼•~þèÖëÎnßc.êóñŠD}ŽtêÑ6düê£ÈÂô'¯R3@]‹é»¬HÌ´ª¾|ÛÖ¨&–¼d™^‚ ¤ŠHä”<GX C©šù1XOtcå¤.¶LW%=ˆ¬¾&É’µ³!ÈAoÎÞ‰ø‡c3^æiVî€2ÍSÑ8RÄO»7Þ_‰É+9 ‘j
+¹½Ãôá€ÀÐ“Þ +è
+4+uÞ­ƒ’=7 »)`¼0Uñ¿Õ5<BJ„ˆ”®&5¦2°dæl Z™^'ëÌõRC!IÕ|â@|}º€~>€r¤‚ v¨õ Aú‘d KO‡2Ž/Ó¡e „Jè²T:„¹rͱ ˜uÓ $;‹Åh2Äãº@= JæXµ]ÜØ@;1«õíoD¥©ÉhŽU3<¾…¶ŒýYÏÀ™$mæŠë(Ýè6¤£Fμ7b-ÕÈéÜ
+ê#õuO–zðYÖ™·¶SœÂìY`ûn» NñÏØÅÕ TT'y¿GñÅ0Dg~_üÊÚ´WÅ}<€ˆ”²H·7ɑЩ„‘ê~
+@÷‘Fˆ•Â\
+@ÂÍR+ao•‘ÞĪ’Žuožì:«p,öÚmìòÚ'zLϹ“S¡›f€^—¬,s8ª©ðÿ/s$Én$ˆž`îÐ2…4,M£JuNÐ6[œûÏs
+ˆd•Æ.F¾èëÌ– ÍÍ5ÞŠf8%›cÑb ®X±æ<Gª€ÃÀùE™Íý1¦¡Ÿ¶1K3¶7é+W‰Mà }Ó¯}Õ¤L?$6v“Ñ®ž™*r?ÏA Ђ¡ÝWMwMÑŽµ–’d (£“6*,„“WĸIâ,B¤‡Œ¾pûf€»<*ô±-MßC/0U‚÷ožÕÃÖÔU›%“ 1±¶õ‡xB¥báæ×Ü%—1ýRySì!8¨dÛ*‰©LB%ìMcê¡@xH‘Ù\PÒôf覥md$hHCÛÏ4…ÒñR¶Ÿ/Ö$µA©‹/CýÓøð:È ÉuÊQjF Dí63_Òá®ÊÅÈ©Š¥
+x£º~ž¶T¦~=yèžÀ8° ËJrrÏá(…˜M®ðÜpv„ç*ò4…†e^Éé€ê:]Ež¦È¬²hgõ}šƒ1ã¤Òß:§M×þAõŸ…‡6ŠæÙ]ž›]Á¸Çq Æ
+
+°‰üâ#l@ý¬ÓoÑÓa _Ì–š²;…GPí¬3¢Õï§ð¸%§ð|¹[¿…§¼f|j8ØjÄÈJHFÜkØ^‰¢HRØ}*b®¡¾
+û’ fž=çÊ„èŠÜ5 5ׯ¯ûÍí b™ G[ä‡Ú1, ¤ç—ᇠíµmߢ¿fQ9”€Ö¼ñLA—Ø9˜·÷¦¨BÙŽÎW­ÐÁ¦t¬ÃaŠ¾‹z†¡+ËÌV¿)ê0ÛÌ°…éÍuíÖkP,kæ}Óßßi“:úÇhê¿ÿû¯?þ·ÆÞ`÷Îï2àZf+¿
+5Á@bÇßœS ‹‘jm<d .¡ÀµJìÉõÃa0]ˆw¯’¥@Ëx uXsÓEV©W6ê*š”Ân
+DG\yVásôÑ`°—ÐTÌ4/ãBY‚É„#¾9í ²&<TÛMØýˆ,ô°¬
+yŒ YË“hb—7JáaàŠ¡1É cM«‰$F†E¯‹xŽŸ¯s@=“ð…o\%•GEØŸ®äÍaÝ[}>xèÉØ›ôÁ0BÍÁ¶XŽ"™Í" Ù&še`°˜µ«;}0zÌä(e^RÎ_¥P«„Ä€”WàŒïøs]Õd¬1`Ú©Ë0 •ë§- HsíÓo»WA$LSæêÌwñD^&jÞç¨;‘qA¼cu§à”–jßL†CôѶ_Þçt»G°¥5`š¦­åbˆ ˆëØè‚+®ùØúh[§`s{uJöþ±£ x—Ñ‹{NžÂâã˜òVxØU„‹i–˜ÈŸáKö·¨o3®y_E¿YV†j» R½à2G´
+†5ssoĉèÐYZ-ŸN£<U³õ­úwØ›Éײ]ÿ¶çAÛ.ôñ„1/J
+½Ì?çQ=í 4rDaÊsÊéÛ'M)ž€PÛ›sN²åkH 8s€ŠKÚ „k0(i©äÏà e:ŸeÜÆ Š)y3Ï$šT«ÛØõ@GEO‡rân-œõóZ%Uc¦/ˆŸ…º´’"ýµŠ
+¶”š>ðùt?ôå¾î¢Û\룅Ü ¯h"ÌJ ƒŸ¤u7ñ.Ê,
+õ“Ášçñ±ˆÝôÌžF
+3(¥è¾i¤ˆè•&~àç: RÅÀcáä •€Âž™ÌÚÒvVÓî€h0j^N‘3ŠÆ:±È
+©Ço›ñ÷wÖgmYÕ[
+Jq“X8KdÏaIyŒì”¬«€ŽÉ¶wçÈfÁó%äiyî¯iØ-HEâ7ÝT{á‡Õ)üÑíMC- Bbm4·ÁWÉ9¨uÕ]©•¡$H
+ûá| %üþÆAÂÞ£()ô@_ïÚý;šC‘šÃ{›~8-V ÊZ†(£»£:KÖUÇȽsàx_sàÏÕgßÍñæhñ—;õç\=&Qu \ß]_«‡&'A‡½Ö7„pݙùÎa/¿I¬•8k~Å•ó5º#œþzSDLý6M†*Ä»{³ÇR
+IŽË‹+r÷˱¼;èœsÎáúš{šÇ«ÖU'*”ŒhpØÀ9¡…k©†i`¡g·äpš?¿ƒcA>LÈãUàÕ*æD€ó~¹E2J>f×2U‚¡ffü «º‰IïµCj³¤(]°š}g&¹Du
+”IW' äLÝ_/*³)ðw~pI8Œ©®%2 s?(
+L(½ø©$ÙœXqÚ£Cyâ=äòBÍúC½•ñÆéJÿ4èIÍ>^€š]›”Ž ¢Î§E¦#èH<®Qø Tçd’{#»pˆt%È?Ü] £±G&mÛ Fª‰| ç¶uy¿Í…„°Ùðžµôó^è\‚£®
+^<EòÆ3}bª_d#|Jšu"Ek¾Œî&³8ê»ínFQb‡ÝÃ:ÅW«hÏÍï¸få¨ÊU_G¡ ŽçBÌ·Ù"àTµAb¸CÖ(E€pöœ‰
+K^ã/w
+q(XørH¡1 m1r• @óÊ8 ‹ÍÆ“«“´¨£ÀËÔ‹G
+öLD§7õp™»Þvý}—6È9BÝn©
+ªù´}l„ýB
+ë,æh©Œ:4±,oNä]ò P™Aêè1×3ZèwÕ:1DÊÒaZ\üSÏ ˜Z
+^‡)qˆ³‚+^LY¿Ï ™'Fèb›—F¬]b˜‹Ø¤£ÎÅ̶
+5òÑKŠÂp«$?T“Vë'¯sê–ɾL«0ÒÅ•€õ¤DT²Ö(¡*!QcûJ`°Î.\ZÌ(åÚ _'¡•uŒX•©BØ®ƒxóC„Hˆª$æ6€”‰´Dq]A•°Óó©|¤'&®\åJ™;š‡ïÂ8ð (g×óœÁºƒpI4¬m˜ä™™MÐ?‚Ún›Š‘`8\–‹,ñɵÉÑ
+“´#±ýÜA·þøKËRÄ‚X4§@º§7
+šY›¼ÎxÈÙнÕÂVälQp(kyüˆ:Ødð$‰r@Ñç>R%ÿ"ˆô83vá˜/BŽœ!ÒO¦ˆ 7 qVâÝ«dËËȧ­ä m|?ŸšŠBE–U/oÊNh±£ó!
+3vS¥ô086‰&¡Ç+J \ ½„¾(ýÙÑÜ0ÅK5 ºR¬zŠ’Âá
+*S‚8`«¦aÏ øC
+µEN¯ˆ‰qg<'#ý‚˜¯Í]‘‹F>HἨ^®WKÙ˜P—–LBHˆôª˜dŒà€‰Uãt{(Bás2a3Šc Tkgd¯âÖüQIM0űçIò•¼FªÛ§¨Ô\ŠÕ°Ào1Ðxü®XGR…@œê$?õ£Á…œÉ6‘< ¶ÙàˆNS[5×ÐÝhÙ¼SõO%¤uéžü°‚É•ŠÈ¬Ão5ìtV³•Ò‚E@`ÜIñô¤°we)»À;è9Ei7c*ñv6S#»ÔÁ‘Ž­ªHômPS]ÿ®;²o [q®)»ï…
+›:Æ,bA”2ðRŸ;€Ø§°[å.RJŠóÛSÄÿeÄûJüò×[zÿãï·/ÿ¾å÷?ßHÁ¤RÙT"^W„Áã—Cà$³¥rùòO As†èpaÍ͉­KÝxÉ}p¿ãOì·ù"è7É•“ª‡}?_**xO‡xîY †»­CÎÀ͗†C¤¡¼\¥¢¼É†åè.­Ü“9¢Ù¸
+üÂ#˜*·€¾½e¸ŒPwj'¬•’SjÞüsH ¤E.¸¥÷Éu…†—n„CŽ«L»i[Â…¿÷~*âP 2óh÷Ãì3£ëlír]z¬¤`hÄÎ:*¨„À5Ùæ‹ÇúÅ£‹*éðC‘–›Ÿ'uG²füàëÔæ˜È^ 7F‚æ¬^øÄÎZ«´  cÊf.éšÊŸ4KçgŸ9)ÇåáÀÖàÊÂøžuèüB“ž_„^`à()–A¸üI R-(s~D Ål®ÎËß17.fÙ<£= x¹ž•XBœP-ØvòC“ r¶«_Zƒ¡%ô¦á“ñÌi‚/ôÁ§Èi3H!«Ó~jŸã—Šá‡‰|¥4ú?ÆË$I®ä¢'è;ðe1kiÉ[ô¶úþ[=)òg¤±%S“ªFEü
+n†S—p—Ï'3\ÞézEtض¥””^q”‡¥§^‚lÔ>ò+œUÒžÛôåVóˆ?¯Mþ—E$Lv”DÄc ·"ðMo0T{8‘Wñ%üç('üšð½ài9g©,û}Ä ÚŠêǶ÷Ÿôpí£äÄ¥šŽ ìKWÄi;5„1Tkkãb*øÜCnõRâÈ`ÈäT3(íÃ9ØN¼ê”‘‰¯ࢫA
+*Ñ!) mZñÁd[Á ÙqÞ
+6W6;6}a°‡è¹8©äN¾ñ˜Ä*gYùœ±®à
+Ò^)CPz%?пb#„/–ä%}¿éˆJ0‡8z~P=ß¾é‘á‘ír4õxˇ± J8F±Î׬%»N›•Ô!ý¡ëë”HµY Ù ×£v’‚&
+˜Ž‹Á'œì¬“ &RˆRféí:ÔÅ!%nzVH± èL£±3Æ:즒ªöªûæ=Š¾?1L¹4-Iögn
+S¹–4‹E¨ÇGÇMïE­&Lc(ËŽ‘ŽMðÞúñ[,`¨³|›?{7¬CQ’ óýVð ëæ¸Ë‚ªX„ÕežpÆË•}NOM}8[&>‚ƒÍ\]Jx+l8»™¯Ç ‡@•‹f9ñ1T™ÖµEk«•áýؾ,ƒ“¸ÿh hšš=zÊ&m2‰ŸÃ<‰ºè`Ê)zS Gô§¬v-À³HKÅ;Àf4eIfíq
+5_Š¾½È¶0¹ÞúÅAòÙ_¥|‰ÙÃ<—zì!»Ï^BÌ^²•Ö4 ~M“§Ã½`ëº~/Þ­hĶ2ŸíKg;–² ØÅ߆ùf¯aÙ
+x5Vß aS6šUTéÂ’[[a·±ñ˜k ‡‹‹Ïy/úMZ¿ÿþþ|ÙS4QTPQñeÙÿ±¢ŒÄrñ£ž¯Œð?ÆËIÎä†Â'˜;У#÷Å”OwNÀ×4u}@–Ȫ,5C£è&:3àá-é´|È£ÝÃÕd: ­õGY»Lô˜3wšeWœæ°RӜ֪æ#-
+¸XÝtóáÖEX¯`jowRU Áb7H²÷w›¶ø‚ØA)77l°“Ög¼f|\9$’Õœæ c½‚1ð@qÕ@|˜ÈI‚…ôrœÒ>[©çoéïf)…Ÿ²¢Ô¼Ô¨‚U-4Y‚<0PPÒdšÙ*¯ €w62všŒ)‰vU™g5tL¤±ÓT/êDº‰üÀÆÑ%üN &ƒ©ùƒ“V¾‰$˜óÏ¥‰½™”ocך*ÚZ,
+OÞô;{É‹uf7gìÄÌ ÌEx‰ƒx¸ˆPQý¸ Q <pøæ0bÐÎõuÏ›@ al¦&Q#å=•D§ÍõáJm×s¦®¢7ôúæð`ì*öy+!
+ÆZ“ì‹\>ÖÀ+>jK ª‹H`sŸÐÙèHsû¬”Î0÷—’+==Õ|¿×TeK
+¦¥Û[…²Ã>݉2k_aX$tbg²ƒZYË°P™rYØûðù¸åÍ7'‘Ž#k#£úHl…Þ ©tE¿©öÕ+;’6-ç[¸“nL*ôfnáÖ¯bpÑ8º±Û›sŠd˜MDVŽ7‚ 5¼ÙùJ—…ùÎW1¶‰ù «i³SÎoçÓ\à@½Kl‹˜.kßw‹ô¢¬Ë.oJŠ‘
+ä™·¹'AX¬.¸¥¼.EŸoŠpÄ
+wÎQÀµNÎÄ÷q
+šm´N˽×ýt٢ɉz^£äÃôÙm%É6<Áà 6R ÿN’â½~ Gš€øï19‹Ïàžä;çBøÊ2y²˜t¡‰Kzc%"å¥H°ƒ$ÄÌ('<Ç9I<¡Ò\š ó
+x-"Z)| ½À.cÞIbËÐ>ô^Èš2s %P8§¶Ð…¤¸¨5ÄÂY¸ý¦¬Ÿ ¾r¦ÿóWKãcU2/²S.Ùîç­‚Áí}uÙ¿VÍWä‡S4«?•ã¦•ì xòlõ’3pÍD 7ÜÝ õÀNb?E¤¬Àú¦Á“ýÀa>„Šsa!¹µj%x¶gQj»”øUZŒ®}÷7çL@Ü•G*o^ÃþcƒªÐ›ß~œ„ÚNè*åks𠤾‹¥ûÚâ[ÉosÒU·"YR¸¸hÑ¿Ý_CzÀ«€öj¯yý* (;l’3Ê­;À FšâDDûÛ­Ç”l}ÖÊêàuVO%~•8S·•ðA—s
+<+ùfâyV¬/…ÈKâ_°S^‚c8ý䲆‘ LaÖ/B¹Q­‰HœÅ®i¨o )Q‰t¿ÈçëAH
+M¬^ ô±Uì]”?ÌdÐlg‡$–¦,ïOÕ<£Òµv˜ a}Åf‚¾6ìS¥vÛÛõ)ÖÓJ\$?öºµ {ëÂåfç0ÚÚN´7öÅ„S).˜Ò¢[xy(Ï›s&jšOγÛ(ð0I©—ëiËÅ­Ø97¤p¢xÛ×õ)ä¥.òãív Ù4Œ
+i묡á%1çn}`<˜Ä†–ô¼!ÌjÌ•nmÙ癃DJ[¨J\Ô9A˜4Ô‹K# ÏP ™7P/i… X ýT·¦I­.Œ`ƒkeCä®t¯up*¤½«„úi÷9A_Üõù½_±ÐUƒ2gK;ëÔçU,õAøPÃ=;^VÞ”¹OBå
+ƒI»4‡l Âàt¬Xä ߊ¥‚àõ(F ]/C)ù;”!Rº,Ò
+]#ÂÇ"ËDIŒ½K2ˆ¾¤õ­(GtÜx¸çôE±Ž¸ä–.ûü;Y ‚äàI­¹Ø!ÃH6ñ‡Åo|.ÎlúµÔMt±ƒÀo Vý¸2SPXç%=К"cL®ûòÈ®ñks^Û€ˆ Œ ½JÅm&Ó5!8‰P)fé•<îjóRÖ|ó˜…Ñ% Ä€¥öÂë
+:]™²>õ-ß-›
+š#Ü”¡‹¦òLhEGЛi„?1J‹dÒ
+ï„pƒ ²Ì8yDr oŒÍˆ5 ƒÆ·cìvså›™ùGm>5|ÕS€wZ’#«‚P,ïàF®< „ÓfE߀¸çtNe™52uºn“q$üV‘Ö£3¯âj±Õ{}ÎçI²ëʤgJúh0 G‹?Z¶>GæÆI;×ø ¤xN*)¥:‰Ùæ@n'üëײ ž! 8Ò*
+èÍ ù^;ŽeaMÙšv"[r7ŠEŽ¥PÈD¶/©>œ‹&Í@$*\bGšbi$FRŒcT† }ë­^‹@a1Î{È^ ϺJäa[­šÖòÈM¢–¦h_úïáú4œ6°nÒS€wÆã^ŒHSÉi žk<
+Æü>×*Ñ÷DÎJ«®íãyƒöÇ“ožt"ÒÛ‡çLúJ†Ð²I¡€éæq}d5¸L×v(kj+Ëo~µäØNVjR&Úå\ÛÉKùVDg5rýâ|ºã;g sŽ6¶s´òt“‡ëœ´:\ǃ®ó-‡?]DúYôØ.òð]Çü¸%Ã]*²¢²¸®Cym„<mŽzã:”„è*ožéê؋뤒—¨aľ½Ä/ÈʴЮÔvã:™~°”¨Iž5 ‚bt„Pëcu]çd³ã:È3¦)aoü‘½4ýäJ~ ­ª5ðoé· >SJ¼ >Îס
+I\åc Sp´¾ô\ ¬ê+#Ù^@‚þ
+é|ƒjW}' &CDÒ MP’ÅÙ¹~m/Íã¶ó2í/ ç (ì3G+Wlª4¯±¶Äk'/Aý›4G¥•’~Ö˜p§¨Ë/­Àw
+T ¹èW¼€nb+S&#ÉÊÜô=Ð' <,oÞ·¡àx‰ö¡¹ ˆº”Ÿ¯VãÈ”
+߯+…¼™µŒŸ¹Á®rP¡­øþ:‡g#9ÝLÍ]_€°Ð%áh‡Ñb ß“V _¦¯ ÜîÅ¥“q˜œ ÆŽ9xgß÷á.ÌH@ô ¡û¢ÙÀZYˆ¡×áK¨W4Güå=òo»Î úb°ÏŸ¸ðgJ•³3!Rlg¯üsb2PÚÈâö¶ <†Êò¾`!t,‘Q)oAx-²­¯DrÔ$•å­sYëižAòJŒ
+ÞWò:¯&p‘ìÃå}ؽO¤Ý.²òaeõ9Çâ@ô%Î¥e“}É ÂˆÖÒ­+<aÄ:€n~„ð X©9Ñò×?èëÊð¼Á°›ôNâÃ~í[Aé¡‹äYãGRYñS±¬êö¼<w“0抖P°íåF y3QÃÔ›]aE_Æ:†Áj9³ÚÆâù„Ú cc×"F]šqX‰zE”QPõ›s^þz™WÖ8/:¹wTædðQ_òw›~ÿdZÞ¯$<Ò£ Nh,“ÚEñÏ 5à⊬xÀ5çJ”Dšöa’²¼.=™ 20c¤g ÕC»yÕ¯Ñ5ÄÃÌ‚àI¢%Ÿ¬@H/o”…ééÉõ¯í‡@0^Ó¹ÎÙ=¦(áèì[!^—áõ´^›$I_ÐÊÄ{TeãúdÀ¹å¢>HÖäi&-~Æ+ŠƒsñΩ¼Ûj╸‘[b•ìOÁó:4²Éð†qöX&pB^Úþô1“A%Óƒ¾?”‡vUîRt·:t:°¼R<Q̉ö»À’$´‰ÿì^Öµ E•á[f»aíE¬jõ·šS&­N” #fËlÙ=fˆ¸c¦¿ÃÛæÄPÈËŒË4mW홨0#½û½†A6h${²Õ‡¨‡âvEª}ª€Hãüu'FjŒéRŸU±7o¤Pdm€´&ÙRóÝhjˆÙÀÚ%+/eH–ª„GÒâÕ÷*ë€â.µWì×&1<øE‚-¼Šÿô%8¾ß|î ¢ ö0ZÂ<o@ˆ%Ë!ƒ¢¶7e}™²»Y;Sòñ±ô AˆÙ*•Ëì †ú‹T›@„7Y|ždèÔ¯dÄ–ÇoÅVAôKk{ }$ãÎ]éwŒY? phwr ûSìHxEëHX¾9‡D2¸ ýk$)—55¶¾y}àQ 2|ñ[NÌÂtªÝ†w!"Å ¸¦Ïý,•ÛŠ¦ƒÇtQ?¡Grv3¥Ð Ý]N9¼Q.#<¦4ÁeØmI]4[ëÂh ƒÊ÷/åe’ÝFÑð:æaM/} muÿµ$P|fUÒ¢{×t¨
+¬KÙXÖÕp×"#"$c Ù8Ïjãm È¡{p§q<ê,¢b ,ÐÒhàd7–§GA|=ŠšµhÇêc¿Ûª¡L¨~mœ„&Y?©ð­‘,8úÙÑc#S’‘ö†:«”¦à¢°ÆŒ£8ç(yøßÙ¼[ÒµB0áޥ幹F‡ÓfŠ³¤ËFñÞÆ4§Š7éëܘºÖX&¹ÛhÓ¥¬^­èx7¬•£kˆL_±Àê~‡w@›‘'‚ŠÑ«œŒ‰ªŸ%u
+QùéÀk\®ŽPìî7OtÙJuÂB~iבÇÛ"ŽËôCfðû…ˆe/€3 ±Y,BØ%=À¯iîå"PNjø±$vLÒè‚õtlŸ“1$çS¶Ê;ö)òNÇ’÷ù"˜a ñ¦¸Å$d¶& ‹³#ù²²Ð½»Ú×v¿ƒ&)ØG…m@
+æ""Ù ³“ŒG—‘T‚q0r“Gˆ®äLˆŽÈ)v‘/#š,×P_ìL®èÒ€Þqoˆ®„ø³sÈ`âǯ!ùÔ¶2»Õ£·ú`2m’)¢ƒéƒ Í+l$*bŒ¼/ýZµRм%ñLÅ£®CIì$Dü²“€±¨73©vÏdQdiŸi XÄ;C:ÚC¨B=1öéMIèìÀ‹zóöÓûí*É”+4¡‡­H#ÉhŒÌ¨ºxâ$ú6­U´Û)ÂK"ÄkŽ-ÁÀˆ}4JÒŠXN¹…1Èû˜ð4ê—%ŠŽgº¬^—¦’Ê4Ô!1ÛW Ò<3hæR
+H‰”—Ard¹ DO ;è® AÖ=˾…"¼ÒÜë’¿mÕgYšU·ª²HH$ÝÆcD©£ÕZ{×÷µ.%F“ÒjØûç[?‚Ê0iÑ­Õ÷„xw-ÃL,!õáÞ9Bª¹Æ„„Y-üjŒ¡ b1j¢!Öß?¸Ê%*?Ñ<)Hk35N*!ï mEj©šNH3 é¥ÞbBºò­ZïÅꂈj+y›ï«L‡òNm®ª=4Lt´¡ów.ÑZUdŸSBK÷ˆ:Ê
+(†:Üð}Óÿåp’½l²_8JRÇ’(NS4‰ŸXßt¿ƒèPGŽRqfLômcZ8×î$BZ¢JjÈ
+›ŠçÛé8ÓÉ>v$9´+îQM0újxÔ“
+Yô•iE
+QfC=D¤óvO&öY/x=KÐý
+ºÌÃÅc)Ô@Œ óiª+²º@É”tı½ñ]”“¼ÆjCž
+¨ÁÀ.y à°Æ”ÌU@†mÝyd„nhÓ% 1=ÇzË*DcšNo…™ù˜7õN/ðQ‘¸”¼f=è—ªëz•ZY¾f8n©Þ˜bâM‘yθ¶t…i¥Ÿi6Gy2QHwAŸ ß¯@Œž©©¦“­%íLäÈ…E;f†ˆ $8MÏ–œX9*y»ís°Š¼¡¢Û^'„ÿS#¼‰ÿIÏÿÚ¤y%…˜X\»LdšGMÉ%¬yNvÖ‡zÍú¾æ¿þètIªãTÒ¦ÊIõ=W)XÍN’–±$z_’F†®^‚ŨK!—¢ñ¬ìqø=»UqÈؘâ$üÖûHú)Åð$¡€heó=|s8ŒÁ¬-îGH{0ß‹e‹0«’GP¥°ìûé6˜3âB²>Û¯²vÆÑ ‹Ð„5a}½üIWc<QúY³
+Uè½9*Ù©ð7E„c-k^S®^ÃN…­EqìR篂Ȉ*Ò+,\ý… ²NtrÁQ½0”‡³–ÌìaÃâ2b\­…g³|3~AHCZZXº›Yi¬5HÞ\˜›2ü3 Y ={M·7IqÀÐ $¦g×®‚3S ykÙ.“}“æ)ôBó%–ˆÉè…EÛ¾Î1”;efs‚ÙÖÓ ÒC¾ý!¢KçÏX¤ ŸÝj#˜vv™´xžÉŽKïÉ ·Šª`WÑ«\Ú‹s 'J#(LÞѤÐÌš-ã™ô"%ô“¡]Wåì´3Ü Ì˜Á)ÌÙ1Ô“»z¾¯bqÔ'íÙ‘€OnÓ‚•ºIÕ1H™N™êÎIÈ+vŒ9ÜöW„®O±ô±¤p¢n9¼–î[MÉŹ>P¦h.Þëâß”ñõ,éx±S@ J¥š4d¯»AO×=ƒ>Óï:F*“ ?vŠ*Cg3âïÍùP_„~Ý}äáº'ÐçObZ¡7V„Moò"ò¦¦áQÂŒ·éÀ$Á°áŒöÖÿèöºÃmßc !
+F!¢ôèçÆà&è P}õåj>«9w[Ž‚Úc}–!/¾Ã­ð)ÿN6†õR<ûÑĺ¨l†°Ä Ód[Eæ|ƒh:c¤ ÊsècÃ6ánò›“½Ñ¸´ªÕ&ÄZUpÓ¼<#>&?Ø[™Rr(ÉZÑBwã6$[0çuš
+þE4…œB8bG–QêÍoØA7óÑÒ˜0€ô„qÁ<„NX®™å;èÐY0kçù”ä€nÍ~ºî 'ýùüF-%c£•Áädý…”esAâqü/Dø:ÍÛu§ùñmL3ôî|ntFf^¨0 ôPƒáþbôÝA‡Ðï×Bÿ>¦ºÑºxR![—7B…1€¸!¸>#7<.æ#; Å¡0·!pHeЪ’yÍõ¯6‚Ø?Rˆ³ËNNƒvâ#– úƒú5doç°“àFæ.³ßUÒ¶æÈ¥O;Opšx$áè¡õ(¸Ÿ”¯(¸Ì›z’[€»ŽµŽòIi9X$)ö„ ©ùµiC°Q2!
+ÅX)*{T,ÇŒîåð BŒã¯·èÞ“ùÎYcuê¯fë”#0EV±Ï³òwJ†Å÷åd3‡XÔÂÇ/‹nZƒÇ¢ÿ¶ 0¿áë©s“9}Y‹8ªùñËít £—0bÛÓ™ˆ8:ÂÉ“‚2}tA¬·•Å×"ŵÇÏÞA‡ô öÊpŒ4dÖ
+y
+ÈËØwÕþÂÇA·œ®û¨¤ÝªpªÅêGKIþE•Ñ„°Ð,5cšÔŠ‘‰ ¡Iæøâu­‡l­[‹é–Žv ‹ZdÃ|B
+‚d1ˆ}Ž&•¸[•™žKoá*—‹ûê­@¤µÑlŒV_x´l)ŒÃ@ËühõzºJÉu2—È òÅ£AÚ‚/czœƒG[ÊP e'Ú4`ÏFåóípY‚x§3(˜M6ãq‘4Á ª"R2u¹ZFLÈ4~c”êã
+‡$@î«#¤HŽ—B¹Y‡D‚ü2‹†l[1¨ÎÀ².áC¾Äû·_Èá ÷RYI[èÅÍ8å‚.}ám÷Óöß6ôêû<–i"3µõéž.èzªÌ;“ü#èéþè¹Rîv?
+##á”qv¯ïVš¨¯Vñû]CNGûgŸNßóYTg [Ñ/ »æ9n9Š’zñ_“ )ƒ¼ŠPµ»ux=ÎÚÈ1y¯[’ÝÈ°#û}/ w" aÈÅoûëV—¶WµpfœÎÈäõôº+ CĘžý¶Wqmf5BݸY†„Ae&< ÃëzòÜõÈa ×âOŠò±t# .èÒÞv?
+š™[É–@¸ïštyÞ¶FÀÞHw‘ÁP²ð„Õ¸¿´ ’QAeq¦ÖMkò kpA‚õ¨Tá3s±š888 C+ßZ#¼ÊoP{_ÎçÄõ‰ÇÕh§öš‰Å‘
+{Ïõû'£æ
+Ö\ämì€#©×å>n0 v’@êšÛ:U–Ì””Áv—ˆ^`„ê6Íi‰ñ#–m˜•é<™l;5bTi#°T¶'#«Å1H³D©ý,˜™eÐ|['LÞgMc¡ß<üˆQŒ¢(lÞýu`3}Åy·£‰¨¡8ƒXœdH;hP=Ó†ÔY×"3ÃÜŠÑ_#>ŽI“…ºÉ&êHN<¸ˆALQW”Cì$­«¯#GPÎ_±²0E’¸í©rÊ xܸÚøŠyzù÷¬…$µì{/b´ hh3­Ìƒ´[—£·}ó†™SŽù€`ÕԗÊL%4dòdÞ2¸uT…ÆØLšp§¬Ûü˜ÈøŠš‹+–O“eò æ túëÀ.Ù?¾§^¶Ó´:ùgäP7K™‡Híq„[‘ð$Š˜K þu`iƒJ /*èÔ¼×ao XÇhÖ‚^¯.©ÖÏm….Gx+wM¶ÓßÛóá’‰Tã$ë©ïå?n@˜VSäQ5¤§©¾6²”טH2]Ñ¥Î!ÊŒ ˜„;98k¢ŠsUHÇÈC¬8ý©Ð!òEÁ2ÍÚªˆ€ ÷•Æ‚zø‹¤¦\ˆ
+ oÁmô
+…¾Äâ1žKAxB+5 Åe¼¦>9uNšDë
+û°Žz]ª—üSÐ5ì   _ЗsÞ€8ÒÄj™×˜–Ž<6Ý…iî4Ý]Ó¡ˆ¬"9*f^³¸>ÆÕtô¨½jΑOoQ£ª£Ø¯´ÓP n?7„ÌK(h>c"Q!)>oŠAUf²þÐTc2–•/‰¥i¼x|ÇszEo "ÍzÁÕ›òɤ)l§¿÷ÖÓih‡„‰,bgâ§ð°«Ûtš¾B¥qC„Ä ÒW`â8ã L ”]Ónîq!dÝx§ Òl©jÆ Ö°}9u
+[ø0uŸ£ôQ®›"ÑA;lšNDâ—y—Bɬñ ’
+N×PÉV¢ï(óðuÀÊ’ØÊ¡Þ˜‘ÁT·t>Þ¨2E´Õ2'} ¢¡Åë< 2«Ä_ãí\¨âHÙBrf<•§òŠR-9á4ÎI@'=_
+B4üŠ; z@SèÔŒâB°ÛV Y‹æNW’ 4t °ÖQ~â{“1“f<Ô
+v«Žµ£Ñ«6H–+•€å½Äã¿+„Gˆý©Mü!'&CÏV¨!û3òå^·¸¡±¢þ^½¼,ÞÀGâª~—»ÅãÒ»ö¹úë` D-بˆ‘‚–Ç®¢&¡¹ µé¾b‰Q.üï³­H¯¸'«šÉPeÎO ©¤»«ÆÀ(TXfºƒ ^¦ËþJ‘ˆª˜g«TÄ”â„Qü¸T2“+ˆNÃáÎìû<¸Ô@$Rꦵ:¯ÄŒ7.+z"
+ÚJC‡cŠSaDC0|û僾Øëû÷ü'ž"ÂQN¼éÙÓ™5"Œ\!þSnãêA‚‰‰Ê¨G
+·'yl²2ï*¤Ð¸ª‘=GV:Rû-rÈ«ò CHcÊÚ¢Š’RÝëd%´ªÎæì‚xV>äI¨ÁŸÂOŒÅ@‰8ÓªcVõé¯BOÅè€þ½E½ Bâý¾A#î’ðf‡ÎÜ‹\GÇšˆ83£x~TLóMWhqAxSØ¢,ù¨Ïgºþ^[UfV¼-µÇت&–§MkZ…ó£Oc¸oõí›ÿIr@É‘]É™qͦUFž’ÜÔ£7£ã Ú”ÇcÀ£>RÒº¤SfI\ Lë‚ “ ƒ[­ÛsW”U€DÑɺB¨ÛÐôÔNd Hdï
+ý+Æ^L½ifä ùæí—‹ ,æxâî÷ äBBKðZé„JY[®¼/ú˜6•ƒb ŸÓ8¢úEÄvK<“‘‡7ƒÌˆLÖÝ|Uz€ïÎ/ºë½ùE¿aÉšªÃ‚z·þÄƸ~Èv+âX"׊€Œ‚E²–éüÃ@"\Fi›C× £iˆóœ##2Ü=òÃU@M¯b‘N\­ÑèVòç†D=
+´Áã6M!àê'[¾¯:D¯ ¸ñåêók‘BhÎBX›ˆKPM$ͬ#ê“:K±ý© Dk-–´3:H¢žåL~½©!þc¼Ìq#9‚(zÞ¶ "÷Å(“®N0Ç1u½ŸI «³Db AͨÜâÇ_øïpd
+4Ÿ'"Ùã‹h ß0XªmêWõ3‰IPŽ<ßç*J8Ô^a+°êÌ…¹âJ ž]«‹„àƒÑÏü÷-Šq™(|N@¼%?¦6cë0홿ßÇ"”Ô
+Ÿ7E¤t½¥¥K
+†üR>8xcÒšýwï9Õ´yT›qÈËÊpÎM¨õØ·Û
+ÑbZ!²ùåf®ëº:ñ²~š€¡Æò†ã)ïŽòOü·ß
+™âfÇAŠ§žbåñužÉ@ƒw¢Pjk.F?àC.?Â-²fé¦K •í¯®Ýe0’ÅN (=&ç¹x}„•&T‚P)_ Ç&¨ãÔŒ½#¦KÑÞœs¢¼JƒƒçXï·‡U¨ò¸…UÀ ±*P\±5Yg8J*°âø©Ý*ñ1¥1zƒI„0€ðÅ…vQB
+i ¸16³ŠÌЧj³ ‹AùIqo?ÖœYN8È=yp
+£á-¸`Ü]?D¥(…¼­0™ñ
+v¨m“§R_èÑqÎÜ&ñ“†ó9>
+²X¬fœxXAr`l3ÌŠâ–z(úô"Þ»Ná7J¨¡1ÆÒn¥KæäAÛΠu¹0´ÛJäâlùÁc*¨’Y ËuûÚÏS!ÊiÃÇÆ$äÊÓ¦®1] ö(¾ 
+ “ÕÞݼ”ØÐñ=LɧoÖÁþàý&Ìæëˆ
+}ù’nDÂÜ…°û%-wHN‘]‰_×–!C±rn ŸŒ(+L²ëÏ·Võ}9Zhc,i :Xèß«ˆìÓ¤^<ŽK«®Íä¢òÁ41Ó=Ì „úô•°AQ÷uY%Ì)4F‘Ž~A¼ 4Á³¶ßÁÔa°Éñ—a†ä:{G§|£ŠoŠœ…™)VÔÅïa7ˆxNhGˆ/¬?]BãÇ: γëîÒ”]äó¦()
+ˆ‘ÄÖISc]§Áà‘c6¯©© iB©Þ/ÉSá¡à—b­ÂŠQ+ .V‚ÐQ€ÂÚNx(ô-Åk‘ÑÞÙJ^—ïè3TE7E"p
+KOE®YEE¯€zo~õ®45 8ÐÕ‹$£+Ù‚QࢋW†³”UÁ÷Q)–GÜjeºr^¯£[ªT'Ê,Úó
+•ôÇ_×2xq€Å¢e å©Mÿßk¡"8*ÛOÉ{rüп$Ï]Ÿ ™Ÿ@/‡MõPd ˜uŽy@¼Jx ‡)
+ÓKPØÐI ŒnY·Çª_º1KIÎì
+èÀg¥v¦Õ>} 6ŠØ+˃’†(xœÿX`
+Û¤™_Œ`3Å–˜ñÿ*a\>Jf]î,û -ƒ{nˆ½Å ªT¹õˆøÕјFûX%jr?Ó?TÍ„ó˜ýHÁï§è2 B\„ÿÞÈÛµzÐÀ(P´¥·wRÿ`»åpØbã×,‡á‰XEJ/ƒ .Ü
+Ô‚}/¨HÌ;tOA×;)à‘ RÐÜ~¨à[¸-ãߎÒÙÉPN"š€¨PCë)œÃ~Hª5£(Å’³«ËÖR-IÄ9úVý$ôù§;ý,RÉ´H*Qß=z‘Ó Ëpaî“~áà¿FÕJ EA¼¬Ù…Š"†'ñØaT½¼:Wƻऽµ½v½âV²2q$²º÷ˆ¥¥gjRÅÒpY±+¸.Û$ãÛIjr(..Ç(#‚Âò+Õ2ꄦÇ)ZÕÕ×J6*”–•o}òä µ·ï`‹C‰
+ ,q}ˆ†Œ‚ŸH‘\´3â…%{‘É›ì¢}fÈC–R1é2ÿòŸ•2SA«ã6Cç;L9Ï嬋—IGÿ¡ðW fö#és›`¸*FÛQû@NϺ-’Ê ØD|z¼Ò=êõEc1yóò¨J8 #jýž£~FI5‹4A%`YÌwÄjDçE‘í%÷R­S¡ù¨îCuÈuÏ£¿3PDïÿ
+ ^Â:2!x¼„ŠUÎÈzesè,ñ׸ežÊ¢›FJkÈøvÒQ¯†€ÄŠP/ïh‡0wɪÑ.®tÀ-ÉWÍÙ>;†²„´ïT:0÷°Ã ³®<NiŒÀ0×¥:¥))£>Hò„TYÒ~¶¼
+P\ò—AS4Ù©s•oPæLòEÊÖXx  ŠÇS½¶HxE˜*‰Ä-³R-Ìa2pÄ…¼“ D…Ý“€:¢A |f"yzß ‡¦ð"¥ê‘÷Ó³ö*æ ð^/#³5‚è¹}'b EA§$H‰zLOZU†§H-.ͬ†â¶´ÙÄ~<ùεç·Ûܹã¼êNÁ[uî<¾ÕØ›‡‡^ý÷ÊÐh¾Âž/>÷Ô)@ðzÿÇEm"&fP‹Ý«”ÎË¢òÉ.GÍA˜êõºŽjhòñ°Óí@h<Y¤œ*…†¦"­l â"-SÙ ÑIM6;ø>ÈÔ½õw±ƒdÚ½RáS£{_ÎQRH;ª‹%‘k¶P"ݾ÷„HðCˆY
+혆Spyþ¥Ë»t¸>:™Z¥k¼'kgAJOê'^¢÷È86D=Q8b©Ø]Êè?q( òù惺þå‚h.q«o\tÚaH:>Ë
+V¶T’n¹3©säõpâ±*¥kï…H©‡nklw}Ii¡“WHˆ-KÄCÂb¨ÍS-þëíp®T^WA!`ùaáYê|Gk_Tp¬ÇÕ „b°Pø”=³\×RðþJ,¨ y¸ô=W\•£ȼ40È“¡"kö÷5*“»ò»YwËD;r˜xq בIzB Qî}Ô/s¹²\úÀP[L zËÙãŘRQ'oÎgû¢ºož÷Ó^ÁdÛ(ιxê@º*[s¢"*#~®ÂNp*jÀLªXZ5©sô¤PÔw¿&“‰÷!\uU Úÿò²
+„„”$-Pè8»4„¸Òȸ¡mÆŠú=ÊQ9OZÃévT'Cvf[Ëf==oEÂËÙ“Qˆh‘ÃÇ1\“&¾ÊÜ7‚ôA>.MΨÖOôŸ¾\rCä„J£UcòýÊ÷@Rˆ#+;Og¶ûÊ<¹õ±«|-!á²…¢ÐUį—|êôó–rE‹I¬¼"ú_™(»yhsß$c>¨ÑÿŒ—I’$·DO ;Ô ÊÎ\·–} m«î¿Õs©Î¤*;Éúg£ƒ$àðïXDQ$\—9N¹Î‚Àι-ß.¨gA8VÛÁ%A
+=O¿â‰DÞá¼gÞëÇÑI¤Gódö>ú»Xƒ1¶Ãk¼Â•üú™•`Z=r_ãgJ‡µ.¸ÃѤ%Á±};‹š,ý^'±e@G‘ìx9ŠC0/Óép0ŒEî­uëu³-²°œW7aepSµœ×½¨i“bÃÁ›‹î1Æ[E,$%Š‡Õ6,®EI-D–ß †bC„µ„eÐnRW›„°R¡åÝFíØ׋VÆÕ`eÑel®V°ç9F±”œKÉ:J7fªCYÁý$fŒ…¦QÖ*Éâ¦N?S1 ŒÔchŽ¸ÁãØ8vë@qÀÿÊÔš5‡yAÆ]%(/ò@3 ÈÝ&ŒÐÕl.i1~¢Ò-6ù (u Ûec™£ÍAX],zo°N#ºµ7Ãñ>f c¶9Ož5‰¨•&Ðy$ø#™þšy—w˜°=ýpÞS`Û¤ur./!ƒÁ$Þ#€üÙbÈD¯¨‘9céLZ>ö‰o6Q.¬Ug‰ì”r
+]œ’æQHwáp(ºßÑ
+E@œêƒãø‡‚ªÔ1Kô*þ>)ìMÚ’CœÃ¸‰;v
+jr¹‹ÈwÜ@d–_]ï“ñ&E¯€I˜
+¾„‡‘söšÛ™zߣU¦ÁðX‹Ñò«$ÅP¹ªÀÊW—âA«UÅs#
+LF“D>><„Q"¹š1ÆcŸô O´¢TÈ F)2—É"É
+ÆRÅw‹ÞÔqD6XÓ$}¯‹LR8øRÞEÔËŽ±ßaœ5DóñëÀvóSĺ®®-€Nn¾pfq)YSO<&Ž´ñâ;¡¨="ë~»$ ¥Nþ`)]#V¢¶s›âÚ Y#5õX7ïÊý¢µŒi,,_áž>UÁêòíÔÜ6ŒOˆà ÛGý°[ÿéT ñPtŠ¯tç^„}¯<(≤•^¸(;–Û‰¨Ÿ•rd™zÂB—ÑÎÛšÄ+δµ.NNIâÕTK©Ã•(Ÿx#Ÿl@l 2dYÊ,ÑíáÐóŒN‰Ž"ÇA¤À‰?¦ì~GÍ»h NgßS2“OG-gI60/6‚]Wûh4çjúmIWô4Ž/·
+†DéƒѺî àÖØ£MäÜ>°”½w›%†P•Z¹Bi«fáEÐaë<8‘Ì*0ß²³˜ö4g›l²ÛQ\[맊‡AZà͸šŒ_à ËgNÉš'ê‰ÛÃñ¿ß¹Žüv›;tœGÝxkÎÆ·{ëð4ªÞÙ™ÿTNBÃðx¹¾P¹{/‘ÇäÖ²öËb€ÇŽÞ*'’¤ ¸õ:™tŸÆדÃAÏj|\½o2UÜEH ÆŽl¯¯dXÓ˜nÇþ>éÓ}ôw•‹’#òŒ6Ê fî “ÓüÐÎM¡¡4aíVEŒõÂë¶?
+Œò°lLÈPŠnOëå ¹!„TE‰»H®—ós‘] hœ«’Q6GóçNÕ\uó¸ò¦’@¢fº¬Öýï
+Á=Ê"ë(x¼Ìǽ…Ç "Љ ¦ùS+¬vÛëÁú4¼ÊN ¿âO€þúË)q€Þõ¯¸>+¹¾]tß½ûioÔ\ÉÀ]«kÑ"–òÉÀåüƒ §sÞ÷*’oÒ×þe¼L’¹• zÝ¡O@×ú7émõý·z ù›‰H«’Éd"ˉ)"|@¶I׆õÅ1H±)¬z’W$å™ðå¨1ˆ‘ô,k®¾¤—ö’Â<Àœ‹zï«4H•Q'ò0|ÙI¥ª§ Ï4BÙØz=&étð³ò'èL’lƯhàð7PÔÃ7Ø)¢ž«d–A(ʪóf Ž Ol;Pò¶•7O !3#¹ÄònW¬‹1„\,çý„AÖ¼×p™|ì'œ¨|q AÉgÌ,›¤Áp#¤Áu ¿gW`C`™Ì»çö°NF3 ꊬKóY'ÐZÐT¦KG»¦+ÉûtU˜µöŒ b]âå¿à^l+PÆ\%døOƒÓÛ¾A—µêz<½
+D–è&t.ƒ³o0’¼y\ Η†©YÃø¨#ƒbÕh'¬
+Y±!….—ÿ
+Qõ-7íW’lé§Í·:´_ï ¥GÊjŽhkâè îàH¾ôó ¢AÛ¢bsV‘ÐÃò¨8“rù§›ôóRÁG©=Iÿ r*†¯®|• «›ŸÊz‚é÷@7é¸è݉BÉÕ~LüÔH<pOÑ×~Ñã`fØÓxÉíßÚïTâ®Ù¢5^“U9oª¾ö«ä³3îšÐ‡uxà‘˜Ü˜=éWgáäx-˜ì:íMúÁ„ZåèÎàj?]Lfaô±°–/ãô¡ýjtŠ—1å3WûOÌYo08qH…ôü :Çøûù\c\™vî’;Õ²2L–G$lŒÞmŽ1~.6EþrEn˜5*/Þè+µ: gJ“IK|¸^ 9Àôq´V@…æÔâ·{y¶
+Áâw …¤‡r>,”ñé(1‰³¢B {…i%¬›Å¿ y€AwÃCDc;U²\D·Òzp|@#‚‰íËñ<êGªàVxhsnÊgo³¯ŸTWMýhxIî›,{™;¦A·3=•¥ãìƒc/̆Ó7 R0¸%ÈùŒ½_'L5S+a°<ë‰ãBp>8¥jÞâœÉÀhq~ÅMÄ wÔ®% Ò‚q§˜©Ñîʘeta£µd;]¢­âk
+}ã¢õJi·uâ«[4u £ìÓ0}&Qè¼æ‚T(™@6¶^¯Ô?ÁÃkþ=âò¼^]bx‚ üŽs¨è}=7Sƒ%qÏî U~ ŠÆôm>Bç¸|NTÚWÇ6$Ö¬<ÀÅŸÖ¨ÆçÏs„ `:‡ÝQ™:ëVŽ¸^ãAç%Ø&9UÐ*ݘ&)<­s¯ùqš³wœ[-x¼ÎÑÆÇ;ÃðQ©ß?VÐhRFÏ@0ÊÊÎr\­j¨|{ R°Hò™TwW•­ðÀxÊDÀDŠÉ=›Œ¼c¸ö%>64;w×oÉE˜”_„|‰Ç‡á:Ë~.5íQåãðV¡•‡Òƒ1O¥^G‘Zçf[¤)ÏLšª|µW…†Ý Ú¹Ïɹ¶Ú“Ê:]T<
+O
+Lû "±ŠàílÛ’ñ˜’´L„DRÂ%Ctv5<v1JYyp+EUx…b[¯M·tå=­äÈ ¥‡”!#^ZwE LVß‚€öVÔ(6­^$
+öEBïnË‹c'[mÝïyÍ(K|ÙD?P7c€åD¼™ýäDm‡¦rNÚR•@&#€”uð›êì9ÄòËËE©™£:zEì2¬ŸGòäÂ)Æ©:ª)ÒLç’º» Á@q%½U‡›Ò<¥N¢Áh5þܽ¸/Uãgî¹fCÊ}]®z¦_òßX½|owt:DãYs!LK$h¬†÷VßÌ–Æ0@æ…ô”[Þ¼™5\è G/sµ<ìBÊ…¯v½%ESÃÈ©‡`ÊJ0<·PŒyªòŠømȲWöÿZGôC‡O “ ¶Š‘%GÖ_•Lñ£.Œ×b7:kBÎ ÄÕú^¶ED¸[ÊkÞã´”ºv½ ªHÏAÚÄÅÔç:˜"šf±öÁêB¸AjA¨¨¼më[nµK2ˆø#kþ:LoÁ7hä10b¥8­· é“Ã0ë(†jK*¼3 ‘?AeÍ ´†ï³²Ö]Š®ZM_¤—|à®”zVB7Kü‹M² °#Ohø
+»6#rq3Œ<!9@djùheׄ<²½«8?êiç.©i!8è~®ÿQ@JSÚ†pÏ.°Öò‚4>b!e×bñx ¿¤ù>qu3cÑß’ÙÆ,àúý>Õ¸ D·>µ<Í)AуšÐ×O&l bx¥JŽár<fq”þÏÁÆ-Ñ¥¶ DϤ€äb%f‹ÀµŒ;Rþ ©¼<@ÿ1>iP{Œ-£Ã:
+¨bñL¢š=¬Ã_YEfû²Jü&Ñ}*ܘÊr by§N_æ€NÑH’³
+±üsÖL›eš±I€WÆðA·ÝÜÂ×GøG|1Þý·[Ð#"WX,ňk³¿u^µ½©Ê]ŽÚu T]>RšžR÷Ü•¼‘`Z¿‰È /'yöWXæ·Èû–±wú¦ ßa%i<M®8CÁ^Hýã‚Œ&ÂZS¶ª‰ÁÞŸ÷î[3ö*"®êp&ò‚`Gµ¦ÀÃ/ Ê)v€ðmÌt £có’
+¸âgÙx5Óž±4LçÐV¨<~êÂŒÔá@´UUPäŽ KñÎ*Ÿë4$MÝ¥Y½&žhiòexô´ ¼pEع…íèÈ+›É1´5èC¿\ê—Šb2bs3ÜF Ñ0eW•Fµ€+sÍ«÷ÉΤ öœRpn„•D"÷Õ=wÇvâ²£Q3£Ïup2z`ž$^¹GŽ}b|ÒzeÄŸ ZâE>ˆUO|fætô•EnËœ%?s¶Žs©£·9›øxao>*õû'óF)‹$¹Æàþ¹øÁÈÅSäÄà?ÆË$KŽ[‡¢+ðjyØ7ãòP»ð´´ÿ©ïÉ<ʤª<øþNAAÀëªÐU¨£œöé$UŽB‰‡`½^‡õbq¡˜´Ûˆ
+’tîÀƒi+vÉ n`"j!àŠ±ú“æãR~Ô"o•*´äÐa dнy&DÕ¼3†ã)êßAƒ¶Â‹¶ÝûQQ–ÙŸ´d±6Õ«aÙ'¶ËÕKJZÐ'4´'"§‰C׬É]‚Á%¨5à´è#GúŸ…Ä¢ð÷˜ÛÊ#a‚AŠÁX—³¡ê)sÖRÈ1JŒÃ:‰° ŠQŽv`ñR²Nšè:ê4÷;üPåéðF3g@$RÊáÿÏoº/$®Ü¼¢ûeE`‹1;ùyc‡Í!9‚Ò´š
+~çÊ…Ð)t@ÂV‚xPÃFÞ%Ên˜$ñøÒÙDè
+µ!¥µU‚OIP oÛ”@ßeùØ!VüÓN"9$H#’î}­¢$ÿ¢Ø6E5=€?âôi‡ÁM!4•ôæå0š~ ¢›U‚$f8 8•y^Þ)'…>6zPà™,^šw¤ÉL°éqÛjÜ
+ Ð`ÎÍŠ1¹Ìu‹ü›“Ù<VÒ¸ùãœÈ5¥œ#ý`“YÖëØ&;Ÿqò6©¡/0rì››1o³t¢jFðhÛîÁù Ý®­ÃÐì ÓsyâF_ûÉŸ";È0&ä5ÆHÀ¢¨îý„Ô™ó´÷¬ø.e â"åR)1r¸#!Òhñy•ã» A)ndÞ‹À¢/2™q#óZ”#%ÙÈÊ¢ˆå›hÑ‘kÎœ²"èº26…îH b;Ù†'²M˜Ž'ÿ‘\€!ÂZc¤\œ7ÿuŸƒ£xÎOä5~üËÓHJ¹ÀJÝ™)‰J1„…Q
+w(a4ØaÞ¶mã~Tà3KÕHŸ'‰I5‹hco †Å@@±&û+°,¸îÊ{Ý>“Ò”E `/!œ®XZY%Qü‘µuæîë “²™$ŠóHº?uŠ~½)Sˆ)-‹¶)jXSùÜö¥1#ÜÐõt"-¡ÓÍÓÇî_Ñ붪X¯ÓÒ¬gs¢Ì(ˆ(Ù|%ExŽâ÷t¬3]kXŸIžÃ‚pŠãïç­Í{3hùPbå“Ž¯Íh\8(>pW­5°jàf ý¨u%oĶj¦ëƒÖ¹³[­'§§œå˜íõ•&J
+Nt$Jì'xEJ2 *ãð/·è¶ˆ\™@‡;fhg3Øÿ)B
+®cßØKÇXy-û`Ÿ’(•:!ÌÕ«z<1-:i<éÆÀ´Ý;XYí'û
+|‰%RØ}éÒêćBh«$›ÑnxòáxªÏü"TSÜ›¨È`îÆŠ0K”(¶%÷¢,ÇÞ•=¬‡3<âêLÉþLæ™ÉH¾NyQ^<æõU'(Rj²÷ÕMFÂ8+Es§4’µ ²óï†|÷FÃýÉ8€t—ÑR…åeD‚5ò_å e_¸ @¬S= ¯ÓñÊ´N|Ø>ÅŒiŸA-ºìÁë4Ò‹P0’Ý ¤Ådç‘Ÿ‹P£PŠ„;!Ô'h“Å_VD‹á.Z-¦ZÜ•1UbËnãäYC‹ÍßÝvæƀߩ(WLÅC#=d!–ž‚Àf|[ÜòN9EŒ&Ñ}š Ù\Ø„ñĘŽãÎÐ-ØoVÂßÆBÊ–—è”hbiÂTå¢Ûó8ߊ£:MéQ-„1qo°½n£WñÔˆŽí6@©¶Å·÷"Ò%«BF¢r¤MëÒRúq Ævñyxš^ÿ²ãØ:Ì8ôÝJ[t½“|6ÉŠF`ûAaŠ…׶zbȇ…&x±TÑçPˆ;Ië•S™óŒ˜Rx7úÌÌPa5Àe"¼¯°™ý§„Ó]`~uÄlx F‰”†íÅÏ­²ßlá/[Vô­‘‡k¨*óW©YÀ6¯]uj¤„=‡¥l9ÒšúY-–™z–‰ÃsqU`»EúbÈU2¢‚iVR]¾Ú£ó ²ä.¢Ùì%@^Ѻѳl³¯
+š…*$³Vmq¦1;%ë¤
+N‚BVÒ놖.mƒÀr3J@Ñm?(XùB¿9í˜îNËY«¢£æC©Â`›ê–í càBì‰Udì>]4¿Wè ¢¼Ü8éú ;ăjon® V9À½_ÝóÄZLÅ4ô^»a6ÙÀtxb|;HEæ)±ë4š’ „O;v ÁÝÌ /)!qÞ})Y¿¶ïöû.Zã ‡1q_üCå=ÌëRć2g5<àZŠëjEµù“\ZÝ(‚^h?_ÎQßc­|°•'*ñ[—5|~È>݈‡¿½ˆþÄ83LÉ:D&VÚée—À+L’dnvqF`©‘ãôœÊºx¡âd³yNE½îß”hGu<3%‘ÛpOÍ”Te[ˆwjTArL23 ìé™Bé?4ÈÝw^Dö
+
+ ì…Ö£:¹;\/TÜgq©XCG¶)ˆÄ¶šÝÏ°M‰&‘zææ'@Ø0¥°¸ö+͈™d<s_¯jXf0ØS1š’±p³‰V¶5ĹVù²ìù¡‚ZÆ‘»W‘,÷Êu´0ã>èï°2þg¼\’k aº•¬àc`œíäíš# ièz]™%
+4¶%K
+A #…Ë8ÙMSÛ`ß:ìÁPd£KâeüÄ× ”,À¸€öãêh¿îè¯Ôÿ<`S6eØ1:ç ëÚÍ::FX4/Ò©ÕeˆêZ<Ââ<ʤ3©¢
+˜Möŵ₴ŽÔ¢dƒÄ€¨0¬Á|íI“©ÂqÖ€$ÖP•§«- ó*ÈYº<Ûçáõ·Ð¬°èr ¯L/ºöuê„>
+H‰Œ—M’d· „O0wè ¸‚
+ã)|6lcjÁáÒ"Ö2½Ÿ4“ÞƆ¤„Ob}ô QÓ4Z˵Š>´EóÙø¬Ÿl•9s´6uæô×H€=´÷Ï ŠÑºp—¸68uouuÒ$5œï6 ×½]{ë}b*;›NÿzinâÝl=N>Fô9¹zö®×÷>]oº—éüÈ.:æF´Ñ4sòMD{fo*)ÚŸ?þó£}ü{ígÊ_ì1´??…yúpýøµ*–JÔ5²ÍhW¬ñ ÉmÂ6¤§óEK(±‹J)^ŽÑî­„7 53ö_÷¼býñB¼ì3|ô™-D>
+‘Ÿ¨û¸ }X²[›Þ碄–Ý%ûpYä8(p Öi”eÕýôÂDŽÜxˆ™ÒlÜuå(©)0JöÕÛœÓe^Âi2ZŒ9ý\ºxft+2xªE+2$ÒçTdaáâM»&¤jy,ýïˆÍ±hP1j?®2ÍÑGs¨§bøis@z_"ψ ™Ž„ë‘gÆÚi"uÎÃinQ¨Ã[ÉÖ§®enÑ%trÕKWÓÑQohA '*“¦ûJº<e"4齃'P è©ÈÕ›,¥Ÿ<ã[ tÎ3r£ÀŸG=4£iÑ?sÓ7-+Ì°}w{ŒÆOjÅŒM ¤PN(ü¶Á­àŠ
+b»X‘ÊÕŒ'dñ ‚ZªÈ‡«æÜ;ñ¯hÒr ‹§é1Ö± <¬åÄ^};÷dóy
++ÃLoÖ 8Ý&&ª¤—5!€ÎK$‡OæÕ
+®WÛ žl~ñ¢ îÁE›Ìy«Ü ¾¬9WÕé¢Ùë /HÄP‘Agér¤ / Ó­!Cœ§Q>¶Ëuš5mEår‹Ï2ÌQÊ™Øàr/3J‰aÆ*'ך¼_C9=NFù /-Û-¿°RÏÅs_­¤Œ¢ÚËrÇCyxå€ö
+,¿Ñ~‡n“ó‰ð
+ãj’284Ž+Ë‚4Åa‚¿ùÕ°ÿ ©bøcp%æy^Æ”À13¿O\§SŽ9悘V@Ý-¯®T€+«Î«¯ þp/VBN~]úãÍ‚¾Y¿[Œoƒ°0Κœ£ê±]Ñi®&-ïAOgÂǸyÆàÖîþþ¨–ÐÔQ²\ Ai'Õ™ûhô…ñO¦J1à)å•ŽÖLMiO䲆ó*œÂÙ·“Q7¨I‹þ8 H5ŒÛš×N`á_‹¬K,î]áëoÈŠÀ»ŠÑÉH?’}w­Í1Ì7dµ
+TÈÉ[²Òê‘(vØòˆ)D9D)n¸ŸÉjëRõéÄ^Èú
+:ðNĽF™¡wd}xÈv‚Ä’~ù O':PÕ*àb£#ÊLTå ±Öʳ45=‘̪-jE%¼ãMŠ ¢øyiV®:¯©¥áãâGª¾Òë@Õ?rpSµqèœÞ«¯RT¿ÆëÙ/æb*«ó–Õ¥o‹îÓàiõÏžÎKõŠ„ÖÆÉèsVO¦5“§Gœò0§ÔÉ6*’{³-'ªÑ|.ò±”ºÀoY
+«„É˯ÃèpK¨áÕýk§\¡Œ®òíÕâ¤F¤B)€@l®…hIW«Äô«_©p‡!…ň~wÃ…/ÏëÃÕM WžrèJ"^IzÞAö [SLÖêGEæ#ˆ¡¬u¼ u¯rïNf!ö«êœÌÐÈÒÝÏ^c•X¾"þ^´€³£EhŽÃ* ðuXO¿N’p˜a’v¿dÿÊAø($¯¤†Q“l“\fº\åt:†5§QA¯‰íÛ ƒõ°s
+ð±DºêPçgIž‡Õ7ëðï×Æœä`ÈàÑy^­àD Ï&©gÃÍÚÑ$êÓ' M”WjT"ïWÝüÅ^$¿KT‰%"žK|Qýôšà82%ó"½(¿ :˜à ôd‚’
+?oXÁOݪÈØãÜË24§äÚ*ô–/$"'q‹YWŠR‚\æÆË¢
+%4k¿ùGæ¦Çý£=0 Yå亖ñ5-1«M½veMŠ+—!cÐàûõvÕ:‹ÄŠEŠÈk¹Q_ç%wÌñmP
+HÙ¡•‘zê¹T8R®ÞɃiã:ù¨-"+ßyÊQ·Ð$¦.
+>,NÉÆí*T:šLšÄWÁÔæ®úÏ ã’tJ}QFeqˆS5Ÿþ/ŒVrÇ(y,ÜÔ<š<xÝàüt+zô©¨vÃvðn4?¨‰†ñZÆúìåÅíñ·•Ü¹ñ©á¾—ß °Ñð4{?æ“ÙàÒxr÷¿hå×YÞw%º6/a¦`MÂ~=¦°Þ´¢aê¹±T²bA_^âBÀXÃ`’°Þ·d=‹.V’Â¥a åþÊÔ<‹VÄ?—‘"›½.úp'"oJˆ„(ú²“ V‘¬—E¨
+N}—ðÈ¥zŒÇ]éa€‚‡…ßE}@
+ŽÖÓižâ¦'qÀ®K®€çú¸¡%V?É¢œ%ý[K©ÀÖ(
+t
+*Ñ匭Í49oìðôÜ€6*âòÌÚÑ,“‡/[b¨à‰„Éb{1Ïw%ûK“Û©Ë=×û9@G #÷§¤t:Ea.¥N&x4n®n ¡däé [Ø°µ¦·—3¤¤TÚgȇDŽ<EVâKŸXÕ8Úüí_¾ù’W¶kÞ$B®ÒKÒ7“±îW‰€‚!Qè8Xœ½Ñ¶Ë­G–ÑO&Ë%··ÉŸùL¾ êüõp=‹Õsr©n"ã!Rñ]çPOžZã )0È4ÈvEÃ#{(À*…N “¨q0µžWaþðíßE?_ix€õ4§"h½Éž‡F0™tsÆ-b4ˆ ̃4=»¡ƒf^xL´Ga¿-â/ÿ;V\€·Àƒ«DRp‰ûc«õoÎ/Æô鸅ŒÈÀei›&¤}á° FÊpH/½ßŠ| ÝŒÜc/$ò2Ýž(”cõ…2Å
+æŸÙØbóÞ°ÒQRÁÈ.Œ  ›ú£ç@=–›¾rÇfëZÂp&沑JzÛVðVdbsÎaÉ7Ï=‹8Ô•30\îp5e'q«¬ìOO5¿¨juŒ#wNFáYžXŠxNÒpè
+=3ÛMYq ¾,õæÇŸsºz
+§æz)q¤+K2X(uÅEš$–7hþøU–“ ·qYøŠ¦¯È^Œ»<Qô™êÓÿª
+)Àía‡üV]À¬ä#Z´o]Þ÷<î›ExP=À^yTdJz`Ý=}3–…7Ê“œ %P}EÚ »œ£DÐÁ¯WÄÄ+ø¢¨g°D3*°*&';¤=lnÇØvè]ËQ<¡ NCþ ¦`ý)€= F¢ øöš=­©{Éò¬‰BÑ¢KÉߎ¼Zý×ïÌ-¾’TIyW §[—”ƒ
+Z,ÇÓw‰¼mÅ™ZÛÑ,
+=mµìAî<YvÕü39ÃeUÚ.1._÷¼u@åø?#:眓Ì9LDD |ƒÄÞ±—(’ì0£…¶L$—‘ï¦=Q$Zï2-s\_ý:Ç¡ýÖí!…1Ÿ1FX”‘±rÿ
+LŒjûM¬Ê”c釗xì Ùs5ÍdZœD¸AËI>ÖÇšDdƒÍÏ£ÑMYF9[‰¶ ÊqÅå6ÿZóX'šC3B9mÌo]VüyÜ·Š^hçó;܆¼dñ‰¹¡|½_÷ L8•—+êdQTcyµ]}—™zî!ûä%l3æWe;ŽŒK%øT:
+[«Þˆµ÷CÏð¯ ÷ dÑÞE([}ßç@áúQcrìvf@™HØC¦À.JKŽ» ò¥$ŽâRš ªåÍw2ï _×ìÛ ¬ ¤dQÂ^³HdÏÓæQ¥ˆ¦âÃF'(äÂYÑí
+nÂË@/~Tr\a’¸Á9ªñ
+e4”f¼)Ò6f¾ŽzMHÖ3åßé ‡€”Ït¶›˜‰šB»›¨Cå¸bÞ¯29•¾äÁÎ*d96àˆ-ÚœÅoÃRîˆkú }}WhÞ}ˆkT ©¨üx›²Š ˜ ’±WÁiX41¯Ñ:À8Öd%b¥Â+&}35ÖKÀ8b:T
+oþ+Ú øqÁºeŒ†7Y æ­GÇ0þ¾zÖ°úcNobo0<@”êâ,Ú€øÌŒ»Ù7~öw" t°&)œ '1§¯D‡;­ZFµâ¦©¯SK%[ÆuS±[ÆܘÀ3­sz|%}l¡(sì¯ÈPâ·ùï„ïÛ j%P?‡WmVûÒ–Eﲚ5©–ædÿŒ»|œÞ`Dƒá3†‰î×½ Ï>b¸ Žç2?ü$,Í’†p¿}†þÐIü©œÛþR—dAŽÒ¿0
+ŒŽÝèPæøDú˜7FÊïÃÚ±a]V~ÅÀÁ6gËçs>-®Úy¿²ŸŽŸêYA³57‘†ìð&·“5“$ö7Ãü­$NB¢à(Vœðæ;Æß ZrØÁ ÈRÂÒñxl»Ô"ÞdB§ñÔÙBa{$‘_ð³³ Ìv &c…y|ØK˜“"èqŸóïõN6ÿõßÿ(Aá“ÙØ™WyÀζ 7¼ ‘«ë&” ]SÆZq…ÛhHRŠòŠ†R±½“^o6®R}4„{èíÔˆ:äÛ±à[»ˆxŠN´åMY’”€&£ÁÛ
+@…X÷‘éO‹ö¢MmôÍ[P~’+‚ <¦xŒm¯‰îØ¥èç›".‡7AGJk»ŠØMô­”\öÀ¿ÜAêb[*`UèHˆÜ‚¦‡wu¬y ƒBûjÎÿÛÿý üma­+^âÙ<~ÖS|©…¦7ƒúãÀ…´±Œt‰¨½QX`C¶cô%&7žÓ<Xj¤‘‘„aj.ò2]L5ñê ç¨lŒ\e…üýãC¹'U÷;ãB *AèÁ*˱7¬ |5 BéíZ‚üàƒ‰¨hwx-‚©’¬8¼äDw)z@QWFÓE­³œ¹Ê%à0|7ýé ›o£Bã {A¢ù¥íÅx…4Fzâ>4ø-6µ"LkÇ~ýÃÓ c]Q0¬\ï_ÜY² ®Ò%±²èCx7”ãMÄãnû ñGW“-²ED.â›,IkO ¬
+È@°ç5ÞA:HTG”Xɘ”ÚéÅΊUŠ Î]͇y+¢Õ‹ƒ‡èØWýFß*’]œrd)âÁ¥¨jÜN%Oç9®Äˆ¹V4¨âýXBôd,x‚¨°íYÁñ¼
+hÁ2á04r€'³P[O–8"q£}~˜2$γÁ—7ÝÒš³šKVÕZ½ëŸ˜Å”q˜Í7ßai0¾©Ø^s9Þ®ìÑV "~Cæuå-êF@ã³iëŸð¥V°T qž-‡ñÄ\d(1I>B¯;û@3±ªà%_!X¥PT7žðŒ¡½ã8³órþ! Êb‹O/Âfæœp8}«:„Ì$e}š¼Š kÂíªåJ¤Ò©Èž^'^€øëZTC½ð=tF·â½B ûN{ «’ã‡yy dåá Krl|•ˆDrþ>º¿´·<z=ºù¶é(¸:‘wÝ
+U™án›ÿ{7Y# ¬ûØ5˜ŒÙdÊ”]§O½‰°f“dò•ec'®K‚X!Òsó6Ö}cÞ¥__ýRÏVtt¯åú=’€Ú8JÈ ÝG=* æq$:cz1€¶c’¯ˆPâl:K`
+D¼âJ®¦{VömžøäÇKàÇï£þ
+ŒOêÅq¤sˆ¥œë4”ÁWBpªÍÝ0Y(@ï¦ ¯¥oÅÆ{ÂøôGàÒÖ £¸ÔYä¿oñ'½ÅÖûê<‹:ˆÎ$B3í׆…pÑÄáÎKÚT·X+ËKà"‹k}—LM›ƒ|Ĭx59bFE¦)%41ØR²]q›~i„<Xo±È°˜
+aÙ~$ @9ìQÆfmå'ÞBøæ&/s&ÆðÄf=”&ÀØ'n06
+ŒåŠí´H¹R0L|ìBÓ96=Ãà h2P…D3]i£fU€Nù¸@;3xAœ
+v>A"^1ghÙÓ¯7±Sau¬öSf©`lÝ!*Pçì²›ÖWеäy*²ñ€ÌQëñeÐÃðÝ_™ÐÏ^r3‹N ͸çP-¾31” sîHgîŒ(ÓÓ$­’ À!¿ã 3G©Zê!ØRF\±û'dˆ’Ð>J¼QET%d¼¦‚DT£¨ÞòP@Z§Í$„1ï4IŠäšÇa.å—Àà9=V Q|c59`… ¨Å´çî ª‰þK‰ »°/(®¬CªžŠãüü #¹¿¦ùbÀ=›Að@Ü2Ð_ÕÏùªŠt_ÏÓÌìÂb8…%„É
+È¢#Yº¢#‹„`;fL§'äŸ@0#š[t%5ßê
+ÒY,˜aëXE5Wy«SRFŽþÏ†à—Œ˜<@üJ€½6¤‘[ìKÉOÑNXúÍ#Ê{¥°Öaˆ5@îÇüe'ìCÁèQˆ2ˆ[¾¿MÚjzs)ÉRfs;!WŠ]Å ®6CÎÔ™’ÛÃ!LKòN$Àñè[ã%òµ)¼zÓ'|tfw{Cî›m™¹¯uhÚ—çP;8õØ +˜8Yž³s±‚ _nëqm•ølä¼EŒK1¨Ä˜Uw „6Ô/~í !áÐRëNx`, q€ ³}­RxáM¨u0Ô É:0Í¡„…éŽAò„‹
+“&I¨¾Õ$Ï“pRm$:ËAò¿oÉ@SEµìR’©S÷î£.Ad¬‘Œ¾Çñ5>ÆÉŽÂ`´–éäLBd¥åó‚(ôðžP[DˆÒsãêæñŒ¾> ŒNŠM"=ű÷' ŒN)­qõ¸Ôfbøê¶xN†)›!2ÅŽS_šÅµ’“i‘ÊÛ¸tIpT/¹èR‘ŽÇ•S]ëȉ(ÙÍCš"LÉ'ûV"Ò- åd‚ŸKiêÇu(D r–›1t<ÎàNسŸô I è.ÝLjeQ²KN?tÙ &§}…aõu
+‹û¤'(Ζ
+¯•Û*@×a÷Ü”iåß?n': ÿQ¬Ì!Vã\AE61“¢&Ý6ãy§T¤õmâRÇ8qLP2ˆúcŠÿykÄ„í¨wi~žèg}½¿"–63&(` ¨ ?{¼›9®1«{M«á{T–Þ¦JyzL¬Wæ2#îNý^xÞ õëA„OÚGºŒƒ¬© ù´JA÷(r‘ãéù%!5VþÑ6>&É+æÔäzѸÔ^]ƒäô=H
+B¹$-³s$ø—¿àEFáœ#³,?T2:Ë!GÊð˜ÿ€Ôª£ñðuÜcŽTÁ''ã¼PH}²NbÖHEéè#AȬ Í››ÀKŽ¤CÜ,‚0Ž1R=¬©ƒÔ¦§yhókŒ<€Ç'pºI—â÷ŸµÎT…)EaóñyÚîte>דg5ò·;ïYÖœ¦›Ër™)NE+ÆÞ A"Ö§ÃÆWýÎbaE= µ÷à2(¸,a).8Èœq×_‘C6©`ß%@`£Æ¶%_…þg‘lÅ\M¨Cû"ð§l6·-.¤ÒdQ.V8@w©-¾0¤Ý³~M#@sª‡ÔwdG~ß<d^.N#¨”hÓw«h5ùu@Ä~q.‰ÌÊÕxw ]ɽ¥Ã`*Àû™§=F„S5£FšïÐWF8ñy ugÚ(ÆtMBFH‘B‚h),‡‚7úÚJù‰özwÛjà“8<2f>®Bi•ó´e¤û’¦ä»V?GÞ„Œ K’ÅÞÕÊyÑFˆÊ0«œ¾¼ˆÎ¢,ëºÖÉŕ]Ì#×QŽ’õÆ(áÃbÆ¡no§2<JÖµšWá£)í†å ¡ó*z¬j™/t?Š„-±¢m5(fGf;×à<& X¸"–ÚvR£wÑIW@9)ÄuZ.~û:uª÷q;ÍïAu-\gÇÄåGšôœt@Aè1ìÔ MXôŽò%.ÏÍó‘“ÚÈË' (w ra16¶h:¿ñ²Eí{î¹VóšŸh
+Å_õ8®£¾ÁFWyîý8H%ÔW¢ÈÚÒj69D£µS’¯I ¾Õ&ˆÉdF[&…Àtð›™}'Yp ,Â’Ï<‘x"$;§ÖM ˜±ƒLøä~eãHš Û«.Õyª¬dÒt'cÚ„4E­â:²¸vœz—¶BTÉ`ÿóõD¾éÉÍJX
+=JÉ ²¤P*FMàAù'žML9­ÃHQUªª½ Ål¡nðÌ g(ŠÄ› !sëÖeeüé<Á®IÌ€*5@ÊçNüÍé‡f~>ãÇPÓ}´aV×7jDk™ëzÆïÄ'½Á-ORôâr<´Á›”{²É7 µh!oKÚFU4Õþ$"[ä÷=¬-…?¾eH¢2°<“±·Þ(D!PR^Úwl‘)æšÉIy¤jû\°Šu':¹3õ¤u%FŠ%.2ÐÚ6öU%‰Aø8U<¶ˆ<ת`¤ŸDå¥4<? j%l€çcÇUSKCªã*ñ'DäL'€P¥9»Ž‹yQ P×®²¾€°´!Œ',6×»‚x";NQ$UrÿöÊ(×,Åýóôù„6²Ij=²ú<û0ªņ&õtZ£-Õë¡&U«‚1êá1ÖŸÒPè÷V áÒ$¦“9‰øŸéÜRk1 Úâž­9×GjÒ}¦ÓÕ„°6æ†Þ¨ÿîq}Úø[J
+s`ÈÈù(ÜÐH~1¤¿({ƒæù­WB%§ælÌ¡½ãÌç&™ÎÄ‚`w£±'%,­¡6¨% A¾“C²ù)&­íY|:±ZÊöPç>ÜHØ}}}N“ÿÉŠŠ­©‚зU=vúcmÜCn=–W„€¥vœCæ&ÑçUg¬É†Û-Ÿ‰‚ëD”‚í’ vÅ"ÊM |(˜Ì —ƒŸƒø>NfÛHÊø!ªPdKãY|.ÿ)n¸^XtCùðÓ,ò
+”¤$f2HA£{¿÷ãã´I± 7¯gˆ©qÒBÈÆ™AÜf›Ð¼·Ðö2¢ ìÒ6X€W÷P/ ˆ(;pÔÈÍöY)ÜŒ«p®QHŽcÕˆà’×xÃYQ’©‰w/¢;üâ)uz˜\ú.|’
+ºµ
+1‰hçsÏwaß™ƒ– ;Fg¨(7\ãÔoc™óòAi.‹<\ÆFé 11›™À¼€~½
+H蟀ž®+ F¶9ÚíÃï ŠÒ1(Lf¡èqÝT2 Ttøþ lúǨþºÖò;æózÿR[*B«Yü äèñM˜-Ï-ÙöÞ+èá›êþPˆJ®Õ»òæÛeŸï|‘øóY>¥Ý,ÏVŒ|Ñ»è‘*•AàîA Iþ èñºËûÞ‘O¨c-Êá«Åu‘/*'¿"x1–I+PiûEc@ŸïTÜÃ7°‹2¢‹×._õß+èRΆ9KSðÀ´ëÛ K*Šž´Uh&öÛ K¥ž¯{”my#úTÐ+ˆh„É $*°A†½vÌZ`KÉ¢v¬¡Œ¬IÆÓB“úÞz_“Ð*B—o¯#¥­ßx§£<CŽeÉ;ªÃÅ„£Bš HÓgY ^Ž ÷•X=tn\Äòº•‡›b…h±d`x2´Ü°/®2ã¤SÙRQÓŸ½´¾uvÞº¼Ùäzü˯¢$’ ØcD%PaÛ…æaÃ?)ìs„|”ôˆ2‘•ˆŒz1H×/Ñlp] ~Õä-QS¢á‹s¶„mµ3”j±a²=TååJ¤ß$g"‘5šÀ'­Ê<ËÌPaq$qœ‡@ì6D]º‡: Òˆ¹XžUÏ«ˆ¢,=™[ Ò‰I9÷ê!?ÿºc´³65¬Õ8z» 6-sQΰï°åx·VA¶ÞÝücÀB>ò>ÜHçu:2Í%&eTEMdêœÃPå–»bK9«\$¡@ÒÉ9ЧyÈÙZo¿©KèYmXÇÊ‹s”-{FèòÅ‚°t}\ÏóœÂw±Èd­Eq•GŒ%fYEpê¯bÆtÊÐÙu^Ž,¥>¶Ah-ŸCŠ¥P´ã•Z)ãª,9¢,_21¼ð(‹Ù9MdBZ‘ã*n­ð‚ÙÜÝÊÀV­B I¢õú<¤±ÑðÐ-(ÑÈžÜÔýAÿî¾–4œ™,®úyu‰‹Øjd+¥ôÄìò6èÁ
+Ásx ™ï’žh³9ÉòcÃD‚(þ¶ûü„¥É®ÒIÌ¢$‰‚ì¤=U¹w¶8rVY 1ÒÆ<óK~ÓÊ2Îfº$·ãGŒ_…èëc†f'2._‹²EIÐÔoÓV½Äà¿­]þÉžñÉUÌ$hdi« ¹&H‘:
+kÎÄ™1A:íåG©+%„¿d-‚O\f¯êò6ÒÊXÑ©5‡~9Içéd°Š¿Ñ°Ìѯ¤R’bW”°«ÊˆÏ<_C-è9ÎUæÌTIºœåzKbÁ€áj\cJRð3æ¿äðjéÓZÞÖ$Yл|…¬)Y‡AªxÇÙ‡|ß
+(ØsÞšÜ,ýA®ã&j5Tˆân5qÑá÷‹Ÿƒ
+Î`år…=Ž¶ã·S·§32µÑ7¥¯t†-B1–õS)•Fa¡ÚÉB•‘ˆ6Ã
+×4q!%ó‰S†õˆFÒ*S2&w¡ œ~Ä6ŒB«ç´MR·©øêb\ÉÜ*@ÕaŒ²ÄÒL[ÌÒ˜>¶\ør‘ãÞW>MªEŠq,]bBHGXªM²eÖ›‰¸âOçVFrõ7NÒÆ‘%aó™Ö¹cöAvÊð‡%ïjœ½„‡>¯w×!Æ ‚¨Ïë:†ÿ$?8å: ï÷”a9{I‡»
+mŠ)£çeM1éw¥³Yç™ÓÕ üà¸?<<þìÕ騹f÷U¡/|v„—Þû7Z*¡¸ºhð S±Èem•XÉUøí6™r¢®ŒØh'/]êèÃi0½³ªyšçμòp”bYÅßšÞ²3Žå0†Hà!Ñe Âî/†êŒY<ÝæemìLÉt#íÀ}þºû½Hßiè e†xî¡ù¹üSWm™õ°î^zI, /Ã?×{KS´ïǤîe¾PF$:{0_<îÒ˜ÕQÈPžFàî”´„©y˜› d |e†™âŒŽ8ѶpšãïQëxº”`ÀP.¾_xø|çRelå°ÜCÂS™«<}=ssέE;*^.uu”=ïh™X›(£–s*¯ =JjP³îóR’8†¹áŸÃ¹¾¬cÔNâÛ‚WèãêºÕz ½2ÓÝd.ìàI#*AîmiÄsÞ/7O ¾¯-SÚëû•?¯N lð¾póKÆV8”÷.—ûÏóp…Gì—ºÒ×!~I:@¿@AÑø~úYGE|w Q Yi­!ŠòŒv‚Ç>à³5ô]âR2á.µÏ'Æ
+³ñd¬G )ŠcÌJãZûžd©&•Ž©«gH+ˆ\¢$ ïD¶Z;ùà²B×¾ŠhGât=}hõǵw›I¥z•èÛÝ»™ÀÊù<Vo笸¶ê4{ÏIc'óùêñmVCš!®Á7p¶‡]=Î NÕmt‚x½’ßDÆÇñ]Ôl’_% &œÈlx|#¸%—ôu<B0í}ʼö¡½rÃF…{,ÃÙtà™íÆY…ÞµòEÌx"ÉêêÕÔM
+ yp[Ç‘¯c¦æVƒ@å«_cð=±÷õ0c£#Òá„Q™™qôxžôé«ì‚ŒØa;.^Šã&Ï©·’¸›Ý—lþVÄÁú¸3 SèÓ\Mîæa63—¢zN•ênšãâÇú"go¢Pat×¥¶ ¦¹¶rŽ"Òɘþs¼^8?ŸÍÏ3JXÅà$G¼_PƒÔ‚Æ9·2é;ˆ ßgžðÂ-–~h/pè>žÚ(Iœ †Ë°g§ëœØ[”YbN<˜nk¤vÛ°;‰$u•z²!;gT†ÚÆ*‰N³äìQìä–ïW¥ú([±EÀƧ—!¬¿­…¡¬Óu¶µNÍx‡ü÷k°ëWïf¥y+þ„#fí‡Óù+:…‚V¸[êÉ·Þ v˜Ãë\~Ê3ƒ:187×æ _¶Þ˜ ø̺úbóç4w¨÷0áãÀONf¯cuÊ
+S31Óâ¨hèÚKΚíÈ
+1ÃL%[LmXí$ œ@Ònô¸Rx˜þë (Ú"K-$4ÈIû¹Dnhâžž+¬’=Ö÷òeð~)YV4¾¹qÇ—uŽô¤$»µò®s¸ÃaL¾=ß[„ ŸFa×ûµM'q»qµ€ÝOäuu³x=¬_JòpúPÂ_+4ü^d
+9œÒž:ˆkCŽž¸»mÇfÜÔ°Þ÷öƒU﹊ç÷Px~E†I8ç›Ë Æ:½Æ6–ùŒJ0O–~ê{ª½H4×ÎŒþxù\¡†²-þºŽd½¨‘¹¯9Rw€¶ÅÊO…Ñ#ßœâ»Ù&Ûó·“ÒíGûöuíʘw}0×Xq5O¾­ŠîviÊh„;ZìaÚ<Kà Õ{h Ô6¼¸ÎÓ<Ôø*9^4!x
+Ž ðœËRœ1^oúøê îG;+ZHL1Ê‹Ç
+*áZ+Jĸ¿uÙØðR¨Ba([@r‘£°Í{™»"…¨¨÷ò„˜®†Œ]Œ^§âVÕ ÛA2, »F¨š¶Žâ,³†H¦ì\Ã’Í‡Ç Aæ¨J·¼b 4Qb|Õ>v*X!$Xj¯”¾6ÜÑÒ“ÑðÇ[ê6ÝEÄפ%@¸°ržÚ©9òÚ°…,”;¦´(VÒ½he<ƒ Ç@#«¸ ]jyq(òL‡™e.È·’ðý"K BŽûg«ÚÆ-ðä°£±ŽNƒ:n.Y{uXk‰R½€pÒPÄÙâ¡«eRuøÛ4&îå/“¬c•ÄÍŒ…u]‹Ž`’ËpÚ1bgc‘QÄŒAÉ ;±B–IâÓhgLÂdΦhÆÖ™èòßã:#^µ2•òYÖ´–]3þ—ÎgªùS7ðùËêqZx­'–T¯$KWI4$CÏ ÷ƒø;“N'ŸÈNõq}'vhUã²F½®w)TΤF>w“0?ˆ¿Ó£““ài‰²§ì%J,šÔy»ü$JH´0§’yQ
+[®ž² X)v,F£á“ù¦„-˜PÙtï
+Òáh.æ¾~|¹ 3±Ì,Vb¬ èûˆ¢b¥òÁ:JŒCãà_\JóùÄ)m]éŠóh¾AÀ^uÅÔà0y¯ÂÁW˜Æ%Zb@y›0 »@ÓA^3+šY·:è>˜ùœÝ)PÖ€ÿw?CC–Ö ’$}5ó‚ݵö0]æ³á"÷¯¢—Õ§Š‡ÙzÜ=b"ÌŒƒÊéìCpK½ÏP
+Cs¬®| KŒÇÝ5… Áè<áVU‹O'f¤h>g¹ B†ù=œ÷¦úb™]á?îÀ@žÃPËUÝtºµ xLÚÎù(^‚ÍtFLŒÚNL‡(ß”’bYÚ­qbò÷ŠÈ¢è',­\IàA„¼‡!9ön°ÙˆÃpC¬Ÿ®eí)“ši+$:@–ÿáeùÏÇWñž˜0k\
+b ŸÛ2_+Ø´€TˆóFšwT°K¥w¥A8gŽ‹ã@ª“îäe‰·’ìdè¸h ÝF¨{𖮦©‡ù¬ŠÛæá?榨‘Ë0È8†`ò75'®¹ImÝœàUè.J]§?™‹ #š<þe¨‘JŠzŒâÓ“oÌ l2åëÜ#ùLa±¿0S0eòoXBÆw ¶`@òÄI7OW€ TŽ–ëqƒŽH<§ÅÔ*Ñ ˆbkÒ±‡CÆJY–Pø»K¤°‰BÑ?'ñ$¸5¬š™¤¦æCí“ ãHšqŠ!:2ªa¿ |X¤ÍxËû¶ŠhlÉÒÀb¹^/¶²”Jà*”E6q
+7
+«Ò+‹ N(ú/€ðˆÔÿ™šÉ0p hwNƒ´­„¢WÚÇp=¨™@¤×:{ÞJ žšhXäMÔVl†ˆH<Wl…+¦‡óZ|A
+^«‰I á5”}NO¦Iÿärv BëH‹”°zqÖÉ¥+e â‘Y W›ë¤Éϲ½²Ÿ8çš² "U9“nR/ôQ. Ñëoƒ(Osʾ3ß/@;´HÎoÊoÉŒþ~¶r‹•‚:‰E+®8VA›uu¶ „@ÌåPb˜×©Ò¯*Íž7å?þXäÚ ÆVü4è,6åOµ_rZl%‡*6í'ÊË¡°clE0eÚÅ]î--EHÈÁFOÝ;³K±Ž˜Zü2†7Ÿ­ 3È‹”ò1Š”')Äñ;['[t©<銭š  T…1pò!M†ŒŽ'M2i³´­!†æq¶ºæúv•`œ&^e—…çýøÊú­âÉQ óÕøá°•Ž#ùBvs®Š¤oy`ÝýRƒšô—œãĬÒ4{Û$„)»™¬~ÞM&§à$Ü- T”ñâsž÷„‘ËV_‡Ž0h󛆕¸L%Yã]:Ž":sž•¬È~ÌÃñâ4ª‚gŸbÒÂȯR?Tºä:™¨ÇYÜÃÊŒýt::Úe²‰þšr¼ÄxÆêÈ_“##‘ü1ÆÏyÐ[æ­ãɯüŽHdü¶HÁ‚2|Jiú¼”ï°)ÉUî¼ø,•j Îeä+)7ùB'~dÚ‘Q¦hc×ë˾!ëlKMÐ\€ c Ù"üùcMº\K»GÓõ²gEQS#ZN[6ú2 ®{ eÆÑ‹ñOòdÌs‘©ÌÝäSOŽ±!Vöv •RXH²¥…2ïáTBÝ•%:´rM°±× y*Q×Í3
+ë šæd‰_ßêy¤žÖa´<Ã\ùÅ­8 ôZb¿/±Å·âù3LÀv=/ÞD7øœ¤[±ð€’vžÞ‚htÚ°„³*ò¢F¬BWÁ&ÖÓ¶z >•­9š¶Î¤xÝâN‡J³\¢M#Q>à`K”»Ç*fî'9R‰­hifa)¿F•QbhÃgÛ:8?•JY* 6™ÔÊ‚ƒAðY ‘³˜p:RO‘–ØŠ|ÅJ›–q` ÔJ!
+"B-PÚs¶ú‡áF¤²?‹óK±½+:Á½Ý¢i7"7¯N %hÙƒ IéM wã}̯;ß9‘üy­ArÅ• ‘IÈèëè× TèóA½éüzôæ-ÐÓvÏ÷{ t15•XÊ@Ì„]wCÿèb´ž‹ù
+åúÀ:}âd!eJ7ÊtÖÓ3»‘0™Î•*¡þ‘:Š÷L,œvæuåТ¾¸÷Ž#¢»Aþe¼Lrã<b(|‚ÜA'0jÖÎÒ·•sÿm¾G²©»~$†l-vM||Ã\|aà÷‰ tžÈü&¶’a›…Ó%O”¦ðy¶uð\¸ (>¬˜wÀÐÐm« ×@ 9c'Þ÷˪{•<<`–ÍÚ2D3Ö'ÑêzVaN3r“Yª^
+†a
+«¸ðLýlóRÒͼÑ5ÚâkmqŠÒÿ“dLl׺¾Ú$K€Ø|í4‰‰Ë4n‰Ãà
+¹jæ/ñôÎ[Ê/XÞT-Qyƒ?ø‹£,¾i4 wìÄ´ÇžØ.E‰xnÃR¹FIÅ5ˆd;–\iTlŽì¢\^„K"Ph`÷Û‹è5GNü7¯Vš 䳓“Å;d}~Žƒg‚-yV–¶‘ïfÓ€Aô¿ÇMÍ¢3ÅJŽ™ãâìÄÐ6‡ªuׂú·(Ù4¥ÀN¤]§tVBÒO£¾•퀊ºhÆ­È8t¶ ÷p«(µô–vh8¿ÂUòZûrÞ0 ‚?@î9q‘–ÍTDäVB;õœ¤ÛXFâøD !É(“ZDh@<g;þp;œNüÃqöÕÏ«s@Wxγñ å¼`éÆ:˸[.
+>fé–bt½±'Væ¢Ø)¥ªÜÊ¡ "®°œ%DÃÁŸ­Þ‹à‘D|å ŸÖy‰¢·’·(z-2ÊüÈ«xÉbØÑ\DDQD,"s^¹”´¬‚‹!”üuõÄS28Ù%éMÖùfÃJ¾êâχ[}+ú}µÎaùºHútŒn9é­öôxe ù–]-½JI9t•3wLkC·9ÒÅÜ ÃÏÎR)¥æeäã,Ä¢|!Õ¦ 3>%bpà­$ÜÒwý¸,Óuz<‡Ž;-9S¨ê‰ÍÎBû #ltžø/:QÞž ¨:¨K¾㬔§é<0ÇÚõuÕ†)’j8µ¬‹màaŠÌhj§¥`4§ˆÓ–aÉ­Ø€,†[J8–ãਇ—ภ÷
+Â&¢OòO,»wìÐ3Ö'K·Ãˆ¬á®›m³››WE¾ÂZpŒM'ÁIy(yóµïEDDl(ÅÛapát‰{3ZÊS—Káö¡rU>¥Ñ°Z‹¸¼/1|+bGÊo9’Жåv÷N)çÀ‡9Õ’…­¼¨<~!%]­¾UûaÔÁ½™3E!˜6²ä%XOÔŸ»¡­Ž¿ìÀé6©AüŒ-#„ˆgÂ}
+dÕ7×DaŽ@Ë$q4ä86²9ÈŠqÊ£*!«me’±²ˆPx„|í«–Ï¡÷gz±‡õ_¤á¥è÷SQYJ‚PEÜ꽄åÕEJí±Ä*=¯ŸÚªà£qìãóêIv9ˆvî¡þ'šs*ˆo­o¾3˽b ˆ8tžŽ Úœ½ÐNø;°cB±òxž
+ C
+
+£¥tI/àY/A›-FþÙK˜g$:AÍ-ì R/OÏ„)«¨ñ@h‰­ªœ¥rfRæ¤dM!c3slUÛáý4+WHšAŽ0Jh—Â#†sÇVL=’Åf‰¢¼ikN²fÍÖÁ¢iMQß±Kx·/3¤å‡¹ôÒ¦>{0T%I0Wê%–bæ û:ºøá=JÜg¯+wå´D?Q‚žÂ%ÊH’b+± k>/EïñBÇAëái(«ž"%EZ'ª´ÍXRíÜŠ¬^¢‹‹ñäàýyL"l»Z[NŒKc”ö¹ùæÆÌ(P]Ñ+Ú G…˜—¼KÇ¡Î>x(ùŠQ¡¹¬ ¾2¼Xiŵ,}¥{ 3`P+“;s¡xãE ¦ZG³9‡ŒS
+UV‡Ôœ> Fx (Áǘúe§'uØ\ï1Rð?¥ÀÀ0}X2#>ÐƧ\•-d»ØJ~Ž|µw:Ž§žŠ_«ÅN¢y ¬’¯Î+4°ÈW=~€ 
+RÍC ÁpțɀîôZÄX‚°)¿1|ÁŸKbäNÉ’M’'¿L‚ÓfÊŽE~‹1!Üt+Ù…ñR “Âû: -EÊI!]^>í}E3{â€øËvVùT(ª:Õd¾ŽwbfpuDé¨A”¨ýèa´:DË@ó¨™.”`‘è›0vâkc¢sy7AFÞð%• À'RdL0
+ ƒx[<ž ÕTŽ)Nð¤4xi‹"F”H¡Ø;ÕqÕ* òF¨=è Fes…<~]ññ¿ŠØn{8!›rêÌ¢´„œè) 6¥ÑLV&ó—AB/g¦ÙÊð_$“°\YÎÿÝÄ:¦ai"˜ËÇè¬Î#•”´ŒŠIÐd4”=ó¬Ê^ËJð CtµÝêø’"ËÂØ’åÓð6Ù£ÐZ)NEú*9‰žyÀè¥"«­j%ÈD‹7üpŸ†•ŽzÅN»”8Ÿ7FT1;IXñKáCG@ÚÃdYdd—˜Â*DÌ׆!71¶×#ᤅýW€
+H‰Œ—AŽd¹ DOPwÈõ
+ê„25¢w!4Ÿ ÒE»vŸsʹhN¡:ÔÏEÝ%¼ ’^ xÊЩfÍ÷U£îO>6¤ ?#Mk âéc¦ŒvAzS®>ÎUÑr´dµå% Oiº¯JSë¦ÉëO4*Ñ:±ñ¶¾éM[Z¡v ­;×tò¬w Á'ÃB¬Æ8Õ¬ú’ i1ý´ dÓšöéùëœF9[ã­;ËSÚ$”“Û7„šµÞ{Uì<=“¼q|ŸctD39/÷¡¡.|—A~Ŧñýò„%©üYo û&‡4ÚYÜ œs4#ù1t×ScXVÜrƒ4(¤Ýäâ׃”†µë10cö”‹¢_›a<á‘ìÍ÷9ÔN'ÕKåW â;é*ϨG y qõ²ýýð4¯H‡lÚÈ3O¶ž½ºcz˜ ¯&³±"1Æzø©$xíüÐ{o âÍé2:ñœ¢ÒxÍ ±sÕð Y片î [½ »Fðï¦?ù–ÏÌÛ›ï½+RÎ%¹çš‰Ÿ|ê­ÉNË×HúSiI9HMܾ¨ÓsðŽæÒŠf_Å—6g“âÙ³á:-sSÔä2h5û†dDBi§±vLe iÃÚÄÅM³]l¸ìSáÝ,{úì<€FÛ¼ÓÖú( ÈÌQ*àHŸ©± KZ.µȨ"sùDÎUƒ”êl„c'àÁ¨µöbÍ’^X} OÆHðbžmÜêˆ!øPR—ô¾Ñ{¢ºÈuòJ{ü FŸ†rÏä÷1¿]ö¹@E5FƒÐó6h}Ecn«œ_ž®HS•sÜÞ¤ÐJãa\Oïí¤Ð év(ÆÌ{½˜£5÷` ê2™&h©]9×|™y²ƒ:TãÓÎU½TovŠÂÉ©IÖ±Žá•­”Žm" £)‘¹ïAºÈC8o˜Î µ36™ó¶cx@«x![«ïÈ.:Çtë2º¢z4Cž«\™a“jrãb…>gsdÚR¿ *åƒ(¼ÞõT3û
+&8|lâlC1c•
+q"åÎ1 ¯@çSLÍÇo€@K’oBO8_AH£¸U‚3Û §Zx¢CÎœ¾-jŸœi턼{_Î#o ?÷U¨‚éÚõÝ9Lë¹™v P«7)ˆrvÝðu=•—ï›x´•?Ã_]º_"Åð É;ƒÑK· Ö£ûÌLø æ†|-'FŽFù]»èÕDjºÁþæ×â¸Ƚy\G]÷kšñÀj÷Íd‚¡ñ1*×¢4<¿ñ_½IGO¢®¶?á@‘ªI]Ç,â :.[’e8yi¬ùéLLŸÇûÉÒV\Çœ›ðtÝÄœlNøÉÄOâg˜âëœ$>Åì¯aœŒºGF7rõ=º„ôßüóCÿ×Çÿù€<Ï
+ƒ´¡á½jÉ@¡ÞÕ,þkƒÈ®`%—Ƥ;:“‰YR÷¹@^ºÒ‘®ß'a¬-„ÄK
+§xÑôU¸º SŒ”I§§„hGáŒo ??
+5ÀQ<´àþœ,[L–1×U§aª–1LL.ãea|é¾j>‹®AJ ¹®JÀC‘-’ôîþ”5}ÛÅ×h8ÄBÀ±Ñï_ÕäY¢€1•ÄAݤÓ*]„…`ub"—’ Mî?Ó½Üiµ3–¡4
+?
+î.DµzS»äyõ¨e‡é¯¤ëÿP9†×žÇ•$Žæ(«6Ö1P„aÜËžX¨½ÊÁÅÙÁüMÜM_ÚRUª™Eº|B¬7˜›jG}D;”é°ø6ˆÛØìZí hÙâÄ ¨Þ^› \föô¥k« ;y›ã¨gðÌ{zÛÁÎUlº K0nÏqò>ËÙ)8; Ù()[ßãÌzÁuè9‘ÊPÆwåïs—¦Õ§wgõøwA_%ùó;ºý'Jßÿ
+Nh–ÉÍ=JI
+Ju+þ¨_&Q[÷„°˜ ÇŸKäÔ¿"¦Ç‚ÃÆ䣼®LD"Uî—A·®E{D剮RÁ!r@ ùWQ9®M’ ƒ¾õ3"|©@ ZÄ˺أã~ꉂS,¨ù´>Qïº×;·b¥ù{{@bqñ)OÐ9ÊVY솤®u ¤NØzeN€¦ =„.€œGÉÀÃFxÎ~§í”%G'²p7 ö/}¤—Šî”Ž­S
+Þº>;gqAíµw…ZRW®Ì”î3eZB„,\z°~zÉwíQšr­!Øó2D "ÃÆï¶+@0ÔCÛƒ·hç¸'ÞŠ±€Ïs¶Å¼oïèóì N¬lÂÜåXI-ŠW•ÞçæYÛSXje»ÔY[Ö3b$õ›jj¶È=óª1¨V(6Î/Þù;±rs;ÊÐOÓ¤xr€åi{Ûù
+cÒþ1\_xRÞúT{±_Ÿ°;v©%X–§¥-ÑÇ‹]ˆÔîÕüƒ¸æ·ÿóÀÁÞ4vqK±®Ó8š±d º , n–ø,Í´F
+Ô’6öf÷PÞ„5ÓÃß ÆG ·ç`þ'ãÀë[ñ$Æèü• ¡D–•{÷vËŸx8«k}¿ƒLâØN±3÷a¯ƒÉ‡O˜þ @Ÿ¢3ŒÆ˜œ½ ¸¸†%hÛ)ƒâ±7 XÞIt|BÛ„Zi7ŠL¥é•§¿³ê›L³ P¡Rµ.¿õ&ˆ^™Ç¡$ÌäwÑú¤‘¡ÞO ?dí<ê‚ÑTÁD²£Ž2eK™òšèjTÍTTƒl®R#®Ø›Î¼S1¶b4kŪcÓv$­ý×@ «JÉúÀ„(Ç“ üe’5ôɆ~b€Xî²L¤‡úu'µ3ŽÜß Ž‚¡‹Rï8c† _ëôê&ã¨7×ã¸!åÙØÖkq5†b+àìkæ ÅOðß^Fø ÷ˆÍÏVi¶«`Þ¦òa)¡)bÄ(7—1#ÿå•Îì»Å¤…Çtž´Õ‡–g÷'ÁiD
+K€Ï²'ÝÕh çVQbÖì-˜vé L@Ô[„­ÁVÐô.?´üQ¿¬Ö·îÐ"ÒÉtQðÀf~HŒ'¢˜{D^,Ä'45%?‡BAÐÎÐPÏ!f¼̩€âF`XFC™N?
+îËÍ(&*ÉøuŽÁ›.Ušý.çrIBÅß·-¼ ¨Ç”Ää,„«) ²ãpšˆáTBEz¾SÎß ' 
+sÀ $ ¢Åç%Ú““}9_è&¤%võEï»ô»¼ÔàR|DäØ‚Zk+Â|Wço›ô=)Qr••‘ôFT9Š
+LÉ;ÄáL>hOzõKÏÿ\$ §+Èä怪Îtà£0]Èu­çž ™b’.r”K ALd7HmÍ$ó×)+¦~úlÀ$ jÊrþ®†™ #ÞDÑ@X\÷*Ls½X(LÛéÒVc°bØ€d^!
+öp
+ÃÐdÆ/eo:÷‡[¾%QæqEKESn&…[B û(dPì©|¦¿u{H-åŠVŒ•ø.0¶Ìi›!b
+Öª‘ÁÝôËh1óì IsËþfTŠÇ|YߥFᩧì“‚ª1Îþ•súbÁFž¯ÂSºø•%[àQEH—Ûâ74Áìû,8š¡ªúÑÓýVJ%ºŒí“7œ‚Õƒ“â61<Ñ-(u9È»g{‚vìyÜôã;}ó«c¥ä(
+™Á^\Gg‚&j/®þ
+}<›%£ô‚ªôÅE˜ã2Pß è#ñ"|
+G®!!Â5,-þ…ÛV!hvÛ
+‰ÖDç©'¡-Õ ì•'{V·'{nM
+v˜É€²ðs:MõfGA;ƒƒ©:&µ„î¨i)­K¬’m>
+1…*oT®“pj5H‹þ=ÜX`à`â¥öøÆÆoGcER¨ñºúûk†£Áênu=A–ì\׎o'Pú•ôkã­?d¬ÛWÇ^ìè˜ÄQçÂí+î&ª3‘3´8¦N5q?ýFjÄaj{U‘WŒ`¥æ²¥¤‘ÐBmíÃ[ƒ˸ák©(ÇïÀy¼îʬUKÜÆÔcžßd\ µl™Z5=ûUH‚Ø%Á3Žb™Ò™Ì}fP‘¿ÁÒOM5äó`²Ù‚РǦ
+Q²¹!ØV“©¯54AÒ"o‹Z˜7ôâ ©Rc¶só?IÇ ü)‘Ð’b· ÆÄø¹àL]l}äâÎX@ÈŠ²$TuHó(Çž#$„ML¤‰¥)u/¡¦¯/–E½Ü¼J4‡–eÿJ‡XSäíšš²¤Ê<µê‰ò ZUÑò
+7kt•ó9áã03¦šã‰jV–܉)ï‡'å£Ë=ý ´gf 4°žã°Ár«ъ´öË[ûpa5ölSn ¶ðw»8ìíŽÕçg OÐȶÂM£ÈE~™·1Û×Ó‹–,öfZó^!(2Aࢂh­aÖxÿî&·âÝÒ‚y€ìédåÊmi_ÓwàÖZ Œ$Ž38š¼Z‹í¼6µ§) Ïïq_ ªÏ¼–~p”ÿd,†¶á1ôU+›aþÞÔ™áGQÚLGKµ ©ò‹ô—½®‰ù鈰ÇóÒ 3ž“\å¾~Ap»9·  Ru¸òtnSåű¯Œo‡Ê©5 PTËy
+‹¦Ù£}4/)ypè;CñÁ¤o2ƒõ pg¹½k ÔI˜¿ª=œŽBéR̨‡5»'‚**„LÞìÅqh´f)‹ÕÂìµnŸ äb¯7ZÛÉ•_Áb–ôbý fÕÌ{)‹ƒžòúñÎýƶýó_üû?ÿóG&ð@b«‡¨k©Çùzîpá~AÙå»hœ¨ç‡CØ@Kî/ºHµeZ)F±;ĆøCèÐ|9Ä ‹„…å%N 4q'ÔCû.läJÊŒ$ÎêGUšFí!˜Œb@ <˘m7‹C$3ÖèZ™ƒè¨ú)Ie&pô}¾ø–‡0Šà”Q¯Ûà
+ñsûï± <iŠÒ˜ö`šÚ~Ü'†áfo3²ò|Míó£&õ«€µzì^Fœ¶xýØNŠ;ÄÆ\ëùðð;d?üVÀçwmxÞæÑÎû«v;ï´@4XïRäQÏ D€³
+F™/ \ž¶IåÖÅœß0YüONzQSŽ¶túÞÏ ”'‚’ª)Ú
+‚»›ŸÎ®ŽôZ°¶2´qUMˆ<ŠÛ£­ØQ©•žWÇ™‚ßî±= m¢Ú¸å%hŒ2æ;Ѹ~Ȩ‚ wðv…°cÖu‚ȱci–ŒØé;0‘×àS¡ùåÞXâÇ@rñ_ÆËäHŽ\ ¢|(AÖ
+š“JØ|™ñtº9‡GfÔë›m~ •Šóq¥²È@´bò¢¶Kؘ`#ÐÌåÕ<œ`(è´¯ážÅ‚•)ÀƒF1Jì2vxŠm²¹vÞ g2|œÃÔWå‚LòI5 ‰jæRvÞ•ÏÀT±î×徬
+ƒ˜¯/å^öR2 qLÆÊ7 Ê'™ÁÊÔ×Ô \äV+>ì³ãºJjf^>’ œP‡¢âž %9%$¨ƒÿqÐ6ßÌ$+‘w;e ¶C-Ëhç îeÀ×ÓI¡a3’ò@³8‡`j¢k÷éˆ# ÚrÄÉ!6!è/g™Åj,ecPâ,Ož”lw„Êh¯‚)ò Òo'†`‰ÑŸý
+ìy7´øŽ„b`©!¨ 1ݤÃ-£þÙÄŸr¬ŸNÙ!|guJ¹Ýd¼ÒLò(<úºÉã=ã…™
+@$‡cY8U|ÛØÖâYÜä­Iþ¥'ˆÍÁý`^yÛÃeæ‹­48…§öv|Ó|‰\Šª’Ü&*3weˆÀ6 <Ê;_Ø"ù)š›ŽMº!üC÷^Ž¹ÏËá.÷¹;¾é>¿Úœ¶àVà¿nÓϽtôÛe;ìx,]#ÑÓiý<@‡˜ñü܉.þz'ñEVÈ,25cëVyô÷”q¾ i˜Ø*gÊøR,)>‚j‡3»¬:01²X;†‚\Ÿ\€a)¾brµH¿f-»CÅç$Ë8Äœk| gŒUÇÄŒëQDÁ•ãʆÈÑ` P¦1}Š\ÐäYZk©¬ã9D$‡»ŒÔb×m”8e„åï‹rŒ®³äEr¼ªcÝ ksÐ'hë(J‚ˆ²½Þ4¢Ì*½ËDŒ$]t«ËögÅ(]™V\ªÙŽêöxúI%%<skÄó6÷~Þå_ºƒ¹Ü!Î}ÚiI"#mJGÈ›¹üõIÖЧ=ôØk²—)·Õ“Wý}Æ@_¹ð AGÓ¡ôW©‚\4ƒî‘:Œ<m#ºB­Hnþç ec’…z±š|a'ZO¬ÛÉö&éÜ×~NìQM¢¾y´²Õ?Eçûî¼[Y ÇÂÌ)q!-ÇsšR®Ò.p\¾Ú†fàGŸxCzpXüQ ³¢iur[1ñMó”1¸Õ-ÔËú«*0¦ ?äò]Ð˾Ž ¦ïÚsrR ;•'ž0šøøH¤„?cd¼º4Z*‡¬ì5²[Д˜B\Ú‹ú ÊL Ùb7a‰Ì­píıá´A
+i¢,¾ænÂdvpÞ®.ãpÛÌÞ!g2<œSaa'”É’•H†i÷‹LJ¯¦ì%.Ì•l9¸^Cˆ¯Æ\2ˆ5ÎÉÌÉÿØôsôÑÆyW@L¹$áðɆ4ëUüÉE
+ªPCã]C”Tæ‚ÚìЪw×…1áa%6+pÉâÖWf(gVl[å¬\T´þ*œùˆPï o$šû m€tclqÒh^Ú&»@ƒÙ°KŒYÔ ’»¼YZ%
+1^žeÊvR0}`8±lcZˆ9'¾„µ–Ïç<BftöGæÂ/D ³Ò¯¥©²ˆHZ·B ¹hÇw†UhsóC #Ò‡r5†ß/óÐüæTùÌ°¿¾¡WÿÙ9DªSTֲϫDŒÊ1Xgs;—Ø7¨1[ïvÂÐç¡Õ¤›ØYV9™/Ebè*%Kòý— “¦CW,©O|zqy*”ÄE—âÚ F£ø9d(4QŠÓ†ñÏÑ©º_¦ã‹( žÁ†ÊÅíæcø¥}1º¥„WÎCçwÌ—ñ2qnñg½cúÓ…`Vfõë$<{_៶!Ä2vXdé
+„WW3†ÚÂuï¡`
+çPF9B¨
+«Ä°ÈÜgE‘YÎÅŽv|ØÜç°«Xµ!;òg‹åmøÅ=Bò¦‰1ó³âSOJÌú1@­ìs†2S(øµY" ÙìÏÞã#Qµ{5Ò4þù#ÊG×x)›ÜkÓâM¦ÀvãÄÓÞÞ¡ÇMé>6L Q†•í×§Þ Ÿøâ ôÏV½ª5˜¡`Œ螃œãþ¬¼tå?žµÐttqä)•·Ä¿”|j×$S°:2Ü1Äšéï:¾µý8pœ òŒ |9|Ø;½Ä±÷r¥¿7ˆ· )Ï”ã9jÅà ÎЕm,Çn
+Èt7;!T¿Ä$ã€pq›¹5:øloZù
+!OtŨ©AÉhÉÚ:œ5í´Š…%%Ä£ð7Óôöéˆp4Ɔ'ˆ¿¢8Dʶ¥mÎ6&S‘§FœEšàRß{‚¨S2m­±Å[N $?ëã"ì*ûeÂJC\è÷Úðd›Ñƒµ™<ø ‹!-§²vX”q‘ÿ›„ø} {~ü«bÉaµ<HŒè„·m¯2Æk²!M–”¿–°@ÏTË´2–“Â;}Ä"|¸ €ƒžŸûhûÐ$ÏÎ2#:_@lMÚB„N
+Âê(~äHBX ¤€cö{ÙÁýàÚÉlüÚ_„t€w]‚²ZAÈ¥Š[&‡L`&•‘\/ãøÿîRn6=)Ñ8PJÈf@&b u<f¾²²i͆弾„²Zev¦rúP‹O.úvÝ/wÜÈr$Š®@{ÝF‚ þÍÚwVPhO2{ÿs., óñ¡h4ªTWd|îgÑØ+ˆ*¸ñ9ôï4& ðµçNTˆ<áÓJ<>üâ h Fz™Î|ƒb¥:¼° •PÊñ;å"Ø$ˆ/!þ”Ì“YéÞĦ½nñE¥2­
+óÝ/ÓôUJœ%†lÒHoƒñzØ— =ú+hm{~h›d
+ÇýÜ€¨+| ã|
+Òä 7BZôå#ÁBÐÅ ©¨$‚±È‘òÒª_ó¨"R—ô#° ”°KÌ1†%®ï$Þ®²êtwjÃ=àÍÑ'„vs“€ítRD–,s™hGHIÍí#šq“ó)Ï%b†¶²SÈ.Æ)ÈéÁëšL_ÛN­rf]µ%?H¿`™ÏæævnØü,/Ëwì1–óe›ºC°Ð‰)r'ƾˌÐSäDG@Ïæ‰ÛÄPä[±å(“z7É/$Ü^õ,·*²ÆwÁ¥~’m Ì^Äa!fR¸ˆ¿š‰dNŠg¡áâ |›îÅ~üNÁöH1ñFqR²n“zÎð‘°ƒàÏpú˜c½ÞT-eÜ©¨fTQ8É`lciC_2×A˜{™W¶íaŸöü ‘ãÖ SÏ<)å²0¯ ï/”ùcf“o•þÄ•C6ï¸HŠÁ*?ªÆ,J0_?@Vq`Tˆ%_;~úŽüM/%°3[ªÙ_^ÏûgDÊHQSÇ™€`‡ê¬£08%ˆž¸øŽ,bé[“Ó•ñ’r¦§Õé’ðóù1µ‚̇®™ÏèôTäyÓ…-îŠß#RÁ„Üýn0úbŽÝ¼G Gi~Ï(ä“":Ë3Ü7FX~l˜‚æ}àzr eó‰‘rŠÂL‰ë65—¡Í§ç;2¤!j†O)_„¤bdm<±‘ê¢,,¬Ì$BáFØ—²!ìviê‚wÁÔÊúUOCF)éÿy/æH¾h \¡Öá™8 »Ò§ZœtÞÉ™!)l溂ŒŠÀqxJuÝøe;a*æ-Y?,¹ü49)‘¦úI/T!Cé{­Öçp®ÆâB\'ƒÂp‘A{Rˆ'F”bjq—u{©3›yÇD°¸¬Û~6NO)YiÕ'}ÎF‡%Ù/gÉ—Ø7«‹ø% AÙÍ?Å>‰$G›~‰Š».îÏìó·5†«££Êu^¿À‚™ ¦bj•4Gæ’Ä‘ÜUðH ÉýBYÔýHG—ï¢8µŠqØ‘ ïÐœõ¡Äœ,°î „á¤Ý°³; (Ö*Gþ—_`ààÞToû‰Çç÷ä6œO(û¢Ô[6„
+Ê·‰
+5 : fã“-kG~Ïõów0š<öæQ£o¤Ác²ÝÁúÍ£ Hnå¡-¥cqd{qŒ•d÷«^J|‚<µÊº€
+Wö3Ê?ŸoSáKÌMì<¿ª©NÒI‹~Òµ8H!AdVõ®f
+)ôLâºiÔdĵá—ï\¦æé*§Ñ{}Ïi‚_ÊrÚƒ—âþqŸ´Ä¢‡”›a>¥P¶.`¥’Î/ÓŒ|Œ†:ŠãÝú‡zÎ: ã‚~#¦p™ŽC?¤`ü²fà{]IRA(®pJLؤ* ‹ { GµV΂È) µ='Í1Å@|e“Ü
+¢p;ñ”2†xU JF‘øyAÍÁ!²°ŸQŒI êy#8-–Šn΃šdi\Çã¯
+MçJ!¦³¢ýØؼ qȈ"Öy¬âô5Æ2žu›4ô×Ëí/4€8SS‚×íãEÙf¢ŽÓ¬¿±Yÿû×ÐqXºæ„ ô¤…UÄæ'¸ŸÎk+ì„ã?õáAJ<¥‹§$N‚¬«"X‰î1åUË7BßY‘¦èt@üš'q8îˆ~ŒÖn>ŸSTm±ßFÔ\àHlÝ¥È
+Özú¡¦2w]¾ûëã
+¹×uså·ŽF‹«Œíû9ad ’€)Ë
++°!]N·¢ ¤<v›òí> 뢂V$|Y½ˆd(WKSÑŒ_œ_iH6³GA’K®B.UìÊ(@’6,ïf†'ˆ‚•†rß’o¾Ãx³jÙvûdÀ*Bò&‚¦C}6÷Å:€aKs5® l#´'®1<,H¨Øqòغi ÈHÏVO5Pä5™u›*ë­™o3¯·Ie¿œSPô=ìÙ ¢a }¡-môtf<mUÑì×Ó¥$ö7Ú²º #ËúP”µ\—ï\~½ÍepN¯º àµ:‡I)ññÔ©_oì‹6+ÌÍŠŒM­ø¶&û¾~îç„7ÆçHZƒt¥Á9ôј>ón”†&§ÎÅsž>p¡p2/ÞÝ`¤ c¿ê¾9~„¦ÒùXê ~ΡüûAAPUÒ^GŠº6þ@uH:NÃð‚(zºùÞN$Šü2¨Ž•‰
+1^ãWªtÑ…홟‹è2ÃBÏ˘ËkƒÙ9X§ ‚ÀæxDmÝk3äûé¹|È‚
+¹‚†E¯0ˆW¦_¹n¤ YU8ÅùÃn.?â)ü6nlI6æŸc¸ˆãŽ ƒaV ÊJJö.fèË .i(Žy=ìÔxÀÁ¬D$CÎQ=ð“ÞÁ i-@d‚GPðœË¥f(C¬Üå1ÔOž‘½_Ú™ ‰.q;àç;«M “ùšžjýôùfI>
+2c”¹å„ðwŠ5I- ë(È¡eÔç| Ò™­J>ÁÐÍ0Gl˜}^1ïKñ‚2^Ÿlp€<YÙUa~4oqû]œšBKÍßdMÆ]šQàºX¯!èÄjZn%ãE$˜¥¶XÀêq5”4úQpcÓ ¾lŸ¤ )fRÇ!ƒ…PtÃwÒ“ˆÃÚhGR”®¬%Æ ¢¾ø"l¶ <Xî}`–ü21Ejœ„Ÿ /úõq
+¡è$ðñq˜É­Ì—œ¡9 hœ³`þ8/‹ZŒ ¦Øf˜¢{Nßbš*ŸE…š
+s¡Cc–$åZñ%[–éM²P%ÍOÕ‡€KÔ¬ò]›½iUl⓪öUYú]å÷¬‹À¸%ì­_‚IH‚wXã¼]üÓ·W$&±I ;‘|»²ÜœÆq˜*ïé”DY_ÃÔÝåK e.a0ÇFùræp5¨Î<ïFwâA
+èWî9Ú™”±¯û2À*`ÕFù÷ü€T”Ít˜²ÆÒsƒT7J+þ=‰l~ÛL×e¢£è`}ê²1”ô¤¬§6cË¼Í È
+ (è_ëyÝáKÑZO zYß·Á¬Ë<ɯW±MóÜæð’äŒ3´gýô¸±Í¨?îhø3®L¯Ìsø-0Þ>Bs‚¦7Î[`U ü¡ §{ÈÁ&’ÛXÞ\·Ô1ý”¥…Q0¡p£|1N`ƒIKZôƒQˆõÌ
+ŽÏ‚@hi1 ȤÇðÛΆ3öŠãFuÉ‚ÍCù‚"c¡7¼uÐÓ Ñ}@sœ^‘xEz&L)錪Ӏ\·jäcÍ[ÚÚ+ô;cÍs–8ípÃúOõ ‘Ò¨ÛÒ$,áA‹¿ë2Oö>ÈçT6~ýŸ•tmpI^ófÿ3‹µ8°†ñ‰èËìó#³L¦×‘†ÜÌ81XZc%ÛìÙC…È
+‰Ü
+2Í ÆòzTÙpÆ#¯x€Eq•¶çóò*teÆâx„¿øÀ|q¤5Qf]Ÿ‚­rˆoHð™ŠwÑÊÀ(_ã×Í…—r1´MÎ¥(hAšŒÀ\kSo«h.NÏ‹ŒÄ±c¸AzMƒlVT™ت?‰ç¥äŸù%™¦Š¨ô£'·c=謓?× ‹ =@ eëÀ²Ÿ%f}ITŒ*.7v+’H ––x$ 0—/ ‰…ìZ€öÛ"1g¥(hm „¯EѺ“_t»x
+ŠTHt¢ƒÀ`,WTTUÉ
+7U9!÷èÞ†ñÁÈZ²ñæUDÜ&ì°Ì =·æP‘g¸’Æ·Å^É˨ö—nE O/ÿ
+|¾Ü»@b<3+f&÷IU/3•0—ýkgˆ-Öép=e5ïÚ_J8£kü•Ý9]J$®ó¾sGÍí6wôÝå¸Êï JBûòWÄ)¤˜áõç,^,¹q¸‡Ö41$øîH±O–·‡ï»}ÿæFOî:¤' ÿß'c^}Ö[gDËø(Æõ5+ ^²PâiUt4 1ŠØï8KÐfÄ›YåYp ,(v“=L3WPÄëaTðx“UáP`²NÁxÀ°H^µÝÈ$G@Ęj¤—(
+a|'#ßÙŽýM±•Í­óc….à˜¢.½õZÞKrr{ùµbuøÒÀû1·!\Ç&%í“ R‹ö§.  H&:QVZã¼Ã/¬,ÁxSR•á°‰±¦óª0âÃD<‰©vºÙ‡¾ã¾Šå€»¼"Ù$*E…צ«ãz }x/¸jc™­œzL)RÒ,!@p#*:8že]r«™AŠ<ËB|ƒ­ 4%sc %hKÖë[|'G¦ù/x2›{N{,ÿÔõ£Û$ƒ&íb^i’Zåƒüß`¾OAÆÍ‹û¤2™£¨ÊѯÏEq6r(«”hb´ü¶H.ŠÈÂIqeõœ{sÛ´÷r_éÉœä ˜SW‹²Ž†kù«yùâ>kÉš{•®ídÁÛ9©ðOt„0¸YJ¾Pv"ÎìJI`R
+3¹žG)1†þÍ/Ô.3dyw°ðÄ(Dúò– `
+È5ö¦"Ðs <YòþÒ‡Ýz
+´Nyð_‰ŸùwâƤ©o·(?€P"%‰4oM­ÖRÛf¨¬Ä%M²³„ÐardÄœ¾J¬ŠÁ_k}GéÚò?ª¨°.q
+_¢©ë©EäsÕV w /‡ã\ãb©å`¥q^ÉÞT¹š(¯+;ç
+—mÅ«ü`ÿŠˆƒ¿ ú’Z#Ë_ãž`ä.½+-ižÓå)å2¹‡ÕdºyB°´^Ü 1d»„à|²âj2_1¼ |×’5O^ vêÌ=‡‘ãVX‹\í|ª´¾Áv¼Ä‚ô
+2L9Ac!”©À3Uïf@37Å5T¤Q•F6™´j¬3Nñí¹­äyŒ©,X'¸©m ‘ÅY‡ZuJ]4z²#YWÁ0œŒ¯” ¢Žs¢:'1^Cú1+®Q”êÇW B&F]‚C†Õ@±úáSò’©}Õ!¢e)\ÞfíÃÿ}$m-cßt”z¿dæ ¶SöE÷ØÐü°“ ‹/+|÷#1-:DxŒìfê&YWÝ3îœs¯çknåç½é¨a@T Žðù@Ŕ鵹?(^²ôçj» Ò
+
+2 Æ%¬`&>Ä;&­“•5 ñƒ(©S‡¼øÉë:0bŽ”_î“Ô«™U†¡ñ4’ö1šU#sΧ»èûÜÍסŠó ˜JM{f2uµ‰PŠѲ±Pø~‹2°ra!Hp6ȶkølæÈÚ#瞨†D¢Ð²\i(Gò5¯Ê:–nÿÇ…û9A˜&b϶¿Æê\C`\Zízª5ÓÞ³Á;û Oó3ži4¤àƒr{`»Säd_·ÁVYæ^ž%÷æL¦*jÊ*oç&¶ÂÁF·ºÏJ+<Â
+cƨ ~ÂÈúz§;Óñˆ€Qƒ¢uÇäHÄTc³‰aVì`5O
+W^þ‹Kh7ºˆç sq FšÖ¤áã’UZ{È~ºù"IJœé!ÝÅ%I8ƒIžm×Ú‰KŽèÄ%Wô:Ë‘dã±Bš="Ы
+Ç{ÔQ€gp¼Z¿…Ø“Üqé÷žùÁ%¤¸ ®_ø·?à’+zÅ%$¬kuˆ‡ZÉ.!ÁÁxª ,n¸¤(¡f¯+º.)!Í Æv­.æ8©?q ùbƒ«pöÏ¡ ™ÏA“¡÷âáö"½a¯ù‚ÀW\B3‰Sá‚ûW\2؃‡3`¨ ¦_qI’Îfé)ÅÇ%WtÇ%GtÇ%}rî’8ÑÅ%½œ(ðÅ‘þ>.)‚$‚š¯Ãš‹KêvJ5iŠq¼‡9®aÞqIukÒˆ”˜ùçP]Q8bv½â’ª”Gи”÷¨>.©Úµ@5…‹Kj™X»¾År²—œž:qé÷d¼³ôBîøÆ>Mµñʯb.³þëŠVÈŸðÄ\Va“\rDpH¦|tNoïºwDÊÓO²ö:aÁ3lB1u†ømÖòÀÉwZ¡ÔfÄ©)L’@[B3°Ü6s©m‚ŠÚâ
+7":”ð'5﹉Ÿ1a± Üü©IÏÉ®šçQ?£/¦‚SûC»Ù“|Í‚ôtã`\…xœ“k’$:å’˜ “FÈ!}8‰˜ßKðX#‰×4Oó‘ßSDwµ<x¥];Œ*Ph”ÀzEH ƒ€Áo®$ÃÂV˜ýåÂÚ£I¹\“áaªôáÃpájãòüŸºÑ"c+›L`<Ãèݶ%ÖŒA-2Ju_Å_ Gë;›‚Bâ@,Fçpj¤x¥í\EÖDÊ«æL>%·ýó#òŠ]0°/Ú2§ü¶ÈiÀóº·D7Sp{ë.úñÂ_`“À&Èn^Éb2¥c{ h¤tòU&¯uDË?¦Íí,Z)4c„°%Zº²RàJ¦]d†}Í›(J*‚÷9Ù'L
+ã º[»ƒ[y¿V🠋I'ãƒal ŒÖ×\Åì Lý‘<˜GdrÈX7ý
+ƒRN»o*$[‡þÃAJpeþKn ¬›³R ê1…ˆÙ6w¯rU£VùV ئ%5\E‘ û’$Ôdž´Cßa3EÂ¥ q”vð¤'5•&ÉQËÖM.ùÇS„´ $t¶ måiraWÛs‹8ÍÔTAìl%‚Ǫ“yW¹ûPÎT4coEµ$Ý2ÜÕ‚¤’Mùü6>@ÝQxÙ^<‘‡K°;3†C†v…>«Æ*‹¯À~ —~þs)p¸Ç¦ÍœWÞX«ª…ì° 8pf†‚8!ÇþÍ8îf¡RB""!êÉÀ¹mäç¹€¾L$ßðï`Ú8ÅRʧ-ÓM>6ˆØÅ+Á¿«nÁ™xj‚Èg¹y\¸œ1Õm}õmUѽÁ'§Ýɤ´F—êØ,ÖBÇÁ™ÐÈœÔ}ú2œ–Ìp f:$— °ÇUAð@FQ€îø ¯Óá9E!íGibBÓÔ». ·PmxV&;kö|wª@ƒAÒÃ#L3$pï»±¡ãÌŒ­qw†*¡À¸’ôˆšFÛ aí‡PÜJÔ±¶~ÔÝU*û%Fce™ÒæÃc}óæ¿£\C‘ÉM
+ݤÊK¤qz(°Bm+ŸQãKîÑ«¡$]²ÜÉ=èÆLòFð}Tч³“Ѩ7ò:‚”«+3‹8—¼+Z†«àʈ<ÔLªß•Ýw¹A(DgÁÉ-ã”°{“ÿÓÕŒgï 22teÚ
+Ùª¡eö"«BËs!%›“{§H}’–¡ 6þ´Ø…Á‡,Áæo·*ÿˆ°—ÍÜ‚¯ :—OŠâÿ~v„AD8êçX¨3W¥}BïKz9# ¢ä—x.6߆I:ÃÌJ+-ïg—» ižpI™¹œdB d"2ªtøVcuF§ö°&10¨2>§Ä"¾“›kw-ÌÄÐ8f6$PÃÜ
+
+¢©ä2ØWÇa@ŠÍZv
+*%ù°¯ƒ3/=*<w–x¡ * _IG™òBRÉÕjãÈJ¢²Ã5žÈ|PY.ŽƒP®.Å4ß*·¥6¦ðGà¾hà·Z*Ù…ù’>)
+I!-+SªQ¸éÇ¡¥E]™ÖÂVßv W¥rL„“,k™t%´M£&ŸoSÊP±Þ/=UåF¤Ê¤iä¶L&n¤¡m;SN—0@ä}C„>€QÉ(
+¡k¿Ä è5¶Àj¾G}4Vë ’‚‘¶ÔIyPê7ßΤü­3?ƒ^^˜‘^Fƒé € P¹ÁôvÉỆ«–¢CZ;[ý_Qy.|G ìI™e‰‰)²?¬#hÌaj€ð
+&‹äÄÿÞ1U6CŠKlõ ?3deÊ%ýU¥$äå4}õð{úcƒzÇï«~ìd*WIÉN þL.w`ñìB<̪Pb/ÿààuÉ˳B¼\.4Qíü2é„’Ykj¿êØÅÔʃe‡@*œDuAû2Çì}g„ (ÑѬËȆû1H‡VŒ!ÜÒêôî%\“þ"Ú“. A`åëYrÖÄ —¤JÜuCò@)lSÁ‹^rs”Ž¯5iQ¤Ÿ•˜«’Öšƒö…„5n¼j{€`wE^ÊÊÚêš<—äB‘Qëü'À
+H‰l—½’$¹ „Ÿ@ïÐŽ þ
+É;ó ½½¾kFŠéÞÙíéÊ"A ‘H¦3,ö¯é§Ø#—E·°—E¶ÙóÿúbË5òì¯þ῾£önódÏ™éýÖ±¶Îl}NËy¶û˜/cÇÜ Œu_ìø†¨ |µ<¬|µ÷§uzëã°N¬áÄ>Íã,BÂù°f{y®1‡=ýDÅi<í¹€ô`™hÑ»â bQB¢Y&'ìmŒ±ÏY;<ë68™ò¾ŸL:³ egÎBÍIV×°³Ö©uV_Cˆ±c]„»Ö±íõÜú ž ¾ûÌ1ëùñßûøí±Žßh" •±g­ã§»¬{=¦*ëS¯"Ø„®X{£ÈCŸ{}ïÔm™â=kZ%„pÉ+ŸPÊ ëyQ,1›Õ:k“Êb´Ó“h‰Ù|GÔN©ú"âÝ)Ëþ°ŽñE$¹#µÚ)–yBÕƒc¶? ŠvYÀ×ÏN?QÇ|ÐmvruÞOÀI‘ÒZ÷¾s:MáJï&ßJ/﹘ÿQÓ†A’Mž-·u2¯sÜņBP>ª‘4éËÍG ±‘tgæ3UØ„xÑN,ìF6d<ŠÇwãصÁÜJ*+LN«wh°–™}
+ƺP›<Õ2æî…@# q|ŠåÚˆB8«"é!àöúÞéêµI½€Œu—!šùÌ Ô»ˆDÄåbKLÔÈsÜ%6ÜÓ6Ï÷¼‘†]ïIKƨÔ”9³Ï Ø;|äH+ZïÊ©Cr·8|Ó¢î°çHo¨´ÃéÉ
+ÃÑ;zt èÄ´ƒÞÙ—ª<V[†D‰v>ÁHõ¢8Ï€£‡FAC©»þº,ÑJ‘ˆåil2ž$T3‹·áP×Û¦ñÉÄkŽ)ã„® e]Ž
+ü
+ó†©U,ybPŽ‰­uBÃÿ"BQ ÁOEHªN¢¶¹zíIš¼$ÅŒÂBQ¥
+sQ5ÙÏ¡aBIÉ,ûȱÐØù Íx,ÞûCqKò!s2«€ÔËU@l–â¿¿y±ëiIF´zý³¡ãp`0DK¤ûíõÜ^®K½µ;؃ó4(Ç‘ªkªýDýí ¹ <¸ÖñŒ†´­"ÀPy0BóH9¹zHY–¾kþTÕ>!—É©#_—£7ò¹d@p‘!ˆÆJÝ™LFŽ…k]’Ó.¹ß§Æ0sG"‡…Éoˆ¯[Øà©naP,>¬£[“R—פIRÙ‡¾?‡îµ­?Q(ÃK|ÚS%Ä÷™ìòÃùé!ÔÏë¾Fzð– Ös)@û¬¬]"šþ˜ÕEKÓ5tþf–¢"Iˆ 4äžñÂÄ6z¾,>v,Ú”é¬Ö5íw_‡å¨g“Y„6Š}×g_OÈâ4°®“bð˜¬õ%ç tw`à CÉÑCî_´aé–Ãf¾Ù3c<,c­Ç¾cŒã0®xM?eÎ'4ÑUÂqQ°KaÐÞ½!”qúŽà¸{2œíÃ:TŸ:vYe×°—O'(XEæ8Ç„¦+ýŸ,Œ¼½ä
+í( \îØ$з5þÌt5Ë›r®‰Ì–M,¹„×”«Õ=¦ çÞ)Øxd%S‰œRLÔÅ_Z¢9KèÖ
+™ë^ë I6¿´í*†î¥ä»SvêkN>7×÷æÚÿ·Ó’gÅ×~Q¡t‚º«h¥X¸¦éL_=÷ |ÖAhÜõ,ç)íP3@5~ ¹BÌ2_wQªÛÑXÝ¡¸Êcèwå’@Ð0ôJ:ÏÐ\5%¸]Òbù¸½Òuäõl2UˆjÀu¨Âå¡ÇáL$Nz©‹­f8‚/éolJ¢ÝŒ4ÜuÜKЖ[˜(̾îÍqÐKC‚+B§¬£+Ý‹q ‘ºœ6ÃV‚Ç÷XÌ,ÝGÜuW ÅIGWyü.[I-ÖWWƒ¢%%U\Þä¾~ØY¯Wü<à÷zÀY< ˜û¾Á™vŒ/—V(¦‹Pl÷ Žf6žSëø©Û6„æìÔ,‹B°Àâ”bêVÇN_vÊuK“é
+uˆš]"ºsH¦)ÅJ´—Ca¨‚bÁWR{ý>ô…*‹ÇnH²‰ÝOï¹F¬¬Mˆ(tf—k#^pÈœ8
+åBpšó½S»D¼!W¼¹Y ¦¢.³.ÚÈ™ëzø_ÖË%׶«†¢]9Å ÁÍú ”@ ¢P
+I¤èDïÃÞç"Q • ºoû¬—==&ÏWÂcÑÙ;Þ7h÷Eí½Rà jUy6*ãy Fõ#ùf¡MåA®€z_ñ:¼xw^¢&üšb°—N“ CèP9ûbýÆ˱hØ%gKÏfo–!D/Ž;ñZ±Óñm@X„
+fÖ„EAT£æ-4™?¶­ÎT""šã­Ý0—F8„s§¸+Í}2#Dou|¨<åEÔö)#bxs’Vr#,Øq÷r̸ª„e9nF”륡fFncW•e—§U°fpW4ŠŸ ÇÌBÍ%ª»¡®_‹êä9[®z!ÂóiæÖŽx[Ï+Fyîº*–v)p õôðB´˜6“‹íœÈd£çètɧ’¨O7Ћ\W÷ãÔ} '†£X4þÚÔÓrRÊÓnü
+…"¦túýÔc‚¨ xUoÕNWa|“á(ÅR"Ô†šRX«Ïè,çTA³°×»Íªò;“.?!–ÿ ö"âG~¯E¿š´ý”bÌîˆÐq9ËçNN49ŠÄbÀwc?ô6•æÔ-×¥ñ†;7Ê*#Jug\ÑzŠžQϸáTà
+-h¹ó$ÖÑãÑ$‹¦X ‚žeåNÌÝŒ!×X­”Ï£©Ç+¤ù0­6eÌpÛ¼ñkh‘Ž6[pb¼Ðü@!ù
+Ü4¢´F•1sýæ ʬ€m÷Yã8"ºsŒÆäΈˋ‹Òç™2£P4ýG+õQ (RÁ„Æ–du”~;ï‘̾Nj×HÜG_Ó®A—]úøƳ‘Ú ÔóL¸<”ÃNv8å‚!‡ºK3§Ë#
+
+Íz&eØ6ºˆ5HLkª aÿÖÃ0à ú;íèf¥·“Ru‘
+,äÍB1i?*å¤o†qsêÍz.[õ(È3-ù5ÿ5KSY‘ð3˜…wù€mI]â1/T5x‚âÍ ÝHíÁ
+3‹Hoo9âNZKx åÂ÷‘Æ›0XXTÛ´»wmtûœŸOÂÞ> ˆH4|~ˆanMáÀü0¬11e@€FPÒãDíïŒP*ßëFÉð>ºñ‘}_Yfj€FÍ×»f9U»“ëNs h1­%ïO'r¤©Iàúšž&óþ`VxQF]Ç:gÆÕ~›w~O烨«wÃ*PøGH2ä@‰µ¤+îXUwS»Ñ‘Ê!5!} ëP˜éŽJ¸„êyùK®ª­Eº¸^«¡-8‡µïD‘ˆ²3ˆ¢ÃjDA»-VÌ!~¢
+D æ$´Jr+Òzm뇙M¥èÃ!óØóLçÏŽw%
+Ã22j¤—ÃfåN’¶ì¿ó,¶ '.W³JÄÍ;•æ j‰ÌňQ9éë•‘Ùëä'5ØAô‰5©zuý¸0ècZÛ0ڤ촠
+CÑ/jëƒ]é€y¾ß„¨Hcñm¼þÖzžøùrðs2™?Çb ¢8pµÜM¡¾“£Ï ñˆšæ5¢ìa£žexIIÙÏ9¯oÎ9Q©Œ€nŒè>qD´x7@çÙht\@˜Ti¢Æãâ˜]Ue'õ5®´£x¨oF7  
+ø«)ý\­<µ&ú5dÂ6ã1ûÇÒ|2næg¦Jˆ)Q„Z6Åim/¼~Ügƒ¬ðÂŒÆl?ÚæÄ)H5BîÖ®ÐÝÒNç)^1sèñò¸¥&DÙ&¨/ºÂ[p¥4¸I‰€_*T2Ÿ’"uµ2¹AYC³ÕÕÇO—†µ ýÜW«sc£N·ÐFN&i®…ÝÛäzŽ' ¥
+UZ¥îJ=@%÷ˆÐñ±iÏg'è.TÒD‡Ux¢ÐSJ±rÌHU"]u‹6ÿ^3 
+¬†&šdªŠÈ’Z® z¡f'{fæ»I
+JÆŒŸgO³þùN@f÷è¥V)
+majßi˜\a× |RÊi—ÛjnTÛ{NõÆwÅu¼³¢žl €è}ç¡V*¢d°æµ'Âò­Š@(wD<T„M £ÀÁ±~Oº‚{¢´cGmÙ‰VE³Î#.¸vDêá¾èªŠXç)¸±.I‚ž^ïƒ Dß
+ì-Ñd`ocd©þÚrNmwÄŠñá6N:NûИ9Ù“@éW<<·×A Q?cHГ¬½«Õdt¯hàeÒ•à[ðfª!ªÒ›ˆU`G­CE\Õ•ù0µGÙÍÔ<Êt"¸ -á§Q¸0„×3ÝÕI3½ØyfVÈIál›TD$=¥Ø¹ÅþÃ’‰ZQÊ›Ž^Q#ƒ*ÊüÎÊ’Í<TÄœ¡´´Nþz76¬¹#JôŒqÿšùܼÕ-zõãt¸‚×NÅV®6oßw}=Õ&ŸÞç_i³)@l–F¾O˜âÍ
+ÓÒÓæ‘n*~§o%Ã)»ˆu–àIOÔ¨õòÎ{–ÖÑŒõ>~¸®a_<Ò
+˜—?6š¤³Íq«Õ
+r!-5 ia1C2¯÷‘©rÛë;y«-ô¿WФ…ÄJ®‹±Ú/
+
+D¶pó:
+ÕÌ8¹0Ö4zòŽ¿Áf/‰|>'EøÀ h‚éRÙ ¡HîŽZå!»UoÅEÊò…;ñ¿&™»öç¿•%ü>Râ10´õ÷jçíÙ#uÉ1Nu_Õ´±“'Ç4ª"û{µ´¦g§Aëwg%‰ò¹¯û£Â{¢HìyòÌóÁ
+ØS˜€lUý}¤cþŽÝó_y@>âÏúHXî¹C57Oô@2¨oçvÄ8‰.;÷?Íÿ²/—"R¢bWÃÂ~äòm_^ýQì5Ž
+…²&oÞd=7° îÖ]Jæåîì¬
+_¬Êc`+B#֎غ‹xߊЖ7òî“ü!: ýœ¬µ]ððõ– µE¿õ/-‰G{ÃŒ¾«šÃ-ª"àÞ"–ºÊŠ/88‡Ó»‰ç î!ΡkæK‘&ïTfPÍYš§à½Ÿ4#¢¶rŽL)¦øš&ø†›6auj<¨7Œú.f©ƒq€«üR®é¥j——2…iÙgh0"m) GÛõ§ÿ!`¶Øh”}h]5|ŒÃ³l¶¶P|+±L\.Á´öæ€çûÜT’žÀ8äªüwÿ*©a1¢¤Œ½ "Âe4,” ..üxJþ»ÎÈ=…­ÛñW­£–” íY?}¼kýë%-ËQ°²(é*X$a†Røx_i„Îp6´–[–Ìt\âáÕDQH5ƒ¢¶ç(ÁùUž›8L„IØœð)}ŸÔC™†1î“Þ ¾«9~Obër’yÉ,%j•Ë,õÊ3¹ÌD´zk"à`0­ˆ}[ƒ1ZE”Úè…§‡šø‡7"Ûf€j½ ®ž =· ÙœD+é(¦øâɘ„wlní­¡œ6lüøÎiîæ!YÓ´µK¨/dmVø)=­Ì¸ÎϬ»“®—4Q3æT«ó=ã,GÀ½¦ÂâyÜOßÛ1¢bï6raó¿¹Öõx\\D0ü%VèÈùÜ1¯ò’‰6o³±žFÂ×bÉrGpð…§7±
+Fõ‘Þ:’ø¼ˆ¿QˆR¤§¢¼$'ELÏʳ¢íU†EZ;Ï µzлJg8ðZŽÐÒ»f*{?­ì]E kRQ68“† a‰'âÚi8¬JSû>(ˆ=WE¿o°ówÎ18PF‹¨Þ¹‡:t«0sD“É€Ò‘óHq×Óqʱß‹ý/Ú£$Ci[ÀAlŸÒÝ×( dÚ²aÕ»™-/­=/ PòÕlH¾G>wŒ}™PB­qí¦…DôxiÖúç¤z““¾,Ñe#º
+ö•WÁ‘®D¼gû
+Ë%OÛvKÞ/_­ÌÚ¶í[G2ô÷3ž ßÓ+jŒIˆ½!þÞr3•æüƒ{¥ÞÑÚÎÌí%¥ÈÙ¾z ‹¬òVV9m™å<YÊ"Q¤áÐEè^DXì†@å˜Dç~ãû/’—‡‚£‚Ö^*´o°á»“¨Ï™Ó "&±Ã”â
+tf
+Ø¡¯¢­:ü,¥¨Ÿ%S"Xñ„Ð`$ ŽÄõAà=j»M…ÍÊ”Mqr›xª §ÇþGd=ú÷÷~¸GÌt¡?Ó'ë\¼ vcœâë¼?j·õ„fǦ´´Õ¢Gh3 w:æíï1 Þ™"Ô¢žJ3e¾ÂAÒ´“@e–Ð%³ §îd±Mt‚ÌLz;Y‚x»v£ÅY†+B^f¿Ç:%\=¥©ßD%‘ê)‡ÕÞÝ©Yei¦ºUs—¹–¶)O¼7µ’R SSˆÈv-—ê劽T1¢˜ÃòLûd”Ë$øØhí 3—s’´
+&Öõ Ñ l‘£¤@±¡æ‚{6¯äiá›
+Šæ™QŠ@,40Â̪µöó K¼è¸Òžroý, ¯]ñƒlE„ñ W:,ú\V”¡ ‚ðNÒ—…Æó@ýÔ!"¦phÏMáջ؀"ˆ4Q&­@ÑÌV¢öŒyú( ‘ êÙ–xNqÖœ‚Ð#¢öt•uÆc!¯ˆÂIÔ`”MHZfyÈKΣ‹i`^WB|FÄŒL«9ÇÒ?€àâ/æGÎjlc„©"
+2YÇwRxõ‚Ü’¿Â6U³Bç"1éšO\}»ìÝÌD‰½7
+zÄñgXà­¦ ¿BÁº„nѤS¦fë°)ñ5¥Ô}À\åZˆG½M9TAÐgÁñÁ.ð
+ù?q~PÒ ƒj¡ƒ³*@à~‹˜@:c€§(Pf-¬v¹·Šƒ5?³Ð˜”ÏÍ@¯œë™€›Ir  À ]d^„brµ8Âóp˜, Ù0vÈV¡Œ=â0¦~2RoYFkf4i4¿¶ ÿ{D¬DJÂ…SrýÚ<.Ý-1¶\m™”lÍ !sEÎä6Vmš!ž¨¦Y1iVÚyÏ“E_±µWZšµj˜!j1O£DŒ¹Ó iÜAxyóöǶëºL,*Æyÿ¿æ°† ÄgC¦L)/”ƒ«ŸMÊ(F·°z A¹¼‡†­Q"óí¶L,ÃRPZI°Ôx:ðjÌÅãò2&)ò²)Ìû=lð«'B
+$™^9%ø§ß•
+¼~]"½ë%bEAì4`ü1 ÚHœç
+¨bYa#Sï €‚#Euƒƒ(* ‰½¦-š$Â@¨"  3\f òîh?®¢x6ç PÍ@Vk>èð-PõHÔY®•öƒÐ$bqXšÊMRÍuç@Sù*©è^1ëª;Ë™Wb‘¾´ XHÚO·Ì,pUÞy¡¼sÔç5×ÑÌTÕ6ºŽò„" LaköuùÜY÷Jp(Æ6 YDÇ”<ˆ?$Q‘éF” ‚Ýû•iÂ}ñ~P:ìEYƒö~À. ]06´€$äϨ…t²eÓY/“$‡r
+^¥NPÁ™ükßÉçw&@U;$Exã^tG×âà 0/âìI8å°Øº7zÛ·ŸØŽP‰9Ê<Ó/“˜¿´)Í
+|w¥TÕ'‡{N”Ý"*d•øÁóñ“Fñù>ù3ß©÷
+œUD„ Ä>õT.ÿÛ‡Lêi ©BÔѳ´þx!£
+³LEv°þªZ 'xŒŸÿéŽþŸÁQ†Ciµ¢W—Ù6Ñ(˜Wx1D{BÇ *ÎÊ0>?¼”ÌÏÉA:U†Í¢NÒì:Ò'ÒA5=ZÓY{Z0Q¬(ìOOTH¨âÖÉ”Á
+KÿäF´@lÔf BÀñ¢¶\RíéÓD­Ò¼íÓ\_¯Ã¸9_A:¸áøƇֆæiÑg &WÌ ç*Õc<ΘbWš©Çy)%-ÁƇð”•Møû}Òqy 0übqäy—ïpE;ÕU¥"UQG»t\\û°£DF²€‰Îu庇æEz¨Y€yÜ)#S¯ÀÐlHé¾T'#PIPõb$0ÈEU¨
+>62"Hè3´tÚh›eEtƒ¬OÓKÿgŸ‡2‰v (èmˆ¢O®c©T;½¥`µ¼'´%üŽð;ƒz—?úÆbÕ–ÞÍ\u4QÃ-¬Å•ÍŒpÚ™²CÚï4úu1³=V w´ÄÕÛ¦¼$/ˆ[8ç´ÐNÆš_µÉ-1üx¤ðøh­¿þ\‚< žü¦äŸß4-õ¶ñh}•uq§¡{i©h ¤a±ØÓüèqȈu°<‘€óÚáËb
+H ÁîÃi=QÁ 6×—]‘h­\È#Ò\zæjVùDÄùñ Ÿ}B*ÇøDA›4„^µÃ´Ç»Mx! ƒyùêc¾È"ÿ¦¿ìúe‡\ò¡§yŸc ³E¾¢7èˆÅ´ D*9§£z¿D”‹¸>kô’F÷·àŸD“(" %cPŽ/°dŸ¦»‚¥dz?¼ã¥; Ÿ|wzG!*ÔÜ wnÄñoÇØ…‘#Öä DÇàJ\í8aÍŒ+ïˆW/¡ÜûÄL)éëà‹P vâ‘ô¤¨Ÿ¹L$·Ao¯¶Õ´¢(o'dÓ¿¹¾ß ~=‚¿uC0˜j
+(•ãyÊAÿä¸Î;®Á$oˆ,&Õhû`‹Æ]fŸ Ò‰4-çà/ϽpãÆB÷ æRy r©ãnó"f‹Y †£½ͤqÚ¶8mÈ»ñ^Tp8œðuSXÞù^šqEG°Ëú¶Qâ¦AX¼{L:ý¿>F»{~¸ìÙ·_ïǦ×mž›ùØ„*Óù€ÌmÅÅ‚=¼õ]Þ¸j­¾ ^×¼7î&¦ÏuÆúµ‰Ntø æÁ_ßSÄO@(ZÄv!A†7AOÄÔ(VZgÄd¯Ì ˆ?\b`«aŽ D]¢¡Êcõźû‘/EÛI!Øçõ±£N…“é×;’Þ©sBWäR¬°yDÅ~=xIé³3(Bן@5Í4`EÅmÊ ù¡ÁוŒ1 "¦:î.B±KıM árCÔ4a=ª!:Ç0ÑyŒ b#®òD
+Dæœ ¥
+Døk×Ø¥bžP9©î¶W©\›Î»$qËÃ}Waá‡>cÖ)È&}1!¨L-:íâYuQhqÚŸý_µØÎú‰Ò¯í0ŒM±¡¼Q»Šäc¿ (9Y
+yª¬ôr†ï¨Mà?œ`•¢ãe˜-Î0BéöŒ¥µã&‹«p“SBiÞµtΘ†UÆ-N{¹-D@q…a ÔmÀ„“kEàEz ¢Ü 07#˜@`¿Á[Äáýÿv¢0'PSÏJç
+ˆâ´—7e˜Vž¥Bd"F„³¨Ž¯AufË;!æÞiÎúrxî¡Â…W¶ö<Úà Nò9a솖Ëæ!­ž/ˆ‰9*ˆº½ö+à| {¸‘¾ªá3ãðD)Vç’kå ú
+#<¤3Øc4˜œþ‰økmfÁÖ^LÊEi²¶ãŽ Âc˜aà¬ÑÜqâ²ÌEc†ÄXÝ9{öΆªà
+:mº
+²ÿÖË®7¯£ŠÂå½L*pæûœ)W E¨Ôª"½²¢$E4Ž‚ƒ”Ïóì9v‚¹£­]¿ï¬ùÚ³÷ZkÃÆçeL­ÆK³€¶<yQµg
+•$b/wùµc§m™3HáJçƒ$ð8dÄ~cQ ÐÓ5®Ž
+'-øZµ¶Yñ—Õl+˜F®çÈÞIÇC¯hM„·àÝZÚW.ÈdË$)DZzR[N<MÏ»ñ¶}4‹V¹.‘Ö|W=?_ðtT)šºOA/‹ÐáŶÕ2k'Óœl´(²=Sh$yŠUÑ~jíA Ír&ÂÑ“Yð’?W´1„žær—ièg4ä#Ü\ƱS[6‰6–:vî„:;iNÇ"zYäut[D8ÛòvœÂAœ•×@l q—¬X‘£³À{~dÚ*ü %E`µ î¢ï䙸?‚ ¼ŽÜ…\Þ½Ê1c ÉŽ j¬C
+6ƒQLž8Á‘ª!7„súºÓ\±Âßithú¢n)å-.Lƒ²P6–D”‚^Ó‡=ã@SM¥Ì u@g ±‘À"ð–@…c ¾÷;È’f”ÖN½›iT<ëlj ¸hò+FÊóW»bWËßÐbÇɤ»åÙ“68̽ø¶¤¶T¦É€[1
+éEºMeXÎHuò¡šêgÎ[¢¤”îv:¡H¯@u$¿»„áÄ´}o;m*šU<K¿C ¿³Ÿk§²ÌÞ@ÃŒž×Áë†à~ü"“+E5yÔ_‹Šh_ùz ÆrYÄåxà¬eY%#
+‚½ÚB•}¡øçò§ã<S‘Vl^¢&–®bèšiíHO8FiRðÝ
+âé¹n õ“@¬¶©¨r¿ßH‡ê ¹›Â3oë˜Gg3e[
+!±¤åXpÝõ?„—Ù‘+7 ES™¦š;ùíPœ>hɶZe¸êyúŠ Üå6,W˜äòv‚а
+D3 %bõ)Ê»½ ŠD‰½}Á;ð3óæ±IEŽ"MÊ°VåÅ’Ð!·1%ÜÉVÚGÄæºvX.}ngÓ$¸ÂHRpQ¹ÒêlÍ»1Ä=ÉrxPÈr+^¨0Ó«ešÑlpM/E÷äÁý›Ž‘ÝóñÒh‘¼Œ€íE'®"´
+û…}
+
+ÚHE|w¢éM„<!¢Z êƒõú=‡«ÊHD© ¥
+—6_\ÐOÔ‡n׺8¶ 2¸Œ-7š»†¾ p@ÕÃBPý•w(D‡<©ðJY'Gb"ðÈT|riwó#éÌ„©úiŠá{ú´?y'ä„ú¨Ò˜¶/(KR7sH—Ab-Î ®"@”/î4_ñüÈäãã¢Ôγ¥FŒ°´d—Ãñ¤îÌØéM—?­éúÔÖ¼6€'†®†<„% æit«FA@Ëeºã,
+ÚML£Œ]ÅèC5jU­^ínµ³ðFÝ  •‘SÈk‚R›3km™þ… GèĪZÓ¿ççÿMÓ+™PW8IDÈ:$ÆZX6þíNÅÅÃ^Ò®N
+ÎñúD\eÒ»<ÐaÌ ŸëË:+H”G§9|sÖÅG¢oƒ­ë àŠ¼.Åà>cýÝ\ÿU£…ƒÄèób?ã/jmL'Œü%™˜¿†2BÑ9•ƒ*/ŸæÛ`åI0¼ #¾¿­B¶‚Åü?Ìi¡*—ê˲þò
+] Vi@ŧˆv\"Ôä÷NH–©À”DÙs¸ì‚UŽu8¥)4õÑš`%jî6D ˆG "ä(xÐ{#ƨ:L‰tBJ•Nôî@qê  Ç¸tÿ=ŠM—©³ë¬QŒ:ÙâÈ7B4£…«Gx¬¢_ƒÿyê=[Úw¾Ù.zšñ k*šT^q¿xçDo¢w2™#V‘~g°næoÆ-á
+è„ÆÔ}~ÏZiàí3dy”õ\ƒlÁ#3J”<ECBhyÖý†À?]¾‰Þäý&Ÿ(èÂêmÝ™yZh•nÅúã‘å²8¼c¢Ä¢ãq¢ø_ãÑ'â5£1ŸÛØXÖCNÑÚÇÆ—?›±ÐïÈXü—«>P¤‰C°†¶*$»/`¸C«/ ŒóŸ_ƒ7ÄmP˜"+EëÉ1Wð|Yò¦×%ïnÞ¼=Ù¸=™W]5£+îÒ@˜Tltœ¶Ý6Qäǃ#îÑ]5~51"À¥9Í72°öBPÑ"¸R(>ï¨{¬s­¹‚“õËÜç\SQàR»Çwd¯h¡ÑÍ}¬0r5î{ T]¿Ž½:"ÔäK ÍzW~MÁ
+üƒq¤µ~J¸ú®bL=
+mÚETöýÖ¢½ ‡@,»ƒH9úëa:ÍŠ•Ê‡Çm¸ŽÌÓ›dDr‡ŒËE¨E­¦ž/¯`€dùãÞùëEƒª†cÂ(3óWÑ8`9G<ŽÏI3ç༬wÕ¾?¼[_në²ñ?~…Ïk%0eèC‹«wÅHRÄ OGøÁ0_†.}ð18³r‘¶³Á ­Çë(bÌ1´¥€ò1$vtK½ðuW8Z –†ÑÊCŒ\Asˆ¸J¡PßSŒ)s}Yç‚”t˱]æ:²Gü•±i j_L2é
+lAòh’€þ^Y5ý€¾ A9÷t-Y™gQ Z[ÁôA1ê–Ñ>!>‰ßTÙ™˜ŒÌR<­öD6âÁü´$å…ùÄ@_dÚ€ŽÁ…c 3Û©6f¬ûF'
+θV¯®³¿«ù
+£€„°NÂŽSì]´„‹6À×À<^ 'ƒ ´jŠ10SO–¥«Ø§H)û@èT:lÃ’L™xOˆô Â£¼>üå¯1Aßö@K¿>š5øˆ1'ýDÈD#ý4c¯È¡¤0’L˜žT>^ç4~T¶üØÓÏÇ¡Ñä:Ø@@ ×¢D­?ˆv#õ‹`GÍŸ O¢èlõ—^¬>w¶dgxI<â>ˆh† SpÑ3N`H«ˆÁÈÝâ€H?dnN«žÑ„W3ž+*‚²Y"Óæâƒã$Aò¬+¡<ËF¬Œ®)8æbkçète0baO«Nݧ¥AN+.ÂؼÝ+œî…Ê,Fúæ<N;Ó%
+m¢f (t_”Ì+
+ÇžQÇ:ÆežWNøðbâåŽØkÓ¾¯‚&†âlø÷ ›fÄôP9<¾z%n§Ä”öò ¯ƒl‰B©f hÙ.jF,Ñ55  6š-ºÜÂm³Û
+ÄfNDÐÛõµÑalo9 „ªÔå2¬û¾V¶@tät“áûê4ˆ)Ÿ€@.úYcRc×Àj¾TòìDdhñ°8M¨uòÐZZdN<Oj§ë±¦R|øÁ7
+% újý‡EJ5‡p@ö¨2v6ÃiÔ°O²ä0l~z’ WOž(tŽêAY„U&Žu6âÉC•®±„tæ”vâXœN¦$ ®
+È5ŠÄ9j<¦âûd¯õÚgÚ¢•óTúaPÄë Úþ !bîtŸƒ’Æsì~
+‹f<N„ñÍQIØI××èŸLÆ¿{|´¯tZ“¯W /¼NÇýÚÇ¿ŸÅ求uS«ýéÇÌTëq¦Äá §nϸîœï–Ë{ž—[™8r‡ TÇŠ"æ”'ê÷{Šf¸µb{ÕØmú„̦²BÂ} “ÃJ2Ì9©Úû‚÷ "íX¹‹ž(²j=²U0¶õyI´Ó?ð¾H² €ð43%
+ÌT#Øš‘ç÷Ÿô•,Å~Ãõ@i!’…ÁYñpÐõ¦¾C¥^ T~Þ¥6öyAPÄS;‰Ûo×ÂÓåzô'Û Î>•iaòÆÍñÇ$JÉ®IÅ2Ö#»ÝY“Hq0šG\´× =j>Ö sç…™Ow†ý6Ž[4z=¬QkC 荒N0›B¤æ·­²uÓ†dÂÓÞrÑÅ.n8Üh!´õ¿b™þm¸a³IuŽ”‘t˜×®âÞÈ*Ð*3Æ~kÊ b„àšúoþcfèç|Dž.¿”Ñ×…oã<}ŒWáÎ'©ADïü'‚ž£Ñs­œÁ|‚T}ûmÏ0½ˆ"¹A²€vÃ#q´ePŒ E`dCõZÓíSqÒ!ŠB#kò
+î#2i¾H…KWµ¢„0Š"S"üCĺŽEs.]‚dÚÒi”"¨i¨È€a7ÐìvH ó“’–¡ÙZ¦#zò¨a¥Þ(º˜ìPµ}k'±¦_;U­¨ö’m<ð~]@‰®ò(:>ôÄÁù
+_2´Qà ²C“›&½vÕ…ƒæs:ãx”1XFRF±ÿ.ïW0Ãg¥"
+–ÿ†ð¾=‘2Ÿˆšs‹è7§±ßEV‹ˆK,7¹ÏéBuDø\€
+RV!oßU¼ÿ¨(£iu¥â*Ј)Õ“! wÅbÆâZ€Á\Ÿx
+¦š«›Zw¶]÷uô­EòHÛ/íÚ,ïH Õ 6÷)
+âêÎX-J
+â鶴¡ûº4e×avCéÅ ¤mš²mGb˜·‚\o*£1¡k¾.*00£`ˆar‰@e‚Ô#2S•0d¦„´1\ Ö³é¼ìßNg„âp‚"Þ\Ñ¢xsÅ`·DMMŠÆgõ8§¸ 7I±‹É#àmGŠ0.5)lÜDáW· 2>ÒøÊyßÙòxÉPf[Ï“Êxp¨ `åK•ܹú‹ÂÈ•$ÀP‹É¼ˆN´X4mBûS¦Â´hÕêµuEeYÆ[B‘ÏéRRÞÖ¸D á@ ¸£àfˆm±>ˆœØ@t=³g¾%¸»“a|Í çVJc
+Ÿêëçü¨ÖÆŠ NF`¦{ÏÏS>§¡z|1l15ÊPø.%â"œè@È’¯c³a¬DËs¹ö-; Ãd ßü‚žòܱk"àXÙw2@;Ïf– ¾}X|=SHõ:O›¹ß™0_­‡…RG¿ÙÝñ´ÿ+wþLÛÓêJLŠ ý þÎ
+‡~qˆJëW…3™HZšÂP<šY$¿ØWWK:‡ÂžD¸_T ½óæ/É…€|Šb¡' êÓsþС€RoäqÇEʦ¸gÇ9sƯ,LUJ(Z¡Õ«V«
+À8˜F´ŸD(y@âã&TD FŸÕ'`P³äM!³AÔm'‹HÄ!“Dè`¡¬‰7ÑÕ"Fy‘Iít|Xí`RƒœÄ>ñ3Z`¨ÎçµA‘h{˜2éS×Äjj>„œ²«Höa-¯§…+ ¿U¡ÂdìÒžçR;-Pˆ¹ªâßÈAtM§.ÅÌÎhóÕdö‰ŒR½jjÆíÁy³‹²%~¾JhWXþ>ˆc3€(.S·Âg$Â:‰m|\€¨Tm“„ý œaQÏ1ˆæ" EÂŒóZsâ„£q|"ˆ²2(Ó#r>.t¯ÝÅå!£Ëx<««Ê묟ꓽìµE³@Õ°À‘©Y²ïêZz¼»E@ !ãBO´¯ DЂœÃöXˆB.åjeêÅ€/ŠO=O“C—cœ¿ë1!G@ ÚÔªìEìY6(;$Ï'¬XM'+§6­›ã5ÔüZÞÅZ(ä°Y›¦P/®ã ¢ áHD%6}WQ³ƒxƒo‚Õ‰›,n…(·‡G¬ªÜK4¼›/º²š àYÅ;áÏÿ²]f¹rä0¼ŠO`h)mßs§9ÿD¤ªž? ÛÝl-™ô¸Z{2g(_”3ÿpùÕ7lkSY•ò•ž9ç­á’£p,%GN¬²–´‘A­F\òµÝKOYO‡OÜfüd
+"EM‰:vÛí>ªÓâzú ÐÙ‚z9Ÿ¸=·ÿè›í)H)‰š™nm §[ØçEA‹#u/ AÙ&¨/÷v˜Ä—C‚‡~ôJŒ`™Þç'0ÁOXÈ#é¸g·žŸ)b%ýîM»<Ù©Û.ciM¬G‘¢<|AÊŠhÏñwfÀ[áR;u!&Îë[7¯ú­- ƒtÇÙGâIût̓O¼R R…cÎAˆØ™£T œ‡Ùu¦ì»$b¿y$Öi†ÆJþ/È|œŒ> TNË6+Ý|}>ynUž(™ýíC”KµÚ­ÝkVu×D¹‘u}™-ª$ˆ€&ð¬W–%un„ãËξ;1~®BY`e ­‰bRÒ8¦©{¨’}53’ÆÿçS!"ùÂéN_#õüõ>‰ÇA5¨ñÕ
+ÚQ¸bYêÔç{~0¨ddŽd‰ŠÕu±Ä‘’ÒoÛ'qÖ³9Ø euÍF#?qPd¹õÛ| MÐaƼ/MùF…F ReGÖ%+!2BæùZ*nD¦F¤tŠ–ìÒñW¼ûN¢ô½žÊϪæ‰ÜL/>÷íflÎW·ôÉöö媿ý¥yöÒñ¸
+
+¸½ Áû#ŸäÕw§§J•/:<N™ü”†tü¶ùAN¦åçµ+Œ%¥>ÑFšÐäóžÒj{“¶1ïžü„IF˜‘Ú^ 6ji´ÏkØD=CjQüåv#f0†]wëxEïD8DÑ®¡0qtÛqrxüv*ÊQXȺWˆŠ ›¢&i`–VÉ¥ž\Rêñ’m&bûE+÷\íÝ
+/òL˜—o8öÂ
+Ü;¬`oDa±œ¡ õHRE°î Y
+KÀò~Û³˜sÉôÔ1.aðª."} ŠÂ ?õ.3Ûeb&´‹¤¥{6Îf ´ØAÎþU²QäCp…gžD 딨ž¦&‚9Áuªû%4*F\­&‚RÑÝk¯š€AžÁ
+覿Ÿ?Ë6ÑVz´ø£¤.õ’õ
+U4 ¹"Ïœ2mM’±Ÿ„y‘­Î@Á« Òá-L
+Žû: ˆßPc´+sÒà+
+áaŠ9‚Jë¹ÕTð3†°'¶þ8çRlwÀ<àÒ¼ºY’4}ç"iÜ¢•ÆGd“—lÑᯒpŠg”˜i“‘¼®("P³Ô‘­lçv½SüÃv"üüÔW¯eî±ÉÂy>ÚZíòm§•£k|'d9ÞбFÇb¹‰”•¦±¬;Dð†YŸŸÌ]/ !ÂT€à‹ø^²~.Y ìЛëpK²sñ”ä’$%+XçØŽËÙ#SÜlÏã ç!ùK\Yã·ÒÌU„çw§©»»}·Ôjпð
+nÝ›íÂÖ\IÇ¢DÊ£{Z
+,6¸ä¿YG‡×*ŒQXaâ-7W/—ÉÔïäJ]Ayܨ²u,ö¯ãêcÒ¸dðzªëø¯QQTÞ|×™ïœÙ/¶™/H²?•«nSo"Êù³ôÕàÅ([;QMß4Ân°Üy Ñ=Ê8Ú=²3Œà²w'FÐRþÙij M¯‡¬hxúù®qñå°Æ=ZlhP "h/¯„‘ï»Ä¨ì‘ù–4ÿóRôÙr¬ÌÙ¹€&ÕõLlÝ¡bdŸéN²ÄX¨œnef¤˜ÙhÖðfwr¡¢yƒUÛÇ?FÙ¥D-KÌ(`­±%Kˆ¡Œ€F"¶4Áº_ð6wƒý56_†óø’nº¿:Î(¬Û3ç!§åWûdCž¾æ‹€Ã.Šu
+eG€ò®¿07çQDûèÈÛÁÅ~C{:gþhW+¥Ë:(ÿ«¶(Nœ"°€ãS‰”<•Ô‚$çt^öa•A‚Ÿñp4K{U¡[ª=_x­^8Øc “DÓÔ+ðÂ
+NAg¬w#‹«­y£jÜs‹SY'ó,£+ÁFäc¸Ó:Žûñ|r€IW¾ËSt ‡T»>Q›v²~ð ÒBÞñðâ8NdlEws
+Û´Ø9Âð•:^Ô®ôE)µ)Ö«OXpÜàÇ¢"ìÙOøröÒ©ag„®ÀC1äH2A÷•/Utu§ÌŽ‚M'âlx6HXkÔ‚~Qk¨ ±®g[6z¶"ëÀ{Š'k°%^Q5½–âŒyå„4¦à¶û÷ÐÓmÑc^UÊÀÈt!<‚@]ïé$m™Ñ*qP“ ™Òfd ñâvï#‘OU”g½nóæÒfP(šA.«'†
+$X³ˆ'±Âãš0Ï[ø%n™õ—ôºžûÂ,
+µ_7à~1-jÒþË¢¸ªÄÄóÔó³ú5”Œ3Q2™QØÅÎ:Ã
+lŽ=rº—¼(MEªHÜ•sÞø@PBÔ»Œ»}3žé À8®Na´ û‘sÞr+ç:QŠðB•·b,÷(L;:÷Ÿ
+H‰”—MŽ\9„OPw¨u)þHËA÷²¶s‚Æ̦«—sÿù(éžÊ—ƒjÛ°¬HQ"ƒÁàcúèctïÍäý×>ôѤ[Þ‚Ÿ¼[f1D»I´™iÑ<,ìý·Õ—$£Ïž¤2\fd›ëž>ZZW=›êîÄZo*}pª·qùííßoíýwÎGiÜ¢Åй¡9¤ác¶ïmNnß›ÎìSƒ(æ3ëV@m´Ø燭ó@\!3çŒsñÞçHCà¬P-ÚìÜ>#N‚÷+qé²^—õIr4O¨áfn|ÃÚεq€˜¥O7ëæͳkצýöœ$Tš8¸:Ò„òô¾!Òç¤:]ü<œ'Xö6,W¤$É•@6¿^î6Að|rQ
+‡ÈN»nÜ‚L%yÎé·¡
+Ýa]]xœPÏ
+7U8m¬sx\‡ ÙúÊp{LjÀ»”/
+­¼~T£zÁ:TÛçã # º--Ë×[ƒ¶!f¦|Ço¿8Ì«'eQï'R篜ۘ¯—Ã⯥ÈFèPËÖÈ™oDtË:—+Ç dS…ÑœC†þ✺\:5o~nLN†$rA.†h“v"ѹƈҢ«YJ–,˜¢'7‰XŽj˜EhDâTyÖÀº5Óæ/ äÒ;уx«â¹x#FòšŒÐç PÒÍP<!Ô¦q¯«Í±ºì\U$ëÃJoVó"””›R\Iæ\¤°iµÖa:)V§hóÆðÑù§Ù– Ä’ëx¢‚ŒgP¤Ä=÷ËG"¾YÙ Ac! çÎ#~‚ Ù-Ùé¡Ch»”¦ŽþneSfÌBì@ÔJpè÷¾Å(hGÒŒ=n ‡£¤‰¾œü¾?‡T…•Y¢_7¦™œö1»ˆ>´à…;5ô¥”ä[cÈíHã'êŽ"GL0Î?Ã76Ú§¼ŠéXP[dÄFªÂ~H! 8Š¢ŒÕ£_@ˆŸp}]ºttà+ˆáˆ’/¢Ï=©(M¥°BÎPD¦0ñÒ¡…Ycš—òz“ÍŠš ¤ÊÊ#žuŸ{ÐÏÃõó;øƒaýË?ßÒÓµ4ísah¿þK;òÙÞÿ|SŠÄAþ£öÒetxp‰Æ9;&@¸QƒXðÿôq²Æ(°³eMuôî ôù;},?ò½ýòÞY­T/£Wè¨õß F½‘ÊÔ¿¾uõj “Á¦+q³Ó©¶š[¹6/-%¤½m»}¼
+Á¨1ÜN(x‹ýÄ<"ëë\kݦ,ß’7:U«€ä£lC`MâB$RŠ45|ÀŠf œh3â}®ƒ×á®^k@¬c˜Ã&IÅÃHý°ŽÁeA S05°?ó²CY]è<m¾8W`ÐI9ëĘXødj¾ Kó¬ÌŒêÍ…w(ºÎÁÊʸž>Ü+åeEÞKqeÐ’¤sΨ*á™ I]Š”Ò£Œé¸R ßËd"Øhî(0IP‹¾ešmŽŽòÖ·ºv¯^¾?¿Þ…§gé’Zœü@œ´ãX½4kA¤£@ƒ…Åû0lZŠ/_O/§ì|±fdŠµ@Œ~¥â$Û.¤ “¬º3áÆÅ}Ù k ÜÜ ¬Ké‚;ÓO(/óÊ÷Šš$ËèiÍŸ…h“<^ëe!´Œ>õ­A»X±Ö¬DÜ@v¤j¡à2=ÚcEù¹ÏÁLcÅp¯~žÍä¬D@ ÛoÂqò>øÏ_7Ýy_Ûêét Ëâ1GöÛGfýÁZ »Naƒ4)c¯!'œSi\•rRLJéâ­'Dé|S\¯B14è¤Ê6‚†Ãß’˜¸j‰ðâòc ^—r}€ª FyË Ô‚˜Jê°!J'ßCƒ¶r5~ŒâÑäÓÙm (ßl‡€ØûøfCŸO’kÌ•ç%Š~e»Ã[ðñUtÊk["i±!اŠ~µ8ÚA¢Ê¡mJ,a°šŸµÜö/Ì" ¾«å‘ÈL)5áSy¿é_ôzGå.I8íš TžØ5šísÍgûóKi1ÙÝמÖý¢õ2‹¨RÚèÿŒ²-Ôº:‡§5ÁróŽ¯Á0—ÞËò!Ï y$û*ീ¬ùŠãbÄ°÷¡íú]Ì—`Ÿß¹Ñ¾øÓQ_æQÝÛh`¤­t‡ù¸Ã|i®ÂHÉ2± <ÎÿÛ ›hÏûèÆÎB%©Œñ¿z²XO™ü‚ùüF¶WMc,0® ôc\üœ§¿nAöÀlçݾIp ªA9PÁÌšÝ "8vxÁTØb‰ÊÄ5UG„5-'‡È™Ž¯éªµµŒj@lJåÕÑ«<ïgÏeÃ*h °‚pA¦}5Üé%nÁTcŸÅ°mˆÑŒŠÆÌS²,S\Ž„ü_‘j½Â¶ˆêU2ÎF*ÓJë”=¹¡fÖ7ÄKa¢6¥²…aü±P rÁ/…1y*is–,$õ@H1ÏÄ\ŽÆ v
+¡DXxæÂÈ,WÃÈÚÙ|H}ÝÏgM¦‚¬éÅôvøÉ*ƒïˆ¬¼ŸP¬•ekpùýœSÆ}- ²N©Ñk€È´n
+·,â ÂrÅaIb†à;N«eïeó¢<HAW‚íȼ6Tô„'³[€¹"=Z,óˆ+þâ6=ÆlЈµöÜC~Öó͇VÖ•u»ý{ÔzJ»à.Þ_@è-º…Z§½‚ÀÝYÛ'š{úàDþ’v¦;»¾8‡},I }Ú_݆åŽ@“ö½¤V+4/×Nƒ5¹)ú‚Heí›v´¦v…ÎHF>æ „˜ˆ0ZØûê”Ck”bÆ‹s¼L“r—ô—fÁZ èÍ«Á‘©AB3Þ€vA±õÌ'„wú™R´%¦ªvö³Œ>uœ([bë]Á eÜÅ/“œ¹n$Ÿ@wÐ
+’ÓZ¾‰¯¬ûoý‡‚êU0ÐhtÿŠâãCŒ[ûʘcˆùïÛ±ªÁÅpê‘ë†Ü'…šö§8’±;n>o °v0­8×~P‰5¡Y^¿™*™[§Èl¡<¨â·Ç„`nØ]zT.«úŒí ´„©¼-åvƆ ­îCXvG&gй ÎDlce1DU¿Ì+›ª5>WŽ³ÒŸ¨?¸Y_ôBIÈÈ\záæYŸ’%0ÁxJ¼­Crà³ðÖ³e|5óŸj}Bð:=·ì·^t§¿P7¶¢
+UwLÒlV a…"I À˜Ç÷DK¨}i„•Kø(U¶*§ä M)5}ïXôMºœý¦ A+*³ÓÅ¡’7÷Ï~Ô]1+Îk ÂÌtQl_ XRîŒ9>¸ e'ªqm]>ý‹x0!m^…’î‘n>Ë,âz½Oòmµ7K «ê)í/%À<™%~Õ¹²âS™ËäÂ%wÅê3ÃmD¸ò¼äñ2ÅW& îD +F–NÂí_Æ«D¡¸ 2Ë1w½‹s*¥jš˜0\v òK@tàvXJà°tžÐ²PfK—u70‚ûIù=3ÈZÉiqµFåšÕ9¸
+mP¨ ÊÛL×~*å}²<þ¨X!îÞ%'¹¶“ /ZnLB‚ž2å¨ÿÓ"
+l.C¶g¬px2ÕQöÀ¨¢ÚÜû‚T¤
+Í[ë· r¦Â=¦B )*Ñ¿kåçT˜äªêxÌEºL ,®(ã¦ck©ÐÍ2VÐj&w*Ðâž•MÇËTHlèSM!ü„‡ÇT
+¥³xÒú˜#ì*O
+U‘./
+z #rBŸ0#¹`2à6㘧œ¸?«îíClU Œ£Œý)&,¥Òæ(SO°©‘)¨ xá”z¹ $*)ÀaÑù²Nªm, p!6µE J&r7êÕп7PÄG¦k±Ý÷0Œ+kvƒ@b¼%¡gö¾AÜŸ‚;cšÓeˆ`s1Ìb¾íæ™^¢ÈJ+Y=9¯>!Dtn”ÖÚ©sʪI¦5.SÕŒ3»äó©o¹.Á ´¼_Ö)¿H쥕+äÙåH;1|D´´WÕØþæ©T-ãκäŠy$¥ò~¼ê¡­X_[š-.ïäs9V5¼)=²ÍDð~,OþâDt î˜GZšŒ-7˜À{lH”h3Fòý‘)b©lúL ÅBö­”ý©N
+7–"O•FpŠÇª¿Xj/.ǽž"˨^rº²ŽN¾¸Z §œý`î°¢’/ô …»€oCckûSä f”—Ñ}éè€JÁî;žQö¦“üžƒÛŽ¹N„F j®÷’³p{¼¯÷©ƒ;Í\|ß „íâkûS’cNßÕÙŽ¸7ÝòPæ˜ëåMLP9J™hâ­"ЄäV2¼!cŒ¸iA‹@'þêþ”ÁQhÀóõ¼A gïJwsQ¸xŽeÙ¥WyÉñ¶È㌠M ñŒÌE'‹—uè9\ìr%A ÛC+Ñá<!œ—˜jšÛæ\Ÿª\c‰'Dá RLòÇ.*ž‘¸ôÀ¬E1â;dµ±ݘ8×óqó§CTþÄòs¶x.‰€HÍeR|‡ºfÖ°#\úž'„³U½oßï­|FÀDvÞg"ûF½,Ìm@ø}@~ê÷ÂoÈ#rþú᜔Ǜ'l†,Í–Æÿ9mÎ=ÚHlîä›ý_mN€ðà|c\ÊTšiØÂOÝ2W¦R¢W8Ç»©}^3’÷ê+ñVæ
+T?9zÙ¼€kè+àz‚ƒiÒ"\ØNó=˜¼œæéÄ_tÿçGüùÐøÒ%‘;²¼?
+½ø9iNÐßØ[H8c]‘|
+Ïl#”,eK_¢Fïþ^_"ˆ3cÓÃtÿš®FìÉs™Ø•VUJq<žÃ¡ §>Æ0ì‹Ï1’œý%vG
+KÐ3Ÿ‡-*
+µeH³^]^ïú±Q
+Í\æQæ28VÑÔ¶vôçè!›F…Æv®@€ËU~S³¿Šn†­1[ç;UNY*÷0c€Ë¼lT9Q†p­$À„­ÂÀû:--V÷;©ÙÌƬ#HˆGÊËa„1´L?›ÌÈ÷ö§ ÅDRÝFDòv#)Äø€[Ï`õ}*$˜‹$²ű}ÏÛÁã!R•Ãô¹Ž<¥ªcODB¦e™ë¡r¥þáß:Âb}¶`Ón« ¤¬ ØÔÓF°{"ˆ1‡k+»7ÑâØ»qúZ;Ô•8)±´”7H5( 68§…{#âÊhò¾`|€ðF7ë3]0PïB
+ûÝ‘¬ R[šYˆ¬íD˜K²=ÛéG#+?‚—Ä×X(ñ^ˆ/Ý«®UJ”ÐO«ë ?•LƒÔuB˜v¬µ˜!ÍJÎ0ø°' ¬Ÿ÷:…nô w‰äÜp_?Û0]¥µâß²f‚`Õ†±¬Fç¬CÀãªh Å¼ÍHs Ž¿R>ª(@Ê¥Yá|èón@XR±Ôd­ ü¼a L
+ÛÆÖQJï½,”±Õ´5¡¿$šø&ãt&ëÉ>D¼iÉ£y,‚!(ꊽ{³à@í÷@üT…NÅ?¯-“}yKœ±íÙ$4g$1f RY2«½Í*·Õ%òŠÀºzw‡<œ:B¶&Ÿ-×æòÿ+Ÿ±ÎIOói¨$X¦G¿BÏ$Ýq[½Þ‰Üê°BÔ"Ö}N@µÿ£¼L’äÊq z à̵z©[ôVuÿm='@YÇFUö¢¥Ÿ$àð¡Ul{¯1sÌ€5tƒ«Û¬W
+…d>Ÿ%CAÒT$N
+ÉÈ"`ßò‰+hþ’/è.èII·ðMÃ͸ gz–9¯ï¯T´ÿÉÊÌ R^ºèõ¶âINþ2SÀGáù R4NtæG Åfe”c«@›Rþqó®÷TËG9‰`€[û)‚áñ Üžk©ÔA$“†ó15Ä >J^°#õšS‘¦º?©3jžTƒ$ù#­€©]K˜¦\™Ã$)~Ô{8gÜH²Dåv°•ÕË*#5¯"VáƒAµÔjD÷Ä8œÃ\JlÌ» ~ ùd ›B]Í'§L#8€œÜèÄÂMr5~+vr)+Nª"…’‡ìl,ð@‘À5cP iH9{„\Ñ@V¸ÝÞ5ºÐÉiw:†K™&:kÝhI›ÐWJÖXÚu˜ë…Ü.õN’ õ¢uD6ÕßYLæY%<"%Cqc¥Îc)ðh×¼Z£²„½1(‚Ã\w(‚ÃPs$jôý*ÓÁxS_‡GqÐtË•ò.!¨b–À—>¦£YÞ’ôZâ(>8L ?U¾
+‘±`,ØG?jæ”û<íPv“¡ÉF ÙÀ`Ô„¦:ÕB7|(1 8‰Vp“9äöòðúÏßb¯i&Ùh:pÌÊ£þ~(^Bd^¸ulƒ)GAìÄGî&#x1ŠàÌF°Î2ð¼þÇo/R†k=ÍvÎâ£àRnu¿ŠæuƒrêP¦€„eÚ<ÀÄ!Ô9 —èðà¢Ì‘9­¸á’ ŽðÔ«ó
+Íë`ô
+L‰kë Õ(÷A¨4$ZÌû°H4~p†nâº2ix}L ‡Ð¡.z¤dLnק±žq]Ná›
+`à€v
+kØøhæ³áô½$ â²E,.ÄÅOq°û3¬Þ„ sb”@X°0óƒô&èeà`¢´Lh$JÆA2ßœq¸)–1Í":ÌOó‡¼Y"ã·zÜ¥b6ÍW‘ÕŸ˜Î%H ˆ@ópÕ Ò
+ï£`ð¼ð¥"•Á7t+õFUAsíËq¯tP±C˜m KËÙ‘%ÇÄ8ÚðÓé‘ç#1ö5‚"÷%掴dTÄ ¹pâßáˆèN"ý”ÂZ•8•s‘»ÂFL™ž2ãc´¸0@Ö8YÚ£uŠ¢`–d2,žÍü1…]¢¹qº‡
+ЬÆ#xUÜfêhv ‘ÿ›ñ2Évë†è
+²¯À‡-HŽ¡×‘‘³ÿin ?QÇöÈ_‚Ø¡P _a)!Éu9oXî(5«uƉ ¤•|9üRÀL[1i%ÖEr&™rοD€ï e¢Êr¥Çüh .øîÔ”&yâƒåÔ¦cHŠ{éVy%æ¤@é¹ÞÇ|â`³¥‡
+ÆÉPsª§ñ"[‚"0â3§“ª@n3ÇNŒ×줔Ñ?”@±(U°^[½/×B™XèÃ: z+y ¢×"ÌOâØöˆu@5܃EËDq t>å+R’ôÜýOœ?‹”V™.x-¯~çã‡ÿPòMüv½Õ£èÇÕ7sèVMÌfôÕ!ºä8—(€A4å@`)‡X=í¬í‘j(ž¢Ó™;&ŽÇ€8ò‰` Ó104 ÑŠæRŒ'‰*µ­mÑE7K:a¢À[I8¥_Åã² eئ­¸¬ „¡ïÞB:Øî,VÕœ®l†Œ
+"ůœÈÈ…a}|¥PL_Ày=NÃ6]ÓÚwe
+|<ƒŽÙÅà ö²Ö³—P¨Ø2r`F­:éŽqÉ긮â\Ê„°—‚Šyb\&þx ^º=F¤Zü³üÄD¯Š›~˜•–KVEnH(—@¼>á~hAuh=JB¢[OòMÓóßs™­ÐMò ŠçY†ùB³®í
+Í¢ùÈJéQÂÒ3.oRA•=Ð2<9
+åøŸÕÇy=™^¾oòžWRz>ñÓŒ(ÂêÀY’êJqb¤{§D/¡üRàJ7‰®ò!
+È[¿fŽ âø à”<Äóé5‹xToî%+‹tùm<ŸšŒ*}XŸJ PÒÿìeè½5‚ZZD“è>|¸Gó$¶ˆ<}UÓÁ%ùkÚ¾ðÙœ—ÑU73¬­ðf~]Š~x‘ˆ>„uâêiÛø®„²÷šu3jwhâñ8*É­/Mï™:¦²—íÒÍIàVÔ¦ì*\—º}XE Î4,Tû´£€²·m}ÏÍE诚 à`çæoE¢$Za²j{³¡ð#%/Gê§b#”Ý”$v fˆ’ÞŽ†¤ŠFxY\ÜÈ'þ/B¶{yšÉ$ûû­zï³4~ØGäÄ[ýG]†oÔ¶>‹Šs$‚NìGDjl†ø4¬@ZqNh¹tîePBþRƒ-R|ƒH1‹Úñ§þ.ÉËÉ‘C(ÁõØ BÄS-5'²bâŹm¾RŽl?bÕ%’ÈŠ<ZŒ&“Ì~®ôKÉ·¿þù+ù­‹cÉ°|µy<ëÛ,%´ñl_þýP„™ê2·ånS’aÀŸVL '„ucª=,ÃK •>%-aOdxŸQ< ªdC®[_£>áØp+cÁz/ù"qãô’IûYaÉK8_­×êøj¥È
+ñÎg«Ñ¥X’Q¢HñQÐ`2m¯ci4uöc—
+Ì•z‰ex,$j„ö:-!\À‹÷6L>TŽ¢¯]/ÑP¦¨{¼m{†IiV§ôRô0t¤ž+$Þç%žšÅþνÙ
+‰=«QD´Ârôáæ,oë@\XÛ§zIS¬DP¸Ë.A¼…ôp…gwt
+AßêGöÙ…c_N,,YN’¤’*Ã…Et¢D¨˜¶u}— èt=ÂEÉ’êÛ8ÅVø@úÇfæ}× q0˜yn_§(–0'¥¼œ ¶sÀ|EIâ]Ûþì8¸ÊJ`fçVœ˜g¯²ú çì ëzb•q)بþ©D³P4¥)ÿÜéYƒA×E¡kù:—@w±r§DÞ—iC“üNp‰p©1ø (HhrRÜÑصMe¾. s›„î(Adx1Üc±h&Á9\ç%©PTuªÉ…È©4¥±Ùàê*ŸO,dH³Ò¨CRÄÑc‹[ѵ¢›ËûN¸&ž4Ë…¢EA“Ú-ßiJe(ð -~®@wzÉs¨(:§òBµØê7“÷M!8Xv]Ä·~r,ÇæÜ‹G Áy/†…Í¡À¹K” +…‹fѸlÅ5×]2”Ry2l؃›™¿©ª}ô*¦ãÐ(2m
+2„˜!¿–ðÐ{cª¡¹!Tw+ðn<ñX(ǵ¤îtÕ-ª7Ñl
+8抭*†8¶=‰qG8˜Ëv?q–åÁ¡¦0±ø«ËÐIÈÏï”ù0yäX¤*°Mw®A»SL²Ñï°`Ø\pJ…—6R}^êE ŽljŽ“]‡
+øCÈ‘(¹ > ª$~@ ÷ûTÄv¢Sì‰"oéÊ‚ÕP< æŒ(’æž•A¾dü`áÙËY‡Ôc »³s‹eçîm\l¬cRIBÂs¼nY
+¡øøÝ e†
+.ž#ù!ÚâG˜ZX{œêH{˜&æ^&ßH¼ŒÝB‚»]åx?ù0MÜh^Ç‚uq >bD;‹¦šã-íÆ®C¼»;Å4½ãõF¬Â *Öqí`Ý"Øâ
+Q.çÀ´6»ÛÕç½ÝÃ/~»NfûÊñF©‚9Bâ­¾2ð’óÉ£h—ˆ¡r‘~­L*’Ÿ‡¯à"5ô"J Š,/OX«3©;ÊÒ w0ª}¸œß £Q¶Fb<£„ÿ¢« Ô;.óJ›°¹¼WøFb N¨¾Ä*\’y784_Š2uìw°PŒ¼ÒØÅÆPÂèw¨Ky
+5°Ð”}( ¶.áÚ{ï B¹m´/·Ã ‘L`•à¡f×;=\0uÚÙ€–÷X‡)H2y¾Œ°É^²³± ¦
+Iò°à‰q §Qðl9í„_tV õ+Àòxˆ—ÇVkÖ`åiqŸn¶xΗig/¡©µ pºôÂÔBoøi &‚³ý:ZbÄ
+•±amûV=wA`Œápgº²‚L©ò¢Ç/$”#M¶0ÿ Hø¹Ä>.A+<h±7ã—‘ÁEù/IK,”(ÐO’ßæÖ å¥ô=uí•|ln
+:Ú/g‚BBšƒ—¯æs\䶹]ÙG]i[‚ËF]é:Š ù`,WwéÑR<™þçhÛˆÅRÌs‡LoARÄ Àòãí¿oýû-½ÿë?oýï­íôü ;Ø)í!ã„i/ÄLVo0¿CÐDwD@,1ó±
+´”ˆrŒß„7örsßä~Ô× L° OHwhËb,Z¥ 0Öz7qnËhBYuv¦;+Ì;–" C¦b£† ©y‰öáì(Sa ô„À/õí;MCÁÏȘÈ;³Â× B‡ú”9‚øQŒ!@„¦¹ ¿Ã4hY}­¢AðšM^Žœsëƒþß•aò™‘;Bv c”Qz¦øÔw°ºD .#Iˆ7Á(ÂÀfÚ‹2žå`BÏwà4•P›“×—…âAT‰ þh-†|1mˆ@mmì?â•”Ž´dvåaËŠ»Ç
+£«jZùå<…™|˜DÈQ€oÐ/YmHIª®z¸
+ê,_þ]Ìóâ…‡}ôDáV=ƒþat©ÊTâP™~¾ÅiŒƒ ›ŠVBÒ/`gQµX›‰¢ÙÖ©………¹ña±¸€*ЇCFmìGÕU½­€eìÎIæG üxZ2ãØûŽÒ°5dÏ©,,BŒÜ ƒ°øL»Æ¶E‹Ðñ$$ÏDaÆÖõð§ÛL‰%sÈ:¥D¯š¤LTšx5-SÕ‚¹c`°™c…5Ž ŸzuŽºP`‡+(ó=¾ ‚[¶Ò…ˆß„ãb†d.ŸƒžkÓÙ@økË^ð“¨Â@ðæ˜K~ºkØ©'ˆÅsǃï<ÏÍí6ÑüÝ^Í1hŽ]
+>\÷m EX%|hŸ~½U?®å#½nRCR ¹Ó÷oo(©§+qÚÛŸ1ÿ˜ˆ5n‡=ƒ~}çFΘÈmÏÙ²aÁ·~hdô²£zÝAD˜¼(Ìb ´0š)%:ÁÉh;ÃY·êdþ5°8ã˱pIœ-‡§lZD,ëjkD­Å|&Ž—Åm…ßÁ5’
+ÙYúsþUyM¹©‚¬ÌCõ™1#4e´å5nyÁ£•ð;©¤ºÔ?^¦cj;åÇÜùÅ£=´´%5vøøHq Ï£u9}XÌ+<ÿ8‚i-O Ÿ š#XÐòÙLoh Ù'’®›<Ú‘¤ÃÈÄx·úXD Rym[y6¿EZäû9;DÖ£MñÛ2ȲÑ1ðy9„֢ɸy¦Ð*ر‰[±€‰O§WØrv]vŸN¬I·QîuuâSÏóyçÚd€úb,ºx–ñ#h±_áwH
+t²‰Ê<K³ÉnÄÄz”\M¯çQä‚…ewëÇ
+S‹ØhÓ) »25؈j
+íHT{åp—yÔj˜f˜%׋-“©”xþ<œ•‹‘òE X%XXI$ûgJRù±tì‚O!~EcúôWôLSoïo%ÛÜ L¯¾óÔíûmnS½ê6}÷âÜgøVâhÌ?µêçwF»•l·Ê£iöÒ\Æ ¾÷;aQqn'm¯ ¬ €Dòª—ÆQÚÜ[»¿U颿6IPP8Ät^ aÖáOüñ‡=’ÞCy$Q½!héH­*„ž%êý͉5ø+„¿ž=466Ð̾œwë2œËå
+¿¢?’‚PA¶ëó(Th“IaNĮّ™ú*—\ü©•‚ëªÜNìL&x öf‡,ÙP«ž®Bü…'¢EÉu0}jý¯o0…²l —¶+csͪ2ÔÃB7'¼æOï­ÏÑ|@èfáù¬Ë§È0#sçj?^ŒeQµ0‚½Z‘[æ ÎÊÉ è•‹çÓåäÐòÚrÌŠÞ^|‡>‘b°Qšís›Ê{¬Ã>ÈhPÕìáU´¦ñ«í»Ì´ó$Æ$}À wù‘Ùì3M•[CÊx ™J
+º$”ñê‘ %œâüsÒûéQ(ãQ(( n{Ôó-X ƒ]/Ûb0©Êá2Œó\ SÒë´¡™%ÎŒÁÆ—´qÞÅÔmDZÝ «[Øë”ëµÉÍkíÒšƒŠZ9kÜ%0Wl>Í2ªJòÕ–äõ“ŒÂgùkY˜ñh(8 ÿ³Äx1ØçÅgà L=$¼žŽóè.‚gjyÄcæ ›áLI¤²t€*(ÆÙ95‰ A@¦ ®³l:2ж¯ðš‚À Îîä&f\í_ü÷’QËÕeP¶œ
+@J§ÚLÈd*®êÊ9Ÿ9Iæ.åï¸í(4ê\™¥D¡êÙÆ£óMbŠj§Âò§•ÓA¨6WÜ?—U_DZ€OA:íÇ{²ßƒöX·7C‹pÄs,Iì8g½øÎfÀ¹ {æ‘ÛÀÎ0dgd}„™M ‰±ñÄ1p«i¨3ÃdGiÎyê’A¶!D
+Ì° 5í=XJœiAפêG=ï–ËU”·èÖŠÀJJIŠsä
+241!uÿÆ–™¹ôY½Cº‘±Rw·Ó:à˜á€ÆN_îŒ,/Å Ó˜FˆÑWz!šIßmªaµG^ŠTåSÅ¿} ‰„à—˜·‘=cmˆ Õ#„AK²ý8æ÷€^Aw¯|AØç$3Ù1ѲL0/Ù<#HÍ©x­@×ÕC•Íáâ/Ú½F¡ËžP˜úÅ(ŒEŠ´,iÖNâoÄRE§lèqª|ø¬#Ù^«sÈ‹
+¨-‘”°—½Mº7ô6•Áðy6{é-CÓ¬&Ú <%gÀiÏßá9 µ…{%ô;ø0zIâmI)ç¶cQÑÉ"|×2Cî“ [^Ý®NÁoïD‚@&A -ÚpA`ç‚YŒ÷ç×5!Mñjà`á&ð1ˆÓÄ•z
+ÍÙ‰d‹\pÓ¹rß ‘‹D«™¢A0¥È³ŒÚ.HPë(©öQ¼“šH·Càe¬ 0õ:Lí¬Í;.«Ê9zLhH’qö&)Àq+] k+f")h‡Íð}JᎡŸ麦-Å“K Òõ¨?ï³o%=ÎçÑøÐ@"IþcùëtÍkŽË7Á˜#Ð|r#)¤þ
+ú|
+’<ã¿;Ë‚éEùƶÊp+ê!…ä=š¹B“蛄dD–ì]ÖÊ‹™—f„f¶)‹©zF2¢“MÁÙ”[§DåϯH¦H9éþÕ9'ÃimöeÛ ¥¢ž7“]Eı– ®#CѼL.` ]…SaŽØužè_‰ýü ¿o“ZsŠ€’ðS'Jøk —U8|J'J
+dä&Ö°7(Uš‹‚î ÈÂ*¢E°GT¯Ãü‹X}fšfnÇUè-„bõ²SbQ °Ì¹-”Ÿ‘å‚û|zñÅ´†† ûN$ÃÂ0P«ìÁ·ð,hë@¤S~$¿Ö!?J5¶SêiCw£T»-اF…Š±e›ˆ©nm“;"¦ÂpugHž¡Š+Ó: ̨]¦‚Бý¾eQ.ƒ!mñ9tWÃPÙ6&©°$_ÆÍ“E5ðo6~íM9ˇՈ;-pĨ¤9LmŽ…e"N —t€|ØV SZYX†üaiœÂ Ô·ÕaÌ3†C¥¨Ê@ƒêtñÜâ Ùè¸ía|{Ý-‹@DDÚ
+šehóµÕ÷áþ X%ƒhPá¬Q_ÕŠ™Žƒº¯[åRÉMžàà÷ÖV“ÀX²ÜÞj f‘TËýבE˜Ä(Žc~×Çÿ{b"˜¾àÁ8ÐÐßJæ¦ØÚuƒ.([Þúê3|ÖD0¼ît†?!×ñËfՠ˦X6/Ûð úæJÙ›©gè¹{Ì{'„’Ep$Æ@äAê‚bJ‚Nú£ëã'ƒ¨;nþý‚ðöa»L@™õa ‘‹TÃßm’ŒnßT— §êcúµ©;¼‚NTóÒMX뙼`¸â¤¡ÀÆ4[§ªÁq-ysŠ,ü„ 6Kr@¨=°îû|…Ðícê ÝšÕ‘Wyk¼šÏd€¸B½ 1E&*ÿÌî.³¤%Èmx•hö¢h‰¯h}ÇMÍ=²Àœû”'u{£úÕ…R.¿©Œ+ÜPH"uæ!úr¦¦ èýTµ;n¯šN  A§Ö÷ÒêPNÂ0dŠç'b ™ ‘¢A
+¾U òwðãe’›Érá«øBÎÃúm} o»ï¿õ$SvWåµa4$ª2“ Æ œKÌ›²ƒňf¢KÂk¨;ðÊfm]"šr6¯¯zM¶ ½¤¥ÇùüQâ£`1i ˆR¾#C{؃rì‘6¼5;ḣ*¨Õó*ÅYAóÙ—Þ³#ëØ#À2e¨Š3œ<«$;•ˆzªøØ‘ÊüP¢Ní-'½ÌËjaž!XÚV=‹~}(Âî
+X%äQtÌ÷7å
+ŸÀ[džlôW‚HÕ{H.¿à‘
+Úû{´“‹L “f-™É¿T F"H«Ç1PþN
+kS©‹‚Ž„˜QùÖ.%~ÌBaa1¥ŠzýxበÐ{ïÔ*!ÅBXIY“Ÿmð]cîð:C.x”Ù4±w‘xEq* ÕÚÄtk~ûÉ
+9 þlîyUœ‚’açòõ;À%4xK8v›F'lG®¨Hš&‹(Œ£Š~¸P˜p‡Ö®¦—6%£[Yá¡ål¡µ¥vöPY€oÚÖÎvþ‰õü‘Û%0¨»ˆ1
+¯hÓô•³hL‡‡=Ô^顽Ç(6b·h.|~ÅÃUh8˱rݼ.%ÿ1`(žÀWHøÑÑ×wÀ/¼«£D>w«ïFsÁ&"û»ä‹+ô“è:#ÎÓ®G܃ ÜAU(LQCSÇU¨sÔv` ½"J-|Xú‹d\©áâUt¹T³ã]Îëo‹žøëoÖÔ·9 >»Áw£{ðD‰:ŠŒ!Ù6ߊ¯\Õ§‰Z _FÎL{¼„m·øBpó’& bf€Æ+ØÉ&‹@˜óÉSSÅÕrK˜äË>CªÈ$à¾{”3²²©š­’¢ÿ˜‹bôü,ñ£2ƒèj-·þðU$¨Kº®·²+\­¥¥xñ@ÃëxGŸÅ«9”`ÿ Ñqê×ßJþUõ*"Sá`X[ÒÛe²GØÌ1ßÞ„Ø ÉæÔѾgoú—þ‚-Å\˜Éw˜,Ï”é»^'õ(ñ£ž¿|牛Ëm^ð»<êbŒ nÎë‰÷.0¡Ušx&µ% ꟳz,e•§qWýXõß>N&bç¾]Ä“^E·,ð<íÆ?ÝH”‘•ëü"JâÁy¿EÆß·"ŒIu²ËeÈüQ‚±)²p0c÷’Š55oÇvX‰´k®!aöŠ.&&¿rª!¤J‰ c.Ì|ã$2`• Hàê’âϲ=<’,zBòqSÝJý€V' Þ.%:È¢ÌYbÀ÷ïll÷B)g„î’Flì"[§Y,…
+á7^WE¸‰i–÷]$ÝÑÄXdêŠ&¦Ãš7F³î~9»2ÍMR¯1ÇMLßO¿ˆ2E ›5XxÚtSnÚ¬ 
+¶NÜÈ÷LÁ¬R•1=½xèP6U+±Õ¡L+J¸J•þk1¬ƒz<Þº
+Zn^$íêÒ¦è[Š,!¨XgO •$Jp½}KI­M;€¯»”Ä–â:-¤´|øNÒ&k F;G±m
+ Ý<‚6PÿÛÚwÏ·\X)“³ûÆ+oJŠ¸%¾Ãïih&,'–x˜þ(A~16h,Ý°–#‰FvÞKèØ4ز
+ãåUö„rã\!Å"1/d%øÝ8[Ôù'–óGbÿŸëià[4>øH‰qår'Ž^¦íK÷Ìà.‘þ.E,#ȳyœ¿Ò¾­äØ]"Þ\`úøÚ†L€w_¿N_MGZ–Ð{‹Ú˜ÂÏü3 Kw±ðÇ;UÓo¥ƒé%Ks˜ÿ·eÉšæ3i’ÁðUD‹HH¸v Ô4ª;Ó@¾Šå>ZúGÑ/ohåYP¾ÑÓ³H%ìHvÑÉšÌ*c± Lc°&·4š”Æ\¼/»¹@6MMÀ~twÅÐ9œÒeî·µqe¿15Û…vc$q/EIÈFõ*Ê´ýÜ@ìG½‹´Kä0lK£@fˆågZ¤;’Íhþ*
+À4­Nó³È, 8Š77Mž•"—•À5¸202™­³—+
+ðzÉæ)/—¹nŠ® {è4ô¥¤±3ô.<íìšs)ÊpU©` ¸Í~’HÞ`Å,ïVsp±e÷è¾XV¯_YªÑ{ÌöÀçC¾/l§yàáfºõûw*Ge¶…Ö”"¾(šÊUf/ßäÂ>ñµx7“ç–¥nI©ŸB) l2Dñr 2°ãöï`£“œ4‘)Q`¬‰4«¿)Ñ°GÔ8驆•Ç êϾ±þeØvH£€0‚Ò뻌àWÍ)œ
+¤íJüfLŠ‡­Ä"EläEx›*ÐÝ€:pEÄ¡¹y†r1FYüÙMÚ0$BÝ¥äÎOOEßßõÖ•:È/c‹é­¨&¼HY槄¦/ÙÆÕ"ÂÈ$2SdÔÓ Ô,zçƸ£ðøI«#_Ë;›ŒŸD8㈗º¹,lËÖ 8?I&êžÄPç±Ù¿ó7„ŠÐë,긔ìQX« KYß|‡‹±×ä²r¾CÂM0kåͺlÚòË<é®sv8¾–h` ×Ýa†t:à©=}·|šCœHJ?–ÊxSâyBÑ¥}è`¦ê²²K?³Ö¯E_oŠ˜IF)ä˜|—õjì:Ï+.œÉš…GáØ7„Å–Åt‹”´HšSÙË"J‚ 0=„ÅDw*§þ
+ÞÃÜÔ}ešÅ“=¶Ð6MCòPƸϥèW‰ýú–M%tr'xHhwS˜ '" a&ôÉÕú¥†™"EðS¦™ËÙ²lÙl4£û‹¢`|¿ŸöÛšgúu¯Á+‚¢ÐUçbjU Ú6†™â&<y æñƼõ9ä*AŽ–ËW‘"@ANÀ‘Î3yà¡×îTHࣖ|ì)y´X]&ï®Þ渔ì“X¯.ÓßR»©Ä´äÉÂè£ÏÔ†9>.SxŠßn“º­G!¬`V| ÃÈ1ð$Cåg5kÀdªõê‰èM¦¡BS—Šýn~›^òSöòú™Ì+å€m0þqcö¯€(•ê%˜Dí¡9a­†j ÐåGeZ\/æsÖc*%øØp=Ü+”Gqš°•q¯†£YóMÉKJz)ºm:‡sÝÂSqƒù/Š^Àw;øà†¬çg–‚OäFIrf'VTÓ‡ñüüPÔr-zˆK*#sCDw®XW±&U´› ¨Ä|8­󒔸)šÁ55XŠè#Ŷ¬Nv¥æj‰ëÚß©`¯6ádç¿É™Mº8÷ߧFµ„iΰ¨\RQJålP&ê
+k°H8#Öa*ŸZ³ú¦¤á‘`yšG½=ʶ^Eý¬t«Ôãbå¸|^Zó~ßd–2,&}–»âm3(n‚Z¸f=ö©*/.ÅFóï S®¾:ÖÓ]À? Yß8#­_ ¼s\–ÀÀO9=q¡sF€
+a{hÎJ<uÓ% ÁÓ“À#Í~sÝ·
+¬14 ËÐ31˜ ‰&ðQº8Ê¥èëM^.3†_+âx<¬ðï%%…ξoÀ
+0
+3Ä&J˪í“ÄH¬c /áÞ Ÿ‚‘eó†î“ª¾83
+¤ó/kUß8<-„|›r`>Ùèé;l,{‡ÖŒã;ü)33å%®$²¬c[3û¯aêû:ú²´Ï)ë¿ÇDC7Ë7¥¹N±ÓY„x_ ×\lÑ0s‚äʈ5–Ã`õRôµ‹
+Qbɪƨh…<Q[}ã›eui`=æMÀ ðz`·A‡_Â.oxÒâ’ŽpMûÙãÎ>C9ýì8kÉ+µºIK•$`8µÈ;ˆ¤Æ­×}š{m°:òc´òý+?ŒëÌ
+SQÌbÖ+¿Œ°Ô]m¿G/¡›‹Šê€jœd3Zy‹òq·˜½È<Ó`û¦äÁª~ÃÑæ9°´PŒ”Èu!þÿ¼V
+],] “Ú]XA%]Êæ+’ƒø¤ ™ËµDÁ(uoÈ(e3Ý­¨ÔN¤Ísã¥æe¹±åeÏPI4Ø
+H‰”—Mr9„Oà;èó‚
+{¸™ëy•ÚKï^ç7¢×lÁa}B¨cÕ0é!}A|¢¹Ò|ïä­ KЪºÏëÒ& Xç:1J‹NyC6¤%ï«r:é'úeá`á
+Ü­{háC=@þúößoU`Rã
+ô˜fìçÏ0P+íý9Aƒ+ µÎ¦—Dw^ ý`»òR(“•§í8ÀP©PMºUN3ž—IZšæŒ×ÈÏD±´k+Í!/iA’l&$Õ5Án½DMªÇêÄD…è-iHÖœ=yëÙO§"ñUPn×›Z¤ªX›]=jc9‚ŽˆY!ä-ë*¶Ä4^ŠÔÒ
+c­ €p4qt«‚f ajÎBÝ^ißI±šõᆈf”À?iÅH]wÖ‚ÂR §¶KK?@ÖV´‰bÀªøµÕ}2P6±!L,c-*bÒ !ÒJCü É?&9<òËèòß¾:Ž"ˆšC…¹R2µ
+uغmZ˜“´«œŠÝií¨œD°ˆ$Ætè9 ÆZ ZÜò¢_•Æ)
+cƒîgºÞ¿2§ßç8ûkªŸCµ:¼t$H7¤ Õæ8?AÌ؃X‚ ¬SA=†…yg—?
+ºÂ›¼.!Æ|A”¦ô84éiàø̺9®Ã?òŸŒí™hÞ@ „ÇZŸ¤‰BQ¯H!x£ð9"4!Ì,Â;A¯çÛÈê%ÂÂ{­åÊõ“uní>æF›Ó­îì{çÁàG…ÿШ_˜•œª²§Š½iFŽüŠ¾·å~Añ"<ÁÌ–¯UŸ§‚J…™éh\ܘ”*zLHë<f‘7^q»Ì6¨Ìå›×ÁÉÜ^n^ÞNRÎt—qÊV/°³L‰õ”èÏm¿‡þdfŒ#ãÀe×ïÂ[‘ È°õë4éÏPŠOQuh*ŸÈÅ0Ò!ÅÂëR žE²0ò¦üŠ‘É&n£)oų“܋̤ îÇCK~ƒaTeAØ9ãªÉ"áxñRâ®åãsÎñ ô¡õï'¢A‡‰´ðµT›ìVtG±)DØûÒžˆQ–¨l£ÊõSÀû®20ïp
+Œ€£Øážwg¤¤dÇmv·¾’ÑŽ{aT×8ÿ+}€SŒsì¿æωÏ÷,ÆãŒyˈTG ¹QËqS¤)»hôX‡°Pwr^û,š—&+S‰ qDƒ 2ô=@ðÁpN„ Aç}‚ §ÊåÊ¡L¿¯™›qˆðÕˆMµÌOC!÷éêwȪñ­„ÏumÈÓ0ÿ„ÀÑ#3˳™Š5ŸQÐNx®­n´ÈXLÜ£Î$A]ëÜÈ´ Ìíq„'£Ck×NÿŸÈ¿‚q!¸äcBÉ’úC0>€2µ1Vü>_l !MÍΤvczÄMCšÈ”AV·>¶¥"‡$\ê[Ã÷ÑÉY{n°³èô œ0>+Pgd$Þ`Ž§D $é‹
+ÃÞb‘>EÒ0W£ZÇeÚ|uB9òlñë0–±Ž‹—#éð`2¢1Œþ éùÄ’
+d†yª²Dà U[ïů€îV÷}néŠôj—OA8lO­É¸~ˆµk[ÀjÃÈèä)Öf‰«Ô‹8¡GÓ|tãä™45CkËV·c°Mjð’©Pˆtyݽàú GXŒ[W&·]‰tª.5D[çN0Õã %zaÒJ”kŠËƒï JkY=$GO´t&OCþî{«ß ×?ÞC$‡Hlç½t G˜sUì½ùœCŠÏ¢A¡±øz
+”¡þ •«þ:éÅàj!¡úHÌ^•Ÿ
+¸É'sO‹ ÕR>nÝâ¶U_ÛÍ
+n ;½·Ž¡–®à.ÛšwºÓ˜ÂÊñëÐZ‹å‚Ⱥ$9ƒ’Ñ÷£bm4ÄG0O8üU˜m&Ñéé…q ˜Ú¼zÅpÁk-ïùÅ ŒÈ÷úFÆvØ)B*h‚œå—(„“si'È@b›ƒ?½/ 7:µQ1Ä&
+ª"1Ý8è=ã úâªÈ äÙU¾ÅA•èq"ÌŸ@˜UÄ“òIËõ°_aP— s‘”œ§’>ù66º 7/jX‘ioûaYµ¶-i:䯒ª¨&w]rsàSt‡AÛ¬ð.8<AÏ ƒväÀ“´Ð(/Ä9W¿Ã ó„Þ:oMxFA§•wt ñŒ‚úQÐè{üˆOTiwqQ槹QÐ+zDA•ðn¨XX&üi TÂû´ÔP£U„DqÈ"þŽN´ÇvÍUQ•ðÞVüŸŠ+´~ãÐå"¢»Î°¬ÑMÊt² ç+ zEWÌcˆ¬DF
+V¿Ys‡<Û âÙ2œ÷Š2³þkæÝ$h×Oðz„'ñN´GL{kkËï=¨ÕiÆmÄ€ïd(ÆË‹;l™“ a‚–©GI^4 rµÁÎNtàþL‚þØ<’à7fëŸ/ÙÉ/bMªÐ]qõeç*ºe‡’ž øÃ’}ÙÉLjŠ%3¡Ç]ª“åò±Ïò¦;Y9Gݱ®\”t íÒÒ/pÉN˜<›\=pü°NãÊ° \V½Ì¡’® ÓUYà²sy²Ãf±(Áee^Wv82;s ,S=!ð].®«{²ó|Á{™« ·î<{éÉΉ[vnô]²ãô);ñ/ÙÁÖáU¡9ÍO²s]²“0ÿ|%W^=EWva"ïŘŒ^?ÈNæ[V@ ±'WvxëVpò<c©Ù•‹ xVv²}ù$;åE3+ˆ¤Ã—ò›Ž‚] ýKßd熳#;w‘#)õUȬ®™*ß-ò4¥¼p†ä>¬yJýcd¤V*ýƒðð
+ËÄxVWx.ÀßÂã Î%<œ®_ÂÃ]kgþ‹Çø ÷± ÆjØ ’Ý0­Ø2¸§M§H%ôŽa¯|ðc„UÀ-E[£|xœ·©•L±ÿkûÐÓRºMê–’ª2%Ï×2ˆn'bˆ^|# ç
+«”؉'O¨¯.ñRhS§î,ÉåaälušÍô!um¥†2 IHßÍŠy Hƒ¶¯[xfŸß04Žv¯­4#Y¦mžva˜,|KÛàBjC (pÌåþþ€ã_ÚáúŽ8
+wLX´ì,ÅtÆCó‘Îëá'_Ü[aZbBãГsóßi^%!7T:1âeBîÈ#/_?”¨ äêT5@~L¥bmš†Û¢ïóøø $¶¯Þž9cåÔ÷ #š ›$p5Ê
+3ÎoÑ¥Å"Y¢$猿bF6ÜEC¹@Pg`m3æ™»ÇN Z´.¦d¡Àubd2b¼hàqÔ4=CîPÅ}§èwyýù –C%8Âä2ä×t qÆhCUˆ*#ú߇¢7Cc%ÐB—ß,uëˆîFêªøЬ‰;—0ù%' f ªE¬‚™Û:!k.H0`ë`Ô eàyÅM†`0¿¼"<»JDÒÑ&§n“Æ9åãøc.â!'A•- 4u/ƒ¦0)Y>QóGHJ« —ù¶h±UXÂi÷.üI­iî
+Í9[QöFðØd!$më…|xĶLDN ©ñÛy°3ã Wk[øTÁ\%<Ú–,žI^ÂÀ¬­òK{sZä ç‘`ú2¼¬KQRƒ™hó-1†Øª´ü¡Äw7æ‰g««è}¸tdjUdZ÷ˆJ‹е÷ ™£¡![®ï*äfc?1Έ çfAx’+"²54[E“_X ‰í
+rÈ/õ‹½4d:Ànë” é!O¹UOd/5š¦yÌ%£¢4ƒ`4§èç‡"Ø–¨A3mxtR ö^7•&!R^Áú¢
+×èrÙÿ1^&¹•Þ0¾ŠO`h¢†u²ô-²µï¿ÍWåÀïéÅ # ¤›–ô“Åp
+ÓöÆ=ÄVg¾Ödââ½õ" #„Z4N²Òñ~Iq.²$¯Í€šx»ês"5éSB••nË‹d˲$>¢ªsi\ÏiïlÀ8ò²8‡äfIä&ê’q
+˜!²¼ÙS†Œh†\<ô|Q[4± §{ Ô‹pÂ~‚¼y)úÜEr?Ñú]„ý†ñ“Åv)³®°»_£D¨ç {)ÙÀ
+Ô¸HY—6ðÌy–›FÂ+$Á\óq°„\R«›²è?;ǽյE% ×\BÀm˜?+6lød91.²~?¥³ø8¾Å,Î=ú…Av,áøœDˆak³§> ¼²€=—|uŠ>ð|Yuãrf#Yx»›K+ŠIô—ð¢ä‡Iý /›ßþæB,ö7„µSºÑþ—5©KAX«6ä÷"aõ“
+£Ûª…›ÌRfa—€ X°Ñ47ýR¬AÓá“ ¶s)™æRAàkÛìUg/ôs}ù©[l8“á„ŠSº‹”‚_D7ÀÎÊ·C€ Ê>e™ì?ÒÔÿåÉùme˜ŠÝ¨5¾ÅÅg%ÙÖ7‘ñ9õQuRö];ìçˆï
+pªÅM¿ ø㸋Aêcxì-Þ©ÍX5³Mw—"4`B“˜Ž.þTI—­`ÔmžeS®™G4/ac‘
+ôÃëF‘ÌÎ4A6Š©Æc¼Í$¢ ÍÌ3@4&û~±0¯a/—tm,‚h†ZåTÿ´HT&Ô‘]ÒV¿k *«tÌ\Õ(–G\`‡ºMWäcaƒaâä©-OU*=JL,Ó(×i!z£nÞܨP„õIºäÞSÖÖ’Èw«^ù‚ìµ÷”+’ ÃÀ‡’ 6T¶W±I÷éœAþ;“›Ái” ÃÌÓÌS€v°3åYœZ<˜Ð*/ "7ì>[Ôú.âßáÃœ¥ŠË•jMÂW™ÄÙ@BÍîS^ºðmdÂ]Òà¬Á¾srqd u‹Y=1ĬÐÏŸ®z³[ ßùðâª@LÑø‡kë «ñQUÛÈ– º¾‚Í\´¹÷f—’=MÈkx´b„×s #$«M>Ê+°â¹·¾‰
+—ƪyåÐ
+Ò¶ÕMnuÁ§Qj»m‹ƒP‡cÚOf—ÑÆ(á«'4‡ôĬJ$EöyŠíÏUÏEq—‚b˜í«dűWØ‘“XÙÒ%ž
+Oy〞ôÒ]*óÜä: VfÙmÄàs˜FÊóê6€2Ž #˜Œòµh&×ÿ¶«ûéKl‹E'„öî‡m2!¤„M›8$[ãlkågB2î )¢15w%Š{ºä«Ù
+M÷sj1ÝïDìP döhÙ½B3€Ê(qÏÏàNÌä!Ã1ç‡ààs~V=Ñ–‰¡Vû®àUb¥RÓŒC´ó+±µ-ÆÄBà-Ö­ßEü ÚÀXø¬R‚k¡'òÊËtM±xðዘyÉ—’;3=}¼("ßfØ™Ûߊ*ª€? *›N‹,N®ñ"6¡2N¼kÉ^Â!š7«×ÃB$¡¦¢³c¶
+>qô½Mž×r¿Zw™ ÜédÓÿ¥¼L’ãÈ• zÝ'(Ã<¬õ—} mÉûoÿó
+/ý䞤:†ô=[ŒcCèÐÔ±xÕÀßånöP°]¸y&#&¶8@òC´;®™¶ô@r5 ™ûX”ëÇÄ è[?D¬5?¯G9qŠ×{½¤!I—Ùi4qßÇý[\?ßQ` š­=DÜŠ.ø‹‹ä9ÚÜ%Ô€põò¹YìãLƒN©9<†_dH ªˆJÔøb€š¨¥áy棭%;Niϸç)B6ëáPQ!¨éGÈäDî<Wq)f¾ rb¤ z’‚0*<ƒeÝ ’ x"ö•ñ)¡ùA²‰$VHäV/Hº­xN€@˜H 4÷ Ó¬OQñœšŒfÈî9 K$i&ÚK’¯Ù¤7¡Žp Å-I’Ý© ü¥¢OI ‡Ë¶Ït‘÷S’tÆýL’'Èkz}ðK9°rÃï‚B}¨¨´L¶gè¼Ó ôùΞ®uÎü¦ ‘ïÛæã„0ýQvïË DAøþÄäVVÔ$_6¦Ge@C‰5É„¥]:”©€ˆñZ»\(ˆ<bX’¾yS’©%qÚ9EB/’Ú>±Â
+ó EÁ4ß}.:YÄ£´ïqeÄV,ÉúØäY &>§OoÐ JÄ­Ón|Z挔¶Ÿ^tK‰a\ÓÁ傦‘Ê­*Ó?JcN7'Í<ã˜T´MºððI}"tª±•‰»À 4kRiC %xF¯ïGÏiv×Ì}‚A’WÎÔ¶­(ÁÁ#51þ°c’ £âGsC¯Áá3Õ°\ŽÌY3=2#%ÏüY|z‚†< ªåÇçÊfÕb'‘Úùò»–¬—ßêgꇙÐÚ‡Ó…S¶îÍôÄ‘ÀÀÄFw‚'6WüzSEÞ÷]ª’$>ÂXó9KEúb‰»ÏÆ?2È}‰u#$vüG-®FÛrøîsëîÂ÷û—³¿ž:ê]°9UÌí¢å D4ƒ+Ú³hlѬßþVªü¸IkêôHÉSGÕ˜õ€¸ÿö2³jÑCfÅZ¸BeÍeªŸã×Í{ŠCY4|„—Ç$rÅcxúu™‘äaÓç$L WÄö' ­ 5J'ÁyW«ße9±m“
+Sa¢¸ PæÀ;Ð+e½¼K‚ZÊÙ2‰‡O±´xÛU9BdXã² ;ˆôƒ²Ô*ò­‡àý š¿%­µb«jgßÛßPBâ‰'|¨aÙÏT¿GG‹ô!Av@ˆ¶T‘Ím‰4í)ÊêMÚU=NÊ Šm.T¹Ú;})>£T [œ&§7´ ’e˜Š´ûŠ~LSi¼žý*)Sô ‡ÐQß VÅ×JD`!÷lÉŠô':pÝ=&’câ”ï}ñªùÀ&V¸²>î1 %£Eÿ+Ûtõ @cXºƺm¥1/¢L—fý„8üÈRÁ­dŸwAº g¤:d[‰“‚g½HÕ!/@ïà £2¶P¶à=æ,Ú[mk
+ìS*u,&<01"H¤KÞ—‘”íÚÌÉÝdyr¹zìÁŠ`wøžø-Ø3ÒÛý©”礼äPÕj¬}ôlŠøt`Ø zÚAë&-j »rz}Þæ˜ïUÇìÕ9'ø¨±7äO­úóκh³ÂÞ,¬•æ=i¹ÔÛy_.ŸŠAÞØñÐí^) ÊlRG®.GÌ¿Wœ$toJŒušRØ-MÒô )íýº:J¬Ôr3Ò*ÂKr™«!P,´Áß/ots^ïØ‘MIMŠ&þ9L4•DInK—)dÄB—J_C/Ö…¶ZžWÊägT"À¥%íO!äð}€[Çzÿ­•¢L†!"äкaï§0lk\’jÀ›Ö$’È_üY2èžzÿé  Æ¥—«áÒver&‰÷¼Œ>u§¤£Å,
+6y&}Óí$/àêhûéL{ ôCóô"15Ä !ÏvN`V‡f%›½‚‡é0Ú1ølyƒlV¦äp4šº{ã…É…€P¦<®ÛÈhcí1)kLÙf™å§ý*V‚¡ñZf¨=Oå4<Ñ®Ž¹Êôbç ;ˆ07J}'%æ a—>üåCê[ÕoÐúý©6ôKB|Ÿ_RÞÊ)n‹Éð#(6Å)Ë
+®Š®ß®Ž®dŠ…û)×¹ƒíš— e'U7ùRAòbM¦üZä'×.Ò^Cv¨ÅìV#Rü|]™&S5²F‘µb¯â*ðà¦Ä.ÐRí÷ÐÈŸ Ç’\úˆ;õ…LÞ–Ä>‡$W9¬XËkÐíâÐB„ÈÙCLV×”:Iß¿M/ƒ1œJ}iZ­±´Ôš­ËÑ‹^ÖŽ†}f~‘C/Nþ±Î#ÊM|Hâõц£rYIüwt )BŠ6°çá«°>C–ÛþÔ“h[V†rD1嵊O ùò@š¢Ô'v >ËÕjÆ›a½ÊL-ýÐÑ5ïso€ÈhBšRÃV¬µ)äV8s @`ƒ‘Ô·íÚ#¥ÉƒéŠTq@Å¡.!þLt±[Æ*º"Ô™£¶iise­‘;§õ4¸ÝqAZSî /ÁÄ},$žvJKxo?¶R˜P𬯎Q4ÍYñ¥¼@4a (혡ø;ÉK/û¾)Q/|Tl;=Ö£`94§ìâ ÖòP“%çc€J§z0,~Ä>& Î!üŸ€dVWɇhb§„¥’ãÞgÄ*1,E=Õ¿Ô¼S:ÝÏh]òî·–m("Y¤?$ÛäeÆÅeè˜@Þáoþ+Ôä?€Ž­ð>÷3辩îÀŸüª­ÏJT…äá³°+YÌ.”$k[‚œ÷ùòÁ…zûj¡=Ðñ>ïs?ƒÌ™<¯e$T%g N—ë7þ –TX¸œƒüü,Ñ]ÙMôÂÚ¾ŠIÓⵘ¢: ¦‰$ÄXö§ø(Þ"IP¢ƒúòX†~ÝÆÔEî5×uÙ.&×´ìYŸÂê%J®åTgý¼Æ¬§§ ÁÅàøñ*ýÃé„ü6„8FXÃbœAã6mòfŠŠ_Þ.»&ûLQËzWË¢Íë˜g¨êáIM
+Fö¬âz7ªGd`þ~é~yÏLk»nÌÈ´ÛEØX,¯Š´jûü<®Ê›d–D…“â%œ.' k¡ Ù¬bQàmœÐÅ´{lz¦Þe…Š§ ýýËy³ž Z.ˆÅ™ Þ; cÿ¼Ï½ºq‚»ZwÐâ—HÃ7õhî³dÖF¼ ’Æ/ˆ¿¡4¬!Rôa‘,ʾùœp6Ðg&fC¤¬Èä.Hcˆ¹ â<WgãC¡/=µ@=Ðd›ñRíœÂšdIÔÿ/—äZr†nÅ+¸!‰úŽ«‡½‹šÚûŸö>7úf2¨çG+%
+^‘Jà9|LRs{¸L`Å&G^Úã=
+p³`v ÃmÎL‚Cü®9ºv*>µ?t­ù\»ÞM†0³ ´û ºˆ?Åä÷wï®)ðÓ„Râ—×^Jð˸ Öë0wL—’õ©Ë´c®q.s‡žó¨;„#„Å þY€¸áb ~%Ô?‡wˆüÚŠ{ž›3²¢ÚxJ`—O.nŸº}ÿå>S.¬àZ†b,­ ^–û™ETbÖFØŒŒˆŠ³©@…>h³·¸klx8rHäa³7"ÄQIn,ð8s?Žå#ƒQ‚Äñ-2AëÙ)a°ÆØ>x…„öúç`ø£öO›”\% 1"³Ë³¤ üƒ…Ƶë‰2ÔÆæÉ“é†9Î#d¢þ9rêÄFøe"¶w›Ž3©0ÙÒìáQãÅ߇ ¼,ŽwÌ fuð×¥Y»9>%múQVK–kècEOÝ\#o3XÜKRd<ãÆÎYŽç/EúX¥;°[°ðŠ &Èêã*IÜ” rµ­ C‰ž°¤å., üR]%‘Gƒ?_±ÝAT‘ôkmgž¨Q©dcÏatË¢ÙÝG®ÄH¤úµøˆ
+(ô
+é4{ÐQ ·²IŠ¦»„€€Ob‹¬
+îÚù Ø°b+?ÉìükNÙÞø
+^™(Å5qËÜžHû@ 4û"&1zÌFnbÈö*˜iê(OçÐ㪶Y<»(szEÓeH0ÉÚ<ÝWv)C&ðᜦ¿ÃøTíÞÆ^
+$Þ^ErƒÁè—L¢wNfðñóVÌ“øÌòƒ5s‡ËŸ+ñ ¦X"y‹ÿN#ö§"Oâ½¢‹ÄS½†L šçJ<ùŸ†>Ó\$^‰qNrÚoUýÔx©EŒ6ÜÇ]èNÚdJbvèÏ°À묯H%¼…]b OyRâ‡Ä§ù¦‘¡CéùAâ@ pll
++®ÄSÂþëÚl£ûO<¨5Î?‹â&ñ·"oæpÜJ€#O-ò$Þ+úx²@”«–4_šñà .ò ÈÞ†XÚ¡ET­ÛÃÌ/,£e&þu%>L¢„F2I+rº“ȘxäéÿZä‰ü¯ê-‘_ÿá<Å &_’–°b)»¢­U[•Ð»ElÇ¡ÄF«
+%èF©Â1WA /SºlZ¡¬UpF ¹*^”URÈB" ™I3¡¨Ñé*Úh«¨üÈMpdž‚mF¨p¢!ìSX‘G®€Š¦­Ä’ˆµÑ¶{…>Ó^€ taû ˆwŠddê)"k}U`£•?y=P% „µˆ÷Y]YÛ>iK1y·Hv¡ä zO-tŠðœ¡Àm'z«„ÐQ;gZæ‘YbÑàÐ9æèF†\áeì—s@€Ì™¼m3–KŽêü<!¬Py= ¢n‹RÖê1á"¡ä’5ËA/¤eäÿœÛ¸ï·¹ÁÆ{Õ}÷æÜ!|k±‡òQýû¾ˆZaR«¼0e¹ó~3ç¼·ˆ,Þ²Í oP“°l<;ŒÎlAÔt
+3DðçÕYl‡‘ǪhÊŽZpìÒvÆÁ ”ÄʶM‚‡(!sð•Çñ_­†;ù«eÈEÃG÷ÏaÎXt ^Íê¹M6d¶ÓØyYÎÊ:á¸#þŽ'†L‚Z‚¡nÑøqÂpv¥*«£l?.¢(?PY×e`:‹‚­Æ*1‹–ØÊfò½ècòß^²30gÜyÞ8G•È¡­Š
+ýe›3ίÐ?œQßêÄ0GÚzÝ×s W ]Û"™ÎOS*6g…*Iv5l•d¦[8žˆé‹å¥d‹²F t,G÷°•¢|\Vì:—ÁÃ
+Ü¢:î*áùaö^CæeÞ/.ƒtv?¬¸»]TµŒ‹Ì>uñe4Ö[ŒvÚÇ
+ç䈮„ôåó®fIQâ?#îOýBΙƒ’½L&
+Ææ%¸­ Ò§žàñqüeSÐÝ`gÉ ì%y~ {>aMµƒ-Ü»ü¬Ïsðz¨Z`Z€Ð…Õæ•ìü¯Œ ‘fMÓ+úзo·¬/ð#9Wži ÔC¤@[½§+ŠÂ_d»±ØÛ~ú¥ƒz5>^öåÌá¾B®ãô6 `§@CÄ)»§ˆ1°™Þ%¹°ðQtb,„b:i§¶ÑÞy¤¤!µ¦í/AƒÖñ6üî>gHâÑ%H²ù…/Š
+¬SC»j8%Ÿ¼SB‚·Ö¥â¶6ÑÁ×¢ïUÄb
+²JM³$æBàsHå±S(K56ÂØ
+‘'ÕÍÍ{rMꔚԺ¹y•œ05“ÓyÆúˆ`ÀMPÝZ‘a4k˜QYZ/GWÄ_|O=ÖCÞà*ÍnLs G¼ÅQì ÕYLñË™Ãmãý²6ÿÑn]ö„5‘綺™.gâÚŒžq¼L»D›u£¬Šð¹ÐëÊš}(êJŽ
+7KðÂ](uÑ:$Åð‡¸‹ûF7…mBš9Eÿu‹@2
+V–Ä÷,JpKVšµ 0 ˆÁ|yžÊ¶yWÜ%ra­ÔUf »¼!sZG‰òƒ «çX¬vÞ8øb.” Ðç:æœ
+­ž#[è€5»BÛzölÛ"¬¬·m#î
+AIIDª&½ÉÏÉ àU;sƒ?1&TO8›Ý@ ùyÇð´©"¡ÚæÙ´Vr7Ò}§„ÉËÞÒZ n‘FˆŠ‰þ·[ã@‘+›‹_Jï…g8uùôÚ×á/k¿½g*OÎ#1 ­í‡_T
+&`äè`ÛŽ'¢Kh#.§¯cÒì˜8tl«GLÅ)
+¢ö\Ü5E1T“ͽ¸ÐG1°Ö3½y¾1ò‡·¦”‰±­,cIÃ÷Ö}M M€,É›øçNðÕ ²OÞš†/Ð¥z>u5×eÖe&GwŸ¼5) ÉLÏZß*gíÕ\Œõ½ÄA¡üi%rAT'K>ùjJXìNOÈÇÈ^µƒæ›P2‚ÈÏÅä-}8wêW¡¼°Ícâ¾Çhî9U6š+£<ËòÝŒ5%Hfnؾ±¦ˆ=Ò»‚Œ«g¬Cû`­€Ç ÏY«„‰ƒbñŽÕ½:k·èjš]Îx«èâ¬ï57g-’“A*뮳YBùÌ“ÍÅwÖ¶yœPÕóÏÎZÔ-XÏÂmNÅŸžX+>ÄËIJ‡ë¬E)]6¹Šž¦wJ•ÇG]ø‹øøΓ³¿M¹Ž ÎÎZèbM@Gòœ5%˜ÍÀ×àÏYßè8k¡¢TˆEãm¾³ÖA,èœ2ÆÙsÖ"ö$7¡¤P]òû–D—³ŽÄHÙ²€w(&KkÀ<Œ‡×Þ‹nÊŽGû¨Ä^ˆ)ÉýE/Δóç‹Ï}_$»8Ù¸QàY­“^V²"›½9Q•ÐŽ©xÜ6ä;¬­„À/ÿÐ¥ ¨>öf6<j
+Ù"¦h¹×‘àðí˜h–¹‡mZá@LÍ°ä3²3U²å”¬/AˆS‘¶¿:¹"š‰Ò¢ƒ2gªà·ÑŒ!2ÿ×ÖÕ~ZQá;&c„Ü36Ÿ™cƒCY¯â?ØbX´úh¥7øª(~Ÿ^RüeÃÄ&—&«ý8II«’)õ<!Ä`[˜NÊV‚6¦Ê} ¨}Kö%µý© AL-O=¸døIC²c2v_€£×l,“ax/Jžúï?N‘‡uŽæ2p˜Y»¿(ºíŸ÷¹7Š.œà®Öµè¸}ÐþѸéÍÁë—Õ0ZVïIrŠT2±
+iS?&h–¨Äjïg¨Ùlì9#e¨(ö窰R•hl×Óu„ ûº¨M âÙçÐb6‰eHa S+œ‘
+f®U;†vÿ% 2i(ú:÷¤Ø¦‘Âa%QÍÓ63Õ‚E„ex#šI½É%‰î
+Ê0C豚†
+d/¯±ïè° y\Ä.¦ˆ•ßA¬$éÔŒ8qï­€
+—øTß÷qŠžƒÉüÛ V¨8 Ø»èìú—Õà.DÂ`¾6—Š2K‰'Û’‹¾ƒç­
+û©
+Å)úéÉ*@YA¶ü\EØÐ&YMeߨBɈ½½*A‘zÈéè„°Áé€-V+]#¢ËáÑž?ñúËÑavü_S@ÛmnÜeÒ€h>­"¾Q³à1-»³únä¿­\E¼2†Q,¶öWKóe3›&˜ìáEÜ54¨}È¥Іar@ÜHV"'‹¿äòçS3ŽõÆì͵÷ùCj —¡Ÿ3:~W%ØZ4¨bg분²{ˆ-Jn‰l>wdÈkZ&Á+‚E›8›ha“¿×Ü°È!³(k„å;§\V4Ʊ^Ž¡!oÁ GÙŸÈl/ÆÓ§L R
+ùcY¨|¯û½xæ¡púò|q¼UÔX8û3 ·@ȶ‹ú µð^Gb·ëØP.*œs¯D·LB«G«'
+(øÑÂ¥ LO7¹ó·0WS<È;=B÷Ud™ÊzÕÐÊ3ÛYýðø-ŠqÅ…*ÌâÔ6WÐzF« ûõãeŽ#ÇDÑôÚ¦1È}1…‘I—'äõ˜º¿ÞÌ°»¢À
+~feÆòeY
+ˆE9œ7–K’§l÷2¦š2°7á°ïìª ·›Œ_aȨ¢<¦Q'ÒC4ñØ =.î9 Ÿ$ÍÒ7»5E°šqÍ ö(¦{^d"ÞâY'N ³_A.?xAño4ßÔ8 ±^j‹³¦?
+‰¥4Ú%èõâÐr ½B‡c >üìï\¨ZGŽ.ÇÇžù”và‰Q;©Ï•»¦«…Ø˱ôyÛl°€YZ«_"êbJHwoÆ€ ˆŸ¢YÛŸúà þ´y ì36âo&üZ˜8‡‰¼bÍ’cÙ%mDüëØPæÚLÙ̦W)ÕÀ ƒ‰çI›æ´àè-%ð+‡nìZ‡QäK¦nœ_Y ªÁïÙÛS ˜(V[qLØ„/ $~(ÄÀlœîžƒ¹cEù L><Òlð†¾+Iœ©G"÷DµÚ@é„BáD¹-íŒÓ¡ñß$Šài:ƒ©ý¼y ž€ì´˜QWÈñ@$R<žIƒÒìcR úÀà CßJ5°8s_yêÑ<"ˆC ‚Š¤½b uÕ˜RT¦%ZîËÑÓÝE¢Ãj,›Û°d¬–ð°6ŸÒÄÔÖM¶j¢ˆ9;}‰©À¾!ÁÛT¼s4Z-‚ð*Çe WH¶Bvk†±& ¬\œ<åSëÆØ#Š]Œ
+Q1ís¸Xª‰¿cîζPâH ˜-¹™õ©×¥ëì¨nÊ^Ÿö)f­¡ ±8 Ç±ê\YʺAYÀžb·Ì9EÌ\å~ZÃàÀ0XZ+òì²
+ü´!¶FFXþIåJph*ÊqÄ<Äà,-œC\‘qÃL„„ $nb³œ Âq&‘Æë YŸÂ¾Àã­£øéâFóˆ#YL%HÓ&:Ö«?\Ìãpîûys0Øû àV‰hT!`7ÒáXâPàSÚ¬îÃ_L ª${¯ßUͤìÆ#’g²!1
+TÚN©¥bF)j‰;oªŽ±Ê€p@žÄ‡„67A;’\yé»v·˜Y* XÚŸ–VkÞ‡^3®TÃÉ‘ÖH¦'ò(Ø£6WYF
+KÖ‡{J¡iüiÊð7Árw\3<$ÄÐͽl¡í™1lz¢”jÓÙk’Ðþ’2~’±UÖ µœœ—“³æ>ÆéLÙ#£”Óª¼`6©Uù:ÔÇ^à1q2fzw: \$Én¨¾ì:TÇ#àœ3b5’:¥‚“¦DÙ?I-!`ûÕ/ÝÎÚÊ¡šµw²SNaÖ—˜»¬°£vî'±¡œÂj¯ÅÖìR>e¸ý%ù{²‰mé Á̺ˆ¤Õv|éú½À†`ÚÑAÅ¢¹­’ÙϤ­µ7‘…à!™ßÝI˜BìDaj=ªI
+(ØÄ@´¢yN|úÙÃ6àe^½ˆy.qÓÅ9É4ªC/æ²tuœ=ijA‚RŽ¸vÌúè3ƒÁ´OÁ½EL0—q÷HÔVç@àÆБǞœF£(=z±µ$ˆõ‘‹ÙŒ¼îÜ5mƒW¥ÃEý$ÛŒ< ]¼Ÿ}\€hn“;è×
+oB’üç~˜ì®”µvS ‘2%±œùÇ&ƒÒ&ºÕ\œ‚¿lÜ®¾vahþMQ´3CÔ&Ÿb?‹#Ÿ7äE¡h³ ¿%”q¼ Òô°8t+E¹~®­`ØáMó…èñÎê(T±±á~.ßœ[}-sW
+a'§mõz ôж> Ä¥£›)Þï+RãØu…vÄ\UAûú<êô»Eæˆ(áItØgw ÚÊÆO˜.¶ðíûOçÈËDe‡Vö»h2ÇNó BÌÚDClû2Pg‡õ'[[Øôò û…±NÞ1mݳTSòƒ
+ÿ*ífiˆ±ËðËüN"PËTh Ó&4ƒÂa¥¶ßNX%t¾P¹ʟij²>{r7NAR/ÎI¡ÇH‘K7µÕ•% –¤$’Šˆ<òû‡û—_LŸ™V»N ¯õƒG!€Ìbø.Hí‚;¿d>¬]ˆ†BŠSmµÅ t§bðQØä¶Kæ #k]´ ‚׬51»íRNÐW˜¢[åòA3¶G‰©_t jâHÔRéWç =ü‡R\˜‰S½#-öÂ$ÊL 1½è>oÈkD†UPrì/v8½ zeæÇ;ô-–ÿñëîý{ûñß-Þÿ¾•†× ú{ÌÑ6 ¼$©ìÚ2š éwÙ&:Ÿ­q@?/@Ùî å(^=.@Aîê…†Ò]>©$Ç·­ø°]0‡«B¶Á¾…`®.-ž…Ÿt£uºËóòï0q ©[ l]­JªSñµ4Kbd‚…é1´B ‘pDB‚W¦yF¬I[hÌw¹8~ðó8ç¸ÊüŸñrIª,†aè
+z¬àU¾Ž3îí¼ýûÈÎí„‚*F r[–%Ù>öŽäš>ìØ5Ši¡j@ð­’¼Á3z…i¡ÉD“v^D)_­£ãl³Ä ̬/u•õg1ùüøEÇå·‹ÈÁ½>nÉž.afý#z bŽ‰­"‡nŽÇé*"ù$Hj>‡a†²Jx?ÖOÉ[a°‡÷>>¬ÅÌÏÑVÀ1ƒÔ@°ê³å ñqP\¤v ¥Ž÷ï—¼˜Âãäww€–{© ݯ~A£Œ\Ò䪮˜BÖƒñêR|ˆŽ7tŽúÕöðA'ìì)Ä«+”Aç;‡[BHzZ‘<gÓvÄ ùn%{X’Ò]Ú‡F¶ãRYïl¼Ã_beî1(ÉdÜŠÓG¹Õ $©ìdQJÞû9•PƒãßЬ&Äej”uç°€ðxÒãWþ¿
+¹uÅØ‘ù Pp†GÁšÒ²™ýö8ÇYrA˜K;%fSö¶ñ× Uœ2ämÂ^U¸J-Ž¿n ê>dî8„çI¾,²¢T-½ý'È;ç¢oiVë šè ÄÓY]áÅv}ÎQÔ3‰S Z(vuõªÈ·&d¹BâÖZo¦0õ+"lmÕ˜yZØžš"‹ò±ç˜aEiðÞGÞ`4Þ.Ór“͈%1¯çÔ8y‘]Îe>l™°9£É­H)Nç<›»×RÕŸ6ùW¹‡~Ô„DV=ÈQÞ‚M®KÂœ >ÅTT(‡^›SÐ3’aMȤ–È+šº’9¸àN*¢ÆæàTœ%®A9 TKؼ£œs\\kP
+A^ŽBã§`Ï>¢oƒªżRùüŽaèD¥uÆ/–
+H‰Œ—Anœ9…Oà;x`
+"ERÒ²‘^z;'0ºWÎrî?%ýF\¥Bìt’våY¢ÈÇÇÇh¯zÞ¢ˆ´Zš¼þÇdÜ|ô6Fáw«¯ï/gPZKXU€œSo¥¹ŒêÃT-·b…¯Ze´1!UJmÝj¸ôäçË¿/åõï—„©µøáuA[ïÏJëœÿk‚ø@GŒ^¼ÙÕâ":št›Wv7Qþh£oHh¡V[Ó Qù8¯c_e½ht¯@gê-À[ot]Õ œpŠî×õÌ@ãÇÊN@*Ö´—¢ò$G_ +׈•sµ÷8ž£7 éZG’üû(fÐû„hm$¶t>ÛÏ\òýˆ}UѪ1¬–ýô¼¨T­š§e°J:ÍLwÁH–GkëÑ÷
+…Ê:V³ˆVÞì^¯GÕÊÿ[q/âÔѳP„#"™5´§&OÿÈetågÂR$$Š[…\LÖÙqÒBè]]F'D-K¾RL7ŠYf/÷“ô=žc7Zcب@eGCü¹)DŸÉuÄ‘ìëU¥;¦F }^e·h'T¢:²³C*Ë@3Hã< -4€AŠ‹~ô²ôñG¥rGÞY/«ž qÊG´/÷Ïr>`Ð$QQo¦Ñº«i¡Ñ*]ö©}(7Ùé¥ ™$wB/Ñÿ2:V8ÐïCèã;“ê¡f>nÕ$%ÍK±ÝçœìA7¢V¿&È9¦Ò/v0Sª›w*‹<áªN ³½ú±ðå&É[1ÈÑŸˆŒ¨u/n̉öäæ!FIxE‚t4„ÎNÈì-f
+Ÿo bÏàˆ¶åŸQEaȪÇî¿J}8—Ìö5fz© #‰[œ…!Ž !² ‘Xj–m=¸=Ö"cæŃørÙwAy]K ™¶¨x}øY‹Ñé7ˆÅ[Þ©(“+õ]ÅG01 àz<sÌp-L·Øå*ÎÄ¢{îaw¢F,¨9>™1jöî*V¹€ŒšŸ0”&Sê(“ê
+™lÌ©P_ ﳚ°.£|ºïûcð*žöµ\¦ø.–À`Ô¬YÿM‡îŸ7ÏŽ¢ƒP8=¦&Ò7år‚™ªí˜àä¡P'Ð]ÁOÑ|eÍéQä;äæŽÃ‡üÞõÁû)×Äor¯Èõ*7¤`¶]¤t9‚Œš~.¿ƒÞVzRÞÂI±dÁ"Hs|ÜÛnªBà2Þ÷óñ€R9„õÖ Þl0–—­ùšeá䆻áeñËYEæÉÄĸá°ÒÚ¦ßç0Y„ Ãf’CÑÅF÷mßœùÍÚ[Ÿëm‚ø~°Ü2Ê–ÈŒa‚FnÖæ9H>S¿½_ÃŽçå^Âé E¤bˆÚ^x  ƒ¡fåRó19"w Ô ·' &‹å
+INlæþ;H¯ôk/¨4Ï@Ì_3…QØV‡•k”ý>§ÑéÜ̻蕛í^°É}¦ÇØ5éI–G½,Œu!âl1Ï¡ué²ô˜£‹õÇ¢'¯ËæiÖ\Ôº~ªPT¬íÈ{'?jÔéÖ¥ú´Š¦-ûo:÷”!¬¾çè>ÿ=mKfþºÇrkˆÂ²ÔCI"®Wr÷#ñ&ÓAfSŽîfß[ßÆ–ê)Ø–cô ‘$
+ñò†:Éñê©:%·µÏ>;î˜Hȹy%]w5¨(ÏäJX¿ž^ò
+ÿ`È'ǼàjX»ˆüx„φ&`‹ÙWåÈqZ&ê„TÍ`¿OwD®®‘¿Ïƨ2p’ºihi›èœ;Î"Ksu¼ÜæÈâWÒ^¦¬
+úsò`åm}Az-JA«l^0ѲܯÝ1õ›oÈðج`#™(T\¶UE%¯Á™.áA=zŽ=Kϸ&.
+^ç ¥-êx=pnžhpõO >Rc“ÍÑ÷U òÛö ¥O±‚h²ç­dI› .¿ŽfÁ€ÏüêHÞŒJÓ¿ôJ¦Ê塪ˆ«Lˆ1:²jxÛÓVÈ£‚ñ8³Œ¥£0õB÷|£Åé°›kàS±­Èy9OÛ’WÁ'D‰Ò=™¶¨
+3=ç"zÆ-È}J3Æ˨ô:™Ù§9/¢Œƒ3l~B)ÚYPýÙWá?Ë@‰ìíE_HÿiLÜýÉŒD§{£§n‡í§.BhœyœËPN~±‰röD ¼SV Bv1SUrú”ŽDÇPîì‹èxo29{/úO< È[øœ¦ÉO9ö
+ÄO‡>æ¯H’—à-5‘.KĹê¿Jûß/î‘Ðh‰d®óê—M"Ë…LÊ?¤š7df[AÍÏ2Æê²’ND<#®³¦‡
+WβÉø2|ž£âø šÔ½%« º¤È_ÌðW`ÌñŒùݹI
+AðtÙjoçªg®Ñ³ïã\Å¡znw Å/¡iËÚW@“@»S¹Ø8{L ¾
+Ó©²Q¢îz‚e0 lÙçHšH]3l¿ÿÙÆ^Ô3|½MdÛì<ŠµÞ"±“ÙýgÇ°bMÞšîÊ„bIJàüµ{LNß:Ég=Ü!
+Š.k§ÝY^VUªI '
+ôÂþ‰ó3)Ò°Ó>¤“"0 Rˆ®Â*p°»²¼ Ý&¬Ñ¿3¥lÈ<Æ߇´.óH¿Š&§ œ»Ï|h}¸›Ÿãx#}÷546BË 7ß“!Z)¨ÃPÌy®z!]ûZ¨äV±ætç×øa ˆ%œåãgü
+HØAï:(maŒŠ62™hr·z "¾)Ó[Ðßa`.Ф竒óû%hh\«ž­-•¤“ÞŸ@ÎYLÌTHx`Ô-Û Ú>Å¢ýì@ÆîIÍ…ˆ·ñx>‰Ï.…³~×yÆ?]¤hkô»,ñÊÇ ž@ñ>ÍOŸÖ(dBž,ãµ’<Î1Áv2úù¾F˜–)UÚµÂ/ük»›Ÿ†Ð¶6ïÈTÖ<Ôbö×j3Fïc†[QàE.²Ýìày8ÆdÝròŒB´JHµÑÏîE7ïÔ•)J#úý4¤cÒÆÜw+ƒ%Â46T»O†~hÄÈýˆÎÄMwŸš« Ôñ&MÜs%,§KØ.×I!¸ÌêcÚ9¶æbïI[šQvˆò¢]ÒØYf=0äM™ìt;˦M´h:›ÝΞ/×zõm©·¸iëc_óÖ¡FŒïÔÍÑÚçM:Á¸
+™E»¡kö‚1»P×w=¸
+6h'õ¤BPŠ{ïg£
+¸ƒ¤tëúÇi/bFæmÒ&¬“ç/3±9­ªmQ¥š¯s¤Ù©’Š€nÑãLÕBœ«›õÚ«Ä3 ª]ŒÄìÍÝ9 ù#2‹É’æºïYÀM]€Ó¢­‚´V<„3&æ r©œ2eÞÂMí<'úOýý?<X|ʼþH:¤¦9í¢b
+¡q@"9ĬO>•juXUû,íyPä±¼Ÿm¨
+â¥ò=štS.§N¿_E¯1oE×÷P)R
+ð¬ L&̉^¯yǼâÍ…ò–ûbþ·.ùØÞâÜ6ÑHªjçªg£¯`жÏY`˜VÇyÕ"MõL¦«xÈÒZ2µöƒÒ™ªÌiÙÍŠOë3å¬PGPø>*ŒÈm±Åb—ب¢9G—Ø¿øÚ¨ì¦0õlÑq"š† <âÍg¤Æ°Gbq:éÚ‚–œ8ïaàZŠI¶dL¢UiFÚ1dHŽOà L¦Ö©<ÏU€3®­5Ùbµ›Btôø @H#^ E ­|i}‰J
+Y&|ÐmeÝ_8LMŽ w(q<FÙù$MèÜâ=èKȱþq°¥åŠÀŸÙJ›°‚>À6I†h)ÖŒ:b°¥u±áçRëÆ`›ä´22]î
+ñ¶*q7éÝÒÕØN’Á±
+‘.ÿX2`È×uùªSç¡W–±LDä|¶µš;QÀŒ¬»½ôóÅÕ².×æSS[–Áo—ÑÒººZº‡úÀ,’˃«•A„3öÉÎÍ¿¸Ú›ÍüâjkWåjóÏðŠã89´ ’.äÿ—U(Ç`‘ýÒ¯1°(`íÝn05[0/cë$G;ñªøÄúî¹&B#z E9:j©YWIªt4/[t›xÙ<ľ¼dŠ´é+*|‹½ÂV=Š9ž¥§l ð„~)úõPÄùÓÀ'iê}QµÜðô¾¥‰âzè·¤FžŽ‹câ‘âxBm•ð0~õ’Ìדh¡ô›WüÓ·âîÜS¶-›ãè
+ºõ›¦ éàCÖ$A¢c\¨lÃ| kšèЕ6Õ‘%+€€µy¸WAÁ.y’‰Vì°3ü°è4«äðÀo°"}µY.Ä ¼!,†ë
+/Àå=„ìÄ£!í7[~iü«½Ɔ’âŸêÕÞSËñ¼s¨“åe&ŠÌžJ†’ÏD…ú‰>Yàcwo¥°V$í6ÇÅ
+«ÏÓeåÑé3\ÔZ[‚„·|%=ºÉ4äðNÝE
+Í>Ób05Ïð}Ö/J&– #ÌG³ßuÐ8”´òö#Ý×Q†[r­pʼn”bàǶ©r7¹Ø•`ƒà+ݶ0ªb‚,é°u4aóÚ´ ŒÎañeŠìøˆÁPÑyFîŠAe†¯¡å¼}è€ìJ•uy¿…ºMñŒ/”
+‹Þ^$GßŘxÎ}/ª1Gm¦~%Áßq©X—ÓÑ>l´ taZÌV’Ÿ‡¾È•N¹“ÿq„ÞÂ
+ˆðAÙf ŒW©4 VR{0"3÷S%Þ.M;p½Î:Áà/ܸâ3²G òÐö°r$3dÀ!ÖYUöôHÉ|hXNMqè
+u_¦`*¤Ê5£‹X˜Ø‰Ó؇ –RxËL2-˜t~ËÖKÑ­çL_Fš2–óÜ·Š´Ì€Ô×ÌY¯EÜ bÝd}ÌçXù­(ëúÌ …Æ*Àít{ëŠáE¦ ß[*8òxWêÕ(ñUe
+Å8‹se#¯äî/øRsyœ©(;+˜ÈšÀï}eæ÷ïÐ÷¯“½f£ibÊÚB¼#ög5è©!Då*¶«‡>©d’û:–³Š& ôáYð#*YºFFs ­„…yÈ,¨{¶˜¸ ùÔ'Ðbj‡j“òPÕV˜ õ”Iå÷pΈ©¾§ÐÒ½„½¡KP\rÍ…à[™Gõ¥ÈB>jûµe*“*o=ìµæÝkºÜ6ËŒ¯Ï5ªÀ¡ðþ•nìqÆÑq<T—5ð¦û™™W‹£$YÁ´…÷™ímIk ])#¬„t“KÁáÀS%®Aš™¢„q]WóVÝJPMÆ‹Kô=×"¢%ä£84÷VÞOÌN_Ö1D’ºÌâØ[¸ŠÈ´Ò/QéO?z;$§ŒÝ`qKn.Á*³äÏ1Óc<
+–"ÒÃêÏÇ£óÑ6”8üµ×¡%PTöÐb8:\ŒìÁ3è%c`‹>ؘ€V«k5”³ÉâkÑҫÚ¼í_‹¤,¸ä•«ï…çâÌU¡ œV™¨È$ïÒFV>H3qæå,¬}¸ëRô¿òúþ –AýãŸ?° œÑð%?÷çW@%ÙM?ÿýj'â’0^\Íï|‘pÄmš ¤÷LTL3»Ö½ýº RÉÃnšŠ.Û}-zÿΙ~¹#ùÛ¿~üñî‰Åô›M“ñ_eºMw|s@rSÚŃˆüH¡NZ߃ÁBü‹\U F×è~vM ”Só
+ÚŒ€¶44þ—{ãõ¹9¤ 0@½÷C‚•;s}»h¨¢#x†ì^$KŽ5ZÞsôÝÃY&µÄm›H{z Þ¹DÆ –¸:Ä©¹ûó"¬qeÌJô
+ˆ8¶å…ö:PŸU¥‚xd=ÁüĦºK“ ×ÓMY™%^Vl “hm"V?Eè’üµö
+BçÛÅÃ[
+ìñšD-¸LövoEFƒ·
+…åÀœ‡ù/ãu’£ÇDø¾ƒN`$™3×î¥oÑ@¯Ü÷ßö÷8VUþ {!CU!’ñâ Jj·ä½„- àLÉ>*ÿ£ Ž¾ë{Ef€<Wlûã·—š—Å,¼µíG<HûÇE{„
+Ò,êÖþå¶R×i,ÌKüg—šP>— d&Æð
+ çÑ<r­¸ø¨]'%Ý{Iý˧¢Éã¥$WÝÈXOÝ9†Ü½œc´p[ à\^ÀsÏlfgQ%ÄN*z¢M ÚE’ñ¯"{0‚XÄÿ÷òé)mî't]†Ä|k`!ÒIçËÆUGH!–ì9'þˆ¿¡¥ýœ=&nQà‰
+ xÁ^Qcü²ÿ-ScÛ’ŸæM®M+0éÐMäp“4înE•”«¶xû‰tØ64%›.\1ƒ!©±/À
+^P»o+y><“Që܇4ùÁ©W’Ö_£ÉŽ–_±ÝZ+g²/aÀm|9?ؼ Xë1Kb®w ï~ !I÷žW1dnßL¬:-{
+Ñ•æ¿Îê{ôÈÌq"pÖ Æ™–swÔÐÕ;ò„þÝÊJ¦íË´;0’ia¢8_ÒÔ„ú™¤p¢È2ŸsûÝ®¸™¾1TòeÙvͧ”t¯ç«J})Wù}MÚ¥&Ïë9‘—‹A®¶^lÚwÅatÖB~l(Í›Mþ+:vÿ$ôŒœ
+[Gå>ÕØIvÏð&œÜ5Á’ók³¦Ât€óŒ4ú=äÀùØðÀŒAP7H»æ!Én¥ØxíÑ<È·CðÖ&†iJhÆ· o¨èÔ:î ['Ù'¹Æ´¦ô¢oÓe1ÎùáqùÄì
+ÂÆ,ï;Vá*S`¼øŽ¼Ó…r¯aÖ žä´ví/a²—
+Q êK
+ýRñ—Pb½çM”RåÕ^ܪÜ>3…ï·ž
+>S„ðÆ]ôj~”.DïÀpõñÓŽVf¬ý*"fGÌŒÿ&(¾†Š“+B¨(#ža^õ5V(Š±»ã®çWEÅE«ÓÙs¼×1ܨax
+S2D?ÕÉÚ S~ÖUT‰çlùãžE~vúxÆ9w ¼oAÿ*9½f¯(A‡úÕ½='yNÛ† wÀ5‹k™^Žü gž¼O£+< +UË -©5ŽéØ—«8KA¬sâ!„^³¼ î6÷9¯:ÑìƒÇ‘ä20çA´½žösZ \ãëôv1SŽ*eŒÍöYwoË'ÒlF3oÂ
+qÒgôtZ®øCÆçÚ2òPvĬFŸë|1ÜìañøýWÊf47ã=Küý=©¿±— S6ßc¾Ìiü¿ ÷’PÀTžùÝ6P¦¸Úóµ$Ñ£ÉvÚ€qÿ~+?þåÛcÈó%*.ÇiÑ8™ê§÷ÿ*"ÌfÅòѲåñ:x(/_ºLòþß1”›^ŠR¢¹m«W|6BÎ^€ŽóõAѬÈ|KÉD‡F>N«‹.2_³HÛÏýŽ*ý˜˜ 3=[¤ñ¾s¿švŽ’šÙ•F®d_Òʼ
+_ô °FFƬ²ÛϧŸ#}>-Ä3Í…'Š@x³[ûï†DT8‡e@‡Ø`¤:˜g1t0WÒö{å/½sQ/€¸§N4‹y(\¸ªÉWJ,™OhÑøk]õ­ÈO·xf˜}Æ9'öàÌ|Ã,9bR™¨¦Óãíyt·N9j
+Mê]ÞG šb]xÆi ¢D@ÁÎòR2®B;;Žu¿œs†q…™‰?Qµû²´gûñô–y‡1õ WŇY/.fªÖÚˆ„ó³ –†™Õ@z87ß²†`Q,c¼Ã¼Èó}óÉŒ0’«<ÉôÆ(ûb™ã~L×ÞÇmŒ{½:K0¡çš:ƒU‚‹x!Ýß(å•'ìyD-²ò¡ÂßcÅÑÖEÿŸmþDL['&Ð!=þkbZÓl·ç8²wbÚïúnŸ2ìΗ¢”ÈAØÎUBcûüb,– N˜„Î{–lѱÓÄŽ{n´˜&N•Q„•ü#ÃÜ÷sö¼•®?G™OÞØ1Ž£Èfw/á5¬CÉnM(‡"1§y<kAÁ½tásáÔ±}`åãú9Å­f꫶9V¹ƒ²náªQ’¶XrT8án¾º08f\å7Úήµ à†xèdˆ²vöâRç1¼â‘Ðâχ’È¥3¸ø6äú­Èg™¥gê1<(ãcaŒñ÷ê°Ü,¹ ŽÄìyÞ«ˆ¦9Ùßf¹Ú)—Òà¥äŠ>s¹
+\lÀnœiv‘ªŒœýOs.*þ_ÊöÄ– ‚Ds›ÛêxŸ€øFÃÑBhè^$[ØÈ“Ë>ô‰"VžÓÊËI œª¼C{«Èkœƒú*ò(ÜÌ0SÊÔ—³|·¿ê25àU–j:îÖSQ;Zœ’h-B&Õà—!
+Pu}Fu¢`Ð5øŽ•ï§Àþ¼QØòp­ëÓ¤òøžZ¤HQëùÔí€ɬ‡9ÿ²>’ ÀŠ÷H'uÙ’rB`P&‘?—ý0§Ÿ¢‚ !±öÀ5¾-ཬŒ…¨g ˜6ú’DÔWØT)ŒÛ`åhDœ« ÊK øèñl\DgÇû÷›
+bÆ+ìT÷s„dÉ¿„' CQN(Ù‹)\ËLØ0{ùÜ•ŸWîà:&gB¿sì“Êì é(1ÜÄFóÌå’X¡O@Çxè&øXésÇ71ˆd sülviLwÕ'À”‡€5 .Ãhf')2 ¥ìc‡hQ«hëÒöuÜ.¿CÆ9³bMzKØ’­qZ‚ÇâOÌÄéìx ì»
+7UXŽ.‹ÒO3߆‚ ãràɼ™U‹!ÑîÕ#„S²j—͵ÓÉ©Ììr3ør—Bç0³œQ€:THgèHt;éçÙ©è6ø·aÌ5Ir=RA/2„`ÀÑí¸ iœ}’0k<yû¸Œú=ä â;w¼¡Hô²Æ§-|ñ >wÍ|O†ê(Ì´Äf®ý)déoVûj<}1´«l|Õa@÷ å
+ÃÓ{rH§šäêl¡°y)%xfõdìæDÎl‹!TÄ°õåORa´”‹gB$Ð b¹Ûy:›\¦¤¹s´Zj í*#f83!à^¾âGáâh<¢Ùöl”°E
+”šß8aû˜6¼_~™ÑhgWAÅ‘5œÚ%$Öáë¬x!"( ¨:2¸!Ô…&T„ºcΤƒ[Ä–—éú5Ä3 Ûhò´öp›È¢¢z×<ÍdŠà_J‘–$;PyI³~qTžJ]Ëò cÅòj¢ƒ »~M‚Q'¡îCña—šCb¶ÓŒ•‘5Xi¥pxã÷
+%‹_ (˜pZFI°ãAòôÆæK¨äªÓ®"ž—Åt 4§Hr8¥½ ÍK• Êøf›á®32(…„žª ™Øb–«\öj IJcï¯ËB@±Ì àžÁx›OG—ë¨0Jæ·Ÿ»”<x ùãÛßß(RlIm¢wÚEÏÿ³c’ú‡2kiÙ%èÏk¥ÇRd” ²ßƒP#‚ãeë1H»^åþÌõò¦ ‡´/Ðú6`釔Õ€C2¿ô™.çÄVýý Ï\ÒýVЀ3Âq²õŒèχ ˜
+L¢©øÝï!K»AD¯ü£Â/°{óa#HSLã@ËØVðF“׊»5B:“×ö¼å‹ÒØ! Ž”–ÙMgx*л€8>ú‘x”©ÜÉ>§‹-
+ˆ@º©‘ŒÄ*l1Ó‡&
+
+P?W-’èq ~)Ìèƒa¸4Ãàa}#Œ(kYázÓ{Ì[|@ËORäs§.瀓Y7\äM<)ï„%‚WÇ‹qàP(Î>©` ¿K§M`‡\)³ëo‚EäÒ§Ý ›¥Z%Åw 5 l–fÄmÐuQ
+.¾×îBâõ¢Toy!+ÉÊÔŽYú²%Y#¡T2SŒœî±¼ò£ º Pdb *DÊä}ÅÜId´*C¹CÔâY@ì”nT…‚c‚yB+Þ¬‚y°sËnÛ«µŽ *@‹`ã–¶ãû…Ñ?}¡bÈ*u
+W»)D…
+£ÎèèÏýp -P„É~U2
+(ÝZUxú›NÌNκP½5ã¼_5S4 å !*4œaÏù€óÎœ=+™(œé½JzƒO´ ª’8
+’
+%÷¹×:ýŽß¹çV
+c£)8ŠoÝu%ò¸«µ÷^Ñ/±2äÜž"çè‰` ñÖ¢B¿(ÞyÕ·LM'¬œmÙ}z–iÿ(™ð„(2o8ŽùT)ºd5f0_ðäZ¶ÂózN£/z3'!~’ZÍr¼”Ì­>'êe“^yûæVNbN÷azŸ3ñ‘hTY[-Ýu(Ý’oÕ‹Sf¶ó´×¯P»_î {ô[x­%§AÆ(qV£qÆ ×*Kä€l ¾¿J„"e+ˆT¢Dó¤ÍW±Màȹ
+ †,$¥óAÁ,qòãnk'į€º^5» OgÝkîtH¡©¤ÀYâ‚¢[hÚ,YÙKôÙã\æ€K.NG ¯½¶‚´‘CšY{`%š{Äʯwi:¡j1ƒz±þzÓ󶮭¨ˆ[Yí,žõ)Ád$,=Ç¡°ßê4yrÍDêmû¦$>¯ýñ)ÿÄ_ü5lÈnn"n<¾¯O6œl&0NÆ|-Ú“ ©4î]
+¿¬ãAòÈs¸3ý«œ\€Cp
+î½ü>ˆä6.t{vù(¹,$Àøì=V:"hôÿI?¤Òá´u¿_{FxX\†sÛsÌë™™\ηÀ1pBÿγûº’ ºØ#ßGÛýÍ:±RAb+$(qï¶!(£2K\Ù°kÙ³ÐX7öìx¶â ¸eré©ž[²B5¿^ÆZÁ–ZæÇ9ÿô—cµÑ3²ŸÓÇ[hœzNvø$ªmá»ów,E£ôæü¹ÑWÚäj—|S²™þJ¾±¨ÇËõïJþ ñÿàû¥øžÏ:ڸܜ9ä&‰+x£ø~ñ`f•¥Û¯—¢1ÜÇ2w)Ç«$¾PÔó×IJ`„ ßY…ÿC¬{§ì {ËU¤A^'LÝçXÎíX%#ñö¾I
+òGIËÜŸíYÇó iTÛé\ƒiÉ0ã“"ëtYu{ð€.èAŸ“Q†Âo×Z;qaÄb{ÿ¦DŸ'd”ïëÙêk‘}]˜®óu/Aô­ä3ˆ¾Öp5x¤·çÄN¶‡`Z¥C:j
+,r>‡ù`Òž¤pŒ&ñß­¾iLsïÇÞ¿YçþMÉÿÅÿ|s«?Šþy5Í‘Ĉ
+Ù×%XÎÚ
+Ô×hŽ”µ¸÷ð3/—@–Š>†ö—ÇY
+²«©-*Riº¢ÊÄ“"؃žM\}|iÔ¡”DÙìvT7ܻėà7p,ì.¿ ¬ŠÃ5_0Jš”F¤[Χf—d1žYNfÄZ¤µsXìÅ¿O>ìDJŒMÀg[‰ùôÒ€úêoUÑÁ
+T¢B5Ó
+MB–iäWGœcãmt¨<œ `ƒXðVËK€#÷cOÒqpMÆ´óðø7f0•wüO ç"l?«qôÜpþ› ÁcAq˜/}/Bý
+9 O¡:?Gö+ߎT3Þ-n²÷ìŸ"Á›p@G£°b¼W–eåV\™ùÂÉt ŠIXU¼;¦‰ªÒÌ‚í¼D•³U£j2`QÄýXÇ„Vønõ+bU‚,ºõœ€Ë¦_FæªÜ&%>a# ¦¼ãKÜ ýÉ2E!éb&æ ß&Ÿ¸ƒÝ…—RÄ8õ
+žbó«êS”°öôEã<›þ€÷øL¡7¨/Óâ=µõc¹"i§Ë%þ¼A ÜjKªy£QÂ2ý¥S؈ –m†P1¥XÉTJ%³¶îè‚Ãð 0{ÞüêUDǥ"Ì´Ú9p†å:|…]F`S[Z*•ÒÜ·é^‚&q>-/A $Îi–ÄkveÁ$uÈ—“åyû.ƒÅFʬ
+r¶4ö®7”èñ”þFppjMébt reô·ýø—E*ÙH‰LºXjÙ
+‘;ÇZ4ÆÏDùå pˆœW2¶ÑÆÕì½"ïÉ4þv…çøâ2•³X'lšÏ“¹ÈÀN{\Š¬„Qcjeˆç(žÉ2gºv&bž»D Ç"ls9q¬*¨1Â0ÃÈÇT³:îÍ_>ØJ)%æ3Jpè8Mw Ë”ðD¹\JüS2J­ÖÌÖŸs`ri 0
+éÇsèz™ÿy ÂÃ?H~V¾zž¡ÌHJIÒ¤³¤¯x Pòµ.oêÜ%jÙM^)…€<’`½%§%±W¨^·À0›Ü‚VæÍUdÅ!©ªê ȱCŒi»½<<ÛȈ
+/òÏ 6…gL-1›Xå|ø¦KÚé° f’èŽÕÒ°Ða¼Ç]ØOÌô’îk ~‘l‡oƒq>õRÄŸÐ;tAºÝ¦™Þ£]õÝû_' L¼Ç1"h‚o¿±¶“o±X;ŽÉXdæŠ|7/Ùµ$qv¯á™
+V›Äìu6#wŽYsŽjb>ìp>!BÇÄ}¹kíü‰»âJœÂFB>0¬(A‰Kg}"F¼î:æÛ6ÔÏ}…Œ,4›¥ÅH'Z²(yên|ÝK:¶²*{žG |n% ¾IIliDä—Ä)ˆ{ û©Ë ¥©ŸËÔ¤ 8•¹dûtÇÅ«¦è.Þõîl)¡{K+ÍýÆÙBo™êTvÜFÞXÔ«¿ÖXt±5¿©h(I3ç±^+ÜЋږ‚ÉnåÍ)ŠO“àÖê©à“[á¦v/ðÀ÷|GGò yGH+ük’¦‚†`}¥™ìÍ›Op€ë$X4MòˆáNþ¥×IÊ1bêØü|V8+Ê”*3Ÿšf¾
+±F³˜°ñÓÔy„+Ø9¼†‡A7Ë°L Û 0å²l*aŠJEÐn[—>µÉ#ô& ÷"• f@ÒeT%>Å|¹ž€‹±²°@w`²áÁ˜Uî/TQÔ¥Ç(L :¯Ls­A @G«ª³/Ñ;ŽÒ²W(+)ÓÈXX¦‹µòÍÚرM3á>iz‹g¥ÄF°À
+7¼˜Bn`Ã_‡J±'¸Öe4­V“éø°°¾šÔMá5ƒ3E(IFxŸ1£V%‡çàƒù,dÓcŸ£ÿV(c<R|¤°ÎâsãY±#_·©¼¯¶ZÉâo ’I_XÅ®C–[Ä;íSù]­“A¡8P@¡âlv·=f'ó&8B3ŠgÞcÞ ÀFb^‚WæóU½„zy„ ¤†-ò³è6ûÏE‹ !td·ÚÇ€*Rˆ½ž=xž”‘.ÒM{:¤—eY
+ž/؉ŽfÒMÎ3G—ÁúÍ(ÚÅO²¼”~Å9ˆ!f„Ø Ùªeqä×2~dË[I³.‹l®cÏs€ÏD¹€I3%¬5_F—SÌ'½Ä«êÁêbÀ´É}jš™Gu§á5dI"mi˜ÞT·ÊÚňq9~‹³N{Å`ú¬\_v"¾ôÕ.·5ß¹!¾_4Šz8Ö»âêÁDºïñ³âJ‹Sîàô+’?Ç
+ÿòÂÒvÌP]/ŽLƒ¬™Qêt+¨2øþvƒJä_ѧ¬Tóû4+a~#áÒ/Ø&:Ü¡šÂšškÁ<`¦—ßGÄG˜`<õ¼¦ôÆu 9÷übM±|äMÌ;¤•O»¬ö&Kô”oãÊ –ð˜*â ã7£ ^a–ÄM~uL—ª+Lsdß¼ÈL¡<Êdç4ý/V˜Ó’a[|*¿*ùÑß¿E'°còYÛ©™—Kr~TôXÀÓç~Pt#…ãnÝ‹œ`H,€U£u©>ø׋*‚ ^{½nõMâ?­¨-ôµÔ%ù9J<9!AÓgFô:J|ãg2è(Ê)/$ž"pFº¬Û휑TÉÚ€¶ƒ¤€ÏF €~ö
+öñó00(Ÿ²XØóχ¢Û’}ØuƶB:pVýqŸC–Ñnv¿X O5((Tý¥š­?PìŽ<ÌÊ’69Ô8­e%ümdI#º óALÈÒ¯Aò#¬Ë`“òcbîçQ
+ jûÆ'üY†ç?Ô€­•—…OuˆŠ¤(ˆ>$7Dz<´‡•ƒÑª•
+ærÓjøxo’,ÃMmÃrŒ^D+T[žql Æø$¥l(ɱ½§’oSòOÝ‹&[>³èüû9÷ÛL²‡\:Щ_5Ù¥#•í»5‡ö)mBnQ„94ëÁL,è´ã”¾øWî£~œq‡Ëá7Ô_s/]‘gsÈnÞÛÐZÈCIsó<UÜ&ô;¶Mn"¦ô÷°Ü2DlÌçéïÀ
+¸v_ÅiN,L—¼üJcE5.¢SJ
+‘ êNEŸ/ŠÒ¨ì:Ûµ«¿¨Ï¬æ±= @ÿ™VÁ5 C¡.³S{ñZIî¿ô˪µŸê¾Ú CWé¤vÝ!³+ömÇ9~+pÀ¬”ºµg\s|œB\L¸Ns^³i´ìlîø(V/2Sf핶 ÿT«RaÌõ5'¦Ìäa‘êS›çà‹ÇÕ;‰¨G½ò©DëÖ‹¨”I]Kð(*ð %\xn;§á,
+Qd¿ƒˆh0ä†h›£XŒ^a˜FP•ð
+wœ,ÈIËùŒ ™ëíP€Øl€(°§y<e¼c©#0/_ÃK›LÓ®®¶µL‘Ô}YX°¡0˼/I1š4"ñ¢>xP’L×®‘'7î½:uRRH^|žN´},Á¬ ]Ü%Éæ5rcfÐ6Fwx(úô"<(”.ÏêË8ˆ¥(Á‚ûî2%âªñ\Ì;¡w@tkléÈŠMqm[œ¥¤i "
+ÔW“á'öJÝñtˆ‘Í6±¥›ò%±gnÒ°$‘†,ñc˜·G ;;YRityqŠÌ. …#XQb(GßÝëqó!+cæÏ_$ÔÉ%悧pvøìuÁD/í*¯x;;O]knóªä›9•÷Îoÿù%ÄqÇ)úY}(ÿ¯Õ”Õd8$¼'ñ0àÏpfà R²@À×®]XÎ11 ëÃÅ®C«¬]vÜЮ`È"_¢šd.qHuÚ9< Ù?k^l ÎÄíš—à—qs<¾Å)ô¯ ×Hv†äÞ6Ä
+,0Ö1˜6‚ˆ¢!ˆJ½|áÄ^ða.yít9‰ÈÅóß6À]#Bbñó¬þ÷ÈŽà@à¸×î½| 0G‡äfƒúy"CŸ™âŒÌ G†j fæ`kCƒ•^Gï±È°–gŠñ xÐÇ­ÈV(Á€Nì¢ü~«­Ý$]ä%„ &YƒÇ³‘ìÐ’¥øù¢èÅ›± ²6ìb+N¡û•¹AU#Xó˜GÚÉB3ÄäiŠ
++2†¾‚l¼/Ÿbÿ`
+H‰”—A²6DOà;ü ŒŠ @\;KßÂU³ú¾ÿv^“Tf¢O•=I%eÉ îÆ£xk3KfñÝ|ìÕ[m~ü+r>þñó[ôxÃJ ›6.A
+ñ,^šy”yBlÌÑG¶Yf_!mô™Ãfk­îàï,f/üï\5ª[óâ}Äɇ?E­^½ŒºÎ™­¤Ç ÓˣƨV‚/|îˆÖú˜¥–s‡ŒÎI™óãû·[=êœÅ¼ËôÕG÷l=ÛàñãÿòÑCÿô˜þñù&hTêêI›}(d˜w#'^¬ˆú0wžZú Î+bò÷i”cðÅ
+Q&ÕÍØÝr~•Q%œ%wP£¤ƒ÷RæjºçèÔdºí,ɯ )®ÌZÆ æ±oÓjƼtœ;½šÛè'™Î+/˜¶qÁ“F ~8빪Á¨Çˆ~`Aûy÷¤YYwLÔœ­‡[ÇD–‡鳞ò97g+VJ7ªÂ@¶ZŒVj}â¢rGi<|ΓN:ãEs´ÕÍx¤ µü—ý<k’d¶Z«­Ë*¸îcN_!4&[„Ñ°2öìq®'H»„|ÿöïoöñ×·=pJ›ìg^pøëÓƒgY·2ƒœ1™¶ÀáY€Ò c]{g¼VùÏʸ'Ùï:|fdhºÕ‚<†šÌˆ÷g;%¾,³•¾ÎiÙ<¨ÁœqÎiô—¬Ö24øĬ—u¾<HŽ!Ià“·cˆ
+ ÞÀ çl€¾nX¼`«h܆ñM@k÷qk®øù(þÞËÂûx8Oož"ì}œõÒ£g¥M ð· užX ú‡"€$ÄX:ƒiûù€ÉÒkoB‹BzN*pè³£­Â¹Tˆ±Ì•ø hnÙ³Þò¦™(L#¾+Ýà]30tIuGWÁa:Þˆ­@‡*ÒpW±¥öd;¥¬ö¼ŠðÆ[Ö¹Ÿ´Ï¨ÆyÀÓ2
+ÄO0Ùƒg,u£E”¼jZìOƒ¶
+Uø "z}ÍÎ-(ySU¿LEˆ1H¦òG_Z€€ÚË}£Œý2Í ÒdÒ˜Båaf>Âe§ˆˆ’ó~ƒ¶ƒÊ,G?Ö9Ìà‘ôØ!H£#Á-»_¢áÕŒ¾ºþ²1½®¢î¾Yãr ŠT[Î÷xŸCNàɺh›>ÑÑ2O¾ÐÍá/3ž DL)C—Fì ¨hÉ8]îÌ<ÙŸ³ÏRñ 01Ô6Û
+ÁnP®
+÷ÄɆ\I1±U­ ‰ p ‚×Ý*ú†ý#®µÅ…J؈w‘屃ÀVôÞ =Ù‚L3JƒOŒOÌ$zÛ7m0š‰çÁD»„ìvQ¨–ÂÄÝÎ{ób¾bNžÉÀ‚œ½Ðt‡ù> b:óF°4’4,HÜǘÀYtiéÀ–!»±.C 蓬ÜÎøUý˜X¸µÀ—.4ÄFx¯3®.B°hø0<DÝÐQ2x«Ù°–‡_Qµbˆ ÜÎ[T‰d’³ôîÈ(¿d‡ÙÇæ[ħÉQÇ<¯Ny¦ýãÆÈXaü^Ô)A>ý†Ú¥Ôñè!Åæ…u~í!DvzQ55<¢c‹<ôç
+’¨¼¾C,‡,KçlYÔ.‘Ì®SÊåÔèJÝùˆ6qxSÓßPwvµŠC|êÂÅ×s:Ú"o)¨ú i0’¨kší¨A¢-è©åkUyÈñ|ÿv r™`@+ ercqSk9ÆÑp<o]%¼Å ¡XvÃ`º­úH-N•YÏ(ÿˆMÜ„à0$ÿ8‚|j±Aÿ>äзÐæCÛM Ló|Jfm IØë›V¼ø¢„Û}“E.¸ù+ºÎ¡¨ßÞUnç4Þ€ÏÄ)üצ1(( J÷É
+‘ëbíI`gŸ,kÛšÐY¬Ž| Âä0]4–R#¥?ÖËEñRúêû²— Ï]èY2o×KBQÐI PÂã°ÙXH`Äš+Dþø³äÉu‘G§ý¾kkI˜˜
+ µçIùÆ´Ð(½/Ô‡_±u[  ÎM²ËÊæûÂáXB4ßϳ1 ;Oëó\…ïV1 ’:S
+åOV¹é\çhãé
+9{ª1N©ÚUž¯êEL(SúäA-£œì÷ ï1lj}£KûúØ5~O€+Š!x¢Æà<œ½h+’-ì°A¨>.AŸo‚Š¥–Fô%}£Ý´|òm”Se ì>¦d0ÚL;‡œØ)¥J-JiÏ—S§Å ¥2™ÚÌÛ§œ­/Lös‘k_€Lç¾N5ñôŸË¡™<íÄÄŠ—`”“ RÈöÿâ'6»µÅãeâæàáÆWBGD^BŽ¿(6†y Rò&Î_µÚ 3(20û{`h É¢6ù:l¹Ú‹:ZÕA#ßÜV … ŠÍ¡×ÉE¶n®õoî¹[^oÒé(‡O”¤È:ùpÅk Ðg™NñYøïkÊ9¨n©â¯i&Nw†d-î¢1ãT
+‡|±Ômp_Íu˜wgs‰ù_mýü^›fÿ¡¼L’äFv z‚¾ƒNó°V/u mK÷ßþç`Dv“»òk!3•P ÀáË›1?]}o¹‘ÿ¤uÒiŠ8ƒŠŒmÚc?L%HJcõxiwcd%zâ¡ð¸ÐCôðÅL):òZ€-ºØa:¸leF¾r“×)E:p ÐÕCŠ¼Ýs@Ñ{Ä2ÍÝR¤[sN‘Uì:Á7 26™žS$% ˈ|Ͻ7×IQ4ª¤gn‰8¥HwçôGI0¢Iù}Ê%EjèMN•ÌïŒx>g@_x–¤@Øv`=ÅH¡Ër¤¬ÄŒ~Œ¤HÏDÿÁÂîÎ9FR’µjLµ-{v‘w´;1Ò)r†Þ_26à ÆŒYó^Ô:@dµXω!·¢û^Š¾>YÓc›ëKXPuYxȯÑZHöøc5p*îRÁh[áS‘JŒ9ðe¾³è‰Éj£µl<ôÛ—u'àqdÇ1´öŠú_!C¿hÕ
+™ýîŦ\‰À ÷¼J™-óXR
+ÿ(ë8S¸«ßu˜žø±wIºÇ_mûòË1Ú~¢ KÏÖ·`¤aƆa@1Í#ÇPŸBÒ­Ø»w/œ°J´ámê>«Ä}[®–ÛÙº³ô›y]ß»<R 
+UÔþ¢SvüõpÝ÷EêuÐs±
+î`K<­«ä4‡ÄÅNE¿GH lLXÞéðrNxa¾«,±c èc•àR#§ð¢· õÞ f‰‰á¿¼®¢|2SúX·mñG È…ÙÎA£Ø‰¤±}bA© 5n|*9Aôç_N‘ö9ÒuùµV?.¹-ŸwÕEBp÷êZd®½tl2CÖvª;Ý÷ç¨aK2ÉH–¨ã> Q~“é¾¢à¦ä…T°h#oö H‹{)1AIJ~az£èg§IL;fߪE?^R±´h˜{ŽR&á,§¢üs·VV‘“ø"  Ëà«Å>òa¢{Ò*ÊxÑøÍüiÍ͇x%±á€B§­9b!ãÂ+c k¬D b +)z2+þô}Eà‰€|ϹshÜ\eUY2›&ža}OáTòÛPÑ sÁ¬Õûªë9X«)3ÿ6¸à ¦:ôÒ*Ðtz 1¾vˆzNç´n’«à7¹k,ü L2;"×íXHa‹]Hy›JÐNþC@äSZâqË­HáYªX¶ŸyEÎÄÛ«â8ºøMÿ´èº~_¬¨9þø›J†¨J}ûøÇŠ"O•½Íž;,ù5±³Ø>¹©â$¿ºL`æ15ív+&åWÄc&u
+}viÂ\;FpãIá¯Û9RF|gby—‹'Í‚+Í®–£D/U>®SX EE·f3©¸¸X9D?›Nѯ‡"^ˆ
+#`š„¥y”À-È "e_£8(já›c.{߯ET‘“’²jû~gŽ‹ef6q˜Ù á1ÚÖèc6¯&Èb¿P½d)ÒœY|VÚO–¶S &¶íûOÉRŽ;Å=ð5{qÎÑR«ßhAjsJh´Nô‘­g¯Û9Ê IÂ…ÜY&“AÒ`QúÁu¬ë¹ìÛ\%
+4q”¾A zE4Ú««^2mØØÑmhYIë’–¶ ã’¦‡Au>K‹RÚÀ(À–¯çÆýÁÿNVÇ‹§…žºzL¢ƒàLÌÞAò8¬êÈy“Ü…¿¥ÓŸf±•Eé*Û'¬]ïˆ]‹"wc]‹
+Ÿ·„Yp×.è »ªØ÷СlÝE¸0Ïÿ¤Õ@½ay{’Ä‘±·’Ú:šgÓ#·ÂÇVãVäa•§Ÿ‰ÿcü êq"ØT˜Š|Ñ—[Q“Úi~!Y‡š<š†Åæ¯Ç‹$ù5ÌË1Rrîšßà”ü6h0ÿ†j.ïçÀaü$ò}}M‰ôCèœñ(Q‰™¶±ƒ´´‘ZÄbM¼yýéðv†lŸy$Äf°K\–JvÕ÷ë“õ9¶ŒêûקgÿX‚‘eŽ¬±ç&3» xʦm8/%>aZilžØ[§ÛšZÆ<"›ð7TS]€P‚y”>¡°kjô1å)ž­Z‰l* J
+ŠËÔˆ-#ê¬Ù‚¾þ¿Z< Ý)ú²¢ H»ÛXÏJòOX
+û~•Ørá,‹YyK®—vÔò?ÊË$9Ž‰¢'Ðr]‹4Ìòu…>­wÔ²ïßï;Te„‡1»´)QŸÀ‡?쬂Âð&Â-µïàØXR)BÝ­âÜ8º¢Óh7,’•QÌ)Z~Ý€¨™…@EÎe9Xü"%ïåRî€RÖ˜ŠýðSM¹™Ú©§‡Ó…«p_ºé·¦‚숃dŠ+Üš-
+:³0ŠF N…Ö³ƒxšŸµ¶ûÄàÉÛC%¥x”¤nG¼-CÀÎÏæ÷Sët¿ž¸ñŤGc”­SSe€ÄÔÑ=“)RêäÊ7Í|¬ÕÔBÚ5çoSxþ„‰a•rù‚YL+òšî1;\O׿m³Z¸@¯Ç„Âé¨Å¾q7ëª1✛&¬»"²lÑÊ´îÒK'cï ’º7ó bÇhk/â’±óŠEb¸a_$0aƒI!Pð­A1S,ó`Ò×»©m®Š+ck‡Nb’ö‡ýXl–•W®,
+ž"#¦c_ó–;_ç¼õóšLU×fߧLÈôó *ËVˆBX/o|˜T,(<{‘Ä5ú@n-7y ‘ø]–âæ;±3$ Ö°{¦)ecfIÇ ˆøæ $mäE6Ž‰KÈ“P®îå-A"“ˆß¹É[W7«åÉ9J™\½Üä‰+ÈÉ[èl§€ç‚tC[çä$ ÚËŽèqzQêÓF#›®7æ·ºyKÓƒ
+™X¼¦öÙ[ŒWÓ±ƒ30ÍímGAœ&«ÄO û駭Ÿ—G;ÅÊ@¯£á‹JÞZgVBR…ñ=ó@\D‘¨Dиú†¥ê,zOÛ€3eWNPq1<Aãýúw˜Bêš:™–UÄÅn
+Ëï`ì°h<’|…Gî…_œ® Á˜¡ý#îÚè(4íNCÔç‚{Ø#µªA\÷žÍZQYN@Z”¯ÏF÷2Jƒ™ß©Ù,¤
+O650j'³.¨FÙKЋ-`9êkæO6S£ÈJ!KÙžÅà´ ¢|˜ÈŸ¹ž>$>DVxqëO‡¦ueìÄ´ï4q¢`Ñ3ªrY„^³6‘Ö-ÜÕJÝ3šN˯—¹ŒŽ÷¨ë^ŠsäK‰=ÈK«>ßÙ-XЂEYx´­J£ó½ß7 Œyj’× Ø DsGÑP»q„ƒAœ „ȼÚÕQU‚µ.¨_g{úø®TUþ6æMù¨>ó¢™ß%àÃ|CÜ0è¶þâ*˜2>L¸¥!ÝÿÝõƒ +Æ9 òs9GApÕx–•ö«(°áÈ»%~c‰Íí‰×/f&Jî]Æ®FÖ"d%²UcÞÌ„5œ×:Šs¹ÏOãHÌ/ X¼síòtêý× ˆz1jʃ)Ûa]Ñ”B‡ÙÆ‘”1&ÆÌ Oç~qK„›¡0•‘¹‹˜®ýôˆ %ÌS‡ñh2LD^R—}‡³$®G4×¼\Ó1ATƒ/“Dà é9%/œ ý¥Æé0AAæke®1-è.*›¿Iµa+ŸPu”ƽ£ß¸}­ü®¼F&‰JG¶ü:‚F\ª«:
+nH“éõÃÛ+ÞY;,!KÖ÷I?,¨ÒØ!œ®?’¸{<Öy©,†öxðø§EÍüUâÀLîU&õX¯ ˆ€0”rbGŸv©"^•µ’4eºG·T1 y›É¾#¸R©‡  Üp FyÊeˆ£“ ¢‰ŠšUÈbA´6
+·wdEêˆ0J¨
+•%ê<(ÁʱH\ .³ñ`E«"‹Aäëð¿¨}ô :ªY¡‡<)·v¿Óž"X‡±…ƒàïF,¼(H–«˜¹Ž áÿbV6dM>~ù y)´ò]}à(l,ÑsXf›˜/˜#HÑVð'*A¬G$R¤ŽB…öYŸñyÜæ‚p9XŒÈ‚ïn"np'>km»óAÌ]q`˜ !T•ì;„ 2¬Š .HWoXK^Z„y‡Y糬¤„½ZyŒÁ«ŸFÍÚdmakÀ8»<ÁÒU²›Yf2(`Í$tDÐÐ×;ûµÖ§@Ugíí[ãÉÿ4 ¢Y—œ^Aέ#³ò“(?ÿ>FReŸÆ¤Æ;ìgŒd áM‚ Ì× †ã/p½z"ëW•õÂ.$Šþããö¿·&R ó°Ïêu…yl„˜*lwBjJ>¨G Ñ@Á8í‡ÞÍ
+`°e¿Ž›ÐF—áP£!{fQq ÃA|Zù©”2»ùLGÇ?‰Óij1¦Ñü¹Bd±¶4à“?œ>9¡H—!HdV©¦½J™t½Ë…z`ï] ´ÚÔ Ã@RH¾Lj(SÍ}ŸtþLAF’FŽ#Å1vƒÐ+7ƒd¼´ÎÞÆálÑõ_Ý´œ)p’šr¥¼ÛmÁ Oˆ!ÊöHPÒªså#å"ÂLGÅ;ÈËp~ür@Þ˜k˜ œ<ÙþÐeó¼ãÞ
+WZv «]¢Ø¤ÈÖ¢²Ù‘ë¥"qö}˜Ö1
+i•s²ß™EÏäûÎ ¤BoSÜm$2D£¨“n ("œ´³û¤+Fj„}g
+Ó)ˆDê›­äÃëùôõÎ`h†Âí ñ±R´ë…)ýí‚®Ó  –›æíML£hŸyÒƒ…¹ ì´–úâ—Óh”ÜYûœAô$#¦ak3C3­âJ†L³4…²õ ~Òtôºæ•ò’WHgŒƒHÂŽMeJö™›Dž=0)Ó .ȃeÑ\ˆ¢+ÞK‹±Ï¹"Æ”¼cø”,ÖUÚÀÂÌÑR_½b'•$2>ÅÃÞÒÚ”A‘R“¿€ø N­ÂŒ­mg%u\*î± õÇi k\»D-dyAgr>ß/Mâ_ÿfÿõŸ_ý÷q aã^ºkõpÃÿ´±¿o@L›µñ!ìÄ#èB_éN €ÉJÒ0Ê'£(ûÒk‡+¡RRXAÅ’ø:YËa0y%ZP;ÆAŽKA`4.ÁŠÁ™óŽ¼úr‚訨ã'h–ÇýÊ7fR'Zjîe¤im@Òyó¨ø”a”ðZuºÅ‚V¥ ÂÈÓ-±yiÕ>êêr«¸ ˆ6=üÛ r _iFÃÿ£¼L’ãÈ• zÞ'(Ã<¬ù—º…¶Ôý·ÿy PÆÊD™ÔÝfmMÑ$bðáü*fšM+«XëªKq` a’M[oº'!Œ€Ñ‡™º@ÖLô ¸XªÞÞœCpTOø–ÜOsš¾Û“NS|)Íi.þëN}±…UKÏ{"–\TäCZ…›6OŠ™ä8{ùi™ø´1!éž “{ÑF¬iqÃ2¿k_¥W1YJz!mŸòáí€Dv²‚üxs©ô4ÁÜš‡X@«•kHhQ#æS˜àŒ+Ç+ØEP1ŒG†·SQ‹@Pé±cÍiÎ䔉$™Y^^ß@^¨÷ë㺩¤UGÎ’Ç1œ|âÄu} ¼v¶3ˆ€’Ùƒ±ZQë€×ñqÁ!̤ 1Æg4ƒ¤Î¤ œö~<Ïî]¦cînÝ‚&%¹¶Ë×±Eöod;Û›‚Ȉ8DäVµü3­Ùa¿Ò0r? ~¯DùIE.üÍ1ÈˈŒÕ•Rb–«¸+,ÈeØõî†ÔbÙ¬]wСíQjeÀÐÂùL²5ênŽ²Ýðúþ‡-ýe»\#•Ä_Sròˆ„L´>x[æ;Á!ÁŽ#jÖûòè°Z•çHÅ!<+ r”à ³&òÃT Ï«ƒ €1¨XÓŠ²AL8àèÙ
+ê´ÞrÀ%*ávÈc_u¡ I0µÍtxoÔ…läÙévQ´t"­+ħ‡V"k Oçd>ä•0^«á@ø&‚ÿFƒ˜vÖw­>˜-3KÞjw*Îp{ãÅ(Þ6ÿòýúGfÍ8Ÿ²à[X¢á2ß“¶rTTÀå¥Ë‡¤Œ‹ö
+#nÅ"»ˆ3g\@¿Þ€lÙ tôoiœ@Htf õ´²X ŠÃcǶûDáÈqŒÅ…º4G­"ãOq±{’Æ ±¡Sá•å„¨Ìv’;¯>ê<»Ö§\« LC¨°GãÝ1¬6+U¸Ÿm;¡ggŒ;$Ó¼5–?ÐP° ¬ ÀV˜ïF¥)øÂÇ€1m¶ˆ°ÆD•ù @üUiŒ˜A†’(É“ …UÀFÐN7³IŠÈäÅ°ó§&±‹qk¿Xír ±LúMË4ùÄïùÝë骼<¯¨û[°¬CmmA(C—¢cÄüªŒ€0rŸnõ—¢Å“«ñÊh›ÐWfdY½Ì¨â0FÏÑ\"©æQ›ÈJ(UÔšÛJÝA(>¿eËù™ ø~ªmM$tæßSÆFŸÁ!ì%#O9°s)tI– =ˆÇúœæ§
+}ÿ‹TýBÕÊ«òîs«DãcÃê &IE;è~³Ñb+"úîÿÄø…È"Èí;è~ÝßAòßX¶§hlÁî \&V_+‡5NŸ¡¼7øi{ôˆÄ1£t–5kª8€/O›ñÛ®ªI*ÌÝOÇËV6žq\ÇpþIa ¼¥] hºÚãÑ~
+D¸nþ Ÿ<ªU™)ìÚå²ëOûmFŠ#H;v¼|î×Ç ÄÁ›Ìô¤%/qŸèWÂæ¼C"[
+“ó×êéÝ7ç@“BäÍ-ž²¨ÞÝWqø<œÀ8žÓ’íÊâŠâÝ4ñy™ "„5‡°ŽSÛ¼ ì_%x°lÓ×ÇtÀF‘Q€ŒU”sùwÐm)N×ý貨Çy¿‚ÖÒ×Ä)¶‡TÊ<„¯?G¬Œƒš äçÓ ¡òIŒDÜñv‘@ù0ƒð0n0ŸÂ5……IÍþmWÁëðBÆEì«h ö˜s–ƒô¦€N²¥mAZàKXÉŒåR¤ä( »+s²ÒIç&ŒÊ=íªò@_3ŠŠgžÛÎÉ.gKŽÙΡ|aqAx¶xº¢ZŸüÀÞQ|kÝânµÁÇÊ-ÙUù1BÆQY“X>~.t„JvNO-³˜‘MM R±Kã ´½T`0:µ‚ÀtõOö58x©¬öA.õpü•Þ@ÅF<©®0<)ÉïqüÓHüoAuR"—·|rÖÍ
+\ŽD…Þ¿ÏE£Yæ›~%‰*‰5ûàÞ ~?ͧò U]VSùè,cAu-#Ð!@õöТám*@‹ ½ÀqB }®ÄtÓÀ¼¨¸¥Žwô5LîÚMÈzM@
+Êœ´³ÌèÇÑÉÇlðn—Ò@›±¤íáÿù8ƤUéC#[\Z'0$Q'm—VsÒG1µÆàþžoÒ©N£CÐøµ¶’Q÷xiôà” ­-e£r’šUÌ%BV‘ÂMñIáóV/ë¼bÛW¬ÿ%ؽÃüõ†lðP7ˆ,ðž÷·shz„Ç7Úþ‰££3(MY†Å'¤ú¨’s8Uxò8РûS<}?Íødˆ€ø™Å`‚Û]ƒ>~çLBÜðøóŸ·?þ}‹oÜ•„Úí¨:aŸ¾%À¬¾ciþÿñ–ó€ ªÀ4rYyT½ eBN&Àûó&(G@4 $»?,÷*íWÅ ¸4qÒ
+)PÕH…V_Ä›†Ü¥ù¤Í£ä¬­xp‘SPFŠ¨¹k'À5zˆŽÊøèj™nªë¸ %>TÎœ|'*LtÍÊÑcxž}©¶J§êY·˜ç[в°€¤ZˆN¡V€(ò
+Q‡Pšvß4f=Šñû](Ð0)…ðɨà‰ß
+aâ©*Xà™(õêSþ=ã!D[‰»b™íf.ŠÎdØöiPÝ……B°|äΟä#»°Äð­
+¥ÈYu;Z‰!h‹…ð L @Gó×a’¥/5÷Ôa!Øœš”­´”L&ð1tË·ªhÀZØ…Cc +Ô\õu¨ð:fŠÅCd—ÅØÜBD> ˜é.¿˜˜«ø(è[pƒ
+#Ñ^Æœ !®uÀ3äm^3ÑJê=ÞD0RêM÷û]ƒŠzœ–cª
+–á99Þ\”¢†¨J#®K¡È3Q8ݸå±*Á
+(õP“J¯0ù­u®*,^ç£Z4+™_ªù»ùÄ·µ!õ4óQ½ë m¢ ¶Ò]ø©ü ×G+ˆtâ4l^!z+šž‹ ý„q¿ y5†&î€F‘_ž¦jq þДË$M ÖL×x}Uu hBý§»\þò£½Ö
+ Féâ
+jq‚WЂêåô„ 0`û®¯×
+\4`±sç“÷kš.pfÚ”) ÒÓ˾¦©yFmÓB€]NùPZk'q6†·Ìɵ=IMHÞ\;‘€.q(~bÉr!SQçi+¤)o¬@nÉ¥ˆ¢†Û^|+èŽRÇ4zp±ä™ÓP¦ªc…heöÁû\º!HȈ}êcÈ«•ƒ°°I–šPðq:MµÓ MltÄñV Ì
+‡¹óèœÍæÖ)FE#ïBhR=ù@–¶ó=
+ÙÞÍ2¬€âˆ«ÛÝ#ãØôgVõäÛÈÔ”5 ífÞ3ò3ä•ù&äK;œád<#B"“êñ Î¸ˆz0@´ÔÛoöCC\GXa¼Æà2òÍdÓ€ÞP!è*Œ
+êo³ŠbÝ!D·
+ÈϲHUdTLRjEÙi—&ùpDžÉPC)ÍÚ¨ä8 %j«4€(fÌÒ¦O|¹2pðÒÖæ±m•ðæf!ܪB,?IgÖ¡²ÝnÒúŒ2£5=«€bAC®f [Ne+*¾fOµÈñí48£øÛèðCˆ¶â͇=]EgúÍ:z;Ð(ΓSÔ¢O+zrð‰òf
+Û!Ðc ÖV¼ºxÜUOTˆT˜…CDD–äSªç!õ5䕘ŽAª N¦¹N|9L5w¬ì¢»ïTŸ‚1ÞÜSåëÛÀC”(ÁŒ,­Ù:+ˆÚ'öæ>‚a¡~Lë0d7E´‚”÷CÈ*
+¦º â=í¸ eS° Mìí!re‰Si^~jŠõ ›ßœm v¥g\•¨ÀV~œ¼ dÇ«a4t 5››:YMç…^` xFz÷|tf‘
+Pæ0%ê3ˆô­?æ£þͧ~2a„F‚š·:
+ãìU«º™øz¿„h« N2qjè#_×É/<£SxeM2ÐlK#„à Mt[{¿ód~ \qO5i½òdfà1ÅÓmPŽN7ms‡Tš¾‘a´À‹Bí#‚[ûV¤6Ë-¥¸^ƒp :ëh$•oöfl m½œ$ r4ªÖC`l“)‰ÄûVÈ Ìó(”šU¸%ÎzÖáΦª^Ón@y‹øð´Ï Í’7¹ÇõÄèE_B#f,Ïænx€ô…è¼ÏhäÇËh­º®³)Wà ­äç6VîÜ@`êŽÛpzjs@)Õmµ<¬3æP!Ó´Í ‹LÚÊŸnÀ?¨FµÑCk¶ÍÕÁX߈’RSu`£]ïDƒS­
+©&@j€ŽáÃ+xáb©Bï]*MZÁÿ”¼WXS×€«¤ÙžBøÑuêab+αòf• J‘“îIåú„Pqy|UØg€&T
+ˆÿrøÌ%h.Q?ÞCä¶G—ÇSÈRJñxrARw§õ 4·KÐ?ÇbQn\:|ýÆ;;dUkÊç]ÝdçW‹å2²^éÝpØfÒqЪE„}’]:ñø'Š†‚10¬@8hŒ.4ÍXG ØT3F¯¡^¸'¸?ôÍ(. Ñá _À-´bêaµáú¥¾‚!mŒÉû+áK™i¤¢W=„~äê¬UKXæÏà¤Fm~d¬q"5ŒTã7æ_ñ.>æ•?íóÂÒ¨;é£ ÷½³tÆ48!„)¦½²t~Ñüü¨‘0Èþƒ¥3WG¶×&¶hÏï,mƒlFS_Åw”¶—ØÁ‚b\ù@iãÕiDËèé 7úŽÒ„0ó˜1ëŠÒöªº³æOêí¥íµÉ¢ò]Ê¥ ! æâ³v…éÏ‹ß‚ñE–T*çÈï§iû48&•cåz+0†ª¦DªþŽ|{Ñmã+Ò“_·Í°0*‡j¿fjO8”=ºoÃ÷yË7£ 4ÃbÄã€Î³‚ÌÍl!º¤²z¸b÷eÉÛð£ÀˆÉÒÄGÔ%‡$ua8ýòkôœš\Ž¤`y·!zŒu˜ ;L@2ç ­øžr 9[eø9;|ô´ϤÇlR!sƒ‘ªztbƒÅØ$ã!¤?Iµ í/B(H‰½Ñ
+HŸ)™GÚ+]vUâu¦|
+i«®Z}Š–t-?< A ð§á)¿:_KUÃjrÛ$E¨ªÞש8Òa8’^…9œq¿8‹˜ÈI[m¡ Uø(­oü䜛8êÀž²Õ¯ÓGï.Žã&þfTosÊï«q¾ýó4å/”ZvµÎ¾§WhˆÉoÜžŠ¨c_S“9•QÀƒÉ³ŽÖˆÑ:z{¸¬×VÐÔï²ÊÑs©þµÕ{^Û( ùÞÃ2¬ h ^OÌôH‡ÕOy܉L-y¾ÙÖâsiå!ä[CÜéd¾2B#MLõëÏ_Å3ã µøV>îú£ È~ª*“LB…q‰Áfdœd+ä w0ÂÌE°+4pjØó –IF ê@-²O[=¾1…0–
+‘èbæçXÙÇm3Ë%Ö—ÂÕÉ/sjø°NÏ8„¼ú3Û¸}¶esG@, mæƒçÁWˆc*æ;Š¼®Æµ1À4ÆZ½
+7G~)'ª7Åa!ÈÇ(å„ð<}=ËCy>¸»Ñ»5x½çåÖ{+¨{­(•ùø%½0µU?!Xæ£cðZ|Oe¾ÎÈKñ”Û']lXsÛ‘BÈø’×a¦XZºóÊ<׺Gh. È©¬¢áó#g´J]S¤8쟓ˌ@S_¼êúqP%ˆiÜ¥òV‚[帜9ïœWrU7fóSC5 ö\Náhá=ÉúôUTëtl¢º—EA3©ÁZP©¾eàTÎë¬ÃÀ$&êÚ<2®L³vÈ «õªÅü02T¤tå(@Ä”KÁHN)$”LѼ“ç>—ãuŒ°ú„€­ÇP³žÇ:½öH€¾lW³é%ŠŸ}QÉâ»(žùÁÛ—B渉.¢o싸Ny!“±9 ·W
+zÔÀ:ÖÑ¿ÖŽ˜0–6Æùâ8¢„Òˆx\í~m~€Rs%¾õ\”oþˆ ”SÒ&3õƒ=5Œ¶1" †½j?!byá:Þí¹D,)³rÆ­Œ‡ãl¨ÝðŽeSôcÝŽH~¦°ëµ­t¾MsïäåG‰,¤AO·Ê `C E¥kNY;5g™‚Žª7†—Äž
+„x×å
+5 L.–ç=‚‘ui ¢ÖÓ>h˜v•ì‹dܵܟgCš¨#êÊÓM‘SåÚ>4
+â+k³â4 „m5¨ýL”ÔšQ™ÆãT)
+–¹
+ºß$£™‹çéYIˆY®sæتÊÒÓV>fà Ø¢·J”_¢êˆD€ßIJ—«\;õ™<ÔñRÁCQ\‹ã
+ƒ1DÂn¾K<!Ótà ÞEäè‰GCìð,o)Ê#( ËQ”ítœÇሦk=”À
+IY£å“nõ)Ú¸7Þž7vzÏMn&íhàakn'W*½Æ0àµó^7Å K_à 2‡<ËSŠÒ«³Éï²4ö+¶â‚ÒP‘ƒ·a
+Áä¸mѼÄv<`îÂÖup?[ßWeË©\ £J¯pÁa2Gf®²XtOïPZ•Wjõø ¼ Ðáñ|Àw.žýõzS–ô·Ó(8H ¯ä±.&‘3ùôá·ÈžFÉŽÈ [»Ç×Á#¯~0 =c¶9S¶s+è¾dYu2/¯Ám(Ä×R²"q.3<{@dp‰üÂ׶QÓ‚´ÏR$iªáCâß*ŽšÂ½L‘ú ±‹» 8‘R3òC‰x,¶Í‡?¿ýû-ÿ‡«qú°•ׄ.’œ#Ùâ?j(R Àœ¯\PxWÈBvÜ`Šúyz`_.E*áþm2~©Ÿ 
+ b]^ Š
+˜@¤Ã:½1÷lºÈå,ÃQà†{†¦tY•cX:®_Œ…“bÉ)añú u<%SG@زØ
+àï1ç ´‡íÉ
+×°Kú±’fzºèI“ë?%”¨C®d0Ožç›b®)ÁQƒ ‚©P–Ò¢S%,²ÜŸtš¹°(hî‰,åTðr­ÿZáÁ84œ§ìÏULL‹9èQÀ d1z~¡wŒsX.œdËÁÃÀp_ŒÜ9m–ìæÅFÊhSê±ZÜæM j•CØ%b? ø"@ÀvPzÑZlE¸âÊSÆkxÅ$áŽ`¿hvÅ)Ñlž ¿§_x³êIŽ'#ö…±R«“Æ_ÃìFq2~'“+C4´™!à®ûÖ·BÆPä³Õ_™æœ”6'+(?BÝz¼3]5rȳmR’SFmòºëR¤âÔÌ4Äb8)!XL@‹3´]Âô²
+ÆO%¼²8gÛbdȈzC݃ ZÃÞ­>÷:UgEo1qd¾™ºˆ¯»¤˜¼[Ö`Å*Ú\¯¸˜’Ø
+´3ÕˆT;p7ADàÐêÓ™1=•bÆèqÞÎy¡½âD râÐ`à}ì¬ÃºçR; ñÊHÅ™NÆÓUphjkÙÔ9(›ÿIÎ&¦+ÝÜK&rňˆú}nnE²(lÍc,gI^¼€d¨±a>«ì¨y§0Ãàh$&_ÕSO£ñÝÊ|$f1-Óüõ
+‘¿2WŽU4Ÿ¸M=XTÐ5ñ‡ŸÒx’d˜zÚÄ>’)Ü|“8yy”–’ߨK‚{³ã§ :î­ ?Ëoè ;Þc \\f¿ù¢ë7.¦_ŽµÈrg¡eĆ_KÏ%i…A˜¸+‹ŠË”à}þì ø9 _ýݯ‡’+¾ç*ÑþýPÄd`š3ÖÇ‚1
+ã"—ßû ôV²9&/‘ÛÛñ¨Ùgv šœ@¢±ÖŒkañµ
+,˜«
+Ö½]í¡bë !À –ÇÙ
+i…ƒ13 •³LB&þ~³@\¢ÀàX>J²¬_"³D’™“–ÂãMV„Y¤-å2.¿¯ÚÑôÙf ^ÌI´ÈbŠ`†+"Ûc8¨Þ9°n¥€.È;í„!+miŽ¨
+ ýÕgê]ÈÈÙÿ4§(ñæݾìÀ ÄÆãkuSdÕ©ýá䜃tn€¤ÕâJïqqb@¬?æŦd÷˜äÆc…O¯È€¤ÒжFSÂCG·~®STÜ¥YÀÆë:ÉwŒgêëôø*Ö<æÄ–“± ØfäW€AÖ$LÇ.!ÀqBKJÎ2Ü'ý–ƒ¨ /LbF>뢘*f¿JÙ<ÆòPx¼¼××’}’„ !‚]j{xNƒëJcý2Y†)w¿ÊÞ;1ñ.iÔƒ4µ"A°zÌòôþ‰yHB–†ì9 L'L÷©¸E¥¾u_ ùÛègØŽxð:â–õkƒ…­+*:ÔËßñ‡8‡±—yòr=H›
+»Vë¾9OÈ=5ŒÞ•€ˆ‹—iÍòz€Q>!°Ý²ˆX‰ÁÀΫî Q›\g@3è2E9+ IYÏ­s
+s‚âw¶éI˜Øb¶«‹eIkh{õØ,þš”6¤¶~·Ùù¤;J†¼ËÊ×F7É<ÿ ô#*ùãÛ_ßhÝ#wž2kpþo«Aè—° »kAÑχ"l%¤=tåK·Ìh^NãA‘v½r]òM•ÍB›ÁÝ[
+Ü’Š€‡ÌÉkµh}U!- fKçˆ=ÀÈ?´RŸŽþ™
+US.}Ó@vÊqܸˆdŒf%ð‚ÞDZ…‹Õ¯š†4®ÀÁVR*c.ü ÿÄ‹Ÿ!ËÀP0¤¼ {* ÄÑÿB¢¢ÕI6X?0Î訄jÂ?”(Š²iò;‹ùR]’¦±È{g(brs -å3‚¬)OÜ =G\ÉÂB³~ÿñ2Ò¡’!*ä‰â×ÉqQxç(ؕĦºp½M»JÄ÷ÐFì|©1'R+T¡|ô`e¨ï2šéG]XˆtöfÙ#ð÷Ò¥k_óªÃ´ÂUé+)A0yå…i8˜¿•ì£ÊÂ
+2
+ÃÉ=Ò+Ñ@‚°ÇôÑb‘xPeì“P |^Qg  sLJÕâ¿RhfJ8‹Î»B‹ÈTXØ B¤ÙÍœ^íËÊŽ]Í… ‹á& ZKN¯"ú™Å‡àýyWƒ#š![B+™áߢJdÙ"µó!%š€Ô”}<î¾O…T˜rÀnz
+95!¾Þw8Ôì±
+¬H>ÒÁÊmaI¦Í Üé0<€öBÌP» ô
+H‰”—]®[G„Wà=h#ôÉ&ŸGïÂÀ<9ûÝ<F,an€$‘JÍn²X,š[W›ÿˆë³éTSËüñóÛlúœÍ€h·n‰‘g,“!S|®x$btïÑu,ký@|ÙjsNiÍ6„oçî]—ÈÒè½E®Q‘šÕù‘ödÒµ»D_}Ó$–K£w?¾kmÆŸ ³qѶÆ+Ò¨KðDmÁ­Î9ËC‡¨¬¶„·ðŠ5¦Ûywç<f÷šÉ»ÉMçGd¤B&$„®“œù\ÒÇ$¶Mr±!-æš:»ˆ%M}!zü£Pó©“›JŒ°ÐŠ”QÉ…ºh]‡Lf•ŒË>Ç"\¦û>Øéb1ÚÚÎeÜWSÎè­¡Œ}
+µ9é3rÓe¨rí1(«Ø©e[’76>&¤Óו›aZòÐÞ׆hÁÓ‰äz9¡Væ”؇®çø¢P®s]È °¢Í¼Ì¢Üî0Ø»IÔ£ºsAƒ:w¤‹§¶¾àIÕ †·ÖyXÛì[ÏiÐ…LP¾:FH7g€òØÉT-’àVoâ#4ˆ_‘,³×C$©ª©<STÕ†ïs 6†:œ¬‚su8 Ó×Xâ’˜®ddk%×êmN¨hd©q,))ŠÆ àôÉINhh–ÃF  éh·¡‚ë5—‚ŒÖ)¿˜ýñ}Gê¡(?…^…á{¾é>~Ђbø®R}‹œÍؽcõä'ÍABJ)ȸ‰˜¶N’;‡ÂY1ªù¤Åšt4ê<œž£…BF¬ëU¤”‡1rHÕ–9mFBNnÂùlöiœ¶©ÖžÅD¨
+¢½u4"[Q4‚@SC¢:9'-»Î£ (…
+¾óê;÷Td€§lÈ22LcR‹ñALìiÉcë©ëQá!\¢lH[†8 R´p›ÃŠNh¼? 'WƒYóˆçtN@b&yË-„í !ë:ä-ú DUhj‘KiÇÒœC½2•ðV­ÉÐОTtð HÊ @v…ºZKÕÑÈs Õì¶xo&Ìk)d³ŸËð¼a´¢Îº.B‚HÒ ðïzÓb$QÉÖ.Œ¦62 ùsR#Œ:—¥U§eBÁ‘õäw0±šùœ×m™?¤¸eGW™:ÍT€E½Æ”*ª$úÒið&Ä5žáíèê:üÜó¥çËGÝ×9EYl––@›$ †´V(›BW#Û‰Mðp–öªÎ´,#nv.“ys.¢±êÙæ©afsün¨IÍèxæPYH‡ôÁ·ÑÝ»\?gfvÃå'°.‡äötŽhäªîâiI%rÕ ½f°Ìü%>£@P~õ6Sö9
+mc†ü,AJ;[ó–GÑÆOê®_T*À¹—"åË—çoÒbЉ¨Æ8†IçÄF{¡&ë@:Ÿ¬à_Ûü]­ÓŸMd‹fˆ3_œ®íK WEBÄ%àcCÖjé0ý@`9}̧dçd!Ì<@ò/ªÿßoˆ+ÄΤâæ¥Ð&©l¿¥DFò?“xƒùqùcFÿ8Á‚' IÓ~¡,¨BÚ½¶)6ž2˜SÌS }ƒœ®n‰£•Ž%øè&\<“¨L|Vÿtñ;Ð[îÂ}¤Û$ÅاÌÙ{Pš_.ÂLÇ[€t¢!ªþШs!ÛѽÁɽ!SaèL©:çð}Ò$ŸR43ɵ!ŒêžÆ ÅÛùAuPdeü…W¨´¸:g9Äuç¶*ÎèHF‹?ÆÑê„#3H
+­™¨|vÔˆ“¿-ªí
+EÆ[vZjò ˆ1ÏbísôJK
+BƒáC©ÄywMk˜…yCüÜÄH“L¯å“&©ˆôòz!Pæ´Àb2S‚¡ŠDÝ\ö’´g#íi=:x?
+‹ZŸCçOïadê9G¤ׂ3݈ÕY먢éïÌá
+‹i`O׳!ø|Šï W¥B„„ÍŸQ¡Po¼õ©BPqŒ+Ž›MªÎáX
+œfùx“î‹G2o BÁ…ÐÔ³Bq[²³»¢"ñà|@¤=-cy ¦ÑÏ£l;AŸ×!ŒKVO¶µœ¢ï
+ÍDgýļڔ««v)Ú.Ühj¸^áw ºëP& 䢻2ÍÓ÷t`Ú39‡”£GŸðØC†6ÒäÂœ%´‹Ü!èøFèQ ·ê]ÄL¤D΀mÉsvÇ}1vZnb&‘º)¯‘ÉÅz i 7Égù]JÈ0€M¡ì^Mô…­WbÝG)Ã`{1„ŠU^áˆ$
+kíïh›ÞÐÃÏζHy
+߯¢DÀR’/‘/Z¤D*2
+ðd@ÁŒÞÈyñ„®€ä2Î(24¹.÷ ¿{—ÆY‹$†~D¥£È %¾ÓÔÞ.h4ßQƽ”žz ‚jôÔÎA¸U%bÆŒ¢
+•a£ šœÂ=È%pùùæ_Üøv³íL_»”ˆd‡t2¬3É«åà!)ƒL¥ËŒRÕúd9äô% ‚$ÙÞ”ÐãA¤MPG½hJR ëw/y
+£·’—0z-â"ÙП°ª2(œÌ–EBÄ£
+<!û~»Œ,8 ¢@`ñë£0òÍ€ªJ;ýK¤ Dn-ŽRÔnÛŸ¸lBe.#Ád†Æ­ ÐËÚ1ªæΑ¨æHIØçÈcRb#’L_d·!K­géì¢}gr'Þ5Ys‘dYžêÂNLµà—…'ÚÃá'Æ(ON…rx´UgwñŠ
+Æ¿Ø»’RÄ0h;˜YÆ£qŠ&ytì
+ÎÐê‡Äå`GÓ‹R«t‚>ÿÚEàj Ök2¿),µé»JäèÆË! P8Ã"tBO»+ 0UW07Ì6€è€ùb-ÇÓT{Ø7y.è‹Ä1Ûþ£XÒ’.[˜ º+sI4ܼic”šHƒqWæqÖHÁî0/Â6¿°TŽ¼K‡ÊâK؉7A“M« ó†Ö …üEŽ“¸/?Wtí¸ã?Ý›WÐpüþ¬Ç±À(³’oJ@âÒÖ¬±úã¤ç"xP9Q)þÌ)jZqqQS0Þˆ€ú›`ÜÜÈý¸ãçPEe, [’-fiBц¿5ÿ W/ÛÝ”|Ü[b˜ìõ™܃þs–œÚkB9ˆ¨šS“óŠ¢ÿð
+£·á¸]Eu&`±uIÇ'“§¾ryIá²|gP´AB§¥ ºHÒ¢wöW™x‚žÈoiÇ=½«øQÏ+¥hðEÆjñ¿³w?YÏjƒ[·íákë'¿â¸Ñ0½)âÅ™=
+¢Íöw >n¢>@XÁJ±9Dº±+$¸
+tk¶€;K‚Öð@FâpPÌÃs¤>4
+É3ÁK‘·yEŸ
+°ØÙºýk’§À½‡ì5Ô‰@ÃæI4x@„XdýºÅGpŒ ùäøý‘Mµ}?<F)× OiZÍI©ÊîBšèš—HIéÒ®ÅQÊ­‹§¾ŽŸ) Ä”ý¾{º‚¢…+b‡<ñæ] â
+]ÂI©Ÿ“^j$ÚØÖñÆË9®c‰¢+À`Ë@Ô<˜ŠÿMºZC²@Sû×¹™U º_½
+'H´‰ª¬Äÿ¬.8¸Q¤k̦t¸4ÐNBfD“`Z/aN§)ÌÄEpËH,òÃpŠ÷˜ú×iFÀ©à¡1CîZ¡B9Hk$‚Œšºì‰—€X‚R©+FŽûEoÐɆpÎãÄÈC,#ìh >pÐ5÷õøG!0­{?>÷’3òRrpà
+|@=`DµÚ9ŠFªRwŽÅžÀ;¡›-ãI‹ø›Æ”µLèzÓ!mÈGk›=1ùŒ_½±¶°cW†l!éÇÑCòI,}(²ø.ƒ9Þ•¤¬˜û‡wõb7ì$
+[ÒÝ:Aax¯¼KðŸ]²™뢂®Ðš÷Nƒh…1“ûÚ‰KÓnA2lêOghRwÁn!8Ò¢~DK}aBC=µÉ´›—ÊeÁ+*Ф,Kº¶Â¤#Mâþº¢IÙsSWI”8ŸNlÓ
+,ô#*~«)¾áòÃc àž¡áÑ¥¯×‹_Jüâx\ç± v˜Ôð~UQ+Ú©’š‘3š‰œNÛê*ÂWð_YürF ùM%IOoJ*#44 4bmõ’€ÿ°@è°c­G·²>+蟋¸1¯"ÿ^Ö±  f«ãoSö
+:N\dW´ßJ°È(cÜv?8cîU¦½µ}r ÔÄsv;y‡F¨]aÆ­èP„G ºš½¤ñ8¸…¤·’R•f¨€á%ÞzL‹°GÙó:Âa†-k×}1p’I¬ÌÛ­Ðô=*Þ`ž¤‰ÙÁKHºh‡j¦~ÎòØfÀ¿Q£ÄÞÂÊ<k†¼¨ Ôä–Ò}M#aF_ãÎà›%'íc7 |º¸¥Aç"<Û’½3<›*’茮ÜýÃAhr¯­¸”x+L ’òŽë
+Ìâ°€ah>ÍhW @#ù:”hj KI:ß7–¿-M¡K­†ûò bÔEgq*A¹Ma}“
+RvdóP¤Þ„ôˆkomsÞ0Ó¯•½ãÉA E‹«xzfÍœ…Š2¶¶âNrªknÁ‡Àˆ‰¶uÝ芚˜¢Å •¹„qå7­jÒÚb!I%n%ë¥4Ü}áJ²æž‹~Ü%Ydò…øExUQdg¼”ˆ\‚³.rÁ’݆“¸I¢nO[{¨ÀùüÍJpo<™8÷ãy>ÙOÛ
+°PÏæe³¨–¿òûÝÖ¤‰™!OÍú²éÿû`éÆ,BT@{B†‡q|4ùR@ Ÿ§HM `y¦:OóÝô˜¤!•æ^¾=†½ ¨9²´ÀÛ¡Ö¢¾}òTð]†byèOYå3nJØ{b$åå»áã¹È†¨4̪„ëù~,zÆ"Æž+C¼DˉrÊ·tY 4%oÓ%@ôÈ‘ÛÓ¦;á‘—€cú2bD(ÅhGY94þ O`ŒAÅ
+3Ïùn11½ŸZl[ž0ŒÌ£7îoQ}ÛüRk@ùŽ˜£­ºäi’ŽbÊ­n»¥Á»ô‘l8›„¡žy­3 [&¢fogý¶PàÉdá’N!žÐ~âÐfþŽ5OÆäÌß*"Iž”Ù„ìßEBX‘¡"P~´vÉçÁ†=A
+˳*0±n|Z ø†áQÞuId_R•$-\À·
+2ë¦LŠÛ=AÐ^"¯‰fÇFÎQ`ù*?Ò- f.gíd%)“\ã&8å<FÇ{f¿‡%<B"59ðK.íŠ1$lE9¤”“íÿeEç`ª4Æ.bð;pev°Œ½
+Äô#j5YÉ %Úó‘T>—¨™DQª¦½ä›urØÆ´î„QÚë2ÏÙJt+ðN—GÚsLãCœsŸ‹N²$y¸½çEÆP(©rÂ
+H™m³Þ¹!øF3Ìds>~ÌZ>õL:L™ëy¬›·DPDÁKß>Ry'i‹Ñ"²kÄdì~Žʺ,=€Ùœ'ÃêýÄ%ec>­siùõ4èœnuàõuŽh|ãcÉC¯~~gj~l#ž•¤eäUêu½_Ç"ì(œC D%B”è;X£Ôr:úQN%4Ñ&Xœý¨^ ºg7öîóàGÕ
+‡œPRS:¤¦éšQ†½Í&“—0ü'gIýlGO5?™‰„¤›<|h{ÑþàÆsÜ,Ƀ3Qy *q‘SSæ d«ë…%U°n¢îpDU €b{á‚
+`ÂVŽ£½9‘åCÉâe\&¢H ÊÇu„,ú”Üç>M›N³Ê†PIqç°o¥ü È%|¶HGqID÷<¸¡º ˜oD†&:#«…¥65L~æKu—\ææ¦Ù\û¹¶úb85Ç8ý·RZEE­ רƒCÔ}†¯EWS†<H€ÔðÒs|»h¼Ywè5ЬèºÝ7ŠL¿àuQ{—5x¿)M’'Tc)¬¤1²PŒêjÉ ‹’‚ÞI%Xluœå•g@/ÆRs7ý½)+òþ-†åa^X°E€T˜Â±gÇ2‰ø€Í¬„†±êÇ
+Ÿ+|ãY¤äãf•‰€G¤ÞØ%ŸÓõ V‚™ä`AQ|p,zð´þÀÓGTad9b ú­bð>‰¢å8^¼ÝLÎâ͘v4°ò~¤¢ß[=¯c9‹?
+EëÈ2¥: ,?!&ßì¼¢`J ´àšadòfr^„²+`Yüè«J
+Œž©‡½ÿ>—Œ|†^F¢Ø€-…’d|î=Ñ·ýõã¶:K?bRþí `üîÇ}+èM¢Éºyi³yh›aÎΤ•–Òž–·¼DA0Uã
+]Ú^ÏX «u÷L¶!ßjû
+UðŸüÓ÷.³AžñSÛÁ‹G놽Ó.æò+/nࢼ¿ÂnŠo”ÑY¯NÄÆF“³RÔ }‚9?¨/‹®Ó
+¯}ËpŒ„©âëL¸¶12ÆX¸µÝÊ,•ñ¨…¶'C©=|&É„) À%Û0£Øã’
+¬ŠháDç]C‹ºb×¢’˜“µ§3äTš§jç\×¢Â*ƒÐ‰XÖ+ËSÜÁÍ׺2Þ)^ö±’sªo‡zé´«ƒ–ê¼
+ú%ä®3e3¡ õá;Ó@¿bTþjA€ £F–Ê<ÊO™h?|`^Ì~[¿š¶B^šípuVàÞòñÎ;fÒÔRl>„|ÁÕ_¢Z°Rkˆ<ßœ;ç›\7Õjô;¯©e>°67`B%°U´q\Û)â€tCc’i)|Pèê Ý Õ¡ƒŒJ÷¤­*‰üºÐ‹›,)°êœgƒ2Ô\j{Âw+*Ø€ÆAV
+¡ÇY{Ѧ?mâ4ßï¦ê|GdH &ɧ95|Æ.zIHP¥¯CM&= â»5
+9GUÀ§K|ê|úŽ‘x:ãoÍCÄTL)#UvHn­¬*‡ÕC
+®ž“ŽïA¶÷]›S’¿·,‚¶è©ÍììF½
+õ÷2+^-D™àñš ã¹H̉a”éNú#É·T,¨¿0Ï›ÁõxÌßKN3¥YA'BÖܵM²ìä¬Ý̤†a=0%g쉣O&Úð°‚êåL ©‡.£ ôó;óµ—KÎ+©h&Âê2¥Š‘tÝki ­tŒ ½ÅÈë ‚þ
+‚´Í¢½K:Y¼ô什Ç|~çFºxþù'¡H â0Ô$%;rÀô8™ÿö壠/(¥~i@#ƒ~1ä°£±~Ñr*ùÁCPUæ'èÙ:ÓU† å‘E‹àÆØ/ä½DèôcQgÉÍ(>!c<"‹öW¢ƒ„‡©åh~G–)<g±æÎ U&FpmÚîF¡¾T ´»Á¢ úõã´]UÅ~Ìf{?lÛ ƒfÂÈ:~Þo|·±ÛË#;Ü „e‡µ?}‡²ð„”\éóžE®ÃK´åaÌüÊ—‹-f\“Rî“h
+Dj¯Bc ‰´xµèîÖJ$TÒÛD[©‚Ò€SÜ}Z`M>ÌOúM«ã“w¼kð&ìÌy^7N!ÝÃ;þkЦ%ƒîÈ}Û•‡kåaÜ÷ó¢rßÜ]¸á¼­¦Qön5®j€AÂll¼¶ëæhפ‹ÓfçTdÆr)×l¨hYMtA¤„¨2E›`_`Kg'g£lé1.,öÖ Wx›{7¿½*ny¬ÎrÆ°±ùz¤ Ûx dGj9¨½ ‹ñ1‹Äü ™We•ÃªÎ¤²£PLŠÄÜùÒR +¥ÅÏ—€;ÐÇ\§Ô@\›±!€Ýx¸¼>ÍS‹ÂµØaAÈ©ÈÁÄCÄä(üNc*×[2rʨ0«œ³}aÒ<æy” —)Ç5…‚Ë.gµj.c"^Å:öw?Í$iuÅd?+BŠzúý>6µ¡
+p‰BŠf¥…È«óLÒŠQ„öt•ŽB¾pHÙùr<°ÑÐ)ô–—ýTˆÜœSáæ<Ãï)Åü(D²HV@ò‚×JsoAQ»‡ZGâÓCÇÝ‚öqHi‚;`¾+A Ô¬äÿÔbÊÆ›œíbBUt±‡¦éåâ¾ì'òþÐÓ)W‚Š8½áAžCv#FYcÈOÔK0J9Kqi—÷ó.8cŸ¤fBþ(!æ<‚¿Oò öÉŠŒÙÃw`ÃÙùŒÓoŒgòôNþûϸÝLÇ$‚>u#á-(,£?Ûæ0VÓ‡™½iø•5ñ(¤aÇܯô5æó;Sª¹Æ,iΚàÆ©h':ˆx Coóæ(¨Ñ  RÃì°g6þíÃCj«dž,ñŽÿ3^îHzA>ÁÞA6ÆF¿&!L¹œ@·2¹?_VW/šz@DBKîtwUVf–l
+E[gˆÑ
+Š
+¶ª–!£»4Q·›û6°™…óm!èÁÓ‘óüQ¦'Ñ\#ˆ¦ýiÊ¢\Ž®ĽØnrŒì*”ø
+ü½BŸFçˆy£Ów*‹É"Êàóɳ
+ `±øD°H^1èá ®†?—€Ï-Zô¸4é? ßþ´’ñ¨ÜÚ0–ê³MqaÚ-zBœ€sq¢HD“ì¹ô*\´ ·¶› +UMi× 2˜v²ªT?èš…‹Ñ¹Ùý;×ᔣ#ø8„9†ÜB5ª¯¤;[Ý&s 3UCT”Ú–Y]%ç/^”ëSà ìYd-VçÏàXÊ—-Ñ~9È[ÐÐ T
+‹ƒû18D9… 0r]øe¢´/°¾zÂ/Hv‘v¥°ìŒ¦´©ÄV÷QT'ç.Šàf« °¶ø…#öB³&›å§Çð³ÑDžéÌåÙmúˆ
+©(]¶™:AJ /G>L$ ú‚Ää˜Äz
+öÍØè]ÔTÚÿôŸd
+~ÉHè…ª²|E2£¹ ê ó²õu
+ %|#E÷A˜²¢Éã
+“íƒ^\¡A NÅ…¦î¨  ¸%j$잉X_NˆÄPUeÞË#Pnè
+—}lm‹
+ÉÓ«ÈTÂ~¢:59ÕÏÛu9¶§s^[î¼æ>:îWÝ&Ð)Ž7ë·{—Vý÷ÎÎ(o[/1Ÿìâ£×ú/„+!ð6È|J:‚L—½ÆŽRƒâæØ ¥‹¤'ìÅ®4Òïòçñýò
+èÐCÆÛOm;¯Y5b­Š~ïòÖ/öÛkûaãmÂ0$CåîÙxfT1T@i×S*þ
+æ“€4­þ°LÑú5<0娸šÊdè*™ ¼+µ„»]ì1‹ÂÔdçpœ=°¼;'M–µn¸±X<ü†©oÅÿ#VLa)¶Åæ¥ëŸÈD ·Ö°›Ú8»+óf¾ëe~’ ÖìfóÅ¡#£|›™G z5µºK<dá1íœv9ƹ˸·ÅÜË3§#ñ‰òæll$²,?ÿçù›*U3ÑjÖ1-6œ*R¡!%ïS–
+ÅSƒ×ÔàHhKSÑ.Å…†¨UÇìÙ9…h;fÝJ„ìcIÐ÷zAŽ•ƒ³i5)
+DGµ +” ƒÔìm> ýtYQJÜÅ\fƯVÅ¢Ð=Ⱥ‡Ùg6&.?ÃÐ%"ìÜÞDã X׌öÿ>š`ˆ\`œ(ù™F÷È¡X“²Ê»6†\“®xÂ˲r©j•JçLTƒè®•#ûá
+~'?,hBùØz¼*öÚK0…¥ +è ÷ iŠ•0’`5
+Tøƒsá×ebod^èž#êŸj„EØÙMgâ`yDcj«Ž ÂÑ á ¶º>lФ<»wN1µƒš£e¿K²™îT
+{Ú‚ ùôY¦?.ê Χ@xÅfÝ¿ƒŽûûKƈ(½¶ñÍ=?‚ìºÞR’5éq•ðaÚÆ€<˜tA:vyÈD´Í»ÔÌJUCí\§ÈéÛVÓ]܃”õ$3UE<`ûöÄŽI”ÃKR)·@¨ "Ë6Öj„­Ì¼·E²®‚m˜
+¿¯z=gà`˜/˜ƒáØÆÃÏñ¸ÚºAht‡³ˆu;Oœ]O|€&Õ®bS|g’ÛŸŽg@Ä”ë¤Âǵ#ü ¢¡Q1Ö r™¹æA^¾ùóë¹Mï–”2ö¦?MÆtlàç;kº¶¹™ 2è0ÍZú&Î.KÛeÒ©Gì:ⶠÀLÏ ‚«ÍÈÄÛ‚`ÔúÀ‘á¯6»L5+€‘åüÏ®R‹‹H£×½¨AÔŽôaV“SG§_Øâ\7k°…¨«Y2Hi$$u¢Ôd]ÅE4)ÊŒ‡s4)˜×:êL¯QvS©¾áñ«¹¶ìÿCm¨1
+ŒÙÁüõ1©fæ‚‘b¬ÔJ$²ã]k·_ a&5:0È¿U½)ÀžÎËü4ùÏbÞ¢‚H€„±¡Lþ®Má9A÷Ls
+C™à@Xõè=V·á„qѸæévêÇ~Ëþ†k4&E•ˆCpŽ>YdžZÁ³[ËÓqëû-\¾ƒ0QÔR–Hâ;4 t`~fÇ2u¤9+@¡Ž êJx†¼Aš.j†–7|Ѐæ@Ŷ=¤^Y±ƒÅËéõ&µ¬u½ƒÔúŠÏ£PPCõé‚î“È‹1_ØÄA½úÊ_™³C}Ö—³–òP±êHåZ‹ûDߥ’6 ¤J•y“ø]âœÎŸR©!€lC÷Ïéˆ;m‘X¤º!ŒNʲ£qfƒ¬àê„È„]™0£ «™íªÎ®³Ð<KV†@ûÈNµ_ËEŒd%äLÖU=âxö/iépŸ6ey2CG²"çv[v5ÞÑ{ì…üè0 }€4aìGRØŽÕ±• ­Éc‡ËJ²f4”UËsñå'BF˜;È`À BúØsÇŽ¤„í²ýQŽ
+õD"¬ë1Á–3ˆX÷Òðû“ÕÆkÖi¢æ¡Û=²£ANäY[iç0Á¦;߉ˆÓQèo!¨'sŽ¥K›+¢ù;þ̱{,‚ÖŠª³2ÄDfe¨Š‡³ž3|´““™]š
+×zÔCÂ<hk +÷ÍŸÁö3Uÿg¼Ì‘#»±(ºî¶ æÁT°Ír{Œö’fï_ç>
+%bL sãFÌxehÅ¥MÇ~­ñK¨B€öéNÕ î|d$&Áœ$ÉH¢ÚP "w‚¬ÃÍa»óÎÁ|(~¶$pyÄ£(!ÿÔ1^]¶ïöÊŠj“£âYÇü8¯ÅÏšcX¿XÎS&Q*„ôJnÄ
+…ˆ°k‹_[4¦¿EÀk›È™+tMA#£I! ¤#çë^fSmÌÚ9NÁ2£µx&²8 :Î ߌö.þ—>ÎHËÝsH0òÐvıÞåȹLФ£¥²´)/*B£$ÖÚïh úTZ“¹=a)
+7ÜÓº’¦”¹T÷2kö(Þƒåçåyï3Ë/2ó’Ç~yR2›"“´713õI“VäF© †€%TØØK!µÊèÓŽ¶Šœ¢ÔÞô«øòŠÆNtåÙ1÷w/s?9î£'Ð)Ž7ì%ö w­úzei´_aïÝLÚö.9ç}» ñ1ʦÐf.HN¼^|žË!+Ïü!|jÈr |ÅÑÜ}uÌLWºëÑ‘\µƒ
+ÕàûrÂHF8–Ø`öéHáÜædËngA®{qÓn<6š¾?õ‡å\Ž7ˈŸZ…'¡ø66ôWJrÕD›•hV?q²È“¤R2H×Ò(%œ˜Ç OZ4ÈqëÚÄNòZš=bø%¸¯@‡²c@d@Üë™BæåRë6úc‘c+qËq)èª8'¥5ò売[( LW†º-1#JL‰»c#AVÄÌQ§¼`*Gdsõì>„ Qa•Ûv•™’©oU­ízú ò6àÒGbkFS[c¶w1í*!•˜Þ<û ½Œ|¢‘FèÊOÎQ>Ój%ùÞ½L7f4iYºAì5(4[°·ËÌ,‡ ±¬EŽl»q296cN5†OO™^
+'؃*iÜ#ŸAÀ¥UŽö
+±‘§È(8ä’¬Ÿæwš»¹˜ÁÀAžõŠï× ç2(²‡óðKžZ5¾/ UÑÖN<ñ~mƒ#$ít‰åÃgTÈ&§áëš–.˜|»§¼oƒÐÀNˆæžQl â©îÊø¹0§-oËè'¡P…ز–. ³b£ÁÆbÇûuñ.ðóíºÂ^J(“P»ÞÙw#ÈC‚4ë¸"׉0wöêÊK^L´³ãòŒ£m™Eõè?¤>vNaô“²LäúÁk7_™”ÝzŸ¸ËvŠüpƒCÚÃ~€83š ã„ÝçßkFkï³1e掩‚ºÀ¡›sJÄŽ ÉÐоOë,Nô~ñ@C2JàËíø7¦„ÑlDr¨ÂâY™íP­ŸYÆô‹O ú›²06•Å¦Ѿ¥Wlœ-o®`’ðy´;ç¹^Ž£‡lùQP}©ðGôá sžùHç図£œ£ºÖÃïÛàŠÞÄóýë¿Øå¿ÿ÷ö×ÿß°nx\¾a_ÏŽt¿1§øˬBpÞ<ÌqqI‰,žÂZÏ ÝðAUyê@¡A§q!%\{Psa`ü!#Û*/Çm³I¦û]ض@”ÉÔ"Û1²—ÜY±(d¨<4ºƒøzãm.¢¤’žÓÔjÊbn"“ eg –ѹÐ؇¼!ð);ߥÜÔÜ17‘àX¤^XÌ_Âj“&Aš×7Pb’°ƒAî¶.™òyÿžô;ÈÞÎòP?Ò
+Ø ¿Æ/ÄÅ 3/‚¦³»4¡ÉEþºÕ~#&NmhÜ_’-VäQ€±>ÀšP†Üò†+©K „;ðÎXåÛ„bNq™¾få¤æ^­¯Ge
+†é¤n»<AÉ«!F}:`8•’ÎÕÝ^zYQÖì£ãoyo8±Šâ'!cWàZí ".,[™j’‰&E¯ö=‚.Ì`¤çSjC “¶ 7¶¹qpl0ÿUŠ!¨h•Rü¦Q¸°HD‰kµ¯ óH¤ÑŠ8­D¤Ò ¸ÉÜóþAn±I3Æ*ÃÜŸu„;-*—miˆU¶.Y¬B¼Ÿo‰J>Þ„™Ž°(“Öß­†¤?Ê(²-Ž¥°VˆV™jô·&µ¥\&z ë;’É:Àdá@`"[b â‚°Ü°nfã#²V2£ùÂøhÊâû€Bp¼@ê5m§8Šþ†‚`[lÒAæJyN—5FY¶^kE©çŽP2²BŒÅ-¤*‚ñ²‰¿t º:­”÷¦âÙ=§|ˆ*0ågwèáv
+-ºsÞ· âæ0ñ‡ Õîä+C“ÉÞE*ÒG¨•MèÁ2¡Œyz´®Þ010 ‰dºD1 ÌÇL=øÔqí¼CAŒ*4$¤÷r îÜ—Q¼Ë”I#2
+e»´úe,ŽmAg‰B? ‡ÚT 6$ÓêWø
+yèÔ盺ëü͉Š0\VÍï×›W“#bÅ”—›ÉEë h€Ý5¾/ @\âHHy¿vÁ<Gk@ ½Nˆñ¸òâ-—s£•¡œÌGËQÜûË0¡jŠ‹›õÉ›’¹-†s pÑ­Í㾬Ó(h?؈5*Õ9ÙôÁ?ö©?.çrHK†/—pÅaX‚,!Ì "súí‚—@A0€Ö¬mÉ^=Pæ/÷s/€¬N.fZK·iõ@ð¶·#n5¿‚ñ\}žô×+"D+[4ˆüÆ$âö)øwÉiƒ´v¹#ž­)õô¼‘‘AñÈnq“ôh¥ædCÇX±¬ë2Ðã*
+Hd}Š‰Ç…p©žSysÜ£þÈ ƒÅ -ò¨”œ ¾y þ†MÐâ¢|q¹ÈŠ“ÕäZöú8˜k½4÷é— £¤&XO65¬úÌ’‡2…wŽ
+„€ÚLN!µñ(¢UO#«ÉÄêøžÏ·+È›ÁÂFÕik|ãlÅõc/6Õø ȶ¾ Eäxt8úβõ5Š#"mô-$â$! ®OÇ­öP‚À21!bTø^±rApfdòd&=)¡œœ4Ðí ¨SÅK”¹šèÚ£È2sL&[o÷R/
+©µçƒÊpÚ˜µQXŒfßSÖX‚ÉÓpfe½Jw6âpQ5Vˆ¬yÎh@2+D6¶9øç¬2./‘Ñúœû6Q쪲'º0lqÀ­[{& ’¡ËûTû`'¹¥ä¢ƒ!˜¦ŠëhÉÎIYÈ?Œ—¹y^9 E+Pª@I€[¬ ÝŤrÿéœË%°þ‘3K†I
+‹†‚$:ôº1ÄaçŽÛ÷]2xE›˜ñE8
+= ƒéù˜ƒ&É€”‹¾[ŒÈ E„Ú|GN2%8ÅQר  3į´€–×4“f Hù8Þ¹f‡•exÌùâ;@­,ì¹å·q¼R‹k#~û;±×,Žñ(ÒÁ®ïýãA³HÄÀÉBꨪãuÊ,§¤ÐÉ ÌãøÜ@²Ê\~¾ÅE’}ÜãäKKEEâÜe †: ՔƲÍC&ø:š0H»cßØLÊæ¯iÒV;8¨¨Ú}89"L;Ò‘Dwp¼k‰Õ>Nžbô+ÀEV†–æ}ýr®ÊÞ»€mÑù„ïI²¾¸h Ƶ;ŠP[²‚j†
+96Ÿ>‹„?bT 9>•ÔÆ¥«Vü9tŠ²à:/ðçôöâ’ÉfŒ?7Œ³š4G‡'ÿ&!‚Ãð#ÝA€8
+ÀPu‚ ¿S?²ËÔ»–ã
+ˆOU=L¶K–}Ã 1«û(„G°'mÛÉ
+‚1TÎõë
+–°ó<+¯ït${ËQý”ð`®øe§dr3hºv?„Ãj×É…Ä
+H"íD’ÁÃ{ÁNmQ-$€æ&Ñ
+‹¾S*ObÁ[;®·"s’í=æÑâ,x½bí šßžxÖ´P”+˜%ƒ®ºüëå”BIAnhÉáoÅÙ"ò2Û#Mp.™„0C€àªèŽŒ…p%g곑àÛ”<üNÝ 'ç¹ ÆÒ6˜að#JºK0uŒHZçã<Ü%‘ ÈõŽ©Íp+sB„äºËáÒÄÕå*°5e9üô‰",HΤ¿ë0ä„›7±Ø!6±)äh«ö¢2oèÂ
+Ãì¬m¬ç¨Vç—6Ì;Ùî&¡/kìäNøá]u9¹°ˆ> !‡‹!¢&ûREÕ·C#á’?¦ÒJ–C3…CÇô›è¨ïUaÿxE^rMXšY6¬Ù9IÌÈÚ²¨z=%$@›aç|*ä] K¹u7–û` †=+tŽBï¡ióåWp@\îžÝ›Ìeßq1:ìŃ“Ïè0ãÈac(Ø8­éaw£’?¦tŽzA½lÇŒ÷Ÿ·Qþã&Q™Ñ£ú™¢HØ°ƒ÷¤ï½éÂϨN÷´ÆAƒÉ](jÍäpLVìuø6ëïyîËã&Ͻ ^¬ï·¾DøÖݱ´H]C@8KCŽáüF¿Ï… Õ±LÀˆ9òQàã¸GÑ×ÿÞé?
+H‰Œ—MŽ;„Oà;ô&!R)®ý–s ³jß;%¥ºR7 îê(ý1ì}Ôÿýñ¿òñÏVûeZÌÚ(%L>þÓ"®Ñ¤´hÞEåã÷ÔK¹´6uM›ö éC½zé2|Az‰êcô!â5Ä{ˆÆ°¤„5­böñkn4Ôl°o1תZ#Ú_«„·jÅ{ײÏ2"*_²Þ¬±K<ZÍ›È8@r+»êè ƨìùf^{„™zŒ{â2´T9!V»—ªÃ‡Ô}+‰p×.&íãç'¨Å¸'ó+|þxn•ãÂ-/i=>žNH§
+Ó¹z1xù^¥Û-Ü\²ñ~ÍÖVÏŽAÔ}ˇսÕ_Úê_Ï)—”1à!ÝÓõ缂¦ç8áYü²zCˆú¾‡îÎëË$4ßèÝ´Sä:nÏixÒàò<ï<x¹*á"‘â¹U^ÐPN®xõÑSw±³£W”ËSÑy8zéd'k+¨ÉcSE:ú¸Ž\>W¤¶ŒÅú‡
+<?@ñî¢~qÒìÂWÐÔg[GçÙ ­¥_F™÷š® £5S ›÷
+
+ÃÀéFÈb¤Ó%<ÍÞèõa<ƒÃbØ1îùºÙÒT×Go5úŠX\x)ôë"O²<Nò$ÝáF'ò¾<Ì©^ž÷¯­ôsvUÈr`Pqhðßû<’éýíã
+¼‚NÓÆËfG¡øËy–Læ¤Á”À —ñÓ.¤½Z½Îc“ä`W%TĪfaØòy æy3!óDÏ>CG›|”¯=LjZf;mö->;MÃ3eÝÆϬ™1rù^Áá Ü‹º!îJZšæø¦s²¢ó™Túx3j†ÖNë—Huwe4/óÍZIÓF 4DT}CÍ«e”#÷èó;…õçµ »¤ úÜ»N3L:§²™Ä
+—`àà@b{À4s-' à $Ùo ’9˜—#×LˆÐc*ú|âa+q\ÓužN73#橶`1'(C·˜^Š§"¹—;`£7\Ó§Ü7g^¬5ÇÓ#”AÊ·6ס;€xšðÎÆÈ ùÖ&÷Oü
+j5fàPß ¦ž²‚
+fßCöϹ)ÉÅÿ/_ØàJKOÙÜ{ÃíæIb¤°õ¶!¾fÆk¾—ôz!Oeúõ&V¿ëwŒ FÞ Yó>Ü ¾9H©41ÐO…`RHþ(Î.j™•B<ìÓÉ4Ãp¦èéÏúô\Bœæ.ÇËžõ‡4"MI
+‰—¹ºñZvG}hÓ‚ Ë.zŽî)bò>…’å“qž?…5•!‚
+Îtó"¶<Ô{p¹‰nf 9frÞ`G7¿×—:ýõÃzªûà 2R√^¿X€-Ý ´²u:/—u%4ÍK<kî—?'PA|.¢3(àáç*(Bèç°®2IW•| ðË9…¶J~©S_é}
+Ý á ²ÌLº{QÖT³ù¢I(“›™×YYé¥LYܺbeâÓ¦¶þÚW}1˜‹`? «›áò亡I ¹Ûø&RXØͪüèÖc^A 2\{”\|´Ôcj7ÄBf1ÛŠF¸cò6jh{m‡}!¾P“‘vî &J4‹R -$—¿Y„úM jœA4–í–þã8HA4 ì0n‚ø$éê(¤’h'á+ŸE‰7@NšrŽ’œr4?ú²¼j‚?þFËÿüçÇÿþ(“ÎG^9¥
+a—”ÐúÌ_¥° bšð²DNê
+MšZÄ°¢©"âóPëêëD£K²X5’…Ìk‚ºsqB~ÙU°wóÐüs@ ›®ü|Ÿƒò ò€ï""Œ=håÚT.]Ã)€u˜×¯¦ªæ”ص»þ½yÕÓmeË4Bs·Û÷ë'™=`}h±á¨*<% ¨OËõìÌ#a€gyX¢ùsdh
+z³çUªE£êØã…¬Z°EŠ6¸;zXnÁ¶‰Œ"º5yÄítØ•¸¿nÃOâº@wÖ
+è¶b&’ÊS‹u§%+#•®m”² ²±ìG[ÿdÙCXM–Ý·™ÑÓä®äÍeùû‚­µ*D‡ªV~éÍ$…­ídÈ?°&©_ºÖ¹O'‡æ’—È„/¼èˆP|òRSh- ªŒ{®€ÃÒ
+?ÈF„'ÑH;:¨ÝmC û
+ÐCe˜oø
+tÀÔñ£](Ò"d­-+D™å¯u·'çÁH¸$w«k^c±4 Íx¸1‚Y‰å~Lv…‚Á4ñsku‘iò&ú©­9fX_r¹l ¢*`W/ûsœ ÿÛêã;Þû›K 
+ á6­­MïeÌ?,ˆ!¦…aLÊéjA¶ÄU!ÈûìW*Y!]cTúy˜{U ‘
+›éE¦¯¡]eì«`[`€§pvBÍÕ¤±[!ü7ßÖ*+ëŠ`¬ØÈPž±}†DrFÔ6š,“õMíÂU]“è½ßIâצ6²tÄÞG®SI­6âÿ0Bƒ¸ò6µ \ ‡ @ÂNÚšEð`Þ„Œ™výe…À~E
+Ãâ7èµùb+*hÝÚ)+d¸Œ0®—Ç&©§h¡:Z¶æâµ¥‘dT‡ôI–ך†Â¾cTW}¯…wTœcÁ¹¸­ønPA4ÖÛ.~ÍåøôÍTàÙÙBPº ŸAö²Ô$"h¥²
+²…ôê¹÷‡¥iK‘- ®Å_ƒÀ¬· ÂÍð#ÌñîÜöóøΉ'øóXZÑi âÜÌhÄ¥A×."„"É CÎÇPžqŠĺH¿Ø§HÂyìÀhm³ H¦ ]u5ÉF·*Û£'&!DãÄšŸŒ"ÁÖÑÒ±¾f0Y¹EËžeRƒÑmié ?b¶Y´ì:1ÃBHs°^”º?‡T±ú0 ¦šËãÛrfÈÉ ¬• "d}‡Î©»„ûå#bIø¦&aÕ«¨~œ›d·Ê1xBÑ–âu«[ͧ]¤= øÐ`ÐÔî9
+›2V× ÊûùxƇ=
+ëê
+‹Av¨±dô <¿VÙNăvŽ’‹P°+ä]*
+›ÂÉñs—Ûf~Õ†y-ÃÕ³/åô¼_]‘ƒ:ŸSÒ¼9‡®¢GQÙÖ6f¨ÓðkÞ°;.™–m§@ž Ð
+e&°×æ¾
+ëË$U·?¹u^AŠi¥²„
+X'M*ÈZPÒóÿ²­d¾0½œÉfDÑõ±Bffhæ¾¹åÑ€½´!”¼‘NYƒ¬H6ɉŽQJèe.õ#
+ˆ@*‹FÐD§ª{ó™DÁTª¼ð²qÞðë`ðnï†AJ»H7©Cž^ä%ø±žGAÇV‘—/ž Zůï~Aœw?–/<æ± ö-Íg|ó4Óø‹•™´GtÖøy(Œ«Øª#Heç¹EÕºêñ¦¤È}¸WýóËT&yºI>©¤„äÞic8Ɔ\>Bô‚’*°ç³ìþ­E»ÌKñ~ÜŠL„‰‘cä[û¥UžElàucø—c3I/læã
+òœ]F"™Pl*ˆä@u†BªŒHgPz®4û´ÏÆ„Ûñ+<‡x3O¾mš¡=ˆ¦ùÁfÄ#ÿ&¹ a`¤!ãk˜(™pWCpØÏ•?‰/ú"V–ÜÁIõNÖel Hw ³bßÚÊV÷:˜á)LÇ™|1›H¢X¡G}îEèué)#‡Œ#)Šo6{Î*ȵ_¯›™U| ];–R<Œ|¡?j£©—
+·ø„‰T*îCúãÂC$e¡o{Ôó¸›×Ũ\C&^›¡üõ•Ý²l76•Ç,!N¹ÜAœ*“¹
+eý8 %­V'Šï Æ÷vQÓÄåªaC“ä€!!¶“]8šDA…ÍÙ#”6úô& Þ…¡QÕÓ¨[ŠÁ2–ž£œÁ6‘â.Áf¤ÈÇ0qs8Ýq¶ w‘èhwž0á4K]‰<þù« Ð¬¾‚ iÊ…¥œ¨ã°úðâ‹ao–ó8´O5°ö8uƒh\pG,q¾lølV1Øzi“Ê%²éA+^Ô‰D€å›¢L|x¬rê:ˆÈ´”r$…‚ ˜r‚,ñÑ!Sg˜«‚Å‚þp~ë·¢'%S0ùsUß3Ëåç°8sËX0‡vN…œ¨ë[÷ Ì>L»ô}6íì-/'‹´«•[t]àXÿÇ·WPÔõÁѬ/5§€¥}ô²ï_YÓŸ×GŽ!«Ä¼(®Ü¡ÅfÃdÜM´P°¬ ƒW@‚tîSLªüÇ!”nXŽ”ðó¤Ã×Ý2 6o<~
+_ûfP HÒ+ÍÞÖŽJSpKdMÁü+"°uyÄîEí˜xÜ>µÍá9ý\…Õ,¿!J„DùØeÂ2щq:„™ 
+MŠ^ýª$=•x{²|òmm4¢m—sç“™Ö„WOÆn}Q8 ë»*©ãb÷iy…¯
+yt—ö]ü425åõçáQåXÓ±¾ÛÈ–ú 6¸"ß
+¹8ü¿÷Ef…ɸðÃrF3Œs‚纫7J"%`}¦™
+ŒÓqD©*HŸUñí¼UÆáÅ-- DÌ…¥éÜÔ© uÊLÉ8&cS[Ø…7·zÈq¢ ®È7”ÊŒ°f~æ®dH¶úUøó-ê­ªòQÏL¡]0#rðâG•§'[æ0 h€ e¦q½G m
+v^Á6£NþÅô}e¥}Hˆp)RrÆ0ªX«î41ß7ˆ'³Ç‚HÄ«²U>åxŽ@a®‘Ù#Ëa~îÔ@ |3>œ%1Ç%™Z¸¸ûþ?cPF¿z´rt
+×E7åt¯"6é¦HžD3…L@sÙ5hȉ‘ù“ëªÐß•õý+ò{Òf¶ÈÆU¦)páô.EJäô5- 'ç6ˆóà.4AIòÝ@gRb¨²Êè.“ó-2p™˜b橽=ߤÝ]™NÊO„ÊÓŒÔrG`WšYþñ-Ñ*Înk‚þ­@Ðk/ƒÔf¦0nµj@Oj Ω$™²ÐfƒlM5¶Þ 51 R5SG«ÒM)š´_vun’Â}Žá`&ÕÄ×”lÇ$˜¹”(”Ñ,@V,UòþsOeÒþøŸöŸÿ~ûãß(=ÖfÄQÎDðE’/³Ãh'ÌhæuˆsvÞ$Ç_ã(CHD
+ybbV½jx¼ù¤Ð·] ·"¯Q¯î¢ŸØG®†Ñífß,<Z{!ÄDÞEý&É\˜í¯_g;aƒZ`«zx΄cðÙ,fO·Y+û«ïá°‹O L@[Ö«'LØr«UÆtÔ¯a¨¬×ÉüŒvïòþ…õ•#|oVbìZ)õÚx9hé?[b)UÃéC$Ü4o-™£IPiC,ŠË“\/µšq+(‡Y¯
+öŸ‡I)p‡ÎX¸½n|Œµ”!hù@•¨Ä–evâ@»1ãr>ÓÖyMi'|
+ï Õ¶Fé‡mGBó´ op@ÍO<a/ËÖáw6¶™Îíèc›,Ø{;d–&ùoÆ·IÌ<ljš5Ù+Èx… @Z»˜ßÓ@Ã2ª0B
+W'"LŒªrÁáUþúY–LývŒÖiòn‘D5
+Ô"©£ýÍ5F¬:¼2¼;D7¿Öù¤]ÖPt †{¯~qЋzŽ)eð´<ö¹:ZW”l4U¶¡5t­ë͸2ÊÏqÖBÉ ?ˆË0«ƒH“V$­ÎjëLŠ^rãݯ샬Àé½Ý›â’0W»ÌŠQY§Ûó:僧«2 ´wY'L~$²• ã)Ö%Ìé4 ÉQÍJR³@ž8ãè{ÂWƒ¥üðW˜'…Ê{»xŽ59ãs9aÚžßáÁ§Sêg¶t?=D:œ(å«‘³šG­Üf” ï$BÉ"s"Õ«“:]ß¹B þãW5Ê—ad\$¿Ö!bP‚*ë3 Õª ƒw°&˜ðlD-j
+×ÖXLë=©äFêŠ ®] ‹ÚᬪkFëPeæ„wG¾Òù݆db& †àÀÎd¯ ¬Ö+j ²w>L¦Ú°|þåi&• g!>‰®þâ!¬ÆI4Ç-jlEŒ@®M
+A à(€­Ã$𱽬`Òcv
+·)§³»ÑÊë¸Ü@ Ž_hµb­ªW°ƒéDÀŽÓrHÝ„¤ æêÓ
++¹\a Àºä œ|îà–H]Zvì7
+‚O¥<3?@&£‰ò&j6¯­n OEöÓ`áÑü9¯‹Ó$0_G°Võ[µÍmªå|8ñ ô™‡
+\”O„¡¹ë8 š‘E¥KÛ‹°åj:¡,C$œˆÂ¶é¯ÉKmÙÎ]ã8à2LpÊW—Š…ÔýŸufâÄæeÎGR ç4Êd\ŠŸÁJ“w튴Ùo™C2T‚ARÒ8NóCÄ-lÜzÐëFÆòS[Ìõ
+¶{}|åLŠ—éý_ÿ}ûëÜs§a7Cl (ûkÁÛè;ŠÿÿóF)H„U—Çþ4­šÑ¶é³=‹Æè3I,HD9ƒ`ÍÐÃMïm‡Ì¤ÔXŒÃ Rhž„"Èp9d© ÉnZY-¨±xW–.Ú&æ'&Ó×!=`8ñxˤ,£"`4W>L“‰F3°LvˆäG­Âb˶šHâb'é¿y
+@(Ÿmô¦.[ûÀÙ¶`Ë!µò
+íE•©hZP^µ•
+
+ì›±§Ú…JûŸúDf²¬zÐÿ€aKñHÞl¢á_ûº|7^ç²eÓ¤Ÿ×êI0ù=
+3DSùÈ ª
+#ÓÅrnĺ~%O0˜·hC‚1*3ÿÏ+¤`(Gӌùê 1S¯ =çêç0Z]ÍJN}b¦yðnŠ›àD¢Àæ}%ÑwWBÜ7aïe
+©h]Bª0€l”¼…•8“C
+0Qm+ÎÁÇR¤WHV§%Y›ñ,$,‚ææ>ŸÒièw …Ûa¢'ëX{±>qdñF ³ MmYÑé„ŽîRVzS§ø¢yDå¯\]Í܈ù‰«ªêõ6¿èŽ·`$h BïWUfȘx·câ7¢Ê†#®û‚/`3ð!yÀùÿr²Ew©±>õŠß…õ×TMø"òyÅȦcñ*ˆ©Z
+7ÖDýy÷·Xa
+t}ökž¢\È--— wWìè»ä;? IR|,æÄÞ0/+ù ›QX&ä ûÛÛ;¨ÉFáùÔ†¼|>ør*Œ$Vð™rk+‰—ž ü<K|× ×,»‘œ.gR‚4V5B
+ž¼«Þ@¬6¾_™Ö2Ù "Û7ÑëeÚ{E¬‚„Ð×¹§ICùøîn–܉žÃWüÉÎ~
+ΚlÀêæ!?9ôí ‚®TšÅüœ©€[á&Èq½C>²”opäýá& ЗYÆöú^ ¨ÜD«!æ}ýê+ä[énˆ—úß o}¼‚Jë¼'kÃãœ$Á.ôXÞ-“ˆ¦SÚåþZÌgq×b­äÂ_^ ЊL¼¯gL×ÚJóHD,±‚-Äj¦‰™\i¿c¼‡Z$ ‘#XŠ¤Ñ îrÞd5azþçV¥ÅÍÐTË…”ráU™ýæÖB¤ ‰òø¾éfåû<™äP  Gsh”^…W†LÞe)$¦¨CžaŠ^ ~“ñ<ïs\ºPP^
+yôxnSZ‘
+5ã—Lµ ¡#‰çr „,gìß”a·Á ÀªãÿÉeæ–ÌŒSFApÿ´bÄ9–ã°Á K1i¯Nò ¸ä5ÇUÕDrˆÏê:Nå“p¨~NYS&n $¬T‹eŒ¹CºþzÓÈ„ˆs.S›‚ôÅUxk(ß‘KX2ZU<2³ %’Å9ÆÔPx™
+¥<A~3_ÿuƒ¼hÊÝç½ ¾2OmGƃ©9-’!n
+‡¡1€xþ\ˆAòoJfIP*ýa¡aÀë¤u ,‡ð`wªècEЦ%Þ-¼“'³y!’(Î.— …4œä“l€JšnÆ.«TD` f%ÅǘÓPÒÕv ˜Ál"” bh É{š’Y^°;Úí®ª[ø+(ì&D^AÚÕ‡'dÉ 3ß3ÎMIôJÈ >­ßx­ŒýÞvNÂ~ÕqEè!KÎb]…ð±Ã`ÑzÁ
+lþx i2¤ÄÕ¾¤¤µíødv¹ñ°Óaš»:§8ÅCVÁ›)% CÌ6¸Á¯ôi€ÅâóØŒl©¢ýl^ná*¤½Õ#†šuâôC•„w©чxˆsÄjÑËœª a€ú‰u ¼8V>´…cEÙ§šÔ»¹=L"½—º/;Üä ´Všý&Èiª±#wš2Î0
+m<"ÉþIVõ*¤Õ”€9©ØSÑrOøÖÄH
+z
+•ä0.d²Î<ÌÉ]×DôGf‡,á­DÛàÓ|rŽ—Ûj yìAPÏÏïž-ÁKÎKBO-ßz"“a HƒË¼ûªÆ ó÷áÉ#D.å°Öz¹éM:d.Ôy]Bîô8¸à »ïåÉ'ËÑY–ô: ±«(¶¬4òG™ K„v’¸Š>#®£§CÑû‰*
+nF…˜°Òkdáµmýà·H×ÔùœÙ®g4!…w÷:udÆ(=T”ü;„ìuI]þØ£üš"g?œóä-ŒŠ¹$ÿ® °,'@÷nx[’º ~b£K+EãªÎ”v»¦ñË
+ÓøgЛØý´ ^P«ÔPÞšW±beU“øÛG¿
+¯RêÐ}
+rHç?ÈÅæYÜ×ç?”B5 „ˆ®ø‘qwºšŠOµÛ]|³ÞÀŒ!áèå?¨ù¨xç
+ã+’¦hKˆ_”ÔÄ(býD4)s óúD ksVY^†|gç@=Î
+QC ù¦qPˆö@ÖÉg„
+5)¶,‰!ì›'}³”@¤°äT‚ɱãß ~äÁ‹ Ï«í‚î'tVýä»PRRæÜŒái_¼÷@Ӏ؂"Ë9à¡›×ÂÁ˜Á&™m×êéd±šj×Ú Âᯠ1š—8d² ÀD/B ÕÔ–mžv#“ ‘`6pã V•å›k}=*Ió­øÕ…)x=SL%Y•È›t=<)1:")Â5±G|Äj!P@¸ ¼Ê—•M!í°€Ñ³JàÂ!岩5ÇÄkÙ0r­ìg †‘à¯j»yCAD¼í1QÎÞ®³®(a*·G1°;CÏ:~æH,ãWA3”ùÊ:ëýà 5zÌ?cÂÎ]m– h66RýÛ¬Á`~)¾(
+¨P1d™o=:[@æ~Ôu|®\íÝßiÒõÁíÖ!ë‰:­¥çr]¸ˆE;äŠhùˆ§ïðK
+ë¬öâ6[¡Ø)øËW1½[¡%œ×Ú°³\:´|¯ï€¨wXåÕï]Âïx.`Þ{ ëm«L;s:[À”f]O"»!xðQ*ŽÁƒÜ†|@®%ŽÂPÞ#<k
+–¿èR†PçÀ4¥XÆ ¶uuÔTXZ5ëÎÂ*÷Àöh&†«Èâ-NÚÞ™”Ò¦Åwp„¼¢iþœbù›
+å m§ã0”!+©;`ªûü„! @EÅ–\$›áÀ$kò{%Ft.d#ÁŽÙ®Á†9¯‚Ëæºj¨*¦*CÛ3Õ\tä×}RƵ!¢ ñŒBù$fcmKê0Hœ·ó¥¦^ÒÞéá'k<au P@ªt¦3)ÆŸ€:”8ª‹>M‘·ë]b¨tv^U
+q‚V–òO=\8@4k×µ{€øQUÔ½Ôóµ^|'ó¸ºR’ëhu· c½îåC I‡ˆÐÚŠÚ˜<-Sy>Ò®Óôl Æ
+ü…Öy±ÉP™Òzܲ'*læ<Ñdé`yÊ~¼ ŒÛ5²@‚ph]µh˜Â»`!a47wù]Bƒ,¡®²2˜ÌzH_O'®àåÎä0š¬ò’õ£$–IÄ0ZÔx(¼@SLK¿ÒuÝö†Î`“Oúdñd®F£¿Çñ¬DˆÔë¨)UJ2¸ŠHcŒŒÀfŽÍI7HÑj˜ƒíÿñ ìJ½ðÞH¯
+à†DW}ûH³ÌB¬#ß07̃Døaè›´E/AÔ f¥¸8o%eQv (Y
+4—$\b8×ÿ
+cØ­!Þý|BD“ò!2¿½€ˆ´T
+7¡ ^6ã$¶'iÑ¥ˆž”xu=“س?ƒ1/I;†‘HüŠ’5Àt’rr½@~ß“!—Ì®Æ $¶ÞŠ ‚bo@ ß…*)= [uä„4T/÷õ“šZ‘?g#eq×óß|H§’–x-²TdŠ¢8+"È5±2öU;8]Cד8á"C*‘h–²ÔçuÎh3L% `‹0Yv£¸®.³@ %I6PÍÝ/Ë{°×nˆ€@W%ƒ6Çy6zEc˜ÚùÎBûÓ·Fõ³ó‡4¶•µ¸í¶!Ð=w)Ð_¼‰"ñÿ.sØâ$„ƒL)vn$Ǥ
+œ¯©ÀdTPˆº(öyn£Z*6øËNb’Tó àÇ<®ÓÓ7Xjv¤le{ñî€éÂ(¯Eý±x8"\œ“ïHØmÙé5MY²ñS òâ*BŠÇ™*QÚ¶)Õ!\”?ëòÀñ–ªhÿŠ;¦›:
+L"ÀÔw½¡ìïùP´)k«Íïö§puË!Ì)gSÂ|¶ß6÷0BfqeG†c»îîÇf1#y…œw
+ÚƧ9[³!DÜAmhäi*Oæ²ùÈJB#†.ƒ’ϳ×t>MhÛŸ‘÷”»á‚Ý!(½Ò>'Û†ìðÄÿ³|æ3ä/ºë$In\ è t@FÀµú,¥ÿm?'€êR&Ód2U¥"1…‡}«Â à”%K}X'¹÷J­s«;ài
+ýצvýâ_q›Ç;ÖYâ'µò\¶JQ©Ñ>‹•ñÆ$ ©e.Ï{T3*k£G"UJNö%yO_Æyý–?æûÓ`‘$Ì.$|–,K”îeŽ¼&/¿
+¾} ÒÆ"vÀ'€ç… nPé¼#os—¿J¤U5FÆžï‚3þýÖþzÀÛLxZïÿôpdÉËrRpøµQFöÈX.ÇS̸Ñp€ªO²æƒÅ}ÞKI^œ_/Юׇu6C
+1ÐyÍL§IëÖ±¹¹²1à ʈ<r€œäË>·Ú›¹ç²ç,Ê5©'wÔ÷e´#ì-65:ŸBg’߸ڹÑ+Ïlq{Ëu“Uí½’Õ‹®?pÞö+¤(Üä“ó^©3˜"˦Q{ùùLÀöÀ5
+i|(ùÎã¿1þ2—zøÜ­ÏËÁ>Ë Suš—Nù~âæj{(º'œ|¹ ¸v³«ÄÏÀ1lËìÛê‹{7>*Ö5yvÏ9v>®.œ«Œ¢#=n™°x/cP‹Ûq”Ħ0“ZÛY+—ò¬çV÷Q²3º®¿²­“Lâ»í³×ᇮ+†,lÉ>-u-á— z ”ÒN]͘\î|?âI{ QnëX·~«„vw©wŽêE%‘ˆQí^G†oaløØõ––þ¾• ;c1JÜ’¼xJ ”Fý„²ÉKãËÿéEšíÈ$§ CäѨ“CIï§w0èõœ¹ÄƒoIYºÕy‡5›RݹÇÑ“.%]_pNM$E¦eÜÜáü…œ>èw6T ÷ã—¶È-%}¹!JWMRüZ}¯¤Þi•›jnBM’å£s
+…Ì3Ò5N‹£pÌrćvÆÑJ;¡éõšïb~q¿¸Ö'X«ÓþmhXbý$˜=MÞGâ’6¸ƒsPPÃ{„s L¶’¶e!&ëX×'ÆÙï‘óÖð{–É8Å¢öÎÓô"Kê3\×~¥«æù‘[+c'GÏì²ôûñ¡Å#ãýGmï%ŒOøúó"/ùó©ä->­9q-×qÍuš4ºE F(ôÚ”7ÝnÇ#‰*)غ$=^Ó:¾µ{´€ÕÀ}Xç»ýþPñ]?^ê¥èÏ£evf] Q;úÀÅ!­  Ç9@Á„uy“è<ZÙ=Óä}è·‘3lÛæ+^d¤*ê"wlŸÔg»„ì÷ŠåÎQ²#8.'„Ùyæb€4ÚþP2lÒßÊñ°ŒƒšÛl׸”^‡˜/fª3š9½ÎÝBÃ¥ºT …¹×±‘éaÑe™Iñ¸`r´¾ÓÂ/€5ìã0K˜ ¿Ã_yÔ˜ œ†{ÅsëÔï²ÆƒF׆M‚™ól·åL Ú¸’â
+Œß#%½¿^ŠáÄç,2ðŒÒF`Ëܳ„Qz ô$ƒt–'uV` Ñ*“²LÖ¿ÝQS @ÄH p~±s:㟈ègVµÇ!]\ÏR¶
+n[(´2YÿE^Šþ|*ª ±uêY„§a]éE:?•°ô&ªl#H¦h±.jºÏ‘Ò74¯37—9qÛ¬(²bÆ×å¹BÕ¨Åôs#Á`ÕM¼1Þ¤*ïŸsÈf|•65a¶ñ
+ö9>•D Cß2N©ò^üH
+ÑÄ—.ýê%Û{ŠʶߛÁ³Úužx{”p+WÆw|³&…ð›8ó¼¯þTd*ñj+ú°Š:Èmùï0¯§w® ã­óæ/Eä ö°
+üÇö•Ec/PÞzƒ´` -AÏ5Ž‘**Df8—\‘—™ ã^±*'Ô&Žx¨VÕŒ«§yH¸Øê6XTßxƒÕ
+Tw-‘ès¤­½qdŸWú«ä÷ÿý(?ÿ!VÕ(±d ün=˜ñ‚#çðåçÿ?9 ÔG.׈;‹¥W<æѺ#¸ö[N"Öz‰Q¤M”»n£ÄÀ8–y꬜¢
+nðê015zºg·ë^§ÝÞÝÏ2¶Š7©±0çÞ·j¥Šw¢ìѦÄÿ7Ĩrn…]5ß<×Yb YmG_çˆYñf"ÒôKñ‘PFuë]rõu̶°T·e1î´¯s4gÔ¡¹_j#ðCÕÖqï=lV%Ãíì;a&µúÞ³m½‡xeú~ÿx*zO9¶Šó@³ˆá,Àk»~ž/:°Ñ¨Ãÿj5°û´„,óÈ«+2¼íamsdÃJ„ŽÙO76£ÀÚ†EÞ¼i2HK0}—Î5Cvß?”üÒàym£iŒVt]ë|ÿ±®7”ÃËUD4ç¨Á•ÒgŹz¹½ƒ¯Q˜édŽÈžmÍÝy—{êÝÉåðL3LH€¥vˆ{Òûµ/ çQÍú²§«÷:ÖÓv 6’“’˜Ô£`‘¥Ý%Þ2i” >fÉÕB^©­c+\ñ©šeHRˈ`(Œ9ÖÁ’"Ž'Z¿œ`¼yº\^B*Y‰ËHN Ç#Ô0ª9¶râóqJ®WpD–©×‚ªc­¶%bSQ9¬¯^‹Öu‹Ç£»kïCEžž1ü*qŠ’w¿Z.…KøPOÚMò{8øW€
+H‰Œ—AŽ$¶ EOÐw¨u€$R¤Èe`/½Í Œx5^æþy”X »:±mS5¿$ŠüüüŒù\Ë]ÝsÍ°ÇßmÊsMË‘¶3×zH
+=ŽMómØ0WàôáŸýöñð?H#npÝ7I¤ÙŒñ‘‹8g'‡CÅ–ûÅI} ¶Q-ïŠ"ኹß׵ƒ<S&£s¨UR晽*ñKÎ –ñ<¶¯sLùSέ(û˜j¦YñûZID^ß^!Þ°÷Ë@væ³3ßvMYh¸UÔ¯ÞÓZFÐg¿nuG?"ZPð‘Vj§5¥¯™Lêe. ÃÝQ‡²t]NX]Á°ÒŒf!jHýŸÕµòr4°u_×ųÎC!ÜXo­í¬µ©@›fÌo¬-4ñ¨ 9;˜êaÌ0®Åí+æ P|<sUf~ƒX°hó_×Ñ—¸á™°K¾9ÅW°•Èà«FÔžjŒ3$ïBЦýûº§d¢\ÿ^»/*ë0j¬Ò-üR6tÕÞh·YÎvRÖòµ¾â¾(, Gï ‰õ*#‰–æÕ¬EF´Ü¨¿¦PÍ5¸¿{ñª°á4Œµ£ KØ“i« õéú¥;ÞêÎCJ†3Äç«+íÔv¬™¯9/0„éÀ‚zW¼ã(ö2&Š[E彘~L§ úÛ£•½Œ “Á×ÖÀRŠ»ÇAãO.ø=H«² ø½êój«XžÓÊy ÁõÖÆ u7äüããoÿüüëãoÿþ@ë™ñÕäåx6‰?o›µÕ ³?ígÖ\ØkШE ÖWh©}ÿ*áq!˜”‘ß9µ.Ⱦ+ ¥Ÿ,|†ü~®ZÕŽµQ—oΙr×£«p²yl¬= ÖÈèý…a©²jâÒþh ixƒÑùÏâKbn6SÁÏt°MÑÚñ1D\rGí³û`¸Ìïü+Ãÿ=èSÔŽ:R—T|ßü8 ©€AU¡OÊÅrDTû>ž¬™|Ö9´!EXË–Ÿj±ÆŒÖÛo ·YŽ²Zøxò¯Ç@žAaئƖ~:}<kÝ”2"ïù…SˆY¾—>7ý_þmçãW µ bŽ1ËrS”Hó†ÈØ2ãÐƳoÒ£|•ó ¨ 4>‘£2ÑZm7ƒÀç [ü-Ûù¿,Ä&zgôû‰œµ áEA˜LÛ.Èê]e’÷:ÇàÂð{¸(;ˈ –¾…Zù…”¿Ó¬1ݪe“}–茔w (Ùì0ëÁ%!OÚ!]nö+æÇMƒ¶„†æY_1¬¶¬?(`I© "ËœÀè¨}_¸;«=™BÌy2s<„BÁdØ9ªT[ ’ïçœ.jeÈð…G¼]žßBò9±«–¨e«¯ú
+Òª$J³‚]æ@jLˆý³NØ™-,¸þ±²Æ<,ï®·TS3ÃaĽÉaåfªÍ®iCÞjˆö½ Ψe[gð¼#ßÿæp‰ôh¶GmtIuìØÝÏúséNÀaem)S¶Ø´Ô¹1jïäµl^È`»É)z!P]Fä^7(Ôz3w½–ŒKv«ùHXaú"h‚Ë-%Û÷"„«¥ o_DNX ¢&z„éU¨Å€ÚoMãV¼ÇyÀûcÎM[ëMÞ¯®†®Aa—¦£l^ߟýèòÎ%±ØÀsþoD…Ëe½Þí¤Š®æУ ä‰.IÙ­˜Þ¨qçôHYºŒŽHé«>ƒ ;Æí-›X| }E”Ýz°ÍPwž~»ól?T\ç•åxr/"ô«ùP?–.ÞÒ-\>¡æP¿ÙaadüãeŽdבCÑp´eTä<˜rËí(Ú+š½ÿ>@RÍÿò‡(j¤P/3ÜI5gƒDÂåÄ#Œ"Là$I[0ÃÐWš—’;/½}¾)¿1 üõ¦HšÀ½‰²½:SBɸ+¸±Åð!` ¢Õi—Àè¹u„lmíqg÷³Ç¹±ë°Œ‡HyŒÆठ×ötÖ§ Ö#Å0k“+nÕY›n[Êki]*|›‚{f¹_?_@¬è=R¼D8JQ\ÛùVI†ûP‚¾1yIæ•ûÈ 8…÷Ž+Âñ0D5™B]‚HnÞšiŠÞqEí'%?Jx^ã&®êΠ̈ÙºÔ|ÝkD·JROÛõ…%Å¿3î¼ãÂ8–Éþó©æVåãJ+ÑÌ,ßrñpvTì²cd÷ðeÉœ+ve3©A^Ò“\»¢^‹Þ˜X¹·§­
+öçXX©ë§ÂËàÖ$3¼põb8I¹ìjššÓÖ¥$Vt1þ=ÝŠŒ±•AøEv:7&gUÑ8úHIÿ
+B)%0å^(øõ’LŠ¸¼š>fýÆÔW™l»,ë_vTB¹s¥¢œ8¶&®Éç–Öäµ×ŠGé
+° ÁËJ›¸!ÆPʹlÊBҒ厫aÞM¶ó Ëä3;çQÖ·Ðt²Éùò¢»uµð¶{´_-ÞèXô³$ûijwŒ à€&úÉž­Ch¿A›'5[~gß—ÓzÜÞãÞ?R¨Ø?%ufñ™{)¹†hûü â:HLeKÑç­¨,@«Lì–»ú¼œöZóõ7ú´{CV³K#q9³^PñË uÅL„k ?äO¸Q‰|£ü0š>böô¶ŠwºR]vþØÔX38ö³uÙY¥÷¹‹)¦)Çöd
+©&Ô¤Ûê$~C<Äìu¬ËF«DAjÄ)­˜)¢)AcëM 1Hò7¥‚?¿ÝŠ°)™/€Z` éR4\ù,&±\Yjº!bxǦTÉ4#/Å_>i—Óx¼„;ÆRœ;š Uâ¢8+¾(5þg[“v >bß9¥Ã?²a6NÞÀ<! ìh)ñÅÁþp‚¹~gˆÊ‰g$WT4nÃx÷†¹I¸VBØeLœTçòŸ¥%•v›à¨ÉSñ*0ãd¾«åï7Ä · $‰Û÷òåúR0–\teÆ»•fý¤—š/¯éZs¬¡øYA¬)ímkà–æÒðÅÐüÂPí¢3Ž)þgÛJ“ÙsjG‹øO„=é§Ã, _¿žw"JŸõs¥ú¤pØf¹çíƒKè(¨‘·¾ ó¥Ä÷†§ ¸%ª×ï4V,þH)¾ÓAÃ
+_æ؈|õ\çñHÓÈ¥OqÛѧ©ºlúƒkp¡}¬9PžYß/ {‘Ä¿~G7%¯ü›ö¯ÿ|ûã¿ß@#çÊE,z½áý‡‘9o¬±²0n@Á³È¨ E¤ìkÏr3øT,Yù¦ˆyXã5(¬ÎŽ¤¢¹H}¯ßPxõ²t]‚ÄðLMË)b³îŸIOðD\f çÚèžò9‰›®Ù•£F>™—y”ËS/S}b SI{:|¸
+&°«;åM †¬(ì Òòa—¢‹Ã¢=[»ií {é8+餰z-âÖ”ÐÈ"©@ èöŠJâtI¦oˆ³ü|º–‡ËyÙZŽZk¯ìB‡‹q¼„‹Š¡
+º¯€ÊQ#zÖ¤ ËÍÄÈAµÝEA¡Ÿ ÔŠ°Eú³qƒ‡ã&ŽC`að¤Òáè]FQ T¶Ò{±§ s·tîŽ\ Úµ®¤Ì$ÅÇè¼Sß;{5ál\ÙòõüN&×&–D.°¥ÁO¤°#bV²¥h˜Ý‘˜>á†Þ¨DGö[A 90ŽâîâûY»my1 Ôû}Z̤ÉMm¤w%ˆÞê)A)øî?¿ÝÐòRôõ¦ÓJ$騽K–§ÁìJ¼‹•6t@i›gKôbßJ| ˜À„*c¬7ß™2Ÿ»A§Áƒ¬Q²’M×€>2+€ÖKsü$^á$,Ý8úU
+]Nš¦Mæj[㙶£Œ
+{¡—vÓßt—1ìF„Q‚:½À§ŽCZϨôˆŽ%@AYÀšºöÁ*½°žßvˆÙ5&fi…´ÆÂ<o5Ô¼
+A Ù¬Šf'Ä—˜_~ ×Gä¤ý¥o͸Ä`h()µÐ~[*×ùã¤é|&¡¿Ò¦~xZI¿qµ¬–Ìid1wDt£k ¯*GˆÔ‹‰‰PâêõÊwø0YÎNÈ:
+PèÔ…Ì·›ÏpuCæX¨ûQL²z¤‹ýCÔA—¤ne sÐ ÔUê}Ü1àäJD¬­½˜çÁ^EkѬÀOâýr]£W¨ ÂÄ}ŽÝ…‰ø‡YLrFXøÊ’æ—Ç…
+¹šÀ¯w1Ó8"Tfi÷«—U’Gëàkb-¶—ßž^¿Ãˆ4MƒÞZÚ¹Mˆˆ;%ž+DÍ‘!eCìåeºP;Š-Y“Ç@c(ƒ˜ã¸¾Ã{îlÄzç87…ÛA¬¢ ‹å ¥i½±öQP´ÐêéÅ@„”Ò–D·³˜(ã÷2ç¿"Xõï™:~ÂÏESFrÈû›óßMôñ“uõ‹ÍFºŸô©eÓmŠëS¤FúZ—T~ºAM¸Bž;û§/©T,Òtæ~£QÇ‚
+"' $^ÅRà%À[T£Í;!\É]vŠ`Ë1Èf®¶LlJ5ÿ­f4Ö2 %– ¼¿Ã¢ ×Íko¡¤ÔZ =³C—
+7ê¼
+çŒÓD¤Ñ¤Ž[àÔÉÚǼÂú‘è³i÷Ãm¬í){ø5ÈiÁLJÑ'D–Ù?r¦âzÜ‚^Õïø— 5ôé‰{Զמõì×çMq*> Qi3g†2Ö|†°Fö}‡*"êBœJ­o[1—óKiŽE²ñ©w±ãäVwL ‡Á,û
+£Í3h’–6‡Ñ/êÌÄg‰•B²"ZÚ¶¢Ñ ©¾»8,ˆºØw©‡ç¦hŽÙÐuPÍÉ"föÓ
+ÈŒº®Å-éÀ0Òá¬Ô™;A6å?q[ÚŽ³¹ä>>™Å‰a®ï
+
+ù§)É"ú¼ ¡Y™ÔèçñPù@ô¼)äål…¨#б©t9†ã7G¡ t/M¬ênÿÓFC? ŒBd» ZÆ~%FR‘:’§€F3D¬ØÙI_#tV"Ñ”®º1Š€˜ñ,¤˜Óg!pD¦*%^yAXx®”Åv¾Äv è»8^]í}EAEðª¤×ígOB7¦$6ÔéÃzwåÍ`é³ôYÎà,šºÓ‡æëÔ‘‡Š ÜÄ+ÃÒ¤æËÅ Y¥d躀•»ï¼Vûr›k×8¯ºvßkr®-üš`¯É¿Ôé÷÷Ã"oöT±mø{|tôúµO7ɈSZÏǾD€ òÔŽæaô‹E(½A5ê äYDcëÂús
+?ÞCX¸5› ÅUö³*ñ(KË^ˆÛ/û ¹[‡áúä¥yän½ÜNl§vî·g»Ïh½À:,ÚÂjÚoÊ9°ÙNmõW}’MäC›¥ôDÑYoHâPn*oBå Î²Ì Ó ëþ;[ý w03=ºÂ¡&¹46ýû›ôŸ²¸!S”&ÿf_'•Âa#ïÈ/ÐmD\ïæòì_m³£J”$›àÜØ .ì.Tž—®Ån.c@yYÎá¡Š#‘Wš±Ž TJX釾J’­e>òÊ*ÖŸÂLk­Ëîñ&àša3µ¼ûS¿ˆ —ž±iHõVŸS…Ô²w$ûô¤µïü¡³…dd{ÚG}3™âRñºtXA›ØÜUX úW}À.›ßK ”òƒˆ“2O¾Ä~ã´~çæûÚ? ‚DLˆˆô—Ú÷1S‡•dWbùáÇ;øC“>,¢KÆ@"Ž ·è&«ª‘ákÀXMf÷ž¥:ËœÇ-Õ!Ç'oÕ·Q/a²¡%™L—Á¹Fv-m´C:"(1;„Ö×FâkwBÖQZ6L‰&¥Þ|ÂÃ7
+áÝx-‘sÛƒ,#…˜£­[Èÿ)/“ÜJr$ˆž ï œ‡µjY·È­tÿm?s2ùƒ%5Õ@J&éƒ XúÆ‡à™œ<±>E ¸±—ܵD
+ß*­s¸„Mwnco;s3Ñt†ýò2¤¿<Ð9í…æÈ苇QÉÿt, ÷¹€nœàn× cìRP*)´¬üê|ïkDþ5ш‘eÿ4-"Tl&OÙàlJºôöU ’WE½åiˆóÈ­Ll¨Ã=FŽÜÕ4o…³¦êó$ÞAJ[4IZx³8·jsY&“ACMÕýt_±í>x$Þ-)?Äèc= Áâí
+:9ùìRäA…P´”èËbáA‹’Ì¡6 ’“JŒ^…Ë‹°`(7íìyOÁõšÂÌ®‹v’’ˆ7`·ÙM†C‹©nõ$®tòÛ¦‚±‚T¥§cˆ¹¼ˆQêñ’6ÂÃÍÑú™¼s¦²Bëgþv(Á <û}•†ÞÓíjÞÛΉ]Œå¶MÙ«Ë¥U9!ƒàù¨žOÿþñËÁxýn–’¸+cÅùè¾|Ÿ?Ùеȕáwñ¦Êºkß“®M‹„¯Œ˜)Ií ̲Ôó¶Äü¤Á«ãĶ$O@Wñ;}Ax3@aeëF•ÆðSD—@´@ ^翘‰ vͬ¶\%o㶊z˜vðÚˆÁÌ Ï2Ù0?)wœpuyà)ü{E6¢!ðá=øaíìbÑ­‹¢…Á!#IÉ­®ù«GûC¥ Uã.%ñïÂï5€]çpßD!FNEK”¯âÝj£Ÿ4YÙO¿yåå`­
+T›z/n“n5 ÷V;çÜæ~™sîœ7yó FB)÷”at·
+ آnT
+¶–Õ¦!
+œÖ·¢oS]Rb½¹ TžŸaˆ£Ã1½£AµöKIg@pÐz&
+÷œòŽ
+wx'kåömRž3Ñš8²ÛËþÎó˜….\ÜŸºÏOϬdË"­õ©ûdu¼ÞÀH1[™ð‡/L<󇿸^õŸC¬i6íù]­ašF¤Ú ˆY ©·™BŽ6ï'¨¯ÓPè”õþÌLÖ ÂO×Ó‚ XX>ŒüN/ÚÔ–pÚª"ÿà†—Oí{c!:š\Ëåô'­l¬j_ÓiU qmã²O¢eI ‚‰ÕB`øÆp kÚ!´£ª¯hM¡<-êÒ¯O•0 ö¢í{Ú•$‹†rnæ02» C·~Àa0iž¼ôÆt4S7´ÅñÁ ³º,_ÆVïó4CΈ!õÕ(ð»©2gyRØîaϪADTX+Y† )Mª ‘î(`Ïœ A7S@=+p°™”Uˆ6`r ãÄ-ižª²Z:Y5&G¥œdï©>ÕÙ‚2ÚÃ9\rdMDzð@dgrÇLîÑ@¹ô?Zºd‡ GøNå Š×r$•aƒë>‡ÀÆ"Ô´Þ6ÌÅ•*qCʤp¥$ß ]ë0áG¶ ŽguØh£(ýü²{kP±ÉÀTK‘ºô òìmG8‰m×·È›7Œ˜)„SöÇ“ŠÃ¢ìeU9ŠÕRݦY -•vv.›èCÏÉ“ô‹”v}*Àa´ldNâÆð¼I~ÅQas<ÀYv*„ßß"3dPƒ ÞÖl(¿VåÚ¾6M€“¸ ¼áå1‘û‘P[='Õ‰RÔ6,- å¦Cñ)] ÚŸ~íTŠiÀ÷¬ôìÞHX`lUÑ/š}*˄ÈR­Vˆµ“£òšVŸ
+÷Œ¸Ÿ^lj
+‡w¾Hv~š;ÙzL8Ã%£vë,£!&­¦íšÅDP¬ÒÙ
+.Ë0ƒ|~™Õ%]V-Pþ@¢ö@Þé=kËpךD÷œ„oÄ
+ËYûÊúü€!H 2¢ÚG8¸¬·…ä$ly êòŒj’ØÑHÅý)HŸÃºøÒUD”[T½bÁ;X… 08žë­o¯Á¯Z‚÷J‹,iEÿøѶÈî#„2Z£”ª±×
+C—ó¢oþwùÐŒ›9‚÷~P䞆YŸR ÂøðÅQÎÁUYcÚ¸Gc@ùrQ Ù›æ úÅ0LØsdÇþ^C˜‘l>ⵋÃÐÒrà,¢£­ŠS¨ù‚$„„t©èÕ ‚¨Puªã>E6¤ÙKÕŒ×ó
+ÿgk¯ïE½€æe—ôÚýcCÌX4ú:½Çýʰ碓c0ïá_ V‡­5X{èo†Ô†gvmì
+vüdÍi5¸‰“²LíšÁ
+§Æ¶ƒc²Ç(%Ýk>fM=ƒŠ`ï5ªÈ 8§¹”kÐ+ï³@«Éþsź¯Â<¡Þ‰¬ÎÎØk+²EËû.uLÅb¯¢Þ¸.!VŠ…”­MÇ
+?÷ä–ˆL{”+¹,Ñ÷ŠL“49ŒÖæ•9{=äcòÙÔ–’Ôd!"Ɔ§Øó}Æ$MH%€‡õ¤D˜bŠ ÷Ì Ó‡‹PŠµõ>«4¾åø
+è”Ńu“Ì¢ûa_†dfFÚ]~]Ã(=W&
+m±AP1øH±Üòi¬4N’@µ% ôp”cb„¬LWvÖê|³°· w¡tÄÖa78.ɺYâ$«³¤ñiÀ_Õ┬A@ ­
+¸IJ߄š“–°óõ½3AíB67¯Ó¬ Càµq T1ǽ@°:Œs(˵(Ù+aÛ%<€Gš3O;l¨Ôº9EEb\h 4³àQÐ=è0Ô}erÖ€MŽy–ˆ,q ðt¹Ã¿xQ¤®ØÞ¬J{xCi­l»Ã¡ì= Î]@™œmMÃøȅ熖wwè–DMݦ‰»³{œûV€j"&Î>èbÉ„¥Q—˜Cpp&Ml²‚®=:F£êm6 CA¬6@ þ9Èÿ&AàÏçƸ3l!¸˜I#£öẢ9X‡0GÕ°’}Š¶ Ÿt(GÇߪb°ª±
+ræþœEl ù¯*¹œ¹¥§¡èr”ð9=–}êÑ~yõÉÁË°>€¨°†løP³ñpNªAö›íî‡asìÍR `Á“9Áö~ñ«
+¿Ä™ðBž÷a"èæ8ûü/IPA¦`Öî |²1BöÀf‰¼‚ÛG˜y
+¬ «‚Ô¨eŸnžÞtøwàNô’w¯ÂNÛíã øi¹èѼ
+sŸË…xxò&þé5´…LBkòR’ˆç‡º†$¿ŸÒ9W1ÅY‰0>»èĘ}mn™’œcÞ–ŽÅa:ãúÒп‚Ý°é¼Ü‘_Ehs=¹ûç€ã‚8F¨–;·‰½ËÆg G"î°èûUüß”—=n%7„Oðî x͆†nêΤÐ÷÷W$G^Íô`ŸaÀßÖrÈî®êªˆÂ|×MànRjž9æšUqŠ‚+u­}Ìs°æ|™×³lw‰1‰œxåº þNêcuŠÀDÑ-Ì&/_0¿´ýÃCà$¥=€F[ŸA1hÚû;(`Ž¹VUá×£YÛ1°ëaÉÐ#ÁÎÞ&Ê‘.omÛb=ÙpfRœ?¬\g¤îyaG=§ÃHt ¬L,•wϱéÌs+6N'X‘Aíxú5 l3ÚÈ-óεðäOa»#/`úû£t¿Èž‹E­[X”i,Ú«‡_ X)^ø¬+¥hüÐVªþþ 5Åâ4˜‚Báe‰¶s`©¨Üaƒš„€z©~%[TG3Èå_„ xTÃ(›5‡i(sT=”Âä<ÑJ R>&H½sô°PI-©k8D;DŒ3„Ñ\´®Ë£¤ƒ1Ì01ìÂCæ°_@W¢û0l9X’ë“}¬%!:ZÅÅÐL®Ä|0ì³<ÈãR&¶)L»Pv&%W›’+jÓ`N:„€žÂ\UK»‚†pb½:róFã 7@l<ûiö“RÉÆsßTÈû ÆŸf o¿9«K m¸Ó±Ç«¥õôp" ÃÎ)ùk3Â:S:‚¼ûKP3PLxlÇËúË\|óÃÙv©U)BÅ›4»Z_¼Yÿ¾<ß<>xǺþQ
+Ã#= Š™z•)†JUt½Ò ôñ G•I
+©N;ƒ½sr槇¹sa*̓-IÞû€¤¼¦ð™6èòµßc†>&#@ì6 à`°IÔÖctt"¨û¿hÂV|¤£ mBØtÈ8zˆÂnÃ|¿ÂÌ™ïóK¨~Åsè3˹Ê/Dä}C´¦æäl¾¹i«‡ Iºz¨K9õ%9=œ)tÏHÂ5 õƒ;ûQpMçQüÅkƒ.¬dÆÂ5Ì™¾€®)jÞgD6\E_Ò–ºÄ&²V}¿ —6$ÎVÌY”³~]«‹ÐÂIîÂM£¼jŽ«*ÚRqÏ)zE/±y_™Õ@¢ÅÔ6!„R46ilˆºÂ…UjûSÐ ÀjÚ‘Ä”çPüpXç$j‚­ž=ü„9—zÜA¾ éÛÃ9Óž1ùUN#ŽzØƧ@zŸ{tÒ—]'ÌR—LÈ•í…•´`QÊIâ eŠ‹ažÉ yÍÌ-“Á i_(ãê±ÿý ‘:Vccò„ý"¨H¸BÆ@œ~º/ÜFl §Ì80XUKïD‹ö»¸mᙹ Ð^ 6ñ@ŸI¯ìP†LF?›{%b íxòåç«h1ìæ=€%Álß­KzÍâ![ãÝêz_z´>t…X.ØR6®п N™°±at÷M™¥Ðn<6ͯµ„ñ`A£°ëKçòf’
+§sù¥Oo›pX&¬ÚÏîwTûsÍ y¢§FMwvåò¤âò¹3èã™;-­0bmgw!º¼xgþg°ò:ki^=ò“¡|ɽþœ †3㨆U½S9{ÕÓÈ DÑÖnŠ`ú)æÐŒ‰‹7 Àè&Y 8ï¾".¬4¸Èž¦³` F#Q"ûBPgöKËHx \m+–ÚÃðˆį(Åf'Â"ä$¬µKÁC%‹6Ùx(±‡!‘¯Z“7µ ˜\‹F¾¹kåã™þj ~üõ/üýøñÏ=áÆš®I‚åDÖÏ ê§<Ø}ãI{^‰
+2••æ³NbЕX+5ÙÍV=BÜÍ©G$žEVd /‚ fÔpìclC}ðjH£òʵ‹yDèXQj[nŽáþq*mC{‰5ø1!F†£È2´uC(gE&W&Äq{¬âDuguŒp‘Ï·6ˆÞ4ü^í³8Üš09È_LÈ|·¼«be ›s4nôÚˆ•Á¤3´•DkÛuò­E©êQ™ZµiØFµU!%­cXrŒ•ȶæðHˆHuZšOŸ:fq«`¡ÞT‚Å]ò.³Åõû²t¨-y†XE|È|.Œ»Ñâœ3Ûò»búñ̸‹ úžb‰òcÞ™5!|®3ÝŸÿau•’”lÉ Ûü`‘e¬8Rt¹F$~}45¤nm³¢¹LJR ‚N¡þq,û±…Ž&`g!/ «´QO|7ë,±òÒ³¯Ú¸ ¬iT_ý&ˆƒpAü\vK…•‰BG…êœzÔ6÷ý9–-/ZcÖæóU
+º_˜ê¶ jø’*ÌU3à¨$Á±ò>;F;K&ÖõâÍ9rFH(jÔò†pùÈ/1p¿9–j‘3A‹<²4ÒþÔo&Hƒf/µWy>
+ÓfU¬M’eòêr¥Šª›H0`ð‹.„.ÁÖ>R‹¥î/ý†–bpä,JÌKw
+Ž Ý/2 ìÓ9þ
+kgT倾 µLƒCP1Çñ¼=<3…¿ÇOÈ–&? º0ÃûÜS [ý¡?óÙE
+‘\ ´g?'cH6%ræèÙPAÐ}¼ßúÚ|‰Óñe,O¡?¸R—›‚I%QÎz7~b\3z¿?u}¸ >–¥Üò”&¶¶yeÓ­KßÒdÁo6ñFÄ6)¡õ=øç†?†ñuι.I>sÇöÇ=÷ }M„M[ÒU˜x‹µº£H‰¡#­=}3Ñ…áT·
+4kÞî„‹iÙLú†¨m…uÀSË„h¼£–òáòYÇüT,ƺ¿¸N†7ÔïØ=8¡"Í×9&ó&$”MÄØíÚñA‡i,ò‰±÷Š‡IÇîQV)*2öW½ô@TÕŠ¬Ã\g/¯ ú’’ÖV)­®g8«'ÄlÇô%@V§~âš0É{ë´DÓèvÅýì——&ûÂ[í kð_ÆË$9’[ ¢'è;Ô Ê0ëÖ²oÁ-ûþÛÿФÌÈOši!•‚H ÂÇA{æŒëš‹Q½0yæÝz»p;dq/I­æø²0Ť2‹¶á‰€Í@ ´Ñ}1A,žá³£ôAÁ|vŸ¦e(ß¾¼U!Qz½H%µ‡ØF]
+òek–÷å½}>U†B…0šVÿ†4`à™f4U/aÄŸº°ÕVVah°S´1Ò…¾X»6C &ÛY”ÂmÒ.´«íÕDB
+7& ¸Çä÷Àòô®[LØóTÚà=.†‡C,0ØñëMX}F…'ÛUàÍ‹ÙéÚŽ·
+2¸4F(ëÊpøR7¸Ë ¬ñ$‡í’ÒfK±%‡Ô¥É~ÍZÌëÝ‹è”B)%-þb-:áRÊ_RuŠ>wãk<VŽ–
+X¶‰82i†Q¸2 OÏq¹ûôÆ—vN)J`®ˆá¸oúm8%6rxE?hõáQœ€ 1Ÿ
+à7À:ÞZ«l°äIènæFišCþ,~%’¨½“3;"QY+8¶¶=,Q÷
+óí%ýC¶ªso:Vgûi‘hc}¬:ÑÈŠpcj#oEºÓÚ eªZÊÜ="²¡¨@µs)3ð­dXFv¹ŽqLþ%E”ˆµƒ=L€O¼LPñ0%‹̪Bìéʈ”tixOšª•Lmâ)Û—¾AÐmà-¢ÜÀÎûF$ÐÞiCQ]@s‹b¢…Ì!y­
+¬*ÛÊ[¦8ë¢Ef’Äxiù¬î
+Ä'×ó|?Ö‡rÛA“ÝE8îJ·Ä‚ûgþ?š²ÉŽö’;ÉöU‚uƒ[peëÒ¥d
+6` Ñ>ëÃ9"…&?Kvo3–—‡È0+³<¼
+•AKD®•ôà6Ç€¹Ëru›¾k‡½Šÿ jé^T[0€¥Ù_º^¦BlÈ]ö»ÿ¨ŠÂÀ.ëmý»6§îæÀ ¼¾¼×÷ȵB»ÈŸMó:ªKÉþÔuäÎ9Wà8·¹àÏ}ÔÆ·æx»piñ·;µ8^“€öÈ&„™rTçßLh«W[’ã×ø@—·".oŸ»}þäNNH/DÊoÐtéÛÞ9Œ€å@^×õ½"‚ÿ&’©*¡I¬Q1'bÕ0 .Ž]!¸RÅ<ͧ§&–äúµíÑ•åž;‘|c^ª
+J•(â³<07˜D9O6ÔG<˜#$ мm,*ãh2^ÄòŠúO‹<欋Z ïŽ-¯ÏE(:fŠÖ-<³‰l¡£
+£H @šÖ:ÌI
+Ð È8›,«9M'e[šŽ9Äç’äŽI@\£tf‡ ô줅LÊxÓhìl{X S–9oÖe¨éô¶E
+ª› Cûºr¸éŒKŠ=ÄS¥öM²ñBÆJØ7SШZ^ÒÜŠY’¤Î&´}Õ99u%‡´â'â»—â*z­U2íjžKu‚¬RKîœsnç5çt¢º6ò%;Þ4œrüãT}ØðQ
+(· ”ißÃut1S-7áŒqÇõ2oküø"Û M$u‰ä|1xgÉRUµ­ÑdºØÏ£‰ì-jº+ÿ˜ÍßÈ ¯!ƒjÁëHú—A „[ÑÆä<ÿº×@°/k¼gHJsF”ÀC|£úˆ.™jd|¥ Šæ
+„£ÄqòŸ¾ª 0Ò!‹ºsÄ®eKGŒ^d"°7]:IósˆqñÔg“ΧB#ˈmê­I¢Õ¬÷<EÅw 0êŒ{›$ö,ÍG2Æ4HÄ;Ù¯\«ˆ™Ž‚ –Ãù¹…ÔiñýPŸ§¡E»­FÝFŠR²f»`°±Ø
+žG±ÉÙÄóu„m𙃠z†ôt•Öèá…ŒLHˆ•Å‘—s
+ï‹Ìƒ,H‚mZ˜(Ó™â˜(9äðŠ‰D ÚØã¤ÛÖÁ<¹¥/C™£1ôÚçº)HgŠz]Œ®X*±»Aðuˆ ¦<ÇxÈw®W+Ç7¤‚Z|[ãGíž‚+ ÈÍt˜xÖ­ðÉ#µ,ƒG7Á'ȧ•"÷9d oÎA8LÆ[ “·äGÑÀf­ä6o"¢gxlÖ1ˆs73—¶º¤×iΧ"}¾ÐÀjõ`­Ÿ2åuÀOç¸oÄ»ÛY^—
+¼ B¬”œOGÏG±š31ˆÒœs«³+ãiõ|ÚUÚº
+Ž­îê‘òìÈ-DÈþθ–ÝÙ=EÆ ƒX
+©ºç §Lg€Âjv_SiB„¿¡*2§‘véòSnv¡”Ïèæø
+9ÕêãÍ=ÕþË!˜²¨*Àba¼{Ož|ÄŽá/´§:R…h8²!Gù
+H‰Œ—KŽ]¹DWP{¨q¾ 3I&9l´‡šz {ÒÒÐû÷ ’Wèz–JêÅ$óÙÊ#çÉZTÿüGõtåQRÊ©‹ø,­\>JÎm$« âÍÝKqëæ@üê=R þ£ ~äÑÌ#u«ÅçØ5zÊ­Õ\[ªÇÛØÕF¤Ô£÷–
+W/=ÊÈ­zž‘ì*–=¬+5/LMa-GM%­HütDmä>$¹gç¡Õ{=B:çZ탣kÛ‘Þ19ܸsö‘Úç·ÿ
+^Ö
+_ñM”Có‰ÑK…z¤dù
+Eo56@GBTªNHΕPEZ|@(Ž3_\£q¿ÑëÃ1dÒ*ýØkùyoi”È-ú‚tb‘Íf»LAY4¬ï@°(7¥‹¯„— W2º-É.^S£I|gWDM(ÈeC:™2-ö)¤~vl‘sZ¡ÐӪΓÝY Rß±IŽq"ú@Æ)B¹/¬Dg~NNË„„,NË&o¹[¹Ä;Çý(RTe˜èéÝIݨë¼B©(dÓ1`kxrÁB ½?@pÌ 'øûŽô†±–4ÊI£qº0=„ãã·Ž]IDG” · ADôAß»NTÃðS­N²Sfw<&ļªMc2žÇ*s–¶º“Ñ*µa—*UÑ7¬«…ßÎá rZxLfY¨¿ÝcÏ­j‚XÒES t_§‰Lþøž&bhš”‰q|™ ˆ8T®oH£É¯®EcQåæÉpK+;}V¸2êšiáJ¬¡ÑÅ^ìG! F³¬s^ ‹g: J.ø3ñ‘¬AULny‡z%Pz¢&ǾFÃr~ži¸”Šî1tÇä O!D Å
+»CÑL˨톖 E¼ÄîÕ×DÕijãâ†,I©Èê$Ê 1Gçšé;¹È”§Ûò7Eæ§RÖÕA¤YŽñs¼þú¾ô<\»¡ß÷ùY`QŠ¶ªÅbÓ´~±a 9gˆ£¼Úô,Ž§wîÙáW$Œuö扥 ¬Ýu;ä„$ktý.yÅиöÍÕ]#Ë Qœv3݉nš£ßŒ@qÅ,ry· 4»Ò›æYgc4 Ô†¤Þ'ÿPÖEs¡m$i‹Ý(˜ªã¡€[b%,ZñÇ4y…§<‹Q½FF5ò2åˆ% ý<éç d+_H}Ø*Ããáœ^Ù4‘tzùž=¼
+dDC9Fô–ŠB
+¥‘ÐÑò†—žqqÎm‡ ÷&ÛÄømP…&ý]j6Ĭ±äsŸƒfñ'çÍûŒ#Úuýœ?ÓŠ×6nÓöË!õ ¦u‹bð¼Yp_4)CǴȬH]RÃÅ-KJ¹1ô#õµŸË©‚‡!¯;Ép^å X~{ÌÚQÜÆîó¡dçc->O:õ²¥üqÔÍÐ÷'>ÀØÈâÙ3q¦¿ëCЄ×À²(}?u|Éfh¿Y ÆÒY)WßEv®)j[Çß!ªRÁÂùé_Ax -¼…]ª<œÃ°À9È+¬ý
+Ì'®‹$=†rv?Ê7ÇèAúX)´E>]æË<œÍIc[iPC†`(Õ7@/ù­|;/qý%M‹ SAþm6Æ CÅÞE1éa@°´´1t°uˆ’)kŒX
+ÈàÇMA7 JÛ¾€¶Ä|±¼T¥¤aGKÜÕ–ÉßçåÆidx cÜó>§°!꤂™ÃGæRî›Þ(É‚ T¶îJ|xû„“Çh¢á òÚ‡Þž.®*4åvÝÃïÖ^mfidE7´#k*€YBî9e^»ýÃÙk0˜K¶ÓÃÊÓ‘«÷éì)â»H´xŒñr*Á–ã¤SŠ¿VÊ´ŠÁŠåÃ'ͯÄ!ÄHœú5í?3.¬œ~=K ›;€h•Ëb§G‰‡Dd1(5£QSÁrð³–/ªH|~kÀõ’Àwñ8¦„2ˆ¿*Iüæ mAXòdý¨FD¦É¬Æ9†ååÛF†œÜP0 H€m^þ1„<üžG¥H LówݯBF U¬u»þV‘ßô~îsè #À\«ÀW06“±3'>-ӽч+/ŒÊz5E®Û>ÆL2œS½>1m¯ÀŸºN‰ñyì—€SˆÂ#Z=Ì›ÕlN¤áÊóÁ)ÍQ«‹&ʽ2Õ$# 9Ûc˜õ’”¸À`-´S0rT´—[‚ @••Â¯#x$S™ ¯¿¼
+¦êÍ"Ã4G|…ÏAJ‘"l±hoòÛ•âJ ›+|.ÿ'¹˜í@
+ƒŸl'|Š¤‘C¦gŽ}Žà?ÅL×aOFÀ¯±îM”Ís'ÓÄú4÷&ôšvaW’' 9B°£4KtìT#èžßçÀÏL‹Ÿ~i^E]®;{P`À˜«P‡›î0.–>W¿Ì¸ÌkÔ•iÈ“uöµÏH4.É ûER!ê +/%k24UÁ”FEíëÀÂ’]qŽi26€Öþk ½AØjÒ-ý륞€}ð¤Ö¥ô”°±Ì
+Æ$ä|QlZù¬ŒÙï£d#²H®'
+KJvVO»,G˜gdæl„8
+˜2ˆnA{v‰ò.ëý
+ Ë[zÍ–ÍÎÒðµeÉ¢˜ÓI&,á6RQ¤ó›š˜M’uIé$³1.³zŽéüEKn|ó¶ÿì‘‚^;%@‚ŽÓË-)‘œ
+w×;¨AM‰_htê'¢'—â%¢ªJ³‡o9`„ûÐóù tܽ,IÆ~ú–Ë #:XÖqò¹J‰ÝrB–½ÜMi6b/žGËn2¼óòÜCÀ•Ÿ Τ…mÕæÝ›
+STfÍ}Ξ8O˺7äUi•¥Éó”À"Ó“UÀqÔyipÎSì)Á塾¹âÓ4èƒ(2ï>`>`
+?¼I™<U OšÏU"¹¤GWûšh‘èm—èA
+“Ïó˜gËç*]ÄÜÔÛi5§ð³MîiÞÔ1>4¤ŠººBÙ¹
+—ÑjCä{rœghŠØîS;çÀYÛN8º®ÀïÔ¾Ä%j¦U}eºˆxÕ/æS¥­ßs´QˆTs{Q¿
+“áá,q=“’NrL¥ú)8æ.+†q¸Wѧ©PŠäÛ)bŽ@O{Tú°=C@¶uR#º¾é'ܽêAJckÒ›3ñGɹJ#cb/ç(/$¤RHñ’LfZ ƒžœ•âï“àEØõ–õ™¼>ßåE«µQÀÒfø(–ÖÈrƒizÿ:ŽŠ‰–qTø“'d´ð˜`w)éÁ7òPà<²Õ‡ož¬Å¨D
+·­Wý¦Tú€ÁÀïÔTCEcTœ¿Kؾ\£øVª÷€&Ö éHgÑ0©‚m¯~0ç÷HÏl‡u g6´ÉPç Äéô3_n²õr_
+ùžuîù1y}¡SâR,dšá›¨ÁsA0Û{cðŽryš¿gKºOÏ»®Aû
+ë4¶}QþË/yGJORAbûÛ9Å}d™íš’E!d–y¿—¹²’†ËºAŸ°åPuó¸³|ÃùœÅ)¤Lì_[a\ŒÜõGûl;9à@í¡=Šš0\Xª™CÚËìîÐ"°unäs'ËÂc™SÛT¤6–o†í’(…)¿*FæùÃ\7pu4 sw'ÎÚ*(k„\ù°ïÛ®¾È%Õ†ÚK Ý<K ¯WÑIˆ/wiÇË9D¢hÓîèK‰ÜÕ?Œ—IŽ\G DO ;èFÎÃZ^ê¼’ï¿õ ’Y°ªò ¶ÕbÿÌä|1 i_íñ(y%ÀÓû¦#¤À¯X\J‘Bä$y¶Ø¡?%®, ¼*ù~e5à†•š,É=o­þãz3T
+]ŠÑaßDNQþ‹(µþå ””bèyˆöû!YÝ8¿=‹]»8² Øþ!ým…x¼ñ EFŽ}ßè—†
+ƒZUì”HP׸"êÚǶN¹ùÚÁg >Þ9!U( ¶PÐe+˜<f‰£„U¢>Qm¡ÙÖ\nO & ifHZ»Nº!Ÿ‹^Ž”¢÷Ä—;ç}^EQ­"9mÞÉçä!Hƒ§¢¢­Y-ÂÜ’…,Ý#(š¿(µûñíó9—P"û“SiÐ2¿ó¹é?ÏúJ r Ÿ®íÞ¦1dúJ fö—ã,Ö*I>´”Œñ™HÎû^EÜu{ærÌS…÷`š¥ÄÃ{Ë÷¯üÉÆ·ˆÚ
+ÖŒEÌFy:GÖe ö*ñ•ŒºãÕ"4V…§ËÅòGT’„úÏp’× w ¸„üOJî0ÉÛþˆqº‘Çc’¹a§´YkÇäats‹ayâ6ÀÄ´5á„°{Ò0?x8µxaC\в܈P@rV€ é 
+eòßθÒi¸U¤~cÈŒ»Õþæ7`—`HÌ™(j"ÀdYë`&K„*ÌÛ… .ˆ§ÃŠ!<Á™(Í»ð=û¯\¶IR9KSTž´KAõ8Ò¢["#Ä¢‚ž8*­LÔtïräy1Ê,}œ¾péMŒ3ÞDz€%¢»£¾¨ôl—µPcÒØIOóuwù#ÆÊÐlº)Ûèu  éç‚d©‚k¾"-°ƒ&«XÝľÈ9_®‰dl‰uNãš¾IOÒ|IÕ¼Áâ³Ì{Ó"£@&f€§2p„T]òÔþ¦Ø@ü“Ę"ìÛ k4…ZK¿ÆÒÛ¯WQmŽÒÜúÞ¤I‘4äoÀ~e.)´ÒʧÕõrôNšD¨êÒþy¡!'L¸[é>AðÒZk<Æ$s=¤˜Õ#¡U’!k<VƒbØ"VKØÛ HI«–!¤xC? ¨ßg³Éh7zuƒ‡¡þ+‡0©è´z“sÜíYDÐw"«N¾ H= ÉÝ,ú)"( ¶ÇÈÔÏKØÕäŽDߌ$!ÍG­ð ‚'bí£`@g
+'‡ø³J1 äRËàØ‚ D}¹ ñ !¡˜Ckóu—)Ó¡çrº·?ŠŒpWÚ±Ìýð.G§OÑJ}¸zsPn)ýë6 Éut\Êù’´†:3)Ðjª×ünÄZ-ÞE ÷*²ܹPãѯµFùLÉ· 1ÍX$™:)±‡À’xú(BÆ€At¢ÄQ°ŒD–qÈ‘aÔ
+
+EÂgŲiògZz·t A å(ºø/Žú B
+\
+­4O­U¶Ê¶fD°ö¹mbRs{·È­+häsþH>e
+TaaAäÁ¢%H(k…´{
+á €X/Œò˜c=Óz_›çñ
+‰¼ÀPükí,ÖRR§ßx©ƒk‚vÆ 0¹šúÑÛ†QÎÌ<—ëqI#ÙSf U.¨Q¥\b§…,‡D®£ê€!Vjõ
+*l9ê·D]löÚùX½İy« H1î—>•xRÍ–ñ„,ñTòêù±«àœ
+ÑkùpNÛb|øÚ>5J×­™Ï)ç{Ù`”…]Åo"ž2 z/†#!L‰%½rJ!(Pówü,C^ 5»J [áºs4N‡i™§Öüªà²˜Ê1†a{;–«ÑË9Ö/Ú_ñŠ7\î
+s’h„Aè,ôÒp~
+uÒ´Ar>~
+cô Suöa:$wq\“£šr` ýhÅÇŠGŒŸÉl zìnþ2„ ™¡žÓOžO—”—‚F/3‘©0 “†/³é«Zb”¿"•Àåìî3Ìl2!sÁÎë¹» ·s0œ`÷½ÌEòÉRtÂ’^w ç*š-)õúÑfÒmU&ñ«˜3…A8 &¾Ÿ°¦¥˜qÛŒš¦“·ÏË¤ß Ê7ñx‚ÚѲ]–».¼ƒø-MEçûe2ï] •ØÎOˆ-?A…Š‡@(ø–5îÛ=ÇèéÐsžsØ9‡æ¨CYW3bk`Ù³qé- ¬K–kb"¾Ñ'¥7ªsœLjç¡õuÕ#¡
+èuïfzˆ:ô ,zª¯ïý͈þõí’µîêÂñ0gp¸ fmr««»¹¦¼äœ-¤™ÕÄ©¤pBÖ¤0K–Õ^>‰Ò¢Üš!m,E¼8¡„Z·8GŠC½p\ÓŠ -̱þÍCc©1
+ˆÍ Cé­™ÐwcªG¼„|^ýD ˜ÐÔM
+*º›WFbȬ¢~ælžæò­¼@pˆ¬¢”7ZcH} ?:Q{ËCËe“Žo¤.ÂEкl
+BœEít'‰Â 4ÐoÞÚx×þÒjW:5[v™O¢|Æ­;×åô]FjŠ¯%nz^Ç&'¢õÏAï%¸\÷G ü;!-/8ësAt˜Qy¶¤õ¹BàŒ¤0l
+_Â(®»ûÈiþ–at¾@þ>“¡$&a6æ‡sþ-“P¬€È33çò‚ù–.*¨—¶«p—hÒÇﯞJsÚüÁÏ{Ô=‚•}´“éÆ
+À.çWý*!
+7±„ánEµûÿH1aÄ•{·Ûâ
+!*Ð5síËž3AºÏ!_Õü=
+C)å §ˆFXgÿâ ÓŽúaKbž;wD@¤w+Š÷ÿÖâÇ1 ‹-àë™PlUèØ¢‘X³† ýü€éÈo’ÛP𦶠Ï-·¦ù‰`¼T¿áç
+²óβŸžLpVéðnqtZa‘›¬$^Ùí<v†Ãqí3Sâ·ÅªªÏ$G½xHZ~G’ÍT ¬/Ã.ˆ=CÝr  Jµdú`Å›˜·¸îô™o î \~Ëa+Ԥز%R@¦Hpž6úb)HaÉ©“°ã_ väÁ‹ Ï«í‚î'tV=òÝo”””¹&_Æð´ï=h[Pd9'<tóÚ@¸3Ø$³íZ=Ý,VSíZû@8ü$F³ñA&€
+^­cºÒ¸óWý„ ÐÁ”%™'¢çÀ™i;²‡0 @¯ôò þB¤£xèˆ7áo»¼ƒ˜îð×D ×ÊÊ0õ¸CÃú…é†@éóô€’‡¢Pf×ú§:ùòÊxbØ(]Vs¼OiÈZà ‡÷»|ö$Z¥–üQò^TœËuл+{(3™
+û†DƒÒ¼8ÁbL‡y&Áý’Э9´.$Â&Ю©^UHÞ·è©r žÊsžj2lÄR'Õ|àÇö9]B¸n¥½W!3çxFD5²Õ‰pÃ4gd,"ªÈAS1¿Æ\ËŒ{»/Ìü
+¸âh±æëByQµˆ‘çLÑŒ `r%¨—O%$š8JÎçÀDF#˜ƒqBØ lçÆ0,ä
+°G3\E‚Õh‘ikg\J›ç yEÓü9Äò›
+ä@m§ã ”A+©{ÀT÷ù„!Œ€
+‹-©H6+¡ÀDkÒ{%Ft.h#ŽÙ®Æ†9¯
+—ÌuÖPT
+DUD mÏTsá‘?w¦ŒjƒDâ-r fcmIHÐa€8·sRS/iïtó“5ž º(Bªx¦3)!Œ:v(‘ª >M–·ë]d(wv^U0
+ÒÜÄÖ쪥B£ù. Pm?¡övº`¦û¬3<:f-iEyÀØNCܳ " % b£Ðks–‚œ€•%ÿSO* ÀšµëÚ½„xª*è^êùZçdN
+Gv|Â{Ъ
+ZîL£É*/IQO%²L†Ñ¢ÆCæ˜bZú®ë–7t!˜|Ò'‹'q5ý=Š· %Z6¨^©¦X)I  *Â1B ›96&ÝBêŒVCt`ÿ?€®Ô í 5ðªÀ>0$ĸêÇ H"–YˆuÈá[̯[Ì Eÿòdð›¸FAÔ d¥¸(oJò¢ì@‘³TÐd\’âùþEÐ[ºõ©A¥Ó@M~ºø{Ð¥ïé~„=“Cn áîï‡ H4É/`"óÇCˆ@K@ó<„4æšAb²MäAHfQFN‹x¡Ü€¿Â÷“ÕkœÃò/ÀVé~¸:K7a^åD:à›¹B¤jP$³.©Àr&ß´ÐeŸÃøŸ¤qŠsäKY=€:Ù!L5*ò·p¯nA¼lF&¶'iÑ ¥°ž”xu=۳Ș—$ŠCH$~#Æ£$ ¸œ\/!ïÉJfW±ã¤d½ ÙÞ|¬$÷¤ЪC'¸¡z¹¯gjRhEú4ž •Æ]Ïÿð!rZµˆ¤ÂSÙYE@‚\)c_µØ5x= nAxHy Í’—ú½ƒ:y ´¢’ ` 3Yv£¸W—YD@%I2PÍÝ/"–÷ ¯]\– ÚçÙð=ajçœ÷A¦lê5f $æil+k7PÛm‡
+DFõ¨‹ì`Ÿç6ª¥lƒO°ä$ò!‰5ß<Íë:½ÁR³#e3ÛÃ{¸¢ A"¿õGâ¡\°p‘' Þ‘Û(²Ókš²$ã§0¤Å+P3U‚´-Sª‡pQ~֥㖪hÿŠ+¦;D±.@Í^&B¨“(ÈŠð5e`$JñÞ¤XÐsPª‡$ù>f-£e#•i5p)-Ï(0Ž
+í
+錢-Û ÌXH„Ý~K÷ „#Äár–fj“+•]ˆ®2XýÇA/é~ÿäN~õ÷³¾“‘‹§„ö­ˆ;ùÇ÷˜_טïK¦˜.KŠ3ùuԟƼçzÚOb.Rf»"€ˆ]îL~t‘W·–| úýƒZ«#ùã/"Ó'˜–PÂlÓqUÀÃj26ÃçéÄ
+¯·ù¾tä3~ !ªûâÏ_Ø
+L¢NÞDt®V¶k›èúeåX¶Öø:©
+u!w´OòÝÎ"û^a¹%‚Ãåˆ0g6FJÛ/%a“¾+Çå3\”-à˜¶ãQÌZļ1SÎhìéž…KåQU&æ.qÛƒ„.óIŠ¯!˜Œå'%ü°=.“ÄLð;øËWM
+ìžÄLZ\ZõZòbhoEÐ:üiSŽï<ÝûÅQŠˆ–^_…‰çI * Où“ÒYH^™ÔBŠUœçŠ‰r3`E·É [O?ÊLÍ#/ðç’JÙÍp4Û$E­ç¨’äÓ
+Á!‡å¨•‹è'Ù¡è/Gç€$NA·âQzºMZõø¹„€N‡N†s ?6\3u¿_‹<‘ ²Wà^%ÛÒmØ9-vh=•„2ãvAÖÐËgL™›t—®®ó"ñ Ö"¬"zÝBÈů‹?aUÎC`žEÒ¶¤¾; äøž –âMø'†+`ü®”ôÚ=!|'\0O ñ„¬ÈØ2Þ™Å(^ú€$iæ›:SÀBB«˜”tXßÜ‘¦@ÈH@áøEçtŒ¿|"D²*'à8Hû.e…àÖD ˆÁʇõŸ¤á©èó]QÅóK&u!®³`]Ò Yh¾+ÁÒ³Q¹ETQ'bmÔ´Ÿ•bnÐ<“9¸Ù챈c´S‘É3|ï$QjaúsÁ 0Mx#ú¤*žÍMù*Æ´ðgÜ`^}Æ»i¡¨ãKæ‚Á§¨r¯?$
+z<cDb¨8PAdZàX\²%/'Aʽª8¡up ‹j©:qu²
+­¶`Qæ†7(,qT@u;I¢g¤­¾p(H?OúVòñãïù矈Ue•°d ü6fø
+‚WÖåóÏÞq:€P=n!îX,f…ÇËÁî¦F[^Â*¢M(wmQÂÂp-öÉYYE¸ÁÀ
+ùLþ£ƒ²x¹Y gÔÀ•¤Ï
+ ëéÙ¼†Â'3${ËÞM+{Jß‘ËðL3Œ
+zš²û,û.Ë>3äœ9J†Ó+6þ¬'—@“Òv¦¸Ã¬—eäoÅIü sc†Ä‹8•.‚é¦|DI–ãÁ ¦ð°Ø+ˆ]Ÿå/³3¡"ßWçø
+“Pc¹-ƒA¸Òmá¿ø"ØF Éo®YSœ„±«¾”À[°¢¼[¾Š %cu¸î]lpiBÄméò×ÿ³ˆ/“¬ÛUKìÙÄŽè枧‡¨S ²œ2ƈP9Ÿé}&Q¿á<È S"Øf²ÎÅÃ:œa”¤ ¢_G<æÖ4æVÞ@¤ ­SŒSÚHŒ[ †c(®ÖbV<®¶Ø3Ÿ–üÞÇÈ7¨&Î)Õ-û?Éù*0
+¡8
+™P“ËÞ2#<î½áþC|Ç>ú2,)S5’W—÷ºvOnñ㺛ccS³‰qa™>ï”òTd%LšÜ"'üøN Bp–ÅTdÿjK”`üyLSZ< ±ÓÚö)âácP’‹>¹·˜Db;£¤ÉXAÿ{D ²À¯pC¹\Jü¨j°$Šzç;˜WK†=ºÌA`]<Å«êPÎ&ØÅ«Û¡„6(›4势®ûPÿPÊt á dy PI7ÚÎò¢áÉŽ‚–hé^¡xÈ…²MAßúƒpŠ.Åþâ+–M™³}Ĉ¶ÛËÃ\(Ë䕘JñxaÛ\ÏK8G
+^ïqT“,\¡jºè{½Q“$aûU9r1¸ƒÅ£ —íΣîíØT.þ!Û›ÇËJ2¢xF}óˆ4[Ç>6³š°Æ“ÑÀ9l#OÅL~;ÕF¶Fd gQà~:µRÛ\f¯xê”âgýR@DHâ·Y÷šaAF»&ÕÞlñ)ëê”±Œš7S};J"Ô5õ#Ä¡Ú¡_ yàxç>všÚRi¤k°´ñ`-ôy8­3î0‡jò\ã0î9Lnò¢9­"„1ËuèŽ<é"TµÃH“ú5Å1X·Á¤B†ˆ†jN]\Ë.JÁ¥vDÛ¬±©@›òÊoЖ2K¿9òŽÃ¨‡a¨eô{ŒE ø0³2“ßD0„BK‚up¢—¸AŒ8l+oVm1•”Ä"BsjÇÎ<Ap ú÷ÚG2!êŸmÆFB‡$[¥Bø‹0´inìÞ,6-¯ñú"±4 5úyN$è%…KÔUÖ Sªht\.$_£ög ^Z §az¯žäÞÔÖô'Jglå~H*ÐpL<_]Ù-·©å}ù|¡Bp¼U}Ä3ÆPú&ZEå‡*Ý+˜NIô%ÖÊ\Æ ó‚×ÔÀP
+ÝCÐð‰×£iU”á[½Ž¶äITš0•cÔ«‰¡joŠó_¿ýã#}Ñú"—þ™¾xl '¾e …~?Óç¿?˜!¾è]¤‹s7ñ1pF"‡ë?ÿø°ÛRì ¸H²•{Ð÷)¨2ÆW›Þd 5I¾o÷ôóäLß\1}þ퟿ý‡{ò2v3F: ˜¾Å R}·5qS€ÚEJyJ¤hë]9ëeHধ ‘¢®¥ô£Ñß-¶D¤âxÆÓ 2P= ´tlŸk}Ÿ¶{„ 7¡Úô ‚ÃCÇÓ ×'øyòNzýüùwB›¬ÓE©qß/yˆ™±§lOÊ,ªæ’a9A
+!w£T5eÒ%à¥jr´z”xŽ.Põô¡ŽÅ¤N“(š:•„í1(Üžtê¸m™}nøµEÇ¢ºÔPÝBùdV^z¬R5™"2ˆeìT7Ó!NSm6PLÒ·üN|Ág*©5BЋœq\ÒBÊBÄ®ÙC8­ÆWt§—تèéaÞ$x´ ξ'šÊ 3}‰íVP¼ÞB¥7â˜ÄÈÇöA‘3Z Éà~þÀ\Š•÷=æçMÌjhžø´“Í¿8.™Å õªÁŠ¢f¨wê”"éË3Ù¿P³:ŠÆ…«àp ä<‚R°Ém×û2¸på!D·e’wH½Å"Ìd@Ûí;Ñ 4hoûª`DsåÍ­*ŸRšSMP¨©D¸z·ˆÒ°¥m
+pEh_]œ–‰0E½×êQyYL=³¼}'ê¥mjz¬S7=h¸ßM½‰HQôž
+jEo2«ˆ ’Ž~²v =D‚¼ÉŠj\CÓ¦¨cD¿é§¥Å*ôem³
+ ®:¦‘Gt­[?TÄ™vÆÔèF« Œm9Bª’º‡¬‰ì,Y¢@Å “6†‡L²Y¬R‡çç¥5»àa×0KäfëèVƒT¹–‡Tù"¤ÈüÙ-¤® ±t°5G
+5U€b^[Uª½3£e
+ˆ crAHk,C›m݈çž*‰ú¢Iv7Ñ™”$ éE¹.…Ý¡(ÔJÄ”‹X1™p «.è˜:Ò?] æ/ƒ ÜݵVÀ‡ÑJ{-F¤ÐÄ*ê¯#Ac‡_œ V©rÉT]G¡_ ³ðR'ÍÇ I.[yÎÑ (éöCˆ·ºr?
++È•´Õ“ YgÙf!ÙEÙÌ‘lª€(2nï^`"+“̼ûQ'‡fN&Ú¬ÿé‰KQd¤ÕC@o* {½¤T©ÎÚ|+f:f&%8ÅVªVÇ«¯ÓMúI×nÙ[©•ómÉoBTçDƒÊ–yÞ1ˆÇ‡È ®òíÌŠ{ Æ!èçM5Ü„'ÙßùæÁ›Ñ‰
+`¦Ùz0‹è}ãìèm-¡ÀÚ: ÈÁH6C#“£ó·RìÝ@j˜av·•pmé%׬AP)|“—xÃC˜uȹ${\C3±á!sÇ:䟄N>X¼ñú`eJ…•‹‡ðÔ¾¼r,7¥ BRBFINˆqí Ü$ª|uÁ[6õ… ßiARÜ#ر H†q{_Ü›ÂQå»í&Œ^¢eÔ4Ï«PG4"°wدˆ ð€«,ïÝ<5Õñ+öÁm ?L°Î +º€Ëï° Êh[ÃzƒW‰.³èN±•€ò@495àÕ‚¢ˆ‹ì³š±“z1w_ÅÊs=õ,FÍqΓVîáJÑĸº˜f(¿V•¼—*µJÍWè”6¼Þ†‹Î
+ARÍ™ÒȬõ<è¾›z¤
+€)žö,ä0éJ3ïŒ0ëÿºMº·g| úyðÖtQCLKœS-un¡)'dgC:”’CÐýP|Ä\âZu>zÙîçÉ™üè·µîÅ$ü£Å8å²ú1w‡˜C¨PgÍIÈëã ûn·Ë=‰4l”:i¯ôÍÄüéfy$[IG–ú¼MÊ/A?^ÛsR2f[8u;êSÐé-3,
+”áSI~tzÉÙ²#
+h=:<Ô}»‡A•¡Á)0ºŸ7AL`AÇãá á™@: åš ²¸‹´ÐeÞ1iªÉ š}å8À€¤í
+Á¨GÍÝC4 8L$šBøoän#Ä„O°ŽF\C!äŸnÀƒ
+_‹WeŠÇ‚€âì})2€ÀC1ÁÕÙ-„/RNçrˆÐN]Ÿ…ì{
+RÈ_ óÝxbñ>N½†…ðÝ0ü…e#d*ºŽ§­ÎŒNVœm¯+hcð#ó<B±É¶S¹]:œÅ5FðˆŠˆêæz½¿jðïÇuB™‘g«ÏÓN›8‚”Žn[%xŽ ¿„S-Uç&£%’ÀÓRKÓ +Bˆ()ÚVâ‰Á´1ø z­Ã.YÙØ:ƒmø=\Ö²„T#£oY’£Í¡Ú:„øV¶#"irã7ë6iU¸9ëáÈ
+ɤ(‘¼ô§'º'iÚX3v‚Ë0^ÕHD½
+hBáF¿’’ŠŸò Td±
+ÖàØMRQ;¹6Ÿ“´ìäÚ|ÆV\Á?*ý#I?¦L|;3ËÊÂpMÚíOã$ M™pýŒc(£‚…ÛÓe" ÚЯ´e+¨¶t£†ƒâß÷ÝômÅ^AH¸’à*bz‡p %ŽDѨé dîJÅif¼™t×Ôbv=gyųyFF4–•0YZ0„vJ³¸à¦äU,d0 M,¾œ6é@ÉCh&_‚ ñ³aêéC´ZDÓ²â¨a†€q1x˜Æ.ÇâÙgø¦nX“GÞ€UYuƒ +UbåS_ŒÌš ð€’â \FÖ4<Jƒ6$:‡mÜ~a¤¹vY ºÍˆPjÖbÍñ!žÃzâþ²qï;èb½ûEjJ² 5}HMï ™ºp'‰yá;ˆ™¬T­Ã€^!˜VØrV¢€¼ à ¤!éâ8l×õ;ÙF¶‘á¾Ä$w›‡EÞx‡ýá¨ùE·a‰sþ¶™ed Ž¼c5˜~v•è/R=GÅ…xêºúxÕ5COq,î‡ï ‰ DÅÆöOïbê£6ñ qPÞ÷¨xUf*¢õ÷™lHQ”÷ÂVSÁTö˜#–â‚B!pØ´~úŽ–­à²ÊþR,&ù„H‘ùû¸ôÜ [ƒ[[áXh.ý¦6}”‚éC2ÔñÌsІ<@W9ß¿‚Þ¬9ù”²ëQiƒˆbžNÊo©JD¸}y'¡a¬{2ÆY‚BÏq¼þ¦î&’ŠæzÅïM-xTAsÕjšQ˜Q0 x‰}h”¬A(‚zŽÿ¤‹xeKÎ]t}†6iW@PÕÅÌâ†}¬&i—Õ¨Ðôr$ŽûõvÄβÐi•ÀF£|Ù…
+ïÄõ(ùqŸ]šÂgóÁCã¹CáñçeB[³<õx܇'á7µeËj†å;šÅa~'tJR؉oóñ“ªj¡|4æ $Hç\º£V· E2Æ "5È ?ñ+£Tón
+Ÿp+1¨»*ès&,&Ú£Š ãÃ$RûŒHH
+ªù5J,úÞ ?#ÜM©ðñ“2Ó†þ#y_¿3¾¶ÔtàÃÚqŠ/D¤²äe!ŸØäVþ*nÅþ³¬r©×LuQ±}§s4öqaÐ] °2®•2ÌÊjªS;º «Ãµ5ŽÂÏ@‹¸ó}¶ÂÖ$kfz±ï°[˜ ô³ù¤n xV0…4ü( „ìNÏ׆1œè8ƒ…*qÓÕa رÄwH¥¹Û®-ÿ 7ãÑ0µ·
+cå\qÕææa
+mÜKö¢¯È¨ 
+ršÓ ,gÚP]¯giè[•€±Œ5b¯
+›ÚÖcRŸ7 ˆž@*úü„§c´qò‚ȔⱨP Ö4ÙŒUyç $½ZÚ}ƒþ ë°Ö8–N“q&TÅm­á¨?¬4¶„)€áçy•Ä•c‹Oy
+5™2œˆÐ8îõÙ/
+‚ Ð
+æv3ø=¯øh¤JŽÎÆÐ?bë"`½„ÉE´²b Í!ê£Ìƒæ[Ž0ˆ8G©Euˆf@f˜´K‘ôëÆ\òöK} ‚à]Û#amÌ<Äeà
+îéßa"h:™åˆèS΂—)}®G¹'®¿ï†Û@kWŒ
+IAõeIÿS«½ÆÒ2MWëìâå“Þªw=–‘”9Y³¦‘Œvf c,ÇäíäÊÈ2ÿãqŸoæóF`¼‹ÈÅ°2ËÌVSöyHù©~‡N7§ÊJE#Ђ¤HðÊʯKÜ>™ŽÁNëþ>8l4„Ásb… ôhù£H4²U¼kÆZ±àJ¨bOït¢@GaJ¾¬ƒ+[IA™Â’û -”Ö›FÜÓ:Î
+ôá;/‚?Í6q;v±­«oxÉ{~zÆà0…ò=1Y>3-h­S>ú¸¹´É½^Üе|ÿc™ä¬ tÞ»É û4¥¿
+sÕ/ˆ¿l,DÞ„+˜`^0Bä%ɦ|ÙŒˆ!äR
+¬@©ÁçÂtfÕ÷»ú9P0€ DêSÊ×ä |>×Tød£&s×Õ‹.æ§H/'-4MÊ& 9E‰í†!Své·ÙÜ_Ä‘i.ÛyE AÞÀTò¿LgR
+ÔÃáÊ”Ò!˜zWC ãàIC©ˆD#Íüq7G;N™yÞ¸:Ø)ç8T 3Í 23y–iÒpHìbÞ'_âJù•iÙ§8³¦,¤®Hé¿ÍÚ{u2dIÒ¸*³¹c.÷‚x(èpÞ~Aø\Òø^ˆ«{Ÿ„ÁÄ×%Cës)eMªç:Wé3cÊ"9+Ú3´Ö#A|‡øI¯‹tùNµ%/ÀÊ÷'q‘´±\¥›ñ¯
+H‰Œ—KŽc·DWÐ{¨ <™L~rÜz<*ïúN¼….é
+(À@[ªÉüEDšYµO«%>þùå9e4‹’½Ì¬ÿ‹j|sÖècûðþX³Œšuô©¥‹QÛÌ™½Õ^{/£¯SÊ#ÇL¯5jº¯«ÆcŒž5ûp/¨Wµ­Å:g”ÚZz´Z}Cæðnµe”دéÑܽÎbíœ2yN™anuœ«ÚlÙûô°Ù6hx›}”ŒcÓÂF‹é™å@zpYc4N^ÓjŽjÒÈŠ×Ösƹ)H•ìÁ;7&FÖÉû¨s$·yw·´ !Ù lWÅPy݆þ·xF¶+® 8­Ï rªG²œôí ”îÒr”ˆó3|ÌÚÞ@ŠÕ6ëh×UßßãóÁ5æÔ2²ô¿ý ¬>rZiNÖÂU beNoÅÉÓÇ· þ -‚ŒqþœEš¥«¨YVzºîó'oÚO9KuËš›$õò®ÄÔÚ‹€7˜¿o1FÆÿ2ü1ŒÖc¢¼ŽúcÐëm/ÁýóZ;
+=ÞFé‘n?1|ÎÜñdžßßåèóÙVMìã/P÷·Ì“­ÎËÑ v²˜¶
+s²B³ôÊ´NÍ.êI<­ù0ù®kÑ2dÆÆ‹#@Œ9'à “Á|ÿ¬›`'Žà;ŽÚ ðŒ3+
+2¦2”ÐV=çh&ÅW´ÅØ«â1’{‰Ú­0½$¼ž›z[ÙnÔmcjÌN
+i²ó^‘šqHy NZøÙPÇ,D™c(õªj/S‹šç"J¤ÇdãŸòJöÒIF[çð{8Äðõ”’Fk–î;j‡ü³X¬Ä d_eP üXtÔ›sÊ4Ø”$ׯ«\,<mš²7ó‘–•äu¿ C}Ói\Œ/EI+f'Ç4 J6¤u3£7cæxBÈl•ÇчuCè
+ví§ö»„¨~õê«B‚ êÄ)ŒSZ‹¸ø¡M7æ–n~ED¯NüJŽ=üß[+0½ÝЃt]†&‘ù2ý\ôÄ2Ê/…°yâªge¿¡<fb"$ôÔ¹é™9Õ¸²ÙZ÷{þmÂøUÖúòÇCùåP>&‹Ÿ+¸5,Õá±ÑØœý0>lJ;ÊÎÕùŠYÓ-äªYn排wâ“õQO _m;5œQNË™§E?(8CÚø€Ôö*é¼HBmÏ8§øTÃZN5\,ˆBj(<=u ØtNýb,Ùn®iõØa˜L„ô‡Ÿ¯c
+Z3̦*½!Œö“Þ·6|Ðy­"ÚÖ‚ÌcÂFû*˜%›È‰Žó¹AðŽIÁxt®s°YQñ‰íøX“—ßDP´bÙ€/í²:¼dR;Iï ¤‹`ÝD…'‚¼A”=a¡HÙš‘Y‚7¨R'ÒÇÖïrº3®Ô_Ï¡´j_fkË‚IcÖ®kÖçrÇÍš¨€$}G>䔡¾çsÕ9™´ýÉ Õ‚/v3㦆øέÔÈáI F§ô~ZŒB{"År'9 c~ØΟp”U!¼¾÷XCè³Zçy1Ÿ 3ŸLÄæ‚N=¹
+ñsÎîò6–9Û¬C/’-Øá
+_ö¤xh•–Õ(´ˆŒµqìsXµš¦¥Û¶¶X4Ç ›m«@›Bwc“¾ËÇÍ1<”9àšÈÓëÔ ‹k[<+÷ÕsHHó2껆mB^ê¹ —ìüHVù¬y¬r‡xã±E¨@+XfH¡JÇÐbån¤ŒâêTBW´KHŒË„–¸ÊÉ‚@ ÚØs¤–ºéX%
+Gœô;3»!2¤ø=:ó2ˆ+þÁHƹŠFG
+>eYö)°¬°»]K™Ñøü„†Úµáð€Œ~Ö…Â|h01/—]À4jäI׺i@š|Àìã‰vo9œËó©y ¨#]EÓÇßØ—GÀPçA›@ê—by²¹cµ FÕ—rÙ1ûì ´ òFÐ @p‘ÌøçÐBÆëj«gÈgêJI°ºýÍ!¤¾ó:&É,:%ÅK›å.HÔ@RÎs)|ÀTN»ßª¸ræÖò˜}’O6ÏsC9xA¿ã£›ìå1²åÁ¸@ ´BCô}•Xƒ0ÑɆ»”(7Ú¬RãŽ0˜Ìn^’¹ I;ã±Â±M™™­¼|æK¬âµ¨è«Ëï–v+u@Lk‡8ËÇÅøOVþ;æó†,Ñœ*p¿ÝxMRo‡xDZ
+7u¬O²•_ªÊvËÎèWàÄ¥}êk?¡näÆ{\¾•—Žeït˜«ØÍʵr%åÅSIâ€d¨ØTÓ®-nä7¸Ý<•ÂéÐùl3ËXßCjaÂP9±sÕ$l…@ëé>$´p†íä­Ô8‰*„Á™À*Ïœˆ ¹fNQÄkZ¾M®ª‰Uì…oC6>o@Ÿ$ši<´\¡ãµØU4S>×e”Ž —Ò]4ñ”Að0©é½¦I’S 1};ôWSp~PCo«Iï rt“þC4ó $Ð{R1ä¯Ð¿ƒ$Ï%‹ÔÛ¤uŸo@ÞµiÈ™øŽ ‡>–Šû%ó¡4©Y?ŒBJ
+¬À´+¤?¿ýûË>þBªª¬^“ᩱö29
+ÂèšuØöã¿7 *»¢E±Íÿ3^æH–7]Aí¡l9¦‚2ÛÕ
+’Å6µ +Äÿò[UŸù¸¸Ca·ÆfRŒy¬°SZOow™^2’΄‡k‹¸¶h•  “NŠÈ<xsh¦ÅU<©Úº¬Ú9DT 2Ûœã*8…@dô#+!iÔJ÷‡ÁK%(\éîðm…7H±`ÅYN‘~ãò¿lçÀÑ‹gsû±J8%‘Òá¯2‹^°§iõ»›Ró%À<‰`5H»â–ßÔ´F Ñwãª~h,H…P"éAvT0ž^0ˆKûù,ºD }JOÅÑÕS$wÅÒâ¯üs&‰u‰êK 㫇"zX¼ëXièvµð´ø4¢-n~ä3NEYþƒM oœùf9m£Ü'¸d|›„~ˆCÖ­ä¤Â3ÀˆŽ0  I"hÿ!K~øÓŠ€È q¨aЊÒ~þfÁ ›uà3‰JÇÈTuT¡³ˆº)áó€öbî n@ÀEf|ìpíÜ·¾°Á¤Òl#9•TdW æ©I%¬)Ÿ-ÍÌV„™z„Ãß¡PìÄèq•ì=tJM
+èòZß´,ËPKÊŠÖŽäP~±zé^“3½w:cÁ"gsÞ~_Œé&Lçú çø¡!$ä£×<X´ö~SÂ…].#sž›^‹ [Ü%Ï`ÓýR¥?aw0ž£o>ª™`µ‰aÜ“5à­<^ö%FA‹Øƒl2-5™>UA B–+Ry¬ha¢¸8‡‚ë¼Ä”ŠªI5ù¯(êˆ
+˜L3ûUž
+¶x¨<Q:8dÁEË1†%‘¥YbÇ(™Âuн}¯K…- |>éì{R³y?XЂƒG_¶µ_®ÔÅúò°?oEâ^Û==ã„%Ê ÈDfÙòï;eË„õKV?+ƒt[.‘˜¨™§!>ú*¦ã£QdáÅÉüà%ø´iÇ ™$®‚‚ †b"õ +)%îêJ‚¢M3q“Üảñ±¨&£ïÓ%™ƒã°–=®bz>¡zI2_‡yC^£9¤ˆ?õ#mD ¢ƒ â±i ‹5.ÃukŠ–¶ì߈Q´Ç}ÜGFJ:ðFp ½*m èýqƒÇ¯
+ ÏGã¬MºÀt¦®bF‰%E,ò¾dýXZç$ÅEÉþÄY#·”X·ÌŸ/.Ö1ÅÁ 
+¡mŒ Í\2/°« ,Ú'¤À2ú"õ@<äc— ‡KIêó„ |ø”:}’?KU™aè1« ¦B±¨`êÓó'?‰6¼7?íB
+QGy x6Xš\Ìפmâe^‰˜õq•ck›šf4ïpiˆ!£'¥ãäÌðÄeä'Ñqj©MŠm :È!Î8«‡Rðï%t‹5„LÐ s¥Vb¬±üë Pqvµ› ,æ„ïY go‘7 ÃÊ­h ‘€ˆÄ®a#oú¨îe,Úàtäÿh‰ÚEgxk€¢€½!_Â=¼…FU:ÑQ¶g‡X€ÀJŒ"J† Nê²RÁðr‘
+;îÂD6Í
+s†gíñÿBW¯÷\¼Ïøû•á÷W´µ‰à%ÆIße à¦=.EV¤Ñ]ùáïs
+Éв?]!d¥Å(aºmœ %žNƒÛ™]má†q!`Ú]Û‰€õ(&”˜Ï(Á‚ØËSw"ƒeNE${)ñ«8VuäœSH¨d—•[Xúð¯Â\s&¡
+½sÉø*Û8—³Ý<ãî/ýåºkÜ}~6q£Š~HôæõñXª5h=ÊÄuošˆ9cA0ÔK ‹"±8oåp“(
+Ad.“M´P Û…­³’²Tf¯ƒŒ"fMʬ3®jâ¾ÍÆ+7C?€iuû #èkùÀÇ`¶09¼‰Q{‰Ub9â‚vü/æj ©\ß·¥‘‡–‘ûhI=Œ^øe÷ŠÎ_«¢ç—¼ç^—Ô*4y}F‡¸ªœij£‡‚ÏLýÄX6r“+°Q ¼Óî³Å1¢fV†ªÝoÖV%ŠFX VgÞ­­øM[ iO7>G;¼åú˜ó¥ÈJª\¡oÕü®„Éfþc]JÜÔ‹ÞSÐÊ»sÌñ1Ž§#œ°PnÁÄ|H—¼ƒ,`Òºâ&Í°QVv"È¿Zh NÕoš–QðëÄØÎ ¶LëÞž^ljÊ
+·søÇÌÈ
+S±ÐmOñOé1Š/ÂxSe±ÉL\ƒ#щqÏ0vÄ«)¡£ Rà½Æ$ Â(Æ€Ÿæ^«™™Bb)d[©ð$ÂRaîԽѩ+»“êh6/7ë0ç7h·ÔÍœþˆr¬‚„¢
+^éqñ$¹,/ð{U o€NB#*×äÐÚÝ3@‡ Ûeb²ŠË´ Í—y&81e·Þ墟”t{izß®™Vä…ÔT®Œ(-98”ÜU¬È5*æ4•<‰Fgy¤¸çÎ}K¼YŸ‡±×Š @p¶4Bgz~Y=¢óGŒ%k§qökz`J˜w'YɨǷ B‘ÝØ;ú<uz(5¦È4×_€Ž¢YÛ}
+k»@—Aµûý`×ߨwÃáêkÓ?]ö÷
+¤SÓÏÈçÕ‡Q“ìd¥ãÜ ÒE€d0éžmK»Çò/]ëDÄõ©Ç°^çÏ“r‰à„Ħ/+v3@—öe¾ñ§ Ç<+¿•Ñ‰%Gt…Û>g‚S ,=/*§¹U›Í™@ÓĆÑ/Ÿ¨Ð‡›+ ©Âþ^㇑dêÑÞ5[)+Ô^n$Ç“9Å@™À¢PP¾BÀìŽIçÁu¼@®dv•#çà8øýf!Œ­¦é×ijׄâ<Ú@àNðB CÓBÐY2û|)¬ÎCƹR®ê÷ ”Pìœfgè!ÉØ“´Çgü†#õ`6!O,çËt1)ew>…ùÇùU¬
+•PB5 ³ž dÏÃMWTÜ5’« ʤI•yà‰ä⢀ëR’°È%¡ùµ]„p8“yœ£Gð(NÓHHÂfùŽ
+v@pwI¤¥i†¯Á΄\ƒ:ƒ :š( ¹¤‘›ì€PÏù8e;Ö-}‰uªBbKôªy+Žu?S:Œ&QОÝZFýÎuâ ¥h„:
+v¦S͹)ãÂþ±Y™NÜÝÕ!¢Ì=1ïYLCÞØ(ë,Vê›;á„|äÑ
+˜Dä®Z‰N׊r|°r.V\û€GG¢ „R!§æëÌ\Š&WÙV5
+ ±á•l»'Ó¥Ùرv*ŒÓFŸÙ±7;_ÒvU±M/o€0IúèxØxw&õ óŸÕÑ:Óóçø߶xÜš ‡9­ß7 â°ßôŸjÉÀ(C%• K-ÃäKPª¾ EHoO¢GÜ Ö5æI›€Øë€4Ä=‰z¸,g€öu2S†Ä¡÷1›“PL«÷Ö­)ÓV»! Â*Þ™£¤zQöqŸØ)³-Ò|!z¿$0ëR—IqHà BKxfˆ|TÌÂ$ zOÓ„±—ŒyeÀìºhÜ1ñPyT´wBãa{O8X槡ÐkÌŒˆÿx=±@X‚RT £ƒçN^©ŽwÑʦ÷a]‹>Sn Ȭ& B« ¬O7ë\ ~œælãRgs¶ññÄäÏJýþdd¾^¶ÏCÔ”œÅ'ù\îû’h9f|ð87ö°tWãâ|üCñUÑdn¼O›U½½@ïÞ‡bˆ#xGþ1M‹Q÷ÓüÐ?pC"%bCƒ½Í\hunPJYŠ±@ÿénn; $Þ.e@–ýÁS5‘À&þœ‚¬<—}ê`9âÒè0<iA˜ö/ë}}`ˆ ÛDˆ‰®ÀÅj›t)ûóDówHÕ’©Óe»Øƒ ÛP7"Œtv^û‚$³ÆË$T_ôb~XoLœã9*ä¸Å»«¹¥ëH¸(#˜zzå²8™"Ä3æ›epW.Y!éuàyp µí¾ájS€ë¯ãf*>Žâæd!(ÿQ^6¹‘åF>îàõ,þ3¹4´ÕvNÐðNZúþþ‚?BW½|îÕ!>2322Bã{IÛ×°ÁØ ‘-cƒRq%Û‚¢¶™ØDwËúÒó@QrŒ[a’mêÿO¦f8Ç&»=â…‡÷ví÷!£
+ 8&ÛL†4Œ¡ŒE @ TLK'*r›HÅètdÊAàÇ)HŸ­÷f&v˜Lè º2æÏ ÜÊ—ÙYæŸcŒ=ìr³õAÆÂhóQÞ ÁŽWåHhxä]]}×^Ó§È[SטÀ4âÍ9†Ëb›ÂÉXÝÛ %OCÝ5¯^àR¿‚(W#Üs’ /òϯsÒ–\„"QÄ*%[x U!aÞAˆ1I81²RŸnÖ]­—–ÕfÕ2ÚÇξ
+rêSÙ™¤YîÌLÜ„%tÍAðŸ$Ù§¬ˆ’µ¢â¡õï^õ{‚Xb,î†_ªæ€|£‹ã +6vEDx;!ü‰`Z‡³ò±Qd&x6E#ÎúŒò|»ýCš&3ÅþhàbP
+t`óš~TÌYB?ú‚XàL!{A¸96‹µÏ
+ñ‰ÍNd¢5ÍF@f ©‡)òØ7¨ '»‚’ÒŒ€T;„0EE~5LEÉ0CrØéç6õ) 4òâÎå˜Ã:•8‘ÆÎ]øª‰ìÍ–*.%î)/ЪE¹Áím”\­ËM×ÒÛ™r)a.‹üß„‰Ò£NdŠè/! ³]Rà
+ÿ-“S^;@+‹Aãa'râeø Òµä½Ið™Fäl?ËôèÁ qƒë]Ko+ÛJ\$ðOJúqVrôÒÁXt\1mýÂgçJd«p2ÕÅéæ¥\Pt£â¥: A²x"ÛOÍ÷„¡¹Ha(koúL·F=4fl¡
+"ÓV½1Pƒè‚ܧ•äâ;Ñ ¯3³º»QW|¥è+U Í8»Õ¸ Ájr¢$8âÍ=9-¾!6¢YèæçSq§Öôزێ/ÀLSZpûS8k
+Œ_ÏÇ?æ†`ýcuû, h(ÉeãdžbYiANp ²ÔºsˆŠŽig0ÓÚƒQä쳜îæû…QÅGÉ0ôœCq“z1h¼­9 ¯WX¯ùëofäßÿyûë¿oEZCØöŠ¶i¯W
+ +LÎ9)*ù°IcðãM‘7`d¤zºQWŠƒÏ”åX¢ M­Èÿà¤xD™ÊVÖ‚ÍK¹»Ü<Dõ,AÈ\Pi#.Hc(P“ò/fH–€ÕÏ–®ÝAüz¦Ééà¢KÿæÌ%–…ܪÞåQøiBT¿&ʧŸSŠð ˜üã͵w†‘Ž"1hø*üV êZ–Ô
+Z\È4 Û»:ký8zœö_ Š ÏÓÓÉ4Ê•QJµÏúô–Îg¦ò¤:‡b¸Y¡4ôõ
+í5(ú ºÂC;FáÑnþÿætHÖS•UçV«k¬ë1šŽR]3äV’Ø4FáiH?ž‹ê2kqkê‚ c°zAäz9¥+§å"xrgæe2특¨ ‹Cþ“¦hI„þ2èZn>×pÿ¦F˜nA: u4’Ws××aò0úù=q©ˆ" ‰/)ÍfˆgÆ„ nâˆk`ƒ¾oÎiï þ”ÿ@4 Üÿ¸<\µíiÔPãd ÄþÐøó¹M(¯B+§µÊZÜŒ¤|ÌC[Ëï)¾°O¸@äZJ“çÞ1ƒ¡n§N¨BΤ }Bø;î mYDW'@‘ú„@CÊÍ9CjŽˆõP÷mFDq`ëL„­2OEû™nHfP:€lköÞ)$ª†tLvx ^Îì`Q/÷¡XZ,;sì¡ ZÖÌ A/ì‰\@,‹ÂÆ~:Õ‹réÖ6¥æÓWD3µyN€vk\~ü7!‹¿áíæù ²¾„U!1 J³›sY8ö„èörs™ÀW"þW3nE˜ÌJŽÄ¼˜³[œ+».%ö ­úõ
+•MGðߧkmòÅ}8ç}߀ô2¨®¬€aa=;H·R!žá›‡§AëêZm¸[VÄèžQÇ‚"„¤ëkǵõŽAV‰¶®À=§**rýb1—ì]¦âªŽÖ]]ý5'gàla2{wkî…:ø+†Ý²[aòЩ7ôÔù¯1€µ—‘×i`®WF|°¾XË}+ËóÓ+½ãopðµî§?W“å2_Vý±§>p¿Î¿ì›ÄíçÄŸ¯Ë9 Ó”ÕÝò³toCÙkfÿWF'ܽ
+²w-w¼DZQéRëȨÆí½Ü@`wˆò¿¶?õ‡ù\>I»Gšåè–bp²€äœå“® Ç)À_ÔÊrþ Ùü¼ùÜ ‰?>¥wlgœ|õ0xGB®”3J£\°1,œ¾-æÞñß²ô)øXÒó¼1D¢PeWÍ\:A¢BDj·1ìpªÕ§"0ÿ$0H$ë±é1èYõˆÍ&dÊë˸¤Y_b£ÑàZ°›sŠ)ÌÄÞœÛ0%P„¬”ƒjœJ?fÞ½^øãÍÑ»Öð J,³Ä܇rᔂrÞž\høü_vŸ~™/ (Î
+Û¯^ÜÌi¤>ƒñâQµùóÞGšÝ“‡ÚWƹJŠh±AÂÈO#Æx8ñ»õÑÓ¯ ‡¸KµYŠñ€.Sá}îÐÓ¤úŒiêñÙïøuÆ€¢ÔæùÈo@Ô²(Ï‘à ¬nîÄjã_öø]PqÕÊ‚˜L-ɨô½3DÔѨHÙõ†"LÔtîèËiV©lêÕÙ[ÝX¯Åeí ‚™ŠÌ·®ÓÿÇx¹$G’ã@ôu àŸkͲnQ[õý·óœ`äŒ"‘]jk«n+A pøGƸé/ì0¹aáY—?ûå•Ü–ña­×CÓ óPMËþ¬–µ¨Â´
+~È ¹ºR¥ëKôNÍy8ìו=Aá:n “Ør®tqîYe 5@Z‰<]4Îo-ÄË9ë!HË ß{bOÀCw°ì %Zž
+Ö>æ+“b›ÛP?™Ìþ­]Dd¥½4@DC¼°$z2̦½)Ýs^`"Ûã|Ýá[Ñ×›"ÆícwFòåcð>`ò8¶‚-zTÈ nÒ‘éHÕáKàØ1œHƒ]K³x%í@.ž!nT-ò”Ûž–Œ@c!øÏ
+Èyžb{ÀˆHÞ?P\ˆ²Õ¨H%>
+F-Z‘ø6Eª¼¹óÛGÁ\$nƒ=9Í!£ò›Ü ™£àg9^‚¥—àî¦DÍÉ»æ7õe?1oTÔUr,Ûó|-ª[þÏ*œ¶çU¡V¾"z¶†=K
+U:̈`Xö¤¶û«÷ZÄ0N(ò%zFíz9¨!­6˜¯Iî,ìŠ÷»ØPi˜ýv°†ÄÀær>ŒËOš4 pÂÁ¬ó©:4Î¥³³…€”æ¬âçðzHèœ9”½¿Êç'*kÿáƒèª ž±QõÌ£ÓèšÑ#«RZȺÃÞs Ñ«E¿ß!AFåºöТ¢,û:Þéc— \Cr–x •\:®[}‡W¤U–KB’!ì q2œêSñÊwr[ðõ7K˜`EëV™3<X) Bôuž„kGvÆ®€ &*&À\\ˆë#7áëù8žð.†Õ9žÞð]K–òáUÊ0˜mžÌ7¬Ÿ%€WÉ¿Î\ðHà†Y1Â2¸y ÿÿì[éü»
+7Æö ÚðkJ“ýç­­úËÓ–2ŽË]C«"Ôt$ŸÉ£ÎàV sðc@ê*«û)l<÷'ý,ŸÔ‹²+f1fÙ¾s
+$ä#%Ř^Îaº¸­Nˆ—_)â2+#õ]RøYÒÒ²çMˆKÓ¡ËûS5f!cèà<\Z€A(¢Û}ýÍ<‹OƒæœßÆí‚1/Qࣈst Ž%e°Î篨D6 Þ–…Ê›Áh!õÒ7®šƒ¢¯C˜¼‚Ã{qnÕß°äve•}“q€YR¹úåR±ë8Á¬ìét
+yÈ‹ˆ&íË:}e0Ä
+°ø@¯½/â¤*l•+œ|ãJoNvÀøliÉ’$BÄš¢!ÅÑÓÊî
+cm¼£JFØå
+à‚Ó,Ÿ/Ý{c|º& )Å~D¶¼Y¤«ô8žÔ­ÄAqŸxpÎ7÷Ûø{}Õ+Œï½ váÞá¿ï”<ƒàF8h¸4è›'šâëSe1WE ¾|íVóõƒý¾2àDs³2®» VREGÊè-ô¶/!¶£rShR­¢jƒê0Lk]Öw7@þŽ”t9læ=Ö[cEш}­oËÌÎ ±8ò,ÆûŠ3‘I’V:]9à›g™ã¡ 1u;þˆ½Eøvp]–íÍ9€0òŽ>‡bO¸1sxIÛë¯õÉÇ\â< ;Ï­¦AÑ-Ö~ýŠ>†¡að
+8ÃÇÉ’âà¸L:(i(¤=uà| íkòЊWÇÃ'é!v ‡ò«<˜B­t­­7%S à ν¾ôï@þ}Û¹­gâÀhÇ
+)<Š‘jâzhûò¤6*×’Q™VCå!¾Ðç çs΋b˜ªÀ/Í=G <ÀM‡Àa/ŸGpÎè%îo+ï¦.r9æé,|Äú
+‹°" <%Ð4÷­Ñ/–V†5/AmÔd½v…6{°å$ $¦ î=sº34™.ƒ•æEÈ‹ ñêœ`ÛoÌ^"·Å
+Ûn¢¹î$ÍŒ²Í"€]‚BK›d1Ê.QöDºâ¼NºŒ¢¿‘PÁ f6«tŸ˶‘÷Ø°=OŠGYƒŠ\zˇ–a‹´8{'¸H‚f?ç`ÒÁa3Lµo(‡T1?{ i)ÚqÉKr g?a¾vÎgƒ8h¢3.ƒjU’î%ˆ}k±µ«û@ÓÀ‚’³~= ‰fc¬oÎÙ¯¢±
+À+°²#l˜"Ý„Æ”Na!ÒÀTÔvÙ"¬oçüòîI ÒNa‘ÂìçÏ¢%}Ë][  ÙÎâÑî¯"önÛ –
+«Ê‰}M½P‰X¸|%Áªðr2]¼3ˆ!‚†<5éöàåáa~˽ÍxŸmbš¥Ç9ƒh©ØÀLæKGq/pšß¢
+Ò’üÏ*ÃÚÒDuùãÍù„ë(N6¹”‘ÊË ü˜^–“Ù 8kè¤$ì”)W:nŸ¸TÊ墠A´*LDhT™Él}\f¡B "Û%ŒbÙ*™Z›¥c#»BÞyžâÜ›ÏõÑPc–™=Æ>yÏ5­±ÿÔ°]¢}œƒÁ{uFêr¡Z ƒgÃT
+(¼†©'kBšÁJ+?íñ'Nq ~Áª­±Fèrg×[Œ„›"»ä†P²—ãæ5Jí•-Z—™ÉŒ‘Š—‚´¶]{Æð;1Nš{‚žâÒ× ˆ‡³;™lF ›÷iøq
+]Bàܧ\Hæ9É%§í'iR‹H+É=§?¡6}Lì]†AUD³õq÷& JmùwŽü^š>%`ÁÜòzomúxó@OmÿºÑ7Bl†ZG{wnL[bżãÉ}8# U‰GfP×ßëÇ®A½Ú¥‡›¿žºÐXþ½Lòô©ò âïÖqŽ’‚ÚRåÝÛ0¢>1eŽ”o^¥AG'‡ëUOÕ9×E%¨;bÆ n Ìñ#Àö§~ØÍi‘š, oç,XZg‰Xh ¡Û\aŒ a°à
+þø7|þÿ¼ýñßée
+ÎÝÀ¤ÉaÅ=`2T
+Ôo2,ËÃtOV©ÊR¤¯pwlÏR–‚ÎQƒÛ¯^3¢µ5V¶NºˆsaF¢EhÄ+YWöŠçÍsŒ™FeËh½ìí‹Hj–JÈx°™ìÈp úî±U¬ÃðA‚
+r
+–¿íë2ÆýYK®½˜÷&´ÁŽlœã˜f'0§lÒºãtœ–†is?¤©zÉoÎqDž Fº¼rÄËñê2R„NHcиª­o¢D:cT¾72FÉ°PÝ$L Rò
+ nâ·MJмžEî»ÄdÒÄssÁ]]¥á>ÕH«ÄhÿŒ™kÅŸŒŽG’‡ašCQ82ÿæŠ ”è
+¹o<£%3@òO3<Þæ7J_X¦Aûœ‹Ð•^"Ìëö I–jhÊx_»"'€C «8åÓæÞå2J~÷Ö‰N¡Õ87]¡íOýõ^jƒ‹²% 6†NQÃvþhЀ3²æp®ï :ä[ß 47Ódß/ù ,¦‡¶æ)ßþç^
+®8ù]ãJ)³´[ÅÌh«H|žC^-‘ŠòáÝÔ¥òÓzù6£oÈ™vÆ–‚óæRÿèØ@ïs/€žXÁ_®'Ðb˜ð08ÈüŠŽ8üµ@QnSn2yâ®r|“ŠÀùV\u' ±~Üǘv_–#¶‚Êñ¿®[òÎÇ5`¨Nµ¹çàÎDüˆ ä±”*FwM^ÌKÌŒ'J±8ã*oMòoèˆkÉ°câªW1|ÌNÑø†Kþò1¬lSR]NŸŠÖ.òiä°íÃÐEŒkDeªM’‚`)ݦœxÖÚÊ¥Pß«íêð]tfŸ¸
+”¤ºê›ŸÂjÖ…‰ü³BØŸÔ³”N™ªfÄ¥µDÍ•L$§n ô­kV bvдj):<e@«Š«RÁj6-,þ2p~|v4žM!|—Sµô¦á„èSƒ‰b©C ¦¤ÖÙ))Za—3W- ˜BŒ‘¨ºdŒ²³€½ªa?~xAïôöç!æ}×SH„3&^Bñ6ƪœ¥s H\Ñ_< Âx‘cò{ ,!ð>µŠ¬§;¢Š$H˜
+¸rÌóðø‰ˆL'd•³f•.ÁI¼ ¯âÇeÎÎquvà‘œ³{!ÿ­ÔïïÌŒÆ+ìñ‚1\¨Êt
+ÿù“JÕÅkÕ.eÀZI8ã´5zL³)(²Bš,v°+Çr÷ÅQ3
+†ÕÅîX®Š‘K5
+õJ§î§åªÃÔªMò[ýshf˜–n‡íæîTÁXø¦8,d],òʺ»§0ûâ×£²ú+ÂÈ¥î—w2œÉ:lò¨‚\ðsìÎ@”˜bFI$l!Ø
+Í\µ]vØ'€M ›â3èV÷?AåE&Éùhö±¤BÖ¢تmGÑæ¹T…Cr:2_.mÝVÔù×N2 $Z0HÒ¼`ƒÁïb&FÅÎgÈ1‹L[Ÿº#¶£•'ª«/jn¨5¾Ûýsh.LYdà/‹‹$8MC«Õ£Œ(!¬±ateŽ*“чݣEÖÚ4ê~TBË Æ´L;aHE¨Ú—|ÞPD Búõ©ûLQ*V ÚV{Íñ߇ӈ·j‘¢š ñ\ðP·*(p̧[ q(sG~Ø6ykÏ}ý¾Ñ±²"Ø6˜+©Ü
+`
+hi´‰æ±ëÚ&ÛÔ“Xê<&Õ´·*"Úꃌ¸¿#éãyòâ‚·Ôº‘²¢/aÝ
+ôtÂE¥-hÖúÔ=5A¾ZKm¼ÏÆ›¡ÂÜéòéê²Zâ^pçœ{×·¹÷žó¦£I Ù‚ÝH«±Ž1@ñVlñßWƒô±Ç rOxRÄüþ\Å,…ê³JˆÓ]Rv‚v¾vÒô—7Z2Á~†@aI?ÞmZQµ”´;E»øäÜ)ÎQ}†YÇü?‚Ø¿
+CíûØAçç¾DëE-½Õ\Ôrà±±IâÆ‹œ¡ec¢®-Ü`ƒ, áÖX kØe| ÕlHÉxeÔ©1¶DGcNu·‰Ï¿ä O‡˜÷’
+ Žìn]eBL T¬6Y;„™ë“‰ÅÆ-„­È¬¤æ½— ‰Wl£[ÃŽÃqU8~MyZY[¨v îĆWEyÓ7¿ 0ÝW6RîVeeÀ­av«.)ßXsŽK‡oAÿ<‘z@äš,ÝÁÚ<hLûF´×Iý§2H.†VŠÒâ~VèÚ)˜ÎÜ-‚®NSÎðÚM:b"8°Ýé·fï9î®B°q ‘O7;‡»¸MkX÷jõuÍ?^ûf@OQðÓÏÎXZÑpf<-£ÅÝ#þU´Ä ÊEóü€n†… RabŒ¡_ÊØ´Ê5R·ö–
+ÑQ—âçfÅBAª LyÐß ©/TŸ´F
+ #)Œ“_Û9ÂVîÃI±îáªÈê† ¤"èæ{eŒ=RÈ›¡iû=ˆ vG%3µqwrüÝ É]º|¬à¸A=¡Zy +—¸@í‘IØ$ Aoh³5Lºä¶(“{SitŠÖ™ß{ü´¿ðñölÉ謽BYò#W-$nƒ ƒKá´|ÁB86˜¿nó²:Ãöš5´ñtNo˜wnu^·éSÊCÆÒÂÐ|Æ9ip¡³ZÄúÓ^eXÕ:®Œúô„§JËêŒ81~ÛîÒ•œæ ýt&· Iq¥åŸtÈÕ¶ã YL¨ƒdKã3ÚXDÿ<m =šæ­sl/ÒPMAŽ.})§¶³”.mÄK;ì½}äÓ‚ôð:€£r•ý¾´8A\)
+åü Zùj>ShÖ««ËåW¹Š\^+É;RHžž—ÌÔ˜mg2
+žÜB¾rsŠÕé40ýÏ`{@a[ 4š¼(¹…ú²à/ Ðš›X
+rSŒ=Uw™°’)¥`Þê+¨ C’O<Q°ŒtÈ«·]ž :]K%úE"]žðBž8ƒžð‚nHK1vÐ/&®º¾ó/
+¼“rPÛAP ‡Ä4Ü{ñ{L)&†|Ÿl–0€=‚è'Â+ö¦shïYŒ[ÈÖ9m{ü®í<Ρ§cæš~ríp /H ´5¥,P}ŸLÜ(þ82þ„¿­›gÀMäæ²ö>äÚ|ˆò–Í.y
+ =ô½üËx¹å6 Ã@ðF…%êy¨Üÿ·C'N`qÕýj@ز¸ÜYÚëU×{ 4wIî‡.q‘*ÿ<6Ewîx ì®Ø)±CÝcÇÂ1͚ƎyÜ Ó/¢pWÜ¡á¸a3 ï0qã…9©Ýù©*üMð}z %fŽCbÇ<@Z)tv¼ÝŠJ*NÇhLî«j숢ˆÏA¾hW.¾aÇ<ÌaÁ£/¯»ã"~y¤Ž¸@ñ˜µ +vD;#v¢*Vêi-Ø‘+w¾Ëø¶¾žŒÅ‹•î(<ªfA}øv[¸Ž9x(é 9e|<Õ x(ò­‡1˜½”kŻǯú¼ì•V`ØE'F–X‚Sž’oüÐ<÷ËÒwâp~ê©À#Õ¼‚GE¨XǸh#ÏflÆ¿‹"Ux]cv³{iOe_Då½`¬èñ1<ŽšØtiÚ±zü €HñäÁŠ ÑŽˆoëà~ÒáBnò9ˆ£2˜,4 äâî =.2 B×PjÝ¡ÇÅ:¦ƒ™Cg‰ž(ù€UÐóm¼>èI|?ŠlŽTÄÈ>tÍ‚6ÿðéÙm[’'J+?5È܇LÏîYGåE9IòKèÅ©+7d… f—Ï'‰’$û=ÌKóF³\¶ís0†´û!7JÜðŽœq ºÙxD‘@ƒ†þ {ï* z82ÞFl…—Í$3⧠öÄ+ ωì‰ ì‰Âð êZÙ#
+z¾ÉøC65hŽ¼Á
+Gú`
+H‰Œ—A’c¹ DOÐw¨ XA‚ ¬{–}‹‰ðªêþ[¿$ùËéËSžq¨”"A ‘HÔâññ/÷þ賎1¬MþóñùË›=ø<-½ÔÖrƒ²Œ>­$ßæ‡ mÔ´^Ìz6AÆ£ÌjÅ[÷ð ¤>rÔ:=¦sÀ äï_uÏêV:GÅ›s̆÷
+ŒO4u¤Uâ±Ñ€”Gfµ1Ë´1|CF)Ñœ¿Y¯ëªò˜}”0k¥] ›Q{q¹ £…GFÑé‚ø#¬F&)òx1²Döj™ÞÎUÏ Æ9mF#µ~ýûWýøë—[<š{Î H¯cåÑÌÍÈKñ¯êµt2BFÜ€Ü?z+$ó”Ì[æh­ž¸ QJ#±DimCZ¯ØkíŠPúÌhY#º$ øͺ)„bÉ;b¶s ß*6[_jœÐ¨j=á–j1­SÇ2>~ÿºÁ8u7øV³·ê¤HÁÌ>K%¯”²Ý€>È4{1j[o@‚Ôä»ÙrŽy.#M#úœ³ä\ÂOFŠ‰ãTnÀ¸1WUm>»«)Üڹɉ­%o g‚¬ˆGõNmHçPêW²Ï||;šM³sÕ3ˆV±Î¾Î ’jQ©òw¥(@üB_lHäèFî{=Óà¾~ÓÛ¸^ýÊT«–y‡‹Èhîr’>
+£ºÖ©¶Ýˆ¼ j+Ò<}^sÀˆÂç*}½…ø®B”ž=/¢¾‚º¨ŽX tsc>Ô‹JüÕ}‘RDul.CÒh$fô­—Ùi6^ ¡ÆÕp¦ŒÖ‰â€È-]‘¹¾ Á\`®Aú±!=&- .
+•³ÑB1híðÈ­8=aþÜcÔ¯)),>–6½`ì‘RS¾'a›¤t4–¸îæ2xÌ A˜;ô_ç°Zb’³¾ÒzŠär£Ö¥I°ÜçAH¦ÚTî÷»!675ìÄÒ5 ú3„fâ †¾m|‚ì›èÓZ¥‹-Û›sÐFssZ£y8˜uëd¦š3lj†FEÁ!.¼:†èÕÏã(»‰f“‹²¹uìC+ÏM©AºbKÓ ¢):Fq3ö,š‹6° )µyú¼©˜†}¡µšmkÆÃþ¤t'J5µ Í;…(䧚Lv\f{pî´3Õ¶@?7Ö£ó…†Ø7Á¢¤ã¡ã<ùCÕ¨\%Â+5´Sʵu?Ù£{‘’^ÕF§šòB]=j‚iSoÓÒrom´áo(í@1Á榖ºñCý¶²=AŽÒZ%†näý9Œ ³HRzŵÀq¯h’ÙdT±i~½Û)ˆ/ZlQϲR$w.Z ïŒa’Ç°°uÎÄñ0™W1§z‡Ñˆo2+Ÿöå‘›ŠDeã©„[(žA˜!î2KéüW 6¨‘ue?œG‡z¼´Ë¼ÑLÒkÑ 6$0PÍ"pZg40ű{ôšÛ‰æô¿Ãõóø¿ÞÔ–Ÿà¼^Çåt ÉoxÁ4}ÝcðxÓüo5  &qá†Ç1qj¡ß”ÙÄ­VY|Äà¼_¢I2êØMakzÓÚZLˆÌ®ak8Ú…tm—U>™Æ˜!OC£H{ 1¬$Sv¶Ý@tUcÈR™Ñi²ñæ9Yn¤ÓãÎY B5/$-â ~!*d!õ‘±*Æe‹L´ü¨œÌ7þšiÂP ùòvšiÕLþBþõîåOäç ¾œóZ“-jÕÛzÚC»C9Œ<W=ó¢©Ûå¾Â¶sx!Ïhˆ:Éíjr¡HBþþ‹ÿ|[SvÑhZj8Ão¶¶¯7t¥ B>a¢#%,¥ßJK]Z¨­ÆX.Ž@§i,^ù©2²²¦ýý^yì‘\TÙ>ùES¡[!צœý²ùNΡ3£ßôrŒüϘÚA‰úD\0Cì’¿å;á­)Š¸Àú7|/L6²‰D2ìærV¾ä±Ðýècý1èEÇÖuò3lz£ŒñT1F×O7nùô`…Ó†m‘×¢ áZ˜ÁW"g<ŽÜm+÷bð_«q³(¶ôŽJÐñæ-Húˆý^  \„-ù¡„ê–F¯y„ŽÍ0šSGö´uU}`‹°à¡au†Ö^D-d?îϺj²v¤Èã„ Ã|Á¦2¨ÏUÿ¿¹Ô†Øò%l,¬iõž'ªªÝrõ ^Ѐ^{3ß?ˆÑŽ–Jƒ\íSÕ;H4N¬ÈÉ:LÂw´"ç†!á½· ¦—AΉ$QŸ5†îÚ§`a1~-q´7½± ÎÆħZê‘ÃÖQ|ªafµÏ+è†Ï…Î Š
+3ñu?qN¹Aþì‹ôw ­+8k„kŒ¶Z$ÎóÛeÊ9Ž¬Ð ÇJ"Lè“ùäã2ÅÔrq°+…@èbòU;ã;Ù+³ËKÆîJñ<™€ÀmbR”°Û #NsÛì؆7çh‘Cï0vƗȲãÖv4´ÝD–~ AÖ"í´4ïê.(ˆ‡©Ì0ñÏÄ¥ÓØLí/Beà`ˆ®FýÀÙÌaÛvE4†ù¹G;>´~CEŒÐkÍI¼"àþcÐS~þ¤M¿MdEuƒÎr‡ñ¸<Y`â ß®íkØÖ<ô“šß€!EÈW`wÛA°FÚ>E´A^0ИêxÔŒ†WžjÅ“®¢Œ‡æiaapí™[ò†
+ŽäcÇó •çÛÌÎUÏ þ‚“AìPOm¹Š†¬‚`d~×
+3Ç4çÂŽ05æ ËßWñPÈíy]ň‘Ö]QþÀ`Gÿ˜ ^ß0ð˜üûr’TµcäˆsÜ5Úצ;âF®Í1M1úìmÓ
+&¯#Øs¤T#ä 7„!7»|ѵˆÈŸó¯i1º8_³Žä×ûå³Ñ+Uv³ƒiÕ$¶†‰¸3UËð0ìæ¶@¯æEõÈF÷tŒÙ^A_Îq=“FÒÎû½¥bPeô]‹©ƒžPÇcÉ´˜
+fhÝU…v¤!Ç÷ÌvãÛq­˜˜s ÜêTëSüz ñ] {O;W½‚ºØNÇÚU ÜUƒ!WR))¬î "ãDŸâécl%OºM¾…"«-—ZÔhÁ"Ž¹Ñú.„+ ÉK9á?Œ—9v¤G„O ‹ÈàË}1çÉ¥«èWmÎýç
+/:|IA*Ý/º”Ü ê©èóM‘äq‹SèÁk2&V¥
+¸C›—Á ’eÐƵÞàâ'ø:6R¥Ê:à ÜÔr°C¯Ä*/ù»zY›ëÔ˜[Ÿ$V/Ác¥Øî—b* &ªÇiclŠ’
+0¹Š9ö±Œk"wö~Š’Y–f:¡>sœÌªx2)Dz{ª
+`vÓ‡g‹•.»Õ+Dj’ÙÎÕjçà:Ùuúœj”ðÍI€”Û‹’Ÿùߦñ°l#î5ß<Ä+ª†Öý/þ
+Ð-Žæ#£¨:¾¯Ô éýª"-ïï…ÂigOfÂ
+^ìzS1Õím»)‡®3“¯§fH—ôìc
+9”8eo¥1V?”Â#l–vÙ™Ø4ÉŠ1–Òê$ùî³
+0 ÷P®˜Án*5¬~)‰‰«Qì)4S¯çà» ⦯ùäY¸m £6”l[³>é윆
+tOø¡x²$:‰WÑêuQµ‡e^n3íÇT+Â.¾ø<„«š!js±Æ).£9ÖƒÍÄÇp¯5/&ˆ~@«,òPÓò/áúè<kLÛ0ìŸö¹¹IdX,v}[¤“òè–W.\ù°"î̧˜N?&ó;[â(£`Á¼Ó/4‡¡ E0[ž¸.tYÍ&s½™7[l“‡|H°Àº8zs—n‰µøÃÙ?̱ßLtåå_ú €Ž£CÂIfì4K¶S‹¹Ùô¡’Lk*bК”Rç¥H%Ø"ü”ƒxGIMÚ{þ¡'*,Z3Ú”w°Çâ7ˆ»CæHEÐ>ö¨âI×)‚. ƒÌ\ýsð^tØNÊoƒçï(!9àÿ\E8%p¶ÃB½5»5XxóÓîóé#4ú¥æáíI¬çÔZ.5ªàf<c…i Á³7ºGó—• ¶Ã‚^u_ÔlÉ–œ~É»#G‡VYtX˜öÀ*XB@6ôÐò.ë,ÉèoJàKäN$\óùÒSsàòt¡«i~cŸ‹¬î—<£â'è:ÞQÓ Yèˆwôç7üð"º„@÷¸„+I€ÝOJeÎøU 6–ÞsͶ.G$ä‹>®|Þ¿(à—ð™í\Ñ.JÌч}XºæòóÄæ·=•èSSxVÆ]
+…×sæ</oR­¸ œ•ï¼Ë*øÛ½±|#ºº„•ÒâKr<‚˜qŠ°þ`:÷°c`9¾•Ôå3SÒ!v’ ¶u->´m@T“U/½‘«ê|lP\6á ìù>kÃyt2UI×™ô\FÛ]uérÄ¿ÙãK¢¥‰³La«‡ä{rÖðrÑ´€,ÞÖfÂ6ð+s_Jî„ñTôù¦H«. uÅãV„)gef¾‚åø²Ì¸JH·RÑR¸ÌŽ®g®$.àà6ö2Xbóå(O˜$#2Öfú"?qúa4‰ÿªÅ‘>ÐÙÁÉ=å×
+ÿNK ×lû2ï§ð‘*Ýâ¨ÐlcÂâô=ÕkIµ°•é¯à@'›]
+Îpê!ÅçTô¬‘òÀEø­Ã2¿,ǽâ¯+ù½³Qƒ&Ä~Ù"ì8Ésm*¼éH3Â):fT aéñ)±%v/±ãÂÇÜü
+í‘oL
+œæ-Š¸jY‹æ%(³Ú"
+z I› ºpD:RŒU‘?öÂ#ºŽcXÅ©¼Àév©Tˬïy"LO'v¶·HeP­) ÍÞä´Î§~Ð+I[ù P ²»#ùƦŸ^TÙƬ}œ‡ßRŒ_g°ìG)K=”y‘7Æš-â’7• ƒ ±Boã]I(ÙòWØbO$6z[²1"Z]cEJïÊtM´\W½4´hqò+¹ëéPj—Hz‡*[†»ƒ¤»KÒ³èáˆíÒìxâ2ô6òÖq{+ ó²Aù4ñY$a7šÂ­wÕ¿$O^Š¿
+õCwT@IY
+¼¡ÛLñ,ÇVBV!ˆ4²î¼Îm+xÀ4¬ƒQyMR v´e0; n=¡ná=Ê…bWô¨‚Žla 2(/¤­¿‹ ø!dD²uiË œ?*”fÅÙt†Õ\fÏ…96‰õä€3‡±eéÀ~/A‰7™þJˆ‰£ÃsžtËmà[⽆´Ýù‰,íðggj°9‹0éþ¡¥Iþæ"² *(欕<¾‰·×ÔÛ[¤3t %ƒPRbΧ~P¬ÿÜ\ÒÊahr^ãê5´¢´–©›¸%8~l¶»Q[Pd%èÚ’Iè×`KMœŠ4$ƒbÒæ¢ì
+uŒ}xñ-ÞݵE*¡ 0ICm˱}òêŬXk‰ 4ûìv ©¢+ÓÁ†WŠæÖ0úvÜ£›SU.ãVÁÿ’RIÕ+#v0™)~Q§Bͤ ±÷ùÒ­ˆIÐÄ=ºú?üÆüÕŸXW°ø^¾lVz­sšUÞמÝCQ‚-ª†›¥R%<¨N¡/Ÿ’Åßð¿ëR¸Õž×† ǵ%É)ZÚ0ÓùPÓ>‰“×ô·Ñ³b^§µˆ;‚=9ˆ"5”s‡\F è.ð&4Ô¯ÛÀ@ÝËVAóªL2­9)8‰Eàö¥•¥]q‰£ÕÓbv‘‚JÏ5ÒÅäøƒ9úµÆx7DÐ\aIsrÙš‰ëdXÔM¤‰ÇjNºLUvŽ¸8È£!;©6z$ƒ8çì<&j`¶0ëì§}uàÜ:W$zyÑØ0ϔޟպcq`=·Ü‹Ûø‹xCg_ŸúVSÆ­èï‹"­ú|AæíãEÑ·4ä<׌
+0iBIó&ßÑ6Íf
+׋˜ÓBWÒN5,¡yÚ¸)#DjúãàšrdI¶¦EaÏE€¤uG2IŒ·ñ.öÈK0‘¸óÉkr²ü4Fí_ÆË9®#¢'À`Ó˜è}1”IW'`È"`êþzYÝ þ/QÀ0ÙKuVf íÊdJÅ6¬>ŽT’Ñÿ”Ùrß …$@ó,ië@ÔˆD—• osÎÊÿ!ÞÈöÓ•¿Û:§‹tlÖ,{£û‘f1#Ž–tSÁÔ{ÛƒhÒ2$m®¾†‰H„fBâ®8MÛÅaþ˜,Bº­Ó"å"›|ðÊ2¯_n€g2‚hT…"ß_îL}@ÚЭڬWîs<ÍadÖµŽ?j›GúÀð=<… ôô·áûdë(è:7þÑ¥­Jïú”$„:PÙõPê Q‹Ødfè>ÍBRM¡]#ÒhÏc ™”BÑ{ÆÛì ´³°…Öhåa$óŒƒè›fHA2é³…TÊɈþ¤l¤ü¼êN\Ž8tÂàïéâ§í•Mëó 2ö#(UlBÇÌ"_Á •*ŽÔ$³nJÇ;¯¹JÀÄ2æ¯ÖAcIB,Éà@¨
+›W9¬Aºæ>M2Þy—xÝ0
+Ö REƒÝÖ.jAFÁÜã™\8dÕwvï}Ë ”˜FJ$JrÃWÙ£[qÈb;Ýu)Ñ€Aþ€¿ýÄ¥Z¦x}/WÂÌñ«²°H$<î`íå€2T’ô1xäõà“ [OxÛ=º´©Oø hµ<)H4ÐÃ¥´ç 9óˆ-‰æÖòˆ„c‘¬ÿj ŒpbÇ"&O~U2PV?áªø]Þ3€ÏÔ ìaù('ª‰Ç~‘†²Vn‹Ú"|ópàBTTá“X2DƇ"ÒÙHÖA‚de£ìAy=Ù8œ¹(¬~¤à#ÐIö²× ‰5ˆDçn„#QÒ¬@´'VêR´ )eo…7"´´µßIO>À”òX[‘å…ƒ.Ýëª5ú Qø | ½Èß´éÞê
+º†&A †¸Ù=ƒ’±„H€È±ù>Ûu6š=âV‡§ŸüßJRvO®ëë5iZº‘¨·âÿ==ˆY…)¨kîl¨t‹_èªFË»©œ$ðAÅ EZJRíã¤8þ1 e(seÁ*ŠRhQë΋$ÁNl'ЗµÓ u¡!1
+§Õ=HÖùZˤ Šhºrë— æÍðÂXt5R›°Á}k÷Å»xO×ÔêñØ™ƒåì@V±à‰†¾‡9g²j•ùq†à]ðAªWVƒGNA>ëüYÒ¬BcNBaZœG*¡ÎÛ¹ÇÚÉXœåQco…lË4&bâ"ÔdI«âéÜrïtý^âuñ¢v;ëÑ+þÆJö¥\e«’—àЙ›[þ>;1 ’`K{,¢èŒt(¸˜Ô«Hª"úêpÅú¤×‹ ¢O+L÷L„˜xŠEKçÍÐÈ}PT„g«
+—šÝ= üYâRç1n—Š|”!e¤Πv)–$%òѹեÄäÓSí­n ¦Zè O±ºWÿ4<
+ AòW—’¡ ^Ñí\êZô‹mÉÌq@½W¯Ä@` Ë­»ûTÈ"ÅõÉu.¼¹Æ£ßíR?×Æë…KÿØSê¾ ‘f”a¶àzˆ›êGÚS¹U?r~ýðë¥&f¾ÆÈBÄή ÷“Ë¢ Š¦ï/½Á<ý65µâ€~x :˜‹Òüˆ? g»+èí™3iÄ ¯ýûòí?þ¿¾½ôژ⒆7'­N¤Oˆb‹q Qº ö¦!ÇDSФ\•å˜v6‘JÀTò× ¾< R(ÂfkÇÜHºü
+z{æv*K¯+?Êü² )1ùªcì8˜½(1É1–aÞ,ì€~x ¯RD
+Ó±J)Ò ÒI(”“í‚|i3#<BeD¤ã‚ gM#ØP
+DÓ‘xË|øÄ´š§Zq Z‚vÊ’à8ìÈ£ é@ lAhY£á˜}•’LÌXÄDÛGA)=:£9J³7²«ßAJÊtL¿ËïØ^ö¨Jh¼Âe×Õ‘íÄ=L$²/“3M{* NuO†À¸žtÄLGÌVã¢q‘lOè<¯.·'øÒè«ž“PM…æ[I.Äøù {–ÀŠhø93¦o«×¹ïÈi8­P€PÂäá|Sˆ-d\$d¤R ÙZm‘÷f´ i iúR°"^ŒHOµIëv?…Ü"º¶°ŠPm|˜š_iÄ=ŨQc¦É”zD¥²õlüÌHÄ”ÉÛ.¿Uzk(”P|&°"ïÏî ùPPâxœªZóßAº+ád#úÚ ¡É¸±§îÖ :ò÷ÖÍÑR[SË’*EÓË2€íëê04P]o
+á©DaëŸÛ;\Ÿ]ïÙÀËÚ8dr ?Ÿ¡Ï‡¤åö@&¢f©8Ýøî‚èF$‰«TÞŒGËJ<`Uy[\¯Æ8J{¶Jó‚&#£411ív¤¥ù•Q°)ï
+¤|?ðˆ‘wðfaÒ)”uàK´š^)a†TÕ˜‰Ú4HÖ¬4¥ ³;mUÌA åÍ]ÇF¼.Πx›¬Ê—! ÄváRq ¼öºTäoIV-Ðu¶“M­#¥Ðž£Tä®ds`Ð¥xùÑúœ·›‘÷Ù:ÇF]ˆHMp<Ä+o/SÈÌü³‘Œóh>þ%þÈ»8Áyì8§Æí#ñÛN ž·´ì°í
+‘Vãæ$ o‚ö–ò7¾ym$N0à ±MÞEÝɘ„ï6†Ô87àIC9ZR8sûN]28™Bß …ÂîSöcï—ÓQnë ”¬ÉN;“㔸)
+§dÀÔ…Ý[ý¡7? ƒ¨ÞF²IÊ$çáß=çš1k&å¸AGB‚ŠÆW?/ÆûLy[¶¦èvª¦,8·Ö‰Ì$Cî8×Éaïhƒ>€˜«’Ê¢Êgî«Hhj—Íj6[Æ#-J
+Ù~}ú dq,¡Âˆ>A¡5w8ñH\ä ç4¨QÍðnêRx&Nb”0úÜj6¦"Š˜ÒÚ*=h’‰Ýñg93 ²D,ˆZ|V@²N&xJ Na²LkuËpMÁDòC’âéñL¼Á¤€o.HN‰ü&¨ÞBµ{ÑzdERP°¡Nvíò¸kßWG`}Ú·žÄ­rjÂÝ‘°¢Ø)*v«âñÂä œ­• ]iÐÏòaAP‘Iñpvêãëò²Xÿ?ãe’IŽDÑäòg®³—u‹Üªî¿í÷t5ÚÝŠD•
+›*©éí¦}ˆ ûσâód,
+®i1Ò©ûÂɘÊì"pÈs„1Ñ<y·>·2B |‹ý¦0h=¹xDoÔ“±®Ð)J'A|&cÔ˜K(uŒÓ‘RX-X:ò-
+w±DµveüéxÍ%‚ŸEY¥´£˜"_X‚ò ØÌŽËe<œÑ4>l,RÎþ÷Ì(Sk½R`þüŠ@·Ìó‚h.”PEUò­ë2ߤÒÔº¢j‘w¢¿Ö²¿6ë‰ôòZ³]ÜÉ7j!”Ù6µ”/¡…|R`#„SåIÊ@¦<Ø€‰˜Üd4)äДúÒŠ@Ç£…BÔvrxœÓ|ùdÊzµ£OPZè|ö!ÎDÕ®ÛŒy, œŠÌºìß½ ›tL?Ž_ƒ>#À~üÖNQ‹¹XYϽ^cf,qt5”âw°v²óù醼Š7´‰W9ƒµeèkƒX(bAõöíè@ÍÝš”QüÕqÒŒR:²)Øå³÷V!í$ܦ°S X…µÇ0£•§Ê(ª¾ïÓ-nĤ"µÅÏaÒ0ù}l܉L”†Õ!•ºJŸPÝ©=
+ÒCu}7Yµ7ç`|1¿ògçÆ\W ¾émO½¢É|§ž>`HØC\|*×”âÖEÓÈú:±´aò‰-& ÷sh‚IOô‡£
+óuXˆ_„Ì"Í£µIôð­ptŸHÅç$1©Sv]þ;‚‰„Âcðölv’‚Lh^ ‘òà¯^Š[MjÚÎ,‰TM¶!×Î1ý¥$‰ƒÀcf Ì9§ÁbsͶÆÓäC"dV
+™lˆˆGµðœ®¸‡bãg:W†¢LÌY»Õ=£ü@q 98ïM¿ùGôÔmIÞƒí¼´ª©x¬ Läë@`ë™PbýŠ@°TúùÙ éPûöPÜeñïÒ¹6½Ø4Ö£«L3öPCG!ˆDýÖ½»_T‘FU£ºò
+i±N?—o#âx¨<dzP‡Qý¥q]ŽBÒªAþ~Ò«oáExWIJ®ÐFýÒ&òIúz¿ÅG¬'9¯E¤„Söèðõ8¤_= $ZÅÆœ£Õ\¨­Ü[¦9¥ªÈ·ûªT…7£rë=ü\p²k,¿¼JÃ.ž…dŽ.m©,“Àú…Î
+A 2•íôõ â“‘³^
+çÅÁïÔ®ûí`V¤¯‘i¨Wòµ ÃDèï)–‘€ëš§@}§i•à6¤ÞA‹®UÔ Ë·í¨Á mOË
+™bâùþ7)ÛÙtš'cÔÈØXÔºð¬hÕR¨ƒWD}K‡FkËÛêW â (jDÔJâMžÂï=pQHƒ”ÃxÅQ (ÿÚ3Û2ä<Õ*Æ?%I»óJÁ×Ó̪¹ðGQ) Mi;§ÐÉ•T£^õ³ÏÐØ©øfÝøv…ôäÊD-ñ.2>-߉, ½²ÄFáÌ“Á$™Ÿƒ°ë²O›ÑJlNn'3üq|EÃy³³EZ0Ÿ˜Î;èt},FfÊ„¯bîu(À¢¾¯’³î÷QœÓ¤ÔgÞ¬dÔ¦ÈðÒ>5ïã:ÜüvÚáâßÞÈïÍXN¦’YÝÒ.³Ë’ÑKè§t¿x—vmyX(¢Å€³°Ð—\â›x%|9þ<˜
+ÇÔ¤’®™å×ZTÏ>¥uÑ6'Dߢ;I‘+¼ËHdòW‘]
+R"⥾O–Ú*v€W„éQZ’`/-ÝŸ+¦;T€¦ò¿IQ¶ÚwêÀܲ ÉR#U†¥á7%“´S[Ø7fk®D'‰}ÈY[ËQç“ïàb)ÌÓû5ŸL5Q ¦ÁFÚ`1º¸“ÜJ¿²Y¡|•9JqJñÒίö¨¬‹åË6J‚ ù>~n`³:0RZ~–~™½^¹„McqøÆnÁë
+Ñ4Ç+Å«à•!éèä˜Øfnf
+J E.xXñ„ó¢
+e¼$èÝ@Å‹XÅùU ®šÑ†¾Y5¢ÙÇI[AŸ¯°ét)¼…$лüvËUùs[ͬAä
+èˆ$ˆä“É›EÿF±‰ ¬„om’!å^OülØfZ‰^2Ä°½ ­×ŠS‚–]=³]®y¥ž@”A*¶5àwˆŽ¢÷T±\³üwÿºMÃjléuã‡Û4cÆDı@s|Œ_pÖFè»KÓd{æR¶lí\àä—FùQwÐ’ "xð©òvºŽZ¡c·y|TUØ£TX±ŒÂúQÅ©bÒ)tÏNÆs dçƱS
+ÞÇ—zÇÀŸ@‚µh dº¿‚ÂTð”†a¦ÓÇï@U¥¢ŸË³èaˆ© >‚
+JŠûÛyØØE„ÂþÎòòK§~²yùíèà»ÔhÉúÍ.¶ê£¡zÈ&Û—δÉêq“zÀ|œ0ã}a¾šJ4v¿04NZŽ:0b}~¹0µøþBb¤»d¤Ãšh#´·Ïw/DFXN¤Œ#Ÿ/I\¨ 5ò
+š•î¨T$7¹Å…™†‰íPÚÔŠ\k™å$Èê lš“Œ¾ÍlÖÊ Ä.V@ó4¿ ¬bI$Ÿ ý¬1§˜Ô
+ÊåÂûl¤œ™sém‘$sÇ©øP4¥?c˜u€øQ8”Œâ›W}òÍ-è$Jºã6Ì_¡rozÔ}Ô-\6åKù;*«1Ì-@ó]öp>JÎ Œ ûŒ¢
+¶°¨òQ>žÇYpJó“pÏäQÊ;G¼û¡¡¥@–Qª­’Ü:úåë€Pa7¹v¨ÙgØqšÀðø@°RÄõ}„¦h˸ΎÊÀ)Y‰#‘Bã$urãñJ˜äþvâŠn¢„í¢á©!,“FO&k3®£þŸs.ób3FÐ8)wÊE*991©OÐØ¥eöõ$H¦ÒCZÊz²ZÉ ´“A*¼³k³ÓIØT¸K¾K_û]!M‰¹Ç`“ '<F‚
+Ý…~I-VEKq…EFQ”–/Î~ d0üB!Ã*À¸  étIKÞ0¿´Ãz"ÒfLÀÒÑðFâ9o'6ˆÔ ˆ
+ïÄôéŒL—²ûÐbc“ÊÆ]¯"70ÇÜ
+–)L¿Ú£z˜n¬|îDgŽ@5ÔÞ%êÒZÊ|€ølmQHÏ8˜þì;Ø:2A
+D§Ë€`Бú$*~ò&¦Ág³ÎWYXLJ¶Ï¨…olE”râë|¿úJa¸JÚHy»zÐ1@‰ÉªãË#>Ø
+@”\bÅýÆh3¾±ˆUclÐ •-Xåä¬D™,ìµ™¸…„qN~‹l (<éµ™6ˆnJì"$ž¼p…#îÂ[(ƒ/ÿ´–an®¨‚Þ·åó¨\‡&Â%‡ìd=ÔÞ-aÁÛüÇx¹#GvÃPtÚì`Šÿ±R¯ÃÑÌþSŸK€]R7»,G¶Œ~$Üg²Nó’Ao+ƒÂĬû4U4dã ëÒš´rÊdÓkÿŽö `F¼\Ï„m¶6‹?á`M +Ûî¸â¢ê'Þ⌲aċ |.?ˆ P®ÁiËcØâA  îðìÂÍ[ŒóÊz†ù´Fâš"?ðŠ*@[¸)oˆš¶§0‘2©²ì…§’» º|g()#ßanÌE ì!ŠIŒ£: 9”[¢¿Y¾‚ªÕº§)Í]àNÏŠ9í12e§ QÒfÈzÙô[Éç~ÜŠÚ–2S¶Á/oa¸! UWÔͯ‡a9 k¦Kô{W
+»¨S œˆIîGEã·Ò@à ‘ïÞœö³šo øó“.ífÊÄŒ •‡ÓeCÑÒŒê%5¸¿„ŠpôñÔß.%82íé‚ŠòÅǪæ”ç7&h1„“D>[z¨aø¹(PX6bFÁh”à­D¡<ÉÜæ~3Ž”€iàÌßú1ŽÏT~ ƒ‡IV6½¹T•`,Ê)}Ü0æDŒ³Ÿ¹Ãžê¼šëv¬“V¨[‰ƒ–Ì%О‡CÒJ™š&zã}h¬—"Ò‘VŒ=$C5/aŽôÅ8+—$¡ƒ`.¶ ZâK'ìêpÙP
+@QA¶{3–JÆúI½#V’fÔ€HŽ rÇ ^š`.Ïúu¶æ ä›Žÿ£QUØ<‹#ûj¬Vwã£"É+†jÌi¬!‰ük–K W8`€~”ÈKÑe^a›' >
+B¦ÝJ‚(øjØÕ¼Þ|§Íeë06‰ˆ$a.Í)‰´ÈløØ8ä—±’IFbæ8ŠV¢Z˜Ù1#>öM¡šÏ&
+v ’|XžÝ¡Ã®ZÞ¯râÌdÑÖ·wM›L7ã¤b  ½ÏK‘”˜ægÇ$Žc>ã/È¢¤tÝdb OP…á1&ãGI¦L6i V½ÍdAŸ÷‘#Æzx?².M€ùù»’¦ÛÁé&Êüü¸]ð ™ÑŽ©£«½ñkÑ«\_ŽûQÄ W²Ûü±l½IÀq’X’ÁL¼¤\ zuÖ0ɹʮYϿÁ+Ä%›G÷^‹pÕØkd
+€º”þ¦„„¦iVy{”˜)ìï9
+Ô+‘ôà¶"‹ö 5¯@4@
+ÿ:¢5l:†`H1F/EßéàZò•Vî%_ Ê»7}­q«ƒ·À”"»'ß}õÅwQå~ÎÏ
+z–í¾
+B¯úqVÀ—(
+-Rè[
+^ d^•æ¥ÄÑöá\i¶ñæ;;»Âêìʹ ¶Ž8ÅB­Ð^Èp?'A-m7‚®ÅQxóq–¹ÆwD ¤¤=›W oçG†È¸iõ
+W a a)_‹Ox›BQõW±&üIÛ’CªDGpÍù
+ó§?Šç$5
+ì½|‡q*Š²cì{÷–‹ @YSµ+ Düïe ÆZ`1 vª¡2MnÚÀtñ´É<<|¦BÕp&¨È­6²Õ£Uüª6å¥?7ø6@zX諱JCŽIÌþçã†-¿ Äëm6 éTûyzëcKpC•œ
+Šð¶«`‹Ò¸äT•$`Î2ãj9L‰#
+ßë/«JÏпÚa‚È'©kÈB£ 8-è2ýü#
+ªöß•yšq½ aºXWÌ„ĺÙB
+ÍÆ#ílÊ $Y»Ø†î媇'üjp_ °r1Ñ"0ü°«¶2›Æ¥¸€~úô@S¥ÊñÃý¸ Ü™ôõ¸>ùrùôèQÂ+ä÷FÜ vaÏËêYóÇLA`ìÙ(An¸oÊå¦û"×HìÀÚ÷¯›j«²¼£Ùv LSF¬=JÓ«((îá,ÄN&Txë/ßX2Ö.Wá2ÛäÝÌxoô fõ’-JÕº‚ÿdœ¬Ü"®È»@CàÙÐ_ÅxLÑÒÓ_ôá1]‚l³›ð/¢uË{´IE™¾†/¾çFXú‹Ò _ÎmCÚE5¹åF £o)•H&ßs# Ìx‡ïfi©ßr#N±óΚs쮺v‡PqiþcF3‰wáO/àB"x4_$ˆ^¡"ß®î ÖÊDÈL_ù€`‚yÄÛ&«1OF98³óHO€pjä iÖIO-êr™ƒ*±ŠUɭݢȂ¦áûØ2*UÿñízÍÝ1}¨Ñ_ßò÷?jòv«ÿœ~©rÔh¸$ªi6­° ¬†¼®b§Xv$'ŒÀÛ¥…!ânìTq ¢=Dí‹Ä= œL‹*ÃåzC)19k}A&¦£Û²×ï¡™ÆÆÊs1ÃU¬˜øw÷½ôð€t<PV÷«H#ë‹Ñßç«vN’?¦¿ÇU˜&"¦ÌÅŽ¯Â”atÙçlô$‡¨,f2¥Ö·ã¨ü̇aEÀq¨žXì­×k’ì&@ü(±…IÚåÅW†ªÑ7³\p@‰tȬZB"<½ 0ª¬7Ù¯—q.zbºlŒÍì~J%u+ c—Äà8<ÿè"å gî× %ÌÂúÉL0O·jmRs1sî;IYf›§”ÏIµåz{Kå‚ð‹š_ÕÛæ|9¦ã“yßQ$kîª2ý“žSÎs;c¿/Æ8®b\rÖHea²’L¹BÒM0ö!רËÄU°eó‹lvñÑeöu‰¨~Ñ&)²¯йŠé •Õì5¯Rˆ¨e…GÏý«·Ä“&aâ1rñ”înþIP
+keõ°œ×09¨ßZJE)f&¶Ç­ŽBZV%ný\•‡1C³‚â$¦ÓµîkKX‹¯r[.ŸÙ-*}®’ï$ã S ø9âäüº*¡¨LØ"®Æ8óé|4º.ÙJÆ\|ÔµY@ñ&µHý „šè“dÓœFo h ˃Lq›?8KW{°’äÂ1ðˆ#H‘M æň¡,P²œâq*2Lv5&¬oø“bÎåýÆ6ªï‘ú¸Ë11§Óß"ÝÂ’Õrb¡|+2œæhg ÌÐ*”Æ=°v—óy@E >Ø­–<)›ÅظõÏ" Öú’¶yF—Úâ¾]?È ÒFo{uC)%“•®‰©?1YÃØ0”7mFÕ|X3kq[N> ñT½nX†½˜M6
+±Û܇$¡R˜‹jÍ+P~ž¢Q˜Ç)eSbê1pr€ Ê.àÛ|
+þuWßµ#…ë©Y\µ’‚3£ï ¾y( Á8ÎëÜÄ7ã9<áâ¡î!C–€@ˆqŽãܲÌ[õˆ«Ø`•MËqkœ$\’ßÄ[&¥ÁpÕKv2½ãs ý9bj.ñ
+ÝDBD½IŸy?‡*A—[þ`Þß"ÛBà*úú×Oâ½’–ÆùnÔ†à$Þvj¨=è»´ðýò$05­ºâaî ºý544Áf²ê8
+ý°`ð ’*,O˜hÛéŒÒ VEÿ+)¹ZCæ
+…¤JyµXL@IS$¾‰&ˆiðx‘Ø˧b‚Ù¿Åøaÿr@’òÜwzͧP8åLDü‡“šÅC©¨ð¹,?¯²Á{h"T‚ÏÈÛ™/Ãzü„ç0÷¼qbq{@´íÒÂÞÞº =m²¢UÝb»äð'ÕÏû øæX™^¶£ËbD(Ù•óÝÙ´±+„§ŠÄÀODŠr†¾§Ó´&œ5Ú€^Ák!¢jŽýCèÊÕ|¯pâþôå¶#Á.“?†Lr þä|4ÑK'B`~ìÃüùÊmb¹±Ý/»ú8‡Zª«ƒ¿á•ãÁ<¾ÞÈù2ˆ®‚S¸¬Æ"ÀMzŠ;z\ÅfJn‰6ºdMÔ@Îjçà g3
+íc²{¨Â'H¦«ìlU½^†Ý¨2¦Ä ùØ´¿ŠIB
+Ž¦ÿÌð:N~én& "+ÁÒ˜uøUÌ‘,‹¿ÞÎa…ĺI3x^3%Í0Á4Dç[¹ØÏß>!XjH{jwù,ù!…-Vž†ˆG&ÂôæOáo¼C2–ÂÙvlù[Ø~d„óè€l9<‡[8_]â‡7lÿæ
+H‰¬—ÏŽgÅ …Ÿ ïp7‘
+¡Q‚FF·ç;®ÛM¤aõtO÷ïžk—íãcWÞ6ûÚ}îäÇõöOþ֛߫¥íî=»çÅó{˜>ËgËËÛºg†ïÞ[ëm]}ûc'·/¿ÜÆ=2ææyØíÛîf#–·~õq7ÏÜ6‡ó9ŽÖ9/w¿GØ–+ÇWŸã^™±s-úå}óê1Ö\fWýná]¿Ó¶­>=âêa÷Ükr6^ˆÍqæ=V#"θ|òz¬Ílâ ¯~[›D´¦sÆ«§Ý×Û‡¾úõžúwöIœsØØŽ¼»~õÕ‚³Uà‘|ЇO×A“·ÍYöèVÉìw8H'2lýO(¶ô°ÝÔ(l5þöè÷2Ççv·­\à¦Jø6_Ö•9¿c©··q¬
+4" ¤„ U¦²Ä¼ËLÏ ¦RP
+¬l~Q!†­H
+*y™¶… ÃÂzÙbvç°c­'¤Üs±xúxŠ{ÃvÛJüH\wB™ÉS¶›‚‚°„’œ%‰7›À68„ñµ‰`Á d¿£h‡Ñò4ãÞ 2µìc/δ‡ÓADÀC*
++ÈYîñ`-vòÎ1OTZ€ 7ŽÈy‘b,ú¥èæþtÇî7 ÁJ[–bñ¤ë ãî;1NÐ.®ªòŠ²4É”õvÓx¾áѤ ª­Öè–¶>ûSš™·äå4%¦B±¹`<ÙÃüØ·ø¶­«—Ö\jÑA
+ŽÐ‹:§R–š‹]mJ ykì*’¯¹ˆl3hOþ '9 ö~ïIün/%pfãŸD´Áˆ æT’F¬“|Ui–JJTôî†K“"5°êtß@9ÚªÉÉÑä¤ÎþxªùŠò7hBÊ“…k4#€[4£×93ˆðhQ'VÃðfõ\ÀG%ÛÖY E`4¾ÚÏ~’s×ÃØÊ+‘‹¢ÃAHc@@ÚQˆÇ1yì^‚¸_0í›PÄ*ê톈ésRp>7uo»Ú® sB¸Zcµ ÑâeUA-7£BÐt[§@é~P4´8‹ˆâf2i¦ÍÜZJ!DŠutꌲ±|Ì}RØ^¦
+:ã:â;ìäÂi‰QëG©­3x¤¶PÇÊA(5j‘ñ-Veª<
+Íʧ_Z*f,ÒËì8#Bý½…@Ìh75å‡j¥dl>|«ÝX2RcItµ·hÃd¦1&•â¡íÜY9F¥Y1—Õ:iFè‰m©[h" ‰Vƒ[»,ÇÒ.K¿Ãù* ‹iõ‚:\K½kñ‘á×PËLƒ$ âI7ƒ"µ×¼¥uB*:1kuÙ5Ðʺ™CÛP×ý‚$ $Ãí¨ï¤>R_[Ú%¹”Ú†nãú¦Ì°àCÌØ`äã !«•X ãÄpi¨¦6šFDAwÖ£ý¨„Ø@D-Ž#’Ät!yÏõ‡N©žq´XO˜‘LjHY«[ð-‹ˆVÒ/ÓäZzg6ÝNÄ™š”RŽŒ øÌ ÜÉIÛ³ÕB‹$÷‰ €å6×Ê3´å«Öµ#ÐÊ,zýbˆébÔ4nɱXÅ·O Õ©g>uD[d¨›Ž
+Á)éغpQIc…¥èô[Óƒd…„!¨ƒ¤‡Ý…•µ‡£¿d*5[*‚ÿòýÅ›÷¾~÷öûŸúîý¯×_ùè“ϵN†à§×ÿüðþÝOß_Ÿ|ùå›·oùñ?øNØO¯¿€üßüûì_ÿ'Cízóþûå÷_?;¿]?Ï×õ»=ýû×RGý®æe:koÑê«©Æ0h Êÿø€Ž„. ¤?@é±J¢Ùf¹Æb1ëÁ,û.%¬ç¶DIãå²7¤‚Më ͵­î3´Ý#Xèe-X]sÝL‹ü,†Ôê$[ƒ#8-(RYhÁVïlÊ<˜g/ÓuíiÚ
+E?K6µÂβÜ/D_\S…0mí R$?6¦½
+^ÞÒVÔå}càxãʨ¬^„²zÑu*A`óÕ*Óqêa¦Y=LL©"õó:WŽz½ü‚ K\ˆ
+¡{\­9Y!¸m”þ¢B1ì´«ýÆz¹$In#Aô*u‚2€ør­£ès~½ç`VË:Ó¤Yhꩤ
+|Aƒ€á¶1Tžá½qzs¥Qˆdq®­äÁOÏãAX§aÞ½[u!Ä §W‡«%(çj9É
+o‘³Ô¯ÿePk¹L¡»jOy¿ÔYQÌ ¦yD‹xL<ÕÎí®¨çû]^£ãC{›ßp½ÞFÍzÞ"eCîü
+f@²'Uvjþ øHŠËÓÚ$¢qI.C e)Cò‚ˆ
+EÇá…~B³¢wgèErZƃ˜fß¡I‚¾´œˆFÖI2lLÉr3aÀ¿·ÜGìܱ5–
+8V.k¨0š—t/ÎïŸ=Âhì –ÞDó³kì]Yǯ/?öè^úÛjñæ׿ú°t¼KÓÝÿ¯Y¬q…&@ ŒâM“ÏеyÉ!Å4ï‡ÕÃ:d%[œˆBé·5Á
+ ¼¬CyyuJ©ü¬Olð¾ ejæ_áãR/Ï}‰#Qz Œ0 ˆF²•-(£|3ˆcÞ/è^'æNœÊ}v 3‚@µv¢‰%R‚sT9¯“GÝ
+2>(ò {Öã‰æèY¸ƒ€zÒØ5_âÔnU#ˆ{]®A!^Ã
+3(
+{˜Ñ„ÎÇA­òìtj æ8€9ò­Ì”gäÛï~ ]¾ Á= µ°ïp‰ÅXóˆô…õ¤3ñ뛃sóÀ§äE^*¬B¨q&,•åzõï(ì'D ÕB$÷S{JÖ¬wq…gº 3•«¦ßظKó¬2Þ§]‰ ,F)
+ÿ¾N§ù7÷WáT|ì—,Êçx€[@4¡lŠ‹Ë:¼£6ʉo!?Ád|[oßØ < µdÜÈUŸéÎ4U¾^PDro8“
+©[âFDŸË6P{Ý>ýêÀuÛú!!T£è{´ûn§Å1ŸG|{ÙšÍë1@ÍÌEɽ\âÑð{±açÓê4ÐÆäõ}¹ýßά¶^t­{ô²ò­GTI< ÁÙ
+x»®ä’É“%æcÅr™¼ 'XBGì+|R^)sŒGAUÝ];ÊŸ…§f“…wVÁïp¥(Ay:™Ç\÷
+D»ˆÓÞ6D–¥ec÷ó‰ÒºítèŽ#Á½“l0
+èN ÿð…¨´Z+Éæ¥'_¨¡:üåV)ºßmPäwÆÆC.”“,ŽâIžPÆA†\ºhœÃ*—T<oÚ®3¼EñõØ Îé?¡œ@àoŽÜHN$#?Ķ€Cýß:'@]ö¥{x9"Ò¥âu{n¥ú—Ö§ë«ãr,†—c(¡¼˜õå& «\L#Û`”mðl¥œtÂÜqÒ‡&`0F ƒz~ ð­,¾„Ïܯ°ó…‚°8r·Þ§JEØÈØ<Rξimœ\Ì`ÙÃ"r¦ÁÌ<4Þä!‰}!Þ­í,ÐÚS‘(.AÔ4 ÕO®„Ý7ìŽ A‚kÎhhÑØ™æ—$™¼6PjšHXº±(Ì3é·–m9Ì
+üƒGBxÉ!ÍÔœ[@áPrù4 mì·¥öoDœéÐ&+‚ÊÒçÿXçAJµË´¾¦%3íÖm3àEò…ø©œŸ(%#8” >KºÑ¿òà À8‡f\Ú’ÊÇ8¢¨píÚý­ðW%@¾¢z-«Ž`R§.ìü†ë0ô–“ü£{y8 †2QS[§`‰hbÊõзj(Ê1Ù4ƒF¹¾nf}â¤'Áíú`‡†pK®ú~ð¯“Y‚ìõbkŒ¯#+8b³,FÇȉmŒÜÃfOPFL¢¯p]ÓØZäsÏÜŽ37¨~Qã‚Æ
+-z—A) '£\YDHžˆ}ã…Ðà`’->„ØÍDA²¢NÓ؉°­@ c—uM„RžhT}r õ2póý¥®£åb4Øþ½k‚ÞŠb¢R¡
+¼U׿æ—@éjÀ1iúÁ@ÍD‡m¡þ•É§Á•Ä—ˆL=Ýbs÷2è0Ëdùúî3Õ}AªûÒÚúÊÐú9òuq‹ ™r ­ØB[·TctÀ¨ïëYëÄõ`)¾¬&ÍÁ¯#,²}÷h,™ö}L¼2$·Ì\óÛL„~Y^2’Ýc9h(ÝÞýÒç:›Ñô
+Ǧ§šI·æíu×È „¸•—ÄØÉwª¾ˆšo±+/pj—¸_íá‹•„FL=ßû ­ir…ÙC‰raðb¯Y¹ãr8€[ØpP¬SçZ›-DPI‹ª¯{¾Yú4l߯7§ÑÛìA¥ZF¼‡ÇDÜíÌ»Õ~æ>QÈc Ág†ñŽŽÜ¨d ÖÚu}PþMÖ€ ÁqÓ…`Ñtú™?Pq$>é‘ö,=ƒA˜iôÒåUž®?H9ü°aÛk¦7ÜñfuH&ÑbÔŒiåæj¹>ŽÏË~à‡ÑÃ}4·ô… B*5X È“
+9 ” <q»°5ö¬ë™è™âêÒwk4çP£úÊŒcÍ4SŸShÖÐ ò¥v®zÍ(”Yý MG ^&P˜€ëìël&òä:Žö”E7¿­x€‘˜¾JƒÿûÓg£;3P]׈°­\甕ͤ@`î]‡Ü¢çEŒ¼~<Ól±´6Þ²"êÔd,
+® ˆ¦ Eò¬8›g`éjø÷û ;÷åüûçu5:‡Vº‚\æ!ˆî`… Üô.ÈzÂà{ ¨ŽÛر¿)*šB—h$ëïe3ì;ÀŽi
+FÍè„»ð%4w-ûN×'ª7n9aËÕô>¸Ñ¡ Ó!鯉TžˆàµÂå­©˜ ƒìû‰xÅ€¾ê óücô_]ˆ4ïØ1ÔÞÜ1—Ï™¥ïR’½Í<d݉aÖ»º_”{Q6b¢¸fQO¤7=¯XàǬù0-l‰Y Fa‰@³æûãŠmÂÅèQ8„\8µ5 f>xjnÁžå¨yÌ£`câKØœ{ýõ¹_‚§Ñ Px@*5ñ½˜îVú|¢ÿðèóÍG‡ÒŸ—:Ì´Í f;z½úy,º·!ŽdEg"ì÷6 q6ÃÒ;;¸£oD6­­&|½>£Þ¦Žóˆè©Óô*„,aF¸:
+WH®€>°1Íù£Sn
+rv¼Þâ=³ÿ•*IŸbáW¤O­=l~Š]̲%=ésP]Êó‡CnÁ2ê`Š"‚Eà!o¬ûNIÎ=± 'Ržg(ÌðmÆ‘¡ b[ݧo©™›c œ–àPb'âݪhBD1ãNåÇ:¤7
+ûth¥\¨»i;ÉLs[²/Õ ee.¶þº“oÄŒ…¢"³â%Û ô4 Û;©rˆ†rfžé™Q+(“—6y`^êæ!ƒ¼=0Û‰2URч#ÅëKÅDýpFYóÞ$fµB÷*b&µê(¸2S¬1Æ‹IDÙ#¢±¼»iŸm%0×Ù=_ç[t?yèäþ‰€q'EWÄóûÛy—GN n¢òPÂç×8H%c0e»i¦–ѧ½jùD£/Þ°Ñ¿X|ˆæ¼ß/}¡h¯@͹ô$] Bƒ°w°ânåq8Ó…½¬óBUä ›iö
+\¿çÏeDÝcJÏp7øC]æ£ø3® =ž°†QõDYîýV=KNŒ¨½ƒ‚UF¢£âß_ÿ¹ûaËŒU®b„‰4^¸füê”_´pM— ÝÔù~P¯XÊz&|Íèë!*ùè-0šÌœ*yn彪jF=·e^D…)ãöÁ;$Ñ™ñ7½ÃÛÊ™Ü
+ÓaT¶rOW­\ žDÐÁß2š–ó+éce%I£F° ¶_-”‡O eæX ÖõíD¶|@ÊlS
+ü÷ZO¤u2 œ¢zy­!cÅí ·½e@â}S»ªXä
+MÒãª0_ÃÂ=Ô*óa*9ééú?Z3¾³jšºÞÃDƒâbGå5«)ÁÓW£g@œ¸<T—“@Ùûºnö¿¬—K^G†ÿÊ·t"wŸ¾ÃÊNÊ@ÞY–“ ì‰ÌÉÿžç©>ß ö8Ê%»êôµê½tnˆçÁ–±j…‹€­´X•ã7E7§À.Ï.t*Z²Äë})#.œ2Éöès^—6¬:º£Äi-7íGË
+(Zumõ’¤E’Á•h¾¹|z
+q] T9”@Í"¦ˆÈŒ/ÊþâتÆIŒÀ-¬_šé“bÜ>k€}÷äpD|±ïòœ Q•
+@| —¯
+æ*›¡ ˆÎ]òÜ?pr "å™BC#{P·—*c{b@èäš½Çnʪ›Ži§òœ{cS
+²+ꑨ}¶zæŸ6MíW8ŒXe>bU 0†‡¤•Yrî¯é_3`ŒHàl‘±¦\›Aì´ç]Š`pÎóMy…ä¿`È^‰}O³p¥Pª®Ô4¼;ã˜)2ŠECFAð™q´€¹3¡¦z2=fñÆ»æÀe¥
+ß‹É6½ºGög%…¦4p
+çGhóSx¼ú•"ª+lÕˆkûA«©W«Ü,ÃÀrƒ
+s‰*¹ô )дç¦Nì¥~N‚|“) B6 c‡¾–â2^„QÆõ³Å‰ }D
+vAŒÉÝ“±Ð‘ד@ý\ÐGÙ]¡ååq–žENšØhB+6É˜Ê ˆ8¨ìW©ùª‹Ýc· Ô±ÙVODz֕èì$.°ñ^z΃.w¾±XK¬8-H_ªÕ•+fJ‚Œ‘&[84( ÁRÎ䵩:Y™d”S)™oFR— "I\7©ÛÓaŽÊ4c…3#H(ñÚyF+„H6£Ë 6 CF‘°W:îY?ÌÁPº±@ÏXÇÞ'ÊwšaãGFˆ;Š´Ø’Â=‘‘5¨{. ½±ï8ŠvN¥ HpÈh…ioÐRE-æ­"mÔFiÎÔ6vé®Ñ;
+‡XÖs”} Ô–s᪱ûöø×wÚãCœ9·±»aŒ±cfÊà9ôއѴ3@ÉoÙn äí®àÞ©Ñ&•¨ëKÄ«¹Y·¹Àúe¬ÊBÀKÜYÊàÏf¥Lª‰‰k=,‡9> À¹ùÓ(¡Æ¨bo5ìJ•äЛSLDíй°'œà™)´Å·Þm#Pý}PG̵¼xDÙR]ÕÅk"‰i‡Kø2¢‡ä@!´<)”üìw?)wAÞ'1&ÍïC®MÜàPqi±p:»ó–þá’LFg³¥ÈQÎ4‘€l)tøiÏo%“@²Ñ)“Y³;(sû%_KõÎ=ƒ : ÏÄl°Ë•š`2ÉÛK¯)8˜®¯˜¯œº²õÌÕI+‚õòˆ©èâ­ÃóRÉ7A´ºööKº¨XXy¿jòˆêj]É ÄmyªíÊ•Q²\‚v®·áĨ]ösá5‰5’q üÖ¾üø"ÖŠ+Ì/Ÿ55u˜þåö(´ú‰)ØS– œÈ®£áÉ`ô1Õ½ü ¦Þÿ² ¢ãÆ·_©-&¡-Ô¯©¬æ­¬œ|t¦ŠQKI‹À_ICT²–3Ãô!®ÅϤÝÔ刈,60¼ÏÔ¾ž²!õÝc*‘èΗ+5Êt_2ÖÔÔ‚°‰äažƒ4T®ØW¬ƒKê“j®ˆÈìÐg"ýž¯‡Ô_çW¼®
+S[7* L kQè @èƒÈJkJiO ´VÚ±`Zψ#Ò÷åç3‡eLÚ¸9ǵ$!Ó#Ôôgħ8xiÐ(BWËvî¾R§íKº½ )¼ù€Ý´ix›x"+éüÀ7§ª'Cwy‰w+õQ>ºç<¡8‘æ‹…¡ àïKE ö }ú{ß
+¿Æ‘Ê%š
+F 3ÞEÄ=q‡ø¶_ß–‚±_ Ì‹ÝYÁsÕ@Ü ýµPj,hïíúqð:Ðß­ u£fþªkab!Š_©
+]åB5…Ý9qzÉ.ävók,ò9© /ocÐÊs(®Óý5Ò ÅÏåóSC\ê*jˆQj?¢?¦jÙ€=ÿùY—ðá]ŽÛƒ…Êdd§³xúÈ55Ï»jÅü¾<ß,@™…`ÏÂV¯’†ƒ}ym6•ª.òÑÝ€ŸK~¦ã3Ê6Vš#|3ãBh]7ÓH»dÜ3~„ ꮀ¹ŒA(âxßXK(4E^󘱒QhU(;Ó¶Dœ]4qnêt½à2“óÐ2 ã™ü-œt!vârïEp¡¡½ ™·èyÎCŒ• QT¸}<R¢a¬°üQ!œ«-`¬ß³C·'ºñòmŸ‚˜±?ƒõÁ–`ëT
+hÀB
+ÿ_˜lvRv¼ùN}©*Rá§ê[Å"ЋgD– ìÄ]Æ]ýú²…D$<
+Wh-WYRЗ
+EÄÎ7 =Pîï“
+–Ñ4¤ÓåU!J¢ÈÄ°` (ïX´{ü\äeþlP©„\3ÇÎÞgDVäS­<öbÐú :SÇ]˜)Ógïí/Qû‘µ¯ïQýŒÒ]‚ÊÁÎûÂë¾ð•àªYÀDÔõÒŒ *$x‰ÞÎÃg@œÃUEö‚,¥ãÇ.KÝH+óh¯™—uÉ<}A¦(À¯&M/Ñ6Z¯‡¤ÞIbËÙ
+½`Ê
+€Pæx¡#ú£Æ@u5Æe—~¼Q¦%º;>wØÇ)lò÷Ìa/@ù£¹"¢&‚ÑbUe•I™D“—ÕD¢8iFRy¥‹}8À«š|G‚9"F62'…2aZ>"h“bD¯œeAwQpÎ’(zþ¾1 ç]Ð^}FDˆ["À…;¸jzî±i0÷í¼I/OåÚqŸ’²{Ρ6¹l¤i9ŽpW\™^TòV="hÔÌ àpæ@|FUùÓ}È"ï¥rg„1[¸3fÐ]!x˜9
+HgíÛ_‘è ˾aa_ÿ±^.½vEý+g˜ púݽaäà!# #+ œH–ƒ”ÏZUû\"® Šâħk÷³ê«õ•¼®©Ž8 Íb€NÌZ,±V¾Æ#ÂÓ^2⃌³SÕ¬s&jÔ~1@«½Ùæå7ŸßlË:oµ ãÜ*€Á¬û? 8 ½›Å¿0¸›ò2cI@À,#Ž~ªмºû¼Z`±ÌÍ­vôëõàr­f õû¼c µr؇¦»ƒÇ«²r«¨È/zúÀ:qj7Ö¿ ¢öÝ6JäŽç謸^¡«EûD7QRÑek¿Žxº…ºlá$ü¬_šg¸‚âC3¬¡¡w´ì70‘¾õUÄ¿÷“XéU…»íl®ÉŽô«hÒgŠl¥…±à2 #:ðus,œ Qø±°e¸Ú¦àº[ÈItb|÷$¼2?Žk`ÓͦyÃ|±tR%Ý¡{D= "Äsµ5ð-÷:ðj€
+1ÑÿÐ-éÆXJþCU¹
+ˆ-(£ë´8ér¡ƒ ¢IyΛ._ðÐ"Jêš@ÂÄÔq&ƾ*†˜:‚N/ѲñLúN¾ð›¹ÌUÒ¨ùxô/|ÎãGŒ29fÆ„&Ñ.îx¯" ]Ú"t ¬¶¬ª×Av6¾‘áA ƒKÃ!44ë²³@«ÁCÆ¡üì”ö~ÅEÓè]‹yp]“ÈDosi<™nèW‡¦j0¯U(·®Ú=œbù&<_·ða¹,­ÁKìû¢þÆ¿¿ú3”ÇÛïýÿŸâÿßùHzÓ£Ä?ùùÎb,¨Y<göÖ„NË»‚óññŽB°—QÔꊨ}¬õ]œbž~ZÌSOFPƒF°ë™u—…Ð0­géiDR-º(N€ø{ãY£¨L<ŠŠä$‡vOIž‹RW†¹£Eª!j¼qT…u‰þЛt‘&.x¹÷²nÖ2Js-œ eÆç$k3Í´´P>‚Š{Žª'"V·Âdµ•&«ük8¤ÅI切h¢‚›‰àáŠÅlr+ô)Ÿ¥å¾é{ޔcëÇ<ZÒ-ä¸XäÙ³‚r6Øcó"»†U÷xuånZ=”£› %(ñaDÕë±VÁ1=(i
+)Î4oÉÆÄT»”kN!öÝÌéy¢ŠFz•¾x€/ÎÛy¢†ÑÑv=ç½jqª÷ kksêbyóZ› ×€'Ÿ”óan±…d'_T"_¬OøðPj!»’ìX¨±2´A³¼Û?¶0:Õ 5òý£Ë²¨)ZW€ +ñ8±’:ûèpJñˆ\,ÜQ#íB7»ä@‡Âã ƹÇñ¼¸ö&$3æ@þúA¶ŽbØìU6]žš_©FˆØNfÏ@¹ÅõL%§çŽ‰®ˆŽni4ôöòhDèè#úçÕ„îŠÃrA_³ðÆž"Cc¯2¼pÿˆi}I}-#[%QÜ`5j´@Ýæ/F
+ê jÙYb;dAû¬œ¢œpî¡hl¤¨±rþryT†fxϯm -BPÆkdéÐê©ÿ¸+î“L)‘äRdL[­/ªŸtKù#«,:Ð3«µÍIWUÙö×K!ŠãAݲ^S2Ñ.œE©iüV”©ðÂ-Ó~Àíj i'Ñ
+oÊ@³HyÒsÈK×a1ÐestP1 .wp§lbW÷ÂœhyNÓTa)èll’´§b ¢Sót§g5DDÌÖmÄ+èyäG´Ÿu mtDo¯`È’ ÉÝ>ŒßÌé$ÏÜ8ÉéWTÕ‰¤è8°`è–3"A ÚËtÒ“ƒÑê$8¦#Eâ÷q$`î 1ô÷p¡1pqíî¹e<(ê£À÷Tn¢°®ºã+|ˆ›ÓjŽ, =!q„,©5&»˜ã²ŽŽ„t‹›vÊ^¹ë­‘¡oDëŠL•Š¦±´m…ñ¼áàÛ¬—²ÒÆT²r?»
+‘*sÆ4d‚@h*¾c˜¹›yöÌ΂È+i0s¡Ê‰[1—g'{±GÒÙÔå\Ëy—ö¬`†¦¬àm1H" ›E¯tr!­õÒÓØj0VKz_£G¸™*x@ûSD ‘—nÌæˆ(©]H‰¹ÒŠžˆà¡®s¯THo*’(
+Q¨xy*i`„¤VÕ«c»ÀsŽßä‹éY÷ev܇ÕTÕü:BW9[~Íß\ŸK¸ÏQFκ¯•«Žû|6RÖ¢ß<ç‡Gu=(¼âk™< ¯Ó[ËŽ4t—>áŒq¦aÍòéA5e¥=ïi+ÓÑX.Î:+¿ìbµa-xLŽ#É\(ÜÇÙÒ.ÚÖŠt…c¾ÌÍv¼,Å®¨Ä™¨]ÃG¢¢•ˆ¶q’´´%
+ê|çر$Q4NUÌì”R,Œòæh„©" à|!\Ù4#! _|QÔˆ;<GÜ}„?\ù¹Å)U*Ud±È˜Šªy'Ý6§ =úL¡Ûég®Š~¾J±8›z–Å7@]~µŠºÆ…ô
+ÈN²vᶷ`õ\@}·ÛÛ6-¯Å]Ì(ØʱæÀ8ƉdYà*. ÞnÌôôËxb,>èªjr:®©ûœ´ÜùAyrÅ]&νq’òŠ@Ÿ²ö©N†LÞšÇè T³ su¯»íú Hìà,wÕ\iø–ìâ´6¥´>jÈ@|$<¡“C­tK40÷ç®ÛsD`;¨ËÓÚT_§½#) „
+á÷H‚ýŠ·'m­¶iºK‡4Èã‡xóʶK?(4•/Ù!øøï*­/~”ׂîX!#¢<И2ÒEy0Àâ {*÷ÁÆù­Æ®%vuX%(3 pJéËâŒèjú“‘<áìC=ƒž&Ÿ<œ£‹'û„é×†í¤ƒè{h‡¯Vh¾`J«±
+Wõ@ß~dé—#OîÎcFÅN*ôn7» ¾•ä.
+¶F¯«»uÙ+(ˆòª6" -W{àWø9õ Âa0ŠT¡ƒpoýµ„‹º§OÏ"¶ü
+‰•_{Å…Àx¡ž AsÐÈ79šN„5Uº»AŽz#O‘ý}îõj~@“[›yèŸÝРA&´1‹8U»¼!µª‚RÁ%>;w .£dÊ¡>¿¸¦¥(’ÓUäÉ¥kˆzÍÕïH¨…,€dâ¼OŸ>&Ü_Ö€ë Œ¡ª¦Œœ
+A²^ …B8S`ò]w‡ULt¯í]PFº¡&æÿèLdÑz…ø·(q™(äìzûð.Á3ggAÓ¿:”ù|Z]#Êý}’-@Ô:˜ £ŽJÁ(P+÷Qê1WÍ
+Þþ„N‚“Žûè\É@Ód‘ÙT7Çñ›Xä!c*ºc1ž–.‘R>¡'bèiˆXv5“cÇþÛös© ³_ïÏ9I!T{D)²Œ‚Nì3UIk0›yÌHI…Ø™†šW¨‡ö÷;(~ ‚—ꯛ7
+Èá67î£[JÊÖe´ÚÃêQšeÝhÂTM7 ¥Ì‘‰€€n”àaß9Èv:œ(&£õèøxèÆÝwy…q¶- HÊÔô·ÏTÄP.1d1kC•}éÉ”ö0‰ÙaLRg߉ÄBè3½ôŒÆTY¬Wu>ÕÛ+a“æ!>-’=ΣÿpÝäWÛVõ…æK‹’,£z[¢eç*©-GáD
+]]—®Ö—¡#‰ QC ƒ ÿÞçyWïÃ`Ÿ D.çë·«ªW­õ^º<&ªA>UþEF²¶4ˆmnX}‹b›¶j'Œ¤6ZU½ø*È“­iD2!ÇNБ„ÂÌ\.ànW9â>1n´dMÒÌ„Ò^ó5%b…µŸ¸{•a*óe–¼Ë›Ó{Üh\YW§'°‹t ËT9º~àÄŽ±/¶û:ÒmBl'éôš“Sëªó´¿?*—Ç‹˜Þü€üœô ûðƒãf`”¼~Hw¯cȈA,ÈAãÎçCâeLÓpÆ»Ñ\TÈš%ùÄv›&é\µNò"‹ Ý®‘á¿Hz-.Qjcð1çãRoèÓð€LêÌ’ï ^;4åT’Ú<ÑÀm.éç#¿|oÐÒ‹_-áÅYåyì.5.ã%êR:@!ô+¨¡‰íLåö[Oe0zµÌ-›2yd“TnD¼ïÉРyu ‚õì3jÈFèþ
+êÔµƒêÑVªºùŠÂÁ]f5$áö˜’ᜅ@‡/¸–ûÙ  C©Îjk Ú]>ÀÚÍ"µ÷zõ8ðŽEÕ*[ÿÐò¸wólX‡˜]¾à²Ë ¯2*“uc÷±;~¢áZ¢ÝÊ@O*:œe~ˆP3¬3Ñ0&–Ãð'„E p)ÒK9ÙÉOr§è/æ+}ˆ@½î„‹{=;"ÍÁ‰)Ū8sè^i &À¹*VÖ#ê_Ç;$ÇCAØ¢
+ªé\ÎÓ¡äÜ»Þ$ïù¹‰]ƒ±+!pÒÆ´õ‡®±¦0bÃz›&x½ u•ö¼U )(o"£IáÈ;WIçJ».Ò8ŠÅ¢ÉDº£?C·ãYO ¥‚@ÏŸ¤Â¹ "ŠŽî£´µSݵ®R #c Tõ–;(¼S„!ÇÂW@ôÎóqcA Æ|D!Ý]÷tfH·É4)]»‚¨0€—uAcÑ¿:EaFw¡FÞf줢訷›#ÆÛTmb×ÑŒ>`’Î÷y7çêâ’·¯“ÜÔmjAãÜá®eOBÆËDÂ-ô™ncR  æ9ìb+w¼ŠHÿTN÷7­yÿýRÜ`Ô~[×»ñµîÈ1+ªz
+Ò¯§<ªfz\^¬Eâ ÔÓ¤°aë“è
+$°PBˆ’l:µè”~²«°:½
+s ØsAT^,楧 •VŠóKœclÇ©ÆcUýXl¬ÊD†´Ú¤@‰/àÓl׶h§…›ZÛ£ÌÄef€áÐБwêô˜&JæÇãÓå*È› ºjƒA¼²" z“Ë>T>¯k@±¾Ü¤±á’ÎA¬b×a²X/ò-äd‰:_|ÆCíÝ¡›7¶Ay»º[;¦òÁ¬ÉçUê¨Þ¹þᜫ¿ÆB&$–},YßɽÜåµøÿú8D5Ö)ºgL©ë¾”? Oo¡j””@ñx)•oïZöˆHºô+*Ï)÷|Â˃dyŒÇÌiÓØœƒó–Ö,RǤ¿˜OîÀ‡MáÙ/Üé³V~H!Ñ6ãAùmÄÃ>?Õ‚œîŸüýÙç?¾ûêÛoÞ}ûÃ÷_ÿøóËøé£ßëÖ ¶óã—ÏþòîÇo¿ÿ×ËG_|ñù7ßüôÝŸx÷µØ_~òüÍÿ?ùëo´Ðñòù?ø×O¿üç'õß_åŸõ׿óßíùÓß~Ž2’‡Nö³«Ø :^f<òÐÁ÷÷ B‡ P¿ù•*ÉHV©Ýlx3¿GÁ­ÞîyP©†²×T¦­I8¸†9Š¶½wÙŸº,:ÿ0Ðt16ÒÎ&Ð"ªp7Ž ?íÊŒg‚
+j"ÂÔKI£~ ·„ ÏG¹ Üxí¤ÄÃEpÒ‘¾¢R+¦ƒµåk£úsôΧ³5Ê”&Á?à4ïTg&9]¥;ƒ&›¼×rêoÌ S ¿_
+†­“ˆæ‘¦¶‰ñÃìƒÎšÄx\Šÿ9IS'D¯äÁ—OóÓѬËEί¢„Üe¦¹Œo|!ô1†Ž q„Û^þ“e@]š×ÉôŒBaJQ«©³"–];ÑÄm÷ò a$šC}¼Î[ËoM Ça_å-ä8ÒP³wŽG¸>†æš”©… E(/ÏÔ‚ùþÄ€hË1%´c_§É4LM9:AÁKŠÙF&>Í#ù’é¼)ñ¶ã-";ÁNšö·¯›íìoürš¼°{çÂÆnåw{Y>(CNÕ´s4-˜ÞÇ´¬²¢æ¨ÞۻűљuøØrK—ôÒ,Uõö¯»®_5™¶óÿzÃØŒ¹’Ⱥ‹ 
+îí–’}–õ=ëÿe½Ls$¶q0zŸ aíòÅæüó©ªíæÏ$@.ÒZ(ò[\iíL ?VÍíCOW²è1ÎMÊdtPWUÈ¢ÿC󒇈¿mÔóBƒÛcøžy׊žyg¤Cà<SÃF"x%)NhÞuS¬õ¡Jƒ1-Æù“%įÌ"Ò˜aEz³jòŒY&È,tFd±#N;¡±N
+Q2ÒO훃–\Ça!c¶Y2c
+QÔ É/‡‚r¡ß
+™!™˜ðD¨1##ŠÉ+ì½ÌÀƒB)dÐÕßÉaaÌ™YsÅC #³Ö}vzÂÅ<(=çÂŒ9â®I“ ˜ÎÀ øð)¨$`°ŒMºµj#+¢i ÙVXDq„F_pÍÚ߬ií§éá©5fÅ™û\åw4ˆ‹Aq;Ï©ý
+ÑÕ¸Ü*C¨vl½W0þ½‚ìøïï`ö¡s˜w–Ô „¥çêÁÇ(Va6åç ÀKÉÊ›Ÿ?ýró’
+Vˆ30Û^°ŠÊÇڌɈÄw°Mô<S9ÂF¤¬8[¨É6ÀÁóá',¯ B”dá8î¼×Þ²8…æCÁà…6´u}/õvýÐXÛžüwÆ^!PmPEÜÝÖ ü(</hQxÓvpðD @â’$jœ?Õ¤²{ÕF4Ï”øº´ñTÐ
+µ§äEÒšB”«Ð*56b­=ŸèÖ«ör£ å;ÂÁþ*À5Æ´1ϱS„™‘ Üf!›{¬3‰ä‘yƒ\G²äCáÏ ÑÁÀ8táo€fó+(½E°‡eà£nÏü•ó(SN§Ìu‘Op$ÍÀx†ñB)øT0×ñ‚°É.êg}pG´QŽ‚l4=ÍHQo¦ú åRË)x;ÎkÞ+fµ3¬Ì*BØà‡éŒ TŸN<ƱjÜè
+Œ!ð‰Kd"z^;Ù£QÕ´"Ɖ £ úDá5ñg_^N¼}x‘9¼x)ÿ:~x$/!m']×ç”QŒ‹é爀ÚýÕkæ6§‚Ïçµ,[!Ä>F‹V†£šá¤5ݤYV1 ƒ 'ŽEàZ)ºë€d+{ªüÅ;¶¸f!‰!뺞ý
+r„Žp¯ntÄêuĪR?´/±¾‡ëî…Ì°8‚=¯_Ð$†ë9õ³Ø.£öDžVÛíìR‰‹‰Î
+0KôY- ®*…T)û2àQ>d#ê¼Ú¡^ƒý^'ò’K¬÷`9®×®‹,ßÿÇaË«<±k\àö›/- ^ÎèÌ|¡KCüi›!ädÍ„5ç¸&)ûQD,§ÏšS}œ`ؼuÐOE埌êm¬qºðHD ‡1‚fÒ&\kQÆŒ€;Öj÷8~\aH®ºŸ3å¸d¢öæ+¶ :¨ÅM°Øó¸|a¦Æ”tŒ‡‹ˆ#Jq#á³÷FuEÊVÚšh Y^Ó¬¨”`‰‚¶¦±R¡M…3ƒæã:b¥ÔXˆYín5š«qF9o€Š=‰8–ˆ”ƒ% øƒ¼7;(gZ åKŒ)’2gïc áqèLÉzr[æY*µ­íèaÈRL»LzÇÓl–~ÄÙrŸ¶£ÅæÃKñx °œÒÎ8ÁG&VÓÜ1“¢óy8ÅŽÝ
+hÐvŽ:ÉMCÕ]B‰ˆ¾"²ÖÉ"•‹õ'ÆÉû©ì-SßåÍtIí2€3 wÕ1
+³FDÙ×õÄ9Êýu<RŽ^³:À‰¼1©Ž±Q§"I^rNNß©y6{H¶Ãeˆ›¼»ÉUÆ@ó JÙmžè dRDz(;£ìí¶ˆ°ˆ¨v
+ˆÃw}`ÿ¾ÃƒøEÍ—¬Ù¯‰Á3"í3È\ݹ¦Ä’ÎPÁˆ–OÛ+hÛŽ~Rö4¨ÖZÃo…1v8ã¤n&’†3NìÄç”YBåTôœ³¤Ä30 ƒåT—˜BA7Ö?SÜÄ¡Žý Ü)ò ÐÃ
+H‰Œ—Ind9DOwÐ:àéä:kY·h WU÷ßösjHú®L ‡ws3£jkùñ/Ïþ
+™ÑTg³ùÑz¼”¨Ûjz:‡È05‹ÿZ µ˜2ŽÙYºY“¾×áoÙæÝÄc´‘žÁÞ Â’ÍÃøÚÊ_³ÏèfÎ1/(̇4.ú± ª:,‡Ïˆ ™SÌæÔè 2¢–³µÈÞªkï#fÏ&þf*U OáŒâTªfÁmăø†&•X|ƒ¯é8[™GNv›ýœXSš4nÚVýœ4›Rí§"1C´õZfAš®<tÚZ¥½rd·žF?ÇÇﵓúÈé­£ ¿ˆf™ðÙ.q{êš„tÎÌVöšsvŠéá2§÷p¥/Ò6ÄùØMÔµŸ“¡‘!2×½íŲ:غžk‡kÞ‡¦ùZ'G>ôÑõ@ŒZN&}Aú£µ)ú®Ä€T”®§D; &°ÄÛÞª)wœBM£ÝnJêàA­ k9ÞŒƒ½\š3úå#qì¥#9wŠ»L§—6D±…€Ñ6{ÞkAüþõ¶’—ĈAñØkÁ`mʤªÁ÷óãï_­Í× Š%‰P; z•F„†PM_ó ­Ô¤‰>‚¿áUïÌy;Ðd
+%§­"ªæ5¨ÇîNæӜѤñk&LÇ Î+ã@H}uè‚8dá³=AöV
+Ñ…–Ÿÿ¾ Kh›É)U/5^0
+ƒò»
+…ªãB_Ó“SC„sšDŨ8ªè¾–éô—yay=å£'!cl¤Ñ&H:û¤äŽ?¡“ÏÞÎVr$dpÉo‡Žt4÷‰aä Y…³ UЃ‘ÖÒ‚„Ú¦Ïy¶‚:áàDyvr#Ìì`:ÊË‹&¹–1¶ï´âB#4i©o ÷À*(ßœLY ¢&fPÐ?å˙ת3¢Òÿ1¨ªÓ©f”KºAëÙž9©CƒÈžeaΡC;ç`¸bCÚ`Ù¡ØÙá:ZÒøU–°›(°3æmxž* n­1 Z{/’κEŸÖ7E€æ¥Ù¹!ÈQÒŽ£ón…³–eqÇ
+ÙÔ²Æ<;ádè,2­ý,ƒGÂí²ï97é+Ú–£mˆÝ/¿·q/e±D³Ìûì„– /'‡q{¬)‚ÀAê‡ÀÈV³¿/…—XÉSÐÄHŽëç!†ÔÐI2![¾þZ Çghq@ÉØ•j×8.á@_9Ã'VUƃÕû†pOÔK€ !-3Èð eBܼw˜ðPŸÌ~¥£Ã‹r4È(õ³úKþ
+xÅtT‚"+È>B„Ñ­Eye(
+ÑK+÷i^zÀ¥˜ö·ÔIŠÉpAe¿ L§r)Õß[!.Ä æ*æš^ßJJä›]lË zøÃ@lÛ“TAO!¢ŸÑB™pJ|ƒà¼|¯ªÂ÷˜¯}äŠYˆPÀ¶<vï¥IY»UçdoÅŠ˜OçˆaoÖùî÷@Rʶ+Xëcn(HV{ x·ÂQˆ+d/‹¸·J1>_ËÐné­’ŒÜâHÉkÑ©W6û™…~Öïÿ¦?ÉUúñ‡ò*lG.éü§G†2Œ6B±²ÕˆŸÎ:}EÚBL)OïB8qZâyÔ°HåÃÆâ´eCÈŠ(ŠTˆ^Ï‘M„ª’ðûÑEb.Q
+ab~¥=³?@öNv¤Ä³x³ÃG­%ùx)z‡ XŒÿ<çE)‚Ö%‘aoÕ0VÏžåK§U±?²µúKu”aîqÚ õ!tdyË$S“)ç%qwú Š–Uwæ鮃ØZq„ t#39Öùæ†|?oÑ$I
+š<úh ”Ÿo’¿ß€¼R:9œYà b•ž¹2Ýéo ˆaòÍäÌ_¥lò©«mâ[Y¼ øº—ô®‘5Ç•í¡‰¶&Õô ¤†Ð ”YIî'H¡F݈٥Rë5Vý@¬à_Ó *×æ’xÁagüG¢4Œ¹úBÀÂø˜9Iò L,NÇ'‰¾ƒJè84êÓÞ­£ƒçâ"qGŒª°Yrõn#rHEì¬{/&N/‡%5_­ô@[É÷ÊŒ?É
+$K¯Ê$ÆÝé'9fÌéá•GD$¥›}Æx‹oÒôû;€N3c · ²gÜyVU‘ôÜ%øÕeGãÊÝGÓÚ|+n…–Y¹6<zpB
+í­ø!‡¥€ðj&ãÊ-§FÁ{Ñ@î“ÿõ
+.J°z )¯¢+ôRlë*·MÞ dný ï²D?¶œD¹u ï
+†ñúVn¤Ö‡–Óæ ¹ Æ“ákŸ~Äc¡‰†^„h¥íÌûö529#„ßù˜ËºãC¾ÇÐI]p ¥‘çw±…IÔæžæËü$²Ì6c’o
+ØÊZfÎFxœöðŠ*HI¹Á,ýdyfñ;: þ€&T¨7ã€(Ò#…ˉ2?¼‚fôzœÅqP¬°"
+T‘±!\39_¥›cZG- ‚m1A£½œ…£Ã'5Á£„QpÀÖ:pBy¹"±ûµVß9¡ò˜]>üùóž@ SÖ# ¤îu0ž+óle§Nép¤âé¾åçù8IÚËì ULH?[ñr-yFJ<Á¸Á¦­F \Y©iÈÉ®eåÌÞzçíVUA9r24ǬzT7OVf¥¦Üç!†L÷ê›Zm`+¢J@ì×9ÿKy™$ǹã@øï>ƒçµßÒ·ðV÷_÷—¨¶þbE«á°TÎâ
+çVÅ”ïPߊ ¼hÊz@ #â‚zvŽ«¼%`Òi0툷Ò)§‰¿Ü,6Z“d#‚i†®¥åÊêzq²)‡#ò窦O [% 9ìŠZ.‹˜É$¢¢„‘öV¥!ð
+½Û¸*4)C¯ïð‚¡gOÑ{€>Þ¦‚äånUhdE
+»Ë+Zš#SèÔÞ97§©ÀÑæËŒ%³‹£«ýb’Žå,Ëÿk¸[í†q=Û|E˜ÜÏæÌæ~¡e”!ß0ô@:d³ ±Ç9ᬮÎå´ÕŽGäÂáÙç´í*¼Æàû:\¢ Ùïô…eX×´§áòDAqßÍ„ÿùŽSÿ©Ç›ý”Tð¾ÈAi—<ˆ¡¯RNÒ òé:¶ì è÷Ĩ£¹©ÄvÜ0˜öÄ”A¦0µùƒ
+Ç d%bÿèe»£R~Ó£ì·@—¼n÷-PÛ>:ÞStóñTd[H
+„š$þf¾ÎäU“¡né@¨NãÔô¸ŸÇÔRÞýÓŒkg€†xÔb+ÔN"€íi±Î»‡$¹ØÜëˆÀ‘‰¦’DçíQÓ„B™˜©ã
+‚êa—Ï5c'ð˜Q‘³]S–CòaBî…é¡&ØäÕ.?»3ØHUÅõ·ë2ŸnÊ´B9ËØ”ÚXÛ¤ìGÙä¤^Ïë;a^‰J ê±*_=†ÚTpÕêóø;Aªš2<@0è˜Hrä<å“ö ñ8 “
+¼bè
+Øqà2÷öIw«Ê½È au©ËqƒV.†ÇwR~ª24}Lªn{†»¯ƒ³>`}²ïß>d÷2—T/ß­
+ ¹df`Ã1ŽUÁë`éž-Š‘¥2Ü«Ÿ#C I»oU£óþÒºœÆá;=g게_N
+^ªHm·ªvMl¡>õË…që|'hV§w«|ÿ+"X‡N'L—r¿¯"Z€ÜàžçÅ 1Jet\ÆÉü IÖÛiÃ1°nÇœñ–$¿F[à > ]>Áå©ê)M¥c¸‚5‡ š¨ÓfkBc*¤qȳf≥KDª¹êÄpˆ'Íâ1Ž%žáeYU½²NßC)-Ùü ž‘ësJtESI|”¨áJS²ÒFlTgd
+yµ7Ûyuîx¶z™¢‘ú-õ7ë|Í£WÈ×<z‡mø#5*±Ê òªÌ c¿Ä…øQú­_‰ˆÉèåæi¾WÐTÔE×á ;!?¬øÈߪøëz«èãêž94½ßl1Áßs›%^/Œ%ΓíáÚ%~zZ*ómz?E.t'‚ÒD‚Ehkú|ÆPeÑLjí“
+Î8žÎçö¡O&ÜeHX¥‡v\Ö©H»çd1-›´;07§s¸±»×ëA|êäøðs™Gß
+aŒüaÒ;¼bÝûÏŠVUi‘©àXdŠìÙE“ í …¼$çña3>½º¸âPúÆä ³":îË ¦Lm\‚¯ –܉һ¬"ÏÕÄæºØ=
+ÎÓÜ éR;@«Òt~²B°¡5™Fg>(¾ÊîšÅk«÷vaFöxj³žšAÔÖ¡|$1²^fʸ@zqâƒ_¹ £SÏ[ó—i£çó]节›:ÕeR~]ÅåúxâåRRiâV¯²W ÅI;ï X‡*…ıƒT
+˜÷ 
+F4Yn_§cdiF.Ö1ÍVÛÖZo y X ¶Ìg+\ß`UÝ#@”zÏrI¾Žá5·rÞ»iXp‘k/c’PÈÿ0^îÈu9^÷ÀتßpJ2¸ÆÎþç;
+[šÊ—€®º~Œ˜ñp ^dÍr|<Ÿíæh ûߤàãˆmº±Lm+‘ü-à Ez!#IÛলâ(@¼š®˜}äFñÊrZb‡ÃcQˆdæ Ø3¾p,:Á»OâÂúçBm´Âq ¹Ñ*&4… d%X×îK.—DÓ‘É“ÛPÑÙ¡opãû[ºl°8r¬KÉÏ·?Þòû¿9/ðVÁî¡‹ýØ
+ÖzÑ~ŽÝï¾(âŽl;Pìqɶ
+CÏ LhBUÂ8‹ðJüú,ò*%[L »¬ˆ‰7EM’¬c¹r\™µÏÀ$Й¨rÆ«%È•ÿžÙä5áÊ‹æèF¼ÚVx PÌÏ·[Ñ%[pÞ'ÿÉJöSÄF Ô˜]o1Ïd–Bæ
+ëËËIƒE!ˆŒÁ­\£B7fEÈ¥avA„ŠÚ"aiÀ±o
+7`q
+³P{j/J~¨°Œ*q]—c_9ø‹V0Á¸ˆ”åå
+<=Â/ Ñ,8͘QÂþÂk÷y¼ÛP\’"4%AJ†L™žÐ[èÈPrh«hÌz;E/`/ÀJî^ÄJ‹#zÛ¢\•ØÚS˜ÂáMéÏ—A³
+”‹O`±J¨Ñ’¥ÂracG-ÎYüÏZ
+€Ò*²R̵Ʊ’À–F¬1«TÝ-3ÆÑ=i°’^1Nâq¼»yï‹}ó\:n2¥/ÙÃÛs}ÖžÃï%€„ºqp«õXDr†<Ê_ÅkX¬¦-ŠxÐÁ0÷#-Æɵ+ˆÜSµ?‹t?de![Nu'zƒSd´…€Ø}c5‰”¬Ç™$&óõ~ (€˜ÜÆ:
+œ”sÀ›cHÞ`i`®¶/Ë`
+¤~?–„¨ã6fY{¬j¤¸Ù¦ÓÂ,1
+çá„wø×ÄŠ1N0 EN=‚MÙÄPëÍ@Ð(ç(€Žx‚Þ–BûÚ*èeU¼‚b±ؤ
+’QÙ§Eöˆ±J0*1N–öítvøW¶ö±½
+æ÷ZE¾¼å¸1¾1NнØ
+H¢ÇÃQTB¤›œºái"@W„
+ëÀN+`ñðîZƒµe 2‡%J´C’ =B£U½»ôó.ÖZŠ™gqªcYi1ÒÝk”4yx”ƒ 7®òHû’r/nUœJÒá"ÒÉ\ñòΛ!¢ÝmÑŸd‚AiëâK ')IÖÓF(Åc!™êRl3ÎÁÒã~¤û.Á¶´‰iÙÀ¿:¼ËÚ=8ÅŸ×Ýl(Ÿ '@ÇöÆ0Àè¢s\ò/EV"SÏꬣ„_myúÃ7¬¸²×1ì0—Ú)ÄÃñý0;°W õ¤¾æUÅ_µ†`Ãd"©$‰àê†Ï½D)… ÞãRâG1ÅMæÀî®szÕ’LŒb£fÉ_7íb9»—T…9$ƒä±¯N‡Þ
+b"G§È)%1$¥«]\¼™WƒôÑ"§dé<"µ}5Èåþ²ä—0(Ç–2^h”ÛìSdb#`!w
+FÊæ6µ cVwïPEC¹‚,ÏÓírqìC¨¢Ô«UXºžô%Àzâ[ WÒ¬‹G+WŽÏ­aØ88êl^e_ˆ`©ÇGºTG&GÊî'x@#ÇP¢hf^Q¦ç1ß6l#BQûk®·É¾fÂ1ïçÛo¿ýç-½ÿë¿o¿ýï ‡ôƒ` †¢v¯i·ÅÎÈb¬÷?­Ò×./^¤æq­ÅýÆ»J†ô„ëÂ#Q‚?£‘h'ëûÎ:`äCAëá¾KÉïo*šy -Ì—Öõ; Ÿ:b &€º2˜b1F°¥•LV~˜Æ< }–¤OR<Úð\³ÅŸ ˆ>ðù‰Pè6élaÍß-Òi"KÜFˆm¿×3góŽnÝaœØPaQ5¤í¬¶qVIñ,QâJfÝÑ?îЈb 98ÊÔ…F<Ë9°ér–[mš¼7c™åVò»¡ñaý`çd:qû ˜ÁìQvOÜ!nÀ+4X6•¿Ž8‰ÜcKsœÛO^%–ŠW#ƒMA·©wT`ß‹ü*¨ŒÆb(B‡;Á5Ýp¾ ÂY»Ã ÏE—q7û½¥ßéc¿U´”ÄfÔ-kc>®»÷Pôùý`¥G›?XÍMá”WÙPgqÌ°4ßãç¢ùCßYr4úú‡ rf£¡O߯‘/‚ iDÍóaÿ\´¥,fCµEŸ/ŠÚHhTjï”ßí"f€1¼D)Ê…©qä*á»|oV¦W`ÀÌ¡0¯bP£FÞ\½Æ&y‘´²ñrâ°Ï”ŽÙn«Ñ%ò*H,‘ÔOÂu¢„‚ڥĂà¸-Zé[xûNo„M‰B;—aUøiƒÎ÷ç9¨‚ÿuƒÙÔO5põ7ãÿ)/“äÊm%Š®À{Ð
+^=0.½‹šJûŸúÜLPQ"“–ìÁø¯®@ ›Û°CÙÂAè (€þ)¼
+rriÊŠ §¼°sPÉÁÓgJ¹Œ["LÑcXBÌ“–/¿óbòű!u`B²Fsc@6¡´ÎäôšQ@Þ´ÞÙ†¨Zõ,Ëç žØXB
+ê‚ÆUŒC¥Pµ‹]‚´ƒwIRg1™g©™Î%€èSx0<f™¤3êÃ9½èÚ˜×Óm0*Œb*²5¯t Ú,&çÕ°:•ú¡”
+ÁÔ°ÆäÏVù—î˜DЀwدêÇÜ/Ã_ôÄÇž#|T“r/–;ÜçþÔµ8€(–·;æ[TâF&@ÞÅN5‡úŠð]~=å>5·«Ü§/xR4Å4rÉ!Ë[¼ Iþø~·R¿öâ%‹pA5 ïœ|JÑæÉÉÞAgÜ>w½ÿäNÆe½dã«8ÎÕYøEfŠ\‚+¢#Z‰Œ–q3Õb쪑:G#éVøÕœá*j¼œ9iL #Eì †‡ |g2Z•Ï¾šé "ƒ=mˆw}
+A`­¶; ÀaC³ÒLCÐÌ<Þaú¡[ 4…É=Dõ§´c QAI‘Ó:ø/§uz•):¬›¸ a¼Ðuë½O”ˆwñZcç‰Ös´Ó·ÂÔÂÑËïÂhŒÖr—›ºü3=ÆÐæ’¬:÷3‹¶ÙÖyÙÛ=`YÆqÉóñ9d@ÌÇ|( és)äzžs+îò¥Iþ©+Kٴ°½Ñm˜nTwQOÒf•ŒJ“x•±¿t­yU­6Ö}½EõÅ$3±yŒ…h´°KˆêÚëàœëÄ·¹N^øªëŹîAPâoöÉd±¹žÄ¨aÃ"ÕÐÖÕíT¬Sù/<@i'ã,Øg>€Ê«Â3 +”á¿76©8ëÕ-¢êFŠ¼?+»h2†m4†Å‚#ªÏ”; ¶Ž-Òóm”‹lDã­Crç~UüÌÚ«s~Mn–3''s€ðïì]aøENe
+H9ƆÀvKöŽ2tŒ}+‰W–
+BBl›¬;Œ‹BæµM5€¨Æ¡ê+…ç „ˆŠÙ³±Ÿ“–1á¬e>”ŸŠé„‰p¿21q•iÊê¡Oë8‘Œ³Åˆvع°+?E-ãk /·Ôê3èëµÇKéN8UÖÚÊë!,B)„z1«!L) yAˆp˜/šæí:h<ÒC¥g
++£‡dyl^ÆEp}&sÔðœ.u…!4ŸAïàÿ%B™Œ• )¬ppŸzRî,ŠUÌzí{HÙí¶d›[ ©=òÆ á>?‡UkHÃP*¡¬º×á¾xM¦G;3?)ã¿·Ó<]®›b«èêß_2ÛEQ#c§?ÈÝŒd²©fÒ:þ†Â4¬+rk˜û§¾ƒH³’íY,šOmUˆ*Œ* ©öµ› Í=”æ2T¿sûvq'®VŽ€é%Än 5Ÿ9.¹G„7å’o‰ºð%-'ÿ_Ä
+Ž‚ jëGûÈo«^&·A1Ð}êçØèòrã„ð†¡?)k¼Ý{ ƒŽ€1Üͼîs±±ï†)¸èe„vz]…ŒÉÂã«ýSlS†~áõ:â|ü—ÕšÜËÃnV*ªÅÄÙ–-° ­2j9“2½”Ì&í3DUBëã ¨îËXrû"ÙûKl7\!šòÚÈr2™ý|²â3rZ<%c<§Ù·òe>ý€¢9//1dÖô”úsÌmí¢ý
+)F•á9ŒkÈ:„å 6É¿tÇ`Ó¸+KÕªs¿ 2‹~×na2xSƒ1IL“­­ú—®¥DÙh?²gñRÝ}#mb£šÃ}EøW®¾Ÿr–àש Þ /=„ß¹_¶a ‘Piüˆ6è—­·™2—²£æ€©?|b Í!¿–W€ù'ÀD qûØôþ“9G :J¼òØu«o&БB£â“>bŒâÎ-ʦ’Zl‰jnªåßÐõ‰”‡tþ†åèåp3¿hi£‘2wÞ‘7åw¸Ž‘›Ås#Ëaºzˆþp/¿ˆømòJkæåõé®ïX|îŽ,­W÷R0ç§#cbsæN 11Ó»Ì
+†ˆä®å^§dœY¦ÂîZ‡éTòi³>”Èñ²¥ˆPÙlV~‹å2J«ùôkðMšWÞðæm¹cOvIÔæ£Lk¶JÐ ,–©Ú0§ %<À I\£=»I
+p«´uœ­Žœ ÝÚ(q…ü Î‚ø>ŒòKÉÞš Y«&{çhkÉl9…t¶X6žÃj‹ØDÒjÞ ³Ù§
+=â²jð½D’ HäÐßï{èà” Ñ/Ãm¸3Ô/É2…lLÁ²íwëª< „™a`‹‹=»Ú'ÙÇ¡u'"âA¡âÆÿ÷GUï©M‚^0Ž|ì¸zO‚KT4¡ã×u¾žCIÖ>ðRH阚
+‹G*ix«‘ìS½àñèߨÖ$£)§kòÝcQóDâKM~ŧ*#?þ¡‡•Fsl…êó¡¶_SfÔLç÷C7d¨HÂŒXå% $Vé*¡=ì×TÆ8k¸€Îߧ=Rr $±08Ÿd-Ââè5JcC¤`bxJ뎔Š2t=º˜êðGûK
+r¨ËîH£¬“R,-׸WyŠ8Qþ*‹­vP8#RšÅ®‹ü…|ïÏ~ˆ<†}ÊËU85²£ú¶w¶õ­„Îbµt¹Øž†3ñWIÄ¢KÏBÚYélø?ÎÅqm;ß^’ñ…®f®ç‚VbñaÒÕÁ.«,×À*¸†€&³¦0%‘Óƒ±`VJ#Ï܆WäLü~›Ûâx¯º-à½;÷5¾õØÛô?Gõ¿ï@Fi1,t•—ž9qÄ›sÜo·Bàâ|EòÇáI¦‹M3ØjE4Qq^æ’,Š™EŒé/ÉE–Ó"üJ5Î6UØ•Ÿavˆkªt,vË=Sïþ=hDz¾…`ÍÜsØø ¼ºDÛì"¿‰ðù½ñLœ¾ô¨ k¯â?×?K9@ˆN¦Ìt9JÚZµ Sñ
+˜´ÕvÚáëà$[Ã6u8°OÃ(Ž¢*‡@×È^Ñ—ÉzE°Fèܱ”!¯wñnÒ}¦Ê8^¯äŒ¦=]Æ™·¤Õè©ÉüÇ%’ötä×Æ(¶Žâ èpTÏæžÕÔQŒqvmÈ,;ÃZ€¢(\CwÏa¹ªRZ;R9—‘U”Ï(}/igc’c5rÇ„6å $C»—”Sñ§Ì´µãšR$/Ö9 †ÕÆf ÜG²cL¨„Ü̃¼Ú~Í0Êø:]*™™XÏ«ÿÙÇñ½ækRX` ãöpâʼn-*¡€WÑxEGÜÿÐÛ¾qœ_ 35E«< ¤dÞŸ±Ðûø%áÞOvd‚,F_›@DÃÎë!žðV¢/%øAyÁ(ç@QJALj'ÉbÉ
+´…xÇ`©@‘Ä%MµGÁˆƒïd19@†uÂiõ«7œÁ«¦öwõf¾4@H
+KmçÑ ™Y¼^Û¢I˜çRøíêNê¯ÿyXšÒ¤HDA‡šCU{9z•Ž8³èò⚬ÉÂÜ¢ ¡éo¥e w• »t ’Û‰P„–X|–›h·¨3ðÜ¢¨—¡ÛÃÊ™w§y ¦/O]j ê¤ó^ è
+ ¤„Ú/ø³…¿wMÇßAZ!äcHÞü~
+B¯Šë@R%´,JÄüM¢¾yîQt8¢c
+F‡ c[ôºŸ±£ÚdÇzÕÔ‘âÀùú´Ì·ãXäk †20A!ó
+5e"6.`FZÆ°ªâšWˆø`üF’†Zô°ËhxO]X!Øpí¢ž5–’žù9„Á.kR˜]Bv2‡3ôÙ–"+ RИ´—ø¥DWaŸâ·æHÜ7çðm Íž@ìþ5ÓE2k!l¾y^NÚ\L2Я͡Ä2mƒÃúµÃ·’ßç7=j0 S;D‹c##š•®X÷Û›šVF¹¦ÌÖgÜôÚš&ædþ]ap~»5¸¡¬•@lå:§¯¯ã~=剙ǧ<±wyÒÂŒ{+ÇÃe pX7ڛƽâ÷}? ‡Q%ÊÈ _öû¿=Il§žÎoÞÑ¥è¹àÏÛžDñç/ÚDÑ> u$¿k™¤L¥‹yZ÷¿´…#ät¨J%lÒµÖ›‘0zIFðK¡y¸Ý´0ø.L}Í>¯n GL
+½çɼ|(û­¸JZ»HÀû,Ž5gx½ùÌ ï„¼ñe^| J”s \C½YÎ@`™>qÀp3¾•#Yò†ó5à±6¼>¸ð’6øž˜Á|¼J¦'‘US‰›à´çºÎË1ÐÊéæJ£øšSׯ¯,§¬ö¦ä%‹ùvŽ t^ÞoEŸoŠ˜+\4²º·œ›y«€Ž3K5XHv{oyÂFaóÅ›i ƒ‡*;LâÝnj“¿§=)nD2vóøı æߎ7ÃüR²¯2NYÝboŽ!¥¦F”ŸChED…‹›pò"@† â„']K¬ÅMIÌ$¾œÎ¤˜¸ D*]~ŽåožNÖ¾€÷ú¦¢Bù>zñ³}Ï"›ò'˜xI†—àC´yV,H
+¨ ±¡ ûI`‡®½«Q‚–o[Ipä©h]«s—@¾£ ÌPOƒÍUC”ZãáÐ=ç‚þMGei6O€ œ ®ù·I¾Tì‹'`C.:Ï~9ÌàE8fÀ
+½h¯¥œÎZÍP!æ­ó’T‹—À‡Ò…¹±.Öœ+»µi”§ FŽµƒÌúÑUìHa”Ã1áˆç€„y,™I!±½f^‚ à²|Jx ;€ÐÇr†¨`cbŸñ—¢oŠ&·Yåf<E˜wd˜îg' pVÌÅ*CôhŽñ¸iÁë˜<}%“.^Ê3,| p¸Äý¢^‹î…-,"„œ¹êôsxv‚@k”ë¨þ8r¡#t0S©DBê.¶Ñ45M°+Ï1)%”ÔÒÉWúGü;ÙS…Õq2Î=è¿) 7·EQ¾‘I¨UüW.¬ O­.?9èÊzÄŒ|+á'˜~­"[Ý^‹2«MraÆ°ÖçµèŠþÉpðè²1Ö9”­ä'ê~9ž¦½8¾ýÆY™È9öâÑ7AåI#so !˜¾óô·Á?<=Cè'¸¯v=§|ÈÃ6Æñú楤žœ½„-ÌCƇ§'’6E=„uû‹Â–ˆQtkÇ,þò¿*à?+…qG¦d±PŠL°!þU/QÎÑ>€žɶ
+Y¼óýŸ{~NÄ!qÕk‹ÿ¶héÕŒ\ºùy+â'I=„ÿ3PÜ$†Ò*ÅÑžqT’=G3ð£»1DôAºR
+xYôÖ>ßÔ°X7^‰@5O›ìCP˜üÉöfv”m^’”,‰¿âÆ·1m†s>K ’
+JÎë—’}ÚÍؾÞë›s
+±Ð çÓ!LÿôÖ­Á¢Û¼;N‚Mç©ù¿-šdÅ;²6æÇu÷^Š>ÿfAø“_JÑ’péºüÏ‹$  °rÁWN¥„' nÇÌÙa(Âh«åé%ʤ%>åaÏ«–…8£>5ƒ‰%þ3—|˜Î&7K¹†‘’ù`~q… ™f†G[»„ïJ¸õœg*âh²Ž `or“÷aCäRó¹k#ÞMsF¤%+ˆ&ÁÍòÉ0t´Ê0ÒëËÉ\ KÉ¢öàT0T¤Øpk‹—cNt«óÀ)…Žó¥DJP6è È·vãæÛ<_JtU•L%µ^·]Ï©0_OW›Ô‡A2@\µ<åä‰_#ù(+dcÁ+åcÓë„ÂÚ‘ÈSáýèéòs:{>´ÜGi3i¡GÚ”G²2±¤9ñußÿ¹Ö|qWŸ·Vƒœ¾†£øñ¹â †€+C7j¿=ûUFvƒ¿vÏ}'`ÀÄ«Öo—<õê1Ê›î±w˜Þ¹$|oΙ|kuwŒ¸¯FÍsðÈ <HËâ"¤‚#ælûI‚9‚’XX܇B<–Åé” ƒoé¸}{îÜ‹æ}ÿç²»]¤F–"/d$ÇMb§!×R7 QMnÐýaòý6x¤—]t‘öbIz„xõ`µÑ.·ú1(»aA‡· {ä—’™É «ŒòÏ<ór Éj
+\Í
+>õH’§I\ì/¾‰…”‹3}°ÑZEA`ÜúpŽ8®ÐâîÛ|¶ÑÁLr¤9r:ùh0ElvŒiŒÏ6š
+ÀOp «àÈN6ZPçªS‘£9Ûè{ÑaÞè°“ Hõ»E×õ{gGåã¿)m/†¥ 
+eæÍt°‰Ð´un%†O›ýT¤Ò¼Ô›ñì*Áyñ}½ªSxWùÆÊÍÈfÂ^ò8 B+þ©ÊŒXR0­søýSp[Έ…àÂk„X­¤Ø±P G;¯vúŸ³Þùó¯C.•Õç3Diý&k ü‘D£{(z[x6¸GO]´/E*‘›…xû‚É|9'^^¥ã*YƒXÇÃFtMAðxf—¨YZ?õc
+Â’Y IrÞ¬˜¯1Ì_@*1cî­€ZÑ°Á:oBzdl@Gz*°â3(ÀÃ%_ÔÉ…Àù¢ƒ©GÂ,qÍêZr&¨KÑÇC‘¦‚Q.§"æMoðœU§ò bŽ/p5䢘JÖY0 ²Jðaß’Z¡]r/­¯Žå( »Í]ă‡å¿¨Ô+Ne·é9b²Cäç< ÅS²ºê9VháùLIL¿óp
+…€÷^·ß¨M s0u™»‰ËóŒ3•áDâeŸùdghMXŠsVRD "ðÜN ñX€£.-6ójØÁ^`MK?½ŠÈXQ”`v‡;~›LHž›;Jˆ–q®Å"‹ª{¸¢ì/ç €†CóÅÒ…Y
+X¯yÀÞrœ°èÂ:*Û4½Ç(Bm)‘B×ËñðØ!PÑÜÈ#Ñ!IÚ
+ÕÄé y*ÝqÍ“pD¸x:¤âœ„c pÅB¢éx O¨+Aõ”ü2IçëÀÂmH¯B–£jûÝ’=ÚÉCšjBˆÀ Ì äÿ!z‰ÌB¤Ê %ŸŠÏ$±ù±®zL|£‰Ãm(šÃ8¹@Ã9·"³E™>Ó°ÑWæ:ñk…PNŠÃ>& È›`܆bú¡m²@ «„ÝTeæ¶4²ŠEãA#ü>‡¢ÿŠìû;Jüa¡³¼¦° yaÜïcJðUž¿™`S ¡
+³=Ö1 ÄÙ0‚
+ìs]ÌLJrŸz.º\T',…]Dû¶EÚoUf>,½Jà›:‡üÑëe ÓgÈLêa¡‹òÉD!´ræÀ <m—4¢ Ä=XØ; Œñ\k[ôO}Ä?Ñ*âvXù–k9Ø·óz(!fï5ƒL„1PxÄ8ýPIÇ° ûS¢¹JrÄÏ£`¬w·žžxÖt8…DyÕ û3–q‰/ 6)6hßœîc
+dåìœ,Qár\jK7iò¡­¯¥´rãP¢OáðY¼…õVŽÇˆš:ŽZÝŠÊÚS1¨V’º<Ò£·F¡
+LK¦–\ÞŠä1׈V’DoSA½åÀiÛø±n\†¼!Çl^œ˜Ã]ä”ÖËqJZ 0q¨X=Ît_ˆ£®åᦦ
+gÆ°ÌC ÓWt}êÿqüñ;W¥1¨(¤š~\ ¶± +Röä8üÔ © /*iãOf(;hO¡ •ÒË<8’Ä?ž-&¶m#4ú½¡âÂrÊöoR •Á¡—«Ó¼60/.ÓB–I6/>…*† l›ÂMÙ9ðr ljZ
+îs*gª¤@£]žâsÍbõ´(PþÜ%6p@ 8&6I@¡¯ñY]ÌoØ/\
+‹îñõyåÅð,Râ÷H+ÑÓE>ø
+R‹ùÓo-Ó‘Jdµ³† âÉ—F¼ 0„œ¤áÌ~Gksµ•&Z>·áoFÛÒP
+W›àß%©Êû 9J%~°¿7ý:\ >‡¥’/èmgžrýØ=Jh&Æ=±æûy[â[Í^O(IƒØý(È"u%ÄÚ×IlÜ%ý›#•]ƒ‡vx(É%à%[cJ}ó³¿îc‹÷ŽëÕÄÄŽfëšÍ|ôØ1E£|8/°?aè4˜£ï5ÌÑeÙ¸3¼®4­_%&íÇF';.å«©r€Bèˆ(=hþò‚!!4%UI±÷’P_ÎV6m!QhëhŸõÁ
+ì/Á…žÂ&}¯æ“r dH`*Wð˜}Ø0<5¿wâ
+ÙI»î}c• óŠìl¬¨0¥+ù°_ÏUŸe÷¶D`à3¾t·!D‚÷ƒp¾£¯ùï8°aÿ
+Il‡÷7™‚-Þ°Š ¹]â;dˆäXŽ¤Y[X©T®×_ˆ
+^”£1[‡'iÎïõõ€F¢1ºž-z=%]
+4£EŽ@GA0©Vlf ¿ƒ±#=w[«§‹z0"XøU‚KÀj…—³Pûä|2V®m‹1=â Ô»½Âš¡¹ƒc¦n²îºNÅnt ¡Œ€Rƒg!,¯”’Ü
+¿ÍÍ£7BÙ/'x“<2ÝOïÀ¥Œ"“Ke¦€²s,÷mÏi2×—æ0©ó…†Õ CV¿ï™Gß¹<ºÍ 9Ñ«|vçãG‹ Ô¿°/r®É7‹‰VÑ·Ëðïû•À`èw. ?¶n$jÐxÚpW+®««äâƒ`–Æ!å¯aïÍŒFc¡|¸IžGäÅ_LâZ)Ì-Òépèw½_pgjè ¢ê½ lSz´z¢¡’)oï¶ÑjšI=û8ÐᎾC°{‰ÁŒÕŠAò¡«OpJ$(Ëé3dÃqØ}¬ Š½K„—k— Ð¸DvKÖÍ›m÷,úmð¯¨H*Ñ3â'ÉÏuÆîa„èP›GÀ,Á„ÙýéX±L‚BE5ñß87¦w™Å®‹d«Ûm†‹ñ$jÌÝdÐÆýqLiŸô0ÒO¦ 9¸ÀçãwxÉáwX>ŽÖõ™Ø8—aPd‚Fß(eÉéB!G Øír”V¹Äš&0sÆ…|áÅ„ èû;$Œêsñîü/:#žÀº¾ÅkZW-Íø¤‹ÿ]Ε?›½
+É”fãª?éJK§E7©$‰o$l–ý(îX“@¦£Œ.Ë=°=uÎ@8ÚLÙô0 ªsU ýP'â:K¦i¬E?'ÝŠ„y݇)ðo_ßA¦¹ ¯®gsŒ3r‡ƒ ¥õ¨ ‹¿B¨r»\ð¥@ÕìϵèÈÈÅ—!& š)õUæ‹|Ât'Ø0\ò™±ð2VâK€@΋æ%1CÝŠ>¾)ÂÖ@.½Ëg8C©è?
+H‰”—AŽ¤5 …OÐw¨5¥8±{‰†eo9ÁV=KîÏç$ÿ@W¥4Ð]ý*‰íg¿çîC›DÜ~mî÷Z[5V´Èʹ߇v­¢cXõ)6ºG¯
+nBz éÚêC2îaÎGEJ1»}}KÕ(Ñ:—õ¾@nµ—ο2tžÓú¨uÔÒÆ‚Œ"b¡À&DLGE5¼Ê¸½g]ÌõxŽñjuQ0ãzMç~±b± ½G“NzÛQ6xïu•™Œ*V[ÝÙ áÃ"±yŽFÕâ¦îýÊqó&ÕIDÓ­™OWñÛ®Ò;xáMÑ¥0gL•ÐÐÒ"Ƹ%"3GE©xµýàJ!DBvÜ¥’^¢u\…Riµ™{”+n!ÖJ'Ÿ¾@݆ÊI£É<§20Ô¥ÇN± ¥ö^kï behÞ]lߤÎŒpn»nÒ¬Z4çÍ Tù…×û(ó˜P‘F±)EÝ2\8£S†Å›Ñ[P· ›¢;%Ú ‘~“ÑKíiš”L
+MAR¯ãŒøDÑ/oϘ×KÎü~\Äÿ
+„ÂBþç@t”©Ó‰¾­Qd|8B“ Áå!-„¾•Ò)†§ìfÆ×EFúlŒãviƒ§”4Ae´yH
+=Ìp•í‹,Û¾`jp9/ $…ŸÓ€5ß7=ƒ> w>&røRé—ü†餹:ò£ Þ6¢ aÓ!mÍÒ¬BÞž¶•z]vãž& ™…j ®=99N¦Hú{ )åzvFɾ:§’©O‰éWdÚîËâÚpn45”˜ú`>Æòq+¦5,M´Ô}ÏÄÁƒ_6ÍçóÛHßËYQhÒ‘ú@÷ ’j–Š‚ážò}9¦“ƒÈõúx¢4oÍ‚µÕßð:‡NZèa,å„!ø^Wc{3&’qíG©ä¥¸_í]&‡š%ówàè!„5e*h ³¬îy;:Öòq;D&ƒBé‹SP1eq“‹|Ÿ7µ9#r`iMRž7>@âX‘ôÇ>=ÃÓæ˜êDU¦´ìäa²[.0Ñ^ šY`g:‹\7=Ò9JUr±Šè$W÷bdä€ x¯×=9âÚäTæF¡qußC¡±
+ß:N†N—uŒL\Ç`t¬–GPL‰@ö1Žå|€¬›ð`…ßøìŠüᘃ{åÔVõÚP(­Xb»¾š–¦âá°üç½kZC˜Ÿaä^³’ƒÂã2òÛ>L<ѪÕOFõËô³Á4wÔè¶loæ
+£Êý1íì C_ÒJÌf«O“¸ÐBÓYÖjØBPûÊ—è+[tgô>Çö•å>I˜:Iž)·ÓK©ó-_Gv+á,‰b9pàÞyB${NÁ¹w=BÖM%Ý7Ó'Ø^çØ=MS«9°z_cŒqM;9 ¦¾
+PéT]˜n-ŽÉ1–Àsè Ç Ÿ ÿ-ÔºéÃ8ᱬŠ¦ë˜çÇ`àé:ìA™ð9(K)bŒ!!4Ö¾ê19|Bâøº3‡ösŠ9y œ\4´+õ±
+õXðçSHsxÉ#÷(LÓ
+ Ÿ›XÏ}Àº€U ÌÒÿVéj:í)ì¹e¶?#œ kvaVH"–ÿ;ÞO 4ؤÂóÆüŸ §ëA?ó¦÷ùôzO9EŽ¨r‰ ƒ¦çb(r=UÇ+c•MÉ ÷#è_½SÁ\FÊÏ‚²¼Jˆ¹¾Ÿþúø™è2 ¿üñVn¿ýùöËßorûý­Ë(ÀŽcúÖ˜4TÎÀÁ4r’ ¸‡ÁÆ«³(!žÃw—ÄÝ:øŒ„º´SÞöaÛ¬ÜÒ %}ÇÆ;‚6=ˆÃ„àf DB·8´´ÅÈ:þsLˆà2œ× ¥û1mi¥[êÇ„¤9%‘[\âûŒÜSwp ÚW—?€>VzX0˜éÆê”üÊÉ”n:¾5!X\Mã¡i'$õ™˜Î,A¹ ²:¨íl‡Ò¹¹ˆÍcÅÄÔä"¶_•‘™c]LOG'´ozñZ2†6“‘õbü ~¸²Û´´ø½r¨”ÉÄ\CsàjÎæS»|[ ôQéKE·3­9B-ýã|ܤXqÚwÛ)è…¤òÁ¾ÅZËf|¸ù´ŸñE*½ãgʲ.¯~Ÿ)IÅÁ˜ñæ3¸ÁŒäAþåe’ÛÖDÑdZÀ¾gê]xjïúÏ-’Fü^ öG€$JlŠ·n£|êØd¯E7»­"C4& [éÙmÃF¸e Ó×:Ii© š3[I•)–± õØí¢x€O—Tk+I3ò/N˜ÇoÃ>uÔ$ꟶN’™f³x $0jÕ|ÿ:²§"em5íM¾•£€ƒZÆÚÖ˜Cbž‚_ÞP/òÐ<XTdS À)::·alá ôáãËÞˆïðâÊY#s žÞ8V™1¨³ÈÅ,ê
+b`p7’Wâ³Å¥èíEQ-š©H‹+¼f>€r”Ö`%‚Ã@ãç ÷¼JpÜi‹}>’ô %)MÄyð˲®0íÞÐ9<~i¤Õ›+¹±²º§DÚqlɇç/^ÉÚŠñ„þð)ù‹uòPÀ Y÷Þwâνâ^õE îŒÁ
+MÁt$rɳÜk>^Ô˜1¦7WÁ~1kÊm™
+u?‘1ÎJz™ZÆH|•4.Å¿¼w8=)öÊ6ŸÛœ`<9Q¶ûâèf²B仄°›uK&dÓÈ̱#h,ßœ’wøîëÐ
+fê_~Jq·ÚL,âÔ!0ˆ¯íUk¨•úšª m KgÃUvw¢ˆu=•pí¨–öœóé1íKâùQ_” @Hò%¦ùûÇ)’Ü ÛM–%úxQÔñæû—P=Û nˆ9þʇûb—ŸEt³•ðF’!ø£ýW9Öyœ¢5èã7B%£Å{ƒÌ
+ ^`nŽãjœ§Ëº™œe94ÌÎ)…ì½¹E—N¾y»]j>~q¢‹AìBTaŠzÒdîÎO±Ì@»úñù¢ˆ¹É9c|ô…L@—˜ÈSÒmTWz¦ž~W"0I „;Dœé)š7¡XHTuØâ„Ç,Ä#^ Ù:%‰‚Õgi7ýRV=äÖLÿ­@Û0
+£à¨³³Æ”ÅÀÃ2Úùø‡Ë9(éüÂelú‹û
+'¼[k%).9)Õ$h Ø*ÂÆ1±qؼà êB×äű)Ø1•$­M`ÔÅ.ßl«Tf8¤UÄŠYˆ’¯³uø/k(GìuFŽ< ýIcPBhÅ?zˆlÚSäg Ò$I¼ŠxÎ ?‰çµNSjJS>üÃNÉãq+Ænì’,¿_9µ¦Jxº¢ÜGÛ–zÈnÀØ Ê*°`Œâ1ËA)üˆ!åR©ï{£<` 9Ï Yôt[eTºËècßöC¡K0/.¡ù¢„ç)æe› _Ä|cH¹hŽûÚ×D›,Ø/ÛV„<n %×Xcè‹öF/m~ÄkX+å?×µP-3(Òî"¥èâÄÓbë`®*ör„VLLxÝÀ£
+¾–Wa¨Ç‚Ÿˆ‚(&Xîu:)CþˆÔºŽÜ•Ä”'ÚÒ?€D9YpÊnØÕåª&kŽTÌ
+ÅI9‚·Ñ€Ãñ¥´Í AI#òódžmhÚpˆÍÞ½<þ[
+¶½½½(ìc †{ƒ¢*CÆðC|«H‘‰bLõdåYY—W–Bï«Ãˆ
+cVAŠ¦ýôF<† -.,Œ O¡ËhîžnI·­à¿8ƒÂ¾x¨\ØŒ-(ç™ÆÉ6É:Ò¦~°W‡ñð­'§ÈÞýAö£ÞÒ‘7zL[鈾#K §Hâa¾š5
+,—£WÂp4 %êÝ¿ü¢¨<†{RJ³‡¿Ý ¨#rÄ$lÀåãlÀíꎹé )ØB2¤!ŜǞ‹ ¤™qeÕ‚4oM`«B¶)' ùo z³¡{E »LÁ#B`)Ls³<+#tV‚¯ÂðËîŸðÊt ô`¸-GXŸX½!ˆ3Ëû4&\iDãoxàÑXõÌU•—¡[Ø•UüÓoLë"oŒË"éü›áïEèf, ‘ìôæ6ø·ESw¦¡ÓÒ™Sƒ:±ÌÁÌ%nÀ¥S®ÃJð ‚
+4}c•¸Áa&ûÌfË`|¹|ªÏÓ“
+Ò(õ©V
+Wž|^û|Q¤žÓ5ÎÆ8¨$b÷5v¾ æé,ßLù}J ñEÒ}ìA,"îÀ÷X‹wÛ‰7ûÌžv È'Ebk«žŽì†úåäjX‘o
+]¸ä蔬×ì¸QV¡ÁãÕ:—¿Ÿæ†ïV7üÝ›sGñ­ÃÐÿ}¨÷ßLÌÛ L¥=i#¡Åü¾³Ü§[0KÖÁṎk£„f˜cäãé…JF-²é<éh~hPòàÅŽÔ³(‘3,t_í†÷寡A Ë‘Ç“»͇$%º+ÆÀÑ2ìC´‰yG-ábñW—\Aè¶Õ„Ú8PAºÝ|' *[§¦Žßà€vfnk+Æ!UÞ¹Ê~rp‹¾=ý‡W$¿ßã`7`³m–%F]60Çš:žcÐú¾:ÃXÎxØ ¬üBMÝ]fº¤)\´l?Š» Q‘¬æjrÁ÷N\.5y²ìå]Þ… ü6êM–(®Mº dZV˜Ü£ïAK¦NN](EÌ9PÆEŸØ0x
+
+4\[ìkÜ6rÍd×]c’Ö1ÿ{î“W8ÒÐŒÍ?§¹äýZ39²ü¹Šˆ1Ê1Úx, A0*¥˜ç¥H¾fƒã÷:£r$°SqêÄöt DœhîÔ$…¼ ¼€‰EoúÒSq+‹#ò‡8?“R †–áËœXâÇ)­¤Ðº1öXákàR Å û’ëL8#^Nä×i.Ë'1„Eæó?ÊË&¹ŽÂ'ðt‚ÿ¹Ö¶oÑ[ùþÛùd9Z¯PaÍxz¢GN±H ‘ÈŒÔB4 +‹ôPsô%ÒYšMÌFšâsé€ÌÉ Æ—b[‘Eëɯ½z‚ I$Èc $b¥’D¥fŸ¿лµ–£S¢D”ViDÕùS>'VTMRuRDh‘ð
+:^ÄvŒ†?‡¯gÿ¡x\91ÕË!¬Ui
+ܬ,fB‹£œ1Õx!¡L@•ƒ¶ôß\Ë5¦ä4 ‹åíÒ
+vÒ?`ñJv´R+%%mó ìH©ø¼Ž/×¾æ&óŠQ0oÊ v… 8^ˆAØ;ç ²9ë6­®É”†ç@34Ñ(~:/B34œ }ÚwyHûÌéœ2žLr1v*· 0@¾Wë5äÈ
+™‹ËÔ`Ô—٫f)çÂómT(û¶àÆŒäˆ+”_ÔØPP†¯œs8×O®¢Éa³<[O> ˆ‹†²Ž,[Æ…Ó¤Ï ¦ŠE­åñõ€Áâ)Íj^ì Y²èBãä%–±RŠ¹ø’bL¸“Õ‰3« Y/9í… Ñ65ãp|óðQ'¾ç% Ì‹ÐÅŸ8v©éÚÑsdls ÏQСÄÈÜvÝæ»ÙÕŠ©äÁ‚m¿úùñ¯°WWþ™Wi(-õ¥uÕ«—dg0ïIù┆¡mM’Ÿ]ãï\ŸNcÓH²œœ7PÐðÌBaGófÔï97Ìl ¿ehd·Kî7z}ýdD}’³\}r#/‚uÖ%zX1­¹ú$ »jróÔ*-BZqÿˆ"_M\Jþ¾ðsiÏÃ÷®x†RB{–,ùkÚ›=®ñ‡ kv ØY:Ì¥m.‡0²åRûã¶zÇTàf;Ùb÷P3XkýXŠª}'7½ºŸÁ5†];N¤Ö†óØŸì°Ì±d
+€/bÏç6Î!{@îÃØ'h… ˆ#*¡xæ¼@oœÝUÆÃ2Éi>§–.Çhîÿô.ì~A +662J* óÛXË«¥@öÓ K™ý„Ñ-óᣣ¦tñçÆg@˜X¶
+KAþÛ©ý¥;†šsYæ«Õ}Ìý2äºN$ãù6ÂGé'$ŒÉª†B§~ïÅDáÚ |ä ¨ÄMƒ[“´©æ¨Qßû;ïí¾òΙà&ïÜ _tç° U’[ˇ÷I0½
+÷ftø©Õ ¡@ ¦ŠµÓó7b¬òJ¨VÁ°›µ?=/R:Tjí4“)!ÝùÒnL™Ydz>ExèêJ冎ÅA“Ó¹LfûÑ=íA7>þ• ÓØ ã8zñ?©z÷}Êe£IMÔ©#B¼i…¯Dk/3…G.™¶}ñ¨Iynß+!ˆ‘^ôŠääTfÖ¦{AåÚÏ}ø»:1uÌñ†dåC¿ûaç 2¦‚¿¼¡z¶süÚŽu2P•c*mè
+`ÖÚ† Ö<«}ö6Ô»¥Qw¶Êõ¥¦ 4äPú–v>QÆÚûÖ„¼ä"~áYüT»I»=ß8„ÍÅü¢i 1Vh•ze TO•¥šÙ´¹_U”ôÌ¥#ÙþÇx™$WvãPtÞƒVÁ¾§‡¹ OåýOë\T”þÃkà§ñ‘
+†Q¡h©ø‡`)^»–ÙE˜}£1’T-N8ç29u¸{‚œª îþ¦ä›ÁúmŸoaçxÈ‘Æ—¢Ï7E M¬C
+²°«[ 4 iÀ§Q¬„í¯‚°Ôð¼Šg&–g­‹qÈÞlòQÍ>ðr ˆŸIíBŠIØÔ¯IÍG†˜š¤2æ·’³7ŠÛ€Øï0ç€Æ$™É‡*¸L—9æ¿<á° òJ€¼ø†‚v4 ¡¿½û—2à ás.~#G‡
+¤¼ímJ6ñè·{À¼°ïõMë±ñKcNo_Tôâï(×`'׺%
+ýÆže¾’R—•€|v˜ù·îþ†ÑWZ…tÕ«.,’Eɺ’ño¬¤'ç$9dȧ)ÔnuN~W’M¤° ÜÐèuaúX ìB‹Š>½dlŒ6×tÃEl€ Ñí:]‚Xê<-iä ŽÝIsÉÐê
+A§®ìÉ`Ïêm2ît.¬ zàOg&0<²s( €Áh¤öË7'ËšâCÆœá8_JΗ8[—¡¼ÖÃsØÀ‹½•Þù£È3P¼´À½Ÿ¸DG¤=ýSȲ6p¹ëÊ»NĉñGɱ¡5SÁî#¶™k€EySòÍ®ÊÕæ¿ù [GÄe-áà¼îÿ׊$yY2MØ ‚Ž03ö0—Ö\h˜ä^eá‹• j¶;ò§ïi™ë:lªçÂxhÍ‚/¯¾Ve¦hÙ€ç}’l¿¬š;3Z 8ÃÒœ’©œ$ðO?e7"ˆ‚’OŽ+òaMQ‹-AÑŸ7Elyt£…Ÿ§¯¶µzýJ
+.k1?EFehK;%RtúEDrq#—q¬àŸMŸER‰‹é¢ˆàG#_È/¸²$ú£¢—Øöù¦†¬•‘‹(i—f·Ù% É{¿”âꦕdL|Ïh«’°µîN‡ã’L®tW¸GÎ(œšWðl¯D‚’©
+`Læ%˜D¤Tˆs= ŠH=6lª<óÌÌxá?)ó ¤“NØFWΰToŠJó;hWLàéÛ€ÿ²ä+LÌã±
+rB[X%P"ÞKÈ!F*bÛc^ã4|aæj=”œéé¥è㦇³´&C8ééTD§è
+΄§BÄÌEvoûkÈäÊbxØ 6¹ô­¨éÖ€Fš9”Á±e,ém “xð—fÏï
+ȦɈ•}æ„ÈÀ¾Ö¾EY!ó
+Ú€ 0ö…ö‚rm–zOSü<DZö
+ìC·äFK;÷ä)Âð;¾W½C\Côµ=q5,kÁ`âb é7ðÌ>LCEðK(x]%òü8xÐå°‘?b*]zä-†άi±ÝŸéˈ¥q8¿i*ÞŽñ`á§Má*Teaí&Œù®v¦ÇïðÊÂéÐŽ}2ú ¨pº(U²eÑ¿b’=¤»*Íè(š>yÃRƒk
+RIlr;(ŠÌÍšrÏ$± ÑV‰Ì'ç—Üœ,¸ãþ±ï¿EÓñ܃tl"çTÿ%Ô&õy˜„ ÒK™G§v½ “,¬H—ÍŽØZ‚À“úTùuŸCÑW‰ýü‰ÌÄiБJ R(ÞkÀßJ39þ:åº<¿ƒaÀž÷]áj¥P¬ð›J:\}Ë6•mf˜Q‹”M?Q ±‚)+WÚ¡ä÷<
+&¨ÒDä¯Ü|ß[”c†çMÆJ¦@ yÄtÃE¯Òá¶s#“™‚2Vn¸"AÆ7²8ºùXîü'E×H0kALÂùõœNÕ)«¸{[$E
+‘Eæøzéd/$‚Pcľ·Ã> ìXNÈÏÓÚ©‚Õ
+$XbKÍúûVMsÔñ¼Xí¬4UóÉhý™Ex^(¢šÛš0.E‡©_{-úüÉÖÕv£
+¼ÌÌIPÝ ­A!¸G`*ŸH 0‹²ñBœ ºÎÖô°!¿çIMÿl”–™4´{²£,ES°PI¤ˆ”…/ ”ÃMu–ðë- ե%cØX\êøþëR&%ÌÄÚšq³Ñi!<
+Z[ó¡èsAgÀ cšÛéK Á$Ü€•-«¤hcy5.¥Î¬a…¥^ùŠÁ+ñÌUNëÒ]l&À´î'5¦Òù)g•ùݘ¡€i¬=
+3ÃbKwØqˆ0T¨Ó©„{
+kûÜ
+Q¬Šº®¢\„k'X ds#žÚ—bvé+Ê qS>€Ÿ°ÞõPrb§—’c‰”œÀDŽîÜôZ¤é
+,Á©ÔäH+9«š[
+l;¨¦ÖáøË´bÒÚÀïô1JÖIü_LTj;G^¾Ã <¦}—šñ$dͱ*”ìÈ9,lò„(á2Ù×E‚Adž;½Ab0®ìõXÔÄ ¡¢F^½éÓ¬±É|ÛùZª$î¯8 0,KßhqéùPôyS”ØE¶1ƒ&:|¬þU(W÷Ë~û˱0+ZÔÚI4)Sð‹þr¦&%Ûu¬¢Êo5åQØ`·c´øÉá6ÕFÓåìÖËun-Õèž"ƒA»ÄÈbqþÛàoŸDÂe *$’¸
+gìj´UQ¥%!š»sPè
+´€¤›¢ú`BâØЗ¡ÑØD#ä9!Î"{®ŽÆ¡‰¡Ü|Éh3dŸH˜ç6A—,’:yÓáåue
+?˜”lݵɦŗ(á¬]l±áfÐiÞ¯c[$wÇUa›¾³rco㷢ϟL÷™PPÄËE¼; =ÄÇ?çš¡´9Ä¢*Pˆ2-f·´IË,ª9Í’$/ƒ”ÜÒ²À4xH´ÛÔ c(|äGa{ZX‡Á°ì²”àóÇT:énéc³âÀÕ­’A8ò,íP¢£?²^¿˜¨ïßA¿q©´›ä—ü(=XX– ®zZøw‘h]£Åû¯ÿ/sÜØŽ$Š®€{ -ƒÈ)r0_&Ý^A£-Qfï¿ÏÍP½Ÿ€
+T“H@!`‘,E ;„©&ÛÉ°ï̪¤9qN›ŸiVÒè×´ÎC²´ rõ„Š ù4d ÷U_OT¡óÄ›rråĽã÷ÇÜÇKê6€÷âܦøVagÎÿÙ§ÿü`]>jL7ú"÷t?ìo/¤k(õædrŠ0L¨ÊQ×´‹Ê)#2ªŒØ ‘vM4–>ÜåÁ_©D£í*ãÞJ@Xƒ}æ.‘$(Jh»-¿
+MŒ> ÷`¶ÌW õÑ.bHR—@$Ø F¦qŒeä[|"L:¯Êè‡Ap†µïžË? D#üŸæ9E G½X­"%Õy„Ñ ±¥mP¼½lß!ht‚ÚÏö‚^úþåMG3"¶8,ÏËðµ™±Ë,éÆIp ÷)™Óvê;8înU¹ñ
+6ö›TVˆ
+'aCû=ÈQG<zH jWR÷䨤ëP=QDV§„vcd(YÒ,›t0dI˜¸×8¢®#jQnk†t´#Ë :?HIDÂÍ
+v‰ $¤d§£ôŸ€ÔÖªû‰b¯
+tf%akÍô‹è¨'æ#Œ†TÀ÷ f±:ø”¤Kö{€Ö$œœDHí"Bôë7é{á;C¾B ð(è´ Lq–íí(ê}ð`€BùÖÊ *9‚ayc
+‚x~¹!
+&ÁfV¡ì­V`=æÏòù¸?!’„ÿ¼Š…A0#nE óq”Ó$Ê×N1œd.“¶}Pp]]V‰o!¢L¶‚©áãœÎU· ô§df·-,‘uA[ [±ëñ ¼4_\X"ÓK°æ¶x:¤Ó°’¾ ˆ!ê1¶ç8>–®æïöîÐêͪh«°ž(vˆèÁñh*ø–Ab.ûx8&  ZåûÅRØUH…“%ÌxI
+''é®,ò7½t
+a›"š2ˆ1Ë¢C†/KÛÙ’£„À„.“ãy‰“¦Øh ÎÄGŒa’ÆÇõxA_;/•µ¶í+BŃoƒÖÞ¡O¡: iÜý€ì˜Öt­fyT,Z=öüA’±¬}gN‰‘÷`†olÅØ õ…1\{‡öc'öM=ÂŽ]ªµ5¯——ˆ55eLFÓ=†©©l%êÛçhž#j ºÛšŽŠTµ„çÊ €
+¡5UVŸÜoV±LµxdP²0üTœÇ0\Ò4Ý®3¥hNXŸ€ì…¬›Àfˆ½ðæà;\Úðòá«Î[˜á) {Ì©w!TAmßJ«-Gì%=øòÒ¨uÕ=‚g([ïÞk`Nd5K_JknR·nºÔFÔ*‚D¢µ}̵ÀP+K/Ý„Øwûô±î¹tû~Èubœ—\ÏÍç:¿÷²Ü—àVÛß,ÓÃαšC’úÃß9ŽÖƒ´Foç8ó˜Ñ`à¤.¼á!”~)Ã߹ɽ˜Pð±YSD†Ó¸v¸| p÷í~ÆëDy¯¸ ¦ŸÍe¾½¢\¶Ä+í=ä¥EšÉ "oÛ‘-EâÙJq³ŠS!| …ÜÜuSÌXr¤á­›Är
+lº¹]º„x çsç1—±sSºN¯Sšë8þÍ.iåþø÷·!ÑÙöÎOpùü‰ÐÃE¾‡÷¿Þ°Ð1³pü<®‚ÕšŒ)¥2«ÙDÐ’_<œmq‚> J†ÌèÂ.Šþt®»}ýäMŸ¤Þÿõß·?þGž(¨™ÍŠ¶þ$2våølòÿ¿ÞjH®Ž.WHctuEK©p¬d¤õ»AHlœ$Ê*W£Í>¤WÑ„ìêÊôGAÌ}©s¤QDòº×ý0(i¨%%¬ h€€k¸»öã K ¾~R'U?¾ÿù†þÄÙÎãðjò›E?Î¥Œn®+(ÆN"“¼ … ";Þa”i
+fˆñ3ÔtLø LCÎ?0¼ìòaºqx Ø>)ˆ%z'Û±ƒp#ŘNfgƒM衾ç1±êºD¥VH˜¦¯ò ´BôO aVaßÄVó_ÂB%ÛAƒklò^œç@’ÀœûŠÈÖN—RÖM¬6¾‡÷Ø‘„UK*46qŠcÑ+¹ð0íçÐ$¬k^À§"Æeï"(°B0±&ËX>DYÙ)–,
+4Ô…Q4±„lÉ úz’K•’ŒÀÕì&¾ H#°¦,i!é€É¬†÷
+üwÑÝy±;Ef™Dïép
+†´ú¤m…L)@¡qŠº CÑ+2 ¹ÆX!|`ÙGpê*¬ hÀZÓ¡šVF‚nÌeo+)ð ´BøšïÙv8C˜5GC@‘$Ó`RÓ}YÓ/ ¬YÍòEY¶ˆv÷yŽŠÃêa¡¬èŠaé)+!ÉþÏx™%irÛ@øºƒNðp{¶Ï¢§Ñý_ý%¶§«Y¶B3šn7$rXM¼ÐÙêQT°e]¯…ü©]Ù‹ÄlÌ(醨òáàÝüVÆ€òï*ëáÕAeY%­Àªâ_]²19zæi œÅ2Ío5¡š.„—%))åÑL z»³x_˜,•dÆŸ÷†F Ï°à÷ Hæ$/Å` Á°:¡²s—Œ”Àh¦ç¼8üÀ‘9tE¯Nü¨ÍK€"sG„‹c«Ám@gÅ‹
+b#Î^ l Ø&~§J•0š|#ÿQÇ‘¾¢¯äñ|£±Ìl¬²{¹0±âßj°HÊ›­Ã8Ì9SÅD«„uQ£nV{÷‰Éˆã˼_+tÆNñg”/E ®
+J °Ý ·ô&ãfÜ4Õ°±²Ÿ‡/§@Ì~ÁÇ¿ce—tõ®ÅÌÙÄ>"ì$’­D‡¡»æƒ'’ÞpúÃüÀ“L&›%ØNæhÈ4n‰!¿‡Ûƒjeu˜ZµŽé¬ ›†ÊBÙ¼T:$5{PɤXx"„XoÙù!Lî³)=e:l]Jb6a
+9ü2Mw ¼ ÅT;t!âG(RZkà헎褓—¨‹×YÁ®¾¤¡µKP÷†#¿ü
+nª/9Œ"-B [ÿU£ïynC¹2p»NÉ·P"ip'æÚ^²¥`;7„Ôì}Ϩ”ä™õð-Wß΄`ŒºØ`àó­Ä·B¶± Wlùe=]Ò`Ñ™(Au…HFÒ;Åï·md^Ë%°øV08ö›ØÙó)ú|(
+ â€9¯šLC€ˆÄ˜¼/ÿe8‘†š‰µ±×ÉФ3²õ“
+·6ÀA@ÃKДX~}Â\™—ªg#”D(•ž¬[ÀL‹ù•x5ˆ
+¬²ãýRBî%ÌÑò”_Jñà¥j04Ær½­ó{‚¸—àtp
+U*QÎV’JrbîíÒô]aü=
+—IqrÑt.ÅWh=£‡šu‰igTas á.Ô°ó(ñxJˆLfüø±ìÌð‘/żâÀÈ<ØtÊ]¢ÀT g”©½°M#„ È˜…ÖVt[ÑÉæÅV\O¹ñjåP(­E Qó’ŽÝ$ãÁÏö‚QŠ¦„HÚ?o@þµ‹ ‘,kbÄJ=z¨8k{3ÃîÑZqP½ð¥¬M´” Ì!‰ÆsG®Û`'ç¸:½ ù£š@'™î±i¶²D™”Wòáš,Þño‡C Ž &Yø÷Xÿ¯‰ê Hø_ƒ» ‘‹V᧑ž~îU÷\KCu(v†IÜzØ2"
+étL×Kv£Æ}°J;$ú=»Á“Ó-†(Ÿ·yd®¶N¦Ú»ûæ„tYi$)ñãÜè÷T¶Ù³)2T°Pt€¤UÔËW±Ñ“‡`¸‰¸#s–C a4«Xk^+*G«±tîˆË³èa7@Oñýô¿þÈþ›R±ÔŸ÷Q²o
+S,`  ”¿wQ⛬9T¬F%¤A
+ —"•ÐUtï
+” 6Ï»Vï]¾pãn’.â¯ÙN¢hcJñ:ØÞi =©ïe€IIz¬Ó4åx£aÓ+¡@†Üç®@kÖÌb@c§ÚÿjF®‰ gæZC‘}N§Ѹ[ªy0š{›½”hÕý¶¹ìlõ(2¥Rè
+F Ã’#çðÄr«‰X×|,¨AL˜8'“[0x,ó´Ié†ÿð{s½ÛW2˼”øVSr*›=f}Y†x;˜Ê(É°º#öç èƒÝ ØV“³JFæ àL¡›]\ø¬5¹=Î:†™«àf¶x>ÓÐ69üì%ÜÉä G{óö×_ò1­ùƒà²4ÜÓ|ˆ6¸,>lßû^§"Nòåps¬ÃÎâzo»H%0ÂqVÀ.2ƒI©™“dØ”7}kû
+f¹ú…4ù§ßÉ^J`!òj‡*[?[ýoÂ7¥ÍM@l2l
+°9H ¯žåJ¥c;–ñÍôte\ŠTÂÒâF©24ÆŠ\J$s^Je$™óÄû]ø’ƒÏõa†ñk³qŸÀJ¹ê¦Jäoë—êBq–€·J¨Wp„`* E¤g3ÇV]Öš}R:ˆèæ ‘Á „K2D´šéñLìšÜ]±_¯1ç $à.· eâõfìdÚ,ßyà*É? “óø:Òibê±èRÐä/¹Åk I¨n>ÎV"è+ËÛ'†$û2&[BL[€8ŸÂ|~ë\€*¦³•ÿ0^.9zÜ0>Aîzµko}‹
+þJv#_ÿ)ñþÁ˜f6Ω{IÓ=42cOç- AcØÉDeDp„'Ïa¦äãR{'wP†déߤ-‹EEá÷00_G÷à: 1`5~àú‡T0‡¸Cå”hvv!cZ˯‘j}YåZÑìêQ2ÿXÆ{»%qÙ&ÄÁ™Ñ¢(ÏLJôOŠ«ì&9
+ÙÖ9AÌajÅri†ËÃ
+$Šgä…ßò# {‰¹±¢ íÎØÚR(g‡«Fé=¥kruÚKuƒ§gE£V±Ö !˾å©—id;Pý ùÐýŸœ@äÍ!çDW•D¾Ž ƒ’"SƒñÕãaäx„Ö&4¼¼AÌWö0%Q‹_*[„q2m'üçnß¡8Qj#=*±pÛ¾×ûf ™¦‹)™ÔÈv˜;3Óã(âû’9cŽ{
+sÏjÁˆqÉ)²uõ)ÉÀGu5¢URü¸öRD%зA ü30]ÃÑÆܟц””Ìò
+2Êì‘Y¶›Ý ~á
+ Æ"8<û†°Kf©\w^Éž[³Uâ
+³ iW¨/i”—ÓúYšǶ œŠü/‡â0\OýÏSÐ<™©Ã>{l%šÅgQºÜy
+Ë#×#Ñ„Æô³)WBæeZ›wpCÉ|Œ‰˜ÞËb*x†È¿ì²À0g§‘Û¤R( ÚwjBÓùxM;<,( ˜\ú½´;\ˆŸ$!6èÅÕßé?D!E^‹l¬¥Uib„Îáæ‡ûê¨N†à×éxíP[ly£4¼Ö¾£é†“ŠŠ;aWæ°)3°•
+Â*)¯¸Dœ¨p^´J‹ÂÕã¨Ë<Ÿ0Bˆ«rA0Ê@°£0%‰¿ChùÃéy9¤(-3ù×® ´êU°™÷« ”Ö†ÛD/IIª`÷+Wn%XâÊœÉ.FÄi2™¤Z˜YÚ²­à ºÑø ‹ 1((A±×ÿºeÀ¯ÐçÖt„ä®Å2­\_ ýâ ºÇVAˆaxc´"!“s øœƒ0¼´íåá?+Hô€ $Q:ɲZ_¾bá`“k/V<Ÿ¤„ø¿ÅÐLübä·£øL'-±ã¬ü\0’dÖ®M þšÄ¥–æôå |±è¿ËU‡ ‚®#³Œz¼ò òxú t+áòÙˆäÖN ¬ævyID^FuäÌÀDˆ¦â¢ÄJN[R*Ƹ´·(³8ÕIòL*½†‡T‘‘w±§B§LP-}½@>„2ÚÉP¯V¨ìäíl„ò$ïÜ'ˆ]\sÉ¡¥ÄÄkþæ1–ŒUbhÍ… ¡ÃErcø¢6¬PpM‘fÕ‡¸‘K¶M2ÿLõ´ßÊÐ,‡3»ÊOOˆŸ„ÝM¤õ"ŵ—ïLþ5Y‡¸À
+‰FRZÛå¬MÉö€zÍÏuÔüÁJ¥MÜÕöËÉ'Y¿×Í`'5ïpk(žï!Çá*\–A*ÙW#x׸ k-õâ‘kŒ8ª.[òt…
+8MÄÞó™²!4½„â<17—9¦¥‡)›1#ä§A‹ô6Áäse)p3pÓÂÙ”à5S†%Wk*;›»Í'è_
+H‰Œ—In¤9…Oà;øHŠ¢Ö¹­[4ЫÌûoû£†@:¬@0
+eçû5P|ÕJÿü磅=JïÝ]¬›ÔÏÿX)0/£•ÑëX }”b:JŒ¦C~ jÞZˆôÒ­ø›í~ò‡v1jñù<j“·ÕzÐ?ÿûhݵ‡Uuíª&‘(­” þü3A2¼/ì‘ úÍko¥v)í3!fnc”¡üÂôÖ=
+ªÉ„¸ª‡ðŧRчS$ýüïÜ)j¯ªV†˜,P±Ñõ´Ü©=XRZé#¢ú‚˜«HsmMë„¥‹nväVíÁéˈ^øs³Ž÷b¬ÃV«8@Æ5ë0|&
+µq¾xÞ»9ŸõfEÅöVTK¤¦»~ü®ù¨Þ{nåânźÝõ“h<Ucž’ÊHžn˜.«tU÷17ò‡(ÿåÈ}C;Qáó|–¦w©QVïÐ(l ÂéŸÎ-Ö¼¯"]Ä¥x~·wRÏ·žÐl̲ jëkÞI’^Û‚dÏðc#êx‰oŒÝxí_7 ½è5\T’Tز‡Šø
+´€ŠÏ»1ˆÕxBJ¹ï®£rq£ávW˜Vktu§S†N ºw‚‰#IL£ž*³4¯-VÂ)PÊþMD»A*6¯z¶¢×FÈàcƒ„ëñZÍ ÿâ M$«»èP>äÞÅ'¤i3ùyzj@CJi±·ªè=|ׯR&îÎU7ƒ“ ˆÇèûâÜ—fKzè’ ôcÆ ²Å‚…ÀЪu¼YŠ„hy«ù…@!ÝìøÂbý(`•1¤¢ å蜫¨²»QÅÑß"‘Ò¹>Špš§ïꨦd¶Î7K-Dþáyì[«ÒVÂ+ûÞJŽ˜Ê^»-s¶oÕç:ÂáCyÂÞû•Y@`3G ÓÃOÜÂ5aQÏ­Ò!‚×å¶{«pŽcYA_GæÍ-jÞÜ÷:à^+Dç3HM&Sõ_7Ð… ú€Vžä—w,þ*éX£ãkºmû²Ý@h·"¶Yy”vêÁ ä<p*ŸÕIA CQlzfÛ„§°sÎì½Ï›ÍÖù”––±Ÿã;ˆ§J›miÛoÖ‰€ÛÖFÁ]ß@†…#-üÍë›­„J÷‘n,ª{²J†­o=(9DÃG¯)y0a“«¦žÁÇþ¼ÓwÌW9¸Bþ–•;äKJyw§/ •xÊ#éèð‡»É<é̓|ÿ3A†Ôy ›a/ „KhC$œ³m‚5$Ëû„àÍÉm¬–tÍzú Þ& }<B;ÒŒu¤¹NP€‡:šÁç3!î'GÄ6¤#Vè²dPš†ˆP0vÝ;5ÌÆ$-¬ˆŸ%OméÐdÜ2{™Ì¥sôŒÀIJ!Â
+¿RÖ¾ )èe®È£ÙÃ)òܪ]h(È+º4!Å[¥52\%úX*HCè¤Pß—úI½léq¶z½*Ôå4ˆ¡6Î<:ém\oõ¢ºk+7jŠ[yº
+6lmAø
+´‘eìG)3–ÔtºyRѨ '|·CFŽ”)t>Ç„ ëØ,ëL;d´1³
+>f’ù‹ÄK¼9áÀIØ÷òØÓ±©òpRúÒt“†DA6IY²åŽ€Às‚Â=7Ȳ81(jÈzPB¾¥ï#óÖL)”t¿9!ˆ¾¦Éâð“†ùÂy+‰Ó;Ø'â2g,© ­0›Y]’Ì5.ÁŠ“¯JT|? gŒÕ¦UæV%.ňô¯œÄ°¿þf9Æ"íÚŸY8þ@¾¼Å2Ü‹ò|»ÊŠ"ózŒ“ÞöV‚[RÛm¯Óøgº–¶•¸’8'’ ájë¸BÊ™ÿ©"Åß[½‚n"k š•@ºnÅ=ɼĪ1‘ßÞ\yþY[qcKÚäû¶ÏÉX…Íxæ…É_(†p;»Jf”åc¾Qä|·ò¡BîÙux¼\™”­/ÌÅዹ§Ãa<œò¿² ˜ƒ(HÝ[ŒÈ<Šg˜èËÎY¦îÆã±òï·r3ÍŠq¾¾Ú„ÇMÇ<W¯ fÉ,ä)–°j£$ƒSÎa½î™Li©<Lˆz윘 “h(ïKv =
+ýê~¤ ƒE/})œ§ï9Ò('[ð8<^¸g”É%øÑ,;¡ˆ`¦9ù\¤Q-Ú­=ñcdü' ÑÄíÍ#0š9«·÷¡
+7—‚¥CòÍ/ ò…ú¿4ËAbBÈ…š§-¡)§$š>p%õƒò(R?%Ÿô•@:’ÇT‡EÕ*ý2¬&¤@rZ™Ü`ztrxêɈyÝémúÑ‚ýâÉ­/ô^ø ;ßÖaèI‰ä¡ý8ù>¢‹ A’êS¹œwíÄ3ÑÔÞý€|Vø¤ Ü$R<yßÅ!²d¸æ­eB°íÂ@Œ“]?£?á!y5r‚ȱ¿§¼D`C$ä9Åo„Œüp•TÛ„Ô<‡˜íyOæ0—“Qô\
+³‚Gè(1j«¶sËúù:1s$ÔÄNÇyñ—äú¢ó$ÉEªL]º€^™a×Û#_!’Z~J9«˜Kãl³Ûo  %”µå$óy‡|ú®W#¾€HV#G””9kÞ‚ñˆÔÝv¤¤CÑô
+ùú7ÈËsç8MhkO$¢óÖ㲇§oÃ÷ÆÌŠåeÊG| `óø
+:ŽsAmÛÒ)ƒ@Щhíê>aöOªô(¨«´v”šN7ÑfùrV(‹}á»ýkß'{CÎIË·³tûÞ;£ñÐ;FA9gÈÃx‹}[±5\Í:¶É ;‡‰Å—hµ{ 3\äj÷›d9•»¨aL!`:/·#gÊ|-¢j 3ô “´¶Þ”Œ¸x̵W@Q•EaŽúŨâ:Y>»éseC¦q1ßBÛ(¦rP ØzØ›™ïNJ¤Dƒú÷„Þ“J;×–«Šâ!
+ ÞGd†8“CÚ8ï e3Ø=Îác&ÅÁpÕKt2Åã=ÐþÁyIW*:õm ÓñæÄÆ „aµ.#çB‡‹ÞÿöQœ"‚¢ ã¼\Ô¿ÔÀêÖAÔ.àDZìƒXaZŽ«~N™ÞÐîÇÜà6¡r¾ŸiŠ«ž\…™Aþ¥3£ûL é‹u®«Gmðï¿éÚr5‡<€qA‘·†ø\„€4ä°£¯ÐĄ̂޳¢žÌ&û”aÀ°R,M’eŒ›:îž/lÊc¶œY³ÆÐó#ª|Ù—_Wéà{H}„’ÍO‚ýð‹8QââŽsJâ  *ë© ‡½½ë&Y¡wE«º#êm~<I´áÀ¡È*Î8ψ”rð¨Ç< 92Cl‹¿>$_B£JE‡Ði-*‘”×┡ìÉ¢×ÎÖ‹˜Ç³{éÈ–«x_eœúÕ€á—|z’*žf~ q<oB;jŠd ‰qG,…ø
+³¸µc ¸C†l÷ änƒ^ÎAµï†~’[|°t‚f“e—AtÕÆPT~å[좯¥Š«…Èc
+mÚ…-àMx ;gËX`'öuô;ä é»|<ƒøÉ”n¢µ [(Ã3· —ÈÒ zõzžOÑÒæRÞA` œ Ÿ¼N•7ìaV/@T†aþ¡2?‡á²þràÕÎWsÐH=LuNy ²Ý˜1Â4yܶÁƘW°1üD’uË1á ,³ÜÞ õ)IÛf\E%ôÁ¬Ò
+—‰¹ß°ä1íhš±eˆW>Ç`%âçf¼a*Þ/¿‰a“±PM߃8smMÂó1l7–¡à ÒDû„ÔSà/6ôÛÇ ‚¨jÕlSÓ¤/íÐÙa‰i=Œ°´6þ%P7Ÿ!! /+3Ç~°kû£-äò½|s@vÒãøà}:Iii±ýD×y}½$îΡÖ*8È›eƉxt Dž«‡nNÂËøÛZ½ÝM‘7;l~‚‰À–2l|ä8épù1ë)pqm^ ±
+Osþ’‚ªÅÍUHI2*é‚÷J=»e£ ˆw2 ~øU ±¬§úæôaFZYßÓV›0’Ü€Ctz±Œc_²”_…â¤É`ãä$¦ˆ‰}èv ŠŠm¡<ùLÅלTóy1ž¢Ä!ÒT‰5†³! Xé.ÊÎPx^ˆ†‡ÎÇ £F2W«„z0d˜è°I`óE°sr'@XƒŸÐLœR|ï*b#Ê7>”wÃÄ4~l~B€ÅŒt@¼å¹?oóRa@¬%MG6{€”9³T(uæ›5CŸ{ë—­2 ‚Ýú8[_¶Aš1Ã7PǼÎÍ‹«1ô7ÒÅ(1PI$Y³^ß>~ZMd7ù&î:y
+—ÝÎé’>FÛpLÅõ1“_Uùvœ³ßo¿*3³Á[‡?ÃØo˜;z}Ô",‘<dÒë9EÑŽw³m€0D2:u²[Fîø³QýœÇÒauhGËÇÐBì½!º0þÛ9’Ç(ÐÊ–<7á)!©]‹›±#¨XBàÆ4ÚR•(0ó
+A‰m‹#šLŸÈÆ­ª¶™ðªâÂ+ Q°2¥”À³@JaÔJÔeb߀4˜µÀoÍúb$
+l¯$Àbƒ™iè­ï!™„ÑX‰Œ®â݆*v)Azg©hVÌ…Ò^JØbË)?ŸL²„
+‹òe¯>èµ~7K?ðTùó;E>£äƒXÿ£ œv›©º!q­læÖ„‘C’Ôœû—ò2IŽã¢è |Ÿ€yXËKÞB[ùþ[¿L”ÕÕ(GÛ MfaHüüÃÜ%(5"s¾:­Dá¬Ç‚\^î–ÁV#­CM(œ¢@ÍŠ˜Q@Ƨ«Ñe™E:
+ºw~€´DÚø””W  À‹ðZ!¹¹ej FÊÖNeÙR¢d}$á#³’jûZ'‰¸’Fk$´š‹3¡­¬‰•ª³2†mEM˜œó ÂhIÚPűNVšó
+š˜ÂaÙv§ñ(âÙ%¶“|ŽR-nm>¬ÃÆܨ*ô㥔2Exo”ÙûÇL— á Ö¿
+ÿK-Û’$)R ‰¡õe.*~˜îÐK¶:&ø©Âåì5J’<¤Ý7œŠpxŠA^ÛÖÑÞc* îÈDÚdl«ûyñT©WXûNœ Ô€Ëávˆ\²þ˶L˜Ê¦à©ww:Š¡"q.Ÿ­öcðPØäÄžñ’d;CoþR“£F
+:›*JÀé!Ì} ªM}¤Æ›YE>ç6ã/2㠠钦ߛÃ%8ed³mÕØ/„­ßIåEcþD h†d´1… S§ÖQ¬MI±ÑKFgçâ* þ÷–·x"ºHïÓ§oŪ
+ø×ëê ¡ÐÅWFºP@G’GOE40 H?; +di¿U\ï€î]óË<@G< ·Sm–×€Tÿ<ñÈI8ë)ÂýÈö¡2dPœýn,
+Œ!#`Ã~ '.ô! ®Í‡’ ýÄñ§ô&d
+X Ò †¾è"dA¥Ó¹cÈKÑ÷±ÉKœø6qö·mdÂ.5õ±ˆi•ÀFÙs”TÓ…¨ð©¢ö•b,Öæó¢ÃvB…‡ø×Zð¾ÝGEzOô›‡mLšìX4“DµË›@ç8¶ª˜ÝcI1Þm OËð¬¢Ù[¸6à˜Œ‰BNÁÒÖÁÌP·°K–qéSØ\%&F枸Bb’dÑØ|+øU€ë©¾N«1ªaynJÄߨp¾ŸO|n±ÌŒRía•b7æ}ß ž)Ð5ÜœŽ±1Â[>JèIX´Æj‡’Ÿ lÄÉ&F¨—é_„n Û¤Mñ4]ÏÞVI^ß$À§óÚN<.^ß³*w!˜¢‘<Dï×3äž•$xõVWIGa#.}ŒÝ>Öƒ@Á_’œJð]0;ü‡ñ\í_$Ì}cÚ¶áy¥2¦æ¤ewcýÁÈ¡^¸”‰d²ù~¦Éý&N‰×½®¨ ‡ÃGOL†)¢ZËÛÇ¢Q>3]d7…òέ­3L´@Ž¾¤|°;¶•Ò“ÎKo‹Al“¥ÌâÇõÜC•ïæE¾¿»%¶”ÏF…"ræTO5Ô^Ä_ÉØÿæ%-ÐQö>²Lòà¢5Ú,4Ú…Scñ~¨°î#uXFy‹™oøp)dYž²¶¶ïtÓœ"/-»#»ß›ä©œÄ"z(‘$Ë’ñ^®£L.~löe™¨
+ÁûËäÒ6úZ÷hîƒûñ8»uEQ¸ìˤeIhÀIÏoTí;ò#Ûé›Á#Xí,Ö´uˆnܹ*¤u¹ÒÑÒ±ŠJ£ÇÊÞê¿ý…ÙeV¼6,ÃóDSóäÒƒ"ç\6äT €ìŸ'yM_ú?ÙÜ0ÚüE/ÛýúäLvô÷µnzd
+‘S‡Ë›º_Š¾Eo–D„·­è ¬]|\tØîý~,M\Qˆ
+V¨>*:ج÷fÞŠ~}Òq=Lüó¯?À¸ W’oên’°Þ!Ä‚ÙŠz›CMZF
+ºaœû*IÄ D
+v÷‡ÑbX
+>ëÈðQEð
+ìPçÙ™¬
+AæÎÛ&ɘ¨Õ+F‡Tš$Ú¨¯(#ªE‰ýÀô_ Á‘ߥD݃ª‡L»—pkúÇôŠ)¡‘Ëpº—d™V;L¾g²…ŸGÓ±XÑþµ?p
+ÉECY¸߉WÄÌ
+ e\Á†Û"}‚Yµ®<ÐñRÛvæB2­ÁûFzW¦¶ˆºýB<1kóJÁfé>Ù”@ŒŒt4ã‘!Pw®ˆ•ÄSÅ}§Ñ”õÒEø-Õîtg«›¼Ÿ8/³,ÃÏÂVûRwî.NBOýÄï,M"Tp`¢Z~(ùÈ@ùÁ)?UÅx@á€Jb§œ~ç `Ç9Ÿç4?c¥æ¡È&|L¬Vÿ‡ñ2I’´†¡ð ¸'¨ð<¬‹eß‚VÕ÷ßò=KêèÊôÀÈJ¥-kxâÐÏ‘eËÌ5óìk·ZYò%7A¿ñ…™cïÐØ'J¥=¦H2uÕßö»òªá|Úðˆ2²61`ÛP:ïé‚øíéâ0§U9úE1
+@’Ir]T%ÈÙ¿Mw¿,†Fj)àž ð1™€ŽõŒa`M¶§3Òa¤‰FïÆ ‚ü%7Ö¶òr¸¯}–¹¨Iï\¼~HA¶ºÈ‡Í3¨R>0-%ÍQ?²c©°[Íf¹0Ôê•à¿„8èˆ+šœRç ­EÆ´·zÊð0Þd&ùX£pÙ¯5€ÈvKØÕÃ4 ÚAÌ `­¹°«¤e`ŠD·ZTG>ˆÖ8¢ p9Œˆˆ€ñ’ÈÙ!ph]³=c
+ä·±&ÚQMÓ±ù¥ 3Ö'ºÌÑ?F@ùtnC¬T8&å„ÚAeÌe1Ì*좇“?ئC¶Ãà<ŸWÊMñͶᣖL0£Ó¦‡ ü2†‚±‘ÞIéBŽÕb`8#ÉŸLê×|F¸:Rçà£ÈºäéB‹jó
+˜/NÍ×%ò]Oai¡­ø‚ï8žÃá^½çXlzÄ œÉF<Ø‚¤D§6Ùp¸a)˜2$‘GpŠ}Nù
+D—Ú´ÛÖhsŒº-X„í~€Mb·Ä›Ø“ b iÁ+1ç3g²#@Dø)¤%N“± &mËxÔêļØ-ä/ÈÛ ?¡Yˆ½Ú•Ã
+® <ÞŒ¢ŒÆç•X^‚¾ž‚ä‚6ži>X‚|FHú '„KÈÑ1µˆŸƒè:?AK­šcB(CV‘«Äö°s~ “†wÕN•ü`h&TRÜpLÞŠJó
+Jߢ­Ó>£G%ë™+öEšáÓ¿ÆS{Ü$øWñ%Ó%‘íWuýŠ‰C»
+`öEj,Ÿ¿ûì*_y!ûøß’~èírÙ^õs,„²˜Òy[ñ/ BMRØÀbÊá1¶~ýHk†,ðõºgYet³bH‘€|ÚØS³ïAL-
+~NÈR!ØîëÔ¹Œº×Ž9:Ôº¡D¸Ü@›žªXÁ7¡éÒ¥!ÑY,+µ…ýåê&X¼ú ]Œc.6p4Ê9‡¦ty)í^‰‘àgÈ?#ßýßM(mÔýp¯™Ø<ïap»‰!aJ¸9[Ëñ£8ò•<<§àI˜:Ôaj™­³{#æOpÒ­e8J#ÉÞA‚3VxKpu&;ÏF< »J¥me$’
+šÎìСu$3„s˜Âд3ãpÞO\¯ÑaK<¨‘>z€?L  Î5kF…˜É—ôøÇ’áå þùÝò@9¬æWMY±rq{£"QÀñûœàuáòÂÅ’ô-:Ì”U”ÌrŸø*§HT¤
+ V®,ú”ÈÉçyOžÄŒMrF,²~©Ú&:#-x.ß, YY3j:Îv¾]\é0N…ÎÊ=‚H¨
+=ù4Æ…uX.|õj([Þ¬.KP›¢ =D:>‰cs<×N@mîUACóç¦6ÝÆj‹è7ì#yù6žšd‘˹Òë+;üøŒÊ‚”’Ÿ'ˆe%!ÄÄ./h%ßCé]`@û b ’fp &a*p”-!â'ŠÁíÍq›ÿ¯,Wbû`ÄÞK^ ls· lk“OdgÊ9GL€£ÚºÚ"À%ÔëÒO€ÄO`>B¨!xËÜ«çv{˜
+ÝÁš$GŠ†*mê=ª`O¿
+þ`'Ð'gEo°{é !ò!H±v°þ„0o#õœá/ƒ@ÝÉF´fóCs€^y™QúAÒLI3rêÆ›Ð1Ú‚]ËQ?l Þ%Iö•gÕÙ
+ „KúÅâ!(Í*5ûÛŠSÝÚxBB"hQL/u&UU±Ð—ç"Tž*iƒ:í*Òb”‡
+I;;ˆŸ¡«x³èËqqÄ}¹µ.¾Nx³iÑÓ姴^[Q)ï ²e’Ä&v?9äOp¨.lÞn . Ää£äbºÜÔ1w# Ï¿À:" –)ùFÎQrlü²
+û|
+¼¤¼c#1´è ¾{'ß]Q_fUÑäÌr`’µŸþr…6Í9®Bã2 èÏᯂ`a#$¯ÔÐÕæ“Šø7PÅüÆÕBm“÷ ðŸUñðÔä ñRd%²ö² "ÄoJ0+4-:êz3&Yæ`Ïï¾£XÇš›žœà‡}€èËAºÑ€Lc®å*Ùòˆ it?
+뉃•â‡øé.4Ä~x ÀJÒ×>øQâ2µ¡Hî#šgÑ"haëœ.xá `.ÔiM?
+z\Êp,g;gdu÷öÞà2±Àä‚Ö=yÑÜ.Ýb'\!p~©4Sü ‘
+0ÁÓHÌâå2¨¶
+/ù°hµuýœûlÑ-G7ðêV#ÒÈ""ô–[ü–$­hËS˦›uÏÊ<±+CÏtó”2Ë  M9ɆJ[…,ÞìÐ JÎAÊtOÿŠT"™Ä¶!%•(ö,•žh ”TQpâ™à$.ZÚp‚)ô°½ÇŽ ñz~NÑɳfðïé1ì"^Xˆ hAw
+γ ‘ï*ÍÍá78e@3!¿,.8d™kž@ ¢&Lï˜Y­¢]²3!ñÓ"–Yˆ¥ˆ×–$Ò±ïp_Æ…f Œ‡ä,…½U¼`óÏAÍãS‘÷õÙ0=ïÖKw;뚈÷é¥è'ì‚©£Ñ`øçý>E¸Y¾ ‹çu‡Þ*kà¨tVijà~ˆ¹vïRÊrÈšµ“t”vL/8kU@
+\ê²ò‰j0Ø @SOÅš*ïÞv ¿2Èj] nÓ‡øcÿ*2ª•ÈÊâö*™X£$[ÔDÂ6÷{ÑËRiœŽ=xQ³òvÇÍm¡3Éô„Eã)’Á‘3‹<¬?T…Ÿd© —ΤÓBÖŽŸŽ¼Ð:d;& X÷YÑίˆƒóRº¨]ºT•Bv4
+
+Š¯£Ò³ZœEê*y%ç ©:¨BK+üŽp Gk'“™•°û¸G1ô´îQO:–½5®
+¾ˆ¦Ôá1-teX‘ÜÀ¼O·µ¹¹Ž¤S‚õŸ¨lÝGiç“Ïô v›‡Ü+Ë+˜R¾×üü¦¦ÉH`2Ö:Múb:Ó§ï)‹F£Ð"ÌÀC%¬Ÿ.Èä—wg!‰[ÃJð›(3?D1ïê—º-üQ¯ò”Ê,ßò¹a_aŸŸD*͹¡"%Õÿ·,dr`L™é6Nü&Êyl-¼1z t›QLÔίŸƒÑ0'ö+U«
+®ËW&³JóZò´±ë‰ßûN_2Y¿ô+#Í(¾Z HƒLŸäº.XtÖ†ò¯ûüo„:Ä>Ñ*ù¢zñ|¼e‘8²Ÿg± Õ”~·€ýþ‚AEµ³ðYˆ
+X àØÞ.
+ Èq4V4
+†ö÷E/wºãUF²%5¥´O ñ¯tq‘ùLÂŒyÁ–­âqˆ›ßÀ•¹Ë›"áÌ5†ý”×ìmÉC×®wˆpýK¸6®]gh“›ËÆðëð[5¸V4nV,‰1_ƒ…óå0UÐô6—;ÑSÀ 8#–§—»èÆ…G €«"Nkß(O݇6òņôr %ÜßJ€­RNÍå„æ’Li}õsð{”$äŠMVí울朔‰ŽôIÑf¹F®†Çå é'ápwæI—sêYŒÇ"å㮂¢
+Ñs„/RË=͸dÏ÷Z<(s܇p“Øû$©ó?Œ—Iv9 DOà;Ô ô8k÷Ò·ðVºÿ¶€¤^W&ªU›n[†˜$ˆA+N×ÃËs:jV]"ë¹ 6#@ÒÙN
+G´µ¼¯ŠùºF¸êô‡³qæ TxÙVÞW÷Rô¹ve¨Ä z¾5ùcªøSÅ8+Aè0¦üÛž&¾º5ƒáÒóÆ*äÎPòL§ÅZméU=.†ë¥
+A¬X DÀF@f™*:Ú¬$C~‰žOnèXGÊZ‰ú!IæÛãfÀ"Õ@…v:2€Áœøsúÿi aãiê
+Q&º Y³UÃS­7ȬØ|ctJ(ðõ$„*9w•p=ð誓öm' [B^ @ú¡@rQÌûSä]¶?*Ò\ŸªÐ¯`V¹œSB(eç4­!KÂ/îWá‘2(fŽïø£ÁÜ,‰O°Ñ¬ƒsÞ☠¥]8Y,VghÈ´,Õ Šðñ¬"Êlö˜wñ¥Ì>§öûAEµôç(ºòåñ\ WþdF¿¬¤Ëà¢øÈ­]‘ó˜AŽ5bŽ¹ˆþNÏÝåÅÓÊúÑ)LÍ §ÓG}p‹V§{NÂW’+0Qs~ÛÈÿf +ö9…¹wFëæݨ·!Ü‹¾ÄRÎiÚÞE¢%ÙcˆN |£èîÄ"œxë@¢ÑTÒE` ç¼y©ëÂI&¬&b.ÙÍ(é9æâÅ)‚h+ëÌ@â·~²B÷aP’ùäL{¶,^“¼“z*YŸ+²ø•ÿM÷œðD¸q¡ÓÓ7 à‹A¨¥ðWsù¯°q ÄÄŸWl’(®˜U¶shDƒ·;ñ1ŸdUð#„± wG;¼Ž†zAƒº!ãVsŸyàGbNhzž{ ÔJ– O*‰tnŸz.ùüy?ÿh‰Ã€ ZV¼QT[aVAÝÜ’S„|F’ ì$æëÒüˆÏÚûEÌ”ÿ@`xÎl5÷¯½S[â-‰(uJm>Ý">׉Åš?¬¤ ÀhHl›˜‡>E42CDI•…–L ›Þ[¸™X0Ÿ§"6‚—&1æ&f6K^0†u >‡-‚”÷VPit)Êe*éÓ@çgé^ÉúÒèâ4»á|qŽ…-OÃQ o” º=#Þp„àì=au5™wÓÚÕÒ¶Á›K»ˆë]ì â–ð‹ã.7êž’Š2öâV°ž 'a#.®L÷Œâ˜Y•”ìËvÂ*Á—=·ŠªÉ9ƒØ3‚(‚¬g[R ]™ DX*Úo®¡5‘=Z)P ›O ì-„ýâĪ1Y¾”º[rçï_^ÑçØst˜­†ŠæÛ5ÎâÝ?öVÑ… ü­º‰X¢‚XHƳŽf‰ÎÅ¡~½(2;1M.AóHBMêØÛ>ðUâh%Xrö $ìÄn@HQv~a(@0#NÊicˆÐÀ×ëø‰J¸®IÌJ¤høV।æÐþ0ªS²>5
+a1Xé`ˆ¬× 7—‚½£ãŒXC„HÐ(iBÃIì
+Ð/¢dXkE#LDµí»ŠXi âpöFy"”Ü5øUD "ÌŠrƒâ8ì{¬û.1H(m «[B
++§û2U4Žíè‹“Ùwv¶òŠUÃ7PEÆÓÚ#‘7Âp-ìƒ:5Xf¤Ú«#»‘(‰»ÿ|‹Ë â
+2WwY$¬”V³9%«¿Zë N–Øîç0jvŽu‚WΕu×.ûˆ 8äOE@ráˆp²Oþ“¼F 2r ¿¸îBÛç·‹£×M Ù«0Q‘chóÌÀ¿¢î"mß A#xêØ 0àœ‰ÉÔ1Á
+DG®c‡‡3¼÷(M”ÜÆŠñïQ
+׆Wt›z›Ä<%ÌÂæÔùvÑe?ßYÔµÏa²H› ¬„'
+D+ôž}Nx!èTEtDÒ°¿!a ¨úàújÞ «º´Åª.n‘CžP'úxGÿs‘á·¯(•:hdס"ø 45Y^¡ ™››A¤§HÄ4|0 w±¹Ò”9ï>?lòN‚¡ ªã°ðj&’ÿFe`G•š•K6Ïqøö†³¬îÛ°`Ï<¤Õ!Á^ílIXç”ð>Œšk (BÎÀߨ¼M¥'Šƒ¹2(ÈÑ:ú²º«´QŸ`~¬?ª=hÆPÇ®-à %ü;Ó—{¸EwÎðÇ÷ýÛZ"ƒ’¿mSQ²ÿõçÔ^¨jv{óç°™ù¬„Íoþ·éä0‹£6mI,B;Ôo–U»Ù©McLã±3íê[­p¿ÔåªuœÃàÒU°ÃYd88Ȥ¯pz˜’ªòà~©¬ÖaÝÁ™ôP
+ùhít&ͽ˜ô]ò¯-6¸VPœ¥¡) ? yå¾.=p?œDOQòÌDÏ­’oŒmºÒ w‡Äù¤•~Å&i~v³kónX+˜%„QH,Ha´óôÇdËãÝ®
+Dd œ©ý¢éY¼´´X4 à©òp}Ϭх™²©RM„yHh⊦ý8†øBqyb'"š Ä[¥ ¸£¬Ék‡Ǹ¨å&jàöµ:Û3L4Áƒ$§›‰ÛÚ¨Š[DH³æ„°„<ˆ®
+d[$¡ÚxÒÊifÔw´Ï—ûá”*ŸlÌÈùò³’©$àhéE©˜ |çü‘*.V|EUkb;E
+\rat Ô†ÿyx–ÜѬ…ßëëÙØ<N$#Ÿë"ˆ>#¬¹g×%áTÝÿÈquÍÁn2 gJèM›gÙÓøm5šM~ŠšÖ
+9>k‰ù׌ñOE*?'àÄò>D@—×âÀ?̬c
+4S°LS9 F­WâsúÀ7:ºâ‡´W·ˆ‰:¦—s°pN*Þ¦!{õ†d~
+æpZ«\ &9ªv€ Æ)ðÜú9¨!ñVÈdz -ä-­éÇý¼”
+Õ¯Â4™Ö9~­ï6çÔHM1\Æ9‰®&”:/ ‹-g]רA"L4ƒ,…³wap‚Ǫä¦ë>LÙªZ–_Log0ÉË£ðhŠðd³ŒÚvõ®ìXJ2R üÆER·üR϶kuM‹«%¬1Ì–­+«()ƒ-"pf‚‚ÌVØÊ‚ŠZç€?m­) Ï£Ù’ˆDy`ç#z\¥°r4ó–ï1 m_Åo69ìÔ×!áUlUQ̳6-¦ÛÛÅ‘‡[d
+ñ74²xd쨈~6ŸÔÈÆ h]ÄkR£F+•Æ]7è¿c£ÆŠŠòÙ*ðÇÀ˜a˜Ù)hÓ€0×mlD‚cð¯8^66†±/]Ûïª>¹ÑÒ\àèg¦ ÛbžûæÅ(<¥ÞhüYíàÆÀtÅç
+žŸöÂuqcxñQ&w÷¿÷ ƒ=µ
+®PïõçtÿæFá•ÀTÉÌòbr£§H§öËÝÎo¦©T)8àAÍÍÄFv;F³h*µú@,nðN‹}yü;5²$QÕ ‚ Q`R£%¹¨ñÔh‰®Äer£’»•E<¼hŒÑ"¾nÂ?©ñ.ä“i<êÕá—úUÓTî”æDz­™
+,ŽWqhó…sc7$óéô¸æ›¯e"°q‰ z¤ŸŒCÆ1²ÌÌȃM,ÿFïr=Fåzø¥¹êG…¤¹.°ì?Ý q}ì’£;ÍZ¿–šÅÚ•{Æ€€ %“µÑ”\ÄèŒ~K$H9¡árL µ‘ð] ™’Â*mÔF˜Eø4±ÙⵑPr!7п“t$aŸtš"yÆùBí*ör þ¼|ðBmSs¢¶DÏvÌÝl±6’ ’`4š@Åbm$ZS(—\\¯6kWÁXÓà¬Ô,ÖFÂô
+
+ã.Û©ús¾¿`»jé¤Ûc6»¥ga`= C²}¾ÇÍÈVÔ¢J5lÁ;m×ÄÌ'žð:á/6n#ÂIiHÍão¸"…(Ó»OÜ6%'n¢·MÑQ„ܘÑ&Ú€ÿ²…Ûzy‚Z½Lôkï9pÛ(æ·Õ|Ä㊎ó-L6’~ã¶Ê fp"ÜV£yNy14c´+&n#á7ÀÆHP}·q›±O*û`¦jó6a3 ¬»¯Þx[ª“%B?(6o¢¥ ÿTtð¶)zçmIð4Àƒ{²x[f)ì ÜZÛ ÷ÎÛ£õþ§¼\’"Ùa(ºV@ø+Ûã·¦°ÿé;WvP©
+ª££"mësï¬ÚfûŠ€›Ñ¤A…ÖÍ-"eÍxPfpÄÄMPAš‡q«gßáà%òÀ}gDÜRAf6˜ºOLÜRSèZ­¼äˆ¸ a±œÎÓs¬ˆ¸ƒ¼7QÉrÚ•œGà&†afº2>ÕjÜzª,›+PÀ?Ttwó GPßœ¡é•.ªtrïb[!ŠY‡ñ=ææQB[‰Þ‚F2g4S™×\%’£ °â!T.ã„„|øQu B¬–ùä3¤ZäNÎ|É[“\(éÃCô(l‘‰Üµ$"^•D¯Â=èF*`<•Õ+ é¿ XA®$¶²§gœv¿Ò(uE¨‡wï§Yȼ®4&ÄK
+L?~Æ£iS¾ê)ÅhRz’BéÞŘmŽ)ëû^\nßA@ŠëP«í„­+yªcÛìc‹²è[ÑU9LýgŠ±<(ƒ—#ݤL/É{ºftá‘´«M&‡ ¿ðo¼ «£w–¥‘/1Å, °P¿‚Øp› ƒ"-:ŒåPó‚´Ã8¼;PµA;5%ñ§=ü$ú€µ­©Ñ ˆ$‹]M…7m/*™Ö§ò1Lœ‘ak9ϺMø¦j€²JFȨ Ä“÷³ÈºÒ¸Z=/0ĺUµ?ÉQ…‘>
+çdDñh"Fòw3ˆîót“*"RbË2OÈÌž-¶ööBE]§Lx)o…’ËÕ?'ãËchiœÀœh.<Á@Y‹VÔÏ6ùpÇÏx. 펓Žp¾èAnªM ÜsÁ}/,²*q#!Z9Ì™ë:*ÅLŒ,ž#Þ›çt îƒÅHû=özÇQ*Q~â oŸ{±Á“?RKÍK£†.\´o¥D¤ ­ÅÝS=f`òUp.'‰ejÃ1Jm#ÚÜ‚b¨Ä
+¼È…wH’i&­ÍªÄÊ™Žì[]Ä𢊒Nz¶{!£ G±¢-?K¾?3â—æ‘s̱uãÊ›‘y¹blqƒvDK'a,³ÏKøøë.ˆGŽNÓ*yŽì-(Aàsø¥rÇ$
+Û¹.ó ¢ q³5‰F1D« NÁÔð£FMHF#Ytù–} ÿOŠV³CËܘtØe¿ýô¿
+H‰Œ—K’9DOPw¨ LA|®Õ˺…Ìf¥¹ÿvH†ÔUiª^H)OFp¸;lª…¼ÿÇd>rF‹¡]µÏ÷·ð|è a¢>¾òÇ”>³ÏÞlÎ÷_€æ£‰j‹é2Û¸}¼ý÷MÞÿÊ? i|™’sì‡jŠ¥‡™¹¾ÿP<DÃ9edÄ ¦=Gãtå‰ó@tôÁ ±õgôÜnsd¼©Í÷ŸëI>¬I ›\yƒ$¸kI͹Ήl}JÌ&ý¼qsé™mdËXÁ…²» K+Hrþóé3=߼ݵh:¨_‹«ÜÁE{ ÖÕòôkøßém¸h“P•9»u3^S<÷5£`S_Ç‚„ Õ¤Û祫Ñm¨Û”\õñ‡ ± †üÝ7H›wN¯Û:GS›Ñ?›TÚŒ6«¼€ð”Ná;U´ó¨gÐŒz·–Óû~e룻ÑzÓÓ¬žÖ§Nב áâ1’¢ÏC
+9tŠÕû9k:,t±K‹XÙ¼¥¯S†kŒ!”Aõ–€åñ(ßåã“4”štÉ›9Û”P3 ÒôáÔh‡òn¼Éf—§ø#eHDƒR–Ò`ï‚ÀäTh®~FÚ¨°!<íâ|Ÿc&—h¿Ÿ$ÍÍçì›Î]][ãµã®ÉÈðÞúf<ß
+Sr|õL‹ iM —o4ðš(R€Ñ=´DOŽI&›`#<nKzNÝ‘’¶eoH?pµž”›5 #i—Ë äè«aÅ>ÚÀÌ^œÃÈ›
+–=ÍO`Â0t„wÅŽ±,¹¤œ~*Œ½PÀAª™ÛËÆ?Ä Ëÿò”Ü„–”SØ:¦l΂F59ôƒò4›i¼6„² åo\ãM«‰SÐœo~¼ÝHAPª2w¶xs‡!Å8Ýá ëëY¼lZ 8úz$¤Sx .#‡G4¡DG¬SΘö¼ÞæôoKýõß­TZDíÓ)sFç•&@ ¦
++]ö -j† æ:f1z^ˆNÉÁŒ÷ioHñ°¾noÅK”"+\ÌHõTajE‹šjí·ç(UDN†ýRHÌåü2„÷°Yc„¼ž¦–@1º?WŸA7@‡/”3ÅìÛ § `=Cí½ !îv‰Ûw‚Ó XÍ÷í5Üàï^’uÕñÓÄ&â"„Û]ħ´•.ÐËà›»qßžO¹ ¤Ü{‰¸9§£Y•vÑ…8(?Ã#t­"Ï+­AOé«ý 2ááŸ%JØQ ?ÆÎ_gKÀü¢(p_”½ñ@XŒ<qø{A •TH+ ØÄu'³¶½zõöà#óGœR×£ÈÆL^Û2Ô°Ô™å£|ä¼:ÊÏ.#ºí aÌÓ×ó$²<a™Ð¹‹Ôh3C‹ºÆ¸dü„'!’ªŠÚõî ¼.ÎúóœC㆘——m©ÇâóŒj¥4E*õÖpÜ€r5&Ìj7cá(s¤œ´_unk½VÕÉ›”ÏDîâ0µQÍcˆÇ d™€
+ÐP››îØ–€Sôi·úýµ 7.ðÜɧSžù€·QcƒTÉàÞòŠ+‘ra<Ó×ãðê+?K½h82èýû˱öDhI®$c}ð›¼6èœGýe¤V>2xrj}Ô€UOËGu³&PÒáb1ó…µ[ùYýÿG1_€X…l–w¢[òŠe,t‹Ö
+Tè/@¶Z‚"…ü.Ó¿Pa”æcLYØâÅAÍÄÙð&Ñx ªe…ôi‘ž.GLÂ
+Ø£
+^Ì–s7€ÙIÆ‹ƒ n5D–<ƒ~}§½¿c‡5(–P«+šQ`§ð"‚RÎDŽ0ƒ~ƒ);fÓ V~-†˜j¯Ñ¨Y/Á ›kZ³r† ¶®YÙd% D±¯_N9 ŒJTƒ}¬s”*2ÞîeWÖrÆZµÄ¾ ¨#׿oǨØÛëÞõñ'»ê|Æ|¼À;`Q™ò÷Uk@Q+6ŠJòy¢Ö&öµ‚T²R`\Ñ6Ñ/f‚‹•Ë ÄgV¶ý½ |Zé~®Gw´Do¯ D“ÉðÆ:‡®O+•í-oZõ׆ÿ¸ì$IA¢¸DÇ'§ÚÌR1Þ´ýè³™­n‰0V½®{ÆŒ ‹Ç“äñÝ‚PáŠRÞ2Ž{SI
+\1hK¨VZ/%$Û^>Å4á#¼3˜BDP¨Š¹¢zÀ: Õxé½…À7"òŠˆ7 d¶ÜdÖžØVD¼}!!OˆýhSÏkŸƒ$üPWn©›û²ÂY"tã‡{$¾’ù‹Öð‘Ñ{ rë‡7MvÄ¢’2‰½®-^œ3k%¢Ýy)8ë†]@¥? t†«ž+ÁGÌ'ˆQº"" x_û¿‘CA¶-66b]®$PÀK×ú.̨R§¤»n
+þVtØ7cO½‹šJûŸúH¦¿‘–l—Ï©L$;Ç&qÒ Mp'B[óî‰J$c¶Š^¡Š³œÂ+¼63Ã…¬¾³2ŠL§¼¿E,M0¼È§è˽¿Y¦Eá?ŽÖNý’Æ9ÜOíG!<à…_í­ʼnfë({«µJ©•Sâar—Ò\ªrÌ)½Çx!}‹2JrÈ#™$;‰{ D4P~&Þ Ž5ç.!Ë+>-o`‚‹´þ¾ AŒQe”9//…UiìI x;ä*¯…#~’Ì«<€øZAL¥Ø|EâÑgçŒ3Pˆ\ƒ†0<vÎú¿áM‘…g
+¡yèJ ñGß+2,ဌ®¡(„ÙŠâë5l?ÐØB¢‚ÔsX'¥F^J¼òt“ApZ"YÀaý}ÛõiV}ÞÞðj6ÐXƒÎd!´v4ŽñÅmí]Nb°.OÅPÕ<%8ÈÍó¼Ú
+}R>8óp©éN!‰„¼:÷l!Ć*ð´Ã Y[eÊ w…xsy³/…®“&ˆæÑ…ô}àBòNŽqÿ1¥" Ä˃¦ÓÚœu0iá"Z¡™BE¼HðŸËÀ,‹…0óf*‡¨–ÝàÀÁx¡nÒ¸ƒœ¬Wz™ƒ:µé¯ƒn¬þ¹P_X1ÁSž%ûXáaã¤ñŽQ]ƒ€Öð}î‹Äšx¸Äò«éì6åÊJ‡Õ=ö¢‘6[]¢!$éJTÆ.Usí©I·4ømv—ªÝ˜'U+ˆ¦ˆA·QÆô¾QµBP}¸,õæQ5!]kjÚ«ÜTMJ¨Ì¯pYqe¯ pÑ&(Û¼Dý”îT‰^ñ yÌäB5ãÛwÌF·s ZM™³béð€j€›A;i ªËÒ^)MUü«ãþT'u¼N!sâ0“ÕnȪ ªÝ ï´ó6êr/!? š›cI•·k/î¾ ú’ò³ORy£¨€^“ÜþvçÜé“(Ž&ði.›çÔ
+¹6ª ‘UG}Œâ ÕÄT$Ä9ã™ü¾C5!ü‰(…ºk.V+{Í´*J¦ûXí9Ä|=ñ/cžPíÄ<˜w£:ébâ‘ê25.YqœHâµ.S«êeåéă©ÁO5LJƒN䳕7Â8Ho>T„'rX@v¸ämºAÁ“ÿ@‘éA5!C‡á-×ò€j‰+ÊÊø|¦äA5!Ðs]]¦»P}ëïfj‰‚`2CKÏg x05AüH¾eSëZ²~îMn²ã~?8¨1u¸ ÃîQª%¨pdú ø`šÑ:AW?—å 5FÞªƒÇ_ÑDŠžÂHÑbîÝ~ÃKÇ(ñ×)¥~ºAó#PÂ(œf [ˆ¨¤ItßÓ¡­&þjVJaäCV¼”Ѳx
+,
+«CÄ—Z7ÑÌvRK±.ÃZòÕ×üêL¢L¦¼s)E–Bg¢éˆÛÆtBÖFS‡0Òk\}¬Ã%Ÿ"ØÛ¸ô˜—Br>c¼ñ_n®<¸Þ˜† y“-²Âæ¸J@Ö½Ò€Î)ôPáOÇ×MÆ^ú-ÔumrHÉ^Ix¬‘ø=;öúW®°þuŒ`(bqÑç™(Æ'u½P3Dî Zô2’a'„ÐG©»1ï[ôf;¥î†<Ôù×ÿ¼ [æ»h¯E¨Yæocœº»7ûUÐà ü¢z½Ø>Uͨ`R*©9Zý² Vøa®~YÔÚè:\Z£‘£U…@ª,’Ñií'%L`Â>ú¿¥ž …sld1÷ib‹YÝ&š¶ùQ¤E¥U sÜGµÿ@‡àE%ãMjï tÕƒVÅVšõt6kâR6›@¶ôiAL= Y"#­:A
+Á`¨ƒÆ_SY'
+0§ –ëêÐd(áO"wÓæ*›AÉDëë1€•tÍÔÛ
+ÊôHº=%¡Y“F•VþÍíèº**xÊ;¾ a—„…b>½ì­î ‰´8DšÖ‘Ń˜$~’•x‡™ñ?ÈÄ"øKLŒµ¶Ê×FXFÎ8Sc"9Ú‘²€èŒö|Üw`î€QËoøƒŽ_S ™sa¨¤ Ç3¿–<ÔË1d„u¼JN+÷—̪Ƹ¡Zߢ'ƒÜ†„¯0"",îÀU ¤;ä#z@
+=[EÙóœöДïEq±ZôœµªJøZÉiÝug¥BžÀ¼š3ˆ M"y¢ÿôãÎ…fçjúP´´ gNƒÔÝ‘#~ÝêÞˆlæØví;3˜%ÕP­ÝÖÁ›Ø„Åû|uô$ØgÿéFhV£v9"RŽ{§;ˆz!š@ÊZ'r+CõX6íJYƒ½ ZH‘ ¸ób´.¯µÎ«ðÊP:Æ]ãñ”Ç£§ö-RJšÆàÖãô䇲0.²½òȤµáoi8!ïŒé[Ðßo‚dQ#/ÍC›1ÝAMsAœtžz¹)mL]—›2ƒ²žZ–á")9\Óºg1!-hÜF?ÆMEUUún‚5¡
+ž½–uw¡ƒ>¼FYฉ–Ëz@
+Å{ùþ~íB ×&Ëî¯ÁˆÃÑU, 1tŽ¦Ä@émé\LžÕšfßýºvÑ>6ÁéNµ°M‘k¢Ëý.ŒB±³7ZZÞ–à·¨zm¿.ïß´6æ冥¨™««eYº1+RÙÔoîw̧£DD?ÕQ¶Ð¹"mœyŸWè ™·eÖdÄÒ!äá`ʺ5R¦±”¡Z[1©âÎC%4×>X»
+%Å×­é¬rºË~`¼•ñˆ.ý"®Ðæà õUÜô“ê&¢a1®’·©Ñ°
+yä`ØÑÄ3]ý$åzî]†ERûûc§ªá'$kW
+»‚tùŠ-Ó "t_oÓx<ž¥Ï9V[QeÁ+ôŒ¼™ƒ:Æe Ç‚¨—I<ƒ4Q€M5Wvùß1L-U¹‰š¤Ò)GŒ§è»±àõ䩲a^¨ldŒ;œgdyö±Oã=§Ûî¿PùÏ¢˜(Ȼ̿,)«“‘ÿ’<+ˆhƒ¯Äí8ÒÌL]8 RIDSÈW;ƒ®ú
+­i°¥ VH% :8°WL(ÁH@(™î˜¿ßÄ`è`7yVü\A©éðä4+
+H©›'YxC Ü m¿àŽÐ¥(Ñ(:a‰.>åèÕ‘êÛ*«fð+^ö¼2 …±nt['ª²Ô“N‹yfê?Óý/¹Eºž ÒÖž ³tÁxŠýSq}zƒ¬’ ЄQÃf!)Q¸4ti>ð¼K’å½FªÓÀòÁÖÀÈÃSãŒ7Ú*BÅZ¢†ëfë°’xtPüÍ5©ÜAýC—A€Ð7'þ|ôÐ '®"5ˆxS$ …^Õ50ô¹nÁ#ΨbvlUÄSËø»j-Ö$H¿j·òËÜ¿[î•sǹWUßã•™CÜe:R@¤wçž0¹&£…Ì„"§©*ZMäÓ£º‚m•©r漉«1Šl(ì'Rpz¾üU¦(ë¼0£}œ«6„ÂäÃ+7½×¦D ¥…›³tÞA zF¨)”5xÞoüÛ É/̨ ÄáQˆ ô®•®X5Ñ~5‹®Q/Sö åºB2ÛW3ð¶K"jdÁ&ë1Ê¢Q€L$FÒÝnxQ€Œ¡i6[¥ãCE”÷Óïñ‰ˆOÇ$¡/Œ6jÎNÈ,G-
+eÆx³pÊÀXåX{š!˜]W,aÀ‰ ‡%œkB3$©Üs”½UC¡¨‚l•yÊ<L©‰®œlP±ñKà陦 &•N6²«A^ÍÈ£˜ÆFck^&½æ@¦VEËò‹åí@+]†ÓĶmGƒj•Yæ\×½dm lÙZ¾,ð?mT~Ûô#fBw<¤Ùp1Øv0à|YÐ?”—»’cED¿`þA6†¢ß“
+ƒeµJuw½2³4ixÆŠ_tJáµÐi›2–7Aú Tlñ0¦ˆ'}B/'™Û!éÎAL±Ô äËnÊÈgKÌéÅ9Qþ2cÑÛ1è$J„FøVY§1J±0>ß=/+‹ªÃt1*å$ åËõ¾ ‚¢lùDçÁ±ó
+h)
+%H–ÁÔœØÒdÀpÂcENy«IÛ‰ATh³Ë‚0 ÊM+N•c
+tD?{ŽYÔZ†o@g%1$ /ŸRí¾B:S/xSPX-’ƒ)ÛVóY£I)«¶7´¸¿8'¢fx”Ú£¿8Ê*RBæ/B+HWöÚQ)) Ž£A¡¨BÝè1‘9yb²cʽÇ(gŸà/CÐ1ÿ5†äÉ‘Ñû%úlžAÝf6„ËbòÙ<ƒXjL$8òXI™ƒÝµoº)¥üìÚ#Ö“)5âRÔÎn“gÇ´âò‚’³r»@xº3¶Z¬WIe¥U%[˜x¦ÜÙÍÌ#ë[íÍ~ƒ¬Æ‰6ü~;,Óé-­ÿ*ÞÆNîZôì¢7~!Úë¸<F9 ø,œ¦î$†_ï(¦„õ—?>Âí×??~ùû#Þ~ÿè¢PoÁÚܼ!ya@HÎ-ˆ àÒEK4N%Ä1¡Ee^!ƒÌaý.O—®:m¦“äê! G0U»žË Y|S’
+áÙ¢3ñåôš¶Þ¯ ¹ØVœzÇEdËî2ì\ÚPéjÏÖj¬öÜ!˜,ŸáhƒQ1‘ m-â„Çóm_]ÏEù»>Y¢¤C(+l¶’L%!2çi
++Ãô-ˆ@³æˆ­Å®þ%Kt5øÁ⾂0k% »–awë8Ä(ü -£[¡ñ>WàÑ>ä%pïÌP:%Ié˜,¬RTŠ)šSžvîť4âS NW)«DÍ =YüäëÆQ-‹w€E# ¬Ì\9:áă©S%Ŀƺ}ÚIXžOH©"=Ž–7¦þñÎuÿvºkûIÊaŽžõ®þÍL6T½, ïÖ™ |ã7ЧT?hnUk—¬Ü÷òc¼mž¶k²®˜,³ƒ—`Ï}tì®L.Ù©Ðã,)¡L)ÃÑ
+NéX›¢Gß®“ð4$³à˜ÙlmLߨÐçtQ4rI3<„›æ^õ6˜MQDºI;n“ò¿ º$|sÛ;˜ 3ï $3Ó¦š°bå t-É ôx§n«¼Ðl•ø'Èc)åU)
+›¢¼ZÍU»~úÜ‚ Hñà‹Æ
+” p ú¸†ñ>hs]¼+fJvñÅÔm@×â]o{³)Ë5o®·]krÝŸ×Í'®™ßJ:DsÑ'qJôY´‡±áÄn‹Ê4똛A c„òÉL4
+†Õ5Hò(>{MRAO’òP–ÄИHý$¹f7ðH™„#Øì–¡:’”°´‹Ç€|âà©Ée³À|LS7ŸOÑüvO | K†œûö@¤¸!’†Ù_Ä#úÇ¢²èW¯K^´çÀ_I»âÏ)»;È·*ùUv6#c¸¦±¢º¾†n©˜8¢Äì£"×2•ÊW]³3&]˳åxnû ³ŠR´Æ>[ó‹:}ƒ¬†¸VûrεgN¯ÙõÞ%ª]Ÿ²³›„SŽ:Q¿ÉŠàëàn¿,hâBÿ+ÏF™oáö×á`X»:’h‹ø áÓØ&ªŒ&û‚¦³UŠ’N…×Êô¹eìè$c£Šº¤›ëΠÇ;oZVk­´e±Yd¬E±®¿¶ØãPŒlŸúÿ_¸rΠ-‘r Œn.÷Ù1Æm&úCvq‹Á0‘™‡†ÁÅl>øX }bMýMLbÞŠü¤P¼çss×[z5±ÞÑðì­Í0hÝA”3üÐ9úÇ:¼7rqTm$èˆ äœ «aÌQª-P•DÄQg }¤À%ÃφZ–jï]–HÜ Éäåæ‘é}ÌBr#?ú²«hè;e,ÂB—Æð›à–œÎˆ÷áˆ:°àX
+ì>9ÎXÝÁ’Z7˜Ç“Ù;0“E&Þ QκE$Å‚¯b­J– Ìñ¬ZÅ$&½A Èø`1ÐÞ
+BÌÉ•ú|1-?™ºß|ÝAYÜZ$ÅÜBƒû€’®YØä’žyÙLpÍ ³éTaxZ¾GoÂÒ“ÿ.-C<é]ü¿73åK¨,U2# ˆ!Ò9µQ;§¢sd•ÈmA8„ƒÅh5ƒÐ€p{`ÐûB˜‘sæ16F9”‡ÔxPH2?,3i­$VŸ,Á6èé>¬‘ôš§AB“ý€¨Ñ‚´B£79’±®ªŒµm¹c¬…eÑdÒÑZ¨Š Qbÿ9½·‰rÑ£ŽßCâG03ô{Ëå¸éª OÐÐELÊ:‡My‡UŠOŠ;'øRVP¥æêx°’3‘Ÿ´ HKéè vÌS‚R¿•œŒ*Èù°½:±“~F”á•¢©p$ø¬ÂèVÚ¯~“úŽ!a9ƒ ÉPQ¶&;†Á"Ê•úQ5âýYlKž0Z˜ÊCeÈ[š#vúñ©t(
+Ç›ŠOì ñÜTÒ’"ÜFCùh9¿Š>Ã!7ú)øüaTh8õײ®¢ h¯øjŽí9P“|Zhl`Ó§…!ÒÂ/ób/‰|ÚÒOáb}†¾`¦E_µ!œR=ÍJ«TÑžÔÔ”†£ ³†rFÈÈ ÒP×ÆvJà[Æ#§žý¦9$@e>õõ;QÊ£”ÿ0^6IzÜ8=î tÿɵ½ô9f%ß;/A೺Š5ãp„Ý–²É"ÈLÀ©²¢l*ÄALoåli[Á§Wð/«ykñ}}·ŠàŸä®ò þ2#Ž]ʃv5˜ÈK^% A+ÈŠGD6¦gÇMÙ\ïÂ'áÉ‚ûYÁI ¤6³«5ýúõJ"
+I›e{ '£ÊŒ§(É0¤Õ¼•c~ñòÏÂLd´´î!'Õb8cs­UùÕI‘ªxeV3ó ‚iˆÐ,HýpÍá6 *à}ÀJвR¡[uåâT­½.’Äâ2Ê¥Ñó›ÄÐ U€N MŒ¼\}¢ª”PMÙÐà ©\ƒ¿ê–?B7ÏĈf|
+_›Ð1ÞAñÖqbêXg±xh¼µÙl΀ÜÃÔÿ eÊoìÒÊ{!¡Ù¿Î`N&%‚¿ d’¢£Ù5iQúõ’ÃÒt¾Õ⇠ʭYrá\¡F«‰í
+³ 뉫,@a_f1 ”¦(8oˆ÷÷UeßÔ™)ª€­Œ³“áM´[¹1V\"¾V®® Ç (’¸áýF.¡qQƒã*
+ÓmŽ.gŸüo¤ª¥v?ªñ*̬±0ùVIV¥u]keÞ&yCÉFp8côò*¤súUÈ5Ì&6|HZµ°1M dž4I}õ½S®IóЛø”ï?¤ç]Ÿ>‹éì qJ…~ Re%¦ÿkåíÊ¡ƒŠÒë_/ ú:´>Ò~õà»ãº”bë äÁ½ˆ~0AM¾¼^Ρ*äB#òñAi*“«ÈúzU‘I-±ÖÈdæ )FŒDœµHê¦é-=(ýêå°øUPR¸iZZJ9‡ÜŒÓ¡&Xî$-‹Ed¬æ¼ è ÂR¯š _o y&9sÛCu€ðÁ$z~•Èc-8‚ê@Ñ‘Šzµ·s¾o'íÄ
+° ¢m\%rã_,F§®DÒ1µnx! ¢2Óuëh¢cH‹¥d$]9Á]²‘Ê‚äEºá{JÌxݦ Ðî%:œØadþõ°2¤P+•¼W7KñØYS>ØÚW‡,²I we°––¹¯•ð –4îÏÐâ¢Í‘70Gû*…¹’ìWaI8b[maʲ¿‚…’;r UK² ¼7mX ;\ãMÅ_•´ËvÖ5ÿö2~(ÚkT‹Hst¥°-£k‹0ùoäÃ0„³.ý&ÞÄ ñÛ+mƒfòXTGMN¾‹(HƦ ×úyÊ67È (*ã›pĵ^ÎÑ&„¹@õoâ‘Y}¸òŽ@¨¾,“žŽñ"8E;ÚFª$zÈZ¤ºWö£¦ŒÇ
+#R8ì[„>ÉÒãU·£œdHë¿/g&£¼¿Ã0)¹—o1—0T8Ú]T.’Låâ¢Xf°³”ðé´+|‚¨~2jƲæ6s݈¼mænöÿçGúùçXò¥EwÌ–\ìR´©jͨ(öÏ¿ T»²#AC{ üebT—Æζ#@eop Ly@‚d|a0¾¤§)5ºRWF’oFƒy«ASÙ<8¾› „òBC2NiʤìNV+o‡¯Z’è áP†ˆ‰¦iº‹¶ ¯#ˆ ¹!¡Ì&P‰:Ä1 E#‹©1ûÉtWì-Å¿7ÉÁQUB¤C4±à’r”A  ‘Yákü*†­“Àx~ósˆ†•Ã^Ö´sD¬NŸ7DfMK¡Q­/ùej¼‹« äŠQ§È„ÍýÉ)©OÈL@0tzÅCûL2‚qô4½€èY’ìC€¸Šà„Ò8LÙA‹&HP»$Ì:Å»«2evùfÁųV¾hs1bû «nNÜ ›x@êJÜœýr†ºq´º:úQpž26CŸLGbõÌZõ«ˆö™)‘ù{uÓ¤T–í†-Ûá<Ï (7LÚ
+ê¾
+ñ„ÃD™¼‡ÔÂ~± IÈò«ÆÀœ‘:Ù¿‡ºñóE†¨ûU´H[–R?‡ñ#ÆϺ!M†
+‰'®ýC ­mÜ̧œøÌÿÂVÖ>µbSc§ø/ãe’É DÑè:ÀyXËK߀Wêûoý>#XPe1áò¢ÑÎþÅ!ñ¤‹<—Ì.]œE-IÍ©Q«· Gí’5˜pÿQÚ±Î×Yù9
+ÇÁddùßⶠ0üHál¾$×V£$R…`ÿîÞåa±“Ƀ#-K“-ØV«m†2Uö&&\)}ÅŠ±ODIæºû\ÃÕÅÐиo…-#¹I¹öL©]á2Ú¢Û[5¢%qWâëGV¸’W‡µÓÑŒ¡g¡Zȶ'¯]AFÁºÁ€¨5º¤!+¾yW Y܋糩£2\\¢ g#t]'qIlõá©Šfù°ùUA”HÂ"æ=˜¿Ìðr‹¶^*­+P.,°{´*¶T¥/dR tÙ!¨¹ÔC¯Ÿ Êÿþ8Ò3¼‚^¹¯Œzá%²ø¹+€!]šŽS)ë9ϹTGžöÕ4Fz¸a@h$Ɍ索rzXúmûŸ½òÐÌu¯çd “ɬNwŠcܵåñ;1¬³Ššb>Å ŸWÁFuˆÔ’žˆ$ß µÿ(=è ®©Š¬1`@iÁ¼”ß!l/·H2¶»sw¦íÑV°lq›š’v³Dæ
+}CøUWLê†@ ñx¸™¼cÞo#úýqAF≞ ”õ”èmôÓàûòÓ-ÙipÊ‹™*s/3i%™Zƒdi q¡‡„¦h|– ¶‹C
+œ¤ ±Ÿ“p¦Nt!³¹[¶¶HÐe.’°×Áñߢl5úÉÚ!ç7Ä{ô¹Õ¯AÄ
+Ä*9vÇGŸ)«ÍË
+¯ð/» ¶.Ã"Á±¤›u²˜ oc»Ä˜*Ú÷8È 0g|œæ)NÙVäîH$;xVjZÀ_˜µN€ó3tŒ ‹§´$}Ç8*ùx:™¼2º1–ÿÆFs¼z¨(ÒÔ´Z«Oñ,ÍÍÕÌŠR„„}UëÇó£Ï,fVž`ÚqwjÂaap¿±¢º6¥Ã¦Ñß2šJ5 {z!ôáżèÕf¾VXz†9@‚)bÚ/…sIA,ÉZ‡£0®4ßìý0TK7á9’¼´ië: r ³×©i꧶9X; |Æz¢ëR¸©¨ž’ÿt€|üË^M<…a©LmÝó›´³‡ÓüsÂÄli+„ŠÞú9c2‘fbD<Ÿ Qè.R"Ãí] ÎÄ
+‰‡å_*Š#ñ¥mŸ|¤ªS¿†vRë°Õr«Š,°m^ëàÐ&h³ŸJý’½¦Û™ü‡7þ ±øÜÅ“Ó©ö°.2Æ…Q‡@\•Ÿa,ã‚?“Ì y8¯m…¶À£¼O©Ûˆcö Nz ÛV¡Ñ#Ê }»:QfbX®¬0œÒŽ„³­ÛdcÛ ¨ZwÊ2‰”¸?l6 “aß:W ,ð(í–©pHb¥G}¤Êeó«`̃SnÊ`{áhš"ø:IûÉý[G†o`O{öudËè ž¼ÜAº|‡²Ô¾xétiä¿å/¾À²¬½ º¸ÀŸ;Ðsp:A:ÈöPû:n /ãz6®‚h]x$ɉ$&ÆœH‹/p~]\â!„øNW fyh8¹R<¯"÷˘c¿×{©Fž 'CÞí% ­qœXÏ«Èî æ.`Än$f÷Wª÷:€5"Œ6 Iý;oŒyÜKü4”“ûòËO—{Ÿ@—ò!Ïop‚¼<% œ^‘ܶ˜D,§´òh+ÒEI§D+ƒT…X’TöFÈ¢” ÆóÖ#º­˜€!ñƒáßY)%‡0¤<OSú ä‰#ý9g
+F!ÆÈžKÎÃÉù6_ ^C¥98×q"E:¸žß‰Tþ~N(ª “±¨aú"08åeeþVžºKžä OéVŠÇ F`È?¦­\œ^Æoæt\§ÑmbuÂÌô³°)®=jHÆBƒT»´ÌÞkŸÛNÕ¢a1ì‹ã=“’ƒ=U£¨.ÍTÆcÂg&)„ApW¼§œíô{Ã](® T­¾v+a²xÒÝÆu™a„ª„¶ÖAJ°[8†V7;âx†\C·KIKœ’ÀíH¹„.ipòÅ^Ð@ör­ÇK+©v;1Ù)šv  ‰£(‡Ê½Ý (,˜zÒY«C_A}9:¥ ’/Ó0RŒ¾ªSt â-)“?—/»bÒ ¦ø^½jKÍÒ-Ì%VdLoƒp»…‹gžƒ ³@H7_Ä<´K|Ĺyó"H–Õ?n÷.è©?oTiÕ2¤/fe®Ä‘òM1 ë¡)¥êb)!Ê» C¡^·{ôúx¡|ÚGŽ´ÌÞªùÿuR9ãç_xÐ/ÚWž<ÌYÜïã½:zCl¢ 'ˆÖ‡„jÖŠãsApl‰2yJD¼$U'Ð>BÀ¦=áDéM¦7b™ðj ·Ê3:ˆ¾Ðmy†jë [Ì]ŒXÜiXcb»Éi ˆš8‘Cd³èæ ,i[ ­éNa ±(Tu­33E&˜Õ˜ýV°#ø@P%âѬråÊN„¯ÌTìmi”¼ö™¢ü‰3€R«;UØR‰ÖV[$’€º¾•ìD"y…X° IC$£Ë<@l+,¹ž¡ªãf*…GÂúó87Wâ_yXò‰\»U1À”¾=‚
+%)¢_Væ']m¶^ʹæë$Ùn
+„s# $km¥*žÁûâ6’ryÜøòæ,åèÂ!Œo${Z·á"Ü|ö Ó2üˆãf
+rÁ+ܹi3b„6ä»ñ]Gév2´nûȤ½#«vÙ»!÷P$<IëàGm$²Ž‡ÅœÎ–UÃÅþH8ºM䙪°Žbu¨&5.åa™’I1H’9­ðFó
+:ͬøQöûù#2Ab²}R¥Ÿ¸ñ¹!Ü"hPܱ ¿„VÐŽ¾•jdWt¼UiêC¨¢ãl¼Ÿ›}¦4Å£ÂQÍYÈ©„¡‹Áä>˜@h «¦,±N"b@Pbz¨s#ßH^.„Ô”G„«ö
+Ðû]Ñ üï'q2d¤Ø]–í
+`ÖØCDÕHKÇ{L–%i$#Š™nÞ3åÒ*?–Ä,P53<ÂÞ¨@ƒÄUÌþ‡µÍ> T'꘷ùAÈE]bA$Ùˆ¨Ÿ|ˆ}ÆZ-•Ò8,ŒAd˜BGÙ¢C`©¯ûME“ËÚqÁ£8&óŠµEoÐ1hî+3*IóJ
+’Z5d³ç|üŽø{„‘3ÜA¨¶'»Zêè êb û´J±P¹ŽOòù%°*8ชlkŒ*¤¹×‰vUFö~Ñ5æ†àŠ‚m3vé'º€Ï&)æ•Ã|™ü@u•ÿÎ)çDºN‚K eñùƒ©;@ÖQâ}¢GmçwùŽªÞµÏü”zÿÐfRâ,Õßhj’»‚‘Ô?(DÓ‹ZÝû€ÀX|–³ì; m@º‡S…÷y2|]‡Aø•bÝ·xH¬’ß‹F?JÉ%ZÊœ^45wµ£kß‘Ø!D] ãDAðAòöÿw–3bç3ö“2tÚš‡í[‚¬F3‡õ(Òý ?s£PEæÐR_o ߭د'Ì‹¼œmßEƒþþþ´Â4AW‡ÍW­ã‡D7,–fìÏ ¨Ñgv…ç =Hlb0’ʲ(‘‹£'mûìaÂi‰´¯bGƒ1…[㈚ˆ­¦GÅÈzuò)# ‰®´ªm­&ÒÂLl2RÙ· RúºLAÀBáDc°‘ð£ðS1ɺaSˆ
+a q$Ý,kų¸•é€ö‰×TìËŠU@X Ež”Ø!D:uˆP×IÌ÷Á¢åáý $-ÆšœÊgÊÇÄ°¦†ryýÂéI‚Ï%K5lÌ
+º1Æ¢£
+®ÛÅîdBåÍwˆ«ÜŸnä}TçÚý9Û4}éÊDŒµEýsV(ÑOŠz¢iæuÞ)‚-Wd[ÌãIÁ@(¬ae(h
+ìÁ—ÄÓø­“^A²xÄO±È”=9\†íPÈPÏÍ^ßÄŽ6Κ¬
+mö£^kƒ°¦òÞ˜Ždßa^˜K;»â¼n\ŽÔ𖉓È:j†*z˜€Ïßanø7ê‚dÅ~¤§ªÅŽÔK˜é¢Zâc1©Uów“
+*Ã3ûf"Tø“5²9®ð-]k£ôÅé®zÚLnÜdú÷m8ñÁæ3í?OûM£Tªª:Ë‘'¶ÿÊ7Ìr´¨yUæ•´sÛ’‚ìä÷ ž Æ“K¡
+%£GÜas|§1¢4–Ð L-&=‰MµÓƒ²¢/«ÔM kRUd¦î–EÊ_b‚Ýh<× lHÜž*DÓGäLƒà]õ3Ó²½Á“B®ROÅÌ(îÎe4§ ·ZÂâ‹
+åuq=ggw<ˆáT«²šÊ?•_yê> {ÄTr>öi32"ÎÊ☇˱übw“Ŷ•èÔAÄêѧœ–ïâwXâ
+⛽¬Â·*%Aãs×r‘^‚¥Ä ß"¯ŠU±Á©òeØÇB„ŠJÓy'/ ­–—Èò(J’÷¢—ã(ÎÃqð~Øêv§kQYl
+]Âvn±Óõ¸¿*¢S8ç½gïV„[ë
+™¥Y óH]ìÀ/B¨yIºGI¼0¸hg®Ç4kÚ0ÁÔêSB«(
+ú î]æøð9*hZ8vX¹ª,,>˵aHÌ@ü_èžéý˜qSé8~¸)!“[ŽH?u[_¤‘U&hΞJ^“Â¥¨ø·T5öG5"ŠÑŸ‚Çð£6¶¿ÜVü¥ÂOB_OPš¸†a>vf„Ä‚£]¢Ãµ®,PŒöá5dõ
+‰/™Ó[Œó£6VŠã{]¿5¶UihÛÑ ÌŒ]½*{‹Ä7¶x]ò—³Š·›0ØOcHBЮ³ì²öx…3Ü=c¯¬„}a7Øqt8XÕÉ•ãñgvT]œåÉm]‰²Áä8¨Ùd•¼€Fõ˜°— ÊòºœK_’“ÅO@É€…ñ3 †·°G‰.‹;C1C™\,çkE1¨—Â7Ù¾kA‰Øˆ“ïåÇO—ašm‰Nç¥èëCQ–bÀÒv¾)1ì ªE£iç0¹•P V§_lr¸‚ÕÅd$¡C:]½ã¯4
+߈–RÝ|~ n›/°c]¾š6tYÄcýI–Û¨³Y p.[â@†%øWˆõï(-ò÷„–ÏÞÒò
+.<_ñJ†1mëù•-f´£Œ‚¨4;GPˆ{˜éáKŽk€Çà©žûÕ±óFfìëNy_JB„{GÉKèì‡ïðr.¯å$0üšˆ“4V¯†ôÒ¦už&­•Ã,½CO¸4?ƒB›œ\ •j¦tåÓ4»Á4áù+ ¼ÈPÀ8KI[‘Nµ ì'åÆ_V®€º,ÐŒQq,AÏ7ÓzJ°&…¨´ó,C>JôµÒ  Ÿ2l}ÈÍÓ ÒP ™†é|8\Ë9BǠӜ󨒞ÝÌ…‰ÂÄ G”øBÓæÅI0?“Ò•¨ÑÆHË5UÀ¨M‡Œë‰WÈ[—óoN˜—Nº¢¯£}ˆWï‹W/º./°Ý¿]ÌÔ7¦Lþ ˆ³C™¼@NéÇ”þ–Cðn]–,åËàcœ
+ÄÎ[†¨Ÿ
+¥²
+Ê.î'¥ï¶žuÕ0ÎÕžb…9ÜQ™M
+¨Bç&ýD —¦D Ûˆ“6U
+iF¸Dz‡¹#O€éðwH)©¢7'J*AÇ*ùšÑn%,šC É„Æ9ïE@9±Šü¶Èµþôy¢0 uOåPò_á “°¢ðªb¯k_QÒuÊ{*ÉÉ<־ǵÛ`…Ì(W+!4f¢9"|
+íll„4U0ÎÿS^îXnÞ0ÞŠW0‡O¬Ýzný·ù.*‘DLºx‘?ÜfR% ]åK÷!rXô¹ÁyÑdþ³ËdϾ—>½h óqBòA2þ9nb·p€Ã}t ¹^ZÂœ3P¨øOulw:îÎ( °Ä>²_xéØ>EìœËrŠ8¢`rŠs†øÏd.ú>‡}e Ð8 p”ðuXRJ1,΃ޤc~ŸJ‚+º2€´óò·sø\P. xø„ÏS¡#3oZâ €¸rh‰OCYVä\…0yøìeš?öU9Y‚AÔó*¼
+Ã1ým3 l®ñ(랈Dµj“$µžåËX¬ê‘1›Ã7°3äPLÈ Vbõ¥/}`%_ ŸyÓoê7ùìáÄró¾*QÄSøzx_œg“¥·Ñ>• ¹#¯"YBð¥èÎÙ0¶ÅÆ7k
+ûÆ8ñžmH£æå²o‰¸«`%HO'‚[† ;a“}{$Ÿ DöÚY üs…-YÝ2Üãwt¼øgoEMnI®@„ñ¡d@ãÈ¥ü⇹”(&¼ê)‚y ¿áoljxjš§ÌÛTºR NbÂ<ìÃQ‘ÁÀ±ç[!®%ÏLp«ø7¡\+žÜɧ=ýr§Ó¾ «+ÖZ¶‹!úË‹½"‹ù±6V]éÔݘÜbx8`&à½2?ì$ Z] Ç)Q¤©Y>É?›?Ȭð©¹ý$è†ÖÙ×|þÀÕsWÓ–r,Æ(ÍK™Ò ôûñ¿CA
+Ρ-q•¤*O¬tb†Ò
+DAoBÞKÄ@ÄLñKqÓKœÍ`é²ÒíGÑorIKɃÏñÕPþõŠñ—’Ý?Spc‹ú‡cØñ®´‚ÕˆàP+,{]Y 8ñÃlnu 0ÇpöH3ŠØU\µ¶/•·-–V5e‚›g8J‚ƒÒ
+øh²Æp4¼ãJ—eÖˆ…B6ô’Œ¾Ñç cZ«Š6bäàGØ †¼ÂA¼ƒ,°F‹§Oe2•%»Ë‚%ܳ Zw®-jãvõHÓÊ0æ¬ûÇt’‚/8*Jà欥‡"ù¯9ëæ„c´ÒE?2Y.^a_ºf"Ý­^‡@ Ì3/ùÚXÞ³h€%n2÷W5Áp[†R mðõƒ)ûs÷S°u9,×ãÞ?EF<`㋲ö‘°´Æ-WþúPÄ€V´{‘º¨çÓ ˆEZm¨ÒäW¬\äYÔº¯³¯ÊåÅ·‹n×/ÙVjKó½µàvÝ7ŠÞt-Rª»Ð)齄üÚÔ€†2ÜK´üÿŽÜº¿ò"¾oH†¶“ˆ'é†`)K­ ‰Êw~{J–gÐ&¬ºJ$•r‘Pœ{Aö´ÅU¤%VƈЩ9Áæçô¤Ž¡ÿJ‚»D oÄD¢DÞ|®u8S8Æv·÷|t~NÀOCIÂÁþhE´z
+¨Ë1
+=
+sõùáQ%áó8Åì¼)Cw깉·HLÔù3ðŽù%aiÜȹ¦
+“@ Güø¹t@4/Á0f¥½%È4»ËZä î7uPM‘ž$$´˜C@¨j]b=±±|(†£(ñP‚é“A"åx3Á•î£ñ<+®¢KF¶oÄ9l=eÌ…Eös
+H‰”—Mr$·„O ;èî
+™sYø§wt÷)aæÝüó·nú˜+7W õÏ?>6¨»…µÑ[/ÐXË­MQ=çtÞ"t¶Uf}u6Þ@\\úðµfë×UOïiiSúšcˆØçÏ?ÉcÍ5xèm 0]o²xÕŠáöù×-(„äcy_ù¦Ÿ€ôK„èb¬_=]÷õ+o:O9‹4¬p—¡ÑÏÃû£©5m­¸ÈÏ[ˆ‘o}„ê°¾A¤NÜL”uýeÐËe/‘ý
+ä¥n jÜ×…Àç°ÿ$B3 ¥ï ò úúïLg9äó÷:ûÁ»iå9gŸ;òˆ¶|i›’5¹Å8µådNÈ¢J]‡JH×&Ú=ÏŠ ‰IÔ¼Qˆa<‚™Ó)äû*Ú?\Ý8«Î>j¾fxó}NNps²ÖA4ñ!Æð˜ƒåïË\f o`^‡t1­› _Ò²Gû…í¨ÂL3<ZÓNÇ\tJÐ6¼è@Q5%±Ü} .J„cÖM|ï„ÚÂO—eßuŸ»­˜ƒ,„â7³y $w)OBØJ:m½œ«dªKkÍ[ïoÎid”úvþ‰ƒ…Û³–ó±ÈV¼*
+1†I•jç"0ô\¦kŒz0Œ1•!}k“É‹9ÂF¯fæ'‹ ÍäbCfðgÍœYSÍòÃ.uUm+Œ÷êrŸ<E7Û9†<FP£ÐQ-Íw™6“ë’Õ¦þ=Ó°!F¦D¨exÔU±i¶ºžìÑļë{Ÿ®çi‹âÒG½Îfƒo[o aDNM¼_=Aô¡ 3AëÁÎZ§HφÎfóªÓsöÐ*
+öè
+özîÊ!¦9mŒ3Ú˜ƒºŸ®JC1U—ÏjˆœJZANâ.ü"ºp"zžm ü}rŠ×[Ž@ü•
+ð¢uÕÕ`S²wù ꙯ž„ýŽ÷ôa#rØrLWÝôÌŸt¤bK{6×ç 룃ì4u ù7™ÿ€ö[ѾÂqM6)îû:SOÐ;Ð’ï“UXWLäÌÕè y¬–+q3»ÚÄhŪé (Ó¸‡/aÚÙ ”jSrFupU«0ÁXŠäUCn¨#½hèQR†Â1GÍ6Dù$Ù›«ë½‡®¬_ÖMð\Š>ÖëÅt‹EÊÓ¾Ï!n¸c§û¸gç¢Ìmd1Æc Š˜§­«z&Kªxuлêà§O;”é¼Ï:%)2E4®§>Ó$s R¯ÔÚãx Üÿ By³SÒM´
+ŠÅ¼$rl Y&ÞAö»×–Y&[æú’íÖ#]~Æs¤s_Õ¬ÔD .ÕÆ0#­Nì‚4ƒé\p9 ÆÈ
+D8¯ææ­O¬äu¥§öQ>f³jI§>üÊ9¶gŽd8Nã¢ÊH8l6V6šœ ô4iÊÒ†RP`:ÖÊDz½Á.ÄHO§Æ4€¸xô¤B‹´x{¾§Ž±K'uQ&,Ä jÄÛ«Ð6’IæWxƒõž™›¡{‘)Zcïk7D
+D2ýÐ3<v]õB+,©ƒ<Ï7œýþðOQüqÓèëÖ9ódTßiKfõ¼fû$Œû•â ‰)¥^~kgmO’Gg‰_cGwNÈ.ùñ€¸ÁK#,zò'É3 Ö¼ SÈXŽCç` X7ù+¤|Ò“tÜœÓQz„VöB iÇÊ/›Ãƒá¡(ÇTÞp²~2zsK๬œÔÀ)äV*–f"}œPOa¼Xr°{M "÷›ló¥—( |ˆ
+sK×®,Aµ0‰°-^ÂÜ)s$gÌ‚P¦¤¼‘ëÞ›m
+Ûî;f×tûäy”½‰ù%ŽÖ*…–›.³]îð^Z\Ô£ÉηÞBÒëàp¦~U´ØRБÿºŠE†.„[ÜÊ° BÆL^Tà¸hD—
+Ò‰@Öc3êK2ôf~³ ëÁ
+{-nüb 8l9Åýêœ#»€qùññç‡|þ΅†Gp0œy9 ìóŽØaþz¢qhÓ¹­W¶=]¨„Ù(÷1$%ð³"ù
+½æ°:$u»Ï0^ήƒ—iÜXap h/ìc°ªmó\?Åf_¹(uÂVçQÁ… •h1Úp]7¡ÀI÷ø˜Sw@˜\ÇF°Î}΀’-rç;n '”»Gîcä[¢öÄ?¢‘
+·9*ä=¿d\a¹=¯ ›Í"õ
+¦ BöÏw$µÁ÷¶k Åùïnéè<ÂÕNv‰rßGÉ@¬¢¶@€ÉJ›õ™ª€´* vå/Ñ) C‡œµ-Gôe{ÎO
+‰¾ª„õí’AÒ@GMG1áãCÆ Ê†ÕV”«WÀïN†uñ.xu/ÂÙ’I°INF%¸a
+/ǽ%\~‰ŠIráÎGûsD9p
+™ÌÇ™ YJwÄÆ»¢µXËçÿ¸.ºÂ²@ëi×ðX¨¸{ÄŒÕ!²“R“]¥ïóܦ,y<΃0‰ì@Ááz«»µXU¹Jœ”± >áó^ó¤sñ`Á«¤¿Ðo¸fä9”)â$Z˜‡.LÕTt•Âb8q± PþXÃÊ°(KZ‰kÝŸ”wÁ) ž£žŠX1ŒuÑø⨷Þè'~S‚1n¯z4ýÁØxGýh`*½›LšÇCk1a…E”ÐDI߉øû´Šn`æsnc¶u pý ©€ý
+4B¡ ¥¯›AÖ/ ¹.ñæñ¥,DÊuö)j²Ç"¯Ž?! çHèHwà|L) ¬ø³,IA¯€àwå5v“Íé8Oùggñµ¢‡ã®1÷ñÖÂ$Š, ¼1D·§ËS!74~#¸ûÞBÛ‘Ìz o.2¦)sè­Òm³xK˜A–™P̳ȸ7lX $‰jò2ö:`AdâéÌ°µ8I¾ bÆ+Ž×ëú‡Ùg´ÝÕTdÅ$`›¢½ß£¸™ì„Wìøý|ǺQ+bF«x}¹íŒ<tƒŒÚžgó0˜¼¨Óg/Q´R\Þ.QÏÍä–-$°)±Á™ýæ–$û4øl¬$ÙŽ‹4€&ø¿Ø¬ 5nÁ`®ã]¬cKMCn7skb¹åóéÆøÌÜrCTc9®ƒßÊÀ§¦|îüÿ«¨z%¦¡íÏ*šÑYýrŽ{z3' ë¿?}ë9Àã½bmÕ€RoŒñ@®ì¯›)J–ó#,ÅI9âÒGÈñát¼¨©Ý÷Ø%IlŸ¡C‰˜¹äOÖôÛ<ÎèWzÕ•¾ÞÞê‚@DñÆìôéÛ€1fq÷M¤ÕuBQR=_t–µƒUw M䃜Z„í8HÀ 1ï-"X䌯e·<O
+4  ¤Õ°]æiÈ~nÏg4œ!‚¥_Á2˜3ÏsÔ²Ø'ÃP¢—LÓQ–ç9Ýæ:‰:‹{EÕ`ºi)èüëå·?^Òëï¾üöÏ šü&‹‡˜+ù©Q­a¶
+þTÊöò{@KYð×û4?”ü°iBÀˆ'gï®ßÑhØ è›é–3Ã]Û–RB{¦X·ð‡xÚJFÕ¶]-ű²†Ë4ØšÃÖ°¥r=Í¿C<ZMÂ×P' ÈxeúÁK®%CqQ J–äé^$C+çßØÍóRÔß{,£¿Þ® k`4aŸ¼óº¾üAIüå”ø±0’™˜ÛEë2Ïgñ.#\}«ñ©d×-Çt.£FÈ?:)Ú‰HT¤xyÉRô 
+QÉ“ìÔ¢Mò
+’, AKÕ_^ZàñÜ‚Ži.HNO³ê ¹a Œ2„ÁÙ™^¨§ñz›Ã“æýR:¿™Âö·ª$Œ/†Ü«o2ÛvEVâïk *ž¨=`¨>-#T s®þå"ÀACsÍfš¸÷¥¢)·<¶tkØÌnEƒ¿`TºÐ4J•¤¡m‰‰,"‘ŽéÎ) €X¤~¡úkðÛض­ßùap ‰ æèl‘_‚’}'«¯†ªvJþe¼Ìq$9’(z‚ºCÊ
+¾/â G¤:'hP«yÿyß̴͢  ˆêŸî¶ü¹¤Ò Ó“‘«eµ;€Ø§”h™–MT©7çÐí¬È¹Å#!žZºiåuHåWHЀAÏb¼ƒ^<á—݇bÁsÐj[.ØL}ä®­4©wUªŒr‘ j¨ép¤Åævã  YªÈXê32‚Œrt„öd·!Zt͈X¶a|,ïr«Õi\ÛšTû‚,‰R{^"qìDÆféç~Uµeë·«drF.­—]æ äiJ|D `ÞëŒÅÅX"Òß;øþ¹oži!Þ®gŒYø†ï’ï‡òG†jƒa€3‹$ì× ¨O‹¡Â׫ÚÉüª!7¦î•]^ЖÐØiÐTƒvO¿ôĥäÖ§jàUV…y7Y5SýÄVýaðúc̶j!­ÇX¬ÓXÌHc•àÒ@ìSôcQÉ3 —ƒŸó¼þvùy:ÓÙùyû*¼7L#§ Ë[uê'¦ÙŠB;¬qyê•}êqL™kìO}Ä·ai\B®ò¼y)Ã
+w ü9ù÷…;ß=Gbg ŒØ &C~ú”ÿUÅ7¬éÖŸa/ü’ùJçs•HC-™Wâ“
+þµ½g
+ê `à…µ½°™Á2êMú›¤½­)@Ù ¢‘“¡ÁádL
+‹[„ê9‡lFHÐÒöÛHÉ$JíÄɉR“eî©ÜÔ¡Òc*«¼LÉ€:÷`'ºÁÿT8}œsà‚Ï—Ó¿ò*©²/ýT H%ᮘ£k1?|Z!ñèG%ÆQÏ|ÒÊÌžV+¦ˆWlùdª7IiÕ?EŒRÚCÿ3«ü‡LT•]ôO1&ˆ
+^© CÃÐc7[:¹#T)MÝ…ºÞys" iÝ´ý’ \éæFÙ†U@¸Øþ(¬ÑÍžM¤*K&£ª$”«Ó
+EØðFVÆ›Œ:éu¾<‡f£Êqœ6MIÒ–)\F©È,ëÁDûÒHµ®œ†®ã}*ä[úˆ/çMùLu>açHøL%Œ¡ ÃM—®'£°Ä èëÄIu Ù¬Yý2÷çg N6ÃIKÄe¹•0Dè Ô½á˜u¸ˆXĬ\T¢—:•ôù£¢
+-5§u¹`¸¼Æ H>3:ïç b#ºˆVÚ¿?õr Ïžˆá
+âŸzÑò³]{±s¶Ì*ÌàQ‡7²4A,{ÞÏ)ôöe¾\E~Ñ”$/"~Y_Î… g°ê­©Ü7§ÆÙ‘ø˜¿©Þ(YTy¾-v Òáù“Ü|&pºh òš-TññÁ°CüÍöŽ]Á<4f§{äã F`Òj2²â²2]ì¤GŸ =O_<œ‡È¬>]¦ —®
+†L² M‹Í°i ??â
+ÿcÜ÷)wÆÉ!„øÀœß1BÀaEç {Ó'‹àÝ:zdÕáåp»Uü¬À ºG¶UeàˆS’?òÛHÐ
+ÔN—ëié‘0’ñŽÓ)šúµc²{ƒL~ÕR‡ðBþˆl£>§:H.Ó™t¢—Kó¼sì0ÄdÕḓÅ3Ž´ç$Ü<÷ESó #í7Æ^‘Mæ¢l%‘LŽÒÃt°h³¢a%-bãh+0 _”1ß”wI³þåàíDžîÆXÃÀÎAuCùKE”MŠiýò%‡žˆhI窒ï )™Ø“vˆ.U¤ò©«=xË\ßåæs™uÝ2œý@œ$®M5»ÿc¬æÒP2U^#†1É:i.Üôãå)ÓÄ­‡ E&°:’Ý@JT9=þƒ¿«­ŸçKÇ”á†ñåæþ›Ú3ƒ}xrhq-K¬=„—sa¢DºL-öÂf_zËþ©ž ÍY~ü$”çý¹™ggHP1טø„„Uøî’ú>)l9ÅåÿÜÀ1ë˜ \Uîí °
+âu9¬@›Ú ¹ã½õ1‰ò‰pIBwÂsògSkXC9ð6™Jt®+nÍñ«Äã|‚ùAèSmAu)(2"‚Í kA^zõã#=õþ+a0!ä¡*,<¢++ÐÀgç¹áÓ‰Qèp’ç¯òk ±©
+³»óçd¬ñÚˆ—p5ô-dFö25^|‡¤7P‰ÜF½9-¢ßðUgÜQžVøF'¨N´4Ô¸µŠ³R Q˜ÁN(oõ€þ}AµË‚Q½†—ÝÃ-^E‡÷X“ _û¿‚Þœ‚@C
+ê°oÆòP»ÐTÞÿÔ7@R¶2Q~úçü$ˆIh
+¸(Š@-;Ž—Ï$Ý%ˆ4˜Ëbì<mú´)d–2+P„åioç-»ä”6lÄmÀsÑf+!0ð>DùÙ£Y·òF´Oå'›%à@BÛ”ÐéJè
+E©Ø9jRki~ 7{ܤ(
+ `Ú³vQ_*#Õ…ÑÅ°²‚Ç<~‰öŒÖò»‚:%+ФÝZs§
+ƒÖ‡·0„ œ8aCF|Qˆ ÀعÍû›·À—¢¯E
+Éü£å§ä¿Ìleë*»ðlÆZPRV5fœ´'*!L¤‰œqF÷6€
+²Ð‘¦˜òJ ·0 ²ÔõýÍ/ÒûÍZ€vŠ®’¢+wšÔ7Ò&_®”?"¶d=EÁƒóü£¦P¬Äµ‰O÷ì1‘I6 oG˜xŒ%@³sàx¼MDU\mÑŠü´äþÊ#°¦È4Z‰«kþ9é‰÷Ää¿-愤b¬¦ázQZº±½ÅFòûPb•‹Ô‚^;{S4]-ÓXÇ2¸7>”w NŠ¸2…¦¼J²|H†lWûps¦£ÇQ—yó‹” 7[ÊÅ…ì9ošòQNÑ×&L^žÑf¦¾ƒ@ Cª'_ËŒiÓc…Ö¥Á`•
+è×Øô8]1ˆIdË:fªG§dϺ"ʱð'%]ÎIFtÌ…sæ¾ ˜6ô³‚ÕJ !¶Hw™Ç k3{]R/JÒC­Hºävi¶uh°Uá%ƸÆd~‘&®i*Œ´Éi­’„U‚U“‚™•$ö¿›…ÄÍ;¿ÁtÑ鱟>m?±¥-ìÀU9–½éµK–„¤)5OÅ¥¸?R¥XUt
+¶CÐGæCFjàVüË1â˜\þGœW ®£LÍaì˜Ê†±Žˆ-IÀF—Ì@[“í^ÎDæ´-FÈ9¬fœòM`exG%x¯*äó9ÏßèSŒ“@’ðS„ôâY¢
+â[Ž%ƒåqM‹:¬D>‰ÿú$óîH*hn&ª÷¢KÐýzó>†ÆËÌ°/]ÆÛ»2Ñ2d'ÄÜݧ_JÖÓq,ÂÊ[î9 b4
+1!XµÍ\Gu¹ÑTìŒ'«ñÇá¤9ŽE°¹q·5Ü Aæ,ùþÔVë[|z·‹(cùè»ø8EˆmÁ`DŠ@ ¼9óäžÈû*ÁÂ#¦dOsx]o‰¬W"± +
+˜ø£Ê…uuIðIC©•£,áfŽžvŽ¬E€`zJž²¨ûÂì'7nÊ *¯Ñ¾eãrÎxFœ[Õz¶xJ~H!%IbI©0;’Ê":EÐ4Ô “åK} Jƒ·XýzB("&(D\¤=K¼ß‚òd¯žf8/¿kÓAçœëœÛ\çùóQëKWXÀß|^[ñ²>'xƒHŠsyQòÏŸ¿Aò·ôôF«ÖŒ§Œe_îÒã]¥‡±t6Cv¥‡~‰YÉ\"MWz¨päEdŽ›'=<.
+kÌÇ{FôƘJ‰ `J[Œ“®î5 ÔÂÄÂdoI¶ŽŘ!Š‹>F Œu¡ì 4ÐHUâ™×£PZD/¥vf§³H­-±‰8º(:È ÂOtJdA
+'v
+†‡Æ.OsuCŘ¯­J,gpú.KŸù—7l9bpƵŒ+b¯Û\IbðmóOÑ“
+GtTc_¥Á¥S{_‹I÷ÑøÐQ’ýjŽ¯›êºgrêÔ@ë³s'¼†ÊÁ 1UW±ìIPÅ> zš#"Ou…Q3]™a²‘§%j”—æRטù*Ëk'åRà)±o«{6`ji†¿â€ƒYfKmƒFqϹÒìÙV:l}²§k_`ºÞ”É07ÛÈUê¾–!r,¤B©ny…BY‘üð5¨ÉQe¦žl°?õƒPIÓ K©pËâ°è×1ÁP–ºØJÄá½Ëê·1û…=gí¡6ZŽ“FIl¨ÎÓÛÊS12QGžë*ˆ~Peü`Öàå$=­ò´Á¼AÑÕMA Šr¤¸xô­2W
+r’Æõscüx§Éü¥Öˆ;Bç–vÎp¨Æ࣠_„»ÃPáqÓÛ(Fš¤Ñò¼•º¾¯a€pÕMY -ù(aõ¥þS{ìæsoP^.™f#ËÆP#æI[% X}þWA6H;Èm1Gý¦-ašÚ¢SÒ ¨a†åÁQD– ¬zc¹ W]“²Î: ®Hd®n=ÞŒŠN2@‚(ã&oþÜÞ58Jö‰zú–<|)Œ[µ9mjt`{lO–¥!õè8cQ“±ÁÔ³†Y$ˆÌË£ùÜçàÑš¬)"Š ì"ÉqCŒË:Ó™(v@N‘è
+Ë÷§þ{‚4gññ?:A¶iÇš)
+"Ui
+wÛÔe5™<\"îò!ÞgØø¶!xp~Ç
+Q£–žZlÆ‘{Zr ºyz¶á\®¾‡sŒ’D– 8D‘Œ+ŽHP\‘
+F3·•þÚÓ&
+kÇÿ:ÈŽz*Nž¶2‹RMn‰¯S«>?|ÐKë» µ"À² ©=¼+wÜ|ZGÉ‘ûtüŒ|þs:áõôs Ñ|,/ZÍ̇µÈçF˃¯ü¼tsz‚ø v9G1fBG3rïÞ†`T÷ÁÛÍ«4í™g…$yw«sÝjœ›ZU&•û»ÇÒ"«1“ö—~ØÏéª  ±~8ÿlÁ:¿CßQË4´Æu<…i¼«X½qïMæn¶Ôb¸3œDë¯"˜ï[¸ò—»hóÖL"»RõŽwÛ²ý0w ?Mz†umÆoæ¿Y)S†â1´ñîJì”rF ‘£nG匘ˆ½ñÆ's£¸fQÓæÕ{¨uçNhBµ›“4€VPoNjÍOK?·WSðÇ_púŸüñÏZõŒò¼#“h7/éÝ_k¢uaòàgÝèäþôxF;ć Aˆx'²ëpH­3*Ð ìñè¯ §éTœ¶ç™è'G@82:u×É©½+4Á€‚R^KÙ:d¥çØ”óD€æ2ºNÅ>âa†á‹R|€IѯÌãƒ$M
+yb£Y Âz…ƒÂC9ðß„dž0>ìÁ!øo5]´k˜*¹Ps2V6«Of‹á2c½N
+DêF4yøÓƒd²È1e-=\)¡7EŽ¦2<"º¥in2Ð'°1ôÄÛ{7kÍ}ÿ„»“âM’BfcŸSÔØ)ƒå’ÅŒ'[Å]D!)¡VA+Š ƒ¤4t·Ÿ,GLaâ´ñ¶ ኤ8¢Z+'ÄNàìcJ œk¬Š­mq Ö…ÁÃäáaF…bc—ƒ ]3ækBhF7ê¡uĬ£ š)­æ‡ïô;´6ŸnâXÅì:þÏ«¤‘dSÚQ†[Œ0³:\VèÕ؃|ôJGy ü£²4ïË» Ug*Qÿ±ýWQõÉ$†C6·^u­ŽÝd»Ä6ÙˆScZÃ’ÀôÒœšÝ^] ë¨kÏï\™ãÜæÊ@÷UW&;Õ¹ÏíƘ«iŽ­¾äA$)îÐú’"µMAí6§5Œ;/'e$G‚iÚ%Ñmn¹?S²¾"Áiº³½‚P AaïˆÞn/Ô’5…£ÇQÄËwËj¥®2âS¸1Í¥æôÏÆ‚ L5"71ÙÚPb"%¢µVsWC®Cx¬øX ¹y»ŸªËì‚âÝZ]P5ºÃ_l¦\ ´jtbér/A¹ ²ª°yB2ԡ¹ŒÒbDšª$zjˆ9T¤<ï=‰NE©qw; jWÕh³Ÿˆ;ùÒýZÈ~‡ü;yGX5©zZqÈùwåœóþLøQ´Ož’¨TÓ~Û†Ä^ã
+ºÚé—C2Q“¶fóBŽ Šd\â¯íúœ›^ÛSsÑêIB y½<ј÷:V(Uâ!ßìŸ[–øî4¾¼>8Ž÷çœYÙø4Z7l.3|æÌp¬@²|B~V0Ymço<—2BL*d]øÅï2Ê Ä¿ZD…L[ ÏÔÀX/. 2֪кº© d‘Úƒ ÙîwÐRRœN+57PƒÈÅ0‚˜¨ï˜{&â6¤EZÅ‚“ºk† ú Ô‚ðe‹X¢áj HbëŠC±øš†E‡]¡ä‡w­túpÍÁ@0&)
+·-æçi@ÆG3º .>|’ŽËç’¨(+mª4ÇÔ€HX‘ ØÉyøG tŒ!„µ™¨a¶÷œß&q ÔÈi9IÁ’´ èùÇ Ëü}ÿdH–ã×ß@¡y&}0R¸-X°Î‰ç<WyŽ¨á‰ A¨t"TÓšw`æŠ1Ñhò âñ$Š[„ éÅpzMø˜2ˆ~$zÐG*'c1(D%Ìù%·Žç*ì[¤<„­oB˜[¦[O;ÄbLêPHÃê€XP¨VˆÄ¨Ð¬·¦e¤,
+rqô=A䑨Ьôã€Tgåh«Š-„É´a¶2³
+Þº/ò€œƒ »›¹‘ÞpØ>À*p«b‡\¬¹U3ÍÿXÅC«Qž”v(¼ñï<–}…Iy„4`B¸jÕœ9[”o’Y#EÆ~@Œ,€„õE²ÜM©·ÇÅç®0]°3M ¥_˜~ÜžZ1aTäž6ãa =–¸ZY Â"]FñPÁ5\!rÅ4㊷âðþNÚ…ÏR®â¯J³8.cI]úÃwo‰¨LÚ*¼˜Xj©ÎndÜÚYyÙb–h|V*·ÝU‚±Êoè­„õt^¢‘ÈLÜüŽú®v°6ð!Qg
+‹ti‰æ¿ ‚‘6ß*ƒ"Y™irž—AqwBÕ
+C“³‘{@$–˜ÅPBѨ©‰àF€!âÐÎòðîÀéàbyké°PY&„GYG{Ü asÓ@³°Làr6‚ˆFTdWûYù9è¶Ì㊼,i}yÆ\®¥SÔøAmö5Ál²Gü³{?‹ˆüHžûÛ.CH3Ot„M®xåý„Éœ¶C˜°bÚ’Ÿ©].Á…® “ú°¤’³åxû¨?Ðð×R^ì&8Æ».ÅàEžJfObú†i@þ")¢Ï P_ ùŒ—I¶7 EW=h:ì›±§ÞE¦öþ§¹$eÿ*(_ÉÉ 6T$‡×ø´n„¨Yô :i=®O½†]BT`¡å|†‘EòÑðº|S?÷Vê’›P8¡¤J˜Ð=²ÎY O“åæV’ TˆC®¡8%:jÀnƒ.ë¥/Uùú!yÀ*ÒüTö»å Ð <X NÃ,s?Œ¨0ž‰Æv÷l!˜ˆ¬4B(tN"{`ahÀ1ì Ix²)ϦCM·hM¬d>3t„Óì–¬c» zøÄVÓ›ïàC¯8"þ ?ÀI+<å5L¤ƒþJ™ö£
+b,VÜàU%abøÍH^ü¢Ž—Í‚·ŽUýÛñ®›c‹
+ÑjÎw®ŽWó Ô*/š½lªè3QžB oâ+§I,@¯k‹ ‡ì^ïZ‘ÿÂvž/lçƒÞÑPÕáh8q¤öy‘£NO„¦›¦wF
+Ý—“
+ÐK¢dD‹áï~1C¹Œ =‚‰×MœÐlk
+“~‹Úcï Ãá£ÃcÿÚIìC­ø©qTƒpØX8Òbµï.HEL½—ê êûÿx±txraƉ ,ÙÛ …Q´@@˜mô6HóàÎ5–·¼6ˆžðŽ^hšJ"-ÅqBV稘øÍ8È{iCáÄ Ž «6x_ŽÛu(9á“èõJ½G3§œã4p¸E¹`•B–&F Þ)º‘³oH"Ùöè<½$­B i=\ö²‚;>å˜ñµ@_½¸¦€«ågüS}3î þ&OW Ä”‡«a¶XŽÚ¸Î<â`êÞØÔlà–ùFfU·Äñ˜ vØA‘Mg¯‘9ì]<nKÓb«{”@*KA–Ý›U÷ª“UúgÏZÚMt¤¬"»^$÷kÕïE_ârÆ'54œÜ2u¥l¨¸ÕtÛ ªG‹À
+Î`:uŒ:]¦¡B 6ò
+s‘Ø„z8v?ÐßœxyK,¸(‡×o‘IòËÿüÅ&‡Rd6÷»Љ7Š$H´¸BOtÎm<rè{TXòmûJ2vìýÀ  6L)AÇÖ î°;1ËË#Ru-Ä·D*Îmzj+h}Ù€¿8¨ßV„<ri¤¶Ä7^Ì)*R†
+•öæ.ºw)úõÉ~ÚÕMÑ03œû»«ß‹0"¦© ¦¹â
+ƒ./-ü¸Èyßý¸‹âŠxlÖ4·"’"y /i(È@N»A ªé ã·-­Lƒˆ³" y`M.€Î¬5Éü‰…(Š˜Z†,~}l?>a"13å;¢8üfeÙò®èÐs•AK¢Eq×ઙ´ÓtJtº§Ýd´ 1¿ùÎÔ‡„[^¶K hØŒŸØ£ú3*H±6èò¾.”#á–ÕN~+§NÆÀŸðŠA³ª}FÍÃöã#J[%SÞ8òÎØ×IµiÍb+ïÌP¢;PQ5åæ;…¥ÇêÖ°î“ùÙЕĔÓ2êðAaò{œü'âu‘ôýå·qÊwW-bZèNÉjr‘”+î o¾úº(»¾`a0‡¦®‰#)hZ™ó~ÕÀ
+Š™f‰û(¦­
+ˆ£1ï£nÍarl#TCxøP)¤—jhÍÔ—Š…ˆû¼/Ÿq@s¹‹‡½Û“< #0½¬ Ôûpešl8&.¼©ø2¦géè5vµõ7þATÎ_ˆ>cŒo¬Ï½È¡ËYŽïùÿÛ,šˆ° Æåã¤êØâßVÄ$1¢"žàzpJÐalCœlÛq¢…„‡ÐvÓ¾(Éú %Û¥wK­Qé)‡…ôôD‰øHéø‡¼Š˜þÍ Z“HO½œø¹1.j`„‰­#®‚¾Ï“`p$ !©¸n
+Ìß}HɧÚa$5H¡òð<½—§§ö SŠØ£
+„dL 6N‚ ¥8±êž@£5Qàïæ¥dÉ+ÆýNxêöt<A/Çmò'½PL=ÆD¸ÿ=§D£”ÑPÖ¼J'ä>1Ûa&‹<`Øg\4À
+V¡t›â™ƒÙ*
+øLѬÜ!Ì5N¯¨§‰§ôÅ´÷"E¶pÀ)¨¬Ö€0Î7hWÉŒ—Kv'†W=h:@ñœgè]díšï/@‰n—#M||Ûeš†ÿITͦ`ëŸ^™…ÖŽâVl*0|:ôO¾HùTl>ripWˆçEŽ÷s*ä·?ÑŒŽ°€—Óë®|¹Ö¢·%DJ‘Í¢»gŸ!ž‘d{hr0YSS'=ˆ/t]¾
+;Jž¼ÒŠjp\í¹V°‹c-ELóut3J$ç{ó˜«Ô÷@l´G(o˜")ÙxùtUÆYqXaš-úõ›!R
+gˆo²kFr†bq¹ŸÊÍwSº½ ^#œ\Æ•OÏg±G¹ÈGLË
+ÆíŠ{ÅÈõü«wP†
+§ _¬Õté*@KY|$Žû\=}\Õ7W.t¤ƒŽÐÏó»?Þà ÛC+bîج5m]ˆÍŽÃ3r¹¦M¾EðFMÚ7´ƒâ$ù˜va â $ðÏï:Fw*ÂÙ1ä)1QoP,gÂô¥ë䎫;Á/8 ËáH÷¬Ä(Hkƒ#ˆ– _ö‹=¨Ãã®È¿.è'Ùa¸óìý(Ò ÚDÉý\FKÙG¥ê:´xtEo8?L4—~ø‡ì ë½ö¯N^û«c †©ƒÑæv®3ß;¥U¢½Z/7?$Ô$4úˆ’ˆPÙ9#|†k'0┲¼Ûg“§NÄíã[ïú §{¼æµV—lR#ð“ØÍ«[®v{uu»¡2S½´„ªñ£!$[Ú”90žCˆ_ÉH–$oFÈ*ê#¿`ÀÐPòFí±_8ù‹¨&u䌠(Ж¤Ø/DóÆMt´Üƒ‘ Ì) *«+\ô€äš„[I0JÉP¿X>‚ O$»^Š—!²¥I#Ϋ؊~Ö[i/•Fhé…C¡gæŒ3ÃÅ£–+.1
+9ãÆæ9t·_hðшeò¼´÷ÃcäÚ3À‘øÅ™#ÍÑÑ&ƒÓ
+Éã"žDÑQ+\2¬7ßké¿žŒ¨ˆQ#”bñA:9BÉô#šy‰¡ˆULM[…uÇ,@èÀ¹ÈÓ|DùM«ªÛá¼²r´.©Áƒ¤{¤«CBˆU„ΞüÓÐR3üBŒ>ÍüÓ<¹÷¼w£øîe¥öŒèUÆ-wEýUj«á:F2EšÐµ²m³’)ø1"Z yä›ùj~Ͼõý1ò‚Œ¿ÿˆÞ¹o^£2UÞ¢ý>(ñüî€Z®±p‹b±S|°LWíë­¦FXV
+*T•9Ž=Y9ÀÒC¢W8ŒÐd4?bú†ú˜Vdn)NMöÎŽwh}Ýç±›Ž_>*ÂzKpª˜j" ­nk…ô…Ô.wýÉøÑ[| ]Û eä„s'Ù¥öÛ!<‚û
+ÁîC”¸yC]MM¡•“Uœ耯
+H‰Œ—Mn$7 …O0wðÒI‰×Îrn1@VžûoóQR à.5¦Hìg©DòýPãa­Œ:lDïíãŸZÛ£yÒ¬v­¿~¸ž0Z«†7qò‘ê£Ö2¤¤>¢» µ&¥Û„ôb®Í›©Ú‚ôƒ#¤U“}UØà­é´êêÖùcã+µmÔ}LåÜÒ먭넸4mµ”1nÑ¥ªŽ6\çÈÃJ륷֊îsøÏ¢Õ=:!\3‚OꦾÕ-Ô‡I>¯~ÄtP¼ÕSš*½®aî‘÷ºK<e§|îöâUø~þ—²|Ϋ´öªTp”Uä'Ð× P¥-&«éz—Ðib´c!L(L”lØúâÒ Oè¢æMˆŒ²·^ý*rã‘ECÚ>¦8·´ZCe¶ªÇðÚ½yDì¹ jÌÝl¼èæ7Ⱥ¨Š3MÖC¬½8GöÒ¬6®7圹öÞ£N_Mè–èFHóQZ㉭ï›JYkãj¦2!\<º×˜“·ŠZÍfíNQFÚÍ;ŠÉ H£ËÊv‘áò!Ý|ðÅÞç'«C,Çmú€¥üÒàÞz_žï‚›º ù½¸x-×›$t(í"}‚7S8­"1Ï1WU€Å ñÙʆJŒU>íV¾ÈÇ‚0…B êÓ6Ä2D)Vb,?–Wƒ­s²*壟¨ûªæRêRdBx`c¨Qº½Ú Àmt}ïÉNíÂ(´8}
+[Ç00ôÒau›þ`Ê­1”¾€T…6…yäð}Õ„
+€€ÈÅb/Dñ^š2ß=ЧX‰8,F!6YS_f•ÛÉ¥)IT7e“í SyNÕd1饖N㤡T+™¶)+U*R²fø ²†"‹ÕÌ`¦ŒÓ9 èpxœÝßWÁl þ•_do¡é5el=
+Ùer¨¡ýñžÙ™“m•mn…N?›Ç¨¡L-±ö¡±½©Hµ”ë@àQ»ÔÇübèl ÂÚ ¯ Bö½+sÒˆ¶éȪ™­ïŽ_à•º^° 5…l23§ÂÄõ5pÔ4:µºÔQwPM´íS€PeÀ Ä¢]Tè“VÝ3Q`2UGš·„Þ›ù ²®âïQg¹;žÃÜÈœ*¿ä‘a0©’Þ× Sêž
+ÀœÆ~“S}>?èàžP,ÄÒÆÖ|JãÈD*&“:ÏÁ)sV3)%×U¾e¼€0s„ˆ©Ùçÿ~ÈÇ¿t ïÊ3v[»ž”ÿ÷ÄšˆÍ„¥€ìA3¯ ³”=fzÛ*%ɬ™\éK"ÍAk£Z™ BdâðX¢]Æ*^¸Ky
+̤¥T"mÉÆFRi›Â¸]>tëŸTéX$ƒ°Ùa'©D*]ëôóˆéçfÆ íÿšACy¶×¯ÄÀØ¢£ðÖ¦„p0‚ËËëöÅJc°äšåùÈ3õ‘‰u‹CDü5»€ ‚G¡Q‰]âžÞm‡·yŽ§{ ^YŽú[¿s2Êž ‡Sj"S9ÉÜ ÒI‰¾RåQæhãÛ2¥Ž?¢ uÅäF›܇ñÄ™¥ìS`EÇD¨|ßÓ!i‡8jû. ®öˆL£Ô?Ùˆü,æ’Íàp‡8΂RQ}´o Ç dÐqT¡R³ñwÐa…Ì8
+|´.W*%;v‚ïXw–‚Ô„?¹¿•N$È$ûáÏÍOØÒùÁf{º¢¤Õ8¥ñSߟC}Ž!nCN†]½h6'q“_¡>P¾Ôs›ÐÁŽt«‚šivu“Äñ‘æ0†K¾åß„PñH£)㪰#®¹;0ÖU#×
+yº¦Áíº¹~aÊžl¯YäŸÇ"¿ êéˆT€²9wPäÄÒÇx/ëô¯,¤¿²’)1q ÝÀ3V"W&ÊŠ¶ƒêî
+ChDß
+Í,
+¸ÜžBËS Õ²«×J¤Ìèy
+©1ÃOòWÄ=Ê-£äuEG3CPº%¾%Ð|QÙÙ“¡FJæ5ò$*¬OqVÁ¿ªi
+/' ™†yŠÕKŸU’ƒl6>5W2ò[ÅQ“zqp}sI}J.Œ2¦~;2Yéé/÷9bô EPæ»`ld½¶Ç§sì!¹éXfúk¿,ô© è±LDŠ7¯ßÆ… Ÿq÷­¹ÏL‚~ã·lúäŸ>øµCUú.æiaû:c˜AáÓ[FñYþCCŠàèÞA+
+P6›w\,h‹;ËäÉ"z•/·Ö âT¯Rѽ i¨Œg )&}¶2·-é8úòkŽ-õ̱úâœäG°"¹ã«cÒ›tÅû'Q^WåòF1óW”K)' Õë*´½Å©ß’@ÜÇP²4›Œ¹Ï9=lÐR‰(èç‘ ‡†—GŽŒhPû}æ~›#š]_yï~Ýèë ‚þLK -͘>¯­ÂPöËpØ’Æ 4.™ 3Å´@éN™à‡ò6(¯²\ËØ çëÞ¡š<«Yz!\ýzú¾M&É0âñèÜ¡ˆûPb!>ªFrl/¶!É¥´'_ðË?‡¾q wìóӌȎ_Û&ÞG¾v”+Cš›O£ž ë*Ö“ÜGæ¾öâþEöpæRäà§N
+½d}¤òû“ñ
+É1DÊŽJ˜SxÃ'˜ œŽ²?õÍ„.g¯±ó˜Þ§ñkŒã߬kMSKyFï·ƒ¦¸~îVCã^Ð 2()÷ÔæÑ8ËaÖÄçB…ú'hÒIÜ>’Kã‘IÅtèàbq):JrA­§Ù YŸ2:&ÉEüs ±œáÔlº4~­„Cã× ‡ÆùX ®ýÕ£ñøà:,!´>Ép',J®9›¨Xê›ID—ûE[XßœÃf0‡ªK]Ð&ñ”r@ëBã,ÓpM`_²Kã„DÌ`C¢Ó=Ó¸r¡ñkÓïá‘ì\Úy¯·‚œ¼~îVЕƿ“ Ì þýñÇ?æ=hš:'“IŒbËëi‘9`
+ŽÐpAD“É £B­0‡®›oßkQ—«á+„¬˜©„2˜½Å³–"+(¬‰yÏ­®äoiçXK]çðŸƒûňNì8è×IsŒFH[1]ŸÝ ѧâß±RPaÎ7Ç`…)„
+÷)ä%‹å™Âh
+ÒCâË>ƒ~S£2€ûÏ'æeeùúX_ŠâéÀ0oad>ÓÇ1ãǺ-æÖQµv|H@€w¥ÆCãigc¹¨É Y F𚄉ìÔ7ç¼Ö€þ•'Á9q¸µ|¤õ©×žÀ:FJµÕýð:‹•/U­]tnknîZ€ˆ ¢J¾>ï´ñAe¸õQGTië&+(ärÄ°†¿ÆtC4¡/PÃ'dŒ;À½©—zСK3¹ùX!üÖ*ã¬Ïü¡Ø›æ@†ÓüMUÿὌÇÒ# 1µzÉmšÐºúZm#ÁßÇU*î9A iL¡ïs
+®)r%¶<…€±QŠÆí¶â‡Ù`_…Þ Ïç€ÉÉCsl
+E›£…â‘ÀÔí „¤k—b(U*ëÌטòÂÙ&šcÏ Û
+óZ q¡¦àÌ@–®P)¶Æ6jìήaó…#Í©qNÓÝY¬¨IÛ)ÛDÍÞÎb­Ašsfi*眺‰ÿÅÕ_´)»!þ™Ï&–¢ŽÉa‹mÚx"àÃoö.šH"ÒŒuLåiúsK.] ìÁ¥­3¼™ÀER¬Oå|êÿË
+ÍPžÝº
+I†Ø`¼ `9A-s5‡„vŽ¢ K’õå]:‘K¸YXZÉ»bÊ‚VŸ
+(ˆ•žksÂÕ+v±>Uù&©¥¹gT„#\gºŠÔ +jÞ‹²üËÍòÍ 67&rêRÙZãDB@ ˆQJ¿tŒé–6BY}c'‚Р˶Ò5Q0Ëä<´Íx’SXÚßZ¾lZ±‚H30K Ò`ÇLæƒ×7֦ŔÅþšó™¤C×vBVcàO0×Ц7ç€Ö @a–6J³6¡!†¹”0PÞ nh4^¥´ûKT–nr(äžó4Ä2º_²¢“ê",;é“Áçò[ ׄN®î=}Q±yð›¡ÜÔ>[ÞtŸ|YkÏ6_Èë幉ÌPÌÕ+5h¯íV¾ˆà·b*Ý…8¹pÔo8($ÉMh^Á9LôˆŠLab9¸esùŽÒ#IfV‚‡nªE~TfdåÓ—ƒüš’Q·•g‚ž
+‹” \+Iô]fÖ³…hÿäì8ö#ð'Ü×nD¢i©ˆ¤uš?9lt­ØC£y±—n! ¾`Çâºu:!ëCܾË5€‚ñæD{ö,¯MÝeºKÈU ¶x0ãI–ק@\¿±Õ”Ã+T€ýd}*O4N4Ë<0—*Ù@¶±¹6;E©
+† À^Ý3åoT\‹µ$pBV’+XÆ
+šð×»-HŽ–RöÀr ‚ ušÏ%…˜ëdVîÉ#‚^¡o­žqòÇPÞYÕÀW¤ð,õ7çh³£ûÉØïÛ0ÀÊý/ãe’É­Ñè}æa­mß‚Ûâý·zŠdf”H“™ôwTˆÁýE˜[ƒö9R–ND ½9ñðv:1IëÉ¡ž‘@;(§œð^_„ÜMz\Í-ò ³?ÐEtjl§+И©Ãa°Ç
+Br§¨¢X—<J¥e>v·ó*Ä œòW
+Üáþsg{•pÅÂjŽÜÂ0!¼
+Øâö'¹ábIšŽL½€û„°¥8«yú}žÞð¡Ú@*Í…ûÈ;#÷#{}¦pÉqÚ ÆtÑž€D)#šõÜÃ.lï…Üàþä´yDM 14êü}3w÷ã~tçûÿWŠ'Þç‰égôIÎ5;Íú±‚œ¬ ¦ð‚ÂÎ6Pw¿5§_:5bKé¹n5mðß}_GµÉGÙ"(ÂÑîÚ)}æþ[!»â©6j/¹'Ðv“Åßã@ÑSžÿñ‚íJ_•¦FW–Ö¥`ˆ-È Ç
+Âùèbô}n帡¨ “PØxÚè!k0™º<WDï4V(ÉØ¡Ëã9;Ç5bUä't~·62ŽNB𺶾Ãû€ÐÕßo'[‹dBr~’3ã$ó¿Ë9êǵBiGчèj<ž˜@¬±kà Zôîä ù>‰:ˆoÙcžªŠTí.!íä $w±t­/:ð‡N~®-„Ò­Öº'œ»é!å6´—Íp‚B™á·c·þ
+ÊÏãEAÄR‹ôz÷©›µ#qŽí¡µV¹,[Û¾4ò4Ô»±U•|j¹¡×k?­Œß coNXDT°ÎÜ­\á¾6°d‡„ÀÎ|ƒ f…à\D‘Ôܺ²O
+ »§3å:îw ºàZg¤-2ŠÐ¤k[tñ-áz°!çQ0^äwl éL  È¸
+K-†R•fŠÚðiFD°e)Î4 隘÷‹piG†ÝUô¾š'iMa  Ç‹ ` Í`®šv-…à&ä˜Ç„se’š¸±4uÝ'@s$Õ­Í–ýt€NKÕ«UkÔ2HhÖ@mÁ@É¡šÕ.;„NC:鮳Ÿ.oA½k9[‰±nÐÒd&@:iGýÛŠÎÕïTä–¸˜cSÚ~vÙ9ÆyËÚüÔ9Ùàº9sB¬GI zE÷„ñâ;Së#Þê“Õ
+Êy6ÊSE.qËAdI{@ÃHO“":ƒl0kû;þ©É*Ų
+3âAIÈ.Ú¬R Ò
+
+È‘Š"p¡“ZÌ!ÐÔ6d®l×q‚.»ÉÏ>üÉ©•ä²Ð´ôtÆýci™CÁµÜ\M ¹È٥˛M!£/Q¥_W
+e ÷ņ–câXòÆÂÆ{Hã‘‚åmîÞð‚„¢ =݉¬>Ü §inP„ýò43* F.ûÚ×iX1–3YÜŽ!•±ýðk?ó3ЊƒzL͆ÝaˆèÔ\åõê~Sp1s…ðqù
+Ά"€k<
+μòÐƨæB#ÑÚdf‚àEL°87wcË:ÎÙ£’f€0”:sç¯ÓK6Ye-§ôÿBB"˜ZLÏ´øG†’‹ÿ5¤iUÕ´*ÁÝÿ2Hj_×þP³5Å5¨C„ŠÜ£é—°HR£õto$Òef—Ä ÿʲu„±œío
+G«³÷¶lð´]/ íkôÚ ÃV­,¦•Vž^Ï‚`ùlÉÝ•¿eTŠÛs–qiÔ
+°Ì"ë3ÃÀ&^S¨ÀªÅ=èF+
+YÇÔö‡-þ:q"}¡êÁ4$•3*{_ðƒ.wbpS”f°rµïŸ8;{Ý“üVÙ‰Pü¢~©® æÕœ¨-Ÿ^¡5U­ê:À’xñΡS5ÏGºë¥C´äÇë0m@æÂñ÷ß´áßխᥠ±_úþ›*|¬ ÚaP¶Àä)¤HÌ#²6ÂÑ…¯àLDÒ4ŽÉåø>–ÍWò·ó™ƒ”‚]Ò }ý“û^§áìX¥ÕfÊÐp½¨Õc_fHãÚ8†ƒz Õ%ûÛOâ[äšFŒ—8AÄg„ˆÑ¸‘Û †…Ö§Ó.s_RãVÞë»ãÛ‚LiF_qŒIjÏf”‘QÎO_ Ϥåfh;ÁÜ%¨1W ¼Z~ á¨4ß´sŽ(ÍÙû!¢MdpV.Ü„¿9Ò‚i¼IÉA-4àÐ"‹J{Ùú6Äp²ã
+>T}™h+¤“"ÌUM7ˆ?í ΨàšÌ{ZŒó.4A³Ãš† B™­z¸­—“eLF«ÓÇí•AF†wŒÚâ~Ãm§ŽÛýàš S]`y°b¦#¤_Yçc1IÚŠiWˆœ ÀF»Y£4®Ç]‚¿¹Óâ‚T
+Èkÿñ2G’d7‚è æ%¡ [`i­Rå hÔfDÞŸÏ$íW"ÒºGë*/
+BŽÌGgéWtßë=L+_ )ÀÂDrH„G›­¸Rãž/ÅhT¡ë‰PËÌ2û+6 Ê YYn²A±¡‡s¨,«õší"ü æ[(‰ $KË7S±6LuGóï«Ra5ôicà“¸ÄD²y ÏEº³©rؽEãT"2]ŠÃ…|ôè×/ät;ŒŽhWÓóO1Îü—ýôI þd}b¹ <“QÉ—£.èS-EÉ:šÝΞ‰žæWÊÒŽÐôƒv‹RKü »ò+W‘Ìà"þÌäCHA lÓPù‚ºLª Šð0BÀ0=WkêxxLæ[ ]ãcP KŸùWäGr’>Èüñâr¤.èSº¯ù¤nTwñæfçSz)þ’n¥î‚Ô­øqÎÙ7ÇcÎþs‚:ûøS‘ºÓð)I¿Ÿª©Iu™¶M Q+Ç¥ªÜ\Ù%¡†ö €NÃçuŽ(ùöM‹7fwä¡f×VFÅ‘
+¿¤~äÅx¹À´¦ŠìÌT­WA™s{ÛYŽ Z…DIO»Ö›Í¨ý(mV‹5‚ASÃ׫1ŠRW¤³ {h˜’W²¦3Ê&òž[qÏ¡wÔ|††J}ŸÃ¸«áù¼ $}þ‰yk,~CûgM^»Ú”$@ò”¸lEŽD™Åãóœ‚J# HîK†ÁÝL(€˜W'3´(bòÌT¯ÙcWkúø—¦vt@L1³&Õš†­õ@j’Ê"¼ãå=ùfMgè¨f±‹¶–•“bþªÜÊ
+}Láwec:‹-šÞæ1Hm˜–]%Ž¾|ÇßÎc[‡;ÁªèÖÇ9´ ÌŠÉ,m³J¯S]MZŠ(pf­mó‡ð$µ(CÝzŸVF\$-?TßjÆVƒU'ˆFOÒËLIü1ÈX} ˆjÓ’úç´ß@¿°Dçª ¨ ÒhÐ{8:ÿç„A˜(ò„ÜŸF[ÍÏö“ت¡iG#W&Dº§hæðhÛŤë°hÐYX„iÍ!‚ÙË‚öáiÈÖUîùô±!¡ÊýÐxuLÐbEý[Ⱥ
+ËjŒ9ì¢ÕÛ9s¼Ùd…¨zt_„CP}DU‚‚Û,ô‘ÜÜ@‘¸{;v7Ãä£Rë¦T ³ T¨ôå>7Aã€MîqB2¦ªåÆ{ÓØÝ3co†1>:[¯¼¼üë¥Zñ¬cJ¢³N7ȪӽÞÎ9÷®q^svß•×Å·äx³pËð·3õõ0zl©‚pêÌ?ðaôš –¡/XÂÙ=˜Žê"ºá»ÐÜàáYRÖúÈ£Vö8„rGïƘ^IoôŽsÎ;^sô©ÔÑíGnΑ92ìA>*åž–;–ÕWK5oôp
+B{Ë$&3D b$1ÄÙF¡
+éGßç éÞ‰cž£í‚Í”2¶ ‘{ãjÚo½¦óFdXœt‡¬« > íÒûìÇó|#œÆÿ|dÍ} #ž:•‡¨øˆ¶¡ch½ºÙ‘ä¬Y[yRÜ{Zí«ƒ
+yHˆ Á¬“PÊi6½E‰ @KÕ*)‡Ñ™m:iž{'ÄÖÙU#h žTúøî̵1-ùFD´‹&ï‹âÛ4éÊkªûÁA˜p¤ŒCœçpq²˜¼H{k]W‘¿Õ1!&–${œî\¬T…àJû"–cƒ ÐÝm,HS´‘ÁªÔ„¹†’ˆ%·º 4²-.kcÌX-Ù‚p ñ´”Û’¢€šæ<3ÞiAH¿1´ìó”Æ×ÚTÍŽ2X‡LXr!Œ5_A*TÚÚuÑB—"pad ëÁü¨·^ÿÇx™cç‘Ã@øs@ûÏ„º…Sùþé|E‚¿­nô“– 6 j™ÉÖÝ«õÚôE€†#²·+h-£Ç7ñk¢Ë_UðnëP¥™à“{¯sVì„Æ#a—
+2˜â|S]jê²Ôp÷eÂÝê°%‡"*ŒŒrß¹¯¢¤Éõ
+i§È‰6¥TòëáD†Ô—Ý*vífŽ€Yçˆbè&úزñ£ä'I­Q²¿ AóÐf•ñe]èi6H@¿°h¥ƒÍŠpêb½Cìs°Ð1^ç¿K`)2q¯¬ÏwЕ2“1=J Y¶nn‡:™¦dº‹§’–uN¨•ö—¦*«ÓDÈÙ¯@eCîæYíK N•ÇàÈÔ÷¸N‘vMö?äb%HEg}òýèÉ—±Kà(ûCr`°r”G%ù%%'>ÓlΊeîâ¬7OHof,I˜*™âXƒ£ƒiPêÍ®·s⊲­ÉÜ{Ô`¸ì±b\©V¼Ú)°
+TÄ
+Îò0‚"¬,x\16µJ(CY¢"OÏOíuÎ׸p¯Àª )´‚û–MÜX{ŒváÏ’Sô¹‹0“ñÝÜ!ïdRlgÝ{QÒH+œqFbŽ…ûåPZ@ÐíãB³4»  }¼zŒB¤ØMª‘vAH)w|€ÃöÀW—ñ0Î/%9`ˆTÒÁd(çÈhDx¹¾&רš¼ÝTˆx³‰Ç£ßò‚¢J …!áêIzþæùJƉ€dì®nÉW{ú¯Ll™°&éj;ÀÀlÂ\²åa½L%ÿ º1·Q”³°¾nA:ðòÙ*0¢ø¾ƒ]áˆkz¸Š¸°Ô…¿6ŸfÒò¥ûSð»ðPºÙT奆¯ìOe (ÔŠ§dJ¶#C€×+R 6À¯×ùt¶›E ÄÈ_…_¥¡”BqÝaï lœ
+_|ûÒµ7
+)l³²ìöæuðCqV|dwR—’ý©ëÄs®¸qnsÅŸûª+ŽµD„!q…YÝeà.øsŒ)ÛéWü=§×Ú%(Sn÷ ¶— \ÜÞ;ù€,‹›¶_½}¸E÷Ô{ÿÚ¥æó7Z™·@!¼ ?À†å­ '#dTÞ¯‹wL`Ë°r* ÿõNãÞ˽sÄÙŠ[ä„™­™äÈ_–ŒydDû$< 6Q s<ô‰;u]ZÖ¯Ô‡"^'S$BØ £-ŽïJŽl‘6óÔºÄúô9¬kÄã3œÏ ¤¤<V ä‡×)¬bG‰R¬éSŸ¸ÛÊßV{z˜ð·³ÓˆãÛ”æwä WDcKòÃïU”dôØ0‚ÜðC‘Ëd}ÐÞ©MÝ%ÐgÝêQëÑÑí¡Å<>¾qcn?ü
+ Ô 3ÃbÙm”ø
+ûšÖ!Ê+ëâLÍÆSŽkRH+»„öã/ îS‚ „^‘}‹½)Îb€®Þ)úx(‚.÷ ó±›MQ‹,î*™Ç7a°ˆF‹óI¸“ÑFˆ|B$Ú’>–%£$(3ðmœÞðÔ–§[¿Ö¦|fG(úvd´xHP_ô˜±@ø"MûSrÔ7mÆ‚À‡ÃÀ›;¾vc‡ˆ!ë¤áí™aþr–Õ=æ—Ž.:÷È«DŸÔEGΧ¸'65%«8䌛>ǘ£¨ö°ƒ²t8™]@QzTžJ’T‡,÷ë½äY³Ø—ÄöùPtâä
+³ˆl6h –¨ÜQ÷}
+W(bãÖwK0ä±çåWH>DÁFÛ ØC‰ÃÅØz!éðé2ÝÉ­0Œ‘ 0¥›ë¿žÂípÿ pYP# ö^d ÆUR }XÏ º.ËÅÈl»µ>TÞ tÄ!¾¥T”'+£PÖ1DšÁÈ «eí…ÔSˆô·»K’4FwISjÅ5û^õk$J³äÞ›:üávø§EÜKÏûAŽ[ÎëƒlÉÞ®Ï+ÄÄg–%Yios%qˆp“å‚Œõ!§˜Æf¹!RÎ<NÞa›ò€`Q›²F®ðÅ=ȇn"ÒŽ+F7äà ;ûK@§Äé)d÷ðˆ7©ØË’ÝpV%™´Ü,Xáö9Ø™zœ™…’›Á"Í>‘S(·¥³âd<æ> IQÐç'2ÅÁ9ˆœ§‡?ÚÛBÒfÒžl H4wáÔW‚ƒ€èQ´³·Xà-9¼nŸ¿Qå:ŸÆ¬rÁqua¿oYtùÄŒ½›øF¼ VÊé$ÀEU‚£ÈÖ)º[ŠøÁVOœÄ‹hlaiÚå©VtýÜŠš>ƒ@O¨‚Zé‘bq¬E\
+˜FX4²†$Øæ*oÀó *¸KZGjãˆ)Ù!IÈ4ž„ ŠP n7{îÁâD!â­ëœ £ûÖVh5Ò.”QRr’j5ôî”ìOáxˆ…žôøpNÃñ$Æ™þfÞ t_qpm
+­nÑ“¹{Œß£ísÛ©]T°°´Ÿö˧<nFŸJôàªþAEœˆ³N‰^>”Y•—Øê3à8ôqôMóIì:䨜K‰.‚êE¸Å.Lí~N÷—jDµP,þÒ^Îg«92´Ï)…dH„:ƒàà"ì–\0úï?^‘ƒv^Á¸ ¤Qïøq‘³÷Ïý¨èÂ
+þr]Š^v?£…StR•ûí׬Ž£ÊÀI¯Ëìƒ`¦¿•—•ˆoY¡jî’$Å€’Í_Ömf?
+0VÊ:c\;›’«Ü>UÞĽBŒè€¬ùxùÛ9¡D†-¹Šfk’1o{ûzw¸.Œ„=¼(¿
+3)•8¢ü{‘dèbŸ­;Ë÷¹j
+Nà°n/u m¥ûoý=Lue2­
+GØí.äOxxPûâ„P
+c%Ô3Yr•)°Pùz˜{Ì3àÖ0,ŸòN©-Ùƺä%¸õq…‚hfÀðpqæ)è–±8J}øµd•8¶VðÞ³]¿“AM/bíÐáÅüŽnÀ@—¸ •À¼Äm ×`6é§CÉÅËË?ºCZ_ñßû(Œu#Åô0Wísꯦ·*à
+ët®2xo¿èì?ÿ¹]†ž`ˆ™:>Žeß.zÚ¿_ßYRßåBš
+üãK½$§T–ÖŽÂĆ#¬s¸ûdÜF—¸K ÿ$£bbføæC%\½ªc ˆ»„à†ïf~W/@¶¢éypÏA ‡Õ8‰jøTº>òñr´¦i‘mùgxqnZÐÞâ¤Ü ¹
+DV0Z`FiÏh’B¹MÎ"ÒvÌ?òÊp“Ÿ3!H¶´Ê7<Z»ŒÎj1æµBNƒ‹³´rQb-$5sbú†ÝŠ~]‹ 6yMž"÷Úš.Ëðå«‚Y,‘¬ép[Gœ¨òD²”(÷âçp¿âÚú„c]†öW°íKaµ?ºð%]æøš®Š3Ó"1ÊZÞ|Ç©écßwìÒ"Iõ5½ÅbÂŤá³Ä˜ø3
+QHš£ÅQØSãõƒphf1!ŸñÑnµžýMîZ^oJ”uE\êa«ç"¦9qüØK<uÛßA3‡ÒÅ1'(„Á²¤µ/#{Ê hCkC±OBgrÖס+t˜Q²Þ©îïàU1žƒ¼©Ó|št¸èC>M5vã_!k°ø6dÏÓ.\úË™D|ŠØŸa•äªE=ÁÀRaü`¥576†•H4Ž¯5<JRǽuüè0”‡:a%36¥.{©x[Àz—Pþ™ÐD{<|]K¦€MDYçQ¯E ›÷ XôÔueº‚±.&ϦߣÁÇY¼{U€„Wí9ÂÀÓÉœ>´Rÿ&K£ê6Iü »ºäÇ ®›œ>‡-•“k¢4ßÃŽ@AêDûØšÜÞ|Ç—»FWøLúí–Œ<êŠ
+Ë>ôk…ÒJŽc 6A",øÇ%+ÛD=ýÚEY†@ý[á ¡.ÃMÛöÞÉ~Ó–&$ÇÀQ l +žû^<B(&ÂR>'!Ý$XøîÑcâÆ’X—è±ìIÞ”Ýc(K€å:³cÂYÒ˜ùfœ_J6rÄÖÌ@©j\¿#=„¨”ÝPì¸ ÿ̳»ömèXÂ2{2UûUŠnŒä8ŠE€Ã'çéì"KÅæ
+
+­ál,nÛ»ÞtCöœi·z#{Z÷
+6FˆBèÕ1ˆ?}PFÕ1¼Ã× G Mצ°½¾™Õÿ¹À‘øbC+Àua± Ú±ÁCãЯT§‘±J½;Ó‹>µwÕ#H!µ˜ìÏ*a¬†Ö›@¤ÞÎà€ûør†³;¡•$Å[½?•`2v¥`Ц7%ÀEA(鸣ãµ­E1öBñÁߊ^ mà;¸u*ôü¾‘™ˆ4û>-Ó² ©31&“'8À¸Ÿ§?CÀx:£=x轂äŽ}ÝùËà_m½›t‚_“ÎÛí# ‡AËyó6ƒüfɃÒHÎ÷â6 #<Ž#Çä£7 Ò·½éø:rÔ€¯03Ç1þÛ«dⶰòö'OâÖ
+¢€}û¨©*Ó6gX«.Ÿ‚ôÍìU.R ô‚ÿ¸vø»E s¸ÄñãÀâµ+Ãëù\ßh‡G1¬ôYØ*Ù””0Wˆ»—,4
+IÐK[ÞH¾/ZеA¿ù_J:ôTD"ó¾/%:©CÊ„­QË›ïà· ø¦Û| I”@7~©=짢Õ`ÉÜ]ŠÚç „´Tۥ̵‚V˜ßÑ
+”Ø:Zü°š¬AúªYbm:Ž«(cÜžþT²›üÜ—ï¼ÌË0>¶|'ÂÕÀ §c6Ï8ŸQÁËù".¿ƒù´ø ±6Ö›XYkJE~Ç”‹§È‚e³«þ~SÄ’²ÎÌ.úP ¦!B‰G–`?ìI¨èò`‹©BþÓÉšü€5ÚWWô
+¬¦MT®ÚçÚyBœPr‚º“²É³!Mš½‹qð<ì-ÐMüÙº¦À„!ÕA¡Y ؘLšN$Fé‹‹!ˆb…AF±°µðwt<®¥2ɬ‡²}j½ IféÓà¡a7—B
+ÕW@¤>Ô½W
+öp— ºŒa½­Ø⾉'[âU±]¿Õ†Í‰Á% f~±¹ö ÿm”Nò0L ph‹ýðîbldI|Â.Sußd/¾H'ŸÉ€×›îµÓ)µ£86Eå2*ÂÛ¼6Ýë {·ÏÜ9ãÜåÎ=÷MwS¤Š¡%›¡Vþ$p2Q±Ûä¿}ú³Ç®v¤*ñGº×Ü$•Ð2Ð_t*†sÜ ôùΖbïQÇ<fÚõ\çË‚ˆ[¸1ô+™{ä¼ï<îM+V%€Š;Ó.]P¨ 4 Äf& Ú»`M¯r‰­¬ŒTŽÊyB2¬WÒf@Æ曑k´ÌŠ'ßØ.µÔ˜š/çoŒ»æR
+‚uÊ;I¤a‡`N´I
+ XuY0nR)'BU|Y»Ÿéöðb ,)¡· ƒÆ-M™ˆ–M"3!¼ïé°L
+y:ƳùÎ
+k©œ€U^(‰0¼ÉÃßÕÚÊòxõz.
+¡Æ§Ûöqž®ÕòùUþ°X¨|Á“»šúTãrO æç`œw9RÂù&ÇQÏÚÎ|Vøð÷³SNN8;î|çÎç6wþ¹¯ºóøÈ Þ4Ü4î·¡ú³G¯WyIj|ï]w³¥xrÊ4ÌcäèÏyœ“~½“Tƒš¿2ÔáK [Ê0;4uÕzø514h
+å'ió&¤Ÿµ "{ÿQt˦NY$¥úí¨ePQ½ØÅø@JÑzØÛf<…¥È²4g‹åÒÚëÂ?6Ñ«Ñ— H–ñ7¤ÙŒŽþjËtA‚pŽKå¨ã¾ŒrëHc¶‚º²»B„’‹]Ïæ9ˆ‘ò>*F ñH¬ {£ãCQz[G±O1®L7F´ÙC¿©œ¢ru!u6TÃHÔ‹¨'ˆ"³X•¤ýó6·$c¾¦oÈñ¯'beÒ0SÔ¶ÙNf£4ü7]#14áSjW4$p™}Ù4•þâz©Û 2â†TQœ¶ ’,‰RÅÈ“4ý
+ WÉ \$@€ärÿuÞÁØ–èJ´“Ô4zøé#Äi}è–K×»”¶9ÈÆ“:9jfl’r¬‹Âž4äõxØMúÛ³£?y`éÂ…¸í¶/¯ž"ã~<–jÎ<ŸCËZ¦°ôûø
+–á‚Þ­ÀÁêE?¢}ÄpþwЛ×eM5’)?½ädª‘(Z
+{­m¨$.!f}ßøQFbFŠ&ÿ²f¯{84m—Ô¶9$o3m+`þáÍ*™³ãŽˆ¨µgÖ¬ýzš0ïÆXÏØ÷yÑ?¦ðÓKU0ÊbäëR®Öâë…:ZãŒÆÀ>•f-¾ï‘ú·@p›E¤¯¶3ätáG%£w`°/G;gV„.e×#/ªL‡à~_ÚßÔµÐùPS ÔvÆMšë¼
+÷ñÔg©m„ô4Ëêr<„äªòQb‚;µüp h3›yg½y‰®ÍÇø'd¥¹ô|]u~b"ï ã‚Ú%Þbz~Øǧm—èy¿
+åÁU mÎýו¯g[úG€
+H‰Œ—KŽ\;DWà=Ô:¡)’c¿áÛ…ÙûŸö¡¤ëvÝ«„ †•‘EƒÁúñQ{I)ÚÌ=øûcx{VZÕj¥âÇ·ÄD«-|Øñþª­«4÷ª ÒÂ"zw)Q'¤iÃO„Ob´QÃ{Ì‹ú«›é(œ+Z4^î2Z-µÏcDzç¿Õ{i ¡V,Z+*ÚÞ@j·Z›uiÑöMw¾¬)ñT·þñï·ÿ~+ÿ
+B:¸ÑS`™Êùçé8ØÐ&Ø¥*õ¤tG.’º3áŸCÀü•\îHˆwý:è9—,„ƒ¢ç’·˜drw§q91ëQœ )òÕƒJšüá!²“( ™ïÏæ÷4 ö; OsûP‹çøÏ’v>`4õZN IiG«0¾K¼HÒËÀáÝ<.º;š¤©ÀPM±YHøD!޲ι“ýn¯Ž»íû{gýî0Sô tÇV)|›+cˆÎ&Ä°-]Ë Íë”ÒGZòzwL•Â¸&¼ M«aˆ ºlÄÒ¢q2CoP–yU°ÑV·¶b·yý Ùmƒì"\…ÇÞŠ€…KáKdAH4'Wù‚¨Dåt_Êæ _z -¥J ߀d0xª7-R‚ŠyΈ2¶]@ ½Ð2“Bòâ
+sˆY^§&¯,»MºÂ×YÚ ÆwÄ"äM­þRóï¿5ºƒ§­ñFõЋ•|ñ@¨=ô,ZÍ ÓØõê!¦.~ŽùÛ'Dg^/~¹%–þ¦/óˆDS'rô­@c>”uZ¯]ÛUaŽƒNzIµdp‹~ÿvÑD¼²Sx­ÎÒŸ@22Ë™ðú›ÑèZÖYtÅ#lvœìˆÙɈ¯§ßI}7âYÒ¥5›ê4Wž…N'íié@_ü¹ŸBl(aKÍßó‹3éRòȬ™tÓü„œ÷k‹Gt”æÚ7¹ D‹v]N ƒ‰’"§3w¸ø§¦èqu
+Sè…uUj/»Eäj° X5ܺ†^ݽ~=\É)Á_3)4àäÅŠ'( :Ž#w—YqgU$[ù²IÌ
+Cyk;,‚pFiÆ^OЈtõÙ©™±`cj?Êß_e4wXŃ…‘ÓC*Õ¶ Š”’a"ZÞY±'¨ç>ÄS›à£—Ÿ×Ý@?¿Sz Tè•ûH
+ˆ–c39»uƒIVhä ñmvaA
+ ÉñMhv°LçÖ¯›î¹„ùº°º¥9dùJGŠFð~¬Ô ²*u¯øáœ;oÑÜèwxÔ“Å$Dz‡ „‹}[PðêH­·7ˆ?Ëôýj:f†u*ÂöÆ9Œi>Y÷ˆÝMÏt˜÷Ï랦çï1-½dÅ(£¶ ñ¯ aYuÚß8™æÁÂÈÙa7z~YxíC‰G· i!— þmTs_e¡à+mL_ 0¦,^6ùoIæX¶@O¬;öŸËçfИ¸¨‹À•äOÓß‹‹rgNGV„9Æ ¦¨É'‘èç KKŸñ¸Žžg– €yYTÉP—húæáCçÒV˶y†¯ä®¶=þxyä2Zᬈõ¥Ç0y“yNúZ>úÉŸjù ðcÖ’¤³¿±–xŒã),IÄ莻íõ†}ƒÊRabB¨~ÏFØ;e $)…[W1ÜؾV²­WÅ ãòä:'ý(òTËVrñ”·ÌÖò?ÆË·Ñ$‰Â'Ðh·!侘ƒjS1Vk̹ÿ|/#~¢I†P,P%*˜Ë[2m›&cÀN;_ƒ¨úJç˜Á¸ŸvFA
+ÑÞHCÜ¢”ñN5)éLÏWøô&ó‡DÑ!ús ±•
+>·á…ë~C˜ùö'×åϬM„UZ<âÖ¤â7L¢R]aßPÆÎèm G¸|BžÔTRóÆ#l,ôñ×¹
+F“D‘—uBA&"ØÊ9¦bÝ
+û‡î¼ÏCˆ³Ñ,D€   t³…Ð^‰ÅÆg„ÆBf ¯‚ð6`ÈŠœéÈ”˜_ A†cöÄ$ñVc+"P·}òëÙýéü=ÏßšÆ ™IM†VÆüÄÓ‹$­mzþ¸]€t¿AÁÅWsYš?%pHt¢=~£åšŽäÓÁä5à
+1±¹ òm/‰Ü5bÊWË´I&8Y£ød3Ü'j‚U§¨ÏG¶W$&S5ª1Vß|«eÁâAü<Km³røM!(I„/ú«d—^ ÉD¹ÏÙ~ˆÄcÀ
+z$¦ŽbÇPéfj‚W‚_HÇ
+4F—PðïƒÂ$=uºž-»“6êÀ:¸Èþ—ÑBàû~Þy¸/zW¤ÙyútSÒD¿óIÔ†A²S!èøSʹ9£nó‘´‹$‚“Ùc×vsÙÙž¥È-A­E\Ì­öyš |@±n=NåP÷ÍtQ è‡1L[)òÞÔÚBì*œ U"l@x-LHý‚è=æOQäg!ìµØâ”qIF€'8¸þô4ßÑeLá@ \À„}‹R~^‰à鯫¥r)!›J¸ZjÄ(ŸqÜÁÞœ†¦2™fŒãð«^ŒA•V9W-1%ý£ÃÆfäju ç94‡þì5ª ¼A9oÑ>Ý¿Þ™å¯K`ÊËIFS»O¸ûß‚è I5HvX #O#Ùç«ÚåÈ">…™NÈ€i!2>›^HÔ¦?@ͯÔšÞôê2/¥R±rÕΡ¤²m‹š×híù€ÕGñ L@× ÄzÏ)ê*wçž#^Xuæ…fó:ÑO¯ŠÇ~ÿÌ7C†<)b7‚ !É.Ióv||´/´¼Aã½ïNvþ1/ÇÓ:'(_Ū?}µ»›|T>*#Ž5B_—=huï Œ‹[f¨|ÔTFAÊ€Ùp<Ÿ²´˜)1ƒN_ÏÂGVEÒ²f3Ö=šSÉHœò:J *I ØzNÙ|]€„\™—ìá*Ò€él Ÿ÷Ù3³“×$ÿf³´„îªIXo‡:,Q›²ÑNƒTÏGgñïmr
+œ¢o2/Œî•Í:°SÖµBùŽt›w¼¡ána‡4¹™déLj”˜`•¦|R§!|¹ºÒeÌ£ e Ñt¶,?´¼åÜÂg˜ŸÙ«_%0)Û“‘èBs&êƒÔ«½Ý«aZ(¢‡íì% A@ð+X‚V÷yÝôô`VOÆ8\ŠW]úÐÛ…Ic”, j“‘”¯&ßmjKÁ“¤ñò6‘\™‡èÓå7€ÊñwkxN±hÚ `uùJ‘©
+ãÛ×®üèÀÊT¾Ø’±£OÂŽáÇ,V‡™‚®[D¨/"³‚R=·j V™
+˜‘õ]È€ÐëÖÁGH¼Òü*ôÔ$£>õÉáÂ"å8Ü¥3u
+0ÇÍ€åÿŒ—I\GDOà;ô Œœ‡µ}½’ï¿õ ’YmýʵR+'c Dæf,kd%©<¡$Ç¥À/GC
+v [u^ñ-ð%²“7:¾UÇÍ €0ú/#ó‹ÑûÃ8ò:¥Ð©
+nñd‹}”°
+ÐÎØl…3¯(¸ ¦I–Ô|52«€±îð"èúÀCaŽl¼ñÒ/È›ÅG‹Ç$‚àÓz/%À Ê‘Éqž­¾Š¸0¯Œ%SÀ´Ë.*Áõ
+V ÞIs‚$ÔEx6LI/W#ÝÚ|L­@"}
+Ö€‚Kå—!]Ì2=ûÜkbÌžñ„™0˜ ÀèBLN½ýx)Ò,‚\¤c¼é°¬;ÌÇæÖJ08ôÞÜØD9ˆ÷›üw\¬16#˜‹·¬É£Ç@€·àAŒ—ÔÜp' %;ÑîÖÊtŽ Z¢£lÄû!lÁ‘dV[ã# aZiûÈ$XuCûä¼4
+÷± ¸ I#»aоÏ*|š6º;”çóåx„ ˆÑÕ4äQ}lRSvQù¡%Ðiéäx¼±˜¡_JB…¹ŽfuzYfî,)9’ŽFÒ—švX YŠNsTˆcêÑH>;.FÆ e O­B,Ê /TXƒÎ2kça,ZÙž’ŸYà©BãÌJ¸(Ý´q ƒ6ñ”»7^<éô"ÞOx_zœ}3Ì’Ò,sL;T+³Æ·Fàìx›É
+ML‡± nI9ÅÆà±s9¶c “°dXIuä 3án_Ûx¬¨y6œ"NÂØ Ù0!2Y¶qW#õKC&ï6²LO—Ûd|OÌ”(ɶr°f¾í«H~
+?@‰™ô¦ÿ­ˆ×Ž&`ëѼŻ‡;¦Ðv¼.7‹­$ˆrë ë ’Ä”(ÊÆŠ“½dÒt•‹­Àn“Vaûþê0öG¨k L´NìÆ“Ìóª!ÑÒMÌGr§t)¨´…³1û+Õ>kž“‰cHøžÈ"ýÿ¬Ñ ož‰;1/šÝkI®à6›´Þ***‹Æ˜I»W<±w«A¨3¾$$¸V䧰H¾Op‚Œ°×PÍ}Œ*XG:¸/Á*ȼ(,C›Ž—elÑ‹m%L^’|³ô¾Ó¥ó?”$K/ë@\cºÅù^J .¾!®LGªÊ8t÷Lö(úñVÄâô&|8Ä/%X<L1VÙrЭä+\‹¦l3^Ÿ7xYç‘®% ‹Ç,À7ålf=QKãÒt/A‰
+pl#q ãJ¾£¼• ¦©ós«¯"î#‰®´+¿¬ƒÍçñxà•Þ¶RÐ#W’FÒi(Š3Ôÿ'ÿ.ò†¢ÖŒQûÙìßqÉJz!õ±gý}Ê¥õ(Ù:!Õ%0Ö!¥ïUÜë·Ä‡ø0è/ÃßÉ›­œÉâÞ²žœÈKàIÅ‘V§Y)Á‚ï’Ä»taÑÉ2gLƆ’:%^®@ÄL( œ°9çâëL£øªÅcÈãìË@Y…U™v…„(ÂrOx+‡^5Ôf¬@×Ä]I…ÉO-Äîim%Ú
+«݈Tiè¾®“‰FK÷žÏ­0Æ •ÇÓ+‹D¹—ÌoÚ¡ˆ$Yd2pR+ú «…1l.Òö?-‡g±ÁYYnYÉÈÜîf_ï(¥¨
+ÇD
+Œ–ôtnÄ÷×áGâþa¼\róÈa |ßaNèýX{;·•sÿí|EQüw«1A€Æ.K-²XUœ¢²¢ª×Ÿ¯ÂèœÐü¢
+’Íg&QÛDL…ÚÙÎËU²ÄD-çf¼¤$³q!ô÷‹‘¢ªŠ+h¿ ;‚’ñî  ehHÒÃ:ç®S©›5—•b<@ºv2kÜ=ú•‘ $xÃçÍȲ2fvíš²<"‰’ÄïÅcÒéHå­ …ÇCv¢½ùŒ rßp²ø‡Ðñ¡<”2¹x¬[éãÂ0»ÄwÛÌëÀ£b(qÔ>+kòÎPÙþÈÜÃLÅA®¨‰Ö
+œJ‡¸7Îè]ÅàiÀÇLB+»]—aÀ°èüa¼X)
+IŒI4•Ã!ŸÒ©ž¥ùô«š‚ ®Çrâ3QIP< Óhë‰*ò†˜íù-GÈ2Õ> I¾lö«î Ô\
+I ª~­¦ XñZPIX
+À„´^EO
+Y R¹¨w ­ÊêÇí„V­¬n 6ÅxzÉ+|.µ6¡üßÝT–¢†cSU#Z9ñÚ’ûTFBדU ´‘ØÙNÂþb”ŒÿKìFq©›„¡¥Û3“¶
+HÖ™%¸1 kÝI‘Ô†À$ö¸U™åî9k;,=¦ÿ3‹§Yþ³r€¶DÇ–Ö“Š¡nõÑÏYNÓQGLš#yr–89ßi_)¢Ï½¥½¥h™šÊM6ɽ8…Z>£ô’%¤¯‹®ßB¨„RÚôû-Ù‘Õ›\n×X› nöøiQ5ˆ¹¬JX¤ÆA#H֌ī|°F1n6·X¡v’r‚à¯]ÈX”p7œŽÐâ¿=ûï¹_^Åäq(F·‹ƒ˜0P8·ø%‰Ã­÷¦ÓmW„÷’ ï6…åd•ñ¹´N›§ÒåaZ¾N¢ÏÁ -K£‘ÎÎt*qøË3~·ù¢¥ê¨ÝÔzã‹Å×v¢’dlZC‰ª—}ÏŠÊdè¥JQ~2`?J½ ø±Ô˜·¬"se½{zÿb0d}¦?íäTíqÄóõ=ýN;˜¬6rq!%ùiyõsø8(ÜìöcG€Ô%™)Ú̹ð¢ÛºÅˆ ]}æW*ý+Ìð‚4(!Ϙ¥{šìºÊË.s"ºC¹h-cG`+ûU¨IP©F¯aWMYFÛ©a©0¹ók»
+ð@ÒIVöŽJ8ôØ9ÕÊ27/ýÎö3äM÷?? %”}F˜Ø¯'ÐPÚÊZ8~"pÕôrRééRDššó”ýét€ZdJäaˆI¤@Jž–Þ€,¤‘=ÄúaQZÌ`EˆÛ Ä sgŽkÛëNs,²Ù9ÑÃr0óRO›
+æ•$’:ãåBe— T€D®ŒµAfA"ÿ´@€Ù’qDwdù’Õ\p'ƒL=yH@â8@ÖU“ÒÀ/Nnóx΄¤Á~¯wlH1?…^Ñ 4Ÿ¹±åC"sî ålr.Ê…šq¸ »H
+¿
+Ž ¥ —
+Dy= Åf{¡lk“#ÒËPµ`
+z¯q`2ÈhLMÍé°X-!Uø#vQÖ½_L¾>ËNCX¹ŒôÇV£¾Xê¿oMÝ:•ùr¡s”@¦ª'õ]ù˜OÉ
+çÝÉuyô-ê¢ó‡íÈ5êôº")\õEÓâ'/Ë"üB`¾5b„¡ûàÍ€¬3zEjrL¤ör=
+nâLb¤Ö±î}€Ïˆ”&´¬â\Âæ¹Èõƒ~i•’ã”Á••W¶Ù2âa¨Ì9)*‰ã•ø0 </Í’»onéNÉ”Ø^}õ"ñmÚç òùñ‹»2Y|eß,U’ë1‹¿O [ã‘”òô\¬çwDµ×nÕì³Ôªz6 ‘¸„¦¨Ý-Åd K’’Ú=Kþ´{'}sðð{Hn1”É«k¶Cj béÀÏkJÆJaå©a‡þ7Ⱥ
+"bAÈÊÃ9Q3>Á,
+?_0Ö9ŽÚv¶ÓF‘ŸEZ Òh:Ç1s§¢ª6Ãq|Ô¯j:$å6ùðƒÓ„˜ŒÌB’mm$þ&P¡õîŠþ#<¾eåâÅ¢,3u[BZhþ4Øê¶kˆ`4HQº=ùƲ®*‘0VµFíÐx;G¥ T[€C¤]t’› ø<‚q' Ç°°Ž¤}#J’fþZÜaæ»2Ä”tt²øVf¬’€ÛYMW‰í§1¨
+«ÐêfJ$´Ý6(Ð@Wx±KuÊa}Ã.É3$Í«ÃÒ… T-«ÆÄë" ˜kÙ1€.RR¡}b) ÂkL‘3<B_¼
+HáØda²aÂgÀ ´¯õö€¸dÏè>Æu²nÈغØ1ãß‚l @Ý‹v¨f3|Ä$MÁ;5uë‘œWâ<kj€Ü8x)k‰rcš!‘mVÇÄÀÅN ¼¤¡Û%óìÜüœ „!™ïáh0èÊ\šð)\p›ëYÕf³¯ºƒhÁ…öpNïr#E¢YŸ ïÉS¼Ààe¨a“ÁרNÄA¨úÏ„DŽvæÈ›_ÿ|¸JeÉìúÉòfêï¹É¯B~ )xk=4ÝJmQ{Œ«^N¿%¿-= ¾ç¿çŠyÛΈNj^äâ‡{˜K¶8@l»•¶[•‘ùáä­Ä#v^êŽÿ1^&Ir%7=Aß'(‹yXSKÝBfZ5ï¿Õs
+A8«•úIŒßîW±,†®-^õf9Ú!XíS´$还-oIìòL;ë´:îº0è©?_|À‡?eAŸ–ßDó
+óVôŽb·Ao
+ü õÿþ•ü‹Ò̲˟0TtË/•Å‚¸1·µþøõPÄËè=ƒÖ~X ûЫ2aD=™IÖlŽéµt͵´‘/øß„‰ÉJû¨,Óâ?d¤ú°sø¼ck±¡ˆtÙ¹æ™p,à
+?‡'‹„jË—ÅQIR
+Jè¨Ô´ç!Šw¾ÁaaFÐN òãNZ(ÿÝ4™üPò ~?áÉd<Yø(ü/_0Ö ¨¿î5|
+¾š|—-¦dÈ÷×£HfÈ|–—l¡õ¯©S-’Âxø,_J ŒKX®1/(_"…Xm;çÌÃäv ƒ¸8•Á”Uè<“R˜—XåYŒ› Žºvm²/^ÄÌ@§~ •ìsÕή6Š,&·¢ÑpkÉ,ðÑ£Üëð?Lð02ñ`GF?šïâRòèŽv>˜ÏÀ,8J)ÙrC„•Þæu÷ÞJ|Ëg'"ò‚кžÃ†Ð$Žâ3[¿~õᨑŽ<Šñ.›g1Í÷Þ,È ™
+IP )ëôoa0‹}®°`Y ‡„ñ
+—Æ67\šJ$9…œsQ¸ÀOy¼ÕHdJ¿7+|VˆD
+ÿ(R’3Š?&E?1àËú2& 5FZR¶ÌÆâ`ePó¾Ùãe© 2QT
+s.´‹ÙÅXü ÷à•ŠˆŒhÒ4ÇOQÀ!$Ç  €¤õe¼2ÿ\Gžn‰‘o.ê„'ñÄÎZPù­DWHð12f|<œÒ”)¹ ÷1¯oš_²·£bc]WmÜ€ƒR–¸ˆÜ©
+
+½Oq-¯¡c»”Ç1³)¯èÒ ì~–O%4Š ë0=;ÿó¯KÑg~Åò;:ê” €Tdí!$0_n]?þ~(¢5¤LÀjZEEZPíægõ¬0ÿœeˆ—ì&%S¬¾øß~¶Š'ËŽ
+†s)âª3r½,ƒ_Õ-¡t#Žùô­ë+ÖßÎbø„è7ç&yEt`L¾™D¿ŠŠåºv=çSìU–% ¼´«iàƒYž¯êä0 ˆ&&ª×þBuÌ ÷n†â(ØÞÇm&Ñ38®ýÇÍ]øOnéß橪yR¤|íq¨ôLÝŠnŠUÀFIÈ{Ò¾¢÷ëT„#§K0ÿǽõà~ÝiårK·e¾•TÈ ñ…=’ÆF ‚nÖ!’Ñä‹ Ûå%(rŒ*Õ
+j]m¾‚-Og¾:B¿o¦èpc8÷
+ã<Ä9ëø¼•’=W>¢C½bñZQ≽è2‡ç?Çl1Iõ‡pÖçXE+èÃÖ[ì¼0pÆ Ï›·Øv̆eÇsƒ«y
+Ø… û²Yû…=øEI³(!,v³ìËPOÑÍäÖº¨¤sí£„ÛÅ"ÅnSG΄úC~÷5u
+²_÷¹Õs ýG§«È ôÏuP·Ó#Êûš%'/TÏ*¡ãÞ@£ø¾­­2‡ÂåÄŽ÷l5ŠqÐÆ}6‡_‚5-&@ý±°zؤ\Ýù61ÿ2x¿Ìèö>£¹­I@%¤uΨ©rÈ<»´~-ê×dý9=Û$Y¾XXH({¢ÏâIbo/Ù#¾ˆè)}FAêyÚż\ô©ÙMñcZpÄ_)©FH@ô
+¯i
+íÃÑLN¼h§ŽÕ:Ús¾TLÕZ¾Iåóü°ÌÃåõ`¹¯×ÜL+»úˆ5ƒv¼ˆÎѼý~9î¤bâ$—Ô5Ûg†sÕ"«ƒT@–5}˜Øum¬ÍHßÇØJ‰¹
+Ï]Ö{;íŒ,¯­p å1uyÁêEŽD¥çœVȳÜÉjPTIü[\ñ ï™;·sñA¾HË9qF0Ö:@Q3B¾’¯ožò½dg”•äϵշ¢Í¥Pë³Wy]‡'ÇÄé ú*9Œ<ÞIê[m1b†YÄ;ŸI•%Òí™ö?,oø.|ù¼±iJ.ãeó¸Ôè{Qa5MÙÁï´Öù”QßK>iã¯nõ©è÷›þŽ‚A†Éí æl §’±B÷›µÍKª©oÞKðã-Éà9y4¹Ç¸ãguhß™ùf‰ÎðTeLžW;÷¸–v<ß+¦ü¢_W!æßY}`½PŠVzI­•%ðÉ°¬_ƒÜàëb ð’ÈÕ¶þ?võÈ€çW=‘åôaŽÍ¯^¥a ‘a$ê²÷ÜÊÔ5/ ô™ëto,o¶ÿ1^&ÉuÜ@=î 80ky©[(Â+êþ[¿¬*æ':ä…öW
+@×EsÞçø²Ï1LÉ–3G*ýÃÕN²@O†…_$êühq•(™gÎ)ظáQÁ\ðp›Õ¨DÈ„¤ºÞÆäÕ×Þ0y[²¸õûí1râi3HH8n¥ ˆ‹…Éš¾OýT&{Ë {¾À­ÑS öô>`æ1Txˆ2•ŒGïW×yfš4‚Üò`ª+³¼Où0¬Y€JÇ1t‘øÐÎDÙáå«ÌÙ²b<‹FÂÃobµäÔºÞ눘ïf¶Ù¾ã¦"'i«;„ÉtAÂN
+–ƒÞÉ™i$©Ü»‡. eP-3ÎeÖ $äÚµ of¶nç(·Âô¥œ×ÀäÌQqúk¨€ ¾JÓ‚¬!™UÕ¸ÇP>ö‹Z9,]µä¬<)‚ÀÅ墛õÆG·êM‰ºLÉúhTÉ„²Í5X–gÐ;ItI£«’0®ê¬^SÉAŒ¨gŸ`X‚‚ˆ$Y(ÐÐ`a‹ÂD@˜™èqËÌ[qD”ý&cNL,•a`[=„ÿbç_@o 2“ÜÁ‚Éæ5
+äÅp1«ø(–ˆ)'E–ä_K†•2Ç|±†$ fžŒ¡tòŸpí1óòIF(H$[#×N(ÿÅ~àøÑ!½ˆéƒÌ€ß„ë/uˆHʼnx'Ü:Õ
+&ÍS`+b+6‘`‘•[ùí å!XÐ äÇ·¸«±ùtO#€ÒxÓ›ÅXæ÷÷Û0zyI«Q&mYó¯UÂ#’}k5f‡¹Æö°ZÝÔµ²X'‘ò°#ÈÀ”1*å¶O¯q¤ÙÈVÏk"êJcLì+þ
+ÿÊîñqÒ’„iè/åbCy e¿@ü¦Â¿ð!Eãá¼=-†}˾~SÁQSñÎNý²2º©°öG,è ÛR¦"¡-;‡€q@å0ÛƤ°œÅ
+Âuo J#㺙 =³çÍNþêk B_²¼c>K5,¸É£&·¾€ÒdªiÙ.ýºU°&¶ic0EAÚÜ•Q¹ ]8Ç §.ž§Ü%;Œ9®)É×÷µ‚¸†x§»'"gC«‰WÁ‚ÓØ$1fãh¹Fè0¤§q61¡<üÉ8ö-S©,ævNÙ”u„Üš³Ïd66ËÔ
+ˆX ÓZ{;ÎöU…¡qü&aR[ü Â`†f}ÓÚÏ]Ì7Qzöy'yÀHÐë\7sa…
+1»ÚÁ P$ ‘è±P+ĉ6×y+Aˆ5x¯­°rÍ @8qÛ/¥\¼Y\ÅD°çaƒO.Ï 0Æff1Íój € _rú¢«€hÑÉoYn6®š
+·L{_ñžjƒ¦Ö õs(6a^bñÖÔF³}ÓS¶-Fy#S+±
+ŠTϦgĆ7â/ï'åäÞI}z=ÏÁõgÑtÏ+ñYØ°•5í)%ËÄNf}Ì÷"¿ºd±)ÕàÊÇF %®lžäÊÊÓ—4Ë€y;B/ã£_'HÜS×8ž•…bù£Æ¹ü¬÷t…ZAê Ò3/ûàªÓv¯œ
+mïdRƒtV‰^°ñz>6þLl~…lc\|þFg§¸©oj¬}c³Ç1EŸ@oW— 9ˆ·Ù—¡:D†¦1>CÏÆ\j(£ïfð¿öâ—ÝTdá¬íå‹¥bíd4ièIsô“DE7²CÒVŠ…7(ÇÝ‘ $Óô t@ü„ó£…vŽ¾’‡¨›µĸŸÔ,aAõd8¤R¤Àl??&g.ÎO—ÅA„qÏ”8 ¿J©&#\™6曧à“îK*ÛHÈ’êTy_Rª_‘õ­»€lu’»ÂžØ²ºÈ|á$UZ›g4 ä;l,†¦ÂÖ£Æ<c9¶‡øç© Æ”¶C‚uÛ1²ï¬Ju ’þ·0bp•r€ ¼þÄÔ7s„@8
+Ÿàå“hOdKÛGì™ZŽ¬V‚ø‚J²lg4;̯“µ+q›ÌÚÑÎz:þ™%Sg¬ô@G1[²Š+ëÉÕ 8>ô­ÑÍz ŽT”,Úny@ÜKžÂ%÷ás΢BOLIÕ¶
+ D?08!hðfˆš9ÞÂ
+"§ÅÒG\ÅfÀð¨Wµ¹hÈ ë¤êçàäÂp3j³spƒJkl'»´Ãmö $4˜•Ã’2 }=œƒhjÍyr;¯ÁÒà„èËg¡úU…•¯é˜ÞÆõ;cƒÌ<j·D«X¡4ÀB üÃùQaÇÿB¶y¡Wò9†±…mi™¢ßZ‡&ûŒ¾9ˆ=•’2
+œ‡µ·º…¯Ô÷ßúý`°`e2òª%‘ÉþàœÉ^xm¼tr„¬f/$wœJ©5²B¤+ ~ÌŠ7mÇ+R€ÞFr¬ÃÎ!Ép*ã[:Dô†oÇl¬«ð°ê:¡,·ˆøEð@/·]’¤Fn³m ó3 f8SÓð´iì4x •€X–¹ß®þNÀż9°ý爛_*@”3?ȺJ”-Sæüpt¹Jæ÷RJÝej¦AÔ¼ï
+³×¼xq(!Ù¨ãÂW?›¬8.˜V¼Y<;ƒà‹‚ÙDÚåZ|±`6Uƒ0”v‘ù6y¯$Kë ”¬b²K ÌÅ`ßÆÞòËD
+³÷Þ%ÏÌU˜Ë6lEˆ˜-ÛÅŠòræªîp»(¬²‘?‰”…Ã**Äü}29È2 ¹Sªg ijŠÏ˜
+©û“¡ô~*!
+‚!ü@08§4)›ÜÄû3]8c3A(^ƒÒPÁ½ ÙŽ”¬/·Iªâ¸®2F6¹$IhÌ#ZÄx„œaÞ'Ñi¦
+Œ˜"yiAaŒ1¦éAaâ4‘r>k“œß†2Ǻ
+B¤kx€ùfQHŸ©éááLltBœ"³¶‰g2
+ͯ"¨dˆí˜0€ÀÏMvwnªmÚî)u7©:A~øÍ¿~@'ƒGÓCÒX’KRùt
+rwÐ-È1„P&~-a÷Û1ÉÁ4Lç!ÉR%(=ÕÎÇ$„ƒ>`é’Žu‚[ãOô”ä
+Î$(+Bu%×Ã.ÿDà Î"-öÓÂA—ñµ®ƒVrÇúÂt`¸àÌ´(¨-eWªD ÔèR¶Ô;±Äƒ×õWê?ŒA‡ܯûÔË!fc#>Ÿˆøý
+¢®h&´-{Ëá\ 2hj€ò)Ò”*[’²AXEªÅ´e‹‘‚0T"ĸùU5²Œzöêçà šÀTÊ(
+oëäàú¢qõ”‰N%ùMÚx\ƒåÚ'ÄlÙ1/´Ã¢È©
+çewAP’ÙÎ,ûvCümc±´ˆ8c¾ævHÅ¡šcR~ÂzbÎxÂ0v+&ô%†yúØu‘hÍŠÌ¥
+•Á6Ì̈v"@!ùÅ»M¾¡ãÕ†’" äKžiWDÞØE.wHDí3™—þ³Ø68!žÃzÚX;h(2ua³ÇÇ0D,%Ÿ›ºC¦Ø0Åf6ïÃy%¯­Èœ!NGËÆ~w
+¬3ˆ Ÿe_…þ%ù0~½¦{i ò²æ[„Úxa R §e-/[ÂצTéù<)£°öCŠÛƒõB? ÖM×…:£ÄTÑå:•äÜe:ÚIpÖM¼EZ‚cx7œ\©œÊÍ¢uFœYM™6GˆÓS!ƒ˜É•R‡ ËRð´â&@BU²b§tR"4.Gä[I¨a`E#c/Û4‰ð˜¯Ó´jg¨1ï«È§ÒE¦¯û9ÌGeÅ‚ŒµƒðÉ°Ó!²H¼ÝLá(Cð겕Œ=aÃòô›(íÀuó·–³î« DÆ…ÕlÉQ´ÄðÏš¤üJÓ~SágZË^9ˆˆ07»Ï)$ òQêaºläUäÌ<@¢61“3ßWý¿µX$¾X¦Æ>u×}Cä6B ýs5.^%å¹Ú'¶Š2ËJŽåÓ>]®ûþä›Ö§ßκjÑ2OgH!ˆÆôuÝÓI Š—4‚ý %} :\w{ßG “›Ñò1IØÓ¶dô3ÐÁaóôýë_
+H‰t—Mrœ9DOà;è£à/
+)³žçŒZJé#š÷ƒ °^Zµ6í z”Jƨ÷&¾?[”säÃûu¶Rûòq‹OgwOHk“gô5ËØæ­ Ã'ër®ªÎG„È'ãÍ9%(eSá.bøªm
+¯ZúçZ-Fèª,_ŽÕ–í‹ÀÔº†u}˜n<«xeû%/Ü¢ÛèY…2×´ÑÖ(¶!ü¹yB¬P›³÷y¯ò.îv"ÅRª*<ˆ}Ž­A‘¢Yæ&TXŠPÆ<Wír[†%û&‡¥æU¶9}ÚI,^´¹˜uÖsN-«ŸÚÏ«èÒ0ëR³ù×ñ HSüÞô‚ H[óï}÷P§‹kãÁ‹Sæ§üéÀU•1o©àÌAÿr@ëÒçý´Tö-¥™1Ò­¢®y"ß7ÄáK?S?„æù”Qëˆ1ÈÆÌpç4Bixöðæ0Š57¯¡',ôÿ5Ô¤ÇèÜÑyù}ÑZ4øyÎùÖÝ‚tx…­>žDB¹S7hÑçí§¯Zë­,›¾Î1ßKUÈ$o’°=
+ߢÃ|ñð"ozРo+ù$aGŽ^e˜ÌD[‹œ÷þòE΢ü%•¿MGƒf;ðû ¢èëÐâ(?õŒ‰NÕz&É7Ðis(Y¹ NH ”z"#ˆÐ”Pë8ˆ9gˆÚôL$Kg%ó^ÈôÅTˆ)ÔÑ“îÓ«SÐ2ì@}R"Ùi“÷ õ+ÃE|Hé%É㔧 .~Œ—à ¾¦H?ÇðjG™ƒ:„·å Æžwö¹¬Ã[Â!B[B9å%kjŸ’©›Ú¡iErŒÎ<FÉw4qAË1ÃÆž0 ¸µ †¡~   ß¡I¨èÖ"'òYÆMõBHÄçǯƒ¡Žê‰ÑzF㢗„ÿJUëš\sý G|R3ÙѶ%ÙZˬŽ„¶6RT“Ëâìb&3Vf‚(£ªY.—}¶µ•9’ù£¿iìF«HCÑ™&(öŒHŠL_< u°7ç D†ú">½&„.+ôh/9Å»ñb˜ByâM¿Péñ0§šdPÄ‘kzèˆNlõ.ÜŸe ¢-<Í«3€£x3!šl Nä¡Þ«ð
+ŠzÏ«PZEOÅ&ÒjM4gÒÂ%)`
+>ÉŽH¡¶#`FM¿²C)yÑ"YòÑ£‰L~X„GIÆѼ¼ŠÉŽëìáo (=wëô{Õ ¨ð(ô™žöxN¡w¯aÓ;H£|Ì`êo®*ˆ)&&1]R)I.Å#9QÄ
+¥¨†1„O˜UÁÔ¸³èD{úÓ0;ëÍ9_ìø3äËdüùæU_@¿ž ´‚fä2û06qÞµÝR…þãfZuœ=Ÿl­JEWII± ó¶žÔŽñ ™Ì¢#w˜ Éü>õ¬R›ë®,)3c‘ ëGqHe
+¬½"Ò*}¯§ ul<ßÛ™UMŠÀÐDgH!†.+´?& ³ƒ!4⺿Š`¢¡(GËÔQ£¥‰s8îO†B^ ÍL[ø­í|èßÇÉ@0êSzlÞÉ`‰˜ÚeßöÅÎKjû'ùÓgÜáO\ÏÄ1SDN6â$ó¹Ñ´‘WÑ‚D‡ñyº£øQÇíìs°Wˆ Ô#oâ u^jÌÝ™Oïžö S5W Ήúã)¹pàäáµØÓ£´önŒX;×Í_ÑDvrvwÉÙØ{¼jÝð38™]”ϵOäÓ)ݦã¹I{ÿ£Þ5·D†ì’g†ð%’Z¹‰Ñ˜ÛUäriLà‡Ë j" ¢))1A -éǹò ?t˜wW&- N44¹9ÛƒšF4Î|ö^7‰ûkAPø¯™s›ùN—Ï­€œÎòÄÌ®c§ùd•àpXtj±$z®£è2Ç0ÊüBh2¢ïxÏ+@xL
+‚ô(öþû;$K°î­Ûv8LýUEžÉ«æ'`žM¾Üùf~½„ÉbXÐúã’x‡AÓ¯J$1, ‡­Ï£¾É|L`e‘Î_ÅkX+˜é†…©¹ó_š×£}Z rþ=Xûø³Hóp’`*§È4ÇÈ“Ðs’§ñ)ÝÒÅc\¸C~ý9 4€`‚Cá·0QìJ6ý+mâ c P{V—¬è’õcښ憩Eîj¤´I8-ì(×Xí(ûvÐÈ?ô0µFj,îê~—ÇMR*›rHôÜqÒ/Ö탭L!/²ásÙô}¢ë’WÛ!/E"‚Ž
+´’y„ v
+GÆfáðºêµ%‹‡A¥ìaÀw©æà„ü¤¡TЄÆÁd€!K«@~ÈGtŽ Ͻ§/)Aë¾€5
+·¡”-÷äßÁ¡Š˜X”%ÌK<%÷Va|,ÙÛé,ðW¶îé‰È+VH¶‰ëM *†IÂD¼¸vîáb6÷¡Pss…
+Ï€/Ò®YƒpñzL“V‰0,¶¥éGD’d™‡—~ž…©aÕ
+ë”`dz÷+oÄW)…¾G‰h8kÛS>2ã‡×þ*lØØX
+ýéC¸”žˆT¢œS”2{(ŸÒ Ô΃ǥÄbŽ›P®?Šø%<ÃÒ?>Ͷ³{‰!+†U×ÕæPÒØÌ­\Óß</CÒ+ˆä<Æ,á6vÍãé$JÎrJjrªðÝ<þcã
+dÄ»dÇi
+ᶗ|ƒºœ/:€OÝ»ÜFþ9ÛOÈc{žµC@­Í_=•ªUà¶KÊs‰£Åkm©dÐ
+.SŒdÔ.å1:Ö…™ o¡õ\ò{ 6ªuK†õÛ@' ja
+†F;³‘'üQ2ŽÂÄ1L<bËÓcÌöî½Ùò”ÌËçPg YæÀJDÉžP œä<HLiioa@6—Ç*y ‡T´iè1"p[áHÆŽ„
+¦~Lmhl†å,'ýæ÷dJIáÈ_Þã9ÞRÂ.E¾ô¢ØK+ð+(ÓûÛÞþø÷[úö¯¿ßþøï[þöç{ ƒð…SÓâ—V „¾R‰×{ A
+2ÔTðB|Ú’£VÏWÿŽÈˆü;{W¬„› Èä(82@ŸšŒA‘ºÛ—u9{Qg_”K‹qÐl²8…~j˶—À$S;Îè–•TbÆì¨ò67üX¢£šì
+û« ×_}‡¹ªu;wã2J0iþkÞôÉ3ÅZYrõxx<1Ã¥¨'^ÙÕŒì>B\/ž ˜YE¨ø/÷§Ë¨Të¹XJøÀ”ý@Ïûõé%þôÇ>}çyJÖ.ò›[Ktho¸y1iŒô<ÕÅ/uÚž¿]ÐU¿7
+:‚՛à bÞ{k„ň®¡}Ì»,ˆ³Ð v‘hã:Œ‡’ :eXÇ=Ëõ;
+U§%°ÈHb¥ü2ôîƒàˆìî”K_ Ëi«‘µ…ì3ß‚9žk>^Ô
+¼Þ¢«² ö¶n>ˆ@Üî¸ë/%Ž v™¾ÓÅÞúõ;@ŠN ph½„U­,2kí%dÜ=±Ã#®%‡dhXjq”TB¦
+–qÔ+éñB  jŽ\°›¶š‰£ÐR¥7°Úr0<ËÉ] «qžúliɲ~xDù€æv}¼à/¸T´‹Kã©Øi–…›ž²Ÿ=”;&¦…†`[èK»­K ÷?ÆË丒¢´²@A‚ûyæØ^ôUíÿu^‚¤4ªb…þa)  $rÁd89õšû×¾×|¼r£ß~qz:q¬à!ׇ‹ß‹ÐžÚ›ÈüçV³ŠÔé·†’?SÑ+E××¾ös Ø’Ñ¿@¶·@óŽSfˆ³(e-}ÌÂcå©2ô•Íp…×}ÏuÀ!¬<:4¦÷ þ&“Å/®oUËK-‚rEè=þ*Ó‡U‚G@þ'¯s(&+`Q`ª7ÍÝÈ”#šâþ¥äƒ#¶)(W–öpS æá3®Ûdƒ@ëk^‚檆 Ü%ðØÂàvÖ§HLƒÏÀ[mÅ‹=(#Xüò
+1 Ä@'ÙIÉi7axÀjv*ú˜E\u(Äá»çÇÐm
+Q§¶5-¡žY ž' ` ˆj$¶`³~h#)ºX?òDš T`Œµù@ïE²4 A®ø@OEÅoè5æ>€ÀFç`þ*[àm–`‡"­`àu•@æÔtNɶ鯡‹(Öii e«µíí4‡›àý(œ2ñíß_È…(¯`Y`½rBÇ_/"TÉ41N£/$Mö èu­6‚βfÚúª`Æ0Z«‘(¤çS„zTEØ‚™\k:¢{):Ÿüp X×ትº-Dú¼ )&·f»·ðØîâs/ã].×0Àë¸×ü~¨©] Ý(qV‚*ˆ¶¹Ç"î€ãŽrÝåM ¦ƒË(FXo‚Ì"Žn„¢ wܳ±æA,½zúºü}fˆÞ›»K$j£‰kÕÁŠ%"«Rý ÉÄóòÁ·)ý8m#,`@r˜yq'µžÀDÙ(³;Žb­Y`€‹¶uà‡ŠŸ/R?•$y ØâÖj„žÂlSµ¨ ÊC<Tqi5"ìv‡Æ1‚pž—I)ïjF- ¤9vî%Ù]ð*RGƽ½ne¨4ß|짢
+!5©»ò—Ö·â|xæj¹ÊS®±c0RMr²_~3„J+©T­Ýƒ¤†ºãIgƒ¿êµÖME×£nç ¼øg\2ôÛ=è‘@±ÈtR’y
+6ò1möž©/æ ìq„°aY¹Va ñ•V]Ú-´)§±ôºap
+f¼ÎÖØðª´CÏm‡T½éKyxà,œáªÊuñ>VÒ+àÃ"
+ú%äô8c…¡b3<‡Þab
+ºhŽ¨Ç«“èÊôy¯êÎê7Æj¦G|¡v‡{Lå¢¼d™kî˜1]?[Ä9h¸l®È#2‡g,·¡Žù²"Q­qŽ‘Ü$eI®lVI‡–8R\Pfs»œ
+ºa3éòo”˜±dAh.¢<± Nukô¤ð$¼ËÉ”>ÝP4<[HÑB·1‹žüîÏÖ…qŸp ç7<ßg&®›Æ9èAVm;r{A^!:y²YÝsH—Ô 7õ_Í¿ôDËwa˜ÄÌÎa=’ÃZʵ ,ÄãBÙ΀gé êBZ}=ÐZÌ`f½xþXÀÈ1φ0d€r4­´o‘¯>Ñf–KA\E­7……Ť´ZxNúÙ: ƒþ,¶\eó2ðcŸIÐì2ntFñܬf0¼Ó§“‰’6~{å&”Š§Nó):I ïzBh"À¥¢\ø…dcPshrßíMôc"É×ÊAÐÃÌÀ; 6“àp°éóM±a
+mŽ~åJ}•
+:ì„pý&;Ù­\‹%ËäQ©²¯CËð±îîË ¾Ôd È-ÉšF~~î;·­ñ¹œÁö™Ù’äü¬/Îéü¢€Ó×F.g¹Â Nª€RÁ<ýS GQŠ°Ñ?¥úƒ£ýj ²ù‰P¡‚#éÏ(JâÄd
+Îf¿Zߺ9ÉãEPÞê/x\«¥…
+õÇwF³•l¶+ÒTG{+:ïãE^&ÂB$¾˜Ôýx àâüÃûÙ¾*7ÏÙBtÀ¸dðê‚Þrºçé-ðr%æôô3ÑÖ5‹_ðuTù'NOb¯\µp”#ë,h)DV÷¸.ƒ¦ƒ««ça!]ê[æ
+•Ðʬým›âç [á/‚pd=‘ÕÕí: äct·Ê._äM¬«óHJWµSüFsaÚrvypYþn¼€1þpÀäÆ¥"{Z¥vÃÈ(«§›
+.h°rú3Á´>чg£ú(ês†Eà¶E짱ヱ7ëúÍe¢$4R£ÎÿçdV×É­éA×YrƉФªº0°B"Nù±/A¿Â Áã
+,X´™š'ÂDQ`ßv”7ÝHÔšÉûÒÑb‹-Ì@Ù‹…p®@¬4™Ð‘VM›%€—ÒY+À&oŽ¤1SͲŸÃmðnJ²jØ?UK+êÎLÒ±3î‚´Ýð'Œi®ð.¸W¶VÍ[LSÞa4ÄU ß-Ÿs®5  ÷ÙúQ8S‘õŸìˆ¡Áob#,ÙÎÿx!prMŸ(:i$ËQD;X0¹Û€´€RÂ=Ƚ=^mɱ°£Ú—`Rá°DÏ1E™¡sÀVÓ~òPÆ4n¦?}&¬ÝñKðëÊ0ÊŽÚ³H?‡Ô®„úSßsŽÎ˜6GWSHZй¯°˜·ó)áZãÓºUx}£fãÓû…pn™êëlèDE1ÚUnÅ¿äBî\ïP´e¼|»PdÏÒR§Ä–QàiŒú"„f
+G¡šiA¿^ñ×I…G¢ v³(ˆ‘aVì[Ïa?…$O¯•·É~Ð-Ý™4aÓ¸3È"2!‰>¢å%µ
+‚FF™tîºH‡Ôr
+-ŠÎØ9SpÒ4TÛñm NšYo³ñœ!Ir>Ec—IW^œƒŒÒ5¹H¾E$­_,¤([߀Xf+m{%lÃœJß¼^ÅpKpsÎk¡ÔÌ*âF2NEéBx<{mVí'ïºþµäïŽÒ
+þrãªí%0‘s'Ôá0OŠ‚ÐÛ.ô |þrDQ›ÞW-Ä  X-òru‚†³q‘d((sdI¨¹’v|X{Ÿ~ÚÔ>„°ühu¬r^—}S„´¿‘i³ŸM´÷Ç1{–7º9R7c ôXJé¼Ñp°
+ ]}÷p‰P&YŒÉ2ƒ%ÂÊ*c&‹Åku£ëfîJ³/p¹…K„Íßn›$ãú#Ú4‚—j‹‚ýÆÔ²³(aßöw SèÊ”—k… 4‘­g+KìSZ"|…˜"ײh]}‡B°†`Û˜Ò‚1UKBrbgÈ$¡~.nîRÀþýGÔŠÁÿmÂôc ä¯ÊXtvôp¹Tô<¼-Dyô¦²ÏxIÜ(‚ßIf/ˆ“2SÄ¢tÑ5ä£]&‹xÇï>ꡘÿ‹ÝQÄ
+S©QË©'ñI0É0»£ƒj 5e’úÑ8
+©5xrJ‘s‘WhJFxN6›³á³ß^N 0ú9,O(³1°?\ìÃô!kÛK‘ÔD‹ŸN2Ð÷ÖÊ(»KlZ‡dŽÚ¨ÓZ ”(¯ìj?4ôt Í°ÝXwCÛß”—I’$·EOÀ;ô Ò0kjÙ·èmñþ[½ï
+Mæ=ñÆðòÉÃÜ ˜J·DF~ «#›Lß% Æ;Ì„‘x¦kk&Ž0BlÁþÁ*D4Bíà»l
+ÇÀ‡ŠC½:KËN;R°”úED×ñ0Ë’RçÄŸM®Pb8†· 8 ¸¦„±åÏØɎו"ŒŠû›k½î §Y¤Šu“+^$öè¿>`ðï\$,¤¨¿²*¶¡ÄÌŒ±}Fö‘^ƒ¡0åˆØFˆ¢Æ¼"<´! t þr7Bp„ :™6"Êÿ  WkÒÌOYjYŽ¹¦’QÎv[’X0(÷ÛnáÙœ³AzDP¸¬>¾m ÇÞ¡ü¡YCî ̵nC¾«"“ß*ô‘9â§Ð×*!¼É*°2©: Aø?+Þ›a\ÓXVåf<ŽeèKÎhÜ’Œ"cÀÐMÙé¥h<À`kãþTÓ˜ËÂö¿‘Ä“ãÎm°yØ>ªÌ=@¦ü­,ëH}ê
+¢ÎêàdãÙšueB‡ã
+k$ÞÄþÕy л zR¦€Ÿ‹Õf'áœÓ—«»€¾\/ï2. ~Hë>x^¸š¸UÃ~_ï£Ënõ±– 7øþ²ÉæuåŽÔya&ÃQeÎ%ÈǬc˜X"}3Oû˜5Gyùú
+îx;‡oÓGu C¶ßwÀüBÃάÁeG³Â71Ko±E%2çNùò±ið9 ‹#Û_¢iÜ·SNüÀ¥²hIÇB§ *$“ùçÿå%ÁÙlz³ñ@’?ì?Þ½vû˜Œ>ž³¤í³`Aœ3üÅ+lϹ`¹m1Æ Y÷¹ƒÞÔèëÉ’Ïb¦ ½H’ö«yŽíŸ…ÁxFò`û¶´•)«âùÃƹcë2ãEœAvòd¸èö™YDQ¢,›ªlÃÑðká'OØd©æZsÏ™/…L3Æ5îÛÈñ²+²kÍ P[Âr¥Ãº5àO™§BÍö&_A·*Ú¥ñ8‘bS”O1·ÜébÞb§!È(¢«{qrŒ 8Ôävc™nŽ1ý†lØ:/uªWlF»8ȉÖN&dˆP7Æ$ÆpsVΡà7àëÚvF†Ñb_ºc<Ó‡öÂa‚<÷S³î kŒùyql½$1BÔ
+¼”RA1 ÍÈõä Ñ
+~'·È¦ ¡ûƒštwG[,(ò½á=Qæ2S]1ç¡£¿B–9²„ÝsºŒY3¯ö1¬
+ÑŒy’ Ì@£#Ú±Þò&B‰^võû~õšCŽmÜ N(î9ÞAV“…ÉɽL—S³ÐϳoR"¤R¾4NÆy¯Í@IÙ`ø ³VÝß!—Fýý—zkü׃ÈÄðöÞÆ/ïÆQpr/»/¯/Í[G`‰¼k|)`coP ì °Ž¹vþâ€`ˆÊj7·›ˆ¿Z·s˜­žÍæÞ¦b¼uBY
+OÒ¨Cœü¸Ç-Ž·U2+åAËvácfOiê‡í\ɤ°5H XrÇŸ(rG¥¡QÛa‘><‰© =Ÿ¸™ŠH¢)ÖÙhþŒ{bŸ°½ÝÑÎפáTˆKÎ-ÿß„*UnØav5ìqªÈ@PqZmTï GÛkÔ¨T‘òƒ[s@Wu××:_j°a@»ž0#ñ0iWÉÅÙå/ÍI!h Ï.'ÐGX©B³›Ë+åe×¥&½…'~*/ ¡¦5köÎA²0UäƒQÒfѤׅ`Ï%E"´ÞH£JBôeéϲ œš_Bwzñ ºˆéoÑ…>!?œîÿºvÌûÚÏ-ΡÚ–™à¯P.Jaª‹‚t8ò%ÛÔ$Ð œ¯"™!˜ 2)ymwÆÛ@y­ÔšêÚ¨ŠÞ öˆu3‹­¦Æ—È2Rú˜Î9°K¡ÊHBÊ`ÝÆG—y¾@þØZ}š€:£{ÕB|{ïwY¸b¾&‘iF,ìnjߧ Ñ•îðÖa»|Q>:@õ°7eÕ˜=ƒ¾à³â2Å‘_øwAjº},îÛD’õ@` ½‚jëÊAí«ä ±ŠÜÙ,,9kÝ=&²\l€Ø•Ù6¢W¯õò_ÆË9®ˆ¢+Ð+˜ÿÐ¥”©W rF†Þ¿Ïm
+/‹3$'íP2‚Ñ£’é“ïÔ¢¦Ù¤U!;Á%€Ô¬ÀU ½€¯Eך£.£c$×Iôa V[ß±˜|ètAÆOÝoŒƒ`„TÓ‚tX±ùé}ã‚»yíZB0^èzžÓäK˜p´¡Òûu^ôØ¢Ÿø» ÏZÁÛ‚ò…¿¥S1¯(ÃÎk -š/4‚mGz²³n¾éíÆ.ÙØD\à£d ­8õFЖºÜZ÷籠=©><س¶C‰†äI¸h˜-ö>|3ÏY 5ßË®¦Ë"…ÉKäʳô 4ì=ÆõÞNŠða\‹.ŽŠ
+VÆÅnñï/=Ú]?Åá_:Š:žÖ<ÜC„FQvµáðHlr,fw:d>Cß÷Çãè?’vœ(5 P]Ž Õ#{OŒxQ&Êyþ%Ⓠ“·¸KÐKšù½ñb‚Œq‘rü~„2“ÓAN”vk;RßKß=X z—2™QŒS9©¤H¡MÙ+of@5™ ±ä%’&tºb¨VIVld å,ýê¸^‰rÏFÌZG!¶Øâè¹K”Éy0x£*´YUé,uS‡r[Ǻ²÷ŽÖk‘šß²"iÆ©9€¸2û•àîÓE^¤sˆ¨¡ŠT¢e…0)Q#RˆÅݬ2
+I4BÌg:FDÜ{Üê±ÓÎáãI‰¦€Ag„Ñûw(ÊR=à‚¿Îuˆ¨Õ¥jôoH$ŽùBi2”
+s×JÎõPôþ¤Èº<“‚l‹ ®Eº6ñ³\’—a— ï‹åoÉÊ;rÒdЪmHºø‡‚>.$ã´8ËÔ°`IJ¨¯œÍ<‰°ËöuùòsÈEnŒPÁ:¶Zó$TO—çRýø‘Fåÿ¤åq‹2J™æ¤·NÒU £UÔz5Ä0à vÏöÊ Åì}±·;’›UY_ÁYŽa$윶ú¤„å€Ýàm7ŽQ€£-*ÚÄKÍÇ“X“ FüC\P§= …ßÛa³‹<˜L娄æWó­'Õª>–X¢õ섳Ò"»;m² ŸnÅIÿ°?Tôô±º—42ö24¹='^KÚv±x«Åþ‰` —ígè7A«ÁÏ“ØPM¶…ÑÙÙ7í."¨1>³_°,rVDçÀøXNÀ[°#ëŘ(ŒŽ¾«1$·¸– ÍÄÕè
+PL3iÅ›§bAk €í#1aáÿŽìJ(È:
+¸£+¤/äd–DÚK’¢¿{³õ ä¥dÄÿ×SQQØÁTAÓŒ?§Ö\²í ržÅ~ò,.mK¥ºL±é˜láKÏOK,CÂäÒó:×¢/ÂúñŠúÊœ„€ó–|@ÀóN`‰ñ!‘Îr4E!²Œ-Ò¸†!•ão*æ%¦@rìÀñUñ²ÂB
+Z16&J‡ï}>)ÒË4é1PT0ð àH_Ö¶ûä±Ü•~ó(‰ÁaãÓÁn¬%8‹Æ¯lˆ“ó% Èæ4Èš˜{ìGÞ'}5H§±?º,á 9ãSäÐm©>ÃF}])÷Ëœ|Ί^‚a†a¤U€ÝBø%˜ÖNxšQb„ÇÓN,rˆ ƹ3X=Z9änVwåOyAŠižÓ0WôÛS§ÙYn€–¯~,z˜úÇ“¢.çƒð²â‡U„#âíƥý
+ØöVÛd•.¹yi ÖC~Àúz¸ì0³DŒÊ24\y Iô`4Ò¹FÜÖ<éâU®<yð<`Â8½‘'cäÀ*B' ÉS²uD
+>Æ8™*Èëçc¸Ñ¢£ØL_v™Ä ÚÊA‘ú/±•9_ÃTp­K¨÷u†Åõ| “6Üç#xgœ‡P‘°F9‘BT@\àl-»†¥pC4èyž`‚ÉS¹£®=,Þˆ³f{;¹¬Óó‰—ÑD½=&0ój]ê
+Jº2míÅÐËáèF,iœþñ<]
+0Ùþ\óõÎzÝmjE𠛘ÕÈm|{í•~Fh7(á¢Û¢{û}ØLóõ¾f^Y÷ì×Éú6].bIÐùé_ó£ R–£*è!ÓaVýyTÈ®ÁÜíaq™ïÜ—Iâç"½/Cè3¹ö Õš¤ºÂ±¨m#ö³æï‹š
+°K”ì^+äU!±´ã.++@ÌâŸÉf`‡†|ZSû¹1Œ†Ó€„“% Q€TºôÖÄñÎ0®þU‹CïDºSd ª.˜ªKr)áPI;4>ó‹Iý2ñ?w›E©I§tÔ…ÚàhÊuðêY#Ó<èJ¢¼zö,á5,Òe¬xÜ„ÇNá^
+‰Ebˆ8œj·šb–˜~4üK ï²€
+ó[ãœ#w®„4Ñ•àéY¢]-.`€ÕGt×ðØä0æ™®ø`3ƒ¢§p5ùÍ"¼ ¹…æà@Q à"†¼gÁ+û,
+g´¡½x,ˆ—ðB¥¡¡|”ì/6G§å𜠾É þ²–#·A‰ü-öA[-àïÏA–›×’îÕœ"U,èÌþÒ`0$1B—?"¢dñÜ♎ö® 3¸ãÈ
+z“š² çþvÑ“ÉþuIï»´ÛÀÝiñ²ÈÊ÷‹"8€t‰ˆ0ÄPY¾ÐrÚ“ÅÛHtDÀËv‰äÍ99_Šƒ\â<²ÀžˆþŠ@ê”Ý—uµ›GvôC%e(Ú®¤1}‘Ê°»Óæ Å(ô–="¤á*<‡ÐƒŸŸC0:ò…ð;,FªeWuÚ±¹å ÅMº)ILVçÏ?Q” ±ÊHcß3‹Š€L“½€)›ŒˆÉ…“LIæÂð£T<­ž%\RJn¥ç帧4L[uÖ‡‘È€«y‰§ô·á[hÖÕd~‡Û/â» dϳ°-|Ûû‹sGþt›gè¯z†àSwžüÔã¨äǬ>ßÙš¿WN)ü52Õé&?8ïûE‘ž&ÂÎxØQ"\¡Ñxƒyõ"à-Å|ôEã^]7ÇÁ)iÂö¡ê2Á7 ÅƃBÙFÿ(»aK _:·ÈÐ; {å]ègû/9 #È–tx L‹¡Æ´õq>Õ•˜0Ì,x?€Æ˜f. cžæh÷C©”}ˆ5馬zÒkO‡«Á!Œ,»bP‚µíIÌ?®-}ðêAÑÃä¿^á)±bÉïÏZä!Mcr¹gŒÔ2l C‘Q5Ña§ÅÒbÑ$>çÝì:¨iÒÆ#Üx­N0-ËüœDø!4å’îŽä‡ó Øò¡ä³Mñ.ÔÒGxNSòJÍ6¯Û R#Ò¼TÍÝ÷˪šìI"ôu š7>
+| nv¶q6.`5ø#ÓÅ)]ÜQ>d–‚úÐv'Ñêç4°[Kb.N™¬ÍnØÖ]’±Còkù*1ãåSAÆðé&Ï¢˜Ë—çš¿/jøKÂ#¾Ô ÎonÊà: ›ÈÉ?ÓN$–Ä`ˆ„ÝÏgG“â#¦N›^bæ" ]ÍÁñ(œ*íSMXÕ,Á ×åºÃ„u7¬6²ÎH»ÞøŸ;Ç'^²r‚J²h66Hˆšó䀨Èç!½Â˜¯y_gö’·bÜl—0qîÅ
+ò=Ëa/be4û'}f@ëˆ<µž“Á/˜Ã)åܦÀ]ÎBÚ½6Z7Ön£Å0¡»Tи­où†áG]laF%ø,?Xíò†0´<íO‘e$q¼¸ç¢'kuùÍ¢–ÕQåvõëE”^Y&˜áÌ0½JïßPÅ ­ÛDg7g:0 ŠqE‹ÚgUHdå@c¦püÒËSÄ‘ÀghÖæs‡1M™pJh¢HÀ®× ?WVa¼ŠéƆ°Õ¶8ZÀdJƒÏz¥˜$mÄzџ͇\„…kL¥M/°ôÖΧà0XâÚuւɃ\}îX#<þoœWU6O|Ö…ôXÁ WÌ'çTÐ -ñ¦yîÃï›ÐÜš”¢šåæ±7wî€,´‰ÚïšÐhU{¶>Y‰_U䋸ÜÄ”ôT?=Ûç늋ÞyzÍü%žtyòâk ‘a6ƒ&ª€÷˜ìû¹Y•(DdÙh#&„lSÔ‹ åÏ1Hm˜ÐÆÚ^Vùo8uõ1w ðJºK¯Ç†²×XÁ½oÚ}.â•|-t’÷´[DªÞ®!É&F…Ù|GƒR0ÆJq¨ßPÖÖ¡¬|O'¬ðli^ÂX
+Â÷Àæ²ùcÍòý!Ûþ ·!x¿±†R:æÛE2L /{þ˜§èñsE_ïlè__ä
+%
+¡ªîÇ¥)Ñuz!]a¥M¢BÒ¼tOÊ6²òÅK€fÐ|[9*ÙŸú—ò29nî‚pʸ°/g9“ÿ*çõ׀ߕh•t›Øf¦—LÜÀ]dœÒOëd s‡™)G/¼R
+*E{ºË@¢øo~Ù8ŸIÄZèK²‹áyÑvžC»u2BPÚ´7R‚ãchçt
+MAq MŽµâ£²ÍÌ© ;YÅ=ÈK“~~x §ÝùÕ«´¤¼Õÿ
+gÅ0ØÚÂ'²VÏK/í<™êÀÃVC$5 © æž Á+æÄÖ>IÓ7™.üNÞûÐùZ ò·b±b%Ÿ%{¸@XrE!9íNƒ®2Á\|]›sF|C¹¬k'j†‚Pöã€ÇJ,µíkÎS”2Òqbœ¦B¹˜ýRö +x`3yž-ºP)n—ƒ9ÉÊ&U%C²Òpðc´œvûüðASÎÎ|- ¸ƒ”r0ÿÍ•ëÈA™¥ò°ÛìS¹É#a1ò®xƒb(^ µ,VIp-]‹ÙêéYqÙ$ƒ±Ù‰dDj`{tyUœ¥è´V±WÆœÁ‘-â‚›R©SÏ®óiy gѯ}ѹUƒô \LÛ¾úd ›RÂÒ›Î#ŽIÃa“”€t™d$‰ò×f¬‘(yÂçD*@l(`ÝG"Â"¡XsÅèñµB»Ôm›|)_èM[Óyq¸Õ=©$rá
+‹¶Æ ©_ô]?zöçŽ2£<t¼G
+Y‰ ÞÅÜs‡³Ù=wü>žf wá±zú(mÖÍŒ 4¯.³)f
+ O(.'(Ê Rå3á°ã ¥‰ùZKч]n.êÁØÐ,XιØý†~£™<ÁeJ3¨4Â`ËÐþªj‘¶oäì„«˜Ká?Õ_ÌiÛjÓ,TèÞaä
+>·,IÚ6íúZ Z™‹q¾R­C£L)†µÓ÷ñ¿°p¹9¾[„Ì@r‘mc‡¥ˆ+ãÜ|6TñÉ}·¬˜‚h"[¬/Qž³zÆÛ©çÝ¿¦ áš¼¢ÆÛ]Gú׸Éiôݬ¡ˆœ ÒAP§dB:Ç.KR¸$ß³­PcQå 󤤒ÄÃÌP¶‚¢ê¸-=*ÇŽ«Ì°ÜªÛ»(W±Ó燇Q¨d´D}8«§ʲyúKŠ¢Î‰aN›%¢4Œ{ó‹’¬G¾¾ ¤êäqxûµÎµ7Ѻ×Ó?\^g
+„—+òz¿dÙ±[=ï FX- 4ÏêépÀrá²&¶^¬dVn}Ë暤+‡‚In…5‡¦u)>[ŠÎ{¤}ñ&Ù'Ã¥ü$íRÅÚˆzë¯ Žèý"žrÊØòNÚROcð=é<Qª ±†Á ÁK8ž°;ƒ
+N%gþ3‚äɲ²<ôªC‚t^7 ´cÒn”‚£iÏ’¹à­#Ès2-³ìúi „Õ¢pšbþÁPßAŽ/
+<5`?iàò6Èá tqÔžb¾èBlOquø+@M–èi¯.œ¡—æ !”¿žqÿΖ~-^¼° tEÓᘄézj8î‹ÉÈž¥ÁÂÝ3­œ³|÷Ô‚D[…øˆáp=µõ`¤+ɼ´ôá•ožZ­<cÄŸñ“æS¾yjëö¬Gé˜øT]Oín%(RKú‰«÷ö6èu
+H‰Œ—Mr\9„O ;Ô¦‚
+;<ì
+çpþµYU{(Ù¹µî‚A öTwne<2ÿHÕnnûêÑ“ñ!¼ÿ\§i‡_<èˆ 1)R*Oij¿dŒÊE‘º·z±yr·ìkÞ9Hu½ОU”Ò… ÊTFõΤ8×yÕdÝn@8ªU®6 ZŸ|C¢Ô† ÕÅÕ‘,
+$­¶q¤ áÛÒ§@²çF•ä¬O/ Ï7 åi¨ÅH‘œu•V<„î%ªhnïÜc7ªBå&](`[d¯>è,£ÇXªœV+Ù]TÖl©Fá[‹êHK3zeí„àuˆC­®:=‹(5óS¥IÌƽCV%øiÑ;§*íÍ:ÉÏPíUÝ¡¨©¡tu‹Ã³Ì%ö½K±@9h?½Šü5
+Dk‚Þ8±ÖA?œåõ–ÀóÄùÿ–‚èý WCàn[ž“M%¼:MpÇüzƒ)°¼Ó£Èf;•…$«ì‹ó up
+8U‘jtKcÚ. êxl׬?»LžîB›á› AWÖ,@±Í‡\
+¼¬ˆ}×¹ñ*Ù»­ÌØEóÉ_Ç;,Aø}l—êi7¤Tcå†ùb¬¿¾ã¾Ÿur/Í è¾ËÉ‘é8¼6‰“&]Ÿ˜fCµ³¸@<.ÅDn4é‡>Gv)J…l,‘ô³6|B ½aãPRˆŸs§”–&Å]z}³ ²WyΚJzÉ&“y­D¼”Á8Beßõ$øà¿–¾=ñº=þM\Ä™¢djðoƒ 2%#“`b61ìÆ e©âï0/'‚ʤ‘`#<aÕ•›PXÒù[›çï tTf¦áÇÖ«,à)¥Ë¤znaj®ˆ:yD¼[îíÁ«Æ΂@HsÍ¿)J'vÁ¢*[þþBÁdªÌHYž¼…Ö
+AĶéi‰N>]l=ŒÌ7ˆ¶äÁT–‚âúƒMùØ!—ud^…Nˆgv 3Æw #1ñYm;SšŒ‘
+Ûr"Nd,µZÍà[ø‰ý¨ã÷jüà^ÕÛ:wn¤7Áé”wÒÁ‘cÜ
+#â[˜H›c¯\ÕLI¤ˆ:MîNøúÌá£åJ}AÍyKÎ[£îþÒ]3$%e˜pKFíPBÒ³TC陈~A7‡‡1$†Ö¶.ßÇ|>ßlö ŠFqÁ(çœs!¥šÕ&?ò”³czÉ¿òß“t¤Pv%aó=Ù €ÚÔ×a!4¦Ï^ñ8å…(—&’TyESw·fy˜†æš`‹ìqÈןó 3“pI¡@q\‡æ¥#x1C/׈™]`šÉãT 9ò1caujÇtãyžÌ~0Úÿ•2Ov㪬¶îUsÖp¾[¯PÿUíx@”®“ÿ£ë‘y¾ô‘sê›e*ž—§C·„ÓVµcå8ºM- Hj¦üýÈŒ DrOÜ;!ά<°¶‹8È.¥µ;9¸§ 1eq?ÿw âµ%h@³â'ÈŽæßA¶Ó¹$< u GïZâzmÀÓnǼ(¹µ^@K]„ÈHÄÈTƒqÝ÷û½0ª«}¼ñ䘀t]àèúdêdâê{Òð9ØÁ2ÔX¬œb–¾fÔ«d)&Ýr^'‡½HW!@]£Ìÿë|"†ˆæLK“ê!¦ÿø8¦K†í `ÓÕ—j| ”“¶Øæ »Þð"ï{vÊÑç±%(‘irÏXbd‚0µ )´6zCÜŒ~ù—9zÈÍêRC>bºŠ¢Š9n™gì¾ÙíM)^">ªÀo2þ»ÁšóÍD}®ñ¶JjøÆ<h»œˆ ƒT¢jR dN
+çŽþª” ТìÂzù©IkÑ"7Æ}¥rn“B¨2¢µ. 2GÃÓûUI:„ä c_R\¢-ô=Íså7ŠVI.ô.¡»Å 0búæä«y†R
+»†þÊL„U"É+üƒ‹ÚK‘ëÈ’0–64F~ÁÜãK">"⶘W&£ËmòÉ((,Ëäl^`ŽŠ¢ßåï’$dÚÒ¦þpfѺљë‰1¼’eîôgq]úÀ¤s_Qô8ˆ6ûK„Ž!ˆ‘ö¾Íà¡"%þ/+Û½…uýPBnÙ!-Ø"D‘OŽƒŒ$§ŠÉç0'U}YQ€œZäƒáœ4m• Í4ÖǸésDÐp2Ê;þȧ¢^Û;¾y-ïâXÇh·½ð$¨1 ÕŽ1îæ ’’d§aá Z<œ±EiC¯£[…ZŒ7r1:%ìK|¢UÞM
+(ç´J«qlõ°\‰' "•èQ‚ÂNõ³ÈNø<‚cÝ{ÑÝÿtÆ<¤¸¸¶Ï‹n‰H_ƒ)ÙÑÄàÇcÍåFðVhò¡mÙ¶·—Ø¡%‡0’:§ôÁ_Äaü”îEn¡¡LJޘ™y‚I¹ŠºåÆ9ûUÜvy³úB³(Äp º}e([h@ ÅÀ^€Œ˜°Bã¨EÞ=ˆ«±pÃj¡¥¤3XDiÊE˜$Å2äÙëRË+b}8»²Ñ>R³z8•ëPh©ØÀ¢†@„ay1©÷"„²®jÂçCÊsŠî(Ìz´†IËgٚˤ¢Œ.nh5–ÑA*-„å ¹"—àbŒQ
+],ðÂT÷c©ç 9IJ¿–&ƒ@Ìd»£LÈ눽Ý(媤¤¡ïs €Š^£¬k=¯+“í2êBYzt_½ü’á³e-ÇÞÏË_jÖzZcÛv{È¿^‰ÊÖ@
+ûŒ`žvN…²yU;î½1ý®”‰¨> ó­d}(Øý›XÝsÀMæ6Àv†Àl•PðêÉJð*1ŽWZ
+Açœ7D±F¼”ÿc¢«}14Át+_(&ˆB«0û\%à¬ÃBX¹4\ªÈ«}ìST~ûínŒ4ZÌ;¿ŠFšH½Ìf´o C`BÛÆ!¶Œ€#:=¬þÒ
+³ÚØ4ïƒò}u#˜“5Jä”=Jh®R» ²¢©àI¡>…z+ºáœK.Ù=aÖ§õÒ~Â]âHu (­’„!%©•ÍˆåÅs¸ªN¦<Ni tYÃ1[ºs‹ ,¬d8œbƒ°UFý8Le=¸M¾ÏJ*±Dóô|Äòo‡¹ž^"vÃ]S>â͇&„Òó\ŸÂ7ù/öÒ5~êr£B™¥dß@Ú´XhùˆžãÃ9×™ßnsÇŽóª;oݹ#ùÖc¯ämV>YmX° ã/&Æ j(u)Ë弊ô4ôCW©À“wažqì È
+q7d%Ò÷¤€Ž~WyFI9áÜ\—,0$¬µÝDF‰€Ë
+ÐDÕ.ÙšóÄR1–ß”†á|(ŠÐ§ƒUI,FL‹6ð¾<œÿ‚™}Ìd’±Có <vG( %¢1¯/áU岓LÿžT€W±U˜c{§è2ù¯‡"ä(•i«}Œ]¯5;VO.(¼iöf4Ç»i-ãÃõ¦ÃaÕFA#°[  Bn[…Ý
+"9â1HÉóªjÖ‡ßè¼2{ÓaË»We*XQ,#—,Áõªš-†šþò®C*ÏfU3ŠLñÌÆ!L³ªQgÙÜožYÕ¾d¼Fà7ÑYϬ:+u7«¿/§™ÕRp;L !^Á¢&7Ë0C“fVÐM»9ÅK(6W}ÓH$Zs6iÈ;ì i"%H~õë„‘KDC
+UJ ¸L­z:d›:ã[:“Ö¢!DÞŒ?{&…6SËUj Øà$'ÚËx )>†–µa_щEŽ6‡d˜÷:Ehªuè:Þõ ««Œidýq¹‹ï°ÔJ;ø¾0öm4ÛТäþåõ¡*"èÝ <ÆôÓm[Á²úØ¢ƒªñ\LE6‹M¹ YíéÒ»ŠÖºÓÝ.ªkcà2º<l)A¯Š$xIÞ* ’—ÁeûA¡?èÍ0¹-ÒM Kç×ÓÅ­È-ëÝ÷I¤•JT‚¿ó;e ±s‘²Ïè¨^VŽåŠ,('í–ß!§ýüãœaÏÌd‡vÐ1²ÂÿºîŸwÚï˜ !ø›u-r Š
+[Áþá·=ˆzŸhc›¸¶&mt¿ƒ
+/‹ËA.–‡ï°/Z7¸·§‡Û(¾%m–öáU˜b|ž™¸9Üâ(0pxïàè–؃œZµOº€˜oÈs)P×ür.Ã\tAfÖ{“.Æ ½e1öI×Ú0qÒF$PØôò*ÈuC¸§Û© duíøí;÷¹¹Ýæ>Ϋîs¬í‡vØsXc¾¼m Äâ¯}ÔäB.­ú<–Bóbéô$àÛOmȦ†ê‚º©¿sÚ4~¿ÑbŒqFj`ØmóO…Ž&*°.N&Ñ?}b5Û&Ö$Οeõ K‰»£FØ4{z ¤s·$;Âøºž«´"³GD­ØÜï`òlàý\]„àÊ*¤}aw@N3dœyõÀóÏò.æ®ëæ,ë }¾‰hç£ígYϲè-êgøôäË: ¶/ÂUÑÑu·g=R¦Ü1T¾®«ç¹ã+e ¿¯04ƒŸŒì ;Pyv• çì »æ
+ƒ&j*?Åù¯°ƒ€Æ#ò<|è »3éwaw@NÇù4Þ“Aüîƒßs@!dü"ü »mÐõ¸ èë]›,÷I“\˜èdÈKÌ;4ÈØ&#EQFÌb$ Äø¯’ö¯pH#Ê• ÂñÈ+Àw2-…`€D$IÈ?v”< ƒ-/é©°DÐÇ™ßnŸ!‹ñ
+Jý°â‡—ׇ›nÝûéèsÊ2’Ôc÷;lDÄÊâÚ¶ö#Q™F,kSÊVñ ûÖ
+s4Ńåá;¡A°m,ÇQgGÍlÉ£C¸pàÅQb¸äÞ+7G­1Æ@áX’ƒ
+ÎŽZ6^€LsöµrZ^ñ “ÅÆ'„RßÝRï¯ *«_d¦°¬A+N/÷ÆÓ>F®
+Év9›,iRÌH!êÁ¹Ï´¬üCJ.›È8pc``aäMQ¶] 'œh<*†õå°AY‘$99 lƨ»gÕ–›ãI•©$‹ñ×4÷7$†ÌrËúê磔†"o”Æýmïfõ%Š ÉèË@­0X;&©: AägýÈmm)hÝÒ µˆIÔMR7–Ë |lr"€ðS« “W-´Ãšh ÊÐTI^žìÆ…qÆd쓺lÇÒÎø
+Bw±ÍÔ­í[ h댗=r]·„W =0VÀÂÿOèRÊÔ+P9£Bïß_
+òiàÂIRñ½ÂywÈ‘»“Ô©€ï‡û’
+#w¸Çq [ ÄlÏdîDL ­!ì~A‹h6‡ænö”`‚H—¸>›è0%œö˜_3lÛ°!E[@!Ï÷¢&”¦8ó>ê
+*Úã3ËC”¶¾SÌüÕ~ÊGc"! }³Az¢ü(Òö ¹cøùÊ×LðMjÇ
+[+koÆ0¦9­N)ÐAâN Þ8Ò©Œ»Æ†Úñ߸ßvÊħ æÃÇ`Û0ü‰-‰+üô1Ç£ÅÉdÄ¥¦|uÂõžö„Š‘Ʊ<ÏùCW”À¸í=J§•H(gê›Äøif
+G•N€lÌ„oƒ—KàØ|ü^RX^’"%d9G²HŒOa2¸ yò"ËDˆ‡gûl9žY y5JF…‰Ÿu¸¸Ï¥hdÈ3/2«œÜÈIÁOÈ,k‘yéÐ…P´ Ñ.k%ñ†¡BMúòE˜Ï'˜À1ó¬Ê¸™žÌÞ'Lؾ°mII—à¢T@+¾
+#?F«´â6ÍÑFì>Ö¾/ó€5–ûZKf0?Z<ü½]\fØñÈKX/„ò¾ŠV/CÙÅe’·øA8Ì#_%a,á_©–0WFvM{ÖàVRó‰ù‡Â CWª"^t¿ST9ÄyÎç(¼<âÒqäRn¹‰—á…a”òÜG Ïb`Æúpb s  sû74C!ˆUU×mÈ[ƒ2Ðþ²5Bf¶R*¦¹n¸‚ðA0&G„Iý|‚‘ðháâP%èhjàÃaÞKŠ{Üÿc ¢ƒÊcü÷raÍÐ'‘Ò®ãöêç+Ë÷ÃòeÄLb#uçv" ·fê˜"è—ð’íÂw˺E#IN £­[þ=0¯›†
+1Qû ‡l
+È z,y2Éôœ +‹ÿ+òí;(`•exd,ÙLa±âÎ.øŠ^$¡ùù
+¿"TF»8KWUV_ùtBL8WluA ±DDZŒ °žãæïDô97“©YBÖãvòjãgR—¥$IbuÈ´-/gE)ì;ÜÛ*›­Í[”ÈS¥Øߕ̇¯=û;DGu¼:û —ß—úõ;ˆ3(ä>öedšJJ‡!àÊÐÒ~·ŽD£«¹p@k¢Ñç@=ÄÃûYxJ<|fU‘'ì¨Úõ*üb^$Ø& á$ÆÔuèŽÿ†ªóÙÈ*qbг¶zç~‡.°„ Î?•¨¨Ñh(Ö­fbfi'åuñËô¨kíï“ø <:¿„†ï †¤sÃÁÏoµÈ|3‘øhy‘‚»Æ9ÃäÝÌ?òøㄨ8ßA6IB;Ï÷,L™‚D Q.Hßc÷Ìl©ˆjV‘g~PO
+Phê‘ ‘ÉálŽ´›Ê7pÝ ¦Öùº:þ”1™ÕìñÚPä˜.cSÛª;º@¼ytõ!ý€rVL…R«Ÿ¡ ‡ñ3Åw½ e,ä xÑa˳, ¿–ñðeQ>7,lŒg/ãÚØÔ<LÅß”§J”J¶>ù2ÏÂ(‹Â.ÏK  Ý©–"Ù0Ð}Â^]üÍÃV´)Qའ2²IUùК1‘:€‡ÕÅ“q§÷upï©ó™+1´™ ôNôáŒ'e6L‘_okàïDMrISÞ#jÅ9l'†àªþ‘Hø‰ïà›øÈ™÷I-Má÷¿Ä¿þùöýßo,%¼+yùJh‰€~A„Ã1Œ>(°CÙ¦²Ä7ƒ˜:’Û‚0¯ å€ã ‚4R69¥YD(㽇e¶Ÿv\ÇNcæØß!°%ºÉêDò¡#Sçædå¢ðX›LD3Ä bLÅ'ßë ©TGç´”½¯ôw ÿæIÖþûUÄh¬ ×,>yRWXÁ,ñã8š[ ¸jÎ…\­¸ö ÚGÝ@ÌÚhT‘ÙG]nÃâ@éÐÄ„WñÞ„¬b"Ø•E at­ *}“˜I˜ò
+¬øñ}
+J%º}º@V£®ív¾s%s›+ùœWÝI|©7—úþq ~0‚L5“ªM<”ì>Lü(K1jsw‘†Ü1COçã<4ÂH¬V:ïâG³u5ÙC #‘c“ÝÙÞÓ,EÒkqKO/:ŠB–9d…úñ]Ò
+>K·éiiX0,œ"Á‡U1‹û´£ò&2X°J³î¹ (æRdàš•š;!˜„-°g‡¤£Ìªh”I°²|ô]fAÚ”­aóuÛY‘ÑÁXHrˆ{FÂÜ4¶xg+u”È"*ØÒaM•jð|ê ƒÛ´ïp ö ¾|ÅÉ)ƒ‹Ô«eˆ±AÐìÈç@tT°B3uå·ûgvF`;²Û„ÿ¡9³„WBee÷}I%"¬á4:ßAÚ.Aö&<·F©ÚL²ÎtÍ‘°Õ¼0± !­kÞt.¾S&YRjÝ$ÀÛˆ™}ASªŠ²ÚöQˆ6eçg"ÝêD6­&Q•ÊX>Kecµï’ Ó
+X!%ü0H‘ 1Ëu.Q¥ìmÈp+1ü°£ _Š,ðZÇ\Þp-*¿êÃè ìáåyλIëpÙèó• “éåÊRÿ ç;HÄØ,ÿ0–Ñu0Î¥‚yŸ*×’M…^1™h4m‹(TÈ9î%@ÖE3ÔaÍ盓õƒíÔ@B&
+‡SêqÖÒ
+]8bÉ`æ·_²â&«¯´¾«Ì#ki¨ã—_¥aY§åõô%aöö}tHŶÀ
+– ‰ÎËß¼ºÏ sÜK GYð§ë³¦3ð
+Ò@ÚìPH
+C*¢˜ €¹: „Ï¿Q^µ|•àvÜôùÊÖÕ1h—‹ ^9cG¬@Ó7›P^mÅ cÄë@\%&vÁñÖˆƒRÃÄfäM¢™lʘöwX<,\
+L°«ÌßØEYZ+©./fÁ>²ŠŒËVŠ—t¼ÌÌ}KJ~92ZÒÊòFn/¾ƒÛçBŠNã¼;‹'1 É.Ç*R¸9·‘Õ-\º£0œ$ñ›ç;¥Èòcvà7³Ò"Ø•” %ëŠÖÁ˜k—ä–È6¨M?%Pv½Mópd<‹‚¡—w”‰¬ü™#q‰‹ž^çëEÕ>ç·)Å[òZHJs¼†‚hökŠ´Ó¸Tt][ÝçŸáþ]¤E*†‹jä!äFÐË}H=×d ƒÔ­hä0v~RC˜;p¬Kf­©
+]”0Cš©|wÙ¡‡FœVïi_†£;§µíЕu´‚‘âÃ9#møsåÒý|Q¤h€)@mN»)¢9KÖ²^@b”8ùgIcóûЫá‡~ž^åäYÖè^ÀN€Ædÿ;Aæ‚™gÃi´w‡)fñX]©„ÈÇÀÓ<+ZD[ᨾ¹"i:èàëHx&Êc££wYH
+6{9ÏÙJÄFaWGÑ 0CtˬCˆZ%o#o”Jr=+F
+_x³Iä’tF ãéÜ‹_0F>G}DO†¥ƒ"V™Ì^ ØŽ4B}€öª!`û»R#¦ŠK1`;MäùdÜÔxØNî!ãNä!*Q" ßùÞ¯®Íß$É_¨ó ¸>‹$’Â`ôÅn Ö/ÜP ‘ãð~ReÔ_ÝîÀ• „Hø¬åø WšÒ¯4êœ!̘r…
+÷[±ÎðôèËvxBz‘|
+Þ|úgšˆÃd»ûÑ]$+% ™5Yu•¸î°‡ÇªWžÁ‘¡7¯‚¢
+è‡u 2½%ÚÔô^ Î`¼ ½Xx^Þ¡zЈ8Ôâ–WÕ]ÀKD?á4ï4fx—©PÆù>¾3&0ÍC‡HUÇ3Âæ%*±âáí\˜DÙî!õ=Ga€ø yôq…1#ë;ã]I‹ÃâW**’zÂdjXBØk®8êÍcaѧlùÁ$Ê™üßÚ[te(‹ô€2±ö«‡OG¸«,;A
+<O¿·PÂMÌ1Ðy‹qÜ` Oá0€)Í÷?nm HBáôµs#“&ÆÆhQÊkØšóã9R†RÜ ˆ"L–Ì;.£ˆq ¬]9ÇÜ÷Åü&Ð|æò÷Ü;{gÝQ‹ûŸå¼oð­h/'­#Ä,èä: ¤ÐCê~&A†§Ú:h U†2Ǭ¤q[†&yg³«Á@ƨƒõ.»‹g«&ÒËÅ¿3XñI Eïl8yuåN¾ÎòV²¢{”ÁÖrø¥E^lÖq}¢ æ¯Èf¯˜Ð#qqæ™ÎepóÚNô°å  ,¨f=E$°.Ç.rß4
+GdJJ’Ánö‹ÿºØÁ˜ùºÌÇdnóè}
+à}!œ?\_!!8² …-J·¿wŽ–uñû! Çp츻/â§Ì·Ôq¾„Ú#zIÖ0Šh(óV¤©ŒP ×n8€Gœ£é%@ŽI¤në¨D&-/Œã
+/s Jhò(ëðÅœÕ$IþëÇ?QÑÓð˜fhM†˜Ñ×o=9#,ê@…  ¿Šœ*ÁšV€¶jº"''M>’Ü`PÂF`_¡ÂúŠ3*ÁÀ:Íøw¶ fÁ®w÷ ÄŒR¼„ÿQˆæ¬-(ùåÈXüarŸËzøÂÞ6€CV`{Ïm²¬ÌÈ e÷©r‘ÌrÞmˆ–?ÚíÕ0.x­EÇ®æà'S[†t ÿ˜…Œxfºè©võr!ky mâ\†wJðn ŸÄÊåáÈxCG¾™pÑ.³oÝvðã;‹*¿œßþýgdVŸág«d‹Ý)è '‡ìñÿ¶gŠ¾2
+b’ `D A1inØýãŽè]æ·&VÚ¼LŒ·â⻄n
+OÁ….gò,yÂB°z™Ó¡<Á/V1–ÜÕ`šp¢1Ÿ%8‡)c_fP¢ñ§wyˆÖkqÈ€†?µ­]¹”æ‹\·í¡¢Â°6ûPwû|^]&m^ˆŠXZ¨žä
+
+dËg¢ÙvÃmªcÊ
+›è¿€þ¾2–†=&”añ¦(ærÝ+èý;ßädõ¯ÿþøë¼S.Y/c,1ßûG¨šÃô6šÿÿóÃ2q{AVAÀGÕÕÄ\-—F/½€Òâ0 wp;W[ÖKùf:¹vø&HΖnS~VÍA_¯û&(aþ‰SžiƒŒÈ4Ó€Œd§¿ z-ÁûwêtœOFÓ
+¿¦ÈC—vˆ„_mÒ¹ZvIA3‰Ã $§0Õ"ÉÞ¢ÅãàÅÚ†hSøǯ!žÂd6ÅÿÙW%EHÈ}8 ,7ŒU°½´ù„W1%êþ{“–Ê0bcCø<ã6H_›º!³Ž•¤æcî«DRzI­+ë „Ci ËW %‹†ÖÕRlŽ}d,»CH9z‡T)õ‚nJÜ —!NE9$ZcXx¸yC:V³¦B/–Cx%¼Â™Ÿ™ˆøèûW•]d‚év½?€ÐKªORÊ»Ÿ@YLŸã“a,D¼°&‘:µb4§­órôo!¦pÁÚ/Dˆb©È!=zŸ~x¼¾Çb·g4K´Á´<”†d»!»Eyò™89IDSù/&‡¶T©ÝÔˆ;HÒ°”ç(Ç2’‚ÃÅD6\VÑ瘨$ ^–‡ @iäÑâ*"ž"¨Ú1Dø­„;t–¡Íñ°2X½_aènPäj›9Â<bK3"_SRËÐ^ºŽAQÀ›™ÒŠ-íñyNÜ„âÍ# ˆ%_…GH9º)­ y¼Ð1Ѐy‘e‚IP9(n+1•4‹á‡mû‘
+›°Å£š,ñ˜UJ7á®ÙúÏð;ˆ)dšx$þdªýQ&KUºCre~ £^‚­çœÎ`Ù† iÎ’æ–VÍÂ0Ñ•rõì ÃD0>‰­}ŽŠƒÙ]¸¼é¦TÈgS‰ñ
+W±£¬'[ƒ/dö«jÛ'ã/RFã”D¹ÖyÕbá,J{˜ÅmèËî£ K•‚n§üN«–°P f×+¡¬S(OTŠSm"8 7˜Á÷>u‹Üçt¹å•ØÏKÃÚ³Tl6ôÙÃa…—Å~6™$v K}h›ZÓæ¤ù«Ñi5škxJu¥A~9¶Jq㸠6–¸ûz&jË«‰<ù+·Ü Ca÷SàpQ$J%Ô†˜Û9ÚY’æ#×i`,£ÀüdŠ#Õ²Kb»Xƒæ¶Î«Ç
+.U°Å9ôœøY—ܸC˜åÉA¿Ý)›?ô:/­kôÒ¤®™Ýl%®4­ðêV®YÞ˜ \“[Ш(uÈ¡ÉÂîðûKÎ!2jI˜±qޤȔø"®é¤j‹?|¡VßÈ×JØ4Y"…æü
+>â¤!É»bèA¸2Ïß¾))äÕ¥_>/ ÷‹€ÄÈÌZ®"<Þ #$ªhÁ 4†áØÁ% CŸX”PÚ™b„
+:E¯)*2ØD¿j± s¯é ÔAZ|& S`n PN†m2]9øœýâó‘F…ÐX¦”e)ÉŽ.0“iJ“‡¹û@™u^më@°]”3S];5i$+Úœ‚e·™*K¿sÜ[Æðo(AúðÓ·8%¹.¢£ Xæiþ¨©á—7SÛ»©t44
+–/`ÅÏÄpÑ®‡sÐl¤«òÅ!/$Ô>'ô"|…Ê—¦uÄÇàn¨x·VK›ö ãl(À©M[Öy´ë;“”Ð2¶>®âKxc5­ð•^ÈÍÛÂ|ÖšRLò^¾
+Â!âÅ<}N_ ;†³S¨³ô įBãPÓz®‡sZíÖ”…Ž¯A•!Ù¯qÓ#ÄÀ}ÈïIůjZ†& ]>¼Ýïj.ù‰aÊɸ,°!r²GR5´2ÇIê`H *¾1®bô 4ètœžK>åùY¿
+sŠ` mÄU64MmÝ!âÄF1Ú±øN™r^çªdÌž†wÆ9øŠÂ]„ÍêÕk†ãu®¢tƒ 9zŒúWä ³æ½+[Ý@/û‰qØN ÈA;º÷ e.2g ï¼yûŠaƒåáèØ~×W].Xô6 _fð
+"vA‡e¹ñ+„-£ *äÈÙ•å'MåÓU0Co¨ rc~¾u¥³³|Ì-ž¥­¯Xü…í´qŒÿïÙ ®ú
+ªpC£DsŒ§sP„,• ¸Cꦓ¡
+!0ÀåÑO“õëðmò›8‘e;̱˜¢
+Û0×¹‰ƒ³îP\CV,“ÙÚ s1“J[O“FËrn7Ö%—}ö¼ìÃYŒB³…uŠ"ádâ6Úù"ç÷1\kQ:Úàßòêt>#tÑÀKϺÓç8ßûù²4OÊÀH×ó)"
+¤gpô†ÔFéù LÌx âgP_üÇ9§k¡ðÍ'‚ø—UÍ4y¤e èq·žâF ™Zë|˜P@
+Nšž&tà·åÞŒ¬‡Ø»v˜ÀÐv«€°]•Õ÷ïA•"Ä+™pf†`ÖáuÉWÆË© ×$ÍWÔ˜¶âyñ$¦ð)ˆÌ
+ú?Ä®?
+:ž–Æ6¿©’ŒX”®:(èÝL> .Ïsª6ƒ£J5˜
+¨·–Põip?ôâæ,IA³#ÿ˶\pFú Ýç_C!OhÓsF "Ûž”Vž*éièŸÍŠnú2Ð
+z¼Ò½ ¡¾–ÉO6Ê‘LÛ$œŸúP§Á¸oèÅÿýPÄZ°z ¤ææŽà^Ä_ø<ºÜ-ÎÈR1lBî”Až *Ôai•¶h ³Û®()ç@ä[Õ BÈ­¯}{\~œ””på‹)ùA0X˜yz…òL—QÂohŸl„—
+QÁ›J‚dЧØ!GèVHyøöæ‘4覴¹š0ãÛe>ñ°-ßGùà²À!Ä-<’ÓÝ$©5|ènM«Ú°l‹ÑÙ5~ƒ?Iñi–0$ÒhØÏäPp†&cµÑ:Ã,ÛŽW´¢kÓ¤³öFP&•a
+þŸ%†‡Ö$0W¼ˆ»Ù 0Éw.eß±ü¥¹¬Xq– ®@ôèÅë—r`Ÿdù4/¨¨r?;”õÉš†3K‚½$b5™ŠÍG±˜S¨HmÜ+ÎNè~Ê
+ž²Ð´óÄŸ0ž¥ú9 %°ñ2ÚÇœBÉ
+v¥" 釒uvC‰‰Ç×òpÎñÁ]‹è%à ¹À×9€Ùà(Æs²¢BΩ)ÁLIè‡#4¥Õþ”ÖÙx°ù÷ÍèÝÙÖ¸Ën5ÉRXà¢ã1@(¨Ý%†@òϨ¶?k v®uFe+؃·×ÕäXVr”wn
+M¼>××8q¬ûèÓâ 3Ö ŒÕõÉÒUÜ2Û_^)w /Ò§,
+q]Q§°”‡߇Ø/iˆH¥l²Lž©Âäbll­³DyS¤ºÇ¡d]…ÌgŽ‘K;©D¾‰uQ
+I»Çòi ÃÜ–UL¥iL´C¨ZTÚÕpðrtì`U„ñýreÄú#iÌõ˜˜òd~\»í!G@?âOdsoŒGÉ t$Û§¼¹íåÃ0ûí¸ƒsl8,T–’q‘Ù`iüUhFž5öwc=ùyF÷öç@.ŠŽ¦òàFõné¤qn[ïÎC‰EŠÐw‰w¡+¡Üæ±Å`ÜXšº_N·%¶pi[ý#­ðwnJqÖjÚHµ·Êù¦ò°È+žUŽ²xÎÝÛQÂÖC ´ÈsWC_ñ_,^?Tüúøçƒq|IÄY»Ž‰ÞÏhp gïÿ=µ™o­â‰
+,øýPD@1 qXýT KcÄÙ.À»}Óè*TT ÏYB* ¨<¢Vï^ò÷¼ ¦ç¢€˜oÛÚX3¼FºÌS˜-®ŸÌ1j;¤•$ÜLÖät%ë&Yµ¨üSy8#ÃÁ2};ääDŸðº
+ð&÷äaõø½§’Û»Eïý;•\¦p*¹MSE] ‘c=£‚d‡^&XØJÂU)Šç @W@H+fÌ"ÔÑš4?z"d¿i¼6²-Jå‘{&Q75áѹª¼ÒŸJþ$J_tây« v(ZãlòÌDBiïL Õ"#=†Í¦øaèª3ï2d†Á˜R»¸=`ÈyÞ
+H‰Œ—Kr„7ƒO;øÓ%Q)­³Í-R5+çþÛù ‡cw«kœEœØøõ"€uxíÅ>þÓR{Ôž#‡›µ­Ç£õ2ê„Ymµ÷QJ¿#Š×lÍR®Ö?þüã‚©Ôkä‘zËÕ?þÓÙ:ÕˆdÅJù5¨?Òˆ‘-×’£~|¾Y”^*‡²ò±ÕÒ°T›ÇBð}ýc%dpÍZ£Ö¤y·ž»Eøøø{îÔ’%wã/ÉÈGd¯ª¶µNK,ÐS n¿ 1Ú¨zA3{éžcx¡ÑæVñpÞÈbqýõÇåÇÉ"§1ÖÕyúV³sÕÎ;}üsÃø#<å^Zæ&m>tyô\·ô:úø5èi·Ï_œhž;ê#G¯¹x‰Rö~©—àÓZ=ºȆ§ä©u^oq­çÌ3ja#à•GižÄ3•O/{™/E§æ—±)ÍóSÙ§«^ŒÚ êhõ°:ø¦Ñ”eÚ#•n\gôž}Cò¹°lna~ÔÕù䡵çi:dÝL|]Å#UVaŸõ2 š{š—š7âã­ý{é]ºvÍ‹GMa£Ÿ{ÃÞ`í–3ëðäyX~Axjk£èW>!¹Ïè· hÑr
+¾²VÄ©”¯|œ¼Îuô64[¦qö‘‡ós`dB
+oOƒÂ…œ%²õ”ì¡×NÅUêNÇÕº068 äŠub•‰BpÎ7!\}cóöá:?l¯¼ˆ$ìzé?½±ôª$óì× d¼_½Â¶Ï7 
+rx‹^Óì›@¯ò®»K}·,՟װ·[ÑŽ‰Uyö^‡œ€ÁàêÛ’F™VTöi0ôlHvz¬Ä¡Žüºä³ÕèE®ïºòò=¥ÜoõZ‰'=äF”±hFå$àŠ³1O’EB8v‹=1T.'í’%]Á$àþ„ðò}/\õxùÁhéØ>] æÍ(µ‚j†²æ¬Ä© vCÇÉÄCY
+vº|Ú`¸1»¶¬9"HäÞ©@cºæ¶/¥ôå²
+N5—ÿ·äSœœ\s-8R ô2ú
+WñСM$·¼7âä‹lD ,Œs>ÆÒ_¬óæÜQÆ!‡ÜR•‹Ñ;öÂdƒ¿$œËÎNO˜gºFZÈß4&…ÏåNOš»¶BoûÂB_PhO=»AHCòêF/ìh:˜Çàž°ïÍlÃÿa:ñ®?Uo„""žGÓ- R^”èX¤§]Uö±/í½­]j_^§RäÙ4°˜)Ц›ÝÆHAšž½)öžÈø3*¿¶Ôkàβé¾~ØŸ«ä‡†\4$2Q¡!§5xB
+=òî~ÆÞ +3\¯ào#"á '^¨ãÐÒŽšø\FÚ†7+Úì²J‘ýÑ ˜n!¢G=jL-„\´è܉˜ÄµˆsœP·IG‘Ža‚q€I)¸ë®7<ËêÔ6MÖ`5JJç%ã„0øëJ E ó”H}ç*ªuãF>G ðSV´n‚y9ö@¹Û„(y178O. Ä(ªÑ±7,Œ¯9µ¡ˆ”EØ>M"`ÀÊ“;U,E\Ë3gMðÞð•/4{fM£®B`ÉL·¬ÛWê¤ù—Xx˜Ãºœ¨2蜑EIËi¾rz±ú`Vƒì'/Øeiˆä‚0XÀ“έR¿@V7½†¦0Œ/Þ¬3çWªÌïO
+æYB3i]¾@‚ç{¦Æ¯Ñ`IŒ©ï}2]ÙjÞrÕ¡rk*ªaìÚ½¤3]ªÉ«_!dæÑ‘‰Á¡÷V¯ Þœ÷—ºØr2êH+¦Ì¼D¿p'b—ÆŠríð'ÈÚŠ ã‘Ñî¼—u¨%ÜÇ\èó3¨™âfU”mq•>eŸy ëÈ›…~Èò[mû¦k]ôÛròLc“K(o@à!?ÌVOyäÅÒ>Ûž¡nü„ËÄù ìiÌ©uµÃkci3ºØqȺÚ 5²cõsõ¤³1G=ÆT:·q òÀ¹:Gƒì¡¨¸•@1Ýa2,Û¢y‰4Eræ‰+ÃÏ>­t¥D‡$øµäÍex®8s‡·2³õÈðæwXHÓçîp¢©–EiOêÈ•êµ
+D ¢6–çöF8вª
+ù1ë] ¯.|é…
+“aàkjÁGÄícÔ°3
+ôIÛ?÷RñL²”°>¿øy¹øôóo§2Ü /唑ÑýÊ
+!xÇ/Ûá–‰Wd—šß@˜B:îà Æ/ŒžéOÌM')¢¼XjHŠÖ:$jh;I‘·Š»ç4¯ï:¹©%û ÉÞ´ÊÉ|Ið³,ó½Ì•KL)OZþåzVš%œRù¥ì¸=*“”Èvɇbgd©·ñƒ^‚Œ™7ùÛº {r´À,`Ñø2Tþ½ýõŒ—I¶\E DWà=üp²oÆfÈ:™ýO¹!)üª,‡ ¶U/;)âÆÂ`ñp ùõ+m$» º×È8Dt[Fßžôú©Ä \´RKý‡ yÛýaÜgÄ;Æ%C½Q½è£+BÍ@ŠlSÄfKÁ|ºÎ#‡ˆeg\1Õ´cá.)5¤‘8Khª¼D‘“6O£<bü=7Q¤'©p¬|œ‘¿¶§®ì;…ßÓü¬ô¸Þ·r-ÛX„;ĸ­Eß‹^læŽKo^ôçüõ;¥•AÁÀ…ÙqÚ2ý¼•QÄh…îe,Ĺ$Ã(’Õ6µÔ´5‹äˆóÀF½õ÷UhåÏ2ö¨àÙZEÊVR¡ûÆüƒM§Dˆ¡ÏFåÔàà m¦ÇÙ±,‹PX™û W¶áî¥]{IÍèy—< _)«m«úeÇ‘&Ùl)±w^ \ ‚¨ ˆÐybÿßrPŒ×+ìF7s§:r–RÉ+p©WtÒkšbŒá eù2£„½¶£ˆÔXÄZë|'¯¡d¬Ã¬{¥R£È\*|þ•n!'—„ýA5€±¥K´’*À>ñ¯h‰ªû‘ ÊW
+zÄkpRÑšÌL
+7ܲ§=†±DcyÄŸ ÄÊ,î½
+_¨)æ k¢{/Ÿaa®šÞŸå”¨ë:;óþºµ7%YýÄúµœ3Ñ*ôª%üœÃœÄ)  ‘ !ÇЉ¤ø©¯cm <s—ë’K}vžkBF:¯3¶ˆ¯y%C_±è]E7l‰{Ž9&¯´t
+3}ÃÈ‚ÇЮ(é< ](l­^Â7½@/GÐ+…®{œ3q¿ràR댞·1Fè³S ÏÀ@3M9íR«¹ðôî/Å'qi¨C¸ì¹¼†ÀpÎÒ½C‡%+ ¡é¨öF•'aÙê`Kò¤3š2¶ïµ < _ø{TÔÚå8Èþ#Ø/rLÆݼ™€j¬>&‚®ò¿S‚éÎ
+p©7!&°'bE¸øyOŠˆ L¡8«Yz½)^
+‚‚:õÇÎÐ8ôõ"¯á×Ø2Ø]’·V‡÷/Åf+ßòñ!ìƒ]çˆBχ(c3‹f[°/ÛȆ@ABÜîtmW^#¼`‘ÑYþªHR‹ÓÌ/’DçÐæT®áúò¡éàq6¯E<ÿÐAZumg3z_pëlæß¡É<ó üsDß\rÞ‰¬–uŒñá;¬ˆŸ¡…Z—͈]°”¢ó<_…+„5ÎÉåL&ê—ƒ'jú.m”GŸ#—y4*!dH[£m0/>Ÿ)UùR/\¡$¸»f5ù‘2~K›ÕÕ÷ÁùŒ’Lð+Ò4
+\Ä0ÐvŸe
+¢îMÎ@?iqGsÒ2ô \½‚` ÙîïwzýJýÍb7Ž6ÇœáP¦¡;taÔB4tÕ9âò8›xD_±qµ¬$b,q¤Õ”‘p€ßÙ¦][C¶.]~/ù&÷n¯EÓ} u¢‘WxÂ{/Ôø <<ïû¡8a×
+qùZ‘ô*4CÕ(r/±Ìaì eQE¦Jn? 
+_`¢6¯,_ Ÿ-EŒó•·DÐð(e>Ô“rº‰jof@MâÿãOmÝBŒ•( à‚ \ËTˆ?ÄR9Û†‘…„‰Ô`ßÐB7Sm:ÃfVéÔ|¾C å-¯Z­.,(½Óû¥Ä—‚Å$úô|ɪI]xJ¸¾q'V¢k"øÔsÃßôç[ VÝ1Ñ¡õM“¹ ³/EÜÓ€ŽØ ¿b#²± Ù%ÐB +ZàÇFË¥Ÿ§drS˜\rŽ]%>Ða{²€$ŒŽçkqÃd¬ÄhÒ gˈ2ÑŒ^Â$2Ðzƒ³Ô”éëìµ? ÿ$À£ž§#0k1\÷-§ÑNh'¼°<êǦ–— ÊÀ! ü\à¿KbÞZý%UË› ŽÐâŽA3Ý~Ü]–0:z _J|)X‡“’Êzª¾ÃT0«Œ,|~ì]º6Òṯ/QÊ—R<%®ð±c(kð,<»Yk<t½åÓ/1©šêsd¢ÄWxû!}áîK¨›¨«‘žîúVÄ;„f(Þë49øÍë5g@‚íѵÅÕàSlŸN?D5H¦c¾;Ê%N¥ªs§ÄP :)Hü?"?ò)¡a…¶<š¼æå†)b0yt¥š3 Ìܱqw<t&f:Z¿L•á#ÌìrûmðVDœ‰
+É;6¢`VüƒÛbý~ Sp†ô‚¼:7E£H *º2ŠØ §Ð} ûAOW>~˜Y«Y ˆ Š"»ßJ|©Lûçú?çõ3|ôžM4z;“…Ð!>¯ÏÝaƬÞóŠ˜ä&'QÄn8
+'[Šï 6 Lë½½ë ”¯VgÉɈù$Èø ù3óíK1ÏȉܮÅdšÐØá°ï@cÜúPö<Áá,4Z_Õ—b µ5ÄfÇR4ÎÖ̳çKUZ¿ÒdËéh¢Ì
+—϶êÙ]0,«€qt¦‘ I:¨~;x H¸óvGN—² E¶ {¤Rû*ËÝ—±f>ãèC#/ÑeÜ?¶ÎD;ø#­\£¨a<[à0C–Ù\%ÚáNûL äÃ%1•ü2A¸:ŽZ/áÆ—&i5É`ê- 3kæø*Ñ­pËÌ×Ù2]"yç·Ý-VšDÁ;æ{…/DOÔ5mS^.ÙrÜ8]÷ðV ÃääØjšÊûŸú–_U1»Ë>ý9²Pd’"näó*¯vOE6B­?ë(!PñŒ+õØi4ÀöH¹Ö}$¼”ƒ#¨k+;ñ.4ò‡"W©`ñu"¡÷Ûû¿¸ô¨º¾þâ£BJ>¨Ú¿ ‹Iùhæ¿oj<ºãuQ$ÉÒ1Ÿ¹K8îBl˜Êr*Q>k²Ì•ål.‚K­×(šÚ ‰îû* )@+Z§:0š½¯ aÜšúƒL}•ÐDM,å
+:e›'TÁU)£ì™‚• °ÌF0Kž
+ÝÕåÝ¿ŽüÑÔó¢HQw¹Y‡Yº” QÏ|Wò]›þ¼9ÕSQ¼' ‚rS¨ÎØ‘ÔÖ{ HA§(5NÎÎEFg¢b*Ç–äá‚Qè
+.Ú艫*˜ËH (.ãòa ÆèN•ïÚ6$ƵÒ÷©p hƒÜÊ9–±A䃥™¤y¢òww|gû&”
+I{+ĈÈÆìm˜¤º8$ä(Á‘5½øÜâÇs#t‰e²m<ár–@ý"<.¢pW•¾K´³´¥|„ÉÃ"¢t€ÌZ<‰— ÂU‹-Å ŠrèDΤGIzàRè+iÁ¦Iè2![–öVLÆç\ž^1ˆpbÛA¯Šèi7bЙ(ùÄ䉯ð²WÃ{)ú}ST\0ƒæ²,áõi7nÔæMIåTc²TÏ7.®ƒ ÞI–-ÇNDcLZ?Pâ*‘òÎ¥²¼Ÿ!)i ÕÞç¥BM…Ä£ÞÁSfì
+œç­³°³ñâ5 ›<rÑH˜Y|*·=UܽÍ[ÍÐßvÉZŠe4·€)|Vvwq„q—ð#Z
+é;!ø¯O8ý'H0qåˆ ‡
+´Cç«lSúÍGÍÍdOE?oŠ˜ÄÌv‰¦zÛ¡h]žòÒ»"3i)†–×{xDän°Í‹K­d¥XñðÇE‡íŒy3Y;Ç~Tôv§í>(2×ïœ]œþÏEêhš¿íCgדœâXâ²+¹á€ê/š¸]Õë•(šÒJ`¾\ëp3ÉŸÓ6¿VÊsÕK
+n u¶ýJ"/rSEŒÇ>w¢âÒoSœ›:¨cÍâú:Ú&ªÏ©_
+Èz• ŸêqH0oUý;k+'œ›_0Ð^2[kžŠôÜ#y³ùÞï âm£ø0Ç¥N>c
+Ec
+dB³};S˜ÊºŒÕêÐià:v‡âð)ɘÄÞæô¥oìP±vzŸ¨·eõˆe+n…°ˆ( ’ûL/ŽSÒ‚áëãÁ¡,E<ðKÂN»š¤Uý´±
+óáŽ3á%ü?ÍŒ§¼K¾CÕ¯£A`ØoÆÅè¨ê똔ØàRÛv}À#ñæŠVT Ê]ºÁ$Æ©ñV1 oµµS&ÙéîÆľNSZÌürnb/®
+Á£`ùûöÞkhË,zäƒ×åô>•Óæ>n¢):pÁ|ùñþx•ÂÊF§Û¿Å›Ñ7x[·Ðá#d`ëAßy÷ÔzÏ«ó[:?1“'jö1¸pØŠ G²áÕî"E gÇj}'$¹;îˆ*^±Sƒ<Û*õÞ'%ÀaòJû:¶Îȳò¯,]G…à¢þa¼^r,¹(€®À{Ð
+Œü’̱<ô:<’÷?õ¹ÉȆëu>ØÔ‚ª£’dÄûaásÛìýF4}Z¾d«£Íƒ>ÙêCàßX/8ëgfÂÁ:é“=ãûñw"¨qýñÎÓ–ïH|¸Ú²)ùAåbý¥X;aì‡%Pº.~2¨˜ç†1Yß<‡ÄjiïÅú,šk>d Übõ«dßÊSãÖÚx‰èw–¬î³N ô‚)硱XêzjŽD (LÊÞ#q&êɳ X\2hO‰ÛëIçÀ.Tp®l’uÐz,~
+5“#¢Ï eCxli÷þLHò‚†ñÜEßNJ{ÖY¡õÛá•cš‹ë¶ÅÉ3 r&†ðˆË, ^³H«ÁT óK"“ÌV@B]'ÌÚ‘G¶gþeJtýºÔÁôÚÀ9ïÄÃ6i׺ X
+}°‚…æ%ÃsYj`C]ìÀ-Ó ¬;Ô^;YŸvŠ¹]âÓ QìЉoLÿKIѬ‹/6rm_¾Ã_[V«äŒ†ɇ tÚ“’<Þ.\~aµæ¼Y2a=ýêÇ)"’¹£-6žôW "DDd1HŸ;ª™MÒ±üg•Î…‚%›XH˜ó+õ
+ýõQƒ6îÔÔi+óÂ!ñ¬O–¼IU8¿“ $´ämJƒ¤ƒù®$ïS]“tüWÇ×ÊT¾À1¬ãý8hoD’ýþÕYÄ¡†Öí(o‚€ ?ä¬Pu ŠÜ÷‰¿qÆÐsTÀã&ü#0µµ=&ÈÉüKló¬Ùï˲=<ÿ´üûz£x/ºV"•áÈ›ç§Bö‚žb÷QPúlM}8Øn¯_FÄreâ3äðØÆ2Ù[à3þ®?%-÷ç+½ôK,PdWâꀰôYäá½–˜Ôj fÙ÷v¯ŒôÒ=ElñÝVE,tª‰HÞÜ—ÛÇônÏ«BN&ã¹ÇöªÏ¦í¾’æ§Æ%ÐÁ‚t`õ=ÚÊÐ@ûTßq«­@û |ì‹ä
+}ìú£ŸQ¥%ÑÕÓ·‡ö?´á£è¯oEKˆÀ"ΫƜ±î c/±£_JzRêy>~©8ë
+§H6ž§g¡/mÝë; 8¼»½«ï´axl†f|-Ùˆ¼ÏÜö¾Ž¢³h•h/ÕB+µÁ q{Z趉ËxV†;[…žM><¿”@€•Bâg¼†ŠÄm?ë¹'V0 £—Žœó­Þ¨‡Öñš…SÂ:Q‡XÀýxüc3PYâD:{Óþ¶åÍ"ÌÒ“¤žI´âˆÀõ[eTÐx@à¶ñÚœ” .«å‚J½£f8ûlò[‘OÈ–Æb¼~§1ø ¿­#¬ö—’Õ uìûóò"˦g¸@Ô}^þQ¤«kâÏŽlïLtA¥euᨴ³OÛ³ä'wEÐE¶å±êt‚[×x¯—gšèxÄΕéÏ“ÆQÍ—ã€|4ŠP™)7Æþâš—ÁU¼ÑÁZ¶
+2z;J]`¯£À˜wß³H{eW-Dâ¸`›GI':éÏR “‘—ç¿Â(ˆŠØfÉÊüÞ;cb…doô
+ƒ^nIS„쌫…³æòÅ--ÉÇáRbñÁ¤ÿ-<¿†)Ûx)ùóoÿúÛúÇ?¸O¢ÔÑ㋧1 §ºCwcSþý¥Èü íýºåÞ/B±ï­üEä…}ðÚvÍŠs ø+«ÜÆ€ÚiÕôpŠ(~ÀcPU´ Þo5ï½X ÈHCuÇÜ@%¦H`ôÌ£¼Üí#<F•¸»†ùö½Ž"ÃPç/¶9zV¡yCÐ|Të ‚1ÈTUl±T„ɧï
+xÊeF%Æßm•tŸÄÍ>Ö Ãybs>IµØºìõjá+DpÆ:ß%€ÒÀó~«$[Ì£t¼|¯èïE/!Ã}¨ ³bæ²Û+|o÷Öž³}IÓ³£<ðš´À*DLg‹ÑWïwö*ái¸âƒÝþÕb±÷¸+ö£ž~a& V¿Zð Z¼D» $ þRò¢A3gé0I$Þ}¬% žˆ/iÇ¿ï".Î…È ûQE€å€5jöGJì0Þ°*ýQË-êàˆä\-þ q÷ó(òf@Øàû·Ò*:}€ÉÀ\ëYE¤öŒµÐnJvöô£¥Ìž[´•åÕ®»" KKBŬhiŽÀPuБ
+†#D2ÏNÖñ£=®ÒïÐdžù擬®ùfE?ÀþH»FÞu]ßLð,a×ÙJ¬PŸ!ÌÖ÷v}+˜¬LòÞ¯£>‹â((d"œÜ%ìÑ‚{¯Gˆ½¯ø×ùn@!Íñ¯ñàîž?·Hÿ£Ÿ–ºêÕ¿5°°`RŸAR€Ýƒ¶^ƒtaÏ8Ÿ‚ÿN*`X‚XnFŽ‡žÆµ™J¡Jt¸ÐKËÎ’="ŒÇ¦i(ѹ>ßÀŠ7±Ÿ:'±“Ô†é÷*ºüŽ?bïïïä}uêÛY!.´2ÜÜÌç*%ž18‹tK9ë¤ÿ±oÿ´–K‰LÜ8çË×õdرÇBÇU"ó{‘ý9A£%[ÜL Æ¿ÉÅÇæ
+íy™9,I
+V|d„“ä`ÚòxEaƒÈÀÂÂ0Gw {9Ћ‰Þž”øFɹÎÜ …<"#¹ÆOƒU{âša,mb0¥®ñH'E’ÓˆCf
+z¦“òN¬L¡œÁ´}ÅAŸŽ;gÜ뵕q±v²Àqz¼<FkaÅy.¢ÜYýòkWú…–Âz¬…Rœ<ÞÐØga(‡À~45 +ã‚G7y[â“AÍ!¡‰?«~”ŒÈ"
+÷¤·Ì$,ŽßÒß_á.¦ÂÚÉÜx;‘IÔŸ¢aÂw€åÄÕ̤
+}Œsç Í_… æPr«Gs[-ñt‘'sÛ°#¼+béß'«T´*øV\@AÍ7
+OÈ*Î,«z:jùzs(LØ.óé;MF¬¿!
+è˜aÑAÌŒTv5b„EA‡ŸÄèOF9Øb®¸JÑ‘ÜÖÎP¬ ež\! õ_A„¯S;A̤àĈ•¶WË–Qúî'±”X€T”ÌÖ>@yteN¯LÅÄÁB¾¯ ‡Â± tp]Ä4²
+<£ì… `ª±—Å¢kh7œ¡_A¹!M¤Ä-&€+œy1öÑ ²“Ë
+k‰ ÐVôKì“2õž2R’]ùù
+Y'áÙ,Ôtõÿô¬`WŽ ‹ —‘Ícx’9ÏÓh6äõIýDZ2´:dØsʨµc}OO}ZGÝAUl9R—³xoÃsaî #><Š(c•P¼Ý¨{màzªÈùlÄëTa S,£“kŽº@ÖQ׎¾s›ÛmîÓwxÕ}ŠoÕ¹ïÂ¥ÆÜ)m¡`AŒ$ƒß³ÃIJˆ*KMñ‹­^%É Zd—±Üœ@­+µB*SÇa}“b öÿéLëT(E· ‚—° ô%JWÈw;JÜÜy‡8ûá;JSI¯WÿU•ßáa-a+%«Am.È”`³¹çñ'úÅÁ3ÖcbÔˆ$ *„¸RÜøUPÄq’¿ˆ7m¶ë4Ü
+?ÖøúhâùŠ"\›i[PKµ$ÌŒcµŽ3ê=EŸ@‚pOd?+mìiÄ@¼±G{‹ݤîÜ£z;ÐW´ 7ÑL*vf¹°ß§KÞ§æ¼dû 1ù6tØúàb,QæËÑ #š<'³S
+>áÞoörF7ÈB)$Ü1 ^(ðè 0¬m›2QûYT¥j%dXål`i‡àý˘ò!iý¡¼üͯ¯Ô‹˜+®(¡•Êðí½Æì7ÆE®¨Ùg˜U
+“™Å~îÔ;.& >©aö¤Ù6u%|WTd[ÃÁÞæ•®nžþZ㹈T/½xZº*°õ}Z€UØF’Þ´™ŠgÜ~'¨Ç䇿aJD®ü¿¹Àíu:‹ó*å¡(¬ež6w¦0à»p^Ìt²ÖŸ@—ITJ™ôµUrÜìªVGìïX{:¨ÓÇô֛Dž—1³Ã÷â2Ñ¢){ŒU\+H`½xë¦Ä ¿Üzâ) ûËDt|óÃw&£K¯8.9OñNŒ¸ŒjRCq;‰!ØÅ D¾ÌX“ìAøvT~Wàœ}Š7\æ¨Á†"Xí;ü91Dî[{ºö ßÊ‚0ÿ,.RdÁ4Éã3‚­"Œ×··3hjŒzÐçmÝO´ñg ÓS lf¾âëm0N e:B?’u«@“CîuA˜c<=‘6÷AvÐv–&ÔËKÓ±(#*e$ß¾¨’èÄ`Í‚»¨8lCfp'ÚDÖ\›Ó ’&wãg†# k.Ä‹ØÃ[hßi “ŒYîÙ-
+(Å­‚ûþŒß0È1.†çWªh³AØôEAqÜRæ²ÞUä§È¡ž˜ò dj’p‡PË?s¡Ð¦Î ãòQÃ.aªÓÂÂÈPHMG³d0&0£ÚÈ LŒðdvÄ|¦K÷ïp4þ­cÖv×¹1œ+ªámA‚'Ÿ]F¡-vh­wÌÝ‚ÁóeœF#E}ÃYL¥CHñª?Nhš…ü'”;ìÆð(ÿÁKb%Ù0¸&
+€±`Äœ1´!‰bÀáB!Py¼:Ñ+:Çà øa¾Ñr4<Þ¢t¢m(îRôYSH¢%¦iÉ “ú2ÌD?†ñ¥—³1ÒCt}·ž&‰Æ‚DHÄ †<-ª¼B¾[4Ï"‡è+xø'`­ðº°ÔA)N}È
+—$~äa6Ò'Чiýa ÞHàbZõû˜ *ô
+ZïRƒ˜gèé¤8* c*S”ÑUe>‰ƒÈÊ#ñá;pÊžùMrýCö1mÈY0 ÜÂjôâ½bÑÉ4ˆt*~’ºFßéêðGA*iC¶~VêyâWÂØEF¿;ÔÁIË~{;Ó^p„¸îb~þi%n ÃþÝNûæ§ݺbÄ,QÉ,`ûp|­+vÝêÏ#&Y½…Âi¨ã»M3æ¶yX’ÁDµ«EWJ±ô0¢%™±Ô*ËXg‰h'êÈmšŽä´ÂJ£Y:‰£ @-ã×Ò¯5üdƒÒ­Ì
+âTÊ8@t”6*f=30‘Çï*†YEÙ3÷¨•9+rÒÙ 5è$mvÏtã@!ñº ÉD¬ ¯pDTÄZv1¯¿Çj„²îƒ)¥ýUÉð.tBÀ IÉ­–îÒ-¥íü8zÜ/è;l"¹vñî*Á™a+­‚D+8l tqO4wãŸiO«ÈûÂ’>OÕÏ ?©WC§Ÿ¿smùí6÷Ñ9¼ê6×Úœ&ýRàäS£¾ac´[Áw+ŒDJ Pé‡Ïý|
+JÆW³¯PF¤©®¦¶˜çRæ,Û¥{Zè/cJ¡wæëÖžnM,jÝó¦ÚÂ0íIΞ;*»ã%ÚÍBÏü°úQXM­5¶N#æ7âks‡·Öá\‘ÇRAé· Ì®|4t¹²|©‘»:ºCXŠ‚¡#Ám#ÖTØÖÿN_©Q_=‘øé·Å6ˆWWü$5û-Èèì4é,Ùæ†<WÄŽùm:\¥‡Ã´=ðO}”êì"!xñœÕ`œ×Ž®@ŠA(ª C£œ­7½¡Æ…y·ëx!cO Šm¦ ”Ãx¾·‚ꦹҟ­]Û†3>D¥¦,Ö²Q Ax€6]È9 ¦x×ÅAk—„à™ÇýÄ!í¿Ú·Ë3µÃKIñ:©UBFþ™…³B‚€Vcx&ËÔE|»ç©a¥²î³W§j³1‘Ä"ÞéÕa0\#vV:Ø 7Q(,ª#KJ\­½fž¥aqÈ »ûxg»ö /¦°®OŸúÜ "ÉÔìƒ
+‡I™Yf1{¡®/³Œ“íˆÌíQ¾]ËòŸKÁ¶ñ»rþ ’‡xÈ~²«ñ&øhÊAgKBÇÅsÍm¿‰ç¾Ý¯·OboE £®×ÁÄay°KÙü3•|Hl“•¸Ì.ê
+¥PŒ¶!ÌU!MÒ¼¾î^÷ϯæÐq/Ê àõè°ÉÿZÑ+oöEe²”Ô´‡ƒÌBy†¶žS}›)\›·¬ŽFˆN¿ÆT¹kø öZd¬êjÌï Ô) ˆ¶ªa‘¼Ô lÒ‡\/ácÁRNÝñâÔµÑüèv îCœ0kl²× 77‡áàuìvÜAä¾
+–´`,zW«ŽÆ
+κ—¨5‡Z`”:NBj”Yü1‰ID–Óð£üa¥YS"œ¥u„<¹‘}ÔíÁòØm(Ä/ÃrʸZµ9±V9N^ÀD ‚CdhY$òì†@&•Ù¯(b¿6‚–)æŒXâÊJu…±Ù{®cQº‚6ʽaýlîfrYÖ
+nõ
+‹…!õt Eæ'H/Ë–o‘(¨3“ ] ]g™},`ßõAlÑün«í
+%Õ¡Òäki”J©ŽêªŽ2 Ò±›YhŒcU™/ƒÐ¨O-µ¿€¨2xc-cV×5¨ð¢&ꊛO Ç fH—)ëž÷¡MäÏ8/ž5º0þ¢Ax ƒÄ8—ëQâ(úÏpÖ/ç¾kÊ,‹
+R-4žOÍýpª@XÂî&C)Šêñ±±O’LZzÊOZëo/  ß&ÃÇYáUè«x±Õ­Ÿ&BžäʳÅ÷¹é®‚¬‡•)@õL°4õ£!MIoä€PK¬(ñlÚ¦~¤>áYõZä
+ö™ôàGe$UšÅ¯S®‡ü#ÈPé¸s‚éc´$ì]w´½›ClÂÿù«—“–˜D’ ¶x!ÕÚÄ^)îÌAÏ "Í°¦àHƒ÷aL &š?¬¡¨Bt¹Ch;Vµ{—0òãÒ(ñš¸ÏôCbïèð—C$.E¹'} V]°(ñ…&¹b0Øê4›ÖBÏÂ)nÊM¶É¥B!Šá Yj(…Ö;M®T…[…bb` }ÈAÇRY7ÊÉ‹öQª$#‹s,aã?éí@õ¥RùN†Òí
+Ÿ@ßûþ8bˆ~¬±… ÚºŸ%—Ëôc¬R/âX •yp÷»ÆμAúqE(¶D£³zóWÒ ýD_ìœGN,yË5LE‡o©¹ìøñ;Ìã©£A<:f” F@ps]™…¥Ï23|Íâ¯Ð~«ž"´d©5[<×^†ºËBÐCߢÂbâ)_©¿O!s.Å‚4R“þ±›Zc~¤ò]f ßÑ|iæ愉Š§Ï#H~TaŒ}Vš
+m{{°²µÉ&1++%;¸È_­x?ó‡›Zæ@òLЉ•9Cž¦ôϯè0ïH&ý.ºÅ» Ã
+ÞN{ óÄ Çåz9¿ŒùAAÀ¸k*ë~ÞçÆ@ul ®ÿ®îÁ¤†„Š®÷SÝÇ‚ìÉp'UJ>É24Gñ¶°©§
+äs3Ô÷)åÃA&1fi‹ ';@öQI·Cþ Í|ü΄ÉF®ø?ãe’GCÑøuça­^úÞÚ÷ßöû YÝ•‰²ÑCX†˜$ðñ‡qÆŃW™”°*:¸…}ìûQFjY’m4O«Í9cˆ
+¶c|öÚÚ1‰!BÉy›1F$iu•È=Ârè@rRÎç¯ÆyWZ¢¹´–oÖ\Öï÷7Vt-rûq öî}ïdö
+X)òÁHûS×æŒ5.Š Åä^‹)aUÁ2f*WwT—’õ©ëÈs®ÀqnóŠ>÷Mw W- äÎÄþ"Ï"£@éMÉË >ÏÞÁ²r#³yD°Öz¦D“yÜç nEwå¿}Ìa‹¯î#¶ˆ¨D¤šP%Í÷<ùÇ­ÁW`ßÇ”o*7¤¥2ˆ³s›(¸=*2’È Q¸Ç´…çZ
+Ø>Áà!Î7çdˆû©‚, z‘Z`%Ü âG´ÛwNQørŒ(·:Ì«éctŒ|Ëk{:ÚÆFÐ-Зíc(„EãÞÓÕÐûÓ-¾·ðvÎ}ˆ1»À­sJyEg'2Û 'Üzê
+ ž>û˜Æ5¶Ë#F'`PpzSR[ìÌd´ó©¿CY zî)?ŦqÄ9ŽJZö@Züã…©£*Ç_<TÒÈ/Ð!Ô:¶àoDE Û×€!c9Igl‚‘šlß¾9oEƒèô±ûJŽµ$Ø5 ¤€DŠMHÕ5Í&b\ÅèÃÓ :4Kø€^½C!Y-PÁú¹‹ðÇ;[.ä/¶Ú…<Ä ã èC[lG\ãÉ–Žõw‹<ÞLfˆ¹1«7ßÖô*Ê’'›ùdóc Ï" Y~r†ÍÒXj®ƒEŸ­ÚûA4dŽ×€{ƵdsöV¶sÊ8øç°^tƒâo÷m:û­v¤\À¸rU8TXxmÞnŸ¨Qž‘w§Ê`Vx$Ì…å+Ü“íD&f`%’[‚ .G[PÝŸúb·žÚÓmX!f™úMC•iÐÖ,]ùcEh:ô1ÅÕ)RIbž §ÉK1­«Kl.­Íu/3¨†·0Ô^W§ˆQ+lÌñüUX±'<„JšØÙ:[çz¶©ðNž«¤6E3"•URà>ÞYô¶£)1A‚¼·q/ùé— „º˜Ê«$ÀÏQò~7=Áíð$=J%Q¹„:vIkl?I$O+!ue>Oy!±_6„žf’ªÒaº×lÖdŒ*Ÿùî:§/æýyº)¡A:äTžë-Ђó&›X½ý±qoø6›gÐôX¸kiVRBƒfI”4aïÏ` N¼ÁÜÈÐ/ðS\ë9çÕPÂ
+Cðñ©Y¶!*p½ÂÂtI&éf(êru`„õªÇiádÄú#®½&&,Eß`æjSñ£WE8UÀ»<Ç36
+:5×(Ø
+tÀW†ª¥u¢ %±@œÈÐ8=º&Aj`àIJaÃzñ’ %eÔ”-R^‚£¤¬Å8>ì[ÅdѸä{E*Éò ü‰;Fõá.|ªÕ½7² ü#–]|p/ÂaÈÙ¢Àd¯bƒçcø‰§õéÒWFÙKyÎËï9P DE2ŽazE6†.„Î…_cà}˜÷è`âš]ô½¦@·äš¿Dñ3R dérÄáU?ÞkÐÊÊS w¬K™s“-u? Mb.4%Åh%¸lT—®¤6ý HMmb˜xÄíÿ!P)LèËláCüŽŒpi^É‚;4DK¹NÙ=Ê3MñÄÉ£r¼L´0ö…ö‚çÆNíâBNâí1p:h—Õª$:¶achçÝ“)[ûcõ8Èzó¥»Év@×H@¶­¹ÝaN¦y…Ôö¡$vŒcõhXƒ‘CFÕí]€²@™êE]%$&dUtaHè¡bÂZöË#i/h§ŸŒŽï«D6…¾ƒÍCM,šÜZÞòw)Y‹{cÿðVºsα\ÐeÊê¾ Ü€rT.˜W šäcáóòæºDS~.¸JX:$ɦEQ{ C
+=aí1»Ôvwè.¢‰s3–‰L†SôüU2à<pÝŒ;?íSp“Z¬—¤U”Q‡šÓXv†Kc`ßìi±!ØSÁn?]´Ü‘È{•ty=S×°ÔȬ_—ü¯§_JÖ@“R ÷ž)»ÇTvz‚/ô7/‘KB® ‘.ÿÓPFÒ*ô äêó?aƒWòg‹”‡ÿxÔ–ÅZ`z‘+û¾-ăíYÝ„Ÿ‡ë‘­€’-²h)n¸É+¹çðúGìÙ3Û`:aÄ2³‹ŠlÓì¤Î :6º ‚iȨå(hÕx&ÿX\;äTÇÀ°ìò]Œ¦Ì‡CÇØ`˜”/ÓÕppüwZ—
+Ú ¶ý ɉj‹e%ŠìïpÕtüœÊÙÎSÛ¢ð׌6Çv‰Þ* R ¶¾¯ Çe¦$%ØO{8ì…í(|}Aãµæç›ÓDþáaBjè2>w²ºÇÉÑå’y”ž¥’6¦ZUžoj²µ(¯˜VÛFP²ðìMÙ
+•W%ëÍü@†ñ<¬ÆŒ»Ë3è“ ØìéFdgè}v'õÕÀ?¥2’´ß\Üãð…†WQpŠÛ$\‰Þ&åò%öCô2pè4RHIGíÙ@ºëù2ŽáÂt}{g[è,xëûœ
+6y¯èD®<2s¡òÿìø;
+bf™þe¼<’#Ëa z]EAºµ¶s‹Ù¶î¿— ÙªB…jÕ¦ðéH8KqO r“uœ9Ž£üG¶ WâA½²ƒ@š†¿Û.)<o øÈ2.:ó$]èúúˆ‹~f·˜4~¯<62ÂÓ*Q‘atðë2!eÄD•Öö©0?IK†mÓ%s‡h³R9–ë³*X…dëÁ…£²hæ 0™1â¶`§îéãè4YéÐ%>¦“UŒs³74»ñå@éJò’ Œ´ý€åë€nÙÕÁ3 ØJŸ>=L `—ÀÌYƒ¤0^,ãhg#ôŽó/ùšs'Oí„Ôfõeè.þ1Ÿ^ƒïÂdµˆ",ÉCÁEeXT~ecS:ä‹gÅñ¨ÛËÛiÖçyÀ† ÆD—m_ >«r‡Z:"Á_ÉT¼‹¼*‰dvg’F`j¾wjêzõššÊð3Y@9¹-UÞLe¬;¤h OêAJøä_8¬ Ð'‹&Ð\j‘cQ¿ˆSècs¢k1ñ(NUø&,å2ÁÍ„|ƒ—`¯¸³qæKeVbÊOÄ;[ ŽÊc=[×!âSÚà¼kƧ!+‘ê
+|}ÞXòÀš³y¨L"„p­Ã’ÔežÃ]XT4éƒÀ<SÑß/„™¦3ZÙŒñ\ÄíÛ\†²˜b„ø
+¦‘Ÿ‰o
+yòʺh̆|#ò­…oäá™d”ÝTRœÕ«
+c¾í²|M
+ÄÛðI —‰]Bȹ8ýœ²ùpmUQ+¡•F´"ŽWÉLɪŒ+I–›É+,y°Ð{«ç"T´AÇ)góe°VrI9]ÎÄðð´¹V¿9Áø`²å‚gÊHáò΄Μv«Äiè±öZk¯3ÑZX’®³á‚3C!£làðžÂ)ƒ½JPòŠž~ý äJ¦”9,‡žž‹8gÀ³É¢y ˆàL8|̃¸&eR/é˜]F™e®S+™oøˆ~(ƒõ³e»ëóÜ*÷áPþO€
+H‰„—=Žm¹ „Wà=tâô€”HJŠÇûp`À™S¯ß_Q§ÛðÜÜÀ`®êHü)É8þäÙ¹Wn÷•_sù³ÓìXYU_qÆsl ±Gø×?þò׿ µf-¡ŽÏsQ€„Zkô552
+×Elüω=¹÷û9ä-y¼æ´ù5“ÄšüxæáÞ8ó!ÊΨZ D>®ô`&¯ç'â:r¸‡¸ã5:ú¾‡0Xa‹íœ±¼¸f¯µ'#~[WùQü
+ŸE”Åã܇„âX¨æ w›q¡¸Õ€Ñ!͇!Æ´¶Äž1”æ}ôw6åÞHËŸw¦{Ÿí±LguÄzH¸ü8‘c\´b‰ÀB$|b@†‹ØY}œï ñN¡°¶ˆë^Ofâ±óÙÏÌê@a–5Ã(«Ÿ1>ˆ¿PðÍÄÏá&Þ“[ÛŽJ9çAª/¡uÚ|¶ÁìÊìLÈ7¦è2•ú¸œTP-çZäíóš™á
+ÕS\¨ç@uL‰š9AüFÞЪÞÌàw\Ç
+}­ÏrIœm,yåæ•3þQGÊ¢æ\ß’ØÊŠªÒAHv„P{{_ª¦žP–pZ¨2ƒr¨7_…ÎÑ
+Mà/bÈ8å à ÐúûP”õCµßÄÑ•è?êÈÄÀû£p >»ÑLju  ·—µ¸¡§ ‚Îj³Û tè¡!ó۴ݺíIëš—¹ú:Y£”åÃ̸Z™2p¿­;!iB ÁbäÚˆh¾fwáóžÞ‡cÊnå2ÏýŠ®‚8Þ¯o³:³Ø›t`È°
+Þj„š(ˆ$Ÿ{DqÎO º›Ö’B€@|CˆYé V×aPˆ¿Z¤ìéÇr Ö³ÈoB†¦²8DZ¸dH1¹½/!ºzéØzC‹4Ò.8Üš5cžâvJàiK;r
+ÝS(³…¦P›Dd·%ΑŸˆ~Àa&ðû„ñë—{Ì0TúHa¡hw´CQ5Ú1TâuÀȄΞ9hÔ ]€|ŽXsárP~èuB)‡€Šâ
+šÇ!~?"ØQ,¥ìãœ=€da*­»òåÖ'Š<n­²T]ÌAbdñUä\:þ·nߣ¨ï1ŸÀõ‡>âKÎÚì…éã—èbÈ.mˆ&/B^~E„¶¿÷þCêþýJÔà
+B,rhÅÄvœéY»ñ(µ-Ú&µ5gßR%RÑpp‚IÜ{¥ÚZE5—´Ï¼"ŸQ͸¢ICt–FM¿{÷”˜Ý½bš„˜YÁF6©¬¸Ùh3¬°=ò#ª˜âãV+ìuÙjï$b‚icY6Jì
+¡¯¾‡úЮÆÚÙt˜¯âÁÿµÀJ¨\
+G„0ÕbÛ´;智·^ª¢îžbhð04Ã6B<߇êÎ_µøšá‚ uK¦m¿÷h .ïFÐG¬5{§<†9!4‰¿/©>Ì¡°5@x6K½„ú F‚I‰”œ<\6OØ"¡šŠ.CŠؤ
+ÊW£v JT£¯Éq>·Ù2Þçý™Q·l„€™+ÿ{=ùµ¬nÓ¶Ú¢&®™IØúêôö±¼ #¯#—R—µjÄø±ŸÊÓ$Fó>jY*®»°‚ªzH(¬t(\«u ,j.Ÿˆ0Í#ë'ÿ{8$p*Iú†"Ah; ³
+âKZ¬JZÌò_ÿa»Lp%Çq(x£‚µKçé;Ìù'‚tf¡ÒÆ
+Y¢œ“Àú²Î¥¥†bÇ;ÍÆ”ùh(ÎË#“ô¸W™žqÄ1Þü‰–ºý9Á "³CÃÔ<8r¬à“yëÒï©i’ùšFv8$äŸãUàônOáœ/ˆ¸J£wy!8¸cæ}¬ÓƒA}uIUå†òŽ6™­£¾ ¸$‰? 8ûÛ]ÿ` ¬?'¼5ôÚíÒF3‘ 9 òÝ{D/ä8¢îe¯q¦Hûò@ÄN¸xô™$Bº»^×!XÁc}ÇK7Æ~©À* TÐË‚VŶ_¶*÷¾k÷@¡)xÃŒˆ¦ ¡n==îW"ßPlªk“T5\‹¢,ézA|Nî°ázÈÎs Q‘Ðø碊!|$CR‡âxÇ‚ºè;&çm²ó¹ÈpJ,eßw
+TPg¦%Õ›ŠëÆÜä:1ã¬cŠÑ
+i„rÜÉA[•@@‡Æ
+‹¸¿;5ã"("ÇL'
+t,Ciz©Ç•
+÷QþœœKžÌÖóEsâë!B•·„n×z[Fã†ðÚ$дñµñµ´7
+U’:Jn¸¤c0<Vƒäƒ¨?
+°iÈd˱
+M‚²–)ã)yçaü¨pùqµÏGÝÌÁ¥1ñ‰€a£ŸdôDÌ¢'‚º$À  Ÿ}Œ5bèk}}Ø9^ Wc•xG À²ÁEP
+jA"`C˜”ˆr&bÁ%÷Nq<æ–:M)ñx<¸Š3¥9
+añÉGßÆ2¿mÂ.jwÅS«q%âôD2­šbåNJ;ÕØzþAU<~º÷Ü Qíƒ#Lçögœnš2þÁ+ ÚP¹¿rˆI¦\iPÖf“ㇱŒØ׳ØÜÐb7ýN¾‘óVcvwºü–³kšQ¯(?Òb9è
+n|Ds„à›¾Ÿ(hÏÄÄX¶¦W±…B
+i!lçD'°n”‹L›
+k@‡Tn±D×Ûñ†Àò&ÍÝ—1—omªhûA-’l¬Ó¤>ÏÂ$wˆ«äN\Gâõ9GîĹD ¸ç»“í/
+GÞïó”ø¹áEŠ?öh;Ùl‡<Í:\JÐ÷ýØ;΃ÃíÕ@›“žÙAªñ$H]H†M
+¼šë™SYoÍú‹ {{TXr]ŸfúE‘÷øÓ\YÌñÓ¯ç`éºH:(2L­fð_›Âr_¤ö
+FgzlÝI–B UÆüÿ"òÉ51Ž¤ùq­·ul§oú"ü©·XS¦y"
+rãØÅüm®PÈ Þa²ëÌLŒ0§)ñæ=Æ‚jÇjé’¹Z r³…ÿ¾ Ý.
+¢¬ªµÊÔUnŽu /±édÀ:‚6ðdÊ ˆÝK¼îpâ8ÆŽÏNišräy”=%ykElUƒÊ‡R‚@á}ÝÖ¤B3ØšœvyXÂb—ÓÔ³¿aݣͰX=*ÂyX. fÛfEdؤê·âòÑÏ[·À¯fhô/"IæÇò=×ÍhGg·JaqÌûåxõк"ú ß}PnÃþ@67'p`âï¯ûÄÇwc$‡|ÜL+˜–úk€ûDd4àUô‹B‡*<ÖÁn µœZ^K¯Ðv*ÌI}7ȱFómth§óóC…œôKœî3ˆ L†ÓŽóWÙ
+BçcQëÿY/—[* ÿ•=LD;u¿èèÄ3t¤: A%ƒ$N„ü{ŸgUí£t7d¢’ö÷v]V­õ^htMák„m|Ê‚EÜÏŒ÷µŒŽx/$¼VOŽ`„ÐülŒx¸oœj NÃIM9¬7€Ón´z2N”y=ü«UàÕoZ‰+kÎqö%ù«N²1øÓ¤3!œX’OÛ‡³£ä!:-,/z ½ã4‹OLL«2–~NÛyÃu <ˆŒ§ H5iH1;ëÏïõ81œDXcÖWÖX_E‰?Î’'ÜB@§‰UzMH¼Á.é ð ÛŸä•× U‘ŒPvlôUw2Œƒ§ÛAûlÝÔÌõ{µ™d(ÐXWUÞAÁîS'1 ¬3Pe²è1¡ÔÞµKq•æ[›÷†¥>OÍÇ¡[Ûv̾áþ|¬"ýƒ8ÍÀšo½ ¯ÈýD%ú8µâ5دx
+ˆ°ˆ¨ôúçf´Æ’NÊy0Ý1¨QG‰u†Fà DZs9géx
+ïÅEïãm
+¿Î0€‚¬k ’¼÷õ§{ CCl<ò°Û´h ©VÒ"·v#Ï
+n¡ t°"!(û¯Ý B­¦škÈß ˪ ˜¶bƒ;ÌÙ®Ø ÷ZeÔA=¦b‰ošzy2Ý[Ä3e¾È&TðÎ:ø‰dmhÈÚ›<¨ñP¸GMc¼E¼‘Þã^_£à+ôˆB³êñM›G'SŠµì ‰
+ï¸ýà½Cÿ ÃD!n½IŠhËd$jQØÚe8£¸Cƒ@h Ø™þa,NåH¹‰ç<G¸B·ô³¶éÚr#é?ùç7å_éñáþ÷Ïñßùw~¤—òHñÿ¿ýr[A²…ÕdKf/Ýáýþ¢²þ¡7[ 8D6M¥ð‡®ã* fxE²¯@ðgèjØ5çRË'"m ‰ëåË‹² „}Ó0Qôâ0 ^FØ,Š¨›tˆø
+wTëU8•"ºÓÈá!q)‡ò–!.åv×çG3^µSP±!¯È¬ Fœ6­`gø¢mx^’ãL› à€•\Ìú°Rµ¿<U²÷ñ¢üñDµ•ÑÝcÖƒîWð'!"`@ЊŠ^†Œ•­T2ǬŒázOeè\ŽéÛÛ‰f‚‹Î ´P"^–ÒñaËyÑ\k $R¡ d®×%ûшëGnÏmÕAÅÎÃÉ2‰ÍAeE¬(!ˆ¬òBëx›»ÁàµÙ€nZŸ7H¸cQ õ·ê¢J1ãŠ(;=È %w'nXÎY’=à0U ðÆYÄ2"<wÚÑ`¹æfëP‰ p*ÁCùšl['m}ˆtï´Ù]âÃ/ðÆd:î”é6ºhÅì¯çk³©ìT®Íöæ ÓW¢ah­° Ý {óÅÞFx¹J:ÔÂDrH/wù£‚Õyíb+W6ª6'4ËÆFæI7Rx¢BüÚ]Ësel•“»™æç B¤—* –Ÿqm”³Yü#¶ƒˆ8S jááYWÍSuh`ÕÝcànzM²¤©ƒä°¢áÀe:±V'6ÓÃü
+¢9ôO\lÃÓ™Þú¬t_s×ÆOZAWãùMŽì„ØíK˜¸v~¢Ï¨sÆÙ°N>VG;ìM›+ÇQ€W°yxvÝ¥j!ëÅas¹æH3íPxÒ^Š ” ;(‚I› J±ŽÌsP§3ß¹|ò;l|u ¾PÓ9MXêh›ç]š'a— Ö§ƒ .Ö–†égž"ÎQî¹Î@™ïÓmafÝ/|1Ny©“éŒßûñÇ«ÄhñÜDÇFäþU¦ûµùãÿq*á LRuèbÊÛTì\œ…nÆ@Š¦#Pø»ƒ²9B.8üCD\|›QÿX§ôëä
+@S8Llm¬ízŒBU9šïxÐt<(’]žc0é:QPö>(®-*¥3¹ì 8Žñ>ôáú¬5ßÑñ¡,õ~SøyybXÌ=7TU`@I«qJŸ
+Àð8ÛªSVøÁ‹:'® B²‘Ú} y
+qeÄ°GÞã9àå¦Ý²`ê‰ó¯Î2ZM—iÁoQAšŠ=-Ï:[+È,Îç»!tõ»˜V $•È¶3wäÙZX¬Õ¬“_±:<‹õ§ÑœTóÚsr'#A€8ë'#¸ò?êflüëe’#ÙnÁ«ô äã|1_fNVý…h# Ñ@eF’Á|àüÆP! ÀywGè¡ÐP8ã³rÎщ¨ „#Í,VÓ[~t­§@¯hÐé¼›Üè¦P~5hS(R[Ö¬îë8˜x†£ô¥ˆÌ$
+³¶¬Ys,q<ªñÀkN«Ê ÷#bh#<Ü®Ÿ×Þ)R‰"ƒvR›ªˆÑ…Ìò"º B„Þ³k¾œ^þK7V]DУ¯½›Pq‡'èÿæZ7
+ÃaïœS·¡m.AÊ’ŒÏ Pá UQ¤ðþ\…Žß‹¬Q”û$*¸GÔ·pN‰`HrÎyOR%û
+áûT5OÅ÷=YFAvcp©;cí0j Ü^Ý:(ªBWñ‹žd\÷ôu˜šêýh‘øé†ø)ðó€<‘x;~-iÀ(>€ÞC¥¤¿¢³|M«œ"FŸ ÔeupRU]ÉBÖŽ­Ñœ,Á@VËF8µtÒ™”ÊhèîiT‡fW‘#Vÿ*­( ¢ÂÀ¶H
+û&Äs¥êDPUQ¸)hî¹£Eu°æ¡y{ËHÝ= ðd,e%=Ð=vÚ@ÂsÌøò²ÿ-|þŸæM5,fœ…jiˆ‰ 2£ÿ©°#³bg()‘ê«w¢x3ë¨á”ûþ ®ƒXð¥tIûkC¾øSÞ
+Q³ÉŸï#x©|À¶®[®D¹Du¥dÎc‚µ¾œÃ$ú3f‘r '6èöI}6‚aü¾üIKô{Óö±DºýF%c¢V»7©¸`¡×Í…;Ìå»ßŸû¢ÂVÞ\äâ÷ƒ!¹÷ìJû!J[P
+èÚZ`½ØÏAPÒ_„ÅSG5x,ý,qv¤ ][?6j§D
+Ådÿ0x\0à|÷ìä%$¬LýF½ ÈâP? lÏ:hsË"Ve®Û_ZË~:ª½|ÝWhÁ¦¨9z_åߌ’Š´áOY]Ä8aèï]/çb»äÜfF—HÖ^Cp¹€¹'­þºÄ¤AÙ:[(aÅÄÑ>’Fã*a8ÏV«Ææï·Ìð¨2D„]Â:¯%\¬=PÝ EQ*µâ–ˆ$‚\„wtl_3vçtW×Ùz7U…ë´B4züg9±@´lŠ‰^´üPj½31tîáyRŽ$å«™QñCâm¿Ó+]Õ±{+êMãÍ7l4`’¿<ébH±ÆÍ%Æï¨5ˆÙŒŠ¼&
+,åŽDýpÿéMÌÉÞª”«ü=G„Ú3صaKîƒvÑÇï\ÓAG1
+,ü¸­Sغ£ˆv Ž2¥uAŒŸå¼ª”'¢-¾”ƒŒÈþÃRª 8uéwDf¸ÎY?1¯ ÿ3ØFöÙcæX<¥qa¶ÿ8@f'9pjœ;´+0´D9fµITšÀœ‚œ\t0
+¬¾ub)uZH:ödíæ|:!CJÚ%U~†@ùTÙf‡ü÷&]—DYÜKýÔþûÒÐïò½/iŸkOïz¦¶\<ᔓu‡ßÃ"ÓI[¹ˆ R9h¥§âÈZBâ%_
+›Ø^í±‘Ö°Zšñ]ð„òGÏOÌVYD£{Q6(Øa&ª»)D‰I÷X8ª`#À¾‹³Ilòå¦Ýox(Òn¿7±SͨYæÄ|‚<Eco0Ws¿Gªz|¤2Žˆýž3&Iºô.@X
+þ<yë¼ð:?hÒçÁñ”ZÂòÆ”I‚a`êѽý^
+÷6Á
+÷cmA¾ˆ@Ú"v}Å‘û@‡‡} ÅU˜[…(:Èv¤1˜†1¡SÓR›,Þœ“®Êa…(^Èê4G
+KߊI[E}:L#ä]®âÖÒÞU¢aƒ‹ÕQÝù"Öm‘[¶œ¬¡&¾€
+å|ù&Kæ·‚
+&›¡€Ž`Ò:w·wCD‹ÕŠ#…×úÀY­
+ð‘߈ó‡
+€q¡ßÄ­Ó0а 3ö-ã]Ç#xD8¾DϘ³š¤ƒÑ Í EÏ_öûALõV©³éÐvájP*QŽ{è."#j@ý¤wCý›VS‹=zígî+•½Fp–Jg+Gyä 6Í„Á…H^ƒ;E/¾)°aË .“R$W ¤Œ4ò;ãÝË<*Æ^Ô`LžD›ëŒ?X×ã⯅–¾þ2è´è¯£Ê¹ÝT¬,ýú{ÖñÉŠ ïŒIÞHÌöbTg·öÕšÑò_ñ²1ŒA­¬‘ÖE^ hìC¢7fÈH¿‚õø°ÄÙ(_ †¹‹æ)‡<yµow{5Iˆév£O%
+%·DöéUn$bc†U–Õ²‘†¹r˜¢%f#š³tÍf_$PSºþ«1°qVŽ‚fN3q¨®7±¸I)NifÖû€Ä;8  \¦¬ ¢
+—‰4ò/ ÅóE#ÔÜ"§[znuû–)짟nû'ÿ}ýÍ/Ÿ¾ûøç?ÿôý/¿½þÀG_üžkÈyýòõõ_>ýòñ§½¾øöÛo~øá×ÿüó§ïýòõ;"ÿÈüûÕ_ÿO ]¯oþÁÿ~ýüãWççïòçùçßù¹<ûÛoGH¿êÓÃæ……_]Ì;ýøœBù_A+
+ ýÒç Xâ«_ )§)>?³Ò¶×»ÙK©Í»>ã£Ðè[ÞZ% ÔE°KJyP>"â¢ÿ¬(f@JØVÛp2;#ùÅóŒehâ¢û‰BZ¢ð^ç²éŽˆûJ£iŠÈ¾…êû,1ËY‹1âb¼1ÂEÞ7‚X/£
+ýE‰kRԅȬÃÀT2‚Q‰`]wB+ǹÒÈH[k‡aN„ÈnÄõæÏ aØóK©Yoò6‘ÙÞQ /ÐT­ DA
+9¤Á«ÐL.чîÆ)±¡Kô¾ ÐÜp –w<«…™À}¢m !#'Ý‹l½:
+A¡Õ ªNºlÑ´xй6ËT‰V<ø}{¾†ÍÛA˜R¹,Érœ²Òö45®S{ƒ­0áîs6¦ïñù ¤m†ò6Üù:)Ôשê ïÅÎ^|@—µpÚåsR|XóoöÙ© Gù Æ+Å›KÀ‰Ã`ÅåM
+.öA]¬DÓèåòA­†Øy8\ x¯‹LO"`WZÕÜ`0øÛÒb†"BhZµ(‡Æ·fÍßþ×) Ó)Y3ˆAëq<3 0Y›ªHø‹7NÆÉÞlÅŸªf"„— ifâ#Sßéúîå©•±t“"¸eæÐÔÔ—ÌÁ´Ƭ0^&ZöÏÞ †[”F=PUo
+•Ïeðì±LƦsµmlµâô|Ðp„y×Gœe.C£[ù{ÛjyÄ@ЇŽß qYA—µ{Xžîø\·v`ÿNt+v‹¯e¾2ºž·ëAW»õïæâ÷¿8‹cBB&Õ­çÞ¦
+‹¨%p¯-ÖéÒ AhÕ¸EÚˆˆ: VpºF=vr´c§Eïš)0ÎÓm®Zr^—]±m‚ðóÅè4-;>Ôœ²ÕŽÃ´Ï0BÉ~"2ÑÎIY|°^Q¦æd›nvdŒÚôd2Úä“)U÷lp—†B}4Å¢TP î Cp…
+H¶"0È$‹Ì¿.ShÿÍ÷Ñ\§J”é
+Møù
+„&R§h¹QEè@`2Ö×N§ÇAhä
+Q|Ç Ĩà
+xç=ZšÖé3}˜(Ö&¨'*TT›=ÖáH5}ýÉmF׆uY3
+×A›Ò´Ç6A }æ/‚É‘(,+¢?mïCœ ¨¦·õpu¹ŽIµÓ^j
+õŽÝ_˜qo¢·xEÄñ ÍŠ×Ör·ãÕ¡>þÏoÔnJÞC[!9®y¸Ç‹È¢çÜ–¹Ã`ö°ÌñÝÜm±<Ûk"(ÞŽ.ØŽÐú®¥59Êq½Ö‹(‰8×h œp8¹Ã #ž¨±/Êfï‹­i+Àä„%( ¹¾Ø>{AÐx§…=)^Anô„¤p㊉Méµd¸c«yÕÈG›¡ˆÐÌÈ–¨Àiˆ«—õðÙH¤„e#qóu•º!BÊ¡B!‡«ÌT¿Af¡”8µJOŽØÒ7N–êçú>ìÞÄ[ÒfH@‚ é'ßfT<ó¤í„èô¸î–ˆ­ôÁ¶«`ƒsC­Ç+@^wª¥zŠš¬P; wâ7G"j‰ Vð ˆìYTN®‘ö‰#ÀP_;|(ø⌌7Í:£côÉ–¿jø Ê«C6ˆi}î´“ü@äUpð÷…m¹Ë¬ ºÊ-U
+ C¹£ž
+ÔÉý/zr¿yS¦QóL ê=4Nœ:¥´N£¤uZ:‚¹ôÐM&žqÜ|±Gò{ÒîΠŠJ;âÜñ¹^‘ À5ù¹¼O!ý'â±HпvõBÔ4aµ°ÌÜF$:¶AHŒyFåDŽ
+DÆžˆÚ‰)4×NA·À€$À£¦.UŽ‹EçV‚Œxúû8…ÊìNc*‚äl\s™Í¢%FŒÒƒ DÕ#\òK¿ˆ–öòã=øWÏJÖ*õÒ­íp‹M¥QÑ645<†Ø7†"¸0GÆ]é…Rþq«í®FSYÍz½9'N²8
+'y4•‘v×òÙ!6qŸ¦}¬"ÀE
+µu÷˜‡ÑíTñ"|lÄÛ“ˆÓK ðÞ lTº{<?+ñ0O f?2…‚¢Mú])) ¹r/õhéx‘ÕÚ}o£(@y&”Ü3!ûÏÍÕ‰¢q[+Ë`¨»<|ôþ1ô<aêœ<NM¤°[5'GóF`0ZYÒ=kÒÍEÙ>¾áò®Òcâ×´ÚzEø<œ©Ò149.·Û߈ŸÒnKª|.ŠWûe€ *—³cÇe=Ö¾]óHdÙvÙÙ;W’bòøú¿ËЭØD„&N•uÉ¡¬KLe Áí¥†$·ÇCI
+B)¢h´óøŒ 0WsÞy`içÑF‰Î
+g-ÆNtcˆ•ñ§ù·d8CCƒÖa®™.°Õ{ë;k¡_S—Js’¸ES…Ü`fl¯uâ1Ârç}¸B†z#Ø@·Þ°’dÜm v¢(føþiKÏN´ÂeX?jahMµª·`ŽCYéЦágI_¼/Ã'Ò´¬ßÛ·.Ã`³ÇF™a>ÕFf_ÇÖ5Ù*ç“öãpzú?ÂVyÁUÑh+2»¯ì€Âø5Q·“(¸
+ÑÒØE”³*¬@ýÌK?5 µÁ£bœÞ¨8ÑTݨ Pg〴4
+îõ1CÅÙ‡2¨Xó[Ò䬷ÝmÕËEñƒ"Ì|ECuzwL´?,øŸÝÃØßÜÌj‚áÍÚ-Î@i]AñRÃ$ Äž‚E FöÑŒ"½‡Ö©MVp‘ÉGFôUyô®sÐî~«÷¥Q£.بÕW x…óWgâÒ{Îc8ÀW ß5à«é§\ÝïRDqRe¨ë~鉰ÒÀqÁÅ8M˜Ã@,þ1Ç\ 1b,äïv^æîDr¥ÿ¨•È´;+‘Uå€û÷À&i~<Åç·ù8YO¦™Ý"åà¨5MË1FëiÖà
+
+)¦œ'RLÇB¼Pºóá¿ZÓ t åÖP¾HtiG kFß÷M 3æÐÞaC‡î«¡. ‹”¢ŸÀ¨aïÖ”Eº¥óüPn®òç—iPBv¦ÇCÆv;9ÍíòòJ'ß%£ÉùÖçÖ1ÔÞ:|Wª¿N(q£º’¨Š'¡_‹-=?g_1àÖs°*Vé=^«ŸùaÄ»+A|RÁ¹O™/‹à-A°1 x˜²íÀr¹ˆ;âŸç¶Âº+?°C °}\TÃí’ ¶©UÁk’Ú—*5O¾B
+òvìA2 9Ջ؉àíó®ƒÌ`n~Ö/óÛfP”
+¥oSBà+‹×¯Þ»ÃŒ¥µÏ«¼PПǕ,šŒiȪûïÜ´Þ°(Å1´8ÒÉ«j‚+²íNÚv7¿ã¼d’DuçŸ:Ìøz™9U+2Í6HRÜø3WÎσµ@ ð 1‚½D¨¨myÍ…æ´Î¨<‚[̳•pÏÕR°·q€þAÃ#‰ ªä Ta| LÛôÏÓ|[Ü–ž‚›ÂïÓt‰>j) 4ÌŽ»<Qêì´¾!° ‹‚ÅgÖÖ?+ý²¼ÕÉuÛy°;¦w·…ªo[–±¤Ï‚?Ï|#r¥–F¬35¢_桵BP?TÁóà ™ÐºD׺xO'ׇÅò^ò…Ç™òj´Œ("P¨ö\Nýß»\ýÛôšš¡U |;ݯZÂ.ÚPŽ†¡õç÷åÛ5›vóÌÏ yG$R+JíÕ™”ÖGD9œ6”"ý?ëeÓcÇQ…á¿ÒK;‚ëúêê*X9 BY PP„Y¬ÄAÄc™1’ÿ=Ïsªg¾Ø °cÝ~»ªºÎ9ï‡l¿ªÁ“ïöÁS4
+r¬'ßæ“øDF:gTð™¬_º½½óD¤|­¢±æ
+VÚÃ)7úŠ†§«é’@¤%-ØÚANbp‘|¥MŒBmªc ‡båPî
+2\¶m%Õò]³X;4ìï!vûŠƒ¤ó£QõP‘(tHˆšIê+8PLæȤü$éÓ>hc |_•±,§& ¸Ÿ9â-üªÅç‚G¾ýp}Km¶?ÇÛY„:ŒìTà3²Xõþ2¹)…¦ŽÄÕi®gøG`#ÌÕ-8÷yðÆírÝÙÕÃJyS²uè8>w~’ú+DÛÑ‚êûLe^%½F©ã´0,5„§„ pë”qÐ0Înir’dbæ÷®q¨·9Â9rm†²/.%rŠv¸–VñͱÜú>cph9rçRÚò0ØÍô^b
+³<ß^üñáÃÛwÙž}ùåËï¿ÿøÓ·÷¯Å>ß~ò×üáÿ_|Ç_ßýŸKÓ¿o)þ÷êÓúe{õÿèÛ³çÛ«?ýçÍòöâËûû¿mÏ^~ó‡×o>¼»ûæïw¿{ýöÝÝW÷ï?ÝÝÿx÷›Þ>üöÃýÇ÷kÓÛo|ûæý›×o~¸c‹;Ü|:AÚ^¾á?þçëß_Çß¿÷oúéüë£þÏò¿~\i<Ác£¹Ðªi‘é¡×èrZ÷Ó‰’Ž@ABi^£DÀl]D‹0âvÃR.ôX ¢Xm ‚þV(fÔ™W’ ¶ï0m}½û¾•½„O›20’ÙrFA6é ï‚™ç8µ2¦íH$LB÷ÇqZuBÛEùÅPÑÕÎs*J£²‚o,0 ˆàƒö…€@c`.©{žës
+¬¼Ï:–1ž1%¼ˆyʬ-,è\ˆ˜½ ƒŽ…à³=lœ¼‡ˆÎ®vèùÄ `I§;aÈñ7«à­.\6ë D=¾Ä+§lÿ~P%\²Â]¢I¹ñ;¦+1âÎ?,~ѲÅE“ʲœ‡ƒd±ºèMäá ˆ1.i†–\¸ÓjƒÔЄt:\|~XUØ@ªJšs˜<7”M‰ô#õ3»Œ=² ¥`'îpO¦Ÿ²§„Åâ£øé´U¢PìC]ZÊR€Â¼Ç2[@ÏìñœŒUfA.¹º€<ÇZŽ3‰¡y<íL
+ØBõHE0$¼ÖÁ“{ p$¦1ÖŸë$˜Ô8IM pŠ8’c¤â´Ä”oñã9{L/vsËpÀrëã*A ¶mCáÐ`ÂØn/š6×ï®ÊÖk9˜ŸÞѶÀ*Òä-h߃Rž.½ßB ´Å2³÷ÈçòŸƒ¨!îãcÈU8ð‹Ž„A«Eä:¬Ý‹ÉÇ”\`¬K!ñì×€ØÈ›¤yçžq4qùŸ/3—æs Ø%žû³Xœ—ñòšÌ€‡KPtçm` ³}r¬Àº6%ÝÊѶä9쉠i+
+#EõÛ|ÜÀ†”Ù±yï&^ÊÃu nÐcËÃpO`¼'œ CõôÀþ¦ì^ð±z\ÁH¿·Y!™’SáÓˆ}‰°- NÍÔpl:,F ¼FÄú$'ú•ËÃeð17ÖðÃÁչļw,ÛîQh´½Þ@ÜjÝ+vÏ‚Š,a ú´MLÌä#G
+'ïHu<±#• ˜p^ a™±ø‚ÁÛrÜóa‘dwz=Ø]VÎ&·±Œwï5Œ7=²ý#ngñ#U5»B|«=g§8æ\¬/‚à! Z
+
+ÕГë$FLĨí8Í7;ö
+ML’ãZBúôÄ°=]Ú®
+'ßEò ä<ãÀtTöS‹LGg€ö(ȳ…¾=à±ôeŸ#yav 8,{¹±NÇô¥¦±§4òæïpxs¿ v5¥VkD=ÖNר‚"‰Rã›7âbFÁ—•ÉQš5À-Stì$ „n¬»¹Ç:6
+B18÷;BíÜÙ)ê!:b³ èYd›‡8SE¥1Û>Á©Ë?1ÙŠÖeU‰hÚÏ}Ø<;]dÛ΋ÏF§¥GiƒmH…»Ò}Å*½ÛT¼2ñúߊT|‹¯/f¿¸ˆ7ö„Òe=d¿Ç2K¹«\ÆFtGMÙe¨t´T¢œ´\ìM'Ü4ÇDÊ°™Õvªçc[ÊY Q´€(Ø(¨paÿ$¼ÌŽÉu(êJYP‘ÜI{žcÿœ¤ÔÓJE¼ù˜è¨¼âwDô˜±Ì 'ðÇDÛÑü
+$žmêc8 ä¡JCõ§wÚ*èºò’ÎevŠ .|.Ñ7ëO/Ž´vz˜‰:Y‰¸:ÁâÑ1/Þ}:>®¹¡¬—eU”PkÌ<÷²&Êô`°u q‡pÒ½ŽÊíNó¦çÈ)"¶ýæ¸hé5Šœ®'Pz/PÌlº<F;PtÃÌꊳ›€c €`³@q/s­<,*ÚðûJTbûz­ãS<0]5\eh²¹¹ÂaüÕ%yòŸÅV–…§ï‹u>øÄçý
+QÁŽ`D½f‹­y"TùÒv 07¥¥|GRÕk-è¾ œã‰xuÛt@ŠÆÂtöX‡(É¥9 Ä^n…ÅQN&ñ2 왦ƒù« ‹Æ9YóŽ?òå ÕÕ›zá^VS„î2pQ Z!(‚8ÁÅÃÄÄ FUA@…&
+k¸ß;Ñv ’’(šCT¹5¤2…~¸VZ˜Ýkn>„' 2ä$”ôÞ‡9ªÎ
+»¦ :C<^¤×@EpéþÞië\k® 攀©—M3­ù=Ê~:[ÚÁ…PÔþ0c‘K¬qß„÷ËÝÌDÇ[ÐÆbà×ôRIWvbLš±ÄÎ)gõ¦ Y{~HsQ~£wõLòÑã×øÍ™~³UÃ!„p¶ÜΖ3†ä&¢¥‰¸CÈ9èd¿ÒÓÿjZ¢q‡)2ëË5pÅ0¸¼'
+ŠXòÉÆÊŠ1°L*:|åÃøNÿv‘CÕ¸Ô¤[Åj ¹äîó €·£g–*X\âwû…¾Ã^,Ãî+í«ì§ÿä<æÏI3ó`¬6¦ÌßÞ‹£³q±kÿùy¥E¬ÀŸ5} §ª«(1øúp@0u¡5YwÇÕñ>EÙü6½ä$.m'#¤(·Q1¶î*®ÛÑ-4¤º.˜Mqå‘Vvo@\¤L(‰ )çPŠõ\åÂéêš‘ü袠¼:ÎÖ¨÷@|UÀOá„Q3zâW›uí_ZR¡TY½àà(. ÙŠ[¨%Ý‚¢‰M¸Úa­Aa1FQºký2Ý+®¥³ÎüeèØ»k¿X‡­ñ1ãTÄÀ!¦ÎœßËÌSbï„Õ‘AL
+¶½_ œ"›ò0¼1£ç1­]Á­.–vΨº÷Ä¡Ù¬f¨Gа뺛éªÌ_„Êbx˜~´¿a=–7(ؾ—qÃMŒ»År㪼É ö9t`õMÈ,òö R„ñ_]Q(¦Ølá—y¾
+w÷)B€fØâIô Ås‚ES¸ðJ´{rÛš¥Á0çµÝ-½¡¨¬.œ{H›ü[õÏ$Ñd‡´ Únî̶-uzñûõ_”À4Qá{Û‡ÏG‚?©ÎÚ¬à"?¤­…—%4ó‘Ô¶;þË;X‡ŽÆ¦s¥V¿Æè6 }d1øhL‚°|u@\Ÿ$LeJ鯈oìBÄ”]Råáû8Hls–§YÇÙ³2³K_ø(+ÿœr;§¬9¤BW"_qÃT ([ÎnŒl(¢kl@Ào40zTº…?Â`•º*%¼ü>Ñ¡m¢"„
+±D–E±ð3Žâ¥à «3 lrŠ’Ä}ž1¦ˆHÞ¾‰å™i=€¸HcTò$Œ-o)c¨Ò€X$ÕM„)ü?À2>‡B²ÚÏ÷
+Zj¨Ë¥ð˜ˆåŽ›"t ™ðóïO…÷ᢱ…“cQ…7ÀI+Tz2LP,ûÓ(ÈÀÚ¡~îWkNž+W£ ŠžÜÍ9êÈÉ~TÂ>a¥ÙI±frŸ¬‰#±*Å nãQõ§Jl*î2è9ŽÆŠÒ*:«¡‡ú› CS¡w‚q|X•&0
+N °1š_S?Æ4š^ %[ô)“cvº¹Nñ^ƒ0†”gyå'…˜ƒ5ÛþÓ0¥EX"ÓTY›‚bf¶
+å£+¶W§¢'ô»®¨|@ÁíS-
+™¬)«Ø×½S_öu·ÿ¡Rn½J¥ƒó˜ÊäÔ|C‰:…öþþÄòÀˆ3á0o½¡«0'S
+¢ªEÇP¯é/øäð&¸1Ï+š"¥ZJU=¦¯…·Ë54á¤ìŒíÌ&ËqCAi\ŠÉ½fuÐÌtµÆ…µàSŒŒ|ºñàø
+Ä`©Áâ0q9Wù žxÐn™ÖÑðQB pð }£ãùÃò=Îò;ÂJ¡öèÓ3:®ãar ÃËþ†³pT€¯ßb˜þЦ2“!—Ev­* ‚e4·U†%2
+G]…¹ßs}j€{b ­ÁË£/€8k[ ê/L‰}½ðm¬ž„J £X#ìùtÃ
+º=> ˆ @1èad)¾£Ô|½&E¯íEItà¡EriË „)¯”=p÷¸é*en¡®¢0³ËP†©á"b<$„'ÛK"ô`ˆ;„
+ž¢ãtw-EòCÆñܨikSÖ@6Qµî ÀÐn¥êŸ"©U¥È˜ˆê3­áˆ° |Äâ,lÖ€¬@É–Í"¨ÄTâEÒ…³F¼,¹©˜ü²1šW~' ý•Æ "“Ûóc{E⇚ˆ%çš3Ã÷ ëak‘£¸bG©Ö¥îwܬÆK¶Ïv)øê²xA›³Þ:hãu,]Äò Eý¼,jÌbv‚Á}}ϳwAÑñSx–Á®ØÞüW€
+H‰Œ—M®\·„Wp÷p7IQ¢8v†Þ…7òÛÿ4%'ö9 w»Z?dU±ôjÝfwþË™Ÿu“—u×.#›Œù9\_ÚÒúÌ6ÚäÛÇpy¥K o2ýqyEd¶!¢Ù¤©y4“ÒdtŸ¦àÒuCXPFæ³ÛÙ¨sã03ò€ºÇèSœÅ}­ãì¥üŇŸ;ì­§š5o 3ÔÂ…c7ýüò1Z¾fÓl­3ãôý hp—¦}äçBÌÌîFåÜx¶öô}s‘è>ÂDú¹ð6lÌh~•X›!i&Þ7HiML–šûâ–ÞÍ¢÷ÁþꚯdQªÇb±wr:¢Ñ¦wÕ á÷Êñ¨`œF¦´éf]¸á€)»|ÉI’}DÛˆª°çtƒ1›5³yHR‚ÔNúê|r®0iÇ›u¨ž@&-ÎaøAjçZVÔ²—d—L›£éÜ…i TuJ®­ì5G ¨2O›Œßp:·—áºîBáYí0KšC«®qCšE•—>MV…ÆT5[Ñæd¯"€ ôaÑæÔ8O·®‚ÚÙl¤³µÖ7„oz|KéœO©²iܨå3´sŸçŠn1L¸•æ:eO( áóõãúù7„TÊ©§%؃Öÿ¿@¥ØIÊx2 1ù;òi§–œœ®¡Õ{ ÒÛ( Ì!Çy:µpŽ– ÐØ6à"M…Öí´ EOŸa%/ pw1^l-£/Q¥±´W؈fõܯ 46_v0_¥{„¥Ó|Ü1_ß`Êåø„¶cLjÓ&­ŸþÔ†¢ îT·ZÚçB‘°u EÆ=Ðs.ˆâ’è]¶'¶~[[ 'éX;%<ÊhüMѶ©ÃIl0M›Ný¦ã_ ‡rp:Áš­a›GÓ†¶ÞL§Æ!þAÑã´úQžnÍPÿ8ÄŽé®h4ʃ€tz†zXI„cL“iÕòƒB&N‚ màÂÈUQÞn½¢`I`6·ZÑ\Ô¢ìçÏFÄ¡ÉF_>0ú¢Y¾ ¡m5þs£aã jG±ã"¼7‹vø¢ûÚcBU.ÊÕŒl_û:—˯ë H&È–† c·Ñ'ë½÷ýÁ‹‡ðR±îóÑÂK=¬.‹tÏi<—mÂÔ± œ.êX^".~ÁþI)ÆÙ
+’ê2vË#¯1Æ…ÛÔ¾f­E{ìRÚ
+ÎY‚¡¸![«Ö Ü݈7”1ŠZ{ôßA¸#â£Ô‘KëOžñ{ ÜñPŠZ©æâÅäS‰mO[]Oxcè¶&:!òÐ6ËQ?P ©m²Çá—XDyÌ’&HŸý·4:CÍ,¾–ÁƒèÑDú‘µk8Kà¡›:˜+IŠ%<ã²·‚0ŒD2ûã20ÇÊ(1}>þ]Y¹mƒ¬0'–/g#2[åÃÁÿúa…38‘,ëÈoÔ®º £f[ëôbUÆÄ©M p:0ñÌG2´±HÜFZuå°Â¬W’%Æ}bdhEd²Æ±wš€uMª¾§ Ö…ñ»,ÛÛöν¥$m—»ýb€¿5Òò\<ˆÍ¤²#QòÊ,mvFFC,\~Û_П!0êHÇâ+Lãðhã³ @†.r­Ò xü3‘$Àí&³0DÇäÛÚHE‡q¹6ó ñ!^ lÍNãr8­§|¶ Ü)I„õ°KÃâ\]jâ­VÜA·ÀR !öjE¼æOA0+sˆácz0peT0žo1·c“>#À°á&‡s“A˜åY—þ'~¯’ÃFõ}§Ð»ŸÒ Ì£Æz­«£ØLZ= Þ-Ã(p²$Á©Ÿ;1,­t»åF®õ`\eôJõgŸß0ðë"*š¬èJîÁðŸZQD­N¼CQµ½é× ¤‰”ƒRH6Y Ûv¿‚¾ÿÉ™~„^ãU±‰˜Ô»]V… ã¨ÙX§§ÝB *ezÏPA0V¦9¯Zq 1°Jï ‚à‹Z:¬‚"—„WÍ—d`&¤íøoºb‰¶V© J¯¸Æf¾ÕŒCËXº^Foó˜"³ ?¤¹øÙ’ÙÄC`PyçXÅ.‘ªw×Íü_@ßHÖª¼ÿ
+Lõ--©—d¬wÓÓLµ²ZÊ7êÝò4u{°ƒ(ü1½oËX…Lªèvćòàk+I ¨&nŽ°«s\DÅÀ _Zì–’Šè„¾¼Þ/$AºÊ¹Öá2 8'—)1Ó*Æ?@l5–"‰é×V7ŽW¼¬ÈZ†7 )ªÁŽ#OFmMq²nMSP Þã¼^ $i.\+#Að°¥Ä¥Ê³—‹R"·ÝëÀ"xHÚ8ɺáÞ¢Œx³¦R*n­š£=@Þ9ÓO ¯ ¢¶XE„vœé‚Z´”˜\†¶ì”Ò01£+AÑkÛ³±!Ä9œAÎýÒ„´t’\Eä¼xŒD£]|†%nÕÓ
+Ì,®ø¸†F=†’`µë·úŠþgŠüÙ­¨x"‰ Чuª\JÀ¿N£dÙzWpÈéÕÌF$—¸nŲõx©ß]š©·¥Tþ6¿2]%ðbø6¹Š†ð„7Á)qåèdð5ψª þÁ¡qÿ/›Çüu¨P{}*ò oÍTüĘŸqºÉeð22LYÎR0„£lוÐdCT”Õ¯Kï·Ö¯Ä§üŽŒ½¶U{[ ëXò¥Ü<,!ê¾·9#°Aî÷D¸7¢é•5aÍ+ˆ ¯\úTߤðVbҌǸWg«u‰sMé§ÔX´†™“{É|·Nå[ Ñ\[wFW\k Xy'••æ¢9“
+¤)û¨¨<kRXÉ9A—xè^ÛÅd Hï‹«˜"f‚Ÿó
+‚ÃZàvHœóÜA?×ï2ƒW°6r>ŽÂc.gÏ“ô¢¦ gÇdZêGÐOifA¤ÆÛäu5ø4ë]¸üKeWŒy\©‰_3óØÐ÷TëØ×ÈrW›×¤A èÊ„¯ed–· ïœ8‰brÑ$úYÂSŠ³–©Ó0`Ë”ðWÙCÍ1"Ì4™¨'Yé2ƒÉì #µŽ¦rüYlö3³áÛ?Œ—9r½„Oðî L`_lººÅïR÷wß—4#¦QSè%>“AðìQ¤ŒeÙÞÕÆ@~–ÃíSX€‰ªð3;°µ¦âÞ˜èÊôF&UÉ6ï/í
+Ï3w‘I=Àãò¬$Gá×–¶
+_3\ûqôÓ³1+–•—èYuíF‡—m§Í3Uâ
+oôø²xwsÈ—”-\bóæñ4Çð#â‚=«ûK %Ç$õ'ÈÓ³ö a»öá
+L?#ÁT6m;q©æé2±1œŠÔ: onõËL-›XL5é–UHuI&Cš—©õ@ƒCÐg\FS³€È‹ðTd.ÈL|N¤]”14%·Ò4_i篠ÃQA{:˜€9hS"³!˜¶ÆÿT¦²Sˆ tª#eeÛrBæ­Yà i„$ÍQžq ëKFÁc )Ó "!c0z¯íá2 –ÙÏX­Î“"<«éÒ·0 à¼(²Áô»åõ omÚ_:@Ê!§B[ö9÷Ë —­‰¦0Õî“ÆKNUîƺ¿Šw«Ìx©h1âÓQÛ?^yà‰Uœœ«Û¦d}éÞnçœÛÌ8—¹Ožû¦s‚±H&Ñ'­nŸkÐÈ=¬ªÌ^z€¼õégëDJKò¦gâü·Ú 3e­è¯b1_ÑçêÑ0Ç×n˜ïOnôW×`â¦Xe¤‹ ?½˜Ìjtဎ;ñ‹5à© ªþ!FŽW¤çvl¨Ž}‚ [çÖ•àØM²\л>Ú^yY³”ç¦o2
+·S™»pê{wg›N3âtÛ9æ>3Îeî£ç>êá77âîÁ»ù}Ÿ¾®µ#„QvH:]4xs#êÖÄÙL^‘|pbº8¾uz‘ßï³ØkH•DÅÇõOÃ…˜¨P”ШÍÇ çuÇ×>Ädùv™ŠÑ‡•à±¦l`Íh±mÇÔòÖ(«ÙÒ¸6,dÍ"]_ŽÞÙã2¯±ç§™£6ÖPcr4fƒ¹Åg/2./ÐÂ5…X(†ÝíóÚÔKª£qA/sEóbžÓèSùÕ¸>Y=ùᜑS‘*ô¶…÷… ›Aªk\´'ÙCm ‰…#/+(ÇÈÜzosc!#T“rXcæ|ê¶?æ8Wéâ*ÍIJÎÅŽ§+5¼AÖ»Ùü4Õ¡ûdˆ¤ˆ#e¿’q&Áj²Ó…`¸xRA|gc?üÝ'Pk‚ª£ãÔo%Ž}r\᯻¼V>b6†FèÁ8˜›|ÈÌN 3>i`äÒ<®¥„æK)˜ñfØÙ’«ÈA¢¢^÷u=¼Gˆ?Œ4üAx5;Á<ñÃ1î¨Ú
+Xäá2üÏIòH5úo
+/È„*`ÈSñœ+ƒÒ8¼»ö ‡Að@·¥ðnssç£1=‹sˆòQáCÙÏF9áløqÎ96ÇeÎésåMñ»Cp6áFp¿íÓ×µu|†ŒC{‡§ýÿV;{ï„H¾žëƒA8@ó_s¯72º =Î&}1ê)$@1ÃkL3Íd€Fœ\1± eÌÕ ÓcN
+­7éhmV)@ )Åè0‡Ò% 0¥œD¡u£)1n.Åõwƪ‹³lÔg¶ë´xwbgîšØ‘P‘¶¿†ÁÒµ»¿| Ê”ž}C
+UšO žZFNì×d/ RÔt‚Ý_FŽF£%”]Cl"ó!{ÛÉÓÒàØ£ß ™ZŒ ¤Ó£YGqŸ cÈ£È6xŸŒ'y8§©`9”jŠY‚2ñŠ),Hå‡b°²?Eƒ¯rË×V0ƒ1°þ±àã6¨B折̊ƒc„ö%!õ*…œ¸zdsAÎiGD™Ù¬VÙ\œ§åðÑÈš(Ên†ø#Ò¶[sGKèøÜôýÉ–.ì)õ!óôÒ×¥°ú9,K¦=`ا /Lk›„HQUÜaž`¶Î-’A&‰vTÁ·Ž4£ûdëXb ïL€™?äË´+f$yIË2øu!ØÂ
+ñq <HÊíd\h`AXXšEÌÂ>X˲T’<„”Ð÷1Qâj8tŸ4D—„©ÔÒºOSa’ ôŽù¦¿ä œAÛÚUÔf&\™êz9Á7P@¨7ïé`mg“èúª1§Ö€Á‰»W¤"h#ËO,ÈÑÍ7„¾„‡"t0ÂuæðÆË$;Ž$¢'¨;ä ø|ÖÝKÝB[êþÛúG¤Ä d‰›z%ôp 6”ð– KmFåí~ŽXF1²íj€
+6Åm´ËŽÐî5ãƒfýK dw¼Z«×›R×›¡’C^¤MŽàc…œècØIT
+£^"º6wY³•Hj¾wóK'Ú«48pSÖ‚TAœa0Zê9G‘
+0×6Ø9XÁÍØyÚðþ Ø”>Ç°QE%_ìéùÔ½ˆÁÁðŸÌïœÃ2KD1€û’%ùDN·Uô©Aâ‘È>M2Ò•¦Ë¿¤è60!lt½Cî‘0Ôó¡%;«nÍ4ßÀï/0–p%G<­/(9—(3X²ƒøÅÙïép:„!ÁëÀ¬â<&¤LG<s‘¥.ïÁ*c§ƒt¥]OƒÅ€¼ÄÂâ¡Šú⸵ä+uŒŽ'ÛÁÙv3/t§€Õ¤õˆÔÕ˜ÏÐÞ‡)éÌcVüN5í7çað$Ãßžy¡ ‚ÌLXI‚h8."ûmØ,è…L‚KµO)SL©q^QŠÛk«É"êŸ\ŽÜ}}Ê-W…úQLûï%ŦÀLYŠÕüK÷"¢Ÿ‰¦Ú1ØLº¾hëö ÝáBêUƒ±gâàž$ œ’)ü£ª9×q탒v`›ýœÆR }K^Ò ÊSB[»o:úÈa-ÌùaJ¶òa/ßø¥ä=})úaqÚÒ%õú|S¸D5,v>Ëc¾h03·j²« î§d°Ã-u–¬^bÊ(‘JØ^’ù»Á——‰‡Pra‘ÑÙóx:º¶=EQÀø”” ,ƒd) î–hu÷žÃ(xcƒaÅ—~~0e­ +ax\$U»ŠÀ"­º–¦¯ŽM…¦¢ØaêSÚ8ǧÃú3Æ%j {(1Ï6tv~þUü}¾)¹`Ö€ î|×Âb~c^ƒt$º‡£ŒR·K—9‡êÄ‹¹ð‡Ëòi^žÜ†± äNŽZçá|c†) gÇf°À08©çá™…å_y»{çÏi¡+±îJð‰Q™x¼C)Ý»¤’`\eX#óÞ´ÀK–{ÈÄ!
+1éi ZEE–³=ô×Yrí-/kéG%ˆÂb=0šÉ!ÜÕî˜éá)aÁPBô£_€À£)hâ~ÿû'*"{³ Mqõ æ^dŒWX_V¡úÇ6­‘™XÖdJØÌ$Qa›¹èèŠT:]L~
+ûÖˆ„«4ý>÷¢/úúùþaAïÂÑÐ}­Mû(ÿÆDþ²"¸6¡Oä y-:W†øᶙíþØ‘k‰IRcKÕJŸV’DwS>n´ DcEx°‡ç tÀ5ï'Û›æh’ùb%¼ªñZ¸røöñéÆO‘œvà^d„Haƒa5Ì÷‹naÀ¾ŸÐWjZ}[s»6€Ç39¸´ü!ýcw)£ÜÞzȱt#‹³Â-dÚ8ìf?) Zff
+õ³féŒ4:Þ!ˆ­-×Þ„P£Xyð2æ47‚Èõü;Áà3L­ýäh5&¡zTWot-V\âL?º ¶ð¡’LaÚ[¼ÓÂZ%u*V’€´x yiöœòø¸žmp] ¶ ÷@ãå©Ç˜Vz†Oëüµ6œßmG+Mä*(¼PB¬aqtt;(9_Ây£KSº¾Âs&z,ÛÁ×[ö¹'{:´¬D)†‡g¶À×Ô¢Tµ¥Ç,FP„LRh­|m‚að1$o"À“Àf<ΕaöÛù¢D“p•3ž‘NAµ_§|)9O—å€ÿ1]‡[‚sÈ3K– ù9¹íÙxW*ÍçYñš‚=æa
+•Â3£þÕ h¸ð?vôùÓÚ8§â©ºÊG+¤'þgd±x@ÜÂ"ºt¶§û§þåW ¢t±b¤ä;»wÞŒ
+,VßOÀ¦á1éÁ4 E«ÙBêäà”ÕÒT-o(˜ÑÖ9µ=âsäeñ6ÈêêÛ÷9±QæÒh€• ’ ­
+`Í÷ûÓ>µE¶•è5,Ó½º_ƒ?€ŸÐKGwŽ*
+\.™‹ÄgYؼ®FYÁP|Vù^Luš¿0]„øHÚ‡´µî´wùNM$M|KÂÛý}QEðQSÂo2ŸÂ gÌú^×J…ȯð³Ó#á:¸á=º=.☿ÔCí~Dd˜'`‚-°±Ãc
+‘`|m“-#½N§s™ÁÅ;YÜ  3ÒØsYP‰ HÚÄטï71˜8sBÑ{X¥Þ†ò1$ß'h4áNº•…¤ˆY”Õ
+iÀ<è‰&ÊÞxá2ïxâçÛü-`ÿZ¨•?]X÷V9!°â9š­ÓB”i¶3Ý2õ1ãÿy&´d6HœlÛÜ¥9þXÆð7‡B.Õí Ë9Mø*³Ìi!2òc/ÍBX£¥Æív‰eÍ­“&›¸þÜÅ4Õd-œ<èЪ`‹17û ˆÄ²úACšINÕÆ=&ò¬ )„@='åÝ‹¹U!ÍÞ+æfß¾EYŒ‰ŸTqÝš¿Èñ²CàëÖ—j¾¸ºoÊ…1ó\¿§Ý±%‚FœªAZ«O×–d1tùE’â> FÍ!Æ©YˆŠ’ãÊ»ö­T^M—À L£h”3™apJåPV…Q²<qõM/E¥‡ƒàØfã,|\Vˆ~O
+Õ¶ÒeN×°VUÖjõ{Ð+ø’ñ9F¤ƒÝRî—µºð‚Ê@#zÇ·¹†i
+HûÓá*M²UÔ
+éŽÊ«„è(
+“¥"».H·U7_ÛF
+ïÑS43š`I§°¤“ó¯Ñ”aéØÞR´BîH}wBV]°Q8Q­o–¨ñd )àôá÷1ÉÛë’8îD™ œû¸æÅš8qU_šî8NÜ-NÈ&fbS—ÔjÒ…C¾M“§¼B.n&
+›ÙûÒ({ªBâ2sG9Oè¸
+VÞꎎãD(î®Rñø…X&
+°ømŒ¤Gg˜^~{›ý.è2xý¼ BØGéXYRæ&Sd†!¹üµs_2 `‘–£…PAü‡Ù–"€:',„hêÉO4ü³¢‘â‘÷ÏÃÖeJŸ…gA‹Äɲ–,„áˆÚò…â„ükHÙ•©ò˜y¸ët¤Ò-Y'lN™y©àV4S#(òµíI÷ ¶^¦žC
+üI”Ñ<‚'Êj)´²ü«PXù€
+Ó2'$µ­Ê³)BÈ_ƛӆ5¹Â&ϵSQoËñjÜ‹Tøó̇u§•‡H‘À¼ ŸÄd!¸òö•1@O¡aKë<:I\ýY‰·›7Ñ¢Fóne)ø&Ù‹S;7‡µ±4–?¶Ü!‰ÁU‚ÌÈÞXÃdd­æw——Í[—š`ð@3xí®#k´2E,Ã<p^Up4o¶z¨#…¼}:ãmƒGF2b;%z€K‘Æš½S°¡”æÝmFAîØ<Y@ÌfÄht/ëÒÓ‘©µhÉtb^ˆïÇ‹ÑLÖ•ja¬;]‹ÕÛC ¢<…{m±oÅ[]¤x_ûòzÂaT „i`Âørrp‡ó[.=8§$FS_i*ráÜßiJÎ1~ˆ[R¯y(PFæÊfOøm•Z!ù}ï4¤ô
+–³mŸm@ó»ÚN÷n v˜Ê$
+÷µ/¯ëâƒÂã
+´*ojÙ¼÷ï%ègõ w$snÏ
+Ìê][{]+«Ÿq|zqìó¤È hÕÑ£+J¤¼K€4R„ò|dø9ê~„•jž<rk–,¹}•ïrÆ­ÿ<'ï܆ۛ—Õ›<:Ň–õün®ÔÒä¦m!>§çlº³e > „–gùL™"æPZØ·¢J©óÆ|Å}w¶:c
+j# /KørâZw"x${G…]¥ˆëÁh
+fìö¼ñ[ÚWZ0êîÏkCRåZÚãXk3@`¿àü6"qñTÈu«dæ͈¤÷cæÃLs¬þˆtÏÂÍï>ú¦ð=~ýŸ 5¿Ðþ\¨˜NN±Ø&… 
+Q¢>Cóß4pµº\=ôÚ„`<<ÑÃ·¨U2m(LL¦Œá)V Uº„ÑEå²-ÃH†É
+''†ÑíÒ(L;ð!R´ð”†D¤C‘»§xù~£®X@±K1”G–#ÝAuÑŠ®¥hrP¸\|ì¦`r X ¡ž¦…ðG±ûyFCBž¿Øx™ÜGäÁö klÔ)š­"Ï(z4“Û ó­Ò»4Ð4ÆŽ
+•äXñ*ΡQ5
+ä€ì«_[Â4!¿ý p,Ç,XŒ”—íBãðe¯‚NÍ»‰LZm *¨ ¬§aO¾4¡LûK!Ȥ‡6§¸ùKþHb–7!"=A*©Þð‚â£ÔCÍ™Çøyt)DŽ<rJ³ ÜÒÑ::°ó5“ ]õÎ `a¯¢wÇ
+‘¯L‡Ñ«þçqAz/hÔT«»’ñ9†ÒAQémž>­ªð‚
+#AïZá"Ì9ä=¯VƒÑÜeô®YXï5œ–Š‡¹_Í‚¾ÂSi<óŒÀº<kî#¿å4ðKÈ*
+\™Q®šï9Ô íb$Úí¬ÃïcÒ°´eP9’îÃí
+·†f À}'ÿ‘§ù¶ Aœ8ƒÊÐŽÙ&‘…°Ïߢ¿*Ò«JŽR‡SYü½¼P~+¤¶tq<Πíþ§KˆO%ôaÖe`{ü¨kˆ[[ÎÖwHœ…%ša 9SÀ.¬ÕòÅ=ñÇlÎHļ?Eß@jbŽÝ‰‰)ݪî'ëÿj¢úI9½Ù4µ0𸕠ËùÔ¤¼Dvrh‡g,>*–ÿÌ'Ï?˜<žƒ¬Œ²ö!i—ßrêBTª
+bYëÀ‰BFˆÓvLèï`Éb.¾xMã
+º6Ì.sÆlaúÀÊŸKx*9
+ò+³G GWÖ®E_«ÉC °zÅ…d¾d8q.é”ñW6¡? Üf’]V‘µÃO(€ µ‡`¹{Ì¢òS¬—÷˜u¨Ïµvì®ùÝÜ·›üîÓ¼mˆ–qÂóœ4On|i"Ab(¦ýv™í³®q ¾7´ƒ„É…§
+õMg ¤ 2)>v[°g¤¨âºLF åæ;<¯kæàûaEL~1\N…IúÆÖø»¹/tÅö6þ¨~¿–m“znG8é fbˆ P• ìjN—oÂñ˜‡8 0)$(‡ðš¦ã‚'06™Ç~F2 Éø*›'_F1c[jø Ü(_Ö»0¨Mò—· {bÁÙTÄ[«.I¢
+±5] ”áeçbma‹"Ͷ8ìk„'65R ï@ƒpaµ˜¡;ÔdÅÂþQXÝ×ÐŽðøt\—Åc
+`eÞ¡‹È4:?Ç›Ó؈R≠êך¯_¼þc—2ÞLä°Îqœ¶õϱ†˜
+-:j¾é„ŠM GI ó
+$¤oŒ</"E¨«Ìwú8mk­(H–ì2¾ ûYþ«d°M(ì²QiÅ.fKâXëq-‚[™1@È_7E±7šÎÏ¡];Œ|ÕE‘;]àðÅ—™AØv$Â’ÚO¢ÄI8‹Ä <Ât1•ˆV*´Õc££ˆ´~ z\Ç YB½ý·dàBµpÆ~þÎuâ—Û\‘sxÕO½9Bý{ƒ%ßõù‹•ùØίtòŽ­ÄYž>÷ç¦(„`›•¥rT(UI<\'÷ÃP*œ$7|v?j!Nѳ¶vt?šäCÆœálZ®s?˜A,ZÃç…t4?`yà(±á,ܾ ¨ew}µ
+ L×Ùá flÙ é MÌDŽ\ƒ×AëiÑtóÃ{¦¼ª}gGhQ`»y~- ʹV2aJ@’å¼1?§¢çþu¬÷ÓÐK1µÙYÈiCöp–mH@6G@ÖÃI‰AJÝÅ£ü4®ª¥þð8ñÑE®%o5Å[wAgÊ%h
+¸ž™dmæQMLù­dá†ÕDþÁÞúø •'k‘dÐ˾ 7H w-:O`šÀ²÷X¹°D,º¾fGõÁØŒ §gvšfÉ0¯k_d*’ÌÎð‹N
+ËgÈ ´;sö4ðlf)Ád`xYܱûGÑ&ô=Š¦(›9Ï#ªçƒeQ¦’Ò§EAÉ¢¼÷G1 ¤od%+aÙÙo¢kÝ\#B.çAð°’PÒú¯z¢ï#MX³5‘ û-Vîm^k>nj2{MåðÕkmûǺ·†ž
+\'!µ¢AŸ/@O<Ô•ÙTP7·æ=½n\1úVÇ‹ürçó-¿h
+.Ø9›rª; ›O Åcƒpo>gý,r$_^«éqhш&ÄAnì/ŽA)ØÙ«Ë€9„ß'c\ ›ä]èÛa×OåþÕýKp«0 œ{e.Š§v²OVµž'H´ÐÐv.\ä€å†à[m! ¶* ö;+ˆ¡EŠ)¾Þ„þY®øÓ@%-­ò’kóë ¾N>%‘övžÅ˜ó#Íl Sâ_µôÛµ‘kÿK¥”jM—•¤Q)êk1ÙrŒ8wD»×
+„-D]*ƒVüñ~ÊÉð'ýS•ÔC£DOFãÂ.nûS¹ÉìB˱¢ Eáhüs3£ŽÈ:úpòp éÖ¼/·6¹csdÙiA„$}I@-"Ì3u&GËÄñ¶Y{
+³?]<¡ ÐtÝæÕ™Ž2Üã¼KûD¹ˆƒüS ‘reµ¤'äJƒŽº@<1ãÇ;ô¹yªEèß–)W2yàðÏt×H»˜&z6åÄŠ¤Õ€‹ÌÉ̦HH‘5&¸°4IÇû
+ƒì!ÂkŠ¬î)Gô¤5{žI7€:©ÈS$ ¥Œ„ÉKëpõ±Ï 7ÍPÆcº‘„
+‚h±Î¡bõšÇT ^x!oéS&²«9AV ¼ Æ2Iƒ@.Q9íé‚ÎñCö
+å‡,ZÝF•TìÎêžø÷yæþŽÁÎ Í rÏ¿¿dOFc¶±Û"ÚQµÁ: 0ÈÆö/yƒr™4øÑ»~I¢°Þ¢ö4 J5¯è¿ U©Äìº(CX¬©2W@”fÂ)_ÉDŸjy0œ³ú ÏÁ©P–r±Wd¡EüÚ ¯ ºAè,£¾FwP6ÓHq›¥[Ó"õd«âÏÒó±,ε‡/kXa¤y `}0ø\·ýŠ)¬óóð¦Ð 4È!é0Þ©qj ã&ç Û!d1yÆK\§ÆL WkHÕ §ìržÄŠ…¸èœçŽßnsgNðª;Ÿ‹Qý©Àä¡Q?Þ™_¾$‘ —†"§’ƒã~¾
+ g“Ìi…µÂà^gãeôaLFéÛ—ôgã?C &²`¾ð¢ÜsØ·’YrêЇk%uŠx÷zt[!ÉjΑ¦ZúПØR¢ópÐK ÿì:Hq)p®Ø9ò GÞ4I!^)ò²¡ˆ¤òâªÌVg{a±gx ÜZ¥ÉûäVêu¸†ÓUî_ÅFÇxU?…{MAæe`©%+×æJ91ÀŒñÚg’j×â5†WXr²Ø­ÄÓ¯ÑHóÈ2-©¦æŸúËpþZ’m²øöeÎCÄÛ’ 0,I9pí “: •ÿLjG9öž²)½êmISDK-É#‘]œ‚)‰ùÑÍ›ÜDc›OyŠ-I îp*™ž£
+‡-ù\¾kŠ?cEógj úɵ™/¦uקnç Ù‘‰ÛXé:¡‰‘Ûû@oF’WìíŒçú4Ѩַ/(aZ¼Œ²5^úš KÁéÄ;+rCH6veK¡ˆkÛµpQbIG]#|úäTù¹„·sî`wAnÆÎ6yÚè! j\ }&FÒ6[ïGƨ_c
+f´=mN®»¼€ô¡Ö,…ëSÿŸÊ¿–WòïÊdð× æãgBZu«›¬©›Šª~Ÿ:äîuäwZk% \áD·GöõEáÅ)D;e¿9Cè+›%ùâY:*˜ˆ«(¡E°š£¥‚a’h!T+‚Σµ8Aj„φ¥hߪ–×UÄ?´ ãRž'±À‰‹ZŠ#¦¼ò"¹‹!ãCݘX²ôäŦloƒîŽß>·Wfëʽì× ø”Ň™/™þs÷ø²†y*¢;{´{TE¹ìj:õ¶4îÝvšÊ1H «píçð
+>·ß´Y}ôh†ÓÊ™L[‚ ëôêÑX g€0ûÍøýeŒ~1O—Êo"-NïœúíAhRüÁZ­æ›âT?jœ5ê
+å ÅKAÏÔÉhÂÉi Ëe b,«<úÎÆÄ–p¬$%@R–£²0åŠRMéò@`©æ›ŠŽù¡ˆ´O,}GMr I)ô#É`X<™uÚ燛$DèÆÉ›~BùøºyšŠ‘B"þ Î+$‘µìVmù(;Àarò,i_CŠuž’ÁjqÕkDÚa×”%؉¤¾É3›t^7Çêis7óL»b…“G5uœ•˜R_•=òAÍ? h:ÂUÛ
+†+_èbÙ;R¡¹cÔóÄՙѴB“
+êªzè³
+²ûX<@Áüòì
+²A5LìÚÑ3K®hASÕvžh¿mÄp&}’»
+j!þ-³ÐBÑ ië(½ Á€òBàž4— Z˜º —ÚŒû¯AchON@…¬Z;ìVé3"µ‹³‰ÀB2c…Ng³þd¾OÓ,˜·„P#ùÀÃ[UIJñ{_ÃpF¬[ØÁîI½žÃ¼ËîIN—çÐ+ƒM&USHI%îé8c¨ƒÿ¢Û:²²
+ôm–,K•šRH%=
+Ä¢% ²0–íj…îïžï¯(*ÀÅxüP¥ª>w×i‹gƒî gWí%ý$U‰•ØPˇÅ`ñÐÜ¢„>¾ó,Q?n—}/ˆqâB>RKƒÊ[À¾>Lí*dªKl]>$AÝaQ5[®æð/±_€Mvæ—]%B”7`ýÏD'¸¶Ên
+Áa ìHni5¬t¢ê‹6†¹ˆ˜²[ˆ_5–ŠgÂ^oαiª püzÍÿÇìÖ
+²‚/°`¢rÓ*× gѧˤ“X)Ÿ ᶬé‹åÕÁF)Öoãªê$¹CNæÑ—OD›LyÏëØ× ‚R;¡#˯Á_4à¹v²än°[8¥¨2–„ãe›Ý4忤ö
+Œ/†
+ö Z²~’h?›YÇ>”G}öþ&äiJþs ºÌû]Ä&L)ˆóÝ Ë
+¾Üö­˜'L¸.×sЇáKÿ‚büøX/(0›˜¨J™ _nA}Ëð*r©¯òÒ¨ìáANò1¢öe½Ê 5c“"¤òs¸iJÂYc±p(F£ í·±³kHù§Ùì…EE0’¥Lž°ð
+A™/Þg Vf ¸.ƒö>C5=AsÛÝ™X$%I¦Z .uc XàÒ=„—²È,‘°Bšøo+ÍBbð64($ºÏUüœõ†AV
+ò‰é.–¯˜B¤¤ÜÃìÊŽ—6ˆm QlÚ_ƒž\ㇽ‡£i˜KÔù·ƒžäó$)ɪUIǾ,3H«H+‰ƒ“8ê\,D”’ÚÖȇÌùñ2Ç‘+‚è úeË(p_LAcÊÕ „ñ$sî¯Iþú3KÓåŸLæYÔvÌN+ÛÑÃõøÑLÂ`–­ÓdWƒÑ/†ÊN3Ũmƨ‹™Â1QÝã@~Zgx".LÏ»¯¸ŸSÔ}S$w¹;l£L"^$,HR{I¥SI/Ì9mÊ
+Ö¯ìTí%48=cÇ„*‚H”º“ƒßáVcXAèv$®¦ÁØA¥ÐWÕÊãÄ85ײbalRý,è6¿>1£¶Ÿ¦I÷dZŸWmÇ`íH*¼"Ókû© B¦*7˜Wô¥Ó-ÃäÄLð†Ü—(T¤Q¶†@®xHðÐ6ªÂÕ!¡{ØÉ)&Õ¬ v
+R¡V%ìaÇéR¤|L]ÄÍ -º,¡9ÝÄúŠN™’çt"ÁÜ0$à ¯/š‡V‹ÿ¤Á¶Š>™5,~f€ 
+@)_@>ÔéÛ5uQÓÉR™š7ä¿W9ùƒµmù‚ NÐ1åÎm'[üD¢‹/?ÞÂãë¿o_þ{‹Þzè|ÅP\Ï…*‘:iÿ4öpA=HàéCjö0NG”pƒcA&Žs?©Ñ4–6É襵“¡F1ʉQkÇDAÛìho1•—®pOÕ¨ë ¢‚ךæ2M?ñ¢!HÁ²AH óÐõYr ºi<Í|ÅñÃî1ã© B[O_s¤Xºì¶Ô[n+X',£c,H¶æ²©M³%ìxCül&À¹,pRVê³þ8#¦ä6zmhíòÞ}C¬7%ŽC9xú§`ª0ûÌL4†Q$¸ ÚŠ¬§ÁþfrE$õzQÂ%Ã;¼ªí›²\=ªíb215Øcsï+TSR³7pŒßz¼®dùÝQûuÓß›X­¬ßÛSkî„eiߺϣ4‚˶v?1>õ|üVœ*E{j“DlŠ–ݕĨrQæ–§!8îWs_Õ"øþƵ+d·>1Dûœ$*Šl¼ _ ·E™$ÂaÃÍ ‚æªd–_C
+ éœ%²r„02Z¸©š{ÎÐ8rWï{!˜¬@Ú¤ªÑkw …âÊ‹áB“ßî©K¡:ÃìWZ€¬0g££ó'1ørØCÛG¯%ùn—‰T,ÔxÌK¦’…ÃÈÄ­)Å|S°:Cì×
+Té5÷ {H–UûëY‹;dÕ‚øºÃ~³{kæ×ÄW44ÁÔeÓÓ°¨·•åôª†Y|Iÿî£sLyeGnÅô$öXíÎH"’D”6ucŽi—Tƒ¼±;5„‹çF•!þ}ÕßGËìj¨8sF½*9l¡HSV@»¨ ”ra㦆άÇôݱ Ô EÖUúóºè×gbºIg¯M› ãbÀ+­CG-.ð÷ï \M—FR9r/{ ¼ÂöyA¨ß€3ðoÙÌä ̺{_9ñJ…95RHK]§
+H‰”—AŽc9DOPwðæCEŠZg/ëÌ*ëþÛy”ô]¶ŒÊÝmgXÉ`0hZ¯!½5®½¶G7¿Zm¥©[7•Ç H”®½ ©ÅÚããÇð§K­Z×jm¨«ã’î@8§yü^ wo¥—î¾0.ü&¢ÔÒåñ91^‹[t± ªÝ¢‰òë#!£µêÝZ-Òö]¥ö¦¦Íl
+ ‹ØGò®‹ªC§È‚0Kª¦ò:ÝX‚ø<p6i×7(˜7æÌ-óœÙu=E\bARB <ÿšzŽÁZ,Ï?@ÖU|ƒ§×joÎi%¥ ¯Þ†½y¦ÏBÑnàmTP«†í®o²CEf6Ô79~…ü»Tû¦LÚ,Ka„‡Óc(æÚrè0sŽAQ—”EúŽù{õœ*ƒs£?s±±Ç)Å@Ô0Ò%í½KõYW=—üpÎ3q¯y&à1ªg"ó ê‡1Ew×1¯ÝPÐ^ͽ"Š¾ü»Rwë5Í€rm8­K«÷*’Ç'‘£žó+èÕ®;Øë¿¿i©†]‘+%“Ö©Ýf+•FãYØ0˜¿ Ã«<ýua;½êˆµ 5í¢OÐ麿ƒàiŠ;ìÓÑfN %+´¼v&ÀcB ›&[·áo,€ÀŸ4‚ÆãdÜÛ¶û„”TöØvË1µ°ºp’ÙmçK¤¯IŸ›ý
+ÖÔë×N€ËÈÙÝçöáWîEq 8W娦C'‡c0‰À·6žsçb™ˆìK,À(uAžê0_‹1MwÊþ8Õê™AŸ„ÔûZm\j¤{§ó.d/ ŒÊq¸gf 
+V©Q+3Ý ;2¢òFn;c—¥sÂ{$]ö‹qJMr€N­›©ú—§ÉMˆNa¼Z웈»±°7Êmkr•ÄXô¥@0²†wW‚ßœ ¼T–«Ìß@þ`èÇèÄõÆ¢ƒª§½T³ÿôÒ§ë¾zÒ„ck=ƒR_d®u®rì!e÷Ã~õ{‚’Á]¤ª˜—0z×Ó¨+…ísÈò,éׄymõó½!ø㑃\€ÎòR¡™i‰Ñl¯¨8+ȇÁ³Úæ9%9eTÜboX„ÊûÃÙm×UEs× $J~ÂÒ#³¼zYzÍÊò`ÐÏ7 :&¨ Ë÷¹@¤§³¢ÆÚ1r9 üÌw¼tÍõ ÒA’ o#’³¸P&kó‰€Ó,_8ˆ¯ìàkÓâàè—¾ÐUy.ÿ–[¢YB2g¼gÌcJŽ[ÞL©Æ›Zý¥æ_Û' ˜²’ì­kyjìE„K‡¢–86– âÓ€¸ç{Í TÜ™¤ÂB1
+&äFrÇ<`-¬¿EV Ä
+¢v>¿*8ÏÙ쯲ñMPÃûAܲå>߀øDÉ¦Ì¼Õ ;Ç›lo©ßÊîš³lÕ SÃ(tñvH¨*#1'äbF«RrÞÃe¤À(Ö:…Ód#•í¶6˜P¯­þDm™[bñ‚ NÎkÊù¨ÃŽQx3ry;ú"ò>!dª;~âkÄ[^Kµ¼ï›¼ ‰|FvoÛ7r¹âÈ=áÑ‘©àóm{÷LŽø‰™_Éi¹*›e¤ŽÓ;¥ìØ’÷»Luà´[½Ô’¬4]VX9ì»tòRºö$…ˆÖ3³Ó*‚â*mBfr[«1·Ëþò
+bÉ2 .ë鯞n{Â|~çE_nBù1Ú–D/Ø…³›
+J±´MV5¥%tsê„•0ÌyÙî&ž%ý  å¡Ê†)âÒ#Tˆ3ÿNª±Œr"2ÒÂu–<mÊ8è—Ñ!šWyt™ÂŠi‚ Š ½’î‘_i‚Úo3e" ù6× M(„z““ÓDô…@8þcÐ;M„A¯4!­k[]·2…Ñùµ
+à€r—Ò„ÒŽ[6‰
+Ì‘aWĈ¾éüc!ÿúF µ/_§åF®~*®ðƒ†6£è(¨Î¥’†ŽP § fÀCrÀQ
+ÁJúfqAO*€n.›iÀB3'Hù:îÀ
+âÍöÖ‘ã6’‘2öç²á:krXBHÛuÜAº(d~DE¯[¡ÎxE¦®F\ôÏ £v¤Œ)- |w0a„1Ñ8ÓhÝNz°@Šôsj£awîë¼[ê˜ ]7n"ÉÀ)òkRNeÁ],–ÑÓe}˜RŠ&Z6àG2ÞC¬d ÝÊn
+çB•Æ1µ$ØóÓДޘ耕5ÊBlwEjZ~ÝÙi^ºÕÔÏŒN×Q7
+Z‘Rû|¶®àîl_>„`þL¬¸®oõ‡îR#ò_55Æž‡váäˆMÓ±Ô„AgÚ«Ê alÊUÅÙä0¤ä Œ²¦©F!?œüDÐYBùî(JoðîA=ŸS¸hlÊ°œï4V’,ðn_ÜQ¥‘8¤s’‰gðAIã„€”¨¬Jw;° QW=ÀÙŠñ Ü&ýMplA…©…ŒW˜ŸœÒ[S:°ûCÓ(+`ÙÛ<!²O&¨ÅõŽJî°'?Ón°7S2\ÜxÎL@[®ÓËj8êds5&DŸMA€2Ï–-C‘ˆ7¸%ûd
+ ɧ¡!1´g§RQ²›tißé‚ðT
+Z£Çš¼'ódÈœ›|ÒÓ£ÐÙØeíaH¶AeJ ºI#™ËøK ‚~›ý VÜû9Ay€Ð*#L0´ï‘y^‹¢&©Ö™{ëbøÀŽæ×ûÆ4^CiûûH°†4 ëp†NM9IÝç!“²9“n ³ùa;‰9:ÿ$*^F^8Áþ2y3%jµ“U1}ÌiQS¦O á·[Ò=àL/Q~Ç#õž‡ò öz(¨‡» §mŽ!/Œú”Íš)KêÈKæ°ùh/º/œmÐ
+8ÆG;_z3‡FGb”œõu Aj
+FHÝBº½#ÕÝÝb6©iõ'ô<
+ZùàÒ|
+Ø#®ÔÀ©Š(ˆ^']¸D­¦P¸ ³’Ý‹»rÑÈCJ9ã>!€5£Ý×¼Žá¢ŠÒc,Õ‹b"ñ˜8ÿy7ô¥RÄM‡­±ñ£m è8´.Z|S|@cí“¢*‹÷ÌâZ£Ó`!ò:Íu]’^`< èX?*Țį|ËÏ˵“˜oµuÎX|›?Z&×hÃr.°§’ù^ºˆ÷ö¡×É#(@=o–ÕpÔäV?è/7ß÷Rè\Vƒê'—,¯RÚjuqãÁU(þÌv-zP3!ÕÓÞuT’ËKàÈçD£\üs‚(›%äóæ»/²›‰ãÔ› °¢aßH`=!ôâbzÛ2f4…?.yU%VÂôêÏÌ>]Ô…ùžUZ×ï…ËÓ3Š—ó5ú3ål!§ë\¦üœ¼p⫵@£>¡vÇ
+‚LkAÊkŒqs
+ô Å´{3!òSÕ3>Ü[Ƙ/8/©­z!Áê,¿8@—ÄÖi_8§¢¤rx¯=1µLj¬aÒŸÙ FƳ†É1„¤VÂe ´¬ˆíÊè×;«¦[š=ŸˆÁË1 é;^È\ŽúÀyhˆ³Ê ZmYUO~qÚ‰Z*¢Â³Œ¦u ¶ì*¹÷ª*tu©Ì½ ]È:œûó¯(èM’Ž©`Lí~'–Að…DyÍòvs @&óÎ^Æ”_Á©‹™?žYxšÞ#™‘uRHC—Ò¯¯£ŽiX'MV/(É7GÄ0¬c$µyÏ1Ç=MCÇ«&»]ŠoÅ4&×ÍEbà~6L²Ó°F‡³~D—¾4?1ÌPÖÉ@ùÑÁo1¿-FÊ€šZ<E²M…%r®UgKµ@ªÌµÎÀEýƒ̵#Q:ÏCcSäü~äü?ÚËu·ªäˆÂOpÞaÿA‚I8ôý’(?0HÑD&‘HÐ E‘e°!ÇöÄc’ðöùV÷Þ{÷<“‰`/wwU­ZµJ¶9§®Š\ eŠqViÅ õ•}².Žê¶ Õó¤7§vR+·†ÉÛs·2Dgg#£él³b±AWlÈLñ‹µ‡Òlðñ†¤RuG\8þ¹V«GaurÜ&²Õx6›‘lÂ,ÎöC­0-Ì]Ê—þ¼ ¢‰5ã¢%¥¹?× >IÔ80úeSÑQ‡º¡‡!tÖQ¨UøÔMŒÕdfþg_fõm³*Ãzâ}ur¶k© 'º{?…q
+7³)ŸÉsë˜Âµô0n–íç˼Êt„mú’að·>ÍJÊ3éCºÖ´ ;
+?q­ŠŽí:W‹±.ÌÍJoR‚Üõ§ãd³v…Â3sGkèà!]í~D͘…q¸s€àÈ&dˆ‹m¼»sØ´õP™E† A’^CËøå-øì$q~hq ”,/* "‹
+
+¾~Íš8ƒ¨Ö\egMãUŽG[µz{ŸžQ{™¹½4¾‡jóà¼ó=  qÊŽ…Û,í]>’ $»Ìx–jj.¡#c0{-åš½ÄaœÉvÉ›&Ξ³â¬0r“ØxŒšÆ®
+R0ÅXDŒÒHÌƵ_é4c§arSÅ$Ý9FçJµ šä—ÇðVæc¡ä /°#P$ä…e†äƒ;ë1e&V‚AG ÿãH—†§Ú@è’ðšažaò€x ~ÙvˆÌ© ýÃ`«x¶îT~·D·9¾A·²o—1/PÆ(“ßB± îMé‘‹ÎØ’,ùìK—Æ“uIsŽ9†W-(óêÆvšdï5=[šdeí4a´
+¾/-³Ô¤ö%§ë‹íH³Y#ê†Ñ&øøSQÒó姑×>feÛVja1TÔ'Z·íFw*ÞõïB{úÞ$º Œž¶žŠ8ÐȺ¥U¬õç®@ƒ§Ž®{×+ñÏ 1✠UŠÞÅAß[¼Ò%f-N£ã¯h@Æô
+÷¦ÿS
+›?ûÒs™é|ñ>ú÷dÍôbúëßÌtÂǯ_næl“ëQ¦Ýdºåù–5üæ#sýù¦õ…÷%…ÂÐ6R°dΰN±HÐÇQ®Äe-¨™Ìx¶š§°«áÉcÀá•e™Ö Ùqø€ÙO &n]ˆQÌtÖß³ºlwŸgnþ¾ùnsA´Š’Çšâí=füqA«Ù'
+ûëJ±-Êj¶Îñvv@b€v3(T/½Â;*{&kŸãÓ2iS`}Ââ'~Ùé®°µ¡Z¶–GV³©ÙñÓl³äc‘Û¨¬ æ’KÒ*2<‡Ý­ÄR•ž\Ưam"£«†APÏtUm‹Î0‰ã8r@ï‹úá F—ycH3Øe¶œaèÞØ-&űÌúPÆ‘£ÑHº£-&j¹7ž5ɦª…Ê#o(C¨(ó8ro˯[£7Á—qà á’‰ ï²u 5»Z0ã3W  …ÒŠ{wP‡éTuUçˆD=Q,ç†lÜ{FåzúFû¼'Ó+ÞÃÀ†‹í›/ Ú/Û$ÔU …­Q=;‚Ë. šùm"E4po½È0­Ì&Þg{àáWjd€¹~?eᥠD¹ùœ$'ÀôReh5«1
+º– ±½®ß#”q9ÕösRR4ô—Ô~Œ©ÜäÞ²ÄðM@¤ñF·Ÿ’Mp‘_0™²/W!ùÜæ
+ '¨;Ôš¬¥¥öº‚lÝÙïÈjiÝ&+™ôàÃ\š-¡÷>ƒx d¥åûëœðã
+Uh ~zÇ¡€›Wá
+N>úíß“.ÔŒÙÅlõ2àô!Ÿ°…&†¢7H†ÖLþ*çŸnøï¬i†ÏLÞd²ã‘»¨aIïþe¥„Ââê‚Í«öa”ÀFÐÇ9ú›Ò’lÍ9ÈL\¥×9œh›ÄæG~¶ûªÝn/(!Ã:Ký8÷6©õùœŠPDÓõyUÓ
+"ýh»3šà¸e¤¾}ΠV
+çòÆ#ÁËžVÁ£Á€éÖ1¤¥DõÛáå*”VÍwX:BÒ5Ú=âÞÑey>§|dþtß[~Í•m¢ gØ’JÓ27HÁq»ž‚>g2àc¶«†ÀîÊÐä‹@vŽ|±y€8­×ˆ´þ1óMì¹]ï©8#
+1Ó7äQFyótBÅ‚þ÷ªx÷¹•áæðijµuL/EiåLʘmõº!QS_!x ó_ÛTz¡»?óh1ë&&5ŽjoËXHÚ80ª¾zÏsL¼öرQ?oà•Ò×MÙvB7s2 ÄKoáuSóýã”h]Ǥh°Y.ý;¤D˜Õe‹IÁJÛcF!®rîÜBÐé9¿n2¿Ü—hwÀÄ`úoÝ”I;ÐfÚÜUK‘3Øû
+á]“Q›Õ»¹Úì¦;0\¯›²·œ[g>Ê20W¬?»ïÕï~Ž©å‚ê°A›WY
+燰Œ~‡\¶BâÓ­ªHvjsÞë)JÕ²]mó%,J£øöICøºY–¤Š›§éå&·{Ë"5"Óx©ò¹×>€q³íq{3äÆËŽ1ŸëÂÂÞ¿$J-Kx­¶™Ô ,=‚àyŽ P6+TÕG þU ¼ ’u4Ëñ|N½Ô´l-@+um0m3Üc Ëk\ØH/éüëª!¡¬õd›¤úV„ÄW/7
+q}ÆW_!¾ƒXj¯³ {°_9¹£ýº)¢€Ž0݈Á
+¯V!´W7Ú-Šß3ŒÝý’Æ«qn!äB_±~WVþgnÅ·¯’HmÆœ\Ú˜@Ì:e®3•±™b¶L[¹ ¿Ÿ!Wð4VÙ¯EÈ_óÕ—£aÏýZýþ!g˜ÕŽžÉïkBEkAº}ýâmî›A†—†ª|Åypœk'øc'žF^wÄ\GÄ}Ò‡Ý3á~-êë=äŸY„¦4+d¶Z'—ì<gÆÔ„hÖÉg¯Œb31c ˜4rò’5ðÙ¨îxNn*'¨‘öÉpwg#ü ÌÌë;€À&0ùo]™OÛéc<NÞ¾Ÿñ¢v}gáÑ5ø0w:C‰[ô}TEß¾/ì×4NÛCBn@·«Q“çùÕu‰kÞqø¡Éu=ÙÈ»(ôža(ýqgv«“|«V«hzƒ^3¼Á™«fÀ“$³¨¦ª)¾îÕàê½ÅÜ÷2§Ú "]”q·ˆ± ã4.¨t7}/Wéêu R@:N¢GüŒ§‡C ‰Ž(4íPG>l´B' ýýõ -^µgŠ]ý~p_71æû^n––ÇSþK ¯Nìõ] &|[]çØŽ‹vèÄc¹Š¤ ã¡s×ú(±{V|¦w~ÈÛ/5Zf*‹_?äݯHo3Œ\ûÑA
+jcÇ醌(¤|§?‹×Gwc> œÑCgìe]Àg¸ ò[¹ŸåpV‡L^ a,s"ì¬Û³Må3GlivÂÕs˜«|çj°¼ÁëQškª†}HéÊ 5?´s†ÙÜT­m¾©¯e(D:厂­âÊÈz"F©¶ý¥¹8”á„žk%GBjlWYéžÒW²÷±RÏUЛ
+¯BÞ´|òa÷M¡üÄ
+ùHý#èým3õùäÔ W_=ƒŽ2Š±"¿|pQrìa°Îz™# 1a…òø†×VŽ1%Ì3ÞC5CKmøž:ÒA®§Î­Îï #é6‚ Jð©šìYeå¥U*DZÆTƒLqÅ9‰ù¤ÝÃ0a`7fx
+Ujê–ê?‰§3Vœw3„$þÌ/Ú¬
+SÇé¢8ñvSlPÌC‚ĦŒ –ëÜáÛ>‚Ùó‘ÉàJN
+°Þ§B®qÌ;ˆtïßÊîñ—RG3w}`çöBÛ‰Q¼Ñªp½ÙlÕ ÀŒïÖ¢×@ÿÎ{K¯§e‚)ý˜¡ßSÑÍ -bH»Dìlµ·’«/îìeªó䪦Ù}ôæ•áaKƒf舑1ûÒ—:bÃá]pžÓ_esò8¯7@Ü6ºZÇSªñ Ýô´Kt½m=/Syçu‹bÑçÈ)`~Ÿg vÔáRn+9åbú*&&uV}‡‹,•å^Bà”¼÷AÑ«»týïÙÑ˱ÿ¹ýíÈ?ÃDËuìNü0ü}×NCK ë ¬þyGNhžzx×*õ~[édTú¼æ+¥ßÀÔöœ§^€®tˆtÐ(ëi˜¶
+îºL¤ÏY´:©ï÷[PjR²+Ѩ©à62¯M‡I‰lWA¿Ÿ à Aòoe¹Y”XgìÆ“-ݽ8òG/ROÔƒD”nP\Mí;1èG³èÇ3|†|¾æ*F/&­
+Œïšˆ
+÷¼½p÷ÀN4hnT©R¤
+õ‡ýΊÊ\Æ;ž~ kÛë„„š•ãq=ƒï'
+ÒÉ2ï·^‡–)û¢›áÀ IßjñžZŸ!—ÊNó*ïr°}¦o^•œQt§‰z¹XP´u®Ó•LMÏ
+dX^ûTÍÄRz{Þ +"íKƒ·ížë æÈ”ñT„ºë‘EQýÙÊg—^ÎÙ+ª¾„·µï}½vRg3ÏøóÅ`”çÖêýZFõÁÀþä¤eë™i-Ô’'5[Eȳ•»õ
+ÎΪ@E=òt»íÊ”«ÇHÙsn) áËi.áè† k6âä~,ç3
+E¨«…"‰é·œ
+lv-÷u´øè¦C–|´ÅJ„k«rŸÝGkIÃáT5çœ8‡`£Uç:¤_ëÂņ;È +…HâÁÂ62¢[Pq«C“nÒ¢A$
+Z …BÎò*XŒ T‰a¼ú¸q¢Ÿ«²wXuÏ‹õN(
+éÎéë: VGCÐ2-(p†oyý:‚mëQH:íÔœ°tj‹« ɸÊ`^–ÒÄÇí{»¤Ð»$¶Î‘8؈ãrŠ*HäÌa*Ä.ª£Ÿ©$½Ã3ˆ™hR¸Ùƒ œ‚o÷"©)½5–ïT›pª9Ym†Y’`Çšf;"@¨š…·þŽð‹.´¤‰¤ã“S”%ê¬ÃÆ3 A>˜ æ“ èÜH—;@àF5õ®°U¹ô 1&8gvR„ðÞ%y5Ö×êJÑx’¿ã*°™ò.xå¾J–Ô{Ñ~²qÀ1Ù§0] BP˜åÿMK€Tà(: ¨¡Q©••(° 5à]c"XIŠ²K³ÇK$
+4'"T$ûÇû©?iñ0<ò DYn”hò²HAØìIÐÏ'H+F§Z¼G¶Ù4m™E;%&'”É3<ü¯ìÛ²[Á½qIŽ¬úŠ1±3Ú$+Q&ÃWÀ,>è×Í(DW&m.T_3ü ¹·¥Tönálv[Çÿ5gäyDd)½B„Gxî$@d¤EhÑ%YÖ‘ðK0!`äØDù%Ý+˜0J‹¥ìrŽö‹cX7”%ðŽØ6vž‡qÕ¹EJ‚”ålÐ Pks AmÒZh”‘Õ UÖ¸hAkèç.X9ŽŒM\UƒðKÃС'ˆk¨ r|å#„OÙcÝ8M™áË.pœfº‘S]'ÏpHà=ü‹f€lfà=œ‚˜Cðóh§°‡ˆÈ–¬ªJe±-Ô#ØG HŽ¬D°Ô
+pÔ[7>WŠ½³vôiT "
+ãϋɨºç1»ª8ôñVÂPO{ Ù‡‡È¼™šÄsÚ§~Åa[ÂË<¨‹@¶Iz{æ—"´hÄö·HH ê©'¸r7Š÷m©åô
+¹¹ÉU¤êÈüŒP4Q·Àþâ9ªêŠ¡9Õ¡èÈBí¦/ Š–ègŽ
+óTï ¾éXÈ©瘒âC‡;2Ö±E=B¿£Èã1 ªiR©ïoBO2´kÌh:z„"
+t³,èç4*Ö‰é]
+‘ÊŸG¸œ¾8 y%þ
+z×tkÙ_45m %³fÈ\.݈WʇÂd¸*8€(µü1ˆ4§W 2X×b° Øû=Ñ«ßÛ{¦I°"RçÍ1ó™£9£(œ ÍуÎ
+È…+«Øk„ÈÆÿt€‚ü ñA7ÂÁ…äA5j Æþ NüdÅîV¾EȺðÓŸæè7áâŽpÀ>9ÿâDq HmAâ…Sql[#©”¦ä;è,ëPB  e¤Äi]™èU.‘<(=tDÌIš/)q…ÀØ êÔ eòeÒZTÇXÀD&iý" ¥Ñ¦­•Éuï¤Ä´‹È·R*‰[jµõE-
+zÁ}”Ì€þýM‰ -Ë(C™ýt'†B)6}X’ÃCH‘‚vßÀ·³ëù[ž!INiÐo~ñl)jO”±T„ 2ëÆ7XN‰½D´”v¢å”AMÌJó»ˆÀ¬`y“å/NBÞ™?ƒÞ¬åÂE u¢•Œî ªÄÍ+~ig ÛPÖRFTÈÖÕU·÷ü5Ï,«,èQ¡èX‡¹NZc…tiw¤|ÿ¢Wˆ ½_¤¦šeA?Ÿ Ìz Ýúùy™BàæV;铳Wꯠw ÛÏB w#ÚÁy‘†<gûÖÐ^.Ð'
+vl‰ Á“ £ø[BM½_ó
+ɲJ‚ö¡ê +Å6µûœ. ÉÞ ž™Qöû¡.è†R,ë
+jWPÒ¯‚Œ&Âa¡NœC¸·ašÚÏ ÈSZ
+GZCƒ¢'Û¢:dáGy5Pï‚XÔ†þEêt#uxš±uzJ²àúäójã{”ˆtD·~aü>×ESi9DS,gü¾b Ðå#ŒÏ?9#å_-c|0ûš
+yzúSÐs —ÝD‚|SìslÄj·®-]ƒe_4ƒ‹sÀ?è­gó°b¨@$þâÍcÈñUÏA
+‚[ŸM­‚ÔÏ^eE±xÏZü2n¦ÁÀTcÄeÚ‘
+˜ñúq¬›ÍØ`Šðæ^)Àà«4ÀcÜð–ò¶‚3‰—÷¥¢fR`8A)À†Imp5=»ºŸÚ.A’n‚‘=ˆˆ‘ð­a‡Æ¢34šõ¸ŒkÃW6rÒÍ ï¼<h1<Ìj4†`¡Û™oÒ B(ú¼ †l§­NÞ‹Ñ+‡¿JÌNïx,–ì|Îdh<SÒÆΘáb½n²ÀßMDgÝæ½8øÅ0Úû|þ{ôž‚ö|š%Y(³]ÂÇË4Ÿâœbá)?BùøôÇ‘8ó0Xd3€ÀˆÄ@ûcˆ6[)H/‚Åi@
+7Uö?¬©åÃ|3‘–âý¦£ëéQ!{>ó($  D‚§sKŒžµ$­,"–ƒMæQ] ’
+À‘•O—CÓƒÞ.ð<_&.½
+J@WN)?AùôtF¢Y¢…¬†ëÅ9L–3Å9¨’‚¦R±¶CÚ`$ÈQÛ¥ú™µžäÂúYöUP+õ£Y渨­mbª³ûÉ‚^$">X n˜™“[ßœCÝÊ
+m/]êò±Y*ÊØË á¤•¸q¢Räz0CMn³gŸØVK!¶h[q>±-ûçÄ2%FÖMÞÚ É0I0w=
+ÄÓª{£¼W’@Ôùœ.+†œ*ÃrÎF«e™èP%4†Ð,ê‡'ïéô¶ƒx;Öu²’DO îtËÁ¥ˆ!Ë,dï2S)œYÅõ{H—|àS²yÆ Ð¶f•©úißÔ¤6ìS»%°áÈ\ÒÂBˆâÕ¹ŸÁXÚ'°-sEˆ@<*kéŒV{”ÅÊlU÷«ZÞÀêA*s†,7wC
+)|O¹"c¾0cÔ|u29bI+cž#_HÔ 2ƒ’ïáï†ü£ïrr(ƒ¦-/ó(û˜"Kp,Ž¦xVòßåcq™‹7Ýá¢U!‰wZy/Ÿl#ò† tU¤ˆ|=¢Í‚–èE2@f‚#gÝõ%yº–ü¨K†0Ø!9“·ß/2«J˜—Ä1Yµ£¡ÖÆ8Gºd™V$“ŒK`Ã)û!p@AxÐæ8W* ~¡/ý½|€Øä¦_%¨ ‡Ÿ¥Ý§"Wø´å"'ž´©õý*LEÁJÌö£ê?ÿÓ©âÖ —Ã.VÌF¢Î÷”µCš6Éò2*m5wK;a`'øÂ1½Ý¯b©Aä3¥oHA£lÅ©k\àïÕÁQº0zQ7ëâ`‹uqn6AþX˜¦8o6ACCJ×=s*ÔÂ7gœKŽÎ~$BŠfµ$V¿WÄUE€hÜ”½<!äG!øcŸCÿaVÁ“BX¿ ÿ ½¤mƇȞ_Ñ×>‘ ©J°‚Á¤­ÎÛ3ç Í‚5ùÖax(CfE”™»F l1a™.Ô-®
+Ê*)ÏõPea„
+~XDÏgåš·LÙˆaÞ5Îhc„ €“E/cuí~•ˆéÑ OþÆóçØ s¼úÀmì‹–ˆæÜ©PÞ,mNû.ž6 et¿§ªVlI6÷äñ}스+[œ£áÌä¿ÆF:CÆ°@×ë1_ÿ#NcŒŒ+Jù~UÖ ÅÛ+QòjC¶Kš!†ZPUš·Ù„/˜œÿj;›Ì6ËV!ÀC`QN<ÛÞ«‡àX¹¥Ù$¸P†í.h¨.m*"©m¾tZß!þ —â-0¯Q÷›° ›ëUµ ÑîàÄmt!…ðb£ZeÄ£ä㱦¢¯@Í_Æ1ÌÖm³¾öžv¯ûM
+§(ÚV˜JëEó%ò%’ÀO0CA´].8/ZÜ]¦aÅcÖN\¹¡º_¿ßôÄDiZÎ/Nù¬þµOáñ”,¡½’J5•zÏF½ç‰¶uUŒ ô<`>nº§ºW¶¨µ1 4x‹]­xnMÉÆ»Ù)°sð¼“@.²›lyÏÊÍédÇU¡Ù(–‚îd·¢r  º©´®Zâ5$ûò³ YV
+u–‡ å©"™Ü
+zÛA4—+>^fðÝ…rð;㘲™coDœª£§›& —Rž_NÃI!¤Hò«X
+b%€‰3ò¼ˆ$‘%Ä# %Ë+ÃÉ蹂s £
+82˜–TªÜ L7Ô5»”øŠi9]v Ç+/¥Kl‹±Ž<ê!°>ª e³B>]~½|¹üqùðÃåû˺~õõõÇ/—týæçˇ?ù÷[þ~Çßøó›ò‚Þ«6N4ÒÙ©ñR‡ÔÑ# ù»‚ 
+T¹§õCÌgÅØM>\Ëh’†Ò÷ 8ãQ-‚ÎÚÄÉò!èíÿ¤ôùòËE‹œF2Œ£y.£²Qô‰Å.Ë3'h`KpL½²¢b¬"#Á¢ˆÞêÂŒ˜v-–HªÃ É„i°Ñ©7Y€%X#Ô2BØÿy<M™üë Û§4™ðø7ãå’[WÑ­dõ§Æ™f™:ûŸö)‘z€ãû:i4h›¾’Èb}ð'ÇÉ©ˆñãHk
+©§h°
+%Jœ¤ÐõTÂ*ÎÀ«¢Ø:Ô[–æ:]Q4i¹®ÄèJ ™¼±6¼$Ì@eŽú°ö O›ñ[N“ ySDëiòÖßg'É@Výd(áVSJý‘Eî„(k
+`E‰£ßž¬‰Be
+ãMɧMøÁÒX,M•içw
+,5yNž{î#†Aàf ’mñ|%
+:œAºˆ•y¨ñ$víÞJ¡à,Eæ¶-G¶¶„d…剣`
+X”‡õLøIÚ*êïÒL ˜ŠsÀö‘)´+˜"X– ç«™c݇ Ž¿b¡ Ð;N#Š†¡Ú§WžúÀìâ3Õ´c,Ì’AÿAc9nG@‚À:Üá6ÎbôõPôóM¾’#]qÉ`ŠhÍTÚ-épñ²]¡eThšxX¸aÝ„F€ëX‰Ü^ ˆ{E»üG²ô[>àšQü«â,­ñ8j³ÀPTjô,õ«`{´È|ƒ Þd…'¡£l´¸ DsÐZ¶Q©;wA†ƒ‰³¯ˆÃGÖȵïóßÔ;¯algAD\dÔ“Åc¨×ëÉ% “»l˜ë{8À´5Ó/Ôé~Ì­T_™^„%÷åØ¥Œ´áó(6ƒ«V/Cþ¿Ä¤9ÎÑ=}f2ël#®”êZB‰Šp~[ô éÍb:E‘%Ï·ïõ7DÙ›’½ùLͲî•iq(ÚQ9Ÿæ7¾±ñxàY{0 ¾ŽÌ€¡‘‡I
+W ‰eTÀ%8|Å~¨8É^#ÿÃZýÍWàoh×2²ê&Å UìBÉ›À,òeß˺Ñ!%-9´s´)ô©PNS6” ŸŠQßN I…"Ëm/€á_Ã^Ï7%H˜@íˆu÷¨/E° ÛK)ãŒï˜¶:i;}&w!3)ZÔû*~­\â¥_ßlú(oÝ·{K¸å¸Vúõ³ì?Ž{E TƒÉ„´Ãêë9àÍ“ZÍ9¡ŽŽ7Ù%y» vEŸŠ4F‚(Å/qö S BÂ(}‚ 6VE¸Øû áy¦Ù!Z È·ç=ødÀÿºMgéNJ4;%cŽddÐ×Y)i@,Eh†‚ …lǃьQ„ö]>HjË`/ ò
+(§Õ¢Ñ™·ÚüR6±K#DƒÏáI·rXmŠäb˜ˆÜhèáÿhª¬º b}D ‚£ïr5%E¿I— e7žäAŒ×0Š”EV¬ \0‰
+ö?ƒÝ+)‹"¶‰uÆb‰ÖU$y ï #ƒ$´á•Åë¡fªÙ
+»ZÛ·’MœÕ4¶ÍvâB(‘ê•Uó¾Ul]g÷‰j³Áø‰óC9·ÆáÊÊÊîu…U¼QiÉ[Ó—–Ë<G9¦œ2â?C«„n·Jä‹(Ô°s·2²×Ç¿àï'P5Aõ ¬vù5mNúw±íR¿^K®©œÍìêL6fÇá‹,‰›“g‡DsÐ(Ý7c£hm–@3òÀûxæêܼþa¿Ì‚SÔÕéÔ—Á×`DÇg//nùRÄ—dupÛý°B׬ÑpZÒ7Súy§îa+ü¹æ  H°+ÔŸ„9Icuö‹5À¨±3m£æÑd–g.sÅaI7ÝN¢KHéH ™4f¯ó…-p$ý2ëžQVÁ‚'ÌšLž˜Jœ¸
+Œ W&â÷Ø71d#_ä‹ä­
+·9•ò]{ØÚ,‚˜5PyÔô¶·–¡-KÒ˜ª³qµ$iÊû‘Qñ éÑ4E8¡¿Èzâ:„‘ ±Ž7qˆQ±Ä•©çUË[K¥5#ZAFÙù*v‡” Í¥MáÈ ”YÚº¯¢u °¶Ò9±èd¹Í†”cŒÑ‰¹®Œâó™“‘»z6¸±°Xª‰l ´6Êu;ì`“™Åb·H‡Š`ˆà™CƒXa`ÿ º_Ï—è@tÊ
+l9m^.“¥AÅ@\‰M†Rþ£¹n
+4¢;°ÿ<œSøGjmËŽ9G`üGC¸*šô“Ê[Êùñ×=ú)óNæôWÉ‚ýµG˜!{ÇŽAZ[9¥«£5÷=æƒ'É T®ãy
+H‰Œ—IŽ\G DOÐw¨ ¸rZËKÝB€Wíûoý˜ä/@Ý¿` PÛªŽÊ ~ˆìþÔÙe “½÷ã¯Þý9dŽ¾ù]Óýøü8 &m‰5ïR]Û׳ù
+×­¦o›rX›×V­¡›­âõªîÜrñÁ¶ÃVEchT«Ïz2–Ui³í- Ùmk Aå«ö£R¾±×4Í
+«ÓO¬Ü %¡þÀû] gcçjì÷8ÇcŽíybyÆÛZ˜æL"ë‰Ð7º±zO˜6ðDz¢ïÜj†hå´.éUTÌ}Šƒ¿B8þ­cUýVH¶µ‘[qíÔÓµIQÙB[ìZû‘·¸Ï;m®4 „Æ›[˜ÚL¾‡ÃªÀ;® 2žŸzPÅw@£Ñ“—ÑÔÊ—ñm  éuùo›-þ/LG®ò„ü0¨=/߆EÑë|«]gÆ©´Q¶qÓl>ÿ¤#ý¤y‹Z
+7!¬½k\íiXèŒ÷°y»ÖY)*‚]»~øŽv‡F©&£˜ÒÂÊÆÑØWÐ矜©ŽÞ¢4,C…õ¼=•Çu
+NÇÈ£¯ ß‚c^%” AÐu9çÏ\‰6²'ºIáßcXO ÿ,Â!zÌBàfþ3wMhèÐEû{2ç£æ“Í cŠiE^ öÂPÅœa
+1aÒ=…
+P8,òj­âÒâòtP>— T@°³Åð‘ŠK³¡ü`ê(âǘ‹òȯÜ
+±%Éܱ‹TJ«$>x®ÓgþŽq­ÃCÃ[<Î2©ôyÚ½
+ݹõ^Iï(",‚Òò
+&m*eW(ûºzÐfEÕ‹#´kEkxêuuº€Rì6Æm0Y‹®Ðó¶è›€È'‚LsÕypU#HFFŸ¹Ž jòWÓë4ÔÜ#°D¢×­E5íQŽ]‡v©SÅšs«ŸEŽ×•$ÃQöqš‡å:$ò©í
+AmñÚh÷B@ÄÑ}«—âÜKàM×ã¼y¸áŽc¿ãéä{$žjä;܃¾œøF;lg+¼ØG­t:φ5çœ?ÿDói ð†b˜F¿Ö
+~cO´Ðt†LÃ㜷¿”;ÇdJ 9—£-¥)ÐÜ‚™
+¬7Yb9•LØ÷ËÝ¿e öÃIi-ÈoÌw†Á1XN ‘©¿ƒ>ÿäLÇ0Tp©Å#íëdF­z!Y4¢3°%‰}ž!R9.+1¶ÌˆíZ"þBB¡Óa=œFÀÅ°q÷ÐÐïx¹¡º)ü‚xO •ë2§®Jè˜Ä­jJ{º£,Í‚ôC!­Æ9ú4ó ¼·ÊJÿ¢—GKðših*-‚LÔÜ «<D/êóÑ´1–Ó#àø(dVü'*‘ö!›®Råüì<ñ®îéŸpÝGÈ
+FЊŽo5€’E†:|`¥D0˜Ä0‡ŸÂIzlB™f¯\ˆÐ-$è„,£Ÿð6»÷Žq
+Þ[*ü?Ú„8Z2LÑ4n cynæ¬)/Ïà¨Þ -zœ÷‚OL?r¥Õ ¿O®˜É/fR4ïÑt«~)Å8ýzô°Pdï$†XM ·Ûžì#UD»7ã ícEv©q„æAxd±<4†ïkålDhè+Z•ÉÆ‹DhZˆi±ÜÀMÆ¥š/üŠˆìÑ—¬·¬žœel¡$ån2b1¤žT"2^« ThCkfy¸$¤×2´R'ðѹ~¬ðˆøYiw‡¦! f” Qâž²n1°ŒÊŸtÅ ß„7yïcáú'íw‡e£=Bá¨x'LÉLrî8–~WHÖ`CÌ_I
+l=NÓ稧ŒaÆ©ñà3AQ¨CûÚN¼”(|¯kñôlÄc¶Úì<C¨b÷zÃÞa%"‰¶0Y/#àÈ8à,)€a 1‹$+E
+ÝøŸF˜zñJ°.ž]–Mâl°sj¿:ŽPd4÷‘7‡8X¶ÁruÚ-½#c±Ú¼<0ÄËÄååD%e³NŒy-|3ØEx3tà:Nƒ°hõònè ‚¨,ÌÛûÝdr ¼ qd®¦oNÃÐ:m¡«VáË­.~‘¶/ì( òy4,êœË4˜Y"YTÓ#Œ eX+« At¤ˆ¸Ê¹%Ñ'¼áµÓþÒ':#}j¿¶ê'î1"–%ÇsŸüÇx™$Wvë@t+ZƒØŒËÃÚEM¥ýO}’
+&v+îQnE(2;Z$ýºN%$úÉÌu`õ¤Y~(©RÞ:š0¸œÊÀE²($ÄVHik—qñsdâèäó=Žîº²‰:¿ìk´÷ÐWe,]_A-ÀÛR–;T}ÌŒ¥G¼š—´É¸N(Ç!ü†,å6a4í<Fœ#ƒwô6û9ÇBÙðŒöæÛ@Ú®áVÂçè¹³{ë\õ­ˆ•ÂPÊ8ýŒ[‘–¨àSè—Ø8Ë íæ%"c˜ƒòº¨Ê¢.Y±àHÉi×H·æÇ
+þ³M±tü™ö¹Ë9RÒ…?ë©B&¾tLû˜
+Óñ–ŸŠ3‹Ž^t{z ¤í"ªŒú@@E¼6L¦ÕÍmX@
+w³멨(ŒŠ—z?ÜLšÁ×uÍ4:+ÑpœãVóþ'œ±©åíoÁ‘1²wŒ7}@Dš€êù ¤ðã¡h€•e<"xÛ%ì;£eEæ8#I$‚%¶T«93‘uÏò „/d;øIóEì[攥#^DÇÌZuê™,¹}šwvŸÇÅrÞÓK¤ÒØB^ó„ÓÃý°'/ÚFøå( CG…)鲉̇5‡0¼ˆpÉ9„‘‘ã2´ËÌ»Â×8‹É9ÌñP¯è¯oþôKü‚‘ãå;*vùQEW¥Û*†¤@xH3o2dwjì"tÑdÛî½–xTù‹)45Øf~Ép°:¯Š¹…Š7ƒcª¸
+·%º²A8©õ@Kפ" W|8#tº²¨­Ÿ›Å“º$ƒUä/âœo(Nè}Ó¾³Ð%ÿÙ¹õ䋃…._{aŒ–ÑEPÆt>|¤82òØ oQcu[4@æEò`1u /Ý'BÉ”ÍVóQ ×¼›Ñ”eŠ Œ0C4-¬:ë1”zÕÏaegB–Ãn@6ub1¦Öã*|imªG‡ÊFˆÌYÝÏÞj¦”´iý`Äw/Â7ó¯^§³† ÝØNž
+I;Ó–£À’WLÞˆ,p¹Ÿ€Ë:«íõðñ—ñ"ÀÏ“Ma ºÊ&9Nñ`Ežø˜YY³¤Ñ·’»¸›Žæñ`{„ŒŠ/£b]]3 Ø 2'–n/Ë‹S÷‚´”m
+üÖž,f6mÚÀáØ%OR@D²©®Œ HŒ-yØ`8½+È?ps¸¤¶ðí(­{P°¹‘ݱ–Í*rÞ–¦ƒùÌQÂ9ˆúà{¾vÎ(j?Ž«Þ(ô‡_ÕX38 ”×X_Á”WÖ€¡¦;6SdŸ´0
+‘“yó’­TDËÁ¶<DFl­^‚ŸàKồqpZ¢xLÀqÙÆRßjÑF¤1u¿ø1²o@—Ö]d2ãì*lo7Ú7áS
+‡[O§¨àø»:†ÓdG¶É>" *&Ðëì%Ef°ÌšdÑ,wb­6Àð‹¢(Ú5ë1­Ï"éI¡ãI` ØÈH¹Z=¿9RÖÒ
+ ýãe)¿:š“”Æ
+ƒöÝÁ0‰&ZšÆƒƒa˜’Éӕàß EÚ&·ìÑÁü~J##8L‘ri"üð"é« *œ5Ùöï}< =óìŠŹUÍó’Þ¶“k÷y„'âMã )d(ôzòöȣTA˜öXÄIdV(utW­Fn$”˜ ÄÚ†8& .˜çs'ô¼ä‡ƒ–w¸ïaµ¼çjä)CÉA!ÊIh¿äXè„jñẫCÐ%M²Aͱóm.úf3_X&öN‡~çˆmU¦a3e`bͪrnMÿXAtÃyK¨4E¶‹ ¾ŒaâyÚŽŒÊ½‡¿(Nê²Ð~Êìì Ô™gl3Ê€{^ò{9JP!¸* ž7öK›a’ï¸i&ö8•ÈçÈ/0
+>¼®å%Nî&5É^ÂÙ­„°OÏI>Gæ“rð4†®6þÅÍω`KV¢? ,+~1ȸ* Þü<V d(?"P®a·"1 z‰~>ý[ÊHùh„5¾-„ƨÇü±Í™‡ž’íÌQðp~¸ù
+`ÑòçËU‡¯WEA²d„Æ15±$ÓFšvØ`ð3Miè£n\Yü#Š”Âíוhl 5È.ßÀ€› ÑèpOy(ér“(‚Nð¸ÄÔƒ#6Hêù=ƒN1ú¦hçewA_–ÑÆ,ÏšN0—d!80€*ßf·¥Þ%Ez*{U£;Ê­ˆ,¶~ÇÄeù<z5JôÆBÀƒò«Ø`À#æ:ce‰°Ð„Bhæm—W•§PãèöO¸Ó•£þƒ5sr/n{Mið’Í9WuÖ†Óç‹¢‹D³€æähøzõ¯¹ëǽ íµ²‹Å»ÿa¼LÒìªa(¼öP+ÈçFîÆa˜]dZìÊ,ù…ÔóƒP¨|méè4XZ¸y¥ð.D‡Æ3ÙZ®ÔOÿ VŽþ8t*V WÇÊbUØ
+<hÎÏ…ù±J8@ÄKpÆ=Ýœþ^xgM¹°pùÉRÃcü¦7†¹Ø¥Õ®n”,IPvH.ÂÕá=XT@ȬÞÄ&¿ØÑ8H :_ÛPB‰þtÏDi+Vâé214fë —4_Ý©piˆ´sšø‘ý
+d‘Ø
+@}¦>þóŽ«“°*ÿ¸o—HÒj”«>”ü¶Uß}ù e¬ ÞµZøzyxHÏ–·"%“yI[L&MÇCœ8ƒEBRg—óõ
+ÔËCüY(:öôQ9Æy{’aìLŽÌƒAeÓ‘U[æ+£p=¼“Á&ÛÁ¯Ù˜^!B­3ð¯8M%E&¼?²1«‘ËŽ5æˆ7=ÌgÉ”O[=O‰uÈ(å~ƒÃ4,®
+£5|ÁĽ¤ìç(1 Æ
+®J€Cl/f…‹µ^áH~ ŒQ™@Í2æù±KH´¤DšC‹Ûa‡`P‘š—€‚z¼âSÐ?À©2-ÿT‘S% ÚT"ò·‚=ƒÄ÷§ä]Y(^Ýjœ“‘Hb¨ÍK  ‚-µWN,JŸà©¶ó)>‹Í+µŒ“ðE aõs Ö’œAµ¬¦5I';*©dbrE*!1… C W9iàýS\PB$Ž*%ÎAÙ=Hçˆt-rGZ¾^½Eÿ à¦wÓ09”¦Ú™•î³ÔØÁÑ™J©ú ˜y#p!LuØ5ã¼ð’qL`Âd`üÃ5¿ekfÅØLÆáà)
+šŽ@8QÉ€éÌþÜؾÚ,}–Ó¡‘øÄEC?ˆd6éX‰žGÙd‰©„C˜kŒ§Åʽ̜@øÝ×j[»²?þ^ó5>îŽ- µ‡çÛÔ:b½ƒ‹q#¨=žǦL”SDñš‚«¸1Ȫ¢uDß_ΪÉw€[;CçÓ` ÷$fòaq4FL˦‹í(]­Q‚‚“¨êRŸª¢d®xr¹S„î5Ôç‹kØ%Ú3|z·Á^é¾(Vz¡t-…îÇ0#úe¼Õ›CEÍZQòˆ«½MÜä
+¬¥®’Üò
+Ò\wœ—"z†÷’{pÙ£¤1‡–'ˆ‡
+¿2÷‘›—Èùaù3œü…b’ŒÚƒ ’L<CNÚŠPˆM(yÜ™Ÿ“4‡‚qMåjªêBs—vâ®QRðšòk%RE8Uø Ì÷q)Ú%  ¯1-È”Û$‰-
+¼`vú9hö’.‰#aæ†ëa>yâ "Šò/—_kòêó%exòÐ ÑY€çˆ qSÜ%8à<p€‘ Ãÿ@G½¸ùŸB£gJ\ “C#üá¬L*Y/OãL{³zWœZX=
+¼‡…?ŒöÅÕaضÍ/®)ƒ¢*««­Š²Œk;êaØ…BIpÙ!d0<#—I‘ûƒq'Üi-©Ûkj/ç (MÄèöîç0SšNæ!ª£]+² s§µï¶má+&'Ö6‰ çù+ìoÇ ¨x‡ßŽIKž˜-kíâG'àTš3Ù–'A…òMÙÝk
+)L¨åu0•EçiirÙñÆ!,øÜ3´A˜-"#¿ƾÁa”h8-
+-¼ ºŒéÀãÉü*ª"Óµ0IHqÀDp5 (JgD—»‰ûÕ³Š>‚Ì6c¬®îî›H+áÁåD–7å@ ”§IW¬³çèQã„7㇎}¶aÇ·ëØ\…ž¼[¨
+}Pòj§ÈP³”§T 7 QM¤hÅ
+Në‚üáߢˆSª–ø4‚2K¤œƒ“——[ô#Èÿ™Ô°1õMdÒþsú)‚¤ÅÁhsÕ·epúÇ™5`z†é¢rÅ – ÷ót6|ÕˆJh?LÑ•
+1^BÞßU‹ eg¢¬$?õöêÿµíwÞë&"ÒU/.}+kÛùÙöã!pPÜ€ÉXã@EÛ¯5ìã!§ð5“°@ˆgš®Ø‹Å$69Xõ„ÖòÃGÈ…°'Uø:Ô?#eŠ3øîô9F©Ê)ÖöPso EÈ „äìøzW1-â*H¨V/ýSMŽ‚Њ#]PBƈÁý~Žu¹Rž‘º¡óR—ÍÞ (á*؈”_Á&»á„V“`²2Q(66‚b†89žŽMiâð•gÒÐHÌ+YÏÓ£¦Œ±]ÆDzA}­IŠ.ýô„W…B+®¶Å8Ò¿ÊàÑ6'hÅ*hŸ˜•Ç•MÁ¦RZ“œ¤ÅKY”Ê9uúþÈ[®¿5RŸ4L²9%Mâ6l3
+6€…—tk?
+Ãl ír‰)+øVfqÌ9 ]Ï¿µ„ È0äZå|Š9¬…û–~¡¨+•ÙìÇ—ËÇòÐ.Á'*©6}ûTª1Æ'IÕØLû°ò ’Ó¡ #N²moH 3q@…Á8Ë)ûµK»1ÿž;"â8îØtê\y4ßîPI“‘;(¡"x ÌL'èVO±á /öý´e™:Lz+~õ:9$ó›w¢\ý²ÏãÁ»¨ˆ¡Æž¶C.Z²cEòHa)ãV»ü¡ï~ÄžÞéTH…2ÒÀ,Ô…`!-z’å4_l+üYë!püV{ØÀÃ&Ñ+’˜8. ÷Ÿ0Æ&–MSoPƒªÀà6öÑq˜-Háã€`vùñ±‰ÿIšÊõæ"·;YqsWHÒk„µ:[‰oÍ)x±;ì!iî°‹#Ò=H3âÃ*¥e°·Æ!›Òw¬¿ôNuYœ$Év{Ê,YǺ@ö§2ªOû<GyƤ8’BÒƒ„XÖÁÅcHxv’óÎñ¡ú’e +zÚ}Bd’ê6œü^¬—D)o)Z–!Qj?çÞï³(›@j©½hèi ö#;1T“4I^Ÿf'Â?¬O;–YFÜGD4@¦j²uDõöÄ˸Ø!ÃÉÚ0`C2ì½$ØêI'4ivS,ýå_Âjèh:ˆ$Èö餰}?jòQU¿°uI?7ƒóá ““£ÉéÓµOÌÞ¦—u¤\ U,‘ 6ñ-î³(\¦1ðdÞs:Ä ‡f‹1Â&í÷¥}u:Ë÷û¾¦µ^yRW¬Üöõ·Ÿ4Q‚ÏžÛضõ"Iâ/ÍÝûƒ Û›e9*†3Iæ}b“< ±8³¶fe‹gÙ,'¤<@®ê±Ìm“Ÿ3(Mî“"¡€32ø0p^- •÷’ê&`Ï4!Ò62Þ ­ãáLÍ-ì«jÁ¥,9¡¸ œ
+lÔiŸ«†L°ð-ds'øˆ;§PÒŠÀŠË<ïÐî¢ðýèV€~Ûîœã„$XÁ!ÓkÚU4Y´vh7ïÄ3Ž©cp‚ nxG¢?"t‡]\ê³ÃEQ§ |/ÓløŽ/k ÝÆLÏz*áIY{\7þ
+b øç/W ¤Ë¯Ê°w Eûxx•+VÉ°mÒ´8¨iÑè ¿É^b«ÂìÒîß÷’çÉrå¸2žß!À`ä±+h‚
+KAO毨§E‘üƒ)Ìñ-? #„¦@()ü7a!ÜÖMÉ ?þ¹.B6 ¼¼9ÆÀ§⫦ó¡T óÍ‘˜a‘!óƒÂßu*šÌx1È nÅg
+M –RúwˆloÛˆ
++<GVºù
+?3¢r~’¹ÄcÝŽwÅ+ˆ3LÿíßbÙ$ÅÆ°át½žÝâQ°'í:C€:ŽÔµqŽÒæ(&‡<!{AɸôøÎ5¦Q ŸIµß”¼Àáä$GNÝ¿dmŸõ»Ž}ùP/L*–‰R.Ô€rÇöL´7€É
+ãé£éÀ²¥^H›ÝW±c-y8>¦„1£H9ÏΕBÀ•œËçTÿÖhà:Ï ¾“Z.àÖG…Et²Ä¼p„|Óˆ$nI¯jLÚÀ‚‡ÇPàoGÄ£Äql'N!‡öOæᔹ8cGÞ̆U†ZNô®©íùðöFDeZTŽ ¾Ðë»L¿¡«Œnaluö’"l/®eFsàf˜¦Þx_í‚ReéýÆ?¼¯É5A‰Y6<̯)NÂyKr‘ÈœÛZƒ-å§~l”ø–
+Ò‡”Š^ý+pŸäm†=¤1\¥‹Ïè¹—`§+A+,I¨ÃûÀm„‚sPæ²Hù«„=&l()h1ü;LEÊÌ@€`žëšE ‹I¿²<õO"}÷£
+ûl8³é®d°4¢¯šci¨á*ôœ]j¾Y˜{T   ~¦özÂïAo¸HF ^v€³²=_!óÙ±ó…ÖJê‰_õŒ̲Š ¿v熄<,ľÃ+{P®ìŸà}ð‰íy(Ìž/„ïË‹žL3-:êÈñ±³r⦹Ø%„o¶’c;5‰]¹•Wdº>Å^5JdhJ&&Ð2ŸDzƒí¬‰ê×¼(úŒ¢Š“ÅúH½w¦\&vcmªÄ7'äUTPf,¡ZÖe™â:Œ
+Ô°îU~Ò6ÄêÔÏW”dšFv1&Gݘ²SÇ}T‘°-°ŸWÅ—ÉÁ†” ó°à6@òp ˜ôÌü¸YlÉ*ŸÁ Îîe{1.—z 9Xpôrð…cÅ„žús›?þbãßÅ 'Ð:¥¨õ8
+!ÄóqûEŽ¡çt8kpñÌßDo§$TçŽzƒÝ¦—ôͯ‰ë1¸~~âø+«>QÔñM–#Àh vº<ļ£O²gXµ%×ÖŠ>â›!x «í{„xA‘{©ŸCWºßÙD¾z¼5%¾¶¶°(‘ã4¹_:v`D¨"cµñEøÇÔ‘_(¶8‡3IF0AËöW*Î-¯ª ‹åPüHãG<d8UËíy("0â¾àÜýûå?Š¾Ùx•èk1ÃR~€KO‚jvKæE¾£‚ÕÙÎê"_/Ç<0A¡íÚÙ¥QÞó˜.…‚M<vì¾þ‚R±hC ¦¦ãaC>³H{B¿RQwÀ|ŽG,‚_˜ žtÉð‹’9F [
+)¸§™ß#žr»ì^¾$¿ék=„”S‚Å
+b-‘c\Þˆ²ÚYÏdÈA9¬¼õí”x,LaÎ “Ô¢ñ5K´´fÕD¶”o¹ü¯«`+‘nϽ`4¾ë§bsú·¯sÃuŽ ýŒ|·lDÊy”´ÇRmi6)ÂñïE¢¨;¥wK°8ƒÍM vM’²(ššñ ßPßž¨ç[I’†+ Øz(J'Þ™'!°E¢Æÿö¯"ùî¥p’'¯+â,;\ÙÏ"äGƳ ÄcõEØy#Yî}6»Îc›bdâ
+õj%eì[Iâ+Epó#úYTYpþgMt>s‡@¤òb×q¥°GMªœAf<_…àKT1!£G‰´›\P˜úµõè\8Îãé –Å:2Ékþt”h‹j>;Ö«âê1¤.ï­À0‰§ù*4K‰V:4~sV`®uu—f°ëÄ‘ÏšÚ}Yÿ/ë ?¢œ¤©‘¼Ã"²¶(PØ€ëK\D&:¡ðÑúúàSÞUNë˽MÑ'G ;a U–=eò6 4®Z¼|âU
+ œÐA y£—‚Žsa*Žœ§vtàNý^КØ{Ì°,RÕ º¶øŸ¸ê,Ť%;H2s!—ɼg‘p#³jÑ•É$Â+SˆŠ°µPü”'ÉáòRùÓü¢—ñÐ¥†@³=
+÷Š›kÉ•ãÙÙÓÝúýæA`d™äøäæão4é˜-2ôÌÑæ«JdµÏ(»h)ƒö+¨dV$ˆ<á¨ý–•èóÁF’%iG§@º ¶ÕF®™o3â /þh>}d‘¤˜åÈ.ý´GÄ×µ åæ'£?Vpc=œ !Læb(®ø©¤ýäFb#·}iWQOYŒ 1¸¦ ,Èéê©€1öV0û4ÖkÝèôSIY'è@cØ#å–äÛq,üZÎ~Õ_°‡Vpe&ïw¤Wáe«Êq 8JÒ‰½RRl?XúÆ|9¸\lãé+Žé´o2ëó1|/ÎÔ²:B™0
+ LÂê}+zQÔ] W¤üÉp±@èMk@û¦L‡2ntNÚÍlìYþ5 Á†b±¼Zzå¤ë¦€¹ "ýz4:&MžRã‹…³Ú^‹#ãü t82Í™&ƒÑ€õˆ’©ƒK®–Ͳˆ_¢‚´hï&Áêî옢´RV¹Ç»)ÂÂíq¬Á v£j<5(ÜL ]½ÁŽF%ò$—œÝ•}õŒ‡‡ä3qÃë.7Á}ˆB­HVÆ–.æÓ'áõjîgïj—!ÿ[c”¤êIRR d⡱ëaŸQ$«Dcæ°ÜcÎË Yà·SRDK•‹Ãœ‹Ð@h-Ÿæ@tK»‡…ƹ 3ŠX?øXi2+wÅÛ$ª Æ ¥S?J>¢„}e‘èT"^À?åó¹h­¦X$*à»BI]å´ù¡¢«ï¬¹#ºgZª• , “{è!ŽqL…ìÓØOñ'ð;ö
+TM l54ÿ›¬}>vðG@¤Dš6á€þWYId"9‘’*gL
+²êéœ0q.;ÞOã¯H ¥$΀/y ¢1o§d@ †u¾¶€ÿ'NµPÁ@Â>Çy@bÛÁœÄ0¯R`ca!¹=É)UÍ8%dYD
+8â„’s
+JÍ}+h×J>qç- (êƒü0þ-¸ÅäÖ¡{”ø„]¸FÇW»ê‡õ$O°Óù®)³Ü%Ç— ¥—h¡\kvN‚Ïé`2ª€Ë$š!zñt¾éßU
+L° „.h±[DŠ19c¬8‡Uäan¯Úä÷˜&»mUcØõ¡è#G>®Åe ùˆ‡ªÕÃÞ²b”£–XçŠcjn0AIÈ ;KP:t„êá‰-ÖWF™fx¦&‰N𜖬MÓ1=.ò-aç:€€*v`ËPBù¿jü¡_>p†[þÚîÇJIäHã~òƒ¸kr;s€¢K”íž%‡þ›ò`€s…¤h²¥‘¢#¿±Ï1Ö²ˆ`‹è²û~Í¢¿
+I\ÊHð#³›)"ÄMW¸[?Ž•‡o²Ûî÷árYPf³EhüÂ,YÆèßYpe†às‹ã
+ðµ£Äw8@ÃÇukÃ…pé=îƒ*Œ~”²¡Þ)QB)œFƒ£Ä´PIùW2n{ûîæD?-ÏîCîÉ¿ë9§aäü¢9ÅÍô‡Dhzb°³TP«ÕZ|Gú‡ürÛ9=D²Ib,
+ùEGÿ£†8’_F 0-#ñª9Z†w~¡ëûÑ?À;¡%;Z*1xjêfŒÎÒxöâ Õ“î‚] –#³8M†cÕSIɉ¨g*êr<ƒ2ˆ*PÿNàÆþ8ÿÎÞ–·Í­¬ÆZçÑ%zUøË–v™(Û¡²è9t\nl/;n„+Ъí.(1h›€“AOêÌ/ ‰çÈ‚´¿Y/
+šû¹VÞ>‡&„1Ck&§´±KC²‡@ú…w¹VÎíiðO䶺,×õ
+œ Å‹
+Z¯mõ3z¦¡ ´KzJv¸ Púr@zéAÐ:ø|åSH…t J¸“rý.ˆ¥
+ÄÜ‚Ö“Y@÷èC“£…·Yúå[Ä[D°XØ d|¤“$0Ȩú¾.! Zâ·naȧþ{ÒÊMkÒH4‹9ùfÞAH½k­Ÿwýt]w*$yä˜d­ØÉ0ÿOj”ô§|0<¡ü²MK¤|~ï—Iü´CØ(jyÓX}§%÷­˜8¦íè^sÈ$uØKm?Ï78Âx1d¦„K·m!¦EDcY~âüÐœmÁuh‹7²!ÏQ¨%Ýx:'4öëÑ`YÇ5ÈS‰QÆ õ)\b¢SÔa)À®3pè%±âMHz)£Øzñ 
+
+å£4~c^ÎPd__vjÁÀ¥YV|†–Aa˜¹í‹BÔѼºY º°”«0Ç­Ÿ“¤¯oO1lª¤ íß¡=›ÊО‘‰!Á=2Lˆ,¨Oż'˜Ï–P Œ‚c AÜ}Mue‰8Hà{Ö ?¼œŒe(’Ç‹ ·JMÙAå"ûA‘•ëf/BuI5Žnç3dŒåeº K*!
+ïU7ÄKNy22%wé—¡©`q¨‡È
+"‡Çb„ M±‚l¡®(E«Ó·Í¢×º*}Ö
+3)ür)
+6„‡ ÃÏÀ&Øñ¤¤áR—?~«“EÈ5G êLeÙ•þD•d)÷³·ÑÙ©rÁ
+Í„ ÌH©ïmAP‘sƒ ÃI‚–ÌqacèÃoÞòeÙµ©mFû±¨6ý¬T
+#í÷¥¥«/ú™ì7*™¦ÖGE"é¥Û ¾$ÿRyE®’ècÚv™òÙWåª(©ïw“dV›È®×˜N¦!Ðì¤6¥»
+@M¯s6W[¬Øµ~,?
+Ž(-ᇜ|­"{ŸX{O‚´ ".^î h×UDº­w
+ðL*à'lÍÏû<ªy8eË2‘Þh|ûÝ0JÖ èöã ‚±=7»ó«¢3èóZH>³[NÊÎWe'øýzï‹0Ø“x.Îà~¬"ì¡ÁHj±48·’¡{Á½R {›<s¬¢o ñ_ýãý0Ci¹0¢^®ë·æ·HY¿â Y†ãüygÝ ú‚^Äæëw)¹IÙŽ×6½"juYŸ¸<”@ø n—[7Mnµ¼ý˜E|›æ2j%+®Å“:ò]ÚÑΡ:FvÎ$S‡1G¦G[Eñø-#ª¶—¢¯ßy]š[m²E¤¸Í°º1ÄhËähOÄÛ𕬃Ý?ŽgѬ*]&ßÃþåCW´O%:½¾-:ñÃBúZì±%\‰—?Æq18ÉœWû¶d¿£°xîØ®ù8ˆˆ·HNÝŸ¢ÁŠ7ËPÖèdX
+'ˆc)©úm^à´?¹9€é¬pÈaäQ“eû[ !°6zìÂ&‰
+ÓÀþ P1‘Ü”ë +÷]ѯW~Yúgü¡A‡@ê·E}C å\ûõË:ýÆÊOb Ñ
+ìë¨_^¥„]<PF¶qõKwõ@6j¦/-NIƒâåýmT¿–ÌQy84ë\¯ÂY¶‚'ìû„Ÿ«í|y¬özUg
+Y½3–÷0õÍýëé•gÎÿøŽWx¾äÞÍqoâþõ;WšœqúaÁùÝ&ì}+%¼1ç1nά0ž¾ì÷"hŽ_É»Xù#%¥’l©ç¼¯EÐø¡ê<ê(Ùâ ™ „Xg‰o˜{ À17ŒK+Þ4ýø©pqÅ[âå8j÷ÙÅÁ¢èr„Ù¸ô>˜%–ìÚÛˆ­£pcéz}ç›P6˽©{èù¹ 8ÁZÜê=JJ“\fÏóyxp1uKfp’"9}‚1€.<£õš í¾Ólœ9Š8p½„²\éåÇ8JêXôæcG}4Ðn߆êã¨f0ÌcÓ8–ר–ø„ F÷ßI ñH¬³â´{N—Gfþ¤.LÌÔÒÂùÛ7¦ªôúœ©ø¨!ø™
+Áehã…žƒP˜ß^ñ½³âL?둳×I¢éVis)Š€…ãŒxKÈø'eaù¿`mYP¤Â@„±î×(’| ÈÀ¯«Ï¥¸ƒ¸÷1*à/z®éÛªH@5Ð2ð—’œDìnÁqqOäÆ›òÀFÀ)tnHïÔ
+¹SŠ2+’o;Í?¦!´ó!Ž+”…>ç£xe݉ºž/E?Þ^Š£tmÛ§@æz*j¸cJX*,29Çòœ[xj_ôÝ¢mA û˜Œ#\;)kæ Ö ˆ_} {§„q—Õ¯ðx¦pÀK ê3rÚ)Ÿ¡1˜ ïŽ
+½e§¯jÝóåúÄMïÉ 3€—»ßcCæØá÷ü¨öÀ)‰¸)’ç×ò²6_³Øô~¯’ÓŸ²d¥2éM/êøÆ0zJؼšlÁ"÷ù œèÃû}ï‹ ²…=m÷¢7K®×Èv/+WÅK<å^çg„n¡t®s~$!›O}¼½qzôãç¸9eé+´µ–a_ä¶ñ¬i±P-ºXa‹(²®bZ›sH»¡Bˆ»ž<ökôà 77ãïz«¯ Ò|ñM·¾m‘Å•ÞŠ(µ¹å0«s6ñî/ú'E¶}$œ¶&e›÷hŽj,DYÜÿ˜ðùMøÛ+„»E4Rd÷2eÎÓ=œ)">œíùRôõ;¶ îáÿõ‡å”ȦçlCNnÁQA½C»^ø/5µ¢Ž p÷ßk~¬~…`#|§€°ÚåŒ|AŠSq *]Óòß+Bgü÷>Zµ®m(HJ|)úú·¥ ÛÇü÷ÿøŸ?öÿô£qÁ:/œðXÃH&rsª;öírTƒµ)/ENFô~ž¶q~GÚ­Y¡ƒ1%Í>_Qųp+ÿ¬¤
+ˆ_ºJÝïY±^¢Ñuñ3&èÕÊ®Ë|ñý*oÂÓ”ÎLÊùU[üøöç‘<¿EÔuùuá·êÛvs5ùG=þȽ“ 
+Ùrš·±ÆôgN®ø
+ŠÂÔr;çeøªœ©|1x\ÏèÆÓ®ÛÓv]ÎÇFJk{œÞe1Á±nÇ$Hˆn4Ì …ùJè›Ûµ½?#įH5{ á¸pÌñàlïÚÖ—÷º#¢ÎãÕU9'hs9àÚI'¹½õva:é0-l£Q;÷LÇó“% –*¡¸”fÇsùzrÛ–hÿ#$‚|Ʊ¦çnÈLícñ£ê%§¶mÌÔ$„#<³i;lqv]õ9(¾âºóSA­WÁEm*ßÛL¥êUUz²Þ3©{õ¤ê¶§¹»Ïù1÷˳¥Ý?¦=³º‚‹ÈF“¥TCˆÙŠmˆóÑyÖ#¶òîÿÖS¡*G¡+¡W†i%ÿõ 9^€“Êfå7*$Žu9xã’.ý½ Ùe§­«Á$˜·_yß
+H‰Ô—MŽd¹ „OÐw¨µ~(R$—†¯úÏÆÕKßߥ,©r=ó™n ²"%Š ƒ!×ð&6§[|ëÍìš*ÝÔZö·ïñøí>S‹f!Ó^!
+üt
+ü”òöÖ®é
+«õ…ŸZdñkú¤Çê]´ÜÔ› ãu½]=j/„“>n‚,¯ Måo[ö¾ã~ÊÀ3æýN––¥÷†GÙéú ׊DѧG_‚1KÜ„òjj×èÇÄî‡gËÐj7‰yó Òtâ"üMÐ!ã3qþÒ„•q,1½:%“Û¤sSaTîaN•{IÀ-¬÷(dÕ}ÝsQžAïw*·
+<™Ýd¶8àté'.E-+;ž ü :ÕnF^Á•4Ö]ЩvÎû˜¶Q ¨|Òœ/ Cñê6ôŸ: Ng<CŽ•{~þ-Щ¼¯%9”÷ëºíòò]Á¼¨èÔvê•*ït\N ’ó³†z9`è\Ù ¬Ÿ|¦Ï ãuŒ„) ?3’øYÿ>ƒNµ{¾íæX–—Ün{®ÉAž¿®Û.¯Î«À\é8b
+æÌð4eB”ý<œöʚɀðöo@ìŠ.Lÿ>€Á‹öd€“<ëÒ%EÐ\\
+™T¤ÈqŠõqÕ¯ þ¿£ëŠÙ²GĈ,Ib\rFÖZ5ÛD–׳9ÚkÃì U€P¶IÚ4²>c_å‹YÃ)xÖÔÿPàâQÊómUsŠAl•®Õ†ÿ#žþA¢…­ðÖô±¨¯ ïÔö„Yä„”Áh¥n»íH~‹I„›ã^ŽÒ—Ó®`á2üÀ#1û´AS6‹[¯.Ý 
+µ³¼HÄ3æý„õì[,O£ª’üñA-¼æÈ+ 4ØÄS+-µ¾ö݆3f…2RŒÕJeï›|¥‚‡5\ÖCFPCô‰ì{&W¨¨eíF9¬y¬«hÖZgpÂîv<~áoÚþÃÝáXF°-yßbãš´jí›±ËÉP˜KóV0® Ð>k·$3왕^žä» hæd°iÒw+¿˜“ÑiÑšž hSÔ@@&!¶}tܯ §ÙÃøñètüCëR´‡mU—Ô 3v±\e؈À\#…¬yº‹EÙ´õ‡Ú—7ˆ¦òØ!è(h¼ áˆÅ†$„1:c #Q%bNcŒÅ_¹*«Vãaéx‡ÉXéÉýúÁ†,EÜsa¢#ߡƶX5ëÍô†±ëûàg21Ÿz>š;Æ4’¬íÖ«¼–®ä{[‚µ^†U{|Eu)é¥tñè&gq­^{RÒÑÔ´£È©.ÿGp4?zûP´‹±^½ÒyçÖ+HE¶QøëÛ+*Ù–Ϲ5(2*îB¼Á>ãQá©ýC¯˜A°m¦ù6^¬’Üšla¬…riðÃÿ­eª H+»—H§•5ý
+_ãÖM`" ‹¡_§ÚÒÍÚâ¥C‚S8³ ¿Yzâî€0êö¡‹¬ù“éBqåhÁ¨H+¬
+4ˆ‰æ݉¼ºh)j"ÆÏq%!æÊÜ®±~î‰ÐëN•¼Õì7†ñCDVñ\‡iZbd˜ÊS-‡›Ü[°Ë=
+§ad'âЂµ‰0ÑŽ^
+p „EC…“L{×>ìãN=9c^7ú¶»‹a%”LüŽ?ª’Ú‹yv^ÑWܨ”gP8¿4h™=©ò>·@áqÍž™­ÄíËæßE­;Ÿv6åR€; è´sOžþ¾o»½(,v`ñ3uº‡ÉÜÍuá$¼½ iOpít³X¹Q8U4ñ
+šä[kÅßÁb¹Ij«®Àׂ€ÿ«s¤r”¿{†(”i¶Uì™*Oƒ¥L©K½˜]7¥Ž|'Ãì Χ÷arr#ùtš‘•˜®¼pµ.“a)OzQ¬Ì;pP²e¯
+û <+OLÊEßa@¨b#ª ýáÉŒ¤®¯‰é•— —¹Õ??®K×y"·ÔìÒ—§ Ý×˸ RB
+@¯ bõÈfd&m9‡qÆ“
+\@ÁÅ/§ÿþFûâSF©k&JãÓd£Øý¶ùÅáП±MK‚*uLÄÀŸ6?o²nÝ#¦Vu;äNÕ§«ýª\‡A9 B)Evb >“Ÿ9¶E0!)WÆ·ãψ›P=ÃÇ%Ѹ‚DšZOb)‚:“¹?ˆé•Å|³Êyò·{±M¦i`Œá¤ïà¹mmP’'[Ä7pˆ­Í”æhèY…Óěͭ‹®C¥©ðÏW ö†_ç
+Wt×Ñ3KÔi&Í­ÜGá—…„ “̸H®Zé.ö'4=ºÉïra¢OsÀló==ž–Ü)¥ìÂdp¤zÍZâªÊÃ; –Ñg1j›©TJ>[ƒÜRh‰ QšÄé*®ð#cÏQ…,êí* –sN¸MÛácÇ­¡¸I¤1‹æ/"mÚ'­§1Ë·j?›/įøº‡CT‚¡/•­ÆS„£¶3=®«‚Ò–„ÎR´¬ o$WæòTÐ
+@¯ êê?S1UÇó)Dðضߴ«2J.û]À¢†˜=[Á.u.‹÷<æ,ï
+C"½à*f=<îcªåHŒ'Å0ò™"
+¼ŒvÒ
+åÃgî.ŒÌ/“¼Lbh*—Áím+ˆ–$¢í:ÆSHžÀÃxñH•Öv´€¤ÃLÕ£òu¦®i6‚º´ži®ÍRzä¤\Ưì•<dŠ˜xV£«¡›e/ ñ”oíbÂt\‡ó!g’ÈhØxwEò?ô
+ʘdzSÁj!ôbJ·X/q5{:Þ¤‡K&ä¬z~¾4¾Zeo%FB!1%_Ê«—qz…åJf‹ ÇÔ ²09þ‰Bec„ux"žÈAPe0qLNGS·%‚Í¢‹òX
+—P¦UzkJ¯_ƒ‹5<_¶A™É\ó×(ÀM´œZ+~Ì :mùôº£š×?þùà ]~˜2×ÀØèä.¶Âÿˆ)ÓþúÁÖ2Øä›B]“+<R°¡®K‰sî|†NÀìÔñ ªîàRR‘ É~pÐôúþ6z¡=þü÷?þÃÿïÿüÅù©?T<Õ!bJEMÕ˜²'¬¶?#)â \²@°ªzü/ÿê/¹‚àäl¤½6‘£ä–a9Qû=P†=æ€ÿbåj|Ü=¢EÍr6ãæõg•ñPæ•–Ðm‚çj/3“Ç—´ó‹ád¦,}8. ŠI;¥ñ•0^CÇã:æ<®DùjÁŒ­÷‡|&T~x~*Ó„JGÍH=
+fJF1-Vì'P5Ä–A)ë¸OÖ©Üï5öwØ­‚ÍÍd³Ý_üwÕû ýReJ0ÎÓøßQ¿ƒPÎF!ÑdR‹C¸;FWL“6„g»<¤ëÝ”¦¢mYX¯€Ÿa~Ǽîôjïû¯Ý äniPJž{ÈÌ7Önëù{gÐW
+¦Ò´MhgO©›˜hOT(Ž®2?%À¼vGÐ5î•e‰„ˆ°GÐåÑQ­>6‚YYKÝž>f]Þh°f(ˆ÷ÂÃKf¦„<RÔ²ôŠ‡C˜@¤pärt¶ ~ß]„ žfÛ¸Þçü¦4´L¹íë’Ú
+^SaŠy§(03w<ˆõƨ£¿s&Þ˳)ÁgŒsàFuvu×Û„ÀŠÐY‚¯ + NÛ›5üugÿ…òs±bÞÞ“Í&®I¬k²3‡ (Wªƒq´éáÒAÉ°|96Ñ=ÞôT´6‚ª¶’q9›_†ó¾"²@úÎÑÜ“ DNT „Ÿ•i°˜|"hn…÷lhM`\ÌÙLBL•`ù@ü(Ã¥;Ž?·ø;èæT'õQÞ·Á ±M}ú)tnIßj?^…ÍCß²Øê}”AP¦¤Ä/d‘´¼ŠJ?—>ë‰+%æn¥3 ¨þaÆšüô8¤ŒI३#Ln¯ ÆÑ“&GA±]ó€Ý[Œ~逸ã]8SrÁÂW¶íA*#áVÝG®O/š>ŽQaAÞ/ÿ½‚¥(¤þ—ñ2ÉÎ#7‚ð t€cXkÛ·ð–ºÿÖ_ Q”øW–HÛ¯Ûƒ
+RW™’q•ô5æ)ZÌà÷ŠA.?“BôJr!/†HÐ]ˆ¯\ϹP–P„PÖ8
+ÔðÑÕ(­G¹P FÅTkQ,Œ¯©ðŽy …Bd>ƒÈ™o×¥á«Tšêò–
+ƒ.ÜRá×úg]I«Òî˜Û©{*¼Ÿ¤Â
+†B˜¸]Åâ`ˆ9zË
+_j‚Í(
+ÒȨP[9ñš È-˜×l¨ãZVb)A4 mØ
+¬eÎÜk´ç!èõº÷„ùÓGÊV&Ñ8ˆ®ˆÈ`âχÑdhXº†CX|hP¤r&¾ôFõyÐÕ­E ¡^Isߎ9‚HÑô~šÎì©™{Ü[r æâž@5^⸄³J½ÇçÀ*4Ù,Ë{ů¡ÇŒÕÑ/>}U•´SwÄÚ?ý^Œ°ië°Üë@ ò.õ` .SKxÉùéeƒý@‘|Iü‰Öm‰Zã ÒþAóÀ³Dh Îk{Ú*ï]Îó«eYmÍ”Ji”k“Ì1Š¸K6¸v¬3øöÔ~ž$@I‹âDÃO)ÛéÔåmEs»¬pÀ`í·ÀÙÐ<(”¡}SãM|t)>‡Zë§Ä™±gèâÜÚD6ĉóÒ3Æ2Ss4¯bðX%‚ª¤çÔ&a·ôÀš†ŸSX8&±a%N…Y}ŒA±RT$2÷¿~ ×„ó‚X]£è~Ëç»H- &;ß®qe"X7ZˆŸ„ÿ»äÎéáõåŸ+ÈödÔ‡@&6i?OXu40\eog Vcâ£E>ƒƒ¿%zRõüp³ÅF JÖ.‚ÊLÎ*8#š›Z5Ü­j‘ʘ>“›^zg
+WÉìÏ—µô’7`ˆˉ²’®ÕxÆ:ï’7Ú—u–pEêUdÀ‘öDO )¡ë0dÙ
+Õ˜ÕíLµÏ'ZWžl‚]c÷·Ø  üÛ™ðjåé$1 FöËó›’¼? Ƕ¬Èrž“ ¡RcË ¨ªÀ ¹½Ï âçáM?'œ¤§§L‰k0s”|Þ¿Ó^Ÿ‚ºä¼j&蜚0Ï„ŒùrݬöýB:6¦ëÙ è¿„B4ùŒêï ñìWÉ}´…¿ãOxpžQžzwEÕÔuÖ´‹©”§´y z°ä ÕWà[
+€CÆQ‡6ü˜ …aKêáiâ%üEy€Ð¾ŸË–릤€×¯söV¶WBªT
+²5þöÚµ1˜Åéwį° Ÿ1ïßiÔŸÐÊ•
+oeÌÄ:–Ï™¼¼§¯ç½‚þ AÁ&¥©€Á÷ñÖùä®n h“T+¶+ÓOÿöRÿù'ɤ{ñÃJ3 ‘‚TÙÏ9CÖ¢Ô´ó ­èçtòL¦ËìH—´û
+í\áäs£ü¦' ©‰Jq4ëãô1¬o,*»±øõQEÚŸ˜ÉEÂûª§â0ƒ¯Âò7¶ôq*1ÉËázŠ­z‚øLxpb×{=llX¦}­› 4² Y;Ž_‘{ P“Ã;ß¿œÆXé-`ϸ'öÇi(ñàåY63!¿wꛕGI½ü6÷o|Œ¶ZÉR‹f%âá8²´›Ë&¼:{ªäë „.R½Ú BÇ4ò€¸±ˆ o1 bìúÉp1 D—©Š ‹z°Coì/ž ªo¼ ¿ s…X ÂC—kéÍ 'I*$GtJßJzOwÝ4Þj¬%Ðâv¬‡ÓÂ'Ãc%¤‚&•`EG2HUå[TßÝü0pOJïVÀ)ÇÚ»~níBØZÖìH9ÄYo|Kd
+8Ç!„Ú5LÚ¢c´ÐK|¬óÅÄ–à5˜~¨gú«&ãNb!ßD1^‹sØ ƒ°ŸL ½Ú+ÁPÖÈç²#s×B½²€{š©¤¤‹=ië4³¨—|X÷s˜è™IOªSä§L†¤<éŠx·¹^f„@²
+7)J‘T—³À€ST»¤‹£ØíÒ¶DcðåÅ+5ýh%£
+Ó²Už)™Ž9¨^<ÙDDL LÃ׸,2u –è<¸øÐY|}S>@ÞH`Þ>1^ôãîXŸ(É?Åá øN+ò@†Ð)1xskàˆ<›òØ‹ O…É;‚à½B—àÃÎ3£ÃJ0¡˜éä%&ÅNú<”Þà‰'wí‹. ˆÔbñÕcî“‚à›{Þ!’½i ¿ð7ÕT{áUY”ź3Ø»x”HFW)Âq`³\©É0©Q~Làï<ÑGôú¢àGÚæN´¿A|$ÐPEGüp?ŸÃôQ]+ô™ ‡¬ÁÄ´ìëòVW¾—å»Û^ñ9€s ‰$I~µ]øe•ñýúçƒŠ<øZüsÞÿdúaÖGxüøBí4Ê!6Qd™X£•éXº7ÉfgGf;`¾0 ÍXMr^ÎþpÙ3èç+_dô¿¾|ý›W2¾ë]}Ðzû'Sƒâè_dBþÿƒªµ.;„nO|‹f¡¢œR8ÈQ+¸ É4˜»ŠB“˜4Sò †Ùg„+ûë  3×ÓA—ë^Ák ‚ÄÛF6ȧ B/ƒžJðó•:­\3ªàL$J+Q™JV?‡ÙÎæï
+„þˆš3ÝR®±”{˜rñ6¦\Æ‘hŒc
+^]þÌA,Q!¾…’†£²ð¹ä¯1Õˆ)ºæWñ+TV)à m#ãÌ6«jà“e}ö9D%©-þ*rOÑWv{äJ­èd"RøU
+Ùνã(BæöU¨Bú0 D~etÈÚÏ€“Ü“í9ã¥…å ¸hÜ@л4¤…˜t^@O³,(ž £½ ¢Î¨–‹"4®¿‚è—ªºLˆ†ð„`9XXðØýQ6ÅCåì¦êRdHK†ŸC¤ÔèªA„B; .kµV
+kˆŠ"7ä?†>?nzÆ„‰‘ÓX2r@Ð}î„ØãâÁBü=‘òÚÝ5p ^œ%›mœÁ2¢ÌÊ|ƒøœ¾Ù=²Ý|W’y¿‚¼¨«,
+ù(;„œÃ¬Q³|t¿
+6©o?ûB1Â0.ŽR@{b¼ãY‚ åà 1eÞ¡Óä¯F ™,Ú]üš„,`þŠÂŽqc‚ »Äµ¼îI,8'¹¼Ã'+tA°„µ:QjvÜ#Ñ1™Y_ï['?¤ç´&¹"²¡£„ÑÎa•x6‚» ‚— ý‚|*I ì¤ñÍô$.g£tQ 93Îgº#¡Ší«‡ñúuµ·¤§Qì·Ü¤Õ£&­^ì„@FÈiÍ eÕ¬TM‡CXhè”Q "!ïT_þÍÈ
+#<X[LañÿÃx¹$ÉqëPtÚC¯ ‚$øËC/EÞÿÔç&À´ºŠòÓ Û’Ð$Àýi)ô´1&Åc¶³M~›rrF²7ƒa}yɨh9Ëúg ¹*øyÒŸÔã¼J…ÝŒ֦ØáJ®±tÉaé"•PÔ$sIcxÄHncJ²º„s#v5wEoè¼lä|!ô5œö¿‹´Ç4Á!üQDÇÊPN¢#ãêá¡ä-ÄKàqdŒ²¸¢H.?4=†Œá aÚ½Ç1L+ãtÇ^Ì‚ÏôV+%¡iÚ4ê¾é£kZÃùpNkd [Õ7œK:d7xÎRÀØWMbɥŇwàÂzAsÄ1$EC VN~‡îæ¥æǒ߉;< •Ÿ[ ¢L*Sn—Q’«²:ÏOâz”5Ë™<}ù¡è½ƒ‡’÷9œJ>æy(‚@l¢eóÃã¾ï–‘$¶â1y¡K€xn~ÃÇ0=,2ý¸þ^$qò6-ç‡s’$_6+O%¼4Õ%¯5n ãúªè¬ÕCQÌó–ü–ÅÐ!NôBÄr“©¼òe3.CØ
+x|Ê ·%UáA$‹ºo’š®Ëö½­BäŒÀçD {$=é{µ&^(î%iÉát67¦-öàðÏr·/)$e„|†¤!ÑXš$7´Ï©À+-ä
+—\‘qÐPçCÉ7_öóÇ©è”rÿd'/×ùõ—Jí*‰E8ènu•#•¸õîÎóPD0œ(^ÏM%%¸¡¤ j^Á2,ºKó,
+ØElF<bš‰F4Ò˜³†ÓÚÖœwZøÙâÇÀäòFð3$Ðû5=_R¢@Ð$a3Þ ÛØ»V÷M ˆÀḬŠ¶ˆlåÝå:§ ÍmÆVŒ]"çoð´æ%ôH‘”)îØ2+ÏÇ+”-²å5
+øP'~õ¹Û÷ÖÀαD†µ’Û*:Üå^á£Íh(Ff“ó “JŠ“ˆc:•ÄUÐK™§ùtÚôÑ·Š¼³SÑ&ÉìÌ›ý?s{ÌðÁªÝ*BüÃàáJ T±4°
+ʳ‚À–Ì~ð<ÊQ2À0æxj è„ $+4Ÿ¢$îà¦hà;Ä•q˜ÿAtœG¦8Ùÿw¾A§‘^jWs¾N¤…û§^ä]*Þ¶ï'4™œ&«~vè‡ÏâD“EÊEa¢å䦨%è-È;íÒ-E£®ÑG ݘãî¡DŸ
+–Žéfá%Æ-Œw I˧1/@¼}T#št(;Ç9‰3ðàp`П‰KÑ°²øºò'5aSxP
+ÿ
+ÐTf·ZÍ+ŒËK;E–.[BSÓòsˆ†žjŠN‘ìE-–‹ Ì©$eˆ :_õ½ˆ½P&\È>òëô¶«7Ø;WÌq=~[
+K%E'дHϹ¸÷›j #‡þÁ’7_ ƒ7fødV"¸°§Y¡ì_YNø[üÎßå{TUY„×Í•š(ˆÿµi±„ˆŽ…i#¿ðt›h' ‰LD$­å>Ñ" iœSHRh¸¦Ü–ìö©IRÃ`’|p³SÑUrQ~‚~<†™â9º÷b‹Si˜ïÿŠ¸,g-Øò›ñ?AÏf
+Ž´*¦‚_}%Ö«¡sŒÈtO¸%À‚øá% tgø$¯xK3†=õÆM,=•Wï"ýà‰¹ú9Ä™<”
+àÚ&« 00£„8†µ‚}gœ2¯TBOù×¾
+FEAضI3™rd’yZ~+O¤, AˆÄºð<€“À7Ý òü?Ó‚±µ0ì#Ýg?ý*ÚI¯ºö:ŠÖdeÉ  óë—©
+ÃÌ’wØ*ˆŽ¿ÉÎ œ«Eo}ú—cµ8Tí{q¦^^ªr"JºlœœäíM¸¢ª²"¾¢ì0ÒÚÞœo%q‡bí&ßðtLuü†£â¦•ëå¤Ù Üi5œuÚ“büV°Ö;‘:Ç
+F¢(×ÎqiñÛ^Ð^³7í¡âÃD~ÁØè1«]’¼ˆSâÇû¶Î˜&™YÂL?áû­"z ò6 Z{:F¤Š­w¨àe xœ`\œoè,•7Ê¢mä“‚™V¯Ø×i…ùèzYÔlê4·¾äŸ··¤“•=.(/¦}}œ£
+¥V"")L[\—äq}¹(šÜ]`qÿàí¼$ñ6VyÌ­Š,až^v9hÜ‘¿-|ôåïöUãÒuÎÞ9¦ó ã€þ甋(Ú-o~õTÂ@ð2ƒ£áÀŸ?NEo›ü·Š¤œ …ÙõóÿQ$c4àö€¤SÜgãBÚ¡ÊE|+ 2d¦à þe¼LräÈ• z‚¾C @ç̵ú&ø«êûoûYÐ=Ô™É
+Ç°ã6ýµ ¡náY™Æ¢ 2
+S èšÝ¶!Uáì4Ö}Õ‹ƒ‚Áé­>œS•Êi—Q<A°qìl¿úãM/Vü yq¶ùQ(%)3ù`ЗÊå°9€~DÑ+›1N6zCj¸Óôƒã“O÷/?`Þ x€¼·áùh' ´¶îHô>òë,‰ôá²Y ûó˜MÙ
+Ê×£}7|#ÆG-£f€S¯†ƒèm¡-Õy‹`ÅlÑïz߃x-¼­‰>1ø¥²ÜÆ;®ü†£\‘dÒ„·æÄxÁûiÆÜ|„&@‹*Ñï´£ Ò' Ü„Ìš õ쨆½Ïs9@p¾¸7n^}3Î'èMeÎ~éCŠdЊ„Z¥©cûä*åã'Ê5—AûŒøøÕ–ìôÚ²6¾±$>LfùSÌÛe?ò"8#¤È'ƒr…ïåwµÚ»ýîwL&(Nú.~še?©hé4ôŒ~ýcPaçú0± !gƒ2漉®yÀŸƒ°Ñ³UB*<ÛÃuz«ÀÏTéªå×ß ³ho¬Ín¥X½ŽÑ$pÅþy
+ë*iÛ.ÿ%ëÖ/.ÇâfRÊõ`F/w7–W?XZ¿©ò¥°
+³9ë½!$º®eY˜b+Á±oÓpò°†è‹ÿìq…õZé#d¬Â+§>ãÅGKjn6ºü.ò˜úp„‘êJ·Š½@ü&¸œCH~v)ßLFˆÚŒ£¦™|A¡­—°˜tÆWC÷U…°†{e.GȆòaD?ñs:y”r@t»°¡rœ9þ¥¦cöXƒ…Ó(7±O†ºTäÐAK’Ö%keŸ£YK”˜L‹E~0yCˆ‡9ª,åf ðqUUåRBÕœÍp9’(ì÷e\‘kÒØNÿ¨i*â\ò¢É‚ºj›Ÿ ®Q2F½vü%g®ßÃÕU®†'¢Ä¥ ØeõY ‘™îN1ŒÚÚsÞ¿¥Üx˲Ìü›ðL…ó[r Þ¢ÝÀÝÎãø½A|ü¨$¶‹ß&àt›Ü(&)®B™à9h~ù¤—Âî úL­Cx[ÉB³±½ìm¢åVâ«-.‡’by_÷„LÄÈW”#OP½†³€^‡­›^é¦Óï6“(¹û)o”E› mÞ­‘=2_§³x_H‰‹Þ”`8VÅä“ɾN<Ü)8æ [`ËæòÆç¿ þÔ¯•ŸÕöæ*Qò ¦"°µ©.˜ÄJqk©¯9µ‡QýP›AYÁl1É™9P†Ý¤³¾SÃ,žß°ARTg™ƒ… ¤¾æTuÒœTÛع”0èðŒm݆^ð½5oGàL±¦5æ­‡Üd`sösȩˆϫÔùúnf®î2Îo­Ù’gƒ³!2ÐuÛžÉ"ñ<2nb
+,Òl&uHãP¢¦jCè÷")‘p¿œ&fùi‹''²*sɶÿ8HÌD´ìåá_XXFÁ,ÚÙ–H§îÅa?Ø*JAÛ¿œû—l(ãêO®0˜ŠÅiÑOžË³aûén¬´Ò’¾ÏyGN™p‘£‚0YQ][ó‰g¼ùn8ÚÆúJ"ÐVF?Á~ zŒöùgÃìVÕº¹;¡™}Š’71½=7X‡ ›ÑÎçðÁEú¿úUp¨ˆˆfŒ¨ÍµÓ8„â;ÚfÖ 3™fi‰ˆLßmøæ$æäï¥¢Æ Ó´œx˜ýÚ°¼ãD+ÊÉGV;3ϼ/3Õ…›¶/)§©8á"¬l0<îCÅá˜Rƒ¶ù¥
+½çWä¾õvÏñÇêWÎîº088Ž ÛÀ j0ýÉù†,Qx ŒÝð€¯r:G¹_ˆm H»œ#í ¾Át¹œþQ”ª‰&g›’¨7ËîFmf‹Ö‘Í™Ÿb&¥ºö{´æd»a‹^â@hõ46^ž5Ä1š˜Ç9ÃèÐܵž0óúF»WG &F+ÄWô"¦Èîs:Þ
+/×Qå[ñ0ÈœQe¢7¨RIjA§áçðwÇQî,ÖDN§—ør2¶º9_S¦àëôyqrX›%?~9° X1ïë. O噃’†‚†îßç0ŠP<6úŽ^ŠÓØoì+»ÉÒn‰o–=ÇPå5qæ Çå‡ä`öà üŠu\¢*]EÖånWÐH&aùUÒëÁkçïR&–•A˜ÛoÊ1c{ø‚Ëz{ ʥ傧Âs²/lÂsxö<@B¡§8
+›•ÊÓ9Kk™eý£ˆ¨Y™rääLsÃZ]yÌ!ÒÞPõ‡d ¯Z »ö(.6UF‚‹sÖµPdÊR:Ôª̉<kñâ…] 4,è.u7ò­ÆÏ„hi%Zý¦ 4‚×ýŽrJ8ªê†“(E³3ÊUªßÄ¡1ì«Ŭ*Á-ÙC‘ñÍû¯âðúy
+z„@ÇÈ/ó½D÷§¿‚iÄŠ‹HÀ1? ‹ÎPVßï‚£åëîÖ ByIr12ÁªvÄÎ47ÐxpùȽLJ#ÀIŒa~ž¢3s¼ÜëG$máæ(O‚¼WÜiö›à2–>.Óã$<2%Á¥ÅE&ÅÃn$ó×L²…á—ª'vª(oÔ¸i "dOrñÝT’èH̼ÆfaV%2 TyïfC|ñ„:ÜAK&i)ðFÖ!B({•ñÖ·rHwjh°: ?«‰ù_ýÿ/ûú[&^E¡¹Óã®
+1el:•o@© òŠb·÷×쩈26Œ¥¶gHÓ/hãü1&ý'ìÌÙý1ˆé
+×þƒbÌVX@…Ô‰
+vûTDÓOßBøš$Ãí%°ão\I¼öNý(^I¢À&8!ãvÁþWÎp-CÙˆà^Í‘IÄòó„e¼(açƒÒ`ŠýÓræÊõhah^Ä ÙOW·¿DžUTeÞ%5)Å}Ô…q*æ?BËÔo‘Š‚ü9=T\à'¦ ‹)KE¬aAQÒSÞŠ8‘ë`h•«f«çŠ÷Ú?¬ÇIÍ‘ÍÞK¸"“Wjr4c8PbìôeŸ@4êë$Ø W0› 7Í1Ù˜nÇFFÒI­.¥06ËØ|Ê
+aD]2Ib æ}”iiaØT†Yx+F÷‰á³VofÔB®Ý±Œi!lnÕÂ9h ¸&öÚ^”ŒÍdkÌw›ZÌJÆÍ’óºòæëÉkð <:õ‡ÕûoÅæH² ñê~úŠ¸aÀ¨¡ðùp
+ˆ¶‘ü‚£‹Ï¡ˆõ†…з®kS AÆ£ìT?vÖdGø•Ž èö0!ŠªIîYª=¡Ë¤)‘TãEòg¼PBê#Œ…阰£&é«ÐÂèíˆ^•d$šå-ywà^rbýCQƒÌIƒÜw²ß¼MS5ys¦%„‚ $-ôz‚„ú–Xý+ä#ÐŽù–Çò£QÐ9bœ]…h ex+}©ÅœZÕ«;¹)ñ!)Q˜
+…Gn" ¼#„g’ú>ªã
+ê«üšmïØØ=ðÉÌÍöË»¼»Òà½èÊ ÚUµuwP–PŒ—pµ
+#õW“#‡3­÷Q¿lµ„[î&ÇñrÞ]’‰ìg&Ä »ÙÕ"+AʶJ2‹ÔE†‡?
+ch¼½¿Wã¿itLPÚÅï s;)ðJÀUx¸ÊF'@öÞ%{í®;uýëkyÈÁç×Q—W ½à µRêG!9ªƒ
+ïÜXµ1Ý">Y%˜K¦lûþJàòÝáçQ#8›ëñ ~à ¹ÿÚ<¢‡ä@ö³Ôî%`¦ öpK$‹ Ë5á¼O{Y~§T9) x ÅÅi C"çôËà‘µ›¥®Ëˆõ75c‹1x¥Éìgv2Q*‚hC õ¼À”ð^«r®þ”^lj­}ýÂÖû$.›ù:§9ë#,ÆÁÈgôÏhjZL+÷@v®À0ƒ{ œûë ÿsîA+ÃPºá‚{²à`_ÈþçXtpF3Wùp³ÐÞ®¹öõÎüâ×OìlŸ¦cüƒ;ÝœjˆŸœ6^ý´ƒOE÷ãnÏ{«èäg"ØBS0ï×ÜÖµ“—š¯7º=gòñ·*{ ·Wxd¥â| "½®ÁŠäM[À댙±(aÅÙû
+cDÅ®º>Ã{Õ tÅV…(™ûŒöex-ÁN1¿ŒTÝ€TjÞ'a±ÐeŽïnBÙX|¯$®ïÀ¾U–e7ê,rfØŒ‰H
+cÃVä6Ä’¯ïøÉ<QÁ(Ùå( º?4¼gä¦{éå³€•
+Î^Â'¶J„×ý(> ÄÈ<zÂ,"1¡T‰Fy~GφŒÕ[%˜]¾Ð±¼cÝ"ã9£·î'qMx3“Hsö“(1Ѓ¹ eå2³i–*ÌÏКYD"«‚þ*R–
+‘Î
+(feDw³dšbÄkÚél1{À&bº¿‰p˜¥9³é_‹Jù
+jÉ–Fÿ|Ӈȋ¿&¬/YΟ•¨çYS_‚MàÉ¿’/pÄÚ§ÖU†4ÅÛƒ“)Öûæw4†ø6}÷’ÌTM¢2!
+ßTÀÏíPâ¬,˜&´SëÃwd¯•qXó’8Ø-ˆQü±…Áqr—‡ oÖ¡5ƒÉ§Å^</ `†˜ûŽš¶oÚ–ÝÎ
+KbmŠ²ß¹ÆÑSÉ-ŽŠ7ÀÍ|côÏàÉ•( ôiîx;’)µF›ýöšÔ­UI
+}·=|ç›%?—|ÓÅ¿õ­èëh¢¹4ÙÒ°ÐX(‡hJ“Wd SûÜÚ8:Û6w*'1Æq-^XFM÷4Æ¢êSüS=À5™ô»^ÁÕØ?¸| ,P¶õþU¬ƒ®úqûL—ÅæPdŒû.E{‰*mq:™”i“vÿÀ$u+BeÍä Wgº¶™‰¶ho0¿U¾Õ;» UÔ|RebkŸ îÊ µ¤Ú“¤÷# €Ñi¿NoJ¿ÍMv§Ž,´­ &•¥Ên"y |Iy«Ë
+ðêþø´Uàù/áeŽ,E Ñp‡9!©ZKÙ`r "°àþ./¥RóÕƒñÉiIµä‚³èó;ršô°XÈTãÆTÕ»¾ïƒ©= ‡â‡ Áëtùp2›ñ?¾Š¨4‹C(výŠL׺Þ)OA;Õ¼¥¬˜Ê ç·k/­cT¼­£fԡ߬F¤!N0I/-ëqוZxkD/Ö‹C’}™¶ƒià[Ê_™.Xbˆ‰§kúp: a_S°à¶ãOüÈJ
+% 'Wâ(ˆ‘Åu~y?ª‰Ã¨FÄ´éKY¶øm{g?3¼_ÌØÎð¼o ŸÂNs!(16J0ì³y–‡¡ÅúÖsu€$ƒ ¤{’=Qu%Æ“|
+´šú?@þ™Q³¯ážld‹xì³k'ц9Ê"]èÀQͼš
+HNûÒãæöäiЮ^k@0(I¹Óæ,‹xðÈ šÈ-„‚Iv ’Uô=*‚¬†PðÿŒ-¼osÄòjE‚/ùY@ CS©œêœ¸^© ûMUÂdT9QY½y è „®ðÉ”™uWl~FEÌrd¡ZEœ7C[ ±Vc¿­Fíãðu<Ž`+Ùø&n"(&ºÆŽlª·pS£'ˆÒKšÛ~ÒG e`‘œ—L믻ˆìy@Ùw¡Ú²ñWWrçÊl@Ú÷t@õÈx(ÇFõà ¹(Ü̸VjMôoßÂî`’/¶F‘v÷’ ÂIx«[dÿÎ*{& ù,ß5fõYNžNæZ³U”`ÉW¾v˜Y„zÔ5Sfº!²cLCrˆ£XlQÒŒŽ‹›*íâÈE;hWÕk!ú0³Ï x}–
+ÜKl¢¥Ñ«ÿnÞ74M½1|oMŠ÷.Ó
+åIÍÙ\Òˆw9õdF5e†¼ˆv•€j@° ‹]{@ˆf¿
+H‰Œ—MŽc9„Owðº6D‰Åe£·µ4fVÙ˹ÿ|”ø
+SÎgt.
+¨´Ã$ÅŸ`Ð\tùãw±ù —ÕÕæc=þúˆOYâÓ»ÆI?E–-ÓÞ›û#!¿¥a¦Ç:®®³™¨Æ
+ª.k-6d=:Å‘zY–×-3´“Øž³…/uZÅÀ­-­¹ÍdÝÚÈ
+VÕ5È;rzl#_- õîÕ;©·QŽz“4“%­äI4e6‹Ÿ7µX1Y^zµ)^•¾³Lë4wŸ™ÐrÕd¶1mæ‡éjÅ3´)3©™Xù‹Ôcè#!kɤébL=ˆ¹4š…ñK;ú~Žåj½ $DE3Cöþ<Ù›Kè^:×Óš2}2v5-J3›f'–%IŽ ~2èB&]ã@l´Ù³\c·2eăb0º2#¡Î`­J²;“îÝG¹š¡t2ضC° ÑœI=ÒOõ-þ@ðE8’ ªüñ“µ#¢\1¬Ù£ `A‚Œ#ü°ö`µÆ8Ò#äãb&k½áÙ.Dhg ¡¢YFbMZ ½Q…¿Î,Dë/avUþQ&¡‡÷mgdnEÂb΂ä{=÷c×8XÂkÊ_Aö„qBtͦvïè'‘yÚF™ô’¥¸ì4HE §ÞN<#§Þ†B„ý@œð[£Ï—®zú°Î[gÒ•ˆ1̱fÆËΘá ú•O¬%»n“ÍÊ“Hey
+íÉÜÄév@“é§^ŒÃw¶»øDÒ$–.§qrf‚jZŒ™š„Íà“£‹q^ÆÁž° ³Ã\,¦È7I\… iêÉrÒQúQL¡ ©j:R葼8ga+ì¹zÕÕÚb`´ù¶³©–uÀ["Ðeៜ‡ú x`EÃ|Ó#‹b¹•«ÆÜS éF‚ô™¾¡íµ±!9²<Ùõ@ frÃœ/{ƒ`«…âºÉ•¿¯ žùœÚyÓIÎ×`H96„@ÑÀ룳G_tÖôgó½dgÁ*ó€`fVšD¾íP–•rÃúiäÁªìtj-–/•ƒÐÆ8èœáÃêZ^®Œí·§ä§Ó–ÒB’ÊNßl64Ú´"ÖIgÓ6¹
+ÔúȬ§NH;ñd½Á\„§ [y 2Ðë X¶ÕÝÞÛ¯í) èÜ a/•[;JŸÉ@+¹¼ †G ±âÚx÷¨x¦ÌA}·±o…›ä
+¯i#\—Ç}1ÿ±a—€1õ\K_­äwžÈΙ…€aL'GU¡«œ#TN†Ù8sCjêñD]¸NQyœa%U"×Xp¢4Ž\~­:
+‰x3ë¨Øòî-ÞEû>ßœ öô‘šÜñ37g'Tàÿ`¹óðÆ£×> Ï ,›”\YÉ“Gµ˜7—1Z²G¥ ÙA·#Ûæa™0즈`V¤š”šÜi^J 8=¡×«ÒoÎ8Œ»Š5h1kr˜´mÃ8‰Ê¢ÌpÊGù¢R(ç&bØŒÌmSôÆzä®$q ‡žê1EÌŒÕìŽs¹"ìö%ö¼›::54’ Ÿàc¾Ý"è8 ° ßw%Ba—[^‰TÄæ¥ÔQ².æÕ饬Àâ)Od6.°ô˜rD¦çwg ®àý[8—]s[X¦‘å½XŸþÁõ&}]ExXD°3NY#-„¤©“–ô°f)%hÇsUƒ˜>°dvr<ÝcUÈ\L·G9«; 0”ζ¦zCK¶óPºÅ$|;M‰,{ϾßÙék‘kˆXü$ ]Ø÷B‘G×¢';­öLT¸Hÿcôì´J13=ôá ÕH*q òÝO> âê‚UË(¡yÁÅM¯g61'ðì­Ô,'eÁC?Ý´€%£ÌFÆì»é…>
+® Ç’ fíCM«|%ÏÈŽ à±4­ïs’œ2!’ǘÈ*攅λDÂ_3¥h¬Zø/<÷„T?ß¼š Ï]¡¬q§= CWÀ)úÞE)‘
+ )‹›ší˲ÄNÂYØl ‹èæ¤= öj(¯d÷”Â=Œx€äb&¿ñûp½¡§‰Þ „mÕu@
+þ3phs¯Ôþà¯Ì±1ÁO,OÝ{ù” Ý’^pl­ÀnôåÓ`<Q
+š9@®q— R”ÅõyÚËqn|"êhæ]ÎHj©¢ùc¤ èÄ®jIbÎR›IéáÔVü
+[ß`ÍÅT&îù•ØãÞ6`¯57f°Àkô‹ÂɆ™¼5çÚ¯6wKV!q>ɹA§m§G\@‘Ztí š&¡Ömšñ‰ÿïBÍ‘[8;Éz2ZJŸ'áƒÏëTè&j0ïe.<ÜŸDù«6ìÙØUæ5ðPËï )QolaŒÁNQÂNASq綧"™B<¡¾.—ç«d-yoyJŽïöÁ¬ â8nrM
+9ÓÀfâ‹( Öæ”X‡5áb„Ï=GC(ÐjÞ‚9w(¼Uì>Dßl˜Ë¨HžNŽŠ¼3®q®â_ׄºxNy,jœØ „äÝCÑOÿ*ëmþÀè
+FL>#Ët¯{ˆ¦b'ô…Ý5©Èàñ$'ˆ(‘•/s4àtúJD^Éßøü|sj\ —x_¡«3_.ò–ï麗Š.„à.ÖµÈÈo%“ÛšäEƒßVDpÍô<œ`ô,Xš•¯4k_$²¹¸ßÑ©9^q§â•¬é“ÓÀ!¾ñÑ£ë1 ©
+ò”cw˜~—$Ô9²“¥8&ýóÍ)re7}ÈÀ²\ìí]˜{.
+z@Ö ã0[{-R,˜Êš|ÞºK˜BèKÕ?bÏî°sÔ]‚ÞaÁ^¯GÁFœÚº¥gh/‘+‚<eÁÎ`ÒìŽâÙñ§,Öê.90ôQÄÎ8#¯h•ÐxÞÄ]ÓJR$’À$#îG±½h:*JìGE¨äkç¾
+í@ñéESBÚÍáH„>Ê>uÅ&¡²'¤T¸Š~F#íz-š†WíeÜÀRy#ãZä=(•D>‡Wk®û÷ýÊ’þɧ¥ëQxÍó¡Þ¸²d 䡵Ï•IŒ«ŠKÕ)Z%00"ØybwBIÔÜ5ÜPñW€®#){"Ô}æ­ ÃYif¬uLÿà
+M’õ³ÉÒ Øœ] +ö¨IŒœ1›Qü7’‡š¯‰tNNÒtm;‰KÑ×M6Ãøìzó½‹Ê8,q¶9O^WXª÷]’YÀÜg9ŒX±ˆ@©£ny—@‡S’”‹uKuD`ï!EìåÿŒ—Kz7 „O;èúH‚ϵ³Ì-¼•î¿Í_$zMcdÙ‹Äv‰M…ª­å­VBæ!*Ì0ßåU³¾oú¯Ç>)cìÄÂ`õqzxaæÞ "ß=c )åhž'à“¥-Lm`†“× wzD‰G0EòßýüAùßñ\’Eb³MõÎ9ÆW´ÒÔåéÞvƈ
+"Þ Ìú8V.¤‘#別 qÀ%Ç<Æ„T䉫à¯ü×YŸ'¹MƒfþòÉæÁ9véï·ô—¥ôÝ-Õ†4L²SV¼ôÜØ%b'¢ö¸ÕøØ;7¥°Ò®åÉZUµuªKâ•Ì€?ªHCà{sùÅÒŒ I-úˆ4zC²XÉwÚ%+d}êÇ…†9‚Ÿ¦â£Oó>‘qV,Òþ¯¿bÐÓæ«Æ@Ì,›„)ø‚ò;Åáå¹£†ãtB†ÌbI·ÒûØžK’'ßøFwÈÄиµñ\‚‰Z ©ªá•ùéB/)6\wµÓ2&<ÅÇ8N=™Öd*ÞK6J€pZ`‚uS²×ø˜¯™³b—X(í›´‡ÊiÍ!Ó%ó$ž(¨…«ÑÿD¡ã•våkÌå=H3žvΡZ„ìc“TÅÕKÉ`DT‰'dZUãÖÌ Â
+Ê^‚µÊF¾¸é ü_‰Ù;ýTr †ifS “VÒ_;_Š
+¢¥Ë®hÃƃœ úüÜçQQÚ,¹\
+›ùŽø}>D–EÉËQÃSØ™GrUè5uô<¼í' -¯Ë?¤•¯UÆ“íðÈí3(Ê>€òZø/>[^¤(Z‰ô9¢ »ƒ;¿=_/1Úg¡D…-¶Íì˅ŸüÆÊ ,‰c‘jÒ£POéÅ$®r¡•£Å›ÁBF
+¦ô¯t¹«²(#"Ó|§â£S*ÃáNŒ'‚ñòÂd¡…ds}ê{žepTQˆÉûðd­ïسÒ*‰ºÆd­ïÖ·‡"t3¤YU RQÉ4^ÕPbX5ö
+̓5tiÍ<†³*asAõŠÔºÊýýKVå°qöÛàW¤jE‰zŽ)ZÃpæºEœ*À›˜ÚçöÐò-^Ü/¿åO#cÀ,£gɽ"¢*#/ì‚¿z)ÏÏ>–Õt#i`F›ËFU!ߎ&ãÞmкAö¶â&k*J=º#è²PrC ›^Ä@â¼D•èŸ“õ7$`-ªÅ‹6
+–Ééi.k»¬\+ðû6Ú´ºÚà+é3&tŽ.\
+avÇý (R)!ŒH´ºE2öñ“©9ÃÕA}’ùuÑùK:ü< 2Q¤žF
+öŒ¤Fž~˜Å][Ã=… ö82IK½¶CP¦ŽÖ]þWïÔ„ºà×ç3¢ygä½vÅAÞKÇô›½Ã"›ã.U"±û—ž†­ç@[£‘-ëiæm|Ÿ@BØ(ÍòÀ—»TÔôz8RƒH(::Ü`2¾‰Ò1]&“ñlã¿c_Єˆê/§ÚJ‰ÍçΦ‚.cÞ}ãÈ$5&­^Â^>Aœ6t l=I5>Ú°Úˆ¤åÑ*n b³”°ôÂ4vëÙzÍ`°èëK6´ÿà×9=Q¤ ­‹ E‡ò¡\S™;„… •"ò)†f’S‘š,7bl–Õäß±CV³ë>J˜ ‹ùx5Ê°á>‹‡Ù³t¯—£] ¹\×E^Jœ=ì-èBhwßÛæ¯ã®j§ÉÏV§þŒ@júÐü.]!Ÿd….VœQûê´ƒ¢ŒvûÜA™ƒsW Æé٠ü‘«ºj×Ëž jK9J“å¡;’IöAsˆþŸT†8Œ‘Ê Ö
+÷‚²¸G1aõØZ.ÈäϱìèlX2.æƒ0˜Lš#Æ”wôÓI ˆ ‰¸.sÍcFIì8êöʦC=L ´Pó€ð¯äî$Bý%"grŸÇÚ™Þé&» º_7&Q÷ óùCøÒhz{=å[¬EYf¾.c @•é×!NË ƒªAnó„s줄_ÜÐÕ—þ&^ÉÔL;‡¼²#ÃØ‚áGw¸ϸ@E“!%pžsZokRgpèî@ŠV(\–½H êUaâP¤ò"m<7<¸Ì3oÂG=Ó/(NÀô¯ŽÿïÓïŸÌ‹F+ÑBuºXKàÚCÿtÜg„QÒßQ[öµÓ ôzRjÈá²@¸$#a=ÇëªÂ„¶ƒ“ô§)kU¦¾<nÞ%ÀƒÌíÙ7…olxy3Œ×iK¨qÒ¿÷=Hú¢˜<†½¿Èú°¹dÍ[¢CXi›¡ó"¡î¡Á¬W¥Nˆ¿)¾zMÄbzÞ¡OöâÀ}ØEÔvΙæ–Ú•2Y`Ià(ä˜Ã!Ô¡§–aÿËRæS|}éüG"餂é 2ýºÎÀO &ÁÅT ähö%<\c'93ûÒJ`0nÍïÇÓ³ÑPÂ: ‚*C=•ÿOºÎ€:Å}€ØœØXØ ó ©ü?âÒä
+ãE‘ÃS¦ÂVUê™ó}> ™Ÿbé¹ÕŽEe„ó%¨úVØ«4³ò‹ØÐÏ,hÆË‘¯òVb5³@žº ÏSGiWÅ~ ýúз£¹ý¡Âl‘øöìþ5= f±íó`fS4¯ì¿µ÷V•Ûµ—¾
+œÄeX0èóxâ‹‘5’$élóKWb½d•5ö’â‡øüžë­sU;(ÉålC@†¬§æ“BØžˆ2
+úàˆ¥UƒjþN‰Œ+’¿vhʧ…‘ïžöØ
+þ?cM½‹žÊûŸöÉ"xeUáу¶^^ ™õ”Ýô.„šöè?8 ¯7øe,L¨ìœ×“MâYQº .fŒECÔÑCf)yÎa\JçÛcÍê\œÚîs˜¹P¨írIÖÃÚZ›ñ‰¸zy½äzJSÝS$­ìUM¯}MÞãºÍ‚äáE¡¹– úzq=M< –ÉÀoBž¸Ýð¿AnÇø\éù«õ'Ðýâ´‘1Ô_¦1ƒ.jGà ¦ãòbT•}™ë*€‚ÍÖÒðÍà%:5Fº:Êyãéñ›Ïµeâ™Q³¢¥`“q›0žžïR€a2ÿ÷Î~gªB"
+_òh§Lõ=2• (E¼ÁÍT ÐñRµË8d*@ÕÖ¿gs3U þd¤áPênBª ‰2×#2 üYä„S?SeÚ‚¹ÍeiÙ#R A6ඉ):D*tTæ§á"bQúe]ÜÔÀ“&7…ëùe0é eò#^ëG* <ºV–é>æ–„@°% H)kÅy‰êêT͹#SÛßϹµÛ¹Ë}jÜ7݇ϩ3ç·;ˆ{¢ú'[¾UiÈ$c
+IBÍÏãþx˜g¢2y9ί£ n¢âRŒy ½$ù¨ts
+©•Åén¢R3p
+ý‰”o¹µgž¢ð`k§<åyê zä)Ý'"õêsºyJ/c¦ö§T?*Ãïz¯ÛûüÌSê5eÕã%!O&ïyJ3Áu«vavS™ ÓlYG7Oi@óÌx9ä) :
+²…m—æï<u‘§©ª›§<Î=òÔ¿ˆ¹œ\©-2¸Ëz­™†aÅ-³Á LJð=Ì:Ì„Âù}v33òèñ¹w@” ±`V°ûýuÀ0Ë •–2‘xvŒüÞ玘ƒ¸BŒ (t‡øad©¦ñѪ]‘™ˆÈ3º+)²ÑÙåšwÍÝ:gä@Ølúºmâ˜BG ˜™º d²g÷Mbe“c()cNþ9íC_+é´Û0äBNîùÙ AÐ-ŒSaá|zíº[Ù/»Î%aSsÛ~·k¾3T³½K[ײCÁU¿ŠÎÐaºq ieýMšÇµÃ)0”O£¦sûs
+£ü„-œ "{Ê¿°VbBmãWسœö—¾›»$Ò˜193‚èéîâ`ù`·Óä}R¦BO €¢Xå
+ß1ÌéW}äñïñ¹·@7Mp¹u™¾z»Ìgo/5ƒ"êÆ óÿç
+ž o ½3'=ç!Kø2T GÍÌ—Q±èoÂv£o›ˆN¬ » ,df°pyy9k†é³SHQ’j&¬ÏWé7ҭɵ‡cþ¢Â=7†ãå¨9üÄöh§—ü>œàÅ©ãUãÇ9éZ¨\'ìpÈ‚W2f·Áäò¤ÊÜZâM:“TÉBß_Â{¶ øSöË©-uI¬^ãÝ5[¨KÛ›µICýN­ ê1)|“á’ÎñuF£äucþ-ʈъÔ‘°"¯ön*Pj¦¹W$2ØS¦w¼%’%;Ñ_‚–hv‰²L³¯s2ÇÀ^¤ÓD‰fb¼yZ® Ñd¹¶RCJò¨47béL[fô-Ðâ¥Y|™’Ê.Y¯)áÌvc‰Oé-¡6ˆCMg‰¨ê¾ksÊ°-°?¼Ó¶Z©ËáÚìK, š‰5i¶`´J@-´~€TØ? š´?ôÀ@T^E±R5új•13ìÅm9¥„.a¬ò!&X.îGÅ̽A\ŽÖÅkiª³tD¥°ìË{fMS­ÛZ}X³¤ÅH#æ! žn7ÔnÖNtO!,•yÍçËHVúW4¡üÌ6Ýl¼ d€ö zßØ—4</0¬Þ> $Oƒ{ yiÃŽåÅSØÏÙãŸè06d‰#7f{B2nØÐ× Z[fíÛ)ráªi‹±wY„7¢ ¹Úʈ3kySã5¤|u甦ÝØm¯“c0ì!kàè^0¶ÆnÌûšm+NøDÆÆ”HšùbŸ¢3U‘G;35Žò.˲I62Q“4êÖ| ImÒãSú’;äþp9t£ÂÐÉ $“dKóá1{ò¢üªçä‡Wý\–}ÁrÀEŒeYΗ(Á¾“ËHuYZ„˃—™\†+,ƒÃ'çÅ ø>$Š-žçXj¡$Wqia`+&(µ-Øa!Ýœ8’šIYµÎÁNmd)ø‚àgŸC¶E›<UV&ªÃœâ@ô)@|—xÉ<GHQqÓ½ ¢gël‘yx ¡™‹&¡òª#ƘŒÆ³[cò£WûSwÅDìXEèû/ÿ6¬–Î¥‹å𪬢K4ùÜþÔ£:éšcTƒù˯1ÍÇv'uzõbcñèùíorn·ñ&ðñ*o’Yx=ú—V:| Šè™´wÞ Õ7ûxR tI²Þ0üÈûce¥‚wËÑýö@rÚx)þÇR£í¿½ÏÝA_ïÜé÷uõÜøë Ì”Ì籩[—ï#µ¬›7Âr]ñSÝ’·Ã6™4‰fVeRÈ—Ãx† •Ò+ÐÆéå•k‡lfb}ÉTúçè±ìפƚQ¦nZ(ôè2z‚°³‚Ì~2ƒ;1×p‰‚Ÿÿ9 ·CqŒ¸x{tó•_H>wâUå5ë•}¨ÐÄPñ æÚ®]ØŒpD]¬ÎÌ"dòP湡
+)$­¢88È Z³yw*“Ù×|*»½ Ô®ƒÝ«qAH¶¤ž(V;û»ŠÐ©öÓ9<@WÆ·N» ²ÆÃDñëQíC[%×ê>å¡4„°÷A<;Î0¥HtÓ•x”ñ‚´Á‘‰JÄvùgÜ5É3Àå¹O‡nË«vLÇ×^Žd¾ƒep+Ž¯±Ñè~£æë ’•ñ6¯ÎÞ(ÓörŠ¬³ŒË#0™Ø1å ©~xDï5ÛÄHˆèñ¹w@ò8D,0+ƒ¹Y_ “7+2æÊXÈ9ˆ Ò —öáØ»qãa¬|>dZÍ
+þÏx™$Y‘+Qt¹‚gê›1'L“ýOë\Éõ #< 1«* n*$on³ЭNc)æ#î5v ÷€á€®ùÍ5`8¯º §:×€áÔø0œ^݆×óû9·É¹ßæ6÷W9“| Þ>| ÿ\«ïíƒO1?5:¼å º5"±ˆ{×Gßr¹¼qùš/þu£Í©bçƒ$)ôhê—(íåìÂÞàË™ªÈžmšÛ#ìüà!<ªLkÕ´"2Ð~Œ÷ ¡¹Ù!µf‘æ{Ùƒ¨±€è¨Xꟃ©•^Ñ«A:–Ù@ªÛÜØ»JCz°Ï`Ø"úTú~ŒD†©æße,ÎMZ¬^ôZ_Šò”|úç`ºs£…’Op',<{»!UÞTvÞ ³âW0§Ô|çªÈ›CîÃ2ãN’äéÛ 'WÝ@²ô FLP?jRûÕ—aJ'SQ³²!¬ÛOMÞ¹
+:-˜Žœê<E,úÅ—j.'Xá'³bÈ"5ƒ&‰÷ §—@$hÞ1@¹¶çÉä2ë(~®¡Ë"ÕÓž¤0— fþe3ºžD¸´÷ð4ÒCRæ{üø–˜†cG(.eXÝÑ÷!\’•xÐᦠ¡
+TDA\{ùß!4õ{ä—¸ÁCÔãAzêTÅ.!nJ³!úçTæ¦Iæñ«ÑÉ· ’à $“cuKæÞ¢ôdñ
+rëS˜‹Ü
+S£69>ŠÛè–×?&¾2$ƒÙa†²ÏÍñ…蘂֋7Bö%ü œ€úçúÈ|¸
+)cï*›Ó;˜"y˜ÈÄqDê}íä«wv*­Y‚¡Jˆõ #nÄd`SÄ&Å –·ÙD
+çA‚ñ\Ñö$4yçáˆf5 ‡ ±CRàJÚ²‡5Ej{m¨¬(–Ñ ×oRŒÄÚ™a¢FçÞ„y;ækÃË\æÆ}ÓuúœÚÜÇüRß;àÏ&ýüƪh©ÂYª0 Ž+Ó~y˜&«D­  FÍÞNŽd”ùÊáx”Š‚¡E¢NÎz2D` ‘6Tq¤÷½ x>&ûØb“  šÖLT<+â5Üq+ÌV SC¢Îé*D¡fv—,÷‹c#xJ#Låš­„ƒr“ âú+È1ÃŒÇÑ|‘ÈÁþTX˜5È€8á àHA¨ëm[ÚÚŠ¹øŽ-èKÛ?]P䤄C\Õîg;sÛ ߘÜìÍ8=kÚÔÈ }RXaéc:E.Y"ö¾Ñ•uP©VÂ0;¡ ÔMmð¡ºÑãGþ‰8lÜ‘BL~^Ów?…½C½yæÌ1Ÿ« [’†0›ŒÑe—sדbáÏXõ’ÞûËe|ñV™Qøã¦Ùú’¦e}¹Yéô8¸£† rÛ8*;‹Ìz¾ó×µ´ð“¡)®ƒÄã½iøµA9Æ•Êp:&£æö† —ä†QÃæ,7%¯¡ØÃ!æ ”¡áÿ¹¿D²(L '«N$u‹´Ï…òBš”yCع¨»Ô ÂdV IÛ—! qy*å”ö¨—J|:핹‚> ùª§s»{dÇ¢F5®Q/2I˜Zyî¾¼¼´gŒ "c³E$…å{)]²—³…!d±­€Uv¬«§yìg¡‰
+†pgÔéæmÒñYd†ÉD¥êŸÂÔBËY®«™½­ò °wM ½#îÍeɧM:ëÛ`åcÙ_’…¨SÑ’y
+vÄ_òÌ}Nc'+¬_›uJA©…lWzƒ0ìEœÐv3Ðrûô@"Iþ÷ÄbÑËñá\YT ¯â‚Éb±zO_;ƒh¯ÖY‘¿–PÌ „Ú'L–>œ>8úqm§«Clž §-s瞃£'‡!çÛœp8z’ÿ–]Ù3Jɧg7ˆÂÙ(ÉÛîO1êh4…Ÿ±¾³e ð>äSó‡³0JjÜfàb`ûÔeñ°'èbPü²z[á èÓ@¹×¡—NSÏÏG¸
+Á²§ë
+srx8¼žÃäD>Œ]§«§ M>(ÈlHá+tÃæØ»õŠnœO-ZÇ|Žã¸ —ž¡ƒ:ÏB6œ‘ô{ ±‘ bÁÐ’é²EØõ“òTà`ð œeæÝÍB&/© }‹„Cj6² `FYÆ(Ùïf«rÎ4q-Š‰ëœýýî¦
+÷éyáñå˜ ñ±ÓWñþ®œ?–ÀÂ(½j×jgÁé#0§´0-}½anŒÂ,$º8Œò ÃÓ×zdÈ)nÐõkßÀ¨ÒhSús_ír ]%VeÊšS!A,!ÄcÍ ê¹gÂVAú2‰ OrL®…ÝÇSÅÍp!øø2íÁQXh` Iû®Æ1ðkMJb¹[ÝNnmé†ù¹ (Sk³%ÿ• gkÚˆv›ÙÛ¿¶âÃiƒ ”‚Jpì¨k'<PKøjÙ|œó.ò\¶°ü<Ø@T¹¤h‹qõ.T¥5)Òôô\Å‘ÏÂh•â› •$Ö­%×DƒÈ5¢Ê¶ºT
+kÚìK ~ꤤûœ Š&Ñ­í9´Oµ©W_–ʃü9 ?þç`ÜAïò ô„'¢üÈÛ½Û羺зYWÌvî¡ÉwF‰ü.V‚vÔÊÏnfq@„…X>î2Ú-YIJ\„¾âŒùã0#õu9žH{wc$Ú¡kx=Î Š…~Ð×”– U¡4ú$¿!¼‡¿Í1̨x(ä-"£É_¦ŽxÈ=Šá…¯‘DáÜ— …¥  BySF`¹HŒÄM”=oˆl”/Úu<i·P‹ý©B+ºâY É>Řá)(ÿÝç`&hägFEI5Oaï†GzÈF±Ëðtb,Óv’®@1±&u¼%’z#è9¶Ÿ“uÊ—›ù‡Au²2KóÒ5qPjå©çS7P–áÙ˜¹nW&j~S†Y :ÄĽF±OÑm¸i/÷¸dQer[>_‚˜Yçh3ñ†có}i4ñ-Ïá.;$¯Cyʃ Öða]qè
+Uþ9¬B¦Nü“ÆYb‰|ÖPç´!UÖ¤†94ŒËÙ"Jñ|JsIJ!‡1â¹0fÞçL±zåM!9îˇôÎÚa2#b·ŒÕ¤÷šFòéB Ž àÚk·õæË\–LjrOë±±©ÖgoƒÑ;kšÉxõÿ/sIŽ$Šž îÐ2…„ï‹HÅVy‚µj‘÷çûîIf„˜Äp0ÓýÓ=Ü–¿Ìcé&ͯàŠÃ6LXl踭7×ú¤t¸Wëzm¦c±p„
+fòYáË9Ø)*‡™uÅ–¢ƒ˜¿:íkà"__«p‘PÍÊÏGQéFàUörJK]R…¸÷1¸Tø;T¾ÁJL熺¶#p mˆ²1«)äçUgÐ`›‚R]µs´’I(ƒÌ„€¢”µ{7ô$¯ß©³‹ì*~¥­OJ
+4›†[š}ŸCbòñ½ÁÔÌL”¢1ö(ôFl^÷Pˆ×
+(’G¥yìܬ=`žŒ®I”ˆ¹b¤b;•T°¦˜fL<ób˜À毋ºñY©ÙU£7õ
+Ëõï†KÛ‡2Ïâi¦ŸpCW§¤PÔ4yA‹TÉY¿(ÞROQÒqÓƒ*0Õ¶!Ë& Ó< !ƒU³ë×´‰l |Í/±-Uœ:ÛS2±AEsÔOÅð"µˆT1Ò:‰êÅ—Õ¥PT"ÏyçîhƒÑKcør
+b8EÍ}<Md¥E#Z›A¶“ƒ‡’C|¹:þ$|DVtÏÃ
+&Qz™ö1‰é®•f×èçíAçÐpãc>MZ01ƒ†˜з"ÔÌÔ0úGb$HÔ‚íˆÅœÂÀ*v1‡g0㑨rìÁ†`LŽÓ\Ù½òYâA“¢RÂvüä !¶Ý(ü=þç(Ík¤ôzù±›&¸®œàåÎ=5ÙîJéˆ(¡`ÍFµ‹,²ºIuî‚WSD™²‡ð‰Õ†jQ2\4ɼÌÄXÌx¾¼ÕÏíg3&›³98¬®c{¹œ¹eÅ„ígP—¬šÜn+cfÄdÁZ ˜>(žç0H¢uP
+eCëM„Œ‘E›û˜™CâÔS73¸NvCB}3¦=:uU‚çå¥ÄxÕ?'=4]¤¦ôækXjüŽÖ¦Ö»Giñ¹†rK~qVÂI”“ý
+_/²‹Î¸¤kŽÈí‡ÿ)yÅKÕtÿE”ÒÁeÇE—¤5”Ýÿá—jÉsÙÑqÓ¦ˆMÄ¥Ý×s.Csþgø®¯r†á€ZiyÜ,é¬B™|>·¦|ƒxiÔsñ2ÌžººµÉ¢-î€_-{ïèDÄöŠFyýô@rظ(þƒšqöOç¶3æû/úÉwÃè æ'…žº±
+GŸñìxÀõáÐú•CGt@?o@§ u]슻(E¬obÔýŽ»c¬øÔn˜ÓgŸAßï¼M%øíÏðã÷¿>~ûû÷$/Á3SÈà>¶uñ´RëôË];$.†Å¬­Ì›6: bi£‡ïp®{$!ìv´ózM„ âfÌÿ+Ê…/ˆ¨¬+e5“è±wÆqbj¤ˆOéYȇXaZ,hÖ½½ãWÝ@d^\›/_8Iê›÷¹qCÖ ÒÚœÒXÓØU|-…¡êåîœÁg)óèDT¬ 7bÂ'àÉ
+qh+Êp’î µsa\]G?;Ik”šËd´‘æÖUA`lé;b8Z|íB²v˜vžv\u>GùÄAHuöÉÔ+’ èËÜÑr]èJ¢ª™Á.‘¹ýüp0WµZŸŒ¡‘qÓoƒ¸mà–:övæ]Á3¦‰½g ÕÒÄdåIPÉϳ! {b$ˆ³3o^[¶gl/GíÑòÆ.Pö¯}UäàÄzçnWUÊÙ§BZ-ûH“}UØ°‰FJ&í s×8)ÎÎÚä­¢ùÚcÁHt­èŒ·çT½Á¬{!³M"& o ›G%Ɔf9€H‹{ÝÍ:ƒÜFD¥§"#Õn;zÂ8ÖâÿgïWš2§ìjŽÏK¢Ákó᤯µ_€`åéônTkÎAM Kp@HDH–±Æèq]!C¶œ|ò ”Ê„ñ´RnNQÈ€Ò û”.Åà£ãbÌD^ SL5¯ØEL¢É¥ û&Ò€ì£4æcûäÉ´4”}L—OâÞjÊ×UÕÁo]¤Ì Á¬éjùã±%oR\h]ÁäΫ\@º­”.†‹XÈÕsÔµ‚LB©ÝÚ0ø$jÌFšÌ²²< .ÃzÉï'¬X‹ùÚ#ÁÁÐfâöª Ãyµ9&dA m
+†\ ƒðŒN®XÀ]Â3ÆUœ°Æƒ¤¯~äíVZ‹‹Ý‚¹W 9ƒ¾ßÙšµ\?þ´`Hd?˜âmÅø
+ö²(à„²ì
+bñ¿°KËsc}À&QŽ¨—é×»>¤\‰Ü„3¹ë[åÏtÑìmFÿœöàmú¸ûrõÁ²ÃAè—½iBÏ ¶¥oiò@U:QSçÁ›¥<P,³É¼±ÖÝ.ÓþkÒ†4Åx •ˆýr(•7P½>}õDH¢†ØŸ/§„-àSŸ,Aö±
+ÂÏüL"]—äù ±«ˆ§0ÒîÆÝOŒ-ØòaÈ®_ƒÀ•øÞPï^U–eASøqò«ãLà¹Æä¥W_ïL²†>ì¡Ï°L"èE=Ú9ï—âÓÑË”Åø9¤ý]zÃbSó£$ÃIÙá²!‘ƒoÊ+ãƒ!¶(}z¹CݹZÒë$õ¦™V‚Añ á`á“g»öÞñ~šÅ©{ˆU7Ç(¿áüÒË÷?†PÕyñ@ss¼{Q§vØ”ŽÅ8§Ã¡C¹JõK|†œ[e2v½´þÛE4‚0ƒúÂ’¶Ë¯Ÿ,ö‘ÖðAœ—¼§C­ø¬»² l“=ýµ„²¬º…±|rÂûpµºt Éy»®§0Yl:›Fx™þ—”GškË님ôØM ºìWæ²TÔ·Á¤ŒQ¸ú$»¹¯HóõÎrš3Œ¼=%½G¯þ«k‡Ñ½VÕo…"_N0¢ênšIðM Ý g7½x›ÏýÕ$„©r߸3@ˆµËsÈÍ+¶ ‹!Xs~9FŒ“HÜzL€‰ÛìäÀÐ2pŒ9H&!×6ГÔ'ÊäÂಣq?K_å6š3a»Æpo`HçÍ Ê
+ÀIXܧ׸œ.30dã؆"[ŒKF ê/[‚-ÕòªA®†S×EA¿Â2il¾ðÐÌŽbOû®emR{zÞ’Åo bkqC(7¦º¦L%ø8¦ çÌdìf: ¼Æú<èÄLÖ´¤>HéÓæÉ eSzlÆëØRÒ£Æȃ0k“mdM°9`Ð_
+Sc1ƒuƒPGH"b{9
+-SÁíGÑ2ã“ä„Ž
+2UKŽÐË"Â'
+1¶šœ¸XÌoI” Ü|oÏv&Ö%ÒvH”Z"-†&÷9CZ¿÷pžþO5?—¸FBMY{¥„çäÕ_³Bx°™;ªÄ†³x‰Õ @9JTJ3œ=Éb±5cÅ5 x²¼¼oôÉ Ãâ“àFº!¯/sKr+Š® öPv΃)”ÌtµA^¶©ýë\¾ÇÌú è 4u?ƒ|ÃbGL¹Dˆ%mfºž30ì¥Hëf€xEÒ«1ÂÓQn¨¼ìI¥Z䑇…Ç;(±4\zàQÖ}‚’j¸Þú6(,+Ín³)FN7Œ”¦òÎ䋼†¸¬½m®4È/êʤ1‘Ýa°Y}K%MËWT7_€v¬“¼
+áÎ!CIq˜.SŽ©…Ú:
+ÿñ›Jçsá4.ÖÌ© ÝfJ&|ÓñÚAÚ2õ\ªÿ}êÖu§¼ÈŒ[ÂÜg£ƒ( åb€jögEʼn1¥‡‡¡ò56>JgÒ9ª€!ÿà¿»"éÃ)2G“8S‡GÒ.úòL@Bç-ÈôÒI¤ÜY ¾T~Ac°*õíþ%Ü/.…Q!Aü\‚¥Êsk>
+#KÌ—–G9 þœÏß?î㜳ÒÍîOéßvïúµw068nÕdü82÷„Šèœ=͵¨’Íøñz±iṃї=]^/WÚˆÌôiÌWÖ¯6›Ðedäɬ§Iq3#‚¥ûžãjºŒ1f}íç§$Ï'52±R$CÈû3„I·Á‹ƒ¹*hûCøq\ vùqp©L伬åõ±±ÌX!7ƒFc ðêi¤NªÌ}ÝX¨ÝIk…û±OaNi#!•auº/ª'DP†S´×8ºšüœeùd¬‚­ÄòÎD”"ùmx;—cÖèØþ”Bͧd’’çÍÎá ~…p×òAyrÆ¥”1 ÔœDV°±}ê
+¢#l§êš»_YÛIè«Û Ñ"¦ÀMÅ?Å*òN˜ÔòÓ Wiàù&,—÷§ðeE¶;æ;ÁbeœÚ0òW˜ÃZC<yE„»+ReæJ‘myp·€Ð’ROؤT™ îÅα‰ðYNAr˜€„¤øž³ôST‹"9Of€JÜŒµÉ…±Ì$>Jvo»ãиâÑù¼mÃÒ;{W”ùøo_ß GÒ¼/kûú|
+lT`9*]µs&*ØJ`J½4“»1&…ôW+Õ`bf§¶I’± ½)½9ˆV3ú îìvN²Іڣ3—z‰?àm¹z£zåLÊ:Û¤%©ð¨4÷6k&ÏçDŠfÕ)6}¢(eb›Ó±È¹ó†œ}aX §«9Ìß›z…ãÚ;žeð¦–53¾¤¦mjHšj”Gw©¢ÆŒ‘­?@ª¼û
+« Àçe<ƒ/ÀO#¾nÙsÙK_8rSÏsÚ6طЕ•O&C°{¾RÆ —èŠy3˜Ä"QÖ¿çÈ‹Qý-?ûóŸ?ä$ Dõ¾‰{|þŒ^ˆëh{+s@@–VÐœ²éï’¾$QêÑPï„A0œfV PAb˜ph¾ï0© 'èvZ
+¬-"Ê_åã7BŽEÒ¿ûÍ=µdyw†{Í” ^™˜œ¹³Œ. °õõÂy\‡®¤i±SûBKŒÈáÐ爈‚>`3†} ˆ˜wh&½6ÐaѲr‹½¿±HæoU›ŽpÒ*¥È½UŠ%ìÕ°PDc Gle'ºx&;7“÷!AA‡X?iœ½œúkÇêòô@IM¿BZÝEÆ¥°¾¨Æl"Y ·t¯ø—ú:ƒ;/B5nºó¾vÚÓŽ†HLï=¢òÈü7qήdö·åèxŒHAcqFŸ'ÀêÝËÁóm8Ô½)¹NÄB±8ÝJ Û33Ó¨cA4…$,ÚÞº.>"ï„Üʪ—ÝKÒ%ÆZ„<lpn å³, ŽÃLù ´Â*],@ñoAû¸î¨A€c ؤh\<–[2½õ°ÉlÃìâ¾Î ô"¬Ÿï¨ï‡ì)!¿&H9ØÚy銇¬´ÎKÇ£î˜Ö˜|Îv£wýØôùÎ>¾}5æ XÖÈF§p†¾á¾±X°êßGV +€Ù.ôg þŽ‘Ó…]+)‚•fÂÐ;ÕBüßvx9¥œ%¾köã/Æ Z‚Ä·ÁÂÉ(â2nÍŽA¤¬hL;†¹; a%Õ§°´,PËH2–æáªF7ˆEÉ_Äà
+0’üÏ
+!l!ÙãÄD’¬®8¤:’ÆZr=”C“7'FW ,"I»Qh-S¯Ã·)|µŸA¾&#ÔôÝN‰I¬$;-Šä®øÒ<viºÌPŒ‹l™"ó‚ªÙ
+G ÛÂ\‡ŽÒâ’õ8ê9èÏÉ\K}’ H{.2E±ÂŠ¨-Ú4øêêl}¾VçƱ‰r[A±ûá÷D™ ‹¥[úÀÃHtõ📚Á˜²`Òv\oC51nT-¿•Æv´cÚ"¸›¦šÕž”>]&‘Û‘tVÔïc»âd9ÞJ¨³ôéРV´-Ȥ–_ŽÝì£Ìèäf¨ôñ[  8ScTȾ9/!ÌÅ/ñî=—'uÊÀ‡6ÝyU*ÑA–w^Lkˆ[˜7^ªŸ\ÍÀx`h˜
+6^v£ þ”’dÕviÞx±:yAÈŽ?M¼õPá7 e–«Y'Áå¥hÞƒµ3É™ÄNaðꊃäãø½í1%·ªÍY—_ ?[ôýŸƒq{‹Ø`¦$UÔƒ¼ù»÷táw¶® Í/ö?vt&œÓ&'ºüry·2d4ë ®–ƒ¼JÝŽ{òøÅÁ\ùÅØjâd´]ê>¿
+H‰Œ—An£;„OÐwÈÆHŠ”Ö½Í-˜UÞý·óñ凶݈S–(²X,Êìþõß_½ûxh_Òš5[òõŸòh1¬ûËûüº ÖÜWW›}lHïÒbšëò±!CL¦†/½AÔMÞ%ZĈ韎‰Ö»ö0·^Á¬hj«‰ÄÌ5¢5þ?ñ:ºùò9¾~ÿº¹¸Ï9]_?õÞW³z–‡F7U¡õòcéã„Ì¡äjøŠ¾Ê¿Þ–t™ïˆzx ·X&!v{Š=æìá|¦Z'1@ â½ aƒ
+P‚Q)6in\¯²o2¾æ&^爆H×Í|Ÿ3tiŸmTÀý±b8ÇrU³2mF,áWŒ¯·‰ƒ€ ýë;Aú
+ñõÏÄÞ&tWe)5umX‹PÓÑF“†àþ5F§¼³è0—'é.¬<F¶VtÞV%éPºûþ<9ÃÚìÒ´÷Ù/znDóÅ—è¹ÝÇí1W'<³¯Òƒþ ¤¢Åêï¤g[»:GïƒÏ¬ N p¨õº!ªƒ"9y(H\²·ÚXç¦>'i #‹Ë&™(ÀÜçКϦ=6D[zØ¥í¾
+ åì9 ’œH¥"ON\JÜqRW†ådǪ©´—ÔÒG¦ ô|jõ‹ðÄT¥<x‹~xÖ
+ÏuZœŽÁ¢HKæ× k9Iœ ý\…ÞäCiñš@$ÎDŽ³ø
+úù›˜*tc{£'7n…ñŸ KúµÉ—6
+&h±ÑYÙ/Ò…X1„×F¤[Ðœx{fàâr“"؉õ<7Ñ!´:Ý/å*ƒ! IY¢,gØHSi»eF‚`³À]ƒH\–'ôÃÊv⢤[n¬Û›b¤1yq:eMqtìiFbîc,7
+ž}¼4Ê+=÷ƶõNÒÑÞŒÌUñrÜ+5¡bç"½\še¿•ß&äÏ)„ïs4»ƒ¯Q¼2ÿ$»`Õ?@pi˜G²ÊFõ¼ê¤¬(=³Y£BÆ¡ÔÑ~6£–æ›Q<­®‚±ì ø ½<MRβnz™9@¨‰‚RÊ À(÷]paDI×Úˆ´`.âÁ^³!+ñÆ ä°/ .¿[ï÷ç@t8ÎÌÐÍ­ †ågŒ+¹œ|"±En½ÉpÃR¯}•Á6ƒ ›+9䊛Ùæ–îs¨ö`î0^+ņZYgç› Á
+7 @n'—EÍ%'9ávúù
+‹¨ÌØw1ö0|DA’¨$’uºácK—XŠ=rƒJ$Ç6µ,PT®î²JŒQ:KÃB3nˆ*e9­{é~\Ž+×?WMºÂWÏøÊÂã¨ä¹
+b‚—çîA5ÓNE´¸G šŸ¥*%éÜó
+‰¼‰Œ3†ö!ì`žÅ­Y—›£‡VÁR’ZýÁ9J-»«† [GYÁ„ $K CšF -X™¯»¡*)肸E (‰|'¤ÒwÄ¡Ãü?ãeŽG’DÑà”[€Å¾ˆcP©Î h£âÜÞð¬dz4Æ~Åæî‘z‹k†»ŠRÌCö×I¨^QƒÖf5µT Om*p;jI¹_-¹2px¯aV‰Ž¯Uµ" )oJÙ6‚È7'šÄv‚À&°9,Ü8ÌÇÞI¶½b8²ú4±qÒÄ£YÇ M¹Rœa±Á….ȃìj)•ˆš1à=_§û`ï‰CÖ­rX†ñûû‰áEê8ë¾·,}½ÈËØÀÁwYÛD+TÄnpGV«õPË/Û 3'ée§tZ‡…YæuȾ‹-!C£{8„v˜O»Çs­´qõ'ÜôO©ª½ ZÉó0„Ñóa0aQ:®çä‹QµÞ‰0|Ž¤”¸Ã†”N9NÛÏ>@^@ÐH£Ï }X£÷=âÑåÈ7ØÀ zl÷î‡KÔdã-v#:˜{×é³Ê …WE¢æ±€ØfiÙe¼[Íðý©hq D—c ÖAÚÐæ¢à½Ö¥ÁCÇ«…"3GCYuCh¯&Rlb[eÍ
+Ü9F>­Gi^ßGV+vI¶D× _+!H¿&yÔ=îèK(úÜ 5”Ðr¹@¼ }ë»ï¹—hLrtåÂF1V[V1Õ¡¢N…êr‘|ZFâjFr‚xÕŽÂÃTµ}ß¹
+e[¡2™§C¥WÆ*+RÁ X‚jˆ!§/3¿DD§5èw¥<b;Aô‰ÁaNÓi4Äd”x: šNydíþáRrxëšG÷£²¦[òç;/¥º¶ºƒ°+Ê‘¢›öÃ?M7Ò@9ÜJa¡Ê›²ÝµÕãuÒb\ؘåøoœøÜj’‡Z}ØV÷’?—yôÍý0Nÿ=/åô1Šu£ìqI’7 …Ü‹$¥šòñ¥R×ì=ù]"Út‡½[Añ–07´ÐOäÑÆc»;èó;g2ÚÀ¶)Êò¾­xfâ· q+8ó A*’YŠÐ³7”»#
+h)˜-Æ£=$¡à…p°‡
+Œ"B$<xUÅ&V £=$›ÒD…lH><½NÀë ˆü<½ñò´‡ènÓÜí¡s«§“r^çîÈœ7¾û:§VO{èÔü¹Î£sž§yt w«G'ßý¡37ø§±úxM_„UöÅNþr±ºØ¹ü‰î ×Þ·s‰ãOgÚÄ‘!nLH$€ææê÷!PyH2†âj9L¼¤b,îú§·Ýôù3éèýä@Qj¡'GäOw+sŽìÕ—¹å#SÑâmn‡, Q mÎÙ¸³’ò•ä€#R¡‰D¹÷ž\Á«ÙÞ>pFéYM ¾ŸRpÉX‹¥]ûÄrs˜ý¼M©
+…—å³7ä”3siz®‡Nµ}‚ØŽ±j</¶žðáÔL›l`T̵÷é '3J—ÀNØ)ãêÌ 2bž£¹*F–l©ÊÿÚ[áŠ:…ví$þÜSÑ/¶ù –¢ÿœx=óu.0,ó|­òòË£1Hm¹¨Ã2r–µY[ØaÒ‘Õ7Öƒ÷ÚÈ×F_üÛÇ›òê0ˆ×àµê±¢GœÍXøñ¯ÿ¼ýõß7x„z‹¸)8?{cÆcFŒì(#iÐ\ŒÍ Á›9Ó Âò¤fÀº ¶Ëð)w(qCDevÎìñ{†»CþbZ °0x=ÒhÅŸ
+ƒì¹ ‰-ZÔ·lÑT”
+ ËÓ.kÊ(â® ƒ2R”v‡47¤¡ŽÖNÃÝ`(Ÿš][U0h?—–òÆ]DÓö:\&f·PMJy´J„1ŽèE}ûk«;HîoIÉÝŽLþäsÍ%†JEiBÛiLf]Øk >kPù媇Kz©Ô¯(Û±Ri"0qª`«iÄÝuO‚¨óÐ}|_ôí“ZK> g„‚~CIœ?]îxdú^ö`Q$ˆž+ÏÐ †˜6Î(/µº6š8¾ò²4] Œ-¹ W6ähÐëu  c÷ 8Þº2¥ÓMÊ¿³y¼¶ˆöú<€¾ø¢=½jM4<F3` ïÌ &›±g·a ŠzÏì Ú('ýÕç뉱𕛌aë0ËØ1xƒšïR‘‚BR03r<8„4å“[ÍÄúF"p* öi¬a¸ð¼H]D•{E`vª™që-›ÕK´,ÊIŸÌk£¤q'I^‡QZåU`úd‹¬Æâ—ô‰=pâ0ªwjõ
+5íß°ŸÃxž¨z«”^=‘ewÐ7¸ÂH4`=ésž§ìÞâ¬p3” "ŸŒ0v²µ‚¸Y‡iõ1¼ÔJÁÁ&œ™aUÌtÍýþ ;Ã{+Í"þ*¡ÙÜýAËtéY?@ÐqÊÙEI×N Rȵ GÊcŒ’ÑáKs8Ð;“€¡ÙÖNdÒ`]†É3¥§HA´ðK3I|$ŸÔË0 ½ ‡¡RÅH릪I|Î"°[7J=Zâùwë¢gf\ʈ‡%T3ò?æ[*Õ+jP’“±½&OiZÂÈ¥'HG_Í/]Á pí5ÄMj™ö2EÅH]Ž¸Ë-G¯E9Œ„K®‹›9/†¨‹ÜÇÿ/sä8Ž(ˆž
+“‚ ü1?E&«LfÍab”œ`f&nÒ ½`Œ³”;{£íÂÌ6Ï·ËÍd
+k×¹H;Y°Ü·›ŒÞìWÔÆ>™f‰»Ã:…[°8|º×>™ˆéh>£;!bJ¦ÀKVf Ï ³:÷QÔ
+^Y<ÿ[_<a&0dÅ¡PMžW£ì‹w4Oþ‘¨²Sp«MóA>%DýãšMϸ 4µhζ¾Ò¼$Œ®ŠYoX>RµB Ø4åÏÜèPiwÑ_%LÅó1 ÷eBY7ÒEÂ&è©šñ%2'>«‘¬J ö9Úà+4“ä…¨D-¬BÊé†ä/ sÄ”1¼•{düt%y p²Œ sÕ$y PA¯zóý†ä¢)ZPäå6¯>³¼z³t ¤h¯{:Š¬?f]ïƒâqT'‰æ»s¦±‡yŒÍòi¾ªi!6µ\X^3ˆKȵh6ËGQ(EÝR´YHa H=Éû;–?ƒÌ®³}|ÚwªZ_™,o€Î,±%dÌí2óujVKËÍYzûc’St9ºä§Ýœãªó¼À¥=gš÷ÃŒà)u¹£ù3È,žžƒ
+!È;u™4ÿ;þ4ÿþç/kC 8MÎ\W–§4Ö0°'¢z$û9‚»¦„fø”®¾ \@vóÁN2­Ml5TÍ*‘±‰O üš˜"µ¶xh$¾¤Š¤È­“–¡`2Ù\6Ÿ:!t,K
+s1ãKŒ¬ÊÏB·0ÉFfˆÎµuáˆÓQÜáD Ë­öMv]“r`>¢šo‚R&%ú1çꘉq ~ršà´nã±pµ±ÖYò';B­ÊÚø„Bøöx7‰©ðáì6õÂFÔ¬e¦<Ìs|m Wûµ¡¹ÌŽ:]oVØ1O\.3 «™Ê`‘ºvwÎs¿ÛœçÆ|ÕyüŒê“~*±ø¹Q?^Ù­–›«UˆÄÁdˆ¿÷ea¸7ëÍÿÏÕËÇ HöX(82ì(ƒVJ‹ÏíiBØ$4ˆëõ®éHuº=ºá´²ðIÞᑈ¹õÕ ("d¥=Ón}¿Ú–1bllÇ 6gÚŸ1Íè´‡,†ÂådñArC”e0c:ÞEÈìaR¸CNQ@49oo󤕒"ʼnAgU˜©Dïs­S)¤¸MvÓüðWrè¢~¿dÐ
+Ù¥ÕÛDù3`3rfhbªZ6û,ª‹‰n•¸o¢ä„MÃmÏe΂wôõ"4Þ7p·µÃ ºÌ9^:ö]˜ƒ€é'Û²,"¤5‡EÈxhò”ÛmÈyç¨lÇöË#–ý¡_.æPGøñ‘T'p¡†B|MP§øÝôÈogÉž1;fpZtIZŪU†dòæ;à#¹©ÎgAθ_—¤=óÎÃW© gïˆäÙx¯ãCå€'”oؾ‰`ã0‘²+
+ Õæ_…² ¡*ª´sÊU9jî|%Í}9c>†ÑrÚÞRNŽuš]¼ÊüvÉ|^]Fæ/ ÅJ0B =d´s?A­²ˆeÕWe`w²ž¿ê[ýCo= F'/cž=†'<Ù§¤¼á¼
+q¬sØ,Rp+ð!sìMsŽ-á…_¶etÒ
+ãíåó2€½DÁ¨p\kGÿ+ çõ;_ìߥ™–ÌÓ‰þd˜ÃÛç$†¸p[i]ÝM(2@‡·†˜͈u+yA’žƒ‘×ö„°‘è„kZc\D,P4lÀ¤ó%øp…X¶ƒÍ¬hH·˜ÂÍòIñv&¸Bàv
+!2Aü>- DLRRžõ!fŽO£[y)f)µd¡¥óÙpDÌË3"a»>ž-ò%}qÖ˜¿â¿DÅFL¡ûµ`~ «ßþzs:I Ÿß™à°èëF eîýŸ7
+‚ÇòÙÄÌ6ƒçŽŽ]„·,%Îc»€ÈÓD3Rh
+PShBÙr*gfž«þ5(eÏô°Î,8„d„A [¥ásˆ(O^b+N@â,A"Æl2 ÂfÁHá÷bLH€0M­ˆ;]Q¬f4‰ÈBb@ò4=yÆVX}°žä ½šb?øåÀZø+Â:¤kZz–¨õ7©
+I”å·ã¼ öÓ*k¿€>߀úä`ÌüžÉ6¢aFÚ¦9¯>Yß‚±¶ó V ÀrR¹vÕ ný´VÊÄnÃâ/Á‰[tø}Öiœkc';û¹D Â#d2¹$©ØÁÛáœÔC9Tá9b+ÄäéÔ„-•½“ü€`ûk+g»y¬õ½]„·%µóœ};DWN¶¥±ÔØ
+mF4d"cSøÓÕýÉ(¤¾Ï
+Ùuf¾Ÿ½?mª\Ç”^³.WI1°ÌÇthÖXâ$ßWÐ BŸ–C†q‚å_‚÷Áå¬ Áu
+Œ‘íãk4…xWêR*CÀz?•‡¶„x…C®½ÌDà›ñ]>R™;êÕY‡@YðOnÑ×Ü.{Ž5Vò)©›$yú’^“ D*¢{´Óäq,¨>ó]'+ý£(ÎGÚÅÙÍ„Ó¾ê4bDX5Vi¬G‚¹c+Ê#C?y«}@r\|>§ªôDú‘$kš±år… ¸Ž@2 ò1¾ˆùI24¢‚™Y(F×:ÒUöôùD?ʦõk\x@öÊoeqg J‰äºZè—šàž%iÒ
+g2]‡ç%í…\,Q€ÀÌ+Æ:è‰TÞ*ÃÂ}ÈlQ6«!‘üWăt!¢ÄÌÀµ¢wN.êè7®FsI±“
+ ¯Ã!Œ8åË.ÕJÌ÷o™t†YÞXþ pÊ”“«é#ñò!Ç¥±j¹¾KÙʨ°TîÃÉEU?Á $†“*F®Ã§Þ¬ƒßSù@>8¾îïÁÀ$*1>îZÝ„P0ÂC5J‹È’ø…”)@,I¿@ò–¯CdTB¦»úÇÐG(f([mwxq!§1¥i‰RßóÞ—ÎXpż>T5‡É£{uÞ›TÌžÛ˜>D;œ{Åc:©†5U`œ4‘•tc~7bU’?,Vy)¦¾Â±‘±|€i`’.Çß
+}†kŽìÊ¥ƒ±ü{qæ$wU“ ýã d¥øFC1@WŠ
+oPž7Æ‘Ñ$E3“Š U©âä60UPVZ_?¯F}IìàÁ
+ˆª Ÿ »Ú;O ?æz T£Ïrô
+Ïä´ÑZd;(ÃYƒ°¨r–YY9 Ê7'¤]Am™‡ÈT—ÌÈI¦<ÙÍ÷{Žh0pÙóùƒ\p*2¬³ EŠo!9ì€ÝOüÊ&§.ê
+ÝŠ K׿h»@¨6©Ÿc…â
+“‚üY† 4$ÈDîÀ±Ëf Á‚~³ õñ˜<n™;lãÝ:ƒØEtD¯ÚbÒ'Pan<ý\ß-ˆUjµÎŠê)jA±w~; …¢@îäluÜз¬P¦’µ­ÇÝAmª’4íГ­ˆÙ &ÄE÷*j«hSñc“Âè¢p:\ϲ—ƒ‘ ‡à›óù¨ ïPǪVœs#ÛíqÏUö k’¥Iݽo鵞uÈ[ÈÐ}ù#
+áË ýY¾³"TLZØ„nú¡(%•8 #S¬ÅðGíÙ‰rXVÙ~&%Ïd‚]ÅÝ«‹ÃYºæ·i@ÌjHÃœ;|]màªP¦òUåÖ×÷ÂóKÕDE°F
+Qø4ÒÄAÏ:ü3ŒL´ŽƒUé34,Îë‰k§¹Uß*Ië&»%×õöÁ´´B^ÚË™³e;šU]Æ`“ª‚û¨×0“æa¨'½Hf€phè¤@©ZKž[YÉ$VÈÚ¹¨)
+x)YG»I’3Tr¶ŸêÁüP¢ÂhÎg§oG┎8A2b N¨VziP.N‰:¦4ɬ^›X¥:*6^ùnúªŽ“ö´ßš,Í‘W@¾™Ú}O^…L
+çЩ±¢’Ÿac=ÇpëüË©|â(’¨Ì^vl…_eK!áçH
+± B%?‡ì'ëµÇ¡[I‚}'z™|µð‡näZ®+›9vÊR®Déˆ`JI”â]¾R‹n£·!;ÊÅœ´}…@BåaÒwÚ½>[½‚†ÌY_”{l…bó9È­S=át´µšœô¶w!w†h+ Alò9Šé÷^†.Œòs­þÉ+ª7˜B
+¨n}=_#¹S„ÒuˆF”ŒLúyN…p)7íC¾Îè++휶³ÆIVõLVçrضôœ7Ò . _ H–óëÿ/¢Ü?ªšò×Þahø%à8‚S5j,tÓnZ€ ©ìSD¾L( JQßôYÈÍ„ÞA±‰Ø‹íÞV. äŒ3àiu…d@ü!Ùí~,q‚€ÆžñÂ÷.ÅÛ33ö¯Âós0ö"*î 2"ɸL×J ïY}'nçÄæòR•(ãdqÇÈ
+X^¢|Å•a\øÖ6r¢y)ñ­à Lp<­ƒOk³³Y™O§‘Wʲ¸¯‹_n…ó*X±ì5…oȺ‰žO…Ì7Ô©tJ:aÿ–d³ŽqƒœÄH ö–+y½Ô›»à9óÎ&Ãæ3ƒ†åË>31f‡£«OY•VaÌÝù²:lÜ4¢)Š–0môpù­~º!P¬ñ„©àñ»®u|øu"ßI¦å÷ 7ÅàuáÛÀüº*Ç2ŠL
+áÜh6uþëã2TÇ VI i~´UiAµÜaóáì(a™<@}+eª%?Uo%¿ÿöm–acZF¯ç˜æ¢S¹þ×_§(¡¤0˜ñ€–Äwƒ²´]b `HRÜŠl1ÔPòç`Ò†³½RL’ÆYý\=açùÇÂx‘ ·L#ÙY 1©ÈÞÊáÛ؉I#¼”S‚/–2aڥķª:=m.µ<­“嘾ñ*ù¸£ \&-òö-Òî#[•õ­@ÀPs7‹
+µ¾¶b Ò>õœ‚Ø åû:%Á9zˆœÒ‡\L!/
+ˆË<t~-|±[l…SÏR…±"…L¸Abž—¯£þ2ä¼³¯Ó„ZÌ“E 'æ 6ÝŸ"^¤MÒèÈ`0‡fKÍ/¯!Ö èýšTBÆ›ŽweUÃ`&*=àîU~ÒoN'2úÓ[ÌΛ§ª]ºs*ˆU”w¼Y“hÐsŠÆô9—aÍÑx)Ú*¢½Ôc' $#S̼;ƒBÇ !£u$ÿ`Kå°`˜ùYËK¤{Î
+Wô|™xÉ-aû˜Rç.=¦BGZNo<bˆÅK8©ÈßÆ  Ãg¤0tê$R—"ÍaA°_çA…«︲÷ÆbR9Oöcž1›`¢–K÷üæ†ËZ¶·ØÀoâæ€åÆÈ2s„Iø_¦ÝiÝÄ«–ÚÿB j&“ÝÜ¢}Yê•„Ú³Îbüië%Ì7oOÎ+Ùu1  &»Í‚‹íÅ»ÿC…Œ2C¼Ý¦6ébß΢™ u0¼ª(³y)ñŒmeèK­O뼉½L*FŽY»}ŸžAÇÕâ2–Óœ#dz˜Òã)â¥qær‘»7øßp]0»—H³Á$Ô¿n>èÖ¾3KÊS}ý¡G5¾.aNÜ.äq)³¥mܶ¯ú,BhE”¸ùU¶QV ð«â*ßQd †)¢àS¤ ¡°‡¦a%º¥fç ÛæžýÙÎU“Šà­ùYgy˜¬ªîÞA bÖ²Æÿ”èð¼!Âê“NHƒ1 Œjl%k_Ä//ø&jô¢K•™ØÆ
+âX\öi:ˆßW“ºG˜¥ØýãáŠÚEä)G¢xã ä6Î:„;ÄZä|Áµˆ%ê)±†eáöµµK‰oU±û-O¥–§uÔ86¿.•›$›[æ©  Ø‚É)»·TŒ!š SÀÂ'بxûö¨C4`vß B[°¬â„Á ¦ŒÇ6êµä ~¾ÓGÜò 1þòu9Œ}‹àhú=¯ëÆ7´™wÚ´«÷Ö Â Ö‘9GàóY×ÊGl¡¶†‰S¤Æß)Ékªo%¾8ÃeÂ2ÙžÖæŒLá·“Ä66Êw¾Ê×Ä#GÅ?S®ãê²ÅÎ¥\
+8gu¦Îéó’MyÐ0Z^BH.L]Ë^2:ˆüØxjŸF“ä±™¢ŸgX‰q×[”¯Ûx›Ò颽xàÙ®4aß=Q§ýdÜ8lÌÕùfýºÓËNS>„J~@OÁ3’|™d²Àó‰$/E@…–‚Ý|†œçb!ĦyLRh⪓i(azqÉÕšcÖ`øº­›ž§nã×[à”Ç5ù³ä¼Ä'¤§Lƒ° *.š÷©Þ
+ÑÃ/]ã×- tƒá&h_Z=)äæÁD49m¨öq_ e†’ïcû5^{ÕÖÝÜNŒ!¨
+î­ˆU’…LU¶s2¯Ñ´¼HòTÂÙAÒ~]õ^ÄbÐ" Šýª÷¯Qø"55pÂAÇW±¦ðKkZÕ4½œ…ádÅŠ¯;¬Þ˜0¬g T^šòsÝ
+7²¶àøUx ¶y“ˆ`
+&•àVÎX{àäh{2s«‚H4Ív Ò
+:qÉ Aà 4Bq†Ô»ÒÞܤC Öªñ‡)Ø®'Viá¯BÙ%½aœ¡ïæÌ€§Á„(w“#¯ÀHæb¼]«”ÙŠï]įhDð1Ô0¬N¦hÁ›ùqãYcZ _‹5c™ÉW6òˆõ`æ–X¥Õ&þbT%úê0¹JèQÊ=«Q| Ñr ¨•ÄBêAio—0Nvv+åPbW18
+—üÏs@ ¾!V£‹ ÄDaÕ¦éCemɵ¹ÙÃSʱ/VŽr©2[ºÉÛÀo½ó ÁõÕ:ö¥yÉÌ{ÉÉJŠ°ÿðíÂÛØ›cb*¡zh`««&A~+±öáøö|öysÄئ~ <° j?Ò@÷‚UÅ SíVѤˆ‹Õ(¼)³óë´ÆR+yéY6ä%Ù#$îä%­*iÀÎ7±á
+(¡¯ÇYMÍoìoW±<1WY £8øi7¹J;gFƒÙd¹3þŸiÁúPòÆ‚‚hj2;Lf=}?U,ÞXøS òT—Y­.|K©Y‘ÉJ$= ¸·ôà)b
+ØFÆ´ö¤E¶¬Â\Š¥XˆÙüÖ­¢rÆÜ%kžxKaqdè,1!«D[ÉQ€í±Œ[ôU‚Éc#b5;¸Ó™øR0gÐo×OïjWu­Àw·2TÁçT­û`>ù'ñÓ'ÌüzBix@é!¯ `ƒ†ˆÛ™ È­òé%^ô¢æWad[3ÞP@B)Ƙ]N™)(søB7,Xûwã2ü—ðSUÛèÒ%5Û¾¦éëGF6L ;wõúÊt¥BjbRðìWEÅl¡à´ŠÐ:L-ô:ý½Jöxz
+ ö" kƒªœ¥Øšx3Ò¿®ªr¨››ÆžÏlp ‘¦}zÇòðÍ,›Ér‹K(ù°"ŸEà”§YŠšÃÎá}E/p~’úa×q!Ú«sÑ(¢•`Ñ
+þF~Фo„›F F€GŽæ›˜e<*6zZ‰ž HÏbl¼‡e ×ZÐ:ˆúV„pÄ_ÙzÑ_6ä¢-’ ¥H2ž?Ø8ñHÀá<°a&pø+ü]B‘1YÏ-Y‰åEÝ í¼¡KH«»å[ЮøTFÇU§,fW¨&9´_Ÿ|?gH_XR°”¯øÈR£lØhcË*Iú¾C3¶Äž2YGKZUjBãYø¾ÏÙ:‹ù3Èr°=[ Ç æd×P2÷÷ø(Â"‹ÜÌ;Q„³GRöÐr'ˬÊ7)²ùrEmî+Äùs£ÅŒ%BÔºiÊ´’#Ôg;‡ìÃЭ´E j×B£}x™eiø±„…”±
+Ãn»Ù
+Àq 0÷f¦üby2 „B<KD!Ò™î
+(ÊÂ#ÝÎÆÊ09«P©©iŸ3dÖ`+ݽ;üÛk¬I€ä²AKÞ¢ör&~ÃKÜ„qcþ3­’F\œpLu#CO”°ªÝK¢ÌRIÉ%–Ù Ážñ› Á
+)²ˆæcx%}Xg×} ¦°_%rH¡«D‰¦+…Ç ša4eéÜ\|žÃ;2 Í)tϲÄ4 éí»„ÿ™á‚?Gg+ IoS«š5b² Å‹JÁÀžà‡€åÕ1.ç˜@ZÓeŒwÉ
+{Kª»„mÊ‚v±s†Bßb9V4…IXÙñA×M÷"¶“Õftaø9ün'hÆ6I|ð‡Ýû':fuØKãä!'ž§¸Ð¯‚ý²Rà•¯ôîZñFNlBä‘£«ÚOÂÝ 20[´æ‘“YdÝ‚³Çs*w¡YD0—áwá£b6¡@ú!jòˆMò»¹Ô§Gu-
+ä*K@Ù}ѯºQMP™8g€oÄ…€òÅEB„à °-ŽV´Ë•æΣØ7¶yLúÓ¯3cãºyVÀùÕöPòFêâÿpñ?˜"1aZmË©òñõâ:; RWÌcÀœ…³Ðýäüß™(!ÚnhS™ÐOÙ*RHŠ
+éÏÿ(Y=Ö¯ÇááDnx€0ïò§åø~J$…"³½®ºU\&Á Mëv ¨ˆÍ«} Ó¤.š{Ã(ÉÒb˜Åß]”!™ê@?Ô 2Sú9GšǬ™Faì%ð@æ1ØóÂÄ”ñgÒ=Xoª/‰r§A‚SÀL² &'#¶€æ¶?$­‹†Ö3©K¨ù}TL‹îÚiNþJÙPœŠ˜ á}knF ÊS
+ªü@×ä¸5õÓoD ¿ïÛC !iT”4ð<HÈ)\‚â®ÏKõÎ3ÑA´ñ>ªPå•ÆV£I‚x«Æ-–Ç¥`;>„ÙÇ“)®’Úx
+\33Sp›ó g†¿8ÈnP£aë…&=sñðè6ª3(Ì ÂäÙœ
+[0PQ”æìwû
+vGµqS¢˜í ´5÷%®››É¹ÛK†ÒÎ?I6;Q6 ¡¹îcÆŒÕVX æÚIá4£Ï/8&·ñë%¡Œb׋&ÑNC³¤æ[
++Æ(xNsd™¤ðk2³E¢ÜhÚw’LÓšíz)Ï°óAdo+aØÔ}уZëAc…¿AÓ×wôÜÜTÉ^²>,…þˆõA‚÷à(YáÐÏ_;Ñ®7«„@ÖáßÝYk³Ü["Üo­ƒô£–lD²RBÌêÀÝÓû-ùŒ§RçIxCÇKÌ"éra\+ö縉˜W³ôyÈßy™Qö
+Ê&1Ù:ðq%™ ÎT¬š«ÜŽ&âw3Ê!©dÄ(%Ïh…Þ¡¬Îƹ‹.Vp“\,åAò7¦ìOuÈóûIãiíù˜碣cŒ<ƒÄž(ÓÏ™œÒ±/$%ƒâ̺ ×EY "ÔžMB3â0KC#¾Ì[Ï'Ú•'âªG3¤ã˜,ɘ¸: O<:o4Ê’LêÐ猩.Iç3¸MŸ ÆȉŠbÊ—â)šÌŒ8–(ªvÙM¯Ù¶,zb*iÝg<”æPüX
+¢¡ ŠE-Ü—ÆÀp͵=W‰zØ­‰¨8‘3D“?¢NÝ}LI4Zb”ЯßSLdT]O‰-¶ñ"d¨!÷ï`;>OÄ>ØjdÐêKR°Ø.€-v-ŠŒ¢á+ÅóDÐýعB[Š~HTÆ:ÆúNƒ«*³h.áø!°‚ œÕ“~p¹öÁàùs*U±
+50íL
+â"Vû•""‚·[&Ú¸NÒgÉ&Q#ðäüÒqè;Yþ
+|ɨMDsgB«ã¦öRÂ'ZvvˆE³GÐsZýE^…¼Éå/[
+j§y½”Ÿ¾`g&$ìÉQ†v ‰há­£…B·Dn´ƒ&pŠÀd7•Ï=Öº\Š‘~nH2§X5—Í §¸!ÑÇeä©mHeçY€J ñ2òÖûêBg¦FËé}%á‡Xg(Œý;¯N … å+áX[ÉÅÑ?1ÿàæ_ÔŒ
+
+S›¶óÎ> RP÷ø³ºK$ÝRߢ„QÈ!-õØ {G«Dª>LãU d¸ûJ#†ù‘&ÙpÿÅaŠ,ô½rÊ"¨Yß­¾-b&^M8aÃ;rœÂ7З"û›ZŽkS€×eU˲0X"껓¬è%S2~t›ãÍë„L>!‰y#ùgnþÀ§3Ö‡¥öK‰7šH N¾þã:L†xóXì!®èÏéÞʹ‰€ÔÊÚÅwZ˜Ë"ãµêXqqD™xÁ ç³bF«8Oȧ:É„±v^§t ùþu paHÙ&[E}’D§&Æ…¾v°_<gˆ•†Ñ³±5éÃÃCàªKHI¡jÈøÂ+2ÅV{,ð²+f·K¾å”ª»óAè<¡
+Á´Q{¸ÊŠy2 ZüCí‡Qx¶0{h¾NâUö{lÁCÂ1WÍïŠÒ`¸ç” XjÉÅX|ÑcoÕõb6§QÆ aÏ@èv!Ûa÷‡JÂa€>ÂÆN<;岉aÝé;ÒŽÍ9vbLV#ï͘uU2=ÏŠ§þKOéÃp0q—”}]KÀ!…±DÚónõ½˜Bâœ(·ØŠ¸Xvé
+üÂsVߊÿëL[^…€Ñð0ÍÛ¿$^n5OÓ^ø*8x‚¿#‰=$½ETcJù ŽÓL^ e=¥+A+õ½ÞPç—jÿ
+‹ä’ÇÎX—BPÒ,I/MŸ`=ÌN!ìc)}º$c@¸fyñ÷AËM1±V=ÞZ!i h¥lÃMEå´ VŸ;‡}òiÛt@\#j€¤_×!¥ˆ4p"%ÞæêVÔBœ‘Ð>Ž)¸iXJÓOØU gS
+Å„ =¾m!{MÉó.aÞÄÌ´ý†ô{ÉÂÿõÏ­¨%C”†2Þˆ]]?¦ãVf ÔQË…Ï
+l Ê6|æ/n^
+8°r¬ƒê¬ 7þuY§Ñ£<0•UwJ
+Fù–@´ò”+º¼#ËÐŒKI`ô;Ö¿E!J”:sÇs¼j>Œ.hŸÅ=F¥¦$q¼«üYâ­€ý^'t]z[âߌojQòðJYž,0ÉzIó%O™H%¬ÒŒx"ì=R·ƒ@U
+ˆ@«gÃFP”ë©uÞ]è6¬ô–i· ›bàpSq’®þ}ɛü÷—ÔÉÖðÒˆP‹“z¤BO·%cžð}(ÆÓ.#ud³gô‚§üÃ(Èýñ RWüÁ4(ôÿ
+H‰Œ—M’[7 „O;èQ‘ÄÁµ³Ì-R••}ÿm><‚ryôTñÂ5£q‹ F£¡Ö£­a2ö˜ÏXáÒEšõùè-âi-¦/_Óî ßþø÷`lôé€úJ˜={3‹îf&ãñcƒ´‰D̈nÔÖjmŽ©æú¸ cñ'ñ‚ÄœC½ÉXsCúï]#Ú(Ètí2ÆXÚÿìP-Ä´…òåY˜yŸm6Ïsì¹|ðã°s%’ÎÕÄƆÄð9d¶ãr…²§+é2ÝüÓ9ºzôÖÍî_eO1Ïñ
+Õ]CæèÝZ?¡FwZÍtc†(©›ÄÒØÇ´|ÒQ‘D´/NqîX6|Ã&¡F®‚®¹v }®n]B3]dA"´õ.Ë$”ÒʤÁ™"‹ÈÎ7eC&¬Q[ËV…
+~÷Æ©+ô„‚3¯­±CQ:ç2FQE+Ô”«)Çû†^8äƽçˆ^­}4—¤é.ÃÈ!}j¦ëïÉ3_íÒÜšþ6ˆü,¸¬«eß?€Ô ÖúZ1® ‘¦N%Ö¨ ‘ãé0jrÓ‚ôaK]†VEÇu™„ÅÎ!˜Ö¥÷E[¬ûƒ礀­+‰»"Qâ =?¬ $È%vE)ñäfô­PW¹H ¯¾AÓFdN®sÈ=´¶Ù®O·É³Ûf³ #¹å*±6Ä> јtÑ Õ† Š5zCÏ«õÞ™:‡ó¾Ù)’Ì!Å”ì@H:lª¾!’ÂIöf˜N’j«/½}/‘¼EOΨ` ~ÇJúãÍQš°Þý9 z½˜ÊKuvOÔÓaI¾šûi
+[–LM*Üäç‚xo’÷QÕr´  ½ °Á ·jEÿC5"5;¡¸-â­TuÈ'º¹˜ SëœIjÊË‹Ä× ÅX³½AÃV‡Ö‡+?(:I^²'x4˜>:%Û®YCaíá>
+²Þ´…ª'‹•ºôW9y _¢dÞ€ªœ4½,Îu`£Ð56oå‹g»#Ò‹ëôÊ03%‚Ûõ~“»£¥ƒ)‚X¢÷U((˸ •y§È›òϱG³I®OÚQ?q†ÁX¾\~¸ªÑ
+ÓkAEBˆz3í=H-mßk*¦8[ 7P¡x C§§¼¨“pEÖV6ËG’n§aÒ)ªÆýfÍ*„5DÂ7
+µ YŽ3·yÎÛ¬ç6 FG¶s¿µ y_´¨™ä<:–è©®Hp/˜¦0M}ŸƒÍ˜9õ°œ>XTn
+³þqg„îò÷néï4U¿ÚéjèÄÍ×xÌYгcIõ[] *-'¶^r©o$ï6»‚ø#|$wCŒzNˆ{|ÕE2ŒŒ¦»øgGÊqcиêÐtGŒ3‚-ßÇp¢s»%VŠ½Lþ« ÒÇD«@ÍOf§“8¦p.7>ï‚ ÖÊܵzTÎ_ŠÏhÔUú†6¡!0Åd:‘HBÁècÒ^Z€vqcø|ò×”iˆc¯sx0ãÉ!ýEù†€àI˜gìý‚¸"µgj(¢'åÓ1)Î|Ê;—ª+on`ü\KÓVÏn’o1dƉ„b÷ÁéGK5ç…XÛZ4¦}ƒ-°Ã BäT!éÓ7$_Íðbšˉ KJyì J˜é8N:ßÄá4…íc$oß³q«àšæƒwð¯.s•»smpëÈ+].ÝN$Á0©)s]‡Ò2…éu½1ÒFG=êr1¾-ëúñ‹Yð,û±¿B}ñžô“ð¯Î!/¨{ß Éž¿rTÞæÏqÖˆ";éãW©ø}NÈïk[Nr̲ÂØV¯J'€Œ¤FmyiÙAHÛ-ý¾ Šè,à›©Ýh÷þJr2Ï ‡ÐÒgµdáy8âPÁª0±h¡WûŽ4¥Æv?Š÷Ѥ]‡·Ÿy=†3¬é­P>4,':`'ÒÁqæ4†™é=ÈÚãN¶¨”åî½¼Ï;õsŠ‚Õ¤rPö¥~_DÔ÷îÄD@—JoÞô¾$Û%·¶ßôo¨Ûêßò¹0G²ßºØøÒäŸ8?6ˆ;çDÒZ¿€ªÓé
+Ò4² /R¼0£¹›ò¦~¯È@F®%9ýû§P¿ŒÅo^õ èû­…žiŽpÊ”‹*Öe—$¬ÕËá ’š›e·[[;³`ƒÙÆ+É-WDMµ­;OT…é 7ŽêG—*Õ9˜H„‡õ¯ë™‘K"ŽpÊ ä¸¥_§ÇÝ9hâœæè@h÷œ&¬Š5:h:vUžP~ÀS˜Ó|i¾æJÈЉÝTäÅpo8‚Râì0m¹‘jM&+ÝÓÆÝL˜i*òd¯áƒ‘ÂbOÉò—OâÁÇÙîSPª431ŽëX¹ ݲÎL@7H5o4/×ÛK.¾zBÑÊìž®zƒØË,Ê™NxCð3Á>"^>ѲòŽj o–ö„5&ã¾k»ÛàÂÑAÊi»n_Å
+ÈAg"æˆõI i0älôûnÎK­’û¤½Úk½=)Žþj &ÚÄØ<Ž2G³¿òÇ`â+é(j£"-3Ûk[7^LAöX)®ÌOcÿ@]õrîPËu š¢ŸLÃ8½Ü¦üµBqL9îW¬ÜÆÀp,¯+ S;•;v&3•[krúr&³"«åÖôéœHÇ“a,Hî\ˆû„ÂÑ 0¿WŽå2×#ÝL|Ø
+hÞiÅ0/éçú¶Uÿ/“In$Šž ï $8ëÖ²n! WY÷ßöû¤™WE8½YúÁÁhö5 ƒ%ùËG}æÅÅa¨q˜ùRMd=ˆ8z¶#w¬>NVpyæð0Ç$ÔmÄ$ÚºâÆçô(‰€>aåø‹ô_•á ôý
+„âyñÐõ,B\
+†Ôg¢³Kïj-
+Ö7P`Ÿ$³Ü/4=å]Y•j pÅ›'¿…\~ù¬6C$SiEP&¥ëÈ
+¶ô!ûû“O8žV©5>A8.——ºZGáG°&ÅõAÜóÊŠ^ÂM3z>W]x%Ói‡¡nꬮ©ú}€×{&ÙÿJÓÒ¤ý
+ÊÀ}©¯ ¢G±è0HÑY
+ÊÀà$à‚*Sž<6 ,¶áºÁ0\;OØÃåïeôž=I­8j ¸¡sCôž°3ÔìïIpˆ²`<‡ï¡r^Dš {Aªä¡¥¾‚àcz—³57…Q°;OµÔ·éKKÝh@ëõIá`Tê®.EĻȗ» )S6P«j¥¨*gæϳ'r c
+…Mò…Âuó1E*åDÒÐ q [Ôjáu4utÔ,ة؇7 “®1yÌlÜl ËÖ3mTÛ–ép;&µ*V©ó—¦†É¦DiC§ ™H«{J3I¦Ô-ØNE²‚T0úN¹Óÿ-2ÕÑ’(+!gú:ëRvšˆ¦£;X­07$6œã,ä¼qÐʸŠÕŒ¾UØo:±ìîhÜá'.šö:¡i?Ó<,äBµqB|ý‚•C_o ˆ)¥å‹$8.§-È„Sªœ£ ¡¤Ê´%Àºá
+…Úr9Á›Y#'¤ž—üÅiH›êîQõíÐÃ4ÉòÞ¬òä˜L66 êYNŒ«°#ó+JOÈ6Ã1+aŠ‹l•Õ”<E/2tå‹ø1[æbÖůŽ451C‰võ‚¯P†Ä_˜¥ ~FÍK·§‚‚‚4–ÈêEædc9ål¶An€‹ÕtA{ä*HÍ|€ž¶î|ŒŸLÜ»äNýÓ#@j¯ß},‡ îòyù¡·ævéæ¨þìÌvÂmñ ‡SV{Œp+bdxÑŸGƒTÐAnÆ]¿@ÄÔÌ$‘wÃG1Þ4à°Ý˜ëæ ·†ÝÛ^<xº5Ï¥!pÄ®PD%`aEƒì*Qèx¾pZ'|‰xê)¡Ûv¹Ó%Yž©ocD—ðê³Þk£
+² us`øRIK'«/<ÌÄP½º}ÖÅå >.7ßøšü@¦ñ<þžŽ6„q!ßrâzÎXák±ÒSJô¤†¯å{A6Ú¶Rï#åcº¿K,L öº}—†±Ñ´_Œõ2ñs¸j\óf¦è­%±uBŒ$D¼} ¢ÎyE—ç«ÙO @ŸSå‚VKˆïuü»ÆøQÄ”°TÓ¬Hr¡h¸­£«Š5a/²çôEkÉ Ä .N‡FÎ…‰è…X®Þ1º9Už¡ØV$®|F”­"êìþ´ ,€)Ì…z^)eè ;4ã£j?lÞj 0Ä,†£#í
+—On[a*ˆi¸ÅáWÿ= .Ž¦Ó
+ç,•’’σF%Q<p¾Sâù> ‡×¬»BHW¶ë«-3ŽÚò1›;;A"éÏtôÐsGo
+ô†Jð}'&˜ ¤šYHËÒRB|È^‡aââ)àPMïYôQ…y€PÊéS®ÙwÞ@oúröI7’1«Ê‘Ìb0·ÇÉ_86d&$Ü?@œßS³v°¬Ë[ã³8ÕžãA/Û}r&;úm­·(¶Ž>‡B†H ˜èÇè.Ø2Ãòº=š|ºow»ßG WO¸0ŠdÞLó9æ…AÎÏrÒ°«·=KfžèjïÏ·ä<Õ<ÃÜ+pÇÜëÿÆ3Ñðãç˜û^š^ERéßã“Ü@ù o
+s”<Q° Rø(S›­Þ‚î³r+äèû“j¯Gùëo eH1sr¯nð:MßDéy¿‹0ÐL…˜kïЂdå!åÚYz½ z9©Æà (6B´FôHƒ+è¨-Êö:LÝè8—ýWo[:ŸŒV:êî˨ Þßb@iaææi/ËK`í‰{÷æQXËé›D _B+HŠ’¤œ›¥H”7»oEŸ¡â­°Qð,ú’Ë^§·,c…k´u*F»*úäX 'aø¨µ%eRe²Zdÿ ïÐÿ/“$;n$ˆž ï ”a
+Ò
+×dØ×NHýÅ}ÆG Ã9ÚÆ0Æ´#›¹ìÏð6Ò]¶!ÑZ?Â8¿@Ò‚Él+Åø<@ì(óNéÞeªé”{ÞcàdÕ1ßì[öGA¯×’¯ I®LÆ^Q?
+ îÿ0!áýc±ÛÚõˆㄠ`ŸCþ?ÁƒŒ¯C´h
+h)‚*+‡¦¨¼.ŽjS¹ Ê‘¹9(Uk>]üHb¸”C
+ü€h0°Ê”ƒ‰oåJÄBŠ¶‹x
+ö(›•€Póĺú¨#J’/6·ÏIwŒ^ŽÇ``ÎA?î}âbëѶ|0)k¼Edfš5Kýü•ÊëØ3„¬¼!°YㇿûŒ×gÉ ;€~8ˆõAˆŠã|– P&“kÛ˜</|‚<Þ}
+é-Þµ}[¨äþïx¦ØŽtYÒ tA(?6Q,k³ÔyCÔ5,g-kæÜêø.<†E/¿eÀ ßĵ3ŽpK€µÃòAú)Ç„2"ÔàÏ“ø[¶—ˆ’¼ J`ô’Ô7ÚþŽ|
+-/­~È1 ’GZ±ä±“ó36H”û¶'±–$Ò!Tå²²”ëßÁ‚’Oi¯;Ž†1À4'Ù—ˆ1tp>JºÆžÐè&/G‹ô Ùÿ2ƒä ™‡Èg<‚G]9e»²¨o¿®R„?6«Ë´)ýmȸ,¬|‚™CX‚ê,ßב˜.ͯÙ{–¾´#xu+Ëð
+,?ÿ^¹D¶¶÷¥Ø‹ÁCóªï:¤ñÀXy³w‰¢VqøƳJ_ª5ŸAN£ùeŠ Óê·Áú9˜ŸXd¥Â)²úÄÎÄúóDñHÿÆELëÒ·ÅK9›Ã@[Èë7™¾ Rã6m–ÄJжBÉZõøº®ê´i¢§ ËƼf¹¾¹!ÊgDHÚOtÈáÙ& @ðR#5e— ö*á,¼õØß¡UYc8ÃBƒ…V.†­GÈmã¨;¨C¿E±c^VèyB%ÿ¡9‹‡¯ã«H‹$þ¢5{u«yqª{œ_¶¦ÆC”
+µôyÈ‹»WY‰¶Ÿ'ˆ÷ŠÁQÎ\"Å=¿ƒß"‘á’—ß‚#× ÉQ&29ÕÓÔ„hËÏ©ÀY¢æ²3þòŒ‡Í* WØß¡+ÊDS´¶!IÁO›×üݹb! g{©ŸvÞé¹1*Î(‹(ÖÕ ÒmŶ?¹Iª©Â`BF“H©œ·zPŽ±B
+Z…¯×?ÎäF¦Âðiç¦_ñŸ²kÚTYØæ °øs¼QåTÍ4’ÖóÞô AÐâ)«þ82–¢¤âGú†”Á݉ÀÙèNð#ÞN©®kvoX_7rrÊ´½BâG‡ÀA³.öoV/
+Û†¨3¾ùXb ýêºnž/­ªó‹SAD+Zuk9²
+J„•cE€,'ýæmõ¥W"Òqõ‰„[=*f¹G´ŽÅ›ü¶…«z  @KvŒ1,ó8ÈO‘¹oÃ/µJ²Æ\t~Á)k‡u„©hÿº$í ÂÚcï`'eU#M
+P‘¹4"Ó¿×Vg;’6/bÏÅá)_æZ%àIÁ`‘Ÿ…J\;K‰êÜøGx+,­í­Ø6«+iç&¤2HÄÄËdªÃÉä4ÛÆÈ8~‘9Yò!xW`²£Ò{䢇ˆÿd5`™4wÿ0ö¨4jC hòÍsqƒ*Œ.0ô8ÄCIì” ?ÇÝNE”>1ý“Þô}ǘ]6‚ŠAbcèyÁÀ!s
+¯ø ‚µSA•YÑÑåýš‰ÜF|ŒË\ë É¬ÏGÃÊà(¹Z%âb 0š?d;Šp:\ÅEEÑ”&¢kîkð SÁ‰Uqü?qØ[Ï_mk2Ú€$"ª½SUã><ï Lj¦×lÙb\ÌG»ùg ¡ìéÇ’v‹Èƒ‰švoõ^Ä4^{Ï·’¡‚ÿ»‡ñÑÌáŽ'ËÇþpL(›„šÚUkúZX,øב™’=.E)zÏeG22\É—¿<Âï­$àÇg¢ÈÇ>­ÃžìÜŽ½UÃû2œb·˜©¢wK2ü»ÉÁ"ÀŒÐÏô
+)¼9*·o…N·šÖ;| 8%PÔ¼ô½y‚öaMÈ™èfö›’^ùÿÙPmn€ÁÛ”ôÊZ¼Ôå¨W³~d? *Ö^í£ßì÷Æ¢Ne†—‚p>;ßEó"‹ð?8ý'GŠ/¸Å #º.¥X†S™´æ‹þáB*¦€y«‡¢uL
+ŽwúŒ¾0ä~f”ß»Hdñž“[ò"N»j¦ G´º4e¾„8yd`­“4+øöXò¢“¼~± 0¶†ÅqIËiᒻȊ1åÜk« QcdHYy©;n[H–# \c‘¯‹£>uõÓÎFμÃ3*ð X"«„0¼uâ±7‚&¢aNWÓ;øBù.6Sä ­’ZÔ¿Žs5—æ“ù,VÉÓä€8²îŸ`JðÌ
+ǯýP²]à«~œÖ‘ö«÷Íw‰OÉ?~#Ø~jŒ F,,AGHr¡ä½6‘e³¹›ƒÓh Ž/AÆp%ÆäÜàêˆkiôç¨ <A‘€@¿83DpÛØ6‡7‚ÏÞ¤_0<öÌÛÞ¡>´|–¹u#@dEŽ¬‡ƒËñ Ò­ðM¬Qe¡#’`Ð è²–X‡¿™´ŽÑZ%°cæU æê%Ÿ¾ö£ ›´õJã÷é4Xq‡yQLy¯ã­\O AW(ÂŽ­„:ùVßñÌ4?~‰f]ëÀACF7ïà†@båõöK”˜÷…±W ÆÝ“<·»³Êiâ;,âY“³d`dxÃst˜µñÅ´'„À­D §à!qô3¶ª"þ‰ÅQBÚˆ‡mܳ®"ipEb“‡rl3¸ÅŽ”[ä‡%Ûê ™pÎ=%[‡l,™6•§u¦f2ËÆ­ŠˆQj°(ìc‚>2%—ÃæKzðs0 Hê‹6ûosk2AŠ/Q2Ñ6âlíHI§òÖÔeXKt‡¡“ËÅïÕ­ÑýAÀ½ÅÅ@‹Ík9j4/^”ÁÆ,?É@6 r¨QÓ™VÞüJc‹®
+Öä-c£‘À/âÐŒD ˜N„Ó~hÿUÞŠ¾ŠðS‚Ç茣ÆP’M(Â;Ë2I—,¸4øV<Ùv<lÞÈÁ|9®W ÿ­É²xHÇÍ4aŸJøÿª¼Sêí¤¶x+üL¹|Ø>2§ÔÁ†dÞ2dánO%—%e…n䌌»æD=Øq~º&¦p;ÒĆ!±8 Ο¬x:÷iÈ&žX¹æÃ
+,¯ìUVÄ›_xQÚ î|‘D›yYîã­äϯ~Ùï¿´aâT´š=#B`
+âg®uš‹©³¹ÇiòdM|]I)Nƒ ±~Û.ÄàÒDÅgÚÞ
+ÑPJœa¥«siœ*¨‰u*-LPl³˜ ˆ5x8(s•°5£ŒoÕz™®Ø
+‡”§ÐÚ}ÛÙø.[Ù×:ñ ¹Æ:Jq•$®DŠœá¶ßJ´•Md
+k°/NÄPLã³±Jj§ÒÏEsô\H•‹±·ñ(?s7cKøé:ãÕ×:/¢ä%Kv•@,=úeÛN%oÜ[½‘Ò
+£%Æ4àUV#ïB‚³
+Ͻ‘’‡á{)ÙD‰6±WÇuÄÈCG\ʽÎëi(!èf #ÑîáV1äÌ5sTüØÃw Þ4[ãØbJ(wÑ]öðT)˜Æk?ÕÛ“Sù— ×8”`Ø-Ëü
+ó®JAI&àn$i®Q¤WS©ÐÏ„ÁwUw2ÈÑfÈ ›ÌÏÒ_€îÇ)B2 /ƒö'DæzÌ.L?Ñ'?ˆ•®ñ(¢ØÎÊ›1•?e¼€>¾S럫%·”D Í’¯æ,c?I%ðå¯ É|Y~.m!ýŠùù€Á*P|UfÅ Äïø¯Tíq'פ§ë0¶Ëï îØ. Œ°Õ™h±oƒNÇI*7Gjbù Э‡ÓþŒiÒe"™zÎ&}<€xUV/1p²´'ÞM¡`~BpûÁ¤3
+£<p¤-M?JjcìaX++»Ð$×!¨ºÞ=“5“æKYŃˆ…£ L»èßéöÌTW»2‹ÂPýâ9' ¬ L͆ÄÆÚâÆèˆïÜu¡®ßá/Œ‰¼{yuyÕN²ØlÃACí(´f%ŒÚ\heVqO¬üþŒØ´â{²Oº¨:Æ`™ ¬,ƒ'l03Hþe?è"\@
+OL–Ì.»œ]ª­{0¤¾ÄB7²"CƒáÊÅ¥±ÊÆÃxh?‰Ù+´%×fïÆÚ0³qŒ0ìMÈ­7Lf—AÍÙLr ãý¶xms¾R„¡»)7 0<)´Óþ๱1WÓ¾ÂD$4¸æ·ƒŒ_xÚÏá®…Ýä,£{¶ ±FÛÔ(HÁ•°—±rØÒW²¬²ýuÔÿ¸
+³Òì´i.¦ô€˜F:Øù_ ›!
+h6.*w|ï_`¾öñÙůŸ:¸ØPßÙˆP4ïöë ºÔÐAý•Oøºw{Þ·@“Ð>f|­ø·!w[u­ãóñZ¯Ž¼ý#dxgÈC°H±Á”
+‘t0WÉÚr)Êà‚íÊ2@2!ÊãAv ¿J¨,É ¥g8¢N²‚ iˆ¨g (ঈyæVª¸ê%¥MaË=>‰Ú\ì‹æî”D.¨C¬-VOyuHø“ý(íz©dÆ=Gõê„–(2¶¿÷6¹hÅ qQ¿)ó !…«„˜´éž9YbÂ,ýXd%в‘
+«¡yﱯÜ.§€ Bƒ|©OÖKe.¤C¦‰y€ì£° :Šþ”öðT9O´SÂë·ÅQ<¹2!
+šŒZ°ìÊŒ³>04ãj')¼@è ¿V TfzÊ7,H¤2SÞfÚw8T²hxAfo¤É«’w“_ L±l;·@¢öˆç®Éú©7ÕRƒÆMi;¥îÅ:UøEÑ,!$"jGòØ; •bN°ux8;)ÐOêÂŷ˪*Î’³ðâq}Á 2Œœ½íÊ3²Í,ÁÒÖhe¡8£uñÂE(ûÊE- ?Ù6 ü&!| Y:ÖORƒõœX 6Ù[E¯=VÏÝû—¸±†‚ú±Õ0a®»{š¿ ÄF¨¨ŠïßQöQ'q¼fZÔ=ža„îMpÑo{ðM=ãµÀrü-Ï¥LÅqÚqI<ëÉõæ¶57ª±èƒÜ`‚ê'](§mˇ{J½+]ˆë¢ð'$ÈÒ[ )îç“
+¿yєͶcųF,aYtÔÕ!‘v}ÊkGÑì!ë¾GL[›0ÍLUm{Çò~ªÉ²Û”¸"Â@7Aš‰²Æ†ð¿($ÍGßDÒòOóè|·C 9£æGµwiçxØ Q »ŸçÚõ±Þ !«6qC¸.óÝ)ôâ8 ÛF)è ƒ3Mºñ°ƒ@Di¯~¶1xo©?5ïƒ2¼@¡0öy#Èd”¯kcëBHáÐAj ²\qÀ¡´t•˜UäVÅž¤€Š# ñ‚ͦ|Ì¢&ûòIáñ˜ìÍ*A`ƒÝá÷)Pb?l5blÈ"!ÛW( zÎFŽêÁ>P(Æ°g»“ÍÆA„$Åõu°ujAD¦‚Áa”ƒqkX…x€ñÈi##Iöá;ò×¢X&¬$Ãgêäæ^Ã0Ê[.ë¤CY¦l“Ïî²Ì¼r$s Q›×ˆ9Žˆ-åì©Y ŒU)Á.‚+ÍhœD.½H‡¢ME¶©É¢›(æÎFoXÔõµ7Ó¶ÊÐH”ó‘tÆZºQDáÅoÐ ž†k+0EÐ0ÄÕŽ
+æÂÉ×þ +ôä‘˧W(¢!&ux¯
+n ò+cèø8ëÚ½ã¸ßU¼ž \—Ç˽æÓ‡ A‹ý(,_ÆH3y/ÏÑ°±ÌìZð’Ê¥Óo3BXž'ŸÖw54¾‹Yùt6Ã{}ˆøÄ…ðn¶z’”ˆÒ¢ô¶!•Çñ¢`çê
+¸z×ÃÝ׸)9­¯[ÑKÀ°K0|(mj‘À$£dð¹=}á—äØ¥(áÎønv}Ä ’ ^`ÌÃüÒ<D+1PŒ*?T$‹wñ ¹
+‹’-Ÿ‚Kã
+ìàæ´ÑÛÇ*OöŠ Â-Û–ôV$ÓÝüe‹Qñï"]e ¾¯,­0ÿ¬ÃKš(æªÁOØ‚%ÏÓ¦¾ÂsZ
+Âå÷˘-T¢HÆ™ê(™ƒ÷Ò¼‘¯êHó¸ 7ÜףŽ|RøÌ-€…Ab¿ÌëU"(!ð1M¥›0|·˜Õø(!ìuÓ¾~ ¥žW8†OŸyÓßħ±yË©]½¥ä¸z¼ÛÎ}óŠ_×ÅÄuf™Wá/í‹ ‚dz±y\Š¬Dã_ ß …F ™_™Eä™Mrà%,½c¡Éx: ÏhÑ܈kݼbȓþ¼¶S±d?´‰;JøœÌ BäKI4»Ôa!è²E,‡ªnaÖæ½å$…a‚°“ͺZN’dûiéÔ2üûº ý‡ò_SÀ‚Êí;8õ­'‘£}h=m‚ŽïKc®¹ÓvHÜKXtò•2E;ĆȒ|‰"å<J³Ã|±ædb,[ƒ¶¼ƒÏŸÂSÝ)T‰'óíÑ OÐöm
+.ƒ
+K1u q!Ž,æE¸ë1ÍÊM°iÛš!–%ÁçÚ†½猬ð,œ‹—°ÑüŒ ß_m-/Ÿ2 Öü­…‘a’~¸ê‘«P =ŽŠ|+R N£#M€gïw%Ê'U¥®K‰o¨ý$ýí;¼¸‹½‚Ùá¾¾¥>3ûŠ€%®sŽ2‡M.Œ“Š¸ ¥¦ìVÏIƒ,„÷Ūxv(Š,U)`„\Uõ}‘m‹cýyœTÐN°^|Vfê‚Rò#Ë¢ó"¢Š/„,4xâÖIK
+†[ÙÎJh[‘?âe¡‹‰¿IGÀ®„3ŠaNGeó<iÀÖÅÓ!åP×­-µé%›G˜è`\BD|ùM}ÅÂVG…îÙªÔåßGõF۪ /‘øü¦„]‘-¢Ã´æëãÏßþøHŸÿúÏÇoÿû ³!pyØz£ãX$:.7ˆEßüõ¦ˆ&±*¨vc[½„­Ë:—Ý´Ù \ ƒ#_ZIVsy¦:¥
+¬æ¾l–UYÃkøb lJ%©»»göÏðÆ!zÉž“GºD¦eœœ•ýp
+V;­žº
+ß!çæÉ.ßòP–ÜÇm
+ïi— ³•àþ¸>ôUƒ²Œ¼Ù¸¤µiüN
+¼ /Z
+êÄ“’2^Šš²üÈTcI¡íÿŒ—Kz\¹ƒW=Ô
+êÓû1öf=uö?½?$ª:®ÃÓö(‰I$<b9RAøH—˜W
+¾!)(|%Yä/¡¾÷%@4Ä–O$Ë$­¤­™{™‰R9kš)à {ì$ÒG\¾ztb“AÜâÊP2Gÿ; OC·$*ü`®ÛQ:î.+‹3/vߢ”WÕõyŽ’z’5:DǨWÉsêû;jB.´¿CqpxMþ¯gW‰‡,~~ôôš l,† °úÑx¾Í0äÀDž’S_ö¨íTüqR,s'
+Ù­Ï;›¯‚ß©éÚ̸'
+¤àžÕØÕùr@^ÇàB Äj`Òµè¨Æç5q
+“ƒf¶‚7WQ¤ÐcCXdŸÊª:;*ëðÆìÆt÷xE±Xõî2»LMç(ãþQQLšZo‹£µÅòs»¯ÄÈß²“.˜:*T#†˜þe–ùƒGˆ|¼yË\ÆôRûsÔ×Ú°â Á 'ÐÒ´“Þ
+  —$B¬ê6ê b'½5ÜûÎÛØx·¹ŒŸ÷¨Ëã˜jDp†ýár¡NZ-ûŒ‘ò÷éã°®Y™´ìü•æÆ»(O4r(·[ä+Æ!ùõ0_.¾»‘ÉÛŽ6”4•(o.PYì»bH(õææï 
+>̈ƒbŠÍɾw}þäNûêà Ί·}ŠŽPJÇï«
+t.ª_'¿âª£ÂìÇ‚ŒÅBÑÏwXÌgÙ7„äØ`9;öQš?Öx¾ý
+ó[1ocØ
+d±²¦ñ+l`!*OŠü 6Ù9øJ†ü0]Z;ï ¿•g’
+s"°%ºMqÖPgg%küÕŒ2ˆ;”{ýV}×q-p†#)¢!Ÿµf'¨Ùp°æÙp\ÜÀ§† 0ì~¿ÅÆŠx± fœuCp5!ghφä–HQxÛ‘m®Ùz8j2GÝŽ" 9àGYŸ‰š—Ê{ж“nß2o Ø•ñµ•>~y  ƒÀÓâïnüÈEíiÜxä–Ã,;=ÆÐÙ3¬ t—êN€5Z|™h'Àª
+¯VÌèËË¥óžJ)¨tž3Ý”»Yíôø%S#ñÚ¯Ž+~á¡I+¦@˜zÝ‚´uU ûJ¯sôè§a-òOø•lž/))9³ ƒ Õ(î´
+CÂ×î~: ¬šIkøs³ÝÑ€ŸT:ñ•%Â6ü¼aBIÚZÃú.j!‡¸Jƒð‘ŽTžJ¦˜#ÌŒ¬§Á$BÒÑ!y9£eê²úûe‰%—D‘zföÎ'¨t3Ö$ÑÖÑëiªºÊíÔq|ÙˆMÜ$BÜÚ¤ñ)ód˜>ŽÏ\î%têo¢¢Ó*6(¾I×ç9Š}Ógæë=¢WÖ€Ä9õÝv°§3Ž÷æé}8C¸JLŵhäk2ríè O7_Gã™o†¹˜o”
+½Âž¦˜FËF
+ØÊàû!ú2øœ~ü»–YFKÙñ›yÚPcˆBIµÛÊë€"r9aõ,Ùžl›» )¹¯P¨_aíT´š@iPq„º5c[×WœVÛ;a)ОÊ=D¢‡jDl«(ºšA±7„¸„ÂGf#;;*²÷†Z“ÿñ”Õ ["´ÉF„¢¬“´LÂvƒÈÆz¸AúZqŒ*Uç/ Ëx|þº¶UQ-Zã×_iqy¹G¯uźÖö¨Ù¥éꃾ¡¨wø…;»ŸHM)"Á¦…4,j”L3 ±˜´Ùú!ƒ$Æs‹³•PìÀ‹mI˜UË9à@þøͱîtPÓ9ê¿ù÷¿nƒ_ÇØHòx‚#¿\P”²£ë¬AÄtß ¿ÆP7ƪ2›}–¨²-DÓPq‡Þ § Á&éusò—ª€¿:¦%óòJïF/>Ò‡þ’…¯œ_NË=5X»ok=×Õ"GÍšÿæhbï6Î<¿½Êz¤¥X‰sQ Nf€c1u{ȾCxákÙ 2—Ì9Þƒ°ødVŽù9Dõ©4,žùY”
+ògëÜTÎB'~òèw9îG 7Ep¸uÁ˜¶dÉ:ú‹ÊÕ`˜Ì˱mmq0ú.N qÝ=ß
+ñmíÂÐåsÔ[q:ÄîÉߥiG}­0¨Ï 'Ø\ÝF½A줷†_¿s›ëeœñ»>ê:ÅUì™Mµ?|*`n{Öír»ƒüݨï8ÿ’Vþ»H÷ë'ÛHžÚ: ßÈWŒ·ã. ÏŸÜiKÆ ¸X1ê¨W9ßú³AE²N÷‹Å<ÂOÄùà¤Ê\çi g¢0ýhfŽ°ˆüR¾Ç‚ÌŽ®Ó·4<od †xK¸«wí£@h}Žµu×>²7¸!ë´„ ü5*åÐ6„_!º^~¼vûAÇGßGMèÓà÷ÿ/—ì:r†®À{ð
+ÞÑÿ3v蠟Éþ§}!QîóªX‰'ɉ«$<çÑ°û9ƒÛÂ{¼ñðM±Ò»žºB£‡WÍÈF soëþ9ñE¨`›AÃcxseä¦ÜQ15Aµf)Ûƒ†JË•e1õ_Ä£!®™÷âgÙ.ñÇ @Ñ3·¢ßÙXxÁ,s +ØZ”ú^)¥¢sÆ.¾AÀU¢Û–§_ç`Ý·tò‹v_U.Ýô’ċωÁ@˜)…ôs+3}ÜCK{T§å ‚ÙÄ@õ‘{|fÅ,’„Hmýs€ô!s¿b½Â­ðl _H¢Wé—Øüö*R…˜¤ŽþO@élnv2S…ˆò°}Ž¬cEW³ÕÍ×3¿3 ÂUƒÂ^µVñ
+ªúžˆq9Mgöd»øÆë‰>wPÐ>†‚¢kÞzqÁèÖ¸5VrÄ×1¨2Õ zy¨ò`
+£½uñÛ0²C$Ø~*p×x±†
+af²Hóƒ2i+‡¡IéqoÞÐ8,`¨g™\Ï©ÈW …^TWâ@®¢J]¿c<Š‘[ÂN’c£8}Ð]ä¯Gä òQÒææŠ<€iE”rL?”IùVíj«ñ@z‰#´!x¾ È6òMOs~âM#©9{äY,)T'Bäîk|áQI4È]ãEAA˜â”}'³–[N¾Æ“HgE ƒ¸û¤ñ7×s¦OšÆþ"Úüähüãi< fö žpN¿Ìií· Zyãħ@8{÷ÏA½Ð<BUïÇh\E„”ZŸDþòË_ËŠ0„9=Nàä‹ü_Ô{iüç? ;9“Òëu¤ Ý ×Eù{ ý!-Ù•¥EvTk0³¨ÙèÒ¤>8aîªBõñ
+3±3ñàÃNÆn^Ò¤Ž mh»2D
+O HÚç–Má¯@ gø¶iˆÎ4pg>9æ ²¾Äà‘F_ÊàŸÃLÁÄÐ×’íÆ°Pw&ÛH® b&5Ø»q²šÇ`;k)¸&YضZ梈ÏY‰òîoU…|(÷tZ‚š}LÔÊf¾Jw+åËéûå‰(š)g®G‘æ*žñ¿ûù£sNÌɲ®4<Ìn%Æ—¦S›±fjóÃ'±ç霷~{w¹ðÆÓ…^mª_+ì@Þ:õïOFFÓÎt!›¸l‡$øvÞo¤«£GÜ cÐ÷½t|m&šÚW¹S†ƒ6Bët¢y1Y‘¢<JîM¶é»HgÖçÿÀâàô½jýèTÕ5º~ãÞzÇ·@ DuÆYg Õ?‡7È÷–Ž¹Ëç6¼Š ª
+švªÑ”:ÉtöÉʲt§WªÑ4Ó>èÂTØ¢@‡,È@Ïö)FV^k²[ ;[Ù¦í5áÌžT#ÐtìÙ·lüy@wê•Õ€Da­˜gWüÞ ‚€cV>Ú_(•)…DƒÍJéõ )…êmC‰Á/rŽµ4ÔŒÃÅ­·nÓ…W‡ÿ4¢%«?…;(Œû•"ÄNª4∠Ôò„Ù ¼gRµsH¡ÊF²îÝÕÐZR™ôq: _Âôr
+
+mO‚J‹l}.H‘«Ô§çð_ÞøIP«ÝÎDÑ+Äóp‚bdœB­†!L"ô4á²ËÛ"çiÕ¨N;ï„'Ðu–ðpNyá=¼XµÅ×C0<‚º!DÂJÞ`Yǹm©•ŠÅj¯Â©# ˆ.å³W±žåŒ4qŸ ¡æáäÙž&Ú’äO„gYÕ;¾W‰'¢¸ÞׇzS¹_h©e¬+ù̼ÓõÆKs!JgL­á—‡/é.XV-|>5~+àZÈ44eböê»´Á[$—vú Z0R2|£æ§s¦Â§öåìvx†Ä6¬f4ŽJ³ÙÄñئ¨ÌBòÜŸ×*nTÊñW˜HöÏ\Áõ:1+¾q›"/§}ê2y, j) Sžû²qèÿ 0
+H‰Œ—A’\¹ DO ;ô\A
+_/Nçg˜Oþçã¢uðd_ºW¸Ð9[|CèRXŸÝMXiâd^ˆÛâwŒ‘·4*¹óLXZÆ.Ýà†¥[v‹µ·ÕúÿUO‚”INR’ÉôðâRÌo,,xí&HˆÎaz‚vÕZã3©í Ò[ÏŽ6*Ù¯H¾8—'#b™5ˆ7)3 ¯¤yDOv(2Q
+–_L¤ 9¨ì>#°
+zè¢âRZVyÐ2¢“O
+ºy WCÚ,ýt"#7–ü;¸.—âiåÝþ‰„zÓÝ»òž²‰PI\ñ…Ó°ÿEjÁr¢m帕)ôFð¡jCÑDÕŽ•¿A6ɪr›JÖN˜Õ½Axj³Ë Rô…0Âú‚ :ÑŸCP4KÝA™‘äE ëxÚºƒÒd©¸¼ŒWĶ·­,0 ®u:cCã5*oßÞ¤HD(Á¶Ð@ú1iÂn³ÝOÂñ ìQŽeÐ!R²ˆq
+CgÍè/LVKv›ž “„þRÇŽ ¬©¼9ÇÑaD®eÏv4M :ÏݬOEžÆˆíꢦàyméò£/·‚]CàK¯«G;¥¯=u™òôôòwÌ”ŸR"”ÿ¬ôŒ¤¡1‘®y›ü6¥Zº œéSDÎV#†n†`èÙB=*1fCš+|ArfzµØ²C!Õ4ì-úîÈUv; Ø…Ú1JèÇrïß‹‚È-ü&%Wþ˜ÅŽ«û,|@QFïeùú@ˆ‚…  !ƒh\E0<X¡ZÉøùã :—¼¦~òKäó›˜GûýùNþúÛbÛ+·¦}—kÿšƒÀh$yµóÄŸ‘¿Ök‚&„¿ÃlñÝaÙnP­ç|Z†
+,`WeF}<ËðqÏr> ›9_hq>ç ¹öüÆÑ|Ó‚,Tعý¦×”ÍÖÒÓøˆgÇ$„• Òa¯Ûrsð¿¿Óž¿>w!„¡àsr3í¥*ÿׄÇM{AÜŽ™[÷
+~ÇæcRêN0Êò« ’Òܸ“\kŽ²ž.;°¶g‰ŠQm¿êšöl4›VoÕƒu
+­PÿÔœs"³-ðøÜxÛÕX––m öúìáH©¦gÑë´$@èV˜Á—¶….íI*š Y2³Éí¥…¬dd™&ïëÚ€¨' Up÷K¾™Us{kŽb–2P ‘8,"øÄž¬òfOdsr(—hïÎÁ·îø…w·i‚¸GÎy÷*@ƒ¯(¬Ê99¹ ä ÌçÏ=ñ–âäK©v¤¨B7ÆYØ~Ôý2vÄð0ý‹½yücId.Èž³§Ü¤â0‡™½±uË°Îy “Qªz®Ô ²CÝ*þ<çÀ›ûmü{¾êÀã{vÝpOñ?uÕÚl›½ðì#·¹øÜ
+ÖÒÖ¾Yä¬4ðÎ| »Qþ„¶—ª—þ{J>ÃL€íò;ŠHQœÄ¬s(|º1Ówd,<ŸC©~ĉg3¶.€÷(áÛs°Ki…Ðß‹i\ £¿l6² d‰X=­à~7Ž¿±–´›?<As ŒÉøáðÝñ·`s*£`Lª”©ççÀ%48M>—…ûËdŠd¹®É¥d÷srãa Lãp-T KŸ†]Æ‚[.u!ê~R•žég=i.¼l˽ 8$ðÓw=yï`Zý—ñrI¯ãÖð
+¼­@Ið9öÔ»ÈTÞÿ4‘`Û:Í–•ÉMŽpÙ$P¨wösH( ÚLÆåí ?B¬9ø¾^õ5ÿ¤ÀÂ{¹‘RÝòótu¬1ݵ…ù[oÁ¼c§¹\VS
+ öI{nàN»†y €|>ä‡"Ý…LÈ%ôãtØaHk,<¢çû‡ÿE>hñ“h 5<›‘¯‡ã$åìHÖ2k`¾Û“i‰zXP˜ìÇ¢YÒxoà5»Xµ$N!Ýnlñ$Ò$ªáJr…5Èj­/ ¡°.æX8ƒ{;†„laÇÇÎâÁ‹wïC ±Kþë’'{lLÂþÒ×;õGr2ÆW[DXW6c‚Æñq®_Æe§b!CL›>ͽã¯Ù-Ç¡¨wš÷>½%Q2¦íÇnQ‡¢‚ÊPe§*NÄNC‰Ó@S‚7ÌŒ_Ú*éBtŽUÒÄã!…fÀ‹8›òçSQ`sÕì³¹ÄSÄJvpV­Ø¡ècAà˜Zx°¤CÑì3@*Hcz YÒr’óçâq•0ž=Ð[çç LjIÂ=¬öÔwÊ_È݆¯ÐO0IëCõ*ü¯s8挀 Iñ¡óPh(øpM>)´20€Î¢®ÛDõ9ŒîÃbz™Ô‰wòOÁÀ>$”éå\ö—PÒLnW¾mMÀ| $"š¥àuGà¿üóÒæÅ`¯YlWÚûË:è¡ðJëp;®ÌÈYS†×û)¡õ];}‘êa‚Ϊ—”ÙÇòçVˆ9”GFjæ!gØ.þÑfÞä~JsÌ%),-:‹…Æt‚Ģљã@ô!nùtN‚°ñÔp¯?ª1rHfšv|âÀ)òGßdÖÅà7âîúT¡É¼?·ÚÒ¥Õ@ fûHq,‹ ÆýÝh+S%ãç’¨!Lk‹m©ûK·¢ÄÕËa•uK2æøöú±œ)gx˜o(î
+p0x
+ê2ÈGeÓ‹|è*A$2ßÁ‰žJ|¼´w­Ñhçs…‰ÔO/€y¤I|Ü Lö #™</°cUî…¯_ä²r÷" Ä~Ãï
+ÆXòÐœ×gV'Û¶ïs+ú$°ßQáéR…U žXv``€¬
+Aÿ’ÒÊoj
+#6Ñòã9]žz@ìñŠ…ƒq~³Jø…ù!!F•0M3,Òÿµ/EˆDY6 ðhÆÇJ¤íƱ þeä¨X×}*êÙ †M¬`—Çgíðò[‰¿œTƒÕÁ}E;ŸÃºd/ëB÷yNêÀ¡dÜæëšç'\Ì—Ë CKÕÝÚ‚aX€=ñ[EÉoÆ1zÁßAò¯+Td0jœ¼8Ï¢1€5AÿZ¤›c8’¨€×½(!µ@”ÁûÄš<È·ä W˜¥s“ùÕMévÆ3ÑlT[ö4”îˆ|J|—ÄðOßå‰æ†Ùƒ§
+ÿçÕD^&Ç…!Å:«(¿+Y
+z/ J~ ‚–Â÷Š½¨?ð®v<%àLXd‰eõS0Øk`zš;€Ÿ†P:o‹Õ‚øøZ.­è0ÔJ™×F“f9%D–ÓAgo ¯åïØ©â%Á0rð¡i•à„‰¥ e\¸àS@ži²;³°+Ê€·¹4Þ…Áð“ù¨"É•±àæO9cYkîþ1%\ÆB“¼ÇøÊKà”r(ñ&ãiGʹÛùë9 <.™ú~xŽL":t=î›P“tÖ™þaRÄ•Kp¥°ÁˆÙkàœ {ÂXÏ‚õÈV«‹/n ¸R€=uÍDt+Ùb!!üHíç0¬>0‚Èm w…†kk›š^p‘Ö<q˜=›ŸUâÖ“nòOÔ?ƒúæ|Ûr›±d
+).2üpQ{;óöÐX¸ŽlÕþÒ×ä¾¼ºU­,lšq¾¶>Wµ‘KÅ5€$‡ë#™T—ä;1M¼xÙ§"â§l.¦m°b)¡Ü<ä8G¯;u¹ZdóØ0—wiô”°KX»iøò`°x$dØV—
+À#-Pá=_åªBó­Õ¥?±Ûð^±î¼!È×BYç°€»£‡{,ˆ)`oU0S¸…Ÿâ8”ø—b‹¨]%= 1x«Ö7Í }œ<—µ–å•• ü¾²…ê3ÝŸëu/b“ ´J ü±¯Ið1òNÖ~µtX÷þ2Ú`®t*š%ˆ$ S€Ù%HGB'0!c•” „!¬ûSŸHlOzB; ÇÄ`‹cÍ뀪ÿBÞ¼ÂdH~ä´]ÂB?¢›k
+5/az±r LÄÛ d<Ý8Cñ`ìEü'`g€Hø|bMë)kü>‘Í´øHX_÷¢:‘žÂÔÝwdf(¬Æ˜L‰0+™€°V !á¤áy¹7ŠÈ±†Žƒì…‘8Ù¹)3BÎ  @Û’Ãǃð7ÙD/1L=rä¦ö¥böˆw"ö)6ÚÒÎÇàkánYÔkïÁOו »kBHB<üÙÐB•¤‘t×rÜj^düã¡.­ÿ3^æhrÜ0¾Êœ@÷%¶CÝBéèþ©ÿG‚-wÊn%–G²àmUé6n5Ä•6Iç4ÐkW+‘ ™ûá Gþ†‡<ËW`±iÆëå AB;EY9W8ŒÝΑò
+Üe¡(£}8›n-Æ¡W\Þ») „¨Mî9—y;ßr]÷M×õszãmú¥Ã^ÉÛ¤~}‚+lpA&J\ÂHÙ9ï·S¤OG‹ù¦L–K¦„qòú¨”h-H©î:í/×&wå´lMŠh7jÛ —ñjRЉ¨Êh÷ EÖ}ã¡›-Фn“÷È?> +$¹™þ9¬3æPßÎ×ðªA€•=X%Ø šMô鶇°>ŸÌߢ%KŠh.™fkNÁç7Å©9ê>g4%Z<aµ‡t‰g&û ×‹lÒv£ŒŠl¥#sð—!ùVô6úo·÷¼!9(êù…ngç.ÁÙá äg´§óð‘•J’E‘ Óo“ﶧGÌVé/óA-ƒ¶ª£gÛÓÅ¡»Z/9lyËWÚ ˆÕMµw÷mW¬•ì@\ª'í‘p˃ÿÚ–ÂÙì$fÇJ2þ~ગ5ÿµñ@W0˜Ä|šÃÇóÜÛƃ(%À$ss7-†ª:A¸L_>ônëñ¹]ê<¤²î=×Æw¶;Êwwº ã[‘ãI¸0‘'é$´óymƧE‚tÿ§{ÝEêi6+/1ò­<^ÑÄ•±À:pñ%MfHG›>Ö/(üŒ¾R %ÂUÓí Ž¨ˆ+±ÛM €‹­ ÛE¸(tâó/û˜ÑAà·Â‘nä"‹€ã.%EôkGµK‰M–ÈX9(VÿœñCF“#jœÂç‚S‰¶Do“ØŒòËŸ¡³‹¼¢7[û½‹b`óèsÌÇûêÕEš?¬Éø$¹ÓêCL{5b¾SôPh]®˜µë}>“
+ì‹)<vGCÃÌò™”$Ñ ¿ÙŠñOº!+ª´y®â,ïŒãpTÑ"ƒzý„Šê žHwz´½€§ItÜTÜŠËŽ®_ŠümoÄXyeøº—‹<Þ®û¨èÂ
+¸nEÆ0r×Øá·=šÀÁi æÌ…;E²Jˆê3–©?R•d
+‹z>ôúØò8›OwêIyË]‚¤˜’<’´ÀV‡w/î6P'Rw°”¡¾ øýœÄrU”£”sY¬&ás—Døò‹3Ú«p­`졦׫Ȥ
+Z;ÎÓñølŽ4Ñvp­#ËGM7‰€WËmû%¬R•¹Æº¿nºÖ {„PÁív Àgšzëf9èÄ“¦hs?Še~x©fdÁOD”ãG&­h"cÀ>¯ Ý^%YiGÑ­ui˜ðz.¢Kkþå0W„
+âÖGt¼Jøæܦa<!céêq'o%ë*VŽ.åÉ¿ôäŸCW1•Ì"Öý¯¡«„ʬ¼æÓ« =EŠÒ«TÇí%8;Y”гßc§äߣ:7]kØ-D‰í 1}ùú%ÖÛ2î<ªP¥ @ò\ui™UÇÊÞ¥iW]ZLIh
+iÈx®î¨.%¶—‘{ç\çþ5ÎÞ_å,r ?ÓªlΗ<OÏyñýSÉ¿Gõ{(Ebã¡8„‰¨¿"³|Ì‚uý1’èýtŠ–׆ÓÔ¡AÆÚE—ënEߟ|ÓÏõé»O@›$ÑÛYÅ*Íòˆc}:E3*GäÀÂÍÇK+ÏuÍ$ÊevœßÃws%ÀQ+ÂQy$6Ž¾B©ŸGðICÿÑd;ƒ‰Y¸vÔJäÑåLç1ƒ£ÈGòVÀ[7~áaò‹Þ}å·S³#&:€!ÝÚ3Q!T’­¶gUŸ9vF -ÎÛÑ+çÐœxVØ£W”&¿Ûf3ë.qÅŽéN‚é5é|TÐ黤H"Y <ªSbWu%åZ´§s¢&Ç=D=ût5ô
+½íW±]À„ÕöŸ(áÝbX$ðô&ËðÀÅé|pv¤GF@³ûòÊÞªk‹«¡0÷—ƒwàþÉš¦Ó‡E¢D4Ê÷Å|Š.ßt)úþ£ Ê_«4ÿ€Y™üu"¥o2Ððào·»ß™ i
+¥ôŠ3Ï…1<­0Îã\õ¶ëé™Êê×–¿ü$ÖZñ÷gâ·Š
+9Ë‘[ó¹é¿7YKöÒ¥ 25©îÄÈy•HD&
+9mï{-Ò—Ï2h‡|ÖŠq”TZ–+N‡^á®.ÜÅMÒ
+W[‚öc
+Å\Rä¯þ{¥HÆ´Å5åñ¨nö‚6j‘tÈxSĵÿ¿“?Iåsn$˜°&íÛƒMn `<¼¦—S•õ~:ó@T0P¿8â§&º³yÈíŒFòüVH2bFrû£çù#6ÕÖ7³”Ü%yµ{ûtà•!&EåÈz0fµNX㩆˜ŒCCÈçïT;Ê’¿Càa,eÉbÅÙÑy•9{ª!¾T'RÙ¼²£døá„k’X%€YNÜŠ»Ö$J™Ï!QU˜ Þ\Û>ê”
+ 8’è½®ïä©.xùˆ2)á¼,¯¦¥%*}…`8ìcdˆ°ÇTtl
+þ’­!$aËÑtIh’ ¡‘hʼn§áÕÄž’`h‹ÁSÈ<=}‚Ì}¨$3´z.y:µªãÖT%¾gÚIB%€X7š‡ðŸÈšÄ÷WZшØuzôKД=†ºÝ{tø/&
+*E¿Á<s¸
+2$êìŠ`I¥'¶ ¼a³è„œªÄîn5ðláÚO%£Ù÷ì6SC‡³rÆÅ܉š––D䡯û‚ž öëo\ØwMnɲ‚CÉCt£6Ëľo‚žˆÆBà
+‰M+ÄÞÆ™äÜž¾ h7ƒãiä:¹ ¹úö5R[4,Pm¸Ù,­âÅ«Ý£ü›æ@cß”Æ#c,’£¢ìÐ\Ru?Bi9Ô2ûHð›6–)»ó ‰ Yqí l-ݽF&±v£¹"°åX øÑ]hæžÇϺbåc¨Pµàß‘Á÷¨e}‡Ö§™“ ÌjèSøÚê
+ál¸ÈÎ~?P8AŽïG¥@‚-YK2E©Òú^„Àì-¯O2ÕZj7!"nÖ6=ö‘¾KÐëtѽ5®ÒµÈ²ää T`hrÏÙ|Ð'… SJ0÷Q 5+_Ÿ%y[3:¥¯ŠÓŒÓj5ÓYuèÈ,UOü7êEÿü·*È4`oÈJµæZ·AŠY
+S[k+0ÆBXxE.þØ ë"W·øƒu0”2âo‹EÏØ XAü;8îÐú1wŽ_­ñ¢ ¤¸Ckä*ÛÂt úº ‚
+
+4²!Ài}Tʑϵú©æêì°Û#W4ôˆ1$ø{mh}¥ÐršƒN[Ed†Peqfb›é-›‹…`îA|—’e²¤-OÌo ($rjq(¾ø=KšEŒ`öÄwKLÐÕìΛ “¿ÿøu
+bw
+@wÖº]*SÖÀ 0'Õ­T£Æƈö„¶BRÔ©WØn[™-Ô
+Úzß)q¤+§¨å9x8£\¡ k2Þ¢MÿcÝPèôAß±gõÒB²î uBì(ÒÏ1ÿ2^&ÉqäH=îÀÈ0kÕ’·Ð6uÿm½¸'+3*V[·u‘NàþýPçX.ü|›ÝúJF–E›3HS V®BÈ`¨8ŸbLÙã„–Tý[Ñ’ ‚ª ˆ×-(Vœ¡3mÒåÆÌTÿ-n±É½Êb9¼9 »¯ ¶y*±—ÄX<C:Ãø1+hèÄù-Š&g²gY¦Æ$YµH
+»ä_ú;ˆ?¿<F¹ps—DÏíþ9Åu$„#×­Obyj@K­I!Eü{*pæØ%üŸ6øý´yfýÃÊ3b¿8𧳠z¢,³àJ‘ÿ2­Õ\&ÔVói•×8Iü'(ú9Þª®gáër*Ú
+#Xµ("+W´/_ÉnWLöÄèJÖ‚Áÿ¾,f·Ë_­¯ 8úÈ”‰6®rI _70Sk ©™l9’R§#䥈’ù“‡ÀL ùÉT$‹$N+¡ ¥:õšØœUQ{ÂÁ¤nßœ"¶
+h€‘@߾㌠ˆèØVž»—fî‰u›F©4å…MpŒA!—ÇjŸ"Ýð/::jè
+@BñTrGL/EŸ‡¢åv'Þ€HÈ‚î s*BV% <>¼ª¼->aåÍ¥JŒ
+ðë² ­{‰RÚ¢+·ì ½~r“ô‰‰)…ìœD$ÈZëm> 4ŸŸVnmPy$*Å›’Q¤Xà (ï™÷Pe¸E›"â&K¼=ºJ$‚,2ÄcÏ"N0@ˆ·ÛCŠ '¹Ø}x"ÇäÞ}±Ør~@®ðaiÏÙ| ÔÞ”:Xªf‹ðËK%:¿°§‹U3Z^’;'Ù¶]z
+
+î²&Ò¿ 0ïEÇlòþ¹KÑã;w²«Ó…¯T¢© JÍ$¾¯~(zñb»5´}W¬©ðIÐÒvü‚POTØl!·ÉÈ8hwQf˜ŒfE¢)%ߤ¼ïmÅŸF$î.µ”Ê'+s ^ áÀ>m; Ä”b@aúlûÌð
+ÊDelpêmìcÄHЉäsWt(ˆ]aíÂÜ%üÞ”|L“a¨Á(:kã_â6wS™ñ&\P z‰‹Ÿ¹ ïÁE#¦¥¤¥Ñ§lw«»dà;ŒÞºKDù8Vzˆ]YŸâ†t®±‹b%¾tõ"§}Ng è²N³ðÆ“ ^´8µ›%…\h;x~ê½ète&›ÁséNá¢eH)™í‰âkäÑlÕ¤ˆ¬ŸhlzQÛ¨ô8³“ÖIa‡ìÒ÷9Ð!ì\I`éL™x7YTÚÀï¨8à PÕæ …ƒt Ç­s|^¦É®Æ¤»*°8‘Ò³‹RŠ$ !}(ƒþ&1Þäî€(Ó´Ãi’q&dN‘q.gY?”À›Lí"£mÍ–W‡Þ•Ug?=nŠ@¬RòlÓÞÍå ökkVzG‘ ؈öpœ9Lqš¹*‰\1ôöØžæ‡äG`ò4=rÜ )n‡*ÒGh F#Ƴuº ódÁP1 £ä%çsÀ âÒø¯êÍB…€… #ÞùJè. ö$ÍÖÏþÙ âÙ°Û0m¼¯.!Ý”ôcâ°/Ã{ ûƒ¡ £qNÄO±{FÈ ŽÙ»W$ÔÑÁ‰"bß¹ìøC’ ÖvÍ$0E¹ì\ö1‹oé@ÃTÒ©Ùâ´ðÂvÆõÈÙSKÚ\Ì×ÈLF£ÄôfÊ&­
+l÷E(šÝENŽ-—fû‚ÕÁ,`nÒ0Ö'+ÕQ%ýÑ×. Ð`'MS2à@³pÛÄeÈFt DS²<°Ö08á¦Ü”@žLß;=É^ŠYè¿6òÞ¨,%CL®™IzDz‹Ã|²£°^ÐܶÙAÝ$OäÝëXwÌp+ÁŠºâ ¾ 'Y¥Ÿê12رAÉèöû¾”8$è䥊VIª˜[Æyf¾V
+â›/–W¯”;ܯb!ÙÍ„œÍ/}á‰}ìÅšÃ-£Lyª¦Sèa-‘Tl`ÊÀà¼)©k{£œ‡%Y®ÜZå_ò¡èaEÂ:—DÇÍ(A˜rDÕm‡ ÈU³QfŽ¢¥Zsy~Ï †½\ÂGå\êWq²,¦
+ÈTyébâËÛ ÈïÎu\w¦›a¾”nÀ.0Í»sèƒ8ƒû˜ÿÒþ„çÃ$êºXÊž¤œÁÞ±3OYU
+A
+à|Õ
+d¤z¹sÃ(¦’bôCÑçMQ†…aHñ⻄9‘'Äb’ž‰RË~;‰}”±ö8Á5±[‰R/Á \âï=|%çbA³á­ 3Ÿ—% dð?-®ÞÍê¯#8‚ƒ­²”ÎP/$fà€¹YC¬þ<ÙĈƒ™x8” 4­“Ìÿ*YÖWØjdI>…Y¦C9D蔓â•V…²wXÞiˆ«>ÈŸÇ‚ úƒVÒFÅ[ /Ê’WÌwÝã~)9Ã/áv™ôÕÝ&8†y”M®AO{Ûˆ™ìi9Ãü‚bhJ
+Ü“ªùlm`˜õèÂ/Ó>˜y°t½‚õÜð~ HkrFQ/µËÀ9AÙµ®4D ™1БAO!FRÚ?…ó¨ò³—ƒõ]%+6`ɆžF ä°Rè ‰æÊöÈD°ê€ÎüR„Aà•òøˆÏC“¿YÕ°Ž­ "‡Å¥nG'¦†jc§ŸâÔ¤/爮YRg,Ù{„븂›c1ijŒÅy
+q›øy÷Þ8'£Wp¸B…+èÐßèñun²€É¡yNPR…¥ë¾b˜M+`9q­¹SÍÛ‚ÐË;ÛZÏ)Ô7•Pó <†•~ËÚÁfÁ3nar!üb<ëãYî}ªLiØô”WqŸŠ‰ÅšYQ'm£€¸@1°åÅõ (q­ ª»|²Rj O6´ªéˆ¾Øò‡#šÉ(š›6,o÷/ž!Á\w~» ~9øÌzš6mÜ‘;þr3³2„í0ìöv/ƒ—|—rú ZŲÜi7>ûé9¼œ„àFÚØ_"ââ)‰»GõqÀ]²Í|)‹®NÑö©Ôëø<w–ój˜I›|L©Òö›.sÞÕÓFXCÛ«š·þªùØš¤Ž5lzÇ9–Ia çÜ€¶œTë¡!=W´ ­SZõ&ý;}*8WL<Vú&•Ý7 0ÇŒzãž¼¬äîmŽ ¿·j^$»qh´Lg[æ?§Ìå“·!ÚŸœõ9¹É«‹ w €Â¼ãÜ wÀ¦ÉÕ×^ΫXn¨vÏiìVï³Ü3»w&Þš@8]׬C
+£mrD܃Ÿh®?Î*wàfÓ~3ýpúÑ|ˆáä8-ÙŸ F7Èðiª Ý æR?ñ¾Oΰág­=3¢gö¨\Qô˜Ï-ŠZ¤´çÊ?–Èš®>kãò¡<jT>iv|/`5“™VÂúž‚—[\<ž“8F q´áØà’à
+|x)‹ˆèžQÛÌV^qÓðü°Ð:Mwñg‰0mÚ+í×"Ç3.šéAaPf"LÅ¢w¦iìÉa­Í
+…¸,ƒŽªªJˆéën€ôÆÿ‹¨¹Zå­Œ:åÀMo+ú„$Ü ß_;’ý*†“O£ÝŸž©–B^¾¾à¤}ÕKûT!³ùÀYã<åËvôÏnÁ¨SÂ:a,è•¡dFߦ‚CCoÆF·ßÄ+ã(ƒ¯k¶$¯ÉŠìÔ
+žÐÑŸ½Â¯+_„-|lQžgO/ŽàV#Çek]‡` E3 ê2ú5`ÊÆMnIQZ#öS
+°Š–Òª†#™«-h»‹@p>,„)ãZ¨®"¯ùâ†oóìv$Ü{‘ŽBêW H'.÷d|éi'.U†B|‡ ªs÷Ê;q©&kZR™Ü0üçà(AÃͫסøªÆ`°WLºA‰Í‹_Hª5 ƽ^ÅÏ:gÅ
+óÝðW}‚H¬9Ó[¤‡…âû§*Fà¤hÙGH‚X—ùj¶'„d§cnùDX¯¹è•(G‡ÀšŽäŽ7‘‡‘ׇ|1ùOódkÕEHÞÞ…7<¼¤'„¼´³‡çZÁ÷–[Ù=¢2iv-£õ@¢L„447?÷ 2sëAWãAf½HÒí ó;ž‹e¬Ö}€DRE¼–Y+ž
+^A1üÑ›ï€?9é"È*4f¼ø¥îÒ§#šÞ+¿fU`œf«"JŠYò¬Äök‘ÃQ÷×ýR”4¶-J—Ì›¼‹:,˜zí£U÷„„¡Ɇ\–D¼BèX©Š_1š…¢a¥qIrÕJKœ2O³‰ª–Fœ;Æw–ˆE‹AiïÇT¹‰ººï  k[¢ËË’X˜y™7}‘ìT
+¬oA-R<Ñ”¤À*8>mIæ/`
+-I¦ Ÿ^^¯¢{ ^bî'–è0¼˜#…ý‘<¿a¢B<±t{„Ûs&uò¼A$Ê­†µ¥µ •«Vq
+òýç 3òžXÛ–@’JpÎkæJ´Š²©b%TkMª'º?ç┈ðǵà&òãöº_ˆnÃêõ¼³ó¯Ezcû5zìö¹Dü-t‚™ x•EC¸3ZÄ1]GÂvÃÊR¢ZÔGqD2YZ_€é¢8’š D§Œá2$²%^z|@ñJ³sšÊͶ•.Š{¢ŠKԴˆþ"ä ŠKHÉÔ8ûÙ0¾²8—<HŸ´„8¹²8"™õ9#š ãHª _pœŠõSÑÿ¥q¤E)Øèh®¢A %<9²›E³hä•<öPÃ…Ç‘®C´Æµ=ð8"¨â@t¸ÇãµÐ‰6ÔDª¢ÇãžäÆãw‘ÃãW‘׋åÝ`
+‹¬ˆëà@,îì4ÇÇ/-íà¸Fîá ª¹}/¼Ããê1|ƒÏÅfüÇkj9jÐâù˜¯8ŽDì;c4öGÄïðnfël¨GÒiXGÈ.K¢>©”Æú{¢j;¶ñ³Æ¡qGsq)ˆy-jeíž7—]Ò'‰V·'Ÿƒ§+æ\©z,N1£¾«P¯˜]†ÖˆþŽšÿÿŒ—Éq¹„-´€}9ëé…®¤ÿ×ù(è‰ÝÕÒ?111"Sh –\²±¡h0\c,jOç ]I‘çPÅÅ‹ÁldE„–âƒ×tQ;¢)C8\/D>¶bæ†ëůèzqMEPÙq;ù¼êêű™‘¥bP§kÆõ.)Ýäé)ùø/"53^”ÒQy®±En%C‹dÒdf¼ ­y•S2ÖÉLî/›u’zZDÉG(ô€"óVû†D["*ݺYÍÈï
+O{.£Ž¢.*ÉzãZ®c5PQ¢AÖ«XåÔM²i“Üïà?ÜAŽkÄÏ$}•Ù;óüOŸø\År…’þº^<¢ÒLñØB¦¦ƒ¹–CFÖÓ0y/sÖOÖ¡ŸTiG¥"¢ÂöDÍt•†B&)¤øtLÖ…Q%ÈÏ ðg”@Öf
+y™1^Žy¢º9çùÔßqyH¶OÖ°ÌÅØÝ)Ð×ñKŠ,Br Dü‚aåž2É7¸–jtïñº6 éÌm^ï"f =׋©²Ã¤lSžÁmfÍPš[ ”‹%%ïq½aœIä:…d©p ¡¼ RV‚Kœïš±+fmP£3Uüg—'O…0LçH¶†#ÐØHRZÖŒ
+ w¼¡¤Õ%qÙ¶ æLÂ(‹˜WÏpJØmÆŒ\c‰é›ƒ[³
+Áe¥“i×™8ãÈ?MVÿ×þRÀ䆳\òõYÎUÔ9™¿FÎEpZ¶é*UQŽ?lˆx5+—¹õ)l4rXÔðTŸÎ-à{¼Ú|º ‚ΰð½ZžõÃìÁÑ/¦vÙ:”µN¿ÄäÏFÙ—nX°À*™+½ù—áï$’5ÏñáQ8é†apG>Ÿú^Â>†ÒÁPÓ¾t)0Ð4ÈIà6ê±/]~?ç66÷Û8ãw”3Æð/l•,ÕßÜ]¨d5ô(O÷öéçÙ:ôh¢a Bu˜ûˇ3!e.ôh\. Ÿ0.Ÿ»>_¹“F$\¦.F_7kR@-ŠVÌ“ VPs¤ú'òÜ/%q@ö†|¼w©ýµa0$nEY¿‘‰H°ê^ž"h ï[ãf Ç·|K¼eŠì]“¨>FV^m>ÅŒŒ½s¹tÜóÃã
+;s‡@„q 2õзÍu怋7aÃàÌþ´RÔ)ö,Uh£ymù|¥ÁkÞþ4±3¨Ž4%œÐåU™Fqí×Æ`™Ém²Å-žIÏ9—˜ $Â'Š¹-˜ÍªÒÚD¼Â..Oi Þé0â†'¥›ÌÎaï°‰4<¦h·áAŸk¼A¶XdÛ×ÄB-ŠdEyà ¥2/?³; ¬H˜”¨¡hŸJø
+Ü<m±Y€Zy­<vš¦•ó.+¢ŠTÒ 9÷Ä$4~CÊà«i4Xt‹ ÄüSH€vNÆ®3V-qÏ}N4V–)ŧnýµé’¹pƃÌMí­;¡Àæc{šÌ¹›VÓ:¦=v‹ Žjé³nH§¼}hŸ™sKr:YÒ¶çc¼+€dqÉ°O±l¨L‘ƒœC’ŒÝ£ß$,L6}D¨´a̵k8n˜°h&¬_¬«íŒ?‡Ì;ÿêÖR \OWÁV0fuY&hœGgí<e3È‚©Úþ᫘B(Ûˆ_ys
+IËkúqÆ û€¦éÕ=FÃ>ËÔž—l–…kát1 $Ü !ä6ºŸ%{TR›¹L½?¥ªwY­0¢4øC¦’ï¿-«Œ¼1ÆUc`¿aĞߣxÉ%b%[5ÙÉŸ?|~ZTöýpŠü"ˆWH°kEôÿù
+hù4_ª›T ;عå¡ÙÈÙ¬O5>OàÈ“ø6üsÚ»èµh¾jôo„©dÙKëõéUí]£H^üâ
+˜zD¶z€üÙ©ÿo^‚†d¬5}K_ÖPb/»×ô.×Å_?æºøÞh§¹Dr"ð!&y¢+h$#–ÑÉ}ïJ±»IÊlOÑ‚‹SÛŠ±izócm`Û¸™ö©e}-ÌYû|J`‰˜¦9Ço÷òøµüκTÇ4ÊSþQ
+@v1†a|?²^V Œm’¤×½&nI¸ÚÞ¤ðŠÑä¨ê9cëì ½#$1?Ó¹oTh™¶²mµ2ÔuKÂmÄx9¡,ÈEŠ*½2‡k\WøŠIôî+mßê’Ó¾6[‘:wÎîÆ= ¸?*Í\´3¤B$W_™HÆg¥“ϬñŠR_ûSŠP(%½µ«#S0=F(-» ýÁâ ·'Ÿ^B‘ 3N…›Ûh“dàù;d\3 +/
+–æÃÇ øj£`@?7FI:ì„Ý'Hʱ˜e?ý@[‘d»2A–±B@b7ˆdgò/ý·¬Â`À
+ ß½g>– Š{üM ØÈСp¦`ŸÃ^µHïÉ5O½ú{Ï¥Va!ÆH2áÓ³”65£D²#þÇx$׃@ôF.wÈ-²Mî¿ÍCbœ¤¬©dåú.F3‚î¦ûjM÷Ì–Mþ]š>æ¬ç mY‚ý!vÌ”Xë§$ýJ„à¼úA6E\T qڱǸ3‘dˆÕ1|l28zó#Õ3)Æ$²^Joí ]fét|)"2
+®ŠÂAØ¿?äµ–M,†Ê`€I½dYFçåKžjÉh¸S¢AkÀ?° ½”0~3ìT;íTǶÿ.á 'Is\ a¨>6,Ô€¡Çó&‘ÊÉ+@÷éç@‚Gg}XQM¤ˆ½‡;¹@p7xÅä‚ÈŽë'.4½ªÏEK"lõkXV Ï,ª}33!ji±Šÿû©›Jþ‡ž„ ãcaØ’Ž¡xqÚ2}öO.œ±^ÜEL3UǸÙÿoçuÎJŸ]Åš¿82ŠöëRÆ›·ƒßÆÑmêô·×€ÜzÎ7«ýÏüa^d›…9v½öçµHÐ<ËÎbGIJ2\Ž4g熲·“¡°g~˜=‹þAô:eAw¥¬–yá1ZØgiJD½g
+ÄÏè9g!Ә˹ºÝI”á.«.7žíWeŠHAF(×Û1õ4+QƒùM_,Ð]Ò?íP¿pîýK€
+H‰Œ—M’7…OÐw¨µ#œA ,š¥¶s‡½qk9÷Ÿ$³êbÛ¥ÔÕY/ ?W©Å†ŠJ­ooµT¹J±(&V½>~m¥\½«–â­H³Çû ÚÅ:gµ:ÆH»¢µ¦¼"æ_kÄ墣órµ 2ú°ZKáÑ‚ôˆn½yHkßß&h>:¼ö’nÞÝÌêØ爷ÆQRÛX&uÔ>z/±½Á ¨|¤È4U/W+b:F´ó9õ²æ£¦Šž½©—š–P¥·¯nU/)Vzqí½ž£S¯Æ%x·Ž1>@~ÊÕ6õ
+1Që•ÔÈãûÛŸoåñŸ„òºWó̈ŽûñŒIÇc <‚ ÓñrêÅ}ßì
+2K
+FH¬s˜ŽÐŸ¤Ü^„8ƒj{]ØuÐ ûq€LSõª’kt_N  tYV‘T¡B–ªå2fÒi}ªi{SËÈ‹sýa·)”y'xµËÎgî‚<,$¯®sÐ…9œ]iC4'2ž«.H!;6Öƒá™g÷)VN FKäúþv6•,¹â²¯>¶Ã,@ÉþM6êæ÷ Á-@DCvÆÇ\—Pȇûâ, ƒ²¡
+nPÆœ `Ò÷­¹¤ècCn!þˆð›\6»U1?@våÞ2@J‰8ŸS=I§ýàñDŒÙ­FÇÜþJ Îa-–fpˆªŒm r ƒÀråØ5ÚÀSLìr âh
+/§ 2i
+Ò¢è¨è Ôs24.ºÈ“Õ@Iõí²Qá•€5]3ò
+;ÿï Q‡”lÖŸÖuŒ kÑùu{ 0n ¡Ðv}ò‰ºcHÄÝZì%t»ó#nS='zVçnäàÂ9ø| }J³¡]FÑ/›åŸ›îÛÇVù õ Vƒséo®{÷g!€L«ÉØÏ Õy6¨ö!Š-2æw%§HšËi_îÄtô•×»’5Å6c:I}ˆ)sŸ›4[rAøQÔÚö¦ šœ>!ˆà\íòñâg!(È=vR7ßç
+[0>È‚
+ÜÏšc›Ríÿu¾G¿º2™Ñ£m•H. ÞVuöeO
+< Ê8aƒN­;‘ap/\?.^‡æ\Šæ·bhi 0z÷[IS
+~b舛I»ã—¦È×òM¿TчI ܪ@¥rY0ÐÂog«”sݧïl^|™&&PEs‚¶’t‹û*Ìœ;yäýí sìv¸ EC]rWpEèΉpŒ˜=S6ôÑ‘P:­ª^² ë>À çñ‰¯/¾Ø±
+ÏÉÑø˜Ú/¨à–Q-㌭‹A?§¬
+86솪DjËI6ƒØâQOŒ ·ñì%Pxarò%£íÒ~,„Vv[Æÿ²ä$I gÌ ˆ9ŒËR
+†i"þl…‹TvðtAÙŽJqåóœ ¶r†,ôQ¶b,ï/`––ÔÔ Ñ–+º(¹x0ÎCrè”×¥ÆßsßtÛJÀÈ´+s ȯr@÷¦‘lë¥{Á¢<¶d-‘'Ù©o
+LýÊÆ<÷’ã~N :×XõÓØ@ü“$Ïè>5 ÀYùë,Ãg„’ŒùðǬ‰uÀ¹PaÈŠóOšü'|'Ù€"çô“é¼M[g‚‰Zû&‘V>\z§¸ö{(E¢‘tæÖ®57W ד›òî½ùJðŠ5ßà; n^ñ\
+BÁtÔý‚¸OŠHw¼¿8y,zœÝ<Э}ÿf”þ’Ÿúõ_JiÁÉŠ‹#O¦ûsõ=Õ{QÛ]Iú¹®’n:ÁŽ‡Ó.‚»°°^"8ˆ»—ÃxÏX5)Í9:E] üñL.X£69ÿz|ŒJ°ÜWÓë‚…ì1º† ûid²kÑ®VÇ4››œÌ³Ó‹Ë;%rçØïÈ5”8ÓÚA„
+n„õ!^5·(h
+¦8ã|)ñ’r(nùVDIùRÔá èM%é+
+*™ŒCÁã³1ýÈXÆ…“Pa… ¥$Ù˜ÅO£¼•‰«9¬¶¨Uãnz<ߪªu¨c1¿úD݉eÂéðuÀèR0š~«ícvv|ë{Iߣ%ðÀú³ÕktZvÄÜ×Qoœéê_2fSé4¥ymàØ~®,|
+ ´Ç/µUÉíÜ[uÂë A÷|)ñ¤ë¸m ´Æ§uH2¸>tbÆV˜Dr$ˆ‰´
+šHÉ£
+%Ò4+‹À¿ 7q[øÚ‹tõiuZqx8ÑMtͨ\©‚þõ,æ”ûÏ+ý¤œ~b.)’C+ý$.žªii¬õhåJ€žÖcUR~ð…Hû‰N,3mô_wÎVþ–Ќ¹>”ü õß(@
+à¥èl,Uº 4‚µsCtþ>$ÊC[:†æ¥ÈÁ¾HŽ˜†¶¼Dn_rV·©Ô8ãRîƒ×&FE¨
+5Ǭxˆf›.QñLž5É®î@‡?É}ùúâôÌæ¥6ç^UGª¯B|! Kßվèm¦Õ¶×™¤˜
+©Sì¶/žd*שEÔ¤®¶ó“* ˜wϧDϳÔÔ؉!ÜSRO÷î¨#D×}'‘åë©ý”hXàWyÖì%(‡=@ÊOI¼ (.<·=U5tâ£8±á::Êg¢Œo/Šå?ÌNQÍHJ¼N_Ô­DÇѵ9’̼+
+ŽÅã%ýÚÁ¢ÀÜñp8FxÔáE˜ú¤d6‚x&°B€ŸTYð;"ü+ñÌ;Sü‡w$çC´ƒ)R#–ïDT駽 w]Ír-¤D
+4ë½»mA¹¦i¹X…ô“»Y!JðA’—^²Û†ª=ÅJTÃs`½¼+œF–XÒ–ÇÚx¦„]_Ý­v›Œªà
+ÔŠÉšÌжŸi¤ÜeÛ%<†=F‡·^]vËJðÒV{u¼ÀF6> À…«Ê*–qЈU‚–Æ Vê¡DG1è.Ñ#S°^ÇïP¢ÞfW“=;Ê*Ò^Þ%3„J ØG‡Å;]¯ÅïÂÿ0Ÿ]·ä=d>íï(f™fÝéܾ,n™ÇŠ üü¤kK] IYËñqºLC'x ²Õ?é&DEàéF§÷æàèYàÏó×þ©ÅüiæñHƒÊoÿp¯6*R ­â€S‰¡0k4¾¶§ïú <¥UžÐ"pÄÿà¢8y¶tȦv¯@mÄî1Œƒk¬¶á¯jY+?žd0P¬’Úm*A,(
+æôÐ?í&Öw[Z°£Cø
+–¬nþ=¢}ÊÐ(ñÙâ']§}J t}݈âÆmÀ¡Ò¹DçÓCÉ7ü‰)ÃfÊÈ­
+Ø×üÄ”‡¢‚L°ÿ­VG²ÊkÂzÍ^—_èÀJº€Æ@ž†f¬›O>æyâ²ÐA³£˜G‹œDfɽîï(B• 8äR¦`¼¥"²[x$üBÞ¬#µI±7’º¥/Ói=lÑ3– =|»‘ÑuÇhhCƒíν×ç\·°40†¨Ç×TátdVhbî8oÉûÆaŠÞÍaò¦²kpÜ¿K‰S%±’Ãxú8~G”Áý» •ïñå6ñ.¢!möôð*ù…&.SVÍ‘5!íÁ¿l×8¶˜pCú˜ü<>ŒJ.(JøY/Õeä2Sðd3œCIáG×~
+êªB‹£zvÄzaBR|=
+
+r;YèØÜ
+ñ!lc£ ½ç…tË“,¿Ÿ@$!¥#
+²ÎrCt™¸è¸aïxÓtw{Áh=a+þ
+1uöƒ×àÃac &d“I¬OuÝ–‹uÑ RN“—aggÎû;mc˜àÍS¢h¢§ºh—
+âK„dÂK݃x7$®•cø—€iCÝøtÕöÁ,ñ1‹@( P~öcãÄ¢‡­}Z2³ø6ÂObÀŒ©°ãËõ”C6c=ƒš¸WP£TE NÊ^ç:z4WBž½à[mƒª¥§ W‘Ö 9`ðoJ1Dv°×Ʋ‘az;GUZèê8!V¦aïʃædä€>É—Pž¦ƒIãÍ9Bì®HÞêê“Éèø¹§H§Ñ9AæåÂGyH PÉNVâé(µ¬¬%YÓ7ØwND‘ÚÈAgµ–â.èMÒx ÁJ!Ù\\‹UôM@)ê7îp,Ádz¥”âIãˆÁÝY6w‚ôÝ¿eG@{Š#´=-§žpM! Aº´Š‡h·Ûô#ÀP¯!,¥…Gü'/;Ÿz ‚{ ’ÉÒô期Ké%äu)½á3ä—Ä·}ΡúŠÚ„áKUТÔ_Å”½•Áäï@«õ艗 ò6s¦Js̘ßCž¬ñóÍ£ž‚¾®(ÍQ?< /,QOAŠ³R¬*I‚äW¾6SnOš2<“Õ>[-L–‹L ‚Û~„)PQZ„lÌ#¡·X¼Ë ÖÂåò:ûØS„駼ƒ‚CŽXQå\†Æê³æJK¢(w"AíEΆΎ50aE„Æ/"7[í'9[îBJB™Ùuzkvpþ¾Y5Àðrz‚CÂ4™D ¬QLC74<2ÝÇ*?à5G€êYÄâéXC–„
+¬Z°¤v]ÎÁ0ΓÀ½EÛÐ`+è !)¨u_vŠps žÂ¥†@†Q¿²Ê›²} ‚ñX‚áªíþüó2‚qˆ¬iÃ(ëú&¨ŸE¾µÇùTÑ3é¬õØõ˜IЉår»¯"äÉq @ßt5TÒClß¡Øü¡]¦q_1H§Jj’Q&-í!Ø‘MÜåÅæWði×
+V=„¬Ë Ùþ$±ª¾;x–Olݦžµ@ #jå{ÔDÜmsúöm±
++KgB¶z¹ÊÑKÝœñæ‰gFn8r‡¿4Ê®{FÜl%j©ÞÉqðuõfª]µu!äëáÍà„šBè`AK@,&Âx;&Ê:¹#³ä‘:i¬«‹Ñع–ÿ-øÏ®ð#èëÄÇ( …côòÝ€aøqïŠ"½‰à®¼ƒæêßN·x9ÎrÚFÀÛpø/Ž©É…JZŽü žA¤èoC
+5då’!<Þ®[„ö¨G0†1æ¦ÊknA/Ë…Ý™û%Ö– r‚($¾Ñj‰—/X‰bÖ…ÓÔ©YP…¤F/Ú cµÐ}+8MÏ3Ï ³ÄJl {¦‚öª[g¡ÐPVŵözòÔ¡jf=»ƒTÊûQ ÝÑÈ%Sð·U’—KMB˜¹åf«ÍN6x@¸zaÖJZ´tŽä¥È>fï›y„Q €e'ùŽ¢'l b£
+Ô‚¹¶Hßu2ŠŸÂÅ1 ¾L~Ã%vñY:Å@™µ=ú±Ùo*§)Ì}xG9À¨õÔS’½¹Š–W–«ÝâSb_."lw•;N}›Ž{„t–ÊÆ• ‹O13])ŸÑ\Ý”IpÃÖ°ìœJk‚ aûèŠ8$`·©Ò††ÜÉ`ríñ©ÿ½_Lh2»©h5Úœ´<†y’jþ‹š¸ÝÜ‚ꬫÛ\TE¢«È iÁX¡ÛY<„¯¤È÷
+ ËÚ+šöÏìý\É ¹e]Ûú‘¡)›Ù¦ÞmÛ9à'.Æ1eǧšz>ñƒ9Üpøk̆¼1•ÂÐ&Ú2C qìIò7G›ù0ceL®Ã{ÄECÛ–‡°„±x¡=»Ò¹t˜“„Qh.Œ@är¬mÊØxÎAÐ £Ã*–\ày_êËÖÅ8gSP†¬­ûj8
+a˜’}µ7†³ °­Še¨ç¯kü¯ >‡¹ÑÀx$C÷eAyiÝ¢û×y&_ì=ˆq(&EÖ)ã‘!¼™ÄÒ`ËEÕ.Mw"ùýB²®
+ !Ï$etÔbi ¥÷‘I;‡ MÖb¦"BmƒS2ÿ›r/hØqßÎÉ}Æ.G¾KÊ:F5D+·êjº`2úÚ ©vÚÎSKFq-L2­"qlñ)ö(Ñ'¹­ÝìØ€9p?Éa A:¸)J#1²
+ŒKâ÷d¥¢»Iì<½Zì=ç#L¦KSL:V];ÛƒNyuQ§0Ó‚tTFÖ²pK ˆv›W‡${Ü”GR©yl‹qV‹æ­•SŸì®¼½å«QÒ§hW3 ÍÚ™Š’Î?§ð“¬ä÷P]
+‹ïìd$
+
+Ý–döþû\¾Ç€â†”¨
+Áʨ¨ï覣QN±û«³ø"èåsç¼{»6Ž)ˆ¥;Þl×ùòx¯ì‘)Ö·ELô‹$Å0uÅAôNìh3WhrBKÉG¬b€µÍœáVHŽx ·MFñO)Âv“
+ˆöð À€ÛU¦}‰ý¨îØöFYf¾„p˜G&•!T°†øfÜUw£ ±ÔãÚÈF3éü
+ ®¬ÌX-Ef¸Å”`°îóñóŒáÓ„‚¬8ð
+ˆ"ð¹€Ñ XS;Hñ§3°­ˆsÁS0µŸE2@SÕ’„¤£œ™ãS¶Ùö’£†q‚@r3ÊÉÞÿĸ³($ÄiÇà!á«2Šß…‰£ï0BÊôO‹" <@üCU®®)íÝ1­0<uŒ§Í`…)C•R9䵺mG•ZPL‹N –)È"†g+…CÊØ Ô 7äLª#Áù£ÐÑpj§ ´ÚuÑŒµ{p_Òþpî#3sœÏiŸÚÆ2×ÿý6W©¬jÅÈšfe@ UѳLì Fº€8dFeÌ쯒Ħñ²dÇ­Ä‚HI÷©ÁH‰”ÕŽË€~û8€Ž£^WM™xãíÒÜ@§õ»}îK  %7ë
+rzÉòùƒáƒvNüé z
+]Áõý ï? D#Q‚‚gyÏœMk riµÿ ï$_ÒkUº'Èjád̃Õú›s*7DÞ™Æж#YT/ŠíÕ 9tÅNrYŽƒÛ®Mì²þß@/+öÝnÓØʘ5éí‹„‘*É/&TÊ
+xÉB(N¤a> ˆ¬¬œÄ~Ìê(~tD›땲ݵd¯ñªw3z‹Y%m ÎDUb#-®ÇF0Óãa8’b–EŠÊ…óòÅèÂ÷µ ÇsògÇ¡„Ø—Æ–6Šyg†‹mÊ„±O©Ì$L-­i
+;Þ£Yû®ß‚Zªbç@OQDÒ’»5ŒUkÊ,Õ =Ùs·áW'ÿíã
+8¯ÎÅ!€n‹|
+ /Ú_º‚ÐE^Á…cÙŸz½ ZçÁ‰³x.»=
+H¢ûì vù.µÁTºÂ±ÌÀãTa ÁèµiÕNº@üS—ŽŸÎ¹ÌÍé6—ù;>ê2Å”gRT3—Çy:övWˆ0¿¼têÛsõˆDÔÚ}Óz;»˜Ioøâ
+9JÿõSGÆøý}_<þþˆqV>™—bDzsʯ‰òçP2'ÊD…?Cn‰IÓÀº“B2•û2×2HÄËþ<öYÀÈÛÌ ƒAì’£²Q”r*9^².Y@jB¥\ŽYq¬"1Ê\ BØ-èuîOÿý+d}
+wT–Jòã7ç‚yÈNAD^Þ ÁowæÅÂâÝ@ÞAÉ/ömHšÔ±õ­ŽÁ–Îä¸å-IòR“s^ÆíVLXõ$£÷‡äø^Àã9¯mK KÅ«C3%´`8èrÆý¥×¡ÐÃñ‡Ii¤ÍÕÍÛha‹š
+DUý á“üD3ª}éOc¬‰{â¡¢FÀ F?NŽ, 퉿í(ðn+ÖŽŠ_ÂÚopØÞ0rÿnhd Âöq²ËئQâÌÂj”}õ1'¾û]P‚<”dkèVlxAÖz2»‚4z10y!¿1Íš ,3Øtäó9Œ!ó•S,ú¶ß†²ðFv¥žfþê«Ï3Ïô¢’‘c|ž¬œ<üÖ”?bn¬©OáˆÙ%ŒsJï ²|omö°úà‰c"Ï dR³z•<áÂ;è)WÑ…\ñežòí½Wˆ/FÊVÚÑãù&:Bª0œÃp¿14t®ù0^L–d™¹óW1’˜XmvÛãUøo&.´e'êפšó8Íz\ nR ~r†`‹(7‡ÎöüÒïëÕÁÐ2Ì‘f<E•CŸÄ,[ÁŒ¯¢a­Ë„FcX˜ÍELJ #ƒ'"eßÒõ[üÿˆÎS‰|ÄÌŸüSƒVÀÄ\ÝVÒö‰‘èSìf„€¸Rס>‚õ¤ü|È^(芮Ä%†yh¡–›¸`¾¿ÁàHÏ°>«€bìÔ¹á{ü:ÿg¼ÌÑ#Éu |‚¹ƒN ûbk̾E»ÒýÝùƒ
+ôGå4
+ŒµúÚàÕhKu ¡Lô°l—m )lJqj(ñ”\f»kB*kƒz%g D
+;"qH‡G†¤ˆÉ6óÙ9#ˆIqM% "dqS!ZRºïÊžV¯°É¥¶ Œ
+ÑŽùaJ-ZÞŠÄ¥lhèÓb[J0Ɉ\ÆÕß•œX[ŸâÃŒD$ûMÑõÒ_ÔñÝŠ+ b™ã,Ž}YE¼6î,Ž]b4é*„vÒ=ÜY/þ‰È‚
+b,Ü LyÉT–M/'pñp¨«µ¨ÈÕö§¾EàŸOGž¥Ø42Õê ƒr—ÿ‡‹ «¢$¨EµMÚ—¢U‚3þåÓYD¨Ñ¾ô~j¬{A ÿ­Ó®YÓˆ™b(Á¯Šàq )Åf‘E©Ä]Zݧ)¢±mõ
+%rk·|ˆ ÅÁK'óÉ£ÐçØõ¿fDøµæÏ“ll‡Äª¶ÂÀC”Æ°@^D3ùâ
+è^VI™«]£wo ;% pÔÊs”ÐÌŽÖ’8}Q€ A"]ÖIë*ðµÝâÉöŒˆš…E5‰Ÿ8{Výé ¾÷Û§‰Už%òòÅ8O4ïÐÐüðœi^“– ÍitãÔ+1haZÄ&ò4BˆÓ<€®zëìNóü64mqg$š¬ØD:lvÎRZÎ¥—Fâ= h‘¡ðP‚bîz%h9ÝŠ‚é$‰ÙH6úCщÜx(r¢D­oZmx.z›ß›XåPRô’4YÑ,¿Wó^‹¯ˆEA%“³Ù”äû'É,Yº3·\'¢¨¢¯Ë" ó|Ly]_!5ŲéSA¤ ÍîR^yö†3… kTbèºëK•f5Éå9…Vt÷¤uß‹£¸ÉŸ¡õ!{ C `-Õ=(®Ž—)Š˜6Í[Ñ-¸Xã—E¼‘bYÃÅ¡¨Êðý€Qñ•âbÒš%øè¢v`’½Ëcfw>a…6´Û÷ò¡(
+5GK …­––BJñÞ€{¥[ˆ§ž0¨[Ôb×è°m·åœ½ ›f€åêÙ“¸26Q1,9}i¾bÌèx/##tpÏ8ð{.}[Ʊw93¬wEÁÇÁÔ|xQåQd7´Õ׋_õ
+þÔt‰"X­ƒÔ|ÛÅï…gLÑ'%˜Ù¨¼:'yè¸ë”­6¬G:ÐÀºî8äQº…œÃ9ÊSÌ‚:Âߦc®„8ÝJ¸»ÆŸmÒÀ¡Uv@‹lyªK!tö
+øì4ß⟙,tœŸØØÒOT¹PÈbÇœ8»¦|2t«Dì>±¹ç¹…ØH^‚iTÔÄÖ¸YAÔjJ$K‚¡¡ðVtwÄ4C2r²[e,  ¢)é*cUÃòµHŒ
+*0Àb7Gù/`+ܬˆ¹¤Ì® _"û
+·•Ã5H‹—QEäÃM¬ëÍ™b_€ËèV¢ÀÃÙF·ŒQž²ô!K´@q/ºM\{—1]‰i´3nî%j(®!Hg0 ï§"¹ÔS3ïsó
+Éã!š4¾“9Q艹úωT²ðàYÜi_Àw½o íÉ ëÔ;ÚE€´Dm[}t‰À܈ûÉ‚‰U3G”•0‹ó~5¬øOŽ>$ðöÒ˜;á…aYÜ©×+½$,©Ä2rú3Ï„¢5ž¥ÅC`MÙî[’"äQ5Çp$ûM‰‚“î†Å§Ç|­÷}˜«m¼oºÚレ»‡¯ûõ$_êôë;£Ù
+k¶3€[F€orž÷ûADšÉ42W¯ŒÀ5l¯Â[2‚
+ôÍ–$“äØ‚<é¤
+B+¢×ü:ºñ?ºƒŠ ”žë–vA27Mh¡}ýTv•ÞKw4Y]án˜—–V7cHœ¬¬ç4N«’6YS÷ðßõt¦Yx¤,«g‰~P,xD_|Ú/Ãõ`"ùÜñ4d 2ØVš00<k©±+1mßûཕþóAd¬&G%Öq°~ÄÒb˜>VIž€óËzXí¥d€z¯eS'³öù›ýáØL׋èŸ%Â(«‚÷ä]ÎÑ€v³ÍŠ;kÙ1Ë{½s_51—¸îð1AÍñšâGnç4U…é©Ö-ÉÔehã÷³¹ ‰Ùåí4)E3Sà=Xõ!mn @RÕ¸û†+ÐôÖúo¨¨ÔТéÔ¶iük<„›}høè@f-:kù÷Á-C—ñû\šLgT4åæŽcðø¸m.÷ØX£W
+ZÝ’œU.6F'þüÏ]‘ˆÓ…·Ò¸ìï)ðfž\‡]­ëúÞ4²ø
+ÔV3L¹ÏáïD5ÊÝ óTA’Ы×òï+Òoù†‹2ê) J–•B™·©ÅgüŽÆqD†’{"‚bÏ« ( jBkÃÇ3€æ+ø|J.8ê9_aã°
+}à{/„Ž*('+8 J|Ú©ˆ…ƒ¥¦E½›K[¶Óni¶zˆGéžù¢Ù¤üý¤¡\¦?³ÙSÝmüÖéqŽ+F$¸œÄÅ
+~}g¬6Vƒ.År 8²l”¶mdžPÏܼscGJ!0ß–\nD‚Óa»ÂÝïHØüÖÄ> Q¹©}ògÎ1ôðœÁ*ëɇ˒d¹|ʸñ]ÍÌi˜,•Ã˜Ž·Ä!ÇKt££$üÓBŽ Š‹ŽHWò¦bOèˆHŸfâ]t¼ qm%$B²¢Ñqéò×j  Pæ*}z=ÑI`¬ùæ¢#’,>
+oGÇ*·Â¨¸±>Îå|EG$Ék!PŸæ¢£$adÖøøt¼DNɉQx“,…Eõ=‰‡ŽÿÐ?èX³VT†žLš#(׃ߞ墈9˜€$;¢)IºF°)¢zÐ
+ØaÝÙ¥0‘D°}Œ¬kÚ¯\å*Hù dêéÉ‹/µ£Eª½¬Ç@à­ºŒÏ'¤±zÐJœ’†Ap¡”ò
+ÝÅ~VNã0iUá'Õ¸Ž|6×ßì½_EÄ©Ey³ž´Ð€«ªh²êÉÆ »±Æê÷ß¿úøçx,=èmäkÆv»7l®±wÆ!„Vdˆ|W] M¬àϤVua6 ' ‹— À¡¬9aÔú´;÷ ppñ}‹è~Y Gœ
+†›T›df׋å기Ü|Iœe0Í 7y^u‰²L·c*1ÅõœžO¨ÃÖ‘AŸ¤xUä¤c8÷«÷³!Ö„RFŒxôk"„ü¤ŸÍOÓŠÌ Ö3Y˜=‹Ÿ‡¸$Œ'ÔKô$>l›L“ÞC6OòäL_DÿóDÚ9¬…ȇãýxÓ%¢µ…|Ç>‘'
+s*np¼Ëp>ŠÉTijɮܨ‚C­†å@~O!a˜‰UïÆFõŒ°«Î7½çFD‰U"ö®%»¹IÌ,2.p'µ+7z}~åÆKäÔ» ¯dA´Sûž$)ùŠ‰Ya!Å}œ¯šÏïŒæž`ü„‚Åtø˜觢pð{‰ ¥¢­ÒÌM &Æ%$Ñ]ëB‚­‘îÚ ÖÉL´:[ø_åª,SM&'ÍûM˜8 ÿàsôSŒcƒPŸc¤ðÙ_Ö4¶KÏŠRKÀ2nJyQ½>‹eà”Ûh‚æˆ>·ˆÕ…’õ$X3KJ|ÅzãÁ=@$æ[6†Á‚ÄÛ†þÊa:t™½>œ‹cÚèÕ-bÄxtçisI늛¨• 9UxË^5¯|Q´:a>¦|ßÏý c«Š6[!¶ÁŒqK1$ê‡ìó2ôb€NYÈPÈz"þÁ¤õ+sŸ°§áÿ1%°râ—÷rB®/Áßé$žÃÜ®z^¢ÿ3^&Éqä0=ï (8k{ÙGÑý×ý>V¨2™¶Ü GË“ð§@Üá.tO”bó<ñhÆĵ‹_çýÆË•a!zFÑO/¿*ˆ÷ø­Kó
+þ ƃ|¦pмë4šWe¡[©ÙÎ"†žBtãÆ¡)vM"_QÐ=gvΦ µ¯qßPm:¨‘ØÎêpÅËÝcßqwQ<ç~O¢H /4”·9#+²à÷Ÿ·vnìÉÌ[À·æy2Q¼PÒQÕà$lò—K^ ˜p)žßañ™fz¯KÒ¹Ïò˜(
+D–FÉî|Aqâ÷pÞe[Mf&¿Jwz´Ö¡ÁܯÂ/¨(½.[ëW†žœžE€N¢ØÈjO·\¾%´“[¥Oë_Osš·ÐKt„åàÜË×¹O^XÎÒ•ñì¾,ç,R¡á¬ßIXU¦lSR+E¢Dˆr¶ % J¤ÛÏ湬bÉÒ@£k
+PwÁÕØòa(Æ-Ó½iFßv”ÆĹ$®!4á†>î/ÝŠ²w@*ôÕÎpªìR´Cç`a,Å›ÓÞpÿRþnq.রX ¶¿º·òÌÐÝú6µ—YÄXŒZš²7«xb¨Éf±UeáKTӡ䉙ފþ;Ip&.˜‡Ã¦§"tW性û…*î0‚.‘t¥ÁÙ‚œ•”¾¢ýØ1Q3o€Ýò^c ¿^F[°ÅcJ€·Bà 4Òá9KˆK ô†;ÅŸùK&tç§sH¤
+yÐÓ¾L&!è‚ÑÙ ÛR'”œŠ_
+B)Å%¢mzCyf;$\œ»Š¬÷˜
+H‰Œ—A’\7DO ;ô¦‚ XËKßB³’ᅦOþ©Šeu8ÂaWç'A ‘Htí=G|üýMDí!îÙzºóÓøË£OÉ4]çøø¹@jçiKŠGjhL±æÄÒ‡Krx,ˆóEHälž ’üË"ƒ[uÕ|4ɘáÚÅÏç̇´èÙ½7× É·îM6„WIïæ²£ µ˜Sfýõ¾ê ”1£Fd’žÿ~“¿
+©ÆÓz­÷k{R›mê³üó$á3[4+¬þ ÍF^3l¬Ãœ ºò‘,H éÃ÷ã’«²‰
+¹÷X‘÷unèê~G>º§ÖW¾Î±¦ê¹!”ÓÆhM¢é-ªŽ|$Èu•<ÂGN3ãðó9ò˜„«.fºóh#rt²ÉÇ 2Œð%FÚ~UV G…ññýÛÄNðÞÃdz_T|¾ŒWÿ¥ÝacÌ2÷6RªcATÕ¸¾òÑzz׌bë ±“lpªéèïŽqXTxýŠÐÂ7‹©Ó¦Øl\Ó¤u^+”ªìk2ªR¼Åz5qÁ¢ ô:æ…YRÅ&ÐOº¿4z¡w™Ä» ÿ'ãÛÍxB„Q¤žvYçQrÚFæÍøP£Ó¿"+.ó42ÖÛÜI‚Û|Ö&qŒA&T€f 1hlZÝ‘Ké <mWÝ–˜¤Äî­s‚ƒ.ûœ¤&r⺠J»Á“áÑMø„a6 8S¯¤!›m²ƒéÓ+^ò*›ïÃï¬Û6w&ÿɘm|ò7³¶ú¯ž $9F´¾Ô¹ñ¨PŒs¾éŽì@±Þ‰Òæ¥ý¾‚a$:¨æ×/w÷ÁxiÅ…­«¨Ïœ)ƒVþÈkVt; øsŽƒà¹ž‡sžYq%ˆl£3!ì@®zU1)Ú»Ýäú£¥_ÕúÆCöMOLo5¦T:Ú3ü bÍ9Üø¼èß›êš8ä®è¤„hmæªÅ(–’4î?GŽ3fÿe¸óêªôEû:…'áFíEè庯€JÊÞ"FsÆÓE¡WšØŠRs}ö± ÉOàGE£ ¼¡ìŸ=ØHÍ&GÅ¢På:3„àEçâŽ\Ç$ƒ“’x±aõ)uE‘“QOzˆ¬ñÈô< ~¬ŠõjŸ˜c¾;†“èr|ŸÃ|²A£0-?…¨pQ•˜ R}‰Æ ô‹"üÜѲïظ¶E˜1Y¢6ö£ÈÃÀ˜ÀÏ£ÊUò¼®€âXb¼á9ÞäÝ9£S(@{íh¤Ú‹ µË­©߈mÈ„OCꤳí¾JF‰tò¼ç"Ë´åe}ƒ.4’ÃöèJ(wt4ÃçòÄÐïß #×çCLçÏ°/ƒNý÷rÝ—@Ošpl­gÐÖå|,òÖÝþ³A†ïϘö7ã ª“ÃÔî½à÷q
+Z ÞÙ­=#ž[ïçWúóÿ‹iÇ@
+þýs…ŠòCUÊ(§Ù¼¹Á3éLþšzàU1€RlÛ‰j¬½è”Ö
+òŠëXãÙ5.åc:ÛkB*ôn,“ô
+♬H$v:ù»jýÎÄŠ¸ù`~H9´ÅùJ©Öª5wĘªbž›¿ÉÝ¿1ú0%é¿z&'Cµót{©üiJÂDè‘L‚Ó(]}ÁåjºP‘bŒè\ÑàÈù¢ ˜Ù&—ÿC—Ì­¼eqQü£Ç½5ý*Ïdð o”ð^)“]õ/׶!ƒì£©]),É5+V»¿€^ö¦ƒl| ƒfN•µ­¾¯«”oýZKåÈR˜ý¦ “/_&¢mk…³
+¿ „oê›GçGK›ùA=*R-÷ã¢óTçÕ.…¯ õbSæy2‡¼1Ã4kÛÝ`I˃ž!wW¡æîî}¬BQáØO;Š°$95•èËA=¬eÿµíj*Ö@òf´Ã#Ê܆OÇ}G‰
+¼ýçÿû«]çÒ©@ùøš>Žœ¡*GqZÏ.õÔЃ%OdkèÊ/¾ƒëánb~äÝêøŸ†'‰Lƒ)LQ,ó«Ò3*HuöQ’õ’¡Cg9Öñ¢Ú\£Óá+¬Uêe ï¼Ù0Ä,å>G ã;ˆÕÏ;è’!¿^€x´¶‚LÙ÷ +϶åI{£ŸÞ-½æ1î âr÷Ïú-½Æ(ÁUü²>]¸ëì­›'½ÖT°RÔ^éµvf…©Ê ý6Úuµ"ŸÑ5¡lñôWÅŽOXý ÏmJøóð'sD¼a@÷S}6·iT“*&ßàËæ…X‰˜‚—BÅöz^vøòõävp¹Œ¡)"SÎ(? èO²&Xê4½¡ìxB19N̤žó§•eÉv•ÙV(5â(ýé 86>eßil*ƒÑGõê 5¹+fr«cC/?
+‹†uÈaÆzþýK¢ÀÁÖ>ír‰;ÊÓ£A¨2ìʾ 6}ÌNÌíÐ’O©˜½I ‹›÷Ao¡ö,t"€{‡zlB,žbƒÈÒ#W«_ì]“aëy±ÃXÅ”D(­Ùz@jaD ìã‡q]n]ä
+ÇËÒ¼ 9Æ $Â`A2ÕHºÏ‚ ðSƼøâ ô
+ëA Ò‡ô
+ùÙG•ÄJ“†šbAp-œEÅQ<Àƒ¸nÚ'"#}ïâpdPEOöEGÆwœ¶
+rx¹67ÐioǽºÂq·® '¬¥†•±Éæ^.~;ŸÒNb?(ü:0)%RÝXK*g…O5+×­é;)³ IãoñçYí'`i„Ødtòü,“Ï}2¬ìʜًdûrw$B}•mFÁ¸’×V„ýüu]“‘Ž"éQß {ô7!AÅbY2ý©Õ
+xÉH’Ûè¤NÚ«à‰Vc‘u76ŠB­~2 º!è,Mrv–rZQéwQ|äGyÁVôI…R=6‚™Æ.!Žr%Å \4äÁáý€ømSA¤z›¯>ÃåÙgÆÄ*¸,h­+n‚ô¾ÈăCM2|5Õ´OŠJuµ•Üvm²\&Ûoßù-öj_†çÊŸå ìa£Žiˆ“?å`æ?Ý1‡~GÒß¡w!×Åûzg;×/Ã?páµQT¬ïSºÿN<ZäÁ_'Ü´Š¯mý
+¢EÚ\_@`
+ àGÑc4Žg=y Ø©Îˬíµ„E™÷â€Æ¨ŠzêiUâ©ÄÈðÌçd$%BóßþÎT_J4†-dE'0
+ÛÄ™19Ã^ѵ/Xð¿Øôs9ëAÈã*4‘dãô4(§eÉ¿_€Z§{Üœ)_Ç‘à´%­Ñ±ì3X!ÙŽ/˜k»€D‘>Á’5HaMð…Œ{ô S¾eAyVI>˹Ja²,È"o2Æï1¥Á@#ý“‰3vª²ð}½×øÝ"Ç6ÑŸ·£(Ð}ÑÀ;rQ,C§9q Xá'Q…gÆÿ3^&ÉqÜ@=ïÀ00%†µ½ä-´¥î¿õû@¢©îB‘R8¡V¦Ì?­’®i¼‘Jm•p.`²—0pEž"*äÍÊ;˜!0Ê'd󓸗¾™ˆ)“¼* Eã„}FNJ0YeI”ï$^g’ñyÝöN…7Itð¢„Gµ¯urãRYŒ›Ê ¬ç{-™¿ãåð¤LïÞêRôä!Öi††¶Hµ\ü#O"w•4ÉxPyŠYZ 7[±·bövƒÙÅÙdu|fFQ˜Ý’½We±!3uÒš=$¶&K“n 4d^>ËSxø£K ¥$í¾J¦`sDÈo…$×ÀW{+
+Z9}Þq~â‰ûx¬qhºvn1ÁE$n~X’‚Œ 9e›Ê×üCØ<<à-S" /ñ<3
+Þ´2a){JgSyéäK‰ï„¾uÍ~}<ïe(2 •éûhšÏ—ÕOÃýàa¸¶ûVe4ü6šööV g
+U ûæŽd@‡_à ‚b^ö‹³b…ý:¹Ý”pѵ)·<¶z*zéÜ\Ê"•@tÐ/è¹'\ÌP7¿xäýËÂNHž©
+c¸·Â< À@Ìr¢H²ûц\ÝZ§(ÿ)Ív§-µðƒßªáwqÚ)™XSÁ/öÅùq(ï–ÞƒoH0P:Ôä€J¥‘?
+WÃð<YsHU Ú­.㜫IKŸsâE€óE’{Ÿçr
+$êXµ7Æ^&n&Ï¥,LlË+æCѧE9y–þ2ÏŠQA€ÙN¡“
+xsŸ‰=ãP¨¹˜§¹·±gâÏ8ùxå†ÌËEº]ÅÃbC utc|ph4+îÇyñÅ×v^í5, 3uâ@ÚÆøuÛ¸s²‡WÕ•ëĸ[:Ì’€ÑóÎJOáÑ=¢|Ï3
+@ÃC¼ÕyÏYiß­QN²¯Œ 6nôkÍÇM €¢ÃÙ¦ðz
+#™
+†¶NsÕ„#HÈêüáZ¢NP
+¼áí:¸‰Æy5›à&AK½$54,JYËÆpDëL|µøöR$_ŽÜ£Š8Â^ëïP’ð”ìïJ`&‚Õ˜Š
+Z™ƒWÏë^¸Èæ¾Oc 6)7cW‰að$xéqï¥7%¶‹×¢Wûüç°™“ó—"q˜&ãrä+{_®~R=¡HË
+ÿ?‡)Jð6 bç­'Ž_ýäFÔÃ[Ú$µ„$–Z;15ÒÍFzsåùX%0N \Ìá‘ÇZ%x¢DÌ“Îûp=š‘á‘vì¾ã‡¦òãuDì`~ßUe®!º›8¤¤Òe\#À÷~åÞ&G`éV…¦)ǯ*\ ªÁ÷¶ÏÍ÷ä\ÒAt>lâsD›:ÖCà `¡Ûå(L”Ð?ô™ÿoV}*ñ¶’/NCˆÐ3;ü¹9Íe˜_nužø¸’´Íл¶Ø¹¼£¼ÓGc¯ÿ¶èÈó6m~(9~STø“8j¯‹¾Qh¨ MnYy±èzÆŠk†¢ÒÑ€¯v@Ö)›\Þ?ð5Þ=/Ãl0QX
+pÅø¦à“
+#í(n Û«&š
+ó3‹m­;Aórç>ýÔï¿:NçK`¢#õ@T>¦4!¡È'/¾t&yëœæµ ð(©Œ¹(«•á,^pRÆßÇšjŠxF²… ANõ¼q•)—ÌÔTÝÒã¯Áo®…'šSx*aE\eC¶ÊrÐ×"t¬óÃàµÎòó¦èyç‰áS¬Õ
+ƒÓЋDQõXýÄàO¸ ù@‡Šç>Ð!}€ú£ž"ÝðØ¥õ':dÈzB
+ìˆÛ0ÜMuˆfÁP/™Ñ¿ÙpÃÁ¼D… Òèž þ•ô†Ž xˆA ½8íRÍô‚òÚÎ6SwQN¸±«ñ=cl@Z)#Ý­CA@éñ^‡ÁAã\˜A à8Gë[5™(~N8JÑŒá€ÊZF¡öó·áÁ -¢;Íà«Ý÷Á d20Ó¾S  e+ÞMB_›Ž°M¸Ü@eÆä—–£ }&ÁŸÈT¼;é€2wÝtðg`üí5ð ˜6&Ô‘ Ç6&§€GÔRÃ4ìE2Ž ÌyÊ«ÓAÌ"æôt(™„ÛÙ[)"iÆîÖIJÈîX“ü^—Bz žZw€bcú(òðæ„ûZÄótf¬¥Æÿ3^&ÉuäH=AÝ¡N@Ã<¬µå-´¥î¿­ç@€,fFŠŸ‹6k•$šb<Ï­Ç:ãðÄ  úT§CL¤y¶¿€®›æˆÄL ­s
+” 6Yo ÀÀ (²4÷}cˆ¿/Bq½c>ìjèkP¯Íh¾c¤Æ˜Š…&ÉX7¢#õDä2V»‚!ðÀSo†ÄØçBªAY÷Õh*!Ž "Íó !ÙëhÃjìÙ< WÃP–´_Áðön –)ŸOÝ@Ì‘öLV©ÅÖ¡Îrlåœcl¨Á@“¶“híÄÔù+ÍÉzö)¼§*$2‡8êIlÙÞ“ ²
+Ü€AªxHÅQm0£LÉA /MncðŒ˜Éá»Õ`äj ÂnІb_ÌíÊ/ùŒÖظ!"õdüƒç]‡? ‡×ÿ>­³>Eš B©CÒŠ
+ñ'ô±êS¶‹Æu§–˜äßý¥Ì£c ÎSyF!ú=ÆùMÞô7OàB98I­û¸^¡ip¸Èó©(“ÇU´?ŽŽt‹<•ˆ   23ç>ï˜8A¯gÐyÅÅîþ5ÞVFƆвsê§ÄŽLoŒû¡þ+r¥´šJÆLRæÁ§ƒxR¦o w$CFB¢Âø°Ðã:”Θ d1©,W¦v…Åw.¥¡Ù¹M£”fôèÝAo8ädHZþ¤qjUÍ!FKYj¡±fÚìè =HjRn­•”ò”¢HNž±ÅTK.þ2…SGiºÏ^ˆ7‰_È·èèOÅ´Ù*ä,&ŒvS­Úh^p T9zR ê‰Ò´"£8
+2 t(k°ôÊÕ!—D«q>Åè¯Ò‘œXeŸê¤m bžéD™¤¬…ÀÄOeÃqJ…¶']2SÕþÐ,¶×½¶{*G –˸Ë0—ìl¶ *Æ°G5Þ5-í-Jüù˜íj#†Y·slº½åDÕDf>yƒ>b’ØýÜæÑ°W v…J¡àMƲ¯à:ÙÒbe(yH›NÞ¾‚V
+]ï@ô‹›77þ:\”#’}Œ
+ ò‘ÜÚ yâ?2â›Ô·Ú ýbÿ©(øµ·6~Æ+’ïCók/’Y'.Í­½äV{=Ð¥öj7YK)êÅ­½œ*ð˦—›OµWGGÂ&I\ñë½@’HB
+¼Ù‡ø°ˆ="ιŽC¤–Ñ8lMçCè LãêÏõ5Q
+r^<)Æ3°¯Ô× ´BÌ¥Vƒe‹Ûv¾c>^™NbÂXÝ<íÕ)¡\Óy«ÚÇÈT+½ º˜Û»û¹W@×þøá‚è?X4‚#»·
+›¿÷§Hå\o#€Ó'Ū1÷2ÐæaÈã¡!QóR15!;ûRÏb4^ÚÓ:xþÌœz“x’Ý%®Å ß_bU:È‚ÿê®7¥ÐÕ*¢+aDÁðÔ2nf¦Da;•Ò}¤ÓÌé—nüÕ3‰^ﱃãG„|Õ˜ø´N:!úSú)GU_Ö•®ÐÞðU̘vÇT‹ÙNJ°rD¿H…ÈFŠÍö)Ü’„Á»ä´—i•
+‚gµcqä Ç[‰Õƒ\(úëä’=¬`¤:Çë o
+‚°bC!:*Ìo¬Â‡âò€õ²…ÌËX8Âm ¼B'´®X ÄÏÂ*B@˜+lâ•[Ú$ñçž–Ÿª³Î‰Àr®ÒÆù] { øž¾©Ê”'™­S5,p—lÜ­o÷"ôÈKÿ¤3ÈC¯Y aþá¯n+o›$2äª“Ï jhãY/­ê Aþª™¶òš AHÀ‘p¶[À”ˆÎ²£ž/åõiún¶í$8@’EûV‘„rÇÄq
+[‡†Šö½Nò +ÕŒ¤Ú“‘ÝÏO]A—
+
+X²SaîSÚØ»A˜b5Ø„Ê÷ó)…ÄÉ`†0O­hô…šQ.{rAEÈbqã%í…‹)±õ‡˜*jfpØí¯Ã8 „$¤4ÏŒ’ù"4hcä6ds¨øãe’$ÉCÑèq‚4Îú´Ô-´Íºÿ¶ß'Á…;¼+ÛÚÚZ)>þPׯz·HQ'!²ÆøÇÓ–552@\Ö‘p Ç‘EO‹%{³+{L%|ºá,P*
+Ýx¥©¦W8EßE<‡º<Î’“©(°ÍÏTldeà‚^íÆ!¡±-½ÍÚÍô©Å1$¹E;Fªšƒ¶!ÖŽiIVùíuñòYž0’Ýi^JìS,.›O³ÆÓ1¨2ÈŸ<ü¸°> —\Ð.#¸¶f{É/"sƒ~½¿„m‰ £ÙË!$èŒNÛò€Å
+Xñ± kq'¿‚kH>ñ?ý½ 5Ú^úŸê²&Æ%¼™…çƒ-8PeO: ÕíÕì×ä7”:Ùæ¤XÄÎsä£>4dŸ³èvžÝhK£Äf•¹ó#%pË _r¼q¨ ѹá!ý¾™X#™Ý8”`ƒ‹ WB¯î¢¨MÖA±•oµ³tM„¤¤L:À)ÐD´úYoÖ…R¢‰þ
+(üTÆÀ6Š~C.¬·IêG»hz®\Âkéý¥Ï"ŠÀÚ j³¤Ž] J™ ª}Tì.U6µÃàé¨3Öo"ánAÄÑë±ð àݹ¼<Y½y3í“4j”<ž 5â¹ÿ¨šïB~1Ø帺
+ys#f+z„ovè»}[‘ÌyWíf“:@"ϱ2gw£<)Wì'̘& už­Ã* …I„º!ÌÉ¥tVä¿ãw9©ž‘'\iÅèCvÕ¶®ŠïuÌý” â­ûÃ4?JìS©­¡VÒÓ9Æe£ÖUôè¶öÜ(¿+paýëxÈ_ZqTTöóô€] ±åå»L@;q¾ ©Ô‡’·úk›Úü5µáÂiŒŽ÷ýí9V»”¯e˜X?/rìÿýs?)ºF’o·èŽ{Jä‹b׃­Û¬,!û°º]¾B$b8EåS£¨cACJ‹
+hŸˆ‚CÚüâMYî‚$·°úÍâbæ1\s,. éoÝ*ØÙÀ… 8«
+——¨¨ÖÈ~Þ%l
+dЫæ”ا„)H‚°ŸÎÁÌDÉc~º 6yÈò÷µÉ¯R¤’.Ãnnsâ—ô[ž«ßa§äcRö¥[Q#Ñ) e|÷Ë» Æ J“½/–Õn’w#¨É¡öt¾télmÀêÊ©§ñò:L‰ÂIAÿë6Ý·I]JìS—‰{ç\ps¿Í Σn(¦}4Î4|Wyù»@»¡NÌY ù¡äcR¿Îêñê©á2ç¼p ý‘øuÉ·’¼—1®Ÿº}ÿù>›/2~ ­QL½8'ý¶"%¶ÖaèM‡k0 ·ù)ËÕ94mšx ù jb”0…Ê/8[cœw²_¡K³s_1;‰Žz%š*E(?N{V³ Ɉqt[Þt ±ð]˜Ù®@¼¥µJrÆñ¤Aý#~±ì/ §ÉÖ1ŒòtŽB@¦­þ]è`ÁMm¬ÿ¦`¢Ƕ4Ýs(ÉÊ*S׶)äÀI
+%9r¦ôz4gà«„˜Ú³,ÍÛ8˜ðK&”•)œs˜]¿@;Ñ>%7Sq¡.ÇHIUŒÊë‘ûQ„:9
+ܦ\Yöé3rú¦k `#oAíS²4¨öÖüs2ù±a±36ãÚó²4°7y¯"±aY:7NÑãÿUÒÁqr¼ÅèÂí*·;v¶¿—ëÊí÷¢;ÈH~¤ËÌ}üGL¿Äãök‘Ãí” Ó]ÇwŸÚ 5Øj ³–ñÄ튡)T0š{ò¹9Tunìq¯V:´'ó„
+’˜J˜×z:‡û3CŸÛ¯ŠÀŠuÈçU7r
+Z á`6ñ»=;Æ–¢FßÖàïEA$CsÀÛ¾Fæ±lyÁ%Ǽ?¦o/é¾G£$e
+ÚA
+­<~9‰´ÈL`%íÌ1º¡\fóBH°ËXD^¼¯]¾FÉ稽ÞJD+û¹ ¸_ Mæ±B
+~Öœ’o+Ü®ùˆzkXX¢WŸ îCºCAȧÿîŠj2 >„ßÝ%• {±ÇÈèy7î‰äcÖb–„õÓLc´ÓÞJçÓ¬>NiÞñN
+èriux8'i”ŸSAŸðò±aç.¡W“ÈìUx&å‘¡·?¥G$È W ñȃ¹¦™€e‡òx~´Y±Fq¨£œþPj…>Äÿc¼Ì±ã¼±(¼î¡b<˜‡°2íè8£Bï¿¿‹÷PTÕjÑv`Q—ø7Ü«òãíz"¹Ï ^MŸ¸vñë<ÞX„Û2OmË™œ^.V\k®õêuù±‚
+²²Œc»J$r"óÁw¡”«¼ðƒ—vž|e~‡ç#Eäìx>‡É©Ü¾ÄÖwŠìF „71¡b”»©BÁø [œîC*^gjGòaÜbÏbAgæ5õ"VóD›°ªn•Žd¬€±w †5ôb‡ÙDäFÁä}¾
+ƺ¡!“ðõ뺤Z>(“)ʈíÛ ¨ÏÊ´2 Ôõãø¹ï€Tì2q^Ø56|5í
+¢Ü¡ã
+Ñ-TåfT;¢Ò~1&Œ*#ëNlV×L­H¸Ë.­žÁßsGt¿ö-µ‰OT±˜2U ’KîŽÀGd ?]Û.sL
+úÃàäå2 Ilø£È|Ñ'Ô!Oúãí
+ßÆ–ïú±ožá¸WÏ 3ð±Ë€2w¼ºl;É*$¬ó4r9€žL– ù˜x`3²poóÇ(³c$ŽNŽìÜKA0¶‘Fïk'‹hMµî-É•‘ϨU]F6’¦àl3â4ÏVÁ´rÈ‹æTùÞIÉÚxq
+í®¿ŠXŠ³ãÜäv2èÎÜw«pÝUªÛa:‰NÖŒêzËùÔYTŸ£‚„c¸ðâãät÷
+…Öpr“Dœ\¢ø^i_º€ph;G€ð]’„±>™N¸ý› "¬Q¦Ñ)¤ÀãxàÜÚàHñ}¸sH¡‰ I¥î¦BÐïÎåvÔ«'kK˜à¦¼ðvLÀF”ý¥çcK ±wÙå´:s3²C¤þZÛä®·by‡ZǦÜe*b"¢xÐÝ3Óòâ';&9yd¸•sf¸@°, }Ä°
+Ù˜‘Wĉq­@ŸjdQÙƸ±pª•Ù«‹”
+CÏZ7D^˜}(n‚,9ˆ 9»Â([é^"J}¤Iœdç$[•B6‡ð*mA&„1¤ðjÊÈúT¦dhãŒE<Ÿ“™[éØÓœo$H¬‹XùÕ«2l‹_QùâX¼iˆsvöx®ðòЩý¥gü¡(+‹½?õxäb.>œÅ}ÔåQ@&JÒÓþÒSm`Ö*Ï9K ¤·S…ù2ìg5I¼tê âCñÔñÓ9Oss½ÍeþºL1å£xT¦aËËí¼ ¸%hŸ]C~yèÔ×êÅ ‘.-n)ÊÈ8ï¢ê¿¼h/ Q¶>ü†ø8 ä¬É†xìíãô¡gÐçŸoóaW&ÌÁ3]›œâðßÄ´ˆY°K£sžW|Ìù
+Uë’áÖ¿QÛFmyè²1w~Â|~ç]zþ_ÿ} ·ÿüóö׿ož…ìå"¯—ûu‚Èþ“’JÀŽ®»¥¸\o¬üV ã›k‡Ÿú&H¾|H–ÙUâb 9díÄ‚àܨ¸‹Î)‰#ˆ?y¤ðIR‰éZóŸö%ô ílÊ
+†np²È¥¶dAfñê­ø"aq0|”'®ÅwVoäd¯ˆõ!,é
+Pôò€Yœ;9·~G`|åY0ëÑ $ÜŠ £ƒwœúÉ0ËÖBÈÌ(z`Æþ2eòFÜÇ™žE@3*f¿¬Ê.â&¶P'T f#:á/Z<@üá nÍ„‹Ú^ƒíQLKq«WTøƒNX¨Wh3K¿H:c¿üºÎ/‡GRpªß]6âú±?C·ó8è¾»ý­¤Ñ8¯)Y’ªÔFÙ£II´ëž[ð3;í ôñÄìW¥AƒÝëzÌ£‚`î¹%¿
+î²u8DA-"ÅkÄë&æ>qMÌ÷Ƶ „oé“f”"êÇkTÑ.j—Z/€$”5å¾—¿ÿiõÃÖ9™Àó܈[ðmÛò¹-ƒÚ¹È0HÎÿâ GÏatX¿2bÜß¡#!†îUé2t¨…°c’Š"—lA‰ÚI7 ¨.}£ÛÒî°cÁØY²O9¼_‚žh”®ÛO)ê">Ù¥$®=Dº—ôºfZÐNÒ*ƒ"¸g,³m; Lôiò!ÓÍBÔ_“';÷ÊÜÀ,Ä{DÇÌ°%ÒÇj¥‚¬¶¶ý
+
+,m£W¬2¦Òæø
+ 69pZÃÍâ4ûXZ-Hm:sš Òø+Œ)C]“õóŠ"˜N”«tò’Þá¥á–ó
+ ðy¬PÉ)bÉI­TgÉÀ%ßÇJ6*í^/‰nË};íÓ@¹I=4™Ù
+h¾ÒÒ™É
+=LÏæ|¤$GÐ¥ Ї@ ôS¹ŸÅ̪Aa±T
+¦îlƒßßöqÕ¼îÌì7T6f'D{‘·Üª= 1±`R=
+Û =1UYj–v†òÔÄimꜿzjƒ¤¡úR=Ž
+!‰
+ë^xR"ú ~
+åוL›öi{â$êªSªô‘RëÎSey #pQª~‹)®*ÊE‡åòŽ*«t DÃçå UÖG“4ŠSnÁ–¢­YÑy„ÐQ ugSú6„X~÷ªî¡gGWJ‘n¡è´üöM(¦bhØú$ëç­•äŒð,ÐÙ ‰3˜¶#<šøR¢Ñ²ApJY„ð5ûˆl·I@ƒEåAøÅA¬eØkÑ0ÝΙ’Lô-ëLľ´v¢š¡ÐŒlÒçä\U1¼™ ”j|LÕè‹0jÆB®¡¬\ym2kCB–6sœš„Ö㨲®hyNq
+úâ쉼ÈòÞñ¤BݽÒò¡À­!÷`ì4cSôó³R8ó7¥îù¬„¦*¨ú³N8=ÈjAÜæHL
+‘®â§ƒà ÍV)õrEéǸ‚9-Ø,Ðνç; l öi;4Ù MûBž1W
+FÌÜÃu~a“d.Jô¾@h¬CÐÄaÐuj´=Šâˆß½2‡Ò/Üò¡ç "†á%Œ×x zNg¸.SZžÞS˜b’Jc1*ë:çR:‡dŒ8¥II—¯Ö hB®´‹£†”õ£O² ;U}©0ÔOpŸnÀÈvë³m;)“NhèZYŸ‹Wбçìe„Õþ.HÇAÑã’ÖŒŒÑà|›½ÅIÓ¾„·ŸË|ñ!…ˆÑ-ÍÇ-¾8j}(ȇ¶_ zÈ_mOÆc ì
+c%á¯#"ˆ*Ù锂Ýo cyH¡Ldn•Ï¯ï‚^™ùûúþú[½€¡†ìöÜÒX…¾vĉ×ÐüŒ
+Ú „K`ö|Ë?6¬O óXA
+|qµ€°ÏÐ&9äLjI“eð¼uI+—fd°^í¼(ˆEÄæçè;ʃÏV¦Èó€YåÕÒÄ2û¬¹ê\réÇh¹í  ÆSbx:â(a±0lÈu\‡°€²Bxh¿gݸ.mr¦|Ðw’£7âÁæÀ™L<VcïÆ„}"DVyüï¡ê† ædÛÊÔp–û0]˜Û6ÊA¢8„¤8
+Yø`
+H‰Ò÷wVÐ÷u6PprqVà*ä2P036P°04PÐ1ŠR¹Âò€âúîÁ
+éÅ@ù}·\—|®@.W_ ¦@.€
+8;Z,'4.>1P%!M&K+3#%BQUs'$E0Y0B0&\W^Q'U_e6G+lVS/qL]DoKd+#,B3l$)2#h
+WO$l_LsNPA]VJhAao?t6jF+JPF'VPY"=1*FOLRDK7:]\)gY6A5L[iQ.4=erIAU-IU
+s02KkN?e>G(g0)U(&q?aUtE*5Z-D!QD+#6GHTL.`pUUN4fK=Ftn+F*$[eHZ3&F;hY
+!_r)C8*3Y$[bn:/qKQj]YQRNaB(dqm(uHjq,<G*OT`cmS(YH%,O$nqaB[#>!.G45$
+%'@a;dFr'sBZ<,pa:tIrqEdr&@bKc"qLhFJ1O6tX<ULUcQfYj>4'I2pXB[b03BSe*
+br!K)$sl__n#RNE.Y2$#)>7//rB]kA`=X:Lq>>2RcZ:D+;L':,<@M!s+(P1<c4dQ1
+i\<goA+/V'5#9C&FOhlR;0HX(nPp,4IlZ'9k:Oe==O1"h-?Kg^)-q`XK9-*_oRVtP
+g2T8.\c/\e,mq0lHT!oAs%XNE&`72*Gth+(dSr+M$/p!mC@u0InS4`KP?'Li1++ND
+OcEcBj5b?1a.Q>):sX;&1UNHmCJj\S[00N^V?'7EOC!u3gIG=L7;>hr9;VHX4A!g2
+j.=GhQj:7r4g`[,Nh=$[9r%QLR#i3CiNKonmo:3Ak\*L&kQ_nu6aWUQj)`HO4&#el
+R?JrV$/N[I1Z[kI;#71*KnmPk6]dA2dYVKKnNPQqB=tEKNa3rOeI!hSTS":jT!T(U
+D5"k"S*iY>($I^YJ);0Br,EljcDT@8Glf;5rS;E4HL(7Pj?5!W4<\2fMr5+@]TaeL
+,=m$`H.'b\VF=-_0&o-kB//#s=:=G"-%k`\$cr&!Q4YJ!S)j/dbP`C@QZi0^pi2tH
+mgD/F>Ib=-lI[^J9BQWAol<t@PIfV86:RMo0BS,CK[,dAHb"ZaL_t,9@<*NENqW4J
+\B`(Bd45eBlQRObMaTp'lS_E)U<=.8-dfPuj`HLaD^UU#/oHd+]rN(c8QVEnf9fCN
+c!\C5HBiJj9tIU,oCn)hGCn<#prZ0<P-#NTOhBEEV%maTkPVb+r6QU_*F6P6V3!_Y
+#a2Y?f4Jr=bfV$%.99`(I_`QP%:f42gr8?fYL5e@dK81,B.#'u,4_;uZY"1.O0q*c
+J*ZJ(W1;'Zi'=Y"/@bnG56Cmo^g2<~> endstream endobj 13 0 obj <</BBox[0.0 630.0 810.0 0.0]/Group 295 0 R/Length 1678498/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R/GS1 296 0 R>>/XObject<</Fm0 297 0 R>>>>/Subtype/Form>>stream
+0.793 0.801 0.129 0.016 k
+/GS0 gs
+0 630 810 -174.639 re
+f
+0 75.361 810 -75.361 re
+f
+0 0 0 0 k
+q 1 0 0 1 758.8652 63.3223 cm
+0 0 m
+0.44 2.19 l
+1.396 2.19 l
+0.44 -1.294 l
+-0.427 -1.294 l
+-1.397 2.19 l
+-0.441 2.19 l
+h
+3.881 -1.294 m
+2.969 -1.294 l
+2.969 2.278 l
+2.072 1.94 l
+2.072 2.69 l
+3.792 3.395 l
+3.881 3.395 l
+h
+5.409 -0.823 m
+5.409 -0.688 5.453 -0.57 5.541 -0.47 c
+5.637 -0.375 5.765 -0.324 5.923 -0.324 c
+6.077 -0.324 6.206 -0.375 6.306 -0.47 c
+6.412 -0.559 6.468 -0.676 6.468 -0.823 c
+6.468 -0.981 6.412 -1.103 6.306 -1.191 c
+6.206 -1.278 6.077 -1.323 5.923 -1.323 c
+5.776 -1.323 5.651 -1.278 5.556 -1.191 c
+5.456 -1.103 5.409 -0.981 5.409 -0.823 c
+9.998 0.646 m
+9.998 0 9.881 -0.496 9.645 -0.838 c
+9.41 -1.183 9.076 -1.353 8.646 -1.353 c
+8.213 -1.353 7.881 -1.187 7.646 -0.852 c
+7.411 -0.522 7.287 -0.044 7.279 0.573 c
+7.279 1.455 l
+7.279 2.102 7.389 2.595 7.617 2.94 c
+7.852 3.281 8.191 3.454 8.631 3.454 c
+9.072 3.454 9.404 3.281 9.631 2.94 c
+9.867 2.605 9.988 2.124 9.998 1.5 c
+h
+9.088 1.587 m
+9.088 1.959 9.047 2.234 8.97 2.41 c
+8.899 2.595 8.786 2.69 8.631 2.69 c
+8.345 2.69 8.194 2.356 8.176 1.691 c
+8.176 0.515 l
+8.176 0.139 8.213 -0.14 8.294 -0.324 c
+8.371 -0.511 8.488 -0.603 8.646 -0.603 c
+8.793 -0.603 8.899 -0.522 8.97 -0.353 c
+9.036 -0.177 9.076 0.092 9.088 0.455 c
+h
+10.807 -0.823 m
+10.807 -0.688 10.851 -0.57 10.939 -0.47 c
+11.035 -0.375 11.164 -0.324 11.322 -0.324 c
+11.476 -0.324 11.605 -0.375 11.704 -0.47 c
+11.81 -0.559 11.866 -0.676 11.866 -0.823 c
+11.866 -0.981 11.81 -1.103 11.704 -1.191 c
+11.605 -1.278 11.476 -1.323 11.322 -1.323 c
+11.174 -1.323 11.05 -1.278 10.954 -1.191 c
+10.855 -1.103 10.807 -0.981 10.807 -0.823 c
+15.397 0.646 m
+15.397 0 15.279 -0.496 15.044 -0.838 c
+14.809 -1.183 14.474 -1.353 14.044 -1.353 c
+13.611 -1.353 13.28 -1.187 13.045 -0.852 c
+12.81 -0.522 12.685 -0.044 12.677 0.573 c
+12.677 1.455 l
+12.677 2.102 12.787 2.595 13.016 2.94 c
+13.251 3.281 13.589 3.454 14.03 3.454 c
+14.47 3.454 14.802 3.281 15.029 2.94 c
+15.264 2.605 15.386 2.124 15.397 1.5 c
+h
+14.485 1.587 m
+14.485 1.959 14.445 2.234 14.368 2.41 c
+14.298 2.595 14.184 2.69 14.03 2.69 c
+13.743 2.69 13.593 2.356 13.574 1.691 c
+13.574 0.515 l
+13.574 0.139 13.611 -0.14 13.692 -0.324 c
+13.769 -0.511 13.886 -0.603 14.044 -0.603 c
+14.192 -0.603 14.298 -0.522 14.368 -0.353 c
+14.434 -0.177 14.474 0.092 14.485 0.455 c
+h
+f
+Q
+q 1 0 0 1 40.6324 65.0269 cm
+0 0 m
+-0.276 -0.305 -0.632 -0.54 -1.073 -0.706 c
+-1.507 -0.87 -1.97 -0.947 -2.469 -0.941 c
+-2.911 -0.941 -3.293 -0.845 -3.616 -0.661 c
+-3.939 -0.467 -4.189 -0.187 -4.366 0.177 c
+-4.542 0.537 -4.642 0.956 -4.659 1.426 c
+-4.671 1.68 -4.656 1.945 -4.615 2.22 c
+-4.439 3.425 l
+-4.285 4.454 -3.95 5.248 -3.439 5.806 c
+-2.932 6.365 -2.263 6.633 -1.44 6.615 c
+-0.735 6.604 -0.206 6.394 0.147 5.983 c
+0.5 5.571 0.68 4.969 0.69 4.175 c
+-0.721 4.175 l
+-0.735 4.586 l
+-0.775 5.104 -1.037 5.373 -1.514 5.395 c
+-2.213 5.424 -2.668 4.961 -2.881 4.013 c
+-2.955 3.631 l
+-3.16 2.22 l
+-3.2 1.897 -3.215 1.617 -3.204 1.382 c
+-3.197 1.008 -3.113 0.736 -2.955 0.559 c
+-2.801 0.389 -2.583 0.302 -2.308 0.294 c
+-2.043 0.294 -1.749 0.383 -1.426 0.559 c
+-1.176 1.912 l
+-2.19 1.912 l
+-1.984 3.028 l
+0.47 3.028 l
+h
+2.095 -0.837 m
+0.698 -0.837 l
+1.639 4.63 l
+3.05 4.63 l
+h
+1.83 6.012 m
+1.83 6.247 1.903 6.438 2.051 6.585 c
+2.198 6.733 2.373 6.806 2.579 6.806 c
+2.793 6.814 2.977 6.747 3.123 6.6 c
+3.271 6.461 3.344 6.284 3.344 6.072 c
+3.344 5.836 3.271 5.644 3.123 5.498 c
+2.977 5.351 2.793 5.278 2.579 5.278 c
+2.363 5.266 2.183 5.328 2.036 5.469 c
+1.897 5.615 1.83 5.796 1.83 6.012 c
+5.945 5.968 m
+5.71 4.63 l
+6.416 4.63 l
+6.24 3.558 l
+5.534 3.558 l
+5.064 0.794 l
+5.049 0.632 l
+5.038 0.405 5.13 0.294 5.328 0.294 c
+5.388 0.283 5.505 0.287 5.681 0.31 c
+5.548 -0.808 l
+5.343 -0.897 5.097 -0.941 4.814 -0.941 c
+4.409 -0.929 4.108 -0.786 3.902 -0.5 c
+3.697 -0.205 3.616 0.184 3.667 0.676 c
+4.137 3.558 l
+3.52 3.558 l
+3.697 4.63 l
+4.314 4.63 l
+4.549 5.968 l
+h
+7.915 0.397 m
+10.444 0.397 l
+10.238 -0.837 l
+6.254 -0.837 l
+7.533 6.527 l
+8.974 6.527 l
+h
+13.498 -0.837 m
+13.475 -0.72 13.468 -0.58 13.468 -0.411 c
+13.152 -0.764 12.796 -0.941 12.395 -0.941 c
+11.973 -0.941 11.634 -0.789 11.381 -0.484 c
+11.135 -0.183 11.021 0.195 11.042 0.647 c
+11.072 1.235 11.296 1.691 11.719 2.014 c
+12.137 2.345 12.704 2.514 13.409 2.514 c
+13.82 2.514 l
+13.88 2.955 l
+13.895 3.175 l
+13.895 3.469 13.777 3.616 13.542 3.616 c
+13.218 3.624 13.02 3.418 12.954 2.999 c
+11.557 2.984 l
+11.586 3.502 11.8 3.925 12.204 4.248 c
+12.604 4.58 13.093 4.74 13.674 4.734 c
+14.21 4.723 14.622 4.549 14.909 4.219 c
+15.202 3.896 15.324 3.462 15.276 2.926 c
+14.82 0.148 l
+14.805 -0.132 l
+14.795 -0.367 14.82 -0.573 14.879 -0.749 c
+14.865 -0.837 l
+h
+12.792 0.177 m
+13.034 0.166 13.263 0.272 13.468 0.5 c
+13.718 1.646 l
+13.409 1.646 l
+13.145 1.636 12.928 1.548 12.762 1.382 c
+12.594 1.213 12.498 1.004 12.469 0.75 c
+12.469 0.559 l
+12.469 0.449 12.49 0.357 12.542 0.279 c
+12.601 0.21 12.681 0.177 12.792 0.177 c
+18.057 -0.941 m
+17.595 -0.918 17.249 -0.706 17.014 -0.294 c
+16.837 -0.837 l
+15.53 -0.837 l
+16.882 6.924 l
+18.279 6.924 l
+17.749 4.175 l
+18.061 4.557 18.418 4.74 18.822 4.734 c
+19.3 4.723 19.66 4.543 19.895 4.19 c
+20.13 3.844 20.233 3.341 20.204 2.675 c
+20.182 1.9 20.087 1.228 19.91 0.662 c
+19.74 0.092 19.502 -0.323 19.189 -0.588 c
+18.874 -0.841 18.499 -0.959 18.057 -0.941 c
+18.836 2.661 m
+18.844 2.974 18.807 3.194 18.719 3.323 c
+18.639 3.458 18.506 3.528 18.323 3.528 c
+18.065 3.535 17.83 3.404 17.617 3.132 c
+17.19 0.618 l
+17.286 0.383 17.477 0.254 17.764 0.235 c
+18.282 0.206 18.591 0.578 18.69 1.353 c
+18.734 1.721 l
+h
+25.672 1.721 m
+23.262 1.721 l
+23.482 2.911 l
+25.878 2.911 l
+h
+32.933 2.352 m
+30.684 2.352 l
+30.346 0.397 l
+33.007 0.397 l
+32.787 -0.837 l
+28.685 -0.837 l
+29.964 6.527 l
+34.065 6.527 l
+33.845 5.278 l
+31.199 5.278 l
+30.89 3.543 l
+33.139 3.543 l
+h
+35.424 1.206 m
+36.63 4.63 l
+38.115 4.63 l
+35.807 -0.837 l
+34.484 -0.837 l
+33.867 4.63 l
+35.293 4.63 l
+h
+39.966 -0.941 m
+39.526 -0.941 39.147 -0.833 38.835 -0.617 c
+38.53 -0.404 38.31 -0.103 38.173 0.279 c
+38.034 0.662 37.99 1.095 38.041 1.588 c
+38.085 1.956 l
+38.202 2.866 38.474 3.562 38.908 4.042 c
+39.349 4.52 39.912 4.752 40.599 4.734 c
+41.098 4.711 41.488 4.557 41.774 4.263 c
+42.058 3.969 42.223 3.55 42.274 3.014 c
+42.293 2.691 42.282 2.374 42.245 2.073 c
+42.157 1.411 l
+39.423 1.411 l
+39.401 1.272 39.397 1.135 39.409 1 c
+39.438 0.507 39.677 0.258 40.128 0.25 c
+40.548 0.239 40.955 0.401 41.348 0.736 c
+41.819 -0.132 l
+41.631 -0.389 41.371 -0.588 41.04 -0.735 c
+40.706 -0.881 40.349 -0.947 39.966 -0.941 c
+40.467 3.558 m
+40.062 3.564 39.783 3.308 39.629 2.779 c
+39.526 2.411 l
+40.907 2.411 l
+40.944 2.606 40.966 2.764 40.966 2.881 c
+40.974 3.311 40.808 3.535 40.467 3.558 c
+45.799 3.249 m
+45.358 3.308 l
+44.982 3.315 44.681 3.138 44.446 2.779 c
+43.814 -0.837 l
+42.418 -0.837 l
+43.373 4.63 l
+44.681 4.63 l
+44.564 4.028 l
+44.729 4.293 44.891 4.476 45.049 4.586 c
+45.203 4.693 45.376 4.748 45.564 4.748 c
+45.67 4.748 45.813 4.723 45.99 4.675 c
+h
+47.897 1.573 m
+49.146 4.63 l
+50.661 4.63 l
+47.853 -1.66 l
+47.647 -2.15 47.405 -2.502 47.133 -2.719 c
+46.857 -2.944 46.537 -3.057 46.177 -3.057 c
+46.037 -3.057 45.832 -3.021 45.56 -2.954 c
+45.692 -1.837 l
+45.839 -1.851 l
+46.221 -1.851 46.501 -1.679 46.677 -1.337 c
+46.912 -0.823 l
+46.339 4.63 l
+47.808 4.63 l
+h
+52.997 4.734 m
+53.556 4.711 53.986 4.52 54.291 4.16 c
+54.603 3.807 54.776 3.326 54.806 2.72 c
+54.806 2.367 l
+54.754 1.309 54.501 0.485 54.041 -0.103 c
+53.578 -0.683 52.976 -0.959 52.233 -0.941 c
+51.8 -0.929 51.443 -0.816 51.16 -0.588 c
+50.873 -0.363 50.667 -0.055 50.543 0.339 c
+50.426 0.739 50.389 1.199 50.44 1.721 c
+50.469 1.985 l
+50.565 2.845 50.84 3.521 51.292 4.013 c
+51.741 4.513 52.31 4.752 52.997 4.734 c
+51.822 1.147 m
+51.822 0.559 51.997 0.254 52.35 0.235 c
+52.839 0.206 53.159 0.578 53.306 1.353 c
+53.35 1.721 l
+53.398 2.198 53.424 2.506 53.424 2.646 c
+53.424 3.223 53.24 3.528 52.88 3.558 c
+52.604 3.564 52.38 3.44 52.204 3.175 c
+52.035 2.918 51.928 2.547 51.88 2.058 c
+51.839 1.565 51.822 1.264 51.822 1.147 c
+57.246 4.63 m
+57.142 4.087 l
+57.532 4.535 57.958 4.752 58.422 4.734 c
+58.804 4.723 59.091 4.576 59.288 4.293 c
+59.494 4.017 59.598 3.62 59.598 3.102 c
+59.568 2.646 l
+58.98 -0.837 l
+57.584 -0.837 l
+58.157 2.646 l
+58.187 2.94 l
+58.204 3.341 58.058 3.543 57.745 3.543 c
+57.599 3.543 57.47 3.502 57.363 3.425 c
+57.264 3.344 57.175 3.252 57.098 3.146 c
+56.393 -0.837 l
+54.997 -0.837 l
+55.952 4.63 l
+h
+62.141 -0.941 m
+61.699 -0.941 61.321 -0.833 61.009 -0.617 c
+60.703 -0.404 60.483 -0.103 60.347 0.279 c
+60.207 0.662 60.163 1.095 60.215 1.588 c
+60.259 1.956 l
+60.377 2.866 60.649 3.562 61.082 4.042 c
+61.522 4.52 62.085 4.752 62.773 4.734 c
+63.272 4.711 63.662 4.557 63.949 4.263 c
+64.231 3.969 64.396 3.55 64.448 3.014 c
+64.466 2.691 64.456 2.374 64.419 2.073 c
+64.331 1.411 l
+61.597 1.411 l
+61.574 1.272 61.57 1.135 61.582 1 c
+61.611 0.507 61.85 0.258 62.302 0.25 c
+62.721 0.239 63.128 0.401 63.522 0.736 c
+63.992 -0.132 l
+63.805 -0.389 63.544 -0.588 63.213 -0.735 c
+62.879 -0.881 62.522 -0.947 62.141 -0.941 c
+62.64 3.558 m
+62.236 3.564 61.956 3.308 61.802 2.779 c
+61.699 2.411 l
+63.081 2.411 l
+63.118 2.606 63.14 2.764 63.14 2.881 c
+63.147 3.311 62.982 3.535 62.64 3.558 c
+69.034 0.235 m
+69.416 0.225 69.655 0.5 69.754 1.058 c
+71.063 1.058 l
+71.004 0.449 70.776 -0.04 70.386 -0.411 c
+69.993 -0.786 69.519 -0.959 68.961 -0.941 c
+68.45 -0.929 68.042 -0.779 67.741 -0.484 c
+67.436 -0.191 67.255 0.214 67.197 0.736 c
+67.145 1.154 67.171 1.665 67.27 2.264 c
+67.365 2.87 67.554 3.373 67.829 3.778 c
+68.269 4.432 68.894 4.752 69.711 4.734 c
+70.258 4.711 70.681 4.513 70.974 4.131 c
+71.269 3.749 71.4 3.234 71.371 2.587 c
+70.048 2.587 l
+70.048 2.897 l
+70.037 3.315 69.875 3.535 69.563 3.558 c
+69.063 3.576 68.77 3.19 68.682 2.396 c
+68.578 1.485 l
+68.538 1.21 68.523 0.985 68.535 0.809 c
+68.564 0.434 68.729 0.243 69.034 0.235 c
+74.028 -0.837 m
+74.006 -0.72 73.999 -0.58 73.999 -0.411 c
+73.682 -0.764 73.326 -0.941 72.926 -0.941 c
+72.503 -0.941 72.165 -0.789 71.911 -0.484 c
+71.666 -0.183 71.552 0.195 71.573 0.647 c
+71.602 1.235 71.827 1.691 72.25 2.014 c
+72.668 2.345 73.235 2.514 73.94 2.514 c
+74.351 2.514 l
+74.411 2.955 l
+74.425 3.175 l
+74.425 3.469 74.307 3.616 74.072 3.616 c
+73.749 3.624 73.551 3.418 73.484 2.999 c
+72.088 2.984 l
+72.117 3.502 72.331 3.925 72.734 4.248 c
+73.135 4.58 73.624 4.74 74.205 4.734 c
+74.741 4.723 75.153 4.549 75.439 4.219 c
+75.733 3.896 75.855 3.462 75.807 2.926 c
+75.351 0.148 l
+75.336 -0.132 l
+75.325 -0.367 75.351 -0.573 75.41 -0.749 c
+75.396 -0.837 l
+h
+73.322 0.177 m
+73.565 0.166 73.793 0.272 73.999 0.5 c
+74.249 1.646 l
+73.94 1.646 l
+73.675 1.636 73.459 1.548 73.293 1.382 c
+73.124 1.213 73.029 1.004 72.999 0.75 c
+72.999 0.559 l
+72.999 0.449 73.021 0.357 73.073 0.279 c
+73.131 0.21 73.212 0.177 73.322 0.177 c
+78.28 4.63 m
+78.177 4.087 l
+78.567 4.535 78.993 4.752 79.456 4.734 c
+79.838 4.723 80.125 4.576 80.323 4.293 c
+80.529 4.017 80.632 3.62 80.632 3.102 c
+80.603 2.646 l
+80.015 -0.837 l
+78.618 -0.837 l
+79.191 2.646 l
+79.221 2.94 l
+79.238 3.341 79.092 3.543 78.779 3.543 c
+78.633 3.543 78.504 3.502 78.397 3.425 c
+78.298 3.344 78.21 3.252 78.133 3.146 c
+77.428 -0.837 l
+76.031 -0.837 l
+76.986 4.63 l
+h
+85.357 0.235 m
+85.74 0.225 85.979 0.5 86.078 1.058 c
+87.386 1.058 l
+87.327 0.449 87.099 -0.04 86.71 -0.411 c
+86.317 -0.786 85.842 -0.959 85.284 -0.941 c
+84.773 -0.929 84.366 -0.779 84.064 -0.484 c
+83.759 -0.191 83.579 0.214 83.52 0.736 c
+83.469 1.154 83.494 1.665 83.593 2.264 c
+83.689 2.87 83.876 3.373 84.152 3.778 c
+84.593 4.432 85.218 4.752 86.033 4.734 c
+86.581 4.711 87.003 4.513 87.298 4.131 c
+87.591 3.749 87.724 3.234 87.695 2.587 c
+86.372 2.587 l
+86.372 2.897 l
+86.361 3.315 86.199 3.535 85.887 3.558 c
+85.387 3.576 85.093 3.19 85.004 2.396 c
+84.902 1.485 l
+84.861 1.21 84.846 0.985 84.858 0.809 c
+84.887 0.434 85.052 0.243 85.357 0.235 c
+90.627 4.734 m
+91.186 4.711 91.616 4.52 91.921 4.16 c
+92.233 3.807 92.406 3.326 92.435 2.72 c
+92.435 2.367 l
+92.383 1.309 92.13 0.485 91.67 -0.103 c
+91.208 -0.683 90.605 -0.959 89.862 -0.941 c
+89.43 -0.929 89.073 -0.816 88.79 -0.588 c
+88.503 -0.363 88.297 -0.055 88.173 0.339 c
+88.055 0.739 88.018 1.199 88.069 1.721 c
+88.098 1.985 l
+88.194 2.845 88.47 3.521 88.921 4.013 c
+89.37 4.513 89.939 4.752 90.627 4.734 c
+89.451 1.147 m
+89.451 0.559 89.627 0.254 89.98 0.235 c
+90.469 0.206 90.789 0.578 90.936 1.353 c
+90.98 1.721 l
+91.028 2.198 91.053 2.506 91.053 2.646 c
+91.053 3.223 90.87 3.528 90.509 3.558 c
+90.234 3.564 90.01 3.44 89.833 3.175 c
+89.665 2.918 89.557 2.547 89.509 2.058 c
+89.469 1.565 89.451 1.264 89.451 1.147 c
+94.878 4.63 m
+94.776 4.087 l
+95.165 4.535 95.591 4.752 96.054 4.734 c
+96.437 4.723 96.723 4.576 96.922 4.293 c
+97.128 4.017 97.23 3.62 97.23 3.102 c
+97.201 2.646 l
+96.613 -0.837 l
+95.217 -0.837 l
+95.79 2.646 l
+95.819 2.94 l
+95.838 3.341 95.691 3.543 95.379 3.543 c
+95.231 3.543 95.103 3.502 94.996 3.425 c
+94.897 3.344 94.809 3.252 94.732 3.146 c
+94.026 -0.837 l
+92.63 -0.837 l
+93.585 4.63 l
+h
+100.318 5.968 m
+100.082 4.63 l
+100.788 4.63 l
+100.611 3.558 l
+99.906 3.558 l
+99.435 0.794 l
+99.421 0.632 l
+99.41 0.405 99.502 0.294 99.7 0.294 c
+99.759 0.283 99.876 0.287 100.052 0.31 c
+99.921 -0.808 l
+99.715 -0.897 99.468 -0.941 99.186 -0.941 c
+98.781 -0.929 98.48 -0.786 98.274 -0.5 c
+98.068 -0.205 97.987 0.184 98.039 0.676 c
+98.51 3.558 l
+97.892 3.558 l
+98.068 4.63 l
+98.685 4.63 l
+98.921 5.968 l
+h
+103.933 3.249 m
+103.493 3.308 l
+103.117 3.315 102.816 3.138 102.581 2.779 c
+101.949 -0.837 l
+100.553 -0.837 l
+101.508 4.63 l
+102.816 4.63 l
+102.699 4.028 l
+102.864 4.293 103.026 4.476 103.184 4.586 c
+103.338 4.693 103.51 4.748 103.698 4.748 c
+103.805 4.748 103.948 4.723 104.125 4.675 c
+h
+105.304 -0.837 m
+103.907 -0.837 l
+104.848 4.63 l
+106.259 4.63 l
+h
+105.039 6.012 m
+105.039 6.247 105.113 6.438 105.26 6.585 c
+105.407 6.733 105.583 6.806 105.789 6.806 c
+106.003 6.814 106.186 6.747 106.333 6.6 c
+106.479 6.461 106.554 6.284 106.554 6.072 c
+106.554 5.836 106.479 5.644 106.333 5.498 c
+106.186 5.351 106.003 5.278 105.789 5.278 c
+105.573 5.266 105.392 5.328 105.245 5.469 c
+105.106 5.615 105.039 5.796 105.039 6.012 c
+108.876 -0.941 m
+108.413 -0.918 108.067 -0.706 107.832 -0.294 c
+107.655 -0.837 l
+106.348 -0.837 l
+107.7 6.924 l
+109.097 6.924 l
+108.567 4.175 l
+108.879 4.557 109.236 4.74 109.64 4.734 c
+110.118 4.723 110.478 4.543 110.713 4.19 c
+110.948 3.844 111.051 3.341 111.022 2.675 c
+111 1.9 110.905 1.228 110.728 0.662 c
+110.559 0.092 110.32 -0.323 110.007 -0.588 c
+109.692 -0.841 109.317 -0.959 108.876 -0.941 c
+109.655 2.661 m
+109.662 2.974 109.625 3.194 109.537 3.323 c
+109.457 3.458 109.324 3.528 109.141 3.528 c
+108.883 3.535 108.648 3.404 108.435 3.132 c
+108.009 0.618 l
+108.104 0.383 108.295 0.254 108.582 0.235 c
+109.1 0.206 109.409 0.578 109.508 1.353 c
+109.552 1.721 l
+h
+114.003 -0.338 m
+113.656 -0.749 113.257 -0.947 112.797 -0.941 c
+112.433 -0.941 112.143 -0.823 111.929 -0.588 c
+111.713 -0.345 111.591 -0.014 111.562 0.397 c
+111.54 0.632 111.54 0.86 111.562 1.088 c
+112.164 4.63 l
+113.546 4.63 l
+112.958 1.073 l
+112.944 0.823 l
+112.933 0.655 112.958 0.518 113.018 0.412 c
+113.076 0.312 113.168 0.258 113.297 0.25 c
+113.569 0.239 113.826 0.383 114.061 0.676 c
+114.751 4.63 l
+116.148 4.63 l
+115.208 -0.837 l
+113.899 -0.837 l
+h
+118.897 5.968 m
+118.662 4.63 l
+119.368 4.63 l
+119.191 3.558 l
+118.485 3.558 l
+118.015 0.794 l
+118.001 0.632 l
+117.989 0.405 118.082 0.294 118.279 0.294 c
+118.338 0.283 118.456 0.287 118.632 0.31 c
+118.5 -0.808 l
+118.294 -0.897 118.048 -0.941 117.766 -0.941 c
+117.361 -0.929 117.06 -0.786 116.854 -0.5 c
+116.648 -0.205 116.567 0.184 116.619 0.676 c
+117.089 3.558 l
+116.472 3.558 l
+116.648 4.63 l
+117.265 4.63 l
+117.5 5.968 l
+h
+121.396 -0.941 m
+120.955 -0.941 120.576 -0.833 120.264 -0.617 c
+119.959 -0.404 119.738 -0.103 119.603 0.279 c
+119.463 0.662 119.418 1.095 119.47 1.588 c
+119.514 1.956 l
+119.632 2.866 119.904 3.562 120.337 4.042 c
+120.779 4.52 121.34 4.752 122.028 4.734 c
+122.528 4.711 122.917 4.557 123.204 4.263 c
+123.487 3.969 123.652 3.55 123.703 3.014 c
+123.722 2.691 123.711 2.374 123.674 2.073 c
+123.586 1.411 l
+120.852 1.411 l
+120.829 1.272 120.826 1.135 120.837 1 c
+120.866 0.507 121.105 0.258 121.558 0.25 c
+121.976 0.239 122.385 0.401 122.778 0.736 c
+123.248 -0.132 l
+123.061 -0.389 122.799 -0.588 122.469 -0.735 c
+122.134 -0.881 121.778 -0.947 121.396 -0.941 c
+121.895 3.558 m
+121.492 3.564 121.212 3.308 121.058 2.779 c
+120.955 2.411 l
+122.337 2.411 l
+122.373 2.606 122.395 2.764 122.395 2.881 c
+122.402 3.311 122.237 3.535 121.895 3.558 c
+135.212 4.146 m
+135.565 4.546 135.948 4.74 136.359 4.734 c
+136.76 4.723 137.065 4.572 137.271 4.278 c
+137.484 3.992 137.594 3.595 137.594 3.088 c
+137.579 2.602 l
+136.991 -0.837 l
+135.595 -0.837 l
+136.168 2.617 l
+136.197 2.911 l
+136.205 3.323 136.058 3.528 135.757 3.528 c
+135.529 3.535 135.316 3.418 135.11 3.175 c
+134.404 -0.837 l
+133.008 -0.837 l
+134.36 6.924 l
+135.757 6.924 l
+h
+140.696 5.968 m
+140.461 4.63 l
+141.166 4.63 l
+140.99 3.558 l
+140.284 3.558 l
+139.814 0.794 l
+139.799 0.632 l
+139.788 0.405 139.879 0.294 140.078 0.294 c
+140.137 0.283 140.255 0.287 140.431 0.31 c
+140.299 -0.808 l
+140.093 -0.897 139.847 -0.941 139.563 -0.941 c
+139.16 -0.929 138.858 -0.786 138.653 -0.5 c
+138.447 -0.205 138.366 0.184 138.418 0.676 c
+138.888 3.558 l
+138.27 3.558 l
+138.447 4.63 l
+139.064 4.63 l
+139.299 5.968 l
+h
+143.742 5.968 m
+143.507 4.63 l
+144.213 4.63 l
+144.036 3.558 l
+143.33 3.558 l
+142.86 0.794 l
+142.846 0.632 l
+142.834 0.405 142.927 0.294 143.125 0.294 c
+143.183 0.283 143.301 0.287 143.478 0.31 c
+143.345 -0.808 l
+143.139 -0.897 142.894 -0.941 142.61 -0.941 c
+142.206 -0.929 141.905 -0.786 141.699 -0.5 c
+141.493 -0.205 141.412 0.184 141.464 0.676 c
+141.934 3.558 l
+141.317 3.558 l
+141.493 4.63 l
+142.111 4.63 l
+142.346 5.968 l
+h
+146.491 -0.941 m
+146.069 -0.929 145.734 -0.746 145.491 -0.382 c
+145.007 -2.94 l
+143.625 -2.94 l
+144.933 4.63 l
+146.212 4.63 l
+146.123 4.131 l
+146.454 4.543 146.836 4.74 147.27 4.734 c
+147.748 4.723 148.107 4.543 148.342 4.19 c
+148.578 3.837 148.681 3.326 148.652 2.661 c
+148.629 1.867 148.534 1.195 148.357 0.647 c
+148.182 0.107 147.931 -0.301 147.608 -0.573 c
+147.292 -0.837 146.921 -0.959 146.491 -0.941 c
+147.27 2.675 m
+147.27 2.988 147.226 3.209 147.137 3.337 c
+147.056 3.462 146.931 3.532 146.755 3.543 c
+146.509 3.543 146.285 3.418 146.079 3.175 c
+145.624 0.588 l
+145.73 0.372 145.921 0.258 146.197 0.25 c
+146.704 0.221 147.023 0.622 147.152 1.455 c
+147.255 2.484 l
+h
+151.499 0.662 m
+151.536 0.897 151.356 1.106 150.956 1.294 c
+150.551 1.478 150.25 1.65 150.044 1.808 c
+149.846 1.974 149.699 2.153 149.603 2.352 c
+149.515 2.558 149.475 2.786 149.485 3.043 c
+149.515 3.532 149.721 3.94 150.103 4.263 c
+150.485 4.586 150.948 4.74 151.499 4.734 c
+152.047 4.723 152.477 4.561 152.793 4.248 c
+153.105 3.932 153.256 3.521 153.248 3.014 c
+151.852 3.014 l
+151.86 3.249 151.831 3.414 151.764 3.514 c
+151.694 3.62 151.58 3.675 151.426 3.675 c
+151.257 3.675 151.118 3.616 151 3.499 c
+150.89 3.389 150.827 3.256 150.809 3.102 c
+150.757 2.885 150.927 2.691 151.309 2.514 c
+151.698 2.345 151.977 2.205 152.146 2.088 c
+152.646 1.754 152.874 1.297 152.837 0.721 c
+152.815 0.387 152.712 0.092 152.529 -0.161 c
+152.34 -0.419 152.087 -0.613 151.764 -0.749 c
+151.448 -0.885 151.106 -0.947 150.735 -0.941 c
+150.195 -0.929 149.757 -0.76 149.427 -0.426 c
+149.092 -0.084 148.928 0.346 148.928 0.867 c
+150.264 0.853 l
+150.254 0.588 150.294 0.397 150.382 0.279 c
+150.478 0.169 150.625 0.118 150.823 0.118 c
+151 0.118 151.147 0.162 151.264 0.25 c
+151.389 0.346 151.47 0.485 151.499 0.662 c
+153.595 -0.147 m
+153.595 0.096 153.668 0.294 153.815 0.441 c
+153.969 0.595 154.167 0.676 154.403 0.676 c
+154.638 0.676 154.829 0.603 154.976 0.456 c
+155.13 0.316 155.211 0.14 155.211 -0.073 c
+155.211 -0.319 155.126 -0.521 154.961 -0.675 c
+154.803 -0.823 154.612 -0.897 154.388 -0.897 c
+154.152 -0.897 153.961 -0.83 153.815 -0.691 c
+153.668 -0.544 153.595 -0.363 153.595 -0.147 c
+154.535 3.616 m
+154.535 3.859 154.609 4.057 154.755 4.204 c
+154.91 4.358 155.108 4.439 155.343 4.439 c
+155.579 4.439 155.77 4.366 155.916 4.219 c
+156.071 4.079 156.151 3.903 156.151 3.69 c
+156.151 3.444 156.067 3.242 155.902 3.088 c
+155.744 2.94 155.553 2.866 155.328 2.866 c
+155.093 2.866 154.902 2.933 154.755 3.072 c
+154.609 3.219 154.535 3.4 154.535 3.616 c
+156.5 -1.469 m
+155.442 -1.469 l
+158.97 6.527 l
+160.028 6.527 l
+h
+158.566 -1.469 m
+157.508 -1.469 l
+161.036 6.527 l
+162.094 6.527 l
+h
+164.02 -0.837 m
+163.997 -0.72 163.99 -0.58 163.99 -0.411 c
+163.674 -0.764 163.317 -0.941 162.917 -0.941 c
+162.494 -0.941 162.156 -0.789 161.903 -0.484 c
+161.657 -0.183 161.543 0.195 161.564 0.647 c
+161.594 1.235 161.818 1.691 162.241 2.014 c
+162.659 2.345 163.226 2.514 163.931 2.514 c
+164.342 2.514 l
+164.402 2.955 l
+164.416 3.175 l
+164.416 3.469 164.298 3.616 164.063 3.616 c
+163.74 3.624 163.542 3.418 163.475 2.999 c
+162.079 2.984 l
+162.108 3.502 162.322 3.925 162.726 4.248 c
+163.126 4.58 163.615 4.74 164.196 4.734 c
+164.732 4.723 165.144 4.549 165.431 4.219 c
+165.724 3.896 165.846 3.462 165.798 2.926 c
+165.342 0.148 l
+165.327 -0.132 l
+165.316 -0.367 165.342 -0.573 165.401 -0.749 c
+165.387 -0.837 l
+h
+163.314 0.177 m
+163.556 0.166 163.785 0.272 163.99 0.5 c
+164.24 1.646 l
+163.931 1.646 l
+163.667 1.636 163.45 1.548 163.284 1.382 c
+163.116 1.213 163.02 1.004 162.991 0.75 c
+162.991 0.559 l
+162.991 0.449 163.012 0.357 163.064 0.279 c
+163.122 0.21 163.203 0.177 163.314 0.177 c
+168.58 -0.941 m
+168.117 -0.918 167.771 -0.706 167.536 -0.294 c
+167.359 -0.837 l
+166.052 -0.837 l
+167.404 6.924 l
+168.801 6.924 l
+168.271 4.175 l
+168.583 4.557 168.94 4.74 169.344 4.734 c
+169.822 4.723 170.182 4.543 170.417 4.19 c
+170.652 3.844 170.755 3.341 170.725 2.675 c
+170.704 1.9 170.608 1.228 170.432 0.662 c
+170.263 0.092 170.024 -0.323 169.711 -0.588 c
+169.395 -0.841 169.021 -0.959 168.58 -0.941 c
+169.358 2.661 m
+169.366 2.974 169.329 3.194 169.241 3.323 c
+169.16 3.458 169.028 3.528 168.844 3.528 c
+168.587 3.535 168.352 3.404 168.138 3.132 c
+167.712 0.618 l
+167.808 0.383 167.999 0.254 168.286 0.235 c
+168.803 0.206 169.113 0.578 169.212 1.353 c
+169.256 1.721 l
+h
+173.823 4.734 m
+174.382 4.711 174.812 4.52 175.117 4.16 c
+175.429 3.807 175.602 3.326 175.631 2.72 c
+175.631 2.367 l
+175.581 1.309 175.327 0.485 174.867 -0.103 c
+174.405 -0.683 173.802 -0.959 173.059 -0.941 c
+172.626 -0.929 172.269 -0.816 171.986 -0.588 c
+171.7 -0.363 171.494 -0.055 171.369 0.339 c
+171.251 0.739 171.215 1.199 171.266 1.721 c
+171.295 1.985 l
+171.39 2.845 171.666 3.521 172.119 4.013 c
+172.566 4.513 173.136 4.752 173.823 4.734 c
+172.647 1.147 m
+172.647 0.559 172.824 0.254 173.177 0.235 c
+173.665 0.206 173.985 0.578 174.133 1.353 c
+174.176 1.721 l
+174.224 2.198 174.25 2.506 174.25 2.646 c
+174.25 3.223 174.066 3.528 173.706 3.558 c
+173.43 3.564 173.206 3.44 173.03 3.175 c
+172.861 2.918 172.755 2.547 172.707 2.058 c
+172.666 1.565 172.647 1.264 172.647 1.147 c
+178.615 -0.338 m
+178.27 -0.749 177.869 -0.947 177.41 -0.941 c
+177.046 -0.941 176.756 -0.823 176.543 -0.588 c
+176.327 -0.345 176.205 -0.014 176.175 0.397 c
+176.153 0.632 176.153 0.86 176.175 1.088 c
+176.778 4.63 l
+178.16 4.63 l
+177.572 1.073 l
+177.557 0.823 l
+177.546 0.655 177.572 0.518 177.63 0.412 c
+177.69 0.312 177.781 0.258 177.91 0.25 c
+178.182 0.239 178.439 0.383 178.675 0.676 c
+179.365 4.63 l
+180.761 4.63 l
+179.82 -0.837 l
+178.513 -0.837 l
+h
+183.514 5.968 m
+183.279 4.63 l
+183.984 4.63 l
+183.808 3.558 l
+183.103 3.558 l
+182.632 0.794 l
+182.617 0.632 l
+182.606 0.405 182.698 0.294 182.897 0.294 c
+182.955 0.283 183.073 0.287 183.249 0.31 c
+183.117 -0.808 l
+182.912 -0.897 182.665 -0.941 182.382 -0.941 c
+181.978 -0.929 181.677 -0.786 181.47 -0.5 c
+181.265 -0.205 181.185 0.184 181.235 0.676 c
+181.706 3.558 l
+181.089 3.558 l
+181.265 4.63 l
+181.883 4.63 l
+182.118 5.968 l
+h
+183.999 -0.147 m
+183.999 0.096 184.073 0.294 184.219 0.441 c
+184.374 0.595 184.572 0.676 184.807 0.676 c
+185.042 0.676 185.233 0.603 185.381 0.456 c
+185.535 0.316 185.616 0.14 185.616 -0.073 c
+185.616 -0.319 185.532 -0.521 185.366 -0.675 c
+185.208 -0.823 185.017 -0.897 184.793 -0.897 c
+184.558 -0.897 184.366 -0.83 184.219 -0.691 c
+184.073 -0.544 183.999 -0.363 183.999 -0.147 c
+189.137 4.734 m
+189.614 4.711 189.967 4.513 190.195 4.131 c
+190.371 4.63 l
+191.635 4.63 l
+190.71 -0.837 l
+190.61 -1.55 190.334 -2.094 189.886 -2.469 c
+189.445 -2.84 188.886 -3.017 188.21 -2.998 c
+187.916 -2.987 187.622 -2.925 187.328 -2.807 c
+187.034 -2.697 186.792 -2.543 186.608 -2.337 c
+187.167 -1.352 l
+187.45 -1.675 187.803 -1.841 188.225 -1.851 c
+188.82 -1.881 189.187 -1.547 189.328 -0.852 c
+189.43 -0.396 l
+189.107 -0.76 188.743 -0.941 188.343 -0.941 c
+187.872 -0.941 187.512 -0.76 187.269 -0.396 c
+187.024 -0.037 186.906 0.47 186.916 1.118 c
+186.916 1.507 186.953 1.929 187.034 2.382 c
+187.111 2.841 187.226 3.227 187.373 3.543 c
+187.755 4.355 188.343 4.752 189.137 4.734 c
+188.284 1.133 m
+188.284 0.551 188.468 0.258 188.842 0.25 c
+189.085 0.25 189.316 0.357 189.534 0.574 c
+190.004 3.19 l
+189.875 3.414 189.688 3.532 189.445 3.543 c
+188.857 3.572 188.497 3.091 188.372 2.103 c
+188.313 1.632 188.284 1.309 188.284 1.133 c
+193.054 -0.837 m
+191.657 -0.837 l
+192.598 4.63 l
+194.009 4.63 l
+h
+192.789 6.012 m
+192.789 6.247 192.863 6.438 193.01 6.585 c
+193.156 6.733 193.333 6.806 193.538 6.806 c
+193.752 6.814 193.935 6.747 194.083 6.6 c
+194.23 6.461 194.303 6.284 194.303 6.072 c
+194.303 5.836 194.23 5.644 194.083 5.498 c
+193.935 5.351 193.752 5.278 193.538 5.278 c
+193.322 5.266 193.142 5.328 192.995 5.469 c
+192.855 5.615 192.789 5.796 192.789 6.012 c
+196.909 5.968 m
+196.674 4.63 l
+197.379 4.63 l
+197.202 3.558 l
+196.497 3.558 l
+196.027 0.794 l
+196.012 0.632 l
+196.001 0.405 196.092 0.294 196.291 0.294 c
+196.35 0.283 196.468 0.287 196.644 0.31 c
+196.512 -0.808 l
+196.306 -0.897 196.059 -0.941 195.776 -0.941 c
+195.373 -0.929 195.071 -0.786 194.866 -0.5 c
+194.66 -0.205 194.579 0.184 194.631 0.676 c
+195.101 3.558 l
+194.483 3.558 l
+194.66 4.63 l
+195.277 4.63 l
+195.512 5.968 l
+h
+198.614 -0.837 m
+197.218 -0.837 l
+198.555 6.924 l
+199.966 6.924 l
+h
+202.259 -0.837 m
+202.237 -0.72 202.23 -0.58 202.23 -0.411 c
+201.914 -0.764 201.557 -0.941 201.156 -0.941 c
+200.734 -0.941 200.396 -0.789 200.142 -0.484 c
+199.896 -0.183 199.782 0.195 199.805 0.647 c
+199.834 1.235 200.058 1.691 200.48 2.014 c
+200.9 2.345 201.465 2.514 202.171 2.514 c
+202.583 2.514 l
+202.641 2.955 l
+202.656 3.175 l
+202.656 3.469 202.538 3.616 202.303 3.616 c
+201.98 3.624 201.781 3.418 201.715 2.999 c
+200.318 2.984 l
+200.348 3.502 200.561 3.925 200.965 4.248 c
+201.366 4.58 201.854 4.74 202.435 4.734 c
+202.972 4.723 203.383 4.549 203.67 4.219 c
+203.965 3.896 204.085 3.462 204.038 2.926 c
+203.582 0.148 l
+203.568 -0.132 l
+203.556 -0.367 203.582 -0.573 203.641 -0.749 c
+203.626 -0.837 l
+h
+201.553 0.177 m
+201.796 0.166 202.024 0.272 202.23 0.5 c
+202.479 1.646 l
+202.171 1.646 l
+201.906 1.636 201.69 1.548 201.524 1.382 c
+201.355 1.213 201.259 1.004 201.23 0.75 c
+201.23 0.559 l
+201.23 0.449 201.252 0.357 201.303 0.279 c
+201.362 0.21 201.443 0.177 201.553 0.177 c
+206.82 -0.941 m
+206.356 -0.918 206.011 -0.706 205.775 -0.294 c
+205.6 -0.837 l
+204.291 -0.837 l
+205.644 6.924 l
+207.04 6.924 l
+206.511 4.175 l
+206.823 4.557 207.179 4.74 207.584 4.734 c
+208.061 4.723 208.422 4.543 208.657 4.19 c
+208.892 3.844 208.995 3.341 208.965 2.675 c
+208.943 1.9 208.848 1.228 208.671 0.662 c
+208.503 0.092 208.264 -0.323 207.951 -0.588 c
+207.635 -0.841 207.26 -0.959 206.82 -0.941 c
+207.599 2.661 m
+207.606 2.974 207.569 3.194 207.481 3.323 c
+207.4 3.458 207.267 3.528 207.084 3.528 c
+206.826 3.535 206.591 3.404 206.378 3.132 c
+205.952 0.618 l
+206.047 0.383 206.238 0.254 206.525 0.235 c
+207.044 0.206 207.352 0.578 207.452 1.353 c
+207.495 1.721 l
+h
+209.432 -0.147 m
+209.432 0.096 209.505 0.294 209.652 0.441 c
+209.807 0.595 210.005 0.676 210.24 0.676 c
+210.475 0.676 210.666 0.603 210.814 0.456 c
+210.968 0.316 211.049 0.14 211.049 -0.073 c
+211.049 -0.319 210.965 -0.521 210.799 -0.675 c
+210.641 -0.823 210.45 -0.897 210.226 -0.897 c
+209.991 -0.897 209.8 -0.83 209.652 -0.691 c
+209.505 -0.544 209.432 -0.363 209.432 -0.147 c
+214.217 0.235 m
+214.599 0.225 214.838 0.5 214.937 1.058 c
+216.245 1.058 l
+216.187 0.449 215.959 -0.04 215.569 -0.411 c
+215.175 -0.786 214.702 -0.959 214.143 -0.941 c
+213.632 -0.929 213.224 -0.779 212.923 -0.484 c
+212.618 -0.191 212.438 0.214 212.379 0.736 c
+212.328 1.154 212.353 1.665 212.453 2.264 c
+212.548 2.87 212.736 3.373 213.012 3.778 c
+213.452 4.432 214.077 4.752 214.893 4.734 c
+215.441 4.711 215.863 4.513 216.157 4.131 c
+216.451 3.749 216.584 3.234 216.553 2.587 c
+215.231 2.587 l
+215.231 2.897 l
+215.22 3.315 215.058 3.535 214.745 3.558 c
+214.246 3.576 213.952 3.19 213.864 2.396 c
+213.761 1.485 l
+213.721 1.21 213.706 0.985 213.717 0.809 c
+213.746 0.434 213.912 0.243 214.217 0.235 c
+219.49 4.734 m
+220.048 4.711 220.478 4.52 220.783 4.16 c
+221.095 3.807 221.268 3.326 221.298 2.72 c
+221.298 2.367 l
+221.247 1.309 220.993 0.485 220.534 -0.103 c
+220.071 -0.683 219.468 -0.959 218.726 -0.941 c
+218.292 -0.929 217.935 -0.816 217.652 -0.588 c
+217.366 -0.363 217.16 -0.055 217.035 0.339 c
+216.918 0.739 216.881 1.199 216.933 1.721 c
+216.962 1.985 l
+217.057 2.845 217.332 3.521 217.785 4.013 c
+218.233 4.513 218.803 4.752 219.49 4.734 c
+218.314 1.147 m
+218.314 0.559 218.49 0.254 218.843 0.235 c
+219.331 0.206 219.651 0.578 219.799 1.353 c
+219.842 1.721 l
+219.89 2.198 219.917 2.506 219.917 2.646 c
+219.917 3.223 219.732 3.528 219.372 3.558 c
+219.096 3.564 218.872 3.44 218.696 3.175 c
+218.527 2.918 218.421 2.547 218.373 2.058 c
+218.332 1.565 218.314 1.264 218.314 1.147 c
+223.767 4.63 m
+223.68 4.116 l
+224.062 4.535 224.491 4.74 224.973 4.734 c
+225.491 4.711 225.829 4.469 225.987 3.998 c
+226.398 4.499 226.857 4.74 227.368 4.734 c
+227.758 4.723 228.052 4.576 228.251 4.293 c
+228.457 4.017 228.559 3.62 228.559 3.102 c
+228.53 2.646 l
+227.956 -0.837 l
+226.56 -0.837 l
+227.133 2.646 l
+227.163 2.926 l
+227.192 3.337 227.052 3.543 226.751 3.543 c
+226.516 3.543 226.3 3.373 226.105 3.043 c
+226.075 2.808 l
+225.443 -0.837 l
+224.046 -0.837 l
+224.62 2.646 l
+224.649 2.926 l
+224.679 3.337 224.546 3.543 224.252 3.543 c
+224.106 3.543 223.981 3.502 223.885 3.425 c
+223.786 3.344 223.697 3.252 223.62 3.146 c
+222.93 -0.837 l
+221.533 -0.837 l
+222.474 4.63 l
+h
+229.232 -1.469 m
+228.174 -1.469 l
+231.701 6.527 l
+232.76 6.527 l
+h
+f
+Q
+q
+457.854 554.164 m
+456.424 554.164 455.223 553.183 454.899 551.849 c
+408.477 551.849 l
+408.315 551.849 408.182 551.651 408.197 551.489 c
+408.211 551.327 408.373 551.191 408.535 551.191 c
+454.8 551.191 l
+454.8 551.129 l
+454.8 549.449 456.175 548.093 457.854 548.093 c
+459.489 548.093 460.798 549.376 460.871 550.989 c
+467.03 550.126 470.499 546.631 474.067 543.063 c
+477.584 539.549 481.196 535.97 487.366 534.997 c
+487.535 533.475 488.796 532.299 490.361 532.299 c
+491.838 532.299 493.062 533.339 493.338 534.735 c
+505.038 534.735 l
+505.313 533.339 506.537 532.299 508.014 532.299 c
+509.492 532.299 510.711 533.339 510.987 534.735 c
+523.687 534.735 l
+532.026 534.735 536.266 538.947 540.381 543.063 c
+544.067 546.748 547.654 550.338 554.199 551.051 c
+554.239 549.405 555.599 548.093 557.253 548.093 c
+558.936 548.093 560.288 549.449 560.288 551.129 c
+560.288 551.191 l
+586.724 551.191 l
+586.724 551.129 l
+586.724 549.449 588.081 548.093 589.76 548.093 c
+591.399 548.093 592.733 549.386 592.795 551.011 c
+599.329 550.305 604.44 547.512 608.828 543.562 c
+613.928 538.972 618.052 532.821 622.167 526.651 c
+626.283 520.477 630.399 514.285 635.588 509.618 c
+639.369 506.212 643.723 503.643 649.023 502.49 c
+625.261 502.49 l
+624.986 503.886 623.766 504.944 622.289 504.944 c
+620.812 504.944 619.569 503.886 619.293 502.49 c
+592.754 502.49 l
+592.479 503.886 591.237 504.944 589.76 504.944 c
+588.341 504.944 587.161 503.963 586.824 502.648 c
+580.466 503.434 576.916 506.983 573.286 510.615 c
+569.655 514.249 565.914 517.964 559.351 518.783 c
+559.263 520.385 557.94 521.657 556.316 521.657 c
+554.746 521.657 553.486 520.466 553.32 518.941 c
+543.535 518.941 l
+543.369 520.466 542.091 521.657 540.522 521.657 c
+538.952 521.657 537.692 520.466 537.526 518.941 c
+529.001 518.941 l
+534.035 520.283 537.247 523.477 540.381 526.611 c
+543.913 530.142 547.36 533.607 553.397 534.515 c
+553.758 533.237 554.92 532.299 556.316 532.299 c
+557.8 532.299 559.042 533.35 559.311 534.758 c
+586.787 534.758 l
+587.055 533.35 588.275 532.299 589.76 532.299 c
+591.443 532.299 592.817 533.655 592.817 535.334 c
+592.817 537.017 591.443 538.37 589.76 538.37 c
+588.098 538.37 586.757 537.047 586.724 535.397 c
+559.369 535.397 l
+559.336 537.047 557.977 538.37 556.316 538.37 c
+554.633 538.37 553.28 537.017 553.28 535.334 c
+553.28 535.275 553.295 535.217 553.299 535.155 c
+547.07 534.207 543.436 530.602 539.901 527.07 c
+535.917 523.083 532.066 519.18 524.448 518.941 c
+510.09 518.941 l
+510.069 519.136 510.028 519.32 509.969 519.5 c
+509.918 519.673 509.852 519.841 509.771 520 c
+509.683 520.169 509.587 520.331 509.47 520.481 c
+509.381 520.599 509.279 520.694 509.172 520.801 c
+509.146 520.823 509.136 520.856 509.113 520.878 c
+509.076 520.911 509.028 520.929 508.992 520.959 c
+508.889 521.044 508.787 521.127 508.672 521.198 c
+508.521 521.293 508.359 521.374 508.195 521.437 c
+507.849 521.572 507.467 521.657 507.073 521.657 c
+506.867 521.657 506.67 521.635 506.474 521.598 c
+506.273 521.557 506.082 521.495 505.898 521.418 c
+505.729 521.345 505.549 521.26 505.398 521.158 c
+505.391 521.154 505.383 521.143 505.376 521.139 c
+505.215 521.029 505.078 520.896 504.939 520.761 c
+504.814 520.635 504.7 520.503 504.6 520.36 c
+504.531 520.264 504.476 520.165 504.421 520.059 c
+504.384 519.992 504.332 519.93 504.299 519.86 c
+504.292 519.841 504.288 519.82 504.28 519.801 c
+504.203 519.625 504.16 519.452 504.12 519.261 c
+504.101 519.18 504.089 519.103 504.079 519.022 c
+504.068 518.923 504.042 518.824 504.039 518.721 c
+497.747 517.798 494.094 514.168 490.541 510.615 c
+490.008 510.081 489.483 509.56 488.946 509.038 c
+486.308 511.853 484.577 515.282 482.835 518.761 c
+480.777 522.877 478.69 527.044 475.026 530.182 c
+471.811 532.938 467.387 534.864 460.75 535.294 c
+460.368 536.54 459.225 537.451 457.854 537.451 c
+456.524 537.451 455.392 536.588 454.98 535.397 c
+375.929 535.397 l
+375.9 535.4 375.859 535.4 375.83 535.397 c
+375.657 535.371 375.524 535.188 375.551 535.015 c
+375.572 534.845 375.756 534.714 375.929 534.735 c
+454.841 534.735 l
+454.83 534.629 454.8 534.53 454.8 534.415 c
+454.8 532.736 456.175 531.362 457.854 531.362 c
+459.537 531.362 460.889 532.736 460.889 534.415 c
+460.889 534.493 460.875 534.563 460.871 534.636 c
+467.294 534.188 471.531 532.303 474.608 529.665 c
+478.146 526.633 480.178 522.579 482.236 518.463 c
+483.975 514.984 485.735 511.46 488.446 508.56 c
+485.543 505.815 482.354 503.452 477.484 502.728 c
+477.117 503.999 475.956 504.944 474.567 504.944 c
+473.089 504.944 471.869 503.886 471.594 502.49 c
+460.831 502.49 l
+460.555 503.886 459.331 504.944 457.854 504.944 c
+456.377 504.944 455.134 503.886 454.859 502.49 c
+375.551 502.49 l
+326.151 502.49 l
+325.974 502.482 325.823 502.306 325.831 502.129 c
+325.838 501.953 325.996 501.802 326.168 501.809 c
+454.8 501.809 l
+454.845 500.167 456.2 498.855 457.854 498.855 c
+459.508 498.855 460.848 500.167 460.889 501.809 c
+471.531 501.809 l
+471.576 500.167 472.913 498.855 474.567 498.855 c
+476.22 498.855 477.562 500.167 477.602 501.809 c
+504.039 501.809 l
+504.083 500.167 505.42 498.855 507.073 498.855 c
+508.727 498.855 510.087 500.167 510.131 501.809 c
+518.377 501.809 l
+513.343 500.465 510.127 497.275 506.996 494.144 c
+503.495 490.642 500.062 487.218 494.117 486.277 c
+493.672 487.409 492.569 488.213 491.279 488.213 c
+489.898 488.213 488.755 487.28 488.384 486.015 c
+460.75 486.015 l
+460.378 487.28 459.24 488.213 457.854 488.213 c
+456.472 488.213 455.311 487.28 454.94 486.015 c
+392.002 486.015 l
+391.819 486.038 391.642 485.884 391.642 485.695 c
+391.642 485.508 391.819 485.354 392.002 485.377 c
+454.818 485.377 l
+454.815 485.306 454.8 485.248 454.8 485.178 c
+454.8 483.495 456.175 482.121 457.854 482.121 c
+459.537 482.121 460.889 483.495 460.889 485.178 c
+460.889 485.248 460.875 485.306 460.871 485.377 c
+488.266 485.377 l
+488.262 485.306 488.245 485.248 488.245 485.178 c
+488.245 483.495 489.6 482.121 491.279 482.121 c
+492.962 482.121 494.337 483.495 494.337 485.178 c
+494.337 485.346 494.323 485.497 494.296 485.655 c
+500.382 486.655 503.968 490.198 507.455 493.685 c
+511.046 497.275 514.555 500.795 520.791 501.63 c
+520.924 500.072 522.214 498.855 523.808 498.855 c
+525.462 498.855 526.8 500.167 526.844 501.809 c
+537.486 501.809 l
+537.526 500.167 538.868 498.855 540.522 498.855 c
+542.175 498.855 543.535 500.167 543.575 501.809 c
+556.595 501.809 l
+564.713 501.809 568.715 497.797 572.826 493.685 c
+576.461 490.05 580.202 486.35 586.764 485.537 c
+586.75 485.413 586.724 485.302 586.724 485.178 c
+586.724 483.495 588.081 482.121 589.76 482.121 c
+591.443 482.121 592.817 483.495 592.817 485.178 c
+592.817 485.248 592.798 485.306 592.795 485.377 c
+603.459 485.377 l
+603.451 485.306 603.437 485.248 603.437 485.178 c
+603.437 483.495 604.812 482.121 606.495 482.121 c
+608.174 482.121 609.529 483.495 609.529 485.178 c
+609.529 485.248 609.512 485.306 609.508 485.377 c
+635.984 485.377 l
+635.981 485.306 635.966 485.248 635.966 485.178 c
+635.966 483.495 637.318 482.121 639.002 482.121 c
+640.681 482.121 642.037 483.495 642.037 485.178 c
+642.037 485.317 642.015 485.442 641.996 485.578 c
+648.35 486.474 652.021 490.113 655.593 493.685 c
+659.106 497.198 662.534 500.619 668.514 501.549 c
+668.682 500.031 669.943 498.855 671.508 498.855 c
+673.162 498.855 674.518 500.167 674.562 501.809 c
+704.732 501.809 l
+712.853 501.809 716.833 497.797 720.949 493.685 c
+724.502 490.127 728.159 486.512 734.464 485.597 c
+734.446 485.454 734.424 485.325 734.424 485.178 c
+734.424 483.495 735.798 482.121 737.481 482.121 c
+739.161 482.121 740.517 483.495 740.517 485.178 c
+740.517 485.248 740.498 485.306 740.494 485.377 c
+751.177 485.377 l
+751.174 485.306 751.159 485.248 751.159 485.178 c
+751.159 483.495 752.511 482.121 754.194 482.121 c
+755.874 482.121 757.248 483.495 757.248 485.178 c
+757.248 486.857 755.874 488.213 754.194 488.213 c
+752.809 488.213 751.666 487.28 751.299 486.015 c
+740.373 486.015 l
+740.006 487.28 738.863 488.213 737.481 488.213 c
+736.17 488.213 735.052 487.378 734.626 486.214 c
+728.471 487.077 724.995 490.572 721.427 494.144 c
+718.292 497.275 715.08 500.465 710.046 501.809 c
+734.424 501.809 l
+734.468 500.167 735.827 498.855 737.481 498.855 c
+739.161 498.855 740.517 500.207 740.517 501.89 c
+740.517 503.569 739.161 504.944 737.481 504.944 c
+736.004 504.944 734.762 503.886 734.487 502.49 c
+693.571 502.49 l
+698.606 503.835 701.818 507.002 704.952 510.137 c
+708.443 513.628 711.865 517.042 717.774 518.004 c
+718.057 516.616 719.277 515.568 720.746 515.568 c
+722.312 515.568 723.59 516.762 723.763 518.284 c
+734.464 518.284 l
+734.634 516.762 735.916 515.568 737.481 515.568 c
+739.161 515.568 740.517 516.942 740.517 518.621 c
+740.517 520.304 739.161 521.657 737.481 521.657 c
+735.912 521.657 734.63 520.466 734.464 518.941 c
+723.763 518.941 l
+723.598 520.466 722.316 521.657 720.746 521.657 c
+719.081 521.657 717.733 520.323 717.711 518.662 c
+711.593 517.67 707.991 514.112 704.493 510.615 c
+700.378 506.503 696.379 502.49 688.262 502.49 c
+674.504 502.49 l
+674.228 503.886 672.985 504.944 671.508 504.944 c
+670.031 504.944 668.807 503.886 668.532 502.49 c
+658.709 502.49 l
+658.698 502.545 658.683 502.596 658.669 502.648 c
+658.632 502.794 658.584 502.931 658.529 503.066 c
+658.47 503.206 658.404 503.342 658.327 503.467 c
+658.29 503.529 658.249 503.588 658.209 503.647 c
+658.11 503.791 657.993 503.922 657.871 504.047 c
+657.75 504.169 657.628 504.286 657.489 504.386 c
+657.47 504.4 657.449 504.411 657.43 504.427 c
+657.42 504.433 657.401 504.437 657.39 504.444 c
+657.235 504.547 657.081 504.632 656.913 504.705 c
+656.549 504.859 656.152 504.944 655.733 504.944 c
+655.516 504.944 655.317 504.907 655.115 504.863 c
+654.957 504.83 654.799 504.801 654.656 504.745 c
+654.549 504.705 654.453 504.654 654.355 504.606 c
+654.292 504.573 654.233 504.54 654.174 504.506 c
+654.087 504.452 653.998 504.408 653.917 504.346 c
+653.825 504.279 653.74 504.184 653.657 504.107 c
+653.627 504.077 653.586 504.059 653.557 504.026 c
+653.422 503.889 653.304 503.746 653.197 503.588 c
+653.094 503.43 652.992 503.261 652.917 503.089 c
+652.914 503.081 652.921 503.074 652.917 503.066 c
+652.855 502.919 652.797 502.769 652.756 502.607 c
+645.888 503.176 640.57 506.006 636.024 510.096 c
+630.925 514.686 626.819 520.838 622.707 527.007 c
+618.592 533.181 614.476 539.395 609.288 544.062 c
+604.8 548.1 599.491 550.989 592.754 551.687 c
+592.494 553.106 591.252 554.164 589.76 554.164 c
+588.326 554.164 587.15 553.183 586.824 551.849 c
+560.189 551.849 l
+559.865 553.183 558.685 554.164 557.253 554.164 c
+555.776 554.164 554.533 553.124 554.257 551.728 c
+547.434 551.018 543.612 547.23 539.901 543.522 c
+535.789 539.406 531.808 535.397 523.687 535.397 c
+511.05 535.397 l
+511.017 537.047 509.676 538.37 508.014 538.37 c
+506.354 538.37 505.012 537.047 504.979 535.397 c
+493.396 535.397 l
+493.367 537.047 492.022 538.37 490.361 538.37 c
+488.796 538.37 487.535 537.194 487.366 535.676 c
+481.45 536.628 478.021 540.027 474.527 543.522 c
+470.936 547.115 467.261 550.797 460.831 551.669 c
+460.577 553.095 459.354 554.164 457.854 554.164 c
+454.462 523.674 m
+454.462 513.672 l
+457.795 508.66 l
+461.128 513.672 l
+461.128 523.674 l
+h
+668.433 523.674 m
+668.433 513.672 l
+671.766 508.66 l
+675.102 513.672 l
+675.102 523.674 l
+h
+510.09 518.284 m
+537.526 518.284 l
+537.695 516.762 538.956 515.568 540.522 515.568 c
+542.087 515.568 543.365 516.762 543.535 518.284 c
+553.32 518.284 l
+553.49 516.762 554.75 515.568 556.316 515.568 c
+557.826 515.568 559.094 516.678 559.329 518.122 c
+565.664 517.324 569.206 513.76 572.826 510.137 c
+575.961 507.002 579.176 503.835 584.21 502.49 c
+576.023 502.49 l
+575.747 503.886 574.524 504.944 573.047 504.944 c
+571.57 504.944 570.328 503.886 570.052 502.49 c
+543.517 502.49 l
+543.241 503.886 541.998 504.944 540.522 504.944 c
+539.045 504.944 537.821 503.886 537.545 502.49 c
+526.781 502.49 l
+526.505 503.886 525.286 504.944 523.808 504.944 c
+522.331 504.944 521.107 503.886 520.832 502.49 c
+510.069 502.49 l
+509.793 503.886 508.551 504.944 507.073 504.944 c
+505.645 504.944 504.465 503.941 504.141 502.607 c
+497.751 503.059 493.529 504.911 490.464 507.542 c
+490.089 507.862 489.725 508.219 489.384 508.56 c
+489.931 509.086 490.468 509.603 491 510.137 c
+494.547 513.682 498.015 517.166 504.101 518.063 c
+504.13 517.909 504.167 517.751 504.218 517.603 c
+504.23 517.578 504.23 517.549 504.241 517.523 c
+504.292 517.391 504.369 517.269 504.439 517.144 c
+504.454 517.119 504.465 517.093 504.479 517.063 c
+504.534 516.968 504.593 516.876 504.66 516.784 c
+504.744 516.67 504.839 516.568 504.939 516.464 c
+505.045 516.358 505.155 516.259 505.277 516.167 c
+505.336 516.123 505.394 516.086 505.456 516.046 c
+505.541 515.994 505.629 515.951 505.718 515.906 c
+505.809 515.862 505.898 515.803 505.997 515.766 c
+506.148 515.708 506.313 515.66 506.474 515.627 c
+506.673 515.587 506.867 515.568 507.073 515.568 c
+507.467 515.568 507.831 515.652 508.172 515.789 c
+508.349 515.855 508.514 515.932 508.672 516.028 c
+508.787 516.097 508.889 516.182 508.992 516.266 c
+509.028 516.296 509.076 516.314 509.113 516.347 c
+509.153 516.383 509.194 516.428 509.231 516.464 c
+509.452 516.689 509.624 516.946 509.771 517.225 c
+509.852 517.383 509.918 517.553 509.969 517.725 c
+510.025 517.905 510.069 518.093 510.09 518.284 c
+488.906 508.101 m
+489.266 507.737 489.644 507.38 490.041 507.043 c
+492.482 504.951 495.619 503.342 499.886 502.49 c
+479.62 502.49 l
+483.551 503.537 486.367 505.705 488.906 508.101 c
+750.717 505.565 m
+750.717 495.559 l
+754.054 490.569 l
+757.387 495.559 l
+757.387 505.565 l
+h
+561.905 501.809 m
+569.993 501.809 l
+570.033 500.167 571.393 498.855 573.047 498.855 c
+574.7 498.855 576.042 500.167 576.082 501.809 c
+586.724 501.809 l
+586.768 500.167 588.106 498.855 589.76 498.855 c
+591.414 498.855 592.773 500.167 592.817 501.809 c
+619.231 501.809 l
+619.276 500.167 620.635 498.855 622.289 498.855 c
+623.943 498.855 625.28 500.167 625.324 501.809 c
+652.679 501.809 l
+652.679 501.736 652.69 501.663 652.697 501.589 c
+652.712 501.46 652.73 501.335 652.756 501.211 c
+652.797 501.034 652.848 500.872 652.917 500.711 c
+652.955 500.623 653.009 500.538 653.057 500.454 c
+653.197 500.193 653.37 499.939 653.576 499.733 c
+653.612 499.697 653.657 499.667 653.697 499.634 c
+653.946 499.41 654.222 499.226 654.534 499.094 c
+654.542 499.091 654.549 499.097 654.557 499.094 c
+654.733 499.02 654.92 498.954 655.115 498.914 c
+655.119 498.914 655.126 498.918 655.134 498.914 c
+655.325 498.877 655.531 498.855 655.733 498.855 c
+655.942 498.855 656.133 498.873 656.331 498.914 c
+656.633 498.977 656.909 499.094 657.169 499.234 c
+657.258 499.282 657.349 499.318 657.43 499.373 c
+657.581 499.476 657.721 499.586 657.85 499.715 c
+657.867 499.733 l
+657.894 499.756 657.908 499.789 657.93 499.814 c
+658.041 499.932 658.158 500.057 658.249 500.193 c
+658.349 500.34 658.438 500.49 658.507 500.652 c
+658.518 500.67 658.522 500.693 658.529 500.711 c
+658.596 500.872 658.65 501.038 658.687 501.211 c
+658.717 501.335 658.735 501.46 658.75 501.589 c
+658.754 501.663 658.764 501.736 658.768 501.809 c
+666.496 501.809 l
+661.461 500.465 658.247 497.275 655.115 494.144 c
+651.533 490.561 648.049 487.062 641.857 486.214 c
+641.427 487.378 640.313 488.213 639.002 488.213 c
+637.616 488.213 636.473 487.28 636.105 486.015 c
+609.386 486.015 l
+609.019 487.28 607.877 488.213 606.495 488.213 c
+605.109 488.213 603.948 487.28 603.576 486.015 c
+592.673 486.015 l
+592.306 487.28 591.145 488.213 589.76 488.213 c
+588.433 488.213 587.323 487.361 586.905 486.177 c
+580.495 486.942 576.931 490.499 573.286 494.144 c
+570.155 497.275 566.939 500.465 561.905 501.809 c
+540.481 495.761 m
+537.144 490.767 l
+537.144 480.746 l
+543.814 480.746 l
+543.814 490.767 l
+h
+W n
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 g
+/GS1 gs
+0 TL/Fm0 Do
+Q
+Q
+q 1 0 0 1 175.2352 546.0975 cm
+0 0 m
+-3.391 0 l
+-3.381 -25.219 l
+10.33 -25.219 l
+10.33 -22.1 l
+0.008 -22.1 l
+h
+f
+Q
+q 1 0 0 1 198.6173 525.3062 cm
+0 0 m
+-0.845 -0.886 -2.271 -1.771 -4.196 -1.771 c
+-6.776 -1.771 -7.816 -0.5 -7.816 1.153 c
+-7.816 3.657 -6.081 4.85 -2.385 4.85 c
+-1.693 4.85 -0.577 4.773 0 4.659 c
+h
+-3.501 15.283 m
+-6.239 15.283 -8.749 14.313 -10.708 12.7 c
+-9.51 10.628 l
+-8.124 11.436 -6.43 12.244 -4.005 12.244 c
+-1.23 12.244 0 10.819 0 8.43 c
+0 7.199 l
+-0.536 7.316 -1.653 7.393 -2.348 7.393 c
+-8.279 7.393 -11.281 5.313 -11.281 0.962 c
+-11.281 -2.926 -8.893 -4.891 -5.273 -4.891 c
+-2.836 -4.891 -0.5 -3.774 0.31 -1.966 c
+0.927 -4.428 l
+3.315 -4.428 l
+3.315 8.47 l
+3.315 12.553 1.544 15.283 -3.501 15.283 c
+f
+Q
+q 1 0 0 1 212.8648 523.4172 cm
+0 0 m
+-1.272 0 -2.389 0.154 -3.233 0.54 c
+-3.233 12.208 l
+-2.08 13.17 -0.654 13.861 1.158 13.861 c
+4.428 13.861 5.7 11.553 5.7 7.817 c
+5.7 2.502 3.66 0 0 0 c
+1.426 17.172 m
+-1.602 17.172 -3.233 15.114 y
+-3.233 18.366 l
+-3.245 22.68 l
+-6.556 22.68 l
+-6.545 -1.922 l
+-4.891 -2.616 -2.616 -3.002 -0.154 -3.002 c
+6.163 -3.002 9.205 1.039 9.205 8.011 c
+9.205 13.516 6.394 17.172 1.426 17.172 c
+f
+Q
+q 1 0 0 1 138.3959 543.3637 cm
+0 0 m
+3.002 0 4.928 -1.003 6.199 -2.003 c
+7.655 0.518 l
+5.67 2.26 3.002 3.194 0.154 3.194 c
+-7.048 3.194 -12.094 -1.194 -12.094 -10.05 c
+-12.094 -19.329 -6.651 -22.948 -0.422 -22.948 c
+2.697 -22.948 5.354 -22.217 7.085 -21.487 c
+7.015 -11.572 l
+7.015 -8.452 l
+-2.227 -8.452 l
+-2.227 -11.572 l
+3.663 -11.572 l
+3.734 -19.098 l
+2.965 -19.484 1.617 -19.792 -0.191 -19.792 c
+-5.199 -19.792 -8.548 -16.643 -8.548 -10.009 c
+-8.548 -3.274 -5.082 0 0 0 c
+f
+Q
+q 1 0 0 1 162.2267 546.0975 cm
+0 0 m
+-3.311 0 l
+-3.3 -4.237 l
+-3.3 -18.866 l
+-3.3 -22.948 -1.529 -25.682 3.516 -25.682 c
+4.215 -25.682 4.895 -25.62 5.556 -25.499 c
+5.556 -22.526 l
+5.078 -22.599 4.567 -22.64 4.016 -22.64 c
+1.246 -22.64 0.011 -21.214 0.011 -18.829 c
+0.011 -8.742 l
+5.556 -8.742 l
+5.556 -5.967 l
+0.011 -5.967 l
+h
+f
+Q
+150.512 540.13 3.314 -19.252 re
+f
+150.512 546.098 3.314 -3.311 re
+f
+0 0.672 0.855 0 k
+q 1 0 0 1 111.978 528.4332 cm
+0 0 m
+-4.278 13.163 l
+-12.755 39.254 l
+-13.193 40.595 -15.092 40.595 -15.53 39.254 c
+-24.008 13.163 l
+-52.159 13.163 l
+-60.641 39.254 l
+-61.075 40.595 -62.975 40.595 -63.411 39.254 c
+-71.889 13.163 l
+-76.167 0 l
+-76.56 -1.201 -76.13 -2.514 -75.109 -3.256 c
+-38.086 -30.152 l
+-1.058 -3.256 l
+-0.037 -2.514 0.389 -1.201 0 0 c
+f
+Q
+0.031 0.844 0.875 0.004 k
+q 1 0 0 1 73.8922 498.2816 cm
+0 0 m
+14.078 43.314 l
+-14.073 43.314 l
+h
+f
+Q
+0 0.672 0.855 0 k
+q 1 0 0 1 73.8922 498.2816 cm
+0 0 m
+-14.073 43.314 l
+-33.804 43.314 l
+h
+f
+Q
+0 0.43 0.871 0 k
+q 1 0 0 1 40.0886 541.5961 cm
+0 0 m
+-4.278 -13.163 l
+-4.671 -14.364 -4.241 -15.68 -3.219 -16.419 c
+33.804 -43.314 l
+h
+f
+Q
+0.031 0.844 0.875 0.004 k
+q 1 0 0 1 40.0886 541.5961 cm
+0 0 m
+19.73 0 l
+11.248 26.091 l
+10.815 27.432 8.914 27.432 8.478 26.091 c
+h
+f
+Q
+0 0.672 0.855 0 k
+q 1 0 0 1 73.8922 498.2816 cm
+0 0 m
+14.078 43.314 l
+33.808 43.314 l
+h
+f
+Q
+0 0.43 0.871 0 k
+q 1 0 0 1 107.7005 541.5961 cm
+0 0 m
+4.278 -13.163 l
+4.667 -14.364 4.241 -15.68 3.219 -16.419 c
+-33.808 -43.314 l
+h
+f
+Q
+0.031 0.844 0.875 0.004 k
+q 1 0 0 1 107.7005 541.5961 cm
+0 0 m
+-19.73 0 l
+-11.252 26.091 l
+-10.815 27.432 -8.915 27.432 -8.478 26.091 c
+h
+f
+Q
+0 0 0 0 k
+q 1 0 0 1 142.633 472.606 cm
+0 0 m
+-0.559 -0.706 l
+-2.168 -2.579 -4.513 -3.513 -7.599 -3.513 c
+-10.356 -3.513 -12.517 -2.601 -14.082 -0.779 c
+-15.64 1.044 -16.441 3.62 -16.478 6.953 c
+-16.478 11.936 l
+-16.478 15.501 -15.769 18.172 -14.346 19.947 c
+-12.917 21.718 -10.782 22.608 -7.938 22.608 c
+-5.519 22.608 -3.627 21.931 -2.263 20.579 c
+-0.893 19.227 -0.14 17.301 0 14.803 c
+-3.19 14.803 l
+-3.319 16.378 -3.759 17.613 -4.513 18.506 c
+-5.259 19.395 -6.394 19.844 -7.923 19.844 c
+-9.746 19.844 -11.083 19.245 -11.936 18.051 c
+-12.788 16.864 -13.23 14.964 -13.259 12.347 c
+-13.259 7.159 l
+-13.259 4.601 -12.773 2.635 -11.803 1.264 c
+-10.826 -0.099 -9.419 -0.779 -7.585 -0.779 c
+-5.872 -0.779 -4.564 -0.363 -3.66 0.47 c
+-3.19 0.912 l
+-3.19 6.776 l
+-7.834 6.776 l
+-7.834 9.496 l
+0 9.496 l
+h
+8.712 -3.16 -3.189 25.415 re
+28.737 19.491 m
+22.048 19.491 l
+22.048 -3.16 l
+18.859 -3.16 l
+18.859 19.491 l
+12.185 19.491 l
+12.185 22.255 l
+28.737 22.255 l
+h
+56.088 4.91 m
+55.941 2.183 55.169 0.096 53.78 -1.352 c
+52.398 -2.792 50.436 -3.513 47.901 -3.513 c
+45.35 -3.513 43.322 -2.543 41.815 -0.602 c
+40.315 1.338 39.566 3.961 39.566 7.277 c
+39.566 11.892 l
+39.566 15.192 40.334 17.804 41.873 19.726 c
+43.421 21.645 45.526 22.608 48.194 22.608 c
+50.642 22.608 52.537 21.865 53.883 20.388 c
+55.236 18.918 55.97 16.816 56.088 14.082 c
+52.854 14.082 l
+52.714 16.158 52.281 17.639 51.546 18.521 c
+50.81 19.403 49.694 19.844 48.194 19.844 c
+46.46 19.844 45.126 19.16 44.196 17.801 c
+43.263 16.449 42.8 14.464 42.8 11.848 c
+42.8 7.188 l
+42.8 4.619 43.23 2.65 44.093 1.279 c
+44.965 -0.095 46.232 -0.779 47.901 -0.779 c
+49.565 -0.779 50.759 -0.367 51.487 0.456 c
+52.221 1.279 52.678 2.764 52.854 4.91 c
+h
+77.144 -3.16 m
+73.939 -3.16 l
+73.939 8.584 l
+63.709 8.584 l
+63.709 -3.16 l
+60.505 -3.16 l
+60.505 22.255 l
+63.709 22.255 l
+63.709 11.333 l
+73.939 11.333 l
+73.939 22.255 l
+77.144 22.255 l
+h
+95.025 8.584 m
+86.045 8.584 l
+86.045 -0.426 l
+96.51 -0.426 l
+96.51 -3.16 l
+82.84 -3.16 l
+82.84 22.255 l
+96.334 22.255 l
+96.334 19.491 l
+86.045 19.491 l
+86.045 11.333 l
+95.025 11.333 l
+h
+112.54 3.469 m
+103.955 3.469 l
+101.971 -3.16 l
+98.693 -3.16 l
+106.88 22.255 l
+109.614 22.255 l
+117.831 -3.16 l
+114.539 -3.16 l
+h
+104.793 6.232 m
+111.717 6.232 l
+108.247 17.787 l
+h
+134.007 19.491 m
+127.32 19.491 l
+127.32 -3.16 l
+124.13 -3.16 l
+124.13 19.491 l
+117.456 19.491 l
+117.456 22.255 l
+134.007 22.255 l
+h
+156.527 3.263 m
+156.527 4.528 156.181 5.498 155.498 6.174 c
+154.81 6.85 153.572 7.504 151.779 8.144 c
+149.985 8.79 148.611 9.467 147.663 10.172 c
+146.711 10.878 146.002 11.678 145.531 12.582 c
+145.061 13.483 144.826 14.512 144.826 15.67 c
+144.826 17.687 145.495 19.348 146.84 20.652 c
+148.181 21.953 149.942 22.608 152.117 22.608 c
+153.605 22.608 154.931 22.269 156.1 21.593 c
+157.265 20.924 158.162 19.998 158.79 18.815 c
+159.415 17.629 159.731 16.324 159.731 14.905 c
+156.527 14.905 l
+156.527 16.47 156.144 17.687 155.38 18.55 c
+154.623 19.41 153.535 19.844 152.117 19.844 c
+150.831 19.844 149.831 19.48 149.118 18.757 c
+148.412 18.04 148.06 17.029 148.06 15.728 c
+148.06 14.659 148.442 13.762 149.206 13.039 c
+149.978 12.311 151.165 11.664 152.764 11.098 c
+155.27 10.275 157.059 9.253 158.129 8.041 c
+159.205 6.835 159.745 5.251 159.745 3.293 c
+159.745 1.235 159.073 -0.419 157.732 -1.66 c
+156.398 -2.896 154.579 -3.513 152.278 -3.513 c
+150.798 -3.513 149.431 -3.189 148.177 -2.543 c
+146.931 -1.896 145.95 -0.992 145.238 0.177 c
+144.521 1.353 144.165 2.694 144.165 4.204 c
+147.369 4.204 l
+147.369 2.635 147.81 1.415 148.691 0.544 c
+149.574 -0.33 150.768 -0.764 152.278 -0.764 c
+153.69 -0.764 154.748 -0.407 155.453 0.309 c
+156.167 1.033 156.527 2.018 156.527 3.263 c
+180.662 -3.16 m
+177.458 -3.16 l
+177.458 8.584 l
+167.227 8.584 l
+167.227 -3.16 l
+164.023 -3.16 l
+164.023 22.255 l
+167.227 22.255 l
+167.227 11.333 l
+177.458 11.333 l
+177.458 22.255 l
+180.662 22.255 l
+h
+198.544 8.584 m
+189.563 8.584 l
+189.563 -0.426 l
+200.028 -0.426 l
+200.028 -3.16 l
+186.358 -3.16 l
+186.358 22.255 l
+199.852 22.255 l
+199.852 19.491 l
+189.563 19.491 l
+189.563 11.333 l
+198.544 11.333 l
+h
+216.38 8.584 m
+207.4 8.584 l
+207.4 -0.426 l
+217.866 -0.426 l
+217.866 -3.16 l
+204.195 -3.16 l
+204.195 22.255 l
+217.689 22.255 l
+217.689 19.491 l
+207.4 19.491 l
+207.4 11.333 l
+216.38 11.333 l
+h
+237.246 19.491 m
+230.558 19.491 l
+230.558 -3.16 l
+227.368 -3.16 l
+227.368 19.491 l
+220.695 19.491 l
+220.695 22.255 l
+237.246 22.255 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+535.667 442.028 238.665 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 655.8467 428.4505 cm
+0 0 m
+-2.573 0 l
+-3.072 -1.911 l
+-5.072 -1.911 l
+-2.147 7.453 l
+-0.426 7.453 l
+2.528 -1.911 l
+0.5 -1.911 l
+h
+-2.161 1.587 m
+-0.412 1.587 l
+-1.294 4.924 l
+h
+3.686 -0.985 m
+3.686 -0.691 3.781 -0.453 3.979 -0.264 c
+4.174 -0.081 4.428 0.014 4.744 0.014 c
+5.045 0.014 5.295 -0.081 5.494 -0.264 c
+5.699 -0.453 5.802 -0.691 5.802 -0.985 c
+5.802 -1.29 5.699 -1.536 5.494 -1.72 c
+5.295 -1.897 5.045 -1.985 4.744 -1.985 c
+4.438 -1.985 4.185 -1.893 3.979 -1.706 c
+3.781 -1.521 3.686 -1.279 3.686 -0.985 c
+17.055 -0.852 m
+16.691 -1.246 16.246 -1.544 15.717 -1.75 c
+15.187 -1.945 14.607 -2.043 13.982 -2.043 c
+12.902 -2.043 12.064 -1.713 11.469 -1.044 c
+10.869 -0.379 10.565 0.592 10.557 1.866 c
+10.557 3.557 l
+10.557 4.85 10.836 5.843 11.395 6.541 c
+11.962 7.235 12.785 7.584 13.865 7.584 c
+14.883 7.584 15.647 7.327 16.158 6.82 c
+16.676 6.321 16.974 5.534 17.055 4.469 c
+15.217 4.469 l
+15.166 5.064 15.044 5.471 14.85 5.689 c
+14.651 5.901 14.343 6.011 13.924 6.011 c
+13.413 6.011 13.041 5.824 12.806 5.453 c
+12.579 5.078 12.461 4.486 12.453 3.675 c
+12.453 1.97 l
+12.453 1.117 12.579 0.492 12.835 0.103 c
+13.089 -0.279 13.504 -0.47 14.085 -0.47 c
+14.456 -0.47 14.761 -0.397 14.996 -0.25 c
+15.158 -0.133 l
+15.158 1.587 l
+13.836 1.587 l
+13.836 3.013 l
+17.055 3.013 l
+h
+20.612 -1.911 -1.897 9.363 re
+28.192 5.88 m
+25.87 5.88 l
+25.87 -1.911 l
+23.974 -1.911 l
+23.974 5.88 l
+21.695 5.88 l
+21.695 7.453 l
+28.192 7.453 l
+h
+34.187 -1.911 -1.897 9.363 re
+42.436 -1.911 m
+40.54 -1.911 l
+37.776 4.233 l
+37.776 -1.911 l
+35.88 -1.911 l
+35.88 7.453 l
+37.776 7.453 l
+40.54 1.308 l
+40.54 7.453 l
+42.436 7.453 l
+h
+48.143 0.544 m
+48.143 0.926 48.044 1.213 47.849 1.411 c
+47.65 1.606 47.298 1.808 46.79 2.013 c
+45.85 2.373 45.173 2.793 44.762 3.263 c
+44.35 3.74 44.145 4.31 44.145 4.968 c
+44.145 5.751 44.424 6.383 44.982 6.865 c
+45.541 7.342 46.25 7.584 47.114 7.584 c
+47.691 7.584 48.205 7.459 48.657 7.217 c
+49.106 6.971 49.451 6.629 49.686 6.188 c
+49.929 5.747 50.054 5.247 50.054 4.689 c
+48.172 4.689 l
+48.172 5.13 48.076 5.46 47.893 5.689 c
+47.706 5.913 47.438 6.026 47.085 6.026 c
+46.75 6.026 46.49 5.928 46.305 5.733 c
+46.129 5.545 46.041 5.284 46.041 4.953 c
+46.041 4.696 46.144 4.461 46.349 4.247 c
+46.555 4.042 46.916 3.825 47.438 3.601 c
+48.348 3.278 49.01 2.873 49.422 2.396 c
+49.84 1.914 50.054 1.301 50.054 0.559 c
+50.054 -0.258 49.792 -0.893 49.275 -1.353 c
+48.753 -1.816 48.047 -2.043 47.158 -2.043 c
+46.548 -2.043 45.997 -1.918 45.497 -1.675 c
+45.005 -1.422 44.618 -1.066 44.336 -0.603 c
+44.049 -0.133 43.91 0.415 43.91 1.043 c
+45.806 1.043 l
+45.806 0.503 45.909 0.11 46.114 -0.133 c
+46.328 -0.379 46.677 -0.5 47.158 -0.5 c
+47.812 -0.5 48.143 -0.154 48.143 0.544 c
+57.587 5.88 m
+55.265 5.88 l
+55.265 -1.911 l
+53.368 -1.911 l
+53.368 5.88 l
+51.09 5.88 l
+51.09 7.453 l
+57.587 7.453 l
+h
+62.548 0 m
+59.976 0 l
+59.475 -1.911 l
+57.476 -1.911 l
+60.402 7.453 l
+62.122 7.453 l
+65.077 -1.911 l
+63.047 -1.911 l
+h
+60.387 1.587 m
+62.137 1.587 l
+61.254 4.924 l
+h
+68.071 -0.339 m
+71.393 -0.339 l
+71.393 -1.911 l
+66.175 -1.911 l
+66.175 7.453 l
+68.071 7.453 l
+h
+74.554 -0.339 m
+77.875 -0.339 l
+77.875 -1.911 l
+72.657 -1.911 l
+72.657 7.453 l
+74.554 7.453 l
+h
+83.74 0 m
+81.168 0 l
+80.668 -1.911 l
+78.669 -1.911 l
+81.594 7.453 l
+83.314 7.453 l
+86.269 -1.911 l
+84.24 -1.911 l
+h
+81.579 1.587 m
+83.329 1.587 l
+82.446 4.924 l
+h
+92.644 5.88 m
+90.321 5.88 l
+90.321 -1.911 l
+88.426 -1.911 l
+88.426 5.88 l
+86.147 5.88 l
+86.147 7.453 l
+92.644 7.453 l
+h
+95.915 -1.911 -1.897 9.363 re
+104.205 1.926 m
+104.205 0.669 103.904 -0.305 103.308 -1 c
+102.709 -1.698 101.886 -2.043 100.839 -2.043 c
+99.788 -2.043 98.961 -1.702 98.355 -1.014 c
+97.756 -0.32 97.451 0.646 97.444 1.881 c
+97.444 3.484 l
+97.444 4.766 97.741 5.77 98.34 6.497 c
+98.936 7.221 99.766 7.584 100.825 7.584 c
+101.86 7.584 102.68 7.224 103.279 6.512 c
+103.885 5.806 104.194 4.81 104.205 3.528 c
+h
+102.309 3.499 m
+102.309 4.34 102.184 4.968 101.941 5.38 c
+101.706 5.791 101.332 5.997 100.825 5.997 c
+100.324 5.997 99.95 5.795 99.707 5.394 c
+99.472 5.001 99.347 4.402 99.339 3.601 c
+99.339 1.926 l
+99.339 1.109 99.461 0.507 99.707 0.118 c
+99.95 -0.276 100.328 -0.47 100.839 -0.47 c
+101.328 -0.47 101.691 -0.279 101.926 0.103 c
+102.169 0.484 102.298 1.072 102.309 1.866 c
+h
+112.337 -1.911 m
+110.441 -1.911 l
+107.678 4.233 l
+107.678 -1.911 l
+105.781 -1.911 l
+105.781 7.453 l
+107.678 7.453 l
+110.441 1.308 l
+110.441 7.453 l
+112.337 7.453 l
+h
+f
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 539.3305 407.9565 cm
+0 0 m
+-2.263 0 l
+-2.263 -2.955 l
+-3.102 -2.955 l
+-3.102 3.733 l
+0.383 3.733 l
+0.383 3.013 l
+-2.263 3.013 l
+-2.263 0.72 l
+0 0.72 l
+h
+0.871 -0.235 m
+0.871 0.477 1.04 1.047 1.386 1.469 c
+1.739 1.888 2.201 2.102 2.782 2.102 c
+3.359 2.102 3.818 1.896 4.164 1.484 c
+4.517 1.08 4.696 0.521 4.708 -0.191 c
+4.708 -0.706 l
+4.708 -1.434 4.532 -2.007 4.179 -2.425 c
+3.833 -2.837 3.373 -3.043 2.797 -3.043 c
+2.216 -3.043 1.754 -2.845 1.401 -2.44 c
+1.055 -2.029 0.879 -1.478 0.871 -0.779 c
+h
+1.679 -0.706 m
+1.679 -1.216 1.775 -1.617 1.974 -1.912 c
+2.18 -2.205 2.452 -2.352 2.797 -2.352 c
+3.51 -2.352 3.877 -1.834 3.899 -0.794 c
+3.899 -0.235 l
+3.899 0.272 3.796 0.675 3.591 0.97 c
+3.392 1.263 3.124 1.411 2.782 1.411 c
+2.448 1.411 2.18 1.263 1.974 0.97 c
+1.775 0.675 1.679 0.272 1.679 -0.235 c
+h
+7.622 1.249 m
+7.504 1.267 7.379 1.278 7.254 1.278 c
+6.832 1.278 6.541 1.051 6.387 0.602 c
+6.387 -2.955 l
+5.564 -2.955 l
+5.564 2.013 l
+6.358 2.013 l
+6.373 1.514 l
+6.585 1.903 6.894 2.102 7.298 2.102 c
+7.423 2.102 7.533 2.08 7.622 2.043 c
+h
+14.93 -2.117 m
+14.784 -2.308 l
+14.361 -2.801 13.744 -3.043 12.931 -3.043 c
+12.204 -3.043 11.638 -2.804 11.227 -2.323 c
+10.815 -1.845 10.602 -1.169 10.595 -0.294 c
+10.595 1.014 l
+10.595 1.955 10.778 2.653 11.153 3.116 c
+11.524 3.586 12.087 3.821 12.844 3.821 c
+13.479 3.821 13.976 3.645 14.328 3.293 c
+14.689 2.94 14.89 2.433 14.93 1.778 c
+14.093 1.778 l
+14.053 2.19 13.935 2.514 13.74 2.749 c
+13.542 2.984 13.244 3.102 12.844 3.102 c
+12.362 3.102 12.01 2.944 11.786 2.631 c
+11.557 2.315 11.439 1.815 11.433 1.132 c
+11.433 -0.235 l
+11.433 -0.912 11.557 -1.434 11.815 -1.794 c
+12.079 -2.147 12.451 -2.323 12.931 -2.323 c
+13.38 -2.323 13.725 -2.216 13.961 -1.999 c
+14.093 -1.881 l
+14.093 -0.339 l
+12.858 -0.339 l
+12.858 0.382 l
+14.93 0.382 l
+h
+20.454 -2.955 m
+19.616 -2.955 l
+16.941 2.204 l
+16.941 -2.955 l
+16.089 -2.955 l
+16.089 3.733 l
+16.941 3.733 l
+19.616 -1.441 l
+19.616 3.733 l
+20.454 3.733 l
+h
+25.661 3.733 m
+25.661 -1.014 l
+25.661 -1.654 25.477 -2.153 25.118 -2.514 c
+24.754 -2.866 24.25 -3.043 23.603 -3.043 c
+22.946 -3.043 22.442 -2.874 22.09 -2.529 c
+21.737 -2.176 21.56 -1.673 21.56 -1.014 c
+21.56 3.733 l
+22.398 3.733 l
+22.398 -0.985 l
+22.398 -1.437 22.49 -1.775 22.677 -1.999 c
+22.872 -2.216 23.181 -2.323 23.603 -2.323 c
+24.033 -2.323 24.342 -2.216 24.529 -1.999 c
+24.725 -1.775 24.823 -1.437 24.823 -0.985 c
+24.823 3.733 l
+h
+26.878 -3.528 m
+26.186 -3.528 l
+28.524 3.733 l
+29.215 3.733 l
+h
+30.964 -2.234 m
+33.581 -2.234 l
+33.581 -2.955 l
+30.111 -2.955 l
+30.111 3.733 l
+30.964 3.733 l
+h
+35.16 -2.955 -0.808 4.968 re
+35.219 3.337 m
+35.219 3.197 35.179 3.079 35.102 2.984 c
+35.032 2.896 34.917 2.851 34.763 2.851 c
+34.605 2.851 34.488 2.896 34.41 2.984 c
+34.341 3.079 34.308 3.197 34.308 3.337 c
+34.308 3.472 34.341 3.586 34.41 3.675 c
+34.488 3.77 34.602 3.821 34.749 3.821 c
+34.903 3.821 35.021 3.77 35.102 3.675 c
+35.179 3.586 35.219 3.472 35.219 3.337 c
+37.108 2.013 m
+37.138 1.484 l
+37.45 1.896 37.851 2.102 38.343 2.102 c
+39.225 2.102 39.669 1.517 39.68 0.353 c
+39.68 -2.955 l
+38.872 -2.955 l
+38.872 0.309 l
+38.872 0.698 38.805 0.974 38.68 1.132 c
+38.552 1.297 38.358 1.381 38.092 1.381 c
+37.887 1.381 37.703 1.311 37.549 1.176 c
+37.391 1.047 37.263 0.874 37.167 0.661 c
+37.167 -2.955 l
+36.344 -2.955 l
+36.344 2.013 l
+h
+43.26 -2.514 m
+42.984 -2.866 42.587 -3.043 42.069 -3.043 c
+41.628 -3.043 41.29 -2.893 41.055 -2.587 c
+40.827 -2.275 40.71 -1.819 40.702 -1.22 c
+40.702 2.013 l
+41.51 2.013 l
+41.51 -1.162 l
+41.51 -1.937 41.745 -2.323 42.216 -2.323 c
+42.705 -2.323 43.043 -2.103 43.23 -1.661 c
+43.23 2.013 l
+44.053 2.013 l
+44.053 -2.955 l
+43.274 -2.955 l
+h
+46.53 0.205 m
+47.382 2.013 l
+48.323 2.013 l
+46.971 -0.441 l
+48.353 -2.955 l
+47.426 -2.955 l
+46.545 -1.088 l
+45.663 -2.955 l
+44.722 -2.955 l
+46.104 -0.441 l
+44.766 2.013 l
+45.693 2.013 l
+h
+51.208 -0.25 m
+51.208 0.521 51.347 1.105 51.634 1.499 c
+51.918 1.899 52.329 2.102 52.87 2.102 c
+53.34 2.102 53.707 1.911 53.971 1.529 c
+53.971 4.101 l
+54.794 4.101 l
+54.794 -2.955 l
+54.045 -2.955 l
+54.001 -2.425 l
+53.736 -2.837 53.358 -3.043 52.87 -3.043 c
+52.347 -3.043 51.943 -2.849 51.649 -2.455 c
+51.355 -2.055 51.208 -1.484 51.208 -0.75 c
+h
+52.016 -0.706 m
+52.016 -1.264 52.097 -1.676 52.267 -1.941 c
+52.432 -2.198 52.7 -2.323 53.075 -2.323 c
+53.475 -2.323 53.773 -2.124 53.971 -1.72 c
+53.971 0.793 l
+53.766 1.182 53.464 1.381 53.075 1.381 c
+52.7 1.381 52.432 1.249 52.267 0.985 c
+52.097 0.727 52.016 0.33 52.016 -0.206 c
+h
+56.731 -2.955 -0.808 4.968 re
+56.79 3.337 m
+56.79 3.197 56.749 3.079 56.672 2.984 c
+56.602 2.896 56.489 2.851 56.334 2.851 c
+56.176 2.851 56.059 2.896 55.982 2.984 c
+55.912 3.079 55.879 3.197 55.879 3.337 c
+55.879 3.472 55.912 3.586 55.982 3.675 c
+56.059 3.77 56.172 3.821 56.319 3.821 c
+56.474 3.821 56.592 3.77 56.672 3.675 c
+56.749 3.586 56.79 3.472 56.79 3.337 c
+60.34 -1.676 m
+60.34 -1.511 60.27 -1.364 60.134 -1.235 c
+60.005 -1.11 59.752 -0.963 59.37 -0.794 c
+58.936 -0.611 58.628 -0.452 58.443 -0.324 c
+58.268 -0.198 58.135 -0.055 58.046 0.103 c
+57.959 0.268 57.915 0.463 57.915 0.691 c
+57.915 1.103 58.061 1.44 58.356 1.705 c
+58.649 1.969 59.024 2.102 59.488 2.102 c
+59.976 2.102 60.369 1.959 60.664 1.675 c
+60.957 1.4 61.104 1.043 61.104 0.602 c
+60.296 0.602 l
+60.296 0.827 60.215 1.018 60.061 1.176 c
+59.914 1.33 59.723 1.411 59.488 1.411 c
+59.253 1.411 59.064 1.344 58.929 1.22 c
+58.789 1.103 58.723 0.933 58.723 0.72 c
+58.723 0.551 58.771 0.419 58.87 0.323 c
+58.966 0.224 59.208 0.091 59.59 -0.073 c
+60.197 -0.309 60.608 -0.541 60.825 -0.765 c
+61.038 -0.992 61.148 -1.276 61.148 -1.617 c
+61.148 -2.051 60.994 -2.396 60.693 -2.66 c
+60.398 -2.918 60.002 -3.043 59.502 -3.043 c
+58.991 -3.043 58.576 -2.896 58.252 -2.602 c
+57.929 -2.308 57.768 -1.933 57.768 -1.47 c
+58.591 -1.47 l
+58.598 -1.746 58.682 -1.962 58.84 -2.117 c
+58.995 -2.275 59.216 -2.352 59.502 -2.352 c
+59.774 -2.352 59.98 -2.293 60.12 -2.176 c
+60.267 -2.051 60.34 -1.881 60.34 -1.676 c
+63.096 3.219 m
+63.096 2.013 l
+63.845 2.013 l
+63.845 1.352 l
+63.096 1.352 l
+63.096 -1.72 l
+63.096 -1.918 63.126 -2.07 63.184 -2.176 c
+63.251 -2.275 63.364 -2.323 63.523 -2.323 c
+63.629 -2.323 63.735 -2.305 63.845 -2.264 c
+63.831 -2.955 l
+63.654 -3.013 63.467 -3.043 63.272 -3.043 c
+62.949 -3.043 62.703 -2.933 62.538 -2.705 c
+62.368 -2.469 62.287 -2.147 62.287 -1.735 c
+62.287 1.352 l
+61.523 1.352 l
+61.523 2.013 l
+62.287 2.013 l
+62.287 3.219 l
+h
+66.745 1.249 m
+66.627 1.267 66.503 1.278 66.378 1.278 c
+65.955 1.278 65.665 1.051 65.51 0.602 c
+65.51 -2.955 l
+64.687 -2.955 l
+64.687 2.013 l
+65.481 2.013 l
+65.495 1.514 l
+65.709 1.903 66.017 2.102 66.422 2.102 c
+66.547 2.102 66.657 2.08 66.745 2.043 c
+h
+68.321 -2.955 -0.808 4.968 re
+68.38 3.337 m
+68.38 3.197 68.34 3.079 68.263 2.984 c
+68.193 2.896 68.078 2.851 67.924 2.851 c
+67.766 2.851 67.649 2.896 67.571 2.984 c
+67.502 3.079 67.469 3.197 67.469 3.337 c
+67.469 3.472 67.502 3.586 67.571 3.675 c
+67.649 3.77 67.763 3.821 67.91 3.821 c
+68.064 3.821 68.182 3.77 68.263 3.675 c
+68.34 3.586 68.38 3.472 68.38 3.337 c
+73.102 -0.706 m
+73.102 -1.482 72.959 -2.066 72.676 -2.455 c
+72.4 -2.849 72 -3.043 71.471 -3.043 c
+70.949 -3.043 70.559 -2.818 70.295 -2.367 c
+70.251 -2.955 l
+69.516 -2.955 l
+69.516 4.101 l
+70.324 4.101 l
+70.324 1.469 l
+70.588 1.888 70.971 2.102 71.471 2.102 c
+72.007 2.102 72.415 1.911 72.691 1.529 c
+72.963 1.146 73.102 0.562 73.102 -0.221 c
+h
+72.294 -0.25 m
+72.294 0.338 72.209 0.753 72.044 0.999 c
+71.886 1.253 71.621 1.381 71.251 1.381 c
+70.839 1.381 70.53 1.153 70.324 0.706 c
+70.324 -1.661 l
+70.519 -2.103 70.831 -2.323 71.265 -2.323 c
+71.625 -2.323 71.886 -2.198 72.044 -1.941 c
+72.209 -1.687 72.294 -1.29 72.294 -0.75 c
+h
+76.508 -2.514 m
+76.233 -2.866 75.837 -3.043 75.318 -3.043 c
+74.877 -3.043 74.54 -2.893 74.304 -2.587 c
+74.076 -2.275 73.958 -1.819 73.952 -1.22 c
+73.952 2.013 l
+74.76 2.013 l
+74.76 -1.162 l
+74.76 -1.937 74.995 -2.323 75.465 -2.323 c
+75.954 -2.323 76.292 -2.103 76.479 -1.661 c
+76.479 2.013 l
+77.302 2.013 l
+77.302 -2.955 l
+76.523 -2.955 l
+h
+79.412 3.219 m
+79.412 2.013 l
+80.161 2.013 l
+80.161 1.352 l
+79.412 1.352 l
+79.412 -1.72 l
+79.412 -1.918 79.442 -2.07 79.5 -2.176 c
+79.566 -2.275 79.681 -2.323 79.838 -2.323 c
+79.945 -2.323 80.051 -2.305 80.161 -2.264 c
+80.147 -2.955 l
+79.97 -3.013 79.783 -3.043 79.589 -3.043 c
+79.265 -3.043 79.018 -2.933 78.854 -2.705 c
+78.684 -2.469 78.604 -2.147 78.604 -1.735 c
+78.604 1.352 l
+77.839 1.352 l
+77.839 2.013 l
+78.604 2.013 l
+78.604 3.219 l
+h
+81.885 -2.955 -0.808 4.968 re
+81.944 3.337 m
+81.944 3.197 81.904 3.079 81.826 2.984 c
+81.757 2.896 81.642 2.851 81.488 2.851 c
+81.33 2.851 81.212 2.896 81.135 2.984 c
+81.065 3.079 81.032 3.197 81.032 3.337 c
+81.032 3.472 81.065 3.586 81.135 3.675 c
+81.212 3.77 81.327 3.821 81.474 3.821 c
+81.628 3.821 81.746 3.77 81.826 3.675 c
+81.904 3.586 81.944 3.472 81.944 3.337 c
+82.903 -0.235 m
+82.903 0.477 83.072 1.047 83.417 1.469 c
+83.77 1.888 84.233 2.102 84.814 2.102 c
+85.391 2.102 85.85 1.896 86.195 1.484 c
+86.548 1.08 86.729 0.521 86.739 -0.191 c
+86.739 -0.706 l
+86.739 -1.434 86.563 -2.007 86.21 -2.425 c
+85.865 -2.837 85.405 -3.043 84.828 -3.043 c
+84.248 -3.043 83.785 -2.845 83.432 -2.44 c
+83.087 -2.029 82.91 -1.478 82.903 -0.779 c
+h
+83.712 -0.706 m
+83.712 -1.216 83.807 -1.617 84.005 -1.912 c
+84.211 -2.205 84.483 -2.352 84.828 -2.352 c
+85.542 -2.352 85.909 -1.834 85.931 -0.794 c
+85.931 -0.235 l
+85.931 0.272 85.828 0.675 85.622 0.97 c
+85.424 1.263 85.156 1.411 84.814 1.411 c
+84.479 1.411 84.211 1.263 84.005 0.97 c
+83.807 0.675 83.712 0.272 83.712 -0.235 c
+h
+88.364 2.013 m
+88.393 1.484 l
+88.705 1.896 89.106 2.102 89.598 2.102 c
+90.48 2.102 90.925 1.517 90.936 0.353 c
+90.936 -2.955 l
+90.128 -2.955 l
+90.128 0.309 l
+90.128 0.698 90.062 0.974 89.937 1.132 c
+89.808 1.297 89.613 1.381 89.349 1.381 c
+89.143 1.381 88.959 1.311 88.805 1.176 c
+88.647 1.047 88.518 0.874 88.422 0.661 c
+88.422 -2.955 l
+87.599 -2.955 l
+87.599 2.013 l
+h
+94.398 -1.676 m
+94.398 -1.511 94.328 -1.364 94.192 -1.235 c
+94.064 -1.11 93.81 -0.963 93.428 -0.794 c
+92.994 -0.611 92.685 -0.452 92.501 -0.324 c
+92.325 -0.198 92.192 -0.055 92.105 0.103 c
+92.017 0.268 91.972 0.463 91.972 0.691 c
+91.972 1.103 92.119 1.44 92.414 1.705 c
+92.707 1.969 93.082 2.102 93.545 2.102 c
+94.034 2.102 94.427 1.959 94.721 1.675 c
+95.015 1.4 95.162 1.043 95.162 0.602 c
+94.353 0.602 l
+94.353 0.827 94.272 1.018 94.118 1.176 c
+93.971 1.33 93.78 1.411 93.545 1.411 c
+93.31 1.411 93.123 1.344 92.986 1.22 c
+92.847 1.103 92.78 0.933 92.78 0.72 c
+92.78 0.551 92.828 0.419 92.928 0.323 c
+93.023 0.224 93.266 0.091 93.648 -0.073 c
+94.255 -0.309 94.666 -0.541 94.883 -0.765 c
+95.096 -0.992 95.206 -1.276 95.206 -1.617 c
+95.206 -2.051 95.051 -2.396 94.75 -2.66 c
+94.457 -2.918 94.06 -3.043 93.559 -3.043 c
+93.049 -3.043 92.634 -2.896 92.31 -2.602 c
+91.987 -2.308 91.826 -1.933 91.826 -1.47 c
+92.649 -1.47 l
+92.655 -1.746 92.74 -1.962 92.898 -2.117 c
+93.052 -2.275 93.273 -2.352 93.559 -2.352 c
+93.831 -2.352 94.037 -2.293 94.177 -2.176 c
+94.324 -2.051 94.398 -1.881 94.398 -1.676 c
+102.699 -2.117 m
+102.552 -2.308 l
+102.13 -2.801 101.512 -3.043 100.7 -3.043 c
+99.972 -3.043 99.406 -2.804 98.995 -2.323 c
+98.583 -1.845 98.37 -1.169 98.363 -0.294 c
+98.363 1.014 l
+98.363 1.955 98.546 2.653 98.922 3.116 c
+99.292 3.586 99.855 3.821 100.612 3.821 c
+101.247 3.821 101.744 3.645 102.097 3.293 c
+102.456 2.94 102.658 2.433 102.699 1.778 c
+101.861 1.778 l
+101.821 2.19 101.703 2.514 101.509 2.749 c
+101.31 2.984 101.012 3.102 100.612 3.102 c
+100.13 3.102 99.778 2.944 99.553 2.631 c
+99.325 2.315 99.208 1.815 99.201 1.132 c
+99.201 -0.235 l
+99.201 -0.912 99.325 -1.434 99.583 -1.794 c
+99.847 -2.147 100.219 -2.323 100.7 -2.323 c
+101.148 -2.323 101.494 -2.216 101.729 -1.999 c
+101.861 -1.881 l
+101.861 -0.339 l
+100.626 -0.339 l
+100.626 0.382 l
+102.699 0.382 l
+h
+104.617 -2.955 -0.808 4.968 re
+104.676 3.337 m
+104.676 3.197 104.636 3.079 104.558 2.984 c
+104.489 2.896 104.375 2.851 104.22 2.851 c
+104.062 2.851 103.944 2.896 103.867 2.984 c
+103.797 3.079 103.765 3.197 103.765 3.337 c
+103.765 3.472 103.797 3.586 103.867 3.675 c
+103.944 3.77 104.059 3.821 104.206 3.821 c
+104.36 3.821 104.478 3.77 104.558 3.675 c
+104.636 3.586 104.676 3.472 104.676 3.337 c
+106.874 3.219 m
+106.874 2.013 l
+107.623 2.013 l
+107.623 1.352 l
+106.874 1.352 l
+106.874 -1.72 l
+106.874 -1.918 106.903 -2.07 106.962 -2.176 c
+107.028 -2.275 107.142 -2.323 107.3 -2.323 c
+107.406 -2.323 107.512 -2.305 107.623 -2.264 c
+107.608 -2.955 l
+107.432 -3.013 107.244 -3.043 107.05 -3.043 c
+106.727 -3.043 106.48 -2.933 106.315 -2.705 c
+106.146 -2.469 106.065 -2.147 106.065 -1.735 c
+106.065 1.352 l
+105.301 1.352 l
+105.301 2.013 l
+106.065 2.013 l
+106.065 3.219 l
+h
+113.197 -1.676 m
+113.197 -1.511 113.128 -1.364 112.992 -1.235 c
+112.863 -1.11 112.609 -0.963 112.227 -0.794 c
+111.794 -0.611 111.485 -0.452 111.302 -0.324 c
+111.125 -0.198 110.993 -0.055 110.905 0.103 c
+110.816 0.268 110.772 0.463 110.772 0.691 c
+110.772 1.103 110.92 1.44 111.213 1.705 c
+111.508 1.969 111.882 2.102 112.345 2.102 c
+112.834 2.102 113.227 1.959 113.521 1.675 c
+113.815 1.4 113.962 1.043 113.962 0.602 c
+113.154 0.602 l
+113.154 0.827 113.073 1.018 112.919 1.176 c
+112.771 1.33 112.58 1.411 112.345 1.411 c
+112.11 1.411 111.923 1.344 111.786 1.22 c
+111.647 1.103 111.581 0.933 111.581 0.72 c
+111.581 0.551 111.628 0.419 111.728 0.323 c
+111.823 0.224 112.066 0.091 112.448 -0.073 c
+113.054 -0.309 113.466 -0.541 113.683 -0.765 c
+113.896 -0.992 114.006 -1.276 114.006 -1.617 c
+114.006 -2.051 113.852 -2.396 113.55 -2.66 c
+113.257 -2.918 112.86 -3.043 112.36 -3.043 c
+111.849 -3.043 111.433 -2.896 111.111 -2.602 c
+110.787 -2.308 110.625 -1.933 110.625 -1.47 c
+111.448 -1.47 l
+111.456 -1.746 111.541 -1.962 111.699 -2.117 c
+111.853 -2.275 112.073 -2.352 112.36 -2.352 c
+112.632 -2.352 112.838 -2.293 112.977 -2.176 c
+113.124 -2.051 113.197 -1.881 113.197 -1.676 c
+115.703 1.499 m
+116.005 1.899 116.399 2.102 116.879 2.102 c
+117.762 2.102 118.207 1.517 118.217 0.353 c
+118.217 -2.955 l
+117.409 -2.955 l
+117.409 0.309 l
+117.409 0.698 117.343 0.974 117.218 1.132 c
+117.089 1.297 116.894 1.381 116.63 1.381 c
+116.424 1.381 116.241 1.311 116.086 1.176 c
+115.928 1.047 115.799 0.874 115.703 0.661 c
+115.703 -2.955 l
+114.88 -2.955 l
+114.88 4.101 l
+115.703 4.101 l
+h
+119.077 -0.235 m
+119.077 0.477 119.246 1.047 119.591 1.469 c
+119.944 1.888 120.407 2.102 120.989 2.102 c
+121.565 2.102 122.024 1.896 122.37 1.484 c
+122.722 1.08 122.903 0.521 122.914 -0.191 c
+122.914 -0.706 l
+122.914 -1.434 122.737 -2.007 122.385 -2.425 c
+122.039 -2.837 121.58 -3.043 121.003 -3.043 c
+120.422 -3.043 119.959 -2.845 119.607 -2.44 c
+119.261 -2.029 119.084 -1.478 119.077 -0.779 c
+h
+119.886 -0.706 m
+119.886 -1.216 119.981 -1.617 120.179 -1.912 c
+120.385 -2.205 120.657 -2.352 121.003 -2.352 c
+121.716 -2.352 122.084 -1.834 122.105 -0.794 c
+122.105 -0.235 l
+122.105 0.272 122.003 0.675 121.797 0.97 c
+121.598 1.263 121.33 1.411 120.989 1.411 c
+120.654 1.411 120.385 1.263 120.179 0.97 c
+119.981 0.675 119.886 0.272 119.886 -0.235 c
+h
+126.317 -2.514 m
+126.041 -2.866 125.644 -3.043 125.126 -3.043 c
+124.685 -3.043 124.347 -2.893 124.112 -2.587 c
+123.884 -2.275 123.767 -1.819 123.759 -1.22 c
+123.759 2.013 l
+124.567 2.013 l
+124.567 -1.162 l
+124.567 -1.937 124.802 -2.323 125.273 -2.323 c
+125.762 -2.323 126.1 -2.103 126.287 -1.661 c
+126.287 2.013 l
+127.11 2.013 l
+127.11 -2.955 l
+126.331 -2.955 l
+h
+129.029 -2.955 -0.809 7.056 re
+130.05 -0.25 m
+130.05 0.521 130.19 1.105 130.477 1.499 c
+130.759 1.899 131.171 2.102 131.711 2.102 c
+132.181 2.102 132.549 1.911 132.814 1.529 c
+132.814 4.101 l
+133.637 4.101 l
+133.637 -2.955 l
+132.887 -2.955 l
+132.843 -2.425 l
+132.578 -2.837 132.2 -3.043 131.711 -3.043 c
+131.189 -3.043 130.785 -2.849 130.491 -2.455 c
+130.197 -2.055 130.05 -1.484 130.05 -0.75 c
+h
+130.859 -0.706 m
+130.859 -1.264 130.94 -1.676 131.108 -1.941 c
+131.274 -2.198 131.542 -2.323 131.917 -2.323 c
+132.318 -2.323 132.615 -2.124 132.814 -1.72 c
+132.814 0.793 l
+132.608 1.182 132.307 1.381 131.917 1.381 c
+131.542 1.381 131.274 1.249 131.108 0.985 c
+130.94 0.727 130.859 0.33 130.859 -0.206 c
+h
+140.6 -0.706 m
+140.6 -1.482 140.457 -2.066 140.174 -2.455 c
+139.898 -2.849 139.498 -3.043 138.969 -3.043 c
+138.447 -3.043 138.057 -2.818 137.793 -2.367 c
+137.749 -2.955 l
+137.014 -2.955 l
+137.014 4.101 l
+137.822 4.101 l
+137.822 1.469 l
+138.086 1.888 138.469 2.102 138.969 2.102 c
+139.505 2.102 139.913 1.911 140.189 1.529 c
+140.461 1.146 140.6 0.562 140.6 -0.221 c
+h
+139.792 -0.25 m
+139.792 0.338 139.707 0.753 139.542 0.999 c
+139.384 1.253 139.119 1.381 138.749 1.381 c
+138.337 1.381 138.028 1.153 137.822 0.706 c
+137.822 -1.661 l
+138.017 -2.103 138.329 -2.323 138.763 -2.323 c
+139.123 -2.323 139.384 -2.198 139.542 -1.941 c
+139.707 -1.687 139.792 -1.29 139.792 -0.75 c
+h
+143.243 -3.043 m
+142.626 -3.043 142.148 -2.863 141.816 -2.5 c
+141.494 -2.128 141.325 -1.584 141.317 -0.867 c
+141.317 -0.265 l
+141.317 0.47 141.479 1.047 141.802 1.469 c
+142.125 1.888 142.574 2.102 143.154 2.102 c
+143.731 2.102 144.161 1.914 144.448 1.543 c
+144.742 1.18 144.893 0.606 144.904 -0.177 c
+144.904 -0.706 l
+142.125 -0.706 l
+142.125 -0.823 l
+142.125 -1.364 142.221 -1.757 142.42 -1.999 c
+142.626 -2.234 142.911 -2.352 143.287 -2.352 c
+143.53 -2.352 143.742 -2.308 143.919 -2.22 c
+144.103 -2.132 144.276 -1.992 144.434 -1.794 c
+144.86 -2.308 l
+144.507 -2.801 143.966 -3.043 143.243 -3.043 c
+143.154 1.411 m
+142.82 1.411 142.57 1.294 142.404 1.058 c
+142.236 0.83 142.144 0.474 142.125 -0.015 c
+144.081 -0.015 l
+144.081 0.103 l
+144.058 0.573 143.977 0.904 143.831 1.103 c
+143.684 1.308 143.455 1.411 143.154 1.411 c
+150.562 -2.955 m
+150.511 -2.849 150.478 -2.668 150.46 -2.411 c
+150.173 -2.833 149.805 -3.043 149.357 -3.043 c
+148.905 -3.043 148.552 -2.922 148.299 -2.675 c
+148.053 -2.422 147.931 -2.066 147.931 -1.602 c
+147.931 -1.095 148.101 -0.691 148.446 -0.397 c
+148.787 -0.103 149.258 0.047 149.857 0.058 c
+150.445 0.058 l
+150.445 0.588 l
+150.445 0.882 150.375 1.091 150.239 1.22 c
+150.111 1.344 149.916 1.411 149.651 1.411 c
+149.405 1.411 149.203 1.338 149.049 1.19 c
+148.902 1.043 148.828 0.856 148.828 0.632 c
+148.005 0.632 l
+148.005 0.885 148.079 1.132 148.226 1.367 c
+148.38 1.602 148.586 1.782 148.843 1.911 c
+149.097 2.036 149.379 2.102 149.695 2.102 c
+150.202 2.102 150.592 1.973 150.857 1.72 c
+151.121 1.473 151.254 1.105 151.254 0.617 c
+151.254 -1.881 l
+151.261 -2.264 151.316 -2.598 151.415 -2.881 c
+151.415 -2.955 l
+h
+149.49 -2.308 m
+149.685 -2.308 149.872 -2.257 150.048 -2.147 c
+150.232 -2.029 150.364 -1.893 150.445 -1.735 c
+150.445 -0.544 l
+149.99 -0.544 l
+149.608 -0.555 149.302 -0.643 149.078 -0.809 c
+148.85 -0.977 148.74 -1.206 148.74 -1.5 c
+148.74 -1.786 148.791 -1.992 148.902 -2.117 c
+149.02 -2.246 149.214 -2.308 149.49 -2.308 c
+153.668 -1.72 m
+154.609 2.013 l
+155.447 2.013 l
+153.961 -2.955 l
+153.359 -2.955 l
+151.86 2.013 l
+152.698 2.013 l
+h
+158.607 -2.955 m
+158.555 -2.849 158.522 -2.668 158.504 -2.411 c
+158.217 -2.833 157.849 -3.043 157.402 -3.043 c
+156.949 -3.043 156.596 -2.922 156.343 -2.675 c
+156.097 -2.422 155.976 -2.066 155.976 -1.602 c
+155.976 -1.095 156.145 -0.691 156.49 -0.397 c
+156.832 -0.103 157.302 0.047 157.901 0.058 c
+158.489 0.058 l
+158.489 0.588 l
+158.489 0.882 158.42 1.091 158.283 1.22 c
+158.154 1.344 157.96 1.411 157.695 1.411 c
+157.449 1.411 157.247 1.338 157.093 1.19 c
+156.945 1.043 156.872 0.856 156.872 0.632 c
+156.049 0.632 l
+156.049 0.885 156.122 1.132 156.27 1.367 c
+156.424 1.602 156.629 1.782 156.887 1.911 c
+157.14 2.036 157.423 2.102 157.739 2.102 c
+158.247 2.102 158.636 1.973 158.9 1.72 c
+159.166 1.473 159.297 1.105 159.297 0.617 c
+159.297 -1.881 l
+159.305 -2.264 159.36 -2.598 159.459 -2.881 c
+159.459 -2.955 l
+h
+157.533 -2.308 m
+157.728 -2.308 157.916 -2.257 158.092 -2.147 c
+158.276 -2.029 158.408 -1.893 158.489 -1.735 c
+158.489 -0.544 l
+158.034 -0.544 l
+157.651 -0.555 157.346 -0.643 157.122 -0.809 c
+156.895 -0.977 156.784 -1.206 156.784 -1.5 c
+156.784 -1.786 156.835 -1.992 156.945 -2.117 c
+157.063 -2.246 157.258 -2.308 157.533 -2.308 c
+161.252 -2.955 -0.808 4.968 re
+161.311 3.337 m
+161.311 3.197 161.271 3.079 161.194 2.984 c
+161.124 2.896 161.01 2.851 160.856 2.851 c
+160.698 2.851 160.581 2.896 160.503 2.984 c
+160.433 3.079 160.4 3.197 160.4 3.337 c
+160.4 3.472 160.433 3.586 160.503 3.675 c
+160.581 3.77 160.694 3.821 160.841 3.821 c
+160.995 3.821 161.113 3.77 161.194 3.675 c
+161.271 3.586 161.311 3.472 161.311 3.337 c
+163.314 -2.955 -0.809 7.056 re
+167.055 -2.955 m
+167.004 -2.849 166.971 -2.668 166.952 -2.411 c
+166.665 -2.833 166.298 -3.043 165.85 -3.043 c
+165.398 -3.043 165.045 -2.922 164.791 -2.675 c
+164.545 -2.422 164.424 -2.066 164.424 -1.602 c
+164.424 -1.095 164.593 -0.691 164.938 -0.397 c
+165.28 -0.103 165.751 0.047 166.349 0.058 c
+166.937 0.058 l
+166.937 0.588 l
+166.937 0.882 166.867 1.091 166.732 1.22 c
+166.603 1.344 166.408 1.411 166.144 1.411 c
+165.898 1.411 165.695 1.338 165.541 1.19 c
+165.394 1.043 165.321 0.856 165.321 0.632 c
+164.498 0.632 l
+164.498 0.885 164.571 1.132 164.718 1.367 c
+164.872 1.602 165.078 1.782 165.335 1.911 c
+165.589 2.036 165.872 2.102 166.188 2.102 c
+166.695 2.102 167.085 1.973 167.349 1.72 c
+167.613 1.473 167.746 1.105 167.746 0.617 c
+167.746 -1.881 l
+167.754 -2.264 167.808 -2.598 167.908 -2.881 c
+167.908 -2.955 l
+h
+165.982 -2.308 m
+166.177 -2.308 166.364 -2.257 166.541 -2.147 c
+166.724 -2.029 166.856 -1.893 166.937 -1.735 c
+166.937 -0.544 l
+166.482 -0.544 l
+166.1 -0.555 165.795 -0.643 165.57 -0.809 c
+165.342 -0.977 165.232 -1.206 165.232 -1.5 c
+165.232 -1.786 165.284 -1.992 165.394 -2.117 c
+165.512 -2.246 165.707 -2.308 165.982 -2.308 c
+172.421 -0.706 m
+172.421 -1.482 172.277 -2.066 171.994 -2.455 c
+171.718 -2.849 171.318 -3.043 170.788 -3.043 c
+170.266 -3.043 169.877 -2.818 169.612 -2.367 c
+169.568 -2.955 l
+168.834 -2.955 l
+168.834 4.101 l
+169.642 4.101 l
+169.642 1.469 l
+169.907 1.888 170.289 2.102 170.788 2.102 c
+171.325 2.102 171.733 1.911 172.009 1.529 c
+172.28 1.146 172.421 0.562 172.421 -0.221 c
+h
+171.612 -0.25 m
+171.612 0.338 171.527 0.753 171.362 0.999 c
+171.204 1.253 170.939 1.381 170.568 1.381 c
+170.156 1.381 169.848 1.153 169.642 0.706 c
+169.642 -1.661 l
+169.836 -2.103 170.149 -2.323 170.582 -2.323 c
+170.943 -2.323 171.204 -2.198 171.362 -1.941 c
+171.527 -1.687 171.612 -1.29 171.612 -0.75 c
+h
+174.162 -2.955 -0.808 7.056 re
+177.124 -3.043 m
+176.506 -3.043 176.028 -2.863 175.698 -2.5 c
+175.375 -2.128 175.205 -1.584 175.199 -0.867 c
+175.199 -0.265 l
+175.199 0.47 175.36 1.047 175.683 1.469 c
+176.007 1.888 176.455 2.102 177.036 2.102 c
+177.613 2.102 178.042 1.914 178.329 1.543 c
+178.623 1.18 178.773 0.606 178.785 -0.177 c
+178.785 -0.706 l
+176.007 -0.706 l
+176.007 -0.823 l
+176.007 -1.364 176.103 -1.757 176.301 -1.999 c
+176.506 -2.234 176.793 -2.352 177.168 -2.352 c
+177.41 -2.352 177.624 -2.308 177.8 -2.22 c
+177.984 -2.132 178.156 -1.992 178.314 -1.794 c
+178.74 -2.308 l
+178.388 -2.801 177.848 -3.043 177.124 -3.043 c
+177.036 1.411 m
+176.701 1.411 176.452 1.294 176.286 1.058 c
+176.117 0.83 176.026 0.474 176.007 -0.015 c
+177.962 -0.015 l
+177.962 0.103 l
+177.94 0.573 177.859 0.904 177.712 1.103 c
+177.565 1.308 177.337 1.411 177.036 1.411 c
+182.769 -2.955 -0.809 4.968 re
+182.827 3.337 m
+182.827 3.197 182.787 3.079 182.709 2.984 c
+182.64 2.896 182.526 2.851 182.372 2.851 c
+182.214 2.851 182.096 2.896 182.019 2.984 c
+181.948 3.079 181.915 3.197 181.915 3.337 c
+181.915 3.472 181.948 3.586 182.019 3.675 c
+182.096 3.77 182.21 3.821 182.357 3.821 c
+182.511 3.821 182.629 3.77 182.709 3.675 c
+182.787 3.586 182.827 3.472 182.827 3.337 c
+184.716 2.013 m
+184.745 1.484 l
+185.058 1.896 185.458 2.102 185.95 2.102 c
+186.833 2.102 187.277 1.517 187.288 0.353 c
+187.288 -2.955 l
+186.48 -2.955 l
+186.48 0.309 l
+186.48 0.698 186.414 0.974 186.289 1.132 c
+186.16 1.297 185.965 1.381 185.701 1.381 c
+185.495 1.381 185.312 1.311 185.157 1.176 c
+184.999 1.047 184.87 0.874 184.774 0.661 c
+184.774 -2.955 l
+183.951 -2.955 l
+183.951 2.013 l
+h
+191.706 3.219 m
+191.706 2.013 l
+192.455 2.013 l
+192.455 1.352 l
+191.706 1.352 l
+191.706 -1.72 l
+191.706 -1.918 191.735 -2.07 191.793 -2.176 c
+191.86 -2.275 191.974 -2.323 192.132 -2.323 c
+192.238 -2.323 192.345 -2.305 192.455 -2.264 c
+192.44 -2.955 l
+192.264 -3.013 192.076 -3.043 191.882 -3.043 c
+191.558 -3.043 191.313 -2.933 191.147 -2.705 c
+190.978 -2.469 190.897 -2.147 190.897 -1.735 c
+190.897 1.352 l
+190.133 1.352 l
+190.133 2.013 l
+190.897 2.013 l
+190.897 3.219 l
+h
+194.12 1.499 m
+194.421 1.899 194.814 2.102 195.296 2.102 c
+196.177 2.102 196.622 1.517 196.633 0.353 c
+196.633 -2.955 l
+195.824 -2.955 l
+195.824 0.309 l
+195.824 0.698 195.758 0.974 195.633 1.132 c
+195.505 1.297 195.311 1.381 195.045 1.381 c
+194.84 1.381 194.656 1.311 194.502 1.176 c
+194.344 1.047 194.216 0.874 194.12 0.661 c
+194.12 -2.955 l
+193.297 -2.955 l
+193.297 4.101 l
+194.12 4.101 l
+h
+199.433 -3.043 m
+198.816 -3.043 198.338 -2.863 198.008 -2.5 c
+197.684 -2.128 197.515 -1.584 197.508 -0.867 c
+197.508 -0.265 l
+197.508 0.47 197.669 1.047 197.993 1.469 c
+198.316 1.888 198.764 2.102 199.346 2.102 c
+199.922 2.102 200.352 1.914 200.639 1.543 c
+200.933 1.18 201.083 0.606 201.094 -0.177 c
+201.094 -0.706 l
+198.316 -0.706 l
+198.316 -0.823 l
+198.316 -1.364 198.411 -1.757 198.61 -1.999 c
+198.816 -2.234 199.103 -2.352 199.477 -2.352 c
+199.72 -2.352 199.933 -2.308 200.109 -2.22 c
+200.293 -2.132 200.466 -1.992 200.624 -1.794 c
+201.05 -2.308 l
+200.697 -2.801 200.157 -3.043 199.433 -3.043 c
+199.346 1.411 m
+199.011 1.411 198.761 1.294 198.596 1.058 c
+198.426 0.83 198.334 0.474 198.316 -0.015 c
+200.271 -0.015 l
+200.271 0.103 l
+200.25 0.573 200.169 0.904 200.021 1.103 c
+199.874 1.308 199.647 1.411 199.346 1.411 c
+206.621 -1.676 m
+206.621 -1.511 206.552 -1.364 206.415 -1.235 c
+206.287 -1.11 206.033 -0.963 205.651 -0.794 c
+205.218 -0.611 204.909 -0.452 204.725 -0.324 c
+204.549 -0.198 204.416 -0.055 204.329 0.103 c
+204.24 0.268 204.196 0.463 204.196 0.691 c
+204.196 1.103 204.343 1.44 204.637 1.705 c
+204.931 1.969 205.306 2.102 205.769 2.102 c
+206.257 2.102 206.65 1.959 206.945 1.675 c
+207.238 1.4 207.386 1.043 207.386 0.602 c
+206.577 0.602 l
+206.577 0.827 206.496 1.018 206.342 1.176 c
+206.195 1.33 206.004 1.411 205.769 1.411 c
+205.534 1.411 205.346 1.344 205.21 1.22 c
+205.071 1.103 205.004 0.933 205.004 0.72 c
+205.004 0.551 205.052 0.419 205.151 0.323 c
+205.247 0.224 205.49 0.091 205.871 -0.073 c
+206.478 -0.309 206.889 -0.541 207.107 -0.765 c
+207.319 -0.992 207.429 -1.276 207.429 -1.617 c
+207.429 -2.051 207.275 -2.396 206.974 -2.66 c
+206.68 -2.918 206.283 -3.043 205.783 -3.043 c
+205.272 -3.043 204.857 -2.896 204.534 -2.602 c
+204.21 -2.308 204.049 -1.933 204.049 -1.47 c
+204.872 -1.47 l
+204.879 -1.746 204.964 -1.962 205.122 -2.117 c
+205.276 -2.275 205.497 -2.352 205.783 -2.352 c
+206.055 -2.352 206.261 -2.293 206.401 -2.176 c
+206.548 -2.051 206.621 -1.881 206.621 -1.676 c
+209.378 3.219 m
+209.378 2.013 l
+210.127 2.013 l
+210.127 1.352 l
+209.378 1.352 l
+209.378 -1.72 l
+209.378 -1.918 209.407 -2.07 209.465 -2.176 c
+209.532 -2.275 209.646 -2.323 209.804 -2.323 c
+209.91 -2.323 210.016 -2.305 210.127 -2.264 c
+210.112 -2.955 l
+209.935 -3.013 209.748 -3.043 209.553 -3.043 c
+209.23 -3.043 208.984 -2.933 208.819 -2.705 c
+208.649 -2.469 208.569 -2.147 208.569 -1.735 c
+208.569 1.352 l
+207.805 1.352 l
+207.805 2.013 l
+208.569 2.013 l
+208.569 3.219 l
+h
+213.526 -2.955 m
+213.474 -2.849 213.442 -2.668 213.423 -2.411 c
+213.137 -2.833 212.769 -3.043 212.321 -3.043 c
+211.869 -3.043 211.516 -2.922 211.263 -2.675 c
+211.016 -2.422 210.895 -2.066 210.895 -1.602 c
+210.895 -1.095 211.064 -0.691 211.41 -0.397 c
+211.751 -0.103 212.221 0.047 212.821 0.058 c
+213.409 0.058 l
+213.409 0.588 l
+213.409 0.882 213.339 1.091 213.203 1.22 c
+213.074 1.344 212.879 1.411 212.615 1.411 c
+212.368 1.411 212.167 1.338 212.012 1.19 c
+211.865 1.043 211.792 0.856 211.792 0.632 c
+210.968 0.632 l
+210.968 0.885 211.042 1.132 211.188 1.367 c
+211.344 1.602 211.549 1.782 211.807 1.911 c
+212.06 2.036 212.343 2.102 212.659 2.102 c
+213.166 2.102 213.555 1.973 213.82 1.72 c
+214.085 1.473 214.217 1.105 214.217 0.617 c
+214.217 -1.881 l
+214.224 -2.264 214.28 -2.598 214.378 -2.881 c
+214.378 -2.955 l
+h
+212.453 -2.308 m
+212.648 -2.308 212.835 -2.257 213.012 -2.147 c
+213.195 -2.029 213.328 -1.893 213.409 -1.735 c
+213.409 -0.544 l
+212.952 -0.544 l
+212.57 -0.555 212.266 -0.643 212.042 -0.809 c
+211.814 -0.977 211.703 -1.206 211.703 -1.5 c
+211.703 -1.786 211.755 -1.992 211.865 -2.117 c
+211.982 -2.246 212.177 -2.308 212.453 -2.308 c
+216.054 2.013 m
+216.084 1.484 l
+216.396 1.896 216.796 2.102 217.289 2.102 c
+218.171 2.102 218.616 1.517 218.627 0.353 c
+218.627 -2.955 l
+217.818 -2.955 l
+217.818 0.309 l
+217.818 0.698 217.752 0.974 217.627 1.132 c
+217.499 1.297 217.303 1.381 217.039 1.381 c
+216.833 1.381 216.649 1.311 216.495 1.176 c
+216.337 1.047 216.208 0.874 216.113 0.661 c
+216.113 -2.955 l
+215.29 -2.955 l
+215.29 2.013 l
+h
+219.505 -0.25 m
+219.505 0.521 219.645 1.105 219.931 1.499 c
+220.214 1.899 220.626 2.102 221.166 2.102 c
+221.636 2.102 222.004 1.911 222.268 1.529 c
+222.268 4.101 l
+223.092 4.101 l
+223.092 -2.955 l
+222.342 -2.955 l
+222.298 -2.425 l
+222.033 -2.837 221.654 -3.043 221.166 -3.043 c
+220.644 -3.043 220.24 -2.849 219.946 -2.455 c
+219.652 -2.055 219.505 -1.484 219.505 -0.75 c
+h
+220.314 -0.706 m
+220.314 -1.264 220.395 -1.676 220.563 -1.941 c
+220.729 -2.198 220.997 -2.323 221.372 -2.323 c
+221.772 -2.323 222.07 -2.124 222.268 -1.72 c
+222.268 0.793 l
+222.062 1.182 221.761 1.381 221.372 1.381 c
+220.997 1.381 220.729 1.249 220.563 0.985 c
+220.395 0.727 220.314 0.33 220.314 -0.206 c
+h
+226.704 -2.955 m
+226.652 -2.849 226.619 -2.668 226.601 -2.411 c
+226.315 -2.833 225.947 -3.043 225.498 -3.043 c
+225.047 -3.043 224.694 -2.922 224.44 -2.675 c
+224.194 -2.422 224.073 -2.066 224.073 -1.602 c
+224.073 -1.095 224.241 -0.691 224.587 -0.397 c
+224.929 -0.103 225.399 0.047 225.999 0.058 c
+226.587 0.058 l
+226.587 0.588 l
+226.587 0.882 226.516 1.091 226.38 1.22 c
+226.252 1.344 226.057 1.411 225.793 1.411 c
+225.546 1.411 225.344 1.338 225.19 1.19 c
+225.043 1.043 224.969 0.856 224.969 0.632 c
+224.146 0.632 l
+224.146 0.885 224.22 1.132 224.366 1.367 c
+224.521 1.602 224.727 1.782 224.984 1.911 c
+225.237 2.036 225.521 2.102 225.837 2.102 c
+226.344 2.102 226.733 1.973 226.998 1.72 c
+227.262 1.473 227.395 1.105 227.395 0.617 c
+227.395 -1.881 l
+227.402 -2.264 227.457 -2.598 227.556 -2.881 c
+227.556 -2.955 l
+h
+225.631 -2.308 m
+225.825 -2.308 226.013 -2.257 226.19 -2.147 c
+226.373 -2.029 226.506 -1.893 226.587 -1.735 c
+226.587 -0.544 l
+226.13 -0.544 l
+225.748 -0.555 225.443 -0.643 225.22 -0.809 c
+224.991 -0.977 224.881 -1.206 224.881 -1.5 c
+224.881 -1.786 224.933 -1.992 225.043 -2.117 c
+225.16 -2.246 225.355 -2.308 225.631 -2.308 c
+230.525 1.249 m
+230.408 1.267 230.283 1.278 230.159 1.278 c
+229.735 1.278 229.445 1.051 229.291 0.602 c
+229.291 -2.955 l
+228.468 -2.955 l
+228.468 2.013 l
+229.261 2.013 l
+229.276 1.514 l
+229.49 1.903 229.798 2.102 230.202 2.102 c
+230.327 2.102 230.437 2.08 230.525 2.043 c
+h
+230.97 -0.25 m
+230.97 0.521 231.11 1.105 231.396 1.499 c
+231.68 1.899 232.091 2.102 232.631 2.102 c
+233.101 2.102 233.469 1.911 233.733 1.529 c
+233.733 4.101 l
+234.556 4.101 l
+234.556 -2.955 l
+233.807 -2.955 l
+233.763 -2.425 l
+233.498 -2.837 233.12 -3.043 232.631 -3.043 c
+232.11 -3.043 231.705 -2.849 231.412 -2.455 c
+231.117 -2.055 230.97 -1.484 230.97 -0.75 c
+h
+231.778 -0.706 m
+231.778 -1.264 231.859 -1.676 232.029 -1.941 c
+232.194 -2.198 232.462 -2.323 232.837 -2.323 c
+233.238 -2.323 233.535 -2.124 233.733 -1.72 c
+233.733 0.793 l
+233.528 1.182 233.226 1.381 232.837 1.381 c
+232.462 1.381 232.194 1.249 232.029 0.985 c
+231.859 0.727 231.778 0.33 231.778 -0.206 c
+h
+f
+Q
+q 1 0 0 1 538.5375 394.9912 cm
+0 0 m
+0 0.166 -0.071 0.312 -0.206 0.441 c
+-0.335 0.566 -0.588 0.713 -0.971 0.882 c
+-1.405 1.066 -1.713 1.224 -1.896 1.353 c
+-2.073 1.478 -2.205 1.621 -2.294 1.779 c
+-2.382 1.945 -2.426 2.139 -2.426 2.367 c
+-2.426 2.779 -2.278 3.117 -1.985 3.381 c
+-1.69 3.645 -1.316 3.778 -0.853 3.778 c
+-0.364 3.778 0.029 3.635 0.323 3.352 c
+0.617 3.076 0.764 2.72 0.764 2.278 c
+-0.044 2.278 l
+-0.044 2.503 -0.125 2.694 -0.279 2.852 c
+-0.427 3.007 -0.618 3.088 -0.853 3.088 c
+-1.088 3.088 -1.276 3.021 -1.411 2.897 c
+-1.551 2.779 -1.617 2.61 -1.617 2.396 c
+-1.617 2.228 -1.569 2.095 -1.47 1.999 c
+-1.374 1.9 -1.133 1.768 -0.75 1.603 c
+-0.144 1.368 0.268 1.135 0.484 0.912 c
+0.698 0.684 0.808 0.401 0.808 0.059 c
+0.808 -0.374 0.654 -0.72 0.353 -0.984 c
+0.058 -1.242 -0.339 -1.367 -0.838 -1.367 c
+-1.349 -1.367 -1.764 -1.219 -2.087 -0.926 c
+-2.411 -0.631 -2.573 -0.257 -2.573 0.206 c
+-1.75 0.206 l
+-1.742 -0.07 -1.658 -0.286 -1.5 -0.44 c
+-1.345 -0.598 -1.125 -0.675 -0.838 -0.675 c
+-0.566 -0.675 -0.36 -0.617 -0.221 -0.5 c
+-0.074 -0.374 0 -0.205 0 0 c
+3.05 0.074 m
+3.946 3.69 l
+4.814 3.69 l
+3.182 -1.984 l
+3.064 -2.403 2.892 -2.726 2.667 -2.954 c
+2.439 -3.179 2.186 -3.293 1.903 -3.293 c
+1.793 -3.293 1.657 -3.266 1.491 -3.218 c
+1.491 -2.528 l
+1.668 -2.543 l
+1.903 -2.543 2.083 -2.484 2.212 -2.366 c
+2.348 -2.256 2.454 -2.065 2.535 -1.793 c
+2.697 -1.234 l
+1.242 3.69 l
+2.123 3.69 l
+h
+7.867 0 m
+7.867 0.166 7.798 0.312 7.661 0.441 c
+7.532 0.566 7.279 0.713 6.898 0.882 c
+6.464 1.066 6.155 1.224 5.971 1.353 c
+5.795 1.478 5.662 1.621 5.574 1.779 c
+5.486 1.945 5.442 2.139 5.442 2.367 c
+5.442 2.779 5.589 3.117 5.882 3.381 c
+6.177 3.645 6.551 3.778 7.015 3.778 c
+7.503 3.778 7.897 3.635 8.191 3.352 c
+8.485 3.076 8.631 2.72 8.631 2.278 c
+7.823 2.278 l
+7.823 2.503 7.742 2.694 7.588 2.852 c
+7.441 3.007 7.25 3.088 7.015 3.088 c
+6.78 3.088 6.592 3.021 6.456 2.897 c
+6.316 2.779 6.25 2.61 6.25 2.396 c
+6.25 2.228 6.298 2.095 6.397 1.999 c
+6.493 1.9 6.736 1.768 7.118 1.603 c
+7.723 1.368 8.136 1.135 8.352 0.912 c
+8.565 0.684 8.676 0.401 8.676 0.059 c
+8.676 -0.374 8.521 -0.72 8.22 -0.984 c
+7.926 -1.242 7.529 -1.367 7.029 -1.367 c
+6.518 -1.367 6.104 -1.219 5.78 -0.926 c
+5.456 -0.631 5.294 -0.257 5.294 0.206 c
+6.118 0.206 l
+6.125 -0.07 6.21 -0.286 6.368 -0.44 c
+6.522 -0.598 6.742 -0.675 7.029 -0.675 c
+7.301 -0.675 7.507 -0.617 7.646 -0.5 c
+7.794 -0.374 7.867 -0.205 7.867 0 c
+10.624 4.896 m
+10.624 3.69 l
+11.373 3.69 l
+11.373 3.028 l
+10.624 3.028 l
+10.624 -0.043 l
+10.624 -0.242 10.653 -0.393 10.711 -0.5 c
+10.778 -0.598 10.892 -0.646 11.05 -0.646 c
+11.156 -0.646 11.263 -0.628 11.373 -0.588 c
+11.359 -1.278 l
+11.182 -1.337 10.994 -1.367 10.8 -1.367 c
+10.476 -1.367 10.231 -1.257 10.065 -1.028 c
+9.896 -0.793 9.815 -0.47 9.815 -0.058 c
+9.815 3.028 l
+9.051 3.028 l
+9.051 3.69 l
+9.815 3.69 l
+9.815 4.896 l
+h
+13.994 -1.367 m
+13.375 -1.367 12.898 -1.186 12.567 -0.823 c
+12.244 -0.452 12.074 0.092 12.068 0.809 c
+12.068 1.411 l
+12.068 2.147 12.23 2.723 12.552 3.146 c
+12.876 3.564 13.325 3.778 13.905 3.778 c
+14.482 3.778 14.912 3.591 15.199 3.219 c
+15.492 2.856 15.643 2.282 15.654 1.5 c
+15.654 0.971 l
+12.876 0.971 l
+12.876 0.853 l
+12.876 0.312 12.972 -0.081 13.17 -0.323 c
+13.375 -0.558 13.662 -0.675 14.037 -0.675 c
+14.279 -0.675 14.493 -0.631 14.669 -0.544 c
+14.853 -0.455 15.026 -0.316 15.184 -0.118 c
+15.61 -0.631 l
+15.257 -1.124 14.717 -1.367 13.994 -1.367 c
+13.905 3.088 m
+13.57 3.088 13.321 2.97 13.155 2.735 c
+12.986 2.506 12.895 2.151 12.876 1.661 c
+14.831 1.661 l
+14.831 1.779 l
+14.809 2.249 14.728 2.58 14.581 2.779 c
+14.434 2.984 14.206 3.088 13.905 3.088 c
+17.212 3.69 m
+17.242 3.234 l
+17.543 3.595 17.94 3.778 18.432 3.778 c
+18.991 3.778 19.376 3.535 19.594 3.057 c
+19.906 3.535 20.344 3.778 20.901 3.778 c
+21.82 3.778 22.291 3.209 22.312 2.073 c
+22.312 -1.278 l
+21.504 -1.278 l
+21.504 1.999 l
+21.504 2.352 21.435 2.617 21.298 2.793 c
+21.169 2.97 20.953 3.057 20.652 3.057 c
+20.406 3.057 20.203 2.962 20.049 2.779 c
+19.902 2.591 19.814 2.352 19.785 2.058 c
+19.785 -1.278 l
+18.976 -1.278 l
+18.976 2.029 l
+18.965 2.712 18.686 3.057 18.138 3.057 c
+17.726 3.057 17.437 2.852 17.271 2.44 c
+17.271 -1.278 l
+16.463 -1.278 l
+16.463 3.69 l
+h
+28.942 2.926 m
+28.824 2.944 28.699 2.955 28.575 2.955 c
+28.152 2.955 27.862 2.727 27.708 2.278 c
+27.708 -1.278 l
+26.884 -1.278 l
+26.884 3.69 l
+27.677 3.69 l
+27.693 3.19 l
+27.906 3.58 28.215 3.778 28.618 3.778 c
+28.743 3.778 28.853 3.756 28.942 3.72 c
+h
+31.327 -1.367 m
+30.71 -1.367 30.232 -1.186 29.901 -0.823 c
+29.578 -0.452 29.408 0.092 29.401 0.809 c
+29.401 1.411 l
+29.401 2.147 29.563 2.723 29.886 3.146 c
+30.21 3.564 30.658 3.778 31.239 3.778 c
+31.816 3.778 32.246 3.591 32.533 3.219 c
+32.826 2.856 32.977 2.282 32.988 1.5 c
+32.988 0.971 l
+30.21 0.971 l
+30.21 0.853 l
+30.21 0.312 30.305 -0.081 30.503 -0.323 c
+30.71 -0.558 30.996 -0.675 31.371 -0.675 c
+31.614 -0.675 31.827 -0.631 32.003 -0.544 c
+32.186 -0.455 32.36 -0.316 32.518 -0.118 c
+32.944 -0.631 l
+32.591 -1.124 32.051 -1.367 31.327 -1.367 c
+31.239 3.088 m
+30.904 3.088 30.654 2.97 30.489 2.735 c
+30.32 2.506 30.228 2.151 30.21 1.661 c
+32.165 1.661 l
+32.165 1.779 l
+32.142 2.249 32.062 2.58 31.915 2.779 c
+31.768 2.984 31.54 3.088 31.239 3.088 c
+37.383 0.971 m
+37.383 0.184 37.239 -0.404 36.957 -0.793 c
+36.67 -1.176 36.269 -1.367 35.751 -1.367 c
+35.251 -1.367 34.869 -1.182 34.605 -0.808 c
+34.605 -3.189 l
+33.796 -3.189 l
+33.796 3.69 l
+34.532 3.69 l
+34.561 3.146 l
+34.833 3.564 35.226 3.778 35.737 3.778 c
+36.284 3.778 36.692 3.587 36.957 3.205 c
+37.229 2.83 37.372 2.268 37.383 1.515 c
+h
+36.574 1.426 m
+36.574 1.985 36.487 2.396 36.31 2.661 c
+36.14 2.926 35.869 3.057 35.487 3.057 c
+35.093 3.057 34.8 2.866 34.605 2.485 c
+34.605 -0.103 l
+34.8 -0.484 35.097 -0.675 35.502 -0.675 c
+35.862 -0.675 36.126 -0.544 36.296 -0.278 c
+36.472 -0.014 36.564 0.389 36.574 0.941 c
+h
+38.081 1.441 m
+38.081 2.153 38.25 2.723 38.596 3.146 c
+38.948 3.564 39.411 3.778 39.992 3.778 c
+40.569 3.778 41.029 3.572 41.374 3.161 c
+41.727 2.756 41.906 2.198 41.917 1.485 c
+41.917 0.971 l
+41.917 0.243 41.742 -0.33 41.389 -0.749 c
+41.043 -1.161 40.583 -1.367 40.007 -1.367 c
+39.426 -1.367 38.963 -1.168 38.611 -0.764 c
+38.265 -0.353 38.089 0.198 38.081 0.897 c
+h
+38.889 0.971 m
+38.889 0.46 38.985 0.059 39.184 -0.235 c
+39.39 -0.529 39.662 -0.675 40.007 -0.675 c
+40.72 -0.675 41.087 -0.158 41.109 0.882 c
+41.109 1.441 l
+41.109 1.948 41.006 2.352 40.801 2.646 c
+40.602 2.94 40.334 3.088 39.992 3.088 c
+39.658 3.088 39.39 2.94 39.184 2.646 c
+38.985 2.352 38.889 1.948 38.889 1.441 c
+h
+45.203 0 m
+45.203 0.166 45.133 0.312 44.997 0.441 c
+44.869 0.566 44.615 0.713 44.233 0.882 c
+43.799 1.066 43.49 1.224 43.307 1.353 c
+43.13 1.478 42.998 1.621 42.91 1.779 c
+42.822 1.945 42.777 2.139 42.777 2.367 c
+42.777 2.779 42.924 3.117 43.219 3.381 c
+43.512 3.645 43.887 3.778 44.35 3.778 c
+44.839 3.778 45.233 3.635 45.526 3.352 c
+45.821 3.076 45.967 2.72 45.967 2.278 c
+45.158 2.278 l
+45.158 2.503 45.077 2.694 44.923 2.852 c
+44.776 3.007 44.585 3.088 44.35 3.088 c
+44.115 3.088 43.928 3.021 43.791 2.897 c
+43.652 2.779 43.586 2.61 43.586 2.396 c
+43.586 2.228 43.633 2.095 43.733 1.999 c
+43.828 1.9 44.071 1.768 44.453 1.603 c
+45.06 1.368 45.471 1.135 45.688 0.912 c
+45.901 0.684 46.012 0.401 46.012 0.059 c
+46.012 -0.374 45.857 -0.72 45.555 -0.984 c
+45.262 -1.242 44.865 -1.367 44.365 -1.367 c
+43.854 -1.367 43.439 -1.219 43.116 -0.926 c
+42.792 -0.631 42.631 -0.257 42.631 0.206 c
+43.454 0.206 l
+43.461 -0.07 43.546 -0.286 43.704 -0.44 c
+43.858 -0.598 44.078 -0.675 44.365 -0.675 c
+44.637 -0.675 44.842 -0.617 44.982 -0.5 c
+45.129 -0.374 45.203 -0.205 45.203 0 c
+47.768 -1.278 -0.809 4.968 re
+47.826 5.013 m
+47.826 4.873 47.786 4.755 47.709 4.66 c
+47.639 4.572 47.525 4.528 47.371 4.528 c
+47.213 4.528 47.095 4.572 47.018 4.66 c
+46.949 4.755 46.916 4.873 46.916 5.013 c
+46.916 5.149 46.949 5.263 47.018 5.351 c
+47.095 5.446 47.209 5.498 47.356 5.498 c
+47.51 5.498 47.628 5.446 47.709 5.351 c
+47.786 5.263 47.826 5.149 47.826 5.013 c
+50.024 4.896 m
+50.024 3.69 l
+50.774 3.69 l
+50.774 3.028 l
+50.024 3.028 l
+50.024 -0.043 l
+50.024 -0.242 50.054 -0.393 50.112 -0.5 c
+50.178 -0.598 50.292 -0.646 50.45 -0.646 c
+50.557 -0.646 50.664 -0.628 50.774 -0.588 c
+50.759 -1.278 l
+50.583 -1.337 50.395 -1.367 50.201 -1.367 c
+49.877 -1.367 49.631 -1.257 49.466 -1.028 c
+49.297 -0.793 49.216 -0.47 49.216 -0.058 c
+49.216 3.028 l
+48.451 3.028 l
+48.451 3.69 l
+49.216 3.69 l
+49.216 4.896 l
+h
+51.365 1.441 m
+51.365 2.153 51.535 2.723 51.88 3.146 c
+52.233 3.564 52.695 3.778 53.276 3.778 c
+53.854 3.778 54.313 3.572 54.658 3.161 c
+55.011 2.756 55.19 2.198 55.202 1.485 c
+55.202 0.971 l
+55.202 0.243 55.026 -0.33 54.673 -0.749 c
+54.328 -1.161 53.868 -1.367 53.291 -1.367 c
+52.711 -1.367 52.248 -1.168 51.895 -0.764 c
+51.549 -0.353 51.373 0.198 51.365 0.897 c
+h
+52.173 0.971 m
+52.173 0.46 52.269 0.059 52.468 -0.235 c
+52.674 -0.529 52.946 -0.675 53.291 -0.675 c
+54.004 -0.675 54.371 -0.158 54.394 0.882 c
+54.394 1.441 l
+54.394 1.948 54.29 2.352 54.085 2.646 c
+53.887 2.94 53.618 3.088 53.276 3.088 c
+52.942 3.088 52.674 2.94 52.468 2.646 c
+52.269 2.352 52.173 1.948 52.173 1.441 c
+h
+58.116 2.926 m
+57.998 2.944 57.873 2.955 57.748 2.955 c
+57.326 2.955 57.035 2.727 56.881 2.278 c
+56.881 -1.278 l
+56.058 -1.278 l
+56.058 3.69 l
+56.852 3.69 l
+56.867 3.19 l
+57.079 3.58 57.388 3.778 57.792 3.778 c
+57.918 3.778 58.028 3.756 58.116 3.72 c
+h
+60.266 0.074 m
+61.162 3.69 l
+62.03 3.69 l
+60.398 -1.984 l
+60.281 -2.403 60.108 -2.726 59.884 -2.954 c
+59.656 -3.179 59.402 -3.293 59.119 -3.293 c
+59.009 -3.293 58.873 -3.266 58.708 -3.218 c
+58.708 -2.528 l
+58.884 -2.543 l
+59.119 -2.543 59.299 -2.484 59.427 -2.366 c
+59.564 -2.256 59.67 -2.065 59.751 -1.793 c
+59.913 -1.234 l
+58.458 3.69 l
+59.34 3.69 l
+h
+62.239 -0.837 m
+62.239 -0.69 62.284 -0.569 62.371 -0.47 c
+62.459 -0.374 62.585 -0.323 62.754 -0.323 c
+62.93 -0.323 63.062 -0.374 63.15 -0.47 c
+63.246 -0.569 63.298 -0.69 63.298 -0.837 c
+63.298 -0.977 63.246 -1.095 63.15 -1.19 c
+63.062 -1.278 62.93 -1.323 62.754 -1.323 c
+62.585 -1.323 62.459 -1.278 62.371 -1.19 c
+62.284 -1.095 62.239 -0.977 62.239 -0.837 c
+71.426 1.676 m
+69.162 1.676 l
+69.162 -1.278 l
+68.324 -1.278 l
+68.324 5.409 l
+71.809 5.409 l
+71.809 4.69 l
+69.162 4.69 l
+69.162 2.396 l
+71.426 2.396 l
+h
+72.297 1.441 m
+72.297 2.153 72.466 2.723 72.811 3.146 c
+73.164 3.564 73.627 3.778 74.207 3.778 c
+74.785 3.778 75.244 3.572 75.589 3.161 c
+75.942 2.756 76.122 2.198 76.133 1.485 c
+76.133 0.971 l
+76.133 0.243 75.957 -0.33 75.604 -0.749 c
+75.259 -1.161 74.799 -1.367 74.222 -1.367 c
+73.642 -1.367 73.179 -1.168 72.826 -0.764 c
+72.48 -0.353 72.304 0.198 72.297 0.897 c
+h
+73.105 0.971 m
+73.105 0.46 73.201 0.059 73.399 -0.235 c
+73.605 -0.529 73.877 -0.675 74.222 -0.675 c
+74.936 -0.675 75.302 -0.158 75.325 0.882 c
+75.325 1.441 l
+75.325 1.948 75.221 2.352 75.016 2.646 c
+74.818 2.94 74.55 3.088 74.207 3.088 c
+73.873 3.088 73.605 2.94 73.399 2.646 c
+73.201 2.352 73.105 1.948 73.105 1.441 c
+h
+79.051 2.926 m
+78.934 2.944 78.809 2.955 78.683 2.955 c
+78.261 2.955 77.97 2.727 77.816 2.278 c
+77.816 -1.278 l
+76.993 -1.278 l
+76.993 3.69 l
+77.787 3.69 l
+77.802 3.19 l
+78.015 3.58 78.323 3.778 78.728 3.778 c
+78.853 3.778 78.963 3.756 79.051 3.72 c
+h
+85.17 -1.367 m
+84.552 -1.367 84.075 -1.186 83.744 -0.823 c
+83.42 -0.452 83.252 0.092 83.244 0.809 c
+83.244 1.411 l
+83.244 2.147 83.406 2.723 83.729 3.146 c
+84.052 3.564 84.501 3.778 85.081 3.778 c
+85.658 3.778 86.088 3.591 86.375 3.219 c
+86.668 2.856 86.82 2.282 86.83 1.5 c
+86.83 0.971 l
+84.052 0.971 l
+84.052 0.853 l
+84.052 0.312 84.148 -0.081 84.347 -0.323 c
+84.552 -0.558 84.839 -0.675 85.214 -0.675 c
+85.456 -0.675 85.669 -0.631 85.845 -0.544 c
+86.03 -0.455 86.202 -0.316 86.36 -0.118 c
+86.786 -0.631 l
+86.433 -1.124 85.893 -1.367 85.17 -1.367 c
+85.081 3.088 m
+84.747 3.088 84.497 2.97 84.332 2.735 c
+84.162 2.506 84.071 2.151 84.052 1.661 c
+86.007 1.661 l
+86.007 1.779 l
+85.985 2.249 85.905 2.58 85.758 2.779 c
+85.61 2.984 85.382 3.088 85.081 3.088 c
+89.065 1.881 m
+89.918 3.69 l
+90.859 3.69 l
+89.506 1.235 l
+90.888 -1.278 l
+89.961 -1.278 l
+89.08 0.588 l
+88.197 -1.278 l
+87.256 -1.278 l
+88.638 1.235 l
+87.301 3.69 l
+88.227 3.69 l
+h
+94.151 -1.278 m
+94.099 -1.172 94.066 -0.992 94.048 -0.735 c
+93.761 -1.157 93.394 -1.367 92.945 -1.367 c
+92.494 -1.367 92.141 -1.246 91.887 -0.999 c
+91.641 -0.746 91.52 -0.389 91.52 0.074 c
+91.52 0.581 91.688 0.985 92.034 1.279 c
+92.376 1.573 92.846 1.723 93.445 1.735 c
+94.034 1.735 l
+94.034 2.264 l
+94.034 2.558 93.963 2.768 93.827 2.897 c
+93.699 3.021 93.504 3.088 93.24 3.088 c
+92.993 3.088 92.791 3.014 92.636 2.866 c
+92.49 2.72 92.416 2.532 92.416 2.309 c
+91.593 2.309 l
+91.593 2.562 91.667 2.808 91.813 3.043 c
+91.968 3.279 92.174 3.458 92.43 3.587 c
+92.685 3.712 92.968 3.778 93.284 3.778 c
+93.791 3.778 94.18 3.649 94.445 3.396 c
+94.709 3.15 94.842 2.782 94.842 2.294 c
+94.842 -0.205 l
+94.849 -0.588 94.904 -0.922 95.003 -1.205 c
+95.003 -1.278 l
+h
+93.078 -0.631 m
+93.272 -0.631 93.46 -0.58 93.636 -0.47 c
+93.82 -0.353 93.953 -0.216 94.034 -0.058 c
+94.034 1.133 l
+93.577 1.133 l
+93.195 1.121 92.891 1.033 92.666 0.867 c
+92.438 0.699 92.328 0.47 92.328 0.177 c
+92.328 -0.11 92.38 -0.316 92.49 -0.44 c
+92.607 -0.569 92.802 -0.631 93.078 -0.631 c
+96.679 3.69 m
+96.708 3.234 l
+97.01 3.595 97.407 3.778 97.899 3.778 c
+98.458 3.778 98.844 3.535 99.06 3.057 c
+99.372 3.535 99.81 3.778 100.368 3.778 c
+101.287 3.778 101.757 3.209 101.78 2.073 c
+101.78 -1.278 l
+100.971 -1.278 l
+100.971 1.999 l
+100.971 2.352 100.901 2.617 100.765 2.793 c
+100.637 2.97 100.42 3.057 100.118 3.057 c
+99.873 3.057 99.67 2.962 99.516 2.779 c
+99.368 2.591 99.281 2.352 99.251 2.058 c
+99.251 -1.278 l
+98.443 -1.278 l
+98.443 2.029 l
+98.431 2.712 98.152 3.057 97.605 3.057 c
+97.193 3.057 96.903 2.852 96.738 2.44 c
+96.738 -1.278 l
+95.929 -1.278 l
+95.929 3.69 l
+h
+106.306 0.971 m
+106.306 0.184 106.163 -0.404 105.88 -0.793 c
+105.594 -1.176 105.194 -1.367 104.675 -1.367 c
+104.176 -1.367 103.794 -1.182 103.528 -0.808 c
+103.528 -3.189 l
+102.72 -3.189 l
+102.72 3.69 l
+103.455 3.69 l
+103.484 3.146 l
+103.757 3.564 104.149 3.778 104.66 3.778 c
+105.208 3.778 105.616 3.587 105.88 3.205 c
+106.152 2.83 106.296 2.268 106.306 1.515 c
+h
+105.498 1.426 m
+105.498 1.985 105.41 2.396 105.234 2.661 c
+105.065 2.926 104.793 3.057 104.411 3.057 c
+104.018 3.057 103.723 2.866 103.528 2.485 c
+103.528 -0.103 l
+103.723 -0.484 104.021 -0.675 104.425 -0.675 c
+104.785 -0.675 105.05 -0.544 105.219 -0.278 c
+105.396 -0.014 105.487 0.389 105.498 0.941 c
+h
+108.049 -1.278 -0.808 7.056 re
+111.01 -1.367 m
+110.393 -1.367 109.915 -1.186 109.585 -0.823 c
+109.261 -0.452 109.092 0.092 109.085 0.809 c
+109.085 1.411 l
+109.085 2.147 109.246 2.723 109.57 3.146 c
+109.893 3.564 110.342 3.778 110.923 3.778 c
+111.499 3.778 111.929 3.591 112.216 3.219 c
+112.51 2.856 112.66 2.282 112.671 1.5 c
+112.671 0.971 l
+109.893 0.971 l
+109.893 0.853 l
+109.893 0.312 109.988 -0.081 110.187 -0.323 c
+110.393 -0.558 110.68 -0.675 111.054 -0.675 c
+111.297 -0.675 111.511 -0.631 111.686 -0.544 c
+111.87 -0.455 112.043 -0.316 112.201 -0.118 c
+112.627 -0.631 l
+112.274 -1.124 111.734 -1.367 111.01 -1.367 c
+110.923 3.088 m
+110.588 3.088 110.338 2.97 110.173 2.735 c
+110.003 2.506 109.911 2.151 109.893 1.661 c
+111.848 1.661 l
+111.848 1.779 l
+111.827 2.249 111.746 2.58 111.598 2.779 c
+111.451 2.984 111.224 3.088 110.923 3.088 c
+117.992 -1.278 -0.808 4.968 re
+118.052 5.013 m
+118.052 4.873 118.011 4.755 117.934 4.66 c
+117.864 4.572 117.75 4.528 117.595 4.528 c
+117.437 4.528 117.32 4.572 117.242 4.66 c
+117.173 4.755 117.14 4.873 117.14 5.013 c
+117.14 5.149 117.173 5.263 117.242 5.351 c
+117.32 5.446 117.434 5.498 117.581 5.498 c
+117.735 5.498 117.853 5.446 117.934 5.351 c
+118.011 5.263 118.052 5.149 118.052 5.013 c
+119.94 3.69 m
+119.97 3.161 l
+120.282 3.572 120.683 3.778 121.175 3.778 c
+122.057 3.778 122.501 3.194 122.513 2.029 c
+122.513 -1.278 l
+121.704 -1.278 l
+121.704 1.985 l
+121.704 2.374 121.638 2.65 121.513 2.808 c
+121.385 2.974 121.19 3.057 120.925 3.057 c
+120.719 3.057 120.535 2.988 120.381 2.852 c
+120.223 2.723 120.095 2.55 119.999 2.338 c
+119.999 -1.278 l
+119.176 -1.278 l
+119.176 3.69 l
+h
+127.315 -1.278 m
+127.315 5.409 l
+128.918 5.409 l
+129.689 5.409 130.292 5.164 130.725 4.675 c
+131.155 4.183 131.372 3.499 131.372 2.617 c
+131.372 1.5 l
+131.372 0.607 131.152 -0.081 130.711 -0.558 c
+130.277 -1.039 129.645 -1.278 128.815 -1.278 c
+h
+128.153 4.69 m
+128.153 -0.558 l
+128.83 -0.558 l
+129.425 -0.558 129.859 -0.393 130.123 -0.058 c
+130.395 0.283 130.538 0.786 130.549 1.455 c
+130.549 2.646 l
+130.549 3.341 130.41 3.855 130.137 4.19 c
+129.862 4.52 129.454 4.69 128.918 4.69 c
+h
+134.125 -1.367 m
+133.507 -1.367 133.03 -1.186 132.699 -0.823 c
+132.375 -0.452 132.207 0.092 132.199 0.809 c
+132.199 1.411 l
+132.199 2.147 132.361 2.723 132.685 3.146 c
+133.007 3.564 133.456 3.778 134.037 3.778 c
+134.613 3.778 135.043 3.591 135.33 3.219 c
+135.624 2.856 135.775 2.282 135.785 1.5 c
+135.785 0.971 l
+133.007 0.971 l
+133.007 0.853 l
+133.007 0.312 133.103 -0.081 133.302 -0.323 c
+133.507 -0.558 133.794 -0.675 134.169 -0.675 c
+134.411 -0.675 134.625 -0.631 134.801 -0.544 c
+134.985 -0.455 135.157 -0.316 135.315 -0.118 c
+135.742 -0.631 l
+135.388 -1.124 134.848 -1.367 134.125 -1.367 c
+134.037 3.088 m
+133.703 3.088 133.452 2.97 133.287 2.735 c
+133.117 2.506 133.026 2.151 133.007 1.661 c
+134.962 1.661 l
+134.962 1.779 l
+134.941 2.249 134.86 2.58 134.713 2.779 c
+134.566 2.984 134.338 3.088 134.037 3.088 c
+140.181 0.971 m
+140.181 0.195 140.038 -0.389 139.754 -0.779 c
+139.479 -1.172 139.078 -1.367 138.549 -1.367 c
+138.027 -1.367 137.638 -1.142 137.373 -0.69 c
+137.329 -1.278 l
+136.594 -1.278 l
+136.594 5.777 l
+137.403 5.777 l
+137.403 3.146 l
+137.667 3.564 138.05 3.778 138.549 3.778 c
+139.086 3.778 139.494 3.587 139.769 3.205 c
+140.041 2.822 140.181 2.238 140.181 1.455 c
+h
+139.372 1.426 m
+139.372 2.014 139.288 2.429 139.122 2.675 c
+138.964 2.929 138.7 3.057 138.329 3.057 c
+137.917 3.057 137.609 2.83 137.403 2.382 c
+137.403 0.015 l
+137.597 -0.426 137.91 -0.646 138.343 -0.646 c
+138.704 -0.646 138.964 -0.521 139.122 -0.264 c
+139.288 -0.01 139.372 0.387 139.372 0.927 c
+h
+141.927 -1.278 -0.809 4.968 re
+141.985 5.013 m
+141.985 4.873 141.944 4.755 141.867 4.66 c
+141.798 4.572 141.684 4.528 141.53 4.528 c
+141.372 4.528 141.254 4.572 141.177 4.66 c
+141.107 4.755 141.073 4.873 141.073 5.013 c
+141.073 5.149 141.107 5.263 141.177 5.351 c
+141.254 5.446 141.368 5.498 141.514 5.498 c
+141.669 5.498 141.786 5.446 141.867 5.351 c
+141.944 5.263 141.985 5.149 141.985 5.013 c
+145.663 -1.278 m
+145.612 -1.172 145.579 -0.992 145.561 -0.735 c
+145.274 -1.157 144.907 -1.367 144.458 -1.367 c
+144.007 -1.367 143.654 -1.246 143.4 -0.999 c
+143.153 -0.746 143.033 -0.389 143.033 0.074 c
+143.033 0.581 143.201 0.985 143.547 1.279 c
+143.889 1.573 144.359 1.723 144.958 1.735 c
+145.546 1.735 l
+145.546 2.264 l
+145.546 2.558 145.476 2.768 145.34 2.897 c
+145.212 3.021 145.017 3.088 144.753 3.088 c
+144.506 3.088 144.304 3.014 144.149 2.866 c
+144.003 2.72 143.929 2.532 143.929 2.309 c
+143.106 2.309 l
+143.106 2.562 143.18 2.808 143.326 3.043 c
+143.481 3.279 143.687 3.458 143.943 3.587 c
+144.198 3.712 144.481 3.778 144.796 3.778 c
+145.304 3.778 145.693 3.649 145.958 3.396 c
+146.222 3.15 146.355 2.782 146.355 2.294 c
+146.355 -0.205 l
+146.362 -0.588 146.417 -0.922 146.516 -1.205 c
+146.516 -1.278 l
+h
+144.591 -0.631 m
+144.785 -0.631 144.973 -0.58 145.149 -0.47 c
+145.333 -0.353 145.465 -0.216 145.546 -0.058 c
+145.546 1.133 l
+145.09 1.133 l
+144.708 1.121 144.404 1.033 144.179 0.867 c
+143.951 0.699 143.841 0.47 143.841 0.177 c
+143.841 -0.11 143.893 -0.316 144.003 -0.44 c
+144.12 -0.569 144.315 -0.631 144.591 -0.631 c
+148.192 3.69 m
+148.221 3.161 l
+148.533 3.572 148.934 3.778 149.426 3.778 c
+150.309 3.778 150.754 3.194 150.764 2.029 c
+150.764 -1.278 l
+149.956 -1.278 l
+149.956 1.985 l
+149.956 2.374 149.89 2.65 149.765 2.808 c
+149.636 2.974 149.441 3.057 149.177 3.057 c
+148.971 3.057 148.787 2.988 148.633 2.852 c
+148.475 2.723 148.346 2.55 148.25 2.338 c
+148.25 -1.278 l
+147.427 -1.278 l
+147.427 3.69 l
+h
+152.039 -1.851 m
+151.348 -1.851 l
+153.686 5.409 l
+154.376 5.409 l
+h
+159.257 5.409 m
+159.257 0.662 l
+159.257 0.023 159.072 -0.477 158.712 -0.837 c
+158.349 -1.19 157.846 -1.367 157.198 -1.367 c
+156.541 -1.367 156.038 -1.198 155.685 -0.852 c
+155.332 -0.5 155.155 0.004 155.155 0.662 c
+155.155 5.409 l
+155.993 5.409 l
+155.993 0.691 l
+155.993 0.239 156.085 -0.099 156.273 -0.323 c
+156.468 -0.54 156.776 -0.646 157.198 -0.646 c
+157.628 -0.646 157.937 -0.54 158.124 -0.323 c
+158.32 -0.099 158.419 0.239 158.419 0.691 c
+158.419 5.409 l
+h
+163.835 0.971 m
+163.835 0.195 163.691 -0.389 163.409 -0.779 c
+163.134 -1.172 162.733 -1.367 162.203 -1.367 c
+161.682 -1.367 161.293 -1.142 161.027 -0.69 c
+160.984 -1.278 l
+160.248 -1.278 l
+160.248 5.777 l
+161.057 5.777 l
+161.057 3.146 l
+161.322 3.564 161.704 3.778 162.203 3.778 c
+162.74 3.778 163.148 3.587 163.423 3.205 c
+163.695 2.822 163.835 2.238 163.835 1.455 c
+h
+163.026 1.426 m
+163.026 2.014 162.943 2.429 162.777 2.675 c
+162.619 2.929 162.355 3.057 161.983 3.057 c
+161.572 3.057 161.262 2.83 161.057 2.382 c
+161.057 0.015 l
+161.252 -0.426 161.565 -0.646 161.998 -0.646 c
+162.357 -0.646 162.619 -0.521 162.777 -0.264 c
+162.943 -0.01 163.026 0.387 163.026 0.927 c
+h
+167.242 -0.837 m
+166.966 -1.19 166.569 -1.367 166.051 -1.367 c
+165.611 -1.367 165.272 -1.216 165.037 -0.911 c
+164.809 -0.598 164.691 -0.143 164.684 0.456 c
+164.684 3.69 l
+165.492 3.69 l
+165.492 0.515 l
+165.492 -0.261 165.727 -0.646 166.198 -0.646 c
+166.687 -0.646 167.024 -0.426 167.213 0.015 c
+167.213 3.69 l
+168.036 3.69 l
+168.036 -1.278 l
+167.257 -1.278 l
+h
+169.836 3.69 m
+169.866 3.161 l
+170.178 3.572 170.579 3.778 171.071 3.778 c
+171.953 3.778 172.397 3.194 172.408 2.029 c
+172.408 -1.278 l
+171.6 -1.278 l
+171.6 1.985 l
+171.6 2.374 171.534 2.65 171.409 2.808 c
+171.28 2.974 171.086 3.057 170.821 3.057 c
+170.615 3.057 170.431 2.988 170.277 2.852 c
+170.119 2.723 169.991 2.55 169.895 2.338 c
+169.895 -1.278 l
+169.072 -1.278 l
+169.072 3.69 l
+h
+174.518 4.896 m
+174.518 3.69 l
+175.267 3.69 l
+175.267 3.028 l
+174.518 3.028 l
+174.518 -0.043 l
+174.518 -0.242 174.548 -0.393 174.606 -0.5 c
+174.672 -0.598 174.786 -0.646 174.944 -0.646 c
+175.051 -0.646 175.157 -0.628 175.267 -0.588 c
+175.253 -1.278 l
+175.076 -1.337 174.889 -1.367 174.694 -1.367 c
+174.371 -1.367 174.124 -1.257 173.959 -1.028 c
+173.79 -0.793 173.709 -0.47 173.709 -0.058 c
+173.709 3.028 l
+172.945 3.028 l
+172.945 3.69 l
+173.709 3.69 l
+173.709 4.896 l
+h
+178.652 -0.837 m
+178.376 -1.19 177.979 -1.367 177.461 -1.367 c
+177.02 -1.367 176.682 -1.216 176.447 -0.911 c
+176.219 -0.598 176.102 -0.143 176.094 0.456 c
+176.094 3.69 l
+176.902 3.69 l
+176.902 0.515 l
+176.902 -0.261 177.137 -0.646 177.608 -0.646 c
+178.097 -0.646 178.435 -0.426 178.623 0.015 c
+178.623 3.69 l
+179.446 3.69 l
+179.446 -1.278 l
+178.666 -1.278 l
+h
+187.732 0.971 m
+187.732 0.184 187.589 -0.404 187.306 -0.793 c
+187.019 -1.176 186.619 -1.367 186.101 -1.367 c
+185.601 -1.367 185.218 -1.182 184.954 -0.808 c
+184.954 -3.189 l
+184.146 -3.189 l
+184.146 3.69 l
+184.881 3.69 l
+184.91 3.146 l
+185.182 3.564 185.575 3.778 186.086 3.778 c
+186.633 3.778 187.042 3.587 187.306 3.205 c
+187.578 2.83 187.721 2.268 187.732 1.515 c
+h
+186.924 1.426 m
+186.924 1.985 186.836 2.396 186.659 2.661 c
+186.49 2.926 186.218 3.057 185.836 3.057 c
+185.443 3.057 185.149 2.866 184.954 2.485 c
+184.954 -0.103 l
+185.149 -0.484 185.446 -0.675 185.851 -0.675 c
+186.211 -0.675 186.475 -0.544 186.645 -0.278 c
+186.82 -0.014 186.913 0.389 186.924 0.941 c
+h
+189.474 -1.278 -0.809 7.056 re
+192.436 -1.367 m
+191.819 -1.367 191.341 -1.186 191.011 -0.823 c
+190.687 -0.452 190.518 0.092 190.51 0.809 c
+190.51 1.411 l
+190.51 2.147 190.672 2.723 190.995 3.146 c
+191.319 3.564 191.767 3.778 192.347 3.778 c
+192.925 3.778 193.355 3.591 193.641 3.219 c
+193.935 2.856 194.086 2.282 194.097 1.5 c
+194.097 0.971 l
+191.319 0.971 l
+191.319 0.853 l
+191.319 0.312 191.414 -0.081 191.613 -0.323 c
+191.819 -0.558 192.106 -0.675 192.48 -0.675 c
+192.723 -0.675 192.935 -0.631 193.112 -0.544 c
+193.296 -0.455 193.469 -0.316 193.627 -0.118 c
+194.053 -0.631 l
+193.7 -1.124 193.16 -1.367 192.436 -1.367 c
+192.347 3.088 m
+192.013 3.088 191.763 2.97 191.598 2.735 c
+191.429 2.506 191.337 2.151 191.319 1.661 c
+193.274 1.661 l
+193.274 1.779 l
+193.251 2.249 193.17 2.58 193.024 2.779 c
+192.877 2.984 192.649 3.088 192.347 3.088 c
+197.448 -1.278 m
+197.397 -1.172 197.363 -0.992 197.346 -0.735 c
+197.059 -1.157 196.692 -1.367 196.243 -1.367 c
+195.791 -1.367 195.439 -1.246 195.185 -0.999 c
+194.938 -0.746 194.817 -0.389 194.817 0.074 c
+194.817 0.581 194.986 0.985 195.331 1.279 c
+195.674 1.573 196.144 1.723 196.742 1.735 c
+197.33 1.735 l
+197.33 2.264 l
+197.33 2.558 197.261 2.768 197.125 2.897 c
+196.997 3.021 196.802 3.088 196.536 3.088 c
+196.291 3.088 196.089 3.014 195.934 2.866 c
+195.788 2.72 195.714 2.532 195.714 2.309 c
+194.89 2.309 l
+194.89 2.562 194.964 2.808 195.111 3.043 c
+195.266 3.279 195.472 3.458 195.728 3.587 c
+195.982 3.712 196.265 3.778 196.581 3.778 c
+197.088 3.778 197.477 3.649 197.742 3.396 c
+198.007 3.15 198.139 2.782 198.139 2.294 c
+198.139 -0.205 l
+198.146 -0.588 198.202 -0.922 198.301 -1.205 c
+198.301 -1.278 l
+h
+196.376 -0.631 m
+196.57 -0.631 196.758 -0.58 196.933 -0.47 c
+197.118 -0.353 197.25 -0.216 197.33 -0.058 c
+197.33 1.133 l
+196.875 1.133 l
+196.493 1.121 196.187 1.033 195.964 0.867 c
+195.736 0.699 195.626 0.47 195.626 0.177 c
+195.626 -0.11 195.677 -0.316 195.788 -0.44 c
+195.905 -0.569 196.1 -0.631 196.376 -0.631 c
+201.637 0 m
+201.637 0.166 201.568 0.312 201.432 0.441 c
+201.303 0.566 201.049 0.713 200.667 0.882 c
+200.234 1.066 199.925 1.224 199.742 1.353 c
+199.565 1.478 199.433 1.621 199.345 1.779 c
+199.256 1.945 199.212 2.139 199.212 2.367 c
+199.212 2.779 199.359 3.117 199.653 3.381 c
+199.948 3.645 200.322 3.778 200.785 3.778 c
+201.274 3.778 201.667 3.635 201.961 3.352 c
+202.255 3.076 202.402 2.72 202.402 2.278 c
+201.594 2.278 l
+201.594 2.503 201.513 2.694 201.359 2.852 c
+201.211 3.007 201.02 3.088 200.785 3.088 c
+200.55 3.088 200.362 3.021 200.226 2.897 c
+200.087 2.779 200.021 2.61 200.021 2.396 c
+200.021 2.228 200.068 2.095 200.168 1.999 c
+200.263 1.9 200.505 1.768 200.888 1.603 c
+201.494 1.368 201.906 1.135 202.123 0.912 c
+202.336 0.684 202.446 0.401 202.446 0.059 c
+202.446 -0.374 202.292 -0.72 201.99 -0.984 c
+201.696 -1.242 201.299 -1.367 200.8 -1.367 c
+200.289 -1.367 199.873 -1.219 199.551 -0.926 c
+199.227 -0.631 199.065 -0.257 199.065 0.206 c
+199.888 0.206 l
+199.896 -0.07 199.981 -0.286 200.139 -0.44 c
+200.293 -0.598 200.513 -0.675 200.8 -0.675 c
+201.072 -0.675 201.278 -0.617 201.417 -0.5 c
+201.564 -0.374 201.637 -0.205 201.637 0 c
+205.099 -1.367 m
+204.482 -1.367 204.004 -1.186 203.673 -0.823 c
+203.35 -0.452 203.181 0.092 203.173 0.809 c
+203.173 1.411 l
+203.173 2.147 203.335 2.723 203.659 3.146 c
+203.982 3.564 204.43 3.778 205.011 3.778 c
+205.588 3.778 206.018 3.591 206.304 3.219 c
+206.599 2.856 206.749 2.282 206.76 1.5 c
+206.76 0.971 l
+203.982 0.971 l
+203.982 0.853 l
+203.982 0.312 204.077 -0.081 204.276 -0.323 c
+204.482 -0.558 204.768 -0.675 205.143 -0.675 c
+205.386 -0.675 205.599 -0.631 205.775 -0.544 c
+205.959 -0.455 206.132 -0.316 206.29 -0.118 c
+206.716 -0.631 l
+206.363 -1.124 205.823 -1.367 205.099 -1.367 c
+205.011 3.088 m
+204.677 3.088 204.426 2.97 204.261 2.735 c
+204.092 2.506 204 2.151 203.982 1.661 c
+205.937 1.661 l
+205.937 1.779 l
+205.915 2.249 205.834 2.58 205.687 2.779 c
+205.54 2.984 205.313 3.088 205.011 3.088 c
+212.276 4.896 m
+212.276 3.69 l
+213.026 3.69 l
+213.026 3.028 l
+212.276 3.028 l
+212.276 -0.043 l
+212.276 -0.242 212.305 -0.393 212.364 -0.5 c
+212.43 -0.598 212.544 -0.646 212.702 -0.646 c
+212.808 -0.646 212.916 -0.628 213.026 -0.588 c
+213.011 -1.278 l
+212.835 -1.337 212.648 -1.367 212.452 -1.367 c
+212.129 -1.367 211.883 -1.257 211.717 -1.028 c
+211.549 -0.793 211.468 -0.47 211.468 -0.058 c
+211.468 3.028 l
+210.703 3.028 l
+210.703 3.69 l
+211.468 3.69 l
+211.468 4.896 l
+h
+215.235 0.074 m
+216.131 3.69 l
+216.998 3.69 l
+215.366 -1.984 l
+215.249 -2.403 215.077 -2.726 214.852 -2.954 c
+214.624 -3.179 214.371 -3.293 214.088 -3.293 c
+213.978 -3.293 213.841 -3.266 213.676 -3.218 c
+213.676 -2.528 l
+213.853 -2.543 l
+214.088 -2.543 214.268 -2.484 214.396 -2.366 c
+214.532 -2.256 214.639 -2.065 214.72 -1.793 c
+214.881 -1.234 l
+213.427 3.69 l
+214.308 3.69 l
+h
+221.224 0.971 m
+221.224 0.184 221.08 -0.404 220.798 -0.793 c
+220.511 -1.176 220.11 -1.367 219.592 -1.367 c
+219.093 -1.367 218.711 -1.182 218.446 -0.808 c
+218.446 -3.189 l
+217.637 -3.189 l
+217.637 3.69 l
+218.372 3.69 l
+218.402 3.146 l
+218.674 3.564 219.067 3.778 219.578 3.778 c
+220.126 3.778 220.533 3.587 220.798 3.205 c
+221.07 2.83 221.213 2.268 221.224 1.515 c
+h
+220.415 1.426 m
+220.415 1.985 220.328 2.396 220.151 2.661 c
+219.982 2.926 219.71 3.057 219.328 3.057 c
+218.935 3.057 218.641 2.866 218.446 2.485 c
+218.446 -0.103 l
+218.641 -0.484 218.938 -0.675 219.343 -0.675 c
+219.703 -0.675 219.967 -0.544 220.136 -0.278 c
+220.313 -0.014 220.405 0.389 220.415 0.941 c
+h
+223.862 -1.367 m
+223.245 -1.367 222.767 -1.186 222.437 -0.823 c
+222.113 -0.452 221.944 0.092 221.936 0.809 c
+221.936 1.411 l
+221.936 2.147 222.098 2.723 222.422 3.146 c
+222.745 3.564 223.193 3.778 223.775 3.778 c
+224.351 3.778 224.781 3.591 225.068 3.219 c
+225.362 2.856 225.512 2.282 225.523 1.5 c
+225.523 0.971 l
+222.745 0.971 l
+222.745 0.853 l
+222.745 0.312 222.84 -0.081 223.039 -0.323 c
+223.245 -0.558 223.532 -0.675 223.906 -0.675 c
+224.149 -0.675 224.363 -0.631 224.539 -0.544 c
+224.722 -0.455 224.895 -0.316 225.053 -0.118 c
+225.479 -0.631 l
+225.126 -1.124 224.586 -1.367 223.862 -1.367 c
+223.775 3.088 m
+223.44 3.088 223.19 2.97 223.025 2.735 c
+222.856 2.506 222.763 2.151 222.745 1.661 c
+224.7 1.661 l
+224.7 1.779 l
+224.679 2.249 224.598 2.58 224.45 2.779 c
+224.303 2.984 224.076 3.088 223.775 3.088 c
+230.848 -1.278 -0.808 4.968 re
+230.907 5.013 m
+230.907 4.873 230.867 4.755 230.79 4.66 c
+230.719 4.572 230.605 4.528 230.451 4.528 c
+230.293 4.528 230.175 4.572 230.098 4.66 c
+230.029 4.755 229.996 4.873 229.996 5.013 c
+229.996 5.149 230.029 5.263 230.098 5.351 c
+230.175 5.446 230.289 5.498 230.437 5.498 c
+230.591 5.498 230.709 5.446 230.79 5.351 c
+230.867 5.263 230.907 5.149 230.907 5.013 c
+232.793 3.69 m
+232.822 3.161 l
+233.134 3.572 233.535 3.778 234.027 3.778 c
+234.909 3.778 235.353 3.194 235.365 2.029 c
+235.365 -1.278 l
+234.556 -1.278 l
+234.556 1.985 l
+234.556 2.374 234.49 2.65 234.365 2.808 c
+234.237 2.974 234.042 3.057 233.777 3.057 c
+233.571 3.057 233.387 2.988 233.233 2.852 c
+233.075 2.723 232.947 2.55 232.851 2.338 c
+232.851 -1.278 l
+232.028 -1.278 l
+232.028 3.69 l
+h
+f
+Q
+q 1 0 0 1 537.1847 388.598 cm
+0 0 m
+0 -1.206 l
+0.75 -1.206 l
+0.75 -1.867 l
+0 -1.867 l
+0 -4.939 l
+0 -5.137 0.029 -5.289 0.088 -5.395 c
+0.154 -5.494 0.268 -5.542 0.426 -5.542 c
+0.532 -5.542 0.64 -5.524 0.75 -5.484 c
+0.735 -6.174 l
+0.559 -6.232 0.372 -6.263 0.177 -6.263 c
+-0.147 -6.263 -0.393 -6.152 -0.559 -5.924 c
+-0.727 -5.689 -0.808 -5.366 -0.808 -4.954 c
+-0.808 -1.867 l
+-1.573 -1.867 l
+-1.573 -1.206 l
+-0.808 -1.206 l
+-0.808 0 l
+h
+2.414 -1.721 m
+2.716 -1.32 3.109 -1.118 3.59 -1.118 c
+4.472 -1.118 4.917 -1.702 4.928 -2.866 c
+4.928 -6.174 l
+4.12 -6.174 l
+4.12 -2.911 l
+4.12 -2.521 4.054 -2.246 3.929 -2.087 c
+3.8 -1.922 3.605 -1.838 3.341 -1.838 c
+3.135 -1.838 2.951 -1.908 2.797 -2.043 c
+2.639 -2.172 2.51 -2.345 2.414 -2.558 c
+2.414 -6.174 l
+1.591 -6.174 l
+1.591 0.881 l
+2.414 0.881 l
+h
+7.724 -6.263 m
+7.107 -6.263 6.629 -6.082 6.298 -5.719 c
+5.976 -5.347 5.806 -4.803 5.799 -4.087 c
+5.799 -3.485 l
+5.799 -2.749 5.961 -2.172 6.284 -1.75 c
+6.607 -1.331 7.056 -1.118 7.636 -1.118 c
+8.214 -1.118 8.644 -1.305 8.93 -1.676 c
+9.224 -2.04 9.374 -2.613 9.386 -3.396 c
+9.386 -3.925 l
+6.607 -3.925 l
+6.607 -4.042 l
+6.607 -4.583 6.703 -4.977 6.901 -5.218 c
+7.107 -5.453 7.393 -5.571 7.769 -5.571 c
+8.011 -5.571 8.224 -5.527 8.401 -5.439 c
+8.584 -5.351 8.757 -5.212 8.915 -5.013 c
+9.342 -5.527 l
+8.989 -6.02 8.449 -6.263 7.724 -6.263 c
+7.636 -1.808 m
+7.302 -1.808 7.052 -1.926 6.886 -2.161 c
+6.718 -2.389 6.626 -2.745 6.607 -3.234 c
+8.563 -3.234 l
+8.563 -3.117 l
+8.54 -2.646 8.459 -2.315 8.312 -2.117 c
+8.166 -1.912 7.938 -1.808 7.636 -1.808 c
+13.317 0 m
+13.317 -1.206 l
+14.067 -1.206 l
+14.067 -1.867 l
+13.317 -1.867 l
+13.317 -4.939 l
+13.317 -5.137 13.347 -5.289 13.406 -5.395 c
+13.471 -5.494 13.585 -5.542 13.743 -5.542 c
+13.851 -5.542 13.957 -5.524 14.067 -5.484 c
+14.053 -6.174 l
+13.876 -6.232 13.689 -6.263 13.494 -6.263 c
+13.17 -6.263 12.924 -6.152 12.759 -5.924 c
+12.59 -5.689 12.509 -5.366 12.509 -4.954 c
+12.509 -1.867 l
+11.744 -1.867 l
+11.744 -1.206 l
+12.509 -1.206 l
+12.509 0 l
+h
+16.687 -6.263 m
+16.07 -6.263 15.592 -6.082 15.262 -5.719 c
+14.938 -5.347 14.769 -4.803 14.761 -4.087 c
+14.761 -3.485 l
+14.761 -2.749 14.923 -2.172 15.247 -1.75 c
+15.57 -1.331 16.018 -1.118 16.599 -1.118 c
+17.176 -1.118 17.606 -1.305 17.893 -1.676 c
+18.186 -2.04 18.337 -2.613 18.348 -3.396 c
+18.348 -3.925 l
+15.57 -3.925 l
+15.57 -4.042 l
+15.57 -4.583 15.665 -4.977 15.864 -5.218 c
+16.07 -5.453 16.357 -5.571 16.731 -5.571 c
+16.974 -5.571 17.187 -5.527 17.363 -5.439 c
+17.547 -5.351 17.72 -5.212 17.878 -5.013 c
+18.304 -5.527 l
+17.951 -6.02 17.411 -6.263 16.687 -6.263 c
+16.599 -1.808 m
+16.264 -1.808 16.014 -1.926 15.85 -2.161 c
+15.68 -2.389 15.588 -2.745 15.57 -3.234 c
+17.525 -3.234 l
+17.525 -3.117 l
+17.503 -2.646 17.422 -2.315 17.275 -2.117 c
+17.128 -1.912 16.9 -1.808 16.599 -1.808 c
+21.2 -1.97 m
+21.082 -1.952 20.957 -1.941 20.833 -1.941 c
+20.409 -1.941 20.12 -2.168 19.965 -2.617 c
+19.965 -6.174 l
+19.142 -6.174 l
+19.142 -1.206 l
+19.935 -1.206 l
+19.95 -1.706 l
+20.164 -1.316 20.472 -1.118 20.876 -1.118 c
+21.001 -1.118 21.111 -1.139 21.2 -1.176 c
+h
+22.655 -1.206 m
+22.684 -1.661 l
+22.986 -1.301 23.382 -1.118 23.875 -1.118 c
+24.434 -1.118 24.82 -1.36 25.036 -1.838 c
+25.348 -1.36 25.786 -1.118 26.344 -1.118 c
+27.264 -1.118 27.734 -1.687 27.756 -2.822 c
+27.756 -6.174 l
+26.948 -6.174 l
+26.948 -2.897 l
+26.948 -2.544 26.877 -2.278 26.741 -2.103 c
+26.613 -1.926 26.396 -1.838 26.094 -1.838 c
+25.849 -1.838 25.646 -1.933 25.492 -2.117 c
+25.344 -2.305 25.257 -2.544 25.227 -2.837 c
+25.227 -6.174 l
+24.419 -6.174 l
+24.419 -2.866 l
+24.407 -2.183 24.128 -1.838 23.581 -1.838 c
+23.169 -1.838 22.879 -2.043 22.714 -2.455 c
+22.714 -6.174 l
+21.905 -6.174 l
+21.905 -1.206 l
+h
+29.564 -6.174 -0.809 4.968 re
+29.622 0.118 m
+29.622 -0.023 29.582 -0.14 29.504 -0.235 c
+29.435 -0.324 29.321 -0.368 29.167 -0.368 c
+29.009 -0.368 28.891 -0.324 28.814 -0.235 c
+28.744 -0.14 28.711 -0.023 28.711 0.118 c
+28.711 0.253 28.744 0.367 28.814 0.455 c
+28.891 0.55 29.005 0.602 29.152 0.602 c
+29.306 0.602 29.424 0.55 29.504 0.455 c
+29.582 0.367 29.622 0.253 29.622 0.118 c
+31.511 -1.206 m
+31.54 -1.735 l
+31.852 -1.324 32.253 -1.118 32.746 -1.118 c
+33.628 -1.118 34.073 -1.702 34.084 -2.866 c
+34.084 -6.174 l
+33.275 -6.174 l
+33.275 -2.911 l
+33.275 -2.521 33.209 -2.246 33.084 -2.087 c
+32.955 -1.922 32.76 -1.838 32.496 -1.838 c
+32.29 -1.838 32.107 -1.908 31.952 -2.043 c
+31.794 -2.172 31.665 -2.345 31.57 -2.558 c
+31.57 -6.174 l
+30.747 -6.174 l
+30.747 -1.206 l
+h
+37.678 -6.174 m
+37.626 -6.068 37.593 -5.887 37.574 -5.63 c
+37.288 -6.053 36.92 -6.263 36.472 -6.263 c
+36.02 -6.263 35.667 -6.141 35.414 -5.895 c
+35.168 -5.642 35.046 -5.285 35.046 -4.821 c
+35.046 -4.314 35.216 -3.911 35.561 -3.616 c
+35.902 -3.323 36.373 -3.172 36.972 -3.161 c
+37.56 -3.161 l
+37.56 -2.631 l
+37.56 -2.338 37.49 -2.128 37.354 -1.999 c
+37.225 -1.875 37.03 -1.808 36.766 -1.808 c
+36.519 -1.808 36.317 -1.881 36.163 -2.029 c
+36.016 -2.176 35.943 -2.363 35.943 -2.587 c
+35.12 -2.587 l
+35.12 -2.334 35.193 -2.087 35.34 -1.852 c
+35.495 -1.617 35.7 -1.437 35.958 -1.309 c
+36.211 -1.183 36.494 -1.118 36.81 -1.118 c
+37.317 -1.118 37.707 -1.246 37.971 -1.5 c
+38.236 -1.746 38.368 -2.114 38.368 -2.602 c
+38.368 -5.101 l
+38.376 -5.484 38.431 -5.818 38.53 -6.101 c
+38.53 -6.174 l
+h
+36.604 -5.527 m
+36.799 -5.527 36.986 -5.476 37.163 -5.366 c
+37.346 -5.248 37.479 -5.112 37.56 -4.954 c
+37.56 -3.763 l
+37.104 -3.763 l
+36.722 -3.774 36.417 -3.863 36.193 -4.028 c
+35.965 -4.197 35.854 -4.425 35.854 -4.719 c
+35.854 -5.006 35.906 -5.212 36.016 -5.336 c
+36.134 -5.465 36.328 -5.527 36.604 -5.527 c
+40.323 -6.174 -0.808 7.056 re
+41.566 -5.733 m
+41.566 -5.586 41.61 -5.465 41.697 -5.366 c
+41.786 -5.27 41.911 -5.218 42.08 -5.218 c
+42.256 -5.218 42.389 -5.27 42.476 -5.366 c
+42.572 -5.465 42.624 -5.586 42.624 -5.733 c
+42.624 -5.873 42.572 -5.991 42.476 -6.086 c
+42.389 -6.174 42.256 -6.218 42.08 -6.218 c
+41.911 -6.218 41.786 -6.174 41.697 -6.086 c
+41.61 -5.991 41.566 -5.873 41.566 -5.733 c
+41.566 -1.97 m
+41.566 -1.823 41.61 -1.702 41.697 -1.603 c
+41.786 -1.507 41.911 -1.455 42.08 -1.455 c
+42.256 -1.455 42.389 -1.507 42.476 -1.603 c
+42.572 -1.702 42.624 -1.823 42.624 -1.97 c
+42.624 -2.11 42.572 -2.228 42.476 -2.323 c
+42.389 -2.411 42.256 -2.455 42.08 -2.455 c
+41.911 -2.455 41.786 -2.411 41.697 -2.323 c
+41.61 -2.228 41.566 -2.11 41.566 -1.97 c
+f
+Q
+537.111 348.557 -0.838 6.688 re
+538.735 348.557 m
+538.735 352.864 l
+538.089 352.864 l
+538.089 353.525 l
+538.735 353.525 l
+538.735 354.099 l
+538.743 354.606 538.871 355.003 539.118 355.289 c
+539.361 355.572 539.71 355.715 540.161 355.715 c
+540.315 355.715 540.477 355.686 540.647 355.628 c
+540.602 354.936 l
+540.492 354.967 540.371 354.981 540.235 354.981 c
+539.772 354.981 539.544 354.65 539.544 353.996 c
+539.544 353.525 l
+540.396 353.525 l
+540.396 352.864 l
+539.544 352.864 l
+539.544 348.557 l
+h
+544.655 349.91 m
+545.553 353.525 l
+546.419 353.525 l
+544.788 347.852 l
+544.67 347.433 544.497 347.109 544.273 346.882 c
+544.046 346.657 543.792 346.543 543.509 346.543 c
+543.398 346.543 543.263 346.57 543.097 346.617 c
+543.097 347.308 l
+543.274 347.293 l
+543.509 347.293 543.689 347.352 543.818 347.47 c
+543.953 347.58 544.061 347.771 544.141 348.043 c
+544.302 348.602 l
+542.848 353.525 l
+543.729 353.525 l
+h
+546.816 351.277 m
+546.816 351.989 546.985 352.559 547.331 352.982 c
+547.683 353.4 548.146 353.614 548.728 353.614 c
+549.304 353.614 549.763 353.408 550.109 352.997 c
+550.461 352.592 550.642 352.033 550.652 351.321 c
+550.652 350.807 l
+550.652 350.078 550.476 349.506 550.124 349.087 c
+549.778 348.675 549.318 348.469 548.742 348.469 c
+548.161 348.469 547.698 348.667 547.346 349.072 c
+547 349.483 546.824 350.034 546.816 350.733 c
+h
+547.625 350.807 m
+547.625 350.296 547.72 349.895 547.919 349.601 c
+548.125 349.307 548.396 349.16 548.742 349.16 c
+549.455 349.16 549.823 349.678 549.844 350.718 c
+549.844 351.277 l
+549.844 351.784 549.742 352.188 549.536 352.482 c
+549.337 352.776 549.069 352.923 548.728 352.923 c
+548.393 352.923 548.125 352.776 547.919 352.482 c
+547.72 352.188 547.625 351.784 547.625 351.277 c
+h
+554.052 348.999 m
+553.776 348.646 553.38 348.469 552.861 348.469 c
+552.42 348.469 552.082 348.619 551.847 348.925 c
+551.619 349.237 551.502 349.693 551.494 350.292 c
+551.494 353.525 l
+552.302 353.525 l
+552.302 350.35 l
+552.302 349.575 552.538 349.189 553.008 349.189 c
+553.497 349.189 553.835 349.41 554.022 349.851 c
+554.022 353.525 l
+554.845 353.525 l
+554.845 348.557 l
+554.066 348.557 l
+h
+561.651 349.983 m
+562.415 353.525 l
+563.209 353.525 l
+562.004 348.557 l
+561.357 348.557 l
+560.387 352.114 l
+559.431 348.557 l
+558.785 348.557 l
+557.58 353.525 l
+558.373 353.525 l
+559.153 350.071 l
+560.063 353.525 l
+560.711 353.525 l
+h
+566.381 348.557 m
+566.33 348.664 566.296 348.844 566.278 349.101 c
+565.991 348.679 565.624 348.469 565.175 348.469 c
+564.723 348.469 564.37 348.59 564.117 348.837 c
+563.871 349.09 563.749 349.446 563.749 349.91 c
+563.749 350.417 563.919 350.821 564.264 351.115 c
+564.605 351.409 565.076 351.559 565.675 351.57 c
+566.263 351.57 l
+566.263 352.1 l
+566.263 352.394 566.193 352.603 566.057 352.732 c
+565.929 352.856 565.734 352.923 565.469 352.923 c
+565.223 352.923 565.021 352.85 564.867 352.702 c
+564.72 352.555 564.646 352.368 564.646 352.144 c
+563.823 352.144 l
+563.823 352.397 563.897 352.644 564.044 352.879 c
+564.198 353.114 564.404 353.294 564.661 353.423 c
+564.915 353.548 565.197 353.614 565.513 353.614 c
+566.02 353.614 566.41 353.485 566.675 353.232 c
+566.939 352.985 567.072 352.618 567.072 352.129 c
+567.072 349.631 l
+567.079 349.248 567.134 348.914 567.233 348.631 c
+567.233 348.557 l
+h
+565.308 349.205 m
+565.503 349.205 565.69 349.255 565.866 349.365 c
+566.05 349.483 566.182 349.619 566.263 349.777 c
+566.263 350.968 l
+565.808 350.968 l
+565.426 350.957 565.12 350.869 564.896 350.703 c
+564.668 350.535 564.558 350.306 564.558 350.013 c
+564.558 349.726 564.609 349.52 564.72 349.396 c
+564.838 349.267 565.032 349.205 565.308 349.205 c
+568.909 353.525 m
+568.938 352.997 l
+569.251 353.408 569.651 353.614 570.143 353.614 c
+571.026 353.614 571.47 353.03 571.481 351.865 c
+571.481 348.557 l
+570.673 348.557 l
+570.673 351.821 l
+570.673 352.21 570.606 352.486 570.482 352.644 c
+570.353 352.809 570.159 352.893 569.894 352.893 c
+569.688 352.893 569.504 352.824 569.35 352.688 c
+569.191 352.559 569.064 352.386 568.968 352.173 c
+568.968 348.557 l
+568.144 348.557 l
+568.144 353.525 l
+h
+573.59 354.731 m
+573.59 353.525 l
+574.34 353.525 l
+574.34 352.864 l
+573.59 352.864 l
+573.59 349.792 l
+573.59 349.594 573.62 349.443 573.679 349.336 c
+573.745 349.237 573.858 349.189 574.017 349.189 c
+574.124 349.189 574.23 349.207 574.34 349.248 c
+574.326 348.557 l
+574.149 348.499 573.962 348.469 573.767 348.469 c
+573.444 348.469 573.197 348.579 573.032 348.808 c
+572.863 349.043 572.782 349.365 572.782 349.777 c
+572.782 352.864 l
+572.017 352.864 l
+572.017 353.525 l
+572.782 353.525 l
+572.782 354.731 l
+h
+577.081 351.277 m
+577.081 351.989 577.251 352.559 577.596 352.982 c
+577.949 353.4 578.412 353.614 578.992 353.614 c
+579.57 353.614 580.029 353.408 580.374 352.997 c
+580.727 352.592 580.907 352.033 580.918 351.321 c
+580.918 350.807 l
+580.918 350.078 580.742 349.506 580.389 349.087 c
+580.044 348.675 579.584 348.469 579.007 348.469 c
+578.427 348.469 577.964 348.667 577.611 349.072 c
+577.265 349.483 577.089 350.034 577.081 350.733 c
+h
+577.891 350.807 m
+577.891 350.296 577.986 349.895 578.184 349.601 c
+578.39 349.307 578.662 349.16 579.007 349.16 c
+579.72 349.16 580.087 349.678 580.11 350.718 c
+580.11 351.277 l
+580.11 351.784 580.007 352.188 579.801 352.482 c
+579.603 352.776 579.335 352.923 578.992 352.923 c
+578.658 352.923 578.39 352.776 578.184 352.482 c
+577.986 352.188 577.891 351.784 577.891 351.277 c
+h
+583.836 352.761 m
+583.718 352.779 583.593 352.791 583.468 352.791 c
+583.046 352.791 582.755 352.563 582.601 352.114 c
+582.601 348.557 l
+581.778 348.557 l
+581.778 353.525 l
+582.572 353.525 l
+582.587 353.026 l
+582.799 353.415 583.108 353.614 583.512 353.614 c
+583.637 353.614 583.748 353.592 583.836 353.555 c
+h
+587.356 353.525 m
+587.385 352.997 l
+587.698 353.408 588.098 353.614 588.591 353.614 c
+589.473 353.614 589.918 353.03 589.928 351.865 c
+589.928 348.557 l
+589.12 348.557 l
+589.12 351.821 l
+589.12 352.21 589.054 352.486 588.929 352.644 c
+588.8 352.809 588.606 352.893 588.341 352.893 c
+588.135 352.893 587.952 352.824 587.797 352.688 c
+587.639 352.559 587.51 352.386 587.415 352.173 c
+587.415 348.557 l
+586.592 348.557 l
+586.592 353.525 l
+h
+592.748 348.469 m
+592.13 348.469 591.652 348.65 591.321 349.013 c
+590.998 349.384 590.828 349.928 590.822 350.645 c
+590.822 351.247 l
+590.822 351.983 590.984 352.559 591.306 352.982 c
+591.63 353.4 592.079 353.614 592.659 353.614 c
+593.236 353.614 593.666 353.427 593.953 353.055 c
+594.246 352.692 594.397 352.118 594.408 351.335 c
+594.408 350.807 l
+591.63 350.807 l
+591.63 350.689 l
+591.63 350.148 591.726 349.755 591.924 349.513 c
+592.13 349.278 592.417 349.16 592.791 349.16 c
+593.034 349.16 593.247 349.205 593.423 349.292 c
+593.607 349.38 593.78 349.52 593.938 349.718 c
+594.364 349.205 l
+594.011 348.712 593.471 348.469 592.748 348.469 c
+592.659 352.923 m
+592.324 352.923 592.075 352.806 591.909 352.57 c
+591.74 352.342 591.649 351.986 591.63 351.497 c
+593.585 351.497 l
+593.585 351.615 l
+593.563 352.085 593.482 352.416 593.336 352.615 c
+593.188 352.82 592.96 352.923 592.659 352.923 c
+596.981 348.469 m
+596.363 348.469 595.885 348.65 595.555 349.013 c
+595.231 349.384 595.062 349.928 595.055 350.645 c
+595.055 351.247 l
+595.055 351.983 595.217 352.559 595.54 352.982 c
+595.863 353.4 596.312 353.614 596.893 353.614 c
+597.469 353.614 597.899 353.427 598.186 353.055 c
+598.479 352.692 598.63 352.118 598.641 351.335 c
+598.641 350.807 l
+595.863 350.807 l
+595.863 350.689 l
+595.863 350.148 595.959 349.755 596.158 349.513 c
+596.363 349.278 596.65 349.16 597.024 349.16 c
+597.267 349.16 597.48 349.205 597.656 349.292 c
+597.841 349.38 598.013 349.52 598.171 349.718 c
+598.597 349.205 l
+598.244 348.712 597.704 348.469 596.981 348.469 c
+596.893 352.923 m
+596.558 352.923 596.308 352.806 596.143 352.57 c
+595.973 352.342 595.882 351.986 595.863 351.497 c
+597.818 351.497 l
+597.818 351.615 l
+597.797 352.085 597.716 352.416 597.569 352.615 c
+597.421 352.82 597.193 352.923 596.893 352.923 c
+599.273 351.262 m
+599.273 352.033 599.413 352.618 599.7 353.011 c
+599.983 353.412 600.394 353.614 600.935 353.614 c
+601.405 353.614 601.772 353.423 602.037 353.041 c
+602.037 355.613 l
+602.861 355.613 l
+602.861 348.557 l
+602.111 348.557 l
+602.067 349.087 l
+601.802 348.675 601.423 348.469 600.935 348.469 c
+600.413 348.469 600.008 348.664 599.715 349.057 c
+599.42 349.458 599.273 350.028 599.273 350.762 c
+h
+600.082 350.807 m
+600.082 350.248 600.162 349.836 600.332 349.571 c
+600.497 349.315 600.766 349.189 601.14 349.189 c
+601.541 349.189 601.839 349.388 602.037 349.792 c
+602.037 352.305 l
+601.831 352.695 601.529 352.893 601.14 352.893 c
+600.766 352.893 600.497 352.761 600.332 352.497 c
+600.162 352.239 600.082 351.842 600.082 351.306 c
+h
+607.053 354.731 m
+607.053 353.525 l
+607.802 353.525 l
+607.802 352.864 l
+607.053 352.864 l
+607.053 349.792 l
+607.053 349.594 607.083 349.443 607.141 349.336 c
+607.208 349.237 607.322 349.189 607.48 349.189 c
+607.586 349.189 607.692 349.207 607.802 349.248 c
+607.788 348.557 l
+607.611 348.499 607.424 348.469 607.229 348.469 c
+606.906 348.469 606.66 348.579 606.495 348.808 c
+606.325 349.043 606.244 349.365 606.244 349.777 c
+606.244 352.864 l
+605.48 352.864 l
+605.48 353.525 l
+606.244 353.525 l
+606.244 354.731 l
+h
+608.39 351.277 m
+608.39 351.989 608.56 352.559 608.905 352.982 c
+609.258 353.4 609.72 353.614 610.302 353.614 c
+610.878 353.614 611.338 353.408 611.683 352.997 c
+612.036 352.592 612.216 352.033 612.227 351.321 c
+612.227 350.807 l
+612.227 350.078 612.051 349.506 611.698 349.087 c
+611.353 348.675 610.894 348.469 610.316 348.469 c
+609.735 348.469 609.273 348.667 608.92 349.072 c
+608.575 349.483 608.398 350.034 608.39 350.733 c
+h
+609.199 350.807 m
+609.199 350.296 609.294 349.895 609.493 349.601 c
+609.699 349.307 609.971 349.16 610.316 349.16 c
+611.029 349.16 611.397 349.678 611.418 350.718 c
+611.418 351.277 l
+611.418 351.784 611.316 352.188 611.11 352.482 c
+610.911 352.776 610.643 352.923 610.302 352.923 c
+609.967 352.923 609.699 352.776 609.493 352.482 c
+609.294 352.188 609.199 351.784 609.199 351.277 c
+h
+616.034 348.557 -0.808 4.968 re
+616.093 354.849 m
+616.093 354.709 616.053 354.591 615.975 354.496 c
+615.906 354.408 615.792 354.364 615.638 354.364 c
+615.48 354.364 615.362 354.408 615.285 354.496 c
+615.214 354.591 615.181 354.709 615.181 354.849 c
+615.181 354.984 615.214 355.098 615.285 355.187 c
+615.362 355.282 615.476 355.333 615.623 355.333 c
+615.777 355.333 615.894 355.282 615.975 355.187 c
+616.053 355.098 616.093 354.984 616.093 354.849 c
+617.978 353.525 m
+618.007 352.997 l
+618.32 353.408 618.721 353.614 619.212 353.614 c
+620.095 353.614 620.539 353.03 620.55 351.865 c
+620.55 348.557 l
+619.742 348.557 l
+619.742 351.821 l
+619.742 352.21 619.676 352.486 619.551 352.644 c
+619.422 352.809 619.228 352.893 618.963 352.893 c
+618.757 352.893 618.574 352.824 618.419 352.688 c
+618.261 352.559 618.133 352.386 618.036 352.173 c
+618.036 348.557 l
+617.213 348.557 l
+617.213 353.525 l
+h
+624.016 349.836 m
+624.016 350.001 623.946 350.148 623.81 350.277 c
+623.681 350.402 623.428 350.549 623.046 350.718 c
+622.612 350.902 622.304 351.06 622.119 351.189 c
+621.943 351.314 621.811 351.457 621.722 351.615 c
+621.635 351.78 621.591 351.975 621.591 352.203 c
+621.591 352.615 621.737 352.952 622.031 353.217 c
+622.325 353.481 622.7 353.614 623.164 353.614 c
+623.652 353.614 624.045 353.471 624.339 353.188 c
+624.633 352.912 624.78 352.555 624.78 352.114 c
+623.972 352.114 l
+623.972 352.339 623.891 352.53 623.737 352.688 c
+623.59 352.842 623.399 352.923 623.164 352.923 c
+622.928 352.923 622.74 352.856 622.605 352.732 c
+622.465 352.615 622.399 352.445 622.399 352.232 c
+622.399 352.063 622.447 351.931 622.546 351.835 c
+622.641 351.736 622.884 351.603 623.266 351.439 c
+623.872 351.204 624.284 350.971 624.5 350.747 c
+624.714 350.52 624.824 350.237 624.824 349.895 c
+624.824 349.461 624.67 349.116 624.369 348.852 c
+624.074 348.594 623.677 348.469 623.178 348.469 c
+622.667 348.469 622.252 348.617 621.928 348.91 c
+621.605 349.205 621.443 349.579 621.443 350.042 c
+622.266 350.042 l
+622.274 349.766 622.358 349.55 622.516 349.396 c
+622.671 349.237 622.891 349.16 623.178 349.16 c
+623.45 349.16 623.656 349.219 623.795 349.336 c
+623.943 349.461 624.016 349.631 624.016 349.836 c
+626.772 354.731 m
+626.772 353.525 l
+627.521 353.525 l
+627.521 352.864 l
+626.772 352.864 l
+626.772 349.792 l
+626.772 349.594 626.801 349.443 626.86 349.336 c
+626.927 349.237 627.04 349.189 627.198 349.189 c
+627.305 349.189 627.411 349.207 627.521 349.248 c
+627.507 348.557 l
+627.33 348.499 627.143 348.469 626.948 348.469 c
+626.625 348.469 626.379 348.579 626.213 348.808 c
+626.044 349.043 625.963 349.365 625.963 349.777 c
+625.963 352.864 l
+625.199 352.864 l
+625.199 353.525 l
+625.963 353.525 l
+625.963 354.731 l
+h
+630.921 348.557 m
+630.869 348.664 630.836 348.844 630.817 349.101 c
+630.531 348.679 630.164 348.469 629.715 348.469 c
+629.263 348.469 628.911 348.59 628.657 348.837 c
+628.411 349.09 628.29 349.446 628.29 349.91 c
+628.29 350.417 628.458 350.821 628.804 351.115 c
+629.146 351.409 629.616 351.559 630.215 351.57 c
+630.803 351.57 l
+630.803 352.1 l
+630.803 352.394 630.733 352.603 630.597 352.732 c
+630.468 352.856 630.274 352.923 630.009 352.923 c
+629.763 352.923 629.561 352.85 629.406 352.702 c
+629.26 352.555 629.186 352.368 629.186 352.144 c
+628.363 352.144 l
+628.363 352.397 628.436 352.644 628.583 352.879 c
+628.737 353.114 628.943 353.294 629.201 353.423 c
+629.454 353.548 629.737 353.614 630.054 353.614 c
+630.561 353.614 630.95 353.485 631.214 353.232 c
+631.479 352.985 631.611 352.618 631.611 352.129 c
+631.611 349.631 l
+631.619 349.248 631.674 348.914 631.773 348.631 c
+631.773 348.557 l
+h
+629.848 349.205 m
+630.042 349.205 630.229 349.255 630.406 349.365 c
+630.59 349.483 630.722 349.619 630.803 349.777 c
+630.803 350.968 l
+630.347 350.968 l
+629.965 350.957 629.66 350.869 629.436 350.703 c
+629.208 350.535 629.098 350.306 629.098 350.013 c
+629.098 349.726 629.15 349.52 629.26 349.396 c
+629.377 349.267 629.572 349.205 629.848 349.205 c
+633.566 348.557 -0.808 7.056 re
+635.628 348.557 -0.809 7.056 re
+643.183 349.396 m
+643.037 349.205 l
+642.613 348.712 641.996 348.469 641.184 348.469 c
+640.456 348.469 639.891 348.708 639.479 349.189 c
+639.068 349.667 638.854 350.343 638.847 351.218 c
+638.847 352.526 l
+638.847 353.467 639.031 354.165 639.405 354.628 c
+639.777 355.098 640.339 355.333 641.096 355.333 c
+641.732 355.333 642.228 355.158 642.58 354.805 c
+642.941 354.452 643.143 353.945 643.183 353.29 c
+642.345 353.29 l
+642.305 353.702 642.187 354.026 641.992 354.261 c
+641.794 354.496 641.497 354.614 641.096 354.614 c
+640.614 354.614 640.261 354.456 640.038 354.143 c
+639.81 353.827 639.692 353.327 639.685 352.644 c
+639.685 351.277 l
+639.685 350.601 639.81 350.078 640.067 349.718 c
+640.332 349.365 640.703 349.189 641.184 349.189 c
+641.632 349.189 641.978 349.296 642.214 349.513 c
+642.345 349.631 l
+642.345 351.173 l
+641.111 351.173 l
+641.111 351.894 l
+643.183 351.894 l
+h
+645.102 348.557 -0.809 4.968 re
+645.16 354.849 m
+645.16 354.709 645.119 354.591 645.042 354.496 c
+644.973 354.408 644.859 354.364 644.705 354.364 c
+644.547 354.364 644.429 354.408 644.352 354.496 c
+644.282 354.591 644.249 354.709 644.249 354.849 c
+644.249 354.984 644.282 355.098 644.352 355.187 c
+644.429 355.282 644.543 355.333 644.69 355.333 c
+644.844 355.333 644.961 355.282 645.042 355.187 c
+645.119 355.098 645.16 354.984 645.16 354.849 c
+647.358 354.731 m
+647.358 353.525 l
+648.107 353.525 l
+648.107 352.864 l
+647.358 352.864 l
+647.358 349.792 l
+647.358 349.594 647.388 349.443 647.446 349.336 c
+647.512 349.237 647.626 349.189 647.784 349.189 c
+647.891 349.189 647.997 349.207 648.107 349.248 c
+648.093 348.557 l
+647.916 348.499 647.729 348.469 647.534 348.469 c
+647.211 348.469 646.964 348.579 646.8 348.808 c
+646.63 349.043 646.549 349.365 646.549 349.777 c
+646.549 352.864 l
+645.785 352.864 l
+645.785 353.525 l
+646.549 353.525 l
+646.549 354.731 l
+h
+651.378 348.557 m
+651.378 352.864 l
+650.731 352.864 l
+650.731 353.525 l
+651.378 353.525 l
+651.378 354.099 l
+651.386 354.606 651.514 355.003 651.76 355.289 c
+652.003 355.572 652.352 355.715 652.803 355.715 c
+652.958 355.715 653.12 355.686 653.289 355.628 c
+653.245 354.936 l
+653.135 354.967 653.013 354.981 652.878 354.981 c
+652.414 354.981 652.186 354.65 652.186 353.996 c
+652.186 353.525 l
+653.039 353.525 l
+653.039 352.864 l
+652.186 352.864 l
+652.186 348.557 l
+h
+655.928 352.761 m
+655.81 352.779 655.685 352.791 655.56 352.791 c
+655.137 352.791 654.847 352.563 654.692 352.114 c
+654.692 348.557 l
+653.869 348.557 l
+653.869 353.525 l
+654.663 353.525 l
+654.678 353.026 l
+654.891 353.415 655.2 353.614 655.604 353.614 c
+655.729 353.614 655.839 353.592 655.928 353.555 c
+h
+656.368 351.277 m
+656.368 351.989 656.537 352.559 656.883 352.982 c
+657.235 353.4 657.698 353.614 658.279 353.614 c
+658.856 353.614 659.315 353.408 659.661 352.997 c
+660.013 352.592 660.194 352.033 660.205 351.321 c
+660.205 350.807 l
+660.205 350.078 660.028 349.506 659.676 349.087 c
+659.33 348.675 658.871 348.469 658.294 348.469 c
+657.713 348.469 657.25 348.667 656.898 349.072 c
+656.552 349.483 656.375 350.034 656.368 350.733 c
+h
+657.177 350.807 m
+657.177 350.296 657.272 349.895 657.47 349.601 c
+657.677 349.307 657.948 349.16 658.294 349.16 c
+659.007 349.16 659.375 349.678 659.396 350.718 c
+659.396 351.277 l
+659.396 351.784 659.294 352.188 659.088 352.482 c
+658.889 352.776 658.621 352.923 658.279 352.923 c
+657.945 352.923 657.677 352.776 657.47 352.482 c
+657.272 352.188 657.177 351.784 657.177 351.277 c
+h
+661.829 353.525 m
+661.858 353.07 l
+662.16 353.43 662.557 353.614 663.049 353.614 c
+663.608 353.614 663.994 353.371 664.21 352.893 c
+664.522 353.371 664.96 353.614 665.518 353.614 c
+666.438 353.614 666.908 353.045 666.93 351.909 c
+666.93 348.557 l
+666.122 348.557 l
+666.122 351.835 l
+666.122 352.188 666.051 352.453 665.915 352.629 c
+665.787 352.806 665.57 352.893 665.268 352.893 c
+665.023 352.893 664.82 352.798 664.666 352.615 c
+664.518 352.427 664.431 352.188 664.402 351.894 c
+664.402 348.557 l
+663.593 348.557 l
+663.593 351.865 l
+663.581 352.548 663.303 352.893 662.756 352.893 c
+662.344 352.893 662.053 352.688 661.888 352.276 c
+661.888 348.557 l
+661.079 348.557 l
+661.079 353.525 l
+h
+672.346 349.836 m
+672.346 350.001 672.277 350.148 672.14 350.277 c
+672.011 350.402 671.758 350.549 671.376 350.718 c
+670.943 350.902 670.633 351.06 670.45 351.189 c
+670.274 351.314 670.141 351.457 670.053 351.615 c
+669.965 351.78 669.921 351.975 669.921 352.203 c
+669.921 352.615 670.068 352.952 670.361 353.217 c
+670.656 353.481 671.03 353.614 671.494 353.614 c
+671.982 353.614 672.376 353.471 672.67 353.188 c
+672.964 352.912 673.11 352.555 673.11 352.114 c
+672.302 352.114 l
+672.302 352.339 672.221 352.53 672.067 352.688 c
+671.92 352.842 671.729 352.923 671.494 352.923 c
+671.259 352.923 671.071 352.856 670.935 352.732 c
+670.795 352.615 670.729 352.445 670.729 352.232 c
+670.729 352.063 670.777 351.931 670.876 351.835 c
+670.972 351.736 671.215 351.603 671.597 351.439 c
+672.202 351.204 672.615 350.971 672.831 350.747 c
+673.044 350.52 673.154 350.237 673.154 349.895 c
+673.154 349.461 673 349.116 672.699 348.852 c
+672.405 348.594 672.008 348.469 671.508 348.469 c
+670.997 348.469 670.582 348.617 670.259 348.91 c
+669.935 349.205 669.773 349.579 669.773 350.042 c
+670.597 350.042 l
+670.604 349.766 670.689 349.55 670.847 349.396 c
+671.001 349.237 671.222 349.16 671.508 349.16 c
+671.78 349.16 671.986 349.219 672.125 349.336 c
+672.273 349.461 672.346 349.631 672.346 349.836 c
+673.868 351.277 m
+673.868 351.989 674.037 352.559 674.382 352.982 c
+674.735 353.4 675.198 353.614 675.778 353.614 c
+676.356 353.614 676.815 353.408 677.16 352.997 c
+677.513 352.592 677.693 352.033 677.704 351.321 c
+677.704 350.807 l
+677.704 350.078 677.528 349.506 677.175 349.087 c
+676.829 348.675 676.37 348.469 675.793 348.469 c
+675.213 348.469 674.75 348.667 674.396 349.072 c
+674.051 349.483 673.875 350.034 673.868 350.733 c
+h
+674.676 350.807 m
+674.676 350.296 674.772 349.895 674.97 349.601 c
+675.176 349.307 675.448 349.16 675.793 349.16 c
+676.506 349.16 676.873 349.678 676.896 350.718 c
+676.896 351.277 l
+676.896 351.784 676.792 352.188 676.587 352.482 c
+676.389 352.776 676.121 352.923 675.778 352.923 c
+675.444 352.923 675.176 352.776 674.97 352.482 c
+674.772 352.188 674.676 351.784 674.676 351.277 c
+h
+681.107 348.999 m
+680.831 348.646 680.434 348.469 679.917 348.469 c
+679.475 348.469 679.138 348.619 678.902 348.925 c
+678.674 349.237 678.556 349.693 678.55 350.292 c
+678.55 353.525 l
+679.358 353.525 l
+679.358 350.35 l
+679.358 349.575 679.593 349.189 680.063 349.189 c
+680.552 349.189 680.89 349.41 681.077 349.851 c
+681.077 353.525 l
+681.901 353.525 l
+681.901 348.557 l
+681.122 348.557 l
+h
+684.995 352.761 m
+684.877 352.779 684.752 352.791 684.628 352.791 c
+684.204 352.791 683.915 352.563 683.76 352.114 c
+683.76 348.557 l
+682.937 348.557 l
+682.937 353.525 l
+683.73 353.525 l
+683.745 353.026 l
+683.959 353.415 684.267 353.614 684.671 353.614 c
+684.796 353.614 684.906 353.592 684.995 353.555 c
+h
+687.335 349.16 m
+687.6 349.16 687.809 349.241 687.967 349.41 c
+688.133 349.575 688.225 349.814 688.247 350.13 c
+689.012 350.13 l
+688.989 349.649 688.821 349.252 688.497 348.939 c
+688.181 348.623 687.795 348.469 687.335 348.469 c
+686.718 348.469 686.248 348.66 685.924 349.043 c
+685.601 349.432 685.44 350.013 685.44 350.776 c
+685.44 351.321 l
+685.44 352.074 685.601 352.644 685.924 353.026 c
+686.248 353.415 686.711 353.614 687.321 353.614 c
+687.828 353.614 688.233 353.452 688.526 353.128 c
+688.827 352.812 688.989 352.38 689.012 351.821 c
+688.247 351.821 l
+688.225 352.181 688.137 352.457 687.982 352.644 c
+687.824 352.827 687.604 352.923 687.321 352.923 c
+686.968 352.923 686.704 352.798 686.527 352.555 c
+686.351 352.32 686.255 351.931 686.248 351.395 c
+686.248 350.762 l
+686.248 350.174 686.336 349.755 686.512 349.513 c
+686.689 349.278 686.961 349.16 687.335 349.16 c
+691.558 348.469 m
+690.94 348.469 690.463 348.65 690.132 349.013 c
+689.808 349.384 689.64 349.928 689.632 350.645 c
+689.632 351.247 l
+689.632 351.983 689.794 352.559 690.117 352.982 c
+690.441 353.4 690.889 353.614 691.47 353.614 c
+692.046 353.614 692.476 353.427 692.763 353.055 c
+693.057 352.692 693.208 352.118 693.219 351.335 c
+693.219 350.807 l
+690.441 350.807 l
+690.441 350.689 l
+690.441 350.148 690.536 349.755 690.735 349.513 c
+690.94 349.278 691.227 349.16 691.602 349.16 c
+691.844 349.16 692.058 349.205 692.234 349.292 c
+692.418 349.38 692.59 349.52 692.748 349.718 c
+693.174 349.205 l
+692.822 348.712 692.282 348.469 691.558 348.469 c
+691.47 352.923 m
+691.135 352.923 690.886 352.806 690.72 352.57 c
+690.551 352.342 690.459 351.986 690.441 351.497 c
+692.395 351.497 l
+692.395 351.615 l
+692.374 352.085 692.293 352.416 692.146 352.615 c
+691.998 352.82 691.771 352.923 691.47 352.923 c
+694.028 347.22 m
+693.542 347.558 l
+693.825 347.962 693.976 348.374 693.997 348.792 c
+693.997 349.557 l
+694.821 349.557 l
+694.821 348.895 l
+694.821 348.59 694.741 348.285 694.585 347.969 c
+694.439 347.657 694.252 347.407 694.028 347.22 c
+699.227 349.91 m
+700.124 353.525 l
+700.991 353.525 l
+699.36 347.852 l
+699.242 347.433 699.069 347.109 698.845 346.882 c
+698.617 346.657 698.363 346.543 698.08 346.543 c
+697.97 346.543 697.835 346.57 697.669 346.617 c
+697.669 347.308 l
+697.845 347.293 l
+698.08 347.293 698.261 347.352 698.389 347.47 c
+698.525 347.58 698.632 347.771 698.712 348.043 c
+698.874 348.602 l
+697.419 353.525 l
+698.301 353.525 l
+h
+701.384 351.277 m
+701.384 351.989 701.554 352.559 701.899 352.982 c
+702.252 353.4 702.714 353.614 703.295 353.614 c
+703.872 353.614 704.331 353.408 704.677 352.997 c
+705.03 352.592 705.209 352.033 705.221 351.321 c
+705.221 350.807 l
+705.221 350.078 705.045 349.506 704.692 349.087 c
+704.346 348.675 703.887 348.469 703.31 348.469 c
+702.729 348.469 702.267 348.667 701.913 349.072 c
+701.568 349.483 701.392 350.034 701.384 350.733 c
+h
+702.192 350.807 m
+702.192 350.296 702.288 349.895 702.487 349.601 c
+702.693 349.307 702.965 349.16 703.31 349.16 c
+704.023 349.16 704.39 349.678 704.412 350.718 c
+704.412 351.277 l
+704.412 351.784 704.309 352.188 704.104 352.482 c
+703.905 352.776 703.637 352.923 703.295 352.923 c
+702.961 352.923 702.693 352.776 702.487 352.482 c
+702.288 352.188 702.192 351.784 702.192 351.277 c
+h
+708.623 348.999 m
+708.348 348.646 707.951 348.469 707.433 348.469 c
+706.992 348.469 706.654 348.619 706.419 348.925 c
+706.19 349.237 706.073 349.693 706.066 350.292 c
+706.066 353.525 l
+706.874 353.525 l
+706.874 350.35 l
+706.874 349.575 707.109 349.189 707.58 349.189 c
+708.068 349.189 708.407 349.41 708.594 349.851 c
+708.594 353.525 l
+709.417 353.525 l
+709.417 348.557 l
+708.638 348.557 l
+h
+714.253 349.16 m
+714.518 349.16 714.728 349.241 714.886 349.41 c
+715.05 349.575 715.143 349.814 715.164 350.13 c
+715.929 350.13 l
+715.907 349.649 715.738 349.252 715.414 348.939 c
+715.098 348.623 714.713 348.469 714.253 348.469 c
+713.635 348.469 713.165 348.66 712.842 349.043 c
+712.519 349.432 712.357 350.013 712.357 350.776 c
+712.357 351.321 l
+712.357 352.074 712.519 352.644 712.842 353.026 c
+713.165 353.415 713.629 353.614 714.238 353.614 c
+714.745 353.614 715.15 353.452 715.444 353.128 c
+715.745 352.812 715.907 352.38 715.929 351.821 c
+715.164 351.821 l
+715.143 352.181 715.054 352.457 714.9 352.644 c
+714.742 352.827 714.522 352.923 714.238 352.923 c
+713.886 352.923 713.621 352.798 713.444 352.555 c
+713.269 352.32 713.173 351.931 713.165 351.395 c
+713.165 350.762 l
+713.165 350.174 713.253 349.755 713.43 349.513 c
+713.606 349.278 713.878 349.16 714.253 349.16 c
+719.254 348.557 m
+719.203 348.664 719.17 348.844 719.152 349.101 c
+718.865 348.679 718.497 348.469 718.049 348.469 c
+717.597 348.469 717.244 348.59 716.991 348.837 c
+716.745 349.09 716.623 349.446 716.623 349.91 c
+716.623 350.417 716.793 350.821 717.138 351.115 c
+717.479 351.409 717.95 351.559 718.549 351.57 c
+719.137 351.57 l
+719.137 352.1 l
+719.137 352.394 719.067 352.603 718.931 352.732 c
+718.803 352.856 718.608 352.923 718.343 352.923 c
+718.097 352.923 717.895 352.85 717.741 352.702 c
+717.593 352.555 717.52 352.368 717.52 352.144 c
+716.697 352.144 l
+716.697 352.397 716.77 352.644 716.918 352.879 c
+717.072 353.114 717.278 353.294 717.535 353.423 c
+717.789 353.548 718.071 353.614 718.387 353.614 c
+718.894 353.614 719.284 353.485 719.549 353.232 c
+719.813 352.985 719.945 352.618 719.945 352.129 c
+719.945 349.631 l
+719.952 349.248 720.008 348.914 720.107 348.631 c
+720.107 348.557 l
+h
+718.181 349.205 m
+718.376 349.205 718.564 349.255 718.74 349.365 c
+718.924 349.483 719.056 349.619 719.137 349.777 c
+719.137 350.968 l
+718.682 350.968 l
+718.299 350.957 717.994 350.869 717.77 350.703 c
+717.542 350.535 717.432 350.306 717.432 350.013 c
+717.432 349.726 717.483 349.52 717.593 349.396 c
+717.711 349.267 717.905 349.205 718.181 349.205 c
+721.783 353.525 m
+721.812 352.997 l
+722.125 353.408 722.525 353.614 723.017 353.614 c
+723.9 353.614 724.344 353.03 724.355 351.865 c
+724.355 348.557 l
+723.547 348.557 l
+723.547 351.821 l
+723.547 352.21 723.48 352.486 723.355 352.644 c
+723.227 352.809 723.032 352.893 722.767 352.893 c
+722.562 352.893 722.378 352.824 722.223 352.688 c
+722.065 352.559 721.937 352.386 721.841 352.173 c
+721.841 348.557 l
+721.018 348.557 l
+721.018 353.525 l
+h
+727.295 351.262 m
+727.295 352.033 727.434 352.618 727.721 353.011 c
+728.004 353.412 728.415 353.614 728.956 353.614 c
+729.463 353.614 729.853 353.397 730.117 352.967 c
+730.146 353.525 l
+730.881 353.525 l
+730.881 348.513 l
+730.881 347.896 730.724 347.426 730.411 347.102 c
+730.096 346.772 729.662 346.602 729.103 346.602 c
+728.857 346.602 728.577 346.668 728.265 346.793 c
+727.96 346.911 727.736 347.058 727.588 347.235 c
+727.912 347.793 l
+728.254 347.462 728.625 347.293 729.03 347.293 c
+729.705 347.293 730.051 347.661 730.073 348.396 c
+730.073 349.043 l
+729.809 348.66 729.433 348.469 728.956 348.469 c
+728.434 348.469 728.033 348.66 727.75 349.043 c
+727.464 349.432 727.313 349.99 727.295 350.718 c
+h
+728.118 350.807 m
+728.118 350.248 728.195 349.836 728.353 349.571 c
+728.519 349.315 728.787 349.189 729.161 349.189 c
+729.562 349.189 729.867 349.396 730.073 349.807 c
+730.073 352.291 l
+729.867 352.692 729.566 352.893 729.176 352.893 c
+728.802 352.893 728.533 352.761 728.368 352.497 c
+728.199 352.239 728.118 351.842 728.118 351.306 c
+h
+733.682 348.469 m
+733.065 348.469 732.587 348.65 732.255 349.013 c
+731.933 349.384 731.763 349.928 731.756 350.645 c
+731.756 351.247 l
+731.756 351.983 731.918 352.559 732.241 352.982 c
+732.565 353.4 733.013 353.614 733.593 353.614 c
+734.171 353.614 734.601 353.427 734.887 353.055 c
+735.181 352.692 735.332 352.118 735.343 351.335 c
+735.343 350.807 l
+732.565 350.807 l
+732.565 350.689 l
+732.565 350.148 732.66 349.755 732.858 349.513 c
+733.065 349.278 733.35 349.16 733.726 349.16 c
+733.969 349.16 734.181 349.205 734.358 349.292 c
+734.541 349.38 734.715 349.52 734.873 349.718 c
+735.299 349.205 l
+734.946 348.712 734.406 348.469 733.682 348.469 c
+733.593 352.923 m
+733.259 352.923 733.009 352.806 732.843 352.57 c
+732.675 352.342 732.583 351.986 732.565 351.497 c
+734.52 351.497 l
+734.52 351.615 l
+734.497 352.085 734.416 352.416 734.27 352.615 c
+734.123 352.82 733.895 352.923 733.593 352.923 c
+737.209 354.731 m
+737.209 353.525 l
+737.959 353.525 l
+737.959 352.864 l
+737.209 352.864 l
+737.209 349.792 l
+737.209 349.594 737.239 349.443 737.298 349.336 c
+737.364 349.237 737.478 349.189 737.636 349.189 c
+737.742 349.189 737.849 349.207 737.959 349.248 c
+737.944 348.557 l
+737.768 348.499 737.581 348.469 737.386 348.469 c
+737.063 348.469 736.816 348.579 736.651 348.808 c
+736.482 349.043 736.401 349.365 736.401 349.777 c
+736.401 352.864 l
+735.636 352.864 l
+735.636 353.525 l
+736.401 353.525 l
+736.401 354.731 l
+h
+741.744 348.557 -0.808 4.968 re
+741.803 354.849 m
+741.803 354.709 741.763 354.591 741.685 354.496 c
+741.616 354.408 741.502 354.364 741.347 354.364 c
+741.189 354.364 741.072 354.408 740.994 354.496 c
+740.924 354.591 740.891 354.709 740.891 354.849 c
+740.891 354.984 740.924 355.098 740.994 355.187 c
+741.072 355.282 741.186 355.333 741.333 355.333 c
+741.487 355.333 741.605 355.282 741.685 355.187 c
+741.763 355.098 741.803 354.984 741.803 354.849 c
+744.001 354.731 m
+744.001 353.525 l
+744.75 353.525 l
+744.75 352.864 l
+744.001 352.864 l
+744.001 349.792 l
+744.001 349.594 744.03 349.443 744.089 349.336 c
+744.155 349.237 744.269 349.189 744.427 349.189 c
+744.533 349.189 744.639 349.207 744.75 349.248 c
+744.735 348.557 l
+744.559 348.499 744.371 348.469 744.177 348.469 c
+743.854 348.469 743.607 348.579 743.442 348.808 c
+743.272 349.043 743.192 349.365 743.192 349.777 c
+743.192 352.864 l
+742.428 352.864 l
+742.428 353.525 l
+743.192 353.525 l
+743.192 354.731 l
+h
+748.02 348.557 m
+748.02 352.864 l
+747.374 352.864 l
+747.374 353.525 l
+748.02 353.525 l
+748.02 354.099 l
+748.028 354.606 748.157 355.003 748.402 355.289 c
+748.645 355.572 748.994 355.715 749.447 355.715 c
+749.601 355.715 749.763 355.686 749.931 355.628 c
+749.888 354.936 l
+749.777 354.967 749.655 354.981 749.52 354.981 c
+749.057 354.981 748.829 354.65 748.829 353.996 c
+748.829 353.525 l
+749.682 353.525 l
+749.682 352.864 l
+748.829 352.864 l
+748.829 348.557 l
+h
+752.57 352.761 m
+752.452 352.779 752.327 352.791 752.203 352.791 c
+751.78 352.791 751.49 352.563 751.336 352.114 c
+751.336 348.557 l
+750.512 348.557 l
+750.512 353.525 l
+751.305 353.525 l
+751.32 353.026 l
+751.534 353.415 751.843 353.614 752.246 353.614 c
+752.371 353.614 752.481 353.592 752.57 353.555 c
+h
+753.015 351.277 m
+753.015 351.989 753.183 352.559 753.529 352.982 c
+753.882 353.4 754.345 353.614 754.925 353.614 c
+755.502 353.614 755.962 353.408 756.307 352.997 c
+756.66 352.592 756.84 352.033 756.851 351.321 c
+756.851 350.807 l
+756.851 350.078 756.674 349.506 756.321 349.087 c
+755.976 348.675 755.517 348.469 754.94 348.469 c
+754.359 348.469 753.896 348.667 753.543 349.072 c
+753.198 349.483 753.022 350.034 753.015 350.733 c
+h
+753.823 350.807 m
+753.823 350.296 753.919 349.895 754.117 349.601 c
+754.322 349.307 754.594 349.16 754.94 349.16 c
+755.654 349.16 756.02 349.678 756.043 350.718 c
+756.043 351.277 l
+756.043 351.784 755.94 352.188 755.734 352.482 c
+755.535 352.776 755.267 352.923 754.925 352.923 c
+754.591 352.923 754.322 352.776 754.117 352.482 c
+753.919 352.188 753.823 351.784 753.823 351.277 c
+h
+758.472 353.525 m
+758.501 353.07 l
+758.802 353.43 759.199 353.614 759.691 353.614 c
+760.25 353.614 760.636 353.371 760.853 352.893 c
+761.165 353.371 761.603 353.614 762.161 353.614 c
+763.08 353.614 763.55 353.045 763.572 351.909 c
+763.572 348.557 l
+762.764 348.557 l
+762.764 351.835 l
+762.764 352.188 762.694 352.453 762.558 352.629 c
+762.43 352.806 762.212 352.893 761.911 352.893 c
+761.665 352.893 761.463 352.798 761.308 352.615 c
+761.162 352.427 761.073 352.188 761.044 351.894 c
+761.044 348.557 l
+760.236 348.557 l
+760.236 351.865 l
+760.225 352.548 759.945 352.893 759.398 352.893 c
+758.986 352.893 758.696 352.688 758.53 352.276 c
+758.53 348.557 l
+757.722 348.557 l
+757.722 353.525 l
+h
+f
+q 1 0 0 1 536.9344 341.7219 cm
+0 0 m
+0.302 0.401 0.695 0.603 1.176 0.603 c
+2.058 0.603 2.503 0.019 2.514 -1.146 c
+2.514 -4.453 l
+1.706 -4.453 l
+1.706 -1.19 l
+1.706 -0.801 1.64 -0.525 1.515 -0.367 c
+1.386 -0.201 1.191 -0.118 0.927 -0.118 c
+0.721 -0.118 0.537 -0.187 0.383 -0.323 c
+0.225 -0.452 0.096 -0.625 0 -0.837 c
+0 -4.453 l
+-0.823 -4.453 l
+-0.823 2.602 l
+0 2.602 l
+h
+4.605 1.721 m
+4.605 0.515 l
+5.355 0.515 l
+5.355 -0.147 l
+4.605 -0.147 l
+4.605 -3.218 l
+4.605 -3.417 4.634 -3.568 4.693 -3.674 c
+4.759 -3.773 4.873 -3.821 5.031 -3.821 c
+5.137 -3.821 5.245 -3.803 5.355 -3.763 c
+5.34 -4.453 l
+5.164 -4.512 4.976 -4.542 4.781 -4.542 c
+4.458 -4.542 4.212 -4.432 4.046 -4.203 c
+3.877 -3.968 3.797 -3.645 3.797 -3.233 c
+3.797 -0.147 l
+3.032 -0.147 l
+3.032 0.515 l
+3.797 0.515 l
+3.797 1.721 l
+h
+7.269 1.721 m
+7.269 0.515 l
+8.019 0.515 l
+8.019 -0.147 l
+7.269 -0.147 l
+7.269 -3.218 l
+7.269 -3.417 7.298 -3.568 7.358 -3.674 c
+7.423 -3.773 7.537 -3.821 7.695 -3.821 c
+7.802 -3.821 7.909 -3.803 8.019 -3.763 c
+8.004 -4.453 l
+7.828 -4.512 7.64 -4.542 7.445 -4.542 c
+7.122 -4.542 6.876 -4.432 6.71 -4.203 c
+6.542 -3.968 6.461 -3.645 6.461 -3.233 c
+6.461 -0.147 l
+5.696 -0.147 l
+5.696 0.515 l
+6.461 0.515 l
+6.461 1.721 l
+h
+12.462 -2.204 m
+12.462 -2.991 12.318 -3.579 12.035 -3.968 c
+11.749 -4.351 11.348 -4.542 10.83 -4.542 c
+10.33 -4.542 9.948 -4.357 9.683 -3.983 c
+9.683 -6.364 l
+8.875 -6.364 l
+8.875 0.515 l
+9.61 0.515 l
+9.64 -0.029 l
+9.912 0.389 10.305 0.603 10.816 0.603 c
+11.363 0.603 11.771 0.412 12.035 0.03 c
+12.307 -0.345 12.451 -0.907 12.462 -1.66 c
+h
+11.653 -1.749 m
+11.653 -1.19 11.565 -0.779 11.389 -0.514 c
+11.219 -0.249 10.947 -0.118 10.565 -0.118 c
+10.172 -0.118 9.878 -0.309 9.683 -0.69 c
+9.683 -3.277 l
+9.878 -3.659 10.176 -3.85 10.581 -3.85 c
+10.94 -3.85 11.205 -3.719 11.374 -3.453 c
+11.55 -3.189 11.642 -2.786 11.653 -2.234 c
+h
+15.747 -3.175 m
+15.747 -3.009 15.676 -2.863 15.541 -2.734 c
+15.412 -2.609 15.159 -2.462 14.776 -2.293 c
+14.343 -2.109 14.034 -1.951 13.851 -1.822 c
+13.675 -1.697 13.542 -1.554 13.454 -1.396 c
+13.365 -1.23 13.322 -1.036 13.322 -0.808 c
+13.322 -0.396 13.469 -0.058 13.762 0.206 c
+14.057 0.47 14.431 0.603 14.894 0.603 c
+15.383 0.603 15.776 0.46 16.07 0.177 c
+16.364 -0.099 16.511 -0.455 16.511 -0.897 c
+15.703 -0.897 l
+15.703 -0.672 15.622 -0.481 15.468 -0.323 c
+15.321 -0.168 15.129 -0.087 14.894 -0.087 c
+14.659 -0.087 14.471 -0.154 14.336 -0.278 c
+14.196 -0.396 14.13 -0.565 14.13 -0.779 c
+14.13 -0.947 14.178 -1.08 14.277 -1.176 c
+14.373 -1.275 14.615 -1.407 14.997 -1.572 c
+15.603 -1.807 16.015 -2.04 16.232 -2.263 c
+16.445 -2.491 16.555 -2.774 16.555 -3.116 c
+16.555 -3.549 16.401 -3.895 16.1 -4.159 c
+15.805 -4.417 15.408 -4.542 14.909 -4.542 c
+14.398 -4.542 13.983 -4.394 13.66 -4.101 c
+13.336 -3.806 13.174 -3.432 13.174 -2.969 c
+13.997 -2.969 l
+14.005 -3.245 14.089 -3.461 14.247 -3.615 c
+14.402 -3.773 14.622 -3.85 14.909 -3.85 c
+15.181 -3.85 15.387 -3.792 15.526 -3.674 c
+15.674 -3.549 15.747 -3.38 15.747 -3.175 c
+17.492 -4.012 m
+17.492 -3.865 17.536 -3.744 17.625 -3.645 c
+17.713 -3.549 17.837 -3.498 18.007 -3.498 c
+18.184 -3.498 18.315 -3.549 18.404 -3.645 c
+18.499 -3.744 18.55 -3.865 18.55 -4.012 c
+18.55 -4.152 18.499 -4.27 18.404 -4.365 c
+18.315 -4.453 18.184 -4.498 18.007 -4.498 c
+17.837 -4.498 17.713 -4.453 17.625 -4.365 c
+17.536 -4.27 17.492 -4.152 17.492 -4.012 c
+17.492 -0.249 m
+17.492 -0.103 17.536 0.019 17.625 0.118 c
+17.713 0.214 17.837 0.265 18.007 0.265 c
+18.184 0.265 18.315 0.214 18.404 0.118 c
+18.499 0.019 18.55 -0.103 18.55 -0.249 c
+18.55 -0.389 18.499 -0.507 18.404 -0.602 c
+18.315 -0.69 18.184 -0.735 18.007 -0.735 c
+17.837 -0.735 17.713 -0.69 17.625 -0.602 c
+17.536 -0.507 17.492 -0.389 17.492 -0.249 c
+19.786 -5.026 m
+19.094 -5.026 l
+21.432 2.234 l
+22.122 2.234 l
+h
+22.148 -5.026 m
+21.457 -5.026 l
+23.794 2.234 l
+24.486 2.234 l
+h
+25.103 -1.749 m
+25.103 -0.977 25.242 -0.393 25.529 0 c
+25.812 0.401 26.223 0.603 26.764 0.603 c
+27.271 0.603 27.661 0.387 27.925 -0.043 c
+27.954 0.515 l
+28.69 0.515 l
+28.69 -4.498 l
+28.69 -5.115 28.532 -5.585 28.219 -5.909 c
+27.903 -6.239 27.47 -6.408 26.911 -6.408 c
+26.664 -6.408 26.385 -6.342 26.073 -6.217 c
+25.768 -6.1 25.544 -5.953 25.397 -5.776 c
+25.72 -5.217 l
+26.062 -5.548 26.433 -5.718 26.837 -5.718 c
+27.514 -5.718 27.859 -5.35 27.881 -4.615 c
+27.881 -3.968 l
+27.616 -4.351 27.241 -4.542 26.764 -4.542 c
+26.242 -4.542 25.841 -4.351 25.558 -3.968 c
+25.272 -3.579 25.122 -3.021 25.103 -2.293 c
+h
+25.926 -2.204 m
+25.926 -2.763 26.003 -3.175 26.161 -3.439 c
+26.327 -3.696 26.595 -3.821 26.969 -3.821 c
+27.37 -3.821 27.675 -3.615 27.881 -3.204 c
+27.881 -0.72 l
+27.675 -0.319 27.374 -0.118 26.984 -0.118 c
+26.61 -0.118 26.341 -0.249 26.176 -0.514 c
+26.007 -0.771 25.926 -1.168 25.926 -1.705 c
+h
+30.593 -4.453 -0.808 4.968 re
+30.651 1.838 m
+30.651 1.698 30.611 1.58 30.534 1.485 c
+30.464 1.397 30.35 1.353 30.196 1.353 c
+30.038 1.353 29.92 1.397 29.843 1.485 c
+29.773 1.58 29.741 1.698 29.741 1.838 c
+29.741 1.974 29.773 2.088 29.843 2.176 c
+29.92 2.271 30.034 2.323 30.181 2.323 c
+30.336 2.323 30.453 2.271 30.534 2.176 c
+30.611 2.088 30.651 1.974 30.651 1.838 c
+32.845 1.721 m
+32.845 0.515 l
+33.595 0.515 l
+33.595 -0.147 l
+32.845 -0.147 l
+32.845 -3.218 l
+32.845 -3.417 32.875 -3.568 32.933 -3.674 c
+33 -3.773 33.114 -3.821 33.272 -3.821 c
+33.378 -3.821 33.485 -3.803 33.595 -3.763 c
+33.581 -4.453 l
+33.404 -4.512 33.217 -4.542 33.022 -4.542 c
+32.698 -4.542 32.453 -4.432 32.287 -4.203 c
+32.118 -3.968 32.037 -3.645 32.037 -3.233 c
+32.037 -0.147 l
+31.273 -0.147 l
+31.273 0.515 l
+32.037 0.515 l
+32.037 1.721 l
+h
+35.95 -1.955 -1.896 0.691 re
+39.104 -3.175 m
+39.104 -3.009 39.034 -2.863 38.898 -2.734 c
+38.769 -2.609 38.516 -2.462 38.134 -2.293 c
+37.7 -2.109 37.392 -1.951 37.207 -1.822 c
+37.031 -1.697 36.899 -1.554 36.81 -1.396 c
+36.723 -1.23 36.679 -1.036 36.679 -0.808 c
+36.679 -0.396 36.825 -0.058 37.119 0.206 c
+37.413 0.47 37.788 0.603 38.251 0.603 c
+38.74 0.603 39.133 0.46 39.427 0.177 c
+39.721 -0.099 39.868 -0.455 39.868 -0.897 c
+39.06 -0.897 l
+39.06 -0.672 38.979 -0.481 38.824 -0.323 c
+38.678 -0.168 38.487 -0.087 38.251 -0.087 c
+38.016 -0.087 37.828 -0.154 37.693 -0.278 c
+37.552 -0.396 37.487 -0.565 37.487 -0.779 c
+37.487 -0.947 37.535 -1.08 37.633 -1.176 c
+37.729 -1.275 37.972 -1.407 38.354 -1.572 c
+38.96 -1.807 39.372 -2.04 39.588 -2.263 c
+39.802 -2.491 39.912 -2.774 39.912 -3.116 c
+39.912 -3.549 39.758 -3.895 39.456 -4.159 c
+39.162 -4.417 38.765 -4.542 38.266 -4.542 c
+37.755 -4.542 37.34 -4.394 37.016 -4.101 c
+36.693 -3.806 36.531 -3.432 36.531 -2.969 c
+37.354 -2.969 l
+37.362 -3.245 37.446 -3.461 37.604 -3.615 c
+37.758 -3.773 37.979 -3.85 38.266 -3.85 c
+38.537 -3.85 38.743 -3.792 38.883 -3.674 c
+39.03 -3.549 39.104 -3.38 39.104 -3.175 c
+42.521 -3.85 m
+42.786 -3.85 42.996 -3.77 43.153 -3.601 c
+43.318 -3.436 43.41 -3.197 43.432 -2.881 c
+44.197 -2.881 l
+44.174 -3.362 44.006 -3.759 43.682 -4.072 c
+43.366 -4.388 42.981 -4.542 42.521 -4.542 c
+41.903 -4.542 41.433 -4.351 41.11 -3.968 c
+40.787 -3.579 40.625 -2.998 40.625 -2.234 c
+40.625 -1.69 l
+40.625 -0.937 40.787 -0.367 41.11 0.015 c
+41.433 0.405 41.897 0.603 42.506 0.603 c
+43.013 0.603 43.418 0.441 43.711 0.118 c
+44.013 -0.198 44.174 -0.631 44.197 -1.19 c
+43.432 -1.19 l
+43.41 -0.83 43.322 -0.554 43.168 -0.367 c
+43.01 -0.183 42.79 -0.087 42.506 -0.087 c
+42.154 -0.087 41.889 -0.213 41.712 -0.455 c
+41.536 -0.69 41.441 -1.08 41.433 -1.616 c
+41.433 -2.248 l
+41.433 -2.836 41.521 -3.256 41.698 -3.498 c
+41.874 -3.733 42.146 -3.85 42.521 -3.85 c
+45.733 0.515 m
+45.762 0.059 l
+46.063 0.42 46.46 0.603 46.953 0.603 c
+47.511 0.603 47.897 0.36 48.114 -0.118 c
+48.427 0.36 48.864 0.603 49.423 0.603 c
+50.341 0.603 50.811 0.034 50.834 -1.102 c
+50.834 -4.453 l
+50.025 -4.453 l
+50.025 -1.176 l
+50.025 -0.823 49.955 -0.558 49.819 -0.382 c
+49.691 -0.205 49.474 -0.118 49.172 -0.118 c
+48.926 -0.118 48.724 -0.213 48.569 -0.396 c
+48.423 -0.584 48.334 -0.823 48.305 -1.117 c
+48.305 -4.453 l
+47.497 -4.453 l
+47.497 -1.146 l
+47.486 -0.463 47.206 -0.118 46.659 -0.118 c
+46.248 -0.118 45.957 -0.323 45.791 -0.735 c
+45.791 -4.453 l
+44.983 -4.453 l
+44.983 0.515 l
+h
+51.818 -4.012 m
+51.818 -3.865 51.862 -3.744 51.951 -3.645 c
+52.039 -3.549 52.164 -3.498 52.332 -3.498 c
+52.509 -3.498 52.641 -3.549 52.729 -3.645 c
+52.825 -3.744 52.876 -3.865 52.876 -4.012 c
+52.876 -4.152 52.825 -4.27 52.729 -4.365 c
+52.641 -4.453 52.509 -4.498 52.332 -4.498 c
+52.164 -4.498 52.039 -4.453 51.951 -4.365 c
+51.862 -4.27 51.818 -4.152 51.818 -4.012 c
+55.875 -3.85 m
+56.14 -3.85 56.349 -3.77 56.507 -3.601 c
+56.673 -3.436 56.764 -3.197 56.787 -2.881 c
+57.551 -2.881 l
+57.529 -3.362 57.36 -3.759 57.036 -4.072 c
+56.72 -4.388 56.334 -4.542 55.875 -4.542 c
+55.258 -4.542 54.788 -4.351 54.464 -3.968 c
+54.141 -3.579 53.979 -2.998 53.979 -2.234 c
+53.979 -1.69 l
+53.979 -0.937 54.141 -0.367 54.464 0.015 c
+54.788 0.405 55.251 0.603 55.86 0.603 c
+56.367 0.603 56.772 0.441 57.066 0.118 c
+57.367 -0.198 57.529 -0.631 57.551 -1.19 c
+56.787 -1.19 l
+56.764 -0.83 56.677 -0.554 56.522 -0.367 c
+56.363 -0.183 56.143 -0.087 55.86 -0.087 c
+55.507 -0.087 55.243 -0.213 55.067 -0.455 c
+54.89 -0.69 54.795 -1.08 54.788 -1.616 c
+54.788 -2.248 l
+54.788 -2.836 54.875 -3.256 55.052 -3.498 c
+55.228 -3.733 55.501 -3.85 55.875 -3.85 c
+58.157 -1.734 m
+58.157 -1.022 58.326 -0.452 58.672 -0.029 c
+59.025 0.389 59.488 0.603 60.068 0.603 c
+60.645 0.603 61.105 0.397 61.45 -0.014 c
+61.803 -0.419 61.983 -0.977 61.994 -1.69 c
+61.994 -2.204 l
+61.994 -2.932 61.817 -3.505 61.464 -3.924 c
+61.119 -4.336 60.66 -4.542 60.083 -4.542 c
+59.502 -4.542 59.039 -4.343 58.686 -3.939 c
+58.341 -3.528 58.165 -2.977 58.157 -2.277 c
+h
+58.966 -2.204 m
+58.966 -2.715 59.062 -3.116 59.26 -3.41 c
+59.465 -3.704 59.737 -3.85 60.083 -3.85 c
+60.795 -3.85 61.163 -3.333 61.185 -2.293 c
+61.185 -1.734 l
+61.185 -1.227 61.082 -0.823 60.876 -0.529 c
+60.678 -0.235 60.41 -0.087 60.068 -0.087 c
+59.734 -0.087 59.465 -0.235 59.26 -0.529 c
+59.062 -0.823 58.966 -1.227 58.966 -1.734 c
+h
+63.617 0.515 m
+63.648 0.059 l
+63.949 0.42 64.346 0.603 64.838 0.603 c
+65.396 0.603 65.782 0.36 66 -0.118 c
+66.312 0.36 66.749 0.603 67.307 0.603 c
+68.226 0.603 68.697 0.034 68.718 -1.102 c
+68.718 -4.453 l
+67.91 -4.453 l
+67.91 -1.176 l
+67.91 -0.823 67.841 -0.558 67.704 -0.382 c
+67.575 -0.205 67.359 -0.118 67.058 -0.118 c
+66.811 -0.118 66.609 -0.213 66.455 -0.396 c
+66.308 -0.584 66.22 -0.823 66.19 -1.117 c
+66.19 -4.453 l
+65.381 -4.453 l
+65.381 -1.146 l
+65.371 -0.463 65.092 -0.118 64.544 -0.118 c
+64.132 -0.118 63.842 -0.323 63.677 -0.735 c
+63.677 -4.453 l
+62.869 -4.453 l
+62.869 0.515 l
+h
+69.88 -5.026 m
+69.189 -5.026 l
+71.526 2.234 l
+72.217 2.234 l
+h
+72.834 -1.749 m
+72.834 -0.977 72.974 -0.393 73.26 0 c
+73.543 0.401 73.955 0.603 74.495 0.603 c
+74.966 0.603 75.333 0.412 75.598 0.03 c
+75.598 2.602 l
+76.421 2.602 l
+76.421 -4.453 l
+75.671 -4.453 l
+75.627 -3.924 l
+75.363 -4.336 74.984 -4.542 74.495 -4.542 c
+73.973 -4.542 73.57 -4.347 73.275 -3.954 c
+72.982 -3.553 72.834 -2.983 72.834 -2.248 c
+h
+73.643 -2.204 m
+73.643 -2.763 73.724 -3.175 73.892 -3.439 c
+74.058 -3.696 74.326 -3.821 74.701 -3.821 c
+75.101 -3.821 75.399 -3.623 75.598 -3.218 c
+75.598 -0.706 l
+75.392 -0.316 75.091 -0.118 74.701 -0.118 c
+74.326 -0.118 74.058 -0.249 73.892 -0.514 c
+73.724 -0.771 73.643 -1.168 73.643 -1.705 c
+h
+77.314 -1.734 m
+77.314 -1.022 77.483 -0.452 77.828 -0.029 c
+78.181 0.389 78.644 0.603 79.224 0.603 c
+79.802 0.603 80.261 0.397 80.606 -0.014 c
+80.959 -0.419 81.139 -0.977 81.15 -1.69 c
+81.15 -2.204 l
+81.15 -2.932 80.974 -3.505 80.621 -3.924 c
+80.275 -4.336 79.816 -4.542 79.239 -4.542 c
+78.659 -4.542 78.195 -4.343 77.843 -3.939 c
+77.497 -3.528 77.321 -2.977 77.314 -2.277 c
+h
+78.122 -2.204 m
+78.122 -2.715 78.218 -3.116 78.416 -3.41 c
+78.622 -3.704 78.894 -3.85 79.239 -3.85 c
+79.953 -3.85 80.319 -3.333 80.342 -2.293 c
+80.342 -1.734 l
+80.342 -1.227 80.239 -0.823 80.033 -0.529 c
+79.835 -0.235 79.567 -0.087 79.224 -0.087 c
+78.89 -0.087 78.622 -0.235 78.416 -0.529 c
+78.218 -0.823 78.122 -1.227 78.122 -1.734 c
+h
+85.715 -3.027 m
+86.478 0.515 l
+87.272 0.515 l
+86.067 -4.453 l
+85.42 -4.453 l
+84.45 -0.897 l
+83.494 -4.453 l
+82.848 -4.453 l
+81.643 0.515 l
+82.436 0.515 l
+83.215 -2.94 l
+84.127 0.515 l
+84.774 0.515 l
+h
+88.647 0.515 m
+88.676 -0.014 l
+88.988 0.397 89.389 0.603 89.881 0.603 c
+90.763 0.603 91.208 0.019 91.219 -1.146 c
+91.219 -4.453 l
+90.411 -4.453 l
+90.411 -1.19 l
+90.411 -0.801 90.345 -0.525 90.22 -0.367 c
+90.091 -0.201 89.896 -0.118 89.632 -0.118 c
+89.426 -0.118 89.242 -0.187 89.088 -0.323 c
+88.93 -0.452 88.801 -0.625 88.705 -0.837 c
+88.705 -4.453 l
+87.882 -4.453 l
+87.882 0.515 l
+h
+93.137 -4.453 -0.808 7.056 re
+94.159 -1.734 m
+94.159 -1.022 94.328 -0.452 94.673 -0.029 c
+95.026 0.389 95.489 0.603 96.069 0.603 c
+96.647 0.603 97.106 0.397 97.451 -0.014 c
+97.804 -0.419 97.985 -0.977 97.995 -1.69 c
+97.995 -2.204 l
+97.995 -2.932 97.819 -3.505 97.466 -3.924 c
+97.121 -4.336 96.661 -4.542 96.084 -4.542 c
+95.504 -4.542 95.041 -4.343 94.688 -3.939 c
+94.343 -3.528 94.166 -2.977 94.159 -2.277 c
+h
+94.968 -2.204 m
+94.968 -2.715 95.063 -3.116 95.261 -3.41 c
+95.467 -3.704 95.739 -3.85 96.084 -3.85 c
+96.797 -3.85 97.165 -3.333 97.187 -2.293 c
+97.187 -1.734 l
+97.187 -1.227 97.084 -0.823 96.878 -0.529 c
+96.68 -0.235 96.412 -0.087 96.069 -0.087 c
+95.735 -0.087 95.467 -0.235 95.261 -0.529 c
+95.063 -0.823 94.968 -1.227 94.968 -1.734 c
+h
+101.413 -4.453 m
+101.362 -4.347 101.328 -4.167 101.31 -3.91 c
+101.023 -4.332 100.656 -4.542 100.208 -4.542 c
+99.755 -4.542 99.402 -4.421 99.149 -4.174 c
+98.903 -3.921 98.781 -3.564 98.781 -3.101 c
+98.781 -2.594 98.951 -2.19 99.296 -1.896 c
+99.637 -1.602 100.108 -1.452 100.707 -1.44 c
+101.295 -1.44 l
+101.295 -0.911 l
+101.295 -0.617 101.225 -0.407 101.089 -0.278 c
+100.961 -0.154 100.766 -0.087 100.501 -0.087 c
+100.256 -0.087 100.053 -0.161 99.899 -0.309 c
+99.752 -0.455 99.678 -0.642 99.678 -0.866 c
+98.855 -0.866 l
+98.855 -0.613 98.929 -0.367 99.076 -0.132 c
+99.23 0.104 99.436 0.283 99.693 0.412 c
+99.947 0.537 100.229 0.603 100.545 0.603 c
+101.052 0.603 101.442 0.474 101.707 0.221 c
+101.971 -0.025 102.104 -0.393 102.104 -0.881 c
+102.104 -3.38 l
+102.111 -3.763 102.166 -4.097 102.265 -4.38 c
+102.265 -4.453 l
+h
+100.34 -3.806 m
+100.535 -3.806 100.722 -3.755 100.898 -3.645 c
+101.082 -3.528 101.214 -3.391 101.295 -3.233 c
+101.295 -2.042 l
+100.84 -2.042 l
+100.458 -2.054 100.152 -2.142 99.928 -2.308 c
+99.701 -2.476 99.59 -2.705 99.59 -2.998 c
+99.59 -3.285 99.641 -3.491 99.752 -3.615 c
+99.87 -3.744 100.065 -3.806 100.34 -3.806 c
+103.015 -1.749 m
+103.015 -0.977 103.155 -0.393 103.441 0 c
+103.724 0.401 104.136 0.603 104.676 0.603 c
+105.146 0.603 105.514 0.412 105.779 0.03 c
+105.779 2.602 l
+106.602 2.602 l
+106.602 -4.453 l
+105.852 -4.453 l
+105.808 -3.924 l
+105.543 -4.336 105.164 -4.542 104.676 -4.542 c
+104.154 -4.542 103.75 -4.347 103.456 -3.954 c
+103.162 -3.553 103.015 -2.983 103.015 -2.248 c
+h
+103.824 -2.204 m
+103.824 -2.763 103.905 -3.175 104.073 -3.439 c
+104.239 -3.696 104.507 -3.821 104.882 -3.821 c
+105.282 -3.821 105.58 -3.623 105.779 -3.218 c
+105.779 -0.706 l
+105.573 -0.316 105.272 -0.118 104.882 -0.118 c
+104.507 -0.118 104.239 -0.249 104.073 -0.514 c
+103.905 -0.771 103.824 -1.168 103.824 -1.705 c
+h
+110.082 -3.175 m
+110.082 -3.009 110.012 -2.863 109.876 -2.734 c
+109.748 -2.609 109.494 -2.462 109.112 -2.293 c
+108.678 -2.109 108.369 -1.951 108.185 -1.822 c
+108.009 -1.697 107.876 -1.554 107.789 -1.396 c
+107.701 -1.23 107.656 -1.036 107.656 -0.808 c
+107.656 -0.396 107.803 -0.058 108.098 0.206 c
+108.391 0.47 108.766 0.603 109.229 0.603 c
+109.718 0.603 110.111 0.46 110.405 0.177 c
+110.699 -0.099 110.846 -0.455 110.846 -0.897 c
+110.037 -0.897 l
+110.037 -0.672 109.956 -0.481 109.802 -0.323 c
+109.655 -0.168 109.464 -0.087 109.229 -0.087 c
+108.994 -0.087 108.807 -0.154 108.67 -0.278 c
+108.531 -0.396 108.464 -0.565 108.464 -0.779 c
+108.464 -0.947 108.512 -1.08 108.612 -1.176 c
+108.707 -1.275 108.95 -1.407 109.332 -1.572 c
+109.939 -1.807 110.35 -2.04 110.567 -2.263 c
+110.78 -2.491 110.89 -2.774 110.89 -3.116 c
+110.89 -3.549 110.735 -3.895 110.434 -4.159 c
+110.141 -4.417 109.744 -4.542 109.243 -4.542 c
+108.733 -4.542 108.318 -4.394 107.994 -4.101 c
+107.671 -3.806 107.51 -3.432 107.51 -2.969 c
+108.333 -2.969 l
+108.339 -3.245 108.424 -3.461 108.582 -3.615 c
+108.736 -3.773 108.957 -3.85 109.243 -3.85 c
+109.515 -3.85 109.721 -3.792 109.861 -3.674 c
+110.008 -3.549 110.082 -3.38 110.082 -3.175 c
+111.824 -4.012 m
+111.824 -3.865 111.867 -3.744 111.956 -3.645 c
+112.044 -3.549 112.169 -3.498 112.337 -3.498 c
+112.514 -3.498 112.647 -3.549 112.734 -3.645 c
+112.83 -3.744 112.882 -3.865 112.882 -4.012 c
+112.882 -4.152 112.83 -4.27 112.734 -4.365 c
+112.647 -4.453 112.514 -4.498 112.337 -4.498 c
+112.169 -4.498 112.044 -4.453 111.956 -4.365 c
+111.867 -4.27 111.824 -4.152 111.824 -4.012 c
+f
+Q
+q 1 0 0 1 539.3456 316.4395 cm
+0 0 m
+-2.249 0 l
+-2.778 -1.749 l
+-3.645 -1.749 l
+-1.484 4.939 l
+-0.765 4.939 l
+1.396 -1.749 l
+0.529 -1.749 l
+h
+-2.028 0.721 m
+-0.206 0.721 l
+-1.132 3.763 l
+h
+2.734 3.219 m
+2.763 2.691 l
+3.075 3.102 3.476 3.308 3.969 3.308 c
+4.85 3.308 5.295 2.723 5.307 1.559 c
+5.307 -1.749 l
+4.498 -1.749 l
+4.498 1.515 l
+4.498 1.904 4.432 2.18 4.307 2.338 c
+4.178 2.503 3.983 2.587 3.719 2.587 c
+3.513 2.587 3.329 2.517 3.175 2.382 c
+3.017 2.253 2.888 2.08 2.793 1.867 c
+2.793 -1.749 l
+1.97 -1.749 l
+1.97 3.219 l
+h
+10.911 -1.837 m
+10.293 -1.837 9.816 -1.657 9.485 -1.294 c
+9.161 -0.922 8.992 -0.378 8.985 0.339 c
+8.985 0.941 l
+8.985 1.676 9.147 2.253 9.469 2.675 c
+9.793 3.094 10.242 3.308 10.822 3.308 c
+11.399 3.308 11.829 3.12 12.116 2.749 c
+12.409 2.386 12.56 1.812 12.571 1.029 c
+12.571 0.5 l
+9.793 0.5 l
+9.793 0.383 l
+9.793 -0.158 9.889 -0.551 10.088 -0.793 c
+10.293 -1.028 10.58 -1.146 10.954 -1.146 c
+11.197 -1.146 11.41 -1.102 11.586 -1.014 c
+11.771 -0.926 11.943 -0.786 12.101 -0.588 c
+12.527 -1.102 l
+12.174 -1.595 11.634 -1.837 10.911 -1.837 c
+10.822 2.617 m
+10.487 2.617 10.238 2.5 10.073 2.264 c
+9.903 2.036 9.812 1.68 9.793 1.191 c
+11.748 1.191 l
+11.748 1.309 l
+11.726 1.779 11.645 2.11 11.499 2.309 c
+11.351 2.514 11.123 2.617 10.822 2.617 c
+14.805 1.411 m
+15.658 3.219 l
+16.598 3.219 l
+15.247 0.765 l
+16.628 -1.749 l
+15.702 -1.749 l
+14.82 0.118 l
+13.938 -1.749 l
+12.997 -1.749 l
+14.379 0.765 l
+13.042 3.219 l
+13.968 3.219 l
+h
+18.98 -1.146 m
+19.245 -1.146 19.455 -1.065 19.613 -0.897 c
+19.777 -0.731 19.869 -0.492 19.891 -0.176 c
+20.656 -0.176 l
+20.634 -0.658 20.465 -1.055 20.141 -1.367 c
+19.825 -1.683 19.439 -1.837 18.98 -1.837 c
+18.362 -1.837 17.892 -1.646 17.569 -1.263 c
+17.246 -0.874 17.084 -0.294 17.084 0.47 c
+17.084 1.015 l
+17.084 1.768 17.246 2.338 17.569 2.72 c
+17.892 3.109 18.356 3.308 18.965 3.308 c
+19.472 3.308 19.877 3.146 20.17 2.822 c
+20.472 2.506 20.634 2.073 20.656 1.515 c
+19.891 1.515 l
+19.869 1.875 19.781 2.151 19.627 2.338 c
+19.469 2.521 19.248 2.617 18.965 2.617 c
+18.613 2.617 18.348 2.492 18.171 2.249 c
+17.995 2.014 17.899 1.625 17.892 1.088 c
+17.892 0.456 l
+17.892 -0.132 17.98 -0.551 18.157 -0.793 c
+18.333 -1.028 18.605 -1.146 18.98 -1.146 c
+23.206 -1.837 m
+22.589 -1.837 22.111 -1.657 21.78 -1.294 c
+21.456 -0.922 21.288 -0.378 21.28 0.339 c
+21.28 0.941 l
+21.28 1.676 21.442 2.253 21.766 2.675 c
+22.089 3.094 22.537 3.308 23.118 3.308 c
+23.694 3.308 24.124 3.12 24.411 2.749 c
+24.706 2.386 24.856 1.812 24.867 1.029 c
+24.867 0.5 l
+22.089 0.5 l
+22.089 0.383 l
+22.089 -0.158 22.185 -0.551 22.383 -0.793 c
+22.589 -1.028 22.875 -1.146 23.25 -1.146 c
+23.493 -1.146 23.706 -1.102 23.883 -1.014 c
+24.066 -0.926 24.239 -0.786 24.397 -0.588 c
+24.823 -1.102 l
+24.471 -1.595 23.93 -1.837 23.206 -1.837 c
+23.118 2.617 m
+22.784 2.617 22.534 2.5 22.368 2.264 c
+22.2 2.036 22.107 1.68 22.089 1.191 c
+24.043 1.191 l
+24.043 1.309 l
+24.022 1.779 23.941 2.11 23.794 2.309 c
+23.647 2.514 23.419 2.617 23.118 2.617 c
+26.543 -1.749 -0.809 7.056 re
+28.608 -1.749 -0.808 7.056 re
+31.567 -1.837 m
+30.948 -1.837 30.471 -1.657 30.14 -1.294 c
+29.817 -0.922 29.648 -0.378 29.641 0.339 c
+29.641 0.941 l
+29.641 1.676 29.803 2.253 30.125 2.675 c
+30.449 3.094 30.898 3.308 31.478 3.308 c
+32.055 3.308 32.485 3.12 32.772 2.749 c
+33.065 2.386 33.216 1.812 33.227 1.029 c
+33.227 0.5 l
+30.449 0.5 l
+30.449 0.383 l
+30.449 -0.158 30.545 -0.551 30.743 -0.793 c
+30.948 -1.028 31.235 -1.146 31.61 -1.146 c
+31.852 -1.146 32.066 -1.102 32.242 -1.014 c
+32.426 -0.926 32.599 -0.786 32.757 -0.588 c
+33.183 -1.102 l
+32.83 -1.595 32.29 -1.837 31.567 -1.837 c
+31.478 2.617 m
+31.143 2.617 30.894 2.5 30.728 2.264 c
+30.559 2.036 30.468 1.68 30.449 1.191 c
+32.404 1.191 l
+32.404 1.309 l
+32.382 1.779 32.301 2.11 32.154 2.309 c
+32.007 2.514 31.779 2.617 31.478 2.617 c
+34.785 3.219 m
+34.815 2.691 l
+35.127 3.102 35.528 3.308 36.02 3.308 c
+36.901 3.308 37.346 2.723 37.358 1.559 c
+37.358 -1.749 l
+36.549 -1.749 l
+36.549 1.515 l
+36.549 1.904 36.483 2.18 36.358 2.338 c
+36.23 2.503 36.035 2.587 35.77 2.587 c
+35.565 2.587 35.38 2.517 35.226 2.382 c
+35.068 2.253 34.94 2.08 34.844 1.867 c
+34.844 -1.749 l
+34.021 -1.749 l
+34.021 3.219 l
+h
+39.471 4.425 m
+39.471 3.219 l
+40.22 3.219 l
+40.22 2.558 l
+39.471 2.558 l
+39.471 -0.514 l
+39.471 -0.712 39.5 -0.864 39.559 -0.97 c
+39.625 -1.069 39.739 -1.117 39.897 -1.117 c
+40.003 -1.117 40.11 -1.099 40.22 -1.058 c
+40.205 -1.749 l
+40.029 -1.807 39.841 -1.837 39.647 -1.837 c
+39.324 -1.837 39.077 -1.727 38.912 -1.499 c
+38.743 -1.263 38.662 -0.941 38.662 -0.529 c
+38.662 2.558 l
+37.898 2.558 l
+37.898 3.219 l
+38.662 3.219 l
+38.662 4.425 l
+h
+48.154 -0.911 m
+48.007 -1.102 l
+47.584 -1.595 46.967 -1.837 46.155 -1.837 c
+45.427 -1.837 44.861 -1.598 44.45 -1.117 c
+44.038 -0.639 43.825 0.037 43.818 0.912 c
+43.818 2.22 l
+43.818 3.161 44.001 3.859 44.377 4.322 c
+44.747 4.792 45.31 5.027 46.067 5.027 c
+46.702 5.027 47.199 4.851 47.552 4.499 c
+47.912 4.146 48.113 3.639 48.154 2.984 c
+47.316 2.984 l
+47.276 3.396 47.158 3.72 46.964 3.955 c
+46.765 4.19 46.467 4.308 46.067 4.308 c
+45.585 4.308 45.233 4.15 45.009 3.837 c
+44.78 3.521 44.663 3.021 44.656 2.338 c
+44.656 0.971 l
+44.656 0.294 44.78 -0.228 45.038 -0.588 c
+45.302 -0.941 45.674 -1.117 46.155 -1.117 c
+46.603 -1.117 46.948 -1.01 47.184 -0.793 c
+47.316 -0.675 l
+47.316 0.867 l
+46.081 0.867 l
+46.081 1.588 l
+48.154 1.588 l
+h
+50.076 -1.749 -0.808 4.968 re
+50.135 4.543 m
+50.135 4.403 50.094 4.285 50.017 4.19 c
+49.948 4.102 49.834 4.057 49.679 4.057 c
+49.521 4.057 49.404 4.102 49.327 4.19 c
+49.256 4.285 49.223 4.403 49.223 4.543 c
+49.223 4.678 49.256 4.792 49.327 4.881 c
+49.404 4.976 49.518 5.027 49.664 5.027 c
+49.819 5.027 49.936 4.976 50.017 4.881 c
+50.094 4.792 50.135 4.678 50.135 4.543 c
+52.329 4.425 m
+52.329 3.219 l
+53.078 3.219 l
+53.078 2.558 l
+52.329 2.558 l
+52.329 -0.514 l
+52.329 -0.712 52.358 -0.864 52.417 -0.97 c
+52.483 -1.069 52.597 -1.117 52.755 -1.117 c
+52.861 -1.117 52.968 -1.099 53.078 -1.058 c
+53.063 -1.749 l
+52.887 -1.807 52.699 -1.837 52.505 -1.837 c
+52.182 -1.837 51.935 -1.727 51.77 -1.499 c
+51.6 -1.263 51.521 -0.941 51.521 -0.529 c
+51.521 2.558 l
+50.756 2.558 l
+50.756 3.219 l
+51.521 3.219 l
+51.521 4.425 l
+h
+58.443 -1.146 m
+58.708 -1.146 58.918 -1.065 59.076 -0.897 c
+59.241 -0.731 59.332 -0.492 59.355 -0.176 c
+60.119 -0.176 l
+60.097 -0.658 59.928 -1.055 59.604 -1.367 c
+59.288 -1.683 58.903 -1.837 58.443 -1.837 c
+57.826 -1.837 57.356 -1.646 57.032 -1.263 c
+56.709 -0.874 56.547 -0.294 56.547 0.47 c
+56.547 1.015 l
+56.547 1.768 56.709 2.338 57.032 2.72 c
+57.356 3.109 57.819 3.308 58.428 3.308 c
+58.935 3.308 59.34 3.146 59.634 2.822 c
+59.935 2.506 60.097 2.073 60.119 1.515 c
+59.355 1.515 l
+59.332 1.875 59.244 2.151 59.09 2.338 c
+58.932 2.521 58.712 2.617 58.428 2.617 c
+58.076 2.617 57.811 2.492 57.635 2.249 c
+57.458 2.014 57.362 1.625 57.356 1.088 c
+57.356 0.456 l
+57.356 -0.132 57.443 -0.551 57.62 -0.793 c
+57.796 -1.028 58.068 -1.146 58.443 -1.146 c
+60.729 0.971 m
+60.729 1.683 60.898 2.253 61.243 2.675 c
+61.596 3.094 62.06 3.308 62.64 3.308 c
+63.217 3.308 63.676 3.102 64.022 2.691 c
+64.375 2.286 64.555 1.727 64.566 1.015 c
+64.566 0.5 l
+64.566 -0.228 64.389 -0.801 64.036 -1.219 c
+63.691 -1.631 63.232 -1.837 62.654 -1.837 c
+62.074 -1.837 61.611 -1.639 61.258 -1.234 c
+60.913 -0.823 60.736 -0.272 60.729 0.427 c
+h
+61.538 0.5 m
+61.538 -0.01 61.633 -0.411 61.831 -0.706 c
+62.037 -0.999 62.309 -1.146 62.654 -1.146 c
+63.367 -1.146 63.735 -0.628 63.757 0.412 c
+63.757 0.971 l
+63.757 1.478 63.654 1.881 63.448 2.176 c
+63.25 2.469 62.982 2.617 62.64 2.617 c
+62.305 2.617 62.037 2.469 61.831 2.176 c
+61.633 1.881 61.538 1.478 61.538 0.971 c
+h
+67.968 -1.308 m
+67.693 -1.66 67.296 -1.837 66.777 -1.837 c
+66.337 -1.837 65.998 -1.687 65.763 -1.381 c
+65.536 -1.069 65.418 -0.613 65.411 -0.014 c
+65.411 3.219 l
+66.22 3.219 l
+66.22 0.044 l
+66.22 -0.731 66.455 -1.117 66.925 -1.117 c
+67.413 -1.117 67.751 -0.897 67.939 -0.455 c
+67.939 3.219 l
+68.762 3.219 l
+68.762 -1.749 l
+67.984 -1.749 l
+h
+71.857 2.455 m
+71.739 2.473 71.614 2.484 71.489 2.484 c
+71.066 2.484 70.776 2.257 70.621 1.808 c
+70.621 -1.749 l
+69.798 -1.749 l
+69.798 3.219 l
+70.592 3.219 l
+70.607 2.72 l
+70.82 3.109 71.128 3.308 71.533 3.308 c
+71.658 3.308 71.768 3.286 71.857 3.249 c
+h
+74.976 -0.47 m
+74.976 -0.305 74.906 -0.158 74.77 -0.029 c
+74.642 0.096 74.388 0.243 74.006 0.412 c
+73.572 0.595 73.264 0.754 73.08 0.882 c
+72.904 1.008 72.771 1.151 72.683 1.309 c
+72.595 1.474 72.551 1.669 72.551 1.897 c
+72.551 2.309 72.698 2.646 72.992 2.911 c
+73.285 3.175 73.661 3.308 74.124 3.308 c
+74.612 3.308 75.005 3.165 75.3 2.882 c
+75.593 2.606 75.741 2.249 75.741 1.808 c
+74.932 1.808 l
+74.932 2.033 74.851 2.224 74.697 2.382 c
+74.55 2.536 74.359 2.617 74.124 2.617 c
+73.888 2.617 73.701 2.55 73.565 2.426 c
+73.426 2.309 73.359 2.139 73.359 1.926 c
+73.359 1.757 73.407 1.625 73.507 1.529 c
+73.602 1.43 73.844 1.297 74.226 1.133 c
+74.833 0.897 75.244 0.665 75.461 0.441 c
+75.674 0.214 75.784 -0.07 75.784 -0.411 c
+75.784 -0.845 75.63 -1.19 75.329 -1.454 c
+75.035 -1.712 74.638 -1.837 74.138 -1.837 c
+73.628 -1.837 73.212 -1.69 72.888 -1.396 c
+72.566 -1.102 72.404 -0.727 72.404 -0.264 c
+73.227 -0.264 l
+73.235 -0.54 73.318 -0.756 73.476 -0.911 c
+73.631 -1.069 73.852 -1.146 74.138 -1.146 c
+74.411 -1.146 74.616 -1.087 74.756 -0.97 c
+74.903 -0.845 74.976 -0.675 74.976 -0.47 c
+78.438 -1.837 m
+77.821 -1.837 77.343 -1.657 77.012 -1.294 c
+76.688 -0.922 76.52 -0.378 76.512 0.339 c
+76.512 0.941 l
+76.512 1.676 76.674 2.253 76.998 2.675 c
+77.32 3.094 77.769 3.308 78.349 3.308 c
+78.926 3.308 79.356 3.12 79.643 2.749 c
+79.937 2.386 80.088 1.812 80.098 1.029 c
+80.098 0.5 l
+77.32 0.5 l
+77.32 0.383 l
+77.32 -0.158 77.416 -0.551 77.615 -0.793 c
+77.821 -1.028 78.107 -1.146 78.482 -1.146 c
+78.725 -1.146 78.937 -1.102 79.114 -1.014 c
+79.298 -0.926 79.47 -0.786 79.628 -0.588 c
+80.055 -1.102 l
+79.702 -1.595 79.161 -1.837 78.438 -1.837 c
+78.349 2.617 m
+78.016 2.617 77.765 2.5 77.6 2.264 c
+77.43 2.036 77.339 1.68 77.32 1.191 c
+79.275 1.191 l
+79.275 1.309 l
+79.254 1.779 79.173 2.11 79.026 2.309 c
+78.879 2.514 78.651 2.617 78.349 2.617 c
+85.416 -1.146 m
+85.681 -1.146 85.89 -1.065 86.049 -0.897 c
+86.213 -0.731 86.305 -0.492 86.328 -0.176 c
+87.092 -0.176 l
+87.07 -0.658 86.901 -1.055 86.577 -1.367 c
+86.261 -1.683 85.875 -1.837 85.416 -1.837 c
+84.799 -1.837 84.329 -1.646 84.005 -1.263 c
+83.682 -0.874 83.52 -0.294 83.52 0.47 c
+83.52 1.015 l
+83.52 1.768 83.682 2.338 84.005 2.72 c
+84.329 3.109 84.792 3.308 85.401 3.308 c
+85.908 3.308 86.313 3.146 86.606 2.822 c
+86.908 2.506 87.07 2.073 87.092 1.515 c
+86.328 1.515 l
+86.305 1.875 86.217 2.151 86.063 2.338 c
+85.905 2.521 85.684 2.617 85.401 2.617 c
+85.048 2.617 84.784 2.492 84.607 2.249 c
+84.431 2.014 84.335 1.625 84.329 1.088 c
+84.329 0.456 l
+84.329 -0.132 84.416 -0.551 84.593 -0.793 c
+84.769 -1.028 85.041 -1.146 85.416 -1.146 c
+90.421 -1.749 m
+90.369 -1.643 90.336 -1.462 90.319 -1.205 c
+90.032 -1.627 89.664 -1.837 89.216 -1.837 c
+88.763 -1.837 88.411 -1.716 88.158 -1.469 c
+87.911 -1.216 87.79 -0.86 87.79 -0.396 c
+87.79 0.111 87.959 0.515 88.304 0.809 c
+88.646 1.103 89.116 1.253 89.715 1.264 c
+90.303 1.264 l
+90.303 1.794 l
+90.303 2.088 90.234 2.297 90.097 2.426 c
+89.969 2.55 89.775 2.617 89.509 2.617 c
+89.264 2.617 89.062 2.544 88.907 2.396 c
+88.76 2.249 88.686 2.062 88.686 1.838 c
+87.863 1.838 l
+87.863 2.091 87.937 2.338 88.084 2.573 c
+88.239 2.808 88.445 2.988 88.701 3.117 c
+88.954 3.242 89.238 3.308 89.554 3.308 c
+90.061 3.308 90.45 3.179 90.716 2.926 c
+90.98 2.679 91.112 2.311 91.112 1.823 c
+91.112 -0.675 l
+91.119 -1.058 91.175 -1.392 91.273 -1.675 c
+91.273 -1.749 l
+h
+89.348 -1.102 m
+89.543 -1.102 89.731 -1.051 89.906 -0.941 c
+90.091 -0.823 90.223 -0.687 90.303 -0.529 c
+90.303 0.662 l
+89.848 0.662 l
+89.465 0.651 89.16 0.563 88.937 0.397 c
+88.709 0.229 88.599 0 88.599 -0.294 c
+88.599 -0.58 88.65 -0.786 88.76 -0.911 c
+88.877 -1.04 89.073 -1.102 89.348 -1.102 c
+92.95 3.219 m
+92.979 2.691 l
+93.291 3.102 93.692 3.308 94.184 3.308 c
+95.066 3.308 95.51 2.723 95.522 1.559 c
+95.522 -1.749 l
+94.714 -1.749 l
+94.714 1.515 l
+94.714 1.904 94.647 2.18 94.522 2.338 c
+94.394 2.503 94.199 2.587 93.934 2.587 c
+93.729 2.587 93.544 2.517 93.39 2.382 c
+93.232 2.253 93.104 2.08 93.008 1.867 c
+93.008 -1.749 l
+92.185 -1.749 l
+92.185 3.219 l
+h
+102.949 0.5 m
+102.949 -0.276 102.805 -0.86 102.522 -1.249 c
+102.246 -1.643 101.846 -1.837 101.317 -1.837 c
+100.795 -1.837 100.405 -1.612 100.141 -1.161 c
+100.097 -1.749 l
+99.362 -1.749 l
+99.362 5.307 l
+100.17 5.307 l
+100.17 2.675 l
+100.435 3.094 100.817 3.308 101.317 3.308 c
+101.853 3.308 102.261 3.117 102.537 2.735 c
+102.809 2.352 102.949 1.768 102.949 0.985 c
+h
+102.14 0.956 m
+102.14 1.544 102.055 1.959 101.89 2.205 c
+101.733 2.459 101.467 2.587 101.097 2.587 c
+100.685 2.587 100.376 2.359 100.17 1.912 c
+100.17 -0.455 l
+100.365 -0.897 100.677 -1.117 101.111 -1.117 c
+101.471 -1.117 101.733 -0.992 101.89 -0.735 c
+102.055 -0.481 102.14 -0.084 102.14 0.456 c
+h
+105.59 -1.837 m
+104.973 -1.837 104.495 -1.657 104.164 -1.294 c
+103.842 -0.922 103.672 -0.378 103.665 0.339 c
+103.665 0.941 l
+103.665 1.676 103.827 2.253 104.15 2.675 c
+104.474 3.094 104.922 3.308 105.502 3.308 c
+106.08 3.308 106.51 3.12 106.796 2.749 c
+107.09 2.386 107.241 1.812 107.252 1.029 c
+107.252 0.5 l
+104.474 0.5 l
+104.474 0.383 l
+104.474 -0.158 104.569 -0.551 104.767 -0.793 c
+104.973 -1.028 105.26 -1.146 105.635 -1.146 c
+105.877 -1.146 106.09 -1.102 106.267 -1.014 c
+106.45 -0.926 106.623 -0.786 106.781 -0.588 c
+107.208 -1.102 l
+106.855 -1.595 106.315 -1.837 105.59 -1.837 c
+105.502 2.617 m
+105.168 2.617 104.918 2.5 104.752 2.264 c
+104.584 2.036 104.492 1.68 104.474 1.191 c
+106.429 1.191 l
+106.429 1.309 l
+106.406 1.779 106.325 2.11 106.178 2.309 c
+106.032 2.514 105.804 2.617 105.502 2.617 c
+111.202 -1.749 m
+111.202 2.558 l
+110.555 2.558 l
+110.555 3.219 l
+111.202 3.219 l
+111.202 3.793 l
+111.209 4.3 111.337 4.697 111.584 4.983 c
+111.827 5.266 112.176 5.409 112.628 5.409 c
+112.782 5.409 112.943 5.38 113.113 5.322 c
+113.068 4.63 l
+112.958 4.66 112.837 4.675 112.702 4.675 c
+112.238 4.675 112.01 4.344 112.01 3.69 c
+112.01 3.219 l
+112.863 3.219 l
+112.863 2.558 l
+112.01 2.558 l
+112.01 -1.749 l
+h
+113.532 0.971 m
+113.532 1.683 113.701 2.253 114.046 2.675 c
+114.399 3.094 114.862 3.308 115.443 3.308 c
+116.019 3.308 116.479 3.102 116.825 2.691 c
+117.177 2.286 117.357 1.727 117.369 1.015 c
+117.369 0.5 l
+117.369 -0.228 117.192 -0.801 116.839 -1.219 c
+116.494 -1.631 116.035 -1.837 115.457 -1.837 c
+114.877 -1.837 114.414 -1.639 114.061 -1.234 c
+113.716 -0.823 113.539 -0.272 113.532 0.427 c
+h
+114.34 0.5 m
+114.34 -0.01 114.435 -0.411 114.634 -0.706 c
+114.84 -0.999 115.112 -1.146 115.457 -1.146 c
+116.17 -1.146 116.538 -0.628 116.56 0.412 c
+116.56 0.971 l
+116.56 1.478 116.457 1.881 116.251 2.176 c
+116.052 2.469 115.784 2.617 115.443 2.617 c
+115.108 2.617 114.84 2.469 114.634 2.176 c
+114.435 1.881 114.34 1.478 114.34 0.971 c
+h
+120.767 -1.308 m
+120.492 -1.66 120.095 -1.837 119.576 -1.837 c
+119.136 -1.837 118.797 -1.687 118.562 -1.381 c
+118.335 -1.069 118.217 -0.613 118.21 -0.014 c
+118.21 3.219 l
+119.019 3.219 l
+119.019 0.044 l
+119.019 -0.731 119.254 -1.117 119.724 -1.117 c
+120.212 -1.117 120.55 -0.897 120.738 -0.455 c
+120.738 3.219 l
+121.561 3.219 l
+121.561 -1.749 l
+120.782 -1.749 l
+h
+123.366 3.219 m
+123.395 2.691 l
+123.707 3.102 124.108 3.308 124.6 3.308 c
+125.482 3.308 125.926 2.723 125.938 1.559 c
+125.938 -1.749 l
+125.13 -1.749 l
+125.13 1.515 l
+125.13 1.904 125.063 2.18 124.938 2.338 c
+124.81 2.503 124.615 2.587 124.351 2.587 c
+124.145 2.587 123.96 2.517 123.806 2.382 c
+123.648 2.253 123.52 2.08 123.424 1.867 c
+123.424 -1.749 l
+122.601 -1.749 l
+122.601 3.219 l
+h
+126.813 0.956 m
+126.813 1.727 126.952 2.311 127.239 2.705 c
+127.522 3.105 127.933 3.308 128.473 3.308 c
+128.943 3.308 129.311 3.117 129.576 2.735 c
+129.576 5.307 l
+130.399 5.307 l
+130.399 -1.749 l
+129.649 -1.749 l
+129.605 -1.219 l
+129.34 -1.631 128.962 -1.837 128.473 -1.837 c
+127.952 -1.837 127.547 -1.643 127.253 -1.249 c
+126.959 -0.849 126.813 -0.278 126.813 0.456 c
+h
+127.621 0.5 m
+127.621 -0.058 127.702 -0.47 127.871 -0.735 c
+128.036 -0.992 128.305 -1.117 128.679 -1.117 c
+129.08 -1.117 129.377 -0.918 129.576 -0.514 c
+129.576 1.999 l
+129.37 2.389 129.068 2.587 128.679 2.587 c
+128.305 2.587 128.036 2.455 127.871 2.191 c
+127.702 1.933 127.621 1.536 127.621 1 c
+h
+135.125 -1.749 -0.809 4.968 re
+135.183 4.543 m
+135.183 4.403 135.143 4.285 135.066 4.19 c
+134.996 4.102 134.882 4.057 134.728 4.057 c
+134.57 4.057 134.452 4.102 134.375 4.19 c
+134.306 4.285 134.272 4.403 134.272 4.543 c
+134.272 4.678 134.306 4.792 134.375 4.881 c
+134.452 4.976 134.566 5.027 134.713 5.027 c
+134.867 5.027 134.985 4.976 135.066 4.881 c
+135.143 4.792 135.183 4.678 135.183 4.543 c
+137.072 3.219 m
+137.102 2.691 l
+137.414 3.102 137.815 3.308 138.307 3.308 c
+139.189 3.308 139.634 2.723 139.644 1.559 c
+139.644 -1.749 l
+138.836 -1.749 l
+138.836 1.515 l
+138.836 1.904 138.77 2.18 138.645 2.338 c
+138.516 2.503 138.322 2.587 138.057 2.587 c
+137.851 2.587 137.668 2.517 137.513 2.382 c
+137.355 2.253 137.227 2.08 137.131 1.867 c
+137.131 -1.749 l
+136.307 -1.749 l
+136.307 3.219 l
+h
+144.547 4.425 m
+144.547 3.219 l
+145.296 3.219 l
+145.296 2.558 l
+144.547 2.558 l
+144.547 -0.514 l
+144.547 -0.712 144.577 -0.864 144.635 -0.97 c
+144.701 -1.069 144.815 -1.117 144.973 -1.117 c
+145.08 -1.117 145.186 -1.099 145.296 -1.058 c
+145.282 -1.749 l
+145.105 -1.807 144.918 -1.837 144.723 -1.837 c
+144.4 -1.837 144.153 -1.727 143.988 -1.499 c
+143.819 -1.263 143.738 -0.941 143.738 -0.529 c
+143.738 2.558 l
+142.974 2.558 l
+142.974 3.219 l
+143.738 3.219 l
+143.738 4.425 l
+h
+146.958 2.705 m
+147.259 3.105 147.652 3.308 148.134 3.308 c
+149.015 3.308 149.46 2.723 149.471 1.559 c
+149.471 -1.749 l
+148.662 -1.749 l
+148.662 1.515 l
+148.662 1.904 148.596 2.18 148.471 2.338 c
+148.342 2.503 148.148 2.587 147.883 2.587 c
+147.677 2.587 147.494 2.517 147.34 2.382 c
+147.181 2.253 147.053 2.08 146.958 1.867 c
+146.958 -1.749 l
+146.134 -1.749 l
+146.134 5.307 l
+146.958 5.307 l
+h
+152.271 -1.837 m
+151.654 -1.837 151.176 -1.657 150.846 -1.294 c
+150.522 -0.922 150.353 -0.378 150.345 0.339 c
+150.345 0.941 l
+150.345 1.676 150.507 2.253 150.831 2.675 c
+151.154 3.094 151.602 3.308 152.182 3.308 c
+152.76 3.308 153.19 3.12 153.476 2.749 c
+153.77 2.386 153.921 1.812 153.932 1.029 c
+153.932 0.5 l
+151.154 0.5 l
+151.154 0.383 l
+151.154 -0.158 151.249 -0.551 151.448 -0.793 c
+151.654 -1.028 151.941 -1.146 152.315 -1.146 c
+152.558 -1.146 152.77 -1.102 152.947 -1.014 c
+153.131 -0.926 153.304 -0.786 153.462 -0.588 c
+153.888 -1.102 l
+153.535 -1.595 152.995 -1.837 152.271 -1.837 c
+152.182 2.617 m
+151.849 2.617 151.598 2.5 151.434 2.264 c
+151.264 2.036 151.172 1.68 151.154 1.191 c
+153.109 1.191 l
+153.109 1.309 l
+153.087 1.779 153.006 2.11 152.859 2.309 c
+152.712 2.514 152.484 2.617 152.182 2.617 c
+157.354 0.956 m
+157.354 1.727 157.493 2.311 157.78 2.705 c
+158.062 3.105 158.474 3.308 159.014 3.308 c
+159.521 3.308 159.911 3.091 160.176 2.661 c
+160.205 3.219 l
+160.94 3.219 l
+160.94 -1.793 l
+160.94 -2.41 160.782 -2.881 160.469 -3.204 c
+160.153 -3.534 159.72 -3.704 159.161 -3.704 c
+158.915 -3.704 158.636 -3.638 158.324 -3.513 c
+158.019 -3.395 157.794 -3.248 157.647 -3.072 c
+157.971 -2.513 l
+158.312 -2.844 158.684 -3.013 159.087 -3.013 c
+159.764 -3.013 160.109 -2.645 160.132 -1.911 c
+160.132 -1.263 l
+159.866 -1.646 159.492 -1.837 159.014 -1.837 c
+158.492 -1.837 158.092 -1.646 157.809 -1.263 c
+157.522 -0.874 157.372 -0.316 157.354 0.412 c
+h
+158.177 0.5 m
+158.177 -0.058 158.254 -0.47 158.412 -0.735 c
+158.577 -0.992 158.845 -1.117 159.22 -1.117 c
+159.621 -1.117 159.926 -0.911 160.132 -0.5 c
+160.132 1.985 l
+159.926 2.386 159.625 2.587 159.235 2.587 c
+158.86 2.587 158.592 2.455 158.426 2.191 c
+158.257 1.933 158.177 1.536 158.177 1 c
+h
+164.019 2.455 m
+163.901 2.473 163.777 2.484 163.652 2.484 c
+163.23 2.484 162.939 2.257 162.785 1.808 c
+162.785 -1.749 l
+161.961 -1.749 l
+161.961 3.219 l
+162.755 3.219 l
+162.77 2.72 l
+162.983 3.109 163.292 3.308 163.696 3.308 c
+163.821 3.308 163.931 3.286 164.019 3.249 c
+h
+166.401 -1.837 m
+165.783 -1.837 165.306 -1.657 164.975 -1.294 c
+164.651 -0.922 164.483 -0.378 164.475 0.339 c
+164.475 0.941 l
+164.475 1.676 164.637 2.253 164.96 2.675 c
+165.283 3.094 165.732 3.308 166.312 3.308 c
+166.889 3.308 167.319 3.12 167.606 2.749 c
+167.899 2.386 168.051 1.812 168.061 1.029 c
+168.061 0.5 l
+165.283 0.5 l
+165.283 0.383 l
+165.283 -0.158 165.379 -0.551 165.578 -0.793 c
+165.783 -1.028 166.07 -1.146 166.445 -1.146 c
+166.687 -1.146 166.9 -1.102 167.076 -1.014 c
+167.261 -0.926 167.433 -0.786 167.591 -0.588 c
+168.017 -1.102 l
+167.664 -1.595 167.124 -1.837 166.401 -1.837 c
+166.312 2.617 m
+165.978 2.617 165.728 2.5 165.563 2.264 c
+165.393 2.036 165.302 1.68 165.283 1.191 c
+167.238 1.191 l
+167.238 1.309 l
+167.216 1.779 167.136 2.11 166.989 2.309 c
+166.841 2.514 166.613 2.617 166.312 2.617 c
+171.413 -1.749 m
+171.361 -1.643 171.328 -1.462 171.31 -1.205 c
+171.024 -1.627 170.656 -1.837 170.208 -1.837 c
+169.755 -1.837 169.403 -1.716 169.15 -1.469 c
+168.903 -1.216 168.782 -0.86 168.782 -0.396 c
+168.782 0.111 168.951 0.515 169.296 0.809 c
+169.638 1.103 170.108 1.253 170.708 1.264 c
+171.295 1.264 l
+171.295 1.794 l
+171.295 2.088 171.226 2.297 171.089 2.426 c
+170.961 2.55 170.766 2.617 170.501 2.617 c
+170.255 2.617 170.054 2.544 169.898 2.396 c
+169.752 2.249 169.678 2.062 169.678 1.838 c
+168.855 1.838 l
+168.855 2.091 168.929 2.338 169.075 2.573 c
+169.231 2.808 169.436 2.988 169.693 3.117 c
+169.946 3.242 170.23 3.308 170.546 3.308 c
+171.053 3.308 171.442 3.179 171.707 2.926 c
+171.972 2.679 172.104 2.311 172.104 1.823 c
+172.104 -0.675 l
+172.111 -1.058 172.167 -1.392 172.265 -1.675 c
+172.265 -1.749 l
+h
+170.34 -1.102 m
+170.534 -1.102 170.722 -1.051 170.898 -0.941 c
+171.082 -0.823 171.215 -0.687 171.295 -0.529 c
+171.295 0.662 l
+170.839 0.662 l
+170.457 0.651 170.152 0.563 169.929 0.397 c
+169.701 0.229 169.59 0 169.59 -0.294 c
+169.59 -0.58 169.642 -0.786 169.752 -0.911 c
+169.869 -1.04 170.064 -1.102 170.34 -1.102 c
+174.249 4.425 m
+174.249 3.219 l
+174.999 3.219 l
+174.999 2.558 l
+174.249 2.558 l
+174.249 -0.514 l
+174.249 -0.712 174.28 -0.864 174.338 -0.97 c
+174.405 -1.069 174.518 -1.117 174.677 -1.117 c
+174.783 -1.117 174.889 -1.099 174.999 -1.058 c
+174.985 -1.749 l
+174.808 -1.807 174.621 -1.837 174.426 -1.837 c
+174.103 -1.837 173.856 -1.727 173.692 -1.499 c
+173.522 -1.263 173.441 -0.941 173.441 -0.529 c
+173.441 2.558 l
+172.677 2.558 l
+172.677 3.219 l
+173.441 3.219 l
+173.441 4.425 l
+h
+f
+Q
+q 1 0 0 1 719.2545 317.0425 cm
+0 0 m
+0 -2.352 l
+-1.338 -2.352 l
+-1.338 4.336 l
+0.941 4.336 l
+1.595 4.336 2.12 4.131 2.514 3.719 c
+2.903 3.308 3.102 2.77 3.102 2.117 c
+3.102 1.47 2.903 0.956 2.514 0.573 c
+2.132 0.191 1.595 0 0.912 0 c
+h
+0 1.132 m
+0.941 1.132 l
+1.195 1.132 1.39 1.213 1.529 1.382 c
+1.675 1.548 1.749 1.793 1.749 2.117 c
+1.749 2.448 1.675 2.712 1.529 2.911 c
+1.382 3.105 1.191 3.204 0.956 3.204 c
+0 3.204 l
+h
+6.177 1.367 m
+5.751 1.396 l
+5.388 1.396 5.149 1.238 5.031 0.926 c
+5.031 -2.352 l
+3.723 -2.352 l
+3.723 2.616 l
+4.943 2.616 l
+4.987 2.087 l
+5.193 2.499 5.475 2.705 5.839 2.705 c
+5.986 2.705 6.104 2.683 6.192 2.646 c
+h
+6.416 0.294 m
+6.416 1.047 6.593 1.635 6.945 2.058 c
+7.298 2.488 7.786 2.705 8.415 2.705 c
+9.051 2.705 9.548 2.488 9.9 2.058 c
+10.26 1.635 10.444 1.043 10.444 0.279 c
+10.444 -0.029 l
+10.444 -0.786 10.267 -1.378 9.914 -1.808 c
+9.562 -2.23 9.066 -2.44 8.43 -2.44 c
+7.79 -2.44 7.298 -2.23 6.945 -1.808 c
+6.593 -1.378 6.416 -0.786 6.416 -0.029 c
+h
+7.724 -0.029 m
+7.724 -0.922 7.96 -1.367 8.43 -1.367 c
+8.871 -1.367 9.106 -0.996 9.135 -0.25 c
+9.151 0.294 l
+9.151 0.742 9.084 1.077 8.96 1.294 c
+8.831 1.517 8.65 1.631 8.415 1.631 c
+8.199 1.631 8.025 1.517 7.901 1.294 c
+7.784 1.077 7.724 0.742 7.724 0.294 c
+h
+18.485 -1.587 m
+18.219 -1.874 17.897 -2.088 17.514 -2.234 c
+17.139 -2.371 16.727 -2.44 16.28 -2.44 c
+15.515 -2.44 14.916 -2.205 14.486 -1.735 c
+14.063 -1.257 13.847 -0.559 13.839 0.353 c
+13.839 1.558 l
+13.839 2.477 14.038 3.183 14.442 3.674 c
+14.842 4.175 15.43 4.424 16.206 4.424 c
+16.93 4.424 17.473 4.241 17.837 3.881 c
+18.209 3.528 18.425 2.969 18.485 2.205 c
+17.176 2.205 l
+17.136 2.624 17.043 2.914 16.897 3.072 c
+16.757 3.227 16.536 3.308 16.235 3.308 c
+15.871 3.308 15.607 3.175 15.441 2.911 c
+15.283 2.646 15.199 2.219 15.192 1.631 c
+15.192 0.426 l
+15.192 -0.183 15.279 -0.628 15.456 -0.912 c
+15.64 -1.187 15.945 -1.323 16.368 -1.323 c
+16.632 -1.323 16.845 -1.272 17.014 -1.161 c
+17.132 -1.073 l
+17.132 0.147 l
+16.176 0.147 l
+16.176 1.161 l
+18.485 1.161 l
+h
+20.527 -2.352 -1.308 4.968 re
+19.16 3.91 m
+19.16 4.104 19.223 4.266 19.351 4.395 c
+19.476 4.531 19.649 4.601 19.866 4.601 c
+20.079 4.601 20.251 4.531 20.38 4.395 c
+20.505 4.266 20.571 4.104 20.571 3.91 c
+20.571 3.705 20.505 3.532 20.38 3.395 c
+20.263 3.267 20.09 3.204 19.866 3.204 c
+19.649 3.204 19.476 3.267 19.351 3.395 c
+19.223 3.532 19.16 3.705 19.16 3.91 c
+22.949 3.836 m
+22.949 2.616 l
+23.611 2.616 l
+23.611 1.646 l
+22.949 1.646 l
+22.949 -0.823 l
+22.949 -1.022 22.971 -1.157 23.023 -1.234 c
+23.081 -1.305 23.188 -1.338 23.345 -1.338 c
+23.464 -1.338 23.566 -1.33 23.655 -1.309 c
+23.64 -2.323 l
+23.412 -2.4 23.173 -2.44 22.919 -2.44 c
+22.086 -2.44 21.662 -1.962 21.656 -0.999 c
+21.656 1.646 l
+21.082 1.646 l
+21.082 2.616 l
+21.656 2.616 l
+21.656 3.836 l
+h
+f
+Q
+q 1 0 0 1 749.9827 316.94 cm
+0 0 m
+0 -0.776 -0.143 -1.36 -0.426 -1.75 c
+-0.702 -2.143 -1.103 -2.338 -1.631 -2.338 c
+-2.153 -2.338 -2.543 -2.113 -2.807 -1.661 c
+-2.851 -2.249 l
+-3.586 -2.249 l
+-3.586 4.806 l
+-2.778 4.806 l
+-2.778 2.175 l
+-2.514 2.594 -2.131 2.807 -1.631 2.807 c
+-1.095 2.807 -0.687 2.616 -0.411 2.234 c
+-0.139 1.851 0 1.267 0 0.484 c
+h
+-0.808 0.455 m
+-0.808 1.043 -0.893 1.458 -1.058 1.705 c
+-1.216 1.959 -1.481 2.087 -1.852 2.087 c
+-2.263 2.087 -2.572 1.859 -2.778 1.411 c
+-2.778 -0.956 l
+-2.583 -1.397 -2.271 -1.617 -1.837 -1.617 c
+-1.477 -1.617 -1.216 -1.492 -1.058 -1.235 c
+-0.893 -0.981 -0.808 -0.584 -0.808 -0.044 c
+h
+0.698 0.47 m
+0.698 1.182 0.867 1.753 1.213 2.175 c
+1.565 2.594 2.028 2.807 2.61 2.807 c
+3.186 2.807 3.645 2.601 3.991 2.19 c
+4.343 1.786 4.524 1.227 4.534 0.515 c
+4.534 0 l
+4.534 -0.728 4.359 -1.301 4.006 -1.72 c
+3.66 -2.132 3.2 -2.338 2.624 -2.338 c
+2.043 -2.338 1.581 -2.139 1.228 -1.735 c
+0.883 -1.324 0.706 -0.772 0.698 -0.073 c
+h
+1.507 0 m
+1.507 -0.511 1.602 -0.912 1.801 -1.206 c
+2.007 -1.5 2.279 -1.646 2.624 -1.646 c
+3.337 -1.646 3.705 -1.129 3.726 -0.088 c
+3.726 0.47 l
+3.726 0.977 3.624 1.381 3.418 1.675 c
+3.219 1.969 2.951 2.117 2.61 2.117 c
+2.275 2.117 2.007 1.969 1.801 1.675 c
+1.602 1.381 1.507 0.977 1.507 0.47 c
+h
+5.233 0.47 m
+5.233 1.182 5.402 1.753 5.748 2.175 c
+6.101 2.594 6.564 2.807 7.144 2.807 c
+7.721 2.807 8.18 2.601 8.526 2.19 c
+8.879 1.786 9.058 1.227 9.07 0.515 c
+9.07 0 l
+9.07 -0.728 8.893 -1.301 8.54 -1.72 c
+8.195 -2.132 7.736 -2.338 7.159 -2.338 c
+6.578 -2.338 6.115 -2.139 5.762 -1.735 c
+5.417 -1.324 5.24 -0.772 5.233 -0.073 c
+h
+6.041 0 m
+6.041 -0.511 6.137 -0.912 6.336 -1.206 c
+6.541 -1.5 6.813 -1.646 7.159 -1.646 c
+7.871 -1.646 8.239 -1.129 8.261 -0.088 c
+8.261 0.47 l
+8.261 0.977 8.158 1.381 7.952 1.675 c
+7.754 1.969 7.486 2.117 7.144 2.117 c
+6.809 2.117 6.541 1.969 6.336 1.675 c
+6.137 1.381 6.041 0.977 6.041 0.47 c
+h
+11.175 0.029 m
+10.749 -0.47 l
+10.749 -2.249 l
+9.941 -2.249 l
+9.941 4.806 l
+10.749 4.806 l
+10.749 0.588 l
+12.293 2.719 l
+13.263 2.719 l
+11.69 0.646 l
+13.483 -2.249 l
+12.528 -2.249 l
+h
+20.391 0 m
+20.391 -0.776 20.249 -1.36 19.965 -1.75 c
+19.69 -2.143 19.289 -2.338 18.76 -2.338 c
+18.238 -2.338 17.849 -2.113 17.584 -1.661 c
+17.54 -2.249 l
+16.805 -2.249 l
+16.805 4.806 l
+17.614 4.806 l
+17.614 2.175 l
+17.878 2.594 18.26 2.807 18.76 2.807 c
+19.297 2.807 19.704 2.616 19.979 2.234 c
+20.252 1.851 20.391 1.267 20.391 0.484 c
+h
+19.583 0.455 m
+19.583 1.043 19.499 1.458 19.333 1.705 c
+19.175 1.959 18.911 2.087 18.539 2.087 c
+18.128 2.087 17.82 1.859 17.614 1.411 c
+17.614 -0.956 l
+17.808 -1.397 18.121 -1.617 18.554 -1.617 c
+18.915 -1.617 19.175 -1.492 19.333 -1.235 c
+19.499 -0.981 19.583 -0.584 19.583 -0.044 c
+h
+22.567 -0.897 m
+23.464 2.719 l
+24.33 2.719 l
+22.699 -2.955 l
+22.582 -3.374 22.408 -3.697 22.185 -3.925 c
+21.957 -4.15 21.703 -4.263 21.421 -4.263 c
+21.31 -4.263 21.174 -4.237 21.009 -4.189 c
+21.009 -3.499 l
+21.185 -3.514 l
+21.421 -3.514 21.6 -3.454 21.729 -3.337 c
+21.865 -3.227 21.972 -3.036 22.053 -2.764 c
+22.214 -2.205 l
+20.759 2.719 l
+21.641 2.719 l
+h
+f
+Q
+q 1 0 0 1 539.1547 305.0923 cm
+0 0 m
+0 0.33 -0.096 0.588 -0.279 0.764 c
+-0.456 0.941 -0.779 1.11 -1.249 1.278 c
+-1.72 1.455 -2.084 1.635 -2.338 1.822 c
+-2.583 2.007 -2.771 2.219 -2.896 2.454 c
+-3.025 2.69 -3.087 2.958 -3.087 3.263 c
+-3.087 3.792 -2.911 4.226 -2.558 4.571 c
+-2.198 4.913 -1.731 5.086 -1.162 5.086 c
+-0.772 5.086 -0.422 4.997 -0.118 4.821 c
+0.195 4.644 0.43 4.399 0.588 4.087 c
+0.753 3.781 0.837 3.439 0.837 3.057 c
+0 3.057 l
+0 3.476 -0.104 3.8 -0.309 4.027 c
+-0.507 4.251 -0.79 4.366 -1.162 4.366 c
+-1.507 4.366 -1.771 4.266 -1.955 4.072 c
+-2.143 3.884 -2.234 3.62 -2.234 3.278 c
+-2.234 2.992 -2.132 2.756 -1.926 2.572 c
+-1.72 2.385 -1.407 2.212 -0.985 2.057 c
+-0.331 1.841 0.139 1.573 0.426 1.249 c
+0.709 0.933 0.852 0.522 0.852 0.015 c
+0.852 -0.536 0.676 -0.97 0.324 -1.294 c
+-0.029 -1.617 -0.511 -1.779 -1.118 -1.779 c
+-1.511 -1.779 -1.874 -1.694 -2.205 -1.529 c
+-2.529 -1.363 -2.786 -1.128 -2.969 -0.823 c
+-3.157 -0.511 -3.248 -0.154 -3.248 0.25 c
+-2.411 0.25 l
+-2.411 -0.162 -2.301 -0.485 -2.072 -0.721 c
+-1.837 -0.948 -1.521 -1.058 -1.118 -1.058 c
+-0.746 -1.058 -0.467 -0.966 -0.279 -0.779 c
+-0.096 -0.584 0 -0.324 0 0 c
+3.366 -1.087 m
+3.63 -1.087 3.84 -1.007 3.998 -0.838 c
+4.164 -0.673 4.255 -0.434 4.278 -0.118 c
+5.041 -0.118 l
+5.02 -0.599 4.85 -0.996 4.527 -1.309 c
+4.211 -1.625 3.825 -1.779 3.366 -1.779 c
+2.749 -1.779 2.278 -1.588 1.955 -1.205 c
+1.631 -0.816 1.469 -0.235 1.469 0.529 c
+1.469 1.073 l
+1.469 1.826 1.631 2.396 1.955 2.778 c
+2.278 3.167 2.741 3.366 3.351 3.366 c
+3.858 3.366 4.262 3.204 4.557 2.881 c
+4.858 2.564 5.02 2.132 5.041 1.573 c
+4.278 1.573 l
+4.255 1.933 4.166 2.209 4.012 2.396 c
+3.854 2.58 3.634 2.675 3.351 2.675 c
+2.998 2.675 2.734 2.55 2.558 2.308 c
+2.381 2.072 2.285 1.683 2.278 1.147 c
+2.278 0.515 l
+2.278 -0.073 2.366 -0.493 2.543 -0.735 c
+2.719 -0.97 2.991 -1.087 3.366 -1.087 c
+5.648 1.029 m
+5.648 1.741 5.817 2.311 6.163 2.734 c
+6.516 3.152 6.978 3.366 7.559 3.366 c
+8.135 3.366 8.595 3.16 8.941 2.749 c
+9.294 2.344 9.473 1.786 9.484 1.073 c
+9.484 0.559 l
+9.484 -0.169 9.308 -0.742 8.955 -1.161 c
+8.61 -1.573 8.151 -1.779 7.574 -1.779 c
+6.992 -1.779 6.53 -1.58 6.177 -1.176 c
+5.832 -0.765 5.655 -0.214 5.648 0.485 c
+h
+6.456 0.559 m
+6.456 0.048 6.552 -0.353 6.751 -0.647 c
+6.956 -0.941 7.228 -1.087 7.574 -1.087 c
+8.286 -1.087 8.654 -0.57 8.675 0.47 c
+8.675 1.029 l
+8.675 1.536 8.573 1.94 8.367 2.234 c
+8.168 2.528 7.9 2.675 7.559 2.675 c
+7.224 2.675 6.956 2.528 6.751 2.234 c
+6.552 1.94 6.456 1.536 6.456 1.029 c
+h
+11.418 4.484 m
+11.418 3.278 l
+12.167 3.278 l
+12.167 2.616 l
+11.418 2.616 l
+11.418 -0.455 l
+11.418 -0.654 11.447 -0.805 11.505 -0.912 c
+11.572 -1.01 11.686 -1.058 11.844 -1.058 c
+11.95 -1.058 12.056 -1.04 12.167 -1 c
+12.152 -1.69 l
+11.976 -1.749 11.788 -1.779 11.594 -1.779 c
+11.27 -1.779 11.024 -1.669 10.859 -1.44 c
+10.69 -1.205 10.609 -0.882 10.609 -0.47 c
+10.609 2.616 l
+9.845 2.616 l
+9.845 3.278 l
+10.609 3.278 l
+10.609 4.484 l
+h
+14.082 4.484 m
+14.082 3.278 l
+14.831 3.278 l
+14.831 2.616 l
+14.082 2.616 l
+14.082 -0.455 l
+14.082 -0.654 14.111 -0.805 14.169 -0.912 c
+14.236 -1.01 14.35 -1.058 14.508 -1.058 c
+14.614 -1.058 14.721 -1.04 14.831 -1 c
+14.817 -1.69 l
+14.64 -1.749 14.453 -1.779 14.258 -1.779 c
+13.934 -1.779 13.689 -1.669 13.523 -1.44 c
+13.354 -1.205 13.273 -0.882 13.273 -0.47 c
+13.273 2.616 l
+12.509 2.616 l
+12.509 3.278 l
+13.273 3.278 l
+13.273 4.484 l
+h
+22.416 0.426 m
+22.387 -0.291 22.184 -0.838 21.813 -1.22 c
+21.45 -1.595 20.935 -1.779 20.27 -1.779 c
+19.594 -1.779 19.057 -1.525 18.668 -1.014 c
+18.275 -0.507 18.08 0.183 18.08 1.058 c
+18.08 2.263 l
+18.08 3.135 18.278 3.821 18.682 4.322 c
+19.094 4.829 19.645 5.086 20.343 5.086 c
+20.99 5.086 21.49 4.887 21.843 4.498 c
+22.196 4.116 22.387 3.564 22.416 2.851 c
+21.578 2.851 l
+21.537 3.388 21.42 3.778 21.225 4.013 c
+21.026 4.248 20.733 4.366 20.343 4.366 c
+19.891 4.366 19.542 4.182 19.299 3.821 c
+19.054 3.469 18.932 2.947 18.932 2.263 c
+18.932 1.029 l
+18.932 0.353 19.042 -0.169 19.27 -0.53 c
+19.494 -0.882 19.829 -1.058 20.27 -1.058 c
+20.71 -1.058 21.023 -0.952 21.211 -0.735 c
+21.406 -0.522 21.527 -0.133 21.578 0.426 c
+h
+24.047 2.763 m
+24.349 3.164 24.742 3.366 25.223 3.366 c
+26.105 3.366 26.549 2.782 26.561 1.617 c
+26.561 -1.69 l
+25.753 -1.69 l
+25.753 1.573 l
+25.753 1.962 25.686 2.238 25.562 2.396 c
+25.433 2.562 25.238 2.645 24.974 2.645 c
+24.768 2.645 24.584 2.576 24.43 2.44 c
+24.272 2.311 24.143 2.138 24.047 1.926 c
+24.047 -1.69 l
+23.224 -1.69 l
+23.224 5.365 l
+24.047 5.365 l
+h
+30.14 -1.69 m
+30.089 -1.584 30.056 -1.404 30.037 -1.147 c
+29.751 -1.569 29.383 -1.779 28.935 -1.779 c
+28.483 -1.779 28.13 -1.658 27.877 -1.411 c
+27.63 -1.158 27.509 -0.802 27.509 -0.338 c
+27.509 0.169 27.678 0.573 28.024 0.867 c
+28.365 1.161 28.836 1.311 29.435 1.323 c
+30.023 1.323 l
+30.023 1.852 l
+30.023 2.146 29.953 2.356 29.817 2.484 c
+29.688 2.609 29.493 2.675 29.229 2.675 c
+28.982 2.675 28.781 2.602 28.626 2.454 c
+28.479 2.308 28.406 2.12 28.406 1.897 c
+27.582 1.897 l
+27.582 2.15 27.656 2.396 27.803 2.631 c
+27.958 2.866 28.163 3.046 28.421 3.175 c
+28.674 3.3 28.957 3.366 29.273 3.366 c
+29.78 3.366 30.169 3.237 30.434 2.984 c
+30.699 2.738 30.831 2.37 30.831 1.882 c
+30.831 -0.617 l
+30.838 -1 30.894 -1.334 30.993 -1.617 c
+30.993 -1.69 l
+h
+29.067 -1.043 m
+29.262 -1.043 29.449 -0.992 29.626 -0.882 c
+29.809 -0.765 29.942 -0.628 30.023 -0.47 c
+30.023 0.721 l
+29.566 0.721 l
+29.185 0.709 28.88 0.621 28.656 0.455 c
+28.428 0.287 28.317 0.058 28.317 -0.235 c
+28.317 -0.522 28.369 -0.728 28.479 -0.852 c
+28.596 -0.981 28.791 -1.043 29.067 -1.043 c
+33.639 -1.087 m
+33.903 -1.087 34.112 -1.007 34.27 -0.838 c
+34.436 -0.673 34.528 -0.434 34.549 -0.118 c
+35.314 -0.118 l
+35.292 -0.599 35.123 -0.996 34.8 -1.309 c
+34.484 -1.625 34.098 -1.779 33.639 -1.779 c
+33.021 -1.779 32.55 -1.588 32.228 -1.205 c
+31.904 -0.816 31.742 -0.235 31.742 0.529 c
+31.742 1.073 l
+31.742 1.826 31.904 2.396 32.228 2.778 c
+32.55 3.167 33.014 3.366 33.624 3.366 c
+34.131 3.366 34.535 3.204 34.829 2.881 c
+35.131 2.564 35.292 2.132 35.314 1.573 c
+34.549 1.573 l
+34.528 1.933 34.439 2.209 34.285 2.396 c
+34.127 2.58 33.907 2.675 33.624 2.675 c
+33.271 2.675 33.007 2.55 32.83 2.308 c
+32.654 2.072 32.558 1.683 32.55 1.147 c
+32.55 0.515 l
+32.55 -0.073 32.639 -0.493 32.816 -0.735 c
+32.992 -0.97 33.264 -1.087 33.639 -1.087 c
+35.924 1.029 m
+35.924 1.741 36.093 2.311 36.439 2.734 c
+36.791 3.152 37.254 3.366 37.836 3.366 c
+38.412 3.366 38.871 3.16 39.217 2.749 c
+39.569 2.344 39.75 1.786 39.76 1.073 c
+39.76 0.559 l
+39.76 -0.169 39.584 -0.742 39.232 -1.161 c
+38.886 -1.573 38.426 -1.779 37.85 -1.779 c
+37.269 -1.779 36.806 -1.58 36.454 -1.176 c
+36.108 -0.765 35.931 -0.214 35.924 0.485 c
+h
+36.733 0.559 m
+36.733 0.048 36.828 -0.353 37.026 -0.647 c
+37.233 -0.941 37.504 -1.087 37.85 -1.087 c
+38.563 -1.087 38.931 -0.57 38.952 0.47 c
+38.952 1.029 l
+38.952 1.536 38.85 1.94 38.644 2.234 c
+38.445 2.528 38.177 2.675 37.836 2.675 c
+37.501 2.675 37.233 2.528 37.026 2.234 c
+36.828 1.94 36.733 1.536 36.733 1.029 c
+h
+41.381 3.278 m
+41.41 2.749 l
+41.723 3.16 42.123 3.366 42.616 3.366 c
+43.498 3.366 43.943 2.782 43.953 1.617 c
+43.953 -1.69 l
+43.145 -1.69 l
+43.145 1.573 l
+43.145 1.962 43.079 2.238 42.954 2.396 c
+42.825 2.562 42.63 2.645 42.366 2.645 c
+42.16 2.645 41.977 2.576 41.822 2.44 c
+41.664 2.311 41.535 2.138 41.44 1.926 c
+41.44 -1.69 l
+40.617 -1.69 l
+40.617 3.278 l
+h
+49.988 -1.69 m
+49.936 -1.584 49.903 -1.404 49.885 -1.147 c
+49.598 -1.569 49.231 -1.779 48.782 -1.779 c
+48.33 -1.779 47.978 -1.658 47.724 -1.411 c
+47.478 -1.158 47.357 -0.802 47.357 -0.338 c
+47.357 0.169 47.525 0.573 47.871 0.867 c
+48.213 1.161 48.683 1.311 49.282 1.323 c
+49.87 1.323 l
+49.87 1.852 l
+49.87 2.146 49.8 2.356 49.664 2.484 c
+49.536 2.609 49.341 2.675 49.076 2.675 c
+48.83 2.675 48.628 2.602 48.473 2.454 c
+48.327 2.308 48.253 2.12 48.253 1.897 c
+47.43 1.897 l
+47.43 2.15 47.504 2.396 47.65 2.631 c
+47.805 2.866 48.011 3.046 48.267 3.175 c
+48.521 3.3 48.805 3.366 49.121 3.366 c
+49.628 3.366 50.017 3.237 50.282 2.984 c
+50.546 2.738 50.679 2.37 50.679 1.882 c
+50.679 -0.617 l
+50.686 -1 50.741 -1.334 50.84 -1.617 c
+50.84 -1.69 l
+h
+48.915 -1.043 m
+49.109 -1.043 49.297 -0.992 49.473 -0.882 c
+49.657 -0.765 49.789 -0.628 49.87 -0.47 c
+49.87 0.721 l
+49.414 0.721 l
+49.032 0.709 48.727 0.621 48.503 0.455 c
+48.275 0.287 48.165 0.058 48.165 -0.235 c
+48.165 -0.522 48.217 -0.728 48.327 -0.852 c
+48.444 -0.981 48.639 -1.043 48.915 -1.043 c
+52.516 3.278 m
+52.545 2.749 l
+52.857 3.16 53.258 3.366 53.751 3.366 c
+54.632 3.366 55.077 2.782 55.088 1.617 c
+55.088 -1.69 l
+54.28 -1.69 l
+54.28 1.573 l
+54.28 1.962 54.214 2.238 54.089 2.396 c
+53.96 2.562 53.765 2.645 53.501 2.645 c
+53.295 2.645 53.111 2.576 52.957 2.44 c
+52.799 2.311 52.67 2.138 52.575 1.926 c
+52.575 -1.69 l
+51.751 -1.69 l
+51.751 3.278 l
+h
+55.966 1.014 m
+55.966 1.786 56.106 2.37 56.393 2.763 c
+56.676 3.164 57.087 3.366 57.628 3.366 c
+58.098 3.366 58.465 3.175 58.729 2.793 c
+58.729 5.365 l
+59.552 5.365 l
+59.552 -1.69 l
+58.804 -1.69 l
+58.76 -1.161 l
+58.494 -1.573 58.116 -1.779 57.628 -1.779 c
+57.106 -1.779 56.701 -1.584 56.408 -1.191 c
+56.113 -0.79 55.966 -0.22 55.966 0.515 c
+h
+56.774 0.559 m
+56.774 0 56.855 -0.412 57.025 -0.676 c
+57.19 -0.933 57.458 -1.058 57.833 -1.058 c
+58.234 -1.058 58.532 -0.86 58.729 -0.455 c
+58.729 2.057 l
+58.524 2.447 58.222 2.645 57.833 2.645 c
+57.458 2.645 57.19 2.514 57.025 2.249 c
+56.855 1.992 56.774 1.595 56.774 1.058 c
+h
+63.165 -1.69 m
+63.165 4.997 l
+65.077 4.997 l
+65.682 4.997 66.138 4.843 66.443 4.542 c
+66.756 4.248 66.914 3.807 66.914 3.219 c
+66.914 2.903 66.833 2.624 66.679 2.381 c
+66.521 2.146 66.311 1.962 66.046 1.837 c
+66.347 1.738 66.59 1.547 66.766 1.264 c
+66.951 0.977 67.046 0.636 67.046 0.235 c
+67.046 -0.364 66.877 -0.834 66.546 -1.176 c
+66.212 -1.521 65.742 -1.69 65.135 -1.69 c
+h
+64.003 1.44 m
+64.003 -0.97 l
+65.15 -0.97 l
+65.473 -0.97 65.727 -0.867 65.914 -0.661 c
+66.097 -0.449 66.193 -0.147 66.193 0.235 c
+66.193 1.036 65.855 1.44 65.179 1.44 c
+h
+64.003 2.146 m
+65.091 2.146 l
+65.374 2.146 65.605 2.242 65.782 2.44 c
+65.966 2.635 66.061 2.896 66.061 3.219 c
+66.061 3.59 65.977 3.858 65.811 4.027 c
+65.653 4.193 65.407 4.278 65.077 4.278 c
+64.003 4.278 l
+h
+69.746 -1.779 m
+69.129 -1.779 68.651 -1.598 68.321 -1.235 c
+67.997 -0.864 67.828 -0.32 67.822 0.397 c
+67.822 0.999 l
+67.822 1.735 67.982 2.311 68.306 2.734 c
+68.63 3.152 69.077 3.366 69.659 3.366 c
+70.236 3.366 70.665 3.179 70.952 2.807 c
+71.246 2.444 71.396 1.87 71.408 1.087 c
+71.408 0.559 l
+68.63 0.559 l
+68.63 0.441 l
+68.63 -0.1 68.725 -0.493 68.923 -0.735 c
+69.129 -0.97 69.416 -1.087 69.791 -1.087 c
+70.033 -1.087 70.247 -1.043 70.423 -0.956 c
+70.606 -0.867 70.779 -0.728 70.937 -0.53 c
+71.363 -1.043 l
+71.011 -1.536 70.471 -1.779 69.746 -1.779 c
+69.659 2.675 m
+69.324 2.675 69.075 2.558 68.909 2.323 c
+68.74 2.094 68.648 1.738 68.63 1.249 c
+70.585 1.249 l
+70.585 1.367 l
+70.563 1.837 70.482 2.168 70.334 2.367 c
+70.188 2.572 69.96 2.675 69.659 2.675 c
+72.966 3.278 m
+72.996 2.749 l
+73.308 3.16 73.708 3.366 74.201 3.366 c
+75.082 3.366 75.527 2.782 75.539 1.617 c
+75.539 -1.69 l
+74.729 -1.69 l
+74.729 1.573 l
+74.729 1.962 74.664 2.238 74.538 2.396 c
+74.41 2.562 74.215 2.645 73.951 2.645 c
+73.745 2.645 73.561 2.576 73.407 2.44 c
+73.249 2.311 73.12 2.138 73.025 1.926 c
+73.025 -1.69 l
+72.202 -1.69 l
+72.202 3.278 l
+h
+82.057 0 m
+82.057 0.33 81.962 0.588 81.777 0.764 c
+81.602 0.941 81.278 1.11 80.808 1.278 c
+80.337 1.455 79.973 1.635 79.72 1.822 c
+79.474 2.007 79.286 2.219 79.161 2.454 c
+79.032 2.69 78.97 2.958 78.97 3.263 c
+78.97 3.792 79.147 4.226 79.5 4.571 c
+79.859 4.913 80.327 5.086 80.896 5.086 c
+81.285 5.086 81.635 4.997 81.939 4.821 c
+82.252 4.644 82.487 4.399 82.645 4.087 c
+82.81 3.781 82.895 3.439 82.895 3.057 c
+82.057 3.057 l
+82.057 3.476 81.954 3.8 81.748 4.027 c
+81.55 4.251 81.268 4.366 80.896 4.366 c
+80.551 4.366 80.286 4.266 80.102 4.072 c
+79.915 3.884 79.823 3.62 79.823 3.278 c
+79.823 2.992 79.926 2.756 80.131 2.572 c
+80.337 2.385 80.65 2.212 81.072 2.057 c
+81.727 1.841 82.197 1.573 82.483 1.249 c
+82.766 0.933 82.91 0.522 82.91 0.015 c
+82.91 -0.536 82.733 -0.97 82.38 -1.294 c
+82.028 -1.617 81.546 -1.779 80.94 -1.779 c
+80.547 -1.779 80.183 -1.694 79.853 -1.529 c
+79.529 -1.363 79.271 -1.128 79.088 -0.823 c
+78.901 -0.511 78.808 -0.154 78.808 0.25 c
+79.647 0.25 l
+79.647 -0.162 79.757 -0.485 79.984 -0.721 c
+80.22 -0.948 80.536 -1.058 80.94 -1.058 c
+81.311 -1.058 81.59 -0.966 81.777 -0.779 c
+81.962 -0.584 82.057 -0.324 82.057 0 c
+84.761 4.484 m
+84.761 3.278 l
+85.511 3.278 l
+85.511 2.616 l
+84.761 2.616 l
+84.761 -0.455 l
+84.761 -0.654 84.792 -0.805 84.85 -0.912 c
+84.916 -1.01 85.031 -1.058 85.188 -1.058 c
+85.295 -1.058 85.401 -1.04 85.511 -1 c
+85.497 -1.69 l
+85.32 -1.749 85.133 -1.779 84.938 -1.779 c
+84.615 -1.779 84.368 -1.669 84.204 -1.44 c
+84.034 -1.205 83.953 -0.882 83.953 -0.47 c
+83.953 2.616 l
+83.189 2.616 l
+83.189 3.278 l
+83.953 3.278 l
+83.953 4.484 l
+h
+88.411 2.514 m
+88.293 2.532 88.168 2.543 88.044 2.543 c
+87.62 2.543 87.331 2.315 87.176 1.866 c
+87.176 -1.69 l
+86.353 -1.69 l
+86.353 3.278 l
+87.147 3.278 l
+87.161 2.778 l
+87.375 3.167 87.683 3.366 88.088 3.366 c
+88.212 3.366 88.323 3.344 88.411 3.308 c
+h
+91.483 -1.69 m
+91.431 -1.584 91.398 -1.404 91.381 -1.147 c
+91.094 -1.569 90.726 -1.779 90.278 -1.779 c
+89.826 -1.779 89.473 -1.658 89.22 -1.411 c
+88.973 -1.158 88.852 -0.802 88.852 -0.338 c
+88.852 0.169 89.021 0.573 89.366 0.867 c
+89.708 1.161 90.178 1.311 90.778 1.323 c
+91.365 1.323 l
+91.365 1.852 l
+91.365 2.146 91.296 2.356 91.159 2.484 c
+91.031 2.609 90.836 2.675 90.572 2.675 c
+90.325 2.675 90.124 2.602 89.969 2.454 c
+89.822 2.308 89.748 2.12 89.748 1.897 c
+88.925 1.897 l
+88.925 2.15 88.999 2.396 89.145 2.631 c
+89.301 2.866 89.506 3.046 89.763 3.175 c
+90.016 3.3 90.3 3.366 90.616 3.366 c
+91.123 3.366 91.512 3.237 91.777 2.984 c
+92.042 2.738 92.174 2.37 92.174 1.882 c
+92.174 -0.617 l
+92.181 -1 92.237 -1.334 92.335 -1.617 c
+92.335 -1.69 l
+h
+90.41 -1.043 m
+90.604 -1.043 90.792 -0.992 90.969 -0.882 c
+91.152 -0.765 91.285 -0.628 91.365 -0.47 c
+91.365 0.721 l
+90.91 0.721 l
+90.527 0.709 90.222 0.621 89.999 0.455 c
+89.771 0.287 89.66 0.058 89.66 -0.235 c
+89.66 -0.522 89.712 -0.728 89.822 -0.852 c
+89.939 -0.981 90.134 -1.043 90.41 -1.043 c
+95.79 -1.249 m
+95.514 -1.602 95.117 -1.779 94.599 -1.779 c
+94.159 -1.779 93.82 -1.628 93.585 -1.323 c
+93.357 -1.01 93.239 -0.555 93.232 0.044 c
+93.232 3.278 l
+94.041 3.278 l
+94.041 0.103 l
+94.041 -0.673 94.276 -1.058 94.747 -1.058 c
+95.235 -1.058 95.573 -0.838 95.761 -0.397 c
+95.761 3.278 l
+96.584 3.278 l
+96.584 -1.69 l
+95.805 -1.69 l
+h
+101.224 0.559 m
+101.224 -0.217 101.081 -0.802 100.798 -1.191 c
+100.523 -1.584 100.122 -1.779 99.593 -1.779 c
+99.071 -1.779 98.682 -1.554 98.417 -1.103 c
+98.373 -1.69 l
+97.638 -1.69 l
+97.638 5.365 l
+98.446 5.365 l
+98.446 2.734 l
+98.711 3.152 99.094 3.366 99.593 3.366 c
+100.129 3.366 100.538 3.175 100.813 2.793 c
+101.085 2.41 101.224 1.826 101.224 1.043 c
+h
+100.416 1.014 m
+100.416 1.602 100.332 2.017 100.166 2.263 c
+100.008 2.517 99.744 2.645 99.373 2.645 c
+98.961 2.645 98.652 2.418 98.446 1.97 c
+98.446 -0.397 l
+98.641 -0.838 98.953 -1.058 99.387 -1.058 c
+99.747 -1.058 100.008 -0.933 100.166 -0.676 c
+100.332 -0.422 100.416 -0.025 100.416 0.515 c
+h
+102.144 -1.249 m
+102.144 -1.103 102.188 -0.981 102.276 -0.882 c
+102.364 -0.786 102.489 -0.735 102.658 -0.735 c
+102.834 -0.735 102.967 -0.786 103.055 -0.882 c
+103.15 -0.981 103.202 -1.103 103.202 -1.249 c
+103.202 -1.389 103.15 -1.507 103.055 -1.602 c
+102.967 -1.69 102.834 -1.735 102.658 -1.735 c
+102.489 -1.735 102.364 -1.69 102.276 -1.602 c
+102.188 -1.507 102.144 -1.389 102.144 -1.249 c
+110.757 4.278 m
+108.993 4.278 l
+108.993 -1.69 l
+108.156 -1.69 l
+108.156 4.278 l
+106.406 4.278 l
+106.406 4.997 l
+110.757 4.997 l
+h
+112.264 2.763 m
+112.565 3.164 112.958 3.366 113.44 3.366 c
+114.321 3.366 114.766 2.782 114.778 1.617 c
+114.778 -1.69 l
+113.969 -1.69 l
+113.969 1.573 l
+113.969 1.962 113.903 2.238 113.778 2.396 c
+113.649 2.562 113.454 2.645 113.19 2.645 c
+112.984 2.645 112.8 2.576 112.646 2.44 c
+112.488 2.311 112.359 2.138 112.264 1.926 c
+112.264 -1.69 l
+111.441 -1.69 l
+111.441 5.365 l
+112.264 5.365 l
+h
+117.574 -1.779 m
+116.956 -1.779 116.479 -1.598 116.148 -1.235 c
+115.825 -0.864 115.655 -0.32 115.648 0.397 c
+115.648 0.999 l
+115.648 1.735 115.81 2.311 116.133 2.734 c
+116.457 3.152 116.905 3.366 117.486 3.366 c
+118.063 3.366 118.493 3.179 118.779 2.807 c
+119.073 2.444 119.224 1.87 119.235 1.087 c
+119.235 0.559 l
+116.457 0.559 l
+116.457 0.441 l
+116.457 -0.1 116.552 -0.493 116.75 -0.735 c
+116.956 -0.97 117.243 -1.087 117.618 -1.087 c
+117.86 -1.087 118.073 -1.043 118.25 -0.956 c
+118.433 -0.867 118.607 -0.728 118.765 -0.53 c
+119.191 -1.043 l
+118.838 -1.536 118.298 -1.779 117.574 -1.779 c
+117.486 2.675 m
+117.151 2.675 116.901 2.558 116.736 2.323 c
+116.567 2.094 116.475 1.738 116.457 1.249 c
+118.412 1.249 l
+118.412 1.367 l
+118.389 1.837 118.308 2.168 118.161 2.367 c
+118.015 2.572 117.787 2.675 117.486 2.675 c
+126.069 0.559 m
+126.069 -0.217 125.926 -0.802 125.643 -1.191 c
+125.368 -1.584 124.968 -1.779 124.438 -1.779 c
+123.916 -1.779 123.527 -1.554 123.262 -1.103 c
+123.218 -1.69 l
+122.483 -1.69 l
+122.483 5.365 l
+123.291 5.365 l
+123.291 2.734 l
+123.557 3.152 123.939 3.366 124.438 3.366 c
+124.974 3.366 125.383 3.175 125.658 2.793 c
+125.93 2.41 126.069 1.826 126.069 1.043 c
+h
+125.261 1.014 m
+125.261 1.602 125.177 2.017 125.012 2.263 c
+124.854 2.517 124.589 2.645 124.218 2.645 c
+123.806 2.645 123.497 2.418 123.291 1.97 c
+123.291 -0.397 l
+123.486 -0.838 123.798 -1.058 124.232 -1.058 c
+124.592 -1.058 124.854 -0.933 125.012 -0.676 c
+125.177 -0.422 125.261 -0.025 125.261 0.515 c
+h
+126.768 1.029 m
+126.768 1.741 126.937 2.311 127.283 2.734 c
+127.636 3.152 128.099 3.366 128.679 3.366 c
+129.256 3.366 129.716 3.16 130.061 2.749 c
+130.414 2.344 130.593 1.786 130.605 1.073 c
+130.605 0.559 l
+130.605 -0.169 130.428 -0.742 130.075 -1.161 c
+129.73 -1.573 129.271 -1.779 128.694 -1.779 c
+128.113 -1.779 127.65 -1.58 127.297 -1.176 c
+126.952 -0.765 126.775 -0.214 126.768 0.485 c
+h
+127.576 0.559 m
+127.576 0.048 127.672 -0.353 127.871 -0.647 c
+128.076 -0.941 128.348 -1.087 128.694 -1.087 c
+129.406 -1.087 129.774 -0.57 129.796 0.47 c
+129.796 1.029 l
+129.796 1.536 129.693 1.94 129.487 2.234 c
+129.289 2.528 129.02 2.675 128.679 2.675 c
+128.344 2.675 128.076 2.528 127.871 2.234 c
+127.672 1.94 127.576 1.536 127.576 1.029 c
+h
+131.303 1.029 m
+131.303 1.741 131.472 2.311 131.817 2.734 c
+132.17 3.152 132.633 3.366 133.213 3.366 c
+133.791 3.366 134.25 3.16 134.595 2.749 c
+134.948 2.344 135.128 1.786 135.139 1.073 c
+135.139 0.559 l
+135.139 -0.169 134.963 -0.742 134.61 -1.161 c
+134.264 -1.573 133.805 -1.779 133.228 -1.779 c
+132.648 -1.779 132.184 -1.58 131.832 -1.176 c
+131.486 -0.765 131.31 -0.214 131.303 0.485 c
+h
+132.111 0.559 m
+132.111 0.048 132.207 -0.353 132.405 -0.647 c
+132.611 -0.941 132.883 -1.087 133.228 -1.087 c
+133.941 -1.087 134.308 -0.57 134.331 0.47 c
+134.331 1.029 l
+134.331 1.536 134.227 1.94 134.022 2.234 c
+133.824 2.528 133.556 2.675 133.213 2.675 c
+132.879 2.675 132.611 2.528 132.405 2.234 c
+132.207 1.94 132.111 1.536 132.111 1.029 c
+h
+137.244 0.588 m
+136.818 0.088 l
+136.818 -1.69 l
+136.01 -1.69 l
+136.01 5.365 l
+136.818 5.365 l
+136.818 1.147 l
+138.362 3.278 l
+139.332 3.278 l
+137.759 1.205 l
+139.553 -1.69 l
+138.597 -1.69 l
+h
+143.393 -1.69 -0.809 4.968 re
+143.451 4.601 m
+143.451 4.461 143.411 4.343 143.334 4.248 c
+143.264 4.16 143.15 4.116 142.996 4.116 c
+142.838 4.116 142.72 4.16 142.643 4.248 c
+142.573 4.343 142.54 4.461 142.54 4.601 c
+142.54 4.737 142.573 4.85 142.643 4.939 c
+142.72 5.034 142.834 5.086 142.981 5.086 c
+143.135 5.086 143.253 5.034 143.334 4.939 c
+143.411 4.85 143.451 4.737 143.451 4.601 c
+147.001 -0.412 m
+147.001 -0.246 146.931 -0.1 146.796 0.029 c
+146.667 0.154 146.414 0.301 146.031 0.47 c
+145.597 0.654 145.289 0.812 145.105 0.941 c
+144.928 1.066 144.797 1.209 144.708 1.367 c
+144.62 1.532 144.576 1.727 144.576 1.955 c
+144.576 2.367 144.723 2.705 145.017 2.969 c
+145.311 3.233 145.686 3.366 146.148 3.366 c
+146.638 3.366 147.031 3.223 147.324 2.94 c
+147.619 2.664 147.766 2.308 147.766 1.866 c
+146.958 1.866 l
+146.958 2.091 146.877 2.282 146.722 2.44 c
+146.575 2.595 146.384 2.675 146.148 2.675 c
+145.913 2.675 145.726 2.609 145.59 2.484 c
+145.451 2.367 145.385 2.198 145.385 1.984 c
+145.385 1.816 145.433 1.683 145.531 1.587 c
+145.627 1.488 145.869 1.356 146.252 1.191 c
+146.858 0.956 147.27 0.723 147.486 0.5 c
+147.7 0.272 147.81 -0.011 147.81 -0.353 c
+147.81 -0.786 147.656 -1.132 147.355 -1.396 c
+147.06 -1.654 146.663 -1.779 146.164 -1.779 c
+145.653 -1.779 145.238 -1.631 144.914 -1.338 c
+144.591 -1.043 144.429 -0.669 144.429 -0.206 c
+145.252 -0.206 l
+145.26 -0.482 145.344 -0.698 145.502 -0.852 c
+145.656 -1.01 145.877 -1.087 146.164 -1.087 c
+146.435 -1.087 146.641 -1.029 146.781 -0.912 c
+146.927 -0.786 147.001 -0.617 147.001 -0.412 c
+153.682 -1.69 m
+153.63 -1.584 153.597 -1.404 153.579 -1.147 c
+153.292 -1.569 152.925 -1.779 152.477 -1.779 c
+152.024 -1.779 151.672 -1.658 151.419 -1.411 c
+151.172 -1.158 151.051 -0.802 151.051 -0.338 c
+151.051 0.169 151.22 0.573 151.565 0.867 c
+151.907 1.161 152.377 1.311 152.976 1.323 c
+153.564 1.323 l
+153.564 1.852 l
+153.564 2.146 153.495 2.356 153.358 2.484 c
+153.23 2.609 153.036 2.675 152.77 2.675 c
+152.525 2.675 152.323 2.602 152.168 2.454 c
+152.021 2.308 151.947 2.12 151.947 1.897 c
+151.124 1.897 l
+151.124 2.15 151.197 2.396 151.345 2.631 c
+151.499 2.866 151.705 3.046 151.962 3.175 c
+152.215 3.3 152.499 3.366 152.815 3.366 c
+153.322 3.366 153.711 3.237 153.976 2.984 c
+154.241 2.738 154.372 2.37 154.372 1.882 c
+154.372 -0.617 l
+154.38 -1 154.435 -1.334 154.534 -1.617 c
+154.534 -1.69 l
+h
+152.609 -1.043 m
+152.803 -1.043 152.991 -0.992 153.167 -0.882 c
+153.351 -0.765 153.483 -0.628 153.564 -0.47 c
+153.564 0.721 l
+153.109 0.721 l
+152.726 0.709 152.421 0.621 152.197 0.455 c
+151.97 0.287 151.86 0.058 151.86 -0.235 c
+151.86 -0.522 151.911 -0.728 152.021 -0.852 c
+152.138 -0.981 152.333 -1.043 152.609 -1.043 c
+156.784 -0.455 m
+157.724 3.278 l
+158.562 3.278 l
+157.077 -1.69 l
+156.475 -1.69 l
+154.975 3.278 l
+155.814 3.278 l
+h
+161.722 -1.69 m
+161.671 -1.584 161.638 -1.404 161.62 -1.147 c
+161.333 -1.569 160.965 -1.779 160.517 -1.779 c
+160.065 -1.779 159.712 -1.658 159.459 -1.411 c
+159.213 -1.158 159.091 -0.802 159.091 -0.338 c
+159.091 0.169 159.261 0.573 159.606 0.867 c
+159.947 1.161 160.418 1.311 161.017 1.323 c
+161.605 1.323 l
+161.605 1.852 l
+161.605 2.146 161.535 2.356 161.399 2.484 c
+161.27 2.609 161.075 2.675 160.811 2.675 c
+160.564 2.675 160.363 2.602 160.209 2.454 c
+160.061 2.308 159.988 2.12 159.988 1.897 c
+159.165 1.897 l
+159.165 2.15 159.238 2.396 159.386 2.631 c
+159.54 2.866 159.745 3.046 160.003 3.175 c
+160.256 3.3 160.539 3.366 160.855 3.366 c
+161.362 3.366 161.752 3.237 162.016 2.984 c
+162.281 2.738 162.413 2.37 162.413 1.882 c
+162.413 -0.617 l
+162.42 -1 162.476 -1.334 162.575 -1.617 c
+162.575 -1.69 l
+h
+160.649 -1.043 m
+160.844 -1.043 161.032 -0.992 161.208 -0.882 c
+161.391 -0.765 161.524 -0.628 161.605 -0.47 c
+161.605 0.721 l
+161.15 0.721 l
+160.767 0.709 160.462 0.621 160.238 0.455 c
+160.01 0.287 159.899 0.058 159.899 -0.235 c
+159.899 -0.522 159.951 -0.728 160.061 -0.852 c
+160.179 -0.981 160.373 -1.043 160.649 -1.043 c
+164.368 -1.69 -0.808 4.968 re
+164.427 4.601 m
+164.427 4.461 164.387 4.343 164.31 4.248 c
+164.24 4.16 164.126 4.116 163.972 4.116 c
+163.814 4.116 163.696 4.16 163.619 4.248 c
+163.548 4.343 163.516 4.461 163.516 4.601 c
+163.516 4.737 163.548 4.85 163.619 4.939 c
+163.696 5.034 163.81 5.086 163.957 5.086 c
+164.111 5.086 164.229 5.034 164.31 4.939 c
+164.387 4.85 164.427 4.737 164.427 4.601 c
+166.434 -1.69 -0.809 7.056 re
+170.174 -1.69 m
+170.123 -1.584 170.089 -1.404 170.072 -1.147 c
+169.785 -1.569 169.418 -1.779 168.969 -1.779 c
+168.517 -1.779 168.165 -1.658 167.911 -1.411 c
+167.664 -1.158 167.543 -0.802 167.543 -0.338 c
+167.543 0.169 167.712 0.573 168.057 0.867 c
+168.4 1.161 168.87 1.311 169.469 1.323 c
+170.056 1.323 l
+170.056 1.852 l
+170.056 2.146 169.987 2.356 169.851 2.484 c
+169.723 2.609 169.528 2.675 169.263 2.675 c
+169.017 2.675 168.815 2.602 168.66 2.454 c
+168.514 2.308 168.44 2.12 168.44 1.897 c
+167.617 1.897 l
+167.617 2.15 167.69 2.396 167.837 2.631 c
+167.992 2.866 168.198 3.046 168.454 3.175 c
+168.708 3.3 168.992 3.366 169.307 3.366 c
+169.814 3.366 170.204 3.237 170.469 2.984 c
+170.733 2.738 170.865 2.37 170.865 1.882 c
+170.865 -0.617 l
+170.873 -1 170.928 -1.334 171.027 -1.617 c
+171.027 -1.69 l
+h
+169.102 -1.043 m
+169.296 -1.043 169.484 -0.992 169.659 -0.882 c
+169.844 -0.765 169.976 -0.628 170.056 -0.47 c
+170.056 0.721 l
+169.601 0.721 l
+169.219 0.709 168.913 0.621 168.69 0.455 c
+168.462 0.287 168.352 0.058 168.352 -0.235 c
+168.352 -0.522 168.403 -0.728 168.514 -0.852 c
+168.631 -0.981 168.826 -1.043 169.102 -1.043 c
+175.539 0.559 m
+175.539 -0.217 175.396 -0.802 175.113 -1.191 c
+174.837 -1.584 174.437 -1.779 173.908 -1.779 c
+173.386 -1.779 172.996 -1.554 172.732 -1.103 c
+172.688 -1.69 l
+171.953 -1.69 l
+171.953 5.365 l
+172.761 5.365 l
+172.761 2.734 l
+173.026 3.152 173.408 3.366 173.908 3.366 c
+174.444 3.366 174.852 3.175 175.128 2.793 c
+175.4 2.41 175.539 1.826 175.539 1.043 c
+h
+174.731 1.014 m
+174.731 1.602 174.646 2.017 174.481 2.263 c
+174.323 2.517 174.058 2.645 173.688 2.645 c
+173.276 2.645 172.967 2.418 172.761 1.97 c
+172.761 -0.397 l
+172.956 -0.838 173.268 -1.058 173.702 -1.058 c
+174.062 -1.058 174.323 -0.933 174.481 -0.676 c
+174.646 -0.422 174.731 -0.025 174.731 0.515 c
+h
+177.281 -1.69 -0.808 7.056 re
+180.243 -1.779 m
+179.626 -1.779 179.148 -1.598 178.818 -1.235 c
+178.494 -0.864 178.325 -0.32 178.318 0.397 c
+178.318 0.999 l
+178.318 1.735 178.479 2.311 178.803 2.734 c
+179.126 3.152 179.575 3.366 180.155 3.366 c
+180.732 3.366 181.162 3.179 181.449 2.807 c
+181.742 2.444 181.893 1.87 181.904 1.087 c
+181.904 0.559 l
+179.126 0.559 l
+179.126 0.441 l
+179.126 -0.1 179.221 -0.493 179.42 -0.735 c
+179.626 -0.97 179.913 -1.087 180.287 -1.087 c
+180.53 -1.087 180.743 -1.043 180.919 -0.956 c
+181.103 -0.867 181.276 -0.728 181.434 -0.53 c
+181.86 -1.043 l
+181.507 -1.536 180.967 -1.779 180.243 -1.779 c
+180.155 2.675 m
+179.82 2.675 179.571 2.558 179.406 2.323 c
+179.236 2.094 179.144 1.738 179.126 1.249 c
+181.081 1.249 l
+181.081 1.367 l
+181.059 1.837 180.978 2.168 180.831 2.367 c
+180.684 2.572 180.456 2.675 180.155 2.675 c
+184.973 1.029 m
+184.973 1.741 185.141 2.311 185.487 2.734 c
+185.84 3.152 186.303 3.366 186.883 3.366 c
+187.46 3.366 187.92 3.16 188.265 2.749 c
+188.618 2.344 188.798 1.786 188.809 1.073 c
+188.809 0.559 l
+188.809 -0.169 188.632 -0.742 188.279 -1.161 c
+187.934 -1.573 187.475 -1.779 186.899 -1.779 c
+186.317 -1.779 185.854 -1.58 185.502 -1.176 c
+185.156 -0.765 184.98 -0.214 184.973 0.485 c
+h
+185.781 0.559 m
+185.781 0.048 185.877 -0.353 186.075 -0.647 c
+186.28 -0.941 186.552 -1.087 186.899 -1.087 c
+187.611 -1.087 187.978 -0.57 188.001 0.47 c
+188.001 1.029 l
+188.001 1.536 187.898 1.94 187.692 2.234 c
+187.493 2.528 187.225 2.675 186.883 2.675 c
+186.549 2.675 186.28 2.528 186.075 2.234 c
+185.877 1.94 185.781 1.536 185.781 1.029 c
+h
+190.433 3.278 m
+190.463 2.749 l
+190.775 3.16 191.176 3.366 191.668 3.366 c
+192.549 3.366 192.995 2.782 193.006 1.617 c
+193.006 -1.69 l
+192.197 -1.69 l
+192.197 1.573 l
+192.197 1.962 192.131 2.238 192.006 2.396 c
+191.878 2.562 191.683 2.645 191.418 2.645 c
+191.213 2.645 191.028 2.576 190.874 2.44 c
+190.716 2.311 190.588 2.138 190.492 1.926 c
+190.492 -1.69 l
+189.669 -1.69 l
+189.669 3.278 l
+h
+194.924 -1.69 -0.808 7.056 re
+196.989 -1.69 -0.808 4.968 re
+197.048 4.601 m
+197.048 4.461 197.008 4.343 196.931 4.248 c
+196.86 4.16 196.746 4.116 196.592 4.116 c
+196.434 4.116 196.316 4.16 196.239 4.248 c
+196.169 4.343 196.137 4.461 196.137 4.601 c
+196.137 4.737 196.169 4.85 196.239 4.939 c
+196.316 5.034 196.43 5.086 196.578 5.086 c
+196.732 5.086 196.85 5.034 196.931 4.939 c
+197.008 4.85 197.048 4.737 197.048 4.601 c
+198.937 3.278 m
+198.966 2.749 l
+199.279 3.16 199.679 3.366 200.171 3.366 c
+201.054 3.366 201.498 2.782 201.509 1.617 c
+201.509 -1.69 l
+200.701 -1.69 l
+200.701 1.573 l
+200.701 1.962 200.634 2.238 200.509 2.396 c
+200.381 2.562 200.186 2.645 199.921 2.645 c
+199.716 2.645 199.532 2.576 199.377 2.44 c
+199.219 2.311 199.091 2.138 198.995 1.926 c
+198.995 -1.69 l
+198.172 -1.69 l
+198.172 3.278 l
+h
+204.324 -1.779 m
+203.707 -1.779 203.229 -1.598 202.898 -1.235 c
+202.575 -0.864 202.406 -0.32 202.398 0.397 c
+202.398 0.999 l
+202.398 1.735 202.56 2.311 202.884 2.734 c
+203.207 3.152 203.655 3.366 204.235 3.366 c
+204.813 3.366 205.243 3.179 205.529 2.807 c
+205.823 2.444 205.974 1.87 205.985 1.087 c
+205.985 0.559 l
+203.207 0.559 l
+203.207 0.441 l
+203.207 -0.1 203.302 -0.493 203.501 -0.735 c
+203.707 -0.97 203.994 -1.087 204.368 -1.087 c
+204.611 -1.087 204.823 -1.043 205 -0.956 c
+205.184 -0.867 205.357 -0.728 205.515 -0.53 c
+205.941 -1.043 l
+205.588 -1.536 205.048 -1.779 204.324 -1.779 c
+204.235 2.675 m
+203.901 2.675 203.651 2.558 203.486 2.323 c
+203.317 2.094 203.225 1.738 203.207 1.249 c
+205.162 1.249 l
+205.162 1.367 l
+205.139 1.837 205.059 2.168 204.912 2.367 c
+204.765 2.572 204.537 2.675 204.235 2.675 c
+209.586 -1.69 m
+209.586 2.616 l
+208.939 2.616 l
+208.939 3.278 l
+209.586 3.278 l
+209.586 3.851 l
+209.594 4.358 209.722 4.755 209.968 5.041 c
+210.211 5.325 210.56 5.468 211.012 5.468 c
+211.167 5.468 211.328 5.438 211.497 5.38 c
+211.453 4.689 l
+211.343 4.719 211.221 4.733 211.086 4.733 c
+210.622 4.733 210.394 4.403 210.394 3.748 c
+210.394 3.278 l
+211.247 3.278 l
+211.247 2.616 l
+210.394 2.616 l
+210.394 -1.69 l
+h
+211.912 1.029 m
+211.912 1.741 212.081 2.311 212.426 2.734 c
+212.779 3.152 213.243 3.366 213.823 3.366 c
+214.4 3.366 214.859 3.16 215.204 2.749 c
+215.558 2.344 215.738 1.786 215.749 1.073 c
+215.749 0.559 l
+215.749 -0.169 215.572 -0.742 215.219 -1.161 c
+214.874 -1.573 214.415 -1.779 213.838 -1.779 c
+213.257 -1.779 212.794 -1.58 212.441 -1.176 c
+212.096 -0.765 211.919 -0.214 211.912 0.485 c
+h
+212.721 0.559 m
+212.721 0.048 212.816 -0.353 213.014 -0.647 c
+213.22 -0.941 213.492 -1.087 213.838 -1.087 c
+214.551 -1.087 214.918 -0.57 214.94 0.47 c
+214.94 1.029 l
+214.94 1.536 214.838 1.94 214.632 2.234 c
+214.433 2.528 214.165 2.675 213.823 2.675 c
+213.489 2.675 213.22 2.528 213.014 2.234 c
+212.816 1.94 212.721 1.536 212.721 1.029 c
+h
+218.666 2.514 m
+218.549 2.532 218.424 2.543 218.299 2.543 c
+217.876 2.543 217.586 2.315 217.432 1.866 c
+217.432 -1.69 l
+216.609 -1.69 l
+216.609 3.278 l
+217.402 3.278 l
+217.417 2.778 l
+217.631 3.167 217.939 3.366 218.343 3.366 c
+218.468 3.366 218.578 3.344 218.666 3.308 c
+h
+222.165 -1.69 m
+222.165 2.616 l
+221.518 2.616 l
+221.518 3.278 l
+222.165 3.278 l
+222.165 3.851 l
+222.172 4.358 222.3 4.755 222.547 5.041 c
+222.79 5.325 223.139 5.468 223.591 5.468 c
+223.745 5.468 223.906 5.438 224.076 5.38 c
+224.031 4.689 l
+223.921 4.719 223.8 4.733 223.664 4.733 c
+223.201 4.733 222.973 4.403 222.973 3.748 c
+222.973 3.278 l
+223.826 3.278 l
+223.826 2.616 l
+222.973 2.616 l
+222.973 -1.69 l
+h
+226.714 2.514 m
+226.597 2.532 226.472 2.543 226.347 2.543 c
+225.924 2.543 225.633 2.315 225.479 1.866 c
+225.479 -1.69 l
+224.656 -1.69 l
+224.656 3.278 l
+225.45 3.278 l
+225.465 2.778 l
+225.678 3.167 225.986 3.366 226.391 3.366 c
+226.516 3.366 226.626 3.344 226.714 3.308 c
+h
+229.099 -1.779 m
+228.482 -1.779 228.004 -1.598 227.673 -1.235 c
+227.35 -0.864 227.181 -0.32 227.173 0.397 c
+227.173 0.999 l
+227.173 1.735 227.335 2.311 227.659 2.734 c
+227.982 3.152 228.43 3.366 229.011 3.366 c
+229.588 3.366 230.018 3.179 230.304 2.807 c
+230.599 2.444 230.749 1.87 230.76 1.087 c
+230.76 0.559 l
+227.982 0.559 l
+227.982 0.441 l
+227.982 -0.1 228.077 -0.493 228.276 -0.735 c
+228.482 -0.97 228.768 -1.087 229.143 -1.087 c
+229.386 -1.087 229.599 -1.043 229.776 -0.956 c
+229.959 -0.867 230.132 -0.728 230.29 -0.53 c
+230.716 -1.043 l
+230.364 -1.536 229.823 -1.779 229.099 -1.779 c
+229.011 2.675 m
+228.677 2.675 228.427 2.558 228.261 2.323 c
+228.093 2.094 228 1.738 227.982 1.249 c
+229.937 1.249 l
+229.937 1.367 l
+229.915 1.837 229.834 2.168 229.687 2.367 c
+229.54 2.572 229.313 2.675 229.011 2.675 c
+233.332 -1.779 m
+232.715 -1.779 232.237 -1.598 231.906 -1.235 c
+231.584 -0.864 231.414 -0.32 231.407 0.397 c
+231.407 0.999 l
+231.407 1.735 231.569 2.311 231.892 2.734 c
+232.216 3.152 232.664 3.366 233.244 3.366 c
+233.822 3.366 234.252 3.179 234.538 2.807 c
+234.832 2.444 234.983 1.87 234.994 1.087 c
+234.994 0.559 l
+232.216 0.559 l
+232.216 0.441 l
+232.216 -0.1 232.311 -0.493 232.509 -0.735 c
+232.715 -0.97 233.001 -1.087 233.377 -1.087 c
+233.619 -1.087 233.832 -1.043 234.009 -0.956 c
+234.192 -0.867 234.365 -0.728 234.523 -0.53 c
+234.95 -1.043 l
+234.597 -1.536 234.057 -1.779 233.332 -1.779 c
+233.244 2.675 m
+232.91 2.675 232.66 2.558 232.494 2.323 c
+232.326 2.094 232.234 1.738 232.216 1.249 c
+234.171 1.249 l
+234.171 1.367 l
+234.148 1.837 234.067 2.168 233.92 2.367 c
+233.774 2.572 233.546 2.675 233.244 2.675 c
+f
+Q
+q 1 0 0 1 538.6692 292.1129 cm
+0 0 m
+-0.052 0.106 -0.085 0.287 -0.103 0.544 c
+-0.389 0.121 -0.757 -0.088 -1.205 -0.088 c
+-1.658 -0.088 -2.01 0.033 -2.263 0.279 c
+-2.51 0.532 -2.631 0.889 -2.631 1.353 c
+-2.631 1.86 -2.462 2.263 -2.117 2.558 c
+-1.775 2.851 -1.305 3.002 -0.706 3.013 c
+-0.118 3.013 l
+-0.118 3.543 l
+-0.118 3.836 -0.187 4.046 -0.324 4.175 c
+-0.452 4.299 -0.646 4.366 -0.912 4.366 c
+-1.157 4.366 -1.359 4.293 -1.514 4.145 c
+-1.66 3.998 -1.735 3.811 -1.735 3.587 c
+-2.558 3.587 l
+-2.558 3.84 -2.484 4.087 -2.337 4.322 c
+-2.182 4.557 -1.977 4.737 -1.72 4.866 c
+-1.466 4.991 -1.183 5.056 -0.867 5.056 c
+-0.36 5.056 0.029 4.928 0.294 4.675 c
+0.559 4.428 0.691 4.06 0.691 3.572 c
+0.691 1.073 l
+0.698 0.69 0.754 0.357 0.852 0.073 c
+0.852 0 l
+h
+-1.072 0.647 m
+-0.878 0.647 -0.69 0.698 -0.515 0.808 c
+-0.33 0.926 -0.199 1.062 -0.118 1.22 c
+-0.118 2.411 l
+-0.573 2.411 l
+-0.955 2.4 -1.261 2.311 -1.484 2.146 c
+-1.712 1.977 -1.822 1.749 -1.822 1.455 c
+-1.822 1.168 -1.771 0.962 -1.66 0.838 c
+-1.543 0.709 -1.348 0.647 -1.072 0.647 c
+2.837 6.174 m
+2.837 4.968 l
+3.587 4.968 l
+3.587 4.307 l
+2.837 4.307 l
+2.837 1.235 l
+2.837 1.037 2.866 0.885 2.926 0.779 c
+2.992 0.68 3.105 0.632 3.263 0.632 c
+3.37 0.632 3.476 0.65 3.587 0.69 c
+3.572 0 l
+3.396 -0.058 3.208 -0.088 3.013 -0.088 c
+2.691 -0.088 2.444 0.022 2.278 0.25 c
+2.109 0.485 2.028 0.808 2.028 1.22 c
+2.028 4.307 l
+1.264 4.307 l
+1.264 4.968 l
+2.028 4.968 l
+2.028 6.174 l
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+q 1 0 0 1 545.9825 296.5664 cm
+0 0 m
+0.301 0.401 0.694 0.603 1.176 0.603 c
+2.057 0.603 2.502 0.019 2.513 -1.146 c
+2.513 -4.453 l
+1.705 -4.453 l
+1.705 -1.19 l
+1.705 -0.801 1.639 -0.525 1.514 -0.367 c
+1.385 -0.201 1.19 -0.118 0.926 -0.118 c
+0.72 -0.118 0.536 -0.187 0.382 -0.323 c
+0.224 -0.452 0.095 -0.625 0 -0.837 c
+0 -4.453 l
+-0.823 -4.453 l
+-0.823 2.602 l
+0 2.602 l
+h
+4.608 1.721 m
+4.608 0.515 l
+5.357 0.515 l
+5.357 -0.147 l
+4.608 -0.147 l
+4.608 -3.218 l
+4.608 -3.417 4.637 -3.568 4.696 -3.674 c
+4.762 -3.773 4.876 -3.821 5.034 -3.821 c
+5.14 -3.821 5.247 -3.803 5.357 -3.763 c
+5.342 -4.453 l
+5.167 -4.512 4.979 -4.542 4.785 -4.542 c
+4.461 -4.542 4.214 -4.432 4.049 -4.203 c
+3.881 -3.968 3.8 -3.645 3.8 -3.233 c
+3.8 -0.147 l
+3.035 -0.147 l
+3.035 0.515 l
+3.8 0.515 l
+3.8 1.721 l
+h
+7.272 1.721 m
+7.272 0.515 l
+8.022 0.515 l
+8.022 -0.147 l
+7.272 -0.147 l
+7.272 -3.218 l
+7.272 -3.417 7.301 -3.568 7.36 -3.674 c
+7.426 -3.773 7.54 -3.821 7.698 -3.821 c
+7.804 -3.821 7.912 -3.803 8.022 -3.763 c
+8.007 -4.453 l
+7.831 -4.512 7.643 -4.542 7.449 -4.542 c
+7.125 -4.542 6.879 -4.432 6.713 -4.203 c
+6.545 -3.968 6.464 -3.645 6.464 -3.233 c
+6.464 -0.147 l
+5.699 -0.147 l
+5.699 0.515 l
+6.464 0.515 l
+6.464 1.721 l
+h
+12.461 -2.204 m
+12.461 -2.991 12.317 -3.579 12.035 -3.968 c
+11.748 -4.351 11.347 -4.542 10.829 -4.542 c
+10.329 -4.542 9.947 -4.357 9.683 -3.983 c
+9.683 -6.364 l
+8.874 -6.364 l
+8.874 0.515 l
+9.609 0.515 l
+9.639 -0.029 l
+9.911 0.389 10.304 0.603 10.815 0.603 c
+11.362 0.603 11.77 0.412 12.035 0.03 c
+12.307 -0.345 12.45 -0.907 12.461 -1.66 c
+h
+11.652 -1.749 m
+11.652 -1.19 11.564 -0.779 11.388 -0.514 c
+11.218 -0.249 10.946 -0.118 10.564 -0.118 c
+10.171 -0.118 9.878 -0.309 9.683 -0.69 c
+9.683 -3.277 l
+9.878 -3.659 10.175 -3.85 10.58 -3.85 c
+10.94 -3.85 11.204 -3.719 11.373 -3.453 c
+11.549 -3.189 11.642 -2.786 11.652 -2.234 c
+h
+15.75 -3.175 m
+15.75 -3.009 15.68 -2.863 15.544 -2.734 c
+15.415 -2.609 15.162 -2.462 14.78 -2.293 c
+14.346 -2.109 14.037 -1.951 13.853 -1.822 c
+13.677 -1.697 13.545 -1.554 13.456 -1.396 c
+13.369 -1.23 13.325 -1.036 13.325 -0.808 c
+13.325 -0.396 13.471 -0.058 13.765 0.206 c
+14.059 0.47 14.434 0.603 14.898 0.603 c
+15.386 0.603 15.779 0.46 16.073 0.177 c
+16.367 -0.099 16.514 -0.455 16.514 -0.897 c
+15.706 -0.897 l
+15.706 -0.672 15.625 -0.481 15.47 -0.323 c
+15.324 -0.168 15.133 -0.087 14.898 -0.087 c
+14.662 -0.087 14.474 -0.154 14.339 -0.278 c
+14.198 -0.396 14.133 -0.565 14.133 -0.779 c
+14.133 -0.947 14.181 -1.08 14.279 -1.176 c
+14.375 -1.275 14.618 -1.407 15 -1.572 c
+15.606 -1.807 16.018 -2.04 16.234 -2.263 c
+16.448 -2.491 16.558 -2.774 16.558 -3.116 c
+16.558 -3.549 16.404 -3.895 16.103 -4.159 c
+15.808 -4.417 15.411 -4.542 14.912 -4.542 c
+14.401 -4.542 13.986 -4.394 13.662 -4.101 c
+13.339 -3.806 13.177 -3.432 13.177 -2.969 c
+14 -2.969 l
+14.008 -3.245 14.092 -3.461 14.25 -3.615 c
+14.405 -3.773 14.625 -3.85 14.912 -3.85 c
+15.183 -3.85 15.39 -3.792 15.529 -3.674 c
+15.676 -3.549 15.75 -3.38 15.75 -3.175 c
+17.491 -4.012 m
+17.491 -3.865 17.535 -3.744 17.624 -3.645 c
+17.712 -3.549 17.837 -3.498 18.006 -3.498 c
+18.183 -3.498 18.314 -3.549 18.403 -3.645 c
+18.499 -3.744 18.549 -3.865 18.549 -4.012 c
+18.549 -4.152 18.499 -4.27 18.403 -4.365 c
+18.314 -4.453 18.183 -4.498 18.006 -4.498 c
+17.837 -4.498 17.712 -4.453 17.624 -4.365 c
+17.535 -4.27 17.491 -4.152 17.491 -4.012 c
+17.491 -0.249 m
+17.491 -0.103 17.535 0.019 17.624 0.118 c
+17.712 0.214 17.837 0.265 18.006 0.265 c
+18.183 0.265 18.314 0.214 18.403 0.118 c
+18.499 0.019 18.549 -0.103 18.549 -0.249 c
+18.549 -0.389 18.499 -0.507 18.403 -0.602 c
+18.314 -0.69 18.183 -0.735 18.006 -0.735 c
+17.837 -0.735 17.712 -0.69 17.624 -0.602 c
+17.535 -0.507 17.491 -0.389 17.491 -0.249 c
+19.785 -5.026 m
+19.093 -5.026 l
+21.431 2.234 l
+22.121 2.234 l
+h
+22.148 -5.026 m
+21.456 -5.026 l
+23.794 2.234 l
+24.485 2.234 l
+h
+25.102 -1.749 m
+25.102 -0.977 25.242 -0.393 25.528 0 c
+25.811 0.401 26.223 0.603 26.763 0.603 c
+27.27 0.603 27.66 0.387 27.924 -0.043 c
+27.953 0.515 l
+28.689 0.515 l
+28.689 -4.498 l
+28.689 -5.115 28.531 -5.585 28.218 -5.909 c
+27.902 -6.239 27.469 -6.408 26.91 -6.408 c
+26.663 -6.408 26.384 -6.342 26.072 -6.217 c
+25.767 -6.1 25.543 -5.953 25.396 -5.776 c
+25.719 -5.217 l
+26.061 -5.548 26.432 -5.718 26.836 -5.718 c
+27.513 -5.718 27.858 -5.35 27.88 -4.615 c
+27.88 -3.968 l
+27.615 -4.351 27.241 -4.542 26.763 -4.542 c
+26.241 -4.542 25.84 -4.351 25.558 -3.968 c
+25.271 -3.579 25.121 -3.021 25.102 -2.293 c
+h
+25.925 -2.204 m
+25.925 -2.763 26.002 -3.175 26.16 -3.439 c
+26.326 -3.696 26.594 -3.821 26.969 -3.821 c
+27.369 -3.821 27.675 -3.615 27.88 -3.204 c
+27.88 -0.72 l
+27.675 -0.319 27.373 -0.118 26.983 -0.118 c
+26.609 -0.118 26.341 -0.249 26.175 -0.514 c
+26.006 -0.771 25.925 -1.168 25.925 -1.705 c
+h
+30.592 -4.453 -0.808 4.968 re
+30.651 1.838 m
+30.651 1.698 30.611 1.58 30.534 1.485 c
+30.463 1.397 30.349 1.353 30.195 1.353 c
+30.037 1.353 29.919 1.397 29.842 1.485 c
+29.772 1.58 29.74 1.698 29.74 1.838 c
+29.74 1.974 29.772 2.088 29.842 2.176 c
+29.919 2.271 30.033 2.323 30.181 2.323 c
+30.335 2.323 30.453 2.271 30.534 2.176 c
+30.611 2.088 30.651 1.974 30.651 1.838 c
+32.849 1.721 m
+32.849 0.515 l
+33.598 0.515 l
+33.598 -0.147 l
+32.849 -0.147 l
+32.849 -3.218 l
+32.849 -3.417 32.878 -3.568 32.936 -3.674 c
+33.003 -3.773 33.117 -3.821 33.275 -3.821 c
+33.381 -3.821 33.487 -3.803 33.598 -3.763 c
+33.583 -4.453 l
+33.407 -4.512 33.219 -4.542 33.025 -4.542 c
+32.701 -4.542 32.456 -4.432 32.29 -4.203 c
+32.121 -3.968 32.04 -3.645 32.04 -3.233 c
+32.04 -0.147 l
+31.276 -0.147 l
+31.276 0.515 l
+32.04 0.515 l
+32.04 1.721 l
+h
+35.949 -1.955 -1.896 0.691 re
+39.103 -3.175 m
+39.103 -3.009 39.033 -2.863 38.897 -2.734 c
+38.769 -2.609 38.515 -2.462 38.133 -2.293 c
+37.699 -2.109 37.391 -1.951 37.206 -1.822 c
+37.03 -1.697 36.898 -1.554 36.81 -1.396 c
+36.722 -1.23 36.677 -1.036 36.677 -0.808 c
+36.677 -0.396 36.824 -0.058 37.119 0.206 c
+37.412 0.47 37.788 0.603 38.25 0.603 c
+38.739 0.603 39.132 0.46 39.426 0.177 c
+39.72 -0.099 39.868 -0.455 39.868 -0.897 c
+39.059 -0.897 l
+39.059 -0.672 38.978 -0.481 38.823 -0.323 c
+38.677 -0.168 38.486 -0.087 38.25 -0.087 c
+38.015 -0.087 37.828 -0.154 37.692 -0.278 c
+37.552 -0.396 37.486 -0.565 37.486 -0.779 c
+37.486 -0.947 37.534 -1.08 37.632 -1.176 c
+37.728 -1.275 37.971 -1.407 38.353 -1.572 c
+38.96 -1.807 39.371 -2.04 39.588 -2.263 c
+39.801 -2.491 39.911 -2.774 39.911 -3.116 c
+39.911 -3.549 39.757 -3.895 39.456 -4.159 c
+39.162 -4.417 38.765 -4.542 38.265 -4.542 c
+37.754 -4.542 37.339 -4.394 37.015 -4.101 c
+36.692 -3.806 36.531 -3.432 36.531 -2.969 c
+37.354 -2.969 l
+37.36 -3.245 37.445 -3.461 37.603 -3.615 c
+37.758 -3.773 37.979 -3.85 38.265 -3.85 c
+38.536 -3.85 38.742 -3.792 38.883 -3.674 c
+39.029 -3.549 39.103 -3.38 39.103 -3.175 c
+42.52 -3.85 m
+42.785 -3.85 42.995 -3.77 43.153 -3.601 c
+43.317 -3.436 43.409 -3.197 43.432 -2.881 c
+44.196 -2.881 l
+44.174 -3.362 44.005 -3.759 43.681 -4.072 c
+43.365 -4.388 42.979 -4.542 42.52 -4.542 c
+41.903 -4.542 41.433 -4.351 41.109 -3.968 c
+40.786 -3.579 40.624 -2.998 40.624 -2.234 c
+40.624 -1.69 l
+40.624 -0.937 40.786 -0.367 41.109 0.015 c
+41.433 0.405 41.896 0.603 42.505 0.603 c
+43.012 0.603 43.417 0.441 43.71 0.118 c
+44.012 -0.198 44.174 -0.631 44.196 -1.19 c
+43.432 -1.19 l
+43.409 -0.83 43.321 -0.554 43.167 -0.367 c
+43.009 -0.183 42.789 -0.087 42.505 -0.087 c
+42.153 -0.087 41.888 -0.213 41.711 -0.455 c
+41.535 -0.69 41.44 -1.08 41.433 -1.616 c
+41.433 -2.248 l
+41.433 -2.836 41.52 -3.256 41.697 -3.498 c
+41.873 -3.733 42.145 -3.85 42.52 -3.85 c
+45.732 0.515 m
+45.761 0.059 l
+46.062 0.42 46.459 0.603 46.952 0.603 c
+47.51 0.603 47.897 0.36 48.113 -0.118 c
+48.425 0.36 48.863 0.603 49.422 0.603 c
+50.34 0.603 50.81 0.034 50.833 -1.102 c
+50.833 -4.453 l
+50.024 -4.453 l
+50.024 -1.176 l
+50.024 -0.823 49.954 -0.558 49.819 -0.382 c
+49.69 -0.205 49.473 -0.118 49.171 -0.118 c
+48.925 -0.118 48.724 -0.213 48.569 -0.396 c
+48.422 -0.584 48.334 -0.823 48.304 -1.117 c
+48.304 -4.453 l
+47.496 -4.453 l
+47.496 -1.146 l
+47.485 -0.463 47.205 -0.118 46.658 -0.118 c
+46.247 -0.118 45.956 -0.323 45.79 -0.735 c
+45.79 -4.453 l
+44.982 -4.453 l
+44.982 0.515 l
+h
+51.818 -4.012 m
+51.818 -3.865 51.862 -3.744 51.949 -3.645 c
+52.038 -3.549 52.163 -3.498 52.332 -3.498 c
+52.508 -3.498 52.641 -3.549 52.728 -3.645 c
+52.824 -3.744 52.876 -3.865 52.876 -4.012 c
+52.876 -4.152 52.824 -4.27 52.728 -4.365 c
+52.641 -4.453 52.508 -4.498 52.332 -4.498 c
+52.163 -4.498 52.038 -4.453 51.949 -4.365 c
+51.862 -4.27 51.818 -4.152 51.818 -4.012 c
+55.874 -3.85 m
+56.139 -3.85 56.348 -3.77 56.506 -3.601 c
+56.672 -3.436 56.763 -3.197 56.786 -2.881 c
+57.55 -2.881 l
+57.528 -3.362 57.359 -3.759 57.036 -4.072 c
+56.72 -4.388 56.333 -4.542 55.874 -4.542 c
+55.257 -4.542 54.787 -4.351 54.463 -3.968 c
+54.14 -3.579 53.979 -2.998 53.979 -2.234 c
+53.979 -1.69 l
+53.979 -0.937 54.14 -0.367 54.463 0.015 c
+54.787 0.405 55.25 0.603 55.86 0.603 c
+56.367 0.603 56.771 0.441 57.065 0.118 c
+57.366 -0.198 57.528 -0.631 57.55 -1.19 c
+56.786 -1.19 l
+56.763 -0.83 56.676 -0.554 56.521 -0.367 c
+56.363 -0.183 56.142 -0.087 55.86 -0.087 c
+55.506 -0.087 55.242 -0.213 55.066 -0.455 c
+54.889 -0.69 54.794 -1.08 54.787 -1.616 c
+54.787 -2.248 l
+54.787 -2.836 54.875 -3.256 55.051 -3.498 c
+55.228 -3.733 55.5 -3.85 55.874 -3.85 c
+58.16 -1.734 m
+58.16 -1.022 58.329 -0.452 58.675 -0.029 c
+59.028 0.389 59.49 0.603 60.071 0.603 c
+60.648 0.603 61.108 0.397 61.453 -0.014 c
+61.806 -0.419 61.985 -0.977 61.997 -1.69 c
+61.997 -2.204 l
+61.997 -2.932 61.82 -3.505 61.467 -3.924 c
+61.122 -4.336 60.663 -4.542 60.086 -4.542 c
+59.505 -4.542 59.042 -4.343 58.689 -3.939 c
+58.344 -3.528 58.168 -2.977 58.16 -2.278 c
+h
+58.968 -2.204 m
+58.968 -2.715 59.064 -3.116 59.263 -3.41 c
+59.468 -3.704 59.74 -3.85 60.086 -3.85 c
+60.799 -3.85 61.166 -3.333 61.188 -2.293 c
+61.188 -1.734 l
+61.188 -1.227 61.085 -0.823 60.879 -0.529 c
+60.68 -0.235 60.412 -0.087 60.071 -0.087 c
+59.737 -0.087 59.468 -0.235 59.263 -0.529 c
+59.064 -0.823 58.968 -1.227 58.968 -1.734 c
+h
+63.617 0.515 m
+63.647 0.059 l
+63.948 0.42 64.345 0.603 64.837 0.603 c
+65.395 0.603 65.781 0.36 65.998 -0.118 c
+66.311 0.36 66.748 0.603 67.306 0.603 c
+68.225 0.603 68.696 0.034 68.717 -1.102 c
+68.717 -4.453 l
+67.909 -4.453 l
+67.909 -1.176 l
+67.909 -0.823 67.839 -0.558 67.703 -0.382 c
+67.575 -0.205 67.358 -0.118 67.057 -0.118 c
+66.81 -0.118 66.608 -0.213 66.454 -0.396 c
+66.307 -0.584 66.219 -0.823 66.189 -1.117 c
+66.189 -4.453 l
+65.381 -4.453 l
+65.381 -1.146 l
+65.37 -0.463 65.091 -0.118 64.543 -0.118 c
+64.131 -0.118 63.841 -0.323 63.676 -0.735 c
+63.676 -4.453 l
+62.868 -4.453 l
+62.868 0.515 l
+h
+69.879 -5.026 m
+69.188 -5.026 l
+71.525 2.234 l
+72.216 2.234 l
+h
+76.596 -2.204 m
+76.596 -2.98 76.453 -3.564 76.17 -3.954 c
+75.894 -4.347 75.494 -4.542 74.965 -4.542 c
+74.443 -4.542 74.053 -4.317 73.789 -3.865 c
+73.745 -4.453 l
+73.01 -4.453 l
+73.01 2.602 l
+73.818 2.602 l
+73.818 -0.029 l
+74.083 0.389 74.465 0.603 74.965 0.603 c
+75.501 0.603 75.909 0.412 76.185 0.03 c
+76.457 -0.353 76.596 -0.937 76.596 -1.72 c
+h
+75.788 -1.749 m
+75.788 -1.161 75.703 -0.746 75.538 -0.5 c
+75.38 -0.246 75.115 -0.118 74.745 -0.118 c
+74.332 -0.118 74.024 -0.345 73.818 -0.793 c
+73.818 -3.16 l
+74.013 -3.601 74.325 -3.821 74.759 -3.821 c
+75.119 -3.821 75.38 -3.696 75.538 -3.439 c
+75.703 -3.185 75.788 -2.788 75.788 -2.248 c
+h
+77.299 -1.734 m
+77.299 -1.022 77.467 -0.452 77.812 -0.029 c
+78.165 0.389 78.629 0.603 79.209 0.603 c
+79.786 0.603 80.245 0.397 80.591 -0.014 c
+80.944 -0.419 81.124 -0.977 81.135 -1.69 c
+81.135 -2.204 l
+81.135 -2.932 80.958 -3.505 80.605 -3.924 c
+80.26 -4.336 79.801 -4.542 79.223 -4.542 c
+78.643 -4.542 78.18 -4.343 77.827 -3.939 c
+77.482 -3.528 77.305 -2.977 77.299 -2.278 c
+h
+78.107 -2.204 m
+78.107 -2.715 78.203 -3.116 78.4 -3.41 c
+78.606 -3.704 78.878 -3.85 79.223 -3.85 c
+79.936 -3.85 80.304 -3.333 80.326 -2.293 c
+80.326 -1.734 l
+80.326 -1.227 80.223 -0.823 80.017 -0.529 c
+79.819 -0.235 79.551 -0.087 79.209 -0.087 c
+78.874 -0.087 78.606 -0.235 78.4 -0.529 c
+78.203 -0.823 78.107 -1.227 78.107 -1.734 c
+h
+81.829 -1.734 m
+81.829 -1.022 81.999 -0.452 82.344 -0.029 c
+82.697 0.389 83.159 0.603 83.74 0.603 c
+84.317 0.603 84.777 0.397 85.122 -0.014 c
+85.475 -0.419 85.654 -0.977 85.666 -1.69 c
+85.666 -2.204 l
+85.666 -2.932 85.49 -3.505 85.137 -3.924 c
+84.791 -4.336 84.332 -4.542 83.755 -4.542 c
+83.174 -4.542 82.711 -4.343 82.358 -3.939 c
+82.013 -3.528 81.837 -2.977 81.829 -2.278 c
+h
+82.637 -2.204 m
+82.637 -2.715 82.733 -3.116 82.932 -3.41 c
+83.138 -3.704 83.41 -3.85 83.755 -3.85 c
+84.468 -3.85 84.835 -3.333 84.857 -2.293 c
+84.857 -1.734 l
+84.857 -1.227 84.754 -0.823 84.549 -0.529 c
+84.35 -0.235 84.082 -0.087 83.74 -0.087 c
+83.406 -0.087 83.138 -0.235 82.932 -0.529 c
+82.733 -0.823 82.637 -1.227 82.637 -1.734 c
+h
+87.775 -2.175 m
+87.349 -2.674 l
+87.349 -4.453 l
+86.54 -4.453 l
+86.54 2.602 l
+87.349 2.602 l
+87.349 -1.616 l
+88.892 0.515 l
+89.862 0.515 l
+88.289 -1.558 l
+90.082 -4.453 l
+89.128 -4.453 l
+h
+f
+Q
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 636.6425 292.5541 cm
+0 0 m
+0 0.147 0.044 0.268 0.132 0.367 c
+0.22 0.463 0.345 0.515 0.515 0.515 c
+0.691 0.515 0.823 0.463 0.911 0.367 c
+1.007 0.268 1.058 0.147 1.058 0 c
+1.058 -0.14 1.007 -0.258 0.911 -0.353 c
+0.823 -0.441 0.691 -0.485 0.515 -0.485 c
+0.345 -0.485 0.22 -0.441 0.132 -0.353 c
+0.044 -0.258 0 -0.14 0 0 c
+f
+Q
+0.113 0.082 0.09 0 k
+535.667 375.475 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 540.8555 368.6364 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.088 -0.5 c
+-0.147 -0.647 -0.242 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.243 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.358 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.302 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.683 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.141 1.278 -3.218 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.218 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.042 c
+-0.804 2.991 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.201 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.319 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.318 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.476 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.2 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.102 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+7.975 -0.25 m
+7.975 -0.42 7.934 -0.57 7.857 -0.706 c
+7.787 -0.834 7.684 -0.948 7.548 -1.044 c
+7.42 -1.132 7.258 -1.202 7.063 -1.249 c
+6.876 -1.297 6.659 -1.323 6.417 -1.323 c
+6.189 -1.323 5.991 -1.309 5.814 -1.279 c
+5.638 -1.249 5.48 -1.202 5.343 -1.132 c
+5.204 -1.055 5.093 -0.956 5.006 -0.838 c
+4.917 -0.721 4.848 -0.574 4.8 -0.397 c
+5.608 -0.279 l
+5.627 -0.379 5.656 -0.456 5.696 -0.515 c
+5.744 -0.574 5.802 -0.617 5.873 -0.647 c
+5.939 -0.676 6.02 -0.702 6.108 -0.721 c
+6.196 -0.732 6.299 -0.735 6.417 -0.735 c
+6.512 -0.735 6.608 -0.732 6.696 -0.721 c
+6.784 -0.702 6.861 -0.676 6.931 -0.647 c
+6.997 -0.617 7.049 -0.58 7.078 -0.53 c
+7.115 -0.482 7.136 -0.42 7.136 -0.339 c
+7.136 -0.243 7.107 -0.169 7.049 -0.118 c
+6.997 -0.07 6.931 -0.029 6.843 0 c
+6.755 0.037 6.644 0.066 6.519 0.088 c
+6.402 0.118 6.269 0.147 6.122 0.176 c
+5.983 0.213 5.843 0.253 5.696 0.293 c
+5.557 0.341 5.432 0.404 5.314 0.484 c
+5.204 0.562 5.116 0.661 5.05 0.779 c
+4.979 0.897 4.946 1.047 4.946 1.234 c
+4.946 1.389 4.976 1.532 5.035 1.66 c
+5.101 1.797 5.197 1.911 5.314 1.999 c
+5.439 2.087 5.597 2.153 5.785 2.205 c
+5.968 2.252 6.182 2.278 6.417 2.278 c
+6.6 2.278 6.776 2.256 6.945 2.219 c
+7.111 2.19 7.258 2.135 7.387 2.057 c
+7.512 1.988 7.622 1.889 7.71 1.764 c
+7.798 1.646 7.857 1.502 7.886 1.338 c
+7.092 1.264 l
+7.071 1.341 7.041 1.404 7.005 1.455 c
+6.964 1.514 6.916 1.558 6.857 1.587 c
+6.806 1.624 6.743 1.65 6.666 1.66 c
+6.585 1.668 6.504 1.675 6.417 1.675 c
+6.199 1.675 6.038 1.646 5.931 1.587 c
+5.821 1.536 5.77 1.448 5.77 1.323 c
+5.77 1.242 5.788 1.18 5.829 1.132 c
+5.876 1.08 5.939 1.043 6.02 1.014 c
+6.108 0.985 6.203 0.955 6.313 0.926 c
+6.421 0.904 6.541 0.881 6.681 0.852 c
+6.835 0.823 6.993 0.783 7.151 0.735 c
+7.306 0.683 7.445 0.621 7.563 0.544 c
+7.68 0.463 7.776 0.36 7.857 0.235 c
+7.934 0.106 7.975 -0.055 7.975 -0.25 c
+9.816 2.219 m
+9.816 0.264 l
+9.816 0.125 9.823 0 9.845 -0.118 c
+9.864 -0.228 9.897 -0.32 9.948 -0.397 c
+9.995 -0.478 10.055 -0.54 10.124 -0.588 c
+10.19 -0.628 10.275 -0.647 10.375 -0.647 c
+10.462 -0.647 10.543 -0.628 10.624 -0.588 c
+10.712 -0.54 10.786 -0.47 10.845 -0.382 c
+10.903 -0.287 10.947 -0.177 10.977 -0.059 c
+11.013 0.066 11.036 0.206 11.036 0.353 c
+11.036 2.219 l
+11.932 2.219 l
+11.932 -0.485 l
+11.932 -0.721 l
+11.94 -0.802 11.947 -0.879 11.947 -0.956 c
+11.947 -1.147 l
+11.954 -1.199 11.961 -1.235 11.961 -1.264 c
+11.109 -1.264 l
+11.098 -1.235 11.088 -1.199 11.08 -1.147 c
+11.08 -0.956 l
+11.08 -0.89 11.073 -0.819 11.065 -0.75 c
+11.065 -0.574 l
+11.05 -0.574 l
+10.932 -0.838 10.778 -1.029 10.595 -1.147 c
+10.419 -1.264 10.216 -1.323 9.992 -1.323 c
+9.787 -1.323 9.613 -1.286 9.478 -1.22 c
+9.338 -1.154 9.228 -1.058 9.139 -0.941 c
+9.058 -0.823 9 -0.688 8.963 -0.53 c
+8.933 -0.364 8.919 -0.187 8.919 0 c
+8.919 2.219 l
+h
+15.173 -1.264 m
+15.163 -1.246 15.152 -1.216 15.144 -1.176 c
+15.144 -1.128 15.136 -1.081 15.129 -1.029 c
+15.129 -0.97 15.122 -0.912 15.115 -0.852 c
+15.115 -0.691 l
+14.997 -0.927 14.853 -1.095 14.689 -1.191 c
+14.519 -1.279 14.321 -1.323 14.086 -1.323 c
+13.887 -1.323 13.711 -1.279 13.557 -1.191 c
+13.399 -1.103 13.266 -0.981 13.16 -0.823 c
+13.06 -0.658 12.983 -0.467 12.925 -0.25 c
+12.873 -0.037 12.851 0.206 12.851 0.47 c
+12.851 0.735 12.873 0.974 12.925 1.19 c
+12.983 1.415 13.06 1.606 13.16 1.764 c
+13.266 1.918 13.399 2.043 13.557 2.131 c
+13.722 2.227 13.913 2.278 14.13 2.278 c
+14.225 2.278 14.321 2.263 14.409 2.234 c
+14.504 2.212 14.6 2.179 14.689 2.131 c
+14.776 2.08 14.853 2.017 14.924 1.94 c
+15.001 1.859 15.063 1.768 15.115 1.66 c
+15.115 1.749 l
+15.115 1.896 l
+15.115 2.057 l
+15.115 2.234 l
+15.115 3.513 l
+16.011 3.513 l
+16.011 -0.5 l
+16.011 -0.676 16.015 -0.834 16.026 -0.97 c
+16.033 -1.099 16.041 -1.199 16.041 -1.264 c
+h
+15.129 0.484 m
+15.129 0.72 15.104 0.912 15.056 1.058 c
+15.015 1.213 14.961 1.338 14.894 1.425 c
+14.836 1.514 14.766 1.573 14.689 1.602 c
+14.608 1.639 14.531 1.66 14.454 1.66 c
+14.354 1.66 14.263 1.635 14.174 1.587 c
+14.093 1.547 14.027 1.477 13.968 1.382 c
+13.916 1.282 13.872 1.161 13.835 1.014 c
+13.806 0.867 13.792 0.683 13.792 0.47 c
+13.792 0.077 13.843 -0.217 13.953 -0.412 c
+14.071 -0.611 14.232 -0.706 14.438 -0.706 c
+14.516 -0.706 14.593 -0.688 14.674 -0.647 c
+14.751 -0.611 14.824 -0.544 14.894 -0.456 c
+14.961 -0.368 15.015 -0.246 15.056 -0.088 c
+15.104 0.066 15.129 0.257 15.129 0.484 c
+20.263 0.484 m
+20.263 0.209 20.226 -0.04 20.16 -0.264 c
+20.09 -0.482 19.987 -0.669 19.852 -0.823 c
+19.711 -0.981 19.535 -1.103 19.322 -1.191 c
+19.106 -1.279 18.852 -1.323 18.558 -1.323 c
+18.282 -1.323 18.036 -1.279 17.823 -1.191 c
+17.617 -1.103 17.444 -0.981 17.309 -0.823 c
+17.169 -0.669 17.066 -0.482 17 -0.264 c
+16.93 -0.04 16.897 0.209 16.897 0.484 c
+16.897 0.738 16.927 0.974 16.985 1.19 c
+17.051 1.415 17.154 1.606 17.294 1.764 c
+17.43 1.929 17.606 2.057 17.823 2.146 c
+18.036 2.234 18.294 2.278 18.587 2.278 c
+18.899 2.278 19.161 2.234 19.366 2.146 c
+19.58 2.057 19.752 1.929 19.881 1.764 c
+20.017 1.606 20.116 1.415 20.175 1.19 c
+20.233 0.974 20.263 0.738 20.263 0.484 c
+19.308 0.484 m
+19.308 0.691 19.293 0.867 19.263 1.014 c
+19.241 1.161 19.204 1.282 19.146 1.382 c
+19.087 1.477 19.013 1.547 18.926 1.587 c
+18.837 1.635 18.727 1.66 18.602 1.66 c
+18.338 1.66 18.146 1.562 18.028 1.367 c
+17.911 1.18 17.852 0.885 17.852 0.484 c
+17.852 0.062 17.911 -0.243 18.028 -0.426 c
+18.146 -0.614 18.323 -0.706 18.558 -0.706 c
+18.683 -0.706 18.797 -0.688 18.896 -0.647 c
+18.992 -0.599 19.073 -0.526 19.131 -0.426 c
+19.198 -0.331 19.241 -0.206 19.263 -0.059 c
+19.293 0.088 19.308 0.268 19.308 0.484 c
+26.136 -1.323 m
+25.966 -1.323 25.816 -1.301 25.679 -1.264 c
+25.551 -1.216 25.437 -1.147 25.342 -1.058 c
+25.253 -0.97 25.184 -0.864 25.136 -0.735 c
+25.084 -0.599 25.062 -0.449 25.062 -0.279 c
+25.062 -0.073 25.095 0.095 25.165 0.235 c
+25.232 0.382 25.327 0.492 25.444 0.573 c
+25.569 0.661 25.712 0.723 25.87 0.764 c
+26.036 0.801 26.213 0.827 26.4 0.837 c
+27.121 0.852 l
+27.121 1.029 l
+27.121 1.147 27.109 1.249 27.09 1.338 c
+27.069 1.425 27.036 1.492 26.988 1.543 c
+26.948 1.602 26.899 1.639 26.841 1.66 c
+26.782 1.679 26.716 1.69 26.65 1.69 c
+26.58 1.69 26.518 1.679 26.458 1.66 c
+26.407 1.65 26.36 1.624 26.312 1.587 c
+26.271 1.558 26.238 1.506 26.209 1.44 c
+26.186 1.382 26.172 1.301 26.165 1.205 c
+25.224 1.249 l
+25.253 1.396 25.297 1.532 25.356 1.66 c
+25.423 1.785 25.518 1.896 25.635 1.984 c
+25.753 2.08 25.893 2.153 26.061 2.205 c
+26.238 2.252 26.444 2.278 26.679 2.278 c
+27.121 2.278 27.451 2.167 27.678 1.955 c
+27.914 1.749 28.031 1.44 28.031 1.029 c
+28.031 -0.235 l
+28.031 -0.456 l
+28.039 -0.515 28.054 -0.57 28.075 -0.617 c
+28.094 -0.658 28.123 -0.691 28.164 -0.721 c
+28.201 -0.742 28.252 -0.75 28.311 -0.75 c
+28.376 -0.75 28.447 -0.746 28.517 -0.735 c
+28.517 -1.22 l
+28.457 -1.231 28.403 -1.243 28.355 -1.249 c
+28.314 -1.261 28.274 -1.268 28.237 -1.279 c
+28.197 -1.286 28.153 -1.294 28.105 -1.294 c
+28.054 -1.301 27.995 -1.309 27.929 -1.309 c
+27.701 -1.309 27.535 -1.257 27.429 -1.147 c
+27.318 -1.029 27.256 -0.864 27.237 -0.647 c
+27.223 -0.647 l
+27.154 -0.757 27.083 -0.852 27.017 -0.941 c
+26.948 -1.022 26.87 -1.087 26.782 -1.147 c
+26.693 -1.205 26.595 -1.249 26.488 -1.279 c
+26.389 -1.309 26.271 -1.323 26.136 -1.323 c
+27.121 0.353 m
+26.693 0.338 l
+26.595 0.338 26.503 0.33 26.415 0.324 c
+26.334 0.312 26.267 0.286 26.209 0.249 c
+26.15 0.209 26.099 0.151 26.061 0.073 c
+26.022 0.004 26.003 -0.088 26.003 -0.206 c
+26.003 -0.375 26.036 -0.497 26.106 -0.574 c
+26.172 -0.655 26.271 -0.691 26.4 -0.691 c
+26.506 -0.691 26.606 -0.669 26.693 -0.617 c
+26.789 -0.57 26.87 -0.507 26.929 -0.426 c
+26.995 -0.349 27.046 -0.262 27.076 -0.162 c
+27.106 -0.055 27.121 0.058 27.121 0.176 c
+h
+30.107 2.219 m
+30.115 2.198 30.122 2.165 30.122 2.117 c
+30.13 2.076 30.138 2.028 30.138 1.97 c
+30.144 1.918 30.152 1.866 30.152 1.808 c
+30.152 1.646 l
+30.167 1.646 l
+30.225 1.764 30.292 1.859 30.373 1.94 c
+30.45 2.017 30.534 2.08 30.622 2.131 c
+30.71 2.19 30.799 2.227 30.886 2.248 c
+30.982 2.267 31.081 2.278 31.181 2.278 c
+31.387 2.278 31.567 2.234 31.725 2.146 c
+31.879 2.057 32.008 1.929 32.106 1.764 c
+32.214 1.606 32.291 1.415 32.342 1.19 c
+32.401 0.974 32.43 0.738 32.43 0.484 c
+32.43 0.22 32.401 -0.026 32.342 -0.25 c
+32.291 -0.467 32.214 -0.658 32.106 -0.823 c
+32.008 -0.981 31.875 -1.103 31.709 -1.191 c
+31.551 -1.279 31.364 -1.323 31.152 -1.323 c
+31.052 -1.323 30.953 -1.312 30.857 -1.294 c
+30.758 -1.272 30.666 -1.243 30.578 -1.191 c
+30.497 -1.143 30.42 -1.081 30.343 -1 c
+30.273 -0.923 30.215 -0.831 30.167 -0.721 c
+30.152 -0.721 l
+30.152 -0.809 l
+30.159 -0.85 30.167 -0.897 30.167 -0.956 c
+30.167 -1.118 l
+30.167 -1.294 l
+30.167 -2.631 l
+29.255 -2.631 l
+29.255 1.455 l
+29.255 1.62 29.248 1.768 29.24 1.896 c
+29.24 2.219 l
+h
+30.152 0.455 m
+30.152 0.228 30.17 0.037 30.211 -0.118 c
+30.258 -0.264 30.313 -0.382 30.373 -0.47 c
+30.439 -0.559 30.512 -0.625 30.593 -0.661 c
+30.67 -0.702 30.747 -0.721 30.828 -0.721 c
+30.923 -0.721 31.011 -0.698 31.092 -0.647 c
+31.181 -0.599 31.247 -0.53 31.298 -0.441 c
+31.357 -0.345 31.401 -0.221 31.431 -0.073 c
+31.468 0.081 31.489 0.268 31.489 0.484 c
+31.489 0.874 31.431 1.168 31.313 1.367 c
+31.202 1.562 31.048 1.66 30.843 1.66 c
+30.762 1.66 30.685 1.639 30.608 1.602 c
+30.527 1.562 30.453 1.5 30.387 1.411 c
+30.317 1.323 30.258 1.198 30.211 1.043 c
+30.17 0.885 30.152 0.691 30.152 0.455 c
+33.907 1.602 m
+33.363 1.602 l
+33.363 2.219 l
+33.951 2.219 l
+34.231 3.116 l
+34.804 3.116 l
+34.804 2.219 l
+36.039 2.219 l
+36.039 1.602 l
+34.804 1.602 l
+34.804 -0.103 l
+34.804 -0.324 l
+34.811 -0.393 34.834 -0.456 34.863 -0.515 c
+34.9 -0.566 34.955 -0.611 35.025 -0.647 c
+35.102 -0.676 35.216 -0.691 35.362 -0.691 c
+35.499 -0.691 35.634 -0.688 35.774 -0.676 c
+35.91 -0.658 36.043 -0.632 36.171 -0.603 c
+36.171 -1.205 l
+36.091 -1.216 36.013 -1.231 35.936 -1.249 c
+35.855 -1.261 35.778 -1.268 35.701 -1.279 c
+35.62 -1.286 35.532 -1.294 35.436 -1.294 c
+35.348 -1.301 35.249 -1.309 35.142 -1.309 c
+34.955 -1.309 34.793 -1.294 34.657 -1.264 c
+34.528 -1.228 34.414 -1.183 34.319 -1.132 c
+34.231 -1.085 34.157 -1.025 34.098 -0.956 c
+34.04 -0.879 33.995 -0.802 33.966 -0.721 c
+33.937 -0.632 33.915 -0.544 33.907 -0.456 c
+33.897 -0.36 33.893 -0.264 33.893 -0.177 c
+h
+37.953 0.837 1.867 -0.793 re
+37.953 0.044 m
+42.955 -2.66 m
+42.738 -2.66 42.547 -2.635 42.381 -2.587 c
+42.212 -2.547 42.073 -2.484 41.955 -2.396 c
+41.837 -2.315 41.739 -2.22 41.662 -2.103 c
+41.591 -1.985 41.544 -1.856 41.514 -1.72 c
+42.41 -1.617 l
+42.447 -1.754 42.518 -1.86 42.616 -1.941 c
+42.712 -2.028 42.838 -2.072 42.984 -2.072 c
+43.073 -2.072 43.153 -2.058 43.234 -2.028 c
+43.311 -1.999 43.381 -1.945 43.44 -1.867 c
+43.499 -1.797 43.543 -1.706 43.572 -1.588 c
+43.609 -1.47 43.631 -1.323 43.631 -1.147 c
+43.631 -0.956 l
+43.631 -0.89 43.634 -0.831 43.646 -0.779 c
+43.646 -0.588 l
+43.631 -0.588 l
+43.532 -0.816 43.388 -0.977 43.204 -1.073 c
+43.017 -1.172 42.811 -1.22 42.587 -1.22 c
+42.381 -1.22 42.198 -1.183 42.044 -1.103 c
+41.897 -1.014 41.768 -0.897 41.662 -0.75 c
+41.562 -0.595 41.488 -0.412 41.44 -0.206 c
+41.389 0.008 41.367 0.243 41.367 0.5 c
+41.367 0.771 41.389 1.018 41.44 1.234 c
+41.5 1.448 41.581 1.631 41.691 1.778 c
+41.797 1.932 41.93 2.05 42.088 2.131 c
+42.242 2.219 42.429 2.263 42.646 2.263 c
+42.742 2.263 42.84 2.252 42.94 2.234 c
+43.035 2.212 43.123 2.179 43.204 2.131 c
+43.293 2.08 43.37 2.017 43.44 1.94 c
+43.517 1.859 43.58 1.768 43.631 1.66 c
+43.646 1.66 l
+43.646 1.808 l
+43.653 1.866 43.661 1.918 43.661 1.97 c
+43.667 2.028 43.675 2.076 43.675 2.117 c
+43.682 2.165 43.694 2.198 43.704 2.219 c
+44.557 2.219 l
+44.546 2.138 44.535 2.028 44.527 1.881 c
+44.527 1.411 l
+44.527 -1.162 l
+44.527 -1.415 44.49 -1.636 44.424 -1.823 c
+44.355 -2.007 44.251 -2.161 44.116 -2.278 c
+43.976 -2.404 43.811 -2.499 43.616 -2.558 c
+43.418 -2.624 43.197 -2.66 42.955 -2.66 c
+43.646 0.529 m
+43.646 0.742 43.62 0.918 43.572 1.058 c
+43.532 1.205 43.476 1.323 43.41 1.411 c
+43.351 1.5 43.282 1.558 43.204 1.587 c
+43.123 1.624 43.046 1.646 42.969 1.646 c
+42.87 1.646 42.778 1.62 42.69 1.573 c
+42.609 1.532 42.543 1.462 42.485 1.367 c
+42.433 1.278 42.389 1.161 42.352 1.014 c
+42.323 0.874 42.308 0.706 42.308 0.5 c
+42.308 0.125 42.367 -0.154 42.485 -0.339 c
+42.602 -0.515 42.763 -0.603 42.969 -0.603 c
+43.035 -0.603 43.109 -0.588 43.19 -0.559 c
+43.278 -0.522 43.351 -0.463 43.41 -0.382 c
+43.476 -0.294 43.532 -0.177 43.572 -0.029 c
+43.62 0.118 43.646 0.301 43.646 0.529 c
+47.044 -1.323 m
+46.788 -1.323 46.56 -1.286 46.354 -1.22 c
+46.148 -1.143 45.972 -1.029 45.824 -0.882 c
+45.678 -0.728 45.56 -0.537 45.472 -0.309 c
+45.391 -0.085 45.354 0.18 45.354 0.484 c
+45.354 0.816 45.398 1.095 45.487 1.323 c
+45.582 1.558 45.711 1.741 45.869 1.881 c
+46.034 2.017 46.221 2.117 46.427 2.175 c
+46.633 2.242 46.842 2.278 47.06 2.278 c
+47.331 2.278 47.567 2.227 47.765 2.131 c
+47.971 2.043 48.136 1.911 48.265 1.735 c
+48.401 1.565 48.5 1.359 48.559 1.117 c
+48.625 0.881 48.662 0.617 48.662 0.324 c
+48.662 0.309 l
+46.295 0.309 l
+46.295 0.162 46.31 0.022 46.339 -0.103 c
+46.376 -0.231 46.431 -0.345 46.501 -0.441 c
+46.567 -0.53 46.651 -0.599 46.751 -0.647 c
+46.846 -0.698 46.96 -0.721 47.089 -0.721 c
+47.243 -0.721 47.383 -0.688 47.501 -0.617 c
+47.625 -0.551 47.713 -0.449 47.765 -0.309 c
+48.602 -0.382 l
+48.573 -0.482 48.519 -0.588 48.441 -0.706 c
+48.361 -0.816 48.257 -0.919 48.132 -1.014 c
+48.014 -1.103 47.86 -1.176 47.677 -1.235 c
+47.501 -1.294 47.287 -1.323 47.044 -1.323 c
+47.044 1.705 m
+46.956 1.705 46.869 1.69 46.78 1.66 c
+46.692 1.631 46.611 1.579 46.545 1.514 c
+46.475 1.444 46.416 1.356 46.368 1.249 c
+46.328 1.139 46.31 1.014 46.31 0.867 c
+47.779 0.867 l
+47.779 1.003 47.754 1.124 47.706 1.234 c
+47.665 1.341 47.611 1.429 47.544 1.5 c
+47.486 1.565 47.412 1.617 47.324 1.646 c
+47.235 1.683 47.14 1.705 47.044 1.705 c
+50.15 1.602 m
+49.606 1.602 l
+49.606 2.219 l
+50.194 2.219 l
+50.474 3.116 l
+51.046 3.116 l
+51.046 2.219 l
+52.282 2.219 l
+52.282 1.602 l
+51.046 1.602 l
+51.046 -0.103 l
+51.046 -0.324 l
+51.054 -0.393 51.076 -0.456 51.106 -0.515 c
+51.142 -0.566 51.197 -0.611 51.267 -0.647 c
+51.345 -0.676 51.458 -0.691 51.605 -0.691 c
+51.741 -0.691 51.877 -0.688 52.016 -0.676 c
+52.153 -0.658 52.285 -0.632 52.413 -0.603 c
+52.413 -1.205 l
+52.332 -1.216 52.255 -1.231 52.178 -1.249 c
+52.097 -1.261 52.02 -1.268 51.943 -1.279 c
+51.862 -1.286 51.775 -1.294 51.679 -1.294 c
+51.59 -1.301 51.491 -1.309 51.384 -1.309 c
+51.197 -1.309 51.035 -1.294 50.9 -1.264 c
+50.771 -1.228 50.657 -1.183 50.561 -1.132 c
+50.474 -1.085 50.4 -1.025 50.341 -0.956 c
+50.283 -0.879 50.238 -0.802 50.208 -0.721 c
+50.179 -0.632 50.157 -0.544 50.15 -0.456 c
+50.139 -0.36 50.135 -0.264 50.135 -0.177 c
+h
+59.774 -0.647 m
+60.906 -0.647 l
+60.906 -1.264 l
+57.599 -1.264 l
+57.599 -0.647 l
+58.863 -0.647 l
+58.863 1.602 l
+57.936 1.602 l
+57.936 2.219 l
+59.774 2.219 l
+h
+58.863 3.513 0.911 -0.676 re
+58.863 2.836 m
+63.923 -1.264 m
+63.923 0.72 l
+63.923 1.022 63.879 1.242 63.791 1.382 c
+63.71 1.529 63.574 1.602 63.379 1.602 c
+63.268 1.602 63.166 1.577 63.07 1.529 c
+62.983 1.477 62.902 1.411 62.835 1.323 c
+62.776 1.234 62.725 1.124 62.688 0.999 c
+62.659 0.881 62.644 0.75 62.644 0.602 c
+62.644 -1.264 l
+61.732 -1.264 l
+61.732 1.44 l
+61.732 1.66 l
+61.732 1.749 61.726 1.826 61.718 1.896 c
+61.718 2.087 l
+61.718 2.219 l
+62.57 2.219 l
+62.578 2.19 62.586 2.146 62.586 2.087 c
+62.586 1.896 l
+62.593 1.826 62.6 1.756 62.6 1.69 c
+62.607 1.62 62.615 1.565 62.615 1.529 c
+62.63 1.529 l
+62.747 1.793 62.898 1.984 63.085 2.102 c
+63.268 2.219 63.49 2.278 63.746 2.278 c
+63.93 2.278 64.092 2.248 64.232 2.19 c
+64.367 2.131 64.481 2.043 64.57 1.926 c
+64.658 1.808 64.72 1.664 64.76 1.5 c
+64.808 1.341 64.834 1.153 64.834 0.941 c
+64.834 -1.264 l
+h
+68.895 -0.25 m
+68.895 -0.42 68.855 -0.57 68.777 -0.706 c
+68.708 -0.834 68.604 -0.948 68.469 -1.044 c
+68.34 -1.132 68.178 -1.202 67.983 -1.249 c
+67.796 -1.297 67.579 -1.323 67.337 -1.323 c
+67.109 -1.323 66.91 -1.309 66.734 -1.279 c
+66.557 -1.249 66.399 -1.202 66.264 -1.132 c
+66.124 -1.055 66.014 -0.956 65.926 -0.838 c
+65.838 -0.721 65.768 -0.574 65.72 -0.397 c
+66.528 -0.279 l
+66.547 -0.379 66.576 -0.456 66.617 -0.515 c
+66.665 -0.574 66.723 -0.617 66.792 -0.647 c
+66.859 -0.676 66.94 -0.702 67.028 -0.721 c
+67.116 -0.732 67.219 -0.735 67.337 -0.735 c
+67.432 -0.735 67.528 -0.732 67.616 -0.721 c
+67.704 -0.702 67.781 -0.676 67.851 -0.647 c
+67.918 -0.617 67.968 -0.58 67.999 -0.53 c
+68.035 -0.482 68.057 -0.42 68.057 -0.339 c
+68.057 -0.243 68.028 -0.169 67.968 -0.118 c
+67.918 -0.07 67.851 -0.029 67.763 0 c
+67.675 0.037 67.565 0.066 67.44 0.088 c
+67.322 0.118 67.19 0.147 67.043 0.176 c
+66.904 0.213 66.763 0.253 66.617 0.293 c
+66.476 0.341 66.352 0.404 66.235 0.484 c
+66.124 0.562 66.036 0.661 65.969 0.779 c
+65.9 0.897 65.867 1.047 65.867 1.234 c
+65.867 1.389 65.896 1.532 65.955 1.66 c
+66.021 1.797 66.117 1.911 66.235 1.999 c
+66.359 2.087 66.517 2.153 66.705 2.205 c
+66.888 2.252 67.102 2.278 67.337 2.278 c
+67.521 2.278 67.697 2.256 67.866 2.219 c
+68.031 2.19 68.178 2.135 68.307 2.057 c
+68.432 1.988 68.542 1.889 68.63 1.764 c
+68.718 1.646 68.777 1.502 68.807 1.338 c
+68.013 1.264 l
+67.991 1.341 67.962 1.404 67.924 1.455 c
+67.885 1.514 67.837 1.558 67.777 1.587 c
+67.727 1.624 67.664 1.65 67.586 1.66 c
+67.506 1.668 67.425 1.675 67.337 1.675 c
+67.12 1.675 66.958 1.646 66.852 1.587 c
+66.742 1.536 66.69 1.448 66.69 1.323 c
+66.69 1.242 66.709 1.18 66.748 1.132 c
+66.796 1.08 66.859 1.043 66.94 1.014 c
+67.028 0.985 67.124 0.955 67.234 0.926 c
+67.34 0.904 67.461 0.881 67.602 0.852 c
+67.756 0.823 67.914 0.783 68.072 0.735 c
+68.226 0.683 68.365 0.621 68.483 0.544 c
+68.601 0.463 68.697 0.36 68.777 0.235 c
+68.855 0.106 68.895 -0.055 68.895 -0.25 c
+70.457 1.602 m
+69.913 1.602 l
+69.913 2.219 l
+70.501 2.219 l
+70.78 3.116 l
+71.353 3.116 l
+71.353 2.219 l
+72.588 2.219 l
+72.588 1.602 l
+71.353 1.602 l
+71.353 -0.103 l
+71.353 -0.324 l
+71.361 -0.393 71.382 -0.456 71.412 -0.515 c
+71.449 -0.566 71.504 -0.611 71.573 -0.647 c
+71.651 -0.676 71.765 -0.691 71.912 -0.691 c
+72.047 -0.691 72.184 -0.688 72.323 -0.676 c
+72.46 -0.658 72.591 -0.632 72.72 -0.603 c
+72.72 -1.205 l
+72.639 -1.216 72.562 -1.231 72.485 -1.249 c
+72.404 -1.261 72.327 -1.268 72.25 -1.279 c
+72.169 -1.286 72.08 -1.294 71.985 -1.294 c
+71.897 -1.301 71.798 -1.309 71.691 -1.309 c
+71.504 -1.309 71.342 -1.294 71.206 -1.264 c
+71.078 -1.228 70.964 -1.183 70.868 -1.132 c
+70.78 -1.085 70.707 -1.025 70.648 -0.956 c
+70.589 -0.879 70.545 -0.802 70.515 -0.721 c
+70.486 -0.632 70.464 -0.544 70.457 -0.456 c
+70.445 -0.36 70.442 -0.264 70.442 -0.177 c
+h
+74.873 -1.323 m
+74.705 -1.323 74.554 -1.301 74.418 -1.264 c
+74.289 -1.216 74.175 -1.147 74.079 -1.058 c
+73.992 -0.97 73.922 -0.864 73.874 -0.735 c
+73.823 -0.599 73.801 -0.449 73.801 -0.279 c
+73.801 -0.073 73.834 0.095 73.904 0.235 c
+73.969 0.382 74.065 0.492 74.183 0.573 c
+74.308 0.661 74.451 0.723 74.609 0.764 c
+74.775 0.801 74.951 0.827 75.138 0.837 c
+75.858 0.852 l
+75.858 1.029 l
+75.858 1.147 75.847 1.249 75.829 1.338 c
+75.807 1.425 75.774 1.492 75.726 1.543 c
+75.685 1.602 75.638 1.639 75.579 1.66 c
+75.521 1.679 75.454 1.69 75.388 1.69 c
+75.318 1.69 75.255 1.679 75.197 1.66 c
+75.145 1.65 75.098 1.624 75.05 1.587 c
+75.01 1.558 74.977 1.506 74.947 1.44 c
+74.925 1.382 74.91 1.301 74.903 1.205 c
+73.962 1.249 l
+73.992 1.396 74.036 1.532 74.094 1.66 c
+74.16 1.785 74.256 1.896 74.374 1.984 c
+74.492 2.08 74.631 2.153 74.8 2.205 c
+74.977 2.252 75.182 2.278 75.417 2.278 c
+75.858 2.278 76.19 2.167 76.417 1.955 c
+76.652 1.749 76.77 1.44 76.77 1.029 c
+76.77 -0.235 l
+76.77 -0.456 l
+76.778 -0.515 76.792 -0.57 76.814 -0.617 c
+76.832 -0.658 76.861 -0.691 76.902 -0.721 c
+76.939 -0.742 76.99 -0.75 77.049 -0.75 c
+77.115 -0.75 77.185 -0.746 77.254 -0.735 c
+77.254 -1.22 l
+77.196 -1.231 77.141 -1.243 77.094 -1.249 c
+77.053 -1.261 77.013 -1.268 76.976 -1.279 c
+76.936 -1.286 76.891 -1.294 76.843 -1.294 c
+76.792 -1.301 76.733 -1.309 76.667 -1.309 c
+76.439 -1.309 76.274 -1.257 76.167 -1.147 c
+76.057 -1.029 75.995 -0.864 75.976 -0.647 c
+75.961 -0.647 l
+75.891 -0.757 75.822 -0.852 75.756 -0.941 c
+75.685 -1.022 75.608 -1.087 75.521 -1.147 c
+75.432 -1.205 75.333 -1.249 75.226 -1.279 c
+75.127 -1.309 75.01 -1.323 74.873 -1.323 c
+75.858 0.353 m
+75.432 0.338 l
+75.333 0.338 75.241 0.33 75.153 0.324 c
+75.072 0.312 75.006 0.286 74.947 0.249 c
+74.889 0.209 74.837 0.151 74.8 0.073 c
+74.76 0.004 74.742 -0.088 74.742 -0.206 c
+74.742 -0.375 74.775 -0.497 74.844 -0.574 c
+74.91 -0.655 75.01 -0.691 75.138 -0.691 c
+75.245 -0.691 75.344 -0.669 75.432 -0.617 c
+75.528 -0.57 75.608 -0.507 75.667 -0.426 c
+75.733 -0.349 75.785 -0.262 75.814 -0.162 c
+75.843 -0.055 75.858 0.058 75.858 0.176 c
+h
+80.08 -0.647 m
+81.213 -0.647 l
+81.213 -1.264 l
+77.905 -1.264 l
+77.905 -0.647 l
+79.17 -0.647 l
+79.17 2.896 l
+78.243 2.896 l
+78.243 3.513 l
+80.08 3.513 l
+h
+84.142 -0.647 m
+85.273 -0.647 l
+85.273 -1.264 l
+81.966 -1.264 l
+81.966 -0.647 l
+83.23 -0.647 l
+83.23 2.896 l
+82.304 2.896 l
+82.304 3.513 l
+84.142 3.513 l
+h
+91.689 -2.66 m
+91.473 -2.66 91.282 -2.635 91.116 -2.587 c
+90.947 -2.547 90.808 -2.484 90.69 -2.396 c
+90.573 -2.315 90.473 -2.22 90.396 -2.103 c
+90.326 -1.985 90.278 -1.856 90.249 -1.72 c
+91.145 -1.617 l
+91.182 -1.754 91.252 -1.86 91.351 -1.941 c
+91.446 -2.028 91.572 -2.072 91.718 -2.072 c
+91.807 -2.072 91.888 -2.058 91.969 -2.028 c
+92.046 -1.999 92.115 -1.945 92.175 -1.867 c
+92.233 -1.797 92.277 -1.706 92.306 -1.588 c
+92.344 -1.47 92.366 -1.323 92.366 -1.147 c
+92.366 -0.956 l
+92.366 -0.89 92.369 -0.831 92.381 -0.779 c
+92.381 -0.588 l
+92.366 -0.588 l
+92.267 -0.816 92.123 -0.977 91.939 -1.073 c
+91.752 -1.172 91.546 -1.22 91.322 -1.22 c
+91.116 -1.22 90.933 -1.183 90.778 -1.103 c
+90.631 -1.014 90.502 -0.897 90.396 -0.75 c
+90.297 -0.595 90.224 -0.412 90.176 -0.206 c
+90.124 0.008 90.102 0.243 90.102 0.5 c
+90.102 0.771 90.124 1.018 90.176 1.234 c
+90.234 1.448 90.315 1.631 90.425 1.778 c
+90.532 1.932 90.664 2.05 90.822 2.131 c
+90.976 2.219 91.164 2.263 91.381 2.263 c
+91.477 2.263 91.575 2.252 91.675 2.234 c
+91.77 2.212 91.859 2.179 91.939 2.131 c
+92.028 2.08 92.105 2.017 92.175 1.94 c
+92.252 1.859 92.314 1.768 92.366 1.66 c
+92.381 1.66 l
+92.381 1.808 l
+92.387 1.866 92.395 1.918 92.395 1.97 c
+92.402 2.028 92.41 2.076 92.41 2.117 c
+92.417 2.165 92.428 2.198 92.439 2.219 c
+93.291 2.219 l
+93.281 2.138 93.27 2.028 93.262 1.881 c
+93.262 1.411 l
+93.262 -1.162 l
+93.262 -1.415 93.225 -1.636 93.16 -1.823 c
+93.089 -2.007 92.986 -2.161 92.851 -2.278 c
+92.711 -2.404 92.545 -2.499 92.35 -2.558 c
+92.152 -2.624 91.932 -2.66 91.689 -2.66 c
+92.381 0.529 m
+92.381 0.742 92.354 0.918 92.306 1.058 c
+92.267 1.205 92.211 1.323 92.145 1.411 c
+92.086 1.5 92.017 1.558 91.939 1.587 c
+91.859 1.624 91.781 1.646 91.704 1.646 c
+91.604 1.646 91.513 1.62 91.425 1.573 c
+91.344 1.532 91.278 1.462 91.219 1.367 c
+91.168 1.278 91.124 1.161 91.087 1.014 c
+91.057 0.874 91.043 0.706 91.043 0.5 c
+91.043 0.125 91.101 -0.154 91.219 -0.339 c
+91.336 -0.515 91.498 -0.603 91.704 -0.603 c
+91.77 -0.603 91.843 -0.588 91.924 -0.559 c
+92.013 -0.522 92.086 -0.463 92.145 -0.382 c
+92.211 -0.294 92.267 -0.177 92.306 -0.029 c
+92.354 0.118 92.381 0.301 92.381 0.529 c
+96.327 -0.647 m
+97.459 -0.647 l
+97.459 -1.264 l
+94.151 -1.264 l
+94.151 -0.647 l
+95.415 -0.647 l
+95.415 1.602 l
+94.49 1.602 l
+94.49 2.219 l
+96.327 2.219 l
+h
+95.415 3.513 0.912 -0.676 re
+95.415 2.836 m
+98.889 1.602 m
+98.344 1.602 l
+98.344 2.219 l
+98.932 2.219 l
+99.211 3.116 l
+99.785 3.116 l
+99.785 2.219 l
+101.019 2.219 l
+101.019 1.602 l
+99.785 1.602 l
+99.785 -0.103 l
+99.785 -0.324 l
+99.793 -0.393 99.814 -0.456 99.844 -0.515 c
+99.88 -0.566 99.936 -0.611 100.005 -0.647 c
+100.082 -0.676 100.196 -0.691 100.343 -0.691 c
+100.479 -0.691 100.615 -0.688 100.755 -0.676 c
+100.891 -0.658 101.023 -0.632 101.152 -0.603 c
+101.152 -1.205 l
+101.071 -1.216 100.994 -1.231 100.917 -1.249 c
+100.836 -1.261 100.759 -1.268 100.682 -1.279 c
+100.601 -1.286 100.512 -1.294 100.417 -1.294 c
+100.329 -1.301 100.229 -1.309 100.123 -1.309 c
+99.936 -1.309 99.774 -1.294 99.637 -1.264 c
+99.51 -1.228 99.396 -1.183 99.3 -1.132 c
+99.211 -1.085 99.138 -1.025 99.08 -0.956 c
+99.02 -0.879 98.976 -0.802 98.947 -0.721 c
+98.918 -0.632 98.895 -0.544 98.889 -0.456 c
+98.877 -0.36 98.874 -0.264 98.874 -0.177 c
+h
+f
+Q
+0.793 0.801 0.129 0.016 k
+35.668 201.199 238.665 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 164.9238 185.7106 cm
+0 0 m
+0 9.363 l
+2.896 9.363 l
+3.873 9.363 4.619 9.142 5.13 8.702 c
+5.637 8.268 5.895 7.632 5.895 6.79 c
+5.895 6.32 5.785 5.913 5.571 5.57 c
+5.365 5.225 5.075 4.976 4.704 4.821 c
+5.122 4.692 5.446 4.446 5.674 4.086 c
+5.909 3.733 6.026 3.285 6.026 2.748 c
+6.026 1.844 5.777 1.161 5.277 0.691 c
+4.777 0.228 4.05 0 3.102 0 c
+h
+1.897 4.072 m
+1.897 1.572 l
+3.102 1.572 l
+3.443 1.572 3.705 1.675 3.881 1.881 c
+4.057 2.094 4.145 2.395 4.145 2.778 c
+4.145 3.619 3.836 4.049 3.219 4.072 c
+h
+1.897 5.453 m
+2.882 5.453 l
+3.616 5.453 3.983 5.835 3.983 6.599 c
+3.983 7.018 3.896 7.324 3.719 7.511 c
+3.55 7.694 3.278 7.79 2.896 7.79 c
+1.897 7.79 l
+h
+7.655 0.926 m
+7.655 1.219 7.75 1.458 7.948 1.646 c
+8.143 1.83 8.397 1.925 8.713 1.925 c
+9.014 1.925 9.264 1.83 9.463 1.646 c
+9.668 1.458 9.771 1.219 9.771 0.926 c
+9.771 0.621 9.668 0.374 9.463 0.191 c
+9.264 0.014 9.014 -0.074 8.713 -0.074 c
+8.408 -0.074 8.154 0.018 7.948 0.205 c
+7.75 0.389 7.655 0.631 7.655 0.926 c
+16.644 0 -1.897 9.363 re
+24.702 1.058 m
+24.338 0.665 23.894 0.367 23.364 0.161 c
+22.836 -0.034 22.254 -0.133 21.629 -0.133 c
+20.55 -0.133 19.711 0.198 19.116 0.866 c
+18.518 1.532 18.212 2.502 18.205 3.777 c
+18.205 5.468 l
+18.205 6.761 18.485 7.754 19.042 8.452 c
+19.609 9.146 20.432 9.495 21.512 9.495 c
+22.53 9.495 23.295 9.238 23.805 8.731 c
+24.324 8.231 24.621 7.445 24.702 6.379 c
+22.865 6.379 l
+22.813 6.975 22.692 7.382 22.497 7.599 c
+22.298 7.812 21.99 7.922 21.571 7.922 c
+21.06 7.922 20.689 7.735 20.454 7.364 c
+20.226 6.989 20.108 6.397 20.101 5.585 c
+20.101 3.881 l
+20.101 3.027 20.226 2.403 20.484 2.013 c
+20.737 1.631 21.152 1.44 21.733 1.44 c
+22.104 1.44 22.408 1.514 22.644 1.66 c
+22.805 1.778 l
+22.805 3.498 l
+21.483 3.498 l
+21.483 4.924 l
+24.702 4.924 l
+h
+32.831 0 m
+30.934 0 l
+28.17 6.144 l
+28.17 0 l
+26.275 0 l
+26.275 9.363 l
+28.17 9.363 l
+30.934 3.218 l
+30.934 9.363 l
+32.831 9.363 l
+h
+41.183 3.836 m
+41.183 2.58 40.882 1.606 40.286 0.911 c
+39.688 0.213 38.865 -0.133 37.817 -0.133 c
+36.766 -0.133 35.939 0.209 35.333 0.897 c
+34.734 1.591 34.429 2.557 34.422 3.792 c
+34.422 5.394 l
+34.422 6.676 34.719 7.68 35.318 8.407 c
+35.914 9.132 36.744 9.495 37.803 9.495 c
+38.838 9.495 39.658 9.135 40.257 8.422 c
+40.864 7.717 41.172 6.721 41.183 5.438 c
+h
+39.287 5.409 m
+39.287 6.25 39.162 6.879 38.919 7.291 c
+38.684 7.702 38.31 7.908 37.803 7.908 c
+37.302 7.908 36.928 7.706 36.685 7.305 c
+36.45 6.912 36.325 6.312 36.318 5.512 c
+36.318 3.836 l
+36.318 3.02 36.439 2.418 36.685 2.028 c
+36.928 1.635 37.306 1.44 37.817 1.44 c
+38.306 1.44 38.67 1.631 38.905 2.013 c
+39.147 2.395 39.276 2.983 39.287 3.777 c
+h
+45.593 3.424 m
+44.652 3.424 l
+44.652 0 l
+42.771 0 l
+42.771 9.363 l
+45.784 9.363 l
+46.732 9.363 47.463 9.117 47.974 8.628 c
+48.492 8.136 48.753 7.441 48.753 6.541 c
+48.753 5.294 48.301 4.424 47.401 3.924 c
+49.032 0.087 l
+49.032 0 l
+47.004 0 l
+h
+44.652 4.997 m
+45.726 4.997 l
+46.108 4.997 46.391 5.119 46.578 5.365 c
+46.761 5.618 46.857 5.957 46.857 6.379 c
+46.857 7.32 46.493 7.79 45.769 7.79 c
+44.652 7.79 l
+h
+52.255 0 -1.897 9.363 re
+60.505 0 m
+58.609 0 l
+55.845 6.144 l
+55.845 0 l
+53.95 0 l
+53.95 9.363 l
+55.845 9.363 l
+58.609 3.218 l
+58.609 9.363 l
+60.505 9.363 l
+h
+68.622 1.058 m
+68.259 0.665 67.814 0.367 67.285 0.161 c
+66.756 -0.034 66.175 -0.133 65.551 -0.133 c
+64.47 -0.133 63.633 0.198 63.037 0.866 c
+62.438 1.532 62.133 2.502 62.125 3.777 c
+62.125 5.468 l
+62.125 6.761 62.405 7.754 62.964 8.452 c
+63.529 9.146 64.352 9.495 65.433 9.495 c
+66.451 9.495 67.215 9.238 67.726 8.731 c
+68.244 8.231 68.541 7.445 68.622 6.379 c
+66.785 6.379 l
+66.734 6.975 66.613 7.382 66.418 7.599 c
+66.22 7.812 65.911 7.922 65.491 7.922 c
+64.981 7.922 64.61 7.735 64.375 7.364 c
+64.147 6.989 64.03 6.397 64.022 5.585 c
+64.022 3.881 l
+64.022 3.027 64.147 2.403 64.404 2.013 c
+64.658 1.631 65.073 1.44 65.653 1.44 c
+66.025 1.44 66.33 1.514 66.565 1.66 c
+66.727 1.778 l
+66.727 3.498 l
+65.404 3.498 l
+65.404 4.924 l
+68.622 4.924 l
+h
+78.151 3.821 m
+75.211 3.821 l
+75.211 0 l
+73.316 0 l
+73.316 9.363 l
+78.504 9.363 l
+78.504 7.79 l
+75.211 7.79 l
+75.211 5.394 l
+78.151 5.394 l
+h
+81.756 0 -1.896 9.363 re
+85.347 1.572 m
+88.668 1.572 l
+88.668 0 l
+83.45 0 l
+83.45 9.363 l
+85.347 9.363 l
+h
+94.768 4.056 m
+91.828 4.056 l
+91.828 1.572 l
+95.312 1.572 l
+95.312 0 l
+89.933 0 l
+89.933 9.363 l
+95.298 9.363 l
+95.298 7.79 l
+91.828 7.79 l
+91.828 5.57 l
+94.768 5.57 l
+h
+100.526 2.454 m
+100.526 2.836 100.428 3.123 100.233 3.322 c
+100.034 3.516 99.682 3.719 99.175 3.924 c
+98.234 4.284 97.558 4.704 97.146 5.174 c
+96.734 5.651 96.528 6.221 96.528 6.879 c
+96.528 7.661 96.808 8.294 97.367 8.775 c
+97.925 9.252 98.635 9.495 99.498 9.495 c
+100.075 9.495 100.589 9.37 101.041 9.128 c
+101.49 8.882 101.835 8.54 102.07 8.099 c
+102.313 7.658 102.438 7.158 102.438 6.599 c
+100.557 6.599 l
+100.557 7.04 100.461 7.371 100.277 7.599 c
+100.09 7.823 99.821 7.937 99.468 7.937 c
+99.134 7.937 98.874 7.838 98.689 7.644 c
+98.513 7.455 98.425 7.195 98.425 6.864 c
+98.425 6.607 98.527 6.372 98.733 6.158 c
+98.939 5.953 99.3 5.736 99.821 5.512 c
+100.732 5.188 101.394 4.784 101.806 4.307 c
+102.225 3.825 102.438 3.212 102.438 2.469 c
+102.438 1.653 102.177 1.018 101.659 0.558 c
+101.137 0.095 100.431 -0.133 99.542 -0.133 c
+98.932 -0.133 98.381 -0.008 97.881 0.235 c
+97.389 0.488 97.002 0.845 96.72 1.308 c
+96.433 1.778 96.293 2.326 96.293 2.954 c
+98.19 2.954 l
+98.19 2.414 98.292 2.021 98.498 1.778 c
+98.712 1.532 99.061 1.411 99.542 1.411 c
+100.196 1.411 100.526 1.756 100.526 2.454 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 173.911 238.665 -49.165 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 167.0717 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.278 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.431 -1.323 m
+6.145 -1.323 5.902 -1.282 5.696 -1.205 c
+5.49 -1.117 5.318 -0.996 5.182 -0.838 c
+5.042 -0.684 4.939 -0.496 4.873 -0.279 c
+4.803 -0.055 4.77 0.191 4.77 0.455 c
+4.77 0.75 4.803 1.007 4.873 1.235 c
+4.95 1.459 5.056 1.646 5.197 1.793 c
+5.343 1.947 5.519 2.065 5.725 2.146 c
+5.931 2.234 6.167 2.278 6.431 2.278 c
+6.656 2.278 6.857 2.249 7.034 2.19 c
+7.21 2.132 7.361 2.047 7.489 1.941 c
+7.614 1.841 7.717 1.72 7.798 1.573 c
+7.875 1.433 7.93 1.282 7.96 1.118 c
+7.048 1.073 l
+7.019 1.249 6.949 1.389 6.843 1.5 c
+6.743 1.606 6.6 1.661 6.417 1.661 c
+6.17 1.661 5.993 1.558 5.887 1.353 c
+5.777 1.154 5.725 0.867 5.725 0.485 c
+5.725 -0.309 5.961 -0.706 6.431 -0.706 c
+6.596 -0.706 6.739 -0.654 6.857 -0.544 c
+6.975 -0.437 7.048 -0.276 7.078 -0.058 c
+7.989 -0.103 l
+7.96 -0.272 7.905 -0.426 7.828 -0.573 c
+7.757 -0.721 7.655 -0.852 7.518 -0.97 c
+7.39 -1.08 7.232 -1.168 7.048 -1.234 c
+6.872 -1.294 6.666 -1.323 6.431 -1.323 c
+9.889 -1.323 m
+9.72 -1.323 9.569 -1.301 9.434 -1.264 c
+9.305 -1.216 9.191 -1.147 9.095 -1.058 c
+9.008 -0.97 8.937 -0.864 8.889 -0.735 c
+8.838 -0.599 8.816 -0.449 8.816 -0.279 c
+8.816 -0.073 8.85 0.096 8.919 0.235 c
+8.985 0.382 9.081 0.492 9.199 0.573 c
+9.323 0.661 9.467 0.724 9.625 0.765 c
+9.79 0.802 9.966 0.827 10.153 0.838 c
+10.874 0.852 l
+10.874 1.029 l
+10.874 1.147 10.863 1.249 10.845 1.338 c
+10.822 1.426 10.789 1.492 10.741 1.544 c
+10.701 1.602 10.654 1.639 10.595 1.661 c
+10.535 1.679 10.469 1.691 10.404 1.691 c
+10.334 1.691 10.271 1.679 10.213 1.661 c
+10.161 1.65 10.113 1.625 10.065 1.588 c
+10.025 1.558 9.992 1.507 9.962 1.44 c
+9.941 1.382 9.926 1.301 9.918 1.205 c
+8.977 1.249 l
+9.008 1.396 9.051 1.532 9.11 1.661 c
+9.176 1.786 9.272 1.897 9.39 1.984 c
+9.507 2.08 9.646 2.153 9.816 2.205 c
+9.992 2.253 10.198 2.278 10.433 2.278 c
+10.874 2.278 11.204 2.168 11.433 1.955 c
+11.668 1.749 11.786 1.44 11.786 1.029 c
+11.786 -0.235 l
+11.786 -0.455 l
+11.792 -0.515 11.807 -0.569 11.83 -0.617 c
+11.848 -0.658 11.877 -0.69 11.917 -0.721 c
+11.954 -0.742 12.006 -0.75 12.065 -0.75 c
+12.131 -0.75 12.2 -0.746 12.27 -0.735 c
+12.27 -1.22 l
+12.212 -1.231 12.156 -1.242 12.108 -1.249 c
+12.068 -1.261 12.027 -1.268 11.991 -1.278 c
+11.95 -1.286 11.907 -1.294 11.859 -1.294 c
+11.807 -1.301 11.749 -1.309 11.682 -1.309 c
+11.454 -1.309 11.289 -1.257 11.183 -1.147 c
+11.072 -1.029 11.009 -0.864 10.992 -0.646 c
+10.977 -0.646 l
+10.907 -0.757 10.837 -0.852 10.771 -0.941 c
+10.701 -1.022 10.624 -1.087 10.535 -1.147 c
+10.448 -1.205 10.348 -1.249 10.242 -1.278 c
+10.143 -1.309 10.025 -1.323 9.889 -1.323 c
+10.874 0.353 m
+10.448 0.338 l
+10.348 0.338 10.257 0.331 10.168 0.324 c
+10.088 0.312 10.022 0.287 9.962 0.25 c
+9.904 0.21 9.852 0.151 9.816 0.073 c
+9.775 0.004 9.756 -0.088 9.756 -0.206 c
+9.756 -0.374 9.79 -0.496 9.86 -0.573 c
+9.926 -0.654 10.025 -0.69 10.153 -0.69 c
+10.261 -0.69 10.359 -0.669 10.448 -0.617 c
+10.543 -0.569 10.624 -0.507 10.683 -0.426 c
+10.749 -0.349 10.801 -0.261 10.83 -0.162 c
+10.859 -0.055 10.874 0.059 10.874 0.177 c
+h
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.566 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.69 15.056 -0.69 c
+15.192 -0.69 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.603 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.309 14.835 -1.309 c
+14.648 -1.309 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.956 c
+13.733 -0.879 13.689 -0.801 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+22.178 -0.279 0.926 -0.985 re
+22.178 -1.264 m
+26.709 -2.66 m
+26.491 -2.66 26.3 -2.635 26.136 -2.587 c
+25.966 -2.547 25.826 -2.484 25.709 -2.396 c
+25.591 -2.315 25.492 -2.219 25.415 -2.102 c
+25.345 -1.984 25.297 -1.856 25.268 -1.72 c
+26.165 -1.617 l
+26.201 -1.753 26.271 -1.86 26.371 -1.94 c
+26.466 -2.028 26.591 -2.072 26.738 -2.072 c
+26.826 -2.072 26.907 -2.057 26.988 -2.028 c
+27.065 -1.999 27.135 -1.944 27.194 -1.866 c
+27.252 -1.797 27.296 -1.705 27.326 -1.587 c
+27.362 -1.469 27.385 -1.323 27.385 -1.147 c
+27.385 -0.956 l
+27.385 -0.889 27.389 -0.831 27.399 -0.779 c
+27.399 -0.588 l
+27.385 -0.588 l
+27.285 -0.816 27.142 -0.977 26.959 -1.073 c
+26.771 -1.172 26.566 -1.22 26.341 -1.22 c
+26.136 -1.22 25.951 -1.183 25.797 -1.103 c
+25.65 -1.014 25.521 -0.897 25.415 -0.75 c
+25.315 -0.595 25.242 -0.411 25.195 -0.206 c
+25.143 0.008 25.121 0.243 25.121 0.5 c
+25.121 0.771 25.143 1.018 25.195 1.235 c
+25.253 1.448 25.334 1.631 25.444 1.779 c
+25.55 1.933 25.683 2.051 25.841 2.132 c
+25.995 2.219 26.183 2.263 26.4 2.263 c
+26.495 2.263 26.595 2.253 26.693 2.234 c
+26.789 2.213 26.878 2.179 26.959 2.132 c
+27.046 2.08 27.123 2.018 27.194 1.941 c
+27.271 1.86 27.333 1.768 27.385 1.661 c
+27.399 1.661 l
+27.399 1.808 l
+27.407 1.866 27.414 1.918 27.414 1.97 c
+27.422 2.028 27.429 2.076 27.429 2.117 c
+27.436 2.165 27.447 2.198 27.458 2.219 c
+28.311 2.219 l
+28.299 2.138 28.289 2.028 28.281 1.881 c
+28.281 1.411 l
+28.281 -1.161 l
+28.281 -1.415 28.245 -1.635 28.178 -1.822 c
+28.108 -2.007 28.006 -2.161 27.869 -2.278 c
+27.73 -2.404 27.565 -2.499 27.37 -2.558 c
+27.171 -2.624 26.951 -2.66 26.709 -2.66 c
+27.399 0.53 m
+27.399 0.742 27.374 0.919 27.326 1.058 c
+27.285 1.205 27.231 1.323 27.164 1.411 c
+27.106 1.5 27.036 1.558 26.959 1.588 c
+26.878 1.625 26.801 1.646 26.724 1.646 c
+26.624 1.646 26.532 1.621 26.444 1.573 c
+26.363 1.532 26.296 1.463 26.238 1.367 c
+26.186 1.278 26.142 1.161 26.106 1.014 c
+26.076 0.875 26.061 0.706 26.061 0.5 c
+26.061 0.125 26.121 -0.154 26.238 -0.338 c
+26.356 -0.515 26.518 -0.603 26.724 -0.603 c
+26.789 -0.603 26.863 -0.588 26.944 -0.559 c
+27.032 -0.522 27.106 -0.463 27.164 -0.382 c
+27.231 -0.294 27.285 -0.176 27.326 -0.029 c
+27.374 0.118 27.399 0.301 27.399 0.53 c
+31.343 -0.646 m
+32.474 -0.646 l
+32.474 -1.264 l
+29.167 -1.264 l
+29.167 -0.646 l
+30.431 -0.646 l
+30.431 1.602 l
+29.505 1.602 l
+29.505 2.219 l
+31.343 2.219 l
+h
+30.431 3.513 0.912 -0.675 re
+30.431 2.837 m
+33.907 1.602 m
+33.363 1.602 l
+33.363 2.219 l
+33.951 2.219 l
+34.231 3.117 l
+34.804 3.117 l
+34.804 2.219 l
+36.039 2.219 l
+36.039 1.602 l
+34.804 1.602 l
+34.804 -0.103 l
+34.804 -0.324 l
+34.811 -0.393 34.834 -0.455 34.863 -0.515 c
+34.9 -0.566 34.955 -0.61 35.025 -0.646 c
+35.102 -0.676 35.216 -0.69 35.362 -0.69 c
+35.499 -0.69 35.634 -0.687 35.774 -0.676 c
+35.91 -0.658 36.043 -0.632 36.17 -0.603 c
+36.17 -1.205 l
+36.091 -1.216 36.013 -1.231 35.935 -1.249 c
+35.855 -1.261 35.778 -1.268 35.7 -1.278 c
+35.62 -1.286 35.532 -1.294 35.436 -1.294 c
+35.348 -1.301 35.249 -1.309 35.142 -1.309 c
+34.955 -1.309 34.793 -1.294 34.657 -1.264 c
+34.528 -1.228 34.414 -1.183 34.319 -1.132 c
+34.231 -1.084 34.157 -1.025 34.098 -0.956 c
+34.04 -0.879 33.995 -0.801 33.966 -0.721 c
+33.937 -0.632 33.915 -0.544 33.907 -0.455 c
+33.897 -0.36 33.893 -0.264 33.893 -0.176 c
+h
+39.467 -0.646 m
+40.599 -0.646 l
+40.599 -1.264 l
+37.292 -1.264 l
+37.292 -0.646 l
+38.556 -0.646 l
+38.556 1.602 l
+37.63 1.602 l
+37.63 2.219 l
+39.467 2.219 l
+h
+38.556 3.513 0.911 -0.675 re
+38.556 2.837 m
+42.954 -2.66 m
+42.738 -2.66 42.547 -2.635 42.381 -2.587 c
+42.212 -2.547 42.073 -2.484 41.955 -2.396 c
+41.837 -2.315 41.738 -2.219 41.661 -2.102 c
+41.591 -1.984 41.543 -1.856 41.514 -1.72 c
+42.41 -1.617 l
+42.447 -1.753 42.518 -1.86 42.616 -1.94 c
+42.712 -2.028 42.837 -2.072 42.984 -2.072 c
+43.072 -2.072 43.153 -2.057 43.234 -2.028 c
+43.311 -1.999 43.381 -1.944 43.44 -1.866 c
+43.499 -1.797 43.542 -1.705 43.572 -1.587 c
+43.609 -1.469 43.631 -1.323 43.631 -1.147 c
+43.631 -0.956 l
+43.631 -0.889 43.634 -0.831 43.646 -0.779 c
+43.646 -0.588 l
+43.631 -0.588 l
+43.532 -0.816 43.388 -0.977 43.204 -1.073 c
+43.017 -1.172 42.811 -1.22 42.587 -1.22 c
+42.381 -1.22 42.198 -1.183 42.044 -1.103 c
+41.896 -1.014 41.768 -0.897 41.661 -0.75 c
+41.562 -0.595 41.488 -0.411 41.441 -0.206 c
+41.389 0.008 41.367 0.243 41.367 0.5 c
+41.367 0.771 41.389 1.018 41.441 1.235 c
+41.5 1.448 41.58 1.631 41.691 1.779 c
+41.797 1.933 41.93 2.051 42.088 2.132 c
+42.242 2.219 42.429 2.263 42.646 2.263 c
+42.742 2.263 42.84 2.253 42.94 2.234 c
+43.035 2.213 43.123 2.179 43.204 2.132 c
+43.293 2.08 43.37 2.018 43.44 1.941 c
+43.517 1.86 43.58 1.768 43.631 1.661 c
+43.646 1.661 l
+43.646 1.808 l
+43.653 1.866 43.66 1.918 43.66 1.97 c
+43.667 2.028 43.675 2.076 43.675 2.117 c
+43.682 2.165 43.694 2.198 43.704 2.219 c
+44.557 2.219 l
+44.546 2.138 44.535 2.028 44.527 1.881 c
+44.527 1.411 l
+44.527 -1.161 l
+44.527 -1.415 44.49 -1.635 44.425 -1.822 c
+44.355 -2.007 44.251 -2.161 44.116 -2.278 c
+43.976 -2.404 43.811 -2.499 43.616 -2.558 c
+43.418 -2.624 43.197 -2.66 42.954 -2.66 c
+43.646 0.53 m
+43.646 0.742 43.619 0.919 43.572 1.058 c
+43.532 1.205 43.476 1.323 43.41 1.411 c
+43.351 1.5 43.282 1.558 43.204 1.588 c
+43.123 1.625 43.046 1.646 42.969 1.646 c
+42.87 1.646 42.778 1.621 42.69 1.573 c
+42.609 1.532 42.543 1.463 42.484 1.367 c
+42.433 1.278 42.389 1.161 42.352 1.014 c
+42.323 0.875 42.308 0.706 42.308 0.5 c
+42.308 0.125 42.366 -0.154 42.484 -0.338 c
+42.601 -0.515 42.763 -0.603 42.969 -0.603 c
+43.035 -0.603 43.109 -0.588 43.189 -0.559 c
+43.278 -0.522 43.351 -0.463 43.41 -0.382 c
+43.476 -0.294 43.532 -0.176 43.572 -0.029 c
+43.619 0.118 43.646 0.301 43.646 0.53 c
+47.677 -1.264 m
+47.677 0.721 l
+47.677 1.022 47.632 1.242 47.544 1.382 c
+47.463 1.529 47.328 1.602 47.133 1.602 c
+47.023 1.602 46.919 1.577 46.824 1.529 c
+46.736 1.477 46.655 1.411 46.589 1.323 c
+46.53 1.235 46.479 1.124 46.441 1 c
+46.412 0.882 46.398 0.75 46.398 0.603 c
+46.398 -1.264 l
+45.487 -1.264 l
+45.487 1.44 l
+45.487 1.661 l
+45.487 1.749 45.479 1.826 45.472 1.897 c
+45.472 2.088 l
+45.472 2.219 l
+46.324 2.219 l
+46.331 2.19 46.339 2.146 46.339 2.088 c
+46.339 1.897 l
+46.346 1.826 46.354 1.756 46.354 1.691 c
+46.362 1.621 46.368 1.565 46.368 1.529 c
+46.383 1.529 l
+46.501 1.793 46.651 1.984 46.838 2.102 c
+47.023 2.219 47.243 2.278 47.5 2.278 c
+47.684 2.278 47.846 2.249 47.985 2.19 c
+48.122 2.132 48.235 2.043 48.323 1.926 c
+48.411 1.808 48.474 1.665 48.515 1.5 c
+48.562 1.342 48.588 1.154 48.588 0.941 c
+48.588 -1.264 l
+h
+52.752 0.485 m
+52.752 0.21 52.714 -0.04 52.648 -0.264 c
+52.579 -0.482 52.476 -0.669 52.34 -0.823 c
+52.201 -0.981 52.024 -1.103 51.811 -1.191 c
+51.594 -1.278 51.341 -1.323 51.046 -1.323 c
+50.771 -1.323 50.524 -1.278 50.312 -1.191 c
+50.106 -1.103 49.933 -0.981 49.797 -0.823 c
+49.657 -0.669 49.554 -0.482 49.489 -0.264 c
+49.419 -0.04 49.385 0.21 49.385 0.485 c
+49.385 0.738 49.415 0.974 49.473 1.191 c
+49.539 1.415 49.643 1.606 49.782 1.764 c
+49.918 1.929 50.094 2.058 50.312 2.146 c
+50.524 2.234 50.782 2.278 51.076 2.278 c
+51.388 2.278 51.649 2.234 51.854 2.146 c
+52.068 2.058 52.241 1.929 52.369 1.764 c
+52.505 1.606 52.604 1.415 52.664 1.191 c
+52.722 0.974 52.752 0.738 52.752 0.485 c
+51.796 0.485 m
+51.796 0.691 51.781 0.867 51.752 1.014 c
+51.73 1.161 51.693 1.282 51.634 1.382 c
+51.576 1.477 51.502 1.548 51.414 1.588 c
+51.326 1.635 51.216 1.661 51.091 1.661 c
+50.826 1.661 50.635 1.562 50.517 1.367 c
+50.4 1.18 50.341 0.885 50.341 0.485 c
+50.341 0.062 50.4 -0.243 50.517 -0.426 c
+50.635 -0.613 50.811 -0.706 51.046 -0.706 c
+51.172 -0.706 51.285 -0.687 51.384 -0.646 c
+51.48 -0.599 51.561 -0.526 51.619 -0.426 c
+51.686 -0.331 51.73 -0.206 51.752 -0.058 c
+51.781 0.088 51.796 0.268 51.796 0.485 c
+56.625 1.47 m
+56.525 1.477 56.423 1.488 56.316 1.5 c
+56.205 1.517 56.084 1.529 55.949 1.529 c
+55.772 1.529 55.614 1.488 55.478 1.411 c
+55.339 1.342 55.22 1.242 55.125 1.118 c
+55.037 0.989 54.967 0.842 54.919 0.676 c
+54.879 0.507 54.861 0.331 54.861 0.147 c
+54.861 -1.264 l
+53.965 -1.264 l
+53.965 0.985 l
+53.965 1.11 53.953 1.235 53.934 1.353 c
+53.924 1.477 53.909 1.595 53.89 1.706 c
+53.88 1.823 53.865 1.918 53.847 1.999 c
+53.824 2.088 53.807 2.161 53.788 2.219 c
+54.67 2.219 l
+54.677 2.168 54.688 2.117 54.699 2.058 c
+54.717 1.999 54.732 1.933 54.743 1.866 c
+54.761 1.808 54.776 1.742 54.788 1.675 c
+54.794 1.606 54.806 1.544 54.817 1.484 c
+54.831 1.484 l
+54.869 1.602 54.919 1.708 54.979 1.808 c
+55.045 1.903 55.125 1.988 55.214 2.058 c
+55.301 2.124 55.405 2.179 55.522 2.219 c
+55.648 2.256 55.794 2.278 55.964 2.278 c
+56.088 2.278 56.205 2.271 56.316 2.263 c
+56.434 2.253 56.537 2.238 56.625 2.219 c
+h
+59.23 -1.323 m
+58.973 -1.323 58.745 -1.286 58.539 -1.22 c
+58.333 -1.143 58.157 -1.029 58.01 -0.881 c
+57.863 -0.727 57.745 -0.536 57.657 -0.309 c
+57.576 -0.085 57.539 0.18 57.539 0.485 c
+57.539 0.816 57.584 1.095 57.672 1.323 c
+57.768 1.558 57.896 1.742 58.054 1.881 c
+58.22 2.018 58.407 2.117 58.613 2.176 c
+58.819 2.242 59.028 2.278 59.245 2.278 c
+59.517 2.278 59.752 2.227 59.951 2.132 c
+60.157 2.043 60.321 1.911 60.45 1.735 c
+60.586 1.565 60.685 1.359 60.744 1.118 c
+60.81 0.882 60.847 0.617 60.847 0.324 c
+60.847 0.309 l
+58.48 0.309 l
+58.48 0.162 58.495 0.022 58.524 -0.103 c
+58.561 -0.231 58.617 -0.345 58.686 -0.441 c
+58.752 -0.529 58.837 -0.599 58.936 -0.646 c
+59.031 -0.698 59.145 -0.721 59.274 -0.721 c
+59.428 -0.721 59.569 -0.687 59.686 -0.617 c
+59.81 -0.551 59.899 -0.449 59.951 -0.309 c
+60.788 -0.382 l
+60.759 -0.482 60.704 -0.588 60.627 -0.706 c
+60.546 -0.816 60.443 -0.919 60.317 -1.014 c
+60.2 -1.103 60.046 -1.176 59.862 -1.234 c
+59.686 -1.294 59.473 -1.323 59.23 -1.323 c
+59.23 1.706 m
+59.141 1.706 59.054 1.691 58.966 1.661 c
+58.877 1.631 58.796 1.58 58.73 1.514 c
+58.661 1.444 58.601 1.356 58.553 1.249 c
+58.513 1.139 58.495 1.014 58.495 0.867 c
+59.965 0.867 l
+59.965 1.003 59.939 1.124 59.891 1.235 c
+59.851 1.342 59.796 1.43 59.729 1.5 c
+59.671 1.565 59.598 1.617 59.509 1.646 c
+59.421 1.683 59.326 1.706 59.23 1.706 c
+f
+Q
+q 1 0 0 1 40.8309 120.2041 cm
+0 0 m
+-1.411 0 l
+-1.411 -4.777 l
+-2.072 -4.777 l
+-2.072 0 l
+-3.484 0 l
+-3.484 0.573 l
+0 0.573 l
+h
+1.352 -1.206 m
+1.606 -0.882 1.926 -0.721 2.308 -0.721 c
+3.013 -0.721 3.37 -1.191 3.381 -2.132 c
+3.381 -4.777 l
+2.734 -4.777 l
+2.734 -2.161 l
+2.734 -1.849 2.679 -1.628 2.572 -1.5 c
+2.462 -1.374 2.308 -1.309 2.102 -1.309 c
+1.944 -1.309 1.797 -1.363 1.661 -1.47 c
+1.532 -1.58 1.429 -1.716 1.352 -1.881 c
+1.352 -4.777 l
+0.706 -4.777 l
+0.706 0.867 l
+1.352 0.867 l
+h
+6.379 -4.777 m
+6.339 -4.69 6.313 -4.542 6.306 -4.337 c
+6.071 -4.682 5.777 -4.851 5.424 -4.851 c
+5.06 -4.851 4.777 -4.755 4.571 -4.557 c
+4.373 -4.351 4.278 -4.065 4.278 -3.69 c
+4.278 -3.289 4.413 -2.97 4.689 -2.735 c
+4.961 -2.492 5.336 -2.367 5.806 -2.367 c
+6.291 -2.367 l
+6.291 -1.941 l
+6.291 -1.706 6.236 -1.54 6.13 -1.441 c
+6.019 -1.334 5.857 -1.279 5.644 -1.279 c
+5.446 -1.279 5.284 -1.338 5.159 -1.455 c
+5.041 -1.573 4.983 -1.72 4.983 -1.897 c
+4.336 -1.897 l
+4.336 -1.702 4.395 -1.511 4.513 -1.324 c
+4.638 -1.139 4.8 -0.992 4.997 -0.882 c
+5.203 -0.775 5.431 -0.721 5.689 -0.721 c
+6.089 -0.721 6.394 -0.823 6.6 -1.029 c
+6.813 -1.235 6.927 -1.529 6.938 -1.912 c
+6.938 -3.925 l
+6.938 -4.23 6.975 -4.495 7.056 -4.719 c
+7.056 -4.777 l
+h
+5.512 -4.263 m
+5.677 -4.263 5.828 -4.219 5.968 -4.131 c
+6.115 -4.042 6.221 -3.932 6.291 -3.793 c
+6.291 -2.852 l
+5.924 -2.852 l
+5.608 -2.852 5.365 -2.922 5.189 -3.057 c
+5.012 -3.186 4.924 -3.373 4.924 -3.616 c
+4.924 -3.844 4.968 -4.009 5.056 -4.116 c
+5.145 -4.215 5.295 -4.263 5.512 -4.263 c
+8.555 -0.794 m
+8.569 -1.235 l
+8.823 -0.893 9.147 -0.721 9.54 -0.721 c
+10.246 -0.721 10.601 -1.191 10.612 -2.132 c
+10.612 -4.777 l
+9.966 -4.777 l
+9.966 -2.161 l
+9.966 -1.849 9.911 -1.628 9.804 -1.5 c
+9.694 -1.374 9.54 -1.309 9.334 -1.309 c
+9.176 -1.309 9.029 -1.363 8.893 -1.47 c
+8.764 -1.58 8.661 -1.716 8.584 -1.881 c
+8.584 -4.777 l
+7.937 -4.777 l
+7.937 -0.794 l
+h
+12.582 -2.955 m
+12.245 -3.352 l
+12.245 -4.777 l
+11.582 -4.777 l
+11.582 0.867 l
+12.245 0.867 l
+12.245 -2.514 l
+13.479 -0.794 l
+14.258 -0.794 l
+12.993 -2.455 l
+14.42 -4.777 l
+13.67 -4.777 l
+h
+16.918 -3.763 m
+16.918 -3.616 16.864 -3.495 16.756 -3.396 c
+16.646 -3.3 16.44 -3.183 16.139 -3.043 c
+15.794 -2.896 15.551 -2.774 15.405 -2.675 c
+15.258 -2.569 15.148 -2.452 15.081 -2.323 c
+15.011 -2.198 14.978 -2.04 14.978 -1.852 c
+14.978 -1.529 15.096 -1.261 15.331 -1.044 c
+15.566 -0.831 15.867 -0.721 16.243 -0.721 c
+16.625 -0.721 16.933 -0.834 17.168 -1.058 c
+17.404 -1.286 17.521 -1.573 17.521 -1.926 c
+16.874 -1.926 l
+16.874 -1.75 16.816 -1.598 16.698 -1.47 c
+16.581 -1.345 16.426 -1.279 16.243 -1.279 c
+16.044 -1.279 15.893 -1.334 15.787 -1.441 c
+15.676 -1.54 15.625 -1.673 15.625 -1.837 c
+15.625 -1.966 15.661 -2.072 15.742 -2.161 c
+15.819 -2.242 16.01 -2.344 16.316 -2.469 c
+16.794 -2.657 17.124 -2.845 17.301 -3.028 c
+17.477 -3.205 17.565 -3.433 17.565 -3.705 c
+17.565 -4.057 17.44 -4.337 17.198 -4.542 c
+16.962 -4.748 16.646 -4.851 16.257 -4.851 c
+15.835 -4.851 15.497 -4.734 15.243 -4.499 c
+14.986 -4.256 14.861 -3.95 14.861 -3.587 c
+15.507 -3.587 l
+15.515 -3.815 15.584 -3.991 15.713 -4.116 c
+15.838 -4.233 16.022 -4.293 16.257 -4.293 c
+16.47 -4.293 16.632 -4.245 16.742 -4.146 c
+16.86 -4.05 16.918 -3.921 16.918 -3.763 c
+23.518 0.162 m
+23.518 -0.794 l
+24.122 -0.794 l
+24.122 -1.324 l
+23.518 -1.324 l
+23.518 -3.793 l
+23.518 -3.95 23.54 -4.068 23.592 -4.146 c
+23.651 -4.226 23.739 -4.263 23.856 -4.263 c
+23.945 -4.263 24.033 -4.248 24.122 -4.219 c
+24.122 -4.777 l
+23.974 -4.825 23.82 -4.851 23.665 -4.851 c
+23.408 -4.851 23.214 -4.759 23.077 -4.572 c
+22.938 -4.388 22.871 -4.127 22.871 -3.793 c
+22.871 -1.324 l
+22.269 -1.324 l
+22.269 -0.794 l
+22.871 -0.794 l
+22.871 0.162 l
+h
+24.679 -2.602 m
+24.679 -2.025 24.816 -1.569 25.091 -1.235 c
+25.375 -0.893 25.745 -0.721 26.208 -0.721 c
+26.668 -0.721 27.035 -0.89 27.311 -1.22 c
+27.594 -1.544 27.741 -1.992 27.752 -2.558 c
+27.752 -2.984 l
+27.752 -3.554 27.609 -4.009 27.326 -4.351 c
+27.05 -4.686 26.682 -4.851 26.223 -4.851 c
+25.76 -4.851 25.389 -4.69 25.106 -4.366 c
+24.83 -4.035 24.687 -3.595 24.679 -3.043 c
+h
+25.327 -2.984 m
+25.327 -3.389 25.404 -3.705 25.562 -3.94 c
+25.727 -4.175 25.947 -4.293 26.223 -4.293 c
+26.788 -4.293 27.083 -3.881 27.105 -3.057 c
+27.105 -2.602 l
+27.105 -2.201 27.021 -1.881 26.855 -1.646 c
+26.697 -1.404 26.48 -1.279 26.208 -1.279 c
+25.944 -1.279 25.727 -1.404 25.562 -1.646 c
+25.404 -1.881 25.327 -2.201 25.327 -2.602 c
+h
+33.701 0.162 m
+33.701 -0.794 l
+34.304 -0.794 l
+34.304 -1.324 l
+33.701 -1.324 l
+33.701 -3.793 l
+33.701 -3.95 33.724 -4.068 33.774 -4.146 c
+33.834 -4.226 33.922 -4.263 34.04 -4.263 c
+34.127 -4.263 34.216 -4.248 34.304 -4.219 c
+34.304 -4.777 l
+34.157 -4.825 34.002 -4.851 33.848 -4.851 c
+33.591 -4.851 33.396 -4.759 33.26 -4.572 c
+33.121 -4.388 33.055 -4.127 33.055 -3.793 c
+33.055 -1.324 l
+32.452 -1.324 l
+32.452 -0.794 l
+33.055 -0.794 l
+33.055 0.162 l
+h
+35.715 -1.206 m
+35.968 -0.882 36.288 -0.721 36.67 -0.721 c
+37.376 -0.721 37.732 -1.191 37.743 -2.132 c
+37.743 -4.777 l
+37.097 -4.777 l
+37.097 -2.161 l
+37.097 -1.849 37.042 -1.628 36.935 -1.5 c
+36.824 -1.374 36.67 -1.309 36.465 -1.309 c
+36.307 -1.309 36.159 -1.363 36.024 -1.47 c
+35.895 -1.58 35.792 -1.716 35.715 -1.881 c
+35.715 -4.777 l
+35.068 -4.777 l
+35.068 0.867 l
+35.715 0.867 l
+h
+39.405 -4.777 -0.647 3.983 re
+39.449 0.249 m
+39.449 0.139 39.419 0.048 39.361 -0.03 c
+39.301 -0.1 39.206 -0.133 39.081 -0.133 c
+38.964 -0.133 38.868 -0.1 38.802 -0.03 c
+38.743 0.048 38.713 0.139 38.713 0.249 c
+38.713 0.367 38.743 0.459 38.802 0.529 c
+38.868 0.606 38.964 0.646 39.081 0.646 c
+39.206 0.646 39.301 0.606 39.361 0.529 c
+39.419 0.448 39.449 0.357 39.449 0.249 c
+42.359 -3.763 m
+42.359 -3.616 42.304 -3.495 42.197 -3.396 c
+42.087 -3.3 41.881 -3.183 41.58 -3.043 c
+41.234 -2.896 40.992 -2.774 40.845 -2.675 c
+40.698 -2.569 40.587 -2.452 40.521 -2.323 c
+40.452 -2.198 40.419 -2.04 40.419 -1.852 c
+40.419 -1.529 40.536 -1.261 40.772 -1.044 c
+41.007 -0.831 41.308 -0.721 41.683 -0.721 c
+42.065 -0.721 42.374 -0.834 42.609 -1.058 c
+42.844 -1.286 42.962 -1.573 42.962 -1.926 c
+42.314 -1.926 l
+42.314 -1.75 42.256 -1.598 42.139 -1.47 c
+42.021 -1.345 41.867 -1.279 41.683 -1.279 c
+41.485 -1.279 41.333 -1.334 41.227 -1.441 c
+41.117 -1.54 41.065 -1.673 41.065 -1.837 c
+41.065 -1.966 41.102 -2.072 41.183 -2.161 c
+41.26 -2.242 41.451 -2.344 41.757 -2.469 c
+42.234 -2.657 42.565 -2.845 42.742 -3.028 c
+42.917 -3.205 43.006 -3.433 43.006 -3.705 c
+43.006 -4.057 42.881 -4.337 42.638 -4.542 c
+42.403 -4.748 42.087 -4.851 41.697 -4.851 c
+41.275 -4.851 40.937 -4.734 40.683 -4.499 c
+40.426 -4.256 40.301 -3.95 40.301 -3.587 c
+40.948 -3.587 l
+40.955 -3.815 41.025 -3.991 41.154 -4.116 c
+41.279 -4.233 41.462 -4.293 41.697 -4.293 c
+41.911 -4.293 42.073 -4.245 42.183 -4.146 c
+42.3 -4.05 42.359 -3.921 42.359 -3.763 c
+48.279 -4.777 m
+48.279 -1.324 l
+47.764 -1.324 l
+47.764 -0.794 l
+48.279 -0.794 l
+48.279 -0.426 l
+48.286 0.004 48.4 0.338 48.617 0.573 c
+48.841 0.816 49.154 0.941 49.558 0.941 c
+49.705 0.941 49.844 0.918 49.984 0.881 c
+50.131 0.841 50.282 0.786 50.44 0.72 c
+50.322 0.147 l
+50.087 0.272 49.844 0.338 49.601 0.338 c
+49.356 0.338 49.183 0.268 49.088 0.132 c
+48.988 0.004 48.94 -0.191 48.94 -0.456 c
+48.94 -0.794 l
+49.587 -0.794 l
+49.587 -1.324 l
+48.94 -1.324 l
+48.94 -4.777 l
+h
+50.748 -4.777 -0.646 3.983 re
+52.498 -4.777 -0.647 5.644 re
+54.937 -4.851 m
+54.438 -4.851 54.056 -4.704 53.791 -4.41 c
+53.526 -4.116 53.394 -3.682 53.394 -3.102 c
+53.394 -2.631 l
+53.394 -2.036 53.52 -1.569 53.776 -1.235 c
+54.041 -0.893 54.401 -0.721 54.864 -0.721 c
+55.324 -0.721 55.665 -0.875 55.893 -1.176 c
+56.128 -1.47 56.25 -1.933 56.261 -2.558 c
+56.261 -2.984 l
+54.041 -2.984 l
+54.041 -3.072 l
+54.041 -3.506 54.118 -3.818 54.276 -4.013 c
+54.442 -4.2 54.673 -4.293 54.967 -4.293 c
+55.162 -4.293 55.334 -4.26 55.481 -4.189 c
+55.629 -4.112 55.764 -3.994 55.893 -3.836 c
+56.231 -4.248 l
+55.945 -4.653 55.515 -4.851 54.937 -4.851 c
+54.864 -1.279 m
+54.588 -1.279 54.386 -1.374 54.262 -1.559 c
+54.133 -1.746 54.06 -2.036 54.041 -2.425 c
+55.614 -2.425 l
+55.614 -2.338 l
+55.592 -1.955 55.525 -1.687 55.408 -1.529 c
+55.29 -1.363 55.107 -1.279 54.864 -1.279 c
+64.738 -4.116 m
+64.62 -4.263 l
+64.286 -4.656 63.791 -4.851 63.136 -4.851 c
+62.555 -4.851 62.1 -4.659 61.769 -4.278 c
+61.445 -3.896 61.277 -3.352 61.269 -2.646 c
+61.269 -1.602 l
+61.269 -0.849 61.416 -0.287 61.711 0.087 c
+62.012 0.459 62.463 0.646 63.062 0.646 c
+63.57 0.646 63.967 0.503 64.253 0.22 c
+64.547 -0.067 64.709 -0.47 64.738 -1 c
+64.062 -1 l
+64.04 -0.669 63.948 -0.408 63.783 -0.221 c
+63.625 -0.026 63.39 0.073 63.078 0.073 c
+62.684 0.073 62.401 -0.055 62.224 -0.309 c
+62.048 -0.566 61.952 -0.967 61.946 -1.515 c
+61.946 -2.602 l
+61.946 -3.142 62.048 -3.558 62.254 -3.851 c
+62.459 -4.138 62.754 -4.278 63.136 -4.278 c
+63.496 -4.278 63.776 -4.189 63.974 -4.013 c
+64.062 -3.925 l
+64.062 -2.691 l
+63.092 -2.691 l
+63.092 -2.117 l
+64.738 -2.117 l
+h
+66.458 -4.777 -0.646 3.983 re
+66.502 0.249 m
+66.502 0.139 66.473 0.048 66.414 -0.03 c
+66.355 -0.1 66.26 -0.133 66.135 -0.133 c
+66.017 -0.133 65.921 -0.1 65.856 -0.03 c
+65.796 0.048 65.767 0.139 65.767 0.249 c
+65.767 0.367 65.796 0.459 65.856 0.529 c
+65.921 0.606 66.017 0.646 66.135 0.646 c
+66.26 0.646 66.355 0.606 66.414 0.529 c
+66.473 0.448 66.502 0.357 66.502 0.249 c
+68.325 0.162 m
+68.325 -0.794 l
+68.927 -0.794 l
+68.927 -1.324 l
+68.325 -1.324 l
+68.325 -3.793 l
+68.325 -3.95 68.347 -4.068 68.398 -4.146 c
+68.457 -4.226 68.545 -4.263 68.663 -4.263 c
+68.751 -4.263 68.84 -4.248 68.927 -4.219 c
+68.927 -4.777 l
+68.78 -4.825 68.626 -4.851 68.472 -4.851 c
+68.215 -4.851 68.02 -4.759 67.884 -4.572 c
+67.745 -4.388 67.678 -4.127 67.678 -3.793 c
+67.678 -1.324 l
+67.076 -1.324 l
+67.076 -0.794 l
+67.678 -0.794 l
+67.678 0.162 l
+h
+76.921 -3.631 m
+77.523 -0.794 l
+78.17 -0.794 l
+77.185 -4.777 l
+76.67 -4.777 l
+75.891 -1.926 l
+75.142 -4.777 l
+74.612 -4.777 l
+73.657 -0.794 l
+74.289 -0.794 l
+74.906 -3.558 l
+75.641 -0.794 l
+76.156 -0.794 l
+h
+79.551 -4.777 -0.646 3.983 re
+79.595 0.249 m
+79.595 0.139 79.566 0.048 79.508 -0.03 c
+79.448 -0.1 79.353 -0.133 79.228 -0.133 c
+79.111 -0.133 79.015 -0.1 78.949 -0.03 c
+78.889 0.048 78.86 0.139 78.86 0.249 c
+78.86 0.367 78.889 0.459 78.949 0.529 c
+79.015 0.606 79.111 0.646 79.228 0.646 c
+79.353 0.646 79.448 0.606 79.508 0.529 c
+79.566 0.448 79.595 0.357 79.595 0.249 c
+81.271 -4.777 -0.647 5.644 re
+82.991 -4.777 -0.647 5.644 re
+88.973 -4.777 -0.646 3.983 re
+89.017 0.249 m
+89.017 0.139 88.988 0.048 88.929 -0.03 c
+88.871 -0.1 88.775 -0.133 88.65 -0.133 c
+88.532 -0.133 88.437 -0.1 88.37 -0.03 c
+88.312 0.048 88.283 0.139 88.283 0.249 c
+88.283 0.367 88.312 0.459 88.37 0.529 c
+88.437 0.606 88.532 0.646 88.65 0.646 c
+88.775 0.646 88.871 0.606 88.929 0.529 c
+88.988 0.448 89.017 0.357 89.017 0.249 c
+89.856 -2.602 m
+89.856 -1.985 89.966 -1.521 90.193 -1.206 c
+90.417 -0.882 90.752 -0.721 91.192 -0.721 c
+91.593 -0.721 91.898 -0.897 92.104 -1.249 c
+92.148 -0.794 l
+92.736 -0.794 l
+92.736 -4.821 l
+92.736 -5.31 92.607 -5.689 92.354 -5.953 c
+92.096 -6.218 91.744 -6.35 91.296 -6.35 c
+91.097 -6.35 90.877 -6.299 90.635 -6.203 c
+90.388 -6.104 90.208 -5.983 90.091 -5.835 c
+90.355 -5.395 l
+90.62 -5.659 90.917 -5.792 91.252 -5.792 c
+91.788 -5.792 92.064 -5.498 92.075 -4.91 c
+92.075 -4.381 l
+91.869 -4.696 91.568 -4.851 91.178 -4.851 c
+90.766 -4.851 90.444 -4.7 90.208 -4.395 c
+89.98 -4.083 89.862 -3.631 89.856 -3.043 c
+h
+90.517 -2.984 m
+90.517 -3.425 90.579 -3.755 90.708 -3.969 c
+90.833 -4.175 91.049 -4.278 91.354 -4.278 c
+91.678 -4.278 91.917 -4.112 92.075 -3.778 c
+92.075 -1.794 l
+91.906 -1.47 91.667 -1.309 91.354 -1.309 c
+91.061 -1.309 90.843 -1.411 90.708 -1.617 c
+90.579 -1.823 90.517 -2.147 90.517 -2.587 c
+h
+94.309 -0.794 m
+94.324 -1.235 l
+94.577 -0.893 94.901 -0.721 95.294 -0.721 c
+96 -0.721 96.356 -1.191 96.367 -2.132 c
+96.367 -4.777 l
+95.72 -4.777 l
+95.72 -2.161 l
+95.72 -1.849 95.665 -1.628 95.558 -1.5 c
+95.448 -1.374 95.294 -1.309 95.088 -1.309 c
+94.93 -1.309 94.783 -1.363 94.647 -1.47 c
+94.519 -1.58 94.415 -1.716 94.338 -1.881 c
+94.338 -4.777 l
+93.692 -4.777 l
+93.692 -0.794 l
+h
+97.205 -2.602 m
+97.205 -2.025 97.341 -1.569 97.617 -1.235 c
+97.899 -0.893 98.271 -0.721 98.733 -0.721 c
+99.192 -0.721 99.56 -0.89 99.836 -1.22 c
+100.119 -1.544 100.266 -1.992 100.277 -2.558 c
+100.277 -2.984 l
+100.277 -3.554 100.133 -4.009 99.851 -4.351 c
+99.575 -4.686 99.208 -4.851 98.748 -4.851 c
+98.285 -4.851 97.914 -4.69 97.631 -4.366 c
+97.355 -4.035 97.212 -3.595 97.205 -3.043 c
+h
+97.852 -2.984 m
+97.852 -3.389 97.929 -3.705 98.087 -3.94 c
+98.252 -4.175 98.473 -4.293 98.748 -4.293 c
+99.314 -4.293 99.608 -3.881 99.63 -3.057 c
+99.63 -2.602 l
+99.63 -2.201 99.545 -1.881 99.381 -1.646 c
+99.223 -1.404 99.005 -1.279 98.733 -1.279 c
+98.469 -1.279 98.252 -1.404 98.087 -1.646 c
+97.929 -1.881 97.852 -2.201 97.852 -2.602 c
+h
+102.761 -1.411 m
+102.673 -1.393 102.574 -1.382 102.467 -1.382 c
+102.132 -1.382 101.897 -1.565 101.762 -1.926 c
+101.762 -4.777 l
+101.114 -4.777 l
+101.114 -0.794 l
+101.747 -0.794 l
+101.762 -1.206 l
+101.938 -0.882 102.18 -0.721 102.496 -0.721 c
+102.603 -0.721 102.691 -0.742 102.761 -0.779 c
+h
+104.76 -4.851 m
+104.26 -4.851 103.878 -4.704 103.614 -4.41 c
+103.349 -4.116 103.217 -3.682 103.217 -3.102 c
+103.217 -2.631 l
+103.217 -2.036 103.342 -1.569 103.599 -1.235 c
+103.863 -0.893 104.224 -0.721 104.686 -0.721 c
+105.145 -0.721 105.488 -0.875 105.716 -1.176 c
+105.951 -1.47 106.072 -1.933 106.083 -2.558 c
+106.083 -2.984 l
+103.863 -2.984 l
+103.863 -3.072 l
+103.863 -3.506 103.94 -3.818 104.098 -4.013 c
+104.264 -4.2 104.495 -4.293 104.79 -4.293 c
+104.985 -4.293 105.157 -4.26 105.304 -4.189 c
+105.451 -4.112 105.587 -3.994 105.716 -3.836 c
+106.053 -4.248 l
+105.767 -4.653 105.337 -4.851 104.76 -4.851 c
+104.686 -1.279 m
+104.411 -1.279 104.209 -1.374 104.084 -1.559 c
+103.955 -1.746 103.882 -2.036 103.863 -2.425 c
+105.436 -2.425 l
+105.436 -2.338 l
+105.415 -1.955 105.348 -1.687 105.23 -1.529 c
+105.113 -1.363 104.929 -1.279 104.686 -1.279 c
+113.164 -4.777 m
+113.124 -4.69 113.099 -4.542 113.091 -4.337 c
+112.856 -4.682 112.561 -4.851 112.209 -4.851 c
+111.845 -4.851 111.562 -4.755 111.356 -4.557 c
+111.158 -4.351 111.063 -4.065 111.063 -3.69 c
+111.063 -3.289 111.198 -2.97 111.474 -2.735 c
+111.746 -2.492 112.121 -2.367 112.591 -2.367 c
+113.076 -2.367 l
+113.076 -1.941 l
+113.076 -1.706 113.021 -1.54 112.914 -1.441 c
+112.804 -1.334 112.642 -1.279 112.43 -1.279 c
+112.231 -1.279 112.069 -1.338 111.944 -1.455 c
+111.827 -1.573 111.768 -1.72 111.768 -1.897 c
+111.121 -1.897 l
+111.121 -1.702 111.18 -1.511 111.298 -1.324 c
+111.422 -1.139 111.584 -0.992 111.782 -0.882 c
+111.988 -0.775 112.216 -0.721 112.474 -0.721 c
+112.874 -0.721 113.179 -0.823 113.385 -1.029 c
+113.598 -1.235 113.712 -1.529 113.723 -1.912 c
+113.723 -3.925 l
+113.723 -4.23 113.76 -4.495 113.841 -4.719 c
+113.841 -4.777 l
+h
+112.297 -4.263 m
+112.463 -4.263 112.613 -4.219 112.752 -4.131 c
+112.9 -4.042 113.006 -3.932 113.076 -3.793 c
+113.076 -2.852 l
+112.709 -2.852 l
+112.393 -2.852 112.15 -2.922 111.973 -3.057 c
+111.797 -3.186 111.709 -3.373 111.709 -3.616 c
+111.709 -3.844 111.753 -4.009 111.842 -4.116 c
+111.929 -4.215 112.081 -4.263 112.297 -4.263 c
+115.428 -4.777 -0.646 5.644 re
+117.147 -4.777 -0.646 5.644 re
+122.597 -4.777 m
+122.597 -1.324 l
+122.083 -1.324 l
+122.083 -0.794 l
+122.597 -0.794 l
+122.597 -0.426 l
+122.605 0.004 122.719 0.338 122.936 0.573 c
+123.16 0.816 123.472 0.941 123.877 0.941 c
+124.023 0.941 124.163 0.918 124.303 0.881 c
+124.449 0.841 124.6 0.786 124.758 0.72 c
+124.64 0.147 l
+124.405 0.272 124.163 0.338 123.92 0.338 c
+123.674 0.338 123.501 0.268 123.406 0.132 c
+123.306 0.004 123.258 -0.191 123.258 -0.456 c
+123.258 -0.794 l
+123.906 -0.794 l
+123.906 -1.324 l
+123.258 -1.324 l
+123.258 -4.777 l
+h
+125.067 -4.777 -0.646 3.983 re
+126.816 -4.777 -0.647 5.644 re
+129.256 -4.851 m
+128.756 -4.851 128.374 -4.704 128.11 -4.41 c
+127.845 -4.116 127.713 -3.682 127.713 -3.102 c
+127.713 -2.631 l
+127.713 -2.036 127.838 -1.569 128.095 -1.235 c
+128.359 -0.893 128.719 -0.721 129.182 -0.721 c
+129.642 -0.721 129.984 -0.875 130.212 -1.176 c
+130.447 -1.47 130.568 -1.933 130.579 -2.558 c
+130.579 -2.984 l
+128.359 -2.984 l
+128.359 -3.072 l
+128.359 -3.506 128.436 -3.818 128.594 -4.013 c
+128.76 -4.2 128.991 -4.293 129.286 -4.293 c
+129.481 -4.293 129.653 -4.26 129.799 -4.189 c
+129.947 -4.112 130.083 -3.994 130.212 -3.836 c
+130.549 -4.248 l
+130.263 -4.653 129.833 -4.851 129.256 -4.851 c
+129.182 -1.279 m
+128.907 -1.279 128.704 -1.374 128.58 -1.559 c
+128.451 -1.746 128.378 -2.036 128.359 -2.425 c
+129.932 -2.425 l
+129.932 -2.338 l
+129.91 -1.955 129.844 -1.687 129.726 -1.529 c
+129.608 -1.363 129.425 -1.279 129.182 -1.279 c
+133.298 -3.763 m
+133.298 -3.616 133.244 -3.495 133.136 -3.396 c
+133.026 -3.3 132.82 -3.183 132.519 -3.043 c
+132.174 -2.896 131.931 -2.774 131.784 -2.675 c
+131.638 -2.569 131.527 -2.452 131.461 -2.323 c
+131.391 -2.198 131.358 -2.04 131.358 -1.852 c
+131.358 -1.529 131.476 -1.261 131.711 -1.044 c
+131.946 -0.831 132.247 -0.721 132.622 -0.721 c
+133.005 -0.721 133.313 -0.834 133.548 -1.058 c
+133.783 -1.286 133.901 -1.573 133.901 -1.926 c
+133.254 -1.926 l
+133.254 -1.75 133.196 -1.598 133.078 -1.47 c
+132.96 -1.345 132.806 -1.279 132.622 -1.279 c
+132.424 -1.279 132.273 -1.334 132.166 -1.441 c
+132.056 -1.54 132.005 -1.673 132.005 -1.837 c
+132.005 -1.966 132.041 -2.072 132.122 -2.161 c
+132.199 -2.242 132.39 -2.344 132.696 -2.469 c
+133.173 -2.657 133.504 -2.845 133.681 -3.028 c
+133.857 -3.205 133.945 -3.433 133.945 -3.705 c
+133.945 -4.057 133.82 -4.337 133.577 -4.542 c
+133.342 -4.748 133.026 -4.851 132.637 -4.851 c
+132.214 -4.851 131.877 -4.734 131.623 -4.499 c
+131.366 -4.256 131.241 -3.95 131.241 -3.587 c
+131.887 -3.587 l
+131.894 -3.815 131.964 -3.991 132.093 -4.116 c
+132.218 -4.233 132.402 -4.293 132.637 -4.293 c
+132.85 -4.293 133.012 -4.245 133.122 -4.146 c
+133.24 -4.05 133.298 -3.921 133.298 -3.763 c
+139.748 -4.777 -0.647 3.983 re
+139.792 0.249 m
+139.792 0.139 139.762 0.048 139.704 -0.03 c
+139.644 -0.1 139.549 -0.133 139.424 -0.133 c
+139.307 -0.133 139.211 -0.1 139.145 -0.03 c
+139.086 0.048 139.056 0.139 139.056 0.249 c
+139.056 0.367 139.086 0.459 139.145 0.529 c
+139.211 0.606 139.307 0.646 139.424 0.646 c
+139.549 0.646 139.644 0.606 139.704 0.529 c
+139.762 0.448 139.792 0.357 139.792 0.249 c
+141.379 -0.794 m
+141.394 -1.235 l
+141.647 -0.893 141.971 -0.721 142.364 -0.721 c
+143.07 -0.721 143.426 -1.191 143.437 -2.132 c
+143.437 -4.777 l
+142.79 -4.777 l
+142.79 -2.161 l
+142.79 -1.849 142.735 -1.628 142.628 -1.5 c
+142.518 -1.374 142.364 -1.309 142.158 -1.309 c
+142 -1.309 141.853 -1.363 141.717 -1.47 c
+141.589 -1.58 141.485 -1.716 141.408 -1.881 c
+141.408 -4.777 l
+140.762 -4.777 l
+140.762 -0.794 l
+h
+f
+Q
+190.482 115.427 -1.043 5.644 re
+191.172 117.544 m
+191.172 118.15 191.312 118.624 191.599 118.969 c
+191.882 119.311 192.275 119.484 192.775 119.484 c
+193.282 119.484 193.678 119.311 193.965 118.969 c
+194.249 118.624 194.392 118.15 194.392 117.544 c
+194.392 117.278 l
+194.392 116.68 194.249 116.21 193.965 115.867 c
+193.678 115.522 193.282 115.353 192.775 115.353 c
+192.264 115.353 191.867 115.522 191.584 115.867 c
+191.309 116.21 191.172 116.684 191.172 117.293 c
+h
+192.217 117.278 m
+192.217 116.573 192.4 116.22 192.775 116.22 c
+193.128 116.22 193.319 116.515 193.348 117.102 c
+193.348 117.544 l
+193.348 117.903 193.296 118.175 193.201 118.352 c
+193.102 118.528 192.959 118.616 192.775 118.616 c
+192.598 118.616 192.459 118.528 192.363 118.352 c
+192.264 118.175 192.217 117.903 192.217 117.544 c
+h
+194.906 117.544 m
+194.906 118.19 195.024 118.675 195.259 118.998 c
+195.494 119.322 195.825 119.484 196.259 119.484 c
+196.612 119.484 196.884 119.34 197.082 119.057 c
+197.126 119.41 l
+198.067 119.41 l
+198.067 115.427 l
+198.067 114.92 197.923 114.53 197.64 114.265 c
+197.354 113.993 196.949 113.854 196.421 113.854 c
+196.192 113.854 195.957 113.898 195.715 113.986 c
+195.48 114.074 195.303 114.188 195.185 114.324 c
+195.538 115.044 l
+195.634 114.938 195.762 114.853 195.92 114.795 c
+196.074 114.728 196.222 114.691 196.361 114.691 c
+196.597 114.691 196.762 114.751 196.861 114.868 c
+196.968 114.978 197.023 115.155 197.023 115.397 c
+197.023 115.75 l
+196.824 115.485 196.567 115.353 196.244 115.353 c
+195.821 115.353 195.494 115.515 195.259 115.838 c
+195.031 116.169 194.914 116.64 194.906 117.249 c
+h
+195.95 117.278 m
+195.95 116.904 195.997 116.636 196.097 116.47 c
+196.192 116.301 196.346 116.22 196.552 116.22 c
+196.766 116.22 196.924 116.297 197.023 116.455 c
+197.023 118.352 l
+196.913 118.517 196.758 118.602 196.552 118.602 c
+196.346 118.602 196.192 118.517 196.097 118.352 c
+195.997 118.182 195.95 117.914 195.95 117.544 c
+h
+200.595 116.499 m
+200.595 116.588 200.551 116.665 200.462 116.735 c
+200.375 116.812 200.186 116.915 199.904 117.043 c
+199.471 117.22 199.172 117.4 199.008 117.587 c
+198.85 117.771 198.773 118.003 198.773 118.278 c
+198.773 118.62 198.893 118.903 199.139 119.131 c
+199.393 119.366 199.731 119.484 200.154 119.484 c
+200.583 119.484 200.932 119.37 201.198 119.146 c
+201.462 118.918 201.595 118.616 201.595 118.234 c
+200.551 118.234 l
+200.551 118.558 200.411 118.72 200.139 118.72 c
+200.028 118.72 199.941 118.683 199.874 118.616 c
+199.805 118.547 199.772 118.448 199.772 118.323 c
+199.772 118.234 199.808 118.153 199.889 118.087 c
+199.966 118.028 200.147 117.933 200.433 117.808 c
+200.863 117.65 201.161 117.473 201.329 117.278 c
+201.506 117.091 201.595 116.842 201.595 116.529 c
+201.595 116.176 201.462 115.89 201.198 115.676 c
+200.932 115.46 200.583 115.353 200.154 115.353 c
+199.86 115.353 199.598 115.408 199.375 115.515 c
+199.147 115.632 198.971 115.794 198.846 116 c
+198.728 116.206 198.669 116.426 198.669 116.661 c
+199.654 116.661 l
+199.654 116.474 199.691 116.338 199.772 116.25 c
+199.86 116.162 199.992 116.118 200.169 116.118 c
+200.452 116.118 200.595 116.243 200.595 116.499 c
+f
+q 1 0 0 1 206.4894 117.602 cm
+0 0 m
+0 0.607 0.11 1.073 0.338 1.396 c
+0.573 1.72 0.9 1.881 1.323 1.881 c
+1.705 1.881 2.003 1.723 2.219 1.411 c
+2.219 3.469 l
+2.866 3.469 l
+2.866 -2.175 l
+2.278 -2.175 l
+2.234 -1.749 l
+2.028 -2.084 1.723 -2.249 1.323 -2.249 c
+0.911 -2.249 0.588 -2.094 0.353 -1.779 c
+0.118 -1.455 0 -0.999 0 -0.411 c
+h
+0.646 -0.382 m
+0.646 -0.823 0.709 -1.153 0.837 -1.367 c
+0.974 -1.573 1.194 -1.675 1.499 -1.675 c
+1.822 -1.675 2.061 -1.514 2.219 -1.191 c
+2.219 0.823 l
+2.05 1.135 1.811 1.294 1.499 1.294 c
+1.194 1.294 0.974 1.191 0.837 0.985 c
+0.709 0.779 0.646 0.455 0.646 0.015 c
+h
+4.571 -2.175 -0.646 3.983 re
+4.615 2.851 m
+4.615 2.741 4.586 2.65 4.527 2.572 c
+4.469 2.502 4.373 2.469 4.247 2.469 c
+4.13 2.469 4.035 2.502 3.968 2.572 c
+3.91 2.65 3.881 2.741 3.881 2.851 c
+3.881 2.969 3.91 3.061 3.968 3.131 c
+4.035 3.208 4.13 3.248 4.247 3.248 c
+4.373 3.248 4.469 3.208 4.527 3.131 c
+4.586 3.05 4.615 2.959 4.615 2.851 c
+7.231 1.191 m
+7.143 1.209 7.044 1.22 6.938 1.22 c
+6.603 1.22 6.368 1.037 6.232 0.676 c
+6.232 -2.175 l
+5.585 -2.175 l
+5.585 1.808 l
+6.217 1.808 l
+6.232 1.396 l
+6.408 1.72 6.651 1.881 6.967 1.881 c
+7.073 1.881 7.162 1.86 7.231 1.823 c
+h
+9.231 -2.249 m
+8.731 -2.249 8.349 -2.102 8.084 -1.808 c
+7.819 -1.514 7.688 -1.08 7.688 -0.5 c
+7.688 -0.029 l
+7.688 0.566 7.812 1.033 8.07 1.367 c
+8.334 1.709 8.694 1.881 9.157 1.881 c
+9.616 1.881 9.959 1.727 10.186 1.426 c
+10.422 1.132 10.543 0.669 10.553 0.044 c
+10.553 -0.382 l
+8.334 -0.382 l
+8.334 -0.47 l
+8.334 -0.904 8.411 -1.216 8.569 -1.411 c
+8.735 -1.598 8.966 -1.691 9.26 -1.691 c
+9.454 -1.691 9.628 -1.658 9.774 -1.587 c
+9.922 -1.51 10.057 -1.392 10.186 -1.234 c
+10.524 -1.646 l
+10.237 -2.051 9.807 -2.249 9.231 -2.249 c
+9.157 1.323 m
+8.882 1.323 8.679 1.228 8.554 1.043 c
+8.426 0.856 8.353 0.566 8.334 0.177 c
+9.907 0.177 l
+9.907 0.264 l
+9.885 0.647 9.819 0.915 9.701 1.073 c
+9.583 1.239 9.4 1.323 9.157 1.323 c
+12.714 -1.691 m
+12.928 -1.691 13.1 -1.627 13.229 -1.5 c
+13.365 -1.363 13.438 -1.172 13.45 -0.926 c
+14.067 -0.926 l
+14.044 -1.309 13.909 -1.627 13.655 -1.881 c
+13.398 -2.128 13.086 -2.249 12.714 -2.249 c
+12.222 -2.249 11.847 -2.098 11.582 -1.793 c
+11.326 -1.481 11.2 -1.014 11.2 -0.397 c
+11.2 0.044 l
+11.2 0.64 11.326 1.095 11.582 1.411 c
+11.847 1.723 12.222 1.881 12.714 1.881 c
+13.115 1.881 13.435 1.749 13.67 1.484 c
+13.913 1.228 14.044 0.882 14.067 0.441 c
+13.45 0.441 l
+13.427 0.735 13.354 0.956 13.229 1.103 c
+13.111 1.249 12.939 1.323 12.714 1.323 c
+12.421 1.323 12.203 1.224 12.068 1.029 c
+11.928 0.842 11.854 0.533 11.847 0.103 c
+11.847 -0.411 l
+11.847 -0.881 11.914 -1.216 12.053 -1.411 c
+12.2 -1.598 12.421 -1.691 12.714 -1.691 c
+15.669 2.764 m
+15.669 1.808 l
+16.272 1.808 l
+16.272 1.278 l
+15.669 1.278 l
+15.669 -1.191 l
+15.669 -1.348 15.691 -1.466 15.742 -1.544 c
+15.802 -1.624 15.889 -1.661 16.007 -1.661 c
+16.095 -1.661 16.184 -1.646 16.272 -1.617 c
+16.272 -2.175 l
+16.124 -2.223 15.97 -2.249 15.816 -2.249 c
+15.559 -2.249 15.364 -2.157 15.228 -1.97 c
+15.089 -1.786 15.022 -1.525 15.022 -1.191 c
+15.022 1.278 l
+14.42 1.278 l
+14.42 1.808 l
+15.022 1.808 l
+15.022 2.764 l
+h
+16.83 0 m
+16.83 0.577 16.966 1.033 17.242 1.367 c
+17.525 1.709 17.896 1.881 18.359 1.881 c
+18.819 1.881 19.185 1.712 19.461 1.382 c
+19.744 1.058 19.891 0.611 19.902 0.044 c
+19.902 -0.382 l
+19.902 -0.952 19.759 -1.407 19.476 -1.749 c
+19.2 -2.084 18.833 -2.249 18.374 -2.249 c
+17.911 -2.249 17.539 -2.088 17.256 -1.764 c
+16.98 -1.433 16.837 -0.992 16.83 -0.441 c
+h
+17.477 -0.382 m
+17.477 -0.786 17.554 -1.103 17.712 -1.338 c
+17.878 -1.573 18.098 -1.691 18.374 -1.691 c
+18.939 -1.691 19.233 -1.278 19.255 -0.455 c
+19.255 0 l
+19.255 0.401 19.171 0.721 19.006 0.956 c
+18.848 1.199 18.63 1.323 18.359 1.323 c
+18.094 1.323 17.878 1.199 17.712 0.956 c
+17.554 0.721 17.477 0.401 17.477 0 c
+h
+22.387 1.191 m
+22.298 1.209 22.199 1.22 22.092 1.22 c
+21.758 1.22 21.523 1.037 21.387 0.676 c
+21.387 -2.175 l
+20.74 -2.175 l
+20.74 1.808 l
+21.372 1.808 l
+21.387 1.396 l
+21.564 1.72 21.805 1.881 22.122 1.881 c
+22.229 1.881 22.316 1.86 22.387 1.823 c
+h
+24.194 -1.087 m
+24.915 1.808 l
+25.605 1.808 l
+24.312 -2.734 l
+24.213 -3.075 24.07 -3.337 23.885 -3.513 c
+23.709 -3.69 23.507 -3.778 23.283 -3.778 c
+23.195 -3.778 23.081 -3.755 22.945 -3.719 c
+22.945 -3.175 l
+23.092 -3.19 l
+23.276 -3.19 23.423 -3.146 23.533 -3.057 c
+23.64 -2.969 23.727 -2.811 23.798 -2.587 c
+23.915 -2.146 l
+22.754 1.808 l
+23.459 1.808 l
+h
+30.482 0 m
+30.482 0.636 30.57 1.239 30.746 1.808 c
+30.923 2.374 31.166 2.87 31.482 3.293 c
+31.677 3.557 31.864 3.748 32.04 3.865 c
+32.172 3.41 l
+31.878 3.135 31.636 2.712 31.452 2.146 c
+31.265 1.577 31.162 0.945 31.143 0.25 c
+31.143 -0.044 l
+31.143 -0.908 31.261 -1.672 31.496 -2.337 c
+31.679 -2.837 31.908 -3.219 32.172 -3.484 c
+32.04 -3.91 l
+31.812 -3.752 31.588 -3.502 31.364 -3.16 c
+30.776 -2.278 30.482 -1.228 30.482 0 c
+34.289 -2.249 m
+33.789 -2.249 33.407 -2.102 33.142 -1.808 c
+32.878 -1.514 32.745 -1.08 32.745 -0.5 c
+32.745 -0.029 l
+32.745 0.566 32.87 1.033 33.128 1.367 c
+33.392 1.709 33.753 1.881 34.216 1.881 c
+34.675 1.881 35.016 1.727 35.245 1.426 c
+35.48 1.132 35.6 0.669 35.612 0.044 c
+35.612 -0.382 l
+33.392 -0.382 l
+33.392 -0.47 l
+33.392 -0.904 33.47 -1.216 33.628 -1.411 c
+33.793 -1.598 34.025 -1.691 34.318 -1.691 c
+34.513 -1.691 34.686 -1.658 34.833 -1.587 c
+34.98 -1.51 35.116 -1.392 35.245 -1.234 c
+35.583 -1.646 l
+35.296 -2.051 34.866 -2.249 34.289 -2.249 c
+34.216 1.323 m
+33.94 1.323 33.738 1.228 33.613 1.043 c
+33.484 0.856 33.41 0.566 33.392 0.177 c
+34.965 0.177 l
+34.965 0.264 l
+34.943 0.647 34.877 0.915 34.759 1.073 c
+34.642 1.239 34.458 1.323 34.216 1.323 c
+37.537 0.353 m
+38.228 1.808 l
+38.978 1.808 l
+37.89 -0.162 l
+39.008 -2.175 l
+38.258 -2.175 l
+37.552 -0.691 l
+36.847 -2.175 l
+36.097 -2.175 l
+37.2 -0.162 l
+36.126 1.808 l
+36.876 1.808 l
+h
+41.007 -1.691 m
+41.219 -1.691 41.393 -1.627 41.52 -1.5 c
+41.657 -1.363 41.73 -1.172 41.742 -0.926 c
+42.359 -0.926 l
+42.337 -1.309 42.201 -1.627 41.948 -1.881 c
+41.69 -2.128 41.377 -2.249 41.007 -2.249 c
+40.514 -2.249 40.139 -2.098 39.874 -1.793 c
+39.617 -1.481 39.492 -1.014 39.492 -0.397 c
+39.492 0.044 l
+39.492 0.64 39.617 1.095 39.874 1.411 c
+40.139 1.723 40.514 1.881 41.007 1.881 c
+41.407 1.881 41.727 1.749 41.962 1.484 c
+42.204 1.228 42.337 0.882 42.359 0.441 c
+41.742 0.441 l
+41.719 0.735 41.646 0.956 41.52 1.103 c
+41.403 1.249 41.231 1.323 41.007 1.323 c
+40.712 1.323 40.496 1.224 40.36 1.029 c
+40.22 0.842 40.146 0.533 40.139 0.103 c
+40.139 -0.411 l
+40.139 -0.881 40.205 -1.216 40.345 -1.411 c
+40.492 -1.598 40.712 -1.691 41.007 -1.691 c
+43.814 -2.175 -0.647 5.644 re
+46.856 -1.822 m
+46.64 -2.109 46.328 -2.249 45.916 -2.249 c
+45.552 -2.249 45.277 -2.128 45.092 -1.881 c
+44.917 -1.627 44.821 -1.264 44.813 -0.794 c
+44.813 1.808 l
+45.46 1.808 l
+45.46 -0.735 l
+45.46 -1.363 45.644 -1.675 46.019 -1.675 c
+46.419 -1.675 46.695 -1.5 46.842 -1.147 c
+46.842 1.808 l
+47.489 1.808 l
+47.489 -2.175 l
+46.871 -2.175 l
+h
+48.341 0 m
+48.341 0.607 48.452 1.073 48.68 1.396 c
+48.915 1.72 49.241 1.881 49.664 1.881 c
+50.046 1.881 50.344 1.723 50.561 1.411 c
+50.561 3.469 l
+51.207 3.469 l
+51.207 -2.175 l
+50.619 -2.175 l
+50.575 -1.749 l
+50.37 -2.084 50.064 -2.249 49.664 -2.249 c
+49.252 -2.249 48.929 -2.094 48.694 -1.779 c
+48.458 -1.455 48.341 -0.999 48.341 -0.411 c
+h
+48.988 -0.382 m
+48.988 -0.823 49.05 -1.153 49.179 -1.367 c
+49.315 -1.573 49.536 -1.675 49.84 -1.675 c
+50.164 -1.675 50.403 -1.514 50.561 -1.191 c
+50.561 0.823 l
+50.392 1.135 50.153 1.294 49.84 1.294 c
+49.536 1.294 49.315 1.191 49.179 0.985 c
+49.05 0.779 48.988 0.455 48.988 0.015 c
+h
+52.913 -2.175 -0.647 3.983 re
+52.957 2.851 m
+52.957 2.741 52.927 2.65 52.869 2.572 c
+52.809 2.502 52.714 2.469 52.589 2.469 c
+52.472 2.469 52.376 2.502 52.31 2.572 c
+52.251 2.65 52.221 2.741 52.221 2.851 c
+52.221 2.969 52.251 3.061 52.31 3.131 c
+52.376 3.208 52.472 3.248 52.589 3.248 c
+52.714 3.248 52.809 3.208 52.869 3.131 c
+52.927 3.05 52.957 2.959 52.957 2.851 c
+54.544 1.808 m
+54.559 1.367 l
+54.812 1.709 55.136 1.881 55.529 1.881 c
+56.235 1.881 56.591 1.411 56.602 0.47 c
+56.602 -2.175 l
+55.955 -2.175 l
+55.955 0.441 l
+55.955 0.754 55.901 0.974 55.793 1.103 c
+55.683 1.228 55.529 1.294 55.323 1.294 c
+55.165 1.294 55.018 1.239 54.883 1.132 c
+54.754 1.022 54.65 0.886 54.573 0.721 c
+54.573 -2.175 l
+53.927 -2.175 l
+53.927 1.808 l
+h
+57.44 0 m
+57.44 0.617 57.55 1.081 57.778 1.396 c
+58.002 1.72 58.336 1.881 58.777 1.881 c
+59.178 1.881 59.483 1.706 59.689 1.353 c
+59.733 1.808 l
+60.321 1.808 l
+60.321 -2.219 l
+60.321 -2.708 60.192 -3.087 59.939 -3.351 c
+59.681 -3.616 59.329 -3.748 58.881 -3.748 c
+58.682 -3.748 58.461 -3.697 58.219 -3.601 c
+57.973 -3.502 57.792 -3.381 57.675 -3.233 c
+57.94 -2.793 l
+58.204 -3.057 58.502 -3.19 58.837 -3.19 c
+59.373 -3.19 59.649 -2.896 59.66 -2.308 c
+59.66 -1.779 l
+59.454 -2.094 59.153 -2.249 58.763 -2.249 c
+58.351 -2.249 58.028 -2.098 57.792 -1.793 c
+57.565 -1.481 57.447 -1.029 57.44 -0.441 c
+h
+58.101 -0.382 m
+58.101 -0.823 58.164 -1.153 58.293 -1.367 c
+58.417 -1.573 58.634 -1.675 58.939 -1.675 c
+59.263 -1.675 59.502 -1.51 59.66 -1.176 c
+59.66 0.808 l
+59.49 1.132 59.252 1.294 58.939 1.294 c
+58.646 1.294 58.428 1.191 58.293 0.985 c
+58.164 0.779 58.101 0.455 58.101 0.015 c
+h
+f
+Q
+q 1 0 0 1 38.464 110.9585 cm
+0 0 m
+0 -0.956 l
+0.603 -0.956 l
+0.603 -1.485 l
+0 -1.485 l
+0 -3.955 l
+0 -4.112 0.023 -4.23 0.073 -4.308 c
+0.133 -4.388 0.221 -4.425 0.339 -4.425 c
+0.426 -4.425 0.515 -4.41 0.603 -4.381 c
+0.603 -4.939 l
+0.456 -4.987 0.302 -5.013 0.148 -5.013 c
+-0.11 -5.013 -0.305 -4.921 -0.44 -4.734 c
+-0.58 -4.549 -0.646 -4.289 -0.646 -3.955 c
+-0.646 -1.485 l
+-1.249 -1.485 l
+-1.249 -0.956 l
+-0.646 -0.956 l
+-0.646 0 l
+h
+2.014 -1.368 m
+2.267 -1.044 2.587 -0.882 2.97 -0.882 c
+3.675 -0.882 4.031 -1.353 4.042 -2.294 c
+4.042 -4.939 l
+3.396 -4.939 l
+3.396 -2.323 l
+3.396 -2.01 3.341 -1.79 3.234 -1.661 c
+3.124 -1.536 2.97 -1.47 2.764 -1.47 c
+2.606 -1.47 2.459 -1.525 2.323 -1.632 c
+2.194 -1.742 2.091 -1.878 2.014 -2.043 c
+2.014 -4.939 l
+1.367 -4.939 l
+1.367 0.706 l
+2.014 0.706 l
+h
+6.423 -5.013 m
+5.924 -5.013 5.542 -4.866 5.278 -4.572 c
+5.012 -4.278 4.881 -3.844 4.881 -3.263 c
+4.881 -2.793 l
+4.881 -2.198 5.006 -1.731 5.262 -1.397 c
+5.527 -1.055 5.887 -0.882 6.35 -0.882 c
+6.81 -0.882 7.152 -1.037 7.379 -1.338 c
+7.614 -1.632 7.736 -2.095 7.747 -2.72 c
+7.747 -3.146 l
+5.527 -3.146 l
+5.527 -3.234 l
+5.527 -3.668 5.604 -3.98 5.762 -4.175 c
+5.928 -4.362 6.159 -4.454 6.453 -4.454 c
+6.648 -4.454 6.82 -4.421 6.968 -4.351 c
+7.115 -4.274 7.25 -4.156 7.379 -3.998 c
+7.717 -4.41 l
+7.431 -4.815 7.001 -5.013 6.423 -5.013 c
+6.35 -1.441 m
+6.074 -1.441 5.872 -1.536 5.748 -1.721 c
+5.619 -1.908 5.546 -2.198 5.527 -2.587 c
+7.1 -2.587 l
+7.1 -2.5 l
+7.078 -2.117 7.011 -1.849 6.894 -1.691 c
+6.776 -1.525 6.593 -1.441 6.35 -1.441 c
+f
+Q
+q 1 0 0 1 50.5656 106.5483 cm
+0 0 m
+0 0.166 0.051 0.301 0.161 0.412 c
+0.268 0.518 0.415 0.573 0.602 0.573 c
+0.768 0.573 0.911 0.518 1.028 0.412 c
+1.146 0.301 1.205 0.166 1.205 0 c
+1.205 -0.169 1.146 -0.305 1.028 -0.411 c
+0.911 -0.522 0.768 -0.573 0.602 -0.573 c
+0.426 -0.573 0.278 -0.522 0.161 -0.411 c
+0.051 -0.305 0 -0.169 0 0 c
+1.969 1.588 m
+1.969 2.234 2.087 2.72 2.322 3.042 c
+2.557 3.366 2.888 3.528 3.322 3.528 c
+3.675 3.528 3.946 3.385 4.145 3.102 c
+4.189 3.454 l
+5.13 3.454 l
+5.13 -0.529 l
+5.13 -1.036 4.986 -1.426 4.704 -1.691 c
+4.417 -1.962 4.012 -2.102 3.484 -2.102 c
+3.256 -2.102 3.021 -2.057 2.778 -1.97 c
+2.543 -1.881 2.366 -1.768 2.248 -1.631 c
+2.601 -0.912 l
+2.697 -1.018 2.826 -1.103 2.983 -1.161 c
+3.138 -1.228 3.285 -1.264 3.424 -1.264 c
+3.659 -1.264 3.825 -1.205 3.924 -1.087 c
+4.031 -0.977 4.086 -0.801 4.086 -0.559 c
+4.086 -0.206 l
+3.887 -0.47 3.63 -0.603 3.307 -0.603 c
+2.884 -0.603 2.557 -0.441 2.322 -0.118 c
+2.094 0.214 1.977 0.684 1.969 1.294 c
+h
+3.013 1.323 m
+3.013 0.948 3.061 0.68 3.16 0.515 c
+3.256 0.345 3.41 0.264 3.615 0.264 c
+3.829 0.264 3.987 0.341 4.086 0.5 c
+4.086 2.396 l
+3.976 2.562 3.821 2.646 3.615 2.646 c
+3.41 2.646 3.256 2.562 3.16 2.396 c
+3.061 2.227 3.013 1.959 3.013 1.588 c
+h
+6.981 -0.529 -1.043 3.983 re
+5.894 4.484 m
+5.894 4.638 5.942 4.767 6.04 4.866 c
+6.148 4.972 6.283 5.027 6.453 5.027 c
+6.628 5.027 6.765 4.972 6.864 4.866 c
+6.971 4.767 7.025 4.638 7.025 4.484 c
+7.025 4.314 6.971 4.179 6.864 4.072 c
+6.765 3.973 6.628 3.925 6.453 3.925 c
+6.283 3.925 6.148 3.973 6.04 4.072 c
+5.942 4.179 5.894 4.314 5.894 4.484 c
+8.995 4.424 m
+8.995 3.454 l
+9.525 3.454 l
+9.525 2.66 l
+8.995 2.66 l
+8.995 0.69 l
+8.995 0.533 9.014 0.426 9.055 0.368 c
+9.102 0.309 9.186 0.279 9.304 0.279 c
+9.411 0.279 9.495 0.287 9.554 0.309 c
+9.554 -0.5 l
+9.377 -0.565 9.186 -0.603 8.98 -0.603 c
+8.305 -0.603 7.96 -0.216 7.952 0.559 c
+7.952 2.66 l
+7.496 2.66 l
+7.496 3.454 l
+7.952 3.454 l
+7.952 4.424 l
+h
+11.421 1 m
+11.141 0.69 l
+11.141 -0.529 l
+10.098 -0.529 l
+10.098 5.116 l
+11.141 5.116 l
+11.141 2.072 l
+11.259 2.263 l
+11.979 3.454 l
+13.229 3.454 l
+12.068 1.808 l
+13.331 -0.529 l
+12.141 -0.529 l
+h
+15.228 -0.603 m
+14.699 -0.603 14.279 -0.448 13.978 -0.133 c
+13.684 0.191 13.537 0.651 13.537 1.249 c
+13.537 1.558 l
+13.537 2.183 13.674 2.668 13.949 3.013 c
+14.221 3.356 14.614 3.528 15.125 3.528 c
+15.625 3.528 15.996 3.366 16.242 3.042 c
+16.496 2.72 16.628 2.242 16.639 1.617 c
+16.639 1.118 l
+14.566 1.118 l
+14.585 0.823 14.647 0.607 14.757 0.47 c
+14.875 0.331 15.056 0.264 15.301 0.264 c
+15.643 0.264 15.933 0.382 16.168 0.617 c
+16.581 -0.015 l
+16.452 -0.191 16.264 -0.334 16.022 -0.441 c
+15.775 -0.548 15.511 -0.603 15.228 -0.603 c
+14.581 1.837 m
+15.61 1.837 l
+15.61 1.941 l
+15.61 2.176 15.569 2.352 15.492 2.469 c
+15.422 2.595 15.294 2.66 15.11 2.66 c
+14.934 2.66 14.802 2.591 14.713 2.454 c
+14.632 2.326 14.588 2.12 14.581 1.837 c
+18.829 -0.603 m
+18.3 -0.603 17.881 -0.448 17.58 -0.133 c
+17.286 0.191 17.138 0.651 17.138 1.249 c
+17.138 1.558 l
+17.138 2.183 17.275 2.668 17.55 3.013 c
+17.822 3.356 18.216 3.528 18.726 3.528 c
+19.226 3.528 19.597 3.366 19.843 3.042 c
+20.097 2.72 20.23 2.242 20.24 1.617 c
+20.24 1.118 l
+18.168 1.118 l
+18.186 0.823 18.248 0.607 18.359 0.47 c
+18.476 0.331 18.657 0.264 18.902 0.264 c
+19.245 0.264 19.534 0.382 19.77 0.617 c
+20.182 -0.015 l
+20.053 -0.191 19.866 -0.334 19.623 -0.441 c
+19.376 -0.548 19.112 -0.603 18.829 -0.603 c
+18.183 1.837 m
+19.211 1.837 l
+19.211 1.941 l
+19.211 2.176 19.17 2.352 19.093 2.469 c
+19.024 2.595 18.896 2.66 18.711 2.66 c
+18.535 2.66 18.403 2.591 18.314 2.454 c
+18.233 2.326 18.19 2.12 18.183 1.837 c
+23.93 1.338 m
+23.93 0.709 23.82 0.228 23.606 -0.103 c
+23.39 -0.437 23.07 -0.603 22.651 -0.603 c
+22.328 -0.603 22.067 -0.47 21.872 -0.206 c
+21.872 -2.057 l
+20.828 -2.057 l
+20.828 3.454 l
+21.784 3.454 l
+21.828 3.087 l
+22.023 3.381 22.291 3.528 22.636 3.528 c
+23.055 3.528 23.375 3.37 23.592 3.057 c
+23.804 2.753 23.918 2.282 23.93 1.646 c
+h
+22.886 1.602 m
+22.886 1.984 22.842 2.253 22.754 2.411 c
+22.673 2.565 22.534 2.646 22.328 2.646 c
+22.121 2.646 21.967 2.558 21.872 2.381 c
+21.872 0.515 l
+21.96 0.345 22.114 0.264 22.343 0.264 c
+22.548 0.264 22.688 0.345 22.769 0.515 c
+22.846 0.69 22.886 0.963 22.886 1.338 c
+h
+f
+Q
+q 1 0 0 1 78.9929 106.0196 cm
+0 0 m
+0 3.453 l
+-0.515 3.453 l
+-0.515 3.983 l
+0 3.983 l
+0 4.351 l
+0.008 4.781 0.121 5.115 0.339 5.35 c
+0.563 5.593 0.875 5.718 1.279 5.718 c
+1.426 5.718 1.565 5.695 1.706 5.659 c
+1.852 5.618 2.003 5.564 2.161 5.497 c
+2.043 4.924 l
+1.808 5.049 1.565 5.115 1.324 5.115 c
+1.077 5.115 0.904 5.045 0.809 4.909 c
+0.709 4.781 0.661 4.586 0.661 4.321 c
+0.661 3.983 l
+1.309 3.983 l
+1.309 3.453 l
+0.661 3.453 l
+0.661 0 l
+h
+2.469 0 -0.646 3.983 re
+4.219 0 -0.647 5.644 re
+6.659 -0.074 m
+6.159 -0.074 5.777 0.073 5.513 0.367 c
+5.247 0.661 5.116 1.095 5.116 1.675 c
+5.116 2.146 l
+5.116 2.741 5.241 3.208 5.498 3.542 c
+5.762 3.884 6.122 4.056 6.585 4.056 c
+7.045 4.056 7.387 3.902 7.614 3.601 c
+7.85 3.307 7.971 2.844 7.982 2.219 c
+7.982 1.793 l
+5.762 1.793 l
+5.762 1.705 l
+5.762 1.271 5.839 0.959 5.997 0.764 c
+6.163 0.577 6.394 0.484 6.689 0.484 c
+6.883 0.484 7.056 0.517 7.203 0.588 c
+7.35 0.665 7.485 0.783 7.614 0.941 c
+7.952 0.529 l
+7.666 0.124 7.236 -0.074 6.659 -0.074 c
+6.585 3.498 m
+6.31 3.498 6.108 3.403 5.983 3.218 c
+5.854 3.031 5.781 2.741 5.762 2.352 c
+7.335 2.352 l
+7.335 2.439 l
+7.313 2.822 7.247 3.09 7.129 3.248 c
+7.011 3.414 6.828 3.498 6.585 3.498 c
+10.157 2.131 m
+10.157 1.043 9.912 0.077 9.422 -0.765 c
+9.158 -1.213 8.882 -1.536 8.599 -1.735 c
+8.482 -1.309 l
+8.783 -1.015 9.029 -0.563 9.216 0.043 c
+9.411 0.65 9.511 1.315 9.511 2.042 c
+9.511 2.175 l
+9.511 3.104 9.357 3.939 9.055 4.674 c
+8.886 5.074 8.695 5.394 8.482 5.629 c
+8.599 6.04 l
+8.871 5.853 9.136 5.556 9.393 5.144 c
+9.9 4.292 10.157 3.285 10.157 2.131 c
+11.171 -1.073 m
+10.774 -0.809 l
+11.009 -0.485 11.131 -0.151 11.142 0.191 c
+11.142 0.808 l
+11.803 0.808 l
+11.803 0.278 l
+11.803 0.022 11.738 -0.225 11.612 -0.47 c
+11.495 -0.713 11.348 -0.915 11.171 -1.073 c
+19.198 1.146 m
+19.8 3.983 l
+20.447 3.983 l
+19.462 0 l
+18.947 0 l
+18.169 2.851 l
+17.419 0 l
+16.889 0 l
+15.934 3.983 l
+16.566 3.983 l
+17.184 1.219 l
+17.918 3.983 l
+18.433 3.983 l
+h
+21.77 3.571 m
+22.023 3.895 22.343 4.056 22.725 4.056 c
+23.431 4.056 23.788 3.586 23.798 2.645 c
+23.798 0 l
+23.152 0 l
+23.152 2.616 l
+23.152 2.929 23.096 3.149 22.99 3.278 c
+22.88 3.403 22.725 3.469 22.519 3.469 c
+22.361 3.469 22.215 3.414 22.078 3.307 c
+21.949 3.197 21.847 3.061 21.77 2.896 c
+21.77 0 l
+21.122 0 l
+21.122 5.644 l
+21.77 5.644 l
+h
+24.621 2.175 m
+24.621 2.752 24.757 3.208 25.033 3.542 c
+25.315 3.884 25.687 4.056 26.15 4.056 c
+26.61 4.056 26.977 3.887 27.252 3.557 c
+27.535 3.233 27.682 2.786 27.693 2.219 c
+27.693 1.793 l
+27.693 1.223 27.551 0.768 27.267 0.426 c
+26.992 0.091 26.624 -0.074 26.165 -0.074 c
+25.702 -0.074 25.33 0.087 25.047 0.411 c
+24.772 0.742 24.629 1.183 24.621 1.734 c
+h
+25.268 1.793 m
+25.268 1.389 25.345 1.072 25.503 0.837 c
+25.669 0.602 25.889 0.484 26.165 0.484 c
+26.73 0.484 27.025 0.897 27.046 1.72 c
+27.046 2.175 l
+27.046 2.576 26.962 2.896 26.797 3.131 c
+26.639 3.373 26.422 3.498 26.15 3.498 c
+25.885 3.498 25.669 3.373 25.503 3.131 c
+25.345 2.896 25.268 2.576 25.268 2.175 c
+h
+29.236 0 -0.646 5.644 re
+31.677 -0.074 m
+31.177 -0.074 30.795 0.073 30.53 0.367 c
+30.265 0.661 30.134 1.095 30.134 1.675 c
+30.134 2.146 l
+30.134 2.741 30.258 3.208 30.516 3.542 c
+30.78 3.884 31.14 4.056 31.603 4.056 c
+32.062 4.056 32.405 3.902 32.632 3.601 c
+32.868 3.307 32.989 2.844 32.999 2.219 c
+32.999 1.793 l
+30.78 1.793 l
+30.78 1.705 l
+30.78 1.271 30.857 0.959 31.015 0.764 c
+31.181 0.577 31.412 0.484 31.706 0.484 c
+31.9 0.484 32.074 0.517 32.22 0.588 c
+32.368 0.665 32.503 0.783 32.632 0.941 c
+32.97 0.529 l
+32.684 0.124 32.254 -0.074 31.677 -0.074 c
+31.603 3.498 m
+31.328 3.498 31.125 3.403 31 3.218 c
+30.872 3.031 30.799 2.741 30.78 2.352 c
+32.353 2.352 l
+32.353 2.439 l
+32.331 2.822 32.265 3.09 32.147 3.248 c
+32.029 3.414 31.846 3.498 31.603 3.498 c
+f
+Q
+q 1 0 0 1 117.4607 110.9726 cm
+0 0 m
+0 -0.97 l
+0.529 -0.97 l
+0.529 -1.764 l
+0 -1.764 l
+0 -3.734 l
+0 -3.891 0.018 -3.998 0.058 -4.056 c
+0.106 -4.116 0.191 -4.145 0.309 -4.145 c
+0.415 -4.145 0.5 -4.137 0.559 -4.116 c
+0.559 -4.924 l
+0.382 -4.99 0.191 -5.027 -0.015 -5.027 c
+-0.691 -5.027 -1.037 -4.641 -1.043 -3.865 c
+-1.043 -1.764 l
+-1.5 -1.764 l
+-1.5 -0.97 l
+-1.043 -0.97 l
+-1.043 0 l
+h
+2.072 -0.97 m
+2.102 -1.338 l
+2.337 -1.043 2.645 -0.897 3.028 -0.897 c
+3.429 -0.897 3.707 -1.08 3.866 -1.44 c
+4.101 -1.08 4.428 -0.897 4.85 -0.897 c
+5.546 -0.897 5.897 -1.382 5.909 -2.352 c
+5.909 -4.953 l
+4.88 -4.953 l
+4.88 -2.41 l
+4.88 -2.186 4.843 -2.024 4.777 -1.926 c
+4.719 -1.83 4.608 -1.778 4.453 -1.778 c
+4.255 -1.778 4.116 -1.896 4.027 -2.131 c
+4.027 -4.953 l
+2.984 -4.953 l
+2.984 -2.425 l
+2.984 -2.19 2.955 -2.024 2.896 -1.926 c
+2.837 -1.83 2.726 -1.778 2.572 -1.778 c
+2.396 -1.778 2.253 -1.874 2.146 -2.057 c
+2.146 -4.953 l
+1.103 -4.953 l
+1.103 -0.97 l
+h
+9.789 -3.087 m
+9.789 -3.715 9.679 -4.197 9.466 -4.527 c
+9.249 -4.862 8.93 -5.027 8.511 -5.027 c
+8.187 -5.027 7.927 -4.895 7.732 -4.63 c
+7.732 -6.482 l
+6.688 -6.482 l
+6.688 -0.97 l
+7.644 -0.97 l
+7.688 -1.338 l
+7.882 -1.043 8.151 -0.897 8.496 -0.897 c
+8.915 -0.897 9.234 -1.055 9.452 -1.367 c
+9.664 -1.672 9.779 -2.142 9.789 -2.778 c
+h
+8.746 -2.822 m
+8.746 -2.44 8.702 -2.171 8.613 -2.013 c
+8.533 -1.859 8.393 -1.778 8.187 -1.778 c
+7.981 -1.778 7.827 -1.866 7.732 -2.043 c
+7.732 -3.91 l
+7.819 -4.079 7.975 -4.16 8.202 -4.16 c
+8.407 -4.16 8.548 -4.079 8.628 -3.91 c
+8.706 -3.734 8.746 -3.461 8.746 -3.087 c
+h
+f
+Q
+q 1 0 0 1 131.4391 108.1946 cm
+0 0 m
+0 0.607 0.111 1.073 0.339 1.396 c
+0.574 1.72 0.9 1.881 1.324 1.881 c
+1.706 1.881 2.003 1.723 2.22 1.411 c
+2.22 3.469 l
+2.866 3.469 l
+2.866 -2.175 l
+2.278 -2.175 l
+2.234 -1.749 l
+2.029 -2.084 1.723 -2.249 1.324 -2.249 c
+0.912 -2.249 0.588 -2.094 0.353 -1.779 c
+0.118 -1.455 0 -0.999 0 -0.411 c
+h
+0.647 -0.382 m
+0.647 -0.823 0.709 -1.153 0.838 -1.367 c
+0.975 -1.573 1.195 -1.675 1.5 -1.675 c
+1.823 -1.675 2.062 -1.514 2.22 -1.191 c
+2.22 0.823 l
+2.051 1.135 1.812 1.294 1.5 1.294 c
+1.195 1.294 0.975 1.191 0.838 0.985 c
+0.709 0.779 0.647 0.455 0.647 0.015 c
+h
+4.572 -2.175 -0.647 3.983 re
+4.616 2.851 m
+4.616 2.741 4.586 2.65 4.528 2.572 c
+4.469 2.502 4.374 2.469 4.248 2.469 c
+4.131 2.469 4.035 2.502 3.969 2.572 c
+3.911 2.65 3.881 2.741 3.881 2.851 c
+3.881 2.969 3.911 3.061 3.969 3.131 c
+4.035 3.208 4.131 3.248 4.248 3.248 c
+4.374 3.248 4.469 3.208 4.528 3.131 c
+4.586 3.05 4.616 2.959 4.616 2.851 c
+7.232 1.191 m
+7.144 1.209 7.045 1.22 6.938 1.22 c
+6.604 1.22 6.369 1.037 6.232 0.676 c
+6.232 -2.175 l
+5.586 -2.175 l
+5.586 1.808 l
+6.218 1.808 l
+6.232 1.396 l
+6.409 1.72 6.652 1.881 6.968 1.881 c
+7.074 1.881 7.163 1.86 7.232 1.823 c
+h
+9.231 -2.249 m
+8.732 -2.249 8.349 -2.102 8.085 -1.808 c
+7.82 -1.514 7.688 -1.08 7.688 -0.5 c
+7.688 -0.029 l
+7.688 0.566 7.813 1.033 8.07 1.367 c
+8.335 1.709 8.695 1.881 9.158 1.881 c
+9.617 1.881 9.959 1.727 10.186 1.426 c
+10.422 1.132 10.543 0.669 10.554 0.044 c
+10.554 -0.382 l
+8.335 -0.382 l
+8.335 -0.47 l
+8.335 -0.904 8.412 -1.216 8.57 -1.411 c
+8.736 -1.598 8.967 -1.691 9.261 -1.691 c
+9.455 -1.691 9.628 -1.658 9.775 -1.587 c
+9.922 -1.51 10.058 -1.392 10.186 -1.234 c
+10.525 -1.646 l
+10.238 -2.051 9.808 -2.249 9.231 -2.249 c
+9.158 1.323 m
+8.882 1.323 8.68 1.228 8.555 1.043 c
+8.426 0.856 8.353 0.566 8.335 0.177 c
+9.908 0.177 l
+9.908 0.264 l
+9.885 0.647 9.819 0.915 9.702 1.073 c
+9.584 1.239 9.401 1.323 9.158 1.323 c
+12.715 -1.691 m
+12.929 -1.691 13.101 -1.627 13.23 -1.5 c
+13.365 -1.363 13.439 -1.172 13.45 -0.926 c
+14.067 -0.926 l
+14.045 -1.309 13.91 -1.627 13.656 -1.881 c
+13.399 -2.128 13.086 -2.249 12.715 -2.249 c
+12.223 -2.249 11.848 -2.098 11.583 -1.793 c
+11.326 -1.481 11.201 -1.014 11.201 -0.397 c
+11.201 0.044 l
+11.201 0.64 11.326 1.095 11.583 1.411 c
+11.848 1.723 12.223 1.881 12.715 1.881 c
+13.116 1.881 13.436 1.749 13.671 1.484 c
+13.913 1.228 14.045 0.882 14.067 0.441 c
+13.45 0.441 l
+13.428 0.735 13.355 0.956 13.23 1.103 c
+13.112 1.249 12.939 1.323 12.715 1.323 c
+12.421 1.323 12.204 1.224 12.068 1.029 c
+11.929 0.842 11.855 0.533 11.848 0.103 c
+11.848 -0.411 l
+11.848 -0.881 11.914 -1.216 12.054 -1.411 c
+12.2 -1.598 12.421 -1.691 12.715 -1.691 c
+15.67 2.764 m
+15.67 1.808 l
+16.272 1.808 l
+16.272 1.278 l
+15.67 1.278 l
+15.67 -1.191 l
+15.67 -1.348 15.692 -1.466 15.743 -1.544 c
+15.802 -1.624 15.89 -1.661 16.008 -1.661 c
+16.096 -1.661 16.184 -1.646 16.272 -1.617 c
+16.272 -2.175 l
+16.125 -2.223 15.971 -2.249 15.817 -2.249 c
+15.559 -2.249 15.364 -2.157 15.229 -1.97 c
+15.089 -1.786 15.023 -1.525 15.023 -1.191 c
+15.023 1.278 l
+14.42 1.278 l
+14.42 1.808 l
+15.023 1.808 l
+15.023 2.764 l
+h
+16.831 0 m
+16.831 0.577 16.967 1.033 17.242 1.367 c
+17.525 1.709 17.897 1.881 18.359 1.881 c
+18.819 1.881 19.186 1.712 19.462 1.382 c
+19.745 1.058 19.892 0.611 19.903 0.044 c
+19.903 -0.382 l
+19.903 -0.952 19.759 -1.407 19.476 -1.749 c
+19.201 -2.084 18.834 -2.249 18.375 -2.249 c
+17.911 -2.249 17.54 -2.087 17.257 -1.764 c
+16.981 -1.433 16.838 -0.992 16.831 -0.441 c
+h
+17.477 -0.382 m
+17.477 -0.786 17.554 -1.103 17.712 -1.338 c
+17.878 -1.573 18.099 -1.691 18.375 -1.691 c
+18.94 -1.691 19.234 -1.278 19.256 -0.455 c
+19.256 0 l
+19.256 0.401 19.171 0.721 19.006 0.956 c
+18.849 1.199 18.631 1.323 18.359 1.323 c
+18.095 1.323 17.878 1.199 17.712 0.956 c
+17.554 0.721 17.477 0.401 17.477 0 c
+h
+22.387 1.191 m
+22.298 1.209 22.2 1.22 22.093 1.22 c
+21.758 1.22 21.523 1.037 21.388 0.676 c
+21.388 -2.175 l
+20.74 -2.175 l
+20.74 1.808 l
+21.373 1.808 l
+21.388 1.396 l
+21.564 1.72 21.806 1.881 22.122 1.881 c
+22.229 1.881 22.317 1.86 22.387 1.823 c
+h
+24.195 -1.087 m
+24.915 1.808 l
+25.606 1.808 l
+24.312 -2.734 l
+24.214 -3.075 24.07 -3.337 23.886 -3.513 c
+23.709 -3.69 23.508 -3.778 23.283 -3.778 c
+23.196 -3.778 23.082 -3.755 22.946 -3.719 c
+22.946 -3.175 l
+23.092 -3.19 l
+23.277 -3.19 23.423 -3.146 23.534 -3.057 c
+23.64 -2.969 23.728 -2.811 23.798 -2.587 c
+23.915 -2.146 l
+22.755 1.808 l
+23.46 1.808 l
+h
+31.871 -2.175 m
+31.831 -2.087 31.805 -1.94 31.798 -1.735 c
+31.563 -2.08 31.269 -2.249 30.916 -2.249 c
+30.552 -2.249 30.269 -2.153 30.063 -1.955 c
+29.865 -1.749 29.77 -1.463 29.77 -1.087 c
+29.77 -0.687 29.905 -0.368 30.181 -0.133 c
+30.453 0.11 30.828 0.235 31.298 0.235 c
+31.784 0.235 l
+31.784 0.661 l
+31.784 0.897 31.728 1.062 31.622 1.161 c
+31.511 1.268 31.35 1.323 31.137 1.323 c
+30.938 1.323 30.776 1.264 30.651 1.147 c
+30.534 1.029 30.475 0.882 30.475 0.706 c
+29.828 0.706 l
+29.828 0.9 29.887 1.091 30.005 1.278 c
+30.13 1.463 30.292 1.61 30.489 1.72 c
+30.695 1.827 30.923 1.881 31.181 1.881 c
+31.582 1.881 31.886 1.779 32.092 1.573 c
+32.305 1.367 32.419 1.073 32.43 0.691 c
+32.43 -1.323 l
+32.43 -1.627 32.467 -1.893 32.548 -2.117 c
+32.548 -2.175 l
+h
+31.004 -1.661 m
+31.17 -1.661 31.32 -1.617 31.46 -1.529 c
+31.607 -1.44 31.713 -1.33 31.784 -1.191 c
+31.784 -0.25 l
+31.416 -0.25 l
+31.1 -0.25 30.857 -0.32 30.681 -0.455 c
+30.504 -0.584 30.416 -0.771 30.416 -1.014 c
+30.416 -1.242 30.46 -1.407 30.549 -1.514 c
+30.637 -1.613 30.788 -1.661 31.004 -1.661 c
+34.047 1.808 m
+34.061 1.367 l
+34.315 1.709 34.639 1.881 35.032 1.881 c
+35.738 1.881 36.094 1.411 36.105 0.47 c
+36.105 -2.175 l
+35.458 -2.175 l
+35.458 0.441 l
+35.458 0.754 35.403 0.974 35.297 1.103 c
+35.186 1.228 35.032 1.294 34.826 1.294 c
+34.668 1.294 34.521 1.239 34.385 1.132 c
+34.256 1.022 34.154 0.886 34.076 0.721 c
+34.076 -2.175 l
+33.43 -2.175 l
+33.43 1.808 l
+h
+36.943 0 m
+36.943 0.607 37.053 1.073 37.281 1.396 c
+37.516 1.72 37.843 1.881 38.266 1.881 c
+38.647 1.881 38.946 1.723 39.162 1.411 c
+39.162 3.469 l
+39.809 3.469 l
+39.809 -2.175 l
+39.221 -2.175 l
+39.177 -1.749 l
+38.971 -2.084 38.666 -2.249 38.266 -2.249 c
+37.854 -2.249 37.531 -2.094 37.296 -1.779 c
+37.06 -1.455 36.943 -0.999 36.943 -0.411 c
+h
+37.589 -0.382 m
+37.589 -0.823 37.652 -1.153 37.78 -1.367 c
+37.917 -1.573 38.137 -1.675 38.442 -1.675 c
+38.765 -1.675 39.004 -1.514 39.162 -1.191 c
+39.162 0.823 l
+38.994 1.135 38.755 1.294 38.442 1.294 c
+38.137 1.294 37.917 1.191 37.78 0.985 c
+37.652 0.779 37.589 0.455 37.589 0.015 c
+h
+46.439 -2.175 m
+46.398 -2.087 46.372 -1.94 46.365 -1.735 c
+46.13 -2.08 45.836 -2.249 45.483 -2.249 c
+45.119 -2.249 44.836 -2.153 44.631 -1.955 c
+44.432 -1.749 44.336 -1.463 44.336 -1.087 c
+44.336 -0.687 44.473 -0.368 44.748 -0.133 c
+45.02 0.11 45.394 0.235 45.865 0.235 c
+46.35 0.235 l
+46.35 0.661 l
+46.35 0.897 46.295 1.062 46.188 1.161 c
+46.078 1.268 45.917 1.323 45.703 1.323 c
+45.505 1.323 45.344 1.264 45.219 1.147 c
+45.101 1.029 45.042 0.882 45.042 0.706 c
+44.395 0.706 l
+44.395 0.9 44.454 1.091 44.571 1.278 c
+44.696 1.463 44.858 1.61 45.057 1.72 c
+45.263 1.827 45.49 1.881 45.747 1.881 c
+46.148 1.881 46.453 1.779 46.659 1.573 c
+46.872 1.367 46.986 1.073 46.997 0.691 c
+46.997 -1.323 l
+46.997 -1.627 47.033 -1.893 47.114 -2.117 c
+47.114 -2.175 l
+h
+45.571 -1.661 m
+45.737 -1.661 45.887 -1.617 46.027 -1.529 c
+46.173 -1.44 46.281 -1.33 46.35 -1.191 c
+46.35 -0.25 l
+45.982 -0.25 l
+45.666 -0.25 45.425 -0.32 45.248 -0.455 c
+45.071 -0.584 44.983 -0.771 44.983 -1.014 c
+44.983 -1.242 45.028 -1.407 45.115 -1.514 c
+45.203 -1.613 45.354 -1.661 45.571 -1.661 c
+48.702 -2.175 -0.647 5.644 re
+50.422 -2.175 -0.647 5.644 re
+55.199 -2.175 m
+55.199 1.278 l
+54.684 1.278 l
+54.684 1.808 l
+55.199 1.808 l
+55.199 2.176 l
+55.206 2.606 55.32 2.94 55.538 3.175 c
+55.761 3.418 56.074 3.543 56.478 3.543 c
+56.625 3.543 56.764 3.52 56.904 3.484 c
+57.051 3.443 57.202 3.389 57.36 3.322 c
+57.242 2.749 l
+57.007 2.874 56.764 2.94 56.521 2.94 c
+56.276 2.94 56.103 2.87 56.008 2.734 c
+55.908 2.606 55.86 2.411 55.86 2.146 c
+55.86 1.808 l
+56.507 1.808 l
+56.507 1.278 l
+55.86 1.278 l
+55.86 -2.175 l
+h
+57.668 -2.175 -0.646 3.983 re
+59.417 -2.175 -0.646 5.644 re
+61.857 -2.249 m
+61.358 -2.249 60.976 -2.102 60.712 -1.808 c
+60.446 -1.514 60.315 -1.08 60.315 -0.5 c
+60.315 -0.029 l
+60.315 0.566 60.439 1.033 60.697 1.367 c
+60.961 1.709 61.321 1.881 61.784 1.881 c
+62.243 1.881 62.586 1.727 62.813 1.426 c
+63.048 1.132 63.17 0.669 63.18 0.044 c
+63.18 -0.382 l
+60.961 -0.382 l
+60.961 -0.47 l
+60.961 -0.904 61.038 -1.216 61.196 -1.411 c
+61.362 -1.598 61.593 -1.691 61.887 -1.691 c
+62.082 -1.691 62.254 -1.658 62.401 -1.587 c
+62.549 -1.51 62.684 -1.392 62.813 -1.234 c
+63.151 -1.646 l
+62.865 -2.051 62.435 -2.249 61.857 -2.249 c
+61.784 1.323 m
+61.508 1.323 61.306 1.228 61.182 1.043 c
+61.053 0.856 60.98 0.566 60.961 0.177 c
+62.534 0.177 l
+62.534 0.264 l
+62.512 0.647 62.445 0.915 62.328 1.073 c
+62.21 1.239 62.027 1.323 61.784 1.323 c
+65.9 -1.161 m
+65.9 -1.014 65.845 -0.893 65.738 -0.794 c
+65.628 -0.698 65.422 -0.58 65.121 -0.441 c
+64.776 -0.294 64.533 -0.172 64.386 -0.073 c
+64.239 0.033 64.128 0.151 64.062 0.279 c
+63.993 0.405 63.96 0.563 63.96 0.75 c
+63.96 1.073 64.078 1.341 64.313 1.558 c
+64.548 1.771 64.849 1.881 65.223 1.881 c
+65.606 1.881 65.915 1.768 66.15 1.544 c
+66.385 1.316 66.503 1.029 66.503 0.676 c
+65.856 0.676 l
+65.856 0.852 65.797 1.004 65.68 1.132 c
+65.562 1.257 65.408 1.323 65.223 1.323 c
+65.025 1.323 64.874 1.268 64.768 1.161 c
+64.658 1.062 64.606 0.929 64.606 0.765 c
+64.606 0.636 64.643 0.53 64.724 0.441 c
+64.801 0.36 64.992 0.258 65.297 0.133 c
+65.775 -0.055 66.106 -0.243 66.282 -0.426 c
+66.459 -0.603 66.547 -0.831 66.547 -1.103 c
+66.547 -1.455 66.422 -1.735 66.179 -1.94 c
+65.944 -2.146 65.628 -2.249 65.238 -2.249 c
+64.816 -2.249 64.477 -2.132 64.224 -1.897 c
+63.967 -1.654 63.842 -1.348 63.842 -0.985 c
+64.489 -0.985 l
+64.496 -1.213 64.566 -1.389 64.695 -1.514 c
+64.82 -1.631 65.003 -1.691 65.238 -1.691 c
+65.452 -1.691 65.613 -1.643 65.724 -1.544 c
+65.841 -1.448 65.9 -1.319 65.9 -1.161 c
+f
+Q
+q 1 0 0 1 203.1893 109.5615 cm
+0 0 m
+-1.161 0.324 l
+-0.941 0.985 l
+0.191 0.53 l
+0.132 1.808 l
+0.852 1.808 l
+0.779 0.5 l
+1.897 0.956 l
+2.102 0.279 l
+0.926 -0.044 l
+1.705 -1.029 l
+1.117 -1.44 l
+0.441 -0.367 l
+-0.22 -1.411 l
+-0.809 -0.999 l
+h
+2.631 -3.013 m
+2.631 -2.848 2.682 -2.712 2.793 -2.601 c
+2.899 -2.495 3.046 -2.44 3.233 -2.44 c
+3.399 -2.44 3.543 -2.495 3.659 -2.601 c
+3.778 -2.712 3.836 -2.848 3.836 -3.013 c
+3.836 -3.183 3.778 -3.318 3.659 -3.424 c
+3.543 -3.535 3.399 -3.586 3.233 -3.586 c
+3.057 -3.586 2.911 -3.535 2.793 -3.424 c
+2.682 -3.318 2.631 -3.183 2.631 -3.013 c
+6.497 -2.469 m
+6.497 -2.381 6.453 -2.304 6.365 -2.234 c
+6.277 -2.157 6.089 -2.054 5.806 -1.926 c
+5.373 -1.749 5.074 -1.569 4.91 -1.382 c
+4.752 -1.198 4.675 -0.966 4.675 -0.691 c
+4.675 -0.349 4.796 -0.066 5.041 0.162 c
+5.295 0.397 5.633 0.515 6.056 0.515 c
+6.485 0.515 6.835 0.401 7.1 0.177 c
+7.364 -0.051 7.497 -0.353 7.497 -0.735 c
+6.453 -0.735 l
+6.453 -0.411 6.313 -0.249 6.041 -0.249 c
+5.931 -0.249 5.843 -0.286 5.776 -0.353 c
+5.707 -0.422 5.674 -0.521 5.674 -0.646 c
+5.674 -0.735 5.71 -0.816 5.791 -0.881 c
+5.868 -0.941 6.049 -1.036 6.335 -1.161 c
+6.765 -1.319 7.063 -1.496 7.231 -1.691 c
+7.408 -1.878 7.497 -2.127 7.497 -2.44 c
+7.497 -2.793 7.364 -3.079 7.1 -3.293 c
+6.835 -3.509 6.485 -3.616 6.056 -3.616 c
+5.762 -3.616 5.501 -3.561 5.277 -3.454 c
+5.049 -3.337 4.873 -3.175 4.748 -2.969 c
+4.63 -2.763 4.571 -2.543 4.571 -2.308 c
+5.556 -2.308 l
+5.556 -2.495 5.593 -2.631 5.674 -2.719 c
+5.762 -2.807 5.895 -2.851 6.071 -2.851 c
+6.354 -2.851 6.497 -2.726 6.497 -2.469 c
+11.054 -1.793 m
+11.406 0.441 l
+12.406 0.441 l
+11.582 -3.542 l
+10.715 -3.542 l
+10.157 -1.234 l
+9.598 -3.542 l
+8.731 -3.542 l
+7.908 0.441 l
+8.908 0.441 l
+9.261 -1.793 l
+9.789 0.441 l
+10.524 0.441 l
+h
+16.022 -1.675 m
+16.022 -2.304 15.912 -2.786 15.698 -3.116 c
+15.482 -3.451 15.162 -3.616 14.743 -3.616 c
+14.42 -3.616 14.159 -3.484 13.964 -3.219 c
+13.964 -5.071 l
+12.92 -5.071 l
+12.92 0.441 l
+13.876 0.441 l
+13.92 0.073 l
+14.115 0.368 14.383 0.515 14.728 0.515 c
+15.147 0.515 15.467 0.357 15.684 0.044 c
+15.897 -0.261 16.01 -0.731 16.022 -1.367 c
+h
+14.978 -1.411 m
+14.978 -1.029 14.934 -0.76 14.846 -0.602 c
+14.765 -0.448 14.626 -0.367 14.42 -0.367 c
+14.214 -0.367 14.059 -0.455 13.964 -0.632 c
+13.964 -2.499 l
+14.052 -2.668 14.206 -2.749 14.435 -2.749 c
+14.64 -2.749 14.78 -2.668 14.861 -2.499 c
+14.938 -2.323 14.978 -2.05 14.978 -1.675 c
+h
+f
+Q
+q 1 0 0 1 219.99 106.3724 cm
+0 0 m
+0 0.118 0.033 0.213 0.103 0.294 c
+0.169 0.371 0.272 0.411 0.412 0.411 c
+0.559 0.411 0.665 0.371 0.735 0.294 c
+0.813 0.213 0.853 0.118 0.853 0 c
+0.853 -0.111 0.813 -0.202 0.735 -0.279 c
+0.665 -0.357 0.559 -0.397 0.412 -0.397 c
+0.272 -0.397 0.169 -0.357 0.103 -0.279 c
+0.033 -0.202 0 -0.111 0 0 c
+5.619 -0.353 m
+5.619 4.997 l
+6.897 4.997 l
+7.522 4.997 8.008 4.799 8.353 4.409 c
+8.694 4.016 8.867 3.469 8.867 2.763 c
+8.867 1.866 l
+8.867 1.161 8.691 0.61 8.339 0.22 c
+7.993 -0.162 7.489 -0.353 6.824 -0.353 c
+h
+6.295 4.424 m
+6.295 0.22 l
+6.839 0.22 l
+7.31 0.22 7.651 0.357 7.868 0.631 c
+8.092 0.904 8.206 1.308 8.206 1.837 c
+8.206 2.778 l
+8.206 3.343 8.096 3.763 7.882 4.027 c
+7.666 4.292 7.339 4.424 6.897 4.424 c
+h
+11.248 -0.427 m
+10.749 -0.427 10.367 -0.279 10.103 0.014 c
+9.837 0.309 9.706 0.742 9.706 1.323 c
+9.706 1.793 l
+9.706 2.389 9.83 2.855 10.088 3.189 c
+10.352 3.532 10.712 3.704 11.175 3.704 c
+11.634 3.704 11.977 3.549 12.204 3.248 c
+12.439 2.954 12.561 2.491 12.572 1.866 c
+12.572 1.44 l
+10.352 1.44 l
+10.352 1.352 l
+10.352 0.918 10.429 0.606 10.587 0.411 c
+10.753 0.224 10.984 0.132 11.279 0.132 c
+11.473 0.132 11.645 0.165 11.792 0.235 c
+11.94 0.312 12.075 0.43 12.204 0.588 c
+12.542 0.176 l
+12.256 -0.229 11.826 -0.427 11.248 -0.427 c
+11.175 3.145 m
+10.899 3.145 10.697 3.05 10.573 2.865 c
+10.444 2.678 10.371 2.389 10.352 1.999 c
+11.925 1.999 l
+11.925 2.087 l
+11.903 2.469 11.836 2.738 11.719 2.896 c
+11.601 3.061 11.418 3.145 11.175 3.145 c
+15.291 0.661 m
+15.291 0.808 15.236 0.929 15.129 1.028 c
+15.019 1.124 14.813 1.242 14.512 1.381 c
+14.167 1.529 13.924 1.65 13.777 1.749 c
+13.63 1.855 13.519 1.973 13.454 2.102 c
+13.384 2.227 13.351 2.385 13.351 2.572 c
+13.351 2.896 13.469 3.164 13.704 3.38 c
+13.939 3.594 14.24 3.704 14.614 3.704 c
+14.997 3.704 15.306 3.59 15.541 3.366 c
+15.776 3.138 15.894 2.851 15.894 2.499 c
+15.247 2.499 l
+15.247 2.675 15.188 2.826 15.071 2.954 c
+14.953 3.079 14.799 3.145 14.614 3.145 c
+14.416 3.145 14.265 3.09 14.159 2.983 c
+14.049 2.884 13.997 2.752 13.997 2.587 c
+13.997 2.458 14.034 2.352 14.115 2.263 c
+14.192 2.183 14.383 2.08 14.689 1.955 c
+15.166 1.768 15.497 1.579 15.673 1.396 c
+15.85 1.219 15.938 0.992 15.938 0.72 c
+15.938 0.367 15.813 0.087 15.57 -0.118 c
+15.335 -0.324 15.019 -0.427 14.629 -0.427 c
+14.207 -0.427 13.868 -0.31 13.615 -0.074 c
+13.358 0.168 13.233 0.474 13.233 0.837 c
+13.88 0.837 l
+13.887 0.61 13.957 0.434 14.086 0.309 c
+14.211 0.191 14.394 0.132 14.629 0.132 c
+14.843 0.132 15.004 0.18 15.115 0.278 c
+15.232 0.374 15.291 0.503 15.291 0.661 c
+18.157 0.132 m
+18.371 0.132 18.543 0.195 18.672 0.323 c
+18.807 0.459 18.882 0.65 18.892 0.897 c
+19.51 0.897 l
+19.487 0.514 19.352 0.195 19.098 -0.059 c
+18.841 -0.306 18.529 -0.427 18.157 -0.427 c
+17.665 -0.427 17.29 -0.276 17.025 0.029 c
+16.768 0.341 16.644 0.808 16.644 1.425 c
+16.644 1.866 l
+16.644 2.462 16.768 2.917 17.025 3.233 c
+17.29 3.546 17.665 3.704 18.157 3.704 c
+18.558 3.704 18.878 3.571 19.113 3.307 c
+19.355 3.05 19.487 2.705 19.51 2.263 c
+18.892 2.263 l
+18.87 2.557 18.797 2.778 18.672 2.925 c
+18.554 3.072 18.381 3.145 18.157 3.145 c
+17.864 3.145 17.646 3.046 17.511 2.851 c
+17.371 2.664 17.297 2.356 17.29 1.925 c
+17.29 1.411 l
+17.29 0.941 17.356 0.606 17.496 0.411 c
+17.643 0.224 17.864 0.132 18.157 0.132 c
+21.905 3.013 m
+21.818 3.031 21.718 3.042 21.612 3.042 c
+21.277 3.042 21.042 2.859 20.906 2.499 c
+20.906 -0.353 l
+20.259 -0.353 l
+20.259 3.63 l
+20.891 3.63 l
+20.906 3.218 l
+21.082 3.542 21.325 3.704 21.641 3.704 c
+21.747 3.704 21.835 3.682 21.905 3.645 c
+h
+23.258 -0.353 -0.647 3.983 re
+23.302 4.674 m
+23.302 4.564 23.273 4.472 23.214 4.394 c
+23.155 4.325 23.059 4.292 22.934 4.292 c
+22.817 4.292 22.722 4.325 22.655 4.394 c
+22.597 4.472 22.567 4.564 22.567 4.674 c
+22.567 4.791 22.597 4.883 22.655 4.953 c
+22.722 5.03 22.817 5.071 22.934 5.071 c
+23.059 5.071 23.155 5.03 23.214 4.953 c
+23.273 4.872 23.302 4.781 23.302 4.674 c
+27.154 1.44 m
+27.154 0.823 27.039 0.357 26.815 0.043 c
+26.598 -0.272 26.275 -0.427 25.845 -0.427 c
+25.423 -0.427 25.11 -0.246 24.904 0.118 c
+24.875 -0.353 l
+24.272 -0.353 l
+24.272 5.292 l
+24.919 5.292 l
+24.919 3.189 l
+25.132 3.532 25.44 3.704 25.845 3.704 c
+26.275 3.704 26.598 3.546 26.815 3.233 c
+27.039 2.929 27.154 2.462 27.154 1.837 c
+h
+26.506 1.822 m
+26.506 2.293 26.437 2.624 26.3 2.822 c
+26.172 3.017 25.963 3.116 25.668 3.116 c
+25.334 3.116 25.084 2.932 24.919 2.572 c
+24.919 0.691 l
+25.084 0.326 25.338 0.147 25.683 0.147 c
+25.978 0.147 26.186 0.249 26.315 0.455 c
+26.44 0.661 26.506 0.977 26.506 1.411 c
+h
+29.417 -0.427 m
+28.917 -0.427 28.535 -0.279 28.27 0.014 c
+28.006 0.309 27.873 0.742 27.873 1.323 c
+27.873 1.793 l
+27.873 2.389 27.998 2.855 28.255 3.189 c
+28.52 3.532 28.88 3.704 29.344 3.704 c
+29.803 3.704 30.144 3.549 30.372 3.248 c
+30.607 2.954 30.728 2.491 30.74 1.866 c
+30.74 1.44 l
+28.52 1.44 l
+28.52 1.352 l
+28.52 0.918 28.598 0.606 28.756 0.411 c
+28.92 0.224 29.153 0.132 29.446 0.132 c
+29.641 0.132 29.814 0.165 29.961 0.235 c
+30.107 0.312 30.244 0.43 30.372 0.588 c
+30.71 0.176 l
+30.424 -0.229 29.994 -0.427 29.417 -0.427 c
+29.344 3.145 m
+29.068 3.145 28.866 3.05 28.741 2.865 c
+28.612 2.678 28.538 2.389 28.52 1.999 c
+30.093 1.999 l
+30.093 2.087 l
+30.071 2.469 30.005 2.738 29.887 2.896 c
+29.77 3.061 29.585 3.145 29.344 3.145 c
+31.387 1.822 m
+31.387 2.429 31.497 2.896 31.725 3.218 c
+31.96 3.542 32.287 3.704 32.709 3.704 c
+33.091 3.704 33.389 3.546 33.606 3.233 c
+33.606 5.292 l
+34.252 5.292 l
+34.252 -0.353 l
+33.664 -0.353 l
+33.621 0.073 l
+33.415 -0.262 33.11 -0.427 32.709 -0.427 c
+32.297 -0.427 31.975 -0.272 31.74 0.043 c
+31.504 0.367 31.387 0.823 31.387 1.411 c
+h
+32.033 1.44 m
+32.033 0.999 32.095 0.669 32.224 0.455 c
+32.36 0.249 32.581 0.147 32.885 0.147 c
+33.209 0.147 33.448 0.309 33.606 0.631 c
+33.606 2.645 l
+33.437 2.958 33.198 3.116 32.885 3.116 c
+32.581 3.116 32.36 3.013 32.224 2.807 c
+32.095 2.601 32.033 2.278 32.033 1.837 c
+h
+39.015 -0.353 m
+39.015 3.101 l
+38.501 3.101 l
+38.501 3.63 l
+39.015 3.63 l
+39.015 3.998 l
+39.023 4.428 39.137 4.762 39.353 4.997 c
+39.578 5.24 39.89 5.365 40.294 5.365 c
+40.441 5.365 40.581 5.342 40.72 5.306 c
+40.868 5.265 41.018 5.211 41.176 5.144 c
+41.059 4.571 l
+40.823 4.696 40.581 4.762 40.338 4.762 c
+40.091 4.762 39.919 4.692 39.823 4.556 c
+39.725 4.428 39.677 4.233 39.677 3.968 c
+39.677 3.63 l
+40.324 3.63 l
+40.324 3.101 l
+39.677 3.101 l
+39.677 -0.353 l
+h
+41.485 -0.353 -0.647 3.983 re
+43.234 -0.353 -0.646 5.644 re
+45.674 -0.427 m
+45.174 -0.427 44.792 -0.279 44.527 0.014 c
+44.263 0.309 44.13 0.742 44.13 1.323 c
+44.13 1.793 l
+44.13 2.389 44.255 2.855 44.513 3.189 c
+44.777 3.532 45.138 3.704 45.6 3.704 c
+46.06 3.704 46.401 3.549 46.63 3.248 c
+46.865 2.954 46.986 2.491 46.997 1.866 c
+46.997 1.44 l
+44.777 1.44 l
+44.777 1.352 l
+44.777 0.918 44.854 0.606 45.012 0.411 c
+45.178 0.224 45.409 0.132 45.703 0.132 c
+45.898 0.132 46.071 0.165 46.218 0.235 c
+46.364 0.312 46.501 0.43 46.63 0.588 c
+46.967 0.176 l
+46.68 -0.229 46.251 -0.427 45.674 -0.427 c
+45.6 3.145 m
+45.325 3.145 45.123 3.05 44.997 2.865 c
+44.869 2.678 44.795 2.389 44.777 1.999 c
+46.35 1.999 l
+46.35 2.087 l
+46.328 2.469 46.262 2.738 46.144 2.896 c
+46.027 3.061 45.843 3.145 45.6 3.145 c
+f
+Q
+38.317 96.612 -0.646 3.983 re
+38.361 101.639 m
+38.361 101.529 38.332 101.437 38.273 101.359 c
+38.215 101.29 38.119 101.257 37.994 101.257 c
+37.876 101.257 37.781 101.29 37.714 101.359 c
+37.656 101.437 37.627 101.529 37.627 101.639 c
+37.627 101.756 37.656 101.848 37.714 101.918 c
+37.781 101.995 37.876 102.036 37.994 102.036 c
+38.119 102.036 38.215 101.995 38.273 101.918 c
+38.332 101.837 38.361 101.746 38.361 101.639 c
+39.2 98.787 m
+39.2 99.404 39.31 99.868 39.537 100.183 c
+39.761 100.507 40.096 100.669 40.536 100.669 c
+40.937 100.669 41.242 100.493 41.448 100.14 c
+41.492 100.595 l
+42.08 100.595 l
+42.08 96.568 l
+42.08 96.079 41.951 95.701 41.698 95.436 c
+41.44 95.171 41.088 95.039 40.64 95.039 c
+40.441 95.039 40.221 95.09 39.979 95.186 c
+39.732 95.285 39.552 95.406 39.435 95.554 c
+39.699 95.994 l
+39.964 95.73 40.261 95.597 40.596 95.597 c
+41.132 95.597 41.408 95.892 41.419 96.48 c
+41.419 97.008 l
+41.213 96.693 40.912 96.538 40.522 96.538 c
+40.11 96.538 39.788 96.689 39.552 96.994 c
+39.324 97.307 39.206 97.758 39.2 98.346 c
+h
+39.861 98.405 m
+39.861 97.964 39.923 97.634 40.052 97.42 c
+40.177 97.214 40.393 97.112 40.698 97.112 c
+41.022 97.112 41.261 97.277 41.419 97.611 c
+41.419 99.595 l
+41.249 99.919 41.011 100.081 40.698 100.081 c
+40.405 100.081 40.187 99.978 40.052 99.772 c
+39.923 99.566 39.861 99.243 39.861 98.802 c
+h
+43.653 100.595 m
+43.668 100.154 l
+43.921 100.497 44.245 100.669 44.638 100.669 c
+45.344 100.669 45.7 100.198 45.71 99.258 c
+45.71 96.612 l
+45.064 96.612 l
+45.064 99.228 l
+45.064 99.541 45.009 99.761 44.902 99.89 c
+44.792 100.015 44.638 100.081 44.432 100.081 c
+44.274 100.081 44.127 100.026 43.991 99.919 c
+43.863 99.809 43.759 99.673 43.682 99.508 c
+43.682 96.612 l
+43.036 96.612 l
+43.036 100.595 l
+h
+46.549 98.787 m
+46.549 99.364 46.685 99.82 46.961 100.154 c
+47.243 100.497 47.615 100.669 48.077 100.669 c
+48.536 100.669 48.904 100.499 49.18 100.169 c
+49.463 99.846 49.61 99.398 49.621 98.831 c
+49.621 98.405 l
+49.621 97.835 49.477 97.38 49.195 97.038 c
+48.919 96.703 48.552 96.538 48.092 96.538 c
+47.629 96.538 47.258 96.7 46.975 97.023 c
+46.699 97.354 46.556 97.795 46.549 98.346 c
+h
+47.196 98.405 m
+47.196 98.001 47.273 97.685 47.431 97.45 c
+47.596 97.214 47.817 97.097 48.092 97.097 c
+48.658 97.097 48.952 97.509 48.974 98.332 c
+48.974 98.787 l
+48.974 99.188 48.889 99.508 48.725 99.743 c
+48.567 99.986 48.349 100.11 48.077 100.11 c
+47.813 100.11 47.596 99.986 47.431 99.743 c
+47.273 99.508 47.196 99.188 47.196 98.787 c
+h
+52.105 99.978 m
+52.017 99.996 51.917 100.007 51.811 100.007 c
+51.476 100.007 51.241 99.824 51.106 99.464 c
+51.106 96.612 l
+50.458 96.612 l
+50.458 100.595 l
+51.091 100.595 l
+51.106 100.183 l
+51.282 100.507 51.524 100.669 51.84 100.669 c
+51.947 100.669 52.035 100.647 52.105 100.61 c
+h
+53.457 96.612 -0.647 3.983 re
+53.502 101.639 m
+53.502 101.529 53.472 101.437 53.413 101.359 c
+53.354 101.29 53.259 101.257 53.134 101.257 c
+53.016 101.257 52.921 101.29 52.854 101.359 c
+52.796 101.437 52.766 101.529 52.766 101.639 c
+52.766 101.756 52.796 101.848 52.854 101.918 c
+52.921 101.995 53.016 102.036 53.134 102.036 c
+53.259 102.036 53.354 101.995 53.413 101.918 c
+53.472 101.837 53.502 101.746 53.502 101.639 c
+55.089 100.595 m
+55.104 100.154 l
+55.357 100.497 55.68 100.669 56.074 100.669 c
+56.779 100.669 57.136 100.198 57.147 99.258 c
+57.147 96.612 l
+56.5 96.612 l
+56.5 99.228 l
+56.5 99.541 56.445 99.761 56.338 99.89 c
+56.228 100.015 56.074 100.081 55.868 100.081 c
+55.71 100.081 55.563 100.026 55.427 99.919 c
+55.298 99.809 55.195 99.673 55.118 99.508 c
+55.118 96.612 l
+54.472 96.612 l
+54.472 100.595 l
+h
+57.984 98.787 m
+57.984 99.404 58.095 99.868 58.323 100.183 c
+58.547 100.507 58.881 100.669 59.322 100.669 c
+59.723 100.669 60.028 100.493 60.234 100.14 c
+60.278 100.595 l
+60.866 100.595 l
+60.866 96.568 l
+60.866 96.079 60.737 95.701 60.484 95.436 c
+60.226 95.171 59.873 95.039 59.426 95.039 c
+59.227 95.039 59.006 95.09 58.763 95.186 c
+58.518 95.285 58.337 95.406 58.219 95.554 c
+58.485 95.994 l
+58.749 95.73 59.046 95.597 59.381 95.597 c
+59.918 95.597 60.193 95.892 60.204 96.48 c
+60.204 97.008 l
+59.998 96.693 59.697 96.538 59.308 96.538 c
+58.896 96.538 58.572 96.689 58.337 96.994 c
+58.109 97.307 57.992 97.758 57.984 98.346 c
+h
+58.646 98.405 m
+58.646 97.964 58.709 97.634 58.838 97.42 c
+58.962 97.214 59.179 97.112 59.484 97.112 c
+59.807 97.112 60.046 97.277 60.204 97.611 c
+60.204 99.595 l
+60.035 99.919 59.796 100.081 59.484 100.081 c
+59.19 100.081 58.973 99.978 58.838 99.772 c
+58.709 99.566 58.646 99.243 58.646 98.802 c
+h
+66.734 97.758 m
+67.337 100.595 l
+67.983 100.595 l
+66.998 96.612 l
+66.484 96.612 l
+65.705 99.464 l
+64.955 96.612 l
+64.426 96.612 l
+63.471 100.595 l
+64.103 100.595 l
+64.72 97.831 l
+65.456 100.595 l
+65.97 100.595 l
+h
+69.365 96.612 -0.647 3.983 re
+69.41 101.639 m
+69.41 101.529 69.38 101.437 69.321 101.359 c
+69.263 101.29 69.167 101.257 69.042 101.257 c
+68.924 101.257 68.829 101.29 68.762 101.359 c
+68.704 101.437 68.675 101.529 68.675 101.639 c
+68.675 101.756 68.704 101.848 68.762 101.918 c
+68.829 101.995 68.924 102.036 69.042 102.036 c
+69.167 102.036 69.263 101.995 69.321 101.918 c
+69.38 101.837 69.41 101.746 69.41 101.639 c
+71.085 96.612 -0.646 5.644 re
+72.805 96.612 -0.646 5.644 re
+78.732 97.758 m
+79.335 100.595 l
+79.982 100.595 l
+78.997 96.612 l
+78.482 96.612 l
+77.703 99.464 l
+76.953 96.612 l
+76.425 96.612 l
+75.469 100.595 l
+76.101 100.595 l
+76.718 97.831 l
+77.454 100.595 l
+77.967 100.595 l
+h
+80.525 98.787 m
+80.525 99.364 80.662 99.82 80.937 100.154 c
+81.221 100.497 81.591 100.669 82.054 100.669 c
+82.514 100.669 82.881 100.499 83.157 100.169 c
+83.44 99.846 83.587 99.398 83.598 98.831 c
+83.598 98.405 l
+83.598 97.835 83.455 97.38 83.172 97.038 c
+82.896 96.703 82.528 96.538 82.069 96.538 c
+81.606 96.538 81.235 96.7 80.952 97.023 c
+80.676 97.354 80.533 97.795 80.525 98.346 c
+h
+81.173 98.405 m
+81.173 98.001 81.25 97.685 81.408 97.45 c
+81.573 97.214 81.793 97.097 82.069 97.097 c
+82.635 97.097 82.929 97.509 82.951 98.332 c
+82.951 98.787 l
+82.951 99.188 82.867 99.508 82.701 99.743 c
+82.543 99.986 82.326 100.11 82.054 100.11 c
+81.79 100.11 81.573 99.986 81.408 99.743 c
+81.25 99.508 81.173 99.188 81.173 98.787 c
+h
+86.082 99.978 m
+85.994 99.996 85.894 100.007 85.788 100.007 c
+85.454 100.007 85.219 99.824 85.082 99.464 c
+85.082 96.612 l
+84.436 96.612 l
+84.436 100.595 l
+85.067 100.595 l
+85.082 100.183 l
+85.258 100.507 85.501 100.669 85.817 100.669 c
+85.924 100.669 86.012 100.647 86.082 100.61 c
+h
+87.729 98.434 m
+87.39 98.037 l
+87.39 96.612 l
+86.729 96.612 l
+86.729 102.257 l
+87.39 102.257 l
+87.39 98.876 l
+88.625 100.595 l
+89.404 100.595 l
+88.14 98.934 l
+89.566 96.612 l
+88.816 96.612 l
+h
+92.366 96.612 m
+92.366 100.066 l
+91.837 100.066 l
+91.837 100.595 l
+92.366 100.595 l
+92.366 101.051 l
+92.366 101.451 92.461 101.764 92.66 101.991 c
+92.865 102.216 93.144 102.33 93.497 102.33 c
+93.634 102.33 93.765 102.307 93.894 102.271 c
+93.865 101.727 l
+93.765 101.746 93.667 101.756 93.571 101.756 c
+93.196 101.756 93.013 101.492 93.013 100.963 c
+93.013 100.595 l
+93.688 100.595 l
+93.688 100.066 l
+93.013 100.066 l
+93.013 96.612 l
+h
+94.32 98.787 m
+94.32 99.364 94.457 99.82 94.732 100.154 c
+95.016 100.497 95.386 100.669 95.849 100.669 c
+96.309 100.669 96.676 100.499 96.952 100.169 c
+97.235 99.846 97.382 99.398 97.393 98.831 c
+97.393 98.405 l
+97.393 97.835 97.25 97.38 96.967 97.038 c
+96.691 96.703 96.323 96.538 95.864 96.538 c
+95.401 96.538 95.03 96.7 94.747 97.023 c
+94.471 97.354 94.328 97.795 94.32 98.346 c
+h
+94.968 98.405 m
+94.968 98.001 95.045 97.685 95.203 97.45 c
+95.368 97.214 95.588 97.097 95.864 97.097 c
+96.43 97.097 96.724 97.509 96.746 98.332 c
+96.746 98.787 l
+96.746 99.188 96.662 99.508 96.496 99.743 c
+96.338 99.986 96.121 100.11 95.849 100.11 c
+95.585 100.11 95.368 99.986 95.203 99.743 c
+95.045 99.508 94.968 99.188 94.968 98.787 c
+h
+99.877 99.978 m
+99.789 99.996 99.689 100.007 99.583 100.007 c
+99.249 100.007 99.014 99.824 98.877 99.464 c
+98.877 96.612 l
+98.231 96.612 l
+98.231 100.595 l
+98.862 100.595 l
+98.877 100.183 l
+99.054 100.507 99.296 100.669 99.612 100.669 c
+99.719 100.669 99.807 100.647 99.877 100.61 c
+h
+103.32 101.551 m
+103.32 100.595 l
+103.922 100.595 l
+103.922 100.066 l
+103.32 100.066 l
+103.32 97.596 l
+103.32 97.439 103.342 97.322 103.394 97.243 c
+103.452 97.164 103.541 97.126 103.658 97.126 c
+103.747 97.126 103.835 97.141 103.922 97.17 c
+103.922 96.612 l
+103.776 96.564 103.621 96.538 103.467 96.538 c
+103.21 96.538 103.016 96.63 102.879 96.817 c
+102.74 97.002 102.673 97.262 102.673 97.596 c
+102.673 100.066 l
+102.071 100.066 l
+102.071 100.595 l
+102.673 100.595 l
+102.673 101.551 l
+h
+105.334 100.183 m
+105.588 100.507 105.907 100.669 106.289 100.669 c
+106.995 100.669 107.351 100.198 107.363 99.258 c
+107.363 96.612 l
+106.716 96.612 l
+106.716 99.228 l
+106.716 99.541 106.66 99.761 106.554 99.89 c
+106.444 100.015 106.289 100.081 106.083 100.081 c
+105.925 100.081 105.779 100.026 105.642 99.919 c
+105.514 99.809 105.411 99.673 105.334 99.508 c
+105.334 96.612 l
+104.687 96.612 l
+104.687 102.257 l
+105.334 102.257 l
+h
+109.744 96.538 m
+109.244 96.538 108.861 96.686 108.597 96.979 c
+108.333 97.274 108.2 97.707 108.2 98.288 c
+108.2 98.758 l
+108.2 99.354 108.325 99.82 108.582 100.154 c
+108.847 100.497 109.208 100.669 109.67 100.669 c
+110.129 100.669 110.471 100.514 110.699 100.213 c
+110.934 99.919 111.055 99.456 111.066 98.831 c
+111.066 98.405 l
+108.847 98.405 l
+108.847 98.317 l
+108.847 97.883 108.924 97.571 109.082 97.376 c
+109.247 97.189 109.479 97.097 109.773 97.097 c
+109.968 97.097 110.141 97.13 110.288 97.2 c
+110.434 97.277 110.571 97.395 110.699 97.553 c
+111.037 97.141 l
+110.75 96.736 110.32 96.538 109.744 96.538 c
+109.67 100.11 m
+109.395 100.11 109.193 100.015 109.067 99.831 c
+108.939 99.643 108.865 99.354 108.847 98.964 c
+110.42 98.964 l
+110.42 99.052 l
+110.398 99.434 110.332 99.703 110.214 99.861 c
+110.097 100.026 109.913 100.11 109.67 100.11 c
+113.657 98.787 m
+113.657 99.394 113.767 99.861 113.995 100.183 c
+114.23 100.507 114.558 100.669 114.98 100.669 c
+115.362 100.669 115.66 100.511 115.876 100.198 c
+115.876 102.257 l
+116.524 102.257 l
+116.524 96.612 l
+115.936 96.612 l
+115.892 97.038 l
+115.685 96.703 115.381 96.538 114.98 96.538 c
+114.569 96.538 114.245 96.693 114.01 97.008 c
+113.775 97.332 113.657 97.788 113.657 98.376 c
+h
+114.304 98.405 m
+114.304 97.964 114.367 97.634 114.495 97.42 c
+114.631 97.214 114.852 97.112 115.157 97.112 c
+115.48 97.112 115.718 97.274 115.876 97.596 c
+115.876 99.61 l
+115.708 99.923 115.469 100.081 115.157 100.081 c
+114.852 100.081 114.631 99.978 114.495 99.772 c
+114.367 99.566 114.304 99.243 114.304 98.802 c
+h
+118.228 96.612 -0.646 3.983 re
+118.273 101.639 m
+118.273 101.529 118.243 101.437 118.184 101.359 c
+118.126 101.29 118.03 101.257 117.906 101.257 c
+117.788 101.257 117.692 101.29 117.626 101.359 c
+117.567 101.437 117.538 101.529 117.538 101.639 c
+117.538 101.756 117.567 101.848 117.626 101.918 c
+117.692 101.995 117.788 102.036 117.906 102.036 c
+118.03 102.036 118.126 101.995 118.184 101.918 c
+118.243 101.837 118.273 101.746 118.273 101.639 c
+120.89 99.978 m
+120.801 99.996 120.702 100.007 120.595 100.007 c
+120.261 100.007 120.026 99.824 119.89 99.464 c
+119.89 96.612 l
+119.243 96.612 l
+119.243 100.595 l
+119.875 100.595 l
+119.89 100.183 l
+120.066 100.507 120.308 100.669 120.624 100.669 c
+120.732 100.669 120.819 100.647 120.89 100.61 c
+h
+122.888 96.538 m
+122.388 96.538 122.006 96.686 121.742 96.979 c
+121.477 97.274 121.345 97.707 121.345 98.288 c
+121.345 98.758 l
+121.345 99.354 121.47 99.82 121.727 100.154 c
+121.991 100.497 122.352 100.669 122.814 100.669 c
+123.274 100.669 123.616 100.514 123.844 100.213 c
+124.079 99.919 124.2 99.456 124.211 98.831 c
+124.211 98.405 l
+121.991 98.405 l
+121.991 98.317 l
+121.991 97.883 122.068 97.571 122.226 97.376 c
+122.392 97.189 122.623 97.097 122.918 97.097 c
+123.113 97.097 123.285 97.13 123.432 97.2 c
+123.579 97.277 123.715 97.395 123.844 97.553 c
+124.182 97.141 l
+123.895 96.736 123.465 96.538 122.888 96.538 c
+122.814 100.11 m
+122.539 100.11 122.337 100.015 122.212 99.831 c
+122.083 99.643 122.01 99.354 121.991 98.964 c
+123.564 98.964 l
+123.564 99.052 l
+123.543 99.434 123.477 99.703 123.358 99.861 c
+123.242 100.026 123.057 100.11 122.814 100.11 c
+126.372 97.097 m
+126.585 97.097 126.758 97.16 126.886 97.288 c
+127.022 97.424 127.096 97.615 127.107 97.862 c
+127.724 97.862 l
+127.702 97.479 127.566 97.16 127.313 96.906 c
+127.055 96.659 126.743 96.538 126.372 96.538 c
+125.879 96.538 125.505 96.689 125.24 96.994 c
+124.983 97.307 124.858 97.773 124.858 98.39 c
+124.858 98.831 l
+124.858 99.427 124.983 99.882 125.24 100.198 c
+125.505 100.511 125.879 100.669 126.372 100.669 c
+126.772 100.669 127.092 100.536 127.327 100.272 c
+127.57 100.015 127.702 99.67 127.724 99.228 c
+127.107 99.228 l
+127.084 99.522 127.011 99.743 126.886 99.89 c
+126.769 100.037 126.596 100.11 126.372 100.11 c
+126.078 100.11 125.861 100.011 125.725 99.816 c
+125.586 99.629 125.512 99.321 125.505 98.89 c
+125.505 98.376 l
+125.505 97.906 125.571 97.571 125.71 97.376 c
+125.858 97.189 126.078 97.097 126.372 97.097 c
+129.326 101.551 m
+129.326 100.595 l
+129.929 100.595 l
+129.929 100.066 l
+129.326 100.066 l
+129.326 97.596 l
+129.326 97.439 129.349 97.322 129.4 97.243 c
+129.459 97.164 129.547 97.126 129.665 97.126 c
+129.752 97.126 129.841 97.141 129.929 97.17 c
+129.929 96.612 l
+129.782 96.564 129.628 96.538 129.473 96.538 c
+129.216 96.538 129.021 96.63 128.886 96.817 c
+128.746 97.002 128.68 97.262 128.68 97.596 c
+128.68 100.066 l
+128.077 100.066 l
+128.077 100.595 l
+128.68 100.595 l
+128.68 101.551 l
+h
+130.488 98.787 m
+130.488 99.364 130.623 99.82 130.899 100.154 c
+131.182 100.497 131.553 100.669 132.017 100.669 c
+132.476 100.669 132.844 100.499 133.119 100.169 c
+133.402 99.846 133.549 99.398 133.56 98.831 c
+133.56 98.405 l
+133.56 97.835 133.417 97.38 133.133 97.038 c
+132.858 96.703 132.491 96.538 132.031 96.538 c
+131.568 96.538 131.197 96.7 130.914 97.023 c
+130.639 97.354 130.495 97.795 130.488 98.346 c
+h
+131.134 98.405 m
+131.134 98.001 131.211 97.685 131.369 97.45 c
+131.535 97.214 131.755 97.097 132.031 97.097 c
+132.597 97.097 132.891 97.509 132.913 98.332 c
+132.913 98.787 l
+132.913 99.188 132.829 99.508 132.663 99.743 c
+132.505 99.986 132.289 100.11 132.017 100.11 c
+131.751 100.11 131.535 99.986 131.369 99.743 c
+131.211 99.508 131.134 99.188 131.134 98.787 c
+h
+136.044 99.978 m
+135.956 99.996 135.857 100.007 135.75 100.007 c
+135.416 100.007 135.18 99.824 135.044 99.464 c
+135.044 96.612 l
+134.398 96.612 l
+134.398 100.595 l
+135.03 100.595 l
+135.044 100.183 l
+135.221 100.507 135.464 100.669 135.78 100.669 c
+135.886 100.669 135.974 100.647 136.044 100.61 c
+h
+137.852 97.7 m
+138.572 100.595 l
+139.263 100.595 l
+137.97 96.053 l
+137.87 95.712 137.727 95.45 137.544 95.275 c
+137.367 95.098 137.164 95.009 136.941 95.009 c
+136.852 95.009 136.738 95.032 136.603 95.068 c
+136.603 95.612 l
+136.75 95.597 l
+136.933 95.597 137.08 95.641 137.191 95.73 c
+137.297 95.818 137.386 95.976 137.455 96.2 c
+137.573 96.641 l
+136.412 100.595 l
+137.117 100.595 l
+h
+141.824 98.787 m
+141.824 99.423 141.912 100.026 142.089 100.595 c
+142.265 101.162 142.508 101.657 142.824 102.08 c
+143.019 102.344 143.206 102.535 143.383 102.653 c
+143.514 102.197 l
+143.221 101.922 142.978 101.499 142.795 100.933 c
+142.607 100.364 142.504 99.732 142.486 99.037 c
+142.486 98.743 l
+142.486 97.879 142.604 97.116 142.839 96.45 c
+143.022 95.95 143.25 95.568 143.514 95.304 c
+143.383 94.878 l
+143.154 95.036 142.93 95.285 142.706 95.627 c
+142.118 96.509 141.824 97.56 141.824 98.787 c
+146.249 96.612 m
+146.209 96.7 146.182 96.847 146.175 97.053 c
+145.94 96.707 145.646 96.538 145.293 96.538 c
+144.929 96.538 144.646 96.634 144.441 96.832 c
+144.243 97.038 144.147 97.324 144.147 97.7 c
+144.147 98.1 144.283 98.419 144.559 98.655 c
+144.831 98.897 145.205 99.022 145.675 99.022 c
+146.161 99.022 l
+146.161 99.449 l
+146.161 99.684 146.105 99.849 145.999 99.948 c
+145.889 100.055 145.727 100.11 145.514 100.11 c
+145.315 100.11 145.153 100.052 145.029 99.934 c
+144.911 99.816 144.852 99.67 144.852 99.493 c
+144.206 99.493 l
+144.206 99.688 144.264 99.878 144.382 100.066 c
+144.507 100.25 144.669 100.397 144.867 100.507 c
+145.073 100.614 145.301 100.669 145.558 100.669 c
+145.958 100.669 146.263 100.566 146.469 100.36 c
+146.682 100.154 146.797 99.861 146.807 99.478 c
+146.807 97.465 l
+146.807 97.16 146.844 96.894 146.925 96.671 c
+146.925 96.612 l
+h
+145.382 97.126 m
+145.546 97.126 145.698 97.17 145.837 97.259 c
+145.984 97.347 146.091 97.457 146.161 97.596 c
+146.161 98.537 l
+145.793 98.537 l
+145.477 98.537 145.234 98.467 145.058 98.332 c
+144.881 98.203 144.794 98.016 144.794 97.773 c
+144.794 97.545 144.838 97.38 144.926 97.274 c
+145.014 97.174 145.165 97.126 145.382 97.126 c
+148.424 100.595 m
+148.439 100.154 l
+148.692 100.497 149.016 100.669 149.409 100.669 c
+150.115 100.669 150.471 100.198 150.482 99.258 c
+150.482 96.612 l
+149.835 96.612 l
+149.835 99.228 l
+149.835 99.541 149.78 99.761 149.673 99.89 c
+149.563 100.015 149.409 100.081 149.203 100.081 c
+149.045 100.081 148.898 100.026 148.762 99.919 c
+148.634 99.809 148.53 99.673 148.453 99.508 c
+148.453 96.612 l
+147.807 96.612 l
+147.807 100.595 l
+h
+151.32 98.787 m
+151.32 99.394 151.43 99.861 151.658 100.183 c
+151.894 100.507 152.22 100.669 152.642 100.669 c
+153.025 100.669 153.323 100.511 153.54 100.198 c
+153.54 102.257 l
+154.186 102.257 l
+154.186 96.612 l
+153.598 96.612 l
+153.554 97.038 l
+153.348 96.703 153.043 96.538 152.642 96.538 c
+152.231 96.538 151.908 96.693 151.673 97.008 c
+151.437 97.332 151.32 97.788 151.32 98.376 c
+h
+151.967 98.405 m
+151.967 97.964 152.029 97.634 152.158 97.42 c
+152.293 97.214 152.514 97.112 152.819 97.112 c
+153.143 97.112 153.382 97.274 153.54 97.596 c
+153.54 99.61 l
+153.371 99.923 153.132 100.081 152.819 100.081 c
+152.514 100.081 152.293 99.978 152.158 99.772 c
+152.029 99.566 151.967 99.243 151.967 98.802 c
+h
+158.512 97.097 m
+158.724 97.097 158.897 97.16 159.026 97.288 c
+159.162 97.424 159.235 97.615 159.246 97.862 c
+159.864 97.862 l
+159.842 97.479 159.706 97.16 159.452 96.906 c
+159.195 96.659 158.882 96.538 158.512 96.538 c
+158.019 96.538 157.644 96.689 157.38 96.994 c
+157.122 97.307 156.997 97.773 156.997 98.39 c
+156.997 98.831 l
+156.997 99.427 157.122 99.882 157.38 100.198 c
+157.644 100.511 158.019 100.669 158.512 100.669 c
+158.912 100.669 159.231 100.536 159.467 100.272 c
+159.709 100.015 159.842 99.67 159.864 99.228 c
+159.246 99.228 l
+159.225 99.522 159.15 99.743 159.026 99.89 c
+158.909 100.037 158.736 100.11 158.512 100.11 c
+158.217 100.11 158.001 100.011 157.864 99.816 c
+157.725 99.629 157.652 99.321 157.644 98.89 c
+157.644 98.376 l
+157.644 97.906 157.71 97.571 157.85 97.376 c
+157.997 97.189 158.217 97.097 158.512 97.097 c
+161.261 100.183 m
+161.514 100.507 161.833 100.669 162.215 100.669 c
+162.921 100.669 163.277 100.198 163.289 99.258 c
+163.289 96.612 l
+162.642 96.612 l
+162.642 99.228 l
+162.642 99.541 162.587 99.761 162.48 99.89 c
+162.37 100.015 162.215 100.081 162.009 100.081 c
+161.851 100.081 161.705 100.026 161.569 99.919 c
+161.44 99.809 161.338 99.673 161.261 99.508 c
+161.261 96.612 l
+160.613 96.612 l
+160.613 102.257 l
+161.261 102.257 l
+h
+164.949 96.612 -0.646 3.983 re
+164.993 101.639 m
+164.993 101.529 164.964 101.437 164.906 101.359 c
+164.847 101.29 164.751 101.257 164.627 101.257 c
+164.509 101.257 164.413 101.29 164.347 101.359 c
+164.288 101.437 164.259 101.529 164.259 101.639 c
+164.259 101.756 164.288 101.848 164.347 101.918 c
+164.413 101.995 164.509 102.036 164.627 102.036 c
+164.751 102.036 164.847 101.995 164.906 101.918 c
+164.964 101.837 164.993 101.746 164.993 101.639 c
+166.67 96.612 -0.647 5.644 re
+167.551 98.787 m
+167.551 99.394 167.661 99.861 167.889 100.183 c
+168.124 100.507 168.452 100.669 168.874 100.669 c
+169.257 100.669 169.554 100.511 169.77 100.198 c
+169.77 102.257 l
+170.418 102.257 l
+170.418 96.612 l
+169.83 96.612 l
+169.786 97.038 l
+169.58 96.703 169.275 96.538 168.874 96.538 c
+168.463 96.538 168.139 96.693 167.904 97.008 c
+167.669 97.332 167.551 97.788 167.551 98.376 c
+h
+168.199 98.405 m
+168.199 97.964 168.261 97.634 168.389 97.42 c
+168.525 97.214 168.746 97.112 169.051 97.112 c
+169.374 97.112 169.612 97.274 169.77 97.596 c
+169.77 99.61 l
+169.602 99.923 169.363 100.081 169.051 100.081 c
+168.746 100.081 168.525 99.978 168.389 99.772 c
+168.261 99.566 168.199 99.243 168.199 98.802 c
+h
+173.063 99.978 m
+172.976 99.996 172.876 100.007 172.77 100.007 c
+172.435 100.007 172.199 99.824 172.064 99.464 c
+172.064 96.612 l
+171.417 96.612 l
+171.417 100.595 l
+172.049 100.595 l
+172.064 100.183 l
+172.24 100.507 172.483 100.669 172.799 100.669 c
+172.905 100.669 172.993 100.647 173.063 100.61 c
+h
+175.062 96.538 m
+174.563 96.538 174.181 96.686 173.916 96.979 c
+173.651 97.274 173.519 97.707 173.519 98.288 c
+173.519 98.758 l
+173.519 99.354 173.644 99.82 173.901 100.154 c
+174.166 100.497 174.526 100.669 174.989 100.669 c
+175.449 100.669 175.79 100.514 176.018 100.213 c
+176.253 99.919 176.374 99.456 176.386 98.831 c
+176.386 98.405 l
+174.166 98.405 l
+174.166 98.317 l
+174.166 97.883 174.243 97.571 174.401 97.376 c
+174.566 97.189 174.798 97.097 175.092 97.097 c
+175.287 97.097 175.459 97.13 175.607 97.2 c
+175.754 97.277 175.889 97.395 176.018 97.553 c
+176.356 97.141 l
+176.07 96.736 175.64 96.538 175.062 96.538 c
+174.989 100.11 m
+174.713 100.11 174.512 100.015 174.387 99.831 c
+174.258 99.643 174.184 99.354 174.166 98.964 c
+175.739 98.964 l
+175.739 99.052 l
+175.717 99.434 175.65 99.703 175.533 99.861 c
+175.415 100.026 175.231 100.11 174.989 100.11 c
+177.782 100.595 m
+177.797 100.154 l
+178.05 100.497 178.374 100.669 178.767 100.669 c
+179.472 100.669 179.829 100.198 179.84 99.258 c
+179.84 96.612 l
+179.193 96.612 l
+179.193 99.228 l
+179.193 99.541 179.137 99.761 179.031 99.89 c
+178.921 100.015 178.767 100.081 178.561 100.081 c
+178.403 100.081 178.256 100.026 178.12 99.919 c
+177.992 99.809 177.888 99.673 177.811 99.508 c
+177.811 96.612 l
+177.165 96.612 l
+177.165 100.595 l
+h
+182.622 98.787 m
+182.622 99.394 182.732 99.861 182.96 100.183 c
+183.195 100.507 183.522 100.669 183.945 100.669 c
+184.327 100.669 184.625 100.511 184.841 100.198 c
+184.841 102.257 l
+185.487 102.257 l
+185.487 96.612 l
+184.899 96.612 l
+184.856 97.038 l
+184.65 96.703 184.345 96.538 183.945 96.538 c
+183.533 96.538 183.21 96.693 182.975 97.008 c
+182.74 97.332 182.622 97.788 182.622 98.376 c
+h
+183.268 98.405 m
+183.268 97.964 183.33 97.634 183.459 97.42 c
+183.596 97.214 183.816 97.112 184.121 97.112 c
+184.444 97.112 184.683 97.274 184.841 97.596 c
+184.841 99.61 l
+184.672 99.923 184.433 100.081 184.121 100.081 c
+183.816 100.081 183.596 99.978 183.459 99.772 c
+183.33 99.566 183.268 99.243 183.268 98.802 c
+h
+187.193 96.612 -0.647 3.983 re
+187.237 101.639 m
+187.237 101.529 187.208 101.437 187.149 101.359 c
+187.09 101.29 186.994 101.257 186.869 101.257 c
+186.752 101.257 186.657 101.29 186.59 101.359 c
+186.532 101.437 186.502 101.529 186.502 101.639 c
+186.502 101.756 186.532 101.848 186.59 101.918 c
+186.657 101.995 186.752 102.036 186.869 102.036 c
+186.994 102.036 187.09 101.995 187.149 101.918 c
+187.208 101.837 187.237 101.746 187.237 101.639 c
+189.853 99.978 m
+189.765 99.996 189.666 100.007 189.56 100.007 c
+189.225 100.007 188.99 99.824 188.854 99.464 c
+188.854 96.612 l
+188.207 96.612 l
+188.207 100.595 l
+188.839 100.595 l
+188.854 100.183 l
+189.03 100.507 189.273 100.669 189.589 100.669 c
+189.695 100.669 189.784 100.647 189.853 100.61 c
+h
+191.852 96.538 m
+191.353 96.538 190.971 96.686 190.706 96.979 c
+190.441 97.274 190.309 97.707 190.309 98.288 c
+190.309 98.758 l
+190.309 99.354 190.434 99.82 190.692 100.154 c
+190.956 100.497 191.316 100.669 191.779 100.669 c
+192.238 100.669 192.581 100.514 192.808 100.213 c
+193.043 99.919 193.165 99.456 193.175 98.831 c
+193.175 98.405 l
+190.956 98.405 l
+190.956 98.317 l
+190.956 97.883 191.033 97.571 191.191 97.376 c
+191.357 97.189 191.588 97.097 191.882 97.097 c
+192.076 97.097 192.249 97.13 192.396 97.2 c
+192.543 97.277 192.679 97.395 192.808 97.553 c
+193.146 97.141 l
+192.859 96.736 192.429 96.538 191.852 96.538 c
+191.779 100.11 m
+191.503 100.11 191.301 100.015 191.176 99.831 c
+191.048 99.643 190.975 99.354 190.956 98.964 c
+192.529 98.964 l
+192.529 99.052 l
+192.506 99.434 192.44 99.703 192.323 99.861 c
+192.205 100.026 192.022 100.11 191.779 100.11 c
+195.336 97.097 m
+195.55 97.097 195.722 97.16 195.851 97.288 c
+195.987 97.424 196.06 97.615 196.071 97.862 c
+196.689 97.862 l
+196.666 97.479 196.531 97.16 196.277 96.906 c
+196.02 96.659 195.708 96.538 195.336 96.538 c
+194.844 96.538 194.469 96.689 194.204 96.994 c
+193.947 97.307 193.822 97.773 193.822 98.39 c
+193.822 98.831 l
+193.822 99.427 193.947 99.882 194.204 100.198 c
+194.469 100.511 194.844 100.669 195.336 100.669 c
+195.737 100.669 196.057 100.536 196.292 100.272 c
+196.535 100.015 196.666 99.67 196.689 99.228 c
+196.071 99.228 l
+196.049 99.522 195.976 99.743 195.851 99.89 c
+195.733 100.037 195.56 100.11 195.336 100.11 c
+195.042 100.11 194.825 100.011 194.69 99.816 c
+194.55 99.629 194.476 99.321 194.469 98.89 c
+194.469 98.376 l
+194.469 97.906 194.535 97.571 194.675 97.376 c
+194.821 97.189 195.042 97.097 195.336 97.097 c
+198.291 101.551 m
+198.291 100.595 l
+198.893 100.595 l
+198.893 100.066 l
+198.291 100.066 l
+198.291 97.596 l
+198.291 97.439 198.313 97.322 198.364 97.243 c
+198.423 97.164 198.511 97.126 198.629 97.126 c
+198.717 97.126 198.805 97.141 198.893 97.17 c
+198.893 96.612 l
+198.746 96.564 198.592 96.538 198.438 96.538 c
+198.181 96.538 197.986 96.63 197.85 96.817 c
+197.71 97.002 197.644 97.262 197.644 97.596 c
+197.644 100.066 l
+197.042 100.066 l
+197.042 100.595 l
+197.644 100.595 l
+197.644 101.551 l
+h
+199.452 98.787 m
+199.452 99.364 199.588 99.82 199.864 100.154 c
+200.147 100.497 200.518 100.669 200.98 100.669 c
+201.44 100.669 201.807 100.499 202.083 100.169 c
+202.366 99.846 202.513 99.398 202.524 98.831 c
+202.524 98.405 l
+202.524 97.835 202.38 97.38 202.098 97.038 c
+201.822 96.703 201.455 96.538 200.995 96.538 c
+200.533 96.538 200.161 96.7 199.878 97.023 c
+199.602 97.354 199.459 97.795 199.452 98.346 c
+h
+200.099 98.405 m
+200.099 98.001 200.176 97.685 200.334 97.45 c
+200.499 97.214 200.72 97.097 200.995 97.097 c
+201.561 97.097 201.855 97.509 201.877 98.332 c
+201.877 98.787 l
+201.877 99.188 201.793 99.508 201.628 99.743 c
+201.47 99.986 201.252 100.11 200.98 100.11 c
+200.716 100.11 200.499 99.986 200.334 99.743 c
+200.176 99.508 200.099 99.188 200.099 98.787 c
+h
+205.008 99.978 m
+204.92 99.996 204.821 100.007 204.714 100.007 c
+204.38 100.007 204.145 99.824 204.009 99.464 c
+204.009 96.612 l
+203.361 96.612 l
+203.361 100.595 l
+203.994 100.595 l
+204.009 100.183 l
+204.185 100.507 204.427 100.669 204.743 100.669 c
+204.851 100.669 204.938 100.647 205.008 100.61 c
+h
+206.361 96.612 -0.647 3.983 re
+206.405 101.639 m
+206.405 101.529 206.376 101.437 206.316 101.359 c
+206.258 101.29 206.162 101.257 206.037 101.257 c
+205.919 101.257 205.824 101.29 205.758 101.359 c
+205.699 101.437 205.67 101.529 205.67 101.639 c
+205.67 101.756 205.699 101.848 205.758 101.918 c
+205.824 101.995 205.919 102.036 206.037 102.036 c
+206.162 102.036 206.258 101.995 206.316 101.918 c
+206.376 101.837 206.405 101.746 206.405 101.639 c
+208.801 96.538 m
+208.3 96.538 207.918 96.686 207.654 96.979 c
+207.39 97.274 207.257 97.707 207.257 98.288 c
+207.257 98.758 l
+207.257 99.354 207.382 99.82 207.639 100.154 c
+207.904 100.497 208.264 100.669 208.727 100.669 c
+209.187 100.669 209.528 100.514 209.756 100.213 c
+209.991 99.919 210.112 99.456 210.123 98.831 c
+210.123 98.405 l
+207.904 98.405 l
+207.904 98.317 l
+207.904 97.883 207.981 97.571 208.139 97.376 c
+208.304 97.189 208.536 97.097 208.83 97.097 c
+209.025 97.097 209.198 97.13 209.345 97.2 c
+209.491 97.277 209.628 97.395 209.756 97.553 c
+210.094 97.141 l
+209.807 96.736 209.377 96.538 208.801 96.538 c
+208.727 100.11 m
+208.452 100.11 208.25 100.015 208.124 99.831 c
+207.996 99.643 207.922 99.354 207.904 98.964 c
+209.476 98.964 l
+209.476 99.052 l
+209.455 99.434 209.389 99.703 209.271 99.861 c
+209.154 100.026 208.969 100.11 208.727 100.11 c
+212.843 97.626 m
+212.843 97.773 212.788 97.894 212.681 97.993 c
+212.571 98.089 212.365 98.207 212.063 98.346 c
+211.718 98.494 211.475 98.615 211.329 98.714 c
+211.182 98.82 211.072 98.938 211.005 99.067 c
+210.935 99.192 210.902 99.35 210.902 99.537 c
+210.902 99.861 211.02 100.129 211.255 100.345 c
+211.49 100.559 211.791 100.669 212.167 100.669 c
+212.549 100.669 212.857 100.555 213.092 100.331 c
+213.328 100.103 213.445 99.816 213.445 99.464 c
+212.799 99.464 l
+212.799 99.64 212.74 99.791 212.622 99.919 c
+212.505 100.044 212.35 100.11 212.167 100.11 c
+211.968 100.11 211.818 100.055 211.711 99.948 c
+211.6 99.849 211.55 99.717 211.55 99.552 c
+211.55 99.423 211.586 99.317 211.667 99.228 c
+211.744 99.148 211.935 99.045 212.24 98.92 c
+212.718 98.733 213.048 98.545 213.225 98.361 c
+213.401 98.184 213.489 97.957 213.489 97.685 c
+213.489 97.332 213.364 97.053 213.122 96.847 c
+212.886 96.641 212.571 96.538 212.181 96.538 c
+211.758 96.538 211.421 96.656 211.167 96.891 c
+210.91 97.133 210.785 97.439 210.785 97.802 c
+211.432 97.802 l
+211.439 97.575 211.509 97.399 211.637 97.274 c
+211.762 97.156 211.946 97.097 212.181 97.097 c
+212.394 97.097 212.556 97.145 212.666 97.243 c
+212.784 97.339 212.843 97.468 212.843 97.626 c
+215.724 98.743 m
+215.724 97.656 215.477 96.689 214.989 95.847 c
+214.725 95.4 214.449 95.076 214.166 94.878 c
+214.048 95.304 l
+214.349 95.597 214.596 96.05 214.783 96.656 c
+214.978 97.262 215.077 97.927 215.077 98.655 c
+215.077 98.787 l
+215.077 99.717 214.923 100.551 214.621 101.286 c
+214.453 101.687 214.261 102.006 214.048 102.242 c
+214.166 102.653 l
+214.438 102.466 214.702 102.168 214.96 101.756 c
+215.467 100.904 215.724 99.897 215.724 98.743 c
+221.636 97.758 m
+222.239 100.595 l
+222.886 100.595 l
+221.901 96.612 l
+221.387 96.612 l
+220.608 99.464 l
+219.858 96.612 l
+219.329 96.612 l
+218.373 100.595 l
+219.005 100.595 l
+219.623 97.831 l
+220.358 100.595 l
+220.872 100.595 l
+h
+224.209 100.183 m
+224.462 100.507 224.782 100.669 225.164 100.669 c
+225.87 100.669 226.226 100.198 226.237 99.258 c
+226.237 96.612 l
+225.59 96.612 l
+225.59 99.228 l
+225.59 99.541 225.536 99.761 225.428 99.89 c
+225.318 100.015 225.164 100.081 224.958 100.081 c
+224.801 100.081 224.653 100.026 224.518 99.919 c
+224.389 99.809 224.286 99.673 224.209 99.508 c
+224.209 96.612 l
+223.562 96.612 l
+223.562 102.257 l
+224.209 102.257 l
+h
+228.618 96.538 m
+228.119 96.538 227.737 96.686 227.472 96.979 c
+227.207 97.274 227.075 97.707 227.075 98.288 c
+227.075 98.758 l
+227.075 99.354 227.2 99.82 227.458 100.154 c
+227.722 100.497 228.082 100.669 228.545 100.669 c
+229.004 100.669 229.347 100.514 229.574 100.213 c
+229.809 99.919 229.931 99.456 229.941 98.831 c
+229.941 98.405 l
+227.722 98.405 l
+227.722 98.317 l
+227.722 97.883 227.799 97.571 227.957 97.376 c
+228.123 97.189 228.354 97.097 228.648 97.097 c
+228.842 97.097 229.015 97.13 229.162 97.2 c
+229.31 97.277 229.445 97.395 229.574 97.553 c
+229.912 97.141 l
+229.625 96.736 229.195 96.538 228.618 96.538 c
+228.545 100.11 m
+228.269 100.11 228.067 100.015 227.942 99.831 c
+227.814 99.643 227.741 99.354 227.722 98.964 c
+229.295 98.964 l
+229.295 99.052 l
+229.272 99.434 229.206 99.703 229.089 99.861 c
+228.971 100.026 228.788 100.11 228.545 100.11 c
+232.367 99.978 m
+232.279 99.996 232.179 100.007 232.073 100.007 c
+231.739 100.007 231.504 99.824 231.367 99.464 c
+231.367 96.612 l
+230.721 96.612 l
+230.721 100.595 l
+231.352 100.595 l
+231.367 100.183 l
+231.543 100.507 231.786 100.669 232.102 100.669 c
+232.209 100.669 232.297 100.647 232.367 100.61 c
+h
+234.365 96.538 m
+233.866 96.538 233.484 96.686 233.22 96.979 c
+232.954 97.274 232.823 97.707 232.823 98.288 c
+232.823 98.758 l
+232.823 99.354 232.948 99.82 233.205 100.154 c
+233.469 100.497 233.829 100.669 234.292 100.669 c
+234.752 100.669 235.094 100.514 235.321 100.213 c
+235.556 99.919 235.678 99.456 235.689 98.831 c
+235.689 98.405 l
+233.469 98.405 l
+233.469 98.317 l
+233.469 97.883 233.546 97.571 233.704 97.376 c
+233.87 97.189 234.101 97.097 234.396 97.097 c
+234.59 97.097 234.762 97.13 234.91 97.2 c
+235.057 97.277 235.192 97.395 235.321 97.553 c
+235.66 97.141 l
+235.373 96.736 234.943 96.538 234.365 96.538 c
+234.292 100.11 m
+234.016 100.11 233.815 100.015 233.69 99.831 c
+233.561 99.643 233.488 99.354 233.469 98.964 c
+235.042 98.964 l
+235.042 99.052 l
+235.02 99.434 234.954 99.703 234.836 99.861 c
+234.719 100.026 234.535 100.11 234.292 100.11 c
+f
+q 1 0 0 1 238.3974 97.1409 cm
+0 0 m
+0 0.166 0.051 0.301 0.162 0.412 c
+0.268 0.518 0.415 0.573 0.602 0.573 c
+0.768 0.573 0.911 0.518 1.028 0.412 c
+1.146 0.301 1.205 0.166 1.205 0 c
+1.205 -0.169 1.146 -0.305 1.028 -0.411 c
+0.911 -0.522 0.768 -0.573 0.602 -0.573 c
+0.426 -0.573 0.279 -0.522 0.162 -0.411 c
+0.051 -0.305 0 -0.169 0 0 c
+1.969 1.588 m
+1.969 2.234 2.087 2.72 2.322 3.042 c
+2.557 3.366 2.888 3.528 3.322 3.528 c
+3.675 3.528 3.946 3.385 4.145 3.102 c
+4.189 3.454 l
+5.13 3.454 l
+5.13 -0.529 l
+5.13 -1.036 4.986 -1.426 4.704 -1.691 c
+4.417 -1.962 4.012 -2.102 3.484 -2.102 c
+3.256 -2.102 3.021 -2.057 2.778 -1.97 c
+2.543 -1.881 2.366 -1.768 2.248 -1.631 c
+2.601 -0.912 l
+2.697 -1.018 2.826 -1.103 2.984 -1.161 c
+3.138 -1.228 3.285 -1.264 3.424 -1.264 c
+3.659 -1.264 3.825 -1.205 3.925 -1.087 c
+4.031 -0.977 4.086 -0.801 4.086 -0.559 c
+4.086 -0.206 l
+3.887 -0.47 3.63 -0.603 3.307 -0.603 c
+2.884 -0.603 2.557 -0.441 2.322 -0.118 c
+2.094 0.214 1.977 0.684 1.969 1.294 c
+h
+3.013 1.323 m
+3.013 0.948 3.061 0.68 3.16 0.515 c
+3.256 0.345 3.41 0.264 3.615 0.264 c
+3.829 0.264 3.987 0.341 4.086 0.5 c
+4.086 2.396 l
+3.976 2.562 3.821 2.646 3.615 2.646 c
+3.41 2.646 3.256 2.562 3.16 2.396 c
+3.061 2.227 3.013 1.959 3.013 1.588 c
+h
+6.982 -0.529 -1.044 3.983 re
+5.894 4.484 m
+5.894 4.638 5.942 4.767 6.041 4.866 c
+6.148 4.972 6.283 5.027 6.453 5.027 c
+6.629 5.027 6.765 4.972 6.865 4.866 c
+6.971 4.767 7.025 4.638 7.025 4.484 c
+7.025 4.314 6.971 4.179 6.865 4.072 c
+6.765 3.973 6.629 3.925 6.453 3.925 c
+6.283 3.925 6.148 3.973 6.041 4.072 c
+5.942 4.179 5.894 4.314 5.894 4.484 c
+8.995 4.424 m
+8.995 3.454 l
+9.525 3.454 l
+9.525 2.66 l
+8.995 2.66 l
+8.995 0.69 l
+8.995 0.533 9.014 0.426 9.055 0.368 c
+9.102 0.309 9.186 0.279 9.304 0.279 c
+9.411 0.279 9.495 0.287 9.554 0.309 c
+9.554 -0.5 l
+9.377 -0.565 9.186 -0.603 8.981 -0.603 c
+8.305 -0.603 7.96 -0.216 7.952 0.559 c
+7.952 2.66 l
+7.496 2.66 l
+7.496 3.454 l
+7.952 3.454 l
+7.952 4.424 l
+h
+11.185 -0.529 -1.043 3.983 re
+10.098 4.484 m
+10.098 4.638 10.146 4.767 10.245 4.866 c
+10.352 4.972 10.487 5.027 10.657 5.027 c
+10.833 5.027 10.969 4.972 11.068 4.866 c
+11.175 4.767 11.23 4.638 11.23 4.484 c
+11.23 4.314 11.175 4.179 11.068 4.072 c
+10.969 3.973 10.833 3.925 10.657 3.925 c
+10.487 3.925 10.352 3.973 10.245 4.072 c
+10.146 4.179 10.098 4.314 10.098 4.484 c
+11.862 1.588 m
+11.862 2.234 11.979 2.72 12.215 3.042 c
+12.45 3.366 12.781 3.528 13.215 3.528 c
+13.567 3.528 13.839 3.385 14.038 3.102 c
+14.081 3.454 l
+15.022 3.454 l
+15.022 -0.529 l
+15.022 -1.036 14.879 -1.426 14.596 -1.691 c
+14.31 -1.962 13.905 -2.102 13.375 -2.102 c
+13.148 -2.102 12.913 -2.057 12.67 -1.97 c
+12.435 -1.881 12.259 -1.768 12.141 -1.631 c
+12.494 -0.912 l
+12.589 -1.018 12.718 -1.103 12.876 -1.161 c
+13.03 -1.228 13.177 -1.264 13.317 -1.264 c
+13.552 -1.264 13.718 -1.205 13.817 -1.087 c
+13.923 -0.977 13.978 -0.801 13.978 -0.559 c
+13.978 -0.206 l
+13.78 -0.47 13.523 -0.603 13.2 -0.603 c
+12.777 -0.603 12.45 -0.441 12.215 -0.118 c
+11.987 0.214 11.869 0.684 11.862 1.294 c
+h
+12.905 1.323 m
+12.905 0.948 12.953 0.68 13.053 0.515 c
+13.148 0.345 13.302 0.264 13.508 0.264 c
+13.722 0.264 13.88 0.341 13.978 0.5 c
+13.978 2.396 l
+13.868 2.562 13.714 2.646 13.508 2.646 c
+13.302 2.646 13.148 2.562 13.053 2.396 c
+12.953 2.227 12.905 1.959 12.905 1.588 c
+h
+16.742 3.454 m
+16.771 3.057 l
+17.007 3.37 17.308 3.528 17.683 3.528 c
+18.366 3.528 18.719 3.046 18.741 2.087 c
+18.741 -0.529 l
+17.697 -0.529 l
+17.697 2.014 l
+17.697 2.238 17.661 2.4 17.595 2.499 c
+17.525 2.595 17.407 2.646 17.242 2.646 c
+17.055 2.646 16.907 2.55 16.801 2.367 c
+16.801 -0.529 l
+15.757 -0.529 l
+15.757 3.454 l
+h
+19.373 1.588 m
+19.373 2.194 19.513 2.668 19.8 3.013 c
+20.082 3.356 20.475 3.528 20.976 3.528 c
+21.483 3.528 21.879 3.356 22.166 3.013 c
+22.449 2.668 22.592 2.194 22.592 1.588 c
+22.592 1.323 l
+22.592 0.724 22.449 0.254 22.166 -0.088 c
+21.879 -0.434 21.483 -0.603 20.976 -0.603 c
+20.465 -0.603 20.068 -0.434 19.785 -0.088 c
+19.509 0.254 19.373 0.728 19.373 1.338 c
+h
+20.417 1.323 m
+20.417 0.617 20.6 0.264 20.976 0.264 c
+21.328 0.264 21.519 0.559 21.549 1.147 c
+21.549 1.588 l
+21.549 1.947 21.497 2.219 21.402 2.396 c
+21.302 2.572 21.159 2.66 20.976 2.66 c
+20.799 2.66 20.66 2.572 20.564 2.396 c
+20.465 2.219 20.417 1.947 20.417 1.588 c
+h
+25.194 2.44 m
+24.856 2.469 l
+24.569 2.469 24.378 2.344 24.282 2.102 c
+24.282 -0.529 l
+23.239 -0.529 l
+23.239 3.454 l
+24.209 3.454 l
+24.238 3.013 l
+24.404 3.356 24.635 3.528 24.93 3.528 c
+25.047 3.528 25.138 3.506 25.209 3.469 c
+h
+27.266 -0.603 m
+26.738 -0.603 26.318 -0.448 26.017 -0.133 c
+25.723 0.191 25.576 0.651 25.576 1.249 c
+25.576 1.558 l
+25.576 2.183 25.712 2.668 25.988 3.013 c
+26.26 3.356 26.653 3.528 27.164 3.528 c
+27.663 3.528 28.034 3.366 28.281 3.042 c
+28.535 2.72 28.666 2.242 28.678 1.617 c
+28.678 1.118 l
+26.605 1.118 l
+26.623 0.823 26.686 0.607 26.796 0.47 c
+26.914 0.331 27.093 0.264 27.34 0.264 c
+27.681 0.264 27.972 0.382 28.207 0.617 c
+28.619 -0.015 l
+28.49 -0.191 28.303 -0.334 28.06 -0.441 c
+27.814 -0.548 27.55 -0.603 27.266 -0.603 c
+26.62 1.837 m
+27.648 1.837 l
+27.648 1.941 l
+27.648 2.176 27.608 2.352 27.531 2.469 c
+27.461 2.595 27.332 2.66 27.149 2.66 c
+26.973 2.66 26.84 2.591 26.752 2.454 c
+26.671 2.326 26.627 2.12 26.62 1.837 c
+f
+Q
+q 1 0 0 1 37.7885 87.2048 cm
+0 0 m
+0 3.453 l
+-0.515 3.453 l
+-0.515 3.983 l
+0 3.983 l
+0 4.351 l
+0.007 4.781 0.12 5.115 0.338 5.35 c
+0.562 5.593 0.874 5.718 1.278 5.718 c
+1.425 5.718 1.565 5.695 1.705 5.659 c
+1.851 5.618 2.002 5.564 2.16 5.497 c
+2.042 4.924 l
+1.807 5.049 1.565 5.115 1.323 5.115 c
+1.076 5.115 0.903 5.045 0.808 4.909 c
+0.708 4.781 0.661 4.586 0.661 4.321 c
+0.661 3.983 l
+1.308 3.983 l
+1.308 3.453 l
+0.661 3.453 l
+0.661 0 l
+h
+2.469 0 -0.647 3.983 re
+4.218 0 -0.647 5.644 re
+6.659 -0.074 m
+6.158 -0.074 5.776 0.073 5.512 0.367 c
+5.247 0.661 5.115 1.095 5.115 1.675 c
+5.115 2.146 l
+5.115 2.741 5.24 3.208 5.497 3.542 c
+5.762 3.884 6.121 4.056 6.585 4.056 c
+7.044 4.056 7.386 3.902 7.613 3.601 c
+7.849 3.307 7.97 2.844 7.981 2.219 c
+7.981 1.793 l
+5.762 1.793 l
+5.762 1.705 l
+5.762 1.271 5.839 0.959 5.997 0.764 c
+6.162 0.577 6.393 0.484 6.688 0.484 c
+6.882 0.484 7.056 0.517 7.202 0.588 c
+7.349 0.665 7.485 0.783 7.613 0.941 c
+7.952 0.529 l
+7.665 0.124 7.235 -0.074 6.659 -0.074 c
+6.585 3.498 m
+6.31 3.498 6.107 3.403 5.982 3.218 c
+5.853 3.031 5.78 2.741 5.762 2.352 c
+7.334 2.352 l
+7.334 2.439 l
+7.312 2.822 7.247 3.09 7.129 3.248 c
+7.011 3.414 6.827 3.498 6.585 3.498 c
+11.185 0 -0.646 3.983 re
+11.23 5.026 m
+11.23 4.916 11.2 4.825 11.141 4.747 c
+11.083 4.677 10.987 4.644 10.862 4.644 c
+10.744 4.644 10.649 4.677 10.583 4.747 c
+10.524 4.825 10.495 4.916 10.495 5.026 c
+10.495 5.144 10.524 5.236 10.583 5.306 c
+10.649 5.383 10.744 5.423 10.862 5.423 c
+10.987 5.423 11.083 5.383 11.141 5.306 c
+11.2 5.225 11.23 5.134 11.23 5.026 c
+14.14 1.014 m
+14.14 1.161 14.085 1.282 13.978 1.381 c
+13.868 1.477 13.662 1.595 13.361 1.734 c
+13.016 1.881 12.773 2.003 12.626 2.102 c
+12.479 2.208 12.369 2.326 12.303 2.454 c
+12.232 2.58 12.2 2.738 12.2 2.925 c
+12.2 3.248 12.317 3.516 12.552 3.733 c
+12.787 3.946 13.089 4.056 13.464 4.056 c
+13.846 4.056 14.154 3.943 14.39 3.719 c
+14.625 3.491 14.742 3.204 14.742 2.851 c
+14.096 2.851 l
+14.096 3.027 14.037 3.179 13.919 3.307 c
+13.802 3.432 13.647 3.498 13.464 3.498 c
+13.265 3.498 13.115 3.443 13.009 3.336 c
+12.898 3.237 12.847 3.104 12.847 2.94 c
+12.847 2.811 12.883 2.705 12.964 2.616 c
+13.041 2.535 13.232 2.433 13.537 2.308 c
+14.015 2.12 14.346 1.932 14.522 1.749 c
+14.699 1.572 14.787 1.344 14.787 1.072 c
+14.787 0.72 14.662 0.44 14.42 0.235 c
+14.184 0.029 13.868 -0.074 13.479 -0.074 c
+13.056 -0.074 12.718 0.043 12.465 0.278 c
+12.207 0.521 12.082 0.827 12.082 1.19 c
+12.729 1.19 l
+12.737 0.962 12.806 0.786 12.935 0.661 c
+13.059 0.544 13.244 0.484 13.479 0.484 c
+13.692 0.484 13.853 0.532 13.963 0.631 c
+14.081 0.727 14.14 0.856 14.14 1.014 c
+20.226 1.793 m
+20.226 1.165 20.108 0.694 19.873 0.382 c
+19.645 0.077 19.328 -0.074 18.917 -0.074 c
+18.513 -0.074 18.204 0.077 17.992 0.382 c
+17.992 -1.529 l
+17.344 -1.529 l
+17.344 3.983 l
+17.932 3.983 l
+17.977 3.542 l
+18.19 3.884 18.499 4.056 18.902 4.056 c
+19.343 4.056 19.671 3.902 19.887 3.601 c
+20.101 3.296 20.215 2.84 20.226 2.234 c
+h
+19.579 2.175 m
+19.579 2.616 19.509 2.94 19.373 3.145 c
+19.233 3.358 19.012 3.469 18.711 3.469 c
+18.395 3.469 18.156 3.314 17.992 3.013 c
+17.992 0.941 l
+18.156 0.635 18.395 0.484 18.711 0.484 c
+19.006 0.484 19.218 0.588 19.359 0.793 c
+19.494 1.007 19.567 1.337 19.579 1.778 c
+h
+21.769 0 -0.646 5.644 re
+24.826 0 m
+24.786 0.087 24.76 0.235 24.753 0.44 c
+24.518 0.095 24.224 -0.074 23.871 -0.074 c
+23.507 -0.074 23.224 0.022 23.018 0.22 c
+22.82 0.426 22.724 0.712 22.724 1.087 c
+22.724 1.488 22.86 1.807 23.136 2.042 c
+23.407 2.285 23.783 2.41 24.253 2.41 c
+24.738 2.41 l
+24.738 2.836 l
+24.738 3.072 24.683 3.237 24.577 3.336 c
+24.466 3.443 24.305 3.498 24.091 3.498 c
+23.893 3.498 23.731 3.439 23.606 3.322 c
+23.488 3.204 23.43 3.057 23.43 2.881 c
+22.783 2.881 l
+22.783 3.075 22.842 3.266 22.96 3.453 c
+23.085 3.638 23.246 3.785 23.444 3.895 c
+23.65 4.002 23.878 4.056 24.136 4.056 c
+24.536 4.056 24.841 3.954 25.047 3.748 c
+25.26 3.542 25.374 3.248 25.385 2.865 c
+25.385 0.852 l
+25.385 0.548 25.422 0.282 25.503 0.058 c
+25.503 0 l
+h
+23.959 0.514 m
+24.124 0.514 24.275 0.558 24.415 0.646 c
+24.562 0.735 24.668 0.845 24.738 0.984 c
+24.738 1.925 l
+24.371 1.925 l
+24.055 1.925 23.812 1.855 23.636 1.72 c
+23.459 1.591 23.371 1.404 23.371 1.161 c
+23.371 0.933 23.415 0.768 23.503 0.661 c
+23.592 0.562 23.742 0.514 23.959 0.514 c
+27.766 0.484 m
+27.979 0.484 28.152 0.548 28.281 0.675 c
+28.417 0.812 28.49 1.003 28.501 1.249 c
+29.119 1.249 l
+29.096 0.866 28.961 0.548 28.707 0.294 c
+28.45 0.047 28.138 -0.074 27.766 -0.074 c
+27.274 -0.074 26.899 0.077 26.634 0.382 c
+26.377 0.694 26.252 1.161 26.252 1.778 c
+26.252 2.219 l
+26.252 2.815 26.377 3.27 26.634 3.586 c
+26.899 3.898 27.274 4.056 27.766 4.056 c
+28.167 4.056 28.487 3.924 28.722 3.659 c
+28.964 3.403 29.096 3.057 29.119 2.616 c
+28.501 2.616 l
+28.479 2.91 28.406 3.131 28.281 3.278 c
+28.163 3.424 27.99 3.498 27.766 3.498 c
+27.472 3.498 27.255 3.399 27.12 3.204 c
+26.979 3.017 26.906 2.708 26.899 2.278 c
+26.899 1.764 l
+26.899 1.294 26.965 0.959 27.105 0.764 c
+27.251 0.577 27.472 0.484 27.766 0.484 c
+31.294 -0.074 m
+30.794 -0.074 30.412 0.073 30.147 0.367 c
+29.883 0.661 29.751 1.095 29.751 1.675 c
+29.751 2.146 l
+29.751 2.741 29.875 3.208 30.133 3.542 c
+30.397 3.884 30.757 4.056 31.22 4.056 c
+31.679 4.056 32.022 3.902 32.249 3.601 c
+32.485 3.307 32.606 2.844 32.616 2.219 c
+32.616 1.793 l
+30.397 1.793 l
+30.397 1.705 l
+30.397 1.271 30.474 0.959 30.632 0.764 c
+30.798 0.577 31.029 0.484 31.323 0.484 c
+31.518 0.484 31.691 0.517 31.837 0.588 c
+31.985 0.665 32.121 0.783 32.249 0.941 c
+32.587 0.529 l
+32.301 0.124 31.87 -0.074 31.294 -0.074 c
+31.22 3.498 m
+30.945 3.498 30.742 3.403 30.617 3.218 c
+30.489 3.031 30.416 2.741 30.397 2.352 c
+31.97 2.352 l
+31.97 2.439 l
+31.948 2.822 31.882 3.09 31.764 3.248 c
+31.646 3.414 31.463 3.498 31.22 3.498 c
+33.264 2.175 m
+33.264 2.782 33.374 3.248 33.601 3.571 c
+33.837 3.895 34.164 4.056 34.586 4.056 c
+34.968 4.056 35.266 3.898 35.483 3.586 c
+35.483 5.644 l
+36.13 5.644 l
+36.13 0 l
+35.542 0 l
+35.498 0.426 l
+35.292 0.091 34.987 -0.074 34.586 -0.074 c
+34.175 -0.074 33.852 0.081 33.616 0.396 c
+33.381 0.72 33.264 1.176 33.264 1.764 c
+h
+33.91 1.793 m
+33.91 1.352 33.973 1.022 34.102 0.808 c
+34.237 0.602 34.457 0.5 34.763 0.5 c
+35.086 0.5 35.325 0.661 35.483 0.984 c
+35.483 2.998 l
+35.314 3.31 35.075 3.469 34.763 3.469 c
+34.457 3.469 34.237 3.366 34.102 3.16 c
+33.973 2.954 33.91 2.63 33.91 2.19 c
+h
+37.173 0.353 m
+37.173 0.47 37.206 0.565 37.277 0.646 c
+37.343 0.723 37.445 0.764 37.585 0.764 c
+37.732 0.764 37.838 0.723 37.908 0.646 c
+37.985 0.565 38.026 0.47 38.026 0.353 c
+38.026 0.242 37.985 0.151 37.908 0.073 c
+37.838 -0.004 37.732 -0.044 37.585 -0.044 c
+37.445 -0.044 37.343 -0.004 37.277 0.073 c
+37.206 0.151 37.173 0.242 37.173 0.353 c
+f
+Q
+q 1 0 0 1 37.6521 155.6032 cm
+0 0 m
+2.646 4.835 l
+3.204 4.835 l
+0.588 0 l
+h
+6.059 0.515 m
+7.279 0.515 l
+7.279 0.058 l
+4.119 0.058 l
+4.119 0.515 l
+5.486 0.515 l
+5.486 4.38 l
+4.546 4.38 l
+4.546 4.835 l
+6.059 4.835 l
+h
+11.281 1.808 m
+11.281 1.502 11.241 1.234 11.164 0.999 c
+11.094 0.771 10.992 0.588 10.855 0.44 c
+10.726 0.294 10.565 0.18 10.37 0.103 c
+10.172 0.037 9.951 0 9.708 0 c
+9.473 0 9.261 0.037 9.076 0.103 c
+8.889 0.18 8.727 0.294 8.592 0.44 c
+8.452 0.588 8.345 0.771 8.268 0.999 c
+8.199 1.223 8.166 1.492 8.166 1.808 c
+8.166 2.403 8.301 2.851 8.577 3.145 c
+8.849 3.447 9.238 3.601 9.739 3.601 c
+10.256 3.601 10.643 3.447 10.899 3.145 c
+11.152 2.851 11.281 2.403 11.281 1.808 c
+10.679 1.808 m
+10.679 2.072 10.653 2.293 10.605 2.469 c
+10.554 2.645 10.487 2.782 10.4 2.881 c
+10.319 2.987 10.223 3.061 10.105 3.102 c
+9.995 3.149 9.874 3.175 9.739 3.175 c
+9.598 3.175 9.473 3.149 9.356 3.102 c
+9.238 3.061 9.136 2.987 9.047 2.881 c
+8.966 2.77 8.904 2.631 8.856 2.454 c
+8.804 2.278 8.783 2.061 8.783 1.808 c
+8.783 1.532 8.804 1.308 8.856 1.132 c
+8.904 0.955 8.966 0.812 9.047 0.706 c
+9.136 0.595 9.231 0.521 9.342 0.484 c
+9.459 0.444 9.58 0.426 9.708 0.426 c
+9.845 0.426 9.974 0.444 10.091 0.484 c
+10.209 0.521 10.311 0.595 10.4 0.706 c
+10.487 0.812 10.554 0.955 10.605 1.132 c
+10.653 1.308 10.679 1.532 10.679 1.808 c
+13.799 -1.309 m
+13.6 -1.309 13.431 -1.283 13.284 -1.235 c
+13.137 -1.195 13.009 -1.135 12.902 -1.058 c
+12.792 -0.989 12.704 -0.9 12.638 -0.794 c
+12.579 -0.684 12.534 -0.566 12.505 -0.441 c
+13.093 -0.368 l
+13.13 -0.522 13.211 -0.647 13.328 -0.736 c
+13.454 -0.823 13.615 -0.867 13.814 -0.867 c
+13.931 -0.867 14.038 -0.846 14.136 -0.809 c
+14.232 -0.779 14.317 -0.721 14.387 -0.632 c
+14.453 -0.551 14.508 -0.449 14.549 -0.324 c
+14.585 -0.195 14.607 -0.037 14.607 0.147 c
+14.607 0.764 l
+14.566 0.683 14.512 0.602 14.445 0.515 c
+14.387 0.434 14.306 0.359 14.21 0.294 c
+14.122 0.235 14.019 0.183 13.901 0.147 c
+13.784 0.118 13.652 0.103 13.504 0.103 c
+13.277 0.103 13.086 0.135 12.931 0.205 c
+12.785 0.272 12.66 0.374 12.564 0.515 c
+12.465 0.661 12.391 0.841 12.343 1.058 c
+12.292 1.282 12.27 1.535 12.27 1.822 c
+12.27 2.105 12.292 2.356 12.343 2.572 c
+12.391 2.786 12.465 2.969 12.564 3.116 c
+12.66 3.27 12.792 3.388 12.961 3.469 c
+13.126 3.546 13.328 3.586 13.564 3.586 c
+13.806 3.586 14.019 3.528 14.196 3.41 c
+14.372 3.299 14.508 3.145 14.607 2.94 c
+14.622 2.94 l
+14.622 3.116 l
+14.622 3.307 l
+14.629 3.366 14.637 3.414 14.637 3.454 c
+14.644 3.501 14.651 3.532 14.651 3.542 c
+15.21 3.542 l
+15.199 3.52 15.195 3.487 15.195 3.439 c
+15.195 3.388 15.188 3.329 15.181 3.262 c
+15.181 3.057 l
+15.181 2.822 l
+15.181 0.162 l
+15.181 -0.327 15.067 -0.695 14.842 -0.941 c
+14.614 -1.183 14.269 -1.309 13.799 -1.309 c
+14.607 1.837 m
+14.607 2.072 14.581 2.271 14.533 2.439 c
+14.483 2.616 14.416 2.752 14.328 2.851 c
+14.24 2.958 14.14 3.035 14.034 3.087 c
+13.924 3.135 13.818 3.16 13.71 3.16 c
+13.564 3.16 13.435 3.135 13.328 3.087 c
+13.23 3.035 13.145 2.958 13.078 2.851 c
+13.009 2.752 12.954 2.616 12.917 2.439 c
+12.887 2.271 12.873 2.072 12.873 1.837 c
+12.873 1.58 12.887 1.371 12.917 1.205 c
+12.954 1.036 13.005 0.9 13.064 0.793 c
+13.13 0.694 13.218 0.625 13.328 0.588 c
+13.435 0.548 13.556 0.529 13.696 0.529 c
+13.814 0.529 13.924 0.548 14.034 0.588 c
+14.14 0.635 14.232 0.709 14.313 0.808 c
+14.402 0.914 14.468 1.051 14.519 1.22 c
+14.578 1.385 14.607 1.591 14.607 1.837 c
+19.289 1.058 m
+19.289 0.889 19.252 0.738 19.186 0.602 c
+19.127 0.474 19.032 0.367 18.907 0.279 c
+18.789 0.191 18.642 0.121 18.466 0.073 c
+18.296 0.025 18.102 0 17.878 0 c
+17.672 0 17.485 0.014 17.319 0.044 c
+17.161 0.073 17.018 0.118 16.893 0.176 c
+16.775 0.242 16.673 0.33 16.584 0.44 c
+16.503 0.558 16.452 0.694 16.422 0.852 c
+16.922 0.955 l
+16.952 0.837 16.995 0.742 17.055 0.675 c
+17.113 0.606 17.18 0.551 17.261 0.515 c
+17.338 0.474 17.425 0.448 17.525 0.44 c
+17.631 0.43 17.749 0.426 17.878 0.426 c
+18.003 0.426 18.117 0.434 18.216 0.455 c
+18.323 0.474 18.414 0.503 18.495 0.544 c
+18.572 0.592 18.631 0.65 18.672 0.72 c
+18.719 0.786 18.745 0.87 18.745 0.97 c
+18.745 1.076 18.716 1.165 18.657 1.234 c
+18.605 1.3 18.532 1.356 18.437 1.396 c
+18.348 1.444 18.234 1.484 18.098 1.514 c
+17.97 1.543 17.834 1.58 17.687 1.631 c
+17.547 1.66 17.411 1.693 17.275 1.734 c
+17.136 1.782 17.014 1.841 16.908 1.911 c
+16.797 1.977 16.709 2.065 16.643 2.175 c
+16.584 2.281 16.555 2.418 16.555 2.587 c
+16.555 2.899 16.665 3.145 16.893 3.322 c
+17.117 3.498 17.444 3.586 17.878 3.586 c
+18.054 3.586 18.216 3.572 18.363 3.542 c
+18.51 3.513 18.635 3.461 18.745 3.395 c
+18.863 3.326 18.958 3.237 19.039 3.131 c
+19.116 3.031 19.171 2.91 19.2 2.763 c
+18.686 2.69 l
+18.664 2.786 18.631 2.866 18.583 2.925 c
+18.532 2.984 18.473 3.027 18.407 3.057 c
+18.337 3.094 18.252 3.119 18.157 3.131 c
+18.069 3.149 17.973 3.16 17.878 3.16 c
+17.356 3.16 17.099 2.998 17.099 2.675 c
+17.099 2.576 17.117 2.499 17.157 2.439 c
+17.205 2.381 17.267 2.326 17.348 2.278 c
+17.437 2.238 17.54 2.204 17.658 2.175 c
+17.775 2.146 17.899 2.117 18.04 2.087 c
+18.175 2.046 18.319 2.003 18.466 1.955 c
+18.613 1.914 18.745 1.855 18.863 1.778 c
+18.988 1.708 19.09 1.616 19.171 1.499 c
+19.248 1.381 19.289 1.234 19.289 1.058 c
+20.307 0 m
+22.952 4.835 l
+23.511 4.835 l
+20.895 0 l
+h
+26.161 3.881 m
+27.013 4.203 l
+27.145 3.792 l
+26.249 3.557 l
+26.836 2.749 l
+26.455 2.514 l
+25.969 3.351 l
+25.47 2.528 l
+25.088 2.749 l
+25.705 3.557 l
+24.793 3.792 l
+24.941 4.218 l
+25.808 3.865 l
+25.764 4.821 l
+26.204 4.821 l
+h
+f
+Q
+q 1 0 0 1 39.5044 148.4743 cm
+0 0 m
+-0.485 0 l
+-0.559 3.057 l
+0.073 3.057 l
+h
+-0.559 -0.632 0.617 -0.647 re
+-0.559 -1.279 m
+4.207 -0.823 m
+5.427 -0.823 l
+5.427 -1.279 l
+2.267 -1.279 l
+2.267 -0.823 l
+3.634 -0.823 l
+3.634 3.042 l
+2.693 3.042 l
+2.693 3.498 l
+4.207 3.498 l
+h
+9.429 0.47 m
+9.429 0.165 9.389 -0.103 9.311 -0.339 c
+9.242 -0.566 9.139 -0.75 9.003 -0.897 c
+8.874 -1.044 8.713 -1.158 8.517 -1.235 c
+8.32 -1.301 8.099 -1.338 7.856 -1.338 c
+7.621 -1.338 7.408 -1.301 7.224 -1.235 c
+7.037 -1.158 6.875 -1.044 6.739 -0.897 c
+6.599 -0.75 6.493 -0.566 6.416 -0.339 c
+6.346 -0.115 6.313 0.154 6.313 0.47 c
+6.313 1.065 6.449 1.514 6.724 1.807 c
+6.996 2.109 7.386 2.263 7.886 2.263 c
+8.404 2.263 8.79 2.109 9.047 1.807 c
+9.3 1.514 9.429 1.065 9.429 0.47 c
+8.827 0.47 m
+8.827 0.735 8.801 0.955 8.753 1.132 c
+8.702 1.308 8.635 1.444 8.548 1.543 c
+8.467 1.649 8.371 1.723 8.253 1.764 c
+8.143 1.811 8.022 1.837 7.886 1.837 c
+7.746 1.837 7.621 1.811 7.503 1.764 c
+7.386 1.723 7.283 1.649 7.195 1.543 c
+7.114 1.433 7.052 1.294 7.004 1.117 c
+6.952 0.941 6.93 0.723 6.93 0.47 c
+6.93 0.195 6.952 -0.03 7.004 -0.206 c
+7.052 -0.383 7.114 -0.526 7.195 -0.632 c
+7.283 -0.743 7.379 -0.817 7.489 -0.853 c
+7.607 -0.894 7.728 -0.912 7.856 -0.912 c
+7.993 -0.912 8.121 -0.894 8.239 -0.853 c
+8.357 -0.817 8.459 -0.743 8.548 -0.632 c
+8.635 -0.526 8.702 -0.383 8.753 -0.206 c
+8.801 -0.03 8.827 0.195 8.827 0.47 c
+11.946 -2.646 m
+11.748 -2.646 11.579 -2.621 11.432 -2.573 c
+11.285 -2.532 11.156 -2.473 11.05 -2.396 c
+10.94 -2.326 10.851 -2.238 10.786 -2.132 c
+10.726 -2.022 10.682 -1.904 10.653 -1.779 c
+11.241 -1.706 l
+11.278 -1.86 11.358 -1.985 11.476 -2.073 c
+11.601 -2.161 11.763 -2.205 11.962 -2.205 c
+12.079 -2.205 12.185 -2.183 12.284 -2.147 c
+12.38 -2.117 12.465 -2.058 12.534 -1.97 c
+12.6 -1.889 12.656 -1.786 12.696 -1.661 c
+12.733 -1.532 12.755 -1.374 12.755 -1.191 c
+12.755 -0.574 l
+12.714 -0.655 12.66 -0.736 12.593 -0.823 c
+12.534 -0.904 12.454 -0.978 12.358 -1.044 c
+12.27 -1.103 12.167 -1.154 12.049 -1.191 c
+11.931 -1.22 11.8 -1.235 11.652 -1.235 c
+11.424 -1.235 11.233 -1.202 11.079 -1.133 c
+10.932 -1.066 10.807 -0.963 10.711 -0.823 c
+10.612 -0.676 10.539 -0.497 10.491 -0.279 c
+10.439 -0.055 10.418 0.198 10.418 0.484 c
+10.418 0.768 10.439 1.018 10.491 1.234 c
+10.539 1.448 10.612 1.631 10.711 1.778 c
+10.807 1.932 10.94 2.05 11.108 2.131 c
+11.274 2.208 11.476 2.248 11.711 2.248 c
+11.954 2.248 12.167 2.19 12.343 2.072 c
+12.519 1.961 12.656 1.807 12.755 1.602 c
+12.77 1.602 l
+12.77 1.778 l
+12.77 1.969 l
+12.777 2.028 12.785 2.076 12.785 2.117 c
+12.791 2.164 12.799 2.194 12.799 2.204 c
+13.358 2.204 l
+13.346 2.183 13.343 2.15 13.343 2.102 c
+13.343 2.05 13.336 1.992 13.328 1.925 c
+13.328 1.72 l
+13.328 1.484 l
+13.328 -1.176 l
+13.328 -1.665 13.215 -2.033 12.99 -2.278 c
+12.762 -2.521 12.417 -2.646 11.946 -2.646 c
+12.755 0.5 m
+12.755 0.735 12.729 0.933 12.681 1.102 c
+12.63 1.278 12.564 1.414 12.476 1.514 c
+12.388 1.62 12.288 1.697 12.182 1.749 c
+12.072 1.797 11.965 1.822 11.858 1.822 c
+11.711 1.822 11.582 1.797 11.476 1.749 c
+11.377 1.697 11.293 1.62 11.226 1.514 c
+11.156 1.414 11.102 1.278 11.065 1.102 c
+11.035 0.933 11.021 0.735 11.021 0.5 c
+11.021 0.242 11.035 0.033 11.065 -0.133 c
+11.102 -0.302 11.152 -0.437 11.212 -0.545 c
+11.278 -0.643 11.366 -0.713 11.476 -0.75 c
+11.582 -0.79 11.704 -0.809 11.844 -0.809 c
+11.962 -0.809 12.072 -0.79 12.182 -0.75 c
+12.288 -0.703 12.38 -0.628 12.461 -0.53 c
+12.549 -0.423 12.615 -0.287 12.667 -0.118 c
+12.725 0.047 12.755 0.253 12.755 0.5 c
+17.437 -0.279 m
+17.437 -0.449 17.4 -0.599 17.334 -0.736 c
+17.275 -0.864 17.18 -0.971 17.055 -1.058 c
+16.937 -1.147 16.789 -1.216 16.613 -1.264 c
+16.444 -1.312 16.249 -1.338 16.025 -1.338 c
+15.819 -1.338 15.632 -1.324 15.467 -1.294 c
+15.309 -1.264 15.166 -1.22 15.041 -1.162 c
+14.923 -1.096 14.82 -1.008 14.732 -0.897 c
+14.651 -0.78 14.599 -0.643 14.57 -0.485 c
+15.07 -0.383 l
+15.1 -0.5 15.143 -0.595 15.202 -0.662 c
+15.261 -0.732 15.327 -0.786 15.408 -0.823 c
+15.486 -0.864 15.573 -0.89 15.673 -0.897 c
+15.779 -0.908 15.897 -0.912 16.025 -0.912 c
+16.151 -0.912 16.264 -0.904 16.363 -0.882 c
+16.47 -0.864 16.562 -0.834 16.643 -0.794 c
+16.72 -0.746 16.779 -0.688 16.819 -0.618 c
+16.867 -0.551 16.893 -0.468 16.893 -0.368 c
+16.893 -0.262 16.864 -0.173 16.804 -0.103 c
+16.753 -0.038 16.679 0.018 16.584 0.058 c
+16.496 0.106 16.382 0.147 16.246 0.176 c
+16.118 0.205 15.981 0.242 15.835 0.294 c
+15.694 0.323 15.559 0.356 15.422 0.396 c
+15.283 0.444 15.162 0.503 15.056 0.573 c
+14.945 0.639 14.857 0.727 14.79 0.837 c
+14.732 0.944 14.703 1.08 14.703 1.249 c
+14.703 1.562 14.813 1.807 15.041 1.984 c
+15.264 2.16 15.592 2.248 16.025 2.248 c
+16.201 2.248 16.363 2.234 16.511 2.204 c
+16.658 2.175 16.783 2.123 16.893 2.057 c
+17.01 1.988 17.105 1.899 17.186 1.793 c
+17.264 1.693 17.319 1.572 17.348 1.425 c
+16.834 1.352 l
+16.812 1.448 16.779 1.529 16.731 1.587 c
+16.679 1.646 16.621 1.69 16.554 1.72 c
+16.485 1.756 16.4 1.782 16.305 1.793 c
+16.216 1.811 16.121 1.822 16.025 1.822 c
+15.503 1.822 15.247 1.66 15.247 1.337 c
+15.247 1.238 15.264 1.161 15.305 1.102 c
+15.353 1.043 15.415 0.988 15.496 0.941 c
+15.584 0.9 15.688 0.866 15.805 0.837 c
+15.923 0.808 16.047 0.779 16.187 0.749 c
+16.323 0.708 16.467 0.665 16.613 0.617 c
+16.76 0.577 16.893 0.517 17.01 0.44 c
+17.136 0.371 17.238 0.278 17.319 0.161 c
+17.396 0.043 17.437 -0.103 17.437 -0.279 c
+18.454 -1.338 m
+21.1 3.498 l
+21.659 3.498 l
+19.042 -1.338 l
+h
+23.75 -0.324 0.75 -0.956 re
+23.75 -1.279 m
+28.193 -2.646 m
+27.995 -2.646 27.825 -2.621 27.678 -2.573 c
+27.531 -2.532 27.403 -2.473 27.296 -2.396 c
+27.186 -2.326 27.097 -2.238 27.031 -2.132 c
+26.973 -2.022 26.929 -1.904 26.9 -1.779 c
+27.487 -1.706 l
+27.524 -1.86 27.605 -1.985 27.723 -2.073 c
+27.847 -2.161 28.009 -2.205 28.207 -2.205 c
+28.325 -2.205 28.431 -2.183 28.531 -2.147 c
+28.626 -2.117 28.71 -2.058 28.781 -1.97 c
+28.847 -1.889 28.901 -1.786 28.942 -1.661 c
+28.979 -1.532 29.001 -1.374 29.001 -1.191 c
+29.001 -0.574 l
+28.961 -0.655 28.905 -0.736 28.839 -0.823 c
+28.781 -0.904 28.7 -0.978 28.604 -1.044 c
+28.516 -1.103 28.413 -1.154 28.296 -1.191 c
+28.178 -1.22 28.045 -1.235 27.899 -1.235 c
+27.671 -1.235 27.48 -1.202 27.326 -1.133 c
+27.178 -1.066 27.054 -0.963 26.958 -0.823 c
+26.859 -0.676 26.785 -0.497 26.738 -0.279 c
+26.686 -0.055 26.664 0.198 26.664 0.484 c
+26.664 0.768 26.686 1.018 26.738 1.234 c
+26.785 1.448 26.859 1.631 26.958 1.778 c
+27.054 1.932 27.186 2.05 27.355 2.131 c
+27.52 2.208 27.723 2.248 27.958 2.248 c
+28.2 2.248 28.413 2.19 28.589 2.072 c
+28.766 1.961 28.901 1.807 29.001 1.602 c
+29.016 1.602 l
+29.016 1.778 l
+29.016 1.969 l
+29.023 2.028 29.03 2.076 29.03 2.117 c
+29.038 2.164 29.045 2.194 29.045 2.204 c
+29.604 2.204 l
+29.593 2.183 29.589 2.15 29.589 2.102 c
+29.589 2.05 29.582 1.992 29.574 1.925 c
+29.574 1.72 l
+29.574 1.484 l
+29.574 -1.176 l
+29.574 -1.665 29.46 -2.033 29.236 -2.278 c
+29.009 -2.521 28.663 -2.646 28.193 -2.646 c
+29.001 0.5 m
+29.001 0.735 28.976 0.933 28.928 1.102 c
+28.876 1.278 28.81 1.414 28.722 1.514 c
+28.633 1.62 28.535 1.697 28.428 1.749 c
+28.317 1.797 28.211 1.822 28.105 1.822 c
+27.958 1.822 27.829 1.797 27.723 1.749 c
+27.623 1.697 27.538 1.62 27.472 1.514 c
+27.403 1.414 27.347 1.278 27.311 1.102 c
+27.281 0.933 27.266 0.735 27.266 0.5 c
+27.266 0.242 27.281 0.033 27.311 -0.133 c
+27.347 -0.302 27.399 -0.437 27.457 -0.545 c
+27.524 -0.643 27.612 -0.713 27.723 -0.75 c
+27.829 -0.79 27.95 -0.809 28.09 -0.809 c
+28.207 -0.809 28.317 -0.79 28.428 -0.75 c
+28.535 -0.703 28.626 -0.628 28.707 -0.53 c
+28.795 -0.423 28.861 -0.287 28.913 -0.118 c
+28.972 0.047 29.001 0.253 29.001 0.5 c
+32.664 -0.823 m
+33.885 -0.823 l
+33.885 -1.279 l
+30.725 -1.279 l
+30.725 -0.823 l
+32.091 -0.823 l
+32.091 1.749 l
+31.062 1.749 l
+31.062 2.204 l
+32.664 2.204 l
+h
+32.018 3.498 0.646 -0.617 re
+32.018 2.881 m
+34.95 1.749 m
+34.95 2.204 l
+35.494 2.204 l
+35.671 3.101 l
+36.068 3.101 l
+36.068 2.204 l
+37.449 2.204 l
+37.449 1.749 l
+36.068 1.749 l
+36.068 -0.353 l
+36.068 -0.522 36.111 -0.647 36.2 -0.736 c
+36.288 -0.817 36.439 -0.853 36.656 -0.853 c
+36.733 -0.853 36.817 -0.849 36.905 -0.838 c
+37.001 -0.831 37.09 -0.82 37.171 -0.809 c
+37.258 -0.801 37.335 -0.79 37.406 -0.78 c
+37.483 -0.772 37.545 -0.757 37.597 -0.736 c
+37.597 -1.176 l
+37.556 -1.199 37.497 -1.216 37.42 -1.235 c
+37.339 -1.246 37.25 -1.261 37.155 -1.279 c
+37.067 -1.287 36.968 -1.297 36.861 -1.309 c
+36.751 -1.327 36.641 -1.338 36.523 -1.338 c
+36.178 -1.338 35.92 -1.264 35.744 -1.118 c
+35.567 -0.971 35.48 -0.736 35.48 -0.412 c
+35.48 1.749 l
+h
+41.333 -1.279 m
+40.157 0.323 l
+39.731 0.014 l
+39.731 -1.279 l
+39.158 -1.279 l
+39.158 3.498 l
+39.731 3.498 l
+39.731 0.514 l
+41.26 2.204 l
+41.936 2.204 l
+40.525 0.706 l
+42.01 -1.279 l
+h
+43.483 0.338 m
+43.483 0.151 43.502 -0.023 43.542 -0.177 c
+43.59 -0.324 43.652 -0.452 43.733 -0.559 c
+43.81 -0.669 43.91 -0.757 44.028 -0.823 c
+44.152 -0.882 44.296 -0.912 44.454 -0.912 c
+44.571 -0.912 44.678 -0.897 44.776 -0.867 c
+44.884 -0.838 44.975 -0.805 45.056 -0.765 c
+45.133 -0.717 45.196 -0.659 45.247 -0.588 c
+45.306 -0.522 45.343 -0.452 45.364 -0.383 c
+45.879 -0.515 l
+45.838 -0.614 45.784 -0.713 45.717 -0.809 c
+45.648 -0.908 45.556 -0.996 45.439 -1.073 c
+45.328 -1.154 45.192 -1.216 45.027 -1.264 c
+44.869 -1.312 44.678 -1.338 44.454 -1.338 c
+44.207 -1.338 43.987 -1.301 43.792 -1.235 c
+43.594 -1.158 43.428 -1.044 43.292 -0.897 c
+43.164 -0.743 43.06 -0.551 42.983 -0.324 c
+42.914 -0.088 42.881 0.18 42.881 0.484 c
+42.881 0.816 42.917 1.095 42.998 1.323 c
+43.087 1.547 43.204 1.726 43.351 1.866 c
+43.498 2.013 43.663 2.117 43.851 2.175 c
+44.034 2.234 44.233 2.263 44.439 2.263 c
+44.722 2.263 44.957 2.219 45.144 2.131 c
+45.339 2.042 45.497 1.914 45.615 1.749 c
+45.74 1.591 45.828 1.396 45.879 1.161 c
+45.938 0.933 45.967 0.683 45.967 0.411 c
+45.967 0.338 l
+h
+44.439 1.837 m
+44.34 1.837 44.236 1.822 44.13 1.793 c
+44.02 1.764 43.917 1.705 43.822 1.616 c
+43.733 1.535 43.66 1.429 43.6 1.294 c
+43.542 1.165 43.505 0.992 43.498 0.779 c
+45.379 0.779 l
+45.358 0.984 45.321 1.15 45.262 1.278 c
+45.21 1.414 45.144 1.521 45.056 1.602 c
+44.975 1.69 44.884 1.749 44.776 1.778 c
+44.666 1.815 44.556 1.837 44.439 1.837 c
+47.544 0.338 m
+47.544 0.151 47.562 -0.023 47.602 -0.177 c
+47.65 -0.324 47.713 -0.452 47.793 -0.559 c
+47.87 -0.669 47.97 -0.757 48.088 -0.823 c
+48.213 -0.882 48.356 -0.912 48.514 -0.912 c
+48.632 -0.912 48.738 -0.897 48.838 -0.867 c
+48.944 -0.838 49.036 -0.805 49.117 -0.765 c
+49.194 -0.717 49.256 -0.659 49.308 -0.588 c
+49.366 -0.522 49.403 -0.452 49.426 -0.383 c
+49.94 -0.515 l
+49.9 -0.614 49.844 -0.713 49.778 -0.809 c
+49.709 -0.908 49.616 -0.996 49.499 -1.073 c
+49.389 -1.154 49.252 -1.216 49.087 -1.264 c
+48.93 -1.312 48.738 -1.338 48.514 -1.338 c
+48.267 -1.338 48.047 -1.301 47.853 -1.235 c
+47.654 -1.158 47.489 -1.044 47.353 -0.897 c
+47.224 -0.743 47.122 -0.551 47.044 -0.324 c
+46.974 -0.088 46.941 0.18 46.941 0.484 c
+46.941 0.816 46.978 1.095 47.059 1.323 c
+47.147 1.547 47.265 1.726 47.411 1.866 c
+47.558 2.013 47.724 2.117 47.911 2.175 c
+48.095 2.234 48.294 2.263 48.499 2.263 c
+48.782 2.263 49.017 2.219 49.204 2.131 c
+49.4 2.042 49.557 1.914 49.675 1.749 c
+49.8 1.591 49.888 1.396 49.94 1.161 c
+49.998 0.933 50.028 0.683 50.028 0.411 c
+50.028 0.338 l
+h
+48.499 1.837 m
+48.4 1.837 48.298 1.822 48.19 1.793 c
+48.08 1.764 47.978 1.705 47.882 1.616 c
+47.793 1.535 47.72 1.429 47.662 1.294 c
+47.602 1.165 47.566 0.992 47.558 0.779 c
+49.44 0.779 l
+49.418 0.984 49.381 1.15 49.322 1.278 c
+49.271 1.414 49.204 1.521 49.117 1.602 c
+49.036 1.69 48.944 1.749 48.838 1.778 c
+48.727 1.815 48.616 1.837 48.499 1.837 c
+54.074 0.47 m
+54.074 0.213 54.052 -0.026 54.015 -0.25 c
+53.975 -0.478 53.905 -0.673 53.809 -0.838 c
+53.711 -0.996 53.578 -1.118 53.412 -1.206 c
+53.254 -1.294 53.06 -1.338 52.824 -1.338 c
+52.707 -1.338 52.593 -1.327 52.487 -1.309 c
+52.377 -1.287 52.273 -1.257 52.178 -1.206 c
+52.09 -1.158 52.005 -1.099 51.928 -1.029 c
+51.858 -0.952 51.799 -0.861 51.752 -0.75 c
+51.737 -0.75 l
+51.737 -0.809 l
+51.744 -0.838 51.752 -0.879 51.752 -0.927 c
+51.752 -1.103 l
+51.752 -1.279 l
+51.752 -2.646 l
+51.163 -2.646 l
+51.163 1.484 l
+51.163 1.72 l
+51.163 1.94 l
+51.163 2.006 51.156 2.061 51.149 2.102 c
+51.149 2.204 l
+51.708 2.204 l
+51.714 2.194 51.722 2.167 51.722 2.131 c
+51.722 1.984 l
+51.729 1.932 51.737 1.874 51.737 1.807 c
+51.744 1.738 51.752 1.679 51.752 1.631 c
+51.766 1.631 l
+51.814 1.738 51.872 1.83 51.943 1.911 c
+52.009 1.999 52.082 2.065 52.163 2.117 c
+52.251 2.164 52.347 2.204 52.458 2.234 c
+52.564 2.263 52.685 2.278 52.824 2.278 c
+53.06 2.278 53.254 2.234 53.412 2.146 c
+53.578 2.057 53.711 1.932 53.809 1.778 c
+53.905 1.62 53.975 1.429 54.015 1.205 c
+54.052 0.988 54.074 0.742 54.074 0.47 c
+53.486 0.47 m
+53.486 0.683 53.472 0.874 53.442 1.043 c
+53.42 1.209 53.376 1.352 53.31 1.469 c
+53.251 1.587 53.166 1.675 53.06 1.734 c
+52.95 1.793 52.817 1.822 52.663 1.822 c
+52.545 1.822 52.427 1.801 52.31 1.764 c
+52.2 1.723 52.104 1.649 52.016 1.543 c
+51.935 1.444 51.872 1.3 51.825 1.117 c
+51.774 0.929 51.752 0.694 51.752 0.411 c
+51.752 0.154 51.77 -0.055 51.81 -0.221 c
+51.847 -0.39 51.906 -0.526 51.987 -0.632 c
+52.064 -0.732 52.156 -0.805 52.266 -0.853 c
+52.383 -0.894 52.516 -0.912 52.663 -0.912 c
+52.817 -0.912 52.946 -0.882 53.045 -0.823 c
+53.152 -0.765 53.236 -0.676 53.295 -0.559 c
+53.361 -0.441 53.412 -0.294 53.442 -0.118 c
+53.472 0.058 53.486 0.253 53.486 0.47 c
+f
+Q
+q 1 0 0 1 37.6521 138.6699 cm
+0 0 m
+2.646 4.835 l
+3.204 4.835 l
+0.588 0 l
+h
+4.31 3.087 m
+4.31 3.542 l
+4.854 3.542 l
+5.031 4.438 l
+5.428 4.438 l
+5.428 3.542 l
+6.809 3.542 l
+6.809 3.087 l
+5.428 3.087 l
+5.428 0.985 l
+5.428 0.816 5.471 0.691 5.56 0.602 c
+5.648 0.521 5.799 0.484 6.016 0.484 c
+6.093 0.484 6.177 0.488 6.265 0.5 c
+6.361 0.507 6.449 0.517 6.53 0.529 c
+6.618 0.536 6.695 0.548 6.765 0.558 c
+6.842 0.565 6.905 0.58 6.957 0.602 c
+6.957 0.162 l
+6.916 0.139 6.857 0.121 6.78 0.103 c
+6.699 0.091 6.611 0.077 6.516 0.058 c
+6.427 0.051 6.328 0.04 6.221 0.029 c
+6.111 0.01 6.001 0 5.883 0 c
+5.538 0 5.28 0.073 5.104 0.22 c
+4.928 0.367 4.84 0.602 4.84 0.926 c
+4.84 3.087 l
+h
+9.459 0.058 m
+9.459 2.263 l
+9.459 2.429 9.452 2.572 9.444 2.69 c
+9.433 2.807 9.415 2.899 9.386 2.969 c
+9.356 3.035 9.315 3.087 9.268 3.116 c
+9.216 3.145 9.157 3.16 9.091 3.16 c
+9.022 3.16 8.959 3.131 8.9 3.072 c
+8.849 3.021 8.801 2.944 8.754 2.836 c
+8.713 2.738 8.684 2.62 8.665 2.484 c
+8.644 2.344 8.636 2.19 8.636 2.013 c
+8.636 0.058 l
+8.092 0.058 l
+8.092 2.792 l
+8.092 3.027 l
+8.092 3.104 8.085 3.179 8.077 3.248 c
+8.077 3.439 l
+8.077 3.542 l
+8.548 3.542 l
+8.548 3.454 l
+8.555 3.414 8.563 3.366 8.563 3.307 c
+8.563 3.131 l
+8.569 3.072 8.577 3.017 8.577 2.969 c
+8.606 3.057 8.64 3.135 8.68 3.204 c
+8.717 3.281 8.761 3.351 8.812 3.41 c
+8.871 3.469 8.933 3.513 9.003 3.542 c
+9.08 3.579 9.168 3.601 9.268 3.601 c
+9.463 3.601 9.606 3.546 9.694 3.439 c
+9.789 3.34 9.86 3.182 9.899 2.969 c
+9.937 3.064 9.977 3.152 10.017 3.233 c
+10.065 3.31 10.12 3.373 10.179 3.424 c
+10.246 3.484 10.315 3.528 10.385 3.557 c
+10.462 3.586 10.554 3.601 10.664 3.601 c
+10.789 3.601 10.899 3.575 10.988 3.528 c
+11.075 3.476 11.15 3.403 11.208 3.307 c
+11.267 3.208 11.304 3.079 11.326 2.925 c
+11.355 2.778 11.37 2.594 11.37 2.381 c
+11.37 0.058 l
+10.826 0.058 l
+10.826 2.263 l
+10.826 2.429 10.819 2.572 10.811 2.69 c
+10.801 2.807 10.782 2.899 10.753 2.969 c
+10.723 3.035 10.682 3.087 10.635 3.116 c
+10.583 3.145 10.525 3.16 10.458 3.16 c
+10.319 3.16 10.209 3.064 10.12 2.881 c
+10.04 2.705 10.003 2.433 10.003 2.072 c
+10.003 0.058 l
+h
+15.312 1.808 m
+15.312 1.55 15.291 1.311 15.254 1.087 c
+15.214 0.86 15.144 0.665 15.048 0.5 c
+14.949 0.341 14.817 0.22 14.651 0.132 c
+14.493 0.044 14.298 0 14.063 0 c
+13.946 0 13.832 0.01 13.725 0.029 c
+13.615 0.051 13.512 0.081 13.417 0.132 c
+13.328 0.18 13.244 0.239 13.167 0.309 c
+13.097 0.386 13.038 0.477 12.991 0.588 c
+12.976 0.588 l
+12.976 0.529 l
+12.983 0.5 12.991 0.459 12.991 0.411 c
+12.991 0.235 l
+12.991 0.058 l
+12.991 -1.309 l
+12.403 -1.309 l
+12.403 2.822 l
+12.403 3.057 l
+12.403 3.278 l
+12.403 3.343 12.395 3.399 12.388 3.439 c
+12.388 3.542 l
+12.946 3.542 l
+12.954 3.532 12.961 3.505 12.961 3.469 c
+12.961 3.322 l
+12.968 3.27 12.976 3.212 12.976 3.145 c
+12.983 3.075 12.991 3.017 12.991 2.969 c
+13.005 2.969 l
+13.053 3.075 13.112 3.167 13.182 3.248 c
+13.247 3.337 13.321 3.403 13.402 3.454 c
+13.49 3.501 13.585 3.542 13.696 3.572 c
+13.803 3.601 13.924 3.615 14.063 3.615 c
+14.298 3.615 14.493 3.572 14.651 3.484 c
+14.817 3.395 14.949 3.27 15.048 3.116 c
+15.144 2.958 15.214 2.767 15.254 2.543 c
+15.291 2.326 15.312 2.08 15.312 1.808 c
+14.724 1.808 m
+14.724 2.021 14.71 2.212 14.68 2.381 c
+14.659 2.547 14.614 2.69 14.549 2.807 c
+14.489 2.925 14.406 3.013 14.298 3.072 c
+14.188 3.131 14.056 3.16 13.901 3.16 c
+13.784 3.16 13.666 3.138 13.549 3.102 c
+13.438 3.061 13.343 2.987 13.255 2.881 c
+13.174 2.782 13.112 2.638 13.064 2.454 c
+13.012 2.267 12.991 2.032 12.991 1.749 c
+12.991 1.492 13.009 1.282 13.049 1.117 c
+13.086 0.947 13.145 0.812 13.226 0.706 c
+13.303 0.606 13.394 0.532 13.504 0.484 c
+13.623 0.444 13.755 0.426 13.901 0.426 c
+14.056 0.426 14.184 0.455 14.284 0.515 c
+14.39 0.573 14.475 0.661 14.533 0.779 c
+14.6 0.897 14.651 1.043 14.68 1.22 c
+14.71 1.396 14.724 1.591 14.724 1.808 c
+f
+Q
+q 1 0 0 1 39.4451 134.0838 cm
+0 0 m
+0.853 0.323 l
+0.985 -0.088 l
+0.088 -0.324 l
+0.676 -1.132 l
+0.294 -1.367 l
+-0.191 -0.53 l
+-0.69 -1.353 l
+-1.072 -1.132 l
+-0.455 -0.324 l
+-1.367 -0.088 l
+-1.22 0.338 l
+-0.353 -0.015 l
+-0.397 0.941 l
+0.044 0.941 l
+h
+3.502 -2.866 0.75 -0.956 re
+3.502 -3.822 m
+9.371 -2.822 m
+9.371 -2.992 9.334 -3.142 9.268 -3.278 c
+9.209 -3.406 9.114 -3.514 8.989 -3.601 c
+8.871 -3.69 8.724 -3.759 8.548 -3.807 c
+8.379 -3.855 8.184 -3.881 7.96 -3.881 c
+7.754 -3.881 7.566 -3.866 7.401 -3.836 c
+7.243 -3.807 7.1 -3.763 6.975 -3.705 c
+6.857 -3.639 6.755 -3.55 6.666 -3.44 c
+6.585 -3.323 6.534 -3.186 6.504 -3.028 c
+7.005 -2.926 l
+7.034 -3.043 7.078 -3.138 7.136 -3.205 c
+7.196 -3.275 7.262 -3.329 7.342 -3.366 c
+7.42 -3.406 7.508 -3.433 7.607 -3.44 c
+7.713 -3.451 7.832 -3.454 7.96 -3.454 c
+8.085 -3.454 8.199 -3.447 8.298 -3.425 c
+8.405 -3.406 8.497 -3.377 8.577 -3.337 c
+8.654 -3.289 8.713 -3.23 8.754 -3.161 c
+8.802 -3.094 8.827 -3.01 8.827 -2.911 c
+8.827 -2.804 8.798 -2.716 8.739 -2.646 c
+8.688 -2.58 8.614 -2.525 8.518 -2.484 c
+8.43 -2.437 8.316 -2.396 8.181 -2.367 c
+8.052 -2.338 7.915 -2.301 7.769 -2.249 c
+7.629 -2.22 7.493 -2.187 7.358 -2.147 c
+7.217 -2.099 7.096 -2.04 6.99 -1.97 c
+6.88 -1.904 6.791 -1.816 6.725 -1.706 c
+6.666 -1.599 6.637 -1.463 6.637 -1.294 c
+6.637 -0.981 6.747 -0.736 6.975 -0.559 c
+7.199 -0.383 7.526 -0.294 7.96 -0.294 c
+8.136 -0.294 8.298 -0.309 8.445 -0.339 c
+8.592 -0.368 8.717 -0.42 8.827 -0.485 c
+8.945 -0.555 9.041 -0.643 9.121 -0.75 c
+9.199 -0.849 9.253 -0.971 9.282 -1.118 c
+8.769 -1.191 l
+8.746 -1.095 8.713 -1.014 8.665 -0.956 c
+8.614 -0.897 8.555 -0.853 8.489 -0.823 c
+8.419 -0.786 8.335 -0.761 8.239 -0.75 c
+8.151 -0.732 8.056 -0.721 7.96 -0.721 c
+7.438 -0.721 7.181 -0.882 7.181 -1.206 c
+7.181 -1.305 7.199 -1.382 7.24 -1.441 c
+7.287 -1.5 7.35 -1.555 7.431 -1.602 c
+7.518 -1.643 7.622 -1.676 7.739 -1.706 c
+7.857 -1.735 7.982 -1.764 8.121 -1.794 c
+8.258 -1.834 8.401 -1.878 8.548 -1.926 c
+8.694 -1.966 8.827 -2.025 8.945 -2.103 c
+9.07 -2.172 9.172 -2.264 9.253 -2.382 c
+9.33 -2.5 9.371 -2.646 9.371 -2.822 c
+13.299 -3.822 m
+12.638 -3.822 l
+12.182 -2.308 l
+12.16 -2.249 12.142 -2.187 12.123 -2.117 c
+12.102 -2.04 12.083 -1.966 12.065 -1.897 c
+12.035 -1.819 12.009 -1.742 11.991 -1.661 c
+11.969 -1.742 11.95 -1.819 11.932 -1.897 c
+11.91 -1.966 11.892 -2.04 11.873 -2.117 c
+11.851 -2.187 11.83 -2.257 11.8 -2.323 c
+11.329 -3.822 l
+10.683 -3.822 l
+10.095 -0.339 l
+10.668 -0.339 l
+10.962 -2.294 l
+10.969 -2.345 10.98 -2.419 10.992 -2.514 c
+10.999 -2.602 11.009 -2.701 11.021 -2.808 c
+11.028 -2.907 11.04 -3.007 11.05 -3.102 c
+11.057 -3.201 11.065 -3.278 11.065 -3.337 c
+11.094 -3.248 11.117 -3.161 11.138 -3.072 c
+11.157 -2.995 11.175 -2.918 11.198 -2.837 c
+11.215 -2.76 11.234 -2.701 11.256 -2.66 c
+11.682 -1.294 l
+12.314 -1.294 l
+12.726 -2.66 l
+12.733 -2.701 12.748 -2.76 12.771 -2.837 c
+12.788 -2.918 12.807 -2.992 12.829 -3.057 c
+12.858 -3.146 12.881 -3.242 12.902 -3.337 c
+12.902 -3.278 12.906 -3.201 12.917 -3.102 c
+12.925 -3.007 12.935 -2.907 12.946 -2.808 c
+12.954 -2.701 12.965 -2.602 12.976 -2.514 c
+12.994 -2.425 13.009 -2.352 13.02 -2.294 c
+13.343 -0.339 l
+13.902 -0.339 l
+h
+17.584 -2.072 m
+17.584 -2.33 17.562 -2.569 17.525 -2.793 c
+17.485 -3.021 17.415 -3.215 17.319 -3.381 c
+17.22 -3.539 17.088 -3.66 16.923 -3.749 c
+16.765 -3.836 16.57 -3.881 16.335 -3.881 c
+16.217 -3.881 16.103 -3.87 15.996 -3.851 c
+15.886 -3.83 15.784 -3.8 15.688 -3.749 c
+15.599 -3.701 15.515 -3.642 15.438 -3.572 c
+15.368 -3.495 15.31 -3.404 15.262 -3.293 c
+15.247 -3.293 l
+15.247 -3.352 l
+15.254 -3.381 15.262 -3.421 15.262 -3.469 c
+15.262 -3.645 l
+15.262 -3.822 l
+15.262 -5.189 l
+14.674 -5.189 l
+14.674 -1.058 l
+14.674 -0.823 l
+14.674 -0.603 l
+14.674 -0.537 14.666 -0.482 14.659 -0.441 c
+14.659 -0.339 l
+15.217 -0.339 l
+15.225 -0.349 15.232 -0.375 15.232 -0.412 c
+15.232 -0.559 l
+15.239 -0.611 15.247 -0.669 15.247 -0.736 c
+15.254 -0.805 15.262 -0.864 15.262 -0.912 c
+15.277 -0.912 l
+15.324 -0.805 15.383 -0.713 15.453 -0.632 c
+15.518 -0.544 15.593 -0.478 15.673 -0.426 c
+15.761 -0.379 15.857 -0.339 15.967 -0.309 c
+16.073 -0.279 16.195 -0.265 16.335 -0.265 c
+16.57 -0.265 16.765 -0.309 16.923 -0.397 c
+17.088 -0.485 17.22 -0.611 17.319 -0.765 c
+17.415 -0.923 17.485 -1.114 17.525 -1.338 c
+17.562 -1.555 17.584 -1.801 17.584 -2.072 c
+16.996 -2.072 m
+16.996 -1.86 16.981 -1.669 16.952 -1.5 c
+16.93 -1.334 16.885 -1.191 16.819 -1.073 c
+16.761 -0.956 16.676 -0.867 16.57 -0.809 c
+16.459 -0.75 16.327 -0.721 16.173 -0.721 c
+16.056 -0.721 15.938 -0.742 15.82 -0.779 c
+15.709 -0.819 15.614 -0.894 15.526 -1 c
+15.445 -1.099 15.383 -1.243 15.335 -1.426 c
+15.283 -1.613 15.262 -1.849 15.262 -2.132 c
+15.262 -2.389 15.279 -2.598 15.32 -2.764 c
+15.358 -2.933 15.416 -3.069 15.497 -3.175 c
+15.574 -3.275 15.666 -3.348 15.776 -3.396 c
+15.894 -3.437 16.025 -3.454 16.173 -3.454 c
+16.327 -3.454 16.455 -3.425 16.555 -3.366 c
+16.661 -3.308 16.746 -3.219 16.805 -3.102 c
+16.871 -2.984 16.923 -2.837 16.952 -2.66 c
+16.981 -2.484 16.996 -2.29 16.996 -2.072 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+35.668 442.028 238.665 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 146.2115 426.5399 cm
+0 0 m
+-1.822 0 l
+-1.822 7.158 l
+-3.601 6.468 l
+-3.601 7.981 l
+-0.191 9.377 l
+0 9.377 l
+h
+3.047 0.926 m
+3.047 1.219 3.142 1.458 3.341 1.646 c
+3.535 1.83 3.789 1.925 4.105 1.925 c
+4.406 1.925 4.657 1.83 4.855 1.646 c
+5.06 1.458 5.164 1.219 5.164 0.926 c
+5.164 0.621 5.06 0.374 4.855 0.191 c
+4.657 0.014 4.406 -0.074 4.105 -0.074 c
+3.801 -0.074 3.547 0.018 3.341 0.205 c
+3.142 0.389 3.047 0.631 3.047 0.926 c
+16.416 1.058 m
+16.052 0.665 15.607 0.367 15.078 0.161 c
+14.549 -0.034 13.968 -0.133 13.343 -0.133 c
+12.263 -0.133 11.425 0.198 10.83 0.866 c
+10.231 1.532 9.926 2.502 9.918 3.777 c
+9.918 5.468 l
+9.918 6.761 10.198 7.754 10.756 8.452 c
+11.323 9.146 12.146 9.495 13.226 9.495 c
+14.244 9.495 15.009 9.238 15.519 8.731 c
+16.037 8.231 16.335 7.445 16.416 6.379 c
+14.579 6.379 l
+14.527 6.975 14.406 7.382 14.211 7.599 c
+14.012 7.812 13.704 7.922 13.285 7.922 c
+12.774 7.922 12.403 7.735 12.168 7.364 c
+11.94 6.989 11.822 6.397 11.815 5.585 c
+11.815 3.881 l
+11.815 3.027 11.94 2.403 12.197 2.013 c
+12.451 1.631 12.866 1.44 13.446 1.44 c
+13.818 1.44 14.122 1.514 14.358 1.66 c
+14.519 1.778 l
+14.519 3.498 l
+13.197 3.498 l
+13.197 4.924 l
+16.416 4.924 l
+h
+19.973 0 -1.897 9.363 re
+27.554 7.79 m
+25.232 7.79 l
+25.232 0 l
+23.335 0 l
+23.335 7.79 l
+21.057 7.79 l
+21.057 9.363 l
+27.554 9.363 l
+h
+37.913 3.116 m
+37.862 2.046 37.56 1.238 37.002 0.691 c
+36.451 0.139 35.678 -0.133 34.679 -0.133 c
+33.61 -0.133 32.79 0.213 32.224 0.911 c
+31.666 1.606 31.387 2.601 31.387 3.895 c
+31.387 5.468 l
+31.387 6.761 31.673 7.754 32.254 8.452 c
+32.842 9.146 33.654 9.495 34.694 9.495 c
+35.711 9.495 36.488 9.205 37.016 8.628 c
+37.546 8.058 37.847 7.239 37.928 6.173 c
+36.031 6.173 l
+36.01 6.838 35.906 7.294 35.723 7.54 c
+35.535 7.794 35.194 7.922 34.694 7.922 c
+34.194 7.922 33.834 7.746 33.621 7.393 c
+33.415 7.04 33.301 6.456 33.282 5.644 c
+33.282 3.881 l
+33.282 2.946 33.386 2.308 33.591 1.955 c
+33.805 1.609 34.169 1.44 34.679 1.44 c
+35.168 1.44 35.506 1.558 35.694 1.793 c
+35.888 2.036 35.995 2.476 36.017 3.116 c
+h
+45.887 3.836 m
+45.887 2.579 45.585 1.606 44.991 0.911 c
+44.392 0.213 43.568 -0.133 42.521 -0.133 c
+41.471 -0.133 40.644 0.209 40.037 0.897 c
+39.438 1.591 39.133 2.557 39.125 3.792 c
+39.125 5.394 l
+39.125 6.676 39.424 7.68 40.022 8.407 c
+40.617 9.132 41.448 9.495 42.506 9.495 c
+43.543 9.495 44.362 9.135 44.961 8.422 c
+45.568 7.717 45.876 6.721 45.887 5.438 c
+h
+43.991 5.409 m
+43.991 6.25 43.866 6.879 43.624 7.291 c
+43.389 7.702 43.013 7.908 42.506 7.908 c
+42.007 7.908 41.631 7.706 41.39 7.305 c
+41.155 6.912 41.029 6.312 41.022 5.512 c
+41.022 3.836 l
+41.022 3.02 41.143 2.418 41.39 2.028 c
+41.631 1.635 42.011 1.44 42.521 1.44 c
+43.01 1.44 43.374 1.631 43.609 2.013 c
+43.852 2.395 43.98 2.983 43.991 3.777 c
+h
+54.019 0 m
+52.124 0 l
+49.36 6.144 l
+49.36 0 l
+47.464 0 l
+47.464 9.363 l
+49.36 9.363 l
+52.124 3.218 l
+52.124 9.363 l
+54.019 9.363 l
+h
+60.608 3.821 m
+57.668 3.821 l
+57.668 0 l
+55.773 0 l
+55.773 9.363 l
+60.961 9.363 l
+60.961 7.79 l
+57.668 7.79 l
+57.668 5.394 l
+60.608 5.394 l
+h
+64.213 0 -1.896 9.363 re
+72.272 1.058 m
+71.908 0.665 71.463 0.367 70.935 0.161 c
+70.405 -0.034 69.825 -0.133 69.2 -0.133 c
+68.12 -0.133 67.282 0.198 66.686 0.866 c
+66.087 1.532 65.782 2.502 65.775 3.777 c
+65.775 5.468 l
+65.775 6.761 66.054 7.754 66.613 8.452 c
+67.179 9.146 68.002 9.495 69.082 9.495 c
+70.1 9.495 70.865 9.238 71.376 8.731 c
+71.893 8.231 72.191 7.445 72.272 6.379 c
+70.435 6.379 l
+70.383 6.975 70.262 7.382 70.067 7.599 c
+69.869 7.812 69.56 7.922 69.142 7.922 c
+68.631 7.922 68.259 7.735 68.024 7.364 c
+67.796 6.989 67.679 6.397 67.671 5.585 c
+67.671 3.881 l
+67.671 3.027 67.796 2.403 68.053 2.013 c
+68.307 1.631 68.722 1.44 69.302 1.44 c
+69.674 1.44 69.979 1.514 70.214 1.66 c
+70.376 1.778 l
+70.376 3.498 l
+69.053 3.498 l
+69.053 4.924 l
+72.272 4.924 l
+h
+79.945 9.363 m
+79.945 2.881 l
+79.934 1.911 79.666 1.165 79.137 0.646 c
+78.607 0.124 77.846 -0.133 76.858 -0.133 c
+75.858 -0.133 75.095 0.124 74.565 0.646 c
+74.036 1.176 73.771 1.932 73.771 2.925 c
+73.771 9.363 l
+75.667 9.363 l
+75.667 2.925 l
+75.667 2.385 75.748 2.002 75.918 1.778 c
+76.094 1.55 76.406 1.44 76.858 1.44 c
+77.306 1.44 77.615 1.55 77.784 1.778 c
+77.95 2.002 78.038 2.37 78.048 2.881 c
+78.048 9.363 l
+h
+84.431 3.424 m
+83.491 3.424 l
+83.491 0 l
+81.609 0 l
+81.609 9.363 l
+84.623 9.363 l
+85.571 9.363 86.302 9.117 86.813 8.628 c
+87.332 8.136 87.592 7.441 87.592 6.541 c
+87.592 5.294 87.14 4.424 86.239 3.924 c
+87.872 0.087 l
+87.872 0 l
+85.842 0 l
+h
+83.491 4.997 m
+84.564 4.997 l
+84.946 4.997 85.229 5.119 85.416 5.365 c
+85.601 5.618 85.696 5.957 85.696 6.379 c
+85.696 7.32 85.332 7.79 84.608 7.79 c
+83.491 7.79 l
+h
+93.578 1.911 m
+91.006 1.911 l
+90.506 0 l
+88.508 0 l
+91.432 9.363 l
+93.152 9.363 l
+96.107 0 l
+94.078 0 l
+h
+91.417 3.498 m
+93.167 3.498 l
+92.285 6.834 l
+h
+102.482 7.79 m
+100.16 7.79 l
+100.16 0 l
+98.264 0 l
+98.264 7.79 l
+95.986 7.79 l
+95.986 9.363 l
+102.482 9.363 l
+h
+105.753 0 -1.897 9.363 re
+114.047 3.836 m
+114.047 2.579 113.746 1.606 113.15 0.911 c
+112.551 0.213 111.728 -0.133 110.681 -0.133 c
+109.63 -0.133 108.803 0.209 108.196 0.897 c
+107.597 1.591 107.292 2.557 107.285 3.792 c
+107.285 5.394 l
+107.285 6.676 107.583 7.68 108.182 8.407 c
+108.777 9.132 109.607 9.495 110.666 9.495 c
+111.702 9.495 112.522 9.135 113.121 8.422 c
+113.727 7.717 114.036 6.721 114.047 5.438 c
+h
+112.15 5.409 m
+112.15 6.25 112.026 6.879 111.783 7.291 c
+111.548 7.702 111.173 7.908 110.666 7.908 c
+110.166 7.908 109.791 7.706 109.549 7.305 c
+109.314 6.912 109.189 6.312 109.181 5.512 c
+109.181 3.836 l
+109.181 3.02 109.303 2.418 109.549 2.028 c
+109.791 1.635 110.17 1.44 110.681 1.44 c
+111.169 1.44 111.533 1.631 111.768 2.013 c
+112.011 2.395 112.14 2.983 112.15 3.777 c
+h
+122.176 0 m
+120.279 0 l
+117.515 6.144 l
+117.515 0 l
+115.62 0 l
+115.62 9.363 l
+117.515 9.363 l
+120.279 3.218 l
+120.279 9.363 l
+122.176 9.363 l
+h
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 411.726 238.665 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 404.8916 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.683 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.437 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.485 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.941 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.028 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.245 1.933 5.376 2.051 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.635 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.177 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.116 l
+14.497 3.116 l
+14.497 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.456 14.556 -0.515 c
+14.593 -0.566 14.648 -0.611 14.718 -0.647 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.688 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.603 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.279 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.309 14.835 -1.309 c
+14.648 -1.309 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.085 13.85 -1.025 13.791 -0.956 c
+13.733 -0.879 13.689 -0.802 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.456 c
+13.59 -0.36 13.586 -0.264 13.586 -0.177 c
+h
+22.677 -1.323 m
+22.391 -1.323 22.148 -1.282 21.943 -1.205 c
+21.737 -1.118 21.564 -0.996 21.428 -0.838 c
+21.288 -0.684 21.185 -0.497 21.119 -0.279 c
+21.049 -0.056 21.016 0.191 21.016 0.455 c
+21.016 0.75 21.049 1.007 21.119 1.234 c
+21.197 1.459 21.303 1.646 21.442 1.793 c
+21.589 1.947 21.766 2.065 21.972 2.146 c
+22.178 2.234 22.413 2.278 22.677 2.278 c
+22.901 2.278 23.104 2.248 23.28 2.19 c
+23.456 2.131 23.607 2.047 23.736 1.94 c
+23.861 1.841 23.963 1.72 24.044 1.573 c
+24.122 1.433 24.176 1.282 24.206 1.117 c
+23.295 1.072 l
+23.265 1.249 23.196 1.389 23.089 1.5 c
+22.99 1.606 22.846 1.66 22.662 1.66 c
+22.416 1.66 22.24 1.558 22.134 1.352 c
+22.023 1.153 21.972 0.867 21.972 0.484 c
+21.972 -0.309 22.207 -0.706 22.677 -0.706 c
+22.843 -0.706 22.986 -0.654 23.104 -0.544 c
+23.221 -0.437 23.295 -0.276 23.324 -0.059 c
+24.235 -0.103 l
+24.206 -0.272 24.151 -0.426 24.074 -0.574 c
+24.004 -0.721 23.901 -0.852 23.765 -0.97 c
+23.636 -1.081 23.478 -1.168 23.295 -1.235 c
+23.119 -1.294 22.913 -1.323 22.677 -1.323 c
+28.384 0.484 m
+28.384 0.209 28.347 -0.04 28.281 -0.264 c
+28.212 -0.482 28.108 -0.669 27.973 -0.823 c
+27.833 -0.981 27.657 -1.103 27.443 -1.191 c
+27.227 -1.279 26.973 -1.323 26.679 -1.323 c
+26.404 -1.323 26.157 -1.279 25.944 -1.191 c
+25.739 -1.103 25.566 -0.981 25.43 -0.823 c
+25.29 -0.669 25.187 -0.482 25.121 -0.264 c
+25.051 -0.04 25.018 0.209 25.018 0.484 c
+25.018 0.738 25.047 0.974 25.106 1.19 c
+25.172 1.415 25.275 1.606 25.415 1.764 c
+25.55 1.929 25.727 2.057 25.944 2.146 c
+26.157 2.234 26.414 2.278 26.709 2.278 c
+27.021 2.278 27.281 2.234 27.487 2.146 c
+27.701 2.057 27.873 1.929 28.002 1.764 c
+28.138 1.606 28.237 1.415 28.296 1.19 c
+28.355 0.974 28.384 0.738 28.384 0.484 c
+27.429 0.484 m
+27.429 0.69 27.414 0.867 27.385 1.014 c
+27.362 1.161 27.326 1.282 27.267 1.382 c
+27.208 1.477 27.135 1.547 27.046 1.587 c
+26.959 1.635 26.848 1.66 26.724 1.66 c
+26.458 1.66 26.267 1.562 26.15 1.367 c
+26.032 1.18 25.974 0.885 25.974 0.484 c
+25.974 0.062 26.032 -0.243 26.15 -0.426 c
+26.267 -0.614 26.444 -0.706 26.679 -0.706 c
+26.804 -0.706 26.918 -0.688 27.017 -0.647 c
+27.113 -0.599 27.194 -0.526 27.252 -0.426 c
+27.318 -0.331 27.362 -0.206 27.385 -0.059 c
+27.414 0.088 27.429 0.268 27.429 0.484 c
+31.43 -1.264 m
+31.43 0.72 l
+31.43 1.022 31.387 1.242 31.298 1.382 c
+31.217 1.529 31.081 1.602 30.886 1.602 c
+30.776 1.602 30.674 1.577 30.578 1.529 c
+30.489 1.477 30.409 1.411 30.343 1.323 c
+30.284 1.234 30.233 1.124 30.196 0.999 c
+30.167 0.881 30.152 0.75 30.152 0.602 c
+30.152 -1.264 l
+29.24 -1.264 l
+29.24 1.44 l
+29.24 1.66 l
+29.24 1.749 29.233 1.826 29.226 1.896 c
+29.226 2.087 l
+29.226 2.219 l
+30.078 2.219 l
+30.086 2.19 30.093 2.146 30.093 2.087 c
+30.093 1.896 l
+30.1 1.826 30.107 1.756 30.107 1.69 c
+30.115 1.62 30.122 1.565 30.122 1.529 c
+30.137 1.529 l
+30.254 1.793 30.406 1.984 30.593 2.102 c
+30.776 2.219 30.996 2.278 31.254 2.278 c
+31.438 2.278 31.599 2.248 31.74 2.19 c
+31.875 2.131 31.989 2.043 32.077 1.926 c
+32.166 1.808 32.228 1.664 32.268 1.5 c
+32.316 1.341 32.342 1.153 32.342 0.941 c
+32.342 -1.264 l
+h
+34.951 1.602 m
+34.951 -1.264 l
+34.054 -1.264 l
+34.054 1.602 l
+33.232 1.602 l
+33.232 2.219 l
+34.054 2.219 l
+34.054 2.484 l
+34.054 2.609 34.069 2.741 34.098 2.881 c
+34.135 3.017 34.205 3.135 34.304 3.233 c
+34.41 3.341 34.554 3.428 34.73 3.499 c
+34.907 3.564 35.131 3.601 35.407 3.601 c
+35.62 3.601 35.819 3.59 35.995 3.572 c
+36.17 3.549 36.322 3.532 36.45 3.513 c
+36.45 2.925 l
+36.322 2.944 36.178 2.958 36.024 2.969 c
+35.866 2.977 35.715 2.984 35.568 2.984 c
+35.44 2.984 35.337 2.969 35.26 2.94 c
+35.179 2.911 35.116 2.87 35.069 2.822 c
+35.017 2.77 34.984 2.708 34.965 2.631 c
+34.955 2.561 34.951 2.484 34.951 2.396 c
+34.951 2.219 l
+36.377 2.219 l
+36.377 1.602 l
+h
+39.467 -0.647 m
+40.599 -0.647 l
+40.599 -1.264 l
+37.292 -1.264 l
+37.292 -0.647 l
+38.556 -0.647 l
+38.556 1.602 l
+37.63 1.602 l
+37.63 2.219 l
+39.467 2.219 l
+h
+38.556 3.513 0.911 -0.676 re
+38.556 2.836 m
+42.954 -2.66 m
+42.738 -2.66 42.547 -2.635 42.381 -2.587 c
+42.212 -2.547 42.073 -2.485 41.955 -2.396 c
+41.837 -2.315 41.738 -2.22 41.661 -2.103 c
+41.591 -1.985 41.543 -1.856 41.514 -1.72 c
+42.41 -1.617 l
+42.447 -1.753 42.518 -1.86 42.616 -1.941 c
+42.712 -2.028 42.837 -2.072 42.984 -2.072 c
+43.072 -2.072 43.153 -2.058 43.234 -2.028 c
+43.311 -1.999 43.381 -1.945 43.44 -1.867 c
+43.499 -1.797 43.542 -1.706 43.572 -1.588 c
+43.609 -1.47 43.631 -1.323 43.631 -1.147 c
+43.631 -0.956 l
+43.631 -0.889 43.634 -0.831 43.646 -0.779 c
+43.646 -0.588 l
+43.631 -0.588 l
+43.532 -0.816 43.388 -0.977 43.204 -1.073 c
+43.017 -1.172 42.811 -1.22 42.587 -1.22 c
+42.381 -1.22 42.198 -1.183 42.044 -1.103 c
+41.896 -1.014 41.768 -0.897 41.661 -0.75 c
+41.562 -0.595 41.488 -0.412 41.441 -0.206 c
+41.389 0.008 41.367 0.243 41.367 0.5 c
+41.367 0.771 41.389 1.018 41.441 1.234 c
+41.5 1.448 41.58 1.631 41.691 1.778 c
+41.797 1.933 41.93 2.051 42.088 2.131 c
+42.242 2.219 42.429 2.263 42.646 2.263 c
+42.742 2.263 42.84 2.252 42.94 2.234 c
+43.035 2.212 43.123 2.179 43.204 2.131 c
+43.293 2.08 43.37 2.017 43.44 1.94 c
+43.517 1.859 43.58 1.768 43.631 1.66 c
+43.646 1.66 l
+43.646 1.808 l
+43.653 1.866 43.66 1.918 43.66 1.97 c
+43.667 2.028 43.675 2.076 43.675 2.117 c
+43.682 2.165 43.694 2.198 43.704 2.219 c
+44.557 2.219 l
+44.546 2.138 44.535 2.028 44.527 1.881 c
+44.527 1.411 l
+44.527 -1.162 l
+44.527 -1.415 44.49 -1.635 44.425 -1.823 c
+44.355 -2.007 44.251 -2.161 44.116 -2.278 c
+43.976 -2.404 43.811 -2.499 43.616 -2.558 c
+43.418 -2.624 43.197 -2.66 42.954 -2.66 c
+43.646 0.529 m
+43.646 0.742 43.619 0.918 43.572 1.058 c
+43.532 1.205 43.476 1.323 43.41 1.411 c
+43.351 1.5 43.282 1.558 43.204 1.587 c
+43.123 1.624 43.046 1.646 42.969 1.646 c
+42.87 1.646 42.778 1.62 42.69 1.573 c
+42.609 1.532 42.543 1.463 42.484 1.367 c
+42.433 1.278 42.389 1.161 42.352 1.014 c
+42.323 0.875 42.308 0.706 42.308 0.5 c
+42.308 0.125 42.366 -0.154 42.484 -0.339 c
+42.601 -0.515 42.763 -0.603 42.969 -0.603 c
+43.035 -0.603 43.109 -0.588 43.189 -0.559 c
+43.278 -0.522 43.351 -0.463 43.41 -0.382 c
+43.476 -0.294 43.532 -0.177 43.572 -0.029 c
+43.619 0.118 43.646 0.301 43.646 0.529 c
+50.135 0.837 1.867 -0.793 re
+50.135 0.044 m
+54.2 0.837 1.866 -0.793 re
+54.2 0.044 m
+59.201 -2.66 m
+58.983 -2.66 58.792 -2.635 58.628 -2.587 c
+58.458 -2.547 58.318 -2.485 58.201 -2.396 c
+58.083 -2.315 57.984 -2.22 57.907 -2.103 c
+57.838 -1.985 57.79 -1.856 57.76 -1.72 c
+58.657 -1.617 l
+58.694 -1.753 58.763 -1.86 58.863 -1.941 c
+58.958 -2.028 59.083 -2.072 59.23 -2.072 c
+59.318 -2.072 59.399 -2.058 59.48 -2.028 c
+59.557 -1.999 59.627 -1.945 59.686 -1.867 c
+59.744 -1.797 59.789 -1.706 59.818 -1.588 c
+59.855 -1.47 59.877 -1.323 59.877 -1.147 c
+59.877 -0.956 l
+59.877 -0.889 59.881 -0.831 59.891 -0.779 c
+59.891 -0.588 l
+59.877 -0.588 l
+59.777 -0.816 59.634 -0.977 59.451 -1.073 c
+59.263 -1.172 59.058 -1.22 58.833 -1.22 c
+58.628 -1.22 58.443 -1.183 58.289 -1.103 c
+58.142 -1.014 58.014 -0.897 57.907 -0.75 c
+57.807 -0.595 57.734 -0.412 57.687 -0.206 c
+57.635 0.008 57.613 0.243 57.613 0.5 c
+57.613 0.771 57.635 1.018 57.687 1.234 c
+57.745 1.448 57.826 1.631 57.936 1.778 c
+58.043 1.933 58.175 2.051 58.333 2.131 c
+58.488 2.219 58.675 2.263 58.892 2.263 c
+58.987 2.263 59.087 2.252 59.186 2.234 c
+59.282 2.212 59.37 2.179 59.451 2.131 c
+59.538 2.08 59.616 2.017 59.686 1.94 c
+59.763 1.859 59.825 1.768 59.877 1.66 c
+59.891 1.66 l
+59.891 1.808 l
+59.899 1.866 59.906 1.918 59.906 1.97 c
+59.914 2.028 59.921 2.076 59.921 2.117 c
+59.928 2.165 59.939 2.198 59.951 2.219 c
+60.803 2.219 l
+60.792 2.138 60.781 2.028 60.774 1.881 c
+60.774 1.411 l
+60.774 -1.162 l
+60.774 -1.415 60.737 -1.635 60.67 -1.823 c
+60.601 -2.007 60.498 -2.161 60.362 -2.278 c
+60.222 -2.404 60.057 -2.499 59.862 -2.558 c
+59.664 -2.624 59.443 -2.66 59.201 -2.66 c
+59.891 0.529 m
+59.891 0.742 59.866 0.918 59.818 1.058 c
+59.777 1.205 59.723 1.323 59.656 1.411 c
+59.598 1.5 59.528 1.558 59.451 1.587 c
+59.37 1.624 59.293 1.646 59.216 1.646 c
+59.116 1.646 59.024 1.62 58.936 1.573 c
+58.855 1.532 58.789 1.463 58.73 1.367 c
+58.679 1.278 58.634 1.161 58.598 1.014 c
+58.569 0.875 58.553 0.706 58.553 0.5 c
+58.553 0.125 58.613 -0.154 58.73 -0.339 c
+58.848 -0.515 59.01 -0.603 59.216 -0.603 c
+59.282 -0.603 59.355 -0.588 59.436 -0.559 c
+59.524 -0.522 59.598 -0.463 59.656 -0.382 c
+59.723 -0.294 59.777 -0.177 59.818 -0.029 c
+59.866 0.118 59.891 0.301 59.891 0.529 c
+63.835 -0.647 m
+64.967 -0.647 l
+64.967 -1.264 l
+61.659 -1.264 l
+61.659 -0.647 l
+62.923 -0.647 l
+62.923 2.896 l
+61.998 2.896 l
+61.998 3.513 l
+63.835 3.513 l
+h
+68.998 0.484 m
+68.998 0.209 68.961 -0.04 68.895 -0.264 c
+68.824 -0.482 68.722 -0.669 68.586 -0.823 c
+68.446 -0.981 68.27 -1.103 68.057 -1.191 c
+67.84 -1.279 67.586 -1.323 67.293 -1.323 c
+67.017 -1.323 66.771 -1.279 66.557 -1.191 c
+66.352 -1.103 66.179 -0.981 66.043 -0.823 c
+65.903 -0.669 65.801 -0.482 65.734 -0.264 c
+65.665 -0.04 65.632 0.209 65.632 0.484 c
+65.632 0.738 65.661 0.974 65.72 1.19 c
+65.786 1.415 65.888 1.606 66.029 1.764 c
+66.164 1.929 66.341 2.057 66.557 2.146 c
+66.771 2.234 67.028 2.278 67.322 2.278 c
+67.634 2.278 67.895 2.234 68.101 2.146 c
+68.314 2.057 68.487 1.929 68.616 1.764 c
+68.751 1.606 68.851 1.415 68.909 1.19 c
+68.968 0.974 68.998 0.738 68.998 0.484 c
+68.042 0.484 m
+68.042 0.69 68.028 0.867 67.999 1.014 c
+67.976 1.161 67.939 1.282 67.881 1.382 c
+67.822 1.477 67.748 1.547 67.66 1.587 c
+67.571 1.635 67.461 1.66 67.336 1.66 c
+67.072 1.66 66.881 1.562 66.763 1.367 c
+66.646 1.18 66.587 0.885 66.587 0.484 c
+66.587 0.062 66.646 -0.243 66.763 -0.426 c
+66.881 -0.614 67.058 -0.706 67.293 -0.706 c
+67.417 -0.706 67.531 -0.688 67.631 -0.647 c
+67.726 -0.599 67.807 -0.526 67.866 -0.426 c
+67.932 -0.331 67.976 -0.206 67.999 -0.059 c
+68.028 0.088 68.042 0.268 68.042 0.484 c
+69.854 -1.264 m
+69.854 -0.97 l
+69.861 -0.834 69.869 -0.676 69.869 -0.5 c
+69.869 3.513 l
+70.78 3.513 l
+70.78 2.234 l
+70.78 2.072 l
+70.78 1.896 l
+70.78 1.845 70.773 1.801 70.765 1.764 c
+70.765 1.675 l
+70.78 1.675 l
+70.827 1.782 70.89 1.874 70.971 1.955 c
+71.048 2.032 71.133 2.094 71.221 2.146 c
+71.309 2.194 71.401 2.227 71.5 2.248 c
+71.596 2.267 71.695 2.278 71.794 2.278 c
+72.007 2.278 72.194 2.234 72.352 2.146 c
+72.507 2.057 72.635 1.929 72.735 1.764 c
+72.841 1.606 72.919 1.415 72.97 1.19 c
+73.017 0.974 73.044 0.735 73.044 0.47 c
+73.044 0.213 73.015 -0.026 72.955 -0.25 c
+72.897 -0.467 72.812 -0.658 72.705 -0.823 c
+72.595 -0.981 72.463 -1.103 72.309 -1.191 c
+72.151 -1.279 71.97 -1.323 71.764 -1.323 c
+71.666 -1.323 71.567 -1.312 71.471 -1.294 c
+71.382 -1.272 71.294 -1.242 71.206 -1.191 c
+71.118 -1.132 71.037 -1.066 70.971 -0.985 c
+70.901 -0.908 70.839 -0.809 70.78 -0.691 c
+70.765 -0.691 l
+70.765 -0.852 l
+70.765 -0.912 70.758 -0.97 70.75 -1.029 c
+70.75 -1.081 70.743 -1.128 70.736 -1.176 c
+70.736 -1.216 70.729 -1.246 70.721 -1.264 c
+h
+70.765 0.5 m
+70.765 0.264 70.783 0.066 70.824 -0.088 c
+70.872 -0.246 70.931 -0.368 71 -0.456 c
+71.066 -0.544 71.14 -0.611 71.221 -0.647 c
+71.298 -0.688 71.375 -0.706 71.456 -0.706 c
+71.662 -0.706 71.816 -0.611 71.926 -0.412 c
+72.044 -0.217 72.103 0.077 72.103 0.47 c
+72.103 0.683 72.08 0.867 72.044 1.014 c
+72.015 1.168 71.97 1.294 71.912 1.382 c
+71.86 1.477 71.794 1.55 71.706 1.602 c
+71.625 1.65 71.537 1.675 71.442 1.675 c
+71.361 1.675 71.284 1.654 71.206 1.617 c
+71.126 1.577 71.052 1.514 70.985 1.425 c
+70.927 1.338 70.872 1.213 70.824 1.058 c
+70.783 0.912 70.765 0.723 70.765 0.5 c
+74.873 -1.323 m
+74.704 -1.323 74.554 -1.301 74.418 -1.264 c
+74.289 -1.216 74.175 -1.147 74.079 -1.058 c
+73.992 -0.97 73.921 -0.864 73.874 -0.735 c
+73.823 -0.599 73.8 -0.449 73.8 -0.279 c
+73.8 -0.073 73.834 0.095 73.904 0.235 c
+73.969 0.382 74.065 0.492 74.183 0.573 c
+74.308 0.661 74.451 0.723 74.609 0.764 c
+74.775 0.801 74.951 0.827 75.138 0.837 c
+75.858 0.852 l
+75.858 1.029 l
+75.858 1.147 75.847 1.249 75.829 1.338 c
+75.807 1.425 75.774 1.492 75.726 1.543 c
+75.685 1.602 75.638 1.639 75.579 1.66 c
+75.521 1.679 75.454 1.69 75.388 1.69 c
+75.318 1.69 75.255 1.679 75.197 1.66 c
+75.145 1.65 75.097 1.624 75.05 1.587 c
+75.01 1.558 74.976 1.506 74.947 1.44 c
+74.925 1.382 74.91 1.301 74.903 1.205 c
+73.962 1.249 l
+73.992 1.396 74.035 1.532 74.095 1.66 c
+74.16 1.785 74.256 1.896 74.374 1.984 c
+74.492 2.08 74.631 2.153 74.8 2.205 c
+74.976 2.252 75.182 2.278 75.417 2.278 c
+75.858 2.278 76.19 2.168 76.417 1.955 c
+76.652 1.749 76.77 1.44 76.77 1.029 c
+76.77 -0.235 l
+76.77 -0.456 l
+76.777 -0.515 76.792 -0.57 76.814 -0.617 c
+76.832 -0.658 76.861 -0.691 76.902 -0.721 c
+76.938 -0.742 76.99 -0.75 77.049 -0.75 c
+77.115 -0.75 77.185 -0.746 77.254 -0.735 c
+77.254 -1.22 l
+77.196 -1.231 77.141 -1.242 77.093 -1.249 c
+77.053 -1.261 77.013 -1.268 76.976 -1.279 c
+76.936 -1.286 76.891 -1.294 76.843 -1.294 c
+76.792 -1.301 76.733 -1.309 76.666 -1.309 c
+76.439 -1.309 76.273 -1.257 76.167 -1.147 c
+76.057 -1.029 75.995 -0.864 75.976 -0.647 c
+75.961 -0.647 l
+75.891 -0.757 75.822 -0.852 75.756 -0.941 c
+75.685 -1.022 75.608 -1.087 75.521 -1.147 c
+75.432 -1.205 75.333 -1.249 75.226 -1.279 c
+75.127 -1.309 75.01 -1.323 74.873 -1.323 c
+75.858 0.353 m
+75.432 0.338 l
+75.333 0.338 75.241 0.33 75.153 0.324 c
+75.072 0.312 75.006 0.287 74.947 0.249 c
+74.889 0.209 74.837 0.151 74.8 0.073 c
+74.76 0.004 74.741 -0.088 74.741 -0.206 c
+74.741 -0.375 74.775 -0.497 74.844 -0.574 c
+74.91 -0.654 75.01 -0.691 75.138 -0.691 c
+75.245 -0.691 75.344 -0.669 75.432 -0.617 c
+75.527 -0.57 75.608 -0.507 75.667 -0.426 c
+75.733 -0.349 75.785 -0.262 75.814 -0.162 c
+75.843 -0.056 75.858 0.058 75.858 0.176 c
+h
+80.08 -0.647 m
+81.212 -0.647 l
+81.212 -1.264 l
+77.905 -1.264 l
+77.905 -0.647 l
+79.17 -0.647 l
+79.17 2.896 l
+78.243 2.896 l
+78.243 3.513 l
+80.08 3.513 l
+h
+86.982 2.219 m
+86.982 0.264 l
+86.982 0.125 86.989 0 87.011 -0.118 c
+87.03 -0.228 87.063 -0.32 87.114 -0.397 c
+87.162 -0.478 87.221 -0.54 87.29 -0.588 c
+87.357 -0.628 87.441 -0.647 87.54 -0.647 c
+87.629 -0.647 87.71 -0.628 87.791 -0.588 c
+87.878 -0.54 87.952 -0.47 88.011 -0.382 c
+88.069 -0.287 88.113 -0.177 88.144 -0.059 c
+88.18 0.066 88.202 0.206 88.202 0.353 c
+88.202 2.219 l
+89.098 2.219 l
+89.098 -0.485 l
+89.098 -0.721 l
+89.106 -0.802 89.113 -0.879 89.113 -0.956 c
+89.113 -1.147 l
+89.121 -1.199 89.128 -1.235 89.128 -1.264 c
+88.275 -1.264 l
+88.265 -1.235 88.254 -1.199 88.246 -1.147 c
+88.246 -0.956 l
+88.246 -0.889 88.239 -0.819 88.231 -0.75 c
+88.231 -0.574 l
+88.217 -0.574 l
+88.099 -0.838 87.945 -1.029 87.761 -1.147 c
+87.585 -1.264 87.382 -1.323 87.159 -1.323 c
+86.953 -1.323 86.779 -1.286 86.644 -1.22 c
+86.504 -1.154 86.394 -1.058 86.305 -0.941 c
+86.225 -0.823 86.166 -0.688 86.129 -0.53 c
+86.1 -0.364 86.085 -0.187 86.085 0 c
+86.085 2.219 l
+h
+93.262 -0.25 m
+93.262 -0.419 93.222 -0.57 93.144 -0.706 c
+93.075 -0.834 92.971 -0.948 92.836 -1.044 c
+92.707 -1.132 92.545 -1.202 92.351 -1.249 c
+92.163 -1.297 91.947 -1.323 91.704 -1.323 c
+91.477 -1.323 91.278 -1.309 91.101 -1.279 c
+90.925 -1.249 90.767 -1.202 90.631 -1.132 c
+90.492 -1.055 90.382 -0.956 90.293 -0.838 c
+90.205 -0.721 90.135 -0.574 90.087 -0.397 c
+90.895 -0.279 l
+90.914 -0.379 90.943 -0.456 90.984 -0.515 c
+91.032 -0.574 91.09 -0.617 91.16 -0.647 c
+91.226 -0.676 91.307 -0.702 91.396 -0.721 c
+91.483 -0.732 91.587 -0.735 91.704 -0.735 c
+91.799 -0.735 91.895 -0.732 91.984 -0.721 c
+92.071 -0.702 92.148 -0.676 92.219 -0.647 c
+92.285 -0.617 92.336 -0.58 92.366 -0.53 c
+92.402 -0.482 92.424 -0.419 92.424 -0.339 c
+92.424 -0.243 92.395 -0.169 92.336 -0.118 c
+92.285 -0.07 92.219 -0.029 92.13 0 c
+92.042 0.037 91.932 0.066 91.807 0.088 c
+91.689 0.118 91.557 0.147 91.41 0.176 c
+91.271 0.213 91.13 0.253 90.984 0.293 c
+90.844 0.341 90.719 0.405 90.602 0.484 c
+90.492 0.562 90.403 0.661 90.337 0.779 c
+90.267 0.897 90.234 1.047 90.234 1.234 c
+90.234 1.389 90.263 1.532 90.322 1.66 c
+90.388 1.797 90.484 1.911 90.602 1.999 c
+90.727 2.087 90.885 2.153 91.072 2.205 c
+91.255 2.252 91.469 2.278 91.704 2.278 c
+91.888 2.278 92.065 2.256 92.233 2.219 c
+92.398 2.19 92.545 2.135 92.674 2.057 c
+92.799 1.988 92.909 1.889 92.998 1.764 c
+93.085 1.646 93.144 1.502 93.174 1.338 c
+92.38 1.264 l
+92.358 1.341 92.329 1.404 92.292 1.455 c
+92.252 1.514 92.204 1.558 92.144 1.587 c
+92.094 1.624 92.031 1.65 91.954 1.66 c
+91.873 1.668 91.793 1.675 91.704 1.675 c
+91.487 1.675 91.325 1.646 91.219 1.587 c
+91.109 1.536 91.057 1.448 91.057 1.323 c
+91.057 1.242 91.076 1.18 91.116 1.132 c
+91.164 1.08 91.226 1.043 91.307 1.014 c
+91.396 0.985 91.491 0.955 91.601 0.926 c
+91.708 0.904 91.829 0.881 91.969 0.852 c
+92.123 0.823 92.281 0.783 92.439 0.735 c
+92.593 0.683 92.733 0.621 92.85 0.544 c
+92.969 0.463 93.064 0.36 93.144 0.235 c
+93.222 0.106 93.262 -0.056 93.262 -0.25 c
+95.783 -1.323 m
+95.525 -1.323 95.298 -1.286 95.092 -1.22 c
+94.887 -1.143 94.71 -1.029 94.563 -0.882 c
+94.416 -0.728 94.299 -0.536 94.21 -0.309 c
+94.129 -0.085 94.093 0.18 94.093 0.484 c
+94.093 0.816 94.137 1.095 94.224 1.323 c
+94.32 1.558 94.449 1.741 94.607 1.881 c
+94.772 2.017 94.96 2.117 95.165 2.175 c
+95.371 2.242 95.581 2.278 95.797 2.278 c
+96.069 2.278 96.304 2.227 96.503 2.131 c
+96.709 2.043 96.875 1.911 97.003 1.735 c
+97.139 1.565 97.239 1.359 97.297 1.117 c
+97.363 0.881 97.399 0.617 97.399 0.324 c
+97.399 0.309 l
+95.033 0.309 l
+95.033 0.162 95.048 0.022 95.078 -0.103 c
+95.114 -0.231 95.169 -0.345 95.24 -0.441 c
+95.305 -0.53 95.39 -0.599 95.489 -0.647 c
+95.585 -0.698 95.699 -0.721 95.827 -0.721 c
+95.982 -0.721 96.121 -0.688 96.239 -0.617 c
+96.364 -0.551 96.452 -0.449 96.503 -0.309 c
+97.341 -0.382 l
+97.312 -0.482 97.256 -0.588 97.179 -0.706 c
+97.098 -0.816 96.996 -0.919 96.871 -1.014 c
+96.753 -1.103 96.599 -1.176 96.415 -1.235 c
+96.239 -1.294 96.026 -1.323 95.783 -1.323 c
+95.783 1.705 m
+95.695 1.705 95.606 1.69 95.518 1.66 c
+95.43 1.631 95.35 1.58 95.283 1.514 c
+95.213 1.444 95.155 1.356 95.107 1.249 c
+95.066 1.139 95.048 1.014 95.048 0.867 c
+96.518 0.867 l
+96.518 1.003 96.493 1.124 96.445 1.234 c
+96.404 1.341 96.349 1.429 96.283 1.5 c
+96.224 1.565 96.15 1.617 96.063 1.646 c
+95.974 1.683 95.878 1.705 95.783 1.705 c
+101.299 1.469 m
+101.2 1.477 101.097 1.488 100.99 1.5 c
+100.88 1.517 100.759 1.529 100.622 1.529 c
+100.446 1.529 100.288 1.488 100.152 1.411 c
+100.013 1.341 99.895 1.242 99.799 1.117 c
+99.711 0.989 99.641 0.841 99.593 0.676 c
+99.554 0.507 99.535 0.33 99.535 0.147 c
+99.535 -1.264 l
+98.638 -1.264 l
+98.638 0.985 l
+98.638 1.11 98.627 1.234 98.609 1.352 c
+98.598 1.477 98.583 1.595 98.565 1.705 c
+98.554 1.822 98.539 1.918 98.521 1.999 c
+98.498 2.087 98.48 2.161 98.462 2.219 c
+99.344 2.219 l
+99.351 2.168 99.362 2.117 99.373 2.057 c
+99.392 1.999 99.406 1.933 99.417 1.866 c
+99.435 1.808 99.45 1.741 99.462 1.675 c
+99.469 1.606 99.479 1.543 99.491 1.484 c
+99.506 1.484 l
+99.542 1.602 99.593 1.708 99.653 1.808 c
+99.718 1.903 99.799 1.988 99.888 2.057 c
+99.976 2.124 100.079 2.179 100.196 2.219 c
+100.321 2.256 100.468 2.278 100.637 2.278 c
+100.763 2.278 100.88 2.271 100.99 2.263 c
+101.108 2.252 101.21 2.238 101.299 2.219 c
+h
+103.404 -0.279 0.927 -0.985 re
+103.404 -1.264 m
+108.597 -1.264 m
+108.597 0.72 l
+108.597 1.022 108.553 1.242 108.464 1.382 c
+108.384 1.529 108.248 1.602 108.053 1.602 c
+107.942 1.602 107.84 1.577 107.745 1.529 c
+107.656 1.477 107.575 1.411 107.51 1.323 c
+107.45 1.234 107.399 1.124 107.362 0.999 c
+107.333 0.881 107.318 0.75 107.318 0.602 c
+107.318 -1.264 l
+106.407 -1.264 l
+106.407 1.44 l
+106.407 1.66 l
+106.407 1.749 106.399 1.826 106.392 1.896 c
+106.392 2.087 l
+106.392 2.219 l
+107.244 2.219 l
+107.252 2.19 107.259 2.146 107.259 2.087 c
+107.259 1.896 l
+107.267 1.826 107.274 1.756 107.274 1.69 c
+107.281 1.62 107.288 1.565 107.288 1.529 c
+107.304 1.529 l
+107.421 1.793 107.572 1.984 107.759 2.102 c
+107.942 2.219 108.163 2.278 108.42 2.278 c
+108.605 2.278 108.765 2.248 108.906 2.19 c
+109.041 2.131 109.156 2.043 109.243 1.926 c
+109.332 1.808 109.394 1.664 109.434 1.5 c
+109.482 1.341 109.509 1.153 109.509 0.941 c
+109.509 -1.264 l
+h
+111.423 -1.323 m
+111.254 -1.323 111.103 -1.301 110.967 -1.264 c
+110.839 -1.216 110.724 -1.147 110.629 -1.058 c
+110.541 -0.97 110.471 -0.864 110.423 -0.735 c
+110.372 -0.599 110.35 -0.449 110.35 -0.279 c
+110.35 -0.073 110.383 0.095 110.452 0.235 c
+110.519 0.382 110.614 0.492 110.732 0.573 c
+110.857 0.661 111 0.723 111.158 0.764 c
+111.323 0.801 111.5 0.827 111.687 0.837 c
+112.408 0.852 l
+112.408 1.029 l
+112.408 1.147 112.397 1.249 112.378 1.338 c
+112.356 1.425 112.323 1.492 112.275 1.543 c
+112.235 1.602 112.187 1.639 112.129 1.66 c
+112.069 1.679 112.003 1.69 111.938 1.69 c
+111.867 1.69 111.805 1.679 111.746 1.66 c
+111.695 1.65 111.647 1.624 111.599 1.587 c
+111.559 1.558 111.526 1.506 111.496 1.44 c
+111.474 1.382 111.46 1.301 111.452 1.205 c
+110.511 1.249 l
+110.541 1.396 110.585 1.532 110.644 1.66 c
+110.71 1.785 110.805 1.896 110.923 1.984 c
+111.04 2.08 111.18 2.153 111.35 2.205 c
+111.526 2.252 111.732 2.278 111.967 2.278 c
+112.408 2.278 112.738 2.168 112.966 1.955 c
+113.201 1.749 113.319 1.44 113.319 1.029 c
+113.319 -0.235 l
+113.319 -0.456 l
+113.326 -0.515 113.341 -0.57 113.363 -0.617 c
+113.382 -0.658 113.411 -0.691 113.451 -0.721 c
+113.488 -0.742 113.54 -0.75 113.598 -0.75 c
+113.665 -0.75 113.734 -0.746 113.804 -0.735 c
+113.804 -1.22 l
+113.745 -1.231 113.69 -1.242 113.642 -1.249 c
+113.602 -1.261 113.561 -1.268 113.525 -1.279 c
+113.484 -1.286 113.44 -1.294 113.392 -1.294 c
+113.341 -1.301 113.282 -1.309 113.216 -1.309 c
+112.988 -1.309 112.823 -1.257 112.717 -1.147 c
+112.606 -1.029 112.543 -0.864 112.526 -0.647 c
+112.511 -0.647 l
+112.441 -0.757 112.37 -0.852 112.305 -0.941 c
+112.235 -1.022 112.158 -1.087 112.069 -1.147 c
+111.981 -1.205 111.882 -1.249 111.776 -1.279 c
+111.676 -1.309 111.559 -1.323 111.423 -1.323 c
+112.408 0.353 m
+111.981 0.338 l
+111.882 0.338 111.79 0.33 111.702 0.324 c
+111.622 0.312 111.555 0.287 111.496 0.249 c
+111.437 0.209 111.386 0.151 111.35 0.073 c
+111.309 0.004 111.29 -0.088 111.29 -0.206 c
+111.29 -0.375 111.323 -0.497 111.393 -0.574 c
+111.46 -0.654 111.559 -0.691 111.687 -0.691 c
+111.794 -0.691 111.893 -0.669 111.981 -0.617 c
+112.077 -0.57 112.158 -0.507 112.216 -0.426 c
+112.283 -0.349 112.334 -0.262 112.364 -0.162 c
+112.393 -0.056 112.408 0.058 112.408 0.176 c
+h
+115.693 -1.264 m
+115.693 0.852 l
+115.693 1.018 115.686 1.153 115.678 1.264 c
+115.667 1.371 115.649 1.455 115.62 1.514 c
+115.597 1.58 115.568 1.631 115.531 1.66 c
+115.502 1.69 115.462 1.705 115.414 1.705 c
+115.354 1.705 115.3 1.675 115.252 1.617 c
+115.211 1.565 115.178 1.492 115.149 1.396 c
+115.119 1.308 115.094 1.194 115.076 1.058 c
+115.065 0.918 115.061 0.768 115.061 0.602 c
+115.061 -1.264 l
+114.311 -1.264 l
+114.311 1.469 l
+114.311 1.705 l
+114.311 1.926 l
+114.311 2.003 114.304 2.065 114.296 2.117 c
+114.296 2.219 l
+114.972 2.219 l
+114.972 2.131 l
+114.972 1.984 l
+114.98 1.926 114.988 1.866 114.988 1.808 c
+114.988 1.646 l
+115.002 1.646 l
+115.02 1.735 115.05 1.812 115.09 1.881 c
+115.127 1.959 115.171 2.028 115.223 2.087 c
+115.281 2.146 115.348 2.19 115.428 2.219 c
+115.506 2.256 115.593 2.278 115.693 2.278 c
+115.877 2.278 116.017 2.223 116.104 2.117 c
+116.2 2.017 116.27 1.859 116.31 1.646 c
+116.325 1.646 l
+116.362 1.741 116.402 1.83 116.443 1.911 c
+116.49 1.988 116.545 2.051 116.604 2.102 c
+116.663 2.161 116.729 2.205 116.81 2.234 c
+116.887 2.263 116.975 2.278 117.074 2.278 c
+117.21 2.278 117.324 2.252 117.413 2.205 c
+117.501 2.153 117.567 2.08 117.619 1.984 c
+117.677 1.885 117.714 1.756 117.736 1.602 c
+117.766 1.455 117.78 1.271 117.78 1.058 c
+117.78 -1.264 l
+117.06 -1.264 l
+117.06 0.852 l
+117.06 1.018 117.052 1.153 117.045 1.264 c
+117.035 1.371 117.016 1.455 116.987 1.514 c
+116.964 1.58 116.935 1.631 116.898 1.66 c
+116.869 1.69 116.829 1.705 116.781 1.705 c
+116.663 1.705 116.567 1.617 116.501 1.44 c
+116.443 1.271 116.413 1.014 116.413 0.661 c
+116.413 -1.264 l
+h
+120.15 -1.323 m
+119.893 -1.323 119.665 -1.286 119.46 -1.22 c
+119.254 -1.143 119.077 -1.029 118.93 -0.882 c
+118.783 -0.728 118.666 -0.536 118.577 -0.309 c
+118.496 -0.085 118.46 0.18 118.46 0.484 c
+118.46 0.816 118.504 1.095 118.592 1.323 c
+118.687 1.558 118.816 1.741 118.974 1.881 c
+119.14 2.017 119.327 2.117 119.533 2.175 c
+119.739 2.242 119.948 2.278 120.165 2.278 c
+120.437 2.278 120.672 2.227 120.871 2.131 c
+121.076 2.043 121.242 1.911 121.37 1.735 c
+121.506 1.565 121.606 1.359 121.664 1.117 c
+121.731 0.881 121.767 0.617 121.767 0.324 c
+121.767 0.309 l
+119.401 0.309 l
+119.401 0.162 119.416 0.022 119.445 -0.103 c
+119.481 -0.231 119.537 -0.345 119.607 -0.441 c
+119.672 -0.53 119.757 -0.599 119.857 -0.647 c
+119.952 -0.698 120.066 -0.721 120.195 -0.721 c
+120.349 -0.721 120.488 -0.688 120.606 -0.617 c
+120.731 -0.551 120.819 -0.449 120.871 -0.309 c
+121.708 -0.382 l
+121.679 -0.482 121.623 -0.588 121.546 -0.706 c
+121.465 -0.816 121.363 -0.919 121.238 -1.014 c
+121.12 -1.103 120.966 -1.176 120.782 -1.235 c
+120.606 -1.294 120.393 -1.323 120.15 -1.323 c
+120.15 1.705 m
+120.062 1.705 119.974 1.69 119.886 1.66 c
+119.798 1.631 119.717 1.58 119.651 1.514 c
+119.581 1.444 119.522 1.356 119.474 1.249 c
+119.433 1.139 119.416 1.014 119.416 0.867 c
+120.885 0.867 l
+120.885 1.003 120.86 1.124 120.812 1.234 c
+120.771 1.341 120.717 1.429 120.65 1.5 c
+120.591 1.565 120.518 1.617 120.43 1.646 c
+120.341 1.683 120.246 1.705 120.15 1.705 c
+129.286 1.455 m
+128.654 1.455 l
+128.521 3.513 l
+129.418 3.513 l
+h
+127.815 1.455 m
+127.184 1.455 l
+127.067 3.513 l
+127.933 3.513 l
+h
+132.758 0.529 m
+132.758 -1.264 l
+131.817 -1.264 l
+131.817 0.529 l
+130.333 3.072 l
+131.318 3.072 l
+132.288 1.249 l
+133.259 3.072 l
+134.258 3.072 l
+h
+138.043 0.484 m
+138.043 0.209 138.006 -0.04 137.94 -0.264 c
+137.87 -0.482 137.768 -0.669 137.631 -0.823 c
+137.492 -0.981 137.315 -1.103 137.102 -1.191 c
+136.885 -1.279 136.632 -1.323 136.338 -1.323 c
+136.062 -1.323 135.815 -1.279 135.603 -1.191 c
+135.397 -1.103 135.224 -0.981 135.088 -0.823 c
+134.948 -0.669 134.846 -0.482 134.78 -0.264 c
+134.71 -0.04 134.676 0.209 134.676 0.484 c
+134.676 0.738 134.706 0.974 134.765 1.19 c
+134.831 1.415 134.934 1.606 135.073 1.764 c
+135.21 1.929 135.386 2.057 135.603 2.146 c
+135.815 2.234 136.073 2.278 136.367 2.278 c
+136.679 2.278 136.941 2.234 137.146 2.146 c
+137.359 2.057 137.532 1.929 137.66 1.764 c
+137.797 1.606 137.895 1.415 137.955 1.19 c
+138.013 0.974 138.043 0.738 138.043 0.484 c
+137.087 0.484 m
+137.087 0.69 137.072 0.867 137.043 1.014 c
+137.022 1.161 136.985 1.282 136.926 1.382 c
+136.867 1.477 136.793 1.547 136.705 1.587 c
+136.617 1.635 136.507 1.66 136.382 1.66 c
+136.117 1.66 135.926 1.562 135.809 1.367 c
+135.691 1.18 135.632 0.885 135.632 0.484 c
+135.632 0.062 135.691 -0.243 135.809 -0.426 c
+135.926 -0.614 136.102 -0.706 136.338 -0.706 c
+136.463 -0.706 136.577 -0.688 136.675 -0.647 c
+136.771 -0.599 136.852 -0.526 136.91 -0.426 c
+136.977 -0.331 137.022 -0.206 137.043 -0.059 c
+137.072 0.088 137.087 0.268 137.087 0.484 c
+139.781 2.219 m
+139.781 0.264 l
+139.781 0.125 139.788 0 139.81 -0.118 c
+139.829 -0.228 139.862 -0.32 139.913 -0.397 c
+139.961 -0.478 140.02 -0.54 140.089 -0.588 c
+140.156 -0.628 140.241 -0.647 140.339 -0.647 c
+140.428 -0.647 140.509 -0.628 140.59 -0.588 c
+140.677 -0.54 140.751 -0.47 140.81 -0.382 c
+140.868 -0.287 140.912 -0.177 140.943 -0.059 c
+140.979 0.066 141.001 0.206 141.001 0.353 c
+141.001 2.219 l
+141.897 2.219 l
+141.897 -0.485 l
+141.897 -0.721 l
+141.905 -0.802 141.912 -0.879 141.912 -0.956 c
+141.912 -1.147 l
+141.92 -1.199 141.927 -1.235 141.927 -1.264 c
+141.074 -1.264 l
+141.063 -1.235 141.053 -1.199 141.045 -1.147 c
+141.045 -0.956 l
+141.045 -0.889 141.038 -0.819 141.03 -0.75 c
+141.03 -0.574 l
+141.016 -0.574 l
+140.898 -0.838 140.744 -1.029 140.56 -1.147 c
+140.384 -1.264 140.181 -1.323 139.958 -1.323 c
+139.752 -1.323 139.578 -1.286 139.443 -1.22 c
+139.303 -1.154 139.193 -1.058 139.104 -0.941 c
+139.024 -0.823 138.965 -0.688 138.928 -0.53 c
+138.899 -0.364 138.884 -0.187 138.884 0 c
+138.884 2.219 l
+h
+145.973 1.469 m
+145.874 1.477 145.77 1.488 145.664 1.5 c
+145.554 1.517 145.433 1.529 145.296 1.529 c
+145.12 1.529 144.962 1.488 144.826 1.411 c
+144.687 1.341 144.569 1.242 144.474 1.117 c
+144.386 0.989 144.316 0.841 144.268 0.676 c
+144.228 0.507 144.209 0.33 144.209 0.147 c
+144.209 -1.264 l
+143.312 -1.264 l
+143.312 0.985 l
+143.312 1.11 143.301 1.234 143.283 1.352 c
+143.272 1.477 143.258 1.595 143.239 1.705 c
+143.228 1.822 143.213 1.918 143.195 1.999 c
+143.173 2.087 143.154 2.161 143.136 2.219 c
+144.018 2.219 l
+144.025 2.168 144.037 2.117 144.047 2.057 c
+144.066 1.999 144.081 1.933 144.091 1.866 c
+144.11 1.808 144.124 1.741 144.135 1.675 c
+144.143 1.606 144.154 1.543 144.165 1.484 c
+144.18 1.484 l
+144.216 1.602 144.268 1.708 144.326 1.808 c
+144.393 1.903 144.474 1.988 144.562 2.057 c
+144.65 2.124 144.753 2.179 144.87 2.219 c
+144.995 2.256 145.142 2.278 145.311 2.278 c
+145.436 2.278 145.554 2.271 145.664 2.263 c
+145.782 2.252 145.885 2.238 145.973 2.219 c
+h
+153.036 -1.264 m
+151.801 1.984 l
+151.808 1.826 151.823 1.679 151.845 1.543 c
+151.845 1.352 l
+151.852 1.294 151.86 1.228 151.86 1.161 c
+151.867 1.103 151.875 1.043 151.875 0.985 c
+151.881 0.933 151.889 0.889 151.889 0.852 c
+151.889 -1.264 l
+151.066 -1.264 l
+151.066 3.072 l
+152.139 3.072 l
+153.404 -0.264 l
+153.381 -0.118 153.367 0.022 153.359 0.162 c
+153.348 0.287 153.337 0.415 153.33 0.544 c
+153.319 0.679 153.315 0.801 153.315 0.912 c
+153.315 3.072 l
+154.138 3.072 l
+154.138 -1.264 l
+h
+156.101 -1.323 m
+155.931 -1.323 155.781 -1.301 155.644 -1.264 c
+155.516 -1.216 155.403 -1.147 155.307 -1.058 c
+155.218 -0.97 155.149 -0.864 155.101 -0.735 c
+155.05 -0.599 155.027 -0.449 155.027 -0.279 c
+155.027 -0.073 155.06 0.095 155.131 0.235 c
+155.197 0.382 155.293 0.492 155.409 0.573 c
+155.534 0.661 155.678 0.723 155.836 0.764 c
+156.001 0.801 156.178 0.827 156.365 0.837 c
+157.086 0.852 l
+157.086 1.029 l
+157.086 1.147 157.074 1.249 157.055 1.338 c
+157.034 1.425 157.001 1.492 156.953 1.543 c
+156.913 1.602 156.865 1.639 156.806 1.66 c
+156.747 1.679 156.681 1.69 156.615 1.69 c
+156.546 1.69 156.483 1.679 156.424 1.66 c
+156.373 1.65 156.325 1.624 156.277 1.587 c
+156.236 1.558 156.203 1.506 156.174 1.44 c
+156.152 1.382 156.137 1.301 156.13 1.205 c
+155.189 1.249 l
+155.218 1.396 155.262 1.532 155.322 1.66 c
+155.388 1.785 155.484 1.896 155.601 1.984 c
+155.719 2.08 155.858 2.153 156.027 2.205 c
+156.203 2.252 156.409 2.278 156.644 2.278 c
+157.086 2.278 157.416 2.168 157.644 1.955 c
+157.88 1.749 157.996 1.44 157.996 1.029 c
+157.996 -0.235 l
+157.996 -0.456 l
+158.004 -0.515 158.019 -0.57 158.04 -0.617 c
+158.059 -0.658 158.088 -0.691 158.129 -0.721 c
+158.166 -0.742 158.217 -0.75 158.276 -0.75 c
+158.342 -0.75 158.412 -0.746 158.482 -0.735 c
+158.482 -1.22 l
+158.423 -1.231 158.368 -1.242 158.32 -1.249 c
+158.279 -1.261 158.239 -1.268 158.202 -1.279 c
+158.162 -1.286 158.118 -1.294 158.071 -1.294 c
+158.019 -1.301 157.96 -1.309 157.894 -1.309 c
+157.666 -1.309 157.5 -1.257 157.394 -1.147 c
+157.284 -1.029 157.221 -0.864 157.203 -0.647 c
+157.188 -0.647 l
+157.119 -0.757 157.049 -0.852 156.982 -0.941 c
+156.913 -1.022 156.835 -1.087 156.747 -1.147 c
+156.659 -1.205 156.56 -1.249 156.453 -1.279 c
+156.354 -1.309 156.236 -1.323 156.101 -1.323 c
+157.086 0.353 m
+156.659 0.338 l
+156.56 0.338 156.468 0.33 156.38 0.324 c
+156.299 0.312 156.233 0.287 156.174 0.249 c
+156.115 0.209 156.064 0.151 156.027 0.073 c
+155.987 0.004 155.968 -0.088 155.968 -0.206 c
+155.968 -0.375 156.001 -0.497 156.071 -0.574 c
+156.137 -0.654 156.236 -0.691 156.365 -0.691 c
+156.471 -0.691 156.571 -0.669 156.659 -0.617 c
+156.754 -0.57 156.835 -0.507 156.895 -0.426 c
+156.96 -0.349 157.012 -0.262 157.041 -0.162 c
+157.071 -0.056 157.086 0.058 157.086 0.176 c
+h
+160.367 -1.264 m
+160.367 0.852 l
+160.367 1.018 160.359 1.153 160.352 1.264 c
+160.341 1.371 160.323 1.455 160.294 1.514 c
+160.271 1.58 160.242 1.631 160.205 1.66 c
+160.176 1.69 160.135 1.705 160.087 1.705 c
+160.029 1.705 159.974 1.675 159.926 1.617 c
+159.885 1.565 159.852 1.492 159.823 1.396 c
+159.794 1.308 159.768 1.194 159.75 1.058 c
+159.738 0.918 159.735 0.768 159.735 0.602 c
+159.735 -1.264 l
+158.985 -1.264 l
+158.985 1.469 l
+158.985 1.705 l
+158.985 1.926 l
+158.985 2.003 158.977 2.065 158.971 2.117 c
+158.971 2.219 l
+159.646 2.219 l
+159.646 2.131 l
+159.646 1.984 l
+159.654 1.926 159.661 1.866 159.661 1.808 c
+159.661 1.646 l
+159.676 1.646 l
+159.694 1.735 159.723 1.812 159.764 1.881 c
+159.801 1.959 159.845 2.028 159.897 2.087 c
+159.956 2.146 160.022 2.19 160.103 2.219 c
+160.18 2.256 160.268 2.278 160.367 2.278 c
+160.55 2.278 160.691 2.223 160.779 2.117 c
+160.874 2.017 160.944 1.859 160.984 1.646 c
+160.999 1.646 l
+161.036 1.741 161.076 1.83 161.117 1.911 c
+161.165 1.988 161.219 2.051 161.278 2.102 c
+161.337 2.161 161.404 2.205 161.484 2.234 c
+161.562 2.263 161.649 2.278 161.749 2.278 c
+161.884 2.278 161.998 2.252 162.087 2.205 c
+162.175 2.153 162.241 2.08 162.293 1.984 c
+162.351 1.885 162.388 1.756 162.41 1.602 c
+162.439 1.455 162.454 1.271 162.454 1.058 c
+162.454 -1.264 l
+161.734 -1.264 l
+161.734 0.852 l
+161.734 1.018 161.726 1.153 161.72 1.264 c
+161.708 1.371 161.69 1.455 161.66 1.514 c
+161.639 1.58 161.609 1.631 161.572 1.66 c
+161.543 1.69 161.502 1.705 161.454 1.705 c
+161.337 1.705 161.242 1.617 161.175 1.44 c
+161.117 1.271 161.088 1.014 161.088 0.661 c
+161.088 -1.264 l
+h
+164.824 -1.323 m
+164.567 -1.323 164.34 -1.286 164.134 -1.22 c
+163.928 -1.143 163.752 -1.029 163.604 -0.882 c
+163.457 -0.728 163.34 -0.536 163.251 -0.309 c
+163.17 -0.085 163.134 0.18 163.134 0.484 c
+163.134 0.816 163.178 1.095 163.266 1.323 c
+163.362 1.558 163.49 1.741 163.648 1.881 c
+163.814 2.017 164.001 2.117 164.207 2.175 c
+164.413 2.242 164.622 2.278 164.839 2.278 c
+165.111 2.278 165.346 2.227 165.545 2.131 c
+165.751 2.043 165.915 1.911 166.044 1.735 c
+166.181 1.565 166.279 1.359 166.339 1.117 c
+166.405 0.881 166.441 0.617 166.441 0.324 c
+166.441 0.309 l
+164.074 0.309 l
+164.074 0.162 164.089 0.022 164.119 -0.103 c
+164.155 -0.231 164.211 -0.345 164.28 -0.441 c
+164.346 -0.53 164.431 -0.599 164.531 -0.647 c
+164.626 -0.698 164.739 -0.721 164.868 -0.721 c
+165.023 -0.721 165.163 -0.688 165.28 -0.617 c
+165.406 -0.551 165.493 -0.449 165.545 -0.309 c
+166.383 -0.382 l
+166.353 -0.482 166.298 -0.588 166.221 -0.706 c
+166.14 -0.816 166.037 -0.919 165.913 -1.014 c
+165.795 -1.103 165.641 -1.176 165.456 -1.235 c
+165.28 -1.294 165.067 -1.323 164.824 -1.323 c
+164.824 1.705 m
+164.737 1.705 164.648 1.69 164.56 1.66 c
+164.471 1.631 164.39 1.58 164.325 1.514 c
+164.255 1.444 164.196 1.356 164.149 1.249 c
+164.108 1.139 164.089 1.014 164.089 0.867 c
+165.56 0.867 l
+165.56 1.003 165.533 1.124 165.485 1.234 c
+165.445 1.341 165.39 1.429 165.325 1.5 c
+165.265 1.565 165.192 1.617 165.104 1.646 c
+165.015 1.683 164.92 1.705 164.824 1.705 c
+169.899 1.455 m
+169.267 1.455 l
+169.135 3.513 l
+170.031 3.513 l
+h
+168.429 1.455 m
+167.797 1.455 l
+167.679 3.513 l
+168.547 3.513 l
+h
+f
+Q
+q 1 0 0 1 91.976 390.0891 cm
+0 0 m
+0 0.265 -0.073 0.464 -0.22 0.603 c
+-0.36 0.75 -0.617 0.89 -0.999 1.029 c
+-1.374 1.166 -1.66 1.309 -1.866 1.455 c
+-2.065 1.603 -2.212 1.768 -2.308 1.956 c
+-2.406 2.151 -2.454 2.371 -2.454 2.617 c
+-2.454 3.036 -2.315 3.385 -2.028 3.66 c
+-1.745 3.932 -1.378 4.072 -0.926 4.072 c
+-0.613 4.072 -0.334 3.998 -0.088 3.851 c
+0.154 3.712 0.341 3.517 0.47 3.263 c
+0.607 3.017 0.676 2.749 0.676 2.455 c
+0 2.455 l
+0 2.786 -0.081 3.043 -0.235 3.219 c
+-0.393 3.404 -0.625 3.499 -0.926 3.499 c
+-1.19 3.499 -1.404 3.418 -1.558 3.263 c
+-1.705 3.117 -1.779 2.903 -1.779 2.631 c
+-1.779 2.404 -1.701 2.213 -1.544 2.058 c
+-1.378 1.912 -1.124 1.771 -0.779 1.646 c
+-0.261 1.478 0.11 1.268 0.338 1.015 c
+0.573 0.757 0.691 0.427 0.691 0.015 c
+0.691 -0.426 0.548 -0.779 0.264 -1.043 c
+-0.022 -1.3 -0.405 -1.425 -0.881 -1.425 c
+-1.198 -1.425 -1.484 -1.356 -1.749 -1.219 c
+-2.014 -1.084 -2.227 -0.893 -2.381 -0.646 c
+-2.528 -0.404 -2.601 -0.118 -2.601 0.206 c
+-1.926 0.206 l
+-1.926 -0.128 -1.834 -0.389 -1.646 -0.573 c
+-1.463 -0.76 -1.205 -0.852 -0.881 -0.852 c
+-0.588 -0.852 -0.368 -0.779 -0.22 -0.631 c
+-0.073 -0.477 0 -0.264 0 0 c
+2.896 -1.425 m
+2.396 -1.425 2.014 -1.278 1.749 -0.984 c
+1.484 -0.69 1.353 -0.257 1.353 0.324 c
+1.353 0.794 l
+1.353 1.389 1.477 1.856 1.735 2.191 c
+1.999 2.532 2.359 2.705 2.822 2.705 c
+3.281 2.705 3.624 2.55 3.851 2.249 c
+4.087 1.956 4.208 1.492 4.219 0.867 c
+4.219 0.441 l
+1.999 0.441 l
+1.999 0.353 l
+1.999 -0.08 2.076 -0.393 2.234 -0.588 c
+2.4 -0.775 2.631 -0.866 2.925 -0.866 c
+3.12 -0.866 3.293 -0.833 3.439 -0.764 c
+3.587 -0.687 3.723 -0.569 3.851 -0.411 c
+4.189 -0.823 l
+3.903 -1.227 3.473 -1.425 2.896 -1.425 c
+2.822 2.147 m
+2.547 2.147 2.344 2.051 2.219 1.867 c
+2.091 1.68 2.018 1.389 1.999 1 c
+3.572 1 l
+3.572 1.088 l
+3.55 1.47 3.484 1.738 3.366 1.897 c
+3.248 2.062 3.065 2.147 2.822 2.147 c
+5.85 3.587 m
+5.85 2.631 l
+6.453 2.631 l
+6.453 2.103 l
+5.85 2.103 l
+5.85 -0.367 l
+5.85 -0.525 5.872 -0.643 5.924 -0.72 c
+5.982 -0.801 6.071 -0.837 6.188 -0.837 c
+6.277 -0.837 6.365 -0.823 6.453 -0.793 c
+6.453 -1.352 l
+6.306 -1.4 6.151 -1.425 5.997 -1.425 c
+5.74 -1.425 5.546 -1.334 5.409 -1.146 c
+5.27 -0.962 5.203 -0.702 5.203 -0.367 c
+5.203 2.103 l
+4.601 2.103 l
+4.601 2.631 l
+5.203 2.631 l
+5.203 3.587 l
+h
+9.789 3.587 m
+9.789 2.631 l
+10.392 2.631 l
+10.392 2.103 l
+9.789 2.103 l
+9.789 -0.367 l
+9.789 -0.525 9.812 -0.643 9.864 -0.72 c
+9.922 -0.801 10.01 -0.837 10.128 -0.837 c
+10.215 -0.837 10.304 -0.823 10.392 -0.793 c
+10.392 -1.352 l
+10.246 -1.4 10.091 -1.425 9.937 -1.425 c
+9.679 -1.425 9.485 -1.334 9.349 -1.146 c
+9.209 -0.962 9.143 -0.702 9.143 -0.367 c
+9.143 2.103 l
+8.54 2.103 l
+8.54 2.631 l
+9.143 2.631 l
+9.143 3.587 l
+h
+11.803 2.22 m
+12.057 2.544 12.376 2.705 12.759 2.705 c
+13.465 2.705 13.821 2.234 13.832 1.294 c
+13.832 -1.352 l
+13.185 -1.352 l
+13.185 1.264 l
+13.185 1.577 13.13 1.798 13.024 1.926 c
+12.914 2.051 12.759 2.117 12.553 2.117 c
+12.395 2.117 12.248 2.062 12.112 1.956 c
+11.983 1.845 11.881 1.709 11.803 1.544 c
+11.803 -1.352 l
+11.156 -1.352 l
+11.156 4.293 l
+11.803 4.293 l
+h
+16.214 -1.425 m
+15.713 -1.425 15.331 -1.278 15.067 -0.984 c
+14.803 -0.69 14.67 -0.257 14.67 0.324 c
+14.67 0.794 l
+14.67 1.389 14.795 1.856 15.052 2.191 c
+15.316 2.532 15.676 2.705 16.139 2.705 c
+16.599 2.705 16.941 2.55 17.168 2.249 c
+17.404 1.956 17.525 1.492 17.536 0.867 c
+17.536 0.441 l
+15.316 0.441 l
+15.316 0.353 l
+15.316 -0.08 15.393 -0.393 15.551 -0.588 c
+15.717 -0.775 15.948 -0.866 16.243 -0.866 c
+16.438 -0.866 16.61 -0.833 16.757 -0.764 c
+16.904 -0.687 17.04 -0.569 17.168 -0.411 c
+17.506 -0.823 l
+17.22 -1.227 16.79 -1.425 16.214 -1.425 c
+16.139 2.147 m
+15.864 2.147 15.662 2.051 15.537 1.867 c
+15.408 1.68 15.335 1.389 15.316 1 c
+16.889 1 l
+16.889 1.088 l
+16.867 1.47 16.801 1.738 16.684 1.897 c
+16.565 2.062 16.382 2.147 16.139 2.147 c
+20.652 2.631 m
+20.667 2.191 l
+20.92 2.532 21.244 2.705 21.637 2.705 c
+22.343 2.705 22.699 2.234 22.71 1.294 c
+22.71 -1.352 l
+22.063 -1.352 l
+22.063 1.264 l
+22.063 1.577 22.009 1.798 21.901 1.926 c
+21.791 2.051 21.637 2.117 21.431 2.117 c
+21.273 2.117 21.126 2.062 20.991 1.956 c
+20.862 1.845 20.759 1.709 20.682 1.544 c
+20.682 -1.352 l
+20.035 -1.352 l
+20.035 2.631 l
+h
+25.724 -1.352 m
+25.683 -1.263 25.658 -1.117 25.65 -0.911 c
+25.415 -1.256 25.121 -1.425 24.768 -1.425 c
+24.405 -1.425 24.121 -1.33 23.915 -1.132 c
+23.717 -0.926 23.621 -0.639 23.621 -0.264 c
+23.621 0.136 23.757 0.456 24.033 0.691 c
+24.305 0.934 24.68 1.058 25.151 1.058 c
+25.635 1.058 l
+25.635 1.485 l
+25.635 1.721 25.581 1.885 25.473 1.985 c
+25.363 2.091 25.201 2.147 24.989 2.147 c
+24.79 2.147 24.629 2.087 24.503 1.97 c
+24.386 1.852 24.327 1.706 24.327 1.529 c
+23.68 1.529 l
+23.68 1.723 23.74 1.915 23.856 2.103 c
+23.981 2.286 24.143 2.433 24.342 2.544 c
+24.548 2.65 24.775 2.705 25.032 2.705 c
+25.433 2.705 25.738 2.602 25.944 2.396 c
+26.157 2.191 26.271 1.897 26.282 1.515 c
+26.282 -0.5 l
+26.282 -0.804 26.319 -1.069 26.4 -1.294 c
+26.4 -1.352 l
+h
+24.856 -0.837 m
+25.022 -0.837 25.172 -0.793 25.312 -0.706 c
+25.459 -0.617 25.566 -0.507 25.635 -0.367 c
+25.635 0.574 l
+25.267 0.574 l
+24.952 0.574 24.709 0.504 24.533 0.368 c
+24.357 0.239 24.268 0.052 24.268 -0.191 c
+24.268 -0.419 24.312 -0.584 24.401 -0.69 c
+24.488 -0.789 24.64 -0.837 24.856 -0.837 c
+27.899 2.631 m
+27.914 2.264 l
+28.156 2.558 28.476 2.705 28.869 2.705 c
+29.31 2.705 29.619 2.507 29.795 2.117 c
+30.049 2.507 30.398 2.705 30.838 2.705 c
+31.574 2.705 31.948 2.242 31.971 1.324 c
+31.971 -1.352 l
+31.324 -1.352 l
+31.324 1.264 l
+31.324 1.559 31.268 1.771 31.162 1.912 c
+31.063 2.047 30.89 2.117 30.647 2.117 c
+30.449 2.117 30.287 2.037 30.163 1.881 c
+30.045 1.735 29.975 1.544 29.957 1.309 c
+29.957 -1.352 l
+29.296 -1.352 l
+29.296 1.294 l
+29.296 1.841 29.074 2.117 28.634 2.117 c
+28.299 2.117 28.064 1.956 27.929 1.632 c
+27.929 -1.352 l
+27.281 -1.352 l
+27.281 2.631 l
+h
+34.366 -1.425 m
+33.867 -1.425 33.485 -1.278 33.22 -0.984 c
+32.955 -0.69 32.823 -0.257 32.823 0.324 c
+32.823 0.794 l
+32.823 1.389 32.948 1.856 33.205 2.191 c
+33.47 2.532 33.83 2.705 34.293 2.705 c
+34.753 2.705 35.094 2.55 35.322 2.249 c
+35.557 1.956 35.678 1.492 35.69 0.867 c
+35.69 0.441 l
+33.47 0.441 l
+33.47 0.353 l
+33.47 -0.08 33.547 -0.393 33.705 -0.588 c
+33.87 -0.775 34.102 -0.866 34.396 -0.866 c
+34.591 -0.866 34.763 -0.833 34.91 -0.764 c
+35.058 -0.687 35.193 -0.569 35.322 -0.411 c
+35.66 -0.823 l
+35.374 -1.227 34.944 -1.425 34.366 -1.425 c
+34.293 2.147 m
+34.017 2.147 33.815 2.051 33.691 1.867 c
+33.562 1.68 33.488 1.389 33.47 1 c
+35.043 1 l
+35.043 1.088 l
+35.021 1.47 34.954 1.738 34.837 1.897 c
+34.719 2.062 34.535 2.147 34.293 2.147 c
+39.041 3.587 m
+39.041 2.631 l
+39.644 2.631 l
+39.644 2.103 l
+39.041 2.103 l
+39.041 -0.367 l
+39.041 -0.525 39.063 -0.643 39.114 -0.72 c
+39.173 -0.801 39.262 -0.837 39.378 -0.837 c
+39.467 -0.837 39.555 -0.823 39.644 -0.793 c
+39.644 -1.352 l
+39.497 -1.4 39.342 -1.425 39.187 -1.425 c
+38.931 -1.425 38.736 -1.334 38.599 -1.146 c
+38.46 -0.962 38.394 -0.702 38.394 -0.367 c
+38.394 2.103 l
+37.791 2.103 l
+37.791 2.631 l
+38.394 2.631 l
+38.394 3.587 l
+h
+41.055 2.22 m
+41.308 2.544 41.628 2.705 42.01 2.705 c
+42.715 2.705 43.072 2.234 43.083 1.294 c
+43.083 -1.352 l
+42.436 -1.352 l
+42.436 1.264 l
+42.436 1.577 42.381 1.798 42.275 1.926 c
+42.164 2.051 42.01 2.117 41.805 2.117 c
+41.647 2.117 41.499 2.062 41.363 1.956 c
+41.234 1.845 41.132 1.709 41.055 1.544 c
+41.055 -1.352 l
+40.408 -1.352 l
+40.408 4.293 l
+41.055 4.293 l
+h
+46.082 -1.352 m
+46.042 -1.263 46.015 -1.117 46.008 -0.911 c
+45.773 -1.256 45.479 -1.425 45.126 -1.425 c
+44.762 -1.425 44.479 -1.33 44.274 -1.132 c
+44.076 -0.926 43.98 -0.639 43.98 -0.264 c
+43.98 0.136 44.116 0.456 44.392 0.691 c
+44.663 0.934 45.038 1.058 45.508 1.058 c
+45.994 1.058 l
+45.994 1.485 l
+45.994 1.721 45.938 1.885 45.832 1.985 c
+45.722 2.091 45.56 2.147 45.346 2.147 c
+45.148 2.147 44.986 2.087 44.862 1.97 c
+44.744 1.852 44.685 1.706 44.685 1.529 c
+44.039 1.529 l
+44.039 1.723 44.097 1.915 44.215 2.103 c
+44.34 2.286 44.502 2.433 44.7 2.544 c
+44.906 2.65 45.134 2.705 45.391 2.705 c
+45.791 2.705 46.096 2.602 46.302 2.396 c
+46.515 2.191 46.63 1.897 46.64 1.515 c
+46.64 -0.5 l
+46.64 -0.804 46.677 -1.069 46.758 -1.294 c
+46.758 -1.352 l
+h
+45.215 -0.837 m
+45.379 -0.837 45.531 -0.793 45.67 -0.706 c
+45.817 -0.617 45.924 -0.507 45.994 -0.367 c
+45.994 0.574 l
+45.626 0.574 l
+45.31 0.574 45.067 0.504 44.891 0.368 c
+44.714 0.239 44.627 0.052 44.627 -0.191 c
+44.627 -0.419 44.671 -0.584 44.758 -0.69 c
+44.847 -0.789 44.997 -0.837 45.215 -0.837 c
+48.492 3.587 m
+48.492 2.631 l
+49.095 2.631 l
+49.095 2.103 l
+48.492 2.103 l
+48.492 -0.367 l
+48.492 -0.525 48.514 -0.643 48.566 -0.72 c
+48.625 -0.801 48.712 -0.837 48.83 -0.837 c
+48.918 -0.837 49.007 -0.823 49.095 -0.793 c
+49.095 -1.352 l
+48.948 -1.4 48.793 -1.425 48.639 -1.425 c
+48.382 -1.425 48.187 -1.334 48.051 -1.146 c
+47.912 -0.962 47.845 -0.702 47.845 -0.367 c
+47.845 2.103 l
+47.243 2.103 l
+47.243 2.631 l
+47.845 2.631 l
+47.845 3.587 l
+h
+54.549 -0.205 m
+55.151 2.631 l
+55.798 2.631 l
+54.813 -1.352 l
+54.299 -1.352 l
+53.52 1.5 l
+52.77 -1.352 l
+52.24 -1.352 l
+51.285 2.631 l
+51.918 2.631 l
+52.535 -0.132 l
+53.269 2.631 l
+53.784 2.631 l
+h
+57.179 -1.352 -0.646 3.983 re
+57.223 3.675 m
+57.223 3.565 57.194 3.473 57.136 3.396 c
+57.077 3.326 56.981 3.293 56.856 3.293 c
+56.739 3.293 56.643 3.326 56.577 3.396 c
+56.518 3.473 56.489 3.565 56.489 3.675 c
+56.489 3.793 56.518 3.884 56.577 3.955 c
+56.643 4.032 56.739 4.072 56.856 4.072 c
+56.981 4.072 57.077 4.032 57.136 3.955 c
+57.194 3.874 57.223 3.782 57.223 3.675 c
+58.899 -1.352 -0.646 5.644 re
+60.619 -1.352 -0.647 5.644 re
+66.234 0.441 m
+66.234 -0.176 66.12 -0.643 65.896 -0.955 c
+65.68 -1.271 65.356 -1.425 64.926 -1.425 c
+64.504 -1.425 64.191 -1.246 63.985 -0.881 c
+63.956 -1.352 l
+63.353 -1.352 l
+63.353 4.293 l
+63.999 4.293 l
+63.999 2.191 l
+64.213 2.532 64.522 2.705 64.926 2.705 c
+65.356 2.705 65.68 2.547 65.896 2.234 c
+66.12 1.929 66.234 1.463 66.234 0.838 c
+h
+65.587 0.823 m
+65.587 1.294 65.518 1.625 65.381 1.823 c
+65.252 2.018 65.044 2.117 64.749 2.117 c
+64.415 2.117 64.165 1.933 63.999 1.573 c
+63.999 -0.309 l
+64.165 -0.672 64.419 -0.852 64.764 -0.852 c
+65.058 -0.852 65.268 -0.749 65.396 -0.544 c
+65.522 -0.338 65.587 -0.022 65.587 0.412 c
+h
+68.498 -1.425 m
+67.998 -1.425 67.616 -1.278 67.351 -0.984 c
+67.087 -0.69 66.954 -0.257 66.954 0.324 c
+66.954 0.794 l
+66.954 1.389 67.079 1.856 67.336 2.191 c
+67.601 2.532 67.961 2.705 68.425 2.705 c
+68.884 2.705 69.225 2.55 69.453 2.249 c
+69.688 1.956 69.809 1.492 69.821 0.867 c
+69.821 0.441 l
+67.601 0.441 l
+67.601 0.353 l
+67.601 -0.08 67.679 -0.393 67.836 -0.588 c
+68.001 -0.775 68.233 -0.866 68.527 -0.866 c
+68.722 -0.866 68.895 -0.833 69.042 -0.764 c
+69.189 -0.687 69.325 -0.569 69.453 -0.411 c
+69.792 -0.823 l
+69.505 -1.227 69.075 -1.425 68.498 -1.425 c
+68.425 2.147 m
+68.149 2.147 67.947 2.051 67.822 1.867 c
+67.693 1.68 67.619 1.389 67.601 1 c
+69.173 1 l
+69.173 1.088 l
+69.152 1.47 69.086 1.738 68.968 1.897 c
+68.851 2.062 68.666 2.147 68.425 2.147 c
+74.363 -1.352 m
+74.322 -1.263 74.297 -1.117 74.289 -0.911 c
+74.054 -1.256 73.76 -1.425 73.407 -1.425 c
+73.044 -1.425 72.761 -1.33 72.555 -1.132 c
+72.356 -0.926 72.261 -0.639 72.261 -0.264 c
+72.261 0.136 72.396 0.456 72.672 0.691 c
+72.944 0.934 73.319 1.058 73.79 1.058 c
+74.274 1.058 l
+74.274 1.485 l
+74.274 1.721 74.22 1.885 74.112 1.985 c
+74.002 2.091 73.841 2.147 73.628 2.147 c
+73.429 2.147 73.268 2.087 73.142 1.97 c
+73.025 1.852 72.967 1.706 72.967 1.529 c
+72.319 1.529 l
+72.319 1.723 72.379 1.915 72.496 2.103 c
+72.62 2.286 72.782 2.433 72.981 2.544 c
+73.187 2.65 73.414 2.705 73.672 2.705 c
+74.072 2.705 74.378 2.602 74.583 2.396 c
+74.796 2.191 74.91 1.897 74.921 1.515 c
+74.921 -0.5 l
+74.921 -0.804 74.958 -1.069 75.039 -1.294 c
+75.039 -1.352 l
+h
+73.495 -0.837 m
+73.661 -0.837 73.811 -0.793 73.951 -0.706 c
+74.098 -0.617 74.205 -0.507 74.274 -0.367 c
+74.274 0.574 l
+73.907 0.574 l
+73.591 0.574 73.348 0.504 73.172 0.368 c
+72.996 0.239 72.907 0.052 72.907 -0.191 c
+72.907 -0.419 72.952 -0.584 73.04 -0.69 c
+73.128 -0.789 73.279 -0.837 73.495 -0.837 c
+76.773 3.587 m
+76.773 2.631 l
+77.376 2.631 l
+77.376 2.103 l
+76.773 2.103 l
+76.773 -0.367 l
+76.773 -0.525 76.795 -0.643 76.847 -0.72 c
+76.905 -0.801 76.994 -0.837 77.111 -0.837 c
+77.2 -0.837 77.287 -0.823 77.376 -0.793 c
+77.376 -1.352 l
+77.229 -1.4 77.075 -1.425 76.92 -1.425 c
+76.663 -1.425 76.468 -1.334 76.333 -1.146 c
+76.192 -0.962 76.126 -0.702 76.126 -0.367 c
+76.126 2.103 l
+75.523 2.103 l
+75.523 2.631 l
+76.126 2.631 l
+76.126 3.587 l
+h
+78.993 3.587 m
+78.993 2.631 l
+79.595 2.631 l
+79.595 2.103 l
+78.993 2.103 l
+78.993 -0.367 l
+78.993 -0.525 79.015 -0.643 79.066 -0.72 c
+79.125 -0.801 79.213 -0.837 79.331 -0.837 c
+79.419 -0.837 79.508 -0.823 79.595 -0.793 c
+79.595 -1.352 l
+79.448 -1.4 79.294 -1.425 79.14 -1.425 c
+78.883 -1.425 78.688 -1.334 78.552 -1.146 c
+78.412 -0.962 78.346 -0.702 78.346 -0.367 c
+78.346 2.103 l
+77.744 2.103 l
+77.744 2.631 l
+78.346 2.631 l
+78.346 3.587 l
+h
+82.403 -1.352 m
+82.363 -1.263 82.337 -1.117 82.33 -0.911 c
+82.095 -1.256 81.8 -1.425 81.447 -1.425 c
+81.084 -1.425 80.801 -1.33 80.595 -1.132 c
+80.397 -0.926 80.301 -0.639 80.301 -0.264 c
+80.301 0.136 80.437 0.456 80.713 0.691 c
+80.984 0.934 81.36 1.058 81.83 1.058 c
+82.315 1.058 l
+82.315 1.485 l
+82.315 1.721 82.26 1.885 82.153 1.985 c
+82.043 2.091 81.881 2.147 81.668 2.147 c
+81.47 2.147 81.308 2.087 81.183 1.97 c
+81.065 1.852 81.006 1.706 81.006 1.529 c
+80.36 1.529 l
+80.36 1.723 80.419 1.915 80.536 2.103 c
+80.661 2.286 80.823 2.433 81.021 2.544 c
+81.227 2.65 81.455 2.705 81.712 2.705 c
+82.112 2.705 82.417 2.602 82.623 2.396 c
+82.837 2.191 82.951 1.897 82.962 1.515 c
+82.962 -0.5 l
+82.962 -0.804 82.999 -1.069 83.08 -1.294 c
+83.08 -1.352 l
+h
+81.536 -0.837 m
+81.701 -0.837 81.852 -0.793 81.991 -0.706 c
+82.139 -0.617 82.245 -0.507 82.315 -0.367 c
+82.315 0.574 l
+81.947 0.574 l
+81.632 0.574 81.389 0.504 81.212 0.368 c
+81.036 0.239 80.948 0.052 80.948 -0.191 c
+80.948 -0.419 80.992 -0.584 81.08 -0.69 c
+81.168 -0.789 81.319 -0.837 81.536 -0.837 c
+85.343 -0.866 m
+85.556 -0.866 85.729 -0.804 85.857 -0.675 c
+85.993 -0.54 86.066 -0.349 86.078 -0.103 c
+86.695 -0.103 l
+86.673 -0.484 86.537 -0.804 86.284 -1.058 c
+86.026 -1.304 85.714 -1.425 85.343 -1.425 c
+84.85 -1.425 84.476 -1.275 84.211 -0.97 c
+83.953 -0.658 83.828 -0.191 83.828 0.427 c
+83.828 0.867 l
+83.828 1.463 83.953 1.919 84.211 2.234 c
+84.476 2.547 84.85 2.705 85.343 2.705 c
+85.744 2.705 86.064 2.573 86.299 2.309 c
+86.54 2.051 86.673 1.706 86.695 1.264 c
+86.078 1.264 l
+86.056 1.559 85.983 1.779 85.857 1.926 c
+85.74 2.073 85.567 2.147 85.343 2.147 c
+85.048 2.147 84.832 2.047 84.696 1.852 c
+84.556 1.665 84.483 1.357 84.476 0.927 c
+84.476 0.412 l
+84.476 -0.058 84.541 -0.393 84.682 -0.588 c
+84.828 -0.775 85.048 -0.866 85.343 -0.866 c
+88.092 2.22 m
+88.345 2.544 88.665 2.705 89.047 2.705 c
+89.752 2.705 90.109 2.234 90.12 1.294 c
+90.12 -1.352 l
+89.473 -1.352 l
+89.473 1.264 l
+89.473 1.577 89.418 1.798 89.312 1.926 c
+89.201 2.051 89.047 2.117 88.842 2.117 c
+88.684 2.117 88.536 2.062 88.4 1.956 c
+88.271 1.845 88.169 1.709 88.092 1.544 c
+88.092 -1.352 l
+87.445 -1.352 l
+87.445 4.293 l
+88.092 4.293 l
+h
+92.501 -1.425 m
+92.001 -1.425 91.62 -1.278 91.354 -0.984 c
+91.09 -0.69 90.958 -0.257 90.958 0.324 c
+90.958 0.794 l
+90.958 1.389 91.082 1.856 91.34 2.191 c
+91.604 2.532 91.965 2.705 92.428 2.705 c
+92.887 2.705 93.229 2.55 93.457 2.249 c
+93.692 1.956 93.813 1.492 93.825 0.867 c
+93.825 0.441 l
+91.604 0.441 l
+91.604 0.353 l
+91.604 -0.08 91.682 -0.393 91.84 -0.588 c
+92.005 -0.775 92.237 -0.866 92.53 -0.866 c
+92.726 -0.866 92.898 -0.833 93.045 -0.764 c
+93.192 -0.687 93.328 -0.569 93.457 -0.411 c
+93.795 -0.823 l
+93.509 -1.227 93.079 -1.425 92.501 -1.425 c
+92.428 2.147 m
+92.152 2.147 91.95 2.051 91.825 1.867 c
+91.697 1.68 91.623 1.389 91.604 1 c
+93.177 1 l
+93.177 1.088 l
+93.156 1.47 93.089 1.738 92.971 1.897 c
+92.854 2.062 92.67 2.147 92.428 2.147 c
+94.471 0.823 m
+94.471 1.43 94.581 1.897 94.809 2.22 c
+95.044 2.544 95.371 2.705 95.794 2.705 c
+96.176 2.705 96.474 2.547 96.69 2.234 c
+96.69 4.293 l
+97.337 4.293 l
+97.337 -1.352 l
+96.749 -1.352 l
+96.705 -0.926 l
+96.499 -1.26 96.194 -1.425 95.794 -1.425 c
+95.382 -1.425 95.059 -1.271 94.824 -0.955 c
+94.589 -0.631 94.471 -0.176 94.471 0.412 c
+h
+95.117 0.441 m
+95.117 0 95.18 -0.33 95.309 -0.544 c
+95.445 -0.749 95.666 -0.852 95.97 -0.852 c
+96.293 -0.852 96.532 -0.69 96.69 -0.367 c
+96.69 1.646 l
+96.522 1.959 96.283 2.117 95.97 2.117 c
+95.666 2.117 95.445 2.014 95.309 1.808 c
+95.18 1.603 95.117 1.279 95.117 0.838 c
+h
+100.909 3.587 m
+100.909 2.631 l
+101.511 2.631 l
+101.511 2.103 l
+100.909 2.103 l
+100.909 -0.367 l
+100.909 -0.525 100.931 -0.643 100.983 -0.72 c
+101.041 -0.801 101.129 -0.837 101.247 -0.837 c
+101.335 -0.837 101.424 -0.823 101.511 -0.793 c
+101.511 -1.352 l
+101.365 -1.4 101.21 -1.425 101.056 -1.425 c
+100.799 -1.425 100.605 -1.334 100.468 -1.146 c
+100.329 -0.962 100.262 -0.702 100.262 -0.367 c
+100.262 2.103 l
+99.66 2.103 l
+99.66 2.631 l
+100.262 2.631 l
+100.262 3.587 l
+h
+102.07 0.823 m
+102.07 1.401 102.207 1.856 102.482 2.191 c
+102.764 2.532 103.136 2.705 103.599 2.705 c
+104.058 2.705 104.426 2.536 104.701 2.205 c
+104.985 1.881 105.131 1.434 105.143 0.867 c
+105.143 0.441 l
+105.143 -0.128 104.999 -0.584 104.717 -0.926 c
+104.441 -1.26 104.073 -1.425 103.614 -1.425 c
+103.151 -1.425 102.78 -1.263 102.496 -0.941 c
+102.221 -0.61 102.078 -0.168 102.07 0.383 c
+h
+102.717 0.441 m
+102.717 0.038 102.795 -0.278 102.953 -0.514 c
+103.117 -0.749 103.338 -0.866 103.614 -0.866 c
+104.179 -0.866 104.474 -0.455 104.495 0.368 c
+104.495 0.823 l
+104.495 1.224 104.411 1.544 104.246 1.779 c
+104.088 2.022 103.871 2.147 103.599 2.147 c
+103.335 2.147 103.117 2.022 102.953 1.779 c
+102.795 1.544 102.717 1.224 102.717 0.823 c
+h
+108.788 -0.264 m
+109.508 2.631 l
+110.199 2.631 l
+108.906 -1.911 l
+108.806 -2.252 108.663 -2.513 108.479 -2.69 c
+108.303 -2.865 108.1 -2.954 107.876 -2.954 c
+107.788 -2.954 107.674 -2.932 107.539 -2.896 c
+107.539 -2.352 l
+107.685 -2.366 l
+107.869 -2.366 108.016 -2.322 108.127 -2.234 c
+108.233 -2.146 108.321 -1.988 108.391 -1.764 c
+108.509 -1.323 l
+107.347 2.631 l
+108.052 2.631 l
+h
+110.639 0.823 m
+110.639 1.401 110.776 1.856 111.051 2.191 c
+111.335 2.532 111.705 2.705 112.169 2.705 c
+112.628 2.705 112.995 2.536 113.271 2.205 c
+113.554 1.881 113.701 1.434 113.712 0.867 c
+113.712 0.441 l
+113.712 -0.128 113.569 -0.584 113.286 -0.926 c
+113.01 -1.26 112.642 -1.425 112.183 -1.425 c
+111.72 -1.425 111.349 -1.263 111.067 -0.941 c
+110.791 -0.61 110.647 -0.168 110.639 0.383 c
+h
+111.287 0.441 m
+111.287 0.038 111.364 -0.278 111.522 -0.514 c
+111.687 -0.749 111.908 -0.866 112.183 -0.866 c
+112.749 -0.866 113.043 -0.455 113.066 0.368 c
+113.066 0.823 l
+113.066 1.224 112.981 1.544 112.815 1.779 c
+112.657 2.022 112.441 2.147 112.169 2.147 c
+111.904 2.147 111.687 2.022 111.522 1.779 c
+111.364 1.544 111.287 1.224 111.287 0.823 c
+h
+116.578 -0.999 m
+116.362 -1.286 116.049 -1.425 115.638 -1.425 c
+115.273 -1.425 114.998 -1.304 114.814 -1.058 c
+114.638 -0.804 114.543 -0.44 114.535 0.03 c
+114.535 2.631 l
+115.182 2.631 l
+115.182 0.088 l
+115.182 -0.54 115.366 -0.852 115.74 -0.852 c
+116.141 -0.852 116.416 -0.675 116.563 -0.323 c
+116.563 2.631 l
+117.21 2.631 l
+117.21 -1.352 l
+116.593 -1.352 l
+h
+119.842 2.014 m
+119.753 2.033 119.655 2.043 119.547 2.043 c
+119.213 2.043 118.978 1.86 118.842 1.5 c
+118.842 -1.352 l
+118.195 -1.352 l
+118.195 2.631 l
+118.828 2.631 l
+118.842 2.22 l
+119.019 2.544 119.261 2.705 119.576 2.705 c
+119.684 2.705 119.772 2.683 119.842 2.646 c
+h
+123.59 -0.866 m
+123.803 -0.866 123.975 -0.804 124.104 -0.675 c
+124.241 -0.54 124.314 -0.349 124.324 -0.103 c
+124.942 -0.103 l
+124.92 -0.484 124.784 -0.804 124.53 -1.058 c
+124.274 -1.304 123.961 -1.425 123.59 -1.425 c
+123.098 -1.425 122.722 -1.275 122.458 -0.97 c
+122.201 -0.658 122.076 -0.191 122.076 0.427 c
+122.076 0.867 l
+122.076 1.463 122.201 1.919 122.458 2.234 c
+122.722 2.547 123.098 2.705 123.59 2.705 c
+123.99 2.705 124.31 2.573 124.545 2.309 c
+124.788 2.051 124.92 1.706 124.942 1.264 c
+124.324 1.264 l
+124.303 1.559 124.229 1.779 124.104 1.926 c
+123.987 2.073 123.814 2.147 123.59 2.147 c
+123.296 2.147 123.079 2.047 122.943 1.852 c
+122.803 1.665 122.73 1.357 122.722 0.927 c
+122.722 0.412 l
+122.722 -0.058 122.789 -0.393 122.928 -0.588 c
+123.075 -0.775 123.296 -0.866 123.59 -0.866 c
+125.56 0.823 m
+125.56 1.401 125.695 1.856 125.971 2.191 c
+126.254 2.532 126.625 2.705 127.088 2.705 c
+127.547 2.705 127.915 2.536 128.191 2.205 c
+128.474 1.881 128.621 1.434 128.632 0.867 c
+128.632 0.441 l
+128.632 -0.128 128.488 -0.584 128.205 -0.926 c
+127.929 -1.26 127.562 -1.425 127.102 -1.425 c
+126.64 -1.425 126.269 -1.263 125.986 -0.941 c
+125.71 -0.61 125.567 -0.168 125.56 0.383 c
+h
+126.206 0.441 m
+126.206 0.038 126.283 -0.278 126.441 -0.514 c
+126.607 -0.749 126.828 -0.866 127.102 -0.866 c
+127.669 -0.866 127.963 -0.455 127.985 0.368 c
+127.985 0.823 l
+127.985 1.224 127.9 1.544 127.735 1.779 c
+127.576 2.022 127.36 2.147 127.088 2.147 c
+126.824 2.147 126.607 2.022 126.441 1.779 c
+126.283 1.544 126.206 1.224 126.206 0.823 c
+h
+130.086 2.631 m
+130.101 2.264 l
+130.344 2.558 130.664 2.705 131.057 2.705 c
+131.497 2.705 131.806 2.507 131.983 2.117 c
+132.237 2.507 132.586 2.705 133.026 2.705 c
+133.762 2.705 134.136 2.242 134.158 1.324 c
+134.158 -1.352 l
+133.512 -1.352 l
+133.512 1.264 l
+133.512 1.559 133.456 1.771 133.35 1.912 c
+133.251 2.047 133.078 2.117 132.835 2.117 c
+132.637 2.117 132.475 2.037 132.351 1.881 c
+132.233 1.735 132.163 1.544 132.145 1.309 c
+132.145 -1.352 l
+131.483 -1.352 l
+131.483 1.294 l
+131.483 1.841 131.262 2.117 130.822 2.117 c
+130.487 2.117 130.252 1.956 130.116 1.632 c
+130.116 -1.352 l
+129.469 -1.352 l
+129.469 2.631 l
+h
+135.746 2.631 m
+135.761 2.264 l
+136.003 2.558 136.323 2.705 136.716 2.705 c
+137.157 2.705 137.466 2.507 137.642 2.117 c
+137.895 2.507 138.244 2.705 138.686 2.705 c
+139.42 2.705 139.796 2.242 139.817 1.324 c
+139.817 -1.352 l
+139.171 -1.352 l
+139.171 1.264 l
+139.171 1.559 139.116 1.771 139.009 1.912 c
+138.91 2.047 138.737 2.117 138.495 2.117 c
+138.296 2.117 138.134 2.037 138.009 1.881 c
+137.892 1.735 137.822 1.544 137.804 1.309 c
+137.804 -1.352 l
+137.142 -1.352 l
+137.142 1.294 l
+137.142 1.841 136.922 2.117 136.481 2.117 c
+136.147 2.117 135.911 1.956 135.775 1.632 c
+135.775 -1.352 l
+135.129 -1.352 l
+135.129 2.631 l
+h
+141.493 -1.352 -0.646 3.983 re
+141.537 3.675 m
+141.537 3.565 141.508 3.473 141.449 3.396 c
+141.39 3.326 141.294 3.293 141.17 3.293 c
+141.053 3.293 140.957 3.326 140.891 3.396 c
+140.831 3.473 140.802 3.565 140.802 3.675 c
+140.802 3.793 140.831 3.884 140.891 3.955 c
+140.957 4.032 141.053 4.072 141.17 4.072 c
+141.294 4.072 141.39 4.032 141.449 3.955 c
+141.508 3.874 141.537 3.782 141.537 3.675 c
+143.36 3.587 m
+143.36 2.631 l
+143.962 2.631 l
+143.962 2.103 l
+143.36 2.103 l
+143.36 -0.367 l
+143.36 -0.525 143.382 -0.643 143.434 -0.72 c
+143.492 -0.801 143.58 -0.837 143.698 -0.837 c
+143.786 -0.837 143.875 -0.823 143.962 -0.793 c
+143.962 -1.352 l
+143.816 -1.4 143.661 -1.425 143.507 -1.425 c
+143.25 -1.425 143.055 -1.334 142.919 -1.146 c
+142.78 -0.962 142.713 -0.702 142.713 -0.367 c
+142.713 2.103 l
+142.111 2.103 l
+142.111 2.631 l
+142.713 2.631 l
+142.713 3.587 l
+h
+146.667 -0.338 m
+146.667 -0.191 146.612 -0.07 146.506 0.03 c
+146.395 0.125 146.19 0.243 145.888 0.383 c
+145.543 0.53 145.3 0.651 145.153 0.75 c
+145.006 0.857 144.896 0.975 144.83 1.103 c
+144.76 1.228 144.727 1.386 144.727 1.573 c
+144.727 1.897 144.845 2.165 145.08 2.382 c
+145.315 2.595 145.616 2.705 145.991 2.705 c
+146.373 2.705 146.682 2.591 146.917 2.367 c
+147.152 2.139 147.27 1.852 147.27 1.5 c
+146.623 1.5 l
+146.623 1.676 146.564 1.827 146.447 1.956 c
+146.329 2.08 146.175 2.147 145.991 2.147 c
+145.793 2.147 145.642 2.091 145.535 1.985 c
+145.425 1.885 145.373 1.754 145.373 1.588 c
+145.373 1.459 145.41 1.353 145.491 1.264 c
+145.568 1.183 145.76 1.081 146.065 0.956 c
+146.543 0.769 146.873 0.581 147.05 0.397 c
+147.226 0.221 147.314 -0.007 147.314 -0.278 c
+147.314 -0.631 147.189 -0.911 146.946 -1.117 c
+146.711 -1.323 146.395 -1.425 146.006 -1.425 c
+145.583 -1.425 145.245 -1.308 144.991 -1.072 c
+144.735 -0.83 144.609 -0.525 144.609 -0.161 c
+145.256 -0.161 l
+145.263 -0.389 145.333 -0.565 145.462 -0.69 c
+145.587 -0.808 145.77 -0.866 146.006 -0.866 c
+146.219 -0.866 146.381 -0.819 146.491 -0.72 c
+146.609 -0.625 146.667 -0.496 146.667 -0.338 c
+151.915 -1.352 m
+151.875 -1.263 151.848 -1.117 151.841 -0.911 c
+151.606 -1.256 151.312 -1.425 150.959 -1.425 c
+150.595 -1.425 150.312 -1.33 150.107 -1.132 c
+149.909 -0.926 149.813 -0.639 149.813 -0.264 c
+149.813 0.136 149.949 0.456 150.225 0.691 c
+150.497 0.934 150.871 1.058 151.341 1.058 c
+151.827 1.058 l
+151.827 1.485 l
+151.827 1.721 151.771 1.885 151.665 1.985 c
+151.555 2.091 151.393 2.147 151.18 2.147 c
+150.981 2.147 150.819 2.087 150.695 1.97 c
+150.578 1.852 150.518 1.706 150.518 1.529 c
+149.872 1.529 l
+149.872 1.723 149.93 1.915 150.048 2.103 c
+150.173 2.286 150.335 2.433 150.533 2.544 c
+150.739 2.65 150.967 2.705 151.224 2.705 c
+151.625 2.705 151.929 2.602 152.135 2.396 c
+152.348 2.191 152.463 1.897 152.473 1.515 c
+152.473 -0.5 l
+152.473 -0.804 152.51 -1.069 152.591 -1.294 c
+152.591 -1.352 l
+h
+151.048 -0.837 m
+151.213 -0.837 151.364 -0.793 151.503 -0.706 c
+151.65 -0.617 151.757 -0.507 151.827 -0.367 c
+151.827 0.574 l
+151.459 0.574 l
+151.143 0.574 150.9 0.504 150.724 0.368 c
+150.547 0.239 150.46 0.052 150.46 -0.191 c
+150.46 -0.419 150.504 -0.584 150.592 -0.69 c
+150.68 -0.789 150.831 -0.837 151.048 -0.837 c
+154.09 2.631 m
+154.105 2.191 l
+154.358 2.532 154.682 2.705 155.075 2.705 c
+155.781 2.705 156.137 2.234 156.149 1.294 c
+156.149 -1.352 l
+155.501 -1.352 l
+155.501 1.264 l
+155.501 1.577 155.446 1.798 155.34 1.926 c
+155.23 2.051 155.075 2.117 154.869 2.117 c
+154.711 2.117 154.564 2.062 154.428 1.956 c
+154.3 1.845 154.197 1.709 154.119 1.544 c
+154.119 -1.352 l
+153.473 -1.352 l
+153.473 2.631 l
+h
+156.986 0.823 m
+156.986 1.43 157.096 1.897 157.325 2.22 c
+157.56 2.544 157.886 2.705 158.309 2.705 c
+158.691 2.705 158.989 2.547 159.206 2.234 c
+159.206 4.293 l
+159.852 4.293 l
+159.852 -1.352 l
+159.264 -1.352 l
+159.22 -0.926 l
+159.014 -1.26 158.709 -1.425 158.309 -1.425 c
+157.897 -1.425 157.574 -1.271 157.339 -0.955 c
+157.103 -0.631 156.986 -0.176 156.986 0.412 c
+h
+157.633 0.441 m
+157.633 0 157.695 -0.33 157.824 -0.544 c
+157.96 -0.749 158.181 -0.852 158.485 -0.852 c
+158.809 -0.852 159.048 -0.69 159.206 -0.367 c
+159.206 1.646 l
+159.037 1.959 158.798 2.117 158.485 2.117 c
+158.181 2.117 157.96 2.014 157.824 1.808 c
+157.695 1.603 157.633 1.279 157.633 0.838 c
+h
+163.424 3.587 m
+163.424 2.631 l
+164.026 2.631 l
+164.026 2.103 l
+163.424 2.103 l
+163.424 -0.367 l
+163.424 -0.525 163.446 -0.643 163.498 -0.72 c
+163.556 -0.801 163.644 -0.837 163.762 -0.837 c
+163.85 -0.837 163.939 -0.823 164.026 -0.793 c
+164.026 -1.352 l
+163.88 -1.4 163.725 -1.425 163.571 -1.425 c
+163.314 -1.425 163.119 -1.334 162.983 -1.146 c
+162.844 -0.962 162.777 -0.702 162.777 -0.367 c
+162.777 2.103 l
+162.175 2.103 l
+162.175 2.631 l
+162.777 2.631 l
+162.777 3.587 l
+h
+166.834 -1.352 m
+166.794 -1.263 166.769 -1.117 166.761 -0.911 c
+166.526 -1.256 166.231 -1.425 165.879 -1.425 c
+165.516 -1.425 165.232 -1.33 165.026 -1.132 c
+164.828 -0.926 164.732 -0.639 164.732 -0.264 c
+164.732 0.136 164.868 0.456 165.144 0.691 c
+165.416 0.934 165.791 1.058 166.262 1.058 c
+166.746 1.058 l
+166.746 1.485 l
+166.746 1.721 166.691 1.885 166.584 1.985 c
+166.474 2.091 166.312 2.147 166.1 2.147 c
+165.901 2.147 165.739 2.087 165.614 1.97 c
+165.497 1.852 165.437 1.706 165.437 1.529 c
+164.791 1.529 l
+164.791 1.723 164.85 1.915 164.967 2.103 c
+165.092 2.286 165.254 2.433 165.453 2.544 c
+165.659 2.65 165.886 2.705 166.143 2.705 c
+166.544 2.705 166.849 2.602 167.055 2.396 c
+167.268 2.191 167.382 1.897 167.393 1.515 c
+167.393 -0.5 l
+167.393 -0.804 167.43 -1.069 167.511 -1.294 c
+167.511 -1.352 l
+h
+165.967 -0.837 m
+166.133 -0.837 166.283 -0.793 166.422 -0.706 c
+166.57 -0.617 166.676 -0.507 166.746 -0.367 c
+166.746 0.574 l
+166.378 0.574 l
+166.063 0.574 165.82 0.504 165.643 0.368 c
+165.468 0.239 165.379 0.052 165.379 -0.191 c
+165.379 -0.419 165.423 -0.584 165.512 -0.69 c
+165.599 -0.789 165.751 -0.837 165.967 -0.837 c
+168.26 0.823 m
+168.26 1.441 168.371 1.904 168.598 2.22 c
+168.822 2.544 169.157 2.705 169.597 2.705 c
+169.998 2.705 170.303 2.529 170.509 2.176 c
+170.553 2.631 l
+171.141 2.631 l
+171.141 -1.396 l
+171.141 -1.884 171.012 -2.263 170.759 -2.528 c
+170.501 -2.792 170.149 -2.925 169.701 -2.925 c
+169.502 -2.925 169.281 -2.873 169.04 -2.778 c
+168.793 -2.678 168.613 -2.557 168.495 -2.41 c
+168.76 -1.969 l
+169.025 -2.234 169.322 -2.366 169.657 -2.366 c
+170.193 -2.366 170.469 -2.072 170.48 -1.484 c
+170.48 -0.955 l
+170.274 -1.271 169.973 -1.425 169.583 -1.425 c
+169.171 -1.425 168.849 -1.275 168.613 -0.97 c
+168.385 -0.658 168.267 -0.205 168.26 0.383 c
+h
+168.922 0.441 m
+168.922 0 168.984 -0.33 169.113 -0.544 c
+169.238 -0.749 169.454 -0.852 169.759 -0.852 c
+170.083 -0.852 170.322 -0.687 170.48 -0.353 c
+170.48 1.632 l
+170.311 1.956 170.072 2.117 169.759 2.117 c
+169.466 2.117 169.248 2.014 169.113 1.808 c
+168.984 1.603 168.922 1.279 168.922 0.838 c
+h
+174.037 -0.338 m
+174.037 -0.191 173.982 -0.07 173.875 0.03 c
+173.765 0.125 173.559 0.243 173.258 0.383 c
+172.913 0.53 172.67 0.651 172.523 0.75 c
+172.376 0.857 172.265 0.975 172.2 1.103 c
+172.13 1.228 172.097 1.386 172.097 1.573 c
+172.097 1.897 172.215 2.165 172.45 2.382 c
+172.685 2.595 172.986 2.705 173.36 2.705 c
+173.743 2.705 174.052 2.591 174.287 2.367 c
+174.522 2.139 174.64 1.852 174.64 1.5 c
+173.993 1.5 l
+173.993 1.676 173.934 1.827 173.817 1.956 c
+173.699 2.08 173.545 2.147 173.36 2.147 c
+173.162 2.147 173.011 2.091 172.905 1.985 c
+172.795 1.885 172.743 1.754 172.743 1.588 c
+172.743 1.459 172.78 1.353 172.861 1.264 c
+172.938 1.183 173.129 1.081 173.434 0.956 c
+173.912 0.769 174.243 0.581 174.419 0.397 c
+174.596 0.221 174.684 -0.007 174.684 -0.278 c
+174.684 -0.631 174.559 -0.911 174.316 -1.117 c
+174.081 -1.323 173.765 -1.425 173.375 -1.425 c
+172.953 -1.425 172.614 -1.308 172.361 -1.072 c
+172.104 -0.83 171.979 -0.525 171.979 -0.161 c
+172.626 -0.161 l
+172.633 -0.389 172.703 -0.565 172.832 -0.69 c
+172.957 -0.808 173.14 -0.866 173.375 -0.866 c
+173.589 -0.866 173.75 -0.819 173.861 -0.72 c
+173.978 -0.625 174.037 -0.496 174.037 -0.338 c
+175.565 -0.999 m
+175.565 -0.881 175.598 -0.786 175.669 -0.706 c
+175.735 -0.628 175.837 -0.588 175.978 -0.588 c
+176.124 -0.588 176.231 -0.628 176.3 -0.706 c
+176.377 -0.786 176.418 -0.881 176.418 -0.999 c
+176.418 -1.109 176.377 -1.201 176.3 -1.278 c
+176.231 -1.356 176.124 -1.396 175.978 -1.396 c
+175.837 -1.396 175.735 -1.356 175.669 -1.278 c
+175.598 -1.201 175.565 -1.109 175.565 -0.999 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 383.129 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 376.2903 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.807 l
+-1.896 -1.807 l
+-1.896 -1.263 l
+-2.142 -1.256 -2.359 -1.219 -2.543 -1.161 c
+-2.719 -1.102 -2.873 -1.024 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.034 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.477 -2.234 -0.514 c
+-2.138 -0.554 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.662 l
+-1.907 0.662 -1.926 0.666 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.757 -3.322 1.985 c
+-3.322 2.18 -3.289 2.345 -3.219 2.485 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.933 -2.645 3.003 -2.469 3.043 c
+-2.293 3.091 -2.102 3.12 -1.896 3.132 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.132 l
+-1.319 3.12 -1.128 3.091 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.621 -0.279 2.485 c
+-0.202 2.345 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.368 l
+-1.514 1.368 l
+-1.506 1.368 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.32 0.871 -0.213 0.743 -0.132 0.588 c
+-0.044 0.431 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.503 -2.113 2.485 -2.19 2.455 c
+-2.26 2.426 -2.319 2.386 -2.366 2.338 c
+-2.406 2.297 -2.437 2.249 -2.454 2.191 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.838 -2.439 1.779 c
+-2.41 1.721 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.201 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.634 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.395 c
+5.284 -2.314 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.83 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.219 6.034 -1.219 c
+5.829 -1.219 5.644 -1.182 5.49 -1.102 c
+5.343 -1.014 5.215 -0.897 5.108 -0.749 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.019 4.888 1.235 c
+4.946 1.449 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.188 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.919 7.107 1.97 c
+7.115 2.029 7.122 2.077 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.414 7.938 -1.635 7.871 -1.822 c
+7.802 -2.006 7.699 -2.16 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.743 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.368 c
+5.88 1.279 5.835 1.162 5.799 1.015 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.514 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.293 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.603 l
+9.199 1.603 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.603 m
+13.057 1.603 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.22 l
+15.732 2.22 l
+15.732 1.603 l
+14.497 1.603 l
+14.497 -0.103 l
+14.497 -0.323 l
+14.504 -0.392 14.527 -0.455 14.556 -0.514 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.675 14.909 -0.69 15.056 -0.69 c
+15.192 -0.69 15.327 -0.687 15.468 -0.675 c
+15.603 -0.658 15.736 -0.631 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.215 15.707 -1.23 15.629 -1.249 c
+15.549 -1.26 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.3 14.942 -1.308 14.835 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.263 c
+14.221 -1.227 14.107 -1.182 14.012 -1.132 c
+13.924 -1.084 13.85 -1.024 13.791 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.631 13.608 -0.544 13.6 -0.455 c
+13.59 -0.359 13.586 -0.264 13.586 -0.176 c
+h
+22.677 -1.323 m
+22.391 -1.323 22.148 -1.282 21.943 -1.205 c
+21.737 -1.117 21.564 -0.995 21.428 -0.837 c
+21.288 -0.683 21.185 -0.496 21.119 -0.278 c
+21.049 -0.055 21.016 0.192 21.016 0.456 c
+21.016 0.75 21.049 1.008 21.119 1.235 c
+21.197 1.459 21.303 1.646 21.442 1.794 c
+21.589 1.948 21.766 2.066 21.972 2.147 c
+22.178 2.234 22.413 2.278 22.677 2.278 c
+22.901 2.278 23.104 2.249 23.28 2.191 c
+23.456 2.132 23.607 2.047 23.736 1.941 c
+23.861 1.842 23.963 1.721 24.044 1.573 c
+24.122 1.434 24.176 1.283 24.206 1.118 c
+23.295 1.073 l
+23.265 1.25 23.196 1.389 23.089 1.5 c
+22.99 1.607 22.846 1.661 22.662 1.661 c
+22.416 1.661 22.24 1.559 22.134 1.353 c
+22.023 1.154 21.972 0.867 21.972 0.485 c
+21.972 -0.309 22.207 -0.706 22.677 -0.706 c
+22.843 -0.706 22.986 -0.654 23.104 -0.544 c
+23.221 -0.436 23.295 -0.275 23.324 -0.058 c
+24.235 -0.103 l
+24.206 -0.272 24.151 -0.426 24.074 -0.573 c
+24.004 -0.72 23.901 -0.852 23.765 -0.97 c
+23.636 -1.08 23.478 -1.168 23.295 -1.234 c
+23.119 -1.294 22.913 -1.323 22.677 -1.323 c
+28.384 0.485 m
+28.384 0.21 28.347 -0.039 28.281 -0.264 c
+28.212 -0.481 28.108 -0.668 27.973 -0.823 c
+27.833 -0.98 27.657 -1.102 27.443 -1.19 c
+27.227 -1.278 26.973 -1.323 26.679 -1.323 c
+26.404 -1.323 26.157 -1.278 25.944 -1.19 c
+25.739 -1.102 25.566 -0.98 25.43 -0.823 c
+25.29 -0.668 25.187 -0.481 25.121 -0.264 c
+25.051 -0.039 25.018 0.21 25.018 0.485 c
+25.018 0.739 25.047 0.975 25.106 1.191 c
+25.172 1.415 25.275 1.607 25.415 1.764 c
+25.55 1.929 25.727 2.058 25.944 2.147 c
+26.157 2.234 26.414 2.278 26.709 2.278 c
+27.021 2.278 27.281 2.234 27.487 2.147 c
+27.701 2.058 27.873 1.929 28.002 1.764 c
+28.138 1.607 28.237 1.415 28.296 1.191 c
+28.355 0.975 28.384 0.739 28.384 0.485 c
+27.429 0.485 m
+27.429 0.691 27.414 0.867 27.385 1.015 c
+27.362 1.162 27.326 1.283 27.267 1.382 c
+27.208 1.478 27.135 1.548 27.046 1.588 c
+26.959 1.636 26.848 1.661 26.724 1.661 c
+26.458 1.661 26.267 1.563 26.15 1.368 c
+26.032 1.18 25.974 0.886 25.974 0.485 c
+25.974 0.063 26.032 -0.242 26.15 -0.426 c
+26.267 -0.613 26.444 -0.706 26.679 -0.706 c
+26.804 -0.706 26.918 -0.687 27.017 -0.646 c
+27.113 -0.598 27.194 -0.525 27.252 -0.426 c
+27.318 -0.33 27.362 -0.205 27.385 -0.058 c
+27.414 0.088 27.429 0.269 27.429 0.485 c
+31.43 -1.263 m
+31.43 0.721 l
+31.43 1.022 31.387 1.243 31.298 1.382 c
+31.217 1.529 31.081 1.603 30.886 1.603 c
+30.776 1.603 30.674 1.577 30.578 1.529 c
+30.489 1.478 30.409 1.411 30.343 1.324 c
+30.284 1.235 30.233 1.125 30.196 1 c
+30.167 0.882 30.152 0.75 30.152 0.603 c
+30.152 -1.263 l
+29.24 -1.263 l
+29.24 1.441 l
+29.24 1.661 l
+29.24 1.75 29.233 1.827 29.226 1.897 c
+29.226 2.087 l
+29.226 2.22 l
+30.078 2.22 l
+30.086 2.191 30.093 2.147 30.093 2.087 c
+30.093 1.897 l
+30.1 1.827 30.107 1.757 30.107 1.691 c
+30.115 1.621 30.122 1.566 30.122 1.529 c
+30.137 1.529 l
+30.254 1.794 30.406 1.985 30.593 2.103 c
+30.776 2.22 30.996 2.278 31.254 2.278 c
+31.438 2.278 31.599 2.249 31.74 2.191 c
+31.875 2.132 31.989 2.043 32.077 1.926 c
+32.166 1.808 32.228 1.665 32.268 1.5 c
+32.316 1.341 32.342 1.154 32.342 0.941 c
+32.342 -1.263 l
+h
+34.951 1.603 m
+34.951 -1.263 l
+34.054 -1.263 l
+34.054 1.603 l
+33.232 1.603 l
+33.232 2.22 l
+34.054 2.22 l
+34.054 2.485 l
+34.054 2.61 34.069 2.742 34.098 2.882 c
+34.135 3.018 34.205 3.135 34.304 3.234 c
+34.41 3.341 34.554 3.429 34.73 3.499 c
+34.907 3.565 35.131 3.602 35.407 3.602 c
+35.62 3.602 35.819 3.591 35.995 3.572 c
+36.17 3.55 36.322 3.532 36.45 3.514 c
+36.45 2.926 l
+36.322 2.944 36.178 2.959 36.024 2.97 c
+35.866 2.977 35.715 2.984 35.568 2.984 c
+35.44 2.984 35.337 2.97 35.26 2.94 c
+35.179 2.911 35.116 2.87 35.069 2.822 c
+35.017 2.771 34.984 2.708 34.965 2.631 c
+34.955 2.562 34.951 2.485 34.951 2.396 c
+34.951 2.22 l
+36.377 2.22 l
+36.377 1.603 l
+h
+39.467 -0.646 m
+40.599 -0.646 l
+40.599 -1.263 l
+37.292 -1.263 l
+37.292 -0.646 l
+38.556 -0.646 l
+38.556 1.603 l
+37.63 1.603 l
+37.63 2.22 l
+39.467 2.22 l
+h
+38.556 3.514 0.911 -0.676 re
+38.556 2.837 m
+42.954 -2.66 m
+42.738 -2.66 42.547 -2.634 42.381 -2.587 c
+42.212 -2.547 42.073 -2.484 41.955 -2.395 c
+41.837 -2.314 41.738 -2.219 41.661 -2.102 c
+41.591 -1.984 41.543 -1.855 41.514 -1.72 c
+42.41 -1.616 l
+42.447 -1.753 42.518 -1.859 42.616 -1.94 c
+42.712 -2.028 42.837 -2.072 42.984 -2.072 c
+43.072 -2.072 43.153 -2.057 43.234 -2.028 c
+43.311 -1.999 43.381 -1.944 43.44 -1.866 c
+43.499 -1.797 43.542 -1.705 43.572 -1.587 c
+43.609 -1.469 43.631 -1.323 43.631 -1.146 c
+43.631 -0.955 l
+43.631 -0.889 43.634 -0.83 43.646 -0.779 c
+43.646 -0.588 l
+43.631 -0.588 l
+43.532 -0.816 43.388 -0.977 43.204 -1.072 c
+43.017 -1.172 42.811 -1.219 42.587 -1.219 c
+42.381 -1.219 42.198 -1.182 42.044 -1.102 c
+41.896 -1.014 41.768 -0.897 41.661 -0.749 c
+41.562 -0.595 41.488 -0.411 41.441 -0.205 c
+41.389 0.008 41.367 0.243 41.367 0.5 c
+41.367 0.772 41.389 1.019 41.441 1.235 c
+41.5 1.449 41.58 1.632 41.691 1.779 c
+41.797 1.933 41.93 2.051 42.088 2.132 c
+42.242 2.22 42.429 2.264 42.646 2.264 c
+42.742 2.264 42.84 2.253 42.94 2.234 c
+43.035 2.213 43.123 2.18 43.204 2.132 c
+43.293 2.08 43.37 2.018 43.44 1.941 c
+43.517 1.86 43.58 1.768 43.631 1.661 c
+43.646 1.661 l
+43.646 1.808 l
+43.653 1.867 43.66 1.919 43.66 1.97 c
+43.667 2.029 43.675 2.077 43.675 2.117 c
+43.682 2.165 43.694 2.198 43.704 2.22 c
+44.557 2.22 l
+44.546 2.139 44.535 2.029 44.527 1.881 c
+44.527 1.411 l
+44.527 -1.161 l
+44.527 -1.414 44.49 -1.635 44.425 -1.822 c
+44.355 -2.006 44.251 -2.16 44.116 -2.278 c
+43.976 -2.403 43.811 -2.499 43.616 -2.557 c
+43.418 -2.624 43.197 -2.66 42.954 -2.66 c
+43.646 0.53 m
+43.646 0.743 43.619 0.919 43.572 1.058 c
+43.532 1.206 43.476 1.324 43.41 1.411 c
+43.351 1.5 43.282 1.559 43.204 1.588 c
+43.123 1.625 43.046 1.646 42.969 1.646 c
+42.87 1.646 42.778 1.621 42.69 1.573 c
+42.609 1.532 42.543 1.463 42.484 1.368 c
+42.433 1.279 42.389 1.162 42.352 1.015 c
+42.323 0.875 42.308 0.706 42.308 0.5 c
+42.308 0.125 42.366 -0.154 42.484 -0.338 c
+42.601 -0.514 42.763 -0.602 42.969 -0.602 c
+43.035 -0.602 43.109 -0.588 43.189 -0.558 c
+43.278 -0.521 43.351 -0.463 43.41 -0.382 c
+43.476 -0.293 43.532 -0.176 43.572 -0.029 c
+43.619 0.118 43.646 0.302 43.646 0.53 c
+50.135 0.838 1.867 -0.794 re
+50.135 0.044 m
+54.2 0.838 1.866 -0.794 re
+54.2 0.044 m
+59.201 -2.66 m
+58.983 -2.66 58.792 -2.634 58.628 -2.587 c
+58.458 -2.547 58.318 -2.484 58.201 -2.395 c
+58.083 -2.314 57.984 -2.219 57.907 -2.102 c
+57.838 -1.984 57.79 -1.855 57.76 -1.72 c
+58.657 -1.616 l
+58.694 -1.753 58.763 -1.859 58.863 -1.94 c
+58.958 -2.028 59.083 -2.072 59.23 -2.072 c
+59.318 -2.072 59.399 -2.057 59.48 -2.028 c
+59.557 -1.999 59.627 -1.944 59.686 -1.866 c
+59.744 -1.797 59.789 -1.705 59.818 -1.587 c
+59.855 -1.469 59.877 -1.323 59.877 -1.146 c
+59.877 -0.955 l
+59.877 -0.889 59.881 -0.83 59.891 -0.779 c
+59.891 -0.588 l
+59.877 -0.588 l
+59.777 -0.816 59.634 -0.977 59.451 -1.072 c
+59.263 -1.172 59.058 -1.219 58.833 -1.219 c
+58.628 -1.219 58.443 -1.182 58.289 -1.102 c
+58.142 -1.014 58.014 -0.897 57.907 -0.749 c
+57.807 -0.595 57.734 -0.411 57.687 -0.205 c
+57.635 0.008 57.613 0.243 57.613 0.5 c
+57.613 0.772 57.635 1.019 57.687 1.235 c
+57.745 1.449 57.826 1.632 57.936 1.779 c
+58.043 1.933 58.175 2.051 58.333 2.132 c
+58.488 2.22 58.675 2.264 58.892 2.264 c
+58.987 2.264 59.087 2.253 59.186 2.234 c
+59.282 2.213 59.37 2.18 59.451 2.132 c
+59.538 2.08 59.616 2.018 59.686 1.941 c
+59.763 1.86 59.825 1.768 59.877 1.661 c
+59.891 1.661 l
+59.891 1.808 l
+59.899 1.867 59.906 1.919 59.906 1.97 c
+59.914 2.029 59.921 2.077 59.921 2.117 c
+59.928 2.165 59.939 2.198 59.951 2.22 c
+60.803 2.22 l
+60.792 2.139 60.781 2.029 60.774 1.881 c
+60.774 1.411 l
+60.774 -1.161 l
+60.774 -1.414 60.737 -1.635 60.67 -1.822 c
+60.601 -2.006 60.498 -2.16 60.362 -2.278 c
+60.222 -2.403 60.057 -2.499 59.862 -2.557 c
+59.664 -2.624 59.443 -2.66 59.201 -2.66 c
+59.891 0.53 m
+59.891 0.743 59.866 0.919 59.818 1.058 c
+59.777 1.206 59.723 1.324 59.656 1.411 c
+59.598 1.5 59.528 1.559 59.451 1.588 c
+59.37 1.625 59.293 1.646 59.216 1.646 c
+59.116 1.646 59.024 1.621 58.936 1.573 c
+58.855 1.532 58.789 1.463 58.73 1.368 c
+58.679 1.279 58.634 1.162 58.598 1.015 c
+58.569 0.875 58.553 0.706 58.553 0.5 c
+58.553 0.125 58.613 -0.154 58.73 -0.338 c
+58.848 -0.514 59.01 -0.602 59.216 -0.602 c
+59.282 -0.602 59.355 -0.588 59.436 -0.558 c
+59.524 -0.521 59.598 -0.463 59.656 -0.382 c
+59.723 -0.293 59.777 -0.176 59.818 -0.029 c
+59.866 0.118 59.891 0.302 59.891 0.53 c
+63.835 -0.646 m
+64.967 -0.646 l
+64.967 -1.263 l
+61.659 -1.263 l
+61.659 -0.646 l
+62.923 -0.646 l
+62.923 2.897 l
+61.998 2.897 l
+61.998 3.514 l
+63.835 3.514 l
+h
+68.998 0.485 m
+68.998 0.21 68.961 -0.039 68.895 -0.264 c
+68.824 -0.481 68.722 -0.668 68.586 -0.823 c
+68.446 -0.98 68.27 -1.102 68.057 -1.19 c
+67.84 -1.278 67.586 -1.323 67.293 -1.323 c
+67.017 -1.323 66.771 -1.278 66.557 -1.19 c
+66.352 -1.102 66.179 -0.98 66.043 -0.823 c
+65.903 -0.668 65.801 -0.481 65.734 -0.264 c
+65.665 -0.039 65.632 0.21 65.632 0.485 c
+65.632 0.739 65.661 0.975 65.72 1.191 c
+65.786 1.415 65.888 1.607 66.029 1.764 c
+66.164 1.929 66.341 2.058 66.557 2.147 c
+66.771 2.234 67.028 2.278 67.322 2.278 c
+67.634 2.278 67.895 2.234 68.101 2.147 c
+68.314 2.058 68.487 1.929 68.616 1.764 c
+68.751 1.607 68.851 1.415 68.909 1.191 c
+68.968 0.975 68.998 0.739 68.998 0.485 c
+68.042 0.485 m
+68.042 0.691 68.028 0.867 67.999 1.015 c
+67.976 1.162 67.939 1.283 67.881 1.382 c
+67.822 1.478 67.748 1.548 67.66 1.588 c
+67.571 1.636 67.461 1.661 67.336 1.661 c
+67.072 1.661 66.881 1.563 66.763 1.368 c
+66.646 1.18 66.587 0.886 66.587 0.485 c
+66.587 0.063 66.646 -0.242 66.763 -0.426 c
+66.881 -0.613 67.058 -0.706 67.293 -0.706 c
+67.417 -0.706 67.531 -0.687 67.631 -0.646 c
+67.726 -0.598 67.807 -0.525 67.866 -0.426 c
+67.932 -0.33 67.976 -0.205 67.999 -0.058 c
+68.028 0.088 68.042 0.269 68.042 0.485 c
+69.854 -1.263 m
+69.854 -0.97 l
+69.861 -0.833 69.869 -0.675 69.869 -0.5 c
+69.869 3.514 l
+70.78 3.514 l
+70.78 2.234 l
+70.78 2.073 l
+70.78 1.897 l
+70.78 1.845 70.773 1.802 70.765 1.764 c
+70.765 1.676 l
+70.78 1.676 l
+70.827 1.783 70.89 1.875 70.971 1.956 c
+71.048 2.033 71.133 2.095 71.221 2.147 c
+71.309 2.195 71.401 2.228 71.5 2.249 c
+71.596 2.268 71.695 2.278 71.794 2.278 c
+72.007 2.278 72.194 2.234 72.352 2.147 c
+72.507 2.058 72.635 1.929 72.735 1.764 c
+72.841 1.607 72.919 1.415 72.97 1.191 c
+73.017 0.975 73.044 0.736 73.044 0.47 c
+73.044 0.214 73.015 -0.025 72.955 -0.249 c
+72.897 -0.467 72.812 -0.658 72.705 -0.823 c
+72.595 -0.98 72.463 -1.102 72.309 -1.19 c
+72.151 -1.278 71.97 -1.323 71.764 -1.323 c
+71.666 -1.323 71.567 -1.311 71.471 -1.294 c
+71.382 -1.271 71.294 -1.242 71.206 -1.19 c
+71.118 -1.132 71.037 -1.065 70.971 -0.984 c
+70.901 -0.907 70.839 -0.808 70.78 -0.69 c
+70.765 -0.69 l
+70.765 -0.852 l
+70.765 -0.911 70.758 -0.97 70.75 -1.028 c
+70.75 -1.08 70.743 -1.128 70.736 -1.176 c
+70.736 -1.215 70.729 -1.246 70.721 -1.263 c
+h
+70.765 0.5 m
+70.765 0.265 70.783 0.067 70.824 -0.087 c
+70.872 -0.246 70.931 -0.367 71 -0.455 c
+71.066 -0.544 71.14 -0.61 71.221 -0.646 c
+71.298 -0.687 71.375 -0.706 71.456 -0.706 c
+71.662 -0.706 71.816 -0.61 71.926 -0.411 c
+72.044 -0.216 72.103 0.078 72.103 0.47 c
+72.103 0.684 72.08 0.867 72.044 1.015 c
+72.015 1.169 71.97 1.294 71.912 1.382 c
+71.86 1.478 71.794 1.551 71.706 1.603 c
+71.625 1.65 71.537 1.676 71.442 1.676 c
+71.361 1.676 71.284 1.654 71.206 1.617 c
+71.126 1.577 71.052 1.515 70.985 1.426 c
+70.927 1.338 70.872 1.214 70.824 1.058 c
+70.783 0.912 70.765 0.724 70.765 0.5 c
+74.873 -1.323 m
+74.704 -1.323 74.554 -1.3 74.418 -1.263 c
+74.289 -1.215 74.175 -1.146 74.079 -1.058 c
+73.992 -0.97 73.921 -0.863 73.874 -0.735 c
+73.823 -0.598 73.8 -0.448 73.8 -0.278 c
+73.8 -0.073 73.834 0.096 73.904 0.235 c
+73.969 0.383 74.065 0.493 74.183 0.574 c
+74.308 0.662 74.451 0.724 74.609 0.765 c
+74.775 0.802 74.951 0.827 75.138 0.838 c
+75.858 0.853 l
+75.858 1.029 l
+75.858 1.147 75.847 1.25 75.829 1.338 c
+75.807 1.426 75.774 1.492 75.726 1.544 c
+75.685 1.603 75.638 1.64 75.579 1.661 c
+75.521 1.68 75.454 1.691 75.388 1.691 c
+75.318 1.691 75.255 1.68 75.197 1.661 c
+75.145 1.65 75.097 1.625 75.05 1.588 c
+75.01 1.559 74.976 1.507 74.947 1.441 c
+74.925 1.382 74.91 1.301 74.903 1.206 c
+73.962 1.25 l
+73.992 1.397 74.035 1.532 74.095 1.661 c
+74.16 1.786 74.256 1.897 74.374 1.985 c
+74.492 2.08 74.631 2.154 74.8 2.205 c
+74.976 2.253 75.182 2.278 75.417 2.278 c
+75.858 2.278 76.19 2.168 76.417 1.956 c
+76.652 1.75 76.77 1.441 76.77 1.029 c
+76.77 -0.235 l
+76.77 -0.455 l
+76.777 -0.514 76.792 -0.569 76.814 -0.617 c
+76.832 -0.658 76.861 -0.69 76.902 -0.72 c
+76.938 -0.742 76.99 -0.749 77.049 -0.749 c
+77.115 -0.749 77.185 -0.745 77.254 -0.735 c
+77.254 -1.219 l
+77.196 -1.23 77.141 -1.242 77.093 -1.249 c
+77.053 -1.26 77.013 -1.267 76.976 -1.278 c
+76.936 -1.286 76.891 -1.294 76.843 -1.294 c
+76.792 -1.3 76.733 -1.308 76.666 -1.308 c
+76.439 -1.308 76.273 -1.256 76.167 -1.146 c
+76.057 -1.028 75.995 -0.863 75.976 -0.646 c
+75.961 -0.646 l
+75.891 -0.756 75.822 -0.852 75.756 -0.941 c
+75.685 -1.021 75.608 -1.087 75.521 -1.146 c
+75.432 -1.205 75.333 -1.249 75.226 -1.278 c
+75.127 -1.308 75.01 -1.323 74.873 -1.323 c
+75.858 0.353 m
+75.432 0.339 l
+75.333 0.339 75.241 0.331 75.153 0.324 c
+75.072 0.313 75.006 0.287 74.947 0.25 c
+74.889 0.21 74.837 0.151 74.8 0.074 c
+74.76 0.004 74.741 -0.087 74.741 -0.205 c
+74.741 -0.374 74.775 -0.496 74.844 -0.573 c
+74.91 -0.654 75.01 -0.69 75.138 -0.69 c
+75.245 -0.69 75.344 -0.668 75.432 -0.617 c
+75.527 -0.569 75.608 -0.507 75.667 -0.426 c
+75.733 -0.349 75.785 -0.261 75.814 -0.161 c
+75.843 -0.055 75.858 0.059 75.858 0.177 c
+h
+80.08 -0.646 m
+81.212 -0.646 l
+81.212 -1.263 l
+77.905 -1.263 l
+77.905 -0.646 l
+79.17 -0.646 l
+79.17 2.897 l
+78.243 2.897 l
+78.243 3.514 l
+80.08 3.514 l
+h
+86.982 2.22 m
+86.982 0.265 l
+86.982 0.125 86.989 0 87.011 -0.118 c
+87.03 -0.228 87.063 -0.319 87.114 -0.396 c
+87.162 -0.477 87.221 -0.54 87.29 -0.588 c
+87.357 -0.627 87.441 -0.646 87.54 -0.646 c
+87.629 -0.646 87.71 -0.627 87.791 -0.588 c
+87.878 -0.54 87.952 -0.47 88.011 -0.382 c
+88.069 -0.286 88.113 -0.176 88.144 -0.058 c
+88.18 0.067 88.202 0.206 88.202 0.353 c
+88.202 2.22 l
+89.098 2.22 l
+89.098 -0.484 l
+89.098 -0.72 l
+89.106 -0.801 89.113 -0.878 89.113 -0.955 c
+89.113 -1.146 l
+89.121 -1.198 89.128 -1.234 89.128 -1.263 c
+88.275 -1.263 l
+88.265 -1.234 88.254 -1.198 88.246 -1.146 c
+88.246 -0.955 l
+88.246 -0.889 88.239 -0.819 88.231 -0.749 c
+88.231 -0.573 l
+88.217 -0.573 l
+88.099 -0.837 87.945 -1.028 87.761 -1.146 c
+87.585 -1.263 87.382 -1.323 87.159 -1.323 c
+86.953 -1.323 86.779 -1.286 86.644 -1.219 c
+86.504 -1.153 86.394 -1.058 86.305 -0.941 c
+86.225 -0.823 86.166 -0.687 86.129 -0.529 c
+86.1 -0.363 86.085 -0.187 86.085 0 c
+86.085 2.22 l
+h
+93.262 -0.249 m
+93.262 -0.419 93.222 -0.569 93.144 -0.706 c
+93.075 -0.833 92.971 -0.947 92.836 -1.043 c
+92.707 -1.132 92.545 -1.201 92.351 -1.249 c
+92.163 -1.296 91.947 -1.323 91.704 -1.323 c
+91.477 -1.323 91.278 -1.308 91.101 -1.278 c
+90.925 -1.249 90.767 -1.201 90.631 -1.132 c
+90.492 -1.055 90.382 -0.955 90.293 -0.837 c
+90.205 -0.72 90.135 -0.573 90.087 -0.396 c
+90.895 -0.278 l
+90.914 -0.378 90.943 -0.455 90.984 -0.514 c
+91.032 -0.573 91.09 -0.617 91.16 -0.646 c
+91.226 -0.675 91.307 -0.702 91.396 -0.72 c
+91.483 -0.731 91.587 -0.735 91.704 -0.735 c
+91.799 -0.735 91.895 -0.731 91.984 -0.72 c
+92.071 -0.702 92.148 -0.675 92.219 -0.646 c
+92.285 -0.617 92.336 -0.58 92.366 -0.529 c
+92.402 -0.481 92.424 -0.419 92.424 -0.338 c
+92.424 -0.242 92.395 -0.168 92.336 -0.118 c
+92.285 -0.07 92.219 -0.029 92.13 0 c
+92.042 0.038 91.932 0.067 91.807 0.088 c
+91.689 0.118 91.557 0.148 91.41 0.177 c
+91.271 0.214 91.13 0.254 90.984 0.294 c
+90.844 0.342 90.719 0.405 90.602 0.485 c
+90.492 0.563 90.403 0.662 90.337 0.78 c
+90.267 0.897 90.234 1.048 90.234 1.235 c
+90.234 1.389 90.263 1.532 90.322 1.661 c
+90.388 1.798 90.484 1.912 90.602 1.999 c
+90.727 2.087 90.885 2.154 91.072 2.205 c
+91.255 2.253 91.469 2.278 91.704 2.278 c
+91.888 2.278 92.065 2.257 92.233 2.22 c
+92.398 2.191 92.545 2.135 92.674 2.058 c
+92.799 1.989 92.909 1.889 92.998 1.764 c
+93.085 1.646 93.144 1.503 93.174 1.338 c
+92.38 1.264 l
+92.358 1.341 92.329 1.405 92.292 1.455 c
+92.252 1.515 92.204 1.559 92.144 1.588 c
+92.094 1.625 92.031 1.65 91.954 1.661 c
+91.873 1.669 91.793 1.676 91.704 1.676 c
+91.487 1.676 91.325 1.646 91.219 1.588 c
+91.109 1.536 91.057 1.449 91.057 1.324 c
+91.057 1.243 91.076 1.18 91.116 1.133 c
+91.164 1.081 91.226 1.044 91.307 1.015 c
+91.396 0.985 91.491 0.956 91.601 0.927 c
+91.708 0.904 91.829 0.882 91.969 0.853 c
+92.123 0.823 92.281 0.784 92.439 0.736 c
+92.593 0.684 92.733 0.622 92.85 0.545 c
+92.969 0.464 93.064 0.36 93.144 0.235 c
+93.222 0.107 93.262 -0.055 93.262 -0.249 c
+95.783 -1.323 m
+95.525 -1.323 95.298 -1.286 95.092 -1.219 c
+94.887 -1.142 94.71 -1.028 94.563 -0.881 c
+94.416 -0.727 94.299 -0.536 94.21 -0.309 c
+94.129 -0.084 94.093 0.181 94.093 0.485 c
+94.093 0.817 94.137 1.096 94.224 1.324 c
+94.32 1.559 94.449 1.742 94.607 1.881 c
+94.772 2.018 94.96 2.117 95.165 2.176 c
+95.371 2.242 95.581 2.278 95.797 2.278 c
+96.069 2.278 96.304 2.228 96.503 2.132 c
+96.709 2.043 96.875 1.912 97.003 1.735 c
+97.139 1.566 97.239 1.36 97.297 1.118 c
+97.363 0.882 97.399 0.618 97.399 0.324 c
+97.399 0.31 l
+95.033 0.31 l
+95.033 0.162 95.048 0.023 95.078 -0.103 c
+95.114 -0.231 95.169 -0.345 95.24 -0.44 c
+95.305 -0.529 95.39 -0.598 95.489 -0.646 c
+95.585 -0.698 95.699 -0.72 95.827 -0.72 c
+95.982 -0.72 96.121 -0.687 96.239 -0.617 c
+96.364 -0.55 96.452 -0.448 96.503 -0.309 c
+97.341 -0.382 l
+97.312 -0.481 97.256 -0.588 97.179 -0.706 c
+97.098 -0.816 96.996 -0.918 96.871 -1.014 c
+96.753 -1.102 96.599 -1.176 96.415 -1.234 c
+96.239 -1.294 96.026 -1.323 95.783 -1.323 c
+95.783 1.706 m
+95.695 1.706 95.606 1.691 95.518 1.661 c
+95.43 1.632 95.35 1.58 95.283 1.515 c
+95.213 1.445 95.155 1.357 95.107 1.25 c
+95.066 1.139 95.048 1.015 95.048 0.867 c
+96.518 0.867 l
+96.518 1.004 96.493 1.125 96.445 1.235 c
+96.404 1.341 96.349 1.43 96.283 1.5 c
+96.224 1.566 96.15 1.617 96.063 1.646 c
+95.974 1.684 95.878 1.706 95.783 1.706 c
+101.299 1.47 m
+101.2 1.478 101.097 1.489 100.99 1.5 c
+100.88 1.518 100.759 1.529 100.622 1.529 c
+100.446 1.529 100.288 1.489 100.152 1.411 c
+100.013 1.341 99.895 1.243 99.799 1.118 c
+99.711 0.989 99.641 0.842 99.593 0.676 c
+99.554 0.508 99.535 0.331 99.535 0.148 c
+99.535 -1.263 l
+98.638 -1.263 l
+98.638 0.985 l
+98.638 1.11 98.627 1.235 98.609 1.353 c
+98.598 1.478 98.583 1.596 98.565 1.706 c
+98.554 1.823 98.539 1.919 98.521 1.999 c
+98.498 2.087 98.48 2.161 98.462 2.22 c
+99.344 2.22 l
+99.351 2.168 99.362 2.117 99.373 2.058 c
+99.392 1.999 99.406 1.933 99.417 1.867 c
+99.435 1.808 99.45 1.742 99.462 1.676 c
+99.469 1.607 99.479 1.544 99.491 1.485 c
+99.506 1.485 l
+99.542 1.603 99.593 1.709 99.653 1.808 c
+99.718 1.904 99.799 1.989 99.888 2.058 c
+99.976 2.124 100.079 2.18 100.196 2.22 c
+100.321 2.257 100.468 2.278 100.637 2.278 c
+100.763 2.278 100.88 2.272 100.99 2.264 c
+101.108 2.253 101.21 2.238 101.299 2.22 c
+h
+103.404 -0.278 0.927 -0.985 re
+103.404 -1.263 m
+107.965 -1.323 m
+107.707 -1.323 107.479 -1.286 107.274 -1.219 c
+107.068 -1.142 106.892 -1.028 106.745 -0.881 c
+106.598 -0.727 106.48 -0.536 106.392 -0.309 c
+106.311 -0.084 106.274 0.181 106.274 0.485 c
+106.274 0.817 106.319 1.096 106.407 1.324 c
+106.502 1.559 106.631 1.742 106.789 1.881 c
+106.955 2.018 107.142 2.117 107.348 2.176 c
+107.553 2.242 107.763 2.278 107.98 2.278 c
+108.252 2.278 108.487 2.228 108.686 2.132 c
+108.891 2.043 109.056 1.912 109.185 1.735 c
+109.32 1.566 109.42 1.36 109.479 1.118 c
+109.545 0.882 109.582 0.618 109.582 0.324 c
+109.582 0.31 l
+107.215 0.31 l
+107.215 0.162 107.23 0.023 107.259 -0.103 c
+107.296 -0.231 107.352 -0.345 107.421 -0.44 c
+107.487 -0.529 107.572 -0.598 107.67 -0.646 c
+107.766 -0.698 107.88 -0.72 108.009 -0.72 c
+108.163 -0.72 108.303 -0.687 108.42 -0.617 c
+108.545 -0.55 108.634 -0.448 108.686 -0.309 c
+109.523 -0.382 l
+109.494 -0.481 109.438 -0.588 109.361 -0.706 c
+109.28 -0.816 109.178 -0.918 109.052 -1.014 c
+108.935 -1.102 108.781 -1.176 108.597 -1.234 c
+108.42 -1.294 108.208 -1.323 107.965 -1.323 c
+107.965 1.706 m
+107.876 1.706 107.788 1.691 107.701 1.661 c
+107.612 1.632 107.531 1.58 107.465 1.515 c
+107.395 1.445 107.336 1.357 107.288 1.25 c
+107.248 1.139 107.23 1.015 107.23 0.867 c
+108.7 0.867 l
+108.7 1.004 108.674 1.125 108.626 1.235 c
+108.586 1.341 108.53 1.43 108.464 1.5 c
+108.406 1.566 108.333 1.617 108.244 1.646 c
+108.156 1.684 108.06 1.706 107.965 1.706 c
+111.628 -1.263 m
+111.628 0.853 l
+111.628 1.019 111.622 1.154 111.614 1.264 c
+111.603 1.372 111.585 1.455 111.555 1.515 c
+111.533 1.58 111.504 1.632 111.467 1.661 c
+111.437 1.691 111.397 1.706 111.35 1.706 c
+111.29 1.706 111.235 1.676 111.188 1.617 c
+111.147 1.566 111.114 1.492 111.084 1.397 c
+111.055 1.309 111.03 1.195 111.011 1.058 c
+111 0.919 110.997 0.769 110.997 0.603 c
+110.997 -1.263 l
+110.247 -1.263 l
+110.247 1.47 l
+110.247 1.706 l
+110.247 1.926 l
+110.247 2.003 110.24 2.066 110.232 2.117 c
+110.232 2.22 l
+110.908 2.22 l
+110.908 2.132 l
+110.908 1.985 l
+110.916 1.926 110.923 1.867 110.923 1.808 c
+110.923 1.646 l
+110.938 1.646 l
+110.956 1.735 110.986 1.812 111.026 1.881 c
+111.063 1.96 111.107 2.029 111.158 2.087 c
+111.217 2.147 111.283 2.191 111.364 2.22 c
+111.441 2.257 111.529 2.278 111.628 2.278 c
+111.813 2.278 111.952 2.224 112.04 2.117 c
+112.135 2.018 112.206 1.86 112.246 1.646 c
+112.26 1.646 l
+112.297 1.742 112.337 1.831 112.378 1.912 c
+112.426 1.989 112.481 2.051 112.54 2.103 c
+112.599 2.161 112.665 2.205 112.746 2.234 c
+112.823 2.264 112.911 2.278 113.01 2.278 c
+113.146 2.278 113.26 2.253 113.349 2.205 c
+113.436 2.154 113.503 2.08 113.554 1.985 c
+113.613 1.885 113.65 1.757 113.671 1.603 c
+113.702 1.455 113.716 1.272 113.716 1.058 c
+113.716 -1.263 l
+112.996 -1.263 l
+112.996 0.853 l
+112.996 1.019 112.988 1.154 112.981 1.264 c
+112.97 1.372 112.952 1.455 112.922 1.515 c
+112.9 1.58 112.871 1.632 112.834 1.661 c
+112.804 1.691 112.764 1.706 112.717 1.706 c
+112.599 1.706 112.503 1.617 112.437 1.441 c
+112.378 1.272 112.349 1.015 112.349 0.662 c
+112.349 -1.263 l
+h
+115.487 -1.323 m
+115.318 -1.323 115.167 -1.3 115.032 -1.263 c
+114.903 -1.215 114.789 -1.146 114.693 -1.058 c
+114.606 -0.97 114.535 -0.863 114.487 -0.735 c
+114.436 -0.598 114.414 -0.448 114.414 -0.278 c
+114.414 -0.073 114.448 0.096 114.517 0.235 c
+114.583 0.383 114.679 0.493 114.797 0.574 c
+114.921 0.662 115.065 0.724 115.223 0.765 c
+115.388 0.802 115.564 0.827 115.751 0.838 c
+116.472 0.853 l
+116.472 1.029 l
+116.472 1.147 116.461 1.25 116.443 1.338 c
+116.42 1.426 116.387 1.492 116.339 1.544 c
+116.299 1.603 116.252 1.64 116.193 1.661 c
+116.133 1.68 116.067 1.691 116.002 1.691 c
+115.932 1.691 115.869 1.68 115.811 1.661 c
+115.759 1.65 115.711 1.625 115.663 1.588 c
+115.623 1.559 115.59 1.507 115.56 1.441 c
+115.539 1.382 115.524 1.301 115.516 1.206 c
+114.575 1.25 l
+114.606 1.397 114.649 1.532 114.708 1.661 c
+114.774 1.786 114.87 1.897 114.988 1.985 c
+115.105 2.08 115.244 2.154 115.414 2.205 c
+115.59 2.253 115.796 2.278 116.031 2.278 c
+116.472 2.278 116.802 2.168 117.031 1.956 c
+117.266 1.75 117.384 1.441 117.384 1.029 c
+117.384 -0.235 l
+117.384 -0.455 l
+117.39 -0.514 117.405 -0.569 117.428 -0.617 c
+117.446 -0.658 117.475 -0.69 117.515 -0.72 c
+117.552 -0.742 117.604 -0.749 117.663 -0.749 c
+117.729 -0.749 117.798 -0.745 117.868 -0.735 c
+117.868 -1.219 l
+117.81 -1.23 117.754 -1.242 117.706 -1.249 c
+117.666 -1.26 117.625 -1.267 117.589 -1.278 c
+117.548 -1.286 117.505 -1.294 117.457 -1.294 c
+117.405 -1.3 117.347 -1.308 117.28 -1.308 c
+117.052 -1.308 116.887 -1.256 116.781 -1.146 c
+116.67 -1.028 116.607 -0.863 116.59 -0.646 c
+116.575 -0.646 l
+116.505 -0.756 116.435 -0.852 116.369 -0.941 c
+116.299 -1.021 116.222 -1.087 116.133 -1.146 c
+116.046 -1.205 115.946 -1.249 115.84 -1.278 c
+115.741 -1.308 115.623 -1.323 115.487 -1.323 c
+116.472 0.353 m
+116.046 0.339 l
+115.946 0.339 115.855 0.331 115.766 0.324 c
+115.686 0.313 115.62 0.287 115.56 0.25 c
+115.502 0.21 115.45 0.151 115.414 0.074 c
+115.373 0.004 115.354 -0.087 115.354 -0.205 c
+115.354 -0.374 115.388 -0.496 115.458 -0.573 c
+115.524 -0.654 115.623 -0.69 115.751 -0.69 c
+115.859 -0.69 115.957 -0.668 116.046 -0.617 c
+116.141 -0.569 116.222 -0.507 116.281 -0.426 c
+116.347 -0.349 116.399 -0.261 116.428 -0.161 c
+116.457 -0.055 116.472 0.059 116.472 0.177 c
+h
+120.694 -0.646 m
+121.826 -0.646 l
+121.826 -1.263 l
+118.519 -1.263 l
+118.519 -0.646 l
+119.782 -0.646 l
+119.782 1.603 l
+118.857 1.603 l
+118.857 2.22 l
+120.694 2.22 l
+h
+119.782 3.514 0.912 -0.676 re
+119.782 2.837 m
+124.755 -0.646 m
+125.887 -0.646 l
+125.887 -1.263 l
+122.579 -1.263 l
+122.579 -0.646 l
+123.844 -0.646 l
+123.844 2.897 l
+122.917 2.897 l
+122.917 3.514 l
+124.755 3.514 l
+h
+133.346 1.455 m
+132.715 1.455 l
+132.582 3.514 l
+133.479 3.514 l
+h
+131.877 1.455 m
+131.244 1.455 l
+131.127 3.514 l
+131.994 3.514 l
+h
+136.764 -1.44 m
+136.683 -1.646 136.602 -1.822 136.514 -1.969 c
+136.434 -2.123 136.341 -2.248 136.235 -2.337 c
+136.124 -2.432 136.007 -2.506 135.882 -2.557 c
+135.753 -2.605 135.603 -2.63 135.426 -2.63 c
+135.327 -2.63 135.224 -2.624 135.117 -2.616 c
+135.019 -2.616 134.926 -2.605 134.838 -2.587 c
+134.838 -1.94 l
+134.857 -1.947 134.882 -1.955 134.911 -1.955 c
+135 -1.955 l
+135.037 -1.961 135.067 -1.969 135.088 -1.969 c
+135.177 -1.969 l
+135.264 -1.969 135.341 -1.959 135.412 -1.94 c
+135.478 -1.918 135.537 -1.884 135.588 -1.837 c
+135.647 -1.797 135.698 -1.738 135.75 -1.66 c
+135.798 -1.591 135.842 -1.499 135.882 -1.381 c
+135.941 -1.234 l
+134.5 2.22 l
+135.456 2.22 l
+136.073 0.515 l
+136.081 0.493 136.091 0.456 136.102 0.397 c
+136.121 0.346 136.139 0.283 136.162 0.206 c
+136.191 0.136 136.212 0.063 136.235 -0.014 c
+136.264 -0.095 136.286 -0.172 136.308 -0.249 c
+136.326 -0.319 136.341 -0.382 136.353 -0.44 c
+136.371 -0.492 136.386 -0.529 136.397 -0.558 c
+136.397 -0.529 136.4 -0.492 136.411 -0.44 c
+136.43 -0.382 136.448 -0.319 136.47 -0.249 c
+136.488 -0.183 136.507 -0.11 136.529 -0.029 c
+136.558 0.048 136.577 0.121 136.588 0.192 c
+136.606 0.258 136.625 0.324 136.646 0.383 c
+136.665 0.441 136.679 0.485 136.69 0.515 c
+137.263 2.22 l
+138.204 2.22 l
+h
+142.103 0.485 m
+142.103 0.21 142.067 -0.039 142.001 -0.264 c
+141.93 -0.481 141.828 -0.668 141.691 -0.823 c
+141.552 -0.98 141.376 -1.102 141.163 -1.19 c
+140.946 -1.278 140.692 -1.323 140.398 -1.323 c
+140.122 -1.323 139.877 -1.278 139.663 -1.19 c
+139.457 -1.102 139.285 -0.98 139.149 -0.823 c
+139.009 -0.668 138.907 -0.481 138.84 -0.264 c
+138.77 -0.039 138.737 0.21 138.737 0.485 c
+138.737 0.739 138.767 0.975 138.826 1.191 c
+138.892 1.415 138.994 1.607 139.134 1.764 c
+139.27 1.929 139.447 2.058 139.663 2.147 c
+139.877 2.234 140.133 2.278 140.428 2.278 c
+140.74 2.278 141.001 2.234 141.207 2.147 c
+141.419 2.058 141.593 1.929 141.721 1.764 c
+141.857 1.607 141.957 1.415 142.015 1.191 c
+142.074 0.975 142.103 0.739 142.103 0.485 c
+141.148 0.485 m
+141.148 0.691 141.133 0.867 141.103 1.015 c
+141.082 1.162 141.045 1.283 140.986 1.382 c
+140.927 1.478 140.854 1.548 140.766 1.588 c
+140.677 1.636 140.567 1.661 140.442 1.661 c
+140.178 1.661 139.987 1.563 139.869 1.368 c
+139.752 1.18 139.692 0.886 139.692 0.485 c
+139.692 0.063 139.752 -0.242 139.869 -0.426 c
+139.987 -0.613 140.163 -0.706 140.398 -0.706 c
+140.523 -0.706 140.637 -0.687 140.736 -0.646 c
+140.831 -0.598 140.912 -0.525 140.972 -0.426 c
+141.038 -0.33 141.082 -0.205 141.103 -0.058 c
+141.133 0.088 141.148 0.269 141.148 0.485 c
+143.842 2.22 m
+143.842 0.265 l
+143.842 0.125 143.848 0 143.871 -0.118 c
+143.889 -0.228 143.923 -0.319 143.974 -0.396 c
+144.022 -0.477 144.081 -0.54 144.15 -0.588 c
+144.216 -0.627 144.301 -0.646 144.4 -0.646 c
+144.488 -0.646 144.569 -0.627 144.65 -0.588 c
+144.738 -0.54 144.812 -0.47 144.87 -0.382 c
+144.929 -0.286 144.974 -0.176 145.003 -0.058 c
+145.039 0.067 145.061 0.206 145.061 0.353 c
+145.061 2.22 l
+145.959 2.22 l
+145.959 -0.484 l
+145.959 -0.72 l
+145.965 -0.801 145.973 -0.878 145.973 -0.955 c
+145.973 -1.146 l
+145.98 -1.198 145.988 -1.234 145.988 -1.263 c
+145.135 -1.263 l
+145.124 -1.234 145.113 -1.198 145.105 -1.146 c
+145.105 -0.955 l
+145.105 -0.889 145.099 -0.819 145.091 -0.749 c
+145.091 -0.573 l
+145.076 -0.573 l
+144.959 -0.837 144.804 -1.028 144.621 -1.146 c
+144.444 -1.263 144.242 -1.323 144.018 -1.323 c
+143.812 -1.323 143.64 -1.286 143.503 -1.219 c
+143.364 -1.153 143.254 -1.058 143.165 -0.941 c
+143.085 -0.823 143.025 -0.687 142.989 -0.529 c
+142.959 -0.363 142.944 -0.187 142.944 0 c
+142.944 2.22 l
+h
+150.387 1.162 m
+150.387 0.857 150.36 0.574 150.313 0.31 c
+150.273 0.052 150.21 -0.168 150.121 -0.353 c
+150.04 -0.529 149.938 -0.672 149.813 -0.779 c
+149.695 -0.878 149.556 -0.926 149.402 -0.926 c
+149.342 -0.926 149.284 -0.922 149.225 -0.911 c
+149.166 -0.893 149.115 -0.863 149.078 -0.823 c
+149.038 -0.786 149.005 -0.735 148.975 -0.675 c
+148.953 -0.61 148.945 -0.525 148.945 -0.426 c
+148.945 -0.396 l
+148.945 -0.353 l
+148.945 -0.309 l
+148.953 -0.29 148.96 -0.275 148.96 -0.264 c
+148.931 -0.264 l
+148.902 -0.353 148.862 -0.436 148.814 -0.514 c
+148.762 -0.595 148.704 -0.668 148.637 -0.735 c
+148.578 -0.793 148.509 -0.845 148.432 -0.881 c
+148.351 -0.911 148.27 -0.926 148.181 -0.926 c
+148.041 -0.926 147.924 -0.893 147.829 -0.823 c
+147.74 -0.756 147.667 -0.668 147.609 -0.558 c
+147.557 -0.44 147.52 -0.309 147.491 -0.161 c
+147.468 -0.007 147.461 0.151 147.461 0.31 c
+147.461 0.474 147.468 0.636 147.491 0.794 c
+147.52 0.948 147.553 1.103 147.594 1.25 c
+147.642 1.397 147.7 1.532 147.769 1.661 c
+147.836 1.786 147.917 1.897 148.005 1.985 c
+148.093 2.073 148.189 2.139 148.299 2.191 c
+148.405 2.249 148.519 2.278 148.637 2.278 c
+148.714 2.278 148.785 2.264 148.843 2.234 c
+148.902 2.205 148.949 2.165 148.99 2.117 c
+149.038 2.066 149.07 2.007 149.093 1.941 c
+149.122 1.871 149.144 1.798 149.166 1.721 c
+149.181 1.721 l
+149.284 2.205 l
+149.725 2.205 l
+149.402 0.574 l
+149.379 0.485 149.361 0.397 149.342 0.31 c
+149.332 0.221 149.321 0.136 149.313 0.059 c
+149.302 -0.022 149.292 -0.095 149.284 -0.161 c
+149.273 -0.22 149.269 -0.275 149.269 -0.323 c
+149.269 -0.374 149.273 -0.411 149.284 -0.44 c
+149.292 -0.47 149.302 -0.496 149.313 -0.514 c
+149.332 -0.525 149.35 -0.536 149.372 -0.544 c
+149.431 -0.544 l
+149.508 -0.544 149.581 -0.507 149.651 -0.426 c
+149.718 -0.338 149.772 -0.22 149.813 -0.073 c
+149.861 0.074 149.894 0.25 149.916 0.456 c
+149.945 0.669 149.96 0.904 149.96 1.162 c
+149.96 1.426 149.93 1.676 149.872 1.912 c
+149.82 2.154 149.743 2.367 149.637 2.544 c
+149.537 2.72 149.409 2.856 149.255 2.955 c
+149.097 3.051 148.92 3.102 148.725 3.102 c
+148.549 3.102 148.388 3.065 148.24 2.999 c
+148.101 2.94 147.975 2.852 147.858 2.735 c
+147.74 2.617 147.638 2.473 147.549 2.309 c
+147.461 2.139 147.388 1.956 147.329 1.75 c
+147.27 1.551 147.218 1.338 147.181 1.103 c
+147.152 0.875 147.138 0.636 147.138 0.383 c
+147.138 0.067 147.167 -0.22 147.226 -0.484 c
+147.293 -0.749 147.388 -0.98 147.505 -1.176 c
+147.623 -1.359 147.763 -1.502 147.931 -1.602 c
+148.097 -1.708 148.288 -1.764 148.505 -1.764 c
+148.629 -1.764 148.747 -1.749 148.858 -1.72 c
+148.964 -1.69 149.067 -1.649 149.166 -1.602 c
+149.261 -1.562 149.357 -1.514 149.446 -1.454 c
+149.533 -1.396 149.614 -1.326 149.695 -1.249 c
+149.916 -1.572 l
+149.828 -1.649 149.728 -1.723 149.622 -1.793 c
+149.523 -1.87 149.409 -1.932 149.284 -1.984 c
+149.166 -2.042 149.038 -2.087 148.902 -2.117 c
+148.773 -2.153 148.629 -2.175 148.475 -2.175 c
+148.199 -2.175 147.954 -2.109 147.74 -1.984 c
+147.524 -1.866 147.336 -1.693 147.181 -1.469 c
+147.023 -1.256 146.906 -0.992 146.829 -0.675 c
+146.748 -0.353 146.711 0 146.711 0.383 c
+146.711 0.831 146.755 1.25 146.844 1.632 c
+146.932 2.014 147.057 2.342 147.226 2.617 c
+147.403 2.9 147.611 3.12 147.858 3.279 c
+148.112 3.433 148.402 3.514 148.725 3.514 c
+149.008 3.514 149.255 3.448 149.46 3.323 c
+149.666 3.194 149.835 3.021 149.975 2.808 c
+150.111 2.591 150.214 2.342 150.283 2.058 c
+150.35 1.771 150.387 1.474 150.387 1.162 c
+148.99 1.264 m
+148.99 1.478 148.96 1.636 148.902 1.735 c
+148.843 1.831 148.754 1.881 148.637 1.881 c
+148.527 1.881 148.432 1.831 148.343 1.735 c
+148.262 1.636 148.197 1.515 148.137 1.368 c
+148.079 1.22 148.035 1.052 148.005 0.867 c
+147.975 0.68 147.961 0.493 147.961 0.31 c
+147.961 0 l
+147.968 -0.099 147.983 -0.187 148.005 -0.264 c
+148.035 -0.345 148.068 -0.407 148.108 -0.455 c
+148.145 -0.507 148.197 -0.529 148.255 -0.529 c
+148.332 -0.529 148.405 -0.507 148.475 -0.455 c
+148.542 -0.396 148.608 -0.319 148.667 -0.22 c
+148.725 -0.114 148.773 0.015 148.814 0.162 c
+148.862 0.31 148.895 0.47 148.916 0.647 c
+148.924 0.684 148.935 0.736 148.945 0.794 c
+148.953 0.853 148.96 0.912 148.96 0.971 c
+148.968 1.029 148.975 1.081 148.975 1.133 c
+148.982 1.18 148.99 1.224 148.99 1.264 c
+152.639 -1.323 m
+152.382 -1.323 152.154 -1.286 151.948 -1.219 c
+151.742 -1.142 151.566 -1.028 151.419 -0.881 c
+151.272 -0.727 151.154 -0.536 151.066 -0.309 c
+150.985 -0.084 150.948 0.181 150.948 0.485 c
+150.948 0.817 150.992 1.096 151.081 1.324 c
+151.176 1.559 151.305 1.742 151.463 1.881 c
+151.628 2.018 151.816 2.117 152.022 2.176 c
+152.228 2.242 152.436 2.278 152.654 2.278 c
+152.926 2.278 153.161 2.228 153.359 2.132 c
+153.565 2.043 153.73 1.912 153.859 1.735 c
+153.995 1.566 154.094 1.36 154.153 1.118 c
+154.219 0.882 154.256 0.618 154.256 0.324 c
+154.256 0.31 l
+151.889 0.31 l
+151.889 0.162 151.904 0.023 151.933 -0.103 c
+151.97 -0.231 152.025 -0.345 152.095 -0.44 c
+152.161 -0.529 152.245 -0.598 152.345 -0.646 c
+152.44 -0.698 152.554 -0.72 152.683 -0.72 c
+152.837 -0.72 152.977 -0.687 153.095 -0.617 c
+153.219 -0.55 153.308 -0.448 153.359 -0.309 c
+154.197 -0.382 l
+154.167 -0.481 154.113 -0.588 154.036 -0.706 c
+153.955 -0.816 153.851 -0.918 153.726 -1.014 c
+153.609 -1.102 153.454 -1.176 153.271 -1.234 c
+153.095 -1.294 152.881 -1.323 152.639 -1.323 c
+152.639 1.706 m
+152.55 1.706 152.463 1.691 152.374 1.661 c
+152.286 1.632 152.205 1.58 152.139 1.515 c
+152.07 1.445 152.01 1.357 151.962 1.25 c
+151.922 1.139 151.904 1.015 151.904 0.867 c
+153.373 0.867 l
+153.373 1.004 153.348 1.125 153.3 1.235 c
+153.26 1.341 153.205 1.43 153.138 1.5 c
+153.08 1.566 153.007 1.617 152.918 1.646 c
+152.83 1.684 152.735 1.706 152.639 1.706 c
+157.497 -1.263 m
+156.659 0 l
+155.806 -1.263 l
+154.851 -1.263 l
+156.159 0.53 l
+154.91 2.22 l
+155.88 2.22 l
+156.659 1.073 l
+157.423 2.22 l
+158.408 2.22 l
+157.159 0.545 l
+158.482 -1.263 l
+h
+160.161 -1.323 m
+159.992 -1.323 159.841 -1.3 159.706 -1.263 c
+159.577 -1.215 159.463 -1.146 159.368 -1.058 c
+159.279 -0.97 159.21 -0.863 159.162 -0.735 c
+159.11 -0.598 159.088 -0.448 159.088 -0.278 c
+159.088 -0.073 159.121 0.096 159.191 0.235 c
+159.257 0.383 159.353 0.493 159.47 0.574 c
+159.595 0.662 159.738 0.724 159.897 0.765 c
+160.062 0.802 160.238 0.827 160.426 0.838 c
+161.146 0.853 l
+161.146 1.029 l
+161.146 1.147 161.135 1.25 161.117 1.338 c
+161.094 1.426 161.061 1.492 161.014 1.544 c
+160.974 1.603 160.926 1.64 160.866 1.661 c
+160.808 1.68 160.741 1.691 160.675 1.691 c
+160.606 1.691 160.544 1.68 160.484 1.661 c
+160.433 1.65 160.386 1.625 160.338 1.588 c
+160.297 1.559 160.264 1.507 160.234 1.441 c
+160.213 1.382 160.198 1.301 160.191 1.206 c
+159.25 1.25 l
+159.279 1.397 159.324 1.532 159.382 1.661 c
+159.448 1.786 159.544 1.897 159.661 1.985 c
+159.779 2.08 159.918 2.154 160.087 2.205 c
+160.264 2.253 160.469 2.278 160.705 2.278 c
+161.146 2.278 161.477 2.168 161.705 1.956 c
+161.94 1.75 162.057 1.441 162.057 1.029 c
+162.057 -0.235 l
+162.057 -0.455 l
+162.065 -0.514 162.079 -0.569 162.102 -0.617 c
+162.119 -0.658 162.15 -0.69 162.19 -0.72 c
+162.227 -0.742 162.278 -0.749 162.337 -0.749 c
+162.403 -0.749 162.472 -0.745 162.543 -0.735 c
+162.543 -1.219 l
+162.484 -1.23 162.428 -1.242 162.381 -1.249 c
+162.341 -1.26 162.3 -1.267 162.263 -1.278 c
+162.223 -1.286 162.179 -1.294 162.131 -1.294 c
+162.079 -1.3 162.021 -1.308 161.955 -1.308 c
+161.726 -1.308 161.562 -1.256 161.454 -1.146 c
+161.344 -1.028 161.282 -0.863 161.263 -0.646 c
+161.249 -0.646 l
+161.179 -0.756 161.109 -0.852 161.043 -0.941 c
+160.974 -1.021 160.897 -1.087 160.808 -1.146 c
+160.72 -1.205 160.621 -1.249 160.514 -1.278 c
+160.415 -1.308 160.297 -1.323 160.161 -1.323 c
+161.146 0.353 m
+160.72 0.339 l
+160.621 0.339 160.529 0.331 160.44 0.324 c
+160.359 0.313 160.294 0.287 160.234 0.25 c
+160.176 0.21 160.124 0.151 160.087 0.074 c
+160.047 0.004 160.029 -0.087 160.029 -0.205 c
+160.029 -0.374 160.062 -0.496 160.132 -0.573 c
+160.198 -0.654 160.297 -0.69 160.426 -0.69 c
+160.532 -0.69 160.631 -0.668 160.72 -0.617 c
+160.816 -0.569 160.897 -0.507 160.955 -0.426 c
+161.021 -0.349 161.072 -0.261 161.102 -0.161 c
+161.132 -0.055 161.146 0.059 161.146 0.177 c
+h
+164.427 -1.263 m
+164.427 0.853 l
+164.427 1.019 164.421 1.154 164.413 1.264 c
+164.402 1.372 164.384 1.455 164.354 1.515 c
+164.332 1.58 164.303 1.632 164.266 1.661 c
+164.236 1.691 164.196 1.706 164.149 1.706 c
+164.089 1.706 164.034 1.676 163.987 1.617 c
+163.946 1.566 163.913 1.492 163.883 1.397 c
+163.854 1.309 163.829 1.195 163.81 1.058 c
+163.799 0.919 163.796 0.769 163.796 0.603 c
+163.796 -1.263 l
+163.046 -1.263 l
+163.046 1.47 l
+163.046 1.706 l
+163.046 1.926 l
+163.046 2.003 163.039 2.066 163.031 2.117 c
+163.031 2.22 l
+163.707 2.22 l
+163.707 2.132 l
+163.707 1.985 l
+163.715 1.926 163.722 1.867 163.722 1.808 c
+163.722 1.646 l
+163.737 1.646 l
+163.755 1.735 163.785 1.812 163.825 1.881 c
+163.862 1.96 163.906 2.029 163.957 2.087 c
+164.016 2.147 164.082 2.191 164.163 2.22 c
+164.24 2.257 164.328 2.278 164.427 2.278 c
+164.612 2.278 164.751 2.224 164.839 2.117 c
+164.935 2.018 165.005 1.86 165.045 1.646 c
+165.059 1.646 l
+165.096 1.742 165.136 1.831 165.177 1.912 c
+165.225 1.989 165.28 2.051 165.339 2.103 c
+165.398 2.161 165.464 2.205 165.545 2.234 c
+165.622 2.264 165.71 2.278 165.809 2.278 c
+165.945 2.278 166.059 2.253 166.148 2.205 c
+166.235 2.154 166.302 2.08 166.353 1.985 c
+166.412 1.885 166.449 1.757 166.47 1.603 c
+166.501 1.455 166.515 1.272 166.515 1.058 c
+166.515 -1.263 l
+165.795 -1.263 l
+165.795 0.853 l
+165.795 1.019 165.787 1.154 165.78 1.264 c
+165.769 1.372 165.751 1.455 165.721 1.515 c
+165.699 1.58 165.67 1.632 165.633 1.661 c
+165.603 1.691 165.563 1.706 165.516 1.706 c
+165.398 1.706 165.302 1.617 165.236 1.441 c
+165.177 1.272 165.148 1.015 165.148 0.662 c
+165.148 -1.263 l
+h
+168.194 2.22 m
+168.201 2.198 168.209 2.165 168.209 2.117 c
+168.216 2.077 168.224 2.029 168.224 1.97 c
+168.231 1.919 168.238 1.867 168.238 1.808 c
+168.238 1.646 l
+168.253 1.646 l
+168.311 1.764 168.378 1.86 168.459 1.941 c
+168.536 2.018 168.62 2.08 168.708 2.132 c
+168.797 2.191 168.885 2.228 168.973 2.249 c
+169.069 2.268 169.168 2.278 169.267 2.278 c
+169.473 2.278 169.653 2.234 169.811 2.147 c
+169.965 2.058 170.094 1.929 170.193 1.764 c
+170.3 1.607 170.377 1.415 170.428 1.191 c
+170.487 0.975 170.517 0.739 170.517 0.485 c
+170.517 0.221 170.487 -0.025 170.428 -0.249 c
+170.377 -0.467 170.3 -0.658 170.193 -0.823 c
+170.094 -0.98 169.962 -1.102 169.796 -1.19 c
+169.639 -1.278 169.451 -1.323 169.238 -1.323 c
+169.138 -1.323 169.04 -1.311 168.944 -1.294 c
+168.845 -1.271 168.753 -1.242 168.664 -1.19 c
+168.583 -1.142 168.506 -1.08 168.429 -0.999 c
+168.359 -0.922 168.301 -0.83 168.253 -0.72 c
+168.238 -0.72 l
+168.238 -0.808 l
+168.246 -0.849 168.253 -0.897 168.253 -0.955 c
+168.253 -1.117 l
+168.253 -1.294 l
+168.253 -2.63 l
+167.342 -2.63 l
+167.342 1.455 l
+167.342 1.621 167.334 1.768 167.326 1.897 c
+167.326 2.22 l
+h
+168.238 0.456 m
+168.238 0.229 168.257 0.038 168.297 -0.118 c
+168.344 -0.264 168.4 -0.382 168.459 -0.47 c
+168.525 -0.558 168.598 -0.625 168.679 -0.661 c
+168.756 -0.702 168.834 -0.72 168.914 -0.72 c
+169.01 -0.72 169.098 -0.698 169.179 -0.646 c
+169.267 -0.598 169.333 -0.529 169.385 -0.44 c
+169.443 -0.345 169.487 -0.22 169.517 -0.073 c
+169.554 0.081 169.576 0.269 169.576 0.485 c
+169.576 0.875 169.517 1.169 169.4 1.368 c
+169.289 1.563 169.135 1.661 168.93 1.661 c
+168.849 1.661 168.771 1.64 168.694 1.603 c
+168.613 1.563 168.54 1.5 168.473 1.411 c
+168.404 1.324 168.344 1.199 168.297 1.044 c
+168.257 0.886 168.238 0.691 168.238 0.456 c
+173.489 -0.646 m
+174.621 -0.646 l
+174.621 -1.263 l
+171.314 -1.263 l
+171.314 -0.646 l
+172.578 -0.646 l
+172.578 2.897 l
+171.652 2.897 l
+171.652 3.514 l
+173.489 3.514 l
+h
+177.01 -1.323 m
+176.753 -1.323 176.525 -1.286 176.319 -1.219 c
+176.113 -1.142 175.937 -1.028 175.79 -0.881 c
+175.643 -0.727 175.525 -0.536 175.438 -0.309 c
+175.357 -0.084 175.32 0.181 175.32 0.485 c
+175.32 0.817 175.363 1.096 175.452 1.324 c
+175.548 1.559 175.676 1.742 175.834 1.881 c
+175.999 2.018 176.186 2.117 176.392 2.176 c
+176.598 2.242 176.808 2.278 177.025 2.278 c
+177.296 2.278 177.532 2.228 177.73 2.132 c
+177.936 2.043 178.102 1.912 178.23 1.735 c
+178.366 1.566 178.465 1.36 178.524 1.118 c
+178.59 0.882 178.627 0.618 178.627 0.324 c
+178.627 0.31 l
+176.261 0.31 l
+176.261 0.162 176.275 0.023 176.304 -0.103 c
+176.341 -0.231 176.396 -0.345 176.466 -0.44 c
+176.533 -0.529 176.616 -0.598 176.716 -0.646 c
+176.811 -0.698 176.926 -0.72 177.054 -0.72 c
+177.208 -0.72 177.348 -0.687 177.466 -0.617 c
+177.591 -0.55 177.678 -0.448 177.73 -0.309 c
+178.568 -0.382 l
+178.538 -0.481 178.484 -0.588 178.407 -0.706 c
+178.326 -0.816 178.222 -0.918 178.098 -1.014 c
+177.98 -1.102 177.826 -1.176 177.642 -1.234 c
+177.466 -1.294 177.252 -1.323 177.01 -1.323 c
+177.01 1.706 m
+176.922 1.706 176.834 1.691 176.745 1.661 c
+176.657 1.632 176.576 1.58 176.51 1.515 c
+176.44 1.445 176.381 1.357 176.334 1.25 c
+176.294 1.139 176.275 1.015 176.275 0.867 c
+177.745 0.867 l
+177.745 1.004 177.719 1.125 177.672 1.235 c
+177.631 1.341 177.576 1.43 177.51 1.5 c
+177.451 1.566 177.377 1.617 177.289 1.646 c
+177.201 1.684 177.105 1.706 177.01 1.706 c
+180.571 -0.278 0.926 -0.985 re
+180.571 -1.263 m
+185.131 -1.323 m
+184.845 -1.323 184.602 -1.282 184.396 -1.205 c
+184.19 -1.117 184.018 -0.995 183.882 -0.837 c
+183.742 -0.683 183.639 -0.496 183.573 -0.278 c
+183.503 -0.055 183.471 0.192 183.471 0.456 c
+183.471 0.75 183.503 1.008 183.573 1.235 c
+183.65 1.459 183.757 1.646 183.897 1.794 c
+184.043 1.948 184.22 2.066 184.425 2.147 c
+184.631 2.234 184.867 2.278 185.131 2.278 c
+185.356 2.278 185.557 2.249 185.734 2.191 c
+185.91 2.132 186.061 2.047 186.189 1.941 c
+186.314 1.842 186.417 1.721 186.498 1.573 c
+186.575 1.434 186.63 1.283 186.66 1.118 c
+185.748 1.073 l
+185.719 1.25 185.649 1.389 185.543 1.5 c
+185.443 1.607 185.3 1.661 185.117 1.661 c
+184.87 1.661 184.693 1.559 184.587 1.353 c
+184.477 1.154 184.425 0.867 184.425 0.485 c
+184.425 -0.309 184.661 -0.706 185.131 -0.706 c
+185.296 -0.706 185.439 -0.654 185.557 -0.544 c
+185.675 -0.436 185.748 -0.275 185.778 -0.058 c
+186.689 -0.103 l
+186.66 -0.272 186.605 -0.426 186.528 -0.573 c
+186.457 -0.72 186.355 -0.852 186.218 -0.97 c
+186.09 -1.08 185.932 -1.168 185.748 -1.234 c
+185.572 -1.294 185.366 -1.323 185.131 -1.323 c
+190.838 0.485 m
+190.838 0.21 190.802 -0.039 190.735 -0.264 c
+190.665 -0.481 190.563 -0.668 190.426 -0.823 c
+190.287 -0.98 190.11 -1.102 189.898 -1.19 c
+189.68 -1.278 189.427 -1.323 189.133 -1.323 c
+188.857 -1.323 188.611 -1.278 188.398 -1.19 c
+188.192 -1.102 188.02 -0.98 187.884 -0.823 c
+187.744 -0.668 187.641 -0.481 187.575 -0.264 c
+187.505 -0.039 187.472 0.21 187.472 0.485 c
+187.472 0.739 187.502 0.975 187.56 1.191 c
+187.627 1.415 187.729 1.607 187.868 1.764 c
+188.005 1.929 188.181 2.058 188.398 2.147 c
+188.611 2.234 188.868 2.278 189.162 2.278 c
+189.474 2.278 189.736 2.234 189.942 2.147 c
+190.154 2.058 190.328 1.929 190.455 1.764 c
+190.592 1.607 190.691 1.415 190.75 1.191 c
+190.808 0.975 190.838 0.739 190.838 0.485 c
+189.883 0.485 m
+189.883 0.691 189.868 0.867 189.838 1.015 c
+189.817 1.162 189.78 1.283 189.721 1.382 c
+189.662 1.478 189.589 1.548 189.501 1.588 c
+189.412 1.636 189.302 1.661 189.177 1.661 c
+188.913 1.661 188.722 1.563 188.604 1.368 c
+188.487 1.18 188.427 0.886 188.427 0.485 c
+188.427 0.063 188.487 -0.242 188.604 -0.426 c
+188.722 -0.613 188.898 -0.706 189.133 -0.706 c
+189.258 -0.706 189.372 -0.687 189.471 -0.646 c
+189.567 -0.598 189.647 -0.525 189.707 -0.426 c
+189.773 -0.33 189.817 -0.205 189.838 -0.058 c
+189.868 0.088 189.883 0.269 189.883 0.485 c
+192.855 -1.263 m
+192.855 0.853 l
+192.855 1.019 192.848 1.154 192.841 1.264 c
+192.83 1.372 192.811 1.455 192.782 1.515 c
+192.76 1.58 192.73 1.632 192.694 1.661 c
+192.664 1.691 192.624 1.706 192.576 1.706 c
+192.518 1.706 192.462 1.676 192.414 1.617 c
+192.374 1.566 192.341 1.492 192.312 1.397 c
+192.282 1.309 192.256 1.195 192.238 1.058 c
+192.227 0.919 192.223 0.769 192.223 0.603 c
+192.223 -1.263 l
+191.473 -1.263 l
+191.473 1.47 l
+191.473 1.706 l
+191.473 1.926 l
+191.473 2.003 191.467 2.066 191.459 2.117 c
+191.459 2.22 l
+192.136 2.22 l
+192.136 2.132 l
+192.136 1.985 l
+192.142 1.926 192.15 1.867 192.15 1.808 c
+192.15 1.646 l
+192.165 1.646 l
+192.183 1.735 192.213 1.812 192.253 1.881 c
+192.29 1.96 192.333 2.029 192.385 2.087 c
+192.444 2.147 192.51 2.191 192.591 2.22 c
+192.668 2.257 192.757 2.278 192.855 2.278 c
+193.039 2.278 193.179 2.224 193.267 2.117 c
+193.362 2.018 193.432 1.86 193.472 1.646 c
+193.488 1.646 l
+193.524 1.742 193.565 1.831 193.605 1.912 c
+193.653 1.989 193.708 2.051 193.767 2.103 c
+193.825 2.161 193.892 2.205 193.973 2.234 c
+194.05 2.264 194.138 2.278 194.237 2.278 c
+194.373 2.278 194.488 2.253 194.575 2.205 c
+194.663 2.154 194.729 2.08 194.781 1.985 c
+194.84 1.885 194.877 1.757 194.899 1.603 c
+194.928 1.455 194.943 1.272 194.943 1.058 c
+194.943 -1.263 l
+194.222 -1.263 l
+194.222 0.853 l
+194.222 1.019 194.215 1.154 194.208 1.264 c
+194.197 1.372 194.178 1.455 194.149 1.515 c
+194.127 1.58 194.098 1.632 194.06 1.661 c
+194.031 1.691 193.991 1.706 193.943 1.706 c
+193.825 1.706 193.73 1.617 193.664 1.441 c
+193.605 1.272 193.576 1.015 193.576 0.662 c
+193.576 -1.263 l
+h
+198.331 1.455 m
+197.699 1.455 l
+197.567 3.514 l
+198.463 3.514 l
+h
+196.861 1.455 m
+196.229 1.455 l
+196.111 3.514 l
+196.979 3.514 l
+h
+f
+Q
+q 1 0 0 1 64.9298 361.4887 cm
+0 0 m
+0 0.264 -0.073 0.463 -0.22 0.603 c
+-0.36 0.75 -0.617 0.889 -0.999 1.029 c
+-1.374 1.165 -1.66 1.309 -1.866 1.455 c
+-2.065 1.602 -2.212 1.768 -2.308 1.955 c
+-2.406 2.15 -2.454 2.371 -2.454 2.616 c
+-2.454 3.036 -2.315 3.385 -2.028 3.66 c
+-1.745 3.932 -1.378 4.072 -0.926 4.072 c
+-0.613 4.072 -0.334 3.998 -0.088 3.851 c
+0.154 3.711 0.341 3.516 0.47 3.263 c
+0.607 3.017 0.676 2.749 0.676 2.454 c
+0 2.454 l
+0 2.786 -0.081 3.042 -0.235 3.219 c
+-0.393 3.403 -0.625 3.499 -0.926 3.499 c
+-1.19 3.499 -1.404 3.418 -1.558 3.263 c
+-1.705 3.117 -1.779 2.903 -1.779 2.631 c
+-1.779 2.404 -1.701 2.213 -1.544 2.058 c
+-1.378 1.911 -1.124 1.771 -0.779 1.646 c
+-0.261 1.477 0.11 1.268 0.338 1.014 c
+0.573 0.757 0.691 0.426 0.691 0.015 c
+0.691 -0.426 0.548 -0.779 0.264 -1.043 c
+-0.022 -1.301 -0.405 -1.426 -0.881 -1.426 c
+-1.198 -1.426 -1.484 -1.356 -1.749 -1.22 c
+-2.014 -1.084 -2.227 -0.893 -2.381 -0.646 c
+-2.528 -0.405 -2.601 -0.118 -2.601 0.206 c
+-1.926 0.206 l
+-1.926 -0.129 -1.833 -0.389 -1.646 -0.573 c
+-1.463 -0.76 -1.205 -0.852 -0.881 -0.852 c
+-0.588 -0.852 -0.368 -0.779 -0.22 -0.632 c
+-0.073 -0.478 0 -0.264 0 0 c
+2.896 -1.426 m
+2.396 -1.426 2.014 -1.278 1.749 -0.985 c
+1.484 -0.691 1.353 -0.257 1.353 0.324 c
+1.353 0.794 l
+1.353 1.389 1.477 1.856 1.735 2.19 c
+1.999 2.532 2.359 2.705 2.822 2.705 c
+3.281 2.705 3.624 2.55 3.851 2.249 c
+4.087 1.955 4.208 1.492 4.219 0.867 c
+4.219 0.441 l
+1.999 0.441 l
+1.999 0.353 l
+1.999 -0.081 2.076 -0.393 2.234 -0.588 c
+2.4 -0.775 2.631 -0.867 2.925 -0.867 c
+3.12 -0.867 3.293 -0.834 3.439 -0.764 c
+3.587 -0.687 3.723 -0.569 3.851 -0.411 c
+4.189 -0.823 l
+3.903 -1.228 3.473 -1.426 2.896 -1.426 c
+2.822 2.146 m
+2.547 2.146 2.344 2.051 2.219 1.866 c
+2.091 1.679 2.018 1.389 1.999 1 c
+3.572 1 l
+3.572 1.087 l
+3.55 1.47 3.484 1.738 3.366 1.897 c
+3.248 2.062 3.065 2.146 2.822 2.146 c
+5.85 3.587 m
+5.85 2.631 l
+6.453 2.631 l
+6.453 2.102 l
+5.85 2.102 l
+5.85 -0.368 l
+5.85 -0.525 5.872 -0.643 5.924 -0.721 c
+5.982 -0.801 6.071 -0.838 6.188 -0.838 c
+6.277 -0.838 6.365 -0.823 6.453 -0.794 c
+6.453 -1.352 l
+6.306 -1.4 6.151 -1.426 5.997 -1.426 c
+5.74 -1.426 5.546 -1.334 5.409 -1.147 c
+5.27 -0.962 5.203 -0.702 5.203 -0.368 c
+5.203 2.102 l
+4.601 2.102 l
+4.601 2.631 l
+5.203 2.631 l
+5.203 3.587 l
+h
+9.789 3.587 m
+9.789 2.631 l
+10.392 2.631 l
+10.392 2.102 l
+9.789 2.102 l
+9.789 -0.368 l
+9.789 -0.525 9.812 -0.643 9.864 -0.721 c
+9.922 -0.801 10.01 -0.838 10.128 -0.838 c
+10.215 -0.838 10.304 -0.823 10.392 -0.794 c
+10.392 -1.352 l
+10.246 -1.4 10.091 -1.426 9.937 -1.426 c
+9.679 -1.426 9.485 -1.334 9.349 -1.147 c
+9.209 -0.962 9.143 -0.702 9.143 -0.368 c
+9.143 2.102 l
+8.54 2.102 l
+8.54 2.631 l
+9.143 2.631 l
+9.143 3.587 l
+h
+11.803 2.219 m
+12.057 2.543 12.376 2.705 12.759 2.705 c
+13.465 2.705 13.821 2.234 13.832 1.294 c
+13.832 -1.352 l
+13.185 -1.352 l
+13.185 1.264 l
+13.185 1.577 13.13 1.797 13.024 1.926 c
+12.914 2.051 12.759 2.117 12.553 2.117 c
+12.395 2.117 12.248 2.062 12.112 1.955 c
+11.983 1.845 11.881 1.709 11.803 1.544 c
+11.803 -1.352 l
+11.156 -1.352 l
+11.156 4.293 l
+11.803 4.293 l
+h
+16.214 -1.426 m
+15.713 -1.426 15.331 -1.278 15.067 -0.985 c
+14.803 -0.691 14.67 -0.257 14.67 0.324 c
+14.67 0.794 l
+14.67 1.389 14.795 1.856 15.052 2.19 c
+15.316 2.532 15.676 2.705 16.139 2.705 c
+16.599 2.705 16.941 2.55 17.169 2.249 c
+17.404 1.955 17.525 1.492 17.536 0.867 c
+17.536 0.441 l
+15.316 0.441 l
+15.316 0.353 l
+15.316 -0.081 15.393 -0.393 15.551 -0.588 c
+15.717 -0.775 15.948 -0.867 16.243 -0.867 c
+16.438 -0.867 16.61 -0.834 16.757 -0.764 c
+16.904 -0.687 17.04 -0.569 17.169 -0.411 c
+17.506 -0.823 l
+17.22 -1.228 16.79 -1.426 16.214 -1.426 c
+16.139 2.146 m
+15.864 2.146 15.662 2.051 15.537 1.866 c
+15.408 1.679 15.335 1.389 15.316 1 c
+16.889 1 l
+16.889 1.087 l
+16.867 1.47 16.801 1.738 16.684 1.897 c
+16.565 2.062 16.382 2.146 16.139 2.146 c
+21.461 -1.426 m
+20.961 -1.426 20.579 -1.278 20.314 -0.985 c
+20.05 -0.691 19.917 -0.257 19.917 0.324 c
+19.917 0.794 l
+19.917 1.389 20.042 1.856 20.299 2.19 c
+20.564 2.532 20.924 2.705 21.388 2.705 c
+21.847 2.705 22.188 2.55 22.416 2.249 c
+22.651 1.955 22.772 1.492 22.784 0.867 c
+22.784 0.441 l
+20.564 0.441 l
+20.564 0.353 l
+20.564 -0.081 20.642 -0.393 20.799 -0.588 c
+20.964 -0.775 21.196 -0.867 21.49 -0.867 c
+21.685 -0.867 21.858 -0.834 22.005 -0.764 c
+22.152 -0.687 22.288 -0.569 22.416 -0.411 c
+22.755 -0.823 l
+22.468 -1.228 22.038 -1.426 21.461 -1.426 c
+21.388 2.146 m
+21.112 2.146 20.91 2.051 20.785 1.866 c
+20.656 1.679 20.582 1.389 20.564 1 c
+22.137 1 l
+22.137 1.087 l
+22.115 1.47 22.049 1.738 21.931 1.897 c
+21.814 2.062 21.629 2.146 21.388 2.146 c
+24.768 0.647 -1.514 0.558 re
+26.047 2.631 m
+26.061 2.263 l
+26.304 2.558 26.624 2.705 27.017 2.705 c
+27.458 2.705 27.767 2.506 27.943 2.117 c
+28.197 2.506 28.546 2.705 28.987 2.705 c
+29.722 2.705 30.096 2.242 30.119 1.323 c
+30.119 -1.352 l
+29.471 -1.352 l
+29.471 1.264 l
+29.471 1.558 29.417 1.771 29.31 1.911 c
+29.211 2.047 29.038 2.117 28.795 2.117 c
+28.598 2.117 28.436 2.036 28.311 1.881 c
+28.193 1.735 28.123 1.544 28.105 1.309 c
+28.105 -1.352 l
+27.443 -1.352 l
+27.443 1.294 l
+27.443 1.841 27.223 2.117 26.782 2.117 c
+26.447 2.117 26.212 1.955 26.076 1.631 c
+26.076 -1.352 l
+25.429 -1.352 l
+25.429 2.631 l
+h
+33.132 -1.352 m
+33.091 -1.264 33.066 -1.117 33.059 -0.912 c
+32.823 -1.257 32.529 -1.426 32.176 -1.426 c
+31.813 -1.426 31.53 -1.33 31.324 -1.132 c
+31.125 -0.926 31.03 -0.64 31.03 -0.264 c
+31.03 0.136 31.166 0.455 31.441 0.69 c
+31.713 0.933 32.088 1.058 32.558 1.058 c
+33.043 1.058 l
+33.043 1.484 l
+33.043 1.72 32.989 1.885 32.882 1.984 c
+32.772 2.091 32.61 2.146 32.397 2.146 c
+32.199 2.146 32.037 2.087 31.912 1.97 c
+31.794 1.852 31.736 1.706 31.736 1.529 c
+31.089 1.529 l
+31.089 1.723 31.147 1.914 31.265 2.102 c
+31.39 2.286 31.551 2.433 31.75 2.543 c
+31.956 2.65 32.184 2.705 32.441 2.705 c
+32.841 2.705 33.147 2.602 33.352 2.396 c
+33.566 2.19 33.679 1.897 33.691 1.514 c
+33.691 -0.5 l
+33.691 -0.804 33.727 -1.07 33.808 -1.294 c
+33.808 -1.352 l
+h
+32.265 -0.838 m
+32.43 -0.838 32.581 -0.794 32.72 -0.706 c
+32.868 -0.617 32.974 -0.507 33.043 -0.368 c
+33.043 0.573 l
+32.677 0.573 l
+32.36 0.573 32.118 0.503 31.941 0.368 c
+31.765 0.239 31.677 0.052 31.677 -0.191 c
+31.677 -0.419 31.721 -0.584 31.809 -0.691 c
+31.897 -0.79 32.048 -0.838 32.265 -0.838 c
+35.395 -1.352 -0.646 3.983 re
+35.44 3.674 m
+35.44 3.564 35.41 3.473 35.351 3.395 c
+35.293 3.326 35.197 3.293 35.072 3.293 c
+34.954 3.293 34.859 3.326 34.793 3.395 c
+34.734 3.473 34.705 3.564 34.705 3.674 c
+34.705 3.792 34.734 3.884 34.793 3.954 c
+34.859 4.031 34.954 4.072 35.072 4.072 c
+35.197 4.072 35.293 4.031 35.351 3.954 c
+35.41 3.873 35.44 3.782 35.44 3.674 c
+37.115 -1.352 -0.646 5.644 re
+41.892 -1.352 m
+41.852 -1.264 41.826 -1.117 41.819 -0.912 c
+41.583 -1.257 41.29 -1.426 40.937 -1.426 c
+40.573 -1.426 40.29 -1.33 40.084 -1.132 c
+39.886 -0.926 39.79 -0.64 39.79 -0.264 c
+39.79 0.136 39.927 0.455 40.202 0.69 c
+40.474 0.933 40.849 1.058 41.319 1.058 c
+41.805 1.058 l
+41.805 1.484 l
+41.805 1.72 41.749 1.885 41.643 1.984 c
+41.533 2.091 41.371 2.146 41.157 2.146 c
+40.959 2.146 40.797 2.087 40.673 1.97 c
+40.554 1.852 40.496 1.706 40.496 1.529 c
+39.849 1.529 l
+39.849 1.723 39.908 1.914 40.026 2.102 c
+40.151 2.286 40.313 2.433 40.511 2.543 c
+40.716 2.65 40.945 2.705 41.202 2.705 c
+41.602 2.705 41.907 2.602 42.113 2.396 c
+42.326 2.19 42.44 1.897 42.451 1.514 c
+42.451 -0.5 l
+42.451 -0.804 42.488 -1.07 42.568 -1.294 c
+42.568 -1.352 l
+h
+41.025 -0.838 m
+41.19 -0.838 41.341 -0.794 41.481 -0.706 c
+41.628 -0.617 41.734 -0.507 41.805 -0.368 c
+41.805 0.573 l
+41.437 0.573 l
+41.121 0.573 40.878 0.503 40.702 0.368 c
+40.525 0.239 40.438 0.052 40.438 -0.191 c
+40.438 -0.419 40.481 -0.584 40.569 -0.691 c
+40.658 -0.79 40.808 -0.838 41.025 -0.838 c
+43.318 0.823 m
+43.318 1.43 43.428 1.897 43.656 2.219 c
+43.891 2.543 44.219 2.705 44.641 2.705 c
+45.023 2.705 45.321 2.547 45.537 2.234 c
+45.537 4.293 l
+46.185 4.293 l
+46.185 -1.352 l
+45.597 -1.352 l
+45.552 -0.926 l
+45.346 -1.261 45.042 -1.426 44.641 -1.426 c
+44.23 -1.426 43.906 -1.271 43.671 -0.956 c
+43.436 -0.632 43.318 -0.176 43.318 0.412 c
+h
+43.965 0.441 m
+43.965 0 44.028 -0.33 44.156 -0.544 c
+44.292 -0.75 44.513 -0.852 44.818 -0.852 c
+45.141 -0.852 45.379 -0.691 45.537 -0.368 c
+45.537 1.646 l
+45.369 1.959 45.13 2.117 44.818 2.117 c
+44.513 2.117 44.292 2.014 44.156 1.808 c
+44.028 1.602 43.965 1.278 43.965 0.838 c
+h
+47.052 0.823 m
+47.052 1.43 47.162 1.897 47.39 2.219 c
+47.625 2.543 47.952 2.705 48.375 2.705 c
+48.757 2.705 49.055 2.547 49.271 2.234 c
+49.271 4.293 l
+49.918 4.293 l
+49.918 -1.352 l
+49.33 -1.352 l
+49.286 -0.926 l
+49.08 -1.261 48.775 -1.426 48.375 -1.426 c
+47.964 -1.426 47.64 -1.271 47.405 -0.956 c
+47.17 -0.632 47.052 -0.176 47.052 0.412 c
+h
+47.698 0.441 m
+47.698 0 47.761 -0.33 47.889 -0.544 c
+48.026 -0.75 48.246 -0.852 48.551 -0.852 c
+48.874 -0.852 49.113 -0.691 49.271 -0.368 c
+49.271 1.646 l
+49.102 1.959 48.864 2.117 48.551 2.117 c
+48.246 2.117 48.026 2.014 47.889 1.808 c
+47.761 1.602 47.698 1.278 47.698 0.838 c
+h
+52.564 2.014 m
+52.475 2.032 52.377 2.043 52.27 2.043 c
+51.935 2.043 51.7 1.86 51.565 1.5 c
+51.565 -1.352 l
+50.918 -1.352 l
+50.918 2.631 l
+51.55 2.631 l
+51.565 2.219 l
+51.741 2.543 51.983 2.705 52.299 2.705 c
+52.406 2.705 52.494 2.683 52.564 2.646 c
+h
+54.563 -1.426 m
+54.063 -1.426 53.681 -1.278 53.416 -0.985 c
+53.152 -0.691 53.019 -0.257 53.019 0.324 c
+53.019 0.794 l
+53.019 1.389 53.144 1.856 53.402 2.19 c
+53.666 2.532 54.027 2.705 54.49 2.705 c
+54.949 2.705 55.291 2.55 55.519 2.249 c
+55.754 1.955 55.875 1.492 55.886 0.867 c
+55.886 0.441 l
+53.666 0.441 l
+53.666 0.353 l
+53.666 -0.081 53.743 -0.393 53.902 -0.588 c
+54.067 -0.775 54.299 -0.867 54.592 -0.867 c
+54.787 -0.867 54.96 -0.834 55.107 -0.764 c
+55.254 -0.687 55.39 -0.569 55.519 -0.411 c
+55.856 -0.823 l
+55.569 -1.228 55.14 -1.426 54.563 -1.426 c
+54.49 2.146 m
+54.214 2.146 54.012 2.051 53.886 1.866 c
+53.759 1.679 53.685 1.389 53.666 1 c
+55.239 1 l
+55.239 1.087 l
+55.217 1.47 55.151 1.738 55.033 1.897 c
+54.916 2.062 54.732 2.146 54.49 2.146 c
+58.605 -0.338 m
+58.605 -0.191 58.55 -0.07 58.443 0.029 c
+58.333 0.125 58.127 0.243 57.826 0.382 c
+57.481 0.53 57.238 0.651 57.092 0.75 c
+56.944 0.856 56.834 0.974 56.768 1.103 c
+56.698 1.228 56.665 1.386 56.665 1.573 c
+56.665 1.897 56.782 2.165 57.017 2.381 c
+57.252 2.595 57.554 2.705 57.929 2.705 c
+58.312 2.705 58.62 2.591 58.855 2.367 c
+59.091 2.139 59.208 1.852 59.208 1.5 c
+58.561 1.5 l
+58.561 1.675 58.503 1.827 58.385 1.955 c
+58.268 2.08 58.113 2.146 57.929 2.146 c
+57.73 2.146 57.58 2.091 57.474 1.984 c
+57.363 1.885 57.312 1.753 57.312 1.588 c
+57.312 1.459 57.348 1.353 57.429 1.264 c
+57.506 1.183 57.697 1.081 58.002 0.956 c
+58.48 0.769 58.811 0.58 58.987 0.397 c
+59.164 0.22 59.252 -0.008 59.252 -0.279 c
+59.252 -0.632 59.127 -0.912 58.885 -1.117 c
+58.649 -1.323 58.333 -1.426 57.944 -1.426 c
+57.522 -1.426 57.183 -1.309 56.93 -1.073 c
+56.672 -0.831 56.547 -0.525 56.547 -0.162 c
+57.194 -0.162 l
+57.202 -0.389 57.271 -0.565 57.4 -0.691 c
+57.525 -0.808 57.709 -0.867 57.944 -0.867 c
+58.157 -0.867 58.318 -0.819 58.428 -0.721 c
+58.547 -0.625 58.605 -0.496 58.605 -0.338 c
+62.03 -0.338 m
+62.03 -0.191 61.975 -0.07 61.869 0.029 c
+61.759 0.125 61.553 0.243 61.251 0.382 c
+60.905 0.53 60.664 0.651 60.516 0.75 c
+60.369 0.856 60.259 0.974 60.193 1.103 c
+60.123 1.228 60.09 1.386 60.09 1.573 c
+60.09 1.897 60.207 2.165 60.443 2.381 c
+60.678 2.595 60.979 2.705 61.354 2.705 c
+61.736 2.705 62.045 2.591 62.28 2.367 c
+62.515 2.139 62.633 1.852 62.633 1.5 c
+61.986 1.5 l
+61.986 1.675 61.927 1.827 61.809 1.955 c
+61.692 2.08 61.538 2.146 61.354 2.146 c
+61.156 2.146 61.005 2.091 60.899 1.984 c
+60.788 1.885 60.737 1.753 60.737 1.588 c
+60.737 1.459 60.774 1.353 60.855 1.264 c
+60.932 1.183 61.123 1.081 61.427 0.956 c
+61.905 0.769 62.236 0.58 62.412 0.397 c
+62.588 0.22 62.677 -0.008 62.677 -0.279 c
+62.677 -0.632 62.552 -0.912 62.31 -1.117 c
+62.075 -1.323 61.759 -1.426 61.369 -1.426 c
+60.946 -1.426 60.608 -1.309 60.354 -1.073 c
+60.097 -0.831 59.972 -0.525 59.972 -0.162 c
+60.619 -0.162 l
+60.626 -0.389 60.697 -0.565 60.824 -0.691 c
+60.95 -0.808 61.134 -0.867 61.369 -0.867 c
+61.582 -0.867 61.744 -0.819 61.854 -0.721 c
+61.971 -0.625 62.03 -0.496 62.03 -0.338 c
+66.087 3.587 m
+66.087 2.631 l
+66.69 2.631 l
+66.69 2.102 l
+66.087 2.102 l
+66.087 -0.368 l
+66.087 -0.525 66.11 -0.643 66.16 -0.721 c
+66.22 -0.801 66.308 -0.838 66.425 -0.838 c
+66.513 -0.838 66.602 -0.823 66.69 -0.794 c
+66.69 -1.352 l
+66.543 -1.4 66.388 -1.426 66.234 -1.426 c
+65.977 -1.426 65.782 -1.334 65.646 -1.147 c
+65.507 -0.962 65.441 -0.702 65.441 -0.368 c
+65.441 2.102 l
+64.838 2.102 l
+64.838 2.631 l
+65.441 2.631 l
+65.441 3.587 l
+h
+68.101 2.219 m
+68.354 2.543 68.674 2.705 69.056 2.705 c
+69.761 2.705 70.118 2.234 70.129 1.294 c
+70.129 -1.352 l
+69.482 -1.352 l
+69.482 1.264 l
+69.482 1.577 69.428 1.797 69.321 1.926 c
+69.21 2.051 69.056 2.117 68.851 2.117 c
+68.693 2.117 68.545 2.062 68.41 1.955 c
+68.281 1.845 68.178 1.709 68.101 1.544 c
+68.101 -1.352 l
+67.454 -1.352 l
+67.454 4.293 l
+68.101 4.293 l
+h
+73.128 -1.352 m
+73.088 -1.264 73.062 -1.117 73.054 -0.912 c
+72.819 -1.257 72.525 -1.426 72.173 -1.426 c
+71.809 -1.426 71.525 -1.33 71.32 -1.132 c
+71.122 -0.926 71.026 -0.64 71.026 -0.264 c
+71.026 0.136 71.162 0.455 71.438 0.69 c
+71.71 0.933 72.084 1.058 72.555 1.058 c
+73.04 1.058 l
+73.04 1.484 l
+73.04 1.72 72.984 1.885 72.878 1.984 c
+72.768 2.091 72.606 2.146 72.393 2.146 c
+72.194 2.146 72.032 2.087 71.908 1.97 c
+71.791 1.852 71.731 1.706 71.731 1.529 c
+71.085 1.529 l
+71.085 1.723 71.143 1.914 71.261 2.102 c
+71.386 2.286 71.548 2.433 71.746 2.543 c
+71.952 2.65 72.18 2.705 72.437 2.705 c
+72.838 2.705 73.142 2.602 73.349 2.396 c
+73.561 2.19 73.676 1.897 73.686 1.514 c
+73.686 -0.5 l
+73.686 -0.804 73.723 -1.07 73.804 -1.294 c
+73.804 -1.352 l
+h
+72.261 -0.838 m
+72.426 -0.838 72.577 -0.794 72.716 -0.706 c
+72.863 -0.617 72.97 -0.507 73.04 -0.368 c
+73.04 0.573 l
+72.672 0.573 l
+72.356 0.573 72.113 0.503 71.937 0.368 c
+71.761 0.239 71.673 0.052 71.673 -0.191 c
+71.673 -0.419 71.717 -0.584 71.805 -0.691 c
+71.893 -0.79 72.044 -0.838 72.261 -0.838 c
+75.539 3.587 m
+75.539 2.631 l
+76.142 2.631 l
+76.142 2.102 l
+75.539 2.102 l
+75.539 -0.368 l
+75.539 -0.525 75.56 -0.643 75.612 -0.721 c
+75.671 -0.801 75.759 -0.838 75.876 -0.838 c
+75.965 -0.838 76.053 -0.823 76.142 -0.794 c
+76.142 -1.352 l
+75.994 -1.4 75.84 -1.426 75.685 -1.426 c
+75.428 -1.426 75.234 -1.334 75.097 -1.147 c
+74.958 -0.962 74.891 -0.702 74.891 -0.368 c
+74.891 2.102 l
+74.289 2.102 l
+74.289 2.631 l
+74.891 2.631 l
+74.891 3.587 l
+h
+81.595 -0.206 m
+82.197 2.631 l
+82.844 2.631 l
+81.859 -1.352 l
+81.345 -1.352 l
+80.566 1.5 l
+79.816 -1.352 l
+79.286 -1.352 l
+78.332 2.631 l
+78.964 2.631 l
+79.581 -0.133 l
+80.316 2.631 l
+80.83 2.631 l
+h
+84.225 -1.352 -0.646 3.983 re
+84.27 3.674 m
+84.27 3.564 84.24 3.473 84.182 3.395 c
+84.123 3.326 84.027 3.293 83.903 3.293 c
+83.785 3.293 83.689 3.326 83.623 3.395 c
+83.564 3.473 83.535 3.564 83.535 3.674 c
+83.535 3.792 83.564 3.884 83.623 3.954 c
+83.689 4.031 83.785 4.072 83.903 4.072 c
+84.027 4.072 84.123 4.031 84.182 3.954 c
+84.24 3.873 84.27 3.782 84.27 3.674 c
+85.945 -1.352 -0.646 5.644 re
+87.666 -1.352 -0.647 5.644 re
+93.28 0.441 m
+93.28 -0.176 93.166 -0.643 92.942 -0.956 c
+92.726 -1.271 92.402 -1.426 91.972 -1.426 c
+91.55 -1.426 91.238 -1.246 91.032 -0.881 c
+91.002 -1.352 l
+90.399 -1.352 l
+90.399 4.293 l
+91.046 4.293 l
+91.046 2.19 l
+91.259 2.532 91.568 2.705 91.972 2.705 c
+92.402 2.705 92.726 2.547 92.942 2.234 c
+93.166 1.929 93.28 1.463 93.28 0.838 c
+h
+92.634 0.823 m
+92.634 1.294 92.564 1.625 92.428 1.823 c
+92.299 2.018 92.09 2.117 91.795 2.117 c
+91.462 2.117 91.211 1.933 91.046 1.573 c
+91.046 -0.309 l
+91.211 -0.673 91.465 -0.852 91.811 -0.852 c
+92.104 -0.852 92.314 -0.75 92.443 -0.544 c
+92.568 -0.338 92.634 -0.022 92.634 0.412 c
+h
+95.544 -1.426 m
+95.044 -1.426 94.662 -1.278 94.398 -0.985 c
+94.133 -0.691 94.001 -0.257 94.001 0.324 c
+94.001 0.794 l
+94.001 1.389 94.126 1.856 94.383 2.19 c
+94.647 2.532 95.007 2.705 95.471 2.705 c
+95.93 2.705 96.271 2.55 96.499 2.249 c
+96.734 1.955 96.856 1.492 96.867 0.867 c
+96.867 0.441 l
+94.647 0.441 l
+94.647 0.353 l
+94.647 -0.081 94.725 -0.393 94.882 -0.588 c
+95.048 -0.775 95.279 -0.867 95.573 -0.867 c
+95.768 -0.867 95.941 -0.834 96.088 -0.764 c
+96.235 -0.687 96.371 -0.569 96.499 -0.411 c
+96.838 -0.823 l
+96.551 -1.228 96.121 -1.426 95.544 -1.426 c
+95.471 2.146 m
+95.195 2.146 94.993 2.051 94.868 1.866 c
+94.739 1.679 94.666 1.389 94.647 1 c
+96.22 1 l
+96.22 1.087 l
+96.198 1.47 96.132 1.738 96.015 1.897 c
+95.897 2.062 95.713 2.146 95.471 2.146 c
+101.409 -1.352 m
+101.368 -1.264 101.343 -1.117 101.335 -0.912 c
+101.1 -1.257 100.806 -1.426 100.453 -1.426 c
+100.09 -1.426 99.807 -1.33 99.601 -1.132 c
+99.402 -0.926 99.307 -0.64 99.307 -0.264 c
+99.307 0.136 99.443 0.455 99.718 0.69 c
+99.99 0.933 100.366 1.058 100.836 1.058 c
+101.32 1.058 l
+101.32 1.484 l
+101.32 1.72 101.266 1.885 101.159 1.984 c
+101.049 2.091 100.887 2.146 100.674 2.146 c
+100.476 2.146 100.314 2.087 100.189 1.97 c
+100.071 1.852 100.013 1.706 100.013 1.529 c
+99.366 1.529 l
+99.366 1.723 99.425 1.914 99.542 2.102 c
+99.667 2.286 99.828 2.433 100.027 2.543 c
+100.233 2.65 100.461 2.705 100.718 2.705 c
+101.118 2.705 101.424 2.602 101.629 2.396 c
+101.843 2.19 101.956 1.897 101.968 1.514 c
+101.968 -0.5 l
+101.968 -0.804 102.004 -1.07 102.085 -1.294 c
+102.085 -1.352 l
+h
+100.542 -0.838 m
+100.707 -0.838 100.858 -0.794 100.997 -0.706 c
+101.145 -0.617 101.251 -0.507 101.32 -0.368 c
+101.32 0.573 l
+100.954 0.573 l
+100.637 0.573 100.395 0.503 100.218 0.368 c
+100.042 0.239 99.954 0.052 99.954 -0.191 c
+99.954 -0.419 99.998 -0.584 100.086 -0.691 c
+100.175 -0.79 100.325 -0.838 100.542 -0.838 c
+103.819 3.587 m
+103.819 2.631 l
+104.422 2.631 l
+104.422 2.102 l
+103.819 2.102 l
+103.819 -0.368 l
+103.819 -0.525 103.842 -0.643 103.893 -0.721 c
+103.952 -0.801 104.04 -0.838 104.158 -0.838 c
+104.246 -0.838 104.334 -0.823 104.422 -0.794 c
+104.422 -1.352 l
+104.275 -1.4 104.121 -1.426 103.967 -1.426 c
+103.709 -1.426 103.514 -1.334 103.379 -1.147 c
+103.239 -0.962 103.173 -0.702 103.173 -0.368 c
+103.173 2.102 l
+102.57 2.102 l
+102.57 2.631 l
+103.173 2.631 l
+103.173 3.587 l
+h
+106.039 3.587 m
+106.039 2.631 l
+106.641 2.631 l
+106.641 2.102 l
+106.039 2.102 l
+106.039 -0.368 l
+106.039 -0.525 106.061 -0.643 106.113 -0.721 c
+106.171 -0.801 106.259 -0.838 106.377 -0.838 c
+106.465 -0.838 106.554 -0.823 106.641 -0.794 c
+106.641 -1.352 l
+106.495 -1.4 106.34 -1.426 106.186 -1.426 c
+105.929 -1.426 105.734 -1.334 105.598 -1.147 c
+105.459 -0.962 105.392 -0.702 105.392 -0.368 c
+105.392 2.102 l
+104.79 2.102 l
+104.79 2.631 l
+105.392 2.631 l
+105.392 3.587 l
+h
+109.449 -1.352 m
+109.409 -1.264 109.384 -1.117 109.376 -0.912 c
+109.141 -1.257 108.846 -1.426 108.494 -1.426 c
+108.13 -1.426 107.847 -1.33 107.641 -1.132 c
+107.443 -0.926 107.347 -0.64 107.347 -0.264 c
+107.347 0.136 107.483 0.455 107.759 0.69 c
+108.031 0.933 108.406 1.058 108.876 1.058 c
+109.361 1.058 l
+109.361 1.484 l
+109.361 1.72 109.306 1.885 109.199 1.984 c
+109.089 2.091 108.927 2.146 108.715 2.146 c
+108.516 2.146 108.354 2.087 108.229 1.97 c
+108.112 1.852 108.052 1.706 108.052 1.529 c
+107.406 1.529 l
+107.406 1.723 107.465 1.914 107.582 2.102 c
+107.707 2.286 107.869 2.433 108.067 2.543 c
+108.273 2.65 108.501 2.705 108.758 2.705 c
+109.159 2.705 109.463 2.602 109.67 2.396 c
+109.883 2.19 109.997 1.897 110.008 1.514 c
+110.008 -0.5 l
+110.008 -0.804 110.045 -1.07 110.126 -1.294 c
+110.126 -1.352 l
+h
+108.582 -0.838 m
+108.748 -0.838 108.898 -0.794 109.037 -0.706 c
+109.185 -0.617 109.291 -0.507 109.361 -0.368 c
+109.361 0.573 l
+108.993 0.573 l
+108.678 0.573 108.435 0.503 108.258 0.368 c
+108.082 0.239 107.994 0.052 107.994 -0.191 c
+107.994 -0.419 108.038 -0.584 108.127 -0.691 c
+108.214 -0.79 108.366 -0.838 108.582 -0.838 c
+112.389 -0.867 m
+112.602 -0.867 112.775 -0.804 112.904 -0.676 c
+113.039 -0.54 113.113 -0.349 113.124 -0.103 c
+113.741 -0.103 l
+113.719 -0.485 113.583 -0.804 113.33 -1.058 c
+113.072 -1.305 112.76 -1.426 112.389 -1.426 c
+111.896 -1.426 111.522 -1.275 111.257 -0.97 c
+111 -0.658 110.875 -0.191 110.875 0.426 c
+110.875 0.867 l
+110.875 1.463 111 1.918 111.257 2.234 c
+111.522 2.547 111.896 2.705 112.389 2.705 c
+112.79 2.705 113.11 2.572 113.345 2.308 c
+113.587 2.051 113.719 1.706 113.741 1.264 c
+113.124 1.264 l
+113.102 1.558 113.029 1.779 112.904 1.926 c
+112.786 2.072 112.613 2.146 112.389 2.146 c
+112.095 2.146 111.878 2.047 111.742 1.852 c
+111.603 1.665 111.529 1.357 111.522 0.926 c
+111.522 0.412 l
+111.522 -0.058 111.588 -0.393 111.728 -0.588 c
+111.875 -0.775 112.095 -0.867 112.389 -0.867 c
+115.138 2.219 m
+115.391 2.543 115.711 2.705 116.093 2.705 c
+116.798 2.705 117.155 2.234 117.166 1.294 c
+117.166 -1.352 l
+116.519 -1.352 l
+116.519 1.264 l
+116.519 1.577 116.464 1.797 116.358 1.926 c
+116.247 2.051 116.093 2.117 115.888 2.117 c
+115.73 2.117 115.582 2.062 115.447 1.955 c
+115.318 1.845 115.215 1.709 115.138 1.544 c
+115.138 -1.352 l
+114.491 -1.352 l
+114.491 4.293 l
+115.138 4.293 l
+h
+119.547 -1.426 m
+119.048 -1.426 118.666 -1.278 118.401 -0.985 c
+118.136 -0.691 118.004 -0.257 118.004 0.324 c
+118.004 0.794 l
+118.004 1.389 118.129 1.856 118.386 2.19 c
+118.651 2.532 119.011 2.705 119.474 2.705 c
+119.933 2.705 120.275 2.55 120.503 2.249 c
+120.738 1.955 120.86 1.492 120.871 0.867 c
+120.871 0.441 l
+118.651 0.441 l
+118.651 0.353 l
+118.651 -0.081 118.728 -0.393 118.886 -0.588 c
+119.051 -0.775 119.283 -0.867 119.576 -0.867 c
+119.772 -0.867 119.944 -0.834 120.091 -0.764 c
+120.239 -0.687 120.374 -0.569 120.503 -0.411 c
+120.841 -0.823 l
+120.555 -1.228 120.125 -1.426 119.547 -1.426 c
+119.474 2.146 m
+119.198 2.146 118.996 2.051 118.871 1.866 c
+118.743 1.679 118.67 1.389 118.651 1 c
+120.224 1 l
+120.224 1.087 l
+120.202 1.47 120.135 1.738 120.018 1.897 c
+119.9 2.062 119.717 2.146 119.474 2.146 c
+121.517 0.823 m
+121.517 1.43 121.627 1.897 121.855 2.219 c
+122.09 2.543 122.417 2.705 122.84 2.705 c
+123.223 2.705 123.52 2.547 123.736 2.234 c
+123.736 4.293 l
+124.384 4.293 l
+124.384 -1.352 l
+123.796 -1.352 l
+123.751 -0.926 l
+123.545 -1.261 123.241 -1.426 122.84 -1.426 c
+122.429 -1.426 122.105 -1.271 121.87 -0.956 c
+121.635 -0.632 121.517 -0.176 121.517 0.412 c
+h
+122.163 0.441 m
+122.163 0 122.227 -0.33 122.355 -0.544 c
+122.491 -0.75 122.712 -0.852 123.017 -0.852 c
+123.339 -0.852 123.578 -0.691 123.736 -0.368 c
+123.736 1.646 l
+123.568 1.959 123.329 2.117 123.017 2.117 c
+122.712 2.117 122.491 2.014 122.355 1.808 c
+122.227 1.602 122.163 1.278 122.163 0.838 c
+h
+127.956 3.587 m
+127.956 2.631 l
+128.558 2.631 l
+128.558 2.102 l
+127.956 2.102 l
+127.956 -0.368 l
+127.956 -0.525 127.977 -0.643 128.029 -0.721 c
+128.087 -0.801 128.176 -0.838 128.293 -0.838 c
+128.382 -0.838 128.47 -0.823 128.558 -0.794 c
+128.558 -1.352 l
+128.411 -1.4 128.257 -1.426 128.102 -1.426 c
+127.846 -1.426 127.651 -1.334 127.514 -1.147 c
+127.375 -0.962 127.308 -0.702 127.308 -0.368 c
+127.308 2.102 l
+126.706 2.102 l
+126.706 2.631 l
+127.308 2.631 l
+127.308 3.587 l
+h
+129.116 0.823 m
+129.116 1.4 129.253 1.856 129.529 2.19 c
+129.811 2.532 130.182 2.705 130.645 2.705 c
+131.104 2.705 131.472 2.535 131.748 2.205 c
+132.031 1.881 132.178 1.434 132.189 0.867 c
+132.189 0.441 l
+132.189 -0.129 132.045 -0.584 131.763 -0.926 c
+131.487 -1.261 131.119 -1.426 130.66 -1.426 c
+130.197 -1.426 129.826 -1.264 129.543 -0.941 c
+129.267 -0.61 129.124 -0.169 129.116 0.382 c
+h
+129.764 0.441 m
+129.764 0.037 129.841 -0.279 129.999 -0.515 c
+130.164 -0.75 130.385 -0.867 130.66 -0.867 c
+131.226 -0.867 131.52 -0.455 131.542 0.368 c
+131.542 0.823 l
+131.542 1.224 131.457 1.544 131.292 1.779 c
+131.134 2.022 130.917 2.146 130.645 2.146 c
+130.381 2.146 130.164 2.022 129.999 1.779 c
+129.841 1.544 129.764 1.224 129.764 0.823 c
+h
+135.834 -0.264 m
+136.554 2.631 l
+137.245 2.631 l
+135.952 -1.911 l
+135.852 -2.252 135.709 -2.514 135.526 -2.69 c
+135.349 -2.866 135.147 -2.955 134.923 -2.955 c
+134.834 -2.955 134.72 -2.932 134.585 -2.896 c
+134.585 -2.352 l
+134.732 -2.367 l
+134.915 -2.367 135.062 -2.323 135.173 -2.234 c
+135.279 -2.146 135.368 -1.988 135.437 -1.764 c
+135.555 -1.323 l
+134.393 2.631 l
+135.099 2.631 l
+h
+137.686 0.823 m
+137.686 1.4 137.822 1.856 138.098 2.19 c
+138.381 2.532 138.752 2.705 139.215 2.705 c
+139.674 2.705 140.041 2.535 140.317 2.205 c
+140.6 1.881 140.747 1.434 140.758 0.867 c
+140.758 0.441 l
+140.758 -0.129 140.615 -0.584 140.332 -0.926 c
+140.056 -1.261 139.689 -1.426 139.229 -1.426 c
+138.767 -1.426 138.395 -1.264 138.113 -0.941 c
+137.837 -0.61 137.693 -0.169 137.686 0.382 c
+h
+138.333 0.441 m
+138.333 0.037 138.41 -0.279 138.568 -0.515 c
+138.734 -0.75 138.954 -0.867 139.229 -0.867 c
+139.796 -0.867 140.089 -0.455 140.112 0.368 c
+140.112 0.823 l
+140.112 1.224 140.027 1.544 139.862 1.779 c
+139.704 2.022 139.487 2.146 139.215 2.146 c
+138.95 2.146 138.734 2.022 138.568 1.779 c
+138.41 1.544 138.333 1.224 138.333 0.823 c
+h
+143.625 -0.999 m
+143.408 -1.286 143.095 -1.426 142.684 -1.426 c
+142.32 -1.426 142.044 -1.305 141.861 -1.058 c
+141.684 -0.804 141.589 -0.441 141.581 0.029 c
+141.581 2.631 l
+142.229 2.631 l
+142.229 0.088 l
+142.229 -0.54 142.412 -0.852 142.786 -0.852 c
+143.187 -0.852 143.463 -0.676 143.609 -0.324 c
+143.609 2.631 l
+144.257 2.631 l
+144.257 -1.352 l
+143.64 -1.352 l
+h
+146.888 2.014 m
+146.8 2.032 146.701 2.043 146.594 2.043 c
+146.26 2.043 146.024 1.86 145.888 1.5 c
+145.888 -1.352 l
+145.242 -1.352 l
+145.242 2.631 l
+145.874 2.631 l
+145.888 2.219 l
+146.065 2.543 146.308 2.705 146.623 2.705 c
+146.73 2.705 146.818 2.683 146.888 2.646 c
+h
+150.636 -0.867 m
+150.849 -0.867 151.022 -0.804 151.15 -0.676 c
+151.287 -0.54 151.36 -0.349 151.371 -0.103 c
+151.989 -0.103 l
+151.966 -0.485 151.831 -0.804 151.577 -1.058 c
+151.32 -1.305 151.007 -1.426 150.636 -1.426 c
+150.144 -1.426 149.769 -1.275 149.504 -0.97 c
+149.247 -0.658 149.122 -0.191 149.122 0.426 c
+149.122 0.867 l
+149.122 1.463 149.247 1.918 149.504 2.234 c
+149.769 2.547 150.144 2.705 150.636 2.705 c
+151.037 2.705 151.356 2.572 151.592 2.308 c
+151.834 2.051 151.966 1.706 151.989 1.264 c
+151.371 1.264 l
+151.349 1.558 151.276 1.779 151.15 1.926 c
+151.033 2.072 150.86 2.146 150.636 2.146 c
+150.342 2.146 150.125 2.047 149.99 1.852 c
+149.849 1.665 149.776 1.357 149.769 0.926 c
+149.769 0.412 l
+149.769 -0.058 149.835 -0.393 149.975 -0.588 c
+150.121 -0.775 150.342 -0.867 150.636 -0.867 c
+152.606 0.823 m
+152.606 1.4 152.741 1.856 153.017 2.19 c
+153.3 2.532 153.672 2.705 154.134 2.705 c
+154.594 2.705 154.961 2.535 155.237 2.205 c
+155.52 1.881 155.667 1.434 155.678 0.867 c
+155.678 0.441 l
+155.678 -0.129 155.534 -0.584 155.251 -0.926 c
+154.976 -1.261 154.609 -1.426 154.149 -1.426 c
+153.686 -1.426 153.315 -1.264 153.032 -0.941 c
+152.756 -0.61 152.613 -0.169 152.606 0.382 c
+h
+153.252 0.441 m
+153.252 0.037 153.329 -0.279 153.487 -0.515 c
+153.653 -0.75 153.874 -0.867 154.149 -0.867 c
+154.715 -0.867 155.009 -0.455 155.031 0.368 c
+155.031 0.823 l
+155.031 1.224 154.946 1.544 154.781 1.779 c
+154.623 2.022 154.406 2.146 154.134 2.146 c
+153.87 2.146 153.653 2.022 153.487 1.779 c
+153.329 1.544 153.252 1.224 153.252 0.823 c
+h
+157.133 2.631 m
+157.148 2.263 l
+157.39 2.558 157.71 2.705 158.103 2.705 c
+158.544 2.705 158.852 2.506 159.029 2.117 c
+159.283 2.506 159.632 2.705 160.072 2.705 c
+160.808 2.705 161.183 2.242 161.204 1.323 c
+161.204 -1.352 l
+160.558 -1.352 l
+160.558 1.264 l
+160.558 1.558 160.502 1.771 160.396 1.911 c
+160.297 2.047 160.124 2.117 159.881 2.117 c
+159.683 2.117 159.521 2.036 159.397 1.881 c
+159.279 1.735 159.209 1.544 159.191 1.309 c
+159.191 -1.352 l
+158.53 -1.352 l
+158.53 1.294 l
+158.53 1.841 158.309 2.117 157.868 2.117 c
+157.533 2.117 157.298 1.955 157.163 1.631 c
+157.163 -1.352 l
+156.516 -1.352 l
+156.516 2.631 l
+h
+162.792 2.631 m
+162.807 2.263 l
+163.049 2.558 163.369 2.705 163.762 2.705 c
+164.203 2.705 164.512 2.506 164.689 2.117 c
+164.942 2.506 165.291 2.705 165.732 2.705 c
+166.467 2.705 166.842 2.242 166.864 1.323 c
+166.864 -1.352 l
+166.217 -1.352 l
+166.217 1.264 l
+166.217 1.558 166.162 1.771 166.056 1.911 c
+165.956 2.047 165.784 2.117 165.541 2.117 c
+165.342 2.117 165.181 2.036 165.056 1.881 c
+164.938 1.735 164.868 1.544 164.85 1.309 c
+164.85 -1.352 l
+164.188 -1.352 l
+164.188 1.294 l
+164.188 1.841 163.968 2.117 163.527 2.117 c
+163.193 2.117 162.958 1.955 162.821 1.631 c
+162.821 -1.352 l
+162.175 -1.352 l
+162.175 2.631 l
+h
+168.539 -1.352 -0.646 3.983 re
+168.583 3.674 m
+168.583 3.564 168.554 3.473 168.495 3.395 c
+168.437 3.326 168.341 3.293 168.216 3.293 c
+168.099 3.293 168.003 3.326 167.937 3.395 c
+167.878 3.473 167.849 3.564 167.849 3.674 c
+167.849 3.792 167.878 3.884 167.937 3.954 c
+168.003 4.031 168.099 4.072 168.216 4.072 c
+168.341 4.072 168.437 4.031 168.495 3.954 c
+168.554 3.873 168.583 3.782 168.583 3.674 c
+170.406 3.587 m
+170.406 2.631 l
+171.009 2.631 l
+171.009 2.102 l
+170.406 2.102 l
+170.406 -0.368 l
+170.406 -0.525 170.428 -0.643 170.48 -0.721 c
+170.538 -0.801 170.627 -0.838 170.744 -0.838 c
+170.833 -0.838 170.921 -0.823 171.009 -0.794 c
+171.009 -1.352 l
+170.862 -1.4 170.708 -1.426 170.553 -1.426 c
+170.296 -1.426 170.102 -1.334 169.965 -1.147 c
+169.826 -0.962 169.759 -0.702 169.759 -0.368 c
+169.759 2.102 l
+169.157 2.102 l
+169.157 2.631 l
+169.759 2.631 l
+169.759 3.587 l
+h
+173.713 -0.338 m
+173.713 -0.191 173.659 -0.07 173.552 0.029 c
+173.441 0.125 173.236 0.243 172.934 0.382 c
+172.589 0.53 172.346 0.651 172.2 0.75 c
+172.053 0.856 171.943 0.974 171.876 1.103 c
+171.806 1.228 171.773 1.386 171.773 1.573 c
+171.773 1.897 171.891 2.165 172.126 2.381 c
+172.361 2.595 172.662 2.705 173.038 2.705 c
+173.42 2.705 173.728 2.591 173.963 2.367 c
+174.199 2.139 174.316 1.852 174.316 1.5 c
+173.669 1.5 l
+173.669 1.675 173.611 1.827 173.493 1.955 c
+173.375 2.08 173.221 2.146 173.038 2.146 c
+172.839 2.146 172.689 2.091 172.581 1.984 c
+172.471 1.885 172.42 1.753 172.42 1.588 c
+172.42 1.459 172.456 1.353 172.537 1.264 c
+172.614 1.183 172.806 1.081 173.111 0.956 c
+173.589 0.769 173.919 0.58 174.096 0.397 c
+174.272 0.22 174.36 -0.008 174.36 -0.279 c
+174.36 -0.632 174.235 -0.912 173.993 -1.117 c
+173.757 -1.323 173.441 -1.426 173.052 -1.426 c
+172.629 -1.426 172.292 -1.309 172.038 -1.073 c
+171.781 -0.831 171.656 -0.525 171.656 -0.162 c
+172.302 -0.162 l
+172.31 -0.389 172.379 -0.565 172.508 -0.691 c
+172.633 -0.808 172.817 -0.867 173.052 -0.867 c
+173.265 -0.867 173.427 -0.819 173.537 -0.721 c
+173.655 -0.625 173.713 -0.496 173.713 -0.338 c
+178.962 -1.352 m
+178.921 -1.264 178.895 -1.117 178.887 -0.912 c
+178.652 -1.257 178.359 -1.426 178.006 -1.426 c
+177.642 -1.426 177.359 -1.33 177.153 -1.132 c
+176.955 -0.926 176.859 -0.64 176.859 -0.264 c
+176.859 0.136 176.995 0.455 177.271 0.69 c
+177.543 0.933 177.917 1.058 178.388 1.058 c
+178.873 1.058 l
+178.873 1.484 l
+178.873 1.72 178.818 1.885 178.711 1.984 c
+178.601 2.091 178.439 2.146 178.226 2.146 c
+178.027 2.146 177.866 2.087 177.741 1.97 c
+177.624 1.852 177.565 1.706 177.565 1.529 c
+176.918 1.529 l
+176.918 1.723 176.977 1.914 177.094 2.102 c
+177.219 2.286 177.381 2.433 177.58 2.543 c
+177.786 2.65 178.013 2.705 178.27 2.705 c
+178.671 2.705 178.976 2.602 179.182 2.396 c
+179.394 2.19 179.509 1.897 179.519 1.514 c
+179.519 -0.5 l
+179.519 -0.804 179.556 -1.07 179.637 -1.294 c
+179.637 -1.352 l
+h
+178.094 -0.838 m
+178.259 -0.838 178.41 -0.794 178.55 -0.706 c
+178.696 -0.617 178.804 -0.507 178.873 -0.368 c
+178.873 0.573 l
+178.505 0.573 l
+178.189 0.573 177.946 0.503 177.771 0.368 c
+177.594 0.239 177.506 0.052 177.506 -0.191 c
+177.506 -0.419 177.55 -0.584 177.638 -0.691 c
+177.726 -0.79 177.877 -0.838 178.094 -0.838 c
+181.137 2.631 m
+181.152 2.19 l
+181.405 2.532 181.728 2.705 182.121 2.705 c
+182.827 2.705 183.184 2.234 183.195 1.294 c
+183.195 -1.352 l
+182.548 -1.352 l
+182.548 1.264 l
+182.548 1.577 182.492 1.797 182.386 1.926 c
+182.276 2.051 182.121 2.117 181.915 2.117 c
+181.757 2.117 181.611 2.062 181.474 1.955 c
+181.346 1.845 181.243 1.709 181.166 1.544 c
+181.166 -1.352 l
+180.519 -1.352 l
+180.519 2.631 l
+h
+184.032 0.823 m
+184.032 1.43 184.142 1.897 184.371 2.219 c
+184.606 2.543 184.932 2.705 185.355 2.705 c
+185.738 2.705 186.035 2.547 186.252 2.234 c
+186.252 4.293 l
+186.899 4.293 l
+186.899 -1.352 l
+186.311 -1.352 l
+186.266 -0.926 l
+186.06 -1.261 185.756 -1.426 185.355 -1.426 c
+184.944 -1.426 184.62 -1.271 184.385 -0.956 c
+184.15 -0.632 184.032 -0.176 184.032 0.412 c
+h
+184.679 0.441 m
+184.679 0 184.741 -0.33 184.87 -0.544 c
+185.006 -0.75 185.227 -0.852 185.532 -0.852 c
+185.855 -0.852 186.094 -0.691 186.252 -0.368 c
+186.252 1.646 l
+186.083 1.959 185.844 2.117 185.532 2.117 c
+185.227 2.117 185.006 2.014 184.87 1.808 c
+184.741 1.602 184.679 1.278 184.679 0.838 c
+h
+190.471 3.587 m
+190.471 2.631 l
+191.073 2.631 l
+191.073 2.102 l
+190.471 2.102 l
+190.471 -0.368 l
+190.471 -0.525 190.492 -0.643 190.544 -0.721 c
+190.602 -0.801 190.691 -0.838 190.808 -0.838 c
+190.897 -0.838 190.985 -0.823 191.073 -0.794 c
+191.073 -1.352 l
+190.926 -1.4 190.772 -1.426 190.617 -1.426 c
+190.36 -1.426 190.166 -1.334 190.029 -1.147 c
+189.89 -0.962 189.823 -0.702 189.823 -0.368 c
+189.823 2.102 l
+189.221 2.102 l
+189.221 2.631 l
+189.823 2.631 l
+189.823 3.587 l
+h
+193.881 -1.352 m
+193.84 -1.264 193.815 -1.117 193.807 -0.912 c
+193.572 -1.257 193.278 -1.426 192.925 -1.426 c
+192.562 -1.426 192.279 -1.33 192.073 -1.132 c
+191.874 -0.926 191.778 -0.64 191.778 -0.264 c
+191.778 0.136 191.915 0.455 192.19 0.69 c
+192.462 0.933 192.837 1.058 193.308 1.058 c
+193.792 1.058 l
+193.792 1.484 l
+193.792 1.72 193.738 1.885 193.63 1.984 c
+193.52 2.091 193.359 2.146 193.146 2.146 c
+192.948 2.146 192.786 2.087 192.661 1.97 c
+192.543 1.852 192.484 1.706 192.484 1.529 c
+191.837 1.529 l
+191.837 1.723 191.897 1.914 192.013 2.102 c
+192.138 2.286 192.3 2.433 192.499 2.543 c
+192.705 2.65 192.932 2.705 193.189 2.705 c
+193.59 2.705 193.895 2.602 194.101 2.396 c
+194.314 2.19 194.428 1.897 194.44 1.514 c
+194.44 -0.5 l
+194.44 -0.804 194.476 -1.07 194.557 -1.294 c
+194.557 -1.352 l
+h
+193.013 -0.838 m
+193.179 -0.838 193.329 -0.794 193.469 -0.706 c
+193.616 -0.617 193.723 -0.507 193.792 -0.368 c
+193.792 0.573 l
+193.424 0.573 l
+193.109 0.573 192.867 0.503 192.69 0.368 c
+192.514 0.239 192.425 0.052 192.425 -0.191 c
+192.425 -0.419 192.47 -0.584 192.558 -0.691 c
+192.646 -0.79 192.797 -0.838 193.013 -0.838 c
+195.306 0.823 m
+195.306 1.44 195.417 1.904 195.645 2.219 c
+195.869 2.543 196.203 2.705 196.644 2.705 c
+197.044 2.705 197.349 2.529 197.555 2.176 c
+197.599 2.631 l
+198.187 2.631 l
+198.187 -1.396 l
+198.187 -1.885 198.059 -2.263 197.806 -2.528 c
+197.548 -2.793 197.195 -2.925 196.747 -2.925 c
+196.549 -2.925 196.328 -2.874 196.086 -2.778 c
+195.839 -2.679 195.66 -2.558 195.541 -2.41 c
+195.806 -1.97 l
+196.071 -2.234 196.368 -2.367 196.703 -2.367 c
+197.239 -2.367 197.515 -2.072 197.526 -1.484 c
+197.526 -0.956 l
+197.32 -1.271 197.019 -1.426 196.63 -1.426 c
+196.218 -1.426 195.895 -1.275 195.66 -0.97 c
+195.431 -0.658 195.313 -0.206 195.306 0.382 c
+h
+195.968 0.441 m
+195.968 0 196.03 -0.33 196.159 -0.544 c
+196.284 -0.75 196.501 -0.852 196.806 -0.852 c
+197.129 -0.852 197.368 -0.687 197.526 -0.353 c
+197.526 1.631 l
+197.357 1.955 197.118 2.117 196.806 2.117 c
+196.512 2.117 196.295 2.014 196.159 1.808 c
+196.03 1.602 195.968 1.278 195.968 0.838 c
+h
+201.083 -0.338 m
+201.083 -0.191 201.028 -0.07 200.921 0.029 c
+200.811 0.125 200.605 0.243 200.304 0.382 c
+199.959 0.53 199.716 0.651 199.569 0.75 c
+199.423 0.856 199.312 0.974 199.246 1.103 c
+199.176 1.228 199.143 1.386 199.143 1.573 c
+199.143 1.897 199.261 2.165 199.496 2.381 c
+199.731 2.595 200.032 2.705 200.407 2.705 c
+200.79 2.705 201.098 2.591 201.333 2.367 c
+201.568 2.139 201.686 1.852 201.686 1.5 c
+201.039 1.5 l
+201.039 1.675 200.98 1.827 200.863 1.955 c
+200.745 2.08 200.591 2.146 200.407 2.146 c
+200.208 2.146 200.058 2.091 199.951 1.984 c
+199.841 1.885 199.79 1.753 199.79 1.588 c
+199.79 1.459 199.826 1.353 199.907 1.264 c
+199.984 1.183 200.175 1.081 200.48 0.956 c
+200.958 0.769 201.289 0.58 201.465 0.397 c
+201.642 0.22 201.73 -0.008 201.73 -0.279 c
+201.73 -0.632 201.605 -0.912 201.362 -1.117 c
+201.127 -1.323 200.811 -1.426 200.422 -1.426 c
+199.999 -1.426 199.661 -1.309 199.408 -1.073 c
+199.15 -0.831 199.026 -0.525 199.026 -0.162 c
+199.672 -0.162 l
+199.679 -0.389 199.749 -0.565 199.878 -0.691 c
+200.003 -0.808 200.187 -0.867 200.422 -0.867 c
+200.635 -0.867 200.796 -0.819 200.907 -0.721 c
+201.025 -0.625 201.083 -0.496 201.083 -0.338 c
+202.612 -0.999 m
+202.612 -0.881 202.645 -0.786 202.715 -0.706 c
+202.781 -0.628 202.884 -0.588 203.024 -0.588 c
+203.171 -0.588 203.277 -0.628 203.346 -0.706 c
+203.424 -0.786 203.464 -0.881 203.464 -0.999 c
+203.464 -1.11 203.424 -1.201 203.346 -1.278 c
+203.277 -1.356 203.171 -1.396 203.024 -1.396 c
+202.884 -1.396 202.781 -1.356 202.715 -1.278 c
+202.645 -1.201 202.612 -1.11 202.612 -0.999 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 354.533 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 347.6937 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.278 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.182 -0.103 1.999 c
+-0.941 1.882 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.437 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.631 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.179 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.882 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.675 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.117 l
+14.497 3.117 l
+14.497 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.566 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.69 15.056 -0.69 c
+15.192 -0.69 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.603 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.278 c
+15.313 -1.286 15.225 -1.293 15.129 -1.293 c
+15.041 -1.301 14.942 -1.309 14.835 -1.309 c
+14.648 -1.309 14.486 -1.293 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.791 -0.956 c
+13.733 -0.879 13.689 -0.802 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+22.677 -1.323 m
+22.391 -1.323 22.148 -1.282 21.943 -1.205 c
+21.737 -1.117 21.564 -0.996 21.428 -0.838 c
+21.288 -0.684 21.185 -0.496 21.119 -0.279 c
+21.049 -0.056 21.016 0.191 21.016 0.455 c
+21.016 0.75 21.049 1.007 21.119 1.235 c
+21.197 1.459 21.303 1.646 21.442 1.793 c
+21.589 1.947 21.766 2.065 21.972 2.146 c
+22.178 2.234 22.413 2.278 22.677 2.278 c
+22.901 2.278 23.104 2.249 23.28 2.19 c
+23.456 2.132 23.607 2.047 23.736 1.941 c
+23.861 1.841 23.963 1.72 24.044 1.573 c
+24.122 1.433 24.176 1.282 24.206 1.118 c
+23.295 1.073 l
+23.265 1.249 23.196 1.389 23.089 1.5 c
+22.99 1.606 22.846 1.661 22.662 1.661 c
+22.416 1.661 22.24 1.558 22.134 1.353 c
+22.023 1.154 21.972 0.867 21.972 0.485 c
+21.972 -0.309 22.207 -0.706 22.677 -0.706 c
+22.843 -0.706 22.986 -0.654 23.104 -0.544 c
+23.221 -0.437 23.295 -0.276 23.324 -0.058 c
+24.235 -0.103 l
+24.206 -0.272 24.151 -0.426 24.074 -0.573 c
+24.004 -0.721 23.901 -0.852 23.765 -0.97 c
+23.636 -1.08 23.478 -1.168 23.295 -1.234 c
+23.119 -1.293 22.913 -1.323 22.677 -1.323 c
+28.384 0.485 m
+28.384 0.21 28.347 -0.04 28.281 -0.264 c
+28.212 -0.482 28.108 -0.669 27.973 -0.823 c
+27.833 -0.981 27.657 -1.103 27.443 -1.191 c
+27.227 -1.278 26.973 -1.323 26.679 -1.323 c
+26.404 -1.323 26.157 -1.278 25.944 -1.191 c
+25.739 -1.103 25.566 -0.981 25.43 -0.823 c
+25.29 -0.669 25.187 -0.482 25.121 -0.264 c
+25.051 -0.04 25.018 0.21 25.018 0.485 c
+25.018 0.738 25.047 0.974 25.106 1.191 c
+25.172 1.415 25.275 1.606 25.415 1.764 c
+25.55 1.929 25.727 2.058 25.944 2.146 c
+26.157 2.234 26.414 2.278 26.709 2.278 c
+27.021 2.278 27.281 2.234 27.487 2.146 c
+27.701 2.058 27.873 1.929 28.002 1.764 c
+28.138 1.606 28.237 1.415 28.296 1.191 c
+28.355 0.974 28.384 0.738 28.384 0.485 c
+27.429 0.485 m
+27.429 0.69 27.414 0.867 27.385 1.014 c
+27.362 1.161 27.326 1.282 27.267 1.382 c
+27.208 1.477 27.135 1.548 27.046 1.588 c
+26.959 1.635 26.848 1.661 26.724 1.661 c
+26.458 1.661 26.267 1.562 26.15 1.367 c
+26.032 1.18 25.974 0.885 25.974 0.485 c
+25.974 0.062 26.032 -0.243 26.15 -0.426 c
+26.267 -0.613 26.444 -0.706 26.679 -0.706 c
+26.804 -0.706 26.918 -0.687 27.017 -0.646 c
+27.113 -0.599 27.194 -0.526 27.252 -0.426 c
+27.318 -0.331 27.362 -0.206 27.385 -0.058 c
+27.414 0.088 27.429 0.268 27.429 0.485 c
+31.43 -1.264 m
+31.43 0.721 l
+31.43 1.022 31.387 1.242 31.298 1.382 c
+31.217 1.529 31.081 1.602 30.886 1.602 c
+30.776 1.602 30.674 1.577 30.578 1.529 c
+30.489 1.477 30.409 1.411 30.343 1.323 c
+30.284 1.235 30.233 1.124 30.196 1 c
+30.167 0.882 30.152 0.75 30.152 0.603 c
+30.152 -1.264 l
+29.24 -1.264 l
+29.24 1.44 l
+29.24 1.661 l
+29.24 1.749 29.233 1.826 29.226 1.897 c
+29.226 2.087 l
+29.226 2.219 l
+30.078 2.219 l
+30.086 2.19 30.093 2.146 30.093 2.087 c
+30.093 1.897 l
+30.1 1.826 30.107 1.756 30.107 1.691 c
+30.115 1.621 30.122 1.565 30.122 1.529 c
+30.137 1.529 l
+30.254 1.793 30.406 1.984 30.593 2.102 c
+30.776 2.219 30.996 2.278 31.254 2.278 c
+31.438 2.278 31.599 2.249 31.74 2.19 c
+31.875 2.132 31.989 2.043 32.077 1.926 c
+32.166 1.808 32.228 1.665 32.268 1.5 c
+32.316 1.341 32.342 1.154 32.342 0.941 c
+32.342 -1.264 l
+h
+34.951 1.602 m
+34.951 -1.264 l
+34.054 -1.264 l
+34.054 1.602 l
+33.232 1.602 l
+33.232 2.219 l
+34.054 2.219 l
+34.054 2.484 l
+34.054 2.609 34.069 2.741 34.098 2.882 c
+34.135 3.017 34.205 3.135 34.304 3.234 c
+34.41 3.341 34.554 3.429 34.73 3.499 c
+34.907 3.564 35.131 3.601 35.407 3.601 c
+35.62 3.601 35.819 3.59 35.995 3.572 c
+36.17 3.549 36.322 3.532 36.45 3.513 c
+36.45 2.925 l
+36.322 2.944 36.178 2.959 36.024 2.969 c
+35.866 2.977 35.715 2.984 35.568 2.984 c
+35.44 2.984 35.337 2.969 35.26 2.94 c
+35.179 2.911 35.116 2.87 35.069 2.822 c
+35.017 2.77 34.984 2.708 34.965 2.631 c
+34.955 2.562 34.951 2.484 34.951 2.396 c
+34.951 2.219 l
+36.377 2.219 l
+36.377 1.602 l
+h
+39.467 -0.646 m
+40.599 -0.646 l
+40.599 -1.264 l
+37.292 -1.264 l
+37.292 -0.646 l
+38.556 -0.646 l
+38.556 1.602 l
+37.63 1.602 l
+37.63 2.219 l
+39.467 2.219 l
+h
+38.556 3.513 0.911 -0.675 re
+38.556 2.837 m
+42.954 -2.66 m
+42.738 -2.66 42.547 -2.635 42.381 -2.587 c
+42.212 -2.547 42.073 -2.484 41.955 -2.396 c
+41.837 -2.315 41.738 -2.219 41.661 -2.102 c
+41.591 -1.984 41.543 -1.856 41.514 -1.72 c
+42.41 -1.617 l
+42.447 -1.753 42.518 -1.86 42.616 -1.94 c
+42.712 -2.028 42.837 -2.072 42.984 -2.072 c
+43.072 -2.072 43.153 -2.057 43.234 -2.028 c
+43.311 -1.999 43.381 -1.944 43.44 -1.866 c
+43.499 -1.797 43.542 -1.705 43.572 -1.587 c
+43.609 -1.469 43.631 -1.323 43.631 -1.147 c
+43.631 -0.956 l
+43.631 -0.889 43.634 -0.831 43.646 -0.779 c
+43.646 -0.588 l
+43.631 -0.588 l
+43.532 -0.816 43.388 -0.977 43.204 -1.073 c
+43.017 -1.172 42.811 -1.22 42.587 -1.22 c
+42.381 -1.22 42.198 -1.183 42.044 -1.103 c
+41.896 -1.014 41.768 -0.897 41.661 -0.75 c
+41.562 -0.595 41.488 -0.411 41.441 -0.206 c
+41.389 0.008 41.367 0.243 41.367 0.5 c
+41.367 0.771 41.389 1.018 41.441 1.235 c
+41.5 1.448 41.58 1.631 41.691 1.779 c
+41.797 1.933 41.93 2.051 42.088 2.132 c
+42.242 2.219 42.429 2.263 42.646 2.263 c
+42.742 2.263 42.84 2.253 42.94 2.234 c
+43.035 2.213 43.123 2.179 43.204 2.132 c
+43.293 2.08 43.37 2.018 43.44 1.941 c
+43.517 1.86 43.58 1.768 43.631 1.661 c
+43.646 1.661 l
+43.646 1.808 l
+43.653 1.866 43.66 1.918 43.66 1.97 c
+43.667 2.028 43.675 2.076 43.675 2.117 c
+43.682 2.165 43.694 2.198 43.704 2.219 c
+44.557 2.219 l
+44.546 2.138 44.535 2.028 44.527 1.882 c
+44.527 1.411 l
+44.527 -1.161 l
+44.527 -1.415 44.49 -1.635 44.425 -1.822 c
+44.355 -2.007 44.251 -2.161 44.116 -2.278 c
+43.976 -2.404 43.811 -2.499 43.616 -2.558 c
+43.418 -2.624 43.197 -2.66 42.954 -2.66 c
+43.646 0.53 m
+43.646 0.742 43.619 0.919 43.572 1.058 c
+43.532 1.205 43.476 1.323 43.41 1.411 c
+43.351 1.5 43.282 1.558 43.204 1.588 c
+43.123 1.625 43.046 1.646 42.969 1.646 c
+42.87 1.646 42.778 1.621 42.69 1.573 c
+42.609 1.532 42.543 1.463 42.484 1.367 c
+42.433 1.278 42.389 1.161 42.352 1.014 c
+42.323 0.875 42.308 0.706 42.308 0.5 c
+42.308 0.125 42.366 -0.154 42.484 -0.338 c
+42.601 -0.515 42.763 -0.603 42.969 -0.603 c
+43.035 -0.603 43.109 -0.588 43.189 -0.559 c
+43.278 -0.522 43.351 -0.463 43.41 -0.382 c
+43.476 -0.294 43.532 -0.176 43.572 -0.029 c
+43.619 0.118 43.646 0.301 43.646 0.53 c
+50.135 0.838 1.867 -0.794 re
+50.135 0.044 m
+54.2 0.838 1.866 -0.794 re
+54.2 0.044 m
+59.201 -2.66 m
+58.983 -2.66 58.792 -2.635 58.628 -2.587 c
+58.458 -2.547 58.318 -2.484 58.201 -2.396 c
+58.083 -2.315 57.984 -2.219 57.907 -2.102 c
+57.838 -1.984 57.79 -1.856 57.76 -1.72 c
+58.657 -1.617 l
+58.694 -1.753 58.763 -1.86 58.863 -1.94 c
+58.958 -2.028 59.083 -2.072 59.23 -2.072 c
+59.318 -2.072 59.399 -2.057 59.48 -2.028 c
+59.557 -1.999 59.627 -1.944 59.686 -1.866 c
+59.744 -1.797 59.789 -1.705 59.818 -1.587 c
+59.855 -1.469 59.877 -1.323 59.877 -1.147 c
+59.877 -0.956 l
+59.877 -0.889 59.881 -0.831 59.891 -0.779 c
+59.891 -0.588 l
+59.877 -0.588 l
+59.777 -0.816 59.634 -0.977 59.451 -1.073 c
+59.263 -1.172 59.058 -1.22 58.833 -1.22 c
+58.628 -1.22 58.443 -1.183 58.289 -1.103 c
+58.142 -1.014 58.014 -0.897 57.907 -0.75 c
+57.807 -0.595 57.734 -0.411 57.687 -0.206 c
+57.635 0.008 57.613 0.243 57.613 0.5 c
+57.613 0.771 57.635 1.018 57.687 1.235 c
+57.745 1.448 57.826 1.631 57.936 1.779 c
+58.043 1.933 58.175 2.051 58.333 2.132 c
+58.488 2.219 58.675 2.263 58.892 2.263 c
+58.987 2.263 59.087 2.253 59.186 2.234 c
+59.282 2.213 59.37 2.179 59.451 2.132 c
+59.538 2.08 59.616 2.018 59.686 1.941 c
+59.763 1.86 59.825 1.768 59.877 1.661 c
+59.891 1.661 l
+59.891 1.808 l
+59.899 1.866 59.906 1.918 59.906 1.97 c
+59.914 2.028 59.921 2.076 59.921 2.117 c
+59.928 2.165 59.939 2.198 59.951 2.219 c
+60.803 2.219 l
+60.792 2.138 60.781 2.028 60.774 1.882 c
+60.774 1.411 l
+60.774 -1.161 l
+60.774 -1.415 60.737 -1.635 60.67 -1.822 c
+60.601 -2.007 60.498 -2.161 60.362 -2.278 c
+60.222 -2.404 60.057 -2.499 59.862 -2.558 c
+59.664 -2.624 59.443 -2.66 59.201 -2.66 c
+59.891 0.53 m
+59.891 0.742 59.866 0.919 59.818 1.058 c
+59.777 1.205 59.723 1.323 59.656 1.411 c
+59.598 1.5 59.528 1.558 59.451 1.588 c
+59.37 1.625 59.293 1.646 59.216 1.646 c
+59.116 1.646 59.024 1.621 58.936 1.573 c
+58.855 1.532 58.789 1.463 58.73 1.367 c
+58.679 1.278 58.634 1.161 58.598 1.014 c
+58.569 0.875 58.553 0.706 58.553 0.5 c
+58.553 0.125 58.613 -0.154 58.73 -0.338 c
+58.848 -0.515 59.01 -0.603 59.216 -0.603 c
+59.282 -0.603 59.355 -0.588 59.436 -0.559 c
+59.524 -0.522 59.598 -0.463 59.656 -0.382 c
+59.723 -0.294 59.777 -0.176 59.818 -0.029 c
+59.866 0.118 59.891 0.301 59.891 0.53 c
+63.835 -0.646 m
+64.967 -0.646 l
+64.967 -1.264 l
+61.659 -1.264 l
+61.659 -0.646 l
+62.923 -0.646 l
+62.923 2.896 l
+61.998 2.896 l
+61.998 3.513 l
+63.835 3.513 l
+h
+68.998 0.485 m
+68.998 0.21 68.961 -0.04 68.895 -0.264 c
+68.824 -0.482 68.722 -0.669 68.586 -0.823 c
+68.446 -0.981 68.27 -1.103 68.057 -1.191 c
+67.84 -1.278 67.586 -1.323 67.293 -1.323 c
+67.017 -1.323 66.771 -1.278 66.557 -1.191 c
+66.352 -1.103 66.179 -0.981 66.043 -0.823 c
+65.903 -0.669 65.801 -0.482 65.734 -0.264 c
+65.665 -0.04 65.632 0.21 65.632 0.485 c
+65.632 0.738 65.661 0.974 65.72 1.191 c
+65.786 1.415 65.888 1.606 66.029 1.764 c
+66.164 1.929 66.341 2.058 66.557 2.146 c
+66.771 2.234 67.028 2.278 67.322 2.278 c
+67.634 2.278 67.895 2.234 68.101 2.146 c
+68.314 2.058 68.487 1.929 68.616 1.764 c
+68.751 1.606 68.851 1.415 68.909 1.191 c
+68.968 0.974 68.998 0.738 68.998 0.485 c
+68.042 0.485 m
+68.042 0.69 68.028 0.867 67.999 1.014 c
+67.976 1.161 67.939 1.282 67.881 1.382 c
+67.822 1.477 67.748 1.548 67.66 1.588 c
+67.571 1.635 67.461 1.661 67.336 1.661 c
+67.072 1.661 66.881 1.562 66.763 1.367 c
+66.646 1.18 66.587 0.885 66.587 0.485 c
+66.587 0.062 66.646 -0.243 66.763 -0.426 c
+66.881 -0.613 67.058 -0.706 67.293 -0.706 c
+67.417 -0.706 67.531 -0.687 67.631 -0.646 c
+67.726 -0.599 67.807 -0.526 67.866 -0.426 c
+67.932 -0.331 67.976 -0.206 67.999 -0.058 c
+68.028 0.088 68.042 0.268 68.042 0.485 c
+69.854 -1.264 m
+69.854 -0.97 l
+69.861 -0.834 69.869 -0.676 69.869 -0.5 c
+69.869 3.513 l
+70.78 3.513 l
+70.78 2.234 l
+70.78 2.072 l
+70.78 1.897 l
+70.78 1.845 70.773 1.801 70.765 1.764 c
+70.765 1.675 l
+70.78 1.675 l
+70.827 1.783 70.89 1.874 70.971 1.955 c
+71.048 2.032 71.133 2.095 71.221 2.146 c
+71.309 2.194 71.401 2.227 71.5 2.249 c
+71.596 2.267 71.695 2.278 71.794 2.278 c
+72.007 2.278 72.194 2.234 72.352 2.146 c
+72.507 2.058 72.635 1.929 72.735 1.764 c
+72.841 1.606 72.919 1.415 72.97 1.191 c
+73.017 0.974 73.044 0.735 73.044 0.47 c
+73.044 0.214 73.015 -0.025 72.955 -0.25 c
+72.897 -0.467 72.812 -0.658 72.705 -0.823 c
+72.595 -0.981 72.463 -1.103 72.309 -1.191 c
+72.151 -1.278 71.97 -1.323 71.764 -1.323 c
+71.666 -1.323 71.567 -1.311 71.471 -1.293 c
+71.382 -1.272 71.294 -1.242 71.206 -1.191 c
+71.118 -1.132 71.037 -1.066 70.971 -0.985 c
+70.901 -0.908 70.839 -0.808 70.78 -0.69 c
+70.765 -0.69 l
+70.765 -0.852 l
+70.765 -0.912 70.758 -0.97 70.75 -1.029 c
+70.75 -1.08 70.743 -1.128 70.736 -1.176 c
+70.736 -1.216 70.729 -1.246 70.721 -1.264 c
+h
+70.765 0.5 m
+70.765 0.264 70.783 0.066 70.824 -0.088 c
+70.872 -0.246 70.931 -0.368 71 -0.455 c
+71.066 -0.544 71.14 -0.61 71.221 -0.646 c
+71.298 -0.687 71.375 -0.706 71.456 -0.706 c
+71.662 -0.706 71.816 -0.61 71.926 -0.411 c
+72.044 -0.216 72.103 0.077 72.103 0.47 c
+72.103 0.684 72.08 0.867 72.044 1.014 c
+72.015 1.168 71.97 1.294 71.912 1.382 c
+71.86 1.477 71.794 1.55 71.706 1.602 c
+71.625 1.65 71.537 1.675 71.442 1.675 c
+71.361 1.675 71.284 1.654 71.206 1.617 c
+71.126 1.577 71.052 1.514 70.985 1.426 c
+70.927 1.338 70.872 1.213 70.824 1.058 c
+70.783 0.912 70.765 0.724 70.765 0.5 c
+74.873 -1.323 m
+74.704 -1.323 74.554 -1.301 74.418 -1.264 c
+74.289 -1.216 74.175 -1.147 74.079 -1.058 c
+73.992 -0.97 73.921 -0.864 73.874 -0.735 c
+73.823 -0.599 73.8 -0.449 73.8 -0.279 c
+73.8 -0.073 73.834 0.096 73.904 0.235 c
+73.969 0.382 74.065 0.492 74.183 0.573 c
+74.308 0.661 74.451 0.724 74.609 0.765 c
+74.775 0.802 74.951 0.827 75.138 0.838 c
+75.858 0.852 l
+75.858 1.029 l
+75.858 1.147 75.847 1.249 75.829 1.338 c
+75.807 1.426 75.774 1.492 75.726 1.544 c
+75.685 1.602 75.638 1.639 75.579 1.661 c
+75.521 1.679 75.454 1.691 75.388 1.691 c
+75.318 1.691 75.255 1.679 75.197 1.661 c
+75.145 1.65 75.097 1.625 75.05 1.588 c
+75.01 1.558 74.976 1.507 74.947 1.44 c
+74.925 1.382 74.91 1.301 74.903 1.205 c
+73.962 1.249 l
+73.992 1.396 74.035 1.532 74.095 1.661 c
+74.16 1.786 74.256 1.897 74.374 1.984 c
+74.492 2.08 74.631 2.153 74.8 2.205 c
+74.976 2.253 75.182 2.278 75.417 2.278 c
+75.858 2.278 76.19 2.168 76.417 1.955 c
+76.652 1.749 76.77 1.44 76.77 1.029 c
+76.77 -0.235 l
+76.77 -0.455 l
+76.777 -0.515 76.792 -0.569 76.814 -0.617 c
+76.832 -0.658 76.861 -0.69 76.902 -0.721 c
+76.938 -0.742 76.99 -0.75 77.049 -0.75 c
+77.115 -0.75 77.185 -0.746 77.254 -0.735 c
+77.254 -1.22 l
+77.196 -1.231 77.141 -1.242 77.093 -1.249 c
+77.053 -1.261 77.013 -1.268 76.976 -1.278 c
+76.936 -1.286 76.891 -1.293 76.843 -1.293 c
+76.792 -1.301 76.733 -1.309 76.666 -1.309 c
+76.439 -1.309 76.273 -1.257 76.167 -1.147 c
+76.057 -1.029 75.995 -0.864 75.976 -0.646 c
+75.961 -0.646 l
+75.891 -0.757 75.822 -0.852 75.756 -0.941 c
+75.685 -1.022 75.608 -1.087 75.521 -1.147 c
+75.432 -1.205 75.333 -1.249 75.226 -1.278 c
+75.127 -1.309 75.01 -1.323 74.873 -1.323 c
+75.858 0.353 m
+75.432 0.338 l
+75.333 0.338 75.241 0.331 75.153 0.324 c
+75.072 0.312 75.006 0.287 74.947 0.25 c
+74.889 0.21 74.837 0.151 74.8 0.073 c
+74.76 0.004 74.741 -0.088 74.741 -0.206 c
+74.741 -0.374 74.775 -0.496 74.844 -0.573 c
+74.91 -0.654 75.01 -0.69 75.138 -0.69 c
+75.245 -0.69 75.344 -0.669 75.432 -0.617 c
+75.527 -0.569 75.608 -0.507 75.667 -0.426 c
+75.733 -0.349 75.785 -0.261 75.814 -0.162 c
+75.843 -0.056 75.858 0.059 75.858 0.177 c
+h
+80.08 -0.646 m
+81.212 -0.646 l
+81.212 -1.264 l
+77.905 -1.264 l
+77.905 -0.646 l
+79.17 -0.646 l
+79.17 2.896 l
+78.243 2.896 l
+78.243 3.513 l
+80.08 3.513 l
+h
+87.658 -1.323 m
+87.371 -1.323 87.128 -1.282 86.923 -1.205 c
+86.717 -1.117 86.544 -0.996 86.409 -0.838 c
+86.269 -0.684 86.166 -0.496 86.1 -0.279 c
+86.03 -0.056 85.997 0.191 85.997 0.455 c
+85.997 0.75 86.03 1.007 86.1 1.235 c
+86.177 1.459 86.284 1.646 86.423 1.793 c
+86.571 1.947 86.747 2.065 86.953 2.146 c
+87.159 2.234 87.394 2.278 87.658 2.278 c
+87.882 2.278 88.084 2.249 88.261 2.19 c
+88.437 2.132 88.588 2.047 88.716 1.941 c
+88.842 1.841 88.944 1.72 89.025 1.573 c
+89.102 1.433 89.158 1.282 89.187 1.118 c
+88.275 1.073 l
+88.246 1.249 88.176 1.389 88.069 1.5 c
+87.97 1.606 87.827 1.661 87.643 1.661 c
+87.398 1.661 87.221 1.558 87.114 1.353 c
+87.004 1.154 86.953 0.867 86.953 0.485 c
+86.953 -0.309 87.188 -0.706 87.658 -0.706 c
+87.824 -0.706 87.967 -0.654 88.084 -0.544 c
+88.202 -0.437 88.275 -0.276 88.304 -0.058 c
+89.216 -0.103 l
+89.187 -0.272 89.131 -0.426 89.054 -0.573 c
+88.985 -0.721 88.882 -0.852 88.746 -0.97 c
+88.617 -1.08 88.46 -1.168 88.275 -1.234 c
+88.099 -1.293 87.893 -1.323 87.658 -1.323 c
+93.365 0.485 m
+93.365 0.21 93.328 -0.04 93.262 -0.264 c
+93.192 -0.482 93.089 -0.669 92.954 -0.823 c
+92.813 -0.981 92.637 -1.103 92.424 -1.191 c
+92.208 -1.278 91.954 -1.323 91.66 -1.323 c
+91.384 -1.323 91.138 -1.278 90.925 -1.191 c
+90.719 -1.103 90.546 -0.981 90.411 -0.823 c
+90.271 -0.669 90.168 -0.482 90.102 -0.264 c
+90.032 -0.04 89.999 0.21 89.999 0.485 c
+89.999 0.738 90.028 0.974 90.087 1.191 c
+90.153 1.415 90.256 1.606 90.396 1.764 c
+90.532 1.929 90.708 2.058 90.925 2.146 c
+91.138 2.234 91.396 2.278 91.689 2.278 c
+92.001 2.278 92.263 2.234 92.468 2.146 c
+92.682 2.058 92.854 1.929 92.983 1.764 c
+93.119 1.606 93.218 1.415 93.277 1.191 c
+93.335 0.974 93.365 0.738 93.365 0.485 c
+92.41 0.485 m
+92.41 0.69 92.395 0.867 92.366 1.014 c
+92.343 1.161 92.306 1.282 92.248 1.382 c
+92.189 1.477 92.115 1.548 92.028 1.588 c
+91.939 1.635 91.829 1.661 91.704 1.661 c
+91.439 1.661 91.248 1.562 91.13 1.367 c
+91.013 1.18 90.954 0.885 90.954 0.485 c
+90.954 0.062 91.013 -0.243 91.13 -0.426 c
+91.248 -0.613 91.425 -0.706 91.66 -0.706 c
+91.785 -0.706 91.899 -0.687 91.998 -0.646 c
+92.094 -0.599 92.175 -0.526 92.233 -0.426 c
+92.3 -0.331 92.343 -0.206 92.366 -0.058 c
+92.395 0.088 92.41 0.268 92.41 0.485 c
+96.327 -0.646 m
+97.459 -0.646 l
+97.459 -1.264 l
+94.151 -1.264 l
+94.151 -0.646 l
+95.415 -0.646 l
+95.415 2.896 l
+94.49 2.896 l
+94.49 3.513 l
+96.327 3.513 l
+h
+101.49 0.485 m
+101.49 0.21 101.453 -0.04 101.387 -0.264 c
+101.317 -0.482 101.214 -0.669 101.079 -0.823 c
+100.939 -0.981 100.763 -1.103 100.549 -1.191 c
+100.333 -1.278 100.079 -1.323 99.785 -1.323 c
+99.51 -1.323 99.263 -1.278 99.049 -1.191 c
+98.844 -1.103 98.671 -0.981 98.535 -0.823 c
+98.396 -0.669 98.293 -0.482 98.226 -0.264 c
+98.157 -0.04 98.124 0.21 98.124 0.485 c
+98.124 0.738 98.153 0.974 98.212 1.191 c
+98.278 1.415 98.381 1.606 98.521 1.764 c
+98.656 1.929 98.833 2.058 99.049 2.146 c
+99.263 2.234 99.52 2.278 99.814 2.278 c
+100.127 2.278 100.387 2.234 100.593 2.146 c
+100.807 2.058 100.979 1.929 101.108 1.764 c
+101.243 1.606 101.343 1.415 101.401 1.191 c
+101.461 0.974 101.49 0.738 101.49 0.485 c
+100.534 0.485 m
+100.534 0.69 100.52 0.867 100.491 1.014 c
+100.468 1.161 100.431 1.282 100.373 1.382 c
+100.314 1.477 100.24 1.548 100.152 1.588 c
+100.064 1.635 99.954 1.661 99.828 1.661 c
+99.564 1.661 99.373 1.562 99.256 1.367 c
+99.138 1.18 99.08 0.885 99.08 0.485 c
+99.08 0.062 99.138 -0.243 99.256 -0.426 c
+99.373 -0.613 99.55 -0.706 99.785 -0.706 c
+99.909 -0.706 100.024 -0.687 100.123 -0.646 c
+100.219 -0.599 100.299 -0.526 100.358 -0.426 c
+100.424 -0.331 100.468 -0.206 100.491 -0.058 c
+100.52 0.088 100.534 0.268 100.534 0.485 c
+105.359 1.47 m
+105.26 1.477 105.158 1.488 105.05 1.5 c
+104.94 1.517 104.819 1.529 104.684 1.529 c
+104.507 1.529 104.349 1.488 104.213 1.411 c
+104.073 1.341 103.955 1.242 103.86 1.118 c
+103.772 0.989 103.702 0.842 103.654 0.676 c
+103.614 0.507 103.595 0.331 103.595 0.147 c
+103.595 -1.264 l
+102.699 -1.264 l
+102.699 0.985 l
+102.699 1.11 102.688 1.235 102.669 1.353 c
+102.658 1.477 102.644 1.595 102.625 1.706 c
+102.614 1.823 102.6 1.918 102.581 1.999 c
+102.559 2.087 102.541 2.161 102.523 2.219 c
+103.404 2.219 l
+103.412 2.168 103.423 2.117 103.433 2.058 c
+103.452 1.999 103.467 1.933 103.478 1.866 c
+103.496 1.808 103.511 1.742 103.522 1.675 c
+103.529 1.606 103.541 1.544 103.552 1.484 c
+103.566 1.484 l
+103.603 1.602 103.654 1.708 103.713 1.808 c
+103.78 1.903 103.86 1.988 103.948 2.058 c
+104.036 2.124 104.139 2.179 104.257 2.219 c
+104.382 2.256 104.529 2.278 104.698 2.278 c
+104.823 2.278 104.94 2.271 105.05 2.263 c
+105.168 2.253 105.272 2.238 105.359 2.219 c
+h
+107.465 -0.279 0.926 -0.985 re
+107.465 -1.264 m
+111.35 2.219 m
+111.35 0.264 l
+111.35 0.125 111.356 0 111.379 -0.118 c
+111.397 -0.228 111.43 -0.32 111.481 -0.397 c
+111.529 -0.478 111.588 -0.54 111.658 -0.588 c
+111.724 -0.628 111.809 -0.646 111.908 -0.646 c
+111.996 -0.646 112.077 -0.628 112.158 -0.588 c
+112.246 -0.54 112.32 -0.47 112.378 -0.382 c
+112.437 -0.287 112.481 -0.176 112.511 -0.058 c
+112.547 0.066 112.569 0.206 112.569 0.353 c
+112.569 2.219 l
+113.466 2.219 l
+113.466 -0.485 l
+113.466 -0.721 l
+113.473 -0.802 113.48 -0.879 113.48 -0.956 c
+113.48 -1.147 l
+113.488 -1.198 113.495 -1.234 113.495 -1.264 c
+112.643 -1.264 l
+112.632 -1.234 112.621 -1.198 112.613 -1.147 c
+112.613 -0.956 l
+112.613 -0.889 112.606 -0.819 112.599 -0.75 c
+112.599 -0.573 l
+112.584 -0.573 l
+112.466 -0.838 112.312 -1.029 112.129 -1.147 c
+111.952 -1.264 111.749 -1.323 111.526 -1.323 c
+111.32 -1.323 111.147 -1.286 111.011 -1.22 c
+110.872 -1.154 110.762 -1.058 110.673 -0.941 c
+110.592 -0.823 110.533 -0.687 110.496 -0.529 c
+110.467 -0.364 110.452 -0.187 110.452 0 c
+110.452 2.219 l
+h
+116.634 -0.646 m
+117.766 -0.646 l
+117.766 -1.264 l
+114.458 -1.264 l
+114.458 -0.646 l
+115.722 -0.646 l
+115.722 1.602 l
+114.797 1.602 l
+114.797 2.219 l
+116.634 2.219 l
+h
+115.722 3.513 0.912 -0.675 re
+115.722 2.837 m
+123.608 -1.323 m
+123.439 -1.323 123.289 -1.301 123.152 -1.264 c
+123.024 -1.216 122.91 -1.147 122.814 -1.058 c
+122.726 -0.97 122.656 -0.864 122.608 -0.735 c
+122.558 -0.599 122.535 -0.449 122.535 -0.279 c
+122.535 -0.073 122.568 0.096 122.639 0.235 c
+122.704 0.382 122.799 0.492 122.917 0.573 c
+123.042 0.661 123.186 0.724 123.344 0.765 c
+123.509 0.802 123.686 0.827 123.873 0.838 c
+124.593 0.852 l
+124.593 1.029 l
+124.593 1.147 124.582 1.249 124.563 1.338 c
+124.542 1.426 124.509 1.492 124.461 1.544 c
+124.42 1.602 124.372 1.639 124.314 1.661 c
+124.255 1.679 124.189 1.691 124.123 1.691 c
+124.052 1.691 123.99 1.679 123.932 1.661 c
+123.88 1.65 123.832 1.625 123.784 1.588 c
+123.744 1.558 123.711 1.507 123.682 1.44 c
+123.659 1.382 123.645 1.301 123.638 1.205 c
+122.697 1.249 l
+122.726 1.396 122.77 1.532 122.829 1.661 c
+122.895 1.786 122.991 1.897 123.109 1.984 c
+123.227 2.08 123.366 2.153 123.535 2.205 c
+123.711 2.253 123.917 2.278 124.152 2.278 c
+124.593 2.278 124.924 2.168 125.151 1.955 c
+125.386 1.749 125.504 1.44 125.504 1.029 c
+125.504 -0.235 l
+125.504 -0.455 l
+125.512 -0.515 125.527 -0.569 125.548 -0.617 c
+125.567 -0.658 125.596 -0.69 125.637 -0.721 c
+125.673 -0.742 125.725 -0.75 125.783 -0.75 c
+125.85 -0.75 125.92 -0.746 125.989 -0.735 c
+125.989 -1.22 l
+125.931 -1.231 125.876 -1.242 125.828 -1.249 c
+125.787 -1.261 125.747 -1.268 125.71 -1.278 c
+125.67 -1.286 125.625 -1.293 125.578 -1.293 c
+125.527 -1.301 125.467 -1.309 125.401 -1.309 c
+125.174 -1.309 125.008 -1.257 124.902 -1.147 c
+124.792 -1.029 124.729 -0.864 124.711 -0.646 c
+124.696 -0.646 l
+124.626 -0.757 124.557 -0.852 124.49 -0.941 c
+124.42 -1.022 124.343 -1.087 124.255 -1.147 c
+124.167 -1.205 124.068 -1.249 123.961 -1.278 c
+123.861 -1.309 123.744 -1.323 123.608 -1.323 c
+124.593 0.353 m
+124.167 0.338 l
+124.068 0.338 123.975 0.331 123.888 0.324 c
+123.807 0.312 123.74 0.287 123.682 0.25 c
+123.623 0.21 123.572 0.151 123.535 0.073 c
+123.495 0.004 123.476 -0.088 123.476 -0.206 c
+123.476 -0.374 123.509 -0.496 123.579 -0.573 c
+123.645 -0.654 123.744 -0.69 123.873 -0.69 c
+123.979 -0.69 124.079 -0.669 124.167 -0.617 c
+124.262 -0.569 124.343 -0.507 124.402 -0.426 c
+124.468 -0.349 124.52 -0.261 124.549 -0.162 c
+124.578 -0.056 124.593 0.059 124.593 0.177 c
+h
+127.595 2.219 m
+127.595 0.264 l
+127.595 0.125 127.603 0 127.624 -0.118 c
+127.643 -0.228 127.676 -0.32 127.728 -0.397 c
+127.776 -0.478 127.834 -0.54 127.904 -0.588 c
+127.971 -0.628 128.054 -0.646 128.154 -0.646 c
+128.243 -0.646 128.323 -0.628 128.403 -0.588 c
+128.492 -0.54 128.565 -0.47 128.624 -0.382 c
+128.683 -0.287 128.727 -0.176 128.756 -0.058 c
+128.794 0.066 128.815 0.206 128.815 0.353 c
+128.815 2.219 l
+129.712 2.219 l
+129.712 -0.485 l
+129.712 -0.721 l
+129.719 -0.802 129.727 -0.879 129.727 -0.956 c
+129.727 -1.147 l
+129.735 -1.198 129.741 -1.234 129.741 -1.264 c
+128.889 -1.264 l
+128.878 -1.234 128.867 -1.198 128.86 -1.147 c
+128.86 -0.956 l
+128.86 -0.889 128.852 -0.819 128.845 -0.75 c
+128.845 -0.573 l
+128.83 -0.573 l
+128.713 -0.838 128.559 -1.029 128.374 -1.147 c
+128.198 -1.264 127.996 -1.323 127.772 -1.323 c
+127.566 -1.323 127.393 -1.286 127.258 -1.22 c
+127.117 -1.154 127.007 -1.058 126.919 -0.941 c
+126.838 -0.823 126.78 -0.687 126.743 -0.529 c
+126.714 -0.364 126.699 -0.187 126.699 0 c
+126.699 2.219 l
+h
+131.377 1.602 m
+130.833 1.602 l
+130.833 2.219 l
+131.42 2.219 l
+131.7 3.117 l
+132.274 3.117 l
+132.274 2.219 l
+133.508 2.219 l
+133.508 1.602 l
+132.274 1.602 l
+132.274 -0.103 l
+132.274 -0.324 l
+132.281 -0.393 132.303 -0.455 132.332 -0.515 c
+132.369 -0.566 132.424 -0.61 132.494 -0.646 c
+132.571 -0.676 132.685 -0.69 132.831 -0.69 c
+132.968 -0.69 133.104 -0.687 133.243 -0.676 c
+133.38 -0.658 133.512 -0.632 133.64 -0.603 c
+133.64 -1.205 l
+133.56 -1.216 133.482 -1.231 133.405 -1.249 c
+133.324 -1.261 133.247 -1.268 133.17 -1.278 c
+133.089 -1.286 133.001 -1.293 132.906 -1.293 c
+132.817 -1.301 132.718 -1.309 132.611 -1.309 c
+132.424 -1.309 132.262 -1.293 132.126 -1.264 c
+131.998 -1.228 131.884 -1.183 131.788 -1.132 c
+131.7 -1.084 131.626 -1.025 131.568 -0.956 c
+131.509 -0.879 131.465 -0.802 131.435 -0.721 c
+131.406 -0.632 131.384 -0.544 131.377 -0.455 c
+131.366 -0.36 131.362 -0.264 131.362 -0.176 c
+h
+138.043 0.485 m
+138.043 0.21 138.006 -0.04 137.94 -0.264 c
+137.87 -0.482 137.768 -0.669 137.631 -0.823 c
+137.492 -0.981 137.315 -1.103 137.102 -1.191 c
+136.885 -1.278 136.632 -1.323 136.338 -1.323 c
+136.062 -1.323 135.815 -1.278 135.603 -1.191 c
+135.397 -1.103 135.224 -0.981 135.088 -0.823 c
+134.948 -0.669 134.846 -0.482 134.78 -0.264 c
+134.71 -0.04 134.676 0.21 134.676 0.485 c
+134.676 0.738 134.706 0.974 134.765 1.191 c
+134.831 1.415 134.934 1.606 135.073 1.764 c
+135.21 1.929 135.386 2.058 135.603 2.146 c
+135.815 2.234 136.073 2.278 136.367 2.278 c
+136.679 2.278 136.941 2.234 137.146 2.146 c
+137.359 2.058 137.532 1.929 137.66 1.764 c
+137.797 1.606 137.895 1.415 137.955 1.191 c
+138.013 0.974 138.043 0.738 138.043 0.485 c
+137.087 0.485 m
+137.087 0.69 137.072 0.867 137.043 1.014 c
+137.022 1.161 136.985 1.282 136.926 1.382 c
+136.867 1.477 136.793 1.548 136.705 1.588 c
+136.617 1.635 136.507 1.661 136.382 1.661 c
+136.117 1.661 135.926 1.562 135.809 1.367 c
+135.691 1.18 135.632 0.885 135.632 0.485 c
+135.632 0.062 135.691 -0.243 135.809 -0.426 c
+135.926 -0.613 136.102 -0.706 136.338 -0.706 c
+136.463 -0.706 136.577 -0.687 136.675 -0.646 c
+136.771 -0.599 136.852 -0.526 136.91 -0.426 c
+136.977 -0.331 137.022 -0.206 137.043 -0.058 c
+137.072 0.088 137.087 0.268 137.087 0.485 c
+f
+Q
+q 1 0 0 1 156.4026 334.0088 cm
+0 0 m
+-1.897 0 l
+-1.897 -1.896 l
+0.309 -1.896 l
+0.309 -2.469 l
+-2.573 -2.469 l
+-2.573 2.881 l
+0.278 2.881 l
+0.278 2.308 l
+-1.897 2.308 l
+-1.897 0.588 l
+0 0.588 l
+h
+1.69 1.515 m
+1.705 1.073 l
+1.959 1.415 2.281 1.588 2.675 1.588 c
+3.38 1.588 3.737 1.118 3.748 0.177 c
+3.748 -2.469 l
+3.101 -2.469 l
+3.101 0.147 l
+3.101 0.459 3.046 0.68 2.939 0.809 c
+2.829 0.933 2.675 1 2.468 1 c
+2.311 1 2.164 0.944 2.028 0.838 c
+1.899 0.728 1.797 0.592 1.72 0.426 c
+1.72 -2.469 l
+1.072 -2.469 l
+1.072 1.515 l
+h
+6.761 -2.469 m
+6.721 -2.381 6.695 -2.234 6.688 -2.028 c
+6.453 -2.374 6.158 -2.543 5.805 -2.543 c
+5.442 -2.543 5.159 -2.447 4.953 -2.248 c
+4.755 -2.043 4.659 -1.756 4.659 -1.382 c
+4.659 -0.981 4.795 -0.661 5.071 -0.426 c
+5.342 -0.183 5.718 -0.058 6.188 -0.058 c
+6.673 -0.058 l
+6.673 0.368 l
+6.673 0.603 6.618 0.769 6.511 0.867 c
+6.401 0.974 6.239 1.029 6.026 1.029 c
+5.828 1.029 5.666 0.97 5.541 0.852 c
+5.423 0.735 5.365 0.588 5.365 0.412 c
+4.718 0.412 l
+4.718 0.607 4.777 0.798 4.895 0.985 c
+5.019 1.168 5.181 1.316 5.379 1.426 c
+5.585 1.532 5.813 1.588 6.071 1.588 c
+6.47 1.588 6.776 1.484 6.981 1.279 c
+7.195 1.073 7.309 0.779 7.32 0.397 c
+7.32 -1.617 l
+7.32 -1.922 7.357 -2.186 7.437 -2.41 c
+7.437 -2.469 l
+h
+5.894 -1.955 m
+6.059 -1.955 6.21 -1.911 6.35 -1.822 c
+6.497 -1.735 6.603 -1.624 6.673 -1.484 c
+6.673 -0.544 l
+6.306 -0.544 l
+5.99 -0.544 5.747 -0.613 5.57 -0.75 c
+5.394 -0.878 5.306 -1.066 5.306 -1.308 c
+5.306 -1.536 5.35 -1.701 5.438 -1.808 c
+5.526 -1.907 5.677 -1.955 5.894 -1.955 c
+11.2 -0.676 m
+11.2 -1.294 11.087 -1.76 10.862 -2.072 c
+10.645 -2.389 10.322 -2.543 9.892 -2.543 c
+9.469 -2.543 9.157 -2.363 8.951 -1.999 c
+8.922 -2.469 l
+8.319 -2.469 l
+8.319 3.175 l
+8.966 3.175 l
+8.966 1.073 l
+9.179 1.415 9.487 1.588 9.892 1.588 c
+10.322 1.588 10.645 1.43 10.862 1.118 c
+11.087 0.812 11.2 0.345 11.2 -0.279 c
+h
+10.553 -0.294 m
+10.553 0.177 10.484 0.507 10.348 0.706 c
+10.219 0.9 10.009 1 9.716 1 c
+9.381 1 9.132 0.816 8.966 0.456 c
+8.966 -1.425 l
+9.132 -1.789 9.385 -1.97 9.73 -1.97 c
+10.025 -1.97 10.233 -1.866 10.362 -1.66 c
+10.487 -1.455 10.553 -1.139 10.553 -0.706 c
+h
+12.743 -2.469 -0.646 5.644 re
+15.184 -2.543 m
+14.684 -2.543 14.302 -2.396 14.037 -2.102 c
+13.772 -1.808 13.641 -1.374 13.641 -0.794 c
+13.641 -0.324 l
+13.641 0.272 13.765 0.738 14.023 1.073 c
+14.287 1.415 14.647 1.588 15.11 1.588 c
+15.569 1.588 15.912 1.434 16.139 1.132 c
+16.374 0.838 16.496 0.375 16.506 -0.249 c
+16.506 -0.676 l
+14.287 -0.676 l
+14.287 -0.764 l
+14.287 -1.198 14.364 -1.51 14.522 -1.705 c
+14.688 -1.893 14.919 -1.984 15.213 -1.984 c
+15.407 -1.984 15.581 -1.951 15.727 -1.882 c
+15.874 -1.804 16.01 -1.687 16.139 -1.529 c
+16.477 -1.94 l
+16.19 -2.344 15.76 -2.543 15.184 -2.543 c
+15.11 1.029 m
+14.835 1.029 14.632 0.933 14.507 0.75 c
+14.379 0.563 14.306 0.272 14.287 -0.118 c
+15.86 -0.118 l
+15.86 -0.029 l
+15.837 0.353 15.771 0.621 15.654 0.779 c
+15.536 0.944 15.353 1.029 15.11 1.029 c
+20.946 -1.455 m
+20.946 -1.308 20.891 -1.187 20.784 -1.088 c
+20.674 -0.993 20.468 -0.875 20.167 -0.735 c
+19.821 -0.588 19.579 -0.467 19.432 -0.367 c
+19.285 -0.261 19.174 -0.143 19.108 -0.014 c
+19.039 0.11 19.006 0.268 19.006 0.456 c
+19.006 0.779 19.123 1.047 19.359 1.264 c
+19.594 1.477 19.895 1.588 20.269 1.588 c
+20.652 1.588 20.961 1.474 21.196 1.249 c
+21.431 1.022 21.549 0.735 21.549 0.382 c
+20.901 0.382 l
+20.901 0.559 20.843 0.709 20.725 0.838 c
+20.608 0.963 20.454 1.029 20.269 1.029 c
+20.071 1.029 19.92 0.974 19.814 0.867 c
+19.704 0.769 19.652 0.636 19.652 0.47 c
+19.652 0.341 19.689 0.235 19.77 0.147 c
+19.847 0.066 20.038 -0.037 20.343 -0.162 c
+20.82 -0.349 21.152 -0.536 21.327 -0.72 c
+21.504 -0.897 21.593 -1.124 21.593 -1.396 c
+21.593 -1.749 21.468 -2.028 21.225 -2.234 c
+20.99 -2.44 20.674 -2.543 20.284 -2.543 c
+19.862 -2.543 19.523 -2.425 19.27 -2.19 c
+19.012 -1.947 18.888 -1.643 18.888 -1.278 c
+19.534 -1.278 l
+19.542 -1.506 19.612 -1.683 19.74 -1.808 c
+19.866 -1.926 20.049 -1.984 20.284 -1.984 c
+20.498 -1.984 20.659 -1.936 20.77 -1.837 c
+20.887 -1.741 20.946 -1.613 20.946 -1.455 c
+22.298 -0.294 m
+22.298 0.283 22.434 0.738 22.709 1.073 c
+22.993 1.415 23.363 1.588 23.827 1.588 c
+24.286 1.588 24.654 1.419 24.93 1.087 c
+25.212 0.765 25.36 0.316 25.37 -0.249 c
+25.37 -0.676 l
+25.37 -1.246 25.227 -1.701 24.944 -2.043 c
+24.668 -2.377 24.301 -2.543 23.841 -2.543 c
+23.378 -2.543 23.008 -2.381 22.724 -2.057 c
+22.449 -1.727 22.305 -1.286 22.298 -0.735 c
+h
+22.945 -0.676 m
+22.945 -1.08 23.022 -1.396 23.18 -1.631 c
+23.345 -1.866 23.566 -1.984 23.841 -1.984 c
+24.407 -1.984 24.701 -1.573 24.724 -0.75 c
+24.724 -0.294 l
+24.724 0.106 24.639 0.426 24.473 0.661 c
+24.315 0.904 24.099 1.029 23.827 1.029 c
+23.562 1.029 23.345 0.904 23.18 0.661 c
+23.022 0.426 22.945 0.106 22.945 -0.294 c
+h
+26.825 1.515 m
+26.84 1.147 l
+27.083 1.44 27.402 1.588 27.795 1.588 c
+28.236 1.588 28.545 1.389 28.722 1 c
+28.975 1.389 29.324 1.588 29.765 1.588 c
+30.5 1.588 30.875 1.124 30.897 0.206 c
+30.897 -2.469 l
+30.25 -2.469 l
+30.25 0.147 l
+30.25 0.441 30.195 0.654 30.089 0.794 c
+29.989 0.929 29.817 1 29.574 1 c
+29.376 1 29.214 0.919 29.089 0.765 c
+28.971 0.617 28.901 0.426 28.884 0.191 c
+28.884 -2.469 l
+28.222 -2.469 l
+28.222 0.177 l
+28.222 0.724 28.001 1 27.56 1 c
+27.226 1 26.991 0.838 26.854 0.515 c
+26.854 -2.469 l
+26.208 -2.469 l
+26.208 1.515 l
+h
+33.293 -2.543 m
+32.793 -2.543 32.411 -2.396 32.146 -2.102 c
+31.882 -1.808 31.75 -1.374 31.75 -0.794 c
+31.75 -0.324 l
+31.75 0.272 31.874 0.738 32.132 1.073 c
+32.396 1.415 32.757 1.588 33.219 1.588 c
+33.678 1.588 34.021 1.434 34.249 1.132 c
+34.484 0.838 34.605 0.375 34.615 -0.249 c
+34.615 -0.676 l
+32.396 -0.676 l
+32.396 -0.764 l
+32.396 -1.198 32.473 -1.51 32.631 -1.705 c
+32.797 -1.893 33.028 -1.984 33.322 -1.984 c
+33.517 -1.984 33.69 -1.951 33.837 -1.882 c
+33.983 -1.804 34.12 -1.687 34.249 -1.529 c
+34.586 -1.94 l
+34.299 -2.344 33.869 -2.543 33.293 -2.543 c
+33.219 1.029 m
+32.944 1.029 32.742 0.933 32.616 0.75 c
+32.488 0.563 32.415 0.272 32.396 -0.118 c
+33.969 -0.118 l
+33.969 -0.029 l
+33.947 0.353 33.881 0.621 33.763 0.779 c
+33.646 0.944 33.462 1.029 33.219 1.029 c
+38.496 -1.984 m
+38.71 -1.984 38.883 -1.922 39.011 -1.793 c
+39.147 -1.658 39.22 -1.466 39.232 -1.22 c
+39.849 -1.22 l
+39.826 -1.602 39.691 -1.922 39.437 -2.175 c
+39.18 -2.421 38.868 -2.543 38.496 -2.543 c
+38.004 -2.543 37.63 -2.392 37.364 -2.088 c
+37.107 -1.775 36.982 -1.308 36.982 -0.691 c
+36.982 -0.249 l
+36.982 0.345 37.107 0.801 37.364 1.118 c
+37.63 1.43 38.004 1.588 38.496 1.588 c
+38.897 1.588 39.217 1.455 39.452 1.191 c
+39.694 0.933 39.826 0.588 39.849 0.147 c
+39.232 0.147 l
+39.209 0.441 39.136 0.661 39.011 0.809 c
+38.893 0.956 38.721 1.029 38.496 1.029 c
+38.202 1.029 37.985 0.929 37.85 0.735 c
+37.71 0.547 37.636 0.239 37.63 -0.191 c
+37.63 -0.706 l
+37.63 -1.176 37.695 -1.51 37.835 -1.705 c
+37.981 -1.893 38.202 -1.984 38.496 -1.984 c
+40.466 -0.294 m
+40.466 0.283 40.602 0.738 40.878 1.073 c
+41.16 1.415 41.532 1.588 41.995 1.588 c
+42.454 1.588 42.822 1.419 43.097 1.087 c
+43.38 0.765 43.527 0.316 43.538 -0.249 c
+43.538 -0.676 l
+43.538 -1.246 43.394 -1.701 43.112 -2.043 c
+42.837 -2.377 42.469 -2.543 42.01 -2.543 c
+41.547 -2.543 41.175 -2.381 40.892 -2.057 c
+40.616 -1.727 40.473 -1.286 40.466 -0.735 c
+h
+41.113 -0.676 m
+41.113 -1.08 41.19 -1.396 41.348 -1.631 c
+41.513 -1.866 41.734 -1.984 42.01 -1.984 c
+42.575 -1.984 42.869 -1.573 42.891 -0.75 c
+42.891 -0.294 l
+42.891 0.106 42.806 0.426 42.642 0.661 c
+42.484 0.904 42.266 1.029 41.995 1.029 c
+41.73 1.029 41.513 0.904 41.348 0.661 c
+41.19 0.426 41.113 0.106 41.113 -0.294 c
+h
+45.081 -2.469 -0.646 5.644 re
+45.964 -0.294 m
+45.964 0.283 46.099 0.738 46.375 1.073 c
+46.658 1.415 47.029 1.588 47.492 1.588 c
+47.951 1.588 48.319 1.419 48.595 1.087 c
+48.878 0.765 49.025 0.316 49.035 -0.249 c
+49.035 -0.676 l
+49.035 -1.246 48.892 -1.701 48.609 -2.043 c
+48.333 -2.377 47.966 -2.543 47.507 -2.543 c
+47.044 -2.543 46.673 -2.381 46.39 -2.057 c
+46.114 -1.727 45.971 -1.286 45.964 -0.735 c
+h
+46.61 -0.676 m
+46.61 -1.08 46.687 -1.396 46.845 -1.631 c
+47.011 -1.866 47.232 -1.984 47.507 -1.984 c
+48.073 -1.984 48.367 -1.573 48.389 -0.75 c
+48.389 -0.294 l
+48.389 0.106 48.304 0.426 48.139 0.661 c
+47.981 0.904 47.764 1.029 47.492 1.029 c
+47.228 1.029 47.011 0.904 46.845 0.661 c
+46.687 0.426 46.61 0.106 46.61 -0.294 c
+h
+51.52 0.896 m
+51.431 0.915 51.332 0.927 51.226 0.927 c
+50.891 0.927 50.656 0.742 50.521 0.382 c
+50.521 -2.469 l
+49.873 -2.469 l
+49.873 1.515 l
+50.505 1.515 l
+50.521 1.103 l
+50.696 1.426 50.939 1.588 51.255 1.588 c
+51.362 1.588 51.45 1.565 51.52 1.529 c
+h
+52.872 -2.469 -0.647 3.984 re
+52.917 2.558 m
+52.917 2.448 52.886 2.356 52.828 2.278 c
+52.769 2.209 52.674 2.176 52.549 2.176 c
+52.431 2.176 52.335 2.209 52.269 2.278 c
+52.211 2.356 52.181 2.448 52.181 2.558 c
+52.181 2.675 52.211 2.768 52.269 2.837 c
+52.335 2.914 52.431 2.955 52.549 2.955 c
+52.674 2.955 52.769 2.914 52.828 2.837 c
+52.886 2.756 52.917 2.664 52.917 2.558 c
+54.489 -1.911 m
+56.414 -1.911 l
+56.414 -2.469 l
+53.754 -2.469 l
+53.754 -1.97 l
+55.577 0.941 l
+53.769 0.941 l
+53.769 1.515 l
+56.341 1.515 l
+56.341 1.029 l
+h
+59.236 -2.469 m
+59.196 -2.381 59.171 -2.234 59.163 -2.028 c
+58.928 -2.374 58.634 -2.543 58.282 -2.543 c
+57.918 -2.543 57.634 -2.447 57.428 -2.248 c
+57.23 -2.043 57.135 -1.756 57.135 -1.382 c
+57.135 -0.981 57.27 -0.661 57.546 -0.426 c
+57.818 -0.183 58.193 -0.058 58.664 -0.058 c
+59.148 -0.058 l
+59.148 0.368 l
+59.148 0.603 59.093 0.769 58.987 0.867 c
+58.876 0.974 58.715 1.029 58.502 1.029 c
+58.303 1.029 58.141 0.97 58.016 0.852 c
+57.899 0.735 57.84 0.588 57.84 0.412 c
+57.193 0.412 l
+57.193 0.607 57.252 0.798 57.37 0.985 c
+57.495 1.168 57.657 1.316 57.855 1.426 c
+58.061 1.532 58.288 1.588 58.546 1.588 c
+58.947 1.588 59.252 1.484 59.458 1.279 c
+59.67 1.073 59.784 0.779 59.795 0.397 c
+59.795 -1.617 l
+59.795 -1.922 59.832 -2.186 59.913 -2.41 c
+59.913 -2.469 l
+h
+58.369 -1.955 m
+58.535 -1.955 58.685 -1.911 58.825 -1.822 c
+58.972 -1.735 59.078 -1.624 59.148 -1.484 c
+59.148 -0.544 l
+58.781 -0.544 l
+58.465 -0.544 58.222 -0.613 58.046 -0.75 c
+57.87 -0.878 57.781 -1.066 57.781 -1.308 c
+57.781 -1.536 57.825 -1.701 57.914 -1.808 c
+58.002 -1.907 58.153 -1.955 58.369 -1.955 c
+61.648 2.469 m
+61.648 1.515 l
+62.25 1.515 l
+62.25 0.985 l
+61.648 0.985 l
+61.648 -1.484 l
+61.648 -1.643 61.669 -1.76 61.721 -1.837 c
+61.779 -1.918 61.868 -1.955 61.985 -1.955 c
+62.074 -1.955 62.162 -1.94 62.25 -1.911 c
+62.25 -2.469 l
+62.103 -2.517 61.949 -2.543 61.794 -2.543 c
+61.538 -2.543 61.343 -2.451 61.206 -2.263 c
+61.067 -2.08 61 -1.818 61 -1.484 c
+61 0.985 l
+60.398 0.985 l
+60.398 1.515 l
+61 1.515 l
+61 2.469 l
+h
+63.72 -2.469 -0.647 3.984 re
+63.764 2.558 m
+63.764 2.448 63.735 2.356 63.676 2.278 c
+63.617 2.209 63.522 2.176 63.396 2.176 c
+63.279 2.176 63.184 2.209 63.117 2.278 c
+63.059 2.356 63.03 2.448 63.03 2.558 c
+63.03 2.675 63.059 2.768 63.117 2.837 c
+63.184 2.914 63.279 2.955 63.396 2.955 c
+63.522 2.955 63.617 2.914 63.676 2.837 c
+63.735 2.756 63.764 2.664 63.764 2.558 c
+64.602 -0.294 m
+64.602 0.283 64.738 0.738 65.014 1.073 c
+65.297 1.415 65.667 1.588 66.13 1.588 c
+66.59 1.588 66.957 1.419 67.233 1.087 c
+67.516 0.765 67.663 0.316 67.674 -0.249 c
+67.674 -0.676 l
+67.674 -1.246 67.531 -1.701 67.248 -2.043 c
+66.972 -2.377 66.604 -2.543 66.145 -2.543 c
+65.682 -2.543 65.311 -2.381 65.028 -2.057 c
+64.753 -1.727 64.609 -1.286 64.602 -0.735 c
+h
+65.249 -0.676 m
+65.249 -1.08 65.326 -1.396 65.484 -1.631 c
+65.65 -1.866 65.87 -1.984 66.145 -1.984 c
+66.712 -1.984 67.005 -1.573 67.028 -0.75 c
+67.028 -0.294 l
+67.028 0.106 66.943 0.426 66.777 0.661 c
+66.619 0.904 66.402 1.029 66.13 1.029 c
+65.866 1.029 65.65 0.904 65.484 0.661 c
+65.326 0.426 65.249 0.106 65.249 -0.294 c
+h
+69.129 1.515 m
+69.144 1.073 l
+69.397 1.415 69.721 1.588 70.114 1.588 c
+70.82 1.588 71.176 1.118 71.187 0.177 c
+71.187 -2.469 l
+70.54 -2.469 l
+70.54 0.147 l
+70.54 0.459 70.485 0.68 70.379 0.809 c
+70.269 0.933 70.114 1 69.908 1 c
+69.75 1 69.603 0.944 69.467 0.838 c
+69.339 0.728 69.236 0.592 69.158 0.426 c
+69.158 -2.469 l
+68.512 -2.469 l
+68.512 1.515 l
+h
+73.745 -0.294 m
+73.745 0.283 73.881 0.738 74.157 1.073 c
+74.439 1.415 74.81 1.588 75.273 1.588 c
+75.732 1.588 76.1 1.419 76.376 1.087 c
+76.659 0.765 76.806 0.316 76.817 -0.249 c
+76.817 -0.676 l
+76.817 -1.246 76.673 -1.701 76.391 -2.043 c
+76.115 -2.377 75.747 -2.543 75.288 -2.543 c
+74.825 -2.543 74.454 -2.381 74.171 -2.057 c
+73.895 -1.727 73.752 -1.286 73.745 -0.735 c
+h
+74.392 -0.676 m
+74.392 -1.08 74.469 -1.396 74.627 -1.631 c
+74.792 -1.866 75.013 -1.984 75.288 -1.984 c
+75.854 -1.984 76.148 -1.573 76.17 -0.75 c
+76.17 -0.294 l
+76.17 0.106 76.085 0.426 75.92 0.661 c
+75.762 0.904 75.545 1.029 75.273 1.029 c
+75.009 1.029 74.792 0.904 74.627 0.661 c
+74.469 0.426 74.392 0.106 74.392 -0.294 c
+h
+77.949 -2.469 m
+77.949 0.985 l
+77.419 0.985 l
+77.419 1.515 l
+77.949 1.515 l
+77.949 1.97 l
+77.949 2.371 78.044 2.683 78.242 2.911 c
+78.448 3.134 78.728 3.248 79.08 3.248 c
+79.217 3.248 79.349 3.227 79.477 3.19 c
+79.448 2.646 l
+79.349 2.664 79.25 2.675 79.154 2.675 c
+78.779 2.675 78.595 2.411 78.595 1.881 c
+78.595 1.515 l
+79.271 1.515 l
+79.271 0.985 l
+78.595 0.985 l
+78.595 -2.469 l
+h
+85.195 -1.808 m
+85.078 -1.955 l
+84.744 -2.348 84.247 -2.543 83.593 -2.543 c
+83.013 -2.543 82.556 -2.352 82.226 -1.97 c
+81.903 -1.587 81.733 -1.043 81.726 -0.338 c
+81.726 0.706 l
+81.726 1.459 81.873 2.022 82.167 2.396 c
+82.469 2.768 82.921 2.955 83.52 2.955 c
+84.027 2.955 84.424 2.811 84.71 2.529 c
+85.004 2.242 85.166 1.837 85.195 1.309 c
+84.519 1.309 l
+84.497 1.639 84.405 1.9 84.239 2.087 c
+84.081 2.282 83.846 2.381 83.534 2.381 c
+83.141 2.381 82.858 2.253 82.682 1.999 c
+82.506 1.742 82.41 1.341 82.402 0.794 c
+82.402 -0.294 l
+82.402 -0.834 82.506 -1.249 82.711 -1.543 c
+82.917 -1.83 83.211 -1.97 83.593 -1.97 c
+83.953 -1.97 84.233 -1.882 84.431 -1.705 c
+84.519 -1.617 l
+84.519 -0.382 l
+83.549 -0.382 l
+83.549 0.191 l
+85.195 0.191 l
+h
+86.915 -2.469 -0.646 3.984 re
+86.959 2.558 m
+86.959 2.448 86.93 2.356 86.871 2.278 c
+86.812 2.209 86.716 2.176 86.591 2.176 c
+86.474 2.176 86.379 2.209 86.313 2.278 c
+86.254 2.356 86.224 2.448 86.224 2.558 c
+86.224 2.675 86.254 2.768 86.313 2.837 c
+86.379 2.914 86.474 2.955 86.591 2.955 c
+86.716 2.955 86.812 2.914 86.871 2.837 c
+86.93 2.756 86.959 2.664 86.959 2.558 c
+88.781 2.469 m
+88.781 1.515 l
+89.384 1.515 l
+89.384 0.985 l
+88.781 0.985 l
+88.781 -1.484 l
+88.781 -1.643 88.804 -1.76 88.856 -1.837 c
+88.914 -1.918 89.002 -1.955 89.12 -1.955 c
+89.208 -1.955 89.296 -1.94 89.384 -1.911 c
+89.384 -2.469 l
+89.238 -2.517 89.083 -2.543 88.929 -2.543 c
+88.671 -2.543 88.477 -2.451 88.341 -2.263 c
+88.201 -2.08 88.135 -1.818 88.135 -1.484 c
+88.135 0.985 l
+87.532 0.985 l
+87.532 1.515 l
+88.135 1.515 l
+88.135 2.469 l
+h
+91.736 -0.294 m
+91.736 0.283 91.873 0.738 92.148 1.073 c
+92.43 1.415 92.802 1.588 93.265 1.588 c
+93.724 1.588 94.092 1.419 94.367 1.087 c
+94.651 0.765 94.797 0.316 94.809 -0.249 c
+94.809 -0.676 l
+94.809 -1.246 94.665 -1.701 94.382 -2.043 c
+94.107 -2.377 93.739 -2.543 93.28 -2.543 c
+92.817 -2.543 92.446 -2.381 92.162 -2.057 c
+91.887 -1.727 91.744 -1.286 91.736 -0.735 c
+h
+92.383 -0.676 m
+92.383 -1.08 92.461 -1.396 92.619 -1.631 c
+92.783 -1.866 93.004 -1.984 93.28 -1.984 c
+93.845 -1.984 94.14 -1.573 94.161 -0.75 c
+94.161 -0.294 l
+94.161 0.106 94.077 0.426 93.912 0.661 c
+93.754 0.904 93.537 1.029 93.265 1.029 c
+93.001 1.029 92.783 0.904 92.619 0.661 c
+92.461 0.426 92.383 0.106 92.383 -0.294 c
+h
+97.675 -2.117 m
+97.458 -2.404 97.145 -2.543 96.734 -2.543 c
+96.37 -2.543 96.095 -2.421 95.911 -2.175 c
+95.734 -1.922 95.639 -1.558 95.632 -1.088 c
+95.632 1.515 l
+96.278 1.515 l
+96.278 -1.029 l
+96.278 -1.658 96.462 -1.97 96.837 -1.97 c
+97.238 -1.97 97.513 -1.793 97.66 -1.44 c
+97.66 1.515 l
+98.307 1.515 l
+98.307 -2.469 l
+97.689 -2.469 l
+h
+100.145 2.469 m
+100.145 1.515 l
+100.747 1.515 l
+100.747 0.985 l
+100.145 0.985 l
+100.145 -1.484 l
+100.145 -1.643 100.166 -1.76 100.218 -1.837 c
+100.276 -1.918 100.365 -1.955 100.482 -1.955 c
+100.571 -1.955 100.659 -1.94 100.747 -1.911 c
+100.747 -2.469 l
+100.6 -2.517 100.446 -2.543 100.291 -2.543 c
+100.034 -2.543 99.839 -2.451 99.703 -2.263 c
+99.563 -2.08 99.497 -1.818 99.497 -1.484 c
+99.497 0.985 l
+98.894 0.985 l
+98.894 1.515 l
+99.497 1.515 l
+99.497 2.469 l
+h
+104.392 -0.676 m
+104.392 -1.305 104.274 -1.775 104.039 -2.088 c
+103.812 -2.392 103.495 -2.543 103.084 -2.543 c
+102.68 -2.543 102.371 -2.392 102.158 -2.088 c
+102.158 -3.998 l
+101.511 -3.998 l
+101.511 1.515 l
+102.099 1.515 l
+102.144 1.073 l
+102.356 1.415 102.665 1.588 103.069 1.588 c
+103.51 1.588 103.837 1.434 104.054 1.132 c
+104.267 0.827 104.382 0.372 104.392 -0.235 c
+h
+103.746 -0.294 m
+103.746 0.147 103.676 0.47 103.54 0.676 c
+103.4 0.889 103.179 1 102.878 1 c
+102.562 1 102.323 0.846 102.158 0.544 c
+102.158 -1.529 l
+102.323 -1.834 102.562 -1.984 102.878 -1.984 c
+103.172 -1.984 103.385 -1.882 103.525 -1.675 c
+103.661 -1.463 103.734 -1.132 103.746 -0.691 c
+h
+107.258 -2.117 m
+107.042 -2.404 106.73 -2.543 106.318 -2.543 c
+105.954 -2.543 105.678 -2.421 105.495 -2.175 c
+105.319 -1.922 105.223 -1.558 105.215 -1.088 c
+105.215 1.515 l
+105.862 1.515 l
+105.862 -1.029 l
+105.862 -1.658 106.046 -1.97 106.42 -1.97 c
+106.821 -1.97 107.097 -1.793 107.244 -1.44 c
+107.244 1.515 l
+107.891 1.515 l
+107.891 -2.469 l
+107.273 -2.469 l
+h
+109.728 2.469 m
+109.728 1.515 l
+110.331 1.515 l
+110.331 0.985 l
+109.728 0.985 l
+109.728 -1.484 l
+109.728 -1.643 109.75 -1.76 109.801 -1.837 c
+109.86 -1.918 109.948 -1.955 110.066 -1.955 c
+110.154 -1.955 110.242 -1.94 110.331 -1.911 c
+110.331 -2.469 l
+110.183 -2.517 110.029 -2.543 109.875 -2.543 c
+109.618 -2.543 109.423 -2.451 109.287 -2.263 c
+109.147 -2.08 109.082 -1.818 109.082 -1.484 c
+109.082 0.985 l
+108.479 0.985 l
+108.479 1.515 l
+109.082 1.515 l
+109.082 2.469 l
+h
+111.139 -2.117 m
+111.139 -1.999 111.172 -1.903 111.242 -1.822 c
+111.308 -1.745 111.411 -1.705 111.551 -1.705 c
+111.698 -1.705 111.804 -1.745 111.874 -1.822 c
+111.951 -1.903 111.991 -1.999 111.991 -2.117 c
+111.991 -2.227 111.951 -2.319 111.874 -2.396 c
+111.804 -2.473 111.698 -2.514 111.551 -2.514 c
+111.411 -2.514 111.308 -2.473 111.242 -2.396 c
+111.172 -2.319 111.139 -2.227 111.139 -2.117 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+35.668 318.604 238.665 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 140.2002 303.1149 cm
+0 0 m
+-5.513 0 l
+-5.513 1.294 l
+-2.984 4.513 l
+-2.66 4.954 -2.425 5.34 -2.278 5.675 c
+-2.124 6.016 -2.043 6.34 -2.043 6.644 c
+-2.043 7.063 -2.117 7.394 -2.263 7.629 c
+-2.404 7.865 -2.61 7.982 -2.881 7.982 c
+-3.175 7.982 -3.41 7.842 -3.587 7.57 c
+-3.763 7.294 -3.851 6.909 -3.851 6.409 c
+-5.674 6.409 l
+-5.674 6.986 -5.556 7.512 -5.321 7.982 c
+-5.079 8.46 -4.74 8.835 -4.307 9.099 c
+-3.877 9.363 -3.392 9.496 -2.851 9.496 c
+-2.022 9.496 -1.374 9.264 -0.912 8.805 c
+-0.441 8.342 -0.206 7.688 -0.206 6.835 c
+-0.206 6.306 -0.339 5.77 -0.603 5.233 c
+-0.86 4.693 -1.32 4.028 -1.985 3.234 c
+-3.19 1.515 l
+0 1.515 l
+h
+1.532 0.927 m
+1.532 1.22 1.627 1.459 1.826 1.646 c
+2.021 1.831 2.275 1.926 2.591 1.926 c
+2.892 1.926 3.142 1.831 3.341 1.646 c
+3.546 1.459 3.649 1.22 3.649 0.927 c
+3.649 0.621 3.546 0.375 3.341 0.191 c
+3.142 0.015 2.892 -0.073 2.591 -0.073 c
+2.286 -0.073 2.032 0.019 1.826 0.206 c
+1.627 0.389 1.532 0.632 1.532 0.927 c
+12.49 2.455 m
+12.49 2.837 12.391 3.124 12.197 3.323 c
+11.998 3.517 11.645 3.719 11.138 3.925 c
+10.198 4.285 9.521 4.704 9.109 5.174 c
+8.698 5.652 8.492 6.222 8.492 6.88 c
+8.492 7.662 8.771 8.295 9.33 8.775 c
+9.889 9.253 10.597 9.496 11.461 9.496 c
+12.039 9.496 12.553 9.371 13.005 9.128 c
+13.454 8.882 13.799 8.54 14.034 8.1 c
+14.277 7.659 14.401 7.159 14.401 6.6 c
+12.519 6.6 l
+12.519 7.041 12.424 7.372 12.241 7.599 c
+12.053 7.824 11.785 7.938 11.432 7.938 c
+11.098 7.938 10.836 7.838 10.653 7.644 c
+10.477 7.456 10.389 7.196 10.389 6.865 c
+10.389 6.608 10.491 6.373 10.697 6.159 c
+10.903 5.953 11.263 5.737 11.785 5.513 c
+12.696 5.189 13.358 4.785 13.77 4.307 c
+14.188 3.826 14.401 3.212 14.401 2.469 c
+14.401 1.654 14.14 1.018 13.622 0.559 c
+13.101 0.096 12.395 -0.132 11.505 -0.132 c
+10.896 -0.132 10.344 -0.007 9.845 0.235 c
+9.352 0.489 8.966 0.846 8.683 1.309 c
+8.397 1.779 8.257 2.326 8.257 2.955 c
+10.153 2.955 l
+10.153 2.415 10.256 2.022 10.462 1.779 c
+10.675 1.532 11.025 1.411 11.505 1.411 c
+12.16 1.411 12.49 1.757 12.49 2.455 c
+21.938 7.791 m
+19.615 7.791 l
+19.615 0 l
+17.72 0 l
+17.72 7.791 l
+15.441 7.791 l
+15.441 9.363 l
+21.938 9.363 l
+h
+26.896 1.912 m
+24.323 1.912 l
+23.823 0 l
+21.824 0 l
+24.749 9.363 l
+26.47 9.363 l
+29.424 0 l
+27.395 0 l
+h
+24.735 3.499 m
+26.484 3.499 l
+25.602 6.835 l
+h
+33.363 3.425 m
+32.423 3.425 l
+32.423 0 l
+30.541 0 l
+30.541 9.363 l
+33.554 9.363 l
+34.503 9.363 35.234 9.118 35.744 8.628 c
+36.263 8.136 36.523 7.441 36.523 6.541 c
+36.523 5.295 36.072 4.424 35.171 3.925 c
+36.803 0.088 l
+36.803 0 l
+34.774 0 l
+h
+32.423 4.998 m
+33.495 4.998 l
+33.878 4.998 34.16 5.12 34.348 5.365 c
+34.532 5.619 34.627 5.957 34.627 6.38 c
+34.627 7.321 34.264 7.791 33.539 7.791 c
+32.423 7.791 l
+h
+43.792 7.791 m
+41.47 7.791 l
+41.47 0 l
+39.573 0 l
+39.573 7.791 l
+37.295 7.791 l
+37.295 9.363 l
+43.792 9.363 l
+h
+47.062 0 -1.896 9.363 re
+55.309 0 m
+53.412 0 l
+50.649 6.145 l
+50.649 0 l
+48.753 0 l
+48.753 9.363 l
+50.649 9.363 l
+53.412 3.219 l
+53.412 9.363 l
+55.309 9.363 l
+h
+63.427 1.058 m
+63.062 0.665 62.617 0.368 62.089 0.162 c
+61.559 -0.033 60.979 -0.132 60.354 -0.132 c
+59.274 -0.132 58.436 0.198 57.84 0.867 c
+57.241 1.532 56.936 2.502 56.929 3.778 c
+56.929 5.469 l
+56.929 6.762 57.208 7.755 57.767 8.453 c
+58.333 9.147 59.157 9.496 60.236 9.496 c
+61.254 9.496 62.019 9.239 62.53 8.732 c
+63.047 8.231 63.346 7.445 63.427 6.38 c
+61.589 6.38 l
+61.538 6.975 61.416 7.383 61.221 7.599 c
+61.023 7.813 60.714 7.923 60.296 7.923 c
+59.785 7.923 59.413 7.736 59.178 7.364 c
+58.951 6.99 58.833 6.398 58.825 5.586 c
+58.825 3.881 l
+58.825 3.028 58.951 2.404 59.207 2.014 c
+59.461 1.632 59.876 1.441 60.457 1.441 c
+60.828 1.441 61.133 1.515 61.368 1.661 c
+61.53 1.779 l
+61.53 3.499 l
+60.207 3.499 l
+60.207 4.925 l
+63.427 4.925 l
+h
+72.591 1.912 m
+70.019 1.912 l
+69.519 0 l
+67.52 0 l
+70.445 9.363 l
+72.165 9.363 l
+75.119 0 l
+73.091 0 l
+h
+70.43 3.499 m
+72.179 3.499 l
+71.298 6.835 l
+h
+81.238 3.293 m
+81.238 0 l
+79.342 0 l
+79.342 9.363 l
+82.531 9.363 l
+83.461 9.363 84.2 9.073 84.751 8.497 c
+85.299 7.927 85.574 7.181 85.574 6.263 c
+85.574 5.351 85.299 4.623 84.751 4.087 c
+84.21 3.558 83.461 3.293 82.502 3.293 c
+h
+81.238 4.866 m
+82.531 4.866 l
+82.891 4.866 83.171 4.983 83.369 5.218 c
+83.564 5.453 83.663 5.795 83.663 6.247 c
+83.663 6.718 83.56 7.088 83.354 7.364 c
+83.156 7.637 82.891 7.78 82.561 7.791 c
+81.238 7.791 l
+h
+89.877 3.425 m
+88.937 3.425 l
+88.937 0 l
+87.055 0 l
+87.055 9.363 l
+90.068 9.363 l
+91.017 9.363 91.747 9.118 92.258 8.628 c
+92.777 8.136 93.037 7.441 93.037 6.541 c
+93.037 5.295 92.586 4.424 91.685 3.925 c
+93.317 0.088 l
+93.317 0 l
+91.288 0 l
+h
+88.937 4.998 m
+90.01 4.998 l
+90.392 4.998 90.675 5.12 90.862 5.365 c
+91.046 5.619 91.142 5.957 91.142 6.38 c
+91.142 7.321 90.778 7.791 90.053 7.791 c
+88.937 7.791 l
+h
+101.155 3.836 m
+101.155 2.58 100.854 1.606 100.258 0.912 c
+99.659 0.214 98.836 -0.132 97.789 -0.132 c
+96.738 -0.132 95.911 0.21 95.304 0.897 c
+94.706 1.592 94.4 2.558 94.394 3.793 c
+94.394 5.395 l
+94.394 6.677 94.691 7.68 95.29 8.408 c
+95.886 9.132 96.716 9.496 97.774 9.496 c
+98.811 9.496 99.63 9.136 100.229 8.422 c
+100.835 7.717 101.144 6.722 101.155 5.439 c
+h
+99.259 5.409 m
+99.259 6.251 99.134 6.88 98.891 7.291 c
+98.656 7.703 98.282 7.909 97.774 7.909 c
+97.274 7.909 96.9 7.707 96.657 7.306 c
+96.422 6.913 96.297 6.313 96.289 5.513 c
+96.289 3.836 l
+96.289 3.021 96.411 2.419 96.657 2.029 c
+96.9 1.636 97.278 1.441 97.789 1.441 c
+98.278 1.441 98.641 1.632 98.876 2.014 c
+99.119 2.396 99.248 2.984 99.259 3.778 c
+h
+105.814 9.363 m
+107.696 9.363 l
+107.696 2.882 l
+107.696 1.97 107.439 1.239 106.932 0.691 c
+106.432 0.14 105.785 -0.132 104.991 -0.132 c
+104.128 -0.132 103.456 0.121 102.978 0.632 c
+102.508 1.139 102.272 1.852 102.272 2.764 c
+104.168 2.764 l
+104.168 1.881 104.44 1.441 104.991 1.441 c
+105.539 1.441 105.814 1.948 105.814 2.97 c
+h
+114.208 4.057 m
+111.268 4.057 l
+111.268 1.573 l
+114.751 1.573 l
+114.751 0 l
+109.372 0 l
+109.372 9.363 l
+114.737 9.363 l
+114.737 7.791 l
+111.268 7.791 l
+111.268 5.571 l
+114.208 5.571 l
+h
+122.362 3.117 m
+122.31 2.047 122.009 1.239 121.45 0.691 c
+120.899 0.14 120.128 -0.132 119.129 -0.132 c
+118.059 -0.132 117.24 0.214 116.673 0.912 c
+116.115 1.606 115.836 2.602 115.836 3.896 c
+115.836 5.469 l
+115.836 6.762 116.122 7.755 116.703 8.453 c
+117.291 9.147 118.103 9.496 119.143 9.496 c
+120.161 9.496 120.937 9.205 121.465 8.628 c
+121.995 8.059 122.296 7.24 122.377 6.174 c
+120.481 6.174 l
+120.459 6.839 120.355 7.294 120.172 7.541 c
+119.985 7.794 119.643 7.923 119.143 7.923 c
+118.643 7.923 118.283 7.747 118.07 7.394 c
+117.864 7.041 117.75 6.457 117.732 5.644 c
+117.732 3.881 l
+117.732 2.947 117.835 2.308 118.04 1.955 c
+118.254 1.61 118.618 1.441 119.129 1.441 c
+119.617 1.441 119.956 1.559 120.143 1.794 c
+120.338 2.036 120.444 2.477 120.466 3.117 c
+h
+129.624 7.791 m
+127.301 7.791 l
+127.301 0 l
+125.405 0 l
+125.405 7.791 l
+123.127 7.791 l
+123.127 9.363 l
+129.624 9.363 l
+h
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 288.302 238.665 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 281.4638 cm
+0 0 m
+0 -0.188 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.776 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.206 -1.278 -1.243 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.148 c
+-2.55 -0.228 -2.506 -0.302 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.675 c
+-1.977 0.683 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.042 c
+-0.804 2.991 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.263 -0.941 1.205 c
+-0.756 1.146 -0.595 1.066 -0.455 0.97 c
+-0.32 0.87 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.437 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.499 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.558 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.754 5.964 -1.86 6.064 -1.941 c
+6.159 -2.029 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.029 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.324 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.89 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.245 1.932 5.376 2.05 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.969 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.164 7.14 2.198 7.152 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.636 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.5 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.499 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.462 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.874 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.383 c
+6.924 -0.294 6.978 -0.177 7.019 -0.03 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.116 l
+14.497 3.116 l
+14.497 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.104 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.456 14.556 -0.515 c
+14.593 -0.566 14.648 -0.611 14.718 -0.647 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.688 15.468 -0.676 c
+15.603 -0.659 15.736 -0.632 15.865 -0.603 c
+15.865 -1.206 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.279 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.309 14.835 -1.309 c
+14.648 -1.309 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.085 13.85 -1.025 13.791 -0.956 c
+13.733 -0.879 13.689 -0.802 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.456 c
+13.59 -0.36 13.586 -0.265 13.586 -0.177 c
+h
+23.221 -0.647 m
+24.353 -0.647 l
+24.353 -1.264 l
+21.045 -1.264 l
+21.045 -0.647 l
+22.31 -0.647 l
+22.31 1.602 l
+21.384 1.602 l
+21.384 2.219 l
+23.221 2.219 l
+h
+22.31 3.513 0.912 -0.676 re
+22.31 2.836 m
+27.37 -1.264 m
+27.37 0.72 l
+27.37 1.022 27.326 1.242 27.237 1.381 c
+27.156 1.529 27.021 1.602 26.826 1.602 c
+26.716 1.602 26.613 1.576 26.518 1.529 c
+26.429 1.477 26.348 1.411 26.282 1.323 c
+26.223 1.234 26.172 1.124 26.136 0.999 c
+26.106 0.881 26.091 0.75 26.091 0.602 c
+26.091 -1.264 l
+25.18 -1.264 l
+25.18 1.44 l
+25.18 1.66 l
+25.18 1.749 25.172 1.826 25.165 1.896 c
+25.165 2.087 l
+25.165 2.219 l
+26.018 2.219 l
+26.025 2.19 26.032 2.146 26.032 2.087 c
+26.032 1.896 l
+26.04 1.826 26.047 1.756 26.047 1.69 c
+26.055 1.62 26.061 1.565 26.061 1.529 c
+26.076 1.529 l
+26.194 1.793 26.344 1.984 26.532 2.102 c
+26.716 2.219 26.936 2.278 27.194 2.278 c
+27.377 2.278 27.539 2.248 27.678 2.19 c
+27.815 2.131 27.929 2.043 28.017 1.926 c
+28.105 1.808 28.168 1.664 28.208 1.499 c
+28.255 1.341 28.281 1.153 28.281 0.941 c
+28.281 -1.264 l
+h
+31.343 -0.647 m
+32.474 -0.647 l
+32.474 -1.264 l
+29.167 -1.264 l
+29.167 -0.647 l
+30.431 -0.647 l
+30.431 1.602 l
+29.505 1.602 l
+29.505 2.219 l
+31.343 2.219 l
+h
+30.431 3.513 0.912 -0.676 re
+30.431 2.836 m
+33.907 1.602 m
+33.363 1.602 l
+33.363 2.219 l
+33.951 2.219 l
+34.231 3.116 l
+34.804 3.116 l
+34.804 2.219 l
+36.039 2.219 l
+36.039 1.602 l
+34.804 1.602 l
+34.804 -0.104 l
+34.804 -0.324 l
+34.811 -0.393 34.834 -0.456 34.863 -0.515 c
+34.9 -0.566 34.955 -0.611 35.025 -0.647 c
+35.102 -0.676 35.216 -0.691 35.362 -0.691 c
+35.499 -0.691 35.634 -0.688 35.774 -0.676 c
+35.91 -0.659 36.043 -0.632 36.17 -0.603 c
+36.17 -1.206 l
+36.091 -1.216 36.013 -1.231 35.935 -1.249 c
+35.855 -1.261 35.778 -1.268 35.7 -1.279 c
+35.62 -1.286 35.532 -1.294 35.436 -1.294 c
+35.348 -1.301 35.249 -1.309 35.142 -1.309 c
+34.955 -1.309 34.793 -1.294 34.657 -1.264 c
+34.528 -1.228 34.414 -1.183 34.319 -1.132 c
+34.231 -1.085 34.157 -1.025 34.098 -0.956 c
+34.04 -0.879 33.995 -0.802 33.966 -0.721 c
+33.937 -0.632 33.915 -0.544 33.907 -0.456 c
+33.897 -0.36 33.893 -0.265 33.893 -0.177 c
+h
+42.175 -2.631 m
+42.175 3.513 l
+44.101 3.513 l
+44.101 2.896 l
+43.028 2.896 l
+43.028 -2.014 l
+44.101 -2.014 l
+44.101 -2.631 l
+h
+46.354 2.219 m
+46.362 2.198 46.368 2.164 46.368 2.117 c
+46.376 2.076 46.383 2.028 46.383 1.969 c
+46.391 1.918 46.398 1.866 46.398 1.808 c
+46.398 1.646 l
+46.412 1.646 l
+46.472 1.764 46.537 1.859 46.618 1.94 c
+46.695 2.017 46.78 2.08 46.869 2.131 c
+46.956 2.19 47.044 2.227 47.133 2.248 c
+47.228 2.267 47.328 2.278 47.426 2.278 c
+47.632 2.278 47.813 2.234 47.97 2.146 c
+48.125 2.057 48.253 1.928 48.353 1.764 c
+48.459 1.606 48.536 1.415 48.588 1.19 c
+48.647 0.974 48.676 0.738 48.676 0.484 c
+48.676 0.22 48.647 -0.026 48.588 -0.25 c
+48.536 -0.467 48.459 -0.659 48.353 -0.823 c
+48.253 -0.981 48.122 -1.103 47.956 -1.191 c
+47.798 -1.279 47.611 -1.324 47.397 -1.324 c
+47.298 -1.324 47.199 -1.312 47.104 -1.294 c
+47.004 -1.272 46.912 -1.243 46.824 -1.191 c
+46.743 -1.143 46.666 -1.081 46.589 -1 c
+46.519 -0.923 46.46 -0.831 46.412 -0.721 c
+46.398 -0.721 l
+46.398 -0.809 l
+46.405 -0.85 46.412 -0.897 46.412 -0.956 c
+46.412 -1.118 l
+46.412 -1.294 l
+46.412 -2.631 l
+45.501 -2.631 l
+45.501 1.455 l
+45.501 1.62 45.494 1.768 45.487 1.896 c
+45.487 2.219 l
+h
+46.398 0.455 m
+46.398 0.228 46.416 0.037 46.457 -0.118 c
+46.505 -0.265 46.559 -0.383 46.618 -0.47 c
+46.684 -0.559 46.758 -0.625 46.838 -0.661 c
+46.916 -0.702 46.993 -0.721 47.074 -0.721 c
+47.17 -0.721 47.258 -0.698 47.339 -0.647 c
+47.426 -0.599 47.493 -0.53 47.544 -0.441 c
+47.603 -0.345 47.648 -0.221 47.677 -0.073 c
+47.713 0.081 47.735 0.268 47.735 0.484 c
+47.735 0.874 47.677 1.168 47.559 1.367 c
+47.449 1.562 47.295 1.66 47.089 1.66 c
+47.008 1.66 46.931 1.639 46.854 1.602 c
+46.773 1.562 46.699 1.499 46.633 1.411 c
+46.563 1.323 46.505 1.198 46.457 1.043 c
+46.416 0.885 46.398 0.69 46.398 0.455 c
+52.56 1.469 m
+52.461 1.477 52.359 1.488 52.251 1.499 c
+52.141 1.517 52.02 1.529 51.885 1.529 c
+51.708 1.529 51.55 1.488 51.414 1.411 c
+51.274 1.341 51.156 1.242 51.061 1.117 c
+50.973 0.988 50.903 0.841 50.855 0.675 c
+50.815 0.507 50.796 0.33 50.796 0.147 c
+50.796 -1.264 l
+49.9 -1.264 l
+49.9 0.985 l
+49.9 1.109 49.889 1.234 49.87 1.352 c
+49.859 1.477 49.845 1.595 49.826 1.705 c
+49.815 1.822 49.801 1.918 49.782 1.999 c
+49.76 2.087 49.742 2.161 49.724 2.219 c
+50.605 2.219 l
+50.613 2.167 50.624 2.117 50.635 2.057 c
+50.653 1.999 50.668 1.932 50.679 1.866 c
+50.697 1.808 50.712 1.741 50.723 1.675 c
+50.73 1.606 50.742 1.543 50.752 1.484 c
+50.767 1.484 l
+50.804 1.602 50.855 1.708 50.914 1.808 c
+50.981 1.903 51.061 1.988 51.149 2.057 c
+51.237 2.124 51.341 2.179 51.458 2.219 c
+51.583 2.256 51.73 2.278 51.899 2.278 c
+52.024 2.278 52.141 2.271 52.251 2.263 c
+52.369 2.252 52.473 2.238 52.56 2.219 c
+h
+56.816 0.484 m
+56.816 0.209 56.779 -0.04 56.712 -0.265 c
+56.643 -0.482 56.54 -0.669 56.404 -0.823 c
+56.265 -0.981 56.088 -1.103 55.875 -1.191 c
+55.658 -1.279 55.405 -1.324 55.11 -1.324 c
+54.835 -1.324 54.589 -1.279 54.376 -1.191 c
+54.17 -1.103 53.997 -0.981 53.861 -0.823 c
+53.722 -0.669 53.618 -0.482 53.553 -0.265 c
+53.483 -0.04 53.45 0.209 53.45 0.484 c
+53.45 0.738 53.479 0.974 53.537 1.19 c
+53.604 1.415 53.707 1.606 53.847 1.764 c
+53.982 1.928 54.159 2.057 54.376 2.146 c
+54.589 2.234 54.846 2.278 55.14 2.278 c
+55.453 2.278 55.713 2.234 55.919 2.146 c
+56.132 2.057 56.305 1.928 56.434 1.764 c
+56.569 1.606 56.669 1.415 56.728 1.19 c
+56.787 0.974 56.816 0.738 56.816 0.484 c
+55.86 0.484 m
+55.86 0.69 55.846 0.867 55.816 1.014 c
+55.794 1.161 55.758 1.282 55.698 1.381 c
+55.64 1.477 55.567 1.547 55.478 1.587 c
+55.39 1.635 55.28 1.66 55.155 1.66 c
+54.89 1.66 54.699 1.562 54.582 1.367 c
+54.464 1.18 54.405 0.885 54.405 0.484 c
+54.405 0.062 54.464 -0.243 54.582 -0.426 c
+54.699 -0.614 54.875 -0.706 55.11 -0.706 c
+55.236 -0.706 55.349 -0.688 55.449 -0.647 c
+55.544 -0.599 55.625 -0.526 55.684 -0.426 c
+55.75 -0.331 55.794 -0.206 55.816 -0.059 c
+55.846 0.087 55.86 0.268 55.86 0.484 c
+59.995 -1.309 m
+59.995 -1.544 59.951 -1.742 59.862 -1.912 c
+59.781 -2.076 59.675 -2.213 59.538 -2.323 c
+59.399 -2.429 59.245 -2.506 59.068 -2.558 c
+58.892 -2.606 58.709 -2.631 58.524 -2.631 c
+58.436 -2.631 58.348 -2.624 58.26 -2.617 c
+58.172 -2.606 58.083 -2.595 57.996 -2.587 c
+57.915 -2.577 57.841 -2.562 57.775 -2.543 c
+57.705 -2.533 57.647 -2.517 57.599 -2.5 c
+57.599 -1.823 l
+57.687 -1.841 57.805 -1.86 57.951 -1.882 c
+58.098 -1.912 58.26 -1.926 58.436 -1.926 c
+58.532 -1.926 58.62 -1.914 58.701 -1.897 c
+58.778 -1.874 58.848 -1.841 58.906 -1.794 c
+58.966 -1.742 59.01 -1.669 59.039 -1.573 c
+59.068 -1.484 59.083 -1.371 59.083 -1.235 c
+59.083 1.602 l
+57.951 1.602 l
+57.951 2.219 l
+59.995 2.219 l
+h
+59.098 3.513 0.897 -0.676 re
+59.098 2.836 m
+63.291 -1.324 m
+63.033 -1.324 62.806 -1.286 62.6 -1.22 c
+62.395 -1.143 62.218 -1.029 62.071 -0.882 c
+61.924 -0.728 61.806 -0.537 61.718 -0.309 c
+61.637 -0.085 61.601 0.18 61.601 0.484 c
+61.601 0.816 61.645 1.095 61.732 1.323 c
+61.828 1.558 61.957 1.741 62.115 1.881 c
+62.28 2.017 62.468 2.117 62.673 2.175 c
+62.879 2.242 63.089 2.278 63.305 2.278 c
+63.577 2.278 63.812 2.227 64.011 2.131 c
+64.217 2.043 64.382 1.911 64.511 1.734 c
+64.647 1.565 64.746 1.359 64.805 1.117 c
+64.871 0.881 64.907 0.617 64.907 0.323 c
+64.907 0.309 l
+62.541 0.309 l
+62.541 0.162 62.555 0.022 62.586 -0.104 c
+62.622 -0.231 62.677 -0.345 62.746 -0.441 c
+62.813 -0.53 62.898 -0.599 62.997 -0.647 c
+63.093 -0.698 63.206 -0.721 63.335 -0.721 c
+63.49 -0.721 63.629 -0.688 63.746 -0.618 c
+63.872 -0.551 63.96 -0.449 64.011 -0.309 c
+64.849 -0.383 l
+64.82 -0.482 64.764 -0.588 64.687 -0.706 c
+64.606 -0.816 64.504 -0.919 64.379 -1.014 c
+64.261 -1.103 64.107 -1.176 63.922 -1.235 c
+63.746 -1.294 63.533 -1.324 63.291 -1.324 c
+63.291 1.705 m
+63.203 1.705 63.114 1.69 63.026 1.66 c
+62.938 1.631 62.857 1.579 62.791 1.514 c
+62.721 1.444 62.663 1.356 62.615 1.249 c
+62.574 1.139 62.555 1.014 62.555 0.867 c
+64.026 0.867 l
+64.026 1.003 64 1.124 63.952 1.234 c
+63.912 1.341 63.856 1.429 63.791 1.499 c
+63.731 1.565 63.658 1.616 63.57 1.646 c
+63.482 1.683 63.386 1.705 63.291 1.705 c
+67.351 -1.324 m
+67.064 -1.324 66.823 -1.283 66.617 -1.206 c
+66.411 -1.118 66.237 -0.996 66.102 -0.838 c
+65.962 -0.684 65.859 -0.497 65.793 -0.279 c
+65.724 -0.056 65.69 0.191 65.69 0.455 c
+65.69 0.75 65.724 1.007 65.793 1.234 c
+65.871 1.458 65.977 1.646 66.117 1.793 c
+66.264 1.947 66.44 2.065 66.646 2.146 c
+66.852 2.234 67.087 2.278 67.351 2.278 c
+67.575 2.278 67.777 2.248 67.954 2.19 c
+68.13 2.131 68.281 2.046 68.41 1.94 c
+68.535 1.841 68.637 1.72 68.718 1.573 c
+68.795 1.433 68.851 1.282 68.88 1.117 c
+67.968 1.072 l
+67.939 1.249 67.87 1.389 67.763 1.499 c
+67.664 1.606 67.521 1.66 67.336 1.66 c
+67.091 1.66 66.914 1.558 66.808 1.352 c
+66.697 1.153 66.646 0.867 66.646 0.484 c
+66.646 -0.309 66.881 -0.706 67.351 -0.706 c
+67.517 -0.706 67.66 -0.655 67.777 -0.544 c
+67.895 -0.437 67.968 -0.276 67.999 -0.059 c
+68.909 -0.104 l
+68.88 -0.272 68.824 -0.426 68.747 -0.574 c
+68.678 -0.721 68.575 -0.853 68.439 -0.971 c
+68.311 -1.081 68.153 -1.168 67.968 -1.235 c
+67.792 -1.294 67.586 -1.324 67.351 -1.324 c
+70.457 1.602 m
+69.913 1.602 l
+69.913 2.219 l
+70.501 2.219 l
+70.78 3.116 l
+71.353 3.116 l
+71.353 2.219 l
+72.587 2.219 l
+72.587 1.602 l
+71.353 1.602 l
+71.353 -0.104 l
+71.353 -0.324 l
+71.361 -0.393 71.382 -0.456 71.412 -0.515 c
+71.448 -0.566 71.504 -0.611 71.573 -0.647 c
+71.65 -0.676 71.764 -0.691 71.912 -0.691 c
+72.047 -0.691 72.184 -0.688 72.323 -0.676 c
+72.46 -0.659 72.591 -0.632 72.72 -0.603 c
+72.72 -1.206 l
+72.639 -1.216 72.562 -1.231 72.485 -1.249 c
+72.404 -1.261 72.327 -1.268 72.25 -1.279 c
+72.169 -1.286 72.08 -1.294 71.985 -1.294 c
+71.897 -1.301 71.798 -1.309 71.691 -1.309 c
+71.504 -1.309 71.342 -1.294 71.206 -1.264 c
+71.078 -1.228 70.964 -1.183 70.868 -1.132 c
+70.78 -1.085 70.706 -1.025 70.648 -0.956 c
+70.588 -0.879 70.545 -0.802 70.515 -0.721 c
+70.486 -0.632 70.464 -0.544 70.457 -0.456 c
+70.445 -0.36 70.442 -0.265 70.442 -0.177 c
+h
+80.169 -1.264 m
+80.169 0.72 l
+80.169 1.022 80.125 1.242 80.036 1.381 c
+79.955 1.529 79.82 1.602 79.625 1.602 c
+79.515 1.602 79.412 1.576 79.317 1.529 c
+79.228 1.477 79.147 1.411 79.081 1.323 c
+79.022 1.234 78.971 1.124 78.935 0.999 c
+78.905 0.881 78.89 0.75 78.89 0.602 c
+78.89 -1.264 l
+77.979 -1.264 l
+77.979 1.44 l
+77.979 1.66 l
+77.979 1.749 77.971 1.826 77.964 1.896 c
+77.964 2.087 l
+77.964 2.219 l
+78.817 2.219 l
+78.824 2.19 78.831 2.146 78.831 2.087 c
+78.831 1.896 l
+78.839 1.826 78.846 1.756 78.846 1.69 c
+78.854 1.62 78.86 1.565 78.86 1.529 c
+78.875 1.529 l
+78.993 1.793 79.143 1.984 79.331 2.102 c
+79.515 2.219 79.735 2.278 79.993 2.278 c
+80.176 2.278 80.338 2.248 80.477 2.19 c
+80.614 2.131 80.728 2.043 80.816 1.926 c
+80.904 1.808 80.967 1.664 81.007 1.499 c
+81.054 1.341 81.08 1.153 81.08 0.941 c
+81.08 -1.264 l
+h
+82.995 -1.324 m
+82.826 -1.324 82.675 -1.301 82.54 -1.264 c
+82.411 -1.216 82.297 -1.147 82.201 -1.058 c
+82.112 -0.971 82.043 -0.864 81.995 -0.736 c
+81.944 -0.599 81.921 -0.449 81.921 -0.279 c
+81.921 -0.073 81.954 0.095 82.025 0.235 c
+82.091 0.382 82.187 0.492 82.304 0.573 c
+82.429 0.661 82.572 0.723 82.73 0.764 c
+82.895 0.801 83.072 0.827 83.259 0.837 c
+83.98 0.852 l
+83.98 1.028 l
+83.98 1.146 83.969 1.249 83.951 1.338 c
+83.928 1.425 83.895 1.492 83.847 1.543 c
+83.807 1.602 83.759 1.639 83.7 1.66 c
+83.641 1.679 83.575 1.69 83.509 1.69 c
+83.44 1.69 83.377 1.679 83.318 1.66 c
+83.267 1.65 83.219 1.624 83.171 1.587 c
+83.13 1.558 83.097 1.506 83.068 1.44 c
+83.047 1.381 83.032 1.301 83.024 1.205 c
+82.083 1.249 l
+82.112 1.396 82.157 1.532 82.216 1.66 c
+82.282 1.786 82.378 1.896 82.495 1.984 c
+82.613 2.08 82.752 2.153 82.921 2.204 c
+83.097 2.252 83.303 2.278 83.539 2.278 c
+83.98 2.278 84.31 2.167 84.539 1.955 c
+84.774 1.749 84.891 1.44 84.891 1.028 c
+84.891 -0.235 l
+84.891 -0.456 l
+84.898 -0.515 84.913 -0.57 84.935 -0.618 c
+84.953 -0.659 84.983 -0.691 85.023 -0.721 c
+85.06 -0.742 85.112 -0.75 85.17 -0.75 c
+85.237 -0.75 85.306 -0.746 85.376 -0.736 c
+85.376 -1.22 l
+85.318 -1.231 85.262 -1.243 85.214 -1.249 c
+85.174 -1.261 85.133 -1.268 85.096 -1.279 c
+85.056 -1.286 85.012 -1.294 84.965 -1.294 c
+84.913 -1.301 84.854 -1.309 84.788 -1.309 c
+84.56 -1.309 84.395 -1.257 84.288 -1.147 c
+84.178 -1.029 84.115 -0.864 84.097 -0.647 c
+84.082 -0.647 l
+84.013 -0.757 83.943 -0.853 83.876 -0.941 c
+83.807 -1.022 83.73 -1.088 83.641 -1.147 c
+83.554 -1.206 83.454 -1.249 83.348 -1.279 c
+83.248 -1.309 83.13 -1.324 82.995 -1.324 c
+83.98 0.353 m
+83.554 0.338 l
+83.454 0.338 83.363 0.33 83.274 0.323 c
+83.193 0.312 83.127 0.286 83.068 0.249 c
+83.01 0.209 82.958 0.151 82.921 0.073 c
+82.881 0.004 82.862 -0.088 82.862 -0.206 c
+82.862 -0.375 82.895 -0.497 82.966 -0.574 c
+83.032 -0.655 83.13 -0.691 83.259 -0.691 c
+83.366 -0.691 83.465 -0.669 83.554 -0.618 c
+83.649 -0.57 83.73 -0.507 83.789 -0.426 c
+83.855 -0.349 83.906 -0.262 83.936 -0.162 c
+83.965 -0.056 83.98 0.058 83.98 0.176 c
+h
+87.261 -1.264 m
+87.261 0.852 l
+87.261 1.018 87.254 1.153 87.246 1.263 c
+87.236 1.371 87.217 1.455 87.188 1.514 c
+87.166 1.579 87.136 1.631 87.099 1.66 c
+87.07 1.69 87.03 1.705 86.982 1.705 c
+86.923 1.705 86.868 1.675 86.82 1.616 c
+86.779 1.565 86.747 1.492 86.717 1.396 c
+86.688 1.308 86.662 1.194 86.644 1.058 c
+86.633 0.918 86.629 0.768 86.629 0.602 c
+86.629 -1.264 l
+85.879 -1.264 l
+85.879 1.469 l
+85.879 1.705 l
+85.879 1.926 l
+85.879 2.003 85.873 2.065 85.865 2.117 c
+85.865 2.219 l
+86.54 2.219 l
+86.54 2.131 l
+86.54 1.984 l
+86.548 1.926 86.556 1.866 86.556 1.808 c
+86.556 1.646 l
+86.571 1.646 l
+86.588 1.734 86.619 1.811 86.658 1.881 c
+86.696 1.959 86.739 2.028 86.791 2.087 c
+86.85 2.146 86.916 2.19 86.997 2.219 c
+87.074 2.256 87.162 2.278 87.261 2.278 c
+87.445 2.278 87.585 2.223 87.673 2.117 c
+87.768 2.017 87.838 1.859 87.878 1.646 c
+87.893 1.646 l
+87.93 1.741 87.97 1.83 88.011 1.911 c
+88.059 1.988 88.113 2.05 88.173 2.102 c
+88.231 2.161 88.298 2.204 88.379 2.234 c
+88.456 2.263 88.543 2.278 88.643 2.278 c
+88.779 2.278 88.892 2.252 88.981 2.204 c
+89.069 2.153 89.135 2.08 89.187 1.984 c
+89.245 1.885 89.283 1.756 89.304 1.602 c
+89.334 1.455 89.349 1.271 89.349 1.058 c
+89.349 -1.264 l
+88.628 -1.264 l
+88.628 0.852 l
+88.628 1.018 88.62 1.153 88.614 1.263 c
+88.603 1.371 88.584 1.455 88.555 1.514 c
+88.533 1.579 88.503 1.631 88.466 1.66 c
+88.437 1.69 88.397 1.705 88.349 1.705 c
+88.231 1.705 88.136 1.616 88.069 1.44 c
+88.011 1.271 87.982 1.014 87.982 0.661 c
+87.982 -1.264 l
+h
+91.718 -1.324 m
+91.462 -1.324 91.234 -1.286 91.028 -1.22 c
+90.822 -1.143 90.646 -1.029 90.498 -0.882 c
+90.351 -0.728 90.234 -0.537 90.146 -0.309 c
+90.065 -0.085 90.028 0.18 90.028 0.484 c
+90.028 0.816 90.072 1.095 90.16 1.323 c
+90.256 1.558 90.384 1.741 90.542 1.881 c
+90.708 2.017 90.895 2.117 91.101 2.175 c
+91.307 2.242 91.517 2.278 91.733 2.278 c
+92.005 2.278 92.24 2.227 92.439 2.131 c
+92.645 2.043 92.811 1.911 92.938 1.734 c
+93.075 1.565 93.174 1.359 93.233 1.117 c
+93.299 0.881 93.335 0.617 93.335 0.323 c
+93.335 0.309 l
+90.969 0.309 l
+90.969 0.162 90.984 0.022 91.013 -0.104 c
+91.049 -0.231 91.105 -0.345 91.175 -0.441 c
+91.241 -0.53 91.325 -0.599 91.425 -0.647 c
+91.52 -0.698 91.635 -0.721 91.763 -0.721 c
+91.917 -0.721 92.057 -0.688 92.175 -0.618 c
+92.3 -0.551 92.387 -0.449 92.439 -0.309 c
+93.277 -0.383 l
+93.247 -0.482 93.192 -0.588 93.115 -0.706 c
+93.034 -0.816 92.931 -0.919 92.807 -1.014 c
+92.689 -1.103 92.535 -1.176 92.351 -1.235 c
+92.175 -1.294 91.961 -1.324 91.718 -1.324 c
+91.718 1.705 m
+91.631 1.705 91.542 1.69 91.454 1.66 c
+91.366 1.631 91.285 1.579 91.219 1.514 c
+91.149 1.444 91.09 1.356 91.043 1.249 c
+91.002 1.139 90.984 1.014 90.984 0.867 c
+92.454 0.867 l
+92.454 1.003 92.428 1.124 92.38 1.234 c
+92.34 1.341 92.285 1.429 92.219 1.499 c
+92.16 1.565 92.086 1.616 91.998 1.646 c
+91.909 1.683 91.814 1.705 91.718 1.705 c
+94.592 -2.631 m
+94.592 -2.014 l
+95.666 -2.014 l
+95.666 2.896 l
+94.592 2.896 l
+94.592 3.513 l
+96.518 3.513 l
+96.518 -2.631 l
+h
+f
+Q
+q 1 0 0 1 59.3004 267.0178 cm
+0 0 m
+-0.029 -0.58 -0.191 -1.022 -0.485 -1.323 c
+-0.779 -1.627 -1.199 -1.778 -1.735 -1.778 c
+-2.264 -1.778 -2.691 -1.579 -3.013 -1.176 c
+-3.329 -0.764 -3.484 -0.206 -3.484 0.5 c
+-3.484 1.47 l
+-3.484 2.165 -3.322 2.712 -2.999 3.117 c
+-2.675 3.517 -2.23 3.719 -1.661 3.719 c
+-1.154 3.719 -0.757 3.561 -0.47 3.248 c
+-0.187 2.944 -0.029 2.502 0 1.926 c
+-0.691 1.926 l
+-0.721 2.367 -0.816 2.679 -0.97 2.866 c
+-1.118 3.05 -1.349 3.146 -1.661 3.146 c
+-2.036 3.146 -2.319 2.999 -2.514 2.705 c
+-2.712 2.419 -2.808 2.003 -2.808 1.455 c
+-2.808 0.47 l
+-2.808 -0.07 -2.716 -0.484 -2.529 -0.779 c
+-2.344 -1.066 -2.08 -1.205 -1.735 -1.205 c
+-1.382 -1.205 -1.128 -1.117 -0.97 -0.941 c
+-0.816 -0.764 -0.721 -0.452 -0.691 0 c
+h
+2.454 1.661 m
+2.366 1.679 2.267 1.691 2.161 1.691 c
+1.826 1.691 1.591 1.507 1.455 1.147 c
+1.455 -1.705 l
+0.808 -1.705 l
+0.808 2.278 l
+1.44 2.278 l
+1.455 1.867 l
+1.631 2.19 1.874 2.352 2.19 2.352 c
+2.296 2.352 2.385 2.33 2.454 2.294 c
+h
+4.453 -1.778 m
+3.954 -1.778 3.572 -1.631 3.307 -1.338 c
+3.042 -1.043 2.91 -0.61 2.91 -0.029 c
+2.91 0.441 l
+2.91 1.037 3.035 1.503 3.293 1.837 c
+3.557 2.18 3.917 2.352 4.38 2.352 c
+4.839 2.352 5.182 2.198 5.409 1.897 c
+5.644 1.602 5.766 1.139 5.776 0.515 c
+5.776 0.088 l
+3.557 0.088 l
+3.557 0 l
+3.557 -0.434 3.634 -0.746 3.792 -0.941 c
+3.958 -1.128 4.189 -1.22 4.483 -1.22 c
+4.677 -1.22 4.85 -1.186 4.997 -1.117 c
+5.145 -1.039 5.28 -0.922 5.409 -0.764 c
+5.747 -1.176 l
+5.46 -1.579 5.03 -1.778 4.453 -1.778 c
+4.38 1.793 m
+4.104 1.793 3.902 1.698 3.777 1.515 c
+3.649 1.326 3.576 1.037 3.557 0.647 c
+5.13 0.647 l
+5.13 0.735 l
+5.107 1.118 5.041 1.386 4.924 1.544 c
+4.806 1.709 4.623 1.793 4.38 1.793 c
+8.598 -1.705 m
+8.558 -1.617 8.533 -1.469 8.525 -1.264 c
+8.29 -1.61 7.996 -1.778 7.644 -1.778 c
+7.279 -1.778 6.996 -1.683 6.791 -1.484 c
+6.593 -1.278 6.497 -0.992 6.497 -0.617 c
+6.497 -0.216 6.633 0.103 6.908 0.339 c
+7.181 0.58 7.555 0.706 8.025 0.706 c
+8.511 0.706 l
+8.511 1.132 l
+8.511 1.367 8.455 1.532 8.349 1.632 c
+8.239 1.738 8.077 1.793 7.864 1.793 c
+7.665 1.793 7.503 1.735 7.378 1.617 c
+7.262 1.5 7.202 1.353 7.202 1.176 c
+6.556 1.176 l
+6.556 1.371 6.614 1.562 6.732 1.75 c
+6.857 1.933 7.019 2.08 7.217 2.19 c
+7.422 2.297 7.651 2.352 7.908 2.352 c
+8.309 2.352 8.613 2.249 8.819 2.043 c
+9.032 1.837 9.146 1.544 9.157 1.162 c
+9.157 -0.852 l
+9.157 -1.157 9.194 -1.421 9.275 -1.646 c
+9.275 -1.705 l
+h
+7.732 -1.19 m
+7.897 -1.19 8.047 -1.147 8.187 -1.058 c
+8.334 -0.97 8.44 -0.86 8.511 -0.72 c
+8.511 0.221 l
+8.143 0.221 l
+7.827 0.221 7.584 0.151 7.408 0.015 c
+7.231 -0.114 7.143 -0.301 7.143 -0.544 c
+7.143 -0.771 7.187 -0.937 7.276 -1.043 c
+7.364 -1.143 7.515 -1.19 7.732 -1.19 c
+11.009 3.234 m
+11.009 2.278 l
+11.612 2.278 l
+11.612 1.75 l
+11.009 1.75 l
+11.009 -0.72 l
+11.009 -0.878 11.031 -0.995 11.083 -1.072 c
+11.141 -1.153 11.23 -1.19 11.347 -1.19 c
+11.436 -1.19 11.524 -1.176 11.612 -1.147 c
+11.612 -1.705 l
+11.465 -1.753 11.311 -1.778 11.156 -1.778 c
+10.899 -1.778 10.705 -1.687 10.568 -1.5 c
+10.429 -1.315 10.362 -1.055 10.362 -0.72 c
+10.362 1.75 l
+9.76 1.75 l
+9.76 2.278 l
+10.362 2.278 l
+10.362 3.234 l
+h
+13.803 -1.778 m
+13.302 -1.778 12.92 -1.631 12.656 -1.338 c
+12.391 -1.043 12.259 -0.61 12.259 -0.029 c
+12.259 0.441 l
+12.259 1.037 12.384 1.503 12.641 1.837 c
+12.906 2.18 13.265 2.352 13.728 2.352 c
+14.188 2.352 14.53 2.198 14.757 1.897 c
+14.993 1.602 15.114 1.139 15.125 0.515 c
+15.125 0.088 l
+12.906 0.088 l
+12.906 0 l
+12.906 -0.434 12.983 -0.746 13.141 -0.941 c
+13.306 -1.128 13.537 -1.22 13.832 -1.22 c
+14.026 -1.22 14.199 -1.186 14.346 -1.117 c
+14.493 -1.039 14.629 -0.922 14.757 -0.764 c
+15.096 -1.176 l
+14.809 -1.579 14.379 -1.778 13.803 -1.778 c
+13.728 1.793 m
+13.454 1.793 13.251 1.698 13.126 1.515 c
+12.997 1.326 12.924 1.037 12.906 0.647 c
+14.478 0.647 l
+14.478 0.735 l
+14.456 1.118 14.39 1.386 14.273 1.544 c
+14.155 1.709 13.971 1.793 13.728 1.793 c
+18.241 2.278 m
+18.256 1.837 l
+18.51 2.18 18.833 2.352 19.226 2.352 c
+19.931 2.352 20.288 1.882 20.299 0.941 c
+20.299 -1.705 l
+19.652 -1.705 l
+19.652 0.912 l
+19.652 1.224 19.597 1.444 19.491 1.573 c
+19.38 1.698 19.226 1.764 19.021 1.764 c
+18.863 1.764 18.715 1.709 18.58 1.602 c
+18.451 1.492 18.348 1.357 18.271 1.191 c
+18.271 -1.705 l
+17.624 -1.705 l
+17.624 2.278 l
+h
+22.695 -1.778 m
+22.195 -1.778 21.813 -1.631 21.549 -1.338 c
+21.284 -1.043 21.152 -0.61 21.152 -0.029 c
+21.152 0.441 l
+21.152 1.037 21.277 1.503 21.534 1.837 c
+21.799 2.18 22.158 2.352 22.622 2.352 c
+23.081 2.352 23.423 2.198 23.65 1.897 c
+23.885 1.602 24.007 1.139 24.018 0.515 c
+24.018 0.088 l
+21.799 0.088 l
+21.799 0 l
+21.799 -0.434 21.876 -0.746 22.034 -0.941 c
+22.199 -1.128 22.43 -1.22 22.725 -1.22 c
+22.919 -1.22 23.092 -1.186 23.239 -1.117 c
+23.386 -1.039 23.522 -0.922 23.65 -0.764 c
+23.989 -1.176 l
+23.702 -1.579 23.272 -1.778 22.695 -1.778 c
+22.622 1.793 m
+22.346 1.793 22.144 1.698 22.019 1.515 c
+21.89 1.326 21.817 1.037 21.799 0.647 c
+23.371 0.647 l
+23.371 0.735 l
+23.349 1.118 23.283 1.386 23.166 1.544 c
+23.048 1.709 22.864 1.793 22.622 1.793 c
+27.766 -0.559 m
+28.369 2.278 l
+29.015 2.278 l
+28.031 -1.705 l
+27.517 -1.705 l
+26.738 1.147 l
+25.988 -1.705 l
+25.458 -1.705 l
+24.503 2.278 l
+25.136 2.278 l
+25.753 -0.484 l
+26.487 2.278 l
+27.002 2.278 l
+h
+32.117 -1.705 -0.646 5.644 re
+32.999 0.47 m
+32.999 1.048 33.135 1.503 33.41 1.837 c
+33.694 2.18 34.065 2.352 34.528 2.352 c
+34.987 2.352 35.355 2.183 35.631 1.852 c
+35.914 1.529 36.06 1.081 36.071 0.515 c
+36.071 0.088 l
+36.071 -0.481 35.928 -0.937 35.645 -1.278 c
+35.369 -1.613 35.002 -1.778 34.543 -1.778 c
+34.079 -1.778 33.709 -1.617 33.425 -1.294 c
+33.15 -0.962 33.007 -0.521 32.999 0.029 c
+h
+33.646 0.088 m
+33.646 -0.316 33.723 -0.632 33.881 -0.867 c
+34.046 -1.103 34.267 -1.22 34.543 -1.22 c
+35.108 -1.22 35.403 -0.808 35.424 0.015 c
+35.424 0.47 l
+35.424 0.871 35.34 1.191 35.174 1.426 c
+35.016 1.669 34.8 1.793 34.528 1.793 c
+34.264 1.793 34.046 1.669 33.881 1.426 c
+33.723 1.191 33.646 0.871 33.646 0.47 c
+h
+38.291 -1.22 m
+38.504 -1.22 38.677 -1.157 38.805 -1.029 c
+38.941 -0.893 39.014 -0.702 39.026 -0.455 c
+39.643 -0.455 l
+39.621 -0.837 39.485 -1.157 39.232 -1.411 c
+38.974 -1.657 38.662 -1.778 38.291 -1.778 c
+37.798 -1.778 37.424 -1.627 37.159 -1.323 c
+36.901 -1.01 36.776 -0.544 36.776 0.073 c
+36.776 0.515 l
+36.776 1.11 36.901 1.565 37.159 1.882 c
+37.424 2.194 37.798 2.352 38.291 2.352 c
+38.692 2.352 39.012 2.22 39.247 1.955 c
+39.488 1.698 39.621 1.353 39.643 0.912 c
+39.026 0.912 l
+39.004 1.205 38.931 1.426 38.805 1.573 c
+38.688 1.72 38.515 1.793 38.291 1.793 c
+37.996 1.793 37.78 1.694 37.644 1.5 c
+37.504 1.312 37.431 1.004 37.424 0.574 c
+37.424 0.059 l
+37.424 -0.411 37.489 -0.746 37.63 -0.941 c
+37.776 -1.128 37.996 -1.22 38.291 -1.22 c
+42.436 -1.705 m
+42.395 -1.617 42.37 -1.469 42.362 -1.264 c
+42.127 -1.61 41.834 -1.778 41.48 -1.778 c
+41.117 -1.778 40.834 -1.683 40.628 -1.484 c
+40.429 -1.278 40.334 -0.992 40.334 -0.617 c
+40.334 -0.216 40.47 0.103 40.745 0.339 c
+41.017 0.58 41.393 0.706 41.863 0.706 c
+42.347 0.706 l
+42.347 1.132 l
+42.347 1.367 42.293 1.532 42.186 1.632 c
+42.076 1.738 41.914 1.793 41.701 1.793 c
+41.503 1.793 41.341 1.735 41.216 1.617 c
+41.098 1.5 41.04 1.353 41.04 1.176 c
+40.393 1.176 l
+40.393 1.371 40.452 1.562 40.569 1.75 c
+40.695 1.933 40.855 2.08 41.054 2.19 c
+41.26 2.297 41.488 2.352 41.745 2.352 c
+42.146 2.352 42.451 2.249 42.656 2.043 c
+42.869 1.837 42.983 1.544 42.995 1.162 c
+42.995 -0.852 l
+42.995 -1.157 43.031 -1.421 43.112 -1.646 c
+43.112 -1.705 l
+h
+41.568 -1.19 m
+41.734 -1.19 41.885 -1.147 42.025 -1.058 c
+42.171 -0.97 42.278 -0.86 42.347 -0.72 c
+42.347 0.221 l
+41.98 0.221 l
+41.664 0.221 41.422 0.151 41.245 0.015 c
+41.069 -0.114 40.98 -0.301 40.98 -0.544 c
+40.98 -0.771 41.025 -0.937 41.113 -1.043 c
+41.202 -1.143 41.352 -1.19 41.568 -1.19 c
+44.699 -1.705 -0.646 5.644 re
+49.08 1.661 m
+48.992 1.679 48.892 1.691 48.786 1.691 c
+48.452 1.691 48.217 1.507 48.08 1.147 c
+48.08 -1.705 l
+47.434 -1.705 l
+47.434 2.278 l
+48.065 2.278 l
+48.08 1.867 l
+48.257 2.19 48.499 2.352 48.815 2.352 c
+48.922 2.352 49.01 2.33 49.08 2.294 c
+h
+51.079 -1.778 m
+50.579 -1.778 50.197 -1.631 49.933 -1.338 c
+49.668 -1.043 49.536 -0.61 49.536 -0.029 c
+49.536 0.441 l
+49.536 1.037 49.661 1.503 49.918 1.837 c
+50.182 2.18 50.542 2.352 51.006 2.352 c
+51.465 2.352 51.807 2.198 52.034 1.897 c
+52.269 1.602 52.391 1.139 52.402 0.515 c
+52.402 0.088 l
+50.182 0.088 l
+50.182 0 l
+50.182 -0.434 50.26 -0.746 50.417 -0.941 c
+50.583 -1.128 50.814 -1.22 51.108 -1.22 c
+51.303 -1.22 51.476 -1.186 51.623 -1.117 c
+51.77 -1.039 51.906 -0.922 52.034 -0.764 c
+52.373 -1.176 l
+52.086 -1.579 51.656 -1.778 51.079 -1.778 c
+51.006 1.793 m
+50.73 1.793 50.528 1.698 50.403 1.515 c
+50.274 1.326 50.201 1.037 50.182 0.647 c
+51.755 0.647 l
+51.755 0.735 l
+51.733 1.118 51.667 1.386 51.55 1.544 c
+51.432 1.709 51.248 1.793 51.006 1.793 c
+56.061 0.088 m
+56.061 -0.54 55.945 -1.01 55.71 -1.323 c
+55.481 -1.627 55.165 -1.778 54.754 -1.778 c
+54.349 -1.778 54.041 -1.627 53.828 -1.323 c
+53.828 -3.233 l
+53.181 -3.233 l
+53.181 2.278 l
+53.769 2.278 l
+53.813 1.837 l
+54.026 2.18 54.335 2.352 54.739 2.352 c
+55.18 2.352 55.507 2.198 55.724 1.897 c
+55.937 1.592 56.051 1.135 56.061 0.53 c
+h
+55.415 0.47 m
+55.415 0.912 55.346 1.235 55.209 1.44 c
+55.07 1.654 54.849 1.764 54.548 1.764 c
+54.232 1.764 53.993 1.61 53.828 1.309 c
+53.828 -0.764 l
+53.993 -1.069 54.232 -1.22 54.548 -1.22 c
+54.842 -1.22 55.055 -1.117 55.195 -0.912 c
+55.331 -0.698 55.404 -0.367 55.415 0.073 c
+h
+56.767 0.47 m
+56.767 1.048 56.903 1.503 57.179 1.837 c
+57.462 2.18 57.833 2.352 58.297 2.352 c
+58.756 2.352 59.123 2.183 59.398 1.852 c
+59.681 1.529 59.828 1.081 59.839 0.515 c
+59.839 0.088 l
+59.839 -0.481 59.697 -0.937 59.413 -1.278 c
+59.138 -1.613 58.77 -1.778 58.311 -1.778 c
+57.848 -1.778 57.476 -1.617 57.194 -1.294 c
+56.918 -0.962 56.775 -0.521 56.767 0.029 c
+h
+57.414 0.088 m
+57.414 -0.316 57.491 -0.632 57.649 -0.867 c
+57.815 -1.103 58.035 -1.22 58.311 -1.22 c
+58.877 -1.22 59.171 -0.808 59.193 0.015 c
+59.193 0.47 l
+59.193 0.871 59.109 1.191 58.943 1.426 c
+58.785 1.669 58.569 1.793 58.297 1.793 c
+58.031 1.793 57.815 1.669 57.649 1.426 c
+57.491 1.191 57.414 0.871 57.414 0.47 c
+h
+62.617 -0.69 m
+62.617 -0.544 62.563 -0.422 62.456 -0.324 c
+62.346 -0.228 62.14 -0.11 61.839 0.029 c
+61.493 0.177 61.251 0.298 61.104 0.397 c
+60.957 0.503 60.846 0.621 60.78 0.75 c
+60.711 0.875 60.678 1.033 60.678 1.22 c
+60.678 1.544 60.795 1.812 61.03 2.028 c
+61.266 2.242 61.567 2.352 61.941 2.352 c
+62.324 2.352 62.633 2.238 62.868 2.014 c
+63.103 1.786 63.221 1.5 63.221 1.147 c
+62.573 1.147 l
+62.573 1.323 62.515 1.474 62.397 1.602 c
+62.28 1.727 62.125 1.793 61.941 1.793 c
+61.744 1.793 61.592 1.738 61.486 1.632 c
+61.376 1.532 61.324 1.401 61.324 1.235 c
+61.324 1.106 61.361 1 61.442 0.912 c
+61.519 0.831 61.71 0.728 62.015 0.603 c
+62.492 0.416 62.824 0.228 63 0.044 c
+63.176 -0.132 63.265 -0.36 63.265 -0.632 c
+63.265 -0.985 63.14 -1.264 62.897 -1.469 c
+62.662 -1.675 62.346 -1.778 61.956 -1.778 c
+61.534 -1.778 61.196 -1.66 60.942 -1.425 c
+60.685 -1.183 60.56 -0.878 60.56 -0.515 c
+61.206 -0.515 l
+61.214 -0.742 61.284 -0.918 61.412 -1.043 c
+61.538 -1.161 61.721 -1.22 61.956 -1.22 c
+62.17 -1.22 62.331 -1.172 62.442 -1.072 c
+62.559 -0.977 62.617 -0.849 62.617 -0.69 c
+64.808 -1.705 -0.646 3.983 re
+64.852 3.322 m
+64.852 3.212 64.823 3.12 64.763 3.043 c
+64.705 2.973 64.609 2.94 64.485 2.94 c
+64.367 2.94 64.271 2.973 64.205 3.043 c
+64.146 3.12 64.117 3.212 64.117 3.322 c
+64.117 3.439 64.146 3.532 64.205 3.601 c
+64.271 3.678 64.367 3.719 64.485 3.719 c
+64.609 3.719 64.705 3.678 64.763 3.601 c
+64.823 3.52 64.852 3.429 64.852 3.322 c
+66.675 3.234 m
+66.675 2.278 l
+67.277 2.278 l
+67.277 1.75 l
+66.675 1.75 l
+66.675 -0.72 l
+66.675 -0.878 66.697 -0.995 66.748 -1.072 c
+66.807 -1.153 66.895 -1.19 67.013 -1.19 c
+67.101 -1.19 67.189 -1.176 67.277 -1.147 c
+67.277 -1.705 l
+67.13 -1.753 66.976 -1.778 66.822 -1.778 c
+66.565 -1.778 66.37 -1.687 66.234 -1.5 c
+66.094 -1.315 66.028 -1.055 66.028 -0.72 c
+66.028 1.75 l
+65.426 1.75 l
+65.426 2.278 l
+66.028 2.278 l
+66.028 3.234 l
+h
+67.836 0.47 m
+67.836 1.048 67.972 1.503 68.248 1.837 c
+68.53 2.18 68.902 2.352 69.364 2.352 c
+69.824 2.352 70.191 2.183 70.467 1.852 c
+70.75 1.529 70.897 1.081 70.908 0.515 c
+70.908 0.088 l
+70.908 -0.481 70.764 -0.937 70.482 -1.278 c
+70.206 -1.613 69.839 -1.778 69.38 -1.778 c
+68.917 -1.778 68.545 -1.617 68.262 -1.294 c
+67.986 -0.962 67.843 -0.521 67.836 0.029 c
+h
+68.483 0.088 m
+68.483 -0.316 68.56 -0.632 68.718 -0.867 c
+68.883 -1.103 69.104 -1.22 69.38 -1.22 c
+69.945 -1.22 70.239 -0.808 70.261 0.015 c
+70.261 0.47 l
+70.261 0.871 70.176 1.191 70.012 1.426 c
+69.854 1.669 69.636 1.793 69.364 1.793 c
+69.1 1.793 68.883 1.669 68.718 1.426 c
+68.56 1.191 68.483 0.871 68.483 0.47 c
+h
+73.392 1.661 m
+73.304 1.679 73.205 1.691 73.098 1.691 c
+72.763 1.691 72.528 1.507 72.393 1.147 c
+72.393 -1.705 l
+71.745 -1.705 l
+71.745 2.278 l
+72.378 2.278 l
+72.393 1.867 l
+72.569 2.19 72.811 2.352 73.127 2.352 c
+73.234 2.352 73.322 2.33 73.392 2.294 c
+h
+75.2 -0.617 m
+75.92 2.278 l
+76.611 2.278 l
+75.317 -2.263 l
+75.219 -2.605 75.075 -2.866 74.891 -3.042 c
+74.715 -3.219 74.513 -3.307 74.289 -3.307 c
+74.201 -3.307 74.087 -3.285 73.951 -3.248 c
+73.951 -2.705 l
+74.097 -2.719 l
+74.282 -2.719 74.428 -2.675 74.539 -2.587 c
+74.645 -2.499 74.733 -2.341 74.803 -2.117 c
+74.92 -1.675 l
+73.76 2.278 l
+74.465 2.278 l
+h
+76.89 -1.352 m
+76.89 -1.234 76.923 -1.139 76.993 -1.058 c
+77.06 -0.981 77.162 -0.941 77.302 -0.941 c
+77.449 -0.941 77.555 -0.981 77.625 -1.058 c
+77.702 -1.139 77.743 -1.234 77.743 -1.352 c
+77.743 -1.462 77.702 -1.554 77.625 -1.631 c
+77.555 -1.708 77.449 -1.749 77.302 -1.749 c
+77.162 -1.749 77.06 -1.708 76.993 -1.631 c
+76.923 -1.554 76.89 -1.462 76.89 -1.352 c
+81.345 -1.705 -0.676 5.35 re
+82.726 -1.705 m
+82.726 1.75 l
+82.197 1.75 l
+82.197 2.278 l
+82.726 2.278 l
+82.726 2.734 l
+82.726 3.135 82.822 3.447 83.02 3.675 c
+83.226 3.899 83.505 4.013 83.857 4.013 c
+83.994 4.013 84.127 3.991 84.255 3.954 c
+84.225 3.41 l
+84.127 3.429 84.027 3.439 83.932 3.439 c
+83.556 3.439 83.373 3.175 83.373 2.646 c
+83.373 2.278 l
+84.048 2.278 l
+84.048 1.75 l
+83.373 1.75 l
+83.373 -1.705 l
+h
+f
+Q
+q 1 0 0 1 147.1862 270.7224 cm
+0 0 m
+-0.383 0 l
+-0.383 -5.835 l
+0 -5.835 l
+0 -6.659 l
+-1.426 -6.659 l
+-1.426 0.823 l
+0 0.823 l
+h
+3.63 -3.543 m
+3.63 -4.171 3.52 -4.653 3.307 -4.983 c
+3.09 -5.318 2.77 -5.483 2.352 -5.483 c
+2.028 -5.483 1.768 -5.351 1.572 -5.087 c
+1.572 -6.938 l
+0.529 -6.938 l
+0.529 -1.426 l
+1.484 -1.426 l
+1.529 -1.794 l
+1.723 -1.5 1.992 -1.353 2.337 -1.353 c
+2.755 -1.353 3.075 -1.511 3.293 -1.823 c
+3.505 -2.128 3.619 -2.598 3.63 -3.234 c
+h
+2.587 -3.278 m
+2.587 -2.896 2.543 -2.628 2.454 -2.469 c
+2.373 -2.315 2.234 -2.234 2.028 -2.234 c
+1.822 -2.234 1.668 -2.323 1.572 -2.5 c
+1.572 -4.366 l
+1.66 -4.535 1.815 -4.616 2.042 -4.616 c
+2.248 -4.616 2.389 -4.535 2.469 -4.366 c
+2.547 -4.189 2.587 -3.917 2.587 -3.543 c
+h
+6.217 -2.44 m
+5.88 -2.411 l
+5.593 -2.411 5.402 -2.536 5.306 -2.778 c
+5.306 -5.409 l
+4.262 -5.409 l
+4.262 -1.426 l
+5.232 -1.426 l
+5.262 -1.867 l
+5.427 -1.525 5.659 -1.353 5.953 -1.353 c
+6.071 -1.353 6.162 -1.374 6.232 -1.411 c
+h
+6.511 -3.293 m
+6.511 -2.687 6.651 -2.213 6.938 -1.867 c
+7.22 -1.525 7.613 -1.353 8.114 -1.353 c
+8.621 -1.353 9.018 -1.525 9.304 -1.867 c
+9.587 -2.213 9.73 -2.687 9.73 -3.293 c
+9.73 -3.558 l
+9.73 -4.156 9.587 -4.627 9.304 -4.969 c
+9.018 -5.314 8.621 -5.483 8.114 -5.483 c
+7.603 -5.483 7.206 -5.314 6.923 -4.969 c
+6.647 -4.627 6.511 -4.152 6.511 -3.543 c
+h
+7.555 -3.558 m
+7.555 -4.263 7.739 -4.616 8.114 -4.616 c
+8.467 -4.616 8.658 -4.322 8.687 -3.734 c
+8.687 -3.293 l
+8.687 -2.932 8.635 -2.66 8.54 -2.484 c
+8.44 -2.308 8.297 -2.22 8.114 -2.22 c
+7.937 -2.22 7.798 -2.308 7.702 -2.484 c
+7.603 -2.66 7.555 -2.932 7.555 -3.293 c
+h
+11.465 -1.426 m
+11.465 -5.63 l
+11.465 -6.078 11.362 -6.423 11.156 -6.659 c
+10.95 -6.894 10.657 -7.011 10.274 -7.011 c
+10.105 -7.011 9.951 -6.99 9.803 -6.953 c
+9.789 -6.101 l
+9.907 -6.13 9.999 -6.145 10.069 -6.145 c
+10.304 -6.145 10.421 -5.983 10.421 -5.659 c
+10.421 -1.426 l
+h
+10.362 -0.397 m
+10.362 -0.243 10.41 -0.114 10.509 -0.015 c
+10.616 0.091 10.752 0.147 10.921 0.147 c
+11.098 0.147 11.233 0.091 11.333 -0.015 c
+11.439 -0.114 11.494 -0.243 11.494 -0.397 c
+11.494 -0.566 11.439 -0.702 11.333 -0.809 c
+11.233 -0.908 11.098 -0.956 10.921 -0.956 c
+10.744 -0.956 10.605 -0.908 10.509 -0.809 c
+10.41 -0.702 10.362 -0.566 10.362 -0.397 c
+13.832 -5.483 m
+13.302 -5.483 12.883 -5.328 12.582 -5.012 c
+12.288 -4.69 12.141 -4.23 12.141 -3.631 c
+12.141 -3.323 l
+12.141 -2.697 12.277 -2.213 12.552 -1.867 c
+12.824 -1.525 13.217 -1.353 13.728 -1.353 c
+14.229 -1.353 14.599 -1.515 14.846 -1.837 c
+15.099 -2.161 15.232 -2.639 15.243 -3.263 c
+15.243 -3.763 l
+13.17 -3.763 l
+13.188 -4.057 13.251 -4.274 13.361 -4.41 c
+13.479 -4.549 13.659 -4.616 13.905 -4.616 c
+14.247 -4.616 14.537 -4.499 14.772 -4.263 c
+15.184 -4.895 l
+15.056 -5.072 14.867 -5.215 14.626 -5.322 c
+14.379 -5.428 14.115 -5.483 13.832 -5.483 c
+13.184 -3.043 m
+14.214 -3.043 l
+14.214 -2.94 l
+14.214 -2.705 14.173 -2.529 14.096 -2.411 c
+14.026 -2.286 13.898 -2.22 13.714 -2.22 c
+13.537 -2.22 13.406 -2.29 13.317 -2.425 c
+13.236 -2.554 13.192 -2.76 13.184 -3.043 c
+17.271 -4.616 m
+17.565 -4.616 17.716 -4.421 17.726 -4.028 c
+18.697 -4.028 l
+18.697 -4.461 18.565 -4.814 18.3 -5.087 c
+18.035 -5.351 17.697 -5.483 17.286 -5.483 c
+16.775 -5.483 16.382 -5.328 16.11 -5.012 c
+15.845 -4.69 15.706 -4.219 15.698 -3.601 c
+15.698 -3.278 l
+15.698 -2.654 15.831 -2.176 16.095 -1.852 c
+16.367 -1.521 16.764 -1.353 17.286 -1.353 c
+17.716 -1.353 18.057 -1.492 18.314 -1.764 c
+18.568 -2.04 18.697 -2.421 18.697 -2.911 c
+17.726 -2.911 l
+17.726 -2.697 17.686 -2.529 17.609 -2.411 c
+17.539 -2.286 17.422 -2.22 17.256 -2.22 c
+17.08 -2.22 16.951 -2.286 16.874 -2.411 c
+16.793 -2.539 16.749 -2.789 16.742 -3.161 c
+16.742 -3.572 l
+16.742 -3.896 16.756 -4.123 16.786 -4.248 c
+16.822 -4.377 16.878 -4.469 16.947 -4.528 c
+17.025 -4.586 17.132 -4.616 17.271 -4.616 c
+20.505 -0.456 m
+20.505 -1.426 l
+21.034 -1.426 l
+21.034 -2.22 l
+20.505 -2.22 l
+20.505 -4.189 l
+20.505 -4.347 20.523 -4.454 20.564 -4.513 c
+20.612 -4.572 20.696 -4.601 20.814 -4.601 c
+20.92 -4.601 21.005 -4.594 21.063 -4.572 c
+21.063 -5.38 l
+20.887 -5.446 20.696 -5.483 20.49 -5.483 c
+19.814 -5.483 19.469 -5.097 19.461 -4.322 c
+19.461 -2.22 l
+19.006 -2.22 l
+19.006 -1.426 l
+19.461 -1.426 l
+19.461 -0.456 l
+h
+24.297 -1.426 m
+24.327 -1.823 l
+24.562 -1.511 24.863 -1.353 25.238 -1.353 c
+25.921 -1.353 26.274 -1.833 26.296 -2.793 c
+26.296 -5.409 l
+25.252 -5.409 l
+25.252 -2.866 l
+25.252 -2.643 25.216 -2.481 25.15 -2.382 c
+25.08 -2.286 24.963 -2.234 24.797 -2.234 c
+24.61 -2.234 24.463 -2.33 24.356 -2.514 c
+24.356 -5.409 l
+23.312 -5.409 l
+23.312 -1.426 l
+h
+28.928 -5.409 m
+28.898 -5.351 28.868 -5.247 28.839 -5.101 c
+28.652 -5.358 28.402 -5.483 28.09 -5.483 c
+27.756 -5.483 27.476 -5.376 27.251 -5.16 c
+27.035 -4.935 26.929 -4.645 26.929 -4.293 c
+26.929 -3.881 27.06 -3.564 27.326 -3.337 c
+27.59 -3.102 27.972 -2.984 28.471 -2.984 c
+28.795 -2.984 l
+28.795 -2.66 l
+28.795 -2.484 28.758 -2.363 28.693 -2.294 c
+28.633 -2.216 28.545 -2.176 28.427 -2.176 c
+28.17 -2.176 28.045 -2.33 28.045 -2.631 c
+27.002 -2.631 l
+27.002 -2.261 27.137 -1.955 27.413 -1.72 c
+27.685 -1.478 28.034 -1.353 28.457 -1.353 c
+28.898 -1.353 29.236 -1.47 29.471 -1.706 c
+29.714 -1.933 29.838 -2.257 29.838 -2.675 c
+29.838 -4.542 l
+29.838 -4.888 29.886 -5.156 29.986 -5.351 c
+29.986 -5.409 l
+h
+28.325 -4.659 m
+28.431 -4.659 28.523 -4.642 28.604 -4.601 c
+28.693 -4.553 28.755 -4.495 28.795 -4.424 c
+28.795 -3.601 l
+28.545 -3.601 l
+28.369 -3.601 28.226 -3.66 28.119 -3.778 c
+28.02 -3.888 27.972 -4.035 27.972 -4.219 c
+27.972 -4.513 28.09 -4.659 28.325 -4.659 c
+31.559 -1.426 m
+31.588 -1.794 l
+31.823 -1.5 32.132 -1.353 32.514 -1.353 c
+32.915 -1.353 33.194 -1.536 33.352 -1.897 c
+33.587 -1.536 33.914 -1.353 34.337 -1.353 c
+35.031 -1.353 35.384 -1.837 35.395 -2.808 c
+35.395 -5.409 l
+34.366 -5.409 l
+34.366 -2.866 l
+34.366 -2.643 34.329 -2.481 34.263 -2.382 c
+34.204 -2.286 34.094 -2.234 33.94 -2.234 c
+33.741 -2.234 33.601 -2.352 33.514 -2.587 c
+33.514 -5.409 l
+32.47 -5.409 l
+32.47 -2.882 l
+32.47 -2.646 32.44 -2.481 32.381 -2.382 c
+32.323 -2.286 32.213 -2.234 32.059 -2.234 c
+31.882 -2.234 31.739 -2.33 31.632 -2.514 c
+31.632 -5.409 l
+30.588 -5.409 l
+30.588 -1.426 l
+h
+37.776 -5.483 m
+37.247 -5.483 36.828 -5.328 36.527 -5.012 c
+36.233 -4.69 36.086 -4.23 36.086 -3.631 c
+36.086 -3.323 l
+36.086 -2.697 36.222 -2.213 36.498 -1.867 c
+36.77 -1.525 37.163 -1.353 37.674 -1.353 c
+38.173 -1.353 38.544 -1.515 38.79 -1.837 c
+39.044 -2.161 39.176 -2.639 39.187 -3.263 c
+39.187 -3.763 l
+37.115 -3.763 l
+37.133 -4.057 37.196 -4.274 37.306 -4.41 c
+37.424 -4.549 37.603 -4.616 37.85 -4.616 c
+38.191 -4.616 38.482 -4.499 38.717 -4.263 c
+39.128 -4.895 l
+39 -5.072 38.812 -5.215 38.57 -5.322 c
+38.324 -5.428 38.059 -5.483 37.776 -5.483 c
+37.129 -3.043 m
+38.158 -3.043 l
+38.158 -2.94 l
+38.158 -2.705 38.118 -2.529 38.041 -2.411 c
+37.971 -2.286 37.842 -2.22 37.659 -2.22 c
+37.482 -2.22 37.35 -2.29 37.262 -2.425 c
+37.181 -2.554 37.137 -2.76 37.129 -3.043 c
+39.525 0.823 m
+40.951 0.823 l
+40.951 -6.659 l
+39.525 -6.659 l
+39.525 -5.835 l
+39.907 -5.835 l
+39.907 0 l
+39.525 0 l
+h
+f
+Q
+191.474 265.313 -0.647 3.983 re
+191.518 270.34 m
+191.518 270.229 191.488 270.138 191.43 270.061 c
+191.371 269.991 191.276 269.958 191.151 269.958 c
+191.033 269.958 190.937 269.991 190.871 270.061 c
+190.813 270.138 190.783 270.229 190.783 270.34 c
+190.783 270.457 190.813 270.549 190.871 270.619 c
+190.937 270.696 191.033 270.737 191.151 270.737 c
+191.276 270.737 191.371 270.696 191.43 270.619 c
+191.488 270.538 191.518 270.447 191.518 270.34 c
+194.428 266.327 m
+194.428 266.474 194.374 266.595 194.267 266.694 c
+194.156 266.79 193.951 266.908 193.649 267.047 c
+193.304 267.195 193.061 267.316 192.915 267.415 c
+192.768 267.521 192.658 267.639 192.591 267.768 c
+192.521 267.893 192.488 268.051 192.488 268.238 c
+192.488 268.562 192.606 268.83 192.841 269.046 c
+193.076 269.26 193.377 269.37 193.753 269.37 c
+194.135 269.37 194.443 269.256 194.678 269.032 c
+194.914 268.804 195.031 268.517 195.031 268.165 c
+194.384 268.165 l
+194.384 268.34 194.326 268.492 194.208 268.62 c
+194.09 268.745 193.936 268.811 193.753 268.811 c
+193.554 268.811 193.404 268.756 193.296 268.65 c
+193.186 268.55 193.135 268.419 193.135 268.253 c
+193.135 268.124 193.171 268.018 193.252 267.929 c
+193.329 267.848 193.521 267.746 193.826 267.621 c
+194.304 267.434 194.634 267.245 194.811 267.062 c
+194.987 266.886 195.075 266.657 195.075 266.386 c
+195.075 266.033 194.95 265.753 194.708 265.548 c
+194.472 265.342 194.156 265.24 193.767 265.24 c
+193.344 265.24 193.007 265.357 192.753 265.593 c
+192.496 265.834 192.371 266.14 192.371 266.503 c
+193.017 266.503 l
+193.025 266.276 193.094 266.1 193.223 265.974 c
+193.348 265.857 193.532 265.798 193.767 265.798 c
+193.98 265.798 194.142 265.846 194.252 265.945 c
+194.37 266.04 194.428 266.169 194.428 266.327 c
+200.514 267.106 m
+200.514 266.478 200.396 266.007 200.161 265.695 c
+199.933 265.39 199.617 265.24 199.206 265.24 c
+198.802 265.24 198.493 265.39 198.28 265.695 c
+198.28 263.784 l
+197.633 263.784 l
+197.633 269.296 l
+198.221 269.296 l
+198.265 268.855 l
+198.478 269.197 198.787 269.37 199.191 269.37 c
+199.632 269.37 199.959 269.215 200.176 268.914 c
+200.389 268.609 200.503 268.153 200.514 267.547 c
+h
+199.868 267.488 m
+199.868 267.929 199.797 268.253 199.662 268.458 c
+199.521 268.672 199.301 268.782 199 268.782 c
+198.684 268.782 198.445 268.627 198.28 268.326 c
+198.28 266.254 l
+198.445 265.949 198.684 265.798 199 265.798 c
+199.294 265.798 199.507 265.901 199.646 266.106 c
+199.783 266.32 199.856 266.651 199.868 267.091 c
+h
+202.998 268.679 m
+202.91 268.697 202.811 268.708 202.704 268.708 c
+202.37 268.708 202.135 268.525 201.998 268.165 c
+201.998 265.313 l
+201.352 265.313 l
+201.352 269.296 l
+201.984 269.296 l
+201.998 268.885 l
+202.175 269.208 202.418 269.37 202.734 269.37 c
+202.84 269.37 202.929 269.348 202.998 269.311 c
+h
+203.44 267.488 m
+203.44 268.066 203.575 268.521 203.851 268.855 c
+204.134 269.197 204.504 269.37 204.967 269.37 c
+205.427 269.37 205.794 269.201 206.07 268.87 c
+206.353 268.546 206.5 268.099 206.511 267.532 c
+206.511 267.106 l
+206.511 266.537 206.368 266.081 206.085 265.739 c
+205.809 265.404 205.441 265.24 204.982 265.24 c
+204.52 265.24 204.148 265.401 203.866 265.724 c
+203.59 266.055 203.446 266.497 203.44 267.047 c
+h
+204.086 267.106 m
+204.086 266.702 204.163 266.386 204.321 266.15 c
+204.487 265.915 204.707 265.798 204.982 265.798 c
+205.549 265.798 205.842 266.21 205.865 267.033 c
+205.865 267.488 l
+205.865 267.889 205.78 268.209 205.615 268.444 c
+205.456 268.687 205.24 268.811 204.967 268.811 c
+204.703 268.811 204.487 268.687 204.321 268.444 c
+204.163 268.209 204.086 267.889 204.086 267.488 c
+h
+208.422 266.298 m
+209.171 269.296 l
+209.833 269.296 l
+208.657 265.313 l
+208.172 265.313 l
+206.981 269.296 l
+207.643 269.296 l
+h
+211.215 265.313 -0.646 3.983 re
+211.259 270.34 m
+211.259 270.229 211.23 270.138 211.171 270.061 c
+211.112 269.991 211.016 269.958 210.891 269.958 c
+210.774 269.958 210.679 269.991 210.612 270.061 c
+210.553 270.138 210.524 270.229 210.524 270.34 c
+210.524 270.457 210.553 270.549 210.612 270.619 c
+210.679 270.696 210.774 270.737 210.891 270.737 c
+211.016 270.737 211.112 270.696 211.171 270.619 c
+211.23 270.538 211.259 270.447 211.259 270.34 c
+212.097 267.488 m
+212.097 268.095 212.207 268.562 212.435 268.885 c
+212.67 269.208 212.997 269.37 213.42 269.37 c
+213.802 269.37 214.1 269.212 214.316 268.899 c
+214.316 270.958 l
+214.964 270.958 l
+214.964 265.313 l
+214.375 265.313 l
+214.331 265.739 l
+214.125 265.404 213.821 265.24 213.42 265.24 c
+213.008 265.24 212.685 265.394 212.45 265.71 c
+212.215 266.033 212.097 266.489 212.097 267.077 c
+h
+212.743 267.106 m
+212.743 266.665 212.806 266.335 212.934 266.121 c
+213.071 265.915 213.291 265.813 213.596 265.813 c
+213.919 265.813 214.158 265.974 214.316 266.298 c
+214.316 268.311 l
+214.147 268.624 213.908 268.782 213.596 268.782 c
+213.291 268.782 213.071 268.679 212.934 268.473 c
+212.806 268.267 212.743 267.944 212.743 267.503 c
+h
+217.389 265.24 m
+216.888 265.24 216.506 265.387 216.242 265.68 c
+215.978 265.974 215.845 266.408 215.845 266.989 c
+215.845 267.459 l
+215.845 268.054 215.97 268.521 216.227 268.855 c
+216.491 269.197 216.852 269.37 217.315 269.37 c
+217.775 269.37 218.116 269.215 218.344 268.914 c
+218.579 268.62 218.7 268.157 218.712 267.532 c
+218.712 267.106 l
+216.491 267.106 l
+216.491 267.018 l
+216.491 266.584 216.569 266.272 216.727 266.077 c
+216.892 265.89 217.124 265.798 217.418 265.798 c
+217.613 265.798 217.786 265.832 217.933 265.901 c
+218.079 265.978 218.216 266.096 218.344 266.254 c
+218.682 265.842 l
+218.395 265.438 217.965 265.24 217.389 265.24 c
+217.315 268.811 m
+217.04 268.811 216.838 268.716 216.712 268.532 c
+216.584 268.344 216.51 268.054 216.491 267.665 c
+218.064 267.665 l
+218.064 267.753 l
+218.043 268.135 217.977 268.403 217.859 268.562 c
+217.742 268.727 217.557 268.811 217.315 268.811 c
+219.358 267.488 m
+219.358 268.095 219.469 268.562 219.696 268.885 c
+219.932 269.208 220.258 269.37 220.681 269.37 c
+221.063 269.37 221.361 269.212 221.578 268.899 c
+221.578 270.958 l
+222.224 270.958 l
+222.224 265.313 l
+221.636 265.313 l
+221.592 265.739 l
+221.387 265.404 221.081 265.24 220.681 265.24 c
+220.269 265.24 219.946 265.394 219.711 265.71 c
+219.475 266.033 219.358 266.489 219.358 267.077 c
+h
+220.005 267.106 m
+220.005 266.665 220.067 266.335 220.196 266.121 c
+220.332 265.915 220.553 265.813 220.857 265.813 c
+221.181 265.813 221.42 265.974 221.578 266.298 c
+221.578 268.311 l
+221.409 268.624 221.17 268.782 220.857 268.782 c
+220.553 268.782 220.332 268.679 220.196 268.473 c
+220.067 268.267 220.005 267.944 220.005 267.503 c
+h
+223.238 264.24 m
+222.841 264.504 l
+223.077 264.828 223.198 265.163 223.209 265.504 c
+223.209 266.121 l
+223.871 266.121 l
+223.871 265.593 l
+223.871 265.335 223.805 265.088 223.68 264.843 c
+223.562 264.6 223.415 264.398 223.238 264.24 c
+229.868 265.974 m
+229.75 265.828 l
+229.416 265.435 228.919 265.24 228.266 265.24 c
+227.685 265.24 227.23 265.431 226.899 265.813 c
+226.575 266.195 226.407 266.738 226.399 267.444 c
+226.399 268.488 l
+226.399 269.242 226.546 269.803 226.839 270.179 c
+227.141 270.549 227.593 270.737 228.192 270.737 c
+228.699 270.737 229.096 270.594 229.383 270.31 c
+229.677 270.024 229.839 269.62 229.868 269.09 c
+229.191 269.09 l
+229.17 269.421 229.077 269.682 228.913 269.869 c
+228.755 270.065 228.52 270.164 228.207 270.164 c
+227.814 270.164 227.531 270.035 227.354 269.782 c
+227.178 269.524 227.082 269.124 227.075 268.576 c
+227.075 267.488 l
+227.075 266.948 227.178 266.533 227.384 266.239 c
+227.589 265.952 227.884 265.813 228.266 265.813 c
+228.626 265.813 228.905 265.901 229.104 266.077 c
+229.191 266.165 l
+229.191 267.4 l
+228.221 267.4 l
+228.221 267.974 l
+229.868 267.974 l
+h
+231.587 265.313 -0.646 3.983 re
+231.632 270.34 m
+231.632 270.229 231.602 270.138 231.543 270.061 c
+231.485 269.991 231.389 269.958 231.265 269.958 c
+231.147 269.958 231.051 269.991 230.985 270.061 c
+230.926 270.138 230.897 270.229 230.897 270.34 c
+230.897 270.457 230.926 270.549 230.985 270.619 c
+231.051 270.696 231.147 270.737 231.265 270.737 c
+231.389 270.737 231.485 270.696 231.543 270.619 c
+231.602 270.538 231.632 270.447 231.632 270.34 c
+233.455 270.252 m
+233.455 269.296 l
+234.057 269.296 l
+234.057 268.768 l
+233.455 268.768 l
+233.455 266.298 l
+233.455 266.14 233.476 266.022 233.528 265.945 c
+233.586 265.864 233.675 265.828 233.792 265.828 c
+233.881 265.828 233.969 265.842 234.057 265.871 c
+234.057 265.313 l
+233.91 265.265 233.756 265.24 233.602 265.24 c
+233.345 265.24 233.15 265.331 233.014 265.518 c
+232.874 265.703 232.808 265.963 232.808 266.298 c
+232.808 268.768 l
+232.205 268.768 l
+232.205 269.296 l
+232.808 269.296 l
+232.808 270.252 l
+h
+239.51 266.459 m
+240.113 269.296 l
+240.76 269.296 l
+239.775 265.313 l
+239.261 265.313 l
+238.482 268.165 l
+237.732 265.313 l
+237.203 265.313 l
+236.247 269.296 l
+236.879 269.296 l
+237.497 266.533 l
+238.232 269.296 l
+238.746 269.296 l
+h
+242.142 265.313 -0.646 3.983 re
+242.186 270.34 m
+242.186 270.229 242.157 270.138 242.097 270.061 c
+242.039 269.991 241.943 269.958 241.818 269.958 c
+241.7 269.958 241.605 269.991 241.539 270.061 c
+241.48 270.138 241.451 270.229 241.451 270.34 c
+241.451 270.457 241.48 270.549 241.539 270.619 c
+241.605 270.696 241.7 270.737 241.818 270.737 c
+241.943 270.737 242.039 270.696 242.097 270.619 c
+242.157 270.538 242.186 270.447 242.186 270.34 c
+243.861 265.313 -0.646 5.644 re
+245.581 265.313 -0.646 5.644 re
+249.697 265.798 m
+249.91 265.798 250.083 265.861 250.211 265.989 c
+250.348 266.125 250.421 266.316 250.432 266.562 c
+251.049 266.562 l
+251.027 266.181 250.891 265.861 250.637 265.607 c
+250.381 265.361 250.068 265.24 249.697 265.24 c
+249.205 265.24 248.83 265.39 248.565 265.695 c
+248.308 266.007 248.183 266.474 248.183 267.091 c
+248.183 267.532 l
+248.183 268.128 248.308 268.583 248.565 268.899 c
+248.83 269.212 249.205 269.37 249.697 269.37 c
+250.097 269.37 250.417 269.238 250.652 268.973 c
+250.895 268.716 251.027 268.371 251.049 267.929 c
+250.432 267.929 l
+250.41 268.223 250.336 268.444 250.211 268.591 c
+250.094 268.737 249.921 268.811 249.697 268.811 c
+249.403 268.811 249.186 268.712 249.05 268.517 c
+248.91 268.33 248.837 268.022 248.83 267.592 c
+248.83 267.077 l
+248.83 266.607 248.896 266.272 249.035 266.077 c
+249.182 265.89 249.403 265.798 249.697 265.798 c
+253.445 268.679 m
+253.357 268.697 253.258 268.708 253.151 268.708 c
+252.817 268.708 252.582 268.525 252.445 268.165 c
+252.445 265.313 l
+251.799 265.313 l
+251.799 269.296 l
+252.431 269.296 l
+252.445 268.885 l
+252.622 269.208 252.865 269.37 253.18 269.37 c
+253.287 269.37 253.376 269.348 253.445 269.311 c
+h
+255.445 265.24 m
+254.945 265.24 254.562 265.387 254.298 265.68 c
+254.033 265.974 253.901 266.408 253.901 266.989 c
+253.901 267.459 l
+253.901 268.054 254.026 268.521 254.283 268.855 c
+254.548 269.197 254.907 269.37 255.371 269.37 c
+255.83 269.37 256.172 269.215 256.399 268.914 c
+256.635 268.62 256.756 268.157 256.767 267.532 c
+256.767 267.106 l
+254.548 267.106 l
+254.548 267.018 l
+254.548 266.584 254.625 266.272 254.783 266.077 c
+254.948 265.89 255.18 265.798 255.474 265.798 c
+255.668 265.798 255.842 265.832 255.988 265.901 c
+256.135 265.978 256.271 266.096 256.399 266.254 c
+256.738 265.842 l
+256.451 265.438 256.021 265.24 255.445 265.24 c
+255.371 268.811 m
+255.096 268.811 254.893 268.716 254.768 268.532 c
+254.639 268.344 254.566 268.054 254.548 267.665 c
+256.121 267.665 l
+256.121 267.753 l
+256.098 268.135 256.033 268.403 255.915 268.562 c
+255.797 268.727 255.613 268.811 255.371 268.811 c
+259.589 265.313 m
+259.549 265.401 259.524 265.548 259.516 265.753 c
+259.281 265.408 258.986 265.24 258.634 265.24 c
+258.271 265.24 257.987 265.335 257.781 265.533 c
+257.583 265.739 257.488 266.026 257.488 266.401 c
+257.488 266.801 257.623 267.121 257.899 267.356 c
+258.171 267.598 258.546 267.723 259.017 267.723 c
+259.501 267.723 l
+259.501 268.149 l
+259.501 268.385 259.446 268.55 259.339 268.65 c
+259.229 268.756 259.067 268.811 258.855 268.811 c
+258.656 268.811 258.494 268.753 258.369 268.635 c
+258.252 268.517 258.193 268.371 258.193 268.194 c
+257.546 268.194 l
+257.546 268.388 257.605 268.579 257.723 268.768 c
+257.847 268.951 258.009 269.098 258.208 269.208 c
+258.414 269.315 258.641 269.37 258.899 269.37 c
+259.299 269.37 259.604 269.267 259.81 269.061 c
+260.023 268.855 260.137 268.562 260.148 268.18 c
+260.148 266.165 l
+260.148 265.861 260.185 265.596 260.266 265.371 c
+260.266 265.313 l
+h
+258.722 265.828 m
+258.888 265.828 259.038 265.871 259.178 265.959 c
+259.325 266.048 259.431 266.158 259.501 266.298 c
+259.501 267.239 l
+259.134 267.239 l
+258.818 267.239 258.575 267.168 258.398 267.033 c
+258.223 266.904 258.134 266.717 258.134 266.474 c
+258.134 266.246 258.178 266.081 258.267 265.974 c
+258.354 265.875 258.506 265.828 258.722 265.828 c
+262.001 270.252 m
+262.001 269.296 l
+262.603 269.296 l
+262.603 268.768 l
+262.001 268.768 l
+262.001 266.298 l
+262.001 266.14 262.022 266.022 262.074 265.945 c
+262.132 265.864 262.221 265.828 262.338 265.828 c
+262.427 265.828 262.514 265.842 262.603 265.871 c
+262.603 265.313 l
+262.456 265.265 262.302 265.24 262.147 265.24 c
+261.89 265.24 261.695 265.331 261.559 265.518 c
+261.419 265.703 261.353 265.963 261.353 266.298 c
+261.353 268.768 l
+260.75 268.768 l
+260.75 269.296 l
+261.353 269.296 l
+261.353 270.252 l
+h
+264.793 265.24 m
+264.293 265.24 263.911 265.387 263.647 265.68 c
+263.382 265.974 263.25 266.408 263.25 266.989 c
+263.25 267.459 l
+263.25 268.054 263.374 268.521 263.632 268.855 c
+263.896 269.197 264.256 269.37 264.719 269.37 c
+265.178 269.37 265.521 269.215 265.748 268.914 c
+265.984 268.62 266.105 268.157 266.115 267.532 c
+266.115 267.106 l
+263.896 267.106 l
+263.896 267.018 l
+263.896 266.584 263.973 266.272 264.131 266.077 c
+264.297 265.89 264.528 265.798 264.823 265.798 c
+265.018 265.798 265.19 265.832 265.336 265.901 c
+265.484 265.978 265.62 266.096 265.748 266.254 c
+266.086 265.842 l
+265.8 265.438 265.37 265.24 264.793 265.24 c
+264.719 268.811 m
+264.444 268.811 264.241 268.716 264.117 268.532 c
+263.988 268.344 263.915 268.054 263.896 267.665 c
+265.469 267.665 l
+265.469 267.753 l
+265.447 268.135 265.381 268.403 265.263 268.562 c
+265.145 268.727 264.962 268.811 264.719 268.811 c
+f
+q 1 0 0 1 41.8817 255.9058 cm
+0 0 m
+-0.04 0.087 -0.066 0.235 -0.073 0.44 c
+-0.309 0.095 -0.603 -0.073 -0.955 -0.073 c
+-1.319 -0.073 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.102 0.713 -2.102 1.087 c
+-2.102 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.285 -1.043 2.41 -0.573 2.41 c
+-0.088 2.41 l
+-0.088 2.836 l
+-0.088 3.072 -0.143 3.237 -0.249 3.337 c
+-0.36 3.443 -0.522 3.498 -0.735 3.498 c
+-0.933 3.498 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.984 3.266 -1.866 3.454 c
+-1.741 3.638 -1.58 3.785 -1.382 3.895 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.29 4.056 0.015 3.954 0.221 3.748 c
+0.434 3.542 0.548 3.248 0.559 2.866 c
+0.559 0.852 l
+0.559 0.548 0.595 0.283 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.515 m
+-0.702 0.515 -0.551 0.558 -0.411 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.985 c
+-0.088 1.926 l
+-0.455 1.926 l
+-0.771 1.926 -1.014 1.855 -1.19 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.234 0.562 -1.084 0.515 -0.867 0.515 c
+3.896 3.983 m
+3.91 3.542 l
+4.164 3.884 4.487 4.056 4.881 4.056 c
+5.586 4.056 5.943 3.586 5.953 2.645 c
+5.953 0 l
+5.307 0 l
+5.307 2.616 l
+5.307 2.929 5.251 3.149 5.145 3.278 c
+5.035 3.403 4.881 3.469 4.675 3.469 c
+4.516 3.469 4.37 3.414 4.233 3.307 c
+4.104 3.197 4.002 3.061 3.925 2.896 c
+3.925 0 l
+3.278 0 l
+3.278 3.983 l
+h
+8.349 -0.073 m
+7.85 -0.073 7.468 0.073 7.202 0.367 c
+6.938 0.661 6.806 1.095 6.806 1.675 c
+6.806 2.146 l
+6.806 2.741 6.93 3.208 7.188 3.542 c
+7.453 3.884 7.813 4.056 8.276 4.056 c
+8.735 4.056 9.077 3.902 9.305 3.601 c
+9.54 3.307 9.661 2.844 9.672 2.219 c
+9.672 1.793 l
+7.453 1.793 l
+7.453 1.705 l
+7.453 1.271 7.53 0.959 7.688 0.764 c
+7.853 0.577 8.085 0.484 8.378 0.484 c
+8.573 0.484 8.746 0.518 8.893 0.588 c
+9.04 0.665 9.176 0.783 9.305 0.941 c
+9.643 0.529 l
+9.356 0.125 8.926 -0.073 8.349 -0.073 c
+8.276 3.498 m
+8 3.498 7.798 3.403 7.673 3.219 c
+7.545 3.031 7.471 2.741 7.453 2.352 c
+9.025 2.352 l
+9.025 2.439 l
+9.003 2.822 8.937 3.09 8.819 3.248 c
+8.702 3.414 8.518 3.498 8.276 3.498 c
+13.421 1.146 m
+14.023 3.983 l
+14.67 3.983 l
+13.685 0 l
+13.17 0 l
+12.391 2.851 l
+11.642 0 l
+11.113 0 l
+10.157 3.983 l
+10.789 3.983 l
+11.406 1.22 l
+12.141 3.983 l
+12.656 3.983 l
+h
+16.933 2.175 m
+16.933 2.782 17.043 3.248 17.271 3.572 c
+17.506 3.895 17.834 4.056 18.256 4.056 c
+18.639 4.056 18.936 3.898 19.153 3.586 c
+19.153 5.644 l
+19.8 5.644 l
+19.8 0 l
+19.212 0 l
+19.168 0.426 l
+18.962 0.091 18.657 -0.073 18.256 -0.073 c
+17.845 -0.073 17.521 0.081 17.286 0.397 c
+17.051 0.72 16.933 1.176 16.933 1.764 c
+h
+17.581 1.793 m
+17.581 1.352 17.643 1.022 17.771 0.808 c
+17.907 0.602 18.128 0.5 18.433 0.5 c
+18.756 0.5 18.995 0.661 19.153 0.985 c
+19.153 2.998 l
+18.984 3.31 18.745 3.469 18.433 3.469 c
+18.128 3.469 17.907 3.366 17.771 3.16 c
+17.643 2.954 17.581 2.631 17.581 2.19 c
+h
+21.504 0 -0.646 3.983 re
+21.549 5.026 m
+21.549 4.916 21.519 4.825 21.461 4.748 c
+21.402 4.677 21.307 4.644 21.182 4.644 c
+21.064 4.644 20.968 4.677 20.902 4.748 c
+20.843 4.825 20.814 4.916 20.814 5.026 c
+20.814 5.144 20.843 5.236 20.902 5.306 c
+20.968 5.383 21.064 5.423 21.182 5.423 c
+21.307 5.423 21.402 5.383 21.461 5.306 c
+21.519 5.225 21.549 5.134 21.549 5.026 c
+24.166 3.366 m
+24.077 3.384 23.978 3.395 23.871 3.395 c
+23.537 3.395 23.302 3.212 23.166 2.851 c
+23.166 0 l
+22.519 0 l
+22.519 3.983 l
+23.151 3.983 l
+23.166 3.572 l
+23.343 3.895 23.584 4.056 23.9 4.056 c
+24.008 4.056 24.095 4.035 24.166 3.998 c
+h
+26.165 -0.073 m
+25.664 -0.073 25.282 0.073 25.018 0.367 c
+24.754 0.661 24.621 1.095 24.621 1.675 c
+24.621 2.146 l
+24.621 2.741 24.746 3.208 25.003 3.542 c
+25.267 3.884 25.628 4.056 26.091 4.056 c
+26.55 4.056 26.892 3.902 27.12 3.601 c
+27.355 3.307 27.476 2.844 27.487 2.219 c
+27.487 1.793 l
+25.267 1.793 l
+25.267 1.705 l
+25.267 1.271 25.345 0.959 25.503 0.764 c
+25.668 0.577 25.899 0.484 26.194 0.484 c
+26.389 0.484 26.562 0.518 26.709 0.588 c
+26.855 0.665 26.992 0.783 27.12 0.941 c
+27.458 0.529 l
+27.171 0.125 26.741 -0.073 26.165 -0.073 c
+26.091 3.498 m
+25.816 3.498 25.614 3.403 25.488 3.219 c
+25.36 3.031 25.286 2.741 25.267 2.352 c
+26.84 2.352 l
+26.84 2.439 l
+26.819 2.822 26.753 3.09 26.635 3.248 c
+26.518 3.414 26.333 3.498 26.091 3.498 c
+29.648 0.484 m
+29.861 0.484 30.034 0.548 30.163 0.675 c
+30.298 0.812 30.372 1.003 30.383 1.249 c
+31 1.249 l
+30.979 0.867 30.842 0.548 30.589 0.294 c
+30.331 0.048 30.019 -0.073 29.648 -0.073 c
+29.155 -0.073 28.781 0.077 28.517 0.382 c
+28.259 0.694 28.134 1.161 28.134 1.778 c
+28.134 2.219 l
+28.134 2.815 28.259 3.27 28.517 3.586 c
+28.781 3.898 29.155 4.056 29.648 4.056 c
+30.049 4.056 30.368 3.925 30.603 3.659 c
+30.846 3.403 30.979 3.057 31 2.616 c
+30.383 2.616 l
+30.361 2.91 30.287 3.131 30.163 3.278 c
+30.045 3.424 29.872 3.498 29.648 3.498 c
+29.354 3.498 29.138 3.399 29.001 3.204 c
+28.862 3.017 28.788 2.708 28.781 2.278 c
+28.781 1.764 l
+28.781 1.294 28.847 0.959 28.987 0.764 c
+29.134 0.577 29.354 0.484 29.648 0.484 c
+32.602 4.939 m
+32.602 3.983 l
+33.205 3.983 l
+33.205 3.454 l
+32.602 3.454 l
+32.602 0.985 l
+32.602 0.827 32.625 0.709 32.677 0.632 c
+32.735 0.551 32.823 0.515 32.941 0.515 c
+33.028 0.515 33.117 0.529 33.205 0.558 c
+33.205 0 l
+33.059 -0.048 32.904 -0.073 32.75 -0.073 c
+32.492 -0.073 32.297 0.018 32.162 0.205 c
+32.022 0.389 31.956 0.65 31.956 0.985 c
+31.956 3.454 l
+31.353 3.454 l
+31.353 3.983 l
+31.956 3.983 l
+31.956 4.939 l
+h
+33.764 2.175 m
+33.764 2.753 33.9 3.208 34.175 3.542 c
+34.458 3.884 34.83 4.056 35.293 4.056 c
+35.752 4.056 36.12 3.888 36.395 3.557 c
+36.678 3.233 36.825 2.786 36.836 2.219 c
+36.836 1.793 l
+36.836 1.224 36.693 0.768 36.409 0.426 c
+36.134 0.091 35.767 -0.073 35.307 -0.073 c
+34.844 -0.073 34.473 0.087 34.19 0.411 c
+33.915 0.742 33.772 1.183 33.764 1.734 c
+h
+34.41 1.793 m
+34.41 1.389 34.488 1.072 34.646 0.837 c
+34.811 0.602 35.031 0.484 35.307 0.484 c
+35.873 0.484 36.167 0.897 36.189 1.72 c
+36.189 2.175 l
+36.189 2.576 36.105 2.896 35.939 3.131 c
+35.781 3.374 35.565 3.498 35.293 3.498 c
+35.028 3.498 34.811 3.374 34.646 3.131 c
+34.488 2.896 34.41 2.576 34.41 2.175 c
+h
+39.32 3.366 m
+39.232 3.384 39.133 3.395 39.027 3.395 c
+38.692 3.395 38.456 3.212 38.321 2.851 c
+38.321 0 l
+37.674 0 l
+37.674 3.983 l
+38.306 3.983 l
+38.321 3.572 l
+38.497 3.895 38.74 4.056 39.056 4.056 c
+39.162 4.056 39.25 4.035 39.32 3.998 c
+h
+41.128 1.087 m
+41.849 3.983 l
+42.539 3.983 l
+41.246 -0.559 l
+41.146 -0.9 41.003 -1.162 40.82 -1.338 c
+40.643 -1.515 40.441 -1.602 40.217 -1.602 c
+40.128 -1.602 40.014 -1.58 39.879 -1.544 c
+39.879 -1 l
+40.026 -1.014 l
+40.209 -1.014 40.357 -0.971 40.467 -0.882 c
+40.573 -0.794 40.662 -0.636 40.731 -0.412 c
+40.849 0.029 l
+39.688 3.983 l
+40.393 3.983 l
+h
+45.508 3.983 m
+45.523 3.542 l
+45.776 3.884 46.1 4.056 46.493 4.056 c
+47.199 4.056 47.555 3.586 47.567 2.645 c
+47.567 0 l
+46.919 0 l
+46.919 2.616 l
+46.919 2.929 46.865 3.149 46.758 3.278 c
+46.648 3.403 46.493 3.469 46.287 3.469 c
+46.129 3.469 45.982 3.414 45.847 3.307 c
+45.718 3.197 45.615 3.061 45.537 2.896 c
+45.537 0 l
+44.891 0 l
+44.891 3.983 l
+h
+50.58 0 m
+50.539 0.087 50.513 0.235 50.506 0.44 c
+50.271 0.095 49.977 -0.073 49.624 -0.073 c
+49.26 -0.073 48.978 0.022 48.772 0.22 c
+48.573 0.426 48.477 0.713 48.477 1.087 c
+48.477 1.488 48.614 1.808 48.889 2.043 c
+49.161 2.285 49.536 2.41 50.006 2.41 c
+50.491 2.41 l
+50.491 2.836 l
+50.491 3.072 50.436 3.237 50.33 3.337 c
+50.219 3.443 50.058 3.498 49.845 3.498 c
+49.647 3.498 49.485 3.439 49.36 3.322 c
+49.242 3.204 49.183 3.057 49.183 2.881 c
+48.536 2.881 l
+48.536 3.075 48.595 3.266 48.712 3.454 c
+48.838 3.638 48.999 3.785 49.198 3.895 c
+49.404 4.002 49.632 4.056 49.888 4.056 c
+50.289 4.056 50.594 3.954 50.8 3.748 c
+51.013 3.542 51.127 3.248 51.138 2.866 c
+51.138 0.852 l
+51.138 0.548 51.175 0.283 51.256 0.058 c
+51.256 0 l
+h
+49.712 0.515 m
+49.878 0.515 50.029 0.558 50.168 0.646 c
+50.315 0.735 50.422 0.845 50.491 0.985 c
+50.491 1.926 l
+50.124 1.926 l
+49.807 1.926 49.566 1.855 49.389 1.72 c
+49.213 1.591 49.124 1.404 49.124 1.161 c
+49.124 0.933 49.169 0.768 49.256 0.661 c
+49.345 0.562 49.495 0.515 49.712 0.515 c
+52.755 3.983 m
+52.77 3.615 l
+53.013 3.91 53.332 4.056 53.725 4.056 c
+54.166 4.056 54.474 3.858 54.651 3.469 c
+54.904 3.858 55.254 4.056 55.695 4.056 c
+56.43 4.056 56.805 3.594 56.826 2.675 c
+56.826 0 l
+56.18 0 l
+56.18 2.616 l
+56.18 2.91 56.125 3.123 56.018 3.262 c
+55.919 3.399 55.746 3.469 55.504 3.469 c
+55.305 3.469 55.143 3.388 55.019 3.233 c
+54.901 3.087 54.831 2.896 54.813 2.66 c
+54.813 0 l
+54.152 0 l
+54.152 2.645 l
+54.152 3.193 53.931 3.469 53.49 3.469 c
+53.156 3.469 52.92 3.307 52.784 2.984 c
+52.784 0 l
+52.138 0 l
+52.138 3.983 l
+h
+59.222 -0.073 m
+58.723 -0.073 58.341 0.073 58.077 0.367 c
+57.811 0.661 57.68 1.095 57.68 1.675 c
+57.68 2.146 l
+57.68 2.741 57.804 3.208 58.061 3.542 c
+58.326 3.884 58.686 4.056 59.149 4.056 c
+59.608 4.056 59.951 3.902 60.178 3.601 c
+60.413 3.307 60.535 2.844 60.545 2.219 c
+60.545 1.793 l
+58.326 1.793 l
+58.326 1.705 l
+58.326 1.271 58.403 0.959 58.561 0.764 c
+58.727 0.577 58.958 0.484 59.252 0.484 c
+59.447 0.484 59.619 0.518 59.766 0.588 c
+59.914 0.665 60.049 0.783 60.178 0.941 c
+60.516 0.529 l
+60.23 0.125 59.8 -0.073 59.222 -0.073 c
+59.149 3.498 m
+58.873 3.498 58.671 3.403 58.547 3.219 c
+58.418 3.031 58.345 2.741 58.326 2.352 c
+59.899 2.352 l
+59.899 2.439 l
+59.877 2.822 59.81 3.09 59.693 3.248 c
+59.575 3.414 59.392 3.498 59.149 3.498 c
+61.192 2.175 m
+61.192 2.782 61.302 3.248 61.53 3.572 c
+61.765 3.895 62.093 4.056 62.515 4.056 c
+62.897 4.056 63.195 3.898 63.411 3.586 c
+63.411 5.644 l
+64.059 5.644 l
+64.059 0 l
+63.471 0 l
+63.427 0.426 l
+63.221 0.091 62.916 -0.073 62.515 -0.073 c
+62.104 -0.073 61.78 0.081 61.545 0.397 c
+61.31 0.72 61.192 1.176 61.192 1.764 c
+h
+61.84 1.793 m
+61.84 1.352 61.902 1.022 62.03 0.808 c
+62.166 0.602 62.387 0.5 62.692 0.5 c
+63.015 0.5 63.253 0.661 63.411 0.985 c
+63.411 2.998 l
+63.243 3.31 63.004 3.469 62.692 3.469 c
+62.387 3.469 62.166 3.366 62.03 3.16 c
+61.902 2.954 61.84 2.631 61.84 2.19 c
+h
+f
+Q
+q 1 0 0 1 110.0119 261.315 cm
+0 0 m
+-0.382 0 l
+-0.382 -5.835 l
+0 -5.835 l
+0 -6.659 l
+-1.426 -6.659 l
+-1.426 0.823 l
+0 0.823 l
+h
+3.63 -3.543 m
+3.63 -4.171 3.52 -4.653 3.308 -4.983 c
+3.09 -5.318 2.77 -5.483 2.352 -5.483 c
+2.028 -5.483 1.768 -5.351 1.573 -5.087 c
+1.573 -6.938 l
+0.53 -6.938 l
+0.53 -1.426 l
+1.484 -1.426 l
+1.529 -1.794 l
+1.723 -1.5 1.992 -1.353 2.337 -1.353 c
+2.756 -1.353 3.076 -1.511 3.293 -1.823 c
+3.506 -2.128 3.62 -2.598 3.63 -3.234 c
+h
+2.587 -3.278 m
+2.587 -2.896 2.543 -2.627 2.454 -2.469 c
+2.373 -2.315 2.234 -2.234 2.028 -2.234 c
+1.822 -2.234 1.668 -2.323 1.573 -2.5 c
+1.573 -4.366 l
+1.661 -4.535 1.816 -4.616 2.043 -4.616 c
+2.249 -4.616 2.389 -4.535 2.469 -4.366 c
+2.547 -4.189 2.587 -3.917 2.587 -3.543 c
+h
+6.217 -2.44 m
+5.88 -2.411 l
+5.593 -2.411 5.402 -2.536 5.307 -2.778 c
+5.307 -5.409 l
+4.262 -5.409 l
+4.262 -1.426 l
+5.233 -1.426 l
+5.263 -1.867 l
+5.428 -1.525 5.66 -1.353 5.953 -1.353 c
+6.071 -1.353 6.163 -1.374 6.232 -1.411 c
+h
+6.512 -3.293 m
+6.512 -2.687 6.651 -2.213 6.938 -1.867 c
+7.221 -1.525 7.614 -1.353 8.114 -1.353 c
+8.621 -1.353 9.018 -1.525 9.305 -1.867 c
+9.587 -2.213 9.731 -2.687 9.731 -3.293 c
+9.731 -3.558 l
+9.731 -4.156 9.587 -4.627 9.305 -4.969 c
+9.018 -5.314 8.621 -5.483 8.114 -5.483 c
+7.603 -5.483 7.206 -5.314 6.923 -4.969 c
+6.647 -4.627 6.512 -4.152 6.512 -3.543 c
+h
+7.555 -3.558 m
+7.555 -4.263 7.739 -4.616 8.114 -4.616 c
+8.467 -4.616 8.658 -4.322 8.687 -3.734 c
+8.687 -3.293 l
+8.687 -2.932 8.636 -2.66 8.54 -2.484 c
+8.441 -2.308 8.297 -2.22 8.114 -2.22 c
+7.937 -2.22 7.798 -2.308 7.702 -2.484 c
+7.603 -2.66 7.555 -2.932 7.555 -3.293 c
+h
+11.465 -1.426 m
+11.465 -5.63 l
+11.465 -6.078 11.362 -6.423 11.156 -6.659 c
+10.951 -6.894 10.657 -7.011 10.275 -7.011 c
+10.105 -7.011 9.951 -6.99 9.804 -6.953 c
+9.789 -6.101 l
+9.907 -6.13 9.999 -6.145 10.069 -6.145 c
+10.304 -6.145 10.422 -5.983 10.422 -5.659 c
+10.422 -1.426 l
+h
+10.363 -0.397 m
+10.363 -0.243 10.41 -0.114 10.51 -0.015 c
+10.616 0.091 10.753 0.147 10.921 0.147 c
+11.098 0.147 11.233 0.091 11.333 -0.015 c
+11.439 -0.114 11.495 -0.243 11.495 -0.397 c
+11.495 -0.566 11.439 -0.702 11.333 -0.809 c
+11.233 -0.908 11.098 -0.956 10.921 -0.956 c
+10.745 -0.956 10.605 -0.908 10.51 -0.809 c
+10.41 -0.702 10.363 -0.566 10.363 -0.397 c
+13.832 -5.483 m
+13.303 -5.483 12.883 -5.328 12.582 -5.012 c
+12.289 -4.69 12.141 -4.23 12.141 -3.631 c
+12.141 -3.323 l
+12.141 -2.697 12.278 -2.213 12.553 -1.867 c
+12.825 -1.525 13.218 -1.353 13.729 -1.353 c
+14.229 -1.353 14.6 -1.515 14.846 -1.837 c
+15.1 -2.161 15.232 -2.639 15.243 -3.263 c
+15.243 -3.763 l
+13.17 -3.763 l
+13.189 -4.057 13.251 -4.274 13.361 -4.41 c
+13.479 -4.549 13.66 -4.616 13.905 -4.616 c
+14.247 -4.616 14.537 -4.499 14.772 -4.263 c
+15.184 -4.895 l
+15.056 -5.072 14.868 -5.215 14.626 -5.322 c
+14.379 -5.428 14.115 -5.483 13.832 -5.483 c
+13.185 -3.043 m
+14.214 -3.043 l
+14.214 -2.94 l
+14.214 -2.705 14.173 -2.529 14.096 -2.411 c
+14.026 -2.286 13.898 -2.22 13.714 -2.22 c
+13.538 -2.22 13.406 -2.29 13.317 -2.425 c
+13.236 -2.554 13.192 -2.76 13.185 -3.043 c
+17.271 -4.616 m
+17.565 -4.616 17.716 -4.421 17.727 -4.028 c
+18.697 -4.028 l
+18.697 -4.461 18.565 -4.814 18.3 -5.087 c
+18.036 -5.351 17.698 -5.483 17.286 -5.483 c
+16.775 -5.483 16.382 -5.328 16.11 -5.012 c
+15.846 -4.69 15.706 -4.219 15.698 -3.601 c
+15.698 -3.278 l
+15.698 -2.654 15.831 -2.176 16.095 -1.852 c
+16.367 -1.521 16.764 -1.353 17.286 -1.353 c
+17.716 -1.353 18.057 -1.492 18.315 -1.764 c
+18.568 -2.04 18.697 -2.421 18.697 -2.911 c
+17.727 -2.911 l
+17.727 -2.697 17.687 -2.529 17.61 -2.411 c
+17.54 -2.286 17.423 -2.22 17.257 -2.22 c
+17.08 -2.22 16.952 -2.286 16.874 -2.411 c
+16.794 -2.539 16.75 -2.789 16.742 -3.161 c
+16.742 -3.572 l
+16.742 -3.896 16.757 -4.123 16.787 -4.248 c
+16.823 -4.377 16.878 -4.469 16.948 -4.528 c
+17.026 -4.586 17.132 -4.616 17.271 -4.616 c
+20.505 -0.456 m
+20.505 -1.426 l
+21.034 -1.426 l
+21.034 -2.22 l
+20.505 -2.22 l
+20.505 -4.189 l
+20.505 -4.347 20.523 -4.454 20.564 -4.513 c
+20.612 -4.572 20.696 -4.601 20.814 -4.601 c
+20.92 -4.601 21.005 -4.594 21.064 -4.572 c
+21.064 -5.38 l
+20.887 -5.446 20.696 -5.483 20.49 -5.483 c
+19.815 -5.483 19.469 -5.097 19.461 -4.322 c
+19.461 -2.22 l
+19.006 -2.22 l
+19.006 -1.426 l
+19.461 -1.426 l
+19.461 -0.456 l
+h
+24.297 -1.426 m
+24.327 -1.823 l
+24.562 -1.511 24.864 -1.353 25.238 -1.353 c
+25.922 -1.353 26.275 -1.833 26.296 -2.793 c
+26.296 -5.409 l
+25.253 -5.409 l
+25.253 -2.866 l
+25.253 -2.643 25.217 -2.481 25.15 -2.382 c
+25.08 -2.286 24.963 -2.234 24.797 -2.234 c
+24.61 -2.234 24.463 -2.33 24.357 -2.514 c
+24.357 -5.409 l
+23.312 -5.409 l
+23.312 -1.426 l
+h
+28.928 -5.409 m
+28.899 -5.351 28.868 -5.247 28.839 -5.101 c
+28.652 -5.358 28.402 -5.483 28.09 -5.483 c
+27.756 -5.483 27.476 -5.376 27.252 -5.16 c
+27.035 -4.935 26.929 -4.645 26.929 -4.293 c
+26.929 -3.881 27.061 -3.564 27.326 -3.337 c
+27.59 -3.102 27.972 -2.984 28.472 -2.984 c
+28.795 -2.984 l
+28.795 -2.66 l
+28.795 -2.484 28.758 -2.363 28.693 -2.294 c
+28.633 -2.216 28.546 -2.176 28.428 -2.176 c
+28.17 -2.176 28.046 -2.33 28.046 -2.631 c
+27.002 -2.631 l
+27.002 -2.261 27.138 -1.955 27.414 -1.72 c
+27.686 -1.478 28.035 -1.353 28.457 -1.353 c
+28.899 -1.353 29.236 -1.47 29.471 -1.706 c
+29.714 -1.933 29.839 -2.257 29.839 -2.675 c
+29.839 -4.542 l
+29.839 -4.888 29.887 -5.156 29.986 -5.351 c
+29.986 -5.409 l
+h
+28.325 -4.659 m
+28.432 -4.659 28.523 -4.642 28.604 -4.601 c
+28.693 -4.553 28.755 -4.495 28.795 -4.424 c
+28.795 -3.601 l
+28.546 -3.601 l
+28.369 -3.601 28.226 -3.66 28.12 -3.778 c
+28.02 -3.888 27.972 -4.035 27.972 -4.219 c
+27.972 -4.513 28.09 -4.659 28.325 -4.659 c
+31.559 -1.426 m
+31.588 -1.794 l
+31.823 -1.5 32.132 -1.353 32.515 -1.353 c
+32.915 -1.353 33.194 -1.536 33.352 -1.897 c
+33.587 -1.536 33.915 -1.353 34.337 -1.353 c
+35.031 -1.353 35.384 -1.837 35.395 -2.808 c
+35.395 -5.409 l
+34.366 -5.409 l
+34.366 -2.866 l
+34.366 -2.643 34.329 -2.481 34.264 -2.382 c
+34.204 -2.286 34.094 -2.234 33.94 -2.234 c
+33.742 -2.234 33.602 -2.352 33.514 -2.587 c
+33.514 -5.409 l
+32.471 -5.409 l
+32.471 -2.881 l
+32.471 -2.646 32.44 -2.481 32.382 -2.382 c
+32.323 -2.286 32.213 -2.234 32.059 -2.234 c
+31.883 -2.234 31.739 -2.33 31.632 -2.514 c
+31.632 -5.409 l
+30.589 -5.409 l
+30.589 -1.426 l
+h
+37.776 -5.483 m
+37.248 -5.483 36.828 -5.328 36.527 -5.012 c
+36.234 -4.69 36.086 -4.23 36.086 -3.631 c
+36.086 -3.323 l
+36.086 -2.697 36.222 -2.213 36.498 -1.867 c
+36.77 -1.525 37.163 -1.353 37.674 -1.353 c
+38.173 -1.353 38.545 -1.515 38.79 -1.837 c
+39.044 -2.161 39.177 -2.639 39.187 -3.263 c
+39.187 -3.763 l
+37.115 -3.763 l
+37.134 -4.057 37.196 -4.274 37.306 -4.41 c
+37.424 -4.549 37.604 -4.616 37.85 -4.616 c
+38.192 -4.616 38.482 -4.499 38.717 -4.263 c
+39.129 -4.895 l
+39 -5.072 38.813 -5.215 38.57 -5.322 c
+38.324 -5.428 38.059 -5.483 37.776 -5.483 c
+37.13 -3.043 m
+38.159 -3.043 l
+38.159 -2.94 l
+38.159 -2.705 38.119 -2.529 38.041 -2.411 c
+37.971 -2.286 37.843 -2.22 37.659 -2.22 c
+37.483 -2.22 37.35 -2.29 37.262 -2.425 c
+37.181 -2.554 37.138 -2.76 37.13 -3.043 c
+39.526 0.823 m
+40.951 0.823 l
+40.951 -6.659 l
+39.526 -6.659 l
+39.526 -5.835 l
+39.908 -5.835 l
+39.908 0 l
+39.526 0 l
+h
+f
+Q
+q 1 0 0 1 155.6378 255.9058 cm
+0 0 m
+-0.04 0.087 -0.066 0.235 -0.073 0.44 c
+-0.309 0.095 -0.603 -0.073 -0.956 -0.073 c
+-1.319 -0.073 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.102 0.713 -2.102 1.087 c
+-2.102 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.285 -1.043 2.41 -0.573 2.41 c
+-0.088 2.41 l
+-0.088 2.836 l
+-0.088 3.072 -0.143 3.237 -0.25 3.337 c
+-0.36 3.443 -0.522 3.498 -0.735 3.498 c
+-0.933 3.498 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.984 3.266 -1.866 3.454 c
+-1.742 3.638 -1.58 3.785 -1.382 3.895 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.291 4.056 0.015 3.954 0.22 3.748 c
+0.434 3.542 0.548 3.248 0.559 2.866 c
+0.559 0.852 l
+0.559 0.548 0.595 0.283 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.515 m
+-0.702 0.515 -0.551 0.558 -0.411 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.985 c
+-0.088 1.926 l
+-0.455 1.926 l
+-0.771 1.926 -1.014 1.855 -1.191 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.235 0.562 -1.084 0.515 -0.867 0.515 c
+2.176 3.983 m
+2.19 3.542 l
+2.444 3.884 2.767 4.056 3.16 4.056 c
+3.865 4.056 4.222 3.586 4.233 2.645 c
+4.233 0 l
+3.587 0 l
+3.587 2.616 l
+3.587 2.929 3.532 3.149 3.425 3.278 c
+3.314 3.403 3.16 3.469 2.955 3.469 c
+2.797 3.469 2.649 3.414 2.514 3.307 c
+2.385 3.197 2.282 3.061 2.205 2.896 c
+2.205 0 l
+1.558 0 l
+1.558 3.983 l
+h
+5.072 2.175 m
+5.072 2.782 5.182 3.248 5.409 3.572 c
+5.644 3.895 5.972 4.056 6.394 4.056 c
+6.776 4.056 7.074 3.898 7.291 3.586 c
+7.291 5.644 l
+7.937 5.644 l
+7.937 0 l
+7.35 0 l
+7.306 0.426 l
+7.1 0.091 6.795 -0.073 6.394 -0.073 c
+5.982 -0.073 5.659 0.081 5.424 0.397 c
+5.189 0.72 5.072 1.176 5.072 1.764 c
+h
+5.718 1.793 m
+5.718 1.352 5.781 1.022 5.909 0.808 c
+6.045 0.602 6.265 0.5 6.57 0.5 c
+6.894 0.5 7.133 0.661 7.291 0.985 c
+7.291 2.998 l
+7.121 3.31 6.882 3.469 6.57 3.469 c
+6.265 3.469 6.045 3.366 5.909 3.16 c
+5.781 2.954 5.718 2.631 5.718 2.19 c
+h
+13.626 1.146 m
+14.229 3.983 l
+14.876 3.983 l
+13.891 0 l
+13.376 0 l
+12.597 2.851 l
+11.848 0 l
+11.318 0 l
+10.363 3.983 l
+10.994 3.983 l
+11.612 1.22 l
+12.347 3.983 l
+12.862 3.983 l
+h
+16.257 0 -0.646 3.983 re
+16.301 5.026 m
+16.301 4.916 16.272 4.825 16.213 4.748 c
+16.154 4.677 16.058 4.644 15.933 4.644 c
+15.817 4.644 15.721 4.677 15.655 4.748 c
+15.596 4.825 15.566 4.916 15.566 5.026 c
+15.566 5.144 15.596 5.236 15.655 5.306 c
+15.721 5.383 15.817 5.423 15.933 5.423 c
+16.058 5.423 16.154 5.383 16.213 5.306 c
+16.272 5.225 16.301 5.134 16.301 5.026 c
+17.977 0 -0.646 5.644 re
+19.696 0 -0.646 5.644 re
+23.137 0 -0.647 3.983 re
+23.181 5.026 m
+23.181 4.916 23.151 4.825 23.092 4.748 c
+23.033 4.677 22.938 4.644 22.813 4.644 c
+22.695 4.644 22.599 4.677 22.534 4.748 c
+22.475 4.825 22.445 4.916 22.445 5.026 c
+22.445 5.144 22.475 5.236 22.534 5.306 c
+22.599 5.383 22.695 5.423 22.813 5.423 c
+22.938 5.423 23.033 5.383 23.092 5.306 c
+23.151 5.225 23.181 5.134 23.181 5.026 c
+24.768 3.983 m
+24.783 3.542 l
+25.036 3.884 25.359 4.056 25.753 4.056 c
+26.458 4.056 26.815 3.586 26.826 2.645 c
+26.826 0 l
+26.179 0 l
+26.179 2.616 l
+26.179 2.929 26.124 3.149 26.017 3.278 c
+25.907 3.403 25.753 3.469 25.547 3.469 c
+25.389 3.469 25.242 3.414 25.105 3.307 c
+24.978 3.197 24.874 3.061 24.797 2.896 c
+24.797 0 l
+24.151 0 l
+24.151 3.983 l
+h
+28.502 0 -0.647 3.983 re
+28.546 5.026 m
+28.546 4.916 28.517 4.825 28.457 4.748 c
+28.398 4.677 28.303 4.644 28.178 4.644 c
+28.06 4.644 27.965 4.677 27.899 4.748 c
+27.84 4.825 27.811 4.916 27.811 5.026 c
+27.811 5.144 27.84 5.236 27.899 5.306 c
+27.965 5.383 28.06 5.423 28.178 5.423 c
+28.303 5.423 28.398 5.383 28.457 5.306 c
+28.517 5.225 28.546 5.134 28.546 5.026 c
+30.368 4.939 m
+30.368 3.983 l
+30.971 3.983 l
+30.971 3.454 l
+30.368 3.454 l
+30.368 0.985 l
+30.368 0.827 30.391 0.709 30.441 0.632 c
+30.501 0.551 30.589 0.515 30.707 0.515 c
+30.794 0.515 30.883 0.529 30.971 0.558 c
+30.971 0 l
+30.824 -0.048 30.67 -0.073 30.515 -0.073 c
+30.258 -0.073 30.063 0.018 29.928 0.205 c
+29.788 0.389 29.722 0.65 29.722 0.985 c
+29.722 3.454 l
+29.119 3.454 l
+29.119 3.983 l
+29.722 3.983 l
+29.722 4.939 l
+h
+32.44 0 -0.646 3.983 re
+32.485 5.026 m
+32.485 4.916 32.456 4.825 32.396 4.748 c
+32.338 4.677 32.242 4.644 32.118 4.644 c
+32 4.644 31.904 4.677 31.838 4.748 c
+31.779 4.825 31.75 4.916 31.75 5.026 c
+31.75 5.144 31.779 5.236 31.838 5.306 c
+31.904 5.383 32 5.423 32.118 5.423 c
+32.242 5.423 32.338 5.383 32.396 5.306 c
+32.456 5.225 32.485 5.134 32.485 5.026 c
+35.498 0 m
+35.457 0.087 35.432 0.235 35.424 0.44 c
+35.189 0.095 34.896 -0.073 34.543 -0.073 c
+34.179 -0.073 33.896 0.022 33.691 0.22 c
+33.492 0.426 33.396 0.713 33.396 1.087 c
+33.396 1.488 33.533 1.808 33.807 2.043 c
+34.08 2.285 34.455 2.41 34.925 2.41 c
+35.41 2.41 l
+35.41 2.836 l
+35.41 3.072 35.355 3.237 35.249 3.337 c
+35.138 3.443 34.977 3.498 34.763 3.498 c
+34.565 3.498 34.403 3.439 34.278 3.322 c
+34.161 3.204 34.102 3.057 34.102 2.881 c
+33.456 2.881 l
+33.456 3.075 33.514 3.266 33.631 3.454 c
+33.757 3.638 33.918 3.785 34.117 3.895 c
+34.322 4.002 34.551 4.056 34.807 4.056 c
+35.208 4.056 35.513 3.954 35.719 3.748 c
+35.932 3.542 36.045 3.248 36.057 2.866 c
+36.057 0.852 l
+36.057 0.548 36.093 0.283 36.174 0.058 c
+36.174 0 l
+h
+34.631 0.515 m
+34.796 0.515 34.947 0.558 35.087 0.646 c
+35.234 0.735 35.34 0.845 35.41 0.985 c
+35.41 1.926 l
+35.043 1.926 l
+34.726 1.926 34.484 1.855 34.308 1.72 c
+34.131 1.591 34.043 1.404 34.043 1.161 c
+34.043 0.933 34.087 0.768 34.175 0.661 c
+34.264 0.562 34.414 0.515 34.631 0.515 c
+37.762 0 -0.647 5.644 re
+39.482 0 -0.647 3.983 re
+39.526 5.026 m
+39.526 4.916 39.496 4.825 39.438 4.748 c
+39.378 4.677 39.283 4.644 39.158 4.644 c
+39.041 4.644 38.945 4.677 38.879 4.748 c
+38.821 4.825 38.79 4.916 38.79 5.026 c
+38.79 5.144 38.821 5.236 38.879 5.306 c
+38.945 5.383 39.041 5.423 39.158 5.423 c
+39.283 5.423 39.378 5.383 39.438 5.306 c
+39.496 5.225 39.526 5.134 39.526 5.026 c
+41.098 0.558 m
+43.024 0.558 l
+43.024 0 l
+40.363 0 l
+40.363 0.5 l
+42.187 3.41 l
+40.378 3.41 l
+40.378 3.983 l
+42.95 3.983 l
+42.95 3.498 l
+h
+45.171 -0.073 m
+44.67 -0.073 44.288 0.073 44.024 0.367 c
+43.759 0.661 43.627 1.095 43.627 1.675 c
+43.627 2.146 l
+43.627 2.741 43.752 3.208 44.009 3.542 c
+44.274 3.884 44.633 4.056 45.096 4.056 c
+45.556 4.056 45.898 3.902 46.125 3.601 c
+46.361 3.307 46.482 2.844 46.493 2.219 c
+46.493 1.793 l
+44.274 1.793 l
+44.274 1.705 l
+44.274 1.271 44.351 0.959 44.509 0.764 c
+44.674 0.577 44.905 0.484 45.2 0.484 c
+45.394 0.484 45.567 0.518 45.714 0.588 c
+45.861 0.665 45.997 0.783 46.125 0.941 c
+46.464 0.529 l
+46.177 0.125 45.747 -0.073 45.171 -0.073 c
+45.096 3.498 m
+44.822 3.498 44.619 3.403 44.494 3.219 c
+44.365 3.031 44.292 2.741 44.274 2.352 c
+45.846 2.352 l
+45.846 2.439 l
+45.824 2.822 45.758 3.09 45.641 3.248 c
+45.523 3.414 45.339 3.498 45.096 3.498 c
+51.035 0 m
+50.995 0.087 50.969 0.235 50.962 0.44 c
+50.727 0.095 50.432 -0.073 50.079 -0.073 c
+49.716 -0.073 49.433 0.022 49.227 0.22 c
+49.029 0.426 48.934 0.713 48.934 1.087 c
+48.934 1.488 49.069 1.808 49.345 2.043 c
+49.616 2.285 49.992 2.41 50.462 2.41 c
+50.947 2.41 l
+50.947 2.836 l
+50.947 3.072 50.892 3.237 50.785 3.337 c
+50.675 3.443 50.513 3.498 50.3 3.498 c
+50.102 3.498 49.94 3.439 49.815 3.322 c
+49.697 3.204 49.639 3.057 49.639 2.881 c
+48.992 2.881 l
+48.992 3.075 49.051 3.266 49.169 3.454 c
+49.293 3.638 49.455 3.785 49.653 3.895 c
+49.859 4.002 50.087 4.056 50.345 4.056 c
+50.744 4.056 51.05 3.954 51.255 3.748 c
+51.469 3.542 51.583 3.248 51.594 2.866 c
+51.594 0.852 l
+51.594 0.548 51.631 0.283 51.711 0.058 c
+51.711 0 l
+h
+50.168 0.515 m
+50.333 0.515 50.484 0.558 50.624 0.646 c
+50.771 0.735 50.877 0.845 50.947 0.985 c
+50.947 1.926 l
+50.58 1.926 l
+50.264 1.926 50.021 1.855 49.844 1.72 c
+49.668 1.591 49.58 1.404 49.58 1.161 c
+49.58 0.933 49.624 0.768 49.712 0.661 c
+49.8 0.562 49.951 0.515 50.168 0.515 c
+55.959 3.366 m
+55.871 3.384 55.772 3.395 55.665 3.395 c
+55.331 3.395 55.095 3.212 54.96 2.851 c
+54.96 0 l
+54.313 0 l
+54.313 3.983 l
+54.945 3.983 l
+54.96 3.572 l
+55.136 3.895 55.378 4.056 55.695 4.056 c
+55.801 4.056 55.889 4.035 55.959 3.998 c
+h
+57.958 -0.073 m
+57.458 -0.073 57.077 0.073 56.812 0.367 c
+56.547 0.661 56.415 1.095 56.415 1.675 c
+56.415 2.146 l
+56.415 2.741 56.54 3.208 56.797 3.542 c
+57.061 3.884 57.422 4.056 57.885 4.056 c
+58.345 4.056 58.686 3.902 58.914 3.601 c
+59.149 3.307 59.27 2.844 59.282 2.219 c
+59.282 1.793 l
+57.061 1.793 l
+57.061 1.705 l
+57.061 1.271 57.139 0.959 57.297 0.764 c
+57.462 0.577 57.694 0.484 57.988 0.484 c
+58.183 0.484 58.355 0.518 58.502 0.588 c
+58.649 0.665 58.785 0.783 58.914 0.941 c
+59.252 0.529 l
+58.966 0.125 58.536 -0.073 57.958 -0.073 c
+57.885 3.498 m
+57.609 3.498 57.407 3.403 57.283 3.219 c
+57.154 3.031 57.08 2.741 57.061 2.352 c
+58.634 2.352 l
+58.634 2.439 l
+58.613 2.822 58.546 3.09 58.428 3.248 c
+58.311 3.414 58.127 3.498 57.885 3.498 c
+62.941 1.793 m
+62.941 1.165 62.824 0.694 62.588 0.382 c
+62.361 0.077 62.045 -0.073 61.633 -0.073 c
+61.229 -0.073 60.92 0.077 60.707 0.382 c
+60.707 -1.529 l
+60.061 -1.529 l
+60.061 3.983 l
+60.649 3.983 l
+60.693 3.542 l
+60.905 3.884 61.214 4.056 61.618 4.056 c
+62.06 4.056 62.386 3.902 62.603 3.601 c
+62.816 3.296 62.931 2.84 62.941 2.234 c
+h
+62.295 2.175 m
+62.295 2.616 62.225 2.94 62.089 3.145 c
+61.95 3.358 61.728 3.469 61.427 3.469 c
+61.111 3.469 60.872 3.314 60.707 3.013 c
+60.707 0.941 l
+60.872 0.636 61.111 0.484 61.427 0.484 c
+61.721 0.484 61.935 0.588 62.074 0.793 c
+62.21 1.007 62.284 1.338 62.295 1.778 c
+h
+63.647 2.175 m
+63.647 2.753 63.783 3.208 64.059 3.542 c
+64.342 3.884 64.713 4.056 65.175 4.056 c
+65.635 4.056 66.002 3.888 66.278 3.557 c
+66.561 3.233 66.708 2.786 66.719 2.219 c
+66.719 1.793 l
+66.719 1.224 66.576 0.768 66.293 0.426 c
+66.017 0.091 65.649 -0.073 65.19 -0.073 c
+64.728 -0.073 64.356 0.087 64.073 0.411 c
+63.797 0.742 63.654 1.183 63.647 1.734 c
+h
+64.294 1.793 m
+64.294 1.389 64.371 1.072 64.529 0.837 c
+64.695 0.602 64.915 0.484 65.19 0.484 c
+65.756 0.484 66.05 0.897 66.072 1.72 c
+66.072 2.175 l
+66.072 2.576 65.988 2.896 65.823 3.131 c
+65.665 3.374 65.447 3.498 65.175 3.498 c
+64.911 3.498 64.695 3.374 64.529 3.131 c
+64.371 2.896 64.294 2.576 64.294 2.175 c
+h
+69.497 1.014 m
+69.497 1.161 69.442 1.282 69.335 1.381 c
+69.225 1.477 69.019 1.595 68.718 1.734 c
+68.373 1.881 68.13 2.003 67.983 2.102 c
+67.836 2.208 67.726 2.326 67.66 2.454 c
+67.59 2.579 67.557 2.738 67.557 2.925 c
+67.557 3.248 67.674 3.516 67.909 3.733 c
+68.144 3.946 68.446 4.056 68.821 4.056 c
+69.204 4.056 69.512 3.943 69.747 3.719 c
+69.983 3.491 70.1 3.204 70.1 2.851 c
+69.453 2.851 l
+69.453 3.027 69.395 3.179 69.277 3.307 c
+69.159 3.432 69.005 3.498 68.821 3.498 c
+68.622 3.498 68.472 3.443 68.365 3.337 c
+68.255 3.237 68.204 3.105 68.204 2.94 c
+68.204 2.811 68.24 2.705 68.321 2.616 c
+68.398 2.535 68.589 2.433 68.894 2.308 c
+69.372 2.12 69.703 1.932 69.879 1.749 c
+70.056 1.573 70.144 1.344 70.144 1.072 c
+70.144 0.72 70.019 0.44 69.777 0.235 c
+69.541 0.029 69.225 -0.073 68.836 -0.073 c
+68.413 -0.073 68.075 0.044 67.822 0.279 c
+67.564 0.521 67.439 0.827 67.439 1.19 c
+68.086 1.19 l
+68.094 0.962 68.163 0.786 68.292 0.661 c
+68.417 0.544 68.601 0.484 68.836 0.484 c
+69.048 0.484 69.21 0.532 69.32 0.632 c
+69.439 0.727 69.497 0.856 69.497 1.014 c
+71.687 0 -0.646 3.983 re
+71.731 5.026 m
+71.731 4.916 71.702 4.825 71.643 4.748 c
+71.585 4.677 71.489 4.644 71.364 4.644 c
+71.246 4.644 71.151 4.677 71.085 4.748 c
+71.026 4.825 70.997 4.916 70.997 5.026 c
+70.997 5.144 71.026 5.236 71.085 5.306 c
+71.151 5.383 71.246 5.423 71.364 5.423 c
+71.489 5.423 71.585 5.383 71.643 5.306 c
+71.702 5.225 71.731 5.134 71.731 5.026 c
+73.554 4.939 m
+73.554 3.983 l
+74.157 3.983 l
+74.157 3.454 l
+73.554 3.454 l
+73.554 0.985 l
+73.554 0.827 73.576 0.709 73.628 0.632 c
+73.686 0.551 73.775 0.515 73.892 0.515 c
+73.981 0.515 74.068 0.529 74.157 0.558 c
+74.157 0 l
+74.01 -0.048 73.856 -0.073 73.701 -0.073 c
+73.444 -0.073 73.249 0.018 73.113 0.205 c
+72.973 0.389 72.907 0.65 72.907 0.985 c
+72.907 3.454 l
+72.304 3.454 l
+72.304 3.983 l
+72.907 3.983 l
+72.907 4.939 l
+h
+74.715 2.175 m
+74.715 2.753 74.851 3.208 75.126 3.542 c
+75.41 3.884 75.781 4.056 76.244 4.056 c
+76.703 4.056 77.071 3.888 77.347 3.557 c
+77.63 3.233 77.777 2.786 77.787 2.219 c
+77.787 1.793 l
+77.787 1.224 77.644 0.768 77.361 0.426 c
+77.085 0.091 76.718 -0.073 76.259 -0.073 c
+75.795 -0.073 75.425 0.087 75.142 0.411 c
+74.866 0.742 74.723 1.183 74.715 1.734 c
+h
+75.362 1.793 m
+75.362 1.389 75.439 1.072 75.597 0.837 c
+75.762 0.602 75.984 0.484 76.259 0.484 c
+76.825 0.484 77.119 0.897 77.141 1.72 c
+77.141 2.175 l
+77.141 2.576 77.056 2.896 76.89 3.131 c
+76.732 3.374 76.516 3.498 76.244 3.498 c
+75.98 3.498 75.762 3.374 75.597 3.131 c
+75.439 2.896 75.362 2.576 75.362 2.175 c
+h
+80.271 3.366 m
+80.183 3.384 80.084 3.395 79.978 3.395 c
+79.643 3.395 79.408 3.212 79.272 2.851 c
+79.272 0 l
+78.625 0 l
+78.625 3.983 l
+79.257 3.983 l
+79.272 3.572 l
+79.448 3.895 79.691 4.056 80.007 4.056 c
+80.113 4.056 80.202 4.035 80.271 3.998 c
+h
+82.079 1.087 m
+82.8 3.983 l
+83.491 3.983 l
+82.197 -0.559 l
+82.098 -0.9 81.954 -1.162 81.771 -1.338 c
+81.594 -1.515 81.393 -1.602 81.168 -1.602 c
+81.079 -1.602 80.966 -1.58 80.83 -1.544 c
+80.83 -1 l
+80.977 -1.014 l
+81.16 -1.014 81.308 -0.971 81.418 -0.882 c
+81.524 -0.794 81.613 -0.636 81.683 -0.412 c
+81.8 0.029 l
+80.639 3.983 l
+81.345 3.983 l
+h
+86.548 0 -0.646 3.983 re
+86.592 5.026 m
+86.592 4.916 86.563 4.825 86.504 4.748 c
+86.445 4.677 86.35 4.644 86.224 4.644 c
+86.107 4.644 86.012 4.677 85.945 4.748 c
+85.887 4.825 85.858 4.916 85.858 5.026 c
+85.858 5.144 85.887 5.236 85.945 5.306 c
+86.012 5.383 86.107 5.423 86.224 5.423 c
+86.35 5.423 86.445 5.383 86.504 5.306 c
+86.563 5.225 86.592 5.134 86.592 5.026 c
+88.179 3.983 m
+88.194 3.542 l
+88.448 3.884 88.771 4.056 89.164 4.056 c
+89.87 4.056 90.226 3.586 90.238 2.645 c
+90.238 0 l
+89.59 0 l
+89.59 2.616 l
+89.59 2.929 89.536 3.149 89.43 3.278 c
+89.318 3.403 89.164 3.469 88.959 3.469 c
+88.801 3.469 88.653 3.414 88.518 3.307 c
+88.389 3.197 88.287 3.061 88.209 2.896 c
+88.209 0 l
+87.562 0 l
+87.562 3.983 l
+h
+93.148 1.014 m
+93.148 1.161 93.093 1.282 92.986 1.381 c
+92.876 1.477 92.67 1.595 92.368 1.734 c
+92.023 1.881 91.781 2.003 91.634 2.102 c
+91.487 2.208 91.377 2.326 91.311 2.454 c
+91.24 2.579 91.208 2.738 91.208 2.925 c
+91.208 3.248 91.325 3.516 91.56 3.733 c
+91.795 3.946 92.096 4.056 92.472 4.056 c
+92.854 4.056 93.162 3.943 93.398 3.719 c
+93.633 3.491 93.75 3.204 93.75 2.851 c
+93.104 2.851 l
+93.104 3.027 93.045 3.179 92.927 3.307 c
+92.81 3.432 92.655 3.498 92.472 3.498 c
+92.273 3.498 92.123 3.443 92.017 3.337 c
+91.906 3.237 91.855 3.105 91.855 2.94 c
+91.855 2.811 91.891 2.705 91.972 2.616 c
+92.049 2.535 92.24 2.433 92.545 2.308 c
+93.023 2.12 93.353 1.932 93.53 1.749 c
+93.706 1.573 93.795 1.344 93.795 1.072 c
+93.795 0.72 93.669 0.44 93.428 0.235 c
+93.192 0.029 92.876 -0.073 92.487 -0.073 c
+92.064 -0.073 91.726 0.044 91.472 0.279 c
+91.215 0.521 91.09 0.827 91.09 1.19 c
+91.737 1.19 l
+91.744 0.962 91.814 0.786 91.942 0.661 c
+92.067 0.544 92.252 0.484 92.487 0.484 c
+92.7 0.484 92.861 0.532 92.971 0.632 c
+93.089 0.727 93.148 0.856 93.148 1.014 c
+95.338 0 -0.647 3.983 re
+95.382 5.026 m
+95.382 4.916 95.352 4.825 95.294 4.748 c
+95.235 4.677 95.14 4.644 95.015 4.644 c
+94.897 4.644 94.801 4.677 94.735 4.748 c
+94.677 4.825 94.647 4.916 94.647 5.026 c
+94.647 5.144 94.677 5.236 94.735 5.306 c
+94.801 5.383 94.897 5.423 95.015 5.423 c
+95.14 5.423 95.235 5.383 95.294 5.306 c
+95.352 5.225 95.382 5.134 95.382 5.026 c
+96.22 2.175 m
+96.22 2.782 96.33 3.248 96.558 3.572 c
+96.794 3.895 97.12 4.056 97.542 4.056 c
+97.925 4.056 98.223 3.898 98.44 3.586 c
+98.44 5.644 l
+99.086 5.644 l
+99.086 0 l
+98.498 0 l
+98.454 0.426 l
+98.248 0.091 97.943 -0.073 97.542 -0.073 c
+97.131 -0.073 96.808 0.081 96.573 0.397 c
+96.337 0.72 96.22 1.176 96.22 1.764 c
+h
+96.867 1.793 m
+96.867 1.352 96.929 1.022 97.058 0.808 c
+97.193 0.602 97.414 0.5 97.719 0.5 c
+98.043 0.5 98.282 0.661 98.44 0.985 c
+98.44 2.998 l
+98.271 3.31 98.032 3.469 97.719 3.469 c
+97.414 3.469 97.193 3.366 97.058 3.16 c
+96.929 2.954 96.867 2.631 96.867 2.19 c
+h
+101.511 -0.073 m
+101.012 -0.073 100.63 0.073 100.365 0.367 c
+100.1 0.661 99.969 1.095 99.969 1.675 c
+99.969 2.146 l
+99.969 2.741 100.093 3.208 100.351 3.542 c
+100.615 3.884 100.975 4.056 101.438 4.056 c
+101.897 4.056 102.24 3.902 102.467 3.601 c
+102.702 3.307 102.824 2.844 102.835 2.219 c
+102.835 1.793 l
+100.615 1.793 l
+100.615 1.705 l
+100.615 1.271 100.692 0.959 100.85 0.764 c
+101.016 0.577 101.247 0.484 101.541 0.484 c
+101.736 0.484 101.908 0.518 102.055 0.588 c
+102.203 0.665 102.338 0.783 102.467 0.941 c
+102.805 0.529 l
+102.519 0.125 102.089 -0.073 101.511 -0.073 c
+101.438 3.498 m
+101.162 3.498 100.96 3.403 100.835 3.219 c
+100.707 3.031 100.634 2.741 100.615 2.352 c
+102.188 2.352 l
+102.188 2.439 l
+102.166 2.822 102.099 3.09 101.982 3.248 c
+101.864 3.414 101.681 3.498 101.438 3.498 c
+106.039 0 -0.647 3.983 re
+106.083 5.026 m
+106.083 4.916 106.053 4.825 105.995 4.748 c
+105.936 4.677 105.841 4.644 105.716 4.644 c
+105.598 4.644 105.502 4.677 105.436 4.748 c
+105.378 4.825 105.348 4.916 105.348 5.026 c
+105.348 5.144 105.378 5.236 105.436 5.306 c
+105.502 5.383 105.598 5.423 105.716 5.423 c
+105.841 5.423 105.936 5.383 105.995 5.306 c
+106.053 5.225 106.083 5.134 106.083 5.026 c
+107.906 4.939 m
+107.906 3.983 l
+108.509 3.983 l
+108.509 3.454 l
+107.906 3.454 l
+107.906 0.985 l
+107.906 0.827 107.928 0.709 107.979 0.632 c
+108.038 0.551 108.126 0.515 108.244 0.515 c
+108.332 0.515 108.42 0.529 108.509 0.558 c
+108.509 0 l
+108.361 -0.048 108.207 -0.073 108.052 -0.073 c
+107.796 -0.073 107.601 0.018 107.464 0.205 c
+107.325 0.389 107.259 0.65 107.259 0.985 c
+107.259 3.454 l
+106.656 3.454 l
+106.656 3.983 l
+107.259 3.983 l
+107.259 4.939 l
+h
+109.317 0.353 m
+109.317 0.47 109.35 0.565 109.42 0.646 c
+109.486 0.723 109.589 0.764 109.729 0.764 c
+109.876 0.764 109.982 0.723 110.051 0.646 c
+110.129 0.565 110.169 0.47 110.169 0.353 c
+110.169 0.243 110.129 0.151 110.051 0.073 c
+109.982 -0.004 109.876 -0.044 109.729 -0.044 c
+109.589 -0.044 109.486 -0.004 109.42 0.073 c
+109.35 0.151 109.317 0.243 109.317 0.353 c
+f
+Q
+39.089 246.498 -0.675 5.35 re
+40.471 246.498 m
+40.471 249.953 l
+39.942 249.953 l
+39.942 250.481 l
+40.471 250.481 l
+40.471 250.937 l
+40.471 251.338 40.567 251.65 40.765 251.878 c
+40.97 252.102 41.249 252.216 41.602 252.216 c
+41.739 252.216 41.87 252.194 41.999 252.157 c
+41.97 251.613 l
+41.87 251.632 41.772 251.642 41.676 251.642 c
+41.301 251.642 41.118 251.378 41.118 250.849 c
+41.118 250.481 l
+41.793 250.481 l
+41.793 249.953 l
+41.118 249.953 l
+41.118 246.498 l
+h
+f
+q 1 0 0 1 45.6305 251.9076 cm
+0 0 m
+-0.383 0 l
+-0.383 -5.835 l
+0 -5.835 l
+0 -6.659 l
+-1.426 -6.659 l
+-1.426 0.823 l
+0 0.823 l
+h
+3.63 -3.543 m
+3.63 -4.171 3.52 -4.653 3.307 -4.983 c
+3.09 -5.318 2.77 -5.483 2.352 -5.483 c
+2.028 -5.483 1.767 -5.351 1.572 -5.087 c
+1.572 -6.938 l
+0.529 -6.938 l
+0.529 -1.426 l
+1.484 -1.426 l
+1.528 -1.794 l
+1.723 -1.5 1.992 -1.353 2.337 -1.353 c
+2.755 -1.353 3.075 -1.511 3.293 -1.823 c
+3.505 -2.128 3.619 -2.598 3.63 -3.234 c
+h
+2.587 -3.278 m
+2.587 -2.896 2.543 -2.627 2.454 -2.469 c
+2.373 -2.315 2.233 -2.234 2.028 -2.234 c
+1.822 -2.234 1.668 -2.323 1.572 -2.5 c
+1.572 -4.366 l
+1.66 -4.535 1.815 -4.616 2.042 -4.616 c
+2.248 -4.616 2.389 -4.535 2.469 -4.366 c
+2.547 -4.189 2.587 -3.917 2.587 -3.543 c
+h
+6.217 -2.44 m
+5.88 -2.411 l
+5.593 -2.411 5.402 -2.536 5.306 -2.778 c
+5.306 -5.409 l
+4.262 -5.409 l
+4.262 -1.426 l
+5.232 -1.426 l
+5.262 -1.867 l
+5.427 -1.525 5.659 -1.353 5.953 -1.353 c
+6.071 -1.353 6.162 -1.374 6.231 -1.411 c
+h
+6.511 -3.293 m
+6.511 -2.687 6.651 -2.213 6.937 -1.867 c
+7.22 -1.525 7.613 -1.353 8.113 -1.353 c
+8.621 -1.353 9.017 -1.525 9.304 -1.867 c
+9.587 -2.213 9.73 -2.687 9.73 -3.293 c
+9.73 -3.558 l
+9.73 -4.156 9.587 -4.627 9.304 -4.969 c
+9.017 -5.314 8.621 -5.483 8.113 -5.483 c
+7.603 -5.483 7.206 -5.314 6.923 -4.969 c
+6.647 -4.627 6.511 -4.152 6.511 -3.543 c
+h
+7.555 -3.558 m
+7.555 -4.263 7.739 -4.616 8.113 -4.616 c
+8.467 -4.616 8.658 -4.322 8.687 -3.734 c
+8.687 -3.293 l
+8.687 -2.932 8.635 -2.66 8.54 -2.484 c
+8.44 -2.308 8.297 -2.22 8.113 -2.22 c
+7.937 -2.22 7.798 -2.308 7.702 -2.484 c
+7.603 -2.66 7.555 -2.932 7.555 -3.293 c
+h
+11.465 -1.426 m
+11.465 -5.63 l
+11.465 -6.078 11.362 -6.423 11.156 -6.659 c
+10.95 -6.894 10.657 -7.011 10.274 -7.011 c
+10.105 -7.011 9.951 -6.99 9.803 -6.953 c
+9.789 -6.101 l
+9.907 -6.13 9.998 -6.145 10.069 -6.145 c
+10.304 -6.145 10.422 -5.983 10.422 -5.66 c
+10.422 -1.426 l
+h
+10.362 -0.397 m
+10.362 -0.243 10.41 -0.114 10.509 -0.015 c
+10.616 0.091 10.752 0.147 10.921 0.147 c
+11.097 0.147 11.233 0.091 11.332 -0.015 c
+11.439 -0.114 11.494 -0.243 11.494 -0.397 c
+11.494 -0.566 11.439 -0.702 11.332 -0.809 c
+11.233 -0.908 11.097 -0.956 10.921 -0.956 c
+10.744 -0.956 10.605 -0.908 10.509 -0.809 c
+10.41 -0.702 10.362 -0.566 10.362 -0.397 c
+13.832 -5.483 m
+13.302 -5.483 12.883 -5.328 12.581 -5.012 c
+12.288 -4.69 12.141 -4.23 12.141 -3.631 c
+12.141 -3.323 l
+12.141 -2.697 12.277 -2.213 12.552 -1.867 c
+12.824 -1.525 13.217 -1.353 13.728 -1.353 c
+14.228 -1.353 14.599 -1.515 14.846 -1.837 c
+15.099 -2.161 15.232 -2.639 15.243 -3.263 c
+15.243 -3.763 l
+13.17 -3.763 l
+13.188 -4.057 13.25 -4.274 13.361 -4.41 c
+13.479 -4.549 13.659 -4.616 13.905 -4.616 c
+14.247 -4.616 14.537 -4.499 14.772 -4.263 c
+15.184 -4.895 l
+15.055 -5.072 14.867 -5.215 14.625 -5.322 c
+14.379 -5.428 14.114 -5.483 13.832 -5.483 c
+13.184 -3.043 m
+14.214 -3.043 l
+14.214 -2.94 l
+14.214 -2.705 14.173 -2.529 14.096 -2.411 c
+14.026 -2.286 13.898 -2.22 13.714 -2.22 c
+13.537 -2.22 13.406 -2.29 13.317 -2.425 c
+13.236 -2.554 13.192 -2.76 13.184 -3.043 c
+17.271 -4.616 m
+17.565 -4.616 17.716 -4.421 17.726 -4.028 c
+18.696 -4.028 l
+18.696 -4.461 18.565 -4.814 18.3 -5.087 c
+18.035 -5.351 17.697 -5.483 17.285 -5.483 c
+16.775 -5.483 16.382 -5.328 16.109 -5.012 c
+15.845 -4.69 15.706 -4.219 15.698 -3.601 c
+15.698 -3.278 l
+15.698 -2.654 15.831 -2.176 16.095 -1.852 c
+16.367 -1.521 16.764 -1.353 17.285 -1.353 c
+17.716 -1.353 18.057 -1.492 18.314 -1.764 c
+18.568 -2.04 18.696 -2.421 18.696 -2.911 c
+17.726 -2.911 l
+17.726 -2.697 17.686 -2.529 17.609 -2.411 c
+17.539 -2.286 17.422 -2.22 17.256 -2.22 c
+17.08 -2.22 16.951 -2.286 16.874 -2.411 c
+16.793 -2.539 16.749 -2.789 16.741 -3.161 c
+16.741 -3.572 l
+16.741 -3.896 16.756 -4.123 16.786 -4.248 c
+16.822 -4.377 16.878 -4.469 16.947 -4.528 c
+17.025 -4.586 17.131 -4.616 17.271 -4.616 c
+20.504 -0.456 m
+20.504 -1.426 l
+21.034 -1.426 l
+21.034 -2.22 l
+20.504 -2.22 l
+20.504 -4.189 l
+20.504 -4.347 20.523 -4.454 20.564 -4.513 c
+20.612 -4.572 20.696 -4.601 20.813 -4.601 c
+20.92 -4.601 21.005 -4.594 21.063 -4.572 c
+21.063 -5.38 l
+20.887 -5.446 20.696 -5.483 20.49 -5.483 c
+19.814 -5.483 19.469 -5.097 19.461 -4.322 c
+19.461 -2.22 l
+19.006 -2.22 l
+19.006 -1.426 l
+19.461 -1.426 l
+19.461 -0.456 l
+h
+24.297 -1.426 m
+24.327 -1.823 l
+24.562 -1.511 24.863 -1.353 25.238 -1.353 c
+25.921 -1.353 26.274 -1.834 26.296 -2.793 c
+26.296 -5.409 l
+25.252 -5.409 l
+25.252 -2.866 l
+25.252 -2.643 25.216 -2.481 25.15 -2.382 c
+25.08 -2.286 24.963 -2.234 24.797 -2.234 c
+24.61 -2.234 24.462 -2.33 24.356 -2.514 c
+24.356 -5.409 l
+23.312 -5.409 l
+23.312 -1.426 l
+h
+28.928 -5.409 m
+28.898 -5.351 28.868 -5.247 28.839 -5.101 c
+28.652 -5.358 28.402 -5.483 28.09 -5.483 c
+27.755 -5.483 27.476 -5.376 27.251 -5.16 c
+27.035 -4.935 26.928 -4.645 26.928 -4.293 c
+26.928 -3.881 27.06 -3.564 27.325 -3.337 c
+27.59 -3.102 27.972 -2.984 28.471 -2.984 c
+28.795 -2.984 l
+28.795 -2.66 l
+28.795 -2.484 28.758 -2.363 28.693 -2.294 c
+28.633 -2.216 28.545 -2.176 28.427 -2.176 c
+28.17 -2.176 28.045 -2.33 28.045 -2.631 c
+27.002 -2.631 l
+27.002 -2.261 27.138 -1.955 27.413 -1.72 c
+27.685 -1.478 28.034 -1.353 28.457 -1.353 c
+28.898 -1.353 29.236 -1.47 29.471 -1.706 c
+29.713 -1.933 29.838 -2.257 29.838 -2.675 c
+29.838 -4.542 l
+29.838 -4.888 29.886 -5.156 29.985 -5.351 c
+29.985 -5.409 l
+h
+28.325 -4.659 m
+28.431 -4.659 28.523 -4.642 28.604 -4.601 c
+28.693 -4.553 28.755 -4.495 28.795 -4.424 c
+28.795 -3.601 l
+28.545 -3.601 l
+28.369 -3.601 28.225 -3.66 28.119 -3.778 c
+28.02 -3.888 27.972 -4.035 27.972 -4.219 c
+27.972 -4.513 28.09 -4.659 28.325 -4.659 c
+31.558 -1.426 m
+31.588 -1.794 l
+31.823 -1.5 32.132 -1.353 32.514 -1.353 c
+32.915 -1.353 33.194 -1.536 33.352 -1.897 c
+33.587 -1.536 33.914 -1.353 34.337 -1.353 c
+35.031 -1.353 35.384 -1.837 35.394 -2.808 c
+35.394 -5.409 l
+34.366 -5.409 l
+34.366 -2.866 l
+34.366 -2.643 34.329 -2.481 34.263 -2.382 c
+34.204 -2.286 34.094 -2.234 33.94 -2.234 c
+33.742 -2.234 33.601 -2.352 33.513 -2.587 c
+33.513 -5.409 l
+32.47 -5.409 l
+32.47 -2.881 l
+32.47 -2.646 32.44 -2.481 32.381 -2.382 c
+32.323 -2.286 32.213 -2.234 32.059 -2.234 c
+31.882 -2.234 31.739 -2.33 31.631 -2.514 c
+31.631 -5.409 l
+30.588 -5.409 l
+30.588 -1.426 l
+h
+37.776 -5.483 m
+37.247 -5.483 36.828 -5.328 36.527 -5.012 c
+36.233 -4.69 36.086 -4.23 36.086 -3.631 c
+36.086 -3.323 l
+36.086 -2.697 36.221 -2.213 36.497 -1.867 c
+36.769 -1.525 37.162 -1.353 37.673 -1.353 c
+38.173 -1.353 38.544 -1.515 38.79 -1.837 c
+39.044 -2.161 39.176 -2.639 39.187 -3.263 c
+39.187 -3.763 l
+37.115 -3.763 l
+37.133 -4.057 37.196 -4.274 37.306 -4.41 c
+37.424 -4.549 37.603 -4.616 37.85 -4.616 c
+38.191 -4.616 38.482 -4.499 38.717 -4.263 c
+39.128 -4.895 l
+39 -5.072 38.812 -5.215 38.57 -5.322 c
+38.324 -5.428 38.059 -5.483 37.776 -5.483 c
+37.129 -3.043 m
+38.158 -3.043 l
+38.158 -2.94 l
+38.158 -2.705 38.118 -2.529 38.041 -2.411 c
+37.971 -2.286 37.842 -2.22 37.659 -2.22 c
+37.482 -2.22 37.35 -2.29 37.262 -2.425 c
+37.181 -2.554 37.137 -2.76 37.129 -3.043 c
+39.525 0.823 m
+40.951 0.823 l
+40.951 -6.659 l
+39.525 -6.659 l
+39.525 -5.835 l
+39.907 -5.835 l
+39.907 0 l
+39.525 0 l
+h
+f
+Q
+89.919 246.498 -0.647 3.983 re
+89.962 251.525 m
+89.962 251.415 89.933 251.323 89.874 251.246 c
+89.815 251.176 89.72 251.143 89.595 251.143 c
+89.477 251.143 89.381 251.176 89.316 251.246 c
+89.256 251.323 89.227 251.415 89.227 251.525 c
+89.227 251.642 89.256 251.735 89.316 251.804 c
+89.381 251.881 89.477 251.922 89.595 251.922 c
+89.72 251.922 89.815 251.881 89.874 251.804 c
+89.933 251.723 89.962 251.632 89.962 251.525 c
+92.873 247.512 m
+92.873 247.659 92.818 247.781 92.711 247.879 c
+92.601 247.975 92.395 248.093 92.094 248.232 c
+91.748 248.38 91.506 248.501 91.359 248.6 c
+91.212 248.706 91.101 248.824 91.035 248.953 c
+90.966 249.078 90.933 249.236 90.933 249.423 c
+90.933 249.747 91.05 250.015 91.286 250.231 c
+91.521 250.445 91.822 250.555 92.197 250.555 c
+92.579 250.555 92.888 250.441 93.123 250.217 c
+93.358 249.989 93.476 249.703 93.476 249.35 c
+92.828 249.35 l
+92.828 249.526 92.77 249.677 92.652 249.805 c
+92.535 249.93 92.381 249.996 92.197 249.996 c
+91.999 249.996 91.847 249.941 91.741 249.835 c
+91.631 249.735 91.579 249.604 91.579 249.438 c
+91.579 249.309 91.616 249.203 91.697 249.115 c
+91.774 249.034 91.965 248.931 92.27 248.806 c
+92.747 248.619 93.079 248.431 93.255 248.247 c
+93.431 248.071 93.52 247.843 93.52 247.571 c
+93.52 247.218 93.395 246.939 93.152 246.734 c
+92.917 246.528 92.601 246.425 92.211 246.425 c
+91.789 246.425 91.451 246.543 91.197 246.778 c
+90.94 247.02 90.815 247.325 90.815 247.688 c
+91.461 247.688 l
+91.469 247.461 91.539 247.285 91.667 247.16 c
+91.793 247.042 91.976 246.983 92.211 246.983 c
+92.425 246.983 92.587 247.031 92.697 247.131 c
+92.814 247.226 92.873 247.354 92.873 247.512 c
+96.695 250.481 m
+96.709 250.04 l
+96.963 250.383 97.287 250.555 97.68 250.555 c
+98.385 250.555 98.742 250.084 98.752 249.144 c
+98.752 246.498 l
+98.106 246.498 l
+98.106 249.115 l
+98.106 249.427 98.05 249.647 97.944 249.776 c
+97.834 249.901 97.68 249.967 97.474 249.967 c
+97.316 249.967 97.169 249.912 97.033 249.805 c
+96.904 249.695 96.801 249.56 96.724 249.394 c
+96.724 246.498 l
+96.077 246.498 l
+96.077 250.481 l
+h
+99.59 248.673 m
+99.59 249.251 99.726 249.706 100.001 250.04 c
+100.285 250.383 100.656 250.555 101.119 250.555 c
+101.578 250.555 101.946 250.386 102.222 250.055 c
+102.505 249.732 102.652 249.284 102.662 248.718 c
+102.662 248.291 l
+102.662 247.722 102.519 247.266 102.236 246.925 c
+101.96 246.59 101.593 246.425 101.134 246.425 c
+100.67 246.425 100.3 246.586 100.017 246.909 c
+99.741 247.241 99.598 247.682 99.59 248.232 c
+h
+100.237 248.291 m
+100.237 247.887 100.314 247.571 100.472 247.336 c
+100.637 247.1 100.858 246.983 101.134 246.983 c
+101.7 246.983 101.994 247.395 102.016 248.218 c
+102.016 248.673 l
+102.016 249.074 101.931 249.394 101.765 249.629 c
+101.607 249.872 101.391 249.996 101.119 249.996 c
+100.855 249.996 100.637 249.872 100.472 249.629 c
+100.314 249.394 100.237 249.074 100.237 248.673 c
+h
+104.352 251.437 m
+104.352 250.481 l
+104.955 250.481 l
+104.955 249.953 l
+104.352 249.953 l
+104.352 247.483 l
+104.352 247.325 104.375 247.208 104.427 247.131 c
+104.485 247.05 104.573 247.013 104.691 247.013 c
+104.779 247.013 104.867 247.027 104.955 247.056 c
+104.955 246.498 l
+104.809 246.45 104.654 246.425 104.5 246.425 c
+104.242 246.425 104.048 246.516 103.912 246.703 c
+103.772 246.888 103.706 247.148 103.706 247.483 c
+103.706 249.953 l
+103.103 249.953 l
+103.103 250.481 l
+103.706 250.481 l
+103.706 251.437 l
+h
+110.32 248.291 m
+110.32 247.663 110.203 247.193 109.968 246.88 c
+109.74 246.576 109.424 246.425 109.012 246.425 c
+108.608 246.425 108.3 246.576 108.086 246.88 c
+108.086 244.97 l
+107.44 244.97 l
+107.44 250.481 l
+108.028 250.481 l
+108.071 250.04 l
+108.285 250.383 108.593 250.555 108.998 250.555 c
+109.439 250.555 109.765 250.401 109.983 250.1 c
+110.195 249.795 110.309 249.338 110.32 248.733 c
+h
+109.674 248.673 m
+109.674 249.115 109.604 249.438 109.468 249.643 c
+109.328 249.857 109.108 249.967 108.807 249.967 c
+108.491 249.967 108.252 249.813 108.086 249.512 c
+108.086 247.439 l
+108.252 247.134 108.491 246.983 108.807 246.983 c
+109.1 246.983 109.314 247.086 109.453 247.291 c
+109.59 247.505 109.663 247.836 109.674 248.276 c
+h
+112.805 249.864 m
+112.717 249.882 112.618 249.893 112.51 249.893 c
+112.177 249.893 111.941 249.71 111.805 249.35 c
+111.805 246.498 l
+111.159 246.498 l
+111.159 250.481 l
+111.791 250.481 l
+111.805 250.07 l
+111.982 250.393 112.225 250.555 112.54 250.555 c
+112.647 250.555 112.735 250.533 112.805 250.497 c
+h
+113.245 248.673 m
+113.245 249.251 113.382 249.706 113.657 250.04 c
+113.94 250.383 114.311 250.555 114.775 250.555 c
+115.234 250.555 115.601 250.386 115.876 250.055 c
+116.16 249.732 116.306 249.284 116.318 248.718 c
+116.318 248.291 l
+116.318 247.722 116.175 247.266 115.892 246.925 c
+115.616 246.59 115.248 246.425 114.789 246.425 c
+114.326 246.425 113.955 246.586 113.672 246.909 c
+113.397 247.241 113.253 247.682 113.245 248.232 c
+h
+113.892 248.291 m
+113.892 247.887 113.97 247.571 114.128 247.336 c
+114.293 247.1 114.513 246.983 114.789 246.983 c
+115.355 246.983 115.649 247.395 115.671 248.218 c
+115.671 248.673 l
+115.671 249.074 115.587 249.394 115.421 249.629 c
+115.263 249.872 115.047 249.996 114.775 249.996 c
+114.51 249.996 114.293 249.872 114.128 249.629 c
+113.97 249.394 113.892 249.074 113.892 248.673 c
+h
+118.228 247.483 m
+118.978 250.481 l
+119.639 250.481 l
+118.464 246.498 l
+117.979 246.498 l
+116.788 250.481 l
+117.449 250.481 l
+h
+121.021 246.498 -0.646 3.983 re
+121.066 251.525 m
+121.066 251.415 121.036 251.323 120.977 251.246 c
+120.919 251.176 120.823 251.143 120.698 251.143 c
+120.58 251.143 120.485 251.176 120.419 251.246 c
+120.36 251.323 120.331 251.415 120.331 251.525 c
+120.331 251.642 120.36 251.735 120.419 251.804 c
+120.485 251.881 120.58 251.922 120.698 251.922 c
+120.823 251.922 120.919 251.881 120.977 251.804 c
+121.036 251.723 121.066 251.632 121.066 251.525 c
+121.904 248.673 m
+121.904 249.28 122.014 249.747 122.242 250.07 c
+122.477 250.393 122.804 250.555 123.226 250.555 c
+123.608 250.555 123.907 250.397 124.123 250.084 c
+124.123 252.143 l
+124.769 252.143 l
+124.769 246.498 l
+124.182 246.498 l
+124.138 246.925 l
+123.932 246.59 123.627 246.425 123.226 246.425 c
+122.814 246.425 122.492 246.579 122.257 246.895 c
+122.021 247.218 121.904 247.674 121.904 248.262 c
+h
+122.55 248.291 m
+122.55 247.85 122.613 247.52 122.741 247.306 c
+122.877 247.1 123.098 246.998 123.402 246.998 c
+123.726 246.998 123.965 247.16 124.123 247.483 c
+124.123 249.497 l
+123.954 249.809 123.715 249.967 123.402 249.967 c
+123.098 249.967 122.877 249.864 122.741 249.658 c
+122.613 249.452 122.55 249.13 122.55 248.688 c
+h
+127.195 246.425 m
+126.695 246.425 126.313 246.572 126.049 246.865 c
+125.784 247.16 125.652 247.593 125.652 248.174 c
+125.652 248.644 l
+125.652 249.24 125.777 249.706 126.034 250.04 c
+126.299 250.383 126.658 250.555 127.121 250.555 c
+127.581 250.555 127.923 250.401 128.15 250.1 c
+128.385 249.805 128.507 249.342 128.518 248.718 c
+128.518 248.291 l
+126.299 248.291 l
+126.299 248.203 l
+126.299 247.769 126.376 247.457 126.534 247.262 c
+126.699 247.075 126.93 246.983 127.225 246.983 c
+127.419 246.983 127.591 247.017 127.739 247.086 c
+127.886 247.163 128.022 247.281 128.15 247.439 c
+128.489 247.027 l
+128.202 246.623 127.772 246.425 127.195 246.425 c
+127.121 249.996 m
+126.846 249.996 126.644 249.901 126.519 249.718 c
+126.39 249.529 126.317 249.24 126.299 248.85 c
+127.871 248.85 l
+127.871 248.938 l
+127.849 249.321 127.783 249.589 127.666 249.747 c
+127.548 249.912 127.364 249.996 127.121 249.996 c
+129.164 248.673 m
+129.164 249.28 129.275 249.747 129.503 250.07 c
+129.738 250.393 130.065 250.555 130.488 250.555 c
+130.87 250.555 131.167 250.397 131.385 250.084 c
+131.385 252.143 l
+132.031 252.143 l
+132.031 246.498 l
+131.443 246.498 l
+131.399 246.925 l
+131.194 246.59 130.888 246.425 130.488 246.425 c
+130.076 246.425 129.752 246.579 129.517 246.895 c
+129.282 247.218 129.164 247.674 129.164 248.262 c
+h
+129.812 248.291 m
+129.812 247.85 129.874 247.52 130.003 247.306 c
+130.139 247.1 130.359 246.998 130.664 246.998 c
+130.988 246.998 131.227 247.16 131.385 247.483 c
+131.385 249.497 l
+131.215 249.809 130.976 249.967 130.664 249.967 c
+130.359 249.967 130.139 249.864 130.003 249.658 c
+129.874 249.452 129.812 249.13 129.812 248.688 c
+h
+133.045 245.425 m
+132.649 245.689 l
+132.884 246.013 133.005 246.348 133.016 246.689 c
+133.016 247.306 l
+133.677 247.306 l
+133.677 246.778 l
+133.677 246.52 133.611 246.274 133.486 246.028 c
+133.369 245.785 133.222 245.583 133.045 245.425 c
+137.087 251.437 m
+137.087 250.481 l
+137.69 250.481 l
+137.69 249.953 l
+137.087 249.953 l
+137.087 247.483 l
+137.087 247.325 137.11 247.208 137.161 247.131 c
+137.22 247.05 137.308 247.013 137.426 247.013 c
+137.513 247.013 137.602 247.027 137.69 247.056 c
+137.69 246.498 l
+137.544 246.45 137.389 246.425 137.234 246.425 c
+136.977 246.425 136.783 246.516 136.647 246.703 c
+136.507 246.888 136.441 247.148 136.441 247.483 c
+136.441 249.953 l
+135.838 249.953 l
+135.838 250.481 l
+136.441 250.481 l
+136.441 251.437 l
+h
+139.101 250.07 m
+139.354 250.393 139.674 250.555 140.056 250.555 c
+140.762 250.555 141.118 250.084 141.13 249.144 c
+141.13 246.498 l
+140.483 246.498 l
+140.483 249.115 l
+140.483 249.427 140.428 249.647 140.322 249.776 c
+140.212 249.901 140.056 249.967 139.851 249.967 c
+139.693 249.967 139.546 249.912 139.41 249.805 c
+139.281 249.695 139.179 249.56 139.101 249.394 c
+139.101 246.498 l
+138.454 246.498 l
+138.454 252.143 l
+139.101 252.143 l
+h
+143.511 246.425 m
+143.011 246.425 142.629 246.572 142.365 246.865 c
+142.1 247.16 141.968 247.593 141.968 248.174 c
+141.968 248.644 l
+141.968 249.24 142.093 249.706 142.35 250.04 c
+142.614 250.383 142.974 250.555 143.437 250.555 c
+143.897 250.555 144.239 250.401 144.466 250.1 c
+144.702 249.805 144.823 249.342 144.834 248.718 c
+144.834 248.291 l
+142.614 248.291 l
+142.614 248.203 l
+142.614 247.769 142.691 247.457 142.849 247.262 c
+143.015 247.075 143.246 246.983 143.541 246.983 c
+143.736 246.983 143.908 247.017 144.054 247.086 c
+144.202 247.163 144.338 247.281 144.466 247.439 c
+144.804 247.027 l
+144.518 246.623 144.088 246.425 143.511 246.425 c
+143.437 249.996 m
+143.162 249.996 142.959 249.901 142.835 249.718 c
+142.706 249.529 142.633 249.24 142.614 248.85 c
+144.187 248.85 l
+144.187 248.938 l
+144.165 249.321 144.099 249.589 143.981 249.747 c
+143.863 249.912 143.68 249.996 143.437 249.996 c
+146.23 250.481 m
+146.245 250.04 l
+146.498 250.383 146.822 250.555 147.215 250.555 c
+147.921 250.555 148.277 250.084 148.288 249.144 c
+148.288 246.498 l
+147.641 246.498 l
+147.641 249.115 l
+147.641 249.427 147.586 249.647 147.48 249.776 c
+147.37 249.901 147.215 249.967 147.009 249.967 c
+146.851 249.967 146.704 249.912 146.568 249.805 c
+146.44 249.695 146.337 249.56 146.26 249.394 c
+146.26 246.498 l
+145.613 246.498 l
+145.613 250.481 l
+h
+153.022 246.498 m
+152.981 246.586 152.955 246.734 152.948 246.939 c
+152.713 246.593 152.419 246.425 152.066 246.425 c
+151.702 246.425 151.419 246.52 151.213 246.718 c
+151.015 246.925 150.919 247.211 150.919 247.586 c
+150.919 247.987 151.055 248.306 151.331 248.542 c
+151.603 248.783 151.977 248.909 152.448 248.909 c
+152.933 248.909 l
+152.933 249.335 l
+152.933 249.57 152.878 249.735 152.771 249.835 c
+152.661 249.941 152.499 249.996 152.286 249.996 c
+152.088 249.996 151.927 249.938 151.801 249.82 c
+151.684 249.703 151.625 249.556 151.625 249.379 c
+150.978 249.379 l
+150.978 249.574 151.037 249.765 151.154 249.953 c
+151.279 250.136 151.441 250.283 151.64 250.393 c
+151.846 250.5 152.073 250.555 152.33 250.555 c
+152.731 250.555 153.036 250.452 153.242 250.246 c
+153.455 250.04 153.569 249.747 153.58 249.365 c
+153.58 247.351 l
+153.58 247.046 153.617 246.782 153.697 246.557 c
+153.697 246.498 l
+h
+152.154 247.013 m
+152.32 247.013 152.47 247.056 152.61 247.145 c
+152.756 247.233 152.864 247.343 152.933 247.483 c
+152.933 248.424 l
+152.565 248.424 l
+152.249 248.424 152.007 248.354 151.831 248.218 c
+151.655 248.089 151.566 247.902 151.566 247.659 c
+151.566 247.432 151.61 247.266 151.699 247.16 c
+151.786 247.06 151.937 247.013 152.154 247.013 c
+156.916 250.481 m
+156.931 250.04 l
+157.185 250.383 157.508 250.555 157.901 250.555 c
+158.607 250.555 158.963 250.084 158.975 249.144 c
+158.975 246.498 l
+158.327 246.498 l
+158.327 249.115 l
+158.327 249.427 158.273 249.647 158.166 249.776 c
+158.055 249.901 157.901 249.967 157.695 249.967 c
+157.538 249.967 157.39 249.912 157.255 249.805 c
+157.126 249.695 157.023 249.56 156.946 249.394 c
+156.946 246.498 l
+156.299 246.498 l
+156.299 250.481 l
+h
+161.371 246.425 m
+160.871 246.425 160.488 246.572 160.224 246.865 c
+159.96 247.16 159.827 247.593 159.827 248.174 c
+159.827 248.644 l
+159.827 249.24 159.952 249.706 160.209 250.04 c
+160.474 250.383 160.833 250.555 161.297 250.555 c
+161.756 250.555 162.098 250.401 162.325 250.1 c
+162.561 249.805 162.682 249.342 162.693 248.718 c
+162.693 248.291 l
+160.474 248.291 l
+160.474 248.203 l
+160.474 247.769 160.551 247.457 160.709 247.262 c
+160.874 247.075 161.106 246.983 161.4 246.983 c
+161.595 246.983 161.768 247.017 161.914 247.086 c
+162.061 247.163 162.198 247.281 162.325 247.439 c
+162.664 247.027 l
+162.377 246.623 161.947 246.425 161.371 246.425 c
+161.297 249.996 m
+161.022 249.996 160.819 249.901 160.694 249.718 c
+160.565 249.529 160.492 249.24 160.474 248.85 c
+162.047 248.85 l
+162.047 248.938 l
+162.024 249.321 161.959 249.589 161.841 249.747 c
+161.723 249.912 161.539 249.996 161.297 249.996 c
+166.441 247.644 m
+167.044 250.481 l
+167.691 250.481 l
+166.706 246.498 l
+166.192 246.498 l
+165.413 249.35 l
+164.663 246.498 l
+164.134 246.498 l
+163.179 250.481 l
+163.81 250.481 l
+164.428 247.719 l
+165.163 250.481 l
+165.677 250.481 l
+h
+171.733 249.864 m
+171.645 249.882 171.546 249.893 171.439 249.893 c
+171.104 249.893 170.869 249.71 170.734 249.35 c
+170.734 246.498 l
+170.087 246.498 l
+170.087 250.481 l
+170.719 250.481 l
+170.734 250.07 l
+170.91 250.393 171.152 250.555 171.469 250.555 c
+171.575 250.555 171.663 250.533 171.733 250.497 c
+h
+173.732 246.425 m
+173.232 246.425 172.85 246.572 172.586 246.865 c
+172.321 247.16 172.189 247.593 172.189 248.174 c
+172.189 248.644 l
+172.189 249.24 172.314 249.706 172.571 250.04 c
+172.835 250.383 173.196 250.555 173.659 250.555 c
+174.119 250.555 174.46 250.401 174.688 250.1 c
+174.923 249.805 175.044 249.342 175.056 248.718 c
+175.056 248.291 l
+172.835 248.291 l
+172.835 248.203 l
+172.835 247.769 172.913 247.457 173.071 247.262 c
+173.236 247.075 173.468 246.983 173.762 246.983 c
+173.957 246.983 174.129 247.017 174.277 247.086 c
+174.423 247.163 174.559 247.281 174.688 247.439 c
+175.025 247.027 l
+174.739 246.623 174.31 246.425 173.732 246.425 c
+173.659 249.996 m
+173.383 249.996 173.181 249.901 173.056 249.718 c
+172.928 249.529 172.854 249.24 172.835 248.85 c
+174.408 248.85 l
+174.408 248.938 l
+174.387 249.321 174.32 249.589 174.202 249.747 c
+174.085 249.912 173.901 249.996 173.659 249.996 c
+178.715 248.291 m
+178.715 247.663 178.597 247.193 178.362 246.88 c
+178.135 246.576 177.819 246.425 177.407 246.425 c
+177.003 246.425 176.694 246.576 176.481 246.88 c
+176.481 244.97 l
+175.834 244.97 l
+175.834 250.481 l
+176.422 250.481 l
+176.467 250.04 l
+176.679 250.383 176.988 250.555 177.392 250.555 c
+177.834 250.555 178.16 250.401 178.377 250.1 c
+178.59 249.795 178.705 249.338 178.715 248.733 c
+h
+178.069 248.673 m
+178.069 249.115 177.999 249.438 177.863 249.643 c
+177.723 249.857 177.502 249.967 177.201 249.967 c
+176.885 249.967 176.646 249.813 176.481 249.512 c
+176.481 247.439 l
+176.646 247.134 176.885 246.983 177.201 246.983 c
+177.495 246.983 177.708 247.086 177.848 247.291 c
+177.984 247.505 178.057 247.836 178.069 248.276 c
+h
+179.421 248.673 m
+179.421 249.251 179.557 249.706 179.833 250.04 c
+180.116 250.383 180.486 250.555 180.949 250.555 c
+181.409 250.555 181.776 250.386 182.052 250.055 c
+182.335 249.732 182.482 249.284 182.493 248.718 c
+182.493 248.291 l
+182.493 247.722 182.35 247.266 182.067 246.925 c
+181.791 246.59 181.423 246.425 180.964 246.425 c
+180.501 246.425 180.13 246.586 179.847 246.909 c
+179.571 247.241 179.428 247.682 179.421 248.232 c
+h
+180.068 248.291 m
+180.068 247.887 180.145 247.571 180.303 247.336 c
+180.469 247.1 180.689 246.983 180.964 246.983 c
+181.53 246.983 181.824 247.395 181.846 248.218 c
+181.846 248.673 l
+181.846 249.074 181.762 249.394 181.597 249.629 c
+181.438 249.872 181.221 249.996 180.949 249.996 c
+180.685 249.996 180.469 249.872 180.303 249.629 c
+180.145 249.394 180.068 249.074 180.068 248.673 c
+h
+185.271 247.512 m
+185.271 247.659 185.216 247.781 185.109 247.879 c
+184.999 247.975 184.793 248.093 184.492 248.232 c
+184.147 248.38 183.904 248.501 183.757 248.6 c
+183.61 248.706 183.5 248.824 183.434 248.953 c
+183.364 249.078 183.33 249.236 183.33 249.423 c
+183.33 249.747 183.449 250.015 183.684 250.231 c
+183.919 250.445 184.22 250.555 184.595 250.555 c
+184.977 250.555 185.286 250.441 185.521 250.217 c
+185.756 249.989 185.874 249.703 185.874 249.35 c
+185.227 249.35 l
+185.227 249.526 185.169 249.677 185.051 249.805 c
+184.933 249.93 184.779 249.996 184.595 249.996 c
+184.396 249.996 184.246 249.941 184.139 249.835 c
+184.029 249.735 183.978 249.604 183.978 249.438 c
+183.978 249.309 184.014 249.203 184.095 249.115 c
+184.172 249.034 184.363 248.931 184.668 248.806 c
+185.146 248.619 185.477 248.431 185.653 248.247 c
+185.83 248.071 185.917 247.843 185.917 247.571 c
+185.917 247.218 185.793 246.939 185.55 246.734 c
+185.315 246.528 184.999 246.425 184.61 246.425 c
+184.187 246.425 183.849 246.543 183.596 246.778 c
+183.338 247.02 183.214 247.325 183.214 247.688 c
+183.86 247.688 l
+183.867 247.461 183.937 247.285 184.066 247.16 c
+184.191 247.042 184.375 246.983 184.61 246.983 c
+184.822 246.983 184.984 247.031 185.095 247.131 c
+185.212 247.226 185.271 247.354 185.271 247.512 c
+187.461 246.498 -0.646 3.983 re
+187.505 251.525 m
+187.505 251.415 187.476 251.323 187.417 251.246 c
+187.359 251.176 187.263 251.143 187.137 251.143 c
+187.02 251.143 186.925 251.176 186.858 251.246 c
+186.8 251.323 186.771 251.415 186.771 251.525 c
+186.771 251.642 186.8 251.735 186.858 251.804 c
+186.925 251.881 187.02 251.922 187.137 251.922 c
+187.263 251.922 187.359 251.881 187.417 251.804 c
+187.476 251.723 187.505 251.632 187.505 251.525 c
+189.328 251.437 m
+189.328 250.481 l
+189.931 250.481 l
+189.931 249.953 l
+189.328 249.953 l
+189.328 247.483 l
+189.328 247.325 189.35 247.208 189.402 247.131 c
+189.46 247.05 189.549 247.013 189.666 247.013 c
+189.755 247.013 189.842 247.027 189.931 247.056 c
+189.931 246.498 l
+189.784 246.45 189.629 246.425 189.475 246.425 c
+189.217 246.425 189.023 246.516 188.887 246.703 c
+188.747 246.888 188.681 247.148 188.681 247.483 c
+188.681 249.953 l
+188.078 249.953 l
+188.078 250.481 l
+188.681 250.481 l
+188.681 251.437 l
+h
+190.489 248.673 m
+190.489 249.251 190.625 249.706 190.9 250.04 c
+191.184 250.383 191.555 250.555 192.018 250.555 c
+192.477 250.555 192.845 250.386 193.121 250.055 c
+193.404 249.732 193.55 249.284 193.562 248.718 c
+193.562 248.291 l
+193.562 247.722 193.418 247.266 193.135 246.925 c
+192.859 246.59 192.492 246.425 192.032 246.425 c
+191.569 246.425 191.199 246.586 190.915 246.909 c
+190.64 247.241 190.497 247.682 190.489 248.232 c
+h
+191.136 248.291 m
+191.136 247.887 191.213 247.571 191.371 247.336 c
+191.536 247.1 191.757 246.983 192.032 246.983 c
+192.598 246.983 192.893 247.395 192.915 248.218 c
+192.915 248.673 l
+192.915 249.074 192.83 249.394 192.664 249.629 c
+192.506 249.872 192.29 249.996 192.018 249.996 c
+191.754 249.996 191.536 249.872 191.371 249.629 c
+191.213 249.394 191.136 249.074 191.136 248.673 c
+h
+196.045 249.864 m
+195.957 249.882 195.858 249.893 195.752 249.893 c
+195.417 249.893 195.182 249.71 195.046 249.35 c
+195.046 246.498 l
+194.399 246.498 l
+194.399 250.481 l
+195.031 250.481 l
+195.046 250.07 l
+195.222 250.393 195.465 250.555 195.781 250.555 c
+195.887 250.555 195.976 250.533 196.045 250.497 c
+h
+197.853 247.586 m
+198.574 250.481 l
+199.265 250.481 l
+197.971 245.94 l
+197.872 245.598 197.728 245.337 197.545 245.161 c
+197.368 244.984 197.167 244.896 196.942 244.896 c
+196.854 244.896 196.74 244.918 196.604 244.955 c
+196.604 245.498 l
+196.751 245.484 l
+196.934 245.484 197.082 245.528 197.192 245.616 c
+197.298 245.704 197.387 245.862 197.456 246.086 c
+197.574 246.528 l
+196.413 250.481 l
+197.119 250.481 l
+h
+202.322 246.498 -0.647 3.983 re
+202.366 251.525 m
+202.366 251.415 202.337 251.323 202.278 251.246 c
+202.219 251.176 202.123 251.143 201.998 251.143 c
+201.881 251.143 201.786 251.176 201.719 251.246 c
+201.661 251.323 201.631 251.415 201.631 251.525 c
+201.631 251.642 201.661 251.735 201.719 251.804 c
+201.786 251.881 201.881 251.922 201.998 251.922 c
+202.123 251.922 202.219 251.881 202.278 251.804 c
+202.337 251.723 202.366 251.632 202.366 251.525 c
+205.277 247.512 m
+205.277 247.659 205.221 247.781 205.115 247.879 c
+205.005 247.975 204.799 248.093 204.497 248.232 c
+204.152 248.38 203.91 248.501 203.762 248.6 c
+203.615 248.706 203.505 248.824 203.44 248.953 c
+203.369 249.078 203.336 249.236 203.336 249.423 c
+203.336 249.747 203.454 250.015 203.689 250.231 c
+203.924 250.445 204.225 250.555 204.6 250.555 c
+204.982 250.555 205.291 250.441 205.526 250.217 c
+205.761 249.989 205.879 249.703 205.879 249.35 c
+205.233 249.35 l
+205.233 249.526 205.173 249.677 205.056 249.805 c
+204.938 249.93 204.784 249.996 204.6 249.996 c
+204.402 249.996 204.251 249.941 204.145 249.835 c
+204.034 249.735 203.983 249.604 203.983 249.438 c
+203.983 249.309 204.02 249.203 204.101 249.115 c
+204.178 249.034 204.369 248.931 204.674 248.806 c
+205.152 248.619 205.482 248.431 205.659 248.247 c
+205.835 248.071 205.923 247.843 205.923 247.571 c
+205.923 247.218 205.798 246.939 205.556 246.734 c
+205.321 246.528 205.005 246.425 204.615 246.425 c
+204.192 246.425 203.854 246.543 203.6 246.778 c
+203.344 247.02 203.218 247.325 203.218 247.688 c
+203.866 247.688 l
+203.872 247.461 203.943 247.285 204.071 247.16 c
+204.196 247.042 204.38 246.983 204.615 246.983 c
+204.828 246.983 204.99 247.031 205.1 247.131 c
+205.218 247.226 205.277 247.354 205.277 247.512 c
+209.187 246.498 -0.647 3.983 re
+209.231 251.525 m
+209.231 251.415 209.201 251.323 209.142 251.246 c
+209.084 251.176 208.988 251.143 208.863 251.143 c
+208.745 251.143 208.65 251.176 208.584 251.246 c
+208.525 251.323 208.495 251.415 208.495 251.525 c
+208.495 251.642 208.525 251.735 208.584 251.804 c
+208.65 251.881 208.745 251.922 208.863 251.922 c
+208.988 251.922 209.084 251.881 209.142 251.804 c
+209.201 251.723 209.231 251.632 209.231 251.525 c
+210.818 250.481 m
+210.833 250.04 l
+211.086 250.383 211.409 250.555 211.803 250.555 c
+212.508 250.555 212.865 250.084 212.876 249.144 c
+212.876 246.498 l
+212.229 246.498 l
+212.229 249.115 l
+212.229 249.427 212.174 249.647 212.067 249.776 c
+211.957 249.901 211.803 249.967 211.597 249.967 c
+211.439 249.967 211.292 249.912 211.156 249.805 c
+211.028 249.695 210.925 249.56 210.847 249.394 c
+210.847 246.498 l
+210.201 246.498 l
+210.201 250.481 l
+h
+214.552 246.498 -0.647 3.983 re
+214.596 251.525 m
+214.596 251.415 214.567 251.323 214.507 251.246 c
+214.449 251.176 214.353 251.143 214.228 251.143 c
+214.11 251.143 214.015 251.176 213.949 251.246 c
+213.89 251.323 213.861 251.415 213.861 251.525 c
+213.861 251.642 213.89 251.735 213.949 251.804 c
+214.015 251.881 214.11 251.922 214.228 251.922 c
+214.353 251.922 214.449 251.881 214.507 251.804 c
+214.567 251.723 214.596 251.632 214.596 251.525 c
+216.418 251.437 m
+216.418 250.481 l
+217.021 250.481 l
+217.021 249.953 l
+216.418 249.953 l
+216.418 247.483 l
+216.418 247.325 216.441 247.208 216.491 247.131 c
+216.551 247.05 216.639 247.013 216.757 247.013 c
+216.845 247.013 216.933 247.027 217.021 247.056 c
+217.021 246.498 l
+216.874 246.45 216.72 246.425 216.566 246.425 c
+216.308 246.425 216.113 246.516 215.978 246.703 c
+215.838 246.888 215.772 247.148 215.772 247.483 c
+215.772 249.953 l
+215.169 249.953 l
+215.169 250.481 l
+215.772 250.481 l
+215.772 251.437 l
+h
+218.491 246.498 -0.647 3.983 re
+218.535 251.525 m
+218.535 251.415 218.505 251.323 218.447 251.246 c
+218.388 251.176 218.293 251.143 218.168 251.143 c
+218.05 251.143 217.954 251.176 217.888 251.246 c
+217.829 251.323 217.8 251.415 217.8 251.525 c
+217.8 251.642 217.829 251.735 217.888 251.804 c
+217.954 251.881 218.05 251.922 218.168 251.922 c
+218.293 251.922 218.388 251.881 218.447 251.804 c
+218.505 251.723 218.535 251.632 218.535 251.525 c
+221.549 246.498 m
+221.508 246.586 221.482 246.734 221.474 246.939 c
+221.239 246.593 220.946 246.425 220.593 246.425 c
+220.229 246.425 219.946 246.52 219.741 246.718 c
+219.542 246.925 219.446 247.211 219.446 247.586 c
+219.446 247.987 219.583 248.306 219.858 248.542 c
+220.13 248.783 220.505 248.909 220.975 248.909 c
+221.46 248.909 l
+221.46 249.335 l
+221.46 249.57 221.405 249.735 221.299 249.835 c
+221.188 249.941 221.027 249.996 220.813 249.996 c
+220.615 249.996 220.453 249.938 220.329 249.82 c
+220.211 249.703 220.152 249.556 220.152 249.379 c
+219.505 249.379 l
+219.505 249.574 219.564 249.765 219.681 249.953 c
+219.807 250.136 219.968 250.283 220.167 250.393 c
+220.373 250.5 220.6 250.555 220.857 250.555 c
+221.258 250.555 221.563 250.452 221.769 250.246 c
+221.982 250.04 222.096 249.747 222.107 249.365 c
+222.107 247.351 l
+222.107 247.046 222.143 246.782 222.224 246.557 c
+222.224 246.498 l
+h
+220.681 247.013 m
+220.846 247.013 220.997 247.056 221.137 247.145 c
+221.283 247.233 221.391 247.343 221.46 247.483 c
+221.46 248.424 l
+221.093 248.424 l
+220.776 248.424 220.534 248.354 220.358 248.218 c
+220.181 248.089 220.093 247.902 220.093 247.659 c
+220.093 247.432 220.138 247.266 220.225 247.16 c
+220.314 247.06 220.464 247.013 220.681 247.013 c
+223.812 246.498 -0.647 5.644 re
+225.532 246.498 -0.647 3.983 re
+225.576 251.525 m
+225.576 251.415 225.547 251.323 225.488 251.246 c
+225.428 251.176 225.333 251.143 225.208 251.143 c
+225.091 251.143 224.996 251.176 224.929 251.246 c
+224.871 251.323 224.841 251.415 224.841 251.525 c
+224.841 251.642 224.871 251.735 224.929 251.804 c
+224.996 251.881 225.091 251.922 225.208 251.922 c
+225.333 251.922 225.428 251.881 225.488 251.804 c
+225.547 251.723 225.576 251.632 225.576 251.525 c
+227.149 247.056 m
+229.075 247.056 l
+229.075 246.498 l
+226.413 246.498 l
+226.413 246.998 l
+228.236 249.909 l
+226.428 249.909 l
+226.428 250.481 l
+229 250.481 l
+229 249.996 l
+h
+231.221 246.425 m
+230.721 246.425 230.338 246.572 230.074 246.865 c
+229.809 247.16 229.677 247.593 229.677 248.174 c
+229.677 248.644 l
+229.677 249.24 229.802 249.706 230.059 250.04 c
+230.324 250.383 230.683 250.555 231.147 250.555 c
+231.606 250.555 231.948 250.401 232.175 250.1 c
+232.411 249.805 232.532 249.342 232.543 248.718 c
+232.543 248.291 l
+230.324 248.291 l
+230.324 248.203 l
+230.324 247.769 230.401 247.457 230.559 247.262 c
+230.724 247.075 230.956 246.983 231.25 246.983 c
+231.444 246.983 231.618 247.017 231.764 247.086 c
+231.911 247.163 232.047 247.281 232.175 247.439 c
+232.514 247.027 l
+232.227 246.623 231.797 246.425 231.221 246.425 c
+231.147 249.996 m
+230.872 249.996 230.669 249.901 230.544 249.718 c
+230.415 249.529 230.342 249.24 230.324 248.85 c
+231.897 248.85 l
+231.897 248.938 l
+231.874 249.321 231.808 249.589 231.691 249.747 c
+231.573 249.912 231.389 249.996 231.147 249.996 c
+233.189 248.673 m
+233.189 249.28 233.3 249.747 233.528 250.07 c
+233.763 250.393 234.091 250.555 234.513 250.555 c
+234.895 250.555 235.192 250.397 235.41 250.084 c
+235.41 252.143 l
+236.056 252.143 l
+236.056 246.498 l
+235.468 246.498 l
+235.425 246.925 l
+235.219 246.59 234.914 246.425 234.513 246.425 c
+234.101 246.425 233.778 246.579 233.543 246.895 c
+233.308 247.218 233.189 247.674 233.189 248.262 c
+h
+233.837 248.291 m
+233.837 247.85 233.899 247.52 234.028 247.306 c
+234.164 247.1 234.384 246.998 234.689 246.998 c
+235.013 246.998 235.252 247.16 235.41 247.483 c
+235.41 249.497 l
+235.24 249.809 235.001 249.967 234.689 249.967 c
+234.384 249.967 234.164 249.864 234.028 249.658 c
+233.899 249.452 233.837 249.13 233.837 248.688 c
+h
+239.481 246.498 -0.647 3.983 re
+239.525 251.525 m
+239.525 251.415 239.496 251.323 239.437 251.246 c
+239.379 251.176 239.283 251.143 239.158 251.143 c
+239.04 251.143 238.945 251.176 238.878 251.246 c
+238.82 251.323 238.791 251.415 238.791 251.525 c
+238.791 251.642 238.82 251.735 238.878 251.804 c
+238.945 251.881 239.04 251.922 239.158 251.922 c
+239.283 251.922 239.379 251.881 239.437 251.804 c
+239.496 251.723 239.525 251.632 239.525 251.525 c
+241.112 250.481 m
+241.127 250.04 l
+241.381 250.383 241.704 250.555 242.097 250.555 c
+242.803 250.555 243.159 250.084 243.171 249.144 c
+243.171 246.498 l
+242.523 246.498 l
+242.523 249.115 l
+242.523 249.427 242.469 249.647 242.362 249.776 c
+242.252 249.901 242.097 249.967 241.891 249.967 c
+241.734 249.967 241.587 249.912 241.451 249.805 c
+241.322 249.695 241.22 249.56 241.143 249.394 c
+241.143 246.498 l
+240.495 246.498 l
+240.495 250.481 l
+h
+247.242 246.983 m
+247.456 246.983 247.628 247.046 247.757 247.174 c
+247.892 247.31 247.967 247.501 247.977 247.748 c
+248.595 247.748 l
+248.572 247.366 248.437 247.046 248.183 246.792 c
+247.926 246.546 247.614 246.425 247.242 246.425 c
+246.75 246.425 246.375 246.576 246.111 246.88 c
+245.853 247.193 245.729 247.659 245.729 248.276 c
+245.729 248.718 l
+245.729 249.313 245.853 249.768 246.111 250.084 c
+246.375 250.397 246.75 250.555 247.242 250.555 c
+247.643 250.555 247.963 250.423 248.198 250.158 c
+248.44 249.901 248.572 249.556 248.595 249.115 c
+247.977 249.115 l
+247.955 249.408 247.882 249.629 247.757 249.776 c
+247.639 249.923 247.466 249.996 247.242 249.996 c
+246.949 249.996 246.731 249.897 246.595 249.703 c
+246.456 249.515 246.382 249.207 246.375 248.777 c
+246.375 248.262 l
+246.375 247.792 246.441 247.457 246.581 247.262 c
+246.728 247.075 246.949 246.983 247.242 246.983 c
+251.373 246.851 m
+251.156 246.564 250.843 246.425 250.432 246.425 c
+250.068 246.425 249.793 246.546 249.609 246.792 c
+249.432 247.046 249.337 247.409 249.33 247.879 c
+249.33 250.481 l
+249.976 250.481 l
+249.976 247.939 l
+249.976 247.31 250.16 246.998 250.535 246.998 c
+250.936 246.998 251.211 247.174 251.358 247.527 c
+251.358 250.481 l
+252.004 250.481 l
+252.004 246.498 l
+251.387 246.498 l
+h
+254.636 249.864 m
+254.548 249.882 254.448 249.893 254.342 249.893 c
+254.007 249.893 253.772 249.71 253.636 249.35 c
+253.636 246.498 l
+252.989 246.498 l
+252.989 250.481 l
+253.621 250.481 l
+253.636 250.07 l
+253.812 250.393 254.055 250.555 254.371 250.555 c
+254.477 250.555 254.566 250.533 254.636 250.497 c
+h
+256.929 249.864 m
+256.841 249.882 256.742 249.893 256.635 249.893 c
+256.301 249.893 256.065 249.71 255.929 249.35 c
+255.929 246.498 l
+255.283 246.498 l
+255.283 250.481 l
+255.915 250.481 l
+255.929 250.07 l
+256.106 250.393 256.349 250.555 256.665 250.555 c
+256.771 250.555 256.859 250.533 256.929 250.497 c
+h
+258.928 246.425 m
+258.429 246.425 258.046 246.572 257.781 246.865 c
+257.517 247.16 257.384 247.593 257.384 248.174 c
+257.384 248.644 l
+257.384 249.24 257.509 249.706 257.767 250.04 c
+258.032 250.383 258.392 250.555 258.855 250.555 c
+259.314 250.555 259.655 250.401 259.884 250.1 c
+260.119 249.805 260.24 249.342 260.251 248.718 c
+260.251 248.291 l
+258.032 248.291 l
+258.032 248.203 l
+258.032 247.769 258.109 247.457 258.267 247.262 c
+258.432 247.075 258.664 246.983 258.957 246.983 c
+259.152 246.983 259.325 247.017 259.472 247.086 c
+259.619 247.163 259.755 247.281 259.884 247.439 c
+260.222 247.027 l
+259.935 246.623 259.505 246.425 258.928 246.425 c
+258.855 249.996 m
+258.579 249.996 258.377 249.901 258.252 249.718 c
+258.123 249.529 258.049 249.24 258.032 248.85 c
+259.604 248.85 l
+259.604 248.938 l
+259.582 249.321 259.516 249.589 259.398 249.747 c
+259.281 249.912 259.097 249.996 258.855 249.996 c
+261.647 250.481 m
+261.662 250.04 l
+261.916 250.383 262.239 250.555 262.632 250.555 c
+263.337 250.555 263.694 250.084 263.705 249.144 c
+263.705 246.498 l
+263.058 246.498 l
+263.058 249.115 l
+263.058 249.427 263.003 249.647 262.897 249.776 c
+262.786 249.901 262.632 249.967 262.427 249.967 c
+262.269 249.967 262.121 249.912 261.986 249.805 c
+261.857 249.695 261.754 249.56 261.677 249.394 c
+261.677 246.498 l
+261.03 246.498 l
+261.03 250.481 l
+h
+265.528 251.437 m
+265.528 250.481 l
+266.13 250.481 l
+266.13 249.953 l
+265.528 249.953 l
+265.528 247.483 l
+265.528 247.325 265.55 247.208 265.602 247.131 c
+265.66 247.05 265.748 247.013 265.866 247.013 c
+265.954 247.013 266.042 247.027 266.13 247.056 c
+266.13 246.498 l
+265.984 246.45 265.829 246.425 265.675 246.425 c
+265.417 246.425 265.223 246.516 265.087 246.703 c
+264.947 246.888 264.881 247.148 264.881 247.483 c
+264.881 249.953 l
+264.278 249.953 l
+264.278 250.481 l
+264.881 250.481 l
+264.881 251.437 l
+h
+f
+q 1 0 0 1 239.0691 239.266 cm
+0 0 m
+0 0.607 0.111 1.073 0.339 1.397 c
+0.574 1.72 0.901 1.881 1.324 1.881 c
+1.706 1.881 2.003 1.723 2.22 1.411 c
+2.22 3.469 l
+2.866 3.469 l
+2.866 -2.175 l
+2.278 -2.175 l
+2.235 -1.749 l
+2.029 -2.084 1.724 -2.248 1.324 -2.248 c
+0.912 -2.248 0.589 -2.094 0.354 -1.778 c
+0.119 -1.455 0 -0.999 0 -0.411 c
+h
+0.647 -0.382 m
+0.647 -0.823 0.709 -1.153 0.838 -1.367 c
+0.975 -1.573 1.195 -1.675 1.5 -1.675 c
+1.823 -1.675 2.062 -1.514 2.22 -1.19 c
+2.22 0.823 l
+2.051 1.135 1.812 1.294 1.5 1.294 c
+1.195 1.294 0.975 1.191 0.838 0.985 c
+0.709 0.779 0.647 0.456 0.647 0.015 c
+h
+4.572 -2.175 -0.647 3.983 re
+4.616 2.851 m
+4.616 2.741 4.587 2.65 4.528 2.573 c
+4.469 2.502 4.374 2.469 4.248 2.469 c
+4.131 2.469 4.036 2.502 3.969 2.573 c
+3.911 2.65 3.882 2.741 3.882 2.851 c
+3.882 2.969 3.911 3.061 3.969 3.131 c
+4.036 3.208 4.131 3.248 4.248 3.248 c
+4.374 3.248 4.469 3.208 4.528 3.131 c
+4.587 3.05 4.616 2.959 4.616 2.851 c
+7.232 1.191 m
+7.144 1.209 7.045 1.22 6.939 1.22 c
+6.604 1.22 6.369 1.037 6.233 0.676 c
+6.233 -2.175 l
+5.586 -2.175 l
+5.586 1.808 l
+6.218 1.808 l
+6.233 1.397 l
+6.409 1.72 6.652 1.881 6.968 1.881 c
+7.074 1.881 7.163 1.86 7.232 1.823 c
+h
+9.231 -2.248 m
+8.732 -2.248 8.35 -2.102 8.085 -1.808 c
+7.82 -1.514 7.688 -1.08 7.688 -0.5 c
+7.688 -0.029 l
+7.688 0.566 7.813 1.033 8.071 1.367 c
+8.335 1.709 8.695 1.881 9.158 1.881 c
+9.617 1.881 9.959 1.727 10.187 1.426 c
+10.422 1.132 10.544 0.669 10.554 0.044 c
+10.554 -0.382 l
+8.335 -0.382 l
+8.335 -0.47 l
+8.335 -0.904 8.412 -1.216 8.57 -1.411 c
+8.736 -1.598 8.967 -1.691 9.261 -1.691 c
+9.455 -1.691 9.628 -1.657 9.775 -1.587 c
+9.922 -1.51 10.058 -1.392 10.187 -1.234 c
+10.525 -1.646 l
+10.238 -2.05 9.808 -2.248 9.231 -2.248 c
+9.158 1.323 m
+8.882 1.323 8.68 1.228 8.555 1.044 c
+8.427 0.856 8.353 0.566 8.335 0.177 c
+9.908 0.177 l
+9.908 0.264 l
+9.885 0.647 9.819 0.915 9.702 1.073 c
+9.584 1.239 9.401 1.323 9.158 1.323 c
+12.715 -1.691 m
+12.929 -1.691 13.101 -1.627 13.23 -1.5 c
+13.366 -1.363 13.439 -1.172 13.45 -0.926 c
+14.068 -0.926 l
+14.045 -1.308 13.91 -1.627 13.656 -1.881 c
+13.399 -2.127 13.087 -2.248 12.715 -2.248 c
+12.223 -2.248 11.848 -2.098 11.583 -1.793 c
+11.327 -1.481 11.201 -1.014 11.201 -0.397 c
+11.201 0.044 l
+11.201 0.64 11.327 1.095 11.583 1.411 c
+11.848 1.723 12.223 1.881 12.715 1.881 c
+13.116 1.881 13.436 1.75 13.671 1.484 c
+13.914 1.228 14.045 0.882 14.068 0.441 c
+13.45 0.441 l
+13.428 0.735 13.355 0.956 13.23 1.103 c
+13.112 1.249 12.939 1.323 12.715 1.323 c
+12.422 1.323 12.204 1.224 12.069 1.029 c
+11.929 0.842 11.855 0.533 11.848 0.103 c
+11.848 -0.411 l
+11.848 -0.881 11.914 -1.216 12.054 -1.411 c
+12.2 -1.598 12.422 -1.691 12.715 -1.691 c
+15.67 2.764 m
+15.67 1.808 l
+16.272 1.808 l
+16.272 1.279 l
+15.67 1.279 l
+15.67 -1.19 l
+15.67 -1.348 15.692 -1.466 15.743 -1.543 c
+15.802 -1.624 15.89 -1.66 16.008 -1.66 c
+16.096 -1.66 16.184 -1.646 16.272 -1.617 c
+16.272 -2.175 l
+16.125 -2.223 15.971 -2.248 15.817 -2.248 c
+15.56 -2.248 15.365 -2.157 15.229 -1.97 c
+15.089 -1.786 15.023 -1.525 15.023 -1.19 c
+15.023 1.279 l
+14.421 1.279 l
+14.421 1.808 l
+15.023 1.808 l
+15.023 2.764 l
+h
+16.831 0 m
+16.831 0.578 16.967 1.033 17.243 1.367 c
+17.525 1.709 17.897 1.881 18.359 1.881 c
+18.819 1.881 19.186 1.713 19.462 1.382 c
+19.745 1.058 19.892 0.611 19.903 0.044 c
+19.903 -0.382 l
+19.903 -0.951 19.759 -1.407 19.477 -1.749 c
+19.201 -2.084 18.834 -2.248 18.375 -2.248 c
+17.912 -2.248 17.54 -2.087 17.257 -1.764 c
+16.981 -1.433 16.838 -0.992 16.831 -0.441 c
+h
+17.478 -0.382 m
+17.478 -0.786 17.555 -1.103 17.713 -1.338 c
+17.878 -1.573 18.099 -1.691 18.375 -1.691 c
+18.94 -1.691 19.234 -1.278 19.256 -0.455 c
+19.256 0 l
+19.256 0.401 19.171 0.721 19.007 0.956 c
+18.849 1.199 18.631 1.323 18.359 1.323 c
+18.095 1.323 17.878 1.199 17.713 0.956 c
+17.555 0.721 17.478 0.401 17.478 0 c
+h
+22.387 1.191 m
+22.299 1.209 22.2 1.22 22.093 1.22 c
+21.758 1.22 21.523 1.037 21.388 0.676 c
+21.388 -2.175 l
+20.741 -2.175 l
+20.741 1.808 l
+21.373 1.808 l
+21.388 1.397 l
+21.564 1.72 21.806 1.881 22.122 1.881 c
+22.229 1.881 22.317 1.86 22.387 1.823 c
+h
+24.195 -1.087 m
+24.915 1.808 l
+25.606 1.808 l
+24.312 -2.734 l
+24.214 -3.075 24.07 -3.337 23.886 -3.513 c
+23.71 -3.69 23.508 -3.777 23.283 -3.777 c
+23.196 -3.777 23.082 -3.755 22.946 -3.719 c
+22.946 -3.175 l
+23.092 -3.189 l
+23.277 -3.189 23.423 -3.146 23.534 -3.057 c
+23.64 -2.969 23.728 -2.811 23.798 -2.587 c
+23.915 -2.146 l
+22.755 1.808 l
+23.46 1.808 l
+h
+25.885 -1.822 m
+25.885 -1.705 25.918 -1.61 25.989 -1.529 c
+26.055 -1.452 26.157 -1.411 26.297 -1.411 c
+26.444 -1.411 26.551 -1.452 26.62 -1.529 c
+26.697 -1.61 26.738 -1.705 26.738 -1.822 c
+26.738 -1.932 26.697 -2.024 26.62 -2.102 c
+26.551 -2.179 26.444 -2.219 26.297 -2.219 c
+26.157 -2.219 26.055 -2.179 25.989 -2.102 c
+25.918 -2.024 25.885 -1.932 25.885 -1.822 c
+f
+Q
+0.113 0.082 0.09 0 k
+35.668 231.104 238.665 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 40.8563 224.2696 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.683 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.437 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.941 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.028 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.245 1.933 5.376 2.051 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.152 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.635 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.177 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.116 l
+14.497 3.116 l
+14.497 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.497 1.602 l
+14.497 -0.103 l
+14.497 -0.324 l
+14.504 -0.393 14.527 -0.456 14.556 -0.515 c
+14.593 -0.566 14.648 -0.611 14.718 -0.647 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.688 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.603 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.279 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.041 -1.301 14.942 -1.309 14.835 -1.309 c
+14.648 -1.309 14.486 -1.294 14.35 -1.264 c
+14.221 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.085 13.85 -1.025 13.791 -0.956 c
+13.733 -0.879 13.689 -0.802 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.456 c
+13.59 -0.36 13.586 -0.264 13.586 -0.177 c
+h
+22.677 -1.323 m
+22.391 -1.323 22.148 -1.282 21.943 -1.205 c
+21.737 -1.118 21.564 -0.996 21.428 -0.838 c
+21.288 -0.684 21.185 -0.497 21.119 -0.279 c
+21.049 -0.056 21.016 0.191 21.016 0.455 c
+21.016 0.75 21.049 1.007 21.119 1.234 c
+21.197 1.459 21.303 1.646 21.442 1.793 c
+21.589 1.947 21.766 2.065 21.972 2.146 c
+22.178 2.234 22.413 2.278 22.677 2.278 c
+22.901 2.278 23.104 2.248 23.28 2.19 c
+23.456 2.131 23.607 2.047 23.736 1.94 c
+23.861 1.841 23.963 1.72 24.044 1.573 c
+24.122 1.433 24.176 1.282 24.206 1.117 c
+23.295 1.072 l
+23.265 1.249 23.196 1.389 23.089 1.5 c
+22.99 1.606 22.846 1.66 22.662 1.66 c
+22.416 1.66 22.24 1.558 22.134 1.352 c
+22.023 1.153 21.972 0.867 21.972 0.484 c
+21.972 -0.309 22.207 -0.706 22.677 -0.706 c
+22.843 -0.706 22.986 -0.654 23.104 -0.544 c
+23.221 -0.437 23.295 -0.276 23.324 -0.059 c
+24.235 -0.103 l
+24.206 -0.272 24.151 -0.426 24.074 -0.574 c
+24.004 -0.721 23.901 -0.852 23.765 -0.97 c
+23.636 -1.081 23.478 -1.168 23.295 -1.235 c
+23.119 -1.294 22.913 -1.323 22.677 -1.323 c
+27.281 -0.647 m
+28.413 -0.647 l
+28.413 -1.264 l
+25.106 -1.264 l
+25.106 -0.647 l
+26.371 -0.647 l
+26.371 2.896 l
+25.444 2.896 l
+25.444 3.513 l
+27.281 3.513 l
+h
+32.445 0.484 m
+32.445 0.209 32.408 -0.04 32.342 -0.264 c
+32.272 -0.482 32.17 -0.669 32.033 -0.823 c
+31.894 -0.981 31.717 -1.103 31.504 -1.191 c
+31.287 -1.279 31.034 -1.323 30.74 -1.323 c
+30.464 -1.323 30.217 -1.279 30.005 -1.191 c
+29.799 -1.103 29.626 -0.981 29.49 -0.823 c
+29.35 -0.669 29.248 -0.482 29.182 -0.264 c
+29.112 -0.04 29.078 0.209 29.078 0.484 c
+29.078 0.738 29.108 0.974 29.167 1.19 c
+29.233 1.415 29.336 1.606 29.475 1.764 c
+29.612 1.929 29.788 2.057 30.005 2.146 c
+30.217 2.234 30.475 2.278 30.769 2.278 c
+31.081 2.278 31.343 2.234 31.548 2.146 c
+31.761 2.057 31.934 1.929 32.062 1.764 c
+32.199 1.606 32.297 1.415 32.357 1.19 c
+32.415 0.974 32.445 0.738 32.445 0.484 c
+31.489 0.484 m
+31.489 0.69 31.474 0.867 31.445 1.014 c
+31.424 1.161 31.387 1.282 31.328 1.382 c
+31.269 1.477 31.195 1.547 31.107 1.587 c
+31.019 1.635 30.909 1.66 30.784 1.66 c
+30.519 1.66 30.328 1.562 30.211 1.367 c
+30.093 1.18 30.034 0.885 30.034 0.484 c
+30.034 0.062 30.093 -0.243 30.211 -0.426 c
+30.328 -0.614 30.504 -0.706 30.74 -0.706 c
+30.865 -0.706 30.979 -0.688 31.077 -0.647 c
+31.173 -0.599 31.254 -0.526 31.313 -0.426 c
+31.379 -0.331 31.424 -0.206 31.445 -0.059 c
+31.474 0.088 31.489 0.268 31.489 0.484 c
+35.495 -1.264 m
+35.495 0.72 l
+35.495 1.022 35.451 1.242 35.362 1.382 c
+35.281 1.529 35.146 1.602 34.951 1.602 c
+34.84 1.602 34.738 1.577 34.643 1.529 c
+34.554 1.477 34.473 1.411 34.408 1.323 c
+34.348 1.234 34.297 1.124 34.26 0.999 c
+34.231 0.881 34.216 0.75 34.216 0.602 c
+34.216 -1.264 l
+33.305 -1.264 l
+33.305 1.44 l
+33.305 1.66 l
+33.305 1.749 33.297 1.826 33.29 1.896 c
+33.29 2.087 l
+33.29 2.219 l
+34.142 2.219 l
+34.15 2.19 34.157 2.146 34.157 2.087 c
+34.157 1.896 l
+34.165 1.826 34.172 1.756 34.172 1.691 c
+34.179 1.62 34.186 1.565 34.186 1.529 c
+34.202 1.529 l
+34.319 1.793 34.47 1.984 34.657 2.102 c
+34.84 2.219 35.061 2.278 35.318 2.278 c
+35.503 2.278 35.663 2.248 35.804 2.19 c
+35.939 2.131 36.054 2.043 36.141 1.926 c
+36.23 1.808 36.292 1.664 36.332 1.5 c
+36.38 1.341 36.406 1.153 36.406 0.941 c
+36.406 -1.264 l
+h
+38.923 -1.323 m
+38.666 -1.323 38.439 -1.286 38.233 -1.22 c
+38.027 -1.143 37.851 -1.029 37.703 -0.882 c
+37.556 -0.728 37.439 -0.536 37.35 -0.309 c
+37.269 -0.085 37.233 0.18 37.233 0.484 c
+37.233 0.816 37.277 1.095 37.365 1.323 c
+37.461 1.558 37.589 1.741 37.747 1.881 c
+37.913 2.017 38.1 2.117 38.306 2.175 c
+38.512 2.242 38.721 2.278 38.938 2.278 c
+39.21 2.278 39.445 2.227 39.644 2.131 c
+39.85 2.043 40.014 1.911 40.143 1.735 c
+40.28 1.565 40.378 1.359 40.438 1.117 c
+40.504 0.881 40.54 0.617 40.54 0.324 c
+40.54 0.309 l
+38.173 0.309 l
+38.173 0.162 38.188 0.022 38.218 -0.103 c
+38.254 -0.231 38.31 -0.345 38.379 -0.441 c
+38.445 -0.53 38.53 -0.599 38.63 -0.647 c
+38.725 -0.698 38.838 -0.721 38.967 -0.721 c
+39.122 -0.721 39.262 -0.688 39.379 -0.617 c
+39.504 -0.551 39.592 -0.449 39.644 -0.309 c
+40.482 -0.382 l
+40.452 -0.482 40.397 -0.588 40.32 -0.706 c
+40.239 -0.816 40.136 -0.919 40.012 -1.014 c
+39.894 -1.103 39.739 -1.176 39.555 -1.235 c
+39.379 -1.294 39.166 -1.323 38.923 -1.323 c
+38.923 1.705 m
+38.836 1.705 38.747 1.691 38.659 1.66 c
+38.57 1.631 38.489 1.58 38.424 1.514 c
+38.354 1.444 38.295 1.356 38.248 1.249 c
+38.207 1.139 38.188 1.014 38.188 0.867 c
+39.659 0.867 l
+39.659 1.003 39.632 1.124 39.584 1.234 c
+39.544 1.341 39.489 1.429 39.424 1.5 c
+39.364 1.565 39.291 1.617 39.202 1.646 c
+39.114 1.683 39.019 1.705 38.923 1.705 c
+46.236 -2.631 m
+46.236 3.513 l
+48.162 3.513 l
+48.162 2.896 l
+47.089 2.896 l
+47.089 -2.014 l
+48.162 -2.014 l
+48.162 -2.631 l
+h
+50.414 2.219 m
+50.422 2.198 50.429 2.165 50.429 2.117 c
+50.437 2.076 50.443 2.028 50.443 1.97 c
+50.451 1.918 50.458 1.866 50.458 1.808 c
+50.458 1.646 l
+50.473 1.646 l
+50.532 1.764 50.598 1.859 50.679 1.94 c
+50.756 2.017 50.84 2.08 50.929 2.131 c
+51.017 2.19 51.106 2.227 51.193 2.248 c
+51.289 2.267 51.388 2.278 51.488 2.278 c
+51.693 2.278 51.873 2.234 52.031 2.146 c
+52.186 2.057 52.315 1.929 52.413 1.764 c
+52.52 1.606 52.597 1.415 52.648 1.19 c
+52.708 0.974 52.737 0.738 52.737 0.484 c
+52.737 0.22 52.708 -0.026 52.648 -0.25 c
+52.597 -0.467 52.52 -0.658 52.413 -0.823 c
+52.315 -0.981 52.182 -1.103 52.016 -1.191 c
+51.858 -1.279 51.671 -1.323 51.458 -1.323 c
+51.359 -1.323 51.26 -1.312 51.164 -1.294 c
+51.065 -1.272 50.973 -1.242 50.885 -1.191 c
+50.804 -1.143 50.727 -1.081 50.649 -1 c
+50.58 -0.923 50.521 -0.831 50.473 -0.721 c
+50.458 -0.721 l
+50.458 -0.809 l
+50.466 -0.849 50.473 -0.897 50.473 -0.956 c
+50.473 -1.118 l
+50.473 -1.294 l
+50.473 -2.631 l
+49.562 -2.631 l
+49.562 1.455 l
+49.562 1.62 49.554 1.768 49.547 1.896 c
+49.547 2.219 l
+h
+50.458 0.455 m
+50.458 0.228 50.477 0.037 50.517 -0.118 c
+50.565 -0.264 50.62 -0.382 50.679 -0.47 c
+50.745 -0.559 50.819 -0.625 50.9 -0.661 c
+50.977 -0.702 51.054 -0.721 51.135 -0.721 c
+51.23 -0.721 51.318 -0.698 51.399 -0.647 c
+51.488 -0.599 51.553 -0.53 51.605 -0.441 c
+51.664 -0.345 51.708 -0.221 51.737 -0.073 c
+51.774 0.081 51.796 0.268 51.796 0.484 c
+51.796 0.875 51.737 1.168 51.619 1.367 c
+51.509 1.562 51.355 1.66 51.149 1.66 c
+51.068 1.66 50.991 1.639 50.914 1.602 c
+50.833 1.562 50.759 1.5 50.694 1.411 c
+50.624 1.323 50.565 1.198 50.517 1.043 c
+50.477 0.885 50.458 0.69 50.458 0.455 c
+56.625 1.469 m
+56.525 1.477 56.423 1.488 56.316 1.5 c
+56.205 1.517 56.084 1.529 55.949 1.529 c
+55.772 1.529 55.614 1.488 55.478 1.411 c
+55.339 1.341 55.22 1.242 55.125 1.117 c
+55.037 0.989 54.967 0.841 54.919 0.676 c
+54.879 0.507 54.861 0.33 54.861 0.147 c
+54.861 -1.264 l
+53.965 -1.264 l
+53.965 0.985 l
+53.965 1.11 53.953 1.234 53.934 1.352 c
+53.924 1.477 53.909 1.595 53.89 1.705 c
+53.88 1.822 53.865 1.918 53.847 1.999 c
+53.824 2.087 53.807 2.161 53.788 2.219 c
+54.67 2.219 l
+54.677 2.168 54.688 2.117 54.699 2.057 c
+54.717 1.999 54.732 1.933 54.743 1.866 c
+54.761 1.808 54.776 1.741 54.788 1.675 c
+54.794 1.606 54.806 1.543 54.817 1.484 c
+54.831 1.484 l
+54.869 1.602 54.919 1.708 54.979 1.808 c
+55.045 1.903 55.125 1.988 55.214 2.057 c
+55.301 2.124 55.405 2.179 55.522 2.219 c
+55.648 2.256 55.794 2.278 55.964 2.278 c
+56.088 2.278 56.205 2.271 56.316 2.263 c
+56.434 2.252 56.537 2.238 56.625 2.219 c
+h
+60.876 0.484 m
+60.876 0.209 60.839 -0.04 60.774 -0.264 c
+60.704 -0.482 60.601 -0.669 60.465 -0.823 c
+60.325 -0.981 60.149 -1.103 59.935 -1.191 c
+59.719 -1.279 59.465 -1.323 59.172 -1.323 c
+58.896 -1.323 58.649 -1.279 58.436 -1.191 c
+58.231 -1.103 58.058 -0.981 57.922 -0.823 c
+57.782 -0.669 57.68 -0.482 57.613 -0.264 c
+57.543 -0.04 57.51 0.209 57.51 0.484 c
+57.51 0.738 57.539 0.974 57.599 1.19 c
+57.665 1.415 57.768 1.606 57.907 1.764 c
+58.043 1.929 58.22 2.057 58.436 2.146 c
+58.649 2.234 58.906 2.278 59.201 2.278 c
+59.513 2.278 59.774 2.234 59.98 2.146 c
+60.193 2.057 60.365 1.929 60.494 1.764 c
+60.63 1.606 60.729 1.415 60.788 1.19 c
+60.847 0.974 60.876 0.738 60.876 0.484 c
+59.921 0.484 m
+59.921 0.69 59.906 0.867 59.877 1.014 c
+59.855 1.161 59.818 1.282 59.76 1.382 c
+59.7 1.477 59.627 1.547 59.538 1.587 c
+59.451 1.635 59.34 1.66 59.216 1.66 c
+58.95 1.66 58.76 1.562 58.642 1.367 c
+58.524 1.18 58.466 0.885 58.466 0.484 c
+58.466 0.062 58.524 -0.243 58.642 -0.426 c
+58.76 -0.614 58.936 -0.706 59.172 -0.706 c
+59.297 -0.706 59.411 -0.688 59.509 -0.647 c
+59.605 -0.599 59.686 -0.526 59.744 -0.426 c
+59.81 -0.331 59.855 -0.206 59.877 -0.059 c
+59.906 0.088 59.921 0.268 59.921 0.484 c
+64.055 -1.309 m
+64.055 -1.544 64.011 -1.742 63.922 -1.911 c
+63.842 -2.076 63.735 -2.213 63.6 -2.323 c
+63.459 -2.429 63.305 -2.506 63.129 -2.558 c
+62.952 -2.606 62.769 -2.631 62.586 -2.631 c
+62.497 -2.631 62.409 -2.624 62.32 -2.616 c
+62.233 -2.606 62.144 -2.595 62.056 -2.587 c
+61.975 -2.576 61.902 -2.562 61.836 -2.543 c
+61.766 -2.532 61.707 -2.517 61.659 -2.499 c
+61.659 -1.823 l
+61.747 -1.841 61.865 -1.86 62.012 -1.881 c
+62.159 -1.911 62.32 -1.926 62.497 -1.926 c
+62.592 -1.926 62.681 -1.914 62.761 -1.897 c
+62.839 -1.874 62.908 -1.841 62.967 -1.793 c
+63.026 -1.742 63.07 -1.669 63.1 -1.573 c
+63.129 -1.484 63.143 -1.371 63.143 -1.235 c
+63.143 1.602 l
+62.012 1.602 l
+62.012 2.219 l
+64.055 2.219 l
+h
+63.158 3.513 0.897 -0.676 re
+63.158 2.836 m
+67.351 -1.323 m
+67.094 -1.323 66.866 -1.286 66.661 -1.22 c
+66.455 -1.143 66.278 -1.029 66.131 -0.882 c
+65.984 -0.728 65.867 -0.536 65.778 -0.309 c
+65.697 -0.085 65.661 0.18 65.661 0.484 c
+65.661 0.816 65.705 1.095 65.793 1.323 c
+65.888 1.558 66.017 1.741 66.175 1.881 c
+66.341 2.017 66.528 2.117 66.734 2.175 c
+66.94 2.242 67.149 2.278 67.366 2.278 c
+67.638 2.278 67.873 2.227 68.072 2.131 c
+68.277 2.043 68.443 1.911 68.571 1.735 c
+68.707 1.565 68.807 1.359 68.865 1.117 c
+68.932 0.881 68.968 0.617 68.968 0.324 c
+68.968 0.309 l
+66.602 0.309 l
+66.602 0.162 66.617 0.022 66.646 -0.103 c
+66.682 -0.231 66.738 -0.345 66.808 -0.441 c
+66.873 -0.53 66.958 -0.599 67.058 -0.647 c
+67.153 -0.698 67.267 -0.721 67.396 -0.721 c
+67.55 -0.721 67.689 -0.688 67.807 -0.617 c
+67.932 -0.551 68.02 -0.449 68.072 -0.309 c
+68.909 -0.382 l
+68.88 -0.482 68.824 -0.588 68.747 -0.706 c
+68.666 -0.816 68.564 -0.919 68.439 -1.014 c
+68.321 -1.103 68.167 -1.176 67.983 -1.235 c
+67.807 -1.294 67.594 -1.323 67.351 -1.323 c
+67.351 1.705 m
+67.263 1.705 67.175 1.691 67.087 1.66 c
+66.999 1.631 66.918 1.58 66.852 1.514 c
+66.782 1.444 66.723 1.356 66.675 1.249 c
+66.634 1.139 66.617 1.014 66.617 0.867 c
+68.086 0.867 l
+68.086 1.003 68.061 1.124 68.013 1.234 c
+67.972 1.341 67.918 1.429 67.851 1.5 c
+67.792 1.565 67.719 1.617 67.631 1.646 c
+67.542 1.683 67.447 1.705 67.351 1.705 c
+71.412 -1.323 m
+71.126 -1.323 70.883 -1.282 70.677 -1.205 c
+70.471 -1.118 70.299 -0.996 70.162 -0.838 c
+70.023 -0.684 69.92 -0.497 69.854 -0.279 c
+69.784 -0.056 69.751 0.191 69.751 0.455 c
+69.751 0.75 69.784 1.007 69.854 1.234 c
+69.931 1.459 70.037 1.646 70.177 1.793 c
+70.324 1.947 70.501 2.065 70.706 2.146 c
+70.912 2.234 71.147 2.278 71.412 2.278 c
+71.636 2.278 71.839 2.248 72.015 2.19 c
+72.191 2.131 72.342 2.047 72.47 1.94 c
+72.595 1.841 72.698 1.72 72.779 1.573 c
+72.856 1.433 72.911 1.282 72.94 1.117 c
+72.03 1.072 l
+71.999 1.249 71.93 1.389 71.824 1.5 c
+71.724 1.606 71.581 1.66 71.397 1.66 c
+71.151 1.66 70.975 1.558 70.868 1.352 c
+70.758 1.153 70.706 0.867 70.706 0.484 c
+70.706 -0.309 70.941 -0.706 71.412 -0.706 c
+71.577 -0.706 71.721 -0.654 71.839 -0.544 c
+71.956 -0.437 72.03 -0.276 72.059 -0.059 c
+72.97 -0.103 l
+72.94 -0.272 72.886 -0.426 72.809 -0.574 c
+72.739 -0.721 72.635 -0.852 72.5 -0.97 c
+72.371 -1.081 72.213 -1.168 72.03 -1.235 c
+71.853 -1.294 71.647 -1.323 71.412 -1.323 c
+74.521 1.602 m
+73.977 1.602 l
+73.977 2.219 l
+74.565 2.219 l
+74.844 3.116 l
+75.417 3.116 l
+75.417 2.219 l
+76.652 2.219 l
+76.652 1.602 l
+75.417 1.602 l
+75.417 -0.103 l
+75.417 -0.324 l
+75.425 -0.393 75.446 -0.456 75.476 -0.515 c
+75.513 -0.566 75.568 -0.611 75.638 -0.647 c
+75.715 -0.676 75.829 -0.691 75.976 -0.691 c
+76.112 -0.691 76.248 -0.688 76.387 -0.676 c
+76.523 -0.658 76.656 -0.632 76.784 -0.603 c
+76.784 -1.205 l
+76.703 -1.216 76.626 -1.231 76.549 -1.249 c
+76.468 -1.261 76.391 -1.268 76.314 -1.279 c
+76.233 -1.286 76.145 -1.294 76.049 -1.294 c
+75.961 -1.301 75.862 -1.309 75.756 -1.309 c
+75.568 -1.309 75.407 -1.294 75.27 -1.264 c
+75.142 -1.228 75.028 -1.183 74.933 -1.132 c
+74.844 -1.085 74.771 -1.025 74.712 -0.956 c
+74.653 -0.879 74.609 -0.802 74.58 -0.721 c
+74.55 -0.632 74.528 -0.544 74.521 -0.456 c
+74.509 -0.36 74.506 -0.264 74.506 -0.177 c
+h
+82.921 2.219 m
+82.921 0.264 l
+82.921 0.125 82.929 0 82.951 -0.118 c
+82.969 -0.228 83.002 -0.32 83.053 -0.397 c
+83.101 -0.478 83.16 -0.54 83.23 -0.588 c
+83.296 -0.628 83.381 -0.647 83.48 -0.647 c
+83.568 -0.647 83.649 -0.628 83.73 -0.588 c
+83.818 -0.54 83.891 -0.47 83.951 -0.382 c
+84.009 -0.287 84.053 -0.177 84.082 -0.059 c
+84.119 0.066 84.142 0.206 84.142 0.353 c
+84.142 2.219 l
+85.038 2.219 l
+85.038 -0.485 l
+85.038 -0.721 l
+85.046 -0.802 85.052 -0.879 85.052 -0.956 c
+85.052 -1.147 l
+85.06 -1.199 85.067 -1.235 85.067 -1.264 c
+84.215 -1.264 l
+84.204 -1.235 84.192 -1.199 84.186 -1.147 c
+84.186 -0.956 l
+84.186 -0.889 84.178 -0.819 84.171 -0.75 c
+84.171 -0.574 l
+84.156 -0.574 l
+84.038 -0.838 83.884 -1.029 83.7 -1.147 c
+83.524 -1.264 83.322 -1.323 83.097 -1.323 c
+82.892 -1.323 82.719 -1.286 82.583 -1.22 c
+82.444 -1.154 82.334 -1.058 82.245 -0.941 c
+82.164 -0.823 82.106 -0.688 82.069 -0.53 c
+82.039 -0.364 82.025 -0.187 82.025 0 c
+82.025 2.219 l
+h
+89.113 1.469 m
+89.014 1.477 88.911 1.488 88.805 1.5 c
+88.695 1.517 88.573 1.529 88.437 1.529 c
+88.261 1.529 88.103 1.488 87.967 1.411 c
+87.827 1.341 87.71 1.242 87.614 1.117 c
+87.525 0.989 87.456 0.841 87.408 0.676 c
+87.367 0.507 87.35 0.33 87.35 0.147 c
+87.35 -1.264 l
+86.453 -1.264 l
+86.453 0.985 l
+86.453 1.11 86.442 1.234 86.423 1.352 c
+86.413 1.477 86.398 1.595 86.38 1.705 c
+86.368 1.822 86.353 1.918 86.335 1.999 c
+86.313 2.087 86.295 2.161 86.276 2.219 c
+87.159 2.219 l
+87.166 2.168 87.176 2.117 87.188 2.057 c
+87.206 1.999 87.221 1.933 87.232 1.866 c
+87.25 1.808 87.265 1.741 87.276 1.675 c
+87.284 1.606 87.294 1.543 87.305 1.484 c
+87.32 1.484 l
+87.357 1.602 87.408 1.708 87.467 1.808 c
+87.533 1.903 87.614 1.988 87.702 2.057 c
+87.791 2.124 87.893 2.179 88.011 2.219 c
+88.136 2.256 88.283 2.278 88.452 2.278 c
+88.577 2.278 88.695 2.271 88.805 2.263 c
+88.922 2.252 89.025 2.238 89.113 2.219 c
+h
+92.263 -0.647 m
+93.395 -0.647 l
+93.395 -1.264 l
+90.087 -1.264 l
+90.087 -0.647 l
+91.351 -0.647 l
+91.351 2.896 l
+90.425 2.896 l
+90.425 3.513 l
+92.263 3.513 l
+h
+94.592 -2.631 m
+94.592 -2.014 l
+95.666 -2.014 l
+95.666 2.896 l
+94.592 2.896 l
+94.592 3.513 l
+96.518 3.513 l
+96.518 -2.631 l
+h
+f
+Q
+q 1 0 0 1 65.7934 208.1152 cm
+0 0 m
+0 5.35 l
+1.278 5.35 l
+1.904 5.35 2.389 5.152 2.734 4.762 c
+3.076 4.369 3.248 3.821 3.248 3.116 c
+3.248 2.219 l
+3.248 1.514 3.072 0.962 2.72 0.573 c
+2.374 0.191 1.87 0 1.205 0 c
+h
+0.676 4.777 m
+0.676 0.573 l
+1.22 0.573 l
+1.691 0.573 2.032 0.709 2.249 0.985 c
+2.473 1.257 2.587 1.661 2.587 2.19 c
+2.587 3.131 l
+2.587 3.697 2.477 4.116 2.263 4.38 c
+2.047 4.644 1.72 4.777 1.278 4.777 c
+h
+4.072 2.175 m
+4.072 2.753 4.208 3.208 4.484 3.543 c
+4.767 3.884 5.137 4.056 5.6 4.056 c
+6.059 4.056 6.427 3.888 6.703 3.557 c
+6.986 3.233 7.133 2.786 7.144 2.219 c
+7.144 1.793 l
+7.144 1.224 7 0.768 6.718 0.426 c
+6.442 0.092 6.074 -0.073 5.615 -0.073 c
+5.152 -0.073 4.781 0.088 4.498 0.411 c
+4.222 0.742 4.079 1.183 4.072 1.735 c
+h
+4.719 1.793 m
+4.719 1.389 4.796 1.073 4.954 0.838 c
+5.119 0.603 5.34 0.485 5.615 0.485 c
+6.181 0.485 6.475 0.897 6.497 1.72 c
+6.497 2.175 l
+6.497 2.576 6.413 2.896 6.247 3.131 c
+6.089 3.373 5.872 3.499 5.6 3.499 c
+5.336 3.499 5.119 3.373 4.954 3.131 c
+4.796 2.896 4.719 2.576 4.719 2.175 c
+h
+10.951 1.147 m
+11.553 3.983 l
+12.2 3.983 l
+11.215 0 l
+10.701 0 l
+9.922 2.851 l
+9.172 0 l
+8.644 0 l
+7.688 3.983 l
+8.32 3.983 l
+8.937 1.22 l
+9.672 3.983 l
+10.186 3.983 l
+h
+13.494 3.983 m
+13.508 3.543 l
+13.762 3.884 14.086 4.056 14.479 4.056 c
+15.184 4.056 15.541 3.586 15.551 2.645 c
+15.551 0 l
+14.905 0 l
+14.905 2.616 l
+14.905 2.929 14.85 3.15 14.743 3.278 c
+14.633 3.403 14.479 3.469 14.273 3.469 c
+14.115 3.469 13.968 3.414 13.832 3.308 c
+13.704 3.197 13.6 3.061 13.523 2.896 c
+13.523 0 l
+12.877 0 l
+12.877 3.983 l
+h
+17.228 0 -0.647 5.644 re
+18.109 2.175 m
+18.109 2.753 18.246 3.208 18.521 3.543 c
+18.803 3.884 19.175 4.056 19.638 4.056 c
+20.097 4.056 20.465 3.888 20.74 3.557 c
+21.024 3.233 21.17 2.786 21.182 2.219 c
+21.182 1.793 l
+21.182 1.224 21.038 0.768 20.756 0.426 c
+20.48 0.092 20.112 -0.073 19.653 -0.073 c
+19.19 -0.073 18.819 0.088 18.535 0.411 c
+18.26 0.742 18.117 1.183 18.109 1.735 c
+h
+18.756 1.793 m
+18.756 1.389 18.834 1.073 18.992 0.838 c
+19.156 0.603 19.377 0.485 19.653 0.485 c
+20.218 0.485 20.513 0.897 20.534 1.72 c
+20.534 2.175 l
+20.534 2.576 20.45 2.896 20.285 3.131 c
+20.127 3.373 19.91 3.499 19.638 3.499 c
+19.374 3.499 19.156 3.373 18.992 3.131 c
+18.834 2.896 18.756 2.576 18.756 2.175 c
+h
+24.062 0 m
+24.022 0.088 23.996 0.235 23.989 0.441 c
+23.754 0.096 23.46 -0.073 23.107 -0.073 c
+22.743 -0.073 22.46 0.022 22.254 0.22 c
+22.056 0.426 21.961 0.713 21.961 1.088 c
+21.961 1.488 22.096 1.808 22.372 2.043 c
+22.644 2.286 23.019 2.41 23.489 2.41 c
+23.974 2.41 l
+23.974 2.837 l
+23.974 3.072 23.919 3.237 23.813 3.337 c
+23.702 3.443 23.541 3.499 23.328 3.499 c
+23.129 3.499 22.967 3.439 22.842 3.322 c
+22.725 3.204 22.666 3.057 22.666 2.881 c
+22.019 2.881 l
+22.019 3.075 22.078 3.267 22.196 3.454 c
+22.321 3.638 22.483 3.785 22.68 3.896 c
+22.886 4.002 23.114 4.056 23.372 4.056 c
+23.772 4.056 24.077 3.954 24.283 3.748 c
+24.496 3.543 24.61 3.248 24.621 2.866 c
+24.621 0.852 l
+24.621 0.548 24.658 0.283 24.739 0.058 c
+24.739 0 l
+h
+23.195 0.515 m
+23.36 0.515 23.511 0.559 23.651 0.646 c
+23.798 0.735 23.904 0.845 23.974 0.985 c
+23.974 1.926 l
+23.607 1.926 l
+23.291 1.926 23.048 1.856 22.872 1.72 c
+22.695 1.591 22.607 1.404 22.607 1.161 c
+22.607 0.933 22.651 0.768 22.74 0.661 c
+22.828 0.563 22.979 0.515 23.195 0.515 c
+25.488 2.175 m
+25.488 2.782 25.598 3.248 25.826 3.572 c
+26.061 3.896 26.389 4.056 26.811 4.056 c
+27.193 4.056 27.491 3.898 27.708 3.586 c
+27.708 5.644 l
+28.355 5.644 l
+28.355 0 l
+27.767 0 l
+27.723 0.426 l
+27.517 0.092 27.212 -0.073 26.811 -0.073 c
+26.4 -0.073 26.076 0.081 25.841 0.397 c
+25.606 0.721 25.488 1.176 25.488 1.764 c
+h
+26.135 1.793 m
+26.135 1.352 26.198 1.022 26.326 0.808 c
+26.462 0.603 26.682 0.5 26.988 0.5 c
+27.311 0.5 27.55 0.661 27.708 0.985 c
+27.708 2.998 l
+27.539 3.31 27.3 3.469 26.988 3.469 c
+26.682 3.469 26.462 3.366 26.326 3.16 c
+26.198 2.955 26.135 2.631 26.135 2.19 c
+h
+31.295 1.014 m
+31.295 1.161 31.239 1.282 31.133 1.382 c
+31.023 1.477 30.817 1.595 30.516 1.735 c
+30.17 1.881 29.928 2.003 29.78 2.102 c
+29.633 2.209 29.523 2.326 29.457 2.454 c
+29.387 2.58 29.354 2.738 29.354 2.925 c
+29.354 3.248 29.471 3.516 29.707 3.734 c
+29.942 3.946 30.244 4.056 30.618 4.056 c
+31 4.056 31.309 3.943 31.544 3.719 c
+31.779 3.491 31.897 3.204 31.897 2.851 c
+31.25 2.851 l
+31.25 3.028 31.191 3.179 31.074 3.308 c
+30.956 3.432 30.802 3.499 30.618 3.499 c
+30.42 3.499 30.269 3.443 30.163 3.337 c
+30.052 3.237 30.001 3.105 30.001 2.94 c
+30.001 2.811 30.038 2.705 30.119 2.616 c
+30.196 2.535 30.387 2.433 30.692 2.308 c
+31.17 2.12 31.5 1.933 31.677 1.749 c
+31.852 1.573 31.941 1.345 31.941 1.073 c
+31.941 0.721 31.816 0.441 31.574 0.235 c
+31.339 0.029 31.023 -0.073 30.633 -0.073 c
+30.21 -0.073 29.872 0.044 29.618 0.279 c
+29.361 0.522 29.236 0.827 29.236 1.191 c
+29.884 1.191 l
+29.891 0.962 29.961 0.786 30.089 0.661 c
+30.214 0.544 30.398 0.485 30.633 0.485 c
+30.846 0.485 31.008 0.532 31.118 0.632 c
+31.235 0.727 31.295 0.856 31.295 1.014 c
+36.542 0 m
+36.502 0.088 36.476 0.235 36.469 0.441 c
+36.234 0.096 35.939 -0.073 35.586 -0.073 c
+35.222 -0.073 34.94 0.022 34.734 0.22 c
+34.535 0.426 34.44 0.713 34.44 1.088 c
+34.44 1.488 34.576 1.808 34.852 2.043 c
+35.123 2.286 35.498 2.41 35.968 2.41 c
+36.454 2.41 l
+36.454 2.837 l
+36.454 3.072 36.398 3.237 36.292 3.337 c
+36.182 3.443 36.02 3.499 35.807 3.499 c
+35.609 3.499 35.447 3.439 35.322 3.322 c
+35.204 3.204 35.145 3.057 35.145 2.881 c
+34.499 2.881 l
+34.499 3.075 34.557 3.267 34.675 3.454 c
+34.8 3.638 34.962 3.785 35.16 3.896 c
+35.366 4.002 35.594 4.056 35.851 4.056 c
+36.251 4.056 36.556 3.954 36.762 3.748 c
+36.976 3.543 37.09 3.248 37.101 2.866 c
+37.101 0.852 l
+37.101 0.548 37.138 0.283 37.218 0.058 c
+37.218 0 l
+h
+35.675 0.515 m
+35.84 0.515 35.991 0.559 36.13 0.646 c
+36.278 0.735 36.384 0.845 36.454 0.985 c
+36.454 1.926 l
+36.086 1.926 l
+35.771 1.926 35.528 1.856 35.351 1.72 c
+35.175 1.591 35.087 1.404 35.087 1.161 c
+35.087 0.933 35.131 0.768 35.219 0.661 c
+35.307 0.563 35.457 0.515 35.675 0.515 c
+42.701 1.793 m
+42.701 1.165 42.584 0.694 42.348 0.382 c
+42.121 0.077 41.805 -0.073 41.393 -0.073 c
+40.988 -0.073 40.679 0.077 40.467 0.382 c
+40.467 -1.529 l
+39.82 -1.529 l
+39.82 3.983 l
+40.408 3.983 l
+40.452 3.543 l
+40.665 3.884 40.974 4.056 41.377 4.056 c
+41.819 4.056 42.146 3.902 42.362 3.601 c
+42.576 3.296 42.69 2.84 42.701 2.234 c
+h
+42.054 2.175 m
+42.054 2.616 41.984 2.94 41.848 3.146 c
+41.709 3.358 41.488 3.469 41.187 3.469 c
+40.87 3.469 40.631 3.314 40.467 3.013 c
+40.467 0.941 l
+40.631 0.636 40.87 0.485 41.187 0.485 c
+41.481 0.485 41.694 0.588 41.834 0.794 c
+41.969 1.007 42.043 1.338 42.054 1.779 c
+h
+45.185 3.366 m
+45.097 3.385 44.997 3.395 44.891 3.395 c
+44.556 3.395 44.321 3.212 44.186 2.851 c
+44.186 0 l
+43.538 0 l
+43.538 3.983 l
+44.171 3.983 l
+44.186 3.572 l
+44.362 3.896 44.604 4.056 44.92 4.056 c
+45.027 4.056 45.115 4.035 45.185 3.998 c
+h
+45.626 2.175 m
+45.626 2.753 45.762 3.208 46.038 3.543 c
+46.32 3.884 46.692 4.056 47.155 4.056 c
+47.614 4.056 47.981 3.888 48.257 3.557 c
+48.54 3.233 48.687 2.786 48.698 2.219 c
+48.698 1.793 l
+48.698 1.224 48.554 0.768 48.272 0.426 c
+47.997 0.092 47.629 -0.073 47.17 -0.073 c
+46.707 -0.073 46.335 0.088 46.052 0.411 c
+45.776 0.742 45.633 1.183 45.626 1.735 c
+h
+46.273 1.793 m
+46.273 1.389 46.35 1.073 46.508 0.838 c
+46.673 0.603 46.894 0.485 47.17 0.485 c
+47.735 0.485 48.029 0.897 48.051 1.72 c
+48.051 2.175 l
+48.051 2.576 47.966 2.896 47.802 3.131 c
+47.644 3.373 47.426 3.499 47.155 3.499 c
+46.89 3.499 46.673 3.373 46.508 3.131 c
+46.35 2.896 46.273 2.576 46.273 2.175 c
+h
+50.197 3.983 m
+50.197 -0.5 l
+50.197 -1.235 49.907 -1.602 49.33 -1.602 c
+49.19 -1.602 49.069 -1.58 48.963 -1.544 c
+48.963 -1 l
+49.032 -1.018 49.117 -1.029 49.227 -1.029 c
+49.333 -1.029 49.411 -0.985 49.462 -0.897 c
+49.521 -0.816 49.551 -0.676 49.551 -0.47 c
+49.551 3.983 l
+h
+50.227 5.027 m
+50.227 4.917 50.197 4.825 50.139 4.748 c
+50.079 4.678 49.984 4.644 49.859 4.644 c
+49.742 4.644 49.647 4.678 49.58 4.748 c
+49.521 4.825 49.491 4.917 49.491 5.027 c
+49.491 5.145 49.521 5.236 49.58 5.307 c
+49.647 5.384 49.742 5.424 49.859 5.424 c
+49.984 5.424 50.079 5.384 50.139 5.307 c
+50.197 5.226 50.227 5.134 50.227 5.027 c
+52.652 -0.073 m
+52.153 -0.073 51.77 0.073 51.505 0.368 c
+51.241 0.661 51.108 1.095 51.108 1.675 c
+51.108 2.146 l
+51.108 2.741 51.234 3.208 51.49 3.543 c
+51.756 3.884 52.116 4.056 52.579 4.056 c
+53.038 4.056 53.379 3.902 53.607 3.601 c
+53.842 3.308 53.964 2.844 53.975 2.219 c
+53.975 1.793 l
+51.756 1.793 l
+51.756 1.705 l
+51.756 1.272 51.833 0.959 51.991 0.764 c
+52.156 0.577 52.388 0.485 52.681 0.485 c
+52.876 0.485 53.049 0.518 53.196 0.588 c
+53.343 0.665 53.479 0.783 53.607 0.941 c
+53.946 0.529 l
+53.659 0.125 53.229 -0.073 52.652 -0.073 c
+52.579 3.499 m
+52.303 3.499 52.101 3.403 51.976 3.219 c
+51.847 3.032 51.774 2.741 51.756 2.352 c
+53.329 2.352 l
+53.329 2.44 l
+53.306 2.822 53.24 3.09 53.123 3.248 c
+53.005 3.414 52.822 3.499 52.579 3.499 c
+56.136 0.485 m
+56.349 0.485 56.521 0.548 56.65 0.676 c
+56.786 0.812 56.859 1.003 56.871 1.249 c
+57.488 1.249 l
+57.466 0.867 57.33 0.548 57.077 0.294 c
+56.82 0.048 56.507 -0.073 56.136 -0.073 c
+55.644 -0.073 55.268 0.077 55.004 0.382 c
+54.746 0.694 54.622 1.161 54.622 1.779 c
+54.622 2.219 l
+54.622 2.815 54.746 3.271 55.004 3.586 c
+55.268 3.898 55.644 4.056 56.136 4.056 c
+56.537 4.056 56.856 3.925 57.092 3.66 c
+57.333 3.403 57.466 3.057 57.488 2.616 c
+56.871 2.616 l
+56.849 2.911 56.775 3.131 56.65 3.278 c
+56.533 3.425 56.36 3.499 56.136 3.499 c
+55.841 3.499 55.625 3.399 55.489 3.204 c
+55.349 3.017 55.276 2.708 55.268 2.278 c
+55.268 1.764 l
+55.268 1.294 55.334 0.959 55.474 0.764 c
+55.621 0.577 55.841 0.485 56.136 0.485 c
+59.091 4.939 m
+59.091 3.983 l
+59.693 3.983 l
+59.693 3.454 l
+59.091 3.454 l
+59.091 0.985 l
+59.091 0.827 59.112 0.709 59.164 0.632 c
+59.222 0.551 59.311 0.515 59.428 0.515 c
+59.517 0.515 59.604 0.529 59.693 0.559 c
+59.693 0 l
+59.546 -0.048 59.392 -0.073 59.237 -0.073 c
+58.98 -0.073 58.785 0.018 58.649 0.206 c
+58.509 0.389 58.443 0.65 58.443 0.985 c
+58.443 3.454 l
+57.84 3.454 l
+57.84 3.983 l
+58.443 3.983 l
+58.443 4.939 l
+h
+65.146 1.147 m
+65.749 3.983 l
+66.396 3.983 l
+65.411 0 l
+64.896 0 l
+64.117 2.851 l
+63.367 0 l
+62.839 0 l
+61.883 3.983 l
+62.515 3.983 l
+63.132 1.22 l
+63.868 3.983 l
+64.382 3.983 l
+h
+67.777 0 -0.647 3.983 re
+67.822 5.027 m
+67.822 4.917 67.792 4.825 67.733 4.748 c
+67.675 4.678 67.579 4.644 67.454 4.644 c
+67.336 4.644 67.241 4.678 67.174 4.748 c
+67.116 4.825 67.087 4.917 67.087 5.027 c
+67.087 5.145 67.116 5.236 67.174 5.307 c
+67.241 5.384 67.336 5.424 67.454 5.424 c
+67.579 5.424 67.675 5.384 67.733 5.307 c
+67.792 5.226 67.822 5.134 67.822 5.027 c
+69.644 4.939 m
+69.644 3.983 l
+70.247 3.983 l
+70.247 3.454 l
+69.644 3.454 l
+69.644 0.985 l
+69.644 0.827 69.666 0.709 69.717 0.632 c
+69.777 0.551 69.865 0.515 69.983 0.515 c
+70.07 0.515 70.158 0.529 70.247 0.559 c
+70.247 0 l
+70.1 -0.048 69.946 -0.073 69.792 -0.073 c
+69.534 -0.073 69.339 0.018 69.204 0.206 c
+69.063 0.389 68.998 0.65 68.998 0.985 c
+68.998 3.454 l
+68.395 3.454 l
+68.395 3.983 l
+68.998 3.983 l
+68.998 4.939 l
+h
+71.658 3.572 m
+71.911 3.896 72.231 4.056 72.614 4.056 c
+73.319 4.056 73.676 3.586 73.686 2.645 c
+73.686 0 l
+73.04 0 l
+73.04 2.616 l
+73.04 2.929 72.984 3.15 72.878 3.278 c
+72.768 3.403 72.614 3.469 72.408 3.469 c
+72.25 3.469 72.103 3.414 71.967 3.308 c
+71.838 3.197 71.735 3.061 71.658 2.896 c
+71.658 0 l
+71.011 0 l
+71.011 5.644 l
+71.658 5.644 l
+h
+77.788 -0.073 m
+77.287 -0.073 76.905 0.073 76.641 0.368 c
+76.377 0.661 76.244 1.095 76.244 1.675 c
+76.244 2.146 l
+76.244 2.741 76.369 3.208 76.626 3.543 c
+76.89 3.884 77.251 4.056 77.714 4.056 c
+78.174 4.056 78.515 3.902 78.743 3.601 c
+78.978 3.308 79.099 2.844 79.111 2.219 c
+79.111 1.793 l
+76.89 1.793 l
+76.89 1.705 l
+76.89 1.272 76.968 0.959 77.126 0.764 c
+77.291 0.577 77.523 0.485 77.817 0.485 c
+78.012 0.485 78.184 0.518 78.332 0.588 c
+78.478 0.665 78.615 0.783 78.743 0.941 c
+79.081 0.529 l
+78.794 0.125 78.365 -0.073 77.788 -0.073 c
+77.714 3.499 m
+77.439 3.499 77.237 3.403 77.111 3.219 c
+76.983 3.032 76.909 2.741 76.89 2.352 c
+78.463 2.352 l
+78.463 2.44 l
+78.442 2.822 78.376 3.09 78.258 3.248 c
+78.141 3.414 77.956 3.499 77.714 3.499 c
+80.507 3.983 m
+80.522 3.543 l
+80.775 3.884 81.098 4.056 81.492 4.056 c
+82.197 4.056 82.554 3.586 82.565 2.645 c
+82.565 0 l
+81.918 0 l
+81.918 2.616 l
+81.918 2.929 81.863 3.15 81.756 3.278 c
+81.646 3.403 81.492 3.469 81.286 3.469 c
+81.127 3.469 80.981 3.414 80.845 3.308 c
+80.716 3.197 80.613 3.061 80.536 2.896 c
+80.536 0 l
+79.889 0 l
+79.889 3.983 l
+h
+84.387 4.939 m
+84.387 3.983 l
+84.99 3.983 l
+84.99 3.454 l
+84.387 3.454 l
+84.387 0.985 l
+84.387 0.827 84.41 0.709 84.461 0.632 c
+84.52 0.551 84.608 0.515 84.726 0.515 c
+84.813 0.515 84.902 0.529 84.99 0.559 c
+84.99 0 l
+84.843 -0.048 84.689 -0.073 84.534 -0.073 c
+84.277 -0.073 84.082 0.018 83.946 0.206 c
+83.807 0.389 83.741 0.65 83.741 0.985 c
+83.741 3.454 l
+83.138 3.454 l
+83.138 3.983 l
+83.741 3.983 l
+83.741 4.939 l
+h
+86.46 0 -0.646 3.983 re
+86.504 5.027 m
+86.504 4.917 86.475 4.825 86.415 4.748 c
+86.357 4.678 86.261 4.644 86.137 4.644 c
+86.019 4.644 85.923 4.678 85.858 4.748 c
+85.798 4.825 85.769 4.917 85.769 5.027 c
+85.769 5.145 85.798 5.236 85.858 5.307 c
+85.923 5.384 86.019 5.424 86.137 5.424 c
+86.261 5.424 86.357 5.384 86.415 5.307 c
+86.475 5.226 86.504 5.134 86.504 5.027 c
+89.12 3.366 m
+89.033 3.385 88.933 3.395 88.826 3.395 c
+88.493 3.395 88.257 3.212 88.121 2.851 c
+88.121 0 l
+87.474 0 l
+87.474 3.983 l
+88.106 3.983 l
+88.121 3.572 l
+88.297 3.896 88.54 4.056 88.856 4.056 c
+88.963 4.056 89.05 4.035 89.12 3.998 c
+h
+91.119 -0.073 m
+90.62 -0.073 90.238 0.073 89.973 0.368 c
+89.708 0.661 89.576 1.095 89.576 1.675 c
+89.576 2.146 l
+89.576 2.741 89.701 3.208 89.958 3.543 c
+90.223 3.884 90.583 4.056 91.046 4.056 c
+91.506 4.056 91.847 3.902 92.075 3.601 c
+92.31 3.308 92.431 2.844 92.443 2.219 c
+92.443 1.793 l
+90.223 1.793 l
+90.223 1.705 l
+90.223 1.272 90.3 0.959 90.458 0.764 c
+90.623 0.577 90.855 0.485 91.149 0.485 c
+91.344 0.485 91.516 0.518 91.664 0.588 c
+91.811 0.665 91.946 0.783 92.075 0.941 c
+92.413 0.529 l
+92.127 0.125 91.697 -0.073 91.119 -0.073 c
+91.046 3.499 m
+90.77 3.499 90.569 3.403 90.444 3.219 c
+90.315 3.032 90.241 2.741 90.223 2.352 c
+91.795 2.352 l
+91.795 2.44 l
+91.774 2.822 91.707 3.09 91.589 3.248 c
+91.472 3.414 91.288 3.499 91.046 3.499 c
+95.588 3.572 m
+95.842 3.896 96.161 4.056 96.543 4.056 c
+97.249 4.056 97.605 3.586 97.617 2.645 c
+97.617 0 l
+96.97 0 l
+96.97 2.616 l
+96.97 2.929 96.915 3.15 96.808 3.278 c
+96.698 3.403 96.543 3.469 96.337 3.469 c
+96.179 3.469 96.033 3.414 95.897 3.308 c
+95.768 3.197 95.665 3.061 95.588 2.896 c
+95.588 0 l
+94.941 0 l
+94.941 5.644 l
+95.588 5.644 l
+h
+99.277 0 -0.646 3.983 re
+99.321 5.027 m
+99.321 4.917 99.292 4.825 99.233 4.748 c
+99.175 4.678 99.079 4.644 98.954 4.644 c
+98.837 4.644 98.741 4.678 98.675 4.748 c
+98.616 4.825 98.587 4.917 98.587 5.027 c
+98.587 5.145 98.616 5.236 98.675 5.307 c
+98.741 5.384 98.837 5.424 98.954 5.424 c
+99.079 5.424 99.175 5.384 99.233 5.307 c
+99.292 5.226 99.321 5.134 99.321 5.027 c
+102.232 1.014 m
+102.232 1.161 102.177 1.282 102.07 1.382 c
+101.96 1.477 101.754 1.595 101.453 1.735 c
+101.108 1.881 100.865 2.003 100.718 2.102 c
+100.571 2.209 100.461 2.326 100.395 2.454 c
+100.325 2.58 100.291 2.738 100.291 2.925 c
+100.291 3.248 100.409 3.516 100.644 3.734 c
+100.879 3.946 101.181 4.056 101.556 4.056 c
+101.938 4.056 102.247 3.943 102.482 3.719 c
+102.717 3.491 102.835 3.204 102.835 2.851 c
+102.188 2.851 l
+102.188 3.028 102.129 3.179 102.012 3.308 c
+101.894 3.432 101.74 3.499 101.556 3.499 c
+101.357 3.499 101.207 3.443 101.1 3.337 c
+100.99 3.237 100.939 3.105 100.939 2.94 c
+100.939 2.811 100.975 2.705 101.056 2.616 c
+101.133 2.535 101.324 2.433 101.629 2.308 c
+102.107 2.12 102.438 1.933 102.614 1.749 c
+102.791 1.573 102.878 1.345 102.878 1.073 c
+102.878 0.721 102.754 0.441 102.511 0.235 c
+102.276 0.029 101.96 -0.073 101.571 -0.073 c
+101.148 -0.073 100.81 0.044 100.557 0.279 c
+100.299 0.522 100.174 0.827 100.174 1.191 c
+100.821 1.191 l
+100.828 0.962 100.898 0.786 101.027 0.661 c
+101.152 0.544 101.335 0.485 101.571 0.485 c
+101.783 0.485 101.945 0.532 102.055 0.632 c
+102.173 0.727 102.232 0.856 102.232 1.014 c
+104.569 4.939 m
+104.569 3.983 l
+105.172 3.983 l
+105.172 3.454 l
+104.569 3.454 l
+104.569 0.985 l
+104.569 0.827 104.591 0.709 104.642 0.632 c
+104.701 0.551 104.79 0.515 104.907 0.515 c
+104.995 0.515 105.083 0.529 105.172 0.559 c
+105.172 0 l
+105.025 -0.048 104.871 -0.073 104.717 -0.073 c
+104.459 -0.073 104.264 0.018 104.129 0.206 c
+103.988 0.389 103.923 0.65 103.923 0.985 c
+103.923 3.454 l
+103.32 3.454 l
+103.32 3.983 l
+103.923 3.983 l
+103.923 4.939 l
+h
+105.731 2.175 m
+105.731 2.753 105.866 3.208 106.142 3.543 c
+106.425 3.884 106.796 4.056 107.259 4.056 c
+107.718 4.056 108.086 3.888 108.362 3.557 c
+108.644 3.233 108.792 2.786 108.802 2.219 c
+108.802 1.793 l
+108.802 1.224 108.659 0.768 108.376 0.426 c
+108.1 0.092 107.733 -0.073 107.273 -0.073 c
+106.811 -0.073 106.44 0.088 106.157 0.411 c
+105.881 0.742 105.738 1.183 105.731 1.735 c
+h
+106.377 1.793 m
+106.377 1.389 106.454 1.073 106.612 0.838 c
+106.778 0.603 106.998 0.485 107.273 0.485 c
+107.84 0.485 108.133 0.897 108.156 1.72 c
+108.156 2.175 l
+108.156 2.576 108.071 2.896 107.906 3.131 c
+107.748 3.373 107.531 3.499 107.259 3.499 c
+106.994 3.499 106.778 3.373 106.612 3.131 c
+106.454 2.896 106.377 2.576 106.377 2.175 c
+h
+111.287 3.366 m
+111.198 3.385 111.099 3.395 110.992 3.395 c
+110.658 3.395 110.423 3.212 110.287 2.851 c
+110.287 0 l
+109.64 0 l
+109.64 3.983 l
+110.273 3.983 l
+110.287 3.572 l
+110.463 3.896 110.706 4.056 111.022 4.056 c
+111.129 4.056 111.217 4.035 111.287 3.998 c
+h
+113.095 1.088 m
+113.814 3.983 l
+114.506 3.983 l
+113.212 -0.559 l
+113.113 -0.9 112.97 -1.161 112.786 -1.338 c
+112.609 -1.514 112.407 -1.602 112.183 -1.602 c
+112.095 -1.602 111.981 -1.58 111.845 -1.544 c
+111.845 -1 l
+111.992 -1.014 l
+112.176 -1.014 112.323 -0.97 112.433 -0.882 c
+112.54 -0.794 112.628 -0.636 112.698 -0.412 c
+112.815 0.029 l
+111.654 3.983 l
+112.36 3.983 l
+h
+117.151 0 m
+117.151 3.454 l
+116.623 3.454 l
+116.623 3.983 l
+117.151 3.983 l
+117.151 4.439 l
+117.151 4.839 117.247 5.152 117.446 5.38 c
+117.652 5.604 117.93 5.718 118.283 5.718 c
+118.419 5.718 118.552 5.696 118.68 5.66 c
+118.651 5.115 l
+118.552 5.134 118.452 5.145 118.357 5.145 c
+117.982 5.145 117.798 4.88 117.798 4.351 c
+117.798 3.983 l
+118.475 3.983 l
+118.475 3.454 l
+117.798 3.454 l
+117.798 0 l
+h
+120.885 3.366 m
+120.797 3.385 120.698 3.395 120.592 3.395 c
+120.257 3.395 120.021 3.212 119.886 2.851 c
+119.886 0 l
+119.239 0 l
+119.239 3.983 l
+119.871 3.983 l
+119.886 3.572 l
+120.062 3.896 120.305 4.056 120.621 4.056 c
+120.727 4.056 120.815 4.035 120.885 3.998 c
+h
+121.326 2.175 m
+121.326 2.753 121.462 3.208 121.737 3.543 c
+122.02 3.884 122.392 4.056 122.855 4.056 c
+123.314 4.056 123.682 3.888 123.957 3.557 c
+124.241 3.233 124.387 2.786 124.398 2.219 c
+124.398 1.793 l
+124.398 1.224 124.255 0.768 123.972 0.426 c
+123.696 0.092 123.329 -0.073 122.869 -0.073 c
+122.406 -0.073 122.036 0.088 121.752 0.411 c
+121.477 0.742 121.334 1.183 121.326 1.735 c
+h
+121.972 1.793 m
+121.972 1.389 122.05 1.073 122.208 0.838 c
+122.373 0.603 122.594 0.485 122.869 0.485 c
+123.435 0.485 123.73 0.897 123.751 1.72 c
+123.751 2.175 l
+123.751 2.576 123.667 2.896 123.501 3.131 c
+123.343 3.373 123.127 3.499 122.855 3.499 c
+122.591 3.499 122.373 3.373 122.208 3.131 c
+122.05 2.896 121.972 2.576 121.972 2.175 c
+h
+125.853 3.983 m
+125.868 3.616 l
+126.111 3.91 126.431 4.056 126.824 4.056 c
+127.264 4.056 127.573 3.859 127.75 3.469 c
+128.004 3.859 128.353 4.056 128.793 4.056 c
+129.529 4.056 129.903 3.594 129.925 2.675 c
+129.925 0 l
+129.278 0 l
+129.278 2.616 l
+129.278 2.911 129.223 3.123 129.116 3.263 c
+129.018 3.399 128.845 3.469 128.602 3.469 c
+128.403 3.469 128.242 3.389 128.117 3.233 c
+128 3.087 127.929 2.896 127.911 2.66 c
+127.911 0 l
+127.25 0 l
+127.25 2.645 l
+127.25 3.193 127.029 3.469 126.589 3.469 c
+126.254 3.469 126.019 3.308 125.883 2.984 c
+125.883 0 l
+125.236 0 l
+125.236 3.983 l
+h
+133.467 4.939 m
+133.467 3.983 l
+134.07 3.983 l
+134.07 3.454 l
+133.467 3.454 l
+133.467 0.985 l
+133.467 0.827 133.49 0.709 133.541 0.632 c
+133.6 0.551 133.688 0.515 133.805 0.515 c
+133.894 0.515 133.982 0.529 134.07 0.559 c
+134.07 0 l
+133.923 -0.048 133.768 -0.073 133.614 -0.073 c
+133.357 -0.073 133.163 0.018 133.026 0.206 c
+132.887 0.389 132.821 0.65 132.821 0.985 c
+132.821 3.454 l
+132.218 3.454 l
+132.218 3.983 l
+132.821 3.983 l
+132.821 4.939 l
+h
+135.482 3.572 m
+135.735 3.896 136.054 4.056 136.436 4.056 c
+137.142 4.056 137.498 3.586 137.51 2.645 c
+137.51 0 l
+136.863 0 l
+136.863 2.616 l
+136.863 2.929 136.808 3.15 136.702 3.278 c
+136.591 3.403 136.436 3.469 136.231 3.469 c
+136.073 3.469 135.926 3.414 135.79 3.308 c
+135.661 3.197 135.559 3.061 135.482 2.896 c
+135.482 0 l
+134.834 0 l
+134.834 5.644 l
+135.482 5.644 l
+h
+139.891 -0.073 m
+139.391 -0.073 139.009 0.073 138.744 0.368 c
+138.48 0.661 138.348 1.095 138.348 1.675 c
+138.348 2.146 l
+138.348 2.741 138.472 3.208 138.73 3.543 c
+138.994 3.884 139.355 4.056 139.817 4.056 c
+140.276 4.056 140.619 3.902 140.847 3.601 c
+141.082 3.308 141.203 2.844 141.213 2.219 c
+141.213 1.793 l
+138.994 1.793 l
+138.994 1.705 l
+138.994 1.272 139.071 0.959 139.229 0.764 c
+139.395 0.577 139.626 0.485 139.92 0.485 c
+140.115 0.485 140.288 0.518 140.435 0.588 c
+140.582 0.665 140.718 0.783 140.847 0.941 c
+141.184 0.529 l
+140.898 0.125 140.468 -0.073 139.891 -0.073 c
+139.817 3.499 m
+139.542 3.499 139.339 3.403 139.214 3.219 c
+139.086 3.032 139.013 2.741 138.994 2.352 c
+140.567 2.352 l
+140.567 2.44 l
+140.546 2.822 140.479 3.09 140.361 3.248 c
+140.244 3.414 140.06 3.499 139.817 3.499 c
+145.359 3.366 m
+145.271 3.385 145.172 3.395 145.065 3.395 c
+144.731 3.395 144.496 3.212 144.359 2.851 c
+144.359 0 l
+143.713 0 l
+143.713 3.983 l
+144.345 3.983 l
+144.359 3.572 l
+144.536 3.896 144.779 4.056 145.094 4.056 c
+145.201 4.056 145.289 4.035 145.359 3.998 c
+h
+147.358 -0.073 m
+146.858 -0.073 146.476 0.073 146.212 0.368 c
+145.947 0.661 145.815 1.095 145.815 1.675 c
+145.815 2.146 l
+145.815 2.741 145.94 3.208 146.197 3.543 c
+146.462 3.884 146.821 4.056 147.285 4.056 c
+147.744 4.056 148.086 3.902 148.313 3.601 c
+148.548 3.308 148.67 2.844 148.681 2.219 c
+148.681 1.793 l
+146.462 1.793 l
+146.462 1.705 l
+146.462 1.272 146.539 0.959 146.697 0.764 c
+146.862 0.577 147.093 0.485 147.388 0.485 c
+147.582 0.485 147.755 0.518 147.902 0.588 c
+148.049 0.665 148.185 0.783 148.313 0.941 c
+148.652 0.529 l
+148.365 0.125 147.935 -0.073 147.358 -0.073 c
+147.285 3.499 m
+147.009 3.499 146.807 3.403 146.682 3.219 c
+146.553 3.032 146.48 2.741 146.462 2.352 c
+148.034 2.352 l
+148.034 2.44 l
+148.012 2.822 147.946 3.09 147.829 3.248 c
+147.711 3.414 147.527 3.499 147.285 3.499 c
+150.077 3.983 m
+150.092 3.616 l
+150.335 3.91 150.655 4.056 151.048 4.056 c
+151.488 4.056 151.797 3.859 151.974 3.469 c
+152.228 3.859 152.577 4.056 153.017 4.056 c
+153.753 4.056 154.127 3.594 154.149 2.675 c
+154.149 0 l
+153.502 0 l
+153.502 2.616 l
+153.502 2.911 153.447 3.123 153.341 3.263 c
+153.242 3.399 153.069 3.469 152.826 3.469 c
+152.627 3.469 152.466 3.389 152.341 3.233 c
+152.224 3.087 152.153 2.896 152.135 2.66 c
+152.135 0 l
+151.474 0 l
+151.474 2.645 l
+151.474 3.193 151.253 3.469 150.813 3.469 c
+150.478 3.469 150.243 3.308 150.107 2.984 c
+150.107 0 l
+149.46 0 l
+149.46 3.983 l
+h
+154.987 2.175 m
+154.987 2.753 155.123 3.208 155.399 3.543 c
+155.681 3.884 156.053 4.056 156.516 4.056 c
+156.975 4.056 157.342 3.888 157.618 3.557 c
+157.901 3.233 158.048 2.786 158.059 2.219 c
+158.059 1.793 l
+158.059 1.224 157.915 0.768 157.633 0.426 c
+157.357 0.092 156.99 -0.073 156.531 -0.073 c
+156.068 -0.073 155.696 0.088 155.413 0.411 c
+155.137 0.742 154.994 1.183 154.987 1.735 c
+h
+155.634 1.793 m
+155.634 1.389 155.711 1.073 155.869 0.838 c
+156.034 0.603 156.255 0.485 156.531 0.485 c
+157.096 0.485 157.39 0.897 157.412 1.72 c
+157.412 2.175 l
+157.412 2.576 157.327 2.896 157.163 3.131 c
+157.005 3.373 156.787 3.499 156.516 3.499 c
+156.251 3.499 156.034 3.373 155.869 3.131 c
+155.711 2.896 155.634 2.576 155.634 2.175 c
+h
+159.75 4.939 m
+159.75 3.983 l
+160.352 3.983 l
+160.352 3.454 l
+159.75 3.454 l
+159.75 0.985 l
+159.75 0.827 159.771 0.709 159.823 0.632 c
+159.881 0.551 159.97 0.515 160.087 0.515 c
+160.176 0.515 160.263 0.529 160.352 0.559 c
+160.352 0 l
+160.205 -0.048 160.051 -0.073 159.897 -0.073 c
+159.639 -0.073 159.444 0.018 159.309 0.206 c
+159.168 0.389 159.103 0.65 159.103 0.985 c
+159.103 3.454 l
+158.5 3.454 l
+158.5 3.983 l
+159.103 3.983 l
+159.103 4.939 l
+h
+162.542 -0.073 m
+162.042 -0.073 161.66 0.073 161.396 0.368 c
+161.131 0.661 160.999 1.095 160.999 1.675 c
+160.999 2.146 l
+160.999 2.741 161.124 3.208 161.381 3.543 c
+161.645 3.884 162.006 4.056 162.469 4.056 c
+162.928 4.056 163.27 3.902 163.498 3.601 c
+163.733 3.308 163.854 2.844 163.866 2.219 c
+163.866 1.793 l
+161.645 1.793 l
+161.645 1.705 l
+161.645 1.272 161.723 0.959 161.881 0.764 c
+162.046 0.577 162.278 0.485 162.572 0.485 c
+162.767 0.485 162.939 0.518 163.086 0.588 c
+163.233 0.665 163.369 0.783 163.498 0.941 c
+163.835 0.529 l
+163.549 0.125 163.12 -0.073 162.542 -0.073 c
+162.469 3.499 m
+162.193 3.499 161.991 3.403 161.866 3.219 c
+161.738 3.032 161.664 2.741 161.645 2.352 c
+163.218 2.352 l
+163.218 2.44 l
+163.197 2.822 163.13 3.09 163.012 3.248 c
+162.895 3.414 162.711 3.499 162.469 3.499 c
+168.01 3.366 m
+167.922 3.385 167.823 3.395 167.716 3.395 c
+167.382 3.395 167.147 3.212 167.01 2.851 c
+167.01 0 l
+166.364 0 l
+166.364 3.983 l
+166.996 3.983 l
+167.01 3.572 l
+167.187 3.896 167.43 4.056 167.746 4.056 c
+167.852 4.056 167.941 4.035 168.01 3.998 c
+h
+170.009 -0.073 m
+169.51 -0.073 169.127 0.073 168.863 0.368 c
+168.598 0.661 168.466 1.095 168.466 1.675 c
+168.466 2.146 l
+168.466 2.741 168.591 3.208 168.848 3.543 c
+169.113 3.884 169.473 4.056 169.936 4.056 c
+170.395 4.056 170.737 3.902 170.964 3.601 c
+171.2 3.308 171.321 2.844 171.332 2.219 c
+171.332 1.793 l
+169.113 1.793 l
+169.113 1.705 l
+169.113 1.272 169.19 0.959 169.348 0.764 c
+169.513 0.577 169.745 0.485 170.039 0.485 c
+170.233 0.485 170.406 0.518 170.553 0.588 c
+170.7 0.665 170.836 0.783 170.964 0.941 c
+171.303 0.529 l
+171.016 0.125 170.586 -0.073 170.009 -0.073 c
+169.936 3.499 m
+169.66 3.499 169.458 3.403 169.333 3.219 c
+169.204 3.032 169.131 2.741 169.113 2.352 c
+170.686 2.352 l
+170.686 2.44 l
+170.663 2.822 170.597 3.09 170.48 3.248 c
+170.362 3.414 170.179 3.499 169.936 3.499 c
+174.993 1.793 m
+174.993 1.165 174.875 0.694 174.64 0.382 c
+174.412 0.077 174.096 -0.073 173.684 -0.073 c
+173.279 -0.073 172.971 0.077 172.758 0.382 c
+172.758 -1.529 l
+172.111 -1.529 l
+172.111 3.983 l
+172.699 3.983 l
+172.743 3.543 l
+172.957 3.884 173.265 4.056 173.669 4.056 c
+174.11 4.056 174.438 3.902 174.654 3.601 c
+174.867 3.296 174.981 2.84 174.993 2.234 c
+h
+174.345 2.175 m
+174.345 2.616 174.276 2.94 174.139 3.146 c
+174 3.358 173.78 3.469 173.478 3.469 c
+173.162 3.469 172.924 3.314 172.758 3.013 c
+172.758 0.941 l
+172.924 0.636 173.162 0.485 173.478 0.485 c
+173.772 0.485 173.985 0.588 174.125 0.794 c
+174.261 1.007 174.335 1.338 174.345 1.779 c
+h
+175.698 2.175 m
+175.698 2.753 175.834 3.208 176.109 3.543 c
+176.392 3.884 176.764 4.056 177.227 4.056 c
+177.686 4.056 178.054 3.888 178.329 3.557 c
+178.612 3.233 178.759 2.786 178.77 2.219 c
+178.77 1.793 l
+178.77 1.224 178.627 0.768 178.343 0.426 c
+178.068 0.092 177.701 -0.073 177.241 -0.073 c
+176.778 -0.073 176.407 0.088 176.124 0.411 c
+175.849 0.742 175.706 1.183 175.698 1.735 c
+h
+176.344 1.793 m
+176.344 1.389 176.422 1.073 176.58 0.838 c
+176.745 0.603 176.965 0.485 177.241 0.485 c
+177.807 0.485 178.101 0.897 178.123 1.72 c
+178.123 2.175 l
+178.123 2.576 178.039 2.896 177.873 3.131 c
+177.715 3.373 177.499 3.499 177.227 3.499 c
+176.962 3.499 176.745 3.373 176.58 3.131 c
+176.422 2.896 176.344 2.576 176.344 2.175 c
+h
+181.548 1.014 m
+181.548 1.161 181.493 1.282 181.387 1.382 c
+181.276 1.477 181.071 1.595 180.769 1.735 c
+180.423 1.881 180.181 2.003 180.034 2.102 c
+179.887 2.209 179.777 2.326 179.71 2.454 c
+179.641 2.58 179.608 2.738 179.608 2.925 c
+179.608 3.248 179.725 3.516 179.961 3.734 c
+180.196 3.946 180.497 4.056 180.872 4.056 c
+181.254 4.056 181.563 3.943 181.798 3.719 c
+182.033 3.491 182.151 3.204 182.151 2.851 c
+181.504 2.851 l
+181.504 3.028 181.445 3.179 181.328 3.308 c
+181.21 3.432 181.056 3.499 180.872 3.499 c
+180.674 3.499 180.523 3.443 180.416 3.337 c
+180.306 3.237 180.254 3.105 180.254 2.94 c
+180.254 2.811 180.292 2.705 180.372 2.616 c
+180.45 2.535 180.641 2.433 180.946 2.308 c
+181.423 2.12 181.754 1.933 181.931 1.749 c
+182.106 1.573 182.195 1.345 182.195 1.073 c
+182.195 0.721 182.07 0.441 181.827 0.235 c
+181.592 0.029 181.276 -0.073 180.886 -0.073 c
+180.464 -0.073 180.126 0.044 179.872 0.279 c
+179.615 0.522 179.49 0.827 179.49 1.191 c
+180.137 1.191 l
+180.144 0.962 180.215 0.786 180.343 0.661 c
+180.468 0.544 180.651 0.485 180.886 0.485 c
+181.1 0.485 181.262 0.532 181.372 0.632 c
+181.489 0.727 181.548 0.856 181.548 1.014 c
+183.739 0 -0.647 3.983 re
+183.782 5.027 m
+183.782 4.917 183.753 4.825 183.694 4.748 c
+183.635 4.678 183.54 4.644 183.415 4.644 c
+183.297 4.644 183.201 4.678 183.136 4.748 c
+183.076 4.825 183.047 4.917 183.047 5.027 c
+183.047 5.145 183.076 5.236 183.136 5.307 c
+183.201 5.384 183.297 5.424 183.415 5.424 c
+183.54 5.424 183.635 5.384 183.694 5.307 c
+183.753 5.226 183.782 5.134 183.782 5.027 c
+185.605 4.939 m
+185.605 3.983 l
+186.208 3.983 l
+186.208 3.454 l
+185.605 3.454 l
+185.605 0.985 l
+185.605 0.827 185.627 0.709 185.678 0.632 c
+185.738 0.551 185.825 0.515 185.943 0.515 c
+186.031 0.515 186.12 0.529 186.208 0.559 c
+186.208 0 l
+186.06 -0.048 185.906 -0.073 185.752 -0.073 c
+185.495 -0.073 185.3 0.018 185.164 0.206 c
+185.025 0.389 184.958 0.65 184.958 0.985 c
+184.958 3.454 l
+184.356 3.454 l
+184.356 3.983 l
+184.958 3.983 l
+184.958 4.939 l
+h
+186.766 2.175 m
+186.766 2.753 186.902 3.208 187.178 3.543 c
+187.461 3.884 187.832 4.056 188.295 4.056 c
+188.755 4.056 189.122 3.888 189.397 3.557 c
+189.68 3.233 189.827 2.786 189.838 2.219 c
+189.838 1.793 l
+189.838 1.224 189.695 0.768 189.412 0.426 c
+189.137 0.092 188.769 -0.073 188.31 -0.073 c
+187.847 -0.073 187.475 0.088 187.192 0.411 c
+186.916 0.742 186.773 1.183 186.766 1.735 c
+h
+187.413 1.793 m
+187.413 1.389 187.49 1.073 187.648 0.838 c
+187.814 0.603 188.034 0.485 188.31 0.485 c
+188.876 0.485 189.17 0.897 189.191 1.72 c
+189.191 2.175 l
+189.191 2.576 189.107 2.896 188.942 3.131 c
+188.784 3.373 188.567 3.499 188.295 3.499 c
+188.03 3.499 187.814 3.373 187.648 3.131 c
+187.49 2.896 187.413 2.576 187.413 2.175 c
+h
+192.323 3.366 m
+192.234 3.385 192.135 3.395 192.028 3.395 c
+191.694 3.395 191.459 3.212 191.323 2.851 c
+191.323 0 l
+190.677 0 l
+190.677 3.983 l
+191.308 3.983 l
+191.323 3.572 l
+191.5 3.896 191.742 4.056 192.058 4.056 c
+192.165 4.056 192.252 4.035 192.323 3.998 c
+h
+194.13 1.088 m
+194.851 3.983 l
+195.541 3.983 l
+194.248 -0.559 l
+194.149 -0.9 194.006 -1.161 193.821 -1.338 c
+193.646 -1.514 193.443 -1.602 193.219 -1.602 c
+193.131 -1.602 193.017 -1.58 192.881 -1.544 c
+192.881 -1 l
+193.028 -1.014 l
+193.212 -1.014 193.359 -0.97 193.469 -0.882 c
+193.576 -0.794 193.663 -0.636 193.734 -0.412 c
+193.852 0.029 l
+192.69 3.983 l
+193.395 3.983 l
+h
+195.821 0.353 m
+195.821 0.47 195.853 0.566 195.924 0.646 c
+195.99 0.723 196.092 0.764 196.233 0.764 c
+196.379 0.764 196.486 0.723 196.556 0.646 c
+196.633 0.566 196.674 0.47 196.674 0.353 c
+196.674 0.243 196.633 0.151 196.556 0.073 c
+196.486 -0.004 196.379 -0.044 196.233 -0.044 c
+196.092 -0.044 195.99 -0.004 195.924 0.073 c
+195.853 0.151 195.821 0.243 195.821 0.353 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+287.665 442.028 234.667 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 400.8622 432.0517 cm
+0 0 m
+0.882 0 l
+1.213 0 1.459 0.118 1.617 0.353 c
+1.771 0.588 1.852 0.897 1.852 1.278 c
+1.852 1.661 1.768 1.955 1.602 2.161 c
+1.444 2.367 1.228 2.469 0.955 2.469 c
+0.709 2.469 0.503 2.37 0.338 2.175 c
+0.18 1.976 0.103 1.727 0.103 1.426 c
+-1.72 1.426 l
+-1.72 1.914 -1.61 2.352 -1.382 2.734 c
+-1.158 3.123 -0.846 3.428 -0.441 3.645 c
+-0.029 3.869 0.419 3.983 0.912 3.983 c
+1.771 3.983 2.447 3.74 2.94 3.263 c
+3.428 2.793 3.675 2.138 3.675 1.309 c
+3.675 0.885 3.561 0.492 3.337 0.133 c
+3.12 -0.231 2.83 -0.511 2.469 -0.706 c
+2.911 -0.893 3.237 -1.176 3.454 -1.558 c
+3.667 -1.933 3.777 -2.374 3.777 -2.882 c
+3.777 -3.715 3.513 -4.388 2.984 -4.895 c
+2.454 -5.394 1.756 -5.644 0.897 -5.644 c
+0.092 -5.644 -0.559 -5.402 -1.058 -4.91 c
+-1.558 -4.41 -1.808 -3.755 -1.808 -2.94 c
+0.014 -2.94 l
+0.014 -3.285 0.103 -3.568 0.279 -3.792 c
+0.455 -4.021 0.68 -4.131 0.955 -4.131 c
+1.268 -4.131 1.514 -4.021 1.691 -3.792 c
+1.874 -3.557 1.97 -3.245 1.97 -2.851 c
+1.97 -1.933 1.61 -1.47 0.897 -1.47 c
+0 -1.47 l
+h
+5.427 -4.586 m
+5.427 -4.293 5.523 -4.054 5.722 -3.866 c
+5.916 -3.682 6.169 -3.587 6.485 -3.587 c
+6.787 -3.587 7.037 -3.682 7.235 -3.866 c
+7.441 -4.054 7.544 -4.293 7.544 -4.586 c
+7.544 -4.891 7.441 -5.137 7.235 -5.321 c
+7.037 -5.498 6.787 -5.586 6.485 -5.586 c
+6.181 -5.586 5.928 -5.494 5.722 -5.307 c
+5.523 -5.122 5.427 -4.881 5.427 -4.586 c
+12.432 -5.512 m
+12.432 3.851 l
+14.915 3.851 l
+16.004 3.851 16.87 3.502 17.518 2.807 c
+18.171 2.109 18.506 1.161 18.517 -0.044 c
+18.517 -1.558 l
+18.517 -2.793 18.194 -3.763 17.547 -4.469 c
+16.9 -5.167 16.004 -5.512 14.857 -5.512 c
+h
+14.327 2.278 m
+14.327 -3.94 l
+14.886 -3.94 l
+15.522 -3.94 15.967 -3.774 16.224 -3.439 c
+16.477 -3.109 16.61 -2.535 16.621 -1.72 c
+16.621 -0.103 l
+16.621 0.779 16.496 1.389 16.253 1.735 c
+16.018 2.076 15.607 2.256 15.019 2.278 c
+h
+24.415 -3.601 m
+21.843 -3.601 l
+21.343 -5.512 l
+19.344 -5.512 l
+22.269 3.851 l
+23.989 3.851 l
+26.944 -5.512 l
+24.915 -5.512 l
+h
+22.254 -2.014 m
+24.003 -2.014 l
+23.121 1.323 l
+h
+29.886 -0.353 m
+31.297 3.851 l
+33.356 3.851 l
+30.842 -2.117 l
+30.842 -5.512 l
+28.932 -5.512 l
+28.932 -2.117 l
+26.418 3.851 l
+28.475 3.851 l
+h
+36.604 -2.263 -3.146 1.529 re
+44.406 2.278 m
+42.083 2.278 l
+42.083 -5.512 l
+40.187 -5.512 l
+40.187 2.278 l
+37.909 2.278 l
+37.909 3.851 l
+44.406 3.851 l
+h
+52.105 -1.675 m
+52.105 -2.932 51.803 -3.906 51.207 -4.601 c
+50.609 -5.299 49.786 -5.644 48.738 -5.644 c
+47.687 -5.644 46.86 -5.303 46.254 -4.615 c
+45.655 -3.921 45.35 -2.955 45.343 -1.72 c
+45.343 -0.118 l
+45.343 1.165 45.64 2.168 46.239 2.896 c
+46.835 3.62 47.665 3.983 48.724 3.983 c
+49.759 3.983 50.579 3.623 51.178 2.911 c
+51.785 2.205 52.093 1.209 52.105 -0.073 c
+h
+50.208 -0.103 m
+50.208 0.738 50.083 1.367 49.84 1.779 c
+49.605 2.19 49.231 2.396 48.724 2.396 c
+48.224 2.396 47.849 2.194 47.606 1.793 c
+47.371 1.4 47.247 0.801 47.239 0 c
+47.239 -1.675 l
+47.239 -2.492 47.361 -3.094 47.606 -3.484 c
+47.849 -3.877 48.227 -4.072 48.738 -4.072 c
+49.227 -4.072 49.591 -3.881 49.826 -3.499 c
+50.069 -3.117 50.197 -2.529 50.208 -1.735 c
+h
+56.871 -2.263 -3.146 1.529 re
+58.601 -5.512 m
+58.601 3.851 l
+61.085 3.851 l
+62.173 3.851 63.041 3.502 63.687 2.807 c
+64.341 2.109 64.676 1.161 64.687 -0.044 c
+64.687 -1.558 l
+64.687 -2.793 64.364 -3.763 63.716 -4.469 c
+63.07 -5.167 62.173 -5.512 61.027 -5.512 c
+h
+60.497 2.278 m
+60.497 -3.94 l
+61.056 -3.94 l
+61.692 -3.94 62.137 -3.774 62.394 -3.439 c
+62.648 -3.109 62.779 -2.535 62.791 -1.72 c
+62.791 -0.103 l
+62.791 0.779 62.665 1.389 62.423 1.735 c
+62.188 2.076 61.776 2.256 61.189 2.278 c
+h
+70.585 -3.601 m
+68.013 -3.601 l
+67.512 -5.512 l
+65.514 -5.512 l
+68.439 3.851 l
+70.158 3.851 l
+73.113 -5.512 l
+71.084 -5.512 l
+h
+68.424 -2.014 m
+70.174 -2.014 l
+69.291 1.323 l
+h
+76.057 -0.353 m
+77.468 3.851 l
+79.525 3.851 l
+77.012 -2.117 l
+77.012 -5.512 l
+75.101 -5.512 l
+75.101 -2.117 l
+72.587 3.851 l
+74.646 3.851 l
+h
+89.796 -2.028 m
+90.679 3.851 l
+92.56 3.851 l
+90.884 -5.512 l
+88.988 -5.512 l
+87.9 0 l
+86.827 -5.512 l
+84.916 -5.512 l
+83.24 3.851 l
+85.122 3.851 l
+86.004 -2.028 l
+87.107 3.851 l
+88.694 3.851 l
+h
+100.472 -1.675 m
+100.472 -2.932 100.17 -3.906 99.575 -4.601 c
+98.976 -5.299 98.153 -5.644 97.106 -5.644 c
+96.054 -5.644 95.227 -5.303 94.621 -4.615 c
+94.022 -3.921 93.717 -2.955 93.71 -1.72 c
+93.71 -0.118 l
+93.71 1.165 94.008 2.168 94.606 2.896 c
+95.202 3.62 96.033 3.983 97.091 3.983 c
+98.127 3.983 98.947 3.623 99.545 2.911 c
+100.152 2.205 100.461 1.209 100.472 -0.073 c
+h
+98.575 -0.103 m
+98.575 0.738 98.45 1.367 98.208 1.779 c
+97.972 2.19 97.598 2.396 97.091 2.396 c
+96.591 2.396 96.216 2.194 95.973 1.793 c
+95.738 1.4 95.614 0.801 95.606 0 c
+95.606 -1.675 l
+95.606 -2.492 95.728 -3.094 95.973 -3.484 c
+96.216 -3.877 96.595 -4.072 97.106 -4.072 c
+97.594 -4.072 97.958 -3.881 98.193 -3.499 c
+98.436 -3.117 98.564 -2.529 98.575 -1.735 c
+h
+104.885 -2.087 m
+103.944 -2.087 l
+103.944 -5.512 l
+102.063 -5.512 l
+102.063 3.851 l
+105.076 3.851 l
+106.024 3.851 106.755 3.605 107.266 3.116 c
+107.784 2.624 108.045 1.929 108.045 1.029 c
+108.045 -0.217 107.593 -1.088 106.693 -1.588 c
+108.324 -5.424 l
+108.324 -5.512 l
+106.296 -5.512 l
+h
+103.944 -0.515 m
+105.018 -0.515 l
+105.4 -0.515 105.683 -0.393 105.87 -0.147 c
+106.053 0.106 106.149 0.445 106.149 0.867 c
+106.149 1.808 105.785 2.278 105.062 2.278 c
+103.944 2.278 l
+h
+112.205 -1.897 m
+111.455 -2.837 l
+111.455 -5.512 l
+109.559 -5.512 l
+109.559 3.851 l
+111.455 3.851 l
+111.455 -0.235 l
+112.058 0.779 l
+113.793 3.851 l
+116.115 3.851 l
+113.425 -0.264 l
+116.159 -5.512 l
+113.91 -5.512 l
+h
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 411.726 234.667 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 404.8916 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.683 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.485 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.941 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.028 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.244 1.933 5.376 2.051 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.635 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.177 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.116 l
+14.494 3.116 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.456 14.552 -0.515 c
+14.589 -0.566 14.645 -0.611 14.714 -0.647 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.688 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.603 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.279 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.085 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.456 c
+13.586 -0.36 13.582 -0.264 13.582 -0.177 c
+h
+24.22 -0.25 m
+24.22 -0.419 24.18 -0.57 24.103 -0.706 c
+24.033 -0.834 23.931 -0.948 23.794 -1.044 c
+23.666 -1.132 23.504 -1.202 23.31 -1.249 c
+23.121 -1.297 22.905 -1.323 22.662 -1.323 c
+22.435 -1.323 22.236 -1.309 22.06 -1.279 c
+21.883 -1.249 21.725 -1.202 21.59 -1.132 c
+21.45 -1.055 21.34 -0.956 21.251 -0.838 c
+21.163 -0.721 21.093 -0.574 21.045 -0.397 c
+21.854 -0.279 l
+21.872 -0.379 21.902 -0.456 21.942 -0.515 c
+21.99 -0.574 22.049 -0.617 22.119 -0.647 c
+22.185 -0.676 22.265 -0.702 22.354 -0.721 c
+22.442 -0.732 22.545 -0.735 22.662 -0.735 c
+22.758 -0.735 22.853 -0.732 22.942 -0.721 c
+23.03 -0.702 23.107 -0.676 23.177 -0.647 c
+23.243 -0.617 23.295 -0.58 23.324 -0.53 c
+23.36 -0.482 23.383 -0.419 23.383 -0.339 c
+23.383 -0.243 23.353 -0.169 23.295 -0.118 c
+23.243 -0.07 23.177 -0.029 23.089 0 c
+23.001 0.037 22.89 0.066 22.766 0.088 c
+22.647 0.118 22.516 0.147 22.369 0.176 c
+22.229 0.213 22.09 0.253 21.942 0.293 c
+21.803 0.341 21.677 0.405 21.56 0.484 c
+21.45 0.562 21.361 0.661 21.296 0.779 c
+21.226 0.897 21.193 1.047 21.193 1.234 c
+21.193 1.389 21.222 1.532 21.28 1.66 c
+21.347 1.797 21.442 1.911 21.56 1.999 c
+21.685 2.087 21.843 2.153 22.03 2.205 c
+22.215 2.252 22.427 2.278 22.662 2.278 c
+22.846 2.278 23.023 2.256 23.192 2.219 c
+23.357 2.19 23.504 2.135 23.632 2.057 c
+23.757 1.988 23.868 1.889 23.956 1.764 c
+24.044 1.646 24.103 1.502 24.133 1.338 c
+23.339 1.264 l
+23.316 1.341 23.287 1.404 23.25 1.455 c
+23.21 1.514 23.162 1.558 23.104 1.587 c
+23.052 1.624 22.99 1.65 22.913 1.66 c
+22.832 1.668 22.751 1.675 22.662 1.675 c
+22.446 1.675 22.284 1.646 22.177 1.587 c
+22.067 1.536 22.016 1.448 22.016 1.323 c
+22.016 1.242 22.034 1.18 22.074 1.132 c
+22.122 1.08 22.185 1.043 22.265 1.014 c
+22.354 0.985 22.45 0.955 22.56 0.926 c
+22.666 0.904 22.788 0.881 22.927 0.852 c
+23.081 0.823 23.239 0.783 23.397 0.735 c
+23.551 0.683 23.692 0.621 23.809 0.544 c
+23.927 0.463 24.022 0.36 24.103 0.235 c
+24.18 0.106 24.22 -0.056 24.22 -0.25 c
+25.783 1.602 m
+25.238 1.602 l
+25.238 2.219 l
+25.826 2.219 l
+26.106 3.116 l
+26.679 3.116 l
+26.679 2.219 l
+27.914 2.219 l
+27.914 1.602 l
+26.679 1.602 l
+26.679 -0.103 l
+26.679 -0.324 l
+26.686 -0.393 26.709 -0.456 26.738 -0.515 c
+26.774 -0.566 26.83 -0.611 26.9 -0.647 c
+26.977 -0.676 27.09 -0.691 27.237 -0.691 c
+27.374 -0.691 27.509 -0.688 27.649 -0.676 c
+27.785 -0.658 27.917 -0.632 28.046 -0.603 c
+28.046 -1.205 l
+27.965 -1.216 27.888 -1.231 27.811 -1.249 c
+27.73 -1.261 27.653 -1.268 27.576 -1.279 c
+27.495 -1.286 27.407 -1.294 27.312 -1.294 c
+27.223 -1.301 27.123 -1.309 27.017 -1.309 c
+26.83 -1.309 26.668 -1.294 26.532 -1.264 c
+26.404 -1.228 26.29 -1.183 26.194 -1.132 c
+26.106 -1.085 26.032 -1.025 25.974 -0.956 c
+25.915 -0.879 25.87 -0.802 25.841 -0.721 c
+25.812 -0.632 25.789 -0.544 25.783 -0.456 c
+25.772 -0.36 25.768 -0.264 25.768 -0.177 c
+h
+30.196 -1.323 m
+30.027 -1.323 29.876 -1.301 29.741 -1.264 c
+29.612 -1.216 29.498 -1.147 29.402 -1.058 c
+29.313 -0.97 29.244 -0.864 29.196 -0.735 c
+29.145 -0.599 29.122 -0.449 29.122 -0.279 c
+29.122 -0.073 29.155 0.095 29.226 0.235 c
+29.292 0.382 29.388 0.492 29.505 0.573 c
+29.63 0.661 29.773 0.723 29.931 0.764 c
+30.096 0.801 30.273 0.827 30.46 0.837 c
+31.181 0.852 l
+31.181 1.029 l
+31.181 1.147 31.17 1.249 31.152 1.338 c
+31.129 1.425 31.096 1.492 31.048 1.543 c
+31.008 1.602 30.96 1.639 30.901 1.66 c
+30.842 1.679 30.776 1.69 30.71 1.69 c
+30.641 1.69 30.578 1.679 30.519 1.66 c
+30.468 1.65 30.42 1.624 30.372 1.587 c
+30.331 1.558 30.298 1.506 30.269 1.44 c
+30.248 1.382 30.233 1.301 30.225 1.205 c
+29.284 1.249 l
+29.313 1.396 29.358 1.532 29.417 1.66 c
+29.483 1.785 29.579 1.896 29.696 1.984 c
+29.814 2.08 29.953 2.153 30.122 2.205 c
+30.298 2.252 30.504 2.278 30.74 2.278 c
+31.181 2.278 31.511 2.168 31.74 1.955 c
+31.975 1.749 32.092 1.44 32.092 1.029 c
+32.092 -0.235 l
+32.092 -0.456 l
+32.099 -0.515 32.114 -0.57 32.136 -0.617 c
+32.154 -0.658 32.184 -0.691 32.224 -0.721 c
+32.261 -0.742 32.313 -0.75 32.371 -0.75 c
+32.438 -0.75 32.507 -0.746 32.577 -0.735 c
+32.577 -1.22 l
+32.519 -1.231 32.463 -1.242 32.415 -1.249 c
+32.375 -1.261 32.334 -1.268 32.297 -1.279 c
+32.257 -1.286 32.213 -1.294 32.166 -1.294 c
+32.114 -1.301 32.055 -1.309 31.989 -1.309 c
+31.761 -1.309 31.596 -1.257 31.489 -1.147 c
+31.379 -1.029 31.316 -0.864 31.298 -0.647 c
+31.283 -0.647 l
+31.214 -0.757 31.144 -0.852 31.077 -0.941 c
+31.008 -1.022 30.931 -1.087 30.842 -1.147 c
+30.755 -1.205 30.655 -1.249 30.549 -1.279 c
+30.449 -1.309 30.331 -1.323 30.196 -1.323 c
+31.181 0.353 m
+30.755 0.338 l
+30.655 0.338 30.564 0.33 30.475 0.324 c
+30.394 0.312 30.328 0.287 30.269 0.249 c
+30.211 0.209 30.159 0.151 30.122 0.073 c
+30.082 0.004 30.063 -0.088 30.063 -0.206 c
+30.063 -0.375 30.096 -0.497 30.167 -0.574 c
+30.233 -0.654 30.331 -0.691 30.46 -0.691 c
+30.567 -0.691 30.666 -0.669 30.755 -0.617 c
+30.85 -0.57 30.931 -0.507 30.99 -0.426 c
+31.056 -0.349 31.107 -0.262 31.137 -0.162 c
+31.166 -0.056 31.181 0.058 31.181 0.176 c
+h
+33.903 1.602 m
+33.36 1.602 l
+33.36 2.219 l
+33.948 2.219 l
+34.227 3.116 l
+34.8 3.116 l
+34.8 2.219 l
+36.035 2.219 l
+36.035 1.602 l
+34.8 1.602 l
+34.8 -0.103 l
+34.8 -0.324 l
+34.807 -0.393 34.83 -0.456 34.859 -0.515 c
+34.896 -0.566 34.951 -0.611 35.021 -0.647 c
+35.098 -0.676 35.212 -0.691 35.359 -0.691 c
+35.495 -0.691 35.631 -0.688 35.771 -0.676 c
+35.906 -0.658 36.039 -0.632 36.168 -0.603 c
+36.168 -1.205 l
+36.087 -1.216 36.01 -1.231 35.932 -1.249 c
+35.852 -1.261 35.774 -1.268 35.697 -1.279 c
+35.616 -1.286 35.528 -1.294 35.432 -1.294 c
+35.345 -1.301 35.245 -1.309 35.138 -1.309 c
+34.951 -1.309 34.789 -1.294 34.653 -1.264 c
+34.525 -1.228 34.41 -1.183 34.315 -1.132 c
+34.227 -1.085 34.154 -1.025 34.094 -0.956 c
+34.036 -0.879 33.992 -0.802 33.963 -0.721 c
+33.933 -0.632 33.911 -0.544 33.903 -0.456 c
+33.893 -0.36 33.889 -0.264 33.889 -0.177 c
+h
+38.248 2.219 m
+38.248 0.264 l
+38.248 0.125 38.254 0 38.277 -0.118 c
+38.295 -0.228 38.328 -0.32 38.379 -0.397 c
+38.427 -0.478 38.486 -0.54 38.556 -0.588 c
+38.622 -0.628 38.707 -0.647 38.806 -0.647 c
+38.894 -0.647 38.975 -0.628 39.056 -0.588 c
+39.144 -0.54 39.218 -0.47 39.276 -0.382 c
+39.335 -0.287 39.379 -0.177 39.409 -0.059 c
+39.445 0.066 39.467 0.206 39.467 0.353 c
+39.467 2.219 l
+40.364 2.219 l
+40.364 -0.485 l
+40.364 -0.721 l
+40.371 -0.802 40.378 -0.879 40.378 -0.956 c
+40.378 -1.147 l
+40.386 -1.199 40.393 -1.235 40.393 -1.264 c
+39.541 -1.264 l
+39.53 -1.235 39.519 -1.199 39.511 -1.147 c
+39.511 -0.956 l
+39.511 -0.889 39.504 -0.819 39.497 -0.75 c
+39.497 -0.574 l
+39.482 -0.574 l
+39.364 -0.838 39.21 -1.029 39.027 -1.147 c
+38.85 -1.264 38.647 -1.323 38.424 -1.323 c
+38.218 -1.323 38.045 -1.286 37.909 -1.22 c
+37.77 -1.154 37.66 -1.058 37.571 -0.941 c
+37.49 -0.823 37.431 -0.688 37.394 -0.53 c
+37.365 -0.364 37.35 -0.187 37.35 0 c
+37.35 2.219 l
+h
+44.527 -0.25 m
+44.527 -0.419 44.487 -0.57 44.409 -0.706 c
+44.34 -0.834 44.237 -0.948 44.101 -1.044 c
+43.972 -1.132 43.811 -1.202 43.616 -1.249 c
+43.428 -1.297 43.212 -1.323 42.969 -1.323 c
+42.742 -1.323 42.543 -1.309 42.366 -1.279 c
+42.19 -1.249 42.032 -1.202 41.896 -1.132 c
+41.757 -1.055 41.647 -0.956 41.558 -0.838 c
+41.47 -0.721 41.4 -0.574 41.352 -0.397 c
+42.161 -0.279 l
+42.179 -0.379 42.208 -0.456 42.249 -0.515 c
+42.297 -0.574 42.356 -0.617 42.425 -0.647 c
+42.491 -0.676 42.572 -0.702 42.661 -0.721 c
+42.749 -0.732 42.852 -0.735 42.969 -0.735 c
+43.065 -0.735 43.16 -0.732 43.249 -0.721 c
+43.337 -0.702 43.414 -0.676 43.484 -0.647 c
+43.55 -0.617 43.601 -0.58 43.631 -0.53 c
+43.667 -0.482 43.69 -0.419 43.69 -0.339 c
+43.69 -0.243 43.66 -0.169 43.601 -0.118 c
+43.55 -0.07 43.484 -0.029 43.395 0 c
+43.307 0.037 43.197 0.066 43.072 0.088 c
+42.954 0.118 42.822 0.147 42.676 0.176 c
+42.536 0.213 42.396 0.253 42.249 0.293 c
+42.109 0.341 41.984 0.405 41.867 0.484 c
+41.757 0.562 41.668 0.661 41.602 0.779 c
+41.533 0.897 41.5 1.047 41.5 1.234 c
+41.5 1.389 41.529 1.532 41.587 1.66 c
+41.654 1.797 41.749 1.911 41.867 1.999 c
+41.992 2.087 42.15 2.153 42.337 2.205 c
+42.521 2.252 42.734 2.278 42.969 2.278 c
+43.153 2.278 43.33 2.256 43.499 2.219 c
+43.663 2.19 43.811 2.135 43.939 2.057 c
+44.064 1.988 44.174 1.889 44.263 1.764 c
+44.351 1.646 44.409 1.502 44.44 1.338 c
+43.646 1.264 l
+43.623 1.341 43.594 1.404 43.557 1.455 c
+43.517 1.514 43.469 1.558 43.41 1.587 c
+43.359 1.624 43.297 1.65 43.219 1.66 c
+43.139 1.668 43.058 1.675 42.969 1.675 c
+42.753 1.675 42.591 1.646 42.484 1.587 c
+42.374 1.536 42.323 1.448 42.323 1.323 c
+42.323 1.242 42.341 1.18 42.381 1.132 c
+42.429 1.08 42.491 1.043 42.572 1.014 c
+42.661 0.985 42.757 0.955 42.867 0.926 c
+42.973 0.904 43.094 0.881 43.234 0.852 c
+43.388 0.823 43.546 0.783 43.704 0.735 c
+43.858 0.683 43.998 0.621 44.116 0.544 c
+44.234 0.463 44.329 0.36 44.409 0.235 c
+44.487 0.106 44.527 -0.056 44.527 -0.25 c
+f
+Q
+q 1 0 0 1 310.8155 390.0891 cm
+0 0 m
+0 0.265 -0.073 0.464 -0.22 0.603 c
+-0.36 0.75 -0.617 0.89 -0.999 1.029 c
+-1.374 1.166 -1.661 1.309 -1.866 1.455 c
+-2.065 1.603 -2.212 1.768 -2.308 1.956 c
+-2.406 2.151 -2.454 2.371 -2.454 2.617 c
+-2.454 3.036 -2.315 3.385 -2.028 3.66 c
+-1.745 3.932 -1.378 4.072 -0.926 4.072 c
+-0.613 4.072 -0.334 3.998 -0.088 3.851 c
+0.154 3.712 0.341 3.517 0.47 3.263 c
+0.607 3.017 0.676 2.749 0.676 2.455 c
+0 2.455 l
+0 2.786 -0.081 3.043 -0.235 3.219 c
+-0.393 3.404 -0.625 3.499 -0.926 3.499 c
+-1.191 3.499 -1.404 3.418 -1.558 3.263 c
+-1.705 3.117 -1.778 2.903 -1.778 2.631 c
+-1.778 2.404 -1.701 2.213 -1.543 2.058 c
+-1.378 1.912 -1.124 1.771 -0.779 1.646 c
+-0.261 1.478 0.11 1.268 0.339 1.015 c
+0.574 0.757 0.691 0.427 0.691 0.015 c
+0.691 -0.426 0.548 -0.779 0.264 -1.043 c
+-0.022 -1.3 -0.405 -1.425 -0.881 -1.425 c
+-1.198 -1.425 -1.484 -1.356 -1.749 -1.219 c
+-2.013 -1.084 -2.227 -0.893 -2.381 -0.646 c
+-2.528 -0.404 -2.602 -0.118 -2.602 0.206 c
+-1.926 0.206 l
+-1.926 -0.128 -1.833 -0.389 -1.646 -0.573 c
+-1.463 -0.76 -1.205 -0.852 -0.881 -0.852 c
+-0.588 -0.852 -0.367 -0.779 -0.22 -0.631 c
+-0.073 -0.477 0 -0.264 0 0 c
+2.896 -1.425 m
+2.396 -1.425 2.014 -1.278 1.75 -0.984 c
+1.484 -0.69 1.353 -0.257 1.353 0.324 c
+1.353 0.794 l
+1.353 1.389 1.477 1.856 1.735 2.191 c
+1.999 2.532 2.359 2.705 2.822 2.705 c
+3.281 2.705 3.624 2.55 3.851 2.249 c
+4.087 1.956 4.208 1.492 4.218 0.867 c
+4.218 0.441 l
+1.999 0.441 l
+1.999 0.353 l
+1.999 -0.08 2.076 -0.393 2.234 -0.588 c
+2.4 -0.775 2.631 -0.866 2.926 -0.866 c
+3.12 -0.866 3.293 -0.833 3.439 -0.764 c
+3.587 -0.687 3.723 -0.569 3.851 -0.411 c
+4.189 -0.823 l
+3.903 -1.227 3.473 -1.425 2.896 -1.425 c
+2.822 2.147 m
+2.547 2.147 2.344 2.051 2.22 1.867 c
+2.091 1.68 2.018 1.389 1.999 1 c
+3.572 1 l
+3.572 1.088 l
+3.55 1.47 3.484 1.738 3.366 1.897 c
+3.248 2.062 3.065 2.147 2.822 2.147 c
+6.423 -1.425 m
+5.924 -1.425 5.542 -1.278 5.278 -0.984 c
+5.012 -0.69 4.881 -0.257 4.881 0.324 c
+4.881 0.794 l
+4.881 1.389 5.005 1.856 5.263 2.191 c
+5.527 2.532 5.887 2.705 6.35 2.705 c
+6.809 2.705 7.151 2.55 7.379 2.249 c
+7.614 1.956 7.736 1.492 7.746 0.867 c
+7.746 0.441 l
+5.527 0.441 l
+5.527 0.353 l
+5.527 -0.08 5.604 -0.393 5.762 -0.588 c
+5.928 -0.775 6.159 -0.866 6.453 -0.866 c
+6.648 -0.866 6.82 -0.833 6.967 -0.764 c
+7.115 -0.687 7.25 -0.569 7.379 -0.411 c
+7.717 -0.823 l
+7.431 -1.227 7.001 -1.425 6.423 -1.425 c
+6.35 2.147 m
+6.074 2.147 5.872 2.051 5.748 1.867 c
+5.619 1.68 5.546 1.389 5.527 1 c
+7.1 1 l
+7.1 1.088 l
+7.078 1.47 7.011 1.738 6.894 1.897 c
+6.776 2.062 6.593 2.147 6.35 2.147 c
+11.098 3.587 m
+11.098 2.631 l
+11.701 2.631 l
+11.701 2.103 l
+11.098 2.103 l
+11.098 -0.367 l
+11.098 -0.525 11.12 -0.643 11.171 -0.72 c
+11.231 -0.801 11.318 -0.837 11.436 -0.837 c
+11.524 -0.837 11.612 -0.823 11.701 -0.793 c
+11.701 -1.352 l
+11.553 -1.4 11.399 -1.425 11.245 -1.425 c
+10.988 -1.425 10.793 -1.334 10.657 -1.146 c
+10.517 -0.962 10.452 -0.702 10.452 -0.367 c
+10.452 2.103 l
+9.849 2.103 l
+9.849 2.631 l
+10.452 2.631 l
+10.452 3.587 l
+h
+13.112 2.22 m
+13.365 2.544 13.685 2.705 14.067 2.705 c
+14.772 2.705 15.129 2.234 15.14 1.294 c
+15.14 -1.352 l
+14.493 -1.352 l
+14.493 1.264 l
+14.493 1.577 14.438 1.798 14.331 1.926 c
+14.221 2.051 14.067 2.117 13.861 2.117 c
+13.704 2.117 13.556 2.062 13.421 1.956 c
+13.292 1.845 13.189 1.709 13.112 1.544 c
+13.112 -1.352 l
+12.465 -1.352 l
+12.465 4.293 l
+13.112 4.293 l
+h
+17.521 -1.425 m
+17.022 -1.425 16.64 -1.278 16.375 -0.984 c
+16.11 -0.69 15.978 -0.257 15.978 0.324 c
+15.978 0.794 l
+15.978 1.389 16.103 1.856 16.36 2.191 c
+16.625 2.532 16.985 2.705 17.448 2.705 c
+17.907 2.705 18.249 2.55 18.477 2.249 c
+18.712 1.956 18.834 1.492 18.844 0.867 c
+18.844 0.441 l
+16.625 0.441 l
+16.625 0.353 l
+16.625 -0.08 16.702 -0.393 16.86 -0.588 c
+17.026 -0.775 17.257 -0.866 17.55 -0.866 c
+17.745 -0.866 17.918 -0.833 18.065 -0.764 c
+18.213 -0.687 18.348 -0.569 18.477 -0.411 c
+18.815 -0.823 l
+18.528 -1.227 18.098 -1.425 17.521 -1.425 c
+17.448 2.147 m
+17.172 2.147 16.97 2.051 16.845 1.867 c
+16.717 1.68 16.644 1.389 16.625 1 c
+18.198 1 l
+18.198 1.088 l
+18.175 1.47 18.109 1.738 17.992 1.897 c
+17.874 2.062 17.691 2.147 17.448 2.147 c
+23.283 -0.338 m
+23.283 -0.191 23.229 -0.07 23.122 0.03 c
+23.011 0.125 22.805 0.243 22.504 0.383 c
+22.159 0.53 21.916 0.651 21.77 0.75 c
+21.622 0.857 21.512 0.975 21.446 1.103 c
+21.376 1.228 21.344 1.386 21.344 1.573 c
+21.344 1.897 21.461 2.165 21.696 2.382 c
+21.931 2.595 22.233 2.705 22.607 2.705 c
+22.99 2.705 23.298 2.591 23.534 2.367 c
+23.769 2.139 23.886 1.852 23.886 1.5 c
+23.239 1.5 l
+23.239 1.676 23.181 1.827 23.063 1.956 c
+22.946 2.08 22.791 2.147 22.607 2.147 c
+22.408 2.147 22.258 2.091 22.152 1.985 c
+22.042 1.885 21.99 1.754 21.99 1.588 c
+21.99 1.459 22.026 1.353 22.107 1.264 c
+22.185 1.183 22.375 1.081 22.681 0.956 c
+23.158 0.769 23.489 0.581 23.665 0.397 c
+23.842 0.221 23.931 -0.007 23.931 -0.278 c
+23.931 -0.631 23.805 -0.911 23.563 -1.117 c
+23.328 -1.323 23.011 -1.425 22.622 -1.425 c
+22.2 -1.425 21.861 -1.308 21.608 -1.072 c
+21.35 -0.83 21.226 -0.525 21.226 -0.161 c
+21.872 -0.161 l
+21.88 -0.389 21.949 -0.565 22.078 -0.69 c
+22.203 -0.808 22.387 -0.866 22.622 -0.866 c
+22.836 -0.866 22.996 -0.819 23.107 -0.72 c
+23.225 -0.625 23.283 -0.496 23.283 -0.338 c
+25.62 3.587 m
+25.62 2.631 l
+26.223 2.631 l
+26.223 2.103 l
+25.62 2.103 l
+25.62 -0.367 l
+25.62 -0.525 25.643 -0.643 25.694 -0.72 c
+25.753 -0.801 25.841 -0.837 25.959 -0.837 c
+26.047 -0.837 26.135 -0.823 26.223 -0.793 c
+26.223 -1.352 l
+26.076 -1.4 25.922 -1.425 25.768 -1.425 c
+25.51 -1.425 25.315 -1.334 25.18 -1.146 c
+25.04 -0.962 24.974 -0.702 24.974 -0.367 c
+24.974 2.103 l
+24.371 2.103 l
+24.371 2.631 l
+24.974 2.631 l
+24.974 3.587 l
+h
+29.031 -1.352 m
+28.991 -1.263 28.964 -1.117 28.957 -0.911 c
+28.722 -1.256 28.428 -1.425 28.075 -1.425 c
+27.711 -1.425 27.428 -1.33 27.223 -1.132 c
+27.025 -0.926 26.929 -0.639 26.929 -0.264 c
+26.929 0.136 27.065 0.456 27.341 0.691 c
+27.613 0.934 27.987 1.058 28.457 1.058 c
+28.943 1.058 l
+28.943 1.485 l
+28.943 1.721 28.887 1.885 28.781 1.985 c
+28.671 2.091 28.509 2.147 28.296 2.147 c
+28.097 2.147 27.935 2.087 27.811 1.97 c
+27.693 1.852 27.634 1.706 27.634 1.529 c
+26.988 1.529 l
+26.988 1.723 27.046 1.915 27.164 2.103 c
+27.289 2.286 27.451 2.433 27.649 2.544 c
+27.855 2.65 28.083 2.705 28.34 2.705 c
+28.741 2.705 29.045 2.602 29.251 2.396 c
+29.464 2.191 29.579 1.897 29.589 1.515 c
+29.589 -0.5 l
+29.589 -0.804 29.626 -1.069 29.707 -1.294 c
+29.707 -1.352 l
+h
+28.164 -0.837 m
+28.328 -0.837 28.48 -0.793 28.619 -0.706 c
+28.766 -0.617 28.873 -0.507 28.943 -0.367 c
+28.943 0.574 l
+28.575 0.574 l
+28.259 0.574 28.016 0.504 27.84 0.368 c
+27.663 0.239 27.576 0.052 27.576 -0.191 c
+27.576 -0.419 27.62 -0.584 27.708 -0.69 c
+27.796 -0.789 27.947 -0.837 28.164 -0.837 c
+31.441 3.587 m
+31.441 2.631 l
+32.044 2.631 l
+32.044 2.103 l
+31.441 2.103 l
+31.441 -0.367 l
+31.441 -0.525 31.463 -0.643 31.515 -0.72 c
+31.574 -0.801 31.662 -0.837 31.779 -0.837 c
+31.868 -0.837 31.956 -0.823 32.044 -0.793 c
+32.044 -1.352 l
+31.897 -1.4 31.742 -1.425 31.588 -1.425 c
+31.331 -1.425 31.137 -1.334 31 -1.146 c
+30.861 -0.962 30.794 -0.702 30.794 -0.367 c
+30.794 2.103 l
+30.192 2.103 l
+30.192 2.631 l
+30.794 2.631 l
+30.794 3.587 l
+h
+34.837 -0.999 m
+34.62 -1.286 34.308 -1.425 33.896 -1.425 c
+33.533 -1.425 33.257 -1.304 33.073 -1.058 c
+32.897 -0.804 32.801 -0.44 32.794 0.03 c
+32.794 2.631 l
+33.44 2.631 l
+33.44 0.088 l
+33.44 -0.54 33.624 -0.852 33.999 -0.852 c
+34.4 -0.852 34.676 -0.675 34.822 -0.323 c
+34.822 2.631 l
+35.469 2.631 l
+35.469 -1.352 l
+34.852 -1.352 l
+h
+38.394 -0.338 m
+38.394 -0.191 38.339 -0.07 38.233 0.03 c
+38.123 0.125 37.917 0.243 37.615 0.383 c
+37.269 0.53 37.027 0.651 36.88 0.75 c
+36.733 0.857 36.623 0.975 36.557 1.103 c
+36.487 1.228 36.454 1.386 36.454 1.573 c
+36.454 1.897 36.571 2.165 36.806 2.382 c
+37.042 2.595 37.343 2.705 37.718 2.705 c
+38.1 2.705 38.409 2.591 38.644 2.367 c
+38.879 2.139 38.996 1.852 38.996 1.5 c
+38.35 1.5 l
+38.35 1.676 38.291 1.827 38.173 1.956 c
+38.056 2.08 37.901 2.147 37.718 2.147 c
+37.52 2.147 37.369 2.091 37.263 1.985 c
+37.152 1.885 37.101 1.754 37.101 1.588 c
+37.101 1.459 37.138 1.353 37.218 1.264 c
+37.296 1.183 37.487 1.081 37.791 0.956 c
+38.269 0.769 38.599 0.581 38.776 0.397 c
+38.952 0.221 39.041 -0.007 39.041 -0.278 c
+39.041 -0.631 38.916 -0.911 38.674 -1.117 c
+38.439 -1.323 38.123 -1.425 37.733 -1.425 c
+37.31 -1.425 36.972 -1.308 36.718 -1.072 c
+36.461 -0.83 36.336 -0.525 36.336 -0.161 c
+36.983 -0.161 l
+36.99 -0.389 37.06 -0.565 37.188 -0.69 c
+37.314 -0.808 37.498 -0.866 37.733 -0.866 c
+37.946 -0.866 38.107 -0.819 38.218 -0.72 c
+38.335 -0.625 38.394 -0.496 38.394 -0.338 c
+41.466 0.823 m
+41.466 1.401 41.602 1.856 41.878 2.191 c
+42.161 2.532 42.532 2.705 42.995 2.705 c
+43.455 2.705 43.822 2.536 44.097 2.205 c
+44.38 1.881 44.527 1.434 44.538 0.867 c
+44.538 0.441 l
+44.538 -0.128 44.395 -0.584 44.112 -0.926 c
+43.837 -1.26 43.469 -1.425 43.01 -1.425 c
+42.547 -1.425 42.175 -1.263 41.892 -0.941 c
+41.616 -0.61 41.473 -0.168 41.466 0.383 c
+h
+42.113 0.441 m
+42.113 0.038 42.19 -0.278 42.348 -0.514 c
+42.514 -0.749 42.734 -0.866 43.01 -0.866 c
+43.575 -0.866 43.87 -0.455 43.891 0.368 c
+43.891 0.823 l
+43.891 1.224 43.807 1.544 43.642 1.779 c
+43.484 2.022 43.267 2.147 42.995 2.147 c
+42.73 2.147 42.514 2.022 42.348 1.779 c
+42.19 1.544 42.113 1.224 42.113 0.823 c
+h
+45.67 -1.352 m
+45.67 2.103 l
+45.14 2.103 l
+45.14 2.631 l
+45.67 2.631 l
+45.67 3.088 l
+45.67 3.487 45.766 3.801 45.965 4.028 c
+46.17 4.252 46.449 4.366 46.802 4.366 c
+46.938 4.366 47.07 4.344 47.199 4.308 c
+47.17 3.763 l
+47.07 3.782 46.971 3.793 46.875 3.793 c
+46.501 3.793 46.316 3.528 46.316 2.999 c
+46.316 2.631 l
+46.993 2.631 l
+46.993 2.103 l
+46.316 2.103 l
+46.316 -1.352 l
+h
+50.565 -0.264 m
+51.285 2.631 l
+51.976 2.631 l
+50.682 -1.911 l
+50.584 -2.252 50.44 -2.513 50.256 -2.69 c
+50.079 -2.865 49.878 -2.954 49.653 -2.954 c
+49.566 -2.954 49.452 -2.932 49.315 -2.896 c
+49.315 -2.352 l
+49.462 -2.366 l
+49.647 -2.366 49.793 -2.322 49.903 -2.234 c
+50.01 -2.146 50.098 -1.988 50.168 -1.764 c
+50.285 -1.323 l
+49.124 2.631 l
+49.83 2.631 l
+h
+52.417 0.823 m
+52.417 1.401 52.553 1.856 52.828 2.191 c
+53.111 2.532 53.483 2.705 53.946 2.705 c
+54.405 2.705 54.773 2.536 55.048 2.205 c
+55.331 1.881 55.478 1.434 55.489 0.867 c
+55.489 0.441 l
+55.489 -0.128 55.346 -0.584 55.062 -0.926 c
+54.787 -1.26 54.42 -1.425 53.961 -1.425 c
+53.497 -1.425 53.126 -1.263 52.843 -0.941 c
+52.568 -0.61 52.425 -0.168 52.417 0.383 c
+h
+53.063 0.441 m
+53.063 0.038 53.141 -0.278 53.299 -0.514 c
+53.464 -0.749 53.685 -0.866 53.961 -0.866 c
+54.526 -0.866 54.821 -0.455 54.842 0.368 c
+54.842 0.823 l
+54.842 1.224 54.758 1.544 54.592 1.779 c
+54.434 2.022 54.218 2.147 53.946 2.147 c
+53.681 2.147 53.464 2.022 53.299 1.779 c
+53.141 1.544 53.063 1.224 53.063 0.823 c
+h
+58.355 -0.999 m
+58.139 -1.286 57.826 -1.425 57.414 -1.425 c
+57.051 -1.425 56.776 -1.304 56.591 -1.058 c
+56.415 -0.804 56.319 -0.44 56.313 0.03 c
+56.313 2.631 l
+56.959 2.631 l
+56.959 0.088 l
+56.959 -0.54 57.142 -0.852 57.518 -0.852 c
+57.918 -0.852 58.194 -0.675 58.341 -0.323 c
+58.341 2.631 l
+58.987 2.631 l
+58.987 -1.352 l
+58.37 -1.352 l
+h
+61.618 2.014 m
+61.53 2.033 61.431 2.043 61.325 2.043 c
+60.99 2.043 60.755 1.86 60.619 1.5 c
+60.619 -1.352 l
+59.972 -1.352 l
+59.972 2.631 l
+60.604 2.631 l
+60.619 2.22 l
+60.795 2.544 61.038 2.705 61.354 2.705 c
+61.46 2.705 61.549 2.683 61.618 2.646 c
+h
+66.954 -0.205 m
+67.557 2.631 l
+68.204 2.631 l
+67.219 -1.352 l
+66.704 -1.352 l
+65.925 1.5 l
+65.175 -1.352 l
+64.647 -1.352 l
+63.691 2.631 l
+64.323 2.631 l
+64.94 -0.132 l
+65.676 2.631 l
+66.19 2.631 l
+h
+68.747 0.823 m
+68.747 1.401 68.884 1.856 69.159 2.191 c
+69.443 2.532 69.813 2.705 70.276 2.705 c
+70.736 2.705 71.103 2.536 71.379 2.205 c
+71.662 1.881 71.809 1.434 71.82 0.867 c
+71.82 0.441 l
+71.82 -0.128 71.677 -0.584 71.394 -0.926 c
+71.118 -1.26 70.75 -1.425 70.291 -1.425 c
+69.828 -1.425 69.457 -1.263 69.174 -0.941 c
+68.898 -0.61 68.755 -0.168 68.747 0.383 c
+h
+69.395 0.441 m
+69.395 0.038 69.472 -0.278 69.63 -0.514 c
+69.795 -0.749 70.015 -0.866 70.291 -0.866 c
+70.857 -0.866 71.151 -0.455 71.173 0.368 c
+71.173 0.823 l
+71.173 1.224 71.089 1.544 70.923 1.779 c
+70.765 2.022 70.548 2.147 70.276 2.147 c
+70.012 2.147 69.795 2.022 69.63 1.779 c
+69.472 1.544 69.395 1.224 69.395 0.823 c
+h
+74.303 2.014 m
+74.216 2.033 74.116 2.043 74.01 2.043 c
+73.676 2.043 73.441 1.86 73.304 1.5 c
+73.304 -1.352 l
+72.657 -1.352 l
+72.657 2.631 l
+73.289 2.631 l
+73.304 2.22 l
+73.481 2.544 73.723 2.705 74.039 2.705 c
+74.146 2.705 74.234 2.683 74.303 2.646 c
+h
+75.95 0.47 m
+75.612 0.074 l
+75.612 -1.352 l
+74.951 -1.352 l
+74.951 4.293 l
+75.612 4.293 l
+75.612 0.912 l
+76.847 2.631 l
+77.626 2.631 l
+76.362 0.971 l
+77.788 -1.352 l
+77.038 -1.352 l
+h
+78.39 -0.999 m
+78.39 -0.881 78.423 -0.786 78.493 -0.706 c
+78.559 -0.628 78.662 -0.588 78.802 -0.588 c
+78.949 -0.588 79.055 -0.628 79.126 -0.706 c
+79.203 -0.786 79.242 -0.881 79.242 -0.999 c
+79.242 -1.109 79.203 -1.201 79.126 -1.278 c
+79.055 -1.356 78.949 -1.396 78.802 -1.396 c
+78.662 -1.396 78.559 -1.356 78.493 -1.278 c
+78.423 -1.201 78.39 -1.109 78.39 -0.999 c
+85.636 -1.352 m
+84.961 -1.352 l
+82.814 2.779 l
+82.814 -1.352 l
+82.139 -1.352 l
+82.139 3.998 l
+82.814 3.998 l
+84.975 -0.147 l
+84.975 3.998 l
+85.636 3.998 l
+h
+88.136 -1.425 m
+87.636 -1.425 87.254 -1.278 86.989 -0.984 c
+86.725 -0.69 86.592 -0.257 86.592 0.324 c
+86.592 0.794 l
+86.592 1.389 86.717 1.856 86.974 2.191 c
+87.239 2.532 87.599 2.705 88.063 2.705 c
+88.522 2.705 88.863 2.55 89.091 2.249 c
+89.326 1.956 89.447 1.492 89.459 0.867 c
+89.459 0.441 l
+87.239 0.441 l
+87.239 0.353 l
+87.239 -0.08 87.317 -0.393 87.474 -0.588 c
+87.639 -0.775 87.871 -0.866 88.165 -0.866 c
+88.36 -0.866 88.533 -0.833 88.68 -0.764 c
+88.827 -0.687 88.963 -0.569 89.091 -0.411 c
+89.43 -0.823 l
+89.143 -1.227 88.713 -1.425 88.136 -1.425 c
+88.063 2.147 m
+87.787 2.147 87.585 2.051 87.46 1.867 c
+87.331 1.68 87.257 1.389 87.239 1 c
+88.811 1 l
+88.811 1.088 l
+88.79 1.47 88.724 1.738 88.606 1.897 c
+88.489 2.062 88.304 2.147 88.063 2.147 c
+93.207 -0.205 m
+93.81 2.631 l
+94.456 2.631 l
+93.472 -1.352 l
+92.957 -1.352 l
+92.177 1.5 l
+91.429 -1.352 l
+90.899 -1.352 l
+89.944 2.631 l
+90.575 2.631 l
+91.193 -0.132 l
+91.928 2.631 l
+92.443 2.631 l
+h
+94.691 -2.425 m
+94.294 -2.16 l
+94.529 -1.837 94.651 -1.502 94.662 -1.161 c
+94.662 -0.544 l
+95.323 -0.544 l
+95.323 -1.072 l
+95.323 -1.33 95.257 -1.576 95.132 -1.822 c
+95.015 -2.065 94.868 -2.267 94.691 -2.425 c
+99.822 -0.338 m
+99.822 -0.191 99.766 -0.07 99.66 0.03 c
+99.549 0.125 99.344 0.243 99.042 0.383 c
+98.697 0.53 98.454 0.651 98.307 0.75 c
+98.161 0.857 98.05 0.975 97.984 1.103 c
+97.914 1.228 97.881 1.386 97.881 1.573 c
+97.881 1.897 97.999 2.165 98.234 2.382 c
+98.469 2.595 98.77 2.705 99.145 2.705 c
+99.527 2.705 99.836 2.591 100.071 2.367 c
+100.306 2.139 100.424 1.852 100.424 1.5 c
+99.778 1.5 l
+99.778 1.676 99.718 1.827 99.601 1.956 c
+99.483 2.08 99.329 2.147 99.145 2.147 c
+98.947 2.147 98.796 2.091 98.689 1.985 c
+98.579 1.885 98.527 1.754 98.527 1.588 c
+98.527 1.459 98.565 1.353 98.646 1.264 c
+98.723 1.183 98.914 1.081 99.219 0.956 c
+99.697 0.769 100.027 0.581 100.204 0.397 c
+100.38 0.221 100.468 -0.007 100.468 -0.278 c
+100.468 -0.631 100.343 -0.911 100.1 -1.117 c
+99.865 -1.323 99.549 -1.425 99.16 -1.425 c
+98.737 -1.425 98.399 -1.308 98.145 -1.072 c
+97.889 -0.83 97.764 -0.525 97.764 -0.161 c
+98.411 -0.161 l
+98.417 -0.389 98.488 -0.565 98.616 -0.69 c
+98.741 -0.808 98.924 -0.866 99.16 -0.866 c
+99.373 -0.866 99.535 -0.819 99.645 -0.72 c
+99.763 -0.625 99.822 -0.496 99.822 -0.338 c
+102.159 3.587 m
+102.159 2.631 l
+102.761 2.631 l
+102.761 2.103 l
+102.159 2.103 l
+102.159 -0.367 l
+102.159 -0.525 102.18 -0.643 102.232 -0.72 c
+102.29 -0.801 102.379 -0.837 102.496 -0.837 c
+102.585 -0.837 102.673 -0.823 102.761 -0.793 c
+102.761 -1.352 l
+102.614 -1.4 102.46 -1.425 102.305 -1.425 c
+102.049 -1.425 101.854 -1.334 101.717 -1.146 c
+101.578 -0.962 101.511 -0.702 101.511 -0.367 c
+101.511 2.103 l
+100.909 2.103 l
+100.909 2.631 l
+101.511 2.631 l
+101.511 3.587 l
+h
+105.569 -1.352 m
+105.528 -1.263 105.503 -1.117 105.495 -0.911 c
+105.26 -1.256 104.966 -1.425 104.613 -1.425 c
+104.25 -1.425 103.967 -1.33 103.761 -1.132 c
+103.562 -0.926 103.466 -0.639 103.466 -0.264 c
+103.466 0.136 103.603 0.456 103.878 0.691 c
+104.15 0.934 104.526 1.058 104.996 1.058 c
+105.48 1.058 l
+105.48 1.485 l
+105.48 1.721 105.426 1.885 105.319 1.985 c
+105.209 2.091 105.047 2.147 104.834 2.147 c
+104.636 2.147 104.474 2.087 104.349 1.97 c
+104.231 1.852 104.172 1.706 104.172 1.529 c
+103.526 1.529 l
+103.526 1.723 103.585 1.915 103.701 2.103 c
+103.827 2.286 103.988 2.433 104.187 2.544 c
+104.393 2.65 104.621 2.705 104.877 2.705 c
+105.278 2.705 105.583 2.602 105.789 2.396 c
+106.003 2.191 106.116 1.897 106.128 1.515 c
+106.128 -0.5 l
+106.128 -0.804 106.164 -1.069 106.245 -1.294 c
+106.245 -1.352 l
+h
+104.701 -0.837 m
+104.867 -0.837 105.018 -0.793 105.157 -0.706 c
+105.304 -0.617 105.411 -0.507 105.48 -0.367 c
+105.48 0.574 l
+105.113 0.574 l
+104.797 0.574 104.555 0.504 104.378 0.368 c
+104.202 0.239 104.114 0.052 104.114 -0.191 c
+104.114 -0.419 104.158 -0.584 104.246 -0.69 c
+104.334 -0.789 104.485 -0.837 104.701 -0.837 c
+106.994 0.823 m
+106.994 1.441 107.105 1.904 107.333 2.22 c
+107.557 2.544 107.892 2.705 108.332 2.705 c
+108.733 2.705 109.037 2.529 109.243 2.176 c
+109.288 2.631 l
+109.876 2.631 l
+109.876 -1.396 l
+109.876 -1.884 109.747 -2.263 109.494 -2.528 c
+109.236 -2.792 108.883 -2.925 108.435 -2.925 c
+108.237 -2.925 108.016 -2.873 107.774 -2.778 c
+107.527 -2.678 107.348 -2.557 107.229 -2.41 c
+107.495 -1.969 l
+107.759 -2.234 108.056 -2.366 108.391 -2.366 c
+108.927 -2.366 109.203 -2.072 109.214 -1.484 c
+109.214 -0.955 l
+109.008 -1.271 108.707 -1.425 108.318 -1.425 c
+107.906 -1.425 107.583 -1.275 107.348 -0.97 c
+107.119 -0.658 107.002 -0.205 106.994 0.383 c
+h
+107.656 0.441 m
+107.656 0 107.718 -0.33 107.847 -0.544 c
+107.972 -0.749 108.189 -0.852 108.494 -0.852 c
+108.817 -0.852 109.056 -0.687 109.214 -0.353 c
+109.214 1.632 l
+109.045 1.956 108.806 2.117 108.494 2.117 c
+108.2 2.117 107.983 2.014 107.847 1.808 c
+107.718 1.603 107.656 1.279 107.656 0.838 c
+h
+112.257 -1.425 m
+111.757 -1.425 111.375 -1.278 111.111 -0.984 c
+110.845 -0.69 110.714 -0.257 110.714 0.324 c
+110.714 0.794 l
+110.714 1.389 110.838 1.856 111.096 2.191 c
+111.36 2.532 111.72 2.705 112.183 2.705 c
+112.642 2.705 112.985 2.55 113.212 2.249 c
+113.448 1.956 113.569 1.492 113.579 0.867 c
+113.579 0.441 l
+111.36 0.441 l
+111.36 0.353 l
+111.36 -0.08 111.437 -0.393 111.595 -0.588 c
+111.761 -0.775 111.992 -0.866 112.287 -0.866 c
+112.481 -0.866 112.654 -0.833 112.8 -0.764 c
+112.948 -0.687 113.083 -0.569 113.212 -0.411 c
+113.55 -0.823 l
+113.264 -1.227 112.834 -1.425 112.257 -1.425 c
+112.183 2.147 m
+111.908 2.147 111.705 2.051 111.581 1.867 c
+111.452 1.68 111.379 1.389 111.36 1 c
+112.933 1 l
+112.933 1.088 l
+112.911 1.47 112.845 1.738 112.727 1.897 c
+112.609 2.062 112.426 2.147 112.183 2.147 c
+114.226 0.823 m
+114.226 1.43 114.337 1.897 114.564 2.22 c
+114.799 2.544 115.127 2.705 115.549 2.705 c
+115.931 2.705 116.229 2.547 116.446 2.234 c
+116.446 4.293 l
+117.093 4.293 l
+117.093 -1.352 l
+116.505 -1.352 l
+116.461 -0.926 l
+116.255 -1.26 115.95 -1.425 115.549 -1.425 c
+115.138 -1.425 114.814 -1.271 114.579 -0.955 c
+114.344 -0.631 114.226 -0.176 114.226 0.412 c
+h
+114.874 0.441 m
+114.874 0 114.936 -0.33 115.065 -0.544 c
+115.2 -0.749 115.421 -0.852 115.726 -0.852 c
+116.05 -0.852 116.288 -0.69 116.446 -0.367 c
+116.446 1.646 l
+116.277 1.959 116.038 2.117 115.726 2.117 c
+115.421 2.117 115.2 2.014 115.065 1.808 c
+114.936 1.603 114.874 1.279 114.874 0.838 c
+h
+118.107 -2.425 m
+117.71 -2.16 l
+117.945 -1.837 118.067 -1.502 118.078 -1.161 c
+118.078 -0.544 l
+118.739 -0.544 l
+118.739 -1.072 l
+118.739 -1.33 118.673 -1.576 118.548 -1.822 c
+118.431 -2.065 118.283 -2.267 118.107 -2.425 c
+121.914 2.631 m
+121.929 2.264 l
+122.171 2.558 122.491 2.705 122.884 2.705 c
+123.325 2.705 123.634 2.507 123.811 2.117 c
+124.064 2.507 124.413 2.705 124.854 2.705 c
+125.589 2.705 125.964 2.242 125.986 1.324 c
+125.986 -1.352 l
+125.339 -1.352 l
+125.339 1.264 l
+125.339 1.559 125.284 1.771 125.178 1.912 c
+125.078 2.047 124.906 2.117 124.663 2.117 c
+124.465 2.117 124.303 2.037 124.178 1.881 c
+124.06 1.735 123.99 1.544 123.972 1.309 c
+123.972 -1.352 l
+123.31 -1.352 l
+123.31 1.294 l
+123.31 1.841 123.09 2.117 122.649 2.117 c
+122.315 2.117 122.08 1.956 121.943 1.632 c
+121.943 -1.352 l
+121.297 -1.352 l
+121.297 2.631 l
+h
+126.824 0.823 m
+126.824 1.401 126.959 1.856 127.235 2.191 c
+127.518 2.532 127.889 2.705 128.353 2.705 c
+128.812 2.705 129.18 2.536 129.455 2.205 c
+129.737 1.881 129.885 1.434 129.895 0.867 c
+129.895 0.441 l
+129.895 -0.128 129.752 -0.584 129.469 -0.926 c
+129.194 -1.26 128.827 -1.425 128.367 -1.425 c
+127.904 -1.425 127.533 -1.263 127.25 -0.941 c
+126.974 -0.61 126.831 -0.168 126.824 0.383 c
+h
+127.47 0.441 m
+127.47 0.038 127.547 -0.278 127.705 -0.514 c
+127.871 -0.749 128.091 -0.866 128.367 -0.866 c
+128.933 -0.866 129.227 -0.455 129.249 0.368 c
+129.249 0.823 l
+129.249 1.224 129.164 1.544 128.999 1.779 c
+128.841 2.022 128.624 2.147 128.353 2.147 c
+128.087 2.147 127.871 2.022 127.705 1.779 c
+127.547 1.544 127.47 1.224 127.47 0.823 c
+h
+130.601 0.823 m
+130.601 1.43 130.711 1.897 130.94 2.22 c
+131.175 2.544 131.501 2.705 131.924 2.705 c
+132.307 2.705 132.604 2.547 132.821 2.234 c
+132.821 4.293 l
+133.467 4.293 l
+133.467 -1.352 l
+132.879 -1.352 l
+132.835 -0.926 l
+132.629 -1.26 132.324 -1.425 131.924 -1.425 c
+131.513 -1.425 131.189 -1.271 130.954 -0.955 c
+130.719 -0.631 130.601 -0.176 130.601 0.412 c
+h
+131.248 0.441 m
+131.248 0 131.31 -0.33 131.439 -0.544 c
+131.575 -0.749 131.796 -0.852 132.101 -0.852 c
+132.424 -0.852 132.663 -0.69 132.821 -0.367 c
+132.821 1.646 l
+132.652 1.959 132.413 2.117 132.101 2.117 c
+131.796 2.117 131.575 2.014 131.439 1.808 c
+131.31 1.603 131.248 1.279 131.248 0.838 c
+h
+135.173 -1.352 -0.647 3.983 re
+135.216 3.675 m
+135.216 3.565 135.187 3.473 135.129 3.396 c
+135.069 3.326 134.974 3.293 134.849 3.293 c
+134.732 3.293 134.636 3.326 134.57 3.396 c
+134.511 3.473 134.482 3.565 134.482 3.675 c
+134.482 3.793 134.511 3.884 134.57 3.955 c
+134.636 4.032 134.732 4.072 134.849 4.072 c
+134.974 4.072 135.069 4.032 135.129 3.955 c
+135.187 3.874 135.216 3.782 135.216 3.675 c
+136.363 -1.352 m
+136.363 2.103 l
+135.848 2.103 l
+135.848 2.631 l
+136.363 2.631 l
+136.363 2.999 l
+136.371 3.429 136.484 3.763 136.702 3.998 c
+136.926 4.241 137.238 4.366 137.642 4.366 c
+137.789 4.366 137.928 4.344 138.069 4.308 c
+138.215 4.267 138.366 4.212 138.524 4.146 c
+138.406 3.572 l
+138.171 3.697 137.928 3.763 137.687 3.763 c
+137.44 3.763 137.267 3.693 137.172 3.558 c
+137.072 3.429 137.024 3.234 137.024 2.97 c
+137.024 2.631 l
+137.672 2.631 l
+137.672 2.103 l
+137.024 2.103 l
+137.024 -1.352 l
+h
+138.832 -1.352 -0.646 3.983 re
+141.302 -1.425 m
+140.802 -1.425 140.42 -1.278 140.155 -0.984 c
+139.891 -0.69 139.759 -0.257 139.759 0.324 c
+139.759 0.794 l
+139.759 1.389 139.883 1.856 140.141 2.191 c
+140.405 2.532 140.766 2.705 141.229 2.705 c
+141.688 2.705 142.03 2.55 142.258 2.249 c
+142.493 1.956 142.614 1.492 142.625 0.867 c
+142.625 0.441 l
+140.405 0.441 l
+140.405 0.353 l
+140.405 -0.08 140.483 -0.393 140.641 -0.588 c
+140.806 -0.775 141.038 -0.866 141.331 -0.866 c
+141.527 -0.866 141.699 -0.833 141.846 -0.764 c
+141.993 -0.687 142.129 -0.569 142.258 -0.411 c
+142.595 -0.823 l
+142.309 -1.227 141.879 -1.425 141.302 -1.425 c
+141.229 2.147 m
+140.953 2.147 140.751 2.051 140.626 1.867 c
+140.498 1.68 140.424 1.389 140.405 1 c
+141.978 1 l
+141.978 1.088 l
+141.957 1.47 141.89 1.738 141.772 1.897 c
+141.655 2.062 141.471 2.147 141.229 2.147 c
+143.272 0.823 m
+143.272 1.43 143.382 1.897 143.61 2.22 c
+143.845 2.544 144.172 2.705 144.594 2.705 c
+144.977 2.705 145.275 2.547 145.491 2.234 c
+145.491 4.293 l
+146.138 4.293 l
+146.138 -1.352 l
+145.55 -1.352 l
+145.506 -0.926 l
+145.3 -1.26 144.995 -1.425 144.594 -1.425 c
+144.183 -1.425 143.86 -1.271 143.625 -0.955 c
+143.389 -0.631 143.272 -0.176 143.272 0.412 c
+h
+143.918 0.441 m
+143.918 0 143.981 -0.33 144.11 -0.544 c
+144.245 -0.749 144.467 -0.852 144.771 -0.852 c
+145.094 -0.852 145.333 -0.69 145.491 -0.367 c
+145.491 1.646 l
+145.323 1.959 145.084 2.117 144.771 2.117 c
+144.467 2.117 144.245 2.014 144.11 1.808 c
+143.981 1.603 143.918 1.279 143.918 0.838 c
+h
+149.034 -1.352 m
+149.034 2.103 l
+148.519 2.103 l
+148.519 2.631 l
+149.034 2.631 l
+149.034 2.999 l
+149.041 3.429 149.155 3.763 149.372 3.998 c
+149.596 4.241 149.909 4.366 150.312 4.366 c
+150.46 4.366 150.599 4.344 150.738 4.308 c
+150.886 4.267 151.037 4.212 151.195 4.146 c
+151.077 3.572 l
+150.842 3.697 150.599 3.763 150.357 3.763 c
+150.111 3.763 149.938 3.693 149.842 3.558 c
+149.743 3.429 149.695 3.234 149.695 2.97 c
+149.695 2.631 l
+150.342 2.631 l
+150.342 2.103 l
+149.695 2.103 l
+149.695 -1.352 l
+h
+151.503 -1.352 -0.646 3.983 re
+153.252 -1.352 -0.646 5.644 re
+155.692 -1.425 m
+155.193 -1.425 154.811 -1.278 154.546 -0.984 c
+154.281 -0.69 154.15 -0.257 154.15 0.324 c
+154.15 0.794 l
+154.15 1.389 154.274 1.856 154.531 2.191 c
+154.796 2.532 155.156 2.705 155.619 2.705 c
+156.078 2.705 156.421 2.55 156.648 2.249 c
+156.883 1.956 157.005 1.492 157.015 0.867 c
+157.015 0.441 l
+154.796 0.441 l
+154.796 0.353 l
+154.796 -0.08 154.873 -0.393 155.031 -0.588 c
+155.197 -0.775 155.428 -0.866 155.722 -0.866 c
+155.916 -0.866 156.089 -0.833 156.236 -0.764 c
+156.383 -0.687 156.519 -0.569 156.648 -0.411 c
+156.986 -0.823 l
+156.699 -1.227 156.269 -1.425 155.692 -1.425 c
+155.619 2.147 m
+155.343 2.147 155.141 2.051 155.016 1.867 c
+154.888 1.68 154.815 1.389 154.796 1 c
+156.369 1 l
+156.369 1.088 l
+156.346 1.47 156.28 1.738 156.163 1.897 c
+156.045 2.062 155.862 2.147 155.619 2.147 c
+159.735 -0.338 m
+159.735 -0.191 159.679 -0.07 159.573 0.03 c
+159.463 0.125 159.257 0.243 158.956 0.383 c
+158.611 0.53 158.368 0.651 158.221 0.75 c
+158.073 0.857 157.963 0.975 157.897 1.103 c
+157.828 1.228 157.794 1.386 157.794 1.573 c
+157.794 1.897 157.913 2.165 158.148 2.382 c
+158.383 2.595 158.684 2.705 159.058 2.705 c
+159.44 2.705 159.75 2.591 159.985 2.367 c
+160.22 2.139 160.338 1.852 160.338 1.5 c
+159.691 1.5 l
+159.691 1.676 159.632 1.827 159.515 1.956 c
+159.397 2.08 159.243 2.147 159.058 2.147 c
+158.86 2.147 158.709 2.091 158.603 1.985 c
+158.493 1.885 158.441 1.754 158.441 1.588 c
+158.441 1.459 158.478 1.353 158.559 1.264 c
+158.636 1.183 158.827 1.081 159.132 0.956 c
+159.61 0.769 159.941 0.581 160.117 0.397 c
+160.294 0.221 160.381 -0.007 160.381 -0.278 c
+160.381 -0.631 160.257 -0.911 160.014 -1.117 c
+159.779 -1.323 159.463 -1.425 159.073 -1.425 c
+158.651 -1.425 158.312 -1.308 158.059 -1.072 c
+157.802 -0.83 157.677 -0.525 157.677 -0.161 c
+158.324 -0.161 l
+158.331 -0.389 158.401 -0.565 158.53 -0.69 c
+158.655 -0.808 158.838 -0.866 159.073 -0.866 c
+159.286 -0.866 159.448 -0.819 159.559 -0.72 c
+159.675 -0.625 159.735 -0.496 159.735 -0.338 c
+161.263 -0.999 m
+161.263 -0.881 161.296 -0.786 161.366 -0.706 c
+161.433 -0.628 161.535 -0.588 161.675 -0.588 c
+161.822 -0.588 161.929 -0.628 161.998 -0.706 c
+162.075 -0.786 162.116 -0.881 162.116 -0.999 c
+162.116 -1.109 162.075 -1.201 161.998 -1.278 c
+161.929 -1.356 161.822 -1.396 161.675 -1.396 c
+161.535 -1.396 161.433 -1.356 161.366 -1.278 c
+161.296 -1.201 161.263 -1.109 161.263 -0.999 c
+168.377 0.353 m
+168.348 -0.228 168.186 -0.668 167.893 -0.97 c
+167.598 -1.275 167.18 -1.425 166.644 -1.425 c
+166.114 -1.425 165.688 -1.227 165.364 -0.823 c
+165.048 -0.411 164.894 0.148 164.894 0.853 c
+164.894 1.823 l
+164.894 2.517 165.056 3.065 165.379 3.469 c
+165.703 3.87 166.147 4.072 166.717 4.072 c
+167.224 4.072 167.621 3.914 167.907 3.602 c
+168.19 3.296 168.348 2.856 168.377 2.278 c
+167.687 2.278 l
+167.658 2.72 167.562 3.032 167.407 3.219 c
+167.261 3.404 167.029 3.499 166.717 3.499 c
+166.342 3.499 166.059 3.352 165.865 3.057 c
+165.666 2.771 165.57 2.356 165.57 1.808 c
+165.57 0.823 l
+165.57 0.283 165.662 -0.132 165.85 -0.426 c
+166.033 -0.712 166.298 -0.852 166.644 -0.852 c
+166.996 -0.852 167.249 -0.764 167.407 -0.588 c
+167.562 -0.411 167.658 -0.099 167.687 0.353 c
+h
+171.215 -0.999 m
+170.998 -1.286 170.686 -1.425 170.274 -1.425 c
+169.91 -1.425 169.634 -1.304 169.451 -1.058 c
+169.275 -0.804 169.179 -0.44 169.171 0.03 c
+169.171 2.631 l
+169.819 2.631 l
+169.819 0.088 l
+169.819 -0.54 170.002 -0.852 170.377 -0.852 c
+170.777 -0.852 171.053 -0.675 171.2 -0.323 c
+171.2 2.631 l
+171.847 2.631 l
+171.847 -1.352 l
+171.23 -1.352 l
+h
+174.478 2.014 m
+174.39 2.033 174.291 2.043 174.184 2.043 c
+173.85 2.043 173.614 1.86 173.478 1.5 c
+173.478 -1.352 l
+172.832 -1.352 l
+172.832 2.631 l
+173.464 2.631 l
+173.478 2.22 l
+173.655 2.544 173.898 2.705 174.214 2.705 c
+174.32 2.705 174.408 2.683 174.478 2.646 c
+h
+176.771 2.014 m
+176.683 2.033 176.583 2.043 176.477 2.043 c
+176.142 2.043 175.907 1.86 175.772 1.5 c
+175.772 -1.352 l
+175.124 -1.352 l
+175.124 2.631 l
+175.756 2.631 l
+175.772 2.22 l
+175.947 2.544 176.19 2.705 176.506 2.705 c
+176.613 2.705 176.701 2.683 176.771 2.646 c
+h
+178.77 -1.425 m
+178.27 -1.425 177.888 -1.278 177.624 -0.984 c
+177.359 -0.69 177.227 -0.257 177.227 0.324 c
+177.227 0.794 l
+177.227 1.389 177.352 1.856 177.609 2.191 c
+177.873 2.532 178.233 2.705 178.696 2.705 c
+179.156 2.705 179.498 2.55 179.725 2.249 c
+179.961 1.956 180.082 1.492 180.093 0.867 c
+180.093 0.441 l
+177.873 0.441 l
+177.873 0.353 l
+177.873 -0.08 177.95 -0.393 178.108 -0.588 c
+178.274 -0.775 178.505 -0.866 178.8 -0.866 c
+178.994 -0.866 179.167 -0.833 179.314 -0.764 c
+179.461 -0.687 179.597 -0.569 179.725 -0.411 c
+180.063 -0.823 l
+179.777 -1.227 179.347 -1.425 178.77 -1.425 c
+178.696 2.147 m
+178.421 2.147 178.218 2.051 178.094 1.867 c
+177.965 1.68 177.892 1.389 177.873 1 c
+179.446 1 l
+179.446 1.088 l
+179.424 1.47 179.358 1.738 179.24 1.897 c
+179.122 2.062 178.939 2.147 178.696 2.147 c
+181.489 2.631 m
+181.504 2.191 l
+181.757 2.532 182.081 2.705 182.474 2.705 c
+183.18 2.705 183.536 2.234 183.548 1.294 c
+183.548 -1.352 l
+182.9 -1.352 l
+182.9 1.264 l
+182.9 1.577 182.845 1.798 182.739 1.926 c
+182.629 2.051 182.474 2.117 182.268 2.117 c
+182.11 2.117 181.964 2.062 181.827 1.956 c
+181.699 1.845 181.596 1.709 181.518 1.544 c
+181.518 -1.352 l
+180.872 -1.352 l
+180.872 2.631 l
+h
+185.37 3.587 m
+185.37 2.631 l
+185.973 2.631 l
+185.973 2.103 l
+185.37 2.103 l
+185.37 -0.367 l
+185.37 -0.525 185.392 -0.643 185.443 -0.72 c
+185.503 -0.801 185.59 -0.837 185.708 -0.837 c
+185.796 -0.837 185.884 -0.823 185.973 -0.793 c
+185.973 -1.352 l
+185.825 -1.4 185.671 -1.425 185.517 -1.425 c
+185.26 -1.425 185.065 -1.334 184.929 -1.146 c
+184.789 -0.962 184.724 -0.702 184.724 -0.367 c
+184.724 2.103 l
+184.121 2.103 l
+184.121 2.631 l
+184.724 2.631 l
+184.724 3.587 l
+h
+191.338 0.441 m
+191.338 -0.176 191.224 -0.643 190.999 -0.955 c
+190.783 -1.271 190.459 -1.425 190.029 -1.425 c
+189.607 -1.425 189.295 -1.246 189.089 -0.881 c
+189.059 -1.352 l
+188.456 -1.352 l
+188.456 4.293 l
+189.104 4.293 l
+189.104 2.191 l
+189.316 2.532 189.625 2.705 190.029 2.705 c
+190.459 2.705 190.783 2.547 190.999 2.234 c
+191.224 1.929 191.338 1.463 191.338 0.838 c
+h
+190.691 0.823 m
+190.691 1.294 190.621 1.625 190.485 1.823 c
+190.357 2.018 190.147 2.117 189.853 2.117 c
+189.519 2.117 189.269 1.933 189.104 1.573 c
+189.104 -0.309 l
+189.269 -0.672 189.522 -0.852 189.868 -0.852 c
+190.162 -0.852 190.371 -0.749 190.5 -0.544 c
+190.625 -0.338 190.691 -0.022 190.691 0.412 c
+h
+193.822 2.014 m
+193.734 2.033 193.634 2.043 193.528 2.043 c
+193.193 2.043 192.958 1.86 192.822 1.5 c
+192.822 -1.352 l
+192.175 -1.352 l
+192.175 2.631 l
+192.807 2.631 l
+192.822 2.22 l
+192.998 2.544 193.241 2.705 193.557 2.705 c
+193.663 2.705 193.752 2.683 193.822 2.646 c
+h
+196.364 -1.352 m
+196.325 -1.263 196.298 -1.117 196.291 -0.911 c
+196.056 -1.256 195.762 -1.425 195.409 -1.425 c
+195.045 -1.425 194.762 -1.33 194.557 -1.132 c
+194.359 -0.926 194.263 -0.639 194.263 -0.264 c
+194.263 0.136 194.399 0.456 194.675 0.691 c
+194.947 0.934 195.321 1.058 195.791 1.058 c
+196.277 1.058 l
+196.277 1.485 l
+196.277 1.721 196.221 1.885 196.115 1.985 c
+196.005 2.091 195.843 2.147 195.63 2.147 c
+195.431 2.147 195.269 2.087 195.145 1.97 c
+195.027 1.852 194.968 1.706 194.968 1.529 c
+194.322 1.529 l
+194.322 1.723 194.38 1.915 194.498 2.103 c
+194.623 2.286 194.785 2.433 194.983 2.544 c
+195.188 2.65 195.417 2.705 195.674 2.705 c
+196.075 2.705 196.379 2.602 196.585 2.396 c
+196.798 2.191 196.913 1.897 196.923 1.515 c
+196.923 -0.5 l
+196.923 -0.804 196.96 -1.069 197.041 -1.294 c
+197.041 -1.352 l
+h
+195.498 -0.837 m
+195.662 -0.837 195.814 -0.793 195.953 -0.706 c
+196.1 -0.617 196.207 -0.507 196.277 -0.367 c
+196.277 0.574 l
+195.909 0.574 l
+195.593 0.574 195.35 0.504 195.174 0.368 c
+194.997 0.239 194.91 0.052 194.91 -0.191 c
+194.91 -0.419 194.953 -0.584 195.042 -0.69 c
+195.13 -0.789 195.281 -0.837 195.498 -0.837 c
+198.54 2.631 m
+198.555 2.191 l
+198.808 2.532 199.132 2.705 199.525 2.705 c
+200.231 2.705 200.587 2.234 200.598 1.294 c
+200.598 -1.352 l
+199.951 -1.352 l
+199.951 1.264 l
+199.951 1.577 199.896 1.798 199.79 1.926 c
+199.679 2.051 199.525 2.117 199.319 2.117 c
+199.161 2.117 199.014 2.062 198.878 1.956 c
+198.75 1.845 198.647 1.709 198.569 1.544 c
+198.569 -1.352 l
+197.923 -1.352 l
+197.923 2.631 l
+h
+202.949 -0.866 m
+203.163 -0.866 203.336 -0.804 203.464 -0.675 c
+203.6 -0.54 203.674 -0.349 203.685 -0.103 c
+204.302 -0.103 l
+204.281 -0.484 204.144 -0.804 203.89 -1.058 c
+203.633 -1.304 203.321 -1.425 202.949 -1.425 c
+202.457 -1.425 202.083 -1.275 201.818 -0.97 c
+201.561 -0.658 201.436 -0.191 201.436 0.427 c
+201.436 0.867 l
+201.436 1.463 201.561 1.919 201.818 2.234 c
+202.083 2.547 202.457 2.705 202.949 2.705 c
+203.35 2.705 203.67 2.573 203.905 2.309 c
+204.148 2.051 204.281 1.706 204.302 1.264 c
+203.685 1.264 l
+203.663 1.559 203.589 1.779 203.464 1.926 c
+203.346 2.073 203.174 2.147 202.949 2.147 c
+202.656 2.147 202.44 2.047 202.303 1.852 c
+202.164 1.665 202.09 1.357 202.083 0.927 c
+202.083 0.412 l
+202.083 -0.058 202.149 -0.393 202.288 -0.588 c
+202.436 -0.775 202.656 -0.866 202.949 -0.866 c
+205.698 2.22 m
+205.952 2.544 206.272 2.705 206.654 2.705 c
+207.36 2.705 207.716 2.234 207.728 1.294 c
+207.728 -1.352 l
+207.08 -1.352 l
+207.08 1.264 l
+207.08 1.577 207.026 1.798 206.918 1.926 c
+206.808 2.051 206.654 2.117 206.448 2.117 c
+206.29 2.117 206.143 2.062 206.007 1.956 c
+205.879 1.845 205.775 1.709 205.698 1.544 c
+205.698 -1.352 l
+205.052 -1.352 l
+205.052 4.293 l
+205.698 4.293 l
+h
+208.727 -0.999 m
+208.727 -0.881 208.759 -0.786 208.829 -0.706 c
+208.896 -0.628 208.998 -0.588 209.139 -0.588 c
+209.285 -0.588 209.392 -0.628 209.461 -0.706 c
+209.538 -0.786 209.579 -0.881 209.579 -0.999 c
+209.579 -1.109 209.538 -1.201 209.461 -1.278 c
+209.392 -1.356 209.285 -1.396 209.139 -1.396 c
+208.998 -1.396 208.896 -1.356 208.829 -1.278 c
+208.759 -1.201 208.727 -1.109 208.727 -0.999 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 383.129 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 376.2903 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.807 l
+-1.896 -1.807 l
+-1.896 -1.263 l
+-2.142 -1.256 -2.359 -1.219 -2.543 -1.161 c
+-2.719 -1.102 -2.873 -1.024 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.034 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.477 -2.234 -0.514 c
+-2.138 -0.554 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.662 l
+-1.907 0.662 -1.926 0.666 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.757 -3.322 1.985 c
+-3.322 2.18 -3.289 2.345 -3.219 2.485 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.933 -2.645 3.003 -2.469 3.043 c
+-2.293 3.091 -2.102 3.12 -1.896 3.132 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.132 l
+-1.319 3.12 -1.128 3.091 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.621 -0.279 2.485 c
+-0.202 2.345 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.368 l
+-1.514 1.368 l
+-1.506 1.368 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.32 0.871 -0.213 0.743 -0.132 0.588 c
+-0.044 0.431 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.503 -2.113 2.485 -2.19 2.455 c
+-2.26 2.426 -2.319 2.386 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.191 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.838 -2.439 1.779 c
+-2.41 1.721 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.201 -0.837 -0.014 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.634 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.395 c
+5.284 -2.314 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.83 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.219 6.034 -1.219 c
+5.829 -1.219 5.644 -1.182 5.49 -1.102 c
+5.343 -1.014 5.215 -0.897 5.108 -0.749 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.019 4.888 1.235 c
+4.946 1.449 5.027 1.632 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.188 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.919 7.107 1.97 c
+7.115 2.029 7.122 2.077 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.414 7.938 -1.635 7.871 -1.822 c
+7.802 -2.006 7.699 -2.16 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.743 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.368 c
+5.88 1.279 5.835 1.162 5.799 1.015 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.514 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.293 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.603 l
+9.199 1.603 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.597 1.603 m
+13.053 1.603 l
+13.053 2.22 l
+13.641 2.22 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.22 l
+15.728 2.22 l
+15.728 1.603 l
+14.494 1.603 l
+14.494 -0.103 l
+14.494 -0.323 l
+14.501 -0.392 14.523 -0.455 14.552 -0.514 c
+14.589 -0.565 14.645 -0.61 14.714 -0.646 c
+14.791 -0.675 14.905 -0.69 15.052 -0.69 c
+15.188 -0.69 15.324 -0.687 15.464 -0.675 c
+15.599 -0.658 15.732 -0.631 15.861 -0.602 c
+15.861 -1.205 l
+15.78 -1.215 15.703 -1.23 15.626 -1.249 c
+15.545 -1.26 15.468 -1.267 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.3 14.938 -1.308 14.832 -1.308 c
+14.645 -1.308 14.483 -1.294 14.346 -1.263 c
+14.218 -1.227 14.104 -1.182 14.009 -1.132 c
+13.92 -1.084 13.847 -1.024 13.788 -0.955 c
+13.729 -0.878 13.685 -0.801 13.656 -0.72 c
+13.627 -0.631 13.604 -0.544 13.597 -0.455 c
+13.586 -0.359 13.582 -0.264 13.582 -0.176 c
+h
+23.295 -1.263 m
+23.283 -1.246 23.273 -1.215 23.265 -1.176 c
+23.265 -1.128 23.258 -1.08 23.25 -1.028 c
+23.25 -0.97 23.243 -0.911 23.236 -0.852 c
+23.236 -0.69 l
+23.118 -0.926 22.975 -1.095 22.809 -1.19 c
+22.641 -1.278 22.442 -1.323 22.207 -1.323 c
+22.009 -1.323 21.832 -1.278 21.677 -1.19 c
+21.519 -1.102 21.388 -0.98 21.28 -0.823 c
+21.182 -0.658 21.105 -0.467 21.045 -0.249 c
+20.994 -0.037 20.972 0.206 20.972 0.47 c
+20.972 0.736 20.994 0.975 21.045 1.191 c
+21.105 1.415 21.182 1.607 21.28 1.764 c
+21.388 1.919 21.519 2.043 21.677 2.132 c
+21.843 2.228 22.034 2.278 22.251 2.278 c
+22.346 2.278 22.442 2.264 22.531 2.234 c
+22.626 2.213 22.722 2.18 22.809 2.132 c
+22.898 2.08 22.975 2.018 23.044 1.941 c
+23.121 1.86 23.185 1.768 23.236 1.661 c
+23.236 1.75 l
+23.236 1.897 l
+23.236 2.058 l
+23.236 2.234 l
+23.236 3.514 l
+24.133 3.514 l
+24.133 -0.5 l
+24.133 -0.675 24.137 -0.833 24.147 -0.97 c
+24.154 -1.098 24.162 -1.198 24.162 -1.263 c
+h
+23.25 0.485 m
+23.25 0.721 23.225 0.912 23.177 1.058 c
+23.137 1.214 23.081 1.338 23.015 1.426 c
+22.957 1.515 22.886 1.573 22.809 1.603 c
+22.728 1.64 22.651 1.661 22.574 1.661 c
+22.475 1.661 22.383 1.636 22.296 1.588 c
+22.215 1.548 22.148 1.478 22.09 1.382 c
+22.038 1.283 21.994 1.162 21.957 1.015 c
+21.928 0.867 21.913 0.684 21.913 0.47 c
+21.913 0.078 21.964 -0.216 22.074 -0.411 c
+22.192 -0.61 22.354 -0.706 22.56 -0.706 c
+22.637 -0.706 22.714 -0.687 22.795 -0.646 c
+22.872 -0.61 22.946 -0.544 23.015 -0.455 c
+23.081 -0.367 23.137 -0.246 23.177 -0.087 c
+23.225 0.067 23.25 0.258 23.25 0.485 c
+27.281 -0.646 m
+28.413 -0.646 l
+28.413 -1.263 l
+25.106 -1.263 l
+25.106 -0.646 l
+26.371 -0.646 l
+26.371 1.603 l
+25.444 1.603 l
+25.444 2.22 l
+27.281 2.22 l
+h
+26.371 3.514 0.911 -0.676 re
+26.371 2.837 m
+30.886 1.603 m
+30.886 -1.263 l
+29.99 -1.263 l
+29.99 1.603 l
+29.167 1.603 l
+29.167 2.22 l
+29.99 2.22 l
+29.99 2.485 l
+29.99 2.61 30.005 2.742 30.034 2.882 c
+30.071 3.018 30.14 3.135 30.24 3.234 c
+30.346 3.341 30.489 3.429 30.666 3.499 c
+30.842 3.565 31.067 3.602 31.343 3.602 c
+31.555 3.602 31.754 3.591 31.931 3.572 c
+32.107 3.55 32.257 3.532 32.386 3.514 c
+32.386 2.926 l
+32.257 2.944 32.114 2.959 31.96 2.97 c
+31.802 2.977 31.651 2.984 31.504 2.984 c
+31.376 2.984 31.272 2.97 31.195 2.94 c
+31.114 2.911 31.052 2.87 31.004 2.822 c
+30.953 2.771 30.919 2.708 30.901 2.631 c
+30.89 2.562 30.886 2.485 30.886 2.396 c
+30.886 2.22 l
+32.313 2.22 l
+32.313 1.603 l
+h
+34.948 1.603 m
+34.948 -1.263 l
+34.051 -1.263 l
+34.051 1.603 l
+33.228 1.603 l
+33.228 2.22 l
+34.051 2.22 l
+34.051 2.485 l
+34.051 2.61 34.065 2.742 34.094 2.882 c
+34.132 3.018 34.202 3.135 34.3 3.234 c
+34.407 3.341 34.551 3.429 34.726 3.499 c
+34.903 3.565 35.127 3.602 35.403 3.602 c
+35.616 3.602 35.815 3.591 35.991 3.572 c
+36.168 3.55 36.318 3.532 36.446 3.514 c
+36.446 2.926 l
+36.318 2.944 36.174 2.959 36.02 2.97 c
+35.862 2.977 35.711 2.984 35.565 2.984 c
+35.436 2.984 35.333 2.97 35.256 2.94 c
+35.175 2.911 35.112 2.87 35.065 2.822 c
+35.013 2.771 34.98 2.708 34.962 2.631 c
+34.951 2.562 34.948 2.485 34.948 2.396 c
+34.948 2.22 l
+36.373 2.22 l
+36.373 1.603 l
+h
+42.175 -2.63 m
+42.175 3.514 l
+44.101 3.514 l
+44.101 2.897 l
+43.028 2.897 l
+43.028 -2.013 l
+44.101 -2.013 l
+44.101 -2.63 l
+h
+47.133 1.603 m
+47.133 -1.263 l
+46.236 -1.263 l
+46.236 1.603 l
+45.413 1.603 l
+45.413 2.22 l
+46.236 2.22 l
+46.236 2.485 l
+46.236 2.61 46.251 2.742 46.281 2.882 c
+46.317 3.018 46.387 3.135 46.486 3.234 c
+46.593 3.341 46.736 3.429 46.912 3.499 c
+47.089 3.565 47.313 3.602 47.588 3.602 c
+47.802 3.602 48 3.591 48.176 3.572 c
+48.353 3.55 48.504 3.532 48.632 3.514 c
+48.632 2.926 l
+48.504 2.944 48.361 2.959 48.205 2.97 c
+48.047 2.977 47.897 2.984 47.75 2.984 c
+47.621 2.984 47.519 2.97 47.441 2.94 c
+47.361 2.911 47.298 2.87 47.251 2.822 c
+47.199 2.771 47.166 2.708 47.147 2.631 c
+47.137 2.562 47.133 2.485 47.133 2.396 c
+47.133 2.22 l
+48.558 2.22 l
+48.558 1.603 l
+h
+51.649 -0.646 m
+52.781 -0.646 l
+52.781 -1.263 l
+49.473 -1.263 l
+49.473 -0.646 l
+50.738 -0.646 l
+50.738 1.603 l
+49.812 1.603 l
+49.812 2.22 l
+51.649 2.22 l
+h
+50.738 3.514 0.912 -0.676 re
+50.738 2.837 m
+55.71 -0.646 m
+56.841 -0.646 l
+56.841 -1.263 l
+53.535 -1.263 l
+53.535 -0.646 l
+54.798 -0.646 l
+54.798 2.897 l
+53.872 2.897 l
+53.872 3.514 l
+55.71 3.514 l
+h
+59.23 -1.323 m
+58.973 -1.323 58.745 -1.286 58.539 -1.219 c
+58.333 -1.142 58.157 -1.028 58.01 -0.881 c
+57.863 -0.727 57.745 -0.536 57.657 -0.309 c
+57.576 -0.084 57.539 0.181 57.539 0.485 c
+57.539 0.817 57.584 1.096 57.672 1.324 c
+57.768 1.559 57.896 1.742 58.054 1.881 c
+58.22 2.018 58.407 2.117 58.613 2.176 c
+58.819 2.242 59.028 2.278 59.245 2.278 c
+59.517 2.278 59.752 2.228 59.951 2.132 c
+60.157 2.043 60.321 1.912 60.45 1.735 c
+60.586 1.566 60.685 1.36 60.744 1.118 c
+60.81 0.882 60.847 0.618 60.847 0.324 c
+60.847 0.31 l
+58.48 0.31 l
+58.48 0.162 58.495 0.023 58.524 -0.103 c
+58.561 -0.231 58.617 -0.345 58.686 -0.44 c
+58.752 -0.529 58.837 -0.598 58.936 -0.646 c
+59.031 -0.698 59.145 -0.72 59.274 -0.72 c
+59.428 -0.72 59.569 -0.687 59.686 -0.617 c
+59.81 -0.55 59.899 -0.448 59.951 -0.309 c
+60.788 -0.382 l
+60.759 -0.481 60.704 -0.588 60.627 -0.706 c
+60.546 -0.816 60.443 -0.918 60.317 -1.014 c
+60.2 -1.102 60.046 -1.176 59.862 -1.234 c
+59.686 -1.294 59.473 -1.323 59.23 -1.323 c
+59.23 1.706 m
+59.141 1.706 59.054 1.691 58.966 1.661 c
+58.877 1.632 58.796 1.58 58.73 1.515 c
+58.661 1.445 58.602 1.357 58.554 1.25 c
+58.514 1.139 58.495 1.015 58.495 0.867 c
+59.965 0.867 l
+59.965 1.004 59.939 1.125 59.891 1.235 c
+59.851 1.341 59.796 1.43 59.729 1.5 c
+59.671 1.566 59.598 1.617 59.509 1.646 c
+59.421 1.684 59.326 1.706 59.23 1.706 c
+62.1 -2.63 m
+62.1 -2.013 l
+63.173 -2.013 l
+63.173 2.897 l
+62.1 2.897 l
+62.1 3.514 l
+64.026 3.514 l
+64.026 -2.63 l
+h
+f
+Q
+q 1 0 0 1 346.0048 361.4887 cm
+0 0 m
+0 0.264 -0.073 0.463 -0.22 0.603 c
+-0.359 0.75 -0.617 0.889 -0.999 1.029 c
+-1.374 1.165 -1.66 1.309 -1.866 1.455 c
+-2.065 1.602 -2.212 1.768 -2.308 1.955 c
+-2.406 2.15 -2.454 2.371 -2.454 2.616 c
+-2.454 3.036 -2.315 3.385 -2.028 3.66 c
+-1.745 3.932 -1.377 4.072 -0.926 4.072 c
+-0.613 4.072 -0.334 3.998 -0.087 3.851 c
+0.154 3.711 0.342 3.516 0.47 3.263 c
+0.607 3.017 0.676 2.749 0.676 2.454 c
+0 2.454 l
+0 2.786 -0.081 3.042 -0.235 3.219 c
+-0.393 3.403 -0.625 3.499 -0.926 3.499 c
+-1.19 3.499 -1.404 3.418 -1.558 3.263 c
+-1.705 3.117 -1.778 2.903 -1.778 2.631 c
+-1.778 2.404 -1.701 2.213 -1.543 2.058 c
+-1.377 1.911 -1.124 1.771 -0.779 1.646 c
+-0.261 1.477 0.111 1.268 0.339 1.014 c
+0.574 0.757 0.691 0.426 0.691 0.015 c
+0.691 -0.426 0.548 -0.779 0.265 -1.043 c
+-0.022 -1.301 -0.404 -1.426 -0.881 -1.426 c
+-1.198 -1.426 -1.484 -1.356 -1.749 -1.22 c
+-2.013 -1.084 -2.227 -0.893 -2.381 -0.646 c
+-2.528 -0.405 -2.601 -0.118 -2.601 0.206 c
+-1.925 0.206 l
+-1.925 -0.129 -1.833 -0.389 -1.646 -0.573 c
+-1.462 -0.76 -1.205 -0.852 -0.881 -0.852 c
+-0.588 -0.852 -0.367 -0.779 -0.22 -0.632 c
+-0.073 -0.478 0 -0.264 0 0 c
+2.117 2.219 m
+2.371 2.543 2.691 2.705 3.072 2.705 c
+3.778 2.705 4.135 2.234 4.146 1.294 c
+4.146 -1.352 l
+3.499 -1.352 l
+3.499 1.264 l
+3.499 1.577 3.444 1.797 3.337 1.926 c
+3.227 2.051 3.072 2.117 2.866 2.117 c
+2.708 2.117 2.562 2.062 2.425 1.955 c
+2.297 1.845 2.194 1.709 2.117 1.544 c
+2.117 -1.352 l
+1.47 -1.352 l
+1.47 4.293 l
+2.117 4.293 l
+h
+4.969 0.823 m
+4.969 1.4 5.104 1.856 5.38 2.19 c
+5.663 2.532 6.034 2.705 6.498 2.705 c
+6.957 2.705 7.325 2.535 7.599 2.205 c
+7.882 1.881 8.03 1.434 8.04 0.867 c
+8.04 0.441 l
+8.04 -0.129 7.898 -0.584 7.614 -0.926 c
+7.339 -1.261 6.972 -1.426 6.512 -1.426 c
+6.049 -1.426 5.678 -1.264 5.395 -0.941 c
+5.12 -0.61 4.976 -0.169 4.969 0.382 c
+h
+5.615 0.441 m
+5.615 0.037 5.692 -0.279 5.85 -0.515 c
+6.016 -0.75 6.236 -0.867 6.512 -0.867 c
+7.078 -0.867 7.372 -0.455 7.394 0.368 c
+7.394 0.823 l
+7.394 1.224 7.31 1.544 7.144 1.779 c
+6.986 2.022 6.77 2.146 6.498 2.146 c
+6.232 2.146 6.016 2.022 5.85 1.779 c
+5.692 1.544 5.615 1.224 5.615 0.823 c
+h
+11.848 -0.206 m
+12.451 2.631 l
+13.097 2.631 l
+12.112 -1.352 l
+11.597 -1.352 l
+10.819 1.5 l
+10.07 -1.352 l
+9.54 -1.352 l
+8.584 2.631 l
+9.216 2.631 l
+9.834 -0.133 l
+10.569 2.631 l
+11.084 2.631 l
+h
+16.875 -0.867 m
+17.088 -0.867 17.261 -0.804 17.39 -0.676 c
+17.525 -0.54 17.598 -0.349 17.61 -0.103 c
+18.227 -0.103 l
+18.205 -0.485 18.069 -0.804 17.816 -1.058 c
+17.558 -1.305 17.246 -1.426 16.875 -1.426 c
+16.382 -1.426 16.008 -1.275 15.743 -0.97 c
+15.486 -0.658 15.36 -0.191 15.36 0.426 c
+15.36 0.867 l
+15.36 1.463 15.486 1.918 15.743 2.234 c
+16.008 2.547 16.382 2.705 16.875 2.705 c
+17.276 2.705 17.596 2.572 17.831 2.308 c
+18.073 2.051 18.205 1.706 18.227 1.264 c
+17.61 1.264 l
+17.588 1.558 17.515 1.779 17.39 1.926 c
+17.272 2.072 17.099 2.146 16.875 2.146 c
+16.581 2.146 16.364 2.047 16.228 1.852 c
+16.089 1.665 16.015 1.357 16.008 0.926 c
+16.008 0.412 l
+16.008 -0.058 16.073 -0.393 16.214 -0.588 c
+16.36 -0.775 16.581 -0.867 16.875 -0.867 c
+19.624 2.219 m
+19.877 2.543 20.197 2.705 20.579 2.705 c
+21.284 2.705 21.641 2.234 21.652 1.294 c
+21.652 -1.352 l
+21.005 -1.352 l
+21.005 1.264 l
+21.005 1.577 20.95 1.797 20.844 1.926 c
+20.733 2.051 20.579 2.117 20.374 2.117 c
+20.216 2.117 20.068 2.062 19.932 1.955 c
+19.804 1.845 19.701 1.709 19.624 1.544 c
+19.624 -1.352 l
+18.977 -1.352 l
+18.977 4.293 l
+19.624 4.293 l
+h
+24.651 -1.352 m
+24.611 -1.264 24.584 -1.117 24.577 -0.912 c
+24.342 -1.257 24.048 -1.426 23.695 -1.426 c
+23.331 -1.426 23.048 -1.33 22.843 -1.132 c
+22.645 -0.926 22.549 -0.64 22.549 -0.264 c
+22.549 0.136 22.685 0.455 22.961 0.69 c
+23.232 0.933 23.607 1.058 24.077 1.058 c
+24.563 1.058 l
+24.563 1.484 l
+24.563 1.72 24.507 1.885 24.401 1.984 c
+24.291 2.091 24.129 2.146 23.915 2.146 c
+23.717 2.146 23.555 2.087 23.431 1.97 c
+23.313 1.852 23.254 1.706 23.254 1.529 c
+22.608 1.529 l
+22.608 1.723 22.666 1.914 22.784 2.102 c
+22.909 2.286 23.071 2.433 23.269 2.543 c
+23.475 2.65 23.703 2.705 23.96 2.705 c
+24.36 2.705 24.665 2.602 24.871 2.396 c
+25.084 2.19 25.199 1.897 25.209 1.514 c
+25.209 -0.5 l
+25.209 -0.804 25.246 -1.07 25.327 -1.294 c
+25.327 -1.352 l
+h
+23.784 -0.838 m
+23.948 -0.838 24.1 -0.794 24.239 -0.706 c
+24.386 -0.617 24.493 -0.507 24.563 -0.368 c
+24.563 0.573 l
+24.195 0.573 l
+23.879 0.573 23.636 0.503 23.46 0.368 c
+23.283 0.239 23.196 0.052 23.196 -0.191 c
+23.196 -0.419 23.24 -0.584 23.328 -0.691 c
+23.416 -0.79 23.566 -0.838 23.784 -0.838 c
+26.826 2.631 m
+26.841 2.19 l
+27.094 2.532 27.418 2.705 27.811 2.705 c
+28.517 2.705 28.873 2.234 28.884 1.294 c
+28.884 -1.352 l
+28.237 -1.352 l
+28.237 1.264 l
+28.237 1.577 28.182 1.797 28.075 1.926 c
+27.965 2.051 27.811 2.117 27.605 2.117 c
+27.447 2.117 27.3 2.062 27.164 1.955 c
+27.036 1.845 26.932 1.709 26.855 1.544 c
+26.855 -1.352 l
+26.209 -1.352 l
+26.209 2.631 l
+h
+29.722 0.823 m
+29.722 1.44 29.832 1.904 30.06 2.219 c
+30.284 2.543 30.618 2.705 31.059 2.705 c
+31.46 2.705 31.765 2.529 31.971 2.176 c
+32.015 2.631 l
+32.603 2.631 l
+32.603 -1.396 l
+32.603 -1.885 32.474 -2.263 32.22 -2.528 c
+31.964 -2.793 31.611 -2.925 31.162 -2.925 c
+30.964 -2.925 30.743 -2.874 30.501 -2.778 c
+30.255 -2.679 30.074 -2.558 29.957 -2.41 c
+30.221 -1.97 l
+30.487 -2.234 30.784 -2.367 31.118 -2.367 c
+31.655 -2.367 31.931 -2.072 31.942 -1.484 c
+31.942 -0.956 l
+31.736 -1.271 31.434 -1.426 31.044 -1.426 c
+30.633 -1.426 30.31 -1.275 30.074 -0.97 c
+29.847 -0.658 29.729 -0.206 29.722 0.382 c
+h
+30.383 0.441 m
+30.383 0 30.446 -0.33 30.574 -0.544 c
+30.699 -0.75 30.916 -0.852 31.221 -0.852 c
+31.545 -0.852 31.784 -0.687 31.942 -0.353 c
+31.942 1.631 l
+31.773 1.955 31.534 2.117 31.221 2.117 c
+30.927 2.117 30.71 2.014 30.574 1.808 c
+30.446 1.602 30.383 1.278 30.383 0.838 c
+h
+34.984 -1.426 m
+34.485 -1.426 34.102 -1.278 33.837 -0.985 c
+33.573 -0.691 33.44 -0.257 33.44 0.324 c
+33.44 0.794 l
+33.44 1.389 33.566 1.856 33.823 2.19 c
+34.088 2.532 34.448 2.705 34.911 2.705 c
+35.37 2.705 35.711 2.55 35.94 2.249 c
+36.175 1.955 36.296 1.492 36.307 0.867 c
+36.307 0.441 l
+34.088 0.441 l
+34.088 0.353 l
+34.088 -0.081 34.165 -0.393 34.323 -0.588 c
+34.488 -0.775 34.72 -0.867 35.013 -0.867 c
+35.208 -0.867 35.381 -0.834 35.528 -0.764 c
+35.675 -0.687 35.811 -0.569 35.94 -0.411 c
+36.278 -0.823 l
+35.991 -1.228 35.561 -1.426 34.984 -1.426 c
+34.911 2.146 m
+34.635 2.146 34.433 2.051 34.308 1.866 c
+34.179 1.679 34.106 1.389 34.088 1 c
+35.661 1 l
+35.661 1.087 l
+35.638 1.47 35.572 1.738 35.455 1.897 c
+35.337 2.062 35.154 2.146 34.911 2.146 c
+39.027 -0.338 m
+39.027 -0.191 38.971 -0.07 38.865 0.029 c
+38.755 0.125 38.549 0.243 38.248 0.382 c
+37.901 0.53 37.66 0.651 37.512 0.75 c
+37.365 0.856 37.255 0.974 37.189 1.103 c
+37.119 1.228 37.086 1.386 37.086 1.573 c
+37.086 1.897 37.203 2.165 37.439 2.381 c
+37.674 2.595 37.976 2.705 38.35 2.705 c
+38.732 2.705 39.041 2.591 39.276 2.367 c
+39.511 2.139 39.629 1.852 39.629 1.5 c
+38.982 1.5 l
+38.982 1.675 38.923 1.827 38.806 1.955 c
+38.688 2.08 38.534 2.146 38.35 2.146 c
+38.152 2.146 38.001 2.091 37.895 1.984 c
+37.784 1.885 37.733 1.753 37.733 1.588 c
+37.733 1.459 37.77 1.353 37.851 1.264 c
+37.928 1.183 38.119 1.081 38.424 0.956 c
+38.901 0.769 39.233 0.58 39.409 0.397 c
+39.584 0.22 39.673 -0.008 39.673 -0.279 c
+39.673 -0.632 39.548 -0.912 39.306 -1.117 c
+39.071 -1.323 38.755 -1.426 38.365 -1.426 c
+37.942 -1.426 37.604 -1.309 37.351 -1.073 c
+37.093 -0.831 36.968 -0.525 36.968 -0.162 c
+37.615 -0.162 l
+37.623 -0.389 37.693 -0.565 37.821 -0.691 c
+37.946 -0.808 38.13 -0.867 38.365 -0.867 c
+38.578 -0.867 38.74 -0.819 38.85 -0.721 c
+38.967 -0.625 39.027 -0.496 39.027 -0.338 c
+45.112 0.441 m
+45.112 -0.176 44.997 -0.643 44.774 -0.956 c
+44.557 -1.271 44.234 -1.426 43.804 -1.426 c
+43.381 -1.426 43.069 -1.246 42.863 -0.881 c
+42.834 -1.352 l
+42.231 -1.352 l
+42.231 4.293 l
+42.877 4.293 l
+42.877 2.19 l
+43.091 2.532 43.399 2.705 43.804 2.705 c
+44.234 2.705 44.557 2.547 44.774 2.234 c
+44.997 1.929 45.112 1.463 45.112 0.838 c
+h
+44.465 0.823 m
+44.465 1.294 44.395 1.625 44.259 1.823 c
+44.131 2.018 43.921 2.117 43.627 2.117 c
+43.293 2.117 43.043 1.933 42.877 1.573 c
+42.877 -0.309 l
+43.043 -0.673 43.297 -0.852 43.642 -0.852 c
+43.936 -0.852 44.145 -0.75 44.274 -0.544 c
+44.399 -0.338 44.465 -0.022 44.465 0.412 c
+h
+47.376 -1.426 m
+46.875 -1.426 46.493 -1.278 46.229 -0.985 c
+45.965 -0.691 45.832 -0.257 45.832 0.324 c
+45.832 0.794 l
+45.832 1.389 45.957 1.856 46.214 2.19 c
+46.479 2.532 46.839 2.705 47.302 2.705 c
+47.761 2.705 48.103 2.55 48.331 2.249 c
+48.566 1.955 48.687 1.492 48.698 0.867 c
+48.698 0.441 l
+46.479 0.441 l
+46.479 0.353 l
+46.479 -0.081 46.556 -0.393 46.714 -0.588 c
+46.879 -0.775 47.11 -0.867 47.405 -0.867 c
+47.6 -0.867 47.773 -0.834 47.919 -0.764 c
+48.066 -0.687 48.203 -0.569 48.331 -0.411 c
+48.669 -0.823 l
+48.382 -1.228 47.952 -1.426 47.376 -1.426 c
+47.302 2.146 m
+47.027 2.146 46.824 2.051 46.699 1.866 c
+46.57 1.679 46.497 1.389 46.479 1 c
+48.051 1 l
+48.051 1.087 l
+48.029 1.47 47.964 1.738 47.846 1.897 c
+47.728 2.062 47.544 2.146 47.302 2.146 c
+50.33 3.587 m
+50.33 2.631 l
+50.933 2.631 l
+50.933 2.102 l
+50.33 2.102 l
+50.33 -0.368 l
+50.33 -0.525 50.352 -0.643 50.403 -0.721 c
+50.462 -0.801 50.551 -0.838 50.668 -0.838 c
+50.757 -0.838 50.844 -0.823 50.933 -0.794 c
+50.933 -1.352 l
+50.786 -1.4 50.632 -1.426 50.477 -1.426 c
+50.22 -1.426 50.025 -1.334 49.889 -1.147 c
+49.749 -0.962 49.683 -0.702 49.683 -0.368 c
+49.683 2.102 l
+49.08 2.102 l
+49.08 2.631 l
+49.683 2.631 l
+49.683 3.587 l
+h
+54.666 -0.206 m
+55.268 2.631 l
+55.916 2.631 l
+54.931 -1.352 l
+54.416 -1.352 l
+53.637 1.5 l
+52.887 -1.352 l
+52.359 -1.352 l
+51.403 2.631 l
+52.035 2.631 l
+52.652 -0.133 l
+53.387 2.631 l
+53.902 2.631 l
+h
+58.017 -1.426 m
+57.518 -1.426 57.136 -1.278 56.871 -0.985 c
+56.606 -0.691 56.474 -0.257 56.474 0.324 c
+56.474 0.794 l
+56.474 1.389 56.599 1.856 56.856 2.19 c
+57.121 2.532 57.481 2.705 57.944 2.705 c
+58.403 2.705 58.745 2.55 58.973 2.249 c
+59.208 1.955 59.33 1.492 59.34 0.867 c
+59.34 0.441 l
+57.121 0.441 l
+57.121 0.353 l
+57.121 -0.081 57.198 -0.393 57.356 -0.588 c
+57.522 -0.775 57.753 -0.867 58.047 -0.867 c
+58.242 -0.867 58.414 -0.834 58.561 -0.764 c
+58.709 -0.687 58.844 -0.569 58.973 -0.411 c
+59.311 -0.823 l
+59.025 -1.228 58.595 -1.426 58.017 -1.426 c
+57.944 2.146 m
+57.668 2.146 57.466 2.051 57.342 1.866 c
+57.213 1.679 57.14 1.389 57.121 1 c
+58.694 1 l
+58.694 1.087 l
+58.672 1.47 58.605 1.738 58.488 1.897 c
+58.37 2.062 58.187 2.146 57.944 2.146 c
+61.545 -1.426 m
+61.046 -1.426 60.664 -1.278 60.399 -0.985 c
+60.134 -0.691 60.002 -0.257 60.002 0.324 c
+60.002 0.794 l
+60.002 1.389 60.126 1.856 60.384 2.19 c
+60.649 2.532 61.009 2.705 61.472 2.705 c
+61.931 2.705 62.273 2.55 62.501 2.249 c
+62.736 1.955 62.857 1.492 62.868 0.867 c
+62.868 0.441 l
+60.649 0.441 l
+60.649 0.353 l
+60.649 -0.081 60.726 -0.393 60.884 -0.588 c
+61.049 -0.775 61.281 -0.867 61.575 -0.867 c
+61.77 -0.867 61.942 -0.834 62.089 -0.764 c
+62.236 -0.687 62.372 -0.569 62.501 -0.411 c
+62.839 -0.823 l
+62.553 -1.228 62.123 -1.426 61.545 -1.426 c
+61.472 2.146 m
+61.196 2.146 60.994 2.051 60.87 1.866 c
+60.741 1.679 60.667 1.389 60.649 1 c
+62.221 1 l
+62.221 1.087 l
+62.2 1.47 62.133 1.738 62.015 1.897 c
+61.898 2.062 61.714 2.146 61.472 2.146 c
+64.265 2.631 m
+64.279 2.19 l
+64.533 2.532 64.856 2.705 65.25 2.705 c
+65.955 2.705 66.312 2.234 66.322 1.294 c
+66.322 -1.352 l
+65.676 -1.352 l
+65.676 1.264 l
+65.676 1.577 65.62 1.797 65.514 1.926 c
+65.404 2.051 65.25 2.117 65.044 2.117 c
+64.886 2.117 64.739 2.062 64.602 1.955 c
+64.475 1.845 64.371 1.709 64.294 1.544 c
+64.294 -1.352 l
+63.648 -1.352 l
+63.648 2.631 l
+h
+f
+Q
+q 1 0 0 1 417.8096 361.8857 cm
+0 0 m
+0.354 2.234 l
+1.353 2.234 l
+0.53 -1.749 l
+-0.338 -1.749 l
+-0.897 0.559 l
+-1.454 -1.749 l
+-2.322 -1.749 l
+-3.145 2.234 l
+-2.146 2.234 l
+-1.793 0 l
+-1.263 2.234 l
+-0.529 2.234 l
+h
+1.75 0.368 m
+1.75 0.974 1.889 1.448 2.176 1.793 c
+2.459 2.135 2.852 2.308 3.352 2.308 c
+3.859 2.308 4.256 2.135 4.543 1.793 c
+4.825 1.448 4.969 0.974 4.969 0.368 c
+4.969 0.103 l
+4.969 -0.496 4.825 -0.966 4.543 -1.309 c
+4.256 -1.654 3.859 -1.823 3.352 -1.823 c
+2.841 -1.823 2.444 -1.654 2.161 -1.309 c
+1.885 -0.966 1.75 -0.492 1.75 0.118 c
+h
+2.793 0.103 m
+2.793 -0.603 2.977 -0.956 3.352 -0.956 c
+3.705 -0.956 3.896 -0.661 3.925 -0.073 c
+3.925 0.368 l
+3.925 0.727 3.874 0.999 3.778 1.176 c
+3.679 1.352 3.535 1.44 3.352 1.44 c
+3.176 1.44 3.036 1.352 2.941 1.176 c
+2.841 0.999 2.793 0.727 2.793 0.368 c
+h
+7.57 1.22 m
+7.232 1.249 l
+6.945 1.249 6.754 1.124 6.659 0.881 c
+6.659 -1.749 l
+5.615 -1.749 l
+5.615 2.234 l
+6.585 2.234 l
+6.615 1.793 l
+6.781 2.135 7.012 2.308 7.306 2.308 c
+7.423 2.308 7.516 2.286 7.585 2.249 c
+h
+9.437 -0.22 m
+9.158 -0.53 l
+9.158 -1.749 l
+8.115 -1.749 l
+8.115 3.896 l
+9.158 3.896 l
+9.158 0.852 l
+9.276 1.043 l
+9.996 2.234 l
+11.246 2.234 l
+10.084 0.588 l
+11.348 -1.749 l
+10.157 -1.749 l
+h
+12.803 -1.749 -1.044 3.983 re
+11.716 3.263 m
+11.716 3.418 11.763 3.547 11.863 3.645 c
+11.969 3.752 12.105 3.807 12.274 3.807 c
+12.451 3.807 12.586 3.752 12.686 3.645 c
+12.792 3.547 12.848 3.418 12.848 3.263 c
+12.848 3.094 12.792 2.959 12.686 2.851 c
+12.586 2.753 12.451 2.705 12.274 2.705 c
+12.105 2.705 11.969 2.753 11.863 2.851 c
+11.763 2.959 11.716 3.094 11.716 3.263 c
+14.567 2.234 m
+14.596 1.837 l
+14.832 2.15 15.133 2.308 15.508 2.308 c
+16.191 2.308 16.544 1.826 16.566 0.867 c
+16.566 -1.749 l
+15.522 -1.749 l
+15.522 0.794 l
+15.522 1.018 15.486 1.18 15.42 1.278 c
+15.35 1.374 15.232 1.426 15.067 1.426 c
+14.88 1.426 14.733 1.33 14.626 1.147 c
+14.626 -1.749 l
+13.582 -1.749 l
+13.582 2.234 l
+h
+17.184 0.368 m
+17.184 1.014 17.301 1.5 17.536 1.822 c
+17.771 2.146 18.103 2.308 18.536 2.308 c
+18.889 2.308 19.161 2.165 19.359 1.881 c
+19.404 2.234 l
+20.344 2.234 l
+20.344 -1.749 l
+20.344 -2.256 20.2 -2.646 19.917 -2.911 c
+19.631 -3.183 19.227 -3.322 18.698 -3.322 c
+18.47 -3.322 18.234 -3.278 17.993 -3.19 c
+17.757 -3.102 17.581 -2.988 17.463 -2.851 c
+17.816 -2.132 l
+17.912 -2.238 18.04 -2.323 18.198 -2.381 c
+18.352 -2.448 18.5 -2.485 18.639 -2.485 c
+18.874 -2.485 19.04 -2.425 19.138 -2.308 c
+19.246 -2.198 19.3 -2.021 19.3 -1.779 c
+19.3 -1.426 l
+19.102 -1.691 18.845 -1.823 18.521 -1.823 c
+18.099 -1.823 17.771 -1.661 17.536 -1.338 c
+17.309 -1.007 17.191 -0.536 17.184 0.073 c
+h
+18.228 0.103 m
+18.228 -0.272 18.275 -0.54 18.375 -0.706 c
+18.47 -0.875 18.624 -0.956 18.83 -0.956 c
+19.043 -0.956 19.201 -0.879 19.3 -0.721 c
+19.3 1.176 l
+19.19 1.341 19.036 1.426 18.83 1.426 c
+18.624 1.426 18.47 1.341 18.375 1.176 c
+18.275 1.007 18.228 0.738 18.228 0.368 c
+h
+22.696 0.368 m
+22.696 1.014 22.803 1.5 23.019 1.822 c
+23.244 2.146 23.566 2.308 23.989 2.308 c
+24.302 2.308 24.555 2.175 24.754 1.911 c
+24.754 3.896 l
+25.812 3.896 l
+25.812 -1.749 l
+24.856 -1.749 l
+24.813 -1.338 l
+24.596 -1.661 24.32 -1.823 23.989 -1.823 c
+23.578 -1.823 23.258 -1.668 23.034 -1.353 c
+22.817 -1.029 22.703 -0.559 22.696 0.058 c
+h
+23.74 0.103 m
+23.74 -0.291 23.776 -0.566 23.857 -0.721 c
+23.946 -0.879 24.092 -0.956 24.298 -0.956 c
+24.503 -0.956 24.655 -0.864 24.754 -0.676 c
+24.754 1.132 l
+24.655 1.326 24.503 1.426 24.298 1.426 c
+24.1 1.426 23.96 1.345 23.872 1.191 c
+23.784 1.043 23.74 0.771 23.74 0.382 c
+h
+27.664 -1.749 -1.044 3.983 re
+26.576 3.263 m
+26.576 3.418 26.624 3.547 26.724 3.645 c
+26.83 3.752 26.966 3.807 27.135 3.807 c
+27.312 3.807 27.447 3.752 27.547 3.645 c
+27.653 3.547 27.708 3.418 27.708 3.263 c
+27.708 3.094 27.653 2.959 27.547 2.851 c
+27.447 2.753 27.312 2.705 27.135 2.705 c
+26.966 2.705 26.83 2.753 26.724 2.851 c
+26.624 2.959 26.576 3.094 26.576 3.263 c
+30.427 1.22 m
+30.09 1.249 l
+29.803 1.249 29.612 1.124 29.517 0.881 c
+29.517 -1.749 l
+28.472 -1.749 l
+28.472 2.234 l
+29.442 2.234 l
+29.472 1.793 l
+29.637 2.135 29.868 2.308 30.163 2.308 c
+30.28 2.308 30.373 2.286 30.442 2.249 c
+h
+32.5 -1.823 m
+31.971 -1.823 31.552 -1.668 31.25 -1.353 c
+30.957 -1.029 30.809 -0.569 30.809 0.029 c
+30.809 0.338 l
+30.809 0.962 30.946 1.448 31.221 1.793 c
+31.493 2.135 31.886 2.308 32.397 2.308 c
+32.897 2.308 33.268 2.146 33.515 1.822 c
+33.768 1.5 33.9 1.022 33.911 0.397 c
+33.911 -0.103 l
+31.838 -0.103 l
+31.857 -0.397 31.919 -0.613 32.029 -0.75 c
+32.147 -0.889 32.328 -0.956 32.574 -0.956 c
+32.915 -0.956 33.205 -0.838 33.44 -0.603 c
+33.852 -1.235 l
+33.724 -1.411 33.536 -1.554 33.294 -1.661 c
+33.047 -1.768 32.783 -1.823 32.5 -1.823 c
+31.853 0.617 m
+32.883 0.617 l
+32.883 0.721 l
+32.883 0.956 32.842 1.132 32.765 1.249 c
+32.694 1.374 32.566 1.44 32.382 1.44 c
+32.206 1.44 32.074 1.371 31.985 1.234 c
+31.905 1.106 31.861 0.9 31.853 0.617 c
+35.94 -0.956 m
+36.233 -0.956 36.384 -0.761 36.395 -0.368 c
+37.365 -0.368 l
+37.365 -0.802 37.233 -1.154 36.968 -1.426 c
+36.704 -1.691 36.366 -1.823 35.954 -1.823 c
+35.443 -1.823 35.05 -1.668 34.778 -1.353 c
+34.514 -1.029 34.375 -0.559 34.367 0.058 c
+34.367 0.382 l
+34.367 1.007 34.499 1.484 34.764 1.808 c
+35.036 2.138 35.433 2.308 35.954 2.308 c
+36.384 2.308 36.726 2.168 36.983 1.897 c
+37.237 1.621 37.365 1.238 37.365 0.75 c
+36.395 0.75 l
+36.395 0.962 36.355 1.132 36.278 1.249 c
+36.208 1.374 36.091 1.44 35.925 1.44 c
+35.748 1.44 35.62 1.374 35.543 1.249 c
+35.462 1.12 35.418 0.871 35.41 0.5 c
+35.41 0.088 l
+35.41 -0.235 35.425 -0.463 35.455 -0.588 c
+35.491 -0.717 35.547 -0.808 35.616 -0.867 c
+35.694 -0.926 35.8 -0.956 35.94 -0.956 c
+39.173 3.204 m
+39.173 2.234 l
+39.703 2.234 l
+39.703 1.44 l
+39.173 1.44 l
+39.173 -0.53 l
+39.173 -0.687 39.192 -0.794 39.233 -0.852 c
+39.28 -0.912 39.364 -0.941 39.482 -0.941 c
+39.589 -0.941 39.673 -0.933 39.732 -0.912 c
+39.732 -1.72 l
+39.555 -1.786 39.364 -1.823 39.159 -1.823 c
+38.483 -1.823 38.138 -1.437 38.13 -0.661 c
+38.13 1.44 l
+37.674 1.44 l
+37.674 2.234 l
+38.13 2.234 l
+38.13 3.204 l
+h
+40.056 0.368 m
+40.056 0.974 40.195 1.448 40.482 1.793 c
+40.765 2.135 41.157 2.308 41.658 2.308 c
+42.165 2.308 42.562 2.135 42.848 1.793 c
+43.131 1.448 43.274 0.974 43.274 0.368 c
+43.274 0.103 l
+43.274 -0.496 43.131 -0.966 42.848 -1.309 c
+42.562 -1.654 42.165 -1.823 41.658 -1.823 c
+41.147 -1.823 40.75 -1.654 40.467 -1.309 c
+40.191 -0.966 40.056 -0.492 40.056 0.118 c
+h
+41.099 0.103 m
+41.099 -0.603 41.282 -0.956 41.658 -0.956 c
+42.011 -0.956 42.202 -0.661 42.231 -0.073 c
+42.231 0.368 l
+42.231 0.727 42.179 0.999 42.084 1.176 c
+41.984 1.352 41.841 1.44 41.658 1.44 c
+41.481 1.44 41.342 1.352 41.246 1.176 c
+41.147 0.999 41.099 0.727 41.099 0.368 c
+h
+45.876 1.22 m
+45.538 1.249 l
+45.251 1.249 45.06 1.124 44.965 0.881 c
+44.965 -1.749 l
+43.921 -1.749 l
+43.921 2.234 l
+44.891 2.234 l
+44.92 1.793 l
+45.086 2.135 45.317 2.308 45.612 2.308 c
+45.729 2.308 45.822 2.286 45.891 2.249 c
+h
+47.773 -0.015 m
+48.301 2.234 l
+49.404 2.234 l
+48.096 -2.352 l
+47.898 -3.017 47.53 -3.352 46.994 -3.352 c
+46.865 -3.352 46.722 -3.329 46.567 -3.293 c
+46.567 -2.469 l
+46.685 -2.485 l
+46.85 -2.485 46.971 -2.448 47.052 -2.381 c
+47.129 -2.323 47.192 -2.213 47.243 -2.058 c
+47.316 -1.793 l
+46.171 2.234 l
+47.287 2.234 l
+h
+f
+Q
+q 1 0 0 1 471.5352 360.1369 cm
+0 0 m
+-0.04 0.087 -0.066 0.235 -0.073 0.44 c
+-0.309 0.095 -0.603 -0.074 -0.956 -0.074 c
+-1.319 -0.074 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.102 0.712 -2.102 1.087 c
+-2.102 1.488 -1.966 1.807 -1.691 2.042 c
+-1.419 2.285 -1.044 2.41 -0.574 2.41 c
+-0.088 2.41 l
+-0.088 2.836 l
+-0.088 3.071 -0.143 3.237 -0.25 3.336 c
+-0.36 3.443 -0.522 3.498 -0.735 3.498 c
+-0.933 3.498 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.985 3.266 -1.866 3.453 c
+-1.742 3.638 -1.58 3.785 -1.382 3.895 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.291 4.056 0.015 3.954 0.22 3.748 c
+0.434 3.542 0.547 3.248 0.559 2.865 c
+0.559 0.852 l
+0.559 0.547 0.595 0.282 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.514 m
+-0.702 0.514 -0.551 0.558 -0.412 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.984 c
+-0.088 1.925 l
+-0.455 1.925 l
+-0.771 1.925 -1.014 1.855 -1.191 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.235 0.562 -1.084 0.514 -0.867 0.514 c
+2.175 3.983 m
+2.19 3.542 l
+2.444 3.884 2.767 4.056 3.16 4.056 c
+3.865 4.056 4.222 3.586 4.233 2.645 c
+4.233 0 l
+3.586 0 l
+3.586 2.616 l
+3.586 2.929 3.532 3.149 3.424 3.278 c
+3.314 3.403 3.16 3.469 2.954 3.469 c
+2.797 3.469 2.649 3.414 2.514 3.307 c
+2.385 3.197 2.282 3.061 2.205 2.896 c
+2.205 0 l
+1.558 0 l
+1.558 3.983 l
+h
+5.071 2.175 m
+5.071 2.782 5.182 3.248 5.409 3.571 c
+5.644 3.895 5.972 4.056 6.394 4.056 c
+6.776 4.056 7.073 3.898 7.291 3.586 c
+7.291 5.644 l
+7.937 5.644 l
+7.937 0 l
+7.349 0 l
+7.306 0.426 l
+7.1 0.091 6.795 -0.074 6.394 -0.074 c
+5.982 -0.074 5.659 0.081 5.424 0.396 c
+5.189 0.72 5.071 1.176 5.071 1.764 c
+h
+5.718 1.793 m
+5.718 1.352 5.78 1.022 5.909 0.808 c
+6.045 0.602 6.265 0.5 6.57 0.5 c
+6.894 0.5 7.133 0.661 7.291 0.984 c
+7.291 2.998 l
+7.121 3.31 6.882 3.469 6.57 3.469 c
+6.265 3.469 6.045 3.366 5.909 3.16 c
+5.78 2.954 5.718 2.63 5.718 2.19 c
+h
+f
+Q
+q 1 0 0 1 483.8532 361.2093 cm
+0 0 m
+0 0.088 -0.044 0.166 -0.133 0.235 c
+-0.221 0.312 -0.408 0.416 -0.691 0.544 c
+-1.125 0.721 -1.422 0.9 -1.588 1.087 c
+-1.746 1.272 -1.823 1.503 -1.823 1.779 c
+-1.823 2.12 -1.702 2.404 -1.455 2.631 c
+-1.202 2.866 -0.864 2.984 -0.441 2.984 c
+-0.011 2.984 0.338 2.87 0.602 2.646 c
+0.867 2.419 0.999 2.117 0.999 1.735 c
+-0.044 1.735 l
+-0.044 2.058 -0.184 2.22 -0.456 2.22 c
+-0.566 2.22 -0.655 2.183 -0.721 2.117 c
+-0.79 2.047 -0.823 1.948 -0.823 1.823 c
+-0.823 1.735 -0.786 1.654 -0.706 1.588 c
+-0.628 1.529 -0.449 1.434 -0.162 1.309 c
+0.268 1.151 0.565 0.974 0.735 0.779 c
+0.911 0.592 0.999 0.342 0.999 0.029 c
+0.999 -0.324 0.867 -0.61 0.602 -0.823 c
+0.338 -1.039 -0.011 -1.147 -0.441 -1.147 c
+-0.736 -1.147 -0.996 -1.091 -1.22 -0.985 c
+-1.448 -0.867 -1.625 -0.706 -1.75 -0.5 c
+-1.867 -0.293 -1.926 -0.073 -1.926 0.162 c
+-0.941 0.162 l
+-0.941 -0.025 -0.904 -0.162 -0.823 -0.249 c
+-0.736 -0.338 -0.603 -0.382 -0.427 -0.382 c
+-0.144 -0.382 0 -0.257 0 0 c
+2.866 3.881 m
+2.866 2.911 l
+3.395 2.911 l
+3.395 2.117 l
+2.866 2.117 l
+2.866 0.147 l
+2.866 -0.01 2.884 -0.118 2.925 -0.176 c
+2.973 -0.235 3.057 -0.264 3.175 -0.264 c
+3.281 -0.264 3.366 -0.257 3.424 -0.235 c
+3.424 -1.043 l
+3.248 -1.109 3.057 -1.147 2.851 -1.147 c
+2.175 -1.147 1.83 -0.76 1.822 0.015 c
+1.822 2.117 l
+1.367 2.117 l
+1.367 2.911 l
+1.822 2.911 l
+1.822 3.881 l
+h
+5.85 -1.072 m
+5.82 -1.014 5.791 -0.912 5.762 -0.764 c
+5.574 -1.022 5.325 -1.147 5.012 -1.147 c
+4.677 -1.147 4.398 -1.039 4.174 -0.823 c
+3.958 -0.598 3.85 -0.309 3.85 0.044 c
+3.85 0.456 3.983 0.772 4.247 1 c
+4.512 1.235 4.895 1.353 5.394 1.353 c
+5.718 1.353 l
+5.718 1.675 l
+5.718 1.852 5.681 1.974 5.614 2.043 c
+5.556 2.12 5.468 2.161 5.35 2.161 c
+5.093 2.161 4.968 2.007 4.968 1.706 c
+3.925 1.706 l
+3.925 2.076 4.06 2.381 4.336 2.616 c
+4.608 2.859 4.957 2.984 5.379 2.984 c
+5.82 2.984 6.158 2.866 6.393 2.631 c
+6.636 2.404 6.761 2.08 6.761 1.661 c
+6.761 -0.206 l
+6.761 -0.551 6.809 -0.819 6.908 -1.014 c
+6.908 -1.072 l
+h
+5.247 -0.324 m
+5.354 -0.324 5.446 -0.305 5.527 -0.264 c
+5.614 -0.216 5.677 -0.158 5.718 -0.088 c
+5.718 0.735 l
+5.468 0.735 l
+5.292 0.735 5.148 0.676 5.041 0.559 c
+4.943 0.449 4.895 0.302 4.895 0.118 c
+4.895 -0.176 5.012 -0.324 5.247 -0.324 c
+7.378 1.044 m
+7.378 1.691 7.496 2.176 7.731 2.499 c
+7.966 2.822 8.297 2.984 8.731 2.984 c
+9.084 2.984 9.356 2.841 9.554 2.558 c
+9.598 2.911 l
+10.539 2.911 l
+10.539 -1.072 l
+10.539 -1.579 10.395 -1.97 10.113 -2.234 c
+9.826 -2.506 9.422 -2.645 8.893 -2.645 c
+8.665 -2.645 8.43 -2.601 8.187 -2.514 c
+7.952 -2.425 7.775 -2.311 7.658 -2.175 c
+8.01 -1.455 l
+8.106 -1.562 8.235 -1.646 8.393 -1.705 c
+8.548 -1.771 8.694 -1.808 8.834 -1.808 c
+9.069 -1.808 9.234 -1.749 9.334 -1.631 c
+9.44 -1.521 9.495 -1.344 9.495 -1.103 c
+9.495 -0.75 l
+9.296 -1.014 9.04 -1.147 8.716 -1.147 c
+8.294 -1.147 7.966 -0.985 7.731 -0.661 c
+7.503 -0.33 7.386 0.14 7.378 0.75 c
+h
+8.422 0.779 m
+8.422 0.405 8.47 0.136 8.569 -0.029 c
+8.665 -0.198 8.819 -0.279 9.025 -0.279 c
+9.238 -0.279 9.396 -0.202 9.495 -0.044 c
+9.495 1.852 l
+9.385 2.018 9.231 2.103 9.025 2.103 c
+8.819 2.103 8.665 2.018 8.569 1.852 c
+8.47 1.683 8.422 1.415 8.422 1.044 c
+h
+12.391 -1.072 -1.044 3.983 re
+11.303 3.94 m
+11.303 4.094 11.351 4.223 11.451 4.322 c
+11.557 4.428 11.692 4.484 11.862 4.484 c
+12.038 4.484 12.174 4.428 12.273 4.322 c
+12.38 4.223 12.435 4.094 12.435 3.94 c
+12.435 3.771 12.38 3.635 12.273 3.528 c
+12.174 3.429 12.038 3.381 11.862 3.381 c
+11.692 3.381 11.557 3.429 11.451 3.528 c
+11.351 3.635 11.303 3.771 11.303 3.94 c
+14.154 2.911 m
+14.184 2.514 l
+14.42 2.826 14.721 2.984 15.095 2.984 c
+15.779 2.984 16.132 2.502 16.154 1.544 c
+16.154 -1.072 l
+15.11 -1.072 l
+15.11 1.47 l
+15.11 1.694 15.073 1.856 15.008 1.955 c
+14.938 2.051 14.82 2.103 14.655 2.103 c
+14.468 2.103 14.32 2.007 14.214 1.823 c
+14.214 -1.072 l
+13.17 -1.072 l
+13.17 2.911 l
+h
+16.772 1.044 m
+16.772 1.691 16.889 2.176 17.124 2.499 c
+17.359 2.822 17.69 2.984 18.123 2.984 c
+18.476 2.984 18.748 2.841 18.947 2.558 c
+18.991 2.911 l
+19.931 2.911 l
+19.931 -1.072 l
+19.931 -1.579 19.788 -1.97 19.505 -2.234 c
+19.218 -2.506 18.815 -2.645 18.285 -2.645 c
+18.057 -2.645 17.822 -2.601 17.58 -2.514 c
+17.344 -2.425 17.168 -2.311 17.051 -2.175 c
+17.404 -1.455 l
+17.499 -1.562 17.628 -1.646 17.786 -1.705 c
+17.94 -1.771 18.087 -1.808 18.227 -1.808 c
+18.462 -1.808 18.627 -1.749 18.726 -1.631 c
+18.833 -1.521 18.888 -1.344 18.888 -1.103 c
+18.888 -0.75 l
+18.69 -1.014 18.432 -1.147 18.109 -1.147 c
+17.686 -1.147 17.359 -0.985 17.124 -0.661 c
+16.897 -0.33 16.779 0.14 16.772 0.75 c
+h
+17.815 0.779 m
+17.815 0.405 17.863 0.136 17.962 -0.029 c
+18.057 -0.198 18.212 -0.279 18.418 -0.279 c
+18.63 -0.279 18.788 -0.202 18.888 -0.044 c
+18.888 1.852 l
+18.778 2.018 18.623 2.103 18.418 2.103 c
+18.212 2.103 18.057 2.018 17.962 1.852 c
+17.863 1.683 17.815 1.415 17.815 1.044 c
+h
+24.312 -1.072 m
+24.282 -1.014 24.253 -0.912 24.224 -0.764 c
+24.036 -1.022 23.787 -1.147 23.474 -1.147 c
+23.139 -1.147 22.86 -1.039 22.636 -0.823 c
+22.42 -0.598 22.312 -0.309 22.312 0.044 c
+22.312 0.456 22.445 0.772 22.709 1 c
+22.975 1.235 23.357 1.353 23.856 1.353 c
+24.18 1.353 l
+24.18 1.675 l
+24.18 1.852 24.143 1.974 24.076 2.043 c
+24.018 2.12 23.93 2.161 23.812 2.161 c
+23.555 2.161 23.43 2.007 23.43 1.706 c
+22.386 1.706 l
+22.386 2.076 22.522 2.381 22.798 2.616 c
+23.07 2.859 23.419 2.984 23.841 2.984 c
+24.282 2.984 24.621 2.866 24.856 2.631 c
+25.098 2.404 25.223 2.08 25.223 1.661 c
+25.223 -0.206 l
+25.223 -0.551 25.271 -0.819 25.37 -1.014 c
+25.37 -1.072 l
+h
+23.709 -0.324 m
+23.816 -0.324 23.908 -0.305 23.989 -0.264 c
+24.076 -0.216 24.139 -0.158 24.18 -0.088 c
+24.18 0.735 l
+23.93 0.735 l
+23.754 0.735 23.61 0.676 23.503 0.559 c
+23.405 0.449 23.357 0.302 23.357 0.118 c
+23.357 -0.176 23.474 -0.324 23.709 -0.324 c
+27.928 1.897 m
+27.59 1.926 l
+27.303 1.926 27.112 1.801 27.016 1.558 c
+27.016 -1.072 l
+25.973 -1.072 l
+25.973 2.911 l
+26.943 2.911 l
+26.973 2.469 l
+27.137 2.812 27.369 2.984 27.663 2.984 c
+27.781 2.984 27.873 2.962 27.943 2.926 c
+h
+30 -1.147 m
+29.471 -1.147 29.052 -0.992 28.751 -0.676 c
+28.457 -0.353 28.31 0.107 28.31 0.706 c
+28.31 1.014 l
+28.31 1.639 28.446 2.124 28.722 2.469 c
+28.994 2.812 29.387 2.984 29.898 2.984 c
+30.397 2.984 30.769 2.822 31.014 2.499 c
+31.268 2.176 31.401 1.698 31.411 1.073 c
+31.411 0.574 l
+29.339 0.574 l
+29.358 0.279 29.42 0.063 29.53 -0.073 c
+29.647 -0.213 29.828 -0.279 30.074 -0.279 c
+30.416 -0.279 30.706 -0.162 30.941 0.073 c
+31.353 -0.559 l
+31.224 -0.735 31.037 -0.878 30.794 -0.985 c
+30.548 -1.091 30.283 -1.147 30 -1.147 c
+29.354 1.294 m
+30.382 1.294 l
+30.382 1.397 l
+30.382 1.632 30.343 1.808 30.265 1.926 c
+30.195 2.051 30.067 2.117 29.883 2.117 c
+29.707 2.117 29.574 2.047 29.486 1.911 c
+29.405 1.783 29.361 1.577 29.354 1.294 c
+33.881 -1.072 m
+33.852 -1.014 33.822 -0.912 33.793 -0.764 c
+33.605 -1.022 33.356 -1.147 33.043 -1.147 c
+32.709 -1.147 32.429 -1.039 32.205 -0.823 c
+31.989 -0.598 31.882 -0.309 31.882 0.044 c
+31.882 0.456 32.014 0.772 32.279 1 c
+32.543 1.235 32.926 1.353 33.425 1.353 c
+33.749 1.353 l
+33.749 1.675 l
+33.749 1.852 33.712 1.974 33.646 2.043 c
+33.587 2.12 33.499 2.161 33.381 2.161 c
+33.124 2.161 32.999 2.007 32.999 1.706 c
+31.955 1.706 l
+31.955 2.076 32.091 2.381 32.367 2.616 c
+32.639 2.859 32.988 2.984 33.41 2.984 c
+33.852 2.984 34.189 2.866 34.424 2.631 c
+34.667 2.404 34.792 2.08 34.792 1.661 c
+34.792 -0.206 l
+34.792 -0.551 34.84 -0.819 34.939 -1.014 c
+34.939 -1.072 l
+h
+33.279 -0.324 m
+33.385 -0.324 33.477 -0.305 33.558 -0.264 c
+33.646 -0.216 33.709 -0.158 33.749 -0.088 c
+33.749 0.735 l
+33.499 0.735 l
+33.323 0.735 33.179 0.676 33.073 0.559 c
+32.973 0.449 32.926 0.302 32.926 0.118 c
+32.926 -0.176 33.043 -0.324 33.279 -0.324 c
+f
+Q
+q 1 0 0 1 519.5421 360.4896 cm
+0 0 m
+0 0.118 0.033 0.213 0.103 0.294 c
+0.169 0.371 0.272 0.411 0.412 0.411 c
+0.559 0.411 0.665 0.371 0.735 0.294 c
+0.812 0.213 0.852 0.118 0.852 0 c
+0.852 -0.111 0.812 -0.202 0.735 -0.279 c
+0.665 -0.357 0.559 -0.397 0.412 -0.397 c
+0.272 -0.397 0.169 -0.357 0.103 -0.279 c
+0.033 -0.202 0 -0.111 0 0 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 356.035 234.667 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 349.197 cm
+0 0 m
+0 -0.188 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.776 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.206 -1.278 -1.243 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.148 c
+-2.55 -0.228 -2.506 -0.302 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.675 c
+-1.977 0.683 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.042 c
+-0.804 2.991 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.882 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.263 -0.941 1.205 c
+-0.756 1.146 -0.595 1.066 -0.455 0.97 c
+-0.32 0.87 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.499 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.558 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.754 5.964 -1.86 6.064 -1.941 c
+6.159 -2.029 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.029 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.324 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.89 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.244 1.932 5.376 2.05 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.969 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.164 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.882 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.636 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.5 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.499 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.462 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.874 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.383 c
+6.924 -0.294 6.978 -0.177 7.019 -0.03 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.116 l
+14.494 3.116 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.456 14.552 -0.515 c
+14.589 -0.566 14.645 -0.611 14.714 -0.647 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.688 15.464 -0.676 c
+15.599 -0.659 15.732 -0.632 15.861 -0.603 c
+15.861 -1.206 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.279 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.085 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.801 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.456 c
+13.586 -0.36 13.582 -0.265 13.582 -0.177 c
+h
+23.295 -1.264 m
+23.283 -1.246 23.273 -1.216 23.265 -1.176 c
+23.265 -1.129 23.258 -1.081 23.25 -1.029 c
+23.25 -0.971 23.243 -0.912 23.236 -0.853 c
+23.236 -0.691 l
+23.118 -0.927 22.975 -1.095 22.809 -1.191 c
+22.641 -1.279 22.442 -1.324 22.207 -1.324 c
+22.009 -1.324 21.832 -1.279 21.677 -1.191 c
+21.519 -1.103 21.388 -0.981 21.28 -0.823 c
+21.182 -0.659 21.105 -0.467 21.045 -0.25 c
+20.994 -0.037 20.972 0.205 20.972 0.47 c
+20.972 0.735 20.994 0.974 21.045 1.19 c
+21.105 1.415 21.182 1.606 21.28 1.764 c
+21.388 1.918 21.519 2.043 21.677 2.131 c
+21.843 2.227 22.034 2.278 22.251 2.278 c
+22.346 2.278 22.442 2.263 22.531 2.234 c
+22.626 2.212 22.722 2.179 22.809 2.131 c
+22.898 2.08 22.975 2.017 23.044 1.94 c
+23.121 1.859 23.185 1.768 23.236 1.66 c
+23.236 1.749 l
+23.236 1.896 l
+23.236 2.057 l
+23.236 2.234 l
+23.236 3.513 l
+24.133 3.513 l
+24.133 -0.5 l
+24.133 -0.676 24.137 -0.834 24.147 -0.971 c
+24.154 -1.099 24.162 -1.199 24.162 -1.264 c
+h
+23.25 0.484 m
+23.25 0.72 23.225 0.911 23.177 1.058 c
+23.137 1.213 23.081 1.338 23.015 1.425 c
+22.957 1.514 22.886 1.573 22.809 1.602 c
+22.728 1.639 22.651 1.66 22.574 1.66 c
+22.475 1.66 22.383 1.635 22.296 1.587 c
+22.215 1.547 22.148 1.477 22.09 1.381 c
+22.038 1.282 21.994 1.161 21.957 1.014 c
+21.928 0.867 21.913 0.683 21.913 0.47 c
+21.913 0.077 21.964 -0.217 22.074 -0.412 c
+22.192 -0.611 22.354 -0.706 22.56 -0.706 c
+22.637 -0.706 22.714 -0.688 22.795 -0.647 c
+22.872 -0.611 22.946 -0.544 23.015 -0.456 c
+23.081 -0.368 23.137 -0.246 23.177 -0.088 c
+23.225 0.066 23.25 0.257 23.25 0.484 c
+27.281 -0.647 m
+28.413 -0.647 l
+28.413 -1.264 l
+25.106 -1.264 l
+25.106 -0.647 l
+26.371 -0.647 l
+26.371 1.602 l
+25.444 1.602 l
+25.444 2.219 l
+27.281 2.219 l
+h
+26.371 3.513 0.911 -0.676 re
+26.371 2.836 m
+30.886 1.602 m
+30.886 -1.264 l
+29.99 -1.264 l
+29.99 1.602 l
+29.167 1.602 l
+29.167 2.219 l
+29.99 2.219 l
+29.99 2.484 l
+29.99 2.609 30.005 2.741 30.034 2.881 c
+30.071 3.017 30.14 3.135 30.24 3.233 c
+30.346 3.34 30.489 3.428 30.666 3.498 c
+30.842 3.564 31.067 3.601 31.343 3.601 c
+31.555 3.601 31.754 3.59 31.931 3.572 c
+32.107 3.549 32.257 3.532 32.386 3.513 c
+32.386 2.925 l
+32.257 2.944 32.114 2.958 31.96 2.969 c
+31.802 2.977 31.651 2.984 31.504 2.984 c
+31.376 2.984 31.272 2.969 31.195 2.94 c
+31.114 2.91 31.052 2.869 31.004 2.822 c
+30.953 2.771 30.919 2.708 30.901 2.631 c
+30.89 2.561 30.886 2.484 30.886 2.396 c
+30.886 2.219 l
+32.313 2.219 l
+32.313 1.602 l
+h
+34.948 1.602 m
+34.948 -1.264 l
+34.051 -1.264 l
+34.051 1.602 l
+33.228 1.602 l
+33.228 2.219 l
+34.051 2.219 l
+34.051 2.484 l
+34.051 2.609 34.065 2.741 34.094 2.881 c
+34.132 3.017 34.202 3.135 34.3 3.233 c
+34.407 3.34 34.551 3.428 34.726 3.498 c
+34.903 3.564 35.127 3.601 35.403 3.601 c
+35.616 3.601 35.815 3.59 35.991 3.572 c
+36.168 3.549 36.318 3.532 36.446 3.513 c
+36.446 2.925 l
+36.318 2.944 36.174 2.958 36.02 2.969 c
+35.862 2.977 35.711 2.984 35.565 2.984 c
+35.436 2.984 35.333 2.969 35.256 2.94 c
+35.175 2.91 35.112 2.869 35.065 2.822 c
+35.013 2.771 34.98 2.708 34.962 2.631 c
+34.951 2.561 34.948 2.484 34.948 2.396 c
+34.948 2.219 l
+36.373 2.219 l
+36.373 1.602 l
+h
+42.013 0.837 1.867 -0.793 re
+42.013 0.044 m
+46.075 0.837 1.866 -0.793 re
+46.075 0.044 m
+52.648 -0.25 m
+52.648 -0.42 52.608 -0.57 52.531 -0.706 c
+52.461 -0.834 52.359 -0.948 52.222 -1.044 c
+52.093 -1.132 51.932 -1.202 51.737 -1.249 c
+51.55 -1.297 51.333 -1.324 51.091 -1.324 c
+50.863 -1.324 50.664 -1.309 50.488 -1.279 c
+50.312 -1.249 50.154 -1.202 50.017 -1.132 c
+49.878 -1.055 49.768 -0.956 49.68 -0.838 c
+49.591 -0.721 49.521 -0.574 49.473 -0.397 c
+50.283 -0.279 l
+50.3 -0.379 50.33 -0.456 50.37 -0.515 c
+50.418 -0.574 50.477 -0.618 50.547 -0.647 c
+50.613 -0.676 50.694 -0.702 50.782 -0.721 c
+50.87 -0.732 50.973 -0.736 51.091 -0.736 c
+51.186 -0.736 51.282 -0.732 51.37 -0.721 c
+51.458 -0.702 51.536 -0.676 51.605 -0.647 c
+51.671 -0.618 51.723 -0.58 51.752 -0.53 c
+51.789 -0.482 51.81 -0.42 51.81 -0.339 c
+51.81 -0.243 51.781 -0.169 51.723 -0.118 c
+51.671 -0.071 51.605 -0.03 51.517 0 c
+51.428 0.037 51.318 0.066 51.193 0.087 c
+51.076 0.118 50.944 0.147 50.796 0.176 c
+50.657 0.213 50.518 0.253 50.37 0.294 c
+50.231 0.341 50.106 0.404 49.988 0.484 c
+49.878 0.562 49.79 0.661 49.724 0.779 c
+49.654 0.897 49.62 1.047 49.62 1.234 c
+49.62 1.389 49.65 1.532 49.709 1.66 c
+49.775 1.797 49.87 1.911 49.988 1.999 c
+50.113 2.087 50.271 2.153 50.458 2.204 c
+50.642 2.252 50.855 2.278 51.091 2.278 c
+51.274 2.278 51.451 2.256 51.619 2.219 c
+51.785 2.19 51.932 2.135 52.061 2.057 c
+52.186 1.988 52.296 1.889 52.384 1.764 c
+52.473 1.646 52.531 1.502 52.56 1.338 c
+51.767 1.263 l
+51.744 1.341 51.715 1.404 51.679 1.455 c
+51.638 1.514 51.59 1.558 51.532 1.587 c
+51.48 1.624 51.418 1.65 51.34 1.66 c
+51.26 1.668 51.179 1.675 51.091 1.675 c
+50.873 1.675 50.712 1.646 50.605 1.587 c
+50.495 1.536 50.443 1.448 50.443 1.323 c
+50.443 1.242 50.462 1.18 50.503 1.132 c
+50.551 1.08 50.613 1.043 50.694 1.014 c
+50.782 0.985 50.877 0.955 50.988 0.926 c
+51.094 0.904 51.216 0.881 51.355 0.852 c
+51.509 0.823 51.667 0.783 51.825 0.735 c
+51.98 0.683 52.12 0.621 52.237 0.544 c
+52.355 0.463 52.45 0.36 52.531 0.235 c
+52.608 0.106 52.648 -0.055 52.648 -0.25 c
+54.21 1.602 m
+53.666 1.602 l
+53.666 2.219 l
+54.254 2.219 l
+54.534 3.116 l
+55.107 3.116 l
+55.107 2.219 l
+56.342 2.219 l
+56.342 1.602 l
+55.107 1.602 l
+55.107 -0.103 l
+55.107 -0.324 l
+55.114 -0.393 55.137 -0.456 55.166 -0.515 c
+55.203 -0.566 55.258 -0.611 55.328 -0.647 c
+55.405 -0.676 55.519 -0.691 55.665 -0.691 c
+55.802 -0.691 55.937 -0.688 56.077 -0.676 c
+56.213 -0.659 56.346 -0.632 56.474 -0.603 c
+56.474 -1.206 l
+56.394 -1.216 56.316 -1.231 56.239 -1.249 c
+56.158 -1.261 56.081 -1.268 56.004 -1.279 c
+55.923 -1.286 55.835 -1.294 55.739 -1.294 c
+55.651 -1.301 55.552 -1.309 55.445 -1.309 c
+55.258 -1.309 55.096 -1.294 54.96 -1.264 c
+54.831 -1.228 54.717 -1.183 54.622 -1.132 c
+54.534 -1.085 54.46 -1.025 54.401 -0.956 c
+54.343 -0.879 54.299 -0.801 54.269 -0.721 c
+54.24 -0.632 54.218 -0.544 54.21 -0.456 c
+54.2 -0.36 54.196 -0.265 54.196 -0.177 c
+h
+58.628 -1.324 m
+58.458 -1.324 58.308 -1.301 58.172 -1.264 c
+58.044 -1.216 57.929 -1.147 57.834 -1.058 c
+57.745 -0.971 57.676 -0.864 57.628 -0.736 c
+57.576 -0.599 57.554 -0.449 57.554 -0.279 c
+57.554 -0.073 57.587 0.095 57.657 0.235 c
+57.724 0.382 57.819 0.492 57.936 0.573 c
+58.061 0.661 58.204 0.723 58.363 0.764 c
+58.528 0.801 58.705 0.827 58.892 0.837 c
+59.612 0.852 l
+59.612 1.028 l
+59.612 1.146 59.601 1.249 59.583 1.338 c
+59.561 1.425 59.528 1.492 59.48 1.543 c
+59.44 1.602 59.392 1.639 59.333 1.66 c
+59.274 1.679 59.208 1.691 59.141 1.691 c
+59.072 1.691 59.01 1.679 58.951 1.66 c
+58.9 1.65 58.852 1.624 58.804 1.587 c
+58.763 1.558 58.73 1.506 58.701 1.44 c
+58.679 1.381 58.664 1.301 58.657 1.205 c
+57.716 1.249 l
+57.745 1.396 57.79 1.532 57.848 1.66 c
+57.915 1.786 58.01 1.896 58.127 1.984 c
+58.245 2.08 58.385 2.153 58.554 2.204 c
+58.73 2.252 58.936 2.278 59.172 2.278 c
+59.612 2.278 59.943 2.167 60.171 1.955 c
+60.406 1.749 60.523 1.44 60.523 1.028 c
+60.523 -0.235 l
+60.523 -0.456 l
+60.531 -0.515 60.546 -0.57 60.568 -0.618 c
+60.586 -0.659 60.616 -0.691 60.656 -0.721 c
+60.693 -0.742 60.744 -0.75 60.803 -0.75 c
+60.869 -0.75 60.939 -0.746 61.009 -0.736 c
+61.009 -1.22 l
+60.95 -1.231 60.895 -1.243 60.847 -1.249 c
+60.807 -1.261 60.766 -1.268 60.729 -1.279 c
+60.689 -1.286 60.645 -1.294 60.597 -1.294 c
+60.546 -1.301 60.487 -1.309 60.421 -1.309 c
+60.193 -1.309 60.028 -1.257 59.921 -1.147 c
+59.81 -1.029 59.748 -0.864 59.729 -0.647 c
+59.715 -0.647 l
+59.646 -0.757 59.575 -0.853 59.509 -0.941 c
+59.44 -1.022 59.363 -1.088 59.274 -1.147 c
+59.186 -1.206 59.087 -1.249 58.981 -1.279 c
+58.881 -1.309 58.763 -1.324 58.628 -1.324 c
+59.612 0.353 m
+59.186 0.338 l
+59.087 0.338 58.995 0.33 58.906 0.323 c
+58.826 0.312 58.76 0.286 58.701 0.249 c
+58.642 0.209 58.591 0.151 58.554 0.073 c
+58.514 0.004 58.495 -0.088 58.495 -0.206 c
+58.495 -0.375 58.528 -0.497 58.598 -0.574 c
+58.664 -0.655 58.763 -0.691 58.892 -0.691 c
+58.998 -0.691 59.098 -0.669 59.186 -0.618 c
+59.282 -0.57 59.363 -0.507 59.421 -0.426 c
+59.488 -0.349 59.538 -0.262 59.569 -0.162 c
+59.598 -0.055 59.612 0.058 59.612 0.176 c
+h
+63.261 -2.66 m
+63.045 -2.66 62.854 -2.635 62.688 -2.587 c
+62.519 -2.547 62.379 -2.484 62.262 -2.396 c
+62.144 -2.315 62.045 -2.22 61.967 -2.103 c
+61.898 -1.985 61.85 -1.856 61.821 -1.72 c
+62.717 -1.617 l
+62.754 -1.754 62.824 -1.86 62.923 -1.941 c
+63.019 -2.029 63.143 -2.072 63.291 -2.072 c
+63.379 -2.072 63.459 -2.058 63.54 -2.029 c
+63.618 -1.999 63.688 -1.945 63.746 -1.867 c
+63.805 -1.797 63.849 -1.706 63.879 -1.588 c
+63.916 -1.47 63.937 -1.324 63.937 -1.147 c
+63.937 -0.956 l
+63.937 -0.89 63.941 -0.831 63.952 -0.779 c
+63.952 -0.588 l
+63.937 -0.588 l
+63.839 -0.816 63.695 -0.977 63.511 -1.073 c
+63.324 -1.172 63.118 -1.22 62.894 -1.22 c
+62.688 -1.22 62.505 -1.183 62.35 -1.103 c
+62.203 -1.014 62.075 -0.897 61.967 -0.75 c
+61.869 -0.595 61.795 -0.412 61.747 -0.206 c
+61.696 0.008 61.674 0.243 61.674 0.5 c
+61.674 0.771 61.696 1.018 61.747 1.234 c
+61.807 1.448 61.887 1.631 61.998 1.778 c
+62.104 1.932 62.236 2.05 62.394 2.131 c
+62.549 2.219 62.736 2.263 62.952 2.263 c
+63.048 2.263 63.147 2.252 63.247 2.234 c
+63.342 2.212 63.43 2.179 63.511 2.131 c
+63.6 2.08 63.677 2.017 63.746 1.94 c
+63.824 1.859 63.886 1.768 63.937 1.66 c
+63.952 1.66 l
+63.952 1.808 l
+63.96 1.866 63.967 1.918 63.967 1.969 c
+63.974 2.028 63.982 2.076 63.982 2.117 c
+63.989 2.164 64 2.198 64.011 2.219 c
+64.864 2.219 l
+64.853 2.138 64.841 2.028 64.834 1.882 c
+64.834 1.411 l
+64.834 -1.162 l
+64.834 -1.415 64.797 -1.636 64.731 -1.823 c
+64.662 -2.007 64.558 -2.161 64.423 -2.278 c
+64.283 -2.404 64.118 -2.5 63.923 -2.558 c
+63.725 -2.624 63.504 -2.66 63.261 -2.66 c
+63.952 0.529 m
+63.952 0.742 63.926 0.918 63.879 1.058 c
+63.839 1.205 63.783 1.323 63.717 1.411 c
+63.658 1.499 63.588 1.558 63.511 1.587 c
+63.43 1.624 63.353 1.646 63.276 1.646 c
+63.177 1.646 63.085 1.62 62.997 1.573 c
+62.916 1.532 62.85 1.462 62.791 1.367 c
+62.74 1.278 62.696 1.161 62.659 1.014 c
+62.629 0.874 62.615 0.706 62.615 0.5 c
+62.615 0.125 62.673 -0.154 62.791 -0.339 c
+62.908 -0.515 63.07 -0.603 63.276 -0.603 c
+63.342 -0.603 63.415 -0.588 63.496 -0.559 c
+63.585 -0.522 63.658 -0.463 63.717 -0.383 c
+63.783 -0.294 63.839 -0.177 63.879 -0.03 c
+63.926 0.118 63.952 0.301 63.952 0.529 c
+67.351 -1.324 m
+67.094 -1.324 66.866 -1.286 66.661 -1.22 c
+66.455 -1.143 66.278 -1.029 66.131 -0.882 c
+65.984 -0.728 65.867 -0.537 65.778 -0.309 c
+65.697 -0.085 65.661 0.18 65.661 0.484 c
+65.661 0.816 65.705 1.095 65.793 1.323 c
+65.888 1.558 66.017 1.741 66.175 1.882 c
+66.341 2.017 66.528 2.117 66.734 2.175 c
+66.94 2.242 67.149 2.278 67.366 2.278 c
+67.638 2.278 67.873 2.227 68.072 2.131 c
+68.277 2.043 68.443 1.911 68.571 1.734 c
+68.707 1.565 68.807 1.359 68.865 1.117 c
+68.932 0.881 68.968 0.617 68.968 0.323 c
+68.968 0.309 l
+66.602 0.309 l
+66.602 0.162 66.617 0.022 66.646 -0.103 c
+66.682 -0.231 66.738 -0.345 66.808 -0.441 c
+66.873 -0.53 66.958 -0.599 67.058 -0.647 c
+67.153 -0.698 67.267 -0.721 67.396 -0.721 c
+67.55 -0.721 67.689 -0.688 67.807 -0.618 c
+67.932 -0.551 68.02 -0.449 68.072 -0.309 c
+68.909 -0.383 l
+68.88 -0.482 68.824 -0.588 68.747 -0.706 c
+68.666 -0.816 68.564 -0.919 68.439 -1.014 c
+68.321 -1.103 68.167 -1.176 67.983 -1.235 c
+67.807 -1.294 67.594 -1.324 67.351 -1.324 c
+67.351 1.705 m
+67.263 1.705 67.175 1.691 67.087 1.66 c
+66.999 1.631 66.918 1.579 66.852 1.514 c
+66.782 1.444 66.723 1.356 66.675 1.249 c
+66.634 1.139 66.617 1.014 66.617 0.867 c
+68.086 0.867 l
+68.086 1.003 68.061 1.124 68.013 1.234 c
+67.972 1.341 67.918 1.429 67.851 1.499 c
+67.793 1.565 67.719 1.616 67.631 1.646 c
+67.542 1.683 67.447 1.705 67.351 1.705 c
+72.03 -1.264 m
+72.018 -1.246 72.007 -1.216 72 -1.176 c
+72 -1.129 71.993 -1.081 71.985 -1.029 c
+71.985 -0.971 71.978 -0.912 71.97 -0.853 c
+71.97 -0.691 l
+71.853 -0.927 71.71 -1.095 71.544 -1.191 c
+71.375 -1.279 71.176 -1.324 70.941 -1.324 c
+70.744 -1.324 70.567 -1.279 70.412 -1.191 c
+70.254 -1.103 70.122 -0.981 70.015 -0.823 c
+69.917 -0.659 69.84 -0.467 69.78 -0.25 c
+69.729 -0.037 69.707 0.205 69.707 0.47 c
+69.707 0.735 69.729 0.974 69.78 1.19 c
+69.84 1.415 69.917 1.606 70.015 1.764 c
+70.122 1.918 70.254 2.043 70.412 2.131 c
+70.578 2.227 70.769 2.278 70.985 2.278 c
+71.081 2.278 71.176 2.263 71.265 2.234 c
+71.361 2.212 71.456 2.179 71.544 2.131 c
+71.633 2.08 71.71 2.017 71.779 1.94 c
+71.857 1.859 71.92 1.768 71.97 1.66 c
+71.97 1.749 l
+71.97 1.896 l
+71.97 2.057 l
+71.97 2.234 l
+71.97 3.513 l
+72.867 3.513 l
+72.867 -0.5 l
+72.867 -0.676 72.871 -0.834 72.882 -0.971 c
+72.889 -1.099 72.897 -1.199 72.897 -1.264 c
+h
+71.985 0.484 m
+71.985 0.72 71.959 0.911 71.912 1.058 c
+71.872 1.213 71.816 1.338 71.75 1.425 c
+71.691 1.514 71.621 1.573 71.544 1.602 c
+71.463 1.639 71.386 1.66 71.309 1.66 c
+71.21 1.66 71.118 1.635 71.03 1.587 c
+70.949 1.547 70.883 1.477 70.824 1.381 c
+70.773 1.282 70.729 1.161 70.692 1.014 c
+70.663 0.867 70.648 0.683 70.648 0.47 c
+70.648 0.077 70.699 -0.217 70.809 -0.412 c
+70.927 -0.611 71.089 -0.706 71.294 -0.706 c
+71.371 -0.706 71.449 -0.688 71.529 -0.647 c
+71.606 -0.611 71.681 -0.544 71.75 -0.456 c
+71.816 -0.368 71.872 -0.246 71.912 -0.088 c
+71.959 0.066 71.985 0.257 71.985 0.484 c
+78.729 -2.631 m
+78.729 3.513 l
+80.654 3.513 l
+80.654 2.896 l
+79.581 2.896 l
+79.581 -2.014 l
+80.654 -2.014 l
+80.654 -2.631 l
+h
+83.685 1.602 m
+83.685 -1.264 l
+82.789 -1.264 l
+82.789 1.602 l
+81.966 1.602 l
+81.966 2.219 l
+82.789 2.219 l
+82.789 2.484 l
+82.789 2.609 82.804 2.741 82.833 2.881 c
+82.87 3.017 82.939 3.135 83.039 3.233 c
+83.145 3.34 83.288 3.428 83.465 3.498 c
+83.641 3.564 83.866 3.601 84.142 3.601 c
+84.354 3.601 84.553 3.59 84.73 3.572 c
+84.905 3.549 85.056 3.532 85.185 3.513 c
+85.185 2.925 l
+85.056 2.944 84.913 2.958 84.759 2.969 c
+84.601 2.977 84.45 2.984 84.303 2.984 c
+84.175 2.984 84.071 2.969 83.994 2.94 c
+83.914 2.91 83.851 2.869 83.803 2.822 c
+83.752 2.771 83.718 2.708 83.7 2.631 c
+83.689 2.561 83.685 2.484 83.685 2.396 c
+83.685 2.219 l
+85.112 2.219 l
+85.112 1.602 l
+h
+88.202 -0.647 m
+89.334 -0.647 l
+89.334 -1.264 l
+86.027 -1.264 l
+86.027 -0.647 l
+87.29 -0.647 l
+87.29 1.602 l
+86.365 1.602 l
+86.365 2.219 l
+88.202 2.219 l
+h
+87.29 3.513 0.912 -0.676 re
+87.29 2.836 m
+92.262 -0.647 m
+93.395 -0.647 l
+93.395 -1.264 l
+90.087 -1.264 l
+90.087 -0.647 l
+91.351 -0.647 l
+91.351 2.896 l
+90.425 2.896 l
+90.425 3.513 l
+92.262 3.513 l
+h
+95.78 -1.324 m
+95.522 -1.324 95.294 -1.286 95.088 -1.22 c
+94.883 -1.143 94.706 -1.029 94.559 -0.882 c
+94.413 -0.728 94.295 -0.537 94.207 -0.309 c
+94.126 -0.085 94.089 0.18 94.089 0.484 c
+94.089 0.816 94.133 1.095 94.222 1.323 c
+94.317 1.558 94.446 1.741 94.604 1.882 c
+94.769 2.017 94.956 2.117 95.162 2.175 c
+95.367 2.242 95.577 2.278 95.794 2.278 c
+96.065 2.278 96.301 2.227 96.499 2.131 c
+96.705 2.043 96.871 1.911 97 1.734 c
+97.135 1.565 97.235 1.359 97.293 1.117 c
+97.359 0.881 97.397 0.617 97.397 0.323 c
+97.397 0.309 l
+95.03 0.309 l
+95.03 0.162 95.045 0.022 95.074 -0.103 c
+95.111 -0.231 95.165 -0.345 95.236 -0.441 c
+95.302 -0.53 95.386 -0.599 95.485 -0.647 c
+95.581 -0.698 95.695 -0.721 95.824 -0.721 c
+95.978 -0.721 96.117 -0.688 96.235 -0.618 c
+96.36 -0.551 96.448 -0.449 96.499 -0.309 c
+97.337 -0.383 l
+97.308 -0.482 97.253 -0.588 97.176 -0.706 c
+97.095 -0.816 96.992 -0.919 96.867 -1.014 c
+96.749 -1.103 96.595 -1.176 96.412 -1.235 c
+96.235 -1.294 96.022 -1.324 95.78 -1.324 c
+95.78 1.705 m
+95.691 1.705 95.603 1.691 95.515 1.66 c
+95.427 1.631 95.346 1.579 95.28 1.514 c
+95.209 1.444 95.151 1.356 95.103 1.249 c
+95.063 1.139 95.045 1.014 95.045 0.867 c
+96.514 0.867 l
+96.514 1.003 96.489 1.124 96.441 1.234 c
+96.4 1.341 96.345 1.429 96.279 1.499 c
+96.221 1.565 96.146 1.616 96.059 1.646 c
+95.97 1.683 95.875 1.705 95.78 1.705 c
+98.653 -2.631 m
+98.653 -2.014 l
+99.726 -2.014 l
+99.726 2.896 l
+98.653 2.896 l
+98.653 3.513 l
+100.578 3.513 l
+100.578 -2.631 l
+h
+f
+Q
+q 1 0 0 1 294.3676 334.3982 cm
+0 0 m
+0 0.264 -0.073 0.463 -0.221 0.603 c
+-0.36 0.75 -0.617 0.89 -1 1.029 c
+-1.374 1.166 -1.661 1.309 -1.867 1.455 c
+-2.066 1.602 -2.213 1.768 -2.308 1.955 c
+-2.407 2.15 -2.455 2.371 -2.455 2.616 c
+-2.455 3.036 -2.315 3.385 -2.028 3.66 c
+-1.746 3.932 -1.378 4.072 -0.927 4.072 c
+-0.614 4.072 -0.335 3.998 -0.088 3.851 c
+0.154 3.712 0.341 3.517 0.47 3.263 c
+0.606 3.017 0.675 2.749 0.675 2.455 c
+0 2.455 l
+0 2.786 -0.081 3.043 -0.235 3.219 c
+-0.393 3.403 -0.625 3.499 -0.927 3.499 c
+-1.191 3.499 -1.404 3.418 -1.558 3.263 c
+-1.706 3.117 -1.779 2.903 -1.779 2.631 c
+-1.779 2.404 -1.702 2.213 -1.544 2.058 c
+-1.378 1.911 -1.125 1.771 -0.779 1.646 c
+-0.262 1.478 0.11 1.268 0.338 1.014 c
+0.573 0.757 0.69 0.426 0.69 0.015 c
+0.69 -0.426 0.548 -0.779 0.264 -1.043 c
+-0.023 -1.301 -0.405 -1.425 -0.882 -1.425 c
+-1.198 -1.425 -1.484 -1.356 -1.75 -1.22 c
+-2.014 -1.084 -2.227 -0.893 -2.382 -0.646 c
+-2.529 -0.404 -2.602 -0.118 -2.602 0.206 c
+-1.926 0.206 l
+-1.926 -0.128 -1.834 -0.389 -1.646 -0.573 c
+-1.463 -0.76 -1.206 -0.852 -0.882 -0.852 c
+-0.588 -0.852 -0.368 -0.779 -0.221 -0.632 c
+-0.073 -0.478 0 -0.264 0 0 c
+2.117 2.22 m
+2.37 2.543 2.69 2.705 3.072 2.705 c
+3.777 2.705 4.134 2.234 4.145 1.294 c
+4.145 -1.352 l
+3.498 -1.352 l
+3.498 1.264 l
+3.498 1.577 3.443 1.797 3.337 1.926 c
+3.226 2.051 3.072 2.117 2.866 2.117 c
+2.708 2.117 2.561 2.062 2.425 1.955 c
+2.296 1.845 2.194 1.709 2.117 1.544 c
+2.117 -1.352 l
+1.469 -1.352 l
+1.469 4.293 l
+2.117 4.293 l
+h
+4.968 0.823 m
+4.968 1.401 5.104 1.856 5.379 2.19 c
+5.662 2.532 6.034 2.705 6.497 2.705 c
+6.956 2.705 7.324 2.536 7.599 2.205 c
+7.882 1.881 8.029 1.434 8.04 0.867 c
+8.04 0.441 l
+8.04 -0.128 7.897 -0.584 7.614 -0.926 c
+7.339 -1.261 6.971 -1.425 6.512 -1.425 c
+6.048 -1.425 5.677 -1.264 5.394 -0.941 c
+5.119 -0.61 4.976 -0.168 4.968 0.382 c
+h
+5.614 0.441 m
+5.614 0.037 5.692 -0.279 5.85 -0.515 c
+6.015 -0.75 6.236 -0.867 6.512 -0.867 c
+7.077 -0.867 7.372 -0.455 7.393 0.368 c
+7.393 0.823 l
+7.393 1.224 7.309 1.544 7.144 1.779 c
+6.986 2.022 6.769 2.146 6.497 2.146 c
+6.232 2.146 6.015 2.022 5.85 1.779 c
+5.692 1.544 5.614 1.224 5.614 0.823 c
+h
+11.848 -0.206 m
+12.45 2.631 l
+13.097 2.631 l
+12.112 -1.352 l
+11.597 -1.352 l
+10.818 1.5 l
+10.069 -1.352 l
+9.539 -1.352 l
+8.584 2.631 l
+9.216 2.631 l
+9.834 -0.132 l
+10.568 2.631 l
+11.083 2.631 l
+h
+16.874 -0.867 m
+17.088 -0.867 17.26 -0.804 17.389 -0.676 c
+17.525 -0.54 17.598 -0.349 17.609 -0.103 c
+18.227 -0.103 l
+18.204 -0.484 18.069 -0.804 17.815 -1.058 c
+17.558 -1.304 17.246 -1.425 16.874 -1.425 c
+16.382 -1.425 16.007 -1.275 15.742 -0.97 c
+15.486 -0.658 15.36 -0.191 15.36 0.426 c
+15.36 0.867 l
+15.36 1.463 15.486 1.918 15.742 2.234 c
+16.007 2.547 16.382 2.705 16.874 2.705 c
+17.275 2.705 17.595 2.573 17.83 2.308 c
+18.073 2.051 18.204 1.706 18.227 1.264 c
+17.609 1.264 l
+17.587 1.558 17.514 1.779 17.389 1.926 c
+17.271 2.072 17.099 2.146 16.874 2.146 c
+16.581 2.146 16.363 2.047 16.228 1.852 c
+16.088 1.665 16.014 1.357 16.007 0.927 c
+16.007 0.412 l
+16.007 -0.058 16.073 -0.393 16.213 -0.588 c
+16.359 -0.775 16.581 -0.867 16.874 -0.867 c
+19.623 2.22 m
+19.877 2.543 20.196 2.705 20.579 2.705 c
+21.284 2.705 21.641 2.234 21.651 1.294 c
+21.651 -1.352 l
+21.005 -1.352 l
+21.005 1.264 l
+21.005 1.577 20.949 1.797 20.843 1.926 c
+20.733 2.051 20.579 2.117 20.373 2.117 c
+20.215 2.117 20.068 2.062 19.931 1.955 c
+19.803 1.845 19.7 1.709 19.623 1.544 c
+19.623 -1.352 l
+18.977 -1.352 l
+18.977 4.293 l
+19.623 4.293 l
+h
+24.65 -1.352 m
+24.61 -1.264 24.584 -1.117 24.577 -0.912 c
+24.342 -1.257 24.047 -1.425 23.694 -1.425 c
+23.331 -1.425 23.048 -1.33 22.842 -1.132 c
+22.644 -0.926 22.548 -0.639 22.548 -0.264 c
+22.548 0.136 22.684 0.456 22.96 0.691 c
+23.232 0.933 23.607 1.058 24.077 1.058 c
+24.562 1.058 l
+24.562 1.484 l
+24.562 1.72 24.507 1.885 24.4 1.985 c
+24.29 2.091 24.128 2.146 23.915 2.146 c
+23.717 2.146 23.555 2.088 23.43 1.97 c
+23.312 1.852 23.253 1.706 23.253 1.529 c
+22.607 1.529 l
+22.607 1.723 22.666 1.914 22.783 2.103 c
+22.908 2.286 23.07 2.433 23.268 2.543 c
+23.474 2.65 23.702 2.705 23.959 2.705 c
+24.359 2.705 24.664 2.602 24.87 2.396 c
+25.084 2.19 25.198 1.897 25.209 1.515 c
+25.209 -0.5 l
+25.209 -0.804 25.246 -1.069 25.327 -1.294 c
+25.327 -1.352 l
+h
+23.783 -0.837 m
+23.948 -0.837 24.099 -0.794 24.238 -0.706 c
+24.386 -0.617 24.492 -0.507 24.562 -0.367 c
+24.562 0.574 l
+24.194 0.574 l
+23.879 0.574 23.636 0.503 23.459 0.368 c
+23.283 0.239 23.195 0.052 23.195 -0.191 c
+23.195 -0.419 23.239 -0.584 23.328 -0.69 c
+23.415 -0.79 23.566 -0.837 23.783 -0.837 c
+26.825 2.631 m
+26.84 2.19 l
+27.094 2.532 27.417 2.705 27.81 2.705 c
+28.516 2.705 28.872 2.234 28.884 1.294 c
+28.884 -1.352 l
+28.236 -1.352 l
+28.236 1.264 l
+28.236 1.577 28.182 1.797 28.075 1.926 c
+27.964 2.051 27.81 2.117 27.605 2.117 c
+27.447 2.117 27.299 2.062 27.164 1.955 c
+27.035 1.845 26.932 1.709 26.855 1.544 c
+26.855 -1.352 l
+26.208 -1.352 l
+26.208 2.631 l
+h
+29.722 0.823 m
+29.722 1.44 29.832 1.904 30.059 2.22 c
+30.283 2.543 30.618 2.705 31.059 2.705 c
+31.459 2.705 31.764 2.529 31.97 2.176 c
+32.014 2.631 l
+32.602 2.631 l
+32.602 -1.396 l
+32.602 -1.885 32.473 -2.263 32.22 -2.528 c
+31.963 -2.793 31.61 -2.925 31.162 -2.925 c
+30.964 -2.925 30.742 -2.873 30.501 -2.778 c
+30.254 -2.678 30.074 -2.558 29.957 -2.41 c
+30.221 -1.97 l
+30.486 -2.234 30.783 -2.366 31.118 -2.366 c
+31.654 -2.366 31.93 -2.072 31.941 -1.484 c
+31.941 -0.955 l
+31.735 -1.271 31.434 -1.425 31.044 -1.425 c
+30.632 -1.425 30.309 -1.275 30.074 -0.97 c
+29.846 -0.658 29.728 -0.206 29.722 0.382 c
+h
+30.383 0.441 m
+30.383 0 30.445 -0.33 30.574 -0.544 c
+30.699 -0.75 30.916 -0.852 31.22 -0.852 c
+31.544 -0.852 31.783 -0.687 31.941 -0.353 c
+31.941 1.632 l
+31.772 1.955 31.533 2.117 31.22 2.117 c
+30.927 2.117 30.71 2.014 30.574 1.808 c
+30.445 1.602 30.383 1.279 30.383 0.838 c
+h
+34.983 -1.425 m
+34.484 -1.425 34.102 -1.278 33.837 -0.985 c
+33.572 -0.69 33.44 -0.257 33.44 0.324 c
+33.44 0.794 l
+33.44 1.389 33.565 1.856 33.822 2.19 c
+34.087 2.532 34.447 2.705 34.91 2.705 c
+35.369 2.705 35.711 2.55 35.939 2.249 c
+36.174 1.955 36.296 1.492 36.307 0.867 c
+36.307 0.441 l
+34.087 0.441 l
+34.087 0.353 l
+34.087 -0.081 34.164 -0.393 34.322 -0.588 c
+34.487 -0.775 34.719 -0.867 35.012 -0.867 c
+35.208 -0.867 35.38 -0.833 35.527 -0.764 c
+35.675 -0.687 35.81 -0.569 35.939 -0.411 c
+36.277 -0.823 l
+35.991 -1.227 35.561 -1.425 34.983 -1.425 c
+34.91 2.146 m
+34.634 2.146 34.432 2.051 34.307 1.867 c
+34.179 1.679 34.106 1.389 34.087 1 c
+35.66 1 l
+35.66 1.087 l
+35.638 1.47 35.571 1.738 35.454 1.897 c
+35.336 2.062 35.153 2.146 34.91 2.146 c
+39.026 -0.338 m
+39.026 -0.191 38.971 -0.07 38.864 0.029 c
+38.754 0.125 38.548 0.243 38.247 0.382 c
+37.901 0.53 37.659 0.651 37.512 0.75 c
+37.364 0.856 37.254 0.974 37.188 1.103 c
+37.119 1.228 37.086 1.386 37.086 1.573 c
+37.086 1.897 37.203 2.165 37.439 2.381 c
+37.674 2.595 37.975 2.705 38.349 2.705 c
+38.732 2.705 39.041 2.591 39.276 2.367 c
+39.511 2.139 39.629 1.852 39.629 1.5 c
+38.981 1.5 l
+38.981 1.675 38.923 1.827 38.805 1.955 c
+38.688 2.08 38.534 2.146 38.349 2.146 c
+38.151 2.146 38 2.091 37.894 1.985 c
+37.784 1.885 37.732 1.754 37.732 1.588 c
+37.732 1.459 37.769 1.353 37.85 1.264 c
+37.927 1.183 38.118 1.081 38.424 0.956 c
+38.901 0.769 39.232 0.58 39.408 0.397 c
+39.584 0.221 39.673 -0.008 39.673 -0.279 c
+39.673 -0.632 39.548 -0.912 39.305 -1.117 c
+39.07 -1.323 38.754 -1.425 38.364 -1.425 c
+37.942 -1.425 37.603 -1.308 37.35 -1.072 c
+37.092 -0.831 36.968 -0.525 36.968 -0.162 c
+37.615 -0.162 l
+37.622 -0.389 37.692 -0.565 37.821 -0.69 c
+37.946 -0.808 38.129 -0.867 38.364 -0.867 c
+38.578 -0.867 38.739 -0.819 38.85 -0.72 c
+38.967 -0.625 39.026 -0.496 39.026 -0.338 c
+45.111 0.441 m
+45.111 -0.176 44.997 -0.643 44.774 -0.955 c
+44.556 -1.271 44.233 -1.425 43.803 -1.425 c
+43.38 -1.425 43.068 -1.246 42.862 -0.881 c
+42.833 -1.352 l
+42.23 -1.352 l
+42.23 4.293 l
+42.877 4.293 l
+42.877 2.19 l
+43.09 2.532 43.399 2.705 43.803 2.705 c
+44.233 2.705 44.556 2.547 44.774 2.234 c
+44.997 1.929 45.111 1.463 45.111 0.838 c
+h
+44.464 0.823 m
+44.464 1.294 44.394 1.625 44.259 1.823 c
+44.13 2.018 43.92 2.117 43.627 2.117 c
+43.292 2.117 43.043 1.933 42.877 1.573 c
+42.877 -0.309 l
+43.043 -0.673 43.296 -0.852 43.641 -0.852 c
+43.935 -0.852 44.145 -0.75 44.273 -0.544 c
+44.398 -0.338 44.464 -0.022 44.464 0.412 c
+h
+47.375 -1.425 m
+46.875 -1.425 46.493 -1.278 46.228 -0.985 c
+45.964 -0.69 45.831 -0.257 45.831 0.324 c
+45.831 0.794 l
+45.831 1.389 45.956 1.856 46.214 2.19 c
+46.478 2.532 46.838 2.705 47.301 2.705 c
+47.76 2.705 48.103 2.55 48.33 2.249 c
+48.566 1.955 48.687 1.492 48.697 0.867 c
+48.697 0.441 l
+46.478 0.441 l
+46.478 0.353 l
+46.478 -0.081 46.555 -0.393 46.713 -0.588 c
+46.879 -0.775 47.11 -0.867 47.404 -0.867 c
+47.599 -0.867 47.772 -0.833 47.918 -0.764 c
+48.066 -0.687 48.202 -0.569 48.33 -0.411 c
+48.668 -0.823 l
+48.381 -1.227 47.951 -1.425 47.375 -1.425 c
+47.301 2.146 m
+47.026 2.146 46.823 2.051 46.698 1.867 c
+46.57 1.679 46.497 1.389 46.478 1 c
+48.051 1 l
+48.051 1.087 l
+48.029 1.47 47.963 1.738 47.845 1.897 c
+47.727 2.062 47.544 2.146 47.301 2.146 c
+50.33 3.587 m
+50.33 2.631 l
+50.932 2.631 l
+50.932 2.103 l
+50.33 2.103 l
+50.33 -0.367 l
+50.33 -0.525 50.351 -0.643 50.403 -0.72 c
+50.461 -0.801 50.55 -0.837 50.667 -0.837 c
+50.756 -0.837 50.844 -0.823 50.932 -0.794 c
+50.932 -1.352 l
+50.785 -1.4 50.631 -1.425 50.476 -1.425 c
+50.219 -1.425 50.025 -1.334 49.888 -1.147 c
+49.749 -0.962 49.682 -0.702 49.682 -0.367 c
+49.682 2.103 l
+49.08 2.103 l
+49.08 2.631 l
+49.682 2.631 l
+49.682 3.587 l
+h
+54.665 -0.206 m
+55.268 2.631 l
+55.915 2.631 l
+54.93 -1.352 l
+54.416 -1.352 l
+53.636 1.5 l
+52.887 -1.352 l
+52.358 -1.352 l
+51.402 2.631 l
+52.034 2.631 l
+52.651 -0.132 l
+53.387 2.631 l
+53.902 2.631 l
+h
+58.017 -1.425 m
+57.517 -1.425 57.135 -1.278 56.871 -0.985 c
+56.606 -0.69 56.474 -0.257 56.474 0.324 c
+56.474 0.794 l
+56.474 1.389 56.599 1.856 56.855 2.19 c
+57.12 2.532 57.48 2.705 57.944 2.705 c
+58.403 2.705 58.744 2.55 58.972 2.249 c
+59.207 1.955 59.329 1.492 59.34 0.867 c
+59.34 0.441 l
+57.12 0.441 l
+57.12 0.353 l
+57.12 -0.081 57.198 -0.393 57.355 -0.588 c
+57.521 -0.775 57.752 -0.867 58.046 -0.867 c
+58.241 -0.867 58.414 -0.833 58.561 -0.764 c
+58.708 -0.687 58.844 -0.569 58.972 -0.411 c
+59.311 -0.823 l
+59.024 -1.227 58.594 -1.425 58.017 -1.425 c
+57.944 2.146 m
+57.668 2.146 57.466 2.051 57.341 1.867 c
+57.212 1.679 57.139 1.389 57.12 1 c
+58.693 1 l
+58.693 1.087 l
+58.671 1.47 58.605 1.738 58.488 1.897 c
+58.37 2.062 58.186 2.146 57.944 2.146 c
+61.545 -1.425 m
+61.045 -1.425 60.663 -1.278 60.398 -0.985 c
+60.134 -0.69 60.001 -0.257 60.001 0.324 c
+60.001 0.794 l
+60.001 1.389 60.126 1.856 60.383 2.19 c
+60.648 2.532 61.008 2.705 61.472 2.705 c
+61.931 2.705 62.272 2.55 62.5 2.249 c
+62.735 1.955 62.856 1.492 62.868 0.867 c
+62.868 0.441 l
+60.648 0.441 l
+60.648 0.353 l
+60.648 -0.081 60.726 -0.393 60.883 -0.588 c
+61.048 -0.775 61.28 -0.867 61.574 -0.867 c
+61.769 -0.867 61.942 -0.833 62.089 -0.764 c
+62.236 -0.687 62.372 -0.569 62.5 -0.411 c
+62.839 -0.823 l
+62.552 -1.227 62.122 -1.425 61.545 -1.425 c
+61.472 2.146 m
+61.196 2.146 60.994 2.051 60.869 1.867 c
+60.74 1.679 60.666 1.389 60.648 1 c
+62.221 1 l
+62.221 1.087 l
+62.199 1.47 62.133 1.738 62.015 1.897 c
+61.898 2.062 61.713 2.146 61.472 2.146 c
+64.264 2.631 m
+64.279 2.19 l
+64.532 2.532 64.855 2.705 65.249 2.705 c
+65.954 2.705 66.311 2.234 66.322 1.294 c
+66.322 -1.352 l
+65.675 -1.352 l
+65.675 1.264 l
+65.675 1.577 65.62 1.797 65.513 1.926 c
+65.403 2.051 65.249 2.117 65.043 2.117 c
+64.885 2.117 64.738 2.062 64.602 1.955 c
+64.474 1.845 64.371 1.709 64.294 1.544 c
+64.294 -1.352 l
+63.647 -1.352 l
+63.647 2.631 l
+h
+f
+Q
+q 1 0 0 1 365.0407 334.1188 cm
+0 0 m
+0 0.088 -0.044 0.166 -0.133 0.235 c
+-0.221 0.312 -0.408 0.416 -0.691 0.544 c
+-1.125 0.721 -1.422 0.9 -1.588 1.088 c
+-1.746 1.272 -1.823 1.503 -1.823 1.779 c
+-1.823 2.12 -1.702 2.404 -1.455 2.631 c
+-1.202 2.866 -0.864 2.984 -0.441 2.984 c
+-0.011 2.984 0.338 2.87 0.602 2.646 c
+0.867 2.419 0.999 2.117 0.999 1.735 c
+-0.044 1.735 l
+-0.044 2.058 -0.184 2.22 -0.456 2.22 c
+-0.566 2.22 -0.655 2.183 -0.721 2.117 c
+-0.79 2.047 -0.823 1.948 -0.823 1.823 c
+-0.823 1.735 -0.786 1.654 -0.706 1.588 c
+-0.628 1.529 -0.449 1.434 -0.162 1.309 c
+0.268 1.151 0.565 0.975 0.735 0.779 c
+0.912 0.592 0.999 0.342 0.999 0.03 c
+0.999 -0.323 0.867 -0.61 0.602 -0.823 c
+0.338 -1.039 -0.011 -1.146 -0.441 -1.146 c
+-0.735 -1.146 -0.996 -1.091 -1.22 -0.985 c
+-1.448 -0.867 -1.625 -0.706 -1.75 -0.499 c
+-1.867 -0.293 -1.926 -0.073 -1.926 0.162 c
+-0.941 0.162 l
+-0.941 -0.025 -0.904 -0.162 -0.823 -0.249 c
+-0.735 -0.338 -0.603 -0.382 -0.426 -0.382 c
+-0.144 -0.382 0 -0.257 0 0 c
+2.866 3.881 m
+2.866 2.911 l
+3.395 2.911 l
+3.395 2.117 l
+2.866 2.117 l
+2.866 0.148 l
+2.866 -0.01 2.884 -0.118 2.925 -0.176 c
+2.973 -0.235 3.057 -0.264 3.175 -0.264 c
+3.281 -0.264 3.366 -0.257 3.424 -0.235 c
+3.424 -1.043 l
+3.248 -1.109 3.057 -1.146 2.851 -1.146 c
+2.175 -1.146 1.83 -0.76 1.822 0.015 c
+1.822 2.117 l
+1.367 2.117 l
+1.367 2.911 l
+1.822 2.911 l
+1.822 3.881 l
+h
+5.85 -1.072 m
+5.82 -1.014 5.791 -0.911 5.762 -0.764 c
+5.575 -1.022 5.325 -1.146 5.012 -1.146 c
+4.677 -1.146 4.399 -1.039 4.174 -0.823 c
+3.958 -0.598 3.85 -0.309 3.85 0.044 c
+3.85 0.456 3.983 0.772 4.247 1 c
+4.513 1.235 4.895 1.353 5.394 1.353 c
+5.718 1.353 l
+5.718 1.676 l
+5.718 1.852 5.681 1.974 5.615 2.043 c
+5.556 2.12 5.468 2.161 5.35 2.161 c
+5.093 2.161 4.968 2.007 4.968 1.706 c
+3.925 1.706 l
+3.925 2.076 4.06 2.382 4.336 2.617 c
+4.608 2.859 4.957 2.984 5.38 2.984 c
+5.82 2.984 6.159 2.866 6.394 2.631 c
+6.636 2.404 6.761 2.08 6.761 1.661 c
+6.761 -0.205 l
+6.761 -0.551 6.809 -0.819 6.908 -1.014 c
+6.908 -1.072 l
+h
+5.247 -0.323 m
+5.354 -0.323 5.446 -0.305 5.527 -0.264 c
+5.615 -0.216 5.677 -0.158 5.718 -0.087 c
+5.718 0.736 l
+5.468 0.736 l
+5.292 0.736 5.148 0.676 5.041 0.559 c
+4.943 0.449 4.895 0.302 4.895 0.118 c
+4.895 -0.176 5.012 -0.323 5.247 -0.323 c
+7.378 1.044 m
+7.378 1.691 7.497 2.176 7.732 2.5 c
+7.967 2.822 8.297 2.984 8.731 2.984 c
+9.084 2.984 9.356 2.841 9.554 2.558 c
+9.598 2.911 l
+10.539 2.911 l
+10.539 -1.072 l
+10.539 -1.579 10.396 -1.969 10.113 -2.234 c
+9.826 -2.506 9.422 -2.645 8.893 -2.645 c
+8.665 -2.645 8.43 -2.601 8.187 -2.514 c
+7.952 -2.425 7.775 -2.311 7.658 -2.175 c
+8.01 -1.455 l
+8.106 -1.562 8.235 -1.646 8.393 -1.705 c
+8.548 -1.77 8.694 -1.808 8.834 -1.808 c
+9.069 -1.808 9.234 -1.749 9.334 -1.631 c
+9.44 -1.521 9.495 -1.344 9.495 -1.103 c
+9.495 -0.75 l
+9.297 -1.014 9.04 -1.146 8.716 -1.146 c
+8.294 -1.146 7.967 -0.985 7.732 -0.661 c
+7.503 -0.33 7.386 0.14 7.378 0.75 c
+h
+8.422 0.779 m
+8.422 0.405 8.47 0.136 8.569 -0.029 c
+8.665 -0.198 8.819 -0.279 9.025 -0.279 c
+9.238 -0.279 9.396 -0.201 9.495 -0.044 c
+9.495 1.852 l
+9.385 2.018 9.231 2.103 9.025 2.103 c
+8.819 2.103 8.665 2.018 8.569 1.852 c
+8.47 1.683 8.422 1.415 8.422 1.044 c
+h
+12.391 -1.072 -1.044 3.983 re
+11.303 3.94 m
+11.303 4.094 11.351 4.223 11.451 4.322 c
+11.557 4.428 11.692 4.484 11.862 4.484 c
+12.039 4.484 12.174 4.428 12.274 4.322 c
+12.38 4.223 12.436 4.094 12.436 3.94 c
+12.436 3.771 12.38 3.635 12.274 3.528 c
+12.174 3.429 12.039 3.381 11.862 3.381 c
+11.692 3.381 11.557 3.429 11.451 3.528 c
+11.351 3.635 11.303 3.771 11.303 3.94 c
+14.155 2.911 m
+14.184 2.514 l
+14.42 2.826 14.721 2.984 15.096 2.984 c
+15.779 2.984 16.132 2.503 16.154 1.544 c
+16.154 -1.072 l
+15.11 -1.072 l
+15.11 1.47 l
+15.11 1.694 15.073 1.856 15.008 1.955 c
+14.938 2.051 14.82 2.103 14.655 2.103 c
+14.468 2.103 14.32 2.007 14.214 1.823 c
+14.214 -1.072 l
+13.17 -1.072 l
+13.17 2.911 l
+h
+16.772 1.044 m
+16.772 1.691 16.889 2.176 17.124 2.5 c
+17.359 2.822 17.69 2.984 18.123 2.984 c
+18.476 2.984 18.748 2.841 18.947 2.558 c
+18.991 2.911 l
+19.931 2.911 l
+19.931 -1.072 l
+19.931 -1.579 19.788 -1.969 19.505 -2.234 c
+19.218 -2.506 18.815 -2.645 18.285 -2.645 c
+18.057 -2.645 17.822 -2.601 17.58 -2.514 c
+17.344 -2.425 17.168 -2.311 17.051 -2.175 c
+17.404 -1.455 l
+17.499 -1.562 17.628 -1.646 17.786 -1.705 c
+17.94 -1.77 18.087 -1.808 18.227 -1.808 c
+18.462 -1.808 18.628 -1.749 18.726 -1.631 c
+18.833 -1.521 18.888 -1.344 18.888 -1.103 c
+18.888 -0.75 l
+18.69 -1.014 18.432 -1.146 18.109 -1.146 c
+17.687 -1.146 17.359 -0.985 17.124 -0.661 c
+16.897 -0.33 16.779 0.14 16.772 0.75 c
+h
+17.815 0.779 m
+17.815 0.405 17.863 0.136 17.962 -0.029 c
+18.057 -0.198 18.212 -0.279 18.418 -0.279 c
+18.63 -0.279 18.788 -0.201 18.888 -0.044 c
+18.888 1.852 l
+18.778 2.018 18.624 2.103 18.418 2.103 c
+18.212 2.103 18.057 2.018 17.962 1.852 c
+17.863 1.683 17.815 1.415 17.815 1.044 c
+h
+24.312 -1.072 m
+24.282 -1.014 24.253 -0.911 24.224 -0.764 c
+24.037 -1.022 23.787 -1.146 23.474 -1.146 c
+23.139 -1.146 22.861 -1.039 22.636 -0.823 c
+22.42 -0.598 22.313 -0.309 22.313 0.044 c
+22.313 0.456 22.445 0.772 22.709 1 c
+22.975 1.235 23.357 1.353 23.856 1.353 c
+24.18 1.353 l
+24.18 1.676 l
+24.18 1.852 24.143 1.974 24.076 2.043 c
+24.018 2.12 23.93 2.161 23.812 2.161 c
+23.555 2.161 23.43 2.007 23.43 1.706 c
+22.387 1.706 l
+22.387 2.076 22.522 2.382 22.798 2.617 c
+23.07 2.859 23.419 2.984 23.841 2.984 c
+24.282 2.984 24.621 2.866 24.856 2.631 c
+25.098 2.404 25.223 2.08 25.223 1.661 c
+25.223 -0.205 l
+25.223 -0.551 25.271 -0.819 25.371 -1.014 c
+25.371 -1.072 l
+h
+23.709 -0.323 m
+23.816 -0.323 23.908 -0.305 23.989 -0.264 c
+24.076 -0.216 24.139 -0.158 24.18 -0.087 c
+24.18 0.736 l
+23.93 0.736 l
+23.754 0.736 23.61 0.676 23.503 0.559 c
+23.405 0.449 23.357 0.302 23.357 0.118 c
+23.357 -0.176 23.474 -0.323 23.709 -0.323 c
+27.928 1.897 m
+27.59 1.926 l
+27.303 1.926 27.112 1.801 27.017 1.559 c
+27.017 -1.072 l
+25.973 -1.072 l
+25.973 2.911 l
+26.943 2.911 l
+26.973 2.469 l
+27.138 2.812 27.369 2.984 27.663 2.984 c
+27.781 2.984 27.873 2.962 27.943 2.926 c
+h
+30 -1.146 m
+29.471 -1.146 29.053 -0.992 28.751 -0.675 c
+28.457 -0.353 28.31 0.107 28.31 0.706 c
+28.31 1.014 l
+28.31 1.64 28.446 2.124 28.722 2.469 c
+28.994 2.812 29.387 2.984 29.898 2.984 c
+30.397 2.984 30.769 2.822 31.015 2.5 c
+31.268 2.176 31.401 1.698 31.411 1.073 c
+31.411 0.574 l
+29.339 0.574 l
+29.358 0.279 29.42 0.063 29.53 -0.073 c
+29.647 -0.213 29.828 -0.279 30.074 -0.279 c
+30.416 -0.279 30.706 -0.162 30.941 0.073 c
+31.353 -0.558 l
+31.224 -0.735 31.037 -0.878 30.794 -0.985 c
+30.548 -1.091 30.283 -1.146 30 -1.146 c
+29.354 1.294 m
+30.383 1.294 l
+30.383 1.397 l
+30.383 1.632 30.343 1.808 30.265 1.926 c
+30.195 2.051 30.067 2.117 29.883 2.117 c
+29.707 2.117 29.574 2.047 29.486 1.912 c
+29.406 1.783 29.361 1.577 29.354 1.294 c
+33.881 -1.072 m
+33.852 -1.014 33.822 -0.911 33.793 -0.764 c
+33.605 -1.022 33.356 -1.146 33.043 -1.146 c
+32.709 -1.146 32.429 -1.039 32.205 -0.823 c
+31.989 -0.598 31.882 -0.309 31.882 0.044 c
+31.882 0.456 32.014 0.772 32.279 1 c
+32.543 1.235 32.926 1.353 33.425 1.353 c
+33.749 1.353 l
+33.749 1.676 l
+33.749 1.852 33.712 1.974 33.646 2.043 c
+33.587 2.12 33.499 2.161 33.381 2.161 c
+33.124 2.161 32.999 2.007 32.999 1.706 c
+31.956 1.706 l
+31.956 2.076 32.091 2.382 32.367 2.617 c
+32.639 2.859 32.988 2.984 33.41 2.984 c
+33.852 2.984 34.189 2.866 34.424 2.631 c
+34.667 2.404 34.792 2.08 34.792 1.661 c
+34.792 -0.205 l
+34.792 -0.551 34.84 -0.819 34.939 -1.014 c
+34.939 -1.072 l
+h
+33.279 -0.323 m
+33.385 -0.323 33.477 -0.305 33.558 -0.264 c
+33.646 -0.216 33.709 -0.158 33.749 -0.087 c
+33.749 0.736 l
+33.499 0.736 l
+33.323 0.736 33.179 0.676 33.073 0.559 c
+32.974 0.449 32.926 0.302 32.926 0.118 c
+32.926 -0.176 33.043 -0.323 33.279 -0.323 c
+f
+Q
+q 1 0 0 1 404.4483 333.0464 cm
+0 0 m
+-0.04 0.087 -0.066 0.235 -0.073 0.44 c
+-0.309 0.095 -0.602 -0.073 -0.955 -0.073 c
+-1.319 -0.073 -1.602 0.022 -1.807 0.22 c
+-2.006 0.426 -2.102 0.713 -2.102 1.087 c
+-2.102 1.488 -1.965 1.808 -1.691 2.043 c
+-1.418 2.285 -1.043 2.41 -0.573 2.41 c
+-0.088 2.41 l
+-0.088 2.836 l
+-0.088 3.072 -0.143 3.237 -0.249 3.337 c
+-0.359 3.443 -0.521 3.498 -0.735 3.498 c
+-0.933 3.498 -1.095 3.439 -1.22 3.322 c
+-1.337 3.204 -1.396 3.057 -1.396 2.881 c
+-2.042 2.881 l
+-2.042 3.075 -1.984 3.266 -1.866 3.454 c
+-1.741 3.638 -1.58 3.785 -1.381 3.895 c
+-1.176 4.002 -0.947 4.056 -0.691 4.056 c
+-0.29 4.056 0.015 3.954 0.221 3.748 c
+0.434 3.542 0.548 3.248 0.559 2.866 c
+0.559 0.852 l
+0.559 0.548 0.595 0.283 0.676 0.058 c
+0.676 0 l
+h
+-0.866 0.515 m
+-0.702 0.515 -0.551 0.558 -0.411 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.985 c
+-0.088 1.926 l
+-0.455 1.926 l
+-0.771 1.926 -1.014 1.855 -1.19 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.234 0.562 -1.084 0.515 -0.866 0.515 c
+2.176 3.983 m
+2.191 3.542 l
+2.444 3.884 2.768 4.056 3.161 4.056 c
+3.866 4.056 4.223 3.586 4.233 2.645 c
+4.233 0 l
+3.587 0 l
+3.587 2.616 l
+3.587 2.929 3.532 3.149 3.425 3.278 c
+3.315 3.403 3.161 3.469 2.955 3.469 c
+2.797 3.469 2.65 3.414 2.514 3.307 c
+2.386 3.197 2.282 3.061 2.205 2.896 c
+2.205 0 l
+1.559 0 l
+1.559 3.983 l
+h
+5.072 2.175 m
+5.072 2.782 5.182 3.248 5.409 3.572 c
+5.644 3.895 5.972 4.056 6.394 4.056 c
+6.776 4.056 7.074 3.898 7.291 3.586 c
+7.291 5.644 l
+7.938 5.644 l
+7.938 0 l
+7.35 0 l
+7.306 0.426 l
+7.1 0.091 6.795 -0.073 6.394 -0.073 c
+5.983 -0.073 5.659 0.081 5.424 0.397 c
+5.189 0.72 5.072 1.176 5.072 1.764 c
+h
+5.719 1.793 m
+5.719 1.352 5.781 1.022 5.91 0.808 c
+6.045 0.602 6.266 0.5 6.571 0.5 c
+6.895 0.5 7.133 0.661 7.291 0.985 c
+7.291 2.998 l
+7.122 3.31 6.883 3.469 6.571 3.469 c
+6.266 3.469 6.045 3.366 5.91 3.16 c
+5.781 2.954 5.719 2.631 5.719 2.19 c
+h
+f
+Q
+416.09 333.046 -1.043 3.983 re
+415.002 338.059 m
+415.002 338.213 415.05 338.342 415.149 338.441 c
+415.256 338.547 415.392 338.602 415.561 338.602 c
+415.737 338.602 415.874 338.547 415.972 338.441 c
+416.08 338.342 416.134 338.213 416.134 338.059 c
+416.134 337.889 416.08 337.754 415.972 337.647 c
+415.874 337.548 415.737 337.5 415.561 337.5 c
+415.392 337.5 415.256 337.548 415.149 337.647 c
+415.05 337.754 415.002 337.889 415.002 338.059 c
+417.854 337.029 m
+417.884 336.632 l
+418.119 336.945 418.42 337.103 418.795 337.103 c
+419.478 337.103 419.831 336.622 419.853 335.663 c
+419.853 333.046 l
+418.81 333.046 l
+418.81 335.589 l
+418.81 335.813 418.773 335.975 418.707 336.074 c
+418.637 336.17 418.519 336.221 418.354 336.221 c
+418.166 336.221 418.019 336.125 417.913 335.942 c
+417.913 333.046 l
+416.869 333.046 l
+416.869 337.029 l
+h
+420.456 335.163 m
+420.456 335.809 420.562 336.295 420.779 336.618 c
+421.003 336.941 421.327 337.103 421.749 337.103 c
+422.062 337.103 422.316 336.971 422.513 336.706 c
+422.513 338.691 l
+423.573 338.691 l
+423.573 333.046 l
+422.617 333.046 l
+422.573 333.457 l
+422.356 333.134 422.081 332.973 421.749 332.973 c
+421.337 332.973 421.018 333.127 420.794 333.443 c
+420.577 333.766 420.463 334.236 420.456 334.854 c
+h
+421.499 334.898 m
+421.499 334.505 421.536 334.23 421.617 334.075 c
+421.705 333.918 421.852 333.839 422.058 333.839 c
+422.264 333.839 422.414 333.932 422.513 334.119 c
+422.513 335.927 l
+422.414 336.122 422.264 336.221 422.058 336.221 c
+421.859 336.221 421.72 336.14 421.632 335.986 c
+421.543 335.839 421.499 335.567 421.499 335.177 c
+h
+425.939 332.973 m
+425.41 332.973 424.99 333.127 424.689 333.443 c
+424.395 333.766 424.248 334.226 424.248 334.824 c
+424.248 335.133 l
+424.248 335.759 424.384 336.243 424.66 336.588 c
+424.932 336.931 425.325 337.103 425.836 337.103 c
+426.336 337.103 426.707 336.941 426.953 336.618 c
+427.207 336.295 427.338 335.817 427.35 335.192 c
+427.35 334.693 l
+425.277 334.693 l
+425.296 334.398 425.358 334.182 425.468 334.045 c
+425.586 333.906 425.766 333.839 426.012 333.839 c
+426.354 333.839 426.644 333.957 426.879 334.192 c
+427.291 333.561 l
+427.162 333.384 426.975 333.241 426.732 333.134 c
+426.486 333.028 426.222 332.973 425.939 332.973 c
+425.292 335.412 m
+426.32 335.412 l
+426.32 335.516 l
+426.32 335.751 426.28 335.927 426.203 336.044 c
+426.133 336.17 426.004 336.235 425.821 336.235 c
+425.645 336.235 425.512 336.166 425.424 336.03 c
+425.343 335.902 425.299 335.696 425.292 335.412 c
+429.26 335.839 m
+429.731 337.029 l
+430.833 337.029 l
+429.923 335.075 l
+430.863 333.046 l
+429.761 333.046 l
+429.275 334.296 l
+428.79 333.046 l
+427.673 333.046 l
+428.614 335.075 l
+427.717 337.029 l
+428.82 337.029 l
+h
+f
+q 1 0 0 1 433.1559 335.2214 cm
+0 0 m
+0 0.636 0.088 1.239 0.264 1.808 c
+0.441 2.374 0.684 2.87 1 3.293 c
+1.195 3.557 1.382 3.749 1.558 3.866 c
+1.691 3.41 l
+1.396 3.135 1.154 2.712 0.97 2.146 c
+0.783 1.577 0.68 0.945 0.661 0.25 c
+0.661 -0.044 l
+0.661 -0.908 0.779 -1.672 1.014 -2.337 c
+1.198 -2.836 1.426 -3.219 1.691 -3.484 c
+1.558 -3.91 l
+1.33 -3.752 1.106 -3.501 0.882 -3.16 c
+0.294 -2.278 0 -1.227 0 0 c
+4.027 1.191 m
+3.939 1.209 3.84 1.22 3.734 1.22 c
+3.399 1.22 3.164 1.037 3.028 0.676 c
+3.028 -2.175 l
+2.381 -2.175 l
+2.381 1.808 l
+3.013 1.808 l
+3.028 1.397 l
+3.204 1.72 3.447 1.881 3.763 1.881 c
+3.869 1.881 3.958 1.86 4.027 1.823 c
+h
+6.026 -2.248 m
+5.527 -2.248 5.145 -2.102 4.88 -1.808 c
+4.615 -1.514 4.484 -1.08 4.484 -0.5 c
+4.484 -0.029 l
+4.484 0.566 4.608 1.033 4.866 1.367 c
+5.13 1.709 5.49 1.881 5.953 1.881 c
+6.412 1.881 6.755 1.727 6.982 1.426 c
+7.217 1.132 7.339 0.669 7.35 0.044 c
+7.35 -0.382 l
+5.13 -0.382 l
+5.13 -0.47 l
+5.13 -0.904 5.207 -1.216 5.365 -1.411 c
+5.531 -1.598 5.762 -1.691 6.056 -1.691 c
+6.251 -1.691 6.423 -1.657 6.57 -1.587 c
+6.718 -1.51 6.853 -1.392 6.982 -1.234 c
+7.32 -1.646 l
+7.034 -2.05 6.604 -2.248 6.026 -2.248 c
+5.953 1.323 m
+5.677 1.323 5.475 1.228 5.35 1.044 c
+5.222 0.856 5.149 0.566 5.13 0.177 c
+6.703 0.177 l
+6.703 0.264 l
+6.681 0.647 6.614 0.915 6.497 1.073 c
+6.379 1.239 6.196 1.323 5.953 1.323 c
+11.009 -0.382 m
+11.009 -1.01 10.892 -1.481 10.657 -1.793 c
+10.429 -2.098 10.113 -2.248 9.702 -2.248 c
+9.297 -2.248 8.989 -2.098 8.775 -1.793 c
+8.775 -3.704 l
+8.129 -3.704 l
+8.129 1.808 l
+8.717 1.808 l
+8.761 1.367 l
+8.974 1.709 9.282 1.881 9.687 1.881 c
+10.128 1.881 10.454 1.727 10.672 1.426 c
+10.884 1.121 10.998 0.665 11.009 0.059 c
+h
+10.363 0 m
+10.363 0.441 10.293 0.765 10.157 0.97 c
+10.017 1.183 9.797 1.294 9.496 1.294 c
+9.18 1.294 8.941 1.139 8.775 0.838 c
+8.775 -1.234 l
+8.941 -1.539 9.18 -1.691 9.496 -1.691 c
+9.789 -1.691 10.003 -1.587 10.142 -1.382 c
+10.279 -1.168 10.352 -0.837 10.363 -0.397 c
+h
+11.715 0 m
+11.715 0.578 11.851 1.033 12.127 1.367 c
+12.409 1.709 12.781 1.881 13.244 1.881 c
+13.703 1.881 14.071 1.713 14.346 1.382 c
+14.629 1.058 14.776 0.611 14.787 0.044 c
+14.787 -0.382 l
+14.787 -0.951 14.644 -1.407 14.361 -1.749 c
+14.086 -2.084 13.718 -2.248 13.259 -2.248 c
+12.796 -2.248 12.424 -2.087 12.141 -1.764 c
+11.866 -1.433 11.723 -0.992 11.715 -0.441 c
+h
+12.362 -0.382 m
+12.362 -0.786 12.439 -1.103 12.597 -1.338 c
+12.762 -1.573 12.983 -1.691 13.259 -1.691 c
+13.824 -1.691 14.119 -1.278 14.14 -0.455 c
+14.14 0 l
+14.14 0.401 14.056 0.721 13.891 0.956 c
+13.733 1.199 13.516 1.323 13.244 1.323 c
+12.979 1.323 12.762 1.199 12.597 0.956 c
+12.439 0.721 12.362 0.401 12.362 0 c
+h
+17.565 -1.161 m
+17.565 -1.014 17.51 -0.893 17.404 -0.794 c
+17.294 -0.698 17.088 -0.58 16.787 -0.441 c
+16.441 -0.294 16.199 -0.172 16.052 -0.073 c
+15.904 0.033 15.794 0.151 15.728 0.279 c
+15.659 0.405 15.625 0.563 15.625 0.75 c
+15.625 1.073 15.742 1.341 15.978 1.558 c
+16.213 1.771 16.515 1.881 16.889 1.881 c
+17.271 1.881 17.58 1.768 17.815 1.544 c
+18.05 1.316 18.168 1.029 18.168 0.676 c
+17.521 0.676 l
+17.521 0.852 17.463 1.004 17.344 1.132 c
+17.228 1.257 17.073 1.323 16.889 1.323 c
+16.691 1.323 16.54 1.268 16.434 1.162 c
+16.324 1.062 16.272 0.93 16.272 0.765 c
+16.272 0.636 16.309 0.53 16.39 0.441 c
+16.467 0.36 16.658 0.258 16.962 0.133 c
+17.44 -0.055 17.772 -0.243 17.947 -0.426 c
+18.124 -0.602 18.212 -0.831 18.212 -1.103 c
+18.212 -1.455 18.088 -1.735 17.845 -1.94 c
+17.61 -2.146 17.294 -2.248 16.904 -2.248 c
+16.482 -2.248 16.143 -2.131 15.89 -1.896 c
+15.632 -1.654 15.507 -1.348 15.507 -0.985 c
+16.154 -0.985 l
+16.162 -1.213 16.232 -1.389 16.36 -1.514 c
+16.485 -1.631 16.669 -1.691 16.904 -1.691 c
+17.117 -1.691 17.279 -1.643 17.389 -1.543 c
+17.506 -1.448 17.565 -1.319 17.565 -1.161 c
+19.756 -2.175 -0.646 3.983 re
+19.8 2.851 m
+19.8 2.741 19.771 2.65 19.711 2.573 c
+19.653 2.502 19.557 2.469 19.432 2.469 c
+19.314 2.469 19.219 2.502 19.153 2.573 c
+19.094 2.65 19.065 2.741 19.065 2.851 c
+19.065 2.969 19.094 3.061 19.153 3.131 c
+19.219 3.208 19.314 3.248 19.432 3.248 c
+19.557 3.248 19.653 3.208 19.711 3.131 c
+19.771 3.05 19.8 2.959 19.8 2.851 c
+21.622 2.764 m
+21.622 1.808 l
+22.225 1.808 l
+22.225 1.279 l
+21.622 1.279 l
+21.622 -1.19 l
+21.622 -1.348 21.645 -1.466 21.696 -1.543 c
+21.755 -1.624 21.843 -1.66 21.961 -1.66 c
+22.048 -1.66 22.137 -1.646 22.225 -1.617 c
+22.225 -2.175 l
+22.078 -2.223 21.924 -2.248 21.77 -2.248 c
+21.512 -2.248 21.317 -2.157 21.182 -1.97 c
+21.042 -1.786 20.976 -1.525 20.976 -1.19 c
+20.976 1.279 l
+20.373 1.279 l
+20.373 1.808 l
+20.976 1.808 l
+20.976 2.764 l
+h
+22.784 0 m
+22.784 0.578 22.919 1.033 23.195 1.367 c
+23.478 1.709 23.85 1.881 24.312 1.881 c
+24.772 1.881 25.139 1.713 25.415 1.382 c
+25.697 1.058 25.845 0.611 25.855 0.044 c
+25.855 -0.382 l
+25.855 -0.951 25.712 -1.407 25.429 -1.749 c
+25.154 -2.084 24.787 -2.248 24.327 -2.248 c
+23.864 -2.248 23.493 -2.087 23.21 -1.764 c
+22.934 -1.433 22.791 -0.992 22.784 -0.441 c
+h
+23.43 -0.382 m
+23.43 -0.786 23.507 -1.103 23.665 -1.338 c
+23.831 -1.573 24.051 -1.691 24.327 -1.691 c
+24.893 -1.691 25.186 -1.278 25.209 -0.455 c
+25.209 0 l
+25.209 0.401 25.124 0.721 24.959 0.956 c
+24.801 1.199 24.584 1.323 24.312 1.323 c
+24.048 1.323 23.831 1.199 23.665 0.956 c
+23.507 0.721 23.43 0.401 23.43 0 c
+h
+28.34 1.191 m
+28.251 1.209 28.153 1.22 28.046 1.22 c
+27.711 1.22 27.476 1.037 27.341 0.676 c
+27.341 -2.175 l
+26.693 -2.175 l
+26.693 1.808 l
+27.326 1.808 l
+27.341 1.397 l
+27.517 1.72 27.759 1.881 28.075 1.881 c
+28.182 1.881 28.27 1.86 28.34 1.823 c
+h
+30.148 -1.087 m
+30.868 1.808 l
+31.559 1.808 l
+30.265 -2.734 l
+30.166 -3.075 30.023 -3.337 29.839 -3.513 c
+29.662 -3.69 29.46 -3.777 29.236 -3.777 c
+29.148 -3.777 29.034 -3.755 28.899 -3.719 c
+28.899 -3.175 l
+29.045 -3.189 l
+29.229 -3.189 29.376 -3.146 29.487 -3.057 c
+29.593 -2.969 29.681 -2.811 29.751 -2.587 c
+29.868 -2.146 l
+28.708 1.808 l
+29.413 1.808 l
+h
+35.293 -1.691 m
+35.505 -1.691 35.678 -1.627 35.807 -1.5 c
+35.943 -1.363 36.016 -1.172 36.027 -0.926 c
+36.645 -0.926 l
+36.623 -1.308 36.487 -1.627 36.234 -1.882 c
+35.976 -2.127 35.663 -2.248 35.293 -2.248 c
+34.8 -2.248 34.425 -2.098 34.161 -1.793 c
+33.903 -1.481 33.778 -1.014 33.778 -0.397 c
+33.778 0.044 l
+33.778 0.64 33.903 1.095 34.161 1.411 c
+34.425 1.723 34.8 1.881 35.293 1.881 c
+35.693 1.881 36.012 1.75 36.248 1.484 c
+36.49 1.228 36.623 0.882 36.645 0.441 c
+36.027 0.441 l
+36.005 0.735 35.932 0.956 35.807 1.103 c
+35.689 1.249 35.517 1.323 35.293 1.323 c
+34.998 1.323 34.782 1.224 34.646 1.029 c
+34.506 0.842 34.433 0.533 34.425 0.103 c
+34.425 -0.411 l
+34.425 -0.881 34.491 -1.216 34.631 -1.411 c
+34.778 -1.598 34.998 -1.691 35.293 -1.691 c
+37.262 0 m
+37.262 0.578 37.398 1.033 37.674 1.367 c
+37.957 1.709 38.328 1.881 38.79 1.881 c
+39.25 1.881 39.617 1.713 39.893 1.382 c
+40.176 1.058 40.323 0.611 40.334 0.044 c
+40.334 -0.382 l
+40.334 -0.951 40.191 -1.407 39.908 -1.749 c
+39.632 -2.084 39.265 -2.248 38.806 -2.248 c
+38.343 -2.248 37.971 -2.087 37.689 -1.764 c
+37.413 -1.433 37.269 -0.992 37.262 -0.441 c
+h
+37.909 -0.382 m
+37.909 -0.786 37.986 -1.103 38.144 -1.338 c
+38.31 -1.573 38.53 -1.691 38.806 -1.691 c
+39.372 -1.691 39.665 -1.278 39.687 -0.455 c
+39.687 0 l
+39.687 0.401 39.603 0.721 39.438 0.956 c
+39.28 1.199 39.062 1.323 38.79 1.323 c
+38.526 1.323 38.31 1.199 38.144 0.956 c
+37.986 0.721 37.909 0.401 37.909 0 c
+h
+41.79 1.808 m
+41.804 1.44 l
+42.046 1.735 42.366 1.881 42.759 1.881 c
+43.201 1.881 43.509 1.683 43.685 1.294 c
+43.939 1.683 44.288 1.881 44.729 1.881 c
+45.464 1.881 45.839 1.419 45.861 0.5 c
+45.861 -2.175 l
+45.215 -2.175 l
+45.215 0.441 l
+45.215 0.735 45.159 0.948 45.053 1.087 c
+44.953 1.224 44.781 1.294 44.538 1.294 c
+44.34 1.294 44.178 1.213 44.053 1.058 c
+43.935 0.912 43.866 0.721 43.847 0.485 c
+43.847 -2.175 l
+43.186 -2.175 l
+43.186 0.47 l
+43.186 1.018 42.965 1.294 42.524 1.294 c
+42.19 1.294 41.955 1.132 41.819 0.809 c
+41.819 -2.175 l
+41.172 -2.175 l
+41.172 1.808 l
+h
+47.448 1.808 m
+47.463 1.44 l
+47.706 1.735 48.026 1.881 48.419 1.881 c
+48.859 1.881 49.169 1.683 49.345 1.294 c
+49.599 1.683 49.948 1.881 50.389 1.881 c
+51.124 1.881 51.498 1.419 51.521 0.5 c
+51.521 -2.175 l
+50.873 -2.175 l
+50.873 0.441 l
+50.873 0.735 50.819 0.948 50.712 1.087 c
+50.613 1.224 50.44 1.294 50.197 1.294 c
+49.998 1.294 49.838 1.213 49.712 1.058 c
+49.595 0.912 49.525 0.721 49.506 0.485 c
+49.506 -2.175 l
+48.845 -2.175 l
+48.845 0.47 l
+48.845 1.018 48.624 1.294 48.184 1.294 c
+47.849 1.294 47.614 1.132 47.478 0.809 c
+47.478 -2.175 l
+46.831 -2.175 l
+46.831 1.808 l
+h
+53.196 -2.175 -0.647 3.983 re
+53.24 2.851 m
+53.24 2.741 53.211 2.65 53.152 2.573 c
+53.093 2.502 52.997 2.469 52.872 2.469 c
+52.755 2.469 52.66 2.502 52.593 2.573 c
+52.535 2.65 52.505 2.741 52.505 2.851 c
+52.505 2.969 52.535 3.061 52.593 3.131 c
+52.66 3.208 52.755 3.248 52.872 3.248 c
+52.997 3.248 53.093 3.208 53.152 3.131 c
+53.211 3.05 53.24 2.959 53.24 2.851 c
+55.062 2.764 m
+55.062 1.808 l
+55.665 1.808 l
+55.665 1.279 l
+55.062 1.279 l
+55.062 -1.19 l
+55.062 -1.348 55.085 -1.466 55.136 -1.543 c
+55.195 -1.624 55.283 -1.66 55.401 -1.66 c
+55.489 -1.66 55.577 -1.646 55.665 -1.617 c
+55.665 -2.175 l
+55.519 -2.223 55.364 -2.248 55.209 -2.248 c
+54.952 -2.248 54.758 -2.157 54.622 -1.97 c
+54.482 -1.786 54.416 -1.525 54.416 -1.19 c
+54.416 1.279 l
+53.813 1.279 l
+53.813 1.808 l
+54.416 1.808 l
+54.416 2.764 l
+h
+57.855 -2.248 m
+57.356 -2.248 56.974 -2.102 56.709 -1.808 c
+56.444 -1.514 56.312 -1.08 56.312 -0.5 c
+56.312 -0.029 l
+56.312 0.566 56.437 1.033 56.695 1.367 c
+56.959 1.709 57.319 1.881 57.782 1.881 c
+58.241 1.881 58.583 1.727 58.811 1.426 c
+59.046 1.132 59.168 0.669 59.178 0.044 c
+59.178 -0.382 l
+56.959 -0.382 l
+56.959 -0.47 l
+56.959 -0.904 57.036 -1.216 57.194 -1.411 c
+57.36 -1.598 57.591 -1.691 57.885 -1.691 c
+58.079 -1.691 58.252 -1.657 58.399 -1.587 c
+58.546 -1.51 58.682 -1.392 58.811 -1.234 c
+59.149 -1.646 l
+58.862 -2.05 58.432 -2.248 57.855 -2.248 c
+57.782 1.323 m
+57.507 1.323 57.304 1.228 57.179 1.044 c
+57.051 0.856 56.977 0.566 56.959 0.177 c
+58.532 0.177 l
+58.532 0.264 l
+58.509 0.647 58.443 0.915 58.326 1.073 c
+58.208 1.239 58.025 1.323 57.782 1.323 c
+59.825 0 m
+59.825 0.607 59.935 1.073 60.163 1.397 c
+60.398 1.72 60.726 1.881 61.148 1.881 c
+61.53 1.881 61.828 1.723 62.045 1.411 c
+62.045 3.469 l
+62.692 3.469 l
+62.692 -2.175 l
+62.104 -2.175 l
+62.06 -1.749 l
+61.854 -2.084 61.549 -2.248 61.148 -2.248 c
+60.737 -2.248 60.413 -2.094 60.178 -1.778 c
+59.943 -1.455 59.825 -0.999 59.825 -0.411 c
+h
+60.472 -0.382 m
+60.472 -0.823 60.535 -1.153 60.663 -1.367 c
+60.799 -1.573 61.019 -1.675 61.324 -1.675 c
+61.648 -1.675 61.887 -1.514 62.045 -1.19 c
+62.045 0.823 l
+61.875 1.135 61.637 1.294 61.324 1.294 c
+61.019 1.294 60.799 1.191 60.663 0.985 c
+60.535 0.779 60.472 0.456 60.472 0.015 c
+h
+67.351 -1.161 m
+67.351 -1.014 67.296 -0.893 67.189 -0.794 c
+67.079 -0.698 66.873 -0.58 66.572 -0.441 c
+66.226 -0.294 65.984 -0.172 65.837 -0.073 c
+65.69 0.033 65.58 0.151 65.514 0.279 c
+65.443 0.405 65.411 0.563 65.411 0.75 c
+65.411 1.073 65.528 1.341 65.763 1.558 c
+65.999 1.771 66.3 1.881 66.675 1.881 c
+67.057 1.881 67.365 1.768 67.601 1.544 c
+67.836 1.316 67.953 1.029 67.953 0.676 c
+67.307 0.676 l
+67.307 0.852 67.248 1.004 67.13 1.132 c
+67.013 1.257 66.858 1.323 66.675 1.323 c
+66.476 1.323 66.326 1.268 66.22 1.162 c
+66.109 1.062 66.058 0.93 66.058 0.765 c
+66.058 0.636 66.094 0.53 66.175 0.441 c
+66.253 0.36 66.444 0.258 66.748 0.133 c
+67.226 -0.055 67.557 -0.243 67.733 -0.426 c
+67.909 -0.602 67.998 -0.831 67.998 -1.103 c
+67.998 -1.455 67.872 -1.735 67.631 -1.94 c
+67.396 -2.146 67.079 -2.248 66.69 -2.248 c
+66.267 -2.248 65.929 -2.131 65.676 -1.896 c
+65.418 -1.654 65.293 -1.348 65.293 -0.985 c
+65.94 -0.985 l
+65.948 -1.213 66.017 -1.389 66.146 -1.514 c
+66.27 -1.631 66.455 -1.691 66.69 -1.691 c
+66.903 -1.691 67.064 -1.643 67.174 -1.543 c
+67.292 -1.448 67.351 -1.319 67.351 -1.161 c
+69.688 2.764 m
+69.688 1.808 l
+70.291 1.808 l
+70.291 1.279 l
+69.688 1.279 l
+69.688 -1.19 l
+69.688 -1.348 69.711 -1.466 69.761 -1.543 c
+69.821 -1.624 69.909 -1.66 70.026 -1.66 c
+70.114 -1.66 70.203 -1.646 70.291 -1.617 c
+70.291 -2.175 l
+70.144 -2.223 69.989 -2.248 69.835 -2.248 c
+69.578 -2.248 69.383 -2.157 69.247 -1.97 c
+69.108 -1.786 69.042 -1.525 69.042 -1.19 c
+69.042 1.279 l
+68.439 1.279 l
+68.439 1.808 l
+69.042 1.808 l
+69.042 2.764 l
+h
+73.098 -2.175 m
+73.058 -2.087 73.032 -1.94 73.025 -1.735 c
+72.79 -2.08 72.496 -2.248 72.142 -2.248 c
+71.779 -2.248 71.496 -2.153 71.29 -1.955 c
+71.092 -1.749 70.997 -1.462 70.997 -1.087 c
+70.997 -0.687 71.132 -0.367 71.408 -0.132 c
+71.68 0.11 72.055 0.235 72.525 0.235 c
+73.01 0.235 l
+73.01 0.661 l
+73.01 0.897 72.955 1.062 72.848 1.162 c
+72.738 1.268 72.576 1.323 72.364 1.323 c
+72.165 1.323 72.003 1.264 71.878 1.147 c
+71.761 1.029 71.702 0.882 71.702 0.706 c
+71.055 0.706 l
+71.055 0.9 71.114 1.091 71.232 1.279 c
+71.357 1.463 71.518 1.61 71.716 1.72 c
+71.922 1.827 72.15 1.881 72.408 1.881 c
+72.809 1.881 73.113 1.779 73.318 1.573 c
+73.532 1.367 73.646 1.073 73.657 0.691 c
+73.657 -1.323 l
+73.657 -1.627 73.694 -1.892 73.775 -2.117 c
+73.775 -2.175 l
+h
+72.231 -1.66 m
+72.396 -1.66 72.547 -1.617 72.687 -1.529 c
+72.834 -1.44 72.94 -1.33 73.01 -1.19 c
+73.01 -0.249 l
+72.643 -0.249 l
+72.327 -0.249 72.084 -0.32 71.907 -0.455 c
+71.731 -0.584 71.643 -0.771 71.643 -1.014 c
+71.643 -1.242 71.687 -1.407 71.776 -1.514 c
+71.864 -1.613 72.015 -1.66 72.231 -1.66 c
+75.509 2.764 m
+75.509 1.808 l
+76.111 1.808 l
+76.111 1.279 l
+75.509 1.279 l
+75.509 -1.19 l
+75.509 -1.348 75.531 -1.466 75.583 -1.543 c
+75.641 -1.624 75.73 -1.66 75.847 -1.66 c
+75.936 -1.66 76.024 -1.646 76.111 -1.617 c
+76.111 -2.175 l
+75.965 -2.223 75.81 -2.248 75.656 -2.248 c
+75.398 -2.248 75.204 -2.157 75.068 -1.97 c
+74.928 -1.786 74.862 -1.525 74.862 -1.19 c
+74.862 1.279 l
+74.259 1.279 l
+74.259 1.808 l
+74.862 1.808 l
+74.862 2.764 l
+h
+78.905 -1.822 m
+78.688 -2.109 78.376 -2.248 77.964 -2.248 c
+77.6 -2.248 77.324 -2.127 77.141 -1.882 c
+76.965 -1.627 76.869 -1.264 76.861 -0.794 c
+76.861 1.808 l
+77.508 1.808 l
+77.508 -0.735 l
+77.508 -1.363 77.692 -1.675 78.066 -1.675 c
+78.467 -1.675 78.743 -1.5 78.889 -1.147 c
+78.889 1.808 l
+79.537 1.808 l
+79.537 -2.175 l
+78.92 -2.175 l
+h
+82.461 -1.161 m
+82.461 -1.014 82.407 -0.893 82.3 -0.794 c
+82.19 -0.698 81.984 -0.58 81.683 -0.441 c
+81.337 -0.294 81.095 -0.172 80.948 -0.073 c
+80.801 0.033 80.69 0.151 80.624 0.279 c
+80.555 0.405 80.522 0.563 80.522 0.75 c
+80.522 1.073 80.639 1.341 80.874 1.558 c
+81.11 1.771 81.411 1.881 81.785 1.881 c
+82.168 1.881 82.477 1.768 82.712 1.544 c
+82.947 1.316 83.064 1.029 83.064 0.676 c
+82.417 0.676 l
+82.417 0.852 82.359 1.004 82.241 1.132 c
+82.124 1.257 81.969 1.323 81.785 1.323 c
+81.587 1.323 81.436 1.268 81.33 1.162 c
+81.22 1.062 81.168 0.93 81.168 0.765 c
+81.168 0.636 81.205 0.53 81.286 0.441 c
+81.363 0.36 81.554 0.258 81.859 0.133 c
+82.336 -0.055 82.668 -0.243 82.844 -0.426 c
+83.02 -0.602 83.109 -0.831 83.109 -1.103 c
+83.109 -1.455 82.984 -1.735 82.741 -1.94 c
+82.506 -2.146 82.19 -2.248 81.8 -2.248 c
+81.378 -2.248 81.04 -2.131 80.786 -1.896 c
+80.529 -1.654 80.404 -1.348 80.404 -0.985 c
+81.05 -0.985 l
+81.058 -1.213 81.127 -1.389 81.256 -1.514 c
+81.382 -1.631 81.565 -1.691 81.8 -1.691 c
+82.014 -1.691 82.175 -1.643 82.286 -1.543 c
+82.403 -1.448 82.461 -1.319 82.461 -1.161 c
+85.343 -0.044 m
+85.343 -1.132 85.096 -2.098 84.607 -2.94 c
+84.343 -3.388 84.067 -3.711 83.785 -3.91 c
+83.667 -3.484 l
+83.969 -3.189 84.215 -2.738 84.402 -2.131 c
+84.597 -1.525 84.696 -0.86 84.696 -0.132 c
+84.696 0 l
+84.696 0.93 84.541 1.764 84.24 2.499 c
+84.071 2.899 83.88 3.219 83.667 3.454 c
+83.785 3.866 l
+84.057 3.678 84.321 3.381 84.578 2.969 c
+85.085 2.117 85.343 1.11 85.343 -0.044 c
+86.386 -1.822 m
+86.386 -1.705 86.419 -1.61 86.489 -1.529 c
+86.556 -1.452 86.658 -1.411 86.798 -1.411 c
+86.945 -1.411 87.051 -1.452 87.121 -1.529 c
+87.198 -1.61 87.239 -1.705 87.239 -1.822 c
+87.239 -1.932 87.198 -2.024 87.121 -2.102 c
+87.051 -2.179 86.945 -2.219 86.798 -2.219 c
+86.658 -2.219 86.556 -2.179 86.489 -2.102 c
+86.419 -2.024 86.386 -1.932 86.386 -1.822 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 327.439 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 320.5995 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.263 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.319 2.385 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.188 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.029 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.293 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.22 l
+13.641 2.22 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.22 l
+15.728 2.22 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.323 l
+14.501 -0.393 14.523 -0.455 14.552 -0.515 c
+14.589 -0.565 14.645 -0.61 14.714 -0.646 c
+14.791 -0.675 14.905 -0.69 15.052 -0.69 c
+15.188 -0.69 15.324 -0.687 15.464 -0.675 c
+15.599 -0.658 15.732 -0.632 15.861 -0.602 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.23 15.626 -1.249 c
+15.545 -1.261 15.468 -1.267 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.308 14.832 -1.308 c
+14.645 -1.308 14.483 -1.294 14.346 -1.263 c
+14.218 -1.227 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.025 13.788 -0.955 c
+13.729 -0.878 13.685 -0.801 13.656 -0.72 c
+13.627 -0.632 13.604 -0.544 13.597 -0.455 c
+13.586 -0.36 13.582 -0.264 13.582 -0.176 c
+h
+22.677 -1.323 m
+22.391 -1.323 22.148 -1.282 21.942 -1.205 c
+21.737 -1.117 21.564 -0.995 21.428 -0.837 c
+21.288 -0.683 21.185 -0.496 21.12 -0.279 c
+21.049 -0.055 21.016 0.191 21.016 0.456 c
+21.016 0.75 21.049 1.008 21.12 1.235 c
+21.197 1.459 21.303 1.646 21.442 1.794 c
+21.59 1.948 21.766 2.066 21.972 2.147 c
+22.177 2.234 22.412 2.279 22.677 2.279 c
+22.901 2.279 23.104 2.249 23.28 2.19 c
+23.456 2.132 23.607 2.047 23.736 1.941 c
+23.861 1.841 23.963 1.72 24.044 1.573 c
+24.121 1.434 24.177 1.283 24.206 1.118 c
+23.295 1.073 l
+23.265 1.249 23.196 1.389 23.089 1.5 c
+22.99 1.606 22.846 1.661 22.662 1.661 c
+22.416 1.661 22.24 1.559 22.134 1.353 c
+22.023 1.154 21.972 0.867 21.972 0.485 c
+21.972 -0.309 22.207 -0.706 22.677 -0.706 c
+22.843 -0.706 22.986 -0.654 23.104 -0.544 c
+23.221 -0.437 23.295 -0.276 23.324 -0.058 c
+24.235 -0.103 l
+24.206 -0.272 24.151 -0.426 24.074 -0.573 c
+24.004 -0.72 23.901 -0.852 23.765 -0.97 c
+23.636 -1.08 23.478 -1.168 23.295 -1.234 c
+23.118 -1.294 22.913 -1.323 22.677 -1.323 c
+26.076 1.515 m
+26.194 1.786 26.344 1.985 26.532 2.103 c
+26.716 2.22 26.936 2.279 27.194 2.279 c
+27.399 2.279 27.568 2.242 27.708 2.176 c
+27.855 2.106 27.965 2.014 28.046 1.897 c
+28.135 1.779 28.193 1.636 28.222 1.47 c
+28.259 1.301 28.281 1.125 28.281 0.941 c
+28.281 -1.263 l
+27.37 -1.263 l
+27.37 0.736 l
+27.37 0.871 27.359 0.992 27.341 1.103 c
+27.329 1.209 27.304 1.297 27.267 1.367 c
+27.227 1.444 27.168 1.503 27.09 1.544 c
+27.021 1.58 26.929 1.602 26.811 1.602 c
+26.701 1.602 26.606 1.577 26.518 1.529 c
+26.429 1.478 26.348 1.411 26.282 1.324 c
+26.223 1.235 26.172 1.125 26.136 1 c
+26.106 0.882 26.091 0.75 26.091 0.603 c
+26.091 -1.263 l
+25.18 -1.263 l
+25.18 3.514 l
+26.091 3.514 l
+26.091 2.205 l
+26.091 2.135 26.084 2.066 26.076 1.999 c
+26.076 1.794 l
+26.076 1.735 26.069 1.679 26.061 1.632 c
+26.061 1.515 l
+h
+30.799 -1.323 m
+30.541 -1.323 30.313 -1.286 30.107 -1.22 c
+29.901 -1.143 29.725 -1.028 29.579 -0.881 c
+29.431 -0.727 29.313 -0.536 29.226 -0.309 c
+29.145 -0.085 29.108 0.181 29.108 0.485 c
+29.108 0.816 29.153 1.095 29.24 1.324 c
+29.336 1.559 29.465 1.742 29.623 1.881 c
+29.788 2.018 29.976 2.117 30.181 2.176 c
+30.387 2.242 30.597 2.279 30.813 2.279 c
+31.085 2.279 31.32 2.227 31.519 2.132 c
+31.725 2.043 31.89 1.912 32.018 1.735 c
+32.154 1.565 32.253 1.36 32.313 1.118 c
+32.378 0.882 32.415 0.618 32.415 0.324 c
+32.415 0.309 l
+30.049 0.309 l
+30.049 0.162 30.063 0.023 30.093 -0.103 c
+30.13 -0.231 30.185 -0.345 30.254 -0.44 c
+30.321 -0.529 30.406 -0.598 30.504 -0.646 c
+30.6 -0.698 30.714 -0.72 30.842 -0.72 c
+30.997 -0.72 31.137 -0.687 31.254 -0.617 c
+31.379 -0.551 31.468 -0.448 31.519 -0.309 c
+32.357 -0.382 l
+32.328 -0.481 32.272 -0.588 32.195 -0.706 c
+32.114 -0.816 32.011 -0.918 31.886 -1.014 c
+31.769 -1.103 31.614 -1.176 31.43 -1.234 c
+31.254 -1.294 31.041 -1.323 30.799 -1.323 c
+30.799 1.706 m
+30.71 1.706 30.622 1.691 30.534 1.661 c
+30.446 1.632 30.365 1.58 30.298 1.515 c
+30.229 1.444 30.17 1.357 30.122 1.249 c
+30.082 1.139 30.063 1.014 30.063 0.867 c
+31.534 0.867 l
+31.534 1.004 31.507 1.125 31.46 1.235 c
+31.42 1.342 31.364 1.43 31.298 1.5 c
+31.239 1.565 31.166 1.617 31.077 1.646 c
+30.99 1.683 30.894 1.706 30.799 1.706 c
+34.859 -1.323 m
+34.572 -1.323 34.329 -1.282 34.124 -1.205 c
+33.918 -1.117 33.745 -0.995 33.61 -0.837 c
+33.47 -0.683 33.367 -0.496 33.301 -0.279 c
+33.231 -0.055 33.198 0.191 33.198 0.456 c
+33.198 0.75 33.231 1.008 33.301 1.235 c
+33.378 1.459 33.485 1.646 33.624 1.794 c
+33.772 1.948 33.948 2.066 34.154 2.147 c
+34.36 2.234 34.595 2.279 34.859 2.279 c
+35.083 2.279 35.285 2.249 35.462 2.19 c
+35.638 2.132 35.789 2.047 35.917 1.941 c
+36.043 1.841 36.145 1.72 36.226 1.573 c
+36.303 1.434 36.359 1.283 36.388 1.118 c
+35.476 1.073 l
+35.447 1.249 35.377 1.389 35.27 1.5 c
+35.171 1.606 35.028 1.661 34.844 1.661 c
+34.599 1.661 34.422 1.559 34.315 1.353 c
+34.205 1.154 34.154 0.867 34.154 0.485 c
+34.154 -0.309 34.389 -0.706 34.859 -0.706 c
+35.025 -0.706 35.168 -0.654 35.285 -0.544 c
+35.403 -0.437 35.476 -0.276 35.505 -0.058 c
+36.417 -0.103 l
+36.388 -0.272 36.332 -0.426 36.255 -0.573 c
+36.186 -0.72 36.083 -0.852 35.947 -0.97 c
+35.818 -1.08 35.661 -1.168 35.476 -1.234 c
+35.3 -1.294 35.094 -1.323 34.859 -1.323 c
+39.688 -1.263 m
+38.761 0.309 l
+38.365 0.044 l
+38.365 -1.263 l
+37.468 -1.263 l
+37.468 3.514 l
+38.365 3.514 l
+38.365 0.779 l
+39.615 2.22 l
+40.584 2.22 l
+39.364 0.853 l
+40.673 -1.263 l
+h
+44.631 0.485 m
+44.631 0.21 44.594 -0.04 44.527 -0.264 c
+44.457 -0.481 44.355 -0.669 44.219 -0.823 c
+44.079 -0.981 43.902 -1.103 43.69 -1.19 c
+43.473 -1.278 43.219 -1.323 42.925 -1.323 c
+42.649 -1.323 42.403 -1.278 42.19 -1.19 c
+41.984 -1.103 41.812 -0.981 41.676 -0.823 c
+41.536 -0.669 41.433 -0.481 41.367 -0.264 c
+41.298 -0.04 41.265 0.21 41.265 0.485 c
+41.265 0.738 41.294 0.974 41.352 1.191 c
+41.419 1.415 41.521 1.606 41.661 1.764 c
+41.797 1.929 41.973 2.058 42.19 2.147 c
+42.403 2.234 42.661 2.279 42.954 2.279 c
+43.267 2.279 43.528 2.234 43.734 2.147 c
+43.947 2.058 44.12 1.929 44.248 1.764 c
+44.384 1.606 44.483 1.415 44.542 1.191 c
+44.6 0.974 44.631 0.738 44.631 0.485 c
+43.675 0.485 m
+43.675 0.691 43.66 0.867 43.631 1.014 c
+43.609 1.162 43.572 1.283 43.513 1.382 c
+43.455 1.478 43.381 1.548 43.293 1.588 c
+43.204 1.636 43.094 1.661 42.969 1.661 c
+42.705 1.661 42.514 1.562 42.396 1.367 c
+42.279 1.18 42.219 0.886 42.219 0.485 c
+42.219 0.063 42.279 -0.243 42.396 -0.426 c
+42.514 -0.613 42.69 -0.706 42.925 -0.706 c
+43.05 -0.706 43.164 -0.687 43.264 -0.646 c
+43.359 -0.598 43.44 -0.525 43.499 -0.426 c
+43.565 -0.33 43.609 -0.205 43.631 -0.058 c
+43.66 0.088 43.675 0.268 43.675 0.485 c
+46.368 2.22 m
+46.368 0.265 l
+46.368 0.125 46.376 0 46.398 -0.118 c
+46.416 -0.228 46.449 -0.32 46.501 -0.397 c
+46.549 -0.478 46.607 -0.54 46.677 -0.588 c
+46.743 -0.628 46.828 -0.646 46.927 -0.646 c
+47.015 -0.646 47.096 -0.628 47.177 -0.588 c
+47.265 -0.54 47.339 -0.47 47.397 -0.382 c
+47.457 -0.286 47.5 -0.176 47.53 -0.058 c
+47.567 0.067 47.588 0.206 47.588 0.353 c
+47.588 2.22 l
+48.485 2.22 l
+48.485 -0.484 l
+48.485 -0.72 l
+48.492 -0.801 48.5 -0.878 48.5 -0.955 c
+48.5 -1.146 l
+48.507 -1.198 48.515 -1.234 48.515 -1.263 c
+47.662 -1.263 l
+47.651 -1.234 47.64 -1.198 47.632 -1.146 c
+47.632 -0.955 l
+47.632 -0.889 47.625 -0.819 47.617 -0.75 c
+47.617 -0.573 l
+47.603 -0.573 l
+47.486 -0.837 47.331 -1.028 47.147 -1.146 c
+46.971 -1.263 46.769 -1.323 46.545 -1.323 c
+46.339 -1.323 46.166 -1.286 46.03 -1.22 c
+45.891 -1.153 45.78 -1.058 45.693 -0.941 c
+45.612 -0.823 45.552 -0.687 45.516 -0.529 c
+45.487 -0.363 45.472 -0.187 45.472 0 c
+45.472 2.22 l
+h
+50.15 1.602 m
+49.606 1.602 l
+49.606 2.22 l
+50.194 2.22 l
+50.473 3.117 l
+51.046 3.117 l
+51.046 2.22 l
+52.281 2.22 l
+52.281 1.602 l
+51.046 1.602 l
+51.046 -0.103 l
+51.046 -0.323 l
+51.054 -0.393 51.076 -0.455 51.105 -0.515 c
+51.142 -0.565 51.197 -0.61 51.267 -0.646 c
+51.344 -0.675 51.458 -0.69 51.605 -0.69 c
+51.741 -0.69 51.877 -0.687 52.016 -0.675 c
+52.153 -0.658 52.284 -0.632 52.413 -0.602 c
+52.413 -1.205 l
+52.332 -1.216 52.255 -1.23 52.178 -1.249 c
+52.097 -1.261 52.02 -1.267 51.943 -1.278 c
+51.862 -1.286 51.774 -1.294 51.679 -1.294 c
+51.59 -1.301 51.491 -1.308 51.384 -1.308 c
+51.197 -1.308 51.035 -1.294 50.9 -1.263 c
+50.771 -1.227 50.657 -1.183 50.561 -1.132 c
+50.473 -1.084 50.399 -1.025 50.341 -0.955 c
+50.283 -0.878 50.238 -0.801 50.208 -0.72 c
+50.179 -0.632 50.157 -0.544 50.15 -0.455 c
+50.139 -0.36 50.135 -0.264 50.135 -0.176 c
+h
+58.26 0.838 1.866 -0.794 re
+58.26 0.044 m
+62.32 0.838 1.867 -0.794 re
+62.32 0.044 m
+70.603 -2.631 m
+70.603 3.514 l
+72.529 3.514 l
+72.529 2.896 l
+71.456 2.896 l
+71.456 -2.013 l
+72.529 -2.013 l
+72.529 -2.631 l
+h
+75.561 1.602 m
+75.561 -1.263 l
+74.664 -1.263 l
+74.664 1.602 l
+73.841 1.602 l
+73.841 2.22 l
+74.664 2.22 l
+74.664 2.484 l
+74.664 2.61 74.679 2.741 74.708 2.882 c
+74.745 3.017 74.815 3.135 74.914 3.234 c
+75.02 3.341 75.164 3.429 75.34 3.499 c
+75.517 3.564 75.741 3.601 76.016 3.601 c
+76.23 3.601 76.428 3.591 76.604 3.572 c
+76.78 3.55 76.932 3.532 77.06 3.514 c
+77.06 2.926 l
+76.932 2.944 76.788 2.959 76.634 2.97 c
+76.476 2.977 76.325 2.984 76.178 2.984 c
+76.049 2.984 75.947 2.97 75.87 2.94 c
+75.789 2.911 75.726 2.87 75.679 2.822 c
+75.627 2.771 75.594 2.708 75.575 2.631 c
+75.564 2.562 75.561 2.484 75.561 2.396 c
+75.561 2.22 l
+76.986 2.22 l
+76.986 1.602 l
+h
+80.08 -0.646 m
+81.212 -0.646 l
+81.212 -1.263 l
+77.905 -1.263 l
+77.905 -0.646 l
+79.17 -0.646 l
+79.17 1.602 l
+78.243 1.602 l
+78.243 2.22 l
+80.08 2.22 l
+h
+79.17 3.514 0.911 -0.676 re
+79.17 2.837 m
+84.142 -0.646 m
+85.273 -0.646 l
+85.273 -1.263 l
+81.966 -1.263 l
+81.966 -0.646 l
+83.23 -0.646 l
+83.23 2.896 l
+82.304 2.896 l
+82.304 3.514 l
+84.142 3.514 l
+h
+87.658 -1.323 m
+87.401 -1.323 87.173 -1.286 86.968 -1.22 c
+86.762 -1.143 86.585 -1.028 86.438 -0.881 c
+86.291 -0.727 86.174 -0.536 86.085 -0.309 c
+86.004 -0.085 85.968 0.181 85.968 0.485 c
+85.968 0.816 86.012 1.095 86.1 1.324 c
+86.195 1.559 86.324 1.742 86.482 1.881 c
+86.648 2.018 86.835 2.117 87.041 2.176 c
+87.246 2.242 87.456 2.279 87.673 2.279 c
+87.945 2.279 88.18 2.227 88.379 2.132 c
+88.584 2.043 88.749 1.912 88.878 1.735 c
+89.014 1.565 89.113 1.36 89.172 1.118 c
+89.239 0.882 89.275 0.618 89.275 0.324 c
+89.275 0.309 l
+86.908 0.309 l
+86.908 0.162 86.923 0.023 86.953 -0.103 c
+86.989 -0.231 87.045 -0.345 87.114 -0.44 c
+87.18 -0.529 87.265 -0.598 87.364 -0.646 c
+87.46 -0.698 87.573 -0.72 87.702 -0.72 c
+87.857 -0.72 87.996 -0.687 88.113 -0.617 c
+88.239 -0.551 88.327 -0.448 88.379 -0.309 c
+89.216 -0.382 l
+89.187 -0.481 89.131 -0.588 89.054 -0.706 c
+88.973 -0.816 88.871 -0.918 88.746 -1.014 c
+88.628 -1.103 88.474 -1.176 88.29 -1.234 c
+88.113 -1.294 87.901 -1.323 87.658 -1.323 c
+87.658 1.706 m
+87.57 1.706 87.481 1.691 87.394 1.661 c
+87.305 1.632 87.224 1.58 87.159 1.515 c
+87.088 1.444 87.03 1.357 86.982 1.249 c
+86.941 1.139 86.923 1.014 86.923 0.867 c
+88.393 0.867 l
+88.393 1.004 88.367 1.125 88.319 1.235 c
+88.279 1.342 88.224 1.43 88.158 1.5 c
+88.099 1.565 88.026 1.617 87.938 1.646 c
+87.849 1.683 87.754 1.706 87.658 1.706 c
+90.528 -2.631 m
+90.528 -2.013 l
+91.601 -2.013 l
+91.601 2.896 l
+90.528 2.896 l
+90.528 3.514 l
+92.454 3.514 l
+92.454 -2.631 l
+h
+f
+Q
+q 1 0 0 1 311.4185 304.4451 cm
+0 0 m
+0 5.351 l
+1.278 5.351 l
+1.903 5.351 2.389 5.152 2.734 4.763 c
+3.075 4.37 3.248 3.822 3.248 3.117 c
+3.248 2.22 l
+3.248 1.515 3.072 0.963 2.719 0.574 c
+2.373 0.192 1.87 0 1.205 0 c
+h
+0.676 4.778 m
+0.676 0.574 l
+1.22 0.574 l
+1.69 0.574 2.032 0.709 2.248 0.985 c
+2.473 1.257 2.587 1.661 2.587 2.191 c
+2.587 3.132 l
+2.587 3.697 2.477 4.116 2.263 4.381 c
+2.047 4.645 1.72 4.778 1.278 4.778 c
+h
+4.91 0 -0.647 3.984 re
+4.953 5.027 m
+4.953 4.917 4.924 4.825 4.865 4.748 c
+4.806 4.678 4.711 4.645 4.586 4.645 c
+4.469 4.645 4.373 4.678 4.307 4.748 c
+4.247 4.825 4.218 4.917 4.218 5.027 c
+4.218 5.145 4.247 5.237 4.307 5.307 c
+4.373 5.384 4.469 5.424 4.586 5.424 c
+4.711 5.424 4.806 5.384 4.865 5.307 c
+4.924 5.226 4.953 5.134 4.953 5.027 c
+7.864 1.015 m
+7.864 1.162 7.809 1.283 7.702 1.382 c
+7.592 1.478 7.386 1.596 7.085 1.735 c
+6.739 1.882 6.497 2.003 6.35 2.103 c
+6.202 2.209 6.092 2.326 6.026 2.455 c
+5.957 2.58 5.924 2.739 5.924 2.926 c
+5.924 3.249 6.041 3.517 6.277 3.734 c
+6.512 3.947 6.813 4.057 7.187 4.057 c
+7.57 4.057 7.879 3.944 8.114 3.72 c
+8.349 3.491 8.467 3.205 8.467 2.852 c
+7.819 2.852 l
+7.819 3.028 7.761 3.179 7.643 3.308 c
+7.526 3.433 7.372 3.499 7.187 3.499 c
+6.989 3.499 6.838 3.444 6.732 3.337 c
+6.622 3.238 6.57 3.105 6.57 2.94 c
+6.57 2.812 6.607 2.705 6.688 2.617 c
+6.765 2.536 6.956 2.433 7.262 2.309 c
+7.739 2.12 8.07 1.933 8.246 1.75 c
+8.422 1.573 8.511 1.345 8.511 1.073 c
+8.511 0.721 8.386 0.441 8.143 0.235 c
+7.908 0.03 7.592 -0.073 7.202 -0.073 c
+6.78 -0.073 6.441 0.044 6.188 0.279 c
+5.93 0.522 5.806 0.827 5.806 1.191 c
+6.453 1.191 l
+6.46 0.963 6.53 0.786 6.659 0.662 c
+6.784 0.545 6.967 0.485 7.202 0.485 c
+7.416 0.485 7.577 0.533 7.688 0.632 c
+7.805 0.728 7.864 0.857 7.864 1.015 c
+10.73 0.485 m
+10.944 0.485 11.116 0.548 11.245 0.676 c
+11.38 0.813 11.454 1.004 11.465 1.25 c
+12.082 1.25 l
+12.06 0.867 11.925 0.548 11.671 0.294 c
+11.414 0.048 11.102 -0.073 10.73 -0.073 c
+10.238 -0.073 9.863 0.077 9.598 0.383 c
+9.341 0.695 9.216 1.162 9.216 1.779 c
+9.216 2.22 l
+9.216 2.816 9.341 3.271 9.598 3.587 c
+9.863 3.899 10.238 4.057 10.73 4.057 c
+11.131 4.057 11.451 3.925 11.686 3.66 c
+11.928 3.404 12.06 3.057 12.082 2.617 c
+11.465 2.617 l
+11.443 2.911 11.37 3.132 11.245 3.279 c
+11.127 3.425 10.954 3.499 10.73 3.499 c
+10.436 3.499 10.219 3.4 10.084 3.205 c
+9.944 3.017 9.87 2.708 9.863 2.278 c
+9.863 1.764 l
+9.863 1.294 9.929 0.96 10.069 0.765 c
+10.215 0.578 10.436 0.485 10.73 0.485 c
+14.875 0 m
+14.835 0.088 14.809 0.235 14.802 0.441 c
+14.566 0.096 14.273 -0.073 13.92 -0.073 c
+13.556 -0.073 13.273 0.023 13.067 0.221 c
+12.868 0.427 12.773 0.713 12.773 1.088 c
+12.773 1.488 12.909 1.808 13.184 2.043 c
+13.456 2.286 13.832 2.411 14.302 2.411 c
+14.787 2.411 l
+14.787 2.837 l
+14.787 3.072 14.732 3.238 14.626 3.337 c
+14.515 3.444 14.354 3.499 14.14 3.499 c
+13.942 3.499 13.78 3.44 13.655 3.323 c
+13.537 3.205 13.479 3.057 13.479 2.882 c
+12.832 2.882 l
+12.832 3.076 12.891 3.267 13.009 3.454 c
+13.134 3.639 13.295 3.786 13.493 3.896 c
+13.699 4.002 13.927 4.057 14.184 4.057 c
+14.585 4.057 14.89 3.955 15.096 3.749 c
+15.309 3.543 15.422 3.249 15.434 2.866 c
+15.434 0.853 l
+15.434 0.548 15.47 0.283 15.551 0.059 c
+15.551 0 l
+h
+14.008 0.515 m
+14.173 0.515 14.324 0.559 14.464 0.647 c
+14.611 0.736 14.717 0.846 14.787 0.985 c
+14.787 1.926 l
+14.42 1.926 l
+14.104 1.926 13.861 1.856 13.685 1.721 c
+13.508 1.592 13.42 1.405 13.42 1.162 c
+13.42 0.934 13.464 0.769 13.552 0.662 c
+13.641 0.563 13.791 0.515 14.008 0.515 c
+18.08 3.367 m
+17.992 3.385 17.892 3.396 17.786 3.396 c
+17.452 3.396 17.216 3.212 17.08 2.852 c
+17.08 0 l
+16.434 0 l
+16.434 3.984 l
+17.065 3.984 l
+17.08 3.572 l
+17.256 3.896 17.499 4.057 17.815 4.057 c
+17.922 4.057 18.01 4.035 18.08 3.998 c
+h
+18.52 2.176 m
+18.52 2.782 18.63 3.249 18.859 3.572 c
+19.094 3.896 19.421 4.057 19.843 4.057 c
+20.226 4.057 20.523 3.899 20.74 3.587 c
+20.74 5.644 l
+21.387 5.644 l
+21.387 0 l
+20.799 0 l
+20.755 0.427 l
+20.549 0.092 20.244 -0.073 19.843 -0.073 c
+19.432 -0.073 19.108 0.081 18.873 0.397 c
+18.638 0.721 18.52 1.176 18.52 1.764 c
+h
+19.168 1.794 m
+19.168 1.353 19.23 1.022 19.359 0.809 c
+19.494 0.603 19.715 0.5 20.02 0.5 c
+20.343 0.5 20.582 0.662 20.74 0.985 c
+20.74 2.999 l
+20.571 3.311 20.332 3.469 20.02 3.469 c
+19.715 3.469 19.494 3.367 19.359 3.161 c
+19.23 2.955 19.168 2.631 19.168 2.191 c
+h
+25.487 0.485 m
+25.701 0.485 25.874 0.548 26.002 0.676 c
+26.138 0.813 26.212 1.004 26.223 1.25 c
+26.84 1.25 l
+26.819 0.867 26.682 0.548 26.428 0.294 c
+26.171 0.048 25.859 -0.073 25.487 -0.073 c
+24.995 -0.073 24.621 0.077 24.356 0.383 c
+24.099 0.695 23.974 1.162 23.974 1.779 c
+23.974 2.22 l
+23.974 2.816 24.099 3.271 24.356 3.587 c
+24.621 3.899 24.995 4.057 25.487 4.057 c
+25.888 4.057 26.208 3.925 26.443 3.66 c
+26.686 3.404 26.819 3.057 26.84 2.617 c
+26.223 2.617 l
+26.201 2.911 26.127 3.132 26.002 3.279 c
+25.884 3.425 25.712 3.499 25.487 3.499 c
+25.194 3.499 24.978 3.4 24.841 3.205 c
+24.702 3.017 24.628 2.708 24.621 2.278 c
+24.621 1.764 l
+24.621 1.294 24.687 0.96 24.826 0.765 c
+24.974 0.578 25.194 0.485 25.487 0.485 c
+28.236 3.572 m
+28.49 3.896 28.81 4.057 29.192 4.057 c
+29.898 4.057 30.254 3.587 30.265 2.646 c
+30.265 0 l
+29.618 0 l
+29.618 2.617 l
+29.618 2.929 29.564 3.15 29.456 3.279 c
+29.346 3.404 29.192 3.469 28.986 3.469 c
+28.828 3.469 28.681 3.414 28.545 3.308 c
+28.417 3.198 28.313 3.061 28.236 2.897 c
+28.236 0 l
+27.59 0 l
+27.59 5.644 l
+28.236 5.644 l
+h
+33.264 0 m
+33.223 0.088 33.198 0.235 33.19 0.441 c
+32.955 0.096 32.662 -0.073 32.308 -0.073 c
+31.945 -0.073 31.662 0.023 31.456 0.221 c
+31.257 0.427 31.162 0.713 31.162 1.088 c
+31.162 1.488 31.297 1.808 31.573 2.043 c
+31.845 2.286 32.22 2.411 32.691 2.411 c
+33.175 2.411 l
+33.175 2.837 l
+33.175 3.072 33.121 3.238 33.013 3.337 c
+32.903 3.444 32.741 3.499 32.529 3.499 c
+32.33 3.499 32.169 3.44 32.043 3.323 c
+31.926 3.205 31.868 3.057 31.868 2.882 c
+31.22 2.882 l
+31.22 3.076 31.28 3.267 31.397 3.454 c
+31.522 3.639 31.683 3.786 31.882 3.896 c
+32.088 4.002 32.315 4.057 32.573 4.057 c
+32.974 4.057 33.279 3.955 33.484 3.749 c
+33.697 3.543 33.811 3.249 33.822 2.866 c
+33.822 0.853 l
+33.822 0.548 33.859 0.283 33.94 0.059 c
+33.94 0 l
+h
+32.396 0.515 m
+32.562 0.515 32.712 0.559 32.852 0.647 c
+32.999 0.736 33.106 0.846 33.175 0.985 c
+33.175 1.926 l
+32.808 1.926 l
+32.492 1.926 32.249 1.856 32.073 1.721 c
+31.897 1.592 31.808 1.405 31.808 1.162 c
+31.808 0.934 31.852 0.769 31.941 0.662 c
+32.029 0.563 32.18 0.515 32.396 0.515 c
+35.44 3.984 m
+35.454 3.543 l
+35.708 3.884 36.031 4.057 36.424 4.057 c
+37.13 4.057 37.486 3.587 37.497 2.646 c
+37.497 0 l
+36.851 0 l
+36.851 2.617 l
+36.851 2.929 36.795 3.15 36.689 3.279 c
+36.579 3.404 36.424 3.469 36.218 3.469 c
+36.06 3.469 35.914 3.414 35.777 3.308 c
+35.648 3.198 35.546 3.061 35.469 2.897 c
+35.469 0 l
+34.821 0 l
+34.821 3.984 l
+h
+38.335 2.176 m
+38.335 2.793 38.445 3.256 38.673 3.572 c
+38.897 3.896 39.232 4.057 39.673 4.057 c
+40.073 4.057 40.378 3.881 40.583 3.528 c
+40.628 3.984 l
+41.216 3.984 l
+41.216 -0.043 l
+41.216 -0.532 41.088 -0.911 40.834 -1.176 c
+40.577 -1.44 40.224 -1.572 39.775 -1.572 c
+39.577 -1.572 39.357 -1.521 39.114 -1.425 c
+38.868 -1.326 38.688 -1.205 38.57 -1.058 c
+38.835 -0.617 l
+39.099 -0.881 39.397 -1.014 39.731 -1.014 c
+40.267 -1.014 40.543 -0.72 40.554 -0.132 c
+40.554 0.397 l
+40.348 0.081 40.047 -0.073 39.658 -0.073 c
+39.247 -0.073 38.923 0.077 38.688 0.383 c
+38.46 0.695 38.343 1.147 38.335 1.735 c
+h
+38.996 1.794 m
+38.996 1.353 39.059 1.022 39.187 0.809 c
+39.313 0.603 39.529 0.5 39.834 0.5 c
+40.157 0.5 40.396 0.665 40.554 1 c
+40.554 2.984 l
+40.385 3.308 40.147 3.469 39.834 3.469 c
+39.54 3.469 39.324 3.367 39.187 3.161 c
+39.059 2.955 38.996 2.631 38.996 2.191 c
+h
+43.597 -0.073 m
+43.097 -0.073 42.715 0.074 42.451 0.368 c
+42.186 0.662 42.054 1.095 42.054 1.676 c
+42.054 2.147 l
+42.054 2.741 42.179 3.209 42.436 3.543 c
+42.7 3.884 43.06 4.057 43.523 4.057 c
+43.983 4.057 44.325 3.903 44.552 3.602 c
+44.788 3.308 44.909 2.845 44.92 2.22 c
+44.92 1.794 l
+42.7 1.794 l
+42.7 1.706 l
+42.7 1.272 42.777 0.96 42.935 0.765 c
+43.101 0.578 43.332 0.485 43.627 0.485 c
+43.822 0.485 43.994 0.518 44.141 0.588 c
+44.288 0.665 44.424 0.783 44.552 0.941 c
+44.891 0.53 l
+44.604 0.125 44.174 -0.073 43.597 -0.073 c
+43.523 3.499 m
+43.248 3.499 43.046 3.404 42.921 3.219 c
+42.792 3.032 42.719 2.741 42.7 2.352 c
+44.273 2.352 l
+44.273 2.44 l
+44.251 2.822 44.186 3.091 44.067 3.249 c
+43.95 3.414 43.766 3.499 43.523 3.499 c
+47.639 1.015 m
+47.639 1.162 47.584 1.283 47.478 1.382 c
+47.367 1.478 47.162 1.596 46.86 1.735 c
+46.515 1.882 46.272 2.003 46.125 2.103 c
+45.979 2.209 45.869 2.326 45.802 2.455 c
+45.732 2.58 45.699 2.739 45.699 2.926 c
+45.699 3.249 45.817 3.517 46.052 3.734 c
+46.287 3.947 46.588 4.057 46.964 4.057 c
+47.346 4.057 47.654 3.944 47.889 3.72 c
+48.124 3.491 48.242 3.205 48.242 2.852 c
+47.595 2.852 l
+47.595 3.028 47.536 3.179 47.419 3.308 c
+47.301 3.433 47.147 3.499 46.964 3.499 c
+46.765 3.499 46.615 3.444 46.507 3.337 c
+46.397 3.238 46.346 3.105 46.346 2.94 c
+46.346 2.812 46.382 2.705 46.463 2.617 c
+46.54 2.536 46.732 2.433 47.037 2.309 c
+47.515 2.12 47.845 1.933 48.022 1.75 c
+48.198 1.573 48.286 1.345 48.286 1.073 c
+48.286 0.721 48.161 0.441 47.918 0.235 c
+47.683 0.03 47.367 -0.073 46.978 -0.073 c
+46.555 -0.073 46.218 0.044 45.964 0.279 c
+45.707 0.522 45.582 0.827 45.582 1.191 c
+46.228 1.191 l
+46.235 0.963 46.305 0.786 46.434 0.662 c
+46.559 0.545 46.743 0.485 46.978 0.485 c
+47.191 0.485 47.353 0.533 47.463 0.632 c
+47.581 0.728 47.639 0.857 47.639 1.015 c
+51.55 0 -0.647 3.984 re
+51.593 5.027 m
+51.593 4.917 51.564 4.825 51.505 4.748 c
+51.446 4.678 51.351 4.645 51.226 4.645 c
+51.108 4.645 51.013 4.678 50.947 4.748 c
+50.887 4.825 50.858 4.917 50.858 5.027 c
+50.858 5.145 50.887 5.237 50.947 5.307 c
+51.013 5.384 51.108 5.424 51.226 5.424 c
+51.351 5.424 51.446 5.384 51.505 5.307 c
+51.564 5.226 51.593 5.134 51.593 5.027 c
+53.181 3.984 m
+53.196 3.543 l
+53.449 3.884 53.773 4.057 54.166 4.057 c
+54.871 4.057 55.228 3.587 55.239 2.646 c
+55.239 0 l
+54.592 0 l
+54.592 2.617 l
+54.592 2.929 54.537 3.15 54.43 3.279 c
+54.32 3.404 54.166 3.469 53.96 3.469 c
+53.802 3.469 53.655 3.414 53.519 3.308 c
+53.391 3.198 53.287 3.061 53.21 2.897 c
+53.21 0 l
+52.564 0 l
+52.564 3.984 l
+h
+f
+Q
+q 1 0 0 1 372.1404 306.1949 cm
+0 0 m
+0.353 2.234 l
+1.352 2.234 l
+0.529 -1.75 l
+-0.339 -1.75 l
+-0.897 0.559 l
+-1.455 -1.75 l
+-2.323 -1.75 l
+-3.146 2.234 l
+-2.147 2.234 l
+-1.794 0 l
+-1.264 2.234 l
+-0.53 2.234 l
+h
+1.749 0.367 m
+1.749 0.974 1.888 1.448 2.175 1.793 c
+2.458 2.135 2.851 2.308 3.351 2.308 c
+3.858 2.308 4.255 2.135 4.542 1.793 c
+4.825 1.448 4.968 0.974 4.968 0.367 c
+4.968 0.103 l
+4.968 -0.497 4.825 -0.967 4.542 -1.309 c
+4.255 -1.654 3.858 -1.823 3.351 -1.823 c
+2.84 -1.823 2.443 -1.654 2.16 -1.309 c
+1.885 -0.967 1.749 -0.493 1.749 0.118 c
+h
+2.792 0.103 m
+2.792 -0.603 2.977 -0.956 3.351 -0.956 c
+3.704 -0.956 3.895 -0.661 3.925 -0.073 c
+3.925 0.367 l
+3.925 0.727 3.873 0.999 3.777 1.176 c
+3.678 1.352 3.534 1.44 3.351 1.44 c
+3.175 1.44 3.035 1.352 2.94 1.176 c
+2.84 0.999 2.792 0.727 2.792 0.367 c
+h
+7.569 1.22 m
+7.231 1.249 l
+6.945 1.249 6.754 1.124 6.659 0.881 c
+6.659 -1.75 l
+5.614 -1.75 l
+5.614 2.234 l
+6.585 2.234 l
+6.614 1.793 l
+6.78 2.135 7.011 2.308 7.305 2.308 c
+7.422 2.308 7.515 2.285 7.584 2.248 c
+h
+9.437 -0.221 m
+9.157 -0.53 l
+9.157 -1.75 l
+8.114 -1.75 l
+8.114 3.895 l
+9.157 3.895 l
+9.157 0.852 l
+9.275 1.043 l
+9.995 2.234 l
+11.245 2.234 l
+10.083 0.588 l
+11.347 -1.75 l
+10.156 -1.75 l
+h
+12.803 -1.75 -1.043 3.984 re
+11.715 3.263 m
+11.715 3.418 11.763 3.546 11.862 3.645 c
+11.968 3.752 12.104 3.807 12.273 3.807 c
+12.45 3.807 12.586 3.752 12.685 3.645 c
+12.791 3.546 12.847 3.418 12.847 3.263 c
+12.847 3.094 12.791 2.958 12.685 2.851 c
+12.586 2.753 12.45 2.705 12.273 2.705 c
+12.104 2.705 11.968 2.753 11.862 2.851 c
+11.763 2.958 11.715 3.094 11.715 3.263 c
+14.566 2.234 m
+14.596 1.837 l
+14.831 2.15 15.133 2.308 15.507 2.308 c
+16.191 2.308 16.544 1.826 16.565 0.867 c
+16.565 -1.75 l
+15.522 -1.75 l
+15.522 0.794 l
+15.522 1.018 15.486 1.18 15.419 1.278 c
+15.349 1.374 15.232 1.425 15.066 1.425 c
+14.879 1.425 14.732 1.33 14.625 1.147 c
+14.625 -1.75 l
+13.581 -1.75 l
+13.581 2.234 l
+h
+17.183 0.367 m
+17.183 1.014 17.3 1.5 17.535 1.822 c
+17.771 2.146 18.102 2.308 18.535 2.308 c
+18.888 2.308 19.16 2.165 19.359 1.882 c
+19.403 2.234 l
+20.343 2.234 l
+20.343 -1.75 l
+20.343 -2.257 20.2 -2.646 19.917 -2.911 c
+19.63 -3.182 19.226 -3.322 18.697 -3.322 c
+18.469 -3.322 18.233 -3.278 17.992 -3.19 c
+17.756 -3.102 17.58 -2.988 17.462 -2.851 c
+17.815 -2.132 l
+17.911 -2.238 18.039 -2.323 18.197 -2.381 c
+18.351 -2.448 18.499 -2.484 18.638 -2.484 c
+18.873 -2.484 19.039 -2.425 19.137 -2.308 c
+19.245 -2.198 19.299 -2.022 19.299 -1.779 c
+19.299 -1.426 l
+19.101 -1.69 18.844 -1.823 18.52 -1.823 c
+18.098 -1.823 17.771 -1.661 17.535 -1.338 c
+17.308 -1.008 17.19 -0.537 17.183 0.073 c
+h
+18.227 0.103 m
+18.227 -0.272 18.274 -0.54 18.374 -0.706 c
+18.469 -0.875 18.623 -0.956 18.829 -0.956 c
+19.042 -0.956 19.201 -0.879 19.299 -0.721 c
+19.299 1.176 l
+19.189 1.341 19.035 1.425 18.829 1.425 c
+18.623 1.425 18.469 1.341 18.374 1.176 c
+18.274 1.007 18.227 0.738 18.227 0.367 c
+h
+22.695 0.367 m
+22.695 1.014 22.802 1.5 23.018 1.822 c
+23.243 2.146 23.566 2.308 23.989 2.308 c
+24.301 2.308 24.554 2.175 24.753 1.911 c
+24.753 3.895 l
+25.811 3.895 l
+25.811 -1.75 l
+24.856 -1.75 l
+24.812 -1.338 l
+24.595 -1.661 24.319 -1.823 23.989 -1.823 c
+23.577 -1.823 23.257 -1.669 23.033 -1.353 c
+22.817 -1.029 22.702 -0.559 22.695 0.058 c
+h
+23.739 0.103 m
+23.739 -0.291 23.775 -0.566 23.856 -0.721 c
+23.945 -0.879 24.091 -0.956 24.297 -0.956 c
+24.503 -0.956 24.654 -0.864 24.753 -0.676 c
+24.753 1.132 l
+24.654 1.326 24.503 1.425 24.297 1.425 c
+24.099 1.425 23.959 1.344 23.871 1.19 c
+23.783 1.043 23.739 0.771 23.739 0.382 c
+h
+27.663 -1.75 -1.044 3.984 re
+26.576 3.263 m
+26.576 3.418 26.623 3.546 26.723 3.645 c
+26.829 3.752 26.965 3.807 27.134 3.807 c
+27.311 3.807 27.446 3.752 27.546 3.645 c
+27.652 3.546 27.708 3.418 27.708 3.263 c
+27.708 3.094 27.652 2.958 27.546 2.851 c
+27.446 2.753 27.311 2.705 27.134 2.705 c
+26.965 2.705 26.829 2.753 26.723 2.851 c
+26.623 2.958 26.576 3.094 26.576 3.263 c
+30.426 1.22 m
+30.089 1.249 l
+29.802 1.249 29.611 1.124 29.516 0.881 c
+29.516 -1.75 l
+28.471 -1.75 l
+28.471 2.234 l
+29.441 2.234 l
+29.471 1.793 l
+29.637 2.135 29.868 2.308 30.162 2.308 c
+30.28 2.308 30.372 2.285 30.441 2.248 c
+h
+32.499 -1.823 m
+31.97 -1.823 31.551 -1.669 31.249 -1.353 c
+30.956 -1.029 30.809 -0.57 30.809 0.029 c
+30.809 0.338 l
+30.809 0.962 30.945 1.448 31.22 1.793 c
+31.492 2.135 31.885 2.308 32.396 2.308 c
+32.896 2.308 33.267 2.146 33.514 1.822 c
+33.767 1.5 33.9 1.022 33.91 0.397 c
+33.91 -0.103 l
+31.837 -0.103 l
+31.856 -0.397 31.918 -0.614 32.028 -0.75 c
+32.147 -0.89 32.327 -0.956 32.573 -0.956 c
+32.915 -0.956 33.204 -0.838 33.44 -0.603 c
+33.852 -1.235 l
+33.723 -1.411 33.535 -1.555 33.293 -1.661 c
+33.047 -1.768 32.782 -1.823 32.499 -1.823 c
+31.853 0.617 m
+32.882 0.617 l
+32.882 0.72 l
+32.882 0.955 32.841 1.132 32.764 1.249 c
+32.694 1.374 32.566 1.44 32.382 1.44 c
+32.205 1.44 32.073 1.371 31.985 1.234 c
+31.904 1.106 31.86 0.9 31.853 0.617 c
+35.939 -0.956 m
+36.233 -0.956 36.383 -0.761 36.394 -0.368 c
+37.364 -0.368 l
+37.364 -0.802 37.233 -1.154 36.967 -1.426 c
+36.703 -1.69 36.365 -1.823 35.953 -1.823 c
+35.442 -1.823 35.049 -1.669 34.777 -1.353 c
+34.513 -1.029 34.374 -0.559 34.366 0.058 c
+34.366 0.382 l
+34.366 1.007 34.499 1.484 34.763 1.808 c
+35.035 2.138 35.432 2.308 35.953 2.308 c
+36.383 2.308 36.726 2.167 36.982 1.896 c
+37.236 1.62 37.364 1.238 37.364 0.75 c
+36.394 0.75 l
+36.394 0.962 36.354 1.132 36.277 1.249 c
+36.207 1.374 36.09 1.44 35.924 1.44 c
+35.748 1.44 35.619 1.374 35.542 1.249 c
+35.461 1.12 35.417 0.871 35.409 0.5 c
+35.409 0.088 l
+35.409 -0.235 35.424 -0.463 35.454 -0.588 c
+35.49 -0.717 35.546 -0.809 35.615 -0.867 c
+35.693 -0.927 35.799 -0.956 35.939 -0.956 c
+39.172 3.204 m
+39.172 2.234 l
+39.702 2.234 l
+39.702 1.44 l
+39.172 1.44 l
+39.172 -0.53 l
+39.172 -0.688 39.191 -0.794 39.232 -0.852 c
+39.28 -0.912 39.363 -0.941 39.481 -0.941 c
+39.588 -0.941 39.673 -0.933 39.731 -0.912 c
+39.731 -1.72 l
+39.554 -1.786 39.363 -1.823 39.158 -1.823 c
+38.482 -1.823 38.137 -1.437 38.129 -0.661 c
+38.129 1.44 l
+37.673 1.44 l
+37.673 2.234 l
+38.129 2.234 l
+38.129 3.204 l
+h
+40.055 0.367 m
+40.055 0.974 40.194 1.448 40.481 1.793 c
+40.764 2.135 41.157 2.308 41.657 2.308 c
+42.164 2.308 42.561 2.135 42.847 1.793 c
+43.13 1.448 43.274 0.974 43.274 0.367 c
+43.274 0.103 l
+43.274 -0.497 43.13 -0.967 42.847 -1.309 c
+42.561 -1.654 42.164 -1.823 41.657 -1.823 c
+41.146 -1.823 40.749 -1.654 40.466 -1.309 c
+40.19 -0.967 40.055 -0.493 40.055 0.118 c
+h
+41.098 0.103 m
+41.098 -0.603 41.282 -0.956 41.657 -0.956 c
+42.01 -0.956 42.201 -0.661 42.23 -0.073 c
+42.23 0.367 l
+42.23 0.727 42.179 0.999 42.083 1.176 c
+41.984 1.352 41.84 1.44 41.657 1.44 c
+41.48 1.44 41.341 1.352 41.245 1.176 c
+41.146 0.999 41.098 0.727 41.098 0.367 c
+h
+45.875 1.22 m
+45.537 1.249 l
+45.251 1.249 45.06 1.124 44.964 0.881 c
+44.964 -1.75 l
+43.92 -1.75 l
+43.92 2.234 l
+44.89 2.234 l
+44.92 1.793 l
+45.085 2.135 45.317 2.308 45.611 2.308 c
+45.728 2.308 45.821 2.285 45.89 2.248 c
+h
+47.772 -0.015 m
+48.3 2.234 l
+49.403 2.234 l
+48.095 -2.352 l
+47.897 -3.017 47.529 -3.352 46.993 -3.352 c
+46.864 -3.352 46.721 -3.329 46.567 -3.293 c
+46.567 -2.469 l
+46.684 -2.484 l
+46.849 -2.484 46.97 -2.448 47.051 -2.381 c
+47.128 -2.323 47.191 -2.213 47.242 -2.058 c
+47.315 -1.793 l
+46.17 2.234 l
+47.286 2.234 l
+h
+f
+Q
+q 1 0 0 1 422.1464 304.7979 cm
+0 0 m
+0 0.118 0.033 0.214 0.103 0.294 c
+0.169 0.372 0.272 0.412 0.411 0.412 c
+0.559 0.412 0.665 0.372 0.735 0.294 c
+0.812 0.214 0.852 0.118 0.852 0 c
+0.852 -0.11 0.812 -0.201 0.735 -0.278 c
+0.665 -0.357 0.559 -0.396 0.411 -0.396 c
+0.272 -0.396 0.169 -0.357 0.103 -0.278 c
+0.033 -0.201 0 -0.11 0 0 c
+6.732 4.425 m
+5.321 4.425 l
+5.321 -0.353 l
+4.659 -0.353 l
+4.659 4.425 l
+3.248 4.425 l
+3.248 4.998 l
+6.732 4.998 l
+h
+8.085 3.219 m
+8.338 3.543 8.658 3.705 9.04 3.705 c
+9.745 3.705 10.102 3.234 10.113 2.294 c
+10.113 -0.353 l
+9.466 -0.353 l
+9.466 2.264 l
+9.466 2.577 9.411 2.797 9.304 2.926 c
+9.194 3.051 9.04 3.117 8.834 3.117 c
+8.676 3.117 8.529 3.061 8.393 2.955 c
+8.264 2.845 8.162 2.708 8.085 2.544 c
+8.085 -0.353 l
+7.437 -0.353 l
+7.437 5.292 l
+8.085 5.292 l
+h
+11.774 -0.353 -0.647 3.984 re
+11.818 4.675 m
+11.818 4.564 11.788 4.472 11.73 4.395 c
+11.671 4.326 11.576 4.293 11.451 4.293 c
+11.333 4.293 11.237 4.326 11.171 4.395 c
+11.112 4.472 11.083 4.564 11.083 4.675 c
+11.083 4.792 11.112 4.884 11.171 4.954 c
+11.237 5.031 11.333 5.072 11.451 5.072 c
+11.576 5.072 11.671 5.031 11.73 4.954 c
+11.788 4.873 11.818 4.781 11.818 4.675 c
+14.728 0.662 m
+14.728 0.809 14.674 0.93 14.566 1.029 c
+14.456 1.125 14.25 1.243 13.949 1.382 c
+13.604 1.529 13.361 1.65 13.215 1.75 c
+13.067 1.856 12.957 1.974 12.891 2.103 c
+12.821 2.228 12.788 2.386 12.788 2.573 c
+12.788 2.897 12.906 3.165 13.141 3.381 c
+13.376 3.595 13.677 3.705 14.052 3.705 c
+14.435 3.705 14.743 3.591 14.978 3.367 c
+15.214 3.138 15.331 2.852 15.331 2.5 c
+14.684 2.5 l
+14.684 2.675 14.626 2.826 14.508 2.955 c
+14.39 3.08 14.236 3.146 14.052 3.146 c
+13.853 3.146 13.703 3.091 13.596 2.984 c
+13.486 2.885 13.435 2.753 13.435 2.587 c
+13.435 2.459 13.471 2.352 13.552 2.264 c
+13.629 2.183 13.82 2.08 14.126 1.956 c
+14.603 1.768 14.934 1.58 15.11 1.397 c
+15.287 1.22 15.375 0.992 15.375 0.721 c
+15.375 0.368 15.25 0.088 15.008 -0.118 c
+14.772 -0.323 14.456 -0.426 14.067 -0.426 c
+13.644 -0.426 13.306 -0.309 13.053 -0.073 c
+12.795 0.169 12.671 0.474 12.671 0.838 c
+13.317 0.838 l
+13.325 0.611 13.394 0.434 13.523 0.31 c
+13.648 0.192 13.832 0.133 14.067 0.133 c
+14.28 0.133 14.441 0.181 14.552 0.279 c
+14.67 0.375 14.728 0.504 14.728 0.662 c
+17.801 1.823 m
+17.801 2.4 17.936 2.856 18.212 3.19 c
+18.495 3.532 18.867 3.705 19.329 3.705 c
+19.788 3.705 20.156 3.535 20.432 3.205 c
+20.714 2.882 20.862 2.433 20.872 1.867 c
+20.872 1.441 l
+20.872 0.871 20.729 0.416 20.446 0.074 c
+20.17 -0.261 19.803 -0.426 19.343 -0.426 c
+18.881 -0.426 18.51 -0.264 18.227 0.059 c
+17.951 0.389 17.808 0.831 17.801 1.382 c
+h
+18.447 1.441 m
+18.447 1.037 18.524 0.721 18.682 0.485 c
+18.848 0.25 19.068 0.133 19.343 0.133 c
+19.91 0.133 20.203 0.545 20.226 1.368 c
+20.226 1.823 l
+20.226 2.224 20.141 2.544 19.976 2.779 c
+19.818 3.021 19.601 3.146 19.329 3.146 c
+19.065 3.146 18.848 3.021 18.682 2.779 c
+18.524 2.544 18.447 2.224 18.447 1.823 c
+h
+24.591 1.441 m
+24.591 0.813 24.474 0.342 24.239 0.03 c
+24.01 -0.276 23.694 -0.426 23.283 -0.426 c
+22.879 -0.426 22.57 -0.276 22.358 0.03 c
+22.358 -1.881 l
+21.71 -1.881 l
+21.71 3.631 l
+22.298 3.631 l
+22.343 3.19 l
+22.555 3.532 22.865 3.705 23.268 3.705 c
+23.709 3.705 24.037 3.55 24.253 3.249 c
+24.467 2.944 24.581 2.488 24.591 1.881 c
+h
+23.945 1.823 m
+23.945 2.264 23.875 2.587 23.739 2.793 c
+23.599 3.007 23.378 3.117 23.077 3.117 c
+22.761 3.117 22.522 2.962 22.358 2.661 c
+22.358 0.588 l
+22.522 0.283 22.761 0.133 23.077 0.133 c
+23.372 0.133 23.584 0.235 23.724 0.441 c
+23.86 0.655 23.933 0.985 23.945 1.426 c
+h
+26.855 -0.426 m
+26.356 -0.426 25.973 -0.278 25.709 0.015 c
+25.444 0.31 25.312 0.742 25.312 1.324 c
+25.312 1.794 l
+25.312 2.389 25.437 2.856 25.693 3.19 c
+25.959 3.532 26.318 3.705 26.782 3.705 c
+27.241 3.705 27.582 3.55 27.81 3.249 c
+28.045 2.955 28.167 2.492 28.178 1.867 c
+28.178 1.441 l
+25.959 1.441 l
+25.959 1.353 l
+25.959 0.919 26.036 0.607 26.194 0.412 c
+26.359 0.225 26.591 0.133 26.884 0.133 c
+27.079 0.133 27.252 0.166 27.399 0.235 c
+27.546 0.312 27.682 0.43 27.81 0.588 c
+28.149 0.177 l
+27.862 -0.228 27.432 -0.426 26.855 -0.426 c
+26.782 3.146 m
+26.506 3.146 26.304 3.051 26.179 2.866 c
+26.05 2.679 25.977 2.389 25.959 1.999 c
+27.532 1.999 l
+27.532 2.088 l
+27.509 2.469 27.443 2.738 27.326 2.897 c
+27.208 3.061 27.024 3.146 26.782 3.146 c
+30.603 3.014 m
+30.515 3.032 30.416 3.043 30.31 3.043 c
+29.975 3.043 29.74 2.859 29.604 2.5 c
+29.604 -0.353 l
+28.957 -0.353 l
+28.957 3.631 l
+29.589 3.631 l
+29.604 3.219 l
+29.78 3.543 30.023 3.705 30.339 3.705 c
+30.445 3.705 30.534 3.682 30.603 3.645 c
+h
+33.146 -0.353 m
+33.106 -0.264 33.08 -0.118 33.073 0.088 c
+32.837 -0.257 32.544 -0.426 32.191 -0.426 c
+31.827 -0.426 31.544 -0.33 31.338 -0.132 c
+31.139 0.074 31.044 0.36 31.044 0.736 c
+31.044 1.135 31.18 1.455 31.456 1.691 c
+31.727 1.933 32.103 2.058 32.573 2.058 c
+33.058 2.058 l
+33.058 2.485 l
+33.058 2.72 33.003 2.885 32.897 2.984 c
+32.786 3.091 32.625 3.146 32.411 3.146 c
+32.213 3.146 32.051 3.088 31.926 2.97 c
+31.808 2.852 31.75 2.705 31.75 2.529 c
+31.103 2.529 l
+31.103 2.723 31.162 2.914 31.28 3.102 c
+31.405 3.286 31.566 3.433 31.765 3.543 c
+31.97 3.649 32.198 3.705 32.456 3.705 c
+32.856 3.705 33.161 3.602 33.367 3.396 c
+33.58 3.19 33.694 2.897 33.705 2.514 c
+33.705 0.5 l
+33.705 0.195 33.741 -0.07 33.822 -0.293 c
+33.822 -0.353 l
+h
+32.279 0.162 m
+32.444 0.162 32.595 0.206 32.735 0.294 c
+32.882 0.383 32.988 0.493 33.058 0.632 c
+33.058 1.573 l
+32.691 1.573 l
+32.375 1.573 32.132 1.503 31.956 1.368 c
+31.779 1.239 31.691 1.052 31.691 0.809 c
+31.691 0.581 31.735 0.416 31.823 0.31 c
+31.912 0.21 32.062 0.162 32.279 0.162 c
+35.557 4.586 m
+35.557 3.631 l
+36.159 3.631 l
+36.159 3.102 l
+35.557 3.102 l
+35.557 0.632 l
+35.557 0.474 35.579 0.357 35.63 0.279 c
+35.689 0.198 35.777 0.162 35.895 0.162 c
+35.983 0.162 36.072 0.177 36.159 0.206 c
+36.159 -0.353 l
+36.012 -0.4 35.858 -0.426 35.704 -0.426 c
+35.447 -0.426 35.252 -0.334 35.116 -0.147 c
+34.977 0.037 34.91 0.298 34.91 0.632 c
+34.91 3.102 l
+34.308 3.102 l
+34.308 3.631 l
+34.91 3.631 l
+34.91 4.586 l
+h
+37.63 -0.353 -0.647 3.984 re
+37.674 4.675 m
+37.674 4.564 37.645 4.472 37.585 4.395 c
+37.526 4.326 37.431 4.293 37.306 4.293 c
+37.188 4.293 37.093 4.326 37.027 4.395 c
+36.968 4.472 36.939 4.564 36.939 4.675 c
+36.939 4.792 36.968 4.884 37.027 4.954 c
+37.093 5.031 37.188 5.072 37.306 5.072 c
+37.431 5.072 37.526 5.031 37.585 4.954 c
+37.645 4.873 37.674 4.781 37.674 4.675 c
+38.511 1.823 m
+38.511 2.4 38.647 2.856 38.923 3.19 c
+39.206 3.532 39.577 3.705 40.04 3.705 c
+40.5 3.705 40.867 3.535 41.142 3.205 c
+41.425 2.882 41.572 2.433 41.583 1.867 c
+41.583 1.441 l
+41.583 0.871 41.44 0.416 41.157 0.074 c
+40.882 -0.261 40.514 -0.426 40.055 -0.426 c
+39.592 -0.426 39.22 -0.264 38.937 0.059 c
+38.663 0.389 38.519 0.831 38.511 1.382 c
+h
+39.158 1.441 m
+39.158 1.037 39.235 0.721 39.393 0.485 c
+39.559 0.25 39.779 0.133 40.055 0.133 c
+40.621 0.133 40.915 0.545 40.937 1.368 c
+40.937 1.823 l
+40.937 2.224 40.853 2.544 40.687 2.779 c
+40.529 3.021 40.312 3.146 40.04 3.146 c
+39.775 3.146 39.559 3.021 39.393 2.779 c
+39.235 2.544 39.158 2.224 39.158 1.823 c
+h
+43.039 3.631 m
+43.054 3.19 l
+43.307 3.532 43.631 3.705 44.024 3.705 c
+44.729 3.705 45.086 3.234 45.096 2.294 c
+45.096 -0.353 l
+44.45 -0.353 l
+44.45 2.264 l
+44.45 2.577 44.394 2.797 44.288 2.926 c
+44.178 3.051 44.024 3.117 43.818 3.117 c
+43.66 3.117 43.513 3.061 43.377 2.955 c
+43.249 2.845 43.145 2.708 43.068 2.544 c
+43.068 -0.353 l
+42.422 -0.353 l
+42.422 3.631 l
+h
+f
+Q
+470.639 304.445 -0.647 3.984 re
+470.683 309.472 m
+470.683 309.362 470.653 309.27 470.594 309.193 c
+470.535 309.123 470.44 309.09 470.315 309.09 c
+470.197 309.09 470.102 309.123 470.036 309.193 c
+469.977 309.27 469.947 309.362 469.947 309.472 c
+469.947 309.59 469.977 309.682 470.036 309.752 c
+470.102 309.829 470.197 309.869 470.315 309.869 c
+470.44 309.869 470.535 309.829 470.594 309.752 c
+470.653 309.671 470.683 309.579 470.683 309.472 c
+473.593 305.46 m
+473.593 305.607 473.538 305.728 473.432 305.827 c
+473.321 305.923 473.116 306.041 472.814 306.18 c
+472.468 306.327 472.226 306.448 472.079 306.548 c
+471.932 306.654 471.822 306.772 471.755 306.9 c
+471.686 307.026 471.653 307.184 471.653 307.371 c
+471.653 307.694 471.77 307.963 472.006 308.179 c
+472.241 308.392 472.542 308.503 472.917 308.503 c
+473.299 308.503 473.608 308.389 473.843 308.165 c
+474.078 307.936 474.196 307.65 474.196 307.297 c
+473.548 307.297 l
+473.548 307.473 473.49 307.624 473.372 307.753 c
+473.255 307.878 473.101 307.944 472.917 307.944 c
+472.719 307.944 472.568 307.889 472.461 307.782 c
+472.351 307.683 472.299 307.55 472.299 307.385 c
+472.299 307.257 472.336 307.15 472.417 307.062 c
+472.494 306.981 472.686 306.878 472.991 306.754 c
+473.468 306.565 473.799 306.378 473.975 306.195 c
+474.151 306.018 474.24 305.79 474.24 305.518 c
+474.24 305.166 474.115 304.886 473.872 304.68 c
+473.637 304.475 473.321 304.372 472.931 304.372 c
+472.509 304.372 472.171 304.489 471.917 304.724 c
+471.66 304.967 471.535 305.272 471.535 305.636 c
+472.182 305.636 l
+472.189 305.408 472.259 305.232 472.388 305.107 c
+472.513 304.99 472.696 304.931 472.931 304.931 c
+473.145 304.931 473.307 304.978 473.417 305.077 c
+473.534 305.173 473.593 305.302 473.593 305.46 c
+f
+q 1 0 0 1 478.6491 304.8129 cm
+0 0 m
+-0.216 -0.294 -0.507 -0.441 -0.867 -0.441 c
+-1.23 -0.441 -1.51 -0.32 -1.705 -0.073 c
+-1.892 0.18 -1.984 0.548 -1.984 1.029 c
+-1.984 3.616 l
+-0.941 3.616 l
+-0.941 1.014 l
+-0.941 0.621 -0.816 0.426 -0.558 0.426 c
+-0.323 0.426 -0.154 0.53 -0.043 0.735 c
+-0.043 3.616 l
+1 3.616 l
+1 -0.368 l
+0.03 -0.368 l
+h
+2.705 3.616 m
+2.735 3.219 l
+2.97 3.532 3.271 3.69 3.645 3.69 c
+4.329 3.69 4.682 3.208 4.704 2.249 c
+4.704 -0.368 l
+3.66 -0.368 l
+3.66 2.176 l
+3.66 2.4 3.624 2.562 3.558 2.66 c
+3.487 2.756 3.37 2.807 3.205 2.807 c
+3.017 2.807 2.87 2.712 2.764 2.529 c
+2.764 -0.368 l
+1.72 -0.368 l
+1.72 3.616 l
+h
+7.408 2.602 m
+7.071 2.631 l
+6.784 2.631 6.593 2.506 6.498 2.263 c
+6.498 -0.368 l
+5.453 -0.368 l
+5.453 3.616 l
+6.423 3.616 l
+6.453 3.175 l
+6.619 3.516 6.85 3.69 7.144 3.69 c
+7.262 3.69 7.354 3.667 7.423 3.63 c
+h
+9.481 -0.441 m
+8.952 -0.441 8.533 -0.287 8.231 0.029 c
+7.938 0.353 7.791 0.812 7.791 1.411 c
+7.791 1.72 l
+7.791 2.344 7.927 2.83 8.202 3.175 c
+8.474 3.516 8.867 3.69 9.378 3.69 c
+9.878 3.69 10.249 3.528 10.496 3.204 c
+10.749 2.881 10.882 2.404 10.892 1.779 c
+10.892 1.278 l
+8.819 1.278 l
+8.838 0.985 8.9 0.768 9.01 0.632 c
+9.129 0.492 9.309 0.426 9.555 0.426 c
+9.897 0.426 10.186 0.544 10.422 0.779 c
+10.834 0.147 l
+10.705 -0.029 10.517 -0.173 10.275 -0.279 c
+10.029 -0.386 9.764 -0.441 9.481 -0.441 c
+8.834 1.999 m
+9.864 1.999 l
+9.864 2.102 l
+9.864 2.337 9.823 2.514 9.746 2.631 c
+9.676 2.756 9.548 2.822 9.364 2.822 c
+9.187 2.822 9.055 2.753 8.967 2.616 c
+8.886 2.488 8.842 2.282 8.834 1.999 c
+12.921 0.426 m
+13.215 0.426 13.365 0.621 13.376 1.014 c
+14.346 1.014 l
+14.346 0.58 14.215 0.228 13.949 -0.044 c
+13.685 -0.309 13.347 -0.441 12.935 -0.441 c
+12.424 -0.441 12.031 -0.287 11.759 0.029 c
+11.495 0.353 11.356 0.823 11.348 1.44 c
+11.348 1.764 l
+11.348 2.389 11.481 2.866 11.745 3.19 c
+12.017 3.52 12.414 3.69 12.935 3.69 c
+13.365 3.69 13.708 3.549 13.964 3.278 c
+14.218 3.002 14.346 2.62 14.346 2.132 c
+13.376 2.132 l
+13.376 2.344 13.336 2.514 13.259 2.631 c
+13.189 2.756 13.072 2.822 12.906 2.822 c
+12.73 2.822 12.601 2.756 12.524 2.631 c
+12.443 2.502 12.399 2.253 12.391 1.881 c
+12.391 1.47 l
+12.391 1.147 12.406 0.919 12.436 0.794 c
+12.472 0.665 12.528 0.573 12.597 0.515 c
+12.675 0.455 12.781 0.426 12.921 0.426 c
+14.832 1.749 m
+14.832 2.356 14.971 2.83 15.258 3.175 c
+15.541 3.516 15.934 3.69 16.434 3.69 c
+16.941 3.69 17.338 3.516 17.625 3.175 c
+17.908 2.83 18.051 2.356 18.051 1.749 c
+18.051 1.484 l
+18.051 0.885 17.908 0.415 17.625 0.073 c
+17.338 -0.272 16.941 -0.441 16.434 -0.441 c
+15.923 -0.441 15.526 -0.272 15.244 0.073 c
+14.968 0.415 14.832 0.889 14.832 1.5 c
+h
+15.875 1.484 m
+15.875 0.779 16.059 0.426 16.434 0.426 c
+16.787 0.426 16.978 0.721 17.007 1.309 c
+17.007 1.749 l
+17.007 2.109 16.956 2.381 16.86 2.558 c
+16.761 2.734 16.617 2.822 16.434 2.822 c
+16.258 2.822 16.118 2.734 16.023 2.558 c
+15.923 2.381 15.875 2.109 15.875 1.749 c
+h
+19.947 1.103 m
+20.447 3.616 l
+21.534 3.616 l
+20.432 -0.368 l
+19.447 -0.368 l
+18.344 3.616 l
+19.433 3.616 l
+h
+23.578 -0.441 m
+23.048 -0.441 22.629 -0.287 22.328 0.029 c
+22.034 0.353 21.887 0.812 21.887 1.411 c
+21.887 1.72 l
+21.887 2.344 22.024 2.83 22.299 3.175 c
+22.571 3.516 22.964 3.69 23.475 3.69 c
+23.975 3.69 24.345 3.528 24.592 3.204 c
+24.846 2.881 24.978 2.404 24.989 1.779 c
+24.989 1.278 l
+22.916 1.278 l
+22.934 0.985 22.997 0.768 23.107 0.632 c
+23.225 0.492 23.405 0.426 23.651 0.426 c
+23.993 0.426 24.283 0.544 24.518 0.779 c
+24.93 0.147 l
+24.802 -0.029 24.614 -0.173 24.372 -0.279 c
+24.125 -0.386 23.861 -0.441 23.578 -0.441 c
+22.931 1.999 m
+23.96 1.999 l
+23.96 2.102 l
+23.96 2.337 23.919 2.514 23.842 2.631 c
+23.773 2.756 23.644 2.822 23.46 2.822 c
+23.283 2.822 23.152 2.753 23.063 2.616 c
+22.982 2.488 22.938 2.282 22.931 1.999 c
+27.532 2.602 m
+27.194 2.631 l
+26.907 2.631 26.716 2.506 26.62 2.263 c
+26.62 -0.368 l
+25.577 -0.368 l
+25.577 3.616 l
+26.547 3.616 l
+26.576 3.175 l
+26.741 3.516 26.973 3.69 27.267 3.69 c
+27.385 3.69 27.476 3.667 27.547 3.63 c
+h
+29.854 -0.368 m
+29.825 -0.309 29.795 -0.206 29.766 -0.058 c
+29.579 -0.316 29.328 -0.441 29.016 -0.441 c
+28.682 -0.441 28.403 -0.334 28.179 -0.118 c
+27.962 0.106 27.855 0.397 27.855 0.75 c
+27.855 1.161 27.987 1.477 28.252 1.706 c
+28.517 1.941 28.899 2.058 29.398 2.058 c
+29.722 2.058 l
+29.722 2.381 l
+29.722 2.558 29.685 2.679 29.619 2.749 c
+29.56 2.826 29.471 2.866 29.355 2.866 c
+29.097 2.866 28.972 2.712 28.972 2.411 c
+27.929 2.411 l
+27.929 2.782 28.064 3.087 28.34 3.322 c
+28.612 3.564 28.962 3.69 29.384 3.69 c
+29.825 3.69 30.163 3.572 30.398 3.337 c
+30.641 3.109 30.766 2.786 30.766 2.367 c
+30.766 0.5 l
+30.766 0.154 30.813 -0.114 30.913 -0.309 c
+30.913 -0.368 l
+h
+29.251 0.382 m
+29.358 0.382 29.45 0.401 29.531 0.441 c
+29.619 0.489 29.681 0.548 29.722 0.617 c
+29.722 1.44 l
+29.471 1.44 l
+29.296 1.44 29.153 1.382 29.045 1.264 c
+28.947 1.154 28.899 1.007 28.899 0.823 c
+28.899 0.53 29.016 0.382 29.251 0.382 c
+34.616 1.5 m
+34.616 0.852 34.514 0.368 34.308 0.044 c
+34.102 -0.279 33.779 -0.441 33.338 -0.441 c
+32.993 -0.441 32.713 -0.287 32.5 0.029 c
+32.471 -0.368 l
+31.515 -0.368 l
+31.515 5.277 l
+32.559 5.277 l
+32.559 3.293 l
+32.754 3.557 33.007 3.69 33.323 3.69 c
+33.764 3.69 34.088 3.528 34.294 3.204 c
+34.499 2.881 34.606 2.411 34.616 1.793 c
+h
+33.573 1.749 m
+33.573 2.168 33.533 2.448 33.456 2.587 c
+33.375 2.734 33.234 2.807 33.029 2.807 c
+32.812 2.807 32.654 2.708 32.559 2.514 c
+32.559 0.706 l
+32.647 0.518 32.808 0.426 33.043 0.426 c
+33.238 0.426 33.375 0.489 33.456 0.617 c
+33.533 0.742 33.573 1 33.573 1.382 c
+h
+36.336 -0.368 -1.043 5.644 re
+38.747 -0.441 m
+38.218 -0.441 37.799 -0.287 37.498 0.029 c
+37.203 0.353 37.057 0.812 37.057 1.411 c
+37.057 1.72 l
+37.057 2.344 37.193 2.83 37.468 3.175 c
+37.741 3.516 38.134 3.69 38.644 3.69 c
+39.144 3.69 39.515 3.528 39.761 3.204 c
+40.015 2.881 40.147 2.404 40.158 1.779 c
+40.158 1.278 l
+38.086 1.278 l
+38.104 0.985 38.167 0.768 38.277 0.632 c
+38.394 0.492 38.574 0.426 38.821 0.426 c
+39.162 0.426 39.453 0.544 39.688 0.779 c
+40.099 0.147 l
+39.971 -0.029 39.783 -0.173 39.541 -0.279 c
+39.295 -0.386 39.03 -0.441 38.747 -0.441 c
+38.1 1.999 m
+39.129 1.999 l
+39.129 2.102 l
+39.129 2.337 39.089 2.514 39.012 2.631 c
+38.942 2.756 38.813 2.822 38.63 2.822 c
+38.453 2.822 38.321 2.753 38.233 2.616 c
+38.152 2.488 38.107 2.282 38.1 1.999 c
+f
+Q
+q 1 0 0 1 519.5421 304.7979 cm
+0 0 m
+0 0.118 0.033 0.214 0.103 0.294 c
+0.169 0.372 0.272 0.412 0.412 0.412 c
+0.559 0.412 0.665 0.372 0.735 0.294 c
+0.812 0.214 0.852 0.118 0.852 0 c
+0.852 -0.11 0.812 -0.201 0.735 -0.278 c
+0.665 -0.357 0.559 -0.396 0.412 -0.396 c
+0.272 -0.396 0.169 -0.357 0.103 -0.278 c
+0.033 -0.201 0 -0.11 0 0 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 298.837 234.667 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 292.0029 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.683 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.941 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.028 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.244 1.933 5.376 2.051 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.635 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.177 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.116 l
+14.494 3.116 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.456 14.552 -0.515 c
+14.589 -0.566 14.645 -0.611 14.714 -0.647 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.688 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.603 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.279 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.085 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.456 c
+13.586 -0.36 13.582 -0.264 13.582 -0.177 c
+h
+22.074 -1.323 m
+21.905 -1.323 21.755 -1.301 21.619 -1.264 c
+21.49 -1.216 21.376 -1.147 21.28 -1.058 c
+21.193 -0.97 21.122 -0.864 21.075 -0.735 c
+21.024 -0.599 21.001 -0.449 21.001 -0.279 c
+21.001 -0.073 21.035 0.095 21.105 0.235 c
+21.17 0.382 21.266 0.492 21.384 0.573 c
+21.509 0.661 21.652 0.723 21.81 0.764 c
+21.976 0.801 22.152 0.827 22.339 0.837 c
+23.059 0.852 l
+23.059 1.029 l
+23.059 1.147 23.048 1.249 23.03 1.338 c
+23.008 1.425 22.975 1.492 22.927 1.543 c
+22.886 1.602 22.839 1.639 22.78 1.66 c
+22.722 1.679 22.655 1.691 22.589 1.691 c
+22.519 1.691 22.456 1.679 22.398 1.66 c
+22.346 1.65 22.298 1.624 22.251 1.587 c
+22.211 1.558 22.177 1.506 22.148 1.44 c
+22.126 1.382 22.111 1.301 22.104 1.205 c
+21.163 1.249 l
+21.193 1.396 21.236 1.532 21.296 1.66 c
+21.361 1.786 21.457 1.896 21.575 1.984 c
+21.693 2.08 21.832 2.153 22.001 2.205 c
+22.177 2.252 22.383 2.278 22.618 2.278 c
+23.059 2.278 23.391 2.168 23.618 1.955 c
+23.853 1.749 23.971 1.44 23.971 1.029 c
+23.971 -0.235 l
+23.971 -0.456 l
+23.979 -0.515 23.993 -0.57 24.015 -0.617 c
+24.033 -0.658 24.062 -0.691 24.103 -0.721 c
+24.139 -0.742 24.191 -0.75 24.25 -0.75 c
+24.316 -0.75 24.386 -0.746 24.455 -0.735 c
+24.455 -1.22 l
+24.397 -1.231 24.342 -1.242 24.294 -1.249 c
+24.254 -1.261 24.214 -1.268 24.177 -1.279 c
+24.137 -1.286 24.092 -1.294 24.044 -1.294 c
+23.993 -1.301 23.934 -1.309 23.868 -1.309 c
+23.64 -1.309 23.474 -1.257 23.368 -1.147 c
+23.258 -1.029 23.196 -0.864 23.177 -0.647 c
+23.162 -0.647 l
+23.092 -0.757 23.023 -0.852 22.957 -0.941 c
+22.886 -1.022 22.809 -1.087 22.722 -1.147 c
+22.633 -1.205 22.534 -1.249 22.427 -1.279 c
+22.328 -1.309 22.211 -1.323 22.074 -1.323 c
+23.059 0.353 m
+22.633 0.338 l
+22.534 0.338 22.442 0.33 22.354 0.324 c
+22.273 0.312 22.207 0.287 22.148 0.249 c
+22.09 0.209 22.038 0.151 22.001 0.073 c
+21.961 0.004 21.942 -0.088 21.942 -0.206 c
+21.942 -0.375 21.976 -0.497 22.045 -0.574 c
+22.111 -0.654 22.211 -0.691 22.339 -0.691 c
+22.446 -0.691 22.545 -0.669 22.633 -0.617 c
+22.728 -0.57 22.809 -0.507 22.868 -0.426 c
+22.934 -0.349 22.986 -0.262 23.015 -0.162 c
+23.044 -0.056 23.059 0.058 23.059 0.176 c
+h
+27.355 -1.264 m
+27.344 -1.246 27.333 -1.216 27.326 -1.176 c
+27.326 -1.128 27.318 -1.081 27.312 -1.029 c
+27.312 -0.97 27.304 -0.912 27.297 -0.852 c
+27.297 -0.691 l
+27.179 -0.927 27.036 -1.095 26.87 -1.191 c
+26.701 -1.279 26.503 -1.323 26.267 -1.323 c
+26.069 -1.323 25.893 -1.279 25.739 -1.191 c
+25.581 -1.103 25.448 -0.981 25.342 -0.823 c
+25.242 -0.658 25.165 -0.467 25.106 -0.25 c
+25.055 -0.037 25.033 0.206 25.033 0.47 c
+25.033 0.735 25.055 0.974 25.106 1.19 c
+25.165 1.415 25.242 1.606 25.342 1.764 c
+25.448 1.918 25.581 2.043 25.739 2.131 c
+25.903 2.227 26.095 2.278 26.312 2.278 c
+26.408 2.278 26.503 2.263 26.591 2.234 c
+26.686 2.212 26.782 2.179 26.87 2.131 c
+26.959 2.08 27.036 2.017 27.106 1.94 c
+27.183 1.859 27.245 1.768 27.297 1.66 c
+27.297 1.749 l
+27.297 1.896 l
+27.297 2.057 l
+27.297 2.234 l
+27.297 3.513 l
+28.193 3.513 l
+28.193 -0.5 l
+28.193 -0.676 28.197 -0.834 28.208 -0.97 c
+28.215 -1.099 28.222 -1.199 28.222 -1.264 c
+h
+27.312 0.484 m
+27.312 0.72 27.285 0.912 27.237 1.058 c
+27.197 1.213 27.142 1.338 27.076 1.425 c
+27.017 1.514 26.947 1.573 26.87 1.602 c
+26.789 1.639 26.712 1.66 26.635 1.66 c
+26.535 1.66 26.444 1.635 26.356 1.587 c
+26.275 1.547 26.209 1.477 26.15 1.382 c
+26.098 1.282 26.055 1.161 26.018 1.014 c
+25.988 0.867 25.974 0.683 25.974 0.47 c
+25.974 0.077 26.025 -0.217 26.136 -0.412 c
+26.253 -0.611 26.414 -0.706 26.62 -0.706 c
+26.697 -0.706 26.774 -0.688 26.855 -0.647 c
+26.932 -0.611 27.006 -0.544 27.076 -0.456 c
+27.142 -0.368 27.197 -0.246 27.237 -0.088 c
+27.285 0.066 27.312 0.257 27.312 0.484 c
+31.416 -1.264 m
+31.405 -1.246 31.393 -1.216 31.387 -1.176 c
+31.387 -1.128 31.379 -1.081 31.372 -1.029 c
+31.372 -0.97 31.364 -0.912 31.357 -0.852 c
+31.357 -0.691 l
+31.239 -0.927 31.096 -1.095 30.931 -1.191 c
+30.762 -1.279 30.564 -1.323 30.328 -1.323 c
+30.13 -1.323 29.953 -1.279 29.799 -1.191 c
+29.641 -1.103 29.508 -0.981 29.402 -0.823 c
+29.303 -0.658 29.226 -0.467 29.167 -0.25 c
+29.116 -0.037 29.093 0.206 29.093 0.47 c
+29.093 0.735 29.116 0.974 29.167 1.19 c
+29.226 1.415 29.303 1.606 29.402 1.764 c
+29.508 1.918 29.641 2.043 29.799 2.131 c
+29.964 2.227 30.155 2.278 30.372 2.278 c
+30.468 2.278 30.564 2.263 30.651 2.234 c
+30.747 2.212 30.842 2.179 30.931 2.131 c
+31.019 2.08 31.096 2.017 31.166 1.94 c
+31.243 1.859 31.306 1.768 31.357 1.66 c
+31.357 1.749 l
+31.357 1.896 l
+31.357 2.057 l
+31.357 2.234 l
+31.357 3.513 l
+32.253 3.513 l
+32.253 -0.5 l
+32.253 -0.676 32.257 -0.834 32.268 -0.97 c
+32.276 -1.099 32.283 -1.199 32.283 -1.264 c
+h
+31.372 0.484 m
+31.372 0.72 31.346 0.912 31.298 1.058 c
+31.258 1.213 31.202 1.338 31.137 1.425 c
+31.077 1.514 31.008 1.573 30.931 1.602 c
+30.85 1.639 30.773 1.66 30.695 1.66 c
+30.597 1.66 30.504 1.635 30.416 1.587 c
+30.335 1.547 30.269 1.477 30.211 1.382 c
+30.159 1.282 30.115 1.161 30.078 1.014 c
+30.049 0.867 30.034 0.683 30.034 0.47 c
+30.034 0.077 30.086 -0.217 30.196 -0.412 c
+30.313 -0.611 30.475 -0.706 30.681 -0.706 c
+30.758 -0.706 30.836 -0.688 30.916 -0.647 c
+30.994 -0.611 31.067 -0.544 31.137 -0.456 c
+31.202 -0.368 31.258 -0.246 31.298 -0.088 c
+31.346 0.066 31.372 0.257 31.372 0.484 c
+38.115 -2.631 m
+38.115 3.513 l
+40.041 3.513 l
+40.041 2.896 l
+38.967 2.896 l
+38.967 -2.014 l
+40.041 -2.014 l
+40.041 -2.631 l
+h
+43.072 1.602 m
+43.072 -1.264 l
+42.175 -1.264 l
+42.175 1.602 l
+41.352 1.602 l
+41.352 2.219 l
+42.175 2.219 l
+42.175 2.484 l
+42.175 2.609 42.19 2.741 42.219 2.881 c
+42.256 3.017 42.326 3.135 42.425 3.233 c
+42.532 3.341 42.676 3.428 42.852 3.499 c
+43.028 3.564 43.252 3.601 43.528 3.601 c
+43.741 3.601 43.939 3.59 44.116 3.572 c
+44.292 3.549 44.442 3.532 44.571 3.513 c
+44.571 2.925 l
+44.442 2.944 44.299 2.958 44.145 2.969 c
+43.987 2.977 43.837 2.984 43.69 2.984 c
+43.561 2.984 43.458 2.969 43.381 2.94 c
+43.3 2.911 43.237 2.87 43.189 2.822 c
+43.139 2.77 43.106 2.708 43.087 2.631 c
+43.076 2.561 43.072 2.484 43.072 2.396 c
+43.072 2.219 l
+44.498 2.219 l
+44.498 1.602 l
+h
+47.588 -0.647 m
+48.72 -0.647 l
+48.72 -1.264 l
+45.413 -1.264 l
+45.413 -0.647 l
+46.677 -0.647 l
+46.677 1.602 l
+45.751 1.602 l
+45.751 2.219 l
+47.588 2.219 l
+h
+46.677 3.513 0.912 -0.676 re
+46.677 2.836 m
+51.649 -0.647 m
+52.781 -0.647 l
+52.781 -1.264 l
+49.473 -1.264 l
+49.473 -0.647 l
+50.738 -0.647 l
+50.738 2.896 l
+49.812 2.896 l
+49.812 3.513 l
+51.649 3.513 l
+h
+55.166 -1.323 m
+54.908 -1.323 54.68 -1.286 54.475 -1.22 c
+54.269 -1.143 54.093 -1.029 53.946 -0.882 c
+53.799 -0.728 53.681 -0.536 53.593 -0.309 c
+53.512 -0.085 53.475 0.18 53.475 0.484 c
+53.475 0.816 53.52 1.095 53.608 1.323 c
+53.703 1.558 53.832 1.741 53.99 1.881 c
+54.156 2.017 54.343 2.117 54.549 2.175 c
+54.754 2.242 54.964 2.278 55.181 2.278 c
+55.453 2.278 55.688 2.227 55.887 2.131 c
+56.092 2.043 56.257 1.911 56.386 1.735 c
+56.522 1.565 56.621 1.359 56.68 1.117 c
+56.746 0.881 56.783 0.617 56.783 0.324 c
+56.783 0.309 l
+54.416 0.309 l
+54.416 0.162 54.431 0.022 54.46 -0.103 c
+54.497 -0.231 54.553 -0.345 54.622 -0.441 c
+54.688 -0.53 54.773 -0.599 54.871 -0.647 c
+54.967 -0.698 55.081 -0.721 55.21 -0.721 c
+55.364 -0.721 55.504 -0.688 55.621 -0.617 c
+55.746 -0.551 55.835 -0.449 55.887 -0.309 c
+56.724 -0.382 l
+56.695 -0.482 56.639 -0.588 56.562 -0.706 c
+56.481 -0.816 56.379 -0.919 56.253 -1.014 c
+56.136 -1.103 55.982 -1.176 55.798 -1.235 c
+55.621 -1.294 55.409 -1.323 55.166 -1.323 c
+55.166 1.705 m
+55.077 1.705 54.989 1.691 54.902 1.66 c
+54.813 1.631 54.732 1.58 54.666 1.514 c
+54.596 1.444 54.537 1.356 54.49 1.249 c
+54.449 1.139 54.431 1.014 54.431 0.867 c
+55.901 0.867 l
+55.901 1.003 55.875 1.124 55.827 1.234 c
+55.787 1.341 55.731 1.429 55.665 1.5 c
+55.607 1.565 55.534 1.617 55.445 1.646 c
+55.357 1.683 55.261 1.705 55.166 1.705 c
+58.04 -2.631 m
+58.04 -2.014 l
+59.112 -2.014 l
+59.112 2.896 l
+58.04 2.896 l
+58.04 3.513 l
+59.965 3.513 l
+59.965 -2.631 l
+h
+f
+Q
+q 1 0 0 1 316.4525 277.2445 cm
+0 0 m
+-1.808 0 l
+-2.219 -1.396 l
+-2.91 -1.396 l
+-1.19 3.954 l
+-0.617 3.954 l
+1.118 -1.396 l
+0.426 -1.396 l
+h
+-1.631 0.588 m
+-0.176 0.588 l
+-0.897 3.013 l
+h
+1.617 0.779 m
+1.617 1.386 1.727 1.852 1.955 2.176 c
+2.19 2.5 2.517 2.66 2.94 2.66 c
+3.323 2.66 3.62 2.502 3.836 2.19 c
+3.836 4.248 l
+4.484 4.248 l
+4.484 -1.396 l
+3.896 -1.396 l
+3.851 -0.97 l
+3.645 -1.304 3.341 -1.469 2.94 -1.469 c
+2.529 -1.469 2.205 -1.315 1.97 -0.999 c
+1.735 -0.675 1.617 -0.22 1.617 0.368 c
+h
+2.264 0.397 m
+2.264 -0.044 2.326 -0.374 2.455 -0.588 c
+2.591 -0.793 2.812 -0.897 3.117 -0.897 c
+3.44 -0.897 3.678 -0.735 3.836 -0.411 c
+3.836 1.602 l
+3.668 1.914 3.429 2.072 3.117 2.072 c
+2.812 2.072 2.591 1.97 2.455 1.764 c
+2.326 1.559 2.264 1.235 2.264 0.794 c
+h
+5.351 0.779 m
+5.351 1.386 5.461 1.852 5.689 2.176 c
+5.924 2.5 6.251 2.66 6.674 2.66 c
+7.056 2.66 7.354 2.502 7.57 2.19 c
+7.57 4.248 l
+8.217 4.248 l
+8.217 -1.396 l
+7.629 -1.396 l
+7.585 -0.97 l
+7.379 -1.304 7.074 -1.469 6.674 -1.469 c
+6.262 -1.469 5.939 -1.315 5.704 -0.999 c
+5.469 -0.675 5.351 -0.22 5.351 0.368 c
+h
+5.997 0.397 m
+5.997 -0.044 6.06 -0.374 6.188 -0.588 c
+6.325 -0.793 6.545 -0.897 6.85 -0.897 c
+7.173 -0.897 7.412 -0.735 7.57 -0.411 c
+7.57 1.602 l
+7.402 1.914 7.163 2.072 6.85 2.072 c
+6.545 2.072 6.325 1.97 6.188 1.764 c
+6.06 1.559 5.997 1.235 5.997 0.794 c
+h
+12.979 -1.396 m
+12.939 -1.308 12.914 -1.161 12.906 -0.955 c
+12.671 -1.3 12.377 -1.469 12.025 -1.469 c
+11.66 -1.469 11.377 -1.374 11.171 -1.176 c
+10.973 -0.97 10.878 -0.683 10.878 -0.309 c
+10.878 0.092 11.013 0.412 11.289 0.647 c
+11.561 0.89 11.936 1.014 12.406 1.014 c
+12.891 1.014 l
+12.891 1.441 l
+12.891 1.676 12.836 1.841 12.73 1.941 c
+12.619 2.047 12.457 2.103 12.245 2.103 c
+12.046 2.103 11.884 2.043 11.759 1.926 c
+11.642 1.808 11.583 1.661 11.583 1.484 c
+10.936 1.484 l
+10.936 1.679 10.995 1.871 11.113 2.058 c
+11.238 2.242 11.4 2.389 11.597 2.5 c
+11.803 2.606 12.031 2.66 12.289 2.66 c
+12.69 2.66 12.994 2.558 13.2 2.352 c
+13.413 2.147 13.527 1.852 13.538 1.47 c
+13.538 -0.544 l
+13.538 -0.849 13.575 -1.113 13.656 -1.338 c
+13.656 -1.396 l
+h
+12.112 -0.881 m
+12.278 -0.881 12.428 -0.837 12.568 -0.75 c
+12.715 -0.661 12.821 -0.551 12.891 -0.411 c
+12.891 0.53 l
+12.524 0.53 l
+12.208 0.53 11.965 0.46 11.789 0.324 c
+11.612 0.195 11.524 0.008 11.524 -0.235 c
+11.524 -0.463 11.568 -0.628 11.657 -0.735 c
+11.745 -0.833 11.896 -0.881 12.112 -0.881 c
+16.434 -1.396 m
+16.434 2.058 l
+15.919 2.058 l
+15.919 2.587 l
+16.434 2.587 l
+16.434 2.955 l
+16.441 3.385 16.555 3.719 16.771 3.954 c
+16.996 4.197 17.309 4.322 17.712 4.322 c
+17.86 4.322 17.999 4.3 18.139 4.263 c
+18.286 4.223 18.437 4.168 18.595 4.102 c
+18.477 3.528 l
+18.242 3.653 17.999 3.719 17.756 3.719 c
+17.511 3.719 17.338 3.649 17.242 3.514 c
+17.143 3.385 17.095 3.19 17.095 2.926 c
+17.095 2.587 l
+17.742 2.587 l
+17.742 2.058 l
+17.095 2.058 l
+17.095 -1.396 l
+h
+18.903 -1.396 -0.646 3.983 re
+20.652 -1.396 -0.646 5.644 re
+23.092 -1.469 m
+22.593 -1.469 22.211 -1.323 21.946 -1.028 c
+21.681 -0.735 21.549 -0.301 21.549 0.279 c
+21.549 0.75 l
+21.549 1.345 21.674 1.812 21.931 2.147 c
+22.196 2.488 22.556 2.66 23.019 2.66 c
+23.478 2.66 23.82 2.506 24.048 2.205 c
+24.283 1.912 24.405 1.448 24.415 0.823 c
+24.415 0.397 l
+22.196 0.397 l
+22.196 0.309 l
+22.196 -0.124 22.273 -0.437 22.431 -0.632 c
+22.597 -0.819 22.828 -0.911 23.121 -0.911 c
+23.316 -0.911 23.489 -0.878 23.636 -0.808 c
+23.784 -0.731 23.919 -0.613 24.048 -0.455 c
+24.386 -0.867 l
+24.1 -1.271 23.669 -1.469 23.092 -1.469 c
+23.019 2.103 m
+22.743 2.103 22.541 2.007 22.416 1.823 c
+22.288 1.636 22.215 1.345 22.196 0.956 c
+23.769 0.956 l
+23.769 1.044 l
+23.746 1.426 23.68 1.694 23.563 1.852 c
+23.445 2.018 23.262 2.103 23.019 2.103 c
+27.767 3.543 m
+27.767 2.587 l
+28.37 2.587 l
+28.37 2.058 l
+27.767 2.058 l
+27.767 -0.411 l
+27.767 -0.569 27.788 -0.687 27.84 -0.764 c
+27.899 -0.845 27.987 -0.881 28.105 -0.881 c
+28.193 -0.881 28.281 -0.867 28.37 -0.837 c
+28.37 -1.396 l
+28.222 -1.444 28.068 -1.469 27.914 -1.469 c
+27.657 -1.469 27.462 -1.378 27.326 -1.19 c
+27.186 -1.007 27.12 -0.746 27.12 -0.411 c
+27.12 2.058 l
+26.518 2.058 l
+26.518 2.587 l
+27.12 2.587 l
+27.12 3.543 l
+h
+28.928 0.779 m
+28.928 1.357 29.064 1.812 29.34 2.147 c
+29.623 2.488 29.994 2.66 30.456 2.66 c
+30.916 2.66 31.283 2.492 31.559 2.161 c
+31.842 1.837 31.989 1.389 32 0.823 c
+32 0.397 l
+32 -0.172 31.857 -0.628 31.574 -0.97 c
+31.298 -1.304 30.931 -1.469 30.471 -1.469 c
+30.009 -1.469 29.637 -1.308 29.354 -0.985 c
+29.078 -0.654 28.935 -0.213 28.928 0.339 c
+h
+29.575 0.397 m
+29.575 -0.007 29.652 -0.323 29.81 -0.558 c
+29.976 -0.793 30.196 -0.911 30.471 -0.911 c
+31.037 -0.911 31.331 -0.5 31.353 0.324 c
+31.353 0.779 l
+31.353 1.18 31.269 1.5 31.104 1.735 c
+30.946 1.977 30.728 2.103 30.456 2.103 c
+30.192 2.103 29.976 1.977 29.81 1.735 c
+29.652 1.5 29.575 1.18 29.575 0.779 c
+h
+35.41 3.543 m
+35.41 2.587 l
+36.013 2.587 l
+36.013 2.058 l
+35.41 2.058 l
+35.41 -0.411 l
+35.41 -0.569 35.432 -0.687 35.484 -0.764 c
+35.543 -0.845 35.63 -0.881 35.748 -0.881 c
+35.837 -0.881 35.925 -0.867 36.013 -0.837 c
+36.013 -1.396 l
+35.866 -1.444 35.711 -1.469 35.557 -1.469 c
+35.3 -1.469 35.106 -1.378 34.969 -1.19 c
+34.83 -1.007 34.763 -0.746 34.763 -0.411 c
+34.763 2.058 l
+34.161 2.058 l
+34.161 2.587 l
+34.763 2.587 l
+34.763 3.543 l
+h
+37.424 2.176 m
+37.678 2.5 37.997 2.66 38.379 2.66 c
+39.085 2.66 39.441 2.19 39.453 1.249 c
+39.453 -1.396 l
+38.805 -1.396 l
+38.805 1.22 l
+38.805 1.532 38.751 1.754 38.644 1.881 c
+38.534 2.007 38.379 2.072 38.173 2.072 c
+38.015 2.072 37.868 2.018 37.733 1.912 c
+37.604 1.801 37.502 1.665 37.424 1.5 c
+37.424 -1.396 l
+36.777 -1.396 l
+36.777 4.248 l
+37.424 4.248 l
+h
+41.834 -1.469 m
+41.334 -1.469 40.952 -1.323 40.687 -1.028 c
+40.423 -0.735 40.29 -0.301 40.29 0.279 c
+40.29 0.75 l
+40.29 1.345 40.415 1.812 40.673 2.147 c
+40.937 2.488 41.297 2.66 41.76 2.66 c
+42.219 2.66 42.561 2.506 42.79 2.205 c
+43.025 1.912 43.146 1.448 43.156 0.823 c
+43.156 0.397 l
+40.937 0.397 l
+40.937 0.309 l
+40.937 -0.124 41.014 -0.437 41.172 -0.632 c
+41.338 -0.819 41.569 -0.911 41.863 -0.911 c
+42.058 -0.911 42.231 -0.878 42.377 -0.808 c
+42.524 -0.731 42.661 -0.613 42.79 -0.455 c
+43.127 -0.867 l
+42.84 -1.271 42.41 -1.469 41.834 -1.469 c
+41.76 2.103 m
+41.485 2.103 41.282 2.007 41.157 1.823 c
+41.029 1.636 40.955 1.345 40.937 0.956 c
+42.51 0.956 l
+42.51 1.044 l
+42.488 1.426 42.422 1.694 42.304 1.852 c
+42.186 2.018 42.003 2.103 41.76 2.103 c
+f
+Q
+q 1 0 0 1 363.7688 276.9219 cm
+0 0 m
+0 0.087 -0.044 0.165 -0.132 0.235 c
+-0.22 0.312 -0.407 0.415 -0.69 0.544 c
+-1.124 0.72 -1.422 0.9 -1.587 1.087 c
+-1.745 1.271 -1.822 1.502 -1.822 1.778 c
+-1.822 2.119 -1.701 2.403 -1.455 2.63 c
+-1.201 2.865 -0.864 2.983 -0.441 2.983 c
+-0.011 2.983 0.339 2.869 0.603 2.645 c
+0.867 2.418 1 2.117 1 1.734 c
+-0.044 1.734 l
+-0.044 2.057 -0.183 2.219 -0.455 2.219 c
+-0.565 2.219 -0.654 2.183 -0.72 2.117 c
+-0.79 2.046 -0.823 1.947 -0.823 1.822 c
+-0.823 1.734 -0.786 1.653 -0.706 1.587 c
+-0.628 1.529 -0.448 1.433 -0.162 1.308 c
+0.268 1.15 0.566 0.974 0.735 0.779 c
+0.912 0.591 1 0.341 1 0.029 c
+1 -0.324 0.867 -0.611 0.603 -0.823 c
+0.339 -1.04 -0.011 -1.147 -0.441 -1.147 c
+-0.735 -1.147 -0.995 -1.092 -1.22 -0.985 c
+-1.448 -0.867 -1.624 -0.706 -1.749 -0.5 c
+-1.866 -0.294 -1.926 -0.074 -1.926 0.161 c
+-0.941 0.161 l
+-0.941 -0.026 -0.904 -0.162 -0.823 -0.25 c
+-0.735 -0.339 -0.602 -0.383 -0.426 -0.383 c
+-0.143 -0.383 0 -0.258 0 0 c
+2.866 3.881 m
+2.866 2.91 l
+3.396 2.91 l
+3.396 2.117 l
+2.866 2.117 l
+2.866 0.147 l
+2.866 -0.011 2.885 -0.118 2.926 -0.177 c
+2.973 -0.235 3.057 -0.265 3.175 -0.265 c
+3.281 -0.265 3.366 -0.258 3.425 -0.235 c
+3.425 -1.044 l
+3.248 -1.11 3.057 -1.147 2.851 -1.147 c
+2.176 -1.147 1.83 -0.761 1.823 0.014 c
+1.823 2.117 l
+1.367 2.117 l
+1.367 2.91 l
+1.823 2.91 l
+1.823 3.881 l
+h
+5.85 -1.073 m
+5.821 -1.015 5.791 -0.912 5.762 -0.765 c
+5.575 -1.022 5.325 -1.147 5.012 -1.147 c
+4.678 -1.147 4.399 -1.04 4.175 -0.823 c
+3.958 -0.599 3.851 -0.31 3.851 0.043 c
+3.851 0.455 3.984 0.771 4.248 0.999 c
+4.513 1.234 4.895 1.352 5.395 1.352 c
+5.718 1.352 l
+5.718 1.675 l
+5.718 1.851 5.681 1.973 5.615 2.042 c
+5.556 2.119 5.469 2.16 5.351 2.16 c
+5.093 2.16 4.968 2.006 4.968 1.705 c
+3.925 1.705 l
+3.925 2.076 4.061 2.381 4.337 2.616 c
+4.609 2.859 4.958 2.983 5.38 2.983 c
+5.821 2.983 6.159 2.865 6.394 2.63 c
+6.637 2.403 6.762 2.08 6.762 1.66 c
+6.762 -0.206 l
+6.762 -0.551 6.809 -0.82 6.909 -1.015 c
+6.909 -1.073 l
+h
+5.247 -0.324 m
+5.355 -0.324 5.446 -0.306 5.527 -0.265 c
+5.615 -0.217 5.677 -0.158 5.718 -0.088 c
+5.718 0.735 l
+5.469 0.735 l
+5.292 0.735 5.149 0.675 5.042 0.558 c
+4.943 0.448 4.895 0.301 4.895 0.118 c
+4.895 -0.177 5.012 -0.324 5.247 -0.324 c
+7.379 1.043 m
+7.379 1.69 7.497 2.175 7.732 2.499 c
+7.967 2.822 8.298 2.983 8.731 2.983 c
+9.084 2.983 9.356 2.84 9.554 2.557 c
+9.598 2.91 l
+10.539 2.91 l
+10.539 -1.073 l
+10.539 -1.58 10.396 -1.97 10.113 -2.234 c
+9.826 -2.506 9.422 -2.646 8.893 -2.646 c
+8.665 -2.646 8.43 -2.602 8.187 -2.514 c
+7.952 -2.426 7.776 -2.311 7.659 -2.176 c
+8.011 -1.455 l
+8.106 -1.563 8.235 -1.646 8.393 -1.706 c
+8.548 -1.771 8.694 -1.808 8.835 -1.808 c
+9.07 -1.808 9.235 -1.75 9.334 -1.632 c
+9.44 -1.522 9.496 -1.345 9.496 -1.103 c
+9.496 -0.75 l
+9.297 -1.015 9.04 -1.147 8.717 -1.147 c
+8.294 -1.147 7.967 -0.985 7.732 -0.662 c
+7.504 -0.331 7.387 0.139 7.379 0.749 c
+h
+8.422 0.779 m
+8.422 0.404 8.47 0.135 8.57 -0.03 c
+8.665 -0.198 8.819 -0.279 9.025 -0.279 c
+9.239 -0.279 9.396 -0.202 9.496 -0.044 c
+9.496 1.851 l
+9.386 2.017 9.231 2.102 9.025 2.102 c
+8.819 2.102 8.665 2.017 8.57 1.851 c
+8.47 1.683 8.422 1.414 8.422 1.043 c
+h
+12.391 -1.073 -1.043 3.983 re
+11.304 3.939 m
+11.304 4.093 11.352 4.222 11.451 4.321 c
+11.557 4.428 11.693 4.483 11.863 4.483 c
+12.039 4.483 12.175 4.428 12.274 4.321 c
+12.38 4.222 12.436 4.093 12.436 3.939 c
+12.436 3.77 12.38 3.634 12.274 3.528 c
+12.175 3.428 12.039 3.38 11.863 3.38 c
+11.693 3.38 11.557 3.428 11.451 3.528 c
+11.352 3.634 11.304 3.77 11.304 3.939 c
+14.155 2.91 m
+14.184 2.513 l
+14.42 2.825 14.722 2.983 15.096 2.983 c
+15.78 2.983 16.133 2.502 16.154 1.543 c
+16.154 -1.073 l
+15.111 -1.073 l
+15.111 1.469 l
+15.111 1.693 15.074 1.855 15.008 1.955 c
+14.938 2.05 14.82 2.102 14.655 2.102 c
+14.468 2.102 14.321 2.006 14.215 1.822 c
+14.215 -1.073 l
+13.17 -1.073 l
+13.17 2.91 l
+h
+16.772 1.043 m
+16.772 1.69 16.889 2.175 17.124 2.499 c
+17.359 2.822 17.691 2.983 18.124 2.983 c
+18.477 2.983 18.749 2.84 18.947 2.557 c
+18.992 2.91 l
+19.932 2.91 l
+19.932 -1.073 l
+19.932 -1.58 19.788 -1.97 19.506 -2.234 c
+19.219 -2.506 18.815 -2.646 18.286 -2.646 c
+18.058 -2.646 17.822 -2.602 17.581 -2.514 c
+17.345 -2.426 17.169 -2.311 17.051 -2.176 c
+17.404 -1.455 l
+17.5 -1.563 17.628 -1.646 17.786 -1.706 c
+17.941 -1.771 18.088 -1.808 18.227 -1.808 c
+18.462 -1.808 18.628 -1.75 18.726 -1.632 c
+18.834 -1.522 18.888 -1.345 18.888 -1.103 c
+18.888 -0.75 l
+18.69 -1.015 18.433 -1.147 18.109 -1.147 c
+17.687 -1.147 17.359 -0.985 17.124 -0.662 c
+16.897 -0.331 16.779 0.139 16.772 0.749 c
+h
+17.816 0.779 m
+17.816 0.404 17.863 0.135 17.962 -0.03 c
+18.058 -0.198 18.213 -0.279 18.418 -0.279 c
+18.631 -0.279 18.789 -0.202 18.888 -0.044 c
+18.888 1.851 l
+18.778 2.017 18.624 2.102 18.418 2.102 c
+18.213 2.102 18.058 2.017 17.962 1.851 c
+17.863 1.683 17.816 1.414 17.816 1.043 c
+h
+f
+Q
+q 1 0 0 1 388.3315 275.8485 cm
+0 0 m
+-0.04 0.088 -0.067 0.235 -0.074 0.441 c
+-0.309 0.096 -0.603 -0.073 -0.956 -0.073 c
+-1.32 -0.073 -1.603 0.022 -1.808 0.22 c
+-2.007 0.426 -2.103 0.713 -2.103 1.087 c
+-2.103 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.286 -1.044 2.41 -0.574 2.41 c
+-0.088 2.41 l
+-0.088 2.837 l
+-0.088 3.072 -0.144 3.237 -0.25 3.337 c
+-0.36 3.443 -0.522 3.499 -0.736 3.499 c
+-0.934 3.499 -1.096 3.439 -1.22 3.322 c
+-1.338 3.204 -1.397 3.057 -1.397 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.985 3.267 -1.867 3.454 c
+-1.742 3.638 -1.58 3.785 -1.382 3.896 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.291 4.056 0.014 3.954 0.22 3.748 c
+0.434 3.543 0.548 3.248 0.558 2.866 c
+0.558 0.852 l
+0.558 0.548 0.595 0.283 0.675 0.058 c
+0.675 0 l
+h
+-0.867 0.515 m
+-0.703 0.515 -0.551 0.559 -0.412 0.646 c
+-0.265 0.735 -0.158 0.845 -0.088 0.985 c
+-0.088 1.926 l
+-0.456 1.926 l
+-0.772 1.926 -1.015 1.856 -1.191 1.72 c
+-1.368 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.324 0.661 c
+-1.235 0.563 -1.085 0.515 -0.867 0.515 c
+3.204 3.366 m
+3.116 3.385 3.017 3.395 2.91 3.395 c
+2.576 3.395 2.341 3.212 2.204 2.851 c
+2.204 0 l
+1.558 0 l
+1.558 3.983 l
+2.19 3.983 l
+2.204 3.572 l
+2.381 3.896 2.624 4.056 2.94 4.056 c
+3.046 4.056 3.135 4.035 3.204 3.998 c
+h
+5.203 -0.073 m
+4.704 -0.073 4.321 0.073 4.056 0.368 c
+3.792 0.661 3.659 1.095 3.659 1.675 c
+3.659 2.146 l
+3.659 2.741 3.785 3.208 4.041 3.543 c
+4.307 3.884 4.667 4.056 5.13 4.056 c
+5.589 4.056 5.93 3.902 6.158 3.601 c
+6.393 3.308 6.515 2.844 6.526 2.219 c
+6.526 1.793 l
+4.307 1.793 l
+4.307 1.705 l
+4.307 1.272 4.384 0.959 4.542 0.764 c
+4.707 0.577 4.939 0.485 5.232 0.485 c
+5.427 0.485 5.6 0.518 5.747 0.588 c
+5.894 0.665 6.03 0.783 6.158 0.941 c
+6.497 0.529 l
+6.21 0.125 5.78 -0.073 5.203 -0.073 c
+5.13 3.499 m
+4.854 3.499 4.652 3.403 4.527 3.219 c
+4.398 3.032 4.325 2.741 4.307 2.352 c
+5.88 2.352 l
+5.88 2.44 l
+5.857 2.822 5.791 3.09 5.674 3.248 c
+5.556 3.414 5.373 3.499 5.13 3.499 c
+9.348 0 m
+9.308 0.088 9.282 0.235 9.275 0.441 c
+9.04 0.096 8.745 -0.073 8.392 -0.073 c
+8.029 -0.073 7.746 0.022 7.54 0.22 c
+7.342 0.426 7.247 0.713 7.247 1.087 c
+7.247 1.488 7.382 1.808 7.658 2.043 c
+7.93 2.286 8.305 2.41 8.775 2.41 c
+9.26 2.41 l
+9.26 2.837 l
+9.26 3.072 9.205 3.237 9.098 3.337 c
+8.988 3.443 8.826 3.499 8.613 3.499 c
+8.415 3.499 8.253 3.439 8.128 3.322 c
+8.01 3.204 7.952 3.057 7.952 2.881 c
+7.305 2.881 l
+7.305 3.075 7.364 3.267 7.482 3.454 c
+7.606 3.638 7.768 3.785 7.966 3.896 c
+8.172 4.002 8.4 4.056 8.658 4.056 c
+9.057 4.056 9.363 3.954 9.568 3.748 c
+9.782 3.543 9.896 3.248 9.907 2.866 c
+9.907 0.852 l
+9.907 0.548 9.944 0.283 10.025 0.058 c
+10.025 0 l
+h
+8.481 0.515 m
+8.646 0.515 8.797 0.559 8.937 0.646 c
+9.084 0.735 9.19 0.845 9.26 0.985 c
+9.26 1.926 l
+8.893 1.926 l
+8.577 1.926 8.334 1.856 8.157 1.72 c
+7.981 1.591 7.893 1.404 7.893 1.161 c
+7.893 0.933 7.937 0.768 8.025 0.661 c
+8.114 0.563 8.264 0.515 8.481 0.515 c
+10.95 0.353 m
+10.95 0.47 10.983 0.566 11.054 0.646 c
+11.12 0.723 11.222 0.764 11.362 0.764 c
+11.509 0.764 11.615 0.723 11.685 0.646 c
+11.762 0.566 11.803 0.47 11.803 0.353 c
+11.803 0.243 11.762 0.151 11.685 0.073 c
+11.615 -0.004 11.509 -0.044 11.362 -0.044 c
+11.222 -0.044 11.12 -0.004 11.054 0.073 c
+10.983 0.151 10.95 0.243 10.95 0.353 c
+17.888 5.35 m
+17.888 1.544 l
+17.888 1.043 17.741 0.646 17.447 0.353 c
+17.161 0.066 16.76 -0.073 16.242 -0.073 c
+15.713 -0.073 15.305 0.062 15.022 0.338 c
+14.746 0.621 14.611 1.022 14.611 1.544 c
+14.611 5.35 l
+15.272 5.35 l
+15.272 1.573 l
+15.272 1.209 15.345 0.941 15.492 0.764 c
+15.646 0.588 15.897 0.5 16.242 0.5 c
+16.583 0.5 16.83 0.588 16.977 0.764 c
+17.132 0.941 17.212 1.209 17.212 1.573 c
+17.212 5.35 l
+h
+20.784 1.014 m
+20.784 1.161 20.729 1.282 20.622 1.382 c
+20.512 1.477 20.306 1.595 20.005 1.735 c
+19.66 1.882 19.417 2.003 19.27 2.102 c
+19.123 2.209 19.013 2.326 18.947 2.454 c
+18.877 2.58 18.844 2.738 18.844 2.925 c
+18.844 3.248 18.962 3.516 19.197 3.734 c
+19.432 3.946 19.733 4.056 20.108 4.056 c
+20.49 4.056 20.799 3.943 21.034 3.719 c
+21.269 3.491 21.387 3.204 21.387 2.851 c
+20.74 2.851 l
+20.74 3.028 20.681 3.179 20.564 3.308 c
+20.446 3.432 20.292 3.499 20.108 3.499 c
+19.91 3.499 19.758 3.443 19.652 3.337 c
+19.542 3.237 19.49 3.105 19.49 2.94 c
+19.49 2.811 19.527 2.705 19.608 2.616 c
+19.685 2.535 19.876 2.433 20.182 2.308 c
+20.66 2.12 20.99 1.933 21.167 1.749 c
+21.343 1.573 21.431 1.345 21.431 1.073 c
+21.431 0.721 21.306 0.441 21.063 0.235 c
+20.828 0.029 20.512 -0.073 20.122 -0.073 c
+19.7 -0.073 19.362 0.044 19.108 0.279 c
+18.851 0.522 18.726 0.827 18.726 1.191 c
+19.373 1.191 l
+19.38 0.962 19.45 0.786 19.579 0.661 c
+19.704 0.544 19.887 0.485 20.122 0.485 c
+20.336 0.485 20.498 0.532 20.608 0.632 c
+20.725 0.727 20.784 0.856 20.784 1.014 c
+23.694 -0.073 m
+23.195 -0.073 22.813 0.073 22.548 0.368 c
+22.283 0.661 22.151 1.095 22.151 1.675 c
+22.151 2.146 l
+22.151 2.741 22.276 3.208 22.534 3.543 c
+22.798 3.884 23.158 4.056 23.621 4.056 c
+24.08 4.056 24.423 3.902 24.65 3.601 c
+24.885 3.308 25.007 2.844 25.017 2.219 c
+25.017 1.793 l
+22.798 1.793 l
+22.798 1.705 l
+22.798 1.272 22.875 0.959 23.033 0.764 c
+23.199 0.577 23.43 0.485 23.724 0.485 c
+23.918 0.485 24.091 0.518 24.238 0.588 c
+24.385 0.665 24.521 0.783 24.65 0.941 c
+24.988 0.529 l
+24.701 0.125 24.271 -0.073 23.694 -0.073 c
+23.621 3.499 m
+23.345 3.499 23.143 3.403 23.018 3.219 c
+22.89 3.032 22.816 2.741 22.798 2.352 c
+24.371 2.352 l
+24.371 2.44 l
+24.348 2.822 24.282 3.09 24.165 3.248 c
+24.047 3.414 23.864 3.499 23.621 3.499 c
+f
+Q
+q 1 0 0 1 415.8331 276.3772 cm
+0 0 m
+0 0.166 0.052 0.302 0.162 0.412 c
+0.268 0.518 0.415 0.574 0.603 0.574 c
+0.768 0.574 0.912 0.518 1.029 0.412 c
+1.147 0.302 1.205 0.166 1.205 0 c
+1.205 -0.168 1.147 -0.305 1.029 -0.411 c
+0.912 -0.521 0.768 -0.573 0.603 -0.573 c
+0.426 -0.573 0.279 -0.521 0.162 -0.411 c
+0.052 -0.305 0 -0.168 0 0 c
+f
+Q
+420.463 275.848 -0.647 3.983 re
+420.508 280.876 m
+420.508 280.766 420.478 280.674 420.419 280.596 c
+420.36 280.527 420.265 280.493 420.14 280.493 c
+420.022 280.493 419.926 280.527 419.86 280.596 c
+419.802 280.674 419.772 280.766 419.772 280.876 c
+419.772 280.993 419.802 281.085 419.86 281.155 c
+419.926 281.232 420.022 281.273 420.14 281.273 c
+420.265 281.273 420.36 281.232 420.419 281.155 c
+420.478 281.074 420.508 280.982 420.508 280.876 c
+422.095 279.832 m
+422.11 279.391 l
+422.363 279.733 422.686 279.905 423.08 279.905 c
+423.785 279.905 424.142 279.435 424.153 278.494 c
+424.153 275.848 l
+423.506 275.848 l
+423.506 278.465 l
+423.506 278.777 423.451 278.998 423.344 279.126 c
+423.234 279.251 423.08 279.317 422.874 279.317 c
+422.716 279.317 422.569 279.262 422.433 279.156 c
+422.304 279.045 422.201 278.91 422.124 278.744 c
+422.124 275.848 l
+421.478 275.848 l
+421.478 279.832 l
+h
+427.063 276.863 m
+427.063 277.009 427.008 277.131 426.902 277.23 c
+426.791 277.325 426.586 277.443 426.284 277.583 c
+425.939 277.73 425.696 277.851 425.549 277.95 c
+425.402 278.057 425.292 278.175 425.225 278.303 c
+425.156 278.428 425.123 278.586 425.123 278.773 c
+425.123 279.097 425.24 279.365 425.476 279.582 c
+425.711 279.795 426.012 279.905 426.387 279.905 c
+426.769 279.905 427.078 279.791 427.313 279.567 c
+427.548 279.34 427.666 279.053 427.666 278.7 c
+427.019 278.7 l
+427.019 278.877 426.96 279.027 426.843 279.156 c
+426.725 279.28 426.571 279.347 426.387 279.347 c
+426.189 279.347 426.038 279.292 425.931 279.185 c
+425.821 279.086 425.769 278.954 425.769 278.788 c
+425.769 278.659 425.806 278.553 425.887 278.465 c
+425.964 278.384 426.156 278.281 426.461 278.156 c
+426.939 277.969 427.269 277.782 427.446 277.597 c
+427.621 277.421 427.71 277.194 427.71 276.922 c
+427.71 276.569 427.585 276.29 427.342 276.084 c
+427.107 275.878 426.791 275.775 426.401 275.775 c
+425.979 275.775 425.641 275.893 425.387 276.128 c
+425.13 276.371 425.005 276.675 425.005 277.039 c
+425.652 277.039 l
+425.659 276.811 425.729 276.635 425.858 276.51 c
+425.983 276.392 426.166 276.334 426.401 276.334 c
+426.615 276.334 426.777 276.381 426.887 276.481 c
+427.004 276.576 427.063 276.705 427.063 276.863 c
+429.4 280.787 m
+429.4 279.832 l
+430.003 279.832 l
+430.003 279.303 l
+429.4 279.303 l
+429.4 276.833 l
+429.4 276.675 429.422 276.558 429.474 276.481 c
+429.532 276.4 429.621 276.363 429.738 276.363 c
+429.827 276.363 429.915 276.377 430.003 276.407 c
+430.003 275.848 l
+429.856 275.801 429.701 275.775 429.547 275.775 c
+429.29 275.775 429.096 275.866 428.959 276.055 c
+428.82 276.238 428.753 276.499 428.753 276.833 c
+428.753 279.303 l
+428.151 279.303 l
+428.151 279.832 l
+428.753 279.832 l
+428.753 280.787 l
+h
+432.193 275.775 m
+431.693 275.775 431.311 275.922 431.047 276.216 c
+430.782 276.51 430.65 276.944 430.65 277.524 c
+430.65 277.994 l
+430.65 278.59 430.775 279.056 431.032 279.391 c
+431.296 279.733 431.656 279.905 432.119 279.905 c
+432.579 279.905 432.921 279.751 433.148 279.45 c
+433.384 279.156 433.505 278.692 433.516 278.068 c
+433.516 277.642 l
+431.296 277.642 l
+431.296 277.553 l
+431.296 277.12 431.373 276.807 431.531 276.612 c
+431.697 276.425 431.928 276.334 432.223 276.334 c
+432.417 276.334 432.59 276.367 432.737 276.436 c
+432.884 276.514 433.019 276.631 433.148 276.789 c
+433.487 276.377 l
+433.2 275.974 432.77 275.775 432.193 275.775 c
+432.119 279.347 m
+431.844 279.347 431.642 279.251 431.517 279.068 c
+431.388 278.88 431.315 278.59 431.296 278.2 c
+432.869 278.2 l
+432.869 278.289 l
+432.847 278.671 432.781 278.939 432.664 279.097 c
+432.546 279.262 432.362 279.347 432.119 279.347 c
+436.338 275.848 m
+436.298 275.937 436.273 276.084 436.265 276.29 c
+436.03 275.944 435.735 275.775 435.383 275.775 c
+435.019 275.775 434.736 275.87 434.53 276.069 c
+434.332 276.275 434.236 276.562 434.236 276.936 c
+434.236 277.337 434.372 277.657 434.648 277.892 c
+434.92 278.134 435.294 278.259 435.765 278.259 c
+436.25 278.259 l
+436.25 278.686 l
+436.25 278.921 436.194 279.086 436.088 279.185 c
+435.978 279.292 435.816 279.347 435.604 279.347 c
+435.405 279.347 435.243 279.288 435.118 279.17 c
+435.001 279.053 434.941 278.906 434.941 278.729 c
+434.295 278.729 l
+434.295 278.924 434.353 279.116 434.471 279.303 c
+434.596 279.486 434.758 279.633 434.956 279.744 c
+435.162 279.85 435.39 279.905 435.647 279.905 c
+436.048 279.905 436.353 279.802 436.559 279.596 c
+436.772 279.391 436.886 279.097 436.897 278.715 c
+436.897 276.701 l
+436.897 276.396 436.934 276.132 437.015 275.907 c
+437.015 275.848 l
+h
+435.471 276.363 m
+435.637 276.363 435.787 276.407 435.926 276.495 c
+436.074 276.583 436.18 276.693 436.25 276.833 c
+436.25 277.774 l
+435.882 277.774 l
+435.567 277.774 435.324 277.705 435.147 277.568 c
+434.971 277.439 434.883 277.252 434.883 277.009 c
+434.883 276.782 434.927 276.616 435.016 276.51 c
+435.103 276.411 435.254 276.363 435.471 276.363 c
+437.764 278.023 m
+437.764 278.63 437.875 279.097 438.102 279.42 c
+438.337 279.744 438.664 279.905 439.087 279.905 c
+439.469 279.905 439.766 279.747 439.984 279.435 c
+439.984 281.493 l
+440.63 281.493 l
+440.63 275.848 l
+440.042 275.848 l
+439.998 276.275 l
+439.793 275.941 439.487 275.775 439.087 275.775 c
+438.675 275.775 438.352 275.929 438.116 276.245 c
+437.881 276.569 437.764 277.024 437.764 277.612 c
+h
+438.411 277.642 m
+438.411 277.2 438.473 276.87 438.602 276.657 c
+438.738 276.452 438.958 276.348 439.263 276.348 c
+439.587 276.348 439.826 276.51 439.984 276.833 c
+439.984 278.847 l
+439.814 279.159 439.575 279.317 439.263 279.317 c
+438.958 279.317 438.738 279.214 438.602 279.008 c
+438.473 278.803 438.411 278.48 438.411 278.039 c
+h
+443.217 278.023 m
+443.217 278.601 443.353 279.056 443.629 279.391 c
+443.912 279.733 444.283 279.905 444.746 279.905 c
+445.206 279.905 445.573 279.737 445.848 279.405 c
+446.131 279.082 446.278 278.634 446.29 278.068 c
+446.29 277.642 l
+446.29 277.072 446.147 276.616 445.863 276.275 c
+445.588 275.941 445.22 275.775 444.761 275.775 c
+444.298 275.775 443.926 275.937 443.643 276.26 c
+443.368 276.591 443.225 277.032 443.217 277.583 c
+h
+443.864 277.642 m
+443.864 277.238 443.941 276.922 444.099 276.687 c
+444.265 276.452 444.485 276.334 444.761 276.334 c
+445.326 276.334 445.621 276.745 445.642 277.568 c
+445.642 278.023 l
+445.642 278.424 445.559 278.744 445.393 278.979 c
+445.235 279.222 445.018 279.347 444.746 279.347 c
+444.481 279.347 444.265 279.222 444.099 278.979 c
+443.941 278.744 443.864 278.424 443.864 278.023 c
+h
+447.421 275.848 m
+447.421 279.303 l
+446.893 279.303 l
+446.893 279.832 l
+447.421 279.832 l
+447.421 280.288 l
+447.421 280.688 447.517 281.001 447.716 281.229 c
+447.921 281.452 448.2 281.566 448.553 281.566 c
+448.689 281.566 448.821 281.545 448.95 281.508 c
+448.921 280.963 l
+448.821 280.982 448.722 280.993 448.626 280.993 c
+448.252 280.993 448.068 280.728 448.068 280.199 c
+448.068 279.832 l
+448.744 279.832 l
+448.744 279.303 l
+448.068 279.303 l
+448.068 275.848 l
+h
+451.522 275.848 m
+451.522 279.303 l
+450.993 279.303 l
+450.993 279.832 l
+451.522 279.832 l
+451.522 280.288 l
+451.522 280.688 451.618 281.001 451.816 281.229 c
+452.022 281.452 452.302 281.566 452.655 281.566 c
+452.79 281.566 452.923 281.545 453.051 281.508 c
+453.021 280.963 l
+452.923 280.982 452.823 280.993 452.728 280.993 c
+452.353 280.993 452.169 280.728 452.169 280.199 c
+452.169 279.832 l
+452.846 279.832 l
+452.846 279.303 l
+452.169 279.303 l
+452.169 275.848 l
+h
+455.638 276.201 m
+455.421 275.914 455.109 275.775 454.697 275.775 c
+454.334 275.775 454.058 275.896 453.874 276.142 c
+453.698 276.396 453.602 276.76 453.595 277.23 c
+453.595 279.832 l
+454.242 279.832 l
+454.242 277.289 l
+454.242 276.66 454.425 276.348 454.8 276.348 c
+455.201 276.348 455.477 276.525 455.624 276.878 c
+455.624 279.832 l
+456.27 279.832 l
+456.27 275.848 l
+455.653 275.848 l
+h
+457.96 275.848 -0.646 5.644 re
+459.68 275.848 -0.646 5.644 re
+462.591 275.848 m
+462.591 279.303 l
+462.076 279.303 l
+462.076 279.832 l
+462.591 279.832 l
+462.591 280.199 l
+462.598 280.629 462.712 280.963 462.928 281.198 c
+463.153 281.441 463.466 281.566 463.869 281.566 c
+464.017 281.566 464.156 281.545 464.296 281.508 c
+464.443 281.468 464.594 281.412 464.752 281.346 c
+464.634 280.772 l
+464.399 280.897 464.156 280.963 463.913 280.963 c
+463.668 280.963 463.495 280.894 463.399 280.758 c
+463.3 280.629 463.252 280.435 463.252 280.17 c
+463.252 279.832 l
+463.899 279.832 l
+463.899 279.303 l
+463.252 279.303 l
+463.252 275.848 l
+h
+465.06 275.848 -0.646 3.983 re
+466.809 275.848 -0.646 5.644 re
+469.249 275.775 m
+468.75 275.775 468.368 275.922 468.102 276.216 c
+467.838 276.51 467.706 276.944 467.706 277.524 c
+467.706 277.994 l
+467.706 278.59 467.831 279.056 468.088 279.391 c
+468.353 279.733 468.713 279.905 469.176 279.905 c
+469.635 279.905 469.977 279.751 470.205 279.45 c
+470.44 279.156 470.562 278.692 470.572 278.068 c
+470.572 277.642 l
+468.353 277.642 l
+468.353 277.553 l
+468.353 277.12 468.43 276.807 468.588 276.612 c
+468.753 276.425 468.985 276.334 469.278 276.334 c
+469.473 276.334 469.646 276.367 469.793 276.436 c
+469.941 276.514 470.076 276.631 470.205 276.789 c
+470.543 276.377 l
+470.256 275.974 469.826 275.775 469.249 275.775 c
+469.176 279.347 m
+468.9 279.347 468.698 279.251 468.573 279.068 c
+468.445 278.88 468.371 278.59 468.353 278.2 c
+469.926 278.2 l
+469.926 278.289 l
+469.904 278.671 469.837 278.939 469.72 279.097 c
+469.602 279.262 469.419 279.347 469.176 279.347 c
+475.952 277.642 m
+475.952 277.013 475.834 276.543 475.599 276.23 c
+475.372 275.926 475.055 275.775 474.644 275.775 c
+474.24 275.775 473.931 275.926 473.718 276.23 c
+473.718 274.32 l
+473.071 274.32 l
+473.071 279.832 l
+473.659 279.832 l
+473.704 279.391 l
+473.916 279.733 474.225 279.905 474.629 279.905 c
+475.07 279.905 475.397 279.751 475.614 279.45 c
+475.827 279.145 475.941 278.689 475.952 278.083 c
+h
+475.306 278.023 m
+475.306 278.465 475.235 278.788 475.1 278.994 c
+474.96 279.207 474.739 279.317 474.438 279.317 c
+474.122 279.317 473.883 279.163 473.718 278.862 c
+473.718 276.789 l
+473.883 276.484 474.122 276.334 474.438 276.334 c
+474.732 276.334 474.945 276.436 475.085 276.642 c
+475.221 276.855 475.294 277.186 475.306 277.627 c
+h
+478.833 275.848 m
+478.793 275.937 478.767 276.084 478.76 276.29 c
+478.525 275.944 478.23 275.775 477.878 275.775 c
+477.514 275.775 477.231 275.87 477.025 276.069 c
+476.827 276.275 476.731 276.562 476.731 276.936 c
+476.731 277.337 476.867 277.657 477.143 277.892 c
+477.415 278.134 477.789 278.259 478.26 278.259 c
+478.745 278.259 l
+478.745 278.686 l
+478.745 278.921 478.69 279.086 478.583 279.185 c
+478.473 279.292 478.311 279.347 478.098 279.347 c
+477.9 279.347 477.738 279.288 477.613 279.17 c
+477.496 279.053 477.436 278.906 477.436 278.729 c
+476.79 278.729 l
+476.79 278.924 476.849 279.116 476.966 279.303 c
+477.091 279.486 477.253 279.633 477.452 279.744 c
+477.658 279.85 477.885 279.905 478.142 279.905 c
+478.543 279.905 478.848 279.802 479.054 279.596 c
+479.267 279.391 479.381 279.097 479.391 278.715 c
+479.391 276.701 l
+479.391 276.396 479.428 276.132 479.509 275.907 c
+479.509 275.848 l
+h
+477.966 276.363 m
+478.132 276.363 478.282 276.407 478.421 276.495 c
+478.568 276.583 478.675 276.693 478.745 276.833 c
+478.745 277.774 l
+478.377 277.774 l
+478.061 277.774 477.819 277.705 477.642 277.568 c
+477.467 277.439 477.378 277.252 477.378 277.009 c
+477.378 276.782 477.422 276.616 477.51 276.51 c
+477.598 276.411 477.749 276.363 477.966 276.363 c
+481.244 280.787 m
+481.244 279.832 l
+481.847 279.832 l
+481.847 279.303 l
+481.244 279.303 l
+481.244 276.833 l
+481.244 276.675 481.266 276.558 481.317 276.481 c
+481.376 276.4 481.465 276.363 481.582 276.363 c
+481.67 276.363 481.758 276.377 481.847 276.407 c
+481.847 275.848 l
+481.7 275.801 481.545 275.775 481.39 275.775 c
+481.134 275.775 480.939 275.866 480.802 276.055 c
+480.663 276.238 480.597 276.499 480.597 276.833 c
+480.597 279.303 l
+479.994 279.303 l
+479.994 279.832 l
+480.597 279.832 l
+480.597 280.787 l
+h
+483.258 279.42 m
+483.511 279.744 483.831 279.905 484.213 279.905 c
+484.918 279.905 485.275 279.435 485.286 278.494 c
+485.286 275.848 l
+484.64 275.848 l
+484.64 278.465 l
+484.64 278.777 484.584 278.998 484.478 279.126 c
+484.367 279.251 484.213 279.317 484.008 279.317 c
+483.849 279.317 483.702 279.262 483.566 279.156 c
+483.437 279.045 483.335 278.91 483.258 278.744 c
+483.258 275.848 l
+482.611 275.848 l
+482.611 281.493 l
+483.258 281.493 l
+h
+486.256 274.775 m
+485.859 275.04 l
+486.094 275.363 486.215 275.698 486.227 276.039 c
+486.227 276.657 l
+486.888 276.657 l
+486.888 276.128 l
+486.888 275.87 486.822 275.625 486.697 275.378 c
+486.579 275.135 486.433 274.933 486.256 274.775 c
+490.298 280.787 m
+490.298 279.832 l
+490.901 279.832 l
+490.901 279.303 l
+490.298 279.303 l
+490.298 276.833 l
+490.298 276.675 490.321 276.558 490.372 276.481 c
+490.431 276.4 490.519 276.363 490.637 276.363 c
+490.724 276.363 490.813 276.377 490.901 276.407 c
+490.901 275.848 l
+490.755 275.801 490.6 275.775 490.445 275.775 c
+490.188 275.775 489.993 275.866 489.857 276.055 c
+489.718 276.238 489.652 276.499 489.652 276.833 c
+489.652 279.303 l
+489.049 279.303 l
+489.049 279.832 l
+489.652 279.832 l
+489.652 280.787 l
+h
+491.46 278.023 m
+491.46 278.601 491.596 279.056 491.871 279.391 c
+492.154 279.733 492.525 279.905 492.989 279.905 c
+493.448 279.905 493.815 279.737 494.09 279.405 c
+494.374 279.082 494.52 278.634 494.532 278.068 c
+494.532 277.642 l
+494.532 277.072 494.389 276.616 494.105 276.275 c
+493.83 275.941 493.462 275.775 493.003 275.775 c
+492.54 275.775 492.168 275.937 491.886 276.26 c
+491.611 276.591 491.467 277.032 491.46 277.583 c
+h
+492.106 277.642 m
+492.106 277.238 492.183 276.922 492.342 276.687 c
+492.507 276.452 492.727 276.334 493.003 276.334 c
+493.569 276.334 493.863 276.745 493.885 277.568 c
+493.885 278.023 l
+493.885 278.424 493.801 278.744 493.635 278.979 c
+493.477 279.222 493.261 279.347 492.989 279.347 c
+492.723 279.347 492.507 279.222 492.342 278.979 c
+492.183 278.744 492.106 278.424 492.106 278.023 c
+h
+499.133 275.848 m
+499.092 275.937 499.067 276.084 499.059 276.29 c
+498.824 275.944 498.53 275.775 498.177 275.775 c
+497.813 275.775 497.531 275.87 497.325 276.069 c
+497.126 276.275 497.03 276.562 497.03 276.936 c
+497.03 277.337 497.167 277.657 497.442 277.892 c
+497.714 278.134 498.089 278.259 498.559 278.259 c
+499.044 278.259 l
+499.044 278.686 l
+499.044 278.921 498.989 279.086 498.883 279.185 c
+498.772 279.292 498.611 279.347 498.398 279.347 c
+498.2 279.347 498.038 279.288 497.913 279.17 c
+497.795 279.053 497.736 278.906 497.736 278.729 c
+497.089 278.729 l
+497.089 278.924 497.148 279.116 497.265 279.303 c
+497.391 279.486 497.552 279.633 497.751 279.744 c
+497.957 279.85 498.184 279.905 498.441 279.905 c
+498.842 279.905 499.147 279.802 499.353 279.596 c
+499.566 279.391 499.68 279.097 499.692 278.715 c
+499.692 276.701 l
+499.692 276.396 499.728 276.132 499.809 275.907 c
+499.809 275.848 l
+h
+498.265 276.363 m
+498.431 276.363 498.581 276.407 498.721 276.495 c
+498.868 276.583 498.975 276.693 499.044 276.833 c
+499.044 277.774 l
+498.676 277.774 l
+498.361 277.774 498.119 277.705 497.942 277.568 c
+497.766 277.439 497.677 277.252 497.677 277.009 c
+497.677 276.782 497.722 276.616 497.81 276.51 c
+497.898 276.411 498.048 276.363 498.265 276.363 c
+500.558 278.023 m
+500.558 278.63 500.669 279.097 500.897 279.42 c
+501.132 279.744 501.458 279.905 501.882 279.905 c
+502.264 279.905 502.561 279.747 502.778 279.435 c
+502.778 281.493 l
+503.424 281.493 l
+503.424 275.848 l
+502.836 275.848 l
+502.792 276.275 l
+502.587 275.941 502.281 275.775 501.882 275.775 c
+501.47 275.775 501.146 275.929 500.911 276.245 c
+500.676 276.569 500.558 277.024 500.558 277.612 c
+h
+501.205 277.642 m
+501.205 277.2 501.267 276.87 501.396 276.657 c
+501.533 276.452 501.753 276.348 502.057 276.348 c
+502.381 276.348 502.62 276.51 502.778 276.833 c
+502.778 278.847 l
+502.609 279.159 502.37 279.317 502.057 279.317 c
+501.753 279.317 501.533 279.214 501.396 279.008 c
+501.267 278.803 501.205 278.48 501.205 278.039 c
+h
+504.292 278.023 m
+504.292 278.63 504.402 279.097 504.63 279.42 c
+504.866 279.744 505.192 279.905 505.614 279.905 c
+505.997 279.905 506.295 279.747 506.512 279.435 c
+506.512 281.493 l
+507.158 281.493 l
+507.158 275.848 l
+506.57 275.848 l
+506.526 276.275 l
+506.32 275.941 506.015 275.775 505.614 275.775 c
+505.203 275.775 504.88 275.929 504.645 276.245 c
+504.409 276.569 504.292 277.024 504.292 277.612 c
+h
+504.939 277.642 m
+504.939 277.2 505.001 276.87 505.13 276.657 c
+505.265 276.452 505.487 276.348 505.791 276.348 c
+506.115 276.348 506.354 276.51 506.512 276.833 c
+506.512 278.847 l
+506.343 279.159 506.104 279.317 505.791 279.317 c
+505.487 279.317 505.265 279.214 505.13 279.008 c
+505.001 278.803 504.939 278.48 504.939 278.039 c
+h
+511.921 275.848 m
+511.881 275.937 511.854 276.084 511.847 276.29 c
+511.612 275.944 511.318 275.775 510.965 275.775 c
+510.601 275.775 510.318 275.87 510.113 276.069 c
+509.915 276.275 509.819 276.562 509.819 276.936 c
+509.819 277.337 509.955 277.657 510.231 277.892 c
+510.503 278.134 510.877 278.259 511.347 278.259 c
+511.833 278.259 l
+511.833 278.686 l
+511.833 278.921 511.777 279.086 511.671 279.185 c
+511.561 279.292 511.399 279.347 511.185 279.347 c
+510.987 279.347 510.826 279.288 510.701 279.17 c
+510.583 279.053 510.524 278.906 510.524 278.729 c
+509.878 278.729 l
+509.878 278.924 509.936 279.116 510.054 279.303 c
+510.179 279.486 510.341 279.633 510.539 279.744 c
+510.745 279.85 510.973 279.905 511.23 279.905 c
+511.63 279.905 511.935 279.802 512.141 279.596 c
+512.354 279.391 512.469 279.097 512.479 278.715 c
+512.479 276.701 l
+512.479 276.396 512.516 276.132 512.597 275.907 c
+512.597 275.848 l
+h
+511.054 276.363 m
+511.218 276.363 511.37 276.407 511.509 276.495 c
+511.656 276.583 511.763 276.693 511.833 276.833 c
+511.833 277.774 l
+511.465 277.774 l
+511.149 277.774 510.906 277.705 510.73 277.568 c
+510.553 277.439 510.466 277.252 510.466 277.009 c
+510.466 276.782 510.51 276.616 510.598 276.51 c
+510.686 276.411 510.836 276.363 511.054 276.363 c
+514.185 275.848 -0.647 5.644 re
+515.904 275.848 -0.647 5.644 re
+f
+q 1 0 0 1 339.0603 266.9265 cm
+0 0 m
+0.213 0 0.386 0.062 0.514 0.191 c
+0.65 0.326 0.723 0.517 0.735 0.764 c
+1.352 0.764 l
+1.33 0.382 1.194 0.062 0.941 -0.192 c
+0.683 -0.437 0.371 -0.559 0 -0.559 c
+-0.493 -0.559 -0.867 -0.408 -1.133 -0.103 c
+-1.389 0.209 -1.515 0.675 -1.515 1.294 c
+-1.515 1.734 l
+-1.515 2.329 -1.389 2.786 -1.133 3.101 c
+-0.867 3.413 -0.493 3.571 0 3.571 c
+0.4 3.571 0.72 3.439 0.955 3.175 c
+1.198 2.917 1.33 2.572 1.352 2.131 c
+0.735 2.131 l
+0.712 2.425 0.639 2.645 0.514 2.792 c
+0.396 2.94 0.224 3.013 0 3.013 c
+-0.294 3.013 -0.511 2.913 -0.647 2.719 c
+-0.786 2.532 -0.86 2.223 -0.867 1.793 c
+-0.867 1.278 l
+-0.867 0.808 -0.801 0.473 -0.662 0.278 c
+-0.515 0.091 -0.294 0 0 0 c
+2.748 3.087 m
+3.002 3.41 3.322 3.571 3.704 3.571 c
+4.409 3.571 4.766 3.101 4.777 2.16 c
+4.777 -0.485 l
+4.13 -0.485 l
+4.13 2.131 l
+4.13 2.443 4.075 2.664 3.968 2.792 c
+3.858 2.917 3.704 2.983 3.498 2.983 c
+3.34 2.983 3.193 2.929 3.057 2.822 c
+2.929 2.711 2.825 2.576 2.748 2.41 c
+2.748 -0.485 l
+2.102 -0.485 l
+2.102 5.159 l
+2.748 5.159 l
+h
+7.775 -0.485 m
+7.735 -0.397 7.709 -0.25 7.702 -0.044 c
+7.467 -0.389 7.173 -0.559 6.82 -0.559 c
+6.456 -0.559 6.173 -0.464 5.967 -0.265 c
+5.769 -0.059 5.674 0.228 5.674 0.602 c
+5.674 1.003 5.809 1.323 6.085 1.558 c
+6.357 1.801 6.732 1.925 7.202 1.925 c
+7.687 1.925 l
+7.687 2.352 l
+7.687 2.587 7.632 2.752 7.526 2.851 c
+7.415 2.958 7.253 3.013 7.04 3.013 c
+6.842 3.013 6.68 2.954 6.555 2.836 c
+6.437 2.719 6.379 2.572 6.379 2.395 c
+5.732 2.395 l
+5.732 2.59 5.791 2.782 5.909 2.969 c
+6.034 3.152 6.196 3.299 6.393 3.41 c
+6.599 3.517 6.827 3.571 7.085 3.571 c
+7.485 3.571 7.79 3.469 7.996 3.262 c
+8.209 3.057 8.323 2.763 8.334 2.381 c
+8.334 0.367 l
+8.334 0.062 8.371 -0.202 8.452 -0.427 c
+8.452 -0.485 l
+h
+6.908 0.029 m
+7.073 0.029 7.224 0.073 7.364 0.161 c
+7.511 0.249 7.617 0.359 7.687 0.5 c
+7.687 1.44 l
+7.32 1.44 l
+7.004 1.44 6.761 1.371 6.585 1.234 c
+6.408 1.105 6.32 0.918 6.32 0.675 c
+6.32 0.448 6.364 0.282 6.453 0.176 c
+6.541 0.077 6.691 0.029 6.908 0.029 c
+9.951 3.498 m
+9.965 3.057 l
+10.219 3.399 10.543 3.571 10.936 3.571 c
+11.642 3.571 11.997 3.101 12.009 2.16 c
+12.009 -0.485 l
+11.362 -0.485 l
+11.362 2.131 l
+11.362 2.443 11.307 2.664 11.2 2.792 c
+11.09 2.917 10.936 2.983 10.73 2.983 c
+10.572 2.983 10.425 2.929 10.289 2.822 c
+10.16 2.711 10.057 2.576 9.98 2.41 c
+9.98 -0.485 l
+9.333 -0.485 l
+9.333 3.498 l
+h
+12.847 1.69 m
+12.847 2.308 12.957 2.77 13.184 3.087 c
+13.408 3.41 13.743 3.571 14.184 3.571 c
+14.584 3.571 14.89 3.395 15.095 3.042 c
+15.139 3.498 l
+15.727 3.498 l
+15.727 -0.53 l
+15.727 -1.018 15.599 -1.397 15.345 -1.661 c
+15.089 -1.926 14.736 -2.058 14.287 -2.058 c
+14.089 -2.058 13.868 -2.007 13.626 -1.912 c
+13.379 -1.812 13.2 -1.69 13.082 -1.544 c
+13.346 -1.103 l
+13.611 -1.368 13.909 -1.5 14.243 -1.5 c
+14.779 -1.5 15.055 -1.206 15.066 -0.618 c
+15.066 -0.088 l
+14.86 -0.405 14.559 -0.559 14.169 -0.559 c
+13.758 -0.559 13.435 -0.408 13.2 -0.103 c
+12.972 0.209 12.854 0.661 12.847 1.249 c
+h
+13.508 1.308 m
+13.508 0.866 13.57 0.536 13.699 0.323 c
+13.824 0.118 14.041 0.014 14.346 0.014 c
+14.669 0.014 14.908 0.18 15.066 0.514 c
+15.066 2.499 l
+14.898 2.822 14.659 2.983 14.346 2.983 c
+14.052 2.983 13.835 2.881 13.699 2.675 c
+13.57 2.469 13.508 2.146 13.508 1.705 c
+h
+18.109 -0.559 m
+17.609 -0.559 17.227 -0.412 16.962 -0.118 c
+16.698 0.176 16.565 0.61 16.565 1.19 c
+16.565 1.66 l
+16.565 2.256 16.691 2.723 16.947 3.057 c
+17.212 3.399 17.572 3.571 18.035 3.571 c
+18.495 3.571 18.836 3.417 19.064 3.116 c
+19.299 2.822 19.421 2.358 19.432 1.734 c
+19.432 1.308 l
+17.212 1.308 l
+17.212 1.219 l
+17.212 0.786 17.289 0.473 17.447 0.278 c
+17.613 0.091 17.844 0 18.138 0 c
+18.333 0 18.505 0.033 18.653 0.103 c
+18.8 0.18 18.935 0.297 19.064 0.455 c
+19.403 0.043 l
+19.116 -0.36 18.686 -0.559 18.109 -0.559 c
+18.035 3.013 m
+17.759 3.013 17.558 2.917 17.433 2.734 c
+17.304 2.547 17.231 2.256 17.212 1.866 c
+18.785 1.866 l
+18.785 1.955 l
+18.763 2.337 18.697 2.605 18.58 2.763 c
+18.462 2.929 18.278 3.013 18.035 3.013 c
+22.151 0.529 m
+22.151 0.675 22.096 0.797 21.99 0.897 c
+21.879 0.992 21.674 1.109 21.372 1.249 c
+21.026 1.396 20.784 1.517 20.637 1.616 c
+20.49 1.723 20.38 1.841 20.313 1.969 c
+20.244 2.094 20.211 2.252 20.211 2.439 c
+20.211 2.763 20.328 3.031 20.564 3.248 c
+20.799 3.461 21.1 3.571 21.475 3.571 c
+21.857 3.571 22.166 3.457 22.401 3.233 c
+22.636 3.006 22.754 2.719 22.754 2.366 c
+22.107 2.366 l
+22.107 2.543 22.048 2.693 21.931 2.822 c
+21.813 2.946 21.659 3.013 21.475 3.013 c
+21.277 3.013 21.126 2.958 21.019 2.851 c
+20.909 2.752 20.857 2.62 20.857 2.454 c
+20.857 2.326 20.894 2.219 20.975 2.131 c
+21.052 2.05 21.244 1.947 21.549 1.822 c
+22.026 1.635 22.357 1.448 22.534 1.263 c
+22.709 1.087 22.798 0.86 22.798 0.588 c
+22.798 0.235 22.673 -0.044 22.43 -0.25 c
+22.195 -0.456 21.879 -0.559 21.489 -0.559 c
+21.067 -0.559 20.729 -0.441 20.475 -0.206 c
+20.218 0.037 20.093 0.341 20.093 0.706 c
+20.74 0.706 l
+20.747 0.477 20.817 0.301 20.946 0.176 c
+21.071 0.058 21.254 0 21.489 0 c
+21.703 0 21.865 0.047 21.975 0.147 c
+22.092 0.242 22.151 0.371 22.151 0.529 c
+25.532 -0.485 m
+25.532 2.969 l
+25.017 2.969 l
+25.017 3.498 l
+25.532 3.498 l
+25.532 3.866 l
+25.539 4.295 25.653 4.629 25.87 4.865 c
+26.094 5.107 26.407 5.232 26.811 5.232 c
+26.958 5.232 27.097 5.211 27.237 5.174 c
+27.384 5.134 27.535 5.078 27.693 5.012 c
+27.575 4.438 l
+27.34 4.564 27.097 4.629 26.854 4.629 c
+26.609 4.629 26.436 4.56 26.341 4.424 c
+26.241 4.295 26.193 4.101 26.193 3.836 c
+26.193 3.498 l
+26.84 3.498 l
+26.84 2.969 l
+26.193 2.969 l
+26.193 -0.485 l
+h
+28.001 -0.485 -0.646 3.983 re
+29.751 -0.485 -0.647 5.644 re
+32.19 -0.559 m
+31.691 -0.559 31.309 -0.412 31.044 -0.118 c
+30.779 0.176 30.647 0.61 30.647 1.19 c
+30.647 1.66 l
+30.647 2.256 30.772 2.723 31.029 3.057 c
+31.294 3.399 31.654 3.571 32.117 3.571 c
+32.576 3.571 32.918 3.417 33.146 3.116 c
+33.381 2.822 33.503 2.358 33.514 1.734 c
+33.514 1.308 l
+31.294 1.308 l
+31.294 1.219 l
+31.294 0.786 31.371 0.473 31.529 0.278 c
+31.694 0.091 31.926 0 32.22 0 c
+32.415 0 32.587 0.033 32.734 0.103 c
+32.882 0.18 33.017 0.297 33.146 0.455 c
+33.484 0.043 l
+33.198 -0.36 32.768 -0.559 32.19 -0.559 c
+32.117 3.013 m
+31.841 3.013 31.639 2.917 31.515 2.734 c
+31.386 2.547 31.313 2.256 31.294 1.866 c
+32.867 1.866 l
+32.867 1.955 l
+32.845 2.337 32.778 2.605 32.661 2.763 c
+32.543 2.929 32.36 3.013 32.117 3.013 c
+36.233 0.529 m
+36.233 0.675 36.178 0.797 36.071 0.897 c
+35.961 0.992 35.755 1.109 35.454 1.249 c
+35.108 1.396 34.866 1.517 34.719 1.616 c
+34.572 1.723 34.461 1.841 34.395 1.969 c
+34.326 2.094 34.293 2.252 34.293 2.439 c
+34.293 2.763 34.41 3.031 34.646 3.248 c
+34.881 3.461 35.182 3.571 35.556 3.571 c
+35.939 3.571 36.248 3.457 36.483 3.233 c
+36.718 3.006 36.836 2.719 36.836 2.366 c
+36.188 2.366 l
+36.188 2.543 36.13 2.693 36.012 2.822 c
+35.895 2.946 35.741 3.013 35.556 3.013 c
+35.359 3.013 35.207 2.958 35.101 2.851 c
+34.991 2.752 34.939 2.62 34.939 2.454 c
+34.939 2.326 34.976 2.219 35.057 2.131 c
+35.134 2.05 35.325 1.947 35.631 1.822 c
+36.107 1.635 36.439 1.448 36.615 1.263 c
+36.791 1.087 36.88 0.86 36.88 0.588 c
+36.88 0.235 36.755 -0.044 36.512 -0.25 c
+36.277 -0.456 35.961 -0.559 35.571 -0.559 c
+35.149 -0.559 34.81 -0.441 34.557 -0.206 c
+34.299 0.037 34.175 0.341 34.175 0.706 c
+34.822 0.706 l
+34.829 0.477 34.899 0.301 35.027 0.176 c
+35.153 0.058 35.336 0 35.571 0 c
+35.785 0 35.946 0.047 36.057 0.147 c
+36.174 0.242 36.233 0.371 36.233 0.529 c
+39.731 -0.485 m
+39.731 2.969 l
+39.202 2.969 l
+39.202 3.498 l
+39.731 3.498 l
+39.731 3.954 l
+39.731 4.354 39.827 4.667 40.025 4.895 c
+40.231 5.119 40.51 5.232 40.863 5.232 c
+40.999 5.232 41.131 5.211 41.26 5.174 c
+41.231 4.629 l
+41.131 4.648 41.032 4.659 40.936 4.659 c
+40.562 4.659 40.378 4.394 40.378 3.866 c
+40.378 3.498 l
+41.054 3.498 l
+41.054 2.969 l
+40.378 2.969 l
+40.378 -0.485 l
+h
+43.465 2.881 m
+43.377 2.899 43.277 2.91 43.171 2.91 c
+42.837 2.91 42.601 2.726 42.465 2.366 c
+42.465 -0.485 l
+41.819 -0.485 l
+41.819 3.498 l
+42.451 3.498 l
+42.465 3.087 l
+42.642 3.41 42.885 3.571 43.2 3.571 c
+43.307 3.571 43.394 3.549 43.465 3.513 c
+h
+43.905 1.69 m
+43.905 2.267 44.042 2.723 44.317 3.057 c
+44.6 3.399 44.971 3.571 45.434 3.571 c
+45.894 3.571 46.261 3.403 46.537 3.072 c
+46.82 2.748 46.966 2.3 46.978 1.734 c
+46.978 1.308 l
+46.978 0.738 46.835 0.282 46.552 -0.059 c
+46.276 -0.393 45.908 -0.559 45.449 -0.559 c
+44.986 -0.559 44.615 -0.397 44.332 -0.074 c
+44.057 0.257 43.913 0.698 43.905 1.249 c
+h
+44.552 1.308 m
+44.552 0.904 44.63 0.588 44.788 0.353 c
+44.953 0.118 45.173 0 45.449 0 c
+46.015 0 46.309 0.411 46.331 1.234 c
+46.331 1.69 l
+46.331 2.09 46.247 2.41 46.081 2.645 c
+45.923 2.888 45.707 3.013 45.434 3.013 c
+45.17 3.013 44.953 2.888 44.788 2.645 c
+44.63 2.41 44.552 2.09 44.552 1.69 c
+h
+48.433 3.498 m
+48.448 3.131 l
+48.69 3.424 49.01 3.571 49.403 3.571 c
+49.844 3.571 50.153 3.374 50.33 2.983 c
+50.583 3.374 50.932 3.571 51.373 3.571 c
+52.108 3.571 52.483 3.108 52.505 2.19 c
+52.505 -0.485 l
+51.858 -0.485 l
+51.858 2.131 l
+51.858 2.425 51.803 2.638 51.696 2.778 c
+51.597 2.913 51.425 2.983 51.182 2.983 c
+50.983 2.983 50.822 2.903 50.696 2.748 c
+50.579 2.601 50.509 2.41 50.49 2.175 c
+50.49 -0.485 l
+49.829 -0.485 l
+49.829 2.16 l
+49.829 2.707 49.609 2.983 49.168 2.983 c
+48.834 2.983 48.599 2.822 48.462 2.499 c
+48.462 -0.485 l
+47.816 -0.485 l
+47.816 3.498 l
+h
+56.576 0 m
+56.79 0 56.962 0.062 57.091 0.191 c
+57.227 0.326 57.3 0.517 57.311 0.764 c
+57.929 0.764 l
+57.906 0.382 57.771 0.062 57.517 -0.192 c
+57.26 -0.437 56.948 -0.559 56.576 -0.559 c
+56.084 -0.559 55.709 -0.408 55.444 -0.103 c
+55.188 0.209 55.062 0.675 55.062 1.294 c
+55.062 1.734 l
+55.062 2.329 55.188 2.786 55.444 3.101 c
+55.709 3.413 56.084 3.571 56.576 3.571 c
+56.977 3.571 57.297 3.439 57.532 3.175 c
+57.775 2.917 57.906 2.572 57.929 2.131 c
+57.311 2.131 l
+57.289 2.425 57.216 2.645 57.091 2.792 c
+56.973 2.94 56.8 3.013 56.576 3.013 c
+56.283 3.013 56.065 2.913 55.93 2.719 c
+55.79 2.532 55.716 2.223 55.709 1.793 c
+55.709 1.278 l
+55.709 0.808 55.776 0.473 55.915 0.278 c
+56.061 0.091 56.283 0 56.576 0 c
+60.707 -0.133 m
+60.49 -0.42 60.177 -0.559 59.766 -0.559 c
+59.402 -0.559 59.126 -0.437 58.943 -0.192 c
+58.766 0.062 58.671 0.426 58.664 0.897 c
+58.664 3.498 l
+59.31 3.498 l
+59.31 0.955 l
+59.31 0.326 59.494 0.014 59.869 0.014 c
+60.269 0.014 60.545 0.191 60.692 0.544 c
+60.692 3.498 l
+61.339 3.498 l
+61.339 -0.485 l
+60.721 -0.485 l
+h
+63.97 2.881 m
+63.882 2.899 63.782 2.91 63.676 2.91 c
+63.342 2.91 63.107 2.726 62.97 2.366 c
+62.97 -0.485 l
+62.324 -0.485 l
+62.324 3.498 l
+62.955 3.498 l
+62.97 3.087 l
+63.147 3.41 63.389 3.571 63.705 3.571 c
+63.812 3.571 63.9 3.549 63.97 3.513 c
+h
+66.263 2.881 m
+66.174 2.899 66.076 2.91 65.969 2.91 c
+65.634 2.91 65.399 2.726 65.264 2.366 c
+65.264 -0.485 l
+64.617 -0.485 l
+64.617 3.498 l
+65.249 3.498 l
+65.264 3.087 l
+65.44 3.41 65.682 3.571 65.999 3.571 c
+66.105 3.571 66.193 3.549 66.263 3.513 c
+h
+68.262 -0.559 m
+67.762 -0.559 67.38 -0.412 67.115 -0.118 c
+66.851 0.176 66.718 0.61 66.718 1.19 c
+66.718 1.66 l
+66.718 2.256 66.843 2.723 67.101 3.057 c
+67.365 3.399 67.726 3.571 68.189 3.571 c
+68.648 3.571 68.99 3.417 69.218 3.116 c
+69.453 2.822 69.574 2.358 69.585 1.734 c
+69.585 1.308 l
+67.365 1.308 l
+67.365 1.219 l
+67.365 0.786 67.443 0.473 67.601 0.278 c
+67.766 0.091 67.998 0 68.291 0 c
+68.486 0 68.659 0.033 68.806 0.103 c
+68.952 0.18 69.089 0.297 69.218 0.455 c
+69.555 0.043 l
+69.269 -0.36 68.839 -0.559 68.262 -0.559 c
+68.189 3.013 m
+67.913 3.013 67.711 2.917 67.586 2.734 c
+67.458 2.547 67.383 2.256 67.365 1.866 c
+68.938 1.866 l
+68.938 1.955 l
+68.916 2.337 68.85 2.605 68.732 2.763 c
+68.615 2.929 68.431 3.013 68.189 3.013 c
+70.982 3.498 m
+70.996 3.057 l
+71.25 3.399 71.573 3.571 71.967 3.571 c
+72.672 3.571 73.028 3.101 73.039 2.16 c
+73.039 -0.485 l
+72.393 -0.485 l
+72.393 2.131 l
+72.393 2.443 72.337 2.664 72.231 2.792 c
+72.121 2.917 71.967 2.983 71.761 2.983 c
+71.603 2.983 71.456 2.929 71.319 2.822 c
+71.19 2.711 71.088 2.576 71.011 2.41 c
+71.011 -0.485 l
+70.364 -0.485 l
+70.364 3.498 l
+h
+74.862 4.453 m
+74.862 3.498 l
+75.464 3.498 l
+75.464 2.969 l
+74.862 2.969 l
+74.862 0.5 l
+74.862 0.341 74.884 0.224 74.936 0.147 c
+74.994 0.066 75.082 0.029 75.2 0.029 c
+75.288 0.029 75.377 0.043 75.464 0.073 c
+75.464 -0.485 l
+75.318 -0.533 75.163 -0.559 75.009 -0.559 c
+74.751 -0.559 74.556 -0.468 74.421 -0.279 c
+74.281 -0.096 74.215 0.165 74.215 0.5 c
+74.215 2.969 l
+73.613 2.969 l
+73.613 3.498 l
+74.215 3.498 l
+74.215 4.453 l
+h
+77.816 1.69 m
+77.816 2.296 77.927 2.763 78.155 3.087 c
+78.39 3.41 78.716 3.571 79.14 3.571 c
+79.522 3.571 79.819 3.413 80.036 3.101 c
+80.036 5.159 l
+80.683 5.159 l
+80.683 -0.485 l
+80.095 -0.485 l
+80.05 -0.059 l
+79.845 -0.393 79.54 -0.559 79.14 -0.559 c
+78.728 -0.559 78.404 -0.405 78.169 -0.088 c
+77.934 0.235 77.816 0.69 77.816 1.278 c
+h
+78.463 1.308 m
+78.463 0.866 78.525 0.536 78.654 0.323 c
+78.79 0.118 79.011 0.014 79.316 0.014 c
+79.639 0.014 79.878 0.176 80.036 0.5 c
+80.036 2.513 l
+79.867 2.825 79.628 2.983 79.316 2.983 c
+79.011 2.983 78.79 2.881 78.654 2.675 c
+78.525 2.469 78.463 2.146 78.463 1.705 c
+h
+82.388 -0.485 -0.647 3.983 re
+82.432 4.542 m
+82.432 4.432 82.402 4.34 82.344 4.263 c
+82.285 4.193 82.19 4.159 82.064 4.159 c
+81.947 4.159 81.851 4.193 81.785 4.263 c
+81.727 4.34 81.697 4.432 81.697 4.542 c
+81.697 4.659 81.727 4.751 81.785 4.821 c
+81.851 4.898 81.947 4.939 82.064 4.939 c
+82.19 4.939 82.285 4.898 82.344 4.821 c
+82.402 4.74 82.432 4.648 82.432 4.542 c
+85.048 2.881 m
+84.96 2.899 84.861 2.91 84.754 2.91 c
+84.42 2.91 84.185 2.726 84.048 2.366 c
+84.048 -0.485 l
+83.402 -0.485 l
+83.402 3.498 l
+84.034 3.498 l
+84.048 3.087 l
+84.225 3.41 84.468 3.571 84.784 3.571 c
+84.89 3.571 84.979 3.549 85.048 3.513 c
+h
+87.048 -0.559 m
+86.548 -0.559 86.165 -0.412 85.901 -0.118 c
+85.636 0.176 85.504 0.61 85.504 1.19 c
+85.504 1.66 l
+85.504 2.256 85.629 2.723 85.886 3.057 c
+86.151 3.399 86.51 3.571 86.974 3.571 c
+87.433 3.571 87.775 3.417 88.002 3.116 c
+88.238 2.822 88.359 2.358 88.37 1.734 c
+88.37 1.308 l
+86.151 1.308 l
+86.151 1.219 l
+86.151 0.786 86.228 0.473 86.386 0.278 c
+86.551 0.091 86.783 0 87.077 0 c
+87.271 0 87.445 0.033 87.591 0.103 c
+87.738 0.18 87.874 0.297 88.002 0.455 c
+88.341 0.043 l
+88.054 -0.36 87.624 -0.559 87.048 -0.559 c
+86.974 3.013 m
+86.699 3.013 86.496 2.917 86.371 2.734 c
+86.242 2.547 86.169 2.256 86.151 1.866 c
+87.724 1.866 l
+87.724 1.955 l
+87.701 2.337 87.636 2.605 87.518 2.763 c
+87.4 2.929 87.216 3.013 86.974 3.013 c
+90.531 0 m
+90.744 0 90.917 0.062 91.046 0.191 c
+91.181 0.326 91.255 0.517 91.266 0.764 c
+91.883 0.764 l
+91.861 0.382 91.725 0.062 91.472 -0.192 c
+91.214 -0.437 90.902 -0.559 90.531 -0.559 c
+90.038 -0.559 89.664 -0.408 89.399 -0.103 c
+89.142 0.209 89.017 0.675 89.017 1.294 c
+89.017 1.734 l
+89.017 2.329 89.142 2.786 89.399 3.101 c
+89.664 3.413 90.038 3.571 90.531 3.571 c
+90.932 3.571 91.252 3.439 91.487 3.175 c
+91.729 2.917 91.861 2.572 91.883 2.131 c
+91.266 2.131 l
+91.244 2.425 91.171 2.645 91.046 2.792 c
+90.928 2.94 90.755 3.013 90.531 3.013 c
+90.237 3.013 90.02 2.913 89.884 2.719 c
+89.745 2.532 89.671 2.223 89.664 1.793 c
+89.664 1.278 l
+89.664 0.808 89.73 0.473 89.87 0.278 c
+90.017 0.091 90.237 0 90.531 0 c
+93.486 4.453 m
+93.486 3.498 l
+94.088 3.498 l
+94.088 2.969 l
+93.486 2.969 l
+93.486 0.5 l
+93.486 0.341 93.508 0.224 93.559 0.147 c
+93.618 0.066 93.706 0.029 93.824 0.029 c
+93.912 0.029 94 0.043 94.088 0.073 c
+94.088 -0.485 l
+93.941 -0.533 93.787 -0.559 93.633 -0.559 c
+93.375 -0.559 93.18 -0.468 93.045 -0.279 c
+92.905 -0.096 92.839 0.165 92.839 0.5 c
+92.839 2.969 l
+92.236 2.969 l
+92.236 3.498 l
+92.839 3.498 l
+92.839 4.453 l
+h
+94.647 1.69 m
+94.647 2.267 94.782 2.723 95.058 3.057 c
+95.341 3.399 95.713 3.571 96.176 3.571 c
+96.635 3.571 97.002 3.403 97.278 3.072 c
+97.561 2.748 97.708 2.3 97.719 1.734 c
+97.719 1.308 l
+97.719 0.738 97.575 0.282 97.292 -0.059 c
+97.017 -0.393 96.65 -0.559 96.191 -0.559 c
+95.727 -0.559 95.356 -0.397 95.073 -0.074 c
+94.797 0.257 94.654 0.698 94.647 1.249 c
+h
+95.293 1.308 m
+95.293 0.904 95.37 0.588 95.528 0.353 c
+95.694 0.118 95.915 0 96.191 0 c
+96.756 0 97.05 0.411 97.072 1.234 c
+97.072 1.69 l
+97.072 2.09 96.987 2.41 96.822 2.645 c
+96.664 2.888 96.447 3.013 96.176 3.013 c
+95.911 3.013 95.694 2.888 95.528 2.645 c
+95.37 2.41 95.293 2.09 95.293 1.69 c
+h
+100.203 2.881 m
+100.114 2.899 100.016 2.91 99.909 2.91 c
+99.574 2.91 99.339 2.726 99.204 2.366 c
+99.204 -0.485 l
+98.557 -0.485 l
+98.557 3.498 l
+99.189 3.498 l
+99.204 3.087 l
+99.38 3.41 99.622 3.571 99.939 3.571 c
+100.045 3.571 100.133 3.549 100.203 3.513 c
+h
+102.011 0.602 m
+102.732 3.498 l
+103.422 3.498 l
+102.129 -1.044 l
+102.03 -1.386 101.886 -1.646 101.702 -1.823 c
+101.526 -1.999 101.324 -2.087 101.099 -2.087 c
+101.012 -2.087 100.897 -2.066 100.762 -2.029 c
+100.762 -1.485 l
+100.908 -1.5 l
+101.092 -1.5 101.24 -1.455 101.35 -1.368 c
+101.456 -1.279 101.544 -1.121 101.614 -0.897 c
+101.732 -0.456 l
+100.571 3.498 l
+101.276 3.498 l
+h
+105.641 1.69 m
+105.641 2.296 105.752 2.763 105.98 3.087 c
+106.215 3.41 106.542 3.571 106.965 3.571 c
+107.347 3.571 107.644 3.413 107.861 3.101 c
+107.861 5.159 l
+108.508 5.159 l
+108.508 -0.485 l
+107.92 -0.485 l
+107.876 -0.059 l
+107.67 -0.393 107.365 -0.559 106.965 -0.559 c
+106.553 -0.559 106.229 -0.405 105.994 -0.088 c
+105.759 0.235 105.641 0.69 105.641 1.278 c
+h
+106.289 1.308 m
+106.289 0.866 106.351 0.536 106.479 0.323 c
+106.616 0.118 106.836 0.014 107.141 0.014 c
+107.464 0.014 107.703 0.176 107.861 0.5 c
+107.861 2.513 l
+107.692 2.825 107.453 2.983 107.141 2.983 c
+106.836 2.983 106.616 2.881 106.479 2.675 c
+106.351 2.469 106.289 2.146 106.289 1.705 c
+h
+109.375 1.69 m
+109.375 2.267 109.511 2.723 109.787 3.057 c
+110.069 3.399 110.441 3.571 110.904 3.571 c
+111.363 3.571 111.731 3.403 112.006 3.072 c
+112.289 2.748 112.436 2.3 112.448 1.734 c
+112.448 1.308 l
+112.448 0.738 112.304 0.282 112.021 -0.059 c
+111.746 -0.393 111.378 -0.559 110.919 -0.559 c
+110.456 -0.559 110.084 -0.397 109.801 -0.074 c
+109.526 0.257 109.383 0.698 109.375 1.249 c
+h
+110.022 1.308 m
+110.022 0.904 110.099 0.588 110.257 0.353 c
+110.422 0.118 110.643 0 110.919 0 c
+111.484 0 111.779 0.411 111.8 1.234 c
+111.8 1.69 l
+111.8 2.09 111.716 2.41 111.551 2.645 c
+111.393 2.888 111.176 3.013 110.904 3.013 c
+110.639 3.013 110.422 2.888 110.257 2.645 c
+110.099 2.41 110.022 2.09 110.022 1.69 c
+h
+116.255 0.661 m
+116.857 3.498 l
+117.504 3.498 l
+116.519 -0.485 l
+116.004 -0.485 l
+115.226 2.366 l
+114.476 -0.485 l
+113.947 -0.485 l
+112.991 3.498 l
+113.623 3.498 l
+114.241 0.735 l
+114.975 3.498 l
+115.49 3.498 l
+h
+118.798 3.498 m
+118.812 3.057 l
+119.066 3.399 119.389 3.571 119.782 3.571 c
+120.488 3.571 120.845 3.101 120.855 2.16 c
+120.855 -0.485 l
+120.209 -0.485 l
+120.209 2.131 l
+120.209 2.443 120.153 2.664 120.047 2.792 c
+119.937 2.917 119.782 2.983 119.576 2.983 c
+119.418 2.983 119.272 2.929 119.135 2.822 c
+119.006 2.711 118.904 2.576 118.827 2.41 c
+118.827 -0.485 l
+118.18 -0.485 l
+118.18 3.498 l
+h
+124.251 -0.485 -0.647 3.983 re
+124.295 4.542 m
+124.295 4.432 124.265 4.34 124.207 4.263 c
+124.148 4.193 124.052 4.159 123.927 4.159 c
+123.81 4.159 123.714 4.193 123.648 4.263 c
+123.59 4.34 123.56 4.432 123.56 4.542 c
+123.56 4.659 123.59 4.751 123.648 4.821 c
+123.714 4.898 123.81 4.939 123.927 4.939 c
+124.052 4.939 124.148 4.898 124.207 4.821 c
+124.265 4.74 124.295 4.648 124.295 4.542 c
+125.882 3.498 m
+125.897 3.057 l
+126.15 3.399 126.474 3.571 126.867 3.571 c
+127.573 3.571 127.929 3.101 127.941 2.16 c
+127.941 -0.485 l
+127.293 -0.485 l
+127.293 2.131 l
+127.293 2.443 127.238 2.664 127.132 2.792 c
+127.021 2.917 126.867 2.983 126.661 2.983 c
+126.503 2.983 126.356 2.929 126.22 2.822 c
+126.092 2.711 125.989 2.576 125.911 2.41 c
+125.911 -0.485 l
+125.265 -0.485 l
+125.265 3.498 l
+h
+129.763 4.453 m
+129.763 3.498 l
+130.366 3.498 l
+130.366 2.969 l
+129.763 2.969 l
+129.763 0.5 l
+129.763 0.341 129.784 0.224 129.836 0.147 c
+129.895 0.066 129.983 0.029 130.101 0.029 c
+130.189 0.029 130.277 0.043 130.366 0.073 c
+130.366 -0.485 l
+130.218 -0.533 130.064 -0.559 129.91 -0.559 c
+129.653 -0.559 129.458 -0.468 129.322 -0.279 c
+129.182 -0.096 129.116 0.165 129.116 0.5 c
+129.116 2.969 l
+128.513 2.969 l
+128.513 3.498 l
+129.116 3.498 l
+129.116 4.453 l
+h
+130.924 1.69 m
+130.924 2.267 131.06 2.723 131.336 3.057 c
+131.619 3.399 131.99 3.571 132.452 3.571 c
+132.912 3.571 133.279 3.403 133.555 3.072 c
+133.838 2.748 133.985 2.3 133.996 1.734 c
+133.996 1.308 l
+133.996 0.738 133.853 0.282 133.57 -0.059 c
+133.294 -0.393 132.926 -0.559 132.467 -0.559 c
+132.005 -0.559 131.633 -0.397 131.351 -0.074 c
+131.075 0.257 130.931 0.698 130.924 1.249 c
+h
+131.571 1.308 m
+131.571 0.904 131.648 0.588 131.806 0.353 c
+131.972 0.118 132.192 0 132.467 0 c
+133.034 0 133.327 0.411 133.35 1.234 c
+133.35 1.69 l
+133.35 2.09 133.265 2.41 133.1 2.645 c
+132.942 2.888 132.725 3.013 132.452 3.013 c
+132.188 3.013 131.972 2.888 131.806 2.645 c
+131.648 2.41 131.571 2.09 131.571 1.69 c
+h
+136.421 1.69 m
+136.421 2.296 136.531 2.763 136.76 3.087 c
+136.995 3.41 137.322 3.571 137.744 3.571 c
+138.127 3.571 138.424 3.413 138.641 3.101 c
+138.641 5.159 l
+139.288 5.159 l
+139.288 -0.485 l
+138.7 -0.485 l
+138.656 -0.059 l
+138.45 -0.393 138.145 -0.559 137.744 -0.559 c
+137.333 -0.559 137.009 -0.405 136.774 -0.088 c
+136.539 0.235 136.421 0.69 136.421 1.278 c
+h
+137.069 1.308 m
+137.069 0.866 137.131 0.536 137.26 0.323 c
+137.395 0.118 137.616 0.014 137.921 0.014 c
+138.244 0.014 138.483 0.176 138.641 0.5 c
+138.641 2.513 l
+138.472 2.825 138.233 2.983 137.921 2.983 c
+137.616 2.983 137.395 2.881 137.26 2.675 c
+137.131 2.469 137.069 2.146 137.069 1.705 c
+h
+140.993 -0.485 -0.647 3.983 re
+141.037 4.542 m
+141.037 4.432 141.007 4.34 140.949 4.263 c
+140.89 4.193 140.795 4.159 140.67 4.159 c
+140.552 4.159 140.456 4.193 140.39 4.263 c
+140.331 4.34 140.302 4.432 140.302 4.542 c
+140.302 4.659 140.331 4.751 140.39 4.821 c
+140.456 4.898 140.552 4.939 140.67 4.939 c
+140.795 4.939 140.89 4.898 140.949 4.821 c
+141.007 4.74 141.037 4.648 141.037 4.542 c
+143.654 2.881 m
+143.565 2.899 143.466 2.91 143.359 2.91 c
+143.025 2.91 142.79 2.726 142.654 2.366 c
+142.654 -0.485 l
+142.007 -0.485 l
+142.007 3.498 l
+142.64 3.498 l
+142.654 3.087 l
+142.831 3.41 143.073 3.571 143.388 3.571 c
+143.496 3.571 143.583 3.549 143.654 3.513 c
+h
+145.653 -0.559 m
+145.152 -0.559 144.77 -0.412 144.506 -0.118 c
+144.242 0.176 144.109 0.61 144.109 1.19 c
+144.109 1.66 l
+144.109 2.256 144.234 2.723 144.491 3.057 c
+144.756 3.399 145.116 3.571 145.579 3.571 c
+146.039 3.571 146.38 3.417 146.608 3.116 c
+146.843 2.822 146.964 2.358 146.975 1.734 c
+146.975 1.308 l
+144.756 1.308 l
+144.756 1.219 l
+144.756 0.786 144.833 0.473 144.991 0.278 c
+145.156 0.091 145.388 0 145.682 0 c
+145.877 0 146.05 0.033 146.197 0.103 c
+146.343 0.18 146.48 0.297 146.608 0.455 c
+146.946 0.043 l
+146.659 -0.36 146.229 -0.559 145.653 -0.559 c
+145.579 3.013 m
+145.304 3.013 145.102 2.917 144.976 2.734 c
+144.848 2.547 144.774 2.256 144.756 1.866 c
+146.328 1.866 l
+146.328 1.955 l
+146.307 2.337 146.241 2.605 146.123 2.763 c
+146.006 2.929 145.821 3.013 145.579 3.013 c
+149.136 0 m
+149.349 0 149.522 0.062 149.651 0.191 c
+149.786 0.326 149.86 0.517 149.871 0.764 c
+150.488 0.764 l
+150.467 0.382 150.33 0.062 150.077 -0.192 c
+149.819 -0.437 149.507 -0.559 149.136 -0.559 c
+148.643 -0.559 148.269 -0.408 148.005 -0.103 c
+147.747 0.209 147.622 0.675 147.622 1.294 c
+147.622 1.734 l
+147.622 2.329 147.747 2.786 148.005 3.101 c
+148.269 3.413 148.643 3.571 149.136 3.571 c
+149.537 3.571 149.856 3.439 150.091 3.175 c
+150.334 2.917 150.467 2.572 150.488 2.131 c
+149.871 2.131 l
+149.849 2.425 149.775 2.645 149.651 2.792 c
+149.533 2.94 149.36 3.013 149.136 3.013 c
+148.842 3.013 148.626 2.913 148.489 2.719 c
+148.35 2.532 148.277 2.223 148.269 1.793 c
+148.269 1.278 l
+148.269 0.808 148.335 0.473 148.475 0.278 c
+148.622 0.091 148.842 0 149.136 0 c
+152.09 4.453 m
+152.09 3.498 l
+152.693 3.498 l
+152.693 2.969 l
+152.09 2.969 l
+152.09 0.5 l
+152.09 0.341 152.113 0.224 152.165 0.147 c
+152.223 0.066 152.311 0.029 152.429 0.029 c
+152.517 0.029 152.605 0.043 152.693 0.073 c
+152.693 -0.485 l
+152.547 -0.533 152.392 -0.559 152.238 -0.559 c
+151.98 -0.559 151.785 -0.468 151.65 -0.279 c
+151.51 -0.096 151.444 0.165 151.444 0.5 c
+151.444 2.969 l
+150.841 2.969 l
+150.841 3.498 l
+151.444 3.498 l
+151.444 4.453 l
+h
+153.252 1.69 m
+153.252 2.267 153.388 2.723 153.663 3.057 c
+153.946 3.399 154.318 3.571 154.781 3.571 c
+155.24 3.571 155.608 3.403 155.883 3.072 c
+156.166 2.748 156.313 2.3 156.324 1.734 c
+156.324 1.308 l
+156.324 0.738 156.181 0.282 155.897 -0.059 c
+155.622 -0.393 155.255 -0.559 154.795 -0.559 c
+154.332 -0.559 153.961 -0.397 153.678 -0.074 c
+153.403 0.257 153.26 0.698 153.252 1.249 c
+h
+153.898 1.308 m
+153.898 0.904 153.976 0.588 154.134 0.353 c
+154.299 0.118 154.519 0 154.795 0 c
+155.361 0 155.655 0.411 155.677 1.234 c
+155.677 1.69 l
+155.677 2.09 155.593 2.41 155.427 2.645 c
+155.269 2.888 155.053 3.013 154.781 3.013 c
+154.516 3.013 154.299 2.888 154.134 2.645 c
+153.976 2.41 153.898 2.09 153.898 1.69 c
+h
+158.808 2.881 m
+158.72 2.899 158.621 2.91 158.515 2.91 c
+158.18 2.91 157.944 2.726 157.809 2.366 c
+157.809 -0.485 l
+157.162 -0.485 l
+157.162 3.498 l
+157.794 3.498 l
+157.809 3.087 l
+157.985 3.41 158.228 3.571 158.544 3.571 c
+158.65 3.571 158.739 3.549 158.808 3.513 c
+h
+160.616 0.602 m
+161.337 3.498 l
+162.027 3.498 l
+160.734 -1.044 l
+160.634 -1.386 160.491 -1.646 160.308 -1.823 c
+160.131 -1.999 159.929 -2.087 159.705 -2.087 c
+159.616 -2.087 159.502 -2.066 159.367 -2.029 c
+159.367 -1.485 l
+159.514 -1.5 l
+159.697 -1.5 159.845 -1.455 159.955 -1.368 c
+160.061 -1.279 160.15 -1.121 160.219 -0.897 c
+160.337 -0.456 l
+159.176 3.498 l
+159.882 3.498 l
+h
+165.231 4.453 m
+165.231 3.498 l
+165.835 3.498 l
+165.835 2.969 l
+165.231 2.969 l
+165.231 0.5 l
+165.231 0.341 165.254 0.224 165.305 0.147 c
+165.364 0.066 165.453 0.029 165.57 0.029 c
+165.658 0.029 165.746 0.043 165.835 0.073 c
+165.835 -0.485 l
+165.688 -0.533 165.533 -0.559 165.378 -0.559 c
+165.121 -0.559 164.927 -0.468 164.79 -0.279 c
+164.651 -0.096 164.585 0.165 164.585 0.5 c
+164.585 2.969 l
+163.982 2.969 l
+163.982 3.498 l
+164.585 3.498 l
+164.585 4.453 l
+h
+168.245 2.881 m
+168.157 2.899 168.057 2.91 167.951 2.91 c
+167.616 2.91 167.381 2.726 167.246 2.366 c
+167.246 -0.485 l
+166.598 -0.485 l
+166.598 3.498 l
+167.231 3.498 l
+167.246 3.087 l
+167.422 3.41 167.664 3.571 167.98 3.571 c
+168.087 3.571 168.175 3.549 168.245 3.513 c
+h
+170.244 -0.559 m
+169.744 -0.559 169.362 -0.412 169.098 -0.118 c
+168.833 0.176 168.701 0.61 168.701 1.19 c
+168.701 1.66 l
+168.701 2.256 168.825 2.723 169.083 3.057 c
+169.347 3.399 169.708 3.571 170.17 3.571 c
+170.629 3.571 170.972 3.417 171.2 3.116 c
+171.435 2.822 171.556 2.358 171.567 1.734 c
+171.567 1.308 l
+169.347 1.308 l
+169.347 1.219 l
+169.347 0.786 169.424 0.473 169.582 0.278 c
+169.748 0.091 169.979 0 170.274 0 c
+170.469 0 170.641 0.033 170.788 0.103 c
+170.935 0.18 171.071 0.297 171.2 0.455 c
+171.537 0.043 l
+171.251 -0.36 170.821 -0.559 170.244 -0.559 c
+170.17 3.013 m
+169.895 3.013 169.692 2.917 169.568 2.734 c
+169.439 2.547 169.366 2.256 169.347 1.866 c
+170.92 1.866 l
+170.92 1.955 l
+170.898 2.337 170.832 2.605 170.714 2.763 c
+170.597 2.929 170.413 3.013 170.17 3.013 c
+173.772 -0.559 m
+173.272 -0.559 172.89 -0.412 172.626 -0.118 c
+172.36 0.176 172.229 0.61 172.229 1.19 c
+172.229 1.66 l
+172.229 2.256 172.353 2.723 172.611 3.057 c
+172.875 3.399 173.235 3.571 173.698 3.571 c
+174.157 3.571 174.5 3.417 174.727 3.116 c
+174.962 2.822 175.084 2.358 175.095 1.734 c
+175.095 1.308 l
+172.875 1.308 l
+172.875 1.219 l
+172.875 0.786 172.952 0.473 173.11 0.278 c
+173.276 0.091 173.507 0 173.802 0 c
+173.996 0 174.168 0.033 174.315 0.103 c
+174.463 0.18 174.598 0.297 174.727 0.455 c
+175.065 0.043 l
+174.779 -0.36 174.349 -0.559 173.772 -0.559 c
+173.698 3.013 m
+173.422 3.013 173.22 2.917 173.096 2.734 c
+172.967 2.547 172.894 2.256 172.875 1.866 c
+174.448 1.866 l
+174.448 1.955 l
+174.426 2.337 174.359 2.605 174.242 2.763 c
+174.124 2.929 173.941 3.013 173.698 3.013 c
+175.918 -0.133 m
+175.918 -0.015 175.951 0.081 176.021 0.161 c
+176.087 0.238 176.19 0.278 176.329 0.278 c
+176.476 0.278 176.583 0.238 176.653 0.161 c
+176.73 0.081 176.771 -0.015 176.771 -0.133 c
+176.771 -0.243 176.73 -0.335 176.653 -0.412 c
+176.583 -0.489 176.476 -0.53 176.329 -0.53 c
+176.19 -0.53 176.087 -0.489 176.021 -0.412 c
+175.951 -0.335 175.918 -0.243 175.918 -0.133 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 261.208 234.667 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 254.3733 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.683 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.485 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.941 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.028 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.596 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.244 1.933 5.376 2.051 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.635 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.177 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.116 l
+14.494 3.116 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.456 14.552 -0.515 c
+14.589 -0.566 14.645 -0.611 14.714 -0.647 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.688 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.603 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.279 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.085 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.456 c
+13.586 -0.36 13.582 -0.264 13.582 -0.177 c
+h
+24.133 1.469 m
+24.033 1.477 23.931 1.488 23.823 1.5 c
+23.713 1.517 23.592 1.529 23.456 1.529 c
+23.28 1.529 23.121 1.488 22.986 1.411 c
+22.846 1.341 22.728 1.242 22.633 1.117 c
+22.545 0.989 22.475 0.841 22.427 0.676 c
+22.387 0.507 22.369 0.33 22.369 0.147 c
+22.369 -1.264 l
+21.471 -1.264 l
+21.471 0.985 l
+21.471 1.11 21.461 1.234 21.442 1.352 c
+21.432 1.477 21.417 1.595 21.398 1.705 c
+21.388 1.822 21.373 1.918 21.355 1.999 c
+21.332 2.087 21.314 2.161 21.296 2.219 c
+22.177 2.219 l
+22.185 2.168 22.196 2.117 22.207 2.057 c
+22.225 1.999 22.24 1.933 22.251 1.866 c
+22.269 1.808 22.284 1.741 22.296 1.675 c
+22.302 1.606 22.313 1.543 22.325 1.484 c
+22.339 1.484 l
+22.375 1.602 22.427 1.708 22.486 1.808 c
+22.552 1.903 22.633 1.988 22.722 2.057 c
+22.809 2.124 22.913 2.179 23.03 2.219 c
+23.155 2.256 23.302 2.278 23.471 2.278 c
+23.596 2.278 23.713 2.271 23.823 2.263 c
+23.942 2.252 24.044 2.238 24.133 2.219 c
+h
+26.738 -1.323 m
+26.481 -1.323 26.253 -1.286 26.047 -1.22 c
+25.841 -1.143 25.665 -1.029 25.518 -0.882 c
+25.371 -0.728 25.253 -0.536 25.165 -0.309 c
+25.084 -0.085 25.047 0.18 25.047 0.484 c
+25.047 0.816 25.091 1.095 25.18 1.323 c
+25.275 1.558 25.404 1.741 25.562 1.881 c
+25.727 2.017 25.915 2.117 26.121 2.175 c
+26.327 2.242 26.535 2.278 26.753 2.278 c
+27.025 2.278 27.26 2.227 27.458 2.131 c
+27.664 2.043 27.829 1.911 27.958 1.735 c
+28.094 1.565 28.193 1.359 28.252 1.117 c
+28.318 0.881 28.355 0.617 28.355 0.324 c
+28.355 0.309 l
+25.988 0.309 l
+25.988 0.162 26.003 0.022 26.032 -0.103 c
+26.069 -0.231 26.124 -0.345 26.194 -0.441 c
+26.26 -0.53 26.344 -0.599 26.444 -0.647 c
+26.539 -0.698 26.653 -0.721 26.782 -0.721 c
+26.936 -0.721 27.076 -0.688 27.194 -0.617 c
+27.318 -0.551 27.407 -0.449 27.458 -0.309 c
+28.296 -0.382 l
+28.266 -0.482 28.212 -0.588 28.135 -0.706 c
+28.054 -0.816 27.95 -0.919 27.825 -1.014 c
+27.708 -1.103 27.553 -1.176 27.37 -1.235 c
+27.194 -1.294 26.98 -1.323 26.738 -1.323 c
+26.738 1.705 m
+26.649 1.705 26.562 1.691 26.473 1.66 c
+26.385 1.631 26.304 1.58 26.238 1.514 c
+26.169 1.444 26.109 1.356 26.061 1.249 c
+26.021 1.139 26.003 1.014 26.003 0.867 c
+27.472 0.867 l
+27.472 1.003 27.447 1.124 27.399 1.234 c
+27.359 1.341 27.304 1.429 27.237 1.5 c
+27.179 1.565 27.106 1.617 27.017 1.646 c
+26.929 1.683 26.834 1.705 26.738 1.705 c
+32.342 -0.25 m
+32.342 -0.419 32.301 -0.57 32.224 -0.706 c
+32.154 -0.834 32.052 -0.948 31.916 -1.044 c
+31.787 -1.132 31.625 -1.202 31.43 -1.249 c
+31.243 -1.297 31.027 -1.323 30.784 -1.323 c
+30.556 -1.323 30.358 -1.309 30.181 -1.279 c
+30.005 -1.249 29.847 -1.202 29.71 -1.132 c
+29.571 -1.055 29.461 -0.956 29.373 -0.838 c
+29.284 -0.721 29.215 -0.574 29.167 -0.397 c
+29.976 -0.279 l
+29.994 -0.379 30.023 -0.456 30.063 -0.515 c
+30.111 -0.574 30.17 -0.617 30.24 -0.647 c
+30.306 -0.676 30.387 -0.702 30.475 -0.721 c
+30.564 -0.732 30.666 -0.735 30.784 -0.735 c
+30.879 -0.735 30.975 -0.732 31.063 -0.721 c
+31.152 -0.702 31.229 -0.676 31.298 -0.647 c
+31.364 -0.617 31.416 -0.58 31.445 -0.53 c
+31.482 -0.482 31.504 -0.419 31.504 -0.339 c
+31.504 -0.243 31.474 -0.169 31.416 -0.118 c
+31.364 -0.07 31.298 -0.029 31.21 0 c
+31.122 0.037 31.011 0.066 30.886 0.088 c
+30.769 0.118 30.637 0.147 30.489 0.176 c
+30.35 0.213 30.211 0.253 30.063 0.294 c
+29.924 0.341 29.799 0.405 29.681 0.484 c
+29.571 0.562 29.483 0.661 29.417 0.779 c
+29.347 0.896 29.313 1.047 29.313 1.234 c
+29.313 1.389 29.344 1.532 29.402 1.66 c
+29.468 1.797 29.564 1.911 29.681 1.999 c
+29.806 2.087 29.964 2.153 30.152 2.205 c
+30.335 2.252 30.549 2.278 30.784 2.278 c
+30.967 2.278 31.144 2.256 31.313 2.219 c
+31.478 2.19 31.625 2.135 31.754 2.057 c
+31.879 1.988 31.989 1.889 32.077 1.764 c
+32.166 1.646 32.224 1.502 32.253 1.338 c
+31.46 1.264 l
+31.438 1.341 31.408 1.404 31.372 1.455 c
+31.331 1.514 31.283 1.558 31.225 1.587 c
+31.173 1.624 31.111 1.65 31.034 1.66 c
+30.953 1.668 30.872 1.675 30.784 1.675 c
+30.567 1.675 30.406 1.646 30.298 1.587 c
+30.188 1.536 30.137 1.448 30.137 1.323 c
+30.137 1.242 30.155 1.18 30.196 1.132 c
+30.244 1.08 30.306 1.043 30.387 1.014 c
+30.475 0.985 30.57 0.955 30.681 0.926 c
+30.788 0.904 30.909 0.881 31.048 0.852 c
+31.202 0.823 31.361 0.783 31.519 0.735 c
+31.673 0.683 31.813 0.621 31.931 0.544 c
+32.048 0.463 32.143 0.36 32.224 0.235 c
+32.301 0.106 32.342 -0.056 32.342 -0.25 c
+34.859 -1.323 m
+34.602 -1.323 34.374 -1.286 34.169 -1.22 c
+33.963 -1.143 33.786 -1.029 33.639 -0.882 c
+33.492 -0.728 33.375 -0.536 33.286 -0.309 c
+33.205 -0.085 33.169 0.18 33.169 0.484 c
+33.169 0.816 33.213 1.095 33.301 1.323 c
+33.396 1.558 33.525 1.741 33.683 1.881 c
+33.849 2.017 34.036 2.117 34.242 2.175 c
+34.447 2.242 34.657 2.278 34.874 2.278 c
+35.146 2.278 35.381 2.227 35.58 2.131 c
+35.785 2.043 35.95 1.911 36.079 1.735 c
+36.215 1.565 36.314 1.359 36.373 1.117 c
+36.44 0.881 36.476 0.617 36.476 0.324 c
+36.476 0.309 l
+34.109 0.309 l
+34.109 0.162 34.124 0.022 34.154 -0.103 c
+34.19 -0.231 34.246 -0.345 34.315 -0.441 c
+34.381 -0.53 34.466 -0.599 34.565 -0.647 c
+34.661 -0.698 34.774 -0.721 34.903 -0.721 c
+35.058 -0.721 35.197 -0.688 35.314 -0.617 c
+35.44 -0.551 35.528 -0.449 35.58 -0.309 c
+36.417 -0.382 l
+36.388 -0.482 36.332 -0.588 36.255 -0.706 c
+36.174 -0.816 36.072 -0.919 35.947 -1.014 c
+35.829 -1.103 35.675 -1.176 35.491 -1.235 c
+35.314 -1.294 35.102 -1.323 34.859 -1.323 c
+34.859 1.705 m
+34.771 1.705 34.682 1.691 34.595 1.66 c
+34.506 1.631 34.425 1.58 34.36 1.514 c
+34.29 1.444 34.231 1.356 34.183 1.249 c
+34.142 1.139 34.124 1.014 34.124 0.867 c
+35.594 0.867 l
+35.594 1.003 35.568 1.124 35.52 1.234 c
+35.48 1.341 35.425 1.429 35.359 1.5 c
+35.3 1.565 35.227 1.617 35.138 1.646 c
+35.05 1.683 34.955 1.705 34.859 1.705 c
+37.968 1.602 m
+37.424 1.602 l
+37.424 2.219 l
+38.012 2.219 l
+38.291 3.116 l
+38.865 3.116 l
+38.865 2.219 l
+40.099 2.219 l
+40.099 1.602 l
+38.865 1.602 l
+38.865 -0.103 l
+38.865 -0.324 l
+38.872 -0.393 38.894 -0.456 38.923 -0.515 c
+38.96 -0.566 39.015 -0.611 39.085 -0.647 c
+39.162 -0.676 39.276 -0.691 39.424 -0.691 c
+39.559 -0.691 39.695 -0.688 39.835 -0.676 c
+39.971 -0.658 40.103 -0.632 40.232 -0.603 c
+40.232 -1.205 l
+40.151 -1.216 40.074 -1.231 39.996 -1.249 c
+39.916 -1.261 39.838 -1.268 39.761 -1.279 c
+39.68 -1.286 39.592 -1.294 39.497 -1.294 c
+39.409 -1.301 39.309 -1.309 39.202 -1.309 c
+39.015 -1.309 38.853 -1.294 38.718 -1.264 c
+38.589 -1.228 38.475 -1.183 38.379 -1.132 c
+38.291 -1.085 38.218 -1.025 38.159 -0.956 c
+38.1 -0.879 38.056 -0.802 38.027 -0.721 c
+37.997 -0.632 37.975 -0.544 37.968 -0.456 c
+37.957 -0.36 37.953 -0.264 37.953 -0.177 c
+h
+46.236 -2.631 m
+46.236 3.513 l
+48.162 3.513 l
+48.162 2.896 l
+47.089 2.896 l
+47.089 -2.014 l
+48.162 -2.014 l
+48.162 -2.631 l
+h
+51.193 1.602 m
+51.193 -1.264 l
+50.297 -1.264 l
+50.297 1.602 l
+49.473 1.602 l
+49.473 2.219 l
+50.297 2.219 l
+50.297 2.484 l
+50.297 2.609 50.312 2.741 50.341 2.881 c
+50.378 3.017 50.447 3.135 50.547 3.233 c
+50.653 3.341 50.796 3.428 50.973 3.499 c
+51.149 3.564 51.374 3.601 51.649 3.601 c
+51.862 3.601 52.061 3.59 52.237 3.572 c
+52.413 3.549 52.564 3.532 52.693 3.513 c
+52.693 2.925 l
+52.564 2.944 52.421 2.958 52.267 2.969 c
+52.108 2.976 51.958 2.984 51.81 2.984 c
+51.682 2.984 51.579 2.969 51.502 2.94 c
+51.421 2.911 51.359 2.87 51.311 2.822 c
+51.26 2.77 51.226 2.708 51.208 2.631 c
+51.197 2.561 51.193 2.484 51.193 2.396 c
+51.193 2.219 l
+52.619 2.219 l
+52.619 1.602 l
+h
+55.71 -0.647 m
+56.841 -0.647 l
+56.841 -1.264 l
+53.535 -1.264 l
+53.535 -0.647 l
+54.798 -0.647 l
+54.798 1.602 l
+53.872 1.602 l
+53.872 2.219 l
+55.71 2.219 l
+h
+54.798 3.513 0.912 -0.676 re
+54.798 2.836 m
+59.774 -0.647 m
+60.905 -0.647 l
+60.905 -1.264 l
+57.599 -1.264 l
+57.599 -0.647 l
+58.863 -0.647 l
+58.863 2.896 l
+57.936 2.896 l
+57.936 3.513 l
+59.774 3.513 l
+h
+63.291 -1.323 m
+63.033 -1.323 62.806 -1.286 62.6 -1.22 c
+62.394 -1.143 62.218 -1.029 62.071 -0.882 c
+61.923 -0.728 61.807 -0.536 61.718 -0.309 c
+61.637 -0.085 61.601 0.18 61.601 0.484 c
+61.601 0.816 61.645 1.095 61.732 1.323 c
+61.828 1.558 61.957 1.741 62.115 1.881 c
+62.28 2.017 62.468 2.117 62.673 2.175 c
+62.879 2.242 63.089 2.278 63.305 2.278 c
+63.577 2.278 63.812 2.227 64.011 2.131 c
+64.217 2.043 64.382 1.911 64.51 1.735 c
+64.647 1.565 64.745 1.359 64.805 1.117 c
+64.871 0.881 64.907 0.617 64.907 0.324 c
+64.907 0.309 l
+62.541 0.309 l
+62.541 0.162 62.555 0.022 62.586 -0.103 c
+62.622 -0.231 62.677 -0.345 62.747 -0.441 c
+62.813 -0.53 62.898 -0.599 62.997 -0.647 c
+63.093 -0.698 63.206 -0.721 63.334 -0.721 c
+63.49 -0.721 63.629 -0.688 63.746 -0.617 c
+63.871 -0.551 63.96 -0.449 64.011 -0.309 c
+64.849 -0.382 l
+64.82 -0.482 64.764 -0.588 64.687 -0.706 c
+64.606 -0.816 64.504 -0.919 64.379 -1.014 c
+64.261 -1.103 64.107 -1.176 63.923 -1.235 c
+63.746 -1.294 63.533 -1.323 63.291 -1.323 c
+63.291 1.705 m
+63.203 1.705 63.114 1.691 63.026 1.66 c
+62.938 1.631 62.857 1.58 62.791 1.514 c
+62.721 1.444 62.663 1.356 62.615 1.249 c
+62.574 1.139 62.555 1.014 62.555 0.867 c
+64.026 0.867 l
+64.026 1.003 64 1.124 63.952 1.234 c
+63.912 1.341 63.856 1.429 63.791 1.5 c
+63.731 1.565 63.658 1.617 63.57 1.646 c
+63.482 1.683 63.386 1.705 63.291 1.705 c
+66.16 -2.631 m
+66.16 -2.014 l
+67.234 -2.014 l
+67.234 2.896 l
+66.16 2.896 l
+66.16 3.513 l
+68.086 3.513 l
+68.086 -2.631 l
+h
+f
+Q
+q 1 0 0 1 371.3314 238.8802 cm
+0 0 m
+-0.118 -0.147 l
+-0.452 -0.54 -0.948 -0.735 -1.602 -0.735 c
+-2.183 -0.735 -2.638 -0.544 -2.969 -0.162 c
+-3.293 0.22 -3.461 0.765 -3.469 1.47 c
+-3.469 2.514 l
+-3.469 3.267 -3.322 3.829 -3.027 4.204 c
+-2.726 4.575 -2.275 4.763 -1.675 4.763 c
+-1.168 4.763 -0.771 4.619 -0.484 4.336 c
+-0.191 4.05 -0.029 3.645 0 3.117 c
+-0.675 3.117 l
+-0.698 3.447 -0.79 3.707 -0.955 3.896 c
+-1.113 4.09 -1.348 4.189 -1.66 4.189 c
+-2.054 4.189 -2.337 4.06 -2.514 3.807 c
+-2.69 3.55 -2.786 3.15 -2.792 2.602 c
+-2.792 1.514 l
+-2.792 0.974 -2.69 0.559 -2.484 0.264 c
+-2.278 -0.022 -1.984 -0.162 -1.602 -0.162 c
+-1.242 -0.162 -0.962 -0.073 -0.764 0.103 c
+-0.675 0.191 l
+-0.675 1.426 l
+-1.646 1.426 l
+-1.646 1.999 l
+0 1.999 l
+h
+2.44 -0.735 m
+1.941 -0.735 1.558 -0.588 1.294 -0.294 c
+1.029 0 0.897 0.434 0.897 1.014 c
+0.897 1.484 l
+0.897 2.08 1.022 2.547 1.279 2.882 c
+1.544 3.223 1.904 3.395 2.367 3.395 c
+2.826 3.395 3.167 3.241 3.396 2.94 c
+3.631 2.646 3.753 2.183 3.763 1.558 c
+3.763 1.132 l
+1.544 1.132 l
+1.544 1.043 l
+1.544 0.611 1.621 0.297 1.779 0.103 c
+1.944 -0.085 2.176 -0.176 2.469 -0.176 c
+2.664 -0.176 2.837 -0.143 2.984 -0.073 c
+3.131 0.004 3.267 0.121 3.396 0.279 c
+3.734 -0.133 l
+3.447 -0.536 3.017 -0.735 2.44 -0.735 c
+2.367 2.837 m
+2.091 2.837 1.889 2.741 1.764 2.558 c
+1.636 2.371 1.562 2.08 1.544 1.691 c
+3.117 1.691 l
+3.117 1.779 l
+3.094 2.161 3.028 2.429 2.911 2.587 c
+2.793 2.753 2.61 2.837 2.367 2.837 c
+5.395 4.278 m
+5.395 3.322 l
+5.997 3.322 l
+5.997 2.793 l
+5.395 2.793 l
+5.395 0.324 l
+5.395 0.166 5.417 0.048 5.469 -0.029 c
+5.527 -0.11 5.615 -0.147 5.733 -0.147 c
+5.821 -0.147 5.91 -0.133 5.997 -0.103 c
+5.997 -0.661 l
+5.85 -0.709 5.696 -0.735 5.542 -0.735 c
+5.284 -0.735 5.089 -0.643 4.954 -0.455 c
+4.814 -0.272 4.748 -0.011 4.748 0.324 c
+4.748 2.793 l
+4.145 2.793 l
+4.145 3.322 l
+4.748 3.322 l
+4.748 4.278 l
+h
+8.658 -0.661 m
+8.658 2.793 l
+8.143 2.793 l
+8.143 3.322 l
+8.658 3.322 l
+8.658 3.69 l
+8.665 4.12 8.779 4.453 8.996 4.689 c
+9.22 4.931 9.533 5.056 9.937 5.056 c
+10.084 5.056 10.223 5.035 10.363 4.998 c
+10.51 4.958 10.66 4.902 10.819 4.836 c
+10.701 4.262 l
+10.466 4.388 10.223 4.453 9.981 4.453 c
+9.735 4.453 9.562 4.384 9.467 4.248 c
+9.367 4.12 9.319 3.925 9.319 3.66 c
+9.319 3.322 l
+9.966 3.322 l
+9.966 2.793 l
+9.319 2.793 l
+9.319 -0.661 l
+h
+11.127 -0.661 -0.646 3.983 re
+12.877 -0.661 -0.647 5.644 re
+15.317 -0.735 m
+14.817 -0.735 14.435 -0.588 14.17 -0.294 c
+13.906 0 13.773 0.434 13.773 1.014 c
+13.773 1.484 l
+13.773 2.08 13.898 2.547 14.155 2.882 c
+14.42 3.223 14.78 3.395 15.243 3.395 c
+15.703 3.395 16.044 3.241 16.272 2.94 c
+16.507 2.646 16.629 2.183 16.64 1.558 c
+16.64 1.132 l
+14.42 1.132 l
+14.42 1.043 l
+14.42 0.611 14.497 0.297 14.655 0.103 c
+14.82 -0.085 15.052 -0.176 15.346 -0.176 c
+15.541 -0.176 15.713 -0.143 15.861 -0.073 c
+16.008 0.004 16.143 0.121 16.272 0.279 c
+16.61 -0.133 l
+16.324 -0.536 15.894 -0.735 15.317 -0.735 c
+15.243 2.837 m
+14.967 2.837 14.766 2.741 14.641 2.558 c
+14.512 2.371 14.438 2.08 14.42 1.691 c
+15.993 1.691 l
+15.993 1.779 l
+15.971 2.161 15.904 2.429 15.787 2.587 c
+15.669 2.753 15.486 2.837 15.243 2.837 c
+22.019 1.132 m
+22.019 0.515 21.905 0.048 21.681 -0.264 c
+21.465 -0.58 21.141 -0.735 20.711 -0.735 c
+20.289 -0.735 19.977 -0.555 19.771 -0.191 c
+19.741 -0.661 l
+19.138 -0.661 l
+19.138 4.983 l
+19.786 4.983 l
+19.786 2.882 l
+19.998 3.223 20.307 3.395 20.711 3.395 c
+21.141 3.395 21.465 3.237 21.681 2.925 c
+21.905 2.62 22.019 2.153 22.019 1.529 c
+h
+21.373 1.514 m
+21.373 1.984 21.303 2.315 21.167 2.514 c
+21.039 2.708 20.829 2.807 20.534 2.807 c
+20.201 2.807 19.95 2.624 19.786 2.263 c
+19.786 0.382 l
+19.95 0.019 20.204 -0.162 20.549 -0.162 c
+20.843 -0.162 21.053 -0.058 21.182 0.147 c
+21.307 0.353 21.373 0.669 21.373 1.103 c
+h
+24.9 -0.661 m
+24.86 -0.573 24.835 -0.426 24.827 -0.22 c
+24.592 -0.565 24.297 -0.735 23.945 -0.735 c
+23.582 -0.735 23.298 -0.64 23.092 -0.441 c
+22.894 -0.235 22.799 0.052 22.799 0.426 c
+22.799 0.827 22.934 1.147 23.21 1.382 c
+23.482 1.625 23.857 1.749 24.328 1.749 c
+24.812 1.749 l
+24.812 2.176 l
+24.812 2.411 24.757 2.576 24.65 2.675 c
+24.54 2.782 24.378 2.837 24.166 2.837 c
+23.967 2.837 23.805 2.778 23.68 2.66 c
+23.563 2.543 23.504 2.396 23.504 2.219 c
+22.857 2.219 l
+22.857 2.414 22.916 2.606 23.034 2.793 c
+23.158 2.977 23.32 3.123 23.518 3.234 c
+23.724 3.341 23.952 3.395 24.21 3.395 c
+24.61 3.395 24.915 3.293 25.121 3.087 c
+25.334 2.882 25.448 2.587 25.459 2.205 c
+25.459 0.191 l
+25.459 -0.114 25.496 -0.378 25.577 -0.603 c
+25.577 -0.661 l
+h
+24.033 -0.147 m
+24.199 -0.147 24.349 -0.103 24.489 -0.015 c
+24.636 0.073 24.742 0.183 24.812 0.324 c
+24.812 1.264 l
+24.445 1.264 l
+24.129 1.264 23.886 1.195 23.709 1.058 c
+23.534 0.929 23.445 0.742 23.445 0.5 c
+23.445 0.272 23.489 0.106 23.578 0 c
+23.665 -0.099 23.817 -0.147 24.033 -0.147 c
+27.84 -0.176 m
+28.054 -0.176 28.226 -0.114 28.355 0.015 c
+28.49 0.151 28.565 0.342 28.575 0.588 c
+29.193 0.588 l
+29.17 0.206 29.035 -0.114 28.781 -0.368 c
+28.524 -0.613 28.212 -0.735 27.84 -0.735 c
+27.348 -0.735 26.973 -0.584 26.709 -0.279 c
+26.451 0.033 26.327 0.5 26.327 1.118 c
+26.327 1.558 l
+26.327 2.153 26.451 2.61 26.709 2.925 c
+26.973 3.237 27.348 3.395 27.84 3.395 c
+28.241 3.395 28.561 3.263 28.796 2.999 c
+29.038 2.741 29.17 2.396 29.193 1.955 c
+28.575 1.955 l
+28.553 2.249 28.48 2.469 28.355 2.616 c
+28.237 2.764 28.064 2.837 27.84 2.837 c
+27.547 2.837 27.329 2.738 27.193 2.543 c
+27.054 2.356 26.98 2.047 26.973 1.617 c
+26.973 1.103 l
+26.973 0.632 27.039 0.297 27.179 0.103 c
+27.326 -0.085 27.547 -0.176 27.84 -0.176 c
+30.942 1.161 m
+30.604 0.765 l
+30.604 -0.661 l
+29.942 -0.661 l
+29.942 4.983 l
+30.604 4.983 l
+30.604 1.602 l
+31.838 3.322 l
+32.617 3.322 l
+31.353 1.661 l
+32.779 -0.661 l
+32.029 -0.661 l
+h
+35.351 -0.661 m
+35.351 2.793 l
+34.822 2.793 l
+34.822 3.322 l
+35.351 3.322 l
+35.351 3.778 l
+35.351 4.178 35.447 4.491 35.646 4.719 c
+35.852 4.943 36.13 5.056 36.484 5.056 c
+36.619 5.056 36.752 5.035 36.88 4.998 c
+36.851 4.453 l
+36.752 4.472 36.652 4.484 36.557 4.484 c
+36.182 4.484 35.998 4.218 35.998 3.69 c
+35.998 3.322 l
+36.675 3.322 l
+36.675 2.793 l
+35.998 2.793 l
+35.998 -0.661 l
+h
+39.085 2.705 m
+38.996 2.723 38.898 2.734 38.791 2.734 c
+38.456 2.734 38.221 2.55 38.086 2.19 c
+38.086 -0.661 l
+37.439 -0.661 l
+37.439 3.322 l
+38.071 3.322 l
+38.086 2.911 l
+38.262 3.234 38.504 3.395 38.821 3.395 c
+38.927 3.395 39.015 3.373 39.085 3.337 c
+h
+39.526 1.514 m
+39.526 2.091 39.662 2.547 39.937 2.882 c
+40.22 3.223 40.592 3.395 41.055 3.395 c
+41.514 3.395 41.882 3.227 42.157 2.896 c
+42.44 2.572 42.587 2.124 42.599 1.558 c
+42.599 1.132 l
+42.599 0.563 42.455 0.106 42.171 -0.235 c
+41.896 -0.569 41.529 -0.735 41.069 -0.735 c
+40.606 -0.735 40.235 -0.573 39.952 -0.25 c
+39.677 0.081 39.534 0.522 39.526 1.073 c
+h
+40.172 1.132 m
+40.172 0.728 40.25 0.412 40.408 0.177 c
+40.573 -0.058 40.794 -0.176 41.069 -0.176 c
+41.635 -0.176 41.93 0.235 41.951 1.058 c
+41.951 1.514 l
+41.951 1.914 41.867 2.234 41.701 2.469 c
+41.543 2.712 41.327 2.837 41.055 2.837 c
+40.79 2.837 40.573 2.712 40.408 2.469 c
+40.25 2.234 40.172 1.914 40.172 1.514 c
+h
+44.053 3.322 m
+44.068 2.955 l
+44.311 3.248 44.631 3.395 45.024 3.395 c
+45.464 3.395 45.773 3.198 45.949 2.807 c
+46.203 3.198 46.552 3.395 46.993 3.395 c
+47.728 3.395 48.103 2.932 48.124 2.014 c
+48.124 -0.661 l
+47.478 -0.661 l
+47.478 1.955 l
+47.478 2.249 47.423 2.462 47.316 2.602 c
+47.218 2.738 47.044 2.807 46.802 2.807 c
+46.603 2.807 46.441 2.727 46.317 2.572 c
+46.2 2.425 46.129 2.234 46.111 1.999 c
+46.111 -0.661 l
+45.45 -0.661 l
+45.45 1.984 l
+45.45 2.532 45.229 2.807 44.789 2.807 c
+44.454 2.807 44.219 2.646 44.083 2.323 c
+44.083 -0.661 l
+43.436 -0.661 l
+43.436 3.322 l
+h
+f
+Q
+q 1 0 0 1 423.8077 239.2923 cm
+0 0 m
+0 0.087 -0.044 0.165 -0.133 0.235 c
+-0.221 0.312 -0.408 0.415 -0.691 0.544 c
+-1.125 0.72 -1.422 0.9 -1.588 1.088 c
+-1.746 1.271 -1.823 1.502 -1.823 1.778 c
+-1.823 2.119 -1.702 2.403 -1.455 2.63 c
+-1.202 2.865 -0.864 2.983 -0.441 2.983 c
+-0.011 2.983 0.338 2.869 0.602 2.645 c
+0.866 2.418 0.999 2.117 0.999 1.734 c
+-0.044 1.734 l
+-0.044 2.057 -0.184 2.219 -0.456 2.219 c
+-0.566 2.219 -0.655 2.183 -0.721 2.117 c
+-0.79 2.046 -0.824 1.947 -0.824 1.822 c
+-0.824 1.734 -0.786 1.653 -0.706 1.587 c
+-0.628 1.529 -0.449 1.433 -0.162 1.308 c
+0.268 1.15 0.565 0.974 0.735 0.779 c
+0.911 0.591 0.999 0.341 0.999 0.029 c
+0.999 -0.324 0.866 -0.611 0.602 -0.823 c
+0.338 -1.04 -0.011 -1.147 -0.441 -1.147 c
+-0.736 -1.147 -0.996 -1.092 -1.22 -0.985 c
+-1.449 -0.867 -1.625 -0.706 -1.75 -0.5 c
+-1.867 -0.294 -1.926 -0.074 -1.926 0.161 c
+-0.941 0.161 l
+-0.941 -0.026 -0.904 -0.162 -0.824 -0.25 c
+-0.736 -0.339 -0.603 -0.383 -0.427 -0.383 c
+-0.144 -0.383 0 -0.258 0 0 c
+2.866 3.881 m
+2.866 2.91 l
+3.395 2.91 l
+3.395 2.117 l
+2.866 2.117 l
+2.866 0.147 l
+2.866 -0.011 2.884 -0.118 2.925 -0.177 c
+2.973 -0.235 3.057 -0.265 3.174 -0.265 c
+3.281 -0.265 3.366 -0.258 3.424 -0.235 c
+3.424 -1.044 l
+3.248 -1.11 3.057 -1.147 2.851 -1.147 c
+2.175 -1.147 1.83 -0.761 1.822 0.014 c
+1.822 2.117 l
+1.367 2.117 l
+1.367 2.91 l
+1.822 2.91 l
+1.822 3.881 l
+h
+5.85 -1.073 m
+5.82 -1.015 5.791 -0.912 5.761 -0.765 c
+5.574 -1.022 5.325 -1.147 5.012 -1.147 c
+4.677 -1.147 4.398 -1.04 4.174 -0.823 c
+3.958 -0.599 3.85 -0.309 3.85 0.043 c
+3.85 0.455 3.983 0.771 4.247 0.999 c
+4.512 1.234 4.895 1.352 5.394 1.352 c
+5.718 1.352 l
+5.718 1.675 l
+5.718 1.851 5.681 1.973 5.614 2.042 c
+5.556 2.119 5.468 2.16 5.35 2.16 c
+5.093 2.16 4.968 2.006 4.968 1.705 c
+3.924 1.705 l
+3.924 2.076 4.06 2.381 4.336 2.616 c
+4.608 2.859 4.957 2.983 5.379 2.983 c
+5.82 2.983 6.158 2.865 6.393 2.63 c
+6.636 2.403 6.761 2.08 6.761 1.66 c
+6.761 -0.206 l
+6.761 -0.551 6.809 -0.82 6.908 -1.015 c
+6.908 -1.073 l
+h
+5.247 -0.324 m
+5.354 -0.324 5.446 -0.306 5.526 -0.265 c
+5.614 -0.217 5.677 -0.158 5.718 -0.088 c
+5.718 0.735 l
+5.468 0.735 l
+5.291 0.735 5.148 0.675 5.041 0.558 c
+4.942 0.448 4.895 0.301 4.895 0.118 c
+4.895 -0.177 5.012 -0.324 5.247 -0.324 c
+7.378 1.043 m
+7.378 1.69 7.496 2.175 7.731 2.499 c
+7.966 2.822 8.297 2.983 8.731 2.983 c
+9.084 2.983 9.356 2.84 9.554 2.557 c
+9.598 2.91 l
+10.539 2.91 l
+10.539 -1.073 l
+10.539 -1.58 10.395 -1.97 10.113 -2.234 c
+9.826 -2.506 9.422 -2.646 8.893 -2.646 c
+8.664 -2.646 8.429 -2.602 8.187 -2.514 c
+7.952 -2.426 7.775 -2.311 7.658 -2.176 c
+8.01 -1.455 l
+8.106 -1.563 8.234 -1.646 8.392 -1.706 c
+8.547 -1.771 8.694 -1.808 8.834 -1.808 c
+9.069 -1.808 9.234 -1.75 9.333 -1.632 c
+9.44 -1.522 9.495 -1.345 9.495 -1.103 c
+9.495 -0.75 l
+9.296 -1.015 9.04 -1.147 8.716 -1.147 c
+8.294 -1.147 7.966 -0.985 7.731 -0.662 c
+7.503 -0.331 7.386 0.139 7.378 0.749 c
+h
+8.422 0.779 m
+8.422 0.404 8.469 0.135 8.569 -0.03 c
+8.664 -0.198 8.819 -0.279 9.025 -0.279 c
+9.238 -0.279 9.396 -0.202 9.495 -0.044 c
+9.495 1.851 l
+9.385 2.017 9.231 2.102 9.025 2.102 c
+8.819 2.102 8.664 2.017 8.569 1.851 c
+8.469 1.683 8.422 1.414 8.422 1.043 c
+h
+12.39 -1.073 -1.043 3.983 re
+11.303 3.939 m
+11.303 4.093 11.351 4.222 11.45 4.321 c
+11.557 4.428 11.692 4.483 11.862 4.483 c
+12.038 4.483 12.174 4.428 12.273 4.321 c
+12.38 4.222 12.435 4.093 12.435 3.939 c
+12.435 3.77 12.38 3.634 12.273 3.528 c
+12.174 3.428 12.038 3.38 11.862 3.38 c
+11.692 3.38 11.557 3.428 11.45 3.528 c
+11.351 3.634 11.303 3.77 11.303 3.939 c
+14.154 2.91 m
+14.184 2.513 l
+14.42 2.825 14.721 2.983 15.095 2.983 c
+15.779 2.983 16.132 2.502 16.153 1.543 c
+16.153 -1.073 l
+15.11 -1.073 l
+15.11 1.469 l
+15.11 1.693 15.073 1.855 15.008 1.955 c
+14.937 2.05 14.819 2.102 14.655 2.102 c
+14.467 2.102 14.32 2.006 14.214 1.822 c
+14.214 -1.073 l
+13.17 -1.073 l
+13.17 2.91 l
+h
+16.771 1.043 m
+16.771 1.69 16.889 2.175 17.124 2.499 c
+17.359 2.822 17.69 2.983 18.123 2.983 c
+18.476 2.983 18.748 2.84 18.947 2.557 c
+18.991 2.91 l
+19.931 2.91 l
+19.931 -1.073 l
+19.931 -1.58 19.788 -1.97 19.505 -2.234 c
+19.218 -2.506 18.815 -2.646 18.285 -2.646 c
+18.057 -2.646 17.822 -2.602 17.58 -2.514 c
+17.344 -2.426 17.168 -2.311 17.05 -2.176 c
+17.404 -1.455 l
+17.499 -1.563 17.628 -1.646 17.786 -1.706 c
+17.94 -1.771 18.087 -1.808 18.226 -1.808 c
+18.461 -1.808 18.627 -1.75 18.726 -1.632 c
+18.833 -1.522 18.888 -1.345 18.888 -1.103 c
+18.888 -0.75 l
+18.69 -1.015 18.432 -1.147 18.109 -1.147 c
+17.686 -1.147 17.359 -0.985 17.124 -0.662 c
+16.896 -0.331 16.778 0.139 16.771 0.749 c
+h
+17.815 0.779 m
+17.815 0.404 17.863 0.135 17.962 -0.03 c
+18.057 -0.198 18.212 -0.279 18.418 -0.279 c
+18.63 -0.279 18.788 -0.202 18.888 -0.044 c
+18.888 1.851 l
+18.777 2.017 18.623 2.102 18.418 2.102 c
+18.212 2.102 18.057 2.017 17.962 1.851 c
+17.863 1.683 17.815 1.414 17.815 1.043 c
+h
+f
+Q
+q 1 0 0 1 448.3695 238.2189 cm
+0 0 m
+-0.04 0.088 -0.066 0.235 -0.073 0.441 c
+-0.309 0.096 -0.603 -0.073 -0.956 -0.073 c
+-1.319 -0.073 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.103 0.713 -2.103 1.087 c
+-2.103 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.286 -1.043 2.41 -0.573 2.41 c
+-0.088 2.41 l
+-0.088 2.837 l
+-0.088 3.072 -0.143 3.237 -0.25 3.337 c
+-0.36 3.443 -0.522 3.499 -0.735 3.499 c
+-0.933 3.499 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.397 3.057 -1.397 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.984 3.267 -1.867 3.454 c
+-1.742 3.638 -1.58 3.785 -1.382 3.896 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.291 4.056 0.014 3.954 0.22 3.748 c
+0.434 3.543 0.548 3.248 0.559 2.866 c
+0.559 0.852 l
+0.559 0.548 0.595 0.283 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.515 m
+-0.702 0.515 -0.551 0.559 -0.412 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.985 c
+-0.088 1.926 l
+-0.456 1.926 l
+-0.771 1.926 -1.014 1.856 -1.191 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.235 0.563 -1.084 0.515 -0.867 0.515 c
+3.204 3.366 m
+3.116 3.385 3.017 3.395 2.911 3.395 c
+2.576 3.395 2.341 3.212 2.205 2.851 c
+2.205 0 l
+1.558 0 l
+1.558 3.983 l
+2.19 3.983 l
+2.205 3.572 l
+2.381 3.896 2.624 4.056 2.94 4.056 c
+3.046 4.056 3.135 4.035 3.204 3.998 c
+h
+5.203 -0.073 m
+4.704 -0.073 4.322 0.073 4.056 0.368 c
+3.792 0.661 3.66 1.095 3.66 1.675 c
+3.66 2.146 l
+3.66 2.741 3.785 3.208 4.042 3.543 c
+4.307 3.884 4.667 4.056 5.13 4.056 c
+5.589 4.056 5.931 3.902 6.159 3.601 c
+6.394 3.308 6.516 2.844 6.526 2.219 c
+6.526 1.793 l
+4.307 1.793 l
+4.307 1.705 l
+4.307 1.272 4.384 0.959 4.542 0.764 c
+4.707 0.577 4.939 0.485 5.232 0.485 c
+5.427 0.485 5.6 0.518 5.747 0.588 c
+5.894 0.665 6.03 0.783 6.159 0.941 c
+6.497 0.529 l
+6.21 0.125 5.78 -0.073 5.203 -0.073 c
+5.13 3.499 m
+4.854 3.499 4.652 3.403 4.527 3.219 c
+4.399 3.032 4.326 2.741 4.307 2.352 c
+5.88 2.352 l
+5.88 2.44 l
+5.857 2.822 5.791 3.09 5.674 3.248 c
+5.556 3.414 5.373 3.499 5.13 3.499 c
+9.348 0 m
+9.308 0.088 9.282 0.235 9.275 0.441 c
+9.04 0.096 8.746 -0.073 8.393 -0.073 c
+8.029 -0.073 7.746 0.022 7.54 0.22 c
+7.342 0.426 7.247 0.713 7.247 1.087 c
+7.247 1.488 7.383 1.808 7.659 2.043 c
+7.93 2.286 8.305 2.41 8.775 2.41 c
+9.261 2.41 l
+9.261 2.837 l
+9.261 3.072 9.205 3.237 9.099 3.337 c
+8.989 3.443 8.827 3.499 8.613 3.499 c
+8.415 3.499 8.253 3.439 8.129 3.322 c
+8.01 3.204 7.952 3.057 7.952 2.881 c
+7.305 2.881 l
+7.305 3.075 7.364 3.267 7.482 3.454 c
+7.607 3.638 7.769 3.785 7.967 3.896 c
+8.172 4.002 8.401 4.056 8.658 4.056 c
+9.058 4.056 9.363 3.954 9.569 3.748 c
+9.782 3.543 9.896 3.248 9.907 2.866 c
+9.907 0.852 l
+9.907 0.548 9.944 0.283 10.025 0.058 c
+10.025 0 l
+h
+8.481 0.515 m
+8.646 0.515 8.797 0.559 8.937 0.646 c
+9.084 0.735 9.19 0.845 9.261 0.985 c
+9.261 1.926 l
+8.893 1.926 l
+8.577 1.926 8.334 1.856 8.158 1.72 c
+7.981 1.591 7.894 1.404 7.894 1.161 c
+7.894 0.933 7.937 0.768 8.025 0.661 c
+8.114 0.563 8.264 0.515 8.481 0.515 c
+13.479 4.939 m
+13.479 3.983 l
+14.082 3.983 l
+14.082 3.454 l
+13.479 3.454 l
+13.479 0.985 l
+13.479 0.827 13.501 0.709 13.552 0.632 c
+13.612 0.551 13.699 0.515 13.817 0.515 c
+13.905 0.515 13.993 0.529 14.082 0.559 c
+14.082 0 l
+13.934 -0.048 13.78 -0.073 13.626 -0.073 c
+13.369 -0.073 13.174 0.018 13.038 0.206 c
+12.898 0.389 12.833 0.65 12.833 0.985 c
+12.833 3.454 l
+12.23 3.454 l
+12.23 3.983 l
+12.833 3.983 l
+12.833 4.939 l
+h
+14.64 2.175 m
+14.64 2.753 14.776 3.208 15.052 3.543 c
+15.335 3.884 15.706 4.056 16.168 4.056 c
+16.629 4.056 16.995 3.888 17.271 3.557 c
+17.554 3.233 17.701 2.786 17.712 2.219 c
+17.712 1.793 l
+17.712 1.224 17.569 0.768 17.286 0.426 c
+17.01 0.092 16.643 -0.073 16.184 -0.073 c
+15.721 -0.073 15.349 0.088 15.066 0.411 c
+14.791 0.742 14.647 1.183 14.64 1.735 c
+h
+15.287 1.793 m
+15.287 1.389 15.364 1.073 15.522 0.838 c
+15.688 0.603 15.908 0.485 16.184 0.485 c
+16.75 0.485 17.043 0.897 17.066 1.72 c
+17.066 2.175 l
+17.066 2.576 16.981 2.896 16.816 3.131 c
+16.658 3.373 16.44 3.499 16.168 3.499 c
+15.904 3.499 15.688 3.373 15.522 3.131 c
+15.364 2.896 15.287 2.576 15.287 2.175 c
+h
+23.239 1.147 m
+23.842 3.983 l
+24.488 3.983 l
+23.503 0 l
+22.989 0 l
+22.21 2.851 l
+21.46 0 l
+20.931 0 l
+19.976 3.983 l
+20.608 3.983 l
+21.225 1.22 l
+21.961 3.983 l
+22.475 3.983 l
+h
+25.032 2.175 m
+25.032 2.753 25.169 3.208 25.444 3.543 c
+25.727 3.884 26.098 4.056 26.561 4.056 c
+27.021 4.056 27.388 3.888 27.663 3.557 c
+27.947 3.233 28.093 2.786 28.105 2.219 c
+28.105 1.793 l
+28.105 1.224 27.962 0.768 27.678 0.426 c
+27.403 0.092 27.035 -0.073 26.576 -0.073 c
+26.113 -0.073 25.741 0.088 25.458 0.411 c
+25.183 0.742 25.04 1.183 25.032 1.735 c
+h
+25.679 1.793 m
+25.679 1.389 25.757 1.073 25.915 0.838 c
+26.08 0.603 26.3 0.485 26.576 0.485 c
+27.141 0.485 27.436 0.897 27.457 1.72 c
+27.457 2.175 l
+27.457 2.576 27.374 2.896 27.208 3.131 c
+27.05 3.373 26.833 3.499 26.561 3.499 c
+26.296 3.499 26.08 3.373 25.915 3.131 c
+25.757 2.896 25.679 2.576 25.679 2.175 c
+h
+30.588 3.366 m
+30.501 3.385 30.401 3.395 30.295 3.395 c
+29.961 3.395 29.725 3.212 29.589 2.851 c
+29.589 0 l
+28.942 0 l
+28.942 3.983 l
+29.574 3.983 l
+29.589 3.572 l
+29.766 3.896 30.008 4.056 30.324 4.056 c
+30.431 4.056 30.519 4.035 30.588 3.998 c
+h
+32.234 1.822 m
+31.897 1.426 l
+31.897 0 l
+31.235 0 l
+31.235 5.644 l
+31.897 5.644 l
+31.897 2.263 l
+33.132 3.983 l
+33.911 3.983 l
+32.646 2.323 l
+34.073 0 l
+33.323 0 l
+h
+35.336 0 -0.646 3.983 re
+35.38 5.027 m
+35.38 4.917 35.351 4.825 35.292 4.748 c
+35.233 4.678 35.138 4.644 35.013 4.644 c
+34.896 4.644 34.8 4.678 34.734 4.748 c
+34.675 4.825 34.646 4.917 34.646 5.027 c
+34.646 5.145 34.675 5.236 34.734 5.307 c
+34.8 5.384 34.896 5.424 35.013 5.424 c
+35.138 5.424 35.233 5.384 35.292 5.307 c
+35.351 5.226 35.38 5.134 35.38 5.027 c
+36.968 3.983 m
+36.982 3.543 l
+37.236 3.884 37.56 4.056 37.953 4.056 c
+38.659 4.056 39.015 3.586 39.026 2.645 c
+39.026 0 l
+38.379 0 l
+38.379 2.616 l
+38.379 2.929 38.324 3.15 38.218 3.278 c
+38.107 3.403 37.953 3.469 37.747 3.469 c
+37.589 3.469 37.442 3.414 37.306 3.308 c
+37.177 3.197 37.075 3.061 36.997 2.896 c
+36.997 0 l
+36.351 0 l
+36.351 3.983 l
+h
+39.864 2.175 m
+39.864 2.793 39.974 3.256 40.202 3.572 c
+40.426 3.896 40.76 4.056 41.202 4.056 c
+41.602 4.056 41.907 3.881 42.112 3.528 c
+42.156 3.983 l
+42.744 3.983 l
+42.744 -0.044 l
+42.744 -0.532 42.616 -0.912 42.362 -1.176 c
+42.106 -1.44 41.753 -1.573 41.304 -1.573 c
+41.106 -1.573 40.885 -1.521 40.643 -1.426 c
+40.396 -1.326 40.217 -1.205 40.099 -1.058 c
+40.363 -0.617 l
+40.628 -0.882 40.926 -1.014 41.26 -1.014 c
+41.797 -1.014 42.073 -0.721 42.083 -0.133 c
+42.083 0.397 l
+41.877 0.081 41.576 -0.073 41.186 -0.073 c
+40.775 -0.073 40.452 0.077 40.217 0.382 c
+39.989 0.694 39.871 1.147 39.864 1.735 c
+h
+40.525 1.793 m
+40.525 1.352 40.587 1.022 40.716 0.808 c
+40.841 0.603 41.058 0.5 41.363 0.5 c
+41.686 0.5 41.925 0.665 42.083 0.999 c
+42.083 2.984 l
+41.915 3.308 41.676 3.469 41.363 3.469 c
+41.069 3.469 40.853 3.366 40.716 3.16 c
+40.587 2.955 40.525 2.631 40.525 2.19 c
+h
+45.288 2.175 m
+45.288 2.782 45.398 3.248 45.626 3.572 c
+45.861 3.896 46.188 4.056 46.611 4.056 c
+46.993 4.056 47.29 3.898 47.507 3.586 c
+47.507 5.644 l
+48.154 5.644 l
+48.154 0 l
+47.566 0 l
+47.521 0.426 l
+47.316 0.092 47.012 -0.073 46.611 -0.073 c
+46.199 -0.073 45.875 0.081 45.64 0.397 c
+45.405 0.721 45.288 1.176 45.288 1.764 c
+h
+45.934 1.793 m
+45.934 1.352 45.996 1.022 46.125 0.808 c
+46.262 0.603 46.482 0.5 46.787 0.5 c
+47.11 0.5 47.349 0.661 47.507 0.985 c
+47.507 2.998 l
+47.338 3.31 47.099 3.469 46.787 3.469 c
+46.482 3.469 46.262 3.366 46.125 3.16 c
+45.996 2.955 45.934 2.631 45.934 2.19 c
+h
+49.859 0 -0.647 3.983 re
+49.903 5.027 m
+49.903 4.917 49.873 4.825 49.815 4.748 c
+49.757 4.678 49.661 4.644 49.536 4.644 c
+49.418 4.644 49.323 4.678 49.256 4.748 c
+49.198 4.825 49.168 4.917 49.168 5.027 c
+49.168 5.145 49.198 5.236 49.256 5.307 c
+49.323 5.384 49.418 5.424 49.536 5.424 c
+49.661 5.424 49.757 5.384 49.815 5.307 c
+49.873 5.226 49.903 5.134 49.903 5.027 c
+52.52 3.366 m
+52.431 3.385 52.332 3.395 52.225 3.395 c
+51.891 3.395 51.656 3.212 51.52 2.851 c
+51.52 0 l
+50.873 0 l
+50.873 3.983 l
+51.505 3.983 l
+51.52 3.572 l
+51.696 3.896 51.939 4.056 52.255 4.056 c
+52.362 4.056 52.45 4.035 52.52 3.998 c
+h
+54.519 -0.073 m
+54.019 -0.073 53.636 0.073 53.372 0.368 c
+53.108 0.661 52.975 1.095 52.975 1.675 c
+52.975 2.146 l
+52.975 2.741 53.1 3.208 53.358 3.543 c
+53.622 3.884 53.982 4.056 54.445 4.056 c
+54.904 4.056 55.246 3.902 55.474 3.601 c
+55.71 3.308 55.831 2.844 55.841 2.219 c
+55.841 1.793 l
+53.622 1.793 l
+53.622 1.705 l
+53.622 1.272 53.699 0.959 53.857 0.764 c
+54.023 0.577 54.254 0.485 54.548 0.485 c
+54.743 0.485 54.916 0.518 55.062 0.588 c
+55.209 0.665 55.346 0.783 55.474 0.941 c
+55.812 0.529 l
+55.525 0.125 55.095 -0.073 54.519 -0.073 c
+54.445 3.499 m
+54.17 3.499 53.967 3.403 53.842 3.219 c
+53.714 3.032 53.64 2.741 53.622 2.352 c
+55.195 2.352 l
+55.195 2.44 l
+55.172 2.822 55.107 3.09 54.989 3.248 c
+54.871 3.414 54.688 3.499 54.445 3.499 c
+58.002 0.485 m
+58.216 0.485 58.388 0.548 58.517 0.676 c
+58.653 0.812 58.727 1.003 58.737 1.249 c
+59.355 1.249 l
+59.332 0.867 59.197 0.548 58.943 0.294 c
+58.686 0.048 58.374 -0.073 58.002 -0.073 c
+57.51 -0.073 57.135 0.077 56.871 0.382 c
+56.614 0.694 56.489 1.161 56.489 1.779 c
+56.489 2.219 l
+56.489 2.815 56.614 3.271 56.871 3.586 c
+57.135 3.898 57.51 4.056 58.002 4.056 c
+58.403 4.056 58.723 3.925 58.958 3.66 c
+59.201 3.403 59.332 3.057 59.355 2.616 c
+58.737 2.616 l
+58.715 2.911 58.642 3.131 58.517 3.278 c
+58.399 3.425 58.226 3.499 58.002 3.499 c
+57.709 3.499 57.491 3.399 57.356 3.204 c
+57.216 3.017 57.142 2.708 57.135 2.278 c
+57.135 1.764 l
+57.135 1.294 57.202 0.959 57.341 0.764 c
+57.488 0.577 57.709 0.485 58.002 0.485 c
+60.957 4.939 m
+60.957 3.983 l
+61.559 3.983 l
+61.559 3.454 l
+60.957 3.454 l
+60.957 0.985 l
+60.957 0.827 60.979 0.709 61.03 0.632 c
+61.089 0.551 61.177 0.515 61.295 0.515 c
+61.383 0.515 61.472 0.529 61.559 0.559 c
+61.559 0 l
+61.412 -0.048 61.258 -0.073 61.104 -0.073 c
+60.847 -0.073 60.652 0.018 60.516 0.206 c
+60.377 0.389 60.31 0.65 60.31 0.985 c
+60.31 3.454 l
+59.708 3.454 l
+59.708 3.983 l
+60.31 3.983 l
+60.31 4.939 l
+h
+62.118 2.175 m
+62.118 2.753 62.254 3.208 62.53 3.543 c
+62.812 3.884 63.184 4.056 63.647 4.056 c
+64.106 4.056 64.474 3.888 64.749 3.557 c
+65.032 3.233 65.179 2.786 65.19 2.219 c
+65.19 1.793 l
+65.19 1.224 65.046 0.768 64.764 0.426 c
+64.489 0.092 64.121 -0.073 63.662 -0.073 c
+63.199 -0.073 62.827 0.088 62.544 0.411 c
+62.268 0.742 62.125 1.183 62.118 1.735 c
+h
+62.765 1.793 m
+62.765 1.389 62.842 1.073 63 0.838 c
+63.165 0.603 63.386 0.485 63.662 0.485 c
+64.227 0.485 64.522 0.897 64.543 1.72 c
+64.543 2.175 l
+64.543 2.576 64.458 2.896 64.294 3.131 c
+64.136 3.373 63.919 3.499 63.647 3.499 c
+63.382 3.499 63.165 3.373 63 3.131 c
+62.842 2.896 62.765 2.576 62.765 2.175 c
+h
+67.674 3.366 m
+67.586 3.385 67.487 3.395 67.38 3.395 c
+67.046 3.395 66.81 3.212 66.675 2.851 c
+66.675 0 l
+66.028 0 l
+66.028 3.983 l
+66.66 3.983 l
+66.675 3.572 l
+66.851 3.896 67.094 4.056 67.41 4.056 c
+67.516 4.056 67.604 4.035 67.674 3.998 c
+h
+69.482 1.087 m
+70.203 3.983 l
+70.893 3.983 l
+69.6 -0.559 l
+69.501 -0.9 69.357 -1.161 69.174 -1.338 c
+68.998 -1.514 68.795 -1.602 68.571 -1.602 c
+68.483 -1.602 68.369 -1.58 68.233 -1.544 c
+68.233 -1 l
+68.38 -1.014 l
+68.564 -1.014 68.711 -0.97 68.821 -0.882 c
+68.927 -0.794 69.015 -0.636 69.085 -0.412 c
+69.203 0.029 l
+68.042 3.983 l
+68.747 3.983 l
+h
+71.173 0.353 m
+71.173 0.47 71.205 0.566 71.275 0.646 c
+71.342 0.723 71.444 0.764 71.585 0.764 c
+71.731 0.764 71.838 0.723 71.907 0.646 c
+71.984 0.566 72.025 0.47 72.025 0.353 c
+72.025 0.243 71.984 0.151 71.907 0.073 c
+71.838 -0.004 71.731 -0.044 71.585 -0.044 c
+71.444 -0.044 71.342 -0.004 71.275 0.073 c
+71.205 0.151 71.173 0.243 71.173 0.353 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 232.611 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 225.772 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.807 l
+-1.896 -1.807 l
+-1.896 -1.263 l
+-2.142 -1.256 -2.359 -1.219 -2.543 -1.161 c
+-2.719 -1.102 -2.873 -1.024 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.034 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.477 -2.234 -0.514 c
+-2.138 -0.554 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.662 l
+-1.907 0.662 -1.926 0.666 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.757 -3.322 1.985 c
+-3.322 2.18 -3.289 2.345 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.933 -2.645 3.003 -2.469 3.043 c
+-2.293 3.091 -2.102 3.12 -1.896 3.132 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.132 l
+-1.319 3.12 -1.128 3.091 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.621 -0.279 2.484 c
+-0.202 2.345 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.368 l
+-1.514 1.368 l
+-1.506 1.368 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.32 0.871 -0.213 0.743 -0.132 0.588 c
+-0.044 0.431 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.503 -2.113 2.484 -2.19 2.455 c
+-2.26 2.426 -2.319 2.386 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.191 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.838 -2.439 1.779 c
+-2.41 1.721 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.201 -0.837 -0.014 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.634 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.395 c
+5.284 -2.314 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.83 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.219 6.034 -1.219 c
+5.829 -1.219 5.644 -1.183 5.49 -1.102 c
+5.343 -1.014 5.215 -0.897 5.108 -0.749 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.019 4.888 1.235 c
+4.946 1.449 5.027 1.632 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.188 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.919 7.107 1.97 c
+7.115 2.029 7.122 2.077 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.414 7.938 -1.635 7.871 -1.822 c
+7.802 -2.006 7.699 -2.16 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.743 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.368 c
+5.88 1.279 5.835 1.162 5.799 1.015 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.514 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.603 l
+9.199 1.603 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.597 1.603 m
+13.053 1.603 l
+13.053 2.22 l
+13.641 2.22 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.22 l
+15.728 2.22 l
+15.728 1.603 l
+14.494 1.603 l
+14.494 -0.103 l
+14.494 -0.323 l
+14.501 -0.392 14.523 -0.455 14.552 -0.514 c
+14.589 -0.565 14.645 -0.61 14.714 -0.646 c
+14.791 -0.675 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.687 15.464 -0.675 c
+15.599 -0.658 15.732 -0.631 15.861 -0.602 c
+15.861 -1.205 l
+15.78 -1.215 15.703 -1.23 15.626 -1.249 c
+15.545 -1.26 15.468 -1.267 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.3 14.938 -1.308 14.832 -1.308 c
+14.645 -1.308 14.483 -1.294 14.346 -1.263 c
+14.218 -1.227 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.024 13.788 -0.955 c
+13.729 -0.878 13.685 -0.801 13.656 -0.72 c
+13.627 -0.631 13.604 -0.544 13.597 -0.455 c
+13.586 -0.359 13.582 -0.264 13.582 -0.176 c
+h
+22.677 -1.323 m
+22.391 -1.323 22.148 -1.282 21.942 -1.205 c
+21.737 -1.117 21.564 -0.995 21.428 -0.837 c
+21.288 -0.683 21.185 -0.496 21.12 -0.278 c
+21.049 -0.055 21.016 0.192 21.016 0.456 c
+21.016 0.75 21.049 1.008 21.12 1.235 c
+21.197 1.459 21.303 1.646 21.442 1.794 c
+21.59 1.948 21.766 2.066 21.972 2.147 c
+22.177 2.234 22.412 2.278 22.677 2.278 c
+22.901 2.278 23.104 2.249 23.28 2.191 c
+23.456 2.132 23.607 2.047 23.736 1.941 c
+23.861 1.842 23.963 1.721 24.044 1.573 c
+24.121 1.434 24.177 1.283 24.206 1.118 c
+23.295 1.073 l
+23.265 1.25 23.196 1.389 23.089 1.5 c
+22.99 1.607 22.846 1.661 22.662 1.661 c
+22.416 1.661 22.24 1.559 22.134 1.353 c
+22.023 1.154 21.972 0.867 21.972 0.485 c
+21.972 -0.309 22.207 -0.706 22.677 -0.706 c
+22.843 -0.706 22.986 -0.654 23.104 -0.544 c
+23.221 -0.437 23.295 -0.275 23.324 -0.058 c
+24.235 -0.103 l
+24.206 -0.272 24.151 -0.426 24.074 -0.573 c
+24.004 -0.72 23.901 -0.852 23.765 -0.97 c
+23.636 -1.08 23.478 -1.168 23.295 -1.234 c
+23.118 -1.294 22.913 -1.323 22.677 -1.323 c
+28.384 0.485 m
+28.384 0.21 28.347 -0.04 28.281 -0.264 c
+28.212 -0.481 28.108 -0.668 27.973 -0.823 c
+27.833 -0.98 27.657 -1.102 27.443 -1.19 c
+27.227 -1.278 26.973 -1.323 26.679 -1.323 c
+26.404 -1.323 26.157 -1.278 25.944 -1.19 c
+25.739 -1.102 25.566 -0.98 25.43 -0.823 c
+25.29 -0.668 25.187 -0.481 25.121 -0.264 c
+25.051 -0.04 25.018 0.21 25.018 0.485 c
+25.018 0.739 25.047 0.975 25.106 1.191 c
+25.172 1.415 25.275 1.607 25.415 1.764 c
+25.551 1.929 25.727 2.058 25.944 2.147 c
+26.157 2.234 26.414 2.278 26.709 2.278 c
+27.021 2.278 27.281 2.234 27.487 2.147 c
+27.701 2.058 27.873 1.929 28.002 1.764 c
+28.138 1.607 28.237 1.415 28.296 1.191 c
+28.355 0.975 28.384 0.739 28.384 0.485 c
+27.429 0.485 m
+27.429 0.691 27.414 0.867 27.385 1.015 c
+27.362 1.162 27.326 1.283 27.267 1.382 c
+27.208 1.478 27.135 1.548 27.046 1.588 c
+26.959 1.636 26.848 1.661 26.724 1.661 c
+26.458 1.661 26.267 1.563 26.15 1.368 c
+26.032 1.18 25.974 0.886 25.974 0.485 c
+25.974 0.063 26.032 -0.242 26.15 -0.426 c
+26.267 -0.613 26.444 -0.706 26.679 -0.706 c
+26.804 -0.706 26.918 -0.687 27.017 -0.646 c
+27.113 -0.598 27.194 -0.525 27.252 -0.426 c
+27.318 -0.33 27.362 -0.205 27.385 -0.058 c
+27.414 0.088 27.429 0.269 27.429 0.485 c
+30.402 -1.263 m
+30.402 0.853 l
+30.402 1.019 30.394 1.154 30.387 1.264 c
+30.376 1.372 30.358 1.455 30.328 1.515 c
+30.306 1.58 30.277 1.632 30.24 1.661 c
+30.211 1.691 30.17 1.706 30.122 1.706 c
+30.063 1.706 30.009 1.676 29.961 1.617 c
+29.92 1.566 29.887 1.492 29.858 1.397 c
+29.828 1.309 29.803 1.195 29.784 1.058 c
+29.773 0.919 29.77 0.769 29.77 0.603 c
+29.77 -1.263 l
+29.02 -1.263 l
+29.02 1.47 l
+29.02 1.706 l
+29.02 1.926 l
+29.02 2.003 29.012 2.066 29.005 2.117 c
+29.005 2.22 l
+29.681 2.22 l
+29.681 2.132 l
+29.681 1.985 l
+29.689 1.926 29.696 1.867 29.696 1.808 c
+29.696 1.646 l
+29.71 1.646 l
+29.729 1.735 29.758 1.812 29.799 1.881 c
+29.836 1.96 29.88 2.029 29.931 2.087 c
+29.99 2.147 30.057 2.191 30.137 2.22 c
+30.214 2.257 30.302 2.278 30.402 2.278 c
+30.585 2.278 30.725 2.224 30.813 2.117 c
+30.909 2.018 30.979 1.86 31.019 1.646 c
+31.034 1.646 l
+31.071 1.742 31.111 1.831 31.152 1.912 c
+31.199 1.989 31.254 2.051 31.313 2.103 c
+31.372 2.161 31.438 2.205 31.519 2.234 c
+31.596 2.264 31.684 2.278 31.783 2.278 c
+31.919 2.278 32.033 2.253 32.122 2.205 c
+32.21 2.154 32.276 2.08 32.328 1.985 c
+32.386 1.885 32.423 1.757 32.445 1.603 c
+32.474 1.455 32.488 1.272 32.488 1.058 c
+32.488 -1.263 l
+31.769 -1.263 l
+31.769 0.853 l
+31.769 1.019 31.761 1.154 31.754 1.264 c
+31.742 1.372 31.725 1.455 31.695 1.515 c
+31.673 1.58 31.644 1.632 31.607 1.661 c
+31.578 1.691 31.537 1.706 31.489 1.706 c
+31.372 1.706 31.276 1.617 31.21 1.441 c
+31.152 1.272 31.122 1.015 31.122 0.662 c
+31.122 -1.263 l
+h
+34.462 -1.263 m
+34.462 0.853 l
+34.462 1.019 34.455 1.154 34.447 1.264 c
+34.437 1.372 34.418 1.455 34.389 1.515 c
+34.367 1.58 34.337 1.632 34.3 1.661 c
+34.271 1.691 34.231 1.706 34.183 1.706 c
+34.124 1.706 34.069 1.676 34.021 1.617 c
+33.98 1.566 33.948 1.492 33.918 1.397 c
+33.889 1.309 33.863 1.195 33.845 1.058 c
+33.834 0.919 33.83 0.769 33.83 0.603 c
+33.83 -1.263 l
+33.08 -1.263 l
+33.08 1.47 l
+33.08 1.706 l
+33.08 1.926 l
+33.08 2.003 33.074 2.066 33.066 2.117 c
+33.066 2.22 l
+33.742 2.22 l
+33.742 2.132 l
+33.742 1.985 l
+33.749 1.926 33.757 1.867 33.757 1.808 c
+33.757 1.646 l
+33.772 1.646 l
+33.789 1.735 33.82 1.812 33.859 1.881 c
+33.897 1.96 33.94 2.029 33.992 2.087 c
+34.051 2.147 34.117 2.191 34.198 2.22 c
+34.275 2.257 34.363 2.278 34.462 2.278 c
+34.646 2.278 34.786 2.224 34.874 2.117 c
+34.969 2.018 35.039 1.86 35.079 1.646 c
+35.094 1.646 l
+35.131 1.742 35.171 1.831 35.212 1.912 c
+35.26 1.989 35.314 2.051 35.374 2.103 c
+35.432 2.161 35.499 2.205 35.58 2.234 c
+35.657 2.264 35.744 2.278 35.844 2.278 c
+35.98 2.278 36.093 2.253 36.182 2.205 c
+36.27 2.154 36.336 2.08 36.388 1.985 c
+36.446 1.885 36.484 1.757 36.505 1.603 c
+36.535 1.455 36.55 1.272 36.55 1.058 c
+36.55 -1.263 l
+35.829 -1.263 l
+35.829 0.853 l
+35.829 1.019 35.821 1.154 35.815 1.264 c
+35.804 1.372 35.785 1.455 35.756 1.515 c
+35.734 1.58 35.704 1.632 35.667 1.661 c
+35.638 1.691 35.598 1.706 35.55 1.706 c
+35.432 1.706 35.337 1.617 35.27 1.441 c
+35.212 1.272 35.183 1.015 35.183 0.662 c
+35.183 -1.263 l
+h
+39.467 -0.646 m
+40.599 -0.646 l
+40.599 -1.263 l
+37.292 -1.263 l
+37.292 -0.646 l
+38.556 -0.646 l
+38.556 1.603 l
+37.63 1.603 l
+37.63 2.22 l
+39.467 2.22 l
+h
+38.556 3.514 0.911 -0.676 re
+38.556 2.837 m
+42.028 1.603 m
+41.485 1.603 l
+41.485 2.22 l
+42.073 2.22 l
+42.352 3.117 l
+42.925 3.117 l
+42.925 2.22 l
+44.16 2.22 l
+44.16 1.603 l
+42.925 1.603 l
+42.925 -0.103 l
+42.925 -0.323 l
+42.933 -0.392 42.954 -0.455 42.984 -0.514 c
+43.021 -0.565 43.076 -0.61 43.146 -0.646 c
+43.223 -0.675 43.337 -0.691 43.484 -0.691 c
+43.619 -0.691 43.756 -0.687 43.895 -0.675 c
+44.031 -0.658 44.164 -0.631 44.292 -0.602 c
+44.292 -1.205 l
+44.211 -1.215 44.134 -1.23 44.057 -1.249 c
+43.976 -1.26 43.899 -1.267 43.822 -1.278 c
+43.741 -1.286 43.653 -1.294 43.557 -1.294 c
+43.469 -1.3 43.37 -1.308 43.264 -1.308 c
+43.076 -1.308 42.914 -1.294 42.778 -1.263 c
+42.649 -1.227 42.536 -1.183 42.441 -1.132 c
+42.352 -1.084 42.279 -1.024 42.219 -0.955 c
+42.161 -0.878 42.117 -0.801 42.088 -0.72 c
+42.058 -0.631 42.036 -0.544 42.028 -0.455 c
+42.017 -0.359 42.013 -0.264 42.013 -0.176 c
+h
+f
+Q
+q 1 0 0 1 299.9491 211.3232 cm
+0 0 m
+-0.029 -0.58 -0.191 -1.022 -0.484 -1.323 c
+-0.779 -1.627 -1.198 -1.779 -1.734 -1.779 c
+-2.263 -1.779 -2.69 -1.58 -3.013 -1.176 c
+-3.329 -0.764 -3.484 -0.206 -3.484 0.5 c
+-3.484 1.47 l
+-3.484 2.165 -3.322 2.712 -2.998 3.117 c
+-2.675 3.516 -2.23 3.719 -1.66 3.719 c
+-1.153 3.719 -0.756 3.561 -0.47 3.248 c
+-0.187 2.944 -0.029 2.502 0 1.926 c
+-0.691 1.926 l
+-0.72 2.367 -0.816 2.679 -0.97 2.866 c
+-1.117 3.05 -1.348 3.146 -1.66 3.146 c
+-2.036 3.146 -2.319 2.999 -2.514 2.705 c
+-2.711 2.418 -2.807 2.003 -2.807 1.455 c
+-2.807 0.47 l
+-2.807 -0.07 -2.715 -0.485 -2.528 -0.779 c
+-2.344 -1.066 -2.08 -1.205 -1.734 -1.205 c
+-1.381 -1.205 -1.128 -1.117 -0.97 -0.941 c
+-0.816 -0.764 -0.72 -0.452 -0.691 0 c
+h
+2.455 1.661 m
+2.367 1.679 2.268 1.691 2.161 1.691 c
+1.827 1.691 1.592 1.507 1.455 1.147 c
+1.455 -1.705 l
+0.809 -1.705 l
+0.809 2.278 l
+1.441 2.278 l
+1.455 1.866 l
+1.632 2.19 1.875 2.352 2.19 2.352 c
+2.297 2.352 2.385 2.33 2.455 2.294 c
+h
+4.454 -1.779 m
+3.954 -1.779 3.572 -1.631 3.308 -1.338 c
+3.043 -1.043 2.911 -0.61 2.911 -0.029 c
+2.911 0.441 l
+2.911 1.037 3.036 1.503 3.293 1.837 c
+3.558 2.18 3.917 2.352 4.381 2.352 c
+4.84 2.352 5.182 2.198 5.409 1.897 c
+5.644 1.602 5.766 1.139 5.777 0.515 c
+5.777 0.088 l
+3.558 0.088 l
+3.558 0 l
+3.558 -0.434 3.635 -0.746 3.793 -0.941 c
+3.958 -1.128 4.189 -1.22 4.484 -1.22 c
+4.678 -1.22 4.851 -1.187 4.998 -1.117 c
+5.145 -1.04 5.281 -0.922 5.409 -0.764 c
+5.748 -1.176 l
+5.461 -1.58 5.031 -1.779 4.454 -1.779 c
+4.381 1.793 m
+4.105 1.793 3.903 1.698 3.778 1.514 c
+3.649 1.326 3.576 1.037 3.558 0.647 c
+5.13 0.647 l
+5.13 0.735 l
+5.108 1.118 5.042 1.386 4.925 1.544 c
+4.807 1.709 4.623 1.793 4.381 1.793 c
+8.599 -1.705 m
+8.559 -1.617 8.533 -1.469 8.526 -1.264 c
+8.291 -1.61 7.996 -1.779 7.643 -1.779 c
+7.28 -1.779 6.997 -1.683 6.791 -1.484 c
+6.593 -1.278 6.498 -0.992 6.498 -0.617 c
+6.498 -0.216 6.633 0.103 6.909 0.338 c
+7.181 0.58 7.556 0.706 8.026 0.706 c
+8.511 0.706 l
+8.511 1.132 l
+8.511 1.367 8.456 1.532 8.349 1.631 c
+8.239 1.738 8.077 1.793 7.865 1.793 c
+7.666 1.793 7.504 1.735 7.379 1.617 c
+7.262 1.5 7.203 1.353 7.203 1.176 c
+6.556 1.176 l
+6.556 1.371 6.615 1.562 6.733 1.749 c
+6.857 1.933 7.019 2.08 7.217 2.19 c
+7.423 2.297 7.651 2.352 7.909 2.352 c
+8.309 2.352 8.614 2.249 8.819 2.043 c
+9.033 1.837 9.147 1.544 9.158 1.161 c
+9.158 -0.852 l
+9.158 -1.157 9.195 -1.422 9.276 -1.646 c
+9.276 -1.705 l
+h
+7.732 -1.191 m
+7.897 -1.191 8.048 -1.147 8.187 -1.058 c
+8.335 -0.97 8.441 -0.86 8.511 -0.721 c
+8.511 0.22 l
+8.144 0.22 l
+7.828 0.22 7.585 0.151 7.408 0.015 c
+7.232 -0.114 7.144 -0.301 7.144 -0.544 c
+7.144 -0.771 7.188 -0.937 7.277 -1.043 c
+7.364 -1.143 7.516 -1.191 7.732 -1.191 c
+11.009 3.234 m
+11.009 2.278 l
+11.612 2.278 l
+11.612 1.749 l
+11.009 1.749 l
+11.009 -0.721 l
+11.009 -0.878 11.032 -0.995 11.084 -1.073 c
+11.142 -1.153 11.231 -1.191 11.348 -1.191 c
+11.437 -1.191 11.524 -1.176 11.612 -1.147 c
+11.612 -1.705 l
+11.466 -1.753 11.311 -1.779 11.157 -1.779 c
+10.899 -1.779 10.705 -1.687 10.569 -1.5 c
+10.429 -1.315 10.363 -1.055 10.363 -0.721 c
+10.363 1.749 l
+9.76 1.749 l
+9.76 2.278 l
+10.363 2.278 l
+10.363 3.234 l
+h
+13.803 -1.779 m
+13.303 -1.779 12.921 -1.631 12.656 -1.338 c
+12.391 -1.043 12.26 -0.61 12.26 -0.029 c
+12.26 0.441 l
+12.26 1.037 12.384 1.503 12.642 1.837 c
+12.906 2.18 13.266 2.352 13.729 2.352 c
+14.188 2.352 14.531 2.198 14.758 1.897 c
+14.993 1.602 15.115 1.139 15.125 0.515 c
+15.125 0.088 l
+12.906 0.088 l
+12.906 0 l
+12.906 -0.434 12.983 -0.746 13.141 -0.941 c
+13.307 -1.128 13.538 -1.22 13.832 -1.22 c
+14.027 -1.22 14.199 -1.187 14.346 -1.117 c
+14.494 -1.04 14.629 -0.922 14.758 -0.764 c
+15.096 -1.176 l
+14.81 -1.58 14.379 -1.779 13.803 -1.779 c
+13.729 1.793 m
+13.453 1.793 13.251 1.698 13.126 1.514 c
+12.998 1.326 12.925 1.037 12.906 0.647 c
+14.479 0.647 l
+14.479 0.735 l
+14.457 1.118 14.39 1.386 14.273 1.544 c
+14.155 1.709 13.972 1.793 13.729 1.793 c
+18.242 2.278 m
+18.257 1.837 l
+18.51 2.18 18.834 2.352 19.227 2.352 c
+19.932 2.352 20.289 1.881 20.299 0.941 c
+20.299 -1.705 l
+19.653 -1.705 l
+19.653 0.912 l
+19.653 1.224 19.597 1.444 19.491 1.573 c
+19.381 1.698 19.227 1.764 19.021 1.764 c
+18.863 1.764 18.716 1.709 18.58 1.602 c
+18.452 1.492 18.348 1.357 18.271 1.191 c
+18.271 -1.705 l
+17.625 -1.705 l
+17.625 2.278 l
+h
+22.695 -1.779 m
+22.196 -1.779 21.814 -1.631 21.549 -1.338 c
+21.284 -1.043 21.152 -0.61 21.152 -0.029 c
+21.152 0.441 l
+21.152 1.037 21.278 1.503 21.534 1.837 c
+21.799 2.18 22.159 2.352 22.622 2.352 c
+23.081 2.352 23.423 2.198 23.651 1.897 c
+23.886 1.602 24.008 1.139 24.019 0.515 c
+24.019 0.088 l
+21.799 0.088 l
+21.799 0 l
+21.799 -0.434 21.876 -0.746 22.034 -0.941 c
+22.2 -1.128 22.431 -1.22 22.725 -1.22 c
+22.92 -1.22 23.092 -1.187 23.239 -1.117 c
+23.387 -1.04 23.522 -0.922 23.651 -0.764 c
+23.989 -1.176 l
+23.703 -1.58 23.273 -1.779 22.695 -1.779 c
+22.622 1.793 m
+22.346 1.793 22.144 1.698 22.02 1.514 c
+21.891 1.326 21.818 1.037 21.799 0.647 c
+23.372 0.647 l
+23.372 0.735 l
+23.35 1.118 23.283 1.386 23.166 1.544 c
+23.048 1.709 22.865 1.793 22.622 1.793 c
+27.767 -0.559 m
+28.37 2.278 l
+29.016 2.278 l
+28.031 -1.705 l
+27.517 -1.705 l
+26.738 1.147 l
+25.988 -1.705 l
+25.459 -1.705 l
+24.503 2.278 l
+25.136 2.278 l
+25.753 -0.485 l
+26.488 2.278 l
+27.002 2.278 l
+h
+32.794 -1.22 m
+33.007 -1.22 33.18 -1.157 33.309 -1.029 c
+33.444 -0.893 33.518 -0.702 33.529 -0.455 c
+34.146 -0.455 l
+34.124 -0.838 33.988 -1.157 33.735 -1.411 c
+33.477 -1.658 33.165 -1.779 32.794 -1.779 c
+32.301 -1.779 31.927 -1.627 31.662 -1.323 c
+31.405 -1.01 31.28 -0.544 31.28 0.073 c
+31.28 0.515 l
+31.28 1.11 31.405 1.565 31.662 1.881 c
+31.927 2.194 32.301 2.352 32.794 2.352 c
+33.194 2.352 33.514 2.219 33.749 1.955 c
+33.992 1.698 34.124 1.353 34.146 0.912 c
+33.529 0.912 l
+33.507 1.205 33.433 1.426 33.309 1.573 c
+33.191 1.72 33.018 1.793 32.794 1.793 c
+32.5 1.793 32.283 1.694 32.147 1.5 c
+32.008 1.312 31.934 1.004 31.927 0.573 c
+31.927 0.059 l
+31.927 -0.411 31.993 -0.746 32.133 -0.941 c
+32.28 -1.128 32.5 -1.22 32.794 -1.22 c
+34.763 0.47 m
+34.763 1.047 34.9 1.503 35.175 1.837 c
+35.458 2.18 35.829 2.352 36.292 2.352 c
+36.752 2.352 37.119 2.183 37.394 1.852 c
+37.678 1.529 37.824 1.081 37.836 0.515 c
+37.836 0.088 l
+37.836 -0.482 37.693 -0.937 37.409 -1.278 c
+37.134 -1.613 36.766 -1.779 36.307 -1.779 c
+35.844 -1.779 35.472 -1.617 35.19 -1.294 c
+34.915 -0.962 34.771 -0.522 34.763 0.029 c
+h
+35.41 0.088 m
+35.41 -0.316 35.487 -0.632 35.646 -0.867 c
+35.811 -1.103 36.031 -1.22 36.307 -1.22 c
+36.873 -1.22 37.167 -0.808 37.189 0.015 c
+37.189 0.47 l
+37.189 0.871 37.105 1.191 36.939 1.426 c
+36.781 1.669 36.565 1.793 36.292 1.793 c
+36.027 1.793 35.811 1.669 35.646 1.426 c
+35.487 1.191 35.41 0.871 35.41 0.47 c
+h
+39.291 2.278 m
+39.306 1.911 l
+39.548 2.205 39.868 2.352 40.261 2.352 c
+40.702 2.352 41.011 2.153 41.187 1.764 c
+41.44 2.153 41.79 2.352 42.231 2.352 c
+42.965 2.352 43.341 1.889 43.362 0.97 c
+43.362 -1.705 l
+42.716 -1.705 l
+42.716 0.912 l
+42.716 1.205 42.661 1.419 42.554 1.558 c
+42.455 1.694 42.282 1.764 42.04 1.764 c
+41.841 1.764 41.679 1.683 41.554 1.529 c
+41.437 1.382 41.367 1.191 41.348 0.956 c
+41.348 -1.705 l
+40.687 -1.705 l
+40.687 0.941 l
+40.687 1.488 40.467 1.764 40.026 1.764 c
+39.692 1.764 39.456 1.602 39.32 1.278 c
+39.32 -1.705 l
+38.674 -1.705 l
+38.674 2.278 l
+h
+44.95 2.278 m
+44.965 1.911 l
+45.207 2.205 45.527 2.352 45.92 2.352 c
+46.361 2.352 46.67 2.153 46.846 1.764 c
+47.1 2.153 47.449 2.352 47.89 2.352 c
+48.625 2.352 48.999 1.889 49.022 0.97 c
+49.022 -1.705 l
+48.375 -1.705 l
+48.375 0.912 l
+48.375 1.205 48.32 1.419 48.213 1.558 c
+48.114 1.694 47.941 1.764 47.698 1.764 c
+47.501 1.764 47.339 1.683 47.214 1.529 c
+47.096 1.382 47.027 1.191 47.008 0.956 c
+47.008 -1.705 l
+46.346 -1.705 l
+46.346 0.941 l
+46.346 1.488 46.126 1.764 45.685 1.764 c
+45.35 1.764 45.115 1.602 44.98 1.278 c
+44.98 -1.705 l
+44.332 -1.705 l
+44.332 2.278 l
+h
+50.697 -1.705 -0.647 3.983 re
+50.742 3.322 m
+50.742 3.212 50.712 3.12 50.653 3.042 c
+50.595 2.973 50.499 2.94 50.374 2.94 c
+50.256 2.94 50.161 2.973 50.094 3.042 c
+50.036 3.12 50.007 3.212 50.007 3.322 c
+50.007 3.439 50.036 3.532 50.094 3.601 c
+50.161 3.678 50.256 3.719 50.374 3.719 c
+50.499 3.719 50.595 3.678 50.653 3.601 c
+50.712 3.52 50.742 3.429 50.742 3.322 c
+52.564 3.234 m
+52.564 2.278 l
+53.167 2.278 l
+53.167 1.749 l
+52.564 1.749 l
+52.564 -0.721 l
+52.564 -0.878 52.586 -0.995 52.637 -1.073 c
+52.696 -1.153 52.785 -1.191 52.902 -1.191 c
+52.99 -1.191 53.078 -1.176 53.167 -1.147 c
+53.167 -1.705 l
+53.02 -1.753 52.866 -1.779 52.711 -1.779 c
+52.454 -1.779 52.259 -1.687 52.124 -1.5 c
+51.983 -1.315 51.918 -1.055 51.918 -0.721 c
+51.918 1.749 l
+51.315 1.749 l
+51.315 2.278 l
+51.918 2.278 l
+51.918 3.234 l
+h
+55.945 -1.705 m
+55.945 1.749 l
+55.416 1.749 l
+55.416 2.278 l
+55.945 2.278 l
+55.945 2.734 l
+55.945 3.135 56.041 3.447 56.239 3.675 c
+56.444 3.899 56.724 4.013 57.077 4.013 c
+57.213 4.013 57.345 3.991 57.474 3.954 c
+57.444 3.41 l
+57.345 3.429 57.246 3.439 57.15 3.439 c
+56.775 3.439 56.592 3.175 56.592 2.646 c
+56.592 2.278 l
+57.267 2.278 l
+57.267 1.749 l
+56.592 1.749 l
+56.592 -1.705 l
+h
+59.679 1.661 m
+59.59 1.679 59.491 1.691 59.384 1.691 c
+59.05 1.691 58.815 1.507 58.679 1.147 c
+58.679 -1.705 l
+58.032 -1.705 l
+58.032 2.278 l
+58.664 2.278 l
+58.679 1.866 l
+58.855 2.19 59.098 2.352 59.414 2.352 c
+59.521 2.352 59.609 2.33 59.679 2.294 c
+h
+60.12 0.47 m
+60.12 1.047 60.255 1.503 60.531 1.837 c
+60.814 2.18 61.185 2.352 61.648 2.352 c
+62.108 2.352 62.475 2.183 62.75 1.852 c
+63.033 1.529 63.181 1.081 63.191 0.515 c
+63.191 0.088 l
+63.191 -0.482 63.048 -0.937 62.765 -1.278 c
+62.49 -1.613 62.122 -1.779 61.663 -1.779 c
+61.2 -1.779 60.829 -1.617 60.546 -1.294 c
+60.27 -0.962 60.126 -0.522 60.12 0.029 c
+h
+60.766 0.088 m
+60.766 -0.316 60.843 -0.632 61.001 -0.867 c
+61.167 -1.103 61.387 -1.22 61.663 -1.22 c
+62.229 -1.22 62.522 -0.808 62.545 0.015 c
+62.545 0.47 l
+62.545 0.871 62.46 1.191 62.295 1.426 c
+62.137 1.669 61.92 1.793 61.648 1.793 c
+61.383 1.793 61.167 1.669 61.001 1.426 c
+60.843 1.191 60.766 0.871 60.766 0.47 c
+h
+64.647 2.278 m
+64.662 1.911 l
+64.904 2.205 65.223 2.352 65.617 2.352 c
+66.058 2.352 66.366 2.153 66.543 1.764 c
+66.796 2.153 67.145 2.352 67.586 2.352 c
+68.321 2.352 68.697 1.889 68.718 0.97 c
+68.718 -1.705 l
+68.072 -1.705 l
+68.072 0.912 l
+68.072 1.205 68.016 1.419 67.91 1.558 c
+67.81 1.694 67.638 1.764 67.395 1.764 c
+67.197 1.764 67.035 1.683 66.91 1.529 c
+66.792 1.382 66.723 1.191 66.705 0.956 c
+66.705 -1.705 l
+66.043 -1.705 l
+66.043 0.941 l
+66.043 1.488 65.823 1.764 65.381 1.764 c
+65.047 1.764 64.812 1.602 64.676 1.278 c
+64.676 -1.705 l
+64.03 -1.705 l
+64.03 2.278 l
+h
+72.79 -1.22 m
+73.003 -1.22 73.175 -1.157 73.304 -1.029 c
+73.441 -0.893 73.514 -0.702 73.525 -0.455 c
+74.142 -0.455 l
+74.12 -0.838 73.984 -1.157 73.73 -1.411 c
+73.474 -1.658 73.161 -1.779 72.79 -1.779 c
+72.298 -1.779 71.922 -1.627 71.658 -1.323 c
+71.401 -1.01 71.276 -0.544 71.276 0.073 c
+71.276 0.515 l
+71.276 1.11 71.401 1.565 71.658 1.881 c
+71.922 2.194 72.298 2.352 72.79 2.352 c
+73.19 2.352 73.51 2.219 73.745 1.955 c
+73.988 1.698 74.12 1.353 74.142 0.912 c
+73.525 0.912 l
+73.503 1.205 73.429 1.426 73.304 1.573 c
+73.187 1.72 73.015 1.793 72.79 1.793 c
+72.496 1.793 72.279 1.694 72.143 1.5 c
+72.003 1.312 71.93 1.004 71.922 0.573 c
+71.922 0.059 l
+71.922 -0.411 71.989 -0.746 72.128 -0.941 c
+72.275 -1.128 72.496 -1.22 72.79 -1.22 c
+75.539 1.866 m
+75.793 2.19 76.112 2.352 76.494 2.352 c
+77.2 2.352 77.556 1.881 77.567 0.941 c
+77.567 -1.705 l
+76.92 -1.705 l
+76.92 0.912 l
+76.92 1.224 76.865 1.444 76.759 1.573 c
+76.649 1.698 76.494 1.764 76.288 1.764 c
+76.13 1.764 75.983 1.709 75.847 1.602 c
+75.719 1.492 75.616 1.357 75.539 1.191 c
+75.539 -1.705 l
+74.892 -1.705 l
+74.892 3.94 l
+75.539 3.94 l
+h
+80.566 -1.705 m
+80.525 -1.617 80.5 -1.469 80.492 -1.264 c
+80.257 -1.61 79.963 -1.779 79.61 -1.779 c
+79.247 -1.779 78.964 -1.683 78.758 -1.484 c
+78.559 -1.278 78.464 -0.992 78.464 -0.617 c
+78.464 -0.216 78.6 0.103 78.875 0.338 c
+79.147 0.58 79.523 0.706 79.993 0.706 c
+80.477 0.706 l
+80.477 1.132 l
+80.477 1.367 80.423 1.532 80.316 1.631 c
+80.206 1.738 80.044 1.793 79.831 1.793 c
+79.633 1.793 79.471 1.735 79.346 1.617 c
+79.228 1.5 79.17 1.353 79.17 1.176 c
+78.523 1.176 l
+78.523 1.371 78.582 1.562 78.699 1.749 c
+78.824 1.933 78.985 2.08 79.184 2.19 c
+79.39 2.297 79.618 2.352 79.875 2.352 c
+80.275 2.352 80.581 2.249 80.786 2.043 c
+81 1.837 81.113 1.544 81.125 1.161 c
+81.125 -0.852 l
+81.125 -1.157 81.161 -1.422 81.242 -1.646 c
+81.242 -1.705 l
+h
+79.698 -1.191 m
+79.864 -1.191 80.015 -1.147 80.154 -1.058 c
+80.301 -0.97 80.408 -0.86 80.477 -0.721 c
+80.477 0.22 l
+80.111 0.22 l
+79.794 0.22 79.552 0.151 79.375 0.015 c
+79.199 -0.114 79.111 -0.301 79.111 -0.544 c
+79.111 -0.771 79.155 -0.937 79.243 -1.043 c
+79.331 -1.143 79.482 -1.191 79.698 -1.191 c
+82.741 2.278 m
+82.756 1.837 l
+83.01 2.18 83.333 2.352 83.726 2.352 c
+84.431 2.352 84.788 1.881 84.799 0.941 c
+84.799 -1.705 l
+84.152 -1.705 l
+84.152 0.912 l
+84.152 1.224 84.097 1.444 83.991 1.573 c
+83.88 1.698 83.726 1.764 83.521 1.764 c
+83.363 1.764 83.215 1.709 83.079 1.602 c
+82.951 1.492 82.848 1.357 82.771 1.191 c
+82.771 -1.705 l
+82.124 -1.705 l
+82.124 2.278 l
+h
+85.637 0.47 m
+85.637 1.087 85.747 1.551 85.975 1.866 c
+86.199 2.19 86.534 2.352 86.974 2.352 c
+87.375 2.352 87.68 2.176 87.886 1.823 c
+87.93 2.278 l
+88.518 2.278 l
+88.518 -1.749 l
+88.518 -2.238 88.389 -2.616 88.136 -2.881 c
+87.878 -3.146 87.525 -3.278 87.078 -3.278 c
+86.879 -3.278 86.659 -3.227 86.416 -3.131 c
+86.17 -3.032 85.99 -2.911 85.873 -2.763 c
+86.137 -2.323 l
+86.401 -2.587 86.699 -2.72 87.033 -2.72 c
+87.57 -2.72 87.845 -2.425 87.857 -1.837 c
+87.857 -1.309 l
+87.651 -1.624 87.35 -1.779 86.96 -1.779 c
+86.548 -1.779 86.225 -1.627 85.99 -1.323 c
+85.762 -1.01 85.644 -0.559 85.637 0.029 c
+h
+86.299 0.088 m
+86.299 -0.353 86.361 -0.683 86.49 -0.897 c
+86.615 -1.103 86.831 -1.205 87.136 -1.205 c
+87.46 -1.205 87.699 -1.04 87.857 -0.706 c
+87.857 1.278 l
+87.687 1.602 87.448 1.764 87.136 1.764 c
+86.842 1.764 86.625 1.661 86.49 1.455 c
+86.361 1.249 86.299 0.926 86.299 0.485 c
+h
+90.899 -1.779 m
+90.399 -1.779 90.017 -1.631 89.753 -1.338 c
+89.488 -1.043 89.356 -0.61 89.356 -0.029 c
+89.356 0.441 l
+89.356 1.037 89.481 1.503 89.738 1.837 c
+90.002 2.18 90.363 2.352 90.826 2.352 c
+91.285 2.352 91.627 2.198 91.855 1.897 c
+92.09 1.602 92.211 1.139 92.223 0.515 c
+92.223 0.088 l
+90.002 0.088 l
+90.002 0 l
+90.002 -0.434 90.08 -0.746 90.238 -0.941 c
+90.403 -1.128 90.635 -1.22 90.929 -1.22 c
+91.124 -1.22 91.296 -1.187 91.443 -1.117 c
+91.59 -1.04 91.726 -0.922 91.855 -0.764 c
+92.192 -1.176 l
+91.906 -1.58 91.477 -1.779 90.899 -1.779 c
+90.826 1.793 m
+90.55 1.793 90.348 1.698 90.223 1.514 c
+90.095 1.326 90.021 1.037 90.002 0.647 c
+91.575 0.647 l
+91.575 0.735 l
+91.554 1.118 91.487 1.386 91.369 1.544 c
+91.252 1.709 91.068 1.793 90.826 1.793 c
+94.941 -0.691 m
+94.941 -0.544 94.887 -0.422 94.779 -0.324 c
+94.669 -0.228 94.463 -0.11 94.162 0.029 c
+93.817 0.177 93.574 0.298 93.428 0.397 c
+93.281 0.503 93.17 0.621 93.104 0.75 c
+93.034 0.875 93.001 1.033 93.001 1.22 c
+93.001 1.544 93.119 1.812 93.354 2.028 c
+93.589 2.242 93.89 2.352 94.265 2.352 c
+94.648 2.352 94.956 2.238 95.192 2.014 c
+95.427 1.786 95.544 1.5 95.544 1.147 c
+94.897 1.147 l
+94.897 1.323 94.839 1.474 94.721 1.602 c
+94.604 1.727 94.449 1.793 94.265 1.793 c
+94.067 1.793 93.916 1.738 93.81 1.631 c
+93.699 1.532 93.648 1.4 93.648 1.235 c
+93.648 1.106 93.684 1 93.765 0.912 c
+93.842 0.831 94.033 0.728 94.339 0.603 c
+94.816 0.416 95.147 0.228 95.323 0.044 c
+95.5 -0.133 95.589 -0.36 95.589 -0.632 c
+95.589 -0.985 95.463 -1.264 95.221 -1.469 c
+94.986 -1.675 94.669 -1.779 94.28 -1.779 c
+93.858 -1.779 93.52 -1.661 93.266 -1.426 c
+93.009 -1.183 92.884 -0.878 92.884 -0.515 c
+93.53 -0.515 l
+93.538 -0.742 93.607 -0.918 93.736 -1.043 c
+93.861 -1.161 94.045 -1.22 94.28 -1.22 c
+94.493 -1.22 94.655 -1.172 94.765 -1.073 c
+94.883 -0.977 94.941 -0.849 94.941 -0.691 c
+100.189 -1.705 m
+100.148 -1.617 100.123 -1.469 100.115 -1.264 c
+99.88 -1.61 99.587 -1.779 99.234 -1.779 c
+98.87 -1.779 98.587 -1.683 98.381 -1.484 c
+98.183 -1.278 98.087 -0.992 98.087 -0.617 c
+98.087 -0.216 98.224 0.103 98.498 0.338 c
+98.771 0.58 99.145 0.706 99.616 0.706 c
+100.101 0.706 l
+100.101 1.132 l
+100.101 1.367 100.046 1.532 99.939 1.631 c
+99.829 1.738 99.668 1.793 99.454 1.793 c
+99.256 1.793 99.094 1.735 98.969 1.617 c
+98.851 1.5 98.793 1.353 98.793 1.176 c
+98.145 1.176 l
+98.145 1.371 98.205 1.562 98.322 1.749 c
+98.447 1.933 98.609 2.08 98.808 2.19 c
+99.013 2.297 99.241 2.352 99.498 2.352 c
+99.899 2.352 100.204 2.249 100.41 2.043 c
+100.622 1.837 100.736 1.544 100.748 1.161 c
+100.748 -0.852 l
+100.748 -1.157 100.784 -1.422 100.865 -1.646 c
+100.865 -1.705 l
+h
+99.321 -1.191 m
+99.487 -1.191 99.637 -1.147 99.778 -1.058 c
+99.924 -0.97 100.031 -0.86 100.101 -0.721 c
+100.101 0.22 l
+99.733 0.22 l
+99.417 0.22 99.175 0.151 98.999 0.015 c
+98.822 -0.114 98.733 -0.301 98.733 -0.544 c
+98.733 -0.771 98.778 -0.937 98.866 -1.043 c
+98.954 -1.143 99.105 -1.191 99.321 -1.191 c
+101.615 0.47 m
+101.615 1.077 101.725 1.544 101.953 1.866 c
+102.188 2.19 102.515 2.352 102.938 2.352 c
+103.32 2.352 103.618 2.194 103.834 1.881 c
+103.834 3.94 l
+104.481 3.94 l
+104.481 -1.705 l
+103.893 -1.705 l
+103.849 -1.278 l
+103.643 -1.613 103.338 -1.779 102.938 -1.779 c
+102.526 -1.779 102.203 -1.624 101.968 -1.309 c
+101.732 -0.985 101.615 -0.529 101.615 0.059 c
+h
+102.261 0.088 m
+102.261 -0.353 102.324 -0.683 102.453 -0.897 c
+102.589 -1.103 102.809 -1.205 103.114 -1.205 c
+103.437 -1.205 103.676 -1.043 103.834 -0.721 c
+103.834 1.294 l
+103.666 1.606 103.427 1.764 103.114 1.764 c
+102.809 1.764 102.589 1.661 102.453 1.455 c
+102.324 1.249 102.261 0.926 102.261 0.485 c
+h
+105.349 0.47 m
+105.349 1.077 105.459 1.544 105.686 1.866 c
+105.922 2.19 106.249 2.352 106.671 2.352 c
+107.053 2.352 107.352 2.194 107.568 1.881 c
+107.568 3.94 l
+108.215 3.94 l
+108.215 -1.705 l
+107.627 -1.705 l
+107.583 -1.278 l
+107.377 -1.613 107.072 -1.779 106.671 -1.779 c
+106.259 -1.779 105.937 -1.624 105.701 -1.309 c
+105.466 -0.985 105.349 -0.529 105.349 0.059 c
+h
+105.995 0.088 m
+105.995 -0.353 106.058 -0.683 106.186 -0.897 c
+106.322 -1.103 106.542 -1.205 106.847 -1.205 c
+107.171 -1.205 107.41 -1.043 107.568 -0.721 c
+107.568 1.294 l
+107.399 1.606 107.161 1.764 106.847 1.764 c
+106.542 1.764 106.322 1.661 106.186 1.455 c
+106.058 1.249 105.995 0.926 105.995 0.485 c
+h
+110.64 -1.779 m
+110.14 -1.779 109.758 -1.631 109.494 -1.338 c
+109.229 -1.043 109.097 -0.61 109.097 -0.029 c
+109.097 0.441 l
+109.097 1.037 109.222 1.503 109.479 1.837 c
+109.744 2.18 110.103 2.352 110.567 2.352 c
+111.026 2.352 111.368 2.198 111.595 1.897 c
+111.83 1.602 111.952 1.139 111.963 0.515 c
+111.963 0.088 l
+109.744 0.088 l
+109.744 0 l
+109.744 -0.434 109.821 -0.746 109.979 -0.941 c
+110.144 -1.128 110.375 -1.22 110.669 -1.22 c
+110.864 -1.22 111.037 -1.187 111.184 -1.117 c
+111.331 -1.04 111.467 -0.922 111.595 -0.764 c
+111.934 -1.176 l
+111.647 -1.58 111.217 -1.779 110.64 -1.779 c
+110.567 1.793 m
+110.291 1.793 110.089 1.698 109.964 1.514 c
+109.835 1.326 109.762 1.037 109.744 0.647 c
+111.316 0.647 l
+111.316 0.735 l
+111.294 1.118 111.228 1.386 111.111 1.544 c
+110.993 1.709 110.809 1.793 110.567 1.793 c
+112.609 0.47 m
+112.609 1.077 112.72 1.544 112.948 1.866 c
+113.183 2.19 113.511 2.352 113.933 2.352 c
+114.315 2.352 114.612 2.194 114.829 1.881 c
+114.829 3.94 l
+115.476 3.94 l
+115.476 -1.705 l
+114.888 -1.705 l
+114.844 -1.278 l
+114.638 -1.613 114.333 -1.779 113.933 -1.779 c
+113.521 -1.779 113.197 -1.624 112.962 -1.309 c
+112.727 -0.985 112.609 -0.529 112.609 0.059 c
+h
+113.257 0.088 m
+113.257 -0.353 113.319 -0.683 113.448 -0.897 c
+113.584 -1.103 113.804 -1.205 114.109 -1.205 c
+114.432 -1.205 114.671 -1.043 114.829 -0.721 c
+114.829 1.294 l
+114.66 1.606 114.421 1.764 114.109 1.764 c
+113.804 1.764 113.584 1.661 113.448 1.455 c
+113.319 1.249 113.257 0.926 113.257 0.485 c
+h
+119.048 3.234 m
+119.048 2.278 l
+119.651 2.278 l
+119.651 1.749 l
+119.048 1.749 l
+119.048 -0.721 l
+119.048 -0.878 119.07 -0.995 119.121 -1.073 c
+119.18 -1.153 119.269 -1.191 119.386 -1.191 c
+119.474 -1.191 119.562 -1.176 119.651 -1.147 c
+119.651 -1.705 l
+119.504 -1.753 119.35 -1.779 119.194 -1.779 c
+118.938 -1.779 118.743 -1.687 118.607 -1.5 c
+118.467 -1.315 118.401 -1.055 118.401 -0.721 c
+118.401 1.749 l
+117.798 1.749 l
+117.798 2.278 l
+118.401 2.278 l
+118.401 3.234 l
+h
+120.21 0.47 m
+120.21 1.047 120.345 1.503 120.621 1.837 c
+120.904 2.18 121.274 2.352 121.738 2.352 c
+122.197 2.352 122.564 2.183 122.84 1.852 c
+123.123 1.529 123.27 1.081 123.281 0.515 c
+123.281 0.088 l
+123.281 -0.482 123.138 -0.937 122.855 -1.278 c
+122.579 -1.613 122.212 -1.779 121.752 -1.779 c
+121.29 -1.779 120.918 -1.617 120.636 -1.294 c
+120.36 -0.962 120.216 -0.522 120.21 0.029 c
+h
+120.856 0.088 m
+120.856 -0.316 120.933 -0.632 121.091 -0.867 c
+121.257 -1.103 121.477 -1.22 121.752 -1.22 c
+122.319 -1.22 122.612 -0.808 122.635 0.015 c
+122.635 0.47 l
+122.635 0.871 122.55 1.191 122.385 1.426 c
+122.227 1.669 122.01 1.793 121.738 1.793 c
+121.473 1.793 121.257 1.669 121.091 1.426 c
+120.933 1.191 120.856 0.871 120.856 0.47 c
+h
+126.691 3.234 m
+126.691 2.278 l
+127.294 2.278 l
+127.294 1.749 l
+126.691 1.749 l
+126.691 -0.721 l
+126.691 -0.878 126.714 -0.995 126.765 -1.073 c
+126.824 -1.153 126.912 -1.191 127.03 -1.191 c
+127.117 -1.191 127.206 -1.176 127.294 -1.147 c
+127.294 -1.705 l
+127.147 -1.753 126.993 -1.779 126.838 -1.779 c
+126.581 -1.779 126.386 -1.687 126.25 -1.5 c
+126.111 -1.315 126.045 -1.055 126.045 -0.721 c
+126.045 1.749 l
+125.442 1.749 l
+125.442 2.278 l
+126.045 2.278 l
+126.045 3.234 l
+h
+128.705 1.866 m
+128.958 2.19 129.278 2.352 129.66 2.352 c
+130.366 2.352 130.722 1.881 130.734 0.941 c
+130.734 -1.705 l
+130.087 -1.705 l
+130.087 0.912 l
+130.087 1.224 130.032 1.444 129.926 1.573 c
+129.815 1.698 129.66 1.764 129.455 1.764 c
+129.297 1.764 129.149 1.709 129.014 1.602 c
+128.885 1.492 128.783 1.357 128.705 1.191 c
+128.705 -1.705 l
+128.058 -1.705 l
+128.058 3.94 l
+128.705 3.94 l
+h
+133.115 -1.779 m
+132.615 -1.779 132.233 -1.631 131.969 -1.338 c
+131.703 -1.043 131.572 -0.61 131.572 -0.029 c
+131.572 0.441 l
+131.572 1.037 131.697 1.503 131.954 1.837 c
+132.218 2.18 132.578 2.352 133.041 2.352 c
+133.501 2.352 133.843 2.198 134.07 1.897 c
+134.306 1.602 134.427 1.139 134.438 0.515 c
+134.438 0.088 l
+132.218 0.088 l
+132.218 0 l
+132.218 -0.434 132.295 -0.746 132.453 -0.941 c
+132.619 -1.128 132.85 -1.22 133.145 -1.22 c
+133.339 -1.22 133.512 -1.187 133.658 -1.117 c
+133.806 -1.04 133.942 -0.922 134.07 -0.764 c
+134.408 -1.176 l
+134.122 -1.58 133.692 -1.779 133.115 -1.779 c
+133.041 1.793 m
+132.766 1.793 132.563 1.698 132.439 1.514 c
+132.31 1.326 132.237 1.037 132.218 0.647 c
+133.791 0.647 l
+133.791 0.735 l
+133.769 1.118 133.703 1.386 133.585 1.544 c
+133.467 1.709 133.284 1.793 133.041 1.793 c
+138.877 -0.691 m
+138.877 -0.544 138.822 -0.422 138.715 -0.324 c
+138.605 -0.228 138.4 -0.11 138.098 0.029 c
+137.752 0.177 137.51 0.298 137.363 0.397 c
+137.216 0.503 137.106 0.621 137.039 0.75 c
+136.97 0.875 136.937 1.033 136.937 1.22 c
+136.937 1.544 137.054 1.812 137.29 2.028 c
+137.525 2.242 137.826 2.352 138.201 2.352 c
+138.583 2.352 138.892 2.238 139.127 2.014 c
+139.362 1.786 139.48 1.5 139.48 1.147 c
+138.832 1.147 l
+138.832 1.323 138.774 1.474 138.656 1.602 c
+138.539 1.727 138.385 1.793 138.201 1.793 c
+138.003 1.793 137.851 1.738 137.745 1.631 c
+137.635 1.532 137.583 1.4 137.583 1.235 c
+137.583 1.106 137.62 1 137.701 0.912 c
+137.778 0.831 137.97 0.728 138.275 0.603 c
+138.752 0.416 139.083 0.228 139.259 0.044 c
+139.435 -0.133 139.524 -0.36 139.524 -0.632 c
+139.524 -0.985 139.399 -1.264 139.156 -1.469 c
+138.921 -1.675 138.605 -1.779 138.215 -1.779 c
+137.793 -1.779 137.455 -1.661 137.201 -1.426 c
+136.944 -1.183 136.819 -0.878 136.819 -0.515 c
+137.466 -0.515 l
+137.473 -0.742 137.543 -0.918 137.672 -1.043 c
+137.797 -1.161 137.98 -1.22 138.215 -1.22 c
+138.429 -1.22 138.591 -1.172 138.701 -1.073 c
+138.818 -0.977 138.877 -0.849 138.877 -0.691 c
+141.214 3.234 m
+141.214 2.278 l
+141.816 2.278 l
+141.816 1.749 l
+141.214 1.749 l
+141.214 -0.721 l
+141.214 -0.878 141.236 -0.995 141.288 -1.073 c
+141.346 -1.153 141.435 -1.191 141.552 -1.191 c
+141.641 -1.191 141.729 -1.176 141.816 -1.147 c
+141.816 -1.705 l
+141.67 -1.753 141.515 -1.779 141.361 -1.779 c
+141.104 -1.779 140.909 -1.687 140.773 -1.5 c
+140.634 -1.315 140.567 -1.055 140.567 -0.721 c
+140.567 1.749 l
+139.965 1.749 l
+139.965 2.278 l
+140.567 2.278 l
+140.567 3.234 l
+h
+144.625 -1.705 m
+144.584 -1.617 144.558 -1.469 144.551 -1.264 c
+144.316 -1.61 144.022 -1.779 143.669 -1.779 c
+143.305 -1.779 143.022 -1.683 142.816 -1.484 c
+142.618 -1.278 142.522 -0.992 142.522 -0.617 c
+142.522 -0.216 142.658 0.103 142.934 0.338 c
+143.206 0.58 143.58 0.706 144.051 0.706 c
+144.536 0.706 l
+144.536 1.132 l
+144.536 1.367 144.481 1.532 144.374 1.631 c
+144.264 1.738 144.102 1.793 143.889 1.793 c
+143.691 1.793 143.53 1.735 143.404 1.617 c
+143.287 1.5 143.228 1.353 143.228 1.176 c
+142.581 1.176 l
+142.581 1.371 142.64 1.562 142.757 1.749 c
+142.882 1.933 143.044 2.08 143.243 2.19 c
+143.449 2.297 143.676 2.352 143.933 2.352 c
+144.334 2.352 144.639 2.249 144.845 2.043 c
+145.058 1.837 145.172 1.544 145.182 1.161 c
+145.182 -0.852 l
+145.182 -1.157 145.22 -1.422 145.3 -1.646 c
+145.3 -1.705 l
+h
+143.757 -1.191 m
+143.923 -1.191 144.073 -1.147 144.213 -1.058 c
+144.359 -0.97 144.466 -0.86 144.536 -0.721 c
+144.536 0.22 l
+144.168 0.22 l
+143.852 0.22 143.61 0.151 143.434 0.015 c
+143.258 -0.114 143.169 -0.301 143.169 -0.544 c
+143.169 -0.771 143.213 -0.937 143.301 -1.043 c
+143.389 -1.143 143.54 -1.191 143.757 -1.191 c
+146.05 0.47 m
+146.05 1.087 146.161 1.551 146.388 1.866 c
+146.612 2.19 146.946 2.352 147.388 2.352 c
+147.788 2.352 148.093 2.176 148.299 1.823 c
+148.343 2.278 l
+148.931 2.278 l
+148.931 -1.749 l
+148.931 -2.238 148.802 -2.616 148.549 -2.881 c
+148.292 -3.146 147.939 -3.278 147.491 -3.278 c
+147.292 -3.278 147.071 -3.227 146.829 -3.131 c
+146.583 -3.032 146.403 -2.911 146.285 -2.763 c
+146.55 -2.323 l
+146.815 -2.587 147.112 -2.72 147.447 -2.72 c
+147.983 -2.72 148.259 -2.425 148.27 -1.837 c
+148.27 -1.309 l
+148.064 -1.624 147.763 -1.779 147.373 -1.779 c
+146.961 -1.779 146.638 -1.627 146.403 -1.323 c
+146.175 -1.01 146.057 -0.559 146.05 0.029 c
+h
+146.711 0.088 m
+146.711 -0.353 146.774 -0.683 146.903 -0.897 c
+147.027 -1.103 147.245 -1.205 147.549 -1.205 c
+147.873 -1.205 148.112 -1.04 148.27 -0.706 c
+148.27 1.278 l
+148.101 1.602 147.862 1.764 147.549 1.764 c
+147.256 1.764 147.038 1.661 146.903 1.455 c
+146.774 1.249 146.711 0.926 146.711 0.485 c
+h
+150.592 -1.705 -0.646 3.983 re
+150.636 3.322 m
+150.636 3.212 150.607 3.12 150.548 3.042 c
+150.489 2.973 150.394 2.94 150.269 2.94 c
+150.151 2.94 150.055 2.973 149.99 3.042 c
+149.93 3.12 149.901 3.212 149.901 3.322 c
+149.901 3.439 149.93 3.532 149.99 3.601 c
+150.055 3.678 150.151 3.719 150.269 3.719 c
+150.394 3.719 150.489 3.678 150.548 3.601 c
+150.607 3.52 150.636 3.429 150.636 3.322 c
+152.224 2.278 m
+152.238 1.837 l
+152.492 2.18 152.816 2.352 153.209 2.352 c
+153.914 2.352 154.271 1.881 154.281 0.941 c
+154.281 -1.705 l
+153.635 -1.705 l
+153.635 0.912 l
+153.635 1.224 153.579 1.444 153.473 1.573 c
+153.363 1.698 153.209 1.764 153.003 1.764 c
+152.845 1.764 152.698 1.709 152.562 1.602 c
+152.433 1.492 152.33 1.357 152.253 1.191 c
+152.253 -1.705 l
+151.607 -1.705 l
+151.607 2.278 l
+h
+155.119 0.47 m
+155.119 1.087 155.23 1.551 155.457 1.866 c
+155.682 2.19 156.016 2.352 156.457 2.352 c
+156.858 2.352 157.163 2.176 157.369 1.823 c
+157.412 2.278 l
+158 2.278 l
+158 -1.749 l
+158 -2.238 157.872 -2.616 157.618 -2.881 c
+157.361 -3.146 157.008 -3.278 156.56 -3.278 c
+156.361 -3.278 156.141 -3.227 155.898 -3.131 c
+155.652 -3.032 155.472 -2.911 155.355 -2.763 c
+155.619 -2.323 l
+155.883 -2.587 156.182 -2.72 156.516 -2.72 c
+157.053 -2.72 157.328 -2.425 157.339 -1.837 c
+157.339 -1.309 l
+157.134 -1.624 156.832 -1.779 156.442 -1.779 c
+156.031 -1.779 155.707 -1.627 155.472 -1.323 c
+155.245 -1.01 155.127 -0.559 155.119 0.029 c
+h
+155.781 0.088 m
+155.781 -0.353 155.844 -0.683 155.972 -0.897 c
+156.097 -1.103 156.314 -1.205 156.619 -1.205 c
+156.942 -1.205 157.181 -1.04 157.339 -0.706 c
+157.339 1.278 l
+157.17 1.602 156.931 1.764 156.619 1.764 c
+156.325 1.764 156.108 1.661 155.972 1.455 c
+155.844 1.249 155.781 0.926 155.781 0.485 c
+h
+162.719 -1.705 m
+162.678 -1.617 162.653 -1.469 162.645 -1.264 c
+162.41 -1.61 162.116 -1.779 161.763 -1.779 c
+161.4 -1.779 161.117 -1.683 160.911 -1.484 c
+160.712 -1.278 160.617 -0.992 160.617 -0.617 c
+160.617 -0.216 160.753 0.103 161.028 0.338 c
+161.3 0.58 161.675 0.706 162.146 0.706 c
+162.63 0.706 l
+162.63 1.132 l
+162.63 1.367 162.576 1.532 162.469 1.631 c
+162.358 1.738 162.197 1.793 161.984 1.793 c
+161.786 1.793 161.624 1.735 161.499 1.617 c
+161.381 1.5 161.323 1.353 161.323 1.176 c
+160.675 1.176 l
+160.675 1.371 160.735 1.562 160.852 1.749 c
+160.977 1.933 161.138 2.08 161.337 2.19 c
+161.543 2.297 161.77 2.352 162.028 2.352 c
+162.429 2.352 162.734 2.249 162.939 2.043 c
+163.152 1.837 163.266 1.544 163.278 1.161 c
+163.278 -0.852 l
+163.278 -1.157 163.314 -1.422 163.395 -1.646 c
+163.395 -1.705 l
+h
+161.851 -1.191 m
+162.017 -1.191 162.167 -1.147 162.308 -1.058 c
+162.454 -0.97 162.561 -0.86 162.63 -0.721 c
+162.63 0.22 l
+162.263 0.22 l
+161.947 0.22 161.705 0.151 161.528 0.015 c
+161.352 -0.114 161.263 -0.301 161.263 -0.544 c
+161.263 -0.771 161.308 -0.937 161.396 -1.043 c
+161.484 -1.143 161.635 -1.191 161.851 -1.191 c
+165.923 1.661 m
+165.835 1.679 165.736 1.691 165.629 1.691 c
+165.294 1.691 165.059 1.507 164.924 1.147 c
+164.924 -1.705 l
+164.277 -1.705 l
+164.277 2.278 l
+164.909 2.278 l
+164.924 1.866 l
+165.1 2.19 165.342 2.352 165.659 2.352 c
+165.765 2.352 165.853 2.33 165.923 2.294 c
+h
+167.922 -1.779 m
+167.422 -1.779 167.04 -1.631 166.776 -1.338 c
+166.511 -1.043 166.379 -0.61 166.379 -0.029 c
+166.379 0.441 l
+166.379 1.037 166.504 1.503 166.761 1.837 c
+167.025 2.18 167.386 2.352 167.849 2.352 c
+168.309 2.352 168.65 2.198 168.878 1.897 c
+169.113 1.602 169.234 1.139 169.246 0.515 c
+169.246 0.088 l
+167.025 0.088 l
+167.025 0 l
+167.025 -0.434 167.103 -0.746 167.261 -0.941 c
+167.426 -1.128 167.658 -1.22 167.952 -1.22 c
+168.147 -1.22 168.319 -1.187 168.466 -1.117 c
+168.613 -1.04 168.749 -0.922 168.878 -0.764 c
+169.215 -1.176 l
+168.929 -1.58 168.5 -1.779 167.922 -1.779 c
+167.849 1.793 m
+167.573 1.793 167.371 1.698 167.246 1.514 c
+167.118 1.326 167.044 1.037 167.025 0.647 c
+168.598 0.647 l
+168.598 0.735 l
+168.577 1.118 168.51 1.386 168.392 1.544 c
+168.275 1.709 168.091 1.793 167.849 1.793 c
+172.068 -1.705 m
+172.027 -1.617 172.001 -1.469 171.993 -1.264 c
+171.758 -1.61 171.465 -1.779 171.112 -1.779 c
+170.748 -1.779 170.465 -1.683 170.26 -1.484 c
+170.061 -1.278 169.965 -0.992 169.965 -0.617 c
+169.965 -0.216 170.102 0.103 170.377 0.338 c
+170.649 0.58 171.024 0.706 171.494 0.706 c
+171.979 0.706 l
+171.979 1.132 l
+171.979 1.367 171.924 1.532 171.818 1.631 c
+171.707 1.738 171.546 1.793 171.332 1.793 c
+171.134 1.793 170.973 1.735 170.848 1.617 c
+170.73 1.5 170.671 1.353 170.671 1.176 c
+170.025 1.176 l
+170.025 1.371 170.083 1.562 170.2 1.749 c
+170.326 1.933 170.487 2.08 170.686 2.19 c
+170.892 2.297 171.12 2.352 171.376 2.352 c
+171.777 2.352 172.082 2.249 172.288 2.043 c
+172.501 1.837 172.615 1.544 172.626 1.161 c
+172.626 -0.852 l
+172.626 -1.157 172.662 -1.422 172.743 -1.646 c
+172.743 -1.705 l
+h
+171.2 -1.191 m
+171.366 -1.191 171.517 -1.147 171.656 -1.058 c
+171.803 -0.97 171.91 -0.86 171.979 -0.721 c
+171.979 0.22 l
+171.612 0.22 l
+171.295 0.22 171.053 0.151 170.877 0.015 c
+170.7 -0.114 170.612 -0.301 170.612 -0.544 c
+170.612 -0.771 170.657 -0.937 170.744 -1.043 c
+170.833 -1.143 170.983 -1.191 171.2 -1.191 c
+173.67 -1.352 m
+173.67 -1.234 173.703 -1.139 173.772 -1.058 c
+173.838 -0.981 173.942 -0.941 174.081 -0.941 c
+174.228 -0.941 174.335 -0.981 174.405 -1.058 c
+174.482 -1.139 174.522 -1.234 174.522 -1.352 c
+174.522 -1.463 174.482 -1.554 174.405 -1.631 c
+174.335 -1.708 174.228 -1.749 174.081 -1.749 c
+173.942 -1.749 173.838 -1.708 173.772 -1.631 c
+173.703 -1.554 173.67 -1.463 173.67 -1.352 c
+180.784 0 m
+180.755 -0.58 180.593 -1.022 180.299 -1.323 c
+180.005 -1.627 179.586 -1.779 179.049 -1.779 c
+178.52 -1.779 178.094 -1.58 177.771 -1.176 c
+177.454 -0.764 177.3 -0.206 177.3 0.5 c
+177.3 1.47 l
+177.3 2.165 177.462 2.712 177.786 3.117 c
+178.108 3.516 178.553 3.719 179.123 3.719 c
+179.63 3.719 180.027 3.561 180.313 3.248 c
+180.597 2.944 180.755 2.502 180.784 1.926 c
+180.093 1.926 l
+180.064 2.367 179.968 2.679 179.814 2.866 c
+179.667 3.05 179.436 3.146 179.123 3.146 c
+178.748 3.146 178.465 2.999 178.27 2.705 c
+178.072 2.418 177.977 2.003 177.977 1.455 c
+177.977 0.47 l
+177.977 -0.07 178.068 -0.485 178.256 -0.779 c
+178.439 -1.066 178.704 -1.205 179.049 -1.205 c
+179.402 -1.205 179.656 -1.117 179.814 -0.941 c
+179.968 -0.764 180.064 -0.452 180.093 0 c
+h
+181.46 0.47 m
+181.46 1.047 181.596 1.503 181.871 1.837 c
+182.154 2.18 182.526 2.352 182.989 2.352 c
+183.448 2.352 183.816 2.183 184.091 1.852 c
+184.375 1.529 184.521 1.081 184.533 0.515 c
+184.533 0.088 l
+184.533 -0.482 184.389 -0.937 184.105 -1.278 c
+183.831 -1.613 183.463 -1.779 183.004 -1.779 c
+182.54 -1.779 182.169 -1.617 181.886 -1.294 c
+181.611 -0.962 181.468 -0.522 181.46 0.029 c
+h
+182.106 0.088 m
+182.106 -0.316 182.185 -0.632 182.342 -0.867 c
+182.507 -1.103 182.728 -1.22 183.004 -1.22 c
+183.569 -1.22 183.864 -0.808 183.885 0.015 c
+183.885 0.47 l
+183.885 0.871 183.801 1.191 183.635 1.426 c
+183.477 1.669 183.261 1.793 182.989 1.793 c
+182.724 1.793 182.507 1.669 182.342 1.426 c
+182.185 1.191 182.106 0.871 182.106 0.47 c
+h
+185.987 2.278 m
+186.002 1.911 l
+186.245 2.205 186.565 2.352 186.958 2.352 c
+187.398 2.352 187.708 2.153 187.884 1.764 c
+188.138 2.153 188.487 2.352 188.927 2.352 c
+189.662 2.352 190.037 1.889 190.059 0.97 c
+190.059 -1.705 l
+189.412 -1.705 l
+189.412 0.912 l
+189.412 1.205 189.357 1.419 189.25 1.558 c
+189.152 1.694 188.979 1.764 188.736 1.764 c
+188.537 1.764 188.376 1.683 188.251 1.529 c
+188.134 1.382 188.064 1.191 188.045 0.956 c
+188.045 -1.705 l
+187.384 -1.705 l
+187.384 0.941 l
+187.384 1.488 187.163 1.764 186.723 1.764 c
+186.388 1.764 186.153 1.602 186.017 1.278 c
+186.017 -1.705 l
+185.37 -1.705 l
+185.37 2.278 l
+h
+191.646 2.278 m
+191.662 1.911 l
+191.904 2.205 192.223 2.352 192.616 2.352 c
+193.058 2.352 193.366 2.153 193.543 1.764 c
+193.796 2.153 194.145 2.352 194.586 2.352 c
+195.321 2.352 195.696 1.889 195.718 0.97 c
+195.718 -1.705 l
+195.072 -1.705 l
+195.072 0.912 l
+195.072 1.205 195.016 1.419 194.91 1.558 c
+194.81 1.694 194.638 1.764 194.395 1.764 c
+194.197 1.764 194.035 1.683 193.91 1.529 c
+193.792 1.382 193.723 1.191 193.705 0.956 c
+193.705 -1.705 l
+193.043 -1.705 l
+193.043 0.941 l
+193.043 1.488 192.822 1.764 192.381 1.764 c
+192.047 1.764 191.812 1.602 191.676 1.278 c
+191.676 -1.705 l
+191.029 -1.705 l
+191.029 2.278 l
+h
+197.393 -1.705 -0.646 3.983 re
+197.438 3.322 m
+197.438 3.212 197.408 3.12 197.35 3.042 c
+197.291 2.973 197.196 2.94 197.071 2.94 c
+196.953 2.94 196.857 2.973 196.791 3.042 c
+196.732 3.12 196.703 3.212 196.703 3.322 c
+196.703 3.439 196.732 3.532 196.791 3.601 c
+196.857 3.678 196.953 3.719 197.071 3.719 c
+197.196 3.719 197.291 3.678 197.35 3.601 c
+197.408 3.52 197.438 3.429 197.438 3.322 c
+199.261 3.234 m
+199.261 2.278 l
+199.863 2.278 l
+199.863 1.749 l
+199.261 1.749 l
+199.261 -0.721 l
+199.261 -0.878 199.282 -0.995 199.334 -1.073 c
+199.392 -1.153 199.481 -1.191 199.599 -1.191 c
+199.687 -1.191 199.775 -1.176 199.863 -1.147 c
+199.863 -1.705 l
+199.716 -1.753 199.562 -1.779 199.408 -1.779 c
+199.151 -1.779 198.956 -1.687 198.82 -1.5 c
+198.68 -1.315 198.614 -1.055 198.614 -0.721 c
+198.614 1.749 l
+198.012 1.749 l
+198.012 2.278 l
+198.614 2.278 l
+198.614 3.234 l
+h
+f
+Q
+q 1 0 0 1 503.1638 213.6016 cm
+0 0 m
+0.029 -0.368 l
+0.264 -0.073 0.573 0.073 0.956 0.073 c
+1.356 0.073 1.635 -0.11 1.793 -0.47 c
+2.028 -0.11 2.356 0.073 2.778 0.073 c
+3.472 0.073 3.825 -0.412 3.836 -1.382 c
+3.836 -3.983 l
+2.807 -3.983 l
+2.807 -1.44 l
+2.807 -1.216 2.77 -1.055 2.705 -0.956 c
+2.645 -0.86 2.535 -0.808 2.381 -0.808 c
+2.182 -0.808 2.043 -0.926 1.955 -1.161 c
+1.955 -3.983 l
+0.912 -3.983 l
+0.912 -1.455 l
+0.912 -1.22 0.881 -1.055 0.823 -0.956 c
+0.764 -0.86 0.654 -0.808 0.499 -0.808 c
+0.324 -0.808 0.18 -0.904 0.073 -1.087 c
+0.073 -3.983 l
+-0.97 -3.983 l
+-0.97 0 l
+h
+6.57 -3.616 m
+6.354 -3.91 6.063 -4.057 5.703 -4.057 c
+5.34 -4.057 5.06 -3.936 4.866 -3.69 c
+4.678 -3.436 4.586 -3.069 4.586 -2.587 c
+4.586 0 l
+5.629 0 l
+5.629 -2.602 l
+5.629 -2.995 5.754 -3.19 6.012 -3.19 c
+6.247 -3.19 6.416 -3.087 6.526 -2.881 c
+6.526 0 l
+7.57 0 l
+7.57 -3.983 l
+6.6 -3.983 l
+h
+10.084 -2.911 m
+10.084 -2.822 10.04 -2.745 9.951 -2.675 c
+9.863 -2.598 9.675 -2.495 9.392 -2.367 c
+8.959 -2.19 8.661 -2.01 8.496 -1.823 c
+8.338 -1.639 8.261 -1.407 8.261 -1.132 c
+8.261 -0.79 8.382 -0.507 8.628 -0.279 c
+8.882 -0.044 9.22 0.073 9.643 0.073 c
+10.073 0.073 10.421 -0.04 10.686 -0.264 c
+10.951 -0.492 11.083 -0.794 11.083 -1.176 c
+10.04 -1.176 l
+10.04 -0.852 9.899 -0.69 9.628 -0.69 c
+9.517 -0.69 9.429 -0.727 9.363 -0.794 c
+9.294 -0.864 9.261 -0.962 9.261 -1.087 c
+9.261 -1.176 9.297 -1.257 9.378 -1.323 c
+9.455 -1.382 9.635 -1.477 9.922 -1.602 c
+10.352 -1.76 10.649 -1.937 10.819 -2.132 c
+10.994 -2.319 11.083 -2.568 11.083 -2.881 c
+11.083 -3.234 10.951 -3.52 10.686 -3.734 c
+10.421 -3.95 10.073 -4.057 9.643 -4.057 c
+9.348 -4.057 9.088 -4.002 8.864 -3.896 c
+8.636 -3.778 8.459 -3.616 8.334 -3.41 c
+8.216 -3.204 8.158 -2.984 8.158 -2.749 c
+9.143 -2.749 l
+9.143 -2.936 9.18 -3.072 9.261 -3.16 c
+9.348 -3.248 9.481 -3.293 9.658 -3.293 c
+9.94 -3.293 10.084 -3.167 10.084 -2.911 c
+12.95 0.97 m
+12.95 0 l
+13.479 0 l
+13.479 -0.794 l
+12.95 -0.794 l
+12.95 -2.764 l
+12.95 -2.921 12.968 -3.028 13.008 -3.087 c
+13.057 -3.146 13.141 -3.175 13.259 -3.175 c
+13.365 -3.175 13.45 -3.167 13.508 -3.146 c
+13.508 -3.954 l
+13.332 -4.02 13.141 -4.057 12.935 -4.057 c
+12.259 -4.057 11.913 -3.671 11.906 -2.896 c
+11.906 -0.794 l
+11.451 -0.794 l
+11.451 0 l
+11.906 0 l
+11.906 0.97 l
+h
+f
+Q
+q 1 0 0 1 468.3414 203.8415 cm
+0 0 m
+0.225 0.283 0.5 0.426 0.823 0.426 c
+1.183 0.426 1.459 0.297 1.646 0.044 c
+1.841 -0.214 1.941 -0.595 1.941 -1.103 c
+1.941 -3.63 l
+0.897 -3.63 l
+0.897 -1.118 l
+0.897 -0.882 0.857 -0.717 0.779 -0.617 c
+0.709 -0.511 0.595 -0.455 0.441 -0.455 c
+0.254 -0.455 0.107 -0.54 0 -0.706 c
+0 -3.63 l
+-1.043 -3.63 l
+-1.043 2.014 l
+0 2.014 l
+h
+4.572 -3.63 m
+4.542 -3.572 4.513 -3.469 4.484 -3.322 c
+4.296 -3.58 4.046 -3.705 3.734 -3.705 c
+3.4 -3.705 3.12 -3.597 2.896 -3.381 c
+2.679 -3.156 2.573 -2.866 2.573 -2.514 c
+2.573 -2.102 2.705 -1.786 2.97 -1.558 c
+3.234 -1.323 3.616 -1.205 4.116 -1.205 c
+4.439 -1.205 l
+4.439 -0.882 l
+4.439 -0.706 4.403 -0.584 4.337 -0.515 c
+4.278 -0.437 4.189 -0.397 4.072 -0.397 c
+3.815 -0.397 3.69 -0.551 3.69 -0.852 c
+2.646 -0.852 l
+2.646 -0.482 2.782 -0.177 3.057 0.058 c
+3.329 0.301 3.679 0.426 4.102 0.426 c
+4.542 0.426 4.881 0.309 5.116 0.073 c
+5.358 -0.154 5.483 -0.478 5.483 -0.897 c
+5.483 -2.764 l
+5.483 -3.109 5.531 -3.377 5.63 -3.572 c
+5.63 -3.63 l
+h
+3.969 -2.881 m
+4.075 -2.881 4.167 -2.863 4.248 -2.822 c
+4.337 -2.774 4.399 -2.716 4.439 -2.646 c
+4.439 -1.823 l
+4.189 -1.823 l
+4.013 -1.823 3.87 -1.881 3.763 -1.999 c
+3.664 -2.109 3.616 -2.256 3.616 -2.44 c
+3.616 -2.734 3.734 -2.881 3.969 -2.881 c
+7.482 -2.161 m
+7.982 0.353 l
+9.07 0.353 l
+7.967 -3.63 l
+6.982 -3.63 l
+5.88 0.353 l
+6.968 0.353 l
+h
+11.113 -3.705 m
+10.583 -3.705 10.165 -3.549 9.864 -3.234 c
+9.569 -2.911 9.422 -2.451 9.422 -1.852 c
+9.422 -1.544 l
+9.422 -0.919 9.559 -0.434 9.834 -0.088 c
+10.106 0.254 10.5 0.426 11.009 0.426 c
+11.51 0.426 11.881 0.264 12.127 -0.059 c
+12.381 -0.382 12.513 -0.86 12.524 -1.484 c
+12.524 -1.984 l
+10.452 -1.984 l
+10.469 -2.278 10.533 -2.495 10.643 -2.631 c
+10.76 -2.77 10.94 -2.837 11.186 -2.837 c
+11.528 -2.837 11.819 -2.72 12.054 -2.484 c
+12.465 -3.117 l
+12.337 -3.293 12.149 -3.436 11.907 -3.543 c
+11.66 -3.649 11.396 -3.705 11.113 -3.705 c
+10.466 -1.264 m
+11.495 -1.264 l
+11.495 -1.161 l
+11.495 -0.926 11.454 -0.75 11.377 -0.632 c
+11.308 -0.507 11.179 -0.441 10.995 -0.441 c
+10.819 -0.441 10.687 -0.511 10.598 -0.647 c
+10.517 -0.775 10.473 -0.981 10.466 -1.264 c
+f
+Q
+q 1 0 0 1 485.3189 200.2112 cm
+0 0 m
+-0.04 0.087 -0.066 0.235 -0.073 0.44 c
+-0.309 0.095 -0.602 -0.074 -0.955 -0.074 c
+-1.319 -0.074 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.102 0.712 -2.102 1.087 c
+-2.102 1.488 -1.966 1.807 -1.691 2.042 c
+-1.419 2.285 -1.043 2.41 -0.573 2.41 c
+-0.088 2.41 l
+-0.088 2.836 l
+-0.088 3.072 -0.143 3.237 -0.249 3.336 c
+-0.36 3.443 -0.521 3.498 -0.735 3.498 c
+-0.933 3.498 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.984 3.266 -1.866 3.453 c
+-1.741 3.638 -1.58 3.785 -1.382 3.895 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.29 4.056 0.015 3.954 0.221 3.748 c
+0.434 3.542 0.547 3.248 0.559 2.865 c
+0.559 0.852 l
+0.559 0.548 0.595 0.282 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.514 m
+-0.702 0.514 -0.551 0.558 -0.411 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.984 c
+-0.088 1.925 l
+-0.455 1.925 l
+-0.771 1.925 -1.014 1.855 -1.19 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.234 0.562 -1.084 0.514 -0.867 0.514 c
+3.896 3.983 m
+3.91 3.615 l
+4.152 3.91 4.472 4.056 4.865 4.056 c
+5.307 4.056 5.615 3.858 5.791 3.469 c
+6.045 3.858 6.394 4.056 6.835 4.056 c
+7.57 4.056 7.945 3.594 7.967 2.675 c
+7.967 0 l
+7.32 0 l
+7.32 2.616 l
+7.32 2.91 7.265 3.123 7.159 3.262 c
+7.059 3.399 6.887 3.469 6.644 3.469 c
+6.446 3.469 6.284 3.388 6.159 3.233 c
+6.041 3.087 5.972 2.896 5.953 2.66 c
+5.953 0 l
+5.292 0 l
+5.292 2.645 l
+5.292 3.193 5.072 3.469 4.63 3.469 c
+4.296 3.469 4.061 3.307 3.925 2.983 c
+3.925 0 l
+3.278 0 l
+3.278 3.983 l
+h
+10.363 -0.074 m
+9.864 -0.074 9.481 0.073 9.216 0.367 c
+8.952 0.661 8.819 1.095 8.819 1.675 c
+8.819 2.146 l
+8.819 2.741 8.944 3.208 9.201 3.542 c
+9.467 3.884 9.827 4.056 10.29 4.056 c
+10.749 4.056 11.09 3.902 11.318 3.601 c
+11.553 3.307 11.675 2.844 11.686 2.219 c
+11.686 1.793 l
+9.467 1.793 l
+9.467 1.705 l
+9.467 1.271 9.544 0.959 9.702 0.764 c
+9.866 0.577 10.099 0.484 10.392 0.484 c
+10.587 0.484 10.76 0.517 10.907 0.588 c
+11.054 0.665 11.19 0.783 11.318 0.941 c
+11.657 0.529 l
+11.37 0.124 10.94 -0.074 10.363 -0.074 c
+10.29 3.498 m
+10.014 3.498 9.812 3.403 9.687 3.218 c
+9.558 3.031 9.485 2.741 9.467 2.352 c
+11.04 2.352 l
+11.04 2.439 l
+11.017 2.822 10.951 3.09 10.834 3.248 c
+10.716 3.414 10.533 3.498 10.29 3.498 c
+14.406 1.014 m
+14.406 1.161 14.35 1.282 14.244 1.381 c
+14.134 1.477 13.928 1.595 13.627 1.734 c
+13.28 1.881 13.039 2.003 12.891 2.102 c
+12.744 2.208 12.634 2.326 12.568 2.454 c
+12.498 2.58 12.465 2.738 12.465 2.925 c
+12.465 3.248 12.582 3.516 12.818 3.733 c
+13.053 3.946 13.355 4.056 13.729 4.056 c
+14.111 4.056 14.42 3.943 14.655 3.719 c
+14.89 3.491 15.008 3.204 15.008 2.851 c
+14.361 2.851 l
+14.361 3.027 14.302 3.179 14.184 3.307 c
+14.067 3.432 13.913 3.498 13.729 3.498 c
+13.531 3.498 13.38 3.443 13.274 3.336 c
+13.163 3.237 13.112 3.104 13.112 2.94 c
+13.112 2.811 13.149 2.705 13.23 2.616 c
+13.307 2.535 13.498 2.433 13.802 2.308 c
+14.28 2.12 14.611 1.932 14.788 1.749 c
+14.963 1.572 15.052 1.344 15.052 1.072 c
+15.052 0.72 14.927 0.44 14.685 0.235 c
+14.45 0.029 14.134 -0.074 13.744 -0.074 c
+13.321 -0.074 12.983 0.043 12.729 0.278 c
+12.472 0.521 12.347 0.827 12.347 1.19 c
+12.994 1.19 l
+13.002 0.962 13.072 0.786 13.199 0.661 c
+13.325 0.544 13.509 0.484 13.744 0.484 c
+13.957 0.484 14.119 0.532 14.229 0.631 c
+14.346 0.727 14.406 0.856 14.406 1.014 c
+17.83 1.014 m
+17.83 1.161 17.775 1.282 17.668 1.381 c
+17.558 1.477 17.353 1.595 17.051 1.734 c
+16.706 1.881 16.463 2.003 16.316 2.102 c
+16.169 2.208 16.059 2.326 15.993 2.454 c
+15.923 2.58 15.89 2.738 15.89 2.925 c
+15.89 3.248 16.008 3.516 16.243 3.733 c
+16.478 3.946 16.779 4.056 17.154 4.056 c
+17.536 4.056 17.845 3.943 18.08 3.719 c
+18.315 3.491 18.433 3.204 18.433 2.851 c
+17.786 2.851 l
+17.786 3.027 17.727 3.179 17.61 3.307 c
+17.492 3.432 17.338 3.498 17.154 3.498 c
+16.956 3.498 16.805 3.443 16.698 3.336 c
+16.588 3.237 16.536 3.104 16.536 2.94 c
+16.536 2.811 16.573 2.705 16.654 2.616 c
+16.731 2.535 16.922 2.433 17.228 2.308 c
+17.706 2.12 18.036 1.932 18.213 1.749 c
+18.389 1.572 18.477 1.344 18.477 1.072 c
+18.477 0.72 18.352 0.44 18.109 0.235 c
+17.874 0.029 17.558 -0.074 17.168 -0.074 c
+16.746 -0.074 16.408 0.043 16.154 0.278 c
+15.898 0.521 15.772 0.827 15.772 1.19 c
+16.419 1.19 l
+16.426 0.962 16.496 0.786 16.625 0.661 c
+16.75 0.544 16.933 0.484 17.168 0.484 c
+17.382 0.484 17.544 0.532 17.654 0.631 c
+17.772 0.727 17.83 0.856 17.83 1.014 c
+21.358 0 m
+21.317 0.087 21.292 0.235 21.284 0.44 c
+21.049 0.095 20.756 -0.074 20.403 -0.074 c
+20.039 -0.074 19.755 0.022 19.549 0.22 c
+19.352 0.426 19.256 0.712 19.256 1.087 c
+19.256 1.488 19.392 1.807 19.667 2.042 c
+19.94 2.285 20.314 2.41 20.785 2.41 c
+21.27 2.41 l
+21.27 2.836 l
+21.27 3.072 21.215 3.237 21.108 3.336 c
+20.998 3.443 20.836 3.498 20.623 3.498 c
+20.424 3.498 20.263 3.439 20.137 3.322 c
+20.02 3.204 19.962 3.057 19.962 2.881 c
+19.314 2.881 l
+19.314 3.075 19.374 3.266 19.491 3.453 c
+19.616 3.638 19.778 3.785 19.977 3.895 c
+20.182 4.002 20.41 4.056 20.667 4.056 c
+21.068 4.056 21.373 3.954 21.579 3.748 c
+21.791 3.542 21.905 3.248 21.916 2.865 c
+21.916 0.852 l
+21.916 0.548 21.953 0.282 22.034 0.058 c
+22.034 0 l
+h
+20.49 0.514 m
+20.656 0.514 20.806 0.558 20.947 0.646 c
+21.093 0.735 21.2 0.845 21.27 0.984 c
+21.27 1.925 l
+20.902 1.925 l
+20.586 1.925 20.343 1.855 20.168 1.72 c
+19.991 1.591 19.902 1.404 19.902 1.161 c
+19.902 0.933 19.946 0.768 20.035 0.661 c
+20.123 0.562 20.274 0.514 20.49 0.514 c
+22.784 2.175 m
+22.784 2.792 22.894 3.256 23.121 3.571 c
+23.346 3.895 23.68 4.056 24.121 4.056 c
+24.522 4.056 24.827 3.881 25.033 3.528 c
+25.076 3.983 l
+25.664 3.983 l
+25.664 -0.044 l
+25.664 -0.533 25.536 -0.912 25.282 -1.176 c
+25.026 -1.441 24.673 -1.573 24.224 -1.573 c
+24.026 -1.573 23.805 -1.522 23.563 -1.426 c
+23.316 -1.327 23.137 -1.206 23.019 -1.058 c
+23.283 -0.618 l
+23.548 -0.882 23.846 -1.015 24.18 -1.015 c
+24.717 -1.015 24.993 -0.721 25.003 -0.133 c
+25.003 0.396 l
+24.798 0.081 24.496 -0.074 24.106 -0.074 c
+23.695 -0.074 23.372 0.077 23.137 0.382 c
+22.909 0.694 22.791 1.146 22.784 1.734 c
+h
+23.445 1.793 m
+23.445 1.352 23.507 1.022 23.636 0.808 c
+23.761 0.602 23.978 0.5 24.283 0.5 c
+24.606 0.5 24.845 0.665 25.003 0.999 c
+25.003 2.983 l
+24.835 3.307 24.596 3.469 24.283 3.469 c
+23.989 3.469 23.772 3.366 23.636 3.16 c
+23.507 2.954 23.445 2.63 23.445 2.19 c
+h
+28.046 -0.074 m
+27.546 -0.074 27.164 0.073 26.9 0.367 c
+26.635 0.661 26.502 1.095 26.502 1.675 c
+26.502 2.146 l
+26.502 2.741 26.628 3.208 26.884 3.542 c
+27.15 3.884 27.509 4.056 27.973 4.056 c
+28.432 4.056 28.773 3.902 29.001 3.601 c
+29.236 3.307 29.358 2.844 29.369 2.219 c
+29.369 1.793 l
+27.15 1.793 l
+27.15 1.705 l
+27.15 1.271 27.227 0.959 27.385 0.764 c
+27.55 0.577 27.781 0.484 28.075 0.484 c
+28.27 0.484 28.443 0.517 28.59 0.588 c
+28.737 0.665 28.873 0.783 29.001 0.941 c
+29.34 0.529 l
+29.053 0.124 28.623 -0.074 28.046 -0.074 c
+27.973 3.498 m
+27.697 3.498 27.495 3.403 27.37 3.218 c
+27.241 3.031 27.168 2.741 27.15 2.352 c
+28.722 2.352 l
+28.722 2.439 l
+28.7 2.822 28.634 3.09 28.517 3.248 c
+28.399 3.414 28.215 3.498 27.973 3.498 c
+30.883 1.514 m
+30.295 1.514 l
+30.25 5.35 l
+30.942 5.35 l
+h
+30.603 0.735 m
+30.728 0.735 30.824 0.694 30.883 0.617 c
+30.949 0.548 30.986 0.455 30.986 0.338 c
+30.986 0.228 30.949 0.135 30.883 0.058 c
+30.824 -0.008 30.728 -0.044 30.603 -0.044 c
+30.486 -0.044 30.391 -0.008 30.325 0.058 c
+30.265 0.135 30.236 0.228 30.236 0.338 c
+30.236 0.455 30.265 0.548 30.325 0.617 c
+30.391 0.694 30.486 0.735 30.603 0.735 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 170.897 234.667 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 164.0623 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.775 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.683 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.941 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.028 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.244 1.933 5.376 2.051 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.635 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.177 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.116 l
+14.494 3.116 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.456 14.552 -0.515 c
+14.589 -0.566 14.645 -0.611 14.714 -0.647 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.688 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.603 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.279 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.085 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.456 c
+13.586 -0.36 13.582 -0.264 13.582 -0.177 c
+h
+24.22 -0.25 m
+24.22 -0.419 24.18 -0.57 24.103 -0.706 c
+24.033 -0.834 23.931 -0.948 23.794 -1.044 c
+23.666 -1.132 23.504 -1.202 23.31 -1.249 c
+23.121 -1.297 22.905 -1.323 22.662 -1.323 c
+22.435 -1.323 22.236 -1.309 22.06 -1.279 c
+21.883 -1.249 21.725 -1.202 21.59 -1.132 c
+21.45 -1.055 21.34 -0.956 21.251 -0.838 c
+21.163 -0.721 21.093 -0.574 21.045 -0.397 c
+21.854 -0.279 l
+21.872 -0.379 21.902 -0.456 21.942 -0.515 c
+21.99 -0.574 22.049 -0.617 22.119 -0.647 c
+22.185 -0.676 22.265 -0.702 22.354 -0.721 c
+22.442 -0.732 22.545 -0.735 22.662 -0.735 c
+22.758 -0.735 22.853 -0.732 22.942 -0.721 c
+23.03 -0.702 23.107 -0.676 23.177 -0.647 c
+23.243 -0.617 23.295 -0.58 23.324 -0.53 c
+23.36 -0.482 23.383 -0.419 23.383 -0.339 c
+23.383 -0.243 23.353 -0.169 23.295 -0.118 c
+23.243 -0.07 23.177 -0.029 23.089 0 c
+23.001 0.037 22.89 0.066 22.766 0.088 c
+22.647 0.118 22.516 0.147 22.369 0.176 c
+22.229 0.213 22.09 0.253 21.942 0.294 c
+21.803 0.341 21.677 0.405 21.56 0.484 c
+21.45 0.562 21.361 0.661 21.296 0.779 c
+21.226 0.897 21.193 1.047 21.193 1.234 c
+21.193 1.389 21.222 1.532 21.28 1.66 c
+21.347 1.797 21.442 1.911 21.56 1.999 c
+21.685 2.087 21.843 2.153 22.03 2.205 c
+22.215 2.252 22.427 2.278 22.662 2.278 c
+22.846 2.278 23.023 2.256 23.192 2.219 c
+23.357 2.19 23.504 2.135 23.632 2.057 c
+23.757 1.988 23.868 1.889 23.956 1.764 c
+24.044 1.646 24.103 1.502 24.133 1.338 c
+23.339 1.264 l
+23.316 1.341 23.287 1.404 23.25 1.455 c
+23.21 1.514 23.162 1.558 23.104 1.587 c
+23.052 1.624 22.99 1.65 22.913 1.66 c
+22.832 1.668 22.751 1.675 22.662 1.675 c
+22.446 1.675 22.284 1.646 22.177 1.587 c
+22.067 1.536 22.016 1.448 22.016 1.323 c
+22.016 1.242 22.034 1.18 22.074 1.132 c
+22.122 1.08 22.185 1.043 22.265 1.014 c
+22.354 0.985 22.45 0.955 22.56 0.926 c
+22.666 0.904 22.788 0.881 22.927 0.852 c
+23.081 0.823 23.239 0.783 23.397 0.735 c
+23.551 0.683 23.692 0.621 23.809 0.544 c
+23.927 0.463 24.022 0.36 24.103 0.235 c
+24.18 0.106 24.22 -0.056 24.22 -0.25 c
+25.783 1.602 m
+25.238 1.602 l
+25.238 2.219 l
+25.826 2.219 l
+26.106 3.116 l
+26.679 3.116 l
+26.679 2.219 l
+27.914 2.219 l
+27.914 1.602 l
+26.679 1.602 l
+26.679 -0.103 l
+26.679 -0.324 l
+26.686 -0.393 26.709 -0.456 26.738 -0.515 c
+26.774 -0.566 26.83 -0.611 26.9 -0.647 c
+26.977 -0.676 27.09 -0.691 27.237 -0.691 c
+27.374 -0.691 27.509 -0.688 27.649 -0.676 c
+27.785 -0.658 27.917 -0.632 28.046 -0.603 c
+28.046 -1.205 l
+27.965 -1.216 27.888 -1.231 27.811 -1.249 c
+27.73 -1.261 27.653 -1.268 27.576 -1.279 c
+27.495 -1.286 27.407 -1.294 27.312 -1.294 c
+27.223 -1.301 27.123 -1.309 27.017 -1.309 c
+26.83 -1.309 26.668 -1.294 26.532 -1.264 c
+26.404 -1.228 26.29 -1.183 26.194 -1.132 c
+26.106 -1.085 26.032 -1.025 25.974 -0.956 c
+25.915 -0.879 25.87 -0.802 25.841 -0.721 c
+25.812 -0.632 25.789 -0.544 25.783 -0.456 c
+25.772 -0.36 25.768 -0.264 25.768 -0.177 c
+h
+30.196 -1.323 m
+30.027 -1.323 29.876 -1.301 29.741 -1.264 c
+29.612 -1.216 29.498 -1.147 29.402 -1.058 c
+29.313 -0.97 29.244 -0.864 29.196 -0.735 c
+29.145 -0.599 29.122 -0.449 29.122 -0.279 c
+29.122 -0.073 29.155 0.095 29.226 0.235 c
+29.292 0.382 29.388 0.492 29.505 0.573 c
+29.63 0.661 29.773 0.723 29.931 0.764 c
+30.096 0.801 30.273 0.827 30.46 0.837 c
+31.181 0.852 l
+31.181 1.029 l
+31.181 1.147 31.17 1.249 31.152 1.338 c
+31.129 1.425 31.096 1.492 31.048 1.543 c
+31.008 1.602 30.96 1.639 30.901 1.66 c
+30.842 1.679 30.776 1.691 30.71 1.691 c
+30.641 1.691 30.578 1.679 30.519 1.66 c
+30.468 1.65 30.42 1.624 30.372 1.587 c
+30.331 1.558 30.298 1.506 30.269 1.44 c
+30.248 1.382 30.233 1.301 30.225 1.205 c
+29.284 1.249 l
+29.313 1.396 29.358 1.532 29.417 1.66 c
+29.483 1.786 29.579 1.896 29.696 1.984 c
+29.814 2.08 29.953 2.153 30.122 2.205 c
+30.298 2.252 30.504 2.278 30.74 2.278 c
+31.181 2.278 31.511 2.168 31.74 1.955 c
+31.975 1.749 32.092 1.44 32.092 1.029 c
+32.092 -0.235 l
+32.092 -0.456 l
+32.099 -0.515 32.114 -0.57 32.136 -0.617 c
+32.154 -0.658 32.184 -0.691 32.224 -0.721 c
+32.261 -0.742 32.313 -0.75 32.371 -0.75 c
+32.438 -0.75 32.507 -0.746 32.577 -0.735 c
+32.577 -1.22 l
+32.519 -1.231 32.463 -1.242 32.415 -1.249 c
+32.375 -1.261 32.334 -1.268 32.297 -1.279 c
+32.257 -1.286 32.213 -1.294 32.166 -1.294 c
+32.114 -1.301 32.055 -1.309 31.989 -1.309 c
+31.761 -1.309 31.596 -1.257 31.489 -1.147 c
+31.379 -1.029 31.316 -0.864 31.298 -0.647 c
+31.283 -0.647 l
+31.214 -0.757 31.144 -0.852 31.077 -0.941 c
+31.008 -1.022 30.931 -1.087 30.842 -1.147 c
+30.755 -1.205 30.655 -1.249 30.549 -1.279 c
+30.449 -1.309 30.331 -1.323 30.196 -1.323 c
+31.181 0.353 m
+30.755 0.338 l
+30.655 0.338 30.564 0.33 30.475 0.324 c
+30.394 0.312 30.328 0.287 30.269 0.249 c
+30.211 0.209 30.159 0.151 30.122 0.073 c
+30.082 0.004 30.063 -0.088 30.063 -0.206 c
+30.063 -0.375 30.096 -0.497 30.167 -0.574 c
+30.233 -0.654 30.331 -0.691 30.46 -0.691 c
+30.567 -0.691 30.666 -0.669 30.755 -0.617 c
+30.85 -0.57 30.931 -0.507 30.99 -0.426 c
+31.056 -0.349 31.107 -0.262 31.137 -0.162 c
+31.166 -0.056 31.181 0.058 31.181 0.176 c
+h
+36.403 -0.25 m
+36.403 -0.419 36.362 -0.57 36.285 -0.706 c
+36.215 -0.834 36.112 -0.948 35.976 -1.044 c
+35.848 -1.132 35.686 -1.202 35.491 -1.249 c
+35.304 -1.297 35.087 -1.323 34.844 -1.323 c
+34.616 -1.323 34.418 -1.309 34.242 -1.279 c
+34.065 -1.249 33.907 -1.202 33.772 -1.132 c
+33.631 -1.055 33.521 -0.956 33.433 -0.838 c
+33.345 -0.721 33.275 -0.574 33.228 -0.397 c
+34.036 -0.279 l
+34.055 -0.379 34.084 -0.456 34.124 -0.515 c
+34.171 -0.574 34.231 -0.617 34.3 -0.647 c
+34.367 -0.676 34.447 -0.702 34.535 -0.721 c
+34.624 -0.732 34.726 -0.735 34.844 -0.735 c
+34.94 -0.735 35.035 -0.732 35.123 -0.721 c
+35.212 -0.702 35.289 -0.676 35.359 -0.647 c
+35.425 -0.617 35.476 -0.58 35.505 -0.53 c
+35.543 -0.482 35.565 -0.419 35.565 -0.339 c
+35.565 -0.243 35.535 -0.169 35.476 -0.118 c
+35.425 -0.07 35.359 -0.029 35.27 0 c
+35.183 0.037 35.073 0.066 34.948 0.088 c
+34.83 0.118 34.697 0.147 34.551 0.176 c
+34.41 0.213 34.271 0.253 34.124 0.294 c
+33.984 0.341 33.859 0.405 33.742 0.484 c
+33.631 0.562 33.544 0.661 33.477 0.779 c
+33.408 0.897 33.375 1.047 33.375 1.234 c
+33.375 1.389 33.404 1.532 33.463 1.66 c
+33.529 1.797 33.624 1.911 33.742 1.999 c
+33.867 2.087 34.025 2.153 34.212 2.205 c
+34.396 2.252 34.609 2.278 34.844 2.278 c
+35.028 2.278 35.204 2.256 35.374 2.219 c
+35.539 2.19 35.686 2.135 35.815 2.057 c
+35.939 1.988 36.05 1.889 36.138 1.764 c
+36.226 1.646 36.285 1.502 36.314 1.338 c
+35.52 1.264 l
+35.499 1.341 35.469 1.404 35.432 1.455 c
+35.392 1.514 35.345 1.558 35.285 1.587 c
+35.234 1.624 35.171 1.65 35.094 1.66 c
+35.013 1.668 34.932 1.675 34.844 1.675 c
+34.628 1.675 34.466 1.646 34.36 1.587 c
+34.249 1.536 34.198 1.448 34.198 1.323 c
+34.198 1.242 34.216 1.18 34.256 1.132 c
+34.304 1.08 34.367 1.043 34.447 1.014 c
+34.535 0.985 34.631 0.955 34.742 0.926 c
+34.848 0.904 34.969 0.881 35.109 0.852 c
+35.264 0.823 35.422 0.783 35.58 0.735 c
+35.734 0.683 35.873 0.621 35.991 0.544 c
+36.108 0.463 36.204 0.36 36.285 0.235 c
+36.362 0.106 36.403 -0.056 36.403 -0.25 c
+38.262 1.514 m
+38.379 1.786 38.53 1.984 38.718 2.102 c
+38.901 2.219 39.122 2.278 39.379 2.278 c
+39.584 2.278 39.754 2.242 39.894 2.175 c
+40.041 2.105 40.151 2.013 40.232 1.896 c
+40.32 1.778 40.378 1.635 40.408 1.469 c
+40.444 1.301 40.467 1.124 40.467 0.941 c
+40.467 -1.264 l
+39.555 -1.264 l
+39.555 0.735 l
+39.555 0.871 39.544 0.992 39.526 1.103 c
+39.515 1.209 39.489 1.297 39.453 1.367 c
+39.412 1.444 39.353 1.502 39.276 1.543 c
+39.206 1.58 39.114 1.602 38.996 1.602 c
+38.886 1.602 38.791 1.577 38.703 1.529 c
+38.615 1.477 38.534 1.411 38.468 1.323 c
+38.409 1.234 38.358 1.124 38.321 0.999 c
+38.291 0.881 38.277 0.75 38.277 0.602 c
+38.277 -1.264 l
+37.365 -1.264 l
+37.365 3.513 l
+38.277 3.513 l
+38.277 2.205 l
+38.277 2.135 38.269 2.065 38.262 1.999 c
+38.262 1.793 l
+38.262 1.735 38.254 1.679 38.248 1.631 c
+38.248 1.514 l
+h
+f
+Q
+q 1 0 0 1 414.8848 150.0096 cm
+0 0 m
+0 -2.102 l
+-0.676 -2.102 l
+-0.676 3.248 l
+1.029 3.248 l
+1.529 3.248 1.918 3.094 2.205 2.793 c
+2.488 2.5 2.631 2.106 2.631 1.617 c
+2.631 1.095 2.492 0.699 2.219 0.426 c
+1.955 0.151 1.577 0.008 1.087 0 c
+h
+0 0.574 m
+1.029 0.574 l
+1.33 0.574 1.562 0.661 1.72 0.838 c
+1.874 1.014 1.955 1.268 1.955 1.602 c
+1.955 1.926 1.87 2.183 1.706 2.382 c
+1.548 2.577 1.326 2.675 1.043 2.675 c
+0 2.675 l
+h
+5.438 -1.749 m
+5.222 -2.036 4.91 -2.175 4.498 -2.175 c
+4.135 -2.175 3.859 -2.054 3.675 -1.808 c
+3.499 -1.554 3.403 -1.19 3.395 -0.72 c
+3.395 1.881 l
+4.042 1.881 l
+4.042 -0.661 l
+4.042 -1.29 4.226 -1.602 4.601 -1.602 c
+5.002 -1.602 5.277 -1.425 5.424 -1.072 c
+5.424 1.881 l
+6.071 1.881 l
+6.071 -2.102 l
+5.453 -2.102 l
+h
+7.908 2.837 m
+7.908 1.881 l
+8.511 1.881 l
+8.511 1.353 l
+7.908 1.353 l
+7.908 -1.117 l
+7.908 -1.275 7.93 -1.392 7.982 -1.469 c
+8.04 -1.55 8.129 -1.587 8.247 -1.587 c
+8.334 -1.587 8.422 -1.573 8.511 -1.543 c
+8.511 -2.102 l
+8.364 -2.15 8.21 -2.175 8.056 -2.175 c
+7.798 -2.175 7.603 -2.084 7.468 -1.896 c
+7.327 -1.712 7.262 -1.452 7.262 -1.117 c
+7.262 1.353 l
+6.659 1.353 l
+6.659 1.881 l
+7.262 1.881 l
+7.262 2.837 l
+h
+12.083 -1.014 m
+12.803 1.881 l
+13.494 1.881 l
+12.2 -2.66 l
+12.101 -3.002 11.958 -3.262 11.774 -3.439 c
+11.597 -3.615 11.395 -3.704 11.171 -3.704 c
+11.083 -3.704 10.969 -3.682 10.834 -3.645 c
+10.834 -3.102 l
+10.98 -3.116 l
+11.164 -3.116 11.311 -3.072 11.422 -2.984 c
+11.528 -2.896 11.616 -2.738 11.686 -2.514 c
+11.803 -2.072 l
+10.643 1.881 l
+11.348 1.881 l
+h
+13.935 0.073 m
+13.935 0.651 14.071 1.106 14.346 1.441 c
+14.629 1.783 15 1.955 15.464 1.955 c
+15.923 1.955 16.291 1.786 16.565 1.455 c
+16.849 1.132 16.996 0.684 17.007 0.118 c
+17.007 -0.309 l
+17.007 -0.878 16.864 -1.334 16.581 -1.675 c
+16.305 -2.009 15.938 -2.175 15.478 -2.175 c
+15.015 -2.175 14.644 -2.013 14.361 -1.691 c
+14.086 -1.359 13.942 -0.918 13.935 -0.367 c
+h
+14.581 -0.309 m
+14.581 -0.712 14.659 -1.028 14.817 -1.263 c
+14.982 -1.499 15.202 -1.616 15.478 -1.616 c
+16.044 -1.616 16.338 -1.205 16.36 -0.382 c
+16.36 0.073 l
+16.36 0.474 16.276 0.794 16.11 1.029 c
+15.952 1.272 15.736 1.397 15.464 1.397 c
+15.199 1.397 14.982 1.272 14.817 1.029 c
+14.659 0.794 14.581 0.474 14.581 0.073 c
+h
+19.873 -1.749 m
+19.657 -2.036 19.344 -2.175 18.932 -2.175 c
+18.568 -2.175 18.293 -2.054 18.109 -1.808 c
+17.933 -1.554 17.837 -1.19 17.83 -0.72 c
+17.83 1.881 l
+18.477 1.881 l
+18.477 -0.661 l
+18.477 -1.29 18.66 -1.602 19.036 -1.602 c
+19.436 -1.602 19.711 -1.425 19.858 -1.072 c
+19.858 1.881 l
+20.505 1.881 l
+20.505 -2.102 l
+19.888 -2.102 l
+h
+23.137 1.264 m
+23.048 1.283 22.949 1.294 22.842 1.294 c
+22.508 1.294 22.273 1.11 22.137 0.75 c
+22.137 -2.102 l
+21.49 -2.102 l
+21.49 1.881 l
+22.122 1.881 l
+22.137 1.47 l
+22.313 1.794 22.556 1.955 22.872 1.955 c
+22.978 1.955 23.067 1.933 23.137 1.897 c
+h
+26.884 -1.616 m
+27.098 -1.616 27.27 -1.554 27.399 -1.425 c
+27.535 -1.29 27.609 -1.099 27.619 -0.852 c
+28.237 -0.852 l
+28.215 -1.234 28.079 -1.554 27.825 -1.808 c
+27.568 -2.054 27.256 -2.175 26.884 -2.175 c
+26.392 -2.175 26.017 -2.024 25.753 -1.72 c
+25.496 -1.407 25.371 -0.941 25.371 -0.323 c
+25.371 0.118 l
+25.371 0.713 25.496 1.169 25.753 1.484 c
+26.017 1.797 26.392 1.955 26.884 1.955 c
+27.285 1.955 27.605 1.823 27.84 1.559 c
+28.083 1.301 28.215 0.956 28.237 0.515 c
+27.619 0.515 l
+27.598 0.809 27.524 1.029 27.399 1.176 c
+27.281 1.324 27.109 1.397 26.884 1.397 c
+26.591 1.397 26.374 1.297 26.238 1.103 c
+26.098 0.915 26.025 0.607 26.017 0.177 c
+26.017 -0.338 l
+26.017 -0.808 26.084 -1.143 26.223 -1.338 c
+26.37 -1.525 26.591 -1.616 26.884 -1.616 c
+31.015 -1.749 m
+30.798 -2.036 30.486 -2.175 30.074 -2.175 c
+29.71 -2.175 29.435 -2.054 29.251 -1.808 c
+29.074 -1.554 28.979 -1.19 28.972 -0.72 c
+28.972 1.881 l
+29.619 1.881 l
+29.619 -0.661 l
+29.619 -1.29 29.803 -1.602 30.177 -1.602 c
+30.578 -1.602 30.853 -1.425 31 -1.072 c
+31 1.881 l
+31.647 1.881 l
+31.647 -2.102 l
+31.03 -2.102 l
+h
+34.279 1.264 m
+34.19 1.283 34.091 1.294 33.984 1.294 c
+33.65 1.294 33.415 1.11 33.279 0.75 c
+33.279 -2.102 l
+32.632 -2.102 l
+32.632 1.881 l
+33.264 1.881 l
+33.279 1.47 l
+33.456 1.794 33.697 1.955 34.013 1.955 c
+34.121 1.955 34.208 1.933 34.279 1.897 c
+h
+36.571 1.264 m
+36.483 1.283 36.384 1.294 36.278 1.294 c
+35.943 1.294 35.708 1.11 35.572 0.75 c
+35.572 -2.102 l
+34.925 -2.102 l
+34.925 1.881 l
+35.557 1.881 l
+35.572 1.47 l
+35.748 1.794 35.991 1.955 36.307 1.955 c
+36.413 1.955 36.502 1.933 36.571 1.897 c
+h
+38.57 -2.175 m
+38.071 -2.175 37.689 -2.028 37.424 -1.734 c
+37.159 -1.44 37.027 -1.007 37.027 -0.426 c
+37.027 0.044 l
+37.027 0.64 37.152 1.106 37.409 1.441 c
+37.674 1.783 38.034 1.955 38.497 1.955 c
+38.956 1.955 39.298 1.801 39.526 1.5 c
+39.761 1.206 39.883 0.742 39.893 0.118 c
+39.893 -0.309 l
+37.674 -0.309 l
+37.674 -0.397 l
+37.674 -0.83 37.751 -1.143 37.909 -1.338 c
+38.075 -1.525 38.306 -1.616 38.599 -1.616 c
+38.794 -1.616 38.967 -1.583 39.114 -1.514 c
+39.262 -1.437 39.397 -1.319 39.526 -1.161 c
+39.864 -1.573 l
+39.577 -1.977 39.147 -2.175 38.57 -2.175 c
+38.497 1.397 m
+38.221 1.397 38.019 1.301 37.894 1.118 c
+37.766 0.93 37.693 0.64 37.674 0.25 c
+39.247 0.25 l
+39.247 0.339 l
+39.224 0.721 39.158 0.989 39.041 1.147 c
+38.923 1.312 38.74 1.397 38.497 1.397 c
+41.29 1.881 m
+41.304 1.441 l
+41.558 1.783 41.882 1.955 42.275 1.955 c
+42.98 1.955 43.337 1.484 43.347 0.544 c
+43.347 -2.102 l
+42.701 -2.102 l
+42.701 0.515 l
+42.701 0.827 42.646 1.048 42.539 1.176 c
+42.429 1.301 42.275 1.367 42.069 1.367 c
+41.911 1.367 41.764 1.312 41.628 1.206 c
+41.499 1.095 41.396 0.96 41.319 0.794 c
+41.319 -2.102 l
+40.673 -2.102 l
+40.673 1.881 l
+h
+45.171 2.837 m
+45.171 1.881 l
+45.773 1.881 l
+45.773 1.353 l
+45.171 1.353 l
+45.171 -1.117 l
+45.171 -1.275 45.192 -1.392 45.244 -1.469 c
+45.302 -1.55 45.391 -1.587 45.508 -1.587 c
+45.597 -1.587 45.685 -1.573 45.773 -1.543 c
+45.773 -2.102 l
+45.626 -2.15 45.472 -2.175 45.317 -2.175 c
+45.06 -2.175 44.866 -2.084 44.729 -1.896 c
+44.59 -1.712 44.523 -1.452 44.523 -1.117 c
+44.523 1.353 l
+43.921 1.353 l
+43.921 1.881 l
+44.523 1.881 l
+44.523 2.837 l
+h
+49.639 -1.616 m
+49.852 -1.616 50.025 -1.554 50.154 -1.425 c
+50.289 -1.29 50.362 -1.099 50.374 -0.852 c
+50.991 -0.852 l
+50.969 -1.234 50.833 -1.554 50.58 -1.808 c
+50.322 -2.054 50.01 -2.175 49.639 -2.175 c
+49.146 -2.175 48.772 -2.024 48.507 -1.72 c
+48.25 -1.407 48.124 -0.941 48.124 -0.323 c
+48.124 0.118 l
+48.124 0.713 48.25 1.169 48.507 1.484 c
+48.772 1.797 49.146 1.955 49.639 1.955 c
+50.04 1.955 50.359 1.823 50.594 1.559 c
+50.837 1.301 50.969 0.956 50.991 0.515 c
+50.374 0.515 l
+50.352 0.809 50.278 1.029 50.154 1.176 c
+50.036 1.324 49.863 1.397 49.639 1.397 c
+49.345 1.397 49.128 1.297 48.992 1.103 c
+48.853 0.915 48.779 0.607 48.772 0.177 c
+48.772 -0.338 l
+48.772 -0.808 48.838 -1.143 48.978 -1.338 c
+49.124 -1.525 49.345 -1.616 49.639 -1.616 c
+52.388 1.47 m
+52.641 1.794 52.961 1.955 53.343 1.955 c
+54.048 1.955 54.405 1.484 54.416 0.544 c
+54.416 -2.102 l
+53.769 -2.102 l
+53.769 0.515 l
+53.769 0.827 53.714 1.048 53.608 1.176 c
+53.497 1.301 53.343 1.367 53.138 1.367 c
+52.98 1.367 52.832 1.312 52.696 1.206 c
+52.568 1.095 52.465 0.96 52.388 0.794 c
+52.388 -2.102 l
+51.741 -2.102 l
+51.741 3.543 l
+52.388 3.543 l
+h
+57.414 -2.102 m
+57.374 -2.013 57.348 -1.866 57.341 -1.66 c
+57.106 -2.006 56.812 -2.175 56.459 -2.175 c
+56.095 -2.175 55.812 -2.08 55.606 -1.881 c
+55.409 -1.675 55.313 -1.389 55.313 -1.014 c
+55.313 -0.613 55.449 -0.294 55.725 -0.058 c
+55.997 0.184 56.371 0.309 56.841 0.309 c
+57.327 0.309 l
+57.327 0.736 l
+57.327 0.971 57.271 1.135 57.165 1.235 c
+57.055 1.341 56.893 1.397 56.68 1.397 c
+56.481 1.397 56.319 1.338 56.195 1.22 c
+56.077 1.103 56.018 0.956 56.018 0.779 c
+55.371 0.779 l
+55.371 0.974 55.43 1.166 55.548 1.353 c
+55.673 1.536 55.835 1.683 56.033 1.794 c
+56.238 1.9 56.467 1.955 56.724 1.955 c
+57.125 1.955 57.429 1.852 57.635 1.646 c
+57.848 1.441 57.962 1.147 57.973 0.765 c
+57.973 -1.249 l
+57.973 -1.554 58.01 -1.818 58.091 -2.043 c
+58.091 -2.102 l
+h
+56.547 -1.587 m
+56.712 -1.587 56.863 -1.543 57.003 -1.455 c
+57.15 -1.367 57.256 -1.257 57.327 -1.117 c
+57.327 -0.176 l
+56.959 -0.176 l
+56.643 -0.176 56.4 -0.246 56.224 -0.382 c
+56.047 -0.511 55.96 -0.698 55.96 -0.941 c
+55.96 -1.168 56.003 -1.334 56.092 -1.44 c
+56.18 -1.539 56.331 -1.587 56.547 -1.587 c
+59.59 1.881 m
+59.604 1.441 l
+59.858 1.783 60.182 1.955 60.575 1.955 c
+61.281 1.955 61.637 1.484 61.648 0.544 c
+61.648 -2.102 l
+61.001 -2.102 l
+61.001 0.515 l
+61.001 0.827 60.946 1.048 60.84 1.176 c
+60.729 1.301 60.575 1.367 60.369 1.367 c
+60.211 1.367 60.064 1.312 59.928 1.206 c
+59.8 1.095 59.697 0.96 59.619 0.794 c
+59.619 -2.102 l
+58.973 -2.102 l
+58.973 1.881 l
+h
+62.486 0.073 m
+62.486 0.691 62.596 1.154 62.824 1.47 c
+63.048 1.794 63.382 1.955 63.824 1.955 c
+64.224 1.955 64.529 1.779 64.735 1.426 c
+64.778 1.881 l
+65.367 1.881 l
+65.367 -2.146 l
+65.367 -2.634 65.238 -3.013 64.984 -3.278 c
+64.728 -3.542 64.375 -3.675 63.926 -3.675 c
+63.728 -3.675 63.507 -3.623 63.265 -3.528 c
+63.018 -3.428 62.839 -3.307 62.721 -3.16 c
+62.985 -2.719 l
+63.251 -2.984 63.548 -3.116 63.882 -3.116 c
+64.419 -3.116 64.695 -2.822 64.705 -2.234 c
+64.705 -1.705 l
+64.5 -2.021 64.198 -2.175 63.808 -2.175 c
+63.397 -2.175 63.074 -2.024 62.839 -1.72 c
+62.611 -1.407 62.493 -0.955 62.486 -0.367 c
+h
+63.147 -0.309 m
+63.147 -0.75 63.21 -1.08 63.338 -1.294 c
+63.463 -1.499 63.681 -1.602 63.985 -1.602 c
+64.308 -1.602 64.547 -1.437 64.705 -1.103 c
+64.705 0.882 l
+64.537 1.206 64.298 1.367 63.985 1.367 c
+63.691 1.367 63.474 1.264 63.338 1.058 c
+63.21 0.853 63.147 0.53 63.147 0.088 c
+h
+67.748 -2.175 m
+67.249 -2.175 66.866 -2.028 66.602 -1.734 c
+66.337 -1.44 66.205 -1.007 66.205 -0.426 c
+66.205 0.044 l
+66.205 0.64 66.33 1.106 66.586 1.441 c
+66.852 1.783 67.211 1.955 67.675 1.955 c
+68.134 1.955 68.475 1.801 68.703 1.5 c
+68.938 1.206 69.06 0.742 69.071 0.118 c
+69.071 -0.309 l
+66.852 -0.309 l
+66.852 -0.397 l
+66.852 -0.83 66.929 -1.143 67.087 -1.338 c
+67.252 -1.525 67.484 -1.616 67.777 -1.616 c
+67.972 -1.616 68.145 -1.583 68.292 -1.514 c
+68.439 -1.437 68.575 -1.319 68.703 -1.161 c
+69.042 -1.573 l
+68.755 -1.977 68.325 -2.175 67.748 -2.175 c
+67.675 1.397 m
+67.399 1.397 67.197 1.301 67.072 1.118 c
+66.943 0.93 66.87 0.64 66.852 0.25 c
+68.425 0.25 l
+68.425 0.339 l
+68.402 0.721 68.336 0.989 68.219 1.147 c
+68.101 1.312 67.917 1.397 67.675 1.397 c
+71.791 -1.087 m
+71.791 -0.941 71.735 -0.819 71.629 -0.72 c
+71.519 -0.625 71.313 -0.507 71.012 -0.367 c
+70.666 -0.22 70.423 -0.099 70.276 0 c
+70.129 0.107 70.019 0.225 69.952 0.353 c
+69.883 0.478 69.85 0.636 69.85 0.823 c
+69.85 1.147 69.967 1.415 70.203 1.632 c
+70.438 1.845 70.739 1.955 71.114 1.955 c
+71.496 1.955 71.805 1.841 72.04 1.617 c
+72.275 1.389 72.393 1.103 72.393 0.75 c
+71.746 0.75 l
+71.746 0.927 71.687 1.077 71.57 1.206 c
+71.452 1.33 71.298 1.397 71.114 1.397 c
+70.916 1.397 70.765 1.341 70.658 1.235 c
+70.548 1.135 70.497 1.004 70.497 0.838 c
+70.497 0.709 70.534 0.603 70.615 0.515 c
+70.692 0.434 70.883 0.331 71.188 0.206 c
+71.666 0.019 71.996 -0.168 72.173 -0.353 c
+72.349 -0.529 72.437 -0.756 72.437 -1.028 c
+72.437 -1.381 72.312 -1.66 72.069 -1.866 c
+71.834 -2.072 71.519 -2.175 71.128 -2.175 c
+70.706 -2.175 70.368 -2.057 70.114 -1.822 c
+69.857 -1.579 69.732 -1.275 69.732 -0.911 c
+70.38 -0.911 l
+70.386 -1.139 70.457 -1.315 70.585 -1.44 c
+70.71 -1.558 70.893 -1.616 71.128 -1.616 c
+71.342 -1.616 71.504 -1.569 71.614 -1.469 c
+71.731 -1.374 71.791 -1.246 71.791 -1.087 c
+75.7 -2.102 -0.647 3.983 re
+75.745 2.926 m
+75.745 2.816 75.715 2.723 75.656 2.646 c
+75.597 2.577 75.502 2.543 75.377 2.543 c
+75.259 2.543 75.164 2.577 75.097 2.646 c
+75.039 2.723 75.01 2.816 75.01 2.926 c
+75.01 3.043 75.039 3.135 75.097 3.205 c
+75.164 3.282 75.259 3.323 75.377 3.323 c
+75.502 3.323 75.597 3.282 75.656 3.205 c
+75.715 3.124 75.745 3.032 75.745 2.926 c
+77.332 1.881 m
+77.347 1.441 l
+77.6 1.783 77.923 1.955 78.317 1.955 c
+79.022 1.955 79.379 1.484 79.39 0.544 c
+79.39 -2.102 l
+78.743 -2.102 l
+78.743 0.515 l
+78.743 0.827 78.688 1.048 78.581 1.176 c
+78.471 1.301 78.317 1.367 78.111 1.367 c
+77.952 1.367 77.806 1.312 77.67 1.206 c
+77.541 1.095 77.439 0.96 77.362 0.794 c
+77.362 -2.102 l
+76.714 -2.102 l
+76.714 1.881 l
+h
+81.212 2.837 m
+81.212 1.881 l
+81.815 1.881 l
+81.815 1.353 l
+81.212 1.353 l
+81.212 -1.117 l
+81.212 -1.275 81.235 -1.392 81.286 -1.469 c
+81.345 -1.55 81.433 -1.587 81.551 -1.587 c
+81.638 -1.587 81.727 -1.573 81.815 -1.543 c
+81.815 -2.102 l
+81.668 -2.15 81.514 -2.175 81.36 -2.175 c
+81.102 -2.175 80.907 -2.084 80.771 -1.896 c
+80.632 -1.712 80.566 -1.452 80.566 -1.117 c
+80.566 1.353 l
+79.963 1.353 l
+79.963 1.881 l
+80.566 1.881 l
+80.566 2.837 l
+h
+82.374 0.073 m
+82.374 0.651 82.509 1.106 82.785 1.441 c
+83.068 1.783 83.439 1.955 83.903 1.955 c
+84.362 1.955 84.73 1.786 85.004 1.455 c
+85.287 1.132 85.435 0.684 85.445 0.118 c
+85.445 -0.309 l
+85.445 -0.878 85.303 -1.334 85.019 -1.675 c
+84.744 -2.009 84.377 -2.175 83.917 -2.175 c
+83.454 -2.175 83.083 -2.013 82.8 -1.691 c
+82.525 -1.359 82.381 -0.918 82.374 -0.367 c
+h
+83.02 -0.309 m
+83.02 -0.712 83.097 -1.028 83.255 -1.263 c
+83.421 -1.499 83.641 -1.616 83.917 -1.616 c
+84.483 -1.616 84.777 -1.205 84.799 -0.382 c
+84.799 0.073 l
+84.799 0.474 84.715 0.794 84.549 1.029 c
+84.391 1.272 84.175 1.397 83.903 1.397 c
+83.637 1.397 83.421 1.272 83.255 1.029 c
+83.097 0.794 83.02 0.474 83.02 0.073 c
+h
+f
+Q
+q 1 0 0 1 504.5495 148.9813 cm
+0 0 m
+0 0.087 -0.044 0.165 -0.133 0.235 c
+-0.221 0.312 -0.408 0.415 -0.691 0.544 c
+-1.125 0.72 -1.422 0.9 -1.588 1.087 c
+-1.746 1.271 -1.823 1.502 -1.823 1.778 c
+-1.823 2.119 -1.702 2.403 -1.455 2.63 c
+-1.202 2.865 -0.864 2.983 -0.441 2.983 c
+-0.011 2.983 0.338 2.869 0.602 2.645 c
+0.867 2.418 0.999 2.117 0.999 1.734 c
+-0.044 1.734 l
+-0.044 2.057 -0.184 2.219 -0.456 2.219 c
+-0.566 2.219 -0.655 2.183 -0.721 2.117 c
+-0.79 2.046 -0.823 1.947 -0.823 1.822 c
+-0.823 1.734 -0.786 1.653 -0.706 1.587 c
+-0.628 1.529 -0.449 1.433 -0.162 1.308 c
+0.268 1.15 0.565 0.974 0.735 0.779 c
+0.911 0.591 0.999 0.341 0.999 0.029 c
+0.999 -0.324 0.867 -0.611 0.602 -0.823 c
+0.338 -1.04 -0.011 -1.147 -0.441 -1.147 c
+-0.736 -1.147 -0.996 -1.092 -1.22 -0.985 c
+-1.448 -0.867 -1.625 -0.706 -1.75 -0.5 c
+-1.867 -0.294 -1.926 -0.074 -1.926 0.161 c
+-0.941 0.161 l
+-0.941 -0.026 -0.904 -0.162 -0.823 -0.25 c
+-0.736 -0.339 -0.603 -0.383 -0.427 -0.383 c
+-0.144 -0.383 0 -0.258 0 0 c
+2.866 3.881 m
+2.866 2.91 l
+3.395 2.91 l
+3.395 2.117 l
+2.866 2.117 l
+2.866 0.147 l
+2.866 -0.011 2.884 -0.118 2.925 -0.177 c
+2.973 -0.235 3.057 -0.265 3.175 -0.265 c
+3.281 -0.265 3.366 -0.258 3.424 -0.235 c
+3.424 -1.044 l
+3.248 -1.11 3.057 -1.147 2.851 -1.147 c
+2.175 -1.147 1.83 -0.761 1.822 0.014 c
+1.822 2.117 l
+1.367 2.117 l
+1.367 2.91 l
+1.822 2.91 l
+1.822 3.881 l
+h
+5.85 -1.073 m
+5.82 -1.015 5.791 -0.912 5.762 -0.765 c
+5.574 -1.022 5.325 -1.147 5.012 -1.147 c
+4.677 -1.147 4.398 -1.04 4.174 -0.823 c
+3.958 -0.599 3.85 -0.31 3.85 0.043 c
+3.85 0.455 3.983 0.771 4.247 0.999 c
+4.512 1.234 4.895 1.352 5.394 1.352 c
+5.718 1.352 l
+5.718 1.675 l
+5.718 1.851 5.681 1.973 5.614 2.042 c
+5.556 2.119 5.468 2.16 5.35 2.16 c
+5.093 2.16 4.968 2.006 4.968 1.705 c
+3.925 1.705 l
+3.925 2.076 4.06 2.381 4.336 2.616 c
+4.608 2.859 4.957 2.983 5.379 2.983 c
+5.82 2.983 6.158 2.865 6.393 2.63 c
+6.636 2.403 6.761 2.08 6.761 1.66 c
+6.761 -0.206 l
+6.761 -0.551 6.809 -0.82 6.908 -1.015 c
+6.908 -1.073 l
+h
+5.247 -0.324 m
+5.354 -0.324 5.446 -0.306 5.527 -0.265 c
+5.614 -0.217 5.677 -0.158 5.718 -0.088 c
+5.718 0.735 l
+5.468 0.735 l
+5.292 0.735 5.148 0.675 5.041 0.558 c
+4.943 0.448 4.895 0.301 4.895 0.118 c
+4.895 -0.177 5.012 -0.324 5.247 -0.324 c
+9.275 0 m
+9.275 0.087 9.231 0.165 9.142 0.235 c
+9.055 0.312 8.866 0.415 8.584 0.544 c
+8.151 0.72 7.852 0.9 7.688 1.087 c
+7.53 1.271 7.453 1.502 7.453 1.778 c
+7.453 2.119 7.573 2.403 7.819 2.63 c
+8.073 2.865 8.411 2.983 8.834 2.983 c
+9.263 2.983 9.612 2.869 9.878 2.645 c
+10.142 2.418 10.275 2.117 10.275 1.734 c
+9.231 1.734 l
+9.231 2.057 9.091 2.219 8.819 2.219 c
+8.708 2.219 8.621 2.183 8.554 2.117 c
+8.484 2.046 8.452 1.947 8.452 1.822 c
+8.452 1.734 8.488 1.653 8.569 1.587 c
+8.646 1.529 8.826 1.433 9.113 1.308 c
+9.543 1.15 9.841 0.974 10.009 0.779 c
+10.186 0.591 10.275 0.341 10.275 0.029 c
+10.275 -0.324 10.142 -0.611 9.878 -0.823 c
+9.612 -1.04 9.263 -1.147 8.834 -1.147 c
+8.54 -1.147 8.279 -1.092 8.055 -0.985 c
+7.827 -0.867 7.65 -0.706 7.526 -0.5 c
+7.408 -0.294 7.349 -0.074 7.349 0.161 c
+8.334 0.161 l
+8.334 -0.026 8.371 -0.162 8.452 -0.25 c
+8.54 -0.339 8.672 -0.383 8.849 -0.383 c
+9.132 -0.383 9.275 -0.258 9.275 0 c
+11.95 2.557 m
+12.174 2.84 12.45 2.983 12.773 2.983 c
+13.134 2.983 13.409 2.855 13.596 2.601 c
+13.791 2.344 13.89 1.962 13.89 1.454 c
+13.89 -1.073 l
+12.847 -1.073 l
+12.847 1.44 l
+12.847 1.675 12.806 1.841 12.729 1.94 c
+12.66 2.046 12.546 2.102 12.391 2.102 c
+12.203 2.102 12.057 2.017 11.95 1.851 c
+11.95 -1.073 l
+10.906 -1.073 l
+10.906 4.571 l
+11.95 4.571 l
+h
+14.728 -0.545 m
+14.728 -0.379 14.78 -0.243 14.89 -0.133 c
+14.996 -0.026 15.143 0.029 15.33 0.029 c
+15.496 0.029 15.64 -0.026 15.757 -0.133 c
+15.875 -0.243 15.933 -0.379 15.933 -0.545 c
+15.933 -0.713 15.875 -0.849 15.757 -0.956 c
+15.64 -1.066 15.496 -1.118 15.33 -1.118 c
+15.154 -1.118 15.008 -1.066 14.89 -0.956 c
+14.78 -0.849 14.728 -0.713 14.728 -0.545 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 142.3 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 135.461 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.807 l
+-1.896 -1.807 l
+-1.896 -1.263 l
+-2.142 -1.256 -2.359 -1.219 -2.543 -1.161 c
+-2.719 -1.102 -2.873 -1.024 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.034 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.477 -2.234 -0.514 c
+-2.138 -0.554 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.662 l
+-1.907 0.662 -1.926 0.666 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.279 -3.219 1.426 c
+-3.289 1.573 -3.322 1.757 -3.322 1.985 c
+-3.322 2.18 -3.289 2.345 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.933 -2.645 3.003 -2.469 3.043 c
+-2.293 3.091 -2.102 3.12 -1.896 3.132 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.132 l
+-1.319 3.12 -1.128 3.091 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.621 -0.279 2.484 c
+-0.202 2.345 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.368 l
+-1.514 1.368 l
+-1.506 1.368 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.32 0.871 -0.213 0.743 -0.132 0.588 c
+-0.044 0.431 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.503 -2.113 2.484 -2.19 2.455 c
+-2.26 2.426 -2.319 2.386 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.191 c
+-2.477 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.838 -2.439 1.779 c
+-2.41 1.721 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.201 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.201 -0.837 -0.014 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.634 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.395 c
+5.284 -2.314 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.83 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.219 6.034 -1.219 c
+5.829 -1.219 5.644 -1.183 5.49 -1.102 c
+5.343 -1.014 5.215 -0.897 5.108 -0.749 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.772 4.836 1.019 4.888 1.235 c
+4.946 1.449 5.027 1.632 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.188 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.919 7.107 1.97 c
+7.115 2.029 7.122 2.077 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.993 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.414 7.938 -1.635 7.871 -1.822 c
+7.802 -2.006 7.699 -2.16 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.743 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.368 c
+5.88 1.279 5.835 1.162 5.799 1.015 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.514 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.603 l
+9.199 1.603 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.597 1.603 m
+13.053 1.603 l
+13.053 2.22 l
+13.641 2.22 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.22 l
+15.728 2.22 l
+15.728 1.603 l
+14.494 1.603 l
+14.494 -0.103 l
+14.494 -0.323 l
+14.501 -0.392 14.523 -0.455 14.552 -0.514 c
+14.589 -0.565 14.645 -0.61 14.714 -0.646 c
+14.791 -0.675 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.687 15.464 -0.675 c
+15.599 -0.658 15.732 -0.631 15.861 -0.602 c
+15.861 -1.205 l
+15.78 -1.215 15.703 -1.23 15.626 -1.249 c
+15.545 -1.26 15.468 -1.267 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.3 14.938 -1.308 14.832 -1.308 c
+14.645 -1.308 14.483 -1.294 14.346 -1.263 c
+14.218 -1.227 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.024 13.788 -0.955 c
+13.729 -0.878 13.685 -0.801 13.656 -0.72 c
+13.627 -0.631 13.604 -0.544 13.597 -0.455 c
+13.586 -0.359 13.582 -0.264 13.582 -0.176 c
+h
+24.22 -0.249 m
+24.22 -0.419 24.18 -0.569 24.103 -0.706 c
+24.033 -0.833 23.931 -0.947 23.794 -1.043 c
+23.666 -1.132 23.504 -1.201 23.31 -1.249 c
+23.121 -1.296 22.905 -1.323 22.662 -1.323 c
+22.435 -1.323 22.236 -1.308 22.06 -1.278 c
+21.883 -1.249 21.725 -1.201 21.59 -1.132 c
+21.45 -1.055 21.34 -0.955 21.251 -0.837 c
+21.163 -0.72 21.093 -0.573 21.045 -0.396 c
+21.854 -0.278 l
+21.872 -0.378 21.902 -0.455 21.942 -0.514 c
+21.99 -0.573 22.049 -0.617 22.119 -0.646 c
+22.185 -0.675 22.265 -0.702 22.354 -0.72 c
+22.442 -0.731 22.545 -0.735 22.662 -0.735 c
+22.758 -0.735 22.853 -0.731 22.942 -0.72 c
+23.03 -0.702 23.107 -0.675 23.177 -0.646 c
+23.243 -0.617 23.295 -0.58 23.324 -0.529 c
+23.36 -0.481 23.383 -0.419 23.383 -0.338 c
+23.383 -0.242 23.353 -0.168 23.295 -0.118 c
+23.243 -0.07 23.177 -0.029 23.089 0 c
+23.001 0.038 22.89 0.067 22.766 0.088 c
+22.647 0.118 22.516 0.148 22.369 0.177 c
+22.229 0.214 22.09 0.254 21.942 0.294 c
+21.803 0.342 21.677 0.405 21.56 0.485 c
+21.45 0.563 21.361 0.662 21.296 0.78 c
+21.226 0.897 21.193 1.048 21.193 1.235 c
+21.193 1.389 21.222 1.532 21.28 1.661 c
+21.347 1.798 21.442 1.912 21.56 1.999 c
+21.685 2.088 21.843 2.154 22.03 2.205 c
+22.215 2.253 22.427 2.278 22.662 2.278 c
+22.846 2.278 23.023 2.257 23.192 2.22 c
+23.357 2.191 23.504 2.135 23.632 2.058 c
+23.757 1.989 23.868 1.889 23.956 1.764 c
+24.044 1.646 24.103 1.503 24.133 1.338 c
+23.339 1.264 l
+23.316 1.341 23.287 1.405 23.25 1.455 c
+23.21 1.515 23.162 1.559 23.104 1.588 c
+23.052 1.625 22.99 1.65 22.913 1.661 c
+22.832 1.669 22.751 1.676 22.662 1.676 c
+22.446 1.676 22.284 1.646 22.177 1.588 c
+22.067 1.536 22.016 1.449 22.016 1.324 c
+22.016 1.243 22.034 1.18 22.074 1.133 c
+22.122 1.081 22.185 1.044 22.265 1.015 c
+22.354 0.985 22.45 0.956 22.56 0.927 c
+22.666 0.904 22.788 0.882 22.927 0.853 c
+23.081 0.823 23.239 0.784 23.397 0.736 c
+23.551 0.684 23.692 0.622 23.809 0.545 c
+23.927 0.464 24.022 0.36 24.103 0.235 c
+24.18 0.107 24.22 -0.055 24.22 -0.249 c
+25.783 1.603 m
+25.238 1.603 l
+25.238 2.22 l
+25.826 2.22 l
+26.106 3.117 l
+26.679 3.117 l
+26.679 2.22 l
+27.914 2.22 l
+27.914 1.603 l
+26.679 1.603 l
+26.679 -0.103 l
+26.679 -0.323 l
+26.686 -0.392 26.709 -0.455 26.738 -0.514 c
+26.774 -0.565 26.83 -0.61 26.9 -0.646 c
+26.977 -0.675 27.09 -0.691 27.237 -0.691 c
+27.374 -0.691 27.509 -0.687 27.649 -0.675 c
+27.785 -0.658 27.917 -0.631 28.046 -0.602 c
+28.046 -1.205 l
+27.965 -1.215 27.888 -1.23 27.811 -1.249 c
+27.73 -1.26 27.653 -1.267 27.576 -1.278 c
+27.495 -1.286 27.407 -1.294 27.312 -1.294 c
+27.223 -1.3 27.123 -1.308 27.017 -1.308 c
+26.83 -1.308 26.668 -1.294 26.532 -1.263 c
+26.404 -1.227 26.29 -1.183 26.194 -1.132 c
+26.106 -1.084 26.032 -1.024 25.974 -0.955 c
+25.915 -0.878 25.87 -0.801 25.841 -0.72 c
+25.812 -0.631 25.789 -0.544 25.783 -0.455 c
+25.772 -0.359 25.768 -0.264 25.768 -0.176 c
+h
+30.196 -1.323 m
+30.027 -1.323 29.876 -1.3 29.741 -1.263 c
+29.612 -1.215 29.498 -1.146 29.402 -1.058 c
+29.313 -0.97 29.244 -0.863 29.196 -0.735 c
+29.145 -0.598 29.122 -0.448 29.122 -0.278 c
+29.122 -0.073 29.155 0.096 29.226 0.235 c
+29.292 0.383 29.388 0.493 29.505 0.574 c
+29.63 0.662 29.773 0.724 29.931 0.765 c
+30.096 0.802 30.273 0.827 30.46 0.838 c
+31.181 0.853 l
+31.181 1.029 l
+31.181 1.147 31.17 1.25 31.152 1.338 c
+31.129 1.426 31.096 1.492 31.048 1.544 c
+31.008 1.603 30.96 1.64 30.901 1.661 c
+30.842 1.68 30.776 1.691 30.71 1.691 c
+30.641 1.691 30.578 1.68 30.519 1.661 c
+30.468 1.65 30.42 1.625 30.372 1.588 c
+30.331 1.559 30.298 1.507 30.269 1.441 c
+30.248 1.382 30.233 1.301 30.225 1.206 c
+29.284 1.25 l
+29.313 1.397 29.358 1.532 29.417 1.661 c
+29.483 1.786 29.579 1.897 29.696 1.985 c
+29.814 2.08 29.953 2.154 30.122 2.205 c
+30.298 2.253 30.504 2.278 30.74 2.278 c
+31.181 2.278 31.511 2.168 31.74 1.956 c
+31.975 1.75 32.092 1.441 32.092 1.029 c
+32.092 -0.235 l
+32.092 -0.455 l
+32.099 -0.514 32.114 -0.569 32.136 -0.617 c
+32.154 -0.658 32.184 -0.691 32.224 -0.72 c
+32.261 -0.742 32.313 -0.749 32.371 -0.749 c
+32.438 -0.749 32.507 -0.745 32.577 -0.735 c
+32.577 -1.219 l
+32.519 -1.23 32.463 -1.242 32.415 -1.249 c
+32.375 -1.26 32.334 -1.267 32.297 -1.278 c
+32.257 -1.286 32.213 -1.294 32.166 -1.294 c
+32.114 -1.3 32.055 -1.308 31.989 -1.308 c
+31.761 -1.308 31.596 -1.256 31.489 -1.146 c
+31.379 -1.028 31.316 -0.863 31.298 -0.646 c
+31.283 -0.646 l
+31.214 -0.756 31.144 -0.852 31.077 -0.941 c
+31.008 -1.021 30.931 -1.087 30.842 -1.146 c
+30.755 -1.205 30.655 -1.249 30.549 -1.278 c
+30.449 -1.308 30.331 -1.323 30.196 -1.323 c
+31.181 0.353 m
+30.755 0.339 l
+30.655 0.339 30.564 0.331 30.475 0.324 c
+30.394 0.313 30.328 0.287 30.269 0.25 c
+30.211 0.21 30.159 0.151 30.122 0.074 c
+30.082 0.004 30.063 -0.087 30.063 -0.205 c
+30.063 -0.374 30.096 -0.496 30.167 -0.573 c
+30.233 -0.654 30.331 -0.691 30.46 -0.691 c
+30.567 -0.691 30.666 -0.668 30.755 -0.617 c
+30.85 -0.569 30.931 -0.507 30.99 -0.426 c
+31.056 -0.349 31.107 -0.261 31.137 -0.161 c
+31.166 -0.055 31.181 0.059 31.181 0.177 c
+h
+36.403 -0.249 m
+36.403 -0.419 36.362 -0.569 36.285 -0.706 c
+36.215 -0.833 36.112 -0.947 35.976 -1.043 c
+35.848 -1.132 35.686 -1.201 35.491 -1.249 c
+35.304 -1.296 35.087 -1.323 34.844 -1.323 c
+34.616 -1.323 34.418 -1.308 34.242 -1.278 c
+34.065 -1.249 33.907 -1.201 33.772 -1.132 c
+33.631 -1.055 33.521 -0.955 33.433 -0.837 c
+33.345 -0.72 33.275 -0.573 33.228 -0.396 c
+34.036 -0.278 l
+34.055 -0.378 34.084 -0.455 34.124 -0.514 c
+34.171 -0.573 34.231 -0.617 34.3 -0.646 c
+34.367 -0.675 34.447 -0.702 34.535 -0.72 c
+34.624 -0.731 34.726 -0.735 34.844 -0.735 c
+34.94 -0.735 35.035 -0.731 35.123 -0.72 c
+35.212 -0.702 35.289 -0.675 35.359 -0.646 c
+35.425 -0.617 35.476 -0.58 35.505 -0.529 c
+35.543 -0.481 35.565 -0.419 35.565 -0.338 c
+35.565 -0.242 35.535 -0.168 35.476 -0.118 c
+35.425 -0.07 35.359 -0.029 35.27 0 c
+35.183 0.038 35.073 0.067 34.948 0.088 c
+34.83 0.118 34.697 0.148 34.551 0.177 c
+34.41 0.214 34.271 0.254 34.124 0.294 c
+33.984 0.342 33.859 0.405 33.742 0.485 c
+33.631 0.563 33.544 0.662 33.477 0.78 c
+33.408 0.897 33.375 1.048 33.375 1.235 c
+33.375 1.389 33.404 1.532 33.463 1.661 c
+33.529 1.798 33.624 1.912 33.742 1.999 c
+33.867 2.088 34.025 2.154 34.212 2.205 c
+34.396 2.253 34.609 2.278 34.844 2.278 c
+35.028 2.278 35.204 2.257 35.374 2.22 c
+35.539 2.191 35.686 2.135 35.815 2.058 c
+35.939 1.989 36.05 1.889 36.138 1.764 c
+36.226 1.646 36.285 1.503 36.314 1.338 c
+35.52 1.264 l
+35.499 1.341 35.469 1.405 35.432 1.455 c
+35.392 1.515 35.345 1.559 35.285 1.588 c
+35.234 1.625 35.171 1.65 35.094 1.661 c
+35.013 1.669 34.932 1.676 34.844 1.676 c
+34.628 1.676 34.466 1.646 34.36 1.588 c
+34.249 1.536 34.198 1.449 34.198 1.324 c
+34.198 1.243 34.216 1.18 34.256 1.133 c
+34.304 1.081 34.367 1.044 34.447 1.015 c
+34.535 0.985 34.631 0.956 34.742 0.927 c
+34.848 0.904 34.969 0.882 35.109 0.853 c
+35.264 0.823 35.422 0.784 35.58 0.736 c
+35.734 0.684 35.873 0.622 35.991 0.545 c
+36.108 0.464 36.204 0.36 36.285 0.235 c
+36.362 0.107 36.403 -0.055 36.403 -0.249 c
+38.262 1.515 m
+38.379 1.786 38.53 1.985 38.718 2.103 c
+38.901 2.22 39.122 2.278 39.379 2.278 c
+39.584 2.278 39.754 2.242 39.894 2.176 c
+40.041 2.106 40.151 2.014 40.232 1.897 c
+40.32 1.779 40.378 1.636 40.408 1.47 c
+40.444 1.301 40.467 1.125 40.467 0.941 c
+40.467 -1.263 l
+39.555 -1.263 l
+39.555 0.736 l
+39.555 0.871 39.544 0.992 39.526 1.103 c
+39.515 1.21 39.489 1.297 39.453 1.368 c
+39.412 1.445 39.353 1.503 39.276 1.544 c
+39.206 1.58 39.114 1.603 38.996 1.603 c
+38.886 1.603 38.791 1.577 38.703 1.529 c
+38.615 1.478 38.534 1.411 38.468 1.324 c
+38.409 1.235 38.358 1.125 38.321 1 c
+38.291 0.882 38.277 0.75 38.277 0.603 c
+38.277 -1.263 l
+37.365 -1.263 l
+37.365 3.514 l
+38.277 3.514 l
+38.277 2.205 l
+38.277 2.135 38.269 2.066 38.262 1.999 c
+38.262 1.794 l
+38.262 1.735 38.254 1.68 38.248 1.632 c
+38.248 1.515 l
+h
+46.354 2.22 m
+46.362 2.198 46.368 2.165 46.368 2.117 c
+46.376 2.077 46.383 2.029 46.383 1.97 c
+46.391 1.919 46.398 1.867 46.398 1.808 c
+46.398 1.646 l
+46.412 1.646 l
+46.472 1.764 46.537 1.86 46.618 1.941 c
+46.695 2.018 46.78 2.08 46.869 2.132 c
+46.956 2.191 47.044 2.228 47.133 2.249 c
+47.228 2.268 47.328 2.278 47.426 2.278 c
+47.632 2.278 47.812 2.234 47.97 2.147 c
+48.125 2.058 48.253 1.929 48.353 1.764 c
+48.459 1.607 48.536 1.415 48.588 1.191 c
+48.647 0.975 48.676 0.739 48.676 0.485 c
+48.676 0.221 48.647 -0.025 48.588 -0.249 c
+48.536 -0.467 48.459 -0.658 48.353 -0.823 c
+48.253 -0.98 48.122 -1.102 47.956 -1.19 c
+47.798 -1.278 47.611 -1.323 47.397 -1.323 c
+47.298 -1.323 47.199 -1.311 47.104 -1.294 c
+47.004 -1.271 46.912 -1.242 46.824 -1.19 c
+46.743 -1.142 46.666 -1.08 46.589 -0.999 c
+46.52 -0.922 46.46 -0.83 46.412 -0.72 c
+46.398 -0.72 l
+46.398 -0.808 l
+46.405 -0.849 46.412 -0.897 46.412 -0.955 c
+46.412 -1.117 l
+46.412 -1.294 l
+46.412 -2.63 l
+45.501 -2.63 l
+45.501 1.455 l
+45.501 1.621 45.494 1.768 45.487 1.897 c
+45.487 2.22 l
+h
+46.398 0.456 m
+46.398 0.229 46.416 0.038 46.457 -0.118 c
+46.505 -0.264 46.559 -0.382 46.618 -0.47 c
+46.684 -0.558 46.758 -0.625 46.838 -0.661 c
+46.916 -0.702 46.993 -0.72 47.074 -0.72 c
+47.17 -0.72 47.258 -0.698 47.339 -0.646 c
+47.426 -0.598 47.493 -0.529 47.544 -0.44 c
+47.603 -0.345 47.647 -0.22 47.677 -0.073 c
+47.713 0.081 47.735 0.269 47.735 0.485 c
+47.735 0.875 47.677 1.169 47.559 1.368 c
+47.449 1.563 47.295 1.661 47.089 1.661 c
+47.008 1.661 46.931 1.64 46.854 1.603 c
+46.773 1.563 46.699 1.5 46.633 1.411 c
+46.563 1.324 46.505 1.199 46.457 1.044 c
+46.416 0.886 46.398 0.691 46.398 0.456 c
+52.751 0.485 m
+52.751 0.21 52.714 -0.04 52.648 -0.264 c
+52.579 -0.481 52.476 -0.668 52.34 -0.823 c
+52.201 -0.98 52.024 -1.102 51.81 -1.19 c
+51.594 -1.278 51.34 -1.323 51.046 -1.323 c
+50.771 -1.323 50.524 -1.278 50.312 -1.19 c
+50.106 -1.102 49.933 -0.98 49.797 -0.823 c
+49.657 -0.668 49.554 -0.481 49.489 -0.264 c
+49.419 -0.04 49.385 0.21 49.385 0.485 c
+49.385 0.739 49.415 0.975 49.473 1.191 c
+49.539 1.415 49.643 1.607 49.782 1.764 c
+49.918 1.929 50.094 2.058 50.312 2.147 c
+50.524 2.234 50.782 2.278 51.076 2.278 c
+51.388 2.278 51.649 2.234 51.854 2.147 c
+52.068 2.058 52.241 1.929 52.369 1.764 c
+52.505 1.607 52.604 1.415 52.664 1.191 c
+52.722 0.975 52.751 0.739 52.751 0.485 c
+51.796 0.485 m
+51.796 0.691 51.781 0.867 51.752 1.015 c
+51.73 1.162 51.694 1.283 51.634 1.382 c
+51.575 1.478 51.502 1.548 51.414 1.588 c
+51.326 1.636 51.216 1.661 51.091 1.661 c
+50.826 1.661 50.634 1.563 50.518 1.368 c
+50.399 1.18 50.341 0.886 50.341 0.485 c
+50.341 0.063 50.399 -0.242 50.518 -0.426 c
+50.634 -0.613 50.811 -0.706 51.046 -0.706 c
+51.172 -0.706 51.285 -0.687 51.384 -0.646 c
+51.48 -0.598 51.561 -0.525 51.619 -0.426 c
+51.686 -0.33 51.73 -0.205 51.752 -0.058 c
+51.781 0.088 51.796 0.269 51.796 0.485 c
+54.475 2.22 m
+54.482 2.198 54.49 2.165 54.49 2.117 c
+54.497 2.077 54.505 2.029 54.505 1.97 c
+54.512 1.919 54.519 1.867 54.519 1.808 c
+54.519 1.646 l
+54.534 1.646 l
+54.593 1.764 54.659 1.86 54.74 1.941 c
+54.817 2.018 54.902 2.08 54.989 2.132 c
+55.077 2.191 55.166 2.228 55.254 2.249 c
+55.349 2.268 55.449 2.278 55.548 2.278 c
+55.754 2.278 55.934 2.234 56.092 2.147 c
+56.246 2.058 56.375 1.929 56.474 1.764 c
+56.581 1.607 56.658 1.415 56.71 1.191 c
+56.768 0.975 56.797 0.739 56.797 0.485 c
+56.797 0.221 56.768 -0.025 56.71 -0.249 c
+56.658 -0.467 56.581 -0.658 56.474 -0.823 c
+56.375 -0.98 56.242 -1.102 56.077 -1.19 c
+55.919 -1.278 55.731 -1.323 55.519 -1.323 c
+55.419 -1.323 55.32 -1.311 55.224 -1.294 c
+55.125 -1.271 55.033 -1.242 54.946 -1.19 c
+54.865 -1.142 54.788 -1.08 54.711 -0.999 c
+54.64 -0.922 54.582 -0.83 54.534 -0.72 c
+54.519 -0.72 l
+54.519 -0.808 l
+54.526 -0.849 54.534 -0.897 54.534 -0.955 c
+54.534 -1.117 l
+54.534 -1.294 l
+54.534 -2.63 l
+53.622 -2.63 l
+53.622 1.455 l
+53.622 1.621 53.615 1.768 53.608 1.897 c
+53.608 2.22 l
+h
+54.519 0.456 m
+54.519 0.229 54.537 0.038 54.578 -0.118 c
+54.626 -0.264 54.68 -0.382 54.74 -0.47 c
+54.806 -0.558 54.879 -0.625 54.96 -0.661 c
+55.037 -0.702 55.114 -0.72 55.195 -0.72 c
+55.291 -0.72 55.379 -0.698 55.459 -0.646 c
+55.548 -0.598 55.614 -0.529 55.665 -0.44 c
+55.725 -0.345 55.769 -0.22 55.798 -0.073 c
+55.835 0.081 55.856 0.269 55.856 0.485 c
+55.856 0.875 55.798 1.169 55.68 1.368 c
+55.57 1.563 55.416 1.661 55.21 1.661 c
+55.129 1.661 55.052 1.64 54.975 1.603 c
+54.894 1.563 54.821 1.5 54.754 1.411 c
+54.684 1.324 54.626 1.199 54.578 1.044 c
+54.537 0.886 54.519 0.691 54.519 0.456 c
+f
+Q
+q 1 0 0 1 326.4111 120.7036 cm
+0 0 m
+-1.808 0 l
+-2.219 -1.396 l
+-2.911 -1.396 l
+-1.19 3.954 l
+-0.617 3.954 l
+1.118 -1.396 l
+0.426 -1.396 l
+h
+-1.631 0.588 m
+-0.176 0.588 l
+-0.897 3.013 l
+h
+4.63 0.397 m
+4.63 -0.231 4.513 -0.702 4.278 -1.014 c
+4.05 -1.319 3.734 -1.47 3.323 -1.47 c
+2.918 -1.47 2.61 -1.319 2.396 -1.014 c
+2.396 -2.925 l
+1.75 -2.925 l
+1.75 2.587 l
+2.338 2.587 l
+2.382 2.146 l
+2.595 2.488 2.903 2.66 3.308 2.66 c
+3.749 2.66 4.075 2.506 4.293 2.205 c
+4.505 1.9 4.619 1.444 4.63 0.838 c
+h
+3.984 0.779 m
+3.984 1.22 3.913 1.544 3.778 1.749 c
+3.638 1.962 3.418 2.072 3.117 2.072 c
+2.801 2.072 2.562 1.918 2.396 1.617 c
+2.396 -0.455 l
+2.562 -0.761 2.801 -0.912 3.117 -0.912 c
+3.41 -0.912 3.624 -0.808 3.763 -0.603 c
+3.899 -0.389 3.973 -0.059 3.984 0.382 c
+h
+8.349 0.397 m
+8.349 -0.231 8.231 -0.702 7.996 -1.014 c
+7.769 -1.319 7.453 -1.47 7.041 -1.47 c
+6.637 -1.47 6.328 -1.319 6.115 -1.014 c
+6.115 -2.925 l
+5.469 -2.925 l
+5.469 2.587 l
+6.056 2.587 l
+6.101 2.146 l
+6.313 2.488 6.622 2.66 7.026 2.66 c
+7.468 2.66 7.794 2.506 8.011 2.205 c
+8.224 1.9 8.339 1.444 8.349 0.838 c
+h
+7.703 0.779 m
+7.703 1.22 7.633 1.544 7.497 1.749 c
+7.358 1.962 7.136 2.072 6.835 2.072 c
+6.519 2.072 6.28 1.918 6.115 1.617 c
+6.115 -0.455 l
+6.28 -0.761 6.519 -0.912 6.835 -0.912 c
+7.129 -0.912 7.342 -0.808 7.482 -0.603 c
+7.618 -0.389 7.691 -0.059 7.703 0.382 c
+h
+9.893 -1.396 -0.647 5.644 re
+11.994 -0.309 m
+12.715 2.587 l
+13.406 2.587 l
+12.112 -1.955 l
+12.013 -2.296 11.869 -2.558 11.686 -2.734 c
+11.51 -2.911 11.308 -2.999 11.084 -2.999 c
+10.995 -2.999 10.882 -2.977 10.745 -2.94 c
+10.745 -2.396 l
+10.892 -2.411 l
+11.076 -2.411 11.223 -2.367 11.333 -2.278 c
+11.439 -2.19 11.528 -2.032 11.597 -1.808 c
+11.715 -1.367 l
+10.554 2.587 l
+11.26 2.587 l
+h
+17.698 -0.382 m
+17.698 -0.235 17.643 -0.114 17.536 -0.015 c
+17.426 0.081 17.22 0.198 16.919 0.338 c
+16.573 0.485 16.331 0.607 16.184 0.706 c
+16.037 0.812 15.927 0.929 15.861 1.058 c
+15.79 1.183 15.757 1.341 15.757 1.529 c
+15.757 1.852 15.875 2.12 16.11 2.337 c
+16.345 2.55 16.647 2.66 17.022 2.66 c
+17.404 2.66 17.712 2.547 17.947 2.323 c
+18.183 2.095 18.3 1.808 18.3 1.455 c
+17.654 1.455 l
+17.654 1.631 17.595 1.783 17.477 1.911 c
+17.359 2.036 17.205 2.102 17.022 2.102 c
+16.823 2.102 16.673 2.047 16.566 1.94 c
+16.455 1.841 16.405 1.708 16.405 1.544 c
+16.405 1.415 16.441 1.309 16.522 1.22 c
+16.599 1.139 16.79 1.037 17.095 0.912 c
+17.573 0.724 17.904 0.536 18.08 0.353 c
+18.256 0.176 18.344 -0.052 18.344 -0.324 c
+18.344 -0.676 18.219 -0.956 17.978 -1.161 c
+17.742 -1.367 17.426 -1.47 17.037 -1.47 c
+16.613 -1.47 16.276 -1.353 16.023 -1.118 c
+15.765 -0.875 15.64 -0.569 15.64 -0.206 c
+16.287 -0.206 l
+16.295 -0.434 16.364 -0.61 16.493 -0.735 c
+16.617 -0.852 16.802 -0.912 17.037 -0.912 c
+17.249 -0.912 17.411 -0.864 17.521 -0.765 c
+17.639 -0.669 17.698 -0.54 17.698 -0.382 c
+20.035 3.543 m
+20.035 2.587 l
+20.638 2.587 l
+20.638 2.057 l
+20.035 2.057 l
+20.035 -0.412 l
+20.035 -0.569 20.057 -0.687 20.108 -0.765 c
+20.168 -0.845 20.256 -0.882 20.373 -0.882 c
+20.461 -0.882 20.55 -0.867 20.638 -0.838 c
+20.638 -1.396 l
+20.491 -1.444 20.336 -1.47 20.182 -1.47 c
+19.925 -1.47 19.73 -1.378 19.594 -1.191 c
+19.454 -1.007 19.389 -0.746 19.389 -0.412 c
+19.389 2.057 l
+18.786 2.057 l
+18.786 2.587 l
+19.389 2.587 l
+19.389 3.543 l
+h
+21.197 0.779 m
+21.197 1.356 21.332 1.812 21.608 2.146 c
+21.891 2.488 22.262 2.66 22.725 2.66 c
+23.185 2.66 23.551 2.491 23.827 2.161 c
+24.11 1.837 24.257 1.389 24.268 0.823 c
+24.268 0.397 l
+24.268 -0.173 24.125 -0.628 23.842 -0.97 c
+23.566 -1.305 23.199 -1.47 22.74 -1.47 c
+22.277 -1.47 21.905 -1.309 21.623 -0.985 c
+21.347 -0.654 21.203 -0.214 21.197 0.338 c
+h
+21.843 0.397 m
+21.843 -0.008 21.92 -0.324 22.078 -0.559 c
+22.244 -0.794 22.464 -0.912 22.74 -0.912 c
+23.306 -0.912 23.599 -0.5 23.622 0.324 c
+23.622 0.779 l
+23.622 1.18 23.537 1.5 23.372 1.735 c
+23.214 1.977 22.997 2.102 22.725 2.102 c
+22.46 2.102 22.244 1.977 22.078 1.735 c
+21.92 1.5 21.843 1.18 21.843 0.779 c
+h
+26.753 1.97 m
+26.664 1.988 26.566 1.999 26.458 1.999 c
+26.124 1.999 25.889 1.816 25.753 1.455 c
+25.753 -1.396 l
+25.106 -1.396 l
+25.106 2.587 l
+25.739 2.587 l
+25.753 2.175 l
+25.93 2.499 26.172 2.66 26.487 2.66 c
+26.595 2.66 26.683 2.639 26.753 2.602 c
+h
+28.752 -1.47 m
+28.252 -1.47 27.869 -1.323 27.605 -1.029 c
+27.341 -0.735 27.208 -0.301 27.208 0.279 c
+27.208 0.75 l
+27.208 1.345 27.333 1.812 27.59 2.146 c
+27.855 2.488 28.215 2.66 28.678 2.66 c
+29.138 2.66 29.479 2.506 29.707 2.205 c
+29.942 1.911 30.063 1.448 30.074 0.823 c
+30.074 0.397 l
+27.855 0.397 l
+27.855 0.309 l
+27.855 -0.125 27.932 -0.437 28.09 -0.632 c
+28.255 -0.819 28.487 -0.912 28.781 -0.912 c
+28.976 -0.912 29.149 -0.879 29.296 -0.808 c
+29.442 -0.731 29.579 -0.613 29.707 -0.455 c
+30.045 -0.867 l
+29.758 -1.272 29.328 -1.47 28.752 -1.47 c
+28.678 2.102 m
+28.403 2.102 28.201 2.007 28.075 1.822 c
+27.947 1.635 27.873 1.345 27.855 0.956 c
+29.428 0.956 l
+29.428 1.043 l
+29.406 1.426 29.34 1.694 29.222 1.852 c
+29.105 2.018 28.92 2.102 28.678 2.102 c
+30.721 0.779 m
+30.721 1.386 30.832 1.852 31.059 2.175 c
+31.295 2.499 31.622 2.66 32.044 2.66 c
+32.426 2.66 32.724 2.502 32.941 2.19 c
+32.941 4.248 l
+33.587 4.248 l
+33.587 -1.396 l
+32.999 -1.396 l
+32.956 -0.97 l
+32.75 -1.305 32.445 -1.47 32.044 -1.47 c
+31.632 -1.47 31.31 -1.315 31.074 -1 c
+30.839 -0.676 30.721 -0.22 30.721 0.368 c
+h
+31.368 0.397 m
+31.368 -0.044 31.43 -0.374 31.559 -0.588 c
+31.695 -0.794 31.916 -0.897 32.22 -0.897 c
+32.544 -0.897 32.783 -0.735 32.941 -0.412 c
+32.941 1.602 l
+32.772 1.914 32.533 2.072 32.22 2.072 c
+31.916 2.072 31.695 1.97 31.559 1.764 c
+31.43 1.558 31.368 1.234 31.368 0.794 c
+h
+f
+Q
+q 1 0 0 1 364.3793 120.38 cm
+0 0 m
+0 0.088 -0.044 0.166 -0.133 0.235 c
+-0.221 0.312 -0.408 0.416 -0.691 0.544 c
+-1.125 0.721 -1.422 0.9 -1.588 1.087 c
+-1.746 1.272 -1.823 1.503 -1.823 1.779 c
+-1.823 2.12 -1.702 2.404 -1.455 2.631 c
+-1.202 2.866 -0.864 2.984 -0.441 2.984 c
+-0.011 2.984 0.338 2.87 0.602 2.646 c
+0.867 2.419 0.999 2.117 0.999 1.735 c
+-0.044 1.735 l
+-0.044 2.058 -0.184 2.22 -0.456 2.22 c
+-0.566 2.22 -0.655 2.183 -0.721 2.117 c
+-0.79 2.047 -0.823 1.948 -0.823 1.823 c
+-0.823 1.735 -0.786 1.654 -0.706 1.588 c
+-0.628 1.529 -0.449 1.434 -0.162 1.309 c
+0.268 1.151 0.565 0.974 0.735 0.779 c
+0.911 0.592 0.999 0.342 0.999 0.029 c
+0.999 -0.324 0.867 -0.61 0.602 -0.823 c
+0.338 -1.04 -0.011 -1.147 -0.441 -1.147 c
+-0.736 -1.147 -0.996 -1.091 -1.22 -0.985 c
+-1.448 -0.867 -1.625 -0.706 -1.75 -0.5 c
+-1.867 -0.294 -1.926 -0.073 -1.926 0.162 c
+-0.941 0.162 l
+-0.941 -0.025 -0.904 -0.162 -0.823 -0.249 c
+-0.736 -0.338 -0.603 -0.382 -0.427 -0.382 c
+-0.144 -0.382 0 -0.257 0 0 c
+2.866 3.881 m
+2.866 2.911 l
+3.395 2.911 l
+3.395 2.117 l
+2.866 2.117 l
+2.866 0.147 l
+2.866 -0.01 2.884 -0.118 2.925 -0.176 c
+2.973 -0.235 3.057 -0.264 3.175 -0.264 c
+3.281 -0.264 3.366 -0.257 3.424 -0.235 c
+3.424 -1.043 l
+3.248 -1.109 3.057 -1.147 2.851 -1.147 c
+2.175 -1.147 1.83 -0.76 1.822 0.015 c
+1.822 2.117 l
+1.367 2.117 l
+1.367 2.911 l
+1.822 2.911 l
+1.822 3.881 l
+h
+5.85 -1.072 m
+5.82 -1.014 5.791 -0.912 5.762 -0.764 c
+5.574 -1.022 5.325 -1.147 5.012 -1.147 c
+4.677 -1.147 4.398 -1.04 4.174 -0.823 c
+3.958 -0.598 3.85 -0.309 3.85 0.044 c
+3.85 0.456 3.983 0.772 4.247 1 c
+4.512 1.235 4.895 1.353 5.394 1.353 c
+5.718 1.353 l
+5.718 1.675 l
+5.718 1.852 5.681 1.974 5.614 2.043 c
+5.556 2.12 5.468 2.161 5.35 2.161 c
+5.093 2.161 4.968 2.007 4.968 1.706 c
+3.925 1.706 l
+3.925 2.076 4.06 2.381 4.336 2.616 c
+4.608 2.859 4.957 2.984 5.379 2.984 c
+5.82 2.984 6.158 2.866 6.393 2.631 c
+6.636 2.404 6.761 2.08 6.761 1.661 c
+6.761 -0.206 l
+6.761 -0.551 6.809 -0.819 6.908 -1.014 c
+6.908 -1.072 l
+h
+5.247 -0.324 m
+5.354 -0.324 5.446 -0.305 5.527 -0.264 c
+5.614 -0.216 5.677 -0.158 5.718 -0.088 c
+5.718 0.735 l
+5.468 0.735 l
+5.292 0.735 5.148 0.676 5.041 0.559 c
+4.943 0.449 4.895 0.302 4.895 0.118 c
+4.895 -0.176 5.012 -0.324 5.247 -0.324 c
+9.275 0 m
+9.275 0.088 9.231 0.166 9.142 0.235 c
+9.055 0.312 8.866 0.416 8.584 0.544 c
+8.151 0.721 7.852 0.9 7.688 1.087 c
+7.53 1.272 7.453 1.503 7.453 1.779 c
+7.453 2.12 7.573 2.404 7.819 2.631 c
+8.073 2.866 8.411 2.984 8.834 2.984 c
+9.263 2.984 9.612 2.87 9.878 2.646 c
+10.142 2.419 10.275 2.117 10.275 1.735 c
+9.231 1.735 l
+9.231 2.058 9.091 2.22 8.819 2.22 c
+8.708 2.22 8.621 2.183 8.554 2.117 c
+8.485 2.047 8.452 1.948 8.452 1.823 c
+8.452 1.735 8.488 1.654 8.569 1.588 c
+8.646 1.529 8.826 1.434 9.113 1.309 c
+9.543 1.151 9.841 0.974 10.009 0.779 c
+10.186 0.592 10.275 0.342 10.275 0.029 c
+10.275 -0.324 10.142 -0.61 9.878 -0.823 c
+9.612 -1.04 9.263 -1.147 8.834 -1.147 c
+8.54 -1.147 8.279 -1.091 8.055 -0.985 c
+7.827 -0.867 7.65 -0.706 7.526 -0.5 c
+7.408 -0.294 7.349 -0.073 7.349 0.162 c
+8.334 0.162 l
+8.334 -0.025 8.371 -0.162 8.452 -0.249 c
+8.54 -0.338 8.672 -0.382 8.849 -0.382 c
+9.132 -0.382 9.275 -0.257 9.275 0 c
+11.95 2.558 m
+12.174 2.841 12.45 2.984 12.773 2.984 c
+13.134 2.984 13.409 2.855 13.597 2.602 c
+13.791 2.344 13.89 1.962 13.89 1.455 c
+13.89 -1.072 l
+12.847 -1.072 l
+12.847 1.44 l
+12.847 1.675 12.806 1.841 12.729 1.941 c
+12.66 2.047 12.546 2.103 12.391 2.103 c
+12.203 2.103 12.057 2.018 11.95 1.852 c
+11.95 -1.072 l
+10.906 -1.072 l
+10.906 4.572 l
+11.95 4.572 l
+h
+f
+Q
+q 1 0 0 1 382.2233 119.7921 cm
+0 0 m
+0.214 0 0.387 0.063 0.515 0.191 c
+0.651 0.327 0.724 0.518 0.736 0.765 c
+1.353 0.765 l
+1.33 0.382 1.195 0.063 0.941 -0.191 c
+0.684 -0.437 0.372 -0.559 0 -0.559 c
+-0.492 -0.559 -0.867 -0.407 -1.132 -0.103 c
+-1.389 0.21 -1.514 0.676 -1.514 1.294 c
+-1.514 1.735 l
+-1.514 2.33 -1.389 2.786 -1.132 3.102 c
+-0.867 3.414 -0.492 3.572 0 3.572 c
+0.401 3.572 0.721 3.439 0.956 3.175 c
+1.198 2.918 1.33 2.573 1.353 2.132 c
+0.736 2.132 l
+0.713 2.425 0.64 2.646 0.515 2.793 c
+0.397 2.94 0.225 3.013 0 3.013 c
+-0.294 3.013 -0.511 2.914 -0.646 2.72 c
+-0.786 2.532 -0.86 2.224 -0.867 1.793 c
+-0.867 1.279 l
+-0.867 0.809 -0.801 0.474 -0.661 0.279 c
+-0.514 0.092 -0.294 0 0 0 c
+1.97 1.691 m
+1.97 2.267 2.106 2.723 2.382 3.057 c
+2.664 3.4 3.036 3.572 3.499 3.572 c
+3.958 3.572 4.326 3.403 4.601 3.072 c
+4.884 2.749 5.031 2.301 5.042 1.735 c
+5.042 1.309 l
+5.042 0.738 4.898 0.283 4.616 -0.058 c
+4.341 -0.393 3.973 -0.559 3.514 -0.559 c
+3.051 -0.559 2.679 -0.397 2.396 -0.073 c
+2.12 0.258 1.977 0.698 1.97 1.249 c
+h
+2.617 1.309 m
+2.617 0.904 2.694 0.588 2.852 0.353 c
+3.017 0.118 3.238 0 3.514 0 c
+4.079 0 4.373 0.412 4.395 1.235 c
+4.395 1.691 l
+4.395 2.091 4.31 2.411 4.146 2.646 c
+3.988 2.889 3.77 3.013 3.499 3.013 c
+3.234 3.013 3.017 2.889 2.852 2.646 c
+2.694 2.411 2.617 2.091 2.617 1.691 c
+h
+6.498 3.499 m
+6.512 3.057 l
+6.766 3.4 7.089 3.572 7.483 3.572 c
+8.188 3.572 8.544 3.102 8.555 2.161 c
+8.555 -0.484 l
+7.909 -0.484 l
+7.909 2.132 l
+7.909 2.444 7.853 2.664 7.747 2.793 c
+7.637 2.918 7.483 2.984 7.277 2.984 c
+7.119 2.984 6.972 2.929 6.835 2.822 c
+6.707 2.712 6.604 2.577 6.527 2.411 c
+6.527 -0.484 l
+5.88 -0.484 l
+5.88 3.499 l
+h
+10.378 4.454 m
+10.378 3.499 l
+10.98 3.499 l
+10.98 2.969 l
+10.378 2.969 l
+10.378 0.5 l
+10.378 0.342 10.4 0.225 10.452 0.147 c
+10.51 0.067 10.598 0.029 10.716 0.029 c
+10.804 0.029 10.892 0.044 10.98 0.073 c
+10.98 -0.484 l
+10.834 -0.532 10.679 -0.559 10.525 -0.559 c
+10.267 -0.559 10.073 -0.467 9.937 -0.279 c
+9.797 -0.095 9.731 0.166 9.731 0.5 c
+9.731 2.969 l
+9.129 2.969 l
+9.129 3.499 l
+9.731 3.499 l
+9.731 4.454 l
+h
+13.17 -0.559 m
+12.671 -0.559 12.289 -0.411 12.025 -0.118 c
+11.759 0.177 11.628 0.611 11.628 1.191 c
+11.628 1.661 l
+11.628 2.257 11.753 2.723 12.009 3.057 c
+12.274 3.4 12.634 3.572 13.097 3.572 c
+13.557 3.572 13.898 3.418 14.126 3.117 c
+14.361 2.822 14.483 2.359 14.494 1.735 c
+14.494 1.309 l
+12.274 1.309 l
+12.274 1.22 l
+12.274 0.786 12.351 0.474 12.509 0.279 c
+12.675 0.092 12.906 0 13.2 0 c
+13.395 0 13.567 0.033 13.714 0.103 c
+13.862 0.181 13.997 0.298 14.126 0.456 c
+14.464 0.044 l
+14.178 -0.36 13.748 -0.559 13.17 -0.559 c
+13.097 3.013 m
+12.821 3.013 12.619 2.918 12.495 2.734 c
+12.366 2.547 12.293 2.257 12.274 1.867 c
+13.847 1.867 l
+13.847 1.955 l
+13.825 2.338 13.758 2.606 13.641 2.764 c
+13.523 2.929 13.34 3.013 13.097 3.013 c
+15.89 3.499 m
+15.905 3.057 l
+16.158 3.4 16.482 3.572 16.875 3.572 c
+17.581 3.572 17.937 3.102 17.947 2.161 c
+17.947 -0.484 l
+17.301 -0.484 l
+17.301 2.132 l
+17.301 2.444 17.246 2.664 17.139 2.793 c
+17.029 2.918 16.875 2.984 16.669 2.984 c
+16.511 2.984 16.364 2.929 16.228 2.822 c
+16.1 2.712 15.996 2.577 15.919 2.411 c
+15.919 -0.484 l
+15.273 -0.484 l
+15.273 3.499 l
+h
+19.771 4.454 m
+19.771 3.499 l
+20.374 3.499 l
+20.374 2.969 l
+19.771 2.969 l
+19.771 0.5 l
+19.771 0.342 19.792 0.225 19.844 0.147 c
+19.903 0.067 19.991 0.029 20.108 0.029 c
+20.197 0.029 20.285 0.044 20.374 0.073 c
+20.374 -0.484 l
+20.226 -0.532 20.072 -0.559 19.917 -0.559 c
+19.66 -0.559 19.466 -0.467 19.329 -0.279 c
+19.19 -0.095 19.123 0.166 19.123 0.5 c
+19.123 2.969 l
+18.521 2.969 l
+18.521 3.499 l
+19.123 3.499 l
+19.123 4.454 l
+h
+23.563 -0.484 -0.646 3.983 re
+23.607 4.542 m
+23.607 4.432 23.578 4.341 23.518 4.262 c
+23.46 4.193 23.364 4.16 23.24 4.16 c
+23.121 4.16 23.026 4.193 22.961 4.262 c
+22.901 4.341 22.872 4.432 22.872 4.542 c
+22.872 4.659 22.901 4.752 22.961 4.821 c
+23.026 4.898 23.121 4.939 23.24 4.939 c
+23.364 4.939 23.46 4.898 23.518 4.821 c
+23.578 4.74 23.607 4.649 23.607 4.542 c
+25.195 3.499 m
+25.209 3.057 l
+25.463 3.4 25.787 3.572 26.179 3.572 c
+26.884 3.572 27.241 3.102 27.252 2.161 c
+27.252 -0.484 l
+26.606 -0.484 l
+26.606 2.132 l
+26.606 2.444 26.55 2.664 26.444 2.793 c
+26.334 2.918 26.179 2.984 25.974 2.984 c
+25.816 2.984 25.669 2.929 25.533 2.822 c
+25.404 2.712 25.301 2.577 25.224 2.411 c
+25.224 -0.484 l
+24.577 -0.484 l
+24.577 3.499 l
+h
+29.075 4.454 m
+29.075 3.499 l
+29.678 3.499 l
+29.678 2.969 l
+29.075 2.969 l
+29.075 0.5 l
+29.075 0.342 29.097 0.225 29.149 0.147 c
+29.207 0.067 29.296 0.029 29.413 0.029 c
+29.502 0.029 29.59 0.044 29.678 0.073 c
+29.678 -0.484 l
+29.531 -0.532 29.376 -0.559 29.222 -0.559 c
+28.964 -0.559 28.77 -0.467 28.634 -0.279 c
+28.494 -0.095 28.428 0.166 28.428 0.5 c
+28.428 2.969 l
+27.825 2.969 l
+27.825 3.499 l
+28.428 3.499 l
+28.428 4.454 l
+h
+30.236 1.691 m
+30.236 2.267 30.373 2.723 30.647 3.057 c
+30.931 3.4 31.302 3.572 31.765 3.572 c
+32.224 3.572 32.592 3.403 32.868 3.072 c
+33.151 2.749 33.297 2.301 33.309 1.735 c
+33.309 1.309 l
+33.309 0.738 33.165 0.283 32.883 -0.058 c
+32.607 -0.393 32.239 -0.559 31.78 -0.559 c
+31.316 -0.559 30.946 -0.397 30.662 -0.073 c
+30.387 0.258 30.244 0.698 30.236 1.249 c
+h
+30.883 1.309 m
+30.883 0.904 30.961 0.588 31.118 0.353 c
+31.283 0.118 31.504 0 31.78 0 c
+32.345 0 32.64 0.412 32.662 1.235 c
+32.662 1.691 l
+32.662 2.091 32.577 2.411 32.412 2.646 c
+32.254 2.889 32.037 3.013 31.765 3.013 c
+31.501 3.013 31.283 2.889 31.118 2.646 c
+30.961 2.411 30.883 2.091 30.883 1.691 c
+h
+f
+Q
+q 1 0 0 1 420.882 121.0564 cm
+0 0 m
+0.353 2.234 l
+1.353 2.234 l
+0.53 -1.749 l
+-0.338 -1.749 l
+-0.897 0.559 l
+-1.455 -1.749 l
+-2.323 -1.749 l
+-3.146 2.234 l
+-2.146 2.234 l
+-1.793 0 l
+-1.264 2.234 l
+-0.529 2.234 l
+h
+1.75 0.368 m
+1.75 0.974 1.889 1.448 2.176 1.793 c
+2.458 2.135 2.851 2.308 3.352 2.308 c
+3.859 2.308 4.256 2.135 4.542 1.793 c
+4.825 1.448 4.968 0.974 4.968 0.368 c
+4.968 0.103 l
+4.968 -0.496 4.825 -0.966 4.542 -1.309 c
+4.256 -1.654 3.859 -1.823 3.352 -1.823 c
+2.841 -1.823 2.444 -1.654 2.161 -1.309 c
+1.885 -0.966 1.75 -0.492 1.75 0.118 c
+h
+2.793 0.103 m
+2.793 -0.603 2.977 -0.956 3.352 -0.956 c
+3.705 -0.956 3.896 -0.661 3.925 -0.073 c
+3.925 0.368 l
+3.925 0.727 3.873 0.999 3.778 1.176 c
+3.678 1.352 3.535 1.44 3.352 1.44 c
+3.175 1.44 3.036 1.352 2.94 1.176 c
+2.841 0.999 2.793 0.727 2.793 0.368 c
+h
+7.57 1.22 m
+7.232 1.249 l
+6.945 1.249 6.754 1.124 6.659 0.881 c
+6.659 -1.749 l
+5.615 -1.749 l
+5.615 2.234 l
+6.585 2.234 l
+6.614 1.793 l
+6.78 2.135 7.011 2.308 7.306 2.308 c
+7.423 2.308 7.515 2.286 7.585 2.249 c
+h
+9.437 -0.22 m
+9.157 -0.53 l
+9.157 -1.749 l
+8.114 -1.749 l
+8.114 3.896 l
+9.157 3.896 l
+9.157 0.852 l
+9.276 1.043 l
+9.995 2.234 l
+11.245 2.234 l
+10.084 0.588 l
+11.348 -1.749 l
+10.157 -1.749 l
+h
+12.803 -1.749 -1.044 3.983 re
+11.715 3.263 m
+11.715 3.418 11.763 3.547 11.863 3.645 c
+11.969 3.752 12.104 3.807 12.274 3.807 c
+12.45 3.807 12.586 3.752 12.685 3.645 c
+12.792 3.547 12.847 3.418 12.847 3.263 c
+12.847 3.094 12.792 2.959 12.685 2.851 c
+12.586 2.753 12.45 2.705 12.274 2.705 c
+12.104 2.705 11.969 2.753 11.863 2.851 c
+11.763 2.959 11.715 3.094 11.715 3.263 c
+14.566 2.234 m
+14.596 1.837 l
+14.832 2.15 15.133 2.308 15.507 2.308 c
+16.191 2.308 16.544 1.826 16.566 0.867 c
+16.566 -1.749 l
+15.522 -1.749 l
+15.522 0.794 l
+15.522 1.018 15.486 1.18 15.42 1.278 c
+15.35 1.374 15.232 1.426 15.067 1.426 c
+14.88 1.426 14.732 1.33 14.626 1.147 c
+14.626 -1.749 l
+13.582 -1.749 l
+13.582 2.234 l
+h
+17.184 0.368 m
+17.184 1.014 17.301 1.5 17.536 1.822 c
+17.771 2.146 18.102 2.308 18.535 2.308 c
+18.888 2.308 19.16 2.165 19.359 1.881 c
+19.403 2.234 l
+20.343 2.234 l
+20.343 -1.749 l
+20.343 -2.256 20.2 -2.646 19.917 -2.911 c
+19.63 -3.183 19.227 -3.322 18.697 -3.322 c
+18.47 -3.322 18.234 -3.278 17.992 -3.19 c
+17.756 -3.102 17.581 -2.988 17.463 -2.851 c
+17.816 -2.132 l
+17.911 -2.238 18.04 -2.323 18.198 -2.381 c
+18.352 -2.448 18.499 -2.485 18.639 -2.485 c
+18.874 -2.485 19.039 -2.425 19.138 -2.308 c
+19.245 -2.198 19.3 -2.021 19.3 -1.779 c
+19.3 -1.426 l
+19.102 -1.691 18.844 -1.823 18.521 -1.823 c
+18.098 -1.823 17.771 -1.661 17.536 -1.338 c
+17.309 -1.007 17.191 -0.536 17.184 0.073 c
+h
+18.227 0.103 m
+18.227 -0.272 18.275 -0.54 18.374 -0.706 c
+18.47 -0.875 18.624 -0.956 18.83 -0.956 c
+19.042 -0.956 19.2 -0.879 19.3 -0.721 c
+19.3 1.176 l
+19.19 1.341 19.035 1.426 18.83 1.426 c
+18.624 1.426 18.47 1.341 18.374 1.176 c
+18.275 1.007 18.227 0.738 18.227 0.368 c
+h
+22.695 0.368 m
+22.695 1.014 22.802 1.5 23.019 1.822 c
+23.243 2.146 23.566 2.308 23.989 2.308 c
+24.301 2.308 24.555 2.175 24.754 1.911 c
+24.754 3.896 l
+25.812 3.896 l
+25.812 -1.749 l
+24.856 -1.749 l
+24.812 -1.338 l
+24.596 -1.661 24.32 -1.823 23.989 -1.823 c
+23.578 -1.823 23.258 -1.668 23.033 -1.353 c
+22.817 -1.029 22.703 -0.559 22.695 0.058 c
+h
+23.739 0.103 m
+23.739 -0.291 23.776 -0.566 23.857 -0.721 c
+23.945 -0.879 24.092 -0.956 24.297 -0.956 c
+24.503 -0.956 24.654 -0.864 24.754 -0.676 c
+24.754 1.132 l
+24.654 1.326 24.503 1.426 24.297 1.426 c
+24.099 1.426 23.96 1.345 23.871 1.191 c
+23.783 1.043 23.739 0.771 23.739 0.382 c
+h
+27.663 -1.749 -1.043 3.983 re
+26.576 3.263 m
+26.576 3.418 26.624 3.547 26.723 3.645 c
+26.83 3.752 26.965 3.807 27.135 3.807 c
+27.311 3.807 27.447 3.752 27.546 3.645 c
+27.653 3.547 27.708 3.418 27.708 3.263 c
+27.708 3.094 27.653 2.959 27.546 2.851 c
+27.447 2.753 27.311 2.705 27.135 2.705 c
+26.965 2.705 26.83 2.753 26.723 2.851 c
+26.624 2.959 26.576 3.094 26.576 3.263 c
+30.427 1.22 m
+30.089 1.249 l
+29.803 1.249 29.612 1.124 29.516 0.881 c
+29.516 -1.749 l
+28.472 -1.749 l
+28.472 2.234 l
+29.442 2.234 l
+29.471 1.793 l
+29.637 2.135 29.868 2.308 30.163 2.308 c
+30.28 2.308 30.372 2.286 30.442 2.249 c
+h
+32.5 -1.823 m
+31.97 -1.823 31.551 -1.668 31.25 -1.353 c
+30.956 -1.029 30.809 -0.569 30.809 0.029 c
+30.809 0.338 l
+30.809 0.962 30.946 1.448 31.221 1.793 c
+31.493 2.135 31.886 2.308 32.397 2.308 c
+32.897 2.308 33.267 2.146 33.514 1.822 c
+33.768 1.5 33.9 1.022 33.911 0.397 c
+33.911 -0.103 l
+31.838 -0.103 l
+31.856 -0.397 31.919 -0.613 32.029 -0.75 c
+32.147 -0.889 32.327 -0.956 32.573 -0.956 c
+32.915 -0.956 33.205 -0.838 33.44 -0.603 c
+33.852 -1.235 l
+33.724 -1.411 33.536 -1.554 33.294 -1.661 c
+33.047 -1.768 32.783 -1.823 32.5 -1.823 c
+31.853 0.617 m
+32.882 0.617 l
+32.882 0.721 l
+32.882 0.956 32.841 1.132 32.764 1.249 c
+32.694 1.374 32.566 1.44 32.382 1.44 c
+32.205 1.44 32.074 1.371 31.985 1.234 c
+31.904 1.106 31.86 0.9 31.853 0.617 c
+35.939 -0.956 m
+36.233 -0.956 36.384 -0.761 36.395 -0.368 c
+37.365 -0.368 l
+37.365 -0.802 37.233 -1.154 36.968 -1.426 c
+36.704 -1.691 36.365 -1.823 35.954 -1.823 c
+35.443 -1.823 35.05 -1.668 34.778 -1.353 c
+34.514 -1.029 34.374 -0.559 34.366 0.058 c
+34.366 0.382 l
+34.366 1.007 34.499 1.484 34.763 1.808 c
+35.035 2.138 35.432 2.308 35.954 2.308 c
+36.384 2.308 36.726 2.168 36.983 1.897 c
+37.236 1.621 37.365 1.238 37.365 0.75 c
+36.395 0.75 l
+36.395 0.962 36.355 1.132 36.278 1.249 c
+36.207 1.374 36.09 1.44 35.925 1.44 c
+35.748 1.44 35.619 1.374 35.542 1.249 c
+35.461 1.12 35.418 0.871 35.41 0.5 c
+35.41 0.088 l
+35.41 -0.235 35.424 -0.463 35.455 -0.588 c
+35.491 -0.717 35.546 -0.808 35.616 -0.867 c
+35.694 -0.926 35.8 -0.956 35.939 -0.956 c
+39.173 3.204 m
+39.173 2.234 l
+39.702 2.234 l
+39.702 1.44 l
+39.173 1.44 l
+39.173 -0.53 l
+39.173 -0.687 39.191 -0.794 39.232 -0.852 c
+39.28 -0.912 39.364 -0.941 39.482 -0.941 c
+39.588 -0.941 39.673 -0.933 39.731 -0.912 c
+39.731 -1.72 l
+39.555 -1.786 39.364 -1.823 39.158 -1.823 c
+38.482 -1.823 38.137 -1.437 38.129 -0.661 c
+38.129 1.44 l
+37.674 1.44 l
+37.674 2.234 l
+38.129 2.234 l
+38.129 3.204 l
+h
+40.055 0.368 m
+40.055 0.974 40.195 1.448 40.481 1.793 c
+40.764 2.135 41.157 2.308 41.657 2.308 c
+42.164 2.308 42.561 2.135 42.848 1.793 c
+43.131 1.448 43.274 0.974 43.274 0.368 c
+43.274 0.103 l
+43.274 -0.496 43.131 -0.966 42.848 -1.309 c
+42.561 -1.654 42.164 -1.823 41.657 -1.823 c
+41.146 -1.823 40.75 -1.654 40.467 -1.309 c
+40.191 -0.966 40.055 -0.492 40.055 0.118 c
+h
+41.099 0.103 m
+41.099 -0.603 41.282 -0.956 41.657 -0.956 c
+42.01 -0.956 42.202 -0.661 42.231 -0.073 c
+42.231 0.368 l
+42.231 0.727 42.179 0.999 42.083 1.176 c
+41.984 1.352 41.841 1.44 41.657 1.44 c
+41.481 1.44 41.341 1.352 41.246 1.176 c
+41.146 0.999 41.099 0.727 41.099 0.368 c
+h
+45.876 1.22 m
+45.537 1.249 l
+45.251 1.249 45.06 1.124 44.965 0.881 c
+44.965 -1.749 l
+43.921 -1.749 l
+43.921 2.234 l
+44.891 2.234 l
+44.92 1.793 l
+45.086 2.135 45.317 2.308 45.611 2.308 c
+45.729 2.308 45.821 2.286 45.89 2.249 c
+h
+47.772 -0.015 m
+48.301 2.234 l
+49.404 2.234 l
+48.095 -2.352 l
+47.897 -3.017 47.53 -3.352 46.993 -3.352 c
+46.865 -3.352 46.721 -3.329 46.567 -3.293 c
+46.567 -2.469 l
+46.684 -2.485 l
+46.85 -2.485 46.971 -2.448 47.052 -2.381 c
+47.129 -2.323 47.191 -2.213 47.243 -2.058 c
+47.316 -1.793 l
+46.17 2.234 l
+47.287 2.234 l
+h
+f
+Q
+q 1 0 0 1 470.8588 118.2342 cm
+0 0 m
+-0.397 0.264 l
+-0.162 0.588 -0.04 0.922 -0.029 1.264 c
+-0.029 1.881 l
+0.632 1.881 l
+0.632 1.352 l
+0.632 1.095 0.566 0.849 0.441 0.603 c
+0.324 0.36 0.177 0.158 0 0 c
+5.233 1.073 m
+5.193 1.161 5.167 1.309 5.16 1.514 c
+4.925 1.168 4.63 0.999 4.278 0.999 c
+3.913 0.999 3.63 1.095 3.425 1.294 c
+3.227 1.5 3.131 1.786 3.131 2.161 c
+3.131 2.562 3.267 2.881 3.543 3.116 c
+3.815 3.358 4.189 3.484 4.659 3.484 c
+5.145 3.484 l
+5.145 3.91 l
+5.145 4.145 5.089 4.31 4.983 4.409 c
+4.873 4.516 4.711 4.571 4.498 4.571 c
+4.299 4.571 4.138 4.513 4.013 4.395 c
+3.896 4.278 3.836 4.131 3.836 3.954 c
+3.19 3.954 l
+3.19 4.149 3.248 4.34 3.366 4.527 c
+3.491 4.711 3.653 4.858 3.851 4.968 c
+4.057 5.075 4.285 5.13 4.542 5.13 c
+4.943 5.13 5.247 5.027 5.453 4.821 c
+5.667 4.615 5.781 4.322 5.791 3.939 c
+5.791 1.926 l
+5.791 1.621 5.828 1.356 5.909 1.132 c
+5.909 1.073 l
+h
+4.366 1.587 m
+4.532 1.587 4.682 1.631 4.821 1.72 c
+4.968 1.808 5.075 1.918 5.145 2.057 c
+5.145 2.998 l
+4.777 2.998 l
+4.461 2.998 4.219 2.929 4.042 2.793 c
+3.866 2.664 3.778 2.477 3.778 2.234 c
+3.778 2.007 3.822 1.841 3.91 1.735 c
+3.998 1.635 4.149 1.587 4.366 1.587 c
+7.408 5.056 m
+7.423 4.615 l
+7.676 4.958 8 5.13 8.393 5.13 c
+9.099 5.13 9.455 4.659 9.467 3.719 c
+9.467 1.073 l
+8.819 1.073 l
+8.819 3.69 l
+8.819 4.002 8.765 4.222 8.658 4.351 c
+8.548 4.476 8.393 4.542 8.187 4.542 c
+8.029 4.542 7.882 4.487 7.747 4.38 c
+7.618 4.27 7.515 4.135 7.437 3.969 c
+7.437 1.073 l
+6.791 1.073 l
+6.791 5.056 l
+h
+10.304 3.248 m
+10.304 3.855 10.414 4.322 10.643 4.644 c
+10.878 4.968 11.204 5.13 11.627 5.13 c
+12.009 5.13 12.307 4.972 12.524 4.659 c
+12.524 6.718 l
+13.17 6.718 l
+13.17 1.073 l
+12.582 1.073 l
+12.538 1.5 l
+12.332 1.165 12.027 0.999 11.627 0.999 c
+11.215 0.999 10.892 1.154 10.657 1.469 c
+10.422 1.793 10.304 2.249 10.304 2.837 c
+h
+10.951 2.866 m
+10.951 2.425 11.013 2.095 11.142 1.881 c
+11.278 1.675 11.499 1.573 11.803 1.573 c
+12.127 1.573 12.366 1.735 12.524 2.057 c
+12.524 4.072 l
+12.355 4.384 12.116 4.542 11.803 4.542 c
+11.499 4.542 11.278 4.439 11.142 4.233 c
+11.013 4.027 10.951 3.704 10.951 3.263 c
+h
+17.271 1.558 m
+17.485 1.558 17.658 1.621 17.786 1.749 c
+17.922 1.885 17.995 2.076 18.007 2.323 c
+18.624 2.323 l
+18.602 1.94 18.466 1.621 18.212 1.367 c
+17.955 1.12 17.643 0.999 17.271 0.999 c
+16.779 0.999 16.405 1.151 16.139 1.455 c
+15.882 1.768 15.757 2.234 15.757 2.851 c
+15.757 3.293 l
+15.757 3.888 15.882 4.343 16.139 4.659 c
+16.405 4.972 16.779 5.13 17.271 5.13 c
+17.672 5.13 17.992 4.997 18.227 4.733 c
+18.47 4.476 18.602 4.131 18.624 3.69 c
+18.007 3.69 l
+17.984 3.983 17.911 4.204 17.786 4.351 c
+17.668 4.498 17.496 4.571 17.271 4.571 c
+16.978 4.571 16.761 4.472 16.625 4.278 c
+16.486 4.09 16.411 3.782 16.405 3.351 c
+16.405 2.837 l
+16.405 2.367 16.47 2.032 16.61 1.837 c
+16.757 1.65 16.978 1.558 17.271 1.558 c
+20.079 1.073 -0.647 5.644 re
+22.519 0.999 m
+22.019 0.999 21.637 1.147 21.373 1.44 c
+21.108 1.735 20.976 2.168 20.976 2.749 c
+20.976 3.219 l
+20.976 3.815 21.101 4.281 21.358 4.615 c
+21.623 4.958 21.982 5.13 22.445 5.13 c
+22.905 5.13 23.247 4.976 23.474 4.675 c
+23.709 4.38 23.831 3.917 23.842 3.293 c
+23.842 2.866 l
+21.623 2.866 l
+21.623 2.778 l
+21.623 2.344 21.7 2.032 21.858 1.837 c
+22.023 1.65 22.254 1.558 22.549 1.558 c
+22.743 1.558 22.915 1.591 23.063 1.661 c
+23.21 1.738 23.346 1.856 23.474 2.014 c
+23.813 1.602 l
+23.526 1.198 23.096 0.999 22.519 0.999 c
+22.445 4.571 m
+22.17 4.571 21.968 4.476 21.843 4.292 c
+21.714 4.104 21.641 3.815 21.623 3.425 c
+23.195 3.425 l
+23.195 3.513 l
+23.173 3.896 23.107 4.164 22.99 4.322 c
+22.872 4.487 22.688 4.571 22.445 4.571 c
+26.664 1.073 m
+26.624 1.161 26.598 1.309 26.591 1.514 c
+26.356 1.168 26.061 0.999 25.709 0.999 c
+25.345 0.999 25.062 1.095 24.856 1.294 c
+24.658 1.5 24.562 1.786 24.562 2.161 c
+24.562 2.562 24.698 2.881 24.974 3.116 c
+25.246 3.358 25.621 3.484 26.091 3.484 c
+26.576 3.484 l
+26.576 3.91 l
+26.576 4.145 26.521 4.31 26.414 4.409 c
+26.304 4.516 26.142 4.571 25.93 4.571 c
+25.731 4.571 25.569 4.513 25.444 4.395 c
+25.327 4.278 25.267 4.131 25.267 3.954 c
+24.621 3.954 l
+24.621 4.149 24.68 4.34 24.797 4.527 c
+24.922 4.711 25.084 4.858 25.282 4.968 c
+25.488 5.075 25.716 5.13 25.973 5.13 c
+26.374 5.13 26.678 5.027 26.884 4.821 c
+27.098 4.615 27.212 4.322 27.223 3.939 c
+27.223 1.926 l
+27.223 1.621 27.26 1.356 27.341 1.132 c
+27.341 1.073 l
+h
+25.797 1.587 m
+25.963 1.587 26.113 1.631 26.252 1.72 c
+26.4 1.808 26.506 1.918 26.576 2.057 c
+26.576 2.998 l
+26.208 2.998 l
+25.893 2.998 25.65 2.929 25.473 2.793 c
+25.297 2.664 25.209 2.477 25.209 2.234 c
+25.209 2.007 25.253 1.841 25.342 1.735 c
+25.429 1.635 25.581 1.587 25.797 1.587 c
+29.868 4.439 m
+29.78 4.457 29.681 4.469 29.575 4.469 c
+29.24 4.469 29.005 4.285 28.869 3.925 c
+28.869 1.073 l
+28.222 1.073 l
+28.222 5.056 l
+28.854 5.056 l
+28.869 4.644 l
+29.045 4.968 29.288 5.13 29.604 5.13 c
+29.71 5.13 29.799 5.108 29.868 5.072 c
+h
+f
+Q
+q 1 0 0 1 504.7546 120.38 cm
+0 0 m
+0 0.088 -0.043 0.166 -0.132 0.235 c
+-0.22 0.312 -0.407 0.416 -0.691 0.544 c
+-1.124 0.721 -1.421 0.9 -1.587 1.087 c
+-1.745 1.272 -1.822 1.503 -1.822 1.779 c
+-1.822 2.12 -1.701 2.404 -1.454 2.631 c
+-1.201 2.866 -0.864 2.984 -0.44 2.984 c
+-0.01 2.984 0.339 2.87 0.603 2.646 c
+0.867 2.419 1 2.117 1 1.735 c
+-0.043 1.735 l
+-0.043 2.058 -0.183 2.22 -0.455 2.22 c
+-0.565 2.22 -0.654 2.183 -0.72 2.117 c
+-0.789 2.047 -0.823 1.948 -0.823 1.823 c
+-0.823 1.735 -0.786 1.654 -0.706 1.588 c
+-0.628 1.529 -0.448 1.434 -0.162 1.309 c
+0.269 1.151 0.566 0.974 0.736 0.779 c
+0.912 0.592 1 0.342 1 0.029 c
+1 -0.324 0.867 -0.61 0.603 -0.823 c
+0.339 -1.04 -0.01 -1.147 -0.44 -1.147 c
+-0.735 -1.147 -0.995 -1.091 -1.219 -0.985 c
+-1.448 -0.867 -1.624 -0.706 -1.749 -0.5 c
+-1.866 -0.294 -1.925 -0.073 -1.925 0.162 c
+-0.941 0.162 l
+-0.941 -0.025 -0.904 -0.162 -0.823 -0.249 c
+-0.735 -0.338 -0.602 -0.382 -0.426 -0.382 c
+-0.143 -0.382 0 -0.257 0 0 c
+2.866 3.881 m
+2.866 2.911 l
+3.396 2.911 l
+3.396 2.117 l
+2.866 2.117 l
+2.866 0.147 l
+2.866 -0.01 2.885 -0.118 2.926 -0.176 c
+2.974 -0.235 3.057 -0.264 3.175 -0.264 c
+3.282 -0.264 3.366 -0.257 3.425 -0.235 c
+3.425 -1.043 l
+3.249 -1.109 3.057 -1.147 2.852 -1.147 c
+2.176 -1.147 1.831 -0.76 1.823 0.015 c
+1.823 2.117 l
+1.368 2.117 l
+1.368 2.911 l
+1.823 2.911 l
+1.823 3.881 l
+h
+5.85 -1.072 m
+5.821 -1.014 5.792 -0.912 5.762 -0.764 c
+5.575 -1.022 5.326 -1.147 5.012 -1.147 c
+4.678 -1.147 4.399 -1.04 4.175 -0.823 c
+3.958 -0.598 3.851 -0.309 3.851 0.044 c
+3.851 0.456 3.984 0.772 4.248 1 c
+4.513 1.235 4.896 1.353 5.395 1.353 c
+5.718 1.353 l
+5.718 1.675 l
+5.718 1.852 5.681 1.974 5.615 2.043 c
+5.557 2.12 5.469 2.161 5.351 2.161 c
+5.093 2.161 4.969 2.007 4.969 1.706 c
+3.925 1.706 l
+3.925 2.076 4.061 2.381 4.337 2.616 c
+4.609 2.859 4.958 2.984 5.38 2.984 c
+5.821 2.984 6.159 2.866 6.394 2.631 c
+6.637 2.404 6.762 2.08 6.762 1.661 c
+6.762 -0.206 l
+6.762 -0.551 6.81 -0.819 6.909 -1.014 c
+6.909 -1.072 l
+h
+5.247 -0.324 m
+5.355 -0.324 5.446 -0.305 5.527 -0.264 c
+5.615 -0.216 5.678 -0.158 5.718 -0.088 c
+5.718 0.735 l
+5.469 0.735 l
+5.292 0.735 5.149 0.676 5.042 0.559 c
+4.943 0.449 4.896 0.302 4.896 0.118 c
+4.896 -0.176 5.012 -0.324 5.247 -0.324 c
+9.276 0 m
+9.276 0.088 9.231 0.166 9.143 0.235 c
+9.055 0.312 8.867 0.416 8.584 0.544 c
+8.151 0.721 7.853 0.9 7.688 1.087 c
+7.53 1.272 7.453 1.503 7.453 1.779 c
+7.453 2.12 7.574 2.404 7.82 2.631 c
+8.073 2.866 8.412 2.984 8.834 2.984 c
+9.264 2.984 9.613 2.87 9.878 2.646 c
+10.143 2.419 10.275 2.117 10.275 1.735 c
+9.231 1.735 l
+9.231 2.058 9.091 2.22 8.819 2.22 c
+8.709 2.22 8.621 2.183 8.555 2.117 c
+8.486 2.047 8.453 1.948 8.453 1.823 c
+8.453 1.735 8.489 1.654 8.57 1.588 c
+8.647 1.529 8.827 1.434 9.114 1.309 c
+9.544 1.151 9.841 0.974 10.01 0.779 c
+10.186 0.592 10.275 0.342 10.275 0.029 c
+10.275 -0.324 10.143 -0.61 9.878 -0.823 c
+9.613 -1.04 9.264 -1.147 8.834 -1.147 c
+8.54 -1.147 8.279 -1.091 8.056 -0.985 c
+7.828 -0.867 7.651 -0.706 7.526 -0.5 c
+7.408 -0.294 7.35 -0.073 7.35 0.162 c
+8.335 0.162 l
+8.335 -0.025 8.372 -0.162 8.453 -0.249 c
+8.54 -0.338 8.673 -0.382 8.85 -0.382 c
+9.132 -0.382 9.276 -0.257 9.276 0 c
+11.951 2.558 m
+12.175 2.841 12.451 2.984 12.773 2.984 c
+13.134 2.984 13.409 2.855 13.597 2.602 c
+13.792 2.344 13.891 1.962 13.891 1.455 c
+13.891 -1.072 l
+12.848 -1.072 l
+12.848 1.44 l
+12.848 1.675 12.807 1.841 12.73 1.941 c
+12.66 2.047 12.546 2.103 12.391 2.103 c
+12.204 2.103 12.057 2.018 11.951 1.852 c
+11.951 -1.072 l
+10.907 -1.072 l
+10.907 4.572 l
+11.951 4.572 l
+h
+f
+Q
+q 1 0 0 1 519.5421 119.6604 cm
+0 0 m
+0 0.118 0.033 0.213 0.103 0.294 c
+0.169 0.371 0.272 0.411 0.412 0.411 c
+0.559 0.411 0.665 0.371 0.735 0.294 c
+0.812 0.213 0.852 0.118 0.852 0 c
+0.852 -0.111 0.812 -0.202 0.735 -0.279 c
+0.665 -0.357 0.559 -0.397 0.412 -0.397 c
+0.272 -0.397 0.169 -0.357 0.103 -0.279 c
+0.033 -0.202 0 -0.111 0 0 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 113.704 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 106.8644 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.278 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.631 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.179 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.675 re
+10.124 2.837 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.455 14.552 -0.515 c
+14.589 -0.566 14.645 -0.61 14.714 -0.646 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.687 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.603 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.455 c
+13.586 -0.36 13.582 -0.264 13.582 -0.176 c
+h
+24.22 -0.25 m
+24.22 -0.419 24.18 -0.569 24.103 -0.706 c
+24.033 -0.834 23.931 -0.948 23.794 -1.043 c
+23.666 -1.132 23.504 -1.201 23.31 -1.249 c
+23.121 -1.297 22.905 -1.323 22.662 -1.323 c
+22.435 -1.323 22.236 -1.309 22.06 -1.278 c
+21.883 -1.249 21.725 -1.201 21.59 -1.132 c
+21.45 -1.055 21.34 -0.956 21.251 -0.838 c
+21.163 -0.721 21.093 -0.573 21.045 -0.397 c
+21.854 -0.279 l
+21.872 -0.378 21.902 -0.455 21.942 -0.515 c
+21.99 -0.573 22.049 -0.617 22.119 -0.646 c
+22.185 -0.676 22.265 -0.702 22.354 -0.721 c
+22.442 -0.731 22.545 -0.735 22.662 -0.735 c
+22.758 -0.735 22.853 -0.731 22.942 -0.721 c
+23.03 -0.702 23.107 -0.676 23.177 -0.646 c
+23.243 -0.617 23.295 -0.58 23.324 -0.529 c
+23.36 -0.482 23.383 -0.419 23.383 -0.338 c
+23.383 -0.243 23.353 -0.169 23.295 -0.118 c
+23.243 -0.07 23.177 -0.029 23.089 0 c
+23.001 0.037 22.89 0.066 22.766 0.088 c
+22.647 0.118 22.516 0.147 22.369 0.177 c
+22.229 0.214 22.09 0.254 21.942 0.294 c
+21.803 0.341 21.677 0.405 21.56 0.485 c
+21.45 0.563 21.361 0.661 21.296 0.779 c
+21.226 0.897 21.193 1.047 21.193 1.235 c
+21.193 1.389 21.222 1.532 21.28 1.661 c
+21.347 1.797 21.442 1.911 21.56 1.999 c
+21.685 2.087 21.843 2.153 22.03 2.205 c
+22.215 2.253 22.427 2.278 22.662 2.278 c
+22.846 2.278 23.023 2.256 23.192 2.219 c
+23.357 2.19 23.504 2.135 23.632 2.058 c
+23.757 1.988 23.868 1.889 23.956 1.764 c
+24.044 1.646 24.103 1.503 24.133 1.338 c
+23.339 1.264 l
+23.316 1.341 23.287 1.404 23.25 1.455 c
+23.21 1.514 23.162 1.558 23.104 1.588 c
+23.052 1.625 22.99 1.65 22.913 1.661 c
+22.832 1.668 22.751 1.675 22.662 1.675 c
+22.446 1.675 22.284 1.646 22.177 1.588 c
+22.067 1.536 22.016 1.448 22.016 1.323 c
+22.016 1.242 22.034 1.18 22.074 1.132 c
+22.122 1.08 22.185 1.043 22.265 1.014 c
+22.354 0.985 22.45 0.956 22.56 0.926 c
+22.666 0.904 22.788 0.882 22.927 0.852 c
+23.081 0.823 23.239 0.783 23.397 0.735 c
+23.551 0.684 23.692 0.621 23.809 0.544 c
+23.927 0.463 24.022 0.36 24.103 0.235 c
+24.18 0.106 24.22 -0.056 24.22 -0.25 c
+25.783 1.602 m
+25.238 1.602 l
+25.238 2.219 l
+25.826 2.219 l
+26.106 3.117 l
+26.679 3.117 l
+26.679 2.219 l
+27.914 2.219 l
+27.914 1.602 l
+26.679 1.602 l
+26.679 -0.103 l
+26.679 -0.324 l
+26.686 -0.393 26.709 -0.455 26.738 -0.515 c
+26.774 -0.566 26.83 -0.61 26.9 -0.646 c
+26.977 -0.676 27.09 -0.691 27.237 -0.691 c
+27.374 -0.691 27.509 -0.687 27.649 -0.676 c
+27.785 -0.658 27.917 -0.632 28.046 -0.603 c
+28.046 -1.205 l
+27.965 -1.216 27.888 -1.231 27.811 -1.249 c
+27.73 -1.261 27.653 -1.268 27.576 -1.278 c
+27.495 -1.286 27.407 -1.294 27.312 -1.294 c
+27.223 -1.301 27.123 -1.309 27.017 -1.309 c
+26.83 -1.309 26.668 -1.294 26.532 -1.264 c
+26.404 -1.228 26.29 -1.183 26.194 -1.132 c
+26.106 -1.084 26.032 -1.025 25.974 -0.956 c
+25.915 -0.879 25.87 -0.802 25.841 -0.721 c
+25.812 -0.632 25.789 -0.544 25.783 -0.455 c
+25.772 -0.36 25.768 -0.264 25.768 -0.176 c
+h
+30.196 -1.323 m
+30.027 -1.323 29.876 -1.301 29.741 -1.264 c
+29.612 -1.216 29.498 -1.147 29.402 -1.058 c
+29.313 -0.97 29.244 -0.864 29.196 -0.735 c
+29.145 -0.599 29.122 -0.449 29.122 -0.279 c
+29.122 -0.073 29.155 0.096 29.226 0.235 c
+29.292 0.382 29.388 0.492 29.505 0.573 c
+29.63 0.661 29.773 0.724 29.931 0.765 c
+30.096 0.802 30.273 0.827 30.46 0.838 c
+31.181 0.852 l
+31.181 1.029 l
+31.181 1.147 31.17 1.249 31.152 1.338 c
+31.129 1.426 31.096 1.492 31.048 1.544 c
+31.008 1.602 30.96 1.639 30.901 1.661 c
+30.842 1.679 30.776 1.691 30.71 1.691 c
+30.641 1.691 30.578 1.679 30.519 1.661 c
+30.468 1.65 30.42 1.625 30.372 1.588 c
+30.331 1.558 30.298 1.507 30.269 1.44 c
+30.248 1.382 30.233 1.301 30.225 1.205 c
+29.284 1.249 l
+29.313 1.396 29.358 1.532 29.417 1.661 c
+29.483 1.786 29.579 1.897 29.696 1.984 c
+29.814 2.08 29.953 2.153 30.122 2.205 c
+30.298 2.253 30.504 2.278 30.74 2.278 c
+31.181 2.278 31.511 2.168 31.74 1.955 c
+31.975 1.749 32.092 1.44 32.092 1.029 c
+32.092 -0.235 l
+32.092 -0.455 l
+32.099 -0.515 32.114 -0.569 32.136 -0.617 c
+32.154 -0.658 32.184 -0.691 32.224 -0.721 c
+32.261 -0.742 32.313 -0.75 32.371 -0.75 c
+32.438 -0.75 32.507 -0.746 32.577 -0.735 c
+32.577 -1.22 l
+32.519 -1.231 32.463 -1.242 32.415 -1.249 c
+32.375 -1.261 32.334 -1.268 32.297 -1.278 c
+32.257 -1.286 32.213 -1.294 32.166 -1.294 c
+32.114 -1.301 32.055 -1.309 31.989 -1.309 c
+31.761 -1.309 31.596 -1.257 31.489 -1.147 c
+31.379 -1.029 31.316 -0.864 31.298 -0.646 c
+31.283 -0.646 l
+31.214 -0.757 31.144 -0.852 31.077 -0.941 c
+31.008 -1.022 30.931 -1.087 30.842 -1.147 c
+30.755 -1.205 30.655 -1.249 30.549 -1.278 c
+30.449 -1.309 30.331 -1.323 30.196 -1.323 c
+31.181 0.353 m
+30.755 0.338 l
+30.655 0.338 30.564 0.331 30.475 0.324 c
+30.394 0.312 30.328 0.287 30.269 0.25 c
+30.211 0.21 30.159 0.151 30.122 0.073 c
+30.082 0.004 30.063 -0.088 30.063 -0.206 c
+30.063 -0.374 30.096 -0.496 30.167 -0.573 c
+30.233 -0.654 30.331 -0.691 30.46 -0.691 c
+30.567 -0.691 30.666 -0.669 30.755 -0.617 c
+30.85 -0.569 30.931 -0.507 30.99 -0.426 c
+31.056 -0.349 31.107 -0.261 31.137 -0.162 c
+31.166 -0.056 31.181 0.059 31.181 0.177 c
+h
+33.903 1.602 m
+33.36 1.602 l
+33.36 2.219 l
+33.948 2.219 l
+34.227 3.117 l
+34.8 3.117 l
+34.8 2.219 l
+36.035 2.219 l
+36.035 1.602 l
+34.8 1.602 l
+34.8 -0.103 l
+34.8 -0.324 l
+34.807 -0.393 34.83 -0.455 34.859 -0.515 c
+34.896 -0.566 34.951 -0.61 35.021 -0.646 c
+35.098 -0.676 35.212 -0.691 35.359 -0.691 c
+35.495 -0.691 35.631 -0.687 35.771 -0.676 c
+35.906 -0.658 36.039 -0.632 36.168 -0.603 c
+36.168 -1.205 l
+36.087 -1.216 36.01 -1.231 35.932 -1.249 c
+35.852 -1.261 35.774 -1.268 35.697 -1.278 c
+35.616 -1.286 35.528 -1.294 35.432 -1.294 c
+35.345 -1.301 35.245 -1.309 35.138 -1.309 c
+34.951 -1.309 34.789 -1.294 34.653 -1.264 c
+34.525 -1.228 34.41 -1.183 34.315 -1.132 c
+34.227 -1.084 34.154 -1.025 34.094 -0.956 c
+34.036 -0.879 33.992 -0.802 33.963 -0.721 c
+33.933 -0.632 33.911 -0.544 33.903 -0.455 c
+33.893 -0.36 33.889 -0.264 33.889 -0.176 c
+h
+38.248 2.219 m
+38.248 0.264 l
+38.248 0.125 38.254 0 38.277 -0.118 c
+38.295 -0.228 38.328 -0.32 38.379 -0.397 c
+38.427 -0.478 38.486 -0.54 38.556 -0.588 c
+38.622 -0.628 38.707 -0.646 38.806 -0.646 c
+38.894 -0.646 38.975 -0.628 39.056 -0.588 c
+39.144 -0.54 39.218 -0.47 39.276 -0.382 c
+39.335 -0.287 39.379 -0.176 39.409 -0.058 c
+39.445 0.066 39.467 0.206 39.467 0.353 c
+39.467 2.219 l
+40.364 2.219 l
+40.364 -0.485 l
+40.364 -0.721 l
+40.371 -0.802 40.378 -0.879 40.378 -0.956 c
+40.378 -1.147 l
+40.386 -1.198 40.393 -1.234 40.393 -1.264 c
+39.541 -1.264 l
+39.53 -1.234 39.519 -1.198 39.511 -1.147 c
+39.511 -0.956 l
+39.511 -0.889 39.504 -0.819 39.497 -0.75 c
+39.497 -0.573 l
+39.482 -0.573 l
+39.364 -0.838 39.21 -1.029 39.027 -1.147 c
+38.85 -1.264 38.647 -1.323 38.424 -1.323 c
+38.218 -1.323 38.045 -1.286 37.909 -1.22 c
+37.77 -1.154 37.66 -1.058 37.571 -0.941 c
+37.49 -0.823 37.431 -0.687 37.394 -0.529 c
+37.365 -0.364 37.35 -0.187 37.35 0 c
+37.35 2.219 l
+h
+44.527 -0.25 m
+44.527 -0.419 44.487 -0.569 44.409 -0.706 c
+44.34 -0.834 44.237 -0.948 44.101 -1.043 c
+43.972 -1.132 43.811 -1.201 43.616 -1.249 c
+43.428 -1.297 43.212 -1.323 42.969 -1.323 c
+42.742 -1.323 42.543 -1.309 42.366 -1.278 c
+42.19 -1.249 42.032 -1.201 41.896 -1.132 c
+41.757 -1.055 41.647 -0.956 41.558 -0.838 c
+41.47 -0.721 41.4 -0.573 41.352 -0.397 c
+42.161 -0.279 l
+42.179 -0.378 42.208 -0.455 42.249 -0.515 c
+42.297 -0.573 42.356 -0.617 42.425 -0.646 c
+42.491 -0.676 42.572 -0.702 42.661 -0.721 c
+42.749 -0.731 42.852 -0.735 42.969 -0.735 c
+43.065 -0.735 43.16 -0.731 43.249 -0.721 c
+43.337 -0.702 43.414 -0.676 43.484 -0.646 c
+43.55 -0.617 43.601 -0.58 43.631 -0.529 c
+43.667 -0.482 43.69 -0.419 43.69 -0.338 c
+43.69 -0.243 43.66 -0.169 43.601 -0.118 c
+43.55 -0.07 43.484 -0.029 43.395 0 c
+43.307 0.037 43.197 0.066 43.072 0.088 c
+42.954 0.118 42.822 0.147 42.676 0.177 c
+42.536 0.214 42.396 0.254 42.249 0.294 c
+42.109 0.341 41.984 0.405 41.867 0.485 c
+41.757 0.563 41.668 0.661 41.602 0.779 c
+41.533 0.897 41.5 1.047 41.5 1.235 c
+41.5 1.389 41.529 1.532 41.587 1.661 c
+41.654 1.797 41.749 1.911 41.867 1.999 c
+41.992 2.087 42.15 2.153 42.337 2.205 c
+42.521 2.253 42.734 2.278 42.969 2.278 c
+43.153 2.278 43.33 2.256 43.499 2.219 c
+43.663 2.19 43.811 2.135 43.939 2.058 c
+44.064 1.988 44.174 1.889 44.263 1.764 c
+44.351 1.646 44.409 1.503 44.44 1.338 c
+43.646 1.264 l
+43.623 1.341 43.594 1.404 43.557 1.455 c
+43.517 1.514 43.469 1.558 43.41 1.588 c
+43.359 1.625 43.297 1.65 43.219 1.661 c
+43.139 1.668 43.058 1.675 42.969 1.675 c
+42.753 1.675 42.591 1.646 42.484 1.588 c
+42.374 1.536 42.323 1.448 42.323 1.323 c
+42.323 1.242 42.341 1.18 42.381 1.132 c
+42.429 1.08 42.491 1.043 42.572 1.014 c
+42.661 0.985 42.757 0.956 42.867 0.926 c
+42.973 0.904 43.094 0.882 43.234 0.852 c
+43.388 0.823 43.546 0.783 43.704 0.735 c
+43.858 0.684 43.998 0.621 44.116 0.544 c
+44.234 0.463 44.329 0.36 44.409 0.235 c
+44.487 0.106 44.527 -0.056 44.527 -0.25 c
+f
+Q
+q 1 0 0 1 365.7754 92.4156 cm
+0 0 m
+-0.029 -0.581 -0.191 -1.022 -0.485 -1.324 c
+-0.779 -1.628 -1.198 -1.779 -1.735 -1.779 c
+-2.263 -1.779 -2.69 -1.58 -3.013 -1.176 c
+-3.329 -0.765 -3.484 -0.206 -3.484 0.5 c
+-3.484 1.469 l
+-3.484 2.164 -3.322 2.711 -2.999 3.116 c
+-2.675 3.516 -2.23 3.719 -1.661 3.719 c
+-1.154 3.719 -0.757 3.561 -0.47 3.248 c
+-0.187 2.944 -0.029 2.502 0 1.926 c
+-0.69 1.926 l
+-0.721 2.366 -0.816 2.678 -0.97 2.866 c
+-1.118 3.05 -1.349 3.145 -1.661 3.145 c
+-2.036 3.145 -2.319 2.998 -2.514 2.705 c
+-2.712 2.418 -2.807 2.003 -2.807 1.455 c
+-2.807 0.47 l
+-2.807 -0.071 -2.716 -0.485 -2.529 -0.779 c
+-2.344 -1.066 -2.08 -1.206 -1.735 -1.206 c
+-1.382 -1.206 -1.128 -1.118 -0.97 -0.941 c
+-0.816 -0.765 -0.721 -0.452 -0.69 0 c
+h
+1.514 -1.706 -0.646 5.644 re
+3.954 -1.779 m
+3.454 -1.779 3.072 -1.632 2.807 -1.338 c
+2.543 -1.044 2.41 -0.611 2.41 -0.03 c
+2.41 0.44 l
+2.41 1.036 2.535 1.502 2.793 1.837 c
+3.057 2.179 3.418 2.352 3.881 2.352 c
+4.34 2.352 4.682 2.198 4.91 1.896 c
+5.145 1.602 5.266 1.139 5.277 0.515 c
+5.277 0.087 l
+3.057 0.087 l
+3.057 0 l
+3.057 -0.434 3.135 -0.746 3.293 -0.941 c
+3.458 -1.129 3.69 -1.22 3.983 -1.22 c
+4.178 -1.22 4.351 -1.187 4.498 -1.118 c
+4.645 -1.04 4.781 -0.923 4.91 -0.765 c
+5.247 -1.176 l
+4.961 -1.58 4.531 -1.779 3.954 -1.779 c
+3.881 1.793 m
+3.605 1.793 3.403 1.697 3.278 1.514 c
+3.15 1.326 3.076 1.036 3.057 0.646 c
+4.63 0.646 l
+4.63 0.735 l
+4.608 1.117 4.542 1.385 4.424 1.543 c
+4.307 1.708 4.123 1.793 3.881 1.793 c
+8.099 -1.706 m
+8.058 -1.617 8.033 -1.47 8.025 -1.264 c
+7.79 -1.61 7.497 -1.779 7.144 -1.779 c
+6.78 -1.779 6.497 -1.683 6.292 -1.484 c
+6.093 -1.279 5.997 -0.992 5.997 -0.618 c
+5.997 -0.217 6.134 0.103 6.408 0.338 c
+6.681 0.58 7.056 0.706 7.526 0.706 c
+8.011 0.706 l
+8.011 1.132 l
+8.011 1.367 7.956 1.532 7.85 1.631 c
+7.739 1.738 7.578 1.793 7.364 1.793 c
+7.166 1.793 7.004 1.734 6.879 1.616 c
+6.762 1.499 6.703 1.352 6.703 1.176 c
+6.056 1.176 l
+6.056 1.371 6.115 1.562 6.232 1.749 c
+6.358 1.932 6.519 2.08 6.718 2.19 c
+6.923 2.296 7.152 2.352 7.408 2.352 c
+7.809 2.352 8.114 2.248 8.32 2.043 c
+8.533 1.837 8.646 1.543 8.658 1.161 c
+8.658 -0.853 l
+8.658 -1.158 8.694 -1.422 8.775 -1.646 c
+8.775 -1.706 l
+h
+7.232 -1.191 m
+7.397 -1.191 7.548 -1.147 7.688 -1.058 c
+7.835 -0.971 7.941 -0.86 8.011 -0.721 c
+8.011 0.22 l
+7.644 0.22 l
+7.327 0.22 7.085 0.151 6.909 0.014 c
+6.732 -0.114 6.644 -0.302 6.644 -0.544 c
+6.644 -0.772 6.688 -0.937 6.776 -1.044 c
+6.865 -1.143 7.015 -1.191 7.232 -1.191 c
+11.304 1.66 m
+11.216 1.679 11.116 1.691 11.009 1.691 c
+10.676 1.691 10.44 1.506 10.304 1.146 c
+10.304 -1.706 l
+9.658 -1.706 l
+9.658 2.278 l
+10.29 2.278 l
+10.304 1.866 l
+10.481 2.19 10.723 2.352 11.039 2.352 c
+11.146 2.352 11.233 2.329 11.304 2.293 c
+h
+f
+Q
+q 1 0 0 1 381.1066 91.7834 cm
+0 0 m
+0 0.088 -0.044 0.166 -0.133 0.235 c
+-0.221 0.312 -0.408 0.415 -0.691 0.544 c
+-1.124 0.72 -1.422 0.9 -1.588 1.087 c
+-1.746 1.271 -1.823 1.502 -1.823 1.778 c
+-1.823 2.12 -1.702 2.403 -1.455 2.631 c
+-1.201 2.866 -0.864 2.984 -0.441 2.984 c
+-0.011 2.984 0.338 2.87 0.603 2.645 c
+0.867 2.418 0.999 2.117 0.999 1.735 c
+-0.044 1.735 l
+-0.044 2.057 -0.183 2.219 -0.456 2.219 c
+-0.566 2.219 -0.654 2.183 -0.721 2.117 c
+-0.79 2.047 -0.823 1.947 -0.823 1.822 c
+-0.823 1.735 -0.786 1.654 -0.706 1.587 c
+-0.628 1.529 -0.449 1.433 -0.162 1.308 c
+0.268 1.15 0.565 0.974 0.735 0.779 c
+0.912 0.592 0.999 0.341 0.999 0.029 c
+0.999 -0.324 0.867 -0.611 0.603 -0.823 c
+0.338 -1.04 -0.011 -1.147 -0.441 -1.147 c
+-0.735 -1.147 -0.996 -1.091 -1.22 -0.985 c
+-1.448 -0.867 -1.625 -0.706 -1.749 -0.5 c
+-1.867 -0.294 -1.926 -0.073 -1.926 0.162 c
+-0.941 0.162 l
+-0.941 -0.026 -0.904 -0.162 -0.823 -0.25 c
+-0.735 -0.339 -0.603 -0.382 -0.426 -0.382 c
+-0.143 -0.382 0 -0.258 0 0 c
+2.866 3.881 m
+2.866 2.911 l
+3.395 2.911 l
+3.395 2.117 l
+2.866 2.117 l
+2.866 0.147 l
+2.866 -0.011 2.884 -0.118 2.925 -0.177 c
+2.973 -0.235 3.057 -0.264 3.175 -0.264 c
+3.281 -0.264 3.366 -0.258 3.425 -0.235 c
+3.425 -1.044 l
+3.248 -1.11 3.057 -1.147 2.851 -1.147 c
+2.175 -1.147 1.83 -0.761 1.822 0.014 c
+1.822 2.117 l
+1.367 2.117 l
+1.367 2.911 l
+1.822 2.911 l
+1.822 3.881 l
+h
+5.85 -1.073 m
+5.82 -1.014 5.791 -0.912 5.762 -0.765 c
+5.575 -1.022 5.325 -1.147 5.012 -1.147 c
+4.678 -1.147 4.399 -1.04 4.174 -0.823 c
+3.958 -0.599 3.851 -0.309 3.851 0.044 c
+3.851 0.455 3.983 0.771 4.247 0.999 c
+4.513 1.234 4.895 1.352 5.394 1.352 c
+5.718 1.352 l
+5.718 1.675 l
+5.718 1.852 5.681 1.973 5.615 2.043 c
+5.556 2.12 5.468 2.161 5.35 2.161 c
+5.093 2.161 4.968 2.007 4.968 1.705 c
+3.925 1.705 l
+3.925 2.076 4.06 2.381 4.336 2.616 c
+4.608 2.859 4.957 2.984 5.38 2.984 c
+5.82 2.984 6.159 2.866 6.394 2.631 c
+6.637 2.403 6.761 2.08 6.761 1.66 c
+6.761 -0.206 l
+6.761 -0.551 6.809 -0.819 6.909 -1.014 c
+6.909 -1.073 l
+h
+5.247 -0.324 m
+5.354 -0.324 5.446 -0.305 5.527 -0.264 c
+5.615 -0.217 5.677 -0.158 5.718 -0.088 c
+5.718 0.735 l
+5.468 0.735 l
+5.292 0.735 5.149 0.676 5.041 0.559 c
+4.943 0.448 4.895 0.301 4.895 0.118 c
+4.895 -0.177 5.012 -0.324 5.247 -0.324 c
+9.275 0 m
+9.275 0.088 9.231 0.166 9.143 0.235 c
+9.055 0.312 8.867 0.415 8.584 0.544 c
+8.151 0.72 7.853 0.9 7.688 1.087 c
+7.53 1.271 7.453 1.502 7.453 1.778 c
+7.453 2.12 7.574 2.403 7.819 2.631 c
+8.073 2.866 8.411 2.984 8.834 2.984 c
+9.264 2.984 9.613 2.87 9.878 2.645 c
+10.142 2.418 10.275 2.117 10.275 1.735 c
+9.231 1.735 l
+9.231 2.057 9.091 2.219 8.819 2.219 c
+8.709 2.219 8.621 2.183 8.555 2.117 c
+8.485 2.047 8.452 1.947 8.452 1.822 c
+8.452 1.735 8.488 1.654 8.569 1.587 c
+8.646 1.529 8.827 1.433 9.113 1.308 c
+9.543 1.15 9.841 0.974 10.01 0.779 c
+10.186 0.592 10.275 0.341 10.275 0.029 c
+10.275 -0.324 10.142 -0.611 9.878 -0.823 c
+9.613 -1.04 9.264 -1.147 8.834 -1.147 c
+8.54 -1.147 8.279 -1.091 8.055 -0.985 c
+7.827 -0.867 7.651 -0.706 7.526 -0.5 c
+7.408 -0.294 7.349 -0.073 7.349 0.162 c
+8.334 0.162 l
+8.334 -0.026 8.371 -0.162 8.452 -0.25 c
+8.54 -0.339 8.673 -0.382 8.849 -0.382 c
+9.132 -0.382 9.275 -0.258 9.275 0 c
+11.95 2.558 m
+12.174 2.84 12.45 2.984 12.773 2.984 c
+13.134 2.984 13.409 2.855 13.597 2.601 c
+13.791 2.344 13.89 1.962 13.89 1.455 c
+13.89 -1.073 l
+12.847 -1.073 l
+12.847 1.44 l
+12.847 1.675 12.806 1.841 12.729 1.94 c
+12.66 2.047 12.546 2.102 12.391 2.102 c
+12.204 2.102 12.057 2.017 11.95 1.852 c
+11.95 -1.073 l
+10.907 -1.073 l
+10.907 4.571 l
+11.95 4.571 l
+h
+f
+Q
+q 1 0 0 1 400.5385 91.8568 cm
+0 0 m
+0.603 2.837 l
+1.249 2.837 l
+0.264 -1.147 l
+-0.249 -1.147 l
+-1.029 1.705 l
+-1.779 -1.147 l
+-2.308 -1.147 l
+-3.263 2.837 l
+-2.631 2.837 l
+-2.014 0.073 l
+-1.278 2.837 l
+-0.764 2.837 l
+h
+2.631 -1.147 -0.647 3.984 re
+2.675 3.881 m
+2.675 3.77 2.646 3.678 2.587 3.601 c
+2.529 3.532 2.433 3.499 2.308 3.499 c
+2.19 3.499 2.095 3.532 2.028 3.601 c
+1.97 3.678 1.941 3.77 1.941 3.881 c
+1.941 3.998 1.97 4.09 2.028 4.16 c
+2.095 4.237 2.19 4.278 2.308 4.278 c
+2.433 4.278 2.529 4.237 2.587 4.16 c
+2.646 4.079 2.675 3.987 2.675 3.881 c
+4.498 3.792 m
+4.498 2.837 l
+5.101 2.837 l
+5.101 2.308 l
+4.498 2.308 l
+4.498 -0.162 l
+4.498 -0.32 4.52 -0.437 4.571 -0.515 c
+4.63 -0.595 4.719 -0.632 4.836 -0.632 c
+4.925 -0.632 5.012 -0.617 5.101 -0.588 c
+5.101 -1.147 l
+4.954 -1.195 4.8 -1.22 4.645 -1.22 c
+4.388 -1.22 4.193 -1.128 4.057 -0.941 c
+3.917 -0.757 3.851 -0.496 3.851 -0.162 c
+3.851 2.308 l
+3.248 2.308 l
+3.248 2.837 l
+3.851 2.837 l
+3.851 3.792 l
+h
+6.512 2.425 m
+6.766 2.749 7.085 2.911 7.468 2.911 c
+8.173 2.911 8.53 2.44 8.54 1.5 c
+8.54 -1.147 l
+7.894 -1.147 l
+7.894 1.469 l
+7.894 1.782 7.838 2.003 7.732 2.132 c
+7.622 2.256 7.468 2.323 7.262 2.323 c
+7.104 2.323 6.957 2.267 6.82 2.161 c
+6.691 2.051 6.589 1.914 6.512 1.749 c
+6.512 -1.147 l
+5.866 -1.147 l
+5.866 4.498 l
+6.512 4.498 l
+h
+9.363 1.029 m
+9.363 1.606 9.5 2.061 9.775 2.396 c
+10.058 2.738 10.429 2.911 10.892 2.911 c
+11.352 2.911 11.719 2.741 11.994 2.41 c
+12.278 2.087 12.424 1.639 12.436 1.073 c
+12.436 0.646 l
+12.436 0.077 12.293 -0.378 12.009 -0.721 c
+11.734 -1.055 11.366 -1.22 10.907 -1.22 c
+10.444 -1.22 10.073 -1.058 9.789 -0.735 c
+9.514 -0.405 9.371 0.037 9.363 0.588 c
+h
+10.01 0.646 m
+10.01 0.243 10.088 -0.073 10.246 -0.309 c
+10.411 -0.544 10.631 -0.661 10.907 -0.661 c
+11.472 -0.661 11.767 -0.25 11.788 0.573 c
+11.788 1.029 l
+11.788 1.429 11.705 1.749 11.539 1.984 c
+11.381 2.227 11.164 2.352 10.892 2.352 c
+10.628 2.352 10.411 2.227 10.246 1.984 c
+10.088 1.749 10.01 1.429 10.01 1.029 c
+h
+15.302 -0.794 m
+15.085 -1.081 14.772 -1.22 14.361 -1.22 c
+13.997 -1.22 13.722 -1.099 13.538 -0.852 c
+13.361 -0.599 13.266 -0.235 13.259 0.235 c
+13.259 2.837 l
+13.905 2.837 l
+13.905 0.294 l
+13.905 -0.335 14.089 -0.647 14.464 -0.647 c
+14.865 -0.647 15.14 -0.47 15.287 -0.118 c
+15.287 2.837 l
+15.934 2.837 l
+15.934 -1.147 l
+15.316 -1.147 l
+h
+17.772 3.792 m
+17.772 2.837 l
+18.374 2.837 l
+18.374 2.308 l
+17.772 2.308 l
+17.772 -0.162 l
+17.772 -0.32 17.793 -0.437 17.845 -0.515 c
+17.903 -0.595 17.992 -0.632 18.109 -0.632 c
+18.198 -0.632 18.286 -0.617 18.374 -0.588 c
+18.374 -1.147 l
+18.227 -1.195 18.073 -1.22 17.918 -1.22 c
+17.661 -1.22 17.467 -1.128 17.33 -0.941 c
+17.191 -0.757 17.124 -0.496 17.124 -0.162 c
+17.124 2.308 l
+16.522 2.308 l
+16.522 2.837 l
+17.124 2.837 l
+17.124 3.792 l
+h
+22.901 -1.147 m
+22.861 -1.058 22.836 -0.912 22.828 -0.706 c
+22.593 -1.051 22.298 -1.22 21.946 -1.22 c
+21.582 -1.22 21.299 -1.124 21.093 -0.926 c
+20.895 -0.721 20.799 -0.434 20.799 -0.059 c
+20.799 0.341 20.935 0.661 21.211 0.897 c
+21.483 1.139 21.858 1.264 22.328 1.264 c
+22.813 1.264 l
+22.813 1.691 l
+22.813 1.926 22.758 2.091 22.651 2.19 c
+22.541 2.296 22.379 2.352 22.167 2.352 c
+21.968 2.352 21.806 2.293 21.681 2.175 c
+21.564 2.057 21.504 1.911 21.504 1.735 c
+20.858 1.735 l
+20.858 1.929 20.917 2.12 21.034 2.308 c
+21.159 2.491 21.321 2.639 21.519 2.749 c
+21.725 2.855 21.953 2.911 22.21 2.911 c
+22.611 2.911 22.915 2.807 23.121 2.602 c
+23.335 2.396 23.449 2.102 23.46 1.72 c
+23.46 -0.294 l
+23.46 -0.599 23.497 -0.864 23.578 -1.087 c
+23.578 -1.147 l
+h
+22.034 -0.632 m
+22.2 -0.632 22.35 -0.588 22.489 -0.5 c
+22.637 -0.412 22.743 -0.301 22.813 -0.162 c
+22.813 0.779 l
+22.445 0.779 l
+22.13 0.779 21.887 0.709 21.71 0.573 c
+21.534 0.445 21.446 0.257 21.446 0.015 c
+21.446 -0.214 21.49 -0.378 21.579 -0.485 c
+21.666 -0.584 21.818 -0.632 22.034 -0.632 c
+27.341 0.646 m
+27.341 0.018 27.223 -0.452 26.988 -0.765 c
+26.759 -1.07 26.443 -1.22 26.032 -1.22 c
+25.628 -1.22 25.319 -1.07 25.106 -0.765 c
+25.106 -2.675 l
+24.459 -2.675 l
+24.459 2.837 l
+25.047 2.837 l
+25.091 2.396 l
+25.305 2.738 25.613 2.911 26.017 2.911 c
+26.458 2.911 26.786 2.756 27.002 2.454 c
+27.216 2.15 27.329 1.694 27.341 1.087 c
+h
+26.693 1.029 m
+26.693 1.469 26.624 1.793 26.487 1.999 c
+26.348 2.212 26.128 2.323 25.826 2.323 c
+25.51 2.323 25.271 2.168 25.106 1.866 c
+25.106 -0.206 l
+25.271 -0.511 25.51 -0.661 25.826 -0.661 c
+26.121 -0.661 26.333 -0.559 26.473 -0.353 c
+26.609 -0.14 26.682 0.191 26.693 0.632 c
+h
+31.059 0.646 m
+31.059 0.018 30.942 -0.452 30.707 -0.765 c
+30.479 -1.07 30.163 -1.22 29.751 -1.22 c
+29.346 -1.22 29.038 -1.07 28.825 -0.765 c
+28.825 -2.675 l
+28.178 -2.675 l
+28.178 2.837 l
+28.766 2.837 l
+28.81 2.396 l
+29.024 2.738 29.332 2.911 29.736 2.911 c
+30.177 2.911 30.504 2.756 30.721 2.454 c
+30.934 2.15 31.048 1.694 31.059 1.087 c
+h
+30.412 1.029 m
+30.412 1.469 30.343 1.793 30.206 1.999 c
+30.067 2.212 29.847 2.323 29.545 2.323 c
+29.229 2.323 28.991 2.168 28.825 1.866 c
+28.825 -0.206 l
+28.991 -0.511 29.229 -0.661 29.545 -0.661 c
+29.839 -0.661 30.052 -0.559 30.192 -0.353 c
+30.328 -0.14 30.402 0.191 30.412 0.632 c
+h
+32.602 -1.147 -0.646 5.644 re
+34.705 -0.059 m
+35.424 2.837 l
+36.116 2.837 l
+34.822 -1.706 l
+34.723 -2.047 34.58 -2.308 34.396 -2.484 c
+34.219 -2.66 34.017 -2.749 33.793 -2.749 c
+33.705 -2.749 33.591 -2.727 33.456 -2.69 c
+33.456 -2.146 l
+33.602 -2.161 l
+33.786 -2.161 33.932 -2.117 34.043 -2.028 c
+34.15 -1.941 34.238 -1.783 34.308 -1.558 c
+34.425 -1.118 l
+33.264 2.837 l
+33.969 2.837 l
+h
+37.454 -1.147 -0.647 3.984 re
+37.497 3.881 m
+37.497 3.77 37.468 3.678 37.409 3.601 c
+37.35 3.532 37.255 3.499 37.13 3.499 c
+37.012 3.499 36.917 3.532 36.851 3.601 c
+36.791 3.678 36.762 3.77 36.762 3.881 c
+36.762 3.998 36.791 4.09 36.851 4.16 c
+36.917 4.237 37.012 4.278 37.13 4.278 c
+37.255 4.278 37.35 4.237 37.409 4.16 c
+37.468 4.079 37.497 3.987 37.497 3.881 c
+39.085 2.837 m
+39.1 2.396 l
+39.353 2.738 39.677 2.911 40.07 2.911 c
+40.775 2.911 41.132 2.44 41.143 1.5 c
+41.143 -1.147 l
+40.496 -1.147 l
+40.496 1.469 l
+40.496 1.782 40.441 2.003 40.334 2.132 c
+40.224 2.256 40.07 2.323 39.864 2.323 c
+39.706 2.323 39.559 2.267 39.423 2.161 c
+39.295 2.051 39.191 1.914 39.114 1.749 c
+39.114 -1.147 l
+38.468 -1.147 l
+38.468 2.837 l
+h
+41.98 1.029 m
+41.98 1.646 42.091 2.109 42.319 2.425 c
+42.543 2.749 42.877 2.911 43.318 2.911 c
+43.719 2.911 44.024 2.734 44.23 2.381 c
+44.274 2.837 l
+44.862 2.837 l
+44.862 -1.191 l
+44.862 -1.679 44.733 -2.058 44.479 -2.323 c
+44.222 -2.587 43.87 -2.72 43.421 -2.72 c
+43.222 -2.72 43.002 -2.668 42.759 -2.572 c
+42.514 -2.473 42.333 -2.352 42.216 -2.205 c
+42.48 -1.764 l
+42.745 -2.028 43.043 -2.161 43.377 -2.161 c
+43.914 -2.161 44.189 -1.866 44.201 -1.278 c
+44.201 -0.75 l
+43.995 -1.066 43.693 -1.22 43.303 -1.22 c
+42.892 -1.22 42.568 -1.07 42.333 -0.765 c
+42.106 -0.452 41.988 0 41.98 0.588 c
+h
+42.642 0.646 m
+42.642 0.206 42.705 -0.125 42.833 -0.338 c
+42.958 -0.544 43.175 -0.647 43.48 -0.647 c
+43.804 -0.647 44.043 -0.482 44.201 -0.147 c
+44.201 1.837 l
+44.031 2.161 43.792 2.323 43.48 2.323 c
+43.186 2.323 42.969 2.219 42.833 2.014 c
+42.705 1.808 42.642 1.484 42.642 1.043 c
+h
+48.242 -1.147 -0.646 3.984 re
+48.286 3.881 m
+48.286 3.77 48.257 3.678 48.199 3.601 c
+48.14 3.532 48.044 3.499 47.919 3.499 c
+47.802 3.499 47.706 3.532 47.64 3.601 c
+47.581 3.678 47.552 3.77 47.552 3.881 c
+47.552 3.998 47.581 4.09 47.64 4.16 c
+47.706 4.237 47.802 4.278 47.919 4.278 c
+48.044 4.278 48.14 4.237 48.199 4.16 c
+48.257 4.079 48.286 3.987 48.286 3.881 c
+50.109 3.792 m
+50.109 2.837 l
+50.712 2.837 l
+50.712 2.308 l
+50.109 2.308 l
+50.109 -0.162 l
+50.109 -0.32 50.131 -0.437 50.183 -0.515 c
+50.241 -0.595 50.33 -0.632 50.447 -0.632 c
+50.536 -0.632 50.624 -0.617 50.712 -0.588 c
+50.712 -1.147 l
+50.565 -1.195 50.41 -1.22 50.256 -1.22 c
+49.999 -1.22 49.805 -1.128 49.668 -0.941 c
+49.529 -0.757 49.462 -0.496 49.462 -0.162 c
+49.462 2.308 l
+48.86 2.308 l
+48.86 2.837 l
+49.462 2.837 l
+49.462 3.792 l
+h
+53.902 -1.147 -0.647 3.984 re
+53.946 3.881 m
+53.946 3.77 53.917 3.678 53.857 3.601 c
+53.799 3.532 53.703 3.499 53.578 3.499 c
+53.46 3.499 53.365 3.532 53.299 3.601 c
+53.24 3.678 53.211 3.77 53.211 3.881 c
+53.211 3.998 53.24 4.09 53.299 4.16 c
+53.365 4.237 53.46 4.278 53.578 4.278 c
+53.703 4.278 53.799 4.237 53.857 4.16 c
+53.917 4.079 53.946 3.987 53.946 3.881 c
+55.533 2.837 m
+55.548 2.396 l
+55.802 2.738 56.125 2.911 56.518 2.911 c
+57.223 2.911 57.58 2.44 57.591 1.5 c
+57.591 -1.147 l
+56.944 -1.147 l
+56.944 1.469 l
+56.944 1.782 56.889 2.003 56.782 2.132 c
+56.672 2.256 56.518 2.323 56.312 2.323 c
+56.155 2.323 56.007 2.267 55.871 2.161 c
+55.743 2.051 55.64 1.914 55.563 1.749 c
+55.563 -1.147 l
+54.916 -1.147 l
+54.916 2.837 l
+h
+59.413 3.792 m
+59.413 2.837 l
+60.016 2.837 l
+60.016 2.308 l
+59.413 2.308 l
+59.413 -0.162 l
+59.413 -0.32 59.436 -0.437 59.488 -0.515 c
+59.546 -0.595 59.634 -0.632 59.752 -0.632 c
+59.84 -0.632 59.928 -0.617 60.016 -0.588 c
+60.016 -1.147 l
+59.87 -1.195 59.715 -1.22 59.561 -1.22 c
+59.303 -1.22 59.109 -1.128 58.973 -0.941 c
+58.833 -0.757 58.767 -0.496 58.767 -0.162 c
+58.767 2.308 l
+58.164 2.308 l
+58.164 2.837 l
+58.767 2.837 l
+58.767 3.792 l
+h
+60.575 1.029 m
+60.575 1.606 60.711 2.061 60.986 2.396 c
+61.269 2.738 61.641 2.911 62.104 2.911 c
+62.563 2.911 62.931 2.741 63.206 2.41 c
+63.489 2.087 63.636 1.639 63.647 1.073 c
+63.647 0.646 l
+63.647 0.077 63.504 -0.378 63.221 -0.721 c
+62.945 -1.055 62.578 -1.22 62.118 -1.22 c
+61.655 -1.22 61.284 -1.058 61.001 -0.735 c
+60.726 -0.405 60.583 0.037 60.575 0.588 c
+h
+61.221 0.646 m
+61.221 0.243 61.299 -0.073 61.457 -0.309 c
+61.622 -0.544 61.842 -0.661 62.118 -0.661 c
+62.684 -0.661 62.978 -0.25 63 0.573 c
+63 1.029 l
+63 1.429 62.916 1.749 62.75 1.984 c
+62.592 2.227 62.376 2.352 62.104 2.352 c
+61.84 2.352 61.622 2.227 61.457 1.984 c
+61.299 1.749 61.221 1.429 61.221 1.029 c
+h
+f
+Q
+q 1 0 0 1 469.5361 92.4598 cm
+0 0 m
+0.353 2.234 l
+1.352 2.234 l
+0.529 -1.75 l
+-0.338 -1.75 l
+-0.897 0.558 l
+-1.455 -1.75 l
+-2.323 -1.75 l
+-3.146 2.234 l
+-2.146 2.234 l
+-1.793 0 l
+-1.264 2.234 l
+-0.53 2.234 l
+h
+1.749 0.367 m
+1.749 0.974 1.889 1.448 2.175 1.793 c
+2.458 2.135 2.851 2.308 3.351 2.308 c
+3.858 2.308 4.255 2.135 4.542 1.793 c
+4.825 1.448 4.968 0.974 4.968 0.367 c
+4.968 0.103 l
+4.968 -0.497 4.825 -0.967 4.542 -1.309 c
+4.255 -1.654 3.858 -1.823 3.351 -1.823 c
+2.84 -1.823 2.444 -1.654 2.161 -1.309 c
+1.885 -0.967 1.749 -0.493 1.749 0.118 c
+h
+2.793 0.103 m
+2.793 -0.603 2.977 -0.956 3.351 -0.956 c
+3.704 -0.956 3.896 -0.662 3.925 -0.074 c
+3.925 0.367 l
+3.925 0.727 3.873 0.999 3.777 1.176 c
+3.678 1.352 3.535 1.44 3.351 1.44 c
+3.175 1.44 3.035 1.352 2.94 1.176 c
+2.84 0.999 2.793 0.727 2.793 0.367 c
+h
+7.57 1.219 m
+7.231 1.249 l
+6.945 1.249 6.755 1.124 6.659 0.881 c
+6.659 -1.75 l
+5.615 -1.75 l
+5.615 2.234 l
+6.585 2.234 l
+6.614 1.793 l
+6.78 2.135 7.011 2.308 7.305 2.308 c
+7.423 2.308 7.515 2.285 7.584 2.248 c
+h
+9.437 -0.221 m
+9.157 -0.53 l
+9.157 -1.75 l
+8.114 -1.75 l
+8.114 3.895 l
+9.157 3.895 l
+9.157 0.852 l
+9.275 1.043 l
+9.995 2.234 l
+11.245 2.234 l
+10.084 0.588 l
+11.347 -1.75 l
+10.157 -1.75 l
+h
+12.803 -1.75 -1.043 3.984 re
+11.715 3.262 m
+11.715 3.417 11.763 3.546 11.862 3.645 c
+11.969 3.752 12.104 3.806 12.274 3.806 c
+12.45 3.806 12.586 3.752 12.685 3.645 c
+12.792 3.546 12.847 3.417 12.847 3.262 c
+12.847 3.094 12.792 2.958 12.685 2.851 c
+12.586 2.752 12.45 2.705 12.274 2.705 c
+12.104 2.705 11.969 2.752 11.862 2.851 c
+11.763 2.958 11.715 3.094 11.715 3.262 c
+14.566 2.234 m
+14.596 1.837 l
+14.831 2.15 15.133 2.308 15.507 2.308 c
+16.191 2.308 16.544 1.826 16.565 0.866 c
+16.565 -1.75 l
+15.522 -1.75 l
+15.522 0.793 l
+15.522 1.018 15.486 1.179 15.42 1.278 c
+15.349 1.373 15.232 1.425 15.066 1.425 c
+14.879 1.425 14.732 1.33 14.626 1.146 c
+14.626 -1.75 l
+13.581 -1.75 l
+13.581 2.234 l
+h
+17.183 0.367 m
+17.183 1.014 17.301 1.499 17.536 1.822 c
+17.771 2.146 18.102 2.308 18.535 2.308 c
+18.888 2.308 19.16 2.164 19.359 1.881 c
+19.403 2.234 l
+20.343 2.234 l
+20.343 -1.75 l
+20.343 -2.257 20.201 -2.646 19.917 -2.911 c
+19.63 -3.183 19.226 -3.323 18.697 -3.323 c
+18.47 -3.323 18.234 -3.278 17.992 -3.19 c
+17.756 -3.102 17.58 -2.988 17.462 -2.852 c
+17.815 -2.132 l
+17.911 -2.238 18.04 -2.323 18.198 -2.382 c
+18.352 -2.448 18.499 -2.484 18.638 -2.484 c
+18.873 -2.484 19.039 -2.426 19.138 -2.309 c
+19.245 -2.198 19.299 -2.022 19.299 -1.779 c
+19.299 -1.426 l
+19.102 -1.691 18.844 -1.823 18.52 -1.823 c
+18.098 -1.823 17.771 -1.661 17.536 -1.338 c
+17.308 -1.008 17.19 -0.537 17.183 0.073 c
+h
+18.227 0.103 m
+18.227 -0.273 18.275 -0.541 18.374 -0.706 c
+18.47 -0.875 18.624 -0.956 18.829 -0.956 c
+19.042 -0.956 19.201 -0.879 19.299 -0.721 c
+19.299 1.176 l
+19.189 1.341 19.035 1.425 18.829 1.425 c
+18.624 1.425 18.47 1.341 18.374 1.176 c
+18.275 1.007 18.227 0.738 18.227 0.367 c
+h
+22.695 0.367 m
+22.695 1.014 22.802 1.499 23.019 1.822 c
+23.243 2.146 23.566 2.308 23.989 2.308 c
+24.301 2.308 24.554 2.175 24.753 1.911 c
+24.753 3.895 l
+25.811 3.895 l
+25.811 -1.75 l
+24.856 -1.75 l
+24.812 -1.338 l
+24.595 -1.661 24.319 -1.823 23.989 -1.823 c
+23.577 -1.823 23.258 -1.669 23.033 -1.353 c
+22.817 -1.029 22.703 -0.559 22.695 0.058 c
+h
+23.739 0.103 m
+23.739 -0.291 23.775 -0.566 23.856 -0.721 c
+23.945 -0.879 24.091 -0.956 24.297 -0.956 c
+24.503 -0.956 24.654 -0.864 24.753 -0.676 c
+24.753 1.132 l
+24.654 1.326 24.503 1.425 24.297 1.425 c
+24.099 1.425 23.96 1.344 23.871 1.19 c
+23.783 1.043 23.739 0.771 23.739 0.382 c
+h
+27.663 -1.75 -1.043 3.984 re
+26.576 3.262 m
+26.576 3.417 26.624 3.546 26.723 3.645 c
+26.829 3.752 26.965 3.806 27.135 3.806 c
+27.311 3.806 27.447 3.752 27.546 3.645 c
+27.652 3.546 27.708 3.417 27.708 3.262 c
+27.708 3.094 27.652 2.958 27.546 2.851 c
+27.447 2.752 27.311 2.705 27.135 2.705 c
+26.965 2.705 26.829 2.752 26.723 2.851 c
+26.624 2.958 26.576 3.094 26.576 3.262 c
+30.427 1.219 m
+30.089 1.249 l
+29.803 1.249 29.611 1.124 29.516 0.881 c
+29.516 -1.75 l
+28.471 -1.75 l
+28.471 2.234 l
+29.442 2.234 l
+29.471 1.793 l
+29.637 2.135 29.868 2.308 30.162 2.308 c
+30.28 2.308 30.372 2.285 30.441 2.248 c
+h
+32.5 -1.823 m
+31.97 -1.823 31.551 -1.669 31.25 -1.353 c
+30.956 -1.029 30.809 -0.57 30.809 0.029 c
+30.809 0.338 l
+30.809 0.962 30.945 1.448 31.22 1.793 c
+31.492 2.135 31.885 2.308 32.396 2.308 c
+32.897 2.308 33.267 2.146 33.514 1.822 c
+33.767 1.499 33.9 1.021 33.911 0.396 c
+33.911 -0.103 l
+31.838 -0.103 l
+31.856 -0.397 31.919 -0.614 32.029 -0.75 c
+32.147 -0.89 32.327 -0.956 32.573 -0.956 c
+32.915 -0.956 33.205 -0.838 33.44 -0.603 c
+33.852 -1.235 l
+33.724 -1.411 33.535 -1.555 33.294 -1.661 c
+33.047 -1.768 32.783 -1.823 32.5 -1.823 c
+31.853 0.617 m
+32.882 0.617 l
+32.882 0.72 l
+32.882 0.955 32.841 1.132 32.764 1.249 c
+32.694 1.373 32.566 1.44 32.382 1.44 c
+32.205 1.44 32.074 1.371 31.985 1.234 c
+31.904 1.105 31.86 0.9 31.853 0.617 c
+35.939 -0.956 m
+36.233 -0.956 36.384 -0.761 36.394 -0.368 c
+37.365 -0.368 l
+37.365 -0.802 37.233 -1.154 36.968 -1.426 c
+36.703 -1.691 36.365 -1.823 35.954 -1.823 c
+35.443 -1.823 35.05 -1.669 34.778 -1.353 c
+34.513 -1.029 34.374 -0.559 34.366 0.058 c
+34.366 0.382 l
+34.366 1.007 34.499 1.484 34.763 1.807 c
+35.035 2.138 35.432 2.308 35.954 2.308 c
+36.384 2.308 36.726 2.167 36.982 1.896 c
+37.236 1.62 37.365 1.238 37.365 0.749 c
+36.394 0.749 l
+36.394 0.962 36.354 1.132 36.277 1.249 c
+36.207 1.373 36.09 1.44 35.924 1.44 c
+35.748 1.44 35.619 1.373 35.542 1.249 c
+35.461 1.12 35.417 0.87 35.41 0.5 c
+35.41 0.087 l
+35.41 -0.235 35.424 -0.464 35.454 -0.588 c
+35.49 -0.717 35.546 -0.809 35.615 -0.867 c
+35.693 -0.927 35.8 -0.956 35.939 -0.956 c
+39.173 3.204 m
+39.173 2.234 l
+39.702 2.234 l
+39.702 1.44 l
+39.173 1.44 l
+39.173 -0.53 l
+39.173 -0.688 39.191 -0.794 39.232 -0.853 c
+39.28 -0.912 39.364 -0.941 39.482 -0.941 c
+39.588 -0.941 39.673 -0.934 39.731 -0.912 c
+39.731 -1.721 l
+39.555 -1.786 39.364 -1.823 39.158 -1.823 c
+38.482 -1.823 38.137 -1.437 38.129 -0.662 c
+38.129 1.44 l
+37.674 1.44 l
+37.674 2.234 l
+38.129 2.234 l
+38.129 3.204 l
+h
+40.055 0.367 m
+40.055 0.974 40.194 1.448 40.481 1.793 c
+40.764 2.135 41.157 2.308 41.657 2.308 c
+42.164 2.308 42.561 2.135 42.848 1.793 c
+43.131 1.448 43.274 0.974 43.274 0.367 c
+43.274 0.103 l
+43.274 -0.497 43.131 -0.967 42.848 -1.309 c
+42.561 -1.654 42.164 -1.823 41.657 -1.823 c
+41.146 -1.823 40.749 -1.654 40.466 -1.309 c
+40.191 -0.967 40.055 -0.493 40.055 0.118 c
+h
+41.098 0.103 m
+41.098 -0.603 41.282 -0.956 41.657 -0.956 c
+42.01 -0.956 42.201 -0.662 42.231 -0.074 c
+42.231 0.367 l
+42.231 0.727 42.179 0.999 42.083 1.176 c
+41.984 1.352 41.84 1.44 41.657 1.44 c
+41.481 1.44 41.341 1.352 41.246 1.176 c
+41.146 0.999 41.098 0.727 41.098 0.367 c
+h
+45.875 1.219 m
+45.537 1.249 l
+45.25 1.249 45.06 1.124 44.965 0.881 c
+44.965 -1.75 l
+43.92 -1.75 l
+43.92 2.234 l
+44.891 2.234 l
+44.92 1.793 l
+45.086 2.135 45.317 2.308 45.611 2.308 c
+45.728 2.308 45.821 2.285 45.89 2.248 c
+h
+47.772 -0.015 m
+48.301 2.234 l
+49.403 2.234 l
+48.095 -2.352 l
+47.897 -3.017 47.529 -3.352 46.993 -3.352 c
+46.864 -3.352 46.721 -3.33 46.567 -3.293 c
+46.567 -2.469 l
+46.684 -2.484 l
+46.85 -2.484 46.971 -2.448 47.051 -2.382 c
+47.129 -2.323 47.191 -2.213 47.243 -2.058 c
+47.316 -1.794 l
+46.17 2.234 l
+47.286 2.234 l
+h
+f
+Q
+q 1 0 0 1 519.5421 91.0628 cm
+0 0 m
+0 0.118 0.033 0.214 0.103 0.294 c
+0.169 0.372 0.272 0.412 0.412 0.412 c
+0.559 0.412 0.665 0.372 0.735 0.294 c
+0.812 0.214 0.852 0.118 0.852 0 c
+0.852 -0.11 0.812 -0.202 0.735 -0.279 c
+0.665 -0.357 0.559 -0.397 0.412 -0.397 c
+0.272 -0.397 0.169 -0.357 0.103 -0.279 c
+0.033 -0.202 0 -0.11 0 0 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 113.704 234.667 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 106.8644 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.646 -0.243 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.69 -1.999 0.69 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.142 1.278 -3.219 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.32 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.249 -2.454 2.19 c
+-2.477 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.631 5.137 1.779 c
+5.244 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.179 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.165 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.871 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.621 6.137 1.573 c
+6.056 1.532 5.99 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.675 re
+10.124 2.837 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.117 l
+14.494 3.117 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.455 14.552 -0.515 c
+14.589 -0.566 14.645 -0.61 14.714 -0.646 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.687 15.464 -0.676 c
+15.599 -0.658 15.732 -0.632 15.861 -0.603 c
+15.861 -1.205 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.278 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.084 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.455 c
+13.586 -0.36 13.582 -0.264 13.582 -0.176 c
+h
+24.22 -0.25 m
+24.22 -0.419 24.18 -0.569 24.103 -0.706 c
+24.033 -0.834 23.931 -0.948 23.794 -1.043 c
+23.666 -1.132 23.504 -1.201 23.31 -1.249 c
+23.121 -1.297 22.905 -1.323 22.662 -1.323 c
+22.435 -1.323 22.236 -1.309 22.06 -1.278 c
+21.883 -1.249 21.725 -1.201 21.59 -1.132 c
+21.45 -1.055 21.34 -0.956 21.251 -0.838 c
+21.163 -0.721 21.093 -0.573 21.045 -0.397 c
+21.854 -0.279 l
+21.872 -0.378 21.902 -0.455 21.942 -0.515 c
+21.99 -0.573 22.049 -0.617 22.119 -0.646 c
+22.185 -0.676 22.265 -0.702 22.354 -0.721 c
+22.442 -0.731 22.545 -0.735 22.662 -0.735 c
+22.758 -0.735 22.853 -0.731 22.942 -0.721 c
+23.03 -0.702 23.107 -0.676 23.177 -0.646 c
+23.243 -0.617 23.295 -0.58 23.324 -0.529 c
+23.36 -0.482 23.383 -0.419 23.383 -0.338 c
+23.383 -0.243 23.353 -0.169 23.295 -0.118 c
+23.243 -0.07 23.177 -0.029 23.089 0 c
+23.001 0.037 22.89 0.066 22.766 0.088 c
+22.647 0.118 22.516 0.147 22.369 0.177 c
+22.229 0.214 22.09 0.254 21.942 0.294 c
+21.803 0.341 21.677 0.405 21.56 0.485 c
+21.45 0.563 21.361 0.661 21.296 0.779 c
+21.226 0.897 21.193 1.047 21.193 1.235 c
+21.193 1.389 21.222 1.532 21.28 1.661 c
+21.347 1.797 21.442 1.911 21.56 1.999 c
+21.685 2.087 21.843 2.153 22.03 2.205 c
+22.215 2.253 22.427 2.278 22.662 2.278 c
+22.846 2.278 23.023 2.256 23.192 2.219 c
+23.357 2.19 23.504 2.135 23.632 2.058 c
+23.757 1.988 23.868 1.889 23.956 1.764 c
+24.044 1.646 24.103 1.503 24.133 1.338 c
+23.339 1.264 l
+23.316 1.341 23.287 1.404 23.25 1.455 c
+23.21 1.514 23.162 1.558 23.104 1.588 c
+23.052 1.625 22.99 1.65 22.913 1.661 c
+22.832 1.668 22.751 1.675 22.662 1.675 c
+22.446 1.675 22.284 1.646 22.177 1.588 c
+22.067 1.536 22.016 1.448 22.016 1.323 c
+22.016 1.242 22.034 1.18 22.074 1.132 c
+22.122 1.08 22.185 1.043 22.265 1.014 c
+22.354 0.985 22.45 0.956 22.56 0.926 c
+22.666 0.904 22.788 0.882 22.927 0.852 c
+23.081 0.823 23.239 0.783 23.397 0.735 c
+23.551 0.684 23.692 0.621 23.809 0.544 c
+23.927 0.463 24.022 0.36 24.103 0.235 c
+24.18 0.106 24.22 -0.056 24.22 -0.25 c
+25.783 1.602 m
+25.238 1.602 l
+25.238 2.219 l
+25.826 2.219 l
+26.106 3.117 l
+26.679 3.117 l
+26.679 2.219 l
+27.914 2.219 l
+27.914 1.602 l
+26.679 1.602 l
+26.679 -0.103 l
+26.679 -0.324 l
+26.686 -0.393 26.709 -0.455 26.738 -0.515 c
+26.774 -0.566 26.83 -0.61 26.9 -0.646 c
+26.977 -0.676 27.09 -0.691 27.237 -0.691 c
+27.374 -0.691 27.509 -0.687 27.649 -0.676 c
+27.785 -0.658 27.917 -0.632 28.046 -0.603 c
+28.046 -1.205 l
+27.965 -1.216 27.888 -1.231 27.811 -1.249 c
+27.73 -1.261 27.653 -1.268 27.576 -1.278 c
+27.495 -1.286 27.407 -1.294 27.312 -1.294 c
+27.223 -1.301 27.123 -1.309 27.017 -1.309 c
+26.83 -1.309 26.668 -1.294 26.532 -1.264 c
+26.404 -1.228 26.29 -1.183 26.194 -1.132 c
+26.106 -1.084 26.032 -1.025 25.974 -0.956 c
+25.915 -0.879 25.87 -0.802 25.841 -0.721 c
+25.812 -0.632 25.789 -0.544 25.783 -0.455 c
+25.772 -0.36 25.768 -0.264 25.768 -0.176 c
+h
+30.196 -1.323 m
+30.027 -1.323 29.876 -1.301 29.741 -1.264 c
+29.612 -1.216 29.498 -1.147 29.402 -1.058 c
+29.313 -0.97 29.244 -0.864 29.196 -0.735 c
+29.145 -0.599 29.122 -0.449 29.122 -0.279 c
+29.122 -0.073 29.155 0.096 29.226 0.235 c
+29.292 0.382 29.388 0.492 29.505 0.573 c
+29.63 0.661 29.773 0.724 29.931 0.765 c
+30.096 0.802 30.273 0.827 30.46 0.838 c
+31.181 0.852 l
+31.181 1.029 l
+31.181 1.147 31.17 1.249 31.152 1.338 c
+31.129 1.426 31.096 1.492 31.048 1.544 c
+31.008 1.602 30.96 1.639 30.901 1.661 c
+30.842 1.679 30.776 1.691 30.71 1.691 c
+30.641 1.691 30.578 1.679 30.519 1.661 c
+30.468 1.65 30.42 1.625 30.372 1.588 c
+30.331 1.558 30.298 1.507 30.269 1.44 c
+30.248 1.382 30.233 1.301 30.225 1.205 c
+29.284 1.249 l
+29.313 1.396 29.358 1.532 29.417 1.661 c
+29.483 1.786 29.579 1.897 29.696 1.984 c
+29.814 2.08 29.953 2.153 30.122 2.205 c
+30.298 2.253 30.504 2.278 30.74 2.278 c
+31.181 2.278 31.511 2.168 31.74 1.955 c
+31.975 1.749 32.092 1.44 32.092 1.029 c
+32.092 -0.235 l
+32.092 -0.455 l
+32.099 -0.515 32.114 -0.569 32.136 -0.617 c
+32.154 -0.658 32.184 -0.691 32.224 -0.721 c
+32.261 -0.742 32.313 -0.75 32.371 -0.75 c
+32.438 -0.75 32.507 -0.746 32.577 -0.735 c
+32.577 -1.22 l
+32.519 -1.231 32.463 -1.242 32.415 -1.249 c
+32.375 -1.261 32.334 -1.268 32.297 -1.278 c
+32.257 -1.286 32.213 -1.294 32.166 -1.294 c
+32.114 -1.301 32.055 -1.309 31.989 -1.309 c
+31.761 -1.309 31.596 -1.257 31.489 -1.147 c
+31.379 -1.029 31.316 -0.864 31.298 -0.646 c
+31.283 -0.646 l
+31.214 -0.757 31.144 -0.852 31.077 -0.941 c
+31.008 -1.022 30.931 -1.087 30.842 -1.147 c
+30.755 -1.205 30.655 -1.249 30.549 -1.278 c
+30.449 -1.309 30.331 -1.323 30.196 -1.323 c
+31.181 0.353 m
+30.755 0.338 l
+30.655 0.338 30.564 0.331 30.475 0.324 c
+30.394 0.312 30.328 0.287 30.269 0.25 c
+30.211 0.21 30.159 0.151 30.122 0.073 c
+30.082 0.004 30.063 -0.088 30.063 -0.206 c
+30.063 -0.374 30.096 -0.496 30.167 -0.573 c
+30.233 -0.654 30.331 -0.691 30.46 -0.691 c
+30.567 -0.691 30.666 -0.669 30.755 -0.617 c
+30.85 -0.569 30.931 -0.507 30.99 -0.426 c
+31.056 -0.349 31.107 -0.261 31.137 -0.162 c
+31.166 -0.056 31.181 0.059 31.181 0.177 c
+h
+36.403 -0.25 m
+36.403 -0.419 36.362 -0.569 36.285 -0.706 c
+36.215 -0.834 36.112 -0.948 35.976 -1.043 c
+35.848 -1.132 35.686 -1.201 35.491 -1.249 c
+35.304 -1.297 35.087 -1.323 34.844 -1.323 c
+34.616 -1.323 34.418 -1.309 34.242 -1.278 c
+34.065 -1.249 33.907 -1.201 33.772 -1.132 c
+33.631 -1.055 33.521 -0.956 33.433 -0.838 c
+33.345 -0.721 33.275 -0.573 33.228 -0.397 c
+34.036 -0.279 l
+34.055 -0.378 34.084 -0.455 34.124 -0.515 c
+34.171 -0.573 34.231 -0.617 34.3 -0.646 c
+34.367 -0.676 34.447 -0.702 34.535 -0.721 c
+34.624 -0.731 34.726 -0.735 34.844 -0.735 c
+34.94 -0.735 35.035 -0.731 35.123 -0.721 c
+35.212 -0.702 35.289 -0.676 35.359 -0.646 c
+35.425 -0.617 35.476 -0.58 35.505 -0.529 c
+35.543 -0.482 35.565 -0.419 35.565 -0.338 c
+35.565 -0.243 35.535 -0.169 35.476 -0.118 c
+35.425 -0.07 35.359 -0.029 35.27 0 c
+35.183 0.037 35.073 0.066 34.948 0.088 c
+34.83 0.118 34.697 0.147 34.551 0.177 c
+34.41 0.214 34.271 0.254 34.124 0.294 c
+33.984 0.341 33.859 0.405 33.742 0.485 c
+33.631 0.563 33.544 0.661 33.477 0.779 c
+33.408 0.897 33.375 1.047 33.375 1.235 c
+33.375 1.389 33.404 1.532 33.463 1.661 c
+33.529 1.797 33.624 1.911 33.742 1.999 c
+33.867 2.087 34.025 2.153 34.212 2.205 c
+34.396 2.253 34.609 2.278 34.844 2.278 c
+35.028 2.278 35.204 2.256 35.374 2.219 c
+35.539 2.19 35.686 2.135 35.815 2.058 c
+35.939 1.988 36.05 1.889 36.138 1.764 c
+36.226 1.646 36.285 1.503 36.314 1.338 c
+35.52 1.264 l
+35.499 1.341 35.469 1.404 35.432 1.455 c
+35.392 1.514 35.345 1.558 35.285 1.588 c
+35.234 1.625 35.171 1.65 35.094 1.661 c
+35.013 1.668 34.932 1.675 34.844 1.675 c
+34.628 1.675 34.466 1.646 34.36 1.588 c
+34.249 1.536 34.198 1.448 34.198 1.323 c
+34.198 1.242 34.216 1.18 34.256 1.132 c
+34.304 1.08 34.367 1.043 34.447 1.014 c
+34.535 0.985 34.631 0.956 34.742 0.926 c
+34.848 0.904 34.969 0.882 35.109 0.852 c
+35.264 0.823 35.422 0.783 35.58 0.735 c
+35.734 0.684 35.873 0.621 35.991 0.544 c
+36.108 0.463 36.204 0.36 36.285 0.235 c
+36.362 0.106 36.403 -0.056 36.403 -0.25 c
+38.262 1.514 m
+38.379 1.786 38.53 1.984 38.718 2.102 c
+38.901 2.219 39.122 2.278 39.379 2.278 c
+39.584 2.278 39.754 2.242 39.894 2.176 c
+40.041 2.105 40.151 2.014 40.232 1.897 c
+40.32 1.779 40.378 1.635 40.408 1.47 c
+40.444 1.301 40.467 1.124 40.467 0.941 c
+40.467 -1.264 l
+39.555 -1.264 l
+39.555 0.735 l
+39.555 0.871 39.544 0.992 39.526 1.103 c
+39.515 1.209 39.489 1.297 39.453 1.367 c
+39.412 1.444 39.353 1.503 39.276 1.544 c
+39.206 1.58 39.114 1.602 38.996 1.602 c
+38.886 1.602 38.791 1.577 38.703 1.529 c
+38.615 1.477 38.534 1.411 38.468 1.323 c
+38.409 1.235 38.358 1.124 38.321 1 c
+38.291 0.882 38.277 0.75 38.277 0.603 c
+38.277 -1.264 l
+37.365 -1.264 l
+37.365 3.513 l
+38.277 3.513 l
+38.277 2.205 l
+38.277 2.135 38.269 2.065 38.262 1.999 c
+38.262 1.793 l
+38.262 1.735 38.254 1.679 38.248 1.631 c
+38.248 1.514 l
+h
+47.662 -1.264 m
+47.651 -1.246 47.64 -1.216 47.632 -1.176 c
+47.632 -1.128 47.625 -1.08 47.617 -1.029 c
+47.617 -0.97 47.611 -0.912 47.603 -0.852 c
+47.603 -0.691 l
+47.486 -0.926 47.342 -1.095 47.177 -1.191 c
+47.008 -1.278 46.809 -1.323 46.574 -1.323 c
+46.376 -1.323 46.2 -1.278 46.045 -1.191 c
+45.887 -1.103 45.755 -0.981 45.648 -0.823 c
+45.549 -0.658 45.472 -0.467 45.413 -0.25 c
+45.362 -0.037 45.34 0.206 45.34 0.47 c
+45.34 0.735 45.362 0.974 45.413 1.191 c
+45.472 1.415 45.549 1.606 45.648 1.764 c
+45.755 1.918 45.887 2.043 46.045 2.132 c
+46.21 2.227 46.401 2.278 46.618 2.278 c
+46.714 2.278 46.809 2.263 46.898 2.234 c
+46.993 2.213 47.089 2.179 47.177 2.132 c
+47.265 2.08 47.342 2.018 47.412 1.941 c
+47.489 1.86 47.552 1.768 47.603 1.661 c
+47.603 1.749 l
+47.603 1.897 l
+47.603 2.058 l
+47.603 2.234 l
+47.603 3.513 l
+48.5 3.513 l
+48.5 -0.5 l
+48.5 -0.676 48.504 -0.834 48.515 -0.97 c
+48.521 -1.099 48.529 -1.198 48.529 -1.264 c
+h
+47.617 0.485 m
+47.617 0.721 47.592 0.912 47.544 1.058 c
+47.504 1.213 47.449 1.338 47.382 1.426 c
+47.324 1.514 47.254 1.573 47.177 1.602 c
+47.096 1.639 47.019 1.661 46.942 1.661 c
+46.842 1.661 46.751 1.635 46.663 1.588 c
+46.582 1.548 46.516 1.477 46.457 1.382 c
+46.405 1.282 46.362 1.161 46.324 1.014 c
+46.295 0.867 46.281 0.684 46.281 0.47 c
+46.281 0.077 46.331 -0.216 46.441 -0.411 c
+46.559 -0.61 46.721 -0.706 46.927 -0.706 c
+47.004 -0.706 47.081 -0.687 47.162 -0.646 c
+47.239 -0.61 47.313 -0.544 47.382 -0.455 c
+47.449 -0.368 47.504 -0.246 47.544 -0.088 c
+47.592 0.066 47.617 0.257 47.617 0.485 c
+52.56 1.47 m
+52.461 1.477 52.359 1.488 52.251 1.5 c
+52.141 1.517 52.02 1.529 51.885 1.529 c
+51.708 1.529 51.55 1.488 51.414 1.411 c
+51.274 1.341 51.156 1.242 51.061 1.118 c
+50.973 0.989 50.903 0.842 50.855 0.676 c
+50.815 0.507 50.796 0.331 50.796 0.147 c
+50.796 -1.264 l
+49.9 -1.264 l
+49.9 0.985 l
+49.9 1.11 49.889 1.235 49.87 1.353 c
+49.859 1.477 49.845 1.595 49.826 1.706 c
+49.815 1.823 49.801 1.918 49.782 1.999 c
+49.76 2.087 49.742 2.161 49.724 2.219 c
+50.605 2.219 l
+50.613 2.168 50.624 2.117 50.634 2.058 c
+50.653 1.999 50.668 1.933 50.679 1.866 c
+50.697 1.808 50.712 1.742 50.723 1.675 c
+50.73 1.606 50.742 1.544 50.753 1.484 c
+50.767 1.484 l
+50.804 1.602 50.855 1.708 50.914 1.808 c
+50.981 1.903 51.061 1.988 51.149 2.058 c
+51.237 2.124 51.34 2.179 51.458 2.219 c
+51.583 2.256 51.73 2.278 51.899 2.278 c
+52.024 2.278 52.141 2.271 52.251 2.263 c
+52.369 2.253 52.473 2.238 52.56 2.219 c
+h
+56.812 0.485 m
+56.812 0.21 56.776 -0.04 56.71 -0.264 c
+56.639 -0.482 56.537 -0.669 56.4 -0.823 c
+56.261 -0.981 56.084 -1.103 55.871 -1.191 c
+55.654 -1.278 55.401 -1.323 55.107 -1.323 c
+54.831 -1.323 54.585 -1.278 54.372 -1.191 c
+54.166 -1.103 53.994 -0.981 53.857 -0.823 c
+53.718 -0.669 53.615 -0.482 53.549 -0.264 c
+53.479 -0.04 53.446 0.21 53.446 0.485 c
+53.446 0.738 53.475 0.974 53.535 1.191 c
+53.6 1.415 53.703 1.606 53.843 1.764 c
+53.979 1.929 54.156 2.058 54.372 2.146 c
+54.585 2.234 54.842 2.278 55.137 2.278 c
+55.449 2.278 55.71 2.234 55.916 2.146 c
+56.128 2.058 56.301 1.929 56.43 1.764 c
+56.566 1.606 56.665 1.415 56.724 1.191 c
+56.783 0.974 56.812 0.738 56.812 0.485 c
+55.856 0.485 m
+55.856 0.69 55.842 0.867 55.812 1.014 c
+55.791 1.161 55.754 1.282 55.695 1.382 c
+55.636 1.477 55.563 1.548 55.474 1.588 c
+55.386 1.635 55.276 1.661 55.151 1.661 c
+54.887 1.661 54.696 1.562 54.578 1.367 c
+54.46 1.18 54.401 0.885 54.401 0.485 c
+54.401 0.062 54.46 -0.243 54.578 -0.426 c
+54.696 -0.613 54.871 -0.706 55.107 -0.706 c
+55.232 -0.706 55.346 -0.687 55.445 -0.646 c
+55.54 -0.599 55.621 -0.526 55.68 -0.426 c
+55.746 -0.331 55.791 -0.206 55.812 -0.058 c
+55.842 0.088 55.856 0.268 55.856 0.485 c
+58.539 2.219 m
+58.547 2.198 58.554 2.165 58.554 2.117 c
+58.561 2.076 58.569 2.028 58.569 1.97 c
+58.576 1.918 58.584 1.866 58.584 1.808 c
+58.584 1.646 l
+58.598 1.646 l
+58.657 1.764 58.723 1.86 58.804 1.941 c
+58.881 2.018 58.966 2.08 59.054 2.132 c
+59.141 2.19 59.23 2.227 59.318 2.249 c
+59.414 2.267 59.513 2.278 59.612 2.278 c
+59.818 2.278 59.998 2.234 60.157 2.146 c
+60.311 2.058 60.439 1.929 60.538 1.764 c
+60.645 1.606 60.722 1.415 60.774 1.191 c
+60.832 0.974 60.862 0.738 60.862 0.485 c
+60.862 0.22 60.832 -0.025 60.774 -0.25 c
+60.722 -0.467 60.645 -0.658 60.538 -0.823 c
+60.439 -0.981 60.307 -1.103 60.141 -1.191 c
+59.983 -1.278 59.796 -1.323 59.583 -1.323 c
+59.484 -1.323 59.384 -1.311 59.289 -1.294 c
+59.189 -1.272 59.098 -1.242 59.01 -1.191 c
+58.929 -1.143 58.852 -1.08 58.775 -0.999 c
+58.705 -0.922 58.646 -0.831 58.598 -0.721 c
+58.584 -0.721 l
+58.584 -0.808 l
+58.591 -0.849 58.598 -0.897 58.598 -0.956 c
+58.598 -1.117 l
+58.598 -1.294 l
+58.598 -2.631 l
+57.687 -2.631 l
+57.687 1.455 l
+57.687 1.621 57.68 1.768 57.672 1.897 c
+57.672 2.219 l
+h
+58.584 0.455 m
+58.584 0.228 58.602 0.037 58.642 -0.118 c
+58.69 -0.264 58.745 -0.382 58.804 -0.47 c
+58.87 -0.559 58.944 -0.625 59.024 -0.661 c
+59.101 -0.702 59.179 -0.721 59.259 -0.721 c
+59.355 -0.721 59.443 -0.698 59.524 -0.646 c
+59.612 -0.599 59.679 -0.529 59.729 -0.441 c
+59.789 -0.345 59.833 -0.22 59.862 -0.073 c
+59.899 0.081 59.921 0.268 59.921 0.485 c
+59.921 0.875 59.862 1.168 59.744 1.367 c
+59.634 1.562 59.48 1.661 59.274 1.661 c
+59.193 1.661 59.116 1.639 59.039 1.602 c
+58.958 1.562 58.885 1.5 58.819 1.411 c
+58.749 1.323 58.69 1.198 58.642 1.043 c
+58.602 0.885 58.584 0.69 58.584 0.455 c
+f
+Q
+0.113 0.082 0.09 0 k
+287.665 197.991 234.667 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 292.8568 191.1528 cm
+0 0 m
+0 -0.188 -0.029 -0.353 -0.087 -0.5 c
+-0.147 -0.647 -0.243 -0.776 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.206 -1.278 -1.243 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.359 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.148 c
+-2.55 -0.228 -2.506 -0.302 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.675 c
+-1.977 0.683 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.142 1.278 -3.219 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.219 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.991 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.202 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.263 -0.941 1.205 c
+-0.756 1.146 -0.595 1.066 -0.455 0.97 c
+-0.32 0.87 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.319 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.477 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.201 1.558 -2.131 1.529 c
+-2.065 1.499 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.103 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.558 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.184 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.519 -2.484 5.402 -2.396 c
+5.284 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.754 5.964 -1.86 6.064 -1.941 c
+6.159 -2.029 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.029 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.324 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.89 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.215 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.814 0.243 4.814 0.5 c
+4.814 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.244 1.932 5.376 2.05 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.188 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.969 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.129 2.164 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.993 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.636 7.871 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.5 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.499 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.225 1.62 6.137 1.573 c
+6.056 1.532 5.99 1.462 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.874 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.383 c
+6.924 -0.294 6.978 -0.177 7.019 -0.03 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.597 1.602 m
+13.053 1.602 l
+13.053 2.219 l
+13.641 2.219 l
+13.92 3.116 l
+14.494 3.116 l
+14.494 2.219 l
+15.728 2.219 l
+15.728 1.602 l
+14.494 1.602 l
+14.494 -0.103 l
+14.494 -0.324 l
+14.501 -0.393 14.523 -0.456 14.552 -0.515 c
+14.589 -0.566 14.645 -0.611 14.714 -0.647 c
+14.791 -0.676 14.905 -0.691 15.052 -0.691 c
+15.188 -0.691 15.324 -0.688 15.464 -0.676 c
+15.599 -0.659 15.732 -0.632 15.861 -0.603 c
+15.861 -1.206 l
+15.78 -1.216 15.703 -1.231 15.626 -1.249 c
+15.545 -1.261 15.468 -1.268 15.391 -1.279 c
+15.31 -1.286 15.221 -1.294 15.125 -1.294 c
+15.038 -1.301 14.938 -1.309 14.832 -1.309 c
+14.645 -1.309 14.483 -1.294 14.346 -1.264 c
+14.218 -1.228 14.104 -1.183 14.009 -1.132 c
+13.92 -1.085 13.847 -1.025 13.788 -0.956 c
+13.729 -0.879 13.685 -0.802 13.656 -0.721 c
+13.627 -0.632 13.604 -0.544 13.597 -0.456 c
+13.586 -0.36 13.582 -0.265 13.582 -0.177 c
+h
+24.133 1.469 m
+24.033 1.477 23.931 1.488 23.823 1.499 c
+23.713 1.517 23.592 1.529 23.456 1.529 c
+23.28 1.529 23.121 1.488 22.986 1.411 c
+22.846 1.341 22.728 1.242 22.633 1.117 c
+22.545 0.988 22.475 0.841 22.427 0.675 c
+22.387 0.507 22.369 0.33 22.369 0.147 c
+22.369 -1.264 l
+21.471 -1.264 l
+21.471 0.985 l
+21.471 1.109 21.461 1.234 21.442 1.352 c
+21.432 1.477 21.417 1.595 21.398 1.705 c
+21.388 1.822 21.373 1.918 21.355 1.999 c
+21.332 2.087 21.314 2.161 21.296 2.219 c
+22.177 2.219 l
+22.185 2.167 22.196 2.117 22.207 2.057 c
+22.225 1.999 22.24 1.932 22.251 1.866 c
+22.269 1.808 22.284 1.741 22.296 1.675 c
+22.302 1.606 22.313 1.543 22.325 1.484 c
+22.339 1.484 l
+22.375 1.602 22.427 1.708 22.486 1.808 c
+22.552 1.903 22.633 1.988 22.722 2.057 c
+22.809 2.124 22.913 2.179 23.03 2.219 c
+23.155 2.256 23.302 2.278 23.471 2.278 c
+23.596 2.278 23.713 2.271 23.823 2.263 c
+23.942 2.252 24.044 2.238 24.133 2.219 c
+h
+26.341 -1.264 m
+26.341 0.852 l
+26.341 1.018 26.333 1.153 26.327 1.263 c
+26.315 1.371 26.297 1.455 26.267 1.514 c
+26.246 1.579 26.216 1.631 26.179 1.66 c
+26.15 1.691 26.109 1.705 26.061 1.705 c
+26.003 1.705 25.947 1.675 25.9 1.616 c
+25.86 1.565 25.826 1.492 25.797 1.396 c
+25.768 1.308 25.742 1.194 25.724 1.058 c
+25.712 0.918 25.709 0.768 25.709 0.602 c
+25.709 -1.264 l
+24.96 -1.264 l
+24.96 1.469 l
+24.96 1.705 l
+24.96 1.926 l
+24.96 2.003 24.952 2.065 24.945 2.117 c
+24.945 2.219 l
+25.621 2.219 l
+25.621 2.131 l
+25.621 1.984 l
+25.628 1.926 25.635 1.866 25.635 1.808 c
+25.635 1.646 l
+25.65 1.646 l
+25.668 1.734 25.698 1.811 25.739 1.881 c
+25.775 1.959 25.82 2.028 25.87 2.087 c
+25.93 2.146 25.995 2.19 26.076 2.219 c
+26.154 2.256 26.242 2.278 26.341 2.278 c
+26.525 2.278 26.664 2.223 26.753 2.117 c
+26.848 2.017 26.918 1.859 26.959 1.646 c
+26.973 1.646 l
+27.01 1.741 27.05 1.83 27.09 1.911 c
+27.138 1.988 27.194 2.05 27.252 2.102 c
+27.312 2.161 27.377 2.204 27.458 2.234 c
+27.535 2.263 27.624 2.278 27.723 2.278 c
+27.859 2.278 27.973 2.252 28.06 2.204 c
+28.149 2.153 28.215 2.08 28.266 1.984 c
+28.326 1.885 28.362 1.756 28.384 1.602 c
+28.413 1.455 28.428 1.271 28.428 1.058 c
+28.428 -1.264 l
+27.708 -1.264 l
+27.708 0.852 l
+27.708 1.018 27.701 1.153 27.693 1.263 c
+27.682 1.371 27.664 1.455 27.634 1.514 c
+27.613 1.579 27.583 1.631 27.547 1.66 c
+27.517 1.691 27.476 1.705 27.429 1.705 c
+27.312 1.705 27.216 1.616 27.15 1.44 c
+27.09 1.271 27.061 1.014 27.061 0.661 c
+27.061 -1.264 l
+h
+34.051 -2.631 m
+34.051 3.513 l
+35.976 3.513 l
+35.976 2.896 l
+34.903 2.896 l
+34.903 -2.014 l
+35.976 -2.014 l
+35.976 -2.631 l
+h
+39.012 1.602 m
+39.012 -1.264 l
+38.115 -1.264 l
+38.115 1.602 l
+37.292 1.602 l
+37.292 2.219 l
+38.115 2.219 l
+38.115 2.484 l
+38.115 2.609 38.13 2.741 38.159 2.881 c
+38.196 3.017 38.266 3.135 38.365 3.233 c
+38.472 3.34 38.615 3.428 38.791 3.498 c
+38.967 3.564 39.191 3.601 39.467 3.601 c
+39.68 3.601 39.879 3.59 40.055 3.572 c
+40.232 3.549 40.382 3.532 40.511 3.513 c
+40.511 2.925 l
+40.382 2.944 40.239 2.958 40.085 2.969 c
+39.927 2.977 39.776 2.984 39.629 2.984 c
+39.501 2.984 39.397 2.969 39.32 2.94 c
+39.239 2.91 39.177 2.869 39.129 2.822 c
+39.077 2.77 39.044 2.708 39.027 2.631 c
+39.015 2.561 39.012 2.484 39.012 2.396 c
+39.012 2.219 l
+40.438 2.219 l
+40.438 1.602 l
+h
+43.528 -0.647 m
+44.66 -0.647 l
+44.66 -1.264 l
+41.352 -1.264 l
+41.352 -0.647 l
+42.616 -0.647 l
+42.616 1.602 l
+41.691 1.602 l
+41.691 2.219 l
+43.528 2.219 l
+h
+42.616 3.513 0.912 -0.676 re
+42.616 2.836 m
+47.588 -0.647 m
+48.72 -0.647 l
+48.72 -1.264 l
+45.413 -1.264 l
+45.413 -0.647 l
+46.677 -0.647 l
+46.677 2.896 l
+45.751 2.896 l
+45.751 3.513 l
+47.588 3.513 l
+h
+51.105 -1.324 m
+50.848 -1.324 50.62 -1.286 50.414 -1.22 c
+50.208 -1.143 50.032 -1.029 49.886 -0.882 c
+49.738 -0.728 49.62 -0.537 49.533 -0.309 c
+49.452 -0.085 49.415 0.18 49.415 0.484 c
+49.415 0.816 49.458 1.095 49.547 1.323 c
+49.643 1.558 49.772 1.741 49.929 1.881 c
+50.094 2.017 50.283 2.117 50.488 2.175 c
+50.694 2.242 50.903 2.278 51.12 2.278 c
+51.392 2.278 51.627 2.227 51.825 2.131 c
+52.031 2.043 52.197 1.911 52.325 1.734 c
+52.461 1.565 52.56 1.359 52.619 1.117 c
+52.685 0.881 52.722 0.617 52.722 0.323 c
+52.722 0.309 l
+50.356 0.309 l
+50.356 0.162 50.37 0.022 50.399 -0.103 c
+50.437 -0.231 50.491 -0.345 50.561 -0.441 c
+50.628 -0.53 50.712 -0.599 50.811 -0.647 c
+50.907 -0.698 51.021 -0.721 51.149 -0.721 c
+51.303 -0.721 51.443 -0.688 51.561 -0.618 c
+51.686 -0.551 51.774 -0.449 51.825 -0.309 c
+52.664 -0.383 l
+52.634 -0.482 52.579 -0.588 52.502 -0.706 c
+52.421 -0.816 52.318 -0.919 52.193 -1.014 c
+52.076 -1.103 51.921 -1.176 51.737 -1.235 c
+51.561 -1.294 51.347 -1.324 51.105 -1.324 c
+51.105 1.705 m
+51.017 1.705 50.929 1.691 50.84 1.66 c
+50.753 1.631 50.672 1.579 50.605 1.514 c
+50.536 1.444 50.477 1.356 50.429 1.249 c
+50.389 1.139 50.37 1.014 50.37 0.867 c
+51.84 0.867 l
+51.84 1.003 51.814 1.124 51.767 1.234 c
+51.727 1.341 51.671 1.429 51.605 1.499 c
+51.546 1.565 51.473 1.616 51.384 1.646 c
+51.297 1.683 51.201 1.705 51.105 1.705 c
+53.975 -2.631 m
+53.975 -2.014 l
+55.048 -2.014 l
+55.048 2.896 l
+53.975 2.896 l
+53.975 3.513 l
+55.901 3.513 l
+55.901 -2.631 l
+h
+f
+Q
+q 1 0 0 1 318.9586 177.1631 cm
+0 0 m
+-0.941 0 l
+-0.941 -2.161 l
+-1.616 -2.161 l
+-1.616 3.189 l
+-0.118 3.189 l
+0.412 3.189 0.809 3.05 1.073 2.778 c
+1.345 2.502 1.484 2.105 1.484 1.587 c
+1.484 1.253 1.411 0.962 1.264 0.72 c
+1.118 0.474 0.912 0.286 0.647 0.161 c
+1.676 -2.117 l
+1.676 -2.161 l
+0.956 -2.161 l
+h
+-0.941 0.588 m
+-0.118 0.588 l
+0.166 0.588 0.389 0.675 0.559 0.852 c
+0.724 1.036 0.809 1.282 0.809 1.587 c
+0.809 2.271 0.493 2.616 -0.132 2.616 c
+-0.941 2.616 l
+h
+3.72 -2.234 m
+3.219 -2.234 2.837 -2.087 2.573 -1.794 c
+2.309 -1.5 2.176 -1.066 2.176 -0.485 c
+2.176 -0.015 l
+2.176 0.58 2.301 1.047 2.558 1.381 c
+2.822 1.723 3.182 1.896 3.645 1.896 c
+4.105 1.896 4.447 1.741 4.675 1.44 c
+4.91 1.146 5.031 0.683 5.042 0.058 c
+5.042 -0.368 l
+2.822 -0.368 l
+2.822 -0.456 l
+2.822 -0.89 2.899 -1.202 3.057 -1.397 c
+3.223 -1.584 3.454 -1.676 3.749 -1.676 c
+3.944 -1.676 4.116 -1.643 4.263 -1.573 c
+4.41 -1.496 4.546 -1.378 4.675 -1.22 c
+5.012 -1.632 l
+4.726 -2.036 4.296 -2.234 3.72 -2.234 c
+3.645 1.337 m
+3.37 1.337 3.168 1.242 3.043 1.058 c
+2.914 0.87 2.841 0.58 2.822 0.191 c
+4.395 0.191 l
+4.395 0.278 l
+4.373 0.661 4.307 0.929 4.19 1.087 c
+4.072 1.253 3.888 1.337 3.645 1.337 c
+6.438 1.822 m
+6.453 1.454 l
+6.696 1.749 7.015 1.896 7.408 1.896 c
+7.85 1.896 8.158 1.697 8.335 1.308 c
+8.588 1.697 8.937 1.896 9.378 1.896 c
+10.113 1.896 10.488 1.433 10.51 0.514 c
+10.51 -2.161 l
+9.864 -2.161 l
+9.864 0.455 l
+9.864 0.749 9.808 0.962 9.702 1.102 c
+9.602 1.238 9.43 1.308 9.187 1.308 c
+8.989 1.308 8.827 1.227 8.702 1.072 c
+8.584 0.926 8.515 0.735 8.497 0.5 c
+8.497 -2.161 l
+7.834 -2.161 l
+7.834 0.484 l
+7.834 1.032 7.614 1.308 7.173 1.308 c
+6.839 1.308 6.604 1.146 6.468 0.823 c
+6.468 -2.161 l
+5.821 -2.161 l
+5.821 1.822 l
+h
+11.348 0.014 m
+11.348 0.592 11.484 1.047 11.759 1.381 c
+12.042 1.723 12.414 1.896 12.877 1.896 c
+13.336 1.896 13.704 1.727 13.979 1.396 c
+14.263 1.072 14.409 0.625 14.42 0.058 c
+14.42 -0.368 l
+14.42 -0.937 14.277 -1.393 13.993 -1.735 c
+13.718 -2.07 13.351 -2.234 12.892 -2.234 c
+12.428 -2.234 12.057 -2.073 11.774 -1.75 c
+11.499 -1.419 11.356 -0.977 11.348 -0.427 c
+h
+11.994 -0.368 m
+11.994 -0.772 12.072 -1.088 12.23 -1.324 c
+12.395 -1.559 12.616 -1.676 12.892 -1.676 c
+13.457 -1.676 13.752 -1.264 13.773 -0.441 c
+13.773 0.014 l
+13.773 0.415 13.689 0.735 13.523 0.97 c
+13.365 1.213 13.149 1.337 12.877 1.337 c
+12.612 1.337 12.395 1.213 12.23 0.97 c
+12.072 0.735 11.994 0.415 11.994 0.014 c
+h
+16.331 -1.176 m
+17.081 1.822 l
+17.742 1.822 l
+16.566 -2.161 l
+16.081 -2.161 l
+14.89 1.822 l
+15.552 1.822 l
+h
+19.8 -2.234 m
+19.3 -2.234 18.918 -2.087 18.653 -1.794 c
+18.389 -1.5 18.257 -1.066 18.257 -0.485 c
+18.257 -0.015 l
+18.257 0.58 18.381 1.047 18.639 1.381 c
+18.903 1.723 19.263 1.896 19.726 1.896 c
+20.185 1.896 20.528 1.741 20.756 1.44 c
+20.991 1.146 21.112 0.683 21.123 0.058 c
+21.123 -0.368 l
+18.903 -0.368 l
+18.903 -0.456 l
+18.903 -0.89 18.98 -1.202 19.138 -1.397 c
+19.304 -1.584 19.535 -1.676 19.829 -1.676 c
+20.025 -1.676 20.197 -1.643 20.343 -1.573 c
+20.491 -1.496 20.627 -1.378 20.756 -1.22 c
+21.093 -1.632 l
+20.807 -2.036 20.377 -2.234 19.8 -2.234 c
+19.726 1.337 m
+19.451 1.337 19.248 1.242 19.123 1.058 c
+18.995 0.87 18.922 0.58 18.903 0.191 c
+20.476 0.191 l
+20.476 0.278 l
+20.454 0.661 20.388 0.929 20.27 1.087 c
+20.152 1.253 19.969 1.337 19.726 1.337 c
+23.798 -2.161 m
+23.798 1.294 l
+23.283 1.294 l
+23.283 1.822 l
+23.798 1.822 l
+23.798 2.19 l
+23.805 2.62 23.919 2.954 24.137 3.189 c
+24.36 3.432 24.673 3.557 25.077 3.557 c
+25.224 3.557 25.363 3.534 25.503 3.498 c
+25.65 3.457 25.801 3.403 25.959 3.336 c
+25.841 2.763 l
+25.606 2.888 25.363 2.954 25.121 2.954 c
+24.875 2.954 24.702 2.884 24.607 2.748 c
+24.507 2.62 24.459 2.425 24.459 2.16 c
+24.459 1.822 l
+25.106 1.822 l
+25.106 1.294 l
+24.459 1.294 l
+24.459 -2.161 l
+h
+26.267 -2.161 -0.646 3.983 re
+28.017 -2.161 -0.647 5.644 re
+30.456 -2.234 m
+29.957 -2.234 29.575 -2.087 29.311 -1.794 c
+29.045 -1.5 28.914 -1.066 28.914 -0.485 c
+28.914 -0.015 l
+28.914 0.58 29.039 1.047 29.296 1.381 c
+29.56 1.723 29.92 1.896 30.383 1.896 c
+30.843 1.896 31.185 1.741 31.412 1.44 c
+31.647 1.146 31.769 0.683 31.78 0.058 c
+31.78 -0.368 l
+29.56 -0.368 l
+29.56 -0.456 l
+29.56 -0.89 29.637 -1.202 29.795 -1.397 c
+29.961 -1.584 30.192 -1.676 30.487 -1.676 c
+30.681 -1.676 30.853 -1.643 31.001 -1.573 c
+31.148 -1.496 31.283 -1.378 31.412 -1.22 c
+31.75 -1.632 l
+31.464 -2.036 31.034 -2.234 30.456 -2.234 c
+30.383 1.337 m
+30.107 1.337 29.905 1.242 29.781 1.058 c
+29.652 0.87 29.579 0.58 29.56 0.191 c
+31.133 0.191 l
+31.133 0.278 l
+31.111 0.661 31.044 0.929 30.927 1.087 c
+30.809 1.253 30.626 1.337 30.383 1.337 c
+34.572 -2.161 m
+34.572 1.294 l
+34.043 1.294 l
+34.043 1.822 l
+34.572 1.822 l
+34.572 2.278 l
+34.572 2.678 34.668 2.991 34.867 3.218 c
+35.073 3.443 35.351 3.557 35.705 3.557 c
+35.84 3.557 35.973 3.534 36.101 3.498 c
+36.072 2.954 l
+35.973 2.973 35.873 2.983 35.778 2.983 c
+35.403 2.983 35.219 2.719 35.219 2.19 c
+35.219 1.822 l
+35.896 1.822 l
+35.896 1.294 l
+35.219 1.294 l
+35.219 -2.161 l
+h
+38.306 1.205 m
+38.218 1.223 38.119 1.234 38.012 1.234 c
+37.678 1.234 37.442 1.051 37.307 0.69 c
+37.307 -2.161 l
+36.66 -2.161 l
+36.66 1.822 l
+37.292 1.822 l
+37.307 1.411 l
+37.483 1.734 37.726 1.896 38.042 1.896 c
+38.148 1.896 38.236 1.874 38.306 1.837 c
+h
+38.747 0.014 m
+38.747 0.592 38.883 1.047 39.158 1.381 c
+39.441 1.723 39.813 1.896 40.276 1.896 c
+40.735 1.896 41.103 1.727 41.378 1.396 c
+41.662 1.072 41.808 0.625 41.82 0.058 c
+41.82 -0.368 l
+41.82 -0.937 41.676 -1.393 41.393 -1.735 c
+41.117 -2.07 40.75 -2.234 40.29 -2.234 c
+39.827 -2.234 39.456 -2.073 39.173 -1.75 c
+38.898 -1.419 38.755 -0.977 38.747 -0.427 c
+h
+39.393 -0.368 m
+39.393 -0.772 39.471 -1.088 39.629 -1.324 c
+39.794 -1.559 40.015 -1.676 40.29 -1.676 c
+40.856 -1.676 41.151 -1.264 41.172 -0.441 c
+41.172 0.014 l
+41.172 0.415 41.088 0.735 40.922 0.97 c
+40.764 1.213 40.548 1.337 40.276 1.337 c
+40.012 1.337 39.794 1.213 39.629 0.97 c
+39.471 0.735 39.393 0.415 39.393 0.014 c
+h
+43.274 1.822 m
+43.289 1.454 l
+43.532 1.749 43.852 1.896 44.245 1.896 c
+44.685 1.896 44.994 1.697 45.171 1.308 c
+45.424 1.697 45.774 1.896 46.214 1.896 c
+46.949 1.896 47.324 1.433 47.346 0.514 c
+47.346 -2.161 l
+46.699 -2.161 l
+46.699 0.455 l
+46.699 0.749 46.644 0.962 46.537 1.102 c
+46.439 1.238 46.266 1.308 46.023 1.308 c
+45.824 1.308 45.663 1.227 45.538 1.072 c
+45.421 0.926 45.35 0.735 45.332 0.5 c
+45.332 -2.161 l
+44.671 -2.161 l
+44.671 0.484 l
+44.671 1.032 44.45 1.308 44.01 1.308 c
+43.675 1.308 43.44 1.146 43.304 0.823 c
+43.304 -2.161 l
+42.657 -2.161 l
+42.657 1.822 l
+h
+f
+Q
+q 1 0 0 1 371.7877 176.751 cm
+0 0 m
+0.353 2.234 l
+1.352 2.234 l
+0.529 -1.749 l
+-0.339 -1.749 l
+-0.897 0.559 l
+-1.455 -1.749 l
+-2.323 -1.749 l
+-3.146 2.234 l
+-2.147 2.234 l
+-1.794 0 l
+-1.264 2.234 l
+-0.53 2.234 l
+h
+1.749 0.368 m
+1.749 0.974 1.888 1.448 2.175 1.793 c
+2.458 2.135 2.851 2.308 3.351 2.308 c
+3.858 2.308 4.255 2.135 4.542 1.793 c
+4.825 1.448 4.968 0.974 4.968 0.368 c
+4.968 0.103 l
+4.968 -0.496 4.825 -0.966 4.542 -1.309 c
+4.255 -1.654 3.858 -1.822 3.351 -1.822 c
+2.84 -1.822 2.443 -1.654 2.161 -1.309 c
+1.885 -0.966 1.749 -0.492 1.749 0.118 c
+h
+2.792 0.103 m
+2.792 -0.603 2.977 -0.956 3.351 -0.956 c
+3.704 -0.956 3.895 -0.661 3.924 -0.073 c
+3.924 0.368 l
+3.924 0.728 3.873 1 3.777 1.176 c
+3.678 1.353 3.534 1.44 3.351 1.44 c
+3.175 1.44 3.035 1.353 2.94 1.176 c
+2.84 1 2.792 0.728 2.792 0.368 c
+h
+7.57 1.22 m
+7.231 1.249 l
+6.945 1.249 6.754 1.124 6.659 0.882 c
+6.659 -1.749 l
+5.614 -1.749 l
+5.614 2.234 l
+6.585 2.234 l
+6.614 1.793 l
+6.78 2.135 7.011 2.308 7.305 2.308 c
+7.422 2.308 7.515 2.286 7.584 2.249 c
+h
+9.437 -0.22 m
+9.157 -0.529 l
+9.157 -1.749 l
+8.114 -1.749 l
+8.114 3.896 l
+9.157 3.896 l
+9.157 0.852 l
+9.275 1.043 l
+9.995 2.234 l
+11.245 2.234 l
+10.083 0.588 l
+11.347 -1.749 l
+10.157 -1.749 l
+h
+12.803 -1.749 -1.043 3.983 re
+11.715 3.263 m
+11.715 3.418 11.763 3.547 11.862 3.645 c
+11.968 3.752 12.104 3.807 12.274 3.807 c
+12.45 3.807 12.586 3.752 12.685 3.645 c
+12.791 3.547 12.847 3.418 12.847 3.263 c
+12.847 3.094 12.791 2.959 12.685 2.851 c
+12.586 2.753 12.45 2.705 12.274 2.705 c
+12.104 2.705 11.968 2.753 11.862 2.851 c
+11.763 2.959 11.715 3.094 11.715 3.263 c
+14.566 2.234 m
+14.596 1.837 l
+14.831 2.15 15.133 2.308 15.507 2.308 c
+16.191 2.308 16.544 1.827 16.565 0.867 c
+16.565 -1.749 l
+15.522 -1.749 l
+15.522 0.794 l
+15.522 1.018 15.486 1.18 15.419 1.278 c
+15.349 1.374 15.232 1.426 15.066 1.426 c
+14.879 1.426 14.732 1.33 14.626 1.147 c
+14.626 -1.749 l
+13.581 -1.749 l
+13.581 2.234 l
+h
+17.183 0.368 m
+17.183 1.014 17.3 1.5 17.535 1.823 c
+17.771 2.146 18.102 2.308 18.535 2.308 c
+18.888 2.308 19.16 2.165 19.359 1.881 c
+19.403 2.234 l
+20.344 2.234 l
+20.344 -1.749 l
+20.344 -2.256 20.2 -2.645 19.916 -2.911 c
+19.63 -3.183 19.226 -3.322 18.697 -3.322 c
+18.469 -3.322 18.233 -3.278 17.992 -3.19 c
+17.756 -3.102 17.58 -2.988 17.462 -2.851 c
+17.815 -2.132 l
+17.911 -2.238 18.039 -2.323 18.197 -2.381 c
+18.351 -2.447 18.499 -2.484 18.638 -2.484 c
+18.873 -2.484 19.039 -2.425 19.137 -2.308 c
+19.245 -2.198 19.299 -2.021 19.299 -1.779 c
+19.299 -1.426 l
+19.101 -1.691 18.844 -1.822 18.52 -1.822 c
+18.098 -1.822 17.771 -1.661 17.535 -1.338 c
+17.308 -1.007 17.19 -0.536 17.183 0.073 c
+h
+18.227 0.103 m
+18.227 -0.272 18.274 -0.54 18.374 -0.706 c
+18.469 -0.875 18.624 -0.956 18.829 -0.956 c
+19.042 -0.956 19.201 -0.878 19.299 -0.721 c
+19.299 1.176 l
+19.189 1.341 19.035 1.426 18.829 1.426 c
+18.624 1.426 18.469 1.341 18.374 1.176 c
+18.274 1.007 18.227 0.738 18.227 0.368 c
+h
+22.695 0.368 m
+22.695 1.014 22.802 1.5 23.018 1.823 c
+23.243 2.146 23.566 2.308 23.989 2.308 c
+24.301 2.308 24.554 2.176 24.753 1.911 c
+24.753 3.896 l
+25.811 3.896 l
+25.811 -1.749 l
+24.855 -1.749 l
+24.812 -1.338 l
+24.595 -1.661 24.319 -1.822 23.989 -1.822 c
+23.577 -1.822 23.257 -1.668 23.033 -1.352 c
+22.817 -1.029 22.702 -0.559 22.695 0.059 c
+h
+23.739 0.103 m
+23.739 -0.29 23.775 -0.565 23.856 -0.721 c
+23.945 -0.878 24.091 -0.956 24.297 -0.956 c
+24.503 -0.956 24.654 -0.864 24.753 -0.676 c
+24.753 1.132 l
+24.654 1.326 24.503 1.426 24.297 1.426 c
+24.099 1.426 23.959 1.345 23.871 1.191 c
+23.783 1.043 23.739 0.771 23.739 0.382 c
+h
+27.663 -1.749 -1.043 3.983 re
+26.576 3.263 m
+26.576 3.418 26.623 3.547 26.723 3.645 c
+26.829 3.752 26.965 3.807 27.134 3.807 c
+27.311 3.807 27.446 3.752 27.546 3.645 c
+27.652 3.547 27.708 3.418 27.708 3.263 c
+27.708 3.094 27.652 2.959 27.546 2.851 c
+27.446 2.753 27.311 2.705 27.134 2.705 c
+26.965 2.705 26.829 2.753 26.723 2.851 c
+26.623 2.959 26.576 3.094 26.576 3.263 c
+30.426 1.22 m
+30.089 1.249 l
+29.802 1.249 29.611 1.124 29.516 0.882 c
+29.516 -1.749 l
+28.471 -1.749 l
+28.471 2.234 l
+29.442 2.234 l
+29.471 1.793 l
+29.637 2.135 29.868 2.308 30.162 2.308 c
+30.28 2.308 30.372 2.286 30.441 2.249 c
+h
+32.5 -1.822 m
+31.97 -1.822 31.551 -1.668 31.25 -1.352 c
+30.956 -1.029 30.809 -0.569 30.809 0.029 c
+30.809 0.338 l
+30.809 0.963 30.945 1.448 31.22 1.793 c
+31.492 2.135 31.885 2.308 32.396 2.308 c
+32.896 2.308 33.267 2.146 33.514 1.823 c
+33.767 1.5 33.9 1.022 33.911 0.397 c
+33.911 -0.103 l
+31.837 -0.103 l
+31.856 -0.397 31.918 -0.613 32.029 -0.75 c
+32.146 -0.889 32.327 -0.956 32.573 -0.956 c
+32.915 -0.956 33.205 -0.838 33.44 -0.603 c
+33.852 -1.234 l
+33.723 -1.411 33.535 -1.554 33.293 -1.661 c
+33.047 -1.768 32.782 -1.822 32.5 -1.822 c
+31.853 0.617 m
+32.882 0.617 l
+32.882 0.721 l
+32.882 0.956 32.841 1.132 32.764 1.249 c
+32.694 1.374 32.566 1.44 32.381 1.44 c
+32.205 1.44 32.073 1.371 31.985 1.235 c
+31.904 1.106 31.86 0.9 31.853 0.617 c
+35.939 -0.956 m
+36.233 -0.956 36.383 -0.76 36.394 -0.368 c
+37.364 -0.368 l
+37.364 -0.801 37.233 -1.153 36.968 -1.426 c
+36.703 -1.691 36.365 -1.822 35.953 -1.822 c
+35.442 -1.822 35.049 -1.668 34.777 -1.352 c
+34.513 -1.029 34.374 -0.559 34.366 0.059 c
+34.366 0.382 l
+34.366 1.007 34.498 1.484 34.763 1.808 c
+35.035 2.139 35.432 2.308 35.953 2.308 c
+36.383 2.308 36.726 2.168 36.982 1.897 c
+37.236 1.621 37.364 1.239 37.364 0.75 c
+36.394 0.75 l
+36.394 0.963 36.354 1.132 36.277 1.249 c
+36.207 1.374 36.09 1.44 35.924 1.44 c
+35.748 1.44 35.619 1.374 35.542 1.249 c
+35.461 1.121 35.417 0.871 35.409 0.5 c
+35.409 0.088 l
+35.409 -0.235 35.424 -0.463 35.454 -0.588 c
+35.49 -0.717 35.546 -0.808 35.615 -0.867 c
+35.693 -0.926 35.799 -0.956 35.939 -0.956 c
+39.172 3.204 m
+39.172 2.234 l
+39.702 2.234 l
+39.702 1.44 l
+39.172 1.44 l
+39.172 -0.529 l
+39.172 -0.687 39.191 -0.794 39.232 -0.852 c
+39.28 -0.912 39.363 -0.941 39.481 -0.941 c
+39.588 -0.941 39.672 -0.933 39.731 -0.912 c
+39.731 -1.72 l
+39.555 -1.786 39.363 -1.822 39.158 -1.822 c
+38.482 -1.822 38.137 -1.437 38.129 -0.661 c
+38.129 1.44 l
+37.674 1.44 l
+37.674 2.234 l
+38.129 2.234 l
+38.129 3.204 l
+h
+40.055 0.368 m
+40.055 0.974 40.194 1.448 40.481 1.793 c
+40.764 2.135 41.157 2.308 41.657 2.308 c
+42.164 2.308 42.561 2.135 42.848 1.793 c
+43.13 1.448 43.274 0.974 43.274 0.368 c
+43.274 0.103 l
+43.274 -0.496 43.13 -0.966 42.848 -1.309 c
+42.561 -1.654 42.164 -1.822 41.657 -1.822 c
+41.146 -1.822 40.749 -1.654 40.466 -1.309 c
+40.19 -0.966 40.055 -0.492 40.055 0.118 c
+h
+41.098 0.103 m
+41.098 -0.603 41.282 -0.956 41.657 -0.956 c
+42.01 -0.956 42.201 -0.661 42.23 -0.073 c
+42.23 0.368 l
+42.23 0.728 42.179 1 42.083 1.176 c
+41.984 1.353 41.84 1.44 41.657 1.44 c
+41.48 1.44 41.341 1.353 41.245 1.176 c
+41.146 1 41.098 0.728 41.098 0.368 c
+h
+45.875 1.22 m
+45.537 1.249 l
+45.251 1.249 45.06 1.124 44.965 0.882 c
+44.965 -1.749 l
+43.92 -1.749 l
+43.92 2.234 l
+44.89 2.234 l
+44.92 1.793 l
+45.085 2.135 45.316 2.308 45.611 2.308 c
+45.728 2.308 45.821 2.286 45.89 2.249 c
+h
+47.772 -0.015 m
+48.3 2.234 l
+49.403 2.234 l
+48.095 -2.352 l
+47.897 -3.017 47.529 -3.351 46.993 -3.351 c
+46.864 -3.351 46.721 -3.329 46.567 -3.293 c
+46.567 -2.469 l
+46.684 -2.484 l
+46.849 -2.484 46.97 -2.447 47.051 -2.381 c
+47.128 -2.323 47.191 -2.212 47.242 -2.057 c
+47.316 -1.793 l
+46.17 2.234 l
+47.286 2.234 l
+h
+f
+Q
+q 1 0 0 1 425.5124 175.0022 cm
+0 0 m
+-0.04 0.087 -0.066 0.235 -0.073 0.44 c
+-0.309 0.095 -0.603 -0.073 -0.956 -0.073 c
+-1.319 -0.073 -1.602 0.022 -1.808 0.22 c
+-2.007 0.426 -2.102 0.713 -2.102 1.087 c
+-2.102 1.488 -1.966 1.808 -1.691 2.043 c
+-1.419 2.285 -1.043 2.41 -0.573 2.41 c
+-0.088 2.41 l
+-0.088 2.836 l
+-0.088 3.072 -0.143 3.237 -0.25 3.337 c
+-0.36 3.443 -0.522 3.498 -0.735 3.498 c
+-0.933 3.498 -1.095 3.439 -1.22 3.322 c
+-1.338 3.204 -1.396 3.057 -1.396 2.881 c
+-2.043 2.881 l
+-2.043 3.075 -1.984 3.266 -1.866 3.454 c
+-1.742 3.638 -1.58 3.785 -1.382 3.895 c
+-1.176 4.002 -0.948 4.056 -0.691 4.056 c
+-0.291 4.056 0.015 3.954 0.22 3.748 c
+0.434 3.542 0.548 3.248 0.559 2.866 c
+0.559 0.852 l
+0.559 0.548 0.595 0.283 0.676 0.058 c
+0.676 0 l
+h
+-0.867 0.515 m
+-0.702 0.515 -0.551 0.558 -0.412 0.646 c
+-0.264 0.735 -0.158 0.845 -0.088 0.985 c
+-0.088 1.926 l
+-0.455 1.926 l
+-0.771 1.926 -1.014 1.855 -1.191 1.72 c
+-1.367 1.591 -1.455 1.404 -1.455 1.161 c
+-1.455 0.933 -1.411 0.768 -1.323 0.661 c
+-1.234 0.562 -1.084 0.515 -0.867 0.515 c
+2.175 3.983 m
+2.19 3.542 l
+2.444 3.884 2.767 4.056 3.16 4.056 c
+3.866 4.056 4.222 3.586 4.233 2.645 c
+4.233 0 l
+3.586 0 l
+3.586 2.616 l
+3.586 2.929 3.532 3.149 3.425 3.278 c
+3.314 3.403 3.16 3.468 2.955 3.468 c
+2.797 3.468 2.649 3.414 2.514 3.307 c
+2.385 3.197 2.282 3.061 2.205 2.896 c
+2.205 0 l
+1.558 0 l
+1.558 3.983 l
+h
+5.072 2.175 m
+5.072 2.782 5.182 3.248 5.409 3.572 c
+5.644 3.895 5.972 4.056 6.394 4.056 c
+6.776 4.056 7.074 3.898 7.291 3.586 c
+7.291 5.644 l
+7.938 5.644 l
+7.938 0 l
+7.349 0 l
+7.306 0.426 l
+7.1 0.091 6.795 -0.073 6.394 -0.073 c
+5.982 -0.073 5.659 0.081 5.424 0.397 c
+5.189 0.72 5.072 1.176 5.072 1.764 c
+h
+5.718 1.793 m
+5.718 1.352 5.781 1.022 5.909 0.808 c
+6.045 0.602 6.265 0.5 6.57 0.5 c
+6.894 0.5 7.133 0.661 7.291 0.985 c
+7.291 2.998 l
+7.121 3.31 6.882 3.468 6.57 3.468 c
+6.265 3.468 6.045 3.366 5.909 3.16 c
+5.781 2.954 5.718 2.631 5.718 2.19 c
+h
+12.7 0 m
+12.66 0.087 12.634 0.235 12.627 0.44 c
+12.391 0.095 12.097 -0.073 11.744 -0.073 c
+11.381 -0.073 11.098 0.022 10.892 0.22 c
+10.693 0.426 10.598 0.713 10.598 1.087 c
+10.598 1.488 10.734 1.808 11.009 2.043 c
+11.281 2.285 11.657 2.41 12.127 2.41 c
+12.612 2.41 l
+12.612 2.836 l
+12.612 3.072 12.557 3.237 12.45 3.337 c
+12.34 3.443 12.178 3.498 11.965 3.498 c
+11.767 3.498 11.605 3.439 11.48 3.322 c
+11.362 3.204 11.304 3.057 11.304 2.881 c
+10.657 2.881 l
+10.657 3.075 10.716 3.266 10.834 3.454 c
+10.958 3.638 11.12 3.785 11.318 3.895 c
+11.524 4.002 11.752 4.056 12.009 4.056 c
+12.409 4.056 12.715 3.954 12.92 3.748 c
+13.134 3.542 13.247 3.248 13.259 2.866 c
+13.259 0.852 l
+13.259 0.548 13.295 0.283 13.376 0.058 c
+13.376 0 l
+h
+11.833 0.515 m
+11.998 0.515 12.149 0.558 12.288 0.646 c
+12.436 0.735 12.542 0.845 12.612 0.985 c
+12.612 1.926 l
+12.245 1.926 l
+11.929 1.926 11.686 1.855 11.509 1.72 c
+11.333 1.591 11.245 1.404 11.245 1.161 c
+11.245 0.933 11.289 0.768 11.377 0.661 c
+11.466 0.562 11.616 0.515 11.833 0.515 c
+14.126 2.175 m
+14.126 2.782 14.236 3.248 14.464 3.572 c
+14.699 3.895 15.026 4.056 15.449 4.056 c
+15.831 4.056 16.129 3.898 16.345 3.586 c
+16.345 5.644 l
+16.992 5.644 l
+16.992 0 l
+16.405 0 l
+16.36 0.426 l
+16.154 0.091 15.85 -0.073 15.449 -0.073 c
+15.037 -0.073 14.714 0.081 14.479 0.397 c
+14.244 0.72 14.126 1.176 14.126 1.764 c
+h
+14.772 1.793 m
+14.772 1.352 14.835 1.022 14.963 0.808 c
+15.1 0.602 15.32 0.5 15.625 0.5 c
+15.948 0.5 16.187 0.661 16.345 0.985 c
+16.345 2.998 l
+16.176 3.31 15.937 3.468 15.625 3.468 c
+15.32 3.468 15.1 3.366 14.963 3.16 c
+14.835 2.954 14.772 2.631 14.772 2.19 c
+h
+17.859 2.175 m
+17.859 2.782 17.97 3.248 18.198 3.572 c
+18.433 3.895 18.759 4.056 19.183 4.056 c
+19.565 4.056 19.862 3.898 20.079 3.586 c
+20.079 5.644 l
+20.725 5.644 l
+20.725 0 l
+20.137 0 l
+20.093 0.426 l
+19.888 0.091 19.582 -0.073 19.183 -0.073 c
+18.771 -0.073 18.447 0.081 18.212 0.397 c
+17.977 0.72 17.859 1.176 17.859 1.764 c
+h
+18.506 1.793 m
+18.506 1.352 18.568 1.022 18.697 0.808 c
+18.834 0.602 19.054 0.5 19.359 0.5 c
+19.682 0.5 19.921 0.661 20.079 0.985 c
+20.079 2.998 l
+19.91 3.31 19.671 3.468 19.359 3.468 c
+19.054 3.468 18.834 3.366 18.697 3.16 c
+18.568 2.954 18.506 2.631 18.506 2.19 c
+h
+23.312 2.175 m
+23.312 2.782 23.423 3.248 23.651 3.572 c
+23.886 3.895 24.213 4.056 24.636 4.056 c
+25.018 4.056 25.315 3.898 25.533 3.586 c
+25.533 5.644 l
+26.179 5.644 l
+26.179 0 l
+25.591 0 l
+25.547 0.426 l
+25.342 0.091 25.036 -0.073 24.636 -0.073 c
+24.224 -0.073 23.9 0.081 23.665 0.397 c
+23.43 0.72 23.312 1.176 23.312 1.764 c
+h
+23.96 1.793 m
+23.96 1.352 24.022 1.022 24.151 0.808 c
+24.286 0.602 24.507 0.5 24.812 0.5 c
+25.136 0.5 25.375 0.661 25.533 0.985 c
+25.533 2.998 l
+25.363 3.31 25.124 3.468 24.812 3.468 c
+24.507 3.468 24.286 3.366 24.151 3.16 c
+24.022 2.954 23.96 2.631 23.96 2.19 c
+h
+28.604 -0.073 m
+28.105 -0.073 27.723 0.073 27.458 0.367 c
+27.193 0.661 27.061 1.095 27.061 1.675 c
+27.061 2.146 l
+27.061 2.741 27.185 3.208 27.443 3.542 c
+27.708 3.884 28.068 4.056 28.531 4.056 c
+28.99 4.056 29.332 3.902 29.56 3.601 c
+29.795 3.307 29.916 2.844 29.927 2.219 c
+29.927 1.793 l
+27.708 1.793 l
+27.708 1.705 l
+27.708 1.271 27.785 0.959 27.943 0.764 c
+28.108 0.577 28.34 0.484 28.634 0.484 c
+28.829 0.484 29.001 0.518 29.148 0.588 c
+29.296 0.665 29.431 0.783 29.56 0.941 c
+29.898 0.529 l
+29.612 0.125 29.182 -0.073 28.604 -0.073 c
+28.531 3.498 m
+28.255 3.498 28.053 3.403 27.929 3.219 c
+27.8 3.031 27.726 2.741 27.708 2.352 c
+29.281 2.352 l
+29.281 2.439 l
+29.259 2.822 29.192 3.09 29.074 3.248 c
+28.957 3.414 28.773 3.498 28.531 3.498 c
+31.412 0 -0.647 5.644 re
+33.852 -0.073 m
+33.352 -0.073 32.97 0.073 32.706 0.367 c
+32.44 0.661 32.309 1.095 32.309 1.675 c
+32.309 2.146 l
+32.309 2.741 32.434 3.208 32.691 3.542 c
+32.955 3.884 33.315 4.056 33.778 4.056 c
+34.238 4.056 34.58 3.902 34.807 3.601 c
+35.043 3.307 35.164 2.844 35.175 2.219 c
+35.175 1.793 l
+32.955 1.793 l
+32.955 1.705 l
+32.955 1.271 33.032 0.959 33.19 0.764 c
+33.356 0.577 33.587 0.484 33.882 0.484 c
+34.076 0.484 34.249 0.518 34.395 0.588 c
+34.543 0.665 34.678 0.783 34.807 0.941 c
+35.145 0.529 l
+34.859 0.125 34.429 -0.073 33.852 -0.073 c
+33.778 3.498 m
+33.503 3.498 33.3 3.403 33.176 3.219 c
+33.047 3.031 32.974 2.741 32.955 2.352 c
+34.528 2.352 l
+34.528 2.439 l
+34.506 2.822 34.44 3.09 34.322 3.248 c
+34.204 3.414 34.021 3.498 33.778 3.498 c
+36.806 4.939 m
+36.806 3.983 l
+37.409 3.983 l
+37.409 3.454 l
+36.806 3.454 l
+36.806 0.985 l
+36.806 0.827 36.828 0.709 36.88 0.632 c
+36.939 0.551 37.027 0.515 37.144 0.515 c
+37.233 0.515 37.321 0.529 37.409 0.558 c
+37.409 0 l
+37.262 -0.048 37.107 -0.073 36.953 -0.073 c
+36.696 -0.073 36.502 0.018 36.365 0.205 c
+36.226 0.389 36.16 0.65 36.16 0.985 c
+36.16 3.454 l
+35.557 3.454 l
+35.557 3.983 l
+36.16 3.983 l
+36.16 4.939 l
+h
+38.879 0 -0.646 3.983 re
+38.923 5.026 m
+38.923 4.916 38.894 4.825 38.835 4.748 c
+38.776 4.677 38.68 4.644 38.555 4.644 c
+38.438 4.644 38.343 4.677 38.277 4.748 c
+38.218 4.825 38.188 4.916 38.188 5.026 c
+38.188 5.144 38.218 5.236 38.277 5.306 c
+38.343 5.383 38.438 5.423 38.555 5.423 c
+38.68 5.423 38.776 5.383 38.835 5.306 c
+38.894 5.225 38.923 5.134 38.923 5.026 c
+39.761 2.175 m
+39.761 2.753 39.897 3.208 40.172 3.542 c
+40.456 3.884 40.826 4.056 41.29 4.056 c
+41.749 4.056 42.117 3.888 42.392 3.557 c
+42.675 3.233 42.822 2.786 42.833 2.219 c
+42.833 1.793 l
+42.833 1.224 42.69 0.768 42.407 0.426 c
+42.131 0.091 41.764 -0.073 41.304 -0.073 c
+40.841 -0.073 40.471 0.087 40.187 0.411 c
+39.912 0.742 39.768 1.183 39.761 1.734 c
+h
+40.408 1.793 m
+40.408 1.389 40.485 1.072 40.643 0.837 c
+40.808 0.602 41.028 0.484 41.304 0.484 c
+41.87 0.484 42.164 0.897 42.186 1.72 c
+42.186 2.175 l
+42.186 2.576 42.102 2.896 41.936 3.131 c
+41.778 3.373 41.562 3.498 41.29 3.498 c
+41.025 3.498 40.808 3.373 40.643 3.131 c
+40.485 2.896 40.408 2.576 40.408 2.175 c
+h
+44.288 3.983 m
+44.303 3.542 l
+44.556 3.884 44.88 4.056 45.273 4.056 c
+45.979 4.056 46.335 3.586 46.346 2.645 c
+46.346 0 l
+45.699 0 l
+45.699 2.616 l
+45.699 2.929 45.645 3.149 45.537 3.278 c
+45.427 3.403 45.273 3.468 45.067 3.468 c
+44.909 3.468 44.762 3.414 44.627 3.307 c
+44.498 3.197 44.394 3.061 44.317 2.896 c
+44.317 0 l
+43.671 0 l
+43.671 3.983 l
+h
+49.888 4.939 m
+49.888 3.983 l
+50.491 3.983 l
+50.491 3.454 l
+49.888 3.454 l
+49.888 0.985 l
+49.888 0.827 49.911 0.709 49.962 0.632 c
+50.021 0.551 50.109 0.515 50.227 0.515 c
+50.315 0.515 50.403 0.529 50.491 0.558 c
+50.491 0 l
+50.345 -0.048 50.19 -0.073 50.036 -0.073 c
+49.778 -0.073 49.584 0.018 49.447 0.205 c
+49.308 0.389 49.242 0.65 49.242 0.985 c
+49.242 3.454 l
+48.639 3.454 l
+48.639 3.983 l
+49.242 3.983 l
+49.242 4.939 l
+h
+51.05 2.175 m
+51.05 2.753 51.186 3.208 51.461 3.542 c
+51.744 3.884 52.115 4.056 52.579 4.056 c
+53.038 4.056 53.406 3.888 53.68 3.557 c
+53.964 3.233 54.111 2.786 54.122 2.219 c
+54.122 1.793 l
+54.122 1.224 53.979 0.768 53.696 0.426 c
+53.42 0.091 53.053 -0.073 52.593 -0.073 c
+52.13 -0.073 51.759 0.087 51.476 0.411 c
+51.201 0.742 51.057 1.183 51.05 1.734 c
+h
+51.696 1.793 m
+51.696 1.389 51.774 1.072 51.932 0.837 c
+52.097 0.602 52.317 0.484 52.593 0.484 c
+53.159 0.484 53.453 0.897 53.475 1.72 c
+53.475 2.175 l
+53.475 2.576 53.391 2.896 53.225 3.131 c
+53.067 3.373 52.851 3.498 52.579 3.498 c
+52.314 3.498 52.097 3.373 51.932 3.131 c
+51.774 2.896 51.696 2.576 51.696 2.175 c
+h
+f
+Q
+q 1 0 0 1 483.8532 176.0746 cm
+0 0 m
+0 0.088 -0.044 0.166 -0.133 0.235 c
+-0.221 0.312 -0.408 0.416 -0.691 0.544 c
+-1.125 0.721 -1.422 0.9 -1.588 1.088 c
+-1.746 1.272 -1.823 1.503 -1.823 1.779 c
+-1.823 2.12 -1.702 2.404 -1.455 2.631 c
+-1.202 2.866 -0.864 2.984 -0.441 2.984 c
+-0.011 2.984 0.338 2.87 0.602 2.646 c
+0.867 2.419 0.999 2.117 0.999 1.735 c
+-0.044 1.735 l
+-0.044 2.058 -0.184 2.22 -0.456 2.22 c
+-0.566 2.22 -0.655 2.183 -0.721 2.117 c
+-0.79 2.047 -0.823 1.948 -0.823 1.823 c
+-0.823 1.735 -0.786 1.654 -0.706 1.588 c
+-0.628 1.529 -0.449 1.434 -0.162 1.309 c
+0.268 1.151 0.565 0.975 0.735 0.779 c
+0.911 0.592 0.999 0.342 0.999 0.03 c
+0.999 -0.323 0.867 -0.61 0.602 -0.823 c
+0.338 -1.04 -0.011 -1.146 -0.441 -1.146 c
+-0.736 -1.146 -0.996 -1.091 -1.22 -0.985 c
+-1.448 -0.867 -1.625 -0.706 -1.75 -0.5 c
+-1.867 -0.294 -1.926 -0.073 -1.926 0.162 c
+-0.941 0.162 l
+-0.941 -0.025 -0.904 -0.162 -0.823 -0.249 c
+-0.736 -0.338 -0.603 -0.382 -0.427 -0.382 c
+-0.144 -0.382 0 -0.257 0 0 c
+2.866 3.881 m
+2.866 2.911 l
+3.395 2.911 l
+3.395 2.117 l
+2.866 2.117 l
+2.866 0.148 l
+2.866 -0.01 2.884 -0.118 2.925 -0.176 c
+2.973 -0.235 3.057 -0.264 3.175 -0.264 c
+3.281 -0.264 3.366 -0.257 3.424 -0.235 c
+3.424 -1.043 l
+3.248 -1.109 3.057 -1.146 2.851 -1.146 c
+2.175 -1.146 1.83 -0.76 1.822 0.015 c
+1.822 2.117 l
+1.367 2.117 l
+1.367 2.911 l
+1.822 2.911 l
+1.822 3.881 l
+h
+5.85 -1.072 m
+5.82 -1.014 5.791 -0.911 5.762 -0.764 c
+5.574 -1.022 5.325 -1.146 5.012 -1.146 c
+4.677 -1.146 4.398 -1.04 4.174 -0.823 c
+3.958 -0.598 3.85 -0.309 3.85 0.044 c
+3.85 0.456 3.983 0.772 4.247 1 c
+4.512 1.235 4.895 1.353 5.394 1.353 c
+5.718 1.353 l
+5.718 1.676 l
+5.718 1.852 5.681 1.974 5.614 2.043 c
+5.556 2.12 5.468 2.161 5.35 2.161 c
+5.093 2.161 4.968 2.007 4.968 1.706 c
+3.925 1.706 l
+3.925 2.076 4.06 2.382 4.336 2.617 c
+4.608 2.859 4.957 2.984 5.379 2.984 c
+5.82 2.984 6.158 2.866 6.393 2.631 c
+6.636 2.404 6.761 2.08 6.761 1.661 c
+6.761 -0.205 l
+6.761 -0.551 6.809 -0.819 6.908 -1.014 c
+6.908 -1.072 l
+h
+5.247 -0.323 m
+5.354 -0.323 5.446 -0.305 5.527 -0.264 c
+5.614 -0.216 5.677 -0.158 5.718 -0.087 c
+5.718 0.736 l
+5.468 0.736 l
+5.292 0.736 5.148 0.676 5.041 0.559 c
+4.943 0.449 4.895 0.302 4.895 0.118 c
+4.895 -0.176 5.012 -0.323 5.247 -0.323 c
+7.378 1.044 m
+7.378 1.691 7.496 2.176 7.731 2.5 c
+7.966 2.822 8.297 2.984 8.731 2.984 c
+9.084 2.984 9.356 2.841 9.554 2.558 c
+9.598 2.911 l
+10.539 2.911 l
+10.539 -1.072 l
+10.539 -1.579 10.395 -1.969 10.113 -2.234 c
+9.826 -2.506 9.422 -2.645 8.893 -2.645 c
+8.665 -2.645 8.43 -2.601 8.187 -2.514 c
+7.952 -2.425 7.775 -2.311 7.658 -2.175 c
+8.01 -1.455 l
+8.106 -1.562 8.235 -1.646 8.393 -1.705 c
+8.548 -1.77 8.694 -1.808 8.834 -1.808 c
+9.069 -1.808 9.234 -1.749 9.334 -1.631 c
+9.44 -1.521 9.495 -1.344 9.495 -1.103 c
+9.495 -0.75 l
+9.296 -1.014 9.04 -1.146 8.716 -1.146 c
+8.294 -1.146 7.966 -0.985 7.731 -0.661 c
+7.503 -0.33 7.386 0.14 7.378 0.75 c
+h
+8.422 0.779 m
+8.422 0.405 8.47 0.136 8.569 -0.029 c
+8.665 -0.198 8.819 -0.279 9.025 -0.279 c
+9.238 -0.279 9.396 -0.201 9.495 -0.044 c
+9.495 1.852 l
+9.385 2.018 9.231 2.103 9.025 2.103 c
+8.819 2.103 8.665 2.018 8.569 1.852 c
+8.47 1.683 8.422 1.415 8.422 1.044 c
+h
+12.391 -1.072 -1.044 3.983 re
+11.303 3.94 m
+11.303 4.094 11.351 4.223 11.451 4.322 c
+11.557 4.428 11.692 4.484 11.862 4.484 c
+12.038 4.484 12.174 4.428 12.273 4.322 c
+12.38 4.223 12.435 4.094 12.435 3.94 c
+12.435 3.77 12.38 3.635 12.273 3.528 c
+12.174 3.429 12.038 3.381 11.862 3.381 c
+11.692 3.381 11.557 3.429 11.451 3.528 c
+11.351 3.635 11.303 3.77 11.303 3.94 c
+14.154 2.911 m
+14.184 2.514 l
+14.42 2.826 14.721 2.984 15.095 2.984 c
+15.779 2.984 16.132 2.503 16.154 1.544 c
+16.154 -1.072 l
+15.11 -1.072 l
+15.11 1.47 l
+15.11 1.694 15.073 1.856 15.008 1.955 c
+14.938 2.051 14.82 2.103 14.655 2.103 c
+14.468 2.103 14.32 2.007 14.214 1.823 c
+14.214 -1.072 l
+13.17 -1.072 l
+13.17 2.911 l
+h
+16.772 1.044 m
+16.772 1.691 16.889 2.176 17.124 2.5 c
+17.359 2.822 17.69 2.984 18.123 2.984 c
+18.476 2.984 18.748 2.841 18.947 2.558 c
+18.991 2.911 l
+19.931 2.911 l
+19.931 -1.072 l
+19.931 -1.579 19.788 -1.969 19.505 -2.234 c
+19.218 -2.506 18.815 -2.645 18.285 -2.645 c
+18.057 -2.645 17.822 -2.601 17.58 -2.514 c
+17.344 -2.425 17.168 -2.311 17.051 -2.175 c
+17.404 -1.455 l
+17.499 -1.562 17.628 -1.646 17.786 -1.705 c
+17.94 -1.77 18.087 -1.808 18.227 -1.808 c
+18.462 -1.808 18.627 -1.749 18.726 -1.631 c
+18.833 -1.521 18.888 -1.344 18.888 -1.103 c
+18.888 -0.75 l
+18.69 -1.014 18.432 -1.146 18.109 -1.146 c
+17.686 -1.146 17.359 -0.985 17.124 -0.661 c
+16.897 -0.33 16.779 0.14 16.772 0.75 c
+h
+17.815 0.779 m
+17.815 0.405 17.863 0.136 17.962 -0.029 c
+18.057 -0.198 18.212 -0.279 18.418 -0.279 c
+18.63 -0.279 18.788 -0.201 18.888 -0.044 c
+18.888 1.852 l
+18.778 2.018 18.623 2.103 18.418 2.103 c
+18.212 2.103 18.057 2.018 17.962 1.852 c
+17.863 1.683 17.815 1.415 17.815 1.044 c
+h
+24.312 -1.072 m
+24.282 -1.014 24.253 -0.911 24.224 -0.764 c
+24.036 -1.022 23.787 -1.146 23.474 -1.146 c
+23.139 -1.146 22.86 -1.04 22.636 -0.823 c
+22.42 -0.598 22.312 -0.309 22.312 0.044 c
+22.312 0.456 22.445 0.772 22.709 1 c
+22.975 1.235 23.357 1.353 23.856 1.353 c
+24.18 1.353 l
+24.18 1.676 l
+24.18 1.852 24.143 1.974 24.076 2.043 c
+24.018 2.12 23.93 2.161 23.812 2.161 c
+23.555 2.161 23.43 2.007 23.43 1.706 c
+22.386 1.706 l
+22.386 2.076 22.522 2.382 22.798 2.617 c
+23.07 2.859 23.419 2.984 23.841 2.984 c
+24.282 2.984 24.621 2.866 24.856 2.631 c
+25.098 2.404 25.223 2.08 25.223 1.661 c
+25.223 -0.205 l
+25.223 -0.551 25.271 -0.819 25.37 -1.014 c
+25.37 -1.072 l
+h
+23.709 -0.323 m
+23.816 -0.323 23.908 -0.305 23.989 -0.264 c
+24.076 -0.216 24.139 -0.158 24.18 -0.087 c
+24.18 0.736 l
+23.93 0.736 l
+23.754 0.736 23.61 0.676 23.503 0.559 c
+23.405 0.449 23.357 0.302 23.357 0.118 c
+23.357 -0.176 23.474 -0.323 23.709 -0.323 c
+27.928 1.897 m
+27.59 1.926 l
+27.303 1.926 27.112 1.801 27.016 1.559 c
+27.016 -1.072 l
+25.973 -1.072 l
+25.973 2.911 l
+26.943 2.911 l
+26.973 2.469 l
+27.137 2.812 27.369 2.984 27.663 2.984 c
+27.781 2.984 27.873 2.962 27.943 2.926 c
+h
+30 -1.146 m
+29.471 -1.146 29.052 -0.992 28.751 -0.675 c
+28.457 -0.353 28.31 0.107 28.31 0.706 c
+28.31 1.014 l
+28.31 1.64 28.446 2.124 28.722 2.469 c
+28.994 2.812 29.387 2.984 29.898 2.984 c
+30.397 2.984 30.769 2.822 31.014 2.5 c
+31.268 2.176 31.401 1.698 31.411 1.073 c
+31.411 0.574 l
+29.339 0.574 l
+29.358 0.279 29.42 0.063 29.53 -0.073 c
+29.647 -0.213 29.828 -0.279 30.074 -0.279 c
+30.416 -0.279 30.706 -0.162 30.941 0.073 c
+31.353 -0.558 l
+31.224 -0.735 31.037 -0.878 30.794 -0.985 c
+30.548 -1.091 30.283 -1.146 30 -1.146 c
+29.354 1.294 m
+30.382 1.294 l
+30.382 1.397 l
+30.382 1.632 30.343 1.808 30.265 1.926 c
+30.195 2.051 30.067 2.117 29.883 2.117 c
+29.707 2.117 29.574 2.047 29.486 1.912 c
+29.405 1.783 29.361 1.577 29.354 1.294 c
+33.881 -1.072 m
+33.852 -1.014 33.822 -0.911 33.793 -0.764 c
+33.605 -1.022 33.356 -1.146 33.043 -1.146 c
+32.709 -1.146 32.429 -1.04 32.205 -0.823 c
+31.989 -0.598 31.882 -0.309 31.882 0.044 c
+31.882 0.456 32.014 0.772 32.279 1 c
+32.543 1.235 32.926 1.353 33.425 1.353 c
+33.749 1.353 l
+33.749 1.676 l
+33.749 1.852 33.712 1.974 33.646 2.043 c
+33.587 2.12 33.499 2.161 33.381 2.161 c
+33.124 2.161 32.999 2.007 32.999 1.706 c
+31.955 1.706 l
+31.955 2.076 32.091 2.382 32.367 2.617 c
+32.639 2.859 32.988 2.984 33.41 2.984 c
+33.852 2.984 34.189 2.866 34.424 2.631 c
+34.667 2.404 34.792 2.08 34.792 1.661 c
+34.792 -0.205 l
+34.792 -0.551 34.84 -0.819 34.939 -1.014 c
+34.939 -1.072 l
+h
+33.279 -0.323 m
+33.385 -0.323 33.477 -0.305 33.558 -0.264 c
+33.646 -0.216 33.709 -0.158 33.749 -0.087 c
+33.749 0.736 l
+33.499 0.736 l
+33.323 0.736 33.179 0.676 33.073 0.559 c
+32.973 0.449 32.926 0.302 32.926 0.118 c
+32.926 -0.176 33.043 -0.323 33.279 -0.323 c
+f
+Q
+q 1 0 0 1 519.5421 175.3549 cm
+0 0 m
+0 0.118 0.033 0.213 0.103 0.294 c
+0.169 0.371 0.272 0.411 0.412 0.411 c
+0.559 0.411 0.665 0.371 0.735 0.294 c
+0.812 0.213 0.852 0.118 0.852 0 c
+0.852 -0.11 0.812 -0.202 0.735 -0.279 c
+0.665 -0.357 0.559 -0.397 0.412 -0.397 c
+0.272 -0.397 0.169 -0.357 0.103 -0.279 c
+0.033 -0.202 0 -0.11 0 0 c
+f
+Q
+0.793 0.801 0.129 0.016 k
+535.667 282.996 238.665 -21.457 re
+f
+0 0 0 0 k
+q 1 0 0 1 629.4581 271.0489 cm
+0 0 m
+0.794 0 l
+0.794 -1.514 l
+0 -1.514 l
+0 -3.542 l
+-1.823 -3.542 l
+-1.823 -1.514 l
+-4.924 -1.514 l
+-4.998 -0.338 l
+-1.823 5.821 l
+0 5.821 l
+h
+-3.234 0 m
+-1.823 0 l
+-1.823 3.132 l
+-1.852 3.057 l
+h
+2.253 -2.616 m
+2.253 -2.322 2.348 -2.083 2.547 -1.896 c
+2.741 -1.712 2.995 -1.616 3.31 -1.616 c
+3.612 -1.616 3.862 -1.712 4.06 -1.896 c
+4.266 -2.083 4.37 -2.322 4.37 -2.616 c
+4.37 -2.921 4.266 -3.167 4.06 -3.351 c
+3.862 -3.528 3.612 -3.615 3.31 -3.615 c
+3.006 -3.615 2.753 -3.524 2.547 -3.336 c
+2.348 -3.152 2.253 -2.91 2.253 -2.616 c
+15.621 -2.484 m
+15.258 -2.877 14.813 -3.175 14.284 -3.38 c
+13.755 -3.575 13.174 -3.675 12.549 -3.675 c
+11.469 -3.675 10.631 -3.343 10.036 -2.675 c
+9.437 -2.009 9.132 -1.04 9.124 0.235 c
+9.124 1.926 l
+9.124 3.219 9.404 4.212 9.962 4.91 c
+10.528 5.604 11.351 5.954 12.432 5.954 c
+13.45 5.954 14.214 5.696 14.724 5.189 c
+15.243 4.69 15.54 3.903 15.621 2.837 c
+13.784 2.837 l
+13.733 3.433 13.612 3.84 13.417 4.057 c
+13.218 4.27 12.91 4.381 12.49 4.381 c
+11.979 4.381 11.609 4.193 11.374 3.822 c
+11.146 3.447 11.028 2.856 11.021 2.043 c
+11.021 0.339 l
+11.021 -0.514 11.146 -1.139 11.403 -1.529 c
+11.657 -1.911 12.072 -2.102 12.652 -2.102 c
+13.024 -2.102 13.328 -2.028 13.564 -1.882 c
+13.725 -1.764 l
+13.725 -0.043 l
+12.403 -0.043 l
+12.403 1.382 l
+15.621 1.382 l
+h
+19.179 -3.542 -1.896 9.363 re
+26.759 4.248 m
+24.438 4.248 l
+24.438 -3.542 l
+22.541 -3.542 l
+22.541 4.248 l
+20.263 4.248 l
+20.263 5.821 l
+26.759 5.821 l
+h
+30.769 -3.542 m
+30.769 5.821 l
+33.664 5.821 l
+34.642 5.821 35.388 5.601 35.899 5.16 c
+36.406 4.726 36.663 4.09 36.663 3.249 c
+36.663 2.779 36.552 2.371 36.34 2.029 c
+36.134 1.683 35.844 1.434 35.472 1.279 c
+35.891 1.151 36.215 0.904 36.442 0.545 c
+36.678 0.192 36.795 -0.257 36.795 -0.793 c
+36.795 -1.697 36.546 -2.381 36.045 -2.851 c
+35.546 -3.314 34.819 -3.542 33.87 -3.542 c
+h
+32.665 0.53 m
+32.665 -1.969 l
+33.87 -1.969 l
+34.212 -1.969 34.473 -1.866 34.649 -1.66 c
+34.825 -1.448 34.914 -1.146 34.914 -0.764 c
+34.914 0.077 34.605 0.507 33.988 0.53 c
+h
+32.665 1.912 m
+33.649 1.912 l
+34.385 1.912 34.752 2.293 34.752 3.057 c
+34.752 3.477 34.664 3.782 34.487 3.969 c
+34.318 4.152 34.046 4.248 33.664 4.248 c
+32.665 4.248 l
+h
+41.198 -0.118 m
+40.257 -0.118 l
+40.257 -3.542 l
+38.376 -3.542 l
+38.376 5.821 l
+41.389 5.821 l
+42.337 5.821 43.068 5.575 43.579 5.087 c
+44.097 4.594 44.358 3.899 44.358 2.999 c
+44.358 1.754 43.906 0.882 43.006 0.383 c
+44.637 -3.453 l
+44.637 -3.542 l
+42.609 -3.542 l
+h
+40.257 1.455 m
+41.33 1.455 l
+41.712 1.455 41.996 1.577 42.183 1.823 c
+42.366 2.076 42.462 2.415 42.462 2.837 c
+42.462 3.778 42.098 4.248 41.374 4.248 c
+40.257 4.248 l
+h
+50.345 -1.631 m
+47.772 -1.631 l
+47.272 -3.542 l
+45.273 -3.542 l
+48.198 5.821 l
+49.917 5.821 l
+52.872 -3.542 l
+50.844 -3.542 l
+h
+48.184 -0.043 m
+49.933 -0.043 l
+49.051 3.293 l
+h
+60.527 -3.542 m
+58.631 -3.542 l
+55.868 2.602 l
+55.868 -3.542 l
+53.971 -3.542 l
+53.971 5.821 l
+55.868 5.821 l
+58.631 -0.323 l
+58.631 5.821 l
+60.527 5.821 l
+h
+68.63 -0.426 m
+68.578 -1.495 68.277 -2.304 67.718 -2.851 c
+67.167 -3.403 66.395 -3.675 65.396 -3.675 c
+64.327 -3.675 63.507 -3.329 62.941 -2.63 c
+62.382 -1.936 62.104 -0.941 62.104 0.353 c
+62.104 1.926 l
+62.104 3.219 62.39 4.212 62.97 4.91 c
+63.558 5.604 64.371 5.954 65.411 5.954 c
+66.428 5.954 67.204 5.663 67.733 5.087 c
+68.262 4.516 68.564 3.697 68.645 2.631 c
+66.748 2.631 l
+66.727 3.296 66.623 3.753 66.44 3.998 c
+66.253 4.252 65.91 4.381 65.411 4.381 c
+64.911 4.381 64.551 4.204 64.338 3.851 c
+64.132 3.499 64.018 2.914 63.999 2.103 c
+63.999 0.339 l
+63.999 -0.595 64.103 -1.234 64.308 -1.587 c
+64.522 -1.932 64.886 -2.102 65.396 -2.102 c
+65.885 -2.102 66.223 -1.984 66.411 -1.749 c
+66.605 -1.506 66.712 -1.065 66.733 -0.426 c
+h
+76.578 -3.542 m
+74.697 -3.542 l
+74.697 0.47 l
+71.904 0.47 l
+71.904 -3.542 l
+70.008 -3.542 l
+70.008 5.821 l
+71.904 5.821 l
+71.904 2.029 l
+74.697 2.029 l
+74.697 5.821 l
+76.578 5.821 l
+h
+80.297 -3.542 -1.896 9.363 re
+88.547 -3.542 m
+86.651 -3.542 l
+83.888 2.602 l
+83.888 -3.542 l
+81.991 -3.542 l
+81.991 5.821 l
+83.888 5.821 l
+86.651 -0.323 l
+86.651 5.821 l
+88.547 5.821 l
+h
+96.665 -2.484 m
+96.301 -2.877 95.857 -3.175 95.327 -3.38 c
+94.797 -3.575 94.217 -3.675 93.592 -3.675 c
+92.512 -3.675 91.674 -3.343 91.079 -2.675 c
+90.48 -2.009 90.175 -1.04 90.168 0.235 c
+90.168 1.926 l
+90.168 3.219 90.447 4.212 91.005 4.91 c
+91.572 5.604 92.395 5.954 93.475 5.954 c
+94.493 5.954 95.257 5.696 95.768 5.189 c
+96.287 4.69 96.584 3.903 96.665 2.837 c
+94.827 2.837 l
+94.776 3.433 94.654 3.84 94.46 4.057 c
+94.261 4.27 93.953 4.381 93.534 4.381 c
+93.023 4.381 92.652 4.193 92.416 3.822 c
+92.189 3.447 92.071 2.856 92.064 2.043 c
+92.064 0.339 l
+92.064 -0.514 92.189 -1.139 92.445 -1.529 c
+92.699 -1.911 93.114 -2.102 93.696 -2.102 c
+94.066 -2.102 94.371 -2.028 94.606 -1.882 c
+94.768 -1.764 l
+94.768 -0.043 l
+93.445 -0.043 l
+93.445 1.382 l
+96.665 1.382 l
+h
+103.83 5.821 m
+105.609 -0.955 l
+107.358 5.821 l
+109.828 5.821 l
+109.828 -3.542 l
+107.931 -3.542 l
+107.931 -1.014 l
+108.108 2.897 l
+106.241 -3.542 l
+104.948 -3.542 l
+103.081 2.897 l
+103.257 -1.014 l
+103.257 -3.542 l
+101.361 -3.542 l
+101.361 5.821 l
+h
+118.225 0.294 m
+118.225 -0.962 117.924 -1.936 117.328 -2.63 c
+116.729 -3.329 115.906 -3.675 114.859 -3.675 c
+113.808 -3.675 112.981 -3.333 112.374 -2.645 c
+111.775 -1.951 111.47 -0.984 111.463 0.25 c
+111.463 1.852 l
+111.463 3.135 111.761 4.138 112.359 4.865 c
+112.955 5.59 113.785 5.954 114.844 5.954 c
+115.88 5.954 116.7 5.594 117.298 4.881 c
+117.905 4.175 118.213 3.179 118.225 1.897 c
+h
+116.328 1.867 m
+116.328 2.708 116.203 3.337 115.961 3.749 c
+115.726 4.16 115.351 4.366 114.844 4.366 c
+114.344 4.366 113.969 4.164 113.727 3.763 c
+113.492 3.37 113.367 2.771 113.359 1.97 c
+113.359 0.294 l
+113.359 -0.521 113.48 -1.124 113.727 -1.514 c
+113.969 -1.907 114.348 -2.102 114.859 -2.102 c
+115.347 -2.102 115.711 -1.911 115.946 -1.529 c
+116.189 -1.146 116.318 -0.558 116.328 0.235 c
+h
+119.801 -3.542 m
+119.801 5.821 l
+122.285 5.821 l
+123.373 5.821 124.24 5.472 124.887 4.778 c
+125.541 4.079 125.876 3.132 125.886 1.926 c
+125.886 0.412 l
+125.886 -0.823 125.563 -1.793 124.916 -2.499 c
+124.27 -3.197 123.373 -3.542 122.227 -3.542 c
+h
+121.697 4.248 m
+121.697 -1.969 l
+122.256 -1.969 l
+122.892 -1.969 123.336 -1.804 123.593 -1.469 c
+123.846 -1.139 123.979 -0.565 123.99 0.25 c
+123.99 1.867 l
+123.99 2.749 123.865 3.359 123.623 3.705 c
+123.387 4.046 122.976 4.227 122.388 4.248 c
+h
+132.284 0.515 m
+129.344 0.515 l
+129.344 -1.969 l
+132.828 -1.969 l
+132.828 -3.542 l
+127.449 -3.542 l
+127.449 5.821 l
+132.814 5.821 l
+132.814 4.248 l
+129.344 4.248 l
+129.344 2.029 l
+132.284 2.029 l
+h
+135.985 -1.969 m
+139.307 -1.969 l
+139.307 -3.542 l
+134.088 -3.542 l
+134.088 5.821 l
+135.985 5.821 l
+h
+f
+Q
+0.113 0.082 0.09 0 k
+535.667 252.694 238.665 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 540.8555 245.8587 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.088 -0.5 c
+-0.147 -0.646 -0.242 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.147 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.358 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.69 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.141 1.278 -3.218 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.218 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.042 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.201 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.205 c
+-0.756 1.147 -0.595 1.066 -0.455 0.97 c
+-0.319 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.318 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.249 -2.454 2.19 c
+-2.476 2.132 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.2 1.558 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.102 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.52 -2.484 5.403 -2.396 c
+5.285 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.753 5.964 -1.86 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.214 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.206 c
+4.836 0.008 4.815 0.243 4.815 0.5 c
+4.815 0.771 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.631 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.189 2.263 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.179 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.97 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.13 2.165 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.992 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.872 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.499 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.5 6.729 1.558 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.226 1.621 6.137 1.573 c
+6.056 1.532 5.991 1.463 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.301 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.675 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.117 l
+14.498 3.117 l
+14.498 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.498 1.602 l
+14.498 -0.103 l
+14.498 -0.324 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.566 14.648 -0.61 14.718 -0.646 c
+14.795 -0.676 14.909 -0.69 15.056 -0.69 c
+15.192 -0.69 15.327 -0.687 15.468 -0.676 c
+15.603 -0.658 15.736 -0.632 15.865 -0.603 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.042 -1.301 14.942 -1.309 14.836 -1.309 c
+14.648 -1.309 14.486 -1.294 14.35 -1.264 c
+14.222 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.792 -0.956 c
+13.733 -0.879 13.689 -0.801 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+21.12 -1.264 m
+21.12 -0.97 l
+21.126 -0.834 21.134 -0.676 21.134 -0.5 c
+21.134 3.513 l
+22.045 3.513 l
+22.045 2.234 l
+22.045 2.072 l
+22.045 1.897 l
+22.045 1.845 22.038 1.801 22.03 1.764 c
+22.03 1.675 l
+22.045 1.675 l
+22.093 1.783 22.155 1.874 22.236 1.955 c
+22.313 2.032 22.398 2.095 22.487 2.146 c
+22.574 2.194 22.666 2.227 22.766 2.249 c
+22.861 2.267 22.961 2.278 23.059 2.278 c
+23.273 2.278 23.46 2.234 23.618 2.146 c
+23.773 2.058 23.901 1.929 24 1.764 c
+24.106 1.606 24.184 1.415 24.235 1.191 c
+24.283 0.974 24.309 0.735 24.309 0.47 c
+24.309 0.214 24.28 -0.025 24.22 -0.25 c
+24.162 -0.467 24.077 -0.658 23.971 -0.823 c
+23.861 -0.981 23.728 -1.103 23.574 -1.191 c
+23.416 -1.278 23.236 -1.323 23.03 -1.323 c
+22.931 -1.323 22.832 -1.311 22.736 -1.294 c
+22.648 -1.272 22.56 -1.242 22.471 -1.191 c
+22.383 -1.132 22.302 -1.066 22.236 -0.985 c
+22.167 -0.908 22.104 -0.808 22.045 -0.69 c
+22.03 -0.69 l
+22.03 -0.852 l
+22.03 -0.912 22.024 -0.97 22.016 -1.029 c
+22.016 -1.08 22.009 -1.128 22.001 -1.176 c
+22.001 -1.216 21.994 -1.246 21.986 -1.264 c
+h
+22.03 0.5 m
+22.03 0.264 22.049 0.066 22.09 -0.088 c
+22.137 -0.246 22.196 -0.368 22.265 -0.455 c
+22.332 -0.544 22.406 -0.61 22.487 -0.646 c
+22.564 -0.687 22.641 -0.706 22.722 -0.706 c
+22.927 -0.706 23.082 -0.61 23.192 -0.411 c
+23.31 -0.216 23.368 0.077 23.368 0.47 c
+23.368 0.684 23.346 0.867 23.31 1.014 c
+23.28 1.168 23.236 1.294 23.177 1.382 c
+23.125 1.477 23.059 1.55 22.971 1.602 c
+22.89 1.65 22.803 1.675 22.707 1.675 c
+22.626 1.675 22.549 1.654 22.471 1.617 c
+22.391 1.577 22.317 1.514 22.251 1.426 c
+22.192 1.338 22.137 1.213 22.09 1.058 c
+22.049 0.912 22.03 0.724 22.03 0.5 c
+28.193 1.47 m
+28.094 1.477 27.991 1.488 27.884 1.5 c
+27.774 1.517 27.653 1.529 27.517 1.529 c
+27.341 1.529 27.183 1.488 27.046 1.411 c
+26.907 1.342 26.789 1.242 26.693 1.118 c
+26.606 0.989 26.535 0.842 26.488 0.676 c
+26.448 0.507 26.429 0.331 26.429 0.147 c
+26.429 -1.264 l
+25.533 -1.264 l
+25.533 0.985 l
+25.533 1.11 25.521 1.235 25.503 1.353 c
+25.492 1.477 25.477 1.595 25.459 1.706 c
+25.448 1.823 25.434 1.918 25.415 1.999 c
+25.393 2.088 25.375 2.161 25.356 2.219 c
+26.238 2.219 l
+26.246 2.168 26.257 2.117 26.267 2.058 c
+26.286 1.999 26.3 1.933 26.312 1.866 c
+26.33 1.808 26.344 1.742 26.356 1.675 c
+26.363 1.606 26.375 1.544 26.385 1.484 c
+26.4 1.484 l
+26.437 1.602 26.488 1.708 26.547 1.808 c
+26.613 1.903 26.693 1.988 26.782 2.058 c
+26.87 2.124 26.973 2.179 27.09 2.219 c
+27.216 2.256 27.362 2.278 27.532 2.278 c
+27.657 2.278 27.774 2.271 27.884 2.263 c
+28.002 2.253 28.105 2.238 28.193 2.219 c
+h
+30.196 -1.323 m
+30.026 -1.323 29.876 -1.301 29.741 -1.264 c
+29.612 -1.216 29.498 -1.147 29.402 -1.058 c
+29.314 -0.97 29.244 -0.864 29.197 -0.735 c
+29.145 -0.599 29.122 -0.449 29.122 -0.279 c
+29.122 -0.073 29.156 0.096 29.226 0.235 c
+29.292 0.382 29.388 0.492 29.505 0.573 c
+29.63 0.661 29.773 0.724 29.932 0.765 c
+30.097 0.802 30.273 0.827 30.46 0.838 c
+31.181 0.852 l
+31.181 1.029 l
+31.181 1.147 31.169 1.249 31.152 1.338 c
+31.129 1.426 31.096 1.492 31.048 1.544 c
+31.008 1.602 30.961 1.639 30.901 1.661 c
+30.843 1.679 30.776 1.691 30.71 1.691 c
+30.641 1.691 30.578 1.679 30.519 1.661 c
+30.468 1.65 30.42 1.625 30.373 1.588 c
+30.332 1.558 30.298 1.507 30.269 1.44 c
+30.248 1.382 30.233 1.301 30.225 1.205 c
+29.284 1.249 l
+29.314 1.396 29.358 1.532 29.417 1.661 c
+29.483 1.786 29.579 1.897 29.696 1.984 c
+29.814 2.08 29.953 2.153 30.122 2.205 c
+30.298 2.253 30.504 2.278 30.74 2.278 c
+31.181 2.278 31.511 2.168 31.74 1.955 c
+31.975 1.749 32.092 1.44 32.092 1.029 c
+32.092 -0.235 l
+32.092 -0.455 l
+32.099 -0.515 32.114 -0.569 32.137 -0.617 c
+32.154 -0.658 32.184 -0.69 32.224 -0.721 c
+32.261 -0.742 32.313 -0.75 32.372 -0.75 c
+32.438 -0.75 32.507 -0.746 32.577 -0.735 c
+32.577 -1.22 l
+32.519 -1.231 32.463 -1.242 32.415 -1.249 c
+32.375 -1.261 32.334 -1.268 32.297 -1.278 c
+32.257 -1.286 32.214 -1.294 32.166 -1.294 c
+32.114 -1.301 32.056 -1.309 31.989 -1.309 c
+31.761 -1.309 31.596 -1.257 31.489 -1.147 c
+31.379 -1.029 31.316 -0.864 31.298 -0.646 c
+31.283 -0.646 l
+31.214 -0.757 31.144 -0.852 31.078 -0.941 c
+31.008 -1.022 30.931 -1.087 30.843 -1.147 c
+30.755 -1.205 30.655 -1.249 30.549 -1.278 c
+30.45 -1.309 30.332 -1.323 30.196 -1.323 c
+31.181 0.353 m
+30.755 0.338 l
+30.655 0.338 30.564 0.331 30.475 0.324 c
+30.394 0.312 30.328 0.287 30.269 0.25 c
+30.211 0.21 30.159 0.151 30.122 0.073 c
+30.082 0.004 30.063 -0.088 30.063 -0.206 c
+30.063 -0.374 30.097 -0.496 30.167 -0.573 c
+30.233 -0.654 30.332 -0.69 30.46 -0.69 c
+30.567 -0.69 30.666 -0.669 30.755 -0.617 c
+30.85 -0.569 30.931 -0.507 30.99 -0.426 c
+31.056 -0.349 31.107 -0.261 31.137 -0.162 c
+31.166 -0.055 31.181 0.059 31.181 0.177 c
+h
+35.495 -1.264 m
+35.495 0.721 l
+35.495 1.022 35.451 1.242 35.362 1.382 c
+35.281 1.529 35.146 1.602 34.951 1.602 c
+34.841 1.602 34.738 1.577 34.643 1.529 c
+34.554 1.477 34.473 1.411 34.408 1.323 c
+34.348 1.235 34.297 1.124 34.26 1 c
+34.231 0.882 34.216 0.75 34.216 0.603 c
+34.216 -1.264 l
+33.305 -1.264 l
+33.305 1.44 l
+33.305 1.661 l
+33.305 1.749 33.297 1.826 33.29 1.897 c
+33.29 2.088 l
+33.29 2.219 l
+34.142 2.219 l
+34.15 2.19 34.157 2.146 34.157 2.088 c
+34.157 1.897 l
+34.165 1.826 34.172 1.756 34.172 1.691 c
+34.179 1.621 34.186 1.565 34.186 1.529 c
+34.202 1.529 l
+34.319 1.793 34.47 1.984 34.657 2.102 c
+34.841 2.219 35.061 2.278 35.318 2.278 c
+35.503 2.278 35.664 2.249 35.804 2.19 c
+35.939 2.132 36.054 2.043 36.141 1.926 c
+36.23 1.808 36.293 1.665 36.332 1.5 c
+36.38 1.342 36.407 1.154 36.407 0.941 c
+36.407 -1.264 l
+h
+38.923 -1.323 m
+38.637 -1.323 38.394 -1.282 38.188 -1.205 c
+37.982 -1.117 37.81 -0.996 37.674 -0.838 c
+37.535 -0.684 37.431 -0.496 37.365 -0.279 c
+37.296 -0.055 37.263 0.191 37.263 0.455 c
+37.263 0.75 37.296 1.007 37.365 1.235 c
+37.442 1.459 37.549 1.646 37.689 1.793 c
+37.836 1.947 38.012 2.065 38.218 2.146 c
+38.424 2.234 38.659 2.278 38.923 2.278 c
+39.148 2.278 39.35 2.249 39.526 2.19 c
+39.702 2.132 39.854 2.047 39.981 1.941 c
+40.107 1.841 40.21 1.72 40.291 1.573 c
+40.368 1.433 40.423 1.282 40.452 1.118 c
+39.541 1.073 l
+39.511 1.249 39.441 1.389 39.335 1.5 c
+39.235 1.606 39.092 1.661 38.909 1.661 c
+38.662 1.661 38.487 1.558 38.379 1.353 c
+38.269 1.154 38.218 0.867 38.218 0.485 c
+38.218 -0.309 38.453 -0.706 38.923 -0.706 c
+39.089 -0.706 39.232 -0.654 39.35 -0.544 c
+39.467 -0.437 39.541 -0.276 39.57 -0.058 c
+40.482 -0.103 l
+40.452 -0.272 40.397 -0.426 40.32 -0.573 c
+40.25 -0.721 40.147 -0.852 40.012 -0.97 c
+39.883 -1.08 39.725 -1.168 39.541 -1.234 c
+39.364 -1.294 39.158 -1.323 38.923 -1.323 c
+42.323 1.514 m
+42.441 1.786 42.591 1.984 42.778 2.102 c
+42.962 2.219 43.183 2.278 43.44 2.278 c
+43.646 2.278 43.815 2.242 43.954 2.176 c
+44.101 2.105 44.211 2.014 44.292 1.897 c
+44.38 1.779 44.44 1.635 44.469 1.47 c
+44.505 1.301 44.527 1.124 44.527 0.941 c
+44.527 -1.264 l
+43.616 -1.264 l
+43.616 0.735 l
+43.616 0.871 43.605 0.992 43.586 1.103 c
+43.576 1.209 43.55 1.297 43.513 1.367 c
+43.473 1.444 43.414 1.503 43.337 1.544 c
+43.267 1.58 43.175 1.602 43.058 1.602 c
+42.948 1.602 42.852 1.577 42.763 1.529 c
+42.676 1.477 42.595 1.411 42.528 1.323 c
+42.47 1.235 42.418 1.124 42.381 1 c
+42.352 0.882 42.337 0.75 42.337 0.603 c
+42.337 -1.264 l
+41.426 -1.264 l
+41.426 3.513 l
+42.337 3.513 l
+42.337 2.205 l
+42.337 2.135 42.329 2.065 42.323 1.999 c
+42.323 1.793 l
+42.323 1.735 42.315 1.679 42.308 1.631 c
+42.308 1.514 l
+h
+50.297 -2.631 m
+50.297 3.513 l
+52.222 3.513 l
+52.222 2.896 l
+51.149 2.896 l
+51.149 -2.014 l
+52.222 -2.014 l
+52.222 -2.631 l
+h
+54.2 0.838 1.866 -0.794 re
+54.2 0.044 m
+58.628 -1.323 m
+58.458 -1.323 58.308 -1.301 58.172 -1.264 c
+58.044 -1.216 57.93 -1.147 57.834 -1.058 c
+57.745 -0.97 57.676 -0.864 57.628 -0.735 c
+57.576 -0.599 57.554 -0.449 57.554 -0.279 c
+57.554 -0.073 57.587 0.096 57.657 0.235 c
+57.724 0.382 57.819 0.492 57.936 0.573 c
+58.062 0.661 58.204 0.724 58.363 0.765 c
+58.528 0.802 58.705 0.827 58.892 0.838 c
+59.613 0.852 l
+59.613 1.029 l
+59.613 1.147 59.602 1.249 59.583 1.338 c
+59.561 1.426 59.528 1.492 59.48 1.544 c
+59.44 1.602 59.392 1.639 59.333 1.661 c
+59.274 1.679 59.208 1.691 59.142 1.691 c
+59.072 1.691 59.01 1.679 58.951 1.661 c
+58.9 1.65 58.852 1.625 58.804 1.588 c
+58.763 1.558 58.73 1.507 58.701 1.44 c
+58.679 1.382 58.664 1.301 58.657 1.205 c
+57.716 1.249 l
+57.745 1.396 57.79 1.532 57.849 1.661 c
+57.915 1.786 58.01 1.897 58.127 1.984 c
+58.245 2.08 58.385 2.153 58.554 2.205 c
+58.73 2.253 58.936 2.278 59.172 2.278 c
+59.613 2.278 59.943 2.168 60.171 1.955 c
+60.406 1.749 60.523 1.44 60.523 1.029 c
+60.523 -0.235 l
+60.523 -0.455 l
+60.531 -0.515 60.546 -0.569 60.568 -0.617 c
+60.586 -0.658 60.616 -0.69 60.656 -0.721 c
+60.693 -0.742 60.744 -0.75 60.803 -0.75 c
+60.869 -0.75 60.939 -0.746 61.009 -0.735 c
+61.009 -1.22 l
+60.95 -1.231 60.895 -1.242 60.847 -1.249 c
+60.807 -1.261 60.766 -1.268 60.729 -1.278 c
+60.689 -1.286 60.645 -1.294 60.597 -1.294 c
+60.546 -1.301 60.487 -1.309 60.421 -1.309 c
+60.193 -1.309 60.028 -1.257 59.921 -1.147 c
+59.811 -1.029 59.748 -0.864 59.73 -0.646 c
+59.715 -0.646 l
+59.646 -0.757 59.576 -0.852 59.509 -0.941 c
+59.44 -1.022 59.363 -1.087 59.274 -1.147 c
+59.186 -1.205 59.087 -1.249 58.981 -1.278 c
+58.881 -1.309 58.763 -1.323 58.628 -1.323 c
+59.613 0.353 m
+59.186 0.338 l
+59.087 0.338 58.995 0.331 58.907 0.324 c
+58.826 0.312 58.76 0.287 58.701 0.25 c
+58.642 0.21 58.591 0.151 58.554 0.073 c
+58.514 0.004 58.495 -0.088 58.495 -0.206 c
+58.495 -0.374 58.528 -0.496 58.598 -0.573 c
+58.664 -0.654 58.763 -0.69 58.892 -0.69 c
+58.998 -0.69 59.098 -0.669 59.186 -0.617 c
+59.282 -0.569 59.363 -0.507 59.421 -0.426 c
+59.488 -0.349 59.538 -0.261 59.569 -0.162 c
+59.598 -0.055 59.613 0.059 59.613 0.177 c
+h
+62.1 -2.631 m
+62.1 -2.014 l
+63.174 -2.014 l
+63.174 2.896 l
+62.1 2.896 l
+62.1 3.513 l
+64.026 3.513 l
+64.026 -2.631 l
+h
+f
+Q
+q 1 0 0 1 541.9542 230.2781 cm
+0 0 m
+2.103 0 l
+2.103 -0.574 l
+-0.675 -0.574 l
+-0.675 4.777 l
+0 4.777 l
+h
+3.514 -0.574 -0.647 3.984 re
+3.558 4.453 m
+3.558 4.343 3.528 4.251 3.469 4.174 c
+3.41 4.104 3.315 4.072 3.19 4.072 c
+3.073 4.072 2.977 4.104 2.911 4.174 c
+2.852 4.251 2.822 4.343 2.822 4.453 c
+2.822 4.571 2.852 4.663 2.911 4.733 c
+2.977 4.81 3.073 4.85 3.19 4.85 c
+3.315 4.85 3.41 4.81 3.469 4.733 c
+3.528 4.652 3.558 4.56 3.558 4.453 c
+6.468 0.44 m
+6.468 0.588 6.413 0.708 6.306 0.808 c
+6.196 0.903 5.991 1.021 5.689 1.161 c
+5.343 1.308 5.101 1.429 4.954 1.529 c
+4.807 1.635 4.697 1.753 4.63 1.881 c
+4.561 2.006 4.528 2.164 4.528 2.352 c
+4.528 2.675 4.645 2.943 4.881 3.16 c
+5.116 3.373 5.417 3.484 5.792 3.484 c
+6.174 3.484 6.483 3.37 6.718 3.145 c
+6.953 2.917 7.071 2.63 7.071 2.278 c
+6.423 2.278 l
+6.423 2.454 6.365 2.605 6.247 2.734 c
+6.13 2.859 5.976 2.925 5.792 2.925 c
+5.594 2.925 5.442 2.869 5.336 2.763 c
+5.226 2.664 5.174 2.532 5.174 2.366 c
+5.174 2.237 5.211 2.131 5.292 2.042 c
+5.369 1.961 5.561 1.859 5.866 1.734 c
+6.343 1.547 6.674 1.359 6.851 1.176 c
+7.026 0.999 7.115 0.771 7.115 0.5 c
+7.115 0.147 6.99 -0.133 6.747 -0.339 c
+6.512 -0.545 6.196 -0.647 5.806 -0.647 c
+5.384 -0.647 5.046 -0.53 4.792 -0.294 c
+4.535 -0.052 4.41 0.253 4.41 0.617 c
+5.056 0.617 l
+5.064 0.389 5.134 0.213 5.263 0.087 c
+5.388 -0.03 5.571 -0.088 5.806 -0.088 c
+6.02 -0.088 6.182 -0.04 6.292 0.058 c
+6.409 0.154 6.468 0.282 6.468 0.44 c
+8.805 4.365 m
+8.805 3.41 l
+9.407 3.41 l
+9.407 2.881 l
+8.805 2.881 l
+8.805 0.411 l
+8.805 0.253 8.827 0.135 8.879 0.058 c
+8.937 -0.023 9.025 -0.059 9.143 -0.059 c
+9.232 -0.059 9.32 -0.044 9.407 -0.015 c
+9.407 -0.574 l
+9.261 -0.622 9.106 -0.647 8.952 -0.647 c
+8.695 -0.647 8.5 -0.555 8.364 -0.368 c
+8.225 -0.184 8.158 0.077 8.158 0.411 c
+8.158 2.881 l
+7.556 2.881 l
+7.556 3.41 l
+8.158 3.41 l
+8.158 4.365 l
+h
+13.935 -0.574 m
+13.895 -0.485 13.869 -0.339 13.862 -0.133 c
+13.627 -0.478 13.332 -0.647 12.979 -0.647 c
+12.616 -0.647 12.333 -0.551 12.127 -0.353 c
+11.929 -0.148 11.833 0.139 11.833 0.514 c
+11.833 0.914 11.969 1.234 12.245 1.469 c
+12.517 1.712 12.891 1.837 13.361 1.837 c
+13.847 1.837 l
+13.847 2.263 l
+13.847 2.499 13.792 2.664 13.685 2.763 c
+13.575 2.869 13.413 2.925 13.201 2.925 c
+13.002 2.925 12.84 2.865 12.715 2.748 c
+12.597 2.63 12.538 2.484 12.538 2.308 c
+11.892 2.308 l
+11.892 2.502 11.95 2.693 12.068 2.881 c
+12.193 3.064 12.355 3.212 12.553 3.322 c
+12.759 3.428 12.987 3.484 13.244 3.484 c
+13.645 3.484 13.949 3.38 14.155 3.175 c
+14.369 2.969 14.483 2.675 14.494 2.293 c
+14.494 0.278 l
+14.494 -0.026 14.531 -0.291 14.612 -0.515 c
+14.612 -0.574 l
+h
+13.068 -0.059 m
+13.233 -0.059 13.384 -0.015 13.523 0.073 c
+13.671 0.161 13.777 0.272 13.847 0.411 c
+13.847 1.352 l
+13.479 1.352 l
+13.164 1.352 12.921 1.282 12.744 1.146 c
+12.568 1.018 12.48 0.83 12.48 0.588 c
+12.48 0.359 12.524 0.195 12.613 0.087 c
+12.7 -0.011 12.851 -0.059 13.068 -0.059 c
+16.199 -0.574 -0.646 5.644 re
+17.918 -0.574 -0.646 5.644 re
+21.358 -0.574 -0.646 5.644 re
+22.24 1.602 m
+22.24 2.179 22.376 2.634 22.651 2.969 c
+22.934 3.31 23.306 3.484 23.769 3.484 c
+24.228 3.484 24.596 3.314 24.871 2.983 c
+25.154 2.66 25.301 2.212 25.312 1.646 c
+25.312 1.219 l
+25.312 0.65 25.169 0.195 24.885 -0.148 c
+24.611 -0.482 24.243 -0.647 23.784 -0.647 c
+23.32 -0.647 22.949 -0.485 22.666 -0.162 c
+22.391 0.168 22.248 0.61 22.24 1.161 c
+h
+22.886 1.219 m
+22.886 0.816 22.964 0.5 23.122 0.264 c
+23.287 0.029 23.508 -0.088 23.784 -0.088 c
+24.349 -0.088 24.644 0.323 24.665 1.146 c
+24.665 1.602 l
+24.665 2.002 24.581 2.322 24.415 2.557 c
+24.257 2.8 24.041 2.925 23.769 2.925 c
+23.504 2.925 23.287 2.8 23.122 2.557 c
+22.964 2.322 22.886 2.002 22.886 1.602 c
+h
+27.532 -0.088 m
+27.745 -0.088 27.917 -0.026 28.046 0.103 c
+28.182 0.238 28.255 0.43 28.266 0.675 c
+28.884 0.675 l
+28.862 0.294 28.726 -0.026 28.472 -0.279 c
+28.216 -0.526 27.903 -0.647 27.532 -0.647 c
+27.04 -0.647 26.664 -0.497 26.4 -0.192 c
+26.142 0.12 26.018 0.588 26.018 1.205 c
+26.018 1.646 l
+26.018 2.241 26.142 2.697 26.4 3.013 c
+26.664 3.326 27.04 3.484 27.532 3.484 c
+27.932 3.484 28.252 3.351 28.487 3.087 c
+28.729 2.829 28.862 2.484 28.884 2.042 c
+28.266 2.042 l
+28.245 2.337 28.171 2.557 28.046 2.705 c
+27.929 2.851 27.756 2.925 27.532 2.925 c
+27.237 2.925 27.021 2.825 26.884 2.63 c
+26.745 2.443 26.672 2.135 26.664 1.705 c
+26.664 1.19 l
+26.664 0.72 26.73 0.386 26.87 0.191 c
+27.017 0.003 27.237 -0.088 27.532 -0.088 c
+31.677 -0.574 m
+31.636 -0.485 31.611 -0.339 31.603 -0.133 c
+31.368 -0.478 31.075 -0.647 30.722 -0.647 c
+30.358 -0.647 30.075 -0.551 29.868 -0.353 c
+29.67 -0.148 29.575 0.139 29.575 0.514 c
+29.575 0.914 29.71 1.234 29.986 1.469 c
+30.258 1.712 30.633 1.837 31.104 1.837 c
+31.588 1.837 l
+31.588 2.263 l
+31.588 2.499 31.534 2.664 31.427 2.763 c
+31.316 2.869 31.155 2.925 30.942 2.925 c
+30.743 2.925 30.582 2.865 30.456 2.748 c
+30.339 2.63 30.281 2.484 30.281 2.308 c
+29.633 2.308 l
+29.633 2.502 29.693 2.693 29.81 2.881 c
+29.935 3.064 30.097 3.212 30.295 3.322 c
+30.501 3.428 30.728 3.484 30.986 3.484 c
+31.387 3.484 31.692 3.38 31.898 3.175 c
+32.11 2.969 32.224 2.675 32.235 2.293 c
+32.235 0.278 l
+32.235 -0.026 32.272 -0.291 32.353 -0.515 c
+32.353 -0.574 l
+h
+30.809 -0.059 m
+30.975 -0.059 31.125 -0.015 31.266 0.073 c
+31.412 0.161 31.519 0.272 31.588 0.411 c
+31.588 1.352 l
+31.221 1.352 l
+30.905 1.352 30.662 1.282 30.487 1.146 c
+30.31 1.018 30.221 0.83 30.221 0.588 c
+30.221 0.359 30.265 0.195 30.354 0.087 c
+30.442 -0.011 30.593 -0.059 30.809 -0.059 c
+33.94 -0.574 -0.646 5.644 re
+39.555 1.219 m
+39.555 0.602 39.441 0.135 39.218 -0.177 c
+39 -0.493 38.678 -0.647 38.248 -0.647 c
+37.824 -0.647 37.512 -0.468 37.307 -0.103 c
+37.277 -0.574 l
+36.675 -0.574 l
+36.675 5.071 l
+37.321 5.071 l
+37.321 2.969 l
+37.535 3.31 37.843 3.484 38.248 3.484 c
+38.678 3.484 39 3.326 39.218 3.013 c
+39.441 2.707 39.555 2.241 39.555 1.616 c
+h
+38.909 1.602 m
+38.909 2.072 38.838 2.403 38.703 2.601 c
+38.574 2.796 38.365 2.896 38.071 2.896 c
+37.737 2.896 37.487 2.711 37.321 2.352 c
+37.321 0.47 l
+37.487 0.106 37.741 -0.074 38.086 -0.074 c
+38.379 -0.074 38.589 0.029 38.718 0.235 c
+38.842 0.44 38.909 0.756 38.909 1.19 c
+h
+42.04 2.792 m
+41.951 2.811 41.853 2.822 41.745 2.822 c
+41.411 2.822 41.176 2.638 41.04 2.278 c
+41.04 -0.574 l
+40.393 -0.574 l
+40.393 3.41 l
+41.026 3.41 l
+41.04 2.998 l
+41.217 3.322 41.459 3.484 41.775 3.484 c
+41.882 3.484 41.97 3.461 42.04 3.424 c
+h
+44.583 -0.574 m
+44.542 -0.485 44.517 -0.339 44.509 -0.133 c
+44.274 -0.478 43.98 -0.647 43.627 -0.647 c
+43.264 -0.647 42.981 -0.551 42.774 -0.353 c
+42.576 -0.148 42.481 0.139 42.481 0.514 c
+42.481 0.914 42.616 1.234 42.892 1.469 c
+43.164 1.712 43.539 1.837 44.01 1.837 c
+44.494 1.837 l
+44.494 2.263 l
+44.494 2.499 44.44 2.664 44.332 2.763 c
+44.222 2.869 44.06 2.925 43.848 2.925 c
+43.649 2.925 43.488 2.865 43.362 2.748 c
+43.245 2.63 43.187 2.484 43.187 2.308 c
+42.539 2.308 l
+42.539 2.502 42.599 2.693 42.716 2.881 c
+42.84 3.064 43.002 3.212 43.201 3.322 c
+43.407 3.428 43.634 3.484 43.892 3.484 c
+44.292 3.484 44.598 3.38 44.803 3.175 c
+45.016 2.969 45.13 2.675 45.141 2.293 c
+45.141 0.278 l
+45.141 -0.026 45.178 -0.291 45.259 -0.515 c
+45.259 -0.574 l
+h
+43.715 -0.059 m
+43.881 -0.059 44.031 -0.015 44.171 0.073 c
+44.318 0.161 44.425 0.272 44.494 0.411 c
+44.494 1.352 l
+44.127 1.352 l
+43.811 1.352 43.569 1.282 43.392 1.146 c
+43.216 1.018 43.127 0.83 43.127 0.588 c
+43.127 0.359 43.172 0.195 43.26 0.087 c
+43.347 -0.011 43.499 -0.059 43.715 -0.059 c
+46.758 3.41 m
+46.773 2.969 l
+47.027 3.31 47.349 3.484 47.743 3.484 c
+48.448 3.484 48.805 3.013 48.816 2.072 c
+48.816 -0.574 l
+48.169 -0.574 l
+48.169 2.042 l
+48.169 2.355 48.114 2.576 48.008 2.705 c
+47.897 2.829 47.743 2.896 47.537 2.896 c
+47.379 2.896 47.232 2.84 47.096 2.734 c
+46.967 2.624 46.865 2.487 46.788 2.322 c
+46.788 -0.574 l
+46.14 -0.574 l
+46.14 3.41 l
+h
+51.168 -0.088 m
+51.381 -0.088 51.553 -0.026 51.682 0.103 c
+51.818 0.238 51.892 0.43 51.902 0.675 c
+52.52 0.675 l
+52.498 0.294 52.362 -0.026 52.109 -0.279 c
+51.852 -0.526 51.538 -0.647 51.168 -0.647 c
+50.676 -0.647 50.3 -0.497 50.036 -0.192 c
+49.778 0.12 49.654 0.588 49.654 1.205 c
+49.654 1.646 l
+49.654 2.241 49.778 2.697 50.036 3.013 c
+50.3 3.326 50.676 3.484 51.168 3.484 c
+51.569 3.484 51.888 3.351 52.124 3.087 c
+52.365 2.829 52.498 2.484 52.52 2.042 c
+51.902 2.042 l
+51.881 2.337 51.807 2.557 51.682 2.705 c
+51.565 2.851 51.392 2.925 51.168 2.925 c
+50.873 2.925 50.657 2.825 50.521 2.63 c
+50.381 2.443 50.308 2.135 50.3 1.705 c
+50.3 1.19 l
+50.3 0.72 50.366 0.386 50.506 0.191 c
+50.653 0.003 50.873 -0.088 51.168 -0.088 c
+53.917 2.998 m
+54.17 3.322 54.49 3.484 54.871 3.484 c
+55.577 3.484 55.934 3.013 55.945 2.072 c
+55.945 -0.574 l
+55.299 -0.574 l
+55.299 2.042 l
+55.299 2.355 55.243 2.576 55.137 2.705 c
+55.027 2.829 54.871 2.896 54.666 2.896 c
+54.508 2.896 54.361 2.84 54.225 2.734 c
+54.096 2.624 53.994 2.487 53.917 2.322 c
+53.917 -0.574 l
+53.269 -0.574 l
+53.269 5.071 l
+53.917 5.071 l
+h
+58.326 -0.647 m
+57.826 -0.647 57.444 -0.5 57.18 -0.206 c
+56.915 0.087 56.783 0.521 56.783 1.102 c
+56.783 1.572 l
+56.783 2.167 56.908 2.634 57.165 2.969 c
+57.429 3.31 57.79 3.484 58.252 3.484 c
+58.713 3.484 59.054 3.329 59.282 3.027 c
+59.517 2.734 59.638 2.271 59.65 1.646 c
+59.65 1.219 l
+57.429 1.219 l
+57.429 1.132 l
+57.429 0.698 57.506 0.386 57.664 0.191 c
+57.83 0.003 58.062 -0.088 58.356 -0.088 c
+58.551 -0.088 58.723 -0.056 58.87 0.014 c
+59.017 0.091 59.153 0.209 59.282 0.367 c
+59.619 -0.044 l
+59.333 -0.449 58.904 -0.647 58.326 -0.647 c
+58.252 2.925 m
+57.977 2.925 57.775 2.829 57.65 2.645 c
+57.522 2.458 57.448 2.167 57.429 1.778 c
+59.002 1.778 l
+59.002 1.866 l
+58.981 2.248 58.914 2.516 58.796 2.675 c
+58.679 2.84 58.495 2.925 58.252 2.925 c
+62.368 0.44 m
+62.368 0.588 62.314 0.708 62.206 0.808 c
+62.096 0.903 61.89 1.021 61.589 1.161 c
+61.244 1.308 61.001 1.429 60.855 1.529 c
+60.708 1.635 60.597 1.753 60.531 1.881 c
+60.461 2.006 60.428 2.164 60.428 2.352 c
+60.428 2.675 60.546 2.943 60.781 3.16 c
+61.016 3.373 61.317 3.484 61.692 3.484 c
+62.075 3.484 62.383 3.37 62.618 3.145 c
+62.854 2.917 62.971 2.63 62.971 2.278 c
+62.324 2.278 l
+62.324 2.454 62.266 2.605 62.148 2.734 c
+62.031 2.859 61.876 2.925 61.692 2.925 c
+61.494 2.925 61.343 2.869 61.237 2.763 c
+61.126 2.664 61.075 2.532 61.075 2.366 c
+61.075 2.237 61.111 2.131 61.192 2.042 c
+61.269 1.961 61.46 1.859 61.766 1.734 c
+62.243 1.547 62.574 1.359 62.75 1.176 c
+62.927 0.999 63.016 0.771 63.016 0.5 c
+63.016 0.147 62.89 -0.133 62.648 -0.339 c
+62.412 -0.545 62.096 -0.647 61.707 -0.647 c
+61.284 -0.647 60.946 -0.53 60.693 -0.294 c
+60.436 -0.052 60.311 0.253 60.311 0.617 c
+60.957 0.617 l
+60.965 0.389 61.034 0.213 61.163 0.087 c
+61.288 -0.03 61.472 -0.088 61.707 -0.088 c
+61.92 -0.088 62.082 -0.04 62.192 0.058 c
+62.31 0.154 62.368 0.282 62.368 0.44 c
+66.278 -0.574 -0.646 3.984 re
+66.322 4.453 m
+66.322 4.343 66.293 4.251 66.235 4.174 c
+66.175 4.104 66.08 4.072 65.955 4.072 c
+65.838 4.072 65.742 4.104 65.676 4.174 c
+65.617 4.251 65.587 4.343 65.587 4.453 c
+65.587 4.571 65.617 4.663 65.676 4.733 c
+65.742 4.81 65.838 4.85 65.955 4.85 c
+66.08 4.85 66.175 4.81 66.235 4.733 c
+66.293 4.652 66.322 4.56 66.322 4.453 c
+67.91 3.41 m
+67.924 2.969 l
+68.178 3.31 68.502 3.484 68.895 3.484 c
+69.601 3.484 69.957 3.013 69.967 2.072 c
+69.967 -0.574 l
+69.321 -0.574 l
+69.321 2.042 l
+69.321 2.355 69.266 2.576 69.159 2.705 c
+69.049 2.829 68.895 2.896 68.689 2.896 c
+68.531 2.896 68.384 2.84 68.248 2.734 c
+68.12 2.624 68.016 2.487 67.939 2.322 c
+67.939 -0.574 l
+67.293 -0.574 l
+67.293 3.41 l
+h
+74.304 2.792 m
+74.216 2.811 74.116 2.822 74.01 2.822 c
+73.676 2.822 73.441 2.638 73.304 2.278 c
+73.304 -0.574 l
+72.658 -0.574 l
+72.658 3.41 l
+73.29 3.41 l
+73.304 2.998 l
+73.481 3.322 73.724 3.484 74.039 3.484 c
+74.146 3.484 74.235 3.461 74.304 3.424 c
+h
+76.303 -0.647 m
+75.803 -0.647 75.421 -0.5 75.157 -0.206 c
+74.892 0.087 74.76 0.521 74.76 1.102 c
+74.76 1.572 l
+74.76 2.167 74.885 2.634 75.142 2.969 c
+75.407 3.31 75.766 3.484 76.23 3.484 c
+76.689 3.484 77.031 3.329 77.258 3.027 c
+77.493 2.734 77.615 2.271 77.626 1.646 c
+77.626 1.219 l
+75.407 1.219 l
+75.407 1.132 l
+75.407 0.698 75.484 0.386 75.642 0.191 c
+75.807 0.003 76.038 -0.088 76.333 -0.088 c
+76.527 -0.088 76.7 -0.056 76.847 0.014 c
+76.994 0.091 77.13 0.209 77.258 0.367 c
+77.597 -0.044 l
+77.31 -0.449 76.88 -0.647 76.303 -0.647 c
+76.23 2.925 m
+75.954 2.925 75.752 2.829 75.627 2.645 c
+75.498 2.458 75.425 2.167 75.407 1.778 c
+76.979 1.778 l
+76.979 1.866 l
+76.957 2.248 76.891 2.516 76.774 2.675 c
+76.656 2.84 76.472 2.925 76.23 2.925 c
+81.286 1.219 m
+81.286 0.591 81.169 0.12 80.934 -0.192 c
+80.705 -0.497 80.389 -0.647 79.978 -0.647 c
+79.573 -0.647 79.265 -0.497 79.052 -0.192 c
+79.052 -2.103 l
+78.405 -2.103 l
+78.405 3.41 l
+78.993 3.41 l
+79.037 2.969 l
+79.251 3.31 79.559 3.484 79.963 3.484 c
+80.404 3.484 80.731 3.329 80.948 3.027 c
+81.161 2.722 81.275 2.267 81.286 1.66 c
+h
+80.639 1.602 m
+80.639 2.042 80.57 2.366 80.433 2.572 c
+80.294 2.785 80.074 2.896 79.772 2.896 c
+79.456 2.896 79.217 2.741 79.052 2.439 c
+79.052 0.367 l
+79.217 0.062 79.456 -0.088 79.772 -0.088 c
+80.066 -0.088 80.279 0.014 80.419 0.22 c
+80.555 0.433 80.628 0.764 80.639 1.205 c
+h
+81.992 1.602 m
+81.992 2.179 82.128 2.634 82.403 2.969 c
+82.686 3.31 83.057 3.484 83.521 3.484 c
+83.98 3.484 84.348 3.314 84.622 2.983 c
+84.906 2.66 85.053 2.212 85.064 1.646 c
+85.064 1.219 l
+85.064 0.65 84.921 0.195 84.637 -0.148 c
+84.362 -0.482 83.994 -0.647 83.535 -0.647 c
+83.072 -0.647 82.701 -0.485 82.418 -0.162 c
+82.143 0.168 81.999 0.61 81.992 1.161 c
+h
+82.638 1.219 m
+82.638 0.816 82.715 0.5 82.873 0.264 c
+83.039 0.029 83.259 -0.088 83.535 -0.088 c
+84.101 -0.088 84.395 0.323 84.417 1.146 c
+84.417 1.602 l
+84.417 2.002 84.333 2.322 84.167 2.557 c
+84.009 2.8 83.793 2.925 83.521 2.925 c
+83.255 2.925 83.039 2.8 82.873 2.557 c
+82.715 2.322 82.638 2.002 82.638 1.602 c
+h
+87.842 0.44 m
+87.842 0.588 87.787 0.708 87.68 0.808 c
+87.57 0.903 87.365 1.021 87.063 1.161 c
+86.717 1.308 86.475 1.429 86.328 1.529 c
+86.181 1.635 86.071 1.753 86.004 1.881 c
+85.935 2.006 85.902 2.164 85.902 2.352 c
+85.902 2.675 86.019 2.943 86.255 3.16 c
+86.49 3.373 86.791 3.484 87.166 3.484 c
+87.548 3.484 87.857 3.37 88.092 3.145 c
+88.327 2.917 88.445 2.63 88.445 2.278 c
+87.797 2.278 l
+87.797 2.454 87.739 2.605 87.621 2.734 c
+87.504 2.859 87.35 2.925 87.166 2.925 c
+86.968 2.925 86.816 2.869 86.71 2.763 c
+86.6 2.664 86.548 2.532 86.548 2.366 c
+86.548 2.237 86.585 2.131 86.666 2.042 c
+86.743 1.961 86.935 1.859 87.24 1.734 c
+87.717 1.547 88.048 1.359 88.224 1.176 c
+88.4 0.999 88.489 0.771 88.489 0.5 c
+88.489 0.147 88.364 -0.133 88.121 -0.339 c
+87.886 -0.545 87.57 -0.647 87.18 -0.647 c
+86.758 -0.647 86.42 -0.53 86.166 -0.294 c
+85.909 -0.052 85.784 0.253 85.784 0.617 c
+86.43 0.617 l
+86.438 0.389 86.508 0.213 86.636 0.087 c
+86.762 -0.03 86.945 -0.088 87.18 -0.088 c
+87.394 -0.088 87.556 -0.04 87.666 0.058 c
+87.783 0.154 87.842 0.282 87.842 0.44 c
+90.032 -0.574 -0.646 3.984 re
+90.076 4.453 m
+90.076 4.343 90.047 4.251 89.988 4.174 c
+89.929 4.104 89.834 4.072 89.709 4.072 c
+89.591 4.072 89.495 4.104 89.43 4.174 c
+89.37 4.251 89.341 4.343 89.341 4.453 c
+89.341 4.571 89.37 4.663 89.43 4.733 c
+89.495 4.81 89.591 4.85 89.709 4.85 c
+89.834 4.85 89.929 4.81 89.988 4.733 c
+90.047 4.652 90.076 4.56 90.076 4.453 c
+91.899 4.365 m
+91.899 3.41 l
+92.501 3.41 l
+92.501 2.881 l
+91.899 2.881 l
+91.899 0.411 l
+91.899 0.253 91.921 0.135 91.972 0.058 c
+92.031 -0.023 92.119 -0.059 92.237 -0.059 c
+92.325 -0.059 92.414 -0.044 92.501 -0.015 c
+92.501 -0.574 l
+92.354 -0.622 92.2 -0.647 92.046 -0.647 c
+91.789 -0.647 91.594 -0.555 91.458 -0.368 c
+91.319 -0.184 91.252 0.077 91.252 0.411 c
+91.252 2.881 l
+90.65 2.881 l
+90.65 3.41 l
+91.252 3.41 l
+91.252 4.365 l
+h
+93.06 1.602 m
+93.06 2.179 93.196 2.634 93.472 2.969 c
+93.755 3.31 94.126 3.484 94.589 3.484 c
+95.048 3.484 95.415 3.314 95.691 2.983 c
+95.974 2.66 96.121 2.212 96.132 1.646 c
+96.132 1.219 l
+96.132 0.65 95.988 0.195 95.706 -0.148 c
+95.43 -0.482 95.063 -0.647 94.604 -0.647 c
+94.141 -0.647 93.769 -0.485 93.486 -0.162 c
+93.21 0.168 93.067 0.61 93.06 1.161 c
+h
+93.707 1.219 m
+93.707 0.816 93.784 0.5 93.942 0.264 c
+94.107 0.029 94.328 -0.088 94.604 -0.088 c
+95.169 -0.088 95.463 0.323 95.485 1.146 c
+95.485 1.602 l
+95.485 2.002 95.401 2.322 95.236 2.557 c
+95.078 2.8 94.86 2.925 94.589 2.925 c
+94.324 2.925 94.107 2.8 93.942 2.557 c
+93.784 2.322 93.707 2.002 93.707 1.602 c
+h
+98.616 2.792 m
+98.528 2.811 98.429 2.822 98.322 2.822 c
+97.988 2.822 97.753 2.638 97.617 2.278 c
+97.617 -0.574 l
+96.97 -0.574 l
+96.97 3.41 l
+97.602 3.41 l
+97.617 2.998 l
+97.793 3.322 98.035 3.484 98.352 3.484 c
+98.459 3.484 98.546 3.461 98.616 3.424 c
+h
+100.424 0.514 m
+101.145 3.41 l
+101.835 3.41 l
+100.542 -1.133 l
+100.443 -1.474 100.3 -1.735 100.115 -1.912 c
+99.94 -2.087 99.737 -2.176 99.513 -2.176 c
+99.425 -2.176 99.311 -2.154 99.175 -2.117 c
+99.175 -1.573 l
+99.321 -1.588 l
+99.506 -1.588 99.653 -1.544 99.763 -1.455 c
+99.87 -1.368 99.957 -1.21 100.027 -0.985 c
+100.145 -0.545 l
+98.984 3.41 l
+99.689 3.41 l
+h
+102.114 -0.221 m
+102.114 -0.103 102.147 -0.008 102.218 0.073 c
+102.284 0.151 102.386 0.191 102.527 0.191 c
+102.673 0.191 102.78 0.151 102.849 0.073 c
+102.927 -0.008 102.967 -0.103 102.967 -0.221 c
+102.967 -0.331 102.927 -0.423 102.849 -0.5 c
+102.78 -0.578 102.673 -0.618 102.527 -0.618 c
+102.386 -0.618 102.284 -0.578 102.218 -0.5 c
+102.147 -0.423 102.114 -0.331 102.114 -0.221 c
+106.892 1.117 m
+106.965 0.514 l
+107.068 1.043 l
+107.921 4.777 l
+108.48 4.777 l
+109.317 1.043 l
+109.42 0.5 l
+109.494 1.117 l
+110.155 4.777 l
+110.831 4.777 l
+109.758 -0.574 l
+109.156 -0.574 l
+108.258 3.322 l
+108.2 3.645 l
+108.156 3.322 l
+107.23 -0.574 l
+106.612 -0.574 l
+105.554 4.777 l
+106.23 4.777 l
+h
+112.213 -0.574 -0.647 3.984 re
+112.257 4.453 m
+112.257 4.343 112.227 4.251 112.169 4.174 c
+112.11 4.104 112.015 4.072 111.89 4.072 c
+111.772 4.072 111.676 4.104 111.61 4.174 c
+111.551 4.251 111.522 4.343 111.522 4.453 c
+111.522 4.571 111.551 4.663 111.61 4.733 c
+111.676 4.81 111.772 4.85 111.89 4.85 c
+112.015 4.85 112.11 4.81 112.169 4.733 c
+112.227 4.652 112.257 4.56 112.257 4.453 c
+114.08 4.365 m
+114.08 3.41 l
+114.683 3.41 l
+114.683 2.881 l
+114.08 2.881 l
+114.08 0.411 l
+114.08 0.253 114.101 0.135 114.153 0.058 c
+114.212 -0.023 114.3 -0.059 114.417 -0.059 c
+114.506 -0.059 114.594 -0.044 114.683 -0.015 c
+114.683 -0.574 l
+114.535 -0.622 114.381 -0.647 114.226 -0.647 c
+113.97 -0.647 113.775 -0.555 113.638 -0.368 c
+113.499 -0.184 113.432 0.077 113.432 0.411 c
+113.432 2.881 l
+112.83 2.881 l
+112.83 3.41 l
+113.432 3.41 l
+113.432 4.365 l
+h
+116.094 2.998 m
+116.347 3.322 116.667 3.484 117.049 3.484 c
+117.754 3.484 118.111 3.013 118.122 2.072 c
+118.122 -0.574 l
+117.475 -0.574 l
+117.475 2.042 l
+117.475 2.355 117.42 2.576 117.313 2.705 c
+117.203 2.829 117.049 2.896 116.843 2.896 c
+116.685 2.896 116.538 2.84 116.402 2.734 c
+116.274 2.624 116.171 2.487 116.094 2.322 c
+116.094 -0.574 l
+115.447 -0.574 l
+115.447 5.071 l
+116.094 5.071 l
+h
+f
+Q
+664.516 231.572 -1.794 0.866 re
+667.264 229.704 m
+667.234 229.764 667.205 229.866 667.176 230.013 c
+666.989 229.756 666.739 229.631 666.426 229.631 c
+666.091 229.631 665.813 229.737 665.588 229.955 c
+665.372 230.178 665.264 230.469 665.264 230.822 c
+665.264 231.233 665.397 231.549 665.661 231.777 c
+665.927 232.012 666.309 232.13 666.808 232.13 c
+667.132 232.13 l
+667.132 232.453 l
+667.132 232.63 667.095 232.75 667.028 232.821 c
+666.97 232.898 666.882 232.939 666.764 232.939 c
+666.507 232.939 666.382 232.784 666.382 232.482 c
+665.339 232.482 l
+665.339 232.854 665.474 233.159 665.75 233.394 c
+666.022 233.637 666.371 233.762 666.793 233.762 c
+667.234 233.762 667.573 233.644 667.808 233.409 c
+668.05 233.18 668.175 232.858 668.175 232.438 c
+668.175 230.572 l
+668.175 230.226 668.223 229.958 668.322 229.764 c
+668.322 229.704 l
+h
+666.661 230.454 m
+666.768 230.454 666.86 230.473 666.941 230.513 c
+667.028 230.56 667.091 230.62 667.132 230.689 c
+667.132 231.512 l
+666.882 231.512 l
+666.706 231.512 666.562 231.454 666.455 231.336 c
+666.357 231.225 666.309 231.079 666.309 230.895 c
+666.309 230.601 666.426 230.454 666.661 230.454 c
+f
+q 1 0 0 1 669.0717 230.0571 cm
+0 0 m
+0 0.118 0.034 0.214 0.104 0.294 c
+0.169 0.372 0.272 0.412 0.412 0.412 c
+0.559 0.412 0.665 0.372 0.736 0.294 c
+0.813 0.214 0.853 0.118 0.853 0 c
+0.853 -0.11 0.813 -0.202 0.736 -0.279 c
+0.665 -0.357 0.559 -0.397 0.412 -0.397 c
+0.272 -0.397 0.169 -0.357 0.104 -0.279 c
+0.034 -0.202 0 -0.11 0 0 c
+0 2.822 m
+0 2.94 0.034 3.036 0.104 3.117 c
+0.169 3.194 0.272 3.234 0.412 3.234 c
+0.559 3.234 0.665 3.194 0.736 3.117 c
+0.813 3.036 0.853 2.94 0.853 2.822 c
+0.853 2.712 0.813 2.62 0.736 2.543 c
+0.665 2.462 0.559 2.425 0.412 2.425 c
+0.272 2.425 0.169 2.462 0.104 2.543 c
+0.034 2.62 0 2.712 0 2.822 c
+5.38 0.661 m
+5.38 0.809 5.325 0.929 5.218 1.029 c
+5.108 1.124 4.902 1.242 4.601 1.382 c
+4.256 1.529 4.013 1.65 3.866 1.75 c
+3.719 1.856 3.609 1.974 3.543 2.103 c
+3.473 2.227 3.44 2.385 3.44 2.573 c
+3.44 2.896 3.558 3.164 3.793 3.381 c
+4.028 3.594 4.329 3.705 4.704 3.705 c
+5.087 3.705 5.395 3.591 5.63 3.366 c
+5.866 3.138 5.983 2.851 5.983 2.499 c
+5.336 2.499 l
+5.336 2.675 5.278 2.826 5.16 2.955 c
+5.042 3.08 4.888 3.146 4.704 3.146 c
+4.505 3.146 4.355 3.09 4.248 2.984 c
+4.138 2.885 4.087 2.753 4.087 2.587 c
+4.087 2.458 4.123 2.352 4.204 2.263 c
+4.281 2.183 4.472 2.08 4.777 1.955 c
+5.255 1.768 5.586 1.58 5.762 1.397 c
+5.939 1.22 6.027 0.992 6.027 0.721 c
+6.027 0.368 5.902 0.088 5.66 -0.118 c
+5.424 -0.324 5.108 -0.426 4.719 -0.426 c
+4.296 -0.426 3.958 -0.309 3.705 -0.073 c
+3.447 0.169 3.323 0.474 3.323 0.838 c
+3.969 0.838 l
+3.977 0.611 4.046 0.434 4.175 0.309 c
+4.3 0.191 4.484 0.133 4.719 0.133 c
+4.932 0.133 5.093 0.181 5.204 0.279 c
+5.322 0.375 5.38 0.503 5.38 0.661 c
+7.512 3.219 m
+7.765 3.543 8.085 3.705 8.467 3.705 c
+9.172 3.705 9.529 3.234 9.54 2.294 c
+9.54 -0.353 l
+8.893 -0.353 l
+8.893 2.263 l
+8.893 2.576 8.838 2.797 8.732 2.926 c
+8.621 3.05 8.467 3.117 8.262 3.117 c
+8.104 3.117 7.956 3.061 7.82 2.955 c
+7.691 2.845 7.589 2.708 7.512 2.543 c
+7.512 -0.353 l
+6.865 -0.353 l
+6.865 5.292 l
+7.512 5.292 l
+h
+10.363 1.823 m
+10.363 2.4 10.499 2.855 10.774 3.19 c
+11.057 3.532 11.429 3.705 11.892 3.705 c
+12.351 3.705 12.719 3.535 12.994 3.204 c
+13.278 2.881 13.424 2.433 13.436 1.867 c
+13.436 1.44 l
+13.436 0.871 13.292 0.416 13.009 0.073 c
+12.734 -0.261 12.366 -0.426 11.907 -0.426 c
+11.443 -0.426 11.073 -0.264 10.789 0.059 c
+10.514 0.389 10.371 0.831 10.363 1.382 c
+h
+11.009 1.44 m
+11.009 1.037 11.088 0.721 11.245 0.485 c
+11.41 0.25 11.631 0.133 11.907 0.133 c
+12.472 0.133 12.767 0.544 12.788 1.367 c
+12.788 1.823 l
+12.788 2.223 12.704 2.543 12.538 2.778 c
+12.38 3.021 12.164 3.146 11.892 3.146 c
+11.628 3.146 11.41 3.021 11.245 2.778 c
+11.088 2.543 11.009 2.223 11.009 1.823 c
+h
+17.242 0.794 m
+17.845 3.631 l
+18.492 3.631 l
+17.507 -0.353 l
+16.993 -0.353 l
+16.214 2.499 l
+15.464 -0.353 l
+14.934 -0.353 l
+13.979 3.631 l
+14.612 3.631 l
+15.229 0.867 l
+15.963 3.631 l
+16.478 3.631 l
+h
+22.93 -0.353 m
+22.89 -0.264 22.865 -0.118 22.857 0.088 c
+22.622 -0.257 22.328 -0.426 21.976 -0.426 c
+21.612 -0.426 21.328 -0.33 21.122 -0.132 c
+20.925 0.073 20.829 0.36 20.829 0.735 c
+20.829 1.135 20.965 1.455 21.24 1.691 c
+21.513 1.933 21.887 2.058 22.358 2.058 c
+22.843 2.058 l
+22.843 2.484 l
+22.843 2.72 22.788 2.885 22.681 2.984 c
+22.571 3.09 22.409 3.146 22.196 3.146 c
+21.997 3.146 21.836 3.087 21.71 2.969 c
+21.593 2.851 21.534 2.705 21.534 2.529 c
+20.887 2.529 l
+20.887 2.723 20.947 2.914 21.064 3.102 c
+21.189 3.285 21.351 3.433 21.55 3.543 c
+21.755 3.649 21.983 3.705 22.24 3.705 c
+22.641 3.705 22.946 3.601 23.152 3.396 c
+23.364 3.19 23.478 2.896 23.489 2.514 c
+23.489 0.5 l
+23.489 0.195 23.526 -0.07 23.607 -0.294 c
+23.607 -0.353 l
+h
+22.063 0.162 m
+22.229 0.162 22.379 0.206 22.519 0.294 c
+22.666 0.382 22.772 0.493 22.843 0.632 c
+22.843 1.573 l
+22.475 1.573 l
+22.159 1.573 21.916 1.503 21.741 1.367 c
+21.564 1.239 21.475 1.051 21.475 0.809 c
+21.475 0.58 21.519 0.416 21.608 0.309 c
+21.696 0.21 21.847 0.162 22.063 0.162 c
+25.195 -0.353 -0.647 5.644 re
+26.915 -0.353 -0.647 5.644 re
+32.529 1.44 m
+32.529 0.823 32.415 0.357 32.191 0.044 c
+31.975 -0.272 31.651 -0.426 31.221 -0.426 c
+30.799 -0.426 30.487 -0.246 30.281 0.118 c
+30.251 -0.353 l
+29.648 -0.353 l
+29.648 5.292 l
+30.295 5.292 l
+30.295 3.19 l
+30.508 3.532 30.817 3.705 31.221 3.705 c
+31.651 3.705 31.975 3.547 32.191 3.234 c
+32.415 2.929 32.529 2.462 32.529 1.837 c
+h
+31.883 1.823 m
+31.883 2.294 31.813 2.624 31.677 2.822 c
+31.549 3.017 31.339 3.117 31.044 3.117 c
+30.71 3.117 30.46 2.932 30.295 2.573 c
+30.295 0.691 l
+30.46 0.327 30.714 0.147 31.06 0.147 c
+31.353 0.147 31.563 0.25 31.692 0.456 c
+31.817 0.661 31.883 0.977 31.883 1.411 c
+h
+35.013 3.013 m
+34.925 3.032 34.826 3.043 34.72 3.043 c
+34.385 3.043 34.15 2.859 34.014 2.499 c
+34.014 -0.353 l
+33.367 -0.353 l
+33.367 3.631 l
+33.999 3.631 l
+34.014 3.219 l
+34.19 3.543 34.433 3.705 34.749 3.705 c
+34.855 3.705 34.944 3.682 35.013 3.645 c
+h
+37.556 -0.353 m
+37.516 -0.264 37.49 -0.118 37.483 0.088 c
+37.248 -0.257 36.954 -0.426 36.601 -0.426 c
+36.237 -0.426 35.954 -0.33 35.748 -0.132 c
+35.55 0.073 35.455 0.36 35.455 0.735 c
+35.455 1.135 35.59 1.455 35.866 1.691 c
+36.138 1.933 36.513 2.058 36.983 2.058 c
+37.468 2.058 l
+37.468 2.484 l
+37.468 2.72 37.413 2.885 37.307 2.984 c
+37.196 3.09 37.035 3.146 36.822 3.146 c
+36.623 3.146 36.461 3.087 36.336 2.969 c
+36.218 2.851 36.16 2.705 36.16 2.529 c
+35.513 2.529 l
+35.513 2.723 35.572 2.914 35.69 3.102 c
+35.815 3.285 35.977 3.433 36.174 3.543 c
+36.38 3.649 36.608 3.705 36.866 3.705 c
+37.266 3.705 37.571 3.601 37.777 3.396 c
+37.99 3.19 38.104 2.896 38.115 2.514 c
+38.115 0.5 l
+38.115 0.195 38.152 -0.07 38.233 -0.294 c
+38.233 -0.353 l
+h
+36.689 0.162 m
+36.854 0.162 37.005 0.206 37.145 0.294 c
+37.292 0.382 37.398 0.493 37.468 0.632 c
+37.468 1.573 l
+37.101 1.573 l
+36.785 1.573 36.542 1.503 36.366 1.367 c
+36.189 1.239 36.101 1.051 36.101 0.809 c
+36.101 0.58 36.145 0.416 36.234 0.309 c
+36.322 0.21 36.472 0.162 36.689 0.162 c
+39.732 3.631 m
+39.746 3.19 l
+40 3.532 40.324 3.705 40.717 3.705 c
+41.423 3.705 41.778 3.234 41.789 2.294 c
+41.789 -0.353 l
+41.143 -0.353 l
+41.143 2.263 l
+41.143 2.576 41.088 2.797 40.981 2.926 c
+40.871 3.05 40.717 3.117 40.511 3.117 c
+40.353 3.117 40.206 3.061 40.07 2.955 c
+39.941 2.845 39.838 2.708 39.761 2.543 c
+39.761 -0.353 l
+39.114 -0.353 l
+39.114 3.631 l
+h
+44.141 0.133 m
+44.355 0.133 44.527 0.195 44.656 0.324 c
+44.792 0.459 44.866 0.651 44.876 0.897 c
+45.494 0.897 l
+45.472 0.515 45.336 0.195 45.082 -0.058 c
+44.825 -0.305 44.513 -0.426 44.141 -0.426 c
+43.649 -0.426 43.274 -0.276 43.01 0.029 c
+42.753 0.341 42.628 0.809 42.628 1.426 c
+42.628 1.867 l
+42.628 2.462 42.753 2.918 43.01 3.234 c
+43.274 3.547 43.649 3.705 44.141 3.705 c
+44.542 3.705 44.862 3.572 45.097 3.308 c
+45.34 3.05 45.472 2.705 45.494 2.263 c
+44.876 2.263 l
+44.854 2.558 44.781 2.778 44.656 2.926 c
+44.538 3.072 44.365 3.146 44.141 3.146 c
+43.848 3.146 43.631 3.046 43.495 2.851 c
+43.355 2.664 43.281 2.356 43.274 1.926 c
+43.274 1.411 l
+43.274 0.941 43.341 0.607 43.48 0.412 c
+43.627 0.224 43.848 0.133 44.141 0.133 c
+46.89 3.219 m
+47.144 3.543 47.463 3.705 47.846 3.705 c
+48.552 3.705 48.908 3.234 48.918 2.294 c
+48.918 -0.353 l
+48.272 -0.353 l
+48.272 2.263 l
+48.272 2.576 48.217 2.797 48.11 2.926 c
+48 3.05 47.846 3.117 47.64 3.117 c
+47.482 3.117 47.335 3.061 47.199 2.955 c
+47.071 2.845 46.967 2.708 46.89 2.543 c
+46.89 -0.353 l
+46.244 -0.353 l
+46.244 5.292 l
+46.89 5.292 l
+h
+51.3 -0.426 m
+50.8 -0.426 50.418 -0.279 50.154 0.015 c
+49.889 0.309 49.757 0.742 49.757 1.323 c
+49.757 1.793 l
+49.757 2.389 49.882 2.855 50.139 3.19 c
+50.403 3.532 50.763 3.705 51.226 3.705 c
+51.686 3.705 52.028 3.55 52.255 3.248 c
+52.49 2.955 52.612 2.492 52.623 1.867 c
+52.623 1.44 l
+50.403 1.44 l
+50.403 1.353 l
+50.403 0.919 50.48 0.607 50.638 0.412 c
+50.804 0.224 51.035 0.133 51.33 0.133 c
+51.524 0.133 51.696 0.166 51.844 0.235 c
+51.991 0.312 52.126 0.43 52.255 0.588 c
+52.594 0.177 l
+52.307 -0.228 51.877 -0.426 51.3 -0.426 c
+51.226 3.146 m
+50.95 3.146 50.749 3.05 50.624 2.866 c
+50.495 2.679 50.422 2.389 50.403 1.999 c
+51.976 1.999 l
+51.976 2.087 l
+51.954 2.469 51.888 2.738 51.771 2.896 c
+51.653 3.061 51.469 3.146 51.226 3.146 c
+55.342 0.661 m
+55.342 0.809 55.287 0.929 55.181 1.029 c
+55.07 1.124 54.865 1.242 54.563 1.382 c
+54.218 1.529 53.975 1.65 53.828 1.75 c
+53.681 1.856 53.571 1.974 53.505 2.103 c
+53.435 2.227 53.402 2.385 53.402 2.573 c
+53.402 2.896 53.52 3.164 53.755 3.381 c
+53.99 3.594 54.291 3.705 54.666 3.705 c
+55.048 3.705 55.357 3.591 55.592 3.366 c
+55.827 3.138 55.945 2.851 55.945 2.499 c
+55.299 2.499 l
+55.299 2.675 55.239 2.826 55.122 2.955 c
+55.004 3.08 54.85 3.146 54.666 3.146 c
+54.468 3.146 54.317 3.09 54.21 2.984 c
+54.1 2.885 54.048 2.753 54.048 2.587 c
+54.048 2.458 54.086 2.352 54.166 2.263 c
+54.243 2.183 54.435 2.08 54.74 1.955 c
+55.218 1.768 55.548 1.58 55.725 1.397 c
+55.901 1.22 55.989 0.992 55.989 0.721 c
+55.989 0.368 55.864 0.088 55.621 -0.118 c
+55.386 -0.324 55.07 -0.426 54.68 -0.426 c
+54.258 -0.426 53.92 -0.309 53.666 -0.073 c
+53.41 0.169 53.284 0.474 53.284 0.838 c
+53.931 0.838 l
+53.938 0.611 54.008 0.434 54.137 0.309 c
+54.262 0.191 54.445 0.133 54.68 0.133 c
+54.894 0.133 55.056 0.181 55.166 0.279 c
+55.284 0.375 55.342 0.503 55.342 0.661 c
+58.532 1.823 m
+58.532 2.458 58.62 3.061 58.796 3.631 c
+58.973 4.197 59.216 4.692 59.532 5.116 c
+59.727 5.38 59.914 5.571 60.09 5.689 c
+60.222 5.233 l
+59.928 4.958 59.686 4.534 59.502 3.969 c
+59.315 3.399 59.212 2.768 59.193 2.072 c
+59.193 1.779 l
+59.193 0.915 59.311 0.151 59.546 -0.515 c
+59.73 -1.014 59.958 -1.396 60.222 -1.66 c
+60.09 -2.088 l
+59.862 -1.929 59.638 -1.679 59.414 -1.338 c
+58.826 -0.455 58.532 0.595 58.532 1.823 c
+63.883 0.794 m
+64.485 3.631 l
+65.132 3.631 l
+64.147 -0.353 l
+63.633 -0.353 l
+62.854 2.499 l
+62.104 -0.353 l
+61.574 -0.353 l
+60.619 3.631 l
+61.252 3.631 l
+61.869 0.867 l
+62.603 3.631 l
+63.118 3.631 l
+h
+66.513 -0.353 -0.646 3.984 re
+66.557 4.675 m
+66.557 4.564 66.528 4.472 66.47 4.395 c
+66.411 4.326 66.316 4.293 66.19 4.293 c
+66.073 4.293 65.977 4.326 65.911 4.395 c
+65.852 4.472 65.823 4.564 65.823 4.675 c
+65.823 4.792 65.852 4.884 65.911 4.954 c
+65.977 5.031 66.073 5.072 66.19 5.072 c
+66.316 5.072 66.411 5.031 66.47 4.954 c
+66.528 4.873 66.557 4.781 66.557 4.675 c
+68.38 4.586 m
+68.38 3.631 l
+68.983 3.631 l
+68.983 3.102 l
+68.38 3.102 l
+68.38 0.632 l
+68.38 0.474 68.402 0.357 68.454 0.279 c
+68.512 0.198 68.601 0.162 68.718 0.162 c
+68.807 0.162 68.895 0.177 68.983 0.206 c
+68.983 -0.353 l
+68.836 -0.401 68.681 -0.426 68.527 -0.426 c
+68.27 -0.426 68.076 -0.334 67.939 -0.147 c
+67.8 0.037 67.733 0.298 67.733 0.632 c
+67.733 3.102 l
+67.131 3.102 l
+67.131 3.631 l
+67.733 3.631 l
+67.733 4.586 l
+h
+70.394 3.219 m
+70.648 3.543 70.968 3.705 71.349 3.705 c
+72.055 3.705 72.412 3.234 72.423 2.294 c
+72.423 -0.353 l
+71.776 -0.353 l
+71.776 2.263 l
+71.776 2.576 71.721 2.797 71.614 2.926 c
+71.504 3.05 71.349 3.117 71.143 3.117 c
+70.985 3.117 70.839 3.061 70.703 2.955 c
+70.574 2.845 70.471 2.708 70.394 2.543 c
+70.394 -0.353 l
+69.747 -0.353 l
+69.747 5.292 l
+70.394 5.292 l
+h
+76.744 3.013 m
+76.656 3.032 76.556 3.043 76.45 3.043 c
+76.115 3.043 75.88 2.859 75.745 2.499 c
+75.745 -0.353 l
+75.097 -0.353 l
+75.097 3.631 l
+75.729 3.631 l
+75.745 3.219 l
+75.92 3.543 76.163 3.705 76.479 3.705 c
+76.586 3.705 76.674 3.682 76.744 3.645 c
+h
+78.743 -0.426 m
+78.243 -0.426 77.861 -0.279 77.597 0.015 c
+77.332 0.309 77.2 0.742 77.2 1.323 c
+77.2 1.793 l
+77.2 2.389 77.325 2.855 77.582 3.19 c
+77.846 3.532 78.206 3.705 78.669 3.705 c
+79.129 3.705 79.471 3.55 79.698 3.248 c
+79.934 2.955 80.055 2.492 80.066 1.867 c
+80.066 1.44 l
+77.846 1.44 l
+77.846 1.353 l
+77.846 0.919 77.923 0.607 78.081 0.412 c
+78.247 0.224 78.478 0.133 78.773 0.133 c
+78.968 0.133 79.14 0.166 79.287 0.235 c
+79.434 0.312 79.57 0.43 79.698 0.588 c
+80.036 0.177 l
+79.75 -0.228 79.32 -0.426 78.743 -0.426 c
+78.669 3.146 m
+78.394 3.146 78.192 3.05 78.067 2.866 c
+77.938 2.679 77.865 2.389 77.846 1.999 c
+79.419 1.999 l
+79.419 2.087 l
+79.397 2.469 79.331 2.738 79.213 2.896 c
+79.095 3.061 78.912 3.146 78.669 3.146 c
+81.462 3.631 m
+81.477 3.263 l
+81.72 3.557 82.039 3.705 82.432 3.705 c
+82.873 3.705 83.182 3.506 83.359 3.117 c
+83.612 3.506 83.961 3.705 84.402 3.705 c
+85.137 3.705 85.512 3.241 85.534 2.323 c
+85.534 -0.353 l
+84.888 -0.353 l
+84.888 2.263 l
+84.888 2.558 84.832 2.77 84.726 2.911 c
+84.626 3.046 84.454 3.117 84.211 3.117 c
+84.013 3.117 83.851 3.036 83.726 2.881 c
+83.608 2.734 83.539 2.543 83.521 2.308 c
+83.521 -0.353 l
+82.858 -0.353 l
+82.858 2.294 l
+82.858 2.841 82.638 3.117 82.197 3.117 c
+81.863 3.117 81.628 2.955 81.492 2.631 c
+81.492 -0.353 l
+80.845 -0.353 l
+80.845 3.631 l
+h
+86.372 1.823 m
+86.372 2.4 86.508 2.855 86.783 3.19 c
+87.066 3.532 87.438 3.705 87.901 3.705 c
+88.36 3.705 88.728 3.535 89.003 3.204 c
+89.286 2.881 89.433 2.433 89.444 1.867 c
+89.444 1.44 l
+89.444 0.871 89.301 0.416 89.017 0.073 c
+88.742 -0.261 88.375 -0.426 87.915 -0.426 c
+87.452 -0.426 87.081 -0.264 86.798 0.059 c
+86.523 0.389 86.38 0.831 86.372 1.382 c
+h
+87.018 1.44 m
+87.018 1.037 87.096 0.721 87.254 0.485 c
+87.419 0.25 87.639 0.133 87.915 0.133 c
+88.481 0.133 88.776 0.544 88.797 1.367 c
+88.797 1.823 l
+88.797 2.223 88.713 2.543 88.547 2.778 c
+88.389 3.021 88.173 3.146 87.901 3.146 c
+87.636 3.146 87.419 3.021 87.254 2.778 c
+87.096 2.543 87.018 2.223 87.018 1.823 c
+h
+91.134 4.586 m
+91.134 3.631 l
+91.737 3.631 l
+91.737 3.102 l
+91.134 3.102 l
+91.134 0.632 l
+91.134 0.474 91.157 0.357 91.208 0.279 c
+91.267 0.198 91.355 0.162 91.473 0.162 c
+91.56 0.162 91.649 0.177 91.737 0.206 c
+91.737 -0.353 l
+91.59 -0.401 91.436 -0.426 91.282 -0.426 c
+91.024 -0.426 90.829 -0.334 90.694 -0.147 c
+90.554 0.037 90.488 0.298 90.488 0.632 c
+90.488 3.102 l
+89.885 3.102 l
+89.885 3.631 l
+90.488 3.631 l
+90.488 4.586 l
+h
+93.927 -0.426 m
+93.428 -0.426 93.046 -0.279 92.78 0.015 c
+92.516 0.309 92.383 0.742 92.383 1.323 c
+92.383 1.793 l
+92.383 2.389 92.509 2.855 92.766 3.19 c
+93.031 3.532 93.391 3.705 93.854 3.705 c
+94.313 3.705 94.655 3.55 94.883 3.248 c
+95.118 2.955 95.24 2.492 95.25 1.867 c
+95.25 1.44 l
+93.031 1.44 l
+93.031 1.353 l
+93.031 0.919 93.108 0.607 93.266 0.412 c
+93.431 0.224 93.663 0.133 93.956 0.133 c
+94.151 0.133 94.324 0.166 94.471 0.235 c
+94.618 0.312 94.754 0.43 94.883 0.588 c
+95.221 0.177 l
+94.934 -0.228 94.504 -0.426 93.927 -0.426 c
+93.854 3.146 m
+93.578 3.146 93.376 3.05 93.251 2.866 c
+93.123 2.679 93.049 2.389 93.031 1.999 c
+94.604 1.999 l
+94.604 2.087 l
+94.581 2.469 94.515 2.738 94.398 2.896 c
+94.28 3.061 94.097 3.146 93.854 3.146 c
+97.426 1.779 m
+97.426 0.691 97.179 -0.276 96.691 -1.117 c
+96.426 -1.565 96.15 -1.889 95.868 -2.088 c
+95.75 -1.66 l
+96.051 -1.367 96.298 -0.914 96.485 -0.309 c
+96.68 0.298 96.779 0.963 96.779 1.691 c
+96.779 1.823 l
+96.779 2.753 96.624 3.587 96.323 4.322 c
+96.154 4.723 95.963 5.042 95.75 5.278 c
+95.868 5.689 l
+96.14 5.501 96.404 5.203 96.661 4.792 c
+97.168 3.94 97.426 2.932 97.426 1.779 c
+98.469 0 m
+98.469 0.118 98.502 0.214 98.573 0.294 c
+98.638 0.372 98.741 0.412 98.881 0.412 c
+99.028 0.412 99.134 0.372 99.204 0.294 c
+99.282 0.214 99.321 0.118 99.321 0 c
+99.321 -0.11 99.282 -0.202 99.204 -0.279 c
+99.134 -0.357 99.028 -0.397 98.881 -0.397 c
+98.741 -0.397 98.638 -0.357 98.573 -0.279 c
+98.502 -0.202 98.469 -0.11 98.469 0 c
+f
+Q
+0.113 0.082 0.09 0 k
+535.667 224.097 238.665 -12.895 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 540.8555 217.2583 cm
+0 0 m
+0 -0.188 -0.029 -0.353 -0.088 -0.5 c
+-0.147 -0.647 -0.242 -0.776 -0.367 -0.882 c
+-0.496 -0.992 -0.658 -1.081 -0.852 -1.147 c
+-1.051 -1.206 -1.278 -1.243 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.264 l
+-2.142 -1.257 -2.358 -1.22 -2.543 -1.162 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.927 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.214 -3.484 -0.015 c
+-2.66 0.132 l
+-2.642 0.033 -2.616 -0.059 -2.587 -0.148 c
+-2.55 -0.228 -2.506 -0.302 -2.454 -0.368 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.675 c
+-1.977 0.683 -1.992 0.691 -1.999 0.691 c
+-2.167 0.727 -2.329 0.771 -2.484 0.823 c
+-2.642 0.881 -2.786 0.959 -2.91 1.058 c
+-3.039 1.153 -3.141 1.278 -3.218 1.425 c
+-3.289 1.573 -3.322 1.756 -3.322 1.984 c
+-3.322 2.179 -3.289 2.344 -3.218 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.836 c
+-2.8 2.932 -2.645 3.002 -2.469 3.042 c
+-2.293 3.09 -2.102 3.119 -1.896 3.131 c
+-1.896 3.557 l
+-1.543 3.557 l
+-1.543 3.131 l
+-1.319 3.119 -1.128 3.09 -0.97 3.042 c
+-0.804 2.991 -0.669 2.917 -0.558 2.822 c
+-0.44 2.734 -0.349 2.62 -0.279 2.484 c
+-0.201 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.252 -1.213 2.458 -1.543 2.499 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.359 -1.469 1.352 c
+-1.294 1.311 -1.117 1.263 -0.941 1.205 c
+-0.756 1.146 -0.595 1.066 -0.455 0.97 c
+-0.319 0.87 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.454 c
+-2.26 2.425 -2.318 2.385 -2.366 2.337 c
+-2.406 2.296 -2.436 2.248 -2.454 2.19 c
+-2.476 2.131 -2.484 2.065 -2.484 1.999 c
+-2.484 1.911 -2.469 1.837 -2.439 1.778 c
+-2.41 1.72 -2.373 1.668 -2.322 1.631 c
+-2.263 1.591 -2.2 1.558 -2.131 1.529 c
+-2.065 1.499 -1.984 1.469 -1.896 1.44 c
+h
+-0.837 -0.015 m
+-0.837 0.091 -0.86 0.176 -0.897 0.235 c
+-0.926 0.301 -0.977 0.357 -1.043 0.397 c
+-1.102 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.529 -1.448 0.558 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.53 -1.028 -0.441 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.015 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.52 -2.484 5.403 -2.396 c
+5.285 -2.315 5.185 -2.22 5.108 -2.103 c
+5.039 -1.985 4.991 -1.856 4.961 -1.72 c
+5.858 -1.617 l
+5.895 -1.754 5.964 -1.86 6.064 -1.941 c
+6.159 -2.029 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.058 6.681 -2.029 c
+6.758 -1.999 6.828 -1.945 6.887 -1.867 c
+6.945 -1.797 6.99 -1.706 7.019 -1.588 c
+7.056 -1.47 7.078 -1.324 7.078 -1.147 c
+7.078 -0.956 l
+7.078 -0.89 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.073 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.214 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.412 4.888 -0.206 c
+4.836 0.008 4.815 0.243 4.815 0.5 c
+4.815 0.771 4.836 1.018 4.888 1.234 c
+4.946 1.448 5.027 1.631 5.137 1.778 c
+5.245 1.932 5.376 2.05 5.534 2.131 c
+5.689 2.219 5.876 2.263 6.093 2.263 c
+6.189 2.263 6.288 2.252 6.387 2.234 c
+6.483 2.212 6.571 2.179 6.652 2.131 c
+6.739 2.08 6.817 2.017 6.887 1.94 c
+6.964 1.859 7.026 1.768 7.078 1.66 c
+7.092 1.66 l
+7.092 1.808 l
+7.1 1.866 7.107 1.918 7.107 1.969 c
+7.115 2.028 7.122 2.076 7.122 2.117 c
+7.13 2.164 7.14 2.198 7.151 2.219 c
+8.004 2.219 l
+7.992 2.138 7.982 2.028 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.162 l
+7.975 -1.415 7.938 -1.636 7.872 -1.823 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.404 7.258 -2.5 7.063 -2.558 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.529 m
+7.092 0.742 7.067 0.918 7.019 1.058 c
+6.978 1.205 6.924 1.323 6.857 1.411 c
+6.799 1.499 6.729 1.558 6.652 1.587 c
+6.571 1.624 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.226 1.62 6.137 1.573 c
+6.056 1.532 5.991 1.462 5.931 1.367 c
+5.88 1.278 5.835 1.161 5.799 1.014 c
+5.77 0.874 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.339 c
+6.049 -0.515 6.211 -0.603 6.417 -0.603 c
+6.483 -0.603 6.556 -0.588 6.637 -0.559 c
+6.725 -0.522 6.799 -0.463 6.857 -0.383 c
+6.924 -0.294 6.978 -0.177 7.019 -0.03 c
+7.067 0.118 7.092 0.301 7.092 0.529 c
+11.036 -0.647 m
+12.168 -0.647 l
+12.168 -1.264 l
+8.86 -1.264 l
+8.86 -0.647 l
+10.124 -0.647 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.219 l
+11.036 2.219 l
+h
+10.124 3.513 0.912 -0.676 re
+10.124 2.836 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.219 l
+13.644 2.219 l
+13.924 3.116 l
+14.498 3.116 l
+14.498 2.219 l
+15.732 2.219 l
+15.732 1.602 l
+14.498 1.602 l
+14.498 -0.103 l
+14.498 -0.324 l
+14.504 -0.393 14.527 -0.456 14.556 -0.515 c
+14.593 -0.566 14.648 -0.611 14.718 -0.647 c
+14.795 -0.676 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.688 15.468 -0.676 c
+15.603 -0.659 15.736 -0.632 15.865 -0.603 c
+15.865 -1.206 l
+15.784 -1.216 15.707 -1.231 15.629 -1.249 c
+15.549 -1.261 15.471 -1.268 15.394 -1.279 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.042 -1.301 14.942 -1.309 14.836 -1.309 c
+14.648 -1.309 14.486 -1.294 14.35 -1.264 c
+14.222 -1.228 14.107 -1.183 14.012 -1.132 c
+13.924 -1.085 13.85 -1.025 13.792 -0.956 c
+13.733 -0.879 13.689 -0.802 13.66 -0.721 c
+13.63 -0.632 13.608 -0.544 13.6 -0.456 c
+13.59 -0.36 13.586 -0.265 13.586 -0.177 c
+h
+21.12 -1.264 m
+21.12 -0.971 l
+21.126 -0.834 21.134 -0.676 21.134 -0.5 c
+21.134 3.513 l
+22.045 3.513 l
+22.045 2.234 l
+22.045 2.072 l
+22.045 1.896 l
+22.045 1.845 22.038 1.801 22.03 1.764 c
+22.03 1.675 l
+22.045 1.675 l
+22.093 1.782 22.155 1.874 22.236 1.955 c
+22.313 2.032 22.398 2.094 22.487 2.146 c
+22.574 2.194 22.666 2.227 22.766 2.248 c
+22.861 2.267 22.961 2.278 23.059 2.278 c
+23.273 2.278 23.46 2.234 23.618 2.146 c
+23.773 2.057 23.901 1.929 24 1.764 c
+24.106 1.606 24.184 1.415 24.235 1.19 c
+24.283 0.974 24.309 0.735 24.309 0.47 c
+24.309 0.213 24.28 -0.026 24.22 -0.25 c
+24.162 -0.467 24.077 -0.659 23.971 -0.823 c
+23.861 -0.981 23.728 -1.103 23.574 -1.191 c
+23.416 -1.279 23.236 -1.324 23.03 -1.324 c
+22.931 -1.324 22.832 -1.312 22.736 -1.294 c
+22.648 -1.272 22.56 -1.243 22.471 -1.191 c
+22.383 -1.132 22.302 -1.066 22.236 -0.985 c
+22.167 -0.908 22.104 -0.809 22.045 -0.691 c
+22.03 -0.691 l
+22.03 -0.853 l
+22.03 -0.912 22.024 -0.971 22.016 -1.029 c
+22.016 -1.081 22.009 -1.129 22.001 -1.176 c
+22.001 -1.216 21.994 -1.246 21.986 -1.264 c
+h
+22.03 0.5 m
+22.03 0.264 22.049 0.066 22.09 -0.088 c
+22.137 -0.246 22.196 -0.368 22.265 -0.456 c
+22.332 -0.544 22.406 -0.611 22.487 -0.647 c
+22.564 -0.688 22.641 -0.706 22.722 -0.706 c
+22.927 -0.706 23.082 -0.611 23.192 -0.412 c
+23.31 -0.217 23.368 0.077 23.368 0.47 c
+23.368 0.683 23.346 0.867 23.31 1.014 c
+23.28 1.168 23.236 1.294 23.177 1.381 c
+23.125 1.477 23.059 1.55 22.971 1.602 c
+22.89 1.65 22.803 1.675 22.707 1.675 c
+22.626 1.675 22.549 1.654 22.471 1.616 c
+22.391 1.576 22.317 1.514 22.251 1.425 c
+22.192 1.338 22.137 1.213 22.09 1.058 c
+22.049 0.911 22.03 0.723 22.03 0.5 c
+28.193 1.469 m
+28.094 1.477 27.991 1.488 27.884 1.499 c
+27.774 1.517 27.653 1.529 27.517 1.529 c
+27.341 1.529 27.183 1.488 27.046 1.411 c
+26.907 1.341 26.789 1.242 26.693 1.117 c
+26.606 0.988 26.535 0.841 26.488 0.675 c
+26.448 0.507 26.429 0.33 26.429 0.147 c
+26.429 -1.264 l
+25.533 -1.264 l
+25.533 0.985 l
+25.533 1.109 25.521 1.234 25.503 1.352 c
+25.492 1.477 25.477 1.595 25.459 1.705 c
+25.448 1.822 25.434 1.918 25.415 1.999 c
+25.393 2.087 25.375 2.161 25.356 2.219 c
+26.238 2.219 l
+26.246 2.167 26.257 2.117 26.267 2.057 c
+26.286 1.999 26.3 1.932 26.312 1.866 c
+26.33 1.808 26.344 1.741 26.356 1.675 c
+26.363 1.606 26.375 1.543 26.385 1.484 c
+26.4 1.484 l
+26.437 1.602 26.488 1.708 26.547 1.808 c
+26.613 1.903 26.693 1.988 26.782 2.057 c
+26.87 2.124 26.973 2.179 27.09 2.219 c
+27.216 2.256 27.362 2.278 27.532 2.278 c
+27.657 2.278 27.774 2.271 27.884 2.263 c
+28.002 2.252 28.105 2.238 28.193 2.219 c
+h
+30.196 -1.324 m
+30.026 -1.324 29.876 -1.301 29.741 -1.264 c
+29.612 -1.216 29.498 -1.147 29.402 -1.058 c
+29.314 -0.971 29.244 -0.864 29.197 -0.736 c
+29.145 -0.599 29.122 -0.449 29.122 -0.279 c
+29.122 -0.073 29.156 0.095 29.226 0.235 c
+29.292 0.382 29.388 0.492 29.505 0.573 c
+29.63 0.661 29.773 0.723 29.932 0.764 c
+30.097 0.801 30.273 0.827 30.46 0.837 c
+31.181 0.852 l
+31.181 1.028 l
+31.181 1.146 31.169 1.249 31.152 1.338 c
+31.129 1.425 31.096 1.492 31.048 1.543 c
+31.008 1.602 30.961 1.639 30.901 1.66 c
+30.843 1.679 30.776 1.691 30.71 1.691 c
+30.641 1.691 30.578 1.679 30.519 1.66 c
+30.468 1.65 30.42 1.624 30.373 1.587 c
+30.332 1.558 30.298 1.506 30.269 1.44 c
+30.248 1.381 30.233 1.301 30.225 1.205 c
+29.284 1.249 l
+29.314 1.396 29.358 1.532 29.417 1.66 c
+29.483 1.786 29.579 1.896 29.696 1.984 c
+29.814 2.08 29.953 2.153 30.122 2.204 c
+30.298 2.252 30.504 2.278 30.74 2.278 c
+31.181 2.278 31.511 2.167 31.74 1.955 c
+31.975 1.749 32.092 1.44 32.092 1.028 c
+32.092 -0.235 l
+32.092 -0.456 l
+32.099 -0.515 32.114 -0.57 32.137 -0.618 c
+32.154 -0.659 32.184 -0.691 32.224 -0.721 c
+32.261 -0.742 32.313 -0.75 32.372 -0.75 c
+32.438 -0.75 32.507 -0.746 32.577 -0.736 c
+32.577 -1.22 l
+32.519 -1.231 32.463 -1.243 32.415 -1.249 c
+32.375 -1.261 32.334 -1.268 32.297 -1.279 c
+32.257 -1.286 32.214 -1.294 32.166 -1.294 c
+32.114 -1.301 32.056 -1.309 31.989 -1.309 c
+31.761 -1.309 31.596 -1.257 31.489 -1.147 c
+31.379 -1.029 31.316 -0.864 31.298 -0.647 c
+31.283 -0.647 l
+31.214 -0.757 31.144 -0.853 31.078 -0.941 c
+31.008 -1.022 30.931 -1.088 30.843 -1.147 c
+30.755 -1.206 30.655 -1.249 30.549 -1.279 c
+30.45 -1.309 30.332 -1.324 30.196 -1.324 c
+31.181 0.353 m
+30.755 0.338 l
+30.655 0.338 30.564 0.33 30.475 0.323 c
+30.394 0.312 30.328 0.286 30.269 0.249 c
+30.211 0.209 30.159 0.151 30.122 0.073 c
+30.082 0.004 30.063 -0.088 30.063 -0.206 c
+30.063 -0.375 30.097 -0.497 30.167 -0.574 c
+30.233 -0.655 30.332 -0.691 30.46 -0.691 c
+30.567 -0.691 30.666 -0.669 30.755 -0.618 c
+30.85 -0.57 30.931 -0.507 30.99 -0.426 c
+31.056 -0.349 31.107 -0.262 31.137 -0.162 c
+31.166 -0.055 31.181 0.058 31.181 0.176 c
+h
+35.495 -1.264 m
+35.495 0.72 l
+35.495 1.022 35.451 1.242 35.362 1.381 c
+35.281 1.529 35.146 1.602 34.951 1.602 c
+34.841 1.602 34.738 1.576 34.643 1.529 c
+34.554 1.477 34.473 1.411 34.408 1.323 c
+34.348 1.234 34.297 1.124 34.26 0.999 c
+34.231 0.881 34.216 0.75 34.216 0.602 c
+34.216 -1.264 l
+33.305 -1.264 l
+33.305 1.44 l
+33.305 1.66 l
+33.305 1.749 33.297 1.826 33.29 1.896 c
+33.29 2.087 l
+33.29 2.219 l
+34.142 2.219 l
+34.15 2.19 34.157 2.146 34.157 2.087 c
+34.157 1.896 l
+34.165 1.826 34.172 1.756 34.172 1.691 c
+34.179 1.62 34.186 1.565 34.186 1.529 c
+34.202 1.529 l
+34.319 1.793 34.47 1.984 34.657 2.102 c
+34.841 2.219 35.061 2.278 35.318 2.278 c
+35.503 2.278 35.664 2.248 35.804 2.19 c
+35.939 2.131 36.054 2.043 36.141 1.926 c
+36.23 1.808 36.293 1.664 36.332 1.499 c
+36.38 1.341 36.407 1.153 36.407 0.941 c
+36.407 -1.264 l
+h
+38.923 -1.324 m
+38.637 -1.324 38.394 -1.283 38.188 -1.206 c
+37.982 -1.118 37.81 -0.996 37.674 -0.838 c
+37.535 -0.684 37.431 -0.497 37.365 -0.279 c
+37.296 -0.055 37.263 0.191 37.263 0.455 c
+37.263 0.75 37.296 1.007 37.365 1.234 c
+37.442 1.458 37.549 1.646 37.689 1.793 c
+37.836 1.947 38.012 2.065 38.218 2.146 c
+38.424 2.234 38.659 2.278 38.923 2.278 c
+39.148 2.278 39.35 2.248 39.526 2.19 c
+39.702 2.131 39.854 2.046 39.981 1.94 c
+40.107 1.841 40.21 1.72 40.291 1.573 c
+40.368 1.433 40.423 1.282 40.452 1.117 c
+39.541 1.072 l
+39.511 1.249 39.441 1.389 39.335 1.499 c
+39.235 1.606 39.092 1.66 38.909 1.66 c
+38.662 1.66 38.487 1.558 38.379 1.352 c
+38.269 1.153 38.218 0.867 38.218 0.484 c
+38.218 -0.309 38.453 -0.706 38.923 -0.706 c
+39.089 -0.706 39.232 -0.655 39.35 -0.544 c
+39.467 -0.437 39.541 -0.276 39.57 -0.059 c
+40.482 -0.103 l
+40.452 -0.272 40.397 -0.426 40.32 -0.574 c
+40.25 -0.721 40.147 -0.853 40.012 -0.971 c
+39.883 -1.081 39.725 -1.168 39.541 -1.235 c
+39.364 -1.294 39.158 -1.324 38.923 -1.324 c
+42.323 1.514 m
+42.441 1.786 42.591 1.984 42.778 2.102 c
+42.962 2.219 43.183 2.278 43.44 2.278 c
+43.646 2.278 43.815 2.242 43.954 2.175 c
+44.101 2.105 44.211 2.013 44.292 1.896 c
+44.38 1.778 44.44 1.635 44.469 1.469 c
+44.505 1.301 44.527 1.124 44.527 0.941 c
+44.527 -1.264 l
+43.616 -1.264 l
+43.616 0.735 l
+43.616 0.87 43.605 0.992 43.586 1.103 c
+43.576 1.209 43.55 1.297 43.513 1.367 c
+43.473 1.444 43.414 1.502 43.337 1.543 c
+43.267 1.58 43.175 1.602 43.058 1.602 c
+42.948 1.602 42.852 1.576 42.763 1.529 c
+42.676 1.477 42.595 1.411 42.528 1.323 c
+42.47 1.234 42.418 1.124 42.381 0.999 c
+42.352 0.881 42.337 0.75 42.337 0.602 c
+42.337 -1.264 l
+41.426 -1.264 l
+41.426 3.513 l
+42.337 3.513 l
+42.337 2.204 l
+42.337 2.135 42.329 2.065 42.323 1.999 c
+42.323 1.793 l
+42.323 1.734 42.315 1.679 42.308 1.631 c
+42.308 1.514 l
+h
+50.297 -2.631 m
+50.297 3.513 l
+52.222 3.513 l
+52.222 2.896 l
+51.149 2.896 l
+51.149 -2.014 l
+52.222 -2.014 l
+52.222 -2.631 l
+h
+55.802 -1.264 m
+55.802 0.72 l
+55.802 1.022 55.758 1.242 55.669 1.381 c
+55.588 1.529 55.453 1.602 55.258 1.602 c
+55.147 1.602 55.044 1.576 54.95 1.529 c
+54.861 1.477 54.78 1.411 54.714 1.323 c
+54.655 1.234 54.603 1.124 54.567 0.999 c
+54.537 0.881 54.522 0.75 54.522 0.602 c
+54.522 -1.264 l
+53.612 -1.264 l
+53.612 1.44 l
+53.612 1.66 l
+53.612 1.749 53.604 1.826 53.597 1.896 c
+53.597 2.087 l
+53.597 2.219 l
+54.449 2.219 l
+54.457 2.19 54.464 2.146 54.464 2.087 c
+54.464 1.896 l
+54.472 1.826 54.479 1.756 54.479 1.691 c
+54.486 1.62 54.493 1.565 54.493 1.529 c
+54.508 1.529 l
+54.626 1.793 54.776 1.984 54.964 2.102 c
+55.147 2.219 55.368 2.278 55.625 2.278 c
+55.809 2.278 55.97 2.248 56.11 2.19 c
+56.246 2.131 56.361 2.043 56.448 1.926 c
+56.536 1.808 56.599 1.664 56.639 1.499 c
+56.687 1.341 56.713 1.153 56.713 0.941 c
+56.713 -1.264 l
+h
+58.628 -1.324 m
+58.458 -1.324 58.308 -1.301 58.172 -1.264 c
+58.044 -1.216 57.93 -1.147 57.834 -1.058 c
+57.745 -0.971 57.676 -0.864 57.628 -0.736 c
+57.576 -0.599 57.554 -0.449 57.554 -0.279 c
+57.554 -0.073 57.587 0.095 57.657 0.235 c
+57.724 0.382 57.819 0.492 57.936 0.573 c
+58.062 0.661 58.204 0.723 58.363 0.764 c
+58.528 0.801 58.705 0.827 58.892 0.837 c
+59.613 0.852 l
+59.613 1.028 l
+59.613 1.146 59.602 1.249 59.583 1.338 c
+59.561 1.425 59.528 1.492 59.48 1.543 c
+59.44 1.602 59.392 1.639 59.333 1.66 c
+59.274 1.679 59.208 1.691 59.142 1.691 c
+59.072 1.691 59.01 1.679 58.951 1.66 c
+58.9 1.65 58.852 1.624 58.804 1.587 c
+58.763 1.558 58.73 1.506 58.701 1.44 c
+58.679 1.381 58.664 1.301 58.657 1.205 c
+57.716 1.249 l
+57.745 1.396 57.79 1.532 57.849 1.66 c
+57.915 1.786 58.01 1.896 58.127 1.984 c
+58.245 2.08 58.385 2.153 58.554 2.204 c
+58.73 2.252 58.936 2.278 59.172 2.278 c
+59.613 2.278 59.943 2.167 60.171 1.955 c
+60.406 1.749 60.523 1.44 60.523 1.028 c
+60.523 -0.235 l
+60.523 -0.456 l
+60.531 -0.515 60.546 -0.57 60.568 -0.618 c
+60.586 -0.659 60.616 -0.691 60.656 -0.721 c
+60.693 -0.742 60.744 -0.75 60.803 -0.75 c
+60.869 -0.75 60.939 -0.746 61.009 -0.736 c
+61.009 -1.22 l
+60.95 -1.231 60.895 -1.243 60.847 -1.249 c
+60.807 -1.261 60.766 -1.268 60.729 -1.279 c
+60.689 -1.286 60.645 -1.294 60.597 -1.294 c
+60.546 -1.301 60.487 -1.309 60.421 -1.309 c
+60.193 -1.309 60.028 -1.257 59.921 -1.147 c
+59.811 -1.029 59.748 -0.864 59.73 -0.647 c
+59.715 -0.647 l
+59.646 -0.757 59.576 -0.853 59.509 -0.941 c
+59.44 -1.022 59.363 -1.088 59.274 -1.147 c
+59.186 -1.206 59.087 -1.249 58.981 -1.279 c
+58.881 -1.309 58.763 -1.324 58.628 -1.324 c
+59.613 0.353 m
+59.186 0.338 l
+59.087 0.338 58.995 0.33 58.907 0.323 c
+58.826 0.312 58.76 0.286 58.701 0.249 c
+58.642 0.209 58.591 0.151 58.554 0.073 c
+58.514 0.004 58.495 -0.088 58.495 -0.206 c
+58.495 -0.375 58.528 -0.497 58.598 -0.574 c
+58.664 -0.655 58.763 -0.691 58.892 -0.691 c
+58.998 -0.691 59.098 -0.669 59.186 -0.618 c
+59.282 -0.57 59.363 -0.507 59.421 -0.426 c
+59.488 -0.349 59.538 -0.262 59.569 -0.162 c
+59.598 -0.055 59.613 0.058 59.613 0.176 c
+h
+62.894 -1.264 m
+62.894 0.852 l
+62.894 1.018 62.887 1.153 62.879 1.263 c
+62.869 1.371 62.85 1.455 62.821 1.514 c
+62.798 1.58 62.769 1.631 62.732 1.66 c
+62.703 1.691 62.663 1.705 62.615 1.705 c
+62.555 1.705 62.501 1.675 62.453 1.616 c
+62.412 1.565 62.38 1.492 62.35 1.396 c
+62.32 1.308 62.295 1.194 62.277 1.058 c
+62.266 0.918 62.262 0.768 62.262 0.602 c
+62.262 -1.264 l
+61.512 -1.264 l
+61.512 1.469 l
+61.512 1.705 l
+61.512 1.926 l
+61.512 2.003 61.505 2.065 61.497 2.117 c
+61.497 2.219 l
+62.173 2.219 l
+62.173 2.131 l
+62.173 1.984 l
+62.181 1.926 62.189 1.866 62.189 1.808 c
+62.189 1.646 l
+62.203 1.646 l
+62.221 1.734 62.251 1.811 62.291 1.881 c
+62.328 1.959 62.372 2.028 62.424 2.087 c
+62.482 2.146 62.549 2.19 62.63 2.219 c
+62.707 2.256 62.794 2.278 62.894 2.278 c
+63.078 2.278 63.218 2.223 63.305 2.117 c
+63.401 2.017 63.471 1.859 63.511 1.646 c
+63.526 1.646 l
+63.563 1.741 63.603 1.83 63.644 1.911 c
+63.691 1.988 63.746 2.05 63.806 2.102 c
+63.864 2.161 63.93 2.204 64.011 2.234 c
+64.088 2.263 64.176 2.278 64.276 2.278 c
+64.411 2.278 64.525 2.252 64.614 2.204 c
+64.702 2.153 64.768 2.08 64.82 1.984 c
+64.878 1.885 64.915 1.756 64.937 1.602 c
+64.967 1.455 64.982 1.271 64.982 1.058 c
+64.982 -1.264 l
+64.261 -1.264 l
+64.261 0.852 l
+64.261 1.018 64.253 1.153 64.246 1.263 c
+64.236 1.371 64.217 1.455 64.188 1.514 c
+64.165 1.58 64.136 1.631 64.099 1.66 c
+64.07 1.691 64.03 1.705 63.982 1.705 c
+63.864 1.705 63.769 1.616 63.702 1.44 c
+63.644 1.271 63.614 1.014 63.614 0.661 c
+63.614 -1.264 l
+h
+67.351 -1.324 m
+67.094 -1.324 66.867 -1.286 66.661 -1.22 c
+66.455 -1.143 66.278 -1.029 66.131 -0.882 c
+65.984 -0.728 65.867 -0.537 65.778 -0.309 c
+65.698 -0.085 65.661 0.18 65.661 0.484 c
+65.661 0.816 65.705 1.095 65.793 1.323 c
+65.888 1.558 66.017 1.741 66.175 1.881 c
+66.341 2.017 66.528 2.117 66.734 2.175 c
+66.94 2.242 67.149 2.278 67.366 2.278 c
+67.638 2.278 67.873 2.227 68.072 2.131 c
+68.278 2.043 68.443 1.911 68.571 1.734 c
+68.708 1.565 68.807 1.359 68.865 1.117 c
+68.932 0.881 68.968 0.617 68.968 0.323 c
+68.968 0.309 l
+66.602 0.309 l
+66.602 0.162 66.617 0.022 66.646 -0.103 c
+66.682 -0.231 66.738 -0.345 66.808 -0.441 c
+66.873 -0.53 66.958 -0.599 67.058 -0.647 c
+67.153 -0.698 67.267 -0.721 67.396 -0.721 c
+67.55 -0.721 67.689 -0.688 67.808 -0.618 c
+67.932 -0.551 68.02 -0.449 68.072 -0.309 c
+68.909 -0.383 l
+68.88 -0.482 68.825 -0.588 68.748 -0.706 c
+68.667 -0.816 68.564 -0.919 68.439 -1.014 c
+68.321 -1.103 68.167 -1.176 67.983 -1.235 c
+67.808 -1.294 67.594 -1.324 67.351 -1.324 c
+67.351 1.705 m
+67.263 1.705 67.175 1.691 67.087 1.66 c
+66.999 1.631 66.918 1.58 66.852 1.514 c
+66.782 1.444 66.723 1.356 66.675 1.249 c
+66.634 1.139 66.617 1.014 66.617 0.867 c
+68.086 0.867 l
+68.086 1.003 68.061 1.124 68.013 1.234 c
+67.972 1.341 67.918 1.429 67.851 1.499 c
+67.792 1.565 67.719 1.616 67.631 1.646 c
+67.542 1.683 67.447 1.705 67.351 1.705 c
+70.221 -2.631 m
+70.221 -2.014 l
+71.295 -2.014 l
+71.295 2.896 l
+70.221 2.896 l
+70.221 3.513 l
+72.147 3.513 l
+72.147 -2.631 l
+h
+f
+Q
+q 1 0 0 1 626.5183 202.8085 cm
+0 0 m
+-0.029 -0.58 -0.191 -1.022 -0.485 -1.323 c
+-0.779 -1.627 -1.198 -1.778 -1.735 -1.778 c
+-2.263 -1.778 -2.69 -1.579 -3.013 -1.176 c
+-3.329 -0.764 -3.484 -0.205 -3.484 0.5 c
+-3.484 1.47 l
+-3.484 2.165 -3.322 2.712 -2.999 3.117 c
+-2.676 3.517 -2.231 3.719 -1.661 3.719 c
+-1.154 3.719 -0.757 3.561 -0.47 3.248 c
+-0.187 2.944 -0.029 2.502 0 1.926 c
+-0.691 1.926 l
+-0.721 2.367 -0.816 2.679 -0.97 2.866 c
+-1.118 3.051 -1.349 3.146 -1.661 3.146 c
+-2.036 3.146 -2.319 2.999 -2.514 2.705 c
+-2.712 2.419 -2.807 2.003 -2.807 1.455 c
+-2.807 0.47 l
+-2.807 -0.07 -2.716 -0.484 -2.529 -0.779 c
+-2.344 -1.065 -2.08 -1.205 -1.735 -1.205 c
+-1.382 -1.205 -1.128 -1.117 -0.97 -0.941 c
+-0.816 -0.764 -0.721 -0.452 -0.691 0 c
+h
+2.454 1.661 m
+2.367 1.679 2.267 1.691 2.161 1.691 c
+1.826 1.691 1.591 1.507 1.455 1.147 c
+1.455 -1.705 l
+0.808 -1.705 l
+0.808 2.278 l
+1.44 2.278 l
+1.455 1.867 l
+1.631 2.19 1.874 2.352 2.19 2.352 c
+2.296 2.352 2.385 2.33 2.454 2.294 c
+h
+4.453 -1.778 m
+3.954 -1.778 3.572 -1.631 3.308 -1.338 c
+3.042 -1.043 2.911 -0.61 2.911 -0.029 c
+2.911 0.441 l
+2.911 1.037 3.036 1.503 3.293 1.837 c
+3.557 2.18 3.917 2.352 4.38 2.352 c
+4.839 2.352 5.182 2.198 5.409 1.897 c
+5.644 1.602 5.766 1.139 5.777 0.515 c
+5.777 0.088 l
+3.557 0.088 l
+3.557 0 l
+3.557 -0.434 3.634 -0.746 3.792 -0.941 c
+3.958 -1.128 4.189 -1.22 4.484 -1.22 c
+4.678 -1.22 4.85 -1.186 4.997 -1.117 c
+5.145 -1.04 5.28 -0.922 5.409 -0.764 c
+5.747 -1.176 l
+5.461 -1.579 5.031 -1.778 4.453 -1.778 c
+4.38 1.794 m
+4.104 1.794 3.902 1.698 3.778 1.515 c
+3.649 1.326 3.576 1.037 3.557 0.647 c
+5.13 0.647 l
+5.13 0.736 l
+5.108 1.118 5.041 1.386 4.924 1.544 c
+4.806 1.709 4.623 1.794 4.38 1.794 c
+8.599 -1.705 m
+8.559 -1.616 8.532 -1.469 8.525 -1.263 c
+8.29 -1.61 7.996 -1.778 7.643 -1.778 c
+7.279 -1.778 6.996 -1.683 6.791 -1.484 c
+6.593 -1.278 6.497 -0.992 6.497 -0.617 c
+6.497 -0.216 6.633 0.103 6.909 0.339 c
+7.181 0.581 7.555 0.706 8.025 0.706 c
+8.511 0.706 l
+8.511 1.132 l
+8.511 1.367 8.455 1.532 8.349 1.632 c
+8.239 1.738 8.077 1.794 7.864 1.794 c
+7.665 1.794 7.504 1.735 7.379 1.617 c
+7.262 1.5 7.202 1.353 7.202 1.176 c
+6.556 1.176 l
+6.556 1.371 6.614 1.562 6.732 1.75 c
+6.857 1.933 7.019 2.08 7.217 2.19 c
+7.423 2.297 7.651 2.352 7.908 2.352 c
+8.309 2.352 8.613 2.249 8.819 2.043 c
+9.033 1.837 9.147 1.544 9.157 1.162 c
+9.157 -0.852 l
+9.157 -1.157 9.194 -1.421 9.275 -1.646 c
+9.275 -1.705 l
+h
+7.732 -1.19 m
+7.897 -1.19 8.048 -1.146 8.187 -1.058 c
+8.334 -0.97 8.441 -0.86 8.511 -0.72 c
+8.511 0.221 l
+8.143 0.221 l
+7.827 0.221 7.584 0.151 7.408 0.015 c
+7.231 -0.114 7.144 -0.301 7.144 -0.544 c
+7.144 -0.771 7.188 -0.937 7.276 -1.043 c
+7.364 -1.143 7.515 -1.19 7.732 -1.19 c
+11.009 3.234 m
+11.009 2.278 l
+11.612 2.278 l
+11.612 1.75 l
+11.009 1.75 l
+11.009 -0.72 l
+11.009 -0.878 11.032 -0.995 11.083 -1.072 c
+11.142 -1.153 11.23 -1.19 11.347 -1.19 c
+11.436 -1.19 11.524 -1.176 11.612 -1.146 c
+11.612 -1.705 l
+11.465 -1.753 11.31 -1.778 11.156 -1.778 c
+10.899 -1.778 10.705 -1.687 10.568 -1.499 c
+10.429 -1.315 10.363 -1.055 10.363 -0.72 c
+10.363 1.75 l
+9.76 1.75 l
+9.76 2.278 l
+10.363 2.278 l
+10.363 3.234 l
+h
+13.802 -1.778 m
+13.303 -1.778 12.92 -1.631 12.656 -1.338 c
+12.391 -1.043 12.259 -0.61 12.259 -0.029 c
+12.259 0.441 l
+12.259 1.037 12.384 1.503 12.641 1.837 c
+12.906 2.18 13.266 2.352 13.729 2.352 c
+14.188 2.352 14.53 2.198 14.757 1.897 c
+14.992 1.602 15.114 1.139 15.125 0.515 c
+15.125 0.088 l
+12.906 0.088 l
+12.906 0 l
+12.906 -0.434 12.983 -0.746 13.141 -0.941 c
+13.306 -1.128 13.538 -1.22 13.832 -1.22 c
+14.026 -1.22 14.199 -1.186 14.346 -1.117 c
+14.493 -1.04 14.629 -0.922 14.757 -0.764 c
+15.096 -1.176 l
+14.809 -1.579 14.379 -1.778 13.802 -1.778 c
+13.729 1.794 m
+13.453 1.794 13.251 1.698 13.126 1.515 c
+12.997 1.326 12.924 1.037 12.906 0.647 c
+14.479 0.647 l
+14.479 0.736 l
+14.456 1.118 14.39 1.386 14.273 1.544 c
+14.155 1.709 13.972 1.794 13.729 1.794 c
+18.242 2.278 m
+18.256 1.837 l
+18.51 2.18 18.834 2.352 19.226 2.352 c
+19.931 2.352 20.288 1.881 20.299 0.941 c
+20.299 -1.705 l
+19.653 -1.705 l
+19.653 0.912 l
+19.653 1.224 19.597 1.444 19.491 1.573 c
+19.381 1.698 19.226 1.764 19.021 1.764 c
+18.863 1.764 18.715 1.709 18.58 1.602 c
+18.451 1.492 18.348 1.357 18.271 1.191 c
+18.271 -1.705 l
+17.624 -1.705 l
+17.624 2.278 l
+h
+22.695 -1.778 m
+22.196 -1.778 21.813 -1.631 21.549 -1.338 c
+21.284 -1.043 21.151 -0.61 21.151 -0.029 c
+21.151 0.441 l
+21.151 1.037 21.277 1.503 21.534 1.837 c
+21.799 2.18 22.159 2.352 22.622 2.352 c
+23.081 2.352 23.422 2.198 23.651 1.897 c
+23.886 1.602 24.008 1.139 24.018 0.515 c
+24.018 0.088 l
+21.799 0.088 l
+21.799 0 l
+21.799 -0.434 21.876 -0.746 22.034 -0.941 c
+22.2 -1.128 22.431 -1.22 22.724 -1.22 c
+22.919 -1.22 23.092 -1.186 23.239 -1.117 c
+23.386 -1.04 23.522 -0.922 23.651 -0.764 c
+23.989 -1.176 l
+23.702 -1.579 23.272 -1.778 22.695 -1.778 c
+22.622 1.794 m
+22.346 1.794 22.144 1.698 22.019 1.515 c
+21.891 1.326 21.817 1.037 21.799 0.647 c
+23.372 0.647 l
+23.372 0.736 l
+23.349 1.118 23.283 1.386 23.166 1.544 c
+23.048 1.709 22.865 1.794 22.622 1.794 c
+27.767 -0.558 m
+28.369 2.278 l
+29.016 2.278 l
+28.031 -1.705 l
+27.517 -1.705 l
+26.738 1.147 l
+25.988 -1.705 l
+25.458 -1.705 l
+24.503 2.278 l
+25.136 2.278 l
+25.753 -0.484 l
+26.487 2.278 l
+27.002 2.278 l
+h
+34.293 0.088 m
+34.293 -0.529 34.179 -0.995 33.955 -1.308 c
+33.738 -1.624 33.415 -1.778 32.984 -1.778 c
+32.562 -1.778 32.249 -1.598 32.043 -1.234 c
+32.014 -1.705 l
+31.412 -1.705 l
+31.412 3.94 l
+32.058 3.94 l
+32.058 1.837 l
+32.272 2.18 32.581 2.352 32.984 2.352 c
+33.415 2.352 33.738 2.194 33.955 1.881 c
+34.179 1.577 34.293 1.11 34.293 0.485 c
+h
+33.646 0.47 m
+33.646 0.941 33.576 1.272 33.44 1.47 c
+33.312 1.665 33.102 1.764 32.808 1.764 c
+32.474 1.764 32.224 1.58 32.058 1.22 c
+32.058 -0.661 l
+32.224 -1.025 32.477 -1.205 32.823 -1.205 c
+33.117 -1.205 33.327 -1.103 33.455 -0.897 c
+33.58 -0.691 33.646 -0.374 33.646 0.059 c
+h
+36.777 1.661 m
+36.689 1.679 36.589 1.691 36.483 1.691 c
+36.149 1.691 35.914 1.507 35.777 1.147 c
+35.777 -1.705 l
+35.131 -1.705 l
+35.131 2.278 l
+35.763 2.278 l
+35.777 1.867 l
+35.954 2.19 36.197 2.352 36.512 2.352 c
+36.619 2.352 36.708 2.33 36.777 2.294 c
+h
+39.32 -1.705 m
+39.28 -1.616 39.254 -1.469 39.247 -1.263 c
+39.011 -1.61 38.717 -1.778 38.364 -1.778 c
+38 -1.778 37.718 -1.683 37.512 -1.484 c
+37.313 -1.278 37.217 -0.992 37.217 -0.617 c
+37.217 -0.216 37.354 0.103 37.63 0.339 c
+37.901 0.581 38.276 0.706 38.746 0.706 c
+39.232 0.706 l
+39.232 1.132 l
+39.232 1.367 39.176 1.532 39.07 1.632 c
+38.96 1.738 38.798 1.794 38.585 1.794 c
+38.387 1.794 38.225 1.735 38.1 1.617 c
+37.982 1.5 37.923 1.353 37.923 1.176 c
+37.277 1.176 l
+37.277 1.371 37.335 1.562 37.453 1.75 c
+37.578 1.933 37.74 2.08 37.938 2.19 c
+38.144 2.297 38.372 2.352 38.629 2.352 c
+39.029 2.352 39.334 2.249 39.54 2.043 c
+39.754 1.837 39.868 1.544 39.879 1.162 c
+39.879 -0.852 l
+39.879 -1.157 39.916 -1.421 39.996 -1.646 c
+39.996 -1.705 l
+h
+38.453 -1.19 m
+38.618 -1.19 38.769 -1.146 38.908 -1.058 c
+39.056 -0.97 39.162 -0.86 39.232 -0.72 c
+39.232 0.221 l
+38.864 0.221 l
+38.549 0.221 38.306 0.151 38.129 0.015 c
+37.953 -0.114 37.865 -0.301 37.865 -0.544 c
+37.865 -0.771 37.909 -0.937 37.997 -1.043 c
+38.085 -1.143 38.235 -1.19 38.453 -1.19 c
+41.495 2.278 m
+41.51 1.837 l
+41.763 2.18 42.087 2.352 42.48 2.352 c
+43.185 2.352 43.542 1.881 43.553 0.941 c
+43.553 -1.705 l
+42.906 -1.705 l
+42.906 0.912 l
+42.906 1.224 42.852 1.444 42.744 1.573 c
+42.634 1.698 42.48 1.764 42.274 1.764 c
+42.117 1.764 41.969 1.709 41.834 1.602 c
+41.705 1.492 41.602 1.357 41.525 1.191 c
+41.525 -1.705 l
+40.878 -1.705 l
+40.878 2.278 l
+h
+45.905 -1.22 m
+46.118 -1.22 46.291 -1.157 46.42 -1.028 c
+46.555 -0.893 46.629 -0.702 46.64 -0.455 c
+47.257 -0.455 l
+47.235 -0.837 47.099 -1.157 46.846 -1.411 c
+46.588 -1.657 46.276 -1.778 45.905 -1.778 c
+45.412 -1.778 45.038 -1.627 44.773 -1.323 c
+44.516 -1.01 44.391 -0.544 44.391 0.073 c
+44.391 0.515 l
+44.391 1.11 44.516 1.565 44.773 1.881 c
+45.038 2.194 45.412 2.352 45.905 2.352 c
+46.306 2.352 46.625 2.22 46.86 1.955 c
+47.103 1.698 47.235 1.353 47.257 0.912 c
+46.64 0.912 l
+46.618 1.206 46.545 1.426 46.42 1.573 c
+46.302 1.72 46.129 1.794 45.905 1.794 c
+45.611 1.794 45.394 1.694 45.258 1.5 c
+45.119 1.312 45.045 1.004 45.038 0.574 c
+45.038 0.059 l
+45.038 -0.411 45.104 -0.746 45.244 -0.941 c
+45.391 -1.128 45.611 -1.22 45.905 -1.22 c
+48.654 1.867 m
+48.907 2.19 49.227 2.352 49.609 2.352 c
+50.314 2.352 50.671 1.881 50.682 0.941 c
+50.682 -1.705 l
+50.035 -1.705 l
+50.035 0.912 l
+50.035 1.224 49.98 1.444 49.874 1.573 c
+49.763 1.698 49.609 1.764 49.404 1.764 c
+49.246 1.764 49.098 1.709 48.963 1.602 c
+48.834 1.492 48.731 1.357 48.654 1.191 c
+48.654 -1.705 l
+48.007 -1.705 l
+48.007 3.94 l
+48.654 3.94 l
+h
+51.652 -2.778 m
+51.255 -2.514 l
+51.49 -2.19 51.612 -1.855 51.623 -1.514 c
+51.623 -0.897 l
+52.284 -0.897 l
+52.284 -1.425 l
+52.284 -1.683 52.219 -1.929 52.093 -2.175 c
+51.976 -2.418 51.829 -2.62 51.652 -2.778 c
+56.489 1.661 m
+56.4 1.679 56.301 1.691 56.194 1.691 c
+55.86 1.691 55.625 1.507 55.488 1.147 c
+55.488 -1.705 l
+54.842 -1.705 l
+54.842 2.278 l
+55.474 2.278 l
+55.488 1.867 l
+55.665 2.19 55.908 2.352 56.224 2.352 c
+56.33 2.352 56.419 2.33 56.489 2.294 c
+h
+58.488 -1.778 m
+57.988 -1.778 57.605 -1.631 57.341 -1.338 c
+57.077 -1.043 56.944 -0.61 56.944 -0.029 c
+56.944 0.441 l
+56.944 1.037 57.069 1.503 57.326 1.837 c
+57.591 2.18 57.951 2.352 58.414 2.352 c
+58.873 2.352 59.215 2.198 59.442 1.897 c
+59.678 1.602 59.799 1.139 59.81 0.515 c
+59.81 0.088 l
+57.591 0.088 l
+57.591 0 l
+57.591 -0.434 57.668 -0.746 57.826 -0.941 c
+57.991 -1.128 58.223 -1.22 58.517 -1.22 c
+58.711 -1.22 58.885 -1.186 59.031 -1.117 c
+59.178 -1.04 59.315 -0.922 59.442 -0.764 c
+59.781 -1.176 l
+59.494 -1.579 59.064 -1.778 58.488 -1.778 c
+58.414 1.794 m
+58.139 1.794 57.936 1.698 57.811 1.515 c
+57.682 1.326 57.609 1.037 57.591 0.647 c
+59.164 0.647 l
+59.164 0.736 l
+59.141 1.118 59.076 1.386 58.958 1.544 c
+58.84 1.709 58.657 1.794 58.414 1.794 c
+60.884 -1.705 m
+60.884 1.75 l
+60.354 1.75 l
+60.354 2.278 l
+60.884 2.278 l
+60.884 2.735 l
+60.884 3.135 60.979 3.447 61.177 3.675 c
+61.383 3.899 61.663 4.013 62.015 4.013 c
+62.151 4.013 62.283 3.992 62.412 3.954 c
+62.382 3.41 l
+62.283 3.429 62.185 3.44 62.089 3.44 c
+61.714 3.44 61.53 3.175 61.53 2.646 c
+61.53 2.278 l
+62.206 2.278 l
+62.206 1.75 l
+61.53 1.75 l
+61.53 -1.705 l
+h
+64.308 -1.778 m
+63.808 -1.778 63.427 -1.631 63.162 -1.338 c
+62.897 -1.043 62.765 -0.61 62.765 -0.029 c
+62.765 0.441 l
+62.765 1.037 62.89 1.503 63.147 1.837 c
+63.411 2.18 63.772 2.352 64.235 2.352 c
+64.695 2.352 65.036 2.198 65.264 1.897 c
+65.499 1.602 65.62 1.139 65.632 0.515 c
+65.632 0.088 l
+63.411 0.088 l
+63.411 0 l
+63.411 -0.434 63.489 -0.746 63.647 -0.941 c
+63.812 -1.128 64.044 -1.22 64.338 -1.22 c
+64.533 -1.22 64.705 -1.186 64.852 -1.117 c
+64.999 -1.04 65.135 -0.922 65.264 -0.764 c
+65.601 -1.176 l
+65.315 -1.579 64.886 -1.778 64.308 -1.778 c
+64.235 1.794 m
+63.959 1.794 63.757 1.698 63.633 1.515 c
+63.504 1.326 63.43 1.037 63.411 0.647 c
+64.984 0.647 l
+64.984 0.736 l
+64.963 1.118 64.896 1.386 64.778 1.544 c
+64.661 1.709 64.477 1.794 64.235 1.794 c
+68.057 1.661 m
+67.968 1.679 67.869 1.691 67.762 1.691 c
+67.428 1.691 67.193 1.507 67.057 1.147 c
+67.057 -1.705 l
+66.411 -1.705 l
+66.411 2.278 l
+67.043 2.278 l
+67.057 1.867 l
+67.234 2.19 67.476 2.352 67.792 2.352 c
+67.899 2.352 67.987 2.33 68.057 2.294 c
+h
+70.056 -1.778 m
+69.555 -1.778 69.173 -1.631 68.909 -1.338 c
+68.645 -1.043 68.512 -0.61 68.512 -0.029 c
+68.512 0.441 l
+68.512 1.037 68.637 1.503 68.894 1.837 c
+69.159 2.18 69.519 2.352 69.982 2.352 c
+70.442 2.352 70.783 2.198 71.011 1.897 c
+71.246 1.602 71.367 1.139 71.379 0.515 c
+71.379 0.088 l
+69.159 0.088 l
+69.159 0 l
+69.159 -0.434 69.236 -0.746 69.395 -0.941 c
+69.559 -1.128 69.791 -1.22 70.085 -1.22 c
+70.28 -1.22 70.453 -1.186 70.6 -1.117 c
+70.746 -1.04 70.883 -0.922 71.011 -0.764 c
+71.349 -1.176 l
+71.062 -1.579 70.633 -1.778 70.056 -1.778 c
+69.982 1.794 m
+69.707 1.794 69.505 1.698 69.38 1.515 c
+69.251 1.326 69.177 1.037 69.159 0.647 c
+70.731 0.647 l
+70.731 0.736 l
+70.71 1.118 70.644 1.386 70.526 1.544 c
+70.409 1.709 70.224 1.794 69.982 1.794 c
+72.775 2.278 m
+72.79 1.837 l
+73.043 2.18 73.366 2.352 73.76 2.352 c
+74.465 2.352 74.822 1.881 74.833 0.941 c
+74.833 -1.705 l
+74.186 -1.705 l
+74.186 0.912 l
+74.186 1.224 74.131 1.444 74.024 1.573 c
+73.914 1.698 73.76 1.764 73.554 1.764 c
+73.396 1.764 73.249 1.709 73.113 1.602 c
+72.984 1.492 72.882 1.357 72.805 1.191 c
+72.805 -1.705 l
+72.157 -1.705 l
+72.157 2.278 l
+h
+77.185 -1.22 m
+77.397 -1.22 77.57 -1.157 77.699 -1.028 c
+77.835 -0.893 77.908 -0.702 77.919 -0.455 c
+78.537 -0.455 l
+78.515 -0.837 78.379 -1.157 78.126 -1.411 c
+77.868 -1.657 77.555 -1.778 77.185 -1.778 c
+76.692 -1.778 76.317 -1.627 76.053 -1.323 c
+75.795 -1.01 75.67 -0.544 75.67 0.073 c
+75.67 0.515 l
+75.67 1.11 75.795 1.565 76.053 1.881 c
+76.317 2.194 76.692 2.352 77.185 2.352 c
+77.586 2.352 77.904 2.22 78.14 1.955 c
+78.382 1.698 78.515 1.353 78.537 0.912 c
+77.919 0.912 l
+77.898 1.206 77.825 1.426 77.699 1.573 c
+77.582 1.72 77.409 1.794 77.185 1.794 c
+76.89 1.794 76.674 1.694 76.538 1.5 c
+76.398 1.312 76.325 1.004 76.317 0.574 c
+76.317 0.059 l
+76.317 -0.411 76.383 -0.746 76.523 -0.941 c
+76.67 -1.128 76.89 -1.22 77.185 -1.22 c
+79.992 -1.705 -0.646 3.983 re
+80.036 3.323 m
+80.036 3.212 80.007 3.12 79.948 3.043 c
+79.889 2.973 79.793 2.94 79.668 2.94 c
+79.551 2.94 79.456 2.973 79.39 3.043 c
+79.331 3.12 79.301 3.212 79.301 3.323 c
+79.301 3.44 79.331 3.532 79.39 3.601 c
+79.456 3.678 79.551 3.719 79.668 3.719 c
+79.793 3.719 79.889 3.678 79.948 3.601 c
+80.007 3.521 80.036 3.429 80.036 3.323 c
+81.624 2.278 m
+81.638 1.837 l
+81.892 2.18 82.215 2.352 82.608 2.352 c
+83.314 2.352 83.67 1.881 83.682 0.941 c
+83.682 -1.705 l
+83.035 -1.705 l
+83.035 0.912 l
+83.035 1.224 82.98 1.444 82.873 1.573 c
+82.763 1.698 82.608 1.764 82.403 1.764 c
+82.245 1.764 82.098 1.709 81.962 1.602 c
+81.833 1.492 81.73 1.357 81.653 1.191 c
+81.653 -1.705 l
+81.006 -1.705 l
+81.006 2.278 l
+h
+84.52 0.47 m
+84.52 1.088 84.63 1.551 84.857 1.867 c
+85.081 2.19 85.416 2.352 85.857 2.352 c
+86.257 2.352 86.563 2.176 86.768 1.823 c
+86.812 2.278 l
+87.4 2.278 l
+87.4 -1.749 l
+87.4 -2.238 87.272 -2.616 87.018 -2.881 c
+86.762 -3.145 86.409 -3.278 85.96 -3.278 c
+85.761 -3.278 85.541 -3.226 85.299 -3.131 c
+85.052 -3.031 84.873 -2.91 84.755 -2.763 c
+85.019 -2.322 l
+85.284 -2.587 85.582 -2.719 85.916 -2.719 c
+86.452 -2.719 86.728 -2.425 86.739 -1.837 c
+86.739 -1.308 l
+86.533 -1.624 86.232 -1.778 85.842 -1.778 c
+85.43 -1.778 85.108 -1.627 84.873 -1.323 c
+84.645 -1.01 84.527 -0.558 84.52 0.03 c
+h
+85.181 0.088 m
+85.181 -0.353 85.243 -0.683 85.372 -0.897 c
+85.497 -1.103 85.714 -1.205 86.018 -1.205 c
+86.342 -1.205 86.581 -1.04 86.739 -0.706 c
+86.739 1.279 l
+86.57 1.602 86.332 1.764 86.018 1.764 c
+85.725 1.764 85.508 1.661 85.372 1.455 c
+85.243 1.249 85.181 0.927 85.181 0.485 c
+h
+90.928 3.234 m
+90.928 2.278 l
+91.531 2.278 l
+91.531 1.75 l
+90.928 1.75 l
+90.928 -0.72 l
+90.928 -0.878 90.951 -0.995 91.001 -1.072 c
+91.061 -1.153 91.149 -1.19 91.267 -1.19 c
+91.354 -1.19 91.443 -1.176 91.531 -1.146 c
+91.531 -1.705 l
+91.384 -1.753 91.23 -1.778 91.075 -1.778 c
+90.818 -1.778 90.623 -1.687 90.487 -1.499 c
+90.348 -1.315 90.282 -1.055 90.282 -0.72 c
+90.282 1.75 l
+89.679 1.75 l
+89.679 2.278 l
+90.282 2.278 l
+90.282 3.234 l
+h
+92.942 1.867 m
+93.195 2.19 93.515 2.352 93.897 2.352 c
+94.603 2.352 94.959 1.881 94.97 0.941 c
+94.97 -1.705 l
+94.324 -1.705 l
+94.324 0.912 l
+94.324 1.224 94.269 1.444 94.162 1.573 c
+94.052 1.698 93.897 1.764 93.692 1.764 c
+93.534 1.764 93.387 1.709 93.251 1.602 c
+93.122 1.492 93.019 1.357 92.942 1.191 c
+92.942 -1.705 l
+92.295 -1.705 l
+92.295 3.94 l
+92.942 3.94 l
+h
+97.351 -1.778 m
+96.852 -1.778 96.47 -1.631 96.206 -1.338 c
+95.94 -1.043 95.809 -0.61 95.809 -0.029 c
+95.809 0.441 l
+95.809 1.037 95.934 1.503 96.191 1.837 c
+96.455 2.18 96.815 2.352 97.278 2.352 c
+97.737 2.352 98.08 2.198 98.307 1.897 c
+98.542 1.602 98.664 1.139 98.675 0.515 c
+98.675 0.088 l
+96.455 0.088 l
+96.455 0 l
+96.455 -0.434 96.532 -0.746 96.69 -0.941 c
+96.856 -1.128 97.087 -1.22 97.382 -1.22 c
+97.576 -1.22 97.748 -1.186 97.895 -1.117 c
+98.043 -1.04 98.178 -0.922 98.307 -0.764 c
+98.645 -1.176 l
+98.359 -1.579 97.929 -1.778 97.351 -1.778 c
+97.278 1.794 m
+97.002 1.794 96.8 1.698 96.676 1.515 c
+96.547 1.326 96.474 1.037 96.455 0.647 c
+98.028 0.647 l
+98.028 0.736 l
+98.006 1.118 97.939 1.386 97.822 1.544 c
+97.704 1.709 97.521 1.794 97.278 1.794 c
+102.556 -1.22 m
+102.768 -1.22 102.941 -1.157 103.069 -1.028 c
+103.206 -0.893 103.279 -0.702 103.29 -0.455 c
+103.907 -0.455 l
+103.886 -0.837 103.749 -1.157 103.496 -1.411 c
+103.239 -1.657 102.926 -1.778 102.556 -1.778 c
+102.063 -1.778 101.688 -1.627 101.423 -1.323 c
+101.166 -1.01 101.041 -0.544 101.041 0.073 c
+101.041 0.515 l
+101.041 1.11 101.166 1.565 101.423 1.881 c
+101.688 2.194 102.063 2.352 102.556 2.352 c
+102.955 2.352 103.275 2.22 103.51 1.955 c
+103.753 1.698 103.886 1.353 103.907 0.912 c
+103.29 0.912 l
+103.268 1.206 103.194 1.426 103.069 1.573 c
+102.952 1.72 102.779 1.794 102.556 1.794 c
+102.261 1.794 102.045 1.694 101.908 1.5 c
+101.769 1.312 101.696 1.004 101.688 0.574 c
+101.688 0.059 l
+101.688 -0.411 101.754 -0.746 101.893 -0.941 c
+102.041 -1.128 102.261 -1.22 102.556 -1.22 c
+106.685 -1.352 m
+106.469 -1.639 106.157 -1.778 105.745 -1.778 c
+105.382 -1.778 105.106 -1.657 104.922 -1.411 c
+104.746 -1.157 104.65 -0.793 104.642 -0.323 c
+104.642 2.278 l
+105.289 2.278 l
+105.289 -0.264 l
+105.289 -0.893 105.473 -1.205 105.848 -1.205 c
+106.248 -1.205 106.524 -1.028 106.671 -0.675 c
+106.671 2.278 l
+107.318 2.278 l
+107.318 -1.705 l
+106.701 -1.705 l
+h
+109.949 1.661 m
+109.86 1.679 109.762 1.691 109.654 1.691 c
+109.32 1.691 109.085 1.507 108.949 1.147 c
+108.949 -1.705 l
+108.303 -1.705 l
+108.303 2.278 l
+108.935 2.278 l
+108.949 1.867 l
+109.126 2.19 109.368 2.352 109.685 2.352 c
+109.791 2.352 109.879 2.33 109.949 2.294 c
+h
+112.241 1.661 m
+112.154 1.679 112.054 1.691 111.948 1.691 c
+111.613 1.691 111.378 1.507 111.242 1.147 c
+111.242 -1.705 l
+110.595 -1.705 l
+110.595 2.278 l
+111.227 2.278 l
+111.242 1.867 l
+111.418 2.19 111.661 2.352 111.977 2.352 c
+112.083 2.352 112.172 2.33 112.241 2.294 c
+h
+114.24 -1.778 m
+113.741 -1.778 113.359 -1.631 113.095 -1.338 c
+112.829 -1.043 112.698 -0.61 112.698 -0.029 c
+112.698 0.441 l
+112.698 1.037 112.823 1.503 113.08 1.837 c
+113.344 2.18 113.704 2.352 114.167 2.352 c
+114.627 2.352 114.969 2.198 115.196 1.897 c
+115.431 1.602 115.553 1.139 115.564 0.515 c
+115.564 0.088 l
+113.344 0.088 l
+113.344 0 l
+113.344 -0.434 113.421 -0.746 113.579 -0.941 c
+113.745 -1.128 113.976 -1.22 114.271 -1.22 c
+114.465 -1.22 114.638 -1.186 114.785 -1.117 c
+114.932 -1.04 115.067 -0.922 115.196 -0.764 c
+115.534 -1.176 l
+115.248 -1.579 114.818 -1.778 114.24 -1.778 c
+114.167 1.794 m
+113.891 1.794 113.69 1.698 113.565 1.515 c
+113.436 1.326 113.363 1.037 113.344 0.647 c
+114.917 0.647 l
+114.917 0.736 l
+114.895 1.118 114.828 1.386 114.711 1.544 c
+114.593 1.709 114.41 1.794 114.167 1.794 c
+116.96 2.278 m
+116.975 1.837 l
+117.228 2.18 117.552 2.352 117.945 2.352 c
+118.651 2.352 119.007 1.881 119.019 0.941 c
+119.019 -1.705 l
+118.371 -1.705 l
+118.371 0.912 l
+118.371 1.224 118.317 1.444 118.209 1.573 c
+118.099 1.698 117.945 1.764 117.739 1.764 c
+117.581 1.764 117.434 1.709 117.298 1.602 c
+117.17 1.492 117.067 1.357 116.989 1.191 c
+116.989 -1.705 l
+116.343 -1.705 l
+116.343 2.278 l
+h
+120.841 3.234 m
+120.841 2.278 l
+121.444 2.278 l
+121.444 1.75 l
+120.841 1.75 l
+120.841 -0.72 l
+120.841 -0.878 120.863 -0.995 120.914 -1.072 c
+120.973 -1.153 121.061 -1.19 121.178 -1.19 c
+121.267 -1.19 121.355 -1.176 121.444 -1.146 c
+121.444 -1.705 l
+121.296 -1.753 121.142 -1.778 120.987 -1.778 c
+120.731 -1.778 120.536 -1.687 120.4 -1.499 c
+120.26 -1.315 120.194 -1.055 120.194 -0.72 c
+120.194 1.75 l
+119.591 1.75 l
+119.591 2.278 l
+120.194 2.278 l
+120.194 3.234 l
+h
+f
+Q
+q 1 0 0 1 754.1503 201.1039 cm
+0 0 m
+-1.088 0 l
+-1.088 2.293 l
+-2.676 2.293 l
+-2.676 0 l
+-3.763 0 l
+-3.763 5.35 l
+-2.676 5.35 l
+-2.676 3.189 l
+-1.088 3.189 l
+-1.088 5.35 l
+0 5.35 l
+h
+3.616 2.323 m
+1.94 2.323 l
+1.94 0.897 l
+3.925 0.897 l
+3.925 0 l
+0.852 0 l
+0.852 5.35 l
+3.925 5.35 l
+3.925 4.453 l
+1.94 4.453 l
+1.94 3.189 l
+3.616 3.189 l
+h
+7.1 1.103 m
+5.629 1.103 l
+5.336 0 l
+4.204 0 l
+5.865 5.35 l
+6.865 5.35 l
+8.54 0 l
+7.393 0 l
+h
+5.865 1.999 m
+6.865 1.999 l
+6.364 3.91 l
+h
+9.039 0 m
+9.039 5.35 l
+10.466 5.35 l
+11.083 5.35 11.576 5.148 11.95 4.748 c
+12.322 4.355 12.513 3.815 12.523 3.131 c
+12.523 2.263 l
+12.523 1.558 12.336 1.003 11.965 0.602 c
+11.59 0.199 11.083 0 10.437 0 c
+h
+10.127 4.453 m
+10.127 0.897 l
+10.451 0.897 l
+10.811 0.897 11.068 0.989 11.216 1.176 c
+11.362 1.371 11.436 1.698 11.436 2.161 c
+11.436 3.102 l
+11.436 3.601 11.366 3.946 11.23 4.145 c
+11.09 4.34 10.855 4.442 10.524 4.453 c
+h
+f
+Q
+q 1 0 0 1 767.5407 201.4567 cm
+0 0 m
+0 0.118 0.033 0.213 0.104 0.294 c
+0.169 0.371 0.272 0.411 0.412 0.411 c
+0.559 0.411 0.665 0.371 0.735 0.294 c
+0.813 0.213 0.852 0.118 0.852 0 c
+0.852 -0.11 0.813 -0.202 0.735 -0.279 c
+0.665 -0.357 0.559 -0.397 0.412 -0.397 c
+0.272 -0.397 0.169 -0.357 0.104 -0.279 c
+0.033 -0.202 0 -0.11 0 0 c
+f
+Q
+0.113 0.082 0.09 0 k
+535.667 195.5 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 540.8555 188.6608 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.088 -0.5 c
+-0.147 -0.646 -0.242 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.263 l
+-2.142 -1.257 -2.358 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.141 1.279 -3.218 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.218 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.62 -0.279 2.484 c
+-0.201 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.319 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.318 2.385 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.19 c
+-2.476 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.2 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.102 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.52 -2.484 5.403 -2.396 c
+5.285 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.214 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.815 0.243 4.815 0.5 c
+4.815 0.772 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.189 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.029 7.122 2.076 7.122 2.117 c
+7.13 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.992 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.872 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.226 1.621 6.137 1.573 c
+6.056 1.532 5.991 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.498 3.117 l
+14.498 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.498 1.602 l
+14.498 -0.103 l
+14.498 -0.323 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.675 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.687 15.468 -0.675 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.042 -1.301 14.942 -1.308 14.836 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.263 c
+14.222 -1.227 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.792 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+22.677 -1.323 m
+22.391 -1.323 22.148 -1.282 21.943 -1.205 c
+21.737 -1.117 21.564 -0.995 21.428 -0.837 c
+21.288 -0.683 21.185 -0.496 21.12 -0.279 c
+21.049 -0.055 21.016 0.191 21.016 0.456 c
+21.016 0.75 21.049 1.008 21.12 1.235 c
+21.197 1.459 21.303 1.646 21.442 1.794 c
+21.59 1.948 21.766 2.066 21.972 2.147 c
+22.178 2.234 22.413 2.278 22.677 2.278 c
+22.901 2.278 23.104 2.249 23.28 2.19 c
+23.456 2.132 23.607 2.047 23.736 1.941 c
+23.861 1.841 23.963 1.72 24.044 1.573 c
+24.122 1.434 24.177 1.283 24.206 1.118 c
+23.295 1.073 l
+23.265 1.249 23.196 1.389 23.089 1.5 c
+22.99 1.606 22.847 1.661 22.662 1.661 c
+22.417 1.661 22.24 1.559 22.134 1.353 c
+22.024 1.154 21.972 0.867 21.972 0.485 c
+21.972 -0.309 22.207 -0.706 22.677 -0.706 c
+22.843 -0.706 22.986 -0.654 23.104 -0.544 c
+23.221 -0.437 23.295 -0.276 23.324 -0.058 c
+24.235 -0.103 l
+24.206 -0.272 24.151 -0.426 24.073 -0.573 c
+24.004 -0.72 23.901 -0.852 23.765 -0.97 c
+23.636 -1.08 23.478 -1.168 23.295 -1.234 c
+23.119 -1.294 22.913 -1.323 22.677 -1.323 c
+26.076 1.515 m
+26.194 1.786 26.344 1.985 26.532 2.103 c
+26.716 2.22 26.936 2.278 27.194 2.278 c
+27.399 2.278 27.568 2.242 27.708 2.176 c
+27.855 2.106 27.965 2.014 28.046 1.897 c
+28.135 1.779 28.193 1.636 28.222 1.47 c
+28.259 1.301 28.281 1.125 28.281 0.941 c
+28.281 -1.263 l
+27.37 -1.263 l
+27.37 0.736 l
+27.37 0.871 27.359 0.992 27.341 1.103 c
+27.329 1.209 27.304 1.297 27.267 1.367 c
+27.227 1.444 27.168 1.503 27.09 1.544 c
+27.021 1.58 26.929 1.602 26.811 1.602 c
+26.701 1.602 26.606 1.577 26.518 1.529 c
+26.429 1.478 26.348 1.411 26.282 1.324 c
+26.223 1.235 26.172 1.125 26.136 1 c
+26.106 0.882 26.091 0.75 26.091 0.603 c
+26.091 -1.263 l
+25.18 -1.263 l
+25.18 3.514 l
+26.091 3.514 l
+26.091 2.205 l
+26.091 2.135 26.084 2.066 26.076 1.999 c
+26.076 1.794 l
+26.076 1.735 26.069 1.679 26.061 1.632 c
+26.061 1.515 l
+h
+30.799 -1.323 m
+30.541 -1.323 30.313 -1.286 30.107 -1.22 c
+29.902 -1.143 29.725 -1.028 29.579 -0.881 c
+29.432 -0.727 29.314 -0.536 29.226 -0.309 c
+29.145 -0.085 29.108 0.181 29.108 0.485 c
+29.108 0.816 29.153 1.095 29.24 1.324 c
+29.336 1.559 29.465 1.742 29.623 1.881 c
+29.788 2.018 29.976 2.117 30.181 2.176 c
+30.387 2.242 30.597 2.278 30.813 2.278 c
+31.085 2.278 31.32 2.227 31.518 2.132 c
+31.725 2.043 31.89 1.912 32.019 1.735 c
+32.154 1.565 32.254 1.36 32.313 1.118 c
+32.378 0.882 32.415 0.618 32.415 0.324 c
+32.415 0.309 l
+30.049 0.309 l
+30.049 0.162 30.063 0.023 30.093 -0.103 c
+30.13 -0.231 30.185 -0.345 30.255 -0.44 c
+30.321 -0.529 30.406 -0.598 30.504 -0.646 c
+30.6 -0.698 30.714 -0.72 30.843 -0.72 c
+30.997 -0.72 31.137 -0.687 31.254 -0.617 c
+31.379 -0.551 31.468 -0.448 31.518 -0.309 c
+32.357 -0.382 l
+32.328 -0.481 32.272 -0.588 32.195 -0.706 c
+32.114 -0.816 32.011 -0.918 31.886 -1.014 c
+31.769 -1.103 31.615 -1.176 31.431 -1.234 c
+31.254 -1.294 31.041 -1.323 30.799 -1.323 c
+30.799 1.706 m
+30.71 1.706 30.622 1.691 30.534 1.661 c
+30.446 1.632 30.365 1.58 30.298 1.515 c
+30.229 1.444 30.17 1.357 30.122 1.249 c
+30.082 1.139 30.063 1.014 30.063 0.867 c
+31.534 0.867 l
+31.534 1.004 31.508 1.125 31.46 1.235 c
+31.42 1.341 31.364 1.43 31.298 1.5 c
+31.239 1.565 31.166 1.617 31.078 1.646 c
+30.99 1.683 30.894 1.706 30.799 1.706 c
+34.863 -1.323 m
+34.576 -1.323 34.333 -1.282 34.128 -1.205 c
+33.922 -1.117 33.749 -0.995 33.614 -0.837 c
+33.473 -0.683 33.371 -0.496 33.305 -0.279 c
+33.235 -0.055 33.201 0.191 33.201 0.456 c
+33.201 0.75 33.235 1.008 33.305 1.235 c
+33.382 1.459 33.488 1.646 33.628 1.794 c
+33.775 1.948 33.951 2.066 34.157 2.147 c
+34.363 2.234 34.599 2.278 34.863 2.278 c
+35.087 2.278 35.289 2.249 35.466 2.19 c
+35.642 2.132 35.792 2.047 35.921 1.941 c
+36.046 1.841 36.149 1.72 36.23 1.573 c
+36.307 1.434 36.362 1.283 36.392 1.118 c
+35.48 1.073 l
+35.451 1.249 35.381 1.389 35.274 1.5 c
+35.175 1.606 35.032 1.661 34.848 1.661 c
+34.602 1.661 34.425 1.559 34.319 1.353 c
+34.209 1.154 34.157 0.867 34.157 0.485 c
+34.157 -0.309 34.393 -0.706 34.863 -0.706 c
+35.028 -0.706 35.171 -0.654 35.289 -0.544 c
+35.407 -0.437 35.48 -0.276 35.509 -0.058 c
+36.421 -0.103 l
+36.392 -0.272 36.336 -0.426 36.259 -0.573 c
+36.189 -0.72 36.087 -0.852 35.95 -0.97 c
+35.822 -1.08 35.664 -1.168 35.48 -1.234 c
+35.304 -1.294 35.098 -1.323 34.863 -1.323 c
+39.688 -1.263 m
+38.761 0.309 l
+38.365 0.044 l
+38.365 -1.263 l
+37.469 -1.263 l
+37.469 3.514 l
+38.365 3.514 l
+38.365 0.779 l
+39.615 2.22 l
+40.584 2.22 l
+39.364 0.853 l
+40.673 -1.263 l
+h
+44.631 0.485 m
+44.631 0.21 44.594 -0.04 44.527 -0.264 c
+44.457 -0.481 44.355 -0.669 44.219 -0.823 c
+44.079 -0.981 43.902 -1.103 43.69 -1.19 c
+43.473 -1.278 43.219 -1.323 42.925 -1.323 c
+42.649 -1.323 42.404 -1.278 42.19 -1.19 c
+41.984 -1.103 41.812 -0.981 41.676 -0.823 c
+41.536 -0.669 41.433 -0.481 41.367 -0.264 c
+41.297 -0.04 41.265 0.21 41.265 0.485 c
+41.265 0.738 41.294 0.974 41.352 1.191 c
+41.419 1.415 41.521 1.606 41.662 1.764 c
+41.797 1.929 41.974 2.058 42.19 2.147 c
+42.404 2.234 42.661 2.278 42.955 2.278 c
+43.267 2.278 43.528 2.234 43.734 2.147 c
+43.947 2.058 44.12 1.929 44.249 1.764 c
+44.384 1.606 44.484 1.415 44.542 1.191 c
+44.601 0.974 44.631 0.738 44.631 0.485 c
+43.675 0.485 m
+43.675 0.691 43.661 0.867 43.631 1.014 c
+43.609 1.162 43.572 1.283 43.513 1.382 c
+43.455 1.478 43.381 1.548 43.293 1.588 c
+43.204 1.636 43.094 1.661 42.969 1.661 c
+42.705 1.661 42.514 1.562 42.396 1.367 c
+42.279 1.18 42.219 0.886 42.219 0.485 c
+42.219 0.063 42.279 -0.243 42.396 -0.426 c
+42.514 -0.613 42.69 -0.706 42.925 -0.706 c
+43.05 -0.706 43.164 -0.687 43.264 -0.646 c
+43.359 -0.598 43.44 -0.525 43.499 -0.426 c
+43.565 -0.33 43.609 -0.205 43.631 -0.058 c
+43.661 0.088 43.675 0.268 43.675 0.485 c
+46.368 2.22 m
+46.368 0.265 l
+46.368 0.125 46.376 0 46.398 -0.118 c
+46.416 -0.228 46.449 -0.32 46.501 -0.397 c
+46.549 -0.478 46.607 -0.54 46.678 -0.588 c
+46.743 -0.628 46.828 -0.646 46.927 -0.646 c
+47.015 -0.646 47.096 -0.628 47.177 -0.588 c
+47.266 -0.54 47.339 -0.47 47.397 -0.382 c
+47.457 -0.286 47.501 -0.176 47.53 -0.058 c
+47.567 0.067 47.588 0.206 47.588 0.353 c
+47.588 2.22 l
+48.485 2.22 l
+48.485 -0.484 l
+48.485 -0.72 l
+48.492 -0.801 48.5 -0.878 48.5 -0.955 c
+48.5 -1.146 l
+48.507 -1.198 48.515 -1.234 48.515 -1.263 c
+47.662 -1.263 l
+47.651 -1.234 47.64 -1.198 47.632 -1.146 c
+47.632 -0.955 l
+47.632 -0.889 47.625 -0.819 47.618 -0.75 c
+47.618 -0.573 l
+47.603 -0.573 l
+47.486 -0.837 47.331 -1.028 47.148 -1.146 c
+46.971 -1.263 46.769 -1.323 46.545 -1.323 c
+46.339 -1.323 46.167 -1.286 46.03 -1.22 c
+45.891 -1.153 45.78 -1.058 45.693 -0.941 c
+45.612 -0.823 45.552 -0.687 45.516 -0.529 c
+45.487 -0.363 45.472 -0.187 45.472 0 c
+45.472 2.22 l
+h
+50.15 1.602 m
+49.606 1.602 l
+49.606 2.22 l
+50.194 2.22 l
+50.474 3.117 l
+51.046 3.117 l
+51.046 2.22 l
+52.282 2.22 l
+52.282 1.602 l
+51.046 1.602 l
+51.046 -0.103 l
+51.046 -0.323 l
+51.054 -0.393 51.076 -0.455 51.106 -0.515 c
+51.142 -0.565 51.197 -0.61 51.267 -0.646 c
+51.345 -0.675 51.458 -0.691 51.605 -0.691 c
+51.741 -0.691 51.877 -0.687 52.016 -0.675 c
+52.153 -0.658 52.285 -0.632 52.413 -0.602 c
+52.413 -1.205 l
+52.332 -1.216 52.255 -1.23 52.178 -1.249 c
+52.097 -1.261 52.02 -1.267 51.943 -1.278 c
+51.862 -1.286 51.775 -1.294 51.679 -1.294 c
+51.59 -1.301 51.491 -1.308 51.384 -1.308 c
+51.197 -1.308 51.035 -1.294 50.9 -1.263 c
+50.771 -1.227 50.657 -1.183 50.561 -1.132 c
+50.474 -1.084 50.4 -1.025 50.341 -0.955 c
+50.283 -0.878 50.238 -0.801 50.208 -0.72 c
+50.179 -0.632 50.157 -0.544 50.15 -0.455 c
+50.139 -0.36 50.135 -0.264 50.135 -0.176 c
+h
+58.422 -2.631 m
+58.422 3.514 l
+60.347 3.514 l
+60.347 2.896 l
+59.274 2.896 l
+59.274 -2.013 l
+60.347 -2.013 l
+60.347 -2.631 l
+h
+62.32 0.838 1.867 -0.794 re
+62.32 0.044 m
+65.793 -1.263 m
+65.793 -0.97 l
+65.801 -0.833 65.808 -0.675 65.808 -0.5 c
+65.808 3.514 l
+66.719 3.514 l
+66.719 2.234 l
+66.719 2.072 l
+66.719 1.897 l
+66.719 1.845 66.712 1.801 66.705 1.764 c
+66.705 1.676 l
+66.719 1.676 l
+66.767 1.783 66.829 1.874 66.91 1.955 c
+66.987 2.032 67.072 2.095 67.16 2.147 c
+67.249 2.194 67.34 2.227 67.44 2.249 c
+67.535 2.267 67.634 2.278 67.733 2.278 c
+67.947 2.278 68.134 2.234 68.292 2.147 c
+68.446 2.058 68.575 1.929 68.674 1.764 c
+68.781 1.606 68.858 1.415 68.909 1.191 c
+68.957 0.974 68.983 0.736 68.983 0.47 c
+68.983 0.214 68.953 -0.025 68.895 -0.249 c
+68.836 -0.467 68.751 -0.658 68.645 -0.823 c
+68.535 -0.981 68.402 -1.103 68.248 -1.19 c
+68.09 -1.278 67.91 -1.323 67.704 -1.323 c
+67.605 -1.323 67.506 -1.311 67.411 -1.294 c
+67.322 -1.271 67.234 -1.242 67.145 -1.19 c
+67.058 -1.132 66.977 -1.066 66.91 -0.985 c
+66.84 -0.908 66.778 -0.808 66.719 -0.691 c
+66.705 -0.691 l
+66.705 -0.852 l
+66.705 -0.911 66.697 -0.97 66.69 -1.028 c
+66.69 -1.08 66.682 -1.128 66.675 -1.176 c
+66.675 -1.216 66.668 -1.246 66.661 -1.263 c
+h
+66.705 0.5 m
+66.705 0.265 66.723 0.067 66.763 -0.087 c
+66.811 -0.246 66.87 -0.367 66.94 -0.455 c
+67.006 -0.544 67.079 -0.61 67.16 -0.646 c
+67.237 -0.687 67.315 -0.706 67.396 -0.706 c
+67.602 -0.706 67.756 -0.61 67.866 -0.411 c
+67.983 -0.216 68.043 0.077 68.043 0.47 c
+68.043 0.684 68.02 0.867 67.983 1.014 c
+67.954 1.168 67.91 1.294 67.851 1.382 c
+67.8 1.478 67.733 1.551 67.646 1.602 c
+67.565 1.65 67.476 1.676 67.38 1.676 c
+67.3 1.676 67.222 1.654 67.145 1.617 c
+67.064 1.577 66.991 1.515 66.925 1.426 c
+66.867 1.338 66.811 1.213 66.763 1.058 c
+66.723 0.912 66.705 0.724 66.705 0.5 c
+70.221 -2.631 m
+70.221 -2.013 l
+71.295 -2.013 l
+71.295 2.896 l
+70.221 2.896 l
+70.221 3.514 l
+72.147 3.514 l
+72.147 -2.631 l
+h
+78.729 -2.631 m
+78.729 3.514 l
+80.654 3.514 l
+80.654 2.896 l
+79.581 2.896 l
+79.581 -2.013 l
+80.654 -2.013 l
+80.654 -2.631 l
+h
+84.23 -1.263 m
+84.23 0.721 l
+84.23 1.022 84.186 1.243 84.097 1.382 c
+84.017 1.529 83.88 1.602 83.685 1.602 c
+83.575 1.602 83.473 1.577 83.377 1.529 c
+83.289 1.478 83.208 1.411 83.142 1.324 c
+83.083 1.235 83.031 1.125 82.995 1 c
+82.966 0.882 82.951 0.75 82.951 0.603 c
+82.951 -1.263 l
+82.039 -1.263 l
+82.039 1.441 l
+82.039 1.661 l
+82.039 1.75 82.032 1.827 82.025 1.897 c
+82.025 2.087 l
+82.025 2.22 l
+82.877 2.22 l
+82.885 2.19 82.892 2.147 82.892 2.087 c
+82.892 1.897 l
+82.899 1.827 82.906 1.756 82.906 1.691 c
+82.914 1.621 82.922 1.565 82.922 1.529 c
+82.937 1.529 l
+83.054 1.794 83.205 1.985 83.392 2.103 c
+83.575 2.22 83.796 2.278 84.053 2.278 c
+84.237 2.278 84.398 2.249 84.539 2.19 c
+84.674 2.132 84.788 2.043 84.876 1.926 c
+84.965 1.808 85.027 1.665 85.067 1.5 c
+85.115 1.341 85.141 1.154 85.141 0.941 c
+85.141 -1.263 l
+h
+87.055 -1.323 m
+86.887 -1.323 86.736 -1.301 86.6 -1.263 c
+86.471 -1.216 86.357 -1.146 86.262 -1.058 c
+86.174 -0.97 86.104 -0.864 86.056 -0.735 c
+86.004 -0.598 85.983 -0.448 85.983 -0.279 c
+85.983 -0.073 86.016 0.096 86.085 0.235 c
+86.152 0.383 86.247 0.493 86.365 0.574 c
+86.49 0.661 86.633 0.724 86.791 0.765 c
+86.956 0.802 87.132 0.827 87.32 0.838 c
+88.04 0.853 l
+88.04 1.029 l
+88.04 1.147 88.03 1.249 88.011 1.338 c
+87.989 1.426 87.955 1.492 87.908 1.544 c
+87.868 1.602 87.82 1.639 87.761 1.661 c
+87.702 1.679 87.636 1.691 87.57 1.691 c
+87.5 1.691 87.438 1.679 87.379 1.661 c
+87.328 1.65 87.28 1.625 87.232 1.588 c
+87.192 1.559 87.159 1.507 87.129 1.441 c
+87.107 1.382 87.093 1.301 87.085 1.206 c
+86.144 1.249 l
+86.174 1.397 86.218 1.532 86.276 1.661 c
+86.343 1.786 86.438 1.897 86.556 1.985 c
+86.673 2.08 86.813 2.153 86.982 2.205 c
+87.159 2.253 87.365 2.278 87.6 2.278 c
+88.04 2.278 88.371 2.168 88.599 1.955 c
+88.834 1.75 88.952 1.441 88.952 1.029 c
+88.952 -0.235 l
+88.952 -0.455 l
+88.959 -0.515 88.974 -0.569 88.996 -0.617 c
+89.014 -0.658 89.044 -0.691 89.084 -0.72 c
+89.121 -0.742 89.172 -0.75 89.231 -0.75 c
+89.297 -0.75 89.366 -0.746 89.437 -0.735 c
+89.437 -1.22 l
+89.378 -1.23 89.323 -1.242 89.275 -1.249 c
+89.235 -1.261 89.194 -1.267 89.158 -1.278 c
+89.117 -1.286 89.073 -1.294 89.025 -1.294 c
+88.974 -1.301 88.915 -1.308 88.849 -1.308 c
+88.62 -1.308 88.456 -1.257 88.349 -1.146 c
+88.239 -1.028 88.176 -0.864 88.158 -0.646 c
+88.143 -0.646 l
+88.073 -0.756 88.003 -0.852 87.938 -0.941 c
+87.868 -1.022 87.791 -1.087 87.702 -1.146 c
+87.614 -1.205 87.515 -1.249 87.408 -1.278 c
+87.309 -1.308 87.192 -1.323 87.055 -1.323 c
+88.04 0.353 m
+87.614 0.339 l
+87.515 0.339 87.423 0.331 87.334 0.324 c
+87.254 0.312 87.188 0.287 87.129 0.25 c
+87.07 0.21 87.018 0.151 86.982 0.073 c
+86.941 0.004 86.923 -0.087 86.923 -0.205 c
+86.923 -0.374 86.956 -0.496 87.026 -0.573 c
+87.093 -0.654 87.192 -0.691 87.32 -0.691 c
+87.427 -0.691 87.525 -0.669 87.614 -0.617 c
+87.71 -0.569 87.791 -0.507 87.849 -0.426 c
+87.915 -0.349 87.967 -0.261 87.996 -0.162 c
+88.026 -0.055 88.04 0.059 88.04 0.177 c
+h
+91.322 -1.263 m
+91.322 0.853 l
+91.322 1.018 91.315 1.154 91.307 1.264 c
+91.296 1.371 91.278 1.455 91.248 1.515 c
+91.226 1.58 91.197 1.632 91.16 1.661 c
+91.13 1.691 91.091 1.706 91.043 1.706 c
+90.984 1.706 90.929 1.676 90.881 1.617 c
+90.841 1.565 90.808 1.492 90.778 1.397 c
+90.748 1.309 90.723 1.195 90.704 1.058 c
+90.694 0.919 90.69 0.769 90.69 0.603 c
+90.69 -1.263 l
+89.94 -1.263 l
+89.94 1.47 l
+89.94 1.706 l
+89.94 1.926 l
+89.94 2.003 89.933 2.066 89.925 2.117 c
+89.925 2.22 l
+90.602 2.22 l
+90.602 2.132 l
+90.602 1.985 l
+90.609 1.926 90.617 1.867 90.617 1.808 c
+90.617 1.646 l
+90.631 1.646 l
+90.65 1.735 90.679 1.812 90.719 1.881 c
+90.756 1.959 90.8 2.029 90.852 2.087 c
+90.91 2.147 90.976 2.19 91.057 2.22 c
+91.134 2.257 91.223 2.278 91.322 2.278 c
+91.506 2.278 91.645 2.224 91.733 2.117 c
+91.829 2.018 91.899 1.86 91.939 1.646 c
+91.953 1.646 l
+91.991 1.742 92.032 1.831 92.071 1.912 c
+92.119 1.989 92.175 2.051 92.233 2.103 c
+92.292 2.161 92.358 2.205 92.439 2.234 c
+92.516 2.264 92.604 2.278 92.703 2.278 c
+92.84 2.278 92.954 2.253 93.042 2.205 c
+93.129 2.153 93.196 2.08 93.247 1.985 c
+93.306 1.885 93.343 1.756 93.365 1.602 c
+93.395 1.455 93.409 1.272 93.409 1.058 c
+93.409 -1.263 l
+92.689 -1.263 l
+92.689 0.853 l
+92.689 1.018 92.682 1.154 92.674 1.264 c
+92.663 1.371 92.645 1.455 92.616 1.515 c
+92.593 1.58 92.564 1.632 92.527 1.661 c
+92.498 1.691 92.458 1.706 92.41 1.706 c
+92.292 1.706 92.196 1.617 92.13 1.441 c
+92.071 1.272 92.042 1.014 92.042 0.661 c
+92.042 -1.263 l
+h
+95.783 -1.323 m
+95.525 -1.323 95.298 -1.286 95.092 -1.22 c
+94.887 -1.143 94.71 -1.028 94.563 -0.881 c
+94.416 -0.727 94.299 -0.536 94.21 -0.309 c
+94.129 -0.085 94.093 0.181 94.093 0.485 c
+94.093 0.816 94.137 1.095 94.225 1.324 c
+94.32 1.559 94.449 1.742 94.607 1.881 c
+94.773 2.018 94.96 2.117 95.166 2.176 c
+95.371 2.242 95.581 2.278 95.797 2.278 c
+96.069 2.278 96.304 2.227 96.503 2.132 c
+96.709 2.043 96.875 1.912 97.003 1.735 c
+97.139 1.565 97.239 1.36 97.297 1.118 c
+97.363 0.882 97.4 0.618 97.4 0.324 c
+97.4 0.309 l
+95.034 0.309 l
+95.034 0.162 95.049 0.023 95.078 -0.103 c
+95.114 -0.231 95.169 -0.345 95.24 -0.44 c
+95.305 -0.529 95.39 -0.598 95.489 -0.646 c
+95.585 -0.698 95.699 -0.72 95.828 -0.72 c
+95.982 -0.72 96.121 -0.687 96.239 -0.617 c
+96.364 -0.551 96.452 -0.448 96.503 -0.309 c
+97.341 -0.382 l
+97.312 -0.481 97.256 -0.588 97.179 -0.706 c
+97.098 -0.816 96.996 -0.918 96.871 -1.014 c
+96.753 -1.103 96.599 -1.176 96.415 -1.234 c
+96.239 -1.294 96.026 -1.323 95.783 -1.323 c
+95.783 1.706 m
+95.695 1.706 95.606 1.691 95.519 1.661 c
+95.43 1.632 95.35 1.58 95.284 1.515 c
+95.213 1.444 95.155 1.357 95.107 1.249 c
+95.066 1.139 95.049 1.014 95.049 0.867 c
+96.518 0.867 l
+96.518 1.004 96.493 1.125 96.445 1.235 c
+96.404 1.341 96.349 1.43 96.283 1.5 c
+96.224 1.565 96.15 1.617 96.063 1.646 c
+95.974 1.683 95.878 1.706 95.783 1.706 c
+98.653 -2.631 m
+98.653 -2.013 l
+99.726 -2.013 l
+99.726 2.896 l
+98.653 2.896 l
+98.653 3.514 l
+100.578 3.514 l
+100.578 -2.631 l
+h
+f
+Q
+q 1 0 0 1 545.9966 173.8592 cm
+0 0 m
+0 0.264 -0.073 0.463 -0.22 0.603 c
+-0.36 0.75 -0.617 0.889 -0.999 1.029 c
+-1.374 1.165 -1.66 1.309 -1.866 1.455 c
+-2.065 1.602 -2.212 1.768 -2.308 1.955 c
+-2.406 2.15 -2.454 2.37 -2.454 2.616 c
+-2.454 3.035 -2.315 3.385 -2.028 3.66 c
+-1.745 3.932 -1.378 4.072 -0.926 4.072 c
+-0.613 4.072 -0.334 3.998 -0.088 3.851 c
+0.154 3.711 0.342 3.516 0.47 3.263 c
+0.607 3.017 0.676 2.749 0.676 2.454 c
+0 2.454 l
+0 2.786 -0.081 3.042 -0.235 3.219 c
+-0.393 3.403 -0.625 3.499 -0.926 3.499 c
+-1.19 3.499 -1.404 3.418 -1.558 3.263 c
+-1.705 3.116 -1.778 2.903 -1.778 2.631 c
+-1.778 2.404 -1.701 2.212 -1.543 2.057 c
+-1.378 1.911 -1.124 1.771 -0.779 1.646 c
+-0.261 1.477 0.11 1.268 0.339 1.014 c
+0.574 0.757 0.691 0.426 0.691 0.015 c
+0.691 -0.426 0.548 -0.779 0.264 -1.043 c
+-0.022 -1.301 -0.404 -1.426 -0.881 -1.426 c
+-1.198 -1.426 -1.484 -1.357 -1.749 -1.22 c
+-2.013 -1.084 -2.227 -0.893 -2.381 -0.647 c
+-2.528 -0.405 -2.601 -0.118 -2.601 0.206 c
+-1.926 0.206 l
+-1.926 -0.129 -1.833 -0.389 -1.646 -0.573 c
+-1.462 -0.761 -1.205 -0.852 -0.881 -0.852 c
+-0.588 -0.852 -0.367 -0.779 -0.22 -0.632 c
+-0.073 -0.478 0 -0.264 0 0 c
+4.439 -0.206 m
+5.042 2.631 l
+5.689 2.631 l
+4.704 -1.353 l
+4.189 -1.353 l
+3.41 1.5 l
+2.66 -1.353 l
+2.132 -1.353 l
+1.176 2.631 l
+1.808 2.631 l
+2.425 -0.133 l
+3.161 2.631 l
+3.675 2.631 l
+h
+7.071 -1.353 -0.647 3.984 re
+7.115 3.675 m
+7.115 3.564 7.085 3.472 7.026 3.395 c
+6.967 3.326 6.872 3.293 6.747 3.293 c
+6.629 3.293 6.534 3.326 6.468 3.395 c
+6.409 3.472 6.379 3.564 6.379 3.675 c
+6.379 3.792 6.409 3.884 6.468 3.954 c
+6.534 4.031 6.629 4.072 6.747 4.072 c
+6.872 4.072 6.967 4.031 7.026 3.954 c
+7.085 3.873 7.115 3.781 7.115 3.675 c
+8.937 3.586 m
+8.937 2.631 l
+9.54 2.631 l
+9.54 2.102 l
+8.937 2.102 l
+8.937 -0.368 l
+8.937 -0.526 8.96 -0.643 9.01 -0.721 c
+9.07 -0.801 9.158 -0.838 9.276 -0.838 c
+9.363 -0.838 9.452 -0.823 9.54 -0.794 c
+9.54 -1.353 l
+9.393 -1.4 9.239 -1.426 9.084 -1.426 c
+8.827 -1.426 8.632 -1.334 8.496 -1.147 c
+8.357 -0.963 8.291 -0.702 8.291 -0.368 c
+8.291 2.102 l
+7.688 2.102 l
+7.688 2.631 l
+8.291 2.631 l
+8.291 3.586 l
+h
+11.686 -0.867 m
+11.899 -0.867 12.072 -0.805 12.201 -0.676 c
+12.337 -0.54 12.41 -0.349 12.421 -0.103 c
+13.039 -0.103 l
+13.016 -0.485 12.881 -0.805 12.627 -1.058 c
+12.37 -1.305 12.057 -1.426 11.686 -1.426 c
+11.194 -1.426 10.818 -1.276 10.554 -0.97 c
+10.297 -0.658 10.172 -0.191 10.172 0.426 c
+10.172 0.867 l
+10.172 1.463 10.297 1.918 10.554 2.234 c
+10.818 2.547 11.194 2.705 11.686 2.705 c
+12.087 2.705 12.406 2.572 12.642 2.308 c
+12.884 2.051 13.016 1.705 13.039 1.264 c
+12.421 1.264 l
+12.399 1.558 12.326 1.779 12.201 1.926 c
+12.083 2.072 11.91 2.146 11.686 2.146 c
+11.392 2.146 11.175 2.047 11.04 1.852 c
+10.899 1.664 10.826 1.356 10.818 0.926 c
+10.818 0.411 l
+10.818 -0.059 10.885 -0.393 11.025 -0.588 c
+11.171 -0.775 11.392 -0.867 11.686 -0.867 c
+14.435 2.219 m
+14.689 2.543 15.008 2.705 15.391 2.705 c
+16.096 2.705 16.452 2.234 16.463 1.294 c
+16.463 -1.353 l
+15.817 -1.353 l
+15.817 1.264 l
+15.817 1.577 15.761 1.797 15.655 1.926 c
+15.545 2.051 15.391 2.117 15.185 2.117 c
+15.026 2.117 14.88 2.061 14.743 1.955 c
+14.614 1.845 14.512 1.708 14.435 1.544 c
+14.435 -1.353 l
+13.787 -1.353 l
+13.787 4.292 l
+14.435 4.292 l
+h
+f
+Q
+q 1 0 0 1 567.9281 174.2562 cm
+0 0 m
+0.353 2.234 l
+1.352 2.234 l
+0.529 -1.75 l
+-0.339 -1.75 l
+-0.897 0.559 l
+-1.455 -1.75 l
+-2.323 -1.75 l
+-3.146 2.234 l
+-2.147 2.234 l
+-1.794 0 l
+-1.264 2.234 l
+-0.53 2.234 l
+h
+1.749 0.367 m
+1.749 0.974 1.889 1.448 2.175 1.793 c
+2.458 2.135 2.851 2.308 3.351 2.308 c
+3.858 2.308 4.255 2.135 4.542 1.793 c
+4.825 1.448 4.968 0.974 4.968 0.367 c
+4.968 0.103 l
+4.968 -0.497 4.825 -0.967 4.542 -1.309 c
+4.255 -1.654 3.858 -1.823 3.351 -1.823 c
+2.84 -1.823 2.443 -1.654 2.161 -1.309 c
+1.885 -0.967 1.749 -0.493 1.749 0.118 c
+h
+2.792 0.103 m
+2.792 -0.603 2.977 -0.956 3.351 -0.956 c
+3.704 -0.956 3.895 -0.661 3.925 -0.073 c
+3.925 0.367 l
+3.925 0.727 3.873 0.999 3.777 1.176 c
+3.678 1.352 3.535 1.44 3.351 1.44 c
+3.175 1.44 3.035 1.352 2.94 1.176 c
+2.84 0.999 2.792 0.727 2.792 0.367 c
+h
+7.57 1.22 m
+7.231 1.249 l
+6.945 1.249 6.754 1.124 6.659 0.881 c
+6.659 -1.75 l
+5.614 -1.75 l
+5.614 2.234 l
+6.585 2.234 l
+6.614 1.793 l
+6.78 2.135 7.011 2.308 7.305 2.308 c
+7.422 2.308 7.515 2.285 7.584 2.248 c
+h
+9.437 -0.221 m
+9.157 -0.53 l
+9.157 -1.75 l
+8.114 -1.75 l
+8.114 3.895 l
+9.157 3.895 l
+9.157 0.852 l
+9.275 1.043 l
+9.995 2.234 l
+11.245 2.234 l
+10.083 0.588 l
+11.347 -1.75 l
+10.157 -1.75 l
+h
+12.802 -1.75 -1.043 3.984 re
+11.715 3.263 m
+11.715 3.418 11.763 3.546 11.862 3.645 c
+11.968 3.752 12.104 3.807 12.274 3.807 c
+12.45 3.807 12.586 3.752 12.685 3.645 c
+12.791 3.546 12.847 3.418 12.847 3.263 c
+12.847 3.094 12.791 2.958 12.685 2.851 c
+12.586 2.753 12.45 2.705 12.274 2.705 c
+12.104 2.705 11.968 2.753 11.862 2.851 c
+11.763 2.958 11.715 3.094 11.715 3.263 c
+14.566 2.234 m
+14.596 1.837 l
+14.831 2.15 15.133 2.308 15.507 2.308 c
+16.191 2.308 16.544 1.826 16.565 0.867 c
+16.565 -1.75 l
+15.522 -1.75 l
+15.522 0.794 l
+15.522 1.018 15.485 1.18 15.419 1.278 c
+15.349 1.374 15.232 1.425 15.066 1.425 c
+14.879 1.425 14.732 1.33 14.626 1.147 c
+14.626 -1.75 l
+13.581 -1.75 l
+13.581 2.234 l
+h
+17.183 0.367 m
+17.183 1.014 17.3 1.5 17.535 1.822 c
+17.771 2.146 18.102 2.308 18.535 2.308 c
+18.888 2.308 19.16 2.165 19.359 1.881 c
+19.403 2.234 l
+20.344 2.234 l
+20.344 -1.75 l
+20.344 -2.257 20.2 -2.646 19.917 -2.911 c
+19.63 -3.183 19.226 -3.322 18.697 -3.322 c
+18.47 -3.322 18.234 -3.278 17.992 -3.19 c
+17.756 -3.102 17.58 -2.988 17.462 -2.851 c
+17.815 -2.132 l
+17.911 -2.238 18.039 -2.323 18.197 -2.381 c
+18.352 -2.448 18.499 -2.484 18.638 -2.484 c
+18.873 -2.484 19.039 -2.425 19.137 -2.308 c
+19.245 -2.198 19.299 -2.022 19.299 -1.779 c
+19.299 -1.426 l
+19.101 -1.691 18.844 -1.823 18.52 -1.823 c
+18.098 -1.823 17.771 -1.661 17.535 -1.338 c
+17.308 -1.008 17.19 -0.537 17.183 0.073 c
+h
+18.227 0.103 m
+18.227 -0.272 18.274 -0.54 18.374 -0.706 c
+18.47 -0.875 18.624 -0.956 18.829 -0.956 c
+19.042 -0.956 19.2 -0.879 19.299 -0.721 c
+19.299 1.176 l
+19.189 1.341 19.035 1.425 18.829 1.425 c
+18.624 1.425 18.47 1.341 18.374 1.176 c
+18.274 1.007 18.227 0.738 18.227 0.367 c
+h
+22.695 0.367 m
+22.695 1.014 22.802 1.5 23.018 1.822 c
+23.243 2.146 23.566 2.308 23.989 2.308 c
+24.301 2.308 24.554 2.175 24.753 1.911 c
+24.753 3.895 l
+25.811 3.895 l
+25.811 -1.75 l
+24.856 -1.75 l
+24.812 -1.338 l
+24.595 -1.661 24.319 -1.823 23.989 -1.823 c
+23.577 -1.823 23.257 -1.669 23.033 -1.353 c
+22.817 -1.029 22.703 -0.559 22.695 0.058 c
+h
+23.739 0.103 m
+23.739 -0.291 23.775 -0.566 23.856 -0.721 c
+23.945 -0.879 24.091 -0.956 24.297 -0.956 c
+24.503 -0.956 24.654 -0.864 24.753 -0.676 c
+24.753 1.132 l
+24.654 1.326 24.503 1.425 24.297 1.425 c
+24.099 1.425 23.959 1.344 23.871 1.19 c
+23.783 1.043 23.739 0.771 23.739 0.382 c
+h
+27.663 -1.75 -1.043 3.984 re
+26.576 3.263 m
+26.576 3.418 26.624 3.546 26.723 3.645 c
+26.829 3.752 26.965 3.807 27.134 3.807 c
+27.311 3.807 27.446 3.752 27.546 3.645 c
+27.652 3.546 27.708 3.418 27.708 3.263 c
+27.708 3.094 27.652 2.958 27.546 2.851 c
+27.446 2.753 27.311 2.705 27.134 2.705 c
+26.965 2.705 26.829 2.753 26.723 2.851 c
+26.624 2.958 26.576 3.094 26.576 3.263 c
+30.426 1.22 m
+30.089 1.249 l
+29.802 1.249 29.611 1.124 29.516 0.881 c
+29.516 -1.75 l
+28.471 -1.75 l
+28.471 2.234 l
+29.442 2.234 l
+29.471 1.793 l
+29.637 2.135 29.868 2.308 30.162 2.308 c
+30.28 2.308 30.372 2.285 30.441 2.248 c
+h
+32.5 -1.823 m
+31.97 -1.823 31.551 -1.669 31.249 -1.353 c
+30.956 -1.029 30.809 -0.57 30.809 0.029 c
+30.809 0.338 l
+30.809 0.962 30.945 1.448 31.22 1.793 c
+31.492 2.135 31.885 2.308 32.396 2.308 c
+32.896 2.308 33.267 2.146 33.514 1.822 c
+33.767 1.5 33.9 1.022 33.911 0.397 c
+33.911 -0.103 l
+31.837 -0.103 l
+31.856 -0.397 31.918 -0.614 32.029 -0.75 c
+32.147 -0.89 32.327 -0.956 32.573 -0.956 c
+32.915 -0.956 33.205 -0.838 33.44 -0.603 c
+33.852 -1.235 l
+33.723 -1.411 33.535 -1.555 33.293 -1.661 c
+33.047 -1.768 32.782 -1.823 32.5 -1.823 c
+31.852 0.617 m
+32.882 0.617 l
+32.882 0.72 l
+32.882 0.955 32.841 1.132 32.764 1.249 c
+32.694 1.374 32.566 1.44 32.382 1.44 c
+32.205 1.44 32.073 1.371 31.985 1.234 c
+31.904 1.106 31.86 0.9 31.852 0.617 c
+35.939 -0.956 m
+36.233 -0.956 36.383 -0.761 36.394 -0.368 c
+37.364 -0.368 l
+37.364 -0.802 37.233 -1.154 36.968 -1.426 c
+36.703 -1.691 36.365 -1.823 35.953 -1.823 c
+35.442 -1.823 35.05 -1.669 34.777 -1.353 c
+34.513 -1.029 34.374 -0.559 34.366 0.058 c
+34.366 0.382 l
+34.366 1.007 34.499 1.484 34.763 1.808 c
+35.035 2.138 35.432 2.308 35.953 2.308 c
+36.383 2.308 36.726 2.167 36.982 1.896 c
+37.236 1.62 37.364 1.238 37.364 0.75 c
+36.394 0.75 l
+36.394 0.962 36.354 1.132 36.277 1.249 c
+36.207 1.374 36.09 1.44 35.924 1.44 c
+35.748 1.44 35.619 1.374 35.542 1.249 c
+35.461 1.12 35.417 0.871 35.409 0.5 c
+35.409 0.088 l
+35.409 -0.235 35.424 -0.463 35.454 -0.588 c
+35.49 -0.717 35.546 -0.809 35.615 -0.867 c
+35.693 -0.927 35.799 -0.956 35.939 -0.956 c
+39.172 3.204 m
+39.172 2.234 l
+39.702 2.234 l
+39.702 1.44 l
+39.172 1.44 l
+39.172 -0.53 l
+39.172 -0.688 39.191 -0.794 39.232 -0.852 c
+39.28 -0.912 39.363 -0.941 39.481 -0.941 c
+39.588 -0.941 39.673 -0.933 39.731 -0.912 c
+39.731 -1.72 l
+39.555 -1.786 39.363 -1.823 39.158 -1.823 c
+38.482 -1.823 38.137 -1.437 38.129 -0.661 c
+38.129 1.44 l
+37.674 1.44 l
+37.674 2.234 l
+38.129 2.234 l
+38.129 3.204 l
+h
+40.055 0.367 m
+40.055 0.974 40.194 1.448 40.481 1.793 c
+40.764 2.135 41.157 2.308 41.657 2.308 c
+42.164 2.308 42.561 2.135 42.848 1.793 c
+43.13 1.448 43.274 0.974 43.274 0.367 c
+43.274 0.103 l
+43.274 -0.497 43.13 -0.967 42.848 -1.309 c
+42.561 -1.654 42.164 -1.823 41.657 -1.823 c
+41.146 -1.823 40.749 -1.654 40.466 -1.309 c
+40.19 -0.967 40.055 -0.493 40.055 0.118 c
+h
+41.098 0.103 m
+41.098 -0.603 41.282 -0.956 41.657 -0.956 c
+42.01 -0.956 42.201 -0.661 42.23 -0.073 c
+42.23 0.367 l
+42.23 0.727 42.179 0.999 42.083 1.176 c
+41.984 1.352 41.84 1.44 41.657 1.44 c
+41.48 1.44 41.341 1.352 41.245 1.176 c
+41.146 0.999 41.098 0.727 41.098 0.367 c
+h
+45.875 1.22 m
+45.537 1.249 l
+45.25 1.249 45.059 1.124 44.964 0.881 c
+44.964 -1.75 l
+43.92 -1.75 l
+43.92 2.234 l
+44.89 2.234 l
+44.92 1.793 l
+45.085 2.135 45.317 2.308 45.611 2.308 c
+45.728 2.308 45.821 2.285 45.89 2.248 c
+h
+47.772 -0.015 m
+48.3 2.234 l
+49.403 2.234 l
+48.095 -2.352 l
+47.897 -3.017 47.529 -3.352 46.993 -3.352 c
+46.864 -3.352 46.721 -3.329 46.567 -3.293 c
+46.567 -2.469 l
+46.684 -2.484 l
+46.85 -2.484 46.97 -2.448 47.051 -2.381 c
+47.128 -2.323 47.191 -2.213 47.242 -2.058 c
+47.316 -1.793 l
+46.17 2.234 l
+47.286 2.234 l
+h
+f
+Q
+q 1 0 0 1 620.4617 177.4453 cm
+0 0 m
+0 -0.955 l
+0.603 -0.955 l
+0.603 -1.484 l
+0 -1.484 l
+0 -3.954 l
+0 -4.112 0.023 -4.23 0.074 -4.307 c
+0.133 -4.388 0.221 -4.424 0.339 -4.424 c
+0.427 -4.424 0.515 -4.409 0.603 -4.38 c
+0.603 -4.939 l
+0.456 -4.986 0.302 -5.012 0.148 -5.012 c
+-0.11 -5.012 -0.305 -4.92 -0.44 -4.733 c
+-0.58 -4.549 -0.646 -4.288 -0.646 -3.954 c
+-0.646 -1.484 l
+-1.249 -1.484 l
+-1.249 -0.955 l
+-0.646 -0.955 l
+-0.646 0 l
+h
+1.162 -2.763 m
+1.162 -2.186 1.297 -1.73 1.573 -1.396 c
+1.856 -1.055 2.228 -0.881 2.691 -0.881 c
+3.15 -0.881 3.517 -1.051 3.793 -1.381 c
+4.075 -1.705 4.223 -2.153 4.233 -2.719 c
+4.233 -3.145 l
+4.233 -3.715 4.09 -4.17 3.807 -4.512 c
+3.532 -4.847 3.165 -5.012 2.705 -5.012 c
+2.242 -5.012 1.871 -4.85 1.588 -4.527 c
+1.312 -4.197 1.169 -3.755 1.162 -3.204 c
+h
+1.808 -3.145 m
+1.808 -3.549 1.885 -3.866 2.043 -4.101 c
+2.209 -4.336 2.429 -4.453 2.705 -4.453 c
+3.271 -4.453 3.565 -4.041 3.587 -3.218 c
+3.587 -2.763 l
+3.587 -2.362 3.502 -2.042 3.337 -1.807 c
+3.179 -1.565 2.962 -1.44 2.691 -1.44 c
+2.426 -1.44 2.209 -1.565 2.043 -1.807 c
+1.885 -2.042 1.808 -2.362 1.808 -2.763 c
+h
+7.644 0 m
+7.644 -0.955 l
+8.247 -0.955 l
+8.247 -1.484 l
+7.644 -1.484 l
+7.644 -3.954 l
+7.644 -4.112 7.666 -4.23 7.718 -4.307 c
+7.776 -4.388 7.865 -4.424 7.982 -4.424 c
+8.071 -4.424 8.158 -4.409 8.247 -4.38 c
+8.247 -4.939 l
+8.1 -4.986 7.946 -5.012 7.791 -5.012 c
+7.533 -5.012 7.339 -4.92 7.203 -4.733 c
+7.063 -4.549 6.997 -4.288 6.997 -3.954 c
+6.997 -1.484 l
+6.394 -1.484 l
+6.394 -0.955 l
+6.997 -0.955 l
+6.997 0 l
+h
+9.658 -1.367 m
+9.912 -1.043 10.231 -0.881 10.613 -0.881 c
+11.319 -0.881 11.676 -1.352 11.686 -2.293 c
+11.686 -4.939 l
+11.04 -4.939 l
+11.04 -2.322 l
+11.04 -2.009 10.984 -1.789 10.878 -1.66 c
+10.768 -1.535 10.613 -1.469 10.407 -1.469 c
+10.249 -1.469 10.103 -1.525 9.966 -1.631 c
+9.838 -1.741 9.735 -1.878 9.658 -2.042 c
+9.658 -4.939 l
+9.011 -4.939 l
+9.011 0.706 l
+9.658 0.706 l
+h
+14.068 -5.012 m
+13.567 -5.012 13.185 -4.865 12.921 -4.571 c
+12.657 -4.277 12.524 -3.844 12.524 -3.262 c
+12.524 -2.792 l
+12.524 -2.198 12.649 -1.73 12.906 -1.396 c
+13.17 -1.055 13.531 -0.881 13.994 -0.881 c
+14.454 -0.881 14.795 -1.036 15.023 -1.337 c
+15.258 -1.631 15.379 -2.094 15.391 -2.719 c
+15.391 -3.145 l
+13.17 -3.145 l
+13.17 -3.233 l
+13.17 -3.667 13.248 -3.979 13.406 -4.174 c
+13.571 -4.361 13.803 -4.453 14.097 -4.453 c
+14.292 -4.453 14.464 -4.421 14.612 -4.351 c
+14.758 -4.274 14.894 -4.156 15.023 -3.998 c
+15.361 -4.409 l
+15.074 -4.814 14.645 -5.012 14.068 -5.012 c
+13.994 -1.44 m
+13.718 -1.44 13.517 -1.535 13.391 -1.72 c
+13.263 -1.907 13.189 -2.198 13.17 -2.587 c
+14.743 -2.587 l
+14.743 -2.499 l
+14.722 -2.117 14.656 -1.848 14.538 -1.69 c
+14.421 -1.525 14.236 -1.44 13.994 -1.44 c
+19.83 -3.924 m
+19.83 -3.777 19.774 -3.656 19.668 -3.557 c
+19.558 -3.461 19.352 -3.343 19.05 -3.204 c
+18.705 -3.057 18.462 -2.936 18.315 -2.836 c
+18.169 -2.73 18.058 -2.612 17.992 -2.484 c
+17.922 -2.358 17.889 -2.2 17.889 -2.013 c
+17.889 -1.69 18.007 -1.421 18.242 -1.205 c
+18.477 -0.992 18.778 -0.881 19.154 -0.881 c
+19.535 -0.881 19.844 -0.995 20.079 -1.219 c
+20.314 -1.448 20.432 -1.734 20.432 -2.087 c
+19.786 -2.087 l
+19.786 -1.911 19.726 -1.76 19.609 -1.631 c
+19.491 -1.506 19.337 -1.44 19.154 -1.44 c
+18.955 -1.44 18.805 -1.495 18.697 -1.602 c
+18.587 -1.701 18.536 -1.834 18.536 -1.999 c
+18.536 -2.127 18.573 -2.234 18.654 -2.322 c
+18.731 -2.403 18.922 -2.506 19.227 -2.63 c
+19.705 -2.818 20.035 -3.006 20.212 -3.189 c
+20.388 -3.366 20.476 -3.594 20.476 -3.866 c
+20.476 -4.218 20.351 -4.498 20.108 -4.704 c
+19.873 -4.909 19.558 -5.012 19.168 -5.012 c
+18.745 -5.012 18.407 -4.895 18.153 -4.659 c
+17.897 -4.417 17.772 -4.112 17.772 -3.748 c
+18.419 -3.748 l
+18.425 -3.976 18.496 -4.152 18.624 -4.277 c
+18.749 -4.394 18.932 -4.453 19.168 -4.453 c
+19.381 -4.453 19.543 -4.405 19.653 -4.307 c
+19.771 -4.211 19.83 -4.082 19.83 -3.924 c
+24.195 -3.145 m
+24.195 -3.773 24.077 -4.244 23.842 -4.556 c
+23.614 -4.862 23.298 -5.012 22.887 -5.012 c
+22.483 -5.012 22.174 -4.862 21.961 -4.556 c
+21.961 -6.468 l
+21.314 -6.468 l
+21.314 -0.955 l
+21.902 -0.955 l
+21.947 -1.396 l
+22.159 -1.055 22.468 -0.881 22.872 -0.881 c
+23.313 -0.881 23.64 -1.036 23.857 -1.337 c
+24.07 -1.643 24.185 -2.098 24.195 -2.705 c
+h
+23.549 -2.763 m
+23.549 -2.322 23.479 -1.999 23.343 -1.793 c
+23.203 -1.58 22.982 -1.469 22.681 -1.469 c
+22.365 -1.469 22.126 -1.624 21.961 -1.925 c
+21.961 -3.998 l
+22.126 -4.303 22.365 -4.453 22.681 -4.453 c
+22.975 -4.453 23.188 -4.351 23.328 -4.145 c
+23.464 -3.931 23.537 -3.601 23.549 -3.16 c
+h
+26.458 -5.012 m
+25.959 -5.012 25.577 -4.865 25.313 -4.571 c
+25.047 -4.277 24.916 -3.844 24.916 -3.262 c
+24.916 -2.792 l
+24.916 -2.198 25.041 -1.73 25.297 -1.396 c
+25.562 -1.055 25.922 -0.881 26.385 -0.881 c
+26.845 -0.881 27.187 -1.036 27.414 -1.337 c
+27.649 -1.631 27.771 -2.094 27.782 -2.719 c
+27.782 -3.145 l
+25.562 -3.145 l
+25.562 -3.233 l
+25.562 -3.667 25.639 -3.979 25.797 -4.174 c
+25.963 -4.361 26.194 -4.453 26.488 -4.453 c
+26.683 -4.453 26.855 -4.421 27.003 -4.351 c
+27.15 -4.274 27.285 -4.156 27.414 -3.998 c
+27.752 -4.409 l
+27.466 -4.814 27.036 -5.012 26.458 -5.012 c
+26.385 -1.44 m
+26.109 -1.44 25.908 -1.535 25.783 -1.72 c
+25.654 -1.907 25.581 -2.198 25.562 -2.587 c
+27.135 -2.587 l
+27.135 -2.499 l
+27.113 -2.117 27.046 -1.848 26.929 -1.69 c
+26.811 -1.525 26.628 -1.44 26.385 -1.44 c
+29.943 -4.453 m
+30.155 -4.453 30.329 -4.391 30.456 -4.262 c
+30.593 -4.126 30.666 -3.935 30.678 -3.689 c
+31.295 -3.689 l
+31.273 -4.072 31.137 -4.391 30.884 -4.644 c
+30.626 -4.891 30.313 -5.012 29.943 -5.012 c
+29.45 -5.012 29.076 -4.862 28.81 -4.556 c
+28.553 -4.244 28.428 -3.777 28.428 -3.16 c
+28.428 -2.719 l
+28.428 -2.123 28.553 -1.668 28.81 -1.352 c
+29.076 -1.04 29.45 -0.881 29.943 -0.881 c
+30.343 -0.881 30.662 -1.014 30.898 -1.278 c
+31.14 -1.535 31.273 -1.881 31.295 -2.322 c
+30.678 -2.322 l
+30.655 -2.028 30.582 -1.807 30.456 -1.66 c
+30.339 -1.514 30.167 -1.44 29.943 -1.44 c
+29.648 -1.44 29.432 -1.539 29.296 -1.734 c
+29.156 -1.922 29.083 -2.23 29.076 -2.66 c
+29.076 -3.175 l
+29.076 -3.645 29.141 -3.979 29.281 -4.174 c
+29.428 -4.361 29.648 -4.453 29.943 -4.453 c
+32.75 -4.939 -0.647 3.984 re
+32.794 0.088 m
+32.794 -0.022 32.765 -0.114 32.706 -0.191 c
+32.647 -0.261 32.551 -0.294 32.426 -0.294 c
+32.309 -0.294 32.214 -0.261 32.147 -0.191 c
+32.089 -0.114 32.06 -0.022 32.06 0.088 c
+32.06 0.206 32.089 0.298 32.147 0.368 c
+32.214 0.445 32.309 0.485 32.426 0.485 c
+32.551 0.485 32.647 0.445 32.706 0.368 c
+32.765 0.287 32.794 0.195 32.794 0.088 c
+33.941 -4.939 m
+33.941 -1.484 l
+33.426 -1.484 l
+33.426 -0.955 l
+33.941 -0.955 l
+33.941 -0.588 l
+33.948 -0.158 34.062 0.177 34.279 0.412 c
+34.503 0.655 34.815 0.78 35.219 0.78 c
+35.366 0.78 35.506 0.757 35.646 0.721 c
+35.792 0.68 35.944 0.625 36.101 0.559 c
+35.983 -0.014 l
+35.748 0.111 35.506 0.177 35.264 0.177 c
+35.017 0.177 34.845 0.107 34.749 -0.029 c
+34.649 -0.158 34.602 -0.353 34.602 -0.617 c
+34.602 -0.955 l
+35.249 -0.955 l
+35.249 -1.484 l
+34.602 -1.484 l
+34.602 -4.939 l
+h
+36.41 -4.939 -0.647 3.984 re
+38.88 -5.012 m
+38.379 -5.012 37.997 -4.865 37.733 -4.571 c
+37.469 -4.277 37.336 -3.844 37.336 -3.262 c
+37.336 -2.792 l
+37.336 -2.198 37.461 -1.73 37.718 -1.396 c
+37.982 -1.055 38.343 -0.881 38.806 -0.881 c
+39.266 -0.881 39.607 -1.036 39.835 -1.337 c
+40.07 -1.631 40.191 -2.094 40.203 -2.719 c
+40.203 -3.145 l
+37.982 -3.145 l
+37.982 -3.233 l
+37.982 -3.667 38.06 -3.979 38.218 -4.174 c
+38.383 -4.361 38.615 -4.453 38.909 -4.453 c
+39.104 -4.453 39.276 -4.421 39.424 -4.351 c
+39.57 -4.274 39.707 -4.156 39.835 -3.998 c
+40.173 -4.409 l
+39.886 -4.814 39.457 -5.012 38.88 -5.012 c
+38.806 -1.44 m
+38.531 -1.44 38.329 -1.535 38.203 -1.72 c
+38.075 -1.907 38.001 -2.198 37.982 -2.587 c
+39.555 -2.587 l
+39.555 -2.499 l
+39.534 -2.117 39.468 -1.848 39.35 -1.69 c
+39.233 -1.525 39.048 -1.44 38.806 -1.44 c
+40.849 -2.763 m
+40.849 -2.157 40.96 -1.69 41.188 -1.367 c
+41.423 -1.043 41.749 -0.881 42.172 -0.881 c
+42.554 -0.881 42.852 -1.04 43.069 -1.352 c
+43.069 0.706 l
+43.715 0.706 l
+43.715 -4.939 l
+43.127 -4.939 l
+43.083 -4.512 l
+42.878 -4.847 42.572 -5.012 42.172 -5.012 c
+41.76 -5.012 41.437 -4.858 41.202 -4.542 c
+40.966 -4.218 40.849 -3.763 40.849 -3.175 c
+h
+41.496 -3.145 m
+41.496 -3.586 41.558 -3.917 41.687 -4.13 c
+41.823 -4.336 42.044 -4.438 42.348 -4.438 c
+42.672 -4.438 42.911 -4.277 43.069 -3.954 c
+43.069 -1.94 l
+42.9 -1.628 42.661 -1.469 42.348 -1.469 c
+42.044 -1.469 41.823 -1.572 41.687 -1.778 c
+41.558 -1.984 41.496 -2.308 41.496 -2.748 c
+h
+49.315 -3.145 m
+49.315 -3.763 49.202 -4.23 48.978 -4.542 c
+48.76 -4.858 48.438 -5.012 48.008 -5.012 c
+47.585 -5.012 47.272 -4.832 47.067 -4.469 c
+47.037 -4.939 l
+46.435 -4.939 l
+46.435 0.706 l
+47.081 0.706 l
+47.081 -1.396 l
+47.295 -1.055 47.603 -0.881 48.008 -0.881 c
+48.438 -0.881 48.76 -1.04 48.978 -1.352 c
+49.202 -1.657 49.315 -2.123 49.315 -2.748 c
+h
+48.669 -2.763 m
+48.669 -2.293 48.6 -1.962 48.463 -1.764 c
+48.334 -1.569 48.125 -1.469 47.831 -1.469 c
+47.497 -1.469 47.247 -1.653 47.081 -2.013 c
+47.081 -3.895 l
+47.247 -4.259 47.501 -4.438 47.846 -4.438 c
+48.14 -4.438 48.349 -4.336 48.478 -4.13 c
+48.603 -3.924 48.669 -3.609 48.669 -3.175 c
+h
+51.8 -1.572 m
+51.712 -1.554 51.613 -1.543 51.505 -1.543 c
+51.172 -1.543 50.936 -1.727 50.8 -2.087 c
+50.8 -4.939 l
+50.154 -4.939 l
+50.154 -0.955 l
+50.786 -0.955 l
+50.8 -1.367 l
+50.977 -1.043 51.22 -0.881 51.536 -0.881 c
+51.642 -0.881 51.73 -0.904 51.8 -0.941 c
+h
+54.343 -4.939 m
+54.302 -4.85 54.277 -4.704 54.269 -4.498 c
+54.034 -4.843 53.74 -5.012 53.387 -5.012 c
+53.024 -5.012 52.741 -4.916 52.535 -4.718 c
+52.336 -4.512 52.241 -4.226 52.241 -3.85 c
+52.241 -3.451 52.377 -3.131 52.652 -2.896 c
+52.924 -2.653 53.3 -2.528 53.77 -2.528 c
+54.254 -2.528 l
+54.254 -2.102 l
+54.254 -1.866 54.2 -1.701 54.093 -1.602 c
+53.982 -1.495 53.821 -1.44 53.608 -1.44 c
+53.41 -1.44 53.248 -1.499 53.123 -1.616 c
+53.005 -1.734 52.947 -1.881 52.947 -2.057 c
+52.299 -2.057 l
+52.299 -1.863 52.359 -1.672 52.476 -1.484 c
+52.602 -1.3 52.762 -1.153 52.961 -1.043 c
+53.167 -0.937 53.395 -0.881 53.652 -0.881 c
+54.053 -0.881 54.358 -0.984 54.563 -1.19 c
+54.776 -1.396 54.89 -1.69 54.902 -2.072 c
+54.902 -4.086 l
+54.902 -4.391 54.938 -4.656 55.019 -4.88 c
+55.019 -4.939 l
+h
+53.475 -4.424 m
+53.641 -4.424 53.792 -4.38 53.932 -4.292 c
+54.078 -4.203 54.185 -4.093 54.254 -3.954 c
+54.254 -3.013 l
+53.888 -3.013 l
+53.571 -3.013 53.329 -3.083 53.152 -3.218 c
+52.976 -3.347 52.887 -3.534 52.887 -3.777 c
+52.887 -4.005 52.932 -4.17 53.02 -4.277 c
+53.109 -4.376 53.259 -4.424 53.475 -4.424 c
+56.519 -0.955 m
+56.533 -1.396 l
+56.787 -1.055 57.11 -0.881 57.503 -0.881 c
+58.208 -0.881 58.565 -1.352 58.576 -2.293 c
+58.576 -4.939 l
+57.93 -4.939 l
+57.93 -2.322 l
+57.93 -2.009 57.874 -1.789 57.768 -1.66 c
+57.658 -1.535 57.503 -1.469 57.298 -1.469 c
+57.14 -1.469 56.992 -1.525 56.857 -1.631 c
+56.728 -1.741 56.625 -1.878 56.548 -2.042 c
+56.548 -4.939 l
+55.901 -4.939 l
+55.901 -0.955 l
+h
+60.928 -4.453 m
+61.142 -4.453 61.314 -4.391 61.443 -4.262 c
+61.579 -4.126 61.652 -3.935 61.663 -3.689 c
+62.281 -3.689 l
+62.258 -4.072 62.123 -4.391 61.869 -4.644 c
+61.612 -4.891 61.3 -5.012 60.928 -5.012 c
+60.436 -5.012 60.061 -4.862 59.796 -4.556 c
+59.539 -4.244 59.414 -3.777 59.414 -3.16 c
+59.414 -2.719 l
+59.414 -2.123 59.539 -1.668 59.796 -1.352 c
+60.061 -1.04 60.436 -0.881 60.928 -0.881 c
+61.329 -0.881 61.649 -1.014 61.884 -1.278 c
+62.126 -1.535 62.258 -1.881 62.281 -2.322 c
+61.663 -2.322 l
+61.641 -2.028 61.568 -1.807 61.443 -1.66 c
+61.325 -1.514 61.152 -1.44 60.928 -1.44 c
+60.634 -1.44 60.417 -1.539 60.282 -1.734 c
+60.141 -1.922 60.068 -2.23 60.061 -2.66 c
+60.061 -3.175 l
+60.061 -3.645 60.127 -3.979 60.267 -4.174 c
+60.413 -4.361 60.634 -4.453 60.928 -4.453 c
+63.677 -1.367 m
+63.931 -1.043 64.25 -0.881 64.633 -0.881 c
+65.338 -0.881 65.694 -1.352 65.705 -2.293 c
+65.705 -4.939 l
+65.059 -4.939 l
+65.059 -2.322 l
+65.059 -2.009 65.003 -1.789 64.897 -1.66 c
+64.787 -1.535 64.633 -1.469 64.427 -1.469 c
+64.269 -1.469 64.122 -1.525 63.985 -1.631 c
+63.857 -1.741 63.754 -1.878 63.677 -2.042 c
+63.677 -4.939 l
+63.03 -4.939 l
+63.03 0.706 l
+63.677 0.706 l
+h
+66.705 -4.586 m
+66.705 -4.469 66.738 -4.373 66.808 -4.292 c
+66.873 -4.215 66.977 -4.174 67.116 -4.174 c
+67.263 -4.174 67.37 -4.215 67.44 -4.292 c
+67.517 -4.373 67.557 -4.469 67.557 -4.586 c
+67.557 -4.696 67.517 -4.787 67.44 -4.865 c
+67.37 -4.943 67.263 -4.982 67.116 -4.982 c
+66.977 -4.982 66.873 -4.943 66.808 -4.865 c
+66.738 -4.787 66.705 -4.696 66.705 -4.586 c
+71.482 -3.248 m
+71.556 -3.85 l
+71.658 -3.322 l
+72.511 0.412 l
+73.069 0.412 l
+73.907 -3.322 l
+74.01 -3.866 l
+74.083 -3.248 l
+74.746 0.412 l
+75.421 0.412 l
+74.349 -4.939 l
+73.746 -4.939 l
+72.849 -1.043 l
+72.79 -0.72 l
+72.747 -1.043 l
+71.82 -4.939 l
+71.203 -4.939 l
+70.144 0.412 l
+70.821 0.412 l
+h
+76.803 -4.939 -0.646 3.984 re
+76.847 0.088 m
+76.847 -0.022 76.818 -0.114 76.759 -0.191 c
+76.7 -0.261 76.604 -0.294 76.479 -0.294 c
+76.362 -0.294 76.267 -0.261 76.2 -0.191 c
+76.142 -0.114 76.112 -0.022 76.112 0.088 c
+76.112 0.206 76.142 0.298 76.2 0.368 c
+76.267 0.445 76.362 0.485 76.479 0.485 c
+76.604 0.485 76.7 0.445 76.759 0.368 c
+76.818 0.287 76.847 0.195 76.847 0.088 c
+78.669 0 m
+78.669 -0.955 l
+79.272 -0.955 l
+79.272 -1.484 l
+78.669 -1.484 l
+78.669 -3.954 l
+78.669 -4.112 78.692 -4.23 78.744 -4.307 c
+78.802 -4.388 78.891 -4.424 79.008 -4.424 c
+79.096 -4.424 79.184 -4.409 79.272 -4.38 c
+79.272 -4.939 l
+79.126 -4.986 78.971 -5.012 78.817 -5.012 c
+78.559 -5.012 78.365 -4.92 78.229 -4.733 c
+78.089 -4.549 78.023 -4.288 78.023 -3.954 c
+78.023 -1.484 l
+77.42 -1.484 l
+77.42 -0.955 l
+78.023 -0.955 l
+78.023 0 l
+h
+80.683 -1.367 m
+80.938 -1.043 81.256 -0.881 81.639 -0.881 c
+82.345 -0.881 82.701 -1.352 82.712 -2.293 c
+82.712 -4.939 l
+82.065 -4.939 l
+82.065 -2.322 l
+82.065 -2.009 82.01 -1.789 81.904 -1.66 c
+81.794 -1.535 81.639 -1.469 81.433 -1.469 c
+81.275 -1.469 81.128 -1.525 80.992 -1.631 c
+80.864 -1.741 80.761 -1.878 80.683 -2.042 c
+80.683 -4.939 l
+80.037 -4.939 l
+80.037 0.706 l
+80.683 0.706 l
+h
+f
+Q
+707.613 174.374 -1.793 0.867 re
+711.582 174.374 m
+711.582 173.727 711.478 173.242 711.273 172.918 c
+711.067 172.595 710.744 172.433 710.302 172.433 c
+709.957 172.433 709.678 172.587 709.465 172.903 c
+709.436 172.506 l
+708.48 172.506 l
+708.48 178.151 l
+709.523 178.151 l
+709.523 176.167 l
+709.718 176.431 709.972 176.564 710.288 176.564 c
+710.729 176.564 711.052 176.402 711.258 176.078 c
+711.464 175.756 711.57 175.285 711.582 174.667 c
+h
+710.538 174.623 m
+710.538 175.043 710.498 175.322 710.42 175.461 c
+710.34 175.608 710.2 175.681 709.994 175.681 c
+709.777 175.681 709.619 175.583 709.523 175.388 c
+709.523 173.58 l
+709.612 173.393 709.774 173.3 710.009 173.3 c
+710.204 173.3 710.34 173.363 710.42 173.491 c
+710.498 173.616 710.538 173.874 710.538 174.256 c
+h
+f
+q 1 0 0 1 712.3608 172.8592 cm
+0 0 m
+0 0.118 0.033 0.214 0.102 0.294 c
+0.169 0.372 0.272 0.412 0.411 0.412 c
+0.559 0.412 0.665 0.372 0.735 0.294 c
+0.812 0.214 0.852 0.118 0.852 0 c
+0.852 -0.11 0.812 -0.201 0.735 -0.278 c
+0.665 -0.357 0.559 -0.396 0.411 -0.396 c
+0.272 -0.396 0.169 -0.357 0.102 -0.278 c
+0.033 -0.201 0 -0.11 0 0 c
+0 2.822 m
+0 2.94 0.033 3.036 0.102 3.117 c
+0.169 3.194 0.272 3.234 0.411 3.234 c
+0.559 3.234 0.665 3.194 0.735 3.117 c
+0.812 3.036 0.852 2.94 0.852 2.822 c
+0.852 2.712 0.812 2.621 0.735 2.544 c
+0.665 2.463 0.559 2.426 0.411 2.426 c
+0.272 2.426 0.169 2.463 0.102 2.544 c
+0.033 2.621 0 2.712 0 2.822 c
+6.879 0.309 m
+6.761 0.162 l
+6.427 -0.231 5.931 -0.426 5.277 -0.426 c
+4.696 -0.426 4.241 -0.235 3.91 0.148 c
+3.586 0.53 3.418 1.073 3.41 1.779 c
+3.41 2.822 l
+3.41 3.576 3.557 4.138 3.851 4.513 c
+4.152 4.884 4.604 5.072 5.203 5.072 c
+5.71 5.072 6.107 4.929 6.394 4.645 c
+6.688 4.358 6.849 3.955 6.879 3.425 c
+6.203 3.425 l
+6.181 3.756 6.089 4.017 5.924 4.204 c
+5.766 4.399 5.531 4.499 5.218 4.499 c
+4.825 4.499 4.542 4.37 4.366 4.116 c
+4.189 3.859 4.094 3.458 4.087 2.911 c
+4.087 1.823 l
+4.087 1.283 4.189 0.867 4.395 0.574 c
+4.601 0.287 4.895 0.148 5.277 0.148 c
+5.637 0.148 5.916 0.235 6.115 0.412 c
+6.203 0.5 l
+6.203 1.735 l
+5.232 1.735 l
+5.232 2.309 l
+6.879 2.309 l
+h
+8.599 -0.353 -0.647 3.984 re
+8.643 4.675 m
+8.643 4.564 8.613 4.472 8.555 4.395 c
+8.496 4.326 8.401 4.293 8.276 4.293 c
+8.158 4.293 8.062 4.326 7.996 4.395 c
+7.937 4.472 7.908 4.564 7.908 4.675 c
+7.908 4.792 7.937 4.884 7.996 4.954 c
+8.062 5.031 8.158 5.072 8.276 5.072 c
+8.401 5.072 8.496 5.031 8.555 4.954 c
+8.613 4.873 8.643 4.781 8.643 4.675 c
+10.466 4.586 m
+10.466 3.631 l
+11.069 3.631 l
+11.069 3.102 l
+10.466 3.102 l
+10.466 0.632 l
+10.466 0.474 10.487 0.357 10.539 0.279 c
+10.598 0.198 10.686 0.162 10.803 0.162 c
+10.892 0.162 10.98 0.177 11.069 0.206 c
+11.069 -0.353 l
+10.921 -0.4 10.767 -0.426 10.612 -0.426 c
+10.356 -0.426 10.161 -0.334 10.024 -0.147 c
+9.885 0.037 9.818 0.298 9.818 0.632 c
+9.818 3.102 l
+9.216 3.102 l
+9.216 3.631 l
+9.818 3.631 l
+9.818 4.586 l
+h
+16.521 0.794 m
+17.124 3.631 l
+17.771 3.631 l
+16.786 -0.353 l
+16.272 -0.353 l
+15.493 2.5 l
+14.743 -0.353 l
+14.214 -0.353 l
+13.259 3.631 l
+13.891 3.631 l
+14.508 0.867 l
+15.243 3.631 l
+15.757 3.631 l
+h
+19.152 -0.353 -0.646 3.984 re
+19.197 4.675 m
+19.197 4.564 19.168 4.472 19.108 4.395 c
+19.05 4.326 18.954 4.293 18.83 4.293 c
+18.712 4.293 18.616 4.326 18.55 4.395 c
+18.491 4.472 18.462 4.564 18.462 4.675 c
+18.462 4.792 18.491 4.884 18.55 4.954 c
+18.616 5.031 18.712 5.072 18.83 5.072 c
+18.954 5.072 19.05 5.031 19.108 4.954 c
+19.168 4.873 19.197 4.781 19.197 4.675 c
+20.872 -0.353 -0.646 5.644 re
+22.593 -0.353 -0.647 5.644 re
+26.708 0.133 m
+26.921 0.133 27.094 0.195 27.222 0.324 c
+27.359 0.46 27.432 0.651 27.443 0.897 c
+28.06 0.897 l
+28.039 0.515 27.902 0.195 27.649 -0.058 c
+27.391 -0.305 27.079 -0.426 26.708 -0.426 c
+26.216 -0.426 25.841 -0.276 25.576 0.03 c
+25.319 0.342 25.194 0.809 25.194 1.426 c
+25.194 1.867 l
+25.194 2.463 25.319 2.918 25.576 3.234 c
+25.841 3.547 26.216 3.705 26.708 3.705 c
+27.108 3.705 27.428 3.572 27.663 3.308 c
+27.906 3.051 28.039 2.705 28.06 2.264 c
+27.443 2.264 l
+27.421 2.558 27.347 2.779 27.222 2.926 c
+27.104 3.072 26.932 3.146 26.708 3.146 c
+26.414 3.146 26.198 3.047 26.061 2.852 c
+25.922 2.664 25.849 2.356 25.841 1.926 c
+25.841 1.411 l
+25.841 0.941 25.907 0.607 26.046 0.412 c
+26.194 0.225 26.414 0.133 26.708 0.133 c
+30.456 3.014 m
+30.368 3.032 30.269 3.043 30.162 3.043 c
+29.828 3.043 29.593 2.859 29.456 2.5 c
+29.456 -0.353 l
+28.81 -0.353 l
+28.81 3.631 l
+29.442 3.631 l
+29.456 3.219 l
+29.633 3.543 29.876 3.705 30.192 3.705 c
+30.298 3.705 30.387 3.682 30.456 3.645 c
+h
+32.456 -0.426 m
+31.956 -0.426 31.573 -0.278 31.309 0.015 c
+31.044 0.309 30.912 0.742 30.912 1.324 c
+30.912 1.794 l
+30.912 2.389 31.037 2.856 31.295 3.19 c
+31.559 3.532 31.919 3.705 32.382 3.705 c
+32.841 3.705 33.183 3.55 33.411 3.249 c
+33.646 2.955 33.767 2.492 33.778 1.867 c
+33.778 1.441 l
+31.559 1.441 l
+31.559 1.353 l
+31.559 0.919 31.636 0.607 31.794 0.412 c
+31.96 0.225 32.191 0.133 32.485 0.133 c
+32.679 0.133 32.852 0.166 32.999 0.235 c
+33.146 0.312 33.282 0.43 33.411 0.588 c
+33.749 0.177 l
+33.462 -0.228 33.032 -0.426 32.456 -0.426 c
+32.382 3.146 m
+32.106 3.146 31.904 3.051 31.779 2.866 c
+31.65 2.679 31.577 2.389 31.559 1.999 c
+33.132 1.999 l
+33.132 2.087 l
+33.109 2.469 33.043 2.738 32.926 2.897 c
+32.808 3.061 32.625 3.146 32.382 3.146 c
+36.6 -0.353 m
+36.56 -0.264 36.535 -0.118 36.527 0.088 c
+36.292 -0.257 35.998 -0.426 35.646 -0.426 c
+35.281 -0.426 34.998 -0.33 34.792 -0.132 c
+34.594 0.074 34.499 0.36 34.499 0.736 c
+34.499 1.135 34.634 1.455 34.91 1.691 c
+35.182 1.933 35.557 2.058 36.027 2.058 c
+36.512 2.058 l
+36.512 2.484 l
+36.512 2.72 36.457 2.885 36.351 2.984 c
+36.24 3.091 36.079 3.146 35.866 3.146 c
+35.667 3.146 35.505 3.087 35.38 2.97 c
+35.263 2.852 35.204 2.705 35.204 2.529 c
+34.557 2.529 l
+34.557 2.723 34.616 2.914 34.734 3.102 c
+34.859 3.286 35.021 3.433 35.218 3.543 c
+35.424 3.649 35.652 3.705 35.91 3.705 c
+36.311 3.705 36.615 3.602 36.821 3.396 c
+37.034 3.19 37.148 2.897 37.159 2.514 c
+37.159 0.5 l
+37.159 0.195 37.196 -0.07 37.277 -0.294 c
+37.277 -0.353 l
+h
+35.733 0.162 m
+35.899 0.162 36.049 0.206 36.189 0.294 c
+36.336 0.383 36.442 0.493 36.512 0.632 c
+36.512 1.573 l
+36.145 1.573 l
+35.829 1.573 35.586 1.503 35.41 1.368 c
+35.233 1.239 35.145 1.052 35.145 0.809 c
+35.145 0.581 35.189 0.416 35.278 0.309 c
+35.366 0.21 35.517 0.162 35.733 0.162 c
+39.011 4.586 m
+39.011 3.631 l
+39.614 3.631 l
+39.614 3.102 l
+39.011 3.102 l
+39.011 0.632 l
+39.011 0.474 39.033 0.357 39.085 0.279 c
+39.143 0.198 39.232 0.162 39.349 0.162 c
+39.438 0.162 39.526 0.177 39.614 0.206 c
+39.614 -0.353 l
+39.467 -0.4 39.313 -0.426 39.158 -0.426 c
+38.901 -0.426 38.706 -0.334 38.57 -0.147 c
+38.431 0.037 38.364 0.298 38.364 0.632 c
+38.364 3.102 l
+37.762 3.102 l
+37.762 3.631 l
+38.364 3.631 l
+38.364 4.586 l
+h
+41.804 -0.426 m
+41.304 -0.426 40.922 -0.278 40.658 0.015 c
+40.392 0.309 40.261 0.742 40.261 1.324 c
+40.261 1.794 l
+40.261 2.389 40.386 2.856 40.643 3.19 c
+40.907 3.532 41.267 3.705 41.73 3.705 c
+42.19 3.705 42.532 3.55 42.759 3.249 c
+42.995 2.955 43.116 2.492 43.127 1.867 c
+43.127 1.441 l
+40.907 1.441 l
+40.907 1.353 l
+40.907 0.919 40.984 0.607 41.142 0.412 c
+41.308 0.225 41.539 0.133 41.834 0.133 c
+42.028 0.133 42.201 0.166 42.348 0.235 c
+42.495 0.312 42.63 0.43 42.759 0.588 c
+43.097 0.177 l
+42.811 -0.228 42.381 -0.426 41.804 -0.426 c
+41.73 3.146 m
+41.455 3.146 41.253 3.051 41.128 2.866 c
+40.999 2.679 40.926 2.389 40.907 1.999 c
+42.48 1.999 l
+42.48 2.087 l
+42.458 2.469 42.392 2.738 42.274 2.897 c
+42.156 3.061 41.973 3.146 41.73 3.146 c
+46.478 4.586 m
+46.478 3.631 l
+47.081 3.631 l
+47.081 3.102 l
+46.478 3.102 l
+46.478 0.632 l
+46.478 0.474 46.501 0.357 46.551 0.279 c
+46.611 0.198 46.699 0.162 46.817 0.162 c
+46.904 0.162 46.993 0.177 47.081 0.206 c
+47.081 -0.353 l
+46.934 -0.4 46.78 -0.426 46.625 -0.426 c
+46.368 -0.426 46.173 -0.334 46.037 -0.147 c
+45.898 0.037 45.832 0.298 45.832 0.632 c
+45.832 3.102 l
+45.229 3.102 l
+45.229 3.631 l
+45.832 3.631 l
+45.832 4.586 l
+h
+48.492 3.219 m
+48.745 3.543 49.065 3.705 49.447 3.705 c
+50.153 3.705 50.509 3.234 50.52 2.294 c
+50.52 -0.353 l
+49.874 -0.353 l
+49.874 2.264 l
+49.874 2.577 49.819 2.797 49.712 2.926 c
+49.602 3.051 49.447 3.117 49.242 3.117 c
+49.084 3.117 48.937 3.061 48.801 2.955 c
+48.672 2.845 48.569 2.708 48.492 2.544 c
+48.492 -0.353 l
+47.845 -0.353 l
+47.845 5.292 l
+48.492 5.292 l
+h
+52.901 -0.426 m
+52.402 -0.426 52.02 -0.278 51.756 0.015 c
+51.49 0.309 51.359 0.742 51.359 1.324 c
+51.359 1.794 l
+51.359 2.389 51.484 2.856 51.741 3.19 c
+52.005 3.532 52.365 3.705 52.828 3.705 c
+53.287 3.705 53.63 3.55 53.857 3.249 c
+54.092 2.955 54.214 2.492 54.225 1.867 c
+54.225 1.441 l
+52.005 1.441 l
+52.005 1.353 l
+52.005 0.919 52.082 0.607 52.24 0.412 c
+52.406 0.225 52.637 0.133 52.932 0.133 c
+53.126 0.133 53.298 0.166 53.445 0.235 c
+53.593 0.312 53.728 0.43 53.857 0.588 c
+54.195 0.177 l
+53.909 -0.228 53.479 -0.426 52.901 -0.426 c
+52.828 3.146 m
+52.552 3.146 52.35 3.051 52.226 2.866 c
+52.097 2.679 52.024 2.389 52.005 1.999 c
+53.578 1.999 l
+53.578 2.087 l
+53.556 2.469 53.489 2.738 53.372 2.897 c
+53.254 3.061 53.071 3.146 52.828 3.146 c
+f
+Q
+q 1 0 0 1 663.6041 164.1141 cm
+0 0 m
+0 0.147 -0.056 0.268 -0.162 0.367 c
+-0.272 0.463 -0.478 0.58 -0.779 0.72 c
+-1.124 0.866 -1.367 0.988 -1.515 1.087 c
+-1.661 1.194 -1.771 1.311 -1.837 1.44 c
+-1.908 1.565 -1.941 1.723 -1.941 1.911 c
+-1.941 2.234 -1.823 2.502 -1.588 2.719 c
+-1.353 2.932 -1.051 3.042 -0.676 3.042 c
+-0.294 3.042 0.014 2.929 0.249 2.705 c
+0.484 2.476 0.602 2.19 0.602 1.837 c
+-0.044 1.837 l
+-0.044 2.013 -0.104 2.164 -0.221 2.293 c
+-0.339 2.418 -0.493 2.484 -0.676 2.484 c
+-0.875 2.484 -1.025 2.429 -1.132 2.322 c
+-1.242 2.223 -1.294 2.09 -1.294 1.925 c
+-1.294 1.797 -1.257 1.69 -1.176 1.602 c
+-1.099 1.521 -0.908 1.418 -0.603 1.294 c
+-0.125 1.105 0.206 0.918 0.382 0.735 c
+0.559 0.558 0.646 0.33 0.646 0.058 c
+0.646 -0.294 0.522 -0.574 0.279 -0.78 c
+0.044 -0.985 -0.272 -1.088 -0.661 -1.088 c
+-1.084 -1.088 -1.422 -0.971 -1.675 -0.736 c
+-1.933 -0.493 -2.058 -0.188 -2.058 0.176 c
+-1.411 0.176 l
+-1.404 -0.052 -1.334 -0.229 -1.205 -0.353 c
+-1.081 -0.47 -0.897 -0.53 -0.661 -0.53 c
+-0.449 -0.53 -0.287 -0.482 -0.177 -0.383 c
+-0.059 -0.287 0 -0.158 0 0 c
+4.365 0.779 m
+4.365 0.151 4.247 -0.32 4.012 -0.632 c
+3.785 -0.938 3.468 -1.088 3.057 -1.088 c
+2.653 -1.088 2.344 -0.938 2.131 -0.632 c
+2.131 -2.544 l
+1.484 -2.544 l
+1.484 2.969 l
+2.072 2.969 l
+2.117 2.528 l
+2.329 2.869 2.639 3.042 3.042 3.042 c
+3.484 3.042 3.811 2.888 4.027 2.587 c
+4.241 2.281 4.355 1.826 4.365 1.219 c
+h
+3.719 1.161 m
+3.719 1.602 3.649 1.925 3.513 2.131 c
+3.373 2.344 3.152 2.454 2.851 2.454 c
+2.535 2.454 2.296 2.3 2.131 1.999 c
+2.131 -0.074 l
+2.296 -0.379 2.535 -0.53 2.851 -0.53 c
+3.146 -0.53 3.358 -0.427 3.499 -0.221 c
+3.634 -0.008 3.707 0.323 3.719 0.764 c
+h
+6.629 -1.088 m
+6.129 -1.088 5.747 -0.941 5.483 -0.647 c
+5.218 -0.353 5.086 0.08 5.086 0.661 c
+5.086 1.132 l
+5.086 1.726 5.211 2.194 5.468 2.528 c
+5.733 2.869 6.092 3.042 6.556 3.042 c
+7.015 3.042 7.357 2.888 7.584 2.587 c
+7.819 2.293 7.941 1.83 7.952 1.205 c
+7.952 0.779 l
+5.733 0.779 l
+5.733 0.69 l
+5.733 0.257 5.81 -0.056 5.968 -0.25 c
+6.133 -0.437 6.364 -0.53 6.659 -0.53 c
+6.853 -0.53 7.026 -0.497 7.173 -0.427 c
+7.32 -0.35 7.456 -0.232 7.584 -0.074 c
+7.923 -0.485 l
+7.636 -0.89 7.206 -1.088 6.629 -1.088 c
+6.556 2.484 m
+6.28 2.484 6.078 2.389 5.953 2.204 c
+5.824 2.017 5.751 1.726 5.733 1.337 c
+7.305 1.337 l
+7.305 1.425 l
+7.283 1.807 7.217 2.076 7.1 2.234 c
+6.982 2.399 6.798 2.484 6.556 2.484 c
+10.113 -0.53 m
+10.326 -0.53 10.499 -0.468 10.628 -0.339 c
+10.763 -0.202 10.836 -0.011 10.848 0.235 c
+11.465 0.235 l
+11.443 -0.148 11.307 -0.468 11.054 -0.721 c
+10.796 -0.967 10.484 -1.088 10.113 -1.088 c
+9.62 -1.088 9.246 -0.938 8.981 -0.632 c
+8.723 -0.32 8.598 0.147 8.598 0.764 c
+8.598 1.205 l
+8.598 1.801 8.723 2.256 8.981 2.572 c
+9.246 2.884 9.62 3.042 10.113 3.042 c
+10.514 3.042 10.833 2.91 11.068 2.645 c
+11.31 2.389 11.443 2.042 11.465 1.602 c
+10.848 1.602 l
+10.826 1.896 10.753 2.117 10.628 2.263 c
+10.51 2.41 10.337 2.484 10.113 2.484 c
+9.818 2.484 9.602 2.385 9.466 2.19 c
+9.326 2.002 9.253 1.693 9.246 1.263 c
+9.246 0.749 l
+9.246 0.278 9.311 -0.056 9.452 -0.25 c
+9.598 -0.437 9.818 -0.53 10.113 -0.53 c
+12.92 -1.015 -0.646 3.984 re
+12.964 4.012 m
+12.964 3.902 12.935 3.81 12.876 3.733 c
+12.818 3.663 12.722 3.63 12.596 3.63 c
+12.479 3.63 12.384 3.663 12.318 3.733 c
+12.259 3.81 12.23 3.902 12.23 4.012 c
+12.23 4.13 12.259 4.222 12.318 4.292 c
+12.384 4.369 12.479 4.409 12.596 4.409 c
+12.722 4.409 12.818 4.369 12.876 4.292 c
+12.935 4.211 12.964 4.119 12.964 4.012 c
+14.111 -1.015 m
+14.111 2.439 l
+13.596 2.439 l
+13.596 2.969 l
+14.111 2.969 l
+14.111 3.336 l
+14.119 3.766 14.232 4.101 14.449 4.336 c
+14.674 4.579 14.986 4.704 15.389 4.704 c
+15.536 4.704 15.676 4.681 15.816 4.644 c
+15.963 4.604 16.114 4.548 16.272 4.483 c
+16.154 3.91 l
+15.919 4.035 15.676 4.101 15.434 4.101 c
+15.187 4.101 15.015 4.031 14.919 3.895 c
+14.82 3.766 14.772 3.571 14.772 3.307 c
+14.772 2.969 l
+15.419 2.969 l
+15.419 2.439 l
+14.772 2.439 l
+14.772 -1.015 l
+h
+16.581 -1.015 -0.647 3.984 re
+19.05 -1.088 m
+18.55 -1.088 18.167 -0.941 17.903 -0.647 c
+17.639 -0.353 17.506 0.08 17.506 0.661 c
+17.506 1.132 l
+17.506 1.726 17.631 2.194 17.888 2.528 c
+18.153 2.869 18.514 3.042 18.977 3.042 c
+19.436 3.042 19.777 2.888 20.005 2.587 c
+20.24 2.293 20.361 1.83 20.373 1.205 c
+20.373 0.779 l
+18.153 0.779 l
+18.153 0.69 l
+18.153 0.257 18.231 -0.056 18.389 -0.25 c
+18.553 -0.437 18.786 -0.53 19.079 -0.53 c
+19.274 -0.53 19.447 -0.497 19.594 -0.427 c
+19.74 -0.35 19.877 -0.232 20.005 -0.074 c
+20.343 -0.485 l
+20.057 -0.89 19.627 -1.088 19.05 -1.088 c
+18.977 2.484 m
+18.701 2.484 18.499 2.389 18.374 2.204 c
+18.245 2.017 18.171 1.726 18.153 1.337 c
+19.726 1.337 l
+19.726 1.425 l
+19.704 1.807 19.638 2.076 19.52 2.234 c
+19.403 2.399 19.219 2.484 18.977 2.484 c
+21.02 1.161 m
+21.02 1.767 21.13 2.234 21.358 2.557 c
+21.593 2.881 21.92 3.042 22.343 3.042 c
+22.724 3.042 23.022 2.884 23.239 2.572 c
+23.239 4.629 l
+23.885 4.629 l
+23.885 -1.015 l
+23.297 -1.015 l
+23.254 -0.588 l
+23.048 -0.923 22.743 -1.088 22.343 -1.088 c
+21.93 -1.088 21.608 -0.934 21.373 -0.618 c
+21.137 -0.294 21.02 0.161 21.02 0.749 c
+h
+21.666 0.779 m
+21.666 0.338 21.729 0.007 21.857 -0.206 c
+21.993 -0.412 22.214 -0.515 22.518 -0.515 c
+22.842 -0.515 23.081 -0.353 23.239 -0.03 c
+23.239 1.984 l
+23.07 2.296 22.831 2.454 22.518 2.454 c
+22.214 2.454 21.993 2.352 21.857 2.146 c
+21.729 1.94 21.666 1.616 21.666 1.176 c
+h
+29.487 0.779 m
+29.487 0.161 29.372 -0.306 29.148 -0.618 c
+28.931 -0.934 28.608 -1.088 28.178 -1.088 c
+27.756 -1.088 27.443 -0.908 27.237 -0.545 c
+27.208 -1.015 l
+26.605 -1.015 l
+26.605 4.629 l
+27.252 4.629 l
+27.252 2.528 l
+27.465 2.869 27.773 3.042 28.178 3.042 c
+28.608 3.042 28.931 2.884 29.148 2.572 c
+29.372 2.267 29.487 1.801 29.487 1.176 c
+h
+28.839 1.161 m
+28.839 1.631 28.77 1.961 28.633 2.16 c
+28.505 2.355 28.296 2.454 28.001 2.454 c
+27.667 2.454 27.417 2.271 27.252 1.911 c
+27.252 0.029 l
+27.417 -0.335 27.671 -0.515 28.016 -0.515 c
+28.311 -0.515 28.519 -0.412 28.648 -0.206 c
+28.773 0 28.839 0.315 28.839 0.749 c
+h
+31.97 2.352 m
+31.882 2.37 31.783 2.381 31.677 2.381 c
+31.342 2.381 31.106 2.197 30.971 1.837 c
+30.971 -1.015 l
+30.324 -1.015 l
+30.324 2.969 l
+30.956 2.969 l
+30.971 2.557 l
+31.147 2.881 31.39 3.042 31.706 3.042 c
+31.812 3.042 31.9 3.02 31.97 2.983 c
+h
+34.513 -1.015 m
+34.472 -0.927 34.447 -0.78 34.439 -0.574 c
+34.204 -0.919 33.911 -1.088 33.558 -1.088 c
+33.194 -1.088 32.911 -0.992 32.705 -0.794 c
+32.507 -0.588 32.411 -0.302 32.411 0.073 c
+32.411 0.473 32.548 0.793 32.822 1.028 c
+33.095 1.271 33.47 1.396 33.94 1.396 c
+34.425 1.396 l
+34.425 1.822 l
+34.425 2.057 34.37 2.223 34.264 2.322 c
+34.154 2.429 33.992 2.484 33.778 2.484 c
+33.58 2.484 33.418 2.425 33.293 2.308 c
+33.175 2.19 33.117 2.042 33.117 1.866 c
+32.47 1.866 l
+32.47 2.061 32.529 2.252 32.646 2.439 c
+32.772 2.624 32.933 2.77 33.132 2.881 c
+33.337 2.987 33.566 3.042 33.822 3.042 c
+34.223 3.042 34.528 2.94 34.734 2.734 c
+34.947 2.528 35.06 2.234 35.072 1.851 c
+35.072 -0.162 l
+35.072 -0.468 35.108 -0.732 35.189 -0.956 c
+35.189 -1.015 l
+h
+33.646 -0.5 m
+33.811 -0.5 33.962 -0.456 34.102 -0.368 c
+34.248 -0.279 34.355 -0.169 34.425 -0.03 c
+34.425 0.911 l
+34.058 0.911 l
+33.741 0.911 33.499 0.841 33.323 0.706 c
+33.146 0.577 33.057 0.389 33.057 0.147 c
+33.057 -0.081 33.102 -0.246 33.19 -0.353 c
+33.279 -0.452 33.429 -0.5 33.646 -0.5 c
+36.689 2.969 m
+36.703 2.528 l
+36.957 2.869 37.281 3.042 37.674 3.042 c
+38.379 3.042 38.736 2.572 38.746 1.631 c
+38.746 -1.015 l
+38.1 -1.015 l
+38.1 1.602 l
+38.1 1.914 38.044 2.135 37.938 2.263 c
+37.828 2.389 37.674 2.454 37.468 2.454 c
+37.31 2.454 37.163 2.399 37.026 2.293 c
+36.898 2.183 36.795 2.046 36.718 1.881 c
+36.718 -1.015 l
+36.072 -1.015 l
+36.072 2.969 l
+h
+41.098 -0.53 m
+41.312 -0.53 41.485 -0.468 41.613 -0.339 c
+41.749 -0.202 41.822 -0.011 41.834 0.235 c
+42.451 0.235 l
+42.428 -0.148 42.293 -0.468 42.039 -0.721 c
+41.782 -0.967 41.47 -1.088 41.098 -1.088 c
+40.606 -1.088 40.231 -0.938 39.966 -0.632 c
+39.71 -0.32 39.584 0.147 39.584 0.764 c
+39.584 1.205 l
+39.584 1.801 39.71 2.256 39.966 2.572 c
+40.231 2.884 40.606 3.042 41.098 3.042 c
+41.499 3.042 41.819 2.91 42.054 2.645 c
+42.297 2.389 42.428 2.042 42.451 1.602 c
+41.834 1.602 l
+41.811 1.896 41.738 2.117 41.613 2.263 c
+41.495 2.41 41.323 2.484 41.098 2.484 c
+40.805 2.484 40.587 2.385 40.452 2.19 c
+40.312 2.002 40.238 1.693 40.231 1.263 c
+40.231 0.749 l
+40.231 0.278 40.297 -0.056 40.437 -0.25 c
+40.583 -0.437 40.805 -0.53 41.098 -0.53 c
+43.847 2.557 m
+44.101 2.881 44.421 3.042 44.803 3.042 c
+45.508 3.042 45.865 2.572 45.875 1.631 c
+45.875 -1.015 l
+45.229 -1.015 l
+45.229 1.602 l
+45.229 1.914 45.173 2.135 45.067 2.263 c
+44.957 2.389 44.803 2.454 44.597 2.454 c
+44.439 2.454 44.292 2.399 44.155 2.293 c
+44.027 2.183 43.924 2.046 43.847 1.881 c
+43.847 -1.015 l
+43.201 -1.015 l
+43.201 4.629 l
+43.847 4.629 l
+h
+49.256 -1.015 -0.646 3.984 re
+49.3 4.012 m
+49.3 3.902 49.271 3.81 49.212 3.733 c
+49.154 3.663 49.058 3.63 48.932 3.63 c
+48.815 3.63 48.72 3.663 48.654 3.733 c
+48.595 3.81 48.566 3.902 48.566 4.012 c
+48.566 4.13 48.595 4.222 48.654 4.292 c
+48.72 4.369 48.815 4.409 48.932 4.409 c
+49.058 4.409 49.154 4.369 49.212 4.292 c
+49.271 4.211 49.3 4.119 49.3 4.012 c
+50.565 -1.015 m
+50.565 2.439 l
+50.035 2.439 l
+50.035 2.969 l
+50.565 2.969 l
+50.565 3.424 l
+50.565 3.825 50.66 4.137 50.858 4.365 c
+51.064 4.589 51.344 4.704 51.696 4.704 c
+51.833 4.704 51.965 4.681 52.093 4.644 c
+52.064 4.101 l
+51.965 4.119 51.866 4.13 51.77 4.13 c
+51.395 4.13 51.211 3.865 51.211 3.336 c
+51.211 2.969 l
+51.887 2.969 l
+51.887 2.439 l
+51.211 2.439 l
+51.211 -1.015 l
+h
+55.077 -1.015 -0.647 3.984 re
+55.122 4.012 m
+55.122 3.902 55.091 3.81 55.033 3.733 c
+54.974 3.663 54.879 3.63 54.754 3.63 c
+54.636 3.63 54.54 3.663 54.474 3.733 c
+54.416 3.81 54.386 3.902 54.386 4.012 c
+54.386 4.13 54.416 4.222 54.474 4.292 c
+54.54 4.369 54.636 4.409 54.754 4.409 c
+54.879 4.409 54.974 4.369 55.033 4.292 c
+55.091 4.211 55.122 4.119 55.122 4.012 c
+56.944 3.924 m
+56.944 2.969 l
+57.547 2.969 l
+57.547 2.439 l
+56.944 2.439 l
+56.944 -0.03 l
+56.944 -0.188 56.966 -0.306 57.017 -0.383 c
+57.077 -0.464 57.164 -0.5 57.282 -0.5 c
+57.37 -0.5 57.458 -0.485 57.547 -0.456 c
+57.547 -1.015 l
+57.399 -1.062 57.245 -1.088 57.091 -1.088 c
+56.834 -1.088 56.639 -0.996 56.503 -0.809 c
+56.363 -0.626 56.298 -0.364 56.298 -0.03 c
+56.298 2.439 l
+55.695 2.439 l
+55.695 2.969 l
+56.298 2.969 l
+56.298 3.924 l
+h
+59.899 1.161 m
+59.899 1.767 60.009 2.234 60.236 2.557 c
+60.472 2.881 60.799 3.042 61.221 3.042 c
+61.603 3.042 61.902 2.884 62.118 2.572 c
+62.118 4.629 l
+62.765 4.629 l
+62.765 -1.015 l
+62.177 -1.015 l
+62.133 -0.588 l
+61.927 -0.923 61.622 -1.088 61.221 -1.088 c
+60.809 -1.088 60.487 -0.934 60.252 -0.618 c
+60.016 -0.294 59.899 0.161 59.899 0.749 c
+h
+60.545 0.779 m
+60.545 0.338 60.608 0.007 60.736 -0.206 c
+60.872 -0.412 61.093 -0.515 61.397 -0.515 c
+61.721 -0.515 61.96 -0.353 62.118 -0.03 c
+62.118 1.984 l
+61.949 2.296 61.711 2.454 61.397 2.454 c
+61.093 2.454 60.872 2.352 60.736 2.146 c
+60.608 1.94 60.545 1.616 60.545 1.176 c
+h
+63.632 1.161 m
+63.632 1.738 63.768 2.194 64.044 2.528 c
+64.327 2.869 64.697 3.042 65.16 3.042 c
+65.62 3.042 65.987 2.873 66.263 2.543 c
+66.546 2.219 66.693 1.77 66.704 1.205 c
+66.704 0.779 l
+66.704 0.209 66.561 -0.246 66.278 -0.588 c
+66.002 -0.923 65.634 -1.088 65.175 -1.088 c
+64.713 -1.088 64.341 -0.927 64.059 -0.603 c
+63.783 -0.273 63.639 0.168 63.632 0.72 c
+h
+64.279 0.779 m
+64.279 0.374 64.356 0.058 64.514 -0.177 c
+64.68 -0.412 64.9 -0.53 65.175 -0.53 c
+65.742 -0.53 66.035 -0.118 66.058 0.706 c
+66.058 1.161 l
+66.058 1.562 65.973 1.881 65.808 2.117 c
+65.649 2.358 65.433 2.484 65.16 2.484 c
+64.896 2.484 64.68 2.358 64.514 2.117 c
+64.356 1.881 64.279 1.562 64.279 1.161 c
+h
+68.967 -1.088 m
+68.468 -1.088 68.086 -0.941 67.822 -0.647 c
+67.556 -0.353 67.425 0.08 67.425 0.661 c
+67.425 1.132 l
+67.425 1.726 67.55 2.194 67.807 2.528 c
+68.071 2.869 68.431 3.042 68.894 3.042 c
+69.353 3.042 69.696 2.888 69.923 2.587 c
+70.158 2.293 70.28 1.83 70.291 1.205 c
+70.291 0.779 l
+68.071 0.779 l
+68.071 0.69 l
+68.071 0.257 68.148 -0.056 68.306 -0.25 c
+68.472 -0.437 68.703 -0.53 68.998 -0.53 c
+69.192 -0.53 69.364 -0.497 69.511 -0.427 c
+69.659 -0.35 69.794 -0.232 69.923 -0.074 c
+70.261 -0.485 l
+69.975 -0.89 69.545 -1.088 68.967 -1.088 c
+68.894 2.484 m
+68.618 2.484 68.416 2.389 68.292 2.204 c
+68.163 2.017 68.09 1.726 68.071 1.337 c
+69.644 1.337 l
+69.644 1.425 l
+69.622 1.807 69.555 2.076 69.438 2.234 c
+69.32 2.399 69.137 2.484 68.894 2.484 c
+73.01 0 m
+73.01 0.147 72.955 0.268 72.848 0.367 c
+72.738 0.463 72.532 0.58 72.231 0.72 c
+71.886 0.866 71.643 0.988 71.496 1.087 c
+71.349 1.194 71.238 1.311 71.172 1.44 c
+71.103 1.565 71.07 1.723 71.07 1.911 c
+71.07 2.234 71.188 2.502 71.423 2.719 c
+71.658 2.932 71.959 3.042 72.333 3.042 c
+72.716 3.042 73.025 2.929 73.26 2.705 c
+73.495 2.476 73.613 2.19 73.613 1.837 c
+72.966 1.837 l
+72.966 2.013 72.907 2.164 72.79 2.293 c
+72.672 2.418 72.518 2.484 72.333 2.484 c
+72.136 2.484 71.984 2.429 71.878 2.322 c
+71.768 2.223 71.716 2.09 71.716 1.925 c
+71.716 1.797 71.753 1.69 71.834 1.602 c
+71.911 1.521 72.102 1.418 72.408 1.294 c
+72.885 1.105 73.216 0.918 73.392 0.735 c
+73.569 0.558 73.657 0.33 73.657 0.058 c
+73.657 -0.294 73.532 -0.574 73.289 -0.78 c
+73.054 -0.985 72.738 -1.088 72.348 -1.088 c
+71.926 -1.088 71.588 -0.971 71.334 -0.736 c
+71.078 -0.493 70.952 -0.188 70.952 0.176 c
+71.599 0.176 l
+71.606 -0.052 71.676 -0.229 71.805 -0.353 c
+71.93 -0.47 72.113 -0.53 72.348 -0.53 c
+72.562 -0.53 72.724 -0.482 72.834 -0.383 c
+72.951 -0.287 73.01 -0.158 73.01 0 c
+76.832 2.969 m
+76.846 2.528 l
+77.1 2.869 77.424 3.042 77.817 3.042 c
+78.523 3.042 78.879 2.572 78.889 1.631 c
+78.889 -1.015 l
+78.243 -1.015 l
+78.243 1.602 l
+78.243 1.914 78.188 2.135 78.081 2.263 c
+77.971 2.389 77.817 2.454 77.611 2.454 c
+77.453 2.454 77.306 2.399 77.17 2.293 c
+77.041 2.183 76.938 2.046 76.861 1.881 c
+76.861 -1.015 l
+76.214 -1.015 l
+76.214 2.969 l
+h
+79.728 1.161 m
+79.728 1.738 79.863 2.194 80.139 2.528 c
+80.422 2.869 80.794 3.042 81.256 3.042 c
+81.715 3.042 82.083 2.873 82.359 2.543 c
+82.642 2.219 82.789 1.77 82.799 1.205 c
+82.799 0.779 l
+82.799 0.209 82.656 -0.246 82.373 -0.588 c
+82.097 -0.923 81.73 -1.088 81.27 -1.088 c
+80.808 -1.088 80.437 -0.927 80.154 -0.603 c
+79.878 -0.273 79.735 0.168 79.728 0.72 c
+h
+80.374 0.779 m
+80.374 0.374 80.451 0.058 80.609 -0.177 c
+80.775 -0.412 80.996 -0.53 81.27 -0.53 c
+81.837 -0.53 82.131 -0.118 82.153 0.706 c
+82.153 1.161 l
+82.153 1.562 82.068 1.881 81.903 2.117 c
+81.745 2.358 81.528 2.484 81.256 2.484 c
+80.992 2.484 80.775 2.358 80.609 2.117 c
+80.451 1.881 80.374 1.562 80.374 1.161 c
+h
+84.49 3.924 m
+84.49 2.969 l
+85.093 2.969 l
+85.093 2.439 l
+84.49 2.439 l
+84.49 -0.03 l
+84.49 -0.188 84.512 -0.306 84.563 -0.383 c
+84.622 -0.464 84.711 -0.5 84.828 -0.5 c
+84.916 -0.5 85.004 -0.485 85.093 -0.456 c
+85.093 -1.015 l
+84.946 -1.062 84.792 -1.088 84.637 -1.088 c
+84.38 -1.088 84.185 -0.996 84.049 -0.809 c
+83.909 -0.626 83.843 -0.364 83.843 -0.03 c
+83.843 2.439 l
+83.24 2.439 l
+83.24 2.969 l
+83.843 2.969 l
+83.843 3.924 l
+h
+89.002 -1.088 m
+88.503 -1.088 88.121 -0.941 87.856 -0.647 c
+87.591 -0.353 87.46 0.08 87.46 0.661 c
+87.46 1.132 l
+87.46 1.726 87.584 2.194 87.841 2.528 c
+88.106 2.869 88.466 3.042 88.929 3.042 c
+89.388 3.042 89.731 2.888 89.958 2.587 c
+90.193 2.293 90.315 1.83 90.325 1.205 c
+90.325 0.779 l
+88.106 0.779 l
+88.106 0.69 l
+88.106 0.257 88.183 -0.056 88.341 -0.25 c
+88.507 -0.437 88.738 -0.53 89.031 -0.53 c
+89.227 -0.53 89.399 -0.497 89.546 -0.427 c
+89.694 -0.35 89.829 -0.232 89.958 -0.074 c
+90.296 -0.485 l
+90.01 -0.89 89.579 -1.088 89.002 -1.088 c
+88.929 2.484 m
+88.653 2.484 88.451 2.389 88.326 2.204 c
+88.198 2.017 88.125 1.726 88.106 1.337 c
+89.679 1.337 l
+89.679 1.425 l
+89.657 1.807 89.59 2.076 89.473 2.234 c
+89.355 2.399 89.172 2.484 88.929 2.484 c
+92.251 1.514 m
+92.942 2.969 l
+93.692 2.969 l
+92.604 0.999 l
+93.721 -1.015 l
+92.971 -1.015 l
+92.266 0.47 l
+91.56 -1.015 l
+90.81 -1.015 l
+91.913 0.999 l
+90.84 2.969 l
+91.589 2.969 l
+h
+95.117 -1.015 -0.646 3.984 re
+95.161 4.012 m
+95.161 3.902 95.132 3.81 95.073 3.733 c
+95.015 3.663 94.919 3.63 94.794 3.63 c
+94.676 3.63 94.581 3.663 94.515 3.733 c
+94.456 3.81 94.427 3.902 94.427 4.012 c
+94.427 4.13 94.456 4.222 94.515 4.292 c
+94.581 4.369 94.676 4.409 94.794 4.409 c
+94.919 4.409 95.015 4.369 95.073 4.292 c
+95.132 4.211 95.161 4.119 95.161 4.012 c
+98.072 0 m
+98.072 0.147 98.017 0.268 97.91 0.367 c
+97.8 0.463 97.594 0.58 97.293 0.72 c
+96.948 0.866 96.705 0.988 96.557 1.087 c
+96.411 1.194 96.301 1.311 96.235 1.44 c
+96.164 1.565 96.131 1.723 96.131 1.911 c
+96.131 2.234 96.249 2.502 96.484 2.719 c
+96.719 2.932 97.021 3.042 97.396 3.042 c
+97.778 3.042 98.086 2.929 98.321 2.705 c
+98.557 2.476 98.674 2.19 98.674 1.837 c
+98.028 1.837 l
+98.028 2.013 97.969 2.164 97.851 2.293 c
+97.733 2.418 97.579 2.484 97.396 2.484 c
+97.197 2.484 97.047 2.429 96.94 2.322 c
+96.83 2.223 96.779 2.09 96.779 1.925 c
+96.779 1.797 96.815 1.69 96.896 1.602 c
+96.973 1.521 97.164 1.418 97.469 1.294 c
+97.947 1.105 98.278 0.918 98.454 0.735 c
+98.631 0.558 98.718 0.33 98.718 0.058 c
+98.718 -0.294 98.594 -0.574 98.351 -0.78 c
+98.116 -0.985 97.8 -1.088 97.411 -1.088 c
+96.988 -1.088 96.65 -0.971 96.397 -0.736 c
+96.139 -0.493 96.014 -0.188 96.014 0.176 c
+96.661 0.176 l
+96.668 -0.052 96.738 -0.229 96.867 -0.353 c
+96.991 -0.47 97.176 -0.53 97.411 -0.53 c
+97.623 -0.53 97.785 -0.482 97.895 -0.383 c
+98.013 -0.287 98.072 -0.158 98.072 0 c
+100.409 3.924 m
+100.409 2.969 l
+101.012 2.969 l
+101.012 2.439 l
+100.409 2.439 l
+100.409 -0.03 l
+100.409 -0.188 100.431 -0.306 100.482 -0.383 c
+100.542 -0.464 100.63 -0.5 100.747 -0.5 c
+100.835 -0.5 100.923 -0.485 101.012 -0.456 c
+101.012 -1.015 l
+100.865 -1.062 100.711 -1.088 100.556 -1.088 c
+100.299 -1.088 100.104 -0.996 99.968 -0.809 c
+99.828 -0.626 99.763 -0.364 99.763 -0.03 c
+99.763 2.439 l
+99.159 2.439 l
+99.159 2.969 l
+99.763 2.969 l
+99.763 3.924 l
+h
+101.82 -0.662 m
+101.82 -0.545 101.853 -0.449 101.923 -0.368 c
+101.989 -0.291 102.092 -0.25 102.232 -0.25 c
+102.379 -0.25 102.485 -0.291 102.555 -0.368 c
+102.633 -0.449 102.672 -0.545 102.672 -0.662 c
+102.672 -0.772 102.633 -0.864 102.555 -0.941 c
+102.485 -1.019 102.379 -1.058 102.232 -1.058 c
+102.092 -1.058 101.989 -1.019 101.923 -0.941 c
+101.853 -0.864 101.82 -0.772 101.82 -0.662 c
+f
+Q
+0.113 0.082 0.09 0 k
+535.667 157.87 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 540.8555 151.0312 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.088 -0.5 c
+-0.147 -0.646 -0.242 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.263 l
+-2.142 -1.257 -2.358 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.141 1.279 -3.218 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.218 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.62 -0.279 2.484 c
+-0.201 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.319 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.318 2.385 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.19 c
+-2.476 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.2 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.102 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.52 -2.484 5.403 -2.396 c
+5.285 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.214 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.815 0.243 4.815 0.5 c
+4.815 0.772 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.189 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.029 7.122 2.076 7.122 2.117 c
+7.13 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.992 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.872 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.226 1.621 6.137 1.573 c
+6.056 1.532 5.991 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.498 3.117 l
+14.498 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.498 1.602 l
+14.498 -0.103 l
+14.498 -0.323 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.675 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.687 15.468 -0.675 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.042 -1.301 14.942 -1.308 14.836 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.263 c
+14.222 -1.227 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.792 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+22.281 -1.263 m
+22.281 0.853 l
+22.281 1.018 22.273 1.154 22.265 1.264 c
+22.255 1.371 22.236 1.455 22.207 1.515 c
+22.185 1.58 22.155 1.632 22.119 1.661 c
+22.09 1.691 22.049 1.706 22.001 1.706 c
+21.943 1.706 21.887 1.676 21.839 1.617 c
+21.799 1.565 21.766 1.492 21.737 1.397 c
+21.708 1.309 21.681 1.195 21.663 1.058 c
+21.652 0.919 21.648 0.769 21.648 0.603 c
+21.648 -1.263 l
+20.898 -1.263 l
+20.898 1.47 l
+20.898 1.706 l
+20.898 1.926 l
+20.898 2.003 20.891 2.066 20.884 2.117 c
+20.884 2.22 l
+21.56 2.22 l
+21.56 2.132 l
+21.56 1.985 l
+21.567 1.926 21.575 1.867 21.575 1.808 c
+21.575 1.646 l
+21.59 1.646 l
+21.608 1.735 21.637 1.812 21.677 1.881 c
+21.714 1.959 21.758 2.029 21.81 2.088 c
+21.868 2.147 21.935 2.19 22.016 2.22 c
+22.093 2.257 22.182 2.278 22.281 2.278 c
+22.464 2.278 22.604 2.224 22.692 2.117 c
+22.787 2.018 22.857 1.86 22.898 1.646 c
+22.913 1.646 l
+22.949 1.742 22.99 1.831 23.03 1.912 c
+23.078 1.989 23.133 2.051 23.192 2.103 c
+23.25 2.161 23.317 2.205 23.397 2.234 c
+23.474 2.264 23.563 2.278 23.662 2.278 c
+23.798 2.278 23.912 2.253 24 2.205 c
+24.089 2.153 24.154 2.08 24.206 1.985 c
+24.264 1.885 24.301 1.756 24.324 1.602 c
+24.353 1.455 24.368 1.272 24.368 1.058 c
+24.368 -1.263 l
+23.647 -1.263 l
+23.647 0.853 l
+23.647 1.018 23.64 1.154 23.632 1.264 c
+23.622 1.371 23.603 1.455 23.574 1.515 c
+23.552 1.58 23.522 1.632 23.486 1.661 c
+23.456 1.691 23.416 1.706 23.368 1.706 c
+23.25 1.706 23.155 1.617 23.089 1.441 c
+23.03 1.272 23.001 1.014 23.001 0.661 c
+23.001 -1.263 l
+h
+26.738 -1.323 m
+26.481 -1.323 26.253 -1.286 26.047 -1.22 c
+25.841 -1.143 25.665 -1.028 25.518 -0.881 c
+25.371 -0.727 25.253 -0.536 25.165 -0.309 c
+25.084 -0.085 25.047 0.181 25.047 0.485 c
+25.047 0.816 25.091 1.095 25.18 1.324 c
+25.276 1.559 25.404 1.742 25.562 1.881 c
+25.727 2.018 25.915 2.117 26.121 2.176 c
+26.327 2.242 26.535 2.278 26.753 2.278 c
+27.025 2.278 27.26 2.227 27.458 2.132 c
+27.664 2.043 27.829 1.912 27.958 1.735 c
+28.094 1.565 28.193 1.36 28.252 1.118 c
+28.318 0.882 28.355 0.618 28.355 0.324 c
+28.355 0.309 l
+25.988 0.309 l
+25.988 0.162 26.003 0.023 26.032 -0.103 c
+26.069 -0.231 26.124 -0.345 26.194 -0.44 c
+26.26 -0.529 26.344 -0.598 26.444 -0.646 c
+26.539 -0.698 26.653 -0.72 26.782 -0.72 c
+26.936 -0.72 27.076 -0.687 27.194 -0.617 c
+27.318 -0.551 27.407 -0.448 27.458 -0.309 c
+28.297 -0.382 l
+28.266 -0.481 28.212 -0.588 28.135 -0.706 c
+28.054 -0.816 27.95 -0.918 27.826 -1.014 c
+27.708 -1.103 27.553 -1.176 27.37 -1.234 c
+27.194 -1.294 26.98 -1.323 26.738 -1.323 c
+26.738 1.706 m
+26.65 1.706 26.562 1.691 26.473 1.661 c
+26.385 1.632 26.304 1.58 26.238 1.515 c
+26.169 1.444 26.109 1.357 26.061 1.249 c
+26.022 1.139 26.003 1.014 26.003 0.867 c
+27.472 0.867 l
+27.472 1.004 27.447 1.125 27.399 1.235 c
+27.359 1.341 27.304 1.43 27.237 1.5 c
+27.179 1.565 27.106 1.617 27.017 1.646 c
+26.929 1.683 26.834 1.706 26.738 1.706 c
+32.254 1.47 m
+32.154 1.478 32.052 1.488 31.945 1.5 c
+31.835 1.518 31.713 1.529 31.578 1.529 c
+31.401 1.529 31.243 1.488 31.107 1.411 c
+30.967 1.341 30.85 1.243 30.755 1.118 c
+30.666 0.989 30.597 0.842 30.549 0.676 c
+30.508 0.507 30.49 0.331 30.49 0.148 c
+30.49 -1.263 l
+29.593 -1.263 l
+29.593 0.985 l
+29.593 1.11 29.582 1.235 29.564 1.353 c
+29.552 1.478 29.538 1.596 29.519 1.706 c
+29.509 1.823 29.494 1.918 29.475 1.999 c
+29.454 2.088 29.435 2.161 29.417 2.22 c
+30.298 2.22 l
+30.306 2.168 30.317 2.117 30.328 2.058 c
+30.346 1.999 30.361 1.933 30.373 1.867 c
+30.391 1.808 30.406 1.742 30.416 1.676 c
+30.423 1.606 30.435 1.544 30.446 1.484 c
+30.46 1.484 l
+30.497 1.602 30.549 1.709 30.608 1.808 c
+30.674 1.904 30.755 1.989 30.843 2.058 c
+30.931 2.124 31.034 2.18 31.152 2.22 c
+31.276 2.257 31.424 2.278 31.592 2.278 c
+31.717 2.278 31.835 2.271 31.945 2.264 c
+32.062 2.253 32.166 2.238 32.254 2.22 c
+h
+34.834 -2.66 m
+34.616 -2.66 34.425 -2.635 34.26 -2.587 c
+34.091 -2.547 33.951 -2.484 33.834 -2.396 c
+33.716 -2.315 33.617 -2.219 33.54 -2.102 c
+33.471 -1.984 33.423 -1.855 33.392 -1.72 c
+34.29 -1.616 l
+34.327 -1.753 34.396 -1.859 34.495 -1.94 c
+34.591 -2.028 34.716 -2.072 34.863 -2.072 c
+34.951 -2.072 35.032 -2.057 35.113 -2.028 c
+35.19 -1.999 35.26 -1.944 35.318 -1.866 c
+35.377 -1.797 35.422 -1.705 35.451 -1.587 c
+35.487 -1.469 35.509 -1.323 35.509 -1.146 c
+35.509 -0.955 l
+35.509 -0.889 35.513 -0.831 35.524 -0.779 c
+35.524 -0.588 l
+35.509 -0.588 l
+35.41 -0.816 35.267 -0.977 35.083 -1.072 c
+34.896 -1.172 34.69 -1.22 34.466 -1.22 c
+34.26 -1.22 34.076 -1.183 33.922 -1.103 c
+33.775 -1.014 33.646 -0.897 33.54 -0.75 c
+33.44 -0.595 33.367 -0.411 33.319 -0.205 c
+33.268 0.008 33.246 0.243 33.246 0.5 c
+33.246 0.772 33.268 1.018 33.319 1.235 c
+33.378 1.448 33.459 1.632 33.569 1.779 c
+33.676 1.933 33.808 2.051 33.966 2.132 c
+34.121 2.22 34.308 2.264 34.525 2.264 c
+34.62 2.264 34.72 2.253 34.819 2.234 c
+34.915 2.213 35.002 2.18 35.083 2.132 c
+35.171 2.08 35.249 2.018 35.318 1.941 c
+35.395 1.86 35.458 1.768 35.509 1.661 c
+35.524 1.661 l
+35.524 1.808 l
+35.532 1.867 35.539 1.918 35.539 1.97 c
+35.547 2.029 35.553 2.076 35.553 2.117 c
+35.561 2.165 35.572 2.198 35.583 2.22 c
+36.436 2.22 l
+36.424 2.139 36.413 2.029 36.407 1.881 c
+36.407 1.411 l
+36.407 -1.161 l
+36.407 -1.415 36.37 -1.635 36.303 -1.822 c
+36.234 -2.007 36.131 -2.161 35.995 -2.278 c
+35.855 -2.403 35.69 -2.499 35.495 -2.557 c
+35.297 -2.624 35.076 -2.66 34.834 -2.66 c
+35.524 0.53 m
+35.524 0.742 35.499 0.919 35.451 1.058 c
+35.41 1.206 35.355 1.324 35.289 1.411 c
+35.231 1.5 35.16 1.559 35.083 1.588 c
+35.002 1.625 34.925 1.646 34.848 1.646 c
+34.749 1.646 34.657 1.621 34.568 1.573 c
+34.488 1.532 34.422 1.463 34.363 1.367 c
+34.312 1.279 34.267 1.162 34.231 1.014 c
+34.202 0.875 34.186 0.706 34.186 0.5 c
+34.186 0.125 34.246 -0.154 34.363 -0.338 c
+34.481 -0.515 34.643 -0.602 34.848 -0.602 c
+34.915 -0.602 34.988 -0.588 35.069 -0.558 c
+35.156 -0.521 35.231 -0.463 35.289 -0.382 c
+35.355 -0.294 35.41 -0.176 35.451 -0.029 c
+35.499 0.118 35.524 0.302 35.524 0.53 c
+38.923 -1.323 m
+38.666 -1.323 38.439 -1.286 38.233 -1.22 c
+38.027 -1.143 37.851 -1.028 37.704 -0.881 c
+37.556 -0.727 37.439 -0.536 37.35 -0.309 c
+37.27 -0.085 37.234 0.181 37.234 0.485 c
+37.234 0.816 37.277 1.095 37.365 1.324 c
+37.461 1.559 37.589 1.742 37.747 1.881 c
+37.913 2.018 38.1 2.117 38.306 2.176 c
+38.512 2.242 38.722 2.278 38.938 2.278 c
+39.21 2.278 39.445 2.227 39.644 2.132 c
+39.85 2.043 40.015 1.912 40.143 1.735 c
+40.28 1.565 40.378 1.36 40.438 1.118 c
+40.504 0.882 40.54 0.618 40.54 0.324 c
+40.54 0.309 l
+38.174 0.309 l
+38.174 0.162 38.188 0.023 38.218 -0.103 c
+38.254 -0.231 38.31 -0.345 38.379 -0.44 c
+38.446 -0.529 38.53 -0.598 38.63 -0.646 c
+38.725 -0.698 38.839 -0.72 38.967 -0.72 c
+39.122 -0.72 39.262 -0.687 39.379 -0.617 c
+39.504 -0.551 39.592 -0.448 39.644 -0.309 c
+40.482 -0.382 l
+40.452 -0.481 40.397 -0.588 40.32 -0.706 c
+40.239 -0.816 40.136 -0.918 40.012 -1.014 c
+39.894 -1.103 39.74 -1.176 39.555 -1.234 c
+39.379 -1.294 39.166 -1.323 38.923 -1.323 c
+38.923 1.706 m
+38.836 1.706 38.747 1.691 38.659 1.661 c
+38.57 1.632 38.489 1.58 38.424 1.515 c
+38.354 1.444 38.295 1.357 38.248 1.249 c
+38.207 1.139 38.188 1.014 38.188 0.867 c
+39.659 0.867 l
+39.659 1.004 39.632 1.125 39.585 1.235 c
+39.545 1.341 39.489 1.43 39.424 1.5 c
+39.364 1.565 39.291 1.617 39.203 1.646 c
+39.115 1.683 39.019 1.706 38.923 1.706 c
+46.236 -2.631 m
+46.236 3.514 l
+48.162 3.514 l
+48.162 2.896 l
+47.089 2.896 l
+47.089 -2.013 l
+48.162 -2.013 l
+48.162 -2.631 l
+h
+51.193 1.602 m
+51.193 -1.263 l
+50.297 -1.263 l
+50.297 1.602 l
+49.473 1.602 l
+49.473 2.22 l
+50.297 2.22 l
+50.297 2.484 l
+50.297 2.61 50.312 2.741 50.341 2.881 c
+50.378 3.017 50.447 3.135 50.547 3.234 c
+50.653 3.341 50.796 3.429 50.973 3.499 c
+51.149 3.564 51.374 3.601 51.649 3.601 c
+51.862 3.601 52.06 3.591 52.237 3.572 c
+52.413 3.55 52.564 3.532 52.693 3.514 c
+52.693 2.926 l
+52.564 2.944 52.421 2.959 52.267 2.97 c
+52.109 2.977 51.958 2.984 51.811 2.984 c
+51.682 2.984 51.58 2.97 51.502 2.94 c
+51.422 2.911 51.359 2.87 51.311 2.822 c
+51.26 2.771 51.226 2.708 51.208 2.631 c
+51.197 2.562 51.193 2.484 51.193 2.396 c
+51.193 2.22 l
+52.619 2.22 l
+52.619 1.602 l
+h
+56.625 1.47 m
+56.525 1.478 56.423 1.488 56.316 1.5 c
+56.205 1.518 56.085 1.529 55.949 1.529 c
+55.773 1.529 55.615 1.488 55.478 1.411 c
+55.339 1.341 55.221 1.243 55.125 1.118 c
+55.037 0.989 54.967 0.842 54.919 0.676 c
+54.879 0.507 54.861 0.331 54.861 0.148 c
+54.861 -1.263 l
+53.965 -1.263 l
+53.965 0.985 l
+53.965 1.11 53.953 1.235 53.934 1.353 c
+53.924 1.478 53.909 1.596 53.891 1.706 c
+53.88 1.823 53.865 1.918 53.847 1.999 c
+53.824 2.088 53.807 2.161 53.788 2.22 c
+54.67 2.22 l
+54.677 2.168 54.688 2.117 54.699 2.058 c
+54.717 1.999 54.732 1.933 54.744 1.867 c
+54.761 1.808 54.776 1.742 54.788 1.676 c
+54.794 1.606 54.806 1.544 54.817 1.484 c
+54.832 1.484 l
+54.869 1.602 54.919 1.709 54.979 1.808 c
+55.044 1.904 55.125 1.989 55.214 2.058 c
+55.302 2.124 55.405 2.18 55.522 2.22 c
+55.648 2.257 55.794 2.278 55.964 2.278 c
+56.088 2.278 56.205 2.271 56.316 2.264 c
+56.434 2.253 56.536 2.238 56.625 2.22 c
+h
+60.876 0.485 m
+60.876 0.21 60.839 -0.04 60.774 -0.264 c
+60.704 -0.481 60.601 -0.669 60.465 -0.823 c
+60.325 -0.981 60.149 -1.103 59.935 -1.19 c
+59.719 -1.278 59.465 -1.323 59.172 -1.323 c
+58.896 -1.323 58.649 -1.278 58.437 -1.19 c
+58.231 -1.103 58.058 -0.981 57.922 -0.823 c
+57.782 -0.669 57.68 -0.481 57.614 -0.264 c
+57.543 -0.04 57.51 0.21 57.51 0.485 c
+57.51 0.738 57.539 0.974 57.599 1.191 c
+57.664 1.415 57.768 1.606 57.907 1.764 c
+58.044 1.929 58.219 2.058 58.437 2.147 c
+58.649 2.234 58.907 2.278 59.201 2.278 c
+59.513 2.278 59.774 2.234 59.98 2.147 c
+60.193 2.058 60.365 1.929 60.494 1.764 c
+60.631 1.606 60.729 1.415 60.789 1.191 c
+60.847 0.974 60.876 0.738 60.876 0.485 c
+59.921 0.485 m
+59.921 0.691 59.906 0.867 59.877 1.014 c
+59.855 1.162 59.818 1.283 59.76 1.382 c
+59.7 1.478 59.627 1.548 59.538 1.588 c
+59.451 1.636 59.341 1.661 59.216 1.661 c
+58.951 1.661 58.76 1.562 58.642 1.367 c
+58.524 1.18 58.466 0.886 58.466 0.485 c
+58.466 0.063 58.524 -0.243 58.642 -0.426 c
+58.76 -0.613 58.936 -0.706 59.172 -0.706 c
+59.297 -0.706 59.411 -0.687 59.509 -0.646 c
+59.605 -0.598 59.686 -0.525 59.745 -0.426 c
+59.811 -0.33 59.855 -0.205 59.877 -0.058 c
+59.906 0.088 59.921 0.268 59.921 0.485 c
+62.894 -1.263 m
+62.894 0.853 l
+62.894 1.018 62.887 1.154 62.879 1.264 c
+62.869 1.371 62.85 1.455 62.821 1.515 c
+62.798 1.58 62.769 1.632 62.732 1.661 c
+62.703 1.691 62.663 1.706 62.615 1.706 c
+62.555 1.706 62.501 1.676 62.453 1.617 c
+62.412 1.565 62.38 1.492 62.35 1.397 c
+62.32 1.309 62.295 1.195 62.277 1.058 c
+62.266 0.919 62.262 0.769 62.262 0.603 c
+62.262 -1.263 l
+61.512 -1.263 l
+61.512 1.47 l
+61.512 1.706 l
+61.512 1.926 l
+61.512 2.003 61.505 2.066 61.497 2.117 c
+61.497 2.22 l
+62.173 2.22 l
+62.173 2.132 l
+62.173 1.985 l
+62.181 1.926 62.189 1.867 62.189 1.808 c
+62.189 1.646 l
+62.203 1.646 l
+62.221 1.735 62.251 1.812 62.291 1.881 c
+62.328 1.959 62.372 2.029 62.424 2.088 c
+62.482 2.147 62.549 2.19 62.63 2.22 c
+62.707 2.257 62.794 2.278 62.894 2.278 c
+63.078 2.278 63.218 2.224 63.305 2.117 c
+63.401 2.018 63.471 1.86 63.511 1.646 c
+63.526 1.646 l
+63.563 1.742 63.603 1.831 63.644 1.912 c
+63.691 1.989 63.746 2.051 63.806 2.103 c
+63.864 2.161 63.93 2.205 64.011 2.234 c
+64.088 2.264 64.176 2.278 64.276 2.278 c
+64.411 2.278 64.525 2.253 64.614 2.205 c
+64.702 2.153 64.768 2.08 64.82 1.985 c
+64.878 1.885 64.915 1.756 64.937 1.602 c
+64.967 1.455 64.982 1.272 64.982 1.058 c
+64.982 -1.263 l
+64.261 -1.263 l
+64.261 0.853 l
+64.261 1.018 64.253 1.154 64.246 1.264 c
+64.236 1.371 64.217 1.455 64.188 1.515 c
+64.165 1.58 64.136 1.632 64.099 1.661 c
+64.07 1.691 64.03 1.706 63.982 1.706 c
+63.864 1.706 63.769 1.617 63.702 1.441 c
+63.644 1.272 63.614 1.014 63.614 0.661 c
+63.614 -1.263 l
+h
+72.044 -1.263 m
+72.044 0.721 l
+72.044 1.022 72 1.243 71.912 1.382 c
+71.831 1.529 71.695 1.602 71.5 1.602 c
+71.39 1.602 71.287 1.577 71.191 1.529 c
+71.103 1.478 71.022 1.411 70.956 1.324 c
+70.898 1.235 70.846 1.125 70.81 1 c
+70.78 0.882 70.765 0.75 70.765 0.603 c
+70.765 -1.263 l
+69.854 -1.263 l
+69.854 1.441 l
+69.854 1.661 l
+69.854 1.75 69.846 1.827 69.84 1.897 c
+69.84 2.088 l
+69.84 2.22 l
+70.692 2.22 l
+70.699 2.19 70.707 2.147 70.707 2.088 c
+70.707 1.897 l
+70.714 1.827 70.721 1.756 70.721 1.691 c
+70.729 1.621 70.736 1.565 70.736 1.529 c
+70.75 1.529 l
+70.868 1.794 71.019 1.985 71.206 2.103 c
+71.39 2.22 71.61 2.278 71.868 2.278 c
+72.051 2.278 72.213 2.249 72.353 2.19 c
+72.489 2.132 72.602 2.043 72.691 1.926 c
+72.779 1.808 72.841 1.665 72.882 1.5 c
+72.93 1.341 72.955 1.154 72.955 0.941 c
+72.955 -1.263 l
+h
+74.873 -1.323 m
+74.705 -1.323 74.554 -1.301 74.418 -1.263 c
+74.289 -1.216 74.175 -1.146 74.079 -1.058 c
+73.992 -0.97 73.922 -0.864 73.874 -0.735 c
+73.823 -0.598 73.801 -0.448 73.801 -0.279 c
+73.801 -0.073 73.834 0.096 73.904 0.235 c
+73.969 0.383 74.065 0.493 74.183 0.574 c
+74.308 0.661 74.451 0.724 74.609 0.765 c
+74.775 0.802 74.951 0.827 75.138 0.838 c
+75.858 0.853 l
+75.858 1.029 l
+75.858 1.147 75.847 1.249 75.829 1.338 c
+75.807 1.426 75.774 1.492 75.726 1.544 c
+75.685 1.602 75.638 1.639 75.579 1.661 c
+75.521 1.679 75.454 1.691 75.388 1.691 c
+75.318 1.691 75.255 1.679 75.197 1.661 c
+75.145 1.65 75.098 1.625 75.05 1.588 c
+75.01 1.559 74.977 1.507 74.947 1.441 c
+74.925 1.382 74.91 1.301 74.903 1.206 c
+73.962 1.249 l
+73.992 1.397 74.036 1.532 74.094 1.661 c
+74.16 1.786 74.256 1.897 74.374 1.985 c
+74.492 2.08 74.631 2.153 74.8 2.205 c
+74.977 2.253 75.182 2.278 75.417 2.278 c
+75.858 2.278 76.19 2.168 76.417 1.955 c
+76.652 1.75 76.77 1.441 76.77 1.029 c
+76.77 -0.235 l
+76.77 -0.455 l
+76.778 -0.515 76.792 -0.569 76.814 -0.617 c
+76.832 -0.658 76.861 -0.691 76.902 -0.72 c
+76.939 -0.742 76.99 -0.75 77.049 -0.75 c
+77.115 -0.75 77.185 -0.746 77.254 -0.735 c
+77.254 -1.22 l
+77.196 -1.23 77.141 -1.242 77.094 -1.249 c
+77.053 -1.261 77.013 -1.267 76.976 -1.278 c
+76.936 -1.286 76.891 -1.294 76.843 -1.294 c
+76.792 -1.301 76.733 -1.308 76.667 -1.308 c
+76.439 -1.308 76.274 -1.257 76.167 -1.146 c
+76.057 -1.028 75.995 -0.864 75.976 -0.646 c
+75.961 -0.646 l
+75.891 -0.756 75.822 -0.852 75.756 -0.941 c
+75.685 -1.022 75.608 -1.087 75.521 -1.146 c
+75.432 -1.205 75.333 -1.249 75.226 -1.278 c
+75.127 -1.308 75.01 -1.323 74.873 -1.323 c
+75.858 0.353 m
+75.432 0.339 l
+75.333 0.339 75.241 0.331 75.153 0.324 c
+75.072 0.312 75.006 0.287 74.947 0.25 c
+74.889 0.21 74.837 0.151 74.8 0.073 c
+74.76 0.004 74.742 -0.087 74.742 -0.205 c
+74.742 -0.374 74.775 -0.496 74.844 -0.573 c
+74.91 -0.654 75.01 -0.691 75.138 -0.691 c
+75.245 -0.691 75.344 -0.669 75.432 -0.617 c
+75.528 -0.569 75.608 -0.507 75.667 -0.426 c
+75.733 -0.349 75.785 -0.261 75.814 -0.162 c
+75.843 -0.055 75.858 0.059 75.858 0.177 c
+h
+79.14 -1.263 m
+79.14 0.853 l
+79.14 1.018 79.133 1.154 79.126 1.264 c
+79.114 1.371 79.096 1.455 79.066 1.515 c
+79.045 1.58 79.016 1.632 78.978 1.661 c
+78.949 1.691 78.908 1.706 78.861 1.706 c
+78.802 1.706 78.746 1.676 78.699 1.617 c
+78.659 1.565 78.626 1.492 78.596 1.397 c
+78.567 1.309 78.541 1.195 78.523 1.058 c
+78.511 0.919 78.508 0.769 78.508 0.603 c
+78.508 -1.263 l
+77.759 -1.263 l
+77.759 1.47 l
+77.759 1.706 l
+77.759 1.926 l
+77.759 2.003 77.751 2.066 77.744 2.117 c
+77.744 2.22 l
+78.42 2.22 l
+78.42 2.132 l
+78.42 1.985 l
+78.428 1.926 78.434 1.867 78.434 1.808 c
+78.434 1.646 l
+78.449 1.646 l
+78.468 1.735 78.497 1.812 78.538 1.881 c
+78.574 1.959 78.619 2.029 78.669 2.088 c
+78.729 2.147 78.794 2.19 78.875 2.22 c
+78.952 2.257 79.041 2.278 79.14 2.278 c
+79.324 2.278 79.463 2.224 79.552 2.117 c
+79.647 2.018 79.717 1.86 79.758 1.646 c
+79.772 1.646 l
+79.809 1.742 79.849 1.831 79.889 1.912 c
+79.937 1.989 79.993 2.051 80.051 2.103 c
+80.111 2.161 80.176 2.205 80.257 2.234 c
+80.334 2.264 80.423 2.278 80.522 2.278 c
+80.658 2.278 80.772 2.253 80.859 2.205 c
+80.948 2.153 81.015 2.08 81.065 1.985 c
+81.125 1.885 81.161 1.756 81.183 1.602 c
+81.213 1.455 81.227 1.272 81.227 1.058 c
+81.227 -1.263 l
+80.508 -1.263 l
+80.508 0.853 l
+80.508 1.018 80.5 1.154 80.492 1.264 c
+80.481 1.371 80.463 1.455 80.433 1.515 c
+80.412 1.58 80.382 1.632 80.346 1.661 c
+80.316 1.691 80.275 1.706 80.228 1.706 c
+80.111 1.706 80.015 1.617 79.949 1.441 c
+79.889 1.272 79.86 1.014 79.86 0.661 c
+79.86 -1.263 l
+h
+83.598 -1.323 m
+83.34 -1.323 83.112 -1.286 82.906 -1.22 c
+82.701 -1.143 82.524 -1.028 82.378 -0.881 c
+82.231 -0.727 82.113 -0.536 82.025 -0.309 c
+81.944 -0.085 81.907 0.181 81.907 0.485 c
+81.907 0.816 81.952 1.095 82.039 1.324 c
+82.135 1.559 82.264 1.742 82.422 1.881 c
+82.587 2.018 82.775 2.117 82.98 2.176 c
+83.186 2.242 83.396 2.278 83.612 2.278 c
+83.884 2.278 84.119 2.227 84.318 2.132 c
+84.523 2.043 84.689 1.912 84.818 1.735 c
+84.953 1.565 85.053 1.36 85.112 1.118 c
+85.177 0.882 85.214 0.618 85.214 0.324 c
+85.214 0.309 l
+82.848 0.309 l
+82.848 0.162 82.862 0.023 82.892 -0.103 c
+82.929 -0.231 82.984 -0.345 83.054 -0.44 c
+83.12 -0.529 83.205 -0.598 83.303 -0.646 c
+83.399 -0.698 83.513 -0.72 83.642 -0.72 c
+83.796 -0.72 83.936 -0.687 84.053 -0.617 c
+84.178 -0.551 84.267 -0.448 84.318 -0.309 c
+85.156 -0.382 l
+85.127 -0.481 85.071 -0.588 84.994 -0.706 c
+84.913 -0.816 84.81 -0.918 84.685 -1.014 c
+84.568 -1.103 84.414 -1.176 84.23 -1.234 c
+84.053 -1.294 83.84 -1.323 83.598 -1.323 c
+83.598 1.706 m
+83.509 1.706 83.421 1.691 83.333 1.661 c
+83.245 1.632 83.164 1.58 83.097 1.515 c
+83.028 1.444 82.969 1.357 82.922 1.249 c
+82.881 1.139 82.862 1.014 82.862 0.867 c
+84.333 0.867 l
+84.333 1.004 84.307 1.125 84.259 1.235 c
+84.219 1.341 84.163 1.43 84.097 1.5 c
+84.038 1.565 83.965 1.617 83.877 1.646 c
+83.789 1.683 83.693 1.706 83.598 1.706 c
+86.467 -2.631 m
+86.467 -2.013 l
+87.541 -2.013 l
+87.541 2.896 l
+86.467 2.896 l
+86.467 3.514 l
+88.393 3.514 l
+88.393 -2.631 l
+h
+f
+Q
+q 1 0 0 1 546.3644 140.2277 cm
+0 0 m
+0.676 0 l
+0.676 -3.792 l
+0.676 -4.303 0.544 -4.704 0.279 -4.998 c
+0.022 -5.284 -0.324 -5.424 -0.765 -5.424 c
+-1.228 -5.424 -1.584 -5.288 -1.837 -5.012 c
+-2.084 -4.74 -2.205 -4.358 -2.205 -3.865 c
+-1.529 -3.865 l
+-1.529 -4.182 -1.467 -4.424 -1.338 -4.601 c
+-1.201 -4.77 -1.01 -4.85 -0.765 -4.85 c
+-0.54 -4.85 -0.36 -4.759 -0.221 -4.571 c
+-0.073 -4.388 0 -4.123 0 -3.778 c
+h
+1.573 -3.175 m
+1.573 -2.598 1.708 -2.142 1.984 -1.808 c
+2.267 -1.467 2.639 -1.294 3.102 -1.294 c
+3.561 -1.294 3.929 -1.463 4.204 -1.793 c
+4.486 -2.117 4.634 -2.565 4.644 -3.131 c
+4.644 -3.557 l
+4.644 -4.127 4.501 -4.582 4.218 -4.924 c
+3.943 -5.259 3.576 -5.424 3.116 -5.424 c
+2.653 -5.424 2.282 -5.262 1.999 -4.939 c
+1.723 -4.609 1.58 -4.167 1.573 -3.616 c
+h
+2.219 -3.557 m
+2.219 -3.961 2.296 -4.278 2.454 -4.513 c
+2.62 -4.748 2.84 -4.865 3.116 -4.865 c
+3.682 -4.865 3.976 -4.453 3.998 -3.63 c
+3.998 -3.175 l
+3.998 -2.774 3.913 -2.454 3.748 -2.219 c
+3.59 -1.977 3.374 -1.852 3.102 -1.852 c
+2.836 -1.852 2.62 -1.977 2.454 -2.219 c
+2.296 -2.454 2.219 -2.774 2.219 -3.175 c
+h
+6.188 -5.351 -0.647 3.984 re
+6.232 -0.324 m
+6.232 -0.434 6.203 -0.526 6.144 -0.603 c
+6.086 -0.673 5.99 -0.706 5.865 -0.706 c
+5.747 -0.706 5.652 -0.673 5.585 -0.603 c
+5.527 -0.526 5.498 -0.434 5.498 -0.324 c
+5.498 -0.206 5.527 -0.114 5.585 -0.044 c
+5.652 0.033 5.747 0.073 5.865 0.073 c
+5.99 0.073 6.086 0.033 6.144 -0.044 c
+6.203 -0.125 6.232 -0.217 6.232 -0.324 c
+7.819 -1.367 m
+7.835 -1.808 l
+8.088 -1.467 8.411 -1.294 8.804 -1.294 c
+9.51 -1.294 9.866 -1.764 9.878 -2.705 c
+9.878 -5.351 l
+9.231 -5.351 l
+9.231 -2.734 l
+9.231 -2.421 9.176 -2.201 9.069 -2.072 c
+8.959 -1.947 8.804 -1.881 8.598 -1.881 c
+8.441 -1.881 8.294 -1.937 8.158 -2.043 c
+8.029 -2.153 7.927 -2.29 7.85 -2.454 c
+7.85 -5.351 l
+7.202 -5.351 l
+7.202 -1.367 l
+h
+14.508 -4.336 m
+14.508 -4.189 14.453 -4.068 14.346 -3.969 c
+14.236 -3.873 14.03 -3.755 13.729 -3.616 c
+13.383 -3.469 13.141 -3.348 12.993 -3.248 c
+12.847 -3.142 12.737 -3.024 12.671 -2.896 c
+12.6 -2.77 12.567 -2.612 12.567 -2.425 c
+12.567 -2.102 12.685 -1.833 12.92 -1.617 c
+13.155 -1.404 13.457 -1.294 13.832 -1.294 c
+14.214 -1.294 14.522 -1.407 14.757 -1.631 c
+14.993 -1.86 15.11 -2.146 15.11 -2.499 c
+14.464 -2.499 l
+14.464 -2.323 14.405 -2.172 14.287 -2.043 c
+14.169 -1.918 14.015 -1.852 13.832 -1.852 c
+13.633 -1.852 13.483 -1.907 13.376 -2.014 c
+13.265 -2.113 13.215 -2.246 13.215 -2.411 c
+13.215 -2.539 13.251 -2.646 13.332 -2.734 c
+13.409 -2.815 13.6 -2.918 13.905 -3.042 c
+14.383 -3.23 14.713 -3.418 14.89 -3.601 c
+15.066 -3.778 15.154 -4.006 15.154 -4.278 c
+15.154 -4.63 15.029 -4.91 14.787 -5.116 c
+14.552 -5.321 14.236 -5.424 13.847 -5.424 c
+13.423 -5.424 13.086 -5.307 12.832 -5.072 c
+12.575 -4.829 12.45 -4.524 12.45 -4.16 c
+13.097 -4.16 l
+13.104 -4.388 13.174 -4.564 13.302 -4.689 c
+13.427 -4.806 13.612 -4.865 13.847 -4.865 c
+14.059 -4.865 14.221 -4.818 14.331 -4.719 c
+14.449 -4.623 14.508 -4.494 14.508 -4.336 c
+18.873 -3.557 m
+18.873 -4.185 18.755 -4.656 18.52 -4.968 c
+18.293 -5.274 17.977 -5.424 17.565 -5.424 c
+17.161 -5.424 16.852 -5.274 16.639 -4.968 c
+16.639 -6.88 l
+15.993 -6.88 l
+15.993 -1.367 l
+16.581 -1.367 l
+16.625 -1.808 l
+16.837 -1.467 17.146 -1.294 17.55 -1.294 c
+17.992 -1.294 18.319 -1.448 18.535 -1.749 c
+18.749 -2.055 18.863 -2.51 18.873 -3.117 c
+h
+18.227 -3.175 m
+18.227 -2.734 18.157 -2.411 18.021 -2.205 c
+17.882 -1.992 17.66 -1.881 17.359 -1.881 c
+17.043 -1.881 16.804 -2.036 16.639 -2.337 c
+16.639 -4.41 l
+16.804 -4.715 17.043 -4.865 17.359 -4.865 c
+17.653 -4.865 17.867 -4.763 18.006 -4.557 c
+18.142 -4.343 18.216 -4.013 18.227 -3.572 c
+h
+21.138 -5.424 m
+20.637 -5.424 20.255 -5.277 19.991 -4.983 c
+19.726 -4.689 19.594 -4.256 19.594 -3.675 c
+19.594 -3.204 l
+19.594 -2.61 19.719 -2.142 19.976 -1.808 c
+20.24 -1.467 20.6 -1.294 21.063 -1.294 c
+21.523 -1.294 21.865 -1.448 22.092 -1.749 c
+22.327 -2.043 22.449 -2.506 22.46 -3.131 c
+22.46 -3.557 l
+20.24 -3.557 l
+20.24 -3.645 l
+20.24 -4.079 20.317 -4.391 20.475 -4.586 c
+20.641 -4.773 20.872 -4.865 21.167 -4.865 c
+21.361 -4.865 21.534 -4.833 21.681 -4.763 c
+21.828 -4.686 21.963 -4.568 22.092 -4.41 c
+22.431 -4.821 l
+22.144 -5.226 21.714 -5.424 21.138 -5.424 c
+21.063 -1.852 m
+20.788 -1.852 20.586 -1.947 20.461 -2.132 c
+20.332 -2.319 20.259 -2.61 20.24 -2.999 c
+21.813 -2.999 l
+21.813 -2.911 l
+21.791 -2.529 21.725 -2.26 21.608 -2.102 c
+21.49 -1.937 21.306 -1.852 21.063 -1.852 c
+24.621 -4.865 m
+24.834 -4.865 25.007 -4.803 25.136 -4.675 c
+25.271 -4.538 25.344 -4.347 25.356 -4.101 c
+25.973 -4.101 l
+25.951 -4.484 25.815 -4.803 25.562 -5.056 c
+25.304 -5.303 24.992 -5.424 24.621 -5.424 c
+24.128 -5.424 23.754 -5.274 23.489 -4.968 c
+23.232 -4.656 23.106 -4.189 23.106 -3.572 c
+23.106 -3.131 l
+23.106 -2.535 23.232 -2.08 23.489 -1.764 c
+23.754 -1.452 24.128 -1.294 24.621 -1.294 c
+25.021 -1.294 25.341 -1.426 25.576 -1.691 c
+25.819 -1.947 25.951 -2.294 25.973 -2.734 c
+25.356 -2.734 l
+25.334 -2.44 25.26 -2.219 25.136 -2.072 c
+25.018 -1.926 24.845 -1.852 24.621 -1.852 c
+24.327 -1.852 24.11 -1.951 23.974 -2.146 c
+23.835 -2.334 23.761 -2.643 23.754 -3.072 c
+23.754 -3.587 l
+23.754 -4.057 23.82 -4.391 23.96 -4.586 c
+24.106 -4.773 24.327 -4.865 24.621 -4.865 c
+27.428 -5.351 -0.646 3.984 re
+27.472 -0.324 m
+27.472 -0.434 27.443 -0.526 27.384 -0.603 c
+27.326 -0.673 27.23 -0.706 27.105 -0.706 c
+26.987 -0.706 26.892 -0.673 26.825 -0.603 c
+26.767 -0.526 26.738 -0.434 26.738 -0.324 c
+26.738 -0.206 26.767 -0.114 26.825 -0.044 c
+26.892 0.033 26.987 0.073 27.105 0.073 c
+27.23 0.073 27.326 0.033 27.384 -0.044 c
+27.443 -0.125 27.472 -0.217 27.472 -0.324 c
+28.619 -5.351 m
+28.619 -1.897 l
+28.105 -1.897 l
+28.105 -1.367 l
+28.619 -1.367 l
+28.619 -1 l
+28.627 -0.57 28.741 -0.235 28.957 0 c
+29.181 0.243 29.493 0.368 29.898 0.368 c
+30.044 0.368 30.185 0.345 30.324 0.309 c
+30.471 0.268 30.622 0.213 30.78 0.147 c
+30.662 -0.426 l
+30.427 -0.301 30.185 -0.235 29.942 -0.235 c
+29.695 -0.235 29.523 -0.305 29.427 -0.441 c
+29.328 -0.57 29.281 -0.765 29.281 -1.029 c
+29.281 -1.367 l
+29.927 -1.367 l
+29.927 -1.897 l
+29.281 -1.897 l
+29.281 -5.351 l
+h
+31.089 -5.351 -0.647 3.984 re
+33.558 -5.424 m
+33.058 -5.424 32.676 -5.277 32.411 -4.983 c
+32.147 -4.689 32.014 -4.256 32.014 -3.675 c
+32.014 -3.204 l
+32.014 -2.61 32.139 -2.142 32.396 -1.808 c
+32.662 -1.467 33.021 -1.294 33.485 -1.294 c
+33.944 -1.294 34.285 -1.448 34.513 -1.749 c
+34.748 -2.043 34.869 -2.506 34.881 -3.131 c
+34.881 -3.557 l
+32.662 -3.557 l
+32.662 -3.645 l
+32.662 -4.079 32.739 -4.391 32.897 -4.586 c
+33.061 -4.773 33.293 -4.865 33.587 -4.865 c
+33.782 -4.865 33.955 -4.833 34.102 -4.763 c
+34.249 -4.686 34.385 -4.568 34.513 -4.41 c
+34.852 -4.821 l
+34.565 -5.226 34.135 -5.424 33.558 -5.424 c
+33.485 -1.852 m
+33.209 -1.852 33.007 -1.947 32.882 -2.132 c
+32.753 -2.319 32.679 -2.61 32.662 -2.999 c
+34.233 -2.999 l
+34.233 -2.911 l
+34.212 -2.529 34.146 -2.26 34.028 -2.102 c
+33.911 -1.937 33.726 -1.852 33.485 -1.852 c
+35.527 -3.175 m
+35.527 -2.569 35.638 -2.102 35.866 -1.779 c
+36.101 -1.455 36.428 -1.294 36.851 -1.294 c
+37.233 -1.294 37.53 -1.452 37.747 -1.764 c
+37.747 0.294 l
+38.393 0.294 l
+38.393 -5.351 l
+37.805 -5.351 l
+37.761 -4.924 l
+37.556 -5.259 37.25 -5.424 36.851 -5.424 c
+36.439 -5.424 36.115 -5.27 35.88 -4.954 c
+35.645 -4.63 35.527 -4.175 35.527 -3.587 c
+h
+36.174 -3.557 m
+36.174 -3.998 36.236 -4.329 36.365 -4.542 c
+36.502 -4.748 36.722 -4.85 37.027 -4.85 c
+37.35 -4.85 37.589 -4.689 37.747 -4.366 c
+37.747 -2.352 l
+37.578 -2.04 37.339 -1.881 37.027 -1.881 c
+36.722 -1.881 36.502 -1.984 36.365 -2.19 c
+36.236 -2.396 36.174 -2.72 36.174 -3.16 c
+h
+f
+Q
+q 1 0 0 1 588.8294 140.2861 cm
+0 0 m
+-0.382 0 l
+-0.382 -5.835 l
+0 -5.835 l
+0 -6.659 l
+-1.425 -6.659 l
+-1.425 0.823 l
+0 0.823 l
+h
+0.765 -5.409 m
+0.765 -2.219 l
+0.279 -2.219 l
+0.279 -1.425 l
+0.765 -1.425 l
+0.765 -1.072 l
+0.765 -0.631 0.875 -0.294 1.103 -0.058 c
+1.338 0.184 1.654 0.31 2.058 0.31 c
+2.183 0.31 2.341 0.283 2.529 0.235 c
+2.529 -0.588 l
+2.459 -0.569 2.374 -0.558 2.278 -0.558 c
+1.962 -0.558 1.808 -0.742 1.808 -1.102 c
+1.808 -1.425 l
+2.426 -1.425 l
+2.426 -2.219 l
+1.808 -2.219 l
+1.808 -5.409 l
+h
+4.954 -2.439 m
+4.616 -2.41 l
+4.329 -2.41 4.138 -2.535 4.042 -2.778 c
+4.042 -5.409 l
+2.999 -5.409 l
+2.999 -1.425 l
+3.969 -1.425 l
+3.998 -1.866 l
+4.164 -1.525 4.395 -1.352 4.69 -1.352 c
+4.807 -1.352 4.898 -1.374 4.969 -1.411 c
+h
+5.248 -3.293 m
+5.248 -2.686 5.388 -2.212 5.675 -1.866 c
+5.958 -1.525 6.35 -1.352 6.85 -1.352 c
+7.358 -1.352 7.755 -1.525 8.04 -1.866 c
+8.324 -2.212 8.467 -2.686 8.467 -3.293 c
+8.467 -3.557 l
+8.467 -4.156 8.324 -4.627 8.04 -4.968 c
+7.755 -5.313 7.358 -5.483 6.85 -5.483 c
+6.34 -5.483 5.943 -5.313 5.659 -4.968 c
+5.384 -4.627 5.248 -4.152 5.248 -3.542 c
+h
+6.292 -3.557 m
+6.292 -4.262 6.475 -4.615 6.85 -4.615 c
+7.203 -4.615 7.394 -4.321 7.423 -3.733 c
+7.423 -3.293 l
+7.423 -2.932 7.372 -2.66 7.277 -2.484 c
+7.177 -2.308 7.034 -2.219 6.85 -2.219 c
+6.674 -2.219 6.534 -2.308 6.438 -2.484 c
+6.34 -2.66 6.292 -2.932 6.292 -3.293 c
+h
+10.084 -1.425 m
+10.113 -1.793 l
+10.348 -1.499 10.658 -1.352 11.04 -1.352 c
+11.44 -1.352 11.719 -1.535 11.877 -1.896 c
+12.112 -1.535 12.439 -1.352 12.862 -1.352 c
+13.557 -1.352 13.91 -1.837 13.92 -2.807 c
+13.92 -5.409 l
+12.892 -5.409 l
+12.892 -2.865 l
+12.892 -2.642 12.855 -2.48 12.788 -2.381 c
+12.73 -2.285 12.62 -2.234 12.465 -2.234 c
+12.267 -2.234 12.127 -2.352 12.039 -2.587 c
+12.039 -5.409 l
+10.995 -5.409 l
+10.995 -2.881 l
+10.995 -2.645 10.966 -2.48 10.907 -2.381 c
+10.849 -2.285 10.739 -2.234 10.583 -2.234 c
+10.407 -2.234 10.264 -2.329 10.157 -2.513 c
+10.157 -5.409 l
+9.114 -5.409 l
+9.114 -1.425 l
+h
+17.39 -1.425 m
+17.419 -1.822 l
+17.654 -1.51 17.955 -1.352 18.33 -1.352 c
+19.013 -1.352 19.366 -1.833 19.389 -2.792 c
+19.389 -5.409 l
+18.344 -5.409 l
+18.344 -2.865 l
+18.344 -2.642 18.308 -2.48 18.242 -2.381 c
+18.172 -2.285 18.055 -2.234 17.889 -2.234 c
+17.702 -2.234 17.555 -2.329 17.448 -2.513 c
+17.448 -5.409 l
+16.405 -5.409 l
+16.405 -1.425 l
+h
+22.02 -5.409 m
+21.99 -5.35 21.961 -5.247 21.932 -5.1 c
+21.744 -5.357 21.494 -5.483 21.182 -5.483 c
+20.848 -5.483 20.568 -5.375 20.344 -5.159 c
+20.127 -4.935 20.021 -4.644 20.021 -4.292 c
+20.021 -3.881 20.153 -3.564 20.418 -3.336 c
+20.682 -3.101 21.064 -2.983 21.564 -2.983 c
+21.887 -2.983 l
+21.887 -2.66 l
+21.887 -2.484 21.851 -2.362 21.785 -2.293 c
+21.725 -2.215 21.637 -2.175 21.519 -2.175 c
+21.263 -2.175 21.137 -2.329 21.137 -2.63 c
+20.094 -2.63 l
+20.094 -2.26 20.23 -1.955 20.505 -1.72 c
+20.777 -1.477 21.126 -1.352 21.55 -1.352 c
+21.99 -1.352 22.328 -1.469 22.564 -1.705 c
+22.806 -1.932 22.93 -2.256 22.93 -2.675 c
+22.93 -4.542 l
+22.93 -4.887 22.979 -5.155 23.078 -5.35 c
+23.078 -5.409 l
+h
+21.417 -4.659 m
+21.523 -4.659 21.615 -4.641 21.696 -4.6 c
+21.785 -4.552 21.847 -4.494 21.887 -4.424 c
+21.887 -3.601 l
+21.637 -3.601 l
+21.461 -3.601 21.318 -3.659 21.211 -3.777 c
+21.112 -3.887 21.064 -4.035 21.064 -4.218 c
+21.064 -4.512 21.182 -4.659 21.417 -4.659 c
+24.651 -1.425 m
+24.68 -1.793 l
+24.916 -1.499 25.224 -1.352 25.606 -1.352 c
+26.007 -1.352 26.286 -1.535 26.444 -1.896 c
+26.679 -1.535 27.006 -1.352 27.429 -1.352 c
+28.123 -1.352 28.476 -1.837 28.487 -2.807 c
+28.487 -5.409 l
+27.458 -5.409 l
+27.458 -2.865 l
+27.458 -2.642 27.422 -2.48 27.356 -2.381 c
+27.296 -2.285 27.186 -2.234 27.032 -2.234 c
+26.834 -2.234 26.693 -2.352 26.606 -2.587 c
+26.606 -5.409 l
+25.562 -5.409 l
+25.562 -2.881 l
+25.562 -2.645 25.533 -2.48 25.474 -2.381 c
+25.415 -2.285 25.305 -2.234 25.151 -2.234 c
+24.974 -2.234 24.831 -2.329 24.725 -2.513 c
+24.725 -5.409 l
+23.68 -5.409 l
+23.68 -1.425 l
+h
+30.869 -5.483 m
+30.339 -5.483 29.92 -5.328 29.619 -5.012 c
+29.325 -4.689 29.178 -4.23 29.178 -3.63 c
+29.178 -3.322 l
+29.178 -2.697 29.314 -2.212 29.59 -1.866 c
+29.862 -1.525 30.255 -1.352 30.766 -1.352 c
+31.266 -1.352 31.636 -1.514 31.883 -1.837 c
+32.137 -2.16 32.268 -2.638 32.28 -3.262 c
+32.28 -3.763 l
+30.207 -3.763 l
+30.225 -4.056 30.288 -4.274 30.398 -4.409 c
+30.516 -4.549 30.695 -4.615 30.942 -4.615 c
+31.283 -4.615 31.574 -4.498 31.809 -4.262 c
+32.22 -4.895 l
+32.092 -5.071 31.905 -5.215 31.662 -5.321 c
+31.416 -5.427 31.152 -5.483 30.869 -5.483 c
+30.221 -3.042 m
+31.25 -3.042 l
+31.25 -2.94 l
+31.25 -2.705 31.21 -2.528 31.133 -2.41 c
+31.063 -2.285 30.934 -2.219 30.751 -2.219 c
+30.574 -2.219 30.442 -2.289 30.354 -2.425 c
+30.273 -2.553 30.229 -2.759 30.221 -3.042 c
+32.617 0.823 m
+34.044 0.823 l
+34.044 -6.659 l
+32.617 -6.659 l
+32.617 -5.835 l
+33 -5.835 l
+33 0 l
+32.617 0 l
+h
+f
+Q
+q 1 0 0 1 628.3846 136.6708 cm
+0 0 m
+0 -0.618 -0.114 -1.085 -0.338 -1.397 c
+-0.555 -1.713 -0.878 -1.867 -1.308 -1.867 c
+-1.731 -1.867 -2.043 -1.687 -2.248 -1.324 c
+-2.278 -1.794 l
+-2.88 -1.794 l
+-2.88 3.85 l
+-2.234 3.85 l
+-2.234 1.749 l
+-2.021 2.09 -1.712 2.263 -1.308 2.263 c
+-0.878 2.263 -0.555 2.105 -0.338 1.793 c
+-0.114 1.488 0 1.022 0 0.397 c
+h
+-0.646 0.382 m
+-0.646 0.852 -0.716 1.183 -0.852 1.381 c
+-0.981 1.576 -1.19 1.675 -1.484 1.675 c
+-1.818 1.675 -2.069 1.492 -2.234 1.132 c
+-2.234 -0.75 l
+-2.069 -1.114 -1.815 -1.294 -1.469 -1.294 c
+-1.176 -1.294 -0.966 -1.191 -0.837 -0.985 c
+-0.712 -0.779 -0.646 -0.464 -0.646 -0.03 c
+h
+2.485 1.573 m
+2.396 1.591 2.297 1.602 2.19 1.602 c
+1.856 1.602 1.621 1.418 1.484 1.058 c
+1.484 -1.794 l
+0.838 -1.794 l
+0.838 2.19 l
+1.47 2.19 l
+1.484 1.778 l
+1.661 2.102 1.904 2.263 2.22 2.263 c
+2.326 2.263 2.415 2.241 2.485 2.204 c
+h
+5.027 -1.794 m
+4.987 -1.706 4.962 -1.559 4.954 -1.353 c
+4.719 -1.698 4.424 -1.867 4.072 -1.867 c
+3.708 -1.867 3.425 -1.771 3.219 -1.573 c
+3.021 -1.367 2.926 -1.081 2.926 -0.706 c
+2.926 -0.306 3.061 0.014 3.337 0.249 c
+3.609 0.492 3.984 0.617 4.454 0.617 c
+4.939 0.617 l
+4.939 1.043 l
+4.939 1.278 4.884 1.444 4.777 1.543 c
+4.667 1.65 4.505 1.705 4.293 1.705 c
+4.094 1.705 3.932 1.646 3.807 1.529 c
+3.69 1.411 3.631 1.263 3.631 1.087 c
+2.984 1.087 l
+2.984 1.282 3.043 1.473 3.161 1.66 c
+3.286 1.845 3.447 1.992 3.645 2.102 c
+3.851 2.208 4.079 2.263 4.337 2.263 c
+4.738 2.263 5.042 2.161 5.247 1.955 c
+5.461 1.749 5.575 1.455 5.586 1.072 c
+5.586 -0.941 l
+5.586 -1.246 5.623 -1.511 5.704 -1.735 c
+5.704 -1.794 l
+h
+4.16 -1.279 m
+4.326 -1.279 4.476 -1.235 4.616 -1.147 c
+4.763 -1.058 4.869 -0.948 4.939 -0.809 c
+4.939 0.132 l
+4.572 0.132 l
+4.256 0.132 4.013 0.062 3.836 -0.073 c
+3.66 -0.202 3.572 -0.389 3.572 -0.632 c
+3.572 -0.86 3.616 -1.025 3.705 -1.132 c
+3.793 -1.231 3.944 -1.279 4.16 -1.279 c
+7.203 2.19 m
+7.217 1.749 l
+7.471 2.09 7.795 2.263 8.187 2.263 c
+8.893 2.263 9.249 1.793 9.261 0.852 c
+9.261 -1.794 l
+8.614 -1.794 l
+8.614 0.823 l
+8.614 1.135 8.559 1.356 8.453 1.484 c
+8.343 1.61 8.187 1.675 7.982 1.675 c
+7.824 1.675 7.676 1.62 7.541 1.514 c
+7.412 1.404 7.31 1.267 7.232 1.103 c
+7.232 -1.794 l
+6.585 -1.794 l
+6.585 2.19 l
+h
+11.612 -1.309 m
+11.826 -1.309 11.998 -1.246 12.127 -1.118 c
+12.263 -0.981 12.337 -0.79 12.347 -0.544 c
+12.965 -0.544 l
+12.943 -0.927 12.807 -1.246 12.553 -1.5 c
+12.296 -1.746 11.984 -1.867 11.612 -1.867 c
+11.121 -1.867 10.745 -1.717 10.481 -1.411 c
+10.224 -1.099 10.099 -0.632 10.099 -0.015 c
+10.099 0.426 l
+10.099 1.022 10.224 1.477 10.481 1.793 c
+10.745 2.105 11.121 2.263 11.612 2.263 c
+12.013 2.263 12.333 2.131 12.568 1.866 c
+12.811 1.61 12.943 1.263 12.965 0.823 c
+12.347 0.823 l
+12.326 1.117 12.252 1.338 12.127 1.484 c
+12.01 1.631 11.836 1.705 11.612 1.705 c
+11.319 1.705 11.102 1.606 10.966 1.411 c
+10.826 1.223 10.753 0.914 10.745 0.484 c
+10.745 -0.03 l
+10.745 -0.5 10.812 -0.834 10.951 -1.029 c
+11.098 -1.216 11.319 -1.309 11.612 -1.309 c
+14.361 1.778 m
+14.615 2.102 14.934 2.263 15.317 2.263 c
+16.023 2.263 16.378 1.793 16.39 0.852 c
+16.39 -1.794 l
+15.743 -1.794 l
+15.743 0.823 l
+15.743 1.135 15.688 1.356 15.582 1.484 c
+15.471 1.61 15.317 1.675 15.111 1.675 c
+14.953 1.675 14.806 1.62 14.67 1.514 c
+14.541 1.404 14.438 1.267 14.361 1.103 c
+14.361 -1.794 l
+13.714 -1.794 l
+13.714 3.85 l
+14.361 3.85 l
+h
+19.771 -1.794 -0.647 3.984 re
+19.815 3.233 m
+19.815 3.123 19.786 3.031 19.726 2.954 c
+19.668 2.884 19.572 2.851 19.447 2.851 c
+19.329 2.851 19.234 2.884 19.168 2.954 c
+19.109 3.031 19.08 3.123 19.08 3.233 c
+19.08 3.351 19.109 3.443 19.168 3.513 c
+19.234 3.59 19.329 3.63 19.447 3.63 c
+19.572 3.63 19.668 3.59 19.726 3.513 c
+19.786 3.432 19.815 3.34 19.815 3.233 c
+21.402 2.19 m
+21.417 1.749 l
+21.671 2.09 21.994 2.263 22.387 2.263 c
+23.092 2.263 23.449 1.793 23.46 0.852 c
+23.46 -1.794 l
+22.813 -1.794 l
+22.813 0.823 l
+22.813 1.135 22.758 1.356 22.651 1.484 c
+22.541 1.61 22.387 1.675 22.181 1.675 c
+22.023 1.675 21.876 1.62 21.741 1.514 c
+21.612 1.404 21.509 1.267 21.432 1.103 c
+21.432 -1.794 l
+20.785 -1.794 l
+20.785 2.19 l
+h
+25.282 3.145 m
+25.282 2.19 l
+25.885 2.19 l
+25.885 1.66 l
+25.282 1.66 l
+25.282 -0.809 l
+25.282 -0.967 25.305 -1.085 25.356 -1.162 c
+25.415 -1.243 25.503 -1.279 25.621 -1.279 c
+25.709 -1.279 25.797 -1.264 25.885 -1.235 c
+25.885 -1.794 l
+25.739 -1.841 25.584 -1.867 25.43 -1.867 c
+25.172 -1.867 24.978 -1.775 24.842 -1.588 c
+24.702 -1.405 24.636 -1.143 24.636 -0.809 c
+24.636 1.66 l
+24.033 1.66 l
+24.033 2.19 l
+24.636 2.19 l
+24.636 3.145 l
+h
+26.444 0.382 m
+26.444 0.959 26.58 1.415 26.855 1.749 c
+27.138 2.09 27.51 2.263 27.973 2.263 c
+28.432 2.263 28.8 2.094 29.076 1.764 c
+29.358 1.44 29.505 0.992 29.516 0.426 c
+29.516 0 l
+29.516 -0.57 29.373 -1.025 29.09 -1.367 c
+28.814 -1.702 28.447 -1.867 27.987 -1.867 c
+27.524 -1.867 27.154 -1.706 26.87 -1.382 c
+26.595 -1.052 26.452 -0.611 26.444 -0.059 c
+h
+27.091 0 m
+27.091 -0.405 27.168 -0.721 27.326 -0.956 c
+27.491 -1.191 27.711 -1.309 27.987 -1.309 c
+28.553 -1.309 28.847 -0.897 28.869 -0.073 c
+28.869 0.382 l
+28.869 0.783 28.785 1.103 28.619 1.338 c
+28.461 1.579 28.245 1.705 27.973 1.705 c
+27.708 1.705 27.491 1.579 27.326 1.338 c
+27.168 1.103 27.091 0.783 27.091 0.382 c
+h
+33.161 -0.706 m
+33.882 2.19 l
+34.572 2.19 l
+33.279 -2.352 l
+33.18 -2.694 33.037 -2.955 32.853 -3.131 c
+32.677 -3.308 32.474 -3.396 32.25 -3.396 c
+32.162 -3.396 32.048 -3.373 31.912 -3.337 c
+31.912 -2.793 l
+32.059 -2.808 l
+32.243 -2.808 32.39 -2.764 32.5 -2.675 c
+32.606 -2.587 32.695 -2.429 32.764 -2.205 c
+32.882 -1.764 l
+31.721 2.19 l
+32.426 2.19 l
+h
+35.013 0.382 m
+35.013 0.959 35.15 1.415 35.425 1.749 c
+35.708 2.09 36.079 2.263 36.542 2.263 c
+37.001 2.263 37.369 2.094 37.645 1.764 c
+37.928 1.44 38.075 0.992 38.086 0.426 c
+38.086 0 l
+38.086 -0.57 37.942 -1.025 37.66 -1.367 c
+37.384 -1.702 37.016 -1.867 36.557 -1.867 c
+36.094 -1.867 35.723 -1.706 35.44 -1.382 c
+35.164 -1.052 35.021 -0.611 35.013 -0.059 c
+h
+35.661 0 m
+35.661 -0.405 35.738 -0.721 35.896 -0.956 c
+36.06 -1.191 36.282 -1.309 36.557 -1.309 c
+37.123 -1.309 37.417 -0.897 37.439 -0.073 c
+37.439 0.382 l
+37.439 0.783 37.354 1.103 37.189 1.338 c
+37.031 1.579 36.814 1.705 36.542 1.705 c
+36.278 1.705 36.06 1.579 35.896 1.338 c
+35.738 1.103 35.661 0.783 35.661 0.382 c
+h
+40.952 -1.441 m
+40.735 -1.727 40.423 -1.867 40.012 -1.867 c
+39.648 -1.867 39.372 -1.746 39.188 -1.5 c
+39.012 -1.246 38.917 -0.882 38.909 -0.412 c
+38.909 2.19 l
+39.555 2.19 l
+39.555 -0.353 l
+39.555 -0.981 39.74 -1.294 40.114 -1.294 c
+40.515 -1.294 40.791 -1.118 40.937 -0.765 c
+40.937 2.19 l
+41.583 2.19 l
+41.583 -1.794 l
+40.966 -1.794 l
+h
+44.215 1.573 m
+44.127 1.591 44.028 1.602 43.921 1.602 c
+43.586 1.602 43.351 1.418 43.216 1.058 c
+43.216 -1.794 l
+42.568 -1.794 l
+42.568 2.19 l
+43.201 2.19 l
+43.216 1.778 l
+43.392 2.102 43.634 2.263 43.95 2.263 c
+44.057 2.263 44.145 2.241 44.215 2.204 c
+h
+47.964 -1.309 m
+48.176 -1.309 48.349 -1.246 48.478 -1.118 c
+48.614 -0.981 48.687 -0.79 48.698 -0.544 c
+49.315 -0.544 l
+49.294 -0.927 49.157 -1.246 48.904 -1.5 c
+48.647 -1.746 48.334 -1.867 47.964 -1.867 c
+47.471 -1.867 47.096 -1.717 46.832 -1.411 c
+46.574 -1.099 46.449 -0.632 46.449 -0.015 c
+46.449 0.426 l
+46.449 1.022 46.574 1.477 46.832 1.793 c
+47.096 2.105 47.471 2.263 47.964 2.263 c
+48.363 2.263 48.683 2.131 48.918 1.866 c
+49.161 1.61 49.294 1.263 49.315 0.823 c
+48.698 0.823 l
+48.677 1.117 48.602 1.338 48.478 1.484 c
+48.361 1.631 48.188 1.705 47.964 1.705 c
+47.669 1.705 47.453 1.606 47.316 1.411 c
+47.177 1.223 47.104 0.914 47.096 0.484 c
+47.096 -0.03 l
+47.096 -0.5 47.162 -0.834 47.302 -1.029 c
+47.449 -1.216 47.669 -1.309 47.964 -1.309 c
+52.093 -1.441 m
+51.877 -1.727 51.565 -1.867 51.153 -1.867 c
+50.79 -1.867 50.514 -1.746 50.33 -1.5 c
+50.154 -1.246 50.058 -0.882 50.05 -0.412 c
+50.05 2.19 l
+50.697 2.19 l
+50.697 -0.353 l
+50.697 -0.981 50.881 -1.294 51.256 -1.294 c
+51.656 -1.294 51.932 -1.118 52.079 -0.765 c
+52.079 2.19 l
+52.726 2.19 l
+52.726 -1.794 l
+52.109 -1.794 l
+h
+55.357 1.573 m
+55.268 1.591 55.17 1.602 55.063 1.602 c
+54.729 1.602 54.493 1.418 54.358 1.058 c
+54.358 -1.794 l
+53.711 -1.794 l
+53.711 2.19 l
+54.343 2.19 l
+54.358 1.778 l
+54.534 2.102 54.776 2.263 55.093 2.263 c
+55.199 2.263 55.287 2.241 55.357 2.204 c
+h
+57.65 1.573 m
+57.562 1.591 57.462 1.602 57.356 1.602 c
+57.022 1.602 56.787 1.418 56.65 1.058 c
+56.65 -1.794 l
+56.004 -1.794 l
+56.004 2.19 l
+56.635 2.19 l
+56.65 1.778 l
+56.827 2.102 57.069 2.263 57.385 2.263 c
+57.492 2.263 57.58 2.241 57.65 2.204 c
+h
+59.65 -1.867 m
+59.149 -1.867 58.767 -1.72 58.503 -1.426 c
+58.238 -1.132 58.106 -0.699 58.106 -0.118 c
+58.106 0.353 l
+58.106 0.947 58.231 1.415 58.488 1.749 c
+58.752 2.09 59.112 2.263 59.575 2.263 c
+60.035 2.263 60.377 2.109 60.604 1.808 c
+60.84 1.514 60.961 1.051 60.972 0.426 c
+60.972 0 l
+58.752 0 l
+58.752 -0.088 l
+58.752 -0.522 58.83 -0.834 58.987 -1.029 c
+59.153 -1.216 59.384 -1.309 59.679 -1.309 c
+59.873 -1.309 60.046 -1.276 60.193 -1.206 c
+60.34 -1.129 60.476 -1.011 60.604 -0.853 c
+60.943 -1.264 l
+60.656 -1.669 60.226 -1.867 59.65 -1.867 c
+59.575 1.705 m
+59.3 1.705 59.098 1.61 58.973 1.425 c
+58.844 1.238 58.771 0.947 58.752 0.558 c
+60.325 0.558 l
+60.325 0.646 l
+60.303 1.028 60.237 1.297 60.12 1.455 c
+60.002 1.62 59.818 1.705 59.575 1.705 c
+62.368 2.19 m
+62.383 1.749 l
+62.636 2.09 62.96 2.263 63.353 2.263 c
+64.059 2.263 64.415 1.793 64.427 0.852 c
+64.427 -1.794 l
+63.779 -1.794 l
+63.779 0.823 l
+63.779 1.135 63.725 1.356 63.617 1.484 c
+63.507 1.61 63.353 1.675 63.147 1.675 c
+62.989 1.675 62.842 1.62 62.707 1.514 c
+62.578 1.404 62.475 1.267 62.397 1.103 c
+62.397 -1.794 l
+61.751 -1.794 l
+61.751 2.19 l
+h
+66.249 3.145 m
+66.249 2.19 l
+66.852 2.19 l
+66.852 1.66 l
+66.249 1.66 l
+66.249 -0.809 l
+66.249 -0.967 66.271 -1.085 66.322 -1.162 c
+66.381 -1.243 66.47 -1.279 66.587 -1.279 c
+66.675 -1.279 66.763 -1.264 66.852 -1.235 c
+66.852 -1.794 l
+66.705 -1.841 66.551 -1.867 66.396 -1.867 c
+66.139 -1.867 65.944 -1.775 65.808 -1.588 c
+65.668 -1.405 65.603 -1.143 65.603 -0.809 c
+65.603 1.66 l
+64.999 1.66 l
+64.999 2.19 l
+65.603 2.19 l
+65.603 3.145 l
+h
+72.217 0 m
+72.217 -0.618 72.103 -1.085 71.879 -1.397 c
+71.662 -1.713 71.338 -1.867 70.908 -1.867 c
+70.486 -1.867 70.174 -1.687 69.968 -1.324 c
+69.938 -1.794 l
+69.335 -1.794 l
+69.335 3.85 l
+69.983 3.85 l
+69.983 1.749 l
+70.196 2.09 70.505 2.263 70.908 2.263 c
+71.338 2.263 71.662 2.105 71.879 1.793 c
+72.103 1.488 72.217 1.022 72.217 0.397 c
+h
+71.57 0.382 m
+71.57 0.852 71.5 1.183 71.365 1.381 c
+71.236 1.576 71.026 1.675 70.732 1.675 c
+70.397 1.675 70.148 1.492 69.983 1.132 c
+69.983 -0.75 l
+70.148 -1.114 70.401 -1.294 70.746 -1.294 c
+71.041 -1.294 71.251 -1.191 71.379 -0.985 c
+71.504 -0.779 71.57 -0.464 71.57 -0.03 c
+h
+74.701 1.573 m
+74.613 1.591 74.513 1.602 74.407 1.602 c
+74.073 1.602 73.838 1.418 73.701 1.058 c
+73.701 -1.794 l
+73.055 -1.794 l
+73.055 2.19 l
+73.686 2.19 l
+73.701 1.778 l
+73.878 2.102 74.12 2.263 74.436 2.263 c
+74.543 2.263 74.631 2.241 74.701 2.204 c
+h
+77.244 -1.794 m
+77.204 -1.706 77.177 -1.559 77.171 -1.353 c
+76.936 -1.698 76.641 -1.867 76.288 -1.867 c
+75.924 -1.867 75.642 -1.771 75.436 -1.573 c
+75.238 -1.367 75.142 -1.081 75.142 -0.706 c
+75.142 -0.306 75.278 0.014 75.554 0.249 c
+75.826 0.492 76.2 0.617 76.67 0.617 c
+77.156 0.617 l
+77.156 1.043 l
+77.156 1.278 77.1 1.444 76.994 1.543 c
+76.884 1.65 76.722 1.705 76.508 1.705 c
+76.311 1.705 76.149 1.646 76.024 1.529 c
+75.906 1.411 75.847 1.263 75.847 1.087 c
+75.201 1.087 l
+75.201 1.282 75.259 1.473 75.377 1.66 c
+75.502 1.845 75.664 1.992 75.862 2.102 c
+76.068 2.208 76.296 2.263 76.553 2.263 c
+76.953 2.263 77.258 2.161 77.464 1.955 c
+77.678 1.749 77.792 1.455 77.802 1.072 c
+77.802 -0.941 l
+77.802 -1.246 77.84 -1.511 77.92 -1.735 c
+77.92 -1.794 l
+h
+76.377 -1.279 m
+76.542 -1.279 76.693 -1.235 76.832 -1.147 c
+76.979 -1.058 77.086 -0.948 77.156 -0.809 c
+77.156 0.132 l
+76.788 0.132 l
+76.472 0.132 76.23 0.062 76.053 -0.073 c
+75.877 -0.202 75.789 -0.389 75.789 -0.632 c
+75.789 -0.86 75.833 -1.025 75.921 -1.132 c
+76.009 -1.231 76.159 -1.279 76.377 -1.279 c
+79.419 2.19 m
+79.434 1.749 l
+79.687 2.09 80.011 2.263 80.404 2.263 c
+81.11 2.263 81.466 1.793 81.477 0.852 c
+81.477 -1.794 l
+80.83 -1.794 l
+80.83 0.823 l
+80.83 1.135 80.776 1.356 80.668 1.484 c
+80.558 1.61 80.404 1.675 80.198 1.675 c
+80.04 1.675 79.893 1.62 79.758 1.514 c
+79.629 1.404 79.526 1.267 79.448 1.103 c
+79.448 -1.794 l
+78.802 -1.794 l
+78.802 2.19 l
+h
+83.829 -1.309 m
+84.042 -1.309 84.215 -1.246 84.344 -1.118 c
+84.479 -0.981 84.553 -0.79 84.564 -0.544 c
+85.181 -0.544 l
+85.16 -0.927 85.023 -1.246 84.77 -1.5 c
+84.512 -1.746 84.2 -1.867 83.829 -1.867 c
+83.336 -1.867 82.962 -1.717 82.698 -1.411 c
+82.44 -1.099 82.315 -0.632 82.315 -0.015 c
+82.315 0.426 l
+82.315 1.022 82.44 1.477 82.698 1.793 c
+82.962 2.105 83.336 2.263 83.829 2.263 c
+84.23 2.263 84.549 2.131 84.784 1.866 c
+85.027 1.61 85.16 1.263 85.181 0.823 c
+84.564 0.823 l
+84.542 1.117 84.468 1.338 84.344 1.484 c
+84.226 1.631 84.053 1.705 83.829 1.705 c
+83.535 1.705 83.318 1.606 83.182 1.411 c
+83.043 1.223 82.969 0.914 82.962 0.484 c
+82.962 -0.03 l
+82.962 -0.5 83.028 -0.834 83.168 -1.029 c
+83.315 -1.216 83.535 -1.309 83.829 -1.309 c
+86.578 1.778 m
+86.831 2.102 87.151 2.263 87.533 2.263 c
+88.239 2.263 88.595 1.793 88.606 0.852 c
+88.606 -1.794 l
+87.959 -1.794 l
+87.959 0.823 l
+87.959 1.135 87.905 1.356 87.797 1.484 c
+87.687 1.61 87.533 1.675 87.327 1.675 c
+87.169 1.675 87.022 1.62 86.887 1.514 c
+86.758 1.404 86.655 1.267 86.578 1.103 c
+86.578 -1.794 l
+85.931 -1.794 l
+85.931 3.85 l
+86.578 3.85 l
+h
+91.267 0.382 m
+91.267 1.018 91.355 1.62 91.531 2.19 c
+91.708 2.755 91.951 3.252 92.266 3.675 c
+92.462 3.939 92.649 4.13 92.825 4.247 c
+92.957 3.792 l
+92.663 3.516 92.421 3.094 92.237 2.528 c
+92.049 1.959 91.947 1.326 91.928 0.632 c
+91.928 0.338 l
+91.928 -0.526 92.046 -1.29 92.281 -1.955 c
+92.464 -2.455 92.693 -2.837 92.957 -3.102 c
+92.825 -3.528 l
+92.597 -3.37 92.373 -3.12 92.148 -2.778 c
+91.56 -1.897 91.267 -0.846 91.267 0.382 c
+94.5 3.145 m
+94.5 2.19 l
+95.103 2.19 l
+95.103 1.66 l
+94.5 1.66 l
+94.5 -0.809 l
+94.5 -0.967 94.523 -1.085 94.574 -1.162 c
+94.633 -1.243 94.721 -1.279 94.839 -1.279 c
+94.927 -1.279 95.015 -1.264 95.103 -1.235 c
+95.103 -1.794 l
+94.956 -1.841 94.802 -1.867 94.648 -1.867 c
+94.39 -1.867 94.195 -1.775 94.06 -1.588 c
+93.92 -1.405 93.854 -1.143 93.854 -0.809 c
+93.854 1.66 l
+93.251 1.66 l
+93.251 2.19 l
+93.854 2.19 l
+93.854 3.145 l
+h
+96.514 1.778 m
+96.768 2.102 97.087 2.263 97.47 2.263 c
+98.176 2.263 98.532 1.793 98.542 0.852 c
+98.542 -1.794 l
+97.896 -1.794 l
+97.896 0.823 l
+97.896 1.135 97.841 1.356 97.734 1.484 c
+97.624 1.61 97.47 1.675 97.264 1.675 c
+97.106 1.675 96.959 1.62 96.823 1.514 c
+96.695 1.404 96.591 1.267 96.514 1.103 c
+96.514 -1.794 l
+95.868 -1.794 l
+95.868 3.85 l
+96.514 3.85 l
+h
+100.924 -1.867 m
+100.424 -1.867 100.042 -1.72 99.778 -1.426 c
+99.513 -1.132 99.381 -0.699 99.381 -0.118 c
+99.381 0.353 l
+99.381 0.947 99.506 1.415 99.763 1.749 c
+100.027 2.09 100.387 2.263 100.85 2.263 c
+101.31 2.263 101.652 2.109 101.879 1.808 c
+102.114 1.514 102.236 1.051 102.247 0.426 c
+102.247 0 l
+100.027 0 l
+100.027 -0.088 l
+100.027 -0.522 100.104 -0.834 100.262 -1.029 c
+100.428 -1.216 100.659 -1.309 100.954 -1.309 c
+101.148 -1.309 101.32 -1.276 101.468 -1.206 c
+101.615 -1.129 101.75 -1.011 101.879 -0.853 c
+102.218 -1.264 l
+101.931 -1.669 101.501 -1.867 100.924 -1.867 c
+100.85 1.705 m
+100.575 1.705 100.373 1.61 100.248 1.425 c
+100.119 1.238 100.046 0.947 100.027 0.558 c
+101.6 0.558 l
+101.6 0.646 l
+101.578 1.028 101.512 1.297 101.395 1.455 c
+101.277 1.62 101.093 1.705 100.85 1.705 c
+104.613 0.382 m
+104.613 0.959 104.75 1.415 105.025 1.749 c
+105.308 2.09 105.679 2.263 106.142 2.263 c
+106.602 2.263 106.969 2.094 107.244 1.764 c
+107.527 1.44 107.674 0.992 107.685 0.426 c
+107.685 0 l
+107.685 -0.57 107.543 -1.025 107.259 -1.367 c
+106.984 -1.702 106.616 -1.867 106.157 -1.867 c
+105.694 -1.867 105.322 -1.706 105.04 -1.382 c
+104.765 -1.052 104.621 -0.611 104.613 -0.059 c
+h
+105.26 0 m
+105.26 -0.405 105.337 -0.721 105.496 -0.956 c
+105.661 -1.191 105.881 -1.309 106.157 -1.309 c
+106.723 -1.309 107.017 -0.897 107.039 -0.073 c
+107.039 0.382 l
+107.039 0.783 106.955 1.103 106.789 1.338 c
+106.631 1.579 106.415 1.705 106.142 1.705 c
+105.877 1.705 105.661 1.579 105.496 1.338 c
+105.337 1.103 105.26 0.783 105.26 0.382 c
+h
+109.141 2.19 m
+109.156 1.749 l
+109.409 2.09 109.733 2.263 110.126 2.263 c
+110.831 2.263 111.188 1.793 111.198 0.852 c
+111.198 -1.794 l
+110.552 -1.794 l
+110.552 0.823 l
+110.552 1.135 110.496 1.356 110.39 1.484 c
+110.28 1.61 110.126 1.675 109.92 1.675 c
+109.762 1.675 109.615 1.62 109.479 1.514 c
+109.351 1.404 109.247 1.267 109.17 1.103 c
+109.17 -1.794 l
+108.524 -1.794 l
+108.524 2.19 l
+h
+113.594 -1.867 m
+113.095 -1.867 112.713 -1.72 112.448 -1.426 c
+112.183 -1.132 112.052 -0.699 112.052 -0.118 c
+112.052 0.353 l
+112.052 0.947 112.177 1.415 112.433 1.749 c
+112.698 2.09 113.058 2.263 113.521 2.263 c
+113.98 2.263 114.322 2.109 114.55 1.808 c
+114.785 1.514 114.907 1.051 114.918 0.426 c
+114.918 0 l
+112.698 0 l
+112.698 -0.088 l
+112.698 -0.522 112.775 -0.834 112.933 -1.029 c
+113.099 -1.216 113.33 -1.309 113.624 -1.309 c
+113.819 -1.309 113.991 -1.276 114.138 -1.206 c
+114.286 -1.129 114.421 -1.011 114.55 -0.853 c
+114.888 -1.264 l
+114.602 -1.669 114.172 -1.867 113.594 -1.867 c
+113.521 1.705 m
+113.245 1.705 113.043 1.61 112.919 1.425 c
+112.79 1.238 112.717 0.947 112.698 0.558 c
+114.271 0.558 l
+114.271 0.646 l
+114.249 1.028 114.182 1.297 114.065 1.455 c
+113.947 1.62 113.764 1.705 113.521 1.705 c
+118.504 -0.706 m
+119.225 2.19 l
+119.915 2.19 l
+118.622 -2.352 l
+118.523 -2.694 118.379 -2.955 118.195 -3.131 c
+118.019 -3.308 117.817 -3.396 117.592 -3.396 c
+117.505 -3.396 117.391 -3.373 117.255 -3.337 c
+117.255 -2.793 l
+117.401 -2.808 l
+117.586 -2.808 117.733 -2.764 117.843 -2.675 c
+117.949 -2.587 118.037 -2.429 118.107 -2.205 c
+118.225 -1.764 l
+117.064 2.19 l
+117.769 2.19 l
+h
+120.356 0.382 m
+120.356 0.959 120.492 1.415 120.767 1.749 c
+121.051 2.09 121.422 2.263 121.885 2.263 c
+122.344 2.263 122.712 2.094 122.988 1.764 c
+123.271 1.44 123.418 0.992 123.428 0.426 c
+123.428 0 l
+123.428 -0.57 123.285 -1.025 123.002 -1.367 c
+122.726 -1.702 122.359 -1.867 121.899 -1.867 c
+121.436 -1.867 121.066 -1.706 120.783 -1.382 c
+120.507 -1.052 120.364 -0.611 120.356 -0.059 c
+h
+121.003 0 m
+121.003 -0.405 121.08 -0.721 121.238 -0.956 c
+121.403 -1.191 121.624 -1.309 121.899 -1.309 c
+122.465 -1.309 122.76 -0.897 122.782 -0.073 c
+122.782 0.382 l
+122.782 0.783 122.697 1.103 122.531 1.338 c
+122.373 1.579 122.157 1.705 121.885 1.705 c
+121.621 1.705 121.403 1.579 121.238 1.338 c
+121.08 1.103 121.003 0.783 121.003 0.382 c
+h
+126.294 -1.441 m
+126.078 -1.727 125.766 -1.867 125.354 -1.867 c
+124.99 -1.867 124.715 -1.746 124.53 -1.5 c
+124.354 -1.246 124.259 -0.882 124.251 -0.412 c
+124.251 2.19 l
+124.898 2.19 l
+124.898 -0.353 l
+124.898 -0.981 125.082 -1.294 125.457 -1.294 c
+125.858 -1.294 126.132 -1.118 126.28 -0.765 c
+126.28 2.19 l
+126.926 2.19 l
+126.926 -1.794 l
+126.309 -1.794 l
+h
+131.674 -1.794 m
+131.634 -1.706 131.609 -1.559 131.601 -1.353 c
+131.366 -1.698 131.071 -1.867 130.719 -1.867 c
+130.355 -1.867 130.072 -1.771 129.866 -1.573 c
+129.668 -1.367 129.573 -1.081 129.573 -0.706 c
+129.573 -0.306 129.708 0.014 129.984 0.249 c
+130.256 0.492 130.631 0.617 131.102 0.617 c
+131.586 0.617 l
+131.586 1.043 l
+131.586 1.278 131.531 1.444 131.424 1.543 c
+131.314 1.65 131.152 1.705 130.94 1.705 c
+130.741 1.705 130.579 1.646 130.454 1.529 c
+130.337 1.411 130.278 1.263 130.278 1.087 c
+129.631 1.087 l
+129.631 1.282 129.69 1.473 129.808 1.66 c
+129.933 1.845 130.094 1.992 130.292 2.102 c
+130.498 2.208 130.726 2.263 130.984 2.263 c
+131.384 2.263 131.689 2.161 131.895 1.955 c
+132.108 1.749 132.222 1.455 132.233 1.072 c
+132.233 -0.941 l
+132.233 -1.246 132.27 -1.511 132.351 -1.735 c
+132.351 -1.794 l
+h
+130.807 -1.279 m
+130.973 -1.279 131.123 -1.235 131.263 -1.147 c
+131.41 -1.058 131.516 -0.948 131.586 -0.809 c
+131.586 0.132 l
+131.219 0.132 l
+130.903 0.132 130.66 0.062 130.483 -0.073 c
+130.307 -0.202 130.219 -0.389 130.219 -0.632 c
+130.219 -0.86 130.263 -1.025 130.352 -1.132 c
+130.44 -1.231 130.591 -1.279 130.807 -1.279 c
+134.879 1.573 m
+134.791 1.591 134.691 1.602 134.585 1.602 c
+134.25 1.602 134.015 1.418 133.88 1.058 c
+133.88 -1.794 l
+133.232 -1.794 l
+133.232 2.19 l
+133.864 2.19 l
+133.88 1.778 l
+134.055 2.102 134.298 2.263 134.614 2.263 c
+134.721 2.263 134.809 2.241 134.879 2.204 c
+h
+136.878 -1.867 m
+136.378 -1.867 135.996 -1.72 135.732 -1.426 c
+135.466 -1.132 135.335 -0.699 135.335 -0.118 c
+135.335 0.353 l
+135.335 0.947 135.46 1.415 135.717 1.749 c
+135.981 2.09 136.341 2.263 136.804 2.263 c
+137.263 2.263 137.606 2.109 137.833 1.808 c
+138.069 1.514 138.19 1.051 138.201 0.426 c
+138.201 0 l
+135.981 0 l
+135.981 -0.088 l
+135.981 -0.522 136.058 -0.834 136.216 -1.029 c
+136.382 -1.216 136.613 -1.309 136.908 -1.309 c
+137.102 -1.309 137.275 -1.276 137.421 -1.206 c
+137.569 -1.129 137.705 -1.011 137.833 -0.853 c
+138.171 -1.264 l
+137.885 -1.669 137.455 -1.867 136.878 -1.867 c
+136.804 1.705 m
+136.529 1.705 136.326 1.61 136.202 1.425 c
+136.073 1.238 136 0.947 135.981 0.558 c
+137.554 0.558 l
+137.554 0.646 l
+137.532 1.028 137.466 1.297 137.348 1.455 c
+137.23 1.62 137.047 1.705 136.804 1.705 c
+f
+Q
+q 1 0 0 1 727.8236 127.6454 cm
+0 0 m
+0 0.577 0.136 1.033 0.412 1.367 c
+0.695 1.708 1.066 1.881 1.529 1.881 c
+1.989 1.881 2.356 1.712 2.631 1.382 c
+2.914 1.058 3.061 0.61 3.072 0.044 c
+3.072 -0.382 l
+3.072 -0.952 2.93 -1.407 2.646 -1.749 c
+2.371 -2.084 2.003 -2.249 1.544 -2.249 c
+1.081 -2.249 0.709 -2.087 0.427 -1.764 c
+0.151 -1.434 0.008 -0.992 0 -0.441 c
+h
+0.647 -0.382 m
+0.647 -0.786 0.724 -1.103 0.883 -1.338 c
+1.048 -1.573 1.268 -1.691 1.544 -1.691 c
+2.11 -1.691 2.404 -1.278 2.426 -0.455 c
+2.426 0 l
+2.426 0.401 2.342 0.721 2.176 0.956 c
+2.018 1.198 1.802 1.323 1.529 1.323 c
+1.264 1.323 1.048 1.198 0.883 0.956 c
+0.724 0.721 0.647 0.401 0.647 0 c
+h
+4.528 1.808 m
+4.543 1.367 l
+4.796 1.708 5.12 1.881 5.513 1.881 c
+6.218 1.881 6.575 1.411 6.585 0.47 c
+6.585 -2.176 l
+5.939 -2.176 l
+5.939 0.441 l
+5.939 0.754 5.883 0.974 5.777 1.103 c
+5.667 1.228 5.513 1.294 5.307 1.294 c
+5.149 1.294 5.002 1.238 4.866 1.132 c
+4.738 1.022 4.634 0.885 4.557 0.721 c
+4.557 -2.176 l
+3.911 -2.176 l
+3.911 1.808 l
+h
+10.658 -1.691 m
+10.87 -1.691 11.043 -1.628 11.171 -1.5 c
+11.308 -1.363 11.381 -1.172 11.392 -0.926 c
+12.01 -0.926 l
+11.988 -1.309 11.852 -1.628 11.598 -1.881 c
+11.341 -2.128 11.028 -2.249 10.658 -2.249 c
+10.165 -2.249 9.79 -2.099 9.525 -1.793 c
+9.268 -1.481 9.143 -1.014 9.143 -0.397 c
+9.143 0.044 l
+9.143 0.64 9.268 1.095 9.525 1.411 c
+9.79 1.723 10.165 1.881 10.658 1.881 c
+11.057 1.881 11.377 1.749 11.612 1.484 c
+11.855 1.228 11.988 0.881 12.01 0.441 c
+11.392 0.441 l
+11.37 0.735 11.296 0.956 11.171 1.103 c
+11.054 1.249 10.882 1.323 10.658 1.323 c
+10.363 1.323 10.147 1.224 10.01 1.029 c
+9.871 0.841 9.798 0.532 9.79 0.103 c
+9.79 -0.412 l
+9.79 -0.882 9.856 -1.216 9.995 -1.411 c
+10.143 -1.598 10.363 -1.691 10.658 -1.691 c
+14.787 -1.823 m
+14.571 -2.109 14.259 -2.249 13.847 -2.249 c
+13.484 -2.249 13.208 -2.128 13.024 -1.881 c
+12.848 -1.628 12.752 -1.264 12.744 -0.794 c
+12.744 1.808 l
+13.391 1.808 l
+13.391 -0.735 l
+13.391 -1.363 13.575 -1.675 13.95 -1.675 c
+14.35 -1.675 14.626 -1.5 14.773 -1.147 c
+14.773 1.808 l
+15.42 1.808 l
+15.42 -2.176 l
+14.803 -2.176 l
+h
+18.051 1.191 m
+17.963 1.209 17.864 1.22 17.756 1.22 c
+17.423 1.22 17.187 1.036 17.051 0.676 c
+17.051 -2.176 l
+16.405 -2.176 l
+16.405 1.808 l
+17.037 1.808 l
+17.051 1.396 l
+17.228 1.72 17.471 1.881 17.787 1.881 c
+17.893 1.881 17.981 1.859 18.051 1.822 c
+h
+20.344 1.191 m
+20.256 1.209 20.156 1.22 20.05 1.22 c
+19.715 1.22 19.48 1.036 19.344 0.676 c
+19.344 -2.176 l
+18.697 -2.176 l
+18.697 1.808 l
+19.329 1.808 l
+19.344 1.396 l
+19.52 1.72 19.763 1.881 20.079 1.881 c
+20.185 1.881 20.274 1.859 20.344 1.822 c
+h
+22.343 -2.249 m
+21.843 -2.249 21.461 -2.102 21.197 -1.808 c
+20.931 -1.514 20.8 -1.081 20.8 -0.5 c
+20.8 -0.029 l
+20.8 0.565 20.925 1.033 21.182 1.367 c
+21.446 1.708 21.806 1.881 22.269 1.881 c
+22.729 1.881 23.071 1.727 23.298 1.426 c
+23.534 1.132 23.655 0.669 23.666 0.044 c
+23.666 -0.382 l
+21.446 -0.382 l
+21.446 -0.47 l
+21.446 -0.904 21.523 -1.216 21.681 -1.411 c
+21.847 -1.598 22.078 -1.691 22.373 -1.691 c
+22.567 -1.691 22.74 -1.658 22.887 -1.588 c
+23.034 -1.511 23.17 -1.393 23.298 -1.235 c
+23.636 -1.646 l
+23.35 -2.051 22.92 -2.249 22.343 -2.249 c
+22.269 1.323 m
+21.994 1.323 21.792 1.228 21.667 1.043 c
+21.538 0.856 21.465 0.565 21.446 0.176 c
+23.019 0.176 l
+23.019 0.264 l
+22.997 0.646 22.931 0.915 22.813 1.073 c
+22.695 1.238 22.512 1.323 22.269 1.323 c
+25.062 1.808 m
+25.077 1.367 l
+25.33 1.708 25.654 1.881 26.047 1.881 c
+26.753 1.881 27.109 1.411 27.121 0.47 c
+27.121 -2.176 l
+26.473 -2.176 l
+26.473 0.441 l
+26.473 0.754 26.419 0.974 26.312 1.103 c
+26.201 1.228 26.047 1.294 25.841 1.294 c
+25.683 1.294 25.536 1.238 25.4 1.132 c
+25.272 1.022 25.169 0.885 25.091 0.721 c
+25.091 -2.176 l
+24.445 -2.176 l
+24.445 1.808 l
+h
+28.796 -2.176 -0.647 5.644 re
+30.662 2.763 m
+30.662 1.808 l
+31.266 1.808 l
+31.266 1.278 l
+30.662 1.278 l
+30.662 -1.191 l
+30.662 -1.349 30.685 -1.467 30.736 -1.544 c
+30.795 -1.625 30.884 -1.661 31.001 -1.661 c
+31.089 -1.661 31.177 -1.646 31.266 -1.617 c
+31.266 -2.176 l
+31.119 -2.223 30.964 -2.249 30.809 -2.249 c
+30.552 -2.249 30.358 -2.157 30.221 -1.97 c
+30.082 -1.786 30.016 -1.525 30.016 -1.191 c
+30.016 1.278 l
+29.413 1.278 l
+29.413 1.808 l
+30.016 1.808 l
+30.016 2.763 l
+h
+33.118 -1.087 m
+33.837 1.808 l
+34.529 1.808 l
+33.235 -2.734 l
+33.136 -3.076 32.993 -3.337 32.808 -3.513 c
+32.632 -3.69 32.43 -3.778 32.206 -3.778 c
+32.118 -3.778 32.004 -3.755 31.868 -3.719 c
+31.868 -3.175 l
+32.015 -3.19 l
+32.199 -3.19 32.345 -3.146 32.456 -3.057 c
+32.563 -2.969 32.65 -2.811 32.721 -2.587 c
+32.838 -2.146 l
+31.677 1.808 l
+32.382 1.808 l
+h
+36.557 -0.044 m
+36.557 -1.132 36.311 -2.099 35.823 -2.94 c
+35.557 -3.389 35.282 -3.711 34.999 -3.91 c
+34.882 -3.484 l
+35.183 -3.19 35.429 -2.738 35.616 -2.132 c
+35.811 -1.525 35.91 -0.86 35.91 -0.133 c
+35.91 0 l
+35.91 0.929 35.756 1.764 35.455 2.499 c
+35.285 2.899 35.094 3.219 34.882 3.454 c
+34.999 3.865 l
+35.271 3.678 35.536 3.381 35.792 2.969 c
+36.299 2.117 36.557 1.11 36.557 -0.044 c
+37.6 -1.823 m
+37.6 -1.706 37.633 -1.61 37.704 -1.529 c
+37.77 -1.452 37.872 -1.411 38.013 -1.411 c
+38.159 -1.411 38.266 -1.452 38.335 -1.529 c
+38.413 -1.61 38.453 -1.706 38.453 -1.823 c
+38.453 -1.933 38.413 -2.024 38.335 -2.102 c
+38.266 -2.18 38.159 -2.219 38.013 -2.219 c
+37.872 -2.219 37.77 -2.18 37.704 -2.102 c
+37.633 -2.024 37.6 -1.933 37.6 -1.823 c
+f
+Q
+0.113 0.082 0.09 0 k
+535.667 120.241 238.665 -12.898 re
+f
+0.906 0.785 0.617 0.969 k
+q 1 0 0 1 540.8555 113.4016 cm
+0 0 m
+0 -0.187 -0.029 -0.353 -0.088 -0.5 c
+-0.147 -0.646 -0.242 -0.775 -0.367 -0.881 c
+-0.496 -0.992 -0.658 -1.08 -0.852 -1.146 c
+-1.051 -1.205 -1.278 -1.242 -1.543 -1.249 c
+-1.543 -1.808 l
+-1.896 -1.808 l
+-1.896 -1.263 l
+-2.142 -1.257 -2.358 -1.22 -2.543 -1.161 c
+-2.719 -1.103 -2.873 -1.025 -2.998 -0.926 c
+-3.127 -0.819 -3.23 -0.691 -3.307 -0.544 c
+-3.388 -0.389 -3.447 -0.213 -3.484 -0.014 c
+-2.66 0.133 l
+-2.642 0.033 -2.616 -0.058 -2.587 -0.147 c
+-2.55 -0.228 -2.506 -0.301 -2.454 -0.367 c
+-2.396 -0.426 -2.322 -0.478 -2.234 -0.515 c
+-2.138 -0.555 -2.024 -0.58 -1.896 -0.588 c
+-1.896 0.661 l
+-1.907 0.661 -1.926 0.665 -1.955 0.676 c
+-1.977 0.684 -1.992 0.691 -1.999 0.691 c
+-2.167 0.728 -2.329 0.772 -2.484 0.823 c
+-2.642 0.882 -2.786 0.96 -2.91 1.058 c
+-3.039 1.154 -3.141 1.279 -3.218 1.426 c
+-3.289 1.573 -3.322 1.756 -3.322 1.985 c
+-3.322 2.18 -3.289 2.344 -3.218 2.484 c
+-3.152 2.631 -3.054 2.749 -2.925 2.837 c
+-2.8 2.932 -2.645 3.003 -2.469 3.043 c
+-2.293 3.09 -2.102 3.12 -1.896 3.131 c
+-1.896 3.558 l
+-1.543 3.558 l
+-1.543 3.131 l
+-1.319 3.12 -1.128 3.09 -0.97 3.043 c
+-0.804 2.992 -0.669 2.918 -0.558 2.822 c
+-0.44 2.735 -0.349 2.62 -0.279 2.484 c
+-0.201 2.344 -0.143 2.183 -0.103 1.999 c
+-0.941 1.881 l
+-1.01 2.253 -1.213 2.459 -1.543 2.5 c
+-1.543 1.367 l
+-1.514 1.367 l
+-1.506 1.367 -1.492 1.36 -1.469 1.353 c
+-1.294 1.312 -1.117 1.264 -0.941 1.206 c
+-0.756 1.147 -0.595 1.066 -0.455 0.971 c
+-0.319 0.871 -0.213 0.742 -0.132 0.588 c
+-0.044 0.43 0 0.235 0 0 c
+-1.896 2.514 m
+-2.013 2.502 -2.113 2.484 -2.19 2.455 c
+-2.26 2.425 -2.318 2.385 -2.366 2.338 c
+-2.406 2.297 -2.436 2.249 -2.454 2.19 c
+-2.476 2.132 -2.484 2.066 -2.484 1.999 c
+-2.484 1.912 -2.469 1.837 -2.439 1.779 c
+-2.41 1.72 -2.373 1.669 -2.322 1.632 c
+-2.263 1.592 -2.2 1.559 -2.131 1.529 c
+-2.065 1.5 -1.984 1.47 -1.896 1.441 c
+h
+-0.837 -0.014 m
+-0.837 0.092 -0.86 0.177 -0.897 0.235 c
+-0.926 0.302 -0.977 0.357 -1.043 0.397 c
+-1.102 0.434 -1.176 0.47 -1.263 0.5 c
+-1.352 0.53 -1.448 0.559 -1.543 0.588 c
+-1.543 -0.588 l
+-1.319 -0.58 -1.146 -0.529 -1.028 -0.44 c
+-0.904 -0.345 -0.837 -0.202 -0.837 -0.014 c
+6.402 -2.66 m
+6.185 -2.66 5.993 -2.635 5.829 -2.587 c
+5.659 -2.547 5.52 -2.484 5.403 -2.396 c
+5.285 -2.315 5.185 -2.219 5.108 -2.102 c
+5.039 -1.984 4.991 -1.855 4.961 -1.72 c
+5.858 -1.616 l
+5.895 -1.753 5.964 -1.859 6.064 -1.94 c
+6.159 -2.028 6.284 -2.072 6.431 -2.072 c
+6.519 -2.072 6.6 -2.057 6.681 -2.028 c
+6.758 -1.999 6.828 -1.944 6.887 -1.866 c
+6.945 -1.797 6.99 -1.705 7.019 -1.587 c
+7.056 -1.469 7.078 -1.323 7.078 -1.146 c
+7.078 -0.955 l
+7.078 -0.889 7.082 -0.831 7.092 -0.779 c
+7.092 -0.588 l
+7.078 -0.588 l
+6.978 -0.816 6.835 -0.977 6.652 -1.072 c
+6.464 -1.172 6.259 -1.22 6.034 -1.22 c
+5.829 -1.22 5.644 -1.183 5.49 -1.103 c
+5.343 -1.014 5.214 -0.897 5.108 -0.75 c
+5.009 -0.595 4.935 -0.411 4.888 -0.205 c
+4.836 0.008 4.815 0.243 4.815 0.5 c
+4.815 0.772 4.836 1.018 4.888 1.235 c
+4.946 1.448 5.027 1.632 5.137 1.779 c
+5.245 1.933 5.376 2.051 5.534 2.132 c
+5.689 2.22 5.876 2.264 6.093 2.264 c
+6.189 2.264 6.288 2.253 6.387 2.234 c
+6.483 2.213 6.571 2.18 6.652 2.132 c
+6.739 2.08 6.817 2.018 6.887 1.941 c
+6.964 1.86 7.026 1.768 7.078 1.661 c
+7.092 1.661 l
+7.092 1.808 l
+7.1 1.867 7.107 1.918 7.107 1.97 c
+7.115 2.029 7.122 2.076 7.122 2.117 c
+7.13 2.165 7.14 2.198 7.151 2.22 c
+8.004 2.22 l
+7.992 2.139 7.982 2.029 7.975 1.881 c
+7.975 1.411 l
+7.975 -1.161 l
+7.975 -1.415 7.938 -1.635 7.872 -1.822 c
+7.802 -2.007 7.699 -2.161 7.563 -2.278 c
+7.423 -2.403 7.258 -2.499 7.063 -2.557 c
+6.865 -2.624 6.644 -2.66 6.402 -2.66 c
+7.092 0.53 m
+7.092 0.742 7.067 0.919 7.019 1.058 c
+6.978 1.206 6.924 1.324 6.857 1.411 c
+6.799 1.5 6.729 1.559 6.652 1.588 c
+6.571 1.625 6.494 1.646 6.417 1.646 c
+6.317 1.646 6.226 1.621 6.137 1.573 c
+6.056 1.532 5.991 1.463 5.931 1.367 c
+5.88 1.279 5.835 1.162 5.799 1.014 c
+5.77 0.875 5.755 0.706 5.755 0.5 c
+5.755 0.125 5.814 -0.154 5.931 -0.338 c
+6.049 -0.515 6.211 -0.602 6.417 -0.602 c
+6.483 -0.602 6.556 -0.588 6.637 -0.558 c
+6.725 -0.521 6.799 -0.463 6.857 -0.382 c
+6.924 -0.294 6.978 -0.176 7.019 -0.029 c
+7.067 0.118 7.092 0.302 7.092 0.53 c
+11.036 -0.646 m
+12.168 -0.646 l
+12.168 -1.263 l
+8.86 -1.263 l
+8.86 -0.646 l
+10.124 -0.646 l
+10.124 1.602 l
+9.199 1.602 l
+9.199 2.22 l
+11.036 2.22 l
+h
+10.124 3.514 0.912 -0.676 re
+10.124 2.837 m
+13.6 1.602 m
+13.057 1.602 l
+13.057 2.22 l
+13.644 2.22 l
+13.924 3.117 l
+14.498 3.117 l
+14.498 2.22 l
+15.732 2.22 l
+15.732 1.602 l
+14.498 1.602 l
+14.498 -0.103 l
+14.498 -0.323 l
+14.504 -0.393 14.527 -0.455 14.556 -0.515 c
+14.593 -0.565 14.648 -0.61 14.718 -0.646 c
+14.795 -0.675 14.909 -0.691 15.056 -0.691 c
+15.192 -0.691 15.327 -0.687 15.468 -0.675 c
+15.603 -0.658 15.736 -0.632 15.865 -0.602 c
+15.865 -1.205 l
+15.784 -1.216 15.707 -1.23 15.629 -1.249 c
+15.549 -1.261 15.471 -1.267 15.394 -1.278 c
+15.313 -1.286 15.225 -1.294 15.129 -1.294 c
+15.042 -1.301 14.942 -1.308 14.836 -1.308 c
+14.648 -1.308 14.486 -1.294 14.35 -1.263 c
+14.222 -1.227 14.107 -1.183 14.012 -1.132 c
+13.924 -1.084 13.85 -1.025 13.792 -0.955 c
+13.733 -0.878 13.689 -0.801 13.66 -0.72 c
+13.63 -0.632 13.608 -0.544 13.6 -0.455 c
+13.59 -0.36 13.586 -0.264 13.586 -0.176 c
+h
+21.12 -1.263 m
+21.12 -0.97 l
+21.126 -0.833 21.134 -0.675 21.134 -0.5 c
+21.134 3.514 l
+22.045 3.514 l
+22.045 2.234 l
+22.045 2.072 l
+22.045 1.897 l
+22.045 1.845 22.038 1.801 22.03 1.764 c
+22.03 1.676 l
+22.045 1.676 l
+22.093 1.783 22.155 1.874 22.236 1.955 c
+22.313 2.032 22.398 2.095 22.487 2.147 c
+22.574 2.194 22.666 2.227 22.766 2.249 c
+22.861 2.267 22.961 2.278 23.059 2.278 c
+23.273 2.278 23.46 2.234 23.618 2.147 c
+23.773 2.058 23.901 1.929 24 1.764 c
+24.106 1.606 24.184 1.415 24.235 1.191 c
+24.283 0.974 24.309 0.736 24.309 0.47 c
+24.309 0.214 24.28 -0.025 24.22 -0.249 c
+24.162 -0.467 24.077 -0.658 23.971 -0.823 c
+23.861 -0.981 23.728 -1.103 23.574 -1.19 c
+23.416 -1.278 23.236 -1.323 23.03 -1.323 c
+22.931 -1.323 22.832 -1.311 22.736 -1.294 c
+22.648 -1.271 22.56 -1.242 22.471 -1.19 c
+22.383 -1.132 22.302 -1.066 22.236 -0.985 c
+22.167 -0.908 22.104 -0.808 22.045 -0.691 c
+22.03 -0.691 l
+22.03 -0.852 l
+22.03 -0.911 22.024 -0.97 22.016 -1.028 c
+22.016 -1.08 22.009 -1.128 22.001 -1.176 c
+22.001 -1.216 21.994 -1.246 21.986 -1.263 c
+h
+22.03 0.5 m
+22.03 0.265 22.049 0.067 22.09 -0.087 c
+22.137 -0.246 22.196 -0.367 22.265 -0.455 c
+22.332 -0.544 22.406 -0.61 22.487 -0.646 c
+22.564 -0.687 22.641 -0.706 22.722 -0.706 c
+22.927 -0.706 23.082 -0.61 23.192 -0.411 c
+23.31 -0.216 23.368 0.077 23.368 0.47 c
+23.368 0.684 23.346 0.867 23.31 1.014 c
+23.28 1.168 23.236 1.294 23.177 1.382 c
+23.125 1.478 23.059 1.551 22.971 1.602 c
+22.89 1.65 22.803 1.676 22.707 1.676 c
+22.626 1.676 22.549 1.654 22.471 1.617 c
+22.391 1.577 22.317 1.515 22.251 1.426 c
+22.192 1.338 22.137 1.213 22.09 1.058 c
+22.049 0.912 22.03 0.724 22.03 0.5 c
+28.193 1.47 m
+28.094 1.478 27.991 1.488 27.884 1.5 c
+27.774 1.518 27.653 1.529 27.517 1.529 c
+27.341 1.529 27.183 1.488 27.046 1.411 c
+26.907 1.341 26.789 1.243 26.693 1.118 c
+26.606 0.989 26.535 0.842 26.488 0.676 c
+26.448 0.507 26.429 0.331 26.429 0.148 c
+26.429 -1.263 l
+25.533 -1.263 l
+25.533 0.985 l
+25.533 1.11 25.521 1.235 25.503 1.353 c
+25.492 1.478 25.477 1.595 25.459 1.706 c
+25.448 1.823 25.434 1.918 25.415 1.999 c
+25.393 2.087 25.375 2.161 25.356 2.22 c
+26.238 2.22 l
+26.246 2.168 26.257 2.117 26.267 2.058 c
+26.286 1.999 26.3 1.933 26.312 1.867 c
+26.33 1.808 26.344 1.742 26.356 1.676 c
+26.363 1.606 26.375 1.544 26.385 1.484 c
+26.4 1.484 l
+26.437 1.602 26.488 1.709 26.547 1.808 c
+26.613 1.904 26.693 1.989 26.782 2.058 c
+26.87 2.124 26.973 2.18 27.09 2.22 c
+27.216 2.257 27.362 2.278 27.532 2.278 c
+27.657 2.278 27.774 2.271 27.884 2.264 c
+28.002 2.253 28.105 2.238 28.193 2.22 c
+h
+30.196 -1.323 m
+30.026 -1.323 29.876 -1.301 29.741 -1.263 c
+29.612 -1.216 29.498 -1.146 29.402 -1.058 c
+29.314 -0.97 29.244 -0.864 29.197 -0.735 c
+29.145 -0.598 29.122 -0.448 29.122 -0.279 c
+29.122 -0.073 29.156 0.096 29.226 0.235 c
+29.292 0.383 29.388 0.493 29.505 0.574 c
+29.63 0.661 29.773 0.724 29.932 0.765 c
+30.097 0.801 30.273 0.827 30.46 0.838 c
+31.181 0.853 l
+31.181 1.029 l
+31.181 1.147 31.169 1.249 31.152 1.338 c
+31.129 1.426 31.096 1.492 31.048 1.544 c
+31.008 1.602 30.961 1.639 30.901 1.661 c
+30.843 1.679 30.776 1.691 30.71 1.691 c
+30.641 1.691 30.578 1.679 30.519 1.661 c
+30.468 1.65 30.42 1.625 30.373 1.588 c
+30.332 1.559 30.298 1.507 30.269 1.441 c
+30.248 1.382 30.233 1.301 30.225 1.206 c
+29.284 1.249 l
+29.314 1.397 29.358 1.532 29.417 1.661 c
+29.483 1.786 29.579 1.897 29.696 1.985 c
+29.814 2.08 29.953 2.153 30.122 2.205 c
+30.298 2.253 30.504 2.278 30.74 2.278 c
+31.181 2.278 31.511 2.168 31.74 1.955 c
+31.975 1.75 32.092 1.441 32.092 1.029 c
+32.092 -0.235 l
+32.092 -0.455 l
+32.099 -0.515 32.114 -0.569 32.137 -0.617 c
+32.154 -0.658 32.184 -0.691 32.224 -0.72 c
+32.261 -0.742 32.313 -0.75 32.372 -0.75 c
+32.438 -0.75 32.507 -0.746 32.577 -0.735 c
+32.577 -1.22 l
+32.519 -1.23 32.463 -1.242 32.415 -1.249 c
+32.375 -1.261 32.334 -1.267 32.297 -1.278 c
+32.257 -1.286 32.214 -1.294 32.166 -1.294 c
+32.114 -1.301 32.056 -1.308 31.989 -1.308 c
+31.761 -1.308 31.596 -1.257 31.489 -1.146 c
+31.379 -1.028 31.316 -0.864 31.298 -0.646 c
+31.283 -0.646 l
+31.214 -0.756 31.144 -0.852 31.078 -0.941 c
+31.008 -1.022 30.931 -1.087 30.843 -1.146 c
+30.755 -1.205 30.655 -1.249 30.549 -1.278 c
+30.45 -1.308 30.332 -1.323 30.196 -1.323 c
+31.181 0.353 m
+30.755 0.339 l
+30.655 0.339 30.564 0.331 30.475 0.324 c
+30.394 0.312 30.328 0.287 30.269 0.25 c
+30.211 0.21 30.159 0.151 30.122 0.073 c
+30.082 0.004 30.063 -0.087 30.063 -0.205 c
+30.063 -0.374 30.097 -0.496 30.167 -0.573 c
+30.233 -0.654 30.332 -0.691 30.46 -0.691 c
+30.567 -0.691 30.666 -0.669 30.755 -0.617 c
+30.85 -0.569 30.931 -0.507 30.99 -0.426 c
+31.056 -0.349 31.107 -0.261 31.137 -0.162 c
+31.166 -0.055 31.181 0.059 31.181 0.177 c
+h
+35.495 -1.263 m
+35.495 0.721 l
+35.495 1.022 35.451 1.243 35.362 1.382 c
+35.281 1.529 35.146 1.602 34.951 1.602 c
+34.841 1.602 34.738 1.577 34.643 1.529 c
+34.554 1.478 34.473 1.411 34.408 1.324 c
+34.348 1.235 34.297 1.125 34.26 1 c
+34.231 0.882 34.216 0.75 34.216 0.603 c
+34.216 -1.263 l
+33.305 -1.263 l
+33.305 1.441 l
+33.305 1.661 l
+33.305 1.75 33.297 1.827 33.29 1.897 c
+33.29 2.087 l
+33.29 2.22 l
+34.142 2.22 l
+34.15 2.19 34.157 2.147 34.157 2.087 c
+34.157 1.897 l
+34.165 1.827 34.172 1.756 34.172 1.691 c
+34.179 1.621 34.186 1.565 34.186 1.529 c
+34.202 1.529 l
+34.319 1.794 34.47 1.985 34.657 2.103 c
+34.841 2.22 35.061 2.278 35.318 2.278 c
+35.503 2.278 35.664 2.249 35.804 2.19 c
+35.939 2.132 36.054 2.043 36.141 1.926 c
+36.23 1.808 36.293 1.665 36.332 1.5 c
+36.38 1.341 36.407 1.154 36.407 0.941 c
+36.407 -1.263 l
+h
+38.923 -1.323 m
+38.637 -1.323 38.394 -1.282 38.188 -1.205 c
+37.982 -1.117 37.81 -0.995 37.674 -0.837 c
+37.535 -0.683 37.431 -0.496 37.365 -0.279 c
+37.296 -0.055 37.263 0.191 37.263 0.456 c
+37.263 0.75 37.296 1.008 37.365 1.235 c
+37.442 1.459 37.549 1.646 37.689 1.794 c
+37.836 1.948 38.012 2.066 38.218 2.147 c
+38.424 2.234 38.659 2.278 38.923 2.278 c
+39.148 2.278 39.35 2.249 39.526 2.19 c
+39.702 2.132 39.854 2.047 39.981 1.941 c
+40.107 1.841 40.21 1.72 40.291 1.573 c
+40.368 1.434 40.423 1.283 40.452 1.118 c
+39.541 1.073 l
+39.511 1.249 39.441 1.389 39.335 1.5 c
+39.235 1.606 39.092 1.661 38.909 1.661 c
+38.662 1.661 38.487 1.559 38.379 1.353 c
+38.269 1.154 38.218 0.867 38.218 0.485 c
+38.218 -0.309 38.453 -0.706 38.923 -0.706 c
+39.089 -0.706 39.232 -0.654 39.35 -0.544 c
+39.467 -0.437 39.541 -0.276 39.57 -0.058 c
+40.482 -0.103 l
+40.452 -0.272 40.397 -0.426 40.32 -0.573 c
+40.25 -0.72 40.147 -0.852 40.012 -0.97 c
+39.883 -1.08 39.725 -1.168 39.541 -1.234 c
+39.364 -1.294 39.158 -1.323 38.923 -1.323 c
+42.323 1.515 m
+42.441 1.786 42.591 1.985 42.778 2.103 c
+42.962 2.22 43.183 2.278 43.44 2.278 c
+43.646 2.278 43.815 2.242 43.954 2.176 c
+44.101 2.106 44.211 2.014 44.292 1.897 c
+44.38 1.779 44.44 1.636 44.469 1.47 c
+44.505 1.301 44.527 1.125 44.527 0.941 c
+44.527 -1.263 l
+43.616 -1.263 l
+43.616 0.736 l
+43.616 0.871 43.605 0.992 43.586 1.103 c
+43.576 1.209 43.55 1.297 43.513 1.367 c
+43.473 1.444 43.414 1.503 43.337 1.544 c
+43.267 1.58 43.175 1.602 43.058 1.602 c
+42.948 1.602 42.852 1.577 42.763 1.529 c
+42.676 1.478 42.595 1.411 42.528 1.324 c
+42.47 1.235 42.418 1.125 42.381 1 c
+42.352 0.882 42.337 0.75 42.337 0.603 c
+42.337 -1.263 l
+41.426 -1.263 l
+41.426 3.514 l
+42.337 3.514 l
+42.337 2.205 l
+42.337 2.135 42.329 2.066 42.323 1.999 c
+42.323 1.794 l
+42.323 1.735 42.315 1.679 42.308 1.632 c
+42.308 1.515 l
+h
+50.135 0.838 1.867 -0.794 re
+50.135 0.044 m
+55.787 -1.263 m
+55.776 -1.246 55.765 -1.216 55.758 -1.176 c
+55.758 -1.128 55.75 -1.08 55.743 -1.028 c
+55.743 -0.97 55.735 -0.911 55.728 -0.852 c
+55.728 -0.691 l
+55.611 -0.926 55.467 -1.095 55.302 -1.19 c
+55.133 -1.278 54.935 -1.323 54.699 -1.323 c
+54.501 -1.323 54.324 -1.278 54.17 -1.19 c
+54.012 -1.103 53.88 -0.981 53.774 -0.823 c
+53.674 -0.658 53.597 -0.467 53.538 -0.249 c
+53.487 -0.037 53.464 0.206 53.464 0.47 c
+53.464 0.736 53.487 0.974 53.538 1.191 c
+53.597 1.415 53.674 1.606 53.774 1.764 c
+53.88 1.918 54.012 2.043 54.17 2.132 c
+54.335 2.227 54.526 2.278 54.744 2.278 c
+54.839 2.278 54.935 2.264 55.023 2.234 c
+55.118 2.213 55.214 2.18 55.302 2.132 c
+55.39 2.08 55.467 2.018 55.538 1.941 c
+55.615 1.86 55.677 1.768 55.728 1.661 c
+55.728 1.75 l
+55.728 1.897 l
+55.728 2.058 l
+55.728 2.234 l
+55.728 3.514 l
+56.625 3.514 l
+56.625 -0.5 l
+56.625 -0.675 56.629 -0.833 56.639 -0.97 c
+56.647 -1.099 56.654 -1.198 56.654 -1.263 c
+h
+55.743 0.485 m
+55.743 0.721 55.717 0.912 55.669 1.058 c
+55.629 1.213 55.574 1.338 55.507 1.426 c
+55.449 1.515 55.379 1.573 55.302 1.602 c
+55.221 1.639 55.144 1.661 55.067 1.661 c
+54.967 1.661 54.875 1.636 54.788 1.588 c
+54.707 1.548 54.64 1.478 54.582 1.382 c
+54.53 1.283 54.486 1.162 54.449 1.014 c
+54.42 0.867 54.405 0.684 54.405 0.47 c
+54.405 0.077 54.457 -0.216 54.567 -0.411 c
+54.684 -0.61 54.846 -0.706 55.052 -0.706 c
+55.129 -0.706 55.206 -0.687 55.287 -0.646 c
+55.364 -0.61 55.438 -0.544 55.507 -0.455 c
+55.574 -0.367 55.629 -0.246 55.669 -0.087 c
+55.717 0.067 55.743 0.258 55.743 0.485 c
+62.482 -2.631 m
+62.482 3.514 l
+64.408 3.514 l
+64.408 2.896 l
+63.335 2.896 l
+63.335 -2.013 l
+64.408 -2.013 l
+64.408 -2.631 l
+h
+67.983 -1.263 m
+67.983 0.721 l
+67.983 1.022 67.939 1.243 67.851 1.382 c
+67.77 1.529 67.634 1.602 67.44 1.602 c
+67.33 1.602 67.226 1.577 67.131 1.529 c
+67.043 1.478 66.962 1.411 66.896 1.324 c
+66.837 1.235 66.786 1.125 66.748 1 c
+66.719 0.882 66.705 0.75 66.705 0.603 c
+66.705 -1.263 l
+65.793 -1.263 l
+65.793 1.441 l
+65.793 1.661 l
+65.793 1.75 65.786 1.827 65.778 1.897 c
+65.778 2.087 l
+65.778 2.22 l
+66.632 2.22 l
+66.638 2.19 66.646 2.147 66.646 2.087 c
+66.646 1.897 l
+66.653 1.827 66.661 1.756 66.661 1.691 c
+66.668 1.621 66.675 1.565 66.675 1.529 c
+66.69 1.529 l
+66.808 1.794 66.958 1.985 67.145 2.103 c
+67.33 2.22 67.55 2.278 67.808 2.278 c
+67.991 2.278 68.153 2.249 68.292 2.19 c
+68.428 2.132 68.542 2.043 68.63 1.926 c
+68.718 1.808 68.781 1.665 68.822 1.5 c
+68.869 1.341 68.895 1.154 68.895 0.941 c
+68.895 -1.263 l
+h
+70.81 -1.323 m
+70.64 -1.323 70.49 -1.301 70.354 -1.263 c
+70.225 -1.216 70.111 -1.146 70.015 -1.058 c
+69.927 -0.97 69.857 -0.864 69.809 -0.735 c
+69.759 -0.598 69.736 -0.448 69.736 -0.279 c
+69.736 -0.073 69.769 0.096 69.84 0.235 c
+69.905 0.383 70.001 0.493 70.119 0.574 c
+70.243 0.661 70.387 0.724 70.545 0.765 c
+70.71 0.801 70.887 0.827 71.074 0.838 c
+71.794 0.853 l
+71.794 1.029 l
+71.794 1.147 71.783 1.249 71.765 1.338 c
+71.743 1.426 71.71 1.492 71.662 1.544 c
+71.621 1.602 71.573 1.639 71.515 1.661 c
+71.456 1.679 71.39 1.691 71.324 1.691 c
+71.254 1.691 71.191 1.679 71.133 1.661 c
+71.081 1.65 71.033 1.625 70.985 1.588 c
+70.945 1.559 70.912 1.507 70.883 1.441 c
+70.861 1.382 70.846 1.301 70.839 1.206 c
+69.898 1.249 l
+69.927 1.397 69.971 1.532 70.031 1.661 c
+70.096 1.786 70.192 1.897 70.31 1.985 c
+70.428 2.08 70.567 2.153 70.736 2.205 c
+70.912 2.253 71.118 2.278 71.353 2.278 c
+71.794 2.278 72.125 2.168 72.353 1.955 c
+72.588 1.75 72.706 1.441 72.706 1.029 c
+72.706 -0.235 l
+72.706 -0.455 l
+72.713 -0.515 72.728 -0.569 72.749 -0.617 c
+72.768 -0.658 72.797 -0.691 72.838 -0.72 c
+72.874 -0.742 72.926 -0.75 72.984 -0.75 c
+73.051 -0.75 73.121 -0.746 73.19 -0.735 c
+73.19 -1.22 l
+73.132 -1.23 73.077 -1.242 73.029 -1.249 c
+72.988 -1.261 72.948 -1.267 72.911 -1.278 c
+72.871 -1.286 72.826 -1.294 72.779 -1.294 c
+72.728 -1.301 72.668 -1.308 72.602 -1.308 c
+72.375 -1.308 72.209 -1.257 72.103 -1.146 c
+71.993 -1.028 71.93 -0.864 71.912 -0.646 c
+71.897 -0.646 l
+71.827 -0.756 71.758 -0.852 71.691 -0.941 c
+71.621 -1.022 71.544 -1.087 71.456 -1.146 c
+71.368 -1.205 71.269 -1.249 71.162 -1.278 c
+71.063 -1.308 70.945 -1.323 70.81 -1.323 c
+71.794 0.353 m
+71.368 0.339 l
+71.269 0.339 71.177 0.331 71.089 0.324 c
+71.008 0.312 70.942 0.287 70.883 0.25 c
+70.825 0.21 70.773 0.151 70.736 0.073 c
+70.696 0.004 70.677 -0.087 70.677 -0.205 c
+70.677 -0.374 70.71 -0.496 70.78 -0.573 c
+70.846 -0.654 70.945 -0.691 71.074 -0.691 c
+71.18 -0.691 71.28 -0.669 71.368 -0.617 c
+71.463 -0.569 71.544 -0.507 71.603 -0.426 c
+71.669 -0.349 71.721 -0.261 71.75 -0.162 c
+71.779 -0.055 71.794 0.059 71.794 0.177 c
+h
+75.079 -1.263 m
+75.079 0.853 l
+75.079 1.018 75.072 1.154 75.064 1.264 c
+75.054 1.371 75.035 1.455 75.006 1.515 c
+74.984 1.58 74.954 1.632 74.918 1.661 c
+74.889 1.691 74.848 1.706 74.8 1.706 c
+74.742 1.706 74.686 1.676 74.638 1.617 c
+74.598 1.565 74.565 1.492 74.536 1.397 c
+74.507 1.309 74.48 1.195 74.462 1.058 c
+74.451 0.919 74.447 0.769 74.447 0.603 c
+74.447 -1.263 l
+73.698 -1.263 l
+73.698 1.47 l
+73.698 1.706 l
+73.698 1.926 l
+73.698 2.003 73.69 2.066 73.683 2.117 c
+73.683 2.22 l
+74.359 2.22 l
+74.359 2.132 l
+74.359 1.985 l
+74.366 1.926 74.374 1.867 74.374 1.808 c
+74.374 1.646 l
+74.389 1.646 l
+74.407 1.735 74.436 1.812 74.476 1.881 c
+74.513 1.959 74.557 2.029 74.609 2.087 c
+74.667 2.147 74.734 2.19 74.815 2.22 c
+74.892 2.257 74.98 2.278 75.079 2.278 c
+75.263 2.278 75.403 2.224 75.491 2.117 c
+75.586 2.018 75.656 1.86 75.697 1.646 c
+75.712 1.646 l
+75.748 1.742 75.789 1.831 75.829 1.912 c
+75.877 1.989 75.932 2.051 75.991 2.103 c
+76.049 2.161 76.116 2.205 76.196 2.234 c
+76.274 2.264 76.362 2.278 76.461 2.278 c
+76.597 2.278 76.711 2.253 76.799 2.205 c
+76.888 2.153 76.953 2.08 77.005 1.985 c
+77.063 1.885 77.1 1.756 77.123 1.602 c
+77.152 1.455 77.167 1.272 77.167 1.058 c
+77.167 -1.263 l
+76.446 -1.263 l
+76.446 0.853 l
+76.446 1.018 76.439 1.154 76.431 1.264 c
+76.421 1.371 76.402 1.455 76.373 1.515 c
+76.351 1.58 76.321 1.632 76.285 1.661 c
+76.255 1.691 76.215 1.706 76.167 1.706 c
+76.049 1.706 75.954 1.617 75.888 1.441 c
+75.829 1.272 75.8 1.014 75.8 0.661 c
+75.8 -1.263 l
+h
+79.537 -1.323 m
+79.28 -1.323 79.052 -1.286 78.846 -1.22 c
+78.64 -1.143 78.464 -1.028 78.317 -0.881 c
+78.17 -0.727 78.052 -0.536 77.964 -0.309 c
+77.883 -0.085 77.846 0.181 77.846 0.485 c
+77.846 0.816 77.89 1.095 77.979 1.324 c
+78.075 1.559 78.203 1.742 78.361 1.881 c
+78.526 2.018 78.714 2.117 78.92 2.176 c
+79.126 2.242 79.334 2.278 79.552 2.278 c
+79.824 2.278 80.059 2.227 80.257 2.132 c
+80.463 2.043 80.628 1.912 80.757 1.735 c
+80.893 1.565 80.992 1.36 81.051 1.118 c
+81.117 0.882 81.154 0.618 81.154 0.324 c
+81.154 0.309 l
+78.787 0.309 l
+78.787 0.162 78.802 0.023 78.831 -0.103 c
+78.868 -0.231 78.923 -0.345 78.993 -0.44 c
+79.059 -0.529 79.143 -0.598 79.243 -0.646 c
+79.338 -0.698 79.452 -0.72 79.581 -0.72 c
+79.735 -0.72 79.875 -0.687 79.993 -0.617 c
+80.117 -0.551 80.206 -0.448 80.257 -0.309 c
+81.095 -0.382 l
+81.065 -0.481 81.011 -0.588 80.934 -0.706 c
+80.853 -0.816 80.749 -0.918 80.624 -1.014 c
+80.508 -1.103 80.352 -1.176 80.169 -1.234 c
+79.993 -1.294 79.779 -1.323 79.537 -1.323 c
+79.537 1.706 m
+79.448 1.706 79.361 1.691 79.272 1.661 c
+79.184 1.632 79.103 1.58 79.037 1.515 c
+78.968 1.444 78.908 1.357 78.861 1.249 c
+78.821 1.139 78.802 1.014 78.802 0.867 c
+80.272 0.867 l
+80.272 1.004 80.246 1.125 80.198 1.235 c
+80.158 1.341 80.103 1.43 80.037 1.5 c
+79.978 1.565 79.905 1.617 79.816 1.646 c
+79.728 1.683 79.633 1.706 79.537 1.706 c
+82.407 -2.631 m
+82.407 -2.013 l
+83.48 -2.013 l
+83.48 2.896 l
+82.407 2.896 l
+82.407 3.514 l
+84.333 3.514 l
+84.333 -2.631 l
+h
+f
+Q
+q 1 0 0 1 545.482 99.4081 cm
+0 0 m
+-0.941 0 l
+-0.941 -2.161 l
+-1.616 -2.161 l
+-1.616 3.19 l
+-0.118 3.19 l
+0.412 3.19 0.809 3.05 1.073 2.778 c
+1.345 2.502 1.484 2.106 1.484 1.588 c
+1.484 1.253 1.411 0.963 1.264 0.721 c
+1.118 0.474 0.912 0.287 0.647 0.162 c
+1.676 -2.117 l
+1.676 -2.161 l
+0.956 -2.161 l
+h
+-0.941 0.588 m
+-0.118 0.588 l
+0.166 0.588 0.389 0.676 0.559 0.853 c
+0.724 1.037 0.809 1.283 0.809 1.588 c
+0.809 2.271 0.493 2.617 -0.132 2.617 c
+-0.941 2.617 l
+h
+3.719 -2.234 m
+3.219 -2.234 2.837 -2.087 2.573 -1.793 c
+2.308 -1.499 2.176 -1.066 2.176 -0.484 c
+2.176 -0.014 l
+2.176 0.58 2.301 1.048 2.558 1.382 c
+2.822 1.723 3.183 1.897 3.645 1.897 c
+4.105 1.897 4.447 1.742 4.675 1.441 c
+4.91 1.147 5.031 0.684 5.042 0.059 c
+5.042 -0.367 l
+2.822 -0.367 l
+2.822 -0.455 l
+2.822 -0.889 2.899 -1.201 3.057 -1.396 c
+3.223 -1.583 3.454 -1.675 3.749 -1.675 c
+3.944 -1.675 4.116 -1.643 4.263 -1.573 c
+4.41 -1.496 4.546 -1.378 4.675 -1.22 c
+5.012 -1.631 l
+4.726 -2.036 4.296 -2.234 3.719 -2.234 c
+3.645 1.338 m
+3.37 1.338 3.168 1.243 3.043 1.058 c
+2.914 0.871 2.841 0.58 2.822 0.191 c
+4.395 0.191 l
+4.395 0.279 l
+4.374 0.661 4.307 0.93 4.189 1.088 c
+4.072 1.253 3.888 1.338 3.645 1.338 c
+6.438 1.823 m
+6.454 1.455 l
+6.696 1.75 7.015 1.897 7.408 1.897 c
+7.85 1.897 8.158 1.698 8.335 1.309 c
+8.588 1.698 8.937 1.897 9.378 1.897 c
+10.113 1.897 10.488 1.434 10.51 0.515 c
+10.51 -2.161 l
+9.864 -2.161 l
+9.864 0.456 l
+9.864 0.75 9.808 0.963 9.702 1.103 c
+9.602 1.239 9.43 1.309 9.187 1.309 c
+8.989 1.309 8.827 1.228 8.702 1.073 c
+8.584 0.927 8.515 0.736 8.497 0.5 c
+8.497 -2.161 l
+7.834 -2.161 l
+7.834 0.485 l
+7.834 1.033 7.614 1.309 7.173 1.309 c
+6.839 1.309 6.604 1.147 6.468 0.823 c
+6.468 -2.161 l
+5.821 -2.161 l
+5.821 1.823 l
+h
+11.348 0.015 m
+11.348 0.592 11.484 1.048 11.759 1.382 c
+12.042 1.723 12.414 1.897 12.877 1.897 c
+13.336 1.897 13.704 1.727 13.979 1.397 c
+14.263 1.073 14.409 0.625 14.42 0.059 c
+14.42 -0.367 l
+14.42 -0.937 14.277 -1.392 13.994 -1.734 c
+13.718 -2.069 13.351 -2.234 12.891 -2.234 c
+12.428 -2.234 12.057 -2.072 11.774 -1.749 c
+11.499 -1.419 11.356 -0.977 11.348 -0.426 c
+h
+11.994 -0.367 m
+11.994 -0.771 12.072 -1.087 12.23 -1.323 c
+12.395 -1.558 12.616 -1.675 12.891 -1.675 c
+13.457 -1.675 13.752 -1.263 13.773 -0.44 c
+13.773 0.015 l
+13.773 0.416 13.689 0.736 13.523 0.971 c
+13.365 1.213 13.149 1.338 12.877 1.338 c
+12.613 1.338 12.395 1.213 12.23 0.971 c
+12.072 0.736 11.994 0.416 11.994 0.015 c
+h
+16.331 -1.176 m
+17.081 1.823 l
+17.742 1.823 l
+16.566 -2.161 l
+16.081 -2.161 l
+14.89 1.823 l
+15.552 1.823 l
+h
+19.8 -2.234 m
+19.3 -2.234 18.918 -2.087 18.653 -1.793 c
+18.389 -1.499 18.257 -1.066 18.257 -0.484 c
+18.257 -0.014 l
+18.257 0.58 18.381 1.048 18.639 1.382 c
+18.903 1.723 19.264 1.897 19.726 1.897 c
+20.185 1.897 20.528 1.742 20.756 1.441 c
+20.991 1.147 21.112 0.684 21.122 0.059 c
+21.122 -0.367 l
+18.903 -0.367 l
+18.903 -0.455 l
+18.903 -0.889 18.98 -1.201 19.138 -1.396 c
+19.304 -1.583 19.535 -1.675 19.829 -1.675 c
+20.024 -1.675 20.197 -1.643 20.344 -1.573 c
+20.491 -1.496 20.627 -1.378 20.756 -1.22 c
+21.093 -1.631 l
+20.807 -2.036 20.377 -2.234 19.8 -2.234 c
+19.726 1.338 m
+19.451 1.338 19.248 1.243 19.123 1.058 c
+18.995 0.871 18.922 0.58 18.903 0.191 c
+20.476 0.191 l
+20.476 0.279 l
+20.455 0.661 20.388 0.93 20.27 1.088 c
+20.153 1.253 19.969 1.338 19.726 1.338 c
+25.562 -1.146 m
+25.562 -0.999 25.507 -0.878 25.4 -0.779 c
+25.29 -0.683 25.084 -0.565 24.783 -0.426 c
+24.438 -0.279 24.195 -0.158 24.048 -0.058 c
+23.901 0.048 23.79 0.166 23.724 0.294 c
+23.655 0.42 23.622 0.578 23.622 0.765 c
+23.622 1.088 23.74 1.357 23.975 1.573 c
+24.21 1.786 24.511 1.897 24.885 1.897 c
+25.268 1.897 25.577 1.783 25.812 1.559 c
+26.047 1.33 26.165 1.044 26.165 0.691 c
+25.518 0.691 l
+25.518 0.867 25.459 1.018 25.342 1.147 c
+25.224 1.272 25.07 1.338 24.885 1.338 c
+24.688 1.338 24.536 1.283 24.43 1.176 c
+24.32 1.077 24.268 0.945 24.268 0.779 c
+24.268 0.651 24.305 0.544 24.386 0.456 c
+24.463 0.375 24.654 0.272 24.96 0.148 c
+25.437 -0.04 25.768 -0.228 25.944 -0.411 c
+26.121 -0.588 26.209 -0.816 26.209 -1.087 c
+26.209 -1.44 26.084 -1.72 25.841 -1.926 c
+25.606 -2.131 25.29 -2.234 24.9 -2.234 c
+24.478 -2.234 24.14 -2.117 23.886 -1.881 c
+23.63 -1.639 23.504 -1.334 23.504 -0.97 c
+24.151 -0.97 l
+24.158 -1.198 24.228 -1.374 24.357 -1.499 c
+24.482 -1.616 24.665 -1.675 24.9 -1.675 c
+25.114 -1.675 25.276 -1.627 25.386 -1.529 c
+25.503 -1.433 25.562 -1.304 25.562 -1.146 c
+28.472 -2.234 m
+27.973 -2.234 27.59 -2.087 27.326 -1.793 c
+27.061 -1.499 26.929 -1.066 26.929 -0.484 c
+26.929 -0.014 l
+26.929 0.58 27.054 1.048 27.312 1.382 c
+27.576 1.723 27.936 1.897 28.399 1.897 c
+28.858 1.897 29.2 1.742 29.428 1.441 c
+29.663 1.147 29.785 0.684 29.795 0.059 c
+29.795 -0.367 l
+27.576 -0.367 l
+27.576 -0.455 l
+27.576 -0.889 27.653 -1.201 27.811 -1.396 c
+27.977 -1.583 28.208 -1.675 28.502 -1.675 c
+28.696 -1.675 28.869 -1.643 29.016 -1.573 c
+29.163 -1.496 29.299 -1.378 29.428 -1.22 c
+29.766 -1.631 l
+29.479 -2.036 29.049 -2.234 28.472 -2.234 c
+28.399 1.338 m
+28.123 1.338 27.921 1.243 27.796 1.058 c
+27.668 0.871 27.594 0.58 27.576 0.191 c
+29.149 0.191 l
+29.149 0.279 l
+29.126 0.661 29.06 0.93 28.943 1.088 c
+28.825 1.253 28.642 1.338 28.399 1.338 c
+31.28 -2.161 -0.646 5.644 re
+33.72 -2.234 m
+33.22 -2.234 32.838 -2.087 32.573 -1.793 c
+32.309 -1.499 32.176 -1.066 32.176 -0.484 c
+32.176 -0.014 l
+32.176 0.58 32.301 1.048 32.559 1.382 c
+32.823 1.723 33.184 1.897 33.647 1.897 c
+34.106 1.897 34.448 1.742 34.676 1.441 c
+34.911 1.147 35.032 0.684 35.043 0.059 c
+35.043 -0.367 l
+32.823 -0.367 l
+32.823 -0.455 l
+32.823 -0.889 32.901 -1.201 33.059 -1.396 c
+33.224 -1.583 33.456 -1.675 33.749 -1.675 c
+33.944 -1.675 34.117 -1.643 34.264 -1.573 c
+34.41 -1.496 34.547 -1.378 34.676 -1.22 c
+35.013 -1.631 l
+34.726 -2.036 34.297 -2.234 33.72 -2.234 c
+33.647 1.338 m
+33.371 1.338 33.169 1.243 33.043 1.058 c
+32.916 0.871 32.842 0.58 32.823 0.191 c
+34.396 0.191 l
+34.396 0.279 l
+34.374 0.661 34.308 0.93 34.19 1.088 c
+34.073 1.253 33.889 1.338 33.647 1.338 c
+37.203 -1.675 m
+37.417 -1.675 37.589 -1.613 37.718 -1.484 c
+37.854 -1.348 37.928 -1.157 37.938 -0.911 c
+38.556 -0.911 l
+38.534 -1.294 38.398 -1.613 38.144 -1.866 c
+37.887 -2.113 37.575 -2.234 37.203 -2.234 c
+36.711 -2.234 36.336 -2.084 36.072 -1.778 c
+35.815 -1.466 35.69 -0.999 35.69 -0.382 c
+35.69 0.059 l
+35.69 0.655 35.815 1.11 36.072 1.426 c
+36.336 1.738 36.711 1.897 37.203 1.897 c
+37.604 1.897 37.924 1.764 38.159 1.5 c
+38.402 1.243 38.534 0.897 38.556 0.456 c
+37.938 0.456 l
+37.917 0.75 37.843 0.971 37.718 1.118 c
+37.6 1.264 37.427 1.338 37.203 1.338 c
+36.91 1.338 36.693 1.239 36.557 1.044 c
+36.417 0.856 36.344 0.548 36.336 0.118 c
+36.336 -0.397 l
+36.336 -0.867 36.403 -1.201 36.542 -1.396 c
+36.689 -1.583 36.91 -1.675 37.203 -1.675 c
+40.158 2.778 m
+40.158 1.823 l
+40.76 1.823 l
+40.76 1.294 l
+40.158 1.294 l
+40.158 -1.176 l
+40.158 -1.334 40.18 -1.452 40.232 -1.529 c
+40.29 -1.61 40.378 -1.646 40.496 -1.646 c
+40.584 -1.646 40.673 -1.631 40.76 -1.602 c
+40.76 -2.161 l
+40.614 -2.208 40.459 -2.234 40.305 -2.234 c
+40.048 -2.234 39.854 -2.142 39.717 -1.955 c
+39.578 -1.771 39.511 -1.51 39.511 -1.176 c
+39.511 1.294 l
+38.909 1.294 l
+38.909 1.823 l
+39.511 1.823 l
+39.511 2.778 l
+h
+42.951 -2.234 m
+42.451 -2.234 42.069 -2.087 41.805 -1.793 c
+41.54 -1.499 41.408 -1.066 41.408 -0.484 c
+41.408 -0.014 l
+41.408 0.58 41.533 1.048 41.79 1.382 c
+42.054 1.723 42.414 1.897 42.877 1.897 c
+43.337 1.897 43.679 1.742 43.906 1.441 c
+44.141 1.147 44.263 0.684 44.274 0.059 c
+44.274 -0.367 l
+42.054 -0.367 l
+42.054 -0.455 l
+42.054 -0.889 42.131 -1.201 42.289 -1.396 c
+42.455 -1.583 42.686 -1.675 42.981 -1.675 c
+43.175 -1.675 43.347 -1.643 43.495 -1.573 c
+43.642 -1.496 43.777 -1.378 43.906 -1.22 c
+44.245 -1.631 l
+43.958 -2.036 43.528 -2.234 42.951 -2.234 c
+42.877 1.338 m
+42.601 1.338 42.4 1.243 42.275 1.058 c
+42.146 0.871 42.073 0.58 42.054 0.191 c
+43.627 0.191 l
+43.627 0.279 l
+43.605 0.661 43.539 0.93 43.422 1.088 c
+43.304 1.253 43.12 1.338 42.877 1.338 c
+44.92 0.015 m
+44.92 0.621 45.031 1.088 45.259 1.411 c
+45.494 1.735 45.821 1.897 46.244 1.897 c
+46.626 1.897 46.923 1.738 47.141 1.426 c
+47.141 3.484 l
+47.787 3.484 l
+47.787 -2.161 l
+47.199 -2.161 l
+47.155 -1.734 l
+46.95 -2.069 46.644 -2.234 46.244 -2.234 c
+45.832 -2.234 45.508 -2.08 45.273 -1.764 c
+45.038 -1.44 44.92 -0.985 44.92 -0.397 c
+h
+45.568 -0.367 m
+45.568 -0.808 45.63 -1.139 45.759 -1.352 c
+45.894 -1.558 46.115 -1.66 46.42 -1.66 c
+46.744 -1.66 46.982 -1.499 47.141 -1.176 c
+47.141 0.838 l
+46.971 1.151 46.732 1.309 46.42 1.309 c
+46.115 1.309 45.894 1.206 45.759 1 c
+45.63 0.794 45.568 0.47 45.568 0.03 c
+h
+53.387 -0.367 m
+53.387 -0.985 53.273 -1.452 53.049 -1.764 c
+52.833 -2.08 52.509 -2.234 52.079 -2.234 c
+51.657 -2.234 51.344 -2.054 51.139 -1.691 c
+51.109 -2.161 l
+50.506 -2.161 l
+50.506 3.484 l
+51.153 3.484 l
+51.153 1.382 l
+51.366 1.723 51.675 1.897 52.079 1.897 c
+52.509 1.897 52.833 1.738 53.049 1.426 c
+53.273 1.121 53.387 0.655 53.387 0.03 c
+h
+52.741 0.015 m
+52.741 0.485 52.671 0.816 52.535 1.014 c
+52.406 1.209 52.197 1.309 51.902 1.309 c
+51.569 1.309 51.318 1.125 51.153 0.765 c
+51.153 -1.117 l
+51.318 -1.481 51.572 -1.66 51.918 -1.66 c
+52.211 -1.66 52.421 -1.558 52.55 -1.352 c
+52.675 -1.146 52.741 -0.831 52.741 -0.397 c
+h
+55.872 1.206 m
+55.783 1.224 55.684 1.235 55.577 1.235 c
+55.243 1.235 55.008 1.051 54.871 0.691 c
+54.871 -2.161 l
+54.225 -2.161 l
+54.225 1.823 l
+54.857 1.823 l
+54.871 1.411 l
+55.048 1.735 55.291 1.897 55.607 1.897 c
+55.713 1.897 55.802 1.874 55.872 1.837 c
+h
+58.414 -2.161 m
+58.374 -2.072 58.348 -1.926 58.341 -1.72 c
+58.106 -2.065 57.811 -2.234 57.458 -2.234 c
+57.095 -2.234 56.812 -2.138 56.606 -1.94 c
+56.408 -1.734 56.313 -1.448 56.313 -1.072 c
+56.313 -0.673 56.448 -0.353 56.724 -0.118 c
+56.996 0.125 57.371 0.25 57.841 0.25 c
+58.326 0.25 l
+58.326 0.676 l
+58.326 0.912 58.271 1.077 58.164 1.176 c
+58.054 1.283 57.892 1.338 57.68 1.338 c
+57.481 1.338 57.319 1.279 57.194 1.162 c
+57.077 1.044 57.018 0.897 57.018 0.721 c
+56.371 0.721 l
+56.371 0.915 56.43 1.106 56.548 1.294 c
+56.673 1.478 56.834 1.625 57.032 1.735 c
+57.238 1.841 57.466 1.897 57.724 1.897 c
+58.124 1.897 58.429 1.794 58.634 1.588 c
+58.848 1.382 58.962 1.088 58.973 0.706 c
+58.973 -1.308 l
+58.973 -1.613 59.01 -1.878 59.091 -2.102 c
+59.091 -2.161 l
+h
+57.547 -1.646 m
+57.713 -1.646 57.863 -1.602 58.003 -1.514 c
+58.15 -1.425 58.256 -1.315 58.326 -1.176 c
+58.326 -0.235 l
+57.959 -0.235 l
+57.643 -0.235 57.4 -0.305 57.223 -0.44 c
+57.047 -0.569 56.959 -0.756 56.959 -0.999 c
+56.959 -1.227 57.003 -1.392 57.092 -1.499 c
+57.18 -1.598 57.331 -1.646 57.547 -1.646 c
+60.59 1.823 m
+60.604 1.382 l
+60.858 1.723 61.181 1.897 61.574 1.897 c
+62.28 1.897 62.636 1.426 62.648 0.485 c
+62.648 -2.161 l
+62.001 -2.161 l
+62.001 0.456 l
+62.001 0.769 61.946 0.989 61.84 1.118 c
+61.729 1.243 61.574 1.309 61.369 1.309 c
+61.211 1.309 61.064 1.253 60.928 1.147 c
+60.799 1.037 60.697 0.9 60.619 0.736 c
+60.619 -2.161 l
+59.972 -2.161 l
+59.972 1.823 l
+h
+64.999 -1.675 m
+65.213 -1.675 65.385 -1.613 65.514 -1.484 c
+65.65 -1.348 65.724 -1.157 65.734 -0.911 c
+66.352 -0.911 l
+66.33 -1.294 66.194 -1.613 65.94 -1.866 c
+65.683 -2.113 65.371 -2.234 64.999 -2.234 c
+64.508 -2.234 64.132 -2.084 63.868 -1.778 c
+63.611 -1.466 63.486 -0.999 63.486 -0.382 c
+63.486 0.059 l
+63.486 0.655 63.611 1.11 63.868 1.426 c
+64.132 1.738 64.508 1.897 64.999 1.897 c
+65.4 1.897 65.72 1.764 65.955 1.5 c
+66.198 1.243 66.33 0.897 66.352 0.456 c
+65.734 0.456 l
+65.713 0.75 65.639 0.971 65.514 1.118 c
+65.396 1.264 65.223 1.338 64.999 1.338 c
+64.706 1.338 64.489 1.239 64.353 1.044 c
+64.213 0.856 64.14 0.548 64.132 0.118 c
+64.132 -0.397 l
+64.132 -0.867 64.199 -1.201 64.338 -1.396 c
+64.485 -1.583 64.706 -1.675 64.999 -1.675 c
+67.748 1.411 m
+68.001 1.735 68.321 1.897 68.704 1.897 c
+69.41 1.897 69.766 1.426 69.777 0.485 c
+69.777 -2.161 l
+69.13 -2.161 l
+69.13 0.456 l
+69.13 0.769 69.075 0.989 68.969 1.118 c
+68.858 1.243 68.704 1.309 68.498 1.309 c
+68.34 1.309 68.193 1.253 68.057 1.147 c
+67.928 1.037 67.825 0.9 67.748 0.736 c
+67.748 -2.161 l
+67.101 -2.161 l
+67.101 3.484 l
+67.748 3.484 l
+h
+70.746 -3.233 m
+70.35 -2.969 l
+70.586 -2.645 70.707 -2.311 70.717 -1.969 c
+70.717 -1.352 l
+71.379 -1.352 l
+71.379 -1.881 l
+71.379 -2.138 71.313 -2.385 71.188 -2.631 c
+71.07 -2.873 70.923 -3.075 70.746 -3.233 c
+74.642 -2.161 -0.646 3.984 re
+74.686 2.866 m
+74.686 2.756 74.657 2.664 74.598 2.587 c
+74.54 2.517 74.444 2.484 74.318 2.484 c
+74.201 2.484 74.106 2.517 74.039 2.587 c
+73.981 2.664 73.952 2.756 73.952 2.866 c
+73.952 2.984 73.981 3.076 74.039 3.146 c
+74.106 3.223 74.201 3.263 74.318 3.263 c
+74.444 3.263 74.54 3.223 74.598 3.146 c
+74.657 3.065 74.686 2.973 74.686 2.866 c
+75.951 -2.161 m
+75.951 1.294 l
+75.421 1.294 l
+75.421 1.823 l
+75.951 1.823 l
+75.951 2.278 l
+75.951 2.679 76.046 2.992 76.244 3.219 c
+76.45 3.443 76.73 3.558 77.082 3.558 c
+77.218 3.558 77.35 3.535 77.479 3.499 c
+77.449 2.955 l
+77.35 2.973 77.252 2.984 77.156 2.984 c
+76.781 2.984 76.597 2.72 76.597 2.19 c
+76.597 1.823 l
+77.273 1.823 l
+77.273 1.294 l
+76.597 1.294 l
+76.597 -2.161 l
+h
+80.463 -2.161 -0.647 3.984 re
+80.507 2.866 m
+80.507 2.756 80.477 2.664 80.419 2.587 c
+80.36 2.517 80.265 2.484 80.14 2.484 c
+80.022 2.484 79.926 2.517 79.86 2.587 c
+79.801 2.664 79.772 2.756 79.772 2.866 c
+79.772 2.984 79.801 3.076 79.86 3.146 c
+79.926 3.223 80.022 3.263 80.14 3.263 c
+80.265 3.263 80.36 3.223 80.419 3.146 c
+80.477 3.065 80.507 2.973 80.507 2.866 c
+82.33 2.778 m
+82.33 1.823 l
+82.933 1.823 l
+82.933 1.294 l
+82.33 1.294 l
+82.33 -1.176 l
+82.33 -1.334 82.351 -1.452 82.403 -1.529 c
+82.462 -1.61 82.55 -1.646 82.667 -1.646 c
+82.756 -1.646 82.844 -1.631 82.933 -1.602 c
+82.933 -2.161 l
+82.785 -2.208 82.631 -2.234 82.477 -2.234 c
+82.22 -2.234 82.025 -2.142 81.889 -1.955 c
+81.749 -1.771 81.683 -1.51 81.683 -1.176 c
+81.683 1.294 l
+81.081 1.294 l
+81.081 1.823 l
+81.683 1.823 l
+81.683 2.778 l
+h
+86.122 -2.161 -0.646 3.984 re
+86.166 2.866 m
+86.166 2.756 86.137 2.664 86.078 2.587 c
+86.019 2.517 85.923 2.484 85.798 2.484 c
+85.681 2.484 85.586 2.517 85.52 2.587 c
+85.461 2.664 85.431 2.756 85.431 2.866 c
+85.431 2.984 85.461 3.076 85.52 3.146 c
+85.586 3.223 85.681 3.263 85.798 3.263 c
+85.923 3.263 86.019 3.223 86.078 3.146 c
+86.137 3.065 86.166 2.973 86.166 2.866 c
+89.077 -1.146 m
+89.077 -0.999 89.021 -0.878 88.915 -0.779 c
+88.805 -0.683 88.599 -0.565 88.298 -0.426 c
+87.953 -0.279 87.71 -0.158 87.562 -0.058 c
+87.415 0.048 87.305 0.166 87.24 0.294 c
+87.17 0.42 87.136 0.578 87.136 0.765 c
+87.136 1.088 87.254 1.357 87.489 1.573 c
+87.724 1.786 88.026 1.897 88.4 1.897 c
+88.782 1.897 89.091 1.783 89.326 1.559 c
+89.561 1.33 89.679 1.044 89.679 0.691 c
+89.033 0.691 l
+89.033 0.867 88.973 1.018 88.856 1.147 c
+88.738 1.272 88.584 1.338 88.4 1.338 c
+88.202 1.338 88.051 1.283 87.945 1.176 c
+87.835 1.077 87.783 0.945 87.783 0.779 c
+87.783 0.651 87.82 0.544 87.901 0.456 c
+87.978 0.375 88.169 0.272 88.474 0.148 c
+88.952 -0.04 89.283 -0.228 89.459 -0.411 c
+89.636 -0.588 89.723 -0.816 89.723 -1.087 c
+89.723 -1.44 89.599 -1.72 89.356 -1.926 c
+89.121 -2.131 88.805 -2.234 88.415 -2.234 c
+87.992 -2.234 87.654 -2.117 87.401 -1.881 c
+87.143 -1.639 87.018 -1.334 87.018 -0.97 c
+87.666 -0.97 l
+87.673 -1.198 87.743 -1.374 87.872 -1.499 c
+87.996 -1.616 88.18 -1.675 88.415 -1.675 c
+88.628 -1.675 88.79 -1.627 88.9 -1.529 c
+89.017 -1.433 89.077 -1.304 89.077 -1.146 c
+94.324 -2.161 m
+94.284 -2.072 94.258 -1.926 94.251 -1.72 c
+94.016 -2.065 93.721 -2.234 93.368 -2.234 c
+93.005 -2.234 92.722 -2.138 92.516 -1.94 c
+92.318 -1.734 92.223 -1.448 92.223 -1.072 c
+92.223 -0.673 92.358 -0.353 92.634 -0.118 c
+92.906 0.125 93.281 0.25 93.751 0.25 c
+94.236 0.25 l
+94.236 0.676 l
+94.236 0.912 94.181 1.077 94.074 1.176 c
+93.964 1.283 93.802 1.338 93.59 1.338 c
+93.391 1.338 93.229 1.279 93.104 1.162 c
+92.986 1.044 92.928 0.897 92.928 0.721 c
+92.281 0.721 l
+92.281 0.915 92.34 1.106 92.458 1.294 c
+92.582 1.478 92.744 1.625 92.942 1.735 c
+93.148 1.841 93.376 1.897 93.634 1.897 c
+94.034 1.897 94.339 1.794 94.544 1.588 c
+94.758 1.382 94.872 1.088 94.883 0.706 c
+94.883 -1.308 l
+94.883 -1.613 94.92 -1.878 95.001 -2.102 c
+95.001 -2.161 l
+h
+93.457 -1.646 m
+93.622 -1.646 93.773 -1.602 93.912 -1.514 c
+94.06 -1.425 94.166 -1.315 94.236 -1.176 c
+94.236 -0.235 l
+93.869 -0.235 l
+93.553 -0.235 93.31 -0.305 93.133 -0.44 c
+92.957 -0.569 92.869 -0.756 92.869 -0.999 c
+92.869 -1.227 92.913 -1.392 93.002 -1.499 c
+93.089 -1.598 93.24 -1.646 93.457 -1.646 c
+96.588 -2.161 -0.646 5.644 re
+99.248 1.206 m
+99.161 1.224 99.061 1.235 98.955 1.235 c
+98.62 1.235 98.384 1.051 98.249 0.691 c
+98.249 -2.161 l
+97.602 -2.161 l
+97.602 1.823 l
+98.234 1.823 l
+98.249 1.411 l
+98.425 1.735 98.668 1.897 98.984 1.897 c
+99.09 1.897 99.178 1.874 99.248 1.837 c
+h
+101.247 -2.234 m
+100.748 -2.234 100.366 -2.087 100.101 -1.793 c
+99.836 -1.499 99.704 -1.066 99.704 -0.484 c
+99.704 -0.014 l
+99.704 0.58 99.829 1.048 100.086 1.382 c
+100.351 1.723 100.711 1.897 101.174 1.897 c
+101.633 1.897 101.975 1.742 102.203 1.441 c
+102.438 1.147 102.559 0.684 102.571 0.059 c
+102.571 -0.367 l
+100.351 -0.367 l
+100.351 -0.455 l
+100.351 -0.889 100.428 -1.201 100.586 -1.396 c
+100.751 -1.583 100.983 -1.675 101.277 -1.675 c
+101.472 -1.675 101.644 -1.643 101.791 -1.573 c
+101.939 -1.496 102.074 -1.378 102.203 -1.22 c
+102.541 -1.631 l
+102.255 -2.036 101.825 -2.234 101.247 -2.234 c
+101.174 1.338 m
+100.898 1.338 100.696 1.243 100.572 1.058 c
+100.443 0.871 100.369 0.58 100.351 0.191 c
+101.923 0.191 l
+101.923 0.279 l
+101.902 0.661 101.835 0.93 101.717 1.088 c
+101.6 1.253 101.416 1.338 101.174 1.338 c
+105.393 -2.161 m
+105.352 -2.072 105.326 -1.926 105.319 -1.72 c
+105.083 -2.065 104.79 -2.234 104.437 -2.234 c
+104.073 -2.234 103.79 -2.138 103.585 -1.94 c
+103.386 -1.734 103.29 -1.448 103.29 -1.072 c
+103.29 -0.673 103.427 -0.353 103.702 -0.118 c
+103.974 0.125 104.349 0.25 104.819 0.25 c
+105.305 0.25 l
+105.305 0.676 l
+105.305 0.912 105.249 1.077 105.143 1.176 c
+105.033 1.283 104.871 1.338 104.657 1.338 c
+104.459 1.338 104.298 1.279 104.173 1.162 c
+104.055 1.044 103.996 0.897 103.996 0.721 c
+103.35 0.721 l
+103.35 0.915 103.408 1.106 103.526 1.294 c
+103.651 1.478 103.813 1.625 104.011 1.735 c
+104.217 1.841 104.445 1.897 104.701 1.897 c
+105.102 1.897 105.407 1.794 105.613 1.588 c
+105.827 1.382 105.94 1.088 105.951 0.706 c
+105.951 -1.308 l
+105.951 -1.613 105.987 -1.878 106.068 -2.102 c
+106.068 -2.161 l
+h
+104.526 -1.646 m
+104.69 -1.646 104.842 -1.602 104.981 -1.514 c
+105.128 -1.425 105.235 -1.315 105.305 -1.176 c
+105.305 -0.235 l
+104.937 -0.235 l
+104.621 -0.235 104.378 -0.305 104.202 -0.44 c
+104.025 -0.569 103.938 -0.756 103.938 -0.999 c
+103.938 -1.227 103.982 -1.392 104.069 -1.499 c
+104.158 -1.598 104.308 -1.646 104.526 -1.646 c
+106.818 0.015 m
+106.818 0.621 106.928 1.088 107.157 1.411 c
+107.392 1.735 107.718 1.897 108.141 1.897 c
+108.524 1.897 108.821 1.738 109.038 1.426 c
+109.038 3.484 l
+109.685 3.484 l
+109.685 -2.161 l
+109.097 -2.161 l
+109.052 -1.734 l
+108.846 -2.069 108.542 -2.234 108.141 -2.234 c
+107.73 -2.234 107.406 -2.08 107.171 -1.764 c
+106.936 -1.44 106.818 -0.985 106.818 -0.397 c
+h
+107.465 -0.367 m
+107.465 -0.808 107.527 -1.139 107.656 -1.352 c
+107.792 -1.558 108.013 -1.66 108.318 -1.66 c
+108.641 -1.66 108.88 -1.499 109.038 -1.176 c
+109.038 0.838 l
+108.869 1.151 108.63 1.309 108.318 1.309 c
+108.013 1.309 107.792 1.206 107.656 1 c
+107.527 0.794 107.465 0.47 107.465 0.03 c
+h
+111.772 -1.072 m
+112.492 1.823 l
+113.183 1.823 l
+111.89 -2.719 l
+111.79 -3.061 111.647 -3.322 111.464 -3.498 c
+111.287 -3.675 111.084 -3.763 110.86 -3.763 c
+110.772 -3.763 110.658 -3.74 110.523 -3.704 c
+110.523 -3.16 l
+110.67 -3.175 l
+110.853 -3.175 111.001 -3.131 111.111 -3.042 c
+111.217 -2.954 111.305 -2.796 111.375 -2.572 c
+111.493 -2.131 l
+110.332 1.823 l
+111.037 1.823 l
+h
+116.152 1.823 m
+116.167 1.455 l
+116.41 1.75 116.729 1.897 117.122 1.897 c
+117.563 1.897 117.872 1.698 118.049 1.309 c
+118.302 1.698 118.651 1.897 119.092 1.897 c
+119.827 1.897 120.202 1.434 120.224 0.515 c
+120.224 -2.161 l
+119.577 -2.161 l
+119.577 0.456 l
+119.577 0.75 119.522 0.963 119.416 1.103 c
+119.316 1.239 119.144 1.309 118.901 1.309 c
+118.703 1.309 118.541 1.228 118.416 1.073 c
+118.298 0.927 118.228 0.736 118.211 0.5 c
+118.211 -2.161 l
+117.548 -2.161 l
+117.548 0.485 l
+117.548 1.033 117.328 1.309 116.887 1.309 c
+116.553 1.309 116.318 1.147 116.181 0.823 c
+116.181 -2.161 l
+115.535 -2.161 l
+115.535 1.823 l
+h
+122.62 -2.234 m
+122.12 -2.234 121.738 -2.087 121.473 -1.793 c
+121.209 -1.499 121.076 -1.066 121.076 -0.484 c
+121.076 -0.014 l
+121.076 0.58 121.201 1.048 121.459 1.382 c
+121.723 1.723 122.084 1.897 122.546 1.897 c
+123.005 1.897 123.348 1.742 123.576 1.441 c
+123.811 1.147 123.932 0.684 123.942 0.059 c
+123.942 -0.367 l
+121.723 -0.367 l
+121.723 -0.455 l
+121.723 -0.889 121.8 -1.201 121.958 -1.396 c
+122.124 -1.583 122.355 -1.675 122.649 -1.675 c
+122.844 -1.675 123.017 -1.643 123.164 -1.573 c
+123.31 -1.496 123.447 -1.378 123.576 -1.22 c
+123.913 -1.631 l
+123.626 -2.036 123.196 -2.234 122.62 -2.234 c
+122.546 1.338 m
+122.271 1.338 122.068 1.243 121.943 1.058 c
+121.815 0.871 121.742 0.58 121.723 0.191 c
+123.296 0.191 l
+123.296 0.279 l
+123.274 0.661 123.208 0.93 123.09 1.088 c
+122.973 1.253 122.789 1.338 122.546 1.338 c
+126.368 1.206 m
+126.28 1.224 126.18 1.235 126.074 1.235 c
+125.74 1.235 125.505 1.051 125.369 0.691 c
+125.369 -2.161 l
+124.721 -2.161 l
+124.721 1.823 l
+125.354 1.823 l
+125.369 1.411 l
+125.544 1.735 125.787 1.897 126.103 1.897 c
+126.211 1.897 126.298 1.874 126.368 1.837 c
+h
+126.809 0.015 m
+126.809 0.632 126.919 1.095 127.148 1.411 c
+127.371 1.735 127.705 1.897 128.147 1.897 c
+128.547 1.897 128.852 1.72 129.058 1.367 c
+129.102 1.823 l
+129.69 1.823 l
+129.69 -2.204 l
+129.69 -2.693 129.561 -3.072 129.307 -3.337 c
+129.051 -3.601 128.698 -3.733 128.249 -3.733 c
+128.052 -3.733 127.831 -3.682 127.588 -3.586 c
+127.342 -3.487 127.162 -3.366 127.044 -3.219 c
+127.308 -2.778 l
+127.574 -3.042 127.871 -3.175 128.206 -3.175 c
+128.742 -3.175 129.018 -2.881 129.029 -2.293 c
+129.029 -1.764 l
+128.823 -2.08 128.522 -2.234 128.132 -2.234 c
+127.72 -2.234 127.397 -2.084 127.162 -1.778 c
+126.934 -1.466 126.816 -1.014 126.809 -0.426 c
+h
+127.47 -0.367 m
+127.47 -0.808 127.533 -1.139 127.661 -1.352 c
+127.786 -1.558 128.004 -1.66 128.308 -1.66 c
+128.632 -1.66 128.871 -1.496 129.029 -1.161 c
+129.029 0.823 l
+128.86 1.147 128.621 1.309 128.308 1.309 c
+128.014 1.309 127.798 1.206 127.661 1 c
+127.533 0.794 127.47 0.47 127.47 0.03 c
+h
+132.071 -2.234 m
+131.572 -2.234 131.189 -2.087 130.925 -1.793 c
+130.66 -1.499 130.528 -1.066 130.528 -0.484 c
+130.528 -0.014 l
+130.528 0.58 130.653 1.048 130.911 1.382 c
+131.175 1.723 131.535 1.897 131.998 1.897 c
+132.457 1.897 132.799 1.742 133.027 1.441 c
+133.262 1.147 133.383 0.684 133.394 0.059 c
+133.394 -0.367 l
+131.175 -0.367 l
+131.175 -0.455 l
+131.175 -0.889 131.252 -1.201 131.41 -1.396 c
+131.576 -1.583 131.807 -1.675 132.101 -1.675 c
+132.295 -1.675 132.468 -1.643 132.615 -1.573 c
+132.762 -1.496 132.898 -1.378 133.027 -1.22 c
+133.365 -1.631 l
+133.078 -2.036 132.648 -2.234 132.071 -2.234 c
+131.998 1.338 m
+131.722 1.338 131.52 1.243 131.395 1.058 c
+131.266 0.871 131.193 0.58 131.175 0.191 c
+132.748 0.191 l
+132.748 0.279 l
+132.725 0.661 132.659 0.93 132.542 1.088 c
+132.424 1.253 132.241 1.338 131.998 1.338 c
+134.041 0.015 m
+134.041 0.621 134.151 1.088 134.379 1.411 c
+134.614 1.735 134.942 1.897 135.364 1.897 c
+135.746 1.897 136.044 1.738 136.26 1.426 c
+136.26 3.484 l
+136.908 3.484 l
+136.908 -2.161 l
+136.32 -2.161 l
+136.276 -1.734 l
+136.07 -2.069 135.765 -2.234 135.364 -2.234 c
+134.952 -2.234 134.629 -2.08 134.394 -1.764 c
+134.159 -1.44 134.041 -0.985 134.041 -0.397 c
+h
+134.688 -0.367 m
+134.688 -0.808 134.751 -1.139 134.879 -1.352 c
+135.015 -1.558 135.235 -1.66 135.54 -1.66 c
+135.863 -1.66 136.102 -1.499 136.26 -1.176 c
+136.26 0.838 l
+136.091 1.151 135.853 1.309 135.54 1.309 c
+135.235 1.309 135.015 1.206 134.879 1 c
+134.751 0.794 134.688 0.47 134.688 0.03 c
+h
+140.332 -2.161 -0.646 3.984 re
+140.376 2.866 m
+140.376 2.756 140.347 2.664 140.288 2.587 c
+140.229 2.517 140.133 2.484 140.008 2.484 c
+139.891 2.484 139.796 2.517 139.73 2.587 c
+139.671 2.664 139.642 2.756 139.642 2.866 c
+139.642 2.984 139.671 3.076 139.73 3.146 c
+139.796 3.223 139.891 3.263 140.008 3.263 c
+140.133 3.263 140.229 3.223 140.288 3.146 c
+140.347 3.065 140.376 2.973 140.376 2.866 c
+141.964 1.823 m
+141.978 1.382 l
+142.232 1.723 142.555 1.897 142.948 1.897 c
+143.654 1.897 144.01 1.426 144.022 0.485 c
+144.022 -2.161 l
+143.375 -2.161 l
+143.375 0.456 l
+143.375 0.769 143.32 0.989 143.214 1.118 c
+143.103 1.243 142.948 1.309 142.743 1.309 c
+142.585 1.309 142.438 1.253 142.302 1.147 c
+142.173 1.037 142.07 0.9 141.993 0.736 c
+141.993 -2.161 l
+141.346 -2.161 l
+141.346 1.823 l
+h
+145.845 2.778 m
+145.845 1.823 l
+146.447 1.823 l
+146.447 1.294 l
+145.845 1.294 l
+145.845 -1.176 l
+145.845 -1.334 145.866 -1.452 145.918 -1.529 c
+145.976 -1.61 146.065 -1.646 146.182 -1.646 c
+146.271 -1.646 146.358 -1.631 146.447 -1.602 c
+146.447 -2.161 l
+146.3 -2.208 146.146 -2.234 145.992 -2.234 c
+145.734 -2.234 145.539 -2.142 145.404 -1.955 c
+145.263 -1.771 145.198 -1.51 145.198 -1.176 c
+145.198 1.294 l
+144.594 1.294 l
+144.594 1.823 l
+145.198 1.823 l
+145.198 2.778 l
+h
+147.006 0.015 m
+147.006 0.592 147.142 1.048 147.417 1.382 c
+147.7 1.723 148.071 1.897 148.534 1.897 c
+148.993 1.897 149.361 1.727 149.637 1.397 c
+149.92 1.073 150.067 0.625 150.078 0.059 c
+150.078 -0.367 l
+150.078 -0.937 149.934 -1.392 149.651 -1.734 c
+149.375 -2.069 149.008 -2.234 148.549 -2.234 c
+148.086 -2.234 147.715 -2.072 147.432 -1.749 c
+147.156 -1.419 147.013 -0.977 147.006 -0.426 c
+h
+147.652 -0.367 m
+147.652 -0.771 147.729 -1.087 147.887 -1.323 c
+148.053 -1.558 148.274 -1.675 148.549 -1.675 c
+149.115 -1.675 149.409 -1.263 149.431 -0.44 c
+149.431 0.015 l
+149.431 0.416 149.346 0.736 149.181 0.971 c
+149.023 1.213 148.806 1.338 148.534 1.338 c
+148.27 1.338 148.053 1.213 147.887 0.971 c
+147.729 0.736 147.652 0.416 147.652 0.015 c
+h
+154.678 -2.161 m
+154.638 -2.072 154.612 -1.926 154.605 -1.72 c
+154.37 -2.065 154.076 -2.234 153.723 -2.234 c
+153.359 -2.234 153.076 -2.138 152.87 -1.94 c
+152.672 -1.734 152.577 -1.448 152.577 -1.072 c
+152.577 -0.673 152.712 -0.353 152.988 -0.118 c
+153.26 0.125 153.635 0.25 154.105 0.25 c
+154.59 0.25 l
+154.59 0.676 l
+154.59 0.912 154.535 1.077 154.429 1.176 c
+154.318 1.283 154.157 1.338 153.944 1.338 c
+153.745 1.338 153.583 1.279 153.458 1.162 c
+153.341 1.044 153.282 0.897 153.282 0.721 c
+152.635 0.721 l
+152.635 0.915 152.694 1.106 152.812 1.294 c
+152.937 1.478 153.099 1.625 153.296 1.735 c
+153.502 1.841 153.73 1.897 153.988 1.897 c
+154.389 1.897 154.693 1.794 154.899 1.588 c
+155.112 1.382 155.226 1.088 155.237 0.706 c
+155.237 -1.308 l
+155.237 -1.613 155.274 -1.878 155.355 -2.102 c
+155.355 -2.161 l
+h
+153.811 -1.646 m
+153.977 -1.646 154.127 -1.602 154.267 -1.514 c
+154.414 -1.425 154.52 -1.315 154.59 -1.176 c
+154.59 -0.235 l
+154.223 -0.235 l
+153.907 -0.235 153.664 -0.305 153.488 -0.44 c
+153.311 -0.569 153.223 -0.756 153.223 -0.999 c
+153.223 -1.227 153.267 -1.392 153.356 -1.499 c
+153.444 -1.598 153.595 -1.646 153.811 -1.646 c
+156.854 1.823 m
+156.868 1.382 l
+157.122 1.723 157.446 1.897 157.839 1.897 c
+158.545 1.897 158.901 1.426 158.912 0.485 c
+158.912 -2.161 l
+158.265 -2.161 l
+158.265 0.456 l
+158.265 0.769 158.21 0.989 158.104 1.118 c
+157.993 1.243 157.839 1.309 157.633 1.309 c
+157.475 1.309 157.328 1.253 157.192 1.147 c
+157.063 1.037 156.961 0.9 156.883 0.736 c
+156.883 -2.161 l
+156.236 -2.161 l
+156.236 1.823 l
+h
+160.97 -1.072 m
+161.69 1.823 l
+162.381 1.823 l
+161.088 -2.719 l
+160.988 -3.061 160.845 -3.322 160.661 -3.498 c
+160.484 -3.675 160.282 -3.763 160.058 -3.763 c
+159.97 -3.763 159.856 -3.74 159.721 -3.704 c
+159.721 -3.16 l
+159.867 -3.175 l
+160.051 -3.175 160.198 -3.131 160.309 -3.042 c
+160.415 -2.954 160.503 -2.796 160.573 -2.572 c
+160.691 -2.131 l
+159.529 1.823 l
+160.234 1.823 l
+h
+164.6 0.015 m
+164.6 0.592 164.737 1.048 165.012 1.382 c
+165.295 1.723 165.666 1.897 166.129 1.897 c
+166.588 1.897 166.956 1.727 167.232 1.397 c
+167.515 1.073 167.661 0.625 167.673 0.059 c
+167.673 -0.367 l
+167.673 -0.937 167.529 -1.392 167.247 -1.734 c
+166.971 -2.069 166.603 -2.234 166.144 -2.234 c
+165.681 -2.234 165.31 -2.072 165.026 -1.749 c
+164.751 -1.419 164.608 -0.977 164.6 -0.426 c
+h
+165.247 -0.367 m
+165.247 -0.771 165.325 -1.087 165.483 -1.323 c
+165.647 -1.558 165.868 -1.675 166.144 -1.675 c
+166.709 -1.675 167.004 -1.263 167.025 -0.44 c
+167.025 0.015 l
+167.025 0.416 166.941 0.736 166.776 0.971 c
+166.618 1.213 166.401 1.338 166.129 1.338 c
+165.865 1.338 165.647 1.213 165.483 0.971 c
+165.325 0.736 165.247 0.416 165.247 0.015 c
+h
+169.363 2.778 m
+169.363 1.823 l
+169.965 1.823 l
+169.965 1.294 l
+169.363 1.294 l
+169.363 -1.176 l
+169.363 -1.334 169.385 -1.452 169.437 -1.529 c
+169.495 -1.61 169.583 -1.646 169.701 -1.646 c
+169.789 -1.646 169.877 -1.631 169.965 -1.602 c
+169.965 -2.161 l
+169.819 -2.208 169.664 -2.234 169.51 -2.234 c
+169.252 -2.234 169.057 -2.142 168.922 -1.955 c
+168.782 -1.771 168.716 -1.51 168.716 -1.176 c
+168.716 1.294 l
+168.113 1.294 l
+168.113 1.823 l
+168.716 1.823 l
+168.716 2.778 l
+h
+171.376 1.411 m
+171.63 1.735 171.95 1.897 172.332 1.897 c
+173.038 1.897 173.394 1.426 173.405 0.485 c
+173.405 -2.161 l
+172.758 -2.161 l
+172.758 0.456 l
+172.758 0.769 172.703 0.989 172.596 1.118 c
+172.487 1.243 172.332 1.309 172.126 1.309 c
+171.968 1.309 171.821 1.253 171.685 1.147 c
+171.557 1.037 171.454 0.9 171.376 0.736 c
+171.376 -2.161 l
+170.73 -2.161 l
+170.73 3.484 l
+171.376 3.484 l
+h
+175.787 -2.234 m
+175.286 -2.234 174.904 -2.087 174.64 -1.793 c
+174.375 -1.499 174.243 -1.066 174.243 -0.484 c
+174.243 -0.014 l
+174.243 0.58 174.368 1.048 174.625 1.382 c
+174.89 1.723 175.249 1.897 175.713 1.897 c
+176.172 1.897 176.514 1.742 176.741 1.441 c
+176.977 1.147 177.098 0.684 177.109 0.059 c
+177.109 -0.367 l
+174.89 -0.367 l
+174.89 -0.455 l
+174.89 -0.889 174.967 -1.201 175.125 -1.396 c
+175.29 -1.583 175.521 -1.675 175.816 -1.675 c
+176.01 -1.675 176.184 -1.643 176.33 -1.573 c
+176.477 -1.496 176.613 -1.378 176.741 -1.22 c
+177.08 -1.631 l
+176.793 -2.036 176.363 -2.234 175.787 -2.234 c
+175.713 1.338 m
+175.438 1.338 175.235 1.243 175.11 1.058 c
+174.981 0.871 174.908 0.58 174.89 0.191 c
+176.462 0.191 l
+176.462 0.279 l
+176.44 0.661 176.375 0.93 176.257 1.088 c
+176.139 1.253 175.955 1.338 175.713 1.338 c
+179.534 1.206 m
+179.446 1.224 179.347 1.235 179.241 1.235 c
+178.906 1.235 178.671 1.051 178.535 0.691 c
+178.535 -2.161 l
+177.888 -2.161 l
+177.888 1.823 l
+178.52 1.823 l
+178.535 1.411 l
+178.711 1.735 178.954 1.897 179.27 1.897 c
+179.376 1.897 179.465 1.874 179.534 1.837 c
+h
+179.77 -1.808 m
+179.77 -1.691 179.803 -1.595 179.872 -1.514 c
+179.939 -1.437 180.042 -1.396 180.182 -1.396 c
+180.328 -1.396 180.435 -1.437 180.504 -1.514 c
+180.582 -1.595 180.622 -1.691 180.622 -1.808 c
+180.622 -1.918 180.582 -2.009 180.504 -2.087 c
+180.435 -2.165 180.328 -2.204 180.182 -2.204 c
+180.042 -2.204 179.939 -2.165 179.872 -2.087 c
+179.803 -2.009 179.77 -1.918 179.77 -1.808 c
+f
+Q
+730.676 99.115 -1.794 0.867 re
+731.587 97.247 m
+731.587 102.598 l
+733.013 102.598 l
+733.63 102.598 734.123 102.396 734.497 101.995 c
+734.869 101.602 735.06 101.062 735.071 100.379 c
+735.071 99.512 l
+735.071 98.806 734.883 98.251 734.512 97.85 c
+734.137 97.446 733.63 97.247 732.984 97.247 c
+h
+732.675 101.702 m
+732.675 98.145 l
+732.998 98.145 l
+733.358 98.145 733.616 98.236 733.763 98.423 c
+733.909 98.618 733.983 98.945 733.983 99.408 c
+733.983 100.349 l
+733.983 100.849 733.913 101.195 733.778 101.393 c
+733.637 101.588 733.402 101.69 733.072 101.702 c
+h
+f
+738.32 97.247 -0.647 3.984 re
+738.364 102.275 m
+738.364 102.164 738.334 102.072 738.275 101.995 c
+738.216 101.926 738.121 101.893 737.996 101.893 c
+737.878 101.893 737.782 101.926 737.716 101.995 c
+737.658 102.072 737.628 102.164 737.628 102.275 c
+737.628 102.392 737.658 102.484 737.716 102.554 c
+737.782 102.631 737.878 102.672 737.996 102.672 c
+738.121 102.672 738.216 102.631 738.275 102.554 c
+738.334 102.473 738.364 102.381 738.364 102.275 c
+739.951 101.231 m
+739.966 100.79 l
+740.219 101.132 740.542 101.305 740.936 101.305 c
+741.641 101.305 741.998 100.834 742.009 99.894 c
+742.009 97.247 l
+741.362 97.247 l
+741.362 99.864 l
+741.362 100.177 741.307 100.397 741.2 100.526 c
+741.09 100.651 740.936 100.717 740.73 100.717 c
+740.572 100.717 740.425 100.661 740.288 100.555 c
+740.161 100.445 740.057 100.308 739.98 100.144 c
+739.98 97.247 l
+739.334 97.247 l
+739.334 101.231 l
+h
+744.919 98.262 m
+744.919 98.409 744.864 98.53 744.757 98.629 c
+744.647 98.725 744.442 98.843 744.14 98.982 c
+743.795 99.129 743.552 99.25 743.405 99.35 c
+743.258 99.456 743.148 99.574 743.081 99.703 c
+743.012 99.828 742.979 99.986 742.979 100.173 c
+742.979 100.497 743.097 100.765 743.332 100.981 c
+743.567 101.195 743.868 101.305 744.243 101.305 c
+744.625 101.305 744.934 101.191 745.169 100.967 c
+745.404 100.738 745.522 100.452 745.522 100.1 c
+744.875 100.1 l
+744.875 100.275 744.816 100.426 744.699 100.555 c
+744.581 100.68 744.427 100.746 744.243 100.746 c
+744.045 100.746 743.894 100.691 743.787 100.584 c
+743.677 100.485 743.625 100.353 743.625 100.187 c
+743.625 100.059 743.662 99.952 743.743 99.864 c
+743.82 99.783 744.011 99.68 744.317 99.556 c
+744.795 99.368 745.125 99.18 745.302 98.997 c
+745.478 98.82 745.566 98.592 745.566 98.321 c
+745.566 97.968 745.441 97.688 745.198 97.482 c
+744.963 97.277 744.647 97.174 744.257 97.174 c
+743.835 97.174 743.497 97.291 743.243 97.527 c
+742.987 97.769 742.861 98.074 742.861 98.438 c
+743.508 98.438 l
+743.515 98.211 743.585 98.034 743.714 97.91 c
+743.839 97.792 744.022 97.733 744.257 97.733 c
+744.471 97.733 744.633 97.781 744.743 97.879 c
+744.86 97.975 744.919 98.104 744.919 98.262 c
+747.257 102.186 m
+747.257 101.231 l
+747.859 101.231 l
+747.859 100.702 l
+747.257 100.702 l
+747.257 98.232 l
+747.257 98.074 747.278 97.957 747.33 97.879 c
+747.388 97.799 747.477 97.762 747.594 97.762 c
+747.683 97.762 747.771 97.777 747.859 97.806 c
+747.859 97.247 l
+747.712 97.2 747.557 97.174 747.403 97.174 c
+747.146 97.174 746.952 97.266 746.815 97.453 c
+746.676 97.637 746.609 97.898 746.609 98.232 c
+746.609 100.702 l
+746.007 100.702 l
+746.007 101.231 l
+746.609 101.231 l
+746.609 102.186 l
+h
+750.049 97.174 m
+749.549 97.174 749.167 97.322 748.903 97.615 c
+748.638 97.91 748.506 98.342 748.506 98.924 c
+748.506 99.394 l
+748.506 99.989 748.631 100.456 748.888 100.79 c
+749.152 101.132 749.512 101.305 749.975 101.305 c
+750.435 101.305 750.777 101.15 751.004 100.849 c
+751.24 100.555 751.361 100.092 751.372 99.467 c
+751.372 99.041 l
+749.152 99.041 l
+749.152 98.953 l
+749.152 98.519 749.229 98.207 749.387 98.012 c
+749.553 97.825 749.784 97.733 750.079 97.733 c
+750.273 97.733 750.446 97.766 750.593 97.835 c
+750.74 97.912 750.876 98.03 751.004 98.188 c
+751.342 97.777 l
+751.056 97.372 750.626 97.174 750.049 97.174 c
+749.975 100.746 m
+749.7 100.746 749.498 100.651 749.373 100.466 c
+749.244 100.279 749.171 99.989 749.152 99.599 c
+750.725 99.599 l
+750.725 99.688 l
+750.703 100.069 750.637 100.339 750.519 100.497 c
+750.401 100.661 750.218 100.746 749.975 100.746 c
+754.194 97.247 m
+754.154 97.336 754.128 97.482 754.121 97.688 c
+753.886 97.343 753.591 97.174 753.239 97.174 c
+752.875 97.174 752.592 97.27 752.386 97.468 c
+752.188 97.674 752.092 97.96 752.092 98.336 c
+752.092 98.735 752.228 99.055 752.504 99.291 c
+752.776 99.533 753.15 99.658 753.621 99.658 c
+754.106 99.658 l
+754.106 100.085 l
+754.106 100.32 754.051 100.485 753.944 100.584 c
+753.834 100.691 753.672 100.746 753.459 100.746 c
+753.261 100.746 753.099 100.688 752.974 100.57 c
+752.857 100.452 752.797 100.305 752.797 100.129 c
+752.151 100.129 l
+752.151 100.323 752.209 100.514 752.327 100.702 c
+752.452 100.886 752.614 101.033 752.813 101.143 c
+753.019 101.249 753.246 101.305 753.503 101.305 c
+753.904 101.305 754.209 101.202 754.415 100.996 c
+754.628 100.79 754.742 100.497 754.752 100.114 c
+754.752 98.1 l
+754.752 97.795 754.79 97.53 754.87 97.307 c
+754.87 97.247 l
+h
+753.327 97.762 m
+753.493 97.762 753.643 97.806 753.782 97.894 c
+753.929 97.983 754.036 98.093 754.106 98.232 c
+754.106 99.173 l
+753.738 99.173 l
+753.422 99.173 753.18 99.103 753.003 98.968 c
+752.828 98.839 752.739 98.652 752.739 98.409 c
+752.739 98.181 752.783 98.016 752.871 97.91 c
+752.959 97.81 753.11 97.762 753.327 97.762 c
+755.62 99.423 m
+755.62 100.029 755.731 100.497 755.958 100.819 c
+756.194 101.143 756.52 101.305 756.943 101.305 c
+757.325 101.305 757.622 101.147 757.84 100.834 c
+757.84 102.892 l
+758.486 102.892 l
+758.486 97.247 l
+757.898 97.247 l
+757.854 97.674 l
+757.649 97.339 757.343 97.174 756.943 97.174 c
+756.531 97.174 756.208 97.328 755.972 97.644 c
+755.737 97.968 755.62 98.423 755.62 99.011 c
+h
+756.267 99.041 m
+756.267 98.6 756.329 98.269 756.458 98.056 c
+756.594 97.85 756.814 97.748 757.119 97.748 c
+757.443 97.748 757.682 97.91 757.84 98.232 c
+757.84 100.246 l
+757.67 100.559 757.432 100.717 757.119 100.717 c
+756.814 100.717 756.594 100.614 756.458 100.408 c
+756.329 100.202 756.267 99.878 756.267 99.438 c
+h
+761.073 99.423 m
+761.073 100 761.21 100.456 761.485 100.79 c
+761.768 101.132 762.139 101.305 762.602 101.305 c
+763.061 101.305 763.429 101.135 763.704 100.805 c
+763.988 100.482 764.134 100.033 764.146 99.467 c
+764.146 99.041 l
+764.146 98.471 764.002 98.016 763.719 97.674 c
+763.444 97.339 763.076 97.174 762.617 97.174 c
+762.154 97.174 761.782 97.336 761.499 97.659 c
+761.224 97.989 761.081 98.431 761.073 98.982 c
+h
+761.72 99.041 m
+761.72 98.637 761.797 98.321 761.956 98.085 c
+762.12 97.85 762.341 97.733 762.617 97.733 c
+763.182 97.733 763.477 98.145 763.498 98.968 c
+763.498 99.423 l
+763.498 99.824 763.415 100.144 763.249 100.379 c
+763.091 100.621 762.874 100.746 762.602 100.746 c
+762.338 100.746 762.12 100.621 761.956 100.379 c
+761.797 100.144 761.72 99.824 761.72 99.423 c
+h
+765.277 97.247 m
+765.277 100.702 l
+764.748 100.702 l
+764.748 101.231 l
+765.277 101.231 l
+765.277 101.687 l
+765.277 102.087 765.373 102.4 765.571 102.627 c
+765.777 102.851 766.056 102.966 766.409 102.966 c
+766.545 102.966 766.677 102.943 766.806 102.907 c
+766.777 102.363 l
+766.677 102.381 766.578 102.392 766.482 102.392 c
+766.108 102.392 765.924 102.128 765.924 101.598 c
+765.924 101.231 l
+766.6 101.231 l
+766.6 100.702 l
+765.924 100.702 l
+765.924 97.247 l
+h
+f
+714.551 89.707 -1.793 0.867 re
+715.271 89.957 m
+715.271 90.604 715.378 91.089 715.594 91.412 c
+715.819 91.735 716.142 91.897 716.565 91.897 c
+716.877 91.897 717.13 91.765 717.329 91.5 c
+717.329 93.484 l
+718.387 93.484 l
+718.387 87.84 l
+717.432 87.84 l
+717.388 88.252 l
+717.171 87.928 716.895 87.766 716.565 87.766 c
+716.153 87.766 715.833 87.921 715.609 88.237 c
+715.393 88.56 715.279 89.031 715.271 89.648 c
+h
+716.315 89.692 m
+716.315 89.299 716.351 89.023 716.432 88.869 c
+716.521 88.711 716.667 88.634 716.873 88.634 c
+717.079 88.634 717.23 88.726 717.329 88.913 c
+717.329 90.721 l
+717.23 90.916 717.079 91.015 716.873 91.015 c
+716.675 91.015 716.535 90.934 716.447 90.78 c
+716.359 90.633 716.315 90.361 716.315 89.972 c
+h
+f
+q 1 0 0 1 721.2686 87.8399 cm
+0 0 m
+0 3.454 l
+-0.53 3.454 l
+-0.53 3.984 l
+0 3.984 l
+0 4.439 l
+0 4.84 0.095 5.152 0.293 5.38 c
+0.5 5.604 0.779 5.719 1.132 5.719 c
+1.267 5.719 1.4 5.696 1.529 5.659 c
+1.499 5.116 l
+1.4 5.134 1.301 5.145 1.205 5.145 c
+0.831 5.145 0.646 4.881 0.646 4.351 c
+0.646 3.984 l
+1.323 3.984 l
+1.323 3.454 l
+0.646 3.454 l
+0.646 0 l
+h
+1.955 2.176 m
+1.955 2.753 2.09 3.209 2.366 3.543 c
+2.649 3.884 3.021 4.057 3.484 4.057 c
+3.943 4.057 4.31 3.888 4.586 3.558 c
+4.868 3.234 5.016 2.786 5.026 2.22 c
+5.026 1.794 l
+5.026 1.224 4.883 0.769 4.6 0.427 c
+4.325 0.092 3.958 -0.073 3.498 -0.073 c
+3.035 -0.073 2.664 0.088 2.381 0.412 c
+2.105 0.742 1.962 1.183 1.955 1.735 c
+h
+2.601 1.794 m
+2.601 1.389 2.678 1.073 2.836 0.838 c
+3.002 0.603 3.222 0.485 3.498 0.485 c
+4.064 0.485 4.358 0.897 4.38 1.721 c
+4.38 2.176 l
+4.38 2.577 4.295 2.897 4.13 3.132 c
+3.972 3.373 3.755 3.499 3.484 3.499 c
+3.219 3.499 3.002 3.373 2.836 3.132 c
+2.678 2.897 2.601 2.577 2.601 2.176 c
+h
+7.511 3.367 m
+7.422 3.385 7.324 3.396 7.217 3.396 c
+6.882 3.396 6.647 3.212 6.512 2.852 c
+6.512 0 l
+5.865 0 l
+5.865 3.984 l
+6.497 3.984 l
+6.512 3.572 l
+6.688 3.896 6.93 4.057 7.247 4.057 c
+7.353 4.057 7.441 4.035 7.511 3.998 c
+h
+9.466 0.485 m
+9.679 0.485 9.851 0.548 9.98 0.676 c
+10.117 0.813 10.19 1.004 10.2 1.25 c
+10.818 1.25 l
+10.796 0.867 10.66 0.548 10.406 0.294 c
+10.15 0.048 9.837 -0.073 9.466 -0.073 c
+8.974 -0.073 8.598 0.077 8.334 0.383 c
+8.077 0.695 7.952 1.162 7.952 1.779 c
+7.952 2.22 l
+7.952 2.816 8.077 3.271 8.334 3.587 c
+8.598 3.899 8.974 4.057 9.466 4.057 c
+9.866 4.057 10.186 3.925 10.422 3.66 c
+10.664 3.404 10.796 3.057 10.818 2.617 c
+10.2 2.617 l
+10.179 2.911 10.105 3.132 9.98 3.278 c
+9.863 3.425 9.69 3.499 9.466 3.499 c
+9.172 3.499 8.955 3.4 8.819 3.205 c
+8.679 3.017 8.606 2.708 8.598 2.278 c
+8.598 1.764 l
+8.598 1.294 8.665 0.96 8.804 0.765 c
+8.951 0.578 9.172 0.485 9.466 0.485 c
+12.993 -0.073 m
+12.494 -0.073 12.112 0.074 11.847 0.368 c
+11.582 0.662 11.451 1.095 11.451 1.676 c
+11.451 2.147 l
+11.451 2.741 11.575 3.209 11.833 3.543 c
+12.097 3.884 12.457 4.057 12.92 4.057 c
+13.379 4.057 13.722 3.903 13.949 3.602 c
+14.184 3.308 14.306 2.845 14.316 2.22 c
+14.316 1.794 l
+12.097 1.794 l
+12.097 1.706 l
+12.097 1.272 12.174 0.96 12.332 0.765 c
+12.498 0.578 12.729 0.485 13.023 0.485 c
+13.218 0.485 13.39 0.518 13.537 0.588 c
+13.685 0.665 13.82 0.783 13.949 0.941 c
+14.287 0.53 l
+14.001 0.125 13.57 -0.073 12.993 -0.073 c
+12.92 3.499 m
+12.644 3.499 12.442 3.404 12.317 3.219 c
+12.189 3.032 12.116 2.741 12.097 2.352 c
+13.67 2.352 l
+13.67 2.44 l
+13.648 2.822 13.581 3.091 13.464 3.249 c
+13.346 3.414 13.163 3.499 12.92 3.499 c
+17.036 1.015 m
+17.036 1.162 16.981 1.283 16.874 1.382 c
+16.764 1.478 16.558 1.595 16.257 1.735 c
+15.912 1.881 15.669 2.003 15.522 2.103 c
+15.374 2.209 15.264 2.326 15.199 2.455 c
+15.129 2.58 15.096 2.738 15.096 2.926 c
+15.096 3.249 15.214 3.517 15.449 3.734 c
+15.684 3.947 15.985 4.057 16.359 4.057 c
+16.742 4.057 17.051 3.944 17.286 3.72 c
+17.521 3.491 17.639 3.205 17.639 2.852 c
+16.992 2.852 l
+16.992 3.028 16.933 3.179 16.816 3.308 c
+16.698 3.433 16.544 3.499 16.359 3.499 c
+16.161 3.499 16.01 3.444 15.904 3.337 c
+15.794 3.238 15.742 3.105 15.742 2.94 c
+15.742 2.812 15.779 2.705 15.86 2.617 c
+15.937 2.536 16.128 2.433 16.433 2.309 c
+16.911 2.12 17.242 1.933 17.418 1.75 c
+17.595 1.573 17.683 1.345 17.683 1.073 c
+17.683 0.721 17.558 0.441 17.315 0.235 c
+17.08 0.03 16.764 -0.073 16.375 -0.073 c
+15.952 -0.073 15.613 0.044 15.36 0.279 c
+15.103 0.522 14.978 0.827 14.978 1.191 c
+15.625 1.191 l
+15.632 0.963 15.702 0.786 15.831 0.662 c
+15.956 0.545 16.139 0.485 16.375 0.485 c
+16.588 0.485 16.749 0.533 16.86 0.632 c
+16.978 0.728 17.036 0.857 17.036 1.015 c
+20.108 2.176 m
+20.108 2.782 20.218 3.249 20.446 3.572 c
+20.681 3.896 21.009 4.057 21.431 4.057 c
+21.813 4.057 22.111 3.899 22.327 3.587 c
+22.327 5.644 l
+22.975 5.644 l
+22.975 0 l
+22.387 0 l
+22.343 0.427 l
+22.137 0.092 21.832 -0.073 21.431 -0.073 c
+21.019 -0.073 20.696 0.081 20.461 0.397 c
+20.226 0.721 20.108 1.176 20.108 1.764 c
+h
+20.755 1.794 m
+20.755 1.353 20.818 1.022 20.945 0.809 c
+21.082 0.603 21.302 0.5 21.607 0.5 c
+21.93 0.5 22.169 0.662 22.327 0.985 c
+22.327 2.999 l
+22.158 3.311 21.92 3.469 21.607 3.469 c
+21.302 3.469 21.082 3.367 20.945 3.161 c
+20.818 2.955 20.755 2.631 20.755 2.191 c
+h
+25.4 -0.073 m
+24.899 -0.073 24.518 0.074 24.253 0.368 c
+23.989 0.662 23.856 1.095 23.856 1.676 c
+23.856 2.147 l
+23.856 2.741 23.981 3.209 24.238 3.543 c
+24.503 3.884 24.863 4.057 25.327 4.057 c
+25.786 4.057 26.127 3.903 26.355 3.602 c
+26.59 3.308 26.711 2.845 26.723 2.22 c
+26.723 1.794 l
+24.503 1.794 l
+24.503 1.706 l
+24.503 1.272 24.581 0.96 24.739 0.765 c
+24.903 0.578 25.135 0.485 25.429 0.485 c
+25.624 0.485 25.797 0.518 25.944 0.588 c
+26.09 0.665 26.227 0.783 26.355 0.941 c
+26.693 0.53 l
+26.406 0.125 25.977 -0.073 25.4 -0.073 c
+25.327 3.499 m
+25.051 3.499 24.849 3.404 24.724 3.219 c
+24.595 3.032 24.521 2.741 24.503 2.352 c
+26.075 2.352 l
+26.075 2.44 l
+26.054 2.822 25.988 3.091 25.87 3.249 c
+25.753 3.414 25.568 3.499 25.327 3.499 c
+28.207 0 -0.646 5.644 re
+30.647 -0.073 m
+30.148 -0.073 29.765 0.074 29.501 0.368 c
+29.236 0.662 29.104 1.095 29.104 1.676 c
+29.104 2.147 l
+29.104 2.741 29.229 3.209 29.486 3.543 c
+29.751 3.884 30.111 4.057 30.574 4.057 c
+31.033 4.057 31.375 3.903 31.602 3.602 c
+31.837 3.308 31.959 2.845 31.97 2.22 c
+31.97 1.794 l
+29.751 1.794 l
+29.751 1.706 l
+29.751 1.272 29.828 0.96 29.986 0.765 c
+30.152 0.578 30.383 0.485 30.677 0.485 c
+30.871 0.485 31.044 0.518 31.191 0.588 c
+31.338 0.665 31.474 0.783 31.602 0.941 c
+31.941 0.53 l
+31.654 0.125 31.224 -0.073 30.647 -0.073 c
+30.574 3.499 m
+30.298 3.499 30.096 3.404 29.971 3.219 c
+29.842 3.032 29.769 2.741 29.751 2.352 c
+31.324 2.352 l
+31.324 2.44 l
+31.301 2.822 31.235 3.091 31.118 3.249 c
+31 3.414 30.817 3.499 30.574 3.499 c
+33.601 4.939 m
+33.601 3.984 l
+34.204 3.984 l
+34.204 3.454 l
+33.601 3.454 l
+33.601 0.985 l
+33.601 0.827 33.624 0.709 33.676 0.632 c
+33.734 0.551 33.822 0.515 33.94 0.515 c
+34.028 0.515 34.116 0.53 34.204 0.559 c
+34.204 0 l
+34.058 -0.047 33.903 -0.073 33.749 -0.073 c
+33.491 -0.073 33.297 0.019 33.161 0.206 c
+33.021 0.389 32.955 0.651 32.955 0.985 c
+32.955 3.454 l
+32.352 3.454 l
+32.352 3.984 l
+32.955 3.984 l
+32.955 4.939 l
+h
+35.675 0 -0.647 3.984 re
+35.718 5.027 m
+35.718 4.917 35.689 4.825 35.63 4.748 c
+35.571 4.678 35.476 4.645 35.351 4.645 c
+35.234 4.645 35.138 4.678 35.072 4.748 c
+35.012 4.825 34.983 4.917 34.983 5.027 c
+34.983 5.145 35.012 5.237 35.072 5.307 c
+35.138 5.384 35.234 5.424 35.351 5.424 c
+35.476 5.424 35.571 5.384 35.63 5.307 c
+35.689 5.226 35.718 5.134 35.718 5.027 c
+36.556 2.176 m
+36.556 2.753 36.693 3.209 36.968 3.543 c
+37.25 3.884 37.622 4.057 38.085 4.057 c
+38.544 4.057 38.912 3.888 39.187 3.558 c
+39.471 3.234 39.617 2.786 39.629 2.22 c
+39.629 1.794 l
+39.629 1.224 39.485 0.769 39.202 0.427 c
+38.927 0.092 38.559 -0.073 38.1 -0.073 c
+37.637 -0.073 37.266 0.088 36.982 0.412 c
+36.707 0.742 36.564 1.183 36.556 1.735 c
+h
+37.203 1.794 m
+37.203 1.389 37.281 1.073 37.439 0.838 c
+37.603 0.603 37.824 0.485 38.1 0.485 c
+38.665 0.485 38.96 0.897 38.981 1.721 c
+38.981 2.176 l
+38.981 2.577 38.897 2.897 38.732 3.132 c
+38.574 3.373 38.357 3.499 38.085 3.499 c
+37.821 3.499 37.603 3.373 37.439 3.132 c
+37.281 2.897 37.203 2.577 37.203 2.176 c
+h
+41.084 3.984 m
+41.098 3.543 l
+41.352 3.884 41.676 4.057 42.068 4.057 c
+42.773 4.057 43.13 3.587 43.141 2.646 c
+43.141 0 l
+42.495 0 l
+42.495 2.617 l
+42.495 2.929 42.44 3.15 42.333 3.278 c
+42.223 3.404 42.068 3.469 41.863 3.469 c
+41.705 3.469 41.557 3.414 41.422 3.308 c
+41.293 3.198 41.19 3.061 41.113 2.897 c
+41.113 0 l
+40.466 0 l
+40.466 3.984 l
+h
+44.155 0.353 m
+44.155 0.47 44.188 0.566 44.259 0.647 c
+44.325 0.724 44.427 0.765 44.568 0.765 c
+44.714 0.765 44.821 0.724 44.89 0.647 c
+44.968 0.566 45.008 0.47 45.008 0.353 c
+45.008 0.243 44.968 0.151 44.89 0.074 c
+44.821 -0.004 44.714 -0.043 44.568 -0.043 c
+44.427 -0.043 44.325 -0.004 44.259 0.074 c
+44.188 0.151 44.155 0.243 44.155 0.353 c
+f
+Q
+ endstream endobj 295 0 obj <</I true/K false/S/Transparency/Type/Group>> endobj 297 0 obj <</BBox[325.823 554.164 757.387 480.746]/Group 298 0 R/Length 128/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>/Shading<</Sh0 64 0 R>>>>/Subtype/Form>>stream
+q
+325.823 554.164 431.564 -73.418 re
+W n
+q
+0 g
+/GS0 gs
+431.5641174 0 0 431.5641174 325.8230896 517.4552612 cm
+BX /Sh0 sh EX Q
+Q
+ endstream endobj 298 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 296 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 299 0 R/Type/ExtGState/ca 1.0/op false>> endobj 299 0 obj <</BC 300 0 R/G 301 0 R/S/Luminosity/Type/Mask>> endobj 300 0 obj [0.0] endobj 301 0 obj <</BBox[325.823 554.164 757.387 480.746]/Group 302 0 R/Length 92/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 12 0 R>>/XObject<</Fm0 303 0 R>>>>/Subtype/Form>>stream
+0 g
+/GS0 gs
+325.823 554.164 431.564 -73.418 re
+f
+q
+0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 0 TL/Fm0 Do
+Q
+ endstream endobj 302 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 303 0 obj <</BBox[325.823 554.164 757.388 480.746]/Group 304 0 R/Length 128/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 113 0 R>>/ExtGState<</GS0 12 0 R>>/Shading<</Sh0 127 0 R>>>>/Subtype/Form>>stream
+q
+325.823 554.164 431.564 -73.418 re
+W n
+q
+0 g
+/GS0 gs
+431.5641174 0 0 431.5641174 325.8227539 517.4550781 cm
+BX /Sh0 sh EX Q
+Q
+ endstream endobj 304 0 obj <</CS/DeviceCMYK/I true/K false/S/Transparency/Type/Group>> endobj 6 0 obj [5 0 R] endobj 305 0 obj <</CreationDate(D:20161031172420Z)/Creator(Adobe Illustrator CC 2015.3 \(Macintosh\))/ModDate(D:20161031172420Z)/Producer(Adobe PDF library 15.00)/Title(Untitled-2)>> endobj xref 0 306 0000000000 65535 f
+0000000016 00000 n
+0000000144 00000 n
+0000052539 00000 n
+0000000000 00000 f
+0001829223 00000 n
+0007345734 00000 n
+0000052596 00000 n
+0000052949 00000 n
+0005664025 00000 n
+0005664166 00000 n
+0001829413 00000 n
+0001821855 00000 n
+0005665528 00000 n
+0005664232 00000 n
+0000053303 00000 n
+0000053445 00000 n
+0000055549 00000 n
+0000053511 00000 n
+0000054988 00000 n
+0000055036 00000 n
+0001577808 00000 n
+0001825132 00000 n
+0001825248 00000 n
+0001825363 00000 n
+0001825486 00000 n
+0001825609 00000 n
+0001825732 00000 n
+0001577870 00000 n
+0001578205 00000 n
+0001578681 00000 n
+0001584621 00000 n
+0001596870 00000 n
+0001602617 00000 n
+0001616838 00000 n
+0001627887 00000 n
+0001628196 00000 n
+0001637380 00000 n
+0001647988 00000 n
+0001657309 00000 n
+0001660863 00000 n
+0001666195 00000 n
+0001669141 00000 n
+0001670268 00000 n
+0001679870 00000 n
+0001684168 00000 n
+0001688506 00000 n
+0001700981 00000 n
+0001705017 00000 n
+0001710819 00000 n
+0001717241 00000 n
+0001731952 00000 n
+0001741049 00000 n
+0001758297 00000 n
+0001772218 00000 n
+0001777518 00000 n
+0001777876 00000 n
+0001779807 00000 n
+0001786388 00000 n
+0001792684 00000 n
+0001803132 00000 n
+0001809040 00000 n
+0001814611 00000 n
+0001824567 00000 n
+0001824630 00000 n
+0001824504 00000 n
+0001824441 00000 n
+0001824378 00000 n
+0001824315 00000 n
+0001824252 00000 n
+0001824189 00000 n
+0001824126 00000 n
+0001824063 00000 n
+0001824000 00000 n
+0001823937 00000 n
+0001823874 00000 n
+0001823811 00000 n
+0001823748 00000 n
+0001823685 00000 n
+0001823622 00000 n
+0001823559 00000 n
+0001823496 00000 n
+0001823433 00000 n
+0001823370 00000 n
+0001823307 00000 n
+0001823244 00000 n
+0001823181 00000 n
+0001823118 00000 n
+0001823055 00000 n
+0001822992 00000 n
+0001822929 00000 n
+0001822346 00000 n
+0001822553 00000 n
+0001822409 00000 n
+0001822283 00000 n
+0001822220 00000 n
+0001822157 00000 n
+0001822094 00000 n
+0001822031 00000 n
+0001821968 00000 n
+0001821791 00000 n
+0001822716 00000 n
+0001822854 00000 n
+0001824778 00000 n
+0001824894 00000 n
+0001825023 00000 n
+0001827884 00000 n
+0001825848 00000 n
+0001825914 00000 n
+0001825937 00000 n
+0001826264 00000 n
+0001826342 00000 n
+0001826705 00000 n
+0001826928 00000 n
+0001826782 00000 n
+0001826991 00000 n
+0001827358 00000 n
+0001827746 00000 n
+0001827129 00000 n
+0001827205 00000 n
+0001827282 00000 n
+0001827801 00000 n
+0001827950 00000 n
+0001827973 00000 n
+0001828275 00000 n
+0001828353 00000 n
+0001828719 00000 n
+0001828796 00000 n
+0001828942 00000 n
+0001829058 00000 n
+0001829138 00000 n
+0001829295 00000 n
+0001829327 00000 n
+0001829482 00000 n
+0001833966 00000 n
+0001835264 00000 n
+0001847598 00000 n
+0001877099 00000 n
+0001899615 00000 n
+0001922108 00000 n
+0001944924 00000 n
+0001967003 00000 n
+0001989331 00000 n
+0002012637 00000 n
+0002035364 00000 n
+0002056686 00000 n
+0002078809 00000 n
+0002101782 00000 n
+0002136210 00000 n
+0002158624 00000 n
+0002181711 00000 n
+0002204289 00000 n
+0002228492 00000 n
+0002250164 00000 n
+0002272697 00000 n
+0002294933 00000 n
+0002318562 00000 n
+0002339903 00000 n
+0002362514 00000 n
+0002426019 00000 n
+0002449289 00000 n
+0002471861 00000 n
+0002494425 00000 n
+0002516757 00000 n
+0002539012 00000 n
+0002561573 00000 n
+0002583980 00000 n
+0002607446 00000 n
+0002630408 00000 n
+0002653946 00000 n
+0002695995 00000 n
+0002718800 00000 n
+0002741052 00000 n
+0002763140 00000 n
+0002784387 00000 n
+0002806152 00000 n
+0002827702 00000 n
+0002849139 00000 n
+0002870613 00000 n
+0002891878 00000 n
+0002914033 00000 n
+0002956912 00000 n
+0002978248 00000 n
+0002999119 00000 n
+0003020747 00000 n
+0003042038 00000 n
+0003064132 00000 n
+0003086377 00000 n
+0003108223 00000 n
+0003130683 00000 n
+0003152299 00000 n
+0003170763 00000 n
+0003233578 00000 n
+0003255689 00000 n
+0003277269 00000 n
+0003299242 00000 n
+0003321203 00000 n
+0003342098 00000 n
+0003363411 00000 n
+0003386193 00000 n
+0003409378 00000 n
+0003432490 00000 n
+0003453518 00000 n
+0003495238 00000 n
+0003510301 00000 n
+0003567696 00000 n
+0003615863 00000 n
+0003650891 00000 n
+0003669632 00000 n
+0003673380 00000 n
+0003683855 00000 n
+0003704357 00000 n
+0003718623 00000 n
+0003733633 00000 n
+0003759731 00000 n
+0003784054 00000 n
+0003806935 00000 n
+0003829767 00000 n
+0003852352 00000 n
+0003876992 00000 n
+0003900151 00000 n
+0003922783 00000 n
+0003945034 00000 n
+0003968047 00000 n
+0003991391 00000 n
+0004015741 00000 n
+0004039233 00000 n
+0004062214 00000 n
+0004085294 00000 n
+0004107633 00000 n
+0004133573 00000 n
+0004156970 00000 n
+0004181160 00000 n
+0004205488 00000 n
+0004229069 00000 n
+0004252246 00000 n
+0004276651 00000 n
+0004300732 00000 n
+0004323009 00000 n
+0004346653 00000 n
+0004369083 00000 n
+0004398651 00000 n
+0004422686 00000 n
+0004445807 00000 n
+0004468467 00000 n
+0004490778 00000 n
+0004512245 00000 n
+0004535999 00000 n
+0004558483 00000 n
+0004582152 00000 n
+0004605460 00000 n
+0004628178 00000 n
+0004657082 00000 n
+0004679662 00000 n
+0004703617 00000 n
+0004726684 00000 n
+0004749769 00000 n
+0004772895 00000 n
+0004796243 00000 n
+0004818968 00000 n
+0004840801 00000 n
+0004864877 00000 n
+0004887603 00000 n
+0004916892 00000 n
+0004940435 00000 n
+0004962612 00000 n
+0004986831 00000 n
+0005010044 00000 n
+0005032928 00000 n
+0005056580 00000 n
+0005078840 00000 n
+0005102970 00000 n
+0005124736 00000 n
+0005148071 00000 n
+0005177059 00000 n
+0005200432 00000 n
+0005223606 00000 n
+0005246594 00000 n
+0005269811 00000 n
+0005292409 00000 n
+0005314358 00000 n
+0005337995 00000 n
+0005361090 00000 n
+0005383650 00000 n
+0005407255 00000 n
+0005436020 00000 n
+0005458512 00000 n
+0005482512 00000 n
+0005507899 00000 n
+0005530123 00000 n
+0005551770 00000 n
+0005574745 00000 n
+0005597605 00000 n
+0005619438 00000 n
+0005642322 00000 n
+0007344240 00000 n
+0007344705 00000 n
+0007344303 00000 n
+0007344641 00000 n
+0007344822 00000 n
+0007344888 00000 n
+0007344911 00000 n
+0007345213 00000 n
+0007345291 00000 n
+0007345657 00000 n
+0007345757 00000 n
+trailer <</Size 306/Root 1 0 R/Info 305 0 R/ID[<303169ECE1024C918EE9BAFAA71271A8><267E8E7C61CE467AA831715E34FC8183>]>> startxref 7345941 %%EOF \ No newline at end of file
diff --git a/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml b/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml
new file mode 100644
index 00000000000..0bab94a7c2e
--- /dev/null
+++ b/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml
@@ -0,0 +1,10 @@
+before_script:
+ - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
+ - ruby -v
+ - which ruby
+ - gem install bundler --no-ri --no-rdoc
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
+
+rspec:
+ script:
+ - bundle exec rspec
diff --git a/spec/fixtures/patchfiles/0001-A-commit-from-a-patch.patch b/spec/fixtures/patchfiles/0001-A-commit-from-a-patch.patch
new file mode 100644
index 00000000000..cc38682a0ab
--- /dev/null
+++ b/spec/fixtures/patchfiles/0001-A-commit-from-a-patch.patch
@@ -0,0 +1,19 @@
+From 3fee0042e610fb3563e4379e316704cb1210f3de Mon Sep 17 00:00:00 2001
+From: Patch User <patchuser@gitlab.org>
+Date: Thu, 18 Oct 2018 13:40:35 +0200
+Subject: [PATCH] A commit from a patch
+
+---
+ README | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/README b/README
+index 3742e48..e40a3b9 100644
+--- a/README
++++ b/README
+@@ -1 +1,3 @@
+ Sample repo for testing gitlab features
++
++This was applied in a patch!
+--
+2.19.1
diff --git a/spec/fixtures/patchfiles/0001-This-does-not-apply-to-the-feature-branch.patch b/spec/fixtures/patchfiles/0001-This-does-not-apply-to-the-feature-branch.patch
new file mode 100644
index 00000000000..905002ae898
--- /dev/null
+++ b/spec/fixtures/patchfiles/0001-This-does-not-apply-to-the-feature-branch.patch
@@ -0,0 +1,23 @@
+From 00c68c2b4f954370ce82a1162bc29c13f524897e Mon Sep 17 00:00:00 2001
+From: Patch User <patchuser@gitlab.org>
+Date: Mon, 22 Oct 2018 11:05:48 +0200
+Subject: [PATCH] This does not apply to the `feature` branch
+
+---
+ files/ruby/feature.rb | 5 +++++
+ 1 file changed, 5 insertions(+)
+ create mode 100644 files/ruby/feature.rb
+
+diff --git a/files/ruby/feature.rb b/files/ruby/feature.rb
+new file mode 100644
+index 0000000..fef26e4
+--- /dev/null
++++ b/files/ruby/feature.rb
+@@ -0,0 +1,5 @@
++class Feature
++ def bar
++ puts 'foo'
++ end
++end
+--
+2.19.1
diff --git a/spec/fixtures/security-reports/feature-branch.zip b/spec/fixtures/security-reports/feature-branch.zip
new file mode 100644
index 00000000000..730ce3dc5f8
--- /dev/null
+++ b/spec/fixtures/security-reports/feature-branch.zip
Binary files differ
diff --git a/spec/fixtures/security-reports/feature-branch/gl-container-scanning-report.json b/spec/fixtures/security-reports/feature-branch/gl-container-scanning-report.json
new file mode 100644
index 00000000000..9840382df6f
--- /dev/null
+++ b/spec/fixtures/security-reports/feature-branch/gl-container-scanning-report.json
@@ -0,0 +1,18 @@
+{
+ "image": "registry.gitlab.com/bikebilly/auto-devops-10-6/feature-branch:e7315ba964febb11bac8f5cd6ec433db8a3a1583",
+ "unapproved": [
+ "CVE-2017-15650"
+ ],
+ "vulnerabilities": [
+ {
+ "featurename": "musl",
+ "featureversion": "1.1.14-r15",
+ "vulnerability": "CVE-2017-15650",
+ "namespace": "alpine:v3.4",
+ "description": "",
+ "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650",
+ "severity": "Medium",
+ "fixedby": "1.1.14-r16"
+ }
+ ]
+}
diff --git a/spec/fixtures/security-reports/feature-branch/gl-dast-report.json b/spec/fixtures/security-reports/feature-branch/gl-dast-report.json
new file mode 100644
index 00000000000..3a308bf047e
--- /dev/null
+++ b/spec/fixtures/security-reports/feature-branch/gl-dast-report.json
@@ -0,0 +1,40 @@
+{
+ "site": {
+ "alerts": [
+ {
+ "sourceid": "3",
+ "wascid": "15",
+ "cweid": "16",
+ "reference": "<p>http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx</p><p>https://www.owasp.org/index.php/List_of_useful_HTTP_headers</p>",
+ "otherinfo": "<p>This issue still applies to error type pages (401, 403, 500, etc) as those pages are often still affected by injection issues, in which case there is still concern for browsers sniffing pages away from their actual content type.</p><p>At \"High\" threshold this scanner will not alert on client or server error responses.</p>",
+ "solution": "<p>Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to 'nosniff' for all web pages.</p><p>If possible, ensure that the end user uses a standards-compliant and modern web browser that does not perform MIME-sniffing at all, or that can be directed by the web application/web server to not perform MIME-sniffing.</p>",
+ "count": "2",
+ "pluginid": "10021",
+ "alert": "X-Content-Type-Options Header Missing",
+ "name": "X-Content-Type-Options Header Missing",
+ "riskcode": "1",
+ "confidence": "2",
+ "riskdesc": "Low (Medium)",
+ "desc": "<p>The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.</p>",
+ "instances": [
+ {
+ "param": "X-Content-Type-Options",
+ "method": "GET",
+ "uri": "http://bikebilly-spring-auto-devops-review-feature-br-3y2gpb.35.192.176.43.xip.io"
+ },
+ {
+ "param": "X-Content-Type-Options",
+ "method": "GET",
+ "uri": "http://bikebilly-spring-auto-devops-review-feature-br-3y2gpb.35.192.176.43.xip.io/"
+ }
+ ]
+ }
+ ],
+ "@ssl": "false",
+ "@port": "80",
+ "@host": "bikebilly-spring-auto-devops-review-feature-br-3y2gpb.35.192.176.43.xip.io",
+ "@name": "http://bikebilly-spring-auto-devops-review-feature-br-3y2gpb.35.192.176.43.xip.io"
+ },
+ "@generated": "Fri, 13 Apr 2018 09:22:01",
+ "@version": "2.7.0"
+}
diff --git a/spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json b/spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json
new file mode 100644
index 00000000000..4b47e259c0f
--- /dev/null
+++ b/spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json
@@ -0,0 +1,46 @@
+[
+ {
+ "priority": "Unknown",
+ "file": "pom.xml",
+ "cve": "CVE-2012-4387",
+ "url": "http://struts.apache.org/docs/s2-011.html",
+ "message": "Long parameter name DoS for org.apache.struts/struts2-core",
+ "tools": [
+ "gemnasium"
+ ],
+ "tool": "gemnasium"
+ },
+ {
+ "priority": "Unknown",
+ "file": "pom.xml",
+ "cve": "CVE-2013-1966",
+ "url": "http://struts.apache.org/docs/s2-014.html",
+ "message": "Remote command execution due to flaw in the includeParams attribute of URL and Anchor tags for org.apache.struts/struts2-core",
+ "tools": [
+ "gemnasium"
+ ],
+ "tool": "gemnasium"
+ },
+ {
+ "priority": "Unknown",
+ "file": "pom.xml",
+ "cve": "CVE-2013-2115",
+ "url": "http://struts.apache.org/docs/s2-014.html",
+ "message": "Remote command execution due to flaw in the includeParams attribute of URL and Anchor tags for org.apache.struts/struts2-core",
+ "tools": [
+ "gemnasium"
+ ],
+ "tool": "gemnasium"
+ },
+ {
+ "priority": "Unknown",
+ "file": "pom.xml",
+ "cve": "CVE-2013-2134",
+ "url": "http://struts.apache.org/docs/s2-015.html",
+ "message": "Arbitrary OGNL code execution via unsanitized wildcard matching for org.apache.struts/struts2-core",
+ "tools": [
+ "gemnasium"
+ ],
+ "tool": "gemnasium"
+ }
+]
diff --git a/spec/fixtures/security-reports/feature-branch/gl-license-management-report.json b/spec/fixtures/security-reports/feature-branch/gl-license-management-report.json
new file mode 100644
index 00000000000..c1d20fa02fa
--- /dev/null
+++ b/spec/fixtures/security-reports/feature-branch/gl-license-management-report.json
@@ -0,0 +1,242 @@
+{
+ "licenses": [
+ {
+ "count": 13,
+ "name": "MIT"
+ },
+ {
+ "count": 2,
+ "name": "New BSD"
+ },
+ {
+ "count": 1,
+ "name": "LGPL"
+ }
+ ],
+ "dependencies": [
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "bundler",
+ "url": "http://bundler.io",
+ "description": "The best way to manage your application's dependencies",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "concurrent-ruby",
+ "url": "http://www.concurrent-ruby.com",
+ "description": "Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "connection_pool",
+ "url": "https://github.com/mperham/connection_pool",
+ "description": "Generic connection pool for Ruby",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "mini_portile2",
+ "url": "http://github.com/flavorjones/mini_portile",
+ "description": "Simplistic port-like solution for developers",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "mustermann",
+ "url": "https://github.com/sinatra/mustermann",
+ "description": "Your personal string matching expert.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "nokogiri",
+ "url": "http://nokogiri.org",
+ "description": "Nokogiri (鋸) is an HTML, XML, SAX, and Reader parser",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "New BSD",
+ "url": "http://opensource.org/licenses/BSD-3-Clause"
+ },
+ "dependency": {
+ "name": "pg",
+ "url": "https://bitbucket.org/ged/ruby-pg",
+ "description": "Pg is the Ruby interface to the {PostgreSQL RDBMS}[http://www.postgresql.org/]",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "New BSD",
+ "url": "http://opensource.org/licenses/BSD-3-Clause"
+ },
+ "dependency": {
+ "name": "puma",
+ "url": "http://puma.io",
+ "description": "Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rack",
+ "url": "https://rack.github.io/",
+ "description": "a modular Ruby webserver interface",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rack-protection",
+ "url": "http://github.com/sinatra/sinatra/tree/master/rack-protection",
+ "description": "Protect against typical web attacks, works with all Rack apps, including Rails.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "redis",
+ "url": "https://github.com/redis/redis-rb",
+ "description": "A Ruby client library for Redis",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "LGPL",
+ "url": "http://www.gnu.org/licenses/lgpl.txt"
+ },
+ "dependency": {
+ "name": "sidekiq",
+ "url": "http://sidekiq.org",
+ "description": "Simple, efficient background processing for Ruby",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "sinatra",
+ "url": "http://www.sinatrarb.com/",
+ "description": "Classy web-development dressed in a DSL",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "slim",
+ "url": "http://slim-lang.com/",
+ "description": "Slim is a template language.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "temple",
+ "url": "https://github.com/judofyr/temple",
+ "description": "Template compilation framework in Ruby",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "tilt",
+ "url": "http://github.com/rtomayko/tilt/",
+ "description": "Generic interface to multiple Ruby template engines",
+ "pathes": [
+ "."
+ ]
+ }
+ }
+ ]
+}
diff --git a/spec/fixtures/security-reports/feature-branch/gl-sast-report.json b/spec/fixtures/security-reports/feature-branch/gl-sast-report.json
new file mode 100644
index 00000000000..a85b9be8b5f
--- /dev/null
+++ b/spec/fixtures/security-reports/feature-branch/gl-sast-report.json
@@ -0,0 +1,944 @@
+[
+ {
+ "category": "sast",
+ "message": "Probable insecure usage of temp file/directory.",
+ "cve": "python/hardcoded/hardcoded-tmp.py:52865813c884a507be1f152d654245af34aba8a391626d01f1ab6d3f52ec8779:B108",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "start_line": 1,
+ "end_line": 1
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B108",
+ "value": "B108",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "line": 1,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "name": "Predictable pseudorandom number generator",
+ "message": "Predictable pseudorandom number generator",
+ "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:47:PREDICTABLE_RANDOM",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 47,
+ "end_line": 47,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "generateSecretToken2"
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-PREDICTABLE_RANDOM",
+ "value": "PREDICTABLE_RANDOM",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM"
+ }
+ ],
+ "priority": "Medium",
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "line": 47,
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM",
+ "tool": "find_sec_bugs"
+ },
+ {
+ "category": "sast",
+ "name": "Predictable pseudorandom number generator",
+ "message": "Predictable pseudorandom number generator",
+ "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:41:PREDICTABLE_RANDOM",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 41,
+ "end_line": 41,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "generateSecretToken1"
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-PREDICTABLE_RANDOM",
+ "value": "PREDICTABLE_RANDOM",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM"
+ }
+ ],
+ "priority": "Medium",
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "line": 41,
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM",
+ "tool": "find_sec_bugs"
+ },
+ {
+ "category": "sast",
+ "message": "Use of insecure MD2, MD4, or MD5 hash function.",
+ "cve": "python/imports/imports-aliases.py:cb203b465dffb0cb3a8e8bd8910b84b93b0a5995a938e4b903dbb0cd6ffa1254:B303",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 11,
+ "end_line": 11
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B303",
+ "value": "B303"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 11,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Use of insecure MD2, MD4, or MD5 hash function.",
+ "cve": "python/imports/imports-aliases.py:a7173c43ae66bd07466632d819d450e0071e02dbf782763640d1092981f9631b:B303",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 12,
+ "end_line": 12
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B303",
+ "value": "B303"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 12,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Use of insecure MD2, MD4, or MD5 hash function.",
+ "cve": "python/imports/imports-aliases.py:017017b77deb0b8369b6065947833eeea752a92ec8a700db590fece3e934cf0d:B303",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 13,
+ "end_line": 13
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B303",
+ "value": "B303"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 13,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Use of insecure MD2, MD4, or MD5 hash function.",
+ "cve": "python/imports/imports-aliases.py:45fc8c53aea7b84f06bc4e590cc667678d6073c4c8a1d471177ca2146fb22db2:B303",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 14,
+ "end_line": 14
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B303",
+ "value": "B303"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 14,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Pickle library appears to be in use, possible security issue.",
+ "cve": "python/imports/imports-aliases.py:5f200d47291e7bbd8352db23019b85453ca048dd98ea0c291260fa7d009963a4:B301",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 15,
+ "end_line": 15
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B301",
+ "value": "B301"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 15,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "name": "ECB mode is insecure",
+ "message": "ECB mode is insecure",
+ "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:29:ECB_MODE",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 29,
+ "end_line": 29,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "insecureCypher"
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-ECB_MODE",
+ "value": "ECB_MODE",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#ECB_MODE"
+ }
+ ],
+ "priority": "Medium",
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "line": 29,
+ "url": "https://find-sec-bugs.github.io/bugs.htm#ECB_MODE",
+ "tool": "find_sec_bugs"
+ },
+ {
+ "category": "sast",
+ "name": "Cipher with no integrity",
+ "message": "Cipher with no integrity",
+ "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:29:CIPHER_INTEGRITY",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 29,
+ "end_line": 29,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "insecureCypher"
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-CIPHER_INTEGRITY",
+ "value": "CIPHER_INTEGRITY",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
+ }
+ ],
+ "priority": "Medium",
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "line": 29,
+ "url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY",
+ "tool": "find_sec_bugs"
+ },
+ {
+ "category": "sast",
+ "message": "Probable insecure usage of temp file/directory.",
+ "cve": "python/hardcoded/hardcoded-tmp.py:63dd4d626855555b816985d82c4614a790462a0a3ada89dc58eb97f9c50f3077:B108",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "start_line": 14,
+ "end_line": 14
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B108",
+ "value": "B108",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "line": 14,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Probable insecure usage of temp file/directory.",
+ "cve": "python/hardcoded/hardcoded-tmp.py:4ad6d4c40a8c263fc265f3384724014e0a4f8dd6200af83e51ff120420038031:B108",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "start_line": 10,
+ "end_line": 10
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B108",
+ "value": "B108",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "line": 10,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with Popen module.",
+ "cve": "python/imports/imports-aliases.py:2c3e1fa1e54c3c6646e8bcfaee2518153c6799b77587ff8d9a7b0631f6d34785:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 1,
+ "end_line": 1
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-aliases.py",
+ "line": 1,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with pickle module.",
+ "cve": "python/imports/imports.py:af58d07f6ad519ef5287fcae65bf1a6999448a1a3a8bc1ac2a11daa80d0b96bf:B403",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports.py",
+ "start_line": 2,
+ "end_line": 2
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B403",
+ "value": "B403"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports.py",
+ "line": 2,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with subprocess module.",
+ "cve": "python/imports/imports.py:8de9bc98029d212db530785a5f6780cfa663548746ff228ab8fa96c5bb82f089:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports.py",
+ "start_line": 4,
+ "end_line": 4
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports.py",
+ "line": 4,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'blerg'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:97c30f1d76d2a88913e3ce9ae74087874d740f87de8af697a9c455f01119f633:B106",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 22,
+ "end_line": 22
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B106",
+ "value": "B106",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b106_hardcoded_password_funcarg.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 22,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b106_hardcoded_password_funcarg.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'root'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:7431c73a0bc16d94ece2a2e75ef38f302574d42c37ac0c3c38ad0b3bf8a59f10:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 5,
+ "end_line": 5
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 5,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: ''",
+ "cve": "python/hardcoded/hardcoded-passwords.py:d2d1857c27caedd49c57bfbcdc23afcc92bd66a22701fcdc632869aab4ca73ee:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 9,
+ "end_line": 9
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 9,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'ajklawejrkl42348swfgkg'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:fb3866215a61393a5c9c32a3b60e2058171a23219c353f722cbd3567acab21d2:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 13,
+ "end_line": 13
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 13,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'blerg'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:63c62a8b7e1e5224439bd26b28030585ac48741e28ca64561a6071080c560a5f:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 23,
+ "end_line": 23
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 23,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'blerg'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:4311b06d08df8fa58229b341c531da8e1a31ec4520597bdff920cd5c098d86f9:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 24,
+ "end_line": 24
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 24,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with subprocess module.",
+ "cve": "python/imports/imports-function.py:5858400c2f39047787702de44d03361ef8d954c9d14bd54ee1c2bef9e6a7df93:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-function.py",
+ "start_line": 4,
+ "end_line": 4
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-function.py",
+ "line": 4,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with pickle module.",
+ "cve": "python/imports/imports-function.py:dbda3cf4190279d30e0aad7dd137eca11272b0b225e8af4e8bf39682da67d956:B403",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-function.py",
+ "start_line": 2,
+ "end_line": 2
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B403",
+ "value": "B403"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-function.py",
+ "line": 2,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with Popen module.",
+ "cve": "python/imports/imports-from.py:eb8a0db9cd1a8c1ab39a77e6025021b1261cc2a0b026b2f4a11fca4e0636d8dd:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-from.py",
+ "start_line": 7,
+ "end_line": 7
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-from.py",
+ "line": 7,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "subprocess call with shell=True seems safe, but may be changed in the future, consider rewriting without shell",
+ "cve": "python/imports/imports-aliases.py:f99f9721e27537fbcb6699a4cf39c6740d6234d2c6f06cfc2d9ea977313c483d:B602",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 9,
+ "end_line": 9
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B602",
+ "value": "B602",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-aliases.py",
+ "line": 9,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with subprocess module.",
+ "cve": "python/imports/imports-from.py:332a12ab1146698f614a905ce6a6a5401497a12281aef200e80522711c69dcf4:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-from.py",
+ "start_line": 6,
+ "end_line": 6
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-from.py",
+ "line": 6,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with Popen module.",
+ "cve": "python/imports/imports-from.py:0a48de4a3d5348853a03666cb574697e3982998355e7a095a798bd02a5947276:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-from.py",
+ "start_line": 1,
+ "end_line": 2
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-from.py",
+ "line": 1,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with pickle module.",
+ "cve": "python/imports/imports-aliases.py:51b71661dff994bde3529639a727a678c8f5c4c96f00d300913f6d5be1bbdf26:B403",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 7,
+ "end_line": 8
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B403",
+ "value": "B403"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-aliases.py",
+ "line": 7,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with loads module.",
+ "cve": "python/imports/imports-aliases.py:6ff02aeb3149c01ab68484d794a94f58d5d3e3bb0d58557ef4153644ea68ea54:B403",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 6,
+ "end_line": 6
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B403",
+ "value": "B403"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-aliases.py",
+ "line": 6,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120)",
+ "cve": "c/subdir/utils.c:b466873101951fe96e1332f6728eb7010acbbd5dfc3b65d7d53571d091a06d9e:CWE-119!/CWE-120",
+ "confidence": "Low",
+ "solution": "Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length",
+ "scanner": {
+ "id": "flawfinder",
+ "name": "Flawfinder"
+ },
+ "location": {
+ "file": "c/subdir/utils.c",
+ "start_line": 4
+ },
+ "identifiers": [
+ {
+ "type": "cwe",
+ "name": "CWE-119",
+ "value": "119",
+ "url": "https://cwe.mitre.org/data/definitions/119.html"
+ },
+ {
+ "type": "cwe",
+ "name": "CWE-120",
+ "value": "120",
+ "url": "https://cwe.mitre.org/data/definitions/120.html"
+ }
+ ],
+ "file": "c/subdir/utils.c",
+ "line": 4,
+ "url": "https://cwe.mitre.org/data/definitions/119.html",
+ "tool": "flawfinder"
+ },
+ {
+ "category": "sast",
+ "message": "Check when opening files - can an attacker redirect it (via symlinks), force the opening of special file type (e.g., device files), move things around to create a race condition, control its ancestors, or change its contents? (CWE-362)",
+ "cve": "c/subdir/utils.c:bab681140fcc8fc3085b6bba74081b44ea145c1c98b5e70cf19ace2417d30770:CWE-362",
+ "confidence": "Low",
+ "scanner": {
+ "id": "flawfinder",
+ "name": "Flawfinder"
+ },
+ "location": {
+ "file": "c/subdir/utils.c",
+ "start_line": 8
+ },
+ "identifiers": [
+ {
+ "type": "cwe",
+ "name": "CWE-362",
+ "value": "362",
+ "url": "https://cwe.mitre.org/data/definitions/362.html"
+ }
+ ],
+ "file": "c/subdir/utils.c",
+ "line": 8,
+ "url": "https://cwe.mitre.org/data/definitions/362.html",
+ "tool": "flawfinder"
+ },
+ {
+ "category": "sast",
+ "message": "Statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120)",
+ "cve": "cplusplus/src/hello.cpp:c8c6dd0afdae6814194cf0930b719f757ab7b379cf8f261e7f4f9f2f323a818a:CWE-119!/CWE-120",
+ "confidence": "Low",
+ "solution": "Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length",
+ "scanner": {
+ "id": "flawfinder",
+ "name": "Flawfinder"
+ },
+ "location": {
+ "file": "cplusplus/src/hello.cpp",
+ "start_line": 6
+ },
+ "identifiers": [
+ {
+ "type": "cwe",
+ "name": "CWE-119",
+ "value": "119",
+ "url": "https://cwe.mitre.org/data/definitions/119.html"
+ },
+ {
+ "type": "cwe",
+ "name": "CWE-120",
+ "value": "120",
+ "url": "https://cwe.mitre.org/data/definitions/120.html"
+ }
+ ],
+ "file": "cplusplus/src/hello.cpp",
+ "line": 6,
+ "url": "https://cwe.mitre.org/data/definitions/119.html",
+ "tool": "flawfinder"
+ },
+ {
+ "category": "sast",
+ "message": "Does not check for buffer overflows when copying to destination [MS-banned] (CWE-120)",
+ "cve": "cplusplus/src/hello.cpp:331c04062c4fe0c7c486f66f59e82ad146ab33cdd76ae757ca41f392d568cbd0:CWE-120",
+ "confidence": "Low",
+ "solution": "Consider using snprintf, strcpy_s, or strlcpy (warning: strncpy easily misused)",
+ "scanner": {
+ "id": "flawfinder",
+ "name": "Flawfinder"
+ },
+ "location": {
+ "file": "cplusplus/src/hello.cpp",
+ "start_line": 7
+ },
+ "identifiers": [
+ {
+ "type": "cwe",
+ "name": "CWE-120",
+ "value": "120",
+ "url": "https://cwe.mitre.org/data/definitions/120.html"
+ }
+ ],
+ "file": "cplusplus/src/hello.cpp",
+ "line": 7,
+ "url": "https://cwe.mitre.org/data/definitions/120.html",
+ "tool": "flawfinder"
+ }
+]
diff --git a/spec/fixtures/security-reports/master.zip b/spec/fixtures/security-reports/master.zip
new file mode 100644
index 00000000000..4684aecb738
--- /dev/null
+++ b/spec/fixtures/security-reports/master.zip
Binary files differ
diff --git a/spec/fixtures/security-reports/master/gl-container-scanning-report.json b/spec/fixtures/security-reports/master/gl-container-scanning-report.json
new file mode 100644
index 00000000000..500c19e3abb
--- /dev/null
+++ b/spec/fixtures/security-reports/master/gl-container-scanning-report.json
@@ -0,0 +1,18 @@
+{
+ "image": "registry.gitlab.com/bikebilly/auto-devops-10-6/feature-branch:e7315ba964febb11bac8f5cd6ec433db8a3a1583",
+ "unapproved": [
+ "CVE-2017-15651"
+ ],
+ "vulnerabilities": [
+ {
+ "featurename": "musl",
+ "featureversion": "1.1.14-r15",
+ "vulnerability": "CVE-2017-15651",
+ "namespace": "alpine:v3.4",
+ "description": "",
+ "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15651",
+ "severity": "Medium",
+ "fixedby": "1.1.14-r16"
+ }
+ ]
+}
diff --git a/spec/fixtures/security-reports/master/gl-dast-report.json b/spec/fixtures/security-reports/master/gl-dast-report.json
new file mode 100644
index 00000000000..3a308bf047e
--- /dev/null
+++ b/spec/fixtures/security-reports/master/gl-dast-report.json
@@ -0,0 +1,40 @@
+{
+ "site": {
+ "alerts": [
+ {
+ "sourceid": "3",
+ "wascid": "15",
+ "cweid": "16",
+ "reference": "<p>http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx</p><p>https://www.owasp.org/index.php/List_of_useful_HTTP_headers</p>",
+ "otherinfo": "<p>This issue still applies to error type pages (401, 403, 500, etc) as those pages are often still affected by injection issues, in which case there is still concern for browsers sniffing pages away from their actual content type.</p><p>At \"High\" threshold this scanner will not alert on client or server error responses.</p>",
+ "solution": "<p>Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to 'nosniff' for all web pages.</p><p>If possible, ensure that the end user uses a standards-compliant and modern web browser that does not perform MIME-sniffing at all, or that can be directed by the web application/web server to not perform MIME-sniffing.</p>",
+ "count": "2",
+ "pluginid": "10021",
+ "alert": "X-Content-Type-Options Header Missing",
+ "name": "X-Content-Type-Options Header Missing",
+ "riskcode": "1",
+ "confidence": "2",
+ "riskdesc": "Low (Medium)",
+ "desc": "<p>The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.</p>",
+ "instances": [
+ {
+ "param": "X-Content-Type-Options",
+ "method": "GET",
+ "uri": "http://bikebilly-spring-auto-devops-review-feature-br-3y2gpb.35.192.176.43.xip.io"
+ },
+ {
+ "param": "X-Content-Type-Options",
+ "method": "GET",
+ "uri": "http://bikebilly-spring-auto-devops-review-feature-br-3y2gpb.35.192.176.43.xip.io/"
+ }
+ ]
+ }
+ ],
+ "@ssl": "false",
+ "@port": "80",
+ "@host": "bikebilly-spring-auto-devops-review-feature-br-3y2gpb.35.192.176.43.xip.io",
+ "@name": "http://bikebilly-spring-auto-devops-review-feature-br-3y2gpb.35.192.176.43.xip.io"
+ },
+ "@generated": "Fri, 13 Apr 2018 09:22:01",
+ "@version": "2.7.0"
+}
diff --git a/spec/fixtures/security-reports/master/gl-dependency-scanning-report.json b/spec/fixtures/security-reports/master/gl-dependency-scanning-report.json
new file mode 100644
index 00000000000..b4e4e8e7dd5
--- /dev/null
+++ b/spec/fixtures/security-reports/master/gl-dependency-scanning-report.json
@@ -0,0 +1,35 @@
+[
+ {
+ "priority": "Unknown",
+ "file": "pom.xml",
+ "cve": "CVE-2012-4386",
+ "url": "http://struts.apache.org/docs/s2-010.html",
+ "message": "CSRF protection bypass for org.apache.struts/struts2-core",
+ "tools": [
+ "gemnasium"
+ ],
+ "tool": "gemnasium"
+ },
+ {
+ "priority": "Unknown",
+ "file": "pom.xml",
+ "cve": "CVE-2012-4387",
+ "url": "http://struts.apache.org/docs/s2-011.html",
+ "message": "Long parameter name DoS for org.apache.struts/struts2-core",
+ "tools": [
+ "gemnasium"
+ ],
+ "tool": "gemnasium"
+ },
+ {
+ "priority": "Unknown",
+ "file": "pom.xml",
+ "cve": "CVE-2013-1966",
+ "url": "http://struts.apache.org/docs/s2-014.html",
+ "message": "Remote command execution due to flaw in the includeParams attribute of URL and Anchor tags for org.apache.struts/struts2-core",
+ "tools": [
+ "gemnasium"
+ ],
+ "tool": "gemnasium"
+ }
+]
diff --git a/spec/fixtures/security-reports/master/gl-license-management-report.json b/spec/fixtures/security-reports/master/gl-license-management-report.json
new file mode 100644
index 00000000000..fe91e4fb7ee
--- /dev/null
+++ b/spec/fixtures/security-reports/master/gl-license-management-report.json
@@ -0,0 +1,150 @@
+{
+ "licenses": [
+ {
+ "count": 10,
+ "name": "MIT"
+ }
+ ],
+ "dependencies": [
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "mini_portile2",
+ "url": "http://github.com/flavorjones/mini_portile",
+ "description": "Simplistic port-like solution for developers",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "mustermann",
+ "url": "https://github.com/sinatra/mustermann",
+ "description": "Your personal string matching expert.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "nokogiri",
+ "url": "http://nokogiri.org",
+ "description": "Nokogiri (鋸) is an HTML, XML, SAX, and Reader parser",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rack",
+ "url": "https://rack.github.io/",
+ "description": "a modular Ruby webserver interface",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rack-protection",
+ "url": "http://github.com/sinatra/sinatra/tree/master/rack-protection",
+ "description": "Protect against typical web attacks, works with all Rack apps, including Rails.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "redis",
+ "url": "https://github.com/redis/redis-rb",
+ "description": "A Ruby client library for Redis",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "sinatra",
+ "url": "http://www.sinatrarb.com/",
+ "description": "Classy web-development dressed in a DSL",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "slim",
+ "url": "http://slim-lang.com/",
+ "description": "Slim is a template language.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "temple",
+ "url": "https://github.com/judofyr/temple",
+ "description": "Template compilation framework in Ruby",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "tilt",
+ "url": "http://github.com/rtomayko/tilt/",
+ "description": "Generic interface to multiple Ruby template engines",
+ "pathes": [
+ "."
+ ]
+ }
+ }
+ ]
+}
diff --git a/spec/fixtures/security-reports/master/gl-sast-report.json b/spec/fixtures/security-reports/master/gl-sast-report.json
new file mode 100644
index 00000000000..a85b9be8b5f
--- /dev/null
+++ b/spec/fixtures/security-reports/master/gl-sast-report.json
@@ -0,0 +1,944 @@
+[
+ {
+ "category": "sast",
+ "message": "Probable insecure usage of temp file/directory.",
+ "cve": "python/hardcoded/hardcoded-tmp.py:52865813c884a507be1f152d654245af34aba8a391626d01f1ab6d3f52ec8779:B108",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "start_line": 1,
+ "end_line": 1
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B108",
+ "value": "B108",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "line": 1,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "name": "Predictable pseudorandom number generator",
+ "message": "Predictable pseudorandom number generator",
+ "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:47:PREDICTABLE_RANDOM",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 47,
+ "end_line": 47,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "generateSecretToken2"
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-PREDICTABLE_RANDOM",
+ "value": "PREDICTABLE_RANDOM",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM"
+ }
+ ],
+ "priority": "Medium",
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "line": 47,
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM",
+ "tool": "find_sec_bugs"
+ },
+ {
+ "category": "sast",
+ "name": "Predictable pseudorandom number generator",
+ "message": "Predictable pseudorandom number generator",
+ "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:41:PREDICTABLE_RANDOM",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 41,
+ "end_line": 41,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "generateSecretToken1"
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-PREDICTABLE_RANDOM",
+ "value": "PREDICTABLE_RANDOM",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM"
+ }
+ ],
+ "priority": "Medium",
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "line": 41,
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM",
+ "tool": "find_sec_bugs"
+ },
+ {
+ "category": "sast",
+ "message": "Use of insecure MD2, MD4, or MD5 hash function.",
+ "cve": "python/imports/imports-aliases.py:cb203b465dffb0cb3a8e8bd8910b84b93b0a5995a938e4b903dbb0cd6ffa1254:B303",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 11,
+ "end_line": 11
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B303",
+ "value": "B303"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 11,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Use of insecure MD2, MD4, or MD5 hash function.",
+ "cve": "python/imports/imports-aliases.py:a7173c43ae66bd07466632d819d450e0071e02dbf782763640d1092981f9631b:B303",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 12,
+ "end_line": 12
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B303",
+ "value": "B303"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 12,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Use of insecure MD2, MD4, or MD5 hash function.",
+ "cve": "python/imports/imports-aliases.py:017017b77deb0b8369b6065947833eeea752a92ec8a700db590fece3e934cf0d:B303",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 13,
+ "end_line": 13
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B303",
+ "value": "B303"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 13,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Use of insecure MD2, MD4, or MD5 hash function.",
+ "cve": "python/imports/imports-aliases.py:45fc8c53aea7b84f06bc4e590cc667678d6073c4c8a1d471177ca2146fb22db2:B303",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 14,
+ "end_line": 14
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B303",
+ "value": "B303"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 14,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Pickle library appears to be in use, possible security issue.",
+ "cve": "python/imports/imports-aliases.py:5f200d47291e7bbd8352db23019b85453ca048dd98ea0c291260fa7d009963a4:B301",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 15,
+ "end_line": 15
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B301",
+ "value": "B301"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/imports/imports-aliases.py",
+ "line": 15,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "name": "ECB mode is insecure",
+ "message": "ECB mode is insecure",
+ "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:29:ECB_MODE",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 29,
+ "end_line": 29,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "insecureCypher"
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-ECB_MODE",
+ "value": "ECB_MODE",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#ECB_MODE"
+ }
+ ],
+ "priority": "Medium",
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "line": 29,
+ "url": "https://find-sec-bugs.github.io/bugs.htm#ECB_MODE",
+ "tool": "find_sec_bugs"
+ },
+ {
+ "category": "sast",
+ "name": "Cipher with no integrity",
+ "message": "Cipher with no integrity",
+ "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:29:CIPHER_INTEGRITY",
+ "severity": "Medium",
+ "confidence": "High",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 29,
+ "end_line": 29,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "insecureCypher"
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-CIPHER_INTEGRITY",
+ "value": "CIPHER_INTEGRITY",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
+ }
+ ],
+ "priority": "Medium",
+ "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
+ "line": 29,
+ "url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY",
+ "tool": "find_sec_bugs"
+ },
+ {
+ "category": "sast",
+ "message": "Probable insecure usage of temp file/directory.",
+ "cve": "python/hardcoded/hardcoded-tmp.py:63dd4d626855555b816985d82c4614a790462a0a3ada89dc58eb97f9c50f3077:B108",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "start_line": 14,
+ "end_line": 14
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B108",
+ "value": "B108",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "line": 14,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Probable insecure usage of temp file/directory.",
+ "cve": "python/hardcoded/hardcoded-tmp.py:4ad6d4c40a8c263fc265f3384724014e0a4f8dd6200af83e51ff120420038031:B108",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "start_line": 10,
+ "end_line": 10
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B108",
+ "value": "B108",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html"
+ }
+ ],
+ "priority": "Medium",
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "line": 10,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with Popen module.",
+ "cve": "python/imports/imports-aliases.py:2c3e1fa1e54c3c6646e8bcfaee2518153c6799b77587ff8d9a7b0631f6d34785:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 1,
+ "end_line": 1
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-aliases.py",
+ "line": 1,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with pickle module.",
+ "cve": "python/imports/imports.py:af58d07f6ad519ef5287fcae65bf1a6999448a1a3a8bc1ac2a11daa80d0b96bf:B403",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports.py",
+ "start_line": 2,
+ "end_line": 2
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B403",
+ "value": "B403"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports.py",
+ "line": 2,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with subprocess module.",
+ "cve": "python/imports/imports.py:8de9bc98029d212db530785a5f6780cfa663548746ff228ab8fa96c5bb82f089:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports.py",
+ "start_line": 4,
+ "end_line": 4
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports.py",
+ "line": 4,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'blerg'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:97c30f1d76d2a88913e3ce9ae74087874d740f87de8af697a9c455f01119f633:B106",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 22,
+ "end_line": 22
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B106",
+ "value": "B106",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b106_hardcoded_password_funcarg.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 22,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b106_hardcoded_password_funcarg.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'root'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:7431c73a0bc16d94ece2a2e75ef38f302574d42c37ac0c3c38ad0b3bf8a59f10:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 5,
+ "end_line": 5
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 5,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: ''",
+ "cve": "python/hardcoded/hardcoded-passwords.py:d2d1857c27caedd49c57bfbcdc23afcc92bd66a22701fcdc632869aab4ca73ee:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 9,
+ "end_line": 9
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 9,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'ajklawejrkl42348swfgkg'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:fb3866215a61393a5c9c32a3b60e2058171a23219c353f722cbd3567acab21d2:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 13,
+ "end_line": 13
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 13,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'blerg'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:63c62a8b7e1e5224439bd26b28030585ac48741e28ca64561a6071080c560a5f:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 23,
+ "end_line": 23
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 23,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Possible hardcoded password: 'blerg'",
+ "cve": "python/hardcoded/hardcoded-passwords.py:4311b06d08df8fa58229b341c531da8e1a31ec4520597bdff920cd5c098d86f9:B105",
+ "severity": "Low",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "start_line": 24,
+ "end_line": 24
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B105",
+ "value": "B105",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/hardcoded/hardcoded-passwords.py",
+ "line": 24,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with subprocess module.",
+ "cve": "python/imports/imports-function.py:5858400c2f39047787702de44d03361ef8d954c9d14bd54ee1c2bef9e6a7df93:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-function.py",
+ "start_line": 4,
+ "end_line": 4
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-function.py",
+ "line": 4,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with pickle module.",
+ "cve": "python/imports/imports-function.py:dbda3cf4190279d30e0aad7dd137eca11272b0b225e8af4e8bf39682da67d956:B403",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-function.py",
+ "start_line": 2,
+ "end_line": 2
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B403",
+ "value": "B403"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-function.py",
+ "line": 2,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with Popen module.",
+ "cve": "python/imports/imports-from.py:eb8a0db9cd1a8c1ab39a77e6025021b1261cc2a0b026b2f4a11fca4e0636d8dd:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-from.py",
+ "start_line": 7,
+ "end_line": 7
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-from.py",
+ "line": 7,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "subprocess call with shell=True seems safe, but may be changed in the future, consider rewriting without shell",
+ "cve": "python/imports/imports-aliases.py:f99f9721e27537fbcb6699a4cf39c6740d6234d2c6f06cfc2d9ea977313c483d:B602",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 9,
+ "end_line": 9
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B602",
+ "value": "B602",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-aliases.py",
+ "line": 9,
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html",
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with subprocess module.",
+ "cve": "python/imports/imports-from.py:332a12ab1146698f614a905ce6a6a5401497a12281aef200e80522711c69dcf4:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-from.py",
+ "start_line": 6,
+ "end_line": 6
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-from.py",
+ "line": 6,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with Popen module.",
+ "cve": "python/imports/imports-from.py:0a48de4a3d5348853a03666cb574697e3982998355e7a095a798bd02a5947276:B404",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-from.py",
+ "start_line": 1,
+ "end_line": 2
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B404",
+ "value": "B404"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-from.py",
+ "line": 1,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with pickle module.",
+ "cve": "python/imports/imports-aliases.py:51b71661dff994bde3529639a727a678c8f5c4c96f00d300913f6d5be1bbdf26:B403",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 7,
+ "end_line": 8
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B403",
+ "value": "B403"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-aliases.py",
+ "line": 7,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Consider possible security implications associated with loads module.",
+ "cve": "python/imports/imports-aliases.py:6ff02aeb3149c01ab68484d794a94f58d5d3e3bb0d58557ef4153644ea68ea54:B403",
+ "severity": "Low",
+ "confidence": "High",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/imports/imports-aliases.py",
+ "start_line": 6,
+ "end_line": 6
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B403",
+ "value": "B403"
+ }
+ ],
+ "priority": "Low",
+ "file": "python/imports/imports-aliases.py",
+ "line": 6,
+ "tool": "bandit"
+ },
+ {
+ "category": "sast",
+ "message": "Statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120)",
+ "cve": "c/subdir/utils.c:b466873101951fe96e1332f6728eb7010acbbd5dfc3b65d7d53571d091a06d9e:CWE-119!/CWE-120",
+ "confidence": "Low",
+ "solution": "Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length",
+ "scanner": {
+ "id": "flawfinder",
+ "name": "Flawfinder"
+ },
+ "location": {
+ "file": "c/subdir/utils.c",
+ "start_line": 4
+ },
+ "identifiers": [
+ {
+ "type": "cwe",
+ "name": "CWE-119",
+ "value": "119",
+ "url": "https://cwe.mitre.org/data/definitions/119.html"
+ },
+ {
+ "type": "cwe",
+ "name": "CWE-120",
+ "value": "120",
+ "url": "https://cwe.mitre.org/data/definitions/120.html"
+ }
+ ],
+ "file": "c/subdir/utils.c",
+ "line": 4,
+ "url": "https://cwe.mitre.org/data/definitions/119.html",
+ "tool": "flawfinder"
+ },
+ {
+ "category": "sast",
+ "message": "Check when opening files - can an attacker redirect it (via symlinks), force the opening of special file type (e.g., device files), move things around to create a race condition, control its ancestors, or change its contents? (CWE-362)",
+ "cve": "c/subdir/utils.c:bab681140fcc8fc3085b6bba74081b44ea145c1c98b5e70cf19ace2417d30770:CWE-362",
+ "confidence": "Low",
+ "scanner": {
+ "id": "flawfinder",
+ "name": "Flawfinder"
+ },
+ "location": {
+ "file": "c/subdir/utils.c",
+ "start_line": 8
+ },
+ "identifiers": [
+ {
+ "type": "cwe",
+ "name": "CWE-362",
+ "value": "362",
+ "url": "https://cwe.mitre.org/data/definitions/362.html"
+ }
+ ],
+ "file": "c/subdir/utils.c",
+ "line": 8,
+ "url": "https://cwe.mitre.org/data/definitions/362.html",
+ "tool": "flawfinder"
+ },
+ {
+ "category": "sast",
+ "message": "Statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120)",
+ "cve": "cplusplus/src/hello.cpp:c8c6dd0afdae6814194cf0930b719f757ab7b379cf8f261e7f4f9f2f323a818a:CWE-119!/CWE-120",
+ "confidence": "Low",
+ "solution": "Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length",
+ "scanner": {
+ "id": "flawfinder",
+ "name": "Flawfinder"
+ },
+ "location": {
+ "file": "cplusplus/src/hello.cpp",
+ "start_line": 6
+ },
+ "identifiers": [
+ {
+ "type": "cwe",
+ "name": "CWE-119",
+ "value": "119",
+ "url": "https://cwe.mitre.org/data/definitions/119.html"
+ },
+ {
+ "type": "cwe",
+ "name": "CWE-120",
+ "value": "120",
+ "url": "https://cwe.mitre.org/data/definitions/120.html"
+ }
+ ],
+ "file": "cplusplus/src/hello.cpp",
+ "line": 6,
+ "url": "https://cwe.mitre.org/data/definitions/119.html",
+ "tool": "flawfinder"
+ },
+ {
+ "category": "sast",
+ "message": "Does not check for buffer overflows when copying to destination [MS-banned] (CWE-120)",
+ "cve": "cplusplus/src/hello.cpp:331c04062c4fe0c7c486f66f59e82ad146ab33cdd76ae757ca41f392d568cbd0:CWE-120",
+ "confidence": "Low",
+ "solution": "Consider using snprintf, strcpy_s, or strlcpy (warning: strncpy easily misused)",
+ "scanner": {
+ "id": "flawfinder",
+ "name": "Flawfinder"
+ },
+ "location": {
+ "file": "cplusplus/src/hello.cpp",
+ "start_line": 7
+ },
+ "identifiers": [
+ {
+ "type": "cwe",
+ "name": "CWE-120",
+ "value": "120",
+ "url": "https://cwe.mitre.org/data/definitions/120.html"
+ }
+ ],
+ "file": "cplusplus/src/hello.cpp",
+ "line": 7,
+ "url": "https://cwe.mitre.org/data/definitions/120.html",
+ "tool": "flawfinder"
+ }
+]
diff --git a/spec/fixtures/ssh_host_example_key.pub b/spec/fixtures/ssh_host_example_key.pub
index 6bac42b3ad0..d43315ddae8 100644
--- a/spec/fixtures/ssh_host_example_key.pub
+++ b/spec/fixtures/ssh_host_example_key.pub
@@ -1 +1 @@
-random content
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuRkAgwaap/pXThwCpjX8Wd5tR36Tqx3sW2sVVHs3UKB7kd+xNknw7e4qpuEATv56xHrhKm2+ye/JidTuQ/1EwFhjaz7I5wTslfVawQpeH1ZqAGmvdO/xTw+l7fgEFVlGVx9y0HV3m52y2C9yw82qmg+BohbTVgPtjjutpFc+CwLQxLTnTrRhZf5udQgz+YlwLv+Y0kDx6+DWWOl8N9+TWuGyFKBln79CyBgFcK5NFmF48kYn8W+r7rmawfw9XbuF1aa+6JF+6cNR1mCEonyrRLdXP+vWcxpLKYfejB0NmA1y+W9M/K53AcIHA5zlRQ49tFh0P22eh/Gl8JQ6yyuin foo@bar.mynet
diff --git a/spec/fixtures/trace/sample_trace b/spec/fixtures/trace/sample_trace
index 7bfe3f83b7b..3d8beb0dec2 100644
--- a/spec/fixtures/trace/sample_trace
+++ b/spec/fixtures/trace/sample_trace
@@ -2334,12 +2334,12 @@ Boards::Lists::MoveService
keeps position of lists when list type is closed
when list type is set to label
keeps position of lists when new position is nil
- keeps position of lists when new positon is equal to old position
- keeps position of lists when new positon is negative
- keeps position of lists when new positon is equal to number of labels lists
- keeps position of lists when new positon is greater than number of labels lists
- increments position of intermediate lists when new positon is equal to first position
- decrements position of intermediate lists when new positon is equal to last position
+ keeps position of lists when new position is equal to old position
+ keeps position of lists when new position is negative
+ keeps position of lists when new position is equal to number of labels lists
+ keeps position of lists when new position is greater than number of labels lists
+ increments position of intermediate lists when new position is equal to first position
+ decrements position of intermediate lists when new position is equal to last position
decrements position of intermediate lists when new position is greater than old position
increments position of intermediate lists when new position is lower than old position
when board parent is a group
@@ -2347,12 +2347,12 @@ Boards::Lists::MoveService
keeps position of lists when list type is closed
when list type is set to label
keeps position of lists when new position is nil
- keeps position of lists when new positon is equal to old position
- keeps position of lists when new positon is negative
- keeps position of lists when new positon is equal to number of labels lists
- keeps position of lists when new positon is greater than number of labels lists
- increments position of intermediate lists when new positon is equal to first position
- decrements position of intermediate lists when new positon is equal to last position
+ keeps position of lists when new position is equal to old position
+ keeps position of lists when new position is negative
+ keeps position of lists when new position is equal to number of labels lists
+ keeps position of lists when new position is greater than number of labels lists
+ increments position of intermediate lists when new position is equal to first position
+ decrements position of intermediate lists when new position is equal to last position
decrements position of intermediate lists when new position is greater than old position
increments position of intermediate lists when new position is lower than old position
diff --git a/spec/fixtures/valid.po b/spec/fixtures/valid.po
index e43fd5fea15..dbe2f952bad 100644
--- a/spec/fixtures/valid.po
+++ b/spec/fixtures/valid.po
@@ -790,9 +790,6 @@ msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a travÃ
msgid "Set up CI"
msgstr "Configurar CI"
-msgid "Set up Koding"
-msgstr "Configurar Koding"
-
msgid "Set up auto deploy"
msgstr "Configurar auto despliegue"
diff --git a/spec/graphql/types/permission_types/project_spec.rb b/spec/graphql/types/permission_types/project_spec.rb
index 89eecef096e..927153adc5b 100644
--- a/spec/graphql/types/permission_types/project_spec.rb
+++ b/spec/graphql/types/permission_types/project_spec.rb
@@ -10,7 +10,7 @@ describe Types::PermissionTypes::Project do
:read_commit_status, :request_access, :create_pipeline, :create_pipeline_schedule,
:create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label,
- :update_wiki, :destroy_wiki, :create_pages, :destroy_pages
+ :update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content
]
expect(described_class).to have_graphql_fields(expected_permissions)
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 14297a1a544..4135f31e051 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -3,48 +3,66 @@ require 'spec_helper'
describe ApplicationHelper do
describe 'current_controller?' do
- it 'returns true when controller matches argument' do
+ before do
stub_controller_name('foo')
+ end
- expect(helper.current_controller?(:foo)).to eq true
+ it 'returns true when controller matches argument' do
+ expect(helper.current_controller?(:foo)).to be_truthy
end
it 'returns false when controller does not match argument' do
- stub_controller_name('foo')
-
- expect(helper.current_controller?(:bar)).to eq false
+ expect(helper.current_controller?(:bar)).to be_falsey
end
it 'takes any number of arguments' do
- stub_controller_name('foo')
+ expect(helper.current_controller?(:baz, :bar)).to be_falsey
+ expect(helper.current_controller?(:baz, :bar, :foo)).to be_truthy
+ end
- expect(helper.current_controller?(:baz, :bar)).to eq false
- expect(helper.current_controller?(:baz, :bar, :foo)).to eq true
+ context 'when namespaced' do
+ before do
+ stub_controller_path('bar/foo')
+ end
+
+ it 'returns true when controller matches argument' do
+ expect(helper.current_controller?(:foo)).to be_truthy
+ end
+
+ it 'returns true when controller and namespace matches argument in path notation' do
+ expect(helper.current_controller?('bar/foo')).to be_truthy
+ end
+
+ it 'returns false when namespace doesnt match' do
+ expect(helper.current_controller?('foo/foo')).to be_falsey
+ end
end
def stub_controller_name(value)
allow(helper.controller).to receive(:controller_name).and_return(value)
end
+
+ def stub_controller_path(value)
+ allow(helper.controller).to receive(:controller_path).and_return(value)
+ end
end
describe 'current_action?' do
- it 'returns true when action matches' do
+ before do
stub_action_name('foo')
+ end
- expect(helper.current_action?(:foo)).to eq true
+ it 'returns true when action matches' do
+ expect(helper.current_action?(:foo)).to be_truthy
end
it 'returns false when action does not match' do
- stub_action_name('foo')
-
- expect(helper.current_action?(:bar)).to eq false
+ expect(helper.current_action?(:bar)).to be_falsey
end
it 'takes any number of arguments' do
- stub_action_name('foo')
-
- expect(helper.current_action?(:baz, :bar)).to eq false
- expect(helper.current_action?(:baz, :bar, :foo)).to eq true
+ expect(helper.current_action?(:baz, :bar)).to be_falsey
+ expect(helper.current_action?(:baz, :bar, :foo)).to be_truthy
end
def stub_action_name(value)
@@ -100,8 +118,7 @@ describe ApplicationHelper do
end
it 'accepts a custom html_class' do
- expect(element(html_class: 'custom_class').attr('class'))
- .to eq 'js-timeago custom_class'
+ expect(element(html_class: 'custom_class').attr('class')).to eq 'js-timeago custom_class'
end
it 'accepts a custom tooltip placement' do
@@ -114,6 +131,7 @@ describe ApplicationHelper do
it 'add class for the short format' do
timeago_element = element(short_format: 'short')
+
expect(timeago_element.attr('class')).to eq 'js-short-timeago'
expect(timeago_element.next_element).to eq nil
end
@@ -128,11 +146,9 @@ describe ApplicationHelper do
context 'when alternate support url is specified' do
let(:alternate_url) { 'http://company.example.com/getting-help' }
- before do
+ it 'returns the alternate support url' do
stub_application_setting(help_page_support_url: alternate_url)
- end
- it 'returns the alternate support url' do
expect(helper.support_url).to eq(alternate_url)
end
end
@@ -155,9 +171,10 @@ describe ApplicationHelper do
describe '#autocomplete_data_sources' do
let(:project) { create(:project) }
let(:noteable_type) { Issue }
+
it 'returns paths for autocomplete_sources_controller' do
sources = helper.autocomplete_data_sources(project, noteable_type)
- expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands])
+ expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands, :snippets])
sources.keys.each do |key|
expect(sources[key]).not_to be_nil
end
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index 120b23e66ac..f0c2e4768ec 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -42,6 +42,16 @@ describe AuthHelper do
end
end
+ describe 'form_based_auth_provider_has_active_class?' do
+ it 'selects main LDAP server' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :ldapprimary, :ldapsecondary, :kerberos] }
+ expect(helper.form_based_auth_provider_has_active_class?(:twitter)).to be(false)
+ expect(helper.form_based_auth_provider_has_active_class?(:ldapprimary)).to be(true)
+ expect(helper.form_based_auth_provider_has_active_class?(:ldapsecondary)).to be(false)
+ expect(helper.form_based_auth_provider_has_active_class?(:kerberos)).to be(false)
+ end
+ end
+
describe 'enabled_button_based_providers' do
before do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
diff --git a/spec/helpers/auto_devops_helper_spec.rb b/spec/helpers/auto_devops_helper_spec.rb
index 1950c2b129b..75c30dbfe48 100644
--- a/spec/helpers/auto_devops_helper_spec.rb
+++ b/spec/helpers/auto_devops_helper_spec.rb
@@ -16,7 +16,15 @@ describe AutoDevopsHelper do
subject { helper.show_auto_devops_callout?(project) }
- context 'when all conditions are met' do
+ context 'when auto devops is implicitly enabled' do
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when auto devops is not implicitly enabled' do
+ before do
+ Gitlab::CurrentSettings.update!(auto_devops_enabled: false)
+ end
+
it { is_expected.to eq(true) }
end
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index 55ee87163f9..aa0442ab847 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -32,18 +32,6 @@ describe AvatarsHelper do
end
end
- context 'when providing a project path' do
- it_behaves_like 'resource with a default avatar', 'project' do
- let(:resource) { create(:project, name: 'foo') }
- let(:helper_args) { [resource.full_path] }
- end
-
- it_behaves_like 'resource with a custom avatar', 'project' do
- let(:resource) { create(:project, :public, avatar: File.open(uploaded_image_temp_path)) }
- let(:helper_args) { [resource.full_path] }
- end
- end
-
context 'when providing a group' do
it_behaves_like 'resource with a default avatar', 'group' do
let(:resource) { create(:group, name: 'foo') }
@@ -55,18 +43,6 @@ describe AvatarsHelper do
let(:helper_args) { [resource] }
end
end
-
- context 'when providing a group path' do
- it_behaves_like 'resource with a default avatar', 'group' do
- let(:resource) { create(:group, name: 'foo') }
- let(:helper_args) { [resource.full_path] }
- end
-
- it_behaves_like 'resource with a custom avatar', 'group' do
- let(:resource) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
- let(:helper_args) { [resource.full_path] }
- end
- end
end
describe '#avatar_icon_for' do
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index 1c216b3fe97..f709f152c92 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -3,63 +3,13 @@ require 'spec_helper'
describe BlobHelper do
include TreeHelper
- let(:blob_name) { 'test.lisp' }
- let(:no_context_content) { ":type \"assem\"))" }
- let(:blob_content) { "(make-pathname :defaults name\n#{no_context_content}" }
- let(:split_content) { blob_content.split("\n") }
- let(:multiline_content) do
- %q(
- def test(input):
- """This is line 1 of a multi-line comment.
- This is line 2.
- """
- )
- end
-
describe '#highlight' do
- it 'returns plaintext for unknown lexer context' do
- result = helper.highlight(blob_name, no_context_content)
- expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line" lang="">:type "assem"))</span></code></pre>])
+ it 'wraps highlighted content' do
+ expect(helper.highlight('test.rb', '52')).to eq(%q[<pre class="code highlight"><code><span id="LC1" class="line" lang="ruby"><span class="mi">52</span></span></code></pre>])
end
- it 'returns plaintext for long blobs' do
- stub_const('Blob::MAXIMUM_TEXT_HIGHLIGHT_SIZE', 1)
- result = helper.highlight(blob_name, blob_content)
-
- expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line" lang="">(make-pathname :defaults name</span>\n<span id="LC2" class="line" lang="">:type "assem"))</span></code></pre>])
- end
-
- it 'highlights single block' do
- expected = %Q[<pre class="code highlight"><code><span id="LC1" class="line" lang="common_lisp"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
-<span id="LC2" class="line" lang="common_lisp"><span class="ss">:type</span> <span class="s">"assem"</span><span class="p">))</span></span></code></pre>]
-
- expect(helper.highlight(blob_name, blob_content)).to eq(expected)
- end
-
- it 'highlights multi-line comments' do
- result = helper.highlight(blob_name, multiline_content)
- html = Nokogiri::HTML(result)
- lines = html.search('.s')
- expect(lines.count).to eq(3)
- expect(lines[0].text).to eq('"""This is line 1 of a multi-line comment.')
- expect(lines[1].text).to eq(' This is line 2.')
- expect(lines[2].text).to eq(' """')
- end
-
- context 'diff highlighting' do
- let(:blob_name) { 'test.diff' }
- let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"}
- let(:expected) do
- %q(<pre class="code highlight"><code><span id="LC1" class="line" lang="diff"><span class="gi">+aaa</span></span>
-<span id="LC2" class="line" lang="diff"><span class="gi">+bbb</span></span>
-<span id="LC3" class="line" lang="diff"><span class="gd">- ccc</span></span>
-<span id="LC4" class="line" lang="diff"> ddd</span></code></pre>)
- end
-
- it 'highlights each line properly' do
- result = helper.highlight(blob_name, blob_content)
- expect(result).to eq(expected)
- end
+ it 'handles plain version' do
+ expect(helper.highlight('test.rb', '52', plain: true)).to eq(%q[<pre class="code highlight"><code><span id="LC1" class="line" lang="">52</span></code></pre>])
end
end
diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb
index 630f3eff258..eebae1d7290 100644
--- a/spec/helpers/button_helper_spec.rb
+++ b/spec/helpers/button_helper_spec.rb
@@ -40,12 +40,24 @@ describe ButtonHelper do
end
context 'when user has no personal access tokens' do
- it 'has a personal access token text on the dropdown description ' do
+ it 'has a personal access token text on the dropdown description' do
description = element.search('.dropdown-menu-inner-content').first
expect(description.inner_text).to eq 'Create a personal access token on your account to pull or push via HTTP.'
end
end
+
+ context 'when user has personal access tokens' do
+ before do
+ create(:personal_access_token, user: user)
+ end
+
+ it 'does not have a personal access token text on the dropdown description' do
+ description = element.search('.dropdown-menu-inner-content').first
+
+ expect(description).to be_nil
+ end
+ end
end
context 'when user is ldap user' do
@@ -79,6 +91,18 @@ describe ButtonHelper do
end
end
+ context 'without an ssh key on the user and user_show_add_ssh_key_message unset' do
+ before do
+ stub_application_setting(user_show_add_ssh_key_message: false)
+ end
+
+ it 'there is no warning on the dropdown description' do
+ description = element.search('.dropdown-menu-inner-content').first
+
+ expect(description).to be_nil
+ end
+ end
+
context 'with an ssh key on the user' do
before do
create(:key, user: user)
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index 4b6c7c33e5b..9c0e55739d6 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -37,7 +37,7 @@ describe CommitsHelper do
.not_to include('onmouseover="alert(1)"')
end
- it 'escapes the commiter name' do
+ it 'escapes the committer name' do
user = build_stubbed(:user, name: 'Foo <script>alert("XSS")</script>')
commit = double(committer: user, committer_name: '', committer_email: '')
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index fccde8b7eba..8d0679e5699 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -2,18 +2,18 @@ require 'spec_helper'
describe EventsHelper do
describe '#event_commit_title' do
- let(:message) { "foo & bar " + "A" * 70 + "\n" + "B" * 80 }
+ let(:message) { 'foo & bar ' + 'A' * 70 + '\n' + 'B' * 80 }
subject { helper.event_commit_title(message) }
- it "returns the first line, truncated to 70 chars" do
+ it 'returns the first line, truncated to 70 chars' do
is_expected.to eq(message[0..66] + "...")
end
- it "is not html-safe" do
+ it 'is not html-safe' do
is_expected.not_to be_a(ActiveSupport::SafeBuffer)
end
- it "handles empty strings" do
+ it 'handles empty strings' do
expect(helper.event_commit_title("")).to eq("")
end
@@ -22,7 +22,66 @@ describe EventsHelper do
end
it 'does not escape HTML entities' do
- expect(helper.event_commit_title("foo & bar")).to eq("foo & bar")
+ expect(helper.event_commit_title('foo & bar')).to eq('foo & bar')
+ end
+ end
+
+ describe '#event_feed_url' do
+ let(:event) { create(:event) }
+ let(:project) { create(:project, :public, :repository) }
+
+ context 'issue' do
+ before do
+ event.target = create(:issue)
+ end
+
+ it 'returns the project issue url' do
+ expect(helper.event_feed_url(event)).to eq(project_issue_url(event.project, event.target))
+ end
+
+ it 'contains the project issue IID link' do
+ expect(helper.event_feed_title(event)).to include("##{event.target.iid}")
+ end
+ end
+
+ context 'merge request' do
+ before do
+ event.target = create(:merge_request)
+ end
+
+ it 'returns the project merge request url' do
+ expect(helper.event_feed_url(event)).to eq(project_merge_request_url(event.project, event.target))
+ end
+
+ it 'contains the project merge request IID link' do
+ expect(helper.event_feed_title(event)).to include("!#{event.target.iid}")
+ end
+ end
+
+ it 'returns project commit url' do
+ event.target = create(:note_on_commit, project: project)
+
+ expect(helper.event_feed_url(event)).to eq(project_commit_url(event.project, event.note_target))
+ end
+
+ it 'returns event note target url' do
+ event.target = create(:note)
+
+ expect(helper.event_feed_url(event)).to eq(event_note_target_url(event))
+ end
+
+ it 'returns project url' do
+ event.project = project
+ event.action = 1
+
+ expect(helper.event_feed_url(event)).to eq(project_url(event.project))
+ end
+
+ it 'returns push event feed url' do
+ event = create(:push_event)
+ create(:push_event_payload, event: event, action: :pushed)
+
+ expect(helper.event_feed_url(event)).to eq(push_event_feed_url(event))
end
end
end
diff --git a/spec/helpers/icons_helper_spec.rb b/spec/helpers/icons_helper_spec.rb
index 82f588d1a08..4b40d523287 100644
--- a/spec/helpers/icons_helper_spec.rb
+++ b/spec/helpers/icons_helper_spec.rb
@@ -80,6 +80,26 @@ describe IconsHelper do
end
end
+ describe 'audit icon' do
+ it 'returns right icon name for standard auth' do
+ icon_name = 'standard'
+ expect(audit_icon(icon_name).to_s)
+ .to eq '<i class="fa fa-key"></i>'
+ end
+
+ it 'returns right icon name for two-factor auth' do
+ icon_name = 'two-factor'
+ expect(audit_icon(icon_name).to_s)
+ .to eq '<i class="fa fa-key"></i>'
+ end
+
+ it 'returns right icon name for google_oauth2 auth' do
+ icon_name = 'google_oauth2'
+ expect(audit_icon(icon_name).to_s)
+ .to eq '<i class="fa fa-google"></i>'
+ end
+ end
+
describe 'file_type_icon_class' do
it 'returns folder class' do
expect(file_type_icon_class('folder', 0, 'folder_name')).to eq 'folder'
diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb
index 033155617c6..cb0ea4e26ba 100644
--- a/spec/helpers/import_helper_spec.rb
+++ b/spec/helpers/import_helper_spec.rb
@@ -1,6 +1,16 @@
require 'rails_helper'
describe ImportHelper do
+ describe '#sanitize_project_name' do
+ it 'removes whitespace' do
+ expect(helper.sanitize_project_name('my test repo')).to eq('my-test-repo')
+ end
+
+ it 'removes disallowed characters' do
+ expect(helper.sanitize_project_name('Test&me$over*h_ere')).to eq('Test-me-over-h_ere')
+ end
+ end
+
describe '#import_project_target' do
let(:user) { create(:user) }
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index f76ed4bfda4..4af98bc3678 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -184,7 +184,7 @@ describe IssuablesHelper do
issuableRef: "##{issue.iid}",
markdownPreviewPath: "/#{@project.full_path}/preview_markdown",
markdownDocsPath: '/help/user/markdown',
- markdownVersion: 11,
+ markdownVersion: CacheMarkdownField::CACHE_COMMONMARK_VERSION,
issuableTemplates: [],
projectPath: @project.path,
projectNamespace: @project.namespace.path,
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index a2cda58e5d2..c04f679bcf0 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -211,4 +211,29 @@ describe LabelsHelper do
end
end
end
+
+ describe 'labels_filter_path' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project) }
+
+ it 'links to the dashboard labels page' do
+ expect(labels_filter_path).to eq(dashboard_labels_path)
+ end
+
+ it 'links to the group labels page' do
+ assign(:group, group)
+
+ expect(helper.labels_filter_path).to eq(group_labels_path(group))
+ end
+
+ it 'links to the project labels page' do
+ assign(:project, project)
+
+ expect(helper.labels_filter_path).to eq(project_labels_path(project))
+ end
+
+ it 'supports json format' do
+ expect(labels_filter_path(format: :json)).to eq(dashboard_labels_path(format: :json))
+ end
+ end
end
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 597648b064d..a0c0af94fa5 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -25,17 +25,17 @@ describe MarkupHelper do
let(:actual) { "#{merge_request.to_reference} -> #{commit.to_reference} -> #{issue.to_reference}" }
it "links to the merge request" do
- expected = project_merge_request_path(project, merge_request)
+ expected = urls.project_merge_request_path(project, merge_request)
expect(helper.markdown(actual)).to match(expected)
end
it "links to the commit" do
- expected = project_commit_path(project, commit)
+ expected = urls.project_commit_path(project, commit)
expect(helper.markdown(actual)).to match(expected)
end
it "links to the issue" do
- expected = project_issue_path(project, issue)
+ expected = urls.project_issue_path(project, issue)
expect(helper.markdown(actual)).to match(expected)
end
end
@@ -46,7 +46,7 @@ describe MarkupHelper do
let(:second_issue) { create(:issue, project: second_project) }
it 'links to the issue' do
- expected = project_issue_path(second_project, second_issue)
+ expected = urls.project_issue_path(second_project, second_issue)
expect(markdown(actual, project: second_project)).to match(expected)
end
end
@@ -93,7 +93,7 @@ describe MarkupHelper do
# First issue link
expect(doc.css('a')[1].attr('href'))
- .to eq project_issue_path(project, issues[0])
+ .to eq urls.project_issue_path(project, issues[0])
expect(doc.css('a')[1].text).to eq issues[0].to_reference
# Internal commit link
@@ -102,7 +102,7 @@ describe MarkupHelper do
# Second issue link
expect(doc.css('a')[3].attr('href'))
- .to eq project_issue_path(project, issues[1])
+ .to eq urls.project_issue_path(project, issues[1])
expect(doc.css('a')[3].text).to eq issues[1].to_reference
# Trailing commit link
@@ -128,7 +128,7 @@ describe MarkupHelper do
# First issue link
expect(doc.css('a')[1].attr('href'))
- .to eq project_issue_path(project, issues[0])
+ .to eq urls.project_issue_path(project, issues[0])
expect(doc.css('a')[1].text).to eq issues[0].to_reference
# Internal commit link
@@ -137,7 +137,7 @@ describe MarkupHelper do
# Second issue link
expect(doc.css('a')[3].attr('href'))
- .to eq project_issue_path(project, issues[1])
+ .to eq urls.project_issue_path(project, issues[1])
expect(doc.css('a')[3].text).to eq issues[1].to_reference
# Trailing commit link
@@ -183,7 +183,7 @@ describe MarkupHelper do
doc = Nokogiri::HTML.parse(rendered)
expect(doc.css('a')[0].attr('href'))
- .to eq project_issue_path(project, issue)
+ .to eq urls.project_issue_path(project, issue)
expect(doc.css('a')[0].text).to eq issue.to_reference
wrapped = helper.link_to_html(rendered, link)
@@ -207,6 +207,17 @@ describe MarkupHelper do
expect(helper).to receive(:markdown_unsafe).with('wiki content',
pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
+ issuable_state_filter_enabled: true)
+
+ helper.render_wiki_content(@wiki)
+ end
+
+ it 'uses Wiki pipeline for markdown files with RedCarpet if feature disabled' do
+ stub_feature_flags(commonmark_for_repositories: false)
+ allow(@wiki).to receive(:format).and_return(:markdown)
+
+ expect(helper).to receive(:markdown_unsafe).with('wiki content',
+ pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
issuable_state_filter_enabled: true, markdown_engine: :redcarpet)
helper.render_wiki_content(@wiki)
@@ -259,10 +270,18 @@ describe MarkupHelper do
expect(helper.markup('foo.md', content, rendered: '<p>NOEL</p>')).to eq('<p>NOEL</p>')
end
- it 'defaults to Redcarpet' do
- expect(helper).to receive(:markdown_unsafe).with(content, hash_including(markdown_engine: :redcarpet)).and_return('NOEL')
+ it 'defaults to CommonMark' do
+ expect(helper.markup('foo.md', 'x^2')).to include('x^2')
+ end
- expect(helper.markup('foo.md', content)).to eq('NOEL')
+ it 'honors markdown_engine for RedCarpet' do
+ expect(helper.markup('foo.md', 'x^2', { markdown_engine: :redcarpet })).to include('x<sup>2</sup>')
+ end
+
+ it 'uses RedCarpet if feature disabled' do
+ stub_feature_flags(commonmark_for_repositories: false)
+
+ expect(helper.markup('foo.md', 'x^2', { markdown_engine: :redcarpet })).to include('x<sup>2</sup>')
end
end
@@ -320,11 +339,25 @@ describe MarkupHelper do
expect(first_line_in_markdown(object, attribute, 150, project: project)).to eq(expected)
end
- it 'preserves data-src for lazy images' do
- object = create_object("![ImageTest](/uploads/test.png)")
- image_url = "data-src=\".*/uploads/test.png\""
+ context 'when images are allowed' do
+ it 'preserves data-src for lazy images' do
+ object = create_object("![ImageTest](/uploads/test.png)")
+ image_url = "data-src=\".*/uploads/test.png\""
+ text = first_line_in_markdown(object, attribute, 150, project: project, allow_images: true)
+
+ expect(text).to match(image_url)
+ expect(text).to match('<a')
+ end
+ end
+
+ context 'when images are not allowed' do
+ it 'removes any images' do
+ object = create_object("![ImageTest](/uploads/test.png)")
+ text = first_line_in_markdown(object, attribute, 150, project: project)
- expect(first_line_in_markdown(object, attribute, 150, project: project)).to match(image_url)
+ expect(text).not_to match('<img')
+ expect(text).not_to match('<a')
+ end
end
context 'labels formatting' do
@@ -414,4 +447,8 @@ describe MarkupHelper do
expect(helper.cross_project_reference(project, issue)).to include(project.full_path)
end
end
+
+ def urls
+ Gitlab::Routing.url_helpers
+ end
end
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
index 234690e742b..7ccbdcd1332 100644
--- a/spec/helpers/namespaces_helper_spec.rb
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -50,9 +50,12 @@ describe NamespacesHelper do
end
it 'selects the new group by default' do
+ # Ensure we don't select a group with the same name
+ create(:group, name: 'new-group', path: 'another-path')
+
allow(helper).to receive(:current_user).and_return(user)
- options = helper.namespaces_options(:extra_group, display_path: true, extra_group: build(:group, name: 'new-group'))
+ options = helper.namespaces_options(:extra_group, display_path: true, extra_group: build(:group, name: 'new-group', path: 'new-group'))
expect(options).to include(user_group.name)
expect(options).not_to include(admin_group.name)
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 363ebc88afd..c112c8ed633 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -2,6 +2,13 @@ require 'spec_helper'
describe PreferencesHelper do
describe '#dashboard_choices' do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).and_return(false)
+ end
+
it 'raises an exception when defined choices may be missing' do
expect(User).to receive(:dashboards).and_return(foo: 'foo')
expect { helper.dashboard_choices }.to raise_error(RuntimeError)
diff --git a/spec/helpers/profiles_helper_spec.rb b/spec/helpers/profiles_helper_spec.rb
index c1d0614c79e..8e336469c27 100644
--- a/spec/helpers/profiles_helper_spec.rb
+++ b/spec/helpers/profiles_helper_spec.rb
@@ -1,6 +1,40 @@
require 'rails_helper'
describe ProfilesHelper do
+ describe '#commit_email_select_options' do
+ it 'returns an array with private commit email along with all the verified emails' do
+ user = create(:user)
+ create(:email, user: user)
+ confirmed_email1 = create(:email, :confirmed, user: user)
+ confirmed_email2 = create(:email, :confirmed, user: user)
+
+ private_email = user.private_commit_email
+
+ emails = [
+ ["Use a private email - #{private_email}", Gitlab::PrivateCommitEmail::TOKEN],
+ user.email,
+ confirmed_email1.email,
+ confirmed_email2.email
+ ]
+
+ expect(helper.commit_email_select_options(user)).to match_array(emails)
+ end
+ end
+
+ describe '#selected_commit_email' do
+ let(:user) { create(:user) }
+
+ it 'returns main email when commit email attribute is nil' do
+ expect(helper.selected_commit_email(user)).to eq(user.email)
+ end
+
+ it 'returns DB stored commit_email' do
+ user.update(commit_email: Gitlab::PrivateCommitEmail::TOKEN)
+
+ expect(helper.selected_commit_email(user)).to eq(Gitlab::PrivateCommitEmail::TOKEN)
+ end
+ end
+
describe '#email_provider_label' do
it "returns nil for users without external email" do
user = create(:user)
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index cbd4ff0fb4a..976b6c312b4 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -470,4 +470,16 @@ describe ProjectsHelper do
end
end
end
+
+ describe '#legacy_render_context' do
+ it 'returns the redcarpet engine' do
+ params = { legacy_render: '1' }
+
+ expect(helper.legacy_render_context(params)).to include(markdown_engine: :redcarpet)
+ end
+
+ it 'returns nothing' do
+ expect(helper.legacy_render_context({})).to be_empty
+ end
+ end
end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 8bfd520528f..4945749f524 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -135,5 +135,40 @@ describe SearchHelper do
expect(search_filter_input_options('')[:data]['base-endpoint']).to eq("/groups#{group_path(@group)}")
end
end
+
+ context 'dashboard' do
+ it 'does not include group-id and project-id' do
+ expect(search_filter_input_options('')[:data]['project-id']).to eq(nil)
+ expect(search_filter_input_options('')[:data]['group-id']).to eq(nil)
+ end
+
+ it 'includes dashboard base-endpoint' do
+ expect(search_filter_input_options('')[:data]['base-endpoint']).to eq("/dashboard")
+ end
+ end
+ end
+
+ describe 'search_history_storage_prefix' do
+ context 'project' do
+ it 'returns project full_path' do
+ @project = create(:project, :repository)
+
+ expect(search_history_storage_prefix).to eq(@project.full_path)
+ end
+ end
+
+ context 'group' do
+ it 'returns group full_path' do
+ @group = create(:group, :nested, name: 'group-name')
+
+ expect(search_history_storage_prefix).to eq(@group.full_path)
+ end
+ end
+
+ context 'dashboard' do
+ it 'returns dashboard' do
+ expect(search_history_storage_prefix).to eq("dashboard")
+ end
+ end
end
end
diff --git a/spec/helpers/storage_health_helper_spec.rb b/spec/helpers/storage_health_helper_spec.rb
deleted file mode 100644
index 874498e6338..00000000000
--- a/spec/helpers/storage_health_helper_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require 'spec_helper'
-
-describe StorageHealthHelper do
- describe '#failing_storage_health_message' do
- let(:health) do
- Gitlab::Git::Storage::Health.new(
- "<script>alert('storage name');)</script>",
- []
- )
- end
-
- it 'escapes storage names' do
- escaped_storage_name = '&lt;script&gt;alert(&#39;storage name&#39;);)&lt;/script&gt;'
-
- result = helper.failing_storage_health_message(health)
-
- expect(result).to include(escaped_storage_name)
- end
- end
-end
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index a64f8a11ef2..8662cadc7a0 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -162,42 +162,77 @@ describe SubmoduleHelper do
end
context 'submodules with relative links' do
- let(:group) { create(:group, name: "Master Project", path: "master-project") }
+ let(:group) { create(:group, name: "top group", path: "top-group") }
let(:project) { create(:project, group: group) }
- let(:commit_id) { sample_commit[:id] }
+ let(:repo) { double(:repo, project: project) }
+
+ def expect_relative_link_to_resolve_to(relative_path, expected_path)
+ allow(repo).to receive(:submodule_url_for).and_return(relative_path)
+
+ result = submodule_links(submodule_item)
+
+ expect(result).to eq([expected_path, "#{expected_path}/tree/#{submodule_item.id}"])
+ end
- it 'one level down' do
- result = relative_self_links('../test.git', commit_id, project)
- expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
+ it 'handles project under same group' do
+ expect_relative_link_to_resolve_to('../test.git', "/#{group.path}/test")
end
- it 'with trailing whitespace' do
- result = relative_self_links('../test.git ', commit_id, project)
- expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
+ it 'handles trailing whitespace' do
+ expect_relative_link_to_resolve_to('../test.git ', "/#{group.path}/test")
end
- it 'two levels down' do
- result = relative_self_links('../../test.git', commit_id, project)
- expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
+ it 'handles project under another top group' do
+ expect_relative_link_to_resolve_to('../../baz/test.git ', "/baz/test")
+ end
+
+ context 'repo path resolves to be located at root (namespace absent)' do
+ it 'returns nil' do
+ allow(repo).to receive(:submodule_url_for).and_return('../../test.git')
+
+ result = submodule_links(submodule_item)
+
+ expect(result).to eq([nil, nil])
+ end
end
- it 'one level down with namespace and repo' do
- result = relative_self_links('../foobar/test.git', commit_id, project)
- expect(result).to eq(["/foobar/test", "/foobar/test/tree/#{commit_id}"])
+ context 'repo path resolves to be located underneath current project path' do
+ it 'returns nil because it is not possible to have repo nested under another repo' do
+ allow(repo).to receive(:submodule_url_for).and_return('./test.git')
+
+ result = submodule_links(submodule_item)
+
+ expect(result).to eq([nil, nil])
+ end
end
- it 'two levels down with namespace and repo' do
- result = relative_self_links('../foobar/baz/test.git', commit_id, project)
- expect(result).to eq(["/baz/test", "/baz/test/tree/#{commit_id}"])
+ context 'subgroup' do
+ let(:sub_group) { create(:group, parent: group, name: "sub group", path: "sub-group") }
+ let(:sub_project) { create(:project, group: sub_group) }
+
+ context 'project in sub group' do
+ let(:project) { sub_project }
+
+ it "handles referencing ancestor group's project" do
+ expect_relative_link_to_resolve_to('../../../top-group/test.git', "/#{group.path}/test")
+ end
+ end
+
+ it "handles referencing descendent group's project" do
+ expect_relative_link_to_resolve_to('../sub-group/test.git', "/top-group/sub-group/test")
+ end
+
+ it "handles referencing another top group's project" do
+ expect_relative_link_to_resolve_to('../../frontend/css/test.git', "/frontend/css/test")
+ end
end
context 'personal project' do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
- it 'one level down with personal project' do
- result = relative_self_links('../test.git', commit_id, project)
- expect(result).to eq(["/#{user.username}/test", "/#{user.username}/test/tree/#{commit_id}"])
+ it 'handles referencing another personal project' do
+ expect_relative_link_to_resolve_to('../test.git', "/#{user.username}/test")
end
end
end
diff --git a/spec/helpers/tab_helper_spec.rb b/spec/helpers/tab_helper_spec.rb
index b473c0a7416..9abf63d4bd4 100644
--- a/spec/helpers/tab_helper_spec.rb
+++ b/spec/helpers/tab_helper_spec.rb
@@ -9,31 +9,71 @@ describe TabHelper do
allow(self).to receive(:action_name).and_return('foo')
end
- it "captures block output" do
- expect(nav_link { "Testing Blocks" }).to match(/Testing Blocks/)
+ context 'with the content of the li' do
+ it "captures block output" do
+ expect(nav_link { "Testing Blocks" }).to match(/Testing Blocks/)
+ end
end
- it "performs checks on the current controller" do
- expect(nav_link(controller: :foo)).to match(/<li class="active">/)
- expect(nav_link(controller: :bar)).not_to match(/active/)
- expect(nav_link(controller: [:foo, :bar])).to match(/active/)
- end
+ context 'with controller param' do
+ it "performs checks on the current controller" do
+ expect(nav_link(controller: :foo)).to match(/<li class="active">/)
+ expect(nav_link(controller: :bar)).not_to match(/active/)
+ expect(nav_link(controller: [:foo, :bar])).to match(/active/)
+ end
+
+ context 'with action param' do
+ it "performs checks on both controller and action when both are present" do
+ expect(nav_link(controller: :bar, action: :foo)).not_to match(/active/)
+ expect(nav_link(controller: :foo, action: :bar)).not_to match(/active/)
+ expect(nav_link(controller: :foo, action: :foo)).to match(/active/)
+ end
+ end
+
+ context 'with namespace in path notation' do
+ before do
+ allow(controller).to receive(:controller_path).and_return('bar/foo')
+ end
- it "performs checks on the current action" do
- expect(nav_link(action: :foo)).to match(/<li class="active">/)
- expect(nav_link(action: :bar)).not_to match(/active/)
- expect(nav_link(action: [:foo, :bar])).to match(/active/)
+ it 'performs checks on both controller and namespace' do
+ expect(nav_link(controller: 'foo/foo')).not_to match(/active/)
+ expect(nav_link(controller: 'bar/foo')).to match(/active/)
+ end
+
+ context 'with action param' do
+ it "performs checks on both namespace, controller and action when they are all present" do
+ expect(nav_link(controller: 'foo/foo', action: :foo)).not_to match(/active/)
+ expect(nav_link(controller: 'bar/foo', action: :bar)).not_to match(/active/)
+ expect(nav_link(controller: 'bar/foo', action: :foo)).to match(/active/)
+ end
+ end
+ end
end
- it "performs checks on both controller and action when both are present" do
- expect(nav_link(controller: :bar, action: :foo)).not_to match(/active/)
- expect(nav_link(controller: :foo, action: :bar)).not_to match(/active/)
- expect(nav_link(controller: :foo, action: :foo)).to match(/active/)
+ context 'with action param' do
+ it "performs checks on the current action" do
+ expect(nav_link(action: :foo)).to match(/<li class="active">/)
+ expect(nav_link(action: :bar)).not_to match(/active/)
+ expect(nav_link(action: [:foo, :bar])).to match(/active/)
+ end
end
- it "accepts a path shorthand" do
- expect(nav_link(path: 'foo#bar')).not_to match(/active/)
- expect(nav_link(path: 'foo#foo')).to match(/active/)
+ context 'with path param' do
+ it "accepts a path shorthand" do
+ expect(nav_link(path: 'foo#bar')).not_to match(/active/)
+ expect(nav_link(path: 'foo#foo')).to match(/active/)
+ end
+
+ context 'with namespace' do
+ before do
+ allow(controller).to receive(:controller_path).and_return('bar/foo')
+ end
+
+ it 'accepts a path shorthand with namespace' do
+ expect(nav_link(path: 'bar/foo#foo')).to match(/active/)
+ expect(nav_link(path: 'foo/foo#foo')).not_to match(/active/)
+ end
+ end
end
it "passes extra html options to the list element" do
diff --git a/spec/helpers/time_helper_spec.rb b/spec/helpers/time_helper_spec.rb
index 0b371d69ecf..8bf378549fe 100644
--- a/spec/helpers/time_helper_spec.rb
+++ b/spec/helpers/time_helper_spec.rb
@@ -20,18 +20,19 @@ describe TimeHelper do
end
describe "#duration_in_numbers" do
- it "returns minutes and seconds" do
- durations_and_expectations = {
- 100 => "01:40",
- 121 => "02:01",
- 3721 => "01:02:01",
- 0 => "00:00",
- 42 => "00:42"
- }
+ using RSpec::Parameterized::TableSyntax
- durations_and_expectations.each do |duration, expectation|
- expect(duration_in_numbers(duration)).to eq(expectation)
- end
+ where(:duration, :formatted_string) do
+ 0 | "00:00"
+ 1.second | "00:01"
+ 42.seconds | "00:42"
+ 2.minutes + 1.second | "02:01"
+ 3.hours + 2.minutes + 1.second | "03:02:01"
+ 30.hours | "30:00:00"
+ end
+
+ with_them do
+ it { expect(duration_in_numbers(duration)).to eq formatted_string }
end
end
end
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index ffdf6561a53..ab4566e261b 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe TreeHelper do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
- let(:sha) { 'ce369011c189f62c815f5971d096b26759bab0d1' }
+ let(:sha) { 'c1c67abbaf91f624347bb3ae96eabe3a1b742478' }
describe '.render_tree' do
before do
@@ -32,6 +32,49 @@ describe TreeHelper do
end
end
+ describe '.fast_project_blob_path' do
+ it 'generates the same path as project_blob_path' do
+ blob_path = repository.tree(sha, 'with space').entries.first.path
+ fast_path = fast_project_blob_path(project, blob_path)
+ std_path = project_blob_path(project, blob_path)
+
+ expect(fast_path).to eq(std_path)
+ end
+
+ it 'generates the same path with encoded file names' do
+ tree = repository.tree(sha, 'encoding')
+ blob_path = tree.entries.find { |entry| entry.path == 'encoding/テスト.txt' }.path
+ fast_path = fast_project_blob_path(project, blob_path)
+ std_path = project_blob_path(project, blob_path)
+
+ expect(fast_path).to eq(std_path)
+ end
+
+ it 'respects a configured relative URL' do
+ allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
+ blob_path = repository.tree(sha, '').entries.first.path
+ fast_path = fast_project_blob_path(project, blob_path)
+
+ expect(fast_path).to start_with('/gitlab/root')
+ end
+ end
+
+ describe '.fast_project_tree_path' do
+ let(:tree_path) { repository.tree(sha, 'with space').path }
+ let(:fast_path) { fast_project_tree_path(project, tree_path) }
+ let(:std_path) { project_tree_path(project, tree_path) }
+
+ it 'generates the same path as project_tree_path' do
+ expect(fast_path).to eq(std_path)
+ end
+
+ it 'respects a configured relative URL' do
+ allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
+
+ expect(fast_path).to start_with('/gitlab/root')
+ end
+ end
+
describe 'flatten_tree' do
let(:tree) { repository.tree(sha, 'files') }
let(:root_path) { 'files' }
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index b079802cb81..34d9115a1f6 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -42,6 +42,30 @@ describe UsersHelper do
end
end
+ describe '#user_internal_regex_data' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_default_external, :user_default_internal_regex, :result) do
+ false | nil | { user_internal_regex_pattern: nil, user_internal_regex_options: nil }
+ false | '' | { user_internal_regex_pattern: nil, user_internal_regex_options: nil }
+ false | 'mockRegexPattern' | { user_internal_regex_pattern: nil, user_internal_regex_options: nil }
+ true | nil | { user_internal_regex_pattern: nil, user_internal_regex_options: nil }
+ true | '' | { user_internal_regex_pattern: nil, user_internal_regex_options: nil }
+ true | 'mockRegexPattern' | { user_internal_regex_pattern: 'mockRegexPattern', user_internal_regex_options: 'gi' }
+ end
+
+ with_them do
+ before do
+ stub_application_setting(user_default_external: user_default_external)
+ stub_application_setting(user_default_internal_regex: user_default_internal_regex)
+ end
+
+ subject { helper.user_internal_regex_data }
+
+ it { is_expected.to eq(result) }
+ end
+ end
+
describe '#current_user_menu_items' do
subject(:items) { helper.current_user_menu_items }
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index a3be222b7bd..e565ac8c530 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe VisibilityLevelHelper do
+ include ProjectForksHelper
+
let(:project) { build(:project) }
let(:group) { build(:group) }
let(:personal_snippet) { build(:personal_snippet) }
@@ -83,13 +85,13 @@ describe VisibilityLevelHelper do
describe "disallowed_visibility_level?" do
describe "forks" do
- let(:project) { create(:project, :internal) }
- let(:fork_project) { create(:project, forked_from_project: project) }
+ let(:project) { create(:project, :internal) }
+ let(:forked_project) { fork_project(project) }
it "disallows levels" do
- expect(disallowed_visibility_level?(fork_project, Gitlab::VisibilityLevel::PUBLIC)).to be_truthy
- expect(disallowed_visibility_level?(fork_project, Gitlab::VisibilityLevel::INTERNAL)).to be_falsey
- expect(disallowed_visibility_level?(fork_project, Gitlab::VisibilityLevel::PRIVATE)).to be_falsey
+ expect(disallowed_visibility_level?(forked_project, Gitlab::VisibilityLevel::PUBLIC)).to be_truthy
+ expect(disallowed_visibility_level?(forked_project, Gitlab::VisibilityLevel::INTERNAL)).to be_falsey
+ expect(disallowed_visibility_level?(forked_project, Gitlab::VisibilityLevel::PRIVATE)).to be_falsey
end
end
diff --git a/spec/javascripts/.eslintrc.yml b/spec/javascripts/.eslintrc.yml
index 5525c9f5bd0..b863156b57c 100644
--- a/spec/javascripts/.eslintrc.yml
+++ b/spec/javascripts/.eslintrc.yml
@@ -35,3 +35,5 @@ rules:
- error
- ignore:
- 'fixtures/blob'
+ # Temporarily disabled to facilitate an upgrade to eslint-plugin-jasmine
+ jasmine/prefer-toHaveBeenCalledWith: off
diff --git a/spec/javascripts/ajax_loading_spinner_spec.js b/spec/javascripts/ajax_loading_spinner_spec.js
index 261375d3a0e..9389fc94f17 100644
--- a/spec/javascripts/ajax_loading_spinner_spec.js
+++ b/spec/javascripts/ajax_loading_spinner_spec.js
@@ -10,8 +10,8 @@ describe('Ajax Loading Spinner', () => {
AjaxLoadingSpinner.init();
});
- it('change current icon with spinner icon and disable link while waiting ajax response', (done) => {
- spyOn($, 'ajax').and.callFake((req) => {
+ it('change current icon with spinner icon and disable link while waiting ajax response', done => {
+ spyOn($, 'ajax').and.callFake(req => {
const xhr = new XMLHttpRequest();
const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner');
const icon = ajaxLoadingSpinner.querySelector('i');
@@ -33,8 +33,8 @@ describe('Ajax Loading Spinner', () => {
document.querySelector('.js-ajax-loading-spinner').click();
});
- it('use original icon again and enabled the link after complete the ajax request', (done) => {
- spyOn($, 'ajax').and.callFake((req) => {
+ it('use original icon again and enabled the link after complete the ajax request', done => {
+ spyOn($, 'ajax').and.callFake(req => {
const xhr = new XMLHttpRequest();
const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner');
@@ -42,6 +42,7 @@ describe('Ajax Loading Spinner', () => {
req.complete({});
const icon = ajaxLoadingSpinner.querySelector('i');
+
expect(icon).toHaveClass('fa-trash-o');
expect(icon).not.toHaveClass('fa-spinner');
expect(icon).not.toHaveClass('fa-spin');
diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js
index 54cb6d84109..091edf13cfe 100644
--- a/spec/javascripts/api_spec.js
+++ b/spec/javascripts/api_spec.js
@@ -250,71 +250,45 @@ describe('Api', () => {
});
});
- describe('licenseText', () => {
- it('fetches a license text', done => {
- const licenseKey = "driver's license";
- const data = { unused: 'option' };
- const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/templates/licenses/${licenseKey}`;
+ describe('issueTemplate', () => {
+ it('fetches an issue template', done => {
+ const namespace = 'some namespace';
+ const project = 'some project';
+ const templateKey = ' template #%?.key ';
+ const templateType = 'template type';
+ const expectedUrl = `${dummyUrlRoot}/${namespace}/${project}/templates/${templateType}/${encodeURIComponent(
+ templateKey,
+ )}`;
mock.onGet(expectedUrl).reply(200, 'test');
- Api.licenseText(licenseKey, data, response => {
+ Api.issueTemplate(namespace, project, templateKey, templateType, (error, response) => {
expect(response).toBe('test');
done();
});
});
});
- describe('gitignoreText', () => {
- it('fetches a gitignore text', done => {
- const gitignoreKey = 'ignore git';
- const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/templates/gitignores/${gitignoreKey}`;
- mock.onGet(expectedUrl).reply(200, 'test');
-
- Api.gitignoreText(gitignoreKey, response => {
- expect(response).toBe('test');
- done();
- });
- });
- });
+ describe('projectTemplates', () => {
+ it('fetches a list of templates', done => {
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/gitlab-org%2Fgitlab-ce/templates/licenses`;
- describe('gitlabCiYml', () => {
- it('fetches a .gitlab-ci.yml', done => {
- const gitlabCiYmlKey = 'Y CI ML';
- const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/templates/gitlab_ci_ymls/${gitlabCiYmlKey}`;
mock.onGet(expectedUrl).reply(200, 'test');
- Api.gitlabCiYml(gitlabCiYmlKey, response => {
+ Api.projectTemplates('gitlab-org/gitlab-ce', 'licenses', {}, response => {
expect(response).toBe('test');
done();
});
});
});
- describe('dockerfileYml', () => {
- it('fetches a Dockerfile', done => {
- const dockerfileYmlKey = 'a giant whale';
- const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/templates/dockerfiles/${dockerfileYmlKey}`;
- mock.onGet(expectedUrl).reply(200, 'test');
-
- Api.dockerfileYml(dockerfileYmlKey, response => {
- expect(response).toBe('test');
- done();
- });
- });
- });
+ describe('projectTemplate', () => {
+ it('fetches a single template', done => {
+ const data = { unused: 'option' };
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/gitlab-org%2Fgitlab-ce/templates/licenses/test%20license`;
- describe('issueTemplate', () => {
- it('fetches an issue template', done => {
- const namespace = 'some namespace';
- const project = 'some project';
- const templateKey = ' template #%?.key ';
- const templateType = 'template type';
- const expectedUrl = `${dummyUrlRoot}/${namespace}/${project}/templates/${templateType}/${encodeURIComponent(
- templateKey,
- )}`;
mock.onGet(expectedUrl).reply(200, 'test');
- Api.issueTemplate(namespace, project, templateKey, templateType, (error, response) => {
+ Api.projectTemplate('gitlab-org/gitlab-ce', 'licenses', 'test license', data, response => {
expect(response).toBe('test');
done();
});
diff --git a/spec/javascripts/avatar_helper_spec.js b/spec/javascripts/avatar_helper_spec.js
index b2f80678ae7..c1ef08e0f1b 100644
--- a/spec/javascripts/avatar_helper_spec.js
+++ b/spec/javascripts/avatar_helper_spec.js
@@ -21,7 +21,7 @@ describe('avatar_helper', () => {
it(`wraps around if id is bigger than ${IDENTICON_BG_COUNT}`, () => {
expect(getIdenticonBackgroundClass(IDENTICON_BG_COUNT + 4)).toEqual('bg5');
- expect(getIdenticonBackgroundClass((IDENTICON_BG_COUNT * 5) + 6)).toEqual('bg7');
+ expect(getIdenticonBackgroundClass(IDENTICON_BG_COUNT * 5 + 6)).toEqual('bg7');
});
});
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index ada26b37f4a..ce5d2022441 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -1,382 +1,400 @@
-/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-unused-expressions, no-unused-vars, prefer-template, max-len */
-
import $ from 'jquery';
import Cookies from 'js-cookie';
import loadAwardsHandler from '~/awards_handler';
-
import '~/lib/utils/common_utils';
-(function() {
- var awardsHandler, lazyAssert, urlRoot, openAndWaitForEmojiMenu;
+window.gl = window.gl || {};
+window.gon = window.gon || {};
- awardsHandler = null;
+let openAndWaitForEmojiMenu;
+let awardsHandler = null;
+const urlRoot = gon.relative_url_root;
- window.gl || (window.gl = {});
+const lazyAssert = function(done, assertFn) {
+ setTimeout(function() {
+ assertFn();
+ done();
+ // Maybe jasmine.clock here?
+ }, 333);
+};
- window.gon || (window.gon = {});
+describe('AwardsHandler', function() {
+ preloadFixtures('snippets/show.html.raw');
+ beforeEach(function(done) {
+ loadFixtures('snippets/show.html.raw');
+ loadAwardsHandler(true)
+ .then(obj => {
+ awardsHandler = obj;
+ spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb());
+ done();
+ })
+ .catch(fail);
- urlRoot = gon.relative_url_root;
+ let isEmojiMenuBuilt = false;
+ openAndWaitForEmojiMenu = function() {
+ return new Promise(resolve => {
+ if (isEmojiMenuBuilt) {
+ resolve();
+ } else {
+ $('.js-add-award')
+ .eq(0)
+ .click();
+ const $menu = $('.emoji-menu');
+ $menu.one('build-emoji-menu-finish', () => {
+ isEmojiMenuBuilt = true;
+ resolve();
+ });
+ }
+ });
+ };
+ });
- lazyAssert = function(done, assertFn) {
- return setTimeout(function() {
- assertFn();
- return done();
- // Maybe jasmine.clock here?
- }, 333);
- };
+ afterEach(function() {
+ // restore original url root value
+ gon.relative_url_root = urlRoot;
- describe('AwardsHandler', function() {
- preloadFixtures('snippets/show.html.raw');
- beforeEach(function(done) {
- loadFixtures('snippets/show.html.raw');
- loadAwardsHandler(true)
- .then(obj => {
- awardsHandler = obj;
- spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb());
- done();
- })
- .catch(fail);
+ // Undo what we did to the shared <body>
+ $('body').removeAttr('data-page');
- let isEmojiMenuBuilt = false;
- openAndWaitForEmojiMenu = function() {
- return new Promise((resolve, reject) => {
- if (isEmojiMenuBuilt) {
- resolve();
- } else {
- $('.js-add-award')
- .eq(0)
- .click();
- const $menu = $('.emoji-menu');
- $menu.one('build-emoji-menu-finish', () => {
- isEmojiMenuBuilt = true;
- resolve();
- });
- }
- });
- };
- });
- afterEach(function() {
- // restore original url root value
- gon.relative_url_root = urlRoot;
+ awardsHandler.destroy();
+ });
- // Undo what we did to the shared <body>
- $('body').removeAttr('data-page');
+ describe('::showEmojiMenu', function() {
+ it('should show emoji menu when Add emoji button clicked', function(done) {
+ $('.js-add-award')
+ .eq(0)
+ .click();
+ lazyAssert(done, function() {
+ const $emojiMenu = $('.emoji-menu');
- awardsHandler.destroy();
- });
- describe('::showEmojiMenu', function() {
- it('should show emoji menu when Add emoji button clicked', function(done) {
- $('.js-add-award')
- .eq(0)
- .click();
- return lazyAssert(done, function() {
- var $emojiMenu;
- $emojiMenu = $('.emoji-menu');
- expect($emojiMenu.length).toBe(1);
- expect($emojiMenu.hasClass('is-visible')).toBe(true);
- expect($emojiMenu.find('.js-emoji-menu-search').length).toBe(1);
- return expect($('.js-awards-block.current').length).toBe(1);
- });
- });
- it('should also show emoji menu for the smiley icon in notes', function(done) {
- $('.js-add-award.note-action-button').click();
- return lazyAssert(done, function() {
- var $emojiMenu = $('.emoji-menu');
- return expect($emojiMenu.length).toBe(1);
- });
- });
- it('should remove emoji menu when body is clicked', function(done) {
- $('.js-add-award')
- .eq(0)
- .click();
- return lazyAssert(done, function() {
- var $emojiMenu;
- $emojiMenu = $('.emoji-menu');
- $('body').click();
- expect($emojiMenu.length).toBe(1);
- expect($emojiMenu.hasClass('is-visible')).toBe(false);
- return expect($('.js-awards-block.current').length).toBe(0);
- });
- });
- it('should not remove emoji menu when search is clicked', function(done) {
- $('.js-add-award')
- .eq(0)
- .click();
- return lazyAssert(done, function() {
- var $emojiMenu;
- $emojiMenu = $('.emoji-menu');
- $('.emoji-search').click();
- expect($emojiMenu.length).toBe(1);
- expect($emojiMenu.hasClass('is-visible')).toBe(true);
- return expect($('.js-awards-block.current').length).toBe(1);
- });
+ expect($emojiMenu.length).toBe(1);
+ expect($emojiMenu.hasClass('is-visible')).toBe(true);
+ expect($emojiMenu.find('.js-emoji-menu-search').length).toBe(1);
+ expect($('.js-awards-block.current').length).toBe(1);
});
});
- describe('::addAwardToEmojiBar', function() {
- it('should add emoji to votes block', function() {
- var $emojiButton, $votesBlock;
- $votesBlock = $('.js-awards-block').eq(0);
- awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
- $emojiButton = $votesBlock.find('[data-name=heart]');
- expect($emojiButton.length).toBe(1);
- expect($emojiButton.next('.js-counter').text()).toBe('1');
- return expect($votesBlock.hasClass('hidden')).toBe(false);
- });
- it('should remove the emoji when we click again', function() {
- var $emojiButton, $votesBlock;
- $votesBlock = $('.js-awards-block').eq(0);
- awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
- awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
- $emojiButton = $votesBlock.find('[data-name=heart]');
- return expect($emojiButton.length).toBe(0);
- });
- return it('should decrement the emoji counter', function() {
- var $emojiButton, $votesBlock;
- $votesBlock = $('.js-awards-block').eq(0);
- awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
- $emojiButton = $votesBlock.find('[data-name=heart]');
- $emojiButton.next('.js-counter').text(5);
- awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
- expect($emojiButton.length).toBe(1);
- return expect($emojiButton.next('.js-counter').text()).toBe('4');
+ it('should also show emoji menu for the smiley icon in notes', function(done) {
+ $('.js-add-award.note-action-button').click();
+ lazyAssert(done, function() {
+ const $emojiMenu = $('.emoji-menu');
+
+ expect($emojiMenu.length).toBe(1);
});
});
- describe('::userAuthored', function() {
- it('should update tooltip to user authored title', function() {
- var $thumbsUpEmoji, $votesBlock;
- $votesBlock = $('.js-awards-block').eq(0);
- $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
- $thumbsUpEmoji.attr('data-title', 'sam');
- awardsHandler.userAuthored($thumbsUpEmoji);
- return expect($thumbsUpEmoji.data('originalTitle')).toBe(
- 'You cannot vote on your own issue, MR and note',
- );
- });
- it('should restore tooltip back to initial vote list', function() {
- var $thumbsUpEmoji, $votesBlock;
- jasmine.clock().install();
- $votesBlock = $('.js-awards-block').eq(0);
- $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
- $thumbsUpEmoji.attr('data-title', 'sam');
- awardsHandler.userAuthored($thumbsUpEmoji);
- jasmine.clock().tick(2801);
- jasmine.clock().uninstall();
- return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam');
+
+ it('should remove emoji menu when body is clicked', function(done) {
+ $('.js-add-award')
+ .eq(0)
+ .click();
+ lazyAssert(done, function() {
+ const $emojiMenu = $('.emoji-menu');
+ $('body').click();
+
+ expect($emojiMenu.length).toBe(1);
+ expect($emojiMenu.hasClass('is-visible')).toBe(false);
+ expect($('.js-awards-block.current').length).toBe(0);
});
});
- describe('::getAwardUrl', function() {
- return it('returns the url for request', function() {
- return expect(awardsHandler.getAwardUrl()).toBe(
- 'http://test.host/snippets/1/toggle_award_emoji',
- );
+
+ it('should not remove emoji menu when search is clicked', function(done) {
+ $('.js-add-award')
+ .eq(0)
+ .click();
+ lazyAssert(done, function() {
+ const $emojiMenu = $('.emoji-menu');
+ $('.emoji-search').click();
+
+ expect($emojiMenu.length).toBe(1);
+ expect($emojiMenu.hasClass('is-visible')).toBe(true);
+ expect($('.js-awards-block.current').length).toBe(1);
});
});
- describe('::addAward and ::checkMutuality', function() {
- return it('should handle :+1: and :-1: mutuality', function() {
- var $thumbsDownEmoji, $thumbsUpEmoji, $votesBlock, awardUrl;
- awardUrl = awardsHandler.getAwardUrl();
- $votesBlock = $('.js-awards-block').eq(0);
- $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
- $thumbsDownEmoji = $votesBlock.find('[data-name=thumbsdown]').parent();
- awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
- expect($thumbsUpEmoji.hasClass('active')).toBe(true);
- expect($thumbsDownEmoji.hasClass('active')).toBe(false);
- $thumbsUpEmoji.tooltip();
- $thumbsDownEmoji.tooltip();
- awardsHandler.addAward($votesBlock, awardUrl, 'thumbsdown', true);
- expect($thumbsUpEmoji.hasClass('active')).toBe(false);
- return expect($thumbsDownEmoji.hasClass('active')).toBe(true);
- });
+ });
+
+ describe('::addAwardToEmojiBar', function() {
+ it('should add emoji to votes block', function() {
+ const $votesBlock = $('.js-awards-block').eq(0);
+ awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
+ const $emojiButton = $votesBlock.find('[data-name=heart]');
+
+ expect($emojiButton.length).toBe(1);
+ expect($emojiButton.next('.js-counter').text()).toBe('1');
+ expect($votesBlock.hasClass('hidden')).toBe(false);
});
- describe('::removeEmoji', function() {
- return it('should remove emoji', function() {
- var $votesBlock, awardUrl;
- awardUrl = awardsHandler.getAwardUrl();
- $votesBlock = $('.js-awards-block').eq(0);
- awardsHandler.addAward($votesBlock, awardUrl, 'fire', false);
- expect($votesBlock.find('[data-name=fire]').length).toBe(1);
- awardsHandler.removeEmoji($votesBlock.find('[data-name=fire]').closest('button'));
- return expect($votesBlock.find('[data-name=fire]').length).toBe(0);
- });
+
+ it('should remove the emoji when we click again', function() {
+ const $votesBlock = $('.js-awards-block').eq(0);
+ awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
+ awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
+ const $emojiButton = $votesBlock.find('[data-name=heart]');
+
+ expect($emojiButton.length).toBe(0);
});
- describe('::addYouToUserList', function() {
- it('should prepend "You" to the award tooltip', function() {
- var $thumbsUpEmoji, $votesBlock, awardUrl;
- awardUrl = awardsHandler.getAwardUrl();
- $votesBlock = $('.js-awards-block').eq(0);
- $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
- $thumbsUpEmoji.attr('data-title', 'sam, jerry, max, and andy');
- awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
- $thumbsUpEmoji.tooltip();
- return expect($thumbsUpEmoji.data('originalTitle')).toBe('You, sam, jerry, max, and andy');
- });
- return it('handles the special case where "You" is not cleanly comma seperated', function() {
- var $thumbsUpEmoji, $votesBlock, awardUrl;
- awardUrl = awardsHandler.getAwardUrl();
- $votesBlock = $('.js-awards-block').eq(0);
- $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
- $thumbsUpEmoji.attr('data-title', 'sam');
- awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
- $thumbsUpEmoji.tooltip();
- return expect($thumbsUpEmoji.data('originalTitle')).toBe('You and sam');
- });
+
+ it('should decrement the emoji counter', function() {
+ const $votesBlock = $('.js-awards-block').eq(0);
+ awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
+ const $emojiButton = $votesBlock.find('[data-name=heart]');
+ $emojiButton.next('.js-counter').text(5);
+ awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
+
+ expect($emojiButton.length).toBe(1);
+ expect($emojiButton.next('.js-counter').text()).toBe('4');
});
- describe('::removeYouToUserList', function() {
- it('removes "You" from the front of the tooltip', function() {
- var $thumbsUpEmoji, $votesBlock, awardUrl;
- awardUrl = awardsHandler.getAwardUrl();
- $votesBlock = $('.js-awards-block').eq(0);
- $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
- $thumbsUpEmoji.attr('data-title', 'You, sam, jerry, max, and andy');
- $thumbsUpEmoji.addClass('active');
- awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
- $thumbsUpEmoji.tooltip();
- return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam, jerry, max, and andy');
- });
- return it('handles the special case where "You" is not cleanly comma seperated', function() {
- var $thumbsUpEmoji, $votesBlock, awardUrl;
- awardUrl = awardsHandler.getAwardUrl();
- $votesBlock = $('.js-awards-block').eq(0);
- $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
- $thumbsUpEmoji.attr('data-title', 'You and sam');
- $thumbsUpEmoji.addClass('active');
- awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
- $thumbsUpEmoji.tooltip();
- return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam');
- });
+ });
+
+ describe('::userAuthored', function() {
+ it('should update tooltip to user authored title', function() {
+ const $votesBlock = $('.js-awards-block').eq(0);
+ const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
+ $thumbsUpEmoji.attr('data-title', 'sam');
+ awardsHandler.userAuthored($thumbsUpEmoji);
+
+ expect($thumbsUpEmoji.data('originalTitle')).toBe(
+ 'You cannot vote on your own issue, MR and note',
+ );
});
- describe('::searchEmojis', () => {
- it('should filter the emoji', function(done) {
- return openAndWaitForEmojiMenu()
- .then(() => {
- expect($('[data-name=angel]').is(':visible')).toBe(true);
- expect($('[data-name=anger]').is(':visible')).toBe(true);
- awardsHandler.searchEmojis('ali');
- expect($('[data-name=angel]').is(':visible')).toBe(false);
- expect($('[data-name=anger]').is(':visible')).toBe(false);
- expect($('[data-name=alien]').is(':visible')).toBe(true);
- expect($('.js-emoji-menu-search').val()).toBe('ali');
- })
- .then(done)
- .catch(err => {
- done.fail(`Failed to open and build emoji menu: ${err.message}`);
- });
- });
- it('should clear the search when searching for nothing', function(done) {
- return openAndWaitForEmojiMenu()
- .then(() => {
- awardsHandler.searchEmojis('ali');
- expect($('[data-name=angel]').is(':visible')).toBe(false);
- expect($('[data-name=anger]').is(':visible')).toBe(false);
- expect($('[data-name=alien]').is(':visible')).toBe(true);
- awardsHandler.searchEmojis('');
- expect($('[data-name=angel]').is(':visible')).toBe(true);
- expect($('[data-name=anger]').is(':visible')).toBe(true);
- expect($('[data-name=alien]').is(':visible')).toBe(true);
- expect($('.js-emoji-menu-search').val()).toBe('');
- })
- .then(done)
- .catch(err => {
- done.fail(`Failed to open and build emoji menu: ${err.message}`);
- });
+
+ it('should restore tooltip back to initial vote list', function() {
+ jasmine.clock().install();
+ const $votesBlock = $('.js-awards-block').eq(0);
+ const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
+ $thumbsUpEmoji.attr('data-title', 'sam');
+ awardsHandler.userAuthored($thumbsUpEmoji);
+ jasmine.clock().tick(2801);
+ jasmine.clock().uninstall();
+
+ expect($thumbsUpEmoji.data('originalTitle')).toBe('sam');
+ });
+ });
+
+ describe('::getAwardUrl', function() {
+ it('returns the url for request', function() {
+ expect(awardsHandler.getAwardUrl()).toBe('http://test.host/snippets/1/toggle_award_emoji');
+ });
+ });
+
+ describe('::addAward and ::checkMutuality', function() {
+ it('should handle :+1: and :-1: mutuality', function() {
+ const awardUrl = awardsHandler.getAwardUrl();
+ const $votesBlock = $('.js-awards-block').eq(0);
+ const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
+ const $thumbsDownEmoji = $votesBlock.find('[data-name=thumbsdown]').parent();
+ awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
+
+ expect($thumbsUpEmoji.hasClass('active')).toBe(true);
+ expect($thumbsDownEmoji.hasClass('active')).toBe(false);
+ $thumbsUpEmoji.tooltip();
+ $thumbsDownEmoji.tooltip();
+ awardsHandler.addAward($votesBlock, awardUrl, 'thumbsdown', true);
+
+ expect($thumbsUpEmoji.hasClass('active')).toBe(false);
+ expect($thumbsDownEmoji.hasClass('active')).toBe(true);
+ });
+ });
+
+ describe('::removeEmoji', function() {
+ it('should remove emoji', function() {
+ const awardUrl = awardsHandler.getAwardUrl();
+ const $votesBlock = $('.js-awards-block').eq(0);
+ awardsHandler.addAward($votesBlock, awardUrl, 'fire', false);
+
+ expect($votesBlock.find('[data-name=fire]').length).toBe(1);
+ awardsHandler.removeEmoji($votesBlock.find('[data-name=fire]').closest('button'));
+
+ expect($votesBlock.find('[data-name=fire]').length).toBe(0);
+ });
+ });
+
+ describe('::addYouToUserList', function() {
+ it('should prepend "You" to the award tooltip', function() {
+ const awardUrl = awardsHandler.getAwardUrl();
+ const $votesBlock = $('.js-awards-block').eq(0);
+ const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
+ $thumbsUpEmoji.attr('data-title', 'sam, jerry, max, and andy');
+ awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
+ $thumbsUpEmoji.tooltip();
+
+ expect($thumbsUpEmoji.data('originalTitle')).toBe('You, sam, jerry, max, and andy');
+ });
+
+ it('handles the special case where "You" is not cleanly comma separated', function() {
+ const awardUrl = awardsHandler.getAwardUrl();
+ const $votesBlock = $('.js-awards-block').eq(0);
+ const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
+ $thumbsUpEmoji.attr('data-title', 'sam');
+ awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
+ $thumbsUpEmoji.tooltip();
+
+ expect($thumbsUpEmoji.data('originalTitle')).toBe('You and sam');
+ });
+ });
+
+ describe('::removeYouToUserList', function() {
+ it('removes "You" from the front of the tooltip', function() {
+ const awardUrl = awardsHandler.getAwardUrl();
+ const $votesBlock = $('.js-awards-block').eq(0);
+ const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
+ $thumbsUpEmoji.attr('data-title', 'You, sam, jerry, max, and andy');
+ $thumbsUpEmoji.addClass('active');
+ awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
+ $thumbsUpEmoji.tooltip();
+
+ expect($thumbsUpEmoji.data('originalTitle')).toBe('sam, jerry, max, and andy');
+ });
+
+ it('handles the special case where "You" is not cleanly comma separated', function() {
+ const awardUrl = awardsHandler.getAwardUrl();
+ const $votesBlock = $('.js-awards-block').eq(0);
+ const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
+ $thumbsUpEmoji.attr('data-title', 'You and sam');
+ $thumbsUpEmoji.addClass('active');
+ awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
+ $thumbsUpEmoji.tooltip();
+
+ expect($thumbsUpEmoji.data('originalTitle')).toBe('sam');
+ });
+ });
+
+ describe('::searchEmojis', () => {
+ it('should filter the emoji', function(done) {
+ openAndWaitForEmojiMenu()
+ .then(() => {
+ expect($('[data-name=angel]').is(':visible')).toBe(true);
+ expect($('[data-name=anger]').is(':visible')).toBe(true);
+ awardsHandler.searchEmojis('ali');
+
+ expect($('[data-name=angel]').is(':visible')).toBe(false);
+ expect($('[data-name=anger]').is(':visible')).toBe(false);
+ expect($('[data-name=alien]').is(':visible')).toBe(true);
+ expect($('.js-emoji-menu-search').val()).toBe('ali');
+ })
+ .then(done)
+ .catch(err => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
+ });
+ });
+
+ it('should clear the search when searching for nothing', function(done) {
+ openAndWaitForEmojiMenu()
+ .then(() => {
+ awardsHandler.searchEmojis('ali');
+
+ expect($('[data-name=angel]').is(':visible')).toBe(false);
+ expect($('[data-name=anger]').is(':visible')).toBe(false);
+ expect($('[data-name=alien]').is(':visible')).toBe(true);
+ awardsHandler.searchEmojis('');
+
+ expect($('[data-name=angel]').is(':visible')).toBe(true);
+ expect($('[data-name=anger]').is(':visible')).toBe(true);
+ expect($('[data-name=alien]').is(':visible')).toBe(true);
+ expect($('.js-emoji-menu-search').val()).toBe('');
+ })
+ .then(done)
+ .catch(err => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
+ });
+ });
+ });
+
+ describe('emoji menu', function() {
+ const emojiSelector = '[data-name="sunglasses"]';
+ const openEmojiMenuAndAddEmoji = function() {
+ return openAndWaitForEmojiMenu().then(() => {
+ const $menu = $('.emoji-menu');
+ const $block = $('.js-awards-block');
+ const $emoji = $menu.find(`.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`);
+
+ expect($emoji.length).toBe(1);
+ expect($block.find(emojiSelector).length).toBe(0);
+ $emoji.click();
+
+ expect($menu.hasClass('.is-visible')).toBe(false);
+ expect($block.find(emojiSelector).length).toBe(1);
});
+ };
+
+ it('should add selected emoji to awards block', function(done) {
+ openEmojiMenuAndAddEmoji()
+ .then(done)
+ .catch(err => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
+ });
});
- describe('emoji menu', function() {
- const emojiSelector = '[data-name="sunglasses"]';
- const openEmojiMenuAndAddEmoji = function() {
- return openAndWaitForEmojiMenu().then(() => {
- const $menu = $('.emoji-menu');
+ it('should remove already selected emoji', function(done) {
+ openEmojiMenuAndAddEmoji()
+ .then(() => {
+ $('.js-add-award')
+ .eq(0)
+ .click();
const $block = $('.js-awards-block');
- const $emoji = $menu.find('.emoji-menu-list:not(.frequent-emojis) ' + emojiSelector);
+ const $emoji = $('.emoji-menu').find(
+ `.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`,
+ );
+ $emoji.click();
- expect($emoji.length).toBe(1);
expect($block.find(emojiSelector).length).toBe(0);
- $emoji.click();
- expect($menu.hasClass('.is-visible')).toBe(false);
- expect($block.find(emojiSelector).length).toBe(1);
+ })
+ .then(done)
+ .catch(err => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
});
- };
- it('should add selected emoji to awards block', function(done) {
- return openEmojiMenuAndAddEmoji()
- .then(done)
- .catch(err => {
- done.fail(`Failed to open and build emoji menu: ${err.message}`);
- });
- });
- it('should remove already selected emoji', function(done) {
- return openEmojiMenuAndAddEmoji()
- .then(() => {
- $('.js-add-award')
- .eq(0)
- .click();
- const $block = $('.js-awards-block');
- const $emoji = $('.emoji-menu').find(
- `.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`,
- );
- $emoji.click();
- expect($block.find(emojiSelector).length).toBe(0);
- })
- .then(done)
- .catch(err => {
- done.fail(`Failed to open and build emoji menu: ${err.message}`);
- });
- });
});
+ });
- describe('frequently used emojis', function() {
- beforeEach(() => {
- // Clear it out
- Cookies.set('frequently_used_emojis', '');
- });
+ describe('frequently used emojis', function() {
+ beforeEach(() => {
+ // Clear it out
+ Cookies.set('frequently_used_emojis', '');
+ });
- it('shouldn\'t have any "Frequently used" heading if no frequently used emojis', function(done) {
- return openAndWaitForEmojiMenu()
- .then(() => {
- const emojiMenu = document.querySelector('.emoji-menu');
- Array.prototype.forEach.call(emojiMenu.querySelectorAll('.emoji-menu-title'), title => {
- expect(title.textContent.trim().toLowerCase()).not.toBe('frequently used');
- });
- })
- .then(done)
- .catch(err => {
- done.fail(`Failed to open and build emoji menu: ${err.message}`);
+ it('shouldn\'t have any "Frequently used" heading if no frequently used emojis', function(done) {
+ return openAndWaitForEmojiMenu()
+ .then(() => {
+ const emojiMenu = document.querySelector('.emoji-menu');
+ Array.prototype.forEach.call(emojiMenu.querySelectorAll('.emoji-menu-title'), title => {
+ expect(title.textContent.trim().toLowerCase()).not.toBe('frequently used');
});
- });
+ })
+ .then(done)
+ .catch(err => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
+ });
+ });
- it('should have any frequently used section when there are frequently used emojis', function(done) {
- awardsHandler.addEmojiToFrequentlyUsedList('8ball');
-
- return openAndWaitForEmojiMenu()
- .then(() => {
- const emojiMenu = document.querySelector('.emoji-menu');
- const hasFrequentlyUsedHeading = Array.prototype.some.call(
- emojiMenu.querySelectorAll('.emoji-menu-title'),
- title => title.textContent.trim().toLowerCase() === 'frequently used',
- );
-
- expect(hasFrequentlyUsedHeading).toBe(true);
- })
- .then(done)
- .catch(err => {
- done.fail(`Failed to open and build emoji menu: ${err.message}`);
- });
- });
+ it('should have any frequently used section when there are frequently used emojis', function(done) {
+ awardsHandler.addEmojiToFrequentlyUsedList('8ball');
- it('should disregard invalid frequently used emoji that are being attempted to be added', function() {
- awardsHandler.addEmojiToFrequentlyUsedList('8ball');
- awardsHandler.addEmojiToFrequentlyUsedList('invalid_emoji');
- awardsHandler.addEmojiToFrequentlyUsedList('grinning');
+ return openAndWaitForEmojiMenu()
+ .then(() => {
+ const emojiMenu = document.querySelector('.emoji-menu');
+ const hasFrequentlyUsedHeading = Array.prototype.some.call(
+ emojiMenu.querySelectorAll('.emoji-menu-title'),
+ title => title.textContent.trim().toLowerCase() === 'frequently used',
+ );
- expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']);
- });
+ expect(hasFrequentlyUsedHeading).toBe(true);
+ })
+ .then(done)
+ .catch(err => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
+ });
+ });
- it('should disregard invalid frequently used emoji already set in cookie', function() {
- Cookies.set('frequently_used_emojis', '8ball,invalid_emoji,grinning');
+ it('should disregard invalid frequently used emoji that are being attempted to be added', function() {
+ awardsHandler.addEmojiToFrequentlyUsedList('8ball');
+ awardsHandler.addEmojiToFrequentlyUsedList('invalid_emoji');
+ awardsHandler.addEmojiToFrequentlyUsedList('grinning');
- expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']);
- });
+ expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']);
+ });
+
+ it('should disregard invalid frequently used emoji already set in cookie', function() {
+ Cookies.set('frequently_used_emojis', '8ball,invalid_emoji,grinning');
+
+ expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']);
});
});
-}.call(window));
+});
diff --git a/spec/javascripts/badges/components/badge_form_spec.js b/spec/javascripts/badges/components/badge_form_spec.js
index dd21ec279cb..651ac3ba3f9 100644
--- a/spec/javascripts/badges/components/badge_form_spec.js
+++ b/spec/javascripts/badges/components/badge_form_spec.js
@@ -1,21 +1,31 @@
import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import store from '~/badges/store';
+import createEmptyBadge from '~/badges/empty_badge';
import BadgeForm from '~/badges/components/badge_form.vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { createDummyBadge } from '../dummy_badge';
+import { DUMMY_IMAGE_URL, TEST_HOST } from '../../test_constants';
+
+// avoid preview background process
+BadgeForm.methods.debouncedPreview = () => {};
describe('BadgeForm component', () => {
const Component = Vue.extend(BadgeForm);
+ let axiosMock;
let vm;
beforeEach(() => {
setFixtures(`
<div id="dummy-element"></div>
`);
+
+ axiosMock = new MockAdapter(axios);
});
afterEach(() => {
vm.$destroy();
+ axiosMock.restore();
});
describe('methods', () => {
@@ -38,93 +48,92 @@ describe('BadgeForm component', () => {
expect(vm.stopEditing).toHaveBeenCalled();
});
});
+ });
- describe('onSubmit', () => {
- describe('if isEditing is true', () => {
- beforeEach(() => {
- spyOn(vm, 'saveBadge').and.returnValue(Promise.resolve());
- store.replaceState({
- ...store.state,
- isSaving: false,
- badgeInEditForm: createDummyBadge(),
- });
- vm.isEditing = true;
- });
-
- it('returns immediately if imageUrl is empty', () => {
- store.state.badgeInEditForm.imageUrl = '';
-
- vm.onSubmit();
-
- expect(vm.saveBadge).not.toHaveBeenCalled();
- });
+ const sharedSubmitTests = submitAction => {
+ const imageUrlSelector = '#badge-image-url';
+ const findImageUrlElement = () => vm.$el.querySelector(imageUrlSelector);
+ const linkUrlSelector = '#badge-link-url';
+ const findLinkUrlElement = () => vm.$el.querySelector(linkUrlSelector);
+ const setValue = (inputElementSelector, url) => {
+ const inputElement = vm.$el.querySelector(inputElementSelector);
+ inputElement.value = url;
+ inputElement.dispatchEvent(new Event('input'));
+ };
+ const submitForm = () => {
+ const submitButton = vm.$el.querySelector('button[type="submit"]');
+ submitButton.click();
+ };
+ const expectInvalidInput = inputElementSelector => {
+ const inputElement = vm.$el.querySelector(inputElementSelector);
+
+ expect(inputElement).toBeMatchedBy(':invalid');
+ const feedbackElement = vm.$el.querySelector(`${inputElementSelector} + .invalid-feedback`);
+
+ expect(feedbackElement).toBeVisible();
+ };
- it('returns immediately if linkUrl is empty', () => {
- store.state.badgeInEditForm.linkUrl = '';
+ beforeEach(() => {
+ spyOn(vm, submitAction).and.returnValue(Promise.resolve());
+ store.replaceState({
+ ...store.state,
+ badgeInAddForm: createEmptyBadge(),
+ badgeInEditForm: createEmptyBadge(),
+ isSaving: false,
+ });
- vm.onSubmit();
+ setValue(linkUrlSelector, `${TEST_HOST}/link/url`);
+ setValue(imageUrlSelector, `${window.location.origin}${DUMMY_IMAGE_URL}`);
+ });
- expect(vm.saveBadge).not.toHaveBeenCalled();
- });
+ it('returns immediately if imageUrl is empty', () => {
+ setValue(imageUrlSelector, '');
- it('returns immediately if isSaving is true', () => {
- store.state.isSaving = true;
+ submitForm();
- vm.onSubmit();
+ expectInvalidInput(imageUrlSelector);
- expect(vm.saveBadge).not.toHaveBeenCalled();
- });
+ expect(vm[submitAction]).not.toHaveBeenCalled();
+ });
- it('calls saveBadge', () => {
- vm.onSubmit();
+ it('returns immediately if imageUrl is malformed', () => {
+ setValue(imageUrlSelector, 'not-a-url');
- expect(vm.saveBadge).toHaveBeenCalled();
- });
- });
+ submitForm();
- describe('if isEditing is false', () => {
- beforeEach(() => {
- spyOn(vm, 'addBadge').and.returnValue(Promise.resolve());
- store.replaceState({
- ...store.state,
- isSaving: false,
- badgeInAddForm: createDummyBadge(),
- });
- vm.isEditing = false;
- });
+ expectInvalidInput(imageUrlSelector);
- it('returns immediately if imageUrl is empty', () => {
- store.state.badgeInAddForm.imageUrl = '';
+ expect(vm[submitAction]).not.toHaveBeenCalled();
+ });
- vm.onSubmit();
+ it('returns immediately if linkUrl is empty', () => {
+ setValue(linkUrlSelector, '');
- expect(vm.addBadge).not.toHaveBeenCalled();
- });
+ submitForm();
- it('returns immediately if linkUrl is empty', () => {
- store.state.badgeInAddForm.linkUrl = '';
+ expectInvalidInput(linkUrlSelector);
- vm.onSubmit();
+ expect(vm[submitAction]).not.toHaveBeenCalled();
+ });
- expect(vm.addBadge).not.toHaveBeenCalled();
- });
+ it('returns immediately if linkUrl is malformed', () => {
+ setValue(linkUrlSelector, 'not-a-url');
- it('returns immediately if isSaving is true', () => {
- store.state.isSaving = true;
+ submitForm();
- vm.onSubmit();
+ expectInvalidInput(linkUrlSelector);
- expect(vm.addBadge).not.toHaveBeenCalled();
- });
+ expect(vm[submitAction]).not.toHaveBeenCalled();
+ });
- it('calls addBadge', () => {
- vm.onSubmit();
+ it(`calls ${submitAction}`, () => {
+ submitForm();
- expect(vm.addBadge).toHaveBeenCalled();
- });
- });
+ expect(findImageUrlElement()).toBeMatchedBy(':valid');
+ expect(findLinkUrlElement()).toBeMatchedBy(':valid');
+ expect(vm[submitAction]).toHaveBeenCalled();
});
- });
+ };
describe('if isEditing is false', () => {
beforeEach(() => {
@@ -138,12 +147,17 @@ describe('BadgeForm component', () => {
});
it('renders one button', () => {
- const buttons = vm.$el.querySelectorAll('.row-content-block button');
+ expect(vm.$el.querySelector('.row-content-block')).toBeNull();
+ const buttons = vm.$el.querySelectorAll('.form-group:last-of-type button');
+
expect(buttons.length).toBe(1);
const buttonAddElement = buttons[0];
+
expect(buttonAddElement).toBeVisible();
expect(buttonAddElement).toHaveText('Add badge');
});
+
+ sharedSubmitTests('addBadge');
});
describe('if isEditing is true', () => {
@@ -159,13 +173,18 @@ describe('BadgeForm component', () => {
it('renders two buttons', () => {
const buttons = vm.$el.querySelectorAll('.row-content-block button');
+
expect(buttons.length).toBe(2);
const buttonSaveElement = buttons[0];
+
expect(buttonSaveElement).toBeVisible();
expect(buttonSaveElement).toHaveText('Save changes');
const buttonCancelElement = buttons[1];
+
expect(buttonCancelElement).toBeVisible();
expect(buttonCancelElement).toHaveText('Cancel');
});
+
+ sharedSubmitTests('saveBadge');
});
});
diff --git a/spec/javascripts/badges/components/badge_list_row_spec.js b/spec/javascripts/badges/components/badge_list_row_spec.js
index 21bd00d82f0..a5b47cc5f32 100644
--- a/spec/javascripts/badges/components/badge_list_row_spec.js
+++ b/spec/javascripts/badges/components/badge_list_row_spec.js
@@ -34,6 +34,7 @@ describe('BadgeListRow component', () => {
it('renders the badge', () => {
const badgeElement = vm.$el.querySelector('.project-badge');
+
expect(badgeElement).not.toBeNull();
expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl);
});
@@ -48,11 +49,14 @@ describe('BadgeListRow component', () => {
it('shows edit and delete buttons', () => {
const buttons = vm.$el.querySelectorAll('.table-button-footer button');
+
expect(buttons).toHaveLength(2);
const buttonEditElement = buttons[0];
+
expect(buttonEditElement).toBeVisible();
expect(buttonEditElement).toHaveSpriteIcon('pencil');
const buttonDeleteElement = buttons[1];
+
expect(buttonDeleteElement).toBeVisible();
expect(buttonDeleteElement).toHaveSpriteIcon('remove');
});
@@ -91,6 +95,7 @@ describe('BadgeListRow component', () => {
it('hides edit and delete buttons', () => {
const buttons = vm.$el.querySelectorAll('.table-button-footer button');
+
expect(buttons).toHaveLength(0);
});
});
diff --git a/spec/javascripts/badges/components/badge_list_spec.js b/spec/javascripts/badges/components/badge_list_spec.js
index 02e59ae0843..536671db377 100644
--- a/spec/javascripts/badges/components/badge_list_spec.js
+++ b/spec/javascripts/badges/components/badge_list_spec.js
@@ -34,11 +34,13 @@ describe('BadgeList component', () => {
it('renders a header with the badge count', () => {
const header = vm.$el.querySelector('.card-header');
+
expect(header).toHaveText(new RegExp(`Your badges\\s+${numberOfDummyBadges}`));
});
it('renders a row for each badge', () => {
const rows = vm.$el.querySelectorAll('.gl-responsive-table-row');
+
expect(rows).toHaveLength(numberOfDummyBadges);
});
@@ -59,6 +61,7 @@ describe('BadgeList component', () => {
Vue.nextTick()
.then(() => {
const loadingIcon = vm.$el.querySelector('.fa-spinner');
+
expect(loadingIcon).toBeVisible();
})
.then(done)
diff --git a/spec/javascripts/badges/components/badge_settings_spec.js b/spec/javascripts/badges/components/badge_settings_spec.js
index 59367c85125..aca26b736ca 100644
--- a/spec/javascripts/badges/components/badge_settings_spec.js
+++ b/spec/javascripts/badges/components/badge_settings_spec.js
@@ -38,6 +38,7 @@ describe('BadgeSettings component', () => {
$(modal).on('shown.bs.modal', () => {
expect(modal).toContainText('Delete badge?');
const badgeElement = modal.querySelector('img.project-badge');
+
expect(badgeElement).not.toBe(null);
expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl);
@@ -53,14 +54,17 @@ describe('BadgeSettings component', () => {
it('displays a form to add a badge', () => {
const form = vm.$el.querySelector('form:nth-of-type(2)');
+
expect(form).not.toBe(null);
const button = form.querySelector('.btn-success');
+
expect(button).not.toBe(null);
expect(button).toHaveText(/Add badge/);
});
it('displays badge list', () => {
const badgeListElement = vm.$el.querySelector('.card');
+
expect(badgeListElement).not.toBe(null);
expect(badgeListElement).toBeVisible();
expect(badgeListElement).toContainText('Your badges');
@@ -77,17 +81,21 @@ describe('BadgeSettings component', () => {
it('displays a form to edit a badge', () => {
const form = vm.$el.querySelector('form:nth-of-type(1)');
+
expect(form).not.toBe(null);
const submitButton = form.querySelector('.btn-success');
+
expect(submitButton).not.toBe(null);
expect(submitButton).toHaveText(/Save changes/);
const cancelButton = form.querySelector('.btn-cancel');
+
expect(cancelButton).not.toBe(null);
expect(cancelButton).toHaveText(/Cancel/);
});
it('displays no badge list', () => {
const badgeListElement = vm.$el.querySelector('.card');
+
expect(badgeListElement).toBeHidden();
});
});
@@ -102,6 +110,7 @@ describe('BadgeSettings component', () => {
deleteButton.click();
const badge = store.state.badgeInModal;
+
expect(vm.deleteBadge).toHaveBeenCalledWith(badge);
});
});
diff --git a/spec/javascripts/badges/components/badge_spec.js b/spec/javascripts/badges/components/badge_spec.js
index fd1ecc9cdd8..29805408bcf 100644
--- a/spec/javascripts/badges/components/badge_spec.js
+++ b/spec/javascripts/badges/components/badge_spec.js
@@ -107,6 +107,7 @@ describe('Badge component', () => {
expect(vm.isLoading).toBe(false);
expect(vm.hasError).toBe(false);
const { badgeImage, loadingIcon, reloadButton } = findElements();
+
expect(badgeImage).toBeVisible();
expect(loadingIcon).toBeHidden();
expect(reloadButton).toBeHidden();
@@ -119,6 +120,7 @@ describe('Badge component', () => {
Vue.nextTick()
.then(() => {
const { badgeImage, loadingIcon, reloadButton } = findElements();
+
expect(badgeImage).toBeHidden();
expect(loadingIcon).toBeVisible();
expect(reloadButton).toBeHidden();
@@ -134,6 +136,7 @@ describe('Badge component', () => {
Vue.nextTick()
.then(() => {
const { badgeImage, loadingIcon, reloadButton } = findElements();
+
expect(badgeImage).toBeHidden();
expect(loadingIcon).toBeHidden();
expect(reloadButton).toBeVisible();
diff --git a/spec/javascripts/badges/dummy_badge.js b/spec/javascripts/badges/dummy_badge.js
index 6aaff21c503..f0cdaddbd33 100644
--- a/spec/javascripts/badges/dummy_badge.js
+++ b/spec/javascripts/badges/dummy_badge.js
@@ -1,8 +1,9 @@
+import _ from 'underscore';
import { PROJECT_BADGE } from '~/badges/constants';
import { DUMMY_IMAGE_URL, TEST_HOST } from 'spec/test_constants';
export const createDummyBadge = () => {
- const id = Math.floor(1000 * Math.random());
+ const id = _.uniqueId();
return {
id,
imageUrl: `${TEST_HOST}/badges/${id}/image/url`,
diff --git a/spec/javascripts/badges/store/actions_spec.js b/spec/javascripts/badges/store/actions_spec.js
index bb6263c6de4..2623465ebd6 100644
--- a/spec/javascripts/badges/store/actions_spec.js
+++ b/spec/javascripts/badges/store/actions_spec.js
@@ -94,6 +94,7 @@ describe('Badges store actions', () => {
link_url: badgeInAddForm.linkUrl,
}),
);
+
expect(dispatch.calls.allArgs()).toEqual([['requestNewBadge']]);
dispatch.calls.reset();
return [200, dummyResponse];
@@ -117,6 +118,7 @@ describe('Badges store actions', () => {
link_url: badgeInAddForm.linkUrl,
}),
);
+
expect(dispatch.calls.allArgs()).toEqual([['requestNewBadge']]);
dispatch.calls.reset();
return [500, ''];
@@ -296,6 +298,7 @@ describe('Badges store actions', () => {
.loadBadges({ state, dispatch }, dummyData)
.then(() => {
const badges = dummyReponse.map(transformBackendBadge);
+
expect(dispatch.calls.allArgs()).toEqual([['receiveLoadBadges', badges]]);
})
.then(done)
@@ -416,6 +419,7 @@ describe('Badges store actions', () => {
.then(() => {
expect(axios.get.calls.count()).toBe(1);
const url = axios.get.calls.argsFor(0)[0];
+
expect(url).toMatch(`^${dummyEndpointUrl}/render?`);
expect(url).toMatch('\\?link_url=%3Cscript%3EI%20am%20dangerous!%3C%2Fscript%3E&');
expect(url).toMatch('&image_url=%26make-sandwhich%3Dtrue$');
@@ -436,6 +440,7 @@ describe('Badges store actions', () => {
.renderBadge({ state, dispatch })
.then(() => {
const renderedBadge = transformBackendBadge(dummyReponse);
+
expect(dispatch.calls.allArgs()).toEqual([['receiveRenderedBadge', renderedBadge]]);
})
.then(done)
@@ -525,6 +530,7 @@ describe('Badges store actions', () => {
link_url: badgeInEditForm.linkUrl,
}),
);
+
expect(dispatch.calls.allArgs()).toEqual([['requestUpdatedBadge']]);
dispatch.calls.reset();
return [200, dummyResponse];
@@ -548,6 +554,7 @@ describe('Badges store actions', () => {
link_url: badgeInEditForm.linkUrl,
}),
);
+
expect(dispatch.calls.allArgs()).toEqual([['requestUpdatedBadge']]);
dispatch.calls.reset();
return [500, ''];
diff --git a/spec/javascripts/behaviors/autosize_spec.js b/spec/javascripts/behaviors/autosize_spec.js
index c411c5174fb..59abae479d4 100644
--- a/spec/javascripts/behaviors/autosize_spec.js
+++ b/spec/javascripts/behaviors/autosize_spec.js
@@ -12,6 +12,7 @@ describe('Autosize behavior', () => {
it('does not overwrite the resize property', () => {
load();
+
expect($('textarea')).toHaveCss({
resize: 'vertical',
});
diff --git a/spec/javascripts/behaviors/bind_in_out_spec.js b/spec/javascripts/behaviors/bind_in_out_spec.js
index 5ff66167718..0c214f5886a 100644
--- a/spec/javascripts/behaviors/bind_in_out_spec.js
+++ b/spec/javascripts/behaviors/bind_in_out_spec.js
@@ -1,60 +1,60 @@
import BindInOut from '~/behaviors/bind_in_out';
import ClassSpecHelper from '../helpers/class_spec_helper';
-describe('BindInOut', function () {
- describe('constructor', function () {
- beforeEach(function () {
+describe('BindInOut', function() {
+ describe('constructor', function() {
+ beforeEach(function() {
this.in = {};
this.out = {};
this.bindInOut = new BindInOut(this.in, this.out);
});
- it('should set .in', function () {
+ it('should set .in', function() {
expect(this.bindInOut.in).toBe(this.in);
});
- it('should set .out', function () {
+ it('should set .out', function() {
expect(this.bindInOut.out).toBe(this.out);
});
- it('should set .eventWrapper', function () {
+ it('should set .eventWrapper', function() {
expect(this.bindInOut.eventWrapper).toEqual({});
});
- describe('if .in is an input', function () {
- beforeEach(function () {
+ describe('if .in is an input', function() {
+ beforeEach(function() {
this.bindInOut = new BindInOut({ tagName: 'INPUT' });
});
- it('should set .eventType to keyup ', function () {
+ it('should set .eventType to keyup ', function() {
expect(this.bindInOut.eventType).toEqual('keyup');
});
});
- describe('if .in is a textarea', function () {
- beforeEach(function () {
+ describe('if .in is a textarea', function() {
+ beforeEach(function() {
this.bindInOut = new BindInOut({ tagName: 'TEXTAREA' });
});
- it('should set .eventType to keyup ', function () {
+ it('should set .eventType to keyup ', function() {
expect(this.bindInOut.eventType).toEqual('keyup');
});
});
- describe('if .in is not an input or textarea', function () {
- beforeEach(function () {
+ describe('if .in is not an input or textarea', function() {
+ beforeEach(function() {
this.bindInOut = new BindInOut({ tagName: 'SELECT' });
});
- it('should set .eventType to change ', function () {
+ it('should set .eventType to change ', function() {
expect(this.bindInOut.eventType).toEqual('change');
});
});
});
- describe('addEvents', function () {
- beforeEach(function () {
+ describe('addEvents', function() {
+ beforeEach(function() {
this.in = jasmine.createSpyObj('in', ['addEventListener']);
this.bindInOut = new BindInOut(this.in);
@@ -62,25 +62,24 @@ describe('BindInOut', function () {
this.addEvents = this.bindInOut.addEvents();
});
- it('should set .eventWrapper.updateOut', function () {
+ it('should set .eventWrapper.updateOut', function() {
expect(this.bindInOut.eventWrapper.updateOut).toEqual(jasmine.any(Function));
});
- it('should call .addEventListener', function () {
- expect(this.in.addEventListener)
- .toHaveBeenCalledWith(
- this.bindInOut.eventType,
- this.bindInOut.eventWrapper.updateOut,
- );
+ it('should call .addEventListener', function() {
+ expect(this.in.addEventListener).toHaveBeenCalledWith(
+ this.bindInOut.eventType,
+ this.bindInOut.eventWrapper.updateOut,
+ );
});
- it('should return the instance', function () {
+ it('should return the instance', function() {
expect(this.addEvents).toBe(this.bindInOut);
});
});
- describe('updateOut', function () {
- beforeEach(function () {
+ describe('updateOut', function() {
+ beforeEach(function() {
this.in = { value: 'the-value' };
this.out = { textContent: 'not-the-value' };
@@ -89,17 +88,17 @@ describe('BindInOut', function () {
this.updateOut = this.bindInOut.updateOut();
});
- it('should set .out.textContent to .in.value', function () {
+ it('should set .out.textContent to .in.value', function() {
expect(this.out.textContent).toBe(this.in.value);
});
- it('should return the instance', function () {
+ it('should return the instance', function() {
expect(this.updateOut).toBe(this.bindInOut);
});
});
- describe('removeEvents', function () {
- beforeEach(function () {
+ describe('removeEvents', function() {
+ beforeEach(function() {
this.in = jasmine.createSpyObj('in', ['removeEventListener']);
this.updateOut = () => {};
@@ -109,21 +108,20 @@ describe('BindInOut', function () {
this.removeEvents = this.bindInOut.removeEvents();
});
- it('should call .removeEventListener', function () {
- expect(this.in.removeEventListener)
- .toHaveBeenCalledWith(
- this.bindInOut.eventType,
- this.updateOut,
- );
+ it('should call .removeEventListener', function() {
+ expect(this.in.removeEventListener).toHaveBeenCalledWith(
+ this.bindInOut.eventType,
+ this.updateOut,
+ );
});
- it('should return the instance', function () {
+ it('should return the instance', function() {
expect(this.removeEvents).toBe(this.bindInOut);
});
});
- describe('initAll', function () {
- beforeEach(function () {
+ describe('initAll', function() {
+ beforeEach(function() {
this.ins = [0, 1, 2];
this.instances = [];
@@ -136,43 +134,47 @@ describe('BindInOut', function () {
ClassSpecHelper.itShouldBeAStaticMethod(BindInOut, 'initAll');
- it('should call .querySelectorAll', function () {
+ it('should call .querySelectorAll', function() {
expect(document.querySelectorAll).toHaveBeenCalledWith('*[data-bind-in]');
});
- it('should call .map', function () {
+ it('should call .map', function() {
expect(Array.prototype.map).toHaveBeenCalledWith(jasmine.any(Function));
});
- it('should call .init for each element', function () {
+ it('should call .init for each element', function() {
expect(BindInOut.init.calls.count()).toEqual(3);
});
- it('should return an array of instances', function () {
+ it('should return an array of instances', function() {
expect(this.initAll).toEqual(jasmine.any(Array));
});
});
- describe('init', function () {
- beforeEach(function () {
- spyOn(BindInOut.prototype, 'addEvents').and.callFake(function () { return this; });
- spyOn(BindInOut.prototype, 'updateOut').and.callFake(function () { return this; });
+ describe('init', function() {
+ beforeEach(function() {
+ spyOn(BindInOut.prototype, 'addEvents').and.callFake(function() {
+ return this;
+ });
+ spyOn(BindInOut.prototype, 'updateOut').and.callFake(function() {
+ return this;
+ });
this.init = BindInOut.init({}, {});
});
ClassSpecHelper.itShouldBeAStaticMethod(BindInOut, 'init');
- it('should call .addEvents', function () {
+ it('should call .addEvents', function() {
expect(BindInOut.prototype.addEvents).toHaveBeenCalled();
});
- it('should call .updateOut', function () {
+ it('should call .updateOut', function() {
expect(BindInOut.prototype.updateOut).toHaveBeenCalled();
});
- describe('if no anOut is provided', function () {
- beforeEach(function () {
+ describe('if no anOut is provided', function() {
+ beforeEach(function() {
this.anIn = { dataset: { bindIn: 'the-data-bind-in' } };
spyOn(document, 'querySelector');
@@ -180,9 +182,10 @@ describe('BindInOut', function () {
BindInOut.init(this.anIn);
});
- it('should call .querySelector', function () {
- expect(document.querySelector)
- .toHaveBeenCalledWith(`*[data-bind-out="${this.anIn.dataset.bindIn}"]`);
+ it('should call .querySelector', function() {
+ expect(document.querySelector).toHaveBeenCalledWith(
+ `*[data-bind-out="${this.anIn.dataset.bindIn}"]`,
+ );
});
});
});
diff --git a/spec/javascripts/behaviors/copy_as_gfm_spec.js b/spec/javascripts/behaviors/copy_as_gfm_spec.js
index c2db81c6ce4..cf8c1b77861 100644
--- a/spec/javascripts/behaviors/copy_as_gfm_spec.js
+++ b/spec/javascripts/behaviors/copy_as_gfm_spec.js
@@ -29,6 +29,7 @@ describe('CopyAsGFM', () => {
it('wraps pasted code when not already in code tags', () => {
spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => {
const insertedText = textFunc('This is code: ', '');
+
expect(insertedText).toEqual('`code`');
});
@@ -38,6 +39,7 @@ describe('CopyAsGFM', () => {
it('does not wrap pasted code when already in code tags', () => {
spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => {
const insertedText = textFunc('This is code: `', '`');
+
expect(insertedText).toEqual('code');
});
@@ -54,7 +56,7 @@ describe('CopyAsGFM', () => {
const fragment = document.createDocumentFragment();
const node = document.createElement('div');
node.innerHTML = html;
- Array.from(node.childNodes).forEach((item) => fragment.appendChild(item));
+ Array.from(node.childNodes).forEach(item => fragment.appendChild(item));
return fragment;
},
}),
@@ -86,6 +88,7 @@ describe('CopyAsGFM', () => {
simulateCopy();
const expectedGFM = '- List Item1\n- List Item2';
+
expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM);
});
@@ -95,6 +98,7 @@ describe('CopyAsGFM', () => {
simulateCopy();
const expectedGFM = '1. List Item1\n1. List Item2';
+
expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM);
});
});
diff --git a/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js b/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js
index f96f20ed4a5..f656b97fec2 100644
--- a/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js
+++ b/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js
@@ -13,7 +13,7 @@ describe('Unicode Support Map', () => {
spyOn(JSON, 'stringify').and.returnValue(stringSupportMap);
});
- describe('if isLocalStorageAvailable is `true`', function () {
+ describe('if isLocalStorageAvailable is `true`', function() {
beforeEach(() => {
AccessorUtilities.isLocalStorageAccessSafe.and.returnValue(true);
@@ -36,7 +36,7 @@ describe('Unicode Support Map', () => {
});
});
- describe('if isLocalStorageAvailable is `false`', function () {
+ describe('if isLocalStorageAvailable is `false`', function() {
beforeEach(() => {
AccessorUtilities.isLocalStorageAccessSafe.and.returnValue(false);
diff --git a/spec/javascripts/behaviors/markdown/highlight_current_user_spec.js b/spec/javascripts/behaviors/markdown/highlight_current_user_spec.js
new file mode 100644
index 00000000000..3305ddc412d
--- /dev/null
+++ b/spec/javascripts/behaviors/markdown/highlight_current_user_spec.js
@@ -0,0 +1,55 @@
+import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
+
+describe('highlightCurrentUser', () => {
+ let rootElement;
+ let elements;
+
+ beforeEach(() => {
+ setFixtures(`
+ <div id="dummy-root-element">
+ <div data-user="1">@first</div>
+ <div data-user="2">@second</div>
+ </div>
+ `);
+ rootElement = document.getElementById('dummy-root-element');
+ elements = rootElement.querySelectorAll('[data-user]');
+ });
+
+ describe('without current user', () => {
+ beforeEach(() => {
+ window.gon = window.gon || {};
+ window.gon.current_user_id = null;
+ });
+
+ afterEach(() => {
+ delete window.gon.current_user_id;
+ });
+
+ it('does not highlight the user', () => {
+ const initialHtml = rootElement.outerHTML;
+
+ highlightCurrentUser(elements);
+
+ expect(rootElement.outerHTML).toBe(initialHtml);
+ });
+ });
+
+ describe('with current user', () => {
+ beforeEach(() => {
+ window.gon = window.gon || {};
+ window.gon.current_user_id = 2;
+ });
+
+ afterEach(() => {
+ delete window.gon.current_user_id;
+ });
+
+ it('highlights current user', () => {
+ highlightCurrentUser(elements);
+
+ expect(elements.length).toBe(2);
+ expect(elements[0]).not.toHaveClass('current-user');
+ expect(elements[1]).toHaveClass('current-user');
+ });
+ });
+});
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js
index d8aa5c636da..681463aab66 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js
+++ b/spec/javascripts/behaviors/quick_submit_spec.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import '~/behaviors/quick_submit';
-describe('Quick Submit behavior', function () {
+describe('Quick Submit behavior', function() {
const keydownEvent = (options = { keyCode: 13, metaKey: true }) => $.Event('keydown', options);
preloadFixtures('snippets/show.html.raw');
@@ -30,6 +30,7 @@ describe('Quick Submit behavior', function () {
keyCode: 32,
}),
);
+
expect(this.spies.submit).not.toHaveBeenTriggered();
});
@@ -40,6 +41,7 @@ describe('Quick Submit behavior', function () {
metaKey: false,
}),
);
+
expect(this.spies.submit).not.toHaveBeenTriggered();
});
@@ -49,6 +51,7 @@ describe('Quick Submit behavior', function () {
repeat: true,
}),
);
+
expect(this.spies.submit).not.toHaveBeenTriggered();
});
@@ -58,12 +61,14 @@ describe('Quick Submit behavior', function () {
expect(submitButton).toBeDisabled();
});
+
it('disables button of type submit', () => {
const submitButton = $('.js-quick-submit input[type=submit]');
this.textarea.trigger(keydownEvent());
expect(submitButton).toBeDisabled();
});
+
it('only clicks one submit', () => {
const existingSubmit = $('.js-quick-submit input[type=submit]');
// Add an extra submit button
@@ -84,7 +89,8 @@ describe('Quick Submit behavior', function () {
describe('In Macintosh', () => {
it('responds to Meta+Enter', () => {
this.textarea.trigger(keydownEvent());
- return expect(this.spies.submit).toHaveBeenTriggered();
+
+ expect(this.spies.submit).toHaveBeenTriggered();
});
it('excludes other modifier keys', () => {
@@ -103,13 +109,15 @@ describe('Quick Submit behavior', function () {
shiftKey: true,
}),
);
- return expect(this.spies.submit).not.toHaveBeenTriggered();
+
+ expect(this.spies.submit).not.toHaveBeenTriggered();
});
});
} else {
it('responds to Ctrl+Enter', () => {
this.textarea.trigger(keydownEvent());
- return expect(this.spies.submit).toHaveBeenTriggered();
+
+ expect(this.spies.submit).toHaveBeenTriggered();
});
it('excludes other modifier keys', () => {
@@ -128,7 +136,8 @@ describe('Quick Submit behavior', function () {
shiftKey: true,
}),
);
- return expect(this.spies.submit).not.toHaveBeenTriggered();
+
+ expect(this.spies.submit).not.toHaveBeenTriggered();
});
}
});
diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js
index a434949b9da..1bde2bb3024 100644
--- a/spec/javascripts/behaviors/requires_input_spec.js
+++ b/spec/javascripts/behaviors/requires_input_spec.js
@@ -12,33 +12,51 @@ describe('requiresInput', () => {
it('disables submit when any field is required', () => {
$('.js-requires-input').requiresInput();
+
expect(submitButton).toBeDisabled();
});
it('enables submit when no field is required', () => {
$('*[required=required]').prop('required', false);
$('.js-requires-input').requiresInput();
+
expect(submitButton).not.toBeDisabled();
});
it('enables submit when all required fields are pre-filled', () => {
$('*[required=required]').remove();
$('.js-requires-input').requiresInput();
+
expect($('.submit')).not.toBeDisabled();
});
it('enables submit when all required fields receive input', () => {
$('.js-requires-input').requiresInput();
- $('#required1').val('input1').change();
+ $('#required1')
+ .val('input1')
+ .change();
+
expect(submitButton).toBeDisabled();
- $('#optional1').val('input1').change();
+ $('#optional1')
+ .val('input1')
+ .change();
+
expect(submitButton).toBeDisabled();
- $('#required2').val('input2').change();
- $('#required3').val('input3').change();
- $('#required4').val('input4').change();
- $('#required5').val('1').change();
+ $('#required2')
+ .val('input2')
+ .change();
+ $('#required3')
+ .val('input3')
+ .change();
+ $('#required4')
+ .val('input4')
+ .change();
+ $('#required5')
+ .val('1')
+ .change();
+
expect($('.submit')).not.toBeDisabled();
});
});
diff --git a/spec/javascripts/behaviors/secret_values_spec.js b/spec/javascripts/behaviors/secret_values_spec.js
index 95122fcf30f..5aaab093c0c 100644
--- a/spec/javascripts/behaviors/secret_values_spec.js
+++ b/spec/javascripts/behaviors/secret_values_spec.js
@@ -36,12 +36,7 @@ function setupSecretFixture(
placeholderClass = 'js-secret-value-placeholder',
) {
const wrapper = document.createElement('div');
- wrapper.innerHTML = generateFixtureMarkup(
- secrets,
- isRevealed,
- valueClass,
- placeholderClass,
- );
+ wrapper.innerHTML = generateFixtureMarkup(secrets, isRevealed, valueClass, placeholderClass);
const secretValues = new SecretValues({
container: wrapper.querySelector('.js-secret-container'),
@@ -127,11 +122,12 @@ describe('setupSecretValues', () => {
const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
expect(values.length).toEqual(3);
- values.forEach((value) => {
+ values.forEach(value => {
expect(value.classList.contains('hide')).toEqual(true);
});
+
expect(placeholders.length).toEqual(3);
- placeholders.forEach((placeholder) => {
+ placeholders.forEach(placeholder => {
expect(placeholder.classList.contains('hide')).toEqual(false);
});
});
@@ -145,22 +141,24 @@ describe('setupSecretValues', () => {
revealButton.click();
expect(values.length).toEqual(3);
- values.forEach((value) => {
+ values.forEach(value => {
expect(value.classList.contains('hide')).toEqual(false);
});
+
expect(placeholders.length).toEqual(3);
- placeholders.forEach((placeholder) => {
+ placeholders.forEach(placeholder => {
expect(placeholder.classList.contains('hide')).toEqual(true);
});
revealButton.click();
expect(values.length).toEqual(3);
- values.forEach((value) => {
+ values.forEach(value => {
expect(value.classList.contains('hide')).toEqual(true);
});
+
expect(placeholders.length).toEqual(3);
- placeholders.forEach((placeholder) => {
+ placeholders.forEach(placeholder => {
expect(placeholder.classList.contains('hide')).toEqual(false);
});
});
@@ -172,7 +170,9 @@ describe('setupSecretValues', () => {
it('should toggle values and placeholders', () => {
const wrapper = setupSecretFixture(secrets, false);
// Insert the new dynamic row
- wrapper.querySelector('.js-secret-container').insertAdjacentHTML('afterbegin', generateValueMarkup('foobarbazdynamic'));
+ wrapper
+ .querySelector('.js-secret-container')
+ .insertAdjacentHTML('afterbegin', generateValueMarkup('foobarbazdynamic'));
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
const values = wrapper.querySelectorAll('.js-secret-value');
@@ -181,22 +181,24 @@ describe('setupSecretValues', () => {
revealButton.click();
expect(values.length).toEqual(4);
- values.forEach((value) => {
+ values.forEach(value => {
expect(value.classList.contains('hide')).toEqual(false);
});
+
expect(placeholders.length).toEqual(4);
- placeholders.forEach((placeholder) => {
+ placeholders.forEach(placeholder => {
expect(placeholder.classList.contains('hide')).toEqual(true);
});
revealButton.click();
expect(values.length).toEqual(4);
- values.forEach((value) => {
+ values.forEach(value => {
expect(value.classList.contains('hide')).toEqual(true);
});
+
expect(placeholders.length).toEqual(4);
- placeholders.forEach((placeholder) => {
+ placeholders.forEach(placeholder => {
expect(placeholder.classList.contains('hide')).toEqual(false);
});
});
diff --git a/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js b/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js
new file mode 100644
index 00000000000..bc25549cbed
--- /dev/null
+++ b/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js
@@ -0,0 +1,109 @@
+import $ from 'jquery';
+import initCopyAsGFM from '~/behaviors/markdown/copy_as_gfm';
+import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
+
+initCopyAsGFM();
+
+const FORM_SELECTOR = '.js-main-target-form .js-vue-comment-form';
+
+describe('ShortcutsIssuable', function() {
+ const fixtureName = 'snippets/show.html.raw';
+ preloadFixtures(fixtureName);
+
+ beforeEach(() => {
+ loadFixtures(fixtureName);
+ $('body').append(
+ `<div class="js-main-target-form">
+ <textare class="js-vue-comment-form"></textare>
+ </div>`,
+ );
+ document.querySelector('.js-new-note-form').classList.add('js-main-target-form');
+ this.shortcut = new ShortcutsIssuable(true);
+ });
+
+ afterEach(() => {
+ $(FORM_SELECTOR).remove();
+ });
+
+ describe('replyWithSelectedText', () => {
+ // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML.
+ const stubSelection = html => {
+ window.gl.utils.getSelectedFragment = () => {
+ const node = document.createElement('div');
+ node.innerHTML = html;
+
+ return node;
+ };
+ };
+ describe('with empty selection', () => {
+ it('does not return an error', () => {
+ ShortcutsIssuable.replyWithSelectedText(true);
+
+ expect($(FORM_SELECTOR).val()).toBe('');
+ });
+
+ it('triggers `focus`', () => {
+ const spy = spyOn(document.querySelector(FORM_SELECTOR), 'focus');
+ ShortcutsIssuable.replyWithSelectedText(true);
+
+ expect(spy).toHaveBeenCalled();
+ });
+ });
+
+ describe('with any selection', () => {
+ beforeEach(() => {
+ stubSelection('<p>Selected text.</p>');
+ });
+
+ it('leaves existing input intact', () => {
+ $(FORM_SELECTOR).val('This text was already here.');
+
+ expect($(FORM_SELECTOR).val()).toBe('This text was already here.');
+
+ ShortcutsIssuable.replyWithSelectedText(true);
+
+ expect($(FORM_SELECTOR).val()).toBe('This text was already here.\n\n> Selected text.\n\n');
+ });
+
+ it('triggers `input`', () => {
+ let triggered = false;
+ $(FORM_SELECTOR).on('input', () => {
+ triggered = true;
+ });
+
+ ShortcutsIssuable.replyWithSelectedText(true);
+
+ expect(triggered).toBe(true);
+ });
+
+ it('triggers `focus`', () => {
+ const spy = spyOn(document.querySelector(FORM_SELECTOR), 'focus');
+ ShortcutsIssuable.replyWithSelectedText(true);
+
+ expect(spy).toHaveBeenCalled();
+ });
+ });
+
+ describe('with a one-line selection', () => {
+ it('quotes the selection', () => {
+ stubSelection('<p>This text has been selected.</p>');
+ ShortcutsIssuable.replyWithSelectedText(true);
+
+ expect($(FORM_SELECTOR).val()).toBe('> This text has been selected.\n\n');
+ });
+ });
+
+ describe('with a multi-line selection', () => {
+ it('quotes the selected lines as a group', () => {
+ stubSelection(
+ '<p>Selected line one.</p>\n<p>Selected line two.</p>\n<p>Selected line three.</p>',
+ );
+ ShortcutsIssuable.replyWithSelectedText(true);
+
+ expect($(FORM_SELECTOR).val()).toBe(
+ '> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n',
+ );
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js
index 7651792be2e..60be285039f 100644
--- a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js
+++ b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js
@@ -1,21 +1,15 @@
-import {
- BoxGeometry,
-} from 'three/build/three.module';
+import { BoxGeometry } from 'three/build/three.module';
import MeshObject from '~/blob/3d_viewer/mesh_object';
describe('Mesh object', () => {
it('defaults to non-wireframe material', () => {
- const object = new MeshObject(
- new BoxGeometry(10, 10, 10),
- );
+ const object = new MeshObject(new BoxGeometry(10, 10, 10));
expect(object.material.wireframe).toBeFalsy();
});
it('changes to wirefame material', () => {
- const object = new MeshObject(
- new BoxGeometry(10, 10, 10),
- );
+ const object = new MeshObject(new BoxGeometry(10, 10, 10));
object.changeMaterial('wireframe');
@@ -23,18 +17,14 @@ describe('Mesh object', () => {
});
it('scales object down', () => {
- const object = new MeshObject(
- new BoxGeometry(10, 10, 10),
- );
+ const object = new MeshObject(new BoxGeometry(10, 10, 10));
const { radius } = object.geometry.boundingSphere;
expect(radius).not.toBeGreaterThan(4);
});
it('does not scale object down', () => {
- const object = new MeshObject(
- new BoxGeometry(1, 1, 1),
- );
+ const object = new MeshObject(new BoxGeometry(1, 1, 1));
const { radius } = object.geometry.boundingSphere;
expect(radius).toBeLessThan(1);
diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js
index c726fa8e428..5f027f59fcf 100644
--- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js
+++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js
@@ -16,10 +16,13 @@ describe('Balsamiq integration spec', () => {
});
describe('successful response', () => {
- beforeEach((done) => {
+ beforeEach(done => {
endpoint = bmprPath;
- balsamiqViewer.loadFile(endpoint).then(done).catch(done.fail);
+ balsamiqViewer
+ .loadFile(endpoint)
+ .then(done)
+ .catch(done.fail);
});
it('does not show loading icon', () => {
@@ -32,10 +35,13 @@ describe('Balsamiq integration spec', () => {
});
describe('error getting file', () => {
- beforeEach((done) => {
+ beforeEach(done => {
endpoint = 'invalid/path/to/file.bmpr';
- balsamiqViewer.loadFile(endpoint).then(done.fail, null).catch(done);
+ balsamiqViewer
+ .loadFile(endpoint)
+ .then(done.fail, null)
+ .catch(done);
});
it('does not show loading icon', () => {
diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js
index cb0f2ba686d..fd73fb4bfcc 100644
--- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js
+++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js
@@ -18,10 +18,6 @@ describe('BalsamiqViewer', () => {
});
});
- describe('fileLoaded', () => {
-
- });
-
describe('loadFile', () => {
let xhr;
let loadFile;
@@ -64,12 +60,16 @@ describe('BalsamiqViewer', () => {
viewer = jasmine.createSpyObj('viewer', ['appendChild']);
previews = [document.createElement('ul'), document.createElement('ul')];
- balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['initDatabase', 'getPreviews', 'renderPreview']);
+ balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', [
+ 'initDatabase',
+ 'getPreviews',
+ 'renderPreview',
+ ]);
balsamiqViewer.viewer = viewer;
balsamiqViewer.getPreviews.and.returnValue(previews);
balsamiqViewer.renderPreview.and.callFake(preview => preview);
- viewer.appendChild.and.callFake((containerElement) => {
+ viewer.appendChild.and.callFake(containerElement => {
container = containerElement;
});
@@ -198,7 +198,9 @@ describe('BalsamiqViewer', () => {
});
it('should call database.exec', () => {
- expect(database.exec).toHaveBeenCalledWith(`SELECT * FROM resources WHERE id = '${resourceID}'`);
+ expect(database.exec).toHaveBeenCalledWith(
+ `SELECT * FROM resources WHERE id = '${resourceID}'`,
+ );
});
it('should return the selected resource', () => {
@@ -281,7 +283,7 @@ describe('BalsamiqViewer', () => {
expect(BalsamiqViewer.parseTitle).toHaveBeenCalledWith(resource);
});
- it('should return the template string', function () {
+ it('should return the template string', function() {
expect(renderTemplate.replace(/\s/g, '')).toEqual(template.replace(/\s/g, ''));
});
});
diff --git a/spec/javascripts/blob/blob_file_dropzone_spec.js b/spec/javascripts/blob/blob_file_dropzone_spec.js
index 346f795c3f5..432d8a65b0a 100644
--- a/spec/javascripts/blob/blob_file_dropzone_spec.js
+++ b/spec/javascripts/blob/blob_file_dropzone_spec.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import BlobFileDropzone from '~/blob/blob_file_dropzone';
-describe('BlobFileDropzone', function () {
+describe('BlobFileDropzone', function() {
preloadFixtures('blob/show.html.raw');
beforeEach(() => {
diff --git a/spec/javascripts/blob/blob_fork_suggestion_spec.js b/spec/javascripts/blob/blob_fork_suggestion_spec.js
index d1ab0a32f85..9b81b7e6f92 100644
--- a/spec/javascripts/blob/blob_fork_suggestion_spec.js
+++ b/spec/javascripts/blob/blob_fork_suggestion_spec.js
@@ -16,8 +16,7 @@ describe('BlobForkSuggestion', () => {
cancelButtons: cancelButton,
suggestionSections: suggestionSection,
actionTextPieces: actionTextPiece,
- })
- .init();
+ }).init();
});
afterEach(() => {
@@ -26,6 +25,7 @@ describe('BlobForkSuggestion', () => {
it('showSuggestionSection', () => {
blobForkSuggestion.showSuggestionSection('/foo', 'foo');
+
expect(suggestionSection.classList.contains('hidden')).toEqual(false);
expect(forkButton.getAttribute('href')).toEqual('/foo');
expect(actionTextPiece.textContent).toEqual('foo');
@@ -33,6 +33,7 @@ describe('BlobForkSuggestion', () => {
it('hideSuggestionSection', () => {
blobForkSuggestion.hideSuggestionSection();
+
expect(suggestionSection.classList.contains('hidden')).toEqual(true);
});
});
diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js
index 80c09a544d6..28d3b2f5ea3 100644
--- a/spec/javascripts/blob/notebook/index_spec.js
+++ b/spec/javascripts/blob/notebook/index_spec.js
@@ -12,29 +12,27 @@ describe('iPython notebook renderer', () => {
it('shows loading icon', () => {
renderNotebook();
- expect(
- document.querySelector('.loading'),
- ).not.toBeNull();
+ expect(document.querySelector('.loading')).not.toBeNull();
});
describe('successful response', () => {
let mock;
- beforeEach((done) => {
+ beforeEach(done => {
mock = new MockAdapter(axios);
mock.onGet('/test').reply(200, {
- cells: [{
- cell_type: 'markdown',
- source: ['# test'],
- }, {
- cell_type: 'code',
- execution_count: 1,
- source: [
- 'def test(str)',
- ' return str',
- ],
- outputs: [],
- }],
+ cells: [
+ {
+ cell_type: 'markdown',
+ source: ['# test'],
+ },
+ {
+ cell_type: 'code',
+ execution_count: 1,
+ source: ['def test(str)', ' return str'],
+ outputs: [],
+ },
+ ],
});
renderNotebook();
@@ -49,35 +47,23 @@ describe('iPython notebook renderer', () => {
});
it('does not show loading icon', () => {
- expect(
- document.querySelector('.loading'),
- ).toBeNull();
+ expect(document.querySelector('.loading')).toBeNull();
});
it('renders the notebook', () => {
- expect(
- document.querySelector('.md'),
- ).not.toBeNull();
+ expect(document.querySelector('.md')).not.toBeNull();
});
it('renders the markdown cell', () => {
- expect(
- document.querySelector('h1'),
- ).not.toBeNull();
+ expect(document.querySelector('h1')).not.toBeNull();
- expect(
- document.querySelector('h1').textContent.trim(),
- ).toBe('test');
+ expect(document.querySelector('h1').textContent.trim()).toBe('test');
});
it('highlights code', () => {
- expect(
- document.querySelector('.token'),
- ).not.toBeNull();
+ expect(document.querySelector('.token')).not.toBeNull();
- expect(
- document.querySelector('.language-python'),
- ).not.toBeNull();
+ expect(document.querySelector('.language-python')).not.toBeNull();
});
});
@@ -86,12 +72,10 @@ describe('iPython notebook renderer', () => {
beforeEach(done => {
mock = new MockAdapter(axios);
- mock
- .onGet('/test')
- .reply(() =>
- // eslint-disable-next-line prefer-promise-reject-errors
- Promise.reject({ status: 200, data: '{ "cells": [{"cell_type": "markdown"} }' }),
- );
+ mock.onGet('/test').reply(() =>
+ // eslint-disable-next-line prefer-promise-reject-errors
+ Promise.reject({ status: 200, data: '{ "cells": [{"cell_type": "markdown"} }' }),
+ );
renderNotebook();
@@ -105,22 +89,20 @@ describe('iPython notebook renderer', () => {
});
it('does not show loading icon', () => {
- expect(
- document.querySelector('.loading'),
- ).toBeNull();
+ expect(document.querySelector('.loading')).toBeNull();
});
it('shows error message', () => {
- expect(
- document.querySelector('.md').textContent.trim(),
- ).toBe('An error occurred whilst parsing the file.');
+ expect(document.querySelector('.md').textContent.trim()).toBe(
+ 'An error occurred whilst parsing the file.',
+ );
});
});
describe('error getting file', () => {
let mock;
- beforeEach((done) => {
+ beforeEach(done => {
mock = new MockAdapter(axios);
mock.onGet('/test').reply(500, '');
@@ -136,15 +118,13 @@ describe('iPython notebook renderer', () => {
});
it('does not show loading icon', () => {
- expect(
- document.querySelector('.loading'),
- ).toBeNull();
+ expect(document.querySelector('.loading')).toBeNull();
});
it('shows error message', () => {
- expect(
- document.querySelector('.md').textContent.trim(),
- ).toBe('An error occurred whilst loading the file. Please try again later.');
+ expect(document.querySelector('.md').textContent.trim()).toBe(
+ 'An error occurred whilst loading the file. Please try again later.',
+ );
});
});
});
diff --git a/spec/javascripts/blob/pdf/index_spec.js b/spec/javascripts/blob/pdf/index_spec.js
index bbe2500f8e3..be917a0613f 100644
--- a/spec/javascripts/blob/pdf/index_spec.js
+++ b/spec/javascripts/blob/pdf/index_spec.js
@@ -5,7 +5,7 @@ describe('PDF renderer', () => {
let viewer;
let app;
- const checkLoaded = (done) => {
+ const checkLoaded = done => {
if (app.loading) {
setTimeout(() => {
checkLoaded(done);
@@ -26,39 +26,31 @@ describe('PDF renderer', () => {
it('shows loading icon', () => {
renderPDF();
- expect(
- document.querySelector('.loading'),
- ).not.toBeNull();
+ expect(document.querySelector('.loading')).not.toBeNull();
});
describe('successful response', () => {
- beforeEach((done) => {
+ beforeEach(done => {
app = renderPDF();
checkLoaded(done);
});
it('does not show loading icon', () => {
- expect(
- document.querySelector('.loading'),
- ).toBeNull();
+ expect(document.querySelector('.loading')).toBeNull();
});
it('renders the PDF', () => {
- expect(
- document.querySelector('.pdf-viewer'),
- ).not.toBeNull();
+ expect(document.querySelector('.pdf-viewer')).not.toBeNull();
});
it('renders the PDF page', () => {
- expect(
- document.querySelector('.pdf-page'),
- ).not.toBeNull();
+ expect(document.querySelector('.pdf-page')).not.toBeNull();
});
});
describe('error getting file', () => {
- beforeEach((done) => {
+ beforeEach(done => {
viewer.dataset.endpoint = 'invalid/path/to/file.pdf';
app = renderPDF();
@@ -66,15 +58,13 @@ describe('PDF renderer', () => {
});
it('does not show loading icon', () => {
- expect(
- document.querySelector('.loading'),
- ).toBeNull();
+ expect(document.querySelector('.loading')).toBeNull();
});
it('shows error message', () => {
- expect(
- document.querySelector('.md').textContent.trim(),
- ).toBe('An error occurred whilst loading the file. Please try again later.');
+ expect(document.querySelector('.md').textContent.trim()).toBe(
+ 'An error occurred whilst loading the file. Please try again later.',
+ );
});
});
});
diff --git a/spec/javascripts/blob/sketch/index_spec.js b/spec/javascripts/blob/sketch/index_spec.js
index e062a068a92..2b1e81e9cbc 100644
--- a/spec/javascripts/blob/sketch/index_spec.js
+++ b/spec/javascripts/blob/sketch/index_spec.js
@@ -4,15 +4,13 @@ import SketchLoader from '~/blob/sketch';
describe('Sketch viewer', () => {
const generateZipFileArrayBuffer = (zipFile, resolve, done) => {
- zipFile
- .generateAsync({ type: 'arrayBuffer' })
- .then((content) => {
- resolve(content);
-
- setTimeout(() => {
- done();
- }, 100);
- });
+ zipFile.generateAsync({ type: 'arrayBuffer' }).then(content => {
+ resolve(content);
+
+ setTimeout(() => {
+ done();
+ }, 100);
+ });
};
preloadFixtures('static/sketch_viewer.html.raw');
@@ -22,60 +20,63 @@ describe('Sketch viewer', () => {
});
describe('with error message', () => {
- beforeEach((done) => {
- spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve, reject) => {
- reject();
-
- setTimeout(() => {
- done();
- });
- }));
+ beforeEach(done => {
+ spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(
+ () =>
+ new Promise((resolve, reject) => {
+ reject();
+
+ setTimeout(() => {
+ done();
+ });
+ }),
+ );
new SketchLoader(document.getElementById('js-sketch-viewer'));
});
it('renders error message', () => {
- expect(
- document.querySelector('#js-sketch-viewer p'),
- ).not.toBeNull();
+ expect(document.querySelector('#js-sketch-viewer p')).not.toBeNull();
- expect(
- document.querySelector('#js-sketch-viewer p').textContent.trim(),
- ).toContain('Cannot show preview.');
+ expect(document.querySelector('#js-sketch-viewer p').textContent.trim()).toContain(
+ 'Cannot show preview.',
+ );
});
it('removes render the loading icon', () => {
- expect(
- document.querySelector('.js-loading-icon'),
- ).toBeNull();
+ expect(document.querySelector('.js-loading-icon')).toBeNull();
});
});
describe('success', () => {
- beforeEach((done) => {
- spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve) => {
- const zipFile = new JSZip();
- zipFile.folder('previews')
- .file('preview.png', 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=', {
- base64: true,
- });
-
- generateZipFileArrayBuffer(zipFile, resolve, done);
- }));
+ beforeEach(done => {
+ spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(
+ () =>
+ new Promise(resolve => {
+ const zipFile = new JSZip();
+ zipFile
+ .folder('previews')
+ .file(
+ 'preview.png',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=',
+ {
+ base64: true,
+ },
+ );
+
+ generateZipFileArrayBuffer(zipFile, resolve, done);
+ }),
+ );
new SketchLoader(document.getElementById('js-sketch-viewer'));
});
it('does not render error message', () => {
- expect(
- document.querySelector('#js-sketch-viewer p'),
- ).toBeNull();
+ expect(document.querySelector('#js-sketch-viewer p')).toBeNull();
});
it('removes render the loading icon', () => {
- expect(
- document.querySelector('.js-loading-icon'),
- ).toBeNull();
+ expect(document.querySelector('.js-loading-icon')).toBeNull();
});
it('renders preview img', () => {
@@ -95,24 +96,25 @@ describe('Sketch viewer', () => {
});
describe('incorrect file', () => {
- beforeEach((done) => {
- spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve) => {
- const zipFile = new JSZip();
+ beforeEach(done => {
+ spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(
+ () =>
+ new Promise(resolve => {
+ const zipFile = new JSZip();
- generateZipFileArrayBuffer(zipFile, resolve, done);
- }));
+ generateZipFileArrayBuffer(zipFile, resolve, done);
+ }),
+ );
new SketchLoader(document.getElementById('js-sketch-viewer'));
});
it('renders error message', () => {
- expect(
- document.querySelector('#js-sketch-viewer p'),
- ).not.toBeNull();
+ expect(document.querySelector('#js-sketch-viewer p')).not.toBeNull();
- expect(
- document.querySelector('#js-sketch-viewer p').textContent.trim(),
- ).toContain('Cannot show preview.');
+ expect(document.querySelector('#js-sketch-viewer p').textContent.trim()).toContain(
+ 'Cannot show preview.',
+ );
});
});
});
diff --git a/spec/javascripts/blob/viewer/index_spec.js b/spec/javascripts/blob/viewer/index_spec.js
index 8b79624d9f4..93a942fe8d4 100644
--- a/spec/javascripts/blob/viewer/index_spec.js
+++ b/spec/javascripts/blob/viewer/index_spec.js
@@ -35,12 +35,13 @@ describe('Blob viewer', () => {
window.location.hash = '';
});
- it('loads source file after switching views', (done) => {
+ it('loads source file after switching views', done => {
document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
setTimeout(() => {
expect(
- document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]')
+ document
+ .querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]')
.classList.contains('hidden'),
).toBeFalsy();
@@ -48,14 +49,15 @@ describe('Blob viewer', () => {
});
});
- it('loads source file when line number is in hash', (done) => {
+ it('loads source file when line number is in hash', done => {
window.location.hash = '#L1';
new BlobViewer();
setTimeout(() => {
expect(
- document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]')
+ document
+ .querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]')
.classList.contains('hidden'),
).toBeFalsy();
@@ -63,12 +65,13 @@ describe('Blob viewer', () => {
});
});
- it('doesnt reload file if already loaded', (done) => {
- const asyncClick = () => new Promise((resolve) => {
- document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
+ it('doesnt reload file if already loaded', done => {
+ const asyncClick = () =>
+ new Promise(resolve => {
+ document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
- setTimeout(resolve);
- });
+ setTimeout(resolve);
+ });
asyncClick()
.then(() => asyncClick())
@@ -93,15 +96,13 @@ describe('Blob viewer', () => {
});
it('disabled on load', () => {
- expect(
- copyButton.classList.contains('disabled'),
- ).toBeTruthy();
+ expect(copyButton.classList.contains('disabled')).toBeTruthy();
});
it('has tooltip when disabled', () => {
- expect(
- copyButton.getAttribute('data-original-title'),
- ).toBe('Switch to the source to copy it to the clipboard');
+ expect(copyButton.getAttribute('data-original-title')).toBe(
+ 'Switch to the source to copy it to the clipboard',
+ );
});
it('is blurred when clicked and disabled', () => {
@@ -121,25 +122,21 @@ describe('Blob viewer', () => {
expect(copyButton.blur).not.toHaveBeenCalled();
});
- it('enables after switching to simple view', (done) => {
+ it('enables after switching to simple view', done => {
document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
setTimeout(() => {
- expect(
- copyButton.classList.contains('disabled'),
- ).toBeFalsy();
+ expect(copyButton.classList.contains('disabled')).toBeFalsy();
done();
});
});
- it('updates tooltip after switching to simple view', (done) => {
+ it('updates tooltip after switching to simple view', done => {
document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
setTimeout(() => {
- expect(
- copyButton.getAttribute('data-original-title'),
- ).toBe('Copy source to clipboard');
+ expect(copyButton.getAttribute('data-original-title')).toBe('Copy source to clipboard');
done();
});
@@ -162,9 +159,8 @@ describe('Blob viewer', () => {
blob.switchToViewer('simple');
- expect(
- simpleBtn.classList.contains('active'),
- ).toBeTruthy();
+ expect(simpleBtn.classList.contains('active')).toBeTruthy();
+
expect(simpleBtn.blur).toHaveBeenCalled();
});
diff --git a/spec/javascripts/boards/board_blank_state_spec.js b/spec/javascripts/boards/board_blank_state_spec.js
index 89a4fae4b59..8e2a947b0dd 100644
--- a/spec/javascripts/boards/board_blank_state_spec.js
+++ b/spec/javascripts/boards/board_blank_state_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import '~/boards/stores/boards_store';
+import boardsStore from '~/boards/stores/boards_store';
import BoardBlankState from '~/boards/components/board_blank_state.vue';
import { mockBoardService } from './mock_data';
@@ -7,29 +7,35 @@ describe('Boards blank state', () => {
let vm;
let fail = false;
- beforeEach((done) => {
+ beforeEach(done => {
const Comp = Vue.extend(BoardBlankState);
- gl.issueBoards.BoardsStore.create();
+ boardsStore.create();
gl.boardService = mockBoardService();
- spyOn(gl.boardService, 'generateDefaultLists').and.callFake(() => new Promise((resolve, reject) => {
- if (fail) {
- reject();
- } else {
- resolve({
- data: [{
- id: 1,
- title: 'To Do',
- label: { id: 1 },
- }, {
- id: 2,
- title: 'Doing',
- label: { id: 2 },
- }],
- });
- }
- }));
+ spyOn(gl.boardService, 'generateDefaultLists').and.callFake(
+ () =>
+ new Promise((resolve, reject) => {
+ if (fail) {
+ reject();
+ } else {
+ resolve({
+ data: [
+ {
+ id: 1,
+ title: 'To Do',
+ label: { id: 1 },
+ },
+ {
+ id: 2,
+ title: 'Doing',
+ label: { id: 2 },
+ },
+ ],
+ });
+ }
+ }),
+ );
vm = new Comp();
@@ -40,49 +46,47 @@ describe('Boards blank state', () => {
});
it('renders pre-defined labels', () => {
- expect(
- vm.$el.querySelectorAll('.board-blank-state-list li').length,
- ).toBe(2);
+ expect(vm.$el.querySelectorAll('.board-blank-state-list li').length).toBe(2);
- expect(
- vm.$el.querySelectorAll('.board-blank-state-list li')[0].textContent.trim(),
- ).toEqual('To Do');
+ expect(vm.$el.querySelectorAll('.board-blank-state-list li')[0].textContent.trim()).toEqual(
+ 'To Do',
+ );
- expect(
- vm.$el.querySelectorAll('.board-blank-state-list li')[1].textContent.trim(),
- ).toEqual('Doing');
+ expect(vm.$el.querySelectorAll('.board-blank-state-list li')[1].textContent.trim()).toEqual(
+ 'Doing',
+ );
});
- it('clears blank state', (done) => {
+ it('clears blank state', done => {
vm.$el.querySelector('.btn-default').click();
setTimeout(() => {
- expect(gl.issueBoards.BoardsStore.welcomeIsHidden()).toBeTruthy();
+ expect(boardsStore.welcomeIsHidden()).toBeTruthy();
done();
});
});
- it('creates pre-defined labels', (done) => {
- vm.$el.querySelector('.btn-create').click();
+ it('creates pre-defined labels', done => {
+ vm.$el.querySelector('.btn-success').click();
setTimeout(() => {
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
- expect(gl.issueBoards.BoardsStore.state.lists[0].title).toEqual('To Do');
- expect(gl.issueBoards.BoardsStore.state.lists[1].title).toEqual('Doing');
+ expect(boardsStore.state.lists.length).toBe(2);
+ expect(boardsStore.state.lists[0].title).toEqual('To Do');
+ expect(boardsStore.state.lists[1].title).toEqual('Doing');
done();
});
});
- it('resets the store if request fails', (done) => {
+ it('resets the store if request fails', done => {
fail = true;
- vm.$el.querySelector('.btn-create').click();
+ vm.$el.querySelector('.btn-success').click();
setTimeout(() => {
- expect(gl.issueBoards.BoardsStore.welcomeIsHidden()).toBeFalsy();
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1);
+ expect(boardsStore.welcomeIsHidden()).toBeFalsy();
+ expect(boardsStore.state.lists.length).toBe(1);
done();
});
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index ad263791cd4..e1017130bed 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -10,7 +10,7 @@ import eventHub from '~/boards/eventhub';
import '~/vue_shared/models/label';
import '~/vue_shared/models/assignee';
import '~/boards/models/list';
-import '~/boards/stores/boards_store';
+import boardsStore from '~/boards/stores/boards_store';
import boardCard from '~/boards/components/board_card.vue';
import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
@@ -18,13 +18,13 @@ describe('Board card', () => {
let vm;
let mock;
- beforeEach((done) => {
+ beforeEach(done => {
mock = new MockAdapter(axios);
mock.onAny().reply(boardsMockInterceptor);
gl.boardService = mockBoardService();
- gl.issueBoards.BoardsStore.create();
- gl.issueBoards.BoardsStore.detail.issue = {};
+ boardsStore.create();
+ boardsStore.detail.issue = {};
const BoardCardComp = Vue.extend(boardCard);
const list = new List(listObj);
@@ -62,7 +62,7 @@ describe('Board card', () => {
});
it('returns true when detailIssue is equal to card issue', () => {
- gl.issueBoards.BoardsStore.detail.issue = vm.issue;
+ boardsStore.detail.issue = vm.issue;
expect(vm.issueDetailVisible).toBe(true);
});
@@ -71,7 +71,7 @@ describe('Board card', () => {
expect(vm.$el.classList.contains('user-can-drag')).toBe(true);
});
- it('does not add user-can-drag class disabled', (done) => {
+ it('does not add user-can-drag class disabled', done => {
vm.disabled = true;
setTimeout(() => {
@@ -84,7 +84,7 @@ describe('Board card', () => {
expect(vm.$el.classList.contains('is-disabled')).toBe(false);
});
- it('adds disabled class is disabled is true', (done) => {
+ it('adds disabled class is disabled is true', done => {
vm.disabled = true;
setTimeout(() => {
@@ -96,8 +96,23 @@ describe('Board card', () => {
describe('mouse events', () => {
const triggerEvent = (eventName, el = vm.$el) => {
const event = document.createEvent('MouseEvents');
- event.initMouseEvent(eventName, true, true, window, 1, 0, 0, 0, 0, false, false,
- false, false, 0, null);
+ event.initMouseEvent(
+ eventName,
+ true,
+ true,
+ window,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false,
+ false,
+ false,
+ false,
+ 0,
+ null,
+ );
el.dispatchEvent(event);
};
@@ -119,33 +134,35 @@ describe('Board card', () => {
});
it('does not set detail issue if showDetail is false', () => {
- expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({});
+ expect(boardsStore.detail.issue).toEqual({});
});
it('does not set detail issue if link is clicked', () => {
triggerEvent('mouseup', vm.$el.querySelector('a'));
- expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({});
+ expect(boardsStore.detail.issue).toEqual({});
});
it('does not set detail issue if button is clicked', () => {
triggerEvent('mouseup', vm.$el.querySelector('button'));
- expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({});
+ expect(boardsStore.detail.issue).toEqual({});
});
- it('does not set detail issue if img is clicked', (done) => {
- vm.issue.assignees = [new ListAssignee({
- id: 1,
- name: 'testing 123',
- username: 'test',
- avatar: 'test_image',
- })];
+ it('does not set detail issue if img is clicked', done => {
+ vm.issue.assignees = [
+ new ListAssignee({
+ id: 1,
+ name: 'testing 123',
+ username: 'test',
+ avatar: 'test_image',
+ }),
+ ];
Vue.nextTick(() => {
triggerEvent('mouseup', vm.$el.querySelector('img'));
- expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({});
+ expect(boardsStore.detail.issue).toEqual({});
done();
});
@@ -154,7 +171,7 @@ describe('Board card', () => {
it('does not set detail issue if showDetail is false after mouseup', () => {
triggerEvent('mouseup');
- expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({});
+ expect(boardsStore.detail.issue).toEqual({});
});
it('sets detail issue to card issue on mouse up', () => {
@@ -164,10 +181,10 @@ describe('Board card', () => {
triggerEvent('mouseup');
expect(eventHub.$emit).toHaveBeenCalledWith('newDetailIssue', vm.issue);
- expect(gl.issueBoards.BoardsStore.detail.list).toEqual(vm.list);
+ expect(boardsStore.detail.list).toEqual(vm.list);
});
- it('adds active class if detail issue is set', (done) => {
+ it('adds active class if detail issue is set', done => {
vm.detailIssue.issue = vm.issue;
Vue.nextTick()
@@ -181,7 +198,7 @@ describe('Board card', () => {
it('resets detail issue to empty if already set', () => {
spyOn(eventHub, '$emit');
- gl.issueBoards.BoardsStore.detail.issue = vm.issue;
+ boardsStore.detail.issue = vm.issue;
triggerEvent('mousedown');
triggerEvent('mouseup');
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index de261d36c61..2642c8b1bdb 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -1,15 +1,15 @@
/* global List */
/* global ListIssue */
+
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import Sortable from 'sortablejs';
import BoardList from '~/boards/components/board_list.vue';
import eventHub from '~/boards/eventhub';
-import '~/boards/mixins/sortable_default_options';
import '~/boards/models/issue';
import '~/boards/models/list';
-import '~/boards/stores/boards_store';
+import boardsStore from '~/boards/stores/boards_store';
import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
window.Sortable = Sortable;
@@ -18,15 +18,14 @@ describe('Board list component', () => {
let mock;
let component;
- beforeEach((done) => {
+ beforeEach(done => {
const el = document.createElement('div');
document.body.appendChild(el);
mock = new MockAdapter(axios);
mock.onAny().reply(boardsMockInterceptor);
gl.boardService = mockBoardService();
- gl.issueBoards.BoardsStore.create();
- gl.IssueBoardsApp = new Vue();
+ boardsStore.create();
const BoardListComp = Vue.extend(BoardList);
const list = new List(listObj);
@@ -63,122 +62,102 @@ describe('Board list component', () => {
});
it('renders component', () => {
- expect(
- component.$el.classList.contains('board-list-component'),
- ).toBe(true);
+ expect(component.$el.classList.contains('board-list-component')).toBe(true);
});
- it('renders loading icon', (done) => {
+ it('renders loading icon', done => {
component.loading = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-loading'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-list-loading')).not.toBeNull();
done();
});
});
it('renders issues', () => {
- expect(
- component.$el.querySelectorAll('.board-card').length,
- ).toBe(1);
+ expect(component.$el.querySelectorAll('.board-card').length).toBe(1);
});
it('sets data attribute with issue id', () => {
- expect(
- component.$el.querySelector('.board-card').getAttribute('data-issue-id'),
- ).toBe('1');
+ expect(component.$el.querySelector('.board-card').getAttribute('data-issue-id')).toBe('1');
});
- it('shows new issue form', (done) => {
+ it('shows new issue form', done => {
component.toggleForm();
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-new-issue-form'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-new-issue-form')).not.toBeNull();
- expect(
- component.$el.querySelector('.is-smaller'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.is-smaller')).not.toBeNull();
done();
});
});
- it('shows new issue form after eventhub event', (done) => {
+ it('shows new issue form after eventhub event', done => {
eventHub.$emit(`hide-issue-form-${component.list.id}`);
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-new-issue-form'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-new-issue-form')).not.toBeNull();
- expect(
- component.$el.querySelector('.is-smaller'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.is-smaller')).not.toBeNull();
done();
});
});
- it('does not show new issue form for closed list', (done) => {
+ it('does not show new issue form for closed list', done => {
component.list.type = 'closed';
component.toggleForm();
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-new-issue-form'),
- ).toBeNull();
+ expect(component.$el.querySelector('.board-new-issue-form')).toBeNull();
done();
});
});
- it('shows count list item', (done) => {
+ it('shows count list item', done => {
component.showCount = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-count'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-list-count')).not.toBeNull();
- expect(
- component.$el.querySelector('.board-list-count').textContent.trim(),
- ).toBe('Showing all issues');
+ expect(component.$el.querySelector('.board-list-count').textContent.trim()).toBe(
+ 'Showing all issues',
+ );
done();
});
});
- it('sets data attribute with invalid id', (done) => {
+ it('sets data attribute with invalid id', done => {
component.showCount = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-count').getAttribute('data-issue-id'),
- ).toBe('-1');
+ expect(component.$el.querySelector('.board-list-count').getAttribute('data-issue-id')).toBe(
+ '-1',
+ );
done();
});
});
- it('shows how many more issues to load', (done) => {
+ it('shows how many more issues to load', done => {
component.showCount = true;
component.list.issuesSize = 20;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-count').textContent.trim(),
- ).toBe('Showing 1 of 20 issues');
+ expect(component.$el.querySelector('.board-list-count').textContent.trim()).toBe(
+ 'Showing 1 of 20 issues',
+ );
done();
});
});
- it('loads more issues after scrolling', (done) => {
+ it('loads more issues after scrolling', done => {
spyOn(component.list, 'nextPage');
component.$refs.list.style.height = '100px';
component.$refs.list.style.overflow = 'scroll';
@@ -200,14 +179,23 @@ describe('Board list component', () => {
});
});
- it('shows loading more spinner', (done) => {
+ it('does not load issues if already loading', () => {
+ component.list.nextPage = spyOn(component.list, 'nextPage').and.returnValue(
+ new Promise(() => {}),
+ );
+
+ component.onScroll();
+ component.onScroll();
+
+ expect(component.list.nextPage).toHaveBeenCalledTimes(1);
+ });
+
+ it('shows loading more spinner', done => {
component.showCount = true;
component.list.loadingMore = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-count .fa-spinner'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-list-count .fa-spinner')).not.toBeNull();
done();
});
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
index ee37821ad08..721d0b8172d 100644
--- a/spec/javascripts/boards/board_new_issue_spec.js
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -4,6 +4,7 @@ import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import boardNewIssue from '~/boards/components/board_new_issue.vue';
+import boardsStore from '~/boards/stores/boards_store';
import '~/boards/models/list';
import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
@@ -27,7 +28,7 @@ describe('Issue boards new issue form', () => {
return vm.submit(dummySubmitEvent);
};
- beforeEach((done) => {
+ beforeEach(done => {
setFixtures('<div class="test-container"></div>');
const BoardNewIssueComp = Vue.extend(boardNewIssue);
@@ -36,8 +37,7 @@ describe('Issue boards new issue form', () => {
mock.onAny().reply(boardsMockInterceptor);
gl.boardService = mockBoardService();
- gl.issueBoards.BoardsStore.create();
- gl.IssueBoardsApp = new Vue();
+ boardsStore.create();
list = new List(listObj);
@@ -60,7 +60,7 @@ describe('Issue boards new issue form', () => {
mock.restore();
});
- it('calls submit if submit button is clicked', (done) => {
+ it('calls submit if submit button is clicked', done => {
spyOn(vm, 'submit').and.callFake(e => e.preventDefault());
vm.title = 'Testing Title';
@@ -69,7 +69,6 @@ describe('Issue boards new issue form', () => {
vm.$el.querySelector('.btn-success').click();
expect(vm.submit.calls.count()).toBe(1);
- expect(vm.$refs['submit-button']).toBe(vm.$el.querySelector('.btn-success'));
})
.then(done)
.catch(done.fail);
@@ -79,7 +78,7 @@ describe('Issue boards new issue form', () => {
expect(vm.$el.querySelector('.btn-success').disabled).toBe(true);
});
- it('enables submit button if title is not empty', (done) => {
+ it('enables submit button if title is not empty', done => {
vm.title = 'Testing Title';
Vue.nextTick()
@@ -91,7 +90,7 @@ describe('Issue boards new issue form', () => {
.catch(done.fail);
});
- it('clears title after clicking cancel', (done) => {
+ it('clears title after clicking cancel', done => {
vm.$el.querySelector('.btn-default').click();
Vue.nextTick()
@@ -102,7 +101,7 @@ describe('Issue boards new issue form', () => {
.catch(done.fail);
});
- it('does not create new issue if title is empty', (done) => {
+ it('does not create new issue if title is empty', done => {
submitIssue()
.then(() => {
expect(list.newIssue).not.toHaveBeenCalled();
@@ -112,7 +111,7 @@ describe('Issue boards new issue form', () => {
});
describe('submit success', () => {
- it('creates new issue', (done) => {
+ it('creates new issue', done => {
vm.title = 'submit title';
Vue.nextTick()
@@ -124,7 +123,7 @@ describe('Issue boards new issue form', () => {
.catch(done.fail);
});
- it('enables button after submit', (done) => {
+ it('enables button after submit', done => {
vm.title = 'submit issue';
Vue.nextTick()
@@ -136,7 +135,7 @@ describe('Issue boards new issue form', () => {
.catch(done.fail);
});
- it('clears title after submit', (done) => {
+ it('clears title after submit', done => {
vm.title = 'submit issue';
Vue.nextTick()
@@ -148,26 +147,26 @@ describe('Issue boards new issue form', () => {
.catch(done.fail);
});
- it('sets detail issue after submit', (done) => {
- expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe(undefined);
+ it('sets detail issue after submit', done => {
+ expect(boardsStore.detail.issue.title).toBe(undefined);
vm.title = 'submit issue';
Vue.nextTick()
.then(submitIssue)
.then(() => {
- expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe('submit issue');
+ expect(boardsStore.detail.issue.title).toBe('submit issue');
})
.then(done)
.catch(done.fail);
});
- it('sets detail list after submit', (done) => {
+ it('sets detail list after submit', done => {
vm.title = 'submit issue';
Vue.nextTick()
.then(submitIssue)
.then(() => {
- expect(gl.issueBoards.BoardsStore.detail.list.id).toBe(list.id);
+ expect(boardsStore.detail.list.id).toBe(list.id);
})
.then(done)
.catch(done.fail);
@@ -180,7 +179,7 @@ describe('Issue boards new issue form', () => {
vm.title = 'error';
});
- it('removes issue', (done) => {
+ it('removes issue', done => {
Vue.nextTick()
.then(submitIssue)
.then(() => {
@@ -190,7 +189,7 @@ describe('Issue boards new issue form', () => {
.catch(done.fail);
});
- it('shows error', (done) => {
+ it('shows error', done => {
Vue.nextTick()
.then(submitIssue)
.then(() => {
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 1ee6f4cf680..54f1edfb1f9 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, no-unused-vars */
+/* eslint-disable no-unused-vars */
/* global ListIssue */
import Vue from 'vue';
@@ -11,7 +11,7 @@ import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import '~/boards/services/board_service';
-import '~/boards/stores/boards_store';
+import boardsStore from '~/boards/stores/boards_store';
import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data';
describe('Store', () => {
@@ -21,15 +21,18 @@ describe('Store', () => {
mock = new MockAdapter(axios);
mock.onAny().reply(boardsMockInterceptor);
gl.boardService = mockBoardService();
- gl.issueBoards.BoardsStore.create();
+ boardsStore.create();
- spyOn(gl.boardService, 'moveIssue').and.callFake(() => new Promise((resolve) => {
- resolve();
- }));
+ spyOn(gl.boardService, 'moveIssue').and.callFake(
+ () =>
+ new Promise(resolve => {
+ resolve();
+ }),
+ );
Cookies.set('issue_board_welcome_hidden', 'false', {
expires: 365 * 10,
- path: ''
+ path: '',
});
});
@@ -38,35 +41,35 @@ describe('Store', () => {
});
it('starts with a blank state', () => {
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(0);
+ expect(boardsStore.state.lists.length).toBe(0);
});
describe('lists', () => {
it('creates new list without persisting to DB', () => {
- gl.issueBoards.BoardsStore.addList(listObj);
+ boardsStore.addList(listObj);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1);
+ expect(boardsStore.state.lists.length).toBe(1);
});
it('finds list by ID', () => {
- gl.issueBoards.BoardsStore.addList(listObj);
- const list = gl.issueBoards.BoardsStore.findList('id', listObj.id);
+ boardsStore.addList(listObj);
+ const list = boardsStore.findList('id', listObj.id);
expect(list.id).toBe(listObj.id);
});
it('finds list by type', () => {
- gl.issueBoards.BoardsStore.addList(listObj);
- const list = gl.issueBoards.BoardsStore.findList('type', 'label');
+ boardsStore.addList(listObj);
+ const list = boardsStore.findList('type', 'label');
expect(list).toBeDefined();
});
- it('gets issue when new list added', (done) => {
- gl.issueBoards.BoardsStore.addList(listObj);
- const list = gl.issueBoards.BoardsStore.findList('id', listObj.id);
+ it('gets issue when new list added', done => {
+ boardsStore.addList(listObj);
+ const list = boardsStore.findList('id', listObj.id);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1);
+ expect(boardsStore.state.lists.length).toBe(1);
setTimeout(() => {
expect(list.issues.length).toBe(1);
@@ -75,21 +78,23 @@ describe('Store', () => {
}, 0);
});
- it('persists new list', (done) => {
- gl.issueBoards.BoardsStore.new({
+ it('persists new list', done => {
+ boardsStore.new({
title: 'Test',
list_type: 'label',
label: {
id: 1,
title: 'Testing',
color: 'red',
- description: 'testing;'
- }
+ description: 'testing;',
+ },
});
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1);
+
+ expect(boardsStore.state.lists.length).toBe(1);
setTimeout(() => {
- const list = gl.issueBoards.BoardsStore.findList('id', listObj.id);
+ const list = boardsStore.findList('id', listObj.id);
+
expect(list).toBeDefined();
expect(list.id).toBe(listObj.id);
expect(list.position).toBe(0);
@@ -98,61 +103,63 @@ describe('Store', () => {
});
it('check for blank state adding', () => {
- expect(gl.issueBoards.BoardsStore.shouldAddBlankState()).toBe(true);
+ expect(boardsStore.shouldAddBlankState()).toBe(true);
});
it('check for blank state not adding', () => {
- gl.issueBoards.BoardsStore.addList(listObj);
- expect(gl.issueBoards.BoardsStore.shouldAddBlankState()).toBe(false);
+ boardsStore.addList(listObj);
+
+ expect(boardsStore.shouldAddBlankState()).toBe(false);
});
it('check for blank state adding when closed list exist', () => {
- gl.issueBoards.BoardsStore.addList({
- list_type: 'closed'
+ boardsStore.addList({
+ list_type: 'closed',
});
- expect(gl.issueBoards.BoardsStore.shouldAddBlankState()).toBe(true);
+ expect(boardsStore.shouldAddBlankState()).toBe(true);
});
it('adds the blank state', () => {
- gl.issueBoards.BoardsStore.addBlankState();
+ boardsStore.addBlankState();
+
+ const list = boardsStore.findList('type', 'blank', 'blank');
- const list = gl.issueBoards.BoardsStore.findList('type', 'blank', 'blank');
expect(list).toBeDefined();
});
it('removes list from state', () => {
- gl.issueBoards.BoardsStore.addList(listObj);
+ boardsStore.addList(listObj);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1);
+ expect(boardsStore.state.lists.length).toBe(1);
- gl.issueBoards.BoardsStore.removeList(listObj.id, 'label');
+ boardsStore.removeList(listObj.id, 'label');
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(0);
+ expect(boardsStore.state.lists.length).toBe(0);
});
it('moves the position of lists', () => {
- const listOne = gl.issueBoards.BoardsStore.addList(listObj);
- const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
+ const listOne = boardsStore.addList(listObj);
+ const listTwo = boardsStore.addList(listObjDuplicate);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+ expect(boardsStore.state.lists.length).toBe(2);
- gl.issueBoards.BoardsStore.moveList(listOne, [listObjDuplicate.id, listObj.id]);
+ boardsStore.moveList(listOne, [listObjDuplicate.id, listObj.id]);
expect(listOne.position).toBe(1);
});
- it('moves an issue from one list to another', (done) => {
- const listOne = gl.issueBoards.BoardsStore.addList(listObj);
- const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
+ it('moves an issue from one list to another', done => {
+ const listOne = boardsStore.addList(listObj);
+ const listTwo = boardsStore.addList(listObjDuplicate);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+ expect(boardsStore.state.lists.length).toBe(2);
setTimeout(() => {
expect(listOne.issues.length).toBe(1);
expect(listTwo.issues.length).toBe(1);
- gl.issueBoards.BoardsStore.moveIssueToList(listOne, listTwo, listOne.findIssue(1));
+ boardsStore.moveIssueToList(listOne, listTwo, listOne.findIssue(1));
expect(listOne.issues.length).toBe(0);
expect(listTwo.issues.length).toBe(1);
@@ -161,20 +168,20 @@ describe('Store', () => {
}, 0);
});
- it('moves an issue from backlog to a list', (done) => {
- const backlog = gl.issueBoards.BoardsStore.addList({
+ it('moves an issue from backlog to a list', done => {
+ const backlog = boardsStore.addList({
...listObj,
list_type: 'backlog',
});
- const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
+ const listTwo = boardsStore.addList(listObjDuplicate);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+ expect(boardsStore.state.lists.length).toBe(2);
setTimeout(() => {
expect(backlog.issues.length).toBe(1);
expect(listTwo.issues.length).toBe(1);
- gl.issueBoards.BoardsStore.moveIssueToList(backlog, listTwo, backlog.findIssue(1));
+ boardsStore.moveIssueToList(backlog, listTwo, backlog.findIssue(1));
expect(backlog.issues.length).toBe(0);
expect(listTwo.issues.length).toBe(1);
@@ -183,11 +190,11 @@ describe('Store', () => {
}, 0);
});
- it('moves issue to top of another list', (done) => {
- const listOne = gl.issueBoards.BoardsStore.addList(listObj);
- const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
+ it('moves issue to top of another list', done => {
+ const listOne = boardsStore.addList(listObj);
+ const listTwo = boardsStore.addList(listObjDuplicate);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+ expect(boardsStore.state.lists.length).toBe(2);
setTimeout(() => {
listOne.issues[0].id = 2;
@@ -195,7 +202,7 @@ describe('Store', () => {
expect(listOne.issues.length).toBe(1);
expect(listTwo.issues.length).toBe(1);
- gl.issueBoards.BoardsStore.moveIssueToList(listOne, listTwo, listOne.findIssue(2), 0);
+ boardsStore.moveIssueToList(listOne, listTwo, listOne.findIssue(2), 0);
expect(listOne.issues.length).toBe(0);
expect(listTwo.issues.length).toBe(2);
@@ -206,11 +213,11 @@ describe('Store', () => {
}, 0);
});
- it('moves issue to bottom of another list', (done) => {
- const listOne = gl.issueBoards.BoardsStore.addList(listObj);
- const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
+ it('moves issue to bottom of another list', done => {
+ const listOne = boardsStore.addList(listObj);
+ const listTwo = boardsStore.addList(listObjDuplicate);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+ expect(boardsStore.state.lists.length).toBe(2);
setTimeout(() => {
listOne.issues[0].id = 2;
@@ -218,7 +225,7 @@ describe('Store', () => {
expect(listOne.issues.length).toBe(1);
expect(listTwo.issues.length).toBe(1);
- gl.issueBoards.BoardsStore.moveIssueToList(listOne, listTwo, listOne.findIssue(2), 1);
+ boardsStore.moveIssueToList(listOne, listTwo, listOne.findIssue(2), 1);
expect(listOne.issues.length).toBe(0);
expect(listTwo.issues.length).toBe(2);
@@ -229,7 +236,7 @@ describe('Store', () => {
}, 0);
});
- it('moves issue in list', (done) => {
+ it('moves issue in list', done => {
const issue = new ListIssue({
title: 'Testing',
id: 2,
@@ -238,14 +245,14 @@ describe('Store', () => {
labels: [],
assignees: [],
});
- const list = gl.issueBoards.BoardsStore.addList(listObj);
+ const list = boardsStore.addList(listObj);
setTimeout(() => {
list.addIssue(issue);
expect(list.issues.length).toBe(2);
- gl.issueBoards.BoardsStore.moveIssueInList(list, issue, 0, 1, [1, 2]);
+ boardsStore.moveIssueInList(list, issue, 0, 1, [1, 2]);
expect(list.issues[0].id).toBe(2);
expect(gl.boardService.moveIssue).toHaveBeenCalledWith(2, null, null, 1, null);
diff --git a/spec/javascripts/boards/components/board_spec.js b/spec/javascripts/boards/components/board_spec.js
index 19346e305cf..dee7841c088 100644
--- a/spec/javascripts/boards/components/board_spec.js
+++ b/spec/javascripts/boards/components/board_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import '~/boards/services/board_service';
-import '~/boards/components/board';
+import Board from '~/boards/components/board';
import '~/boards/models/list';
import { mockBoardService } from '../mock_data';
@@ -8,7 +8,7 @@ describe('Board component', () => {
let vm;
let el;
- beforeEach((done) => {
+ beforeEach(done => {
loadFixtures('boards/show.html.raw');
el = document.createElement('div');
@@ -21,7 +21,7 @@ describe('Board component', () => {
boardId: 1,
});
- vm = new gl.issueBoards.Board({
+ vm = new Board({
propsData: {
boardId: '1',
disabled: false,
@@ -50,56 +50,46 @@ describe('Board component', () => {
});
it('board is expandable when list type is backlog', () => {
- expect(
- vm.$el.classList.contains('is-expandable'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-expandable')).toBe(true);
});
- it('board is expandable when list type is closed', (done) => {
+ it('board is expandable when list type is closed', done => {
vm.list.type = 'closed';
Vue.nextTick(() => {
- expect(
- vm.$el.classList.contains('is-expandable'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-expandable')).toBe(true);
done();
});
});
- it('board is not expandable when list type is label', (done) => {
+ it('board is not expandable when list type is label', done => {
vm.list.type = 'label';
vm.list.isExpandable = false;
Vue.nextTick(() => {
- expect(
- vm.$el.classList.contains('is-expandable'),
- ).toBe(false);
+ expect(vm.$el.classList.contains('is-expandable')).toBe(false);
done();
});
});
- it('collapses when clicking header', (done) => {
+ it('collapses when clicking header', done => {
vm.$el.querySelector('.board-header').click();
Vue.nextTick(() => {
- expect(
- vm.$el.classList.contains('is-collapsed'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-collapsed')).toBe(true);
done();
});
});
- it('created sets isExpanded to true from localStorage', (done) => {
+ it('created sets isExpanded to true from localStorage', done => {
vm.$el.querySelector('.board-header').click();
return Vue.nextTick()
.then(() => {
- expect(
- vm.$el.classList.contains('is-collapsed'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-collapsed')).toBe(true);
// call created manually
vm.$options.created[0].call(vm);
@@ -107,11 +97,10 @@ describe('Board component', () => {
return Vue.nextTick();
})
.then(() => {
- expect(
- vm.$el.classList.contains('is-collapsed'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-collapsed')).toBe(true);
done();
- });
+ })
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/boards/components/issue_due_date_spec.js b/spec/javascripts/boards/components/issue_due_date_spec.js
new file mode 100644
index 00000000000..9e49330c052
--- /dev/null
+++ b/spec/javascripts/boards/components/issue_due_date_spec.js
@@ -0,0 +1,64 @@
+import Vue from 'vue';
+import dateFormat from 'dateformat';
+import IssueDueDate from '~/boards/components/issue_due_date.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Issue Due Date component', () => {
+ let vm;
+ let date;
+ const Component = Vue.extend(IssueDueDate);
+ const createComponent = (dueDate = new Date()) =>
+ mountComponent(Component, { date: dateFormat(dueDate, 'yyyy-mm-dd', true) });
+
+ beforeEach(() => {
+ date = new Date();
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render "Today" if the due date is today', () => {
+ const timeContainer = vm.$el.querySelector('time');
+
+ expect(timeContainer.textContent.trim()).toEqual('Today');
+ });
+
+ it('should render "Yesterday" if the due date is yesterday', () => {
+ date.setDate(date.getDate() - 1);
+ vm = createComponent(date);
+
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual('Yesterday');
+ });
+
+ it('should render "Tomorrow" if the due date is one day from now', () => {
+ date.setDate(date.getDate() + 1);
+ vm = createComponent(date);
+
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual('Tomorrow');
+ });
+
+ it('should render day of the week if due date is one week away', () => {
+ date.setDate(date.getDate() + 5);
+ vm = createComponent(date);
+
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual(dateFormat(date, 'dddd', true));
+ });
+
+ it('should render month and day for other dates', () => {
+ date.setDate(date.getDate() + 17);
+ vm = createComponent(date);
+
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual(
+ dateFormat(date, 'mmm d', true),
+ );
+ });
+
+ it('should contain the correct `.text-danger` css class for overdue issue', () => {
+ date.setDate(date.getDate() - 17);
+ vm = createComponent(date);
+
+ expect(vm.$el.querySelector('time').classList.contains('text-danger')).toEqual(true);
+ });
+});
diff --git a/spec/javascripts/boards/components/issue_time_estimate_spec.js b/spec/javascripts/boards/components/issue_time_estimate_spec.js
new file mode 100644
index 00000000000..ba65d3287da
--- /dev/null
+++ b/spec/javascripts/boards/components/issue_time_estimate_spec.js
@@ -0,0 +1,40 @@
+import Vue from 'vue';
+import IssueTimeEstimate from '~/boards/components/issue_time_estimate.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Issue Tine Estimate component', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(IssueTimeEstimate);
+ vm = mountComponent(Component, {
+ estimate: 374460,
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders the correct time estimate', () => {
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual('2w 3d 1m');
+ });
+
+ it('renders expanded time estimate in tooltip', () => {
+ expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain(
+ '2 weeks 3 days 1 minute',
+ );
+ });
+
+ it('prevents tooltip xss', done => {
+ const alertSpy = spyOn(window, 'alert');
+ vm.estimate = 'Foo <script>alert("XSS")</script>';
+
+ vm.$nextTick(() => {
+ expect(alertSpy).not.toHaveBeenCalled();
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual('0m');
+ expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain('0m');
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index b6c61e7bad7..6eda5047dd0 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -8,7 +8,6 @@ import '~/vue_shared/models/label';
import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
-import '~/boards/stores/boards_store';
import IssueCardInner from '~/boards/components/issue_card_inner.vue';
import { listObj } from './mock_data';
@@ -118,11 +117,9 @@ describe('Issue card component', () => {
});
it('sets title', () => {
- expect(
- component.$el
- .querySelector('.board-card-assignee img')
- .getAttribute('data-original-title'),
- ).toContain(`Assigned to ${user.name}`);
+ expect(component.$el.querySelector('.js-assignee-tooltip').textContent).toContain(
+ `${user.name}`,
+ );
});
it('sets users path', () => {
@@ -155,7 +152,7 @@ describe('Issue card component', () => {
it('displays defaults avatar if users avatar is null', () => {
expect(component.$el.querySelector('.board-card-assignee img')).not.toBeNull();
expect(component.$el.querySelector('.board-card-assignee img').getAttribute('src')).toBe(
- 'default_avatar?width=20',
+ 'default_avatar?width=24',
);
});
});
@@ -164,7 +161,6 @@ describe('Issue card component', () => {
describe('multiple assignees', () => {
beforeEach(done => {
component.issue.assignees = [
- user,
new ListAssignee({
id: 2,
name: 'user2',
@@ -188,11 +184,11 @@ describe('Issue card component', () => {
Vue.nextTick(() => done());
});
- it('renders all four assignees', () => {
- expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(4);
+ it('renders all three assignees', () => {
+ expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(3);
});
- describe('more than four assignees', () => {
+ describe('more than three assignees', () => {
beforeEach(done => {
component.issue.assignees.push(
new ListAssignee({
@@ -208,12 +204,12 @@ describe('Issue card component', () => {
it('renders more avatar counter', () => {
expect(
- component.$el.querySelector('.board-card-assignee .avatar-counter').innerText,
+ component.$el.querySelector('.board-card-assignee .avatar-counter').innerText.trim(),
).toEqual('+2');
});
- it('renders three assignees', () => {
- expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(3);
+ it('renders two assignees', () => {
+ expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(2);
});
it('renders 99+ avatar counter', done => {
@@ -229,7 +225,7 @@ describe('Issue card component', () => {
Vue.nextTick(() => {
expect(
- component.$el.querySelector('.board-card-assignee .avatar-counter').innerText,
+ component.$el.querySelector('.board-card-assignee .avatar-counter').innerText.trim(),
).toEqual('99+');
done();
});
diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js
index db68096e3bd..437ab4bb3df 100644
--- a/spec/javascripts/boards/issue_spec.js
+++ b/spec/javascripts/boards/issue_spec.js
@@ -1,4 +1,3 @@
-/* eslint-disable comma-dangle */
/* global ListIssue */
import Vue from 'vue';
@@ -7,7 +6,7 @@ import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import '~/boards/services/board_service';
-import '~/boards/stores/boards_store';
+import boardsStore from '~/boards/stores/boards_store';
import { mockBoardService } from './mock_data';
describe('Issue model', () => {
@@ -15,25 +14,29 @@ describe('Issue model', () => {
beforeEach(() => {
gl.boardService = mockBoardService();
- gl.issueBoards.BoardsStore.create();
+ boardsStore.create();
issue = new ListIssue({
title: 'Testing',
id: 1,
iid: 1,
confidential: false,
- labels: [{
- id: 1,
- title: 'test',
- color: 'red',
- description: 'testing'
- }],
- assignees: [{
- id: 1,
- name: 'name',
- username: 'username',
- avatar_url: 'http://avatar_url',
- }],
+ labels: [
+ {
+ id: 1,
+ title: 'test',
+ color: 'red',
+ description: 'testing',
+ },
+ ],
+ assignees: [
+ {
+ id: 1,
+ name: 'name',
+ username: 'username',
+ avatar_url: 'http://avatar_url',
+ },
+ ],
});
});
@@ -46,8 +49,9 @@ describe('Issue model', () => {
id: 2,
title: 'bug',
color: 'blue',
- description: 'bugs!'
+ description: 'bugs!',
});
+
expect(issue.labels.length).toBe(2);
});
@@ -56,7 +60,7 @@ describe('Issue model', () => {
id: 2,
title: 'test',
color: 'blue',
- description: 'bugs!'
+ description: 'bugs!',
});
expect(issue.labels.length).toBe(1);
@@ -64,12 +68,14 @@ describe('Issue model', () => {
it('finds label', () => {
const label = issue.findLabel(issue.labels[0]);
+
expect(label).toBeDefined();
});
it('removes label', () => {
const label = issue.findLabel(issue.labels[0]);
issue.removeLabel(label);
+
expect(issue.labels.length).toBe(0);
});
@@ -78,11 +84,13 @@ describe('Issue model', () => {
id: 2,
title: 'bug',
color: 'blue',
- description: 'bugs!'
+ description: 'bugs!',
});
+
expect(issue.labels.length).toBe(2);
issue.removeLabels([issue.labels[0], issue.labels[1]]);
+
expect(issue.labels.length).toBe(0);
});
@@ -99,17 +107,20 @@ describe('Issue model', () => {
it('finds assignee', () => {
const assignee = issue.findAssignee(issue.assignees[0]);
+
expect(assignee).toBeDefined();
});
it('removes assignee', () => {
const assignee = issue.findAssignee(issue.assignees[0]);
issue.removeAssignee(assignee);
+
expect(issue.assignees.length).toBe(0);
});
it('removes all assignees', () => {
issue.removeAllAssignees();
+
expect(issue.assignees.length).toBe(0);
});
@@ -132,6 +143,7 @@ describe('Issue model', () => {
it('updates data', () => {
issue.updateData({ subscribed: true });
+
expect(issue.subscribed).toBe(true);
});
@@ -150,7 +162,7 @@ describe('Issue model', () => {
});
describe('update', () => {
- it('passes assignee ids when there are assignees', (done) => {
+ it('passes assignee ids when there are assignees', done => {
spyOn(Vue.http, 'patch').and.callFake((url, data) => {
expect(data.issue.assignee_ids).toEqual([1]);
done();
@@ -159,7 +171,7 @@ describe('Issue model', () => {
issue.update('url');
});
- it('passes assignee ids of [0] when there are no assignees', (done) => {
+ it('passes assignee ids of [0] when there are no assignees', done => {
spyOn(Vue.http, 'patch').and.callFake((url, data) => {
expect(data.issue.assignee_ids).toEqual([0]);
done();
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index ac8bbb8f2a8..0d462a6f872 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -1,4 +1,3 @@
-/* eslint-disable comma-dangle */
/* global List */
/* global ListIssue */
@@ -10,7 +9,7 @@ import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import '~/boards/services/board_service';
-import '~/boards/stores/boards_store';
+import boardsStore from '~/boards/stores/boards_store';
import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data';
describe('List model', () => {
@@ -23,7 +22,7 @@ describe('List model', () => {
gl.boardService = mockBoardService({
bulkUpdatePath: '/test/issue-boards/board/1/lists',
});
- gl.issueBoards.BoardsStore.create();
+ boardsStore.create();
list = new List(listObj);
});
@@ -32,21 +31,21 @@ describe('List model', () => {
mock.restore();
});
- it('gets issues when created', (done) => {
+ it('gets issues when created', done => {
setTimeout(() => {
expect(list.issues.length).toBe(1);
done();
}, 0);
});
- it('saves list and returns ID', (done) => {
+ it('saves list and returns ID', done => {
list = new List({
title: 'test',
label: {
id: _.random(10000),
title: 'test',
- color: 'red'
- }
+ color: 'red',
+ },
});
list.save();
@@ -58,31 +57,35 @@ describe('List model', () => {
}, 0);
});
- it('destroys the list', (done) => {
- gl.issueBoards.BoardsStore.addList(listObj);
- list = gl.issueBoards.BoardsStore.findList('id', listObj.id);
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1);
+ it('destroys the list', done => {
+ boardsStore.addList(listObj);
+ list = boardsStore.findList('id', listObj.id);
+
+ expect(boardsStore.state.lists.length).toBe(1);
list.destroy();
setTimeout(() => {
- expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(0);
+ expect(boardsStore.state.lists.length).toBe(0);
done();
}, 0);
});
- it('gets issue from list', (done) => {
+ it('gets issue from list', done => {
setTimeout(() => {
const issue = list.findIssue(1);
+
expect(issue).toBeDefined();
done();
}, 0);
});
- it('removes issue', (done) => {
+ it('removes issue', done => {
setTimeout(() => {
const issue = list.findIssue(1);
+
expect(list.issues.length).toBe(1);
list.removeIssue(issue);
+
expect(list.issues.length).toBe(0);
done();
}, 0);
@@ -106,8 +109,13 @@ describe('List model', () => {
listDup.updateIssueLabel(issue, list);
- expect(gl.boardService.moveIssue)
- .toHaveBeenCalledWith(issue.id, list.id, listDup.id, undefined, undefined);
+ expect(gl.boardService.moveIssue).toHaveBeenCalledWith(
+ issue.id,
+ list.id,
+ listDup.id,
+ undefined,
+ undefined,
+ );
});
describe('page number', () => {
@@ -117,14 +125,16 @@ describe('List model', () => {
it('increase page number if current issue count is more than the page size', () => {
for (let i = 0; i < 30; i += 1) {
- list.issues.push(new ListIssue({
- title: 'Testing',
- id: _.random(10000) + i,
- iid: _.random(10000) + i,
- confidential: false,
- labels: [list.label],
- assignees: [],
- }));
+ list.issues.push(
+ new ListIssue({
+ title: 'Testing',
+ id: _.random(10000) + i,
+ iid: _.random(10000) + i,
+ confidential: false,
+ labels: [list.label],
+ assignees: [],
+ }),
+ );
}
list.issuesSize = 50;
@@ -137,13 +147,15 @@ describe('List model', () => {
});
it('does not increase page number if issue count is less than the page size', () => {
- list.issues.push(new ListIssue({
- title: 'Testing',
- id: _.random(10000),
- confidential: false,
- labels: [list.label],
- assignees: [],
- }));
+ list.issues.push(
+ new ListIssue({
+ title: 'Testing',
+ id: _.random(10000),
+ confidential: false,
+ labels: [list.label],
+ assignees: [],
+ }),
+ );
list.issuesSize = 2;
list.nextPage();
@@ -155,21 +167,25 @@ describe('List model', () => {
describe('newIssue', () => {
beforeEach(() => {
- spyOn(gl.boardService, 'newIssue').and.returnValue(Promise.resolve({
- data: {
- id: 42,
- },
- }));
+ spyOn(gl.boardService, 'newIssue').and.returnValue(
+ Promise.resolve({
+ data: {
+ id: 42,
+ },
+ }),
+ );
});
- it('adds new issue to top of list', (done) => {
- list.issues.push(new ListIssue({
- title: 'Testing',
- id: _.random(10000),
- confidential: false,
- labels: [list.label],
- assignees: [],
- }));
+ it('adds new issue to top of list', done => {
+ list.issues.push(
+ new ListIssue({
+ title: 'Testing',
+ id: _.random(10000),
+ confidential: false,
+ labels: [list.label],
+ assignees: [],
+ }),
+ );
const dummyIssue = new ListIssue({
title: 'new issue',
id: _.random(10000),
@@ -178,7 +194,8 @@ describe('List model', () => {
assignees: [],
});
- list.newIssue(dummyIssue)
+ list
+ .newIssue(dummyIssue)
.then(() => {
expect(list.issues.length).toBe(2);
expect(list.issues[0]).toBe(dummyIssue);
diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js
index 81f1a97112f..c28e41ec175 100644
--- a/spec/javascripts/boards/mock_data.js
+++ b/spec/javascripts/boards/mock_data.js
@@ -1,4 +1,5 @@
-/* global BoardService */
+import BoardService from '~/boards/services/board_service';
+
export const listObj = {
id: 300,
position: 0,
@@ -27,7 +28,7 @@ export const listObjDuplicate = {
export const BoardsMockData = {
GET: {
- '/test/-/boards/1/lists/300/issues?id=300&page=1&=': {
+ '/test/-/boards/1/lists/300/issues?id=300&page=1': {
issues: [
{
title: 'Testing',
diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/javascripts/boards/modal_store_spec.js
index a234c81fadf..3257a3fb8a3 100644
--- a/spec/javascripts/boards/modal_store_spec.js
+++ b/spec/javascripts/boards/modal_store_spec.js
@@ -11,7 +11,7 @@ describe('Modal store', () => {
let issue2;
beforeEach(() => {
- // Setup default state
+ // Set up default state
Store.store.issues = [];
Store.store.selectedIssues = [];
diff --git a/spec/javascripts/boards/utils/query_data_spec.js b/spec/javascripts/boards/utils/query_data_spec.js
deleted file mode 100644
index 922215ffc1d..00000000000
--- a/spec/javascripts/boards/utils/query_data_spec.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import queryData from '~/boards/utils/query_data';
-
-describe('queryData', () => {
- it('parses path for label with trailing +', () => {
- expect(
- queryData('label_name[]=label%2B', {}),
- ).toEqual({
- label_name: ['label+'],
- });
- });
-
- it('parses path for milestone with trailing +', () => {
- expect(
- queryData('milestone_title=A%2B', {}),
- ).toEqual({
- milestone_title: 'A+',
- });
- });
-
- it('parses path for search terms with spaces', () => {
- expect(
- queryData('search=two+words', {}),
- ).toEqual({
- search: 'two words',
- });
- });
-});
diff --git a/spec/javascripts/bootstrap_jquery_spec.js b/spec/javascripts/bootstrap_jquery_spec.js
index 052465d8d88..35340a3bc42 100644
--- a/spec/javascripts/bootstrap_jquery_spec.js
+++ b/spec/javascripts/bootstrap_jquery_spec.js
@@ -3,41 +3,45 @@
import $ from 'jquery';
import '~/commons/bootstrap';
-(function() {
- describe('Bootstrap jQuery extensions', function() {
- describe('disable', function() {
- beforeEach(function() {
- return setFixtures('<input type="text" />');
- });
- it('adds the disabled attribute', function() {
- var $input;
- $input = $('input').first();
- $input.disable();
- return expect($input).toHaveAttr('disabled', 'disabled');
- });
- return it('adds the disabled class', function() {
- var $input;
- $input = $('input').first();
- $input.disable();
- return expect($input).toHaveClass('disabled');
- });
+describe('Bootstrap jQuery extensions', function() {
+ describe('disable', function() {
+ beforeEach(function() {
+ return setFixtures('<input type="text" />');
});
- return describe('enable', function() {
- beforeEach(function() {
- return setFixtures('<input type="text" disabled="disabled" class="disabled" />');
- });
- it('removes the disabled attribute', function() {
- var $input;
- $input = $('input').first();
- $input.enable();
- return expect($input).not.toHaveAttr('disabled');
- });
- return it('removes the disabled class', function() {
- var $input;
- $input = $('input').first();
- $input.enable();
- return expect($input).not.toHaveClass('disabled');
- });
+
+ it('adds the disabled attribute', function() {
+ var $input;
+ $input = $('input').first();
+ $input.disable();
+
+ expect($input).toHaveAttr('disabled', 'disabled');
+ });
+ return it('adds the disabled class', function() {
+ var $input;
+ $input = $('input').first();
+ $input.disable();
+
+ expect($input).toHaveClass('disabled');
+ });
+ });
+ return describe('enable', function() {
+ beforeEach(function() {
+ return setFixtures('<input type="text" disabled="disabled" class="disabled" />');
+ });
+
+ it('removes the disabled attribute', function() {
+ var $input;
+ $input = $('input').first();
+ $input.enable();
+
+ expect($input).not.toHaveAttr('disabled');
+ });
+ return it('removes the disabled class', function() {
+ var $input;
+ $input = $('input').first();
+ $input.enable();
+
+ expect($input).not.toHaveClass('disabled');
});
});
-}).call(window);
+});
diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js
index 6f679369289..c3e3d78ff63 100644
--- a/spec/javascripts/bootstrap_linked_tabs_spec.js
+++ b/spec/javascripts/bootstrap_linked_tabs_spec.js
@@ -1,60 +1,67 @@
import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
-(() => {
- describe('Linked Tabs', () => {
- preloadFixtures('static/linked_tabs.html.raw');
+describe('Linked Tabs', () => {
+ preloadFixtures('static/linked_tabs.html.raw');
+ beforeEach(() => {
+ loadFixtures('static/linked_tabs.html.raw');
+ });
+
+ describe('when is initialized', () => {
beforeEach(() => {
- loadFixtures('static/linked_tabs.html.raw');
+ spyOn(window.history, 'replaceState').and.callFake(function() {});
});
- describe('when is initialized', () => {
- beforeEach(() => {
- spyOn(window.history, 'replaceState').and.callFake(function () {});
+ it('should activate the tab correspondent to the given action', () => {
+ // eslint-disable-next-line no-new
+ new LinkedTabs({
+ action: 'tab1',
+ defaultAction: 'tab1',
+ parentEl: '.linked-tabs',
});
- it('should activate the tab correspondent to the given action', () => {
- const linkedTabs = new LinkedTabs({ // eslint-disable-line
- action: 'tab1',
- defaultAction: 'tab1',
- parentEl: '.linked-tabs',
- });
+ expect(document.querySelector('#tab1').classList).toContain('active');
+ });
- expect(document.querySelector('#tab1').classList).toContain('active');
+ it('should active the default tab action when the action is show', () => {
+ // eslint-disable-next-line no-new
+ new LinkedTabs({
+ action: 'show',
+ defaultAction: 'tab1',
+ parentEl: '.linked-tabs',
});
- it('should active the default tab action when the action is show', () => {
- const linkedTabs = new LinkedTabs({ // eslint-disable-line
- action: 'show',
- defaultAction: 'tab1',
- parentEl: '.linked-tabs',
- });
-
- expect(document.querySelector('#tab1').classList).toContain('active');
- });
+ expect(document.querySelector('#tab1').classList).toContain('active');
});
+ });
- describe('on click', () => {
- it('should change the url according to the clicked tab', () => {
- const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {});
+ describe('on click', () => {
+ it('should change the url according to the clicked tab', () => {
+ const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {});
- const linkedTabs = new LinkedTabs({
- action: 'show',
- defaultAction: 'tab1',
- parentEl: '.linked-tabs',
- });
+ const linkedTabs = new LinkedTabs({
+ action: 'show',
+ defaultAction: 'tab1',
+ parentEl: '.linked-tabs',
+ });
- const secondTab = document.querySelector('.linked-tabs li:nth-child(2) a');
- const newState = secondTab.getAttribute('href') + linkedTabs.currentLocation.search + linkedTabs.currentLocation.hash;
+ const secondTab = document.querySelector('.linked-tabs li:nth-child(2) a');
+ const newState =
+ secondTab.getAttribute('href') +
+ linkedTabs.currentLocation.search +
+ linkedTabs.currentLocation.hash;
- secondTab.click();
+ secondTab.click();
- if (historySpy) {
- expect(historySpy).toHaveBeenCalledWith({
+ if (historySpy) {
+ expect(historySpy).toHaveBeenCalledWith(
+ {
url: newState,
- }, document.title, newState);
- }
- });
+ },
+ document.title,
+ newState,
+ );
+ }
});
});
-})();
+});
diff --git a/spec/javascripts/breakpoints_spec.js b/spec/javascripts/breakpoints_spec.js
index b1b5d36c1fb..5ee777fee3f 100644
--- a/spec/javascripts/breakpoints_spec.js
+++ b/spec/javascripts/breakpoints_spec.js
@@ -1,9 +1,7 @@
-import bp, {
- breakpoints,
-} from '~/breakpoints';
+import bp, { breakpoints } from '~/breakpoints';
describe('breakpoints', () => {
- Object.keys(breakpoints).forEach((key) => {
+ Object.keys(breakpoints).forEach(key => {
const size = breakpoints[key];
it(`returns ${key} when larger than ${size}`, () => {
diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
index ee457a9c48c..1fc0e206d5e 100644
--- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -24,7 +24,7 @@ describe('AjaxFormVariableList', () => {
mock = new MockAdapter(axios);
const ajaxVariableListEl = document.querySelector('.js-ci-variable-list-section');
- saveButton = ajaxVariableListEl.querySelector('.js-secret-variables-save-button');
+ saveButton = ajaxVariableListEl.querySelector('.js-ci-variables-save-button');
errorBox = container.querySelector('.js-ci-variable-error-box');
ajaxVariableList = new AjaxFormVariableList({
container,
@@ -43,8 +43,8 @@ describe('AjaxFormVariableList', () => {
});
describe('onSaveClicked', () => {
- it('shows loading spinner while waiting for the request', (done) => {
- const loadingIcon = saveButton.querySelector('.js-secret-variables-save-loading-icon');
+ it('shows loading spinner while waiting for the request', done => {
+ const loadingIcon = saveButton.querySelector('.js-ci-variables-save-loading-icon');
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => {
expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(false);
@@ -54,7 +54,8 @@ describe('AjaxFormVariableList', () => {
expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true);
- ajaxVariableList.onSaveClicked()
+ ajaxVariableList
+ .onSaveClicked()
.then(() => {
expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true);
})
@@ -62,27 +63,30 @@ describe('AjaxFormVariableList', () => {
.catch(done.fail);
});
- it('calls `updateRowsWithPersistedVariables` with the persisted variables', (done) => {
+ it('calls `updateRowsWithPersistedVariables` with the persisted variables', done => {
const variablesResponse = [{ id: 1, key: 'foo', value: 'bar' }];
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {
variables: variablesResponse,
});
- ajaxVariableList.onSaveClicked()
+ ajaxVariableList
+ .onSaveClicked()
.then(() => {
- expect(ajaxVariableList.updateRowsWithPersistedVariables)
- .toHaveBeenCalledWith(variablesResponse);
+ expect(ajaxVariableList.updateRowsWithPersistedVariables).toHaveBeenCalledWith(
+ variablesResponse,
+ );
})
.then(done)
.catch(done.fail);
});
- it('hides any previous error box', (done) => {
+ it('hides any previous error box', done => {
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200);
expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
- ajaxVariableList.onSaveClicked()
+ ajaxVariableList
+ .onSaveClicked()
.then(() => {
expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
})
@@ -90,14 +94,15 @@ describe('AjaxFormVariableList', () => {
.catch(done.fail);
});
- it('disables remove buttons while waiting for the request', (done) => {
+ it('disables remove buttons while waiting for the request', done => {
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => {
expect(ajaxVariableList.variableList.toggleEnableRow).toHaveBeenCalledWith(false);
return [200, {}];
});
- ajaxVariableList.onSaveClicked()
+ ajaxVariableList
+ .onSaveClicked()
.then(() => {
expect(ajaxVariableList.variableList.toggleEnableRow).toHaveBeenCalledWith(true);
})
@@ -105,7 +110,7 @@ describe('AjaxFormVariableList', () => {
.catch(done.fail);
});
- it('hides secret values', (done) => {
+ it('hides secret values', done => {
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {});
const row = container.querySelector('.js-row:first-child');
@@ -118,7 +123,8 @@ describe('AjaxFormVariableList', () => {
expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(true);
expect(valueInput.classList.contains(HIDE_CLASS)).toBe(false);
- ajaxVariableList.onSaveClicked()
+ ajaxVariableList
+ .onSaveClicked()
.then(() => {
expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(false);
expect(valueInput.classList.contains(HIDE_CLASS)).toBe(true);
@@ -127,29 +133,31 @@ describe('AjaxFormVariableList', () => {
.catch(done.fail);
});
- it('shows error box with validation errors', (done) => {
+ it('shows error box with validation errors', done => {
const validationError = 'some validation error';
- mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(400, [
- validationError,
- ]);
+ mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(400, [validationError]);
expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
- ajaxVariableList.onSaveClicked()
+ ajaxVariableList
+ .onSaveClicked()
.then(() => {
expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(false);
- expect(errorBox.textContent.trim().replace(/\n+\s+/m, ' ')).toEqual(`Validation failed ${validationError}`);
+ expect(errorBox.textContent.trim().replace(/\n+\s+/m, ' ')).toEqual(
+ `Validation failed ${validationError}`,
+ );
})
.then(done)
.catch(done.fail);
});
- it('shows flash message when request fails', (done) => {
+ it('shows flash message when request fails', done => {
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(500);
expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
- ajaxVariableList.onSaveClicked()
+ ajaxVariableList
+ .onSaveClicked()
.then(() => {
expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
})
@@ -164,7 +172,7 @@ describe('AjaxFormVariableList', () => {
container = document.querySelector('.js-ci-variable-list-section');
const ajaxVariableListEl = document.querySelector('.js-ci-variable-list-section');
- saveButton = ajaxVariableListEl.querySelector('.js-secret-variables-save-button');
+ saveButton = ajaxVariableListEl.querySelector('.js-ci-variables-save-button');
errorBox = container.querySelector('.js-ci-variable-error-box');
ajaxVariableList = new AjaxFormVariableList({
container,
@@ -200,11 +208,13 @@ describe('AjaxFormVariableList', () => {
expect(idInput.value).toEqual('');
- ajaxVariableList.updateRowsWithPersistedVariables([{
- id: 3,
- key: 'foo',
- value: 'bar',
- }]);
+ ajaxVariableList.updateRowsWithPersistedVariables([
+ {
+ id: 3,
+ key: 'foo',
+ value: 'bar',
+ },
+ ]);
expect(idInput.value).toEqual('3');
expect(row.dataset.isPersisted).toEqual('true');
diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
index 2fa50975f0f..30b15011def 100644
--- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
@@ -33,7 +33,8 @@ describe('VariableList', () => {
it('should add another row when editing the last rows key input', () => {
const $row = $wrapper.find('.js-row');
- $row.find('.js-ci-variable-input-key')
+ $row
+ .find('.js-ci-variable-input-key')
.val('foo')
.trigger('input');
@@ -41,12 +42,14 @@ describe('VariableList', () => {
// Check for the correct default in the new row
const $keyInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-key');
+
expect($keyInput.val()).toBe('');
});
it('should add another row when editing the last rows value textarea', () => {
const $row = $wrapper.find('.js-row');
- $row.find('.js-ci-variable-input-value')
+ $row
+ .find('.js-ci-variable-input-value')
.val('foo')
.trigger('input');
@@ -54,18 +57,21 @@ describe('VariableList', () => {
// Check for the correct default in the new row
const $valueInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-key');
+
expect($valueInput.val()).toBe('');
});
it('should remove empty row after blurring', () => {
const $row = $wrapper.find('.js-row');
- $row.find('.js-ci-variable-input-key')
+ $row
+ .find('.js-ci-variable-input-key')
.val('foo')
.trigger('input');
expect($wrapper.find('.js-row').length).toBe(2);
- $row.find('.js-ci-variable-input-key')
+ $row
+ .find('.js-ci-variable-input-key')
.val('')
.trigger('input')
.trigger('blur');
@@ -119,7 +125,7 @@ describe('VariableList', () => {
variableList.init();
});
- it('should add another row when editing the last rows protected checkbox', (done) => {
+ it('should add another row when editing the last rows protected checkbox', done => {
const $row = $wrapper.find('.js-row:last-child');
$row.find('.ci-variable-protected-item .js-project-feature-toggle').click();
@@ -128,7 +134,10 @@ describe('VariableList', () => {
expect($wrapper.find('.js-row').length).toBe(2);
// Check for the correct default in the new row
- const $protectedInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-protected');
+ const $protectedInput = $wrapper
+ .find('.js-row:last-child')
+ .find('.js-ci-variable-input-protected');
+
expect($protectedInput.val()).toBe('false');
})
.then(done)
@@ -166,6 +175,7 @@ describe('VariableList', () => {
it('should enable all remove buttons', () => {
variableList.toggleEnableRow(false);
+
expect($wrapper.find('.js-row-remove-button[disabled]').length).toBe(3);
variableList.toggleEnableRow(true);
@@ -175,6 +185,7 @@ describe('VariableList', () => {
it('should enable all key inputs', () => {
variableList.toggleEnableRow(false);
+
expect($wrapper.find('.js-ci-variable-input-key[disabled]').length).toBe(3);
variableList.toggleEnableRow(true);
@@ -200,7 +211,8 @@ describe('VariableList', () => {
const $inputValue = $row.find('.js-ci-variable-input-value');
const $placeholder = $row.find('.js-secret-value-placeholder');
- $row.find('.js-ci-variable-input-value')
+ $row
+ .find('.js-ci-variable-input-value')
.val('foo')
.trigger('input');
diff --git a/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js b/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js
index 94a0c999d66..997d0d54d79 100644
--- a/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js
@@ -19,8 +19,14 @@ describe('NativeFormVariableList', () => {
describe('onFormSubmit', () => {
it('should clear out the `name` attribute on the inputs for the last empty row on form submission (avoid BE validation)', () => {
const $row = $wrapper.find('.js-row');
- expect($row.find('.js-ci-variable-input-key').attr('name')).toBe('schedule[variables_attributes][][key]');
- expect($row.find('.js-ci-variable-input-value').attr('name')).toBe('schedule[variables_attributes][][secret_value]');
+
+ expect($row.find('.js-ci-variable-input-key').attr('name')).toBe(
+ 'schedule[variables_attributes][][key]',
+ );
+
+ expect($row.find('.js-ci-variable-input-value').attr('name')).toBe(
+ 'schedule[variables_attributes][][secret_value]',
+ );
$wrapper.closest('form').trigger('trigger-submit');
diff --git a/spec/javascripts/close_reopen_report_toggle_spec.js b/spec/javascripts/close_reopen_report_toggle_spec.js
index 925e959c85a..04a7ae7f429 100644
--- a/spec/javascripts/close_reopen_report_toggle_spec.js
+++ b/spec/javascripts/close_reopen_report_toggle_spec.js
@@ -1,3 +1,5 @@
+/* eslint-disable jasmine/no-unsafe-spy */
+
import CloseReopenReportToggle from '~/close_reopen_report_toggle';
import DropLab from '~/droplab/drop_lab';
@@ -8,7 +10,7 @@ describe('CloseReopenReportToggle', () => {
const button = {};
let commentTypeToggle;
- beforeEach(function () {
+ beforeEach(function() {
commentTypeToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
@@ -16,15 +18,15 @@ describe('CloseReopenReportToggle', () => {
});
});
- it('sets .dropdownTrigger', function () {
+ it('sets .dropdownTrigger', function() {
expect(commentTypeToggle.dropdownTrigger).toBe(dropdownTrigger);
});
- it('sets .dropdownList', function () {
+ it('sets .dropdownList', function() {
expect(commentTypeToggle.dropdownList).toBe(dropdownList);
});
- it('sets .button', function () {
+ it('sets .button', function() {
expect(commentTypeToggle.button).toBe(button);
});
});
diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js
index d0e0b214509..880b469284b 100644
--- a/spec/javascripts/clusters/clusters_bundle_spec.js
+++ b/spec/javascripts/clusters/clusters_bundle_spec.js
@@ -21,21 +21,21 @@ describe('Clusters', () => {
});
describe('toggle', () => {
- it('should update the button and the input field on click', (done) => {
- const toggleButton = document.querySelector('.js-cluster-enable-toggle-area .js-project-feature-toggle');
- const toggleInput = document.querySelector('.js-cluster-enable-toggle-area .js-project-feature-toggle-input');
+ it('should update the button and the input field on click', done => {
+ const toggleButton = document.querySelector(
+ '.js-cluster-enable-toggle-area .js-project-feature-toggle',
+ );
+ const toggleInput = document.querySelector(
+ '.js-cluster-enable-toggle-area .js-project-feature-toggle-input',
+ );
toggleButton.click();
getSetTimeoutPromise()
.then(() => {
- expect(
- toggleButton.classList,
- ).not.toContain('is-checked');
+ expect(toggleButton.classList).not.toContain('is-checked');
- expect(
- toggleInput.getAttribute('value'),
- ).toEqual('false');
+ expect(toggleInput.getAttribute('value')).toEqual('false');
})
.then(done)
.catch(done.fail);
@@ -46,29 +46,21 @@ describe('Clusters', () => {
it('should update token field type', () => {
cluster.showTokenButton.click();
- expect(
- cluster.tokenField.getAttribute('type'),
- ).toEqual('text');
+ expect(cluster.tokenField.getAttribute('type')).toEqual('text');
cluster.showTokenButton.click();
- expect(
- cluster.tokenField.getAttribute('type'),
- ).toEqual('password');
+ expect(cluster.tokenField.getAttribute('type')).toEqual('password');
});
it('should update show token button text', () => {
cluster.showTokenButton.click();
- expect(
- cluster.showTokenButton.textContent,
- ).toEqual('Hide');
+ expect(cluster.showTokenButton.textContent).toEqual('Hide');
cluster.showTokenButton.click();
- expect(
- cluster.showTokenButton.textContent,
- ).toEqual('Show');
+ expect(cluster.showTokenButton.textContent).toEqual('Show');
});
});
@@ -86,37 +78,50 @@ describe('Clusters', () => {
});
const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
+
expect(flashMessage).toBeNull();
});
it('shows an alert when something gets newly installed', () => {
- cluster.checkForNewInstalls({
- ...INITIAL_APP_MAP,
- helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' },
- }, {
- ...INITIAL_APP_MAP,
- helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' },
- });
+ cluster.checkForNewInstalls(
+ {
+ ...INITIAL_APP_MAP,
+ helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' },
+ },
+ {
+ ...INITIAL_APP_MAP,
+ helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' },
+ },
+ );
const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
+
expect(flashMessage).not.toBeNull();
- expect(flashMessage.textContent.trim()).toEqual('Helm Tiller was successfully installed on your Kubernetes cluster');
+ expect(flashMessage.textContent.trim()).toEqual(
+ 'Helm Tiller was successfully installed on your Kubernetes cluster',
+ );
});
it('shows an alert when multiple things gets newly installed', () => {
- cluster.checkForNewInstalls({
- ...INITIAL_APP_MAP,
- helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' },
- ingress: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Ingress' },
- }, {
- ...INITIAL_APP_MAP,
- helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' },
- ingress: { status: APPLICATION_STATUS.INSTALLED, title: 'Ingress' },
- });
+ cluster.checkForNewInstalls(
+ {
+ ...INITIAL_APP_MAP,
+ helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' },
+ ingress: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Ingress' },
+ },
+ {
+ ...INITIAL_APP_MAP,
+ helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' },
+ ingress: { status: APPLICATION_STATUS.INSTALLED, title: 'Ingress' },
+ },
+ );
const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
+
expect(flashMessage).not.toBeNull();
- expect(flashMessage.textContent.trim()).toEqual('Helm Tiller, Ingress was successfully installed on your Kubernetes cluster');
+ expect(flashMessage.textContent.trim()).toEqual(
+ 'Helm Tiller, Ingress was successfully installed on your Kubernetes cluster',
+ );
});
});
@@ -125,29 +130,21 @@ describe('Clusters', () => {
it('should show the creating container', () => {
cluster.updateContainer(null, 'creating');
- expect(
- cluster.creatingContainer.classList.contains('hidden'),
- ).toBeFalsy();
- expect(
- cluster.successContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.errorContainer.classList.contains('hidden'),
- ).toBeTruthy();
+ expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy();
+
+ expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy();
});
it('should continue to show `creating` banner with subsequent updates of the same status', () => {
cluster.updateContainer('creating', 'creating');
- expect(
- cluster.creatingContainer.classList.contains('hidden'),
- ).toBeFalsy();
- expect(
- cluster.successContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.errorContainer.classList.contains('hidden'),
- ).toBeTruthy();
+ expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy();
+
+ expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy();
});
});
@@ -155,29 +152,21 @@ describe('Clusters', () => {
it('should show the success container and fresh the page', () => {
cluster.updateContainer(null, 'created');
- expect(
- cluster.creatingContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.successContainer.classList.contains('hidden'),
- ).toBeFalsy();
- expect(
- cluster.errorContainer.classList.contains('hidden'),
- ).toBeTruthy();
+ expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.successContainer.classList.contains('hidden')).toBeFalsy();
+
+ expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy();
});
it('should not show a banner when status is already `created`', () => {
cluster.updateContainer('created', 'created');
- expect(
- cluster.creatingContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.successContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.errorContainer.classList.contains('hidden'),
- ).toBeTruthy();
+ expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy();
});
});
@@ -185,40 +174,31 @@ describe('Clusters', () => {
it('should show the error container', () => {
cluster.updateContainer(null, 'errored', 'this is an error');
- expect(
- cluster.creatingContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.successContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.errorContainer.classList.contains('hidden'),
- ).toBeFalsy();
-
- expect(
- cluster.errorReasonContainer.textContent,
- ).toContain('this is an error');
+ expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy();
+
+ expect(cluster.errorReasonContainer.textContent).toContain('this is an error');
});
it('should show `error` banner when previously `creating`', () => {
cluster.updateContainer('creating', 'errored');
- expect(
- cluster.creatingContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.successContainer.classList.contains('hidden'),
- ).toBeTruthy();
- expect(
- cluster.errorContainer.classList.contains('hidden'),
- ).toBeFalsy();
+ expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy();
+
+ expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy();
});
});
});
describe('installApplication', () => {
- it('tries to install helm', (done) => {
+ it('tries to install helm', done => {
spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve());
+
expect(cluster.store.state.applications.helm.requestStatus).toEqual(null);
cluster.installApplication({ id: 'helm' });
@@ -236,8 +216,9 @@ describe('Clusters', () => {
.catch(done.fail);
});
- it('tries to install ingress', (done) => {
+ it('tries to install ingress', done => {
spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve());
+
expect(cluster.store.state.applications.ingress.requestStatus).toEqual(null);
cluster.installApplication({ id: 'ingress' });
@@ -255,8 +236,9 @@ describe('Clusters', () => {
.catch(done.fail);
});
- it('tries to install runner', (done) => {
+ it('tries to install runner', done => {
spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve());
+
expect(cluster.store.state.applications.runner.requestStatus).toEqual(null);
cluster.installApplication({ id: 'runner' });
@@ -274,26 +256,35 @@ describe('Clusters', () => {
.catch(done.fail);
});
- it('tries to install jupyter', (done) => {
+ it('tries to install jupyter', done => {
spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve());
+
expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(null);
- cluster.installApplication({ id: 'jupyter', params: { hostname: cluster.store.state.applications.jupyter.hostname } });
+ cluster.installApplication({
+ id: 'jupyter',
+ params: { hostname: cluster.store.state.applications.jupyter.hostname },
+ });
expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_LOADING);
expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null);
- expect(cluster.service.installApplication).toHaveBeenCalledWith('jupyter', { hostname: cluster.store.state.applications.jupyter.hostname });
+ expect(cluster.service.installApplication).toHaveBeenCalledWith('jupyter', {
+ hostname: cluster.store.state.applications.jupyter.hostname,
+ });
getSetTimeoutPromise()
- .then(() => {
- expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUCCESS);
- expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null);
- })
- .then(done)
- .catch(done.fail);
+ .then(() => {
+ expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUCCESS);
+ expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('sets error request status when the request fails', (done) => {
- spyOn(cluster.service, 'installApplication').and.returnValue(Promise.reject(new Error('STUBBED ERROR')));
+ it('sets error request status when the request fails', done => {
+ spyOn(cluster.service, 'installApplication').and.returnValue(
+ Promise.reject(new Error('STUBBED ERROR')),
+ );
+
expect(cluster.store.state.applications.helm.requestStatus).toEqual(null);
cluster.installApplication({ id: 'helm' });
diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js
index 9da5c248371..45d56514930 100644
--- a/spec/javascripts/clusters/components/application_row_spec.js
+++ b/spec/javascripts/clusters/components/application_row_spec.js
@@ -112,6 +112,17 @@ describe('Application Row', () => {
expect(vm.installButtonDisabled).toEqual(true);
});
+ it('has disabled "Installed" when APPLICATION_STATUS.UPDATING', () => {
+ vm = mountComponent(ApplicationRow, {
+ ...DEFAULT_APPLICATION_STATE,
+ status: APPLICATION_STATUS.UPDATING,
+ });
+
+ expect(vm.installButtonLabel).toEqual('Installed');
+ expect(vm.installButtonLoading).toEqual(false);
+ expect(vm.installButtonDisabled).toEqual(true);
+ });
+
it('has enabled "Install" when APPLICATION_STATUS.ERROR', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
@@ -215,7 +226,9 @@ describe('Application Row', () => {
status: null,
requestStatus: null,
});
- const generalErrorMessage = vm.$el.querySelector('.js-cluster-application-general-error-message');
+ const generalErrorMessage = vm.$el.querySelector(
+ '.js-cluster-application-general-error-message',
+ );
expect(generalErrorMessage).toBeNull();
});
@@ -227,10 +240,17 @@ describe('Application Row', () => {
status: APPLICATION_STATUS.ERROR,
statusReason,
});
- const generalErrorMessage = vm.$el.querySelector('.js-cluster-application-general-error-message');
- const statusErrorMessage = vm.$el.querySelector('.js-cluster-application-status-error-message');
+ const generalErrorMessage = vm.$el.querySelector(
+ '.js-cluster-application-general-error-message',
+ );
+ const statusErrorMessage = vm.$el.querySelector(
+ '.js-cluster-application-status-error-message',
+ );
+
+ expect(generalErrorMessage.textContent.trim()).toEqual(
+ `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`,
+ );
- expect(generalErrorMessage.textContent.trim()).toEqual(`Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`);
expect(statusErrorMessage.textContent.trim()).toEqual(statusReason);
});
@@ -242,10 +262,17 @@ describe('Application Row', () => {
requestStatus: REQUEST_FAILURE,
requestReason,
});
- const generalErrorMessage = vm.$el.querySelector('.js-cluster-application-general-error-message');
- const requestErrorMessage = vm.$el.querySelector('.js-cluster-application-request-error-message');
+ const generalErrorMessage = vm.$el.querySelector(
+ '.js-cluster-application-general-error-message',
+ );
+ const requestErrorMessage = vm.$el.querySelector(
+ '.js-cluster-application-request-error-message',
+ );
+
+ expect(generalErrorMessage.textContent.trim()).toEqual(
+ `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`,
+ );
- expect(generalErrorMessage.textContent.trim()).toEqual(`Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`);
expect(requestErrorMessage.textContent.trim()).toEqual(requestReason);
});
});
diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js
index a70138c7eee..0e2cc13fa52 100644
--- a/spec/javascripts/clusters/components/applications_spec.js
+++ b/spec/javascripts/clusters/components/applications_spec.js
@@ -23,6 +23,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub' },
+ knative: { title: 'Knative' },
},
});
});
@@ -46,6 +47,10 @@ describe('Applications', () => {
it('renders a row for Jupyter', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-jupyter')).not.toBe(null);
});
+
+ it('renders a row for Knative', () => {
+ expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBe(null);
+ });
});
describe('Ingress application', () => {
@@ -63,6 +68,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
+ knative: { title: 'Knative', hostname: '' },
},
});
@@ -86,6 +92,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
+ knative: { title: 'Knative', hostname: '' },
},
});
@@ -105,6 +112,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
+ knative: { title: 'Knative', hostname: '' },
},
});
@@ -123,6 +131,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
+ knative: { title: 'Knative', hostname: '', status: 'installable' },
},
});
@@ -139,6 +148,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
+ knative: { title: 'Knative', hostname: '', status: 'installable' },
},
});
@@ -155,6 +165,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' },
+ knative: { title: 'Knative', status: 'installed', hostname: '' },
},
});
@@ -171,6 +182,7 @@ describe('Applications', () => {
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', status: 'not_installable' },
+ knative: { title: 'Knative' },
},
});
});
diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js
index c7c1412e1c6..73abf6504c0 100644
--- a/spec/javascripts/clusters/services/mock_data.js
+++ b/spec/javascripts/clusters/services/mock_data.js
@@ -6,67 +6,88 @@ const CLUSTERS_MOCK_DATA = {
data: {
status: 'errored',
status_reason: 'Failed to request to CloudPlatform.',
- applications: [{
- name: 'helm',
- status: APPLICATION_STATUS.INSTALLABLE,
- status_reason: null,
- }, {
- name: 'ingress',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- external_ip: null,
- }, {
- name: 'runner',
- status: APPLICATION_STATUS.INSTALLING,
- status_reason: null,
- },
- {
- name: 'prometheus',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- }, {
- name: 'jupyter',
- status: APPLICATION_STATUS.INSTALLING,
- status_reason: 'Cannot connect',
- }],
+ applications: [
+ {
+ name: 'helm',
+ status: APPLICATION_STATUS.INSTALLABLE,
+ status_reason: null,
+ },
+ {
+ name: 'ingress',
+ status: APPLICATION_STATUS.ERROR,
+ status_reason: 'Cannot connect',
+ external_ip: null,
+ },
+ {
+ name: 'runner',
+ status: APPLICATION_STATUS.INSTALLING,
+ status_reason: null,
+ },
+ {
+ name: 'prometheus',
+ status: APPLICATION_STATUS.ERROR,
+ status_reason: 'Cannot connect',
+ },
+ {
+ name: 'jupyter',
+ status: APPLICATION_STATUS.INSTALLING,
+ status_reason: 'Cannot connect',
+ },
+ {
+ name: 'knative',
+ status: APPLICATION_STATUS.INSTALLING,
+ status_reason: 'Cannot connect',
+ },
+ ],
},
},
'/gitlab-org/gitlab-shell/clusters/2/status.json': {
data: {
status: 'errored',
status_reason: 'Failed to request to CloudPlatform.',
- applications: [{
- name: 'helm',
- status: APPLICATION_STATUS.INSTALLED,
- status_reason: null,
- }, {
- name: 'ingress',
- status: APPLICATION_STATUS.INSTALLED,
- status_reason: 'Cannot connect',
- external_ip: '1.1.1.1',
- }, {
- name: 'runner',
- status: APPLICATION_STATUS.INSTALLING,
- status_reason: null,
- },
- {
- name: 'prometheus',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- }, {
- name: 'jupyter',
- status: APPLICATION_STATUS.INSTALLABLE,
- status_reason: 'Cannot connect',
- }],
+ applications: [
+ {
+ name: 'helm',
+ status: APPLICATION_STATUS.INSTALLED,
+ status_reason: null,
+ },
+ {
+ name: 'ingress',
+ status: APPLICATION_STATUS.INSTALLED,
+ status_reason: 'Cannot connect',
+ external_ip: '1.1.1.1',
+ },
+ {
+ name: 'runner',
+ status: APPLICATION_STATUS.INSTALLING,
+ status_reason: null,
+ },
+ {
+ name: 'prometheus',
+ status: APPLICATION_STATUS.ERROR,
+ status_reason: 'Cannot connect',
+ },
+ {
+ name: 'jupyter',
+ status: APPLICATION_STATUS.INSTALLABLE,
+ status_reason: 'Cannot connect',
+ },
+ {
+ name: 'knative',
+ status: APPLICATION_STATUS.INSTALLABLE,
+ status_reason: 'Cannot connect',
+ },
+ ],
},
},
},
POST: {
- '/gitlab-org/gitlab-shell/clusters/1/applications/helm': { },
- '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': { },
- '/gitlab-org/gitlab-shell/clusters/1/applications/runner': { },
- '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': { },
- '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': { },
+ '/gitlab-org/gitlab-shell/clusters/1/applications/helm': {},
+ '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': {},
+ '/gitlab-org/gitlab-shell/clusters/1/applications/runner': {},
+ '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {},
+ '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {},
+ '/gitlab-org/gitlab-shell/clusters/1/applications/knative': {},
},
};
@@ -81,7 +102,4 @@ const DEFAULT_APPLICATION_STATE = {
requestReason: null,
};
-export {
- CLUSTERS_MOCK_DATA,
- DEFAULT_APPLICATION_STATE,
-};
+export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE };
diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js
index 104a064bdd3..34ed36afa5b 100644
--- a/spec/javascripts/clusters/stores/clusters_store_spec.js
+++ b/spec/javascripts/clusters/stores/clusters_store_spec.js
@@ -53,7 +53,8 @@ describe('Clusters Store', () => {
describe('updateStateFromServer', () => {
it('should store new polling data from server', () => {
- const mockResponseData = CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/1/status.json'].data;
+ const mockResponseData =
+ CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/1/status.json'].data;
store.updateStateFromServer(mockResponseData);
expect(store.state).toEqual({
@@ -99,18 +100,27 @@ describe('Clusters Store', () => {
requestReason: null,
hostname: '',
},
+ knative: {
+ title: 'Knative',
+ status: mockResponseData.applications[5].status,
+ statusReason: mockResponseData.applications[5].status_reason,
+ requestStatus: null,
+ requestReason: null,
+ hostname: null,
+ },
},
});
});
it('sets default hostname for jupyter when ingress has a ip address', () => {
- const mockResponseData = CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data;
+ const mockResponseData =
+ CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data;
store.updateStateFromServer(mockResponseData);
- expect(
- store.state.applications.jupyter.hostname,
- ).toEqual(`jupyter.${store.state.applications.ingress.externalIp}.nip.io`);
+ expect(store.state.applications.jupyter.hostname).toEqual(
+ `jupyter.${store.state.applications.ingress.externalIp}.nip.io`,
+ );
});
});
});
diff --git a/spec/javascripts/collapsed_sidebar_todo_spec.js b/spec/javascripts/collapsed_sidebar_todo_spec.js
index 8427e8a0ba7..dc5737558c0 100644
--- a/spec/javascripts/collapsed_sidebar_todo_spec.js
+++ b/spec/javascripts/collapsed_sidebar_todo_spec.js
@@ -18,10 +18,8 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
new Sidebar();
loadFixtures(fixtureName);
- document.querySelector('.js-right-sidebar')
- .classList.toggle('right-sidebar-expanded');
- document.querySelector('.js-right-sidebar')
- .classList.toggle('right-sidebar-collapsed');
+ document.querySelector('.js-right-sidebar').classList.toggle('right-sidebar-expanded');
+ document.querySelector('.js-right-sidebar').classList.toggle('right-sidebar-collapsed');
mock = new MockAdapter(axios);
@@ -44,13 +42,13 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
});
it('shows add todo button', () => {
- expect(
- document.querySelector('.js-issuable-todo.sidebar-collapsed-icon'),
- ).not.toBeNull();
+ expect(document.querySelector('.js-issuable-todo.sidebar-collapsed-icon')).not.toBeNull();
expect(
- document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .fa-plus-square'),
- ).not.toBeNull();
+ document
+ .querySelector('.js-issuable-todo.sidebar-collapsed-icon svg use')
+ .getAttribute('xlink:href'),
+ ).toContain('todo-add');
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
@@ -63,7 +61,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
).toBe('Add todo');
});
- it('toggle todo state', (done) => {
+ it('toggle todo state', done => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setTimeout(() => {
@@ -72,14 +70,16 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
).not.toBeNull();
expect(
- document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .fa-check-square'),
- ).not.toBeNull();
+ document
+ .querySelector('.js-issuable-todo.sidebar-collapsed-icon svg.todo-undone use')
+ .getAttribute('xlink:href'),
+ ).toContain('todo-done');
done();
});
});
- it('toggle todo state of expanded todo toggle', (done) => {
+ it('toggle todo state of expanded todo toggle', done => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setTimeout(() => {
@@ -91,19 +91,21 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
});
});
- it('toggles todo button tooltip', (done) => {
+ it('toggles todo button tooltip', done => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setTimeout(() => {
expect(
- document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('data-original-title'),
+ document
+ .querySelector('.js-issuable-todo.sidebar-collapsed-icon')
+ .getAttribute('data-original-title'),
).toBe('Mark todo as done');
done();
});
});
- it('marks todo as done', (done) => {
+ it('marks todo as done', done => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
timeoutPromise()
@@ -128,25 +130,29 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
.catch(done.fail);
});
- it('updates aria-label to mark todo as done', (done) => {
+ it('updates aria-label to mark todo as done', done => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setTimeout(() => {
expect(
- document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'),
+ document
+ .querySelector('.js-issuable-todo.sidebar-collapsed-icon')
+ .getAttribute('aria-label'),
).toBe('Mark todo as done');
done();
});
});
- it('updates aria-label to add todo', (done) => {
+ it('updates aria-label to add todo', done => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
timeoutPromise()
.then(() => {
expect(
- document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'),
+ document
+ .querySelector('.js-issuable-todo.sidebar-collapsed-icon')
+ .getAttribute('aria-label'),
).toBe('Mark todo as done');
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
@@ -154,7 +160,9 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
.then(timeoutPromise)
.then(() => {
expect(
- document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'),
+ document
+ .querySelector('.js-issuable-todo.sidebar-collapsed-icon')
+ .getAttribute('aria-label'),
).toBe('Add todo');
})
.then(done)
diff --git a/spec/javascripts/comment_type_toggle_spec.js b/spec/javascripts/comment_type_toggle_spec.js
index 0ba709298c5..8b1217a000f 100644
--- a/spec/javascripts/comment_type_toggle_spec.js
+++ b/spec/javascripts/comment_type_toggle_spec.js
@@ -1,9 +1,9 @@
import CommentTypeToggle from '~/comment_type_toggle';
import InputSetter from '~/droplab/plugins/input_setter';
-describe('CommentTypeToggle', function () {
- describe('class constructor', function () {
- beforeEach(function () {
+describe('CommentTypeToggle', function() {
+ describe('class constructor', function() {
+ beforeEach(function() {
this.dropdownTrigger = {};
this.dropdownList = {};
this.noteTypeInput = {};
@@ -19,33 +19,33 @@ describe('CommentTypeToggle', function () {
});
});
- it('should set .dropdownTrigger', function () {
+ it('should set .dropdownTrigger', function() {
expect(this.commentTypeToggle.dropdownTrigger).toBe(this.dropdownTrigger);
});
- it('should set .dropdownList', function () {
+ it('should set .dropdownList', function() {
expect(this.commentTypeToggle.dropdownList).toBe(this.dropdownList);
});
- it('should set .noteTypeInput', function () {
+ it('should set .noteTypeInput', function() {
expect(this.commentTypeToggle.noteTypeInput).toBe(this.noteTypeInput);
});
- it('should set .submitButton', function () {
+ it('should set .submitButton', function() {
expect(this.commentTypeToggle.submitButton).toBe(this.submitButton);
});
- it('should set .closeButton', function () {
+ it('should set .closeButton', function() {
expect(this.commentTypeToggle.closeButton).toBe(this.closeButton);
});
- it('should set .reopenButton', function () {
+ it('should set .reopenButton', function() {
expect(this.commentTypeToggle.reopenButton).toBe(this.reopenButton);
});
});
- describe('initDroplab', function () {
- beforeEach(function () {
+ describe('initDroplab', function() {
+ beforeEach(function() {
this.commentTypeToggle = {
dropdownTrigger: {},
dropdownList: {},
@@ -58,25 +58,27 @@ describe('CommentTypeToggle', function () {
this.droplab = jasmine.createSpyObj('droplab', ['init']);
- this.droplabConstructor = spyOnDependency(CommentTypeToggle, 'DropLab').and.returnValue(this.droplab);
+ this.droplabConstructor = spyOnDependency(CommentTypeToggle, 'DropLab').and.returnValue(
+ this.droplab,
+ );
spyOn(this.commentTypeToggle, 'setConfig').and.returnValue(this.config);
CommentTypeToggle.prototype.initDroplab.call(this.commentTypeToggle);
});
- it('should instantiate a DropLab instance', function () {
+ it('should instantiate a DropLab instance', function() {
expect(this.droplabConstructor).toHaveBeenCalled();
});
- it('should set .droplab', function () {
+ it('should set .droplab', function() {
expect(this.commentTypeToggle.droplab).toBe(this.droplab);
});
- it('should call .setConfig', function () {
+ it('should call .setConfig', function() {
expect(this.commentTypeToggle.setConfig).toHaveBeenCalled();
});
- it('should call DropLab.prototype.init', function () {
+ it('should call DropLab.prototype.init', function() {
expect(this.droplab.init).toHaveBeenCalledWith(
this.commentTypeToggle.dropdownTrigger,
this.commentTypeToggle.dropdownList,
@@ -86,9 +88,9 @@ describe('CommentTypeToggle', function () {
});
});
- describe('setConfig', function () {
- describe('if no .closeButton is provided', function () {
- beforeEach(function () {
+ describe('setConfig', function() {
+ describe('if no .closeButton is provided', function() {
+ beforeEach(function() {
this.commentTypeToggle = {
dropdownTrigger: {},
dropdownList: {},
@@ -100,28 +102,33 @@ describe('CommentTypeToggle', function () {
this.setConfig = CommentTypeToggle.prototype.setConfig.call(this.commentTypeToggle);
});
- it('should not add .closeButton related InputSetter config', function () {
+ it('should not add .closeButton related InputSetter config', function() {
expect(this.setConfig).toEqual({
- InputSetter: [{
- input: this.commentTypeToggle.noteTypeInput,
- valueAttribute: 'data-value',
- }, {
- input: this.commentTypeToggle.submitButton,
- valueAttribute: 'data-submit-text',
- }, {
- input: this.commentTypeToggle.reopenButton,
- valueAttribute: 'data-reopen-text',
- }, {
- input: this.commentTypeToggle.reopenButton,
- valueAttribute: 'data-reopen-text',
- inputAttribute: 'data-alternative-text',
- }],
+ InputSetter: [
+ {
+ input: this.commentTypeToggle.noteTypeInput,
+ valueAttribute: 'data-value',
+ },
+ {
+ input: this.commentTypeToggle.submitButton,
+ valueAttribute: 'data-submit-text',
+ },
+ {
+ input: this.commentTypeToggle.reopenButton,
+ valueAttribute: 'data-reopen-text',
+ },
+ {
+ input: this.commentTypeToggle.reopenButton,
+ valueAttribute: 'data-reopen-text',
+ inputAttribute: 'data-alternative-text',
+ },
+ ],
});
});
});
- describe('if no .reopenButton is provided', function () {
- beforeEach(function () {
+ describe('if no .reopenButton is provided', function() {
+ beforeEach(function() {
this.commentTypeToggle = {
dropdownTrigger: {},
dropdownList: {},
@@ -133,22 +140,27 @@ describe('CommentTypeToggle', function () {
this.setConfig = CommentTypeToggle.prototype.setConfig.call(this.commentTypeToggle);
});
- it('should not add .reopenButton related InputSetter config', function () {
+ it('should not add .reopenButton related InputSetter config', function() {
expect(this.setConfig).toEqual({
- InputSetter: [{
- input: this.commentTypeToggle.noteTypeInput,
- valueAttribute: 'data-value',
- }, {
- input: this.commentTypeToggle.submitButton,
- valueAttribute: 'data-submit-text',
- }, {
- input: this.commentTypeToggle.closeButton,
- valueAttribute: 'data-close-text',
- }, {
- input: this.commentTypeToggle.closeButton,
- valueAttribute: 'data-close-text',
- inputAttribute: 'data-alternative-text',
- }],
+ InputSetter: [
+ {
+ input: this.commentTypeToggle.noteTypeInput,
+ valueAttribute: 'data-value',
+ },
+ {
+ input: this.commentTypeToggle.submitButton,
+ valueAttribute: 'data-submit-text',
+ },
+ {
+ input: this.commentTypeToggle.closeButton,
+ valueAttribute: 'data-close-text',
+ },
+ {
+ input: this.commentTypeToggle.closeButton,
+ valueAttribute: 'data-close-text',
+ inputAttribute: 'data-alternative-text',
+ },
+ ],
});
});
});
diff --git a/spec/javascripts/commit/commit_pipeline_status_component_spec.js b/spec/javascripts/commit/commit_pipeline_status_component_spec.js
index d3776d0c3cf..f6b36e88a5f 100644
--- a/spec/javascripts/commit/commit_pipeline_status_component_spec.js
+++ b/spec/javascripts/commit/commit_pipeline_status_component_spec.js
@@ -22,19 +22,22 @@ describe('Commit pipeline status component', () => {
Component = Vue.extend(commitPipelineStatus);
});
- describe('While polling pipeline data succesfully', () => {
+ describe('While polling pipeline data successfully', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet('/dummy/endpoint').reply(() => {
- const res = Promise.resolve([200, {
- pipelines: [
- {
- details: {
- status: mockCiStatus,
+ const res = Promise.resolve([
+ 200,
+ {
+ pipelines: [
+ {
+ details: {
+ status: mockCiStatus,
+ },
},
- },
- ],
- }]);
+ ],
+ },
+ ]);
return res;
});
vm = mountComponent(Component, {
@@ -48,7 +51,7 @@ describe('Commit pipeline status component', () => {
mock.restore();
});
- it('shows the loading icon when polling is starting', (done) => {
+ it('shows the loading icon when polling is starting', done => {
expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
setTimeout(() => {
expect(vm.$el.querySelector('.loading-container')).toBe(null);
@@ -56,23 +59,25 @@ describe('Commit pipeline status component', () => {
});
});
- it('contains a ciStatus when the polling is succesful ', (done) => {
+ it('contains a ciStatus when the polling is successful ', done => {
setTimeout(() => {
expect(vm.ciStatus).toEqual(mockCiStatus);
done();
});
});
- it('contains a ci-status icon when polling is succesful', (done) => {
+ it('contains a ci-status icon when polling is successful', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.ci-status-icon')).not.toBe(null);
- expect(vm.$el.querySelector('.ci-status-icon').classList).toContain(`ci-status-icon-${mockCiStatus.group}`);
+ expect(vm.$el.querySelector('.ci-status-icon').classList).toContain(
+ `ci-status-icon-${mockCiStatus.group}`,
+ );
done();
});
});
});
- describe('When polling data was not succesful', () => {
+ describe('When polling data was not successful', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet('/dummy/endpoint').reply(502, {});
@@ -89,7 +94,7 @@ describe('Commit pipeline status component', () => {
mock.restore();
});
- it('calls an errorCallback', (done) => {
+ it('calls an errorCallback', done => {
spyOn(vm, 'errorCallback').and.callThrough();
vm.$mount();
setTimeout(() => {
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index a18e09da50a..04c8ab44405 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import pipelinesTable from '~/commit/pipelines/pipelines_table.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-describe('Pipelines table in Commits and Merge requests', function () {
+describe('Pipelines table in Commits and Merge requests', function() {
const jsonFixtureName = 'pipelines/pipelines.json';
let pipeline;
let PipelinesTable;
@@ -29,7 +29,7 @@ describe('Pipelines table in Commits and Merge requests', function () {
describe('successful request', () => {
describe('without pipelines', () => {
- beforeEach(function () {
+ beforeEach(function() {
mock.onGet('endpoint.json').reply(200, []);
vm = mountComponent(PipelinesTable, {
@@ -41,7 +41,7 @@ describe('Pipelines table in Commits and Merge requests', function () {
});
});
- it('should render the empty state', function (done) {
+ it('should render the empty state', function(done) {
setTimeout(() => {
expect(vm.$el.querySelector('.empty-state')).toBeDefined();
expect(vm.$el.querySelector('.realtime-loading')).toBe(null);
@@ -63,7 +63,7 @@ describe('Pipelines table in Commits and Merge requests', function () {
});
});
- it('should render a table with the received pipelines', (done) => {
+ it('should render a table with the received pipelines', done => {
setTimeout(() => {
expect(vm.$el.querySelectorAll('.ci-table .commit').length).toEqual(1);
expect(vm.$el.querySelector('.realtime-loading')).toBe(null);
@@ -72,6 +72,29 @@ describe('Pipelines table in Commits and Merge requests', function () {
done();
}, 0);
});
+
+ describe('with pagination', () => {
+ it('should make an API request when using pagination', done => {
+ setTimeout(() => {
+ spyOn(vm, 'updateContent');
+
+ vm.store.state.pageInfo = {
+ page: 1,
+ total: 10,
+ perPage: 2,
+ nextPage: 2,
+ totalPages: 5,
+ };
+
+ vm.$nextTick(() => {
+ vm.$el.querySelector('.js-next-button a').click();
+
+ expect(vm.updateContent).toHaveBeenCalledWith({ page: '2' });
+ done();
+ });
+ });
+ });
+ });
});
describe('pipeline badge counts', () => {
@@ -79,11 +102,11 @@ describe('Pipelines table in Commits and Merge requests', function () {
mock.onGet('endpoint.json').reply(200, [pipeline]);
});
- it('should receive update-pipelines-count event', (done) => {
+ it('should receive update-pipelines-count event', done => {
const element = document.createElement('div');
document.body.appendChild(element);
- element.addEventListener('update-pipelines-count', (event) => {
+ element.addEventListener('update-pipelines-count', event => {
expect(event.detail.pipelines).toEqual([pipeline]);
done();
});
@@ -114,7 +137,7 @@ describe('Pipelines table in Commits and Merge requests', function () {
});
});
- it('should render error state', function (done) {
+ it('should render error state', function(done) {
setTimeout(() => {
expect(vm.$el.querySelector('.js-pipelines-error-state')).toBeDefined();
expect(vm.$el.querySelector('.realtime-loading')).toBe(null);
diff --git a/spec/javascripts/commit_merge_requests_spec.js b/spec/javascripts/commit_merge_requests_spec.js
index 3466ef51ea8..82968e028d1 100644
--- a/spec/javascripts/commit_merge_requests_spec.js
+++ b/spec/javascripts/commit_merge_requests_spec.js
@@ -3,11 +3,16 @@ import * as CommitMergeRequests from '~/commit_merge_requests';
describe('CommitMergeRequests', () => {
describe('createContent', () => {
it('should return created content', () => {
- const content1 = CommitMergeRequests.createContent([{ iid: 1, path: '/path1', title: 'foo' }, { iid: 2, path: '/path2', title: 'baz' }])[0];
+ const content1 = CommitMergeRequests.createContent([
+ { iid: 1, path: '/path1', title: 'foo' },
+ { iid: 2, path: '/path2', title: 'baz' },
+ ])[0];
+
expect(content1.tagName).toEqual('SPAN');
expect(content1.childElementCount).toEqual(4);
const content2 = CommitMergeRequests.createContent([])[0];
+
expect(content2.tagName).toEqual('SPAN');
expect(content2.childElementCount).toEqual(0);
expect(content2.innerText).toEqual('No related merge requests found');
@@ -26,6 +31,7 @@ describe('CommitMergeRequests', () => {
describe('createHeader', () => {
it('should return created header', () => {
const header = CommitMergeRequests.createHeader(0, 1)[0];
+
expect(header.tagName).toEqual('SPAN');
expect(header.innerText).toEqual('1 merge request');
});
@@ -34,6 +40,7 @@ describe('CommitMergeRequests', () => {
describe('createItem', () => {
it('should return created item', () => {
const item = CommitMergeRequests.createItem({ iid: 1, path: '/path', title: 'foo' })[0];
+
expect(item.tagName).toEqual('SPAN');
expect(item.childElementCount).toEqual(2);
expect(item.children[0].tagName).toEqual('A');
@@ -44,6 +51,7 @@ describe('CommitMergeRequests', () => {
describe('createLink', () => {
it('should return created link', () => {
const link = CommitMergeRequests.createLink({ iid: 1, path: '/path', title: 'foo' })[0];
+
expect(link.tagName).toEqual('A');
expect(link.href).toMatch(/\/path$/);
expect(link.innerText).toEqual('!1');
@@ -53,6 +61,7 @@ describe('CommitMergeRequests', () => {
describe('createTitle', () => {
it('should return created title', () => {
const title = CommitMergeRequests.createTitle({ iid: 1, path: '/path', title: 'foo' })[0];
+
expect(title.tagName).toEqual('SPAN');
expect(title.innerText).toEqual('foo');
});
diff --git a/spec/javascripts/create_item_dropdown_spec.js b/spec/javascripts/create_item_dropdown_spec.js
index ee26122be12..9cf72d7c55b 100644
--- a/spec/javascripts/create_item_dropdown_spec.js
+++ b/spec/javascripts/create_item_dropdown_spec.js
@@ -1,19 +1,23 @@
import $ from 'jquery';
import CreateItemDropdown from '~/create_item_dropdown';
-const DROPDOWN_ITEM_DATA = [{
- title: 'one',
- id: 'one',
- text: 'one',
-}, {
- title: 'two',
- id: 'two',
- text: 'two',
-}, {
- title: 'three',
- id: 'three',
- text: 'three',
-}];
+const DROPDOWN_ITEM_DATA = [
+ {
+ title: 'one',
+ id: 'one',
+ text: 'one',
+ },
+ {
+ title: 'two',
+ id: 'two',
+ text: 'two',
+ },
+ {
+ title: 'three',
+ id: 'three',
+ text: 'three',
+ },
+];
describe('CreateItemDropdown', () => {
preloadFixtures('static/create_item_dropdown.html.raw');
@@ -23,7 +27,8 @@ describe('CreateItemDropdown', () => {
function createItemAndClearInput(text) {
// Filter for the new item
- $wrapperEl.find('.dropdown-input-field')
+ $wrapperEl
+ .find('.dropdown-input-field')
.val(text)
.trigger('input');
@@ -32,7 +37,8 @@ describe('CreateItemDropdown', () => {
$createButton.click();
// Clear out the filter
- $wrapperEl.find('.dropdown-input-field')
+ $wrapperEl
+ .find('.dropdown-input-field')
.val('')
.trigger('input');
}
@@ -63,6 +69,7 @@ describe('CreateItemDropdown', () => {
$('.js-dropdown-menu-toggle').click();
const $itemEls = $wrapperEl.find('.js-dropdown-content a');
+
expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length);
});
});
@@ -84,7 +91,8 @@ describe('CreateItemDropdown', () => {
$('.js-dropdown-menu-toggle').click();
// Filter for the new item
- $wrapperEl.find('.dropdown-input-field')
+ $wrapperEl
+ .find('.dropdown-input-field')
.val(NEW_ITEM_TEXT)
.trigger('input');
});
@@ -106,6 +114,7 @@ describe('CreateItemDropdown', () => {
createItemAndClearInput(NEW_ITEM_TEXT);
const $itemEls = $wrapperEl.find('.js-dropdown-content a');
+
expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length);
expect($($itemEls.get(DROPDOWN_ITEM_DATA.length)).text()).toEqual(NEW_ITEM_TEXT);
});
@@ -114,6 +123,7 @@ describe('CreateItemDropdown', () => {
createItemAndClearInput(DROPDOWN_ITEM_DATA[0].text);
const $itemEls = $wrapperEl.find('.js-dropdown-content a');
+
expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length);
});
});
@@ -137,16 +147,16 @@ describe('CreateItemDropdown', () => {
$('.js-dropdown-menu-toggle').click();
// Filter for an item
- filterInput
- .val('one')
- .trigger('input');
+ filterInput.val('one').trigger('input');
const $itemElsAfterFilter = $wrapperEl.find('.js-dropdown-content a');
+
expect($itemElsAfterFilter.length).toEqual(1);
createItemDropdown.clearDropdown();
const $itemElsAfterClear = $wrapperEl.find('.js-dropdown-content a');
+
expect($itemElsAfterClear.length).toEqual(0);
expect(filterInput.val()).toEqual('');
});
@@ -176,6 +186,7 @@ describe('CreateItemDropdown', () => {
createItemAndClearInput('new-item');
const $itemEls = $wrapperEl.find('.js-dropdown-content a');
+
expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length);
expect($($itemEls[3]).text()).toEqual('new-item-text');
expect($wrapperEl.find('.dropdown-toggle-text').text()).toEqual('new-item-title');
diff --git a/spec/javascripts/create_merge_request_dropdown_spec.js b/spec/javascripts/create_merge_request_dropdown_spec.js
index b229765a8c5..00fe3f451f5 100644
--- a/spec/javascripts/create_merge_request_dropdown_spec.js
+++ b/spec/javascripts/create_merge_request_dropdown_spec.js
@@ -59,6 +59,7 @@ describe('CreateMergeRequestDropdown', () => {
expect(dropdown.createBranchPath).toBe(
`${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`,
);
+
expect(dropdown.createMrPath).toBe(
`${TEST_HOST}/create_merge_request?branch_name=contains%23hash&ref=master`,
);
diff --git a/spec/javascripts/cycle_analytics/banner_spec.js b/spec/javascripts/cycle_analytics/banner_spec.js
index 2815bdba0c2..3ce2c3c4f06 100644
--- a/spec/javascripts/cycle_analytics/banner_spec.js
+++ b/spec/javascripts/cycle_analytics/banner_spec.js
@@ -17,19 +17,20 @@ describe('Cycle analytics banner', () => {
});
it('should render cycle analytics information', () => {
- expect(
- vm.$el.querySelector('h4').textContent.trim(),
- ).toEqual('Introducing Cycle Analytics');
+ expect(vm.$el.querySelector('h4').textContent.trim()).toEqual('Introducing Cycle Analytics');
expect(
- vm.$el.querySelector('p').textContent.trim().replace(/[\r\n]+/g, ' '),
- ).toContain('Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.');
- expect(
- vm.$el.querySelector('a').textContent.trim(),
- ).toEqual('Read more');
- expect(
- vm.$el.querySelector('a').getAttribute('href'),
- ).toEqual('path');
+ vm.$el
+ .querySelector('p')
+ .textContent.trim()
+ .replace(/[\r\n]+/g, ' '),
+ ).toContain(
+ 'Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.',
+ );
+
+ expect(vm.$el.querySelector('a').textContent.trim()).toEqual('Read more');
+
+ expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('path');
});
it('should emit an event when close button is clicked', () => {
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
deleted file mode 100644
index 492171684dc..00000000000
--- a/spec/javascripts/datetime_utility_spec.js
+++ /dev/null
@@ -1,170 +0,0 @@
-import * as datetimeUtility from '~/lib/utils/datetime_utility';
-
-describe('Date time utils', () => {
- describe('timeFor', () => {
- it('returns `past due` when in past', () => {
- const date = new Date();
- date.setFullYear(date.getFullYear() - 1);
-
- expect(
- datetimeUtility.timeFor(date),
- ).toBe('Past due');
- });
-
- it('returns remaining time when in the future', () => {
- const date = new Date();
- date.setFullYear(date.getFullYear() + 1);
-
- // Add a day to prevent a transient error. If date is even 1 second
- // short of a full year, timeFor will return '11 months remaining'
- date.setDate(date.getDate() + 1);
-
- expect(
- datetimeUtility.timeFor(date),
- ).toBe('1 year remaining');
- });
- });
-
- describe('get day name', () => {
- it('should return Sunday', () => {
- const day = datetimeUtility.getDayName(new Date('07/17/2016'));
- expect(day).toBe('Sunday');
- });
-
- it('should return Monday', () => {
- const day = datetimeUtility.getDayName(new Date('07/18/2016'));
- expect(day).toBe('Monday');
- });
-
- it('should return Tuesday', () => {
- const day = datetimeUtility.getDayName(new Date('07/19/2016'));
- expect(day).toBe('Tuesday');
- });
-
- it('should return Wednesday', () => {
- const day = datetimeUtility.getDayName(new Date('07/20/2016'));
- expect(day).toBe('Wednesday');
- });
-
- it('should return Thursday', () => {
- const day = datetimeUtility.getDayName(new Date('07/21/2016'));
- expect(day).toBe('Thursday');
- });
-
- it('should return Friday', () => {
- const day = datetimeUtility.getDayName(new Date('07/22/2016'));
- expect(day).toBe('Friday');
- });
-
- it('should return Saturday', () => {
- const day = datetimeUtility.getDayName(new Date('07/23/2016'));
- expect(day).toBe('Saturday');
- });
- });
-
- describe('get day difference', () => {
- it('should return 7', () => {
- const firstDay = new Date('07/01/2016');
- const secondDay = new Date('07/08/2016');
- const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(7);
- });
-
- it('should return 31', () => {
- const firstDay = new Date('07/01/2016');
- const secondDay = new Date('08/01/2016');
- const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(31);
- });
-
- it('should return 365', () => {
- const firstDay = new Date('07/02/2015');
- const secondDay = new Date('07/01/2016');
- const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(365);
- });
- });
-});
-
-describe('timeIntervalInWords', () => {
- it('should return string with number of minutes and seconds', () => {
- expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds');
- expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second');
- expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds');
- expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds');
- });
-});
-
-describe('dateInWords', () => {
- const date = new Date('07/01/2016');
-
- it('should return date in words', () => {
- expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016');
- });
-
- it('should return abbreviated month name', () => {
- expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
- });
-
- it('should return date in words without year', () => {
- expect(datetimeUtility.dateInWords(date, true, true)).toEqual('Jul 1');
- });
-});
-
-describe('monthInWords', () => {
- const date = new Date('2017-01-20');
-
- it('returns month name from provided date', () => {
- expect(datetimeUtility.monthInWords(date)).toBe('January');
- });
-
- it('returns abbreviated month name from provided date', () => {
- expect(datetimeUtility.monthInWords(date, true)).toBe('Jan');
- });
-});
-
-describe('totalDaysInMonth', () => {
- it('returns number of days in a month for given date', () => {
- // 1st Feb, 2016 (leap year)
- expect(datetimeUtility.totalDaysInMonth(new Date(2016, 1, 1))).toBe(29);
-
- // 1st Feb, 2017
- expect(datetimeUtility.totalDaysInMonth(new Date(2017, 1, 1))).toBe(28);
-
- // 1st Jan, 2017
- expect(datetimeUtility.totalDaysInMonth(new Date(2017, 0, 1))).toBe(31);
- });
-});
-
-describe('getSundays', () => {
- it('returns array of dates representing all Sundays of the month', () => {
- // December, 2017 (it has 5 Sundays)
- const dateOfSundays = [3, 10, 17, 24, 31];
- const sundays = datetimeUtility.getSundays(new Date(2017, 11, 1));
-
- expect(sundays.length).toBe(5);
- sundays.forEach((sunday, index) => {
- expect(sunday.getDate()).toBe(dateOfSundays[index]);
- });
- });
-});
-
-describe('getTimeframeWindowFrom', () => {
- it('returns array of date objects upto provided length start with provided startDate', () => {
- const startDate = new Date(2018, 0, 1);
- const mockTimeframe = [
- new Date(2018, 0, 1),
- new Date(2018, 1, 1),
- new Date(2018, 2, 1),
- new Date(2018, 3, 1),
- new Date(2018, 4, 31),
- ];
- const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, 5);
- expect(timeframe.length).toBe(5);
- timeframe.forEach((timeframeItem, index) => {
- expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBe(true);
- expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBe(true);
- expect(timeframeItem.getDate() === mockTimeframe[index].getDate()).toBeTruthy();
- });
- });
-});
diff --git a/spec/javascripts/deploy_keys/components/app_spec.js b/spec/javascripts/deploy_keys/components/app_spec.js
index 183d7cf2d41..f81c0cb7124 100644
--- a/spec/javascripts/deploy_keys/components/app_spec.js
+++ b/spec/javascripts/deploy_keys/components/app_spec.js
@@ -10,8 +10,8 @@ describe('Deploy keys app component', () => {
let vm;
let mock;
- beforeEach((done) => {
- // setup axios mock before component
+ beforeEach(done => {
+ // set up axios mock before component
mock = new MockAdapter(axios);
mock.onGet(`${TEST_HOST}/dummy/`).replyOnce(200, data);
@@ -60,6 +60,7 @@ describe('Deploy keys app component', () => {
expect(textContent('.js-deployKeys-tab-available_project_keys')).toContain(
'Privately accessible deploy keys',
);
+
expect(textContent('.js-deployKeys-tab-public_keys')).toContain(
'Publicly accessible deploy keys',
);
@@ -67,9 +68,11 @@ describe('Deploy keys app component', () => {
expect(textContent('.js-deployKeys-tab-enabled_keys .badge')).toBe(
`${vm.store.keys.enabled_keys.length}`,
);
+
expect(textContent('.js-deployKeys-tab-available_project_keys .badge')).toBe(
`${vm.store.keys.available_project_keys.length}`,
);
+
expect(textContent('.js-deployKeys-tab-public_keys .badge')).toBe(
`${vm.store.keys.public_keys.length}`,
);
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index d1de9d132b8..7117dc4a9ee 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -82,6 +82,7 @@ describe('Deploy keys key', () => {
it('shows expandable button if more than two projects', () => {
const labels = vm.$el.querySelectorAll('.deploy-project-label');
+
expect(labels.length).toBe(2);
expect(labels[1].textContent).toContain('others');
expect(labels[1].getAttribute('data-original-title')).toContain('Expand');
@@ -93,6 +94,7 @@ describe('Deploy keys key', () => {
Vue.nextTick(() => {
const labels = vm.$el.querySelectorAll('.deploy-project-label');
+
expect(labels.length).toBe(length);
expect(labels[1].textContent).not.toContain(`+${length} others`);
expect(labels[1].getAttribute('data-original-title')).not.toContain('Expand');
@@ -105,6 +107,7 @@ describe('Deploy keys key', () => {
Vue.nextTick(() => {
const labels = vm.$el.querySelectorAll('.deploy-project-label');
+
expect(labels.length).toBe(2);
expect(labels[1].textContent).toContain(
vm.deployKey.deploy_keys_projects[1].project.full_name,
diff --git a/spec/javascripts/diff_comments_store_spec.js b/spec/javascripts/diff_comments_store_spec.js
index d6fc6b56b82..a6d363ce88e 100644
--- a/spec/javascripts/diff_comments_store_spec.js
+++ b/spec/javascripts/diff_comments_store_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable jasmine/no-global-setup, dot-notation, jasmine/no-expect-in-setup-teardown, max-len */
+/* eslint-disable jasmine/no-global-setup, dot-notation, jasmine/no-expect-in-setup-teardown */
/* global CommentsStore */
import '~/diff_notes/models/discussion';
@@ -26,6 +26,7 @@ describe('New discussion', () => {
it('creates new discussion', () => {
expect(Object.keys(CommentsStore.state).length).toBe(0);
createDiscussion();
+
expect(Object.keys(CommentsStore.state).length).toBe(1);
});
@@ -34,6 +35,7 @@ describe('New discussion', () => {
createDiscussion(2);
const discussion = CommentsStore.state['a'];
+
expect(Object.keys(discussion.notes).length).toBe(2);
});
});
@@ -46,6 +48,7 @@ describe('Get note', () => {
it('gets note by ID', () => {
const note = CommentsStore.get('a', 1);
+
expect(note).toBeDefined();
expect(note.id).toBe(1);
});
@@ -59,17 +62,20 @@ describe('Delete discussion', () => {
it('deletes discussion by ID', () => {
CommentsStore.delete('a', 1);
+
expect(Object.keys(CommentsStore.state).length).toBe(0);
});
it('deletes discussion when no more notes', () => {
createDiscussion();
createDiscussion(2);
+
expect(Object.keys(CommentsStore.state).length).toBe(1);
expect(Object.keys(CommentsStore.state['a'].notes).length).toBe(2);
CommentsStore.delete('a', 1);
CommentsStore.delete('a', 2);
+
expect(Object.keys(CommentsStore.state).length).toBe(0);
});
});
@@ -84,6 +90,7 @@ describe('Update note', () => {
CommentsStore.update('a', 1, false, 'test');
const note = CommentsStore.get('a', 1);
+
expect(note.resolved).toBe(false);
});
});
@@ -96,6 +103,7 @@ describe('Discussion resolved', () => {
it('is resolved with single note', () => {
const discussion = CommentsStore.state['a'];
+
expect(discussion.isResolved()).toBe(true);
});
@@ -118,6 +126,7 @@ describe('Discussion resolved', () => {
createDiscussion(2, false);
discussion.resolveAllNotes();
+
expect(discussion.isResolved()).toBe(true);
});
@@ -126,6 +135,7 @@ describe('Discussion resolved', () => {
createDiscussion(2);
discussion.unResolveAllNotes();
+
expect(discussion.isResolved()).toBe(false);
});
});
diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js
index 7237274eb43..3c9b5ee0176 100644
--- a/spec/javascripts/diffs/components/app_spec.js
+++ b/spec/javascripts/diffs/components/app_spec.js
@@ -1 +1,82 @@
-// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
+import Vue from 'vue';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { TEST_HOST } from 'spec/test_constants';
+import App from '~/diffs/components/app.vue';
+import createDiffsStore from '../create_diffs_store';
+import getDiffWithCommit from '../mock_data/diff_with_commit';
+
+describe('diffs/components/app', () => {
+ const oldMrTabs = window.mrTabs;
+ const Component = Vue.extend(App);
+
+ let vm;
+
+ beforeEach(() => {
+ // setup globals (needed for component to mount :/)
+ window.mrTabs = jasmine.createSpyObj('mrTabs', ['resetViewContainer']);
+
+ // setup component
+ const store = createDiffsStore();
+ store.state.diffs.isLoading = false;
+
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: {
+ endpoint: `${TEST_HOST}/diff/endpoint`,
+ projectPath: 'namespace/project',
+ currentUser: {},
+ },
+ });
+ });
+
+ afterEach(() => {
+ // reset globals
+ window.mrTabs = oldMrTabs;
+
+ // reset component
+ vm.$destroy();
+ });
+
+ it('does not show commit info', () => {
+ expect(vm.$el).not.toContainElement('.blob-commit-info');
+ });
+
+ it('shows comments message, with commit', done => {
+ vm.$store.state.diffs.commit = getDiffWithCommit().commit;
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toContainText('Only comments from the following commit are shown below');
+ expect(vm.$el).toContainElement('.blob-commit-info');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('shows comments message, with old mergeRequestDiff', done => {
+ vm.$store.state.diffs.mergeRequestDiff = { latest: false };
+ vm.$store.state.diffs.targetBranch = 'master';
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toContainText(
+ "Not all comments are displayed because you're viewing an old version of the diff.",
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('shows comments message, with startVersion', done => {
+ vm.$store.state.diffs.startVersion = 'test';
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toContainText(
+ "Not all comments are displayed because you're comparing two versions of the diff.",
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/diffs/components/changed_files_spec.js b/spec/javascripts/diffs/components/changed_files_spec.js
deleted file mode 100644
index f737e8fa38e..00000000000
--- a/spec/javascripts/diffs/components/changed_files_spec.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
-import { mountComponentWithStore } from 'spec/helpers';
-import diffsModule from '~/diffs/store/modules';
-import changedFiles from '~/diffs/components/changed_files.vue';
-
-describe('ChangedFiles', () => {
- const Component = Vue.extend(changedFiles);
- const store = new Vuex.Store({
- modules: {
- diffs: diffsModule,
- },
- });
-
- let vm;
-
- beforeEach(() => {
- setFixtures(`
- <div id="dummy-element"></div>
- <div class="js-tabs-affix"></div>
- `);
-
- const props = {
- diffFiles: [
- {
- addedLines: 10,
- removedLines: 20,
- blob: {
- path: 'some/code.txt',
- },
- filePath: 'some/code.txt',
- },
- ],
- };
-
- vm = mountComponentWithStore(Component, { props, store });
- });
-
- describe('with single file added', () => {
- it('shows files changes', () => {
- expect(vm.$el).toContainText('1 changed file');
- });
-
- it('shows file additions and deletions', () => {
- expect(vm.$el).toContainText('10 additions');
- expect(vm.$el).toContainText('20 deletions');
- });
- });
-
- describe('diff view mode buttons', () => {
- let inlineButton;
- let parallelButton;
-
- beforeEach(() => {
- inlineButton = vm.$el.querySelector('.js-inline-diff-button');
- parallelButton = vm.$el.querySelector('.js-parallel-diff-button');
- });
-
- it('should have Inline and Side-by-side buttons', () => {
- expect(inlineButton).toBeDefined();
- expect(parallelButton).toBeDefined();
- });
-
- it('should add active class to Inline button', done => {
- vm.$store.state.diffs.diffViewType = 'inline';
-
- vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(true);
- expect(parallelButton.classList.contains('active')).toEqual(false);
-
- done();
- });
- });
-
- it('should toggle active state of buttons when diff view type changed', done => {
- vm.$store.state.diffs.diffViewType = 'parallel';
-
- vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(false);
- expect(parallelButton.classList.contains('active')).toEqual(true);
-
- done();
- });
- });
-
- describe('clicking them', () => {
- it('should toggle the diff view type', done => {
- parallelButton.click();
-
- vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(false);
- expect(parallelButton.classList.contains('active')).toEqual(true);
-
- inlineButton.click();
-
- vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(true);
- expect(parallelButton.classList.contains('active')).toEqual(false);
- done();
- });
- });
- });
- });
- });
-});
diff --git a/spec/javascripts/diffs/components/commit_item_spec.js b/spec/javascripts/diffs/components/commit_item_spec.js
new file mode 100644
index 00000000000..8b2ca6506c4
--- /dev/null
+++ b/spec/javascripts/diffs/components/commit_item_spec.js
@@ -0,0 +1,164 @@
+import Vue from 'vue';
+import { TEST_HOST } from 'spec/test_constants';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { trimText } from 'spec/helpers/vue_component_helper';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+import CommitItem from '~/diffs/components/commit_item.vue';
+import getDiffWithCommit from '../mock_data/diff_with_commit';
+
+const TEST_AUTHOR_NAME = 'test';
+const TEST_AUTHOR_EMAIL = 'test+test@gitlab.com';
+const TEST_AUTHOR_GRAVATAR = `${TEST_HOST}/avatar/test?s=36`;
+const TEST_SIGNATURE_HTML = '<a>Legit commit</a>';
+const TEST_PIPELINE_STATUS_PATH = `${TEST_HOST}/pipeline/status`;
+
+const getTitleElement = vm => vm.$el.querySelector('.commit-row-message.item-title');
+const getDescElement = vm => vm.$el.querySelector('pre.commit-row-description');
+const getDescExpandElement = vm =>
+ vm.$el.querySelector('.commit-content .text-expander.js-toggle-button');
+const getShaElement = vm => vm.$el.querySelector('.commit-sha-group');
+const getAvatarElement = vm => vm.$el.querySelector('.user-avatar-link');
+const getCommitterElement = vm => vm.$el.querySelector('.commiter');
+const getCommitActionsElement = vm => vm.$el.querySelector('.commit-actions');
+
+describe('diffs/components/commit_item', () => {
+ const Component = Vue.extend(CommitItem);
+ const timeago = getTimeago();
+ const { commit } = getDiffWithCommit();
+
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ commit: getDiffWithCommit().commit,
+ });
+ });
+
+ it('renders commit title', () => {
+ const titleElement = getTitleElement(vm);
+
+ expect(titleElement).toHaveAttr('href', commit.commit_url);
+ expect(titleElement).toHaveText(commit.title_html);
+ });
+
+ it('renders commit description', () => {
+ const descElement = getDescElement(vm);
+ const descExpandElement = getDescExpandElement(vm);
+
+ const expected = commit.description_html.replace(/&#x000A;/g, '');
+
+ expect(trimText(descElement.innerHTML)).toEqual(trimText(expected));
+ expect(descExpandElement).not.toBeNull();
+ });
+
+ it('renders commit sha', () => {
+ const shaElement = getShaElement(vm);
+ const labelElement = shaElement.querySelector('.label');
+ const buttonElement = shaElement.querySelector('button');
+
+ expect(labelElement.textContent).toEqual(commit.short_id);
+ expect(buttonElement).toHaveData('clipboard-text', commit.id);
+ });
+
+ it('renders author avatar', () => {
+ const avatarElement = getAvatarElement(vm);
+ const imgElement = avatarElement.querySelector('img');
+
+ expect(avatarElement).toHaveAttr('href', commit.author.web_url);
+ expect(imgElement).toHaveClass('s36');
+ expect(imgElement).toHaveAttr('alt', commit.author.name);
+ expect(imgElement).toHaveAttr('src', commit.author.avatar_url);
+ });
+
+ it('renders committer text', () => {
+ const committerElement = getCommitterElement(vm);
+ const nameElement = committerElement.querySelector('a');
+
+ const expectTimeText = timeago.format(commit.authored_date);
+ const expectedText = `${commit.author.name} authored ${expectTimeText}`;
+
+ expect(trimText(committerElement.textContent)).toEqual(expectedText);
+ expect(nameElement).toHaveAttr('href', commit.author.web_url);
+ expect(nameElement).toHaveText(commit.author.name);
+ });
+
+ describe('without commit description', () => {
+ beforeEach(done => {
+ vm.commit.description_html = '';
+
+ vm.$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('hides description', () => {
+ const descElement = getDescElement(vm);
+ const descExpandElement = getDescExpandElement(vm);
+
+ expect(descElement).toBeNull();
+ expect(descExpandElement).toBeNull();
+ });
+ });
+
+ describe('with no matching user', () => {
+ beforeEach(done => {
+ vm.commit.author = null;
+ vm.commit.author_email = TEST_AUTHOR_EMAIL;
+ vm.commit.author_name = TEST_AUTHOR_NAME;
+ vm.commit.author_gravatar_url = TEST_AUTHOR_GRAVATAR;
+
+ vm.$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders author avatar', () => {
+ const avatarElement = getAvatarElement(vm);
+ const imgElement = avatarElement.querySelector('img');
+
+ expect(avatarElement).toHaveAttr('href', `mailto:${TEST_AUTHOR_EMAIL}`);
+ expect(imgElement).toHaveAttr('alt', TEST_AUTHOR_NAME);
+ expect(imgElement).toHaveAttr('src', TEST_AUTHOR_GRAVATAR);
+ });
+
+ it('renders committer text', () => {
+ const committerElement = getCommitterElement(vm);
+ const nameElement = committerElement.querySelector('a');
+
+ expect(nameElement).toHaveAttr('href', `mailto:${TEST_AUTHOR_EMAIL}`);
+ expect(nameElement).toHaveText(TEST_AUTHOR_NAME);
+ });
+ });
+
+ describe('with signature', () => {
+ beforeEach(done => {
+ vm.commit.signature_html = TEST_SIGNATURE_HTML;
+
+ vm.$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders signature html', () => {
+ const actionsElement = getCommitActionsElement(vm);
+
+ expect(actionsElement).toContainHtml(TEST_SIGNATURE_HTML);
+ });
+ });
+
+ describe('with pipeline status', () => {
+ beforeEach(done => {
+ vm.commit.pipeline_status_path = TEST_PIPELINE_STATUS_PATH;
+
+ vm.$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders pipeline status', () => {
+ const actionsElement = getCommitActionsElement(vm);
+
+ expect(actionsElement).toContainElement('.ci-status-link');
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/commit_widget_spec.js b/spec/javascripts/diffs/components/commit_widget_spec.js
new file mode 100644
index 00000000000..2b60bd232ed
--- /dev/null
+++ b/spec/javascripts/diffs/components/commit_widget_spec.js
@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import CommitWidget from '~/diffs/components/commit_widget.vue';
+import getDiffWithCommit from '../mock_data/diff_with_commit';
+
+describe('diffs/components/commit_widget', () => {
+ const Component = Vue.extend(CommitWidget);
+ const { commit } = getDiffWithCommit();
+
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ commit: getDiffWithCommit().commit,
+ });
+ });
+
+ it('renders commit item', () => {
+ const commitElement = vm.$el.querySelector('li.commit');
+
+ expect(commitElement).not.toBeNull();
+ expect(commitElement).toContainText(commit.short_id);
+ });
+});
diff --git a/spec/javascripts/diffs/components/compare_versions_spec.js b/spec/javascripts/diffs/components/compare_versions_spec.js
index 7237274eb43..d9d7f61785f 100644
--- a/spec/javascripts/diffs/components/compare_versions_spec.js
+++ b/spec/javascripts/diffs/components/compare_versions_spec.js
@@ -1 +1,125 @@
-// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
+import Vue from 'vue';
+import CompareVersionsComponent from '~/diffs/components/compare_versions.vue';
+import store from '~/mr_notes/stores';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffsMockData from '../mock_data/merge_request_diffs';
+
+describe('CompareVersions', () => {
+ let vm;
+ const targetBranch = { branchName: 'tmp-wine-dev', versionIndex: -1 };
+
+ beforeEach(() => {
+ vm = createComponentWithStore(Vue.extend(CompareVersionsComponent), store, {
+ mergeRequestDiffs: diffsMockData,
+ mergeRequestDiff: diffsMockData[0],
+ targetBranch,
+ }).$mount();
+ });
+
+ describe('template', () => {
+ it('should render Tree List toggle button with correct attribute values', () => {
+ const treeListBtn = vm.$el.querySelector('.js-toggle-tree-list');
+
+ expect(treeListBtn).not.toBeNull();
+ expect(treeListBtn.dataset.originalTitle).toBe('Toggle file browser');
+ expect(treeListBtn.querySelectorAll('svg use').length).not.toBe(0);
+ expect(treeListBtn.querySelector('svg use').getAttribute('xlink:href')).toContain(
+ '#hamburger',
+ );
+ });
+
+ it('should render comparison dropdowns with correct values', () => {
+ const sourceDropdown = vm.$el.querySelector('.mr-version-dropdown');
+ const targetDropdown = vm.$el.querySelector('.mr-version-compare-dropdown');
+
+ expect(sourceDropdown).not.toBeNull();
+ expect(targetDropdown).not.toBeNull();
+ expect(sourceDropdown.querySelector('a span').innerHTML).toContain('latest version');
+ expect(targetDropdown.querySelector('a span').innerHTML).toContain(targetBranch.branchName);
+ });
+
+ it('should not render comparison dropdowns if no mergeRequestDiffs are specified', () => {
+ vm.mergeRequestDiffs = [];
+
+ vm.$nextTick(() => {
+ const sourceDropdown = vm.$el.querySelector('.mr-version-dropdown');
+ const targetDropdown = vm.$el.querySelector('.mr-version-compare-dropdown');
+
+ expect(sourceDropdown).toBeNull();
+ expect(targetDropdown).toBeNull();
+ });
+ });
+
+ it('should render whitespace toggle button with correct attributes', () => {
+ const whitespaceBtn = vm.$el.querySelector('.qa-toggle-whitespace');
+ const href = vm.toggleWhitespacePath;
+
+ expect(whitespaceBtn).not.toBeNull();
+ expect(whitespaceBtn.getAttribute('href')).toEqual(href);
+ expect(whitespaceBtn.innerHTML).toContain('Hide whitespace changes');
+ });
+
+ it('should render view types buttons with correct values', () => {
+ const inlineBtn = vm.$el.querySelector('#inline-diff-btn');
+ const parallelBtn = vm.$el.querySelector('#parallel-diff-btn');
+
+ expect(inlineBtn).not.toBeNull();
+ expect(parallelBtn).not.toBeNull();
+ expect(inlineBtn.dataset.viewType).toEqual('inline');
+ expect(parallelBtn.dataset.viewType).toEqual('parallel');
+ expect(inlineBtn.innerHTML).toContain('Inline');
+ expect(parallelBtn.innerHTML).toContain('Side-by-side');
+ });
+ });
+
+ describe('setInlineDiffViewType', () => {
+ it('should persist the view type in the url', () => {
+ const viewTypeBtn = vm.$el.querySelector('#inline-diff-btn');
+ viewTypeBtn.click();
+
+ expect(window.location.toString()).toContain('?view=inline');
+ });
+ });
+
+ describe('setParallelDiffViewType', () => {
+ it('should persist the view type in the url', () => {
+ const viewTypeBtn = vm.$el.querySelector('#parallel-diff-btn');
+ viewTypeBtn.click();
+
+ expect(window.location.toString()).toContain('?view=parallel');
+ });
+ });
+
+ describe('comparableDiffs', () => {
+ it('should not contain the first item in the mergeRequestDiffs property', () => {
+ const { comparableDiffs } = vm;
+ const comparableDiffsMock = diffsMockData.slice(1);
+
+ expect(comparableDiffs).toEqual(comparableDiffsMock);
+ });
+ });
+
+ describe('isWhitespaceVisible', () => {
+ const originalHref = window.location.href;
+
+ afterEach(() => {
+ window.history.replaceState({}, null, originalHref);
+ });
+
+ it('should return "true" when no "w" flag is present in the URL (default)', () => {
+ expect(vm.isWhitespaceVisible()).toBe(true);
+ });
+
+ it('should return "false" when the flag is set to "1" in the URL', () => {
+ window.history.replaceState({}, null, '?w=1');
+
+ expect(vm.isWhitespaceVisible()).toBe(false);
+ });
+
+ it('should return "true" when the flag is set to "0" in the URL', () => {
+ window.history.replaceState({}, null, '?w=0');
+
+ expect(vm.isWhitespaceVisible()).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js
index dea600a783a..c25f6167163 100644
--- a/spec/javascripts/diffs/components/diff_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_content_spec.js
@@ -1,20 +1,28 @@
import Vue from 'vue';
import DiffContentComponent from '~/diffs/components/diff_content.vue';
-import store from '~/mr_notes/stores';
+import { createStore } from '~/mr_notes/stores';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
+import '~/behaviors/markdown/render_gfm';
import diffFileMockData from '../mock_data/diff_file';
+import discussionsMockData from '../mock_data/diff_discussions';
describe('DiffContent', () => {
const Component = Vue.extend(DiffContentComponent);
let vm;
- const getDiffFileMock = () => Object.assign({}, diffFileMockData);
beforeEach(() => {
+ const store = createStore();
+ store.state.notes.noteableData = {
+ current_user: {
+ can_create_note: false,
+ },
+ };
+
vm = mountComponentWithStore(Component, {
store,
props: {
- diffFile: getDiffFileMock(),
+ diffFile: JSON.parse(JSON.stringify(diffFileMockData)),
},
});
});
@@ -43,25 +51,61 @@ describe('DiffContent', () => {
describe('Non-Text diffs', () => {
beforeEach(() => {
- vm.diffFile.text = false;
+ vm.diffFile.viewer.name = 'image';
});
describe('image diff', () => {
- beforeEach(() => {
- vm.diffFile.newPath = GREEN_BOX_IMAGE_URL;
- vm.diffFile.newSha = 'DEF';
- vm.diffFile.oldPath = RED_BOX_IMAGE_URL;
- vm.diffFile.oldSha = 'ABC';
- vm.diffFile.viewPath = '';
+ beforeEach(done => {
+ vm.diffFile.new_path = GREEN_BOX_IMAGE_URL;
+ vm.diffFile.new_sha = 'DEF';
+ vm.diffFile.old_path = RED_BOX_IMAGE_URL;
+ vm.diffFile.old_sha = 'ABC';
+ vm.diffFile.view_path = '';
+ vm.diffFile.discussions = [{ ...discussionsMockData }];
+ vm.$store.state.diffs.commentForms.push({
+ fileHash: vm.diffFile.file_hash,
+ x: 10,
+ y: 20,
+ width: 100,
+ height: 200,
+ });
+
+ vm.$nextTick(done);
});
- it('should have image diff view in place', done => {
- vm.$nextTick(() => {
- expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(0);
+ it('should have image diff view in place', () => {
+ expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(0);
- expect(vm.$el.querySelectorAll('.diff-viewer .image').length).toEqual(1);
+ expect(vm.$el.querySelectorAll('.diff-viewer .image').length).toEqual(1);
+ });
- done();
+ it('renders image diff overlay', () => {
+ expect(vm.$el.querySelector('.image-diff-overlay')).not.toBe(null);
+ });
+
+ it('renders diff file discussions', () => {
+ expect(vm.$el.querySelectorAll('.discussion .note.timeline-entry').length).toEqual(5);
+ });
+
+ describe('handleSaveNote', () => {
+ it('dispatches handleSaveNote', () => {
+ spyOn(vm.$store, 'dispatch').and.stub();
+
+ vm.handleSaveNote('test');
+
+ expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/saveDiffDiscussion', {
+ note: 'test',
+ formData: {
+ noteableData: jasmine.anything(),
+ noteableType: jasmine.anything(),
+ diffFile: vm.diffFile,
+ positionType: 'image',
+ x: 10,
+ y: 20,
+ width: 100,
+ height: 200,
+ },
+ });
});
});
});
@@ -69,10 +113,10 @@ describe('DiffContent', () => {
describe('file diff', () => {
it('should have download buttons in place', done => {
const el = vm.$el;
- vm.diffFile.newPath = 'test.abc';
- vm.diffFile.newSha = 'DEF';
- vm.diffFile.oldPath = 'test.abc';
- vm.diffFile.oldSha = 'ABC';
+ vm.diffFile.new_path = 'test.abc';
+ vm.diffFile.new_sha = 'DEF';
+ vm.diffFile.old_path = 'test.abc';
+ vm.diffFile.old_sha = 'ABC';
vm.$nextTick(() => {
expect(el.querySelectorAll('.js-diff-inline-view').length).toEqual(0);
diff --git a/spec/javascripts/diffs/components/diff_discussions_spec.js b/spec/javascripts/diffs/components/diff_discussions_spec.js
index 270f363825f..0bc9da5ad0f 100644
--- a/spec/javascripts/diffs/components/diff_discussions_spec.js
+++ b/spec/javascripts/diffs/components/diff_discussions_spec.js
@@ -1,24 +1,90 @@
import Vue from 'vue';
import DiffDiscussions from '~/diffs/components/diff_discussions.vue';
-import store from '~/mr_notes/stores';
+import { createStore } from '~/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import '~/behaviors/markdown/render_gfm';
import discussionsMockData from '../mock_data/diff_discussions';
describe('DiffDiscussions', () => {
- let component;
+ let vm;
const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
- beforeEach(() => {
- component = createComponentWithStore(Vue.extend(DiffDiscussions), store, {
+ function createComponent(props = {}) {
+ const store = createStore();
+
+ vm = createComponentWithStore(Vue.extend(DiffDiscussions), store, {
discussions: getDiscussionsMockData(),
+ ...props,
}).$mount();
+ }
+
+ afterEach(() => {
+ vm.$destroy();
});
describe('template', () => {
it('should have notes list', () => {
- const { $el } = component;
+ createComponent();
+
+ expect(vm.$el.querySelectorAll('.discussion .note.timeline-entry').length).toEqual(5);
+ });
+ });
+
+ describe('image commenting', () => {
+ it('renders collapsible discussion button', () => {
+ createComponent({ shouldCollapseDiscussions: true });
+
+ expect(vm.$el.querySelector('.js-diff-notes-toggle')).not.toBe(null);
+ expect(vm.$el.querySelector('.js-diff-notes-toggle svg')).not.toBe(null);
+ expect(vm.$el.querySelector('.js-diff-notes-toggle').classList).toContain(
+ 'diff-notes-collapse',
+ );
+ });
+
+ it('dispatches toggleDiscussion when clicking collapse button', () => {
+ createComponent({ shouldCollapseDiscussions: true });
+
+ spyOn(vm.$store, 'dispatch').and.stub();
+
+ vm.$el.querySelector('.js-diff-notes-toggle').click();
+
+ expect(vm.$store.dispatch).toHaveBeenCalledWith('toggleDiscussion', {
+ discussionId: vm.discussions[0].id,
+ });
+ });
+
+ it('renders expand button when discussion is collapsed', done => {
+ createComponent({ shouldCollapseDiscussions: true });
+
+ vm.discussions[0].expanded = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.js-diff-notes-toggle').textContent.trim()).toBe('1');
+ expect(vm.$el.querySelector('.js-diff-notes-toggle').className).toContain(
+ 'btn-transparent badge badge-pill',
+ );
+
+ done();
+ });
+ });
+
+ it('hides discussion when collapsed', done => {
+ createComponent({ shouldCollapseDiscussions: true });
+
+ vm.discussions[0].expanded = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.note-discussion').style.display).toBe('none');
+
+ done();
+ });
+ });
+
+ it('renders badge on avatar', () => {
+ createComponent({ renderAvatarBadge: true, discussions: [{ ...discussionsMockData }] });
- expect($el.querySelectorAll('.discussion .note.timeline-entry').length).toEqual(5);
+ expect(vm.$el.querySelector('.user-avatar-link .badge-pill')).not.toBe(null);
+ expect(vm.$el.querySelector('.user-avatar-link .badge-pill').textContent.trim()).toBe('1');
});
});
});
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index 92b2004c4d7..9530b50c729 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -3,9 +3,10 @@ import Vuex from 'vuex';
import diffsModule from '~/diffs/store/modules';
import notesModule from '~/notes/stores/modules';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+Vue.use(Vuex);
+
const discussionFixture = 'merge_requests/diff_discussion.json';
describe('diff_file_header', () => {
@@ -16,15 +17,15 @@ describe('diff_file_header', () => {
const store = new Vuex.Store({
modules: {
- diffs: diffsModule,
- notes: notesModule,
+ diffs: diffsModule(),
+ notes: notesModule(),
},
});
beforeEach(() => {
- const diffFile = convertObjectPropsToCamelCase(diffDiscussionMock.diff_file, { deep: true });
+ const diffFile = diffDiscussionMock.diff_file;
props = {
- diffFile,
+ diffFile: { ...diffFile },
canCurrentUserFork: false,
};
});
@@ -58,19 +59,19 @@ describe('diff_file_header', () => {
describe('titleLink', () => {
beforeEach(() => {
+ props.discussionPath = 'link://to/discussion';
Object.assign(props.diffFile, {
- fileHash: 'badc0ffee',
- submoduleLink: 'link://to/submodule',
- submoduleTreeUrl: 'some://tree/url',
+ submodule_link: 'link://to/submodule',
+ submodule_tree_url: 'some://tree/url',
});
});
- it('returns the fileHash for files', () => {
+ it('returns the discussionPath for files', () => {
props.diffFile.submodule = false;
vm = mountComponentWithStore(Component, { props, store });
- expect(vm.titleLink).toBe(`#${props.diffFile.fileHash}`);
+ expect(vm.titleLink).toBe(props.discussionPath);
});
it('returns the submoduleTreeUrl for submodules', () => {
@@ -78,18 +79,26 @@ describe('diff_file_header', () => {
vm = mountComponentWithStore(Component, { props, store });
- expect(vm.titleLink).toBe(props.diffFile.submoduleTreeUrl);
+ expect(vm.titleLink).toBe(props.diffFile.submodule_tree_url);
});
it('returns the submoduleLink for submodules without submoduleTreeUrl', () => {
Object.assign(props.diffFile, {
submodule: true,
- submoduleTreeUrl: null,
+ submodule_tree_url: null,
});
vm = mountComponentWithStore(Component, { props, store });
- expect(vm.titleLink).toBe(props.diffFile.submoduleLink);
+ expect(vm.titleLink).toBe(props.diffFile.submodule_link);
+ });
+
+ it('sets the correct path to the discussion', () => {
+ props.discussionPath = 'link://to/discussion';
+ vm = mountComponentWithStore(Component, { props, store });
+ const href = vm.$el.querySelector('.js-title-wrapper').getAttribute('href');
+
+ expect(href).toBe(vm.discussionPath);
});
});
@@ -97,7 +106,7 @@ describe('diff_file_header', () => {
beforeEach(() => {
Object.assign(props.diffFile, {
blob: { id: 'b10b1db10b1d' },
- filePath: 'path/to/file',
+ file_path: 'path/to/file',
});
});
@@ -106,7 +115,7 @@ describe('diff_file_header', () => {
vm = mountComponentWithStore(Component, { props, store });
- expect(vm.filePath).toBe(props.diffFile.filePath);
+ expect(vm.filePath).toBe(props.diffFile.file_path);
});
it('appends the truncated blob id for submodules', () => {
@@ -115,14 +124,14 @@ describe('diff_file_header', () => {
vm = mountComponentWithStore(Component, { props, store });
expect(vm.filePath).toBe(
- `${props.diffFile.filePath} @ ${props.diffFile.blob.id.substr(0, 8)}`,
+ `${props.diffFile.file_path} @ ${props.diffFile.blob.id.substr(0, 8)}`,
);
});
});
describe('titleTag', () => {
it('returns a link tag if fileHash is set', () => {
- props.diffFile.fileHash = 'some hash';
+ props.diffFile.file_hash = 'some hash';
vm = mountComponentWithStore(Component, { props, store });
@@ -130,7 +139,7 @@ describe('diff_file_header', () => {
});
it('returns a span tag if fileHash is not set', () => {
- props.diffFile.fileHash = null;
+ props.diffFile.file_hash = null;
vm = mountComponentWithStore(Component, { props, store });
@@ -141,8 +150,8 @@ describe('diff_file_header', () => {
describe('isUsingLfs', () => {
beforeEach(() => {
Object.assign(props.diffFile, {
- storedExternally: true,
- externalStorage: 'lfs',
+ stored_externally: true,
+ external_storage: 'lfs',
});
});
@@ -153,7 +162,7 @@ describe('diff_file_header', () => {
});
it('returns false if file is not stored externally', () => {
- props.diffFile.storedExternally = false;
+ props.diffFile.stored_externally = false;
vm = mountComponentWithStore(Component, { props, store });
@@ -161,7 +170,7 @@ describe('diff_file_header', () => {
});
it('returns false if file is not stored in LFS', () => {
- props.diffFile.externalStorage = 'not lfs';
+ props.diffFile.external_storage = 'not lfs';
vm = mountComponentWithStore(Component, { props, store });
@@ -190,7 +199,7 @@ describe('diff_file_header', () => {
describe('viewFileButtonText', () => {
it('contains the truncated content SHA', () => {
const dummySha = 'deebd00f is no SHA';
- props.diffFile.contentSha = dummySha;
+ props.diffFile.content_sha = dummySha;
vm = mountComponentWithStore(Component, { props, store });
@@ -202,7 +211,7 @@ describe('diff_file_header', () => {
describe('viewReplacedFileButtonText', () => {
it('contains the truncated base SHA', () => {
const dummySha = 'deadabba sings no more';
- props.diffFile.diffRefs.baseSha = dummySha;
+ props.diffFile.diff_refs.base_sha = dummySha;
vm = mountComponentWithStore(Component, { props, store });
@@ -261,6 +270,7 @@ describe('diff_file_header', () => {
it('displays an file icon in the title', () => {
vm = mountComponentWithStore(Component, { props, store });
+
expect(vm.$el.querySelector('svg.js-file-icon use').getAttribute('xlink:href')).toContain(
'ruby',
);
@@ -270,32 +280,32 @@ describe('diff_file_header', () => {
const filePaths = () => vm.$el.querySelectorAll('.file-title-name');
it('displays the path of a added file', () => {
- props.diffFile.renamedFile = false;
+ props.diffFile.renamed_file = false;
vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(1);
- expect(filePaths()[0]).toHaveText(props.diffFile.filePath);
+ expect(filePaths()[0]).toHaveText(props.diffFile.file_path);
});
it('displays path for deleted file', () => {
- props.diffFile.renamedFile = false;
- props.diffFile.deletedFile = true;
+ props.diffFile.renamed_file = false;
+ props.diffFile.deleted_file = true;
vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(1);
- expect(filePaths()[0]).toHaveText(`${props.diffFile.filePath} deleted`);
+ expect(filePaths()[0]).toHaveText(`${props.diffFile.file_path} deleted`);
});
it('displays old and new path if the file was renamed', () => {
- props.diffFile.renamedFile = true;
+ props.diffFile.renamed_file = true;
vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(2);
- expect(filePaths()[0]).toHaveText(props.diffFile.oldPath);
- expect(filePaths()[1]).toHaveText(props.diffFile.newPath);
+ expect(filePaths()[0]).toHaveText(props.diffFile.old_path);
+ expect(filePaths()[1]).toHaveText(props.diffFile.new_path);
});
});
@@ -303,28 +313,33 @@ describe('diff_file_header', () => {
vm = mountComponentWithStore(Component, { props, store });
const button = vm.$el.querySelector('.btn-clipboard');
+
expect(button).not.toBe(null);
- expect(button.dataset.clipboardText).toBe('{"text":"files/ruby/popen.rb","gfm":"`files/ruby/popen.rb`"}');
+ expect(button.dataset.clipboardText).toBe(
+ '{"text":"files/ruby/popen.rb","gfm":"`files/ruby/popen.rb`"}',
+ );
});
describe('file mode', () => {
it('it displays old and new file mode if it changed', () => {
- props.diffFile.modeChanged = true;
+ props.diffFile.mode_changed = true;
vm = mountComponentWithStore(Component, { props, store });
const { fileMode } = vm.$refs;
+
expect(fileMode).not.toBe(undefined);
- expect(fileMode).toContainText(props.diffFile.aMode);
- expect(fileMode).toContainText(props.diffFile.bMode);
+ expect(fileMode).toContainText(props.diffFile.a_mode);
+ expect(fileMode).toContainText(props.diffFile.b_mode);
});
it('does not display the file mode if it has not changed', () => {
- props.diffFile.modeChanged = false;
+ props.diffFile.mode_changed = false;
vm = mountComponentWithStore(Component, { props, store });
const { fileMode } = vm.$refs;
+
expect(fileMode).toBe(undefined);
});
});
@@ -334,8 +349,8 @@ describe('diff_file_header', () => {
it('displays the LFS label for files stored in LFS', () => {
Object.assign(props.diffFile, {
- storedExternally: true,
- externalStorage: 'lfs',
+ stored_externally: true,
+ external_storage: 'lfs',
});
vm = mountComponentWithStore(Component, { props, store });
@@ -345,7 +360,7 @@ describe('diff_file_header', () => {
});
it('does not display the LFS label for files stored in repository', () => {
- props.diffFile.storedExternally = false;
+ props.diffFile.stored_externally = false;
vm = mountComponentWithStore(Component, { props, store });
@@ -362,7 +377,7 @@ describe('diff_file_header', () => {
it('should show edit button when file is editable', () => {
props.addMergeRequestButtons = true;
- props.diffFile.editPath = '/';
+ props.diffFile.edit_path = '/';
vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('.js-edit-blob')).toContainText('Edit');
@@ -370,8 +385,8 @@ describe('diff_file_header', () => {
it('should not show edit button when file is deleted', () => {
props.addMergeRequestButtons = true;
- props.diffFile.deletedFile = true;
- props.diffFile.editPath = '/';
+ props.diffFile.deleted_file = true;
+ props.diffFile.edit_path = '/';
vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null);
@@ -381,7 +396,7 @@ describe('diff_file_header', () => {
describe('addMergeRequestButtons', () => {
beforeEach(() => {
props.addMergeRequestButtons = true;
- props.diffFile.editPath = '';
+ props.diffFile.edit_path = '';
});
describe('view on environment button', () => {
@@ -389,8 +404,8 @@ describe('diff_file_header', () => {
const title = 'url.title';
it('displays link to external url', () => {
- props.diffFile.externalUrl = url;
- props.diffFile.formattedExternalUrl = title;
+ props.diffFile.external_url = url;
+ props.diffFile.formatted_external_url = title;
vm = mountComponentWithStore(Component, { props, store });
@@ -399,8 +414,8 @@ describe('diff_file_header', () => {
});
it('hides link if no external url', () => {
- props.diffFile.externalUrl = '';
- props.diffFile.formattedExternalUrl = title;
+ props.diffFile.external_url = '';
+ props.diffFile.formattedExternal_url = title;
vm = mountComponentWithStore(Component, { props, store });
@@ -418,11 +433,11 @@ describe('diff_file_header', () => {
path: 'lib/base.js',
name: 'base.js',
mode: '100644',
- readableText: true,
+ readable_text: true,
icon: 'file-text-o',
};
propsCopy.addMergeRequestButtons = true;
- propsCopy.diffFile.deletedFile = true;
+ propsCopy.diffFile.deleted_file = true;
vm = mountComponentWithStore(Component, {
props: propsCopy,
@@ -443,20 +458,21 @@ describe('diff_file_header', () => {
path: 'lib/base.js',
name: 'base.js',
mode: '100644',
- readableText: true,
+ readable_text: true,
icon: 'file-text-o',
};
propsCopy.addMergeRequestButtons = true;
- propsCopy.diffFile.deletedFile = true;
+ propsCopy.diffFile.deleted_file = true;
const discussionGetter = () => [diffDiscussionMock];
- notesModule.getters.discussions = discussionGetter;
+ const notesModuleMock = notesModule();
+ notesModuleMock.getters.discussions = discussionGetter;
vm = mountComponentWithStore(Component, {
props: propsCopy,
store: new Vuex.Store({
modules: {
- diffs: diffsModule,
- notes: notesModule,
+ diffs: diffsModule(),
+ notes: notesModuleMock,
},
}),
});
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 7a4616ec8eb..51bb4807960 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -6,11 +6,10 @@ import diffFileMockData from '../mock_data/diff_file';
describe('DiffFile', () => {
let vm;
- const getDiffFileMock = () => Object.assign({}, diffFileMockData);
beforeEach(() => {
vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, {
- file: getDiffFileMock(),
+ file: JSON.parse(JSON.stringify(diffFileMockData)),
canCurrentUserFork: false,
}).$mount();
});
@@ -18,15 +17,22 @@ describe('DiffFile', () => {
describe('template', () => {
it('should render component with file header, file content components', () => {
const el = vm.$el;
- const { fileHash, filePath } = diffFileMockData;
+ const { file_hash, file_path } = vm.file;
- expect(el.id).toEqual(fileHash);
+ expect(el.id).toEqual(file_hash);
expect(el.classList.contains('diff-file')).toEqual(true);
+
expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
expect(el.querySelector('.js-file-title')).toBeDefined();
- expect(el.querySelector('.file-title-name').innerText.indexOf(filePath) > -1).toEqual(true);
+ expect(el.querySelector('.file-title-name').innerText.indexOf(file_path)).toBeGreaterThan(-1);
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
- expect(el.querySelectorAll('.line_content').length > 5).toEqual(true);
+
+ expect(vm.file.renderIt).toEqual(false);
+ vm.file.renderIt = true;
+
+ vm.$nextTick(() => {
+ expect(el.querySelectorAll('.line_content').length).toBeGreaterThan(5);
+ });
});
describe('collapsed', () => {
@@ -34,6 +40,7 @@ describe('DiffFile', () => {
expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1);
expect(vm.file.collapsed).toEqual(false);
vm.file.collapsed = true;
+ vm.file.renderIt = true;
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(0);
@@ -43,6 +50,20 @@ describe('DiffFile', () => {
});
it('should have collapsed text and link', done => {
+ vm.file.renderIt = true;
+ vm.file.collapsed = false;
+ vm.file.highlighted_diff_lines = null;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.innerText).toContain('This diff is collapsed');
+ expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1);
+
+ done();
+ });
+ });
+
+ it('should have collapsed text and link even before rendered', done => {
+ vm.file.renderIt = false;
vm.file.collapsed = true;
vm.$nextTick(() => {
@@ -69,20 +90,43 @@ describe('DiffFile', () => {
describe('too large diff', () => {
it('should have too large warning and blob link', done => {
const BLOB_LINK = '/file/view/path';
- vm.file.tooLarge = true;
- vm.file.viewPath = BLOB_LINK;
+ vm.file.too_large = true;
+ vm.file.view_path = BLOB_LINK;
vm.$nextTick(() => {
expect(vm.$el.innerText).toContain(
'This source diff could not be displayed because it is too large',
);
+
expect(vm.$el.querySelector('.js-too-large-diff')).toBeDefined();
- expect(vm.$el.querySelector('.js-too-large-diff a').href.indexOf(BLOB_LINK) > -1).toEqual(
- true,
- );
+ expect(
+ vm.$el.querySelector('.js-too-large-diff a').href.indexOf(BLOB_LINK),
+ ).toBeGreaterThan(-1);
done();
});
});
});
+
+ describe('watch collapsed', () => {
+ it('calls handleLoadCollapsedDiff if collapsed changed & file has no lines', done => {
+ spyOn(vm, 'handleLoadCollapsedDiff');
+
+ vm.file.highlighted_diff_lines = undefined;
+ vm.file.parallel_diff_lines = [];
+ vm.file.collapsed = true;
+
+ vm.$nextTick()
+ .then(() => {
+ vm.file.collapsed = false;
+
+ return vm.$nextTick();
+ })
+ .then(() => {
+ expect(vm.handleLoadCollapsedDiff).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js
index 0085a16815a..ad2605a5c5c 100644
--- a/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js
+++ b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js
@@ -23,6 +23,7 @@ describe('DiffGutterAvatars', () => {
it('should return false when all discussions are not expanded', () => {
component.discussions[0].expanded = false;
+
expect(component.discussionsExpanded).toEqual(false);
});
});
@@ -56,6 +57,7 @@ describe('DiffGutterAvatars', () => {
it('should return empty string if there is no discussion', () => {
component.discussions = [];
+
expect(component.moreText).toEqual('');
});
});
diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
index a1a37b342b7..038db8eaa7c 100644
--- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
@@ -6,61 +6,65 @@ import discussionsMockData from '../mock_data/diff_discussions';
import diffFileMockData from '../mock_data/diff_file';
describe('DiffLineGutterContent', () => {
- const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
const getDiffFileMock = () => Object.assign({}, diffFileMockData);
const createComponent = (options = {}) => {
const cmp = Vue.extend(DiffLineGutterContent);
const props = Object.assign({}, options);
- props.fileHash = getDiffFileMock().fileHash;
+ props.line = {
+ line_code: 'LC_42',
+ type: 'new',
+ old_line: null,
+ new_line: 1,
+ discussions: [{ ...discussionsMockData }],
+ text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
+ rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
+ meta_data: null,
+ };
+ props.fileHash = getDiffFileMock().file_hash;
props.contextLinesPath = '/context/lines/path';
return createComponentWithStore(cmp, store, props).$mount();
};
- const setDiscussions = component => {
- component.$store.dispatch('setInitialNotes', getDiscussionsMockData());
- };
-
- const resetDiscussions = component => {
- component.$store.dispatch('setInitialNotes', []);
- };
describe('computed', () => {
describe('lineHref', () => {
it('should prepend # to lineCode', () => {
const lineCode = 'LC_42';
- const component = createComponent({ lineCode });
+ const component = createComponent();
+
expect(component.lineHref).toEqual(`#${lineCode}`);
});
it('should return # if there is no lineCode', () => {
- const component = createComponent({ lineCode: null });
+ const component = createComponent();
+ component.line.line_code = '';
+
expect(component.lineHref).toEqual('#');
});
});
describe('discussions, hasDiscussions, shouldShowAvatarsOnGutter', () => {
it('should return empty array when there is no discussion', () => {
- const component = createComponent({ lineCode: 'LC_42' });
- expect(component.discussions).toEqual([]);
+ const component = createComponent();
+ component.line.discussions = [];
+
expect(component.hasDiscussions).toEqual(false);
expect(component.shouldShowAvatarsOnGutter).toEqual(false);
});
it('should return discussions for the given lineCode', () => {
- const { lineCode } = getDiffFileMock().highlightedDiffLines[1];
- const component = createComponent({
- lineCode,
+ const cmp = Vue.extend(DiffLineGutterContent);
+ const props = {
+ line: getDiffFileMock().highlighted_diff_lines[1],
+ fileHash: getDiffFileMock().file_hash,
showCommentButton: true,
- discussions: getDiscussionsMockData(),
- });
+ contextLinesPath: '/context/lines/path',
+ };
+ props.line.discussions = [Object.assign({}, discussionsMockData)];
+ const component = createComponentWithStore(cmp, store, props).$mount();
- setDiscussions(component);
-
- expect(component.discussions).toEqual(getDiscussionsMockData());
expect(component.hasDiscussions).toEqual(true);
expect(component.shouldShowAvatarsOnGutter).toEqual(true);
-
- resetDiscussions(component);
});
});
});
@@ -94,19 +98,17 @@ describe('DiffLineGutterContent', () => {
const component = createComponent({ lineNumber, lineCode });
const link = component.$el.querySelector('a');
- expect(link.href.indexOf(`#${lineCode}`) > -1).toEqual(true);
+ expect(link.href.indexOf(`#${lineCode}`)).toBeGreaterThan(-1);
expect(link.dataset.linenumber).toEqual(lineNumber.toString());
});
it('should render user avatars', () => {
const component = createComponent({
showCommentButton: true,
- lineCode: getDiffFileMock().highlightedDiffLines[1].lineCode,
+ lineCode: getDiffFileMock().highlighted_diff_lines[1].line_code,
});
- setDiscussions(component);
- expect(component.$el.querySelector('.diff-comment-avatar-holders')).toBeDefined();
- resetDiscussions(component);
+ expect(component.$el.querySelector('.diff-comment-avatar-holders')).not.toBe(null);
});
});
});
diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
index 6fe5fdaf7f9..81b66cf7c9b 100644
--- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
@@ -13,10 +13,10 @@ describe('DiffLineNoteForm', () => {
beforeEach(() => {
diffFile = getDiffFileMock();
- diffLines = diffFile.highlightedDiffLines;
+ diffLines = diffFile.highlighted_diff_lines;
component = createComponentWithStore(Vue.extend(DiffLineNoteForm), store, {
- diffFileHash: diffFile.fileHash,
+ diffFileHash: diffFile.file_hash,
diffLines,
line: diffLines[0],
noteTargetLine: diffLines[0],
@@ -36,6 +36,7 @@ describe('DiffLineNoteForm', () => {
spyOn(window, 'confirm').and.returnValue(false);
component.handleCancelCommentForm(true, true);
+
expect(window.confirm).toHaveBeenCalled();
});
@@ -43,9 +44,11 @@ describe('DiffLineNoteForm', () => {
spyOn(window, 'confirm').and.returnValue(false);
component.handleCancelCommentForm(true, false);
+
expect(window.confirm).not.toHaveBeenCalled();
component.handleCancelCommentForm(false, true);
+
expect(window.confirm).not.toHaveBeenCalled();
});
@@ -58,8 +61,9 @@ describe('DiffLineNoteForm', () => {
expect(window.confirm).not.toHaveBeenCalled();
component.$nextTick(() => {
expect(component.cancelCommentForm).toHaveBeenCalledWith({
- lineCode: diffLines[0].lineCode,
+ lineCode: diffLines[0].line_code,
});
+
expect(component.resetAutoSave).toHaveBeenCalled();
done();
@@ -69,22 +73,21 @@ describe('DiffLineNoteForm', () => {
describe('saveNoteForm', () => {
it('should call saveNote action with proper params', done => {
- let isPromiseCalled = false;
- const formDataSpy = spyOnDependency(DiffLineNoteForm, 'getNoteFormData').and.returnValue({
- postData: 1,
- });
- const saveNoteSpy = spyOn(component, 'saveNote').and.returnValue(
- new Promise(() => {
- isPromiseCalled = true;
- done();
- }),
+ const saveDiffDiscussionSpy = spyOn(component, 'saveDiffDiscussion').and.returnValue(
+ Promise.resolve(),
);
-
- component.handleSaveNote('note body');
-
- expect(formDataSpy).toHaveBeenCalled();
- expect(saveNoteSpy).toHaveBeenCalled();
- expect(isPromiseCalled).toEqual(true);
+ spyOnProperty(component, 'formData').and.returnValue('formData');
+
+ component
+ .handleSaveNote('note body')
+ .then(() => {
+ expect(saveDiffDiscussionSpy).toHaveBeenCalledWith({
+ note: 'note body',
+ formData: 'formData',
+ });
+ })
+ .then(done)
+ .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/diffs/components/file_row_stats_spec.js b/spec/javascripts/diffs/components/file_row_stats_spec.js
new file mode 100644
index 00000000000..a8a7f3f1d82
--- /dev/null
+++ b/spec/javascripts/diffs/components/file_row_stats_spec.js
@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import FileRowStats from '~/diffs/components/file_row_stats.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Diff file row stats', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(FileRowStats);
+ });
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ file: {
+ addedLines: 20,
+ removedLines: 10,
+ },
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders added lines count', () => {
+ expect(vm.$el.querySelector('.cgreen').textContent).toContain('+20');
+ });
+
+ it('renders removed lines count', () => {
+ expect(vm.$el.querySelector('.cred').textContent).toContain('-10');
+ });
+});
diff --git a/spec/javascripts/diffs/components/image_diff_overlay_spec.js b/spec/javascripts/diffs/components/image_diff_overlay_spec.js
new file mode 100644
index 00000000000..d76ab745fe1
--- /dev/null
+++ b/spec/javascripts/diffs/components/image_diff_overlay_spec.js
@@ -0,0 +1,146 @@
+import Vue from 'vue';
+import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
+import { createStore } from '~/mr_notes/stores';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { imageDiffDiscussions } from '../mock_data/diff_discussions';
+
+describe('Diffs image diff overlay component', () => {
+ const dimensions = {
+ width: 100,
+ height: 200,
+ };
+ let Component;
+ let vm;
+
+ function createComponent(props = {}, extendStore = () => {}) {
+ const store = createStore();
+
+ extendStore(store);
+
+ vm = createComponentWithStore(Component, store, {
+ discussions: [...imageDiffDiscussions],
+ fileHash: 'ABC',
+ ...props,
+ });
+ }
+
+ beforeAll(() => {
+ Component = Vue.extend(ImageDiffOverlay);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders comment badges', () => {
+ createComponent();
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+
+ expect(vm.$el.querySelectorAll('.js-image-badge').length).toBe(2);
+ });
+
+ it('renders index of discussion in badge', () => {
+ createComponent();
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+
+ expect(vm.$el.querySelectorAll('.js-image-badge')[0].textContent.trim()).toBe('1');
+ expect(vm.$el.querySelectorAll('.js-image-badge')[1].textContent.trim()).toBe('2');
+ });
+
+ it('renders icon when showCommentIcon is true', () => {
+ createComponent({ showCommentIcon: true });
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+
+ expect(vm.$el.querySelector('.js-image-badge svg')).not.toBe(null);
+ });
+
+ it('sets badge comment positions', () => {
+ createComponent();
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+
+ expect(vm.$el.querySelectorAll('.js-image-badge')[0].style.left).toBe('10px');
+ expect(vm.$el.querySelectorAll('.js-image-badge')[0].style.top).toBe('10px');
+
+ expect(vm.$el.querySelectorAll('.js-image-badge')[1].style.left).toBe('5px');
+ expect(vm.$el.querySelectorAll('.js-image-badge')[1].style.top).toBe('5px');
+ });
+
+ it('renders single badge for discussion object', () => {
+ createComponent({
+ discussions: {
+ ...imageDiffDiscussions[0],
+ },
+ });
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+
+ expect(vm.$el.querySelectorAll('.js-image-badge').length).toBe(1);
+ });
+
+ it('dispatches openDiffFileCommentForm when clicking overlay', () => {
+ createComponent({ canComment: true });
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+
+ spyOn(vm.$store, 'dispatch').and.stub();
+
+ vm.$el.querySelector('.js-add-image-diff-note-button').click();
+
+ expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/openDiffFileCommentForm', {
+ fileHash: 'ABC',
+ x: 0,
+ y: 0,
+ width: 100,
+ height: 200,
+ });
+ });
+
+ describe('toggle discussion', () => {
+ it('disables buttons when shouldToggleDiscussion is false', () => {
+ createComponent({ shouldToggleDiscussion: false });
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+
+ expect(vm.$el.querySelector('.js-image-badge').hasAttribute('disabled')).toBe(true);
+ });
+
+ it('dispatches toggleDiscussion when clicking image badge', () => {
+ createComponent();
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+
+ spyOn(vm.$store, 'dispatch').and.stub();
+
+ vm.$el.querySelector('.js-image-badge').click();
+
+ expect(vm.$store.dispatch).toHaveBeenCalledWith('toggleDiscussion', { discussionId: '1' });
+ });
+ });
+
+ describe('comment form', () => {
+ beforeEach(() => {
+ createComponent({}, store => {
+ store.state.diffs.commentForms.push({
+ fileHash: 'ABC',
+ x: 20,
+ y: 10,
+ });
+ });
+ spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
+ vm.$mount();
+ });
+
+ it('renders comment form badge', () => {
+ expect(vm.$el.querySelector('.comment-indicator')).not.toBe(null);
+ });
+
+ it('sets comment form badge position', () => {
+ expect(vm.$el.querySelector('.comment-indicator').style.left).toBe('20px');
+ expect(vm.$el.querySelector('.comment-indicator').style.top).toBe('10px');
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
index b02328dd359..2316ee29106 100644
--- a/spec/javascripts/diffs/components/inline_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import '~/behaviors/markdown/render_gfm';
import InlineDiffView from '~/diffs/components/inline_diff_view.vue';
import store from '~/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
@@ -10,14 +11,16 @@ describe('InlineDiffView', () => {
const getDiffFileMock = () => Object.assign({}, diffFileMockData);
const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
- beforeEach(() => {
+ beforeEach(done => {
const diffFile = getDiffFileMock();
store.dispatch('diffs/setInlineDiffViewType');
component = createComponentWithStore(Vue.extend(InlineDiffView), store, {
diffFile,
- diffLines: diffFile.highlightedDiffLines,
+ diffLines: diffFile.highlighted_diff_lines,
}).$mount();
+
+ Vue.nextTick(done);
});
describe('template', () => {
@@ -27,17 +30,17 @@ describe('InlineDiffView', () => {
expect(el.querySelectorAll('tr.line_holder').length).toEqual(6);
expect(el.querySelectorAll('tr.line_holder.new').length).toEqual(2);
expect(el.querySelectorAll('tr.line_holder.match').length).toEqual(1);
- expect(el.textContent.indexOf('Bad dates') > -1).toEqual(true);
+ expect(el.textContent.indexOf('Bad dates')).toBeGreaterThan(-1);
});
it('should render discussions', done => {
const el = component.$el;
- component.$store.dispatch('setInitialNotes', getDiscussionsMockData());
+ component.diffLines[1].discussions = getDiscussionsMockData();
Vue.nextTick(() => {
expect(el.querySelectorAll('.notes_holder').length).toEqual(1);
expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(5);
- expect(el.innerText.indexOf('comment 5') > -1).toEqual(true);
+ expect(el.innerText.indexOf('comment 5')).toBeGreaterThan(-1);
component.$store.dispatch('setInitialNotes', []);
done();
diff --git a/spec/javascripts/diffs/components/parallel_diff_view_spec.js b/spec/javascripts/diffs/components/parallel_diff_view_spec.js
index 165e4b69b6c..6f6b1c41915 100644
--- a/spec/javascripts/diffs/components/parallel_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/parallel_diff_view_spec.js
@@ -14,15 +14,15 @@ describe('ParallelDiffView', () => {
component = createComponentWithStore(Vue.extend(ParallelDiffView), store, {
diffFile,
- diffLines: diffFile.parallelDiffLines,
+ diffLines: diffFile.parallel_diff_lines,
}).$mount();
});
- describe('computed', () => {
- describe('parallelDiffLines', () => {
+ describe('assigned', () => {
+ describe('diffLines', () => {
it('should normalize lines for empty cells', () => {
- expect(component.parallelDiffLines[0].left.type).toEqual(constants.EMPTY_CELL_TYPE);
- expect(component.parallelDiffLines[1].left.type).toEqual(constants.EMPTY_CELL_TYPE);
+ expect(component.diffLines[0].left.type).toEqual(constants.EMPTY_CELL_TYPE);
+ expect(component.diffLines[1].left.type).toEqual(constants.EMPTY_CELL_TYPE);
});
});
});
diff --git a/spec/javascripts/diffs/components/tree_list_spec.js b/spec/javascripts/diffs/components/tree_list_spec.js
new file mode 100644
index 00000000000..a0b380adfd6
--- /dev/null
+++ b/spec/javascripts/diffs/components/tree_list_spec.js
@@ -0,0 +1,188 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import TreeList from '~/diffs/components/tree_list.vue';
+import createStore from '~/diffs/store/modules';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+
+describe('Diffs tree list component', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(TreeList);
+ });
+
+ beforeEach(() => {
+ Vue.use(Vuex);
+
+ const store = new Vuex.Store({
+ modules: {
+ diffs: createStore(),
+ },
+ });
+
+ // Setup initial state
+ store.state.diffs.addedLines = 10;
+ store.state.diffs.removedLines = 20;
+ store.state.diffs.diffFiles.push('test');
+
+ vm = mountComponentWithStore(Component, { store });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders diff stats', () => {
+ expect(vm.$el.textContent).toContain('1 changed file');
+ expect(vm.$el.textContent).toContain('10 additions');
+ expect(vm.$el.textContent).toContain('20 deletions');
+ });
+
+ it('renders empty text', () => {
+ expect(vm.$el.textContent).toContain('No files found');
+ });
+
+ describe('with files', () => {
+ beforeEach(done => {
+ Object.assign(vm.$store.state.diffs.treeEntries, {
+ 'index.js': {
+ addedLines: 0,
+ changed: true,
+ deleted: false,
+ fileHash: 'test',
+ key: 'index.js',
+ name: 'index.js',
+ path: 'app/index.js',
+ removedLines: 0,
+ tempFile: true,
+ type: 'blob',
+ },
+ app: {
+ key: 'app',
+ path: 'app',
+ name: 'app',
+ type: 'tree',
+ tree: [],
+ },
+ });
+ vm.$store.state.diffs.tree = [
+ vm.$store.state.diffs.treeEntries['index.js'],
+ vm.$store.state.diffs.treeEntries.app,
+ ];
+
+ vm.$nextTick(done);
+ });
+
+ it('renders tree', () => {
+ expect(vm.$el.querySelectorAll('.file-row').length).toBe(2);
+ expect(vm.$el.querySelectorAll('.file-row')[0].textContent).toContain('index.js');
+ expect(vm.$el.querySelectorAll('.file-row')[1].textContent).toContain('app');
+ });
+
+ it('filters tree list to blobs matching search', done => {
+ vm.search = 'app/index';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.file-row').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.file-row')[0].textContent).toContain('index.js');
+
+ done();
+ });
+ });
+
+ it('calls toggleTreeOpen when clicking folder', () => {
+ spyOn(vm.$store, 'dispatch').and.stub();
+
+ vm.$el.querySelectorAll('.file-row')[1].click();
+
+ expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/toggleTreeOpen', 'app');
+ });
+
+ it('calls scrollToFile when clicking blob', () => {
+ spyOn(vm.$store, 'dispatch').and.stub();
+
+ vm.$el.querySelector('.file-row').click();
+
+ expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', 'app/index.js');
+ });
+
+ it('renders as file list when renderTreeList is false', done => {
+ vm.renderTreeList = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.file-row').length).toBe(1);
+
+ done();
+ });
+ });
+
+ it('renders file paths when renderTreeList is false', done => {
+ vm.renderTreeList = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.file-row').textContent).toContain('app/index.js');
+
+ done();
+ });
+ });
+
+ it('hides render buttons when input is focused', done => {
+ const focusEvent = new Event('focus');
+
+ vm.$el.querySelector('.form-control').dispatchEvent(focusEvent);
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.tree-list-view-toggle').style.display).toBe('none');
+
+ done();
+ });
+ });
+
+ it('shows render buttons when input is blurred', done => {
+ const blurEvent = new Event('blur');
+ vm.focusSearch = true;
+
+ vm.$nextTick()
+ .then(() => {
+ vm.$el.querySelector('.form-control').dispatchEvent(blurEvent);
+ })
+ .then(vm.$nextTick)
+ .then(() => {
+ expect(vm.$el.querySelector('.tree-list-view-toggle').style.display).not.toBe('none');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('clearSearch', () => {
+ it('resets search', () => {
+ vm.search = 'test';
+
+ vm.$el.querySelector('.tree-list-clear-icon').click();
+
+ expect(vm.search).toBe('');
+ });
+ });
+
+ describe('toggleRenderTreeList', () => {
+ it('updates renderTreeList', () => {
+ expect(vm.renderTreeList).toBe(true);
+
+ vm.toggleRenderTreeList(false);
+
+ expect(vm.renderTreeList).toBe(false);
+ });
+ });
+
+ describe('toggleFocusSearch', () => {
+ it('updates focusSearch', () => {
+ expect(vm.focusSearch).toBe(false);
+
+ vm.toggleFocusSearch(true);
+
+ expect(vm.focusSearch).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/create_diffs_store.js b/spec/javascripts/diffs/create_diffs_store.js
new file mode 100644
index 00000000000..aacde99964c
--- /dev/null
+++ b/spec/javascripts/diffs/create_diffs_store.js
@@ -0,0 +1,15 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import diffsModule from '~/diffs/store/modules';
+import notesModule from '~/notes/stores/modules';
+
+Vue.use(Vuex);
+
+export default function createDiffsStore() {
+ return new Vuex.Store({
+ modules: {
+ diffs: diffsModule(),
+ notes: notesModule(),
+ },
+ });
+}
diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js
index 41d0dfd8939..5ffe5a366ba 100644
--- a/spec/javascripts/diffs/mock_data/diff_discussions.js
+++ b/spec/javascripts/diffs/mock_data/diff_discussions.js
@@ -2,21 +2,19 @@ export default {
id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
reply_id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
position: {
- formatter: {
- old_line: null,
- new_line: 2,
- old_path: 'CHANGELOG',
- new_path: 'CHANGELOG',
- base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a',
- start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962',
- head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
- },
+ old_line: null,
+ new_line: 2,
+ old_path: 'CHANGELOG',
+ new_path: 'CHANGELOG',
+ base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a',
+ start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962',
+ head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
},
line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
expanded: true,
notes: [
{
- id: 1749,
+ id: '1749',
type: 'DiffNote',
attachment: null,
author: {
@@ -68,7 +66,7 @@ export default {
'/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
},
{
- id: 1753,
+ id: '1753',
type: 'DiffNote',
attachment: null,
author: {
@@ -120,7 +118,7 @@ export default {
'/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
},
{
- id: 1754,
+ id: '1754',
type: 'DiffNote',
attachment: null,
author: {
@@ -162,7 +160,7 @@ export default {
'/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
},
{
- id: 1755,
+ id: '1755',
type: 'DiffNote',
attachment: null,
author: {
@@ -204,7 +202,7 @@ export default {
'/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
},
{
- id: 1756,
+ id: '1756',
type: 'DiffNote',
attachment: null,
author: {
@@ -494,3 +492,24 @@ export default {
image_diff_html:
'<div class="image js-replaced-image" data="">\n<div class="two-up view">\n<div class="wrap">\n<div class="frame deleted">\n<img alt="CHANGELOG" src="http://localhost:3000/gitlab-org/gitlab-test/raw/e63f41fe459e62e1228fcef60d7189127aeba95a/CHANGELOG" />\n</div>\n<p class="image-info hide">\n<span class="meta-filesize">22.3 KB</span>\n|\n<strong>W:</strong>\n<span class="meta-width"></span>\n|\n<strong>H:</strong>\n<span class="meta-height"></span>\n</p>\n</div>\n<div class="wrap">\n<div class="added frame js-image-frame" data-note-type="DiffNote" data-position="{&quot;base_sha&quot;:&quot;e63f41fe459e62e1228fcef60d7189127aeba95a&quot;,&quot;start_sha&quot;:&quot;d9eaefe5a676b820c57ff18cf5b68316025f7962&quot;,&quot;head_sha&quot;:&quot;c48ee0d1bf3b30453f5b32250ce03134beaa6d13&quot;,&quot;old_path&quot;:&quot;CHANGELOG&quot;,&quot;new_path&quot;:&quot;CHANGELOG&quot;,&quot;position_type&quot;:&quot;text&quot;,&quot;old_line&quot;:null,&quot;new_line&quot;:2}">\n<img alt="CHANGELOG" draggable="false" src="http://localhost:3000/gitlab-org/gitlab-test/raw/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG" />\n</div>\n\n<p class="image-info hide">\n<span class="meta-filesize">22.3 KB</span>\n|\n<strong>W:</strong>\n<span class="meta-width"></span>\n|\n<strong>H:</strong>\n<span class="meta-height"></span>\n</p>\n</div>\n</div>\n<div class="swipe view hide">\n<div class="swipe-frame">\n<div class="frame deleted">\n<img alt="CHANGELOG" src="http://localhost:3000/gitlab-org/gitlab-test/raw/e63f41fe459e62e1228fcef60d7189127aeba95a/CHANGELOG" />\n</div>\n<div class="swipe-wrap">\n<div class="added frame js-image-frame" data-note-type="DiffNote" data-position="{&quot;base_sha&quot;:&quot;e63f41fe459e62e1228fcef60d7189127aeba95a&quot;,&quot;start_sha&quot;:&quot;d9eaefe5a676b820c57ff18cf5b68316025f7962&quot;,&quot;head_sha&quot;:&quot;c48ee0d1bf3b30453f5b32250ce03134beaa6d13&quot;,&quot;old_path&quot;:&quot;CHANGELOG&quot;,&quot;new_path&quot;:&quot;CHANGELOG&quot;,&quot;position_type&quot;:&quot;text&quot;,&quot;old_line&quot;:null,&quot;new_line&quot;:2}">\n<img alt="CHANGELOG" draggable="false" src="http://localhost:3000/gitlab-org/gitlab-test/raw/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG" />\n</div>\n\n</div>\n<span class="swipe-bar">\n<span class="top-handle"></span>\n<span class="bottom-handle"></span>\n</span>\n</div>\n</div>\n<div class="onion-skin view hide">\n<div class="onion-skin-frame">\n<div class="frame deleted">\n<img alt="CHANGELOG" src="http://localhost:3000/gitlab-org/gitlab-test/raw/e63f41fe459e62e1228fcef60d7189127aeba95a/CHANGELOG" />\n</div>\n<div class="added frame js-image-frame" data-note-type="DiffNote" data-position="{&quot;base_sha&quot;:&quot;e63f41fe459e62e1228fcef60d7189127aeba95a&quot;,&quot;start_sha&quot;:&quot;d9eaefe5a676b820c57ff18cf5b68316025f7962&quot;,&quot;head_sha&quot;:&quot;c48ee0d1bf3b30453f5b32250ce03134beaa6d13&quot;,&quot;old_path&quot;:&quot;CHANGELOG&quot;,&quot;new_path&quot;:&quot;CHANGELOG&quot;,&quot;position_type&quot;:&quot;text&quot;,&quot;old_line&quot;:null,&quot;new_line&quot;:2}">\n<img alt="CHANGELOG" draggable="false" src="http://localhost:3000/gitlab-org/gitlab-test/raw/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG" />\n</div>\n\n<div class="controls">\n<div class="transparent"></div>\n<div class="drag-track">\n<div class="dragger" style="left: 0px;"></div>\n</div>\n<div class="opaque"></div>\n</div>\n</div>\n</div>\n</div>\n<div class="view-modes hide">\n<ul class="view-modes-menu">\n<li class="two-up" data-mode="two-up">2-up</li>\n<li class="swipe" data-mode="swipe">Swipe</li>\n<li class="onion-skin" data-mode="onion-skin">Onion skin</li>\n</ul>\n</div>\n',
};
+
+export const imageDiffDiscussions = [
+ {
+ id: '1',
+ position: {
+ x: 10,
+ y: 10,
+ width: 100,
+ height: 200,
+ },
+ },
+ {
+ id: '2',
+ position: {
+ x: 5,
+ y: 5,
+ width: 100,
+ height: 200,
+ },
+ },
+];
diff --git a/spec/javascripts/diffs/mock_data/diff_file.js b/spec/javascripts/diffs/mock_data/diff_file.js
index d3bf9525924..031c9842f2f 100644
--- a/spec/javascripts/diffs/mock_data/diff_file.js
+++ b/spec/javascripts/diffs/mock_data/diff_file.js
@@ -1,119 +1,130 @@
export default {
submodule: false,
- submoduleLink: null,
+ submodule_link: null,
blob: {
id: '9e10516ca50788acf18c518a231914a21e5f16f7',
path: 'CHANGELOG',
name: 'CHANGELOG',
mode: '100644',
- readableText: true,
+ readable_text: true,
icon: 'file-text-o',
},
- blobPath: 'CHANGELOG',
- blobName: 'CHANGELOG',
- blobIcon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>',
- fileHash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a',
- filePath: 'CHANGELOG',
- newFile: false,
- deletedFile: false,
- renamedFile: false,
- oldPath: 'CHANGELOG',
- newPath: 'CHANGELOG',
- modeChanged: false,
- aMode: '100644',
- bMode: '100644',
+ blob_path: 'CHANGELOG',
+ blob_name: 'CHANGELOG',
+ blob_icon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>',
+ file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a',
+ file_path: 'CHANGELOG',
+ new_file: false,
+ deleted_file: false,
+ renamed_file: false,
+ old_path: 'CHANGELOG',
+ new_path: 'CHANGELOG',
+ mode_changed: false,
+ a_mode: '100644',
+ b_mode: '100644',
text: true,
- addedLines: 2,
- removedLines: 0,
- diffRefs: {
- baseSha: 'e63f41fe459e62e1228fcef60d7189127aeba95a',
- startSha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962',
- headSha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
+ viewer: {
+ name: 'text',
},
- contentSha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
- storedExternally: null,
- externalStorage: null,
- oldPathHtml: ['CHANGELOG', 'CHANGELOG'],
- newPathHtml: 'CHANGELOG',
- editPath: '/gitlab-org/gitlab-test/edit/spooky-stuff/CHANGELOG',
- viewPath: '/gitlab-org/gitlab-test/blob/spooky-stuff/CHANGELOG',
- replacedViewPath: null,
+ added_lines: 2,
+ removed_lines: 0,
+ diff_refs: {
+ base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a',
+ start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962',
+ head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
+ },
+ content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
+ stored_externally: null,
+ external_storage: null,
+ old_path_html: 'CHANGELOG',
+ new_path_html: 'CHANGELOG',
+ edit_path: '/gitlab-org/gitlab-test/edit/spooky-stuff/CHANGELOG',
+ view_path: '/gitlab-org/gitlab-test/blob/spooky-stuff/CHANGELOG',
+ replaced_view_path: null,
collapsed: false,
- tooLarge: false,
- contextLinesPath:
+ renderIt: false,
+ too_large: false,
+ context_lines_path:
'/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff',
- highlightedDiffLines: [
+ highlighted_diff_lines: [
{
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1',
type: 'new',
- oldLine: null,
- newLine: 1,
+ old_line: null,
+ new_line: 1,
+ discussions: [],
text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
- richText: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
- metaData: null,
+ rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
+ meta_data: null,
},
{
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
type: 'new',
- oldLine: null,
- newLine: 2,
+ old_line: null,
+ new_line: 2,
+ discussions: [],
text: '+<span id="LC2" class="line" lang="plaintext"></span>\n',
- richText: '+<span id="LC2" class="line" lang="plaintext"></span>\n',
- metaData: null,
+ rich_text: '+<span id="LC2" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
},
{
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
type: null,
- oldLine: 1,
- newLine: 3,
+ old_line: 1,
+ new_line: 3,
+ discussions: [],
text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
- richText: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
- metaData: null,
+ rich_text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ meta_data: null,
},
{
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
type: null,
- oldLine: 2,
- newLine: 4,
+ old_line: 2,
+ new_line: 4,
+ discussions: [],
text: ' <span id="LC4" class="line" lang="plaintext"></span>\n',
- richText: ' <span id="LC4" class="line" lang="plaintext"></span>\n',
- metaData: null,
+ rich_text: ' <span id="LC4" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
},
{
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
type: null,
- oldLine: 3,
- newLine: 5,
+ old_line: 3,
+ new_line: 5,
+ discussions: [],
text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
- richText: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
- metaData: null,
+ rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ meta_data: null,
},
{
- lineCode: null,
+ line_code: null,
type: 'match',
- oldLine: null,
- newLine: null,
+ old_line: null,
+ new_line: null,
+ discussions: [],
text: '',
- richText: '',
- metaData: {
- oldPos: 3,
- newPos: 5,
+ rich_text: '',
+ meta_data: {
+ old_pos: 3,
+ new_pos: 5,
},
},
],
- parallelDiffLines: [
+ parallel_diff_lines: [
{
left: {
type: 'empty-cell',
},
right: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1',
type: 'new',
- oldLine: null,
- newLine: 1,
+ old_line: null,
+ new_line: 1,
+ discussions: [],
text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
- richText: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
- metaData: null,
+ rich_text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
+ meta_data: null,
},
},
{
@@ -121,100 +132,110 @@ export default {
type: 'empty-cell',
},
right: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
type: 'new',
- oldLine: null,
- newLine: 2,
+ old_line: null,
+ new_line: 2,
+ discussions: [],
text: '+<span id="LC2" class="line" lang="plaintext"></span>\n',
- richText: '<span id="LC2" class="line" lang="plaintext"></span>\n',
- metaData: null,
+ rich_text: '<span id="LC2" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
},
},
{
left: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
+ line_Code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
type: null,
- oldLine: 1,
- newLine: 3,
+ old_line: 1,
+ new_line: 3,
+ discussions: [],
text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
- richText: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
- metaData: null,
+ rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ meta_data: null,
},
right: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
type: null,
- oldLine: 1,
- newLine: 3,
+ old_line: 1,
+ new_line: 3,
+ discussions: [],
text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
- richText: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
- metaData: null,
+ rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ meta_data: null,
},
},
{
left: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
type: null,
- oldLine: 2,
- newLine: 4,
+ old_line: 2,
+ new_line: 4,
+ discussions: [],
text: ' <span id="LC4" class="line" lang="plaintext"></span>\n',
- richText: '<span id="LC4" class="line" lang="plaintext"></span>\n',
- metaData: null,
+ rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
},
right: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
type: null,
- oldLine: 2,
- newLine: 4,
+ old_line: 2,
+ new_line: 4,
+ discussions: [],
text: ' <span id="LC4" class="line" lang="plaintext"></span>\n',
- richText: '<span id="LC4" class="line" lang="plaintext"></span>\n',
- metaData: null,
+ rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
},
},
{
left: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
type: null,
- oldLine: 3,
- newLine: 5,
+ old_line: 3,
+ new_line: 5,
+ discussions: [],
text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
- richText: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
- metaData: null,
+ rich_text: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ meta_data: null,
},
right: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
type: null,
- oldLine: 3,
- newLine: 5,
+ old_line: 3,
+ new_line: 5,
+ discussions: [],
text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
- richText: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
- metaData: null,
+ rich_text: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ meta_data: null,
},
},
{
left: {
- lineCode: null,
+ line_code: null,
type: 'match',
- oldLine: null,
- newLine: null,
+ old_line: null,
+ new_line: null,
+ discussions: [],
text: '',
- richText: '',
- metaData: {
- oldPos: 3,
- newPos: 5,
+ rich_text: '',
+ meta_data: {
+ old_pos: 3,
+ new_pos: 5,
},
},
right: {
- lineCode: null,
+ line_code: null,
type: 'match',
- oldLine: null,
- newLine: null,
+ old_line: null,
+ new_line: null,
+ discussions: [],
text: '',
- richText: '',
- metaData: {
- oldPos: 3,
- newPos: 5,
+ rich_text: '',
+ meta_data: {
+ old_pos: 3,
+ new_pos: 5,
},
},
},
],
+ discussions: [],
};
diff --git a/spec/javascripts/diffs/mock_data/diff_with_commit.js b/spec/javascripts/diffs/mock_data/diff_with_commit.js
new file mode 100644
index 00000000000..d646294ee84
--- /dev/null
+++ b/spec/javascripts/diffs/mock_data/diff_with_commit.js
@@ -0,0 +1,7 @@
+const FIXTURE = 'merge_request_diffs/with_commit.json';
+
+preloadFixtures(FIXTURE);
+
+export default function getDiffWithCommit() {
+ return getJSONFixture(FIXTURE);
+}
diff --git a/spec/javascripts/diffs/mock_data/merge_request_diffs.js b/spec/javascripts/diffs/mock_data/merge_request_diffs.js
new file mode 100644
index 00000000000..d72ad7818dd
--- /dev/null
+++ b/spec/javascripts/diffs/mock_data/merge_request_diffs.js
@@ -0,0 +1,42 @@
+export default [
+ {
+ versionIndex: 4,
+ createdAt: '2018-10-23T11:49:16.611Z',
+ commitsCount: 4,
+ latest: true,
+ shortCommitSha: 'de7a8f7f',
+ versionPath: '/gnuwget/wget2/merge_requests/6/diffs?diff_id=37',
+ comparePath:
+ '/gnuwget/wget2/merge_requests/6/diffs?diff_id=37&start_sha=de7a8f7f20c3ea2e0bef3ba01cfd41c21f6b4995',
+ },
+ {
+ versionIndex: 3,
+ createdAt: '2018-10-23T11:46:40.617Z',
+ commitsCount: 3,
+ latest: false,
+ shortCommitSha: 'e78fc18f',
+ versionPath: '/gnuwget/wget2/merge_requests/6/diffs?diff_id=36',
+ comparePath:
+ '/gnuwget/wget2/merge_requests/6/diffs?diff_id=37&start_sha=e78fc18fa37acb2185c59ca94d4a964464feb50e',
+ },
+ {
+ versionIndex: 2,
+ createdAt: '2018-10-04T09:57:39.648Z',
+ commitsCount: 2,
+ latest: false,
+ shortCommitSha: '48da7e7e',
+ versionPath: '/gnuwget/wget2/merge_requests/6/diffs?diff_id=35',
+ comparePath:
+ '/gnuwget/wget2/merge_requests/6/diffs?diff_id=37&start_sha=48da7e7e9a99d41c852578bd9cb541ca4d864b3e',
+ },
+ {
+ versionIndex: 1,
+ createdAt: '2018-09-25T20:30:39.493Z',
+ commitsCount: 1,
+ latest: false,
+ shortCommitSha: '47bac2ed',
+ versionPath: '/gnuwget/wget2/merge_requests/6/diffs?diff_id=20',
+ comparePath:
+ '/gnuwget/wget2/merge_requests/6/diffs?diff_id=37&start_sha=47bac2ed972c5bee344c1cea159a22cd7f711dc0',
+ },
+];
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index c1560dac1a0..acd95a3dd8b 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -5,19 +5,58 @@ import {
INLINE_DIFF_VIEW_TYPE,
PARALLEL_DIFF_VIEW_TYPE,
} from '~/diffs/constants';
-import * as actions from '~/diffs/store/actions';
+import actions, {
+ setBaseConfig,
+ fetchDiffFiles,
+ assignDiscussionsToDiff,
+ removeDiscussionsFromDiff,
+ startRenderDiffsQueue,
+ setInlineDiffViewType,
+ setParallelDiffViewType,
+ showCommentForm,
+ cancelCommentForm,
+ loadMoreLines,
+ scrollToLineIfNeededInline,
+ scrollToLineIfNeededParallel,
+ loadCollapsedDiff,
+ expandAllFiles,
+ toggleFileDiscussions,
+ saveDiffDiscussion,
+ toggleTreeOpen,
+ scrollToFile,
+ toggleShowTreeList,
+} from '~/diffs/store/actions';
import * as types from '~/diffs/store/mutation_types';
import axios from '~/lib/utils/axios_utils';
import testAction from '../../helpers/vuex_action_helper';
describe('DiffsStoreActions', () => {
+ const originalMethods = {
+ requestAnimationFrame: global.requestAnimationFrame,
+ requestIdleCallback: global.requestIdleCallback,
+ };
+
+ beforeEach(() => {
+ ['requestAnimationFrame', 'requestIdleCallback'].forEach(method => {
+ global[method] = cb => {
+ cb();
+ };
+ });
+ });
+
+ afterEach(() => {
+ ['requestAnimationFrame', 'requestIdleCallback'].forEach(method => {
+ global[method] = originalMethods[method];
+ });
+ });
+
describe('setBaseConfig', () => {
it('should set given endpoint and project path', done => {
const endpoint = '/diffs/set/endpoint';
const projectPath = '/root/project';
testAction(
- actions.setBaseConfig,
+ setBaseConfig,
{ endpoint, projectPath },
{ endpoint: '', projectPath: '' },
[{ type: types.SET_BASE_CONFIG, payload: { endpoint, projectPath } }],
@@ -35,7 +74,7 @@ describe('DiffsStoreActions', () => {
mock.onGet(endpoint).reply(200, res);
testAction(
- actions.fetchDiffFiles,
+ fetchDiffFiles,
{},
{ endpoint },
[
@@ -53,10 +92,190 @@ describe('DiffsStoreActions', () => {
});
});
+ describe('assignDiscussionsToDiff', () => {
+ it('should merge discussions into diffs', done => {
+ const state = {
+ diffFiles: [
+ {
+ file_hash: 'ABC',
+ parallel_diff_lines: [
+ {
+ left: {
+ line_code: 'ABC_1_1',
+ discussions: [],
+ },
+ right: {
+ line_code: 'ABC_1_1',
+ discussions: [],
+ },
+ },
+ ],
+ highlighted_diff_lines: [
+ {
+ line_code: 'ABC_1_1',
+ discussions: [],
+ old_line: 5,
+ new_line: null,
+ },
+ ],
+ diff_refs: {
+ base_sha: 'abc',
+ head_sha: 'def',
+ start_sha: 'ghi',
+ },
+ new_path: 'file1',
+ old_path: 'file2',
+ },
+ ],
+ };
+
+ const diffPosition = {
+ base_sha: 'abc',
+ head_sha: 'def',
+ start_sha: 'ghi',
+ new_line: null,
+ new_path: 'file1',
+ old_line: 5,
+ old_path: 'file2',
+ };
+
+ const singleDiscussion = {
+ line_code: 'ABC_1_1',
+ diff_discussion: {},
+ diff_file: {
+ file_hash: 'ABC',
+ },
+ file_hash: 'ABC',
+ resolvable: true,
+ position: diffPosition,
+ original_position: diffPosition,
+ };
+
+ const discussions = [singleDiscussion];
+
+ testAction(
+ assignDiscussionsToDiff,
+ discussions,
+ state,
+ [
+ {
+ type: types.SET_LINE_DISCUSSIONS_FOR_FILE,
+ payload: {
+ discussion: singleDiscussion,
+ diffPositionByLineCode: {
+ ABC_1_1: {
+ base_sha: 'abc',
+ head_sha: 'def',
+ start_sha: 'ghi',
+ new_line: null,
+ new_path: 'file1',
+ old_line: 5,
+ old_path: 'file2',
+ line_code: 'ABC_1_1',
+ position_type: 'text',
+ },
+ },
+ },
+ },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('removeDiscussionsFromDiff', () => {
+ it('should remove discussions from diffs', done => {
+ const state = {
+ diffFiles: [
+ {
+ file_hash: 'ABC',
+ parallel_diff_lines: [
+ {
+ left: {
+ line_code: 'ABC_1_1',
+ discussions: [
+ {
+ id: 1,
+ },
+ ],
+ },
+ right: {
+ line_code: 'ABC_1_1',
+ discussions: [],
+ },
+ },
+ ],
+ highlighted_diff_lines: [
+ {
+ line_code: 'ABC_1_1',
+ discussions: [],
+ },
+ ],
+ },
+ ],
+ };
+ const singleDiscussion = {
+ id: '1',
+ file_hash: 'ABC',
+ line_code: 'ABC_1_1',
+ };
+
+ testAction(
+ removeDiscussionsFromDiff,
+ singleDiscussion,
+ state,
+ [
+ {
+ type: types.REMOVE_LINE_DISCUSSIONS_FOR_FILE,
+ payload: {
+ id: '1',
+ fileHash: 'ABC',
+ lineCode: 'ABC_1_1',
+ },
+ },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('startRenderDiffsQueue', () => {
+ it('should set all files to RENDER_FILE', () => {
+ const state = {
+ diffFiles: [
+ {
+ id: 1,
+ renderIt: false,
+ collapsed: false,
+ },
+ {
+ id: 2,
+ renderIt: false,
+ collapsed: false,
+ },
+ ],
+ };
+
+ const pseudoCommit = (commitType, file) => {
+ expect(commitType).toBe(types.RENDER_FILE);
+ Object.assign(file, {
+ renderIt: true,
+ });
+ };
+
+ startRenderDiffsQueue({ state, commit: pseudoCommit });
+
+ expect(state.diffFiles[0].renderIt).toBe(true);
+ expect(state.diffFiles[1].renderIt).toBe(true);
+ });
+ });
+
describe('setInlineDiffViewType', () => {
it('should set diff view type to inline and also set the cookie properly', done => {
testAction(
- actions.setInlineDiffViewType,
+ setInlineDiffViewType,
null,
{},
[{ type: types.SET_DIFF_VIEW_TYPE, payload: INLINE_DIFF_VIEW_TYPE }],
@@ -74,7 +293,7 @@ describe('DiffsStoreActions', () => {
describe('setParallelDiffViewType', () => {
it('should set diff view type to parallel and also set the cookie properly', done => {
testAction(
- actions.setParallelDiffViewType,
+ setParallelDiffViewType,
null,
{},
[{ type: types.SET_DIFF_VIEW_TYPE, payload: PARALLEL_DIFF_VIEW_TYPE }],
@@ -94,7 +313,7 @@ describe('DiffsStoreActions', () => {
const payload = { lineCode: 'lineCode' };
testAction(
- actions.showCommentForm,
+ showCommentForm,
payload,
{},
[{ type: types.ADD_COMMENT_FORM_LINE, payload }],
@@ -109,7 +328,7 @@ describe('DiffsStoreActions', () => {
const payload = { lineCode: 'lineCode' };
testAction(
- actions.cancelCommentForm,
+ cancelCommentForm,
payload,
{},
[{ type: types.REMOVE_COMMENT_FORM_LINE, payload }],
@@ -131,7 +350,7 @@ describe('DiffsStoreActions', () => {
mock.onGet(endpoint).reply(200, contextLines);
testAction(
- actions.loadMoreLines,
+ loadMoreLines,
options,
{},
[
@@ -157,7 +376,7 @@ describe('DiffsStoreActions', () => {
mock.onGet(file.loadCollapsedDiffUrl).reply(200, data);
testAction(
- actions.loadCollapsedDiff,
+ loadCollapsedDiff,
file,
{},
[
@@ -178,7 +397,7 @@ describe('DiffsStoreActions', () => {
describe('expandAllFiles', () => {
it('should change the collapsed prop from the diffFiles', done => {
testAction(
- actions.expandAllFiles,
+ expandAllFiles,
null,
{},
[
@@ -197,42 +416,275 @@ describe('DiffsStoreActions', () => {
const getters = {
getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ id: 1 }]),
diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(true),
- diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(false),
+ diffHasAllCollapsedDiscussions: jasmine.createSpy().and.returnValue(false),
};
const dispatch = jasmine.createSpy('dispatch');
- actions.toggleFileDiscussions({ getters, dispatch });
+ toggleFileDiscussions({ getters, dispatch });
- expect(dispatch).toHaveBeenCalledWith('collapseDiscussion', { discussionId: 1 }, { root: true });
+ expect(dispatch).toHaveBeenCalledWith(
+ 'collapseDiscussion',
+ { discussionId: 1 },
+ { root: true },
+ );
});
it('should dispatch expandDiscussion when all discussions are collapsed', () => {
const getters = {
getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ id: 1 }]),
diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(false),
- diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(true),
+ diffHasAllCollapsedDiscussions: jasmine.createSpy().and.returnValue(true),
};
const dispatch = jasmine.createSpy();
- actions.toggleFileDiscussions({ getters, dispatch });
+ toggleFileDiscussions({ getters, dispatch });
- expect(dispatch).toHaveBeenCalledWith('expandDiscussion', { discussionId: 1 }, { root: true });
+ expect(dispatch).toHaveBeenCalledWith(
+ 'expandDiscussion',
+ { discussionId: 1 },
+ { root: true },
+ );
});
it('should dispatch expandDiscussion when some discussions are collapsed and others are expanded for the collapsed discussion', () => {
const getters = {
getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ expanded: false, id: 1 }]),
diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(false),
- diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(false),
+ diffHasAllCollapsedDiscussions: jasmine.createSpy().and.returnValue(false),
};
const dispatch = jasmine.createSpy();
- actions.toggleFileDiscussions({ getters, dispatch });
+ toggleFileDiscussions({ getters, dispatch });
+
+ expect(dispatch).toHaveBeenCalledWith(
+ 'expandDiscussion',
+ { discussionId: 1 },
+ { root: true },
+ );
+ });
+ });
+
+ describe('scrollToLineIfNeededInline', () => {
+ const lineMock = {
+ lineCode: 'ABC_123',
+ };
+
+ it('should not call handleLocationHash when there is not hash', () => {
+ window.location.hash = '';
+
+ const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
+
+ scrollToLineIfNeededInline({}, lineMock);
+
+ expect(handleLocationHashSpy).not.toHaveBeenCalled();
+ });
+
+ it('should not call handleLocationHash when the hash does not match any line', () => {
+ window.location.hash = 'XYZ_456';
+
+ const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
+
+ scrollToLineIfNeededInline({}, lineMock);
+
+ expect(handleLocationHashSpy).not.toHaveBeenCalled();
+ });
+
+ it('should call handleLocationHash only when the hash matches a line', () => {
+ window.location.hash = 'ABC_123';
+
+ const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
+
+ scrollToLineIfNeededInline(
+ {},
+ {
+ lineCode: 'ABC_456',
+ },
+ );
+ scrollToLineIfNeededInline({}, lineMock);
+ scrollToLineIfNeededInline(
+ {},
+ {
+ lineCode: 'XYZ_456',
+ },
+ );
+
+ expect(handleLocationHashSpy).toHaveBeenCalled();
+ expect(handleLocationHashSpy).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('scrollToLineIfNeededParallel', () => {
+ const lineMock = {
+ left: null,
+ right: {
+ lineCode: 'ABC_123',
+ },
+ };
+
+ it('should not call handleLocationHash when there is not hash', () => {
+ window.location.hash = '';
+
+ const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
+
+ scrollToLineIfNeededParallel({}, lineMock);
+
+ expect(handleLocationHashSpy).not.toHaveBeenCalled();
+ });
+
+ it('should not call handleLocationHash when the hash does not match any line', () => {
+ window.location.hash = 'XYZ_456';
+
+ const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
+
+ scrollToLineIfNeededParallel({}, lineMock);
+
+ expect(handleLocationHashSpy).not.toHaveBeenCalled();
+ });
+
+ it('should call handleLocationHash only when the hash matches a line', () => {
+ window.location.hash = 'ABC_123';
+
+ const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
+
+ scrollToLineIfNeededParallel(
+ {},
+ {
+ left: null,
+ right: {
+ lineCode: 'ABC_456',
+ },
+ },
+ );
+ scrollToLineIfNeededParallel({}, lineMock);
+ scrollToLineIfNeededParallel(
+ {},
+ {
+ left: null,
+ right: {
+ lineCode: 'XYZ_456',
+ },
+ },
+ );
+
+ expect(handleLocationHashSpy).toHaveBeenCalled();
+ expect(handleLocationHashSpy).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('saveDiffDiscussion', () => {
+ beforeEach(() => {
+ spyOnDependency(actions, 'getNoteFormData').and.returnValue('testData');
+ });
+
+ it('dispatches actions', done => {
+ const dispatch = jasmine.createSpy('dispatch').and.callFake(name => {
+ switch (name) {
+ case 'saveNote':
+ return Promise.resolve({
+ discussion: 'test',
+ });
+ case 'updateDiscussion':
+ return Promise.resolve('discussion');
+ default:
+ return Promise.resolve({});
+ }
+ });
+
+ saveDiffDiscussion({ dispatch }, { note: {}, formData: {} })
+ .then(() => {
+ expect(dispatch.calls.argsFor(0)).toEqual(['saveNote', 'testData', { root: true }]);
+ expect(dispatch.calls.argsFor(1)).toEqual(['updateDiscussion', 'test', { root: true }]);
+ expect(dispatch.calls.argsFor(2)).toEqual(['assignDiscussionsToDiff', ['discussion']]);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('toggleTreeOpen', () => {
+ it('commits TOGGLE_FOLDER_OPEN', done => {
+ testAction(
+ toggleTreeOpen,
+ 'path',
+ {},
+ [{ type: types.TOGGLE_FOLDER_OPEN, payload: 'path' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('scrollToFile', () => {
+ let commit;
+
+ beforeEach(() => {
+ commit = jasmine.createSpy();
+ jasmine.clock().install();
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
+ it('updates location hash', () => {
+ const state = {
+ treeEntries: {
+ path: {
+ fileHash: 'test',
+ },
+ },
+ };
+
+ scrollToFile({ state, commit }, 'path');
+
+ expect(document.location.hash).toBe('#test');
+ });
+
+ it('commits UPDATE_CURRENT_DIFF_FILE_ID', () => {
+ const state = {
+ treeEntries: {
+ path: {
+ fileHash: 'test',
+ },
+ },
+ };
+
+ scrollToFile({ state, commit }, 'path');
+
+ expect(commit).toHaveBeenCalledWith(types.UPDATE_CURRENT_DIFF_FILE_ID, 'test');
+ });
+
+ it('resets currentDiffId after timeout', () => {
+ const state = {
+ treeEntries: {
+ path: {
+ fileHash: 'test',
+ },
+ },
+ };
+
+ scrollToFile({ state, commit }, 'path');
+
+ jasmine.clock().tick(1000);
+
+ expect(commit.calls.argsFor(1)).toEqual([types.UPDATE_CURRENT_DIFF_FILE_ID, '']);
+ });
+ });
+
+ describe('toggleShowTreeList', () => {
+ it('commits toggle', done => {
+ testAction(toggleShowTreeList, null, {}, [{ type: types.TOGGLE_SHOW_TREE_LIST }], [], done);
+ });
+
+ it('updates localStorage', () => {
+ spyOn(localStorage, 'setItem');
+
+ toggleShowTreeList({ commit() {}, state: { showTreeList: true } });
- expect(dispatch).toHaveBeenCalledWith('expandDiscussion', { discussionId: 1 }, { root: true });
+ expect(localStorage.setItem).toHaveBeenCalledWith('mr_tree_show', true);
});
});
});
diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js
index a59b26b2634..eef95c823fb 100644
--- a/spec/javascripts/diffs/store/getters_spec.js
+++ b/spec/javascripts/diffs/store/getters_spec.js
@@ -49,15 +49,17 @@ describe('Diffs Module Getters', () => {
});
});
- describe('areAllFilesCollapsed', () => {
+ describe('hasCollapsedFile', () => {
it('returns true when all files are collapsed', () => {
localState.diffFiles = [{ collapsed: true }, { collapsed: true }];
- expect(getters.areAllFilesCollapsed(localState)).toEqual(true);
+
+ expect(getters.hasCollapsedFile(localState)).toEqual(true);
});
- it('returns false when at least one file is not collapsed', () => {
+ it('returns true when at least one file is collapsed', () => {
localState.diffFiles = [{ collapsed: false }, { collapsed: true }];
- expect(getters.areAllFilesCollapsed(localState)).toEqual(false);
+
+ expect(getters.hasCollapsedFile(localState)).toEqual(true);
});
});
@@ -104,13 +106,13 @@ describe('Diffs Module Getters', () => {
});
});
- describe('diffHasAllCollpasedDiscussions', () => {
+ describe('diffHasAllCollapsedDiscussions', () => {
it('returns true when all discussions are collapsed', () => {
discussionMock.diff_file.file_hash = diffFileMock.fileHash;
discussionMock.expanded = false;
expect(
- getters.diffHasAllCollpasedDiscussions(localState, {
+ getters.diffHasAllCollapsedDiscussions(localState, {
getDiffFileDiscussions: () => [discussionMock],
})(diffFileMock),
).toEqual(true);
@@ -118,7 +120,7 @@ describe('Diffs Module Getters', () => {
it('returns false when there are no discussions', () => {
expect(
- getters.diffHasAllCollpasedDiscussions(localState, {
+ getters.diffHasAllCollapsedDiscussions(localState, {
getDiffFileDiscussions: () => [],
})(diffFileMock),
).toEqual(false);
@@ -128,7 +130,7 @@ describe('Diffs Module Getters', () => {
discussionMock1.expanded = false;
expect(
- getters.diffHasAllCollpasedDiscussions(localState, {
+ getters.diffHasAllCollapsedDiscussions(localState, {
getDiffFileDiscussions: () => [discussionMock, discussionMock1],
})(diffFileMock),
).toEqual(false);
@@ -184,107 +186,80 @@ describe('Diffs Module Getters', () => {
});
});
- describe('singleDiscussionByLineCode', () => {
- it('returns found discussion per line Code', () => {
- const discussionsMock = {};
- discussionsMock.ABC = discussionMock;
-
- expect(
- getters.singleDiscussionByLineCode(localState, {}, null, {
- discussionsByLineCode: () => discussionsMock,
- })('DEF'),
- ).toEqual([]);
- });
-
- it('returns empty array when no discussions match', () => {
- expect(
- getters.singleDiscussionByLineCode(localState, {}, null, {
- discussionsByLineCode: () => {},
- })('DEF'),
- ).toEqual([]);
- });
- });
-
describe('shouldRenderParallelCommentRow', () => {
let line;
beforeEach(() => {
line = {};
+ discussionMock.expanded = true;
+
line.left = {
- lineCode: 'ABC',
+ line_code: 'ABC',
+ discussions: [discussionMock],
};
line.right = {
- lineCode: 'DEF',
+ line_code: 'DEF',
+ discussions: [discussionMock1],
};
});
it('returns true when discussion is expanded', () => {
- discussionMock.expanded = true;
-
- expect(
- getters.shouldRenderParallelCommentRow(localState, {
- singleDiscussionByLineCode: () => [discussionMock],
- })(line),
- ).toEqual(true);
+ expect(getters.shouldRenderParallelCommentRow(localState)(line)).toEqual(true);
});
it('returns false when no discussion was found', () => {
+ line.left.discussions = [];
+ line.right.discussions = [];
+
localState.diffLineCommentForms.ABC = false;
localState.diffLineCommentForms.DEF = false;
- expect(
- getters.shouldRenderParallelCommentRow(localState, {
- singleDiscussionByLineCode: () => [],
- })(line),
- ).toEqual(false);
+ expect(getters.shouldRenderParallelCommentRow(localState)(line)).toEqual(false);
});
it('returns true when discussionForm was found', () => {
localState.diffLineCommentForms.ABC = {};
- expect(
- getters.shouldRenderParallelCommentRow(localState, {
- singleDiscussionByLineCode: () => [discussionMock],
- })(line),
- ).toEqual(true);
+ expect(getters.shouldRenderParallelCommentRow(localState)(line)).toEqual(true);
});
});
describe('shouldRenderInlineCommentRow', () => {
+ let line;
+
+ beforeEach(() => {
+ discussionMock.expanded = true;
+
+ line = {
+ lineCode: 'ABC',
+ discussions: [discussionMock],
+ };
+ });
+
it('returns true when diffLineCommentForms has form', () => {
localState.diffLineCommentForms.ABC = {};
- expect(
- getters.shouldRenderInlineCommentRow(localState)({
- lineCode: 'ABC',
- }),
- ).toEqual(true);
+ expect(getters.shouldRenderInlineCommentRow(localState)(line)).toEqual(true);
});
it('returns false when no line discussions were found', () => {
- expect(
- getters.shouldRenderInlineCommentRow(localState, {
- singleDiscussionByLineCode: () => [],
- })('DEF'),
- ).toEqual(false);
+ line.discussions = [];
+
+ expect(getters.shouldRenderInlineCommentRow(localState)(line)).toEqual(false);
});
it('returns true if all found discussions are expanded', () => {
discussionMock.expanded = true;
- expect(
- getters.shouldRenderInlineCommentRow(localState, {
- singleDiscussionByLineCode: () => [discussionMock],
- })('ABC'),
- ).toEqual(true);
+ expect(getters.shouldRenderInlineCommentRow(localState)(line)).toEqual(true);
});
});
describe('getDiffFileDiscussions', () => {
it('returns an array with discussions when fileHash matches and the discussion belongs to a diff', () => {
- discussionMock.diff_file.file_hash = diffFileMock.fileHash;
+ discussionMock.diff_file.file_hash = diffFileMock.file_hash;
expect(
getters.getDiffFileDiscussions(localState, {}, {}, { discussions: [discussionMock] })(
@@ -304,10 +279,10 @@ describe('Diffs Module Getters', () => {
describe('getDiffFileByHash', () => {
it('returns file by hash', () => {
const fileA = {
- fileHash: '123',
+ file_hash: '123',
};
const fileB = {
- fileHash: '456',
+ file_hash: '456',
};
localState.diffFiles = [fileA, fileB];
@@ -316,7 +291,35 @@ describe('Diffs Module Getters', () => {
it('returns null if no matching file is found', () => {
localState.diffFiles = [];
+
expect(getters.getDiffFileByHash(localState)('123')).toBeUndefined();
});
});
+
+ describe('allBlobs', () => {
+ it('returns an array of blobs', () => {
+ localState.treeEntries = {
+ file: {
+ type: 'blob',
+ },
+ tree: {
+ type: 'tree',
+ },
+ };
+
+ expect(getters.allBlobs(localState)).toEqual([
+ {
+ type: 'blob',
+ },
+ ]);
+ });
+ });
+
+ describe('diffFilesLength', () => {
+ it('returns length of diff files', () => {
+ localState.diffFiles.push('test', 'test 2');
+
+ expect(getters.diffFilesLength(localState)).toBe(2);
+ });
+ });
});
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index 1af49f4985c..598d723c940 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -1,6 +1,8 @@
+import createState from '~/diffs/store/modules/diff_state';
import mutations from '~/diffs/store/mutations';
import * as types from '~/diffs/store/mutation_types';
import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
+import diffFileMockData from '../mock_data/diff_file';
describe('DiffsStoreMutations', () => {
describe('SET_BASE_CONFIG', () => {
@@ -10,6 +12,7 @@ describe('DiffsStoreMutations', () => {
const projectPath = '/root/project';
mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath });
+
expect(state.endpoint).toEqual(endpoint);
expect(state.projectPath).toEqual(projectPath);
});
@@ -20,15 +23,34 @@ describe('DiffsStoreMutations', () => {
const state = {};
mutations[types.SET_LOADING](state, false);
+
expect(state.isLoading).toEqual(false);
});
});
+ describe('SET_DIFF_DATA', () => {
+ it('should set diff data type properly', () => {
+ const state = {};
+ const diffMock = {
+ diff_files: [diffFileMockData],
+ };
+
+ mutations[types.SET_DIFF_DATA](state, diffMock);
+
+ const firstLine = state.diffFiles[0].parallel_diff_lines[0];
+
+ expect(firstLine.right.text).toBeUndefined();
+ expect(state.diffFiles[0].renderIt).toEqual(true);
+ expect(state.diffFiles[0].collapsed).toEqual(false);
+ });
+ });
+
describe('SET_DIFF_VIEW_TYPE', () => {
it('should set diff view type properly', () => {
const state = {};
mutations[types.SET_DIFF_VIEW_TYPE](state, INLINE_DIFF_VIEW_TYPE);
+
expect(state.diffViewType).toEqual(INLINE_DIFF_VIEW_TYPE);
});
});
@@ -39,6 +61,7 @@ describe('DiffsStoreMutations', () => {
const lineCode = 'FDE';
mutations[types.ADD_COMMENT_FORM_LINE](state, { lineCode });
+
expect(state.diffLineCommentForms[lineCode]).toBeTruthy();
});
});
@@ -49,9 +72,11 @@ describe('DiffsStoreMutations', () => {
const lineCode = 'FDE';
mutations[types.ADD_COMMENT_FORM_LINE](state, { lineCode });
+
expect(state.diffLineCommentForms[lineCode]).toBeTruthy();
mutations[types.REMOVE_COMMENT_FORM_LINE](state, { lineCode });
+
expect(state.diffLineCommentForms[lineCode]).toBeUndefined();
});
});
@@ -64,6 +89,7 @@ describe('DiffsStoreMutations', () => {
const state = { expandAllFiles: true, diffFiles: [diffFile] };
mutations[types.EXPAND_ALL_FILES](state);
+
expect(state.diffFiles[0].collapsed).toEqual(false);
});
});
@@ -72,19 +98,19 @@ describe('DiffsStoreMutations', () => {
it('should call utils.addContextLines with proper params', () => {
const options = {
lineNumbers: { oldLineNumber: 1, newLineNumber: 2 },
- contextLines: [{ oldLine: 1 }],
+ contextLines: [{ old_line: 1, new_line: 1, line_code: 'ff9200_1_1', discussions: [] }],
fileHash: 'ff9200',
params: {
bottom: true,
},
};
const diffFile = {
- fileHash: options.fileHash,
- highlightedDiffLines: [],
- parallelDiffLines: [],
+ file_hash: options.fileHash,
+ highlighted_diff_lines: [],
+ parallel_diff_lines: [],
};
const state = { diffFiles: [diffFile] };
- const lines = [{ oldLine: 1 }];
+ const lines = [{ old_line: 1, new_line: 1 }];
const findDiffFileSpy = spyOnDependency(mutations, 'findDiffFile').and.returnValue(diffFile);
const removeMatchLineSpy = spyOnDependency(mutations, 'removeMatchLine');
@@ -99,14 +125,16 @@ describe('DiffsStoreMutations', () => {
options.lineNumbers,
options.params.bottom,
);
+
expect(lineRefSpy).toHaveBeenCalledWith(
options.contextLines,
options.lineNumbers,
options.params.bottom,
);
+
expect(addContextLinesSpy).toHaveBeenCalledWith({
- inlineLines: diffFile.highlightedDiffLines,
- parallelLines: diffFile.parallelDiffLines,
+ inlineLines: diffFile.highlighted_diff_lines,
+ parallelLines: diffFile.parallel_diff_lines,
contextLines: options.contextLines,
bottom: options.params.bottom,
lineNumbers: options.lineNumbers,
@@ -116,19 +144,243 @@ describe('DiffsStoreMutations', () => {
describe('ADD_COLLAPSED_DIFFS', () => {
it('should update the state with the given data for the given file hash', () => {
- const spy = spyOnDependency(mutations, 'convertObjectPropsToCamelCase').and.callThrough();
-
const fileHash = 123;
- const state = { diffFiles: [{}, { fileHash, existingField: 0 }] };
- const file = { fileHash };
- const data = { diff_files: [{ file_hash: fileHash, extra_field: 1, existingField: 1 }] };
+ const state = { diffFiles: [{}, { file_hash: fileHash, existing_field: 0 }] };
+ const data = { diff_files: [{ file_hash: fileHash, extra_field: 1, existing_field: 1 }] };
+
+ mutations[types.ADD_COLLAPSED_DIFFS](state, { file: state.diffFiles[1], data });
+
+ expect(state.diffFiles[1].file_hash).toEqual(fileHash);
+ expect(state.diffFiles[1].existing_field).toEqual(1);
+ expect(state.diffFiles[1].extra_field).toEqual(1);
+ });
+ });
+
+ describe('SET_LINE_DISCUSSIONS_FOR_FILE', () => {
+ it('should add discussions to the given line', () => {
+ const diffPosition = {
+ base_sha: 'ed13df29948c41ba367caa757ab3ec4892509910',
+ head_sha: 'b921914f9a834ac47e6fd9420f78db0f83559130',
+ new_line: null,
+ new_path: '500-lines-4.txt',
+ old_line: 5,
+ old_path: '500-lines-4.txt',
+ start_sha: 'ed13df29948c41ba367caa757ab3ec4892509910',
+ };
+
+ const state = {
+ latestDiff: true,
+ diffFiles: [
+ {
+ file_hash: 'ABC',
+ parallel_diff_lines: [
+ {
+ left: {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ right: {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ },
+ ],
+ highlighted_diff_lines: [
+ {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ ],
+ },
+ ],
+ };
+ const discussion = {
+ id: 1,
+ line_code: 'ABC_1',
+ diff_discussion: true,
+ resolvable: true,
+ original_position: diffPosition,
+ position: diffPosition,
+ diff_file: {
+ file_hash: state.diffFiles[0].file_hash,
+ },
+ };
+
+ const diffPositionByLineCode = {
+ ABC_1: diffPosition,
+ };
+
+ mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, {
+ discussion,
+ diffPositionByLineCode,
+ });
+
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(1);
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toEqual(1);
+ expect(state.diffFiles[0].parallel_diff_lines[0].right.discussions).toEqual([]);
+
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(1);
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
+ });
+
+ it('should add legacy discussions to the given line', () => {
+ const diffPosition = {
+ base_sha: 'ed13df29948c41ba367caa757ab3ec4892509910',
+ head_sha: 'b921914f9a834ac47e6fd9420f78db0f83559130',
+ new_line: null,
+ new_path: '500-lines-4.txt',
+ old_line: 5,
+ old_path: '500-lines-4.txt',
+ start_sha: 'ed13df29948c41ba367caa757ab3ec4892509910',
+ line_code: 'ABC_1',
+ };
+
+ const state = {
+ latestDiff: true,
+ diffFiles: [
+ {
+ file_hash: 'ABC',
+ parallel_diff_lines: [
+ {
+ left: {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ right: {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ },
+ ],
+ highlighted_diff_lines: [
+ {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ ],
+ },
+ ],
+ };
+ const discussion = {
+ id: 1,
+ line_code: 'ABC_1',
+ diff_discussion: true,
+ active: true,
+ diff_file: {
+ file_hash: state.diffFiles[0].file_hash,
+ },
+ };
+
+ const diffPositionByLineCode = {
+ ABC_1: diffPosition,
+ };
+
+ mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, {
+ discussion,
+ diffPositionByLineCode,
+ });
+
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(1);
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toEqual(1);
+
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(1);
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
+ });
+ });
+
+ describe('REMOVE_LINE_DISCUSSIONS', () => {
+ it('should remove the existing discussions on the given line', () => {
+ const state = {
+ diffFiles: [
+ {
+ file_hash: 'ABC',
+ parallel_diff_lines: [
+ {
+ left: {
+ line_code: 'ABC_1',
+ discussions: [
+ {
+ id: 1,
+ line_code: 'ABC_1',
+ },
+ {
+ id: 2,
+ line_code: 'ABC_1',
+ },
+ ],
+ },
+ right: {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ },
+ ],
+ highlighted_diff_lines: [
+ {
+ line_code: 'ABC_1',
+ discussions: [
+ {
+ id: 1,
+ line_code: 'ABC_1',
+ },
+ {
+ id: 2,
+ line_code: 'ABC_1',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ };
+
+ mutations[types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, {
+ fileHash: 'ABC',
+ lineCode: 'ABC_1',
+ });
+
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(0);
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(0);
+ });
+ });
+
+ describe('TOGGLE_FOLDER_OPEN', () => {
+ it('toggles entry opened prop', () => {
+ const state = {
+ treeEntries: {
+ path: {
+ opened: false,
+ },
+ },
+ };
+
+ mutations[types.TOGGLE_FOLDER_OPEN](state, 'path');
+
+ expect(state.treeEntries.path.opened).toBe(true);
+ });
+ });
+
+ describe('TOGGLE_SHOW_TREE_LIST', () => {
+ it('toggles showTreeList', () => {
+ const state = createState();
+
+ mutations[types.TOGGLE_SHOW_TREE_LIST](state);
+
+ expect(state.showTreeList).toBe(false, 'Failed to toggle showTreeList to false');
+
+ mutations[types.TOGGLE_SHOW_TREE_LIST](state);
+
+ expect(state.showTreeList).toBe(true, 'Failed to toggle showTreeList to true');
+ });
+ });
+
+ describe('UPDATE_CURRENT_DIFF_FILE_ID', () => {
+ it('updates currentDiffFileId', () => {
+ const state = createState();
- mutations[types.ADD_COLLAPSED_DIFFS](state, { file, data });
- expect(spy).toHaveBeenCalledWith(data, { deep: true });
+ mutations[types.UPDATE_CURRENT_DIFF_FILE_ID](state, 'somefileid');
- expect(state.diffFiles[1].fileHash).toEqual(fileHash);
- expect(state.diffFiles[1].existingField).toEqual(1);
- expect(state.diffFiles[1].extraField).toEqual(1);
+ expect(state.currentDiffFileId).toBe('somefileid');
});
});
});
diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js
index 32136d9ebff..d4ef17c5ef8 100644
--- a/spec/javascripts/diffs/store/utils_spec.js
+++ b/spec/javascripts/diffs/store/utils_spec.js
@@ -3,6 +3,7 @@ import {
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
TEXT_DIFF_POSITION_TYPE,
+ LEGACY_DIFF_NOTE_TYPE,
DIFF_NOTE_TYPE,
NEW_LINE_TYPE,
OLD_LINE_TYPE,
@@ -17,7 +18,7 @@ const getDiffFileMock = () => Object.assign({}, diffFileMockData);
describe('DiffsStoreUtils', () => {
describe('findDiffFile', () => {
- const files = [{ fileHash: 1, name: 'one' }];
+ const files = [{ file_hash: 1, name: 'one' }];
it('should return correct file', () => {
expect(utils.findDiffFile(files, 1).name).toEqual('one');
@@ -40,13 +41,13 @@ describe('DiffsStoreUtils', () => {
describe('findIndexInInlineLines', () => {
it('should return correct index for given line numbers', () => {
- expectSet(utils.findIndexInInlineLines, getDiffFileMock().highlightedDiffLines);
+ expectSet(utils.findIndexInInlineLines, getDiffFileMock().highlighted_diff_lines);
});
});
describe('findIndexInParallelLines', () => {
it('should return correct index for given line numbers', () => {
- expectSet(utils.findIndexInParallelLines, getDiffFileMock().parallelDiffLines, {});
+ expectSet(utils.findIndexInParallelLines, getDiffFileMock().parallel_diff_lines, {});
});
});
});
@@ -55,42 +56,52 @@ describe('DiffsStoreUtils', () => {
it('should remove match line properly by regarding the bottom parameter', () => {
const diffFile = getDiffFileMock();
const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 };
- const inlineIndex = utils.findIndexInInlineLines(diffFile.highlightedDiffLines, lineNumbers);
- const parallelIndex = utils.findIndexInParallelLines(diffFile.parallelDiffLines, lineNumbers);
- const atInlineIndex = diffFile.highlightedDiffLines[inlineIndex];
- const atParallelIndex = diffFile.parallelDiffLines[parallelIndex];
+ const inlineIndex = utils.findIndexInInlineLines(
+ diffFile.highlighted_diff_lines,
+ lineNumbers,
+ );
+ const parallelIndex = utils.findIndexInParallelLines(
+ diffFile.parallel_diff_lines,
+ lineNumbers,
+ );
+ const atInlineIndex = diffFile.highlighted_diff_lines[inlineIndex];
+ const atParallelIndex = diffFile.parallel_diff_lines[parallelIndex];
utils.removeMatchLine(diffFile, lineNumbers, false);
- expect(diffFile.highlightedDiffLines[inlineIndex]).not.toEqual(atInlineIndex);
- expect(diffFile.parallelDiffLines[parallelIndex]).not.toEqual(atParallelIndex);
+
+ expect(diffFile.highlighted_diff_lines[inlineIndex]).not.toEqual(atInlineIndex);
+ expect(diffFile.parallel_diff_lines[parallelIndex]).not.toEqual(atParallelIndex);
utils.removeMatchLine(diffFile, lineNumbers, true);
- expect(diffFile.highlightedDiffLines[inlineIndex + 1]).not.toEqual(atInlineIndex);
- expect(diffFile.parallelDiffLines[parallelIndex + 1]).not.toEqual(atParallelIndex);
+
+ expect(diffFile.highlighted_diff_lines[inlineIndex + 1]).not.toEqual(atInlineIndex);
+ expect(diffFile.parallel_diff_lines[parallelIndex + 1]).not.toEqual(atParallelIndex);
});
});
describe('addContextLines', () => {
it('should add context lines properly with bottom parameter', () => {
const diffFile = getDiffFileMock();
- const inlineLines = diffFile.highlightedDiffLines;
- const parallelLines = diffFile.parallelDiffLines;
+ const inlineLines = diffFile.highlighted_diff_lines;
+ const parallelLines = diffFile.parallel_diff_lines;
const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 };
const contextLines = [{ lineNumber: 42 }];
const options = { inlineLines, parallelLines, contextLines, lineNumbers, bottom: true };
- const inlineIndex = utils.findIndexInInlineLines(diffFile.highlightedDiffLines, lineNumbers);
- const parallelIndex = utils.findIndexInParallelLines(diffFile.parallelDiffLines, lineNumbers);
+ const inlineIndex = utils.findIndexInInlineLines(inlineLines, lineNumbers);
+ const parallelIndex = utils.findIndexInParallelLines(parallelLines, lineNumbers);
const normalizedParallelLine = {
left: options.contextLines[0],
right: options.contextLines[0],
};
utils.addContextLines(options);
+
expect(inlineLines[inlineLines.length - 1]).toEqual(contextLines[0]);
expect(parallelLines[parallelLines.length - 1]).toEqual(normalizedParallelLine);
delete options.bottom;
utils.addContextLines(options);
+
expect(inlineLines[inlineIndex]).toEqual(contextLines[0]);
expect(parallelLines[parallelIndex]).toEqual(normalizedParallelLine);
});
@@ -107,40 +118,100 @@ describe('DiffsStoreUtils', () => {
noteableType: MERGE_REQUEST_NOTEABLE_TYPE,
diffFile,
noteTargetLine: {
- lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
- metaData: null,
- newLine: 3,
- oldLine: 1,
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
+ meta_data: null,
+ new_line: 3,
+ old_line: 1,
},
diffViewType: PARALLEL_DIFF_VIEW_TYPE,
linePosition: LINE_POSITION_LEFT,
};
const position = JSON.stringify({
- base_sha: diffFile.diffRefs.baseSha,
- start_sha: diffFile.diffRefs.startSha,
- head_sha: diffFile.diffRefs.headSha,
- old_path: diffFile.oldPath,
- new_path: diffFile.newPath,
+ base_sha: diffFile.diff_refs.base_sha,
+ start_sha: diffFile.diff_refs.start_sha,
+ head_sha: diffFile.diff_refs.head_sha,
+ old_path: diffFile.old_path,
+ new_path: diffFile.new_path,
position_type: TEXT_DIFF_POSITION_TYPE,
- old_line: options.noteTargetLine.oldLine,
- new_line: options.noteTargetLine.newLine,
+ old_line: options.noteTargetLine.old_line,
+ new_line: options.noteTargetLine.new_line,
});
const postData = {
view: options.diffViewType,
line_type: options.linePosition === LINE_POSITION_RIGHT ? NEW_LINE_TYPE : OLD_LINE_TYPE,
- merge_request_diff_head_sha: diffFile.diffRefs.headSha,
+ merge_request_diff_head_sha: diffFile.diff_refs.head_sha,
in_reply_to_discussion_id: '',
note_project_id: '',
target_type: options.noteableType,
target_id: options.noteableData.id,
+ return_discussion: true,
note: {
noteable_type: options.noteableType,
noteable_id: options.noteableData.id,
commit_id: '',
type: DIFF_NOTE_TYPE,
- line_code: options.noteTargetLine.lineCode,
+ line_code: options.noteTargetLine.line_code,
+ note: options.note,
+ position,
+ },
+ };
+
+ expect(utils.getNoteFormData(options)).toEqual({
+ endpoint: options.noteableData.create_note_path,
+ data: postData,
+ });
+ });
+
+ it('should create legacy note form data', () => {
+ const diffFile = getDiffFileMock();
+ delete diffFile.diff_refs.start_sha;
+ delete diffFile.diff_refs.head_sha;
+
+ noteableDataMock.targetType = MERGE_REQUEST_NOTEABLE_TYPE;
+
+ const options = {
+ note: 'Hello world!',
+ noteableData: noteableDataMock,
+ noteableType: MERGE_REQUEST_NOTEABLE_TYPE,
+ diffFile,
+ noteTargetLine: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
+ meta_data: null,
+ new_line: 3,
+ old_line: 1,
+ },
+ diffViewType: PARALLEL_DIFF_VIEW_TYPE,
+ linePosition: LINE_POSITION_LEFT,
+ };
+
+ const position = JSON.stringify({
+ base_sha: diffFile.diff_refs.base_sha,
+ start_sha: undefined,
+ head_sha: undefined,
+ old_path: diffFile.old_path,
+ new_path: diffFile.new_path,
+ position_type: TEXT_DIFF_POSITION_TYPE,
+ old_line: options.noteTargetLine.old_line,
+ new_line: options.noteTargetLine.new_line,
+ });
+
+ const postData = {
+ view: options.diffViewType,
+ line_type: options.linePosition === LINE_POSITION_RIGHT ? NEW_LINE_TYPE : OLD_LINE_TYPE,
+ merge_request_diff_head_sha: undefined,
+ in_reply_to_discussion_id: '',
+ note_project_id: '',
+ target_type: options.noteableType,
+ target_id: options.noteableData.id,
+ return_discussion: true,
+ note: {
+ noteable_type: options.noteableType,
+ noteable_id: options.noteableData.id,
+ commit_id: '',
+ type: LEGACY_DIFF_NOTE_TYPE,
+ line_code: options.noteTargetLine.line_code,
note: options.note,
position,
},
@@ -160,51 +231,332 @@ describe('DiffsStoreUtils', () => {
const lines = [{ type: null }, { type: MATCH_LINE_TYPE }];
const linesWithReferences = utils.addLineReferences(lines, lineNumbers, true);
- expect(linesWithReferences[0].oldLine).toEqual(lineNumbers.oldLineNumber + 1);
- expect(linesWithReferences[0].newLine).toEqual(lineNumbers.newLineNumber + 1);
- expect(linesWithReferences[1].metaData.oldPos).toEqual(4);
- expect(linesWithReferences[1].metaData.newPos).toEqual(5);
+ expect(linesWithReferences[0].old_line).toEqual(lineNumbers.oldLineNumber + 1);
+ expect(linesWithReferences[0].new_line).toEqual(lineNumbers.newLineNumber + 1);
+ expect(linesWithReferences[1].meta_data.old_pos).toEqual(4);
+ expect(linesWithReferences[1].meta_data.new_pos).toEqual(5);
});
it('should add correct line references when bottom falsy', () => {
const lines = [{ type: null }, { type: MATCH_LINE_TYPE }, { type: null }];
const linesWithReferences = utils.addLineReferences(lines, lineNumbers);
- expect(linesWithReferences[0].oldLine).toEqual(0);
- expect(linesWithReferences[0].newLine).toEqual(1);
- expect(linesWithReferences[1].metaData.oldPos).toEqual(2);
- expect(linesWithReferences[1].metaData.newPos).toEqual(3);
+ expect(linesWithReferences[0].old_line).toEqual(0);
+ expect(linesWithReferences[0].new_line).toEqual(1);
+ expect(linesWithReferences[1].meta_data.old_pos).toEqual(2);
+ expect(linesWithReferences[1].meta_data.new_pos).toEqual(3);
});
});
describe('trimFirstCharOfLineContent', () => {
it('trims the line when it starts with a space', () => {
- expect(utils.trimFirstCharOfLineContent({ richText: ' diff' })).toEqual({ richText: 'diff' });
+ expect(utils.trimFirstCharOfLineContent({ rich_text: ' diff' })).toEqual({
+ discussions: [],
+ rich_text: 'diff',
+ });
});
it('trims the line when it starts with a +', () => {
- expect(utils.trimFirstCharOfLineContent({ richText: '+diff' })).toEqual({ richText: 'diff' });
+ expect(utils.trimFirstCharOfLineContent({ rich_text: '+diff' })).toEqual({
+ discussions: [],
+ rich_text: 'diff',
+ });
});
it('trims the line when it starts with a -', () => {
- expect(utils.trimFirstCharOfLineContent({ richText: '-diff' })).toEqual({ richText: 'diff' });
+ expect(utils.trimFirstCharOfLineContent({ rich_text: '-diff' })).toEqual({
+ discussions: [],
+ rich_text: 'diff',
+ });
});
it('does not trims the line when it starts with a letter', () => {
- expect(utils.trimFirstCharOfLineContent({ richText: 'diff' })).toEqual({ richText: 'diff' });
+ expect(utils.trimFirstCharOfLineContent({ rich_text: 'diff' })).toEqual({
+ discussions: [],
+ rich_text: 'diff',
+ });
});
it('does not modify the provided object', () => {
const lineObj = {
- richText: ' diff',
+ discussions: [],
+ rich_text: ' diff',
};
utils.trimFirstCharOfLineContent(lineObj);
- expect(lineObj).toEqual({ richText: ' diff' });
+
+ expect(lineObj).toEqual({ discussions: [], rich_text: ' diff' });
});
it('handles a undefined or null parameter', () => {
- expect(utils.trimFirstCharOfLineContent()).toEqual({});
+ expect(utils.trimFirstCharOfLineContent()).toEqual({ discussions: [] });
+ });
+ });
+
+ describe('prepareDiffData', () => {
+ it('sets the renderIt and collapsed attribute on files', () => {
+ const preparedDiff = { diff_files: [getDiffFileMock()] };
+ utils.prepareDiffData(preparedDiff);
+
+ const firstParallelDiffLine = preparedDiff.diff_files[0].parallel_diff_lines[2];
+
+ expect(firstParallelDiffLine.left.discussions.length).toBe(0);
+ expect(firstParallelDiffLine.left).not.toHaveAttr('text');
+ expect(firstParallelDiffLine.right.discussions.length).toBe(0);
+ expect(firstParallelDiffLine.right).not.toHaveAttr('text');
+ const firstParallelChar = firstParallelDiffLine.right.rich_text.charAt(0);
+
+ expect(firstParallelChar).not.toBe(' ');
+ expect(firstParallelChar).not.toBe('+');
+ expect(firstParallelChar).not.toBe('-');
+
+ const checkLine = preparedDiff.diff_files[0].highlighted_diff_lines[0];
+
+ expect(checkLine.discussions.length).toBe(0);
+ expect(checkLine).not.toHaveAttr('text');
+ const firstChar = checkLine.rich_text.charAt(0);
+
+ expect(firstChar).not.toBe(' ');
+ expect(firstChar).not.toBe('+');
+ expect(firstChar).not.toBe('-');
+
+ expect(preparedDiff.diff_files[0].renderIt).toBeTruthy();
+ expect(preparedDiff.diff_files[0].collapsed).toBeFalsy();
+ });
+ });
+
+ describe('isDiscussionApplicableToLine', () => {
+ const diffPosition = {
+ baseSha: 'ed13df29948c41ba367caa757ab3ec4892509910',
+ headSha: 'b921914f9a834ac47e6fd9420f78db0f83559130',
+ newLine: null,
+ newPath: '500-lines-4.txt',
+ oldLine: 5,
+ oldPath: '500-lines-4.txt',
+ startSha: 'ed13df29948c41ba367caa757ab3ec4892509910',
+ };
+
+ const wrongDiffPosition = {
+ baseSha: 'wrong',
+ headSha: 'wrong',
+ newLine: null,
+ newPath: '500-lines-4.txt',
+ oldLine: 5,
+ oldPath: '500-lines-4.txt',
+ startSha: 'wrong',
+ };
+
+ const discussions = {
+ upToDateDiscussion1: {
+ original_position: diffPosition,
+ position: wrongDiffPosition,
+ },
+ outDatedDiscussion1: {
+ original_position: wrongDiffPosition,
+ position: wrongDiffPosition,
+ },
+ };
+
+ it('returns true when the discussion is up to date', () => {
+ expect(
+ utils.isDiscussionApplicableToLine({
+ discussion: discussions.upToDateDiscussion1,
+ diffPosition,
+ latestDiff: true,
+ }),
+ ).toBe(true);
+ });
+
+ it('returns false when the discussion is not up to date', () => {
+ expect(
+ utils.isDiscussionApplicableToLine({
+ discussion: discussions.outDatedDiscussion1,
+ diffPosition,
+ latestDiff: true,
+ }),
+ ).toBe(false);
+ });
+
+ it('returns true when line codes match and discussion does not contain position and is not active', () => {
+ const discussion = { ...discussions.outDatedDiscussion1, line_code: 'ABC_1', active: false };
+ delete discussion.original_position;
+ delete discussion.position;
+
+ expect(
+ utils.isDiscussionApplicableToLine({
+ discussion,
+ diffPosition: {
+ ...diffPosition,
+ lineCode: 'ABC_1',
+ },
+ latestDiff: true,
+ }),
+ ).toBe(false);
+ });
+
+ it('returns true when line codes match and discussion does not contain position and is active', () => {
+ const discussion = { ...discussions.outDatedDiscussion1, line_code: 'ABC_1', active: true };
+ delete discussion.original_position;
+ delete discussion.position;
+
+ expect(
+ utils.isDiscussionApplicableToLine({
+ discussion,
+ diffPosition: {
+ ...diffPosition,
+ line_code: 'ABC_1',
+ },
+ latestDiff: true,
+ }),
+ ).toBe(true);
+ });
+
+ it('returns false when not latest diff', () => {
+ const discussion = { ...discussions.outDatedDiscussion1, line_code: 'ABC_1', active: true };
+ delete discussion.original_position;
+ delete discussion.position;
+
+ expect(
+ utils.isDiscussionApplicableToLine({
+ discussion,
+ diffPosition: {
+ ...diffPosition,
+ lineCode: 'ABC_1',
+ },
+ latestDiff: false,
+ }),
+ ).toBe(false);
+ });
+ });
+
+ describe('generateTreeList', () => {
+ let files;
+
+ beforeAll(() => {
+ files = [
+ {
+ new_path: 'app/index.js',
+ deleted_file: false,
+ new_file: false,
+ removed_lines: 10,
+ added_lines: 0,
+ file_hash: 'test',
+ },
+ {
+ new_path: 'app/test/index.js',
+ deleted_file: false,
+ new_file: true,
+ removed_lines: 0,
+ added_lines: 0,
+ file_hash: 'test',
+ },
+ {
+ new_path: 'app/test/filepathneedstruncating.js',
+ deleted_file: false,
+ new_file: true,
+ removed_lines: 0,
+ added_lines: 0,
+ file_hash: 'test',
+ },
+ {
+ new_path: 'package.json',
+ deleted_file: true,
+ new_file: false,
+ removed_lines: 0,
+ added_lines: 0,
+ file_hash: 'test',
+ },
+ ];
+ });
+
+ it('creates a tree of files', () => {
+ const { tree } = utils.generateTreeList(files);
+
+ expect(tree).toEqual([
+ {
+ key: 'app',
+ path: 'app',
+ name: 'app',
+ type: 'tree',
+ tree: [
+ {
+ addedLines: 0,
+ changed: true,
+ deleted: false,
+ fileHash: 'test',
+ key: 'app/index.js',
+ name: 'index.js',
+ path: 'app/index.js',
+ removedLines: 10,
+ tempFile: false,
+ type: 'blob',
+ tree: [],
+ },
+ {
+ key: 'app/test',
+ path: 'app/test',
+ name: 'test',
+ type: 'tree',
+ opened: true,
+ tree: [
+ {
+ addedLines: 0,
+ changed: true,
+ deleted: false,
+ fileHash: 'test',
+ key: 'app/test/index.js',
+ name: 'index.js',
+ path: 'app/test/index.js',
+ removedLines: 0,
+ tempFile: true,
+ type: 'blob',
+ tree: [],
+ },
+ {
+ addedLines: 0,
+ changed: true,
+ deleted: false,
+ fileHash: 'test',
+ key: 'app/test/filepathneedstruncating.js',
+ name: 'filepathneedstruncating.js',
+ path: 'app/test/filepathneedstruncating.js',
+ removedLines: 0,
+ tempFile: true,
+ type: 'blob',
+ tree: [],
+ },
+ ],
+ },
+ ],
+ opened: true,
+ },
+ {
+ key: 'package.json',
+ path: 'package.json',
+ name: 'package.json',
+ type: 'blob',
+ changed: true,
+ tempFile: false,
+ deleted: true,
+ fileHash: 'test',
+ addedLines: 0,
+ removedLines: 0,
+ tree: [],
+ },
+ ]);
+ });
+
+ it('creates flat list of blobs & folders', () => {
+ const { treeEntries } = utils.generateTreeList(files);
+
+ expect(Object.keys(treeEntries)).toEqual([
+ 'app',
+ 'app/index.js',
+ 'app/test',
+ 'app/test/index.js',
+ 'app/test/filepathneedstruncating.js',
+ 'package.json',
+ ]);
});
});
});
diff --git a/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js b/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js
new file mode 100644
index 00000000000..08ffc44605f
--- /dev/null
+++ b/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js
@@ -0,0 +1,29 @@
+import DirtySubmitCollection from '~/dirty_submit/dirty_submit_collection';
+import { setInput, createForm } from './helper';
+
+describe('DirtySubmitCollection', () => {
+ it('disables submits until there are changes', done => {
+ const testElementsCollection = [createForm(), createForm()];
+ const forms = testElementsCollection.map(testElements => testElements.form);
+
+ new DirtySubmitCollection(forms); // eslint-disable-line no-new
+
+ testElementsCollection.forEach(testElements => {
+ const { input, submit } = testElements;
+ const originalValue = input.value;
+
+ expect(submit.disabled).toBe(true);
+
+ return setInput(input, `${originalValue} changes`)
+ .then(() => {
+ expect(submit.disabled).toBe(false);
+ })
+ .then(() => setInput(input, originalValue))
+ .then(() => {
+ expect(submit.disabled).toBe(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/dirty_submit/dirty_submit_factory_spec.js b/spec/javascripts/dirty_submit/dirty_submit_factory_spec.js
new file mode 100644
index 00000000000..40843a68582
--- /dev/null
+++ b/spec/javascripts/dirty_submit/dirty_submit_factory_spec.js
@@ -0,0 +1,18 @@
+import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory';
+import DirtySubmitForm from '~/dirty_submit/dirty_submit_form';
+import DirtySubmitCollection from '~/dirty_submit/dirty_submit_collection';
+import { createForm } from './helper';
+
+describe('DirtySubmitCollection', () => {
+ it('returns a DirtySubmitForm instance for single form elements', () => {
+ const { form } = createForm();
+
+ expect(dirtySubmitFactory(form) instanceof DirtySubmitForm).toBe(true);
+ });
+
+ it('returns a DirtySubmitCollection instance for a collection of form elements', () => {
+ const forms = [createForm().form, createForm().form];
+
+ expect(dirtySubmitFactory(forms) instanceof DirtySubmitCollection).toBe(true);
+ });
+});
diff --git a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js
new file mode 100644
index 00000000000..093fec97951
--- /dev/null
+++ b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js
@@ -0,0 +1,36 @@
+import DirtySubmitForm from '~/dirty_submit/dirty_submit_form';
+import { setInput, createForm } from './helper';
+
+function expectToToggleDisableOnDirtyUpdate(submit, input) {
+ const originalValue = input.value;
+
+ expect(submit.disabled).toBe(true);
+
+ return setInput(input, `${originalValue} changes`)
+ .then(() => expect(submit.disabled).toBe(false))
+ .then(() => setInput(input, originalValue))
+ .then(() => expect(submit.disabled).toBe(true));
+}
+
+describe('DirtySubmitForm', () => {
+ it('disables submit until there are changes', done => {
+ const { form, input, submit } = createForm();
+
+ new DirtySubmitForm(form); // eslint-disable-line no-new
+
+ return expectToToggleDisableOnDirtyUpdate(submit, input)
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('disables submit until there are changes when initializing with a falsy value', done => {
+ const { form, input, submit } = createForm();
+ input.value = '';
+
+ new DirtySubmitForm(form); // eslint-disable-line no-new
+
+ return expectToToggleDisableOnDirtyUpdate(submit, input)
+ .then(done)
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/dirty_submit/helper.js b/spec/javascripts/dirty_submit/helper.js
new file mode 100644
index 00000000000..6d1e643553c
--- /dev/null
+++ b/spec/javascripts/dirty_submit/helper.js
@@ -0,0 +1,31 @@
+import DirtySubmitForm from '~/dirty_submit/dirty_submit_form';
+import setTimeoutPromiseHelper from '../helpers/set_timeout_promise_helper';
+
+export function setInput(element, value) {
+ element.value = value;
+
+ element.dispatchEvent(
+ new Event('input', {
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+
+ return setTimeoutPromiseHelper(DirtySubmitForm.THROTTLE_DURATION);
+}
+
+export function createForm() {
+ const form = document.createElement('form');
+ form.innerHTML = `
+ <input type="text" value="original" class="js-input" name="input" />
+ <button type="submit" class="js-dirty-submit"></button>
+ `;
+ const input = form.querySelector('.js-input');
+ const submit = form.querySelector('.js-dirty-submit');
+
+ return {
+ form,
+ input,
+ submit,
+ };
+}
diff --git a/spec/javascripts/droplab/drop_down_spec.js b/spec/javascripts/droplab/drop_down_spec.js
index 896a04a1a07..18ab03653f4 100644
--- a/spec/javascripts/droplab/drop_down_spec.js
+++ b/spec/javascripts/droplab/drop_down_spec.js
@@ -2,9 +2,9 @@ import DropDown from '~/droplab/drop_down';
import utils from '~/droplab/utils';
import { SELECTED_CLASS } from '~/droplab/constants';
-describe('DropLab DropDown', function () {
- describe('class constructor', function () {
- beforeEach(function () {
+describe('DropLab DropDown', function() {
+ describe('class constructor', function() {
+ beforeEach(function() {
spyOn(DropDown.prototype, 'getItems');
spyOn(DropDown.prototype, 'initTemplateString');
spyOn(DropDown.prototype, 'addEvents');
@@ -13,32 +13,32 @@ describe('DropLab DropDown', function () {
this.dropdown = new DropDown(this.list);
});
- it('sets the .hidden property to true', function () {
+ it('sets the .hidden property to true', function() {
expect(this.dropdown.hidden).toBe(true);
});
- it('sets the .list property', function () {
+ it('sets the .list property', function() {
expect(this.dropdown.list).toBe(this.list);
});
- it('calls .getItems', function () {
+ it('calls .getItems', function() {
expect(DropDown.prototype.getItems).toHaveBeenCalled();
});
- it('calls .initTemplateString', function () {
+ it('calls .initTemplateString', function() {
expect(DropDown.prototype.initTemplateString).toHaveBeenCalled();
});
- it('calls .addEvents', function () {
+ it('calls .addEvents', function() {
expect(DropDown.prototype.addEvents).toHaveBeenCalled();
});
- it('sets the .initialState property to the .list.innerHTML', function () {
+ it('sets the .initialState property to the .list.innerHTML', function() {
expect(this.dropdown.initialState).toBe(this.list.innerHTML);
});
- describe('if the list argument is a string', function () {
- beforeEach(function () {
+ describe('if the list argument is a string', function() {
+ beforeEach(function() {
this.element = {};
this.selector = '.selector';
@@ -47,18 +47,18 @@ describe('DropLab DropDown', function () {
this.dropdown = new DropDown(this.selector);
});
- it('calls .querySelector with the selector string', function () {
+ it('calls .querySelector with the selector string', function() {
expect(Document.prototype.querySelector).toHaveBeenCalledWith(this.selector);
});
- it('sets the .list property element', function () {
+ it('sets the .list property element', function() {
expect(this.dropdown.list).toBe(this.element);
});
});
});
- describe('getItems', function () {
- beforeEach(function () {
+ describe('getItems', function() {
+ beforeEach(function() {
this.list = { querySelectorAll: () => {} };
this.dropdown = { list: this.list };
this.nodeList = [];
@@ -68,37 +68,37 @@ describe('DropLab DropDown', function () {
this.getItems = DropDown.prototype.getItems.call(this.dropdown);
});
- it('calls .querySelectorAll with a list item query', function () {
+ it('calls .querySelectorAll with a list item query', function() {
expect(this.list.querySelectorAll).toHaveBeenCalledWith('li');
});
- it('sets the .items property to the returned list items', function () {
+ it('sets the .items property to the returned list items', function() {
expect(this.dropdown.items).toEqual(jasmine.any(Array));
});
- it('returns the .items', function () {
+ it('returns the .items', function() {
expect(this.getItems).toEqual(jasmine.any(Array));
});
});
- describe('initTemplateString', function () {
- beforeEach(function () {
+ describe('initTemplateString', function() {
+ beforeEach(function() {
this.items = [{ outerHTML: '<a></a>' }, { outerHTML: '<img>' }];
this.dropdown = { items: this.items };
DropDown.prototype.initTemplateString.call(this.dropdown);
});
- it('should set .templateString to the last items .outerHTML', function () {
+ it('should set .templateString to the last items .outerHTML', function() {
expect(this.dropdown.templateString).toBe(this.items[1].outerHTML);
});
- it('should not set .templateString to a non-last items .outerHTML', function () {
+ it('should not set .templateString to a non-last items .outerHTML', function() {
expect(this.dropdown.templateString).not.toBe(this.items[0].outerHTML);
});
- describe('if .items is not set', function () {
- beforeEach(function () {
+ describe('if .items is not set', function() {
+ beforeEach(function() {
this.dropdown = { getItems: () => {} };
spyOn(this.dropdown, 'getItems').and.returnValue([]);
@@ -106,26 +106,26 @@ describe('DropLab DropDown', function () {
DropDown.prototype.initTemplateString.call(this.dropdown);
});
- it('should call .getItems', function () {
+ it('should call .getItems', function() {
expect(this.dropdown.getItems).toHaveBeenCalled();
});
});
- describe('if items array is empty', function () {
- beforeEach(function () {
+ describe('if items array is empty', function() {
+ beforeEach(function() {
this.dropdown = { items: [] };
DropDown.prototype.initTemplateString.call(this.dropdown);
});
- it('should set .templateString to an empty string', function () {
+ it('should set .templateString to an empty string', function() {
expect(this.dropdown.templateString).toBe('');
});
});
});
- describe('clickEvent', function () {
- beforeEach(function () {
+ describe('clickEvent', function() {
+ beforeEach(function() {
this.classList = jasmine.createSpyObj('classList', ['contains']);
this.list = { dispatchEvent: () => {} };
this.dropdown = {
@@ -143,7 +143,7 @@ describe('DropLab DropDown', function () {
};
this.customEvent = {};
this.dummyListItem = document.createElement('li');
- spyOn(this.event.target, 'closest').and.callFake((selector) => {
+ spyOn(this.event.target, 'closest').and.callFake(selector => {
if (selector === 'li') {
return this.dummyListItem;
}
@@ -159,51 +159,51 @@ describe('DropLab DropDown', function () {
this.classList.contains.and.returnValue(false);
});
- it('should call event.target.closest', function () {
+ it('should call event.target.closest', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.event.target.closest).toHaveBeenCalledWith('.droplab-item-ignore');
expect(this.event.target.closest).toHaveBeenCalledWith('li');
});
- it('should call addSelectedClass', function () {
+ it('should call addSelectedClass', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.dropdown.addSelectedClass).toHaveBeenCalledWith(this.dummyListItem);
});
- it('should call .preventDefault', function () {
+ it('should call .preventDefault', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.event.preventDefault).toHaveBeenCalled();
});
- it('should call .hide', function () {
+ it('should call .hide', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.dropdown.hide).toHaveBeenCalled();
});
- it('should construct CustomEvent', function () {
+ it('should construct CustomEvent', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(window.CustomEvent).toHaveBeenCalledWith('click.dl', jasmine.any(Object));
});
- it('should call .dispatchEvent with the customEvent', function () {
+ it('should call .dispatchEvent with the customEvent', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.list.dispatchEvent).toHaveBeenCalledWith(this.customEvent);
});
- describe('if the target is a UL element', function () {
- beforeEach(function () {
+ describe('if the target is a UL element', function() {
+ beforeEach(function() {
this.event.target = document.createElement('ul');
spyOn(this.event.target, 'closest');
});
- it('should return immediately', function () {
+ it('should return immediately', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.event.target.closest).not.toHaveBeenCalled();
@@ -211,8 +211,8 @@ describe('DropLab DropDown', function () {
});
});
- describe('if the target has the droplab-item-ignore class', function () {
- beforeEach(function () {
+ describe('if the target has the droplab-item-ignore class', function() {
+ beforeEach(function() {
this.ignoredButton = document.createElement('button');
this.ignoredButton.classList.add('droplab-item-ignore');
this.event.target = this.ignoredButton;
@@ -220,7 +220,7 @@ describe('DropLab DropDown', function () {
spyOn(this.ignoredButton, 'closest').and.callThrough();
});
- it('does not select element', function () {
+ it('does not select element', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.ignoredButton.closest.calls.count()).toBe(1);
@@ -229,13 +229,13 @@ describe('DropLab DropDown', function () {
});
});
- describe('if no selected element exists', function () {
- beforeEach(function () {
+ describe('if no selected element exists', function() {
+ beforeEach(function() {
this.event.preventDefault.calls.reset();
this.dummyListItem = null;
});
- it('should return before .preventDefault is called', function () {
+ it('should return before .preventDefault is called', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.event.preventDefault).not.toHaveBeenCalled();
@@ -244,12 +244,12 @@ describe('DropLab DropDown', function () {
});
describe('if hideOnClick is false', () => {
- beforeEach(function () {
+ beforeEach(function() {
this.dropdown.hideOnClick = false;
this.dropdown.hide.calls.reset();
});
- it('should not call .hide', function () {
+ it('should not call .hide', function() {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
expect(this.dropdown.hide).not.toHaveBeenCalled();
@@ -257,8 +257,8 @@ describe('DropLab DropDown', function () {
});
});
- describe('addSelectedClass', function () {
- beforeEach(function () {
+ describe('addSelectedClass', function() {
+ beforeEach(function() {
this.items = Array(4).forEach((item, i) => {
this.items[i] = { classList: { add: () => {} } };
spyOn(this.items[i].classList, 'add');
@@ -272,17 +272,17 @@ describe('DropLab DropDown', function () {
DropDown.prototype.addSelectedClass.call(this.dropdown, this.selected);
});
- it('should call .removeSelectedClasses', function () {
+ it('should call .removeSelectedClasses', function() {
expect(this.dropdown.removeSelectedClasses).toHaveBeenCalled();
});
- it('should call .classList.add', function () {
+ it('should call .classList.add', function() {
expect(this.selected.classList.add).toHaveBeenCalledWith(SELECTED_CLASS);
});
});
- describe('removeSelectedClasses', function () {
- beforeEach(function () {
+ describe('removeSelectedClasses', function() {
+ beforeEach(function() {
this.items = Array(4);
this.items.forEach((item, i) => {
this.items[i] = { classList: { add: () => {} } };
@@ -293,14 +293,14 @@ describe('DropLab DropDown', function () {
DropDown.prototype.removeSelectedClasses.call(this.dropdown);
});
- it('should call .classList.remove for all items', function () {
+ it('should call .classList.remove for all items', function() {
this.items.forEach((item, i) => {
expect(this.items[i].classList.add).toHaveBeenCalledWith(SELECTED_CLASS);
});
});
- describe('if .items is not set', function () {
- beforeEach(function () {
+ describe('if .items is not set', function() {
+ beforeEach(function() {
this.dropdown = { getItems: () => {} };
spyOn(this.dropdown, 'getItems').and.returnValue([]);
@@ -308,14 +308,14 @@ describe('DropLab DropDown', function () {
DropDown.prototype.removeSelectedClasses.call(this.dropdown);
});
- it('should call .getItems', function () {
+ it('should call .getItems', function() {
expect(this.dropdown.getItems).toHaveBeenCalled();
});
});
});
- describe('addEvents', function () {
- beforeEach(function () {
+ describe('addEvents', function() {
+ beforeEach(function() {
this.list = {
addEventListener: () => {},
querySelectorAll: () => [],
@@ -328,7 +328,7 @@ describe('DropLab DropDown', function () {
};
});
- it('should call .addEventListener', function () {
+ it('should call .addEventListener', function() {
spyOn(this.list, 'addEventListener');
DropDown.prototype.addEvents.call(this.dropdown);
@@ -338,8 +338,8 @@ describe('DropLab DropDown', function () {
});
});
- describe('setData', function () {
- beforeEach(function () {
+ describe('setData', function() {
+ beforeEach(function() {
this.dropdown = { render: () => {} };
this.data = ['data'];
@@ -348,17 +348,17 @@ describe('DropLab DropDown', function () {
DropDown.prototype.setData.call(this.dropdown, this.data);
});
- it('should set .data', function () {
+ it('should set .data', function() {
expect(this.dropdown.data).toBe(this.data);
});
- it('should call .render with the .data', function () {
+ it('should call .render with the .data', function() {
expect(this.dropdown.render).toHaveBeenCalledWith(this.data);
});
});
- describe('addData', function () {
- beforeEach(function () {
+ describe('addData', function() {
+ beforeEach(function() {
this.dropdown = { render: () => {}, data: ['data1'] };
this.data = ['data2'];
@@ -368,20 +368,20 @@ describe('DropLab DropDown', function () {
DropDown.prototype.addData.call(this.dropdown, this.data);
});
- it('should call .concat with data', function () {
+ it('should call .concat with data', function() {
expect(Array.prototype.concat).toHaveBeenCalledWith(this.data);
});
- it('should set .data with concatination', function () {
+ it('should set .data with concatination', function() {
expect(this.dropdown.data).toEqual(['data1', 'data2']);
});
- it('should call .render with the .data', function () {
+ it('should call .render with the .data', function() {
expect(this.dropdown.render).toHaveBeenCalledWith(['data1', 'data2']);
});
- describe('if .data is undefined', function () {
- beforeEach(function () {
+ describe('if .data is undefined', function() {
+ beforeEach(function() {
this.dropdown = { render: () => {}, data: undefined };
this.data = ['data2'];
@@ -390,14 +390,14 @@ describe('DropLab DropDown', function () {
DropDown.prototype.addData.call(this.dropdown, this.data);
});
- it('should set .data with concatination', function () {
+ it('should set .data with concatination', function() {
expect(this.dropdown.data).toEqual(['data2']);
});
});
});
- describe('render', function () {
- beforeEach(function () {
+ describe('render', function() {
+ beforeEach(function() {
this.list = { querySelector: () => {}, dispatchEvent: () => {} };
this.dropdown = { renderChildren: () => {}, list: this.list };
this.renderableList = {};
@@ -413,45 +413,45 @@ describe('DropLab DropDown', function () {
DropDown.prototype.render.call(this.dropdown, this.data);
});
- it('should call .map', function () {
+ it('should call .map', function() {
expect(this.data.map).toHaveBeenCalledWith(jasmine.any(Function));
});
- it('should call .renderChildren for each data item', function () {
+ it('should call .renderChildren for each data item', function() {
expect(this.dropdown.renderChildren.calls.count()).toBe(this.data.length);
});
- it('sets the renderableList .innerHTML', function () {
+ it('sets the renderableList .innerHTML', function() {
expect(this.renderableList.innerHTML).toBe('01');
});
- it('should call render.dl', function () {
+ it('should call render.dl', function() {
expect(window.CustomEvent).toHaveBeenCalledWith('render.dl', jasmine.any(Object));
});
- it('should call dispatchEvent with the customEvent', function () {
+ it('should call dispatchEvent with the customEvent', function() {
expect(this.list.dispatchEvent).toHaveBeenCalledWith(this.customEvent);
});
- describe('if no data argument is passed', function () {
- beforeEach(function () {
+ describe('if no data argument is passed', function() {
+ beforeEach(function() {
this.data.map.calls.reset();
this.dropdown.renderChildren.calls.reset();
DropDown.prototype.render.call(this.dropdown, undefined);
});
- it('should not call .map', function () {
+ it('should not call .map', function() {
expect(this.data.map).not.toHaveBeenCalled();
});
- it('should not call .renderChildren', function () {
+ it('should not call .renderChildren', function() {
expect(this.dropdown.renderChildren).not.toHaveBeenCalled();
});
});
- describe('if no dynamic list is present', function () {
- beforeEach(function () {
+ describe('if no dynamic list is present', function() {
+ beforeEach(function() {
this.list = { querySelector: () => {}, dispatchEvent: () => {} };
this.dropdown = { renderChildren: () => {}, list: this.list };
this.data = [0, 1];
@@ -463,14 +463,14 @@ describe('DropLab DropDown', function () {
DropDown.prototype.render.call(this.dropdown, this.data);
});
- it('sets the .list .innerHTML', function () {
+ it('sets the .list .innerHTML', function() {
expect(this.list.innerHTML).toBe('01');
});
});
});
- describe('renderChildren', function () {
- beforeEach(function () {
+ describe('renderChildren', function() {
+ beforeEach(function() {
this.templateString = 'templateString';
this.dropdown = { templateString: this.templateString };
this.data = { droplab_hidden: true };
@@ -484,44 +484,44 @@ describe('DropLab DropDown', function () {
this.renderChildren = DropDown.prototype.renderChildren.call(this.dropdown, this.data);
});
- it('should call utils.t with .templateString and data', function () {
+ it('should call utils.t with .templateString and data', function() {
expect(utils.template).toHaveBeenCalledWith(this.templateString, this.data);
});
- it('should call document.createElement', function () {
+ it('should call document.createElement', function() {
expect(document.createElement).toHaveBeenCalledWith('div');
});
- it('should set the templates .innerHTML to the HTML', function () {
+ it('should set the templates .innerHTML to the HTML', function() {
expect(this.template.innerHTML).toBe(this.html);
});
- it('should call .setImagesSrc with the template', function () {
+ it('should call .setImagesSrc with the template', function() {
expect(DropDown.setImagesSrc).toHaveBeenCalledWith(this.template);
});
- it('should set the template display to none', function () {
+ it('should set the template display to none', function() {
expect(this.template.firstChild.style.display).toBe('none');
});
- it('should return the templates .firstChild.outerHTML', function () {
+ it('should return the templates .firstChild.outerHTML', function() {
expect(this.renderChildren).toBe(this.template.firstChild.outerHTML);
});
- describe('if droplab_hidden is false', function () {
- beforeEach(function () {
+ describe('if droplab_hidden is false', function() {
+ beforeEach(function() {
this.data = { droplab_hidden: false };
this.renderChildren = DropDown.prototype.renderChildren.call(this.dropdown, this.data);
});
- it('should set the template display to block', function () {
+ it('should set the template display to block', function() {
expect(this.template.firstChild.style.display).toBe('block');
});
});
});
- describe('setImagesSrc', function () {
- beforeEach(function () {
+ describe('setImagesSrc', function() {
+ beforeEach(function() {
this.template = { querySelectorAll: () => {} };
spyOn(this.template, 'querySelectorAll').and.returnValue([]);
@@ -529,64 +529,64 @@ describe('DropLab DropDown', function () {
DropDown.setImagesSrc(this.template);
});
- it('should call .querySelectorAll', function () {
+ it('should call .querySelectorAll', function() {
expect(this.template.querySelectorAll).toHaveBeenCalledWith('img[data-src]');
});
});
- describe('show', function () {
- beforeEach(function () {
+ describe('show', function() {
+ beforeEach(function() {
this.list = { style: {} };
this.dropdown = { list: this.list, hidden: true };
DropDown.prototype.show.call(this.dropdown);
});
- it('it should set .list display to block', function () {
+ it('it should set .list display to block', function() {
expect(this.list.style.display).toBe('block');
});
- it('it should set .hidden to false', function () {
+ it('it should set .hidden to false', function() {
expect(this.dropdown.hidden).toBe(false);
});
- describe('if .hidden is false', function () {
- beforeEach(function () {
+ describe('if .hidden is false', function() {
+ beforeEach(function() {
this.list = { style: {} };
this.dropdown = { list: this.list, hidden: false };
this.show = DropDown.prototype.show.call(this.dropdown);
});
- it('should return undefined', function () {
+ it('should return undefined', function() {
expect(this.show).toEqual(undefined);
});
- it('should not set .list display to block', function () {
+ it('should not set .list display to block', function() {
expect(this.list.style.display).not.toEqual('block');
});
});
});
- describe('hide', function () {
- beforeEach(function () {
+ describe('hide', function() {
+ beforeEach(function() {
this.list = { style: {} };
this.dropdown = { list: this.list };
DropDown.prototype.hide.call(this.dropdown);
});
- it('it should set .list display to none', function () {
+ it('it should set .list display to none', function() {
expect(this.list.style.display).toBe('none');
});
- it('it should set .hidden to true', function () {
+ it('it should set .hidden to true', function() {
expect(this.dropdown.hidden).toBe(true);
});
});
- describe('toggle', function () {
- beforeEach(function () {
+ describe('toggle', function() {
+ beforeEach(function() {
this.hidden = true;
this.dropdown = { hidden: this.hidden, show: () => {}, hide: () => {} };
@@ -596,12 +596,12 @@ describe('DropLab DropDown', function () {
DropDown.prototype.toggle.call(this.dropdown);
});
- it('should call .show', function () {
+ it('should call .show', function() {
expect(this.dropdown.show).toHaveBeenCalled();
});
- describe('if .hidden is false', function () {
- beforeEach(function () {
+ describe('if .hidden is false', function() {
+ beforeEach(function() {
this.hidden = false;
this.dropdown = { hidden: this.hidden, show: () => {}, hide: () => {} };
@@ -611,14 +611,14 @@ describe('DropLab DropDown', function () {
DropDown.prototype.toggle.call(this.dropdown);
});
- it('should call .hide', function () {
+ it('should call .hide', function() {
expect(this.dropdown.hide).toHaveBeenCalled();
});
});
});
- describe('destroy', function () {
- beforeEach(function () {
+ describe('destroy', function() {
+ beforeEach(function() {
this.list = { removeEventListener: () => {} };
this.eventWrapper = { clickEvent: 'clickEvent' };
this.dropdown = { list: this.list, hide: () => {}, eventWrapper: this.eventWrapper };
@@ -629,12 +629,15 @@ describe('DropLab DropDown', function () {
DropDown.prototype.destroy.call(this.dropdown);
});
- it('it should call .hide', function () {
+ it('it should call .hide', function() {
expect(this.dropdown.hide).toHaveBeenCalled();
});
- it('it should call .removeEventListener', function () {
- expect(this.list.removeEventListener).toHaveBeenCalledWith('click', this.eventWrapper.clickEvent);
+ it('it should call .removeEventListener', function() {
+ expect(this.list.removeEventListener).toHaveBeenCalledWith(
+ 'click',
+ this.eventWrapper.clickEvent,
+ );
});
});
});
diff --git a/spec/javascripts/droplab/hook_spec.js b/spec/javascripts/droplab/hook_spec.js
index 5eed1db2750..40470436f19 100644
--- a/spec/javascripts/droplab/hook_spec.js
+++ b/spec/javascripts/droplab/hook_spec.js
@@ -1,8 +1,8 @@
import Hook from '~/droplab/hook';
-describe('Hook', function () {
- describe('class constructor', function () {
- beforeEach(function () {
+describe('Hook', function() {
+ describe('class constructor', function() {
+ beforeEach(function() {
this.trigger = { id: 'id' };
this.list = {};
this.plugins = {};
@@ -14,58 +14,58 @@ describe('Hook', function () {
this.hook = new Hook(this.trigger, this.list, this.plugins, this.config);
});
- it('should set .trigger', function () {
+ it('should set .trigger', function() {
expect(this.hook.trigger).toBe(this.trigger);
});
- it('should set .list', function () {
+ it('should set .list', function() {
expect(this.hook.list).toBe(this.dropdown);
});
- it('should call DropDown constructor', function () {
+ it('should call DropDown constructor', function() {
expect(this.dropdownConstructor).toHaveBeenCalledWith(this.list, this.config);
});
- it('should set .type', function () {
+ it('should set .type', function() {
expect(this.hook.type).toBe('Hook');
});
- it('should set .event', function () {
+ it('should set .event', function() {
expect(this.hook.event).toBe('click');
});
- it('should set .plugins', function () {
+ it('should set .plugins', function() {
expect(this.hook.plugins).toBe(this.plugins);
});
- it('should set .config', function () {
+ it('should set .config', function() {
expect(this.hook.config).toBe(this.config);
});
- it('should set .id', function () {
+ it('should set .id', function() {
expect(this.hook.id).toBe(this.trigger.id);
});
- describe('if config argument is undefined', function () {
- beforeEach(function () {
+ describe('if config argument is undefined', function() {
+ beforeEach(function() {
this.config = undefined;
this.hook = new Hook(this.trigger, this.list, this.plugins, this.config);
});
- it('should set .config to an empty object', function () {
+ it('should set .config to an empty object', function() {
expect(this.hook.config).toEqual({});
});
});
- describe('if plugins argument is undefined', function () {
- beforeEach(function () {
+ describe('if plugins argument is undefined', function() {
+ beforeEach(function() {
this.plugins = undefined;
this.hook = new Hook(this.trigger, this.list, this.plugins, this.config);
});
- it('should set .plugins to an empty array', function () {
+ it('should set .plugins to an empty array', function() {
expect(this.hook.plugins).toEqual([]);
});
});
diff --git a/spec/javascripts/droplab/plugins/ajax_filter_spec.js b/spec/javascripts/droplab/plugins/ajax_filter_spec.js
index 8155d98b543..5dbe50af07f 100644
--- a/spec/javascripts/droplab/plugins/ajax_filter_spec.js
+++ b/spec/javascripts/droplab/plugins/ajax_filter_spec.js
@@ -38,8 +38,8 @@ describe('AjaxFilter', () => {
dummyList.list.appendChild(dynamicList);
});
- it('calls onLoadingFinished after loading data', (done) => {
- ajaxSpy = (url) => {
+ it('calls onLoadingFinished after loading data', done => {
+ ajaxSpy = url => {
expect(url).toBe('dummy endpoint?dummy search key=');
return Promise.resolve(dummyData);
};
@@ -52,16 +52,16 @@ describe('AjaxFilter', () => {
.catch(done.fail);
});
- it('does not call onLoadingFinished if Ajax call fails', (done) => {
+ it('does not call onLoadingFinished if Ajax call fails', done => {
const dummyError = new Error('My dummy is sick! :-(');
- ajaxSpy = (url) => {
+ ajaxSpy = url => {
expect(url).toBe('dummy endpoint?dummy search key=');
return Promise.reject(dummyError);
};
AjaxFilter.trigger()
.then(done.fail)
- .catch((error) => {
+ .catch(error => {
expect(error).toBe(dummyError);
expect(dummyConfig.onLoadingFinished.calls.count()).toBe(0);
})
diff --git a/spec/javascripts/droplab/plugins/ajax_spec.js b/spec/javascripts/droplab/plugins/ajax_spec.js
index 085f25764fe..2f492d00c0a 100644
--- a/spec/javascripts/droplab/plugins/ajax_spec.js
+++ b/spec/javascripts/droplab/plugins/ajax_spec.js
@@ -8,6 +8,7 @@ describe('Ajax', () => {
describe('is not configured', () => {
it('passes the data through', () => {
const data = ['data'];
+
expect(Ajax.preprocessing(config, data)).toEqual(data);
});
});
@@ -22,13 +23,17 @@ describe('Ajax', () => {
it('calls preprocessing', () => {
Ajax.preprocessing(config, []);
+
expect(config.preprocessing.calls.count()).toBe(1);
});
it('overrides AjaxCache', () => {
- spyOn(AjaxCache, 'override').and.callFake((endpoint, results) => expect(results).toEqual(processedArray));
+ spyOn(AjaxCache, 'override').and.callFake((endpoint, results) => {
+ expect(results).toEqual(processedArray);
+ });
Ajax.preprocessing(config, []);
+
expect(AjaxCache.override.calls.count()).toBe(1);
});
});
diff --git a/spec/javascripts/droplab/plugins/input_setter_spec.js b/spec/javascripts/droplab/plugins/input_setter_spec.js
index bd625f4ae80..711e0486bff 100644
--- a/spec/javascripts/droplab/plugins/input_setter_spec.js
+++ b/spec/javascripts/droplab/plugins/input_setter_spec.js
@@ -1,10 +1,8 @@
-/* eslint-disable */
-
import InputSetter from '~/droplab/plugins/input_setter';
-describe('InputSetter', function () {
- describe('init', function () {
- beforeEach(function () {
+describe('InputSetter', function() {
+ describe('init', function() {
+ beforeEach(function() {
this.config = { InputSetter: {} };
this.hook = { config: this.config };
this.inputSetter = jasmine.createSpyObj('inputSetter', ['addEvents']);
@@ -12,60 +10,62 @@ describe('InputSetter', function () {
InputSetter.init.call(this.inputSetter, this.hook);
});
- it('should set .hook', function () {
+ it('should set .hook', function() {
expect(this.inputSetter.hook).toBe(this.hook);
});
- it('should set .config', function () {
+ it('should set .config', function() {
expect(this.inputSetter.config).toBe(this.config.InputSetter);
});
- it('should set .eventWrapper', function () {
+ it('should set .eventWrapper', function() {
expect(this.inputSetter.eventWrapper).toEqual({});
});
- it('should call .addEvents', function () {
+ it('should call .addEvents', function() {
expect(this.inputSetter.addEvents).toHaveBeenCalled();
});
- describe('if config.InputSetter is not set', function () {
- beforeEach(function () {
+ describe('if config.InputSetter is not set', function() {
+ beforeEach(function() {
this.config = { InputSetter: undefined };
this.hook = { config: this.config };
InputSetter.init.call(this.inputSetter, this.hook);
});
- it('should set .config to an empty object', function () {
+ it('should set .config to an empty object', function() {
expect(this.inputSetter.config).toEqual({});
});
- it('should set hook.config to an empty object', function () {
+ it('should set hook.config to an empty object', function() {
expect(this.hook.config.InputSetter).toEqual({});
});
- })
+ });
});
- describe('addEvents', function () {
- beforeEach(function () {
+ describe('addEvents', function() {
+ beforeEach(function() {
this.hook = { list: { list: jasmine.createSpyObj('list', ['addEventListener']) } };
this.inputSetter = { eventWrapper: {}, hook: this.hook, setInputs: () => {} };
InputSetter.addEvents.call(this.inputSetter);
});
- it('should set .eventWrapper.setInputs', function () {
+ it('should set .eventWrapper.setInputs', function() {
expect(this.inputSetter.eventWrapper.setInputs).toEqual(jasmine.any(Function));
});
- it('should call .addEventListener', function () {
- expect(this.hook.list.list.addEventListener)
- .toHaveBeenCalledWith('click.dl', this.inputSetter.eventWrapper.setInputs);
+ it('should call .addEventListener', function() {
+ expect(this.hook.list.list.addEventListener).toHaveBeenCalledWith(
+ 'click.dl',
+ this.inputSetter.eventWrapper.setInputs,
+ );
});
});
- describe('removeEvents', function () {
- beforeEach(function () {
+ describe('removeEvents', function() {
+ beforeEach(function() {
this.hook = { list: { list: jasmine.createSpyObj('list', ['removeEventListener']) } };
this.eventWrapper = jasmine.createSpyObj('eventWrapper', ['setInputs']);
this.inputSetter = { eventWrapper: this.eventWrapper, hook: this.hook };
@@ -73,14 +73,16 @@ describe('InputSetter', function () {
InputSetter.removeEvents.call(this.inputSetter);
});
- it('should call .removeEventListener', function () {
- expect(this.hook.list.list.removeEventListener)
- .toHaveBeenCalledWith('click.dl', this.eventWrapper.setInputs);
+ it('should call .removeEventListener', function() {
+ expect(this.hook.list.list.removeEventListener).toHaveBeenCalledWith(
+ 'click.dl',
+ this.eventWrapper.setInputs,
+ );
});
});
- describe('setInputs', function () {
- beforeEach(function () {
+ describe('setInputs', function() {
+ beforeEach(function() {
this.event = { detail: { selected: {} } };
this.config = [0, 1];
this.inputSetter = { config: this.config, setInput: () => {} };
@@ -90,7 +92,7 @@ describe('InputSetter', function () {
InputSetter.setInputs.call(this.inputSetter, this.event);
});
- it('should call .setInput for each config element', function () {
+ it('should call .setInput for each config element', function() {
const allArgs = this.inputSetter.setInput.calls.allArgs();
expect(allArgs.length).toEqual(2);
@@ -101,21 +103,21 @@ describe('InputSetter', function () {
});
});
- describe('if config isnt an array', function () {
- beforeEach(function () {
+ describe('if config isnt an array', function() {
+ beforeEach(function() {
this.inputSetter = { config: {}, setInput: () => {} };
InputSetter.setInputs.call(this.inputSetter, this.event);
});
- it('should set .config to an array with .config as the first element', function () {
+ it('should set .config to an array with .config as the first element', function() {
expect(this.inputSetter.config).toEqual([{}]);
});
});
});
- describe('setInput', function () {
- beforeEach(function () {
+ describe('setInput', function() {
+ beforeEach(function() {
this.selectedItem = { getAttribute: () => {} };
this.input = { value: 'oldValue', tagName: 'INPUT', hasAttribute: () => {} };
this.config = { valueAttribute: {}, input: this.input };
@@ -128,20 +130,20 @@ describe('InputSetter', function () {
InputSetter.setInput.call(this.inputSetter, this.config, this.selectedItem);
});
- it('should call .getAttribute', function () {
+ it('should call .getAttribute', function() {
expect(this.selectedItem.getAttribute).toHaveBeenCalledWith(this.config.valueAttribute);
});
- it('should call .hasAttribute', function () {
+ it('should call .hasAttribute', function() {
expect(this.input.hasAttribute).toHaveBeenCalledWith(undefined);
});
- it('should set the value of the input', function () {
+ it('should set the value of the input', function() {
expect(this.input.value).toBe(this.newValue);
});
- describe('if no config.input is provided', function () {
- beforeEach(function () {
+ describe('if no config.input is provided', function() {
+ beforeEach(function() {
this.config = { valueAttribute: {} };
this.trigger = { value: 'oldValue', tagName: 'INPUT', hasAttribute: () => {} };
this.inputSetter = { hook: { trigger: this.trigger } };
@@ -149,26 +151,26 @@ describe('InputSetter', function () {
InputSetter.setInput.call(this.inputSetter, this.config, this.selectedItem);
});
- it('should set the value of the hook.trigger', function () {
+ it('should set the value of the hook.trigger', function() {
expect(this.trigger.value).toBe(this.newValue);
});
});
- describe('if the input tag is not INPUT', function () {
- beforeEach(function () {
+ describe('if the input tag is not INPUT', function() {
+ beforeEach(function() {
this.input = { textContent: 'oldValue', tagName: 'SPAN', hasAttribute: () => {} };
this.config = { valueAttribute: {}, input: this.input };
InputSetter.setInput.call(this.inputSetter, this.config, this.selectedItem);
});
- it('should set the textContent of the input', function () {
+ it('should set the textContent of the input', function() {
expect(this.input.textContent).toBe(this.newValue);
});
});
- describe('if there is an inputAttribute', function () {
- beforeEach(function () {
+ describe('if there is an inputAttribute', function() {
+ beforeEach(function() {
this.selectedItem = { getAttribute: () => {} };
this.input = { id: 'oldValue', hasAttribute: () => {}, setAttribute: () => {} };
this.inputSetter = { hook: { trigger: {} } };
@@ -187,25 +189,25 @@ describe('InputSetter', function () {
InputSetter.setInput.call(this.inputSetter, this.config, this.selectedItem);
});
- it('should call setAttribute', function () {
+ it('should call setAttribute', function() {
expect(this.input.setAttribute).toHaveBeenCalledWith(this.inputAttribute, this.newValue);
});
- it('should not set the value or textContent of the input', function () {
+ it('should not set the value or textContent of the input', function() {
expect(this.input.value).not.toBe('newValue');
expect(this.input.textContent).not.toBe('newValue');
});
});
});
- describe('destroy', function () {
- beforeEach(function () {
+ describe('destroy', function() {
+ beforeEach(function() {
this.inputSetter = jasmine.createSpyObj('inputSetter', ['removeEvents']);
InputSetter.destroy.call(this.inputSetter);
});
- it('should call .removeEvents', function () {
+ it('should call .removeEvents', function() {
expect(this.inputSetter.removeEvents).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/dropzone_input_spec.js b/spec/javascripts/dropzone_input_spec.js
new file mode 100644
index 00000000000..326b2c029fb
--- /dev/null
+++ b/spec/javascripts/dropzone_input_spec.js
@@ -0,0 +1,66 @@
+import $ from 'jquery';
+import dropzoneInput from '~/dropzone_input';
+import { TEST_HOST } from 'spec/test_constants';
+
+const TEST_FILE = {
+ upload: {},
+};
+const TEST_UPLOAD_PATH = `${TEST_HOST}/upload/file`;
+const TEST_ERROR_MESSAGE = 'A big error occurred!';
+const TEMPLATE = `<form class="gfm-form" data-uploads-path="${TEST_UPLOAD_PATH}">
+ <textarea class="js-gfm-input"></textarea>
+ <div class="uploading-error-message"></div>
+</form>`;
+
+describe('dropzone_input', () => {
+ let form;
+ let dropzone;
+ let xhr;
+ let oldXMLHttpRequest;
+
+ beforeEach(() => {
+ form = $(TEMPLATE);
+
+ dropzone = dropzoneInput(form);
+
+ xhr = jasmine.createSpyObj(Object.keys(XMLHttpRequest.prototype));
+ oldXMLHttpRequest = window.XMLHttpRequest;
+ window.XMLHttpRequest = () => xhr;
+ });
+
+ afterEach(() => {
+ window.XMLHttpRequest = oldXMLHttpRequest;
+ });
+
+ it('shows error message, when AJAX fails with json', () => {
+ xhr = {
+ ...xhr,
+ statusCode: 400,
+ readyState: 4,
+ responseText: JSON.stringify({ message: TEST_ERROR_MESSAGE }),
+ getResponseHeader: () => 'application/json',
+ };
+
+ dropzone.processFile(TEST_FILE);
+
+ xhr.onload();
+
+ expect(form.find('.uploading-error-message').text()).toEqual(TEST_ERROR_MESSAGE);
+ });
+
+ it('shows error message, when AJAX fails with text', () => {
+ xhr = {
+ ...xhr,
+ statusCode: 400,
+ readyState: 4,
+ responseText: TEST_ERROR_MESSAGE,
+ getResponseHeader: () => 'text/plain',
+ };
+
+ dropzone.processFile(TEST_FILE);
+
+ xhr.onload();
+
+ expect(form.find('.uploading-error-message').text()).toEqual(TEST_ERROR_MESSAGE);
+ });
+});
diff --git a/spec/javascripts/emoji_spec.js b/spec/javascripts/emoji_spec.js
index 124d91f4477..3db4d9800f1 100644
--- a/spec/javascripts/emoji_spec.js
+++ b/spec/javascripts/emoji_spec.js
@@ -140,6 +140,7 @@ describe('gl_emoji', () => {
},
);
});
+
it('bomb emoji with sprite fallback', () => {
const emojiKey = 'bomb';
const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, {
@@ -195,24 +196,31 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isFlagEmoji('')).toBeFalsy();
});
+
it('should detect flag_ac', () => {
expect(isFlagEmoji('🇦🇨')).toBeTruthy();
});
+
it('should detect flag_us', () => {
expect(isFlagEmoji('🇺🇸')).toBeTruthy();
});
+
it('should detect flag_zw', () => {
expect(isFlagEmoji('🇿🇼')).toBeTruthy();
});
+
it('should not detect flags', () => {
expect(isFlagEmoji('ðŸŽ')).toBeFalsy();
});
+
it('should not detect triangular_flag_on_post', () => {
expect(isFlagEmoji('🚩')).toBeFalsy();
});
+
it('should not detect single letter', () => {
expect(isFlagEmoji('🇦')).toBeFalsy();
});
+
it('should not detect >2 letters', () => {
expect(isFlagEmoji('🇦🇧🇨')).toBeFalsy();
});
@@ -222,15 +230,19 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isRainbowFlagEmoji('')).toBeFalsy();
});
+
it('should detect rainbow_flag', () => {
expect(isRainbowFlagEmoji('ðŸ³ðŸŒˆ')).toBeTruthy();
});
- it('should not detect flag_white on its\' own', () => {
+
+ it("should not detect flag_white on its' own", () => {
expect(isRainbowFlagEmoji('ðŸ³')).toBeFalsy();
});
- it('should not detect rainbow on its\' own', () => {
+
+ it("should not detect rainbow on its' own", () => {
expect(isRainbowFlagEmoji('🌈')).toBeFalsy();
});
+
it('should not detect flag_white with something else', () => {
expect(isRainbowFlagEmoji('ðŸ³ðŸ”µ')).toBeFalsy();
});
@@ -240,15 +252,19 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isKeycapEmoji('')).toBeFalsy();
});
+
it('should detect one(keycap)', () => {
expect(isKeycapEmoji('1ï¸âƒ£')).toBeTruthy();
});
+
it('should detect nine(keycap)', () => {
expect(isKeycapEmoji('9ï¸âƒ£')).toBeTruthy();
});
+
it('should not detect ten(keycap)', () => {
expect(isKeycapEmoji('🔟')).toBeFalsy();
});
+
it('should not detect hash(keycap)', () => {
expect(isKeycapEmoji('#⃣')).toBeFalsy();
});
@@ -258,24 +274,31 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isSkinToneComboEmoji('')).toBeFalsy();
});
+
it('should detect hand_splayed_tone5', () => {
expect(isSkinToneComboEmoji('ðŸ–ðŸ¿')).toBeTruthy();
});
+
it('should not detect hand_splayed', () => {
expect(isSkinToneComboEmoji('ðŸ–')).toBeFalsy();
});
+
it('should detect lifter_tone1', () => {
expect(isSkinToneComboEmoji('ðŸ‹ðŸ»')).toBeTruthy();
});
+
it('should not detect lifter', () => {
expect(isSkinToneComboEmoji('ðŸ‹')).toBeFalsy();
});
+
it('should detect rowboat_tone4', () => {
expect(isSkinToneComboEmoji('🚣ðŸ¾')).toBeTruthy();
});
+
it('should not detect rowboat', () => {
expect(isSkinToneComboEmoji('🚣')).toBeFalsy();
});
+
it('should not detect individual tone emoji', () => {
expect(isSkinToneComboEmoji('ðŸ»')).toBeFalsy();
});
@@ -285,9 +308,11 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isHorceRacingSkinToneComboEmoji('')).toBeFalsy();
});
+
it('should detect horse_racing_tone2', () => {
expect(isHorceRacingSkinToneComboEmoji('ðŸ‡ðŸ¼')).toBeTruthy();
});
+
it('should not detect horse_racing', () => {
expect(isHorceRacingSkinToneComboEmoji('ðŸ‡')).toBeFalsy();
});
@@ -297,36 +322,47 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isPersonZwjEmoji('')).toBeFalsy();
});
+
it('should detect couple_mm', () => {
expect(isPersonZwjEmoji('👨â€â¤ï¸â€ðŸ‘¨')).toBeTruthy();
});
+
it('should not detect couple_with_heart', () => {
expect(isPersonZwjEmoji('💑')).toBeFalsy();
});
+
it('should not detect couplekiss', () => {
expect(isPersonZwjEmoji('ðŸ’')).toBeFalsy();
});
+
it('should detect family_mmb', () => {
expect(isPersonZwjEmoji('👨â€ðŸ‘¨â€ðŸ‘¦')).toBeTruthy();
});
+
it('should detect family_mwgb', () => {
expect(isPersonZwjEmoji('👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦')).toBeTruthy();
});
+
it('should not detect family', () => {
expect(isPersonZwjEmoji('👪')).toBeFalsy();
});
+
it('should detect kiss_ww', () => {
expect(isPersonZwjEmoji('👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘©')).toBeTruthy();
});
+
it('should not detect girl', () => {
expect(isPersonZwjEmoji('👧')).toBeFalsy();
});
+
it('should not detect girl_tone5', () => {
expect(isPersonZwjEmoji('👧ðŸ¿')).toBeFalsy();
});
+
it('should not detect man', () => {
expect(isPersonZwjEmoji('👨')).toBeFalsy();
});
+
it('should not detect woman', () => {
expect(isPersonZwjEmoji('👩')).toBeFalsy();
});
@@ -334,21 +370,17 @@ describe('gl_emoji', () => {
describe('isEmojiUnicodeSupported', () => {
it('should gracefully handle empty string with unicode support', () => {
- const isSupported = isEmojiUnicodeSupported(
- { '1.0': true },
- '',
- '1.0',
- );
+ const isSupported = isEmojiUnicodeSupported({ '1.0': true }, '', '1.0');
+
expect(isSupported).toBeTruthy();
});
+
it('should gracefully handle empty string without unicode support', () => {
- const isSupported = isEmojiUnicodeSupported(
- {},
- '',
- '1.0',
- );
+ const isSupported = isEmojiUnicodeSupported({}, '', '1.0');
+
expect(isSupported).toBeFalsy();
});
+
it('bomb(6.0) with 6.0 support', () => {
const emojiKey = 'bomb';
const unicodeSupportMap = Object.assign({}, emptySupportMap, {
@@ -359,6 +391,7 @@ describe('gl_emoji', () => {
emojiFixtureMap[emojiKey].moji,
emojiFixtureMap[emojiKey].unicodeVersion,
);
+
expect(isSupported).toBeTruthy();
});
@@ -370,6 +403,7 @@ describe('gl_emoji', () => {
emojiFixtureMap[emojiKey].moji,
emojiFixtureMap[emojiKey].unicodeVersion,
);
+
expect(isSupported).toBeFalsy();
});
@@ -383,6 +417,7 @@ describe('gl_emoji', () => {
emojiFixtureMap[emojiKey].moji,
emojiFixtureMap[emojiKey].unicodeVersion,
);
+
expect(isSupported).toBeFalsy();
});
@@ -408,6 +443,7 @@ describe('gl_emoji', () => {
emojiFixtureMap[emojiKey].moji,
emojiFixtureMap[emojiKey].unicodeVersion,
);
+
expect(isSupported).toBeFalsy();
});
@@ -425,6 +461,7 @@ describe('gl_emoji', () => {
emojiFixtureMap[emojiKey].moji,
emojiFixtureMap[emojiKey].unicodeVersion,
);
+
expect(isSupported).toBeTruthy();
});
@@ -442,6 +479,7 @@ describe('gl_emoji', () => {
emojiFixtureMap[emojiKey].moji,
emojiFixtureMap[emojiKey].unicodeVersion,
);
+
expect(isSupported).toBeFalsy();
});
});
diff --git a/spec/javascripts/environments/emtpy_state_spec.js b/spec/javascripts/environments/emtpy_state_spec.js
index 10a19af4175..1f986d49bc7 100644
--- a/spec/javascripts/environments/emtpy_state_spec.js
+++ b/spec/javascripts/environments/emtpy_state_spec.js
@@ -1,4 +1,3 @@
-
import Vue from 'vue';
import emptyState from '~/environments/components/empty_state.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
@@ -25,13 +24,13 @@ describe('environments empty state', () => {
});
it('renders empty state and new environment button', () => {
- expect(
- vm.$el.querySelector('.js-blank-state-title').textContent.trim(),
- ).toEqual('You don\'t have any environments right now.');
+ expect(vm.$el.querySelector('.js-blank-state-title').textContent.trim()).toEqual(
+ "You don't have any environments right now",
+ );
- expect(
- vm.$el.querySelector('.js-new-environment-button').getAttribute('href'),
- ).toEqual('foo');
+ expect(vm.$el.querySelector('.js-new-environment-button').getAttribute('href')).toEqual(
+ 'foo',
+ );
});
});
@@ -45,13 +44,11 @@ describe('environments empty state', () => {
});
it('renders empty state without new button', () => {
- expect(
- vm.$el.querySelector('.js-blank-state-title').textContent.trim(),
- ).toEqual('You don\'t have any environments right now.');
+ expect(vm.$el.querySelector('.js-blank-state-title').textContent.trim()).toEqual(
+ "You don't have any environments right now",
+ );
- expect(
- vm.$el.querySelector('.js-new-environment-button'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.js-new-environment-button')).toBeNull();
});
});
});
diff --git a/spec/javascripts/environments/environment_actions_spec.js b/spec/javascripts/environments/environment_actions_spec.js
index ea40a1fcd4b..787df757d32 100644
--- a/spec/javascripts/environments/environment_actions_spec.js
+++ b/spec/javascripts/environments/environment_actions_spec.js
@@ -1,15 +1,19 @@
import Vue from 'vue';
-import actionsComp from '~/environments/components/environment_actions.vue';
+import eventHub from '~/environments/event_hub';
+import EnvironmentActions from '~/environments/components/environment_actions.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { TEST_HOST } from 'spec/test_constants';
-describe('Actions Component', () => {
- let ActionsComponent;
- let actionsMock;
- let component;
+describe('EnvironmentActions Component', () => {
+ const Component = Vue.extend(EnvironmentActions);
+ let vm;
- beforeEach(() => {
- ActionsComponent = Vue.extend(actionsComp);
+ afterEach(() => {
+ vm.$destroy();
+ });
- actionsMock = [
+ describe('manual actions', () => {
+ const actions = [
{
name: 'bar',
play_path: 'https://gitlab.com/play',
@@ -25,38 +29,89 @@ describe('Actions Component', () => {
},
];
- component = new ActionsComponent({
- propsData: {
- actions: actionsMock,
- },
- }).$mount();
- });
+ beforeEach(() => {
+ vm = mountComponent(Component, { actions });
+ });
+
+ it('should render a dropdown button with icon and title attribute', () => {
+ expect(vm.$el.querySelector('.fa-caret-down')).toBeDefined();
+ expect(vm.$el.querySelector('.dropdown-new').getAttribute('data-original-title')).toEqual(
+ 'Deploy to...',
+ );
- describe('computed', () => {
- it('title', () => {
- expect(component.title).toEqual('Deploy to...');
+ expect(vm.$el.querySelector('.dropdown-new').getAttribute('aria-label')).toEqual(
+ 'Deploy to...',
+ );
});
- });
- it('should render a dropdown button with icon and title attribute', () => {
- expect(component.$el.querySelector('.fa-caret-down')).toBeDefined();
- expect(component.$el.querySelector('.dropdown-new').getAttribute('data-original-title')).toEqual('Deploy to...');
- expect(component.$el.querySelector('.dropdown-new').getAttribute('aria-label')).toEqual('Deploy to...');
- });
+ it('should render a dropdown with the provided list of actions', () => {
+ expect(vm.$el.querySelectorAll('.dropdown-menu li').length).toEqual(actions.length);
+ });
+
+ it("should render a disabled action when it's not playable", () => {
+ expect(
+ vm.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'),
+ ).toEqual('disabled');
- it('should render a dropdown with the provided list of actions', () => {
- expect(
- component.$el.querySelectorAll('.dropdown-menu li').length,
- ).toEqual(actionsMock.length);
+ expect(
+ vm.$el.querySelector('.dropdown-menu li:last-child button').classList.contains('disabled'),
+ ).toEqual(true);
+ });
});
- it('should render a disabled action when it\'s not playable', () => {
- expect(
- component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'),
- ).toEqual('disabled');
+ describe('scheduled jobs', () => {
+ const scheduledJobAction = {
+ name: 'scheduled action',
+ playPath: `${TEST_HOST}/scheduled/job/action`,
+ playable: true,
+ scheduledAt: '2063-04-05T00:42:00Z',
+ };
+ const expiredJobAction = {
+ name: 'expired action',
+ playPath: `${TEST_HOST}/expired/job/action`,
+ playable: true,
+ scheduledAt: '2018-10-05T08:23:00Z',
+ };
+ const findDropdownItem = action => {
+ const buttons = vm.$el.querySelectorAll('.dropdown-menu li button');
+ return Array.prototype.find.call(buttons, element =>
+ element.innerText.trim().startsWith(action.name),
+ );
+ };
+
+ beforeEach(() => {
+ spyOn(Date, 'now').and.callFake(() => new Date('2063-04-04T00:42:00Z').getTime());
+ vm = mountComponent(Component, { actions: [scheduledJobAction, expiredJobAction] });
+ });
- expect(
- component.$el.querySelector('.dropdown-menu li:last-child button').classList.contains('disabled'),
- ).toEqual(true);
+ it('emits postAction event after confirming', () => {
+ const emitSpy = jasmine.createSpy('emit');
+ eventHub.$on('postAction', emitSpy);
+ spyOn(window, 'confirm').and.callFake(() => true);
+
+ findDropdownItem(scheduledJobAction).click();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(emitSpy).toHaveBeenCalledWith({ endpoint: scheduledJobAction.playPath });
+ });
+
+ it('does not emit postAction event if confirmation is cancelled', () => {
+ const emitSpy = jasmine.createSpy('emit');
+ eventHub.$on('postAction', emitSpy);
+ spyOn(window, 'confirm').and.callFake(() => false);
+
+ findDropdownItem(scheduledJobAction).click();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(emitSpy).not.toHaveBeenCalled();
+ });
+
+ it('displays the remaining time in the dropdown', () => {
+ expect(findDropdownItem(scheduledJobAction)).toContainText('24:00:00');
+ });
+
+ it('displays 00:00:00 for expired jobs in the dropdown', () => {
+ expect(findDropdownItem(expiredJobAction)).toContainText('00:00:00');
+ });
});
});
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index 0b933dda431..7618c2f50ce 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -38,7 +38,9 @@ describe('Environment item', () => {
});
it('Should render the number of children in a badge', () => {
- expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.size);
+ expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(
+ mockItem.size,
+ );
});
});
@@ -68,7 +70,8 @@ describe('Environment item', () => {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
commit: {
@@ -84,7 +87,8 @@ describe('Environment item', () => {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
@@ -121,18 +125,18 @@ describe('Environment item', () => {
});
it('should render environment name', () => {
- expect(component.$el.querySelector('.environment-name').textContent).toContain(environment.name);
+ expect(component.$el.querySelector('.environment-name').textContent).toContain(
+ environment.name,
+ );
});
describe('With deployment', () => {
it('should render deployment internal id', () => {
- expect(
- component.$el.querySelector('.deployment-column span').textContent,
- ).toContain(environment.last_deployment.iid);
+ expect(component.$el.querySelector('.deployment-column span').textContent).toContain(
+ environment.last_deployment.iid,
+ );
- expect(
- component.$el.querySelector('.deployment-column span').textContent,
- ).toContain('#');
+ expect(component.$el.querySelector('.deployment-column span').textContent).toContain('#');
});
it('should render last deployment date', () => {
@@ -156,56 +160,46 @@ describe('Environment item', () => {
describe('With build url', () => {
it('Should link to build url provided', () => {
- expect(
- component.$el.querySelector('.build-link').getAttribute('href'),
- ).toEqual(environment.last_deployment.deployable.build_path);
+ expect(component.$el.querySelector('.build-link').getAttribute('href')).toEqual(
+ environment.last_deployment.deployable.build_path,
+ );
});
it('Should render deployable name and id', () => {
- expect(
- component.$el.querySelector('.build-link').getAttribute('href'),
- ).toEqual(environment.last_deployment.deployable.build_path);
+ expect(component.$el.querySelector('.build-link').getAttribute('href')).toEqual(
+ environment.last_deployment.deployable.build_path,
+ );
});
});
describe('With commit information', () => {
it('should render commit component', () => {
- expect(
- component.$el.querySelector('.js-commit-component'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-commit-component')).toBeDefined();
});
});
});
describe('With manual actions', () => {
it('Should render actions component', () => {
- expect(
- component.$el.querySelector('.js-manual-actions-container'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-manual-actions-container')).toBeDefined();
});
});
describe('With external URL', () => {
it('should render external url component', () => {
- expect(
- component.$el.querySelector('.js-external-url-container'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-external-url-container')).toBeDefined();
});
});
describe('With stop action', () => {
it('Should render stop action component', () => {
- expect(
- component.$el.querySelector('.js-stop-component-container'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-stop-component-container')).toBeDefined();
});
});
describe('With retry action', () => {
it('Should render rollback component', () => {
- expect(
- component.$el.querySelector('.js-rollback-component-container'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-rollback-component-container')).toBeDefined();
});
});
});
diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js
index 6968fbc7ce7..e2d81eb454a 100644
--- a/spec/javascripts/environments/environments_app_spec.js
+++ b/spec/javascripts/environments/environments_app_spec.js
@@ -31,9 +31,9 @@ describe('Environment', () => {
mock.restore();
});
- describe('successfull request', () => {
+ describe('successful request', () => {
describe('without environments', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet(mockData.endpoint).reply(200, { environments: [] });
component = mountComponent(EnvironmentsComponent, mockData);
@@ -44,30 +44,34 @@ describe('Environment', () => {
});
it('should render the empty state', () => {
- expect(
- component.$el.querySelector('.js-new-environment-button').textContent,
- ).toContain('New environment');
+ expect(component.$el.querySelector('.js-new-environment-button').textContent).toContain(
+ 'New environment',
+ );
- expect(
- component.$el.querySelector('.js-blank-state-title').textContent,
- ).toContain('You don\'t have any environments right now.');
+ expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain(
+ "You don't have any environments right now",
+ );
});
});
describe('with paginated environments', () => {
- beforeEach((done) => {
- mock.onGet(mockData.endpoint).reply(200, {
- environments: [environment],
- stopped_count: 1,
- available_count: 0,
- }, {
- 'X-nExt-pAge': '2',
- 'x-page': '1',
- 'X-Per-Page': '1',
- 'X-Prev-Page': '',
- 'X-TOTAL': '37',
- 'X-Total-Pages': '2',
- });
+ beforeEach(done => {
+ mock.onGet(mockData.endpoint).reply(
+ 200,
+ {
+ environments: [environment],
+ stopped_count: 1,
+ available_count: 0,
+ },
+ {
+ 'X-nExt-pAge': '2',
+ 'x-page': '1',
+ 'X-Per-Page': '1',
+ 'X-Prev-Page': '',
+ 'X-TOTAL': '37',
+ 'X-Total-Pages': '2',
+ },
+ );
component = mountComponent(EnvironmentsComponent, mockData);
@@ -78,28 +82,27 @@ describe('Environment', () => {
it('should render a table with environments', () => {
expect(component.$el.querySelectorAll('table')).not.toBeNull();
- expect(
- component.$el.querySelector('.environment-name').textContent.trim(),
- ).toEqual(environment.name);
+ expect(component.$el.querySelector('.environment-name').textContent.trim()).toEqual(
+ environment.name,
+ );
});
describe('pagination', () => {
it('should render pagination', () => {
- expect(
- component.$el.querySelectorAll('.gl-pagination li').length,
- ).toEqual(5);
+ expect(component.$el.querySelectorAll('.gl-pagination li').length).toEqual(5);
});
- it('should make an API request when page is clicked', (done) => {
+ it('should make an API request when page is clicked', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
+
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'available', page: '2' });
done();
}, 0);
});
- it('should make an API request when using tabs', (done) => {
+ it('should make an API request when using tabs', done => {
setTimeout(() => {
spyOn(component, 'updateContent');
component.$el.querySelector('.js-environments-tab-stopped').click();
@@ -113,7 +116,7 @@ describe('Environment', () => {
});
describe('unsuccessfull request', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet(mockData.endpoint).reply(500, {});
component = mountComponent(EnvironmentsComponent, mockData);
@@ -124,15 +127,16 @@ describe('Environment', () => {
});
it('should render empty state', () => {
- expect(
- component.$el.querySelector('.js-blank-state-title').textContent,
- ).toContain('You don\'t have any environments right now.');
+ expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain(
+ "You don't have any environments right now",
+ );
});
});
describe('expandable folders', () => {
beforeEach(() => {
- mock.onGet(mockData.endpoint).reply(200,
+ mock.onGet(mockData.endpoint).reply(
+ 200,
{
environments: [folder],
stopped_count: 0,
@@ -153,23 +157,18 @@ describe('Environment', () => {
component = mountComponent(EnvironmentsComponent, mockData);
});
- it('should open a closed folder', (done) => {
+ it('should open a closed folder', done => {
setTimeout(() => {
component.$el.querySelector('.folder-name').click();
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'),
- ).toContain('display: none');
- expect(
- component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'),
- ).not.toContain('display: none');
+ expect(component.$el.querySelector('.folder-icon.ic-chevron-right')).toBe(null);
done();
});
}, 0);
});
- it('should close an opened folder', (done) => {
+ it('should close an opened folder', done => {
setTimeout(() => {
// open folder
component.$el.querySelector('.folder-name').click();
@@ -179,19 +178,14 @@ describe('Environment', () => {
component.$el.querySelector('.folder-name').click();
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'),
- ).toContain('display: none');
- expect(
- component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'),
- ).not.toContain('display: none');
+ expect(component.$el.querySelector('.folder-icon.ic-chevron-down')).toBe(null);
done();
});
});
}, 0);
});
- it('should show children environments and a button to show all environments', (done) => {
+ it('should show children environments and a button to show all environments', done => {
setTimeout(() => {
// open folder
component.$el.querySelector('.folder-name').click();
@@ -200,7 +194,9 @@ describe('Environment', () => {
// wait for next async request
setTimeout(() => {
expect(component.$el.querySelectorAll('.js-child-row').length).toEqual(1);
- expect(component.$el.querySelector('.text-center > a.btn').textContent).toContain('Show all');
+ expect(component.$el.querySelector('.text-center > a.btn').textContent).toContain(
+ 'Show all',
+ );
done();
});
});
@@ -210,7 +206,8 @@ describe('Environment', () => {
describe('methods', () => {
beforeEach(() => {
- mock.onGet(mockData.endpoint).reply(200,
+ mock.onGet(mockData.endpoint).reply(
+ 200,
{
environments: [],
stopped_count: 0,
@@ -224,8 +221,9 @@ describe('Environment', () => {
});
describe('updateContent', () => {
- it('should set given parameters', (done) => {
- component.updateContent({ scope: 'stopped', page: '3' })
+ it('should set given parameters', done => {
+ component
+ .updateContent({ scope: 'stopped', page: '3' })
.then(() => {
expect(component.page).toEqual('3');
expect(component.scope).toEqual('stopped');
diff --git a/spec/javascripts/environments/environments_store_spec.js b/spec/javascripts/environments/environments_store_spec.js
index f2c6ec24dd7..c3d16f10d72 100644
--- a/spec/javascripts/environments/environments_store_spec.js
+++ b/spec/javascripts/environments/environments_store_spec.js
@@ -17,23 +17,27 @@ describe('Store', () => {
it('should store environments', () => {
store.storeEnvironments(serverData);
+
expect(store.state.environments.length).toEqual(serverData.length);
expect(store.state.environments[0]).toEqual(environmentsList[0]);
});
it('should store available count', () => {
store.storeAvailableCount(2);
+
expect(store.state.availableCounter).toEqual(2);
});
it('should store stopped count', () => {
store.storeStoppedCount(2);
+
expect(store.state.stoppedCounter).toEqual(2);
});
describe('store environments', () => {
it('should store environments', () => {
store.storeEnvironments(serverData);
+
expect(store.state.environments.length).toEqual(serverData.length);
});
@@ -45,6 +49,7 @@ describe('Store', () => {
};
store.storeEnvironments([environment]);
+
expect(store.state.environments[0].isFolder).toEqual(true);
expect(store.state.environments[0].folderName).toEqual('bar');
});
@@ -61,17 +66,20 @@ describe('Store', () => {
};
store.storeEnvironments([environment]);
+
expect(store.state.environments[0].last_deployment).toEqual({});
expect(store.state.environments[0].isStoppable).toEqual(true);
});
it('should store latest.name when the environment is not a folder', () => {
store.storeEnvironments(serverData);
+
expect(store.state.environments[0].name).toEqual(serverData[0].latest.name);
});
it('should store root level name when environment is a folder', () => {
store.storeEnvironments(serverData);
+
expect(store.state.environments[1].folderName).toEqual(serverData[1].name);
});
});
@@ -81,9 +89,11 @@ describe('Store', () => {
store.storeEnvironments(serverData);
store.toggleFolder(store.state.environments[1]);
+
expect(store.state.environments[1].isOpen).toEqual(true);
store.toggleFolder(store.state.environments[1]);
+
expect(store.state.environments[1].isOpen).toEqual(false);
});
@@ -91,9 +101,11 @@ describe('Store', () => {
store.storeEnvironments(serverData);
store.toggleFolder(store.state.environments[1]);
+
expect(store.state.environments[1].isOpen).toEqual(true);
store.storeEnvironments(serverData);
+
expect(store.state.environments[1].isOpen).toEqual(true);
});
});
@@ -116,6 +128,7 @@ describe('Store', () => {
expect(store.state.environments[1].children.length).toEqual(serverData.length);
// poll
store.storeEnvironments(serverData);
+
expect(store.state.environments[1].children.length).toEqual(serverData.length);
});
});
@@ -141,6 +154,7 @@ describe('Store', () => {
};
store.setPagination(pagination);
+
expect(store.state.paginationInformation).toEqual(expectedResult);
});
});
@@ -150,6 +164,7 @@ describe('Store', () => {
store.storeEnvironments(serverData);
store.toggleFolder(store.state.environments[1]);
+
expect(store.getOpenFolders()[0]).toEqual(store.state.environments[1]);
});
});
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index 51d4213c38f..7f0a9475d5f 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -30,39 +30,43 @@ describe('Environments Folder View', () => {
component.$destroy();
});
- describe('successfull request', () => {
+ describe('successful request', () => {
beforeEach(() => {
- mock.onGet(mockData.endpoint).reply(200, {
- environments: environmentsList,
- stopped_count: 1,
- available_count: 0,
- }, {
- 'X-nExt-pAge': '2',
- 'x-page': '1',
- 'X-Per-Page': '2',
- 'X-Prev-Page': '',
- 'X-TOTAL': '20',
- 'X-Total-Pages': '10',
- });
+ mock.onGet(mockData.endpoint).reply(
+ 200,
+ {
+ environments: environmentsList,
+ stopped_count: 1,
+ available_count: 0,
+ },
+ {
+ 'X-nExt-pAge': '2',
+ 'x-page': '1',
+ 'X-Per-Page': '2',
+ 'X-Prev-Page': '',
+ 'X-TOTAL': '20',
+ 'X-Total-Pages': '10',
+ },
+ );
component = mountComponent(Component, mockData);
});
- it('should render a table with environments', (done) => {
+ it('should render a table with environments', done => {
setTimeout(() => {
expect(component.$el.querySelectorAll('table')).not.toBeNull();
- expect(
- component.$el.querySelector('.environment-name').textContent.trim(),
- ).toEqual(environmentsList[0].name);
+ expect(component.$el.querySelector('.environment-name').textContent.trim()).toEqual(
+ environmentsList[0].name,
+ );
done();
}, 0);
});
- it('should render available tab with count', (done) => {
+ it('should render available tab with count', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-environments-tab-available').textContent,
- ).toContain('Available');
+ expect(component.$el.querySelector('.js-environments-tab-available').textContent).toContain(
+ 'Available',
+ );
expect(
component.$el.querySelector('.js-environments-tab-available .badge').textContent,
@@ -71,11 +75,11 @@ describe('Environments Folder View', () => {
}, 0);
});
- it('should render stopped tab with count', (done) => {
+ it('should render stopped tab with count', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-environments-tab-stopped').textContent,
- ).toContain('Stopped');
+ expect(component.$el.querySelector('.js-environments-tab-stopped').textContent).toContain(
+ 'Stopped',
+ );
expect(
component.$el.querySelector('.js-environments-tab-stopped .badge').textContent,
@@ -84,36 +88,37 @@ describe('Environments Folder View', () => {
}, 0);
});
- it('should render parent folder name', (done) => {
+ it('should render parent folder name', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-folder-name').textContent.trim(),
- ).toContain('Environments / review');
+ expect(component.$el.querySelector('.js-folder-name').textContent.trim()).toContain(
+ 'Environments / review',
+ );
done();
}, 0);
});
describe('pagination', () => {
- it('should render pagination', (done) => {
+ it('should render pagination', done => {
setTimeout(() => {
- expect(
- component.$el.querySelectorAll('.gl-pagination'),
- ).not.toBeNull();
+ expect(component.$el.querySelectorAll('.gl-pagination')).not.toBeNull();
done();
}, 0);
});
- it('should make an API request when changing page', (done) => {
+ it('should make an API request when changing page', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
component.$el.querySelector('.gl-pagination .js-last-button a').click();
- expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '10' });
+ expect(component.updateContent).toHaveBeenCalledWith({
+ scope: component.scope,
+ page: '10',
+ });
done();
}, 0);
});
- it('should make an API request when using tabs', (done) => {
+ it('should make an API request when using tabs', done => {
setTimeout(() => {
spyOn(component, 'updateContent');
component.$el.querySelector('.js-environments-tab-stopped').click();
@@ -134,20 +139,18 @@ describe('Environments Folder View', () => {
component = mountComponent(Component, mockData);
});
- it('should not render a table', (done) => {
+ it('should not render a table', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('table'),
- ).toBe(null);
+ expect(component.$el.querySelector('table')).toBe(null);
done();
}, 0);
});
- it('should render available tab with count 0', (done) => {
+ it('should render available tab with count 0', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-environments-tab-available').textContent,
- ).toContain('Available');
+ expect(component.$el.querySelector('.js-environments-tab-available').textContent).toContain(
+ 'Available',
+ );
expect(
component.$el.querySelector('.js-environments-tab-available .badge').textContent,
@@ -156,11 +159,11 @@ describe('Environments Folder View', () => {
}, 0);
});
- it('should render stopped tab with count 0', (done) => {
+ it('should render stopped tab with count 0', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-environments-tab-stopped').textContent,
- ).toContain('Stopped');
+ expect(component.$el.querySelector('.js-environments-tab-stopped').textContent).toContain(
+ 'Stopped',
+ );
expect(
component.$el.querySelector('.js-environments-tab-stopped .badge').textContent,
@@ -181,8 +184,9 @@ describe('Environments Folder View', () => {
});
describe('updateContent', () => {
- it('should set given parameters', (done) => {
- component.updateContent({ scope: 'stopped', page: '4' })
+ it('should set given parameters', done => {
+ component
+ .updateContent({ scope: 'stopped', page: '4' })
.then(() => {
expect(component.page).toEqual('4');
expect(component.scope).toEqual('stopped');
diff --git a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
index 2ab6a0077b5..e5795d4cbb1 100644
--- a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
+++ b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
@@ -1,11 +1,7 @@
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import {
- getSelector,
- dismiss,
- inserted,
-} from '~/feature_highlight/feature_highlight_helper';
+import { getSelector, dismiss, inserted } from '~/feature_highlight/feature_highlight_helper';
import { togglePopover } from '~/shared/popover';
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
@@ -14,7 +10,10 @@ describe('feature highlight helper', () => {
describe('getSelector', () => {
it('returns js-feature-highlight selector', () => {
const highlightId = 'highlightId';
- expect(getSelector(highlightId)).toEqual(`.js-feature-highlight[data-highlight=${highlightId}]`);
+
+ expect(getSelector(highlightId)).toEqual(
+ `.js-feature-highlight[data-highlight=${highlightId}]`,
+ );
});
});
@@ -37,7 +36,7 @@ describe('feature highlight helper', () => {
mock.restore();
});
- it('calls persistent dismissal endpoint', (done) => {
+ it('calls persistent dismissal endpoint', done => {
const spy = jasmine.createSpy('dismiss-endpoint-hit');
mock.onPost('/-/callouts/dismiss').reply(spy);
@@ -59,7 +58,7 @@ describe('feature highlight helper', () => {
});
describe('inserted', () => {
- it('registers click event callback', (done) => {
+ it('registers click event callback', done => {
const context = {
getAttribute: () => 'popoverId',
dataset: {
@@ -67,7 +66,7 @@ describe('feature highlight helper', () => {
},
};
- spyOn($.fn, 'on').and.callFake((event) => {
+ spyOn($.fn, 'on').and.callFake(event => {
expect(event).toEqual('click');
done();
});
diff --git a/spec/javascripts/feature_highlight/feature_highlight_spec.js b/spec/javascripts/feature_highlight/feature_highlight_spec.js
index ec46d4f905a..0a9fba789c3 100644
--- a/spec/javascripts/feature_highlight/feature_highlight_spec.js
+++ b/spec/javascripts/feature_highlight/feature_highlight_spec.js
@@ -50,7 +50,7 @@ describe('feature highlight', () => {
expect(toggleSpy).toHaveBeenCalledWith(jasmine.any(Object), true);
});
- it('setup debounced mouseleave', (done) => {
+ it('setup debounced mouseleave', done => {
const toggleSpy = spyOn(popover.togglePopover, 'call');
$(selector).trigger('mouseleave');
@@ -63,7 +63,10 @@ describe('feature highlight', () => {
it('setup show.bs.popover', () => {
$(selector).trigger('show.bs.popover');
- expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function), { once: true });
+
+ expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function), {
+ once: true,
+ });
});
it('removes disabled attribute', () => {
@@ -73,6 +76,7 @@ describe('feature highlight', () => {
it('displays popover', () => {
expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeFalsy();
$(selector).trigger('mouseenter');
+
expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeTruthy();
});
diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
index d926663fac0..d1742dcedfa 100644
--- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
+++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
@@ -1,9 +1,9 @@
import Vue from 'vue';
import eventHub from '~/filtered_search/event_hub';
import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue';
-import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
-const createComponent = (propsData) => {
+const createComponent = propsData => {
const Component = Vue.extend(RecentSearchesDropdownContent);
return new Component({
@@ -18,14 +18,11 @@ const trimMarkupWhitespace = text => text.replace(/(\n|\s)+/gm, ' ').trim();
describe('RecentSearchesDropdownContent', () => {
const propsDataWithoutItems = {
items: [],
- allowedKeys: FilteredSearchTokenKeys.getKeys(),
+ allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(),
};
const propsDataWithItems = {
- items: [
- 'foo',
- 'author:@root label:~foo bar',
- ],
- allowedKeys: FilteredSearchTokenKeys.getKeys(),
+ items: ['foo', 'author:@root label:~foo bar'],
+ allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(),
};
let vm;
@@ -47,6 +44,7 @@ describe('RecentSearchesDropdownContent', () => {
expect(el.querySelector('.dropdown-info-note')).toBeDefined();
const items = el.querySelectorAll('.filtered-search-history-dropdown-item');
+
expect(items.length).toEqual(propsDataWithoutItems.items.length);
});
});
@@ -65,17 +63,27 @@ describe('RecentSearchesDropdownContent', () => {
it('should render recent search items', () => {
const items = el.querySelectorAll('.filtered-search-history-dropdown-item');
+
expect(items.length).toEqual(propsDataWithItems.items.length);
- expect(trimMarkupWhitespace(items[0].querySelector('.filtered-search-history-dropdown-search-token').textContent)).toEqual('foo');
+ expect(
+ trimMarkupWhitespace(
+ items[0].querySelector('.filtered-search-history-dropdown-search-token').textContent,
+ ),
+ ).toEqual('foo');
const item1Tokens = items[1].querySelectorAll('.filtered-search-history-dropdown-token');
+
expect(item1Tokens.length).toEqual(2);
expect(item1Tokens[0].querySelector('.name').textContent).toEqual('author:');
expect(item1Tokens[0].querySelector('.value').textContent).toEqual('@root');
expect(item1Tokens[1].querySelector('.name').textContent).toEqual('label:');
expect(item1Tokens[1].querySelector('.value').textContent).toEqual('~foo');
- expect(trimMarkupWhitespace(items[1].querySelector('.filtered-search-history-dropdown-search-token').textContent)).toEqual('bar');
+ expect(
+ trimMarkupWhitespace(
+ items[1].querySelector('.filtered-search-history-dropdown-search-token').textContent,
+ ),
+ ).toEqual('bar');
});
});
@@ -132,12 +140,14 @@ describe('RecentSearchesDropdownContent', () => {
it('with items', () => {
vm = createComponent(propsDataWithItems);
const { hasItems } = vm;
+
expect(hasItems).toEqual(true);
});
it('with no items', () => {
vm = createComponent(propsDataWithoutItems);
const { hasItems } = vm;
+
expect(hasItems).toEqual(false);
});
});
@@ -161,6 +171,7 @@ describe('RecentSearchesDropdownContent', () => {
it('emits event', () => {
expect(onRecentSearchesItemSelectedSpy).not.toHaveBeenCalled();
vm.onItemActivated('something');
+
expect(onRecentSearchesItemSelectedSpy).toHaveBeenCalledWith('something');
});
});
@@ -182,6 +193,7 @@ describe('RecentSearchesDropdownContent', () => {
it('emits event', () => {
expect(onRequestClearRecentSearchesSpy).not.toHaveBeenCalled();
vm.onRequestClearRecentSearches({ stopPropagation: () => {} });
+
expect(onRequestClearRecentSearchesSpy).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js
index c37a964975d..e8fcc8592eb 100644
--- a/spec/javascripts/filtered_search/dropdown_user_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_user_spec.js
@@ -1,7 +1,7 @@
import DropdownUtils from '~/filtered_search/dropdown_utils';
import DropdownUser from '~/filtered_search/dropdown_user';
import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer';
-import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
+import IssuableFilteredTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
describe('Dropdown User', () => {
describe('getSearchInput', () => {
@@ -14,7 +14,7 @@ describe('Dropdown User', () => {
spyOn(DropdownUtils, 'getSearchInput').and.callFake(() => {});
dropdownUser = new DropdownUser({
- tokenKeys: FilteredSearchTokenKeys,
+ tokenKeys: IssuableFilteredTokenKeys,
});
});
@@ -28,14 +28,14 @@ describe('Dropdown User', () => {
it('should not return the single quote found in value', () => {
spyOn(FilteredSearchTokenizer, 'processTokens').and.returnValue({
- lastToken: '\'larry boy',
+ lastToken: "'larry boy",
});
expect(dropdownUser.getSearchInput()).toBe('larry boy');
});
});
- describe('config AjaxFilter\'s endpoint', () => {
+ describe("config AjaxFilter's endpoint", () => {
beforeEach(() => {
spyOn(DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
spyOn(DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
@@ -88,10 +88,12 @@ describe('Dropdown User', () => {
});
});
- const findCurrentUserElement = () => authorFilterDropdownElement.querySelector('.js-current-user');
+ const findCurrentUserElement = () =>
+ authorFilterDropdownElement.querySelector('.js-current-user');
it('hides the current user from dropdown', () => {
const currentUserElement = findCurrentUserElement();
+
expect(currentUserElement).not.toBe(null);
dropdown.hideCurrentUser();
@@ -102,6 +104,7 @@ describe('Dropdown User', () => {
it('does nothing if no user is logged in', () => {
const currentUserElement = findCurrentUserElement();
currentUserElement.parentNode.removeChild(currentUserElement);
+
expect(findCurrentUserElement()).toBe(null);
dropdown.hideCurrentUser();
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js
index 3d6dec19eca..6605b0a30d7 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js
@@ -1,6 +1,6 @@
import DropdownUtils from '~/filtered_search/dropdown_utils';
import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager';
-import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
describe('Dropdown Utils', () => {
@@ -10,25 +10,30 @@ describe('Dropdown Utils', () => {
describe('getEscapedText', () => {
it('should return same word when it has no space', () => {
const escaped = DropdownUtils.getEscapedText('textWithoutSpace');
+
expect(escaped).toBe('textWithoutSpace');
});
it('should escape with double quotes', () => {
let escaped = DropdownUtils.getEscapedText('text with space');
+
expect(escaped).toBe('"text with space"');
- escaped = DropdownUtils.getEscapedText('won\'t fix');
+ escaped = DropdownUtils.getEscapedText("won't fix");
+
expect(escaped).toBe('"won\'t fix"');
});
it('should escape with single quotes', () => {
const escaped = DropdownUtils.getEscapedText('won"t fix');
- expect(escaped).toBe('\'won"t fix\'');
+
+ expect(escaped).toBe("'won\"t fix'");
});
it('should escape with single quotes by default', () => {
const escaped = DropdownUtils.getEscapedText('won"t\' fix');
- expect(escaped).toBe('\'won"t\' fix\'');
+
+ expect(escaped).toBe("'won\"t' fix'");
});
});
@@ -50,6 +55,7 @@ describe('Dropdown Utils', () => {
input.value = 'roo';
const updatedItem = DropdownUtils.filterWithSymbol('@', input, item);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
@@ -57,6 +63,7 @@ describe('Dropdown Utils', () => {
input.value = '@roo';
const updatedItem = DropdownUtils.filterWithSymbol('@', input, item);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
@@ -69,6 +76,7 @@ describe('Dropdown Utils', () => {
input.value = '"';
const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
@@ -76,6 +84,7 @@ describe('Dropdown Utils', () => {
input.value = '~"';
const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
@@ -83,6 +92,7 @@ describe('Dropdown Utils', () => {
input.value = '"community con';
const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
@@ -90,34 +100,39 @@ describe('Dropdown Utils', () => {
input.value = '~"community con';
const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote', () => {
- input.value = '\'';
+ input.value = "'";
const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote and symbol', () => {
- input.value = '~\'';
+ input.value = "~'";
const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote and multiple words', () => {
- input.value = '\'community con';
+ input.value = "'community con";
const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote, symbol and multiple words', () => {
- input.value = '~\'community con';
+ input.value = "~'community con";
const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+
expect(updatedItem.droplab_hidden).toBe(false);
});
});
@@ -137,7 +152,7 @@ describe('Dropdown Utils', () => {
`);
input = document.getElementById('test');
- allowedKeys = FilteredSearchTokenKeys.getKeys();
+ allowedKeys = IssuableFilteredSearchTokenKeys.getKeys();
});
function config() {
@@ -152,17 +167,20 @@ describe('Dropdown Utils', () => {
let updatedItem = DropdownUtils.filterHint(config(), {
hint: 'label',
});
+
expect(updatedItem.droplab_hidden).toBe(false);
input.value = 'o';
updatedItem = DropdownUtils.filterHint(config(), {
hint: 'label',
});
+
expect(updatedItem.droplab_hidden).toBe(true);
});
it('should return droplab_hidden false when item has no hint', () => {
const updatedItem = DropdownUtils.filterHint(config(), {}, '');
+
expect(updatedItem.droplab_hidden).toBe(false);
});
@@ -172,6 +190,7 @@ describe('Dropdown Utils', () => {
hint: 'label',
type: 'array',
});
+
expect(updatedItem.droplab_hidden).toBe(false);
});
@@ -180,12 +199,14 @@ describe('Dropdown Utils', () => {
let updatedItem = DropdownUtils.filterHint(config(), {
hint: 'milestone',
});
+
expect(updatedItem.droplab_hidden).toBe(true);
updatedItem = DropdownUtils.filterHint(config(), {
hint: 'milestone',
type: 'string',
});
+
expect(updatedItem.droplab_hidden).toBe(true);
});
});
@@ -205,6 +226,7 @@ describe('Dropdown Utils', () => {
};
const updated = DropdownUtils.mergeDuplicateLabels(dataMap, newLabel);
+
expect(updated[newLabel.title]).toEqual(newLabel);
});
@@ -215,6 +237,7 @@ describe('Dropdown Utils', () => {
};
const updated = DropdownUtils.mergeDuplicateLabels(dataMap, duplicate);
+
expect(updated.label.multipleColors).toEqual([dataMap.label.color, duplicate.color]);
});
});
@@ -222,39 +245,64 @@ describe('Dropdown Utils', () => {
describe('duplicateLabelColor', () => {
it('should linear-gradient 2 colors', () => {
const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']);
- expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 50%, #000000 50%, #000000 100%)');
+
+ expect(gradient).toEqual(
+ 'linear-gradient(#FFFFFF 0%, #FFFFFF 50%, #000000 50%, #000000 100%)',
+ );
});
it('should linear-gradient 3 colors', () => {
const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333']);
- expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 33%, #000000 33%, #000000 66%, #333333 66%, #333333 100%)');
+
+ expect(gradient).toEqual(
+ 'linear-gradient(#FFFFFF 0%, #FFFFFF 33%, #000000 33%, #000000 66%, #333333 66%, #333333 100%)',
+ );
});
it('should linear-gradient 4 colors', () => {
- const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD']);
- expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 25%, #000000 25%, #000000 50%, #333333 50%, #333333 75%, #DDDDDD 75%, #DDDDDD 100%)');
+ const gradient = DropdownUtils.duplicateLabelColor([
+ '#FFFFFF',
+ '#000000',
+ '#333333',
+ '#DDDDDD',
+ ]);
+
+ expect(gradient).toEqual(
+ 'linear-gradient(#FFFFFF 0%, #FFFFFF 25%, #000000 25%, #000000 50%, #333333 50%, #333333 75%, #DDDDDD 75%, #DDDDDD 100%)',
+ );
});
it('should not linear-gradient more than 4 colors', () => {
- const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD', '#EEEEEE']);
- expect(gradient.indexOf('#EEEEEE') === -1).toEqual(true);
+ const gradient = DropdownUtils.duplicateLabelColor([
+ '#FFFFFF',
+ '#000000',
+ '#333333',
+ '#DDDDDD',
+ '#EEEEEE',
+ ]);
+
+ expect(gradient.indexOf('#EEEEEE')).toBe(-1);
});
});
describe('duplicateLabelPreprocessing', () => {
it('should set preprocessed to true', () => {
const results = DropdownUtils.duplicateLabelPreprocessing([]);
+
expect(results.preprocessed).toEqual(true);
});
it('should not mutate existing data if there are no duplicates', () => {
- const data = [{
- title: 'label1',
- color: '#FFFFFF',
- }, {
- title: 'label2',
- color: '#000000',
- }];
+ const data = [
+ {
+ title: 'label1',
+ color: '#FFFFFF',
+ },
+ {
+ title: 'label2',
+ color: '#000000',
+ },
+ ];
const results = DropdownUtils.duplicateLabelPreprocessing(data);
expect(results.length).toEqual(2);
@@ -263,13 +311,16 @@ describe('Dropdown Utils', () => {
});
describe('duplicate labels', () => {
- const data = [{
- title: 'label',
- color: '#FFFFFF',
- }, {
- title: 'label',
- color: '#000000',
- }];
+ const data = [
+ {
+ title: 'label',
+ color: '#FFFFFF',
+ },
+ {
+ title: 'label',
+ color: '#000000',
+ },
+ ];
const results = DropdownUtils.duplicateLabelPreprocessing(data);
it('should merge duplicate labels', () => {
@@ -288,25 +339,28 @@ describe('Dropdown Utils', () => {
describe('setDataValueIfSelected', () => {
beforeEach(() => {
- spyOn(FilteredSearchDropdownManager, 'addWordToInput')
- .and.callFake(() => {});
+ spyOn(FilteredSearchDropdownManager, 'addWordToInput').and.callFake(() => {});
});
it('calls addWordToInput when dataValue exists', () => {
const selected = {
getAttribute: () => 'value',
+ hasAttribute: () => false,
};
DropdownUtils.setDataValueIfSelected(null, selected);
+
expect(FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1);
});
it('returns true when dataValue exists', () => {
const selected = {
getAttribute: () => 'value',
+ hasAttribute: () => false,
};
const result = DropdownUtils.setDataValueIfSelected(null, selected);
+
expect(result).toBe(true);
});
@@ -316,6 +370,7 @@ describe('Dropdown Utils', () => {
};
const result = DropdownUtils.setDataValueIfSelected(null, selected);
+
expect(result).toBe(false);
});
});
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 8fcee36beb8..e076120f5cc 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -1,7 +1,7 @@
import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
-import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import '~/lib/utils/common_utils';
import DropdownUtils from '~/filtered_search/dropdown_utils';
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
@@ -9,7 +9,7 @@ import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dro
import FilteredSearchManager from '~/filtered_search/filtered_search_manager';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
-describe('Filtered Search Manager', function () {
+describe('Filtered Search Manager', function() {
let input;
let manager;
let tokensContainer;
@@ -86,7 +86,7 @@ describe('Filtered Search Manager', function () {
expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
expect(RecentSearchesStoreSpy).toHaveBeenCalledWith({
isLocalStorageAvailable,
- allowedKeys: FilteredSearchTokenKeys.getKeys(),
+ allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(),
});
});
});
@@ -97,7 +97,9 @@ describe('Filtered Search Manager', function () {
});
it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => {
- spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => Promise.reject(new RecentSearchesServiceError()));
+ spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() =>
+ Promise.reject(new RecentSearchesServiceError()),
+ );
spyOn(window, 'Flash');
manager.setup();
@@ -134,6 +136,7 @@ describe('Filtered Search Manager', function () {
};
manager.searchState(e);
+
expect(FilteredSearchManager.prototype.search).not.toHaveBeenCalled();
});
@@ -149,6 +152,7 @@ describe('Filtered Search Manager', function () {
};
manager.searchState(e);
+
expect(FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened');
});
});
@@ -160,10 +164,10 @@ describe('Filtered Search Manager', function () {
initializeManager();
});
- it('should search with a single word', (done) => {
+ it('should search with a single word', done => {
input.value = 'searchTerm';
- spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => {
expect(url).toEqual(`${defaultParams}&search=searchTerm`);
done();
});
@@ -171,10 +175,10 @@ describe('Filtered Search Manager', function () {
manager.search();
});
- it('should search with multiple words', (done) => {
+ it('should search with multiple words', done => {
input.value = 'awesome search terms';
- spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => {
expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
done();
});
@@ -182,24 +186,26 @@ describe('Filtered Search Manager', function () {
manager.search();
});
- it('should search with special characters', (done) => {
+ it('should search with special characters', done => {
input.value = '~!@#$%^&*()_+{}:<>,.?/';
- spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => {
- expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
+ spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => {
+ expect(url).toEqual(
+ `${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`,
+ );
done();
});
manager.search();
});
- it('removes duplicated tokens', (done) => {
+ it('removes duplicated tokens', done => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
`);
- spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => {
expect(url).toEqual(`${defaultParams}&label_name[]=bug`);
done();
});
@@ -304,6 +310,7 @@ describe('Filtered Search Manager', function () {
);
tokensContainer.querySelector('.js-visual-token .remove-token').click();
+
expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null);
});
@@ -437,12 +444,18 @@ describe('Filtered Search Manager', function () {
it('toggles on focus', () => {
input.focus();
- expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(true);
+
+ expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(
+ true,
+ );
});
it('toggles on blur', () => {
input.blur();
- expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(false);
+
+ expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(
+ false,
+ );
});
});
@@ -454,9 +467,12 @@ describe('Filtered Search Manager', function () {
});
it('correctly modifies params when custom modifier is passed', () => {
- const modifedParams = manager.getAllParams.call({
- modifyUrlParams: paramsArr => paramsArr.reverse(),
- }, [].concat(this.paramsArr));
+ const modifedParams = manager.getAllParams.call(
+ {
+ modifyUrlParams: paramsArr => paramsArr.reverse(),
+ },
+ [].concat(this.paramsArr),
+ );
expect(modifedParams[0]).toBe(this.paramsArr[1]);
});
diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
index 68158cf52e4..d1fea18dea8 100644
--- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
@@ -1,26 +1,39 @@
import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
describe('Filtered Search Token Keys', () => {
- describe('get', () => {
- let tokenKeys;
-
- beforeEach(() => {
- tokenKeys = FilteredSearchTokenKeys.get();
- });
+ const tokenKeys = [
+ {
+ key: 'author',
+ type: 'string',
+ param: 'username',
+ symbol: '@',
+ icon: 'pencil',
+ tag: '@author',
+ },
+ ];
+
+ const conditions = [
+ {
+ url: 'assignee_id=0',
+ tokenKey: 'assignee',
+ value: 'none',
+ },
+ ];
+ describe('get', () => {
it('should return tokenKeys', () => {
- expect(tokenKeys !== null).toBe(true);
+ expect(new FilteredSearchTokenKeys().get()).not.toBeNull();
});
it('should return tokenKeys as an array', () => {
- expect(tokenKeys instanceof Array).toBe(true);
+ expect(new FilteredSearchTokenKeys().get() instanceof Array).toBe(true);
});
});
describe('getKeys', () => {
it('should return keys', () => {
- const getKeys = FilteredSearchTokenKeys.getKeys();
- const keys = FilteredSearchTokenKeys.get().map(i => i.key);
+ const getKeys = new FilteredSearchTokenKeys(tokenKeys).getKeys();
+ const keys = new FilteredSearchTokenKeys(tokenKeys).get().map(i => i.key);
keys.forEach((key, i) => {
expect(key).toEqual(getKeys[i]);
@@ -29,89 +42,99 @@ describe('Filtered Search Token Keys', () => {
});
describe('getConditions', () => {
- let conditions;
-
- beforeEach(() => {
- conditions = FilteredSearchTokenKeys.getConditions();
- });
-
it('should return conditions', () => {
- expect(conditions !== null).toBe(true);
+ expect(new FilteredSearchTokenKeys().getConditions()).not.toBeNull();
});
it('should return conditions as an array', () => {
- expect(conditions instanceof Array).toBe(true);
+ expect(new FilteredSearchTokenKeys().getConditions() instanceof Array).toBe(true);
});
});
describe('searchByKey', () => {
it('should return null when key not found', () => {
- const tokenKey = FilteredSearchTokenKeys.searchByKey('notakey');
- expect(tokenKey === null).toBe(true);
+ const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchByKey('notakey');
+
+ expect(tokenKey).toBeNull();
});
it('should return tokenKey when found by key', () => {
- const tokenKeys = FilteredSearchTokenKeys.get();
- const result = FilteredSearchTokenKeys.searchByKey(tokenKeys[0].key);
+ const result = new FilteredSearchTokenKeys(tokenKeys).searchByKey(tokenKeys[0].key);
+
expect(result).toEqual(tokenKeys[0]);
});
});
describe('searchBySymbol', () => {
it('should return null when symbol not found', () => {
- const tokenKey = FilteredSearchTokenKeys.searchBySymbol('notasymbol');
- expect(tokenKey === null).toBe(true);
+ const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchBySymbol('notasymbol');
+
+ expect(tokenKey).toBeNull();
});
it('should return tokenKey when found by symbol', () => {
- const tokenKeys = FilteredSearchTokenKeys.get();
- const result = FilteredSearchTokenKeys.searchBySymbol(tokenKeys[0].symbol);
+ const result = new FilteredSearchTokenKeys(tokenKeys).searchBySymbol(tokenKeys[0].symbol);
+
expect(result).toEqual(tokenKeys[0]);
});
});
describe('searchByKeyParam', () => {
it('should return null when key param not found', () => {
- const tokenKey = FilteredSearchTokenKeys.searchByKeyParam('notakeyparam');
- expect(tokenKey === null).toBe(true);
+ const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam('notakeyparam');
+
+ expect(tokenKey).toBeNull();
});
it('should return tokenKey when found by key param', () => {
- const tokenKeys = FilteredSearchTokenKeys.get();
- const result = FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
+ const result = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam(
+ `${tokenKeys[0].key}_${tokenKeys[0].param}`,
+ );
+
expect(result).toEqual(tokenKeys[0]);
});
it('should return alternative tokenKey when found by key param', () => {
- const tokenKeys = FilteredSearchTokenKeys.getAlternatives();
- const result = FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
+ const result = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam(
+ `${tokenKeys[0].key}_${tokenKeys[0].param}`,
+ );
+
expect(result).toEqual(tokenKeys[0]);
});
});
describe('searchByConditionUrl', () => {
it('should return null when condition url not found', () => {
- const condition = FilteredSearchTokenKeys.searchByConditionUrl(null);
- expect(condition === null).toBe(true);
+ const condition = new FilteredSearchTokenKeys([], [], conditions).searchByConditionUrl(null);
+
+ expect(condition).toBeNull();
});
it('should return condition when found by url', () => {
- const conditions = FilteredSearchTokenKeys.getConditions();
- const result = FilteredSearchTokenKeys.searchByConditionUrl(conditions[0].url);
+ const result = new FilteredSearchTokenKeys([], [], conditions).searchByConditionUrl(
+ conditions[0].url,
+ );
+
expect(result).toBe(conditions[0]);
});
});
describe('searchByConditionKeyValue', () => {
it('should return null when condition tokenKey and value not found', () => {
- const condition = FilteredSearchTokenKeys.searchByConditionKeyValue(null, null);
- expect(condition === null).toBe(true);
+ const condition = new FilteredSearchTokenKeys([], [], conditions).searchByConditionKeyValue(
+ null,
+ null,
+ );
+
+ expect(condition).toBeNull();
});
it('should return condition when found by tokenKey and value', () => {
- const conditions = FilteredSearchTokenKeys.getConditions();
- const result = FilteredSearchTokenKeys
- .searchByConditionKeyValue(conditions[0].tokenKey, conditions[0].value);
+ const result = new FilteredSearchTokenKeys([], [], conditions).searchByConditionKeyValue(
+ conditions[0].tokenKey,
+ conditions[0].value,
+ );
+
expect(result).toEqual(conditions[0]);
});
});
diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
index 465f5f79931..dec03e5ab93 100644
--- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
@@ -1,20 +1,24 @@
-import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
+import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer';
describe('Filtered Search Tokenizer', () => {
- const allowedKeys = FilteredSearchTokenKeys.getKeys();
+ const allowedKeys = IssuableFilteredSearchTokenKeys.getKeys();
describe('processTokens', () => {
it('returns for input containing only search value', () => {
const results = FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys);
+
expect(results.searchToken).toBe('searchTerm');
expect(results.tokens.length).toBe(0);
expect(results.lastToken).toBe(results.searchToken);
});
it('returns for input containing only tokens', () => {
- const results = FilteredSearchTokenizer
- .processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens(
+ 'author:@root label:~"Very Important" milestone:%v1.0 assignee:none',
+ allowedKeys,
+ );
+
expect(results.searchToken).toBe('');
expect(results.tokens.length).toBe(4);
expect(results.tokens[3]).toBe(results.lastToken);
@@ -37,8 +41,11 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns for input starting with search value and ending with tokens', () => {
- const results = FilteredSearchTokenizer
- .processTokens('searchTerm anotherSearchTerm milestone:none', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens(
+ 'searchTerm anotherSearchTerm milestone:none',
+ allowedKeys,
+ );
+
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(1);
expect(results.tokens[0]).toBe(results.lastToken);
@@ -48,8 +55,10 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns for input starting with tokens and ending with search value', () => {
- const results = FilteredSearchTokenizer
- .processTokens('assignee:@user searchTerm', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens(
+ 'assignee:@user searchTerm',
+ allowedKeys,
+ );
expect(results.searchToken).toBe('searchTerm');
expect(results.tokens.length).toBe(1);
@@ -60,8 +69,10 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns for input containing search value wrapped between tokens', () => {
- const results = FilteredSearchTokenizer
- .processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens(
+ 'author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none',
+ allowedKeys,
+ );
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(3);
@@ -81,8 +92,11 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns for input containing search value in between tokens', () => {
- const results = FilteredSearchTokenizer
- .processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens(
+ 'author:@root searchTerm assignee:none anotherSearchTerm label:~Doing',
+ allowedKeys,
+ );
+
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(3);
expect(results.tokens[2]).toBe(results.lastToken);
@@ -102,6 +116,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns search value for invalid tokens', () => {
const results = FilteredSearchTokenizer.processTokens('fake:token', allowedKeys);
+
expect(results.lastToken).toBe('fake:token');
expect(results.searchToken).toBe('fake:token');
expect(results.tokens.length).toEqual(0);
@@ -109,6 +124,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns search value and token for mix of valid and invalid tokens', () => {
const results = FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys);
+
expect(results.tokens.length).toEqual(1);
expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('real');
@@ -119,12 +135,14 @@ describe('Filtered Search Tokenizer', () => {
it('returns search value for invalid symbols', () => {
const results = FilteredSearchTokenizer.processTokens('std::includes', allowedKeys);
+
expect(results.lastToken).toBe('std::includes');
expect(results.searchToken).toBe('std::includes');
});
it('removes duplicated values', () => {
const results = FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys);
+
expect(results.tokens.length).toBe(1);
expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('foo');
diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
index 756a654765b..4f561df7943 100644
--- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
@@ -9,7 +9,7 @@ import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
describe('Filtered Search Visual Tokens', () => {
const subject = FilteredSearchVisualTokens;
- const findElements = (tokenElement) => {
+ const findElements = tokenElement => {
const tokenNameElement = tokenElement.querySelector('.name');
const tokenValueContainer = tokenElement.querySelector('.value-container');
const tokenValueElement = tokenValueContainer.querySelector('.value');
@@ -34,8 +34,7 @@ describe('Filtered Search Visual Tokens', () => {
describe('getLastVisualTokenBeforeInput', () => {
it('returns when there are no visual tokens', () => {
- const { lastVisualToken, isLastVisualTokenValid }
- = subject.getLastVisualTokenBeforeInput();
+ const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput();
expect(lastVisualToken).toEqual(null);
expect(isLastVisualTokenValid).toEqual(true);
@@ -47,8 +46,7 @@ describe('Filtered Search Visual Tokens', () => {
bugLabelToken.outerHTML,
);
- const { lastVisualToken, isLastVisualTokenValid }
- = subject.getLastVisualTokenBeforeInput();
+ const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput();
expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token'));
expect(isLastVisualTokenValid).toEqual(true);
@@ -59,8 +57,7 @@ describe('Filtered Search Visual Tokens', () => {
FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('Author'),
);
- const { lastVisualToken, isLastVisualTokenValid }
- = subject.getLastVisualTokenBeforeInput();
+ const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput();
expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token'));
expect(isLastVisualTokenValid).toEqual(false);
@@ -73,8 +70,7 @@ describe('Filtered Search Visual Tokens', () => {
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '@root')}
`);
- const { lastVisualToken, isLastVisualTokenValid }
- = subject.getLastVisualTokenBeforeInput();
+ const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput();
const items = document.querySelectorAll('.tokens-container .js-visual-token');
expect(lastVisualToken.isEqualNode(items[items.length - 1])).toEqual(true);
@@ -88,8 +84,7 @@ describe('Filtered Search Visual Tokens', () => {
${FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('assignee')}
`);
- const { lastVisualToken, isLastVisualTokenValid }
- = subject.getLastVisualTokenBeforeInput();
+ const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput();
const items = document.querySelectorAll('.tokens-container .js-visual-token');
expect(lastVisualToken.isEqualNode(items[items.length - 1])).toEqual(true);
@@ -105,8 +100,7 @@ describe('Filtered Search Visual Tokens', () => {
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '@root')}
`);
- const { lastVisualToken, isLastVisualTokenValid }
- = subject.getLastVisualTokenBeforeInput();
+ const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput();
expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token'));
expect(isLastVisualTokenValid).toEqual(true);
@@ -119,8 +113,7 @@ describe('Filtered Search Visual Tokens', () => {
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '@root')}
`);
- const { lastVisualToken, isLastVisualTokenValid }
- = subject.getLastVisualTokenBeforeInput();
+ const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput();
expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token'));
expect(isLastVisualTokenValid).toEqual(false);
@@ -131,6 +124,7 @@ describe('Filtered Search Visual Tokens', () => {
describe('getEndpointWithQueryParams', () => {
it('returns `endpoint` string as is when second param `endpointQueryParams` is undefined, null or empty string', () => {
const endpoint = 'foo/bar/labels.json';
+
expect(subject.getEndpointWithQueryParams(endpoint)).toBe(endpoint);
expect(subject.getEndpointWithQueryParams(endpoint, null)).toBe(endpoint);
expect(subject.getEndpointWithQueryParams(endpoint, '')).toBe(endpoint);
@@ -141,8 +135,13 @@ describe('Filtered Search Visual Tokens', () => {
const singleQueryParams = '{"foo":"true"}';
const multipleQueryParams = '{"foo":"true","bar":"true"}';
- expect(subject.getEndpointWithQueryParams(endpoint, singleQueryParams)).toBe(`${endpoint}?foo=true`);
- expect(subject.getEndpointWithQueryParams(endpoint, multipleQueryParams)).toBe(`${endpoint}?foo=true&bar=true`);
+ expect(subject.getEndpointWithQueryParams(endpoint, singleQueryParams)).toBe(
+ `${endpoint}?foo=true`,
+ );
+
+ expect(subject.getEndpointWithQueryParams(endpoint, multipleQueryParams)).toBe(
+ `${endpoint}?foo=true&bar=true`,
+ );
});
});
@@ -161,6 +160,7 @@ describe('Filtered Search Visual Tokens', () => {
`);
const selected = tokensContainer.querySelector('.js-visual-token .selected');
+
expect(selected.classList.contains('selected')).toEqual(true);
subject.unselectTokens();
@@ -240,13 +240,17 @@ describe('Filtered Search Visual Tokens', () => {
beforeEach(() => {
setFixtures(`
<div class="test-area">
- ${subject.createVisualTokenElementHTML()}
+ ${subject.createVisualTokenElementHTML('custom-token')}
</div>
`);
tokenElement = document.querySelector('.test-area').firstElementChild;
});
+ it('should add class name to token element', () => {
+ expect(document.querySelector('.test-area .custom-token')).toBeDefined();
+ });
+
it('contains name div', () => {
expect(tokenElement.querySelector('.name')).toEqual(jasmine.anything());
});
@@ -269,7 +273,9 @@ describe('Filtered Search Visual Tokens', () => {
describe('remove token', () => {
it('contains remove-token button', () => {
- expect(tokenElement.querySelector('.value-container .remove-token')).toEqual(jasmine.anything());
+ expect(tokenElement.querySelector('.value-container .remove-token')).toEqual(
+ jasmine.anything(),
+ );
});
it('contains fa-close icon', () => {
@@ -280,7 +286,7 @@ describe('Filtered Search Visual Tokens', () => {
describe('addVisualTokenElement', () => {
it('renders search visual tokens', () => {
- subject.addVisualTokenElement('search term', null, true);
+ subject.addVisualTokenElement('search term', null, { isSearchTerm: true });
const token = tokensContainer.querySelector('.js-visual-token');
expect(token.classList.contains('filtered-search-term')).toEqual(true);
@@ -307,7 +313,9 @@ describe('Filtered Search Visual Tokens', () => {
});
it('inserts visual token before input', () => {
- tokensContainer.appendChild(FilteredSearchSpecHelper.createFilterVisualToken('assignee', '@root'));
+ tokensContainer.appendChild(
+ FilteredSearchSpecHelper.createFilterVisualToken('assignee', '@root'),
+ );
subject.addVisualTokenElement('label', 'Frontend');
const tokens = tokensContainer.querySelectorAll('.js-visual-token');
@@ -546,7 +554,7 @@ describe('Filtered Search Visual Tokens', () => {
token = document.querySelector('.js-visual-token');
});
- it('tokenize\'s existing input', () => {
+ it("tokenize's existing input", () => {
input.value = 'some text';
spyOn(subject, 'tokenizeInput').and.callThrough();
@@ -619,7 +627,7 @@ describe('Filtered Search Visual Tokens', () => {
expect(subject.getLastVisualTokenBeforeInput).not.toHaveBeenCalled();
});
- it('tokenize\'s input', () => {
+ it("tokenize's input", () => {
tokensContainer.innerHTML = `
${FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('label')}
${FilteredSearchSpecHelper.createInputHTML()}
@@ -671,13 +679,17 @@ describe('Filtered Search Visual Tokens', () => {
subject.moveInputToTheRight();
const token = tokensContainer.children[1];
+
expect(token.querySelector('.value').innerText).toEqual('~bug');
});
});
describe('renderVisualTokenValue', () => {
const keywordToken = FilteredSearchSpecHelper.createFilterVisualToken('search');
- const milestoneToken = FilteredSearchSpecHelper.createFilterVisualToken('milestone', 'upcoming');
+ const milestoneToken = FilteredSearchSpecHelper.createFilterVisualToken(
+ 'milestone',
+ 'upcoming',
+ );
let updateLabelTokenColorSpy;
let updateUserTokenAppearanceSpy;
@@ -698,8 +710,9 @@ describe('Filtered Search Visual Tokens', () => {
});
it('renders a author token value element', () => {
- const { tokenNameElement, tokenValueContainer, tokenValueElement } =
- findElements(authorToken);
+ const { tokenNameElement, tokenValueContainer, tokenValueElement } = findElements(
+ authorToken,
+ );
const tokenName = tokenNameElement.innerText;
const tokenValue = 'new value';
@@ -708,13 +721,15 @@ describe('Filtered Search Visual Tokens', () => {
expect(tokenValueElement.innerText).toBe(tokenValue);
expect(updateUserTokenAppearanceSpy.calls.count()).toBe(1);
const expectedArgs = [tokenValueContainer, tokenValueElement, tokenValue];
+
expect(updateUserTokenAppearanceSpy.calls.argsFor(0)).toEqual(expectedArgs);
expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
});
it('renders a label token value element', () => {
- const { tokenNameElement, tokenValueContainer, tokenValueElement } =
- findElements(bugLabelToken);
+ const { tokenNameElement, tokenValueContainer, tokenValueElement } = findElements(
+ bugLabelToken,
+ );
const tokenName = tokenNameElement.innerText;
const tokenValue = 'new value';
@@ -723,6 +738,7 @@ describe('Filtered Search Visual Tokens', () => {
expect(tokenValueElement.innerText).toBe(tokenValue);
expect(updateLabelTokenColorSpy.calls.count()).toBe(1);
const expectedArgs = [tokenValueContainer, tokenValue];
+
expect(updateLabelTokenColorSpy.calls.argsFor(0)).toEqual(expectedArgs);
expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
});
@@ -738,6 +754,50 @@ describe('Filtered Search Visual Tokens', () => {
expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
});
+
+ it('does not update user token appearance for `none` filter', () => {
+ const { tokenNameElement } = findElements(authorToken);
+
+ const tokenName = tokenNameElement.innerText;
+ const tokenValue = 'none';
+
+ subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
+
+ expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update user token appearance for `any` filter', () => {
+ const { tokenNameElement } = findElements(authorToken);
+
+ const tokenName = tokenNameElement.innerText;
+ const tokenValue = 'any';
+
+ subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
+
+ expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update label token color for `none` filter', () => {
+ const { tokenNameElement } = findElements(bugLabelToken);
+
+ const tokenName = tokenNameElement.innerText;
+ const tokenValue = 'none';
+
+ subject.renderVisualTokenValue(bugLabelToken, tokenName, tokenValue);
+
+ expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update label token color for `any` filter', () => {
+ const { tokenNameElement } = findElements(bugLabelToken);
+
+ const tokenName = tokenNameElement.innerText;
+ const tokenValue = 'any';
+
+ subject.renderVisualTokenValue(bugLabelToken, tokenName, tokenValue);
+
+ expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
+ });
});
describe('updateUserTokenAppearance', () => {
@@ -747,96 +807,90 @@ describe('Filtered Search Visual Tokens', () => {
spyOn(UsersCache, 'retrieve').and.callFake(username => usersCacheSpy(username));
});
- it('ignores special value "none"', (done) => {
- usersCacheSpy = (username) => {
- expect(username).toBe('none');
- done.fail('Should not resolve "none"!');
- };
- const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
-
- subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, 'none')
- .then(done)
- .catch(done.fail);
- });
-
- it('ignores error if UsersCache throws', (done) => {
+ it('ignores error if UsersCache throws', done => {
spyOn(window, 'Flash');
const dummyError = new Error('Earth rotated backwards');
const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
const tokenValue = tokenValueElement.innerText;
- usersCacheSpy = (username) => {
+ usersCacheSpy = username => {
expect(`@${username}`).toBe(tokenValue);
return Promise.reject(dummyError);
};
- subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
- .then(() => {
- expect(window.Flash.calls.count()).toBe(0);
- })
- .then(done)
- .catch(done.fail);
+ subject
+ .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
+ .then(() => {
+ expect(window.Flash.calls.count()).toBe(0);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('does nothing if user cannot be found', (done) => {
+ it('does nothing if user cannot be found', done => {
const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
const tokenValue = tokenValueElement.innerText;
- usersCacheSpy = (username) => {
+ usersCacheSpy = username => {
expect(`@${username}`).toBe(tokenValue);
return Promise.resolve(undefined);
};
- subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
- .then(() => {
- expect(tokenValueElement.innerText).toBe(tokenValue);
- })
- .then(done)
- .catch(done.fail);
+ subject
+ .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
+ .then(() => {
+ expect(tokenValueElement.innerText).toBe(tokenValue);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('replaces author token with avatar and display name', (done) => {
+ it('replaces author token with avatar and display name', done => {
const dummyUser = {
name: 'Important Person',
avatar_url: 'https://host.invalid/mypics/avatar.png',
};
const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
const tokenValue = tokenValueElement.innerText;
- usersCacheSpy = (username) => {
+ usersCacheSpy = username => {
expect(`@${username}`).toBe(tokenValue);
return Promise.resolve(dummyUser);
};
- subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
- .then(() => {
- expect(tokenValueContainer.dataset.originalValue).toBe(tokenValue);
- expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
- const avatar = tokenValueElement.querySelector('img.avatar');
- expect(avatar.src).toBe(dummyUser.avatar_url);
- expect(avatar.alt).toBe('');
- })
- .then(done)
- .catch(done.fail);
+ subject
+ .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
+ .then(() => {
+ expect(tokenValueContainer.dataset.originalValue).toBe(tokenValue);
+ expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
+ const avatar = tokenValueElement.querySelector('img.avatar');
+
+ expect(avatar.src).toBe(dummyUser.avatar_url);
+ expect(avatar.alt).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('escapes user name when creating token', (done) => {
+ it('escapes user name when creating token', done => {
const dummyUser = {
name: '<script>',
avatar_url: `${gl.TEST_HOST}/mypics/avatar.png`,
};
const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
const tokenValue = tokenValueElement.innerText;
- usersCacheSpy = (username) => {
+ usersCacheSpy = username => {
expect(`@${username}`).toBe(tokenValue);
return Promise.resolve(dummyUser);
};
- subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
- .then(() => {
- expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
- tokenValueElement.querySelector('.avatar').remove();
- expect(tokenValueElement.innerHTML.trim()).toBe(_.escape(dummyUser.name));
- })
- .then(done)
- .catch(done.fail);
+ subject
+ .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
+ .then(() => {
+ expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
+ tokenValueElement.querySelector('.avatar').remove();
+
+ expect(tokenValueElement.innerHTML.trim()).toBe(_.escape(dummyUser.name));
+ })
+ .then(done)
+ .catch(done.fail);
});
});
@@ -850,23 +904,31 @@ describe('Filtered Search Visual Tokens', () => {
it('should set backgroundColor', () => {
const originalBackgroundColor = bugLabelToken.style.backgroundColor;
const token = subject.setTokenStyle(bugLabelToken, 'blue', 'white');
+
expect(token.style.backgroundColor).toEqual('blue');
expect(token.style.backgroundColor).not.toEqual(originalBackgroundColor);
});
it('should not set backgroundColor when it is a linear-gradient', () => {
- const token = subject.setTokenStyle(bugLabelToken, 'linear-gradient(135deg, red, blue)', 'white');
+ const token = subject.setTokenStyle(
+ bugLabelToken,
+ 'linear-gradient(135deg, red, blue)',
+ 'white',
+ );
+
expect(token.style.backgroundColor).toEqual(bugLabelToken.style.backgroundColor);
});
it('should set textColor', () => {
const token = subject.setTokenStyle(bugLabelToken, 'white', 'black');
+
expect(token.style.color).toEqual('black');
expect(token.style.color).not.toEqual(originalTextColor);
});
it('should add inverted class when textColor is #FFFFFF', () => {
const token = subject.setTokenStyle(bugLabelToken, 'black', '#FFFFFF');
+
expect(token.style.color).toEqual('rgb(255, 255, 255)');
expect(token.style.color).not.toEqual(originalTextColor);
expect(token.querySelector('.remove-token').classList.contains('inverted')).toEqual(true);
@@ -890,14 +952,17 @@ describe('Filtered Search Visual Tokens', () => {
describe('not preprocessed before', () => {
it('returns preprocessed labels', () => {
let labels = [];
+
expect(labels.preprocessed).not.toEqual(true);
labels = FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
+
expect(labels.preprocessed).toEqual(true);
});
it('overrides AjaxCache with preprocessed results', () => {
spyOn(AjaxCache, 'override').and.callFake(() => {});
FilteredSearchVisualTokens.preprocessLabel(endpoint, []);
+
expect(AjaxCache.override.calls.count()).toEqual(1);
});
});
@@ -915,8 +980,14 @@ describe('Filtered Search Visual Tokens', () => {
labelData = getJSONFixture(jsonFixtureName);
});
- const missingLabelToken = FilteredSearchSpecHelper.createFilterVisualToken('label', '~doesnotexist');
- const spaceLabelToken = FilteredSearchSpecHelper.createFilterVisualToken('label', '~"some space"');
+ const missingLabelToken = FilteredSearchSpecHelper.createFilterVisualToken(
+ 'label',
+ '~doesnotexist',
+ );
+ const spaceLabelToken = FilteredSearchSpecHelper.createFilterVisualToken(
+ 'label',
+ '~"some space"',
+ );
beforeEach(() => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
@@ -928,11 +999,11 @@ describe('Filtered Search Visual Tokens', () => {
const filteredSearchInput = document.querySelector('.filtered-search');
filteredSearchInput.dataset.baseEndpoint = dummyEndpoint;
- AjaxCache.internalStorage = { };
+ AjaxCache.internalStorage = {};
AjaxCache.internalStorage[`${dummyEndpoint}/labels.json`] = labelData;
});
- const parseColor = (color) => {
+ const parseColor = color => {
const dummyElement = document.createElement('div');
dummyElement.style.color = color;
return dummyElement.style.color;
@@ -944,16 +1015,16 @@ describe('Filtered Search Visual Tokens', () => {
expect(tokenValueContainer.style.color).toBe(parseColor(label.text_color));
};
- const findLabel = tokenValue => labelData.find(
- label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`,
- );
+ const findLabel = tokenValue =>
+ labelData.find(label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`);
- it('updates the color of a label token', (done) => {
+ it('updates the color of a label token', done => {
const { tokenValueContainer, tokenValueElement } = findElements(bugLabelToken);
const tokenValue = tokenValueElement.innerText;
const matchingLabel = findLabel(tokenValue);
- subject.updateLabelTokenColor(tokenValueContainer, tokenValue)
+ subject
+ .updateLabelTokenColor(tokenValueContainer, tokenValue)
.then(() => {
expectValueContainerStyle(tokenValueContainer, matchingLabel);
})
@@ -961,12 +1032,13 @@ describe('Filtered Search Visual Tokens', () => {
.catch(done.fail);
});
- it('updates the color of a label token with spaces', (done) => {
+ it('updates the color of a label token with spaces', done => {
const { tokenValueContainer, tokenValueElement } = findElements(spaceLabelToken);
const tokenValue = tokenValueElement.innerText;
const matchingLabel = findLabel(tokenValue);
- subject.updateLabelTokenColor(tokenValueContainer, tokenValue)
+ subject
+ .updateLabelTokenColor(tokenValueContainer, tokenValue)
.then(() => {
expectValueContainerStyle(tokenValueContainer, matchingLabel);
})
@@ -974,13 +1046,15 @@ describe('Filtered Search Visual Tokens', () => {
.catch(done.fail);
});
- it('does not change color of a missing label', (done) => {
+ it('does not change color of a missing label', done => {
const { tokenValueContainer, tokenValueElement } = findElements(missingLabelToken);
const tokenValue = tokenValueElement.innerText;
const matchingLabel = findLabel(tokenValue);
+
expect(matchingLabel).toBe(undefined);
- subject.updateLabelTokenColor(tokenValueContainer, tokenValue)
+ subject
+ .updateLabelTokenColor(tokenValueContainer, tokenValue)
.then(() => {
expect(tokenValueContainer.getAttribute('style')).toBe(null);
})
diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js
index d063fcf4f2d..70dd4e9570d 100644
--- a/spec/javascripts/filtered_search/recent_searches_root_spec.js
+++ b/spec/javascripts/filtered_search/recent_searches_root_spec.js
@@ -14,7 +14,7 @@ describe('RecentSearchesRoot', () => {
},
};
- VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake((options) => {
+ VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake(options => {
({ data, template } = options);
});
diff --git a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js
index c293c0afa97..188f83eca16 100644
--- a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js
+++ b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js
@@ -15,48 +15,49 @@ describe('RecentSearchesService', () => {
spyOn(RecentSearchesService, 'isAvailable').and.returnValue(true);
});
- it('should default to empty array', (done) => {
+ it('should default to empty array', done => {
const fetchItemsPromise = service.fetch();
fetchItemsPromise
- .then((items) => {
+ .then(items => {
expect(items).toEqual([]);
})
.then(done)
.catch(done.fail);
});
- it('should reject when unable to parse', (done) => {
+ it('should reject when unable to parse', done => {
window.localStorage.setItem(service.localStorageKey, 'fail');
const fetchItemsPromise = service.fetch();
fetchItemsPromise
.then(done.fail)
- .catch((error) => {
+ .catch(error => {
expect(error).toEqual(jasmine.any(SyntaxError));
})
.then(done)
.catch(done.fail);
});
- it('should reject when service is unavailable', (done) => {
+ it('should reject when service is unavailable', done => {
RecentSearchesService.isAvailable.and.returnValue(false);
- service.fetch()
+ service
+ .fetch()
.then(done.fail)
- .catch((error) => {
+ .catch(error => {
expect(error).toEqual(jasmine.any(Error));
})
.then(done)
.catch(done.fail);
});
- it('should return items from localStorage', (done) => {
+ it('should return items from localStorage', done => {
window.localStorage.setItem(service.localStorageKey, '["foo", "bar"]');
const fetchItemsPromise = service.fetch();
fetchItemsPromise
- .then((items) => {
+ .then(items => {
expect(items).toEqual(['foo', 'bar']);
})
.then(done)
@@ -70,10 +71,11 @@ describe('RecentSearchesService', () => {
spyOn(window.localStorage, 'getItem');
});
- it('should not call .getItem', (done) => {
- RecentSearchesService.prototype.fetch()
+ it('should not call .getItem', done => {
+ RecentSearchesService.prototype
+ .fetch()
.then(done.fail)
- .catch((err) => {
+ .catch(err => {
expect(err).toEqual(new RecentSearchesServiceError());
expect(window.localStorage.getItem).not.toHaveBeenCalled();
})
@@ -92,6 +94,7 @@ describe('RecentSearchesService', () => {
const items = ['foo', 'bar'];
service.save(items);
const newLocalStorageValue = window.localStorage.getItem(service.localStorageKey);
+
expect(JSON.parse(newLocalStorageValue)).toEqual(items);
});
});
diff --git a/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js b/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js
index 1eebc6f2367..56bb82ae941 100644
--- a/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js
+++ b/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js
@@ -38,14 +38,8 @@ describe('RecentSearchesStore', () => {
describe('setRecentSearches', () => {
it('should override list', () => {
- store.setRecentSearches([
- 'foo',
- 'bar',
- ]);
- store.setRecentSearches([
- 'baz',
- 'qux',
- ]);
+ store.setRecentSearches(['foo', 'bar']);
+ store.setRecentSearches(['baz', 'qux']);
expect(store.state.recentSearches).toEqual(['baz', 'qux']);
});
diff --git a/spec/javascripts/fixtures/admin_users.rb b/spec/javascripts/fixtures/admin_users.rb
new file mode 100644
index 00000000000..9989ac4fff2
--- /dev/null
+++ b/spec/javascripts/fixtures/admin_users.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe Admin::UsersController, '(JavaScript fixtures)', type: :controller do
+ include StubENV
+ include JavaScriptFixturesHelpers
+
+ let(:admin) { create(:admin) }
+
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+ sign_in(admin)
+ end
+
+ render_views
+
+ before(:all) do
+ clean_frontend_fixtures('admin/users')
+ end
+
+ it 'admin/users/new_with_internal_user_regex.html.raw' do |example|
+ stub_application_setting(user_default_external: true)
+ stub_application_setting(user_default_internal_regex: '^(?:(?!\.ext@).)*$\r?')
+
+ get :new
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+end
diff --git a/spec/javascripts/fixtures/application_settings.rb b/spec/javascripts/fixtures/application_settings.rb
new file mode 100644
index 00000000000..a9d3043f73d
--- /dev/null
+++ b/spec/javascripts/fixtures/application_settings.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+describe Admin::ApplicationSettingsController, '(JavaScript fixtures)', type: :controller do
+ include StubENV
+ include JavaScriptFixturesHelpers
+
+ let(:admin) { create(:admin) }
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:project) { create(:project_empty_repo, namespace: namespace, path: 'application-settings') }
+
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+ sign_in(admin)
+ end
+
+ render_views
+
+ before(:all) do
+ clean_frontend_fixtures('application_settings/')
+ end
+
+ after do
+ remove_repository(project)
+ end
+
+ it 'application_settings/accounts_and_limit.html.raw' do |example|
+ stub_application_setting(user_default_external: false)
+
+ get :show
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+end
diff --git a/spec/javascripts/fixtures/groups.rb b/spec/javascripts/fixtures/groups.rb
index a2035ceae15..b42f442557c 100644
--- a/spec/javascripts/fixtures/groups.rb
+++ b/spec/javascripts/fixtures/groups.rb
@@ -17,6 +17,16 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
sign_in(admin)
end
+ describe GroupsController, '(JavaScript fixtures)', type: :controller do
+ it 'groups/edit.html.raw' do |example|
+ get :edit,
+ id: group
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+ end
+
describe Groups::Settings::CiCdController, '(JavaScript fixtures)', type: :controller do
it 'groups/ci_cd_settings.html.raw' do |example|
get :show,
diff --git a/spec/javascripts/fixtures/jobs.rb b/spec/javascripts/fixtures/jobs.rb
index 6d5c6d5334f..82d7a5e394e 100644
--- a/spec/javascripts/fixtures/jobs.rb
+++ b/spec/javascripts/fixtures/jobs.rb
@@ -5,16 +5,24 @@ describe Projects::JobsController, '(JavaScript fixtures)', type: :controller do
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
- let(:project) { create(:project_empty_repo, namespace: namespace, path: 'builds-project') }
- let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ let(:project) { create(:project, :repository, namespace: namespace, path: 'builds-project') }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id) }
let!(:build_with_artifacts) { create(:ci_build, :success, :artifacts, :trace_artifact, pipeline: pipeline, stage: 'test', artifacts_expire_at: Time.now + 18.months) }
let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline, stage: 'build') }
let!(:pending_build) { create(:ci_build, :pending, pipeline: pipeline, stage: 'deploy') }
+ let!(:delayed_job) do
+ create(:ci_build, :scheduled,
+ pipeline: pipeline,
+ name: 'delayed job',
+ stage: 'test',
+ commands: 'test')
+ end
render_views
before(:all) do
clean_frontend_fixtures('builds/')
+ clean_frontend_fixtures('jobs/')
end
before do
@@ -34,4 +42,15 @@ describe Projects::JobsController, '(JavaScript fixtures)', type: :controller do
expect(response).to be_success
store_frontend_fixture(response, example.description)
end
+
+ it 'jobs/delayed.json' do |example|
+ get :show,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: delayed_job.to_param,
+ format: :json
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
end
diff --git a/spec/javascripts/fixtures/merge_requests_diffs.rb b/spec/javascripts/fixtures/merge_requests_diffs.rb
index ddce00bc0fe..afe34b834b0 100644
--- a/spec/javascripts/fixtures/merge_requests_diffs.rb
+++ b/spec/javascripts/fixtures/merge_requests_diffs.rb
@@ -9,6 +9,7 @@ describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type
let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') }
let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') }
let(:path) { "files/ruby/popen.rb" }
+ let(:selected_commit) { merge_request.all_commits[0] }
let(:position) do
Gitlab::Diff::Position.new(
old_path: path,
@@ -33,6 +34,14 @@ describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type
remove_repository(project)
end
+ it 'merge_request_diffs/with_commit.json' do |example|
+ # Create a user that matches the selected commit author
+ # This is so that the "author" information will be populated
+ create(:user, email: selected_commit.author_email, name: selected_commit.author_name)
+
+ render_merge_request(example.description, merge_request, commit_id: selected_commit.sha)
+ end
+
it 'merge_request_diffs/inline_changes_tab_with_comments.json' do |example|
create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
create(:note_on_merge_request, author: admin, project: project, noteable: merge_request)
@@ -47,13 +56,14 @@ describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type
private
- def render_merge_request(fixture_file_name, merge_request, view: 'inline')
+ def render_merge_request(fixture_file_name, merge_request, view: 'inline', **extra_params)
get :show,
namespace_id: project.namespace.to_param,
project_id: project,
id: merge_request.to_param,
format: :json,
- view: view
+ view: view,
+ **extra_params
expect(response).to be_success
store_frontend_fixture(response, fixture_file_name)
diff --git a/spec/javascripts/fixtures/projects.rb b/spec/javascripts/fixtures/projects.rb
index 57c78182abc..d98f7f55b20 100644
--- a/spec/javascripts/fixtures/projects.rb
+++ b/spec/javascripts/fixtures/projects.rb
@@ -6,6 +6,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, namespace: namespace, path: 'builds-project') }
+ let(:project_with_repo) { create(:project, :repository, description: 'Code and stuff') }
let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2') }
let!(:variable1) { create(:ci_variable, project: project_variable_populated) }
let!(:variable2) { create(:ci_variable, project: project_variable_populated) }
@@ -35,6 +36,15 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
store_frontend_fixture(response, example.description)
end
+ it 'projects/overview.html.raw' do |example|
+ get :show,
+ namespace_id: project_with_repo.namespace.to_param,
+ id: project_with_repo
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+
it 'projects/edit.html.raw' do |example|
get :edit,
namespace_id: project.namespace.to_param,
diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js
index 7198dbd4cf2..aecab331ead 100644
--- a/spec/javascripts/flash_spec.js
+++ b/spec/javascripts/flash_spec.js
@@ -1,9 +1,4 @@
-import flash, {
- createFlashEl,
- createAction,
- hideFlash,
- removeFlashClickListener,
-} from '~/flash';
+import flash, { createFlashEl, createAction, hideFlash, removeFlashClickListener } from '~/flash';
describe('Flash', () => {
describe('createFlashEl', () => {
@@ -20,28 +15,23 @@ describe('Flash', () => {
it('creates flash element with type', () => {
el.innerHTML = createFlashEl('testing', 'alert');
- expect(
- el.querySelector('.flash-alert'),
- ).not.toBeNull();
+ expect(el.querySelector('.flash-alert')).not.toBeNull();
});
it('escapes text', () => {
el.innerHTML = createFlashEl('<script>alert("a");</script>', 'alert');
- expect(
- el.querySelector('.flash-text').textContent.trim(),
- ).toBe('<script>alert("a");</script>');
+ expect(el.querySelector('.flash-text').textContent.trim()).toBe(
+ '<script>alert("a");</script>',
+ );
});
it('adds container classes when inside content wrapper', () => {
el.innerHTML = createFlashEl('testing', 'alert', true);
- expect(
- el.querySelector('.flash-text').classList.contains('container-fluid'),
- ).toBeTruthy();
- expect(
- el.querySelector('.flash-text').classList.contains('container-limited'),
- ).toBeTruthy();
+ expect(el.querySelector('.flash-text').classList.contains('container-fluid')).toBeTruthy();
+
+ expect(el.querySelector('.flash-text').classList.contains('container-limited')).toBeTruthy();
});
});
@@ -56,28 +46,23 @@ describe('Flash', () => {
it('sets transition style', () => {
hideFlash(el);
- expect(
- el.style.transition,
- ).toBe('opacity 0.3s');
+ expect(el.style['transition-property']).toBe('opacity');
+
+ expect(el.style['transition-duration']).toBe('0.3s');
});
it('sets opacity style', () => {
hideFlash(el);
- expect(
- el.style.opacity,
- ).toBe('0');
+ expect(el.style.opacity).toBe('0');
});
it('does not set styles when fadeTransition is false', () => {
hideFlash(el, false);
- expect(
- el.style.opacity,
- ).toBe('');
- expect(
- el.style.transition,
- ).toBe('');
+ expect(el.style.opacity).toBe('');
+
+ expect(el.style.transition).toBe('');
});
it('removes element after transitionend', () => {
@@ -86,9 +71,7 @@ describe('Flash', () => {
hideFlash(el);
el.dispatchEvent(new Event('transitionend'));
- expect(
- document.querySelector('.js-testing'),
- ).toBeNull();
+ expect(document.querySelector('.js-testing')).toBeNull();
});
it('calls event listener callback once', () => {
@@ -100,9 +83,7 @@ describe('Flash', () => {
el.dispatchEvent(new Event('transitionend'));
el.dispatchEvent(new Event('transitionend'));
- expect(
- el.remove.calls.count(),
- ).toBe(1);
+ expect(el.remove.calls.count()).toBe(1);
});
});
@@ -119,9 +100,7 @@ describe('Flash', () => {
title: 'test',
});
- expect(
- el.querySelector('.flash-action').href,
- ).toContain('testing');
+ expect(el.querySelector('.flash-action').href).toContain('testing');
});
it('uses hash as href when no href is present', () => {
@@ -129,9 +108,7 @@ describe('Flash', () => {
title: 'test',
});
- expect(
- el.querySelector('.flash-action').href,
- ).toContain('#');
+ expect(el.querySelector('.flash-action').href).toContain('#');
});
it('adds role when no href is present', () => {
@@ -139,9 +116,7 @@ describe('Flash', () => {
title: 'test',
});
- expect(
- el.querySelector('.flash-action').getAttribute('role'),
- ).toBe('button');
+ expect(el.querySelector('.flash-action').getAttribute('role')).toBe('button');
});
it('escapes the title text', () => {
@@ -149,9 +124,9 @@ describe('Flash', () => {
title: '<script>alert("a")</script>',
});
- expect(
- el.querySelector('.flash-action').textContent.trim(),
- ).toBe('<script>alert("a")</script>');
+ expect(el.querySelector('.flash-action').textContent.trim()).toBe(
+ '<script>alert("a")</script>',
+ );
});
});
@@ -160,12 +135,9 @@ describe('Flash', () => {
it('does not add to the DOM', () => {
const flashEl = flash('testing');
- expect(
- flashEl,
- ).toBeNull();
- expect(
- document.querySelector('.flash-alert'),
- ).toBeNull();
+ expect(flashEl).toBeNull();
+
+ expect(document.querySelector('.flash-alert')).toBeNull();
});
});
@@ -185,42 +157,30 @@ describe('Flash', () => {
it('adds flash element into container', () => {
flash('test', 'alert', document, null, false, true);
- expect(
- document.querySelector('.flash-alert'),
- ).not.toBeNull();
+ expect(document.querySelector('.flash-alert')).not.toBeNull();
- expect(
- document.body.className,
- ).toContain('flash-shown');
+ expect(document.body.className).toContain('flash-shown');
});
it('adds flash into specified parent', () => {
- flash(
- 'test',
- 'alert',
- document.querySelector('.content-wrapper'),
- );
+ flash('test', 'alert', document.querySelector('.content-wrapper'));
- expect(
- document.querySelector('.content-wrapper .flash-alert'),
- ).not.toBeNull();
+ expect(document.querySelector('.content-wrapper .flash-alert')).not.toBeNull();
});
it('adds container classes when inside content-wrapper', () => {
flash('test');
- expect(
- document.querySelector('.flash-text').className,
- ).toBe('flash-text container-fluid container-limited');
+ expect(document.querySelector('.flash-text').className).toBe(
+ 'flash-text container-fluid container-limited limit-container-width',
+ );
});
it('does not add container when outside of content-wrapper', () => {
document.querySelector('.content-wrapper').className = 'js-content-wrapper';
flash('test');
- expect(
- document.querySelector('.flash-text').className.trim(),
- ).toBe('flash-text');
+ expect(document.querySelector('.flash-text').className.trim()).toContain('flash-text');
});
it('removes element after clicking', () => {
@@ -228,29 +188,18 @@ describe('Flash', () => {
document.querySelector('.flash-alert').click();
- expect(
- document.querySelector('.flash-alert'),
- ).toBeNull();
+ expect(document.querySelector('.flash-alert')).toBeNull();
- expect(
- document.body.className,
- ).not.toContain('flash-shown');
+ expect(document.body.className).not.toContain('flash-shown');
});
describe('with actionConfig', () => {
it('adds action link', () => {
- flash(
- 'test',
- 'alert',
- document,
- {
- title: 'test',
- },
- );
-
- expect(
- document.querySelector('.flash-action'),
- ).not.toBeNull();
+ flash('test', 'alert', document, {
+ title: 'test',
+ });
+
+ expect(document.querySelector('.flash-action')).not.toBeNull();
});
it('calls actionConfig clickHandler on click', () => {
@@ -259,18 +208,11 @@ describe('Flash', () => {
clickHandler: jasmine.createSpy('actionConfig'),
};
- flash(
- 'test',
- 'alert',
- document,
- actionConfig,
- );
+ flash('test', 'alert', document, actionConfig);
document.querySelector('.flash-action').click();
- expect(
- actionConfig.clickHandler,
- ).toHaveBeenCalled();
+ expect(actionConfig.clickHandler).toHaveBeenCalled();
});
});
});
@@ -281,7 +223,7 @@ describe('Flash', () => {
document.body.innerHTML += '<div class="flash-container"><div class="flash"></div></div>';
});
- it('removes global flash on click', (done) => {
+ it('removes global flash on click', done => {
const flashEl = document.querySelector('.flash');
removeFlashClickListener(flashEl, false);
diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js
index eb9330f5e5b..7ef44f29c5b 100644
--- a/spec/javascripts/fly_out_nav_spec.js
+++ b/spec/javascripts/fly_out_nav_spec.js
@@ -45,9 +45,7 @@ describe('Fly out sidebar navigation', () => {
height: 100,
};
- expect(
- calculateTop(boundingRect, 100),
- ).toBe(100);
+ expect(calculateTop(boundingRect, 100)).toBe(100);
});
it('returns boundingRect - bottomOverflow', () => {
@@ -56,27 +54,22 @@ describe('Fly out sidebar navigation', () => {
height: 100,
};
- expect(
- calculateTop(boundingRect, 100),
- ).toBe(window.innerHeight - 50);
+ expect(calculateTop(boundingRect, 100)).toBe(window.innerHeight - 50);
});
});
describe('getHideSubItemsInterval', () => {
beforeEach(() => {
- el.innerHTML = '<div class="sidebar-sub-level-items" style="position: fixed; top: 0; left: 100px; height: 150px;"></div>';
+ el.innerHTML =
+ '<div class="sidebar-sub-level-items" style="position: fixed; top: 0; left: 100px; height: 150px;"></div>';
});
it('returns 0 if currentOpenMenu is nil', () => {
- expect(
- getHideSubItemsInterval(),
- ).toBe(0);
+ expect(getHideSubItemsInterval()).toBe(0);
});
it('returns 0 if mousePos is empty', () => {
- expect(
- getHideSubItemsInterval(),
- ).toBe(0);
+ expect(getHideSubItemsInterval()).toBe(0);
});
it('returns 0 when mouse above sub-items', () => {
@@ -90,9 +83,7 @@ describe('Fly out sidebar navigation', () => {
clientY: el.getBoundingClientRect().top - 50,
});
- expect(
- getHideSubItemsInterval(),
- ).toBe(0);
+ expect(getHideSubItemsInterval()).toBe(0);
});
it('returns 0 when mouse is below sub-items', () => {
@@ -105,12 +96,10 @@ describe('Fly out sidebar navigation', () => {
});
documentMouseMove({
clientX: el.getBoundingClientRect().left,
- clientY: (el.getBoundingClientRect().top - subItems.getBoundingClientRect().height) + 50,
+ clientY: el.getBoundingClientRect().top - subItems.getBoundingClientRect().height + 50,
});
- expect(
- getHideSubItemsInterval(),
- ).toBe(0);
+ expect(getHideSubItemsInterval()).toBe(0);
});
it('returns 300 when mouse is moved towards sub-items', () => {
@@ -124,9 +113,7 @@ describe('Fly out sidebar navigation', () => {
clientY: el.getBoundingClientRect().top + 10,
});
- expect(
- getHideSubItemsInterval(),
- ).toBe(300);
+ expect(getHideSubItemsInterval()).toBe(300);
});
});
@@ -138,9 +125,7 @@ describe('Fly out sidebar navigation', () => {
it('removes is-over class if currentOpenMenu is null', () => {
mouseLeaveTopItem(el);
- expect(
- el.classList.remove,
- ).toHaveBeenCalledWith('is-over');
+ expect(el.classList.remove).toHaveBeenCalledWith('is-over');
});
it('removes is-over class if currentOpenMenu is null & there are sub-items', () => {
@@ -148,9 +133,7 @@ describe('Fly out sidebar navigation', () => {
mouseLeaveTopItem(el);
- expect(
- el.classList.remove,
- ).toHaveBeenCalledWith('is-over');
+ expect(el.classList.remove).toHaveBeenCalledWith('is-over');
});
it('does not remove is-over class if currentOpenMenu is the passed in sub-items', () => {
@@ -159,34 +142,29 @@ describe('Fly out sidebar navigation', () => {
setOpenMenu(el.querySelector('.sidebar-sub-level-items'));
mouseLeaveTopItem(el);
- expect(
- el.classList.remove,
- ).not.toHaveBeenCalled();
+ expect(el.classList.remove).not.toHaveBeenCalled();
});
});
describe('mouseEnterTopItems', () => {
beforeEach(() => {
- el.innerHTML = '<div class="sidebar-sub-level-items" style="position: absolute; top: 0; left: 100px; height: 200px;"></div>';
+ el.innerHTML =
+ '<div class="sidebar-sub-level-items" style="position: absolute; top: 0; left: 100px; height: 200px;"></div>';
});
- it('shows sub-items after 0ms if no menu is open', (done) => {
+ it('shows sub-items after 0ms if no menu is open', done => {
mouseEnterTopItems(el);
- expect(
- getHideSubItemsInterval(),
- ).toBe(0);
+ expect(getHideSubItemsInterval()).toBe(0);
setTimeout(() => {
- expect(
- el.querySelector('.sidebar-sub-level-items').style.display,
- ).toBe('block');
+ expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block');
done();
});
});
- it('shows sub-items after 300ms if a menu is currently open', (done) => {
+ it('shows sub-items after 300ms if a menu is currently open', done => {
documentMouseMove({
clientX: el.getBoundingClientRect().left,
clientY: el.getBoundingClientRect().top,
@@ -201,14 +179,10 @@ describe('Fly out sidebar navigation', () => {
mouseEnterTopItems(el, 0);
- expect(
- getHideSubItemsInterval(),
- ).toBe(300);
+ expect(getHideSubItemsInterval()).toBe(300);
setTimeout(() => {
- expect(
- el.querySelector('.sidebar-sub-level-items').style.display,
- ).toBe('block');
+ expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block');
done();
});
@@ -225,9 +199,7 @@ describe('Fly out sidebar navigation', () => {
showSubLevelItems(el);
- expect(
- el.classList.add,
- ).toHaveBeenCalledWith('is-over');
+ expect(el.classList.add).toHaveBeenCalledWith('is-over');
});
it('does not show sub-items on mobile', () => {
@@ -235,17 +207,13 @@ describe('Fly out sidebar navigation', () => {
showSubLevelItems(el);
- expect(
- el.querySelector('.sidebar-sub-level-items').style.display,
- ).not.toBe('block');
+ expect(el.querySelector('.sidebar-sub-level-items').style.display).not.toBe('block');
});
it('shows sub-items', () => {
showSubLevelItems(el);
- expect(
- el.querySelector('.sidebar-sub-level-items').style.display,
- ).toBe('block');
+ expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block');
});
it('shows collapsed only sub-items if icon only sidebar', () => {
@@ -258,9 +226,7 @@ describe('Fly out sidebar navigation', () => {
showSubLevelItems(el);
- expect(
- el.querySelector('.sidebar-sub-level-items').style.display,
- ).toBe('block');
+ expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block');
});
it('does not show collapsed only sub-items if icon only sidebar', () => {
@@ -269,9 +235,7 @@ describe('Fly out sidebar navigation', () => {
showSubLevelItems(el);
- expect(
- subItems.style.display,
- ).not.toBe('block');
+ expect(subItems.style.display).not.toBe('block');
});
it('sets transform of sub-items', () => {
@@ -285,9 +249,10 @@ describe('Fly out sidebar navigation', () => {
setSidebar(sidebar);
showSubLevelItems(el);
- expect(
- subItems.style.transform,
- ).toBe(`translate3d(200px, ${Math.floor(el.getBoundingClientRect().top) - getHeaderHeight()}px, 0px)`);
+ expect(subItems.style.transform).toBe(
+ `translate3d(200px, ${Math.floor(el.getBoundingClientRect().top) -
+ getHeaderHeight()}px, 0px)`,
+ );
});
it('sets is-above when element is above', () => {
@@ -299,33 +264,25 @@ describe('Fly out sidebar navigation', () => {
showSubLevelItems(el);
- expect(
- subItems.classList.add,
- ).toHaveBeenCalledWith('is-above');
+ expect(subItems.classList.add).toHaveBeenCalledWith('is-above');
});
});
describe('canShowSubItems', () => {
it('returns true if on desktop size', () => {
- expect(
- canShowSubItems(),
- ).toBeTruthy();
+ expect(canShowSubItems()).toBeTruthy();
});
it('returns false if on mobile size', () => {
breakpointSize = 'xs';
- expect(
- canShowSubItems(),
- ).toBeFalsy();
+ expect(canShowSubItems()).toBeFalsy();
});
});
describe('canShowActiveSubItems', () => {
it('returns true by default', () => {
- expect(
- canShowActiveSubItems(el),
- ).toBeTruthy();
+ expect(canShowActiveSubItems(el)).toBeTruthy();
});
it('returns false when active & expanded sidebar', () => {
@@ -334,9 +291,7 @@ describe('Fly out sidebar navigation', () => {
setSidebar(sidebar);
- expect(
- canShowActiveSubItems(el),
- ).toBeFalsy();
+ expect(canShowActiveSubItems(el)).toBeFalsy();
});
it('returns true when active & collapsed sidebar', () => {
@@ -346,9 +301,7 @@ describe('Fly out sidebar navigation', () => {
setSidebar(sidebar);
- expect(
- canShowActiveSubItems(el),
- ).toBeTruthy();
+ expect(canShowActiveSubItems(el)).toBeTruthy();
});
});
@@ -362,18 +315,14 @@ describe('Fly out sidebar navigation', () => {
it('hides subMenu if element is not hovered', () => {
subItemsMouseLeave(el);
- expect(
- getOpenMenu(),
- ).toBeNull();
+ expect(getOpenMenu()).toBeNull();
});
it('does not hide subMenu if element is hovered', () => {
el.classList.add('is-over');
subItemsMouseLeave(el);
- expect(
- getOpenMenu(),
- ).not.toBeNull();
+ expect(getOpenMenu()).not.toBeNull();
});
});
});
diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js
index 834f919524d..b1cc4d8dc8d 100644
--- a/spec/javascripts/frequent_items/components/app_spec.js
+++ b/spec/javascripts/frequent_items/components/app_spec.js
@@ -232,8 +232,7 @@ describe('Frequent Items App Component', () => {
expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1);
vm.$store.dispatch('setSearchQuery', 'gitlab');
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
})
diff --git a/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js
index 201aca77b10..7deed985219 100644
--- a/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js
+++ b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js
@@ -30,9 +30,11 @@ describe('FrequentItemsListItemComponent', () => {
describe('hasAvatar', () => {
it('should return `true` or `false` if whether avatar is present or not', () => {
vm.avatarUrl = 'path/to/avatar.png';
+
expect(vm.hasAvatar).toBe(true);
vm.avatarUrl = null;
+
expect(vm.hasAvatar).toBe(false);
});
});
@@ -40,11 +42,13 @@ describe('FrequentItemsListItemComponent', () => {
describe('highlightedItemName', () => {
it('should enclose part of project name in <b> & </b> which matches with `matcher` prop', () => {
vm.matcher = 'lab';
+
expect(vm.highlightedItemName).toContain('<b>Lab</b>');
});
it('should return project name as it is if `matcher` is not available', () => {
vm.matcher = null;
+
expect(vm.highlightedItemName).toBe(mockProject.name);
});
});
@@ -52,11 +56,13 @@ describe('FrequentItemsListItemComponent', () => {
describe('truncatedNamespace', () => {
it('should truncate project name from namespace string', () => {
vm.namespace = 'platform / nokia-3310';
+
expect(vm.truncatedNamespace).toBe('platform');
});
it('should truncate namespace string from the middle if it includes more than two groups in path', () => {
vm.namespace = 'platform / hardware / broadcom / Wifi Group / Mobile Chipset / nokia-3310';
+
expect(vm.truncatedNamespace).toBe('platform / ... / Mobile Chipset');
});
});
diff --git a/spec/javascripts/frequent_items/components/frequent_items_list_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_spec.js
index 3003b7ee000..8518a681a26 100644
--- a/spec/javascripts/frequent_items/components/frequent_items_list_spec.js
+++ b/spec/javascripts/frequent_items/components/frequent_items_list_spec.js
@@ -30,9 +30,11 @@ describe('FrequentItemsListComponent', () => {
describe('isListEmpty', () => {
it('should return `true` or `false` representing whether if `items` is empty or not with projects', () => {
vm.items = [];
+
expect(vm.isListEmpty).toBe(true);
vm.items = mockFrequentProjects;
+
expect(vm.isListEmpty).toBe(false);
});
});
@@ -40,9 +42,11 @@ describe('FrequentItemsListComponent', () => {
describe('fetched item messages', () => {
it('should return appropriate empty list message based on value of `localStorageFailed` prop with projects', () => {
vm.isFetchFailed = true;
+
expect(vm.listEmptyMessage).toBe('This feature requires browser localStorage support');
vm.isFetchFailed = false;
+
expect(vm.listEmptyMessage).toBe('Projects you visit often will appear here');
});
});
@@ -51,9 +55,11 @@ describe('FrequentItemsListComponent', () => {
it('should return appropriate empty list message based on value of `searchFailed` prop with projects', () => {
vm.hasSearchQuery = true;
vm.isFetchFailed = true;
+
expect(vm.listEmptyMessage).toBe('Something went wrong on our end.');
vm.isFetchFailed = false;
+
expect(vm.listEmptyMessage).toBe('Sorry, no projects matched your search');
});
});
diff --git a/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js
index 6a11038e70a..d564292f1ba 100644
--- a/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js
+++ b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js
@@ -26,6 +26,7 @@ describe('FrequentItemsSearchInputComponent', () => {
spyOn(vm.$refs.search, 'focus');
vm.setFocus();
+
expect(vm.$refs.search.focus).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/gfm_auto_complete_spec.js b/spec/javascripts/gfm_auto_complete_spec.js
index 1cb20a1e7ff..6f414c8ccf1 100644
--- a/spec/javascripts/gfm_auto_complete_spec.js
+++ b/spec/javascripts/gfm_auto_complete_spec.js
@@ -6,39 +6,38 @@ import GfmAutoComplete from '~/gfm_auto_complete';
import 'vendor/jquery.caret';
import 'vendor/jquery.atwho';
-describe('GfmAutoComplete', function () {
+describe('GfmAutoComplete', function() {
const gfmAutoCompleteCallbacks = GfmAutoComplete.prototype.getDefaultCallbacks.call({
fetchData: () => {},
});
- describe('DefaultOptions.sorter', function () {
- describe('assets loading', function () {
- beforeEach(function () {
+ describe('DefaultOptions.sorter', function() {
+ describe('assets loading', function() {
+ beforeEach(function() {
spyOn(GfmAutoComplete, 'isLoading').and.returnValue(true);
this.atwhoInstance = { setting: {} };
this.items = [];
- this.sorterValue = gfmAutoCompleteCallbacks.sorter
- .call(this.atwhoInstance, '', this.items);
+ this.sorterValue = gfmAutoCompleteCallbacks.sorter.call(this.atwhoInstance, '', this.items);
});
- it('should disable highlightFirst', function () {
+ it('should disable highlightFirst', function() {
expect(this.atwhoInstance.setting.highlightFirst).toBe(false);
});
- it('should return the passed unfiltered items', function () {
+ it('should return the passed unfiltered items', function() {
expect(this.sorterValue).toEqual(this.items);
});
});
- describe('assets finished loading', function () {
- beforeEach(function () {
+ describe('assets finished loading', function() {
+ beforeEach(function() {
spyOn(GfmAutoComplete, 'isLoading').and.returnValue(false);
spyOn($.fn.atwho.default.callbacks, 'sorter');
});
- it('should enable highlightFirst if alwaysHighlightFirst is set', function () {
+ it('should enable highlightFirst if alwaysHighlightFirst is set', function() {
const atwhoInstance = { setting: { alwaysHighlightFirst: true } };
gfmAutoCompleteCallbacks.sorter.call(atwhoInstance);
@@ -46,7 +45,7 @@ describe('GfmAutoComplete', function () {
expect(atwhoInstance.setting.highlightFirst).toBe(true);
});
- it('should enable highlightFirst if a query is present', function () {
+ it('should enable highlightFirst if a query is present', function() {
const atwhoInstance = { setting: {} };
gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, 'query');
@@ -54,7 +53,7 @@ describe('GfmAutoComplete', function () {
expect(atwhoInstance.setting.highlightFirst).toBe(true);
});
- it('should call the default atwho sorter', function () {
+ it('should call the default atwho sorter', function() {
const atwhoInstance = { setting: {} };
const query = 'query';
@@ -69,9 +68,8 @@ describe('GfmAutoComplete', function () {
});
describe('DefaultOptions.beforeInsert', () => {
- const beforeInsert = (context, value) => (
- gfmAutoCompleteCallbacks.beforeInsert.call(context, value)
- );
+ const beforeInsert = (context, value) =>
+ gfmAutoCompleteCallbacks.beforeInsert.call(context, value);
const atwhoInstance = { setting: { skipSpecialCharacterTest: false } };
@@ -98,29 +96,51 @@ describe('GfmAutoComplete', function () {
});
});
- describe('DefaultOptions.matcher', function () {
- const defaultMatcher = (context, flag, subtext) => (
- gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext)
- );
+ describe('DefaultOptions.matcher', function() {
+ const defaultMatcher = (context, flag, subtext) =>
+ gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext);
- const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%'];
+ const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
const otherFlags = ['/', ':'];
const flags = flagsUseDefaultMatcher.concat(otherFlags);
- const flagsHash = flags.reduce((hash, el) => { hash[el] = null; return hash; }, {});
+ const flagsHash = flags.reduce((hash, el) => {
+ hash[el] = null;
+ return hash;
+ }, {});
const atwhoInstance = { setting: {}, app: { controllers: flagsHash } };
const minLen = 1;
const maxLen = 20;
const argumentSize = [minLen, maxLen / 2, maxLen];
- const allowedSymbols = ['', 'a', 'n', 'z', 'A', 'Z', 'N', '0', '5', '9', 'Ð', 'а', 'Я', 'Ñ', '.', '\'', '+', '-', '_'];
+ const allowedSymbols = [
+ '',
+ 'a',
+ 'n',
+ 'z',
+ 'A',
+ 'Z',
+ 'N',
+ '0',
+ '5',
+ '9',
+ 'Ð',
+ 'а',
+ 'Я',
+ 'Ñ',
+ '.',
+ "'",
+ '+',
+ '-',
+ '_',
+ ];
const jointAllowedSymbols = allowedSymbols.join('');
describe('should match regular symbols', () => {
- flagsUseDefaultMatcher.forEach((flag) => {
- allowedSymbols.forEach((symbol) => {
- argumentSize.forEach((size) => {
+ flagsUseDefaultMatcher.forEach(flag => {
+ allowedSymbols.forEach(symbol => {
+ argumentSize.forEach(size => {
const query = new Array(size + 1).join(symbol);
const subtext = flag + query;
@@ -142,16 +162,16 @@ describe('GfmAutoComplete', function () {
const shouldNotBeFollowedBy = flags.concat(['\x00', '\x10', '\x3f', '\n', ' ']);
const shouldNotBePrependedBy = ['`'];
- flagsUseDefaultMatcher.forEach((atSign) => {
- shouldNotBeFollowedBy.forEach((followedSymbol) => {
+ flagsUseDefaultMatcher.forEach(atSign => {
+ shouldNotBeFollowedBy.forEach(followedSymbol => {
const seq = atSign + followedSymbol;
- it(`should not match "${seq}"`, () => {
+ it(`should not match ${JSON.stringify(seq)}`, () => {
expect(defaultMatcher(atwhoInstance, atSign, seq)).toBe(null);
});
});
- shouldNotBePrependedBy.forEach((prependedSymbol) => {
+ shouldNotBePrependedBy.forEach(prependedSymbol => {
const seq = prependedSymbol + atSign;
it(`should not match "${seq}"`, () => {
@@ -162,28 +182,26 @@ describe('GfmAutoComplete', function () {
});
});
- describe('isLoading', function () {
- it('should be true with loading data object item', function () {
+ describe('isLoading', function() {
+ it('should be true with loading data object item', function() {
expect(GfmAutoComplete.isLoading({ name: 'loading' })).toBe(true);
});
- it('should be true with loading data array', function () {
+ it('should be true with loading data array', function() {
expect(GfmAutoComplete.isLoading(['loading'])).toBe(true);
});
- it('should be true with loading data object array', function () {
+ it('should be true with loading data object array', function() {
expect(GfmAutoComplete.isLoading([{ name: 'loading' }])).toBe(true);
});
- it('should be false with actual array data', function () {
- expect(GfmAutoComplete.isLoading([
- { title: 'Foo' },
- { title: 'Bar' },
- { title: 'Qux' },
- ])).toBe(false);
+ it('should be false with actual array data', function() {
+ expect(
+ GfmAutoComplete.isLoading([{ title: 'Foo' }, { title: 'Bar' }, { title: 'Qux' }]),
+ ).toBe(false);
});
- it('should be false with actual data item', function () {
+ it('should be false with actual data item', function() {
expect(GfmAutoComplete.isLoading({ title: 'Foo' })).toBe(false);
});
});
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index af58dff7da7..85083653db8 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, no-param-reassign */
+/* eslint-disable no-param-reassign */
import $ from 'jquery';
import GLDropdown from '~/gl_dropdown';
@@ -8,7 +8,8 @@ describe('glDropdown', function describeDropdown() {
preloadFixtures('static/gl_dropdown.html.raw');
loadJSONFixtures('projects.json');
- const NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item';
+ const NON_SELECTABLE_CLASSES =
+ '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item';
const SEARCH_INPUT_SELECTOR = '.dropdown-input-field';
const ITEM_SELECTOR = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES})`;
const FOCUSED_ITEM_SELECTOR = `${ITEM_SELECTOR} a.is-focused`;
@@ -17,7 +18,7 @@ describe('glDropdown', function describeDropdown() {
DOWN: 40,
UP: 38,
ENTER: 13,
- ESC: 27
+ ESC: 27,
};
let remoteCallback;
@@ -28,7 +29,7 @@ describe('glDropdown', function describeDropdown() {
$('body').trigger({
type: 'keydown',
which: ARROW_KEYS[direction],
- keyCode: ARROW_KEYS[direction]
+ keyCode: ARROW_KEYS[direction],
});
i += 1;
if (i <= steps) {
@@ -43,17 +44,23 @@ describe('glDropdown', function describeDropdown() {
};
function initDropDown(hasRemote, isFilterable, extraOpts = {}) {
- const options = Object.assign({
- selectable: true,
- filterable: isFilterable,
- data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData,
- search: {
- fields: ['name']
+ const options = Object.assign(
+ {
+ selectable: true,
+ filterable: isFilterable,
+ data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData,
+ search: {
+ fields: ['name'],
+ },
+ text: project => project.name_with_namespace || project.name,
+ id: project => project.id,
},
- text: project => (project.name_with_namespace || project.name),
- id: project => project.id,
- }, extraOpts);
- this.dropdownButtonElement = $('#js-project-dropdown', this.dropdownContainerElement).glDropdown(options);
+ extraOpts,
+ );
+ this.dropdownButtonElement = $(
+ '#js-project-dropdown',
+ this.dropdownContainerElement,
+ ).glDropdown(options);
}
beforeEach(() => {
@@ -70,8 +77,10 @@ describe('glDropdown', function describeDropdown() {
it('should open on click', () => {
initDropDown.call(this, false);
+
expect(this.dropdownContainerElement).not.toHaveClass('show');
this.dropdownButtonElement.click();
+
expect(this.dropdownContainerElement).toHaveClass('show');
});
@@ -82,9 +91,7 @@ describe('glDropdown', function describeDropdown() {
this.dropdownButtonElement.click();
- expect(
- $('.dropdown-content li:first-child').text(),
- ).toBe('<script>alert("testing");</script>');
+ expect($('.dropdown-content li:first-child').text()).toBe('<script>alert("testing");</script>');
});
it('should output HTML when highlighting', () => {
@@ -97,13 +104,11 @@ describe('glDropdown', function describeDropdown() {
this.dropdownButtonElement.click();
- expect(
- $('.dropdown-content li:first-child').text(),
- ).toBe('testing');
+ expect($('.dropdown-content li:first-child').text()).toBe('testing');
- expect(
- $('.dropdown-content li:first-child a').html(),
- ).toBe('<b>t</b><b>e</b><b>s</b><b>t</b>ing');
+ expect($('.dropdown-content li:first-child a').html()).toBe(
+ '<b>t</b><b>e</b><b>s</b><b>t</b>ing',
+ );
});
describe('that is open', () => {
@@ -114,21 +119,28 @@ describe('glDropdown', function describeDropdown() {
it('should select a following item on DOWN keypress', () => {
expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(0);
- const randomIndex = (Math.floor(Math.random() * (this.projectsData.length - 1)) + 0);
+ const randomIndex = Math.floor(Math.random() * (this.projectsData.length - 1)) + 0;
navigateWithKeys('down', randomIndex, () => {
expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(1);
- expect($(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement)).toHaveClass('is-focused');
+ expect($(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement)).toHaveClass(
+ 'is-focused',
+ );
});
});
it('should select a previous item on UP keypress', () => {
expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(0);
- navigateWithKeys('down', (this.projectsData.length - 1), () => {
+ navigateWithKeys('down', this.projectsData.length - 1, () => {
expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(1);
- const randomIndex = (Math.floor(Math.random() * (this.projectsData.length - 2)) + 0);
+ const randomIndex = Math.floor(Math.random() * (this.projectsData.length - 2)) + 0;
navigateWithKeys('up', randomIndex, () => {
expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(1);
- expect($(`${ITEM_SELECTOR}:eq(${((this.projectsData.length - 2) - randomIndex)}) a`, this.$dropdownMenuElement)).toHaveClass('is-focused');
+ expect(
+ $(
+ `${ITEM_SELECTOR}:eq(${this.projectsData.length - 2 - randomIndex}) a`,
+ this.$dropdownMenuElement,
+ ),
+ ).toHaveClass('is-focused');
});
});
});
@@ -141,9 +153,12 @@ describe('glDropdown', function describeDropdown() {
navigateWithKeys('enter', null, () => {
expect(this.dropdownContainerElement).not.toHaveClass('show');
const link = $(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement);
+
expect(link).toHaveClass('is-active');
const linkedLocation = link.attr('href');
- if (linkedLocation && linkedLocation !== '#') expect(visitUrl).toHaveBeenCalledWith(linkedLocation);
+ if (linkedLocation && linkedLocation !== '#') {
+ expect(visitUrl).toHaveBeenCalledWith(linkedLocation);
+ }
});
});
});
@@ -153,8 +168,9 @@ describe('glDropdown', function describeDropdown() {
this.dropdownContainerElement.trigger({
type: 'keyup',
which: ARROW_KEYS.ESC,
- keyCode: ARROW_KEYS.ESC
+ keyCode: ARROW_KEYS.ESC,
});
+
expect(this.dropdownContainerElement).not.toHaveClass('show');
});
});
@@ -168,19 +184,22 @@ describe('glDropdown', function describeDropdown() {
it('should show loading indicator while search results are being fetched by backend', () => {
const dropdownMenu = document.querySelector('.dropdown-menu');
- expect(dropdownMenu.className.indexOf('is-loading') !== -1).toEqual(true);
+ expect(dropdownMenu.className.indexOf('is-loading')).not.toBe(-1);
remoteCallback();
- expect(dropdownMenu.className.indexOf('is-loading') !== -1).toEqual(false);
+
+ expect(dropdownMenu.className.indexOf('is-loading')).toBe(-1);
});
it('should not focus search input while remote task is not complete', () => {
expect($(document.activeElement)).not.toEqual($(SEARCH_INPUT_SELECTOR));
remoteCallback();
+
expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR));
});
it('should focus search input after remote task is complete', () => {
remoteCallback();
+
expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR));
});
@@ -189,10 +208,11 @@ describe('glDropdown', function describeDropdown() {
this.dropdownContainerElement.trigger({
type: 'keyup',
which: ARROW_KEYS.ESC,
- keyCode: ARROW_KEYS.ESC
+ keyCode: ARROW_KEYS.ESC,
});
this.dropdownButtonElement.click();
this.dropdownContainerElement.trigger('transitionend');
+
expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR));
});
});
@@ -202,6 +222,7 @@ describe('glDropdown', function describeDropdown() {
initDropDown.call(this, false, true);
this.dropdownButtonElement.click();
this.dropdownContainerElement.trigger('transitionend');
+
expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR));
});
});
@@ -213,11 +234,11 @@ describe('glDropdown', function describeDropdown() {
.trigger('focus')
.val('g')
.trigger('input');
+
expect($searchInput.val()).toEqual('g');
this.dropdownButtonElement.trigger('hidden.bs.dropdown');
- $searchInput
- .trigger('blur')
- .trigger('focus');
+ $searchInput.trigger('blur').trigger('focus');
+
expect($searchInput.val()).toEqual('g');
});
@@ -226,31 +247,31 @@ describe('glDropdown', function describeDropdown() {
let dropdown;
beforeEach(() => {
- const dropdownOptions = {
-
- };
+ const dropdownOptions = {};
const $dropdownDiv = $('<div />');
$dropdownDiv.glDropdown(dropdownOptions);
dropdown = $dropdownDiv.data('glDropdown');
});
it('marks items without ID as active', () => {
- const dummyData = { };
+ const dummyData = {};
const html = dropdown.renderItem(dummyData, null, null);
const link = html.querySelector('a');
+
expect(link).toHaveClass('is-active');
});
it('does not mark items with ID as active', () => {
const dummyData = {
- id: 'ea'
+ id: 'ea',
};
const html = dropdown.renderItem(dummyData, null, null);
const link = html.querySelector('a');
+
expect(link).not.toHaveClass('is-active');
});
});
@@ -271,13 +292,14 @@ describe('glDropdown', function describeDropdown() {
// select item the first time
this.dropdownButtonElement.click();
$item.click();
+
expect($item).toHaveClass('is-active');
// select item the second time
this.dropdownButtonElement.click();
$item.click();
+
expect($item).toHaveClass('is-active');
expect($('.dropdown-toggle-text')).toHaveText(this.projectsData[0].id.toString());
});
});
-
diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js
index 21c462cd040..b463c9afbee 100644
--- a/spec/javascripts/gl_field_errors_spec.js
+++ b/spec/javascripts/gl_field_errors_spec.js
@@ -19,6 +19,7 @@ describe('GL Style Field Errors', function() {
expect(this.$form.length).toBe(1);
expect(this.fieldErrors).toBeDefined();
const { inputs } = this.fieldErrors.state;
+
expect(inputs.length).toBe(4);
});
@@ -28,29 +29,50 @@ describe('GL Style Field Errors', function() {
expect(customErrorElem.length).toBe(1);
- const customErrors = this.fieldErrors.state.inputs.filter((input) => {
+ const customErrors = this.fieldErrors.state.inputs.filter(input => {
return input.inputElement.hasClass(customErrorFlag);
});
+
expect(customErrors.length).toBe(0);
});
it('should not show any errors before submit attempt', function() {
- this.$form.find('.email').val('not-a-valid-email').keyup();
- this.$form.find('.text-required').val('').keyup();
- this.$form.find('.alphanumberic').val('?---*').keyup();
+ this.$form
+ .find('.email')
+ .val('not-a-valid-email')
+ .keyup();
+ this.$form
+ .find('.text-required')
+ .val('')
+ .keyup();
+ this.$form
+ .find('.alphanumberic')
+ .val('?---*')
+ .keyup();
const errorsShown = this.$form.find('.gl-field-error-outline');
+
expect(errorsShown.length).toBe(0);
});
it('should show errors when input valid is submitted', function() {
- this.$form.find('.email').val('not-a-valid-email').keyup();
- this.$form.find('.text-required').val('').keyup();
- this.$form.find('.alphanumberic').val('?---*').keyup();
+ this.$form
+ .find('.email')
+ .val('not-a-valid-email')
+ .keyup();
+ this.$form
+ .find('.text-required')
+ .val('')
+ .keyup();
+ this.$form
+ .find('.alphanumberic')
+ .val('?---*')
+ .keyup();
this.$form.submit();
const errorsShown = this.$form.find('.gl-field-error-outline');
+
expect(errorsShown.length).toBe(4);
});
@@ -68,30 +90,35 @@ describe('GL Style Field Errors', function() {
// Then invalid input
emailInputElement.val('not-a-valid-email').keyup();
+
expect(emailInputElement).toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(false);
expect(fieldState.valid).toBe(false);
// Then valid input
emailInputElement.val('email@gitlab.com').keyup();
+
expect(emailInputElement).not.toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(false);
expect(fieldState.valid).toBe(true);
// Then invalid input
emailInputElement.val('not-a-valid-email').keyup();
+
expect(emailInputElement).toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(false);
expect(fieldState.valid).toBe(false);
// Then empty input
emailInputElement.val('').keyup();
+
expect(emailInputElement).toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(true);
expect(fieldState.valid).toBe(false);
// Then valid input
emailInputElement.val('email@gitlab.com').keyup();
+
expect(emailInputElement).not.toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(false);
expect(fieldState.valid).toBe(true);
diff --git a/spec/javascripts/gl_form_spec.js b/spec/javascripts/gl_form_spec.js
index 74383f901b2..69b3dae743a 100644
--- a/spec/javascripts/gl_form_spec.js
+++ b/spec/javascripts/gl_form_spec.js
@@ -5,8 +5,8 @@ import '~/lib/utils/text_utility';
import '~/lib/utils/common_utils';
describe('GLForm', () => {
- describe('when instantiated', function () {
- beforeEach((done) => {
+ describe('when instantiated', function() {
+ beforeEach(done => {
this.form = $('<form class="gfm-form"><textarea class="js-gfm-input"></form>');
this.textarea = this.form.find('textarea');
spyOn($.prototype, 'off').and.returnValue(this.textarea);
@@ -23,7 +23,7 @@ describe('GLForm', () => {
});
describe('setupAutosize', () => {
- beforeEach((done) => {
+ beforeEach(done => {
this.glForm.setupAutosize();
setTimeout(() => {
done();
@@ -101,6 +101,7 @@ describe('GLForm', () => {
spyOn($.prototype, 'outerHeight').and.returnValue(200);
spyOn($.prototype, 'data').and.returnValue(200);
spyOn(autosize, 'destroy');
+
expect(this.glForm.destroyAutosize()).toBeUndefined();
expect(autosize.destroy).not.toHaveBeenCalled();
});
diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js
index 78330dd9633..e73f6d3909e 100644
--- a/spec/javascripts/gpg_badges_spec.js
+++ b/spec/javascripts/gpg_badges_spec.js
@@ -69,6 +69,7 @@ describe('GpgBadges', () => {
.then(() => {
expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
+
expect(spinners.length).toBe(1);
done();
})
@@ -82,6 +83,7 @@ describe('GpgBadges', () => {
.then(() => {
expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
const parentContainer = document.querySelector('.parent-container');
+
expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
done();
})
diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
index d8a8c8cc260..563d134ca81 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
@@ -1,99 +1,119 @@
-/* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */
+/* eslint-disable jasmine/no-suite-dupes, vars-on-top, no-var */
+
import { scaleLinear, scaleTime } from 'd3-scale';
import { timeParse } from 'd3-time-format';
-import { ContributorsGraph, ContributorsMasterGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph';
+import {
+ ContributorsGraph,
+ ContributorsMasterGraph,
+} from '~/pages/projects/graphs/show/stat_graph_contributors_graph';
const d3 = { scaleLinear, scaleTime, timeParse };
-describe("ContributorsGraph", function () {
- describe("#set_x_domain", function () {
- it("set the x_domain", function () {
+describe('ContributorsGraph', function() {
+ describe('#set_x_domain', function() {
+ it('set the x_domain', function() {
ContributorsGraph.set_x_domain(20);
+
expect(ContributorsGraph.prototype.x_domain).toEqual(20);
});
});
- describe("#set_y_domain", function () {
- it("sets the y_domain", function () {
+ describe('#set_y_domain', function() {
+ it('sets the y_domain', function() {
ContributorsGraph.set_y_domain([{ commits: 30 }]);
+
expect(ContributorsGraph.prototype.y_domain).toEqual([0, 30]);
});
});
- describe("#init_x_domain", function () {
- it("sets the initial x_domain", function () {
- ContributorsGraph.init_x_domain([{ date: "2013-01-31" }, { date: "2012-01-31" }]);
- expect(ContributorsGraph.prototype.x_domain).toEqual(["2012-01-31", "2013-01-31"]);
+ describe('#init_x_domain', function() {
+ it('sets the initial x_domain', function() {
+ ContributorsGraph.init_x_domain([{ date: '2013-01-31' }, { date: '2012-01-31' }]);
+
+ expect(ContributorsGraph.prototype.x_domain).toEqual(['2012-01-31', '2013-01-31']);
});
});
- describe("#init_y_domain", function () {
- it("sets the initial y_domain", function () {
+ describe('#init_y_domain', function() {
+ it('sets the initial y_domain', function() {
ContributorsGraph.init_y_domain([{ commits: 30 }]);
+
expect(ContributorsGraph.prototype.y_domain).toEqual([0, 30]);
});
});
- describe("#init_domain", function () {
- it("calls init_x_domain and init_y_domain", function () {
- spyOn(ContributorsGraph, "init_x_domain");
- spyOn(ContributorsGraph, "init_y_domain");
+ describe('#init_domain', function() {
+ it('calls init_x_domain and init_y_domain', function() {
+ spyOn(ContributorsGraph, 'init_x_domain');
+ spyOn(ContributorsGraph, 'init_y_domain');
ContributorsGraph.init_domain();
+
expect(ContributorsGraph.init_x_domain).toHaveBeenCalled();
expect(ContributorsGraph.init_y_domain).toHaveBeenCalled();
});
});
- describe("#set_dates", function () {
- it("sets the dates", function () {
- ContributorsGraph.set_dates("2013-12-01");
- expect(ContributorsGraph.prototype.dates).toEqual("2013-12-01");
+ describe('#set_dates', function() {
+ it('sets the dates', function() {
+ ContributorsGraph.set_dates('2013-12-01');
+
+ expect(ContributorsGraph.prototype.dates).toEqual('2013-12-01');
});
});
- describe("#set_x_domain", function () {
- it("sets the instance's x domain using the prototype's x_domain", function () {
+ describe('#set_x_domain', function() {
+ it("sets the instance's x domain using the prototype's x_domain", function() {
ContributorsGraph.prototype.x_domain = 20;
var instance = new ContributorsGraph();
- instance.x = d3.scaleTime().range([0, 100]).clamp(true);
+ instance.x = d3
+ .scaleTime()
+ .range([0, 100])
+ .clamp(true);
spyOn(instance.x, 'domain');
instance.set_x_domain();
+
expect(instance.x.domain).toHaveBeenCalledWith(20);
});
});
- describe("#set_y_domain", function () {
- it("sets the instance's y domain using the prototype's y_domain", function () {
+ describe('#set_y_domain', function() {
+ it("sets the instance's y domain using the prototype's y_domain", function() {
ContributorsGraph.prototype.y_domain = 30;
var instance = new ContributorsGraph();
- instance.y = d3.scaleLinear().range([100, 0]).nice();
+ instance.y = d3
+ .scaleLinear()
+ .range([100, 0])
+ .nice();
spyOn(instance.y, 'domain');
instance.set_y_domain();
+
expect(instance.y.domain).toHaveBeenCalledWith(30);
});
});
- describe("#set_domain", function () {
- it("calls set_x_domain and set_y_domain", function () {
+ describe('#set_domain', function() {
+ it('calls set_x_domain and set_y_domain', function() {
var instance = new ContributorsGraph();
spyOn(instance, 'set_x_domain');
spyOn(instance, 'set_y_domain');
instance.set_domain();
+
expect(instance.set_x_domain).toHaveBeenCalled();
expect(instance.set_y_domain).toHaveBeenCalled();
});
});
- describe("#set_data", function () {
- it("sets the data", function () {
+ describe('#set_data', function() {
+ it('sets the data', function() {
var instance = new ContributorsGraph();
- instance.set_data("20");
- expect(instance.data).toEqual("20");
+ instance.set_data('20');
+
+ expect(instance.data).toEqual('20');
});
});
});
-describe("ContributorsMasterGraph", function () {
+describe('ContributorsMasterGraph', function() {
// TODO: fix or remove
// describe("#process_dates", function () {
// it("gets and parses dates", function () {
@@ -109,21 +129,23 @@ describe("ContributorsMasterGraph", function () {
// });
// });
- describe("#get_dates", function () {
- it("plucks the date field from data collection", function () {
+ describe('#get_dates', function() {
+ it('plucks the date field from data collection', function() {
var graph = new ContributorsMasterGraph();
- var data = [{ date: "2013-01-01" }, { date: "2012-12-15" }];
- expect(graph.get_dates(data)).toEqual(["2013-01-01", "2012-12-15"]);
+ var data = [{ date: '2013-01-01' }, { date: '2012-12-15' }];
+
+ expect(graph.get_dates(data)).toEqual(['2013-01-01', '2012-12-15']);
});
});
- describe("#parse_dates", function () {
- it("parses the dates", function () {
+ describe('#parse_dates', function() {
+ it('parses the dates', function() {
var graph = new ContributorsMasterGraph();
- var parseDate = d3.timeParse("%Y-%m-%d");
- var data = [{ date: "2013-01-01" }, { date: "2012-12-15" }];
+ var parseDate = d3.timeParse('%Y-%m-%d');
+ var data = [{ date: '2013-01-01' }, { date: '2012-12-15' }];
var correct = [{ date: parseDate(data[0].date) }, { date: parseDate(data[1].date) }];
graph.parse_dates(data);
+
expect(data).toEqual(correct);
});
});
diff --git a/spec/javascripts/graphs/stat_graph_contributors_spec.js b/spec/javascripts/graphs/stat_graph_contributors_spec.js
index e03114c1cc5..2ebb6845a8b 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_spec.js
@@ -20,7 +20,9 @@ describe('ContributorsStatGraph', () => {
graph.change_date_header();
- expect(document.getElementById('date_header').innerText).toBe('31. Januar 2012 – 31. Januar 2013');
+ expect(document.getElementById('date_header').innerText).toBe(
+ '31. Januar 2012 – 31. Januar 2013',
+ );
});
});
});
diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
index 22a9afe1a9d..511b660c671 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
@@ -1,57 +1,86 @@
-/* eslint-disable quotes, no-var, camelcase, object-property-newline, comma-dangle, max-len, vars-on-top, quote-props */
+/* eslint-disable no-var, camelcase, vars-on-top */
import ContributorsStatGraphUtil from '~/pages/projects/graphs/show/stat_graph_contributors_util';
-describe("ContributorsStatGraphUtil", function () {
- describe("#parse_log", function () {
- it("returns a correctly parsed log", function () {
+describe('ContributorsStatGraphUtil', function() {
+ describe('#parse_log', function() {
+ it('returns a correctly parsed log', function() {
var fake_log = [
- { author_email: "karlo@email.com", author_name: "Karlo Soriano", date: "2013-05-09", additions: 471 },
- { author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 6, deletions: 1 },
- { author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 19, deletions: 3 },
- { author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 29, deletions: 3 }
+ {
+ author_email: 'karlo@email.com',
+ author_name: 'Karlo Soriano',
+ date: '2013-05-09',
+ additions: 471,
+ },
+ {
+ author_email: 'dzaporozhets@email.com',
+ author_name: 'Dmitriy Zaporozhets',
+ date: '2013-05-08',
+ additions: 6,
+ deletions: 1,
+ },
+ {
+ author_email: 'dzaporozhets@email.com',
+ author_name: 'Dmitriy Zaporozhets',
+ date: '2013-05-08',
+ additions: 19,
+ deletions: 3,
+ },
+ {
+ author_email: 'dzaporozhets@email.com',
+ author_name: 'Dmitriy Zaporozhets',
+ date: '2013-05-08',
+ additions: 29,
+ deletions: 3,
+ },
];
var correct_parsed_log = {
total: [
- { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 },
- { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 }
+ { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 },
+ { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 },
],
by_author: [
{
- author_name: "Karlo Soriano", author_email: "karlo@email.com",
- "2013-05-09": { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 }
+ author_name: 'Karlo Soriano',
+ author_email: 'karlo@email.com',
+ '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 },
},
{
- author_name: "Dmitriy Zaporozhets", author_email: "dzaporozhets@email.com",
- "2013-05-08": { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 }
- }
- ]
+ author_name: 'Dmitriy Zaporozhets',
+ author_email: 'dzaporozhets@email.com',
+ '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 },
+ },
+ ],
};
+
expect(ContributorsStatGraphUtil.parse_log(fake_log)).toEqual(correct_parsed_log);
});
});
- describe("#store_data", function () {
- var fake_entry = { author: "Karlo Soriano", date: "2013-05-09", additions: 471 };
+ describe('#store_data', function() {
+ var fake_entry = { author: 'Karlo Soriano', date: '2013-05-09', additions: 471 };
var fake_total = {};
var fake_by_author = {};
- it("calls #store_commits", function () {
+ it('calls #store_commits', function() {
spyOn(ContributorsStatGraphUtil, 'store_commits');
ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author);
+
expect(ContributorsStatGraphUtil.store_commits).toHaveBeenCalled();
});
- it("calls #store_additions", function () {
+ it('calls #store_additions', function() {
spyOn(ContributorsStatGraphUtil, 'store_additions');
ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author);
+
expect(ContributorsStatGraphUtil.store_additions).toHaveBeenCalled();
});
- it("calls #store_deletions", function () {
+ it('calls #store_deletions', function() {
spyOn(ContributorsStatGraphUtil, 'store_deletions');
ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author);
+
expect(ContributorsStatGraphUtil.store_deletions).toHaveBeenCalled();
});
});
@@ -68,16 +97,18 @@ describe("ContributorsStatGraphUtil", function () {
// });
// });
- describe("#add", function () {
- it("adds 1 to current test_field in collection", function () {
+ describe('#add', function() {
+ it('adds 1 to current test_field in collection', function() {
var fake_collection = { test_field: 10 };
- ContributorsStatGraphUtil.add(fake_collection, "test_field", 1);
+ ContributorsStatGraphUtil.add(fake_collection, 'test_field', 1);
+
expect(fake_collection.test_field).toEqual(11);
});
- it("inits and adds 1 if test_field in collection is not defined", function () {
+ it('inits and adds 1 if test_field in collection is not defined', function() {
var fake_collection = {};
- ContributorsStatGraphUtil.add(fake_collection, "test_field", 1);
+ ContributorsStatGraphUtil.add(fake_collection, 'test_field', 1);
+
expect(fake_collection.test_field).toEqual(1);
});
});
@@ -106,111 +137,161 @@ describe("ContributorsStatGraphUtil", function () {
// });
// });
- describe("#add_date", function () {
- it("adds a date field to the collection", function () {
- var fake_date = "2013-10-02";
+ describe('#add_date', function() {
+ it('adds a date field to the collection', function() {
+ var fake_date = '2013-10-02';
var fake_collection = {};
ContributorsStatGraphUtil.add_date(fake_date, fake_collection);
- expect(fake_collection[fake_date].date).toEqual("2013-10-02");
+
+ expect(fake_collection[fake_date].date).toEqual('2013-10-02');
});
});
- describe("#add_author", function () {
- it("adds an author field to the collection", function () {
- var fake_author = { author_name: "Author", author_email: 'fake@email.com' };
+ describe('#add_author', function() {
+ it('adds an author field to the collection', function() {
+ var fake_author = { author_name: 'Author', author_email: 'fake@email.com' };
var fake_author_collection = {};
var fake_email_collection = {};
- ContributorsStatGraphUtil.add_author(fake_author, fake_author_collection, fake_email_collection);
- expect(fake_author_collection[fake_author.author_name].author_name).toEqual("Author");
- expect(fake_email_collection[fake_author.author_email].author_name).toEqual("Author");
+ ContributorsStatGraphUtil.add_author(
+ fake_author,
+ fake_author_collection,
+ fake_email_collection,
+ );
+
+ expect(fake_author_collection[fake_author.author_name].author_name).toEqual('Author');
+ expect(fake_email_collection[fake_author.author_email].author_name).toEqual('Author');
});
});
- describe("#get_total_data", function () {
- it("returns the collection sorted via specified field", function () {
+ describe('#get_total_data', function() {
+ it('returns the collection sorted via specified field', function() {
var fake_parsed_log = {
total: [
- { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 },
- { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 }
+ { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 },
+ { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 },
],
by_author: [
{
- author: "Karlo Soriano",
- "2013-05-09": { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 }
+ author: 'Karlo Soriano',
+ '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 },
},
{
- author: "Dmitriy Zaporozhets",
- "2013-05-08": { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 }
- }
- ]
+ author: 'Dmitriy Zaporozhets',
+ '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 },
+ },
+ ],
};
var correct_total_data = [
- { date: "2013-05-08", commits: 3 },
- { date: "2013-05-09", commits: 1 }
+ { date: '2013-05-08', commits: 3 },
+ { date: '2013-05-09', commits: 1 },
];
- expect(ContributorsStatGraphUtil.get_total_data(fake_parsed_log, "commits")).toEqual(correct_total_data);
+
+ expect(ContributorsStatGraphUtil.get_total_data(fake_parsed_log, 'commits')).toEqual(
+ correct_total_data,
+ );
});
});
- describe("#pick_field", function () {
- it("returns the collection with only the specified field and date", function () {
+ describe('#pick_field', function() {
+ it('returns the collection with only the specified field and date', function() {
var fake_parsed_log_total = [
- { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 },
- { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 }
+ { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 },
+ { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 },
];
- ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, "commits");
- var correct_pick_field_data = [{ date: "2013-05-09", commits: 1 }, { date: "2013-05-08", commits: 3 }];
- expect(ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, "commits")).toEqual(correct_pick_field_data);
+ ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, 'commits');
+ var correct_pick_field_data = [
+ { date: '2013-05-09', commits: 1 },
+ { date: '2013-05-08', commits: 3 },
+ ];
+
+ expect(ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, 'commits')).toEqual(
+ correct_pick_field_data,
+ );
});
});
- describe("#get_author_data", function () {
- it("returns the log by author sorted by specified field", function () {
+ describe('#get_author_data', function() {
+ it('returns the log by author sorted by specified field', function() {
var fake_parsed_log = {
total: [
- { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 },
- { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 }
+ { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 },
+ { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 },
],
by_author: [
{
- author_name: "Karlo Soriano", author_email: "karlo@email.com",
- "2013-05-09": { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 }
+ author_name: 'Karlo Soriano',
+ author_email: 'karlo@email.com',
+ '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 },
},
{
- author_name: "Dmitriy Zaporozhets", author_email: "dzaporozhets@email.com",
- "2013-05-08": { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 }
- }
- ]
+ author_name: 'Dmitriy Zaporozhets',
+ author_email: 'dzaporozhets@email.com',
+ '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 },
+ },
+ ],
};
var correct_author_data = [
- { author_name: "Dmitriy Zaporozhets", author_email: "dzaporozhets@email.com", dates: { "2013-05-08": 3 }, deletions: 7, additions: 54, "commits": 3 },
- { author_name: "Karlo Soriano", author_email: "karlo@email.com", dates: { "2013-05-09": 1 }, deletions: 0, additions: 471, commits: 1 }
+ {
+ author_name: 'Dmitriy Zaporozhets',
+ author_email: 'dzaporozhets@email.com',
+ dates: { '2013-05-08': 3 },
+ deletions: 7,
+ additions: 54,
+ commits: 3,
+ },
+ {
+ author_name: 'Karlo Soriano',
+ author_email: 'karlo@email.com',
+ dates: { '2013-05-09': 1 },
+ deletions: 0,
+ additions: 471,
+ commits: 1,
+ },
];
- expect(ContributorsStatGraphUtil.get_author_data(fake_parsed_log, "commits")).toEqual(correct_author_data);
+
+ expect(ContributorsStatGraphUtil.get_author_data(fake_parsed_log, 'commits')).toEqual(
+ correct_author_data,
+ );
});
});
- describe("#parse_log_entry", function () {
- it("adds the corresponding info from the log entry to the author", function () {
- var fake_log_entry = { author_name: "Karlo Soriano", author_email: "karlo@email.com",
- "2013-05-09": { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 }
+ describe('#parse_log_entry', function() {
+ it('adds the corresponding info from the log entry to the author', function() {
+ var fake_log_entry = {
+ author_name: 'Karlo Soriano',
+ author_email: 'karlo@email.com',
+ '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 },
+ };
+ var correct_parsed_log = {
+ author_name: 'Karlo Soriano',
+ author_email: 'karlo@email.com',
+ dates: { '2013-05-09': 1 },
+ deletions: 0,
+ additions: 471,
+ commits: 1,
};
- var correct_parsed_log = { author_name: "Karlo Soriano", author_email: "karlo@email.com", dates: { "2013-05-09": 1 }, deletions: 0, additions: 471, commits: 1 };
- expect(ContributorsStatGraphUtil.parse_log_entry(fake_log_entry, 'commits', null)).toEqual(correct_parsed_log);
+
+ expect(ContributorsStatGraphUtil.parse_log_entry(fake_log_entry, 'commits', null)).toEqual(
+ correct_parsed_log,
+ );
});
});
- describe("#in_range", function () {
- var date = "2013-05-09";
- it("returns true if date_range is null", function () {
+ describe('#in_range', function() {
+ var date = '2013-05-09';
+ it('returns true if date_range is null', function() {
expect(ContributorsStatGraphUtil.in_range(date, null)).toEqual(true);
});
- it("returns true if date is in range", function () {
- var date_range = [new Date("2013-01-01"), new Date("2013-12-12")];
+
+ it('returns true if date is in range', function() {
+ var date_range = [new Date('2013-01-01'), new Date('2013-12-12')];
+
expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(true);
});
- it("returns false if date is not in range", function () {
- var date_range = [new Date("1999-12-01"), new Date("2000-12-01")];
+
+ it('returns false if date is not in range', function() {
+ var date_range = [new Date('1999-12-01'), new Date('2000-12-01')];
+
expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(false);
});
});
diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js
index 03d4b472b87..d832441dc93 100644
--- a/spec/javascripts/groups/components/app_spec.js
+++ b/spec/javascripts/groups/components/app_spec.js
@@ -9,9 +9,14 @@ import GroupsStore from '~/groups/store/groups_store';
import GroupsService from '~/groups/service/groups_service';
import {
- mockEndpoint, mockGroups, mockSearchedGroups,
- mockRawPageInfo, mockParentGroupItem, mockRawChildren,
- mockChildren, mockPageInfo,
+ mockEndpoint,
+ mockGroups,
+ mockSearchedGroups,
+ mockRawPageInfo,
+ mockParentGroupItem,
+ mockRawChildren,
+ mockChildren,
+ mockPageInfo,
} from '../mock_data';
const createComponent = (hideProjects = false) => {
@@ -19,6 +24,8 @@ const createComponent = (hideProjects = false) => {
const store = new GroupsStore(false);
const service = new GroupsService(mockEndpoint);
+ store.state.pageInfo = mockPageInfo;
+
return new Component({
propsData: {
store,
@@ -28,22 +35,23 @@ const createComponent = (hideProjects = false) => {
});
};
-const returnServicePromise = (data, failed) => new Promise((resolve, reject) => {
- if (failed) {
- reject(data);
- } else {
- resolve({
- json() {
- return data;
- },
- });
- }
-});
+const returnServicePromise = (data, failed) =>
+ new Promise((resolve, reject) => {
+ if (failed) {
+ reject(data);
+ } else {
+ resolve({
+ json() {
+ return data;
+ },
+ });
+ }
+ });
describe('AppComponent', () => {
let vm;
- beforeEach((done) => {
+ beforeEach(done => {
Vue.component('group-folder', groupFolderComponent);
Vue.component('group-item', groupItemComponent);
@@ -68,6 +76,7 @@ describe('AppComponent', () => {
spyOn(vm.store, 'getGroups');
const { groups } = vm;
+
expect(vm.store.getGroups).toHaveBeenCalled();
expect(groups).not.toBeDefined();
});
@@ -78,6 +87,7 @@ describe('AppComponent', () => {
spyOn(vm.store, 'getPaginationInfo');
const { pageInfo } = vm;
+
expect(vm.store.getPaginationInfo).toHaveBeenCalled();
expect(pageInfo).not.toBeDefined();
});
@@ -94,7 +104,7 @@ describe('AppComponent', () => {
});
describe('fetchGroups', () => {
- it('should call `getGroups` with all the params provided', (done) => {
+ it('should call `getGroups` with all the params provided', done => {
spyOn(vm.service, 'getGroups').and.returnValue(returnServicePromise(mockGroups));
vm.fetchGroups({
@@ -110,8 +120,10 @@ describe('AppComponent', () => {
}, 0);
});
- it('should set headers to store for building pagination info when called with `updatePagination`', (done) => {
- spyOn(vm.service, 'getGroups').and.returnValue(returnServicePromise({ headers: mockRawPageInfo }));
+ it('should set headers to store for building pagination info when called with `updatePagination`', done => {
+ spyOn(vm.service, 'getGroups').and.returnValue(
+ returnServicePromise({ headers: mockRawPageInfo }),
+ );
spyOn(vm, 'updatePagination');
vm.fetchGroups({ updatePagination: true });
@@ -122,7 +134,7 @@ describe('AppComponent', () => {
}, 0);
});
- it('should show flash error when request fails', (done) => {
+ it('should show flash error when request fails', done => {
spyOn(vm.service, 'getGroups').and.returnValue(returnServicePromise(null, true));
spyOn($, 'scrollTo');
spyOn(window, 'Flash');
@@ -138,12 +150,13 @@ describe('AppComponent', () => {
});
describe('fetchAllGroups', () => {
- it('should fetch default set of groups', (done) => {
+ it('should fetch default set of groups', done => {
spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockGroups));
spyOn(vm, 'updatePagination').and.callThrough();
spyOn(vm, 'updateGroups').and.callThrough();
vm.fetchAllGroups();
+
expect(vm.isLoading).toBe(true);
expect(vm.fetchGroups).toHaveBeenCalled();
setTimeout(() => {
@@ -153,11 +166,12 @@ describe('AppComponent', () => {
}, 0);
});
- it('should fetch matching set of groups when app is loaded with search query', (done) => {
+ it('should fetch matching set of groups when app is loaded with search query', done => {
spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockSearchedGroups));
spyOn(vm, 'updateGroups').and.callThrough();
vm.fetchAllGroups();
+
expect(vm.fetchGroups).toHaveBeenCalledWith({
page: null,
filterGroupsBy: null,
@@ -173,7 +187,7 @@ describe('AppComponent', () => {
});
describe('fetchPage', () => {
- it('should fetch groups for provided page details and update window state', (done) => {
+ it('should fetch groups for provided page details and update window state', done => {
spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockGroups));
spyOn(vm, 'updateGroups').and.callThrough();
const mergeUrlParams = spyOnDependency(appComponent, 'mergeUrlParams').and.callThrough();
@@ -181,6 +195,7 @@ describe('AppComponent', () => {
spyOn($, 'scrollTo');
vm.fetchPage(2, null, null, true);
+
expect(vm.isLoading).toBe(true);
expect(vm.fetchGroups).toHaveBeenCalledWith({
page: 2,
@@ -193,9 +208,14 @@ describe('AppComponent', () => {
expect(vm.isLoading).toBe(false);
expect($.scrollTo).toHaveBeenCalledWith(0);
expect(mergeUrlParams).toHaveBeenCalledWith({ page: 2 }, jasmine.any(String));
- expect(window.history.replaceState).toHaveBeenCalledWith({
- page: jasmine.any(String),
- }, jasmine.any(String), jasmine.any(String));
+ expect(window.history.replaceState).toHaveBeenCalledWith(
+ {
+ page: jasmine.any(String),
+ },
+ jasmine.any(String),
+ jasmine.any(String),
+ );
+
expect(vm.updateGroups).toHaveBeenCalled();
done();
}, 0);
@@ -211,11 +231,12 @@ describe('AppComponent', () => {
groupItem.isChildrenLoading = false;
});
- it('should fetch children of given group and expand it if group is collapsed and children are not loaded', (done) => {
+ it('should fetch children of given group and expand it if group is collapsed and children are not loaded', done => {
spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockRawChildren));
spyOn(vm.store, 'setGroupChildren');
vm.toggleChildren(groupItem);
+
expect(groupItem.isChildrenLoading).toBe(true);
expect(vm.fetchGroups).toHaveBeenCalledWith({
parentId: groupItem.id,
@@ -231,6 +252,7 @@ describe('AppComponent', () => {
groupItem.children = mockRawChildren;
vm.toggleChildren(groupItem);
+
expect(vm.fetchGroups).not.toHaveBeenCalled();
expect(groupItem.isOpen).toBe(true);
});
@@ -240,14 +262,16 @@ describe('AppComponent', () => {
groupItem.isOpen = true;
vm.toggleChildren(groupItem);
+
expect(vm.fetchGroups).not.toHaveBeenCalled();
expect(groupItem.isOpen).toBe(false);
});
- it('should set `isChildrenLoading` back to `false` if load request fails', (done) => {
+ it('should set `isChildrenLoading` back to `false` if load request fails', done => {
spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise({}, true));
vm.toggleChildren(groupItem);
+
expect(groupItem.isChildrenLoading).toBe(true);
setTimeout(() => {
expect(groupItem.isChildrenLoading).toBe(false);
@@ -259,20 +283,26 @@ describe('AppComponent', () => {
describe('showLeaveGroupModal', () => {
it('caches candidate group (as props) which is to be left', () => {
const group = Object.assign({}, mockParentGroupItem);
+
expect(vm.targetGroup).toBe(null);
expect(vm.targetParentGroup).toBe(null);
vm.showLeaveGroupModal(group, mockParentGroupItem);
+
expect(vm.targetGroup).not.toBe(null);
expect(vm.targetParentGroup).not.toBe(null);
});
it('updates props which show modal confirmation dialog', () => {
const group = Object.assign({}, mockParentGroupItem);
+
expect(vm.showModal).toBe(false);
expect(vm.groupLeaveConfirmationMessage).toBe('');
vm.showLeaveGroupModal(group, mockParentGroupItem);
+
expect(vm.showModal).toBe(true);
- expect(vm.groupLeaveConfirmationMessage).toBe(`Are you sure you want to leave the "${group.fullName}" group?`);
+ expect(vm.groupLeaveConfirmationMessage).toBe(
+ `Are you sure you want to leave the "${group.fullName}" group?`,
+ );
});
});
@@ -280,8 +310,10 @@ describe('AppComponent', () => {
it('hides modal confirmation which is shown before leaving the group', () => {
const group = Object.assign({}, mockParentGroupItem);
vm.showLeaveGroupModal(group, mockParentGroupItem);
+
expect(vm.showModal).toBe(true);
vm.hideLeaveGroupModal();
+
expect(vm.showModal).toBe(false);
});
});
@@ -299,7 +331,7 @@ describe('AppComponent', () => {
vm.targetParentGroup = groupItem;
});
- it('hides modal confirmation leave group and remove group item from tree', (done) => {
+ it('hides modal confirmation leave group and remove group item from tree', done => {
const notice = `You left the "${childGroupItem.fullName}" group.`;
spyOn(vm.service, 'leaveGroup').and.returnValue(returnServicePromise({ notice }));
spyOn(vm.store, 'removeGroup').and.callThrough();
@@ -307,6 +339,7 @@ describe('AppComponent', () => {
spyOn($, 'scrollTo');
vm.leaveGroup();
+
expect(vm.showModal).toBe(false);
expect(vm.targetGroup.isBeingRemoved).toBe(true);
expect(vm.service.leaveGroup).toHaveBeenCalledWith(vm.targetGroup.leavePath);
@@ -318,13 +351,16 @@ describe('AppComponent', () => {
}, 0);
});
- it('should show error flash message if request failed to leave group', (done) => {
+ it('should show error flash message if request failed to leave group', done => {
const message = 'An error occurred. Please try again.';
- spyOn(vm.service, 'leaveGroup').and.returnValue(returnServicePromise({ status: 500 }, true));
+ spyOn(vm.service, 'leaveGroup').and.returnValue(
+ returnServicePromise({ status: 500 }, true),
+ );
spyOn(vm.store, 'removeGroup').and.callThrough();
spyOn(window, 'Flash');
vm.leaveGroup();
+
expect(vm.targetGroup.isBeingRemoved).toBe(true);
expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath);
setTimeout(() => {
@@ -335,13 +371,16 @@ describe('AppComponent', () => {
}, 0);
});
- it('should show appropriate error flash message if request forbids to leave group', (done) => {
+ it('should show appropriate error flash message if request forbids to leave group', done => {
const message = 'Failed to leave the group. Please make sure you are not the only owner.';
- spyOn(vm.service, 'leaveGroup').and.returnValue(returnServicePromise({ status: 403 }, true));
+ spyOn(vm.service, 'leaveGroup').and.returnValue(
+ returnServicePromise({ status: 403 }, true),
+ );
spyOn(vm.store, 'removeGroup').and.callThrough();
spyOn(window, 'Flash');
vm.leaveGroup(childGroupItem, groupItem);
+
expect(vm.targetGroup.isBeingRemoved).toBe(true);
expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath);
setTimeout(() => {
@@ -358,6 +397,7 @@ describe('AppComponent', () => {
spyOn(vm.store, 'setPaginationInfo');
vm.updatePagination(mockRawPageInfo);
+
expect(vm.store.setPaginationInfo).toHaveBeenCalledWith(mockRawPageInfo);
});
});
@@ -367,6 +407,7 @@ describe('AppComponent', () => {
spyOn(vm.store, 'setGroups');
vm.updateGroups(mockGroups);
+
expect(vm.store.setGroups).toHaveBeenCalledWith(mockGroups);
});
@@ -374,21 +415,24 @@ describe('AppComponent', () => {
spyOn(vm.store, 'setSearchedGroups');
vm.updateGroups(mockGroups, true);
+
expect(vm.store.setSearchedGroups).toHaveBeenCalledWith(mockGroups);
});
it('should set `isSearchEmpty` prop based on groups count', () => {
vm.updateGroups(mockGroups);
+
expect(vm.isSearchEmpty).toBe(false);
vm.updateGroups([]);
+
expect(vm.isSearchEmpty).toBe(true);
});
});
});
describe('created', () => {
- it('should bind event listeners on eventHub', (done) => {
+ it('should bind event listeners on eventHub', done => {
spyOn(eventHub, '$on');
const newVm = createComponent();
@@ -405,21 +449,21 @@ describe('AppComponent', () => {
});
});
- it('should initialize `searchEmptyMessage` prop with correct string when `hideProjects` is `false`', (done) => {
+ it('should initialize `searchEmptyMessage` prop with correct string when `hideProjects` is `false`', done => {
const newVm = createComponent();
newVm.$mount();
Vue.nextTick(() => {
- expect(newVm.searchEmptyMessage).toBe('Sorry, no groups or projects matched your search');
+ expect(newVm.searchEmptyMessage).toBe('No groups or projects matched your search');
newVm.$destroy();
done();
});
});
- it('should initialize `searchEmptyMessage` prop with correct string when `hideProjects` is `true`', (done) => {
+ it('should initialize `searchEmptyMessage` prop with correct string when `hideProjects` is `true`', done => {
const newVm = createComponent(true);
newVm.$mount();
Vue.nextTick(() => {
- expect(newVm.searchEmptyMessage).toBe('Sorry, no groups matched your search');
+ expect(newVm.searchEmptyMessage).toBe('No groups matched your search');
newVm.$destroy();
done();
});
@@ -427,7 +471,7 @@ describe('AppComponent', () => {
});
describe('beforeDestroy', () => {
- it('should unbind event listeners on eventHub', (done) => {
+ it('should unbind event listeners on eventHub', done => {
spyOn(eventHub, '$off');
const newVm = createComponent();
@@ -454,7 +498,7 @@ describe('AppComponent', () => {
vm.$destroy();
});
- it('should render loading icon', (done) => {
+ it('should render loading icon', done => {
vm.isLoading = true;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
@@ -463,21 +507,21 @@ describe('AppComponent', () => {
});
});
- it('should render groups tree', (done) => {
+ it('should render groups tree', done => {
vm.store.state.groups = [mockParentGroupItem];
vm.isLoading = false;
- vm.store.state.pageInfo = mockPageInfo;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined();
done();
});
});
- it('renders modal confirmation dialog', (done) => {
+ it('renders modal confirmation dialog', done => {
vm.groupLeaveConfirmationMessage = 'Are you sure you want to leave the "foo" group?';
vm.showModal = true;
Vue.nextTick(() => {
const modalDialogEl = vm.$el.querySelector('.modal');
+
expect(modalDialogEl).not.toBe(null);
expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?');
expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave');
diff --git a/spec/javascripts/groups/components/group_folder_spec.js b/spec/javascripts/groups/components/group_folder_spec.js
index 4eb198595fb..fdfd1b82bd8 100644
--- a/spec/javascripts/groups/components/group_folder_spec.js
+++ b/spec/javascripts/groups/components/group_folder_spec.js
@@ -18,7 +18,7 @@ const createComponent = (groups = mockGroups, parentGroup = mockParentGroupItem)
describe('GroupFolderComponent', () => {
let vm;
- beforeEach((done) => {
+ beforeEach(done => {
Vue.component('group-item', groupItemComponent);
vm = createComponent();
@@ -59,6 +59,7 @@ describe('GroupFolderComponent', () => {
const newVm = createComponent(mockGroups, parentGroup);
newVm.$mount();
+
expect(newVm.$el.querySelector('li.group-row a.has-more-items')).toBeDefined();
newVm.$destroy();
});
diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js
index d0cac5efc40..4d6d0c895b6 100644
--- a/spec/javascripts/groups/components/group_item_spec.js
+++ b/spec/javascripts/groups/components/group_item_spec.js
@@ -17,7 +17,7 @@ const createComponent = (group = mockParentGroupItem, parentGroup = mockChildren
describe('GroupItemComponent', () => {
let vm;
- beforeEach((done) => {
+ beforeEach(done => {
Vue.component('group-folder', groupFolderComponent);
vm = createComponent();
@@ -44,8 +44,8 @@ describe('GroupItemComponent', () => {
const { rowClass } = vm;
expect(Object.keys(rowClass).length).toBe(classes.length);
- Object.keys(rowClass).forEach((className) => {
- expect(classes.indexOf(className) > -1).toBeTruthy();
+ Object.keys(rowClass).forEach(className => {
+ expect(classes.indexOf(className)).toBeGreaterThan(-1);
});
});
});
@@ -57,11 +57,13 @@ describe('GroupItemComponent', () => {
group.childrenCount = 5;
newVm = createComponent(group);
+
expect(newVm.hasChildren).toBeTruthy();
newVm.$destroy();
group.childrenCount = 0;
newVm = createComponent(group);
+
expect(newVm.hasChildren).toBeFalsy();
newVm.$destroy();
});
@@ -74,11 +76,13 @@ describe('GroupItemComponent', () => {
group.avatarUrl = null;
newVm = createComponent(group);
+
expect(newVm.hasAvatar).toBeFalsy();
newVm.$destroy();
group.avatarUrl = '/uploads/group_avatar.png';
newVm = createComponent(group);
+
expect(newVm.hasAvatar).toBeTruthy();
newVm.$destroy();
});
@@ -91,11 +95,13 @@ describe('GroupItemComponent', () => {
group.type = 'group';
newVm = createComponent(group);
+
expect(newVm.isGroup).toBeTruthy();
newVm.$destroy();
group.type = 'project';
newVm = createComponent(group);
+
expect(newVm.isGroup).toBeFalsy();
newVm.$destroy();
});
@@ -127,10 +133,11 @@ describe('GroupItemComponent', () => {
spyOn(eventHub, '$emit');
vm.onClickRowGroup(event);
+
expect(eventHub.$emit).toHaveBeenCalledWith('toggleChildren', vm.group);
});
- it('should navigate page to group homepage if group does not have any children present', (done) => {
+ it('should navigate page to group homepage if group does not have any children present', done => {
const group = Object.assign({}, mockParentGroupItem);
group.childrenCount = 0;
const newVm = createComponent(group);
diff --git a/spec/javascripts/groups/components/groups_spec.js b/spec/javascripts/groups/components/groups_spec.js
index 793c4909d89..6ba4fe23a69 100644
--- a/spec/javascripts/groups/components/groups_spec.js
+++ b/spec/javascripts/groups/components/groups_spec.js
@@ -21,7 +21,7 @@ const createComponent = (searchEmpty = false) => {
describe('GroupsComponent', () => {
let vm;
- beforeEach((done) => {
+ beforeEach(done => {
Vue.component('group-folder', groupFolderComponent);
Vue.component('group-item', groupItemComponent);
@@ -42,23 +42,30 @@ describe('GroupsComponent', () => {
spyOn(eventHub, '$emit').and.stub();
vm.change(2);
- expect(eventHub.$emit).toHaveBeenCalledWith('fetchPage', 2, jasmine.any(Object), jasmine.any(Object), jasmine.any(Object));
+
+ expect(eventHub.$emit).toHaveBeenCalledWith(
+ 'fetchPage',
+ 2,
+ jasmine.any(Object),
+ jasmine.any(Object),
+ jasmine.any(Object),
+ );
});
});
});
describe('template', () => {
- it('should render component template correctly', (done) => {
+ it('should render component template correctly', done => {
Vue.nextTick(() => {
expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined();
expect(vm.$el.querySelector('.group-list-tree')).toBeDefined();
expect(vm.$el.querySelector('.gl-pagination')).toBeDefined();
- expect(vm.$el.querySelectorAll('.has-no-search-results').length === 0).toBeTruthy();
+ expect(vm.$el.querySelectorAll('.has-no-search-results').length).toBe(0);
done();
});
});
- it('should render empty search message when `searchEmpty` is `true`', (done) => {
+ it('should render empty search message when `searchEmpty` is `true`', done => {
vm.searchEmpty = true;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.has-no-search-results')).toBeDefined();
diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js
index 15fd37ebcd2..3f66e7fd6f2 100644
--- a/spec/javascripts/groups/components/item_actions_spec.js
+++ b/spec/javascripts/groups/components/item_actions_spec.js
@@ -30,7 +30,12 @@ describe('ItemActionsComponent', () => {
it('emits `showLeaveGroupModal` event with `group` and `parentGroup` props', () => {
spyOn(eventHub, '$emit');
vm.onLeaveGroup();
- expect(eventHub.$emit).toHaveBeenCalledWith('showLeaveGroupModal', vm.group, vm.parentGroup);
+
+ expect(eventHub.$emit).toHaveBeenCalledWith(
+ 'showLeaveGroupModal',
+ vm.group,
+ vm.parentGroup,
+ );
});
});
});
@@ -46,6 +51,7 @@ describe('ItemActionsComponent', () => {
const newVm = createComponent(group);
const editBtn = newVm.$el.querySelector('a.edit-group');
+
expect(editBtn).toBeDefined();
expect(editBtn.classList.contains('no-expand')).toBeTruthy();
expect(editBtn.getAttribute('href')).toBe(group.editPath);
@@ -63,6 +69,7 @@ describe('ItemActionsComponent', () => {
const newVm = createComponent(group);
const leaveBtn = newVm.$el.querySelector('a.leave-group');
+
expect(leaveBtn).toBeDefined();
expect(leaveBtn.classList.contains('no-expand')).toBeTruthy();
expect(leaveBtn.getAttribute('href')).toBe(group.leavePath);
diff --git a/spec/javascripts/groups/components/item_caret_spec.js b/spec/javascripts/groups/components/item_caret_spec.js
index 36f838a104f..6e430dbcdb2 100644
--- a/spec/javascripts/groups/components/item_caret_spec.js
+++ b/spec/javascripts/groups/components/item_caret_spec.js
@@ -16,6 +16,7 @@ describe('ItemCaretComponent', () => {
describe('template', () => {
it('should render component template correctly', () => {
const vm = createComponent();
+
expect(vm.$el.classList.contains('folder-caret')).toBeTruthy();
expect(vm.$el.querySelectorAll('svg').length).toBe(1);
vm.$destroy();
@@ -23,12 +24,14 @@ describe('ItemCaretComponent', () => {
it('should render caret down icon if `isGroupOpen` prop is `true`', () => {
const vm = createComponent(true);
+
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-down');
vm.$destroy();
});
it('should render caret right icon if `isGroupOpen` prop is `false`', () => {
const vm = createComponent();
+
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-right');
vm.$destroy();
});
diff --git a/spec/javascripts/groups/components/item_stats_spec.js b/spec/javascripts/groups/components/item_stats_spec.js
index ee7ee18259e..00d6a4817d7 100644
--- a/spec/javascripts/groups/components/item_stats_spec.js
+++ b/spec/javascripts/groups/components/item_stats_spec.js
@@ -22,9 +22,10 @@ describe('ItemStatsComponent', () => {
describe('computed', () => {
describe('visibilityIcon', () => {
it('should return icon class based on `item.visibility` value', () => {
- Object.keys(VISIBILITY_TYPE_ICON).forEach((visibility) => {
+ Object.keys(VISIBILITY_TYPE_ICON).forEach(visibility => {
const item = Object.assign({}, mockParentGroupItem, { visibility });
const vm = createComponent(item);
+
expect(vm.visibilityIcon).toBe(VISIBILITY_TYPE_ICON[visibility]);
vm.$destroy();
});
@@ -33,24 +34,26 @@ describe('ItemStatsComponent', () => {
describe('visibilityTooltip', () => {
it('should return tooltip string for Group based on `item.visibility` value', () => {
- Object.keys(GROUP_VISIBILITY_TYPE).forEach((visibility) => {
+ Object.keys(GROUP_VISIBILITY_TYPE).forEach(visibility => {
const item = Object.assign({}, mockParentGroupItem, {
visibility,
type: ITEM_TYPE.GROUP,
});
const vm = createComponent(item);
+
expect(vm.visibilityTooltip).toBe(GROUP_VISIBILITY_TYPE[visibility]);
vm.$destroy();
});
});
it('should return tooltip string for Project based on `item.visibility` value', () => {
- Object.keys(PROJECT_VISIBILITY_TYPE).forEach((visibility) => {
+ Object.keys(PROJECT_VISIBILITY_TYPE).forEach(visibility => {
const item = Object.assign({}, mockParentGroupItem, {
visibility,
type: ITEM_TYPE.PROJECT,
});
const vm = createComponent(item);
+
expect(vm.visibilityTooltip).toBe(PROJECT_VISIBILITY_TYPE[visibility]);
vm.$destroy();
});
@@ -64,11 +67,13 @@ describe('ItemStatsComponent', () => {
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT });
vm = createComponent(item);
+
expect(vm.isProject).toBeTruthy();
vm.$destroy();
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
vm = createComponent(item);
+
expect(vm.isProject).toBeFalsy();
vm.$destroy();
});
@@ -81,11 +86,13 @@ describe('ItemStatsComponent', () => {
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
vm = createComponent(item);
+
expect(vm.isGroup).toBeTruthy();
vm.$destroy();
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT });
vm = createComponent(item);
+
expect(vm.isGroup).toBeFalsy();
vm.$destroy();
});
@@ -105,9 +112,10 @@ describe('ItemStatsComponent', () => {
const vm = createComponent();
const visibilityIconEl = vm.$el.querySelector('.item-visibility');
+
expect(visibilityIconEl).not.toBe(null);
expect(visibilityIconEl.dataset.originalTitle).toBe(vm.visibilityTooltip);
- expect(visibilityIconEl.querySelectorAll('svg').length > 0).toBeTruthy();
+ expect(visibilityIconEl.querySelectorAll('svg').length).toBeGreaterThan(0);
vm.$destroy();
});
@@ -120,10 +128,11 @@ describe('ItemStatsComponent', () => {
const vm = createComponent(item);
const projectStarIconEl = vm.$el.querySelector('.project-stars');
- expect(projectStarIconEl).not.toBe(null);
- expect(projectStarIconEl.querySelectorAll('svg').length > 0).toBeTruthy();
- expect(projectStarIconEl.querySelectorAll('.stat-value').length > 0).toBeTruthy();
- expect(vm.$el.querySelectorAll('.last-updated').length > 0).toBeTruthy();
+
+ expect(projectStarIconEl).not.toBeNull();
+ expect(projectStarIconEl.querySelectorAll('svg').length).toBeGreaterThan(0);
+ expect(projectStarIconEl.querySelectorAll('.stat-value').length).toBeGreaterThan(0);
+ expect(vm.$el.querySelectorAll('.last-updated').length).toBeGreaterThan(0);
vm.$destroy();
});
diff --git a/spec/javascripts/groups/components/item_stats_value_spec.js b/spec/javascripts/groups/components/item_stats_value_spec.js
index 5e35ae4d36c..ea8edcf49cd 100644
--- a/spec/javascripts/groups/components/item_stats_value_spec.js
+++ b/spec/javascripts/groups/components/item_stats_value_spec.js
@@ -29,11 +29,13 @@ describe('ItemStatsValueComponent', () => {
describe('isValuePresent', () => {
it('returns true if non-empty `value` is present', () => {
vm = createComponent(Object.assign({}, itemConfig, { value: 10 }));
+
expect(vm.isValuePresent).toBeTruthy();
});
it('returns false if empty `value` is present', () => {
vm = createComponent(itemConfig);
+
expect(vm.isValuePresent).toBeFalsy();
});
@@ -57,8 +59,8 @@ describe('ItemStatsValueComponent', () => {
it('renders component element correctly', () => {
expect(vm.$el.classList.contains('number-subgroups')).toBeTruthy();
- expect(vm.$el.querySelectorAll('svg').length > 0).toBeTruthy();
- expect(vm.$el.querySelectorAll('.stat-value').length > 0).toBeTruthy();
+ expect(vm.$el.querySelectorAll('svg').length).toBeGreaterThan(0);
+ expect(vm.$el.querySelectorAll('.stat-value').length).toBeGreaterThan(0);
});
it('renders element tooltip correctly', () => {
diff --git a/spec/javascripts/groups/components/item_type_icon_spec.js b/spec/javascripts/groups/components/item_type_icon_spec.js
index 24380689b29..73108512222 100644
--- a/spec/javascripts/groups/components/item_type_icon_spec.js
+++ b/spec/javascripts/groups/components/item_type_icon_spec.js
@@ -18,6 +18,7 @@ describe('ItemTypeIconComponent', () => {
it('should render component template correctly', () => {
const vm = createComponent();
vm.$mount();
+
expect(vm.$el.classList.contains('item-type-icon')).toBeTruthy();
vm.$destroy();
});
@@ -27,11 +28,13 @@ describe('ItemTypeIconComponent', () => {
vm = createComponent(ITEM_TYPE.GROUP, true);
vm.$mount();
+
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder-open');
vm.$destroy();
vm = createComponent(ITEM_TYPE.GROUP);
vm.$mount();
+
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder');
vm.$destroy();
});
@@ -41,11 +44,13 @@ describe('ItemTypeIconComponent', () => {
vm = createComponent(ITEM_TYPE.PROJECT);
vm.$mount();
+
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('bookmark');
vm.$destroy();
vm = createComponent(ITEM_TYPE.GROUP);
vm.$mount();
+
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).not.toContain('bookmark');
vm.$destroy();
});
diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js
index 8bf6417487d..2fdc844f3d9 100644
--- a/spec/javascripts/groups/mock_data.js
+++ b/spec/javascripts/groups/mock_data.js
@@ -340,7 +340,8 @@ export const mockSearchedGroups = [
{
id: 17,
name: 'v4.4',
- description: 'Voluptatem qui ea error aperiam veritatis doloremque consequatur temporibus.',
+ description:
+ 'Voluptatem qui ea error aperiam veritatis doloremque consequatur temporibus.',
visibility: 'public',
full_name: 'platform / hardware / bsp / kernel / common / v4.4',
relative_path: '/platform/hardware/bsp/kernel/common/v4.4',
diff --git a/spec/javascripts/groups/service/groups_service_spec.js b/spec/javascripts/groups/service/groups_service_spec.js
index 20bb63687f7..339e5131615 100644
--- a/spec/javascripts/groups/service/groups_service_spec.js
+++ b/spec/javascripts/groups/service/groups_service_spec.js
@@ -24,9 +24,11 @@ describe('GroupsService', () => {
};
service.getGroups(55, 2, 'git', 'created_asc', true);
+
expect(service.groups.get).toHaveBeenCalledWith({ parent_id: 55 });
service.getGroups(null, 2, 'git', 'created_asc', true);
+
expect(service.groups.get).toHaveBeenCalledWith(queryParams);
});
});
@@ -36,6 +38,7 @@ describe('GroupsService', () => {
spyOn(Vue.http, 'delete').and.stub();
service.leaveGroup(mockParentGroupItem.leavePath);
+
expect(Vue.http.delete).toHaveBeenCalledWith(mockParentGroupItem.leavePath);
});
});
diff --git a/spec/javascripts/groups/store/groups_store_spec.js b/spec/javascripts/groups/store/groups_store_spec.js
index d74f38f476e..38de4b89f31 100644
--- a/spec/javascripts/groups/store/groups_store_spec.js
+++ b/spec/javascripts/groups/store/groups_store_spec.js
@@ -1,7 +1,9 @@
import GroupsStore from '~/groups/store/groups_store';
import {
- mockGroups, mockSearchedGroups,
- mockParentGroupItem, mockRawChildren,
+ mockGroups,
+ mockSearchedGroups,
+ mockParentGroupItem,
+ mockRawChildren,
mockRawPageInfo,
} from '../mock_data';
@@ -11,12 +13,14 @@ describe('ProjectsStore', () => {
let store;
store = new GroupsStore();
+
expect(Object.keys(store.state).length).toBe(2);
expect(Array.isArray(store.state.groups)).toBeTruthy();
expect(Object.keys(store.state.pageInfo).length).toBe(0);
expect(store.hideProjects).not.toBeDefined();
store = new GroupsStore(true);
+
expect(store.hideProjects).toBeTruthy();
});
});
@@ -27,9 +31,10 @@ describe('ProjectsStore', () => {
spyOn(store, 'formatGroupItem').and.callThrough();
store.setGroups(mockGroups);
+
expect(store.state.groups.length).toBe(mockGroups.length);
expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object));
- expect(Object.keys(store.state.groups[0]).indexOf('fullName') > -1).toBeTruthy();
+ expect(Object.keys(store.state.groups[0]).indexOf('fullName')).toBeGreaterThan(-1);
});
});
@@ -39,10 +44,13 @@ describe('ProjectsStore', () => {
spyOn(store, 'formatGroupItem').and.callThrough();
store.setSearchedGroups(mockSearchedGroups);
+
expect(store.state.groups.length).toBe(mockSearchedGroups.length);
expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object));
- expect(Object.keys(store.state.groups[0]).indexOf('fullName') > -1).toBeTruthy();
- expect(Object.keys(store.state.groups[0].children[0]).indexOf('fullName') > -1).toBeTruthy();
+ expect(Object.keys(store.state.groups[0]).indexOf('fullName')).toBeGreaterThan(-1);
+ expect(Object.keys(store.state.groups[0].children[0]).indexOf('fullName')).toBeGreaterThan(
+ -1,
+ );
});
});
@@ -52,9 +60,10 @@ describe('ProjectsStore', () => {
spyOn(store, 'formatGroupItem').and.callThrough();
store.setGroupChildren(mockParentGroupItem, mockRawChildren);
+
expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object));
expect(mockParentGroupItem.children.length).toBe(1);
- expect(Object.keys(mockParentGroupItem.children[0]).indexOf('fullName') > -1).toBeTruthy();
+ expect(Object.keys(mockParentGroupItem.children[0]).indexOf('fullName')).toBeGreaterThan(-1);
expect(mockParentGroupItem.isOpen).toBeTruthy();
expect(mockParentGroupItem.isChildrenLoading).toBeFalsy();
});
@@ -65,6 +74,7 @@ describe('ProjectsStore', () => {
const store = new GroupsStore();
store.setPaginationInfo(mockRawPageInfo);
+
expect(store.state.pageInfo.perPage).toBe(10);
expect(store.state.pageInfo.page).toBe(10);
expect(store.state.pageInfo.total).toBe(10);
@@ -81,14 +91,16 @@ describe('ProjectsStore', () => {
store = new GroupsStore();
updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
- expect(Object.keys(updatedGroupItem).indexOf('fullName') > -1).toBeTruthy();
+
+ expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1);
expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].children_count);
expect(updatedGroupItem.isChildrenLoading).toBe(false);
expect(updatedGroupItem.isBeingRemoved).toBe(false);
store = new GroupsStore(true);
updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
- expect(Object.keys(updatedGroupItem).indexOf('fullName') > -1).toBeTruthy();
+
+ expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1);
expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].subgroup_count);
});
});
@@ -104,6 +116,7 @@ describe('ProjectsStore', () => {
const childItem = store.state.groups[0].children[0];
store.removeGroup(childItem, store.state.groups[0]);
+
expect(store.state.groups[0].children.length).toBe(0);
});
});
diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js
index 16ac438f7ac..2fe34e5a76f 100644
--- a/spec/javascripts/header_spec.js
+++ b/spec/javascripts/header_spec.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import initTodoToggle from '~/header';
-describe('Header', function () {
+describe('Header', function() {
const todosPendingCount = '.todos-count';
const fixtureTemplate = 'issues/open-issue.html.raw';
@@ -21,16 +21,19 @@ describe('Header', function () {
it('should update todos-count after receiving the todo:toggle event', () => {
triggerToggle('5');
+
expect($(todosPendingCount).text()).toEqual('5');
});
it('should hide todos-count when it is 0', () => {
triggerToggle('0');
+
expect(isTodosCountHidden()).toEqual(true);
});
it('should show todos-count when it is more than 0', () => {
triggerToggle('10');
+
expect(isTodosCountHidden()).toEqual(false);
});
diff --git a/spec/javascripts/helpers/class_spec_helper_spec.js b/spec/javascripts/helpers/class_spec_helper_spec.js
index fa104ae5bcd..f6268b0fb6d 100644
--- a/spec/javascripts/helpers/class_spec_helper_spec.js
+++ b/spec/javascripts/helpers/class_spec_helper_spec.js
@@ -2,11 +2,13 @@
import './class_spec_helper';
-describe('ClassSpecHelper', function () {
+describe('ClassSpecHelper', function() {
describe('itShouldBeAStaticMethod', () => {
beforeEach(() => {
class TestClass {
- instanceMethod() { this.prop = 'val'; }
+ instanceMethod() {
+ this.prop = 'val';
+ }
static staticMethod() {}
}
diff --git a/spec/javascripts/helpers/locale_helper.js b/spec/javascripts/helpers/locale_helper.js
index 99e6ce61234..80047b06003 100644
--- a/spec/javascripts/helpers/locale_helper.js
+++ b/spec/javascripts/helpers/locale_helper.js
@@ -1,6 +1,6 @@
/* eslint-disable import/prefer-default-export */
-export const setLanguage = (languageCode) => {
+export const setLanguage = languageCode => {
const htmlElement = document.querySelector('html');
if (languageCode) {
diff --git a/spec/javascripts/helpers/set_timeout_promise_helper.js b/spec/javascripts/helpers/set_timeout_promise_helper.js
index 1478073413c..47087619187 100644
--- a/spec/javascripts/helpers/set_timeout_promise_helper.js
+++ b/spec/javascripts/helpers/set_timeout_promise_helper.js
@@ -1,3 +1,4 @@
-export default (time = 0) => new Promise((resolve) => {
- setTimeout(resolve, time);
-});
+export default (time = 0) =>
+ new Promise(resolve => {
+ setTimeout(resolve, time);
+ });
diff --git a/spec/javascripts/helpers/user_mock_data_helper.js b/spec/javascripts/helpers/user_mock_data_helper.js
index f6c3ce5aecc..6999fa1f8a1 100644
--- a/spec/javascripts/helpers/user_mock_data_helper.js
+++ b/spec/javascripts/helpers/user_mock_data_helper.js
@@ -2,14 +2,12 @@ export default {
createNumberRandomUsers(numberUsers) {
const users = [];
for (let i = 0; i < numberUsers; i += 1) {
- users.push(
- {
- avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- id: (i + 1),
- name: `GitLab User ${i}`,
- username: `gitlab${i}`,
- },
- );
+ users.push({
+ avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ id: i + 1,
+ name: `GitLab User ${i}`,
+ username: `gitlab${i}`,
+ });
}
return users;
},
diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js
index 1057f0aca3e..6848c95d95d 100644
--- a/spec/javascripts/helpers/vue_mount_component_helper.js
+++ b/spec/javascripts/helpers/vue_mount_component_helper.js
@@ -1,3 +1,5 @@
+import Vue from 'vue';
+
const mountComponent = (Component, props = {}, el = null) =>
new Component({
propsData: props,
@@ -25,4 +27,12 @@ export const mountComponentWithSlots = (Component, { props, slots }) => {
return component.$mount();
};
+/**
+ * Mount a component with the given render method.
+ *
+ * This helps with inserting slots that need to be compiled.
+ */
+export const mountComponentWithRender = (render, el = null) =>
+ mountComponent(Vue.extend({ render }), {}, el);
+
export default mountComponent;
diff --git a/spec/javascripts/helpers/vue_resource_helper.js b/spec/javascripts/helpers/vue_resource_helper.js
index 70b7ec4e574..0f58af09933 100644
--- a/spec/javascripts/helpers/vue_resource_helper.js
+++ b/spec/javascripts/helpers/vue_resource_helper.js
@@ -1,10 +1,11 @@
// eslint-disable-next-line import/prefer-default-export
export const headersInterceptor = (request, next) => {
- next((response) => {
+ next(response => {
const headers = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
+ // eslint-disable-next-line no-param-reassign
response.headers = headers;
});
};
diff --git a/spec/javascripts/ide/components/branches/item_spec.js b/spec/javascripts/ide/components/branches/item_spec.js
index 8b756c8f168..36b6736bfd4 100644
--- a/spec/javascripts/ide/components/branches/item_spec.js
+++ b/spec/javascripts/ide/components/branches/item_spec.js
@@ -29,13 +29,16 @@ describe('IDE branch item', () => {
it('renders branch name and timeago', () => {
const timeText = getTimeago().format(TEST_BRANCH.committedDate);
+
expect(vm.$el).toContainText(TEST_BRANCH.name);
expect(vm.$el.querySelector('time')).toHaveText(timeText);
expect(vm.$el.querySelector('.ic-mobile-issue-close')).toBe(null);
});
it('renders link to branch', () => {
- const expectedHref = router.resolve(`/project/${TEST_PROJECT_ID}/edit/${TEST_BRANCH.name}`).href;
+ const expectedHref = router.resolve(`/project/${TEST_PROJECT_ID}/edit/${TEST_BRANCH.name}`)
+ .href;
+
expect(vm.$el).toMatch('a');
expect(vm.$el).toHaveAttr('href', expectedHref);
});
diff --git a/spec/javascripts/ide/components/branches/search_list_spec.js b/spec/javascripts/ide/components/branches/search_list_spec.js
index c3f84ba1c24..72a3c2d5dcd 100644
--- a/spec/javascripts/ide/components/branches/search_list_spec.js
+++ b/spec/javascripts/ide/components/branches/search_list_spec.js
@@ -62,8 +62,9 @@ describe('IDE branches search list', () => {
});
it('renders list', () => {
- const elementText = Array.from(vm.$el.querySelectorAll('li strong'))
- .map(x => x.textContent.trim());
+ const elementText = Array.from(vm.$el.querySelectorAll('li strong')).map(x =>
+ x.textContent.trim(),
+ );
expect(elementText).toEqual(testBranches.map(x => x.name));
});
diff --git a/spec/javascripts/ide/components/changed_file_icon_spec.js b/spec/javascripts/ide/components/changed_file_icon_spec.js
deleted file mode 100644
index 7308219f705..00000000000
--- a/spec/javascripts/ide/components/changed_file_icon_spec.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import Vue from 'vue';
-import changedFileIcon from '~/ide/components/changed_file_icon.vue';
-import createComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('IDE changed file icon', () => {
- let vm;
-
- beforeEach(() => {
- const component = Vue.extend(changedFileIcon);
-
- vm = createComponent(component, {
- file: {
- tempFile: false,
- changed: true,
- },
- });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('changedIcon', () => {
- it('equals file-modified when not a temp file and has changes', () => {
- expect(vm.changedIcon).toBe('file-modified');
- });
-
- it('equals file-addition when a temp file', () => {
- vm.file.tempFile = true;
-
- expect(vm.changedIcon).toBe('file-addition');
- });
- });
-
- describe('changedIconClass', () => {
- it('includes ide-file-modified when not a temp file', () => {
- expect(vm.changedIconClass).toContain('ide-file-modified');
- });
-
- it('includes ide-file-addition when a temp file', () => {
- vm.file.tempFile = true;
-
- expect(vm.changedIconClass).toContain('ide-file-addition');
- });
- });
-});
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
index 41d8bfff7e7..bf48d7bfdad 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
@@ -30,11 +30,7 @@ describe('Multi-file editor commit sidebar list item', () => {
});
it('renders file path', () => {
- expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path);
- });
-
- it('renders actionn button', () => {
- expect(vm.$el.querySelector('.multi-file-discard-btn')).not.toBeNull();
+ expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent).toContain(f.path);
});
it('opens a closed file in the editor when clicking the file path', done => {
diff --git a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js b/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js
index 942cc19f46d..af67991eadd 100644
--- a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js
@@ -36,8 +36,7 @@ describe('IDE commit message field', () => {
it('removed is-focused class on blur', done => {
vm.$el.querySelector('textarea').focus();
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.$el.querySelector('.is-focused')).not.toBeNull();
@@ -70,12 +69,12 @@ describe('IDE commit message field', () => {
it('does not highlight less than 50 characters', done => {
vm.text = 'text less than 50 chars';
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.$el.querySelector('.highlights span').textContent).toContain(
'text less than 50 chars',
);
+
expect(vm.$el.querySelector('mark').style.display).toBe('none');
})
.then(done)
@@ -86,12 +85,12 @@ describe('IDE commit message field', () => {
vm.text =
'text less than 50 chars that should not highlighted. text more than 50 should be highlighted';
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.$el.querySelector('.highlights span').textContent).toContain(
'text less than 50 chars that should not highlighte',
);
+
expect(vm.$el.querySelector('mark').style.display).not.toBe('none');
expect(vm.$el.querySelector('mark').textContent).toBe(
'd. text more than 50 should be highlighted',
@@ -106,8 +105,7 @@ describe('IDE commit message field', () => {
it('does not highlight body text less tan 72 characters', done => {
vm.text = 'subject line\nbody content';
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2);
expect(vm.$el.querySelectorAll('mark')[1].style.display).toBe('none');
@@ -120,8 +118,7 @@ describe('IDE commit message field', () => {
vm.text =
'subject line\nbody content that will be highlighted when it is more than 72 characters in length';
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2);
expect(vm.$el.querySelectorAll('mark')[1].style.display).not.toBe('none');
@@ -135,8 +132,7 @@ describe('IDE commit message field', () => {
vm.text =
'text less than 50 chars that should not highlighted\nbody content that will be highlighted when it is more than 72 characters in length';
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2);
expect(vm.$el.querySelectorAll('mark').length).toBe(2);
@@ -154,8 +150,7 @@ describe('IDE commit message field', () => {
it('updates transform of highlights', done => {
vm.text = 'subject line\n\n\n\n\n\n\n\n\n\n\nbody content';
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
vm.$el.querySelector('textarea').scrollTo(0, 50);
diff --git a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
index a5b906da8a1..e09ccbe2a63 100644
--- a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
@@ -29,7 +29,7 @@ describe('IDE stage file button', () => {
});
it('renders button to discard & stage', () => {
- expect(vm.$el.querySelectorAll('.btn').length).toBe(2);
+ expect(vm.$el.querySelectorAll('.btn-blank').length).toBe(2);
});
it('calls store with stage button', () => {
@@ -39,7 +39,7 @@ describe('IDE stage file button', () => {
});
it('calls store with discard button', () => {
- vm.$el.querySelector('.dropdown-menu button').click();
+ vm.$el.querySelector('.btn-danger').click();
expect(vm.discardFileChanges).toHaveBeenCalledWith(f.path);
});
diff --git a/spec/javascripts/ide/components/file_finder/index_spec.js b/spec/javascripts/ide/components/file_finder/index_spec.js
index 4f208e946d2..4d934f92f72 100644
--- a/spec/javascripts/ide/components/file_finder/index_spec.js
+++ b/spec/javascripts/ide/components/file_finder/index_spec.js
@@ -85,8 +85,7 @@ describe('IDE File finder item spec', () => {
it('clear button resets searchText', done => {
vm.searchText = 'index';
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
vm.$el.querySelector('.dropdown-input-clear').click();
})
@@ -102,8 +101,7 @@ describe('IDE File finder item spec', () => {
spyOn(vm.$refs.searchInput, 'focus');
vm.searchText = 'index';
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
vm.$el.querySelector('.dropdown-input-clear').click();
})
@@ -178,8 +176,7 @@ describe('IDE File finder item spec', () => {
vm.searchText = 'test';
vm.$store.state.fileFindVisible = true;
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
vm.$store.state.fileFindVisible = false;
})
diff --git a/spec/javascripts/ide/components/file_row_extra_spec.js b/spec/javascripts/ide/components/file_row_extra_spec.js
new file mode 100644
index 00000000000..c93a939ad71
--- /dev/null
+++ b/spec/javascripts/ide/components/file_row_extra_spec.js
@@ -0,0 +1,159 @@
+import Vue from 'vue';
+import { createStore } from '~/ide/stores';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import FileRowExtra from '~/ide/components/file_row_extra.vue';
+import { file, resetStore } from '../helpers';
+
+describe('IDE extra file row component', () => {
+ let Component;
+ let vm;
+ let unstagedFilesCount = 0;
+ let stagedFilesCount = 0;
+ let changesCount = 0;
+
+ beforeAll(() => {
+ Component = Vue.extend(FileRowExtra);
+ });
+
+ beforeEach(() => {
+ vm = createComponentWithStore(Component, createStore(), {
+ file: {
+ ...file('test'),
+ },
+ mouseOver: false,
+ });
+
+ spyOnProperty(vm, 'getUnstagedFilesCountForPath').and.returnValue(() => unstagedFilesCount);
+ spyOnProperty(vm, 'getStagedFilesCountForPath').and.returnValue(() => stagedFilesCount);
+ spyOnProperty(vm, 'getChangesInFolder').and.returnValue(() => changesCount);
+
+ vm.$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ resetStore(vm.$store);
+
+ stagedFilesCount = 0;
+ unstagedFilesCount = 0;
+ changesCount = 0;
+ });
+
+ describe('folderChangesTooltip', () => {
+ it('returns undefined when changes count is 0', () => {
+ expect(vm.folderChangesTooltip).toBe(undefined);
+ });
+
+ it('returns unstaged changes text', () => {
+ changesCount = 1;
+ unstagedFilesCount = 1;
+
+ expect(vm.folderChangesTooltip).toBe('1 unstaged change');
+ });
+
+ it('returns staged changes text', () => {
+ changesCount = 1;
+ stagedFilesCount = 1;
+
+ expect(vm.folderChangesTooltip).toBe('1 staged change');
+ });
+
+ it('returns staged and unstaged changes text', () => {
+ changesCount = 1;
+ stagedFilesCount = 1;
+ unstagedFilesCount = 1;
+
+ expect(vm.folderChangesTooltip).toBe('1 unstaged and 1 staged changes');
+ });
+ });
+
+ describe('show tree changes count', () => {
+ it('does not show for blobs', () => {
+ vm.file.type = 'blob';
+
+ expect(vm.$el.querySelector('.ide-tree-changes')).toBe(null);
+ });
+
+ it('does not show when changes count is 0', () => {
+ vm.file.type = 'tree';
+
+ expect(vm.$el.querySelector('.ide-tree-changes')).toBe(null);
+ });
+
+ it('does not show when tree is open', done => {
+ vm.file.type = 'tree';
+ vm.file.opened = true;
+ changesCount = 1;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.ide-tree-changes')).toBe(null);
+
+ done();
+ });
+ });
+
+ it('shows for trees with changes', done => {
+ vm.file.type = 'tree';
+ vm.file.opened = false;
+ changesCount = 1;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.ide-tree-changes')).not.toBe(null);
+
+ done();
+ });
+ });
+ });
+
+ describe('changes file icon', () => {
+ it('hides when file is not changed', () => {
+ expect(vm.$el.querySelector('.file-changed-icon')).toBe(null);
+ });
+
+ it('shows when file is changed', done => {
+ vm.file.changed = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null);
+
+ done();
+ });
+ });
+
+ it('shows when file is staged', done => {
+ vm.file.staged = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null);
+
+ done();
+ });
+ });
+
+ it('shows when file is a tempFile', done => {
+ vm.file.tempFile = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null);
+
+ done();
+ });
+ });
+ });
+
+ describe('merge request icon', () => {
+ it('hides when not a merge request change', () => {
+ expect(vm.$el.querySelector('.ic-git-merge')).toBe(null);
+ });
+
+ it('shows when a merge request change', done => {
+ vm.file.mrChange = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.ic-git-merge')).not.toBe(null);
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/file_templates/bar_spec.js b/spec/javascripts/ide/components/file_templates/bar_spec.js
new file mode 100644
index 00000000000..a688f7f69a6
--- /dev/null
+++ b/spec/javascripts/ide/components/file_templates/bar_spec.js
@@ -0,0 +1,117 @@
+import Vue from 'vue';
+import { createStore } from '~/ide/stores';
+import Bar from '~/ide/components/file_templates/bar.vue';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { resetStore, file } from '../../helpers';
+
+describe('IDE file templates bar component', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(Bar);
+ });
+
+ beforeEach(() => {
+ const store = createStore();
+
+ store.state.openFiles.push({
+ ...file('file'),
+ opened: true,
+ active: true,
+ });
+
+ vm = mountComponentWithStore(Component, { store });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ resetStore(vm.$store);
+ });
+
+ describe('template type dropdown', () => {
+ it('renders dropdown component', () => {
+ expect(vm.$el.querySelector('.dropdown').textContent).toContain('Choose a type');
+ });
+
+ it('calls setSelectedTemplateType when clicking item', () => {
+ spyOn(vm, 'setSelectedTemplateType').and.stub();
+
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ expect(vm.setSelectedTemplateType).toHaveBeenCalledWith({
+ name: '.gitlab-ci.yml',
+ key: 'gitlab_ci_ymls',
+ });
+ });
+ });
+
+ describe('template dropdown', () => {
+ beforeEach(done => {
+ vm.$store.state.fileTemplates.templates = [
+ {
+ name: 'test',
+ },
+ ];
+ vm.$store.state.fileTemplates.selectedTemplateType = {
+ name: '.gitlab-ci.yml',
+ key: 'gitlab_ci_ymls',
+ };
+
+ vm.$nextTick(done);
+ });
+
+ it('renders dropdown component', () => {
+ expect(vm.$el.querySelectorAll('.dropdown')[1].textContent).toContain('Choose a template');
+ });
+
+ it('calls fetchTemplate on click', () => {
+ spyOn(vm, 'fetchTemplate').and.stub();
+
+ vm.$el
+ .querySelectorAll('.dropdown-content')[1]
+ .querySelector('button')
+ .click();
+
+ expect(vm.fetchTemplate).toHaveBeenCalledWith({
+ name: 'test',
+ });
+ });
+ });
+
+ it('shows undo button if updateSuccess is true', done => {
+ vm.$store.state.fileTemplates.updateSuccess = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.btn-default').style.display).not.toBe('none');
+
+ done();
+ });
+ });
+
+ it('calls undoFileTemplate when clicking undo button', () => {
+ spyOn(vm, 'undoFileTemplate').and.stub();
+
+ vm.$el.querySelector('.btn-default').click();
+
+ expect(vm.undoFileTemplate).toHaveBeenCalled();
+ });
+
+ it('calls setSelectedTemplateType if activeFile name matches a template', done => {
+ const fileName = '.gitlab-ci.yml';
+
+ spyOn(vm, 'setSelectedTemplateType');
+ vm.$store.state.openFiles[0].name = fileName;
+
+ vm.setInitialType();
+
+ vm.$nextTick(() => {
+ expect(vm.setSelectedTemplateType).toHaveBeenCalledWith({
+ name: fileName,
+ key: 'gitlab_ci_ymls',
+ });
+
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/file_templates/dropdown_spec.js b/spec/javascripts/ide/components/file_templates/dropdown_spec.js
new file mode 100644
index 00000000000..898796f4fa0
--- /dev/null
+++ b/spec/javascripts/ide/components/file_templates/dropdown_spec.js
@@ -0,0 +1,201 @@
+import $ from 'jquery';
+import Vue from 'vue';
+import { createStore } from '~/ide/stores';
+import Dropdown from '~/ide/components/file_templates/dropdown.vue';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { resetStore } from '../../helpers';
+
+describe('IDE file templates dropdown component', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(Dropdown);
+ });
+
+ beforeEach(() => {
+ const store = createStore();
+
+ vm = createComponentWithStore(Component, store, {
+ label: 'Test',
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ resetStore(vm.$store);
+ });
+
+ describe('async', () => {
+ beforeEach(() => {
+ vm.isAsyncData = true;
+ });
+
+ it('calls async store method on Bootstrap dropdown event', () => {
+ spyOn(vm, 'fetchTemplateTypes').and.stub();
+
+ $(vm.$el).trigger('show.bs.dropdown');
+
+ expect(vm.fetchTemplateTypes).toHaveBeenCalled();
+ });
+
+ it('renders templates when async', done => {
+ vm.$store.state.fileTemplates.templates = [
+ {
+ name: 'test',
+ },
+ ];
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.dropdown-content').textContent).toContain('test');
+
+ done();
+ });
+ });
+
+ it('renders loading icon when isLoading is true', done => {
+ vm.$store.state.fileTemplates.isLoading = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
+
+ done();
+ });
+ });
+
+ it('searches template data', () => {
+ vm.$store.state.fileTemplates.templates = [
+ {
+ name: 'test',
+ },
+ ];
+ vm.searchable = true;
+ vm.search = 'hello';
+
+ expect(vm.outputData).toEqual([]);
+ });
+
+ it('does not filter data is searchable is false', () => {
+ vm.$store.state.fileTemplates.templates = [
+ {
+ name: 'test',
+ },
+ ];
+ vm.search = 'hello';
+
+ expect(vm.outputData).toEqual([
+ {
+ name: 'test',
+ },
+ ]);
+ });
+
+ it('calls clickItem on click', done => {
+ spyOn(vm, 'clickItem').and.stub();
+
+ vm.$store.state.fileTemplates.templates = [
+ {
+ name: 'test',
+ },
+ ];
+
+ vm.$nextTick(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ expect(vm.clickItem).toHaveBeenCalledWith({
+ name: 'test',
+ });
+
+ done();
+ });
+ });
+
+ it('renders input when searchable is true', done => {
+ vm.searchable = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.dropdown-input')).not.toBe(null);
+
+ done();
+ });
+ });
+
+ it('does not render input when searchable is true & showLoading is true', done => {
+ vm.searchable = true;
+ vm.$store.state.fileTemplates.isLoading = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.dropdown-input')).toBe(null);
+
+ done();
+ });
+ });
+ });
+
+ describe('sync', () => {
+ beforeEach(done => {
+ vm.data = [
+ {
+ name: 'test sync',
+ },
+ ];
+
+ vm.$nextTick(done);
+ });
+
+ it('renders props data', () => {
+ expect(vm.$el.querySelector('.dropdown-content').textContent).toContain('test sync');
+ });
+
+ it('renders input when searchable is true', done => {
+ vm.searchable = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.dropdown-input')).not.toBe(null);
+
+ done();
+ });
+ });
+
+ it('calls clickItem on click', done => {
+ spyOn(vm, 'clickItem').and.stub();
+
+ vm.$nextTick(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ expect(vm.clickItem).toHaveBeenCalledWith({
+ name: 'test sync',
+ });
+
+ done();
+ });
+ });
+
+ it('searches template data', () => {
+ vm.searchable = true;
+ vm.search = 'hello';
+
+ expect(vm.outputData).toEqual([]);
+ });
+
+ it('does not filter data is searchable is false', () => {
+ vm.search = 'hello';
+
+ expect(vm.outputData).toEqual([
+ {
+ name: 'test sync',
+ },
+ ]);
+ });
+
+ it('renders dropdown title', done => {
+ vm.title = 'Test title';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.dropdown-title').textContent).toContain('Test title');
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/ide_spec.js b/spec/javascripts/ide/components/ide_spec.js
index 49b8e934cdd..c02a1ad246c 100644
--- a/spec/javascripts/ide/components/ide_spec.js
+++ b/spec/javascripts/ide/components/ide_spec.js
@@ -84,8 +84,7 @@ describe('ide component', () => {
it('calls toggleFileFinder on `t` key press', done => {
Mousetrap.trigger('t');
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.toggleFileFinder).toHaveBeenCalled();
})
@@ -96,8 +95,7 @@ describe('ide component', () => {
it('calls toggleFileFinder on `command+p` key press', done => {
Mousetrap.trigger('command+p');
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.toggleFileFinder).toHaveBeenCalled();
})
@@ -108,8 +106,7 @@ describe('ide component', () => {
it('calls toggleFileFinder on `ctrl+p` key press', done => {
Mousetrap.trigger('ctrl+p');
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
expect(vm.toggleFileFinder).toHaveBeenCalled();
})
diff --git a/spec/javascripts/ide/components/ide_status_bar_spec.js b/spec/javascripts/ide/components/ide_status_bar_spec.js
index 0e93c5193a1..ab032b4cb98 100644
--- a/spec/javascripts/ide/components/ide_status_bar_spec.js
+++ b/spec/javascripts/ide/components/ide_status_bar_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import store from '~/ide/stores';
import ideStatusBar from '~/ide/components/ide_status_bar.vue';
+import { rightSidebarViews } from '~/ide/constants';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../helpers';
import { projectData } from '../mock_data';
@@ -49,9 +50,11 @@ describe('ideStatusBar', () => {
expect(vm.commitAgeUpdate).not.toHaveBeenCalled();
jasmine.clock().tick(1100);
+
expect(vm.commitAgeUpdate.calls.count()).toEqual(1);
jasmine.clock().tick(1000);
+
expect(vm.commitAgeUpdate.calls.count()).toEqual(2);
});
});
@@ -64,7 +67,7 @@ describe('ideStatusBar', () => {
describe('pipeline status', () => {
it('opens right sidebar on clicking icon', done => {
- spyOn(vm, 'setRightPane');
+ spyOn(vm, 'openRightPane');
Vue.set(vm.$store.state.pipelines, 'latestPipeline', {
details: {
status: {
@@ -75,12 +78,11 @@ describe('ideStatusBar', () => {
},
});
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
vm.$el.querySelector('.ide-status-pipeline button').click();
- expect(vm.setRightPane).toHaveBeenCalledWith('pipelines-list');
+ expect(vm.openRightPane).toHaveBeenCalledWith(rightSidebarViews.pipelines);
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/ide/components/jobs/detail_spec.js b/spec/javascripts/ide/components/jobs/detail_spec.js
index 8f8d4b9709e..a4e6b81acba 100644
--- a/spec/javascripts/ide/components/jobs/detail_spec.js
+++ b/spec/javascripts/ide/components/jobs/detail_spec.js
@@ -109,8 +109,7 @@ describe('IDE jobs detail view', () => {
vm.scrollPos = 1;
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => vm.$el.querySelector('.btn-scroll').click())
.then(() => vm.$nextTick())
.then(() => {
diff --git a/spec/javascripts/ide/components/merge_requests/item_spec.js b/spec/javascripts/ide/components/merge_requests/item_spec.js
index 750948cae3c..155a247defb 100644
--- a/spec/javascripts/ide/components/merge_requests/item_spec.js
+++ b/spec/javascripts/ide/components/merge_requests/item_spec.js
@@ -29,7 +29,10 @@ describe('IDE merge request item', () => {
});
it('renders link with href', () => {
- const expectedHref = router.resolve(`/project/${vm.item.projectPathWithNamespace}/merge_requests/${vm.item.iid}`).href;
+ const expectedHref = router.resolve(
+ `/project/${vm.item.projectPathWithNamespace}/merge_requests/${vm.item.iid}`,
+ ).href;
+
expect(vm.$el).toMatch('a');
expect(vm.$el).toHaveAttr('href', expectedHref);
});
diff --git a/spec/javascripts/ide/components/merge_requests/list_spec.js b/spec/javascripts/ide/components/merge_requests/list_spec.js
index c761315444c..55e4f46d9ca 100644
--- a/spec/javascripts/ide/components/merge_requests/list_spec.js
+++ b/spec/javascripts/ide/components/merge_requests/list_spec.js
@@ -118,8 +118,9 @@ describe('IDE merge requests list', () => {
vm.$nextTick()
.then(() => {
const expectedSearchTypes = vm.$options.searchTypes.map(x => x.label);
- const renderedSearchTypes = Array.from(vm.$el.querySelectorAll('li'))
- .map(x => x.textContent.trim());
+ const renderedSearchTypes = Array.from(vm.$el.querySelectorAll('li')).map(x =>
+ x.textContent.trim(),
+ );
expect(renderedSearchTypes).toEqual(expectedSearchTypes);
})
diff --git a/spec/javascripts/ide/components/new_dropdown/index_spec.js b/spec/javascripts/ide/components/new_dropdown/index_spec.js
index 8a8cbd2cee4..83e530f0a6a 100644
--- a/spec/javascripts/ide/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/index_spec.js
@@ -36,6 +36,7 @@ describe('new dropdown component', () => {
it('renders new file, upload and new directory links', () => {
const buttons = vm.$el.querySelectorAll('.dropdown-menu button');
+
expect(buttons[0].textContent.trim()).toBe('New file');
expect(buttons[1].textContent.trim()).toBe('Upload file');
expect(buttons[2].textContent.trim()).toBe('New directory');
diff --git a/spec/javascripts/ide/components/new_dropdown/upload_spec.js b/spec/javascripts/ide/components/new_dropdown/upload_spec.js
index 9c76500cfe5..878e17ac805 100644
--- a/spec/javascripts/ide/components/new_dropdown/upload_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/upload_spec.js
@@ -21,23 +21,29 @@ describe('new dropdown upload', () => {
vm.$destroy();
});
- describe('readFile', () => {
- beforeEach(() => {
- spyOn(FileReader.prototype, 'readAsText');
- spyOn(FileReader.prototype, 'readAsDataURL');
- });
+ describe('openFile', () => {
+ it('calls for each file', () => {
+ const files = ['test', 'test2', 'test3'];
- it('calls readAsText for text files', () => {
- const file = {
- type: 'text/html',
- };
+ spyOn(vm, 'readFile');
+ spyOnProperty(vm.$refs.fileUpload, 'files').and.returnValue(files);
- vm.readFile(file);
+ vm.openFile();
+
+ expect(vm.readFile.calls.count()).toBe(3);
- expect(FileReader.prototype.readAsText).toHaveBeenCalledWith(file);
+ files.forEach((file, i) => {
+ expect(vm.readFile.calls.argsFor(i)).toEqual([file]);
+ });
+ });
+ });
+
+ describe('readFile', () => {
+ beforeEach(() => {
+ spyOn(FileReader.prototype, 'readAsDataURL');
});
- it('calls readAsDataURL for non-text files', () => {
+ it('calls readAsDataURL for all files', () => {
const file = {
type: 'images/png',
};
@@ -49,32 +55,37 @@ describe('new dropdown upload', () => {
});
describe('createFile', () => {
- const target = {
- result: 'content',
+ const textTarget = {
+ result: 'base64,cGxhaW4gdGV4dA==',
};
const binaryTarget = {
- result: 'base64,base64content',
+ result: 'base64,w4I=',
+ };
+ const textFile = {
+ name: 'textFile',
+ type: 'text/plain',
};
- const file = {
- name: 'file',
+ const binaryFile = {
+ name: 'binaryFile',
+ type: 'image/png',
};
- it('creates new file', () => {
- vm.createFile(target, file, true);
+ it('creates file in plain text (without encoding) if the file content is plain text', () => {
+ vm.createFile(textTarget, textFile);
expect(vm.$emit).toHaveBeenCalledWith('create', {
- name: file.name,
+ name: textFile.name,
type: 'blob',
- content: target.result,
+ content: 'plain text',
base64: false,
});
});
it('splits content on base64 if binary', () => {
- vm.createFile(binaryTarget, file, false);
+ vm.createFile(binaryTarget, binaryFile);
expect(vm.$emit).toHaveBeenCalledWith('create', {
- name: file.name,
+ name: binaryFile.name,
type: 'blob',
content: binaryTarget.result.split('base64,')[1],
base64: true,
diff --git a/spec/javascripts/ide/components/panes/right_spec.js b/spec/javascripts/ide/components/panes/right_spec.js
index c75975d2af6..4899f850cf4 100644
--- a/spec/javascripts/ide/components/panes/right_spec.js
+++ b/spec/javascripts/ide/components/panes/right_spec.js
@@ -25,7 +25,8 @@ describe('IDE right pane', () => {
describe('active', () => {
it('renders merge request button as active', done => {
- vm.$store.state.rightPane = rightSidebarViews.mergeRequestInfo;
+ vm.$store.state.rightPane.isOpen = true;
+ vm.$store.state.rightPane.currentView = rightSidebarViews.mergeRequestInfo.name;
vm.$store.state.currentMergeRequestId = '123';
vm.$store.state.currentProjectId = 'gitlab-ce';
vm.$store.state.currentMergeRequestId = 1;
@@ -41,20 +42,21 @@ describe('IDE right pane', () => {
},
};
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.ide-sidebar-link.active')).not.toBe(null);
- expect(
- vm.$el.querySelector('.ide-sidebar-link.active').getAttribute('data-original-title'),
- ).toBe('Merge Request');
-
- done();
- });
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.ide-sidebar-link.active')).not.toBe(null);
+ expect(
+ vm.$el.querySelector('.ide-sidebar-link.active').getAttribute('data-original-title'),
+ ).toBe('Merge Request');
+ })
+ .then(done)
+ .catch(done.fail);
});
});
describe('click', () => {
beforeEach(() => {
- spyOn(vm, 'setRightPane');
+ spyOn(vm, 'open');
});
it('sets view to merge request', done => {
@@ -63,7 +65,7 @@ describe('IDE right pane', () => {
vm.$nextTick(() => {
vm.$el.querySelector('.ide-sidebar-link').click();
- expect(vm.setRightPane).toHaveBeenCalledWith(rightSidebarViews.mergeRequestInfo);
+ expect(vm.open).toHaveBeenCalledWith(rightSidebarViews.mergeRequestInfo);
done();
});
diff --git a/spec/javascripts/ide/components/preview/clientside_spec.js b/spec/javascripts/ide/components/preview/clientside_spec.js
index d6983f5a3b8..b9bf5c51ffe 100644
--- a/spec/javascripts/ide/components/preview/clientside_spec.js
+++ b/spec/javascripts/ide/components/preview/clientside_spec.js
@@ -59,8 +59,7 @@ describe('IDE clientside preview', () => {
}),
);
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => vm.initPreview())
.then(vm.$nextTick)
.then(done)
diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js
index 30cd92b2ca4..6c726c1e154 100644
--- a/spec/javascripts/ide/components/repo_commit_section_spec.js
+++ b/spec/javascripts/ide/components/repo_commit_section_spec.js
@@ -103,65 +103,6 @@ describe('RepoCommitSection', () => {
});
});
- it('adds changed files into staged files', done => {
- vm.$el.querySelector('.multi-file-discard-btn .btn').click();
- vm
- .$nextTick()
- .then(() => vm.$el.querySelector('.multi-file-discard-btn .btn').click())
- .then(vm.$nextTick)
- .then(() => {
- expect(vm.$el.querySelector('.ide-commit-list-container').textContent).toContain(
- 'No changes',
- );
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('stages a single file', done => {
- vm.$el.querySelector('.multi-file-discard-btn .btn').click();
-
- Vue.nextTick(() => {
- expect(
- vm.$el
- .querySelector('.ide-commit-list-container')
- .querySelectorAll('.multi-file-commit-list > li').length,
- ).toBe(1);
-
- done();
- });
- });
-
- it('discards a single file', done => {
- vm.$el.querySelector('.multi-file-discard-btn .dropdown-menu button').click();
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.ide-commit-list-container').textContent).not.toContain('file1');
- expect(
- vm.$el
- .querySelector('.ide-commit-list-container')
- .querySelectorAll('.multi-file-commit-list > li').length,
- ).toBe(1);
-
- done();
- });
- });
-
- it('unstages a single file', done => {
- vm.$el
- .querySelectorAll('.multi-file-discard-btn')[2]
- .querySelector('.btn')
- .click();
-
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.ide-commit-list-container')[1].querySelectorAll('li').length,
- ).toBe(1);
-
- done();
- });
- });
-
describe('mounted', () => {
it('opens last opened file', () => {
expect(store.state.openFiles.length).toBe(1);
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index 0e2e246defd..002b5a005b8 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -52,6 +52,7 @@ describe('RepoEditor', () => {
it('renders only an edit tab', done => {
Vue.nextTick(() => {
const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
+
expect(tabs.length).toBe(1);
expect(tabs[0].textContent.trim()).toBe('Edit');
@@ -72,6 +73,7 @@ describe('RepoEditor', () => {
it('renders an Edit and a Preview Tab', done => {
Vue.nextTick(() => {
const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
+
expect(tabs.length).toBe(2);
expect(tabs[0].textContent.trim()).toBe('Edit');
expect(tabs[1].textContent.trim()).toBe('Preview Markdown');
@@ -109,6 +111,7 @@ describe('RepoEditor', () => {
it('renders an Edit and a Preview Tab', done => {
Vue.nextTick(() => {
const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
+
expect(tabs.length).toBe(2);
expect(tabs[0].textContent.trim()).toBe('Review');
expect(tabs[1].textContent.trim()).toBe('Preview Markdown');
@@ -122,8 +125,7 @@ describe('RepoEditor', () => {
vm.file.path = `${vm.file.path}.md`;
vm.$store.state.entries[vm.file.path] = vm.file;
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
vm.$el.querySelectorAll('.ide-mode-tabs .nav-links a')[1].click();
})
@@ -294,8 +296,7 @@ describe('RepoEditor', () => {
it('calls updateDimensions when panelResizing is false', done => {
vm.$store.state.panelResizing = true;
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
vm.$store.state.panelResizing = false;
})
@@ -319,8 +320,8 @@ describe('RepoEditor', () => {
});
});
- it('calls updateDimensions when rightPane is updated', done => {
- vm.$store.state.rightPane = 'testing';
+ it('calls updateDimensions when rightPane is opened', done => {
+ vm.$store.state.rightPane.isOpen = true;
vm.$nextTick(() => {
expect(vm.editor.updateDimensions).toHaveBeenCalled();
@@ -363,8 +364,7 @@ describe('RepoEditor', () => {
vm.file.pending = true;
- vm
- .$nextTick()
+ vm.$nextTick()
.then(() => {
vm.file = file('testing');
diff --git a/spec/javascripts/ide/components/repo_file_spec.js b/spec/javascripts/ide/components/repo_file_spec.js
deleted file mode 100644
index f99d1f9890a..00000000000
--- a/spec/javascripts/ide/components/repo_file_spec.js
+++ /dev/null
@@ -1,124 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoFile from '~/ide/components/repo_file.vue';
-import router from '~/ide/ide_router';
-import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
-import { file } from '../helpers';
-
-describe('RepoFile', () => {
- let vm;
-
- function createComponent(propsData) {
- const RepoFile = Vue.extend(repoFile);
-
- vm = createComponentWithStore(RepoFile, store, propsData);
-
- vm.$mount();
- }
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders link, icon and name', () => {
- createComponent({
- file: file('t4'),
- level: 0,
- });
-
- const name = vm.$el.querySelector('.ide-file-name');
-
- expect(name.href).toMatch('');
- expect(name.textContent.trim()).toEqual(vm.file.name);
- });
-
- it('fires clickFile when the link is clicked', done => {
- spyOn(router, 'push');
- createComponent({
- file: file('t3'),
- level: 0,
- });
-
- vm.$el.querySelector('.file-name').click();
-
- setTimeout(() => {
- expect(router.push).toHaveBeenCalledWith(`/project${vm.file.url}`);
-
- done();
- });
- });
-
- describe('folder', () => {
- it('renders changes count inside folder', () => {
- const f = {
- ...file('folder'),
- path: 'testing',
- type: 'tree',
- branchId: 'master',
- projectId: 'project',
- };
-
- store.state.changedFiles.push({
- ...file('fileName'),
- path: 'testing/fileName',
- });
-
- createComponent({
- file: f,
- level: 0,
- });
-
- const treeChangesEl = vm.$el.querySelector('.ide-tree-changes');
-
- expect(treeChangesEl).not.toBeNull();
- expect(treeChangesEl.textContent).toContain('1');
- });
-
- it('renders action dropdown', done => {
- createComponent({
- file: {
- ...file('t4'),
- type: 'tree',
- branchId: 'master',
- projectId: 'project',
- },
- level: 0,
- });
-
- setTimeout(() => {
- expect(vm.$el.querySelector('.ide-new-btn')).not.toBeNull();
-
- done();
- });
- });
- });
-
- describe('locked file', () => {
- let f;
-
- beforeEach(() => {
- f = file('locked file');
- f.file_lock = {
- user: {
- name: 'testuser',
- updated_at: new Date(),
- },
- };
-
- createComponent({
- file: f,
- level: 0,
- });
- });
-
- it('renders lock icon', () => {
- expect(vm.$el.querySelector('.file-status-icon')).not.toBeNull();
- });
-
- it('renders a tooltip', () => {
- expect(
- vm.$el.querySelector('.ide-file-name span:nth-child(2)').dataset.originalTitle,
- ).toContain('Locked by testuser');
- });
- });
-});
diff --git a/spec/javascripts/ide/components/repo_loading_file_spec.js b/spec/javascripts/ide/components/repo_loading_file_spec.js
deleted file mode 100644
index 7c20b8302f9..00000000000
--- a/spec/javascripts/ide/components/repo_loading_file_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoLoadingFile from '~/ide/components/repo_loading_file.vue';
-import { resetStore } from '../helpers';
-
-describe('RepoLoadingFile', () => {
- let vm;
-
- function createComponent() {
- const RepoLoadingFile = Vue.extend(repoLoadingFile);
-
- return new RepoLoadingFile({
- store,
- }).$mount();
- }
-
- function assertLines(lines) {
- lines.forEach((line, n) => {
- const index = n + 1;
- expect(line.classList.contains(`skeleton-line-${index}`)).toBeTruthy();
- });
- }
-
- function assertColumns(columns) {
- columns.forEach(column => {
- const container = column.querySelector('.animation-container');
- const lines = [...container.querySelectorAll(':scope > div')];
-
- expect(container).toBeTruthy();
- expect(lines.length).toEqual(3);
- assertLines(lines);
- });
- }
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders 3 columns of animated LoC', () => {
- vm = createComponent();
- const columns = [...vm.$el.querySelectorAll('td')];
-
- expect(columns.length).toEqual(3);
- assertColumns(columns);
- });
-
- it('renders 1 column of animated LoC if isMini', done => {
- vm = createComponent();
- vm.$store.state.leftPanelCollapsed = true;
- vm.$store.state.openFiles.push('test');
-
- vm.$nextTick(() => {
- const columns = [...vm.$el.querySelectorAll('td')];
-
- expect(columns.length).toEqual(1);
- assertColumns(columns);
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/ide/components/repo_tab_spec.js b/spec/javascripts/ide/components/repo_tab_spec.js
index 278a0753322..3b52f279bf2 100644
--- a/spec/javascripts/ide/components/repo_tab_spec.js
+++ b/spec/javascripts/ide/components/repo_tab_spec.js
@@ -93,13 +93,13 @@ describe('RepoTab', () => {
Vue.nextTick()
.then(() => {
- expect(vm.$el.querySelector('.ide-file-modified')).toBeNull();
+ expect(vm.$el.querySelector('.file-modified')).toBeNull();
vm.$el.dispatchEvent(new Event('mouseout'));
})
.then(Vue.nextTick)
.then(() => {
- expect(vm.$el.querySelector('.ide-file-modified')).not.toBeNull();
+ expect(vm.$el.querySelector('.file-modified')).not.toBeNull();
done();
})
diff --git a/spec/javascripts/ide/components/shared/tokened_input_spec.js b/spec/javascripts/ide/components/shared/tokened_input_spec.js
index 09940fe8c6a..b09bf760543 100644
--- a/spec/javascripts/ide/components/shared/tokened_input_spec.js
+++ b/spec/javascripts/ide/components/shared/tokened_input_spec.js
@@ -44,8 +44,7 @@ describe('IDE shared/TokenedInput', () => {
});
it('renders tokens', () => {
- const renderedTokens = getTokenElements(vm)
- .map(x => x.textContent.trim());
+ const renderedTokens = getTokenElements(vm).map(x => x.textContent.trim());
expect(renderedTokens).toEqual(TEST_TOKENS.map(x => x.label));
});
@@ -85,10 +84,12 @@ describe('IDE shared/TokenedInput', () => {
vm.value = '';
vm.onBackspace();
+
expect(vm.$emit).not.toHaveBeenCalled();
expect(vm.backspaceCount).toEqual(1);
vm.onBackspace();
+
expect(vm.$emit).toHaveBeenCalledWith('removeToken', TEST_TOKENS[TEST_TOKENS.length - 1]);
expect(vm.backspaceCount).toEqual(0);
});
diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js
index c11c482fef8..7e107747346 100644
--- a/spec/javascripts/ide/helpers.js
+++ b/spec/javascripts/ide/helpers.js
@@ -5,6 +5,8 @@ import commitState from '~/ide/stores/modules/commit/state';
import mergeRequestsState from '~/ide/stores/modules/merge_requests/state';
import pipelinesState from '~/ide/stores/modules/pipelines/state';
import branchesState from '~/ide/stores/modules/branches/state';
+import fileTemplatesState from '~/ide/stores/modules/file_templates/state';
+import paneState from '~/ide/stores/modules/pane/state';
export const resetStore = store => {
const newState = {
@@ -13,6 +15,8 @@ export const resetStore = store => {
mergeRequests: mergeRequestsState(),
pipelines: pipelinesState(),
branches: branchesState(),
+ fileTemplates: fileTemplatesState(),
+ rightPane: paneState(),
};
store.replaceState(newState);
};
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index 72eb20bdc87..1ca811e996b 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -352,10 +352,22 @@ describe('IDE store file actions', () => {
it('calls also getBaseRawFileData service method', done => {
spyOn(service, 'getBaseRawFileData').and.returnValue(Promise.resolve('baseraw'));
+ store.state.currentProjectId = 'gitlab-org/gitlab-ce';
+ store.state.currentMergeRequestId = '1';
+ store.state.projects = {
+ 'gitlab-org/gitlab-ce': {
+ mergeRequests: {
+ 1: {
+ baseCommitSha: 'SHA',
+ },
+ },
+ },
+ };
+
tmpFile.mrChange = { new_file: false };
store
- .dispatch('getRawFileData', { path: tmpFile.path, baseSha: 'SHA' })
+ .dispatch('getRawFileData', { path: tmpFile.path })
.then(() => {
expect(service.getBaseRawFileData).toHaveBeenCalledWith(tmpFile, 'SHA');
expect(tmpFile.baseRaw).toBe('baseraw');
@@ -392,10 +404,7 @@ describe('IDE store file actions', () => {
const dispatch = jasmine.createSpy('dispatch');
actions
- .getRawFileData(
- { state: store.state, commit() {}, dispatch },
- { path: tmpFile.path, baseSha: tmpFile.baseSha },
- )
+ .getRawFileData({ state: store.state, commit() {}, dispatch }, { path: tmpFile.path })
.then(done.fail)
.catch(() => {
expect(dispatch).toHaveBeenCalledWith('setErrorMessage', {
@@ -404,7 +413,6 @@ describe('IDE store file actions', () => {
actionText: 'Please try again',
actionPayload: {
path: tmpFile.path,
- baseSha: tmpFile.baseSha,
},
});
@@ -684,21 +692,6 @@ describe('IDE store file actions', () => {
.then(done)
.catch(done.fail);
});
-
- it('calls scrollToTab', done => {
- const scrollToTabSpy = jasmine.createSpy('scrollToTab');
- const oldScrollToTab = store._actions.scrollToTab; // eslint-disable-line
- store._actions.scrollToTab = [scrollToTabSpy]; // eslint-disable-line
-
- store
- .dispatch('openPendingTab', { file: f, keyPrefix: 'pending' })
- .then(() => {
- expect(scrollToTabSpy).toHaveBeenCalled();
- store._actions.scrollToTab = oldScrollToTab; // eslint-disable-line
- })
- .then(done)
- .catch(done.fail);
- });
});
describe('removePendingTab', () => {
diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js
index 8564f04ce8a..3a4e0d7507f 100644
--- a/spec/javascripts/ide/stores/actions/merge_request_spec.js
+++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js
@@ -262,7 +262,7 @@ describe('IDE store merge request actions', () => {
bar: {},
};
- spyOn(store, 'dispatch').and.callFake((type) => {
+ spyOn(store, 'dispatch').and.callFake(type => {
switch (type) {
case 'getMergeRequestData':
return Promise.resolve(testMergeRequest);
@@ -280,14 +280,20 @@ describe('IDE store merge request actions', () => {
expect(store.dispatch.calls.allArgs()).toEqual([
['getMergeRequestData', mr],
['setCurrentBranchId', testMergeRequest.source_branch],
- ['getBranchData', {
- projectId: mr.projectId,
- branchId: testMergeRequest.source_branch,
- }],
- ['getFiles', {
- projectId: mr.projectId,
- branchId: testMergeRequest.source_branch,
- }],
+ [
+ 'getBranchData',
+ {
+ projectId: mr.projectId,
+ branchId: testMergeRequest.source_branch,
+ },
+ ],
+ [
+ 'getFiles',
+ {
+ projectId: mr.projectId,
+ branchId: testMergeRequest.source_branch,
+ },
+ ],
['getMergeRequestVersions', mr],
['getMergeRequestChanges', mr],
]);
@@ -297,20 +303,21 @@ describe('IDE store merge request actions', () => {
});
it('updates activity bar view and gets file data, if changes are found', done => {
- testMergeRequestChanges.changes = [
- { new_path: 'foo' },
- { new_path: 'bar' },
- ];
+ testMergeRequestChanges.changes = [{ new_path: 'foo' }, { new_path: 'bar' }];
openMergeRequest(store, mr)
.then(() => {
- expect(store.dispatch).toHaveBeenCalledWith('updateActivityBarView', activityBarViews.review);
+ expect(store.dispatch).toHaveBeenCalledWith(
+ 'updateActivityBarView',
+ activityBarViews.review,
+ );
testMergeRequestChanges.changes.forEach((change, i) => {
expect(store.dispatch).toHaveBeenCalledWith('setFileMrChange', {
file: store.state.entries[change.new_path],
mrChange: change,
});
+
expect(store.dispatch).toHaveBeenCalledWith('getFileData', {
path: change.new_path,
makeFileActive: i === 0,
@@ -334,7 +341,6 @@ describe('IDE store merge request actions', () => {
})
.then(done)
.catch(done.fail);
-
});
});
});
diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js
index 667e3e0a7ef..7d8c9edd965 100644
--- a/spec/javascripts/ide/stores/actions/project_spec.js
+++ b/spec/javascripts/ide/stores/actions/project_spec.js
@@ -270,7 +270,10 @@ describe('IDE store project actions', () => {
it('does not handle tree entry action, if entry is pending', done => {
openBranch(store, { ...branch, basePath: 'foo/bar-pending' })
.then(() => {
- expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything());
+ expect(store.dispatch).not.toHaveBeenCalledWith(
+ 'handleTreeEntryAction',
+ jasmine.anything(),
+ );
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js
index 9f098eded08..d47c60dc581 100644
--- a/spec/javascripts/ide/stores/actions/tree_spec.js
+++ b/spec/javascripts/ide/stores/actions/tree_spec.js
@@ -71,6 +71,7 @@ describe('Multi-file store tree actions', () => {
.dispatch('getFiles', basicCallParameters)
.then(() => {
projectTree = store.state.trees['abcproject/master'];
+
expect(projectTree.tree.length).toBe(2);
expect(projectTree.tree[0].type).toBe('tree');
expect(projectTree.tree[0].tree[1].name).toBe('fileinfolder.js');
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index d84f1717a61..df291ade3f7 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -279,8 +279,6 @@ describe('Multi-file store actions', () => {
});
});
- describe('popHistoryState', () => {});
-
describe('scrollToTab', () => {
it('focuses the current active element', done => {
document.body.innerHTML +=
@@ -305,7 +303,11 @@ describe('Multi-file store actions', () => {
describe('stageAllChanges', () => {
it('adds all files from changedFiles to stagedFiles', done => {
- store.state.changedFiles.push(file(), file('new'));
+ const openFile = { ...file(), path: 'test' };
+
+ store.state.openFiles.push(openFile);
+ store.state.stagedFiles.push(openFile);
+ store.state.changedFiles.push(openFile, file('new'));
testAction(
stageAllChanges,
@@ -316,7 +318,12 @@ describe('Multi-file store actions', () => {
{ type: types.STAGE_CHANGE, payload: store.state.changedFiles[0].path },
{ type: types.STAGE_CHANGE, payload: store.state.changedFiles[1].path },
],
- [],
+ [
+ {
+ type: 'openPendingTab',
+ payload: { file: openFile, keyPrefix: 'staged' },
+ },
+ ],
done,
);
});
@@ -324,7 +331,11 @@ describe('Multi-file store actions', () => {
describe('unstageAllChanges', () => {
it('removes all files from stagedFiles after unstaging', done => {
- store.state.stagedFiles.push(file(), file('new'));
+ const openFile = { ...file(), path: 'test' };
+
+ store.state.openFiles.push(openFile);
+ store.state.changedFiles.push(openFile);
+ store.state.stagedFiles.push(openFile, file('new'));
testAction(
unstageAllChanges,
@@ -334,7 +345,12 @@ describe('Multi-file store actions', () => {
{ type: types.UNSTAGE_CHANGE, payload: store.state.stagedFiles[0].path },
{ type: types.UNSTAGE_CHANGE, payload: store.state.stagedFiles[1].path },
],
- [],
+ [
+ {
+ type: 'openPendingTab',
+ payload: { file: openFile, keyPrefix: 'unstaged' },
+ },
+ ],
done,
);
});
diff --git a/spec/javascripts/ide/stores/modules/branches/actions_spec.js b/spec/javascripts/ide/stores/modules/branches/actions_spec.js
index 010f56af03b..2b3eac282f6 100644
--- a/spec/javascripts/ide/stores/modules/branches/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/branches/actions_spec.js
@@ -60,7 +60,6 @@ describe('IDE branches actions', () => {
describe('receiveBranchesError', () => {
it('should should commit error', done => {
-
testAction(
receiveBranchesError,
{ search: TEST_SEARCH },
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index 24a7d76f30b..06b8b452319 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -184,7 +184,7 @@ describe('IDE commit module actions', () => {
branch,
})
.then(() => {
- expect(f.lastCommit.message).toBe(data.message);
+ expect(f.lastCommitSha).toBe(data.id);
})
.then(done)
.catch(done.fail);
@@ -266,19 +266,21 @@ describe('IDE commit module actions', () => {
});
describe('success', () => {
+ const COMMIT_RESPONSE = {
+ id: '123456',
+ short_id: '123',
+ message: 'test message',
+ committed_date: 'date',
+ stats: {
+ additions: '1',
+ deletions: '2',
+ },
+ };
+
beforeEach(() => {
spyOn(service, 'commit').and.returnValue(
Promise.resolve({
- data: {
- id: '123456',
- short_id: '123',
- message: 'test message',
- committed_date: 'date',
- stats: {
- additions: '1',
- deletions: '2',
- },
- },
+ data: COMMIT_RESPONSE,
}),
);
});
@@ -352,8 +354,8 @@ describe('IDE commit module actions', () => {
store
.dispatch('commit/commitChanges')
.then(() => {
- expect(store.state.entries[store.state.openFiles[0].path].lastCommit.message).toBe(
- 'test message',
+ expect(store.state.entries[store.state.openFiles[0].path].lastCommitSha).toBe(
+ COMMIT_RESPONSE.id,
);
done();
diff --git a/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js b/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js
new file mode 100644
index 00000000000..734233100ab
--- /dev/null
+++ b/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js
@@ -0,0 +1,441 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import createState from '~/ide/stores/modules/file_templates/state';
+import * as actions from '~/ide/stores/modules/file_templates/actions';
+import * as types from '~/ide/stores/modules/file_templates/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+
+describe('IDE file templates actions', () => {
+ let state;
+ let mock;
+
+ beforeEach(() => {
+ state = createState();
+
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('requestTemplateTypes', () => {
+ it('commits REQUEST_TEMPLATE_TYPES', done => {
+ testAction(
+ actions.requestTemplateTypes,
+ null,
+ state,
+ [{ type: types.REQUEST_TEMPLATE_TYPES }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveTemplateTypesError', () => {
+ it('commits RECEIVE_TEMPLATE_TYPES_ERROR and dispatches setErrorMessage', done => {
+ testAction(
+ actions.receiveTemplateTypesError,
+ null,
+ state,
+ [{ type: types.RECEIVE_TEMPLATE_TYPES_ERROR }],
+ [
+ {
+ type: 'setErrorMessage',
+ payload: {
+ action: jasmine.any(Function),
+ actionText: 'Please try again',
+ text: 'Error loading template types.',
+ },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('receiveTemplateTypesSuccess', () => {
+ it('commits RECEIVE_TEMPLATE_TYPES_SUCCESS', done => {
+ testAction(
+ actions.receiveTemplateTypesSuccess,
+ 'test',
+ state,
+ [{ type: types.RECEIVE_TEMPLATE_TYPES_SUCCESS, payload: 'test' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchTemplateTypes', () => {
+ describe('success', () => {
+ let nextPage;
+
+ beforeEach(() => {
+ mock.onGet(/api\/(.*)\/templates\/licenses/).replyOnce(() => [
+ 200,
+ [
+ {
+ name: 'MIT',
+ },
+ ],
+ { 'X-NEXT-PAGE': nextPage },
+ ]);
+ });
+
+ it('rejects if selectedTemplateType is empty', done => {
+ const dispatch = jasmine.createSpy('dispatch');
+
+ actions
+ .fetchTemplateTypes({ dispatch, state })
+ .then(done.fail)
+ .catch(() => {
+ expect(dispatch).not.toHaveBeenCalled();
+
+ done();
+ });
+ });
+
+ it('dispatches actions', done => {
+ state.selectedTemplateType = {
+ key: 'licenses',
+ };
+
+ testAction(
+ actions.fetchTemplateTypes,
+ null,
+ state,
+ [],
+ [
+ {
+ type: 'requestTemplateTypes',
+ },
+ {
+ type: 'receiveTemplateTypesSuccess',
+ payload: [
+ {
+ name: 'MIT',
+ },
+ ],
+ },
+ ],
+ done,
+ );
+ });
+
+ it('dispatches actions for next page', done => {
+ nextPage = '2';
+ state.selectedTemplateType = {
+ key: 'licenses',
+ };
+
+ testAction(
+ actions.fetchTemplateTypes,
+ null,
+ state,
+ [],
+ [
+ {
+ type: 'requestTemplateTypes',
+ },
+ {
+ type: 'receiveTemplateTypesSuccess',
+ payload: [
+ {
+ name: 'MIT',
+ },
+ ],
+ },
+ {
+ type: 'fetchTemplateTypes',
+ payload: 2,
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(/api\/(.*)\/templates\/licenses/).replyOnce(500);
+ });
+
+ it('dispatches actions', done => {
+ state.selectedTemplateType = {
+ key: 'licenses',
+ };
+
+ testAction(
+ actions.fetchTemplateTypes,
+ null,
+ state,
+ [],
+ [
+ {
+ type: 'requestTemplateTypes',
+ },
+ {
+ type: 'receiveTemplateTypesError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('setSelectedTemplateType', () => {
+ it('commits SET_SELECTED_TEMPLATE_TYPE', () => {
+ const commit = jasmine.createSpy('commit');
+ const options = {
+ commit,
+ dispatch() {},
+ rootGetters: {
+ activeFile: {
+ name: 'test',
+ prevPath: '',
+ },
+ },
+ };
+
+ actions.setSelectedTemplateType(options, { name: 'test' });
+
+ expect(commit).toHaveBeenCalledWith(types.SET_SELECTED_TEMPLATE_TYPE, { name: 'test' });
+ });
+
+ it('dispatches discardFileChanges if prevPath matches templates name', () => {
+ const dispatch = jasmine.createSpy('dispatch');
+ const options = {
+ commit() {},
+ dispatch,
+ rootGetters: {
+ activeFile: {
+ name: 'test',
+ path: 'test',
+ prevPath: 'test',
+ },
+ },
+ };
+
+ actions.setSelectedTemplateType(options, { name: 'test' });
+
+ expect(dispatch).toHaveBeenCalledWith('discardFileChanges', 'test', { root: true });
+ });
+
+ it('dispatches renameEntry if file name doesnt match', () => {
+ const dispatch = jasmine.createSpy('dispatch');
+ const options = {
+ commit() {},
+ dispatch,
+ rootGetters: {
+ activeFile: {
+ name: 'oldtest',
+ path: 'oldtest',
+ prevPath: '',
+ },
+ },
+ };
+
+ actions.setSelectedTemplateType(options, { name: 'test' });
+
+ expect(dispatch).toHaveBeenCalledWith(
+ 'renameEntry',
+ {
+ path: 'oldtest',
+ name: 'test',
+ },
+ { root: true },
+ );
+ });
+ });
+
+ describe('receiveTemplateError', () => {
+ it('dispatches setErrorMessage', done => {
+ testAction(
+ actions.receiveTemplateError,
+ 'test',
+ state,
+ [],
+ [
+ {
+ type: 'setErrorMessage',
+ payload: {
+ action: jasmine.any(Function),
+ actionText: 'Please try again',
+ text: 'Error loading template.',
+ actionPayload: 'test',
+ },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('fetchTemplate', () => {
+ describe('success', () => {
+ beforeEach(() => {
+ mock.onGet(/api\/(.*)\/templates\/licenses\/mit/).replyOnce(200, {
+ content: 'MIT content',
+ });
+ mock.onGet(/api\/(.*)\/templates\/licenses\/testing/).replyOnce(200, {
+ content: 'testing content',
+ });
+ });
+
+ it('dispatches setFileTemplate if template already has content', done => {
+ const template = {
+ content: 'already has content',
+ };
+
+ testAction(
+ actions.fetchTemplate,
+ template,
+ state,
+ [],
+ [{ type: 'setFileTemplate', payload: template }],
+ done,
+ );
+ });
+
+ it('dispatches success', done => {
+ const template = {
+ key: 'mit',
+ };
+
+ state.selectedTemplateType = {
+ key: 'licenses',
+ };
+
+ testAction(
+ actions.fetchTemplate,
+ template,
+ state,
+ [],
+ [{ type: 'setFileTemplate', payload: { content: 'MIT content' } }],
+ done,
+ );
+ });
+
+ it('dispatches success and uses name key for API call', done => {
+ const template = {
+ name: 'testing',
+ };
+
+ state.selectedTemplateType = {
+ key: 'licenses',
+ };
+
+ testAction(
+ actions.fetchTemplate,
+ template,
+ state,
+ [],
+ [{ type: 'setFileTemplate', payload: { content: 'testing content' } }],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(/api\/(.*)\/templates\/licenses\/mit/).replyOnce(500);
+ });
+
+ it('dispatches error', done => {
+ const template = {
+ name: 'testing',
+ };
+
+ state.selectedTemplateType = {
+ key: 'licenses',
+ };
+
+ testAction(
+ actions.fetchTemplate,
+ template,
+ state,
+ [],
+ [{ type: 'receiveTemplateError', payload: template }],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('setFileTemplate', () => {
+ it('dispatches changeFileContent', () => {
+ const dispatch = jasmine.createSpy('dispatch');
+ const commit = jasmine.createSpy('commit');
+ const rootGetters = {
+ activeFile: { path: 'test' },
+ };
+
+ actions.setFileTemplate({ dispatch, commit, rootGetters }, { content: 'content' });
+
+ expect(dispatch).toHaveBeenCalledWith(
+ 'changeFileContent',
+ { path: 'test', content: 'content' },
+ { root: true },
+ );
+ });
+
+ it('commits SET_UPDATE_SUCCESS', () => {
+ const dispatch = jasmine.createSpy('dispatch');
+ const commit = jasmine.createSpy('commit');
+ const rootGetters = {
+ activeFile: { path: 'test' },
+ };
+
+ actions.setFileTemplate({ dispatch, commit, rootGetters }, { content: 'content' });
+
+ expect(commit).toHaveBeenCalledWith('SET_UPDATE_SUCCESS', true);
+ });
+ });
+
+ describe('undoFileTemplate', () => {
+ it('dispatches changeFileContent', () => {
+ const dispatch = jasmine.createSpy('dispatch');
+ const commit = jasmine.createSpy('commit');
+ const rootGetters = {
+ activeFile: { path: 'test', raw: 'raw content' },
+ };
+
+ actions.undoFileTemplate({ dispatch, commit, rootGetters });
+
+ expect(dispatch).toHaveBeenCalledWith(
+ 'changeFileContent',
+ { path: 'test', content: 'raw content' },
+ { root: true },
+ );
+ });
+
+ it('commits SET_UPDATE_SUCCESS', () => {
+ const dispatch = jasmine.createSpy('dispatch');
+ const commit = jasmine.createSpy('commit');
+ const rootGetters = {
+ activeFile: { path: 'test', raw: 'raw content' },
+ };
+
+ actions.undoFileTemplate({ dispatch, commit, rootGetters });
+
+ expect(commit).toHaveBeenCalledWith('SET_UPDATE_SUCCESS', false);
+ });
+
+ it('dispatches discardFileChanges if file has prevPath', () => {
+ const dispatch = jasmine.createSpy('dispatch');
+ const rootGetters = {
+ activeFile: { path: 'test', prevPath: 'newtest', raw: 'raw content' },
+ };
+
+ actions.undoFileTemplate({ dispatch, commit() {}, rootGetters });
+
+ expect(dispatch.calls.mostRecent().args).toEqual([
+ 'discardFileChanges',
+ 'test',
+ { root: true },
+ ]);
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/file_templates/getters_spec.js b/spec/javascripts/ide/stores/modules/file_templates/getters_spec.js
new file mode 100644
index 00000000000..17cb457881f
--- /dev/null
+++ b/spec/javascripts/ide/stores/modules/file_templates/getters_spec.js
@@ -0,0 +1,59 @@
+import createState from '~/ide/stores/state';
+import { activityBarViews } from '~/ide/constants';
+import * as getters from '~/ide/stores/modules/file_templates/getters';
+
+describe('IDE file templates getters', () => {
+ describe('templateTypes', () => {
+ it('returns list of template types', () => {
+ expect(getters.templateTypes().length).toBe(4);
+ });
+ });
+
+ describe('showFileTemplatesBar', () => {
+ let rootState;
+
+ beforeEach(() => {
+ rootState = createState();
+ });
+
+ it('returns true if template is found and currentActivityView is edit', () => {
+ rootState.currentActivityView = activityBarViews.edit;
+
+ expect(
+ getters.showFileTemplatesBar(
+ null,
+ {
+ templateTypes: getters.templateTypes(),
+ },
+ rootState,
+ )('LICENSE'),
+ ).toBe(true);
+ });
+
+ it('returns false if template is found and currentActivityView is not edit', () => {
+ rootState.currentActivityView = activityBarViews.commit;
+
+ expect(
+ getters.showFileTemplatesBar(
+ null,
+ {
+ templateTypes: getters.templateTypes(),
+ },
+ rootState,
+ )('LICENSE'),
+ ).toBe(false);
+ });
+
+ it('returns undefined if not found', () => {
+ expect(
+ getters.showFileTemplatesBar(
+ null,
+ {
+ templateTypes: getters.templateTypes(),
+ },
+ rootState,
+ )('test'),
+ ).toBe(undefined);
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/file_templates/mutations_spec.js b/spec/javascripts/ide/stores/modules/file_templates/mutations_spec.js
new file mode 100644
index 00000000000..8e0e3ae99a1
--- /dev/null
+++ b/spec/javascripts/ide/stores/modules/file_templates/mutations_spec.js
@@ -0,0 +1,69 @@
+import createState from '~/ide/stores/modules/file_templates/state';
+import * as types from '~/ide/stores/modules/file_templates/mutation_types';
+import mutations from '~/ide/stores/modules/file_templates/mutations';
+
+describe('IDE file templates mutations', () => {
+ let state;
+
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe(types.REQUEST_TEMPLATE_TYPES, () => {
+ it('sets isLoading', () => {
+ mutations[types.REQUEST_TEMPLATE_TYPES](state);
+
+ expect(state.isLoading).toBe(true);
+ });
+ });
+
+ describe(types.RECEIVE_TEMPLATE_TYPES_ERROR, () => {
+ it('sets isLoading', () => {
+ state.isLoading = true;
+
+ mutations[types.RECEIVE_TEMPLATE_TYPES_ERROR](state);
+
+ expect(state.isLoading).toBe(false);
+ });
+ });
+
+ describe(types.RECEIVE_TEMPLATE_TYPES_SUCCESS, () => {
+ it('sets isLoading to false', () => {
+ state.isLoading = true;
+
+ mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, []);
+
+ expect(state.isLoading).toBe(false);
+ });
+
+ it('sets templates', () => {
+ mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, ['test']);
+
+ expect(state.templates).toEqual(['test']);
+ });
+ });
+
+ describe(types.SET_SELECTED_TEMPLATE_TYPE, () => {
+ it('sets selectedTemplateType', () => {
+ mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, 'type');
+
+ expect(state.selectedTemplateType).toBe('type');
+ });
+
+ it('clears templates', () => {
+ state.templates = ['test'];
+
+ mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, 'type');
+
+ expect(state.templates).toEqual([]);
+ });
+ });
+
+ describe(types.SET_UPDATE_SUCCESS, () => {
+ it('sets updateSuccess', () => {
+ mutations[types.SET_UPDATE_SUCCESS](state, true);
+
+ expect(state.updateSuccess).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/pane/actions_spec.js b/spec/javascripts/ide/stores/modules/pane/actions_spec.js
new file mode 100644
index 00000000000..799bc89a0c3
--- /dev/null
+++ b/spec/javascripts/ide/stores/modules/pane/actions_spec.js
@@ -0,0 +1,66 @@
+import * as actions from '~/ide/stores/modules/pane/actions';
+import * as types from '~/ide/stores/modules/pane/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+
+describe('IDE pane module actions', () => {
+ const TEST_VIEW = { name: 'test' };
+ const TEST_VIEW_KEEP_ALIVE = { name: 'test-keep-alive', keepAlive: true };
+
+ describe('toggleOpen', () => {
+ it('dispatches open if closed', done => {
+ testAction(
+ actions.toggleOpen,
+ TEST_VIEW,
+ { isOpen: false },
+ [],
+ [{ type: 'open', payload: TEST_VIEW }],
+ done,
+ );
+ });
+
+ it('dispatches close if opened', done => {
+ testAction(actions.toggleOpen, TEST_VIEW, { isOpen: true }, [], [{ type: 'close' }], done);
+ });
+ });
+
+ describe('open', () => {
+ it('commits SET_OPEN', done => {
+ testAction(actions.open, null, {}, [{ type: types.SET_OPEN, payload: true }], [], done);
+ });
+
+ it('commits SET_CURRENT_VIEW if view is given', done => {
+ testAction(
+ actions.open,
+ TEST_VIEW,
+ {},
+ [
+ { type: types.SET_OPEN, payload: true },
+ { type: types.SET_CURRENT_VIEW, payload: TEST_VIEW.name },
+ ],
+ [],
+ done,
+ );
+ });
+
+ it('commits KEEP_ALIVE_VIEW if keepAlive is true', done => {
+ testAction(
+ actions.open,
+ TEST_VIEW_KEEP_ALIVE,
+ {},
+ [
+ { type: types.SET_OPEN, payload: true },
+ { type: types.SET_CURRENT_VIEW, payload: TEST_VIEW_KEEP_ALIVE.name },
+ { type: types.KEEP_ALIVE_VIEW, payload: TEST_VIEW_KEEP_ALIVE.name },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('close', () => {
+ it('commits SET_OPEN', done => {
+ testAction(actions.close, null, {}, [{ type: types.SET_OPEN, payload: false }], [], done);
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/pane/getters_spec.js b/spec/javascripts/ide/stores/modules/pane/getters_spec.js
new file mode 100644
index 00000000000..8a213323de0
--- /dev/null
+++ b/spec/javascripts/ide/stores/modules/pane/getters_spec.js
@@ -0,0 +1,55 @@
+import * as getters from '~/ide/stores/modules/pane/getters';
+import state from '~/ide/stores/modules/pane/state';
+
+describe('IDE pane module getters', () => {
+ const TEST_VIEW = 'test-view';
+ const TEST_KEEP_ALIVE_VIEWS = {
+ [TEST_VIEW]: true,
+ };
+
+ describe('isActiveView', () => {
+ it('returns true if given view matches currentView', () => {
+ const result = getters.isActiveView({ currentView: 'A' })('A');
+
+ expect(result).toBe(true);
+ });
+
+ it('returns false if given view does not match currentView', () => {
+ const result = getters.isActiveView({ currentView: 'A' })('B');
+
+ expect(result).toBe(false);
+ });
+ });
+
+ describe('isAliveView', () => {
+ it('returns true if given view is in keepAliveViews', () => {
+ const result = getters.isAliveView({ keepAliveViews: TEST_KEEP_ALIVE_VIEWS }, {})(TEST_VIEW);
+
+ expect(result).toBe(true);
+ });
+
+ it('returns true if given view is active view and open', () => {
+ const result = getters.isAliveView(
+ { ...state(), isOpen: true },
+ { isActiveView: () => true },
+ )(TEST_VIEW);
+
+ expect(result).toBe(true);
+ });
+
+ it('returns false if given view is active view and closed', () => {
+ const result = getters.isAliveView(state(), { isActiveView: () => true })(TEST_VIEW);
+
+ expect(result).toBe(false);
+ });
+
+ it('returns false if given view is not activeView', () => {
+ const result = getters.isAliveView(
+ { ...state(), isOpen: true },
+ { isActiveView: () => false },
+ )(TEST_VIEW);
+
+ expect(result).toBe(false);
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/pane/mutations_spec.js b/spec/javascripts/ide/stores/modules/pane/mutations_spec.js
new file mode 100644
index 00000000000..b5fcd35912e
--- /dev/null
+++ b/spec/javascripts/ide/stores/modules/pane/mutations_spec.js
@@ -0,0 +1,42 @@
+import state from '~/ide/stores/modules/pane/state';
+import mutations from '~/ide/stores/modules/pane/mutations';
+import * as types from '~/ide/stores/modules/pane/mutation_types';
+
+describe('IDE pane module mutations', () => {
+ const TEST_VIEW = 'test-view';
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe('SET_OPEN', () => {
+ it('sets isOpen', () => {
+ mockedState.isOpen = false;
+
+ mutations[types.SET_OPEN](mockedState, true);
+
+ expect(mockedState.isOpen).toBe(true);
+ });
+ });
+
+ describe('SET_CURRENT_VIEW', () => {
+ it('sets currentView', () => {
+ mockedState.currentView = null;
+
+ mutations[types.SET_CURRENT_VIEW](mockedState, TEST_VIEW);
+
+ expect(mockedState.currentView).toEqual(TEST_VIEW);
+ });
+ });
+
+ describe('KEEP_ALIVE_VIEW', () => {
+ it('adds entry to keepAliveViews', () => {
+ mutations[types.KEEP_ALIVE_VIEW](mockedState, TEST_VIEW);
+
+ expect(mockedState.keepAliveViews).toEqual({
+ [TEST_VIEW]: true,
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
index 91edb388791..c9c09ee9afe 100644
--- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
@@ -77,7 +77,7 @@ describe('IDE pipelines actions', () => {
{
type: 'setErrorMessage',
payload: {
- text: 'An error occured whilst fetching the latest pipline.',
+ text: 'An error occured whilst fetching the latest pipeline.',
action: jasmine.any(Function),
actionText: 'Please try again',
actionPayload: null,
@@ -315,29 +315,29 @@ describe('IDE pipelines actions', () => {
'job',
mockedState,
[{ type: types.SET_DETAIL_JOB, payload: 'job' }],
- [{ type: 'setRightPane', payload: 'jobs-detail' }],
+ [{ type: 'rightPane/open', payload: rightSidebarViews.jobsDetail }],
done,
);
});
- it('dispatches setRightPane as pipeline when job is null', done => {
+ it('dispatches rightPane/open as pipeline when job is null', done => {
testAction(
setDetailJob,
null,
mockedState,
[{ type: types.SET_DETAIL_JOB, payload: null }],
- [{ type: 'setRightPane', payload: rightSidebarViews.pipelines }],
+ [{ type: 'rightPane/open', payload: rightSidebarViews.pipelines }],
done,
);
});
- it('dispatches setRightPane as job', done => {
+ it('dispatches rightPane/open as job', done => {
testAction(
setDetailJob,
'job',
mockedState,
[{ type: types.SET_DETAIL_JOB, payload: 'job' }],
- [{ type: 'setRightPane', payload: rightSidebarViews.jobsDetail }],
+ [{ type: 'rightPane/open', payload: rightSidebarViews.jobsDetail }],
done,
);
});
diff --git a/spec/javascripts/ide/stores/mutations/merge_request_spec.js b/spec/javascripts/ide/stores/mutations/merge_request_spec.js
index f724bf464f5..e30ca22022f 100644
--- a/spec/javascripts/ide/stores/mutations/merge_request_spec.js
+++ b/spec/javascripts/ide/stores/mutations/merge_request_spec.js
@@ -45,6 +45,7 @@ describe('IDE store merge request mutations', () => {
});
const newMr = localState.projects.abcproject.mergeRequests[1];
+
expect(newMr.changes.diff).toBe('abc');
});
});
@@ -58,6 +59,7 @@ describe('IDE store merge request mutations', () => {
});
const newMr = localState.projects.abcproject.mergeRequests[1];
+
expect(newMr.versions.length).toBe(1);
expect(newMr.versions[0].id).toBe(123);
});
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 1e836dbc3f9..41dd3d3c67f 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -213,6 +213,33 @@ describe('Multi-file store mutations', () => {
expect(localState.changedFiles).toEqual([localState.entries.filePath]);
});
+
+ it('does not add tempFile into changedFiles', () => {
+ localState.entries.filePath = {
+ deleted: false,
+ type: 'blob',
+ tempFile: true,
+ };
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.changedFiles).toEqual([]);
+ });
+
+ it('removes tempFile from changedFiles when deleted', () => {
+ localState.entries.filePath = {
+ path: 'filePath',
+ deleted: false,
+ type: 'blob',
+ tempFile: true,
+ };
+
+ localState.changedFiles.push({ ...localState.entries.filePath });
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.changedFiles).toEqual([]);
+ });
});
describe('UPDATE_FILE_AFTER_COMMIT', () => {
@@ -312,5 +339,13 @@ describe('Multi-file store mutations', () => {
expect(localState.entries.parentPath.tree.length).toBe(1);
});
+
+ it('adds to openFiles if previously opened', () => {
+ localState.entries.oldPath.opened = true;
+
+ mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
+
+ expect(localState.openFiles).toEqual([localState.entries.newPath]);
+ });
});
});
diff --git a/spec/javascripts/image_diff/helpers/badge_helper_spec.js b/spec/javascripts/image_diff/helpers/badge_helper_spec.js
index ce3add1fd90..8ea05203d00 100644
--- a/spec/javascripts/image_diff/helpers/badge_helper_spec.js
+++ b/spec/javascripts/image_diff/helpers/badge_helper_spec.js
@@ -90,6 +90,7 @@ describe('badge helper', () => {
it('should create icon comment button', () => {
const iconEl = buttonEl.querySelector('svg');
+
expect(iconEl).toBeDefined();
});
});
diff --git a/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js b/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js
index a284b981d2a..8e3e7f1222e 100644
--- a/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js
+++ b/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js
@@ -29,10 +29,12 @@ describe('commentIndicatorHelper', () => {
it('should contain image-comment-dark svg', () => {
const svgEl = buttonEl.querySelector('svg');
+
expect(svgEl).toBeDefined();
const svgLink = svgEl.querySelector('use').getAttribute('xlink:href');
- expect(svgLink.indexOf('image-comment-dark') !== -1).toEqual(true);
+
+ expect(svgLink.indexOf('image-comment-dark')).not.toBe(-1);
});
});
});
@@ -40,6 +42,7 @@ describe('commentIndicatorHelper', () => {
describe('removeCommentIndicator', () => {
it('should return removed false if there is no comment-indicator', () => {
const result = commentIndicatorHelper.removeCommentIndicator(containerEl);
+
expect(result.removed).toEqual(false);
});
@@ -84,6 +87,7 @@ describe('commentIndicatorHelper', () => {
it('should set commentIndicator coordinates', () => {
const commentIndicatorEl = containerEl.querySelector('.comment-indicator');
+
expect(commentIndicatorEl.style.left).toEqual(`${coordinate.x}px`);
expect(commentIndicatorEl.style.top).toEqual(`${coordinate.y}px`);
});
@@ -96,6 +100,7 @@ describe('commentIndicatorHelper', () => {
it('should addCommentIndicator', () => {
const buttonEl = containerEl.querySelector('.comment-indicator');
+
expect(buttonEl).toBeDefined();
expect(buttonEl.style.left).toEqual(`${coordinate.x}px`);
expect(buttonEl.style.top).toEqual(`${coordinate.y}px`);
diff --git a/spec/javascripts/image_diff/helpers/dom_helper_spec.js b/spec/javascripts/image_diff/helpers/dom_helper_spec.js
index 8dde924e8ae..ffe712af2dd 100644
--- a/spec/javascripts/image_diff/helpers/dom_helper_spec.js
+++ b/spec/javascripts/image_diff/helpers/dom_helper_spec.js
@@ -92,6 +92,7 @@ describe('domHelper', () => {
it('should force formEl to display none', () => {
const formEl = element.querySelector('.discussion-form');
+
expect(formEl.style.display).toEqual('none');
});
});
@@ -111,6 +112,7 @@ describe('domHelper', () => {
it('should force formEl to display block', () => {
const formEl = element.querySelector('.discussion-form');
+
expect(formEl.style.display).toEqual('block');
});
});
diff --git a/spec/javascripts/image_diff/helpers/utils_helper_spec.js b/spec/javascripts/image_diff/helpers/utils_helper_spec.js
index 31949c39d9c..6f62ee3f93b 100644
--- a/spec/javascripts/image_diff/helpers/utils_helper_spec.js
+++ b/spec/javascripts/image_diff/helpers/utils_helper_spec.js
@@ -5,13 +5,7 @@ import ImageBadge from '~/image_diff/image_badge';
import * as mockData from '../mock_data';
describe('utilsHelper', () => {
- const {
- noteId,
- discussionId,
- image,
- imageProperties,
- imageMeta,
- } = mockData;
+ const { noteId, discussionId, image, imageProperties, imageMeta } = mockData;
describe('resizeCoordinatesToImageElement', () => {
let result;
@@ -57,6 +51,7 @@ describe('utilsHelper', () => {
it('should return actual image properties', () => {
const { actual } = result;
+
expect(actual.x).toEqual(imageMeta.x);
expect(actual.y).toEqual(imageMeta.y);
expect(actual.width).toEqual(imageMeta.width);
@@ -65,6 +60,7 @@ describe('utilsHelper', () => {
it('should return browser image properties', () => {
const { browser } = result;
+
expect(browser.x).toBeDefined();
expect(browser.y).toBeDefined();
expect(browser.width).toBeDefined();
@@ -106,6 +102,7 @@ describe('utilsHelper', () => {
const result = utilsHelper.getTargetSelection(event);
const { browser } = result;
+
expect(browser.x).toEqual(event.offsetX);
expect(browser.y).toEqual(event.offsetY);
expect(browser.width).toEqual(imageProperties.width);
@@ -117,6 +114,7 @@ describe('utilsHelper', () => {
const result = utilsHelper.getTargetSelection(event);
const { actual } = result;
+
expect(actual.x).toEqual(100);
expect(actual.y).toEqual(100);
expect(actual.width).toEqual(imageProperties.naturalWidth);
@@ -127,24 +125,28 @@ describe('utilsHelper', () => {
it('should return x = 0 if x < 0', () => {
const event = generateEvent(-5, 50);
const result = utilsHelper.getTargetSelection(event);
+
expect(result.browser.x).toEqual(0);
});
it('should return x = width if x > width', () => {
const event = generateEvent(1000, 50);
const result = utilsHelper.getTargetSelection(event);
+
expect(result.browser.x).toEqual(imageProperties.width);
});
it('should return y = 0 if y < 0', () => {
const event = generateEvent(50, -10);
const result = utilsHelper.getTargetSelection(event);
+
expect(result.browser.y).toEqual(0);
});
it('should return y = height if y > height', () => {
const event = generateEvent(50, 1000);
const result = utilsHelper.getTargetSelection(event);
+
expect(result.browser.y).toEqual(imageProperties.height);
});
});
@@ -178,6 +180,7 @@ describe('utilsHelper', () => {
`;
const imageDiff = utilsHelper.initImageDiff(fileEl, true, false);
+
expect(ImageDiff.prototype.init).toHaveBeenCalled();
expect(imageDiff.canCreateNote).toEqual(true);
expect(imageDiff.renderCommentBadge).toEqual(false);
@@ -191,6 +194,7 @@ describe('utilsHelper', () => {
`;
const replacedImageDiff = utilsHelper.initImageDiff(fileEl, false, true);
+
expect(ReplacedImageDiff.prototype.init).toHaveBeenCalled();
expect(replacedImageDiff.canCreateNote).toEqual(false);
expect(replacedImageDiff.renderCommentBadge).toEqual(true);
diff --git a/spec/javascripts/image_diff/image_badge_spec.js b/spec/javascripts/image_diff/image_badge_spec.js
index 87f98fc0926..2b23dce5d30 100644
--- a/spec/javascripts/image_diff/image_badge_spec.js
+++ b/spec/javascripts/image_diff/image_badge_spec.js
@@ -10,11 +10,14 @@ describe('ImageBadge', () => {
};
it('should save actual property', () => {
- const imageBadge = new ImageBadge(Object.assign({}, options, {
- actual: imageMeta,
- }));
+ const imageBadge = new ImageBadge(
+ Object.assign({}, options, {
+ actual: imageMeta,
+ }),
+ );
const { actual } = imageBadge;
+
expect(actual.x).toEqual(imageMeta.x);
expect(actual.y).toEqual(imageMeta.y);
expect(actual.width).toEqual(imageMeta.width);
@@ -22,11 +25,14 @@ describe('ImageBadge', () => {
});
it('should save browser property', () => {
- const imageBadge = new ImageBadge(Object.assign({}, options, {
- browser: imageMeta,
- }));
+ const imageBadge = new ImageBadge(
+ Object.assign({}, options, {
+ browser: imageMeta,
+ }),
+ );
const { browser } = imageBadge;
+
expect(browser.x).toEqual(imageMeta.x);
expect(browser.y).toEqual(imageMeta.y);
expect(browser.width).toEqual(imageMeta.width);
@@ -35,11 +41,13 @@ describe('ImageBadge', () => {
it('should save noteId', () => {
const imageBadge = new ImageBadge(options);
+
expect(imageBadge.noteId).toEqual(noteId);
});
it('should save discussionId', () => {
const imageBadge = new ImageBadge(options);
+
expect(imageBadge.discussionId).toEqual(discussionId);
});
@@ -52,6 +60,7 @@ describe('ImageBadge', () => {
it('should return defaultimageMeta if actual property is not provided', () => {
const { actual } = imageBadge;
+
expect(actual.x).toEqual(0);
expect(actual.y).toEqual(0);
expect(actual.width).toEqual(0);
@@ -60,6 +69,7 @@ describe('ImageBadge', () => {
it('should return defaultimageMeta if browser property is not provided', () => {
const { browser } = imageBadge;
+
expect(browser.x).toEqual(0);
expect(browser.y).toEqual(0);
expect(browser.width).toEqual(0);
@@ -73,9 +83,11 @@ describe('ImageBadge', () => {
});
it('should generate browser property', () => {
- const imageBadge = new ImageBadge(Object.assign({}, options, {
- imageEl: document.createElement('img'),
- }));
+ const imageBadge = new ImageBadge(
+ Object.assign({}, options, {
+ imageEl: document.createElement('img'),
+ }),
+ );
expect(imageDiffHelper.resizeCoordinatesToImageElement).toHaveBeenCalled();
expect(imageBadge.browser).toEqual(true);
diff --git a/spec/javascripts/image_diff/image_diff_spec.js b/spec/javascripts/image_diff/image_diff_spec.js
index 346282328c7..21e7b8e2e9b 100644
--- a/spec/javascripts/image_diff/image_diff_spec.js
+++ b/spec/javascripts/image_diff/image_diff_spec.js
@@ -117,23 +117,15 @@ describe('ImageDiff', () => {
it('should register click event delegation to js-diff-notes-toggle', () => {
element.querySelector('.js-diff-notes-toggle').click();
+
expect(imageDiffHelper.toggleCollapsed).toHaveBeenCalled();
});
it('should register click event delegation to comment-indicator', () => {
element.querySelector('.comment-indicator').click();
- expect(imageDiffHelper.commentIndicatorOnClick).toHaveBeenCalled();
- });
- });
- describe('image loaded', () => {
- beforeEach(() => {
- spyOn(imageUtility, 'isImageLoaded').and.returnValue(true);
- imageDiff = new ImageDiff(element);
- imageDiff.imageEl = imageEl;
+ expect(imageDiffHelper.commentIndicatorOnClick).toHaveBeenCalled();
});
-
- it('should renderBadges', () => {});
});
describe('image not loaded', () => {
@@ -147,6 +139,7 @@ describe('ImageDiff', () => {
it('should registers load eventListener', () => {
const loadEvent = new Event('load');
imageEl.dispatchEvent(loadEvent);
+
expect(imageDiff.renderBadges).toHaveBeenCalled();
});
});
@@ -164,24 +157,28 @@ describe('ImageDiff', () => {
it('should register click.imageDiff event', () => {
const event = new CustomEvent('click.imageDiff');
element.dispatchEvent(event);
+
expect(imageDiff.imageClicked).toHaveBeenCalled();
});
it('should register blur.imageDiff event', () => {
const event = new CustomEvent('blur.imageDiff');
element.dispatchEvent(event);
+
expect(imageDiffHelper.removeCommentIndicator).toHaveBeenCalled();
});
it('should register addBadge.imageDiff event', () => {
const event = new CustomEvent('addBadge.imageDiff');
element.dispatchEvent(event);
+
expect(imageDiff.addBadge).toHaveBeenCalled();
});
it('should register removeBadge.imageDiff event', () => {
const event = new CustomEvent('removeBadge.imageDiff');
element.dispatchEvent(event);
+
expect(imageDiff.removeBadge).toHaveBeenCalled();
});
});
@@ -197,6 +194,7 @@ describe('ImageDiff', () => {
it('should not register click.imageDiff event', () => {
const event = new CustomEvent('click.imageDiff');
element.dispatchEvent(event);
+
expect(imageDiff.imageClicked).not.toHaveBeenCalled();
});
});
@@ -240,6 +238,7 @@ describe('ImageDiff', () => {
it('should call renderBadge for each discussionEl', () => {
const discussionEls = element.querySelectorAll('.note-container .discussion-notes .notes');
+
expect(imageDiff.renderBadge.calls.count()).toEqual(discussionEls.length);
});
});
@@ -336,6 +335,7 @@ describe('ImageDiff', () => {
describe('cascade badge count', () => {
it('should update next imageBadgeEl value', () => {
const imageBadgeEls = imageDiff.imageFrameEl.querySelectorAll('.badge');
+
expect(imageBadgeEls[0].innerText).toEqual('1');
expect(imageBadgeEls[1].innerText).toEqual('2');
expect(imageBadgeEls.length).toEqual(2);
diff --git a/spec/javascripts/image_diff/init_discussion_tab_spec.js b/spec/javascripts/image_diff/init_discussion_tab_spec.js
index 7c447d6f70d..7cacc45ab62 100644
--- a/spec/javascripts/image_diff/init_discussion_tab_spec.js
+++ b/spec/javascripts/image_diff/init_discussion_tab_spec.js
@@ -11,7 +11,7 @@ describe('initDiscussionTab', () => {
`);
});
- it('should pass canCreateNote as false to initImageDiff', (done) => {
+ it('should pass canCreateNote as false to initImageDiff', done => {
spyOn(imageDiffHelper, 'initImageDiff').and.callFake((diffFileEl, canCreateNote) => {
expect(canCreateNote).toEqual(false);
done();
@@ -20,11 +20,13 @@ describe('initDiscussionTab', () => {
initDiscussionTab();
});
- it('should pass renderCommentBadge as true to initImageDiff', (done) => {
- spyOn(imageDiffHelper, 'initImageDiff').and.callFake((diffFileEl, canCreateNote, renderCommentBadge) => {
- expect(renderCommentBadge).toEqual(true);
- done();
- });
+ it('should pass renderCommentBadge as true to initImageDiff', done => {
+ spyOn(imageDiffHelper, 'initImageDiff').and.callFake(
+ (diffFileEl, canCreateNote, renderCommentBadge) => {
+ expect(renderCommentBadge).toEqual(true);
+ done();
+ },
+ );
initDiscussionTab();
});
@@ -32,6 +34,7 @@ describe('initDiscussionTab', () => {
it('should call initImageDiff for each diffFileEls', () => {
spyOn(imageDiffHelper, 'initImageDiff').and.callFake(() => {});
initDiscussionTab();
+
expect(imageDiffHelper.initImageDiff.calls.count()).toEqual(2);
});
});
diff --git a/spec/javascripts/image_diff/replaced_image_diff_spec.js b/spec/javascripts/image_diff/replaced_image_diff_spec.js
index 5f8cd7c531a..62e7c8b6c6a 100644
--- a/spec/javascripts/image_diff/replaced_image_diff_spec.js
+++ b/spec/javascripts/image_diff/replaced_image_diff_spec.js
@@ -37,16 +37,28 @@ describe('ReplacedImageDiff', () => {
function setupImageFrameEls() {
replacedImageDiff.imageFrameEls = [];
- replacedImageDiff.imageFrameEls[viewTypes.TWO_UP] = element.querySelector('.two-up .js-image-frame');
- replacedImageDiff.imageFrameEls[viewTypes.SWIPE] = element.querySelector('.swipe .js-image-frame');
- replacedImageDiff.imageFrameEls[viewTypes.ONION_SKIN] = element.querySelector('.onion-skin .js-image-frame');
+ replacedImageDiff.imageFrameEls[viewTypes.TWO_UP] = element.querySelector(
+ '.two-up .js-image-frame',
+ );
+ replacedImageDiff.imageFrameEls[viewTypes.SWIPE] = element.querySelector(
+ '.swipe .js-image-frame',
+ );
+ replacedImageDiff.imageFrameEls[viewTypes.ONION_SKIN] = element.querySelector(
+ '.onion-skin .js-image-frame',
+ );
}
function setupViewModesEls() {
replacedImageDiff.viewModesEls = [];
- replacedImageDiff.viewModesEls[viewTypes.TWO_UP] = element.querySelector('.view-modes-menu .two-up');
- replacedImageDiff.viewModesEls[viewTypes.SWIPE] = element.querySelector('.view-modes-menu .swipe');
- replacedImageDiff.viewModesEls[viewTypes.ONION_SKIN] = element.querySelector('.view-modes-menu .onion-skin');
+ replacedImageDiff.viewModesEls[viewTypes.TWO_UP] = element.querySelector(
+ '.view-modes-menu .two-up',
+ );
+ replacedImageDiff.viewModesEls[viewTypes.SWIPE] = element.querySelector(
+ '.view-modes-menu .swipe',
+ );
+ replacedImageDiff.viewModesEls[viewTypes.ONION_SKIN] = element.querySelector(
+ '.view-modes-menu .onion-skin',
+ );
}
function setupImageEls() {
@@ -58,6 +70,7 @@ describe('ReplacedImageDiff', () => {
it('should extend ImageDiff', () => {
replacedImageDiff = new ReplacedImageDiff(element);
+
expect(replacedImageDiff instanceof ImageDiff).toEqual(true);
});
@@ -72,18 +85,36 @@ describe('ReplacedImageDiff', () => {
it('should set imageFrameEls', () => {
const { imageFrameEls } = replacedImageDiff;
+
expect(imageFrameEls).toBeDefined();
- expect(imageFrameEls[viewTypes.TWO_UP]).toEqual(element.querySelector('.two-up .js-image-frame'));
- expect(imageFrameEls[viewTypes.SWIPE]).toEqual(element.querySelector('.swipe .js-image-frame'));
- expect(imageFrameEls[viewTypes.ONION_SKIN]).toEqual(element.querySelector('.onion-skin .js-image-frame'));
+ expect(imageFrameEls[viewTypes.TWO_UP]).toEqual(
+ element.querySelector('.two-up .js-image-frame'),
+ );
+
+ expect(imageFrameEls[viewTypes.SWIPE]).toEqual(
+ element.querySelector('.swipe .js-image-frame'),
+ );
+
+ expect(imageFrameEls[viewTypes.ONION_SKIN]).toEqual(
+ element.querySelector('.onion-skin .js-image-frame'),
+ );
});
it('should set viewModesEls', () => {
const { viewModesEls } = replacedImageDiff;
+
expect(viewModesEls).toBeDefined();
- expect(viewModesEls[viewTypes.TWO_UP]).toEqual(element.querySelector('.view-modes-menu .two-up'));
- expect(viewModesEls[viewTypes.SWIPE]).toEqual(element.querySelector('.view-modes-menu .swipe'));
- expect(viewModesEls[viewTypes.ONION_SKIN]).toEqual(element.querySelector('.view-modes-menu .onion-skin'));
+ expect(viewModesEls[viewTypes.TWO_UP]).toEqual(
+ element.querySelector('.view-modes-menu .two-up'),
+ );
+
+ expect(viewModesEls[viewTypes.SWIPE]).toEqual(
+ element.querySelector('.view-modes-menu .swipe'),
+ );
+
+ expect(viewModesEls[viewTypes.ONION_SKIN]).toEqual(
+ element.querySelector('.view-modes-menu .onion-skin'),
+ );
});
it('should generateImageEls', () => {
@@ -97,6 +128,7 @@ describe('ReplacedImageDiff', () => {
describe('currentView', () => {
it('should set currentView', () => {
replacedImageDiff.init(viewTypes.ONION_SKIN);
+
expect(replacedImageDiff.currentView).toEqual(viewTypes.ONION_SKIN);
});
@@ -121,6 +153,7 @@ describe('ReplacedImageDiff', () => {
it('should set imageEls', () => {
replacedImageDiff.generateImageEls();
const { imageEls } = replacedImageDiff;
+
expect(imageEls).toBeDefined();
expect(imageEls[viewTypes.TWO_UP]).toEqual(element.querySelector('.two-up img'));
expect(imageEls[viewTypes.SWIPE]).toEqual(element.querySelector('.swipe img'));
@@ -138,11 +171,12 @@ describe('ReplacedImageDiff', () => {
it('should call super.bindEvents', () => {
replacedImageDiff.bindEvents();
+
expect(ImageDiff.prototype.bindEvents).toHaveBeenCalled();
});
- it('should register click eventlistener to 2-up view mode', (done) => {
- spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake((viewMode) => {
+ it('should register click eventlistener to 2-up view mode', done => {
+ spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => {
expect(viewMode).toEqual(viewTypes.TWO_UP);
done();
});
@@ -151,8 +185,8 @@ describe('ReplacedImageDiff', () => {
replacedImageDiff.viewModesEls[viewTypes.TWO_UP].click();
});
- it('should register click eventlistener to swipe view mode', (done) => {
- spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake((viewMode) => {
+ it('should register click eventlistener to swipe view mode', done => {
+ spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => {
expect(viewMode).toEqual(viewTypes.SWIPE);
done();
});
@@ -161,8 +195,8 @@ describe('ReplacedImageDiff', () => {
replacedImageDiff.viewModesEls[viewTypes.SWIPE].click();
});
- it('should register click eventlistener to onion skin view mode', (done) => {
- spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake((viewMode) => {
+ it('should register click eventlistener to onion skin view mode', done => {
+ spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => {
expect(viewMode).toEqual(viewTypes.SWIPE);
done();
});
@@ -184,6 +218,7 @@ describe('ReplacedImageDiff', () => {
expect(replacedImageDiff.imageEl).toEqual(element.querySelector('.two-up img'));
replacedImageDiff.currentView = viewTypes.SWIPE;
+
expect(replacedImageDiff.imageEl).toEqual(element.querySelector('.swipe img'));
});
});
@@ -196,10 +231,15 @@ describe('ReplacedImageDiff', () => {
});
it('should return imageFrameEl based on currentView', () => {
- expect(replacedImageDiff.imageFrameEl).toEqual(element.querySelector('.two-up .js-image-frame'));
+ expect(replacedImageDiff.imageFrameEl).toEqual(
+ element.querySelector('.two-up .js-image-frame'),
+ );
replacedImageDiff.currentView = viewTypes.ONION_SKIN;
- expect(replacedImageDiff.imageFrameEl).toEqual(element.querySelector('.onion-skin .js-image-frame'));
+
+ expect(replacedImageDiff.imageFrameEl).toEqual(
+ element.querySelector('.onion-skin .js-image-frame'),
+ );
});
});
});
@@ -248,6 +288,7 @@ describe('ReplacedImageDiff', () => {
it('should call renderNewView', () => {
jasmine.clock().tick(251);
+
expect(replacedImageDiff.renderNewView).toHaveBeenCalled();
});
});
@@ -284,7 +325,7 @@ describe('ReplacedImageDiff', () => {
setupImageFrameEls();
});
- it('should pass showCommentIndicator normalized indicator values', (done) => {
+ it('should pass showCommentIndicator normalized indicator values', done => {
spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake(() => {});
spyOn(imageDiffHelper, 'resizeCoordinatesToImageElement').and.callFake((imageEl, meta) => {
expect(meta.x).toEqual(indicator.x);
@@ -296,15 +337,17 @@ describe('ReplacedImageDiff', () => {
replacedImageDiff.renderNewView(indicator);
});
- it('should call showCommentIndicator', (done) => {
+ it('should call showCommentIndicator', done => {
const normalized = {
normalized: true,
};
spyOn(imageDiffHelper, 'resizeCoordinatesToImageElement').and.returnValue(normalized);
- spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake((imageFrameEl, normalizedIndicator) => {
- expect(normalizedIndicator).toEqual(normalized);
- done();
- });
+ spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake(
+ (imageFrameEl, normalizedIndicator) => {
+ expect(normalizedIndicator).toEqual(normalized);
+ done();
+ },
+ );
replacedImageDiff.renderNewView(indicator);
});
});
diff --git a/spec/javascripts/importer_status_spec.js b/spec/javascripts/importer_status_spec.js
index 63cdb3d5114..e7f195ed57c 100644
--- a/spec/javascripts/importer_status_spec.js
+++ b/spec/javascripts/importer_status_spec.js
@@ -35,38 +35,43 @@ describe('Importer Status', () => {
});
});
- it('sets table row to active after post request', (done) => {
+ it('sets table row to active after post request', done => {
mock.onPost(importUrl).reply(200, {
id: 1,
full_path: '/full_path',
});
- instance.addToImport({
- currentTarget: document.querySelector('.js-add-to-import'),
- })
- .then(() => {
- expect(document.querySelector('tr').classList.contains('table-active')).toEqual(true);
- done();
- })
- .catch(done.fail);
+ instance
+ .addToImport({
+ currentTarget: document.querySelector('.js-add-to-import'),
+ })
+ .then(() => {
+ expect(document.querySelector('tr').classList.contains('table-active')).toEqual(true);
+ done();
+ })
+ .catch(done.fail);
});
- it('shows error message after failed POST request', (done) => {
+ it('shows error message after failed POST request', done => {
appendSetFixtures('<div class="flash-container"></div>');
mock.onPost(importUrl).reply(422, {
errors: 'You forgot your lunch',
});
- instance.addToImport({
- currentTarget: document.querySelector('.js-add-to-import'),
- })
- .then(() => {
- const flashMessage = document.querySelector('.flash-text');
- expect(flashMessage.textContent.trim()).toEqual('An error occurred while importing project: You forgot your lunch');
- done();
- })
- .catch(done.fail);
+ instance
+ .addToImport({
+ currentTarget: document.querySelector('.js-add-to-import'),
+ })
+ .then(() => {
+ const flashMessage = document.querySelector('.flash-text');
+
+ expect(flashMessage.textContent.trim()).toEqual(
+ 'An error occurred while importing project: You forgot your lunch',
+ );
+ done();
+ })
+ .catch(done.fail);
});
});
@@ -92,14 +97,17 @@ describe('Importer Status', () => {
});
function setupMock(importStatus) {
- mock.onGet(jobsUrl).reply(200, [{
- id: 1,
- import_status: importStatus,
- }]);
+ mock.onGet(jobsUrl).reply(200, [
+ {
+ id: 1,
+ import_status: importStatus,
+ },
+ ]);
}
function expectJobStatus(done, status) {
- instance.autoUpdate()
+ instance
+ .autoUpdate()
.then(() => {
expect(document.querySelector('#project_1').innerText.trim()).toEqual(status);
done();
@@ -107,22 +115,22 @@ describe('Importer Status', () => {
.catch(done.fail);
}
- it('sets the job status to done', (done) => {
+ it('sets the job status to done', done => {
setupMock('finished');
expectJobStatus(done, 'Done');
});
- it('sets the job status to scheduled', (done) => {
+ it('sets the job status to scheduled', done => {
setupMock('scheduled');
expectJobStatus(done, 'Scheduled');
});
- it('sets the job status to started', (done) => {
+ it('sets the job status to started', done => {
setupMock('started');
expectJobStatus(done, 'Started');
});
- it('sets the job status to custom status', (done) => {
+ it('sets the job status to custom status', done => {
setupMock('custom status');
expectJobStatus(done, 'custom status');
});
diff --git a/spec/javascripts/integrations/integration_settings_form_spec.js b/spec/javascripts/integrations/integration_settings_form_spec.js
index e07343810d2..4f4c9a7b463 100644
--- a/spec/javascripts/integrations/integration_settings_form_spec.js
+++ b/spec/javascripts/integrations/integration_settings_form_spec.js
@@ -68,21 +68,27 @@ describe('IntegrationSettingsForm', () => {
integrationSettingsForm.canTestService = true;
integrationSettingsForm.toggleSubmitBtnLabel(true);
- expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Test settings and save changes');
+
+ expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual(
+ 'Test settings and save changes',
+ );
});
it('should set Save button label to "Save changes" when either serviceActive or canTestService (or both) is `false`', () => {
integrationSettingsForm.canTestService = false;
integrationSettingsForm.toggleSubmitBtnLabel(false);
+
expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');
integrationSettingsForm.toggleSubmitBtnLabel(true);
+
expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');
integrationSettingsForm.canTestService = true;
integrationSettingsForm.toggleSubmitBtnLabel(false);
+
expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');
});
});
@@ -127,8 +133,9 @@ describe('IntegrationSettingsForm', () => {
mock.restore();
});
- it('should make an ajax request with provided `formData`', (done) => {
- integrationSettingsForm.testSettings(formData)
+ it('should make an ajax request with provided `formData`', done => {
+ integrationSettingsForm
+ .testSettings(formData)
.then(() => {
expect(axios.put).toHaveBeenCalledWith(integrationSettingsForm.testEndPoint, formData);
@@ -137,7 +144,7 @@ describe('IntegrationSettingsForm', () => {
.catch(done.fail);
});
- it('should show error Flash with `Save anyway` action if ajax request responds with error in test', (done) => {
+ it('should show error Flash with `Save anyway` action if ajax request responds with error in test', done => {
const errorMessage = 'Test failed.';
mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
error: true,
@@ -146,19 +153,32 @@ describe('IntegrationSettingsForm', () => {
test_failed: true,
});
- integrationSettingsForm.testSettings(formData)
+ integrationSettingsForm
+ .testSettings(formData)
.then(() => {
const $flashContainer = $('.flash-container');
- expect($flashContainer.find('.flash-text').text().trim()).toEqual('Test failed. some error');
+
+ expect(
+ $flashContainer
+ .find('.flash-text')
+ .text()
+ .trim(),
+ ).toEqual('Test failed. some error');
+
expect($flashContainer.find('.flash-action')).toBeDefined();
- expect($flashContainer.find('.flash-action').text().trim()).toEqual('Save anyway');
+ expect(
+ $flashContainer
+ .find('.flash-action')
+ .text()
+ .trim(),
+ ).toEqual('Save anyway');
done();
})
.catch(done.fail);
});
- it('should not show error Flash with `Save anyway` action if ajax request responds with error in validation', (done) => {
+ it('should not show error Flash with `Save anyway` action if ajax request responds with error in validation', done => {
const errorMessage = 'Validations failed.';
mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
error: true,
@@ -167,26 +187,40 @@ describe('IntegrationSettingsForm', () => {
test_failed: false,
});
- integrationSettingsForm.testSettings(formData)
+ integrationSettingsForm
+ .testSettings(formData)
.then(() => {
const $flashContainer = $('.flash-container');
- expect($flashContainer.find('.flash-text').text().trim()).toEqual('Validations failed. some error');
+
+ expect(
+ $flashContainer
+ .find('.flash-text')
+ .text()
+ .trim(),
+ ).toEqual('Validations failed. some error');
+
expect($flashContainer.find('.flash-action')).toBeDefined();
- expect($flashContainer.find('.flash-action').text().trim()).toEqual('');
+ expect(
+ $flashContainer
+ .find('.flash-action')
+ .text()
+ .trim(),
+ ).toEqual('');
done();
})
.catch(done.fail);
});
- it('should submit form if ajax request responds without any error in test', (done) => {
+ it('should submit form if ajax request responds without any error in test', done => {
spyOn(integrationSettingsForm.$form, 'submit');
mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
error: false,
});
- integrationSettingsForm.testSettings(formData)
+ integrationSettingsForm
+ .testSettings(formData)
.then(() => {
expect(integrationSettingsForm.$form.submit).toHaveBeenCalled();
@@ -195,7 +229,7 @@ describe('IntegrationSettingsForm', () => {
.catch(done.fail);
});
- it('should submit form when clicked on `Save anyway` action of error Flash', (done) => {
+ it('should submit form when clicked on `Save anyway` action of error Flash', done => {
spyOn(integrationSettingsForm.$form, 'submit');
const errorMessage = 'Test failed.';
@@ -205,9 +239,11 @@ describe('IntegrationSettingsForm', () => {
test_failed: true,
});
- integrationSettingsForm.testSettings(formData)
+ integrationSettingsForm
+ .testSettings(formData)
.then(() => {
const $flashAction = $('.flash-container .flash-action');
+
expect($flashAction).toBeDefined();
$flashAction.get(0).click();
@@ -220,26 +256,32 @@ describe('IntegrationSettingsForm', () => {
.catch(done.fail);
});
- it('should show error Flash if ajax request failed', (done) => {
+ it('should show error Flash if ajax request failed', done => {
const errorMessage = 'Something went wrong on our end.';
mock.onPut(integrationSettingsForm.testEndPoint).networkError();
- integrationSettingsForm.testSettings(formData)
+ integrationSettingsForm
+ .testSettings(formData)
.then(() => {
- expect($('.flash-container .flash-text').text().trim()).toEqual(errorMessage);
+ expect(
+ $('.flash-container .flash-text')
+ .text()
+ .trim(),
+ ).toEqual(errorMessage);
done();
})
.catch(done.fail);
});
- it('should always call `toggleSubmitBtnState` with `false` once request is completed', (done) => {
+ it('should always call `toggleSubmitBtnState` with `false` once request is completed', done => {
mock.onPut(integrationSettingsForm.testEndPoint).networkError();
spyOn(integrationSettingsForm, 'toggleSubmitBtnState');
- integrationSettingsForm.testSettings(formData)
+ integrationSettingsForm
+ .testSettings(formData)
.then(() => {
expect(integrationSettingsForm.toggleSubmitBtnState).toHaveBeenCalledWith(false);
diff --git a/spec/javascripts/issuable_spec.js b/spec/javascripts/issuable_spec.js
index 57bf746f080..25543053eba 100644
--- a/spec/javascripts/issuable_spec.js
+++ b/spec/javascripts/issuable_spec.js
@@ -8,6 +8,7 @@ describe('Issuable', () => {
describe('initBulkUpdate', () => {
it('should not set bulkUpdateSidebar', () => {
Issuable = new IssuableIndex('issue_');
+
expect(Issuable.bulkUpdateSidebar).not.toBeDefined();
});
@@ -17,6 +18,7 @@ describe('Issuable', () => {
document.body.appendChild(element);
Issuable = new IssuableIndex('issue_');
+
expect(Issuable.bulkUpdateSidebar).toBeDefined();
});
});
@@ -47,7 +49,7 @@ describe('Issuable', () => {
mock.restore();
});
- it('should send request to reset email token', (done) => {
+ it('should send request to reset email token', done => {
spyOn(axios, 'put').and.callThrough();
document.querySelector('.incoming-email-token-reset').click();
@@ -60,4 +62,3 @@ describe('Issuable', () => {
});
});
});
-
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 36328382448..2bd1b3996dc 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -11,10 +11,7 @@ function formatText(text) {
return text.trim().replace(/\s\s+/g, ' ');
}
-const REALTIME_REQUEST_STACK = [
- issueShowData.initialRequest,
- issueShowData.secondRequest,
-];
+const REALTIME_REQUEST_STACK = [issueShowData.initialRequest, issueShowData.secondRequest];
describe('Issuable output', () => {
let mock;
@@ -23,7 +20,7 @@ describe('Issuable output', () => {
document.body.innerHTML = '<span id="task_status"></span>';
- beforeEach((done) => {
+ beforeEach(done => {
spyOn(eventHub, '$emit');
const IssuableDescriptionComponent = Vue.extend(issuableApp);
@@ -64,65 +61,67 @@ describe('Issuable output', () => {
vm.$destroy();
});
- it('should render a title/description/edited and update title/description/edited on update', (done) => {
+ it('should render a title/description/edited and update title/description/edited on update', done => {
let editedText;
Vue.nextTick()
- .then(() => {
- editedText = vm.$el.querySelector('.edited-text');
- })
- .then(() => {
- expect(document.querySelector('title').innerText).toContain('this is a title (#1)');
- expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>this is a title</p>');
- expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>');
- expect(vm.$el.querySelector('.js-task-list-field').value).toContain('this is a description');
- expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
- expect(editedText.querySelector('.author-link').href).toMatch(/\/some_user$/);
- expect(editedText.querySelector('time')).toBeTruthy();
- })
- .then(() => {
- vm.poll.makeRequest();
- })
- .then(() => new Promise(resolve => setTimeout(resolve)))
- .then(() => {
- expect(document.querySelector('title').innerText).toContain('2 (#1)');
- expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
- expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>');
- expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42');
- expect(vm.$el.querySelector('.edited-text')).toBeTruthy();
- expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/);
- expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/);
- expect(editedText.querySelector('time')).toBeTruthy();
- })
- .then(done)
- .catch(done.fail);
+ .then(() => {
+ editedText = vm.$el.querySelector('.edited-text');
+ })
+ .then(() => {
+ expect(document.querySelector('title').innerText).toContain('this is a title (#1)');
+ expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>this is a title</p>');
+ expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>');
+ expect(vm.$el.querySelector('.js-task-list-field').value).toContain(
+ 'this is a description',
+ );
+
+ expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
+ expect(editedText.querySelector('.author-link').href).toMatch(/\/some_user$/);
+ expect(editedText.querySelector('time')).toBeTruthy();
+ })
+ .then(() => {
+ vm.poll.makeRequest();
+ })
+ .then(() => new Promise(resolve => setTimeout(resolve)))
+ .then(() => {
+ expect(document.querySelector('title').innerText).toContain('2 (#1)');
+ expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
+ expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>');
+ expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42');
+ expect(vm.$el.querySelector('.edited-text')).toBeTruthy();
+ expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(
+ /Edited[\s\S]+?by Other User/,
+ );
+
+ expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/);
+ expect(editedText.querySelector('time')).toBeTruthy();
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('shows actions if permissions are correct', (done) => {
+ it('shows actions if permissions are correct', done => {
vm.showForm = true;
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.btn'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('.btn')).not.toBeNull();
done();
});
});
- it('does not show actions if permissions are incorrect', (done) => {
+ it('does not show actions if permissions are incorrect', done => {
vm.showForm = true;
vm.canUpdate = false;
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.btn'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.btn')).toBeNull();
done();
});
});
- it('does not update formState if form is already open', (done) => {
+ it('does not update formState if form is already open', done => {
vm.openForm();
vm.state.titleText = 'testing 123';
@@ -130,25 +129,26 @@ describe('Issuable output', () => {
vm.openForm();
Vue.nextTick(() => {
- expect(
- vm.store.formState.title,
- ).not.toBe('testing 123');
+ expect(vm.store.formState.title).not.toBe('testing 123');
done();
});
});
describe('updateIssuable', () => {
- it('fetches new data after update', (done) => {
+ it('fetches new data after update', done => {
spyOn(vm.service, 'getData').and.callThrough();
- spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
- resolve({
- data: {
- confidential: false,
- web_url: window.location.pathname,
- },
- });
- }));
+ spyOn(vm.service, 'updateIssuable').and.callFake(
+ () =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ confidential: false,
+ web_url: window.location.pathname,
+ },
+ });
+ }),
+ );
vm.updateIssuable()
.then(() => {
@@ -158,10 +158,13 @@ describe('Issuable output', () => {
.catch(done.fail);
});
- it('correctly updates issuable data', (done) => {
- spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
- resolve();
- }));
+ it('correctly updates issuable data', done => {
+ spyOn(vm.service, 'updateIssuable').and.callFake(
+ () =>
+ new Promise(resolve => {
+ resolve();
+ }),
+ );
vm.updateIssuable()
.then(() => {
@@ -172,16 +175,19 @@ describe('Issuable output', () => {
.catch(done.fail);
});
- it('does not redirect if issue has not moved', (done) => {
+ it('does not redirect if issue has not moved', done => {
const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
- spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
- resolve({
- data: {
- web_url: window.location.pathname,
- confidential: vm.isConfidential,
- },
- });
- }));
+ spyOn(vm.service, 'updateIssuable').and.callFake(
+ () =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ web_url: window.location.pathname,
+ confidential: vm.isConfidential,
+ },
+ });
+ }),
+ );
vm.updateIssuable();
@@ -191,16 +197,19 @@ describe('Issuable output', () => {
});
});
- it('redirects if returned web_url has changed', (done) => {
+ it('redirects if returned web_url has changed', done => {
const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
- spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
- resolve({
- data: {
- web_url: '/testing-issue-move',
- confidential: vm.isConfidential,
- },
- });
- }));
+ spyOn(vm.service, 'updateIssuable').and.callFake(
+ () =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ web_url: '/testing-issue-move',
+ confidential: vm.isConfidential,
+ },
+ });
+ }),
+ );
vm.updateIssuable();
@@ -211,7 +220,7 @@ describe('Issuable output', () => {
});
describe('shows dialog when issue has unsaved changed', () => {
- it('confirms on title change', (done) => {
+ it('confirms on title change', done => {
vm.showForm = true;
vm.state.titleText = 'title has changed';
const e = { returnValue: null };
@@ -222,7 +231,7 @@ describe('Issuable output', () => {
});
});
- it('confirms on description change', (done) => {
+ it('confirms on description change', done => {
vm.showForm = true;
vm.state.descriptionText = 'description has changed';
const e = { returnValue: null };
@@ -233,7 +242,7 @@ describe('Issuable output', () => {
});
});
- it('does nothing when nothing has changed', (done) => {
+ it('does nothing when nothing has changed', done => {
const e = { returnValue: null };
vm.handleBeforeUnloadEvent(e);
Vue.nextTick(() => {
@@ -246,39 +255,36 @@ describe('Issuable output', () => {
describe('error when updating', () => {
beforeEach(() => {
spyOn(window, 'Flash').and.callThrough();
- spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve, reject) => {
- reject();
- }));
+ spyOn(vm.service, 'updateIssuable').and.callFake(
+ () =>
+ new Promise((resolve, reject) => {
+ reject();
+ }),
+ );
});
- it('closes form on error', (done) => {
+ it('closes form on error', done => {
vm.updateIssuable();
setTimeout(() => {
- expect(
- eventHub.$emit,
- ).toHaveBeenCalledWith('close.form');
- expect(
- window.Flash,
- ).toHaveBeenCalledWith('Error updating issue');
+ expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
+
+ expect(window.Flash).toHaveBeenCalledWith('Error updating issue');
done();
});
});
- it('returns the correct error message for issuableType', (done) => {
+ it('returns the correct error message for issuableType', done => {
vm.issuableType = 'merge request';
Vue.nextTick(() => {
vm.updateIssuable();
setTimeout(() => {
- expect(
- eventHub.$emit,
- ).toHaveBeenCalledWith('close.form');
- expect(
- window.Flash,
- ).toHaveBeenCalledWith('Error updating merge request');
+ expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
+
+ expect(window.Flash).toHaveBeenCalledWith('Error updating merge request');
done();
});
@@ -287,16 +293,18 @@ describe('Issuable output', () => {
});
});
- it('opens recaptcha modal if update rejected as spam', (done) => {
+ it('opens recaptcha modal if update rejected as spam', done => {
function mockScriptSrc() {
- const recaptchaChild = vm.$children
- .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle
+ const recaptchaChild = vm.$children.find(
+ // eslint-disable-next-line no-underscore-dangle
+ child => child.$options._componentTag === 'recaptcha-modal',
+ );
recaptchaChild.scriptSrc = '//scriptsrc';
}
let modal;
- const promise = new Promise((resolve) => {
+ const promise = new Promise(resolve => {
resolve({
data: {
recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
@@ -332,15 +340,18 @@ describe('Issuable output', () => {
});
describe('deleteIssuable', () => {
- it('changes URL when deleted', (done) => {
+ it('changes URL when deleted', done => {
const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
- spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => {
- resolve({
- data: {
- web_url: '/test',
- },
- });
- }));
+ spyOn(vm.service, 'deleteIssuable').and.callFake(
+ () =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ web_url: '/test',
+ },
+ });
+ }),
+ );
vm.deleteIssuable();
@@ -350,42 +361,43 @@ describe('Issuable output', () => {
});
});
- it('stops polling when deleting', (done) => {
+ it('stops polling when deleting', done => {
spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.poll, 'stop').and.callThrough();
- spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => {
- resolve({
- data: {
- web_url: '/test',
- },
- });
- }));
+ spyOn(vm.service, 'deleteIssuable').and.callFake(
+ () =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ web_url: '/test',
+ },
+ });
+ }),
+ );
vm.deleteIssuable();
setTimeout(() => {
- expect(
- vm.poll.stop,
- ).toHaveBeenCalledWith();
+ expect(vm.poll.stop).toHaveBeenCalledWith();
done();
});
});
- it('closes form on error', (done) => {
+ it('closes form on error', done => {
spyOn(window, 'Flash').and.callThrough();
- spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve, reject) => {
- reject();
- }));
+ spyOn(vm.service, 'deleteIssuable').and.callFake(
+ () =>
+ new Promise((resolve, reject) => {
+ reject();
+ }),
+ );
vm.deleteIssuable();
setTimeout(() => {
- expect(
- eventHub.$emit,
- ).toHaveBeenCalledWith('close.form');
- expect(
- window.Flash,
- ).toHaveBeenCalledWith('Error deleting issue');
+ expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
+
+ expect(window.Flash).toHaveBeenCalledWith('Error deleting issue');
done();
});
@@ -393,7 +405,7 @@ describe('Issuable output', () => {
});
describe('open form', () => {
- it('shows locked warning if form is open & data is different', (done) => {
+ it('shows locked warning if form is open & data is different', done => {
vm.$nextTick()
.then(() => {
vm.openForm();
@@ -422,6 +434,7 @@ describe('Issuable output', () => {
it('should render if showInlineEditButton', () => {
vm.showInlineEditButton = true;
+
expect(vm.$el.querySelector('.title-container .note-action-button')).toBeDefined();
});
});
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index 889c8545faa..463f3c89926 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -33,7 +33,7 @@ describe('Description component', () => {
vm.$destroy();
});
- it('animates description changes', (done) => {
+ it('animates description changes', done => {
vm.descriptionHtml = 'changed';
Vue.nextTick(() => {
@@ -51,10 +51,12 @@ describe('Description component', () => {
});
});
- it('opens recaptcha dialog if update rejected as spam', (done) => {
+ it('opens recaptcha dialog if update rejected as spam', done => {
let modal;
- const recaptchaChild = vm.$children
- .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle
+ const recaptchaChild = vm.$children.find(
+ // eslint-disable-next-line no-underscore-dangle
+ child => child.$options._componentTag === 'recaptcha-modal',
+ );
recaptchaChild.scriptSrc = '//scriptsrc';
@@ -84,13 +86,16 @@ describe('Description component', () => {
let TaskList;
beforeEach(() => {
- vm = mountComponent(DescriptionComponent, Object.assign({}, props, {
- issuableType: 'issuableType',
- }));
+ vm = mountComponent(
+ DescriptionComponent,
+ Object.assign({}, props, {
+ issuableType: 'issuableType',
+ }),
+ );
TaskList = spyOnDependency(Description, 'TaskList');
});
- it('re-inits the TaskList when description changed', (done) => {
+ it('re-inits the TaskList when description changed', done => {
vm.descriptionHtml = 'changed';
setTimeout(() => {
@@ -99,7 +104,7 @@ describe('Description component', () => {
});
});
- it('does not re-init the TaskList when canUpdate is false', (done) => {
+ it('does not re-init the TaskList when canUpdate is false', done => {
vm.canUpdate = false;
vm.descriptionHtml = 'changed';
@@ -109,7 +114,7 @@ describe('Description component', () => {
});
});
- it('calls with issuableType dataType', (done) => {
+ it('calls with issuableType dataType', done => {
vm.descriptionHtml = 'changed';
setTimeout(() => {
@@ -125,44 +130,42 @@ describe('Description component', () => {
});
describe('taskStatus', () => {
- it('adds full taskStatus', (done) => {
+ it('adds full taskStatus', done => {
vm.taskStatus = '1 of 1';
setTimeout(() => {
- expect(
- document.querySelector('.issuable-meta #task_status').textContent.trim(),
- ).toBe('1 of 1');
+ expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe(
+ '1 of 1',
+ );
done();
});
});
- it('adds short taskStatus', (done) => {
+ it('adds short taskStatus', done => {
vm.taskStatus = '1 of 1';
setTimeout(() => {
- expect(
- document.querySelector('.issuable-meta #task_status_short').textContent.trim(),
- ).toBe('1/1 task');
+ expect(document.querySelector('.issuable-meta #task_status_short').textContent.trim()).toBe(
+ '1/1 task',
+ );
done();
});
});
- it('clears task status text when no tasks are present', (done) => {
+ it('clears task status text when no tasks are present', done => {
vm.taskStatus = '0 of 0';
setTimeout(() => {
- expect(
- document.querySelector('.issuable-meta #task_status').textContent.trim(),
- ).toBe('');
+ expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe('');
done();
});
});
});
- it('applies syntax highlighting and math when description changed', (done) => {
+ it('applies syntax highlighting and math when description changed', done => {
spyOn(vm, 'renderGFM').and.callThrough();
spyOn($.prototype, 'renderGFM').and.callThrough();
vm.descriptionHtml = 'changed';
diff --git a/spec/javascripts/issue_show/components/edit_actions_spec.js b/spec/javascripts/issue_show/components/edit_actions_spec.js
index d779ab7bb31..d92c54ea83f 100644
--- a/spec/javascripts/issue_show/components/edit_actions_spec.js
+++ b/spec/javascripts/issue_show/components/edit_actions_spec.js
@@ -6,7 +6,7 @@ import Store from '~/issue_show/stores';
describe('Edit Actions components', () => {
let vm;
- beforeEach((done) => {
+ beforeEach(done => {
const Component = Vue.extend(editActions);
const store = new Store({
titleHtml: '',
@@ -21,6 +21,7 @@ describe('Edit Actions components', () => {
propsData: {
canDestroy: true,
formState: store.formState,
+ issuableType: 'issue',
},
}).$mount();
@@ -28,40 +29,32 @@ describe('Edit Actions components', () => {
});
it('renders all buttons as enabled', () => {
- expect(
- vm.$el.querySelectorAll('.disabled').length,
- ).toBe(0);
+ expect(vm.$el.querySelectorAll('.disabled').length).toBe(0);
- expect(
- vm.$el.querySelectorAll('[disabled]').length,
- ).toBe(0);
+ expect(vm.$el.querySelectorAll('[disabled]').length).toBe(0);
});
- it('does not render delete button if canUpdate is false', (done) => {
+ it('does not render delete button if canUpdate is false', done => {
vm.canDestroy = false;
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.btn-danger'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.btn-danger')).toBeNull();
done();
});
});
- it('disables submit button when title is blank', (done) => {
+ it('disables submit button when title is blank', done => {
vm.formState.title = '';
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.btn-save').getAttribute('disabled'),
- ).toBe('disabled');
+ expect(vm.$el.querySelector('.btn-success').getAttribute('disabled')).toBe('disabled');
done();
});
});
- it('should not show delete button if showDeleteButton is false', (done) => {
+ it('should not show delete button if showDeleteButton is false', done => {
vm.showDeleteButton = false;
Vue.nextTick(() => {
@@ -72,32 +65,26 @@ describe('Edit Actions components', () => {
describe('updateIssuable', () => {
it('sends update.issauble event when clicking save button', () => {
- vm.$el.querySelector('.btn-save').click();
+ vm.$el.querySelector('.btn-success').click();
- expect(
- eventHub.$emit,
- ).toHaveBeenCalledWith('update.issuable');
+ expect(eventHub.$emit).toHaveBeenCalledWith('update.issuable');
});
- it('shows loading icon after clicking save button', (done) => {
- vm.$el.querySelector('.btn-save').click();
+ it('shows loading icon after clicking save button', done => {
+ vm.$el.querySelector('.btn-success').click();
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.btn-save .fa'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('.btn-success .fa')).not.toBeNull();
done();
});
});
- it('disabled button after clicking save button', (done) => {
- vm.$el.querySelector('.btn-save').click();
+ it('disabled button after clicking save button', done => {
+ vm.$el.querySelector('.btn-success').click();
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.btn-save').getAttribute('disabled'),
- ).toBe('disabled');
+ expect(vm.$el.querySelector('.btn-success').getAttribute('disabled')).toBe('disabled');
done();
});
@@ -108,9 +95,7 @@ describe('Edit Actions components', () => {
it('emits close.form when clicking cancel', () => {
vm.$el.querySelector('.btn-default').click();
- expect(
- eventHub.$emit,
- ).toHaveBeenCalledWith('close.form');
+ expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
});
});
@@ -119,35 +104,28 @@ describe('Edit Actions components', () => {
spyOn(window, 'confirm').and.returnValue(true);
vm.$el.querySelector('.btn-danger').click();
- expect(
- eventHub.$emit,
- ).toHaveBeenCalledWith('delete.issuable');
+ expect(eventHub.$emit).toHaveBeenCalledWith('delete.issuable');
});
- it('shows loading icon after clicking delete button', (done) => {
+ it('shows loading icon after clicking delete button', done => {
spyOn(window, 'confirm').and.returnValue(true);
vm.$el.querySelector('.btn-danger').click();
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.btn-danger .fa'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('.btn-danger .fa')).not.toBeNull();
done();
});
});
- it('does no actions when confirm is false', (done) => {
+ it('does no actions when confirm is false', done => {
spyOn(window, 'confirm').and.returnValue(false);
vm.$el.querySelector('.btn-danger').click();
Vue.nextTick(() => {
- expect(
- eventHub.$emit,
- ).not.toHaveBeenCalledWith('delete.issuable');
- expect(
- vm.$el.querySelector('.btn-danger .fa'),
- ).toBeNull();
+ expect(eventHub.$emit).not.toHaveBeenCalledWith('delete.issuable');
+
+ expect(vm.$el.querySelector('.btn-danger .fa')).toBeNull();
done();
});
diff --git a/spec/javascripts/issue_show/components/fields/description_spec.js b/spec/javascripts/issue_show/components/fields/description_spec.js
index 299f88e7778..2c3efc8d4d4 100644
--- a/spec/javascripts/issue_show/components/fields/description_spec.js
+++ b/spec/javascripts/issue_show/components/fields/description_spec.js
@@ -8,7 +8,7 @@ describe('Description field component', () => {
let vm;
let store;
- beforeEach((done) => {
+ beforeEach(done => {
const Component = Vue.extend(descriptionField);
const el = document.createElement('div');
store = new Store({
@@ -35,42 +35,32 @@ describe('Description field component', () => {
});
it('renders markdown field with description', () => {
- expect(
- vm.$el.querySelector('.md-area textarea').value,
- ).toBe('test');
+ expect(vm.$el.querySelector('.md-area textarea').value).toBe('test');
});
- it('renders markdown field with a markdown description', (done) => {
+ it('renders markdown field with a markdown description', done => {
store.formState.description = '**test**';
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.md-area textarea').value,
- ).toBe('**test**');
+ expect(vm.$el.querySelector('.md-area textarea').value).toBe('**test**');
done();
});
});
it('focuses field when mounted', () => {
- expect(
- document.activeElement,
- ).toBe(vm.$refs.textarea);
+ expect(document.activeElement).toBe(vm.$refs.textarea);
});
it('triggers update with meta+enter', () => {
vm.$el.querySelector('.md-area textarea').dispatchEvent(keyboardDownEvent(13, true));
- expect(
- eventHub.$emit,
- ).toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalled();
});
it('triggers update with ctrl+enter', () => {
vm.$el.querySelector('.md-area textarea').dispatchEvent(keyboardDownEvent(13, false, true));
- expect(
- eventHub.$emit,
- ).toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/issue_show/components/fields/description_template_spec.js b/spec/javascripts/issue_show/components/fields/description_template_spec.js
index 30441faf844..8d77a620d76 100644
--- a/spec/javascripts/issue_show/components/fields/description_template_spec.js
+++ b/spec/javascripts/issue_show/components/fields/description_template_spec.js
@@ -5,7 +5,7 @@ describe('Issue description template component', () => {
let vm;
let formState;
- beforeEach((done) => {
+ beforeEach(done => {
const Component = Vue.extend(descriptionTemplate);
formState = {
description: 'test',
@@ -24,24 +24,20 @@ describe('Issue description template component', () => {
});
it('renders templates as JSON array in data attribute', () => {
- expect(
- vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data'),
- ).toBe('[{"name":"test"}]');
+ expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe(
+ '[{"name":"test"}]',
+ );
});
it('updates formState when changing template', () => {
vm.issuableTemplate.editor.setValue('test new template');
- expect(
- formState.description,
- ).toBe('test new template');
+ expect(formState.description).toBe('test new template');
});
it('returns formState description with editor getValue', () => {
formState.description = 'testing new template';
- expect(
- vm.issuableTemplate.editor.getValue(),
- ).toBe('testing new template');
+ expect(vm.issuableTemplate.editor.getValue()).toBe('testing new template');
});
});
diff --git a/spec/javascripts/issue_show/components/fields/title_spec.js b/spec/javascripts/issue_show/components/fields/title_spec.js
index a03b462689f..4b96a1feb29 100644
--- a/spec/javascripts/issue_show/components/fields/title_spec.js
+++ b/spec/javascripts/issue_show/components/fields/title_spec.js
@@ -27,24 +27,18 @@ describe('Title field component', () => {
});
it('renders form control with formState title', () => {
- expect(
- vm.$el.querySelector('.form-control').value,
- ).toBe('test');
+ expect(vm.$el.querySelector('.form-control').value).toBe('test');
});
it('triggers update with meta+enter', () => {
vm.$el.querySelector('.form-control').dispatchEvent(keyboardDownEvent(13, true));
- expect(
- eventHub.$emit,
- ).toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalled();
});
it('triggers update with ctrl+enter', () => {
vm.$el.querySelector('.form-control').dispatchEvent(keyboardDownEvent(13, false, true));
- expect(
- eventHub.$emit,
- ).toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/issue_show/components/form_spec.js b/spec/javascripts/issue_show/components/form_spec.js
index 50ce019c32a..523954013cf 100644
--- a/spec/javascripts/issue_show/components/form_spec.js
+++ b/spec/javascripts/issue_show/components/form_spec.js
@@ -4,7 +4,7 @@ import formComponent from '~/issue_show/components/form.vue';
describe('Inline edit form component', () => {
let vm;
- beforeEach((done) => {
+ beforeEach(done => {
const Component = Vue.extend(formComponent);
vm = new Component({
@@ -15,6 +15,7 @@ describe('Inline edit form component', () => {
description: 'a',
lockedWarningVisible: false,
},
+ issuableType: 'issue',
markdownPreviewPath: '/',
markdownDocsPath: '/',
projectPath: '/',
@@ -26,36 +27,28 @@ describe('Inline edit form component', () => {
});
it('does not render template selector if no templates exist', () => {
- expect(
- vm.$el.querySelector('.js-issuable-selector-wrap'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.js-issuable-selector-wrap')).toBeNull();
});
- it('renders template selector when templates exists', (done) => {
+ it('renders template selector when templates exists', done => {
vm.issuableTemplates = ['test'];
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.js-issuable-selector-wrap'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull();
done();
});
});
it('hides locked warning by default', () => {
- expect(
- vm.$el.querySelector('.alert'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.alert')).toBeNull();
});
- it('shows locked warning if formState is different', (done) => {
+ it('shows locked warning if formState is different', done => {
vm.formState.lockedWarningVisible = true;
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.alert'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('.alert')).not.toBeNull();
done();
});
diff --git a/spec/javascripts/issue_show/components/title_spec.js b/spec/javascripts/issue_show/components/title_spec.js
index 5370f4e1fea..9754c8a6755 100644
--- a/spec/javascripts/issue_show/components/title_spec.js
+++ b/spec/javascripts/issue_show/components/title_spec.js
@@ -25,25 +25,21 @@ describe('Title component', () => {
});
it('renders title HTML', () => {
- expect(
- vm.$el.querySelector('.title').innerHTML.trim(),
- ).toBe('Testing <img>');
+ expect(vm.$el.querySelector('.title').innerHTML.trim()).toBe('Testing <img>');
});
- it('updates page title when changing titleHtml', (done) => {
+ it('updates page title when changing titleHtml', done => {
spyOn(vm, 'setPageTitle');
vm.titleHtml = 'test';
Vue.nextTick(() => {
- expect(
- vm.setPageTitle,
- ).toHaveBeenCalled();
+ expect(vm.setPageTitle).toHaveBeenCalled();
done();
});
});
- it('animates title changes', (done) => {
+ it('animates title changes', done => {
vm.titleHtml = 'test';
Vue.nextTick(() => {
@@ -61,14 +57,12 @@ describe('Title component', () => {
});
});
- it('updates page title after changing title', (done) => {
+ it('updates page title after changing title', done => {
vm.titleHtml = 'changed';
vm.titleText = 'changed';
Vue.nextTick(() => {
- expect(
- document.querySelector('title').textContent.trim(),
- ).toContain('changed');
+ expect(document.querySelector('title').textContent.trim()).toContain('changed');
done();
});
@@ -86,12 +80,14 @@ describe('Title component', () => {
it('should not show if canUpdate is false', () => {
vm.showInlineEditButton = true;
vm.canUpdate = false;
+
expect(vm.$el.querySelector('.btn-edit')).toBeNull();
});
it('should show if showInlineEditButton and canUpdate', () => {
vm.showInlineEditButton = true;
vm.canUpdate = true;
+
expect(vm.$el.querySelector('.btn-edit')).toBeDefined();
});
@@ -101,6 +97,7 @@ describe('Title component', () => {
Vue.nextTick(() => {
vm.$el.querySelector('.btn-edit').click();
+
expect(eventHub.$emit).toHaveBeenCalledWith('open.form');
});
});
diff --git a/spec/javascripts/issue_show/index_spec.js b/spec/javascripts/issue_show/index_spec.js
new file mode 100644
index 00000000000..fa0b426c06c
--- /dev/null
+++ b/spec/javascripts/issue_show/index_spec.js
@@ -0,0 +1,19 @@
+import initIssueableApp from '~/issue_show';
+
+describe('Issue show index', () => {
+ describe('initIssueableApp', () => {
+ it('should initialize app with no potential XSS attack', () => {
+ const d = document.createElement('div');
+ d.id = 'js-issuable-app-initial-data';
+ d.innerHTML = JSON.stringify({
+ initialDescriptionHtml: '&lt;img src=x onerror=alert(1)&gt;',
+ });
+ document.body.appendChild(d);
+
+ const alertSpy = spyOn(window, 'alert');
+ initIssueableApp();
+
+ expect(alertSpy).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index e12419b835d..7be495d1d35 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle */
+/* eslint-disable one-var, no-use-before-define */
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
@@ -15,6 +15,7 @@ describe('Issue', function() {
function expectErrorMessage() {
const $flashMessage = $('div.flash-alert');
+
expect($flashMessage).toExist();
expect($flashMessage).toBeVisible();
expect($flashMessage).toHaveText('Unable to update this issue at this time.');
@@ -23,6 +24,7 @@ describe('Issue', function() {
function expectIssueState(isIssueOpen) {
expectVisibility($boxClosed, !isIssueOpen);
expectVisibility($boxOpen, isIssueOpen);
+
expect($btn).toHaveText(isIssueOpen ? 'Close issue' : 'Reopen issue');
}
@@ -32,6 +34,7 @@ describe('Issue', function() {
}
const $available = Issue.$btnNewBranch.find('.available');
+
expect($available).toHaveText('New branch');
if (!isPending && canCreate) {
@@ -41,6 +44,7 @@ describe('Issue', function() {
}
const $unavailable = Issue.$btnNewBranch.find('.unavailable');
+
expect($unavailable).toHaveText('New branch unavailable');
if (!isPending && !canCreate) {
@@ -60,19 +64,22 @@ describe('Issue', function() {
function findElements(isIssueInitiallyOpen) {
$boxClosed = $('div.status-box-issue-closed');
+
expect($boxClosed).toExist();
expect($boxClosed).toHaveText('Closed');
$boxOpen = $('div.status-box-open');
+
expect($boxOpen).toExist();
expect($boxOpen).toHaveText('Open');
$btn = $('.js-issuable-close-button');
+
expect($btn).toExist();
expect($btn).toHaveText(isIssueInitiallyOpen ? 'Close issue' : 'Reopen issue');
}
- [true, false].forEach((isIssueInitiallyOpen) => {
+ [true, false].forEach(isIssueInitiallyOpen => {
describe(`with ${isIssueInitiallyOpen ? 'open' : 'closed'} issue`, function() {
const action = isIssueInitiallyOpen ? 'close' : 'reopen';
let mock;
@@ -127,7 +134,7 @@ describe('Issue', function() {
it(`${action}s the issue`, function(done) {
mockCloseButtonResponseSuccess(this.$triggeredButton.attr('href'), {
- id: 34
+ id: 34,
});
mockCanCreateBranch(!isIssueInitiallyOpen);
@@ -135,6 +142,7 @@ describe('Issue', function() {
setTimeout(() => {
expectIssueState(!isIssueInitiallyOpen);
+
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expect(this.$projectIssuesCounter.text()).toBe(isIssueInitiallyOpen ? '1,000' : '1,002');
expectNewBranchButtonState(false, !isIssueInitiallyOpen);
@@ -145,7 +153,7 @@ describe('Issue', function() {
it(`fails to ${action} the issue if saved:false`, function(done) {
mockCloseButtonResponseSuccess(this.$triggeredButton.attr('href'), {
- saved: false
+ saved: false,
});
mockCanCreateBranch(isIssueInitiallyOpen);
@@ -153,8 +161,10 @@ describe('Issue', function() {
setTimeout(() => {
expectIssueState(isIssueInitiallyOpen);
+
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expectErrorMessage();
+
expect(this.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
@@ -170,8 +180,10 @@ describe('Issue', function() {
setTimeout(() => {
expectIssueState(isIssueInitiallyOpen);
+
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expectErrorMessage();
+
expect(this.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js
deleted file mode 100644
index 2fcb5566ebc..00000000000
--- a/spec/javascripts/job_spec.js
+++ /dev/null
@@ -1,314 +0,0 @@
-import $ from 'jquery';
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import { numberToHumanSize } from '~/lib/utils/number_utils';
-import '~/lib/utils/datetime_utility';
-import Job from '~/job';
-import '~/breakpoints';
-import waitForPromises from 'spec/helpers/wait_for_promises';
-
-describe('Job', () => {
- const JOB_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`;
- let mock;
- let response;
- let job;
-
- preloadFixtures('builds/build-with-artifacts.html.raw');
-
- beforeEach(() => {
- loadFixtures('builds/build-with-artifacts.html.raw');
-
- spyOnDependency(Job, 'visitUrl');
-
- response = {};
-
- mock = new MockAdapter(axios);
-
- mock.onGet(new RegExp(`${JOB_URL}/trace.json?(.*)`)).reply(() => [200, response]);
- });
-
- afterEach(() => {
- mock.restore();
-
- clearTimeout(job.timeout);
- });
-
- describe('class constructor', () => {
- beforeEach(() => {
- jasmine.clock().install();
- });
-
- afterEach(() => {
- jasmine.clock().uninstall();
- });
-
- describe('setup', () => {
- beforeEach(function (done) {
- job = new Job();
-
- waitForPromises()
- .then(done)
- .catch(done.fail);
- });
-
- it('copies build options', function () {
- expect(job.pagePath).toBe(JOB_URL);
- expect(job.buildStatus).toBe('success');
- expect(job.buildStage).toBe('test');
- expect(job.state).toBe('');
- });
-
- it('only shows the jobs matching the current stage', () => {
- expect($('.build-job[data-stage="build"]').is(':visible')).toBe(false);
- expect($('.build-job[data-stage="test"]').is(':visible')).toBe(true);
- expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false);
- });
-
- it('selects the current stage in the build dropdown menu', () => {
- expect($('.stage-selection').text()).toBe('test');
- });
-
- it('updates the jobs when the build dropdown changes', () => {
- $('.stage-item:contains("build")').click();
-
- expect($('.stage-selection').text()).toBe('build');
- expect($('.build-job[data-stage="build"]').is(':visible')).toBe(true);
- expect($('.build-job[data-stage="test"]').is(':visible')).toBe(false);
- expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false);
- });
- });
-
- describe('running build', () => {
- it('updates the build trace on an interval', function (done) {
- response = {
- html: '<span>Update<span>',
- status: 'running',
- state: 'newstate',
- append: true,
- complete: false,
- };
-
- job = new Job();
-
- waitForPromises()
- .then(() => {
- expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
- expect(job.state).toBe('newstate');
-
- response = {
- html: '<span>More</span>',
- status: 'running',
- state: 'finalstate',
- append: true,
- complete: true,
- };
- })
- .then(() => jasmine.clock().tick(4001))
- .then(waitForPromises)
- .then(() => {
- expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/);
- expect(job.state).toBe('finalstate');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('replaces the entire build trace', (done) => {
- response = {
- html: '<span>Update<span>',
- status: 'running',
- append: false,
- complete: false,
- };
-
- job = new Job();
-
- waitForPromises()
- .then(() => {
- expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
-
- response = {
- html: '<span>Different</span>',
- status: 'running',
- append: false,
- };
- })
- .then(() => jasmine.clock().tick(4001))
- .then(waitForPromises)
- .then(() => {
- expect($('#build-trace .js-build-output').text()).not.toMatch(/Update/);
- expect($('#build-trace .js-build-output').text()).toMatch(/Different/);
- })
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('truncated information', () => {
- describe('when size is less than total', () => {
- it('shows information about truncated log', (done) => {
- response = {
- html: '<span>Update</span>',
- status: 'success',
- append: false,
- size: 50,
- total: 100,
- };
-
- job = new Job();
-
- waitForPromises()
- .then(() => {
- expect(document.querySelector('.js-truncated-info').classList).not.toContain('hidden');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('shows the size in KiB', (done) => {
- const size = 50;
-
- response = {
- html: '<span>Update</span>',
- status: 'success',
- append: false,
- size,
- total: 100,
- };
-
- job = new Job();
-
- waitForPromises()
- .then(() => {
- expect(
- document.querySelector('.js-truncated-info-size').textContent.trim(),
- ).toEqual(`${numberToHumanSize(size)}`);
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('shows incremented size', (done) => {
- response = {
- html: '<span>Update</span>',
- status: 'success',
- append: false,
- size: 50,
- total: 100,
- complete: false,
- };
-
- job = new Job();
-
- waitForPromises()
- .then(() => {
- expect(
- document.querySelector('.js-truncated-info-size').textContent.trim(),
- ).toEqual(`${numberToHumanSize(50)}`);
-
- response = {
- html: '<span>Update</span>',
- status: 'success',
- append: true,
- size: 10,
- total: 100,
- complete: true,
- };
- })
- .then(() => jasmine.clock().tick(4001))
- .then(waitForPromises)
- .then(() => {
- expect(
- document.querySelector('.js-truncated-info-size').textContent.trim(),
- ).toEqual(`${numberToHumanSize(60)}`);
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('renders the raw link', () => {
- response = {
- html: '<span>Update</span>',
- status: 'success',
- append: false,
- size: 50,
- total: 100,
- };
-
- job = new Job();
-
- expect(
- document.querySelector('.js-raw-link').textContent.trim(),
- ).toContain('Complete Raw');
- });
- });
-
- describe('when size is equal than total', () => {
- it('does not show the trunctated information', (done) => {
- response = {
- html: '<span>Update</span>',
- status: 'success',
- append: false,
- size: 100,
- total: 100,
- };
-
- job = new Job();
-
- waitForPromises()
- .then(() => {
- expect(document.querySelector('.js-truncated-info').classList).toContain('hidden');
- })
- .then(done)
- .catch(done.fail);
- });
- });
- });
-
- describe('output trace', () => {
- beforeEach((done) => {
- response = {
- html: '<span>Update</span>',
- status: 'success',
- append: false,
- size: 50,
- total: 100,
- };
-
- job = new Job();
-
- waitForPromises()
- .then(done)
- .catch(done.fail);
- });
-
- it('should render trace controls', () => {
- const controllers = document.querySelector('.controllers');
-
- expect(controllers.querySelector('.js-raw-link-controller')).not.toBeNull();
- expect(controllers.querySelector('.js-scroll-up')).not.toBeNull();
- expect(controllers.querySelector('.js-scroll-down')).not.toBeNull();
- });
-
- it('should render received output', () => {
- expect(
- document.querySelector('.js-build-output').innerHTML,
- ).toEqual('<span>Update</span>');
- });
- });
- });
-
- describe('getBuildTrace', () => {
- it('should request build trace with state parameter', (done) => {
- spyOn(axios, 'get').and.callThrough();
- job = new Job();
-
- setTimeout(() => {
- expect(axios.get).toHaveBeenCalledWith(
- `${JOB_URL}/trace.json`, { params: { state: '' } },
- );
- done();
- }, 0);
- });
- });
-});
diff --git a/spec/javascripts/jobs/components/artifacts_block_spec.js b/spec/javascripts/jobs/components/artifacts_block_spec.js
new file mode 100644
index 00000000000..2fa7ff653fe
--- /dev/null
+++ b/spec/javascripts/jobs/components/artifacts_block_spec.js
@@ -0,0 +1,120 @@
+import Vue from 'vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+import component from '~/jobs/components/artifacts_block.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Artifacts block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const expireAt = '2018-08-14T09:38:49.157Z';
+ const timeago = getTimeago();
+ const formatedDate = timeago.format(expireAt);
+
+ const expiredArtifact = {
+ expire_at: expireAt,
+ expired: true,
+ };
+
+ const nonExpiredArtifact = {
+ download_path: '/gitlab-org/gitlab-ce/-/jobs/98314558/artifacts/download',
+ browse_path: '/gitlab-org/gitlab-ce/-/jobs/98314558/artifacts/browse',
+ keep_path: '/gitlab-org/gitlab-ce/-/jobs/98314558/artifacts/keep',
+ expire_at: expireAt,
+ expired: false,
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with expired artifacts', () => {
+ it('renders expired artifact date and info', () => {
+ vm = mountComponent(Component, {
+ artifact: expiredArtifact,
+ });
+
+ expect(vm.$el.querySelector('.js-artifacts-removed')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-artifacts-will-be-removed')).toBeNull();
+ expect(vm.$el.textContent).toContain(formatedDate);
+ expect(vm.$el.querySelector('.js-artifacts-removed').textContent.trim()).toEqual(
+ 'The artifacts were removed',
+ );
+ });
+ });
+
+ describe('with artifacts that will expire', () => {
+ it('renders will expire artifact date and info', () => {
+ vm = mountComponent(Component, {
+ artifact: nonExpiredArtifact,
+ });
+
+ expect(vm.$el.querySelector('.js-artifacts-removed')).toBeNull();
+ expect(vm.$el.querySelector('.js-artifacts-will-be-removed')).not.toBeNull();
+ expect(vm.$el.textContent).toContain(formatedDate);
+ expect(vm.$el.querySelector('.js-artifacts-will-be-removed').textContent.trim()).toEqual(
+ 'The artifacts will be removed in',
+ );
+ });
+ });
+
+ describe('with keep path', () => {
+ it('renders the keep button', () => {
+ vm = mountComponent(Component, {
+ artifact: nonExpiredArtifact,
+ });
+
+ expect(vm.$el.querySelector('.js-keep-artifacts')).not.toBeNull();
+ });
+ });
+
+ describe('without keep path', () => {
+ it('does not render the keep button', () => {
+ vm = mountComponent(Component, {
+ artifact: expiredArtifact,
+ });
+
+ expect(vm.$el.querySelector('.js-keep-artifacts')).toBeNull();
+ });
+ });
+
+ describe('with download path', () => {
+ it('renders the download button', () => {
+ vm = mountComponent(Component, {
+ artifact: nonExpiredArtifact,
+ });
+
+ expect(vm.$el.querySelector('.js-download-artifacts')).not.toBeNull();
+ });
+ });
+
+ describe('without download path', () => {
+ it('does not render the keep button', () => {
+ vm = mountComponent(Component, {
+ artifact: expiredArtifact,
+ });
+
+ expect(vm.$el.querySelector('.js-download-artifacts')).toBeNull();
+ });
+ });
+
+ describe('with browse path', () => {
+ it('does not render the browse button', () => {
+ vm = mountComponent(Component, {
+ artifact: nonExpiredArtifact,
+ });
+
+ expect(vm.$el.querySelector('.js-browse-artifacts')).not.toBeNull();
+ });
+ });
+
+ describe('without browse path', () => {
+ it('does not render the browse button', () => {
+ vm = mountComponent(Component, {
+ artifact: expiredArtifact,
+ });
+
+ expect(vm.$el.querySelector('.js-browse-artifacts')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/commit_block_spec.js b/spec/javascripts/jobs/components/commit_block_spec.js
new file mode 100644
index 00000000000..98eba3ac976
--- /dev/null
+++ b/spec/javascripts/jobs/components/commit_block_spec.js
@@ -0,0 +1,88 @@
+import Vue from 'vue';
+import component from '~/jobs/components/commit_block.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Commit block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const props = {
+ commit: {
+ short_id: '1f0fb84f',
+ commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
+ title: 'Update README.md',
+ },
+ mergeRequest: {
+ iid: '!21244',
+ path: 'merge_requests/21244',
+ },
+ isLastBlock: true,
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('pipeline short sha', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ ...props,
+ });
+ });
+
+ it('renders pipeline short sha link', () => {
+ expect(vm.$el.querySelector('.js-commit-sha').getAttribute('href')).toEqual(
+ props.commit.commit_path,
+ );
+
+ expect(vm.$el.querySelector('.js-commit-sha').textContent.trim()).toEqual(
+ props.commit.short_id,
+ );
+ });
+
+ it('renders clipboard button', () => {
+ expect(vm.$el.querySelector('button').getAttribute('data-clipboard-text')).toEqual(
+ props.commit.short_id,
+ );
+ });
+ });
+
+ describe('with merge request', () => {
+ it('renders merge request link and reference', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ });
+
+ expect(vm.$el.querySelector('.js-link-commit').getAttribute('href')).toEqual(
+ props.mergeRequest.path,
+ );
+
+ expect(vm.$el.querySelector('.js-link-commit').textContent.trim()).toEqual(
+ `!${props.mergeRequest.iid}`,
+ );
+ });
+ });
+
+ describe('without merge request', () => {
+ it('does not render merge request', () => {
+ const copyProps = Object.assign({}, props);
+ delete copyProps.mergeRequest;
+
+ vm = mountComponent(Component, {
+ ...copyProps,
+ });
+
+ expect(vm.$el.querySelector('.js-link-commit')).toBeNull();
+ });
+ });
+
+ describe('git commit title', () => {
+ it('renders git commit title', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ });
+
+ expect(vm.$el.textContent).toContain(props.commit.title);
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/empty_state_spec.js b/spec/javascripts/jobs/components/empty_state_spec.js
new file mode 100644
index 00000000000..a2df79bdda0
--- /dev/null
+++ b/spec/javascripts/jobs/components/empty_state_spec.js
@@ -0,0 +1,93 @@
+import Vue from 'vue';
+import component from '~/jobs/components/empty_state.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Empty State', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const props = {
+ illustrationPath: 'illustrations/pending_job_empty.svg',
+ illustrationSizeClass: 'svg-430',
+ title: 'This job has not started yet',
+ };
+
+ const content = 'This job is in pending state and is waiting to be picked by a runner';
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('renders image and title', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ });
+ });
+
+ it('renders img with provided path and size', () => {
+ expect(vm.$el.querySelector('img').getAttribute('src')).toEqual(props.illustrationPath);
+ expect(vm.$el.querySelector('.svg-content').classList).toContain(props.illustrationSizeClass);
+ });
+
+ it('renders provided title', () => {
+ expect(vm.$el.querySelector('.js-job-empty-state-title').textContent.trim()).toEqual(
+ props.title,
+ );
+ });
+ });
+
+ describe('with content', () => {
+ it('renders content', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state-content').textContent.trim()).toEqual(
+ content,
+ );
+ });
+ });
+
+ describe('without content', () => {
+ it('does not render content', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state-content')).toBeNull();
+ });
+ });
+
+ describe('with action', () => {
+ it('renders action', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ action: {
+ path: 'runner',
+ button_title: 'Check runner',
+ method: 'post',
+ },
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state-action').getAttribute('href')).toEqual(
+ 'runner',
+ );
+ });
+ });
+
+ describe('without action', () => {
+ it('does not render action', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ action: null,
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state-action')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/environments_block_spec.js b/spec/javascripts/jobs/components/environments_block_spec.js
new file mode 100644
index 00000000000..0866ddd21d8
--- /dev/null
+++ b/spec/javascripts/jobs/components/environments_block_spec.js
@@ -0,0 +1,149 @@
+import Vue from 'vue';
+import component from '~/jobs/components/environments_block.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Environments block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+ const status = {
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ };
+
+ const environment = {
+ environment_path: '/environment',
+ name: 'environment',
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with last deployment', () => {
+ it('renders info for most recent deployment', () => {
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'last',
+ environment,
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.textContent.trim()).toEqual(
+ 'This job is the most recent deployment to environment.',
+ );
+ });
+ });
+
+ describe('with out of date deployment', () => {
+ describe('with last deployment', () => {
+ it('renders info for out date and most recent', () => {
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'out_of_date',
+ environment: Object.assign({}, environment, {
+ last_deployment: { iid: 'deployment', deployable: { build_path: 'bar' } },
+ }),
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.textContent.trim()).toEqual(
+ 'This job is an out-of-date deployment to environment. View the most recent deployment #deployment.',
+ );
+
+ expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('bar');
+ });
+ });
+
+ describe('without last deployment', () => {
+ it('renders info about out of date deployment', () => {
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'out_of_date',
+ environment,
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.textContent.trim()).toEqual(
+ 'This job is an out-of-date deployment to environment.',
+ );
+ });
+ });
+ });
+
+ describe('with failed deployment', () => {
+ it('renders info about failed deployment', () => {
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'failed',
+ environment,
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.textContent.trim()).toEqual(
+ 'The deployment of this job to environment did not succeed.',
+ );
+ });
+ });
+
+ describe('creating deployment', () => {
+ describe('with last deployment', () => {
+ it('renders info about creating deployment and overriding latest deployment', () => {
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'creating',
+ environment: Object.assign({}, environment, {
+ last_deployment: {
+ iid: 'deployment',
+ deployable: { build_path: 'foo' },
+ },
+ }),
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.textContent.trim()).toEqual(
+ 'This job is creating a deployment to environment and will overwrite the latest deployment.',
+ );
+
+ expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('foo');
+ });
+ });
+
+ describe('without last deployment', () => {
+ it('renders info about failed deployment', () => {
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'creating',
+ environment,
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.textContent.trim()).toEqual(
+ 'This job is creating a deployment to environment.',
+ );
+ });
+ });
+
+ describe('without environment', () => {
+ it('does not render environment link', () => {
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'creating',
+ environment: null,
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.querySelector('.js-environment-link')).toBeNull();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/erased_block_spec.js b/spec/javascripts/jobs/components/erased_block_spec.js
new file mode 100644
index 00000000000..8e0433d3fb7
--- /dev/null
+++ b/spec/javascripts/jobs/components/erased_block_spec.js
@@ -0,0 +1,56 @@
+import Vue from 'vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+import component from '~/jobs/components/erased_block.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Erased block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const erasedAt = '2016-11-07T11:11:16.525Z';
+ const timeago = getTimeago();
+ const formatedDate = timeago.format(erasedAt);
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with job erased by user', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ user: {
+ username: 'root',
+ web_url: 'gitlab.com/root',
+ },
+ erasedAt,
+ });
+ });
+
+ it('renders username and link', () => {
+ expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('gitlab.com/root');
+
+ expect(vm.$el.textContent).toContain('Job has been erased by');
+ expect(vm.$el.textContent).toContain('root');
+ });
+
+ it('renders erasedAt', () => {
+ expect(vm.$el.textContent).toContain(formatedDate);
+ });
+ });
+
+ describe('with erased job', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ erasedAt,
+ });
+ });
+
+ it('renders username and link', () => {
+ expect(vm.$el.textContent).toContain('Job has been erased');
+ });
+
+ it('renders erasedAt', () => {
+ expect(vm.$el.textContent).toContain(formatedDate);
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js
new file mode 100644
index 00000000000..fcf3780f0ea
--- /dev/null
+++ b/spec/javascripts/jobs/components/job_app_spec.js
@@ -0,0 +1,639 @@
+import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import jobApp from '~/jobs/components/job_app.vue';
+import createStore from '~/jobs/store';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { resetStore } from '../store/helpers';
+import job from '../mock_data';
+
+describe('Job App ', () => {
+ const delayedJobFixture = getJSONFixture('jobs/delayed.json');
+ const Component = Vue.extend(jobApp);
+ let store;
+ let vm;
+ let mock;
+
+ const props = {
+ endpoint: `${gl.TEST_HOST}jobs/123.json`,
+ runnerHelpUrl: 'help/runner',
+ runnerSettingsUrl: 'settings/ci-cd/runners',
+ terminalPath: 'jobs/123/terminal',
+ pagePath: `${gl.TEST_HOST}jobs/123`,
+ logState:
+ 'eyJvZmZzZXQiOjE3NDUxLCJuX29wZW5fdGFncyI6MCwiZmdfY29sb3IiOm51bGwsImJnX2NvbG9yIjpudWxsLCJzdHlsZV9tYXNrIjowfQ%3D%3D',
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ store = createStore();
+ });
+
+ afterEach(() => {
+ resetStore(store);
+ vm.$destroy();
+ mock.restore();
+ });
+
+ describe('while loading', () => {
+ beforeEach(() => {
+ mock.onGet(props.endpoint).reply(200, job, {});
+ mock.onGet(`${props.pagePath}/trace.json`).reply(200, {});
+ vm = mountComponentWithStore(Component, { props, store });
+ });
+
+ it('renders loading icon', done => {
+ expect(vm.$el.querySelector('.js-job-loading')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-job-sidebar')).toBeNull();
+ expect(vm.$el.querySelector('.js-job-content')).toBeNull();
+
+ setTimeout(() => {
+ done();
+ }, 0);
+ });
+ });
+
+ describe('with successful request', () => {
+ beforeEach(() => {
+ mock.onGet(`${props.pagePath}/trace.json`).replyOnce(200, {});
+ });
+
+ describe('Header section', () => {
+ describe('job callout message', () => {
+ it('should not render the reason when reason is absent', done => {
+ mock.onGet(props.endpoint).replyOnce(200, job);
+ vm = mountComponentWithStore(Component, { props, store });
+
+ setTimeout(() => {
+ expect(vm.shouldRenderCalloutMessage).toBe(false);
+
+ done();
+ }, 0);
+ });
+
+ it('should render the reason when reason is present', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ callout_message: 'There is an unknown failure, please try again',
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, { props, store });
+ setTimeout(() => {
+ expect(vm.shouldRenderCalloutMessage).toBe(true);
+ done();
+ }, 0);
+ });
+ });
+
+ describe('triggered job', () => {
+ beforeEach(() => {
+ mock
+ .onGet(props.endpoint)
+ .replyOnce(200, Object.assign({}, job, { started: '2017-05-24T10:59:52.000+01:00' }));
+ vm = mountComponentWithStore(Component, { props, store });
+ });
+
+ it('should render provided job information', done => {
+ setTimeout(() => {
+ expect(
+ vm.$el
+ .querySelector('.header-main-content')
+ .textContent.replace(/\s+/g, ' ')
+ .trim(),
+ ).toContain('passed Job #4757 triggered 1 year ago by Root');
+ done();
+ }, 0);
+ });
+
+ it('should render new issue link', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual(
+ job.new_issue_path,
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('created job', () => {
+ it('should render created key', done => {
+ mock.onGet(props.endpoint).replyOnce(200, job);
+ vm = mountComponentWithStore(Component, { props, store });
+
+ setTimeout(() => {
+ expect(
+ vm.$el
+ .querySelector('.header-main-content')
+ .textContent.replace(/\s+/g, ' ')
+ .trim(),
+ ).toContain('passed Job #4757 created 3 weeks ago by Root');
+ done();
+ }, 0);
+ });
+ });
+ });
+
+ describe('stuck block', () => {
+ describe('without active runners availabl', () => {
+ it('renders stuck block when there are no runners', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ status: {
+ group: 'pending',
+ icon: 'status_pending',
+ label: 'pending',
+ text: 'pending',
+ details_path: 'path',
+ },
+ stuck: true,
+ runners: {
+ available: false,
+ online: false,
+ },
+ tags: [],
+ }),
+ );
+ vm = mountComponentWithStore(Component, { props, store });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-stuck')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(
+ "This job is stuck, because you don't have any active runners that can run this job.",
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('when available runners can not run specified tag', () => {
+ it('renders tags in stuck block when there are no runners', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ status: {
+ group: 'pending',
+ icon: 'status_pending',
+ label: 'pending',
+ text: 'pending',
+ details_path: 'path',
+ },
+ stuck: true,
+ runners: {
+ available: false,
+ online: false,
+ },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(job.tags[0]);
+ expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(
+ "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:",
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('when runners are offline and build has tags', () => {
+ it('renders message about job being stuck because of no runners with the specified tags', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ status: {
+ group: 'pending',
+ icon: 'status_pending',
+ label: 'pending',
+ text: 'pending',
+ details_path: 'path',
+ },
+ stuck: true,
+ runners: {
+ available: true,
+ online: true,
+ },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(job.tags[0]);
+ expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(
+ "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:",
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ it('does not renders stuck block when there are no runners', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ runners: { available: true },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-stuck')).toBeNull();
+
+ done();
+ }, 0);
+ });
+ });
+
+ describe('environments block', () => {
+ it('renders environment block when job has environment', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ deployment_status: {
+ environment: {
+ environment_path: '/path',
+ name: 'foo',
+ },
+ },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-environment')).not.toBeNull();
+
+ done();
+ }, 0);
+ });
+
+ it('does not render environment block when job has environment', done => {
+ mock.onGet(props.endpoint).replyOnce(200, job);
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-environment')).toBeNull();
+ done();
+ }, 0);
+ });
+ });
+
+ describe('erased block', () => {
+ it('renders erased block when `erased` is true', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ erased_by: {
+ username: 'root',
+ web_url: 'gitlab.com/root',
+ },
+ erased_at: '2016-11-07T11:11:16.525Z',
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-erased-block')).not.toBeNull();
+
+ done();
+ }, 0);
+ });
+
+ it('does not render erased block when `erased` is false', done => {
+ mock.onGet(props.endpoint).replyOnce(200, Object.assign({}, job, { erased_at: null }));
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-erased-block')).toBeNull();
+
+ done();
+ }, 0);
+ });
+ });
+
+ describe('empty states block', () => {
+ it('renders empty state when job does not have trace and is not running', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ has_trace: false,
+ status: {
+ group: 'pending',
+ icon: 'status_pending',
+ label: 'pending',
+ text: 'pending',
+ details_path: 'path',
+ illustration: {
+ image: 'path',
+ size: '340',
+ title: 'Empty State',
+ content: 'This is an empty state',
+ },
+ action: {
+ button_title: 'Retry job',
+ method: 'post',
+ path: '/path',
+ },
+ },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-empty-state')).not.toBeNull();
+
+ done();
+ }, 0);
+ });
+
+ it('does not render empty state when job does not have trace but it is running', done => {
+ mock.onGet(props.endpoint).replyOnce(
+ 200,
+ Object.assign({}, job, {
+ has_trace: false,
+ status: {
+ group: 'running',
+ icon: 'status_running',
+ label: 'running',
+ text: 'running',
+ details_path: 'path',
+ },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull();
+
+ done();
+ }, 0);
+ });
+
+ it('does not render empty state when job has trace but it is not running', done => {
+ mock.onGet(props.endpoint).replyOnce(200, Object.assign({}, job, { has_trace: true }));
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull();
+
+ done();
+ }, 0);
+ });
+
+ it('displays remaining time for a delayed job', done => {
+ const oneHourInMilliseconds = 3600000;
+ spyOn(Date, 'now').and.callFake(
+ () => new Date(delayedJobFixture.scheduled_at).getTime() - oneHourInMilliseconds,
+ );
+ mock.onGet(props.endpoint).replyOnce(200, { ...delayedJobFixture });
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ store.subscribeAction(action => {
+ if (action.type !== 'receiveJobSuccess') {
+ return;
+ }
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-job-empty-state')).not.toBeNull();
+
+ const title = vm.$el.querySelector('.js-job-empty-state-title');
+
+ expect(title).toContainText('01:00:00');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+ });
+ });
+
+ describe('archived job', () => {
+ beforeEach(() => {
+ mock.onGet(props.endpoint).reply(200, Object.assign({}, job, { archived: true }), {});
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+ });
+
+ it('renders warning about job being archived', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-archived-job ')).not.toBeNull();
+ done();
+ }, 0);
+ });
+ });
+
+ describe('non-archived job', () => {
+ beforeEach(() => {
+ mock.onGet(props.endpoint).reply(200, job, {});
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+ });
+
+ it('does not warning about job being archived', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-archived-job ')).toBeNull();
+ done();
+ }, 0);
+ });
+ });
+
+ describe('trace output', () => {
+ beforeEach(() => {
+ mock.onGet(props.endpoint).reply(200, job, {});
+ });
+
+ describe('with append flag', () => {
+ it('appends the log content to the existing one', done => {
+ mock.onGet(`${props.pagePath}/trace.json`).reply(200, {
+ html: '<span>More<span>',
+ status: 'running',
+ state: 'newstate',
+ append: true,
+ complete: true,
+ });
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ vm.$store.state.trace = 'Update';
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).toContain('Update');
+
+ done();
+ }, 0);
+ });
+ });
+
+ describe('without append flag', () => {
+ it('replaces the trace', done => {
+ mock.onGet(`${props.pagePath}/trace.json`).reply(200, {
+ html: '<span>Different<span>',
+ status: 'running',
+ append: false,
+ complete: true,
+ });
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+ vm.$store.state.trace = 'Update';
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).not.toContain(
+ 'Update',
+ );
+
+ expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).toContain('Different');
+ done();
+ }, 0);
+ });
+ });
+
+ describe('truncated information', () => {
+ describe('when size is less than total', () => {
+ it('shows information about truncated log', done => {
+ mock.onGet(`${props.pagePath}/trace.json`).reply(200, {
+ html: '<span>Update</span>',
+ status: 'success',
+ append: false,
+ size: 50,
+ total: 100,
+ complete: true,
+ });
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-truncated-info').textContent.trim()).toContain(
+ '50 bytes',
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('when size is equal than total', () => {
+ it('does not show the truncated information', done => {
+ mock.onGet(`${props.pagePath}/trace.json`).reply(200, {
+ html: '<span>Update</span>',
+ status: 'success',
+ append: false,
+ size: 100,
+ total: 100,
+ complete: true,
+ });
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-truncated-info').textContent.trim()).not.toContain(
+ '50 bytes',
+ );
+ done();
+ }, 0);
+ });
+ });
+ });
+
+ describe('trace controls', () => {
+ beforeEach(() => {
+ mock.onGet(`${props.pagePath}/trace.json`).reply(200, {
+ html: '<span>Update</span>',
+ status: 'success',
+ append: false,
+ size: 50,
+ total: 100,
+ complete: true,
+ });
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+ });
+
+ it('should render scroll buttons', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-scroll-top')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-scroll-bottom')).not.toBeNull();
+ done();
+ }, 0);
+ });
+
+ it('should render link to raw ouput', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-raw-link-controller')).not.toBeNull();
+ done();
+ }, 0);
+ });
+
+ it('should render link to erase job', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-erase-link')).not.toBeNull();
+ done();
+ }, 0);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/job_container_item_spec.js b/spec/javascripts/jobs/components/job_container_item_spec.js
new file mode 100644
index 00000000000..2d108f1ad7f
--- /dev/null
+++ b/spec/javascripts/jobs/components/job_container_item_spec.js
@@ -0,0 +1,99 @@
+import Vue from 'vue';
+import JobContainerItem from '~/jobs/components/job_container_item.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import job from '../mock_data';
+
+describe('JobContainerItem', () => {
+ const delayedJobFixture = getJSONFixture('jobs/delayed.json');
+ const Component = Vue.extend(JobContainerItem);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ const sharedTests = () => {
+ it('displays a status icon', () => {
+ expect(vm.$el).toHaveSpriteIcon(job.status.icon);
+ });
+
+ it('displays the job name', () => {
+ expect(vm.$el).toContainText(job.name);
+ });
+
+ it('displays a link to the job', () => {
+ const link = vm.$el.querySelector('.js-job-link');
+
+ expect(link.href).toBe(job.status.details_path);
+ });
+ };
+
+ describe('when a job is not active and not retied', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ job,
+ isActive: false,
+ });
+ });
+
+ sharedTests();
+ });
+
+ describe('when a job is active', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ job,
+ isActive: true,
+ });
+ });
+
+ sharedTests();
+
+ it('displays an arrow', () => {
+ expect(vm.$el).toHaveSpriteIcon('arrow-right');
+ });
+ });
+
+ describe('when a job is retried', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ job: {
+ ...job,
+ retried: true,
+ },
+ isActive: false,
+ });
+ });
+
+ sharedTests();
+
+ it('displays an icon', () => {
+ expect(vm.$el).toHaveSpriteIcon('retry');
+ });
+ });
+
+ describe('for delayed job', () => {
+ beforeEach(() => {
+ const remainingMilliseconds = 1337000;
+ spyOn(Date, 'now').and.callFake(
+ () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingMilliseconds,
+ );
+ });
+
+ it('displays remaining time in tooltip', done => {
+ vm = mountComponent(Component, {
+ job: delayedJobFixture,
+ isActive: false,
+ });
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-job-link').getAttribute('data-original-title')).toEqual(
+ 'delayed job - delayed manual action (00:22:17)',
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/job_log_controllers_spec.js b/spec/javascripts/jobs/components/job_log_controllers_spec.js
new file mode 100644
index 00000000000..d527c6708fc
--- /dev/null
+++ b/spec/javascripts/jobs/components/job_log_controllers_spec.js
@@ -0,0 +1,208 @@
+import Vue from 'vue';
+import component from '~/jobs/components/job_log_controllers.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Job log controllers', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ const props = {
+ rawPath: '/raw',
+ erasePath: '/erase',
+ size: 511952,
+ isScrollTopDisabled: false,
+ isScrollBottomDisabled: false,
+ isScrollingDown: true,
+ isTraceSizeVisible: true,
+ };
+
+ describe('Truncate information', () => {
+ describe('with isTraceSizeVisible', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ });
+
+ it('renders size information', () => {
+ expect(vm.$el.querySelector('.js-truncated-info').textContent).toContain('499.95 KiB');
+ });
+
+ it('renders link to raw trace', () => {
+ expect(vm.$el.querySelector('.js-raw-link').getAttribute('href')).toEqual('/raw');
+ });
+ });
+ });
+
+ describe('links section', () => {
+ describe('with raw trace path', () => {
+ it('renders raw trace link', () => {
+ vm = mountComponent(Component, props);
+
+ expect(vm.$el.querySelector('.js-raw-link-controller').getAttribute('href')).toEqual(
+ '/raw',
+ );
+ });
+ });
+
+ describe('without raw trace path', () => {
+ it('does not render raw trace link', () => {
+ vm = mountComponent(Component, {
+ erasePath: '/erase',
+ size: 511952,
+ isScrollTopDisabled: true,
+ isScrollBottomDisabled: true,
+ isScrollingDown: false,
+ isTraceSizeVisible: true,
+ });
+
+ expect(vm.$el.querySelector('.js-raw-link-controller')).toBeNull();
+ });
+ });
+
+ describe('when is erasable', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ });
+
+ it('renders erase job link', () => {
+ expect(vm.$el.querySelector('.js-erase-link')).not.toBeNull();
+ });
+ });
+
+ describe('when it is not erasable', () => {
+ it('does not render erase button', () => {
+ vm = mountComponent(Component, {
+ rawPath: '/raw',
+ size: 511952,
+ isScrollTopDisabled: true,
+ isScrollBottomDisabled: true,
+ isScrollingDown: false,
+ isTraceSizeVisible: true,
+ });
+
+ expect(vm.$el.querySelector('.js-erase-link')).toBeNull();
+ });
+ });
+ });
+
+ describe('scroll buttons', () => {
+ describe('scroll top button', () => {
+ describe('when user can scroll top', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ });
+
+ it('renders enabled scroll top button', () => {
+ expect(vm.$el.querySelector('.js-scroll-top').getAttribute('disabled')).toBeNull();
+ });
+
+ it('emits scrollJobLogTop event on click', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-scroll-top').click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('scrollJobLogTop');
+ });
+ });
+
+ describe('when user can not scroll top', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ rawPath: '/raw',
+ erasePath: '/erase',
+ size: 511952,
+ isScrollTopDisabled: true,
+ isScrollBottomDisabled: false,
+ isScrollingDown: false,
+ isTraceSizeVisible: true,
+ });
+ });
+
+ it('renders disabled scroll top button', () => {
+ expect(vm.$el.querySelector('.js-scroll-top').getAttribute('disabled')).toEqual(
+ 'disabled',
+ );
+ });
+
+ it('does not emit scrollJobLogTop event on click', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-scroll-top').click();
+
+ expect(vm.$emit).not.toHaveBeenCalledWith('scrollJobLogTop');
+ });
+ });
+ });
+
+ describe('scroll bottom button', () => {
+ describe('when user can scroll bottom', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ });
+
+ it('renders enabled scroll bottom button', () => {
+ expect(vm.$el.querySelector('.js-scroll-bottom').getAttribute('disabled')).toBeNull();
+ });
+
+ it('emits scrollJobLogBottom event on click', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-scroll-bottom').click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('scrollJobLogBottom');
+ });
+ });
+
+ describe('when user can not scroll bottom', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ rawPath: '/raw',
+ erasePath: '/erase',
+ size: 511952,
+ isScrollTopDisabled: false,
+ isScrollBottomDisabled: true,
+ isScrollingDown: false,
+ isTraceSizeVisible: true,
+ });
+ });
+
+ it('renders disabled scroll bottom button', () => {
+ expect(vm.$el.querySelector('.js-scroll-bottom').getAttribute('disabled')).toEqual(
+ 'disabled',
+ );
+ });
+
+ it('does not emit scrollJobLogBottom event on click', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-scroll-bottom').click();
+
+ expect(vm.$emit).not.toHaveBeenCalledWith('scrollJobLogBottom');
+ });
+ });
+
+ describe('while isScrollingDown is true', () => {
+ it('renders animate class for the scroll down button', () => {
+ vm = mountComponent(Component, props);
+
+ expect(vm.$el.querySelector('.js-scroll-bottom').className).toContain('animate');
+ });
+ });
+
+ describe('while isScrollingDown is false', () => {
+ it('does not render animate class for the scroll down button', () => {
+ vm = mountComponent(Component, {
+ rawPath: '/raw',
+ erasePath: '/erase',
+ size: 511952,
+ isScrollTopDisabled: true,
+ isScrollBottomDisabled: false,
+ isScrollingDown: false,
+ isTraceSizeVisible: true,
+ });
+
+ expect(vm.$el.querySelector('.js-scroll-bottom').className).not.toContain('animate');
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/job_log_spec.js b/spec/javascripts/jobs/components/job_log_spec.js
new file mode 100644
index 00000000000..dc0f77ceb80
--- /dev/null
+++ b/spec/javascripts/jobs/components/job_log_spec.js
@@ -0,0 +1,65 @@
+import Vue from 'vue';
+import component from '~/jobs/components/job_log.vue';
+import createStore from '~/jobs/store';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { resetStore } from '../store/helpers';
+
+describe('Job Log', () => {
+ const Component = Vue.extend(component);
+ let store;
+ let vm;
+
+ const trace =
+ 'Running with gitlab-runner 11.1.0 (081978aa)<br> on docker-auto-scale-com d5ae8d25<br>Using Docker executor with image dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29 ...<br>';
+
+ beforeEach(() => {
+ store = createStore();
+ });
+
+ afterEach(() => {
+ resetStore(store);
+ vm.$destroy();
+ });
+
+ it('renders provided trace', () => {
+ vm = mountComponentWithStore(Component, {
+ props: {
+ trace,
+ isComplete: true,
+ },
+ store,
+ });
+
+ expect(vm.$el.querySelector('code').textContent).toContain(
+ 'Running with gitlab-runner 11.1.0 (081978aa)',
+ );
+ });
+
+ describe('while receiving trace', () => {
+ it('renders animation', () => {
+ vm = mountComponentWithStore(Component, {
+ props: {
+ trace,
+ isComplete: false,
+ },
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-log-animation')).not.toBeNull();
+ });
+ });
+
+ describe('when build trace has finishes', () => {
+ it('does not render animation', () => {
+ vm = mountComponentWithStore(Component, {
+ props: {
+ trace,
+ isComplete: true,
+ },
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-log-animation')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/jobs_container_spec.js b/spec/javascripts/jobs/components/jobs_container_spec.js
new file mode 100644
index 00000000000..fa3a2c4c266
--- /dev/null
+++ b/spec/javascripts/jobs/components/jobs_container_spec.js
@@ -0,0 +1,131 @@
+import Vue from 'vue';
+import component from '~/jobs/components/jobs_container.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Jobs List block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const retried = {
+ status: {
+ details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ id: 233432756,
+ tooltip: 'build - passed',
+ retried: true,
+ };
+
+ const active = {
+ name: 'test',
+ status: {
+ details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ id: 2322756,
+ tooltip: 'build - passed',
+ active: true,
+ };
+
+ const job = {
+ name: 'build',
+ status: {
+ details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ id: 232153,
+ tooltip: 'build - passed',
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders list of jobs', () => {
+ vm = mountComponent(Component, {
+ jobs: [job, retried, active],
+ jobId: 12313,
+ });
+
+ expect(vm.$el.querySelectorAll('a').length).toEqual(3);
+ });
+
+ it('renders arrow right when job id matches `jobId`', () => {
+ vm = mountComponent(Component, {
+ jobs: [active],
+ jobId: active.id,
+ });
+
+ expect(vm.$el.querySelector('a .js-arrow-right')).not.toBeNull();
+ });
+
+ it('does not render arrow right when job is not active', () => {
+ vm = mountComponent(Component, {
+ jobs: [job],
+ jobId: active.id,
+ });
+
+ expect(vm.$el.querySelector('a .js-arrow-right')).toBeNull();
+ });
+
+ it('renders job name when present', () => {
+ vm = mountComponent(Component, {
+ jobs: [job],
+ jobId: active.id,
+ });
+
+ expect(vm.$el.querySelector('a').textContent.trim()).toContain(job.name);
+ expect(vm.$el.querySelector('a').textContent.trim()).not.toContain(job.id);
+ });
+
+ it('renders job id when job name is not available', () => {
+ vm = mountComponent(Component, {
+ jobs: [retried],
+ jobId: active.id,
+ });
+
+ expect(vm.$el.querySelector('a').textContent.trim()).toContain(retried.id);
+ });
+
+ it('links to the job page', () => {
+ vm = mountComponent(Component, {
+ jobs: [job],
+ jobId: active.id,
+ });
+
+ expect(vm.$el.querySelector('a').getAttribute('href')).toEqual(job.status.details_path);
+ });
+
+ it('renders retry icon when job was retried', () => {
+ vm = mountComponent(Component, {
+ jobs: [retried],
+ jobId: active.id,
+ });
+
+ expect(vm.$el.querySelector('.js-retry-icon')).not.toBeNull();
+ });
+
+ it('does not render retry icon when job was not retried', () => {
+ vm = mountComponent(Component, {
+ jobs: [job],
+ jobId: active.id,
+ });
+
+ expect(vm.$el.querySelector('.js-retry-icon')).toBeNull();
+ });
+});
diff --git a/spec/javascripts/jobs/components/sidebar_detail_row_spec.js b/spec/javascripts/jobs/components/sidebar_detail_row_spec.js
new file mode 100644
index 00000000000..42d11266dad
--- /dev/null
+++ b/spec/javascripts/jobs/components/sidebar_detail_row_spec.js
@@ -0,0 +1,61 @@
+import Vue from 'vue';
+import sidebarDetailRow from '~/jobs/components/sidebar_detail_row.vue';
+
+describe('Sidebar detail row', () => {
+ let SidebarDetailRow;
+ let vm;
+
+ beforeEach(() => {
+ SidebarDetailRow = Vue.extend(sidebarDetailRow);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render no title', () => {
+ vm = new SidebarDetailRow({
+ propsData: {
+ value: 'this is the value',
+ },
+ }).$mount();
+
+ expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual('this is the value');
+ });
+
+ beforeEach(() => {
+ vm = new SidebarDetailRow({
+ propsData: {
+ title: 'this is the title',
+ value: 'this is the value',
+ },
+ }).$mount();
+ });
+
+ it('should render provided title and value', () => {
+ expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual(
+ 'this is the title: this is the value',
+ );
+ });
+
+ describe('when helpUrl not provided', () => {
+ it('should not render help', () => {
+ expect(vm.$el.querySelector('.help-button')).toBeNull();
+ });
+ });
+
+ describe('when helpUrl provided', () => {
+ beforeEach(() => {
+ vm = new SidebarDetailRow({
+ propsData: {
+ helpUrl: 'help url',
+ value: 'foo',
+ },
+ }).$mount();
+ });
+
+ it('should render help', () => {
+ expect(vm.$el.querySelector('.help-button a').getAttribute('href')).toEqual('help url');
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/sidebar_spec.js b/spec/javascripts/jobs/components/sidebar_spec.js
new file mode 100644
index 00000000000..424092d2d88
--- /dev/null
+++ b/spec/javascripts/jobs/components/sidebar_spec.js
@@ -0,0 +1,189 @@
+import Vue from 'vue';
+import sidebarDetailsBlock from '~/jobs/components/sidebar.vue';
+import createStore from '~/jobs/store';
+import job, { stages, jobsInStage } from '../mock_data';
+import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { trimText } from '../../helpers/vue_component_helper';
+
+describe('Sidebar details block', () => {
+ const SidebarComponent = Vue.extend(sidebarDetailsBlock);
+ let vm;
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('when there is no retry path retry', () => {
+ it('should not render a retry button', () => {
+ const copy = Object.assign({}, job);
+ delete copy.retry_path;
+
+ store.dispatch('receiveJobSuccess', copy);
+ vm = mountComponentWithStore(SidebarComponent, {
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-retry-job')).toBeNull();
+ });
+ });
+
+ describe('without terminal path', () => {
+ it('does not render terminal link', () => {
+ store.dispatch('receiveJobSuccess', job);
+ vm = mountComponentWithStore(SidebarComponent, { store });
+
+ expect(vm.$el.querySelector('.js-terminal-link')).toBeNull();
+ });
+ });
+
+ describe('with terminal path', () => {
+ it('renders terminal link', () => {
+ store.dispatch(
+ 'receiveJobSuccess',
+ Object.assign({}, job, { terminal_path: 'job/43123/terminal' }),
+ );
+ vm = mountComponentWithStore(SidebarComponent, {
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-terminal-link')).not.toBeNull();
+ });
+ });
+
+ beforeEach(() => {
+ store.dispatch('receiveJobSuccess', job);
+ vm = mountComponentWithStore(SidebarComponent, { store });
+ });
+
+ describe('actions', () => {
+ it('should render link to new issue', () => {
+ expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual(
+ job.new_issue_path,
+ );
+
+ expect(vm.$el.querySelector('.js-new-issue').textContent.trim()).toEqual('New issue');
+ });
+
+ it('should render link to retry job', () => {
+ expect(vm.$el.querySelector('.js-retry-job').getAttribute('href')).toEqual(job.retry_path);
+ });
+
+ it('should render link to cancel job', () => {
+ expect(vm.$el.querySelector('.js-cancel-job').getAttribute('href')).toEqual(job.cancel_path);
+ });
+ });
+
+ describe('information', () => {
+ it('should render merge request link', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-mr').textContent)).toEqual('Merge Request: !2');
+
+ expect(vm.$el.querySelector('.js-job-mr a').getAttribute('href')).toEqual(
+ job.merge_request.path,
+ );
+ });
+
+ it('should render job duration', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-duration').textContent)).toEqual(
+ 'Duration: 6 seconds',
+ );
+ });
+
+ it('should render erased date', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-erased').textContent)).toEqual(
+ 'Erased: 3 weeks ago',
+ );
+ });
+
+ it('should render finished date', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-finished').textContent)).toEqual(
+ 'Finished: 3 weeks ago',
+ );
+ });
+
+ it('should render queued date', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-queued').textContent)).toEqual(
+ 'Queued: 9 seconds',
+ );
+ });
+
+ it('should render runner ID', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-runner').textContent)).toEqual(
+ 'Runner: local ci runner (#1)',
+ );
+ });
+
+ it('should render timeout information', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-timeout').textContent)).toEqual(
+ 'Timeout: 1m 40s (from runner)',
+ );
+ });
+
+ it('should render coverage', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-coverage').textContent)).toEqual(
+ 'Coverage: 20%',
+ );
+ });
+
+ it('should render tags', () => {
+ expect(trimText(vm.$el.querySelector('.js-job-tags').textContent)).toEqual('Tags: tag');
+ });
+ });
+
+ describe('stages dropdown', () => {
+ beforeEach(() => {
+ store.dispatch('receiveJobSuccess', job);
+ });
+
+ describe('while fetching stages', () => {
+ it('it does not render dropdown', () => {
+ store.dispatch('requestStages');
+ vm = mountComponentWithStore(SidebarComponent, { store });
+
+ expect(vm.$el.querySelector('.js-selected-stage')).toBeNull();
+ });
+ });
+
+ describe('with stages', () => {
+ beforeEach(() => {
+ store.dispatch('receiveStagesSuccess', stages);
+ vm = mountComponentWithStore(SidebarComponent, { store });
+ });
+
+ it('renders value provided as selectedStage as selected', () => {
+ expect(vm.$el.querySelector('.js-selected-stage').textContent.trim()).toEqual(
+ vm.selectedStage,
+ );
+ });
+ });
+
+ describe('without jobs for stages', () => {
+ beforeEach(() => {
+ store.dispatch('receiveJobSuccess', job);
+ store.dispatch('receiveStagesSuccess', stages);
+ vm = mountComponentWithStore(SidebarComponent, { store });
+ });
+
+ it('does not render job container', () => {
+ expect(vm.$el.querySelector('.js-jobs-container')).toBeNull();
+ });
+ });
+
+ describe('with jobs for stages', () => {
+ beforeEach(() => {
+ store.dispatch('receiveJobSuccess', job);
+ store.dispatch('receiveStagesSuccess', stages);
+ store.dispatch('receiveJobsForStageSuccess', jobsInStage.latest_statuses);
+ vm = mountComponentWithStore(SidebarComponent, { store });
+ });
+
+ it('renders list of jobs', () => {
+ expect(vm.$el.querySelector('.js-jobs-container')).not.toBeNull();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/stages_dropdown_spec.js b/spec/javascripts/jobs/components/stages_dropdown_spec.js
new file mode 100644
index 00000000000..9c731ae2f68
--- /dev/null
+++ b/spec/javascripts/jobs/components/stages_dropdown_spec.js
@@ -0,0 +1,59 @@
+import Vue from 'vue';
+import component from '~/jobs/components/stages_dropdown.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Stages Dropdown', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ pipeline: {
+ id: 28029444,
+ details: {
+ status: {
+ details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ },
+ path: 'pipeline/28029444',
+ },
+ stages: [
+ {
+ name: 'build',
+ },
+ {
+ name: 'test',
+ },
+ ],
+ selectedStage: 'deploy',
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders pipeline status', () => {
+ expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull();
+ });
+
+ it('renders pipeline link', () => {
+ expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual(
+ 'pipeline/28029444',
+ );
+ });
+
+ it('renders dropdown with stages', () => {
+ expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build');
+ });
+
+ it('rendes selected stage', () => {
+ expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy');
+ });
+});
diff --git a/spec/javascripts/jobs/components/stuck_block_spec.js b/spec/javascripts/jobs/components/stuck_block_spec.js
new file mode 100644
index 00000000000..c320793b2be
--- /dev/null
+++ b/spec/javascripts/jobs/components/stuck_block_spec.js
@@ -0,0 +1,81 @@
+import Vue from 'vue';
+import component from '~/jobs/components/stuck_block.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Stuck Block Job component', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with no runners for project', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ hasNoRunnersForProject: true,
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders only information about project not having runners', () => {
+ expect(vm.$el.querySelector('.js-stuck-no-runners')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-with-tags')).toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-no-active-runner')).toBeNull();
+ });
+
+ it('renders link to runners page', () => {
+ expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+
+ describe('with tags', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ hasNoRunnersForProject: false,
+ tags: ['docker', 'gitlab-org'],
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders information about the tags not being set', () => {
+ expect(vm.$el.querySelector('.js-stuck-no-runners')).toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-with-tags')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-no-active-runner')).toBeNull();
+ });
+
+ it('renders tags', () => {
+ expect(vm.$el.textContent).toContain('docker');
+ expect(vm.$el.textContent).toContain('gitlab-org');
+ });
+
+ it('renders link to runners page', () => {
+ expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+
+ describe('without active runners', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ hasNoRunnersForProject: false,
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders information about project not having runners', () => {
+ expect(vm.$el.querySelector('.js-stuck-no-runners')).toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-with-tags')).toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-no-active-runner')).not.toBeNull();
+ });
+
+ it('renders link to runners page', () => {
+ expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/components/trigger_block_spec.js b/spec/javascripts/jobs/components/trigger_block_spec.js
new file mode 100644
index 00000000000..7254851a9e7
--- /dev/null
+++ b/spec/javascripts/jobs/components/trigger_block_spec.js
@@ -0,0 +1,76 @@
+import Vue from 'vue';
+import component from '~/jobs/components/trigger_block.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Trigger block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with short token', () => {
+ it('renders short token', () => {
+ vm = mountComponent(Component, {
+ trigger: {
+ short_token: '0a666b2',
+ },
+ });
+
+ expect(vm.$el.querySelector('.js-short-token').textContent).toContain('0a666b2');
+ });
+ });
+
+ describe('without short token', () => {
+ it('does not render short token', () => {
+ vm = mountComponent(Component, { trigger: {} });
+
+ expect(vm.$el.querySelector('.js-short-token')).toBeNull();
+ });
+ });
+
+ describe('with variables', () => {
+ describe('reveal variables', () => {
+ it('reveals variables on click', done => {
+ vm = mountComponent(Component, {
+ trigger: {
+ short_token: 'bd7e',
+ variables: [
+ { key: 'UPLOAD_TO_GCS', value: 'false', public: false },
+ { key: 'UPLOAD_TO_S3', value: 'true', public: false },
+ ],
+ },
+ });
+
+ vm.$el.querySelector('.js-reveal-variables').click();
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-build-variables')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-build-variables').textContent).toContain(
+ 'UPLOAD_TO_GCS',
+ );
+
+ expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('false');
+ expect(vm.$el.querySelector('.js-build-variables').textContent).toContain(
+ 'UPLOAD_TO_S3',
+ );
+
+ expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('true');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('without variables', () => {
+ it('does not render variables', () => {
+ vm = mountComponent(Component, { trigger: {} });
+
+ expect(vm.$el.querySelector('.js-reveal-variables')).toBeNull();
+ expect(vm.$el.querySelector('.js-build-variables')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/header_spec.js b/spec/javascripts/jobs/header_spec.js
deleted file mode 100644
index e21e2c6d6e3..00000000000
--- a/spec/javascripts/jobs/header_spec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import Vue from 'vue';
-import headerComponent from '~/jobs/components/header.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('Job details header', () => {
- let HeaderComponent;
- let vm;
- let props;
-
- beforeEach(() => {
- HeaderComponent = Vue.extend(headerComponent);
-
- const threeWeeksAgo = new Date();
- threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
-
- const twoDaysAgo = new Date();
- twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);
-
- props = {
- job: {
- status: {
- group: 'failed',
- icon: 'status_failed',
- label: 'failed',
- text: 'failed',
- details_path: 'path',
- },
- id: 123,
- created_at: threeWeeksAgo.toISOString(),
- user: {
- web_url: 'path',
- name: 'Foo',
- username: 'foobar',
- email: 'foo@bar.com',
- avatar_url: 'link',
- },
- started: twoDaysAgo.toISOString(),
- new_issue_path: 'path',
- },
- isLoading: false,
- };
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('job reason', () => {
- it('should not render the reason when reason is absent', () => {
- vm = mountComponent(HeaderComponent, props);
-
- expect(vm.shouldRenderReason).toBe(false);
- });
-
- it('should render the reason when reason is present', () => {
- props.job.callout_message = 'There is an unknown failure, please try again';
-
- vm = mountComponent(HeaderComponent, props);
-
- expect(vm.shouldRenderReason).toBe(true);
- });
- });
-
- describe('triggered job', () => {
- beforeEach(() => {
- vm = mountComponent(HeaderComponent, props);
- });
-
- it('should render provided job information', () => {
- expect(
- vm.$el
- .querySelector('.header-main-content')
- .textContent.replace(/\s+/g, ' ')
- .trim(),
- ).toEqual('failed Job #123 triggered 2 days ago by Foo');
- });
-
- it('should render new issue link', () => {
- expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual(
- props.job.new_issue_path,
- );
- });
- });
-
- describe('created job', () => {
- it('should render created key', () => {
- props.job.started = false;
- vm = mountComponent(HeaderComponent, props);
-
- expect(
- vm.$el
- .querySelector('.header-main-content')
- .textContent.replace(/\s+/g, ' ')
- .trim(),
- ).toEqual('failed Job #123 created 3 weeks ago by Foo');
- });
- });
-});
diff --git a/spec/javascripts/jobs/job_details_mediator_spec.js b/spec/javascripts/jobs/job_details_mediator_spec.js
deleted file mode 100644
index ca5c9cf87e4..00000000000
--- a/spec/javascripts/jobs/job_details_mediator_spec.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import JobMediator from '~/jobs/job_details_mediator';
-import job from './mock_data';
-
-describe('JobMediator', () => {
- let mediator;
- let mock;
-
- beforeEach(() => {
- mediator = new JobMediator({ endpoint: 'jobs/40291672.json' });
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('should set defaults', () => {
- expect(mediator.store).toBeDefined();
- expect(mediator.service).toBeDefined();
- expect(mediator.options).toEqual({ endpoint: 'jobs/40291672.json' });
- expect(mediator.state.isLoading).toEqual(false);
- });
-
- describe('request and store data', () => {
- beforeEach(() => {
- mock.onGet().reply(200, job, {});
- });
-
- it('should store received data', (done) => {
- mediator.fetchJob();
- setTimeout(() => {
- expect(mediator.store.state.job).toEqual(job);
- done();
- }, 0);
- });
- });
-});
diff --git a/spec/javascripts/jobs/job_store_spec.js b/spec/javascripts/jobs/job_store_spec.js
deleted file mode 100644
index d00faf29d1e..00000000000
--- a/spec/javascripts/jobs/job_store_spec.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import JobStore from '~/jobs/stores/job_store';
-import job from './mock_data';
-
-describe('Job Store', () => {
- let store;
-
- beforeEach(() => {
- store = new JobStore();
- });
-
- it('should set defaults', () => {
- expect(store.state.job).toEqual({});
- });
-
- describe('storeJob', () => {
- it('should store empty object if none is provided', () => {
- store.storeJob();
- expect(store.state.job).toEqual({});
- });
-
- it('should store provided argument', () => {
- store.storeJob(job);
- expect(store.state.job).toEqual(job);
- });
- });
-});
diff --git a/spec/javascripts/jobs/mixins/delayed_job_mixin_spec.js b/spec/javascripts/jobs/mixins/delayed_job_mixin_spec.js
new file mode 100644
index 00000000000..48a6b80b365
--- /dev/null
+++ b/spec/javascripts/jobs/mixins/delayed_job_mixin_spec.js
@@ -0,0 +1,93 @@
+import Vue from 'vue';
+import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('DelayedJobMixin', () => {
+ const delayedJobFixture = getJSONFixture('jobs/delayed.json');
+ const dummyComponent = Vue.extend({
+ mixins: [delayedJobMixin],
+ props: {
+ job: {
+ type: Object,
+ required: true,
+ },
+ },
+ template: '<div>{{ remainingTime }}</div>',
+ });
+
+ let vm;
+
+ beforeEach(() => {
+ jasmine.clock().install();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ jasmine.clock().uninstall();
+ });
+
+ describe('if job is empty object', () => {
+ beforeEach(() => {
+ vm = mountComponent(dummyComponent, {
+ job: {},
+ });
+ });
+
+ it('sets remaining time to 00:00:00', () => {
+ expect(vm.$el.innerText).toBe('00:00:00');
+ });
+
+ describe('after mounting', () => {
+ beforeEach(done => {
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('doe not update remaining time', () => {
+ expect(vm.$el.innerText).toBe('00:00:00');
+ });
+ });
+ });
+
+ describe('if job is delayed job', () => {
+ let remainingTimeInMilliseconds = 42000;
+
+ beforeEach(() => {
+ spyOn(Date, 'now').and.callFake(
+ () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingTimeInMilliseconds,
+ );
+ vm = mountComponent(dummyComponent, {
+ job: delayedJobFixture,
+ });
+ });
+
+ it('sets remaining time to 00:00:00', () => {
+ expect(vm.$el.innerText).toBe('00:00:00');
+ });
+
+ describe('after mounting', () => {
+ beforeEach(done => {
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('sets remaining time', () => {
+ expect(vm.$el.innerText).toBe('00:00:42');
+ });
+
+ it('updates remaining time', done => {
+ remainingTimeInMilliseconds = 41000;
+ jasmine.clock().tick(1000);
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.innerText).toBe('00:00:41');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js
index 8fdd9b309b7..0398f184c0a 100644
--- a/spec/javascripts/jobs/mock_data.js
+++ b/spec/javascripts/jobs/mock_data.js
@@ -1,3 +1,5 @@
+import { TEST_HOST } from 'spec/test_constants';
+
const threeWeeksAgo = new Date();
threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
@@ -19,8 +21,9 @@ export default {
label: 'passed',
group: 'success',
has_details: true,
- details_path: '/root/ci-mock/-/jobs/4757',
- favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ details_path: `${TEST_HOST}/root/ci-mock/-/jobs/4757`,
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
@@ -30,6 +33,7 @@ export default {
},
coverage: 20,
erased_at: threeWeeksAgo.toISOString(),
+ erased: false,
duration: 6.785563,
tags: ['tag'],
user: {
@@ -37,7 +41,8 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
erase_path: '/root/ci-mock/-/jobs/4757/erase',
@@ -54,7 +59,8 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
active: false,
@@ -78,7 +84,8 @@ export default {
group: 'success',
has_details: true,
details_path: '/root/ci-mock/pipelines/140',
- favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
},
duration: 6,
finished_at: '2017-06-01T17:32:00.042Z',
@@ -107,11 +114,14 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
- author_gravatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
- commit_url: 'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
+ author_gravatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ commit_url:
+ 'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
commit_path: '/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
},
},
@@ -124,4 +134,1031 @@ export default {
path: '/root/ci-mock/merge_requests/2',
},
raw_path: '/root/ci-mock/builds/4757/raw',
+ has_trace: true,
+};
+
+export const stages = [
+ {
+ name: 'build',
+ title: 'build: running',
+ groups: [
+ {
+ name: 'build:linux',
+ size: 1,
+ status: {
+ icon: 'status_pending',
+ text: 'pending',
+ label: 'pending',
+ group: 'pending',
+ tooltip: 'pending',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
+ illustration: {
+ image: 'illustrations/pending_job_empty.svg',
+ size: 'svg-430',
+ title: 'This job has not started yet',
+ content: 'This job is in pending state and is waiting to be picked by a runner',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 1180,
+ name: 'build:linux',
+ started: false,
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
+ cancel_path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
+ playable: false,
+ created_at: '2018-09-28T11:09:57.229Z',
+ updated_at: '2018-09-28T11:09:57.503Z',
+ status: {
+ icon: 'status_pending',
+ text: 'pending',
+ label: 'pending',
+ group: 'pending',
+ tooltip: 'pending',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
+ illustration: {
+ image: 'illustrations/pending_job_empty.svg',
+ size: 'svg-430',
+ title: 'This job has not started yet',
+ content: 'This job is in pending state and is waiting to be picked by a runner',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'build:osx',
+ size: 1,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 444,
+ name: 'build:osx',
+ started: '2018-05-18T05:32:20.655Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/444',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.364Z',
+ updated_at: '2018-05-18T15:32:54.364Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ ],
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ tooltip: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/pipelines/27#build',
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
+ },
+ path: '/gitlab-org/gitlab-shell/pipelines/27#build',
+ dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
+ },
+ {
+ name: 'test',
+ title: 'test: passed with warnings',
+ groups: [
+ {
+ name: 'jenkins',
+ size: 1,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: null,
+ group: 'success',
+ tooltip: null,
+ has_details: false,
+ details_path: null,
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ },
+ jobs: [
+ {
+ id: 459,
+ name: 'jenkins',
+ started: '2018-05-18T09:32:20.658Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/459',
+ playable: false,
+ created_at: '2018-05-18T15:32:55.330Z',
+ updated_at: '2018-05-18T15:32:55.330Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: null,
+ group: 'success',
+ tooltip: null,
+ has_details: false,
+ details_path: null,
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ },
+ },
+ ],
+ },
+ {
+ name: 'rspec:linux',
+ size: 3,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: false,
+ details_path: null,
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ },
+ jobs: [
+ {
+ id: 445,
+ name: 'rspec:linux 0 3',
+ started: '2018-05-18T07:32:20.655Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/445',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/445/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.425Z',
+ updated_at: '2018-05-18T15:32:54.425Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/445',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/445/retry',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 446,
+ name: 'rspec:linux 1 3',
+ started: '2018-05-18T07:32:20.655Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/446',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/446/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.506Z',
+ updated_at: '2018-05-18T15:32:54.506Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/446',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/446/retry',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 447,
+ name: 'rspec:linux 2 3',
+ started: '2018-05-18T07:32:20.656Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/447',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/447/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.572Z',
+ updated_at: '2018-05-18T15:32:54.572Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/447',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/447/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'rspec:osx',
+ size: 1,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/452',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 452,
+ name: 'rspec:osx',
+ started: '2018-05-18T07:32:20.657Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/452',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.920Z',
+ updated_at: '2018-05-18T15:32:54.920Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/452',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'rspec:windows',
+ size: 3,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: false,
+ details_path: null,
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ },
+ jobs: [
+ {
+ id: 448,
+ name: 'rspec:windows 0 3',
+ started: '2018-05-18T07:32:20.656Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/448',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/448/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.639Z',
+ updated_at: '2018-05-18T15:32:54.639Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/448',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/448/retry',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 449,
+ name: 'rspec:windows 1 3',
+ started: '2018-05-18T07:32:20.656Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/449',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/449/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.703Z',
+ updated_at: '2018-05-18T15:32:54.703Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/449',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/449/retry',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 451,
+ name: 'rspec:windows 2 3',
+ started: '2018-05-18T07:32:20.657Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/451',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/451/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.853Z',
+ updated_at: '2018-05-18T15:32:54.853Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/451',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/451/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'spinach:linux',
+ size: 1,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/453',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 453,
+ name: 'spinach:linux',
+ started: '2018-05-18T07:32:20.657Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/453',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.993Z',
+ updated_at: '2018-05-18T15:32:54.993Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/453',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'spinach:osx',
+ size: 1,
+ status: {
+ icon: 'status_warning',
+ text: 'failed',
+ label: 'failed (allowed to fail)',
+ group: 'failed_with_warnings',
+ tooltip: 'failed - (unknown failure) (allowed to fail)',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/454',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 454,
+ name: 'spinach:osx',
+ started: '2018-05-18T07:32:20.657Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/454',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:55.053Z',
+ updated_at: '2018-05-18T15:32:55.053Z',
+ status: {
+ icon: 'status_warning',
+ text: 'failed',
+ label: 'failed (allowed to fail)',
+ group: 'failed_with_warnings',
+ tooltip: 'failed - (unknown failure) (allowed to fail)',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/454',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
+ method: 'post',
+ },
+ },
+ callout_message: 'There is an unknown failure, please try again',
+ recoverable: true,
+ },
+ ],
+ },
+ ],
+ status: {
+ icon: 'status_warning',
+ text: 'passed',
+ label: 'passed with warnings',
+ group: 'success_with_warnings',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/pipelines/27#test',
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ },
+ path: '/gitlab-org/gitlab-shell/pipelines/27#test',
+ dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=test',
+ },
+ {
+ name: 'deploy',
+ title: 'deploy: running',
+ groups: [
+ {
+ name: 'production',
+ size: 1,
+ status: {
+ icon: 'status_created',
+ text: 'created',
+ label: 'created',
+ group: 'created',
+ tooltip: 'created',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/457',
+ illustration: {
+ image: 'illustrations/job_not_triggered.svg',
+ size: 'svg-306',
+ title: 'This job has not been triggered yet',
+ content:
+ 'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 457,
+ name: 'production',
+ started: false,
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/457',
+ cancel_path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
+ playable: false,
+ created_at: '2018-05-18T15:32:55.259Z',
+ updated_at: '2018-09-28T11:09:57.454Z',
+ status: {
+ icon: 'status_created',
+ text: 'created',
+ label: 'created',
+ group: 'created',
+ tooltip: 'created',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/457',
+ illustration: {
+ image: 'illustrations/job_not_triggered.svg',
+ size: 'svg-306',
+ title: 'This job has not been triggered yet',
+ content:
+ 'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'staging',
+ size: 1,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/455',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 455,
+ name: 'staging',
+ started: '2018-05-18T09:32:20.658Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/455',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:55.119Z',
+ updated_at: '2018-05-18T15:32:55.119Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/455',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'stop staging',
+ size: 1,
+ status: {
+ icon: 'status_created',
+ text: 'created',
+ label: 'created',
+ group: 'created',
+ tooltip: 'created',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/456',
+ illustration: {
+ image: 'illustrations/job_not_triggered.svg',
+ size: 'svg-306',
+ title: 'This job has not been triggered yet',
+ content:
+ 'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 456,
+ name: 'stop staging',
+ started: false,
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/456',
+ cancel_path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
+ playable: false,
+ created_at: '2018-05-18T15:32:55.205Z',
+ updated_at: '2018-09-28T11:09:57.396Z',
+ status: {
+ icon: 'status_created',
+ text: 'created',
+ label: 'created',
+ group: 'created',
+ tooltip: 'created',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/456',
+ illustration: {
+ image: 'illustrations/job_not_triggered.svg',
+ size: 'svg-306',
+ title: 'This job has not been triggered yet',
+ content:
+ 'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ ],
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ tooltip: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/pipelines/27#deploy',
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
+ },
+ path: '/gitlab-org/gitlab-shell/pipelines/27#deploy',
+ dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=deploy',
+ },
+ {
+ name: 'notify',
+ title: 'notify: manual action',
+ groups: [
+ {
+ name: 'slack',
+ size: 1,
+ status: {
+ icon: 'status_manual',
+ text: 'manual',
+ label: 'manual play action',
+ group: 'manual',
+ tooltip: 'manual action',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/458',
+ illustration: {
+ image: 'illustrations/manual_action.svg',
+ size: 'svg-394',
+ title: 'This job requires a manual action',
+ content:
+ 'This job depends on a user to trigger its process. Often they are used to deploy code to production environments',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
+ action: {
+ icon: 'play',
+ title: 'Play',
+ path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 458,
+ name: 'slack',
+ started: null,
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/458',
+ play_path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
+ playable: true,
+ created_at: '2018-05-18T15:32:55.303Z',
+ updated_at: '2018-05-18T15:34:08.535Z',
+ status: {
+ icon: 'status_manual',
+ text: 'manual',
+ label: 'manual play action',
+ group: 'manual',
+ tooltip: 'manual action',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/458',
+ illustration: {
+ image: 'illustrations/manual_action.svg',
+ size: 'svg-394',
+ title: 'This job requires a manual action',
+ content:
+ 'This job depends on a user to trigger its process. Often they are used to deploy code to production environments',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
+ action: {
+ icon: 'play',
+ title: 'Play',
+ path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ ],
+ status: {
+ icon: 'status_manual',
+ text: 'manual',
+ label: 'manual action',
+ group: 'manual',
+ tooltip: 'manual action',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/pipelines/27#notify',
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
+ },
+ path: '/gitlab-org/gitlab-shell/pipelines/27#notify',
+ dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=notify',
+ },
+];
+
+export const jobsInStage = {
+ name: 'build',
+ title: 'build: running',
+ latest_statuses: [
+ {
+ id: 1180,
+ name: 'build:linux',
+ started: false,
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
+ cancel_path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
+ playable: false,
+ created_at: '2018-09-28T11:09:57.229Z',
+ updated_at: '2018-09-28T11:09:57.503Z',
+ status: {
+ icon: 'status_pending',
+ text: 'pending',
+ label: 'pending',
+ group: 'pending',
+ tooltip: 'pending',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
+ illustration: {
+ image: 'illustrations/pending_job_empty.svg',
+ size: 'svg-430',
+ title: 'This job has not started yet',
+ content: 'This job is in pending state and is waiting to be picked by a runner',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 444,
+ name: 'build:osx',
+ started: '2018-05-18T05:32:20.655Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/444',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.364Z',
+ updated_at: '2018-05-18T15:32:54.364Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ retried: [
+ {
+ id: 443,
+ name: 'build:linux',
+ started: '2018-05-18T06:32:20.655Z',
+ build_path: '/gitlab-org/gitlab-shell/-/jobs/443',
+ retry_path: '/gitlab-org/gitlab-shell/-/jobs/443/retry',
+ playable: false,
+ created_at: '2018-05-18T15:32:54.296Z',
+ updated_at: '2018-05-18T15:32:54.296Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed (retried)',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/-/jobs/443',
+ illustration: {
+ image: 'illustrations/skipped-job_empty.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/gitlab-org/gitlab-shell/-/jobs/443/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ tooltip: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-shell/pipelines/27#build',
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
+ },
+ path: '/gitlab-org/gitlab-shell/pipelines/27#build',
+ dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
};
diff --git a/spec/javascripts/jobs/sidebar_detail_row_spec.js b/spec/javascripts/jobs/sidebar_detail_row_spec.js
deleted file mode 100644
index e6bfb0c4adc..00000000000
--- a/spec/javascripts/jobs/sidebar_detail_row_spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import Vue from 'vue';
-import sidebarDetailRow from '~/jobs/components/sidebar_detail_row.vue';
-
-describe('Sidebar detail row', () => {
- let SidebarDetailRow;
- let vm;
-
- beforeEach(() => {
- SidebarDetailRow = Vue.extend(sidebarDetailRow);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('should render no title', () => {
- vm = new SidebarDetailRow({
- propsData: {
- value: 'this is the value',
- },
- }).$mount();
-
- expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual('this is the value');
- });
-
- beforeEach(() => {
- vm = new SidebarDetailRow({
- propsData: {
- title: 'this is the title',
- value: 'this is the value',
- },
- }).$mount();
- });
-
- it('should render provided title and value', () => {
- expect(
- vm.$el.textContent.replace(/\s+/g, ' ').trim(),
- ).toEqual('this is the title: this is the value');
- });
-
- describe('when helpUrl not provided', () => {
- it('should not render help', () => {
- expect(vm.$el.querySelector('.help-button')).toBeNull();
- });
- });
-
- describe('when helpUrl provided', () => {
- beforeEach(() => {
- vm = new SidebarDetailRow({
- propsData: {
- helpUrl: 'help url',
- value: 'foo',
- },
- }).$mount();
- });
-
- it('should render help', () => {
- expect(vm.$el.querySelector('.help-button a').getAttribute('href')).toEqual('help url');
- });
- });
-});
diff --git a/spec/javascripts/jobs/sidebar_details_block_spec.js b/spec/javascripts/jobs/sidebar_details_block_spec.js
deleted file mode 100644
index 21ef5650b80..00000000000
--- a/spec/javascripts/jobs/sidebar_details_block_spec.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import Vue from 'vue';
-import sidebarDetailsBlock from '~/jobs/components/sidebar_details_block.vue';
-import job from './mock_data';
-import mountComponent from '../helpers/vue_mount_component_helper';
-
-describe('Sidebar details block', () => {
- let SidebarComponent;
- let vm;
-
- function trimWhitespace(element) {
- return element.textContent.replace(/\s+/g, ' ').trim();
- }
-
- beforeEach(() => {
- SidebarComponent = Vue.extend(sidebarDetailsBlock);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('when it is loading', () => {
- it('should render a loading spinner', () => {
- vm = mountComponent(SidebarComponent, {
- job: {},
- isLoading: true,
- });
- expect(vm.$el.querySelector('.fa-spinner')).toBeDefined();
- });
- });
-
- describe('when there is no retry path retry', () => {
- it('should not render a retry button', () => {
- vm = mountComponent(SidebarComponent, {
- job: {},
- isLoading: false,
- });
-
- expect(vm.$el.querySelector('.js-retry-job')).toBeNull();
- });
- });
-
- describe('without terminal path', () => {
- it('does not render terminal link', () => {
- vm = mountComponent(SidebarComponent, {
- job,
- isLoading: false,
- });
-
- expect(vm.$el.querySelector('.js-terminal-link')).toBeNull();
- });
- });
-
- describe('with terminal path', () => {
- it('renders terminal link', () => {
- vm = mountComponent(SidebarComponent, {
- job,
- isLoading: false,
- terminalPath: 'job/43123/terminal',
- });
-
- expect(vm.$el.querySelector('.js-terminal-link')).not.toBeNull();
- });
- });
-
- beforeEach(() => {
- vm = mountComponent(SidebarComponent, {
- job,
- isLoading: false,
- });
- });
-
- describe('actions', () => {
- it('should render link to new issue', () => {
- expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual(
- job.new_issue_path,
- );
- expect(vm.$el.querySelector('.js-new-issue').textContent.trim()).toEqual('New issue');
- });
-
- it('should render link to retry job', () => {
- expect(vm.$el.querySelector('.js-retry-job').getAttribute('href')).toEqual(job.retry_path);
- });
-
- it('should render link to cancel job', () => {
- expect(vm.$el.querySelector('.js-cancel-job').getAttribute('href')).toEqual(job.cancel_path);
- });
- });
-
- describe('information', () => {
- it('should render merge request link', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-mr'))).toEqual('Merge Request: !2');
-
- expect(vm.$el.querySelector('.js-job-mr a').getAttribute('href')).toEqual(
- job.merge_request.path,
- );
- });
-
- it('should render job duration', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-duration'))).toEqual(
- 'Duration: 6 seconds',
- );
- });
-
- it('should render erased date', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-erased'))).toEqual('Erased: 3 weeks ago');
- });
-
- it('should render finished date', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-finished'))).toEqual(
- 'Finished: 3 weeks ago',
- );
- });
-
- it('should render queued date', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-queued'))).toEqual('Queued: 9 seconds');
- });
-
- it('should render runner ID', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-runner'))).toEqual(
- 'Runner: local ci runner (#1)',
- );
- });
-
- it('should render timeout information', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-timeout'))).toEqual(
- 'Timeout: 1m 40s (from runner)',
- );
- });
-
- it('should render coverage', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-coverage'))).toEqual('Coverage: 20%');
- });
-
- it('should render tags', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-tags'))).toEqual('Tags: tag');
- });
- });
-});
diff --git a/spec/javascripts/jobs/store/actions_spec.js b/spec/javascripts/jobs/store/actions_spec.js
new file mode 100644
index 00000000000..77b44995b12
--- /dev/null
+++ b/spec/javascripts/jobs/store/actions_spec.js
@@ -0,0 +1,512 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import {
+ setJobEndpoint,
+ setTraceOptions,
+ clearEtagPoll,
+ stopPolling,
+ requestJob,
+ fetchJob,
+ receiveJobSuccess,
+ receiveJobError,
+ scrollTop,
+ scrollBottom,
+ requestTrace,
+ fetchTrace,
+ stopPollingTrace,
+ receiveTraceSuccess,
+ receiveTraceError,
+ requestStages,
+ fetchStages,
+ receiveStagesSuccess,
+ receiveStagesError,
+ requestJobsForStage,
+ fetchJobsForStage,
+ receiveJobsForStageSuccess,
+ receiveJobsForStageError,
+ hideSidebar,
+ showSidebar,
+ toggleSidebar,
+} from '~/jobs/store/actions';
+import state from '~/jobs/store/state';
+import * as types from '~/jobs/store/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('Job State actions', () => {
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe('setJobEndpoint', () => {
+ it('should commit SET_JOB_ENDPOINT mutation', done => {
+ testAction(
+ setJobEndpoint,
+ 'job/872324.json',
+ mockedState,
+ [{ type: types.SET_JOB_ENDPOINT, payload: 'job/872324.json' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setTraceOptions', () => {
+ it('should commit SET_TRACE_OPTIONS mutation', done => {
+ testAction(
+ setTraceOptions,
+ { pagePath: 'job/872324/trace.json' },
+ mockedState,
+ [{ type: types.SET_TRACE_OPTIONS, payload: { pagePath: 'job/872324/trace.json' } }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('hideSidebar', () => {
+ it('should commit HIDE_SIDEBAR mutation', done => {
+ testAction(hideSidebar, null, mockedState, [{ type: types.HIDE_SIDEBAR }], [], done);
+ });
+ });
+
+ describe('showSidebar', () => {
+ it('should commit HIDE_SIDEBAR mutation', done => {
+ testAction(showSidebar, null, mockedState, [{ type: types.SHOW_SIDEBAR }], [], done);
+ });
+ });
+
+ describe('toggleSidebar', () => {
+ describe('when isSidebarOpen is true', () => {
+ it('should dispatch hideSidebar', done => {
+ testAction(toggleSidebar, null, mockedState, [], [{ type: 'hideSidebar' }], done);
+ });
+ });
+
+ describe('when isSidebarOpen is false', () => {
+ it('should dispatch showSidebar', done => {
+ mockedState.isSidebarOpen = false;
+
+ testAction(toggleSidebar, null, mockedState, [], [{ type: 'showSidebar' }], done);
+ });
+ });
+ });
+
+ describe('requestJob', () => {
+ it('should commit REQUEST_JOB mutation', done => {
+ testAction(requestJob, null, mockedState, [{ type: types.REQUEST_JOB }], [], done);
+ });
+ });
+
+ describe('fetchJob', () => {
+ let mock;
+
+ beforeEach(() => {
+ mockedState.jobEndpoint = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ stopPolling();
+ clearEtagPoll();
+ });
+
+ describe('success', () => {
+ it('dispatches requestJob and receiveJobSuccess ', done => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { id: 121212, name: 'karma' });
+
+ testAction(
+ fetchJob,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestJob',
+ },
+ {
+ payload: { id: 121212, name: 'karma' },
+ type: 'receiveJobSuccess',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+ });
+
+ it('dispatches requestJob and receiveJobError ', done => {
+ testAction(
+ fetchJob,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestJob',
+ },
+ {
+ type: 'receiveJobError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveJobSuccess', () => {
+ it('should commit RECEIVE_JOB_SUCCESS mutation', done => {
+ testAction(
+ receiveJobSuccess,
+ { id: 121232132 },
+ mockedState,
+ [{ type: types.RECEIVE_JOB_SUCCESS, payload: { id: 121232132 } }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveJobError', () => {
+ it('should commit RECEIVE_JOB_ERROR mutation', done => {
+ testAction(receiveJobError, null, mockedState, [{ type: types.RECEIVE_JOB_ERROR }], [], done);
+ });
+ });
+
+ describe('scrollTop', () => {
+ it('should dispatch toggleScrollButtons action', done => {
+ testAction(scrollTop, null, mockedState, [], [{ type: 'toggleScrollButtons' }], done);
+ });
+ });
+
+ describe('scrollBottom', () => {
+ it('should dispatch toggleScrollButtons action', done => {
+ testAction(scrollBottom, null, mockedState, [], [{ type: 'toggleScrollButtons' }], done);
+ });
+ });
+
+ describe('requestTrace', () => {
+ it('should commit REQUEST_TRACE mutation', done => {
+ testAction(requestTrace, null, mockedState, [{ type: types.REQUEST_TRACE }], [], done);
+ });
+ });
+
+ describe('fetchTrace', () => {
+ let mock;
+
+ beforeEach(() => {
+ mockedState.traceEndpoint = `${TEST_HOST}/endpoint`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ stopPolling();
+ clearEtagPoll();
+ });
+
+ describe('success', () => {
+ it('dispatches requestTrace, receiveTraceSuccess and stopPollingTrace when job is complete', done => {
+ mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, {
+ html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
+ complete: true,
+ });
+
+ testAction(
+ fetchTrace,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'toggleScrollisInBottom',
+ payload: true,
+ },
+ {
+ payload: {
+ html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
+ complete: true,
+ },
+ type: 'receiveTraceSuccess',
+ },
+ {
+ type: 'stopPollingTrace',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint/trace.json`).reply(500);
+ });
+
+ it('dispatches requestTrace and receiveTraceError ', done => {
+ testAction(
+ fetchTrace,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'receiveTraceError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('stopPollingTrace', () => {
+ it('should commit STOP_POLLING_TRACE mutation ', done => {
+ testAction(
+ stopPollingTrace,
+ null,
+ mockedState,
+ [{ type: types.STOP_POLLING_TRACE }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveTraceSuccess', () => {
+ it('should commit RECEIVE_TRACE_SUCCESS mutation ', done => {
+ testAction(
+ receiveTraceSuccess,
+ 'hello world',
+ mockedState,
+ [{ type: types.RECEIVE_TRACE_SUCCESS, payload: 'hello world' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveTraceError', () => {
+ it('should commit RECEIVE_TRACE_ERROR mutation ', done => {
+ testAction(
+ receiveTraceError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_TRACE_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestStages', () => {
+ it('should commit REQUEST_STAGES mutation ', done => {
+ testAction(requestStages, null, mockedState, [{ type: types.REQUEST_STAGES }], [], done);
+ });
+ });
+
+ describe('fetchStages', () => {
+ let mock;
+
+ beforeEach(() => {
+ mockedState.job.pipeline = {
+ path: `${TEST_HOST}/endpoint`,
+ };
+ mockedState.selectedStage = 'deploy';
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('success', () => {
+ it('dispatches requestStages and receiveStagesSuccess, fetchJobsForStage ', done => {
+ mock
+ .onGet(`${TEST_HOST}/endpoint.json`)
+ .replyOnce(200, { details: { stages: [{ name: 'build' }, { name: 'deploy' }] } });
+
+ testAction(
+ fetchStages,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestStages',
+ },
+ {
+ payload: [{ name: 'build' }, { name: 'deploy' }],
+ type: 'receiveStagesSuccess',
+ },
+ {
+ payload: { name: 'deploy' },
+ type: 'fetchJobsForStage',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+ });
+
+ it('dispatches requestStages and receiveStagesError ', done => {
+ testAction(
+ fetchStages,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestStages',
+ },
+ {
+ type: 'receiveStagesError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveStagesSuccess', () => {
+ it('should commit RECEIVE_STAGES_SUCCESS mutation ', done => {
+ testAction(
+ receiveStagesSuccess,
+ {},
+ mockedState,
+ [{ type: types.RECEIVE_STAGES_SUCCESS, payload: {} }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveStagesError', () => {
+ it('should commit RECEIVE_STAGES_ERROR mutation ', done => {
+ testAction(
+ receiveStagesError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_STAGES_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestJobsForStage', () => {
+ it('should commit REQUEST_JOBS_FOR_STAGE mutation ', done => {
+ testAction(
+ requestJobsForStage,
+ { name: 'deploy' },
+ mockedState,
+ [{ type: types.REQUEST_JOBS_FOR_STAGE, payload: { name: 'deploy' } }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchJobsForStage', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('success', () => {
+ it('dispatches requestJobsForStage and receiveJobsForStageSuccess ', done => {
+ mock
+ .onGet(`${TEST_HOST}/jobs.json`)
+ .replyOnce(200, { latest_statuses: [{ id: 121212, name: 'build' }], retried: [] });
+
+ testAction(
+ fetchJobsForStage,
+ { dropdown_path: `${TEST_HOST}/jobs.json` },
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestJobsForStage',
+ payload: { dropdown_path: `${TEST_HOST}/jobs.json` },
+ },
+ {
+ payload: [{ id: 121212, name: 'build' }],
+ type: 'receiveJobsForStageSuccess',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/jobs.json`).reply(500);
+ });
+
+ it('dispatches requestJobsForStage and receiveJobsForStageError', done => {
+ testAction(
+ fetchJobsForStage,
+ { dropdown_path: `${TEST_HOST}/jobs.json` },
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestJobsForStage',
+ payload: { dropdown_path: `${TEST_HOST}/jobs.json` },
+ },
+ {
+ type: 'receiveJobsForStageError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveJobsForStageSuccess', () => {
+ it('should commit RECEIVE_JOBS_FOR_STAGE_SUCCESS mutation ', done => {
+ testAction(
+ receiveJobsForStageSuccess,
+ [{ id: 121212, name: 'karma' }],
+ mockedState,
+ [{ type: types.RECEIVE_JOBS_FOR_STAGE_SUCCESS, payload: [{ id: 121212, name: 'karma' }] }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveJobsForStageError', () => {
+ it('should commit RECEIVE_JOBS_FOR_STAGE_ERROR mutation ', done => {
+ testAction(
+ receiveJobsForStageError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_JOBS_FOR_STAGE_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/store/getters_spec.js b/spec/javascripts/jobs/store/getters_spec.js
new file mode 100644
index 00000000000..4195d9d3680
--- /dev/null
+++ b/spec/javascripts/jobs/store/getters_spec.js
@@ -0,0 +1,212 @@
+import * as getters from '~/jobs/store/getters';
+import state from '~/jobs/store/state';
+
+describe('Job Store Getters', () => {
+ let localState;
+
+ beforeEach(() => {
+ localState = state();
+ });
+
+ describe('headerActions', () => {
+ describe('with new issue path', () => {
+ it('returns an array with action to create a new issue', () => {
+ localState.job.new_issue_path = 'issues/new';
+
+ expect(getters.headerActions(localState)).toEqual([
+ {
+ label: 'New issue',
+ path: localState.job.new_issue_path,
+ cssClass:
+ 'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block',
+ type: 'link',
+ },
+ ]);
+ });
+ });
+
+ describe('without new issue path', () => {
+ it('returns an empty array', () => {
+ expect(getters.headerActions(localState)).toEqual([]);
+ });
+ });
+ });
+
+ describe('headerTime', () => {
+ describe('when the job has started key', () => {
+ it('returns started key', () => {
+ const started = '2018-08-31T16:20:49.023Z';
+ localState.job.started = started;
+
+ expect(getters.headerTime(localState)).toEqual(started);
+ });
+ });
+
+ describe('when the job does not have started key', () => {
+ it('returns created_at key', () => {
+ const created = '2018-08-31T16:20:49.023Z';
+ localState.job.created_at = created;
+
+ expect(getters.headerTime(localState)).toEqual(created);
+ });
+ });
+ });
+
+ describe('shouldRenderCalloutMessage', () => {
+ describe('with status and callout message', () => {
+ it('returns true', () => {
+ localState.job.callout_message = 'Callout message';
+ localState.job.status = { icon: 'passed' };
+
+ expect(getters.shouldRenderCalloutMessage(localState)).toEqual(true);
+ });
+ });
+
+ describe('without status & with callout message', () => {
+ it('returns false', () => {
+ localState.job.callout_message = 'Callout message';
+
+ expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false);
+ });
+ });
+
+ describe('with status & without callout message', () => {
+ it('returns false', () => {
+ localState.job.status = { icon: 'passed' };
+
+ expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false);
+ });
+ });
+ });
+
+ describe('shouldRenderTriggeredLabel', () => {
+ describe('when started equals null', () => {
+ it('returns false', () => {
+ localState.job.started = null;
+
+ expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(false);
+ });
+ });
+
+ describe('when started equals string', () => {
+ it('returns true', () => {
+ localState.job.started = '2018-08-31T16:20:49.023Z';
+
+ expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(true);
+ });
+ });
+ });
+
+ describe('hasEnvironment', () => {
+ describe('without `deployment_status`', () => {
+ it('returns false', () => {
+ expect(getters.hasEnvironment(localState)).toEqual(false);
+ });
+ });
+
+ describe('with an empty object for `deployment_status`', () => {
+ it('returns false', () => {
+ localState.job.deployment_status = {};
+
+ expect(getters.hasEnvironment(localState)).toEqual(false);
+ });
+ });
+
+ describe('when `deployment_status` is defined and not empty', () => {
+ it('returns true', () => {
+ localState.job.deployment_status = {
+ status: 'creating',
+ environment: {
+ last_deployment: {},
+ },
+ };
+
+ expect(getters.hasEnvironment(localState)).toEqual(true);
+ });
+ });
+ });
+
+ describe('hasTrace', () => {
+ describe('when has_trace is true', () => {
+ it('returns true', () => {
+ localState.job.has_trace = true;
+ localState.job.status = {};
+
+ expect(getters.hasTrace(localState)).toEqual(true);
+ });
+ });
+
+ describe('when job is running', () => {
+ it('returns true', () => {
+ localState.job.has_trace = false;
+ localState.job.status = { group: 'running' };
+
+ expect(getters.hasTrace(localState)).toEqual(true);
+ });
+ });
+
+ describe('when has_trace is false and job is not running', () => {
+ it('returns false', () => {
+ localState.job.has_trace = false;
+ localState.job.status = { group: 'pending' };
+
+ expect(getters.hasTrace(localState)).toEqual(false);
+ });
+ });
+ });
+
+ describe('emptyStateIllustration', () => {
+ describe('with defined illustration', () => {
+ it('returns the state illustration object', () => {
+ localState.job.status = {
+ illustration: {
+ path: 'foo',
+ },
+ };
+
+ expect(getters.emptyStateIllustration(localState)).toEqual({ path: 'foo' });
+ });
+ });
+
+ describe('when illustration is not defined', () => {
+ it('returns an empty object', () => {
+ expect(getters.emptyStateIllustration(localState)).toEqual({});
+ });
+ });
+ });
+
+ describe('hasRunnersForProject', () => {
+ describe('with available and offline runners', () => {
+ it('returns true', () => {
+ localState.job.runners = {
+ available: true,
+ online: false,
+ };
+
+ expect(getters.hasRunnersForProject(localState)).toEqual(true);
+ });
+ });
+
+ describe('with non available runners', () => {
+ it('returns false', () => {
+ localState.job.runners = {
+ available: false,
+ online: false,
+ };
+
+ expect(getters.hasRunnersForProject(localState)).toEqual(false);
+ });
+ });
+
+ describe('with online runners', () => {
+ it('returns false', () => {
+ localState.job.runners = {
+ available: false,
+ online: true,
+ };
+
+ expect(getters.hasRunnersForProject(localState)).toEqual(false);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/store/helpers.js b/spec/javascripts/jobs/store/helpers.js
new file mode 100644
index 00000000000..81a769b4a6e
--- /dev/null
+++ b/spec/javascripts/jobs/store/helpers.js
@@ -0,0 +1,6 @@
+import state from '~/jobs/store/state';
+
+// eslint-disable-next-line import/prefer-default-export
+export const resetStore = store => {
+ store.replaceState(state());
+};
diff --git a/spec/javascripts/jobs/store/mutations_spec.js b/spec/javascripts/jobs/store/mutations_spec.js
new file mode 100644
index 00000000000..d7908efcf13
--- /dev/null
+++ b/spec/javascripts/jobs/store/mutations_spec.js
@@ -0,0 +1,230 @@
+import state from '~/jobs/store/state';
+import mutations from '~/jobs/store/mutations';
+import * as types from '~/jobs/store/mutation_types';
+
+describe('Jobs Store Mutations', () => {
+ let stateCopy;
+
+ const html =
+ 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- : Writing /builds/ab89e95b0fa0b9272ea0c797b76908f24d36992630e9325273a4ce3.png<br>I';
+
+ beforeEach(() => {
+ stateCopy = state();
+ });
+
+ describe('SET_JOB_ENDPOINT', () => {
+ it('should set jobEndpoint', () => {
+ mutations[types.SET_JOB_ENDPOINT](stateCopy, 'job/21312321.json');
+
+ expect(stateCopy.jobEndpoint).toEqual('job/21312321.json');
+ });
+ });
+
+ describe('HIDE_SIDEBAR', () => {
+ it('should set isSidebarOpen to false', () => {
+ mutations[types.HIDE_SIDEBAR](stateCopy);
+
+ expect(stateCopy.isSidebarOpen).toEqual(false);
+ });
+ });
+
+ describe('SHOW_SIDEBAR', () => {
+ it('should set isSidebarOpen to true', () => {
+ mutations[types.SHOW_SIDEBAR](stateCopy);
+
+ expect(stateCopy.isSidebarOpen).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_TRACE_SUCCESS', () => {
+ describe('when trace has state', () => {
+ it('sets traceState', () => {
+ const stateLog =
+ 'eyJvZmZzZXQiOjczNDQ1MSwibl9vcGVuX3RhZ3MiOjAsImZnX2NvbG9yIjpudWxsLCJiZ19jb2xvciI6bnVsbCwic3R5bGVfbWFzayI6MH0=';
+ mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
+ state: stateLog,
+ });
+
+ expect(stateCopy.traceState).toEqual(stateLog);
+ });
+ });
+
+ describe('when traceSize is smaller than the total size', () => {
+ it('sets isTraceSizeVisible to true', () => {
+ mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, { total: 51184600, size: 1231 });
+
+ expect(stateCopy.isTraceSizeVisible).toEqual(true);
+ });
+ });
+
+ describe('when traceSize is bigger than the total size', () => {
+ it('sets isTraceSizeVisible to false', () => {
+ const copy = Object.assign({}, stateCopy, { traceSize: 5118460, size: 2321312 });
+
+ mutations[types.RECEIVE_TRACE_SUCCESS](copy, { total: 511846 });
+
+ expect(copy.isTraceSizeVisible).toEqual(false);
+ });
+ });
+
+ it('sets trace, trace size and isTraceComplete', () => {
+ mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
+ append: true,
+ html,
+ size: 511846,
+ complete: true,
+ });
+
+ expect(stateCopy.trace).toEqual(html);
+ expect(stateCopy.traceSize).toEqual(511846);
+ expect(stateCopy.isTraceComplete).toEqual(true);
+ });
+ });
+
+ describe('STOP_POLLING_TRACE', () => {
+ it('sets isTraceComplete to true', () => {
+ mutations[types.STOP_POLLING_TRACE](stateCopy);
+
+ expect(stateCopy.isTraceComplete).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_TRACE_ERROR', () => {
+ it('resets trace state and sets error to true', () => {
+ mutations[types.RECEIVE_TRACE_ERROR](stateCopy);
+
+ expect(stateCopy.isTraceComplete).toEqual(true);
+ });
+ });
+
+ describe('REQUEST_JOB', () => {
+ it('sets isLoading to true', () => {
+ mutations[types.REQUEST_JOB](stateCopy);
+
+ expect(stateCopy.isLoading).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_JOB_SUCCESS', () => {
+ it('sets is loading to false', () => {
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 });
+
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('sets hasError to false', () => {
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 });
+
+ expect(stateCopy.hasError).toEqual(false);
+ });
+
+ it('sets job data', () => {
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 });
+
+ expect(stateCopy.job).toEqual({ id: 1312321 });
+ });
+
+ it('sets selectedStage when the selectedStage is empty', () => {
+ expect(stateCopy.selectedStage).toEqual('');
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321, stage: 'deploy' });
+
+ expect(stateCopy.selectedStage).toEqual('deploy');
+ });
+
+ it('does not set selectedStage when the selectedStage is not More', () => {
+ stateCopy.selectedStage = 'notify';
+
+ expect(stateCopy.selectedStage).toEqual('notify');
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321, stage: 'deploy' });
+
+ expect(stateCopy.selectedStage).toEqual('notify');
+ });
+ });
+
+ describe('RECEIVE_JOB_ERROR', () => {
+ it('resets job data', () => {
+ mutations[types.RECEIVE_JOB_ERROR](stateCopy);
+
+ expect(stateCopy.isLoading).toEqual(false);
+ expect(stateCopy.job).toEqual({});
+ });
+ });
+
+ describe('REQUEST_STAGES', () => {
+ it('sets isLoadingStages to true', () => {
+ mutations[types.REQUEST_STAGES](stateCopy);
+
+ expect(stateCopy.isLoadingStages).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_STAGES_SUCCESS', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_STAGES_SUCCESS](stateCopy, [{ name: 'build' }]);
+ });
+
+ it('sets isLoadingStages to false', () => {
+ expect(stateCopy.isLoadingStages).toEqual(false);
+ });
+
+ it('sets stages', () => {
+ expect(stateCopy.stages).toEqual([{ name: 'build' }]);
+ });
+ });
+
+ describe('RECEIVE_STAGES_ERROR', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_STAGES_ERROR](stateCopy);
+ });
+
+ it('sets isLoadingStages to false', () => {
+ expect(stateCopy.isLoadingStages).toEqual(false);
+ });
+
+ it('resets stages', () => {
+ expect(stateCopy.stages).toEqual([]);
+ });
+ });
+
+ describe('REQUEST_JOBS_FOR_STAGE', () => {
+ it('sets isLoadingStages to true', () => {
+ mutations[types.REQUEST_JOBS_FOR_STAGE](stateCopy, { name: 'deploy' });
+
+ expect(stateCopy.isLoadingJobs).toEqual(true);
+ });
+
+ it('sets selectedStage', () => {
+ mutations[types.REQUEST_JOBS_FOR_STAGE](stateCopy, { name: 'deploy' });
+
+ expect(stateCopy.selectedStage).toEqual('deploy');
+ });
+ });
+
+ describe('RECEIVE_JOBS_FOR_STAGE_SUCCESS', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_JOBS_FOR_STAGE_SUCCESS](stateCopy, [{ name: 'karma' }]);
+ });
+
+ it('sets isLoadingJobs to false', () => {
+ expect(stateCopy.isLoadingJobs).toEqual(false);
+ });
+
+ it('sets jobs', () => {
+ expect(stateCopy.jobs).toEqual([{ name: 'karma' }]);
+ });
+ });
+
+ describe('RECEIVE_JOBS_FOR_STAGE_ERROR', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_JOBS_FOR_STAGE_ERROR](stateCopy);
+ });
+
+ it('sets isLoadingJobs to false', () => {
+ expect(stateCopy.isLoadingJobs).toEqual(false);
+ });
+
+ it('resets jobs', () => {
+ expect(stateCopy.jobs).toEqual([]);
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/stuck_block_spec.js b/spec/javascripts/jobs/stuck_block_spec.js
deleted file mode 100644
index 4e2108dfdfb..00000000000
--- a/spec/javascripts/jobs/stuck_block_spec.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import Vue from 'vue';
-import component from '~/jobs/components/stuck_block.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
-
-describe('Stuck Block Job component', () => {
- const Component = Vue.extend(component);
- let vm;
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('with no runners for project', () => {
- beforeEach(() => {
- vm = mountComponent(Component, {
- hasNoRunnersForProject: true,
- runnersPath: '/root/project/runners#js-runners-settings',
- });
- });
-
- it('renders only information about project not having runners', () => {
- expect(vm.$el.querySelector('.js-stuck-no-runners')).not.toBeNull();
- expect(vm.$el.querySelector('.js-stuck-with-tags')).toBeNull();
- expect(vm.$el.querySelector('.js-stuck-no-active-runner')).toBeNull();
- });
-
- it('renders link to runners page', () => {
- expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
- '/root/project/runners#js-runners-settings',
- );
- });
- });
-
- describe('with tags', () => {
- beforeEach(() => {
- vm = mountComponent(Component, {
- hasNoRunnersForProject: false,
- tags: ['docker', 'gitlab-org'],
- runnersPath: '/root/project/runners#js-runners-settings',
- });
- });
-
- it('renders information about the tags not being set', () => {
- expect(vm.$el.querySelector('.js-stuck-no-runners')).toBeNull();
- expect(vm.$el.querySelector('.js-stuck-with-tags')).not.toBeNull();
- expect(vm.$el.querySelector('.js-stuck-no-active-runner')).toBeNull();
- });
-
- it('renders tags', () => {
- expect(vm.$el.textContent).toContain('docker');
- expect(vm.$el.textContent).toContain('gitlab-org');
- });
-
- it('renders link to runners page', () => {
- expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
- '/root/project/runners#js-runners-settings',
- );
- });
- });
-
- describe('without active runners', () => {
- beforeEach(() => {
- vm = mountComponent(Component, {
- hasNoRunnersForProject: false,
- runnersPath: '/root/project/runners#js-runners-settings',
- });
- });
-
- it('renders information about project not having runners', () => {
- expect(vm.$el.querySelector('.js-stuck-no-runners')).toBeNull();
- expect(vm.$el.querySelector('.js-stuck-with-tags')).toBeNull();
- expect(vm.$el.querySelector('.js-stuck-no-active-runner')).not.toBeNull();
- });
-
- it('renders link to runners page', () => {
- expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
- '/root/project/runners#js-runners-settings',
- );
- });
- });
-});
diff --git a/spec/javascripts/labels_issue_sidebar_spec.js b/spec/javascripts/labels_issue_sidebar_spec.js
index 5aafb6ad8f0..e5678ee5379 100644
--- a/spec/javascripts/labels_issue_sidebar_spec.js
+++ b/spec/javascripts/labels_issue_sidebar_spec.js
@@ -12,88 +12,106 @@ import '~/api';
import '~/create_label';
import '~/users_select';
-(() => {
- let saveLabelCount = 0;
- let mock;
+let saveLabelCount = 0;
+let mock;
- describe('Issue dropdown sidebar', () => {
- preloadFixtures('static/issue_sidebar_label.html.raw');
+describe('Issue dropdown sidebar', () => {
+ preloadFixtures('static/issue_sidebar_label.html.raw');
- beforeEach(() => {
- loadFixtures('static/issue_sidebar_label.html.raw');
+ beforeEach(() => {
+ loadFixtures('static/issue_sidebar_label.html.raw');
- mock = new MockAdapter(axios);
+ mock = new MockAdapter(axios);
- new IssuableContext('{"id":1,"name":"Administrator","username":"root"}');
- new LabelsSelect();
+ new IssuableContext('{"id":1,"name":"Administrator","username":"root"}');
+ new LabelsSelect();
- mock.onGet('/root/test/labels.json').reply(() => {
- const labels = Array(10).fill().map((_, i) => ({
+ mock.onGet('/root/test/labels.json').reply(() => {
+ const labels = Array(10)
+ .fill()
+ .map((_, i) => ({
id: i,
title: `test ${i}`,
color: '#5CB85C',
}));
- return [200, labels];
- });
+ return [200, labels];
+ });
- mock.onPut('/root/test/issues/2.json').reply(() => {
- const labels = Array(saveLabelCount).fill().map((_, i) => ({
+ mock.onPut('/root/test/issues/2.json').reply(() => {
+ const labels = Array(saveLabelCount)
+ .fill()
+ .map((_, i) => ({
id: i,
title: `test ${i}`,
color: '#5CB85C',
}));
- return [200, { labels }];
- });
- });
-
- afterEach(() => {
- mock.restore();
+ return [200, { labels }];
});
+ });
- it('changes collapsed tooltip when changing labels when less than 5', (done) => {
- saveLabelCount = 5;
- $('.edit-link').get(0).click();
-
- setTimeout(() => {
- expect($('.dropdown-content a').length).toBe(10);
-
- $('.dropdown-content a').each(function (i) {
- if (i < saveLabelCount) {
- $(this).get(0).click();
- }
- });
-
- $('.edit-link').get(0).click();
+ afterEach(() => {
+ mock.restore();
+ });
- setTimeout(() => {
- expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe('test 0, test 1, test 2, test 3, test 4');
- done();
- }, 0);
- }, 0);
- });
+ it('changes collapsed tooltip when changing labels when less than 5', done => {
+ saveLabelCount = 5;
+ $('.edit-link')
+ .get(0)
+ .click();
+
+ setTimeout(() => {
+ expect($('.dropdown-content a').length).toBe(10);
+
+ $('.dropdown-content a').each(function(i) {
+ if (i < saveLabelCount) {
+ $(this)
+ .get(0)
+ .click();
+ }
+ });
- it('changes collapsed tooltip when changing labels when more than 5', (done) => {
- saveLabelCount = 6;
- $('.edit-link').get(0).click();
+ $('.edit-link')
+ .get(0)
+ .click();
setTimeout(() => {
- expect($('.dropdown-content a').length).toBe(10);
+ expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe(
+ 'test 0, test 1, test 2, test 3, test 4',
+ );
+ done();
+ }, 0);
+ }, 0);
+ });
- $('.dropdown-content a').each(function (i) {
- if (i < saveLabelCount) {
- $(this).get(0).click();
- }
- });
+ it('changes collapsed tooltip when changing labels when more than 5', done => {
+ saveLabelCount = 6;
+ $('.edit-link')
+ .get(0)
+ .click();
+
+ setTimeout(() => {
+ expect($('.dropdown-content a').length).toBe(10);
+
+ $('.dropdown-content a').each(function(i) {
+ if (i < saveLabelCount) {
+ $(this)
+ .get(0)
+ .click();
+ }
+ });
- $('.edit-link').get(0).click();
+ $('.edit-link')
+ .get(0)
+ .click();
- setTimeout(() => {
- expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe('test 0, test 1, test 2, test 3, test 4, and 1 more');
- done();
- }, 0);
+ setTimeout(() => {
+ expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe(
+ 'test 0, test 1, test 2, test 3, test 4, and 1 more',
+ );
+ done();
}, 0);
- });
+ }, 0);
});
-})();
+});
diff --git a/spec/javascripts/labels_select_spec.js b/spec/javascripts/labels_select_spec.js
index 386e00bfd0c..acfdc885032 100644
--- a/spec/javascripts/labels_select_spec.js
+++ b/spec/javascripts/labels_select_spec.js
@@ -19,10 +19,12 @@ describe('LabelsSelect', () => {
let $labelEl;
beforeEach(() => {
- $labelEl = $(LabelsSelect.getLabelTemplate({
- labels: mockLabels,
- issueUpdateURL: mockUrl,
- }));
+ $labelEl = $(
+ LabelsSelect.getLabelTemplate({
+ labels: mockLabels,
+ issueUpdateURL: mockUrl,
+ }),
+ );
});
it('generated label item template has correct label URL', () => {
@@ -38,7 +40,9 @@ describe('LabelsSelect', () => {
});
it('generated label item template has correct label styles', () => {
- expect($labelEl.find('span.label').attr('style')).toBe(`background-color: ${label.color}; color: ${label.text_color};`);
+ expect($labelEl.find('span.label').attr('style')).toBe(
+ `background-color: ${label.color}; color: ${label.text_color};`,
+ );
});
it('generated label item has a badge class', () => {
diff --git a/spec/javascripts/landing_spec.js b/spec/javascripts/landing_spec.js
index 7916073190a..2fe5a47b63e 100644
--- a/spec/javascripts/landing_spec.js
+++ b/spec/javascripts/landing_spec.js
@@ -1,9 +1,9 @@
import Landing from '~/landing';
import Cookies from 'js-cookie';
-describe('Landing', function () {
- describe('class constructor', function () {
- beforeEach(function () {
+describe('Landing', function() {
+ describe('class constructor', function() {
+ beforeEach(function() {
this.landingElement = {};
this.dismissButton = {};
this.cookieName = 'cookie_name';
@@ -11,25 +11,25 @@ describe('Landing', function () {
this.landing = new Landing(this.landingElement, this.dismissButton, this.cookieName);
});
- it('should set .landing', function () {
+ it('should set .landing', function() {
expect(this.landing.landingElement).toBe(this.landingElement);
});
- it('should set .cookieName', function () {
+ it('should set .cookieName', function() {
expect(this.landing.cookieName).toBe(this.cookieName);
});
- it('should set .dismissButton', function () {
+ it('should set .dismissButton', function() {
expect(this.landing.dismissButton).toBe(this.dismissButton);
});
- it('should set .eventWrapper', function () {
+ it('should set .eventWrapper', function() {
expect(this.landing.eventWrapper).toEqual({});
});
});
- describe('toggle', function () {
- beforeEach(function () {
+ describe('toggle', function() {
+ beforeEach(function() {
this.isDismissed = false;
this.landingElement = { classList: jasmine.createSpyObj('classList', ['toggle']) };
this.landing = {
@@ -44,20 +44,20 @@ describe('Landing', function () {
Landing.prototype.toggle.call(this.landing);
});
- it('should call .isDismissed', function () {
+ it('should call .isDismissed', function() {
expect(this.landing.isDismissed).toHaveBeenCalled();
});
- it('should call .classList.toggle', function () {
+ it('should call .classList.toggle', function() {
expect(this.landingElement.classList.toggle).toHaveBeenCalledWith('hidden', this.isDismissed);
});
- it('should call .addEvents', function () {
+ it('should call .addEvents', function() {
expect(this.landing.addEvents).toHaveBeenCalled();
});
- describe('if isDismissed is true', function () {
- beforeEach(function () {
+ describe('if isDismissed is true', function() {
+ beforeEach(function() {
this.isDismissed = true;
this.landingElement = { classList: jasmine.createSpyObj('classList', ['toggle']) };
this.landing = {
@@ -74,14 +74,14 @@ describe('Landing', function () {
Landing.prototype.toggle.call(this.landing);
});
- it('should not call .addEvents', function () {
+ it('should not call .addEvents', function() {
expect(this.landing.addEvents).not.toHaveBeenCalled();
});
});
});
- describe('addEvents', function () {
- beforeEach(function () {
+ describe('addEvents', function() {
+ beforeEach(function() {
this.dismissButton = jasmine.createSpyObj('dismissButton', ['addEventListener']);
this.eventWrapper = {};
this.landing = {
@@ -93,17 +93,20 @@ describe('Landing', function () {
Landing.prototype.addEvents.call(this.landing);
});
- it('should set .eventWrapper.dismissLanding', function () {
+ it('should set .eventWrapper.dismissLanding', function() {
expect(this.eventWrapper.dismissLanding).toEqual(jasmine.any(Function));
});
- it('should call .addEventListener', function () {
- expect(this.dismissButton.addEventListener).toHaveBeenCalledWith('click', this.eventWrapper.dismissLanding);
+ it('should call .addEventListener', function() {
+ expect(this.dismissButton.addEventListener).toHaveBeenCalledWith(
+ 'click',
+ this.eventWrapper.dismissLanding,
+ );
});
});
- describe('removeEvents', function () {
- beforeEach(function () {
+ describe('removeEvents', function() {
+ beforeEach(function() {
this.dismissButton = jasmine.createSpyObj('dismissButton', ['removeEventListener']);
this.eventWrapper = { dismissLanding: () => {} };
this.landing = {
@@ -114,13 +117,16 @@ describe('Landing', function () {
Landing.prototype.removeEvents.call(this.landing);
});
- it('should call .removeEventListener', function () {
- expect(this.dismissButton.removeEventListener).toHaveBeenCalledWith('click', this.eventWrapper.dismissLanding);
+ it('should call .removeEventListener', function() {
+ expect(this.dismissButton.removeEventListener).toHaveBeenCalledWith(
+ 'click',
+ this.eventWrapper.dismissLanding,
+ );
});
});
- describe('dismissLanding', function () {
- beforeEach(function () {
+ describe('dismissLanding', function() {
+ beforeEach(function() {
this.landingElement = { classList: jasmine.createSpyObj('classList', ['add']) };
this.cookieName = 'cookie_name';
this.landing = { landingElement: this.landingElement, cookieName: this.cookieName };
@@ -130,17 +136,17 @@ describe('Landing', function () {
Landing.prototype.dismissLanding.call(this.landing);
});
- it('should call .classList.add', function () {
+ it('should call .classList.add', function() {
expect(this.landingElement.classList.add).toHaveBeenCalledWith('hidden');
});
- it('should call Cookies.set', function () {
+ it('should call Cookies.set', function() {
expect(Cookies.set).toHaveBeenCalledWith(this.cookieName, 'true', { expires: 365 });
});
});
- describe('isDismissed', function () {
- beforeEach(function () {
+ describe('isDismissed', function() {
+ beforeEach(function() {
this.cookieName = 'cookie_name';
this.landing = { cookieName: this.cookieName };
@@ -149,11 +155,11 @@ describe('Landing', function () {
this.isDismissed = Landing.prototype.isDismissed.call(this.landing);
});
- it('should call Cookies.get', function () {
+ it('should call Cookies.get', function() {
expect(Cookies.get).toHaveBeenCalledWith(this.cookieName);
});
- it('should return a boolean', function () {
+ it('should return a boolean', function() {
expect(typeof this.isDismissed).toEqual('boolean');
});
});
diff --git a/spec/javascripts/lazy_loader_spec.js b/spec/javascripts/lazy_loader_spec.js
index 1d81e4e2d1a..eac4756e8a9 100644
--- a/spec/javascripts/lazy_loader_spec.js
+++ b/spec/javascripts/lazy_loader_spec.js
@@ -1,57 +1,214 @@
import LazyLoader from '~/lazy_loader';
+import { TEST_HOST } from './test_constants';
let lazyLoader = null;
-describe('LazyLoader', function () {
+const execImmediately = callback => {
+ callback();
+};
+
+describe('LazyLoader', function() {
preloadFixtures('issues/issue_with_comment.html.raw');
- beforeEach(function () {
- loadFixtures('issues/issue_with_comment.html.raw');
- lazyLoader = new LazyLoader({
- observerNode: 'body',
+ describe('with IntersectionObserver disabled', () => {
+ beforeEach(function() {
+ loadFixtures('issues/issue_with_comment.html.raw');
+
+ lazyLoader = new LazyLoader({
+ observerNode: 'foobar',
+ });
+
+ spyOn(LazyLoader, 'supportsIntersectionObserver').and.callFake(() => false);
+
+ spyOn(LazyLoader, 'loadImage').and.callThrough();
+
+ spyOn(window, 'requestAnimationFrame').and.callFake(execImmediately);
+ spyOn(window, 'requestIdleCallback').and.callFake(execImmediately);
+
+ // Doing everything that happens normally in onload
+ lazyLoader.register();
+ });
+
+ afterEach(() => {
+ lazyLoader.unregister();
+ });
+
+ it('should copy value from data-src to src for img 1', function(done) {
+ const img = document.querySelectorAll('img[data-src]')[0];
+ const originalDataSrc = img.getAttribute('data-src');
+ img.scrollIntoView();
+
+ setTimeout(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(img.getAttribute('src')).toBe(originalDataSrc);
+ expect(img).toHaveClass('js-lazy-loaded');
+ done();
+ }, 50);
+ });
+
+ it('should lazy load dynamically added data-src images', function(done) {
+ const newImg = document.createElement('img');
+ const testPath = `${TEST_HOST}/img/testimg.png`;
+ newImg.className = 'lazy';
+ newImg.setAttribute('data-src', testPath);
+ document.body.appendChild(newImg);
+ newImg.scrollIntoView();
+
+ setTimeout(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(newImg.getAttribute('src')).toBe(testPath);
+ expect(newImg).toHaveClass('js-lazy-loaded');
+ done();
+ }, 50);
+ });
+
+ it('should not alter normal images', function(done) {
+ const newImg = document.createElement('img');
+ const testPath = `${TEST_HOST}/img/testimg.png`;
+ newImg.setAttribute('src', testPath);
+ document.body.appendChild(newImg);
+ newImg.scrollIntoView();
+
+ setTimeout(() => {
+ expect(LazyLoader.loadImage).not.toHaveBeenCalled();
+ expect(newImg).not.toHaveClass('js-lazy-loaded');
+ done();
+ }, 50);
+ });
+
+ it('should not load dynamically added pictures if content observer is turned off', done => {
+ lazyLoader.stopContentObserver();
+
+ const newImg = document.createElement('img');
+ const testPath = `${TEST_HOST}/img/testimg.png`;
+ newImg.className = 'lazy';
+ newImg.setAttribute('data-src', testPath);
+ document.body.appendChild(newImg);
+ newImg.scrollIntoView();
+
+ setTimeout(() => {
+ expect(LazyLoader.loadImage).not.toHaveBeenCalled();
+ expect(newImg).not.toHaveClass('js-lazy-loaded');
+ done();
+ }, 50);
+ });
+
+ it('should load dynamically added pictures if content observer is turned off and on again', done => {
+ lazyLoader.stopContentObserver();
+ lazyLoader.startContentObserver();
+
+ const newImg = document.createElement('img');
+ const testPath = `${TEST_HOST}/img/testimg.png`;
+ newImg.className = 'lazy';
+ newImg.setAttribute('data-src', testPath);
+ document.body.appendChild(newImg);
+ newImg.scrollIntoView();
+
+ setTimeout(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(newImg).toHaveClass('js-lazy-loaded');
+ done();
+ }, 50);
});
- // Doing everything that happens normally in onload
- lazyLoader.loadCheck();
});
- describe('behavior', function () {
- it('should copy value from data-src to src for img 1', function (done) {
+
+ describe('with IntersectionObserver enabled', () => {
+ beforeEach(function() {
+ loadFixtures('issues/issue_with_comment.html.raw');
+
+ lazyLoader = new LazyLoader({
+ observerNode: 'foobar',
+ });
+
+ spyOn(LazyLoader, 'loadImage').and.callThrough();
+
+ spyOn(window, 'requestAnimationFrame').and.callFake(execImmediately);
+ spyOn(window, 'requestIdleCallback').and.callFake(execImmediately);
+
+ // Doing everything that happens normally in onload
+ lazyLoader.register();
+ });
+
+ afterEach(() => {
+ lazyLoader.unregister();
+ });
+
+ it('should copy value from data-src to src for img 1', function(done) {
const img = document.querySelectorAll('img[data-src]')[0];
const originalDataSrc = img.getAttribute('data-src');
img.scrollIntoView();
setTimeout(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(img.getAttribute('src')).toBe(originalDataSrc);
- expect(document.getElementsByClassName('js-lazy-loaded').length).toBeGreaterThan(0);
+ expect(img).toHaveClass('js-lazy-loaded');
done();
- }, 100);
+ }, 50);
});
- it('should lazy load dynamically added data-src images', function (done) {
+ it('should lazy load dynamically added data-src images', function(done) {
const newImg = document.createElement('img');
- const testPath = '/img/testimg.png';
+ const testPath = `${TEST_HOST}/img/testimg.png`;
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg.getAttribute('src')).toBe(testPath);
- expect(document.getElementsByClassName('js-lazy-loaded').length).toBeGreaterThan(0);
+ expect(newImg).toHaveClass('js-lazy-loaded');
done();
- }, 100);
+ }, 50);
});
- it('should not alter normal images', function (done) {
+ it('should not alter normal images', function(done) {
const newImg = document.createElement('img');
- const testPath = '/img/testimg.png';
+ const testPath = `${TEST_HOST}/img/testimg.png`;
newImg.setAttribute('src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
+ expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
- }, 100);
+ }, 50);
+ });
+
+ it('should not load dynamically added pictures if content observer is turned off', done => {
+ lazyLoader.stopContentObserver();
+
+ const newImg = document.createElement('img');
+ const testPath = `${TEST_HOST}/img/testimg.png`;
+ newImg.className = 'lazy';
+ newImg.setAttribute('data-src', testPath);
+ document.body.appendChild(newImg);
+ newImg.scrollIntoView();
+
+ setTimeout(() => {
+ expect(LazyLoader.loadImage).not.toHaveBeenCalled();
+ expect(newImg).not.toHaveClass('js-lazy-loaded');
+ done();
+ }, 50);
+ });
+
+ it('should load dynamically added pictures if content observer is turned off and on again', done => {
+ lazyLoader.stopContentObserver();
+ lazyLoader.startContentObserver();
+
+ const newImg = document.createElement('img');
+ const testPath = `${TEST_HOST}/img/testimg.png`;
+ newImg.className = 'lazy';
+ newImg.setAttribute('data-src', testPath);
+ document.body.appendChild(newImg);
+ newImg.scrollIntoView();
+
+ setTimeout(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(newImg).toHaveClass('js-lazy-loaded');
+ done();
+ }, 50);
});
});
});
diff --git a/spec/javascripts/lib/utils/accessor_spec.js b/spec/javascripts/lib/utils/accessor_spec.js
index b768d6f2a68..0045330e470 100644
--- a/spec/javascripts/lib/utils/accessor_spec.js
+++ b/spec/javascripts/lib/utils/accessor_spec.js
@@ -13,7 +13,11 @@ describe('AccessorUtilities', () => {
});
it('should return `false` if access throws an error', () => {
- base = { get testProp() { throw testError; } };
+ base = {
+ get testProp() {
+ throw testError;
+ },
+ };
expect(AccessorUtilities.isPropertyAccessSafe(base, 'testProp')).toBe(false);
});
@@ -35,7 +39,9 @@ describe('AccessorUtilities', () => {
});
it('should return `false` if calling throws an error', () => {
- base.func = () => { throw new Error('test error'); };
+ base.func = () => {
+ throw new Error('test error');
+ };
expect(AccessorUtilities.isFunctionCallSafe(base, 'func')).toBe(false);
});
@@ -58,7 +64,9 @@ describe('AccessorUtilities', () => {
});
it('should return `false` if access to .setItem isnt safe', () => {
- window.localStorage.setItem.and.callFake(() => { throw testError; });
+ window.localStorage.setItem.and.callFake(() => {
+ throw testError;
+ });
expect(AccessorUtilities.isLocalStorageAccessSafe()).toBe(false);
});
diff --git a/spec/javascripts/lib/utils/ajax_cache_spec.js b/spec/javascripts/lib/utils/ajax_cache_spec.js
index 7603400b55e..dc0b04173bf 100644
--- a/spec/javascripts/lib/utils/ajax_cache_spec.js
+++ b/spec/javascripts/lib/utils/ajax_cache_spec.js
@@ -9,8 +9,8 @@ describe('AjaxCache', () => {
};
beforeEach(() => {
- AjaxCache.internalStorage = { };
- AjaxCache.pendingRequests = { };
+ AjaxCache.internalStorage = {};
+ AjaxCache.pendingRequests = {};
});
describe('get', () => {
@@ -59,7 +59,7 @@ describe('AjaxCache', () => {
it('does nothing if cache is empty', () => {
AjaxCache.remove(dummyEndpoint);
- expect(AjaxCache.internalStorage).toEqual({ });
+ expect(AjaxCache.internalStorage).toEqual({});
});
it('does nothing if cache contains no matching data', () => {
@@ -75,7 +75,7 @@ describe('AjaxCache', () => {
AjaxCache.remove(dummyEndpoint);
- expect(AjaxCache.internalStorage).toEqual({ });
+ expect(AjaxCache.internalStorage).toEqual({});
});
});
@@ -101,61 +101,61 @@ describe('AjaxCache', () => {
mock.restore();
});
- it('stores and returns data from Ajax call if cache is empty', (done) => {
+ it('stores and returns data from Ajax call if cache is empty', done => {
mock.onGet(dummyEndpoint).reply(200, dummyResponse);
AjaxCache.retrieve(dummyEndpoint)
- .then((data) => {
- expect(data).toEqual(dummyResponse);
- expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse);
- })
- .then(done)
- .catch(fail);
+ .then(data => {
+ expect(data).toEqual(dummyResponse);
+ expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse);
+ })
+ .then(done)
+ .catch(fail);
});
- it('makes no Ajax call if request is pending', (done) => {
+ it('makes no Ajax call if request is pending', done => {
mock.onGet(dummyEndpoint).reply(200, dummyResponse);
AjaxCache.retrieve(dummyEndpoint)
- .then(done)
- .catch(fail);
+ .then(done)
+ .catch(fail);
AjaxCache.retrieve(dummyEndpoint)
- .then(done)
- .catch(fail);
+ .then(done)
+ .catch(fail);
expect(axios.get.calls.count()).toBe(1);
});
- it('returns undefined if Ajax call fails and cache is empty', (done) => {
+ it('returns undefined if Ajax call fails and cache is empty', done => {
const errorMessage = 'Network Error';
mock.onGet(dummyEndpoint).networkError();
AjaxCache.retrieve(dummyEndpoint)
- .then(data => fail(`Received unexpected data: ${JSON.stringify(data)}`))
- .catch((error) => {
- expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`);
- expect(error.textStatus).toBe(errorMessage);
- done();
- })
- .catch(fail);
+ .then(data => fail(`Received unexpected data: ${JSON.stringify(data)}`))
+ .catch(error => {
+ expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`);
+ expect(error.textStatus).toBe(errorMessage);
+ done();
+ })
+ .catch(fail);
});
- it('makes no Ajax call if matching data exists', (done) => {
+ it('makes no Ajax call if matching data exists', done => {
AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
mock.onGet(dummyEndpoint).reply(() => {
fail(new Error('expected no Ajax call!'));
});
AjaxCache.retrieve(dummyEndpoint)
- .then((data) => {
- expect(data).toBe(dummyResponse);
- })
- .then(done)
- .catch(fail);
+ .then(data => {
+ expect(data).toBe(dummyResponse);
+ })
+ .then(done)
+ .catch(fail);
});
- it('makes Ajax call even if matching data exists when forceRequest parameter is provided', (done) => {
+ it('makes Ajax call even if matching data exists when forceRequest parameter is provided', done => {
const oldDummyResponse = {
important: 'old dummy data',
};
@@ -166,7 +166,7 @@ describe('AjaxCache', () => {
// Call without forceRetrieve param
AjaxCache.retrieve(dummyEndpoint)
- .then((data) => {
+ .then(data => {
expect(data).toBe(oldDummyResponse);
})
.then(done)
@@ -174,7 +174,7 @@ describe('AjaxCache', () => {
// Call with forceRetrieve param
AjaxCache.retrieve(dummyEndpoint, true)
- .then((data) => {
+ .then(data => {
expect(data).toEqual(dummyResponse);
})
.then(done)
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 71b26a315af..0fb90c3b78c 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -1,4 +1,3 @@
-/* eslint-disable promise/catch-or-return */
import axios from '~/lib/utils/axios_utils';
import * as commonUtils from '~/lib/utils/common_utils';
import MockAdapter from 'axios-mock-adapter';
@@ -9,6 +8,7 @@ describe('common_utils', () => {
it('returns an anchor tag with url', () => {
expect(commonUtils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url');
});
+
it('url is escaped', () => {
// IE11 will return a relative pathname while other browsers will return a full pathname.
// parseUrl uses an anchor element for parsing an url. With relative urls, the anchor
@@ -29,24 +29,39 @@ describe('common_utils', () => {
});
});
- describe('getUrlParamsArray', () => {
- it('should return params array', () => {
- expect(commonUtils.getUrlParamsArray() instanceof Array).toBe(true);
+ describe('urlParamsToArray', () => {
+ it('returns empty array for empty querystring', () => {
+ expect(commonUtils.urlParamsToArray('')).toEqual([]);
+ });
+
+ it('should decode params', () => {
+ expect(commonUtils.urlParamsToArray('?label_name%5B%5D=test')[0]).toBe('label_name[]=test');
});
it('should remove the question mark from the search params', () => {
- const paramsArray = commonUtils.getUrlParamsArray();
- expect(paramsArray[0][0] !== '?').toBe(true);
+ const paramsArray = commonUtils.urlParamsToArray('?test=thing');
+
+ expect(paramsArray[0][0]).not.toBe('?');
});
+ });
- it('should decode params', () => {
- window.history.pushState('', '', '?label_name%5B%5D=test');
+ describe('urlParamsToObject', () => {
+ it('parses path for label with trailing +', () => {
+ expect(commonUtils.urlParamsToObject('label_name[]=label%2B', {})).toEqual({
+ label_name: ['label+'],
+ });
+ });
- expect(
- commonUtils.getUrlParamsArray()[0],
- ).toBe('label_name[]=test');
+ it('parses path for milestone with trailing +', () => {
+ expect(commonUtils.urlParamsToObject('milestone_title=A%2B', {})).toEqual({
+ milestone_title: 'A+',
+ });
+ });
- window.history.pushState('', '', '?');
+ it('parses path for search terms with spaces', () => {
+ expect(commonUtils.urlParamsToObject('search=two+words', {})).toEqual({
+ search: 'two words',
+ });
});
});
@@ -99,6 +114,7 @@ describe('common_utils', () => {
commonUtils.handleLocationHash();
expectGetElementIdToHaveBeenCalledWith('test');
+
expect(window.scrollY).toBe(document.getElementById('test').offsetTop);
document.getElementById('parent').remove();
@@ -117,6 +133,7 @@ describe('common_utils', () => {
expectGetElementIdToHaveBeenCalledWith('test');
expectGetElementIdToHaveBeenCalledWith('user-content-test');
+
expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop);
document.getElementById('parent').remove();
@@ -137,6 +154,7 @@ describe('common_utils', () => {
expectGetElementIdToHaveBeenCalledWith('test');
expectGetElementIdToHaveBeenCalledWith('user-content-test');
+
expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop - 50);
expect(window.scrollBy).toHaveBeenCalledWith(0, -50);
@@ -161,7 +179,11 @@ describe('common_utils', () => {
describe('parseQueryStringIntoObject', () => {
it('should return object with query parameters', () => {
- expect(commonUtils.parseQueryStringIntoObject('scope=all&page=2')).toEqual({ scope: 'all', page: '2' });
+ expect(commonUtils.parseQueryStringIntoObject('scope=all&page=2')).toEqual({
+ scope: 'all',
+ page: '2',
+ });
+
expect(commonUtils.parseQueryStringIntoObject('scope=all')).toEqual({ scope: 'all' });
expect(commonUtils.parseQueryStringIntoObject()).toEqual({});
});
@@ -185,7 +207,9 @@ describe('common_utils', () => {
describe('buildUrlWithCurrentLocation', () => {
it('should build an url with current location and given parameters', () => {
expect(commonUtils.buildUrlWithCurrentLocation()).toEqual(window.location.pathname);
- expect(commonUtils.buildUrlWithCurrentLocation('?page=2')).toEqual(`${window.location.pathname}?page=2`);
+ expect(commonUtils.buildUrlWithCurrentLocation('?page=2')).toEqual(
+ `${window.location.pathname}?page=2`,
+ );
});
});
@@ -200,20 +224,24 @@ describe('common_utils', () => {
it('should return valid parameter', () => {
const value = commonUtils.getParameterByName('scope');
+
expect(commonUtils.getParameterByName('p')).toEqual('2');
expect(value).toBe('all');
});
it('should return invalid parameter', () => {
const value = commonUtils.getParameterByName('fakeParameter');
+
expect(value).toBe(null);
});
it('should return valid paramentes if URL is provided', () => {
let value = commonUtils.getParameterByName('foo', 'http://cocteau.twins/?foo=bar');
+
expect(value).toBe('bar');
value = commonUtils.getParameterByName('manan', 'http://cocteau.twins/?foo=bar&manan=canchu');
+
expect(value).toBe('canchu');
});
});
@@ -236,21 +264,24 @@ describe('common_utils', () => {
});
describe('normalizeCRLFHeaders', () => {
- beforeEach(function () {
- this.CLRFHeaders = 'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE';
+ beforeEach(function() {
+ this.CLRFHeaders =
+ 'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE';
spyOn(String.prototype, 'split').and.callThrough();
this.normalizeCRLFHeaders = commonUtils.normalizeCRLFHeaders(this.CLRFHeaders);
});
- it('should split by newline', function () {
+ it('should split by newline', function() {
expect(String.prototype.split).toHaveBeenCalledWith('\n');
});
- it('should split by colon+space for each header', function () {
- expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(3);
+ it('should split by colon+space for each header', function() {
+ expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(
+ 3,
+ );
});
- it('should return a normalized headers object', function () {
+ it('should return a normalized headers object', function() {
expect(this.normalizeCRLFHeaders).toEqual({
'A-HEADER': 'a-value',
'ANOTHER-HEADER': 'ANOTHER-VALUE',
@@ -329,66 +360,80 @@ describe('common_utils', () => {
spyOn(window, 'setTimeout').and.callFake(cb => origSetTimeout(cb, 0));
});
- it('solves the promise from the callback', (done) => {
+ it('solves the promise from the callback', done => {
const expectedResponseValue = 'Success!';
- commonUtils.backOff((next, stop) => (
- new Promise((resolve) => {
- resolve(expectedResponseValue);
- }).then((resp) => {
- stop(resp);
+ commonUtils
+ .backOff((next, stop) =>
+ new Promise(resolve => {
+ resolve(expectedResponseValue);
+ })
+ .then(resp => {
+ stop(resp);
+ })
+ .catch(done.fail),
+ )
+ .then(respBackoff => {
+ expect(respBackoff).toBe(expectedResponseValue);
+ done();
})
- )).then((respBackoff) => {
- expect(respBackoff).toBe(expectedResponseValue);
- done();
- });
+ .catch(done.fail);
});
- it('catches the rejected promise from the callback ', (done) => {
+ it('catches the rejected promise from the callback ', done => {
const errorMessage = 'Mistakes were made!';
- commonUtils.backOff((next, stop) => {
- new Promise((resolve, reject) => {
- reject(new Error(errorMessage));
- }).then((resp) => {
- stop(resp);
- }).catch(err => stop(err));
- }).catch((errBackoffResp) => {
- expect(errBackoffResp instanceof Error).toBe(true);
- expect(errBackoffResp.message).toBe(errorMessage);
- done();
- });
+ commonUtils
+ .backOff((next, stop) => {
+ new Promise((resolve, reject) => {
+ reject(new Error(errorMessage));
+ })
+ .then(resp => {
+ stop(resp);
+ })
+ .catch(err => stop(err));
+ })
+ .catch(errBackoffResp => {
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe(errorMessage);
+ done();
+ });
});
- it('solves the promise correctly after retrying a third time', (done) => {
+ it('solves the promise correctly after retrying a third time', done => {
let numberOfCalls = 1;
const expectedResponseValue = 'Success!';
- commonUtils.backOff((next, stop) => (
- Promise.resolve(expectedResponseValue)
- .then((resp) => {
- if (numberOfCalls < 3) {
- numberOfCalls += 1;
- next();
- } else {
- stop(resp);
- }
- })
- )).then((respBackoff) => {
+ commonUtils
+ .backOff((next, stop) =>
+ Promise.resolve(expectedResponseValue)
+ .then(resp => {
+ if (numberOfCalls < 3) {
+ numberOfCalls += 1;
+ next();
+ } else {
+ stop(resp);
+ }
+ })
+ .catch(done.fail),
+ )
+ .then(respBackoff => {
+ const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout);
+
+ expect(timeouts).toEqual([2000, 4000]);
+ expect(respBackoff).toBe(expectedResponseValue);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('rejects the backOff promise after timing out', done => {
+ commonUtils.backOff(next => next(), 64000).catch(errBackoffResp => {
const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout);
- expect(timeouts).toEqual([2000, 4000]);
- expect(respBackoff).toBe(expectedResponseValue);
+
+ expect(timeouts).toEqual([2000, 4000, 8000, 16000, 32000, 32000]);
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
done();
});
});
-
- it('rejects the backOff promise after timing out', (done) => {
- commonUtils.backOff(next => next(), 64000)
- .catch((errBackoffResp) => {
- const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout);
- expect(timeouts).toEqual([2000, 4000, 8000, 16000, 32000, 32000]);
- expect(errBackoffResp instanceof Error).toBe(true);
- expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
- done();
- });
- });
});
describe('setFavicon', () => {
@@ -403,6 +448,7 @@ describe('common_utils', () => {
afterEach(() => {
document.body.removeChild(document.getElementById('favicon'));
});
+
it('should set page favicon to provided favicon', () => {
const faviconPath = '//custom_favicon';
commonUtils.setFavicon(faviconPath);
@@ -427,16 +473,20 @@ describe('common_utils', () => {
const favicon = document.getElementById('favicon');
favicon.setAttribute('href', 'new/favicon');
commonUtils.resetFavicon();
+
expect(document.getElementById('favicon').getAttribute('href')).toEqual('default/favicon');
});
});
describe('createOverlayIcon', () => {
- it('should return the favicon with the overlay', (done) => {
- commonUtils.createOverlayIcon(faviconDataUrl, overlayDataUrl).then((url) => {
- expect(url).toEqual(faviconWithOverlayDataUrl);
- done();
- });
+ it('should return the favicon with the overlay', done => {
+ commonUtils
+ .createOverlayIcon(faviconDataUrl, overlayDataUrl)
+ .then(url => {
+ expect(url).toEqual(faviconWithOverlayDataUrl);
+ done();
+ })
+ .catch(done.fail);
});
});
@@ -452,11 +502,16 @@ describe('common_utils', () => {
document.body.removeChild(document.getElementById('favicon'));
});
- it('should set page favicon to provided favicon overlay', (done) => {
- commonUtils.setFaviconOverlay(overlayDataUrl).then(() => {
- expect(document.getElementById('favicon').getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
- done();
- });
+ it('should set page favicon to provided favicon overlay', done => {
+ commonUtils
+ .setFaviconOverlay(overlayDataUrl)
+ .then(() => {
+ expect(document.getElementById('favicon').getAttribute('href')).toEqual(
+ faviconWithOverlayDataUrl,
+ );
+ done();
+ })
+ .catch(done.fail);
});
});
@@ -478,28 +533,27 @@ describe('common_utils', () => {
document.body.removeChild(document.getElementById('favicon'));
});
- it('should reset favicon in case of error', (done) => {
- mock.onGet(BUILD_URL).networkError();
+ it('should reset favicon in case of error', done => {
+ mock.onGet(BUILD_URL).replyOnce(500);
- commonUtils.setCiStatusFavicon(BUILD_URL)
- .then(() => {
- const favicon = document.getElementById('favicon');
- expect(favicon.getAttribute('href')).toEqual(faviconDataUrl);
- done();
- })
- // Error is already caught in catch() block of setCiStatusFavicon,
- // It won't throw another error for us to catch
- .catch(done.fail);
+ commonUtils.setCiStatusFavicon(BUILD_URL).catch(() => {
+ const favicon = document.getElementById('favicon');
+
+ expect(favicon.getAttribute('href')).toEqual(faviconDataUrl);
+ done();
+ });
});
- it('should set page favicon to CI status favicon based on provided status', (done) => {
+ it('should set page favicon to CI status favicon based on provided status', done => {
mock.onGet(BUILD_URL).reply(200, {
favicon: overlayDataUrl,
});
- commonUtils.setCiStatusFavicon(BUILD_URL)
+ commonUtils
+ .setCiStatusFavicon(BUILD_URL)
.then(() => {
const favicon = document.getElementById('favicon');
+
expect(favicon.getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
done();
})
@@ -521,11 +575,15 @@ describe('common_utils', () => {
});
it('should return the svg for a linked icon', () => {
- expect(commonUtils.spriteIcon('test')).toEqual('<svg ><use xlink:href="icons.svg#test" /></svg>');
+ expect(commonUtils.spriteIcon('test')).toEqual(
+ '<svg ><use xlink:href="icons.svg#test" /></svg>',
+ );
});
it('should set svg className when passed', () => {
- expect(commonUtils.spriteIcon('test', 'fa fa-test')).toEqual('<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>');
+ expect(commonUtils.spriteIcon('test', 'fa fa-test')).toEqual(
+ '<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>',
+ );
});
});
@@ -545,7 +603,7 @@ describe('common_utils', () => {
const convertedObj = commonUtils.convertObjectPropsToCamelCase(mockObj);
- Object.keys(convertedObj).forEach((prop) => {
+ Object.keys(convertedObj).forEach(prop => {
expect(snakeRegEx.test(prop)).toBeFalsy();
expect(convertedObj[prop]).toBe(mockObj[mappings[prop]]);
});
@@ -564,9 +622,7 @@ describe('common_utils', () => {
},
};
- expect(
- commonUtils.convertObjectPropsToCamelCase(obj),
- ).toEqual({
+ expect(commonUtils.convertObjectPropsToCamelCase(obj)).toEqual({
snakeKey: {
child_snake_key: 'value',
},
@@ -581,9 +637,7 @@ describe('common_utils', () => {
},
};
- expect(
- commonUtils.convertObjectPropsToCamelCase(obj, { deep: true }),
- ).toEqual({
+ expect(commonUtils.convertObjectPropsToCamelCase(obj, { deep: true })).toEqual({
snakeKey: {
childSnakeKey: 'value',
},
@@ -597,9 +651,7 @@ describe('common_utils', () => {
},
];
- expect(
- commonUtils.convertObjectPropsToCamelCase(arr, { deep: true }),
- ).toEqual([
+ expect(commonUtils.convertObjectPropsToCamelCase(arr, { deep: true })).toEqual([
{
childSnakeKey: 'value',
},
@@ -615,9 +667,7 @@ describe('common_utils', () => {
],
];
- expect(
- commonUtils.convertObjectPropsToCamelCase(arr, { deep: true }),
- ).toEqual([
+ expect(commonUtils.convertObjectPropsToCamelCase(arr, { deep: true })).toEqual([
[
{
childSnakeKey: 'value',
diff --git a/spec/javascripts/lib/utils/csrf_token_spec.js b/spec/javascripts/lib/utils/csrf_token_spec.js
index 81a39a97a84..867bee34ee5 100644
--- a/spec/javascripts/lib/utils/csrf_token_spec.js
+++ b/spec/javascripts/lib/utils/csrf_token_spec.js
@@ -1,9 +1,10 @@
import csrf from '~/lib/utils/csrf';
-describe('csrf', function () {
+describe('csrf', function() {
beforeEach(() => {
this.tokenKey = 'X-CSRF-Token';
- this.token = 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ==';
+ this.token =
+ 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ==';
});
it('returns the correct headerKey', () => {
diff --git a/spec/javascripts/lib/utils/datefix_spec.js b/spec/javascripts/lib/utils/datefix_spec.js
deleted file mode 100644
index a9f3abcf2a4..00000000000
--- a/spec/javascripts/lib/utils/datefix_spec.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { pad, pikadayToString } from '~/lib/utils/datefix';
-
-describe('datefix', () => {
- describe('pad', () => {
- it('should add a 0 when length is smaller than 2', () => {
- expect(pad(2)).toEqual('02');
- });
-
- it('should not add a zero when lenght matches the default', () => {
- expect(pad(12)).toEqual('12');
- });
-
- it('should add a 0 when lenght is smaller than the provided', () => {
- expect(pad(12, 3)).toEqual('012');
- });
- });
-
- describe('parsePikadayDate', () => {
- // removed because of https://gitlab.com/gitlab-org/gitlab-ce/issues/39834
- });
-
- describe('pikadayToString', () => {
- it('should format a UTC date into yyyy-mm-dd format', () => {
- expect(pikadayToString(new Date('2020-01-29:00:00'))).toEqual('2020-01-29');
- });
- });
-});
diff --git a/spec/javascripts/lib/utils/datetime_utility_spec.js b/spec/javascripts/lib/utils/datetime_utility_spec.js
new file mode 100644
index 00000000000..bebe76f76c5
--- /dev/null
+++ b/spec/javascripts/lib/utils/datetime_utility_spec.js
@@ -0,0 +1,378 @@
+import * as datetimeUtility from '~/lib/utils/datetime_utility';
+
+describe('Date time utils', () => {
+ describe('timeFor', () => {
+ it('returns `past due` when in past', () => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() - 1);
+
+ expect(datetimeUtility.timeFor(date)).toBe('Past due');
+ });
+
+ it('returns remaining time when in the future', () => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() + 1);
+
+ // Add a day to prevent a transient error. If date is even 1 second
+ // short of a full year, timeFor will return '11 months remaining'
+ date.setDate(date.getDate() + 1);
+
+ expect(datetimeUtility.timeFor(date)).toBe('1 year remaining');
+ });
+ });
+
+ describe('get day name', () => {
+ it('should return Sunday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/17/2016'));
+
+ expect(day).toBe('Sunday');
+ });
+
+ it('should return Monday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/18/2016'));
+
+ expect(day).toBe('Monday');
+ });
+
+ it('should return Tuesday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/19/2016'));
+
+ expect(day).toBe('Tuesday');
+ });
+
+ it('should return Wednesday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/20/2016'));
+
+ expect(day).toBe('Wednesday');
+ });
+
+ it('should return Thursday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/21/2016'));
+
+ expect(day).toBe('Thursday');
+ });
+
+ it('should return Friday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/22/2016'));
+
+ expect(day).toBe('Friday');
+ });
+
+ it('should return Saturday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/23/2016'));
+
+ expect(day).toBe('Saturday');
+ });
+ });
+
+ describe('get day difference', () => {
+ it('should return 7', () => {
+ const firstDay = new Date('07/01/2016');
+ const secondDay = new Date('07/08/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+
+ expect(difference).toBe(7);
+ });
+
+ it('should return 31', () => {
+ const firstDay = new Date('07/01/2016');
+ const secondDay = new Date('08/01/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+
+ expect(difference).toBe(31);
+ });
+
+ it('should return 365', () => {
+ const firstDay = new Date('07/02/2015');
+ const secondDay = new Date('07/01/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+
+ expect(difference).toBe(365);
+ });
+ });
+});
+
+describe('timeIntervalInWords', () => {
+ it('should return string with number of minutes and seconds', () => {
+ expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds');
+ expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second');
+ expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds');
+ expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds');
+ });
+});
+
+describe('dateInWords', () => {
+ const date = new Date('07/01/2016');
+
+ it('should return date in words', () => {
+ expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016');
+ });
+
+ it('should return abbreviated month name', () => {
+ expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
+ });
+
+ it('should return date in words without year', () => {
+ expect(datetimeUtility.dateInWords(date, true, true)).toEqual('Jul 1');
+ });
+});
+
+describe('monthInWords', () => {
+ const date = new Date('2017-01-20');
+
+ it('returns month name from provided date', () => {
+ expect(datetimeUtility.monthInWords(date)).toBe('January');
+ });
+
+ it('returns abbreviated month name from provided date', () => {
+ expect(datetimeUtility.monthInWords(date, true)).toBe('Jan');
+ });
+});
+
+describe('totalDaysInMonth', () => {
+ it('returns number of days in a month for given date', () => {
+ // 1st Feb, 2016 (leap year)
+ expect(datetimeUtility.totalDaysInMonth(new Date(2016, 1, 1))).toBe(29);
+
+ // 1st Feb, 2017
+ expect(datetimeUtility.totalDaysInMonth(new Date(2017, 1, 1))).toBe(28);
+
+ // 1st Jan, 2017
+ expect(datetimeUtility.totalDaysInMonth(new Date(2017, 0, 1))).toBe(31);
+ });
+});
+
+describe('getSundays', () => {
+ it('returns array of dates representing all Sundays of the month', () => {
+ // December, 2017 (it has 5 Sundays)
+ const dateOfSundays = [3, 10, 17, 24, 31];
+ const sundays = datetimeUtility.getSundays(new Date(2017, 11, 1));
+
+ expect(sundays.length).toBe(5);
+ sundays.forEach((sunday, index) => {
+ expect(sunday.getDate()).toBe(dateOfSundays[index]);
+ });
+ });
+});
+
+describe('getTimeframeWindowFrom', () => {
+ it('returns array of date objects upto provided length start with provided startDate', () => {
+ const startDate = new Date(2018, 0, 1);
+ const mockTimeframe = [
+ new Date(2018, 0, 1),
+ new Date(2018, 1, 1),
+ new Date(2018, 2, 1),
+ new Date(2018, 3, 1),
+ new Date(2018, 4, 31),
+ ];
+ const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, 5);
+
+ expect(timeframe.length).toBe(5);
+ timeframe.forEach((timeframeItem, index) => {
+ expect(timeframeItem.getFullYear()).toBe(mockTimeframe[index].getFullYear());
+ expect(timeframeItem.getMonth()).toBe(mockTimeframe[index].getMonth());
+ expect(timeframeItem.getDate()).toBe(mockTimeframe[index].getDate());
+ });
+ });
+});
+
+describe('formatTime', () => {
+ const expectedTimestamps = [
+ [0, '00:00:00'],
+ [1000, '00:00:01'],
+ [42000, '00:00:42'],
+ [121000, '00:02:01'],
+ [10921000, '03:02:01'],
+ [108000000, '30:00:00'],
+ ];
+
+ expectedTimestamps.forEach(([milliseconds, expectedTimestamp]) => {
+ it(`formats ${milliseconds}ms as ${expectedTimestamp}`, () => {
+ expect(datetimeUtility.formatTime(milliseconds)).toBe(expectedTimestamp);
+ });
+ });
+});
+
+describe('datefix', () => {
+ describe('pad', () => {
+ it('should add a 0 when length is smaller than 2', () => {
+ expect(datetimeUtility.pad(2)).toEqual('02');
+ });
+
+ it('should not add a zero when length matches the default', () => {
+ expect(datetimeUtility.pad(12)).toEqual('12');
+ });
+
+ it('should add a 0 when length is smaller than the provided', () => {
+ expect(datetimeUtility.pad(12, 3)).toEqual('012');
+ });
+ });
+
+ describe('parsePikadayDate', () => {
+ // removed because of https://gitlab.com/gitlab-org/gitlab-ce/issues/39834
+ });
+
+ describe('pikadayToString', () => {
+ it('should format a UTC date into yyyy-mm-dd format', () => {
+ expect(datetimeUtility.pikadayToString(new Date('2020-01-29:00:00'))).toEqual('2020-01-29');
+ });
+ });
+});
+
+describe('prettyTime methods', () => {
+ const assertTimeUnits = (obj, minutes, hours, days, weeks) => {
+ expect(obj.minutes).toBe(minutes);
+ expect(obj.hours).toBe(hours);
+ expect(obj.days).toBe(days);
+ expect(obj.weeks).toBe(weeks);
+ };
+
+ describe('parseSeconds', () => {
+ it('should correctly parse a negative value', () => {
+ const zeroSeconds = datetimeUtility.parseSeconds(-1000);
+
+ assertTimeUnits(zeroSeconds, 16, 0, 0, 0);
+ });
+
+ it('should correctly parse a zero value', () => {
+ const zeroSeconds = datetimeUtility.parseSeconds(0);
+
+ assertTimeUnits(zeroSeconds, 0, 0, 0, 0);
+ });
+
+ it('should correctly parse a small non-zero second values', () => {
+ const subOneMinute = datetimeUtility.parseSeconds(10);
+ const aboveOneMinute = datetimeUtility.parseSeconds(100);
+ const manyMinutes = datetimeUtility.parseSeconds(1000);
+
+ assertTimeUnits(subOneMinute, 0, 0, 0, 0);
+ assertTimeUnits(aboveOneMinute, 1, 0, 0, 0);
+ assertTimeUnits(manyMinutes, 16, 0, 0, 0);
+ });
+
+ it('should correctly parse large second values', () => {
+ const aboveOneHour = datetimeUtility.parseSeconds(4800);
+ const aboveOneDay = datetimeUtility.parseSeconds(110000);
+ const aboveOneWeek = datetimeUtility.parseSeconds(25000000);
+
+ assertTimeUnits(aboveOneHour, 20, 1, 0, 0);
+ assertTimeUnits(aboveOneDay, 33, 6, 3, 0);
+ assertTimeUnits(aboveOneWeek, 26, 0, 3, 173);
+ });
+
+ it('should correctly accept a custom param for hoursPerDay', () => {
+ const config = { hoursPerDay: 24 };
+
+ const aboveOneHour = datetimeUtility.parseSeconds(4800, config);
+ const aboveOneDay = datetimeUtility.parseSeconds(110000, config);
+ const aboveOneWeek = datetimeUtility.parseSeconds(25000000, config);
+
+ assertTimeUnits(aboveOneHour, 20, 1, 0, 0);
+ assertTimeUnits(aboveOneDay, 33, 6, 1, 0);
+ assertTimeUnits(aboveOneWeek, 26, 8, 4, 57);
+ });
+
+ it('should correctly accept a custom param for daysPerWeek', () => {
+ const config = { daysPerWeek: 7 };
+
+ const aboveOneHour = datetimeUtility.parseSeconds(4800, config);
+ const aboveOneDay = datetimeUtility.parseSeconds(110000, config);
+ const aboveOneWeek = datetimeUtility.parseSeconds(25000000, config);
+
+ assertTimeUnits(aboveOneHour, 20, 1, 0, 0);
+ assertTimeUnits(aboveOneDay, 33, 6, 3, 0);
+ assertTimeUnits(aboveOneWeek, 26, 0, 0, 124);
+ });
+
+ it('should correctly accept custom params for daysPerWeek and hoursPerDay', () => {
+ const config = { daysPerWeek: 55, hoursPerDay: 14 };
+
+ const aboveOneHour = datetimeUtility.parseSeconds(4800, config);
+ const aboveOneDay = datetimeUtility.parseSeconds(110000, config);
+ const aboveOneWeek = datetimeUtility.parseSeconds(25000000, config);
+
+ assertTimeUnits(aboveOneHour, 20, 1, 0, 0);
+ assertTimeUnits(aboveOneDay, 33, 2, 2, 0);
+ assertTimeUnits(aboveOneWeek, 26, 0, 1, 9);
+ });
+ });
+
+ describe('stringifyTime', () => {
+ it('should stringify values with all non-zero units', () => {
+ const timeObject = {
+ weeks: 1,
+ days: 4,
+ hours: 7,
+ minutes: 20,
+ };
+
+ const timeString = datetimeUtility.stringifyTime(timeObject);
+
+ expect(timeString).toBe('1w 4d 7h 20m');
+ });
+
+ it('should stringify values with some non-zero units', () => {
+ const timeObject = {
+ weeks: 0,
+ days: 4,
+ hours: 0,
+ minutes: 20,
+ };
+
+ const timeString = datetimeUtility.stringifyTime(timeObject);
+
+ expect(timeString).toBe('4d 20m');
+ });
+
+ it('should stringify values with no non-zero units', () => {
+ const timeObject = {
+ weeks: 0,
+ days: 0,
+ hours: 0,
+ minutes: 0,
+ };
+
+ const timeString = datetimeUtility.stringifyTime(timeObject);
+
+ expect(timeString).toBe('0m');
+ });
+
+ it('should return non-condensed representation of time object', () => {
+ const timeObject = { weeks: 1, days: 0, hours: 1, minutes: 0 };
+
+ expect(datetimeUtility.stringifyTime(timeObject, true)).toEqual('1 week 1 hour');
+ });
+ });
+
+ describe('abbreviateTime', () => {
+ it('should abbreviate stringified times for weeks', () => {
+ const fullTimeString = '1w 3d 4h 5m';
+
+ expect(datetimeUtility.abbreviateTime(fullTimeString)).toBe('1w');
+ });
+
+ it('should abbreviate stringified times for non-weeks', () => {
+ const fullTimeString = '0w 3d 4h 5m';
+
+ expect(datetimeUtility.abbreviateTime(fullTimeString)).toBe('3d');
+ });
+ });
+});
+
+describe('calculateRemainingMilliseconds', () => {
+ beforeEach(() => {
+ spyOn(Date, 'now').and.callFake(() => new Date('2063-04-04T00:42:00Z').getTime());
+ });
+
+ it('calculates the remaining time for a given end date', () => {
+ const milliseconds = datetimeUtility.calculateRemainingMilliseconds('2063-04-04T01:44:03Z');
+
+ expect(milliseconds).toBe(3723000);
+ });
+
+ it('returns 0 if the end date has passed', () => {
+ const milliseconds = datetimeUtility.calculateRemainingMilliseconds('2063-04-03T00:00:00Z');
+
+ expect(milliseconds).toBe(0);
+ });
+});
diff --git a/spec/javascripts/lib/utils/dom_utils_spec.js b/spec/javascripts/lib/utils/dom_utils_spec.js
index 867bf5912d1..1fb2e4584a0 100644
--- a/spec/javascripts/lib/utils/dom_utils_spec.js
+++ b/spec/javascripts/lib/utils/dom_utils_spec.js
@@ -18,6 +18,7 @@ describe('DOM Utils', () => {
it('adds class if element exists', () => {
const childElement = parentElement.querySelector('.child');
+
expect(childElement).not.toBe(null);
addClassIfElementExists(childElement, className);
@@ -27,6 +28,7 @@ describe('DOM Utils', () => {
it('does not throw if element does not exist', () => {
const childElement = parentElement.querySelector('.other-child');
+
expect(childElement).toBe(null);
addClassIfElementExists(childElement, className);
diff --git a/spec/javascripts/lib/utils/mock_data.js b/spec/javascripts/lib/utils/mock_data.js
index fd0d62b751f..c466b0cd1ed 100644
--- a/spec/javascripts/lib/utils/mock_data.js
+++ b/spec/javascripts/lib/utils/mock_data.js
@@ -1,5 +1,8 @@
-export const faviconDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACcFBMVEX////iQyniQyniQyniQyniQyniQyniQyniQynhRiriQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniRCniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQynhQiniQiniQiniQinhQinpUSjqUSjqTyjqTyjqTyjlSCniRCniQynjRCjqTyjsZSjrWyj8oib9kSb8pyb9pib8oyb8fyb3ZSb4Zib8fCb8oyb8oyb8oyb8pCb8cSbiQyn7bCb8cib8oyb8oSb8bSbtVSjpTij8nyb8oyb8oyb8lCb2Yyf3ZCf8mCb8oyb8oyb8oyb8iib8bSbiRCn8gyb8oyb8eCbpTinrUSj8oyb8oyb8oyb8pSb8bib4Zif0YCf8byb8oyb8oyb8oyb7oib8oyb8nCbjRSn9bib8ayb8nib8oyb8oyb8oyb8kSbpTyjpTyj8jib8oyb8oyb8oyb8fib0Xyf2ZSb8gCb8oyb6pSb8oyb8dib+cCbgQCnjRSn8cCb8oib8oyb8oyb8oybqUCjnSyn8bCb8oyb8oyb8oyb8myb2YyfyXyf8oyb8oyb8hibhQSn+bib8iSb8oyb8qCb+fSbmSSnqTyj8oib9pCb1YifxXyf7pSb8oCb8pCb+mCb0fCf8pSb7hSXvcSjiQyniQinqTyj9kCb9bib9byb+cCbqUSjiRCnsVCj+cSb8pib8bCb8bSbgQCn7bCb8bibjRSn8oyb8ayb8oib8aib8pCbjRCn8pybhQinhQSn8pSb7ayb7aSb6aib8eib///8IbM+7AAAAr3RSTlMBA3NtX2vT698HGQcRLwWLiXnv++3V+eEd/R8HE2V/Y5HjyefdFw99YWfJ+/3nwQP78/HvX1VTQ/kdA2HzbQXj9fX79/3DGf379/33T/v99/f7ba33+/f1+9/18/v59V339flzF/H9+fX3/fMhBwOh9/v5/fmvBV/z+fP3Awnp9/f38+UFgff7+/37+4c77/f7/flFz/f59dFr7/v98Wnr+/f3I5/197EDBU1ZAwUD8/kLUwAAAAFiS0dEAIgFHUgAAAAHdElNRQfhBQoLHiBV6/1lAAACHUlEQVQ4y41TZXsTQRCe4FAIUigN7m7FXY+iLRQKBG2x4g7BjhZ3Le7uMoEkFJprwyQk0CC/iZnNhUZaHt4vt6/szO7cHcD/wFKjZrJWq3YMq1M3eVc9rFzXR2yQkuA3RGxkjZLGiEk9miA2tURJs1RsnhhokYYtzaU13WZDbBVnW1sjo43J2vI6tZ0lLtFeAh1M0lECneI7dGYtrUtk3RUVIKaEJR25qw27yT0s3W0qEHuPlB4RradivXo7GX36xnbo51SQ+fWHARmCgYMGDxkaxbD3SssYPmIkwKgPLrfA87EETTg/fVaSa/SYsQDjSsd7DcGEsr+BieVKmaRNBsjUtClTfUI900y/5Mt05c8oJQKYSURZ2UqYFa0w283M588JEM2BuRwI5EqT8nmmXzZf4l8XsGNfCIv4QcHFklhiBpaqAsuC4tghj+ySyOdjeJYrP7RCCuR/E5tWAqxaLcmCNSyujdxjHZdbn8UHoA0bN/GoNm8hjQJb/ZzYpo6w3TB27JRduxxqrA7YzbWCezixN8RD2Oc2/Ptlfx7o5uT1A4XMiwzj4HfEikNe7+Ew0ZGjeuW70eEYaeHjxomTiKd++E4XnKGz8d+HDufOB3Ky3RcwdNF1qZiKLyf/B44r2tWf15wV143cwI2qfi8dbtKtX6Hbd+6G74EDqkTm/QcPH/0ufFyNLXjy9NnzF9Xb8BJevYY38C+8fZcg/AF3QTYemVkCwwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0xMFQxMTozMDozMiswMjowMMzup8UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMTBUMTE6MzA6MzIrMDI6MDC9sx95AAAAAElFTkSuQmCC';
+export const faviconDataUrl =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACcFBMVEX////iQyniQyniQyniQyniQyniQyniQyniQynhRiriQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniRCniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQynhQiniQiniQiniQinhQinpUSjqUSjqTyjqTyjqTyjlSCniRCniQynjRCjqTyjsZSjrWyj8oib9kSb8pyb9pib8oyb8fyb3ZSb4Zib8fCb8oyb8oyb8oyb8pCb8cSbiQyn7bCb8cib8oyb8oSb8bSbtVSjpTij8nyb8oyb8oyb8lCb2Yyf3ZCf8mCb8oyb8oyb8oyb8iib8bSbiRCn8gyb8oyb8eCbpTinrUSj8oyb8oyb8oyb8pSb8bib4Zif0YCf8byb8oyb8oyb8oyb7oib8oyb8nCbjRSn9bib8ayb8nib8oyb8oyb8oyb8kSbpTyjpTyj8jib8oyb8oyb8oyb8fib0Xyf2ZSb8gCb8oyb6pSb8oyb8dib+cCbgQCnjRSn8cCb8oib8oyb8oyb8oybqUCjnSyn8bCb8oyb8oyb8oyb8myb2YyfyXyf8oyb8oyb8hibhQSn+bib8iSb8oyb8qCb+fSbmSSnqTyj8oib9pCb1YifxXyf7pSb8oCb8pCb+mCb0fCf8pSb7hSXvcSjiQyniQinqTyj9kCb9bib9byb+cCbqUSjiRCnsVCj+cSb8pib8bCb8bSbgQCn7bCb8bibjRSn8oyb8ayb8oib8aib8pCbjRCn8pybhQinhQSn8pSb7ayb7aSb6aib8eib///8IbM+7AAAAr3RSTlMBA3NtX2vT698HGQcRLwWLiXnv++3V+eEd/R8HE2V/Y5HjyefdFw99YWfJ+/3nwQP78/HvX1VTQ/kdA2HzbQXj9fX79/3DGf379/33T/v99/f7ba33+/f1+9/18/v59V339flzF/H9+fX3/fMhBwOh9/v5/fmvBV/z+fP3Awnp9/f38+UFgff7+/37+4c77/f7/flFz/f59dFr7/v98Wnr+/f3I5/197EDBU1ZAwUD8/kLUwAAAAFiS0dEAIgFHUgAAAAHdElNRQfhBQoLHiBV6/1lAAACHUlEQVQ4y41TZXsTQRCe4FAIUigN7m7FXY+iLRQKBG2x4g7BjhZ3Le7uMoEkFJprwyQk0CC/iZnNhUZaHt4vt6/szO7cHcD/wFKjZrJWq3YMq1M3eVc9rFzXR2yQkuA3RGxkjZLGiEk9miA2tURJs1RsnhhokYYtzaU13WZDbBVnW1sjo43J2vI6tZ0lLtFeAh1M0lECneI7dGYtrUtk3RUVIKaEJR25qw27yT0s3W0qEHuPlB4RradivXo7GX36xnbo51SQ+fWHARmCgYMGDxkaxbD3SssYPmIkwKgPLrfA87EETTg/fVaSa/SYsQDjSsd7DcGEsr+BieVKmaRNBsjUtClTfUI900y/5Mt05c8oJQKYSURZ2UqYFa0w283M588JEM2BuRwI5EqT8nmmXzZf4l8XsGNfCIv4QcHFklhiBpaqAsuC4tghj+ySyOdjeJYrP7RCCuR/E5tWAqxaLcmCNSyujdxjHZdbn8UHoA0bN/GoNm8hjQJb/ZzYpo6w3TB27JRduxxqrA7YzbWCezixN8RD2Oc2/Ptlfx7o5uT1A4XMiwzj4HfEikNe7+Ew0ZGjeuW70eEYaeHjxomTiKd++E4XnKGz8d+HDufOB3Ky3RcwdNF1qZiKLyf/B44r2tWf15wV143cwI2qfi8dbtKtX6Hbd+6G74EDqkTm/QcPH/0ufFyNLXjy9NnzF9Xb8BJevYY38C+8fZcg/AF3QTYemVkCwwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0xMFQxMTozMDozMiswMjowMMzup8UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMTBUMTE6MzA6MzIrMDI6MDC9sx95AAAAAElFTkSuQmCC';
-export const overlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC';
+export const overlayDataUrl =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC';
-export const faviconWithOverlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGt0lEQVRYR8WWf3DT5R3H35/vN0lDiCztSuiPAEnTFhSOUamIzFGokwMH55g0E845yjbP6+4qIoiHY6JjnHLeRI6h9jgQpQcD3ImH2u00eHBwjGthKLI1TSm26S8oKYVS0vT7/X52z7ckTUhqC/yx55/kks/zeb+ez6/nIWYm/B8XJQB4SGq6lL+CJA47vvRtvWs2D0nNl/Kf0qBZxx6u23arv0QAAIHivK8BynB4ffa7BgDQXJx/ngGnw+uThgTwP5ZnMocoJAxVxZDVZ+0L5n5WF75TkMafjLdJxpSg2E+gqW1X7zk3rbpaifhLiEBgTv4mEFbpBoTyu01DoDj/dQAv9rvjtdnp/k3Yx9rgAMV5QYCsAAwAgg6vL/1OTy/2BYrzzwLIBWACuNHhrXPG+otGoKaw0JA58kqGJtOFfgPS8yWT4sz88nzj7UIIfz+wd0mRdEZPLMnp2V/8R0+JrhLbBYFHJvwWzBUxYgqYNzhG+zfEhm24MIE5ectBtP0W+y0Or29FcoDifHFSRxwAcMrh9c0YrmisXaA4r0V0U8xvopgDDq9PpCQ+Ag0/zbEbNUNbMiG9fTwkDTsKHpJa2t1Zmiw1AqLg+tMZ+R6WVVtnZ2qP6Ib+FIjh05G3lsDrB4xjUIbRDeM+WZLJYZ4B1rKMzKPm/fdyzs9qg6WT225IMnPcuYjxbPZhn57qaA00zc4/QYT7b1b/wAZmDYSLjsN1WcmiM+6jXz7JTCs1aNPASBjrtrCGOXVBLK9ph72772bc0REZcsQlkEVoOxblhaFBH0Bxi6GBWFNC8gpV0XqYSe/hI85R9o1zxr/QaZbdbmuW9oRzljRrzBRkW9JhMaTgYugKzl35DlXNJ/Fp43FImoZnz7T0ln7bLihM0g85N627vkWPgLrbvYyCvAP1+rRIWETA5QsyQlcJYOCbMRasWpALtljwSsFyeJxFYsoNWqdN1y/ildM78Y/WGjxx8TL+ol3oluy8VupKe7cfoNLdCJkdqEUPOmBJ5ksJoae91mBps5lQ6pkIm20MPiz6A3KsmcNukDe/3Ye3zh3A77Q2XqcGjslLz88i/nB8pkpSoL8nAFSTBpUN4qSxS5KB5jOGUOniCebmzFQcevSN2xKP+Fp7ajt21f8TOxU/5i45JZFS6XwcTB9HxZgUnGTRNgk31x5jet+aGU7jWw+UweOcPeyTxxoqrGL25+UwdjehSvnmOVIqcz4C8y8GAABcQwjnYI5NheikhQWT+EZmDh2ev/l7cz4U2cGmYyg78TYqVH87Kbtd1wFY4hsVQAt14zu2RiDaTUZMf/BHWD35STx37wDv94k1dLeh7MRmvDZ1GR5Inxg17dX6MPnjZfh5X6tGSqXrV2B8ACIx98UNGOlV4CxCuA6zqIeq9FQ8c68bhx7ZiIK06CQdVF+Il3y1Hq03gnDfk4Uj8zbH2T51dCPOtlW39Q+iPTl2VSMfwKPiKw8aTuhgpl1Zdqxzj8PphRWwm21xZjv9VcgYkYb52dP132PFbSYr/la0DpNtrrg9a2oqsKfB2zlwG+4nSe1z7QDjaQBi2Eh6J4QRwimYt43LwOsuB2oX7YLVMCLqTAya3xx/EwZJxtYHy3WhyMkHExebXz3zAbbXfdo7AFBRaMAz1Ypa6XoaoPejKRGteZm6D3SlWVdOcOHo/Lfj2u9aXw+WHNmA00G/DiFEO0Jd+meyk0fIf/+vLfik6Xhj4qN0v7i5HCY1bBQPk+ij9GSzNbzYNdH03kMrscARfzvHQgiBocSFTVHVCrW+u+WrpK9iCIgS1rRK93oG/1GkRJVIup8KMNs1Sw/1rUtALD36ZzRca8XeJDmPtRc18vDn5SCJViYHENY3IZTK3JkE7RAYtpdkp3bAaJeOzN+CsSMTX+wqa7ih9sbVSLI2WV3znihAJYXZPThA7M6KQoM2MniyhUxTioxTpKLMadjx8Jqh5k3S//8d9GOh92XWmP/aXLKvfHgA0ZTklL0jj9m6UR6L5+9bjFWTPLcFIWbCY1+8pHb0drWybJ4aWLQrODyAWJndzoyylNyGg0hL+bV7Ll4rKIWB5CFBxMlLj21SL4W6QjDQjwOL9n4tNt0+AADPfo+UqgXPHJLSJrkso7F6ylLMy56OFMmYACIKblvtQext8Iqp0swyLYiI3zEAbs6Ml3cXv/p3Y+ryq5KcnSKb1Jmj75P7X0Rm/UV0tvO86r/WIhORwszvkmHEehH2WMo7ikDUQUWhoaIG+NNc96Os8eMEmklE2Qy2ANTO0OrA+CwFOFBfsq8pWZ7+B25aDBxvPp+QAAAAAElFTkSuQmCC';
+export const faviconWithOverlayDataUrl =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGtElEQVRYR8WXf3CT9R3H35/nSdIQIktrCf0RStI0FYRjVBAccxTq5MDBKUoz4ZyjbPO87q4yBsPDMdExTjlvIsdQexyI0oMBeuKhdjsNHhwcMgpjIlublLIm/UlJKZSSJs/z/e6+T5v0CQ22wB/7/pPck8/383l9fj6fEOec8H88NAjAS1LwknsFSVLU8WXd1rtm85LUeKnwGQKzjj3s33azvsEAAEIlnn8ByHL4/Pa7BgAQLCm8QOBOh88vDQkQeMxjMkcQEYKqYsyJWWPhgs/80TsFafzROJtkNIXFfYI0pfXqPeennjqlxPUNikBoTuEmEF+lCRBV3G0aQiWFrwH8d30AWJubGdiEfZzdGqDEEwbICnADQGGHry7zTr0X94IlnnMACggwAWh0+PxOvb5EBGqmTTNkj7ySxWS62C+g5Usm1Zn95YXG24UQ+r5n75Li6Ux4LBkyc7/4t5YSLSr6Lgg9UvBLcKocMEYKON/gGB3YoA/bcGFCczzLQdieLE9bHL66FakBSjzCU0cSAHDa4at7aLhG9XLBEk8zAVnxZxyIEhBy+PwFgwAafpxvNzK5NZUhrX28JA07Cl6SmtvcOUwm4ZAouHj7ad+jMrN1dqb3iG7oS4EYPh2etQS+XiesC8TQ3ZD3yZJsHuUPgbMcI+ej5v3ncv5PasNlk1p7JJnzJL+I0/O5h+u0VCdqIDi78AQRHuirft3hYJzQPvawPydVdPI+/OnTnNNKBjYVXHRa8rFFGeb4w1he0wZ7d/84IXTEhxzxUsgitB2LPFGwvgGUfLSeZUpEXqEqrIdz0nr4iHOUfeOccb/tNMtutzWHPeWcJc0aMxm5lkxYDGloj1zB+Sv/RXXTSXzaeBwSY3j+bHNv2bdtMYCbpHtRkNFd36xFQN3tXkZhvgP1fdPi5kMEXL4oIXKVAA58M8aCVQs84BYLXi5aDq+zGJTqYr+i4PV2vHxmJ/7WUoOn2i/jz6yhW7JjrdSV8U4fQFV+I2Q4UIsedMCSSlcsgp72WtnSajOhzDsBNtsYfFD8e+Rbs4fdIG98uw9vnj+AX7FWvk4NHZOXXphF/INx2SpJIU2L8L4GDAoMwlP9kWSg6awcKVs83tyUnY5Dj75+W8bjutae3o5d9X/HTiWAuUtOS6RUOR8Hp48TxjgU/AMSeKJ1Ej/tMWXG1sxwGt98sBxe5+xhe64XVLiK2Z9XwNgdRLXyzQsC4ENwelIHAFxDBOdh1qdCdNLCoon8RnY+HZ6/+TtzPhTZweAxlJ94C5VqoI2U3a7rACzJjQqgBd24CGscos1kxPQZ38fqSU/jhQkDvN9lrKG7FeUnNuPVKcvwYOb4hGgvi2HSx8vwRKyJkVLl+hk43gdBAcfADBD1cA4RXIdZ1EN1Zjqem+DGoUc2oigjMUlvaV8YL/1qPVpuhOG+JwdH5m1Okn3m6Eacaz3V2jeI9uTbVYY6AKOSKw8MX0MBg2lXjh3r3Hk4s7ASdrMtSWxnoBpZIzIwP3e69lxv3Gay4q/F6zDJ5kq6s6amEnsafJ0Db8P9JKkx1w5wPJuY36IToojgNMzb8rLwmsuB2kW7YDWMSCgTg+YXx9+AQZKxdUaFZiju+a2Mi8uvnH0f2/2f9g4AVE4z4LlTilrlehag9xIpEam4jO4DXfdaV97nwtH5byW137VYD5Yc2YAz4YAGIYx2RLq0z1Sex8l//fUWfBI83jh4Kd1PEuAwqVGjWEwSS+nJJmt0sWu86d0frMQCR/LbWQ8hDAxlXMgUV69Q67ubv0q5FUNAlHKmVLnXE/gfREpUiaQHqAizXbO0UN98BMTSo39Cw7UW7E2Rc728qJGHP68ASbQyNYCQTkAUzCSwQ+CwvSjnsQPGLOnI/C0YO3Lwxq5yhhtqb1KNpGqT1TXvigJU0jh33xpAf7NymoGNDJ9sJtPkYuNkqTh7KnY8vGaoeZPy93+GA1joe4kzzv/SVLqvYngA/dFgVfnlb8tjtm6Ux+I39y/Gqone24IQM+GxL15UO3q7WrhsnhJatCs8PAC9md3OrPK0goaDyEj7uXsuXi0qg4HkIUGE52XHNqmXIl0RGOiHoUV7xb+v5K14SC39At79Ximdhc8ekjImuiyjsXryUszLnY40yThIhSi4bbUHsbfBJ6ZKE5dpQdz4HQOgf2a8tLvklY+M6cuvSnJummxSZ46+X+7biMzaRnSu84IauNYsE5HCOX+HDCPWi7DrKW8/BTcVZ2UN8Me57kc5448TaCYR5XJwC0BtHMwPjs/SgAP1pfuCqSL8Pxhr/wunLWAOAAAAAElFTkSuQmCC';
diff --git a/spec/javascripts/lib/utils/navigation_utility_spec.js b/spec/javascripts/lib/utils/navigation_utility_spec.js
new file mode 100644
index 00000000000..be620e4a27c
--- /dev/null
+++ b/spec/javascripts/lib/utils/navigation_utility_spec.js
@@ -0,0 +1,23 @@
+import findAndFollowLink from '~/lib/utils/navigation_utility';
+
+describe('findAndFollowLink', () => {
+ it('visits a link when the selector exists', () => {
+ const href = '/some/path';
+ const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl');
+
+ setFixtures(`<a class="my-shortcut" href="${href}">link</a>`);
+
+ findAndFollowLink('.my-shortcut');
+
+ expect(visitUrl).toHaveBeenCalledWith(href);
+ });
+
+ it('does not throw an exception when the selector does not exist', () => {
+ const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl');
+
+ // this should not throw an exception
+ findAndFollowLink('.this-selector-does-not-exist');
+
+ expect(visitUrl).not.toHaveBeenCalled();
+ });
+});
diff --git a/spec/javascripts/lib/utils/number_utility_spec.js b/spec/javascripts/lib/utils/number_utility_spec.js
index fcf27f6805f..94c6214c86a 100644
--- a/spec/javascripts/lib/utils/number_utility_spec.js
+++ b/spec/javascripts/lib/utils/number_utility_spec.js
@@ -1,4 +1,10 @@
-import { formatRelevantDigits, bytesToKiB, bytesToMiB, bytesToGiB, numberToHumanSize } from '~/lib/utils/number_utils';
+import {
+ formatRelevantDigits,
+ bytesToKiB,
+ bytesToMiB,
+ bytesToGiB,
+ numberToHumanSize,
+} from '~/lib/utils/number_utils';
describe('Number Utils', () => {
describe('formatRelevantDigits', () => {
@@ -10,6 +16,7 @@ describe('Number Utils', () => {
const formattedNumber = formatRelevantDigits('1000.1234567');
const rightFromDecimal = formattedNumber.split('.')[1];
const leftFromDecimal = formattedNumber.split('.')[0];
+
expect(rightFromDecimal.length).toBe(4);
expect(leftFromDecimal.length).toBe(4);
});
@@ -18,6 +25,7 @@ describe('Number Utils', () => {
const formattedNumber = formatRelevantDigits('0.1234567');
const rightFromDecimal = formattedNumber.split('.')[1];
const leftFromDecimal = formattedNumber.split('.')[0];
+
expect(rightFromDecimal.length).toBe(3);
expect(leftFromDecimal.length).toBe(1);
});
@@ -26,6 +34,7 @@ describe('Number Utils', () => {
const formattedNumber = formatRelevantDigits('10.1234567');
const rightFromDecimal = formattedNumber.split('.')[1];
const leftFromDecimal = formattedNumber.split('.')[0];
+
expect(rightFromDecimal.length).toBe(2);
expect(leftFromDecimal.length).toBe(2);
});
@@ -34,6 +43,7 @@ describe('Number Utils', () => {
const formattedNumber = formatRelevantDigits('100.1234567');
const rightFromDecimal = formattedNumber.split('.')[1];
const leftFromDecimal = formattedNumber.split('.')[0];
+
expect(rightFromDecimal.length).toBe(1);
expect(leftFromDecimal.length).toBe(3);
});
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
index 523f4997bc0..d0da659c3d7 100644
--- a/spec/javascripts/lib/utils/poll_spec.js
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -1,3 +1,5 @@
+/* eslint-disable jasmine/no-unsafe-spy */
+
import Poll from '~/lib/utils/poll';
import { successCodes } from '~/lib/utils/http_status';
@@ -45,7 +47,7 @@ describe('Poll', () => {
service.fetch.calls.reset();
});
- it('calls the success callback when no header for interval is provided', (done) => {
+ it('calls the success callback when no header for interval is provided', done => {
mockServiceCall(service, { status: 200 });
setup();
@@ -57,7 +59,7 @@ describe('Poll', () => {
});
});
- it('calls the error callback when the http request returns an error', (done) => {
+ it('calls the error callback when the http request returns an error', done => {
mockServiceCall(service, { status: 500 }, true);
setup();
@@ -69,7 +71,7 @@ describe('Poll', () => {
});
});
- it('skips the error callback when request is aborted', (done) => {
+ it('skips the error callback when request is aborted', done => {
mockServiceCall(service, { status: 0 }, true);
setup();
@@ -82,19 +84,21 @@ describe('Poll', () => {
});
});
- it('should call the success callback when the interval header is -1', (done) => {
+ it('should call the success callback when the interval header is -1', done => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': -1 } });
- setup().then(() => {
- expect(callbacks.success).toHaveBeenCalled();
- expect(callbacks.error).not.toHaveBeenCalled();
+ setup()
+ .then(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
- done();
- }).catch(done.fail);
+ done();
+ })
+ .catch(done.fail);
});
describe('for 2xx status code', () => {
successCodes.forEach(httpCode => {
- it(`starts polling when http status is ${httpCode} and interval header is provided`, (done) => {
+ it(`starts polling when http status is ${httpCode} and interval header is provided`, done => {
mockServiceCall(service, { status: httpCode, headers: { 'poll-interval': 1 } });
const Polling = new Poll({
@@ -122,7 +126,7 @@ describe('Poll', () => {
});
describe('stop', () => {
- it('stops polling when method is called', (done) => {
+ it('stops polling when method is called', done => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } });
const Polling = new Poll({
@@ -150,7 +154,7 @@ describe('Poll', () => {
});
describe('restart', () => {
- it('should restart polling when its called', (done) => {
+ it('should restart polling when its called', done => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } });
const Polling = new Poll({
diff --git a/spec/javascripts/lib/utils/sticky_spec.js b/spec/javascripts/lib/utils/sticky_spec.js
index b87c836654d..1b1e7da1ed3 100644
--- a/spec/javascripts/lib/utils/sticky_spec.js
+++ b/spec/javascripts/lib/utils/sticky_spec.js
@@ -22,25 +22,19 @@ describe('sticky', () => {
isSticky(el, 0, el.offsetTop);
isSticky(el, 0, el.offsetTop);
- expect(
- el.classList.contains('is-stuck'),
- ).toBeTruthy();
+ expect(el.classList.contains('is-stuck')).toBeTruthy();
});
it('adds is-stuck class', () => {
isSticky(el, 0, el.offsetTop);
- expect(
- el.classList.contains('is-stuck'),
- ).toBeTruthy();
+ expect(el.classList.contains('is-stuck')).toBeTruthy();
});
it('inserts placeholder element', () => {
isSticky(el, 0, el.offsetTop, true);
- expect(
- document.querySelector('.sticky-placeholder'),
- ).not.toBeNull();
+ expect(document.querySelector('.sticky-placeholder')).not.toBeNull();
});
});
@@ -51,29 +45,22 @@ describe('sticky', () => {
isSticky(el, 0, el.offsetTop);
isSticky(el, 0, 0);
- expect(
- el.classList.remove,
- ).toHaveBeenCalledWith('is-stuck');
- expect(
- el.classList.contains('is-stuck'),
- ).toBeFalsy();
+ expect(el.classList.remove).toHaveBeenCalledWith('is-stuck');
+
+ expect(el.classList.contains('is-stuck')).toBeFalsy();
});
it('does not add is-stuck class', () => {
isSticky(el, 0, 0);
- expect(
- el.classList.contains('is-stuck'),
- ).toBeFalsy();
+ expect(el.classList.contains('is-stuck')).toBeFalsy();
});
it('removes placeholder', () => {
isSticky(el, 0, el.offsetTop, true);
isSticky(el, 0, 0, true);
- expect(
- document.querySelector('.sticky-placeholder'),
- ).toBeNull();
+ expect(document.querySelector('.sticky-placeholder')).toBeNull();
});
});
});
diff --git a/spec/javascripts/lib/utils/text_markdown_spec.js b/spec/javascripts/lib/utils/text_markdown_spec.js
index ca0e7c395a0..f71d27eb4e4 100644
--- a/spec/javascripts/lib/utils/text_markdown_spec.js
+++ b/spec/javascripts/lib/utils/text_markdown_spec.js
@@ -21,7 +21,14 @@ describe('init markdown', () => {
textArea.selectionStart = 0;
textArea.selectionEnd = 0;
- insertMarkdownText(textArea, textArea.value, '*', null, '', false);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ });
expect(textArea.value).toEqual(`${initialValue}* `);
});
@@ -32,7 +39,14 @@ describe('init markdown', () => {
textArea.value = initialValue;
textArea.setSelectionRange(initialValue.length, initialValue.length);
- insertMarkdownText(textArea, textArea.value, '*', null, '', false);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ });
expect(textArea.value).toEqual(`${initialValue}\n* `);
});
@@ -43,7 +57,14 @@ describe('init markdown', () => {
textArea.value = initialValue;
textArea.setSelectionRange(initialValue.length, initialValue.length);
- insertMarkdownText(textArea, textArea.value, '*', null, '', false);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ });
expect(textArea.value).toEqual(`${initialValue}* `);
});
@@ -54,9 +75,153 @@ describe('init markdown', () => {
textArea.value = initialValue;
textArea.setSelectionRange(initialValue.length, initialValue.length);
- insertMarkdownText(textArea, textArea.value, '*', null, '', false);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ });
expect(textArea.value).toEqual(`${initialValue}* `);
});
+
+ it('places the cursor inside the tags', () => {
+ const start = 'lorem ';
+ const end = ' ipsum';
+ const tag = '*';
+
+ textArea.value = `${start}${end}`;
+ textArea.setSelectionRange(start.length, start.length);
+
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected: '',
+ wrap: true,
+ });
+
+ expect(textArea.value).toEqual(`${start}**${end}`);
+
+ // cursor placement should be between tags
+ expect(textArea.selectionStart).toBe(start.length + tag.length);
+ });
+ });
+
+ describe('with selection', () => {
+ const text = 'initial selected value';
+ const selected = 'selected';
+ beforeEach(() => {
+ textArea.value = text;
+ const selectedIndex = text.indexOf(selected);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
+ });
+
+ it('applies the tag to the selected value', () => {
+ const selectedIndex = text.indexOf(selected);
+ const tag = '*';
+
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected,
+ wrap: true,
+ });
+
+ expect(textArea.value).toEqual(text.replace(selected, `*${selected}*`));
+
+ // cursor placement should be after selection + 2 tag lengths
+ expect(textArea.selectionStart).toBe(selectedIndex + selected.length + 2 * tag.length);
+ });
+
+ it('replaces the placeholder in the tag', () => {
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '[{text}](url)',
+ blockTag: null,
+ selected,
+ wrap: false,
+ });
+
+ expect(textArea.value).toEqual(text.replace(selected, `[${selected}](url)`));
+ });
+
+ describe('and text to be selected', () => {
+ const tag = '[{text}](url)';
+ const select = 'url';
+
+ it('selects the text', () => {
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected,
+ wrap: false,
+ select,
+ });
+
+ const expectedText = text.replace(selected, `[${selected}](url)`);
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.indexOf(select));
+ expect(textArea.selectionEnd).toEqual(expectedText.indexOf(select) + select.length);
+ });
+
+ it('selects the right text when multiple tags are present', () => {
+ const initialValue = `${tag} ${tag} ${selected}`;
+ textArea.value = initialValue;
+ const selectedIndex = initialValue.indexOf(selected);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected,
+ wrap: false,
+ select,
+ });
+
+ const expectedText = initialValue.replace(selected, `[${selected}](url)`);
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.lastIndexOf(select));
+ expect(textArea.selectionEnd).toEqual(expectedText.lastIndexOf(select) + select.length);
+ });
+
+ it('should support selected urls', () => {
+ const expectedUrl = 'http://www.gitlab.com';
+ const expectedSelectionText = 'text';
+ const expectedText = `text [${expectedSelectionText}](${expectedUrl}) text`;
+ const initialValue = `text ${expectedUrl} text`;
+
+ textArea.value = initialValue;
+ const selectedIndex = initialValue.indexOf(expectedUrl);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + expectedUrl.length);
+
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected: expectedUrl,
+ wrap: false,
+ select,
+ });
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.indexOf(expectedSelectionText, 1));
+ expect(textArea.selectionEnd).toEqual(
+ expectedText.indexOf(expectedSelectionText, 1) + expectedSelectionText.length,
+ );
+ });
+ });
});
});
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index d60485b1308..92ebfc38722 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -63,6 +63,12 @@ describe('text_utility', () => {
});
});
+ describe('slugifyWithHyphens', () => {
+ it('should replaces whitespaces with hyphens and convert to lower case', () => {
+ expect(textUtils.slugifyWithHyphens('My Input String')).toEqual('my-input-string');
+ });
+ });
+
describe('stripHtml', () => {
it('replaces html tag with the default replacement', () => {
expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual(
@@ -114,7 +120,7 @@ describe('text_utility', () => {
});
describe('getFirstCharacterCapitalized', () => {
- it('returns the first character captialized, if first character is alphabetic', () => {
+ it('returns the first character capitalized, if first character is alphabetic', () => {
expect(textUtils.getFirstCharacterCapitalized('loremIpsumDolar')).toEqual('L');
expect(textUtils.getFirstCharacterCapitalized('Sit amit !')).toEqual('S');
});
diff --git a/spec/javascripts/lib/utils/users_cache_spec.js b/spec/javascripts/lib/utils/users_cache_spec.js
index 50371c8c5f6..6adc19bdd51 100644
--- a/spec/javascripts/lib/utils/users_cache_spec.js
+++ b/spec/javascripts/lib/utils/users_cache_spec.js
@@ -6,12 +6,12 @@ describe('UsersCache', () => {
const dummyUser = 'has a farm';
beforeEach(() => {
- UsersCache.internalStorage = { };
+ UsersCache.internalStorage = {};
});
describe('get', () => {
it('returns undefined for empty cache', () => {
- expect(UsersCache.internalStorage).toEqual({ });
+ expect(UsersCache.internalStorage).toEqual({});
const user = UsersCache.get(dummyUsername);
@@ -37,7 +37,7 @@ describe('UsersCache', () => {
describe('hasData', () => {
it('returns false for empty cache', () => {
- expect(UsersCache.internalStorage).toEqual({ });
+ expect(UsersCache.internalStorage).toEqual({});
expect(UsersCache.hasData(dummyUsername)).toBe(false);
});
@@ -57,11 +57,11 @@ describe('UsersCache', () => {
describe('remove', () => {
it('does nothing if cache is empty', () => {
- expect(UsersCache.internalStorage).toEqual({ });
+ expect(UsersCache.internalStorage).toEqual({});
UsersCache.remove(dummyUsername);
- expect(UsersCache.internalStorage).toEqual({ });
+ expect(UsersCache.internalStorage).toEqual({});
});
it('does nothing if cache contains no matching data', () => {
@@ -77,7 +77,7 @@ describe('UsersCache', () => {
UsersCache.remove(dummyUsername);
- expect(UsersCache.internalStorage).toEqual({ });
+ expect(UsersCache.internalStorage).toEqual({});
});
});
@@ -88,7 +88,7 @@ describe('UsersCache', () => {
spyOn(Api, 'users').and.callFake((query, options) => apiSpy(query, options));
});
- it('stores and returns data from API call if cache is empty', (done) => {
+ it('stores and returns data from API call if cache is empty', done => {
apiSpy = (query, options) => {
expect(query).toBe('');
expect(options).toEqual({ username: dummyUsername });
@@ -98,15 +98,15 @@ describe('UsersCache', () => {
};
UsersCache.retrieve(dummyUsername)
- .then((user) => {
- expect(user).toBe(dummyUser);
- expect(UsersCache.internalStorage[dummyUsername]).toBe(dummyUser);
- })
- .then(done)
- .catch(done.fail);
+ .then(user => {
+ expect(user).toBe(dummyUser);
+ expect(UsersCache.internalStorage[dummyUsername]).toBe(dummyUser);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('returns undefined if Ajax call fails and cache is empty', (done) => {
+ it('returns undefined if Ajax call fails and cache is empty', done => {
const dummyError = new Error('server exploded');
apiSpy = (query, options) => {
expect(query).toBe('');
@@ -115,24 +115,24 @@ describe('UsersCache', () => {
};
UsersCache.retrieve(dummyUsername)
- .then(user => fail(`Received unexpected user: ${JSON.stringify(user)}`))
- .catch((error) => {
- expect(error).toBe(dummyError);
- })
- .then(done)
- .catch(done.fail);
+ .then(user => fail(`Received unexpected user: ${JSON.stringify(user)}`))
+ .catch(error => {
+ expect(error).toBe(dummyError);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('makes no Ajax call if matching data exists', (done) => {
+ it('makes no Ajax call if matching data exists', done => {
UsersCache.internalStorage[dummyUsername] = dummyUser;
apiSpy = () => fail(new Error('expected no Ajax call!'));
UsersCache.retrieve(dummyUsername)
- .then((user) => {
- expect(user).toBe(dummyUser);
- })
- .then(done)
- .catch(done.fail);
+ .then(user => {
+ expect(user).toBe(dummyUser);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js
index 8cf0017f4d8..4eea364bd69 100644
--- a/spec/javascripts/line_highlighter_spec.js
+++ b/spec/javascripts/line_highlighter_spec.js
@@ -1,226 +1,265 @@
-/* eslint-disable no-var, quotes, prefer-template, no-else-return, dot-notation, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, no-underscore-dangle, max-len */
+/* eslint-disable no-var, prefer-template, no-else-return, dot-notation, no-return-assign, no-new, no-underscore-dangle */
import $ from 'jquery';
import LineHighlighter from '~/line_highlighter';
-(function() {
- describe('LineHighlighter', function() {
- var clickLine;
- preloadFixtures('static/line_highlighter.html.raw');
- clickLine = function(number, eventData = {}) {
- if ($.isEmptyObject(eventData)) {
- return $("#L" + number).click();
- } else {
- const e = $.Event('click', eventData);
- return $("#L" + number).trigger(e);
+describe('LineHighlighter', function() {
+ var clickLine;
+ preloadFixtures('static/line_highlighter.html.raw');
+ clickLine = function(number, eventData = {}) {
+ if ($.isEmptyObject(eventData)) {
+ return $('#L' + number).click();
+ } else {
+ const e = $.Event('click', eventData);
+ return $('#L' + number).trigger(e);
+ }
+ };
+ beforeEach(function() {
+ loadFixtures('static/line_highlighter.html.raw');
+ this['class'] = new LineHighlighter();
+ this.css = this['class'].highlightLineClass;
+ return (this.spies = {
+ __setLocationHash__: spyOn(this['class'], '__setLocationHash__').and.callFake(function() {}),
+ });
+ });
+
+ describe('behavior', function() {
+ it('highlights one line given in the URL hash', function() {
+ new LineHighlighter({ hash: '#L13' });
+
+ expect($('#LC13')).toHaveClass(this.css);
+ });
+
+ it('highlights one line given in the URL hash with given CSS class name', function() {
+ const hiliter = new LineHighlighter({ hash: '#L13', highlightLineClass: 'hilite' });
+
+ expect(hiliter.highlightLineClass).toBe('hilite');
+ expect($('#LC13')).toHaveClass('hilite');
+ expect($('#LC13')).not.toHaveClass('hll');
+ });
+
+ it('highlights a range of lines given in the URL hash', function() {
+ var line;
+ new LineHighlighter({ hash: '#L5-25' });
+
+ expect($('.' + this.css).length).toBe(21);
+ for (line = 5; line <= 25; line += 1) {
+ expect($('#LC' + line)).toHaveClass(this.css);
}
- };
- beforeEach(function() {
- loadFixtures('static/line_highlighter.html.raw');
- this["class"] = new LineHighlighter();
- this.css = this["class"].highlightLineClass;
- return this.spies = {
- __setLocationHash__: spyOn(this["class"], '__setLocationHash__').and.callFake(function() {})
+ });
+
+ it('scrolls to the first highlighted line on initial load', function() {
+ var spy;
+ spy = spyOn($, 'scrollTo');
+ new LineHighlighter({ hash: '#L5-25' });
+
+ expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything());
+ });
+
+ it('discards click events', function() {
+ var spy;
+ spy = spyOnEvent('a[data-line-number]', 'click');
+ clickLine(13);
+
+ expect(spy).toHaveBeenPrevented();
+ });
+
+ it('handles garbage input from the hash', function() {
+ var func;
+ func = function() {
+ return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' });
};
+
+ expect(func).not.toThrow();
});
- describe('behavior', function() {
- it('highlights one line given in the URL hash', function() {
- new LineHighlighter({ hash: '#L13' });
- return expect($('#LC13')).toHaveClass(this.css);
- });
- it('highlights one line given in the URL hash with given CSS class name', function() {
- const hiliter = new LineHighlighter({ hash: '#L13', highlightLineClass: 'hilite' });
- expect(hiliter.highlightLineClass).toBe('hilite');
- expect($('#LC13')).toHaveClass('hilite');
- expect($('#LC13')).not.toHaveClass('hll');
- });
- it('highlights a range of lines given in the URL hash', function() {
- var line, results;
- new LineHighlighter({ hash: '#L5-25' });
- expect($("." + this.css).length).toBe(21);
- results = [];
- for (line = 5; line <= 25; line += 1) {
- results.push(expect($("#LC" + line)).toHaveClass(this.css));
- }
- return results;
+ });
+
+ describe('clickHandler', function() {
+ it('handles clicking on a child icon element', function() {
+ var spy;
+ spy = spyOn(this['class'], 'setHash').and.callThrough();
+ $('#L13 i')
+ .mousedown()
+ .click();
+
+ expect(spy).toHaveBeenCalledWith(13);
+ expect($('#LC13')).toHaveClass(this.css);
+ });
+
+ describe('without shiftKey', function() {
+ it('highlights one line when clicked', function() {
+ clickLine(13);
+
+ expect($('#LC13')).toHaveClass(this.css);
});
- it('scrolls to the first highlighted line on initial load', function() {
- var spy;
- spy = spyOn($, 'scrollTo');
- new LineHighlighter({ hash: '#L5-25' });
- return expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything());
+
+ it('unhighlights previously highlighted lines', function() {
+ clickLine(13);
+ clickLine(20);
+
+ expect($('#LC13')).not.toHaveClass(this.css);
+ expect($('#LC20')).toHaveClass(this.css);
});
- it('discards click events', function() {
+
+ it('sets the hash', function() {
var spy;
- spy = spyOnEvent('a[data-line-number]', 'click');
+ spy = spyOn(this['class'], 'setHash').and.callThrough();
clickLine(13);
- return expect(spy).toHaveBeenPrevented();
- });
- it('handles garbage input from the hash', function() {
- var func;
- func = function() {
- return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' });
- };
- return expect(func).not.toThrow();
+
+ expect(spy).toHaveBeenCalledWith(13);
});
});
- describe('clickHandler', function() {
- it('handles clicking on a child icon element', function() {
+
+ describe('with shiftKey', function() {
+ it('sets the hash', function() {
var spy;
- spy = spyOn(this["class"], 'setHash').and.callThrough();
- $('#L13 i').mousedown().click();
+ spy = spyOn(this['class'], 'setHash').and.callThrough();
+ clickLine(13);
+ clickLine(20, {
+ shiftKey: true,
+ });
+
expect(spy).toHaveBeenCalledWith(13);
- return expect($('#LC13')).toHaveClass(this.css);
+ expect(spy).toHaveBeenCalledWith(13, 20);
});
- describe('without shiftKey', function() {
- it('highlights one line when clicked', function() {
- clickLine(13);
- return expect($('#LC13')).toHaveClass(this.css);
- });
- it('unhighlights previously highlighted lines', function() {
- clickLine(13);
- clickLine(20);
- expect($('#LC13')).not.toHaveClass(this.css);
- return expect($('#LC20')).toHaveClass(this.css);
- });
- return it('sets the hash', function() {
- var spy;
- spy = spyOn(this["class"], 'setHash').and.callThrough();
- clickLine(13);
- return expect(spy).toHaveBeenCalledWith(13);
+
+ describe('without existing highlight', function() {
+ it('highlights the clicked line', function() {
+ clickLine(13, {
+ shiftKey: true,
+ });
+
+ expect($('#LC13')).toHaveClass(this.css);
+ expect($('.' + this.css).length).toBe(1);
});
- });
- return describe('with shiftKey', function() {
+
it('sets the hash', function() {
var spy;
- spy = spyOn(this["class"], 'setHash').and.callThrough();
- clickLine(13);
- clickLine(20, {
- shiftKey: true
+ spy = spyOn(this['class'], 'setHash');
+ clickLine(13, {
+ shiftKey: true,
});
+
expect(spy).toHaveBeenCalledWith(13);
- return expect(spy).toHaveBeenCalledWith(13, 20);
});
- describe('without existing highlight', function() {
- it('highlights the clicked line', function() {
- clickLine(13, {
- shiftKey: true
- });
- expect($('#LC13')).toHaveClass(this.css);
- return expect($("." + this.css).length).toBe(1);
+ });
+
+ describe('with existing single-line highlight', function() {
+ it('uses existing line as last line when target is lesser', function() {
+ var line;
+ clickLine(20);
+ clickLine(15, {
+ shiftKey: true,
});
- return it('sets the hash', function() {
- var spy;
- spy = spyOn(this["class"], 'setHash');
- clickLine(13, {
- shiftKey: true
- });
- return expect(spy).toHaveBeenCalledWith(13);
+
+ expect($('.' + this.css).length).toBe(6);
+ for (line = 15; line <= 20; line += 1) {
+ expect($('#LC' + line)).toHaveClass(this.css);
+ }
+ });
+
+ it('uses existing line as first line when target is greater', function() {
+ var line;
+ clickLine(5);
+ clickLine(10, {
+ shiftKey: true,
});
+
+ expect($('.' + this.css).length).toBe(6);
+ for (line = 5; line <= 10; line += 1) {
+ expect($('#LC' + line)).toHaveClass(this.css);
+ }
});
- describe('with existing single-line highlight', function() {
- it('uses existing line as last line when target is lesser', function() {
- var line, results;
- clickLine(20);
- clickLine(15, {
- shiftKey: true
- });
- expect($("." + this.css).length).toBe(6);
- results = [];
- for (line = 15; line <= 20; line += 1) {
- results.push(expect($("#LC" + line)).toHaveClass(this.css));
- }
- return results;
+ });
+
+ describe('with existing multi-line highlight', function() {
+ beforeEach(function() {
+ clickLine(10, {
+ shiftKey: true,
});
- return it('uses existing line as first line when target is greater', function() {
- var line, results;
- clickLine(5);
- clickLine(10, {
- shiftKey: true
- });
- expect($("." + this.css).length).toBe(6);
- results = [];
- for (line = 5; line <= 10; line += 1) {
- results.push(expect($("#LC" + line)).toHaveClass(this.css));
- }
- return results;
+ clickLine(13, {
+ shiftKey: true,
});
});
- return describe('with existing multi-line highlight', function() {
- beforeEach(function() {
- clickLine(10, {
- shiftKey: true
- });
- return clickLine(13, {
- shiftKey: true
- });
- });
- it('uses target as first line when it is less than existing first line', function() {
- var line, results;
- clickLine(5, {
- shiftKey: true
- });
- expect($("." + this.css).length).toBe(6);
- results = [];
- for (line = 5; line <= 10; line += 1) {
- results.push(expect($("#LC" + line)).toHaveClass(this.css));
- }
- return results;
+
+ it('uses target as first line when it is less than existing first line', function() {
+ var line;
+ clickLine(5, {
+ shiftKey: true,
});
- return it('uses target as last line when it is greater than existing first line', function() {
- var line, results;
- clickLine(15, {
- shiftKey: true
- });
- expect($("." + this.css).length).toBe(6);
- results = [];
- for (line = 10; line <= 15; line += 1) {
- results.push(expect($("#LC" + line)).toHaveClass(this.css));
- }
- return results;
+
+ expect($('.' + this.css).length).toBe(6);
+ for (line = 5; line <= 10; line += 1) {
+ expect($('#LC' + line)).toHaveClass(this.css);
+ }
+ });
+
+ it('uses target as last line when it is greater than existing first line', function() {
+ var line;
+ clickLine(15, {
+ shiftKey: true,
});
+
+ expect($('.' + this.css).length).toBe(6);
+ for (line = 10; line <= 15; line += 1) {
+ expect($('#LC' + line)).toHaveClass(this.css);
+ }
});
});
});
- describe('hashToRange', function() {
- beforeEach(function() {
- return this.subject = this["class"].hashToRange;
- });
- it('extracts a single line number from the hash', function() {
- return expect(this.subject('#L5')).toEqual([5, null]);
- });
- it('extracts a range of line numbers from the hash', function() {
- return expect(this.subject('#L5-15')).toEqual([5, 15]);
- });
- return it('returns [null, null] when the hash is not a line number', function() {
- return expect(this.subject('#foo')).toEqual([null, null]);
- });
+ });
+
+ describe('hashToRange', function() {
+ beforeEach(function() {
+ this.subject = this['class'].hashToRange;
});
- describe('highlightLine', function() {
- beforeEach(function() {
- return this.subject = this["class"].highlightLine;
- });
- it('highlights the specified line', function() {
- this.subject(13);
- return expect($('#LC13')).toHaveClass(this.css);
- });
- return it('accepts a String-based number', function() {
- this.subject('13');
- return expect($('#LC13')).toHaveClass(this.css);
- });
+
+ it('extracts a single line number from the hash', function() {
+ expect(this.subject('#L5')).toEqual([5, null]);
});
- return describe('setHash', function() {
- beforeEach(function() {
- return this.subject = this["class"].setHash;
- });
- it('sets the location hash for a single line', function() {
- this.subject(5);
- return expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5');
- });
- return it('sets the location hash for a range', function() {
- this.subject(5, 15);
- return expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15');
- });
+
+ it('extracts a range of line numbers from the hash', function() {
+ expect(this.subject('#L5-15')).toEqual([5, 15]);
+ });
+
+ it('returns [null, null] when the hash is not a line number', function() {
+ expect(this.subject('#foo')).toEqual([null, null]);
+ });
+ });
+
+ describe('highlightLine', function() {
+ beforeEach(function() {
+ this.subject = this['class'].highlightLine;
+ });
+
+ it('highlights the specified line', function() {
+ this.subject(13);
+
+ expect($('#LC13')).toHaveClass(this.css);
+ });
+
+ it('accepts a String-based number', function() {
+ this.subject('13');
+
+ expect($('#LC13')).toHaveClass(this.css);
+ });
+ });
+
+ describe('setHash', function() {
+ beforeEach(function() {
+ this.subject = this['class'].setHash;
+ });
+
+ it('sets the location hash for a single line', function() {
+ this.subject(5);
+
+ expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5');
+ });
+
+ it('sets the location hash for a range', function() {
+ this.subject(5, 15);
+
+ expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15');
});
});
-}).call(window);
+});
diff --git a/spec/javascripts/locale/ensure_single_line_spec.js b/spec/javascripts/locale/ensure_single_line_spec.js
new file mode 100644
index 00000000000..20b04cab9c8
--- /dev/null
+++ b/spec/javascripts/locale/ensure_single_line_spec.js
@@ -0,0 +1,38 @@
+import ensureSingleLine from '~/locale/ensure_single_line';
+
+describe('locale', () => {
+ describe('ensureSingleLine', () => {
+ it('should remove newlines at the start of the string', () => {
+ const result = 'Test';
+
+ expect(ensureSingleLine(`\n${result}`)).toBe(result);
+ expect(ensureSingleLine(`\t\n\t${result}`)).toBe(result);
+ expect(ensureSingleLine(`\r\n${result}`)).toBe(result);
+ expect(ensureSingleLine(`\r\n ${result}`)).toBe(result);
+ expect(ensureSingleLine(`\r ${result}`)).toBe(result);
+ expect(ensureSingleLine(` \n ${result}`)).toBe(result);
+ });
+
+ it('should remove newlines at the end of the string', () => {
+ const result = 'Test';
+
+ expect(ensureSingleLine(`${result}\n`)).toBe(result);
+ expect(ensureSingleLine(`${result}\t\n\t`)).toBe(result);
+ expect(ensureSingleLine(`${result}\r\n`)).toBe(result);
+ expect(ensureSingleLine(`${result}\r`)).toBe(result);
+ expect(ensureSingleLine(`${result} \r`)).toBe(result);
+ expect(ensureSingleLine(`${result} \r\n `)).toBe(result);
+ });
+
+ it('should replace newlines in the middle of the string with a single space', () => {
+ const result = 'Test';
+
+ expect(ensureSingleLine(`${result}\n${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result}\t\n\t${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result}\r\n${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result}\r${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result} \r${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result} \r\n ${result}`)).toBe(`${result} ${result}`);
+ });
+ });
+});
diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js
index 7502f1fa2e1..1cb49b49ca7 100644
--- a/spec/javascripts/merge_request_spec.js
+++ b/spec/javascripts/merge_request_spec.js
@@ -7,123 +7,122 @@ import MergeRequest from '~/merge_request';
import CloseReopenReportToggle from '~/close_reopen_report_toggle';
import IssuablesHelper from '~/helpers/issuables_helper';
-(function() {
- describe('MergeRequest', function() {
- describe('task lists', function() {
- let mock;
+describe('MergeRequest', function() {
+ describe('task lists', function() {
+ let mock;
- preloadFixtures('merge_requests/merge_request_with_task_list.html.raw');
- beforeEach(function() {
- loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ preloadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ beforeEach(function() {
+ loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
- spyOn(axios, 'patch').and.callThrough();
- mock = new MockAdapter(axios);
+ spyOn(axios, 'patch').and.callThrough();
+ mock = new MockAdapter(axios);
- mock
- .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`)
- .reply(200, {});
+ mock
+ .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`)
+ .reply(200, {});
- return (this.merge = new MergeRequest());
- });
+ return (this.merge = new MergeRequest());
+ });
- afterEach(() => {
- mock.restore();
- });
+ afterEach(() => {
+ mock.restore();
+ });
- it('modifies the Markdown field', function() {
- spyOn($, 'ajax').and.stub();
- const changeEvent = document.createEvent('HTMLEvents');
- changeEvent.initEvent('change', true, true);
- $('input[type=checkbox]')
- .attr('checked', true)[0]
- .dispatchEvent(changeEvent);
- return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item');
- });
+ it('modifies the Markdown field', function() {
+ spyOn($, 'ajax').and.stub();
+ const changeEvent = document.createEvent('HTMLEvents');
+ changeEvent.initEvent('change', true, true);
+ $('input[type=checkbox]')
+ .attr('checked', true)[0]
+ .dispatchEvent(changeEvent);
- it('submits an ajax request on tasklist:changed', done => {
- $('.js-task-list-field').trigger('tasklist:changed');
-
- setTimeout(() => {
- expect(axios.patch).toHaveBeenCalledWith(
- `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`,
- {
- merge_request: { description: '- [ ] Task List Item' },
- },
- );
- done();
- });
- });
+ expect($('.js-task-list-field').val()).toBe('- [x] Task List Item');
});
- describe('class constructor', () => {
- beforeEach(() => {
- spyOn($, 'ajax').and.stub();
+ it('submits an ajax request on tasklist:changed', done => {
+ $('.js-task-list-field').trigger('tasklist:changed');
+
+ setTimeout(() => {
+ expect(axios.patch).toHaveBeenCalledWith(
+ `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`,
+ {
+ merge_request: { description: '- [ ] Task List Item' },
+ },
+ );
+ done();
});
+ });
+ });
- it('calls .initCloseReopenReport', () => {
- spyOn(IssuablesHelper, 'initCloseReopenReport');
+ describe('class constructor', () => {
+ beforeEach(() => {
+ spyOn($, 'ajax').and.stub();
+ });
- new MergeRequest(); // eslint-disable-line no-new
+ it('calls .initCloseReopenReport', () => {
+ spyOn(IssuablesHelper, 'initCloseReopenReport');
- expect(IssuablesHelper.initCloseReopenReport).toHaveBeenCalled();
- });
+ new MergeRequest(); // eslint-disable-line no-new
- it('calls .initDroplab', () => {
- const container = jasmine.createSpyObj('container', ['querySelector']);
- const dropdownTrigger = {};
- const dropdownList = {};
- const button = {};
+ expect(IssuablesHelper.initCloseReopenReport).toHaveBeenCalled();
+ });
+
+ it('calls .initDroplab', () => {
+ const container = jasmine.createSpyObj('container', ['querySelector']);
+ const dropdownTrigger = {};
+ const dropdownList = {};
+ const button = {};
+
+ spyOn(CloseReopenReportToggle.prototype, 'initDroplab');
+ spyOn(document, 'querySelector').and.returnValue(container);
+ container.querySelector.and.returnValues(dropdownTrigger, dropdownList, button);
- spyOn(CloseReopenReportToggle.prototype, 'initDroplab');
- spyOn(document, 'querySelector').and.returnValue(container);
- container.querySelector.and.returnValues(dropdownTrigger, dropdownList, button);
+ new MergeRequest(); // eslint-disable-line no-new
+
+ expect(document.querySelector).toHaveBeenCalledWith('.js-issuable-close-dropdown');
+ expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-toggle');
+ expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-menu');
+ expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-button');
+ expect(CloseReopenReportToggle.prototype.initDroplab).toHaveBeenCalled();
+ });
+ });
+ describe('hideCloseButton', () => {
+ describe('merge request of another user', () => {
+ beforeEach(() => {
+ loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ this.el = document.querySelector('.js-issuable-actions');
new MergeRequest(); // eslint-disable-line no-new
+ MergeRequest.hideCloseButton();
+ });
- expect(document.querySelector).toHaveBeenCalledWith('.js-issuable-close-dropdown');
- expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-toggle');
- expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-menu');
- expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-button');
- expect(CloseReopenReportToggle.prototype.initDroplab).toHaveBeenCalled();
+ it('hides the dropdown close item and selects the next item', () => {
+ const closeItem = this.el.querySelector('li.close-item');
+ const smallCloseItem = this.el.querySelector('.js-close-item');
+ const reportItem = this.el.querySelector('li.report-item');
+
+ expect(closeItem).toHaveClass('hidden');
+ expect(smallCloseItem).toHaveClass('hidden');
+ expect(reportItem).toHaveClass('droplab-item-selected');
+ expect(reportItem).not.toHaveClass('hidden');
});
});
- describe('hideCloseButton', () => {
- describe('merge request of another user', () => {
- beforeEach(() => {
- loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
- this.el = document.querySelector('.js-issuable-actions');
- new MergeRequest(); // eslint-disable-line no-new
- MergeRequest.hideCloseButton();
- });
-
- it('hides the dropdown close item and selects the next item', () => {
- const closeItem = this.el.querySelector('li.close-item');
- const smallCloseItem = this.el.querySelector('.js-close-item');
- const reportItem = this.el.querySelector('li.report-item');
-
- expect(closeItem).toHaveClass('hidden');
- expect(smallCloseItem).toHaveClass('hidden');
- expect(reportItem).toHaveClass('droplab-item-selected');
- expect(reportItem).not.toHaveClass('hidden');
- });
+ describe('merge request of current_user', () => {
+ beforeEach(() => {
+ loadFixtures('merge_requests/merge_request_of_current_user.html.raw');
+ this.el = document.querySelector('.js-issuable-actions');
+ MergeRequest.hideCloseButton();
});
- describe('merge request of current_user', () => {
- beforeEach(() => {
- loadFixtures('merge_requests/merge_request_of_current_user.html.raw');
- this.el = document.querySelector('.js-issuable-actions');
- MergeRequest.hideCloseButton();
- });
-
- it('hides the close button', () => {
- const closeButton = this.el.querySelector('.btn-close');
- const smallCloseItem = this.el.querySelector('.js-close-item');
+ it('hides the close button', () => {
+ const closeButton = this.el.querySelector('.btn-close');
+ const smallCloseItem = this.el.querySelector('.js-close-item');
- expect(closeButton).toHaveClass('hidden');
- expect(smallCloseItem).toHaveClass('hidden');
- });
+ expect(closeButton).toHaveClass('hidden');
+ expect(smallCloseItem).toHaveClass('hidden');
});
});
});
-}.call(window));
+});
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 7251ce19a90..7714197c821 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -224,6 +224,14 @@ describe('MergeRequestTabs', function() {
expect($('.content-wrapper')).not.toContainElement('.container-limited');
});
+ it('does not add container-limited when fluid layout is prefered', function() {
+ $('.content-wrapper .container-fluid').removeClass('container-limited');
+
+ this.class.expandViewContainer(false);
+
+ expect($('.content-wrapper')).not.toContainElement('.container-limited');
+ });
+
it('does remove container-limited from breadcrumbs', function() {
$('.container-limited').addClass('breadcrumbs');
this.class.expandViewContainer();
diff --git a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
index 1879424c629..092ca9e1dab 100644
--- a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
+++ b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
@@ -39,10 +39,9 @@ describe('Mini Pipeline Graph Dropdown', () => {
});
it('should call getBuildsList', () => {
- const getBuildsListSpy = spyOn(
- MiniPipelineGraph.prototype,
- 'getBuildsList',
- ).and.callFake(function () {});
+ const getBuildsListSpy = spyOn(MiniPipelineGraph.prototype, 'getBuildsList').and.callFake(
+ function() {},
+ );
new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
@@ -61,10 +60,11 @@ describe('Mini Pipeline Graph Dropdown', () => {
new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click();
+
expect(ajaxSpy.calls.allArgs()[0][0]).toEqual('foobar');
});
- it('should not close when user uses cmd/ctrl + click', (done) => {
+ it('should not close when user uses cmd/ctrl + click', done => {
mock.onGet('foobar').reply(200, {
html: `<li>
<a class="mini-pipeline-graph-dropdown-item" href="#">
@@ -90,7 +90,7 @@ describe('Mini Pipeline Graph Dropdown', () => {
.catch(done.fail);
});
- it('should close the dropdown when request returns an error', (done) => {
+ it('should close the dropdown when request returns an error', done => {
mock.onGet('foobar').networkError();
new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index 997163c7602..565b87de248 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -4,28 +4,33 @@ import Dashboard from '~/monitoring/components/dashboard.vue';
import axios from '~/lib/utils/axios_utils';
import { metricsGroupsAPIResponse, mockApiEndpoint, environmentData } from './mock_data';
+const propsData = {
+ hasMetrics: false,
+ documentationPath: '/path/to/docs',
+ settingsPath: '/path/to/settings',
+ clustersPath: '/path/to/clusters',
+ tagsPath: '/path/to/tags',
+ projectPath: '/path/to/project',
+ metricsEndpoint: mockApiEndpoint,
+ deploymentEndpoint: null,
+ emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
+ emptyLoadingSvgPath: '/path/to/loading.svg',
+ emptyNoDataSvgPath: '/path/to/no-data.svg',
+ emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
+ environmentsEndpoint: '/root/hello-prometheus/environments/35',
+ currentEnvironmentName: 'production',
+};
+
+export default propsData;
+
describe('Dashboard', () => {
let DashboardComponent;
- const propsData = {
- hasMetrics: false,
- documentationPath: '/path/to/docs',
- settingsPath: '/path/to/settings',
- clustersPath: '/path/to/clusters',
- tagsPath: '/path/to/tags',
- projectPath: '/path/to/project',
- metricsEndpoint: mockApiEndpoint,
- deploymentEndpoint: null,
- emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
- emptyLoadingSvgPath: '/path/to/loading.svg',
- emptyNoDataSvgPath: '/path/to/no-data.svg',
- emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
- environmentsEndpoint: '/root/hello-prometheus/environments/35',
- currentEnvironmentName: 'production',
- };
-
beforeEach(() => {
- setFixtures('<div class="prometheus-graphs"></div>');
+ setFixtures(`
+ <div class="prometheus-graphs"></div>
+ <div class="nav-sidebar"></div>
+ `);
DashboardComponent = Vue.extend(Dashboard);
});
@@ -102,11 +107,28 @@ describe('Dashboard', () => {
setTimeout(() => {
const dropdownMenuEnvironments = component.$el.querySelectorAll('.dropdown-menu ul li a');
+
expect(dropdownMenuEnvironments.length).toEqual(component.store.environmentsData.length);
done();
});
});
+ it('hides the dropdown list when there is no environments', done => {
+ const component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ });
+
+ component.store.storeEnvironmentsData([]);
+
+ setTimeout(() => {
+ const dropdownMenuEnvironments = component.$el.querySelectorAll('.dropdown-menu ul');
+
+ expect(dropdownMenuEnvironments.length).toEqual(0);
+ done();
+ });
+ });
+
it('renders the dropdown with a single is-active element', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
@@ -119,6 +141,7 @@ describe('Dashboard', () => {
const dropdownIsActiveElement = component.$el.querySelectorAll(
'.dropdown-menu ul li a.is-active',
);
+
expect(dropdownIsActiveElement.length).toEqual(1);
expect(dropdownIsActiveElement[0].textContent.trim()).toEqual(
component.currentEnvironmentName,
@@ -127,4 +150,41 @@ describe('Dashboard', () => {
});
});
});
+
+ describe('when the window resizes', () => {
+ let mock;
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+ jasmine.clock().install();
+ });
+
+ afterEach(() => {
+ mock.restore();
+ jasmine.clock().uninstall();
+ });
+
+ it('rerenders the dashboard when the sidebar is resized', done => {
+ const component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ });
+
+ expect(component.forceRedraw).toEqual(0);
+
+ const navSidebarEl = document.querySelector('.nav-sidebar');
+ navSidebarEl.classList.add('nav-sidebar-collapsed');
+
+ Vue.nextTick()
+ .then(() => {
+ jasmine.clock().tick(1000);
+ return Vue.nextTick();
+ })
+ .then(() => {
+ expect(component.forceRedraw).toEqual(component.elWidth);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/monitoring/dashboard_state_spec.js b/spec/javascripts/monitoring/dashboard_state_spec.js
index b4c5f4baa78..6b2be83aa8c 100644
--- a/spec/javascripts/monitoring/dashboard_state_spec.js
+++ b/spec/javascripts/monitoring/dashboard_state_spec.js
@@ -56,9 +56,17 @@ describe('EmptyState', () => {
});
expect(component.$el.querySelector('svg')).toBeDefined();
- expect(getTextFromNode(component, '.state-title')).toEqual(component.states.gettingStarted.title);
- expect(getTextFromNode(component, '.state-description')).toEqual(component.states.gettingStarted.description);
- expect(getTextFromNode(component, '.btn-success')).toEqual(component.states.gettingStarted.buttonText);
+ expect(getTextFromNode(component, '.state-title')).toEqual(
+ component.states.gettingStarted.title,
+ );
+
+ expect(getTextFromNode(component, '.state-description')).toEqual(
+ component.states.gettingStarted.description,
+ );
+
+ expect(getTextFromNode(component, '.btn-success')).toEqual(
+ component.states.gettingStarted.buttonText,
+ );
});
it('should show the loading state', () => {
@@ -68,7 +76,10 @@ describe('EmptyState', () => {
expect(component.$el.querySelector('svg')).toBeDefined();
expect(getTextFromNode(component, '.state-title')).toEqual(component.states.loading.title);
- expect(getTextFromNode(component, '.state-description')).toEqual(component.states.loading.description);
+ expect(getTextFromNode(component, '.state-description')).toEqual(
+ component.states.loading.description,
+ );
+
expect(getTextFromNode(component, '.btn-success')).toEqual(component.states.loading.buttonText);
});
@@ -78,8 +89,13 @@ describe('EmptyState', () => {
});
expect(component.$el.querySelector('svg')).toBeDefined();
- expect(getTextFromNode(component, '.state-title')).toEqual(component.states.unableToConnect.title);
+ expect(getTextFromNode(component, '.state-title')).toEqual(
+ component.states.unableToConnect.title,
+ );
+
expect(component.$el.querySelector('.state-description a')).toBeDefined();
- expect(getTextFromNode(component, '.btn-success')).toEqual(component.states.unableToConnect.buttonText);
+ expect(getTextFromNode(component, '.btn-success')).toEqual(
+ component.states.unableToConnect.buttonText,
+ );
});
});
diff --git a/spec/javascripts/monitoring/graph/deployment_spec.js b/spec/javascripts/monitoring/graph/deployment_spec.js
index d07db871d69..7d39c4345d2 100644
--- a/spec/javascripts/monitoring/graph/deployment_spec.js
+++ b/spec/javascripts/monitoring/graph/deployment_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import GraphDeployment from '~/monitoring/components/graph/deployment.vue';
import { deploymentData } from '../mock_data';
-const createComponent = (propsData) => {
+const createComponent = propsData => {
const Component = Vue.extend(GraphDeployment);
return new Component({
@@ -33,9 +33,7 @@ describe('MonitoringDeployment', () => {
graphHeightOffset: 120,
});
- expect(
- component.transformDeploymentGroup({ xPos: 16 }),
- ).toContain('translate(11, 20)');
+ expect(component.transformDeploymentGroup({ xPos: 16 })).toContain('translate(11, 20)');
});
describe('Computed props', () => {
diff --git a/spec/javascripts/monitoring/graph/flag_spec.js b/spec/javascripts/monitoring/graph/flag_spec.js
index 19278312b6d..038bfffd44f 100644
--- a/spec/javascripts/monitoring/graph/flag_spec.js
+++ b/spec/javascripts/monitoring/graph/flag_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import GraphFlag from '~/monitoring/components/graph/flag.vue';
import { deploymentData } from '../mock_data';
-const createComponent = (propsData) => {
+const createComponent = propsData => {
const Component = Vue.extend(GraphFlag);
return new Component({
@@ -35,7 +35,7 @@ const defaultValuesComponent = {
unitOfDisplay: 'ms',
currentDataIndex: 0,
legendTitle: 'Average',
- currentCoordinates: [],
+ currentCoordinates: {},
};
const deploymentFlagData = {
@@ -51,8 +51,7 @@ describe('GraphFlag', () => {
it('has a line at the currentXCoordinate', () => {
component = createComponent(defaultValuesComponent);
- expect(component.$el.style.left)
- .toEqual(`${70 + component.currentXCoordinate}px`);
+ expect(component.$el.style.left).toEqual(`${70 + component.currentXCoordinate}px`);
});
describe('Deployment flag', () => {
@@ -62,9 +61,7 @@ describe('GraphFlag', () => {
deploymentFlagData,
});
- expect(
- deploymentFlagComponent.$el.querySelector('.popover-title'),
- ).toContainText('Deployed');
+ expect(deploymentFlagComponent.$el.querySelector('.popover-title')).toContainText('Deployed');
});
it('contains the ref when a tag is available', () => {
@@ -78,13 +75,13 @@ describe('GraphFlag', () => {
},
});
- expect(
- deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
- ).toContainText('f5bcd1d9');
+ expect(deploymentFlagComponent.$el.querySelector('.deploy-meta-content')).toContainText(
+ 'f5bcd1d9',
+ );
- expect(
- deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
- ).toContainText('1.0');
+ expect(deploymentFlagComponent.$el.querySelector('.deploy-meta-content')).toContainText(
+ '1.0',
+ );
});
it('does not contain the ref when a tag is unavailable', () => {
@@ -98,13 +95,13 @@ describe('GraphFlag', () => {
},
});
- expect(
- deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
- ).toContainText('f5bcd1d9');
+ expect(deploymentFlagComponent.$el.querySelector('.deploy-meta-content')).toContainText(
+ 'f5bcd1d9',
+ );
- expect(
- deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
- ).not.toContainText('1.0');
+ expect(deploymentFlagComponent.$el.querySelector('.deploy-meta-content')).not.toContainText(
+ '1.0',
+ );
});
});
diff --git a/spec/javascripts/monitoring/graph/legend_spec.js b/spec/javascripts/monitoring/graph/legend_spec.js
index abcc51aa077..9209e77dcf4 100644
--- a/spec/javascripts/monitoring/graph/legend_spec.js
+++ b/spec/javascripts/monitoring/graph/legend_spec.js
@@ -8,7 +8,7 @@ const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeri
const defaultValuesComponent = {};
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
defaultValuesComponent.timeSeries = timeSeries;
diff --git a/spec/javascripts/monitoring/graph/track_info_spec.js b/spec/javascripts/monitoring/graph/track_info_spec.js
index d3121d553f9..ce93ae28842 100644
--- a/spec/javascripts/monitoring/graph/track_info_spec.js
+++ b/spec/javascripts/monitoring/graph/track_info_spec.js
@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
describe('TrackInfo component', () => {
let vm;
diff --git a/spec/javascripts/monitoring/graph/track_line_spec.js b/spec/javascripts/monitoring/graph/track_line_spec.js
index 27602a861eb..2a4f89ddf6e 100644
--- a/spec/javascripts/monitoring/graph/track_line_spec.js
+++ b/spec/javascripts/monitoring/graph/track_line_spec.js
@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
describe('TrackLine component', () => {
let vm;
diff --git a/spec/javascripts/monitoring/graph_path_spec.js b/spec/javascripts/monitoring/graph_path_spec.js
index 2515e2ad897..fd167b83d51 100644
--- a/spec/javascripts/monitoring/graph_path_spec.js
+++ b/spec/javascripts/monitoring/graph_path_spec.js
@@ -3,7 +3,7 @@ import GraphPath from '~/monitoring/components/graph/path.vue';
import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from './mock_data';
-const createComponent = (propsData) => {
+const createComponent = propsData => {
const Component = Vue.extend(GraphPath);
return new Component({
@@ -13,7 +13,7 @@ const createComponent = (propsData) => {
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
const firstTimeSeries = timeSeries[0];
describe('Monitoring Paths', () => {
@@ -45,9 +45,11 @@ describe('Monitoring Paths', () => {
});
component.lineStyle = 'dashed';
+
expect(component.strokeDashArray).toBe('3, 1');
component.lineStyle = 'dotted';
+
expect(component.strokeDashArray).toBe('1, 1');
});
});
diff --git a/spec/javascripts/monitoring/graph_spec.js b/spec/javascripts/monitoring/graph_spec.js
index a46a387a534..4cc18afdf24 100644
--- a/spec/javascripts/monitoring/graph_spec.js
+++ b/spec/javascripts/monitoring/graph_spec.js
@@ -1,7 +1,6 @@
import Vue from 'vue';
import Graph from '~/monitoring/components/graph.vue';
import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins';
-import eventHub from '~/monitoring/event_hub';
import {
deploymentData,
convertDatesMultipleSeries,
@@ -50,6 +49,7 @@ describe('Graph', () => {
});
const transformedHeight = `${component.graphHeight - 100}`;
+
expect(component.axisTransform.indexOf(transformedHeight)).not.toEqual(-1);
});
@@ -63,29 +63,13 @@ describe('Graph', () => {
});
const viewBoxArray = component.outerViewBox.split(' ');
+
expect(typeof component.outerViewBox).toEqual('string');
expect(viewBoxArray[2]).toEqual(component.graphWidth.toString());
expect(viewBoxArray[3]).toEqual((component.graphHeight - 50).toString());
});
});
- it('sends an event to the eventhub when it has finished resizing', done => {
- const component = createComponent({
- graphData: convertedMetrics[1],
- updateAspectRatio: false,
- deploymentData,
- tagsPath,
- projectPath,
- });
- spyOn(eventHub, '$emit');
-
- component.updateAspectRatio = true;
- Vue.nextTick(() => {
- expect(eventHub.$emit).toHaveBeenCalled();
- done();
- });
- });
-
it('has a title for the y-axis and the chart legend that comes from the backend', () => {
const component = createComponent({
graphData: convertedMetrics[1],
@@ -113,7 +97,11 @@ describe('Graph', () => {
projectPath,
});
+ // simulate moving mouse over data series
+ component.seriesUnderMouse = component.timeSeries;
+
component.positionFlag();
+
expect(component.currentData).toBe(component.timeSeries[0].values[10]);
});
});
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index e4c98a3bcb5..6c833b17f98 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -8,6 +8,7 @@ export const metricsGroupsAPIResponse = {
priority: 1,
metrics: [
{
+ id: 5,
title: 'Memory usage',
weight: 1,
queries: [
diff --git a/spec/javascripts/monitoring/monitoring_store_spec.js b/spec/javascripts/monitoring/monitoring_store_spec.js
index ccdf4eda563..bf68c911549 100644
--- a/spec/javascripts/monitoring/monitoring_store_spec.js
+++ b/spec/javascripts/monitoring/monitoring_store_spec.js
@@ -1,7 +1,7 @@
import MonitoringStore from '~/monitoring/stores/monitoring_store';
import MonitoringMock, { deploymentData, environmentData } from './mock_data';
-describe('MonitoringStore', function () {
+describe('MonitoringStore', function() {
this.store = new MonitoringStore();
this.store.storeMetrics(MonitoringMock.data);
@@ -17,6 +17,7 @@ describe('MonitoringStore', function () {
it('contains deployment data', () => {
this.store.storeDeploymentData(deploymentData);
+
expect(this.store.deploymentData).toBeDefined();
expect(this.store.deploymentData.length).toEqual(3);
expect(typeof this.store.deploymentData[0]).toEqual('object');
@@ -24,6 +25,7 @@ describe('MonitoringStore', function () {
it('only stores environment data that contains deployments', () => {
this.store.storeEnvironmentsData(environmentData);
+
expect(this.store.environmentsData.length).toEqual(2);
});
});
diff --git a/spec/javascripts/monitoring/utils/multiple_time_series_spec.js b/spec/javascripts/monitoring/utils/multiple_time_series_spec.js
index 99584c75287..8937b7d9680 100644
--- a/spec/javascripts/monitoring/utils/multiple_time_series_spec.js
+++ b/spec/javascripts/monitoring/utils/multiple_time_series_spec.js
@@ -2,7 +2,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
const firstTimeSeries = timeSeries[0];
describe('Multiple time series', () => {
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index 122e5bc58b2..1d7b885e64f 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -1,168 +1,199 @@
-/* eslint-disable one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, max-len */
-
import $ from 'jquery';
import NewBranchForm from '~/new_branch_form';
-(function() {
- describe('Branch', function() {
- return describe('create a new branch', function() {
- var expectToHaveError, fillNameWith;
- preloadFixtures('branches/new_branch.html.raw');
- fillNameWith = function(value) {
- return $('.js-branch-name').val(value).trigger('blur');
- };
- expectToHaveError = function(error) {
- return expect($('.js-branch-name-error span').text()).toEqual(error);
- };
- beforeEach(function() {
- loadFixtures('branches/new_branch.html.raw');
- $('form').on('submit', function(e) {
- return e.preventDefault();
- });
- return this.form = new NewBranchForm($('.js-create-branch-form'), []);
- });
- it("can't start with a dot", function() {
- fillNameWith('.foo');
- return expectToHaveError("can't start with '.'");
- });
- it("can't start with a slash", function() {
- fillNameWith('/foo');
- return expectToHaveError("can't start with '/'");
- });
- it("can't have two consecutive dots", function() {
- fillNameWith('foo..bar');
- return expectToHaveError("can't contain '..'");
- });
- it("can't have spaces anywhere", function() {
- fillNameWith(' foo');
- expectToHaveError("can't contain spaces");
- fillNameWith('foo bar');
- expectToHaveError("can't contain spaces");
- fillNameWith('foo ');
- return expectToHaveError("can't contain spaces");
- });
- it("can't have ~ anywhere", function() {
- fillNameWith('~foo');
- expectToHaveError("can't contain '~'");
- fillNameWith('foo~bar');
- expectToHaveError("can't contain '~'");
- fillNameWith('foo~');
- return expectToHaveError("can't contain '~'");
- });
- it("can't have tilde anwhere", function() {
- fillNameWith('~foo');
- expectToHaveError("can't contain '~'");
- fillNameWith('foo~bar');
- expectToHaveError("can't contain '~'");
- fillNameWith('foo~');
- return expectToHaveError("can't contain '~'");
- });
- it("can't have caret anywhere", function() {
- fillNameWith('^foo');
- expectToHaveError("can't contain '^'");
- fillNameWith('foo^bar');
- expectToHaveError("can't contain '^'");
- fillNameWith('foo^');
- return expectToHaveError("can't contain '^'");
- });
- it("can't have : anywhere", function() {
- fillNameWith(':foo');
- expectToHaveError("can't contain ':'");
- fillNameWith('foo:bar');
- expectToHaveError("can't contain ':'");
- fillNameWith(':foo');
- return expectToHaveError("can't contain ':'");
- });
- it("can't have question mark anywhere", function() {
- fillNameWith('?foo');
- expectToHaveError("can't contain '?'");
- fillNameWith('foo?bar');
- expectToHaveError("can't contain '?'");
- fillNameWith('foo?');
- return expectToHaveError("can't contain '?'");
- });
- it("can't have asterisk anywhere", function() {
- fillNameWith('*foo');
- expectToHaveError("can't contain '*'");
- fillNameWith('foo*bar');
- expectToHaveError("can't contain '*'");
- fillNameWith('foo*');
- return expectToHaveError("can't contain '*'");
- });
- it("can't have open bracket anywhere", function() {
- fillNameWith('[foo');
- expectToHaveError("can't contain '['");
- fillNameWith('foo[bar');
- expectToHaveError("can't contain '['");
- fillNameWith('foo[');
- return expectToHaveError("can't contain '['");
- });
- it("can't have a backslash anywhere", function() {
- fillNameWith('\\foo');
- expectToHaveError("can't contain '\\'");
- fillNameWith('foo\\bar');
- expectToHaveError("can't contain '\\'");
- fillNameWith('foo\\');
- return expectToHaveError("can't contain '\\'");
- });
- it("can't contain a sequence @{ anywhere", function() {
- fillNameWith('@{foo');
- expectToHaveError("can't contain '@{'");
- fillNameWith('foo@{bar');
- expectToHaveError("can't contain '@{'");
- fillNameWith('foo@{');
- return expectToHaveError("can't contain '@{'");
- });
- it("can't have consecutive slashes", function() {
- fillNameWith('foo//bar');
- return expectToHaveError("can't contain consecutive slashes");
- });
- it("can't end with a slash", function() {
- fillNameWith('foo/');
- return expectToHaveError("can't end in '/'");
- });
- it("can't end with a dot", function() {
- fillNameWith('foo.');
- return expectToHaveError("can't end in '.'");
- });
- it("can't end with .lock", function() {
- fillNameWith('foo.lock');
- return expectToHaveError("can't end in '.lock'");
- });
- it("can't be the single character @", function() {
- fillNameWith('@');
- return expectToHaveError("can't be '@'");
- });
- it("concatenates all error messages", function() {
- fillNameWith('/foo bar?~.');
- return expectToHaveError("can't start with '/', can't contain spaces, '?', '~', can't end in '.'");
- });
- it("doesn't duplicate error messages", function() {
- fillNameWith('?foo?bar?zoo?');
- return expectToHaveError("can't contain '?'");
- });
- it("removes the error message when is a valid name", function() {
- fillNameWith('foo?bar');
- expect($('.js-branch-name-error span').length).toEqual(1);
- fillNameWith('foobar');
- return expect($('.js-branch-name-error span').length).toEqual(0);
- });
- it("can have dashes anywhere", function() {
- fillNameWith('-foo-bar-zoo-');
- return expect($('.js-branch-name-error span').length).toEqual(0);
- });
- it("can have underscores anywhere", function() {
- fillNameWith('_foo_bar_zoo_');
- return expect($('.js-branch-name-error span').length).toEqual(0);
- });
- it("can have numbers anywhere", function() {
- fillNameWith('1foo2bar3zoo4');
- return expect($('.js-branch-name-error span').length).toEqual(0);
- });
- return it("can be only letters", function() {
- fillNameWith('foo');
- return expect($('.js-branch-name-error span').length).toEqual(0);
+describe('Branch', function() {
+ describe('create a new branch', function() {
+ preloadFixtures('branches/new_branch.html.raw');
+
+ function fillNameWith(value) {
+ $('.js-branch-name')
+ .val(value)
+ .trigger('blur');
+ }
+
+ function expectToHaveError(error) {
+ expect($('.js-branch-name-error span').text()).toEqual(error);
+ }
+
+ beforeEach(function() {
+ loadFixtures('branches/new_branch.html.raw');
+ $('form').on('submit', function(e) {
+ return e.preventDefault();
});
+ this.form = new NewBranchForm($('.js-create-branch-form'), []);
+ });
+
+ it("can't start with a dot", function() {
+ fillNameWith('.foo');
+ expectToHaveError("can't start with '.'");
+ });
+
+ it("can't start with a slash", function() {
+ fillNameWith('/foo');
+ expectToHaveError("can't start with '/'");
+ });
+
+ it("can't have two consecutive dots", function() {
+ fillNameWith('foo..bar');
+ expectToHaveError("can't contain '..'");
+ });
+
+ it("can't have spaces anywhere", function() {
+ fillNameWith(' foo');
+ expectToHaveError("can't contain spaces");
+ fillNameWith('foo bar');
+ expectToHaveError("can't contain spaces");
+ fillNameWith('foo ');
+ expectToHaveError("can't contain spaces");
+ });
+
+ it("can't have ~ anywhere", function() {
+ fillNameWith('~foo');
+ expectToHaveError("can't contain '~'");
+ fillNameWith('foo~bar');
+ expectToHaveError("can't contain '~'");
+ fillNameWith('foo~');
+ expectToHaveError("can't contain '~'");
+ });
+
+ it("can't have tilde anwhere", function() {
+ fillNameWith('~foo');
+ expectToHaveError("can't contain '~'");
+ fillNameWith('foo~bar');
+ expectToHaveError("can't contain '~'");
+ fillNameWith('foo~');
+ expectToHaveError("can't contain '~'");
+ });
+
+ it("can't have caret anywhere", function() {
+ fillNameWith('^foo');
+ expectToHaveError("can't contain '^'");
+ fillNameWith('foo^bar');
+ expectToHaveError("can't contain '^'");
+ fillNameWith('foo^');
+ expectToHaveError("can't contain '^'");
+ });
+
+ it("can't have : anywhere", function() {
+ fillNameWith(':foo');
+ expectToHaveError("can't contain ':'");
+ fillNameWith('foo:bar');
+ expectToHaveError("can't contain ':'");
+ fillNameWith(':foo');
+ expectToHaveError("can't contain ':'");
+ });
+
+ it("can't have question mark anywhere", function() {
+ fillNameWith('?foo');
+ expectToHaveError("can't contain '?'");
+ fillNameWith('foo?bar');
+ expectToHaveError("can't contain '?'");
+ fillNameWith('foo?');
+ expectToHaveError("can't contain '?'");
+ });
+
+ it("can't have asterisk anywhere", function() {
+ fillNameWith('*foo');
+ expectToHaveError("can't contain '*'");
+ fillNameWith('foo*bar');
+ expectToHaveError("can't contain '*'");
+ fillNameWith('foo*');
+ expectToHaveError("can't contain '*'");
+ });
+
+ it("can't have open bracket anywhere", function() {
+ fillNameWith('[foo');
+ expectToHaveError("can't contain '['");
+ fillNameWith('foo[bar');
+ expectToHaveError("can't contain '['");
+ fillNameWith('foo[');
+ expectToHaveError("can't contain '['");
+ });
+
+ it("can't have a backslash anywhere", function() {
+ fillNameWith('\\foo');
+ expectToHaveError("can't contain '\\'");
+ fillNameWith('foo\\bar');
+ expectToHaveError("can't contain '\\'");
+ fillNameWith('foo\\');
+ expectToHaveError("can't contain '\\'");
+ });
+
+ it("can't contain a sequence @{ anywhere", function() {
+ fillNameWith('@{foo');
+ expectToHaveError("can't contain '@{'");
+ fillNameWith('foo@{bar');
+ expectToHaveError("can't contain '@{'");
+ fillNameWith('foo@{');
+ expectToHaveError("can't contain '@{'");
+ });
+
+ it("can't have consecutive slashes", function() {
+ fillNameWith('foo//bar');
+ expectToHaveError("can't contain consecutive slashes");
+ });
+
+ it("can't end with a slash", function() {
+ fillNameWith('foo/');
+ expectToHaveError("can't end in '/'");
+ });
+
+ it("can't end with a dot", function() {
+ fillNameWith('foo.');
+ expectToHaveError("can't end in '.'");
+ });
+
+ it("can't end with .lock", function() {
+ fillNameWith('foo.lock');
+ expectToHaveError("can't end in '.lock'");
+ });
+
+ it("can't be the single character @", function() {
+ fillNameWith('@');
+ expectToHaveError("can't be '@'");
+ });
+
+ it('concatenates all error messages', function() {
+ fillNameWith('/foo bar?~.');
+ expectToHaveError("can't start with '/', can't contain spaces, '?', '~', can't end in '.'");
+ });
+
+ it("doesn't duplicate error messages", function() {
+ fillNameWith('?foo?bar?zoo?');
+ expectToHaveError("can't contain '?'");
+ });
+
+ it('removes the error message when is a valid name', function() {
+ fillNameWith('foo?bar');
+
+ expect($('.js-branch-name-error span').length).toEqual(1);
+ fillNameWith('foobar');
+
+ expect($('.js-branch-name-error span').length).toEqual(0);
+ });
+
+ it('can have dashes anywhere', function() {
+ fillNameWith('-foo-bar-zoo-');
+
+ expect($('.js-branch-name-error span').length).toEqual(0);
+ });
+
+ it('can have underscores anywhere', function() {
+ fillNameWith('_foo_bar_zoo_');
+
+ expect($('.js-branch-name-error span').length).toEqual(0);
+ });
+
+ it('can have numbers anywhere', function() {
+ fillNameWith('1foo2bar3zoo4');
+
+ expect($('.js-branch-name-error span').length).toEqual(0);
+ });
+
+ it('can be only letters', function() {
+ fillNameWith('foo');
+
+ expect($('.js-branch-name-error span').length).toEqual(0);
});
});
-}).call(window);
+});
diff --git a/spec/javascripts/notebook/cells/code_spec.js b/spec/javascripts/notebook/cells/code_spec.js
index 0c432d73f67..4659b83d1b6 100644
--- a/spec/javascripts/notebook/cells/code_spec.js
+++ b/spec/javascripts/notebook/cells/code_spec.js
@@ -12,7 +12,7 @@ describe('Code component', () => {
});
describe('without output', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
cell: json.cells[0],
@@ -31,7 +31,7 @@ describe('Code component', () => {
});
describe('with output', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
cell: json.cells[2],
diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js
index 0b1b11de1fd..540fc8a21f1 100644
--- a/spec/javascripts/notebook/cells/markdown_spec.js
+++ b/spec/javascripts/notebook/cells/markdown_spec.js
@@ -11,7 +11,7 @@ describe('Markdown component', () => {
let cell;
let json;
- beforeEach((done) => {
+ beforeEach(done => {
json = getJSONFixture('blob/notebook/basic.json');
// eslint-disable-next-line prefer-destructuring
@@ -34,18 +34,18 @@ describe('Markdown component', () => {
});
it('does not render the markdown text', () => {
- expect(
- vm.$el.querySelector('.markdown').innerHTML.trim(),
- ).not.toEqual(cell.source.join(''));
+ expect(vm.$el.querySelector('.markdown').innerHTML.trim()).not.toEqual(cell.source.join(''));
});
it('renders the markdown HTML', () => {
expect(vm.$el.querySelector('.markdown h1')).not.toBeNull();
});
- it('sanitizes output', (done) => {
+ it('sanitizes output', done => {
Object.assign(cell, {
- source: ['[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n'],
+ source: [
+ '[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n',
+ ],
});
Vue.nextTick(() => {
@@ -60,7 +60,7 @@ describe('Markdown component', () => {
json = getJSONFixture('blob/notebook/math.json');
});
- it('renders multi-line katex', (done) => {
+ it('renders multi-line katex', done => {
vm = new Component({
propsData: {
cell: json.cells[0],
@@ -68,15 +68,13 @@ describe('Markdown component', () => {
}).$mount();
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.katex'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('.katex')).not.toBeNull();
done();
});
});
- it('renders inline katex', (done) => {
+ it('renders inline katex', done => {
vm = new Component({
propsData: {
cell: json.cells[1],
@@ -84,15 +82,13 @@ describe('Markdown component', () => {
}).$mount();
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('p:first-child .katex'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull();
done();
});
});
- it('renders multiple inline katex', (done) => {
+ it('renders multiple inline katex', done => {
vm = new Component({
propsData: {
cell: json.cells[1],
@@ -100,9 +96,7 @@ describe('Markdown component', () => {
}).$mount();
Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('p:nth-child(2) .katex').length,
- ).toBe(4);
+ expect(vm.$el.querySelectorAll('p:nth-child(2) .katex').length).toBe(4);
done();
});
diff --git a/spec/javascripts/notebook/cells/output/html_sanitize_tests.js b/spec/javascripts/notebook/cells/output/html_sanitize_tests.js
index d587573fc9e..74c48f04367 100644
--- a/spec/javascripts/notebook/cells/output/html_sanitize_tests.js
+++ b/spec/javascripts/notebook/cells/output/html_sanitize_tests.js
@@ -28,7 +28,8 @@ export default {
output: '<a>foo</a>',
},
'protocol-based JS injection: long UTF-8 encoding without semicolons': {
- input: '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
+ input:
+ '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
output: '<a>foo</a>',
},
'protocol-based JS injection: hex encoding': {
@@ -40,7 +41,8 @@ export default {
output: '<a>foo</a>',
},
'protocol-based JS injection: hex encoding without semicolons': {
- input: '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
+ input:
+ '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
output: '<a>foo</a>',
},
'protocol-based JS injection: null char': {
@@ -48,7 +50,7 @@ export default {
output: '<a>foo</a>',
},
'protocol-based JS injection: invalid URL char': {
- input: '<img src=java\script:alert("XSS")>', // eslint-disable-line no-useless-escape
+ input: '<img src=javascript:alert("XSS")>',
output: '<img>',
},
'protocol-based JS injection: Unicode': {
diff --git a/spec/javascripts/notebook/cells/output/html_spec.js b/spec/javascripts/notebook/cells/output/html_spec.js
index 9c5385f2922..bea62f54634 100644
--- a/spec/javascripts/notebook/cells/output/html_spec.js
+++ b/spec/javascripts/notebook/cells/output/html_spec.js
@@ -14,7 +14,7 @@ describe('html output cell', () => {
}
describe('sanitizes output', () => {
- Object.keys(sanitizeTests).forEach((key) => {
+ Object.keys(sanitizeTests).forEach(key => {
it(key, () => {
const test = sanitizeTests[key];
const vm = createComponent(test.input);
diff --git a/spec/javascripts/notebook/cells/output/index_spec.js b/spec/javascripts/notebook/cells/output/index_spec.js
index dbf79f85c7c..feab7ad4212 100644
--- a/spec/javascripts/notebook/cells/output/index_spec.js
+++ b/spec/javascripts/notebook/cells/output/index_spec.js
@@ -7,7 +7,7 @@ describe('Output component', () => {
let vm;
let json;
- const createComponent = (output) => {
+ const createComponent = output => {
vm = new Component({
propsData: {
output,
@@ -22,7 +22,7 @@ describe('Output component', () => {
});
describe('text output', () => {
- beforeEach((done) => {
+ beforeEach(done => {
createComponent(json.cells[2].outputs[0]);
setTimeout(() => {
@@ -40,7 +40,7 @@ describe('Output component', () => {
});
describe('image output', () => {
- beforeEach((done) => {
+ beforeEach(done => {
createComponent(json.cells[3].outputs[0]);
setTimeout(() => {
@@ -58,7 +58,7 @@ describe('Output component', () => {
});
describe('html output', () => {
- beforeEach((done) => {
+ beforeEach(done => {
createComponent(json.cells[4].outputs[0]);
setTimeout(() => {
@@ -77,7 +77,7 @@ describe('Output component', () => {
});
describe('svg output', () => {
- beforeEach((done) => {
+ beforeEach(done => {
createComponent(json.cells[5].outputs[0]);
setTimeout(() => {
@@ -95,7 +95,7 @@ describe('Output component', () => {
});
describe('default to plain text', () => {
- beforeEach((done) => {
+ beforeEach(done => {
createComponent(json.cells[6].outputs[0]);
setTimeout(() => {
@@ -112,7 +112,7 @@ describe('Output component', () => {
expect(vm.$el.querySelector('.prompt span')).not.toBeNull();
});
- it('renders as plain text when doesn\'t recognise other types', (done) => {
+ it("renders as plain text when doesn't recognise other types", done => {
createComponent(json.cells[7].outputs[0]);
setTimeout(() => {
diff --git a/spec/javascripts/notebook/cells/prompt_spec.js b/spec/javascripts/notebook/cells/prompt_spec.js
index 207fa433a59..cbbcb1e68e3 100644
--- a/spec/javascripts/notebook/cells/prompt_spec.js
+++ b/spec/javascripts/notebook/cells/prompt_spec.js
@@ -7,7 +7,7 @@ describe('Prompt component', () => {
let vm;
describe('input', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
type: 'In',
@@ -31,7 +31,7 @@ describe('Prompt component', () => {
});
describe('output', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
type: 'Out',
diff --git a/spec/javascripts/notebook/index_spec.js b/spec/javascripts/notebook/index_spec.js
index bd63ab35426..2e2ea5ad8af 100644
--- a/spec/javascripts/notebook/index_spec.js
+++ b/spec/javascripts/notebook/index_spec.js
@@ -14,7 +14,7 @@ describe('Notebook component', () => {
});
describe('without JSON', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
notebook: {},
@@ -33,7 +33,7 @@ describe('Notebook component', () => {
});
describe('with JSON', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
notebook: json,
@@ -65,7 +65,7 @@ describe('Notebook component', () => {
});
describe('with worksheets', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
notebook: jsonWithWorksheet,
@@ -80,7 +80,9 @@ describe('Notebook component', () => {
});
it('renders cells', () => {
- expect(vm.$el.querySelectorAll('.cell').length).toBe(jsonWithWorksheet.worksheets[0].cells.length);
+ expect(vm.$el.querySelectorAll('.cell').length).toBe(
+ jsonWithWorksheet.worksheets[0].cells.length,
+ );
});
it('renders markdown cell', () => {
diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js
index 155c91dcc46..3c57fe51352 100644
--- a/spec/javascripts/notes/components/comment_form_spec.js
+++ b/spec/javascripts/notes/components/comment_form_spec.js
@@ -51,6 +51,7 @@ describe('issue_comment_form component', () => {
spyOn(vm, 'stopPolling');
vm.handleSave();
+
expect(vm.isSubmitting).toEqual(true);
expect(vm.note).toEqual('');
expect(vm.saveNote).toHaveBeenCalled();
@@ -77,10 +78,14 @@ describe('issue_comment_form component', () => {
vm.handleSave();
Vue.nextTick()
- .then(() => expect(actionButton.disabled).toBeTruthy())
+ .then(() => {
+ expect(actionButton.disabled).toBeTruthy();
+ })
.then(saveNotePromise)
.then(Vue.nextTick)
- .then(() => expect(actionButton.disabled).toBeFalsy())
+ .then(() => {
+ expect(actionButton.disabled).toBeFalsy();
+ })
.then(done)
.catch(done.fail);
});
@@ -121,6 +126,7 @@ describe('issue_comment_form component', () => {
it('should link to markdown docs', () => {
const { markdownDocsPath } = notesDataMock;
+
expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual(
'Markdown',
);
@@ -128,6 +134,7 @@ describe('issue_comment_form component', () => {
it('should link to quick actions docs', () => {
const { quickActionsDocsPath } = notesDataMock;
+
expect(
vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim(),
).toEqual('quick actions');
@@ -215,6 +222,7 @@ describe('issue_comment_form component', () => {
expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual(
'Comment & close issue',
);
+
expect(vm.$el.querySelector('.js-note-discard')).toBeDefined();
done();
});
diff --git a/spec/javascripts/notes/components/diff_with_note_spec.js b/spec/javascripts/notes/components/diff_with_note_spec.js
index 239d7950907..95461396f10 100644
--- a/spec/javascripts/notes/components/diff_with_note_spec.js
+++ b/spec/javascripts/notes/components/diff_with_note_spec.js
@@ -1,7 +1,6 @@
import Vue from 'vue';
import DiffWithNote from '~/notes/components/diff_with_note.vue';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import createStore from '~/notes/stores';
+import { createStore } from '~/mr_notes/stores';
import { mountComponentWithStore } from 'spec/helpers';
const discussionFixture = 'merge_requests/diff_discussion.json';
@@ -11,7 +10,7 @@ describe('diff_with_note', () => {
let store;
let vm;
const diffDiscussionMock = getJSONFixture(discussionFixture)[0];
- const diffDiscussion = convertObjectPropsToCamelCase(diffDiscussionMock);
+ const diffDiscussion = diffDiscussionMock;
const Component = Vue.extend(DiffWithNote);
const props = {
discussion: diffDiscussion,
@@ -65,7 +64,7 @@ describe('diff_with_note', () => {
describe('image diff', () => {
beforeEach(() => {
const imageDiffDiscussionMock = getJSONFixture(imageDiscussionFixture)[0];
- props.discussion = convertObjectPropsToCamelCase(imageDiffDiscussionMock);
+ props.discussion = imageDiffDiscussionMock;
});
it('shows image diff', () => {
diff --git a/spec/javascripts/notes/components/discussion_filter_spec.js b/spec/javascripts/notes/components/discussion_filter_spec.js
new file mode 100644
index 00000000000..9070d968cfd
--- /dev/null
+++ b/spec/javascripts/notes/components/discussion_filter_spec.js
@@ -0,0 +1,86 @@
+import Vue from 'vue';
+import createStore from '~/notes/stores';
+import DiscussionFilter from '~/notes/components/discussion_filter.vue';
+import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { discussionFiltersMock, discussionMock } from '../mock_data';
+
+describe('DiscussionFilter component', () => {
+ let vm;
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+
+ const discussions = [
+ {
+ ...discussionMock,
+ id: discussionMock.id,
+ notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: true }],
+ },
+ ];
+ const Component = Vue.extend(DiscussionFilter);
+ const selectedValue = discussionFiltersMock[0].value;
+
+ store.state.discussions = discussions;
+ vm = mountComponentWithStore(Component, {
+ el: null,
+ store,
+ props: {
+ filters: discussionFiltersMock,
+ selectedValue,
+ },
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders the all filters', () => {
+ expect(vm.$el.querySelectorAll('.dropdown-menu li').length).toEqual(
+ discussionFiltersMock.length,
+ );
+ });
+
+ it('renders the default selected item', () => {
+ expect(vm.$el.querySelector('#discussion-filter-dropdown').textContent.trim()).toEqual(
+ discussionFiltersMock[0].title,
+ );
+ });
+
+ it('updates to the selected item', () => {
+ const filterItem = vm.$el.querySelector('.dropdown-menu li:last-child button');
+ filterItem.click();
+
+ expect(vm.currentFilter.title).toEqual(filterItem.textContent.trim());
+ });
+
+ it('only updates when selected filter changes', () => {
+ const filterItem = vm.$el.querySelector('.dropdown-menu li:first-child button');
+
+ spyOn(vm, 'filterDiscussion');
+ filterItem.click();
+
+ expect(vm.filterDiscussion).not.toHaveBeenCalled();
+ });
+
+ it('disables commenting when "Show history only" filter is applied', () => {
+ const filterItem = vm.$el.querySelector('.dropdown-menu li:last-child button');
+ filterItem.click();
+
+ expect(vm.$store.state.commentsDisabled).toBe(true);
+ });
+
+ it('enables commenting when "Show history only" filter is not applied', () => {
+ const filterItem = vm.$el.querySelector('.dropdown-menu li:first-child button');
+ filterItem.click();
+
+ expect(vm.$store.state.commentsDisabled).toBe(false);
+ });
+
+ it('renders a dropdown divider for the default filter', () => {
+ const defaultFilter = vm.$el.querySelector('.dropdown-menu li:first-child');
+
+ expect(defaultFilter.lastChild.classList).toContain('dropdown-divider');
+ });
+});
diff --git a/spec/javascripts/notes/components/note_actions_spec.js b/spec/javascripts/notes/components/note_actions_spec.js
index 52cc42cb53d..f6c854e6def 100644
--- a/spec/javascripts/notes/components/note_actions_spec.js
+++ b/spec/javascripts/notes/components/note_actions_spec.js
@@ -28,7 +28,7 @@ describe('issue_note_actions component', () => {
canEdit: true,
canAwardEmoji: true,
canReportAsAbuse: true,
- noteId: 539,
+ noteId: '539',
noteUrl: 'https://localhost:3000/group/project/merge_requests/1#note_1',
reportAbusePath:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26',
@@ -55,10 +55,24 @@ describe('issue_note_actions component', () => {
expect(vm.$el.querySelector('.js-note-edit')).toBeDefined();
});
- it('should be possible to report as abuse', () => {
+ it('should be possible to report abuse to GitLab', () => {
expect(vm.$el.querySelector(`a[href="${props.reportAbusePath}"]`)).toBeDefined();
});
+ it('should be possible to copy link to a note', () => {
+ expect(vm.$el.querySelector('.js-btn-copy-note-link')).not.toBeNull();
+ });
+
+ it('should not show copy link action when `noteUrl` prop is empty', done => {
+ vm.noteUrl = '';
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-btn-copy-note-link')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
it('should be possible to delete comment', () => {
expect(vm.$el.querySelector('.js-note-delete')).toBeDefined();
});
@@ -77,7 +91,7 @@ describe('issue_note_actions component', () => {
canEdit: false,
canAwardEmoji: false,
canReportAsAbuse: false,
- noteId: 539,
+ noteId: '539',
noteUrl: 'https://localhost:3000/group/project/merge_requests/1#note_1',
reportAbusePath:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26',
diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js
index 7eb4d3aed29..0081f42c330 100644
--- a/spec/javascripts/notes/components/note_app_spec.js
+++ b/spec/javascripts/notes/components/note_app_spec.js
@@ -121,6 +121,13 @@ describe('note_app', () => {
).toEqual('Write a comment or drag your files here…');
});
+ it('should not render form when commenting is disabled', () => {
+ store.state.commentsDisabled = true;
+ vm = mountComponent();
+
+ expect(vm.$el.querySelector('.js-main-target-form')).toEqual(null);
+ });
+
it('should render form comment button as disabled', () => {
expect(vm.$el.querySelector('.js-note-new-discussion').getAttribute('disabled')).toEqual(
'disabled',
@@ -223,6 +230,7 @@ describe('note_app', () => {
it('should render markdown docs url', () => {
const { markdownDocsPath } = mockData.notesDataMock;
+
expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual(
'Markdown',
);
@@ -230,6 +238,7 @@ describe('note_app', () => {
it('should render quick action docs url', () => {
const { quickActionsDocsPath } = mockData.notesDataMock;
+
expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual(
'quick actions',
);
diff --git a/spec/javascripts/notes/components/note_awards_list_spec.js b/spec/javascripts/notes/components/note_awards_list_spec.js
index 9d98ba219da..6a6a810acff 100644
--- a/spec/javascripts/notes/components/note_awards_list_spec.js
+++ b/spec/javascripts/notes/components/note_awards_list_spec.js
@@ -30,7 +30,7 @@ describe('note_awards_list component', () => {
propsData: {
awards: awardsMock,
noteAuthorId: 2,
- noteId: 545,
+ noteId: '545',
canAwardEmoji: true,
toggleAwardPath: '/gitlab-org/gitlab-ce/notes/545/toggle_award_emoji',
},
@@ -70,7 +70,7 @@ describe('note_awards_list component', () => {
propsData: {
awards: awardsMock,
noteAuthorId: 2,
- noteId: 545,
+ noteId: '545',
canAwardEmoji: false,
toggleAwardPath: '/gitlab-org/gitlab-ce/notes/545/toggle_award_emoji',
},
diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js
index 95d400ab3df..5db20fd285f 100644
--- a/spec/javascripts/notes/components/note_form_spec.js
+++ b/spec/javascripts/notes/components/note_form_spec.js
@@ -19,7 +19,7 @@ describe('issue_note_form component', () => {
props = {
isEditing: false,
noteBody: 'Magni suscipit eius consectetur enim et ex et commodi.',
- noteId: 545,
+ noteId: '545',
};
vm = new Component({
@@ -32,6 +32,22 @@ describe('issue_note_form component', () => {
vm.$destroy();
});
+ describe('noteHash', () => {
+ it('returns note hash string based on `noteId`', () => {
+ expect(vm.noteHash).toBe(`#note_${props.noteId}`);
+ });
+
+ it('return note hash as `#` when `noteId` is empty', done => {
+ vm.noteId = '';
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.noteHash).toBe('#');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
describe('conflicts editing', () => {
it('should show conflict message if note changes outside the component', done => {
vm.isEditing = true;
@@ -60,6 +76,7 @@ describe('issue_note_form component', () => {
it('should link to markdown docs', () => {
const { markdownDocsPath } = notesDataMock;
+
expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual(
'Markdown',
);
@@ -84,6 +101,7 @@ describe('issue_note_form component', () => {
expect(vm.handleUpdate).toHaveBeenCalled();
});
+
it('should save note when ctrl+enter is pressed', () => {
spyOn(vm, 'handleUpdate').and.callThrough();
vm.$el.querySelector('textarea').value = 'Foo';
diff --git a/spec/javascripts/notes/components/note_header_spec.js b/spec/javascripts/notes/components/note_header_spec.js
index a3c6bf78988..379780f43a0 100644
--- a/spec/javascripts/notes/components/note_header_spec.js
+++ b/spec/javascripts/notes/components/note_header_spec.js
@@ -33,7 +33,7 @@ describe('note_header component', () => {
},
createdAt: '2017-08-02T10:51:58.559Z',
includeToggle: false,
- noteId: 1394,
+ noteId: '1394',
expanded: true,
},
}).$mount();
@@ -47,6 +47,16 @@ describe('note_header component', () => {
it('should render timestamp link', () => {
expect(vm.$el.querySelector('a[href="#note_1394"]')).toBeDefined();
});
+
+ it('should not render user information when prop `author` is empty object', done => {
+ vm.author = {};
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.note-header-author-name')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
});
describe('discussion', () => {
@@ -66,7 +76,7 @@ describe('note_header component', () => {
},
createdAt: '2017-08-02T10:51:58.559Z',
includeToggle: true,
- noteId: 1395,
+ noteId: '1395',
expanded: true,
},
}).$mount();
diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js
index 2a01bd85520..81cb3e1f74d 100644
--- a/spec/javascripts/notes/components/noteable_discussion_spec.js
+++ b/spec/javascripts/notes/components/noteable_discussion_spec.js
@@ -3,6 +3,7 @@ import createStore from '~/notes/stores';
import noteableDiscussion from '~/notes/components/noteable_discussion.vue';
import '~/behaviors/markdown/render_gfm';
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
+import mockDiffFile from '../../diffs/mock_data/diff_file';
const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json';
@@ -33,9 +34,20 @@ describe('noteable_discussion component', () => {
expect(vm.$el.querySelector('.user-avatar-link')).not.toBeNull();
});
+ it('should not render discussion header for non diff discussions', () => {
+ expect(vm.$el.querySelector('.discussion-header')).toBeNull();
+ });
+
it('should render discussion header', () => {
- expect(vm.$el.querySelector('.discussion-header')).not.toBeNull();
- expect(vm.$el.querySelector('.notes').children.length).toEqual(discussionMock.notes.length);
+ const discussion = { ...discussionMock };
+ discussion.diff_file = mockDiffFile;
+ discussion.diff_discussion = true;
+ const diffDiscussionVm = new Component({
+ store,
+ propsData: { discussion },
+ }).$mount();
+
+ expect(diffDiscussionVm.$el.querySelector('.discussion-header')).not.toBeNull();
});
describe('actions', () => {
@@ -133,4 +145,27 @@ describe('noteable_discussion component', () => {
});
});
});
+
+ describe('componentData', () => {
+ it('should return first note object for placeholder note', () => {
+ const data = {
+ isPlaceholderNote: true,
+ notes: [{ body: 'hello world!' }],
+ };
+
+ const note = vm.componentData(data);
+
+ expect(note).toEqual(data.notes[0]);
+ });
+
+ it('should return given note for nonplaceholder notes', () => {
+ const data = {
+ notes: [{ id: 12 }],
+ };
+
+ const note = vm.componentData(data);
+
+ expect(note).toEqual(data);
+ });
+ });
});
diff --git a/spec/javascripts/notes/components/noteable_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js
index a31d17cacbb..8ade6fc2ced 100644
--- a/spec/javascripts/notes/components/noteable_note_spec.js
+++ b/spec/javascripts/notes/components/noteable_note_spec.js
@@ -36,6 +36,7 @@ describe('issue_note', () => {
it('should render note header content', () => {
const el = vm.$el.querySelector('.note-header .note-header-author-name');
+
expect(el.textContent.trim()).toEqual(note.author.name);
});
diff --git a/spec/javascripts/notes/components/toggle_replies_widget_spec.js b/spec/javascripts/notes/components/toggle_replies_widget_spec.js
new file mode 100644
index 00000000000..2ead8cc6e6a
--- /dev/null
+++ b/spec/javascripts/notes/components/toggle_replies_widget_spec.js
@@ -0,0 +1,78 @@
+import Vue from 'vue';
+import toggleRepliesWidget from '~/notes/components/toggle_replies_widget.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { note } from '../mock_data';
+
+const deepCloneObject = obj => JSON.parse(JSON.stringify(obj));
+
+describe('toggle replies widget for notes', () => {
+ let vm;
+ let ToggleRepliesWidget;
+ const noteFromOtherUser = deepCloneObject(note);
+ noteFromOtherUser.author.username = 'fatihacet';
+
+ const noteFromAnotherUser = deepCloneObject(note);
+ noteFromAnotherUser.author.username = 'mgreiling';
+ noteFromAnotherUser.author.name = 'Mike Greiling';
+
+ const replies = [note, note, note, noteFromOtherUser, noteFromAnotherUser];
+
+ beforeEach(() => {
+ ToggleRepliesWidget = Vue.extend(toggleRepliesWidget);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('collapsed state', () => {
+ beforeEach(() => {
+ vm = mountComponent(ToggleRepliesWidget, {
+ replies,
+ collapsed: true,
+ });
+ });
+
+ it('should render the collapsed', () => {
+ const vmTextContent = vm.$el.textContent.replace(/\s\s+/g, ' ');
+
+ expect(vm.$el.classList.contains('collapsed')).toEqual(true);
+ expect(vm.$el.querySelectorAll('.user-avatar-link').length).toEqual(3);
+ expect(vm.$el.querySelector('time')).not.toBeNull();
+ expect(vmTextContent).toContain('5 replies');
+ expect(vmTextContent).toContain(`Last reply by ${noteFromAnotherUser.author.name}`);
+ });
+
+ it('should emit toggle event when the replies text clicked', () => {
+ const spy = spyOn(vm, '$emit');
+
+ vm.$el.querySelector('.js-replies-text').click();
+
+ expect(spy).toHaveBeenCalledWith('toggle');
+ });
+ });
+
+ describe('expanded state', () => {
+ beforeEach(() => {
+ vm = mountComponent(ToggleRepliesWidget, {
+ replies,
+ collapsed: false,
+ });
+ });
+
+ it('should render expanded state', () => {
+ const vmTextContent = vm.$el.textContent.replace(/\s\s+/g, ' ');
+
+ expect(vm.$el.querySelector('.collapse-replies-btn')).not.toBeNull();
+ expect(vmTextContent).toContain('Collapse replies');
+ });
+
+ it('should emit toggle event when the collapse replies text called', () => {
+ const spy = spyOn(vm, '$emit');
+
+ vm.$el.querySelector('.js-collapse-replies').click();
+
+ expect(spy).toHaveBeenCalledWith('toggle');
+ });
+ });
+});
diff --git a/spec/javascripts/notes/helpers.js b/spec/javascripts/notes/helpers.js
index a7663710a56..3f349b40ba5 100644
--- a/spec/javascripts/notes/helpers.js
+++ b/spec/javascripts/notes/helpers.js
@@ -1,5 +1,5 @@
// eslint-disable-next-line import/prefer-default-export
-export const resetStore = (store) => {
+export const resetStore = store => {
store.replaceState({
notes: [],
targetNoteHash: null,
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index 67f6a9629d9..ad0e793b915 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -66,7 +66,7 @@ export const individualNote = {
individual_note: true,
notes: [
{
- id: 1390,
+ id: '1390',
attachment: {
url: null,
filename: null,
@@ -111,7 +111,7 @@ export const individualNote = {
};
export const note = {
- id: 546,
+ id: '546',
attachment: {
url: null,
filename: null,
@@ -174,7 +174,7 @@ export const discussionMock = {
expanded: true,
notes: [
{
- id: 1395,
+ id: '1395',
attachment: {
url: null,
filename: null,
@@ -211,7 +211,7 @@ export const discussionMock = {
path: '/gitlab-org/gitlab-ce/notes/1395',
},
{
- id: 1396,
+ id: '1396',
attachment: {
url: null,
filename: null,
@@ -257,7 +257,7 @@ export const discussionMock = {
path: '/gitlab-org/gitlab-ce/notes/1396',
},
{
- id: 1437,
+ id: '1437',
attachment: {
url: null,
filename: null,
@@ -308,7 +308,7 @@ export const discussionMock = {
};
export const loggedOutnoteableData = {
- id: 98,
+ id: '98',
iid: 26,
author_id: 1,
description: '',
@@ -358,7 +358,7 @@ export const collapseNotesMock = [
individual_note: true,
notes: [
{
- id: 1390,
+ id: '1390',
attachment: null,
author: {
id: 1,
@@ -393,7 +393,7 @@ export const collapseNotesMock = [
individual_note: true,
notes: [
{
- id: 1391,
+ id: '1391',
attachment: null,
author: {
id: 1,
@@ -433,7 +433,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = {
expanded: true,
notes: [
{
- id: 1390,
+ id: '1390',
attachment: {
url: null,
filename: null,
@@ -495,7 +495,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = {
expanded: true,
notes: [
{
- id: 1391,
+ id: '1391',
attachment: {
url: null,
filename: null,
@@ -544,7 +544,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = {
'/gitlab-org/gitlab-ce/notes/1471': {
commands_changes: null,
valid: true,
- id: 1471,
+ id: '1471',
attachment: null,
author: {
id: 1,
@@ -600,7 +600,7 @@ export const DISCUSSION_NOTE_RESPONSE_MAP = {
expanded: true,
notes: [
{
- id: 1471,
+ id: '1471',
attachment: {
url: null,
filename: null,
@@ -671,7 +671,7 @@ export const notesWithDescriptionChanges = [
expanded: true,
notes: [
{
- id: 901,
+ id: '901',
type: null,
attachment: null,
author: {
@@ -718,7 +718,7 @@ export const notesWithDescriptionChanges = [
expanded: true,
notes: [
{
- id: 902,
+ id: '902',
type: null,
attachment: null,
author: {
@@ -765,7 +765,7 @@ export const notesWithDescriptionChanges = [
expanded: true,
notes: [
{
- id: 903,
+ id: '903',
type: null,
attachment: null,
author: {
@@ -809,7 +809,7 @@ export const notesWithDescriptionChanges = [
expanded: true,
notes: [
{
- id: 904,
+ id: '904',
type: null,
attachment: null,
author: {
@@ -854,7 +854,7 @@ export const notesWithDescriptionChanges = [
expanded: true,
notes: [
{
- id: 905,
+ id: '905',
type: null,
attachment: null,
author: {
@@ -898,7 +898,7 @@ export const notesWithDescriptionChanges = [
expanded: true,
notes: [
{
- id: 906,
+ id: '906',
type: null,
attachment: null,
author: {
@@ -945,7 +945,7 @@ export const collapsedSystemNotes = [
expanded: true,
notes: [
{
- id: 901,
+ id: '901',
type: null,
attachment: null,
author: {
@@ -992,7 +992,7 @@ export const collapsedSystemNotes = [
expanded: true,
notes: [
{
- id: 902,
+ id: '902',
type: null,
attachment: null,
author: {
@@ -1039,7 +1039,7 @@ export const collapsedSystemNotes = [
expanded: true,
notes: [
{
- id: 904,
+ id: '904',
type: null,
attachment: null,
author: {
@@ -1084,7 +1084,7 @@ export const collapsedSystemNotes = [
expanded: true,
notes: [
{
- id: 905,
+ id: '905',
type: null,
attachment: null,
author: {
@@ -1104,7 +1104,7 @@ export const collapsedSystemNotes = [
resolvable: false,
noteable_iid: 12,
note: 'changed the description',
- note_html: '\n <p dir="auto">changed the description 2 times within 1 minute </p>',
+ note_html: ' <p dir="auto">changed the description 2 times within 1 minute </p>',
current_user: { can_edit: false, can_award_emoji: true },
resolved: false,
resolved_by: null,
@@ -1129,7 +1129,7 @@ export const collapsedSystemNotes = [
expanded: true,
notes: [
{
- id: 906,
+ id: '906',
type: null,
attachment: null,
author: {
@@ -1177,10 +1177,8 @@ export const discussion1 = {
file_path: 'about.md',
},
position: {
- formatter: {
- new_line: 50,
- old_line: null,
- },
+ new_line: 50,
+ old_line: null,
},
notes: [
{
@@ -1197,10 +1195,8 @@ export const resolvedDiscussion1 = {
file_path: 'about.md',
},
position: {
- formatter: {
- new_line: 50,
- old_line: null,
- },
+ new_line: 50,
+ old_line: null,
},
notes: [
{
@@ -1217,10 +1213,8 @@ export const discussion2 = {
file_path: 'README.md',
},
position: {
- formatter: {
- new_line: null,
- old_line: 20,
- },
+ new_line: null,
+ old_line: 20,
},
notes: [
{
@@ -1237,10 +1231,8 @@ export const discussion3 = {
file_path: 'README.md',
},
position: {
- formatter: {
- new_line: 21,
- old_line: null,
- },
+ new_line: 21,
+ old_line: null,
},
notes: [
{
@@ -1252,3 +1244,18 @@ export const discussion3 = {
export const unresolvableDiscussion = {
resolvable: false,
};
+
+export const discussionFiltersMock = [
+ {
+ title: 'Show all activity',
+ value: 0,
+ },
+ {
+ title: 'Show comments only',
+ value: 1,
+ },
+ {
+ title: 'Show system notes only',
+ value: 2,
+ },
+];
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index b66e8e1ceb3..fcdd834e4a0 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -3,6 +3,7 @@ import _ from 'underscore';
import { headersInterceptor } from 'spec/helpers/vue_resource_helper';
import * as actions from '~/notes/stores/actions';
import createStore from '~/notes/stores';
+import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub';
import testAction from '../../helpers/vuex_action_helper';
import { resetStore } from '../helpers';
import {
@@ -317,4 +318,211 @@ describe('Actions Notes Store', () => {
);
});
});
+
+ describe('deleteNote', () => {
+ const interceptor = (request, next) => {
+ next(
+ request.respondWith(JSON.stringify({}), {
+ status: 200,
+ }),
+ );
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(interceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
+ });
+
+ it('commits DELETE_NOTE and dispatches updateMergeRequestWidget', done => {
+ const note = { path: `${gl.TEST_HOST}`, id: 1 };
+
+ testAction(
+ actions.deleteNote,
+ note,
+ store.state,
+ [
+ {
+ type: 'DELETE_NOTE',
+ payload: note,
+ },
+ ],
+ [
+ {
+ type: 'updateMergeRequestWidget',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('createNewNote', () => {
+ describe('success', () => {
+ const res = {
+ id: 1,
+ valid: true,
+ };
+ const interceptor = (request, next) => {
+ next(
+ request.respondWith(JSON.stringify(res), {
+ status: 200,
+ }),
+ );
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(interceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
+ });
+
+ it('commits ADD_NEW_NOTE and dispatches updateMergeRequestWidget', done => {
+ testAction(
+ actions.createNewNote,
+ { endpoint: `${gl.TEST_HOST}`, data: {} },
+ store.state,
+ [
+ {
+ type: 'ADD_NEW_NOTE',
+ payload: res,
+ },
+ ],
+ [
+ {
+ type: 'updateMergeRequestWidget',
+ },
+ {
+ type: 'startTaskList',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ const res = {
+ errors: ['error'],
+ };
+ const interceptor = (request, next) => {
+ next(
+ request.respondWith(JSON.stringify(res), {
+ status: 200,
+ }),
+ );
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(interceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
+ });
+
+ it('does not commit ADD_NEW_NOTE or dispatch updateMergeRequestWidget', done => {
+ testAction(
+ actions.createNewNote,
+ { endpoint: `${gl.TEST_HOST}`, data: {} },
+ store.state,
+ [],
+ [],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('toggleResolveNote', () => {
+ const res = {
+ resolved: true,
+ };
+ const interceptor = (request, next) => {
+ next(
+ request.respondWith(JSON.stringify(res), {
+ status: 200,
+ }),
+ );
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(interceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
+ });
+
+ describe('as note', () => {
+ it('commits UPDATE_NOTE and dispatches updateMergeRequestWidget', done => {
+ testAction(
+ actions.toggleResolveNote,
+ { endpoint: `${gl.TEST_HOST}`, isResolved: true, discussion: false },
+ store.state,
+ [
+ {
+ type: 'UPDATE_NOTE',
+ payload: res,
+ },
+ ],
+ [
+ {
+ type: 'updateMergeRequestWidget',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('as discussion', () => {
+ it('commits UPDATE_DISCUSSION and dispatches updateMergeRequestWidget', done => {
+ testAction(
+ actions.toggleResolveNote,
+ { endpoint: `${gl.TEST_HOST}`, isResolved: true, discussion: true },
+ store.state,
+ [
+ {
+ type: 'UPDATE_DISCUSSION',
+ payload: res,
+ },
+ ],
+ [
+ {
+ type: 'updateMergeRequestWidget',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('updateMergeRequestWidget', () => {
+ it('calls mrWidget checkStatus', () => {
+ spyOn(mrWidgetEventHub, '$emit');
+
+ actions.updateMergeRequestWidget();
+
+ expect(mrWidgetEventHub.$emit).toHaveBeenCalledWith('mr.discussion.updated');
+ });
+ });
+
+ describe('setCommentsDisabled', () => {
+ it('should set comments disabled state', done => {
+ testAction(
+ actions.setCommentsDisabled,
+ true,
+ null,
+ [{ type: 'DISABLE_COMMENTS', payload: true }],
+ [],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/javascripts/notes/stores/collapse_utils_spec.js b/spec/javascripts/notes/stores/collapse_utils_spec.js
index 06a6aab932a..8ede9319088 100644
--- a/spec/javascripts/notes/stores/collapse_utils_spec.js
+++ b/spec/javascripts/notes/stores/collapse_utils_spec.js
@@ -4,10 +4,7 @@ import {
getTimeDifferenceMinutes,
collapseSystemNotes,
} from '~/notes/stores/collapse_utils';
-import {
- notesWithDescriptionChanges,
- collapsedSystemNotes,
-} from '../mock_data';
+import { notesWithDescriptionChanges, collapsedSystemNotes } from '../mock_data';
describe('Collapse utils', () => {
const mockSystemNote = {
@@ -22,14 +19,18 @@ describe('Collapse utils', () => {
});
it('returns false when a system note is not a description type', () => {
- expect(isDescriptionSystemNote(Object.assign({}, mockSystemNote, { note: 'foo' }))).toEqual(false);
+ expect(isDescriptionSystemNote(Object.assign({}, mockSystemNote, { note: 'foo' }))).toEqual(
+ false,
+ );
});
it('changes the description to contain the number of changed times', () => {
const changedNote = changeDescriptionNote(mockSystemNote, 3, 5);
expect(changedNote.times_updated).toEqual(3);
- expect(changedNote.note_html.trim()).toContain('<p dir="auto">changed the description 3 times within 5 minutes </p>');
+ expect(changedNote.note_html.trim()).toContain(
+ '<p dir="auto">changed the description 3 times within 5 minutes </p>',
+ );
});
it('gets the time difference between two notes', () => {
diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js
index 7f8ede51508..f853f9ff088 100644
--- a/spec/javascripts/notes/stores/getters_spec.js
+++ b/spec/javascripts/notes/stores/getters_spec.js
@@ -205,6 +205,7 @@ describe('Getters Notes Store', () => {
'123',
'456',
]);
+
expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(undefined)).toEqual([
'123',
'456',
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
index a15ff1a5888..461de5a3106 100644
--- a/spec/javascripts/notes/stores/mutation_spec.js
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -1,3 +1,4 @@
+import Vue from 'vue';
import mutations from '~/notes/stores/mutations';
import {
note,
@@ -29,11 +30,13 @@ describe('Notes Store mutations', () => {
expect(state).toEqual({
discussions: [noteData],
});
+
expect(state.discussions.length).toBe(1);
});
it('should not add the same note to the notes array', () => {
mutations.ADD_NEW_NOTE(state, note);
+
expect(state.discussions.length).toBe(1);
});
});
@@ -75,7 +78,7 @@ describe('Notes Store mutations', () => {
});
describe('COLLAPSE_DISCUSSION', () => {
- it('should collpase an expanded discussion', () => {
+ it('should collapse an expanded discussion', () => {
const discussion = Object.assign({}, discussionMock, { expanded: true });
const state = {
@@ -105,6 +108,7 @@ describe('Notes Store mutations', () => {
};
mutations.SET_NOTES_DATA(state, notesDataMock);
+
expect(state.notesData).toEqual(notesDataMock);
});
});
@@ -116,6 +120,7 @@ describe('Notes Store mutations', () => {
};
mutations.SET_NOTEABLE_DATA(state, noteableDataMock);
+
expect(state.noteableData).toEqual(noteableDataMock);
});
});
@@ -127,6 +132,7 @@ describe('Notes Store mutations', () => {
};
mutations.SET_USER_DATA(state, userDataMock);
+
expect(state.userData).toEqual(userDataMock);
});
});
@@ -150,11 +156,47 @@ describe('Notes Store mutations', () => {
};
mutations.SET_INITIAL_DISCUSSIONS(state, [note, legacyNote]);
+
expect(state.discussions[0].id).toEqual(note.id);
expect(state.discussions[1].notes[0].note).toBe(legacyNote.notes[0].note);
expect(state.discussions[2].notes[0].note).toBe(legacyNote.notes[1].note);
expect(state.discussions.length).toEqual(3);
});
+
+ it('adds truncated_diff_lines if discussion is a diffFile', () => {
+ const state = {
+ discussions: [],
+ };
+
+ mutations.SET_INITIAL_DISCUSSIONS(state, [
+ {
+ ...note,
+ diff_file: {
+ file_hash: 'a',
+ },
+ truncated_diff_lines: ['a'],
+ },
+ ]);
+
+ expect(state.discussions[0].truncated_diff_lines).toEqual(['a']);
+ });
+
+ it('adds empty truncated_diff_lines when not in discussion', () => {
+ const state = {
+ discussions: [],
+ };
+
+ mutations.SET_INITIAL_DISCUSSIONS(state, [
+ {
+ ...note,
+ diff_file: {
+ file_hash: 'a',
+ },
+ },
+ ]);
+
+ expect(state.discussions[0].truncated_diff_lines).toEqual([]);
+ });
});
describe('SET_LAST_FETCHED_AT', () => {
@@ -164,6 +206,7 @@ describe('Notes Store mutations', () => {
};
mutations.SET_LAST_FETCHED_AT(state, 'timestamp');
+
expect(state.lastFetchedAt).toEqual('timestamp');
});
});
@@ -175,6 +218,7 @@ describe('Notes Store mutations', () => {
};
mutations.SET_TARGET_NOTE_HASH(state, 'hash');
+
expect(state.targetNoteHash).toEqual('hash');
});
});
@@ -185,6 +229,7 @@ describe('Notes Store mutations', () => {
discussions: [],
};
mutations.SHOW_PLACEHOLDER_NOTE(state, note);
+
expect(state.discussions[0].isPlaceholderNote).toEqual(true);
});
});
@@ -225,6 +270,7 @@ describe('Notes Store mutations', () => {
awardName: 'bath_tone3',
};
mutations.TOGGLE_AWARD(state, data);
+
expect(state.discussions[0].award_emoji.length).toEqual(2);
});
});
@@ -280,6 +326,7 @@ describe('Notes Store mutations', () => {
};
mutations.CLOSE_ISSUE(state);
+
expect(state.noteableData.state).toEqual('closed');
});
});
@@ -297,6 +344,7 @@ describe('Notes Store mutations', () => {
};
mutations.REOPEN_ISSUE(state);
+
expect(state.noteableData.state).toEqual('reopened');
});
});
@@ -314,6 +362,7 @@ describe('Notes Store mutations', () => {
};
mutations.TOGGLE_STATE_BUTTON_LOADING(state, true);
+
expect(state.isToggleStateButtonLoading).toEqual(true);
});
@@ -329,18 +378,63 @@ describe('Notes Store mutations', () => {
};
mutations.TOGGLE_STATE_BUTTON_LOADING(state, false);
+
expect(state.isToggleStateButtonLoading).toEqual(false);
});
});
- describe('SET_NOTES_FETCHING_STATE', () => {
+ describe('SET_NOTES_FETCHED_STATE', () => {
it('should set the given state', () => {
const state = {
isNotesFetched: false,
};
mutations.SET_NOTES_FETCHED_STATE(state, true);
+
expect(state.isNotesFetched).toEqual(true);
});
});
+
+ describe('SET_DISCUSSION_DIFF_LINES', () => {
+ it('sets truncated_diff_lines', () => {
+ const state = {
+ discussions: [
+ {
+ id: 1,
+ },
+ ],
+ };
+
+ mutations.SET_DISCUSSION_DIFF_LINES(state, { discussionId: 1, diffLines: ['test'] });
+
+ expect(state.discussions[0].truncated_diff_lines).toEqual(['test']);
+ });
+
+ it('keeps reactivity of discussion', () => {
+ const state = {};
+ Vue.set(state, 'discussions', [
+ {
+ id: 1,
+ expanded: false,
+ },
+ ]);
+ const discussion = state.discussions[0];
+
+ mutations.SET_DISCUSSION_DIFF_LINES(state, { discussionId: 1, diffLines: ['test'] });
+
+ discussion.expanded = true;
+
+ expect(state.discussions[0].expanded).toBe(true);
+ });
+ });
+
+ describe('DISABLE_COMMENTS', () => {
+ it('should set comments disabled state', () => {
+ const state = {};
+
+ mutations.DISABLE_COMMENTS(state, true);
+
+ expect(state.commentsDisabled).toEqual(true);
+ });
+ });
});
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index faeedae40e9..694f581150f 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -10,1011 +10,1024 @@ import '~/behaviors/markdown/render_gfm';
import Notes from '~/notes';
import timeoutPromise from './helpers/set_timeout_promise_helper';
-(function() {
- window.gon || (window.gon = {});
- window.gl = window.gl || {};
- gl.utils = gl.utils || {};
-
- const htmlEscape = comment => {
- const escapedString = comment.replace(/["&'<>]/g, a => {
- const escapedToken = {
- '&': '&amp;',
- '<': '&lt;',
- '>': '&gt;',
- '"': '&quot;',
- "'": '&#x27;',
- '`': '&#x60;',
- }[a];
-
- return escapedToken;
- });
-
- return escapedString;
- };
-
- describe('Notes', function() {
- const FLASH_TYPE_ALERT = 'alert';
- const NOTES_POST_PATH = /(.*)\/notes\?html=true$/;
- var fixture = 'snippets/show.html.raw';
- preloadFixtures(fixture);
+window.gon || (window.gon = {});
+window.gl = window.gl || {};
+gl.utils = gl.utils || {};
+
+const htmlEscape = comment => {
+ const escapedString = comment.replace(/["&'<>]/g, a => {
+ const escapedToken = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ "'": '&#x27;',
+ '`': '&#x60;',
+ }[a];
+
+ return escapedToken;
+ });
- beforeEach(function() {
- loadFixtures(fixture);
- gl.utils.disableButtonIfEmptyField = _.noop;
- window.project_uploads_path = 'http://test.host/uploads';
- $('body').attr('data-page', 'projects:merge_requets:show');
- });
+ return escapedString;
+};
- afterEach(() => {
- // Undo what we did to the shared <body>
- $('body').removeAttr('data-page');
- });
+describe('Notes', function() {
+ const FLASH_TYPE_ALERT = 'alert';
+ const NOTES_POST_PATH = /(.*)\/notes\?html=true$/;
+ var fixture = 'snippets/show.html.raw';
+ preloadFixtures(fixture);
- describe('addBinding', () => {
- it('calls postComment when comment button is clicked', () => {
- spyOn(Notes.prototype, 'postComment');
- this.notes = new Notes('', []);
+ beforeEach(function() {
+ loadFixtures(fixture);
+ gl.utils.disableButtonIfEmptyField = _.noop;
+ window.project_uploads_path = 'http://test.host/uploads';
+ $('body').attr('data-page', 'projects:merge_requets:show');
+ });
- $('.js-comment-button').click();
+ afterEach(() => {
+ // Undo what we did to the shared <body>
+ $('body').removeAttr('data-page');
+ });
- expect(Notes.prototype.postComment).toHaveBeenCalled();
- });
+ describe('addBinding', () => {
+ it('calls postComment when comment button is clicked', () => {
+ spyOn(Notes.prototype, 'postComment');
+ this.notes = new Notes('', []);
+
+ $('.js-comment-button').click();
+
+ expect(Notes.prototype.postComment).toHaveBeenCalled();
});
+ });
- describe('task lists', function() {
- let mock;
+ describe('task lists', function() {
+ let mock;
- beforeEach(function() {
- spyOn(axios, 'patch').and.callFake(() => new Promise(() => {}));
- mock = new MockAdapter(axios);
- mock.onAny().reply(200, {});
+ beforeEach(function() {
+ spyOn(axios, 'patch').and.callFake(() => new Promise(() => {}));
+ mock = new MockAdapter(axios);
+ mock.onAny().reply(200, {});
- $('.js-comment-button').on('click', function(e) {
- e.preventDefault();
- });
- this.notes = new Notes('', []);
+ $('.js-comment-button').on('click', function(e) {
+ e.preventDefault();
});
+ this.notes = new Notes('', []);
+ });
- afterEach(() => {
- mock.restore();
- });
+ afterEach(() => {
+ mock.restore();
+ });
- it('modifies the Markdown field', function() {
- const changeEvent = document.createEvent('HTMLEvents');
- changeEvent.initEvent('change', true, true);
- $('input[type=checkbox]')
- .attr('checked', true)[0]
- .dispatchEvent(changeEvent);
+ it('modifies the Markdown field', function() {
+ const changeEvent = document.createEvent('HTMLEvents');
+ changeEvent.initEvent('change', true, true);
+ $('input[type=checkbox]')
+ .attr('checked', true)[0]
+ .dispatchEvent(changeEvent);
- expect($('.js-task-list-field.original-task-list').val()).toBe('- [x] Task List Item');
- });
+ expect($('.js-task-list-field.original-task-list').val()).toBe('- [x] Task List Item');
+ });
- it('submits an ajax request on tasklist:changed', function(done) {
- $('.js-task-list-container').trigger('tasklist:changed');
+ it('submits an ajax request on tasklist:changed', function(done) {
+ $('.js-task-list-container').trigger('tasklist:changed');
- setTimeout(() => {
- expect(axios.patch).toHaveBeenCalled();
- done();
- });
+ setTimeout(() => {
+ expect(axios.patch).toHaveBeenCalled();
+ done();
});
});
+ });
- describe('comments', function() {
- var textarea = '.js-note-text';
+ describe('comments', function() {
+ var textarea = '.js-note-text';
- beforeEach(function() {
- this.notes = new Notes('', []);
+ beforeEach(function() {
+ this.notes = new Notes('', []);
- this.autoSizeSpy = spyOnEvent($(textarea), 'autosize:update');
- spyOn(this.notes, 'renderNote').and.stub();
+ this.autoSizeSpy = spyOnEvent($(textarea), 'autosize:update');
+ spyOn(this.notes, 'renderNote').and.stub();
- $(textarea).data('autosave', {
- reset: function() {},
- });
+ $(textarea).data('autosave', {
+ reset: function() {},
+ });
- $('.js-comment-button').on('click', e => {
- const $form = $(this);
- e.preventDefault();
- this.notes.addNote($form);
- this.notes.reenableTargetFormSubmitButton(e);
- this.notes.resetMainTargetForm(e);
- });
+ $('.js-comment-button').on('click', e => {
+ const $form = $(this);
+ e.preventDefault();
+ this.notes.addNote($form);
+ this.notes.reenableTargetFormSubmitButton(e);
+ this.notes.resetMainTargetForm(e);
});
+ });
- it('autosizes after comment submission', function() {
- $(textarea).text('This is an example comment note');
- expect(this.autoSizeSpy).not.toHaveBeenTriggered();
+ it('autosizes after comment submission', function() {
+ $(textarea).text('This is an example comment note');
- $('.js-comment-button').click();
- expect(this.autoSizeSpy).toHaveBeenTriggered();
- });
+ expect(this.autoSizeSpy).not.toHaveBeenTriggered();
- it('should not place escaped text in the comment box in case of error', function() {
- const deferred = $.Deferred();
- spyOn($, 'ajax').and.returnValue(deferred.promise());
- $(textarea).text('A comment with `markup`.');
+ $('.js-comment-button').click();
- deferred.reject();
- $('.js-comment-button').click();
- expect($(textarea).val()).toEqual('A comment with `markup`.');
- });
+ expect(this.autoSizeSpy).toHaveBeenTriggered();
});
- describe('updateNote', () => {
- let sampleComment;
- let noteEntity;
- let $form;
- let $notesContainer;
- let mock;
+ it('should not place escaped text in the comment box in case of error', function() {
+ const deferred = $.Deferred();
+ spyOn($, 'ajax').and.returnValue(deferred.promise());
+ $(textarea).text('A comment with `markup`.');
- beforeEach(() => {
- this.notes = new Notes('', []);
- window.gon.current_username = 'root';
- window.gon.current_user_fullname = 'Administrator';
- sampleComment = 'foo';
- noteEntity = {
- id: 1234,
- html: `<li class="note note-row-1234 timeline-entry" id="note_1234">
- <div class="note-text">${sampleComment}</div>
- </li>`,
- note: sampleComment,
- valid: true,
- };
- $form = $('form.js-main-target-form');
- $notesContainer = $('ul.main-notes-list');
- $form.find('textarea.js-note-text').val(sampleComment);
+ deferred.reject();
+ $('.js-comment-button').click();
- mock = new MockAdapter(axios);
- mock.onPost(NOTES_POST_PATH).reply(200, noteEntity);
- });
+ expect($(textarea).val()).toEqual('A comment with `markup`.');
+ });
+ });
- afterEach(() => {
- mock.restore();
- });
+ describe('updateNote', () => {
+ let sampleComment;
+ let noteEntity;
+ let $form;
+ let $notesContainer;
+ let mock;
+
+ beforeEach(() => {
+ this.notes = new Notes('', []);
+ window.gon.current_username = 'root';
+ window.gon.current_user_fullname = 'Administrator';
+ sampleComment = 'foo';
+ noteEntity = {
+ id: 1234,
+ html: `<li class="note note-row-1234 timeline-entry" id="note_1234">
+ <div class="note-text">${sampleComment}</div>
+ </li>`,
+ note: sampleComment,
+ valid: true,
+ };
+ $form = $('form.js-main-target-form');
+ $notesContainer = $('ul.main-notes-list');
+ $form.find('textarea.js-note-text').val(sampleComment);
+
+ mock = new MockAdapter(axios);
+ mock.onPost(NOTES_POST_PATH).reply(200, noteEntity);
+ });
- it('updates note and resets edit form', done => {
- spyOn(this.notes, 'revertNoteEditForm');
- spyOn(this.notes, 'setupNewNote');
+ afterEach(() => {
+ mock.restore();
+ });
- $('.js-comment-button').click();
+ it('updates note and resets edit form', done => {
+ spyOn(this.notes, 'revertNoteEditForm');
+ spyOn(this.notes, 'setupNewNote');
- setTimeout(() => {
- const $targetNote = $notesContainer.find(`#note_${noteEntity.id}`);
- const updatedNote = Object.assign({}, noteEntity);
- updatedNote.note = 'bar';
- this.notes.updateNote(updatedNote, $targetNote);
+ $('.js-comment-button').click();
- expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote);
- expect(this.notes.setupNewNote).toHaveBeenCalled();
+ setTimeout(() => {
+ const $targetNote = $notesContainer.find(`#note_${noteEntity.id}`);
+ const updatedNote = Object.assign({}, noteEntity);
+ updatedNote.note = 'bar';
+ this.notes.updateNote(updatedNote, $targetNote);
- done();
- });
+ expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote);
+ expect(this.notes.setupNewNote).toHaveBeenCalled();
+
+ done();
});
});
+ });
- describe('updateNoteTargetSelector', () => {
- const hash = 'note_foo';
- let $note;
+ describe('updateNoteTargetSelector', () => {
+ const hash = 'note_foo';
+ let $note;
- beforeEach(() => {
- $note = $(`<div id="${hash}"></div>`);
- spyOn($note, 'filter').and.callThrough();
- spyOn($note, 'toggleClass').and.callThrough();
- });
+ beforeEach(() => {
+ $note = $(`<div id="${hash}"></div>`);
+ spyOn($note, 'filter').and.callThrough();
+ spyOn($note, 'toggleClass').and.callThrough();
+ });
- it('sets target when hash matches', () => {
- spyOnDependency(Notes, 'getLocationHash').and.returnValue(hash);
+ it('sets target when hash matches', () => {
+ spyOnDependency(Notes, 'getLocationHash').and.returnValue(hash);
- Notes.updateNoteTargetSelector($note);
+ Notes.updateNoteTargetSelector($note);
- expect($note.filter).toHaveBeenCalledWith(`#${hash}`);
- expect($note.toggleClass).toHaveBeenCalledWith('target', true);
- });
+ expect($note.filter).toHaveBeenCalledWith(`#${hash}`);
+ expect($note.toggleClass).toHaveBeenCalledWith('target', true);
+ });
- it('unsets target when hash does not match', () => {
- spyOnDependency(Notes, 'getLocationHash').and.returnValue('note_doesnotexist');
+ it('unsets target when hash does not match', () => {
+ spyOnDependency(Notes, 'getLocationHash').and.returnValue('note_doesnotexist');
- Notes.updateNoteTargetSelector($note);
+ Notes.updateNoteTargetSelector($note);
- expect($note.toggleClass).toHaveBeenCalledWith('target', false);
- });
+ expect($note.toggleClass).toHaveBeenCalledWith('target', false);
+ });
- it('unsets target when there is not a hash fragment anymore', () => {
- spyOnDependency(Notes, 'getLocationHash').and.returnValue(null);
+ it('unsets target when there is not a hash fragment anymore', () => {
+ spyOnDependency(Notes, 'getLocationHash').and.returnValue(null);
- Notes.updateNoteTargetSelector($note);
+ Notes.updateNoteTargetSelector($note);
- expect($note.toggleClass).toHaveBeenCalledWith('target', false);
- });
+ expect($note.toggleClass).toHaveBeenCalledWith('target', false);
});
+ });
- describe('renderNote', () => {
- let notes;
- let note;
- let $notesList;
+ describe('renderNote', () => {
+ let notes;
+ let note;
+ let $notesList;
- beforeEach(() => {
- note = {
- id: 1,
- valid: true,
- note: 'heya',
- html: '<div>heya</div>',
- };
- $notesList = jasmine.createSpyObj('$notesList', ['find', 'append']);
-
- notes = jasmine.createSpyObj('notes', [
- 'setupNewNote',
- 'refresh',
- 'collapseLongCommitList',
- 'updateNotesCount',
- 'putConflictEditWarningInPlace',
- ]);
- notes.taskList = jasmine.createSpyObj('tasklist', ['init']);
- notes.note_ids = [];
- notes.updatedNotesTrackingMap = {};
-
- spyOn(Notes, 'isNewNote').and.callThrough();
- spyOn(Notes, 'isUpdatedNote').and.callThrough();
- spyOn(Notes, 'animateAppendNote').and.callThrough();
- spyOn(Notes, 'animateUpdateNote').and.callThrough();
+ beforeEach(() => {
+ note = {
+ id: 1,
+ valid: true,
+ note: 'heya',
+ html: '<div>heya</div>',
+ };
+ $notesList = jasmine.createSpyObj('$notesList', ['find', 'append']);
+
+ notes = jasmine.createSpyObj('notes', [
+ 'setupNewNote',
+ 'refresh',
+ 'collapseLongCommitList',
+ 'updateNotesCount',
+ 'putConflictEditWarningInPlace',
+ ]);
+ notes.taskList = jasmine.createSpyObj('tasklist', ['init']);
+ notes.note_ids = [];
+ notes.updatedNotesTrackingMap = {};
+
+ spyOn(Notes, 'isNewNote').and.callThrough();
+ spyOn(Notes, 'isUpdatedNote').and.callThrough();
+ spyOn(Notes, 'animateAppendNote').and.callThrough();
+ spyOn(Notes, 'animateUpdateNote').and.callThrough();
+ });
+
+ describe('when adding note', () => {
+ it('should call .animateAppendNote', () => {
+ Notes.isNewNote.and.returnValue(true);
+ Notes.prototype.renderNote.call(notes, note, null, $notesList);
+
+ expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, $notesList);
});
+ });
- describe('when adding note', () => {
- it('should call .animateAppendNote', () => {
- Notes.isNewNote.and.returnValue(true);
- Notes.prototype.renderNote.call(notes, note, null, $notesList);
+ describe('when note was edited', () => {
+ it('should call .animateUpdateNote', () => {
+ Notes.isNewNote.and.returnValue(false);
+ Notes.isUpdatedNote.and.returnValue(true);
+ const $note = $('<div>');
+ $notesList.find.and.returnValue($note);
+ const $newNote = $(note.html);
+ Notes.animateUpdateNote.and.returnValue($newNote);
- expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, $notesList);
- });
+ Notes.prototype.renderNote.call(notes, note, null, $notesList);
+
+ expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note);
+ expect(notes.setupNewNote).toHaveBeenCalledWith($newNote);
});
- describe('when note was edited', () => {
- it('should call .animateUpdateNote', () => {
+ describe('while editing', () => {
+ it('should update textarea if nothing has been touched', () => {
Notes.isNewNote.and.returnValue(false);
Notes.isUpdatedNote.and.returnValue(true);
- const $note = $('<div>');
+ const $note = $(`<div class="is-editing">
+ <div class="original-note-content">initial</div>
+ <textarea class="js-note-text">initial</textarea>
+ </div>`);
$notesList.find.and.returnValue($note);
- const $newNote = $(note.html);
- Notes.animateUpdateNote.and.returnValue($newNote);
-
Notes.prototype.renderNote.call(notes, note, null, $notesList);
- expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note);
- expect(notes.setupNewNote).toHaveBeenCalledWith($newNote);
+ expect($note.find('.js-note-text').val()).toEqual(note.note);
});
- describe('while editing', () => {
- it('should update textarea if nothing has been touched', () => {
- Notes.isNewNote.and.returnValue(false);
- Notes.isUpdatedNote.and.returnValue(true);
- const $note = $(`<div class="is-editing">
- <div class="original-note-content">initial</div>
- <textarea class="js-note-text">initial</textarea>
- </div>`);
- $notesList.find.and.returnValue($note);
- Notes.prototype.renderNote.call(notes, note, null, $notesList);
-
- expect($note.find('.js-note-text').val()).toEqual(note.note);
- });
-
- it('should call .putConflictEditWarningInPlace', () => {
- Notes.isNewNote.and.returnValue(false);
- Notes.isUpdatedNote.and.returnValue(true);
- const $note = $(`<div class="is-editing">
- <div class="original-note-content">initial</div>
- <textarea class="js-note-text">different</textarea>
- </div>`);
- $notesList.find.and.returnValue($note);
- Notes.prototype.renderNote.call(notes, note, null, $notesList);
-
- expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(note, $note);
- });
+ it('should call .putConflictEditWarningInPlace', () => {
+ Notes.isNewNote.and.returnValue(false);
+ Notes.isUpdatedNote.and.returnValue(true);
+ const $note = $(`<div class="is-editing">
+ <div class="original-note-content">initial</div>
+ <textarea class="js-note-text">different</textarea>
+ </div>`);
+ $notesList.find.and.returnValue($note);
+ Notes.prototype.renderNote.call(notes, note, null, $notesList);
+
+ expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(note, $note);
});
});
});
+ });
- describe('isUpdatedNote', () => {
- it('should consider same note text as the same', () => {
- const result = Notes.isUpdatedNote(
- {
- note: 'initial',
- },
- $(`<div>
- <div class="original-note-content">initial</div>
- </div>`),
- );
+ describe('isUpdatedNote', () => {
+ it('should consider same note text as the same', () => {
+ const result = Notes.isUpdatedNote(
+ {
+ note: 'initial',
+ },
+ $(`<div>
+ <div class="original-note-content">initial</div>
+ </div>`),
+ );
- expect(result).toEqual(false);
- });
+ expect(result).toEqual(false);
+ });
- it('should consider same note with trailing newline as the same', () => {
- const result = Notes.isUpdatedNote(
- {
- note: 'initial\n',
- },
- $(`<div>
- <div class="original-note-content">initial\n</div>
- </div>`),
- );
+ it('should consider same note with trailing newline as the same', () => {
+ const result = Notes.isUpdatedNote(
+ {
+ note: 'initial\n',
+ },
+ $(`<div>
+ <div class="original-note-content">initial\n</div>
+ </div>`),
+ );
- expect(result).toEqual(false);
- });
+ expect(result).toEqual(false);
+ });
- it('should consider different notes as different', () => {
- const result = Notes.isUpdatedNote(
- {
- note: 'foo',
- },
- $(`<div>
- <div class="original-note-content">bar</div>
- </div>`),
- );
+ it('should consider different notes as different', () => {
+ const result = Notes.isUpdatedNote(
+ {
+ note: 'foo',
+ },
+ $(`<div>
+ <div class="original-note-content">bar</div>
+ </div>`),
+ );
- expect(result).toEqual(true);
- });
+ expect(result).toEqual(true);
+ });
+ });
+
+ describe('renderDiscussionNote', () => {
+ let discussionContainer;
+ let note;
+ let notes;
+ let $form;
+ let row;
+
+ beforeEach(() => {
+ note = {
+ html: '<li></li>',
+ discussion_html: '<div></div>',
+ discussion_id: 1,
+ discussion_resolvable: false,
+ diff_discussion_html: false,
+ };
+ $form = jasmine.createSpyObj('$form', ['closest', 'find']);
+ $form.length = 1;
+ row = jasmine.createSpyObj('row', ['prevAll', 'first', 'find']);
+
+ notes = jasmine.createSpyObj('notes', ['isParallelView', 'updateNotesCount']);
+ notes.note_ids = [];
+
+ spyOn(Notes, 'isNewNote');
+ spyOn(Notes, 'animateAppendNote');
+ Notes.isNewNote.and.returnValue(true);
+ notes.isParallelView.and.returnValue(false);
+ row.prevAll.and.returnValue(row);
+ row.first.and.returnValue(row);
+ row.find.and.returnValue(row);
});
- describe('renderDiscussionNote', () => {
- let discussionContainer;
- let note;
- let notes;
- let $form;
- let row;
+ describe('Discussion root note', () => {
+ let body;
beforeEach(() => {
- note = {
- html: '<li></li>',
- discussion_html: '<div></div>',
- discussion_id: 1,
- discussion_resolvable: false,
- diff_discussion_html: false,
- };
- $form = jasmine.createSpyObj('$form', ['closest', 'find']);
- $form.length = 1;
- row = jasmine.createSpyObj('row', ['prevAll', 'first', 'find']);
-
- notes = jasmine.createSpyObj('notes', ['isParallelView', 'updateNotesCount']);
- notes.note_ids = [];
+ body = jasmine.createSpyObj('body', ['attr']);
+ discussionContainer = { length: 0 };
- spyOn(Notes, 'isNewNote');
- spyOn(Notes, 'animateAppendNote');
- Notes.isNewNote.and.returnValue(true);
- notes.isParallelView.and.returnValue(false);
- row.prevAll.and.returnValue(row);
- row.first.and.returnValue(row);
- row.find.and.returnValue(row);
+ $form.closest.and.returnValues(row, $form);
+ $form.find.and.returnValues(discussionContainer);
+ body.attr.and.returnValue('');
});
- describe('Discussion root note', () => {
- let body;
-
- beforeEach(() => {
- body = jasmine.createSpyObj('body', ['attr']);
- discussionContainer = { length: 0 };
+ it('should call Notes.animateAppendNote', () => {
+ Notes.prototype.renderDiscussionNote.call(notes, note, $form);
- $form.closest.and.returnValues(row, $form);
- $form.find.and.returnValues(discussionContainer);
- body.attr.and.returnValue('');
- });
-
- it('should call Notes.animateAppendNote', () => {
- Notes.prototype.renderDiscussionNote.call(notes, note, $form);
-
- expect(Notes.animateAppendNote).toHaveBeenCalledWith(
- note.discussion_html,
- $('.main-notes-list'),
- );
- });
+ expect(Notes.animateAppendNote).toHaveBeenCalledWith(
+ note.discussion_html,
+ $('.main-notes-list'),
+ );
+ });
- it('should append to row selected with line_code', () => {
- $form.length = 0;
- note.discussion_line_code = 'line_code';
- note.diff_discussion_html = '<tr></tr>';
+ it('should append to row selected with line_code', () => {
+ $form.length = 0;
+ note.discussion_line_code = 'line_code';
+ note.diff_discussion_html = '<tr></tr>';
- const line = document.createElement('div');
- line.id = note.discussion_line_code;
- document.body.appendChild(line);
+ const line = document.createElement('div');
+ line.id = note.discussion_line_code;
+ document.body.appendChild(line);
- $form.closest.and.returnValues($form);
+ $form.closest.and.returnValues($form);
- Notes.prototype.renderDiscussionNote.call(notes, note, $form);
+ Notes.prototype.renderDiscussionNote.call(notes, note, $form);
- expect(line.nextSibling.outerHTML).toEqual(note.diff_discussion_html);
- });
+ expect(line.nextSibling.outerHTML).toEqual(note.diff_discussion_html);
});
+ });
- describe('Discussion sub note', () => {
- beforeEach(() => {
- discussionContainer = { length: 1 };
+ describe('Discussion sub note', () => {
+ beforeEach(() => {
+ discussionContainer = { length: 1 };
- $form.closest.and.returnValues(row, $form);
- $form.find.and.returnValues(discussionContainer);
+ $form.closest.and.returnValues(row, $form);
+ $form.find.and.returnValues(discussionContainer);
- Notes.prototype.renderDiscussionNote.call(notes, note, $form);
- });
+ Notes.prototype.renderDiscussionNote.call(notes, note, $form);
+ });
- it('should call Notes.animateAppendNote', () => {
- expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, discussionContainer);
- });
+ it('should call Notes.animateAppendNote', () => {
+ expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, discussionContainer);
});
});
+ });
- describe('animateAppendNote', () => {
- let noteHTML;
- let $notesList;
- let $resultantNote;
+ describe('animateAppendNote', () => {
+ let noteHTML;
+ let $notesList;
+ let $resultantNote;
- beforeEach(() => {
- noteHTML = '<div></div>';
- $notesList = jasmine.createSpyObj('$notesList', ['append']);
+ beforeEach(() => {
+ noteHTML = '<div></div>';
+ $notesList = jasmine.createSpyObj('$notesList', ['append']);
- $resultantNote = Notes.animateAppendNote(noteHTML, $notesList);
- });
+ $resultantNote = Notes.animateAppendNote(noteHTML, $notesList);
+ });
- it('should have `fade-in-full` class', () => {
- expect($resultantNote.hasClass('fade-in-full')).toEqual(true);
- });
+ it('should have `fade-in-full` class', () => {
+ expect($resultantNote.hasClass('fade-in-full')).toEqual(true);
+ });
- it('should append note to the notes list', () => {
- expect($notesList.append).toHaveBeenCalledWith($resultantNote);
- });
+ it('should append note to the notes list', () => {
+ expect($notesList.append).toHaveBeenCalledWith($resultantNote);
});
+ });
- describe('animateUpdateNote', () => {
- let noteHTML;
- let $note;
- let $updatedNote;
+ describe('animateUpdateNote', () => {
+ let noteHTML;
+ let $note;
+ let $updatedNote;
- beforeEach(() => {
- noteHTML = '<div></div>';
- $note = jasmine.createSpyObj('$note', ['replaceWith']);
+ beforeEach(() => {
+ noteHTML = '<div></div>';
+ $note = jasmine.createSpyObj('$note', ['replaceWith']);
- $updatedNote = Notes.animateUpdateNote(noteHTML, $note);
- });
+ $updatedNote = Notes.animateUpdateNote(noteHTML, $note);
+ });
- it('should have `fade-in` class', () => {
- expect($updatedNote.hasClass('fade-in')).toEqual(true);
- });
+ it('should have `fade-in` class', () => {
+ expect($updatedNote.hasClass('fade-in')).toEqual(true);
+ });
- it('should call replaceWith on $note', () => {
- expect($note.replaceWith).toHaveBeenCalledWith($updatedNote);
- });
+ it('should call replaceWith on $note', () => {
+ expect($note.replaceWith).toHaveBeenCalledWith($updatedNote);
});
+ });
- describe('putEditFormInPlace', () => {
- it('should call GLForm with GFM parameter passed through', () => {
- const notes = new Notes('', []);
- const $el = $(`
- <div>
- <form></form>
- </div>
- `);
+ describe('putEditFormInPlace', () => {
+ it('should call GLForm with GFM parameter passed through', () => {
+ const notes = new Notes('', []);
+ const $el = $(`
+ <div>
+ <form></form>
+ </div>
+ `);
- notes.putEditFormInPlace($el);
+ notes.putEditFormInPlace($el);
- expect(notes.glForm.enableGFM).toBeTruthy();
- });
+ expect(notes.glForm.enableGFM).toBeTruthy();
});
+ });
- describe('postComment & updateComment', () => {
- const sampleComment = 'foo';
- const updatedComment = 'bar';
- const note = {
- id: 1234,
- html: `<li class="note note-row-1234 timeline-entry" id="note_1234">
- <div class="note-text">${sampleComment}</div>
- </li>`,
- note: sampleComment,
- valid: true,
- };
- let $form;
- let $notesContainer;
- let mock;
+ describe('postComment & updateComment', () => {
+ const sampleComment = 'foo';
+ const updatedComment = 'bar';
+ const note = {
+ id: 1234,
+ html: `<li class="note note-row-1234 timeline-entry" id="note_1234">
+ <div class="note-text">${sampleComment}</div>
+ </li>`,
+ note: sampleComment,
+ valid: true,
+ };
+ let $form;
+ let $notesContainer;
+ let mock;
+
+ function mockNotesPost() {
+ mock.onPost(NOTES_POST_PATH).reply(200, note);
+ }
+
+ function mockNotesPostError() {
+ mock.onPost(NOTES_POST_PATH).networkError();
+ }
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+
+ this.notes = new Notes('', []);
+ window.gon.current_username = 'root';
+ window.gon.current_user_fullname = 'Administrator';
+ $form = $('form.js-main-target-form');
+ $notesContainer = $('ul.main-notes-list');
+ $form.find('textarea.js-note-text').val(sampleComment);
+ });
- function mockNotesPost() {
- mock.onPost(NOTES_POST_PATH).reply(200, note);
- }
+ afterEach(() => {
+ mock.restore();
+ });
- function mockNotesPostError() {
- mock.onPost(NOTES_POST_PATH).networkError();
- }
+ it('should show placeholder note while new comment is being posted', () => {
+ mockNotesPost();
- beforeEach(() => {
- mock = new MockAdapter(axios);
-
- this.notes = new Notes('', []);
- window.gon.current_username = 'root';
- window.gon.current_user_fullname = 'Administrator';
- $form = $('form.js-main-target-form');
- $notesContainer = $('ul.main-notes-list');
- $form.find('textarea.js-note-text').val(sampleComment);
- });
+ $('.js-comment-button').click();
- afterEach(() => {
- mock.restore();
- });
+ expect($notesContainer.find('.note.being-posted').length).toBeGreaterThan(0);
+ });
- it('should show placeholder note while new comment is being posted', () => {
- mockNotesPost();
+ it('should remove placeholder note when new comment is done posting', done => {
+ mockNotesPost();
- $('.js-comment-button').click();
- expect($notesContainer.find('.note.being-posted').length > 0).toEqual(true);
- });
+ $('.js-comment-button').click();
- it('should remove placeholder note when new comment is done posting', done => {
- mockNotesPost();
+ setTimeout(() => {
+ expect($notesContainer.find('.note.being-posted').length).toEqual(0);
- $('.js-comment-button').click();
+ done();
+ });
+ });
- setTimeout(() => {
- expect($notesContainer.find('.note.being-posted').length).toEqual(0);
+ describe('postComment', () => {
+ it('disables the submit button', done => {
+ const $submitButton = $form.find('.js-comment-submit-button');
- done();
+ expect($submitButton).not.toBeDisabled();
+ const dummyEvent = {
+ preventDefault() {},
+ target: $submitButton,
+ };
+ mock.onPost(NOTES_POST_PATH).replyOnce(() => {
+ expect($submitButton).toBeDisabled();
+ return [200, note];
});
- });
- describe('postComment', () => {
- it('disables the submit button', done => {
- const $submitButton = $form.find('.js-comment-submit-button');
- expect($submitButton).not.toBeDisabled();
- const dummyEvent = {
- preventDefault() {},
- target: $submitButton,
- };
- mock.onPost(NOTES_POST_PATH).replyOnce(() => {
- expect($submitButton).toBeDisabled();
- return [200, note];
- });
-
- this.notes
- .postComment(dummyEvent)
- .then(() => {
- expect($submitButton).not.toBeDisabled();
- })
- .then(done)
- .catch(done.fail);
- });
+ this.notes
+ .postComment(dummyEvent)
+ .then(() => {
+ expect($submitButton).not.toBeDisabled();
+ })
+ .then(done)
+ .catch(done.fail);
});
+ });
- it('should show actual note element when new comment is done posting', done => {
- mockNotesPost();
+ it('should show actual note element when new comment is done posting', done => {
+ mockNotesPost();
- $('.js-comment-button').click();
+ $('.js-comment-button').click();
- setTimeout(() => {
- expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual(true);
+ setTimeout(() => {
+ expect($notesContainer.find(`#note_${note.id}`).length).toBeGreaterThan(0);
- done();
- });
+ done();
});
+ });
- it('should reset Form when new comment is done posting', done => {
- mockNotesPost();
+ it('should reset Form when new comment is done posting', done => {
+ mockNotesPost();
- $('.js-comment-button').click();
+ $('.js-comment-button').click();
- setTimeout(() => {
- expect($form.find('textarea.js-note-text').val()).toEqual('');
+ setTimeout(() => {
+ expect($form.find('textarea.js-note-text').val()).toEqual('');
- done();
- });
+ done();
});
+ });
- it('should show flash error message when new comment failed to be posted', done => {
- mockNotesPostError();
+ it('should show flash error message when new comment failed to be posted', done => {
+ mockNotesPostError();
- $('.js-comment-button').click();
+ $('.js-comment-button').click();
- setTimeout(() => {
- expect(
- $notesContainer
- .parent()
- .find('.flash-container .flash-text')
- .is(':visible'),
- ).toEqual(true);
+ setTimeout(() => {
+ expect(
+ $notesContainer
+ .parent()
+ .find('.flash-container .flash-text')
+ .is(':visible'),
+ ).toEqual(true);
- done();
- });
+ done();
});
+ });
- it('should show flash error message when comment failed to be updated', done => {
- mockNotesPost();
+ it('should show flash error message when comment failed to be updated', done => {
+ mockNotesPost();
- $('.js-comment-button').click();
+ $('.js-comment-button').click();
- timeoutPromise()
- .then(() => {
- const $noteEl = $notesContainer.find(`#note_${note.id}`);
- $noteEl.find('.js-note-edit').click();
- $noteEl.find('textarea.js-note-text').val(updatedComment);
+ timeoutPromise()
+ .then(() => {
+ const $noteEl = $notesContainer.find(`#note_${note.id}`);
+ $noteEl.find('.js-note-edit').click();
+ $noteEl.find('textarea.js-note-text').val(updatedComment);
- mock.restore();
+ mock.restore();
- mockNotesPostError();
+ mockNotesPostError();
- $noteEl.find('.js-comment-save-button').click();
- })
- .then(timeoutPromise)
- .then(() => {
- const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`);
- expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals
- expect(
- $updatedNoteEl
- .find('.note-text')
- .text()
- .trim(),
- ).toEqual(sampleComment); // See if comment reverted back to original
- expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown
-
- done();
- })
- .catch(done.fail);
- });
+ $noteEl.find('.js-comment-save-button').click();
+ })
+ .then(timeoutPromise)
+ .then(() => {
+ const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`);
+
+ expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals
+ expect(
+ $updatedNoteEl
+ .find('.note-text')
+ .text()
+ .trim(),
+ ).toEqual(sampleComment); // See if comment reverted back to original
+
+ expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown
+
+ done();
+ })
+ .catch(done.fail);
});
+ });
- describe('postComment with Slash commands', () => {
- const sampleComment = '/assign @root\n/award :100:';
- const note = {
- commands_changes: {
- assignee_id: 1,
- emoji_award: '100',
- },
- errors: {
- commands_only: ['Commands applied'],
+ describe('postComment with Slash commands', () => {
+ const sampleComment = '/assign @root\n/award :100:';
+ const note = {
+ commands_changes: {
+ assignee_id: 1,
+ emoji_award: '100',
+ },
+ errors: {
+ commands_only: ['Commands applied'],
+ },
+ valid: false,
+ };
+ let $form;
+ let $notesContainer;
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onPost(NOTES_POST_PATH).reply(200, note);
+
+ this.notes = new Notes('', []);
+ window.gon.current_username = 'root';
+ window.gon.current_user_fullname = 'Administrator';
+ gl.awardsHandler = {
+ addAwardToEmojiBar: () => {},
+ scrollToAwards: () => {},
+ };
+ gl.GfmAutoComplete = {
+ dataSources: {
+ commands: '/root/test-project/autocomplete_sources/commands',
},
- valid: false,
};
- let $form;
- let $notesContainer;
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onPost(NOTES_POST_PATH).reply(200, note);
-
- this.notes = new Notes('', []);
- window.gon.current_username = 'root';
- window.gon.current_user_fullname = 'Administrator';
- gl.awardsHandler = {
- addAwardToEmojiBar: () => {},
- scrollToAwards: () => {},
- };
- gl.GfmAutoComplete = {
- dataSources: {
- commands: '/root/test-project/autocomplete_sources/commands',
- },
- };
- $form = $('form.js-main-target-form');
- $notesContainer = $('ul.main-notes-list');
- $form.find('textarea.js-note-text').val(sampleComment);
- });
+ $form = $('form.js-main-target-form');
+ $notesContainer = $('ul.main-notes-list');
+ $form.find('textarea.js-note-text').val(sampleComment);
+ });
- afterEach(() => {
- mock.restore();
- });
+ afterEach(() => {
+ mock.restore();
+ });
- it('should remove slash command placeholder when comment with slash commands is done posting', done => {
- spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough();
- $('.js-comment-button').click();
+ it('should remove slash command placeholder when comment with slash commands is done posting', done => {
+ spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough();
+ $('.js-comment-button').click();
- expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown
+ expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown
- setTimeout(() => {
- expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed
- done();
- });
+ setTimeout(() => {
+ expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed
+ done();
});
});
+ });
- describe('update comment with script tags', () => {
- const sampleComment = '<script></script>';
- const updatedComment = '<script></script>';
- const note = {
- id: 1234,
- html: `<li class="note note-row-1234 timeline-entry" id="note_1234">
- <div class="note-text">${sampleComment}</div>
- </li>`,
- note: sampleComment,
- valid: true,
- };
- let $form;
- let $notesContainer;
- let mock;
+ describe('update comment with script tags', () => {
+ const sampleComment = '<script></script>';
+ const updatedComment = '<script></script>';
+ const note = {
+ id: 1234,
+ html: `<li class="note note-row-1234 timeline-entry" id="note_1234">
+ <div class="note-text">${sampleComment}</div>
+ </li>`,
+ note: sampleComment,
+ valid: true,
+ };
+ let $form;
+ let $notesContainer;
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onPost(NOTES_POST_PATH).reply(200, note);
+
+ this.notes = new Notes('', []);
+ window.gon.current_username = 'root';
+ window.gon.current_user_fullname = 'Administrator';
+ $form = $('form.js-main-target-form');
+ $notesContainer = $('ul.main-notes-list');
+ $form.find('textarea.js-note-text').html(sampleComment);
+ });
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onPost(NOTES_POST_PATH).reply(200, note);
-
- this.notes = new Notes('', []);
- window.gon.current_username = 'root';
- window.gon.current_user_fullname = 'Administrator';
- $form = $('form.js-main-target-form');
- $notesContainer = $('ul.main-notes-list');
- $form.find('textarea.js-note-text').html(sampleComment);
- });
+ afterEach(() => {
+ mock.restore();
+ });
- afterEach(() => {
- mock.restore();
- });
+ it('should not render a script tag', done => {
+ $('.js-comment-button').click();
- it('should not render a script tag', done => {
- $('.js-comment-button').click();
+ setTimeout(() => {
+ const $noteEl = $notesContainer.find(`#note_${note.id}`);
+ $noteEl.find('.js-note-edit').click();
+ $noteEl.find('textarea.js-note-text').html(updatedComment);
+ $noteEl.find('.js-comment-save-button').click();
- setTimeout(() => {
- const $noteEl = $notesContainer.find(`#note_${note.id}`);
- $noteEl.find('.js-note-edit').click();
- $noteEl.find('textarea.js-note-text').html(updatedComment);
- $noteEl.find('.js-comment-save-button').click();
+ const $updatedNoteEl = $notesContainer
+ .find(`#note_${note.id}`)
+ .find('.js-task-list-container');
- const $updatedNoteEl = $notesContainer
- .find(`#note_${note.id}`)
- .find('.js-task-list-container');
- expect(
- $updatedNoteEl
- .find('.note-text')
- .text()
- .trim(),
- ).toEqual('');
+ expect(
+ $updatedNoteEl
+ .find('.note-text')
+ .text()
+ .trim(),
+ ).toEqual('');
- done();
- });
+ done();
});
});
+ });
- describe('getFormData', () => {
- let $form;
- let sampleComment;
+ describe('getFormData', () => {
+ let $form;
+ let sampleComment;
- beforeEach(() => {
- this.notes = new Notes('', []);
+ beforeEach(() => {
+ this.notes = new Notes('', []);
- $form = $('form');
- sampleComment = 'foobar';
- });
+ $form = $('form');
+ sampleComment = 'foobar';
+ });
- it('should return form metadata object from form reference', () => {
- $form.find('textarea.js-note-text').val(sampleComment);
- const { formData, formContent, formAction } = this.notes.getFormData($form);
+ it('should return form metadata object from form reference', () => {
+ $form.find('textarea.js-note-text').val(sampleComment);
+ const { formData, formContent, formAction } = this.notes.getFormData($form);
- expect(formData.indexOf(sampleComment) > -1).toBe(true);
- expect(formContent).toEqual(sampleComment);
- expect(formAction).toEqual($form.attr('action'));
- });
+ expect(formData.indexOf(sampleComment)).toBeGreaterThan(-1);
+ expect(formContent).toEqual(sampleComment);
+ expect(formAction).toEqual($form.attr('action'));
+ });
- it('should return form metadata with sanitized formContent from form reference', () => {
- spyOn(_, 'escape').and.callFake(htmlEscape);
+ it('should return form metadata with sanitized formContent from form reference', () => {
+ spyOn(_, 'escape').and.callFake(htmlEscape);
- sampleComment = '<script>alert("Boom!");</script>';
- $form.find('textarea.js-note-text').val(sampleComment);
+ sampleComment = '<script>alert("Boom!");</script>';
+ $form.find('textarea.js-note-text').val(sampleComment);
- const { formContent } = this.notes.getFormData($form);
+ const { formContent } = this.notes.getFormData($form);
- expect(_.escape).toHaveBeenCalledWith(sampleComment);
- expect(formContent).toEqual('&lt;script&gt;alert(&quot;Boom!&quot;);&lt;/script&gt;');
- });
+ expect(_.escape).toHaveBeenCalledWith(sampleComment);
+ expect(formContent).toEqual('&lt;script&gt;alert(&quot;Boom!&quot;);&lt;/script&gt;');
});
+ });
- describe('hasQuickActions', () => {
- beforeEach(() => {
- this.notes = new Notes('', []);
- });
+ describe('hasQuickActions', () => {
+ beforeEach(() => {
+ this.notes = new Notes('', []);
+ });
- it('should return true when comment begins with a quick action', () => {
- const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
- const hasQuickActions = this.notes.hasQuickActions(sampleComment);
+ it('should return true when comment begins with a quick action', () => {
+ const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
+ const hasQuickActions = this.notes.hasQuickActions(sampleComment);
- expect(hasQuickActions).toBeTruthy();
- });
+ expect(hasQuickActions).toBeTruthy();
+ });
- it('should return false when comment does NOT begin with a quick action', () => {
- const sampleComment = 'Hey, /unassign Merging this';
- const hasQuickActions = this.notes.hasQuickActions(sampleComment);
+ it('should return false when comment does NOT begin with a quick action', () => {
+ const sampleComment = 'Hey, /unassign Merging this';
+ const hasQuickActions = this.notes.hasQuickActions(sampleComment);
- expect(hasQuickActions).toBeFalsy();
- });
+ expect(hasQuickActions).toBeFalsy();
+ });
- it('should return false when comment does NOT have any quick actions', () => {
- const sampleComment = 'Looking good, Awesome!';
- const hasQuickActions = this.notes.hasQuickActions(sampleComment);
+ it('should return false when comment does NOT have any quick actions', () => {
+ const sampleComment = 'Looking good, Awesome!';
+ const hasQuickActions = this.notes.hasQuickActions(sampleComment);
- expect(hasQuickActions).toBeFalsy();
- });
+ expect(hasQuickActions).toBeFalsy();
});
+ });
- describe('stripQuickActions', () => {
- it('should strip quick actions from the comment which begins with a quick action', () => {
- this.notes = new Notes();
- const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
- const stripedComment = this.notes.stripQuickActions(sampleComment);
+ describe('stripQuickActions', () => {
+ it('should strip quick actions from the comment which begins with a quick action', () => {
+ this.notes = new Notes();
+ const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
+ const stripedComment = this.notes.stripQuickActions(sampleComment);
- expect(stripedComment).toBe('');
- });
+ expect(stripedComment).toBe('');
+ });
- it('should strip quick actions from the comment but leaves plain comment if it is present', () => {
- this.notes = new Notes();
- const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this';
- const stripedComment = this.notes.stripQuickActions(sampleComment);
+ it('should strip quick actions from the comment but leaves plain comment if it is present', () => {
+ this.notes = new Notes();
+ const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this';
+ const stripedComment = this.notes.stripQuickActions(sampleComment);
- expect(stripedComment).toBe('Merging this');
- });
+ expect(stripedComment).toBe('Merging this');
+ });
- it('should NOT strip string that has slashes within', () => {
- this.notes = new Notes();
- const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1';
- const stripedComment = this.notes.stripQuickActions(sampleComment);
+ it('should NOT strip string that has slashes within', () => {
+ this.notes = new Notes();
+ const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1';
+ const stripedComment = this.notes.stripQuickActions(sampleComment);
- expect(stripedComment).toBe(sampleComment);
- });
+ expect(stripedComment).toBe(sampleComment);
});
+ });
- describe('getQuickActionDescription', () => {
- const availableQuickActions = [
- { name: 'close', description: 'Close this issue', params: [] },
- { name: 'title', description: 'Change title', params: [{}] },
- { name: 'estimate', description: 'Set time estimate', params: [{}] },
- ];
+ describe('getQuickActionDescription', () => {
+ const availableQuickActions = [
+ { name: 'close', description: 'Close this issue', params: [] },
+ { name: 'title', description: 'Change title', params: [{}] },
+ { name: 'estimate', description: 'Set time estimate', params: [{}] },
+ ];
- beforeEach(() => {
- this.notes = new Notes();
- });
+ beforeEach(() => {
+ this.notes = new Notes();
+ });
- it('should return executing quick action description when note has single quick action', () => {
- const sampleComment = '/close';
- expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe(
- 'Applying command to close this issue',
- );
- });
+ it('should return executing quick action description when note has single quick action', () => {
+ const sampleComment = '/close';
- it('should return generic multiple quick action description when note has multiple quick actions', () => {
- const sampleComment = '/close\n/title [Duplicate] Issue foobar';
- expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe(
- 'Applying multiple commands',
- );
- });
+ expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe(
+ 'Applying command to close this issue',
+ );
+ });
- it('should return generic quick action description when available quick actions list is not populated', () => {
- const sampleComment = '/close\n/title [Duplicate] Issue foobar';
- expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command');
- });
+ it('should return generic multiple quick action description when note has multiple quick actions', () => {
+ const sampleComment = '/close\n/title [Duplicate] Issue foobar';
+
+ expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe(
+ 'Applying multiple commands',
+ );
});
- describe('createPlaceholderNote', () => {
- const sampleComment = 'foobar';
- const uniqueId = 'b1234-a4567';
- const currentUsername = 'root';
- const currentUserFullname = 'Administrator';
- const currentUserAvatar = 'avatar_url';
+ it('should return generic quick action description when available quick actions list is not populated', () => {
+ const sampleComment = '/close\n/title [Duplicate] Issue foobar';
- beforeEach(() => {
- this.notes = new Notes('', []);
- });
+ expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command');
+ });
+ });
- it('should return constructed placeholder element for regular note based on form contents', () => {
- const $tempNote = this.notes.createPlaceholderNote({
- formContent: sampleComment,
- uniqueId,
- isDiscussionNote: false,
- currentUsername,
- currentUserFullname,
- currentUserAvatar,
- });
- const $tempNoteHeader = $tempNote.find('.note-header');
-
- expect($tempNote.prop('nodeName')).toEqual('LI');
- expect($tempNote.attr('id')).toEqual(uniqueId);
- expect($tempNote.hasClass('being-posted')).toBeTruthy();
- expect($tempNote.hasClass('fade-in-half')).toBeTruthy();
- $tempNote.find('.timeline-icon > a, .note-header-info > a').each(function() {
- expect($(this).attr('href')).toEqual(`/${currentUsername}`);
- });
- expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(currentUserAvatar);
- expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy();
- expect(
- $tempNoteHeader
- .find('.d-none.d-sm-inline-block')
- .text()
- .trim(),
- ).toEqual(currentUserFullname);
- expect(
- $tempNoteHeader
- .find('.note-headline-light')
- .text()
- .trim(),
- ).toEqual(`@${currentUsername}`);
- expect(
- $tempNote
- .find('.note-body .note-text p')
- .text()
- .trim(),
- ).toEqual(sampleComment);
- });
+ describe('createPlaceholderNote', () => {
+ const sampleComment = 'foobar';
+ const uniqueId = 'b1234-a4567';
+ const currentUsername = 'root';
+ const currentUserFullname = 'Administrator';
+ const currentUserAvatar = 'avatar_url';
- it('should return constructed placeholder element for discussion note based on form contents', () => {
- const $tempNote = this.notes.createPlaceholderNote({
- formContent: sampleComment,
- uniqueId,
- isDiscussionNote: true,
- currentUsername,
- currentUserFullname,
- });
+ beforeEach(() => {
+ this.notes = new Notes('', []);
+ });
- expect($tempNote.prop('nodeName')).toEqual('LI');
- expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy();
- });
+ it('should return constructed placeholder element for regular note based on form contents', () => {
+ const $tempNote = this.notes.createPlaceholderNote({
+ formContent: sampleComment,
+ uniqueId,
+ isDiscussionNote: false,
+ currentUsername,
+ currentUserFullname,
+ currentUserAvatar,
+ });
+ const $tempNoteHeader = $tempNote.find('.note-header');
+
+ expect($tempNote.prop('nodeName')).toEqual('LI');
+ expect($tempNote.attr('id')).toEqual(uniqueId);
+ expect($tempNote.hasClass('being-posted')).toBeTruthy();
+ expect($tempNote.hasClass('fade-in-half')).toBeTruthy();
+ $tempNote.find('.timeline-icon > a, .note-header-info > a').each(function() {
+ expect($(this).attr('href')).toEqual(`/${currentUsername}`);
+ });
+
+ expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(currentUserAvatar);
+ expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy();
+ expect(
+ $tempNoteHeader
+ .find('.d-none.d-sm-inline-block')
+ .text()
+ .trim(),
+ ).toEqual(currentUserFullname);
+
+ expect(
+ $tempNoteHeader
+ .find('.note-headline-light')
+ .text()
+ .trim(),
+ ).toEqual(`@${currentUsername}`);
+
+ expect(
+ $tempNote
+ .find('.note-body .note-text p')
+ .text()
+ .trim(),
+ ).toEqual(sampleComment);
+ });
- it('should return a escaped user name', () => {
- const currentUserFullnameXSS = 'Foo <script>alert("XSS")</script>';
- const $tempNote = this.notes.createPlaceholderNote({
- formContent: sampleComment,
- uniqueId,
- isDiscussionNote: false,
- currentUsername,
- currentUserFullname: currentUserFullnameXSS,
- currentUserAvatar,
- });
- const $tempNoteHeader = $tempNote.find('.note-header');
- expect(
- $tempNoteHeader
- .find('.d-none.d-sm-inline-block')
- .text()
- .trim(),
- ).toEqual('Foo &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;');
+ it('should return constructed placeholder element for discussion note based on form contents', () => {
+ const $tempNote = this.notes.createPlaceholderNote({
+ formContent: sampleComment,
+ uniqueId,
+ isDiscussionNote: true,
+ currentUsername,
+ currentUserFullname,
});
+
+ expect($tempNote.prop('nodeName')).toEqual('LI');
+ expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy();
});
- describe('createPlaceholderSystemNote', () => {
- const sampleCommandDescription = 'Applying command to close this issue';
- const uniqueId = 'b1234-a4567';
+ it('should return a escaped user name', () => {
+ const currentUserFullnameXSS = 'Foo <script>alert("XSS")</script>';
+ const $tempNote = this.notes.createPlaceholderNote({
+ formContent: sampleComment,
+ uniqueId,
+ isDiscussionNote: false,
+ currentUsername,
+ currentUserFullname: currentUserFullnameXSS,
+ currentUserAvatar,
+ });
+ const $tempNoteHeader = $tempNote.find('.note-header');
+
+ expect(
+ $tempNoteHeader
+ .find('.d-none.d-sm-inline-block')
+ .text()
+ .trim(),
+ ).toEqual('Foo &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;');
+ });
+ });
- beforeEach(() => {
- this.notes = new Notes('', []);
- spyOn(_, 'escape').and.callFake(htmlEscape);
- });
+ describe('createPlaceholderSystemNote', () => {
+ const sampleCommandDescription = 'Applying command to close this issue';
+ const uniqueId = 'b1234-a4567';
- it('should return constructed placeholder element for system note based on form contents', () => {
- const $tempNote = this.notes.createPlaceholderSystemNote({
- formContent: sampleCommandDescription,
- uniqueId,
- });
+ beforeEach(() => {
+ this.notes = new Notes('', []);
+ spyOn(_, 'escape').and.callFake(htmlEscape);
+ });
- expect($tempNote.prop('nodeName')).toEqual('LI');
- expect($tempNote.attr('id')).toEqual(uniqueId);
- expect($tempNote.hasClass('being-posted')).toBeTruthy();
- expect($tempNote.hasClass('fade-in-half')).toBeTruthy();
- expect(
- $tempNote
- .find('.timeline-content i')
- .text()
- .trim(),
- ).toEqual(sampleCommandDescription);
- });
+ it('should return constructed placeholder element for system note based on form contents', () => {
+ const $tempNote = this.notes.createPlaceholderSystemNote({
+ formContent: sampleCommandDescription,
+ uniqueId,
+ });
+
+ expect($tempNote.prop('nodeName')).toEqual('LI');
+ expect($tempNote.attr('id')).toEqual(uniqueId);
+ expect($tempNote.hasClass('being-posted')).toBeTruthy();
+ expect($tempNote.hasClass('fade-in-half')).toBeTruthy();
+ expect(
+ $tempNote
+ .find('.timeline-content i')
+ .text()
+ .trim(),
+ ).toEqual(sampleCommandDescription);
});
+ });
- describe('appendFlash', () => {
- beforeEach(() => {
- this.notes = new Notes();
- });
+ describe('appendFlash', () => {
+ beforeEach(() => {
+ this.notes = new Notes();
+ });
- it('shows a flash message', () => {
- this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0));
+ it('shows a flash message', () => {
+ this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0));
- expect($('.flash-alert').is(':visible')).toBeTruthy();
- });
+ expect($('.flash-alert').is(':visible')).toBeTruthy();
});
+ });
- describe('clearFlash', () => {
- beforeEach(() => {
- $(document).off('ajax:success');
- this.notes = new Notes();
- });
+ describe('clearFlash', () => {
+ beforeEach(() => {
+ $(document).off('ajax:success');
+ this.notes = new Notes();
+ });
- it('hides visible flash message', () => {
- this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0));
+ it('hides visible flash message', () => {
+ this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0));
- this.notes.clearFlash();
+ this.notes.clearFlash();
- expect($('.flash-alert').is(':visible')).toBeFalsy();
- });
+ expect($('.flash-alert').is(':visible')).toBeFalsy();
});
});
-}.call(window));
+});
diff --git a/spec/javascripts/oauth_remember_me_spec.js b/spec/javascripts/oauth_remember_me_spec.js
index 8816fe6defb..2caa266b85f 100644
--- a/spec/javascripts/oauth_remember_me_spec.js
+++ b/spec/javascripts/oauth_remember_me_spec.js
@@ -13,8 +13,13 @@ describe('OAuthRememberMe', () => {
it('adds the "remember_me" query parameter to all OAuth login buttons', () => {
$('#oauth-container #remember_me').click();
- expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe('http://example.com/?remember_me=1');
- expect($('#oauth-container .oauth-login.github').attr('href')).toBe('http://example.com/?remember_me=1');
+ expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe(
+ 'http://example.com/?remember_me=1',
+ );
+
+ expect($('#oauth-container .oauth-login.github').attr('href')).toBe(
+ 'http://example.com/?remember_me=1',
+ );
});
it('removes the "remember_me" query parameter from all OAuth login buttons', () => {
diff --git a/spec/javascripts/pager_spec.js b/spec/javascripts/pager_spec.js
index 04f2e7ef4f9..93efc139254 100644
--- a/spec/javascripts/pager_spec.js
+++ b/spec/javascripts/pager_spec.js
@@ -30,6 +30,7 @@ describe('pager', () => {
const href = `${gl.TEST_HOST}/some_list.json`;
setFixtures(`<div class="content_list" data-href="${href}"></div>`);
Pager.init();
+
expect(Pager.url).toBe(href);
});
@@ -37,12 +38,14 @@ describe('pager', () => {
const href = `${gl.TEST_HOST}/some_list`;
spyOnDependency(Pager, 'removeParams').and.returnValue(href);
Pager.init();
+
expect(Pager.url).toBe(href);
});
it('should get initial offset from query parameter', () => {
window.history.replaceState({}, null, '?offset=100');
Pager.init();
+
expect(Pager.offset).toBe(100);
});
@@ -51,6 +54,7 @@ describe('pager', () => {
const href = `${gl.TEST_HOST}/some_list?filter=test`;
const removeParams = spyOnDependency(Pager, 'removeParams').and.returnValue(href);
Pager.init();
+
expect(removeParams).toHaveBeenCalledWith(['limit', 'offset']);
expect(Pager.url).toEqual(href);
});
@@ -132,6 +136,7 @@ describe('pager', () => {
offset: 100,
},
});
+
expect(url).toBe('/some_list');
done();
diff --git a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js
index b0dc6ccc3d4..23d07056925 100644
--- a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js
+++ b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js
@@ -8,14 +8,15 @@ describe('Abuse Reports', () => {
let $messages;
- const assertMaxLength = $message => expect($message.text().length).toEqual(MAX_MESSAGE_LENGTH);
- const findMessage = searchText => $messages.filter(
- (index, element) => element.innerText.indexOf(searchText) > -1,
- ).first();
+ const assertMaxLength = $message => {
+ expect($message.text().length).toEqual(MAX_MESSAGE_LENGTH);
+ };
+ const findMessage = searchText =>
+ $messages.filter((index, element) => element.innerText.indexOf(searchText) > -1).first();
preloadFixtures(FIXTURE);
- beforeEach(function () {
+ beforeEach(function() {
loadFixtures(FIXTURE);
this.abuseReports = new AbuseReports();
$messages = $('.abuse-reports .message');
@@ -23,18 +24,21 @@ describe('Abuse Reports', () => {
it('should truncate long messages', () => {
const $longMessage = findMessage('LONG MESSAGE');
+
expect($longMessage.data('originalMessage')).toEqual(jasmine.anything());
assertMaxLength($longMessage);
});
it('should not truncate short messages', () => {
const $shortMessage = findMessage('SHORT MESSAGE');
+
expect($shortMessage.data('originalMessage')).not.toEqual(jasmine.anything());
});
it('should allow clicking a truncated message to expand and collapse the full message', () => {
const $longMessage = findMessage('LONG MESSAGE');
$longMessage.click();
+
expect($longMessage.data('originalMessage').length).toEqual($longMessage.text().length);
$longMessage.click();
assertMaxLength($longMessage);
diff --git a/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js b/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js
new file mode 100644
index 00000000000..561bd2c96cb
--- /dev/null
+++ b/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js
@@ -0,0 +1,36 @@
+import $ from 'jquery';
+import initUserInternalRegexPlaceholder, {
+ PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE,
+ PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE,
+} from '~/pages/admin/application_settings/account_and_limits';
+
+describe('AccountAndLimits', () => {
+ const FIXTURE = 'application_settings/accounts_and_limit.html.raw';
+ let $userDefaultExternal;
+ let $userInternalRegex;
+ preloadFixtures(FIXTURE);
+
+ beforeEach(() => {
+ loadFixtures(FIXTURE);
+ initUserInternalRegexPlaceholder();
+ $userDefaultExternal = $('#application_setting_user_default_external');
+ $userInternalRegex = document.querySelector('#application_setting_user_default_internal_regex');
+ });
+
+ describe('Changing of userInternalRegex when userDefaultExternal', () => {
+ it('is unchecked', () => {
+ expect($userDefaultExternal.prop('checked')).toBeFalsy();
+ expect($userInternalRegex.placeholder).toEqual(PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE);
+ expect($userInternalRegex.readOnly).toBeTruthy();
+ });
+
+ it('is checked', done => {
+ if (!$userDefaultExternal.prop('checked')) $userDefaultExternal.click();
+
+ expect($userDefaultExternal.prop('checked')).toBeTruthy();
+ expect($userInternalRegex.placeholder).toEqual(PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE);
+ expect($userInternalRegex.readOnly).toBeFalsy();
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
index b69e5f9a3a0..6bfb3f5ca21 100644
--- a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
+++ b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
@@ -21,10 +21,10 @@ describe('stop_jobs_modal.vue', () => {
});
describe('onSubmit', () => {
- it('stops jobs and redirects to overview page', (done) => {
+ it('stops jobs and redirects to overview page', done => {
const responseURL = `${gl.TEST_HOST}/stop_jobs_modal.vue/jobs`;
const redirectSpy = spyOnDependency(stopJobsModal, 'redirectTo');
- spyOn(axios, 'post').and.callFake((url) => {
+ spyOn(axios, 'post').and.callFake(url => {
expect(url).toBe(props.url);
return Promise.resolve({
request: {
@@ -34,24 +34,24 @@ describe('stop_jobs_modal.vue', () => {
});
vm.onSubmit()
- .then(() => {
- expect(redirectSpy).toHaveBeenCalledWith(responseURL);
- })
- .then(done)
- .catch(done.fail);
+ .then(() => {
+ expect(redirectSpy).toHaveBeenCalledWith(responseURL);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('displays error if stopping jobs failed', (done) => {
+ it('displays error if stopping jobs failed', done => {
const dummyError = new Error('stopping jobs failed');
const redirectSpy = spyOnDependency(stopJobsModal, 'redirectTo');
- spyOn(axios, 'post').and.callFake((url) => {
+ spyOn(axios, 'post').and.callFake(url => {
expect(url).toBe(props.url);
return Promise.reject(dummyError);
});
vm.onSubmit()
.then(done.fail)
- .catch((error) => {
+ .catch(error => {
expect(error).toBe(dummyError);
expect(redirectSpy).not.toHaveBeenCalled();
})
diff --git a/spec/javascripts/pages/admin/users/new/index_spec.js b/spec/javascripts/pages/admin/users/new/index_spec.js
new file mode 100644
index 00000000000..5a849f34bc3
--- /dev/null
+++ b/spec/javascripts/pages/admin/users/new/index_spec.js
@@ -0,0 +1,43 @@
+import $ from 'jquery';
+import UserInternalRegexHandler from '~/pages/admin/users/new/index';
+
+describe('UserInternalRegexHandler', () => {
+ const FIXTURE = 'admin/users/new_with_internal_user_regex.html.raw';
+ let $userExternal;
+ let $userEmail;
+ let $warningMessage;
+
+ preloadFixtures(FIXTURE);
+
+ beforeEach(() => {
+ loadFixtures(FIXTURE);
+ // eslint-disable-next-line no-new
+ new UserInternalRegexHandler();
+ $userExternal = $('#user_external');
+ $userEmail = $('#user_email');
+ $warningMessage = $('#warning_external_automatically_set');
+ if (!$userExternal.prop('checked')) $userExternal.prop('checked', 'checked');
+ });
+
+ describe('Behaviour of userExternal checkbox when', () => {
+ it('matches email as internal', done => {
+ expect($warningMessage.hasClass('hidden')).toBeTruthy();
+
+ $userEmail.val('test@').trigger('input');
+
+ expect($userExternal.prop('checked')).toBeFalsy();
+ expect($warningMessage.hasClass('hidden')).toBeFalsy();
+ done();
+ });
+
+ it('matches email as external', done => {
+ expect($warningMessage.hasClass('hidden')).toBeTruthy();
+
+ $userEmail.val('test.ext@').trigger('input');
+
+ expect($userExternal.prop('checked')).toBeTruthy();
+ expect($warningMessage.hasClass('hidden')).toBeTruthy();
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/pages/labels/components/promote_label_modal_spec.js b/spec/javascripts/pages/labels/components/promote_label_modal_spec.js
index a24f8204fe1..08a8362797b 100644
--- a/spec/javascripts/pages/labels/components/promote_label_modal_spec.js
+++ b/spec/javascripts/pages/labels/components/promote_label_modal_spec.js
@@ -25,7 +25,11 @@ describe('Promote label modal', () => {
});
it('contains the proper description', () => {
- expect(vm.text).toContain(`Promoting ${labelMockData.labelTitle} will make it available for all projects inside ${labelMockData.groupName}`);
+ expect(vm.text).toContain(
+ `Promoting ${labelMockData.labelTitle} will make it available for all projects inside ${
+ labelMockData.groupName
+ }`,
+ );
});
it('contains a label span with the color', () => {
@@ -48,11 +52,14 @@ describe('Promote label modal', () => {
vm.$destroy();
});
- it('redirects when a label is promoted', (done) => {
+ it('redirects when a label is promoted', done => {
const responseURL = `${gl.TEST_HOST}/dummy/endpoint`;
- spyOn(axios, 'post').and.callFake((url) => {
+ spyOn(axios, 'post').and.callFake(url => {
expect(url).toBe(labelMockData.url);
- expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestStarted', labelMockData.url);
+ expect(eventHub.$emit).toHaveBeenCalledWith(
+ 'promoteLabelModal.requestStarted',
+ labelMockData.url,
+ );
return Promise.resolve({
request: {
responseURL,
@@ -62,25 +69,34 @@ describe('Promote label modal', () => {
vm.onSubmit()
.then(() => {
- expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', { labelUrl: labelMockData.url, successful: true });
+ expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', {
+ labelUrl: labelMockData.url,
+ successful: true,
+ });
})
.then(done)
.catch(done.fail);
});
- it('displays an error if promoting a label failed', (done) => {
+ it('displays an error if promoting a label failed', done => {
const dummyError = new Error('promoting label failed');
dummyError.response = { status: 500 };
- spyOn(axios, 'post').and.callFake((url) => {
+ spyOn(axios, 'post').and.callFake(url => {
expect(url).toBe(labelMockData.url);
- expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestStarted', labelMockData.url);
+ expect(eventHub.$emit).toHaveBeenCalledWith(
+ 'promoteLabelModal.requestStarted',
+ labelMockData.url,
+ );
return Promise.reject(dummyError);
});
vm.onSubmit()
- .catch((error) => {
+ .catch(error => {
expect(error).toBe(dummyError);
- expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', { labelUrl: labelMockData.url, successful: false });
+ expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', {
+ labelUrl: labelMockData.url,
+ successful: false,
+ });
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
index 94401beb5c9..fe293083e4c 100644
--- a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
+++ b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
@@ -27,11 +27,14 @@ describe('delete_milestone_modal.vue', () => {
spyOn(eventHub, '$emit');
});
- it('deletes milestone and redirects to overview page', (done) => {
+ it('deletes milestone and redirects to overview page', done => {
const responseURL = `${gl.TEST_HOST}/delete_milestone_modal.vue/milestoneOverview`;
- spyOn(axios, 'delete').and.callFake((url) => {
+ spyOn(axios, 'delete').and.callFake(url => {
expect(url).toBe(props.milestoneUrl);
- expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestStarted', props.milestoneUrl);
+ expect(eventHub.$emit).toHaveBeenCalledWith(
+ 'deleteMilestoneModal.requestStarted',
+ props.milestoneUrl,
+ );
eventHub.$emit.calls.reset();
return Promise.resolve({
request: {
@@ -42,30 +45,39 @@ describe('delete_milestone_modal.vue', () => {
const redirectSpy = spyOnDependency(deleteMilestoneModal, 'redirectTo');
vm.onSubmit()
- .then(() => {
- expect(redirectSpy).toHaveBeenCalledWith(responseURL);
- expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { milestoneUrl: props.milestoneUrl, successful: true });
- })
- .then(done)
- .catch(done.fail);
+ .then(() => {
+ expect(redirectSpy).toHaveBeenCalledWith(responseURL);
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', {
+ milestoneUrl: props.milestoneUrl,
+ successful: true,
+ });
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('displays error if deleting milestone failed', (done) => {
+ it('displays error if deleting milestone failed', done => {
const dummyError = new Error('deleting milestone failed');
dummyError.response = { status: 418 };
- spyOn(axios, 'delete').and.callFake((url) => {
+ spyOn(axios, 'delete').and.callFake(url => {
expect(url).toBe(props.milestoneUrl);
- expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestStarted', props.milestoneUrl);
+ expect(eventHub.$emit).toHaveBeenCalledWith(
+ 'deleteMilestoneModal.requestStarted',
+ props.milestoneUrl,
+ );
eventHub.$emit.calls.reset();
return Promise.reject(dummyError);
});
const redirectSpy = spyOnDependency(deleteMilestoneModal, 'redirectTo');
vm.onSubmit()
- .catch((error) => {
+ .catch(error => {
expect(error).toBe(dummyError);
expect(redirectSpy).not.toHaveBeenCalled();
- expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { milestoneUrl: props.milestoneUrl, successful: false });
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', {
+ milestoneUrl: props.milestoneUrl,
+ successful: false,
+ });
})
.then(done)
.catch(done.fail);
@@ -81,7 +93,8 @@ describe('delete_milestone_modal.vue', () => {
});
it('contains neither issue nor milestone count', () => {
- vm = mountComponent(Component, { ...props,
+ vm = mountComponent(Component, {
+ ...props,
issueCount: 0,
mergeRequestCount: 0,
});
diff --git a/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js
index 8b220423637..2ac73ef3024 100644
--- a/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js
+++ b/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js
@@ -23,7 +23,11 @@ describe('Promote milestone modal', () => {
});
it('contains the proper description', () => {
- expect(vm.text).toContain(`Promoting ${milestoneMockData.milestoneTitle} will make it available for all projects inside ${milestoneMockData.groupName}.`);
+ expect(vm.text).toContain(
+ `Promoting ${
+ milestoneMockData.milestoneTitle
+ } will make it available for all projects inside ${milestoneMockData.groupName}.`,
+ );
});
it('contains the correct title', () => {
@@ -43,11 +47,14 @@ describe('Promote milestone modal', () => {
vm.$destroy();
});
- it('redirects when a milestone is promoted', (done) => {
+ it('redirects when a milestone is promoted', done => {
const responseURL = `${gl.TEST_HOST}/dummy/endpoint`;
- spyOn(axios, 'post').and.callFake((url) => {
+ spyOn(axios, 'post').and.callFake(url => {
expect(url).toBe(milestoneMockData.url);
- expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestStarted', milestoneMockData.url);
+ expect(eventHub.$emit).toHaveBeenCalledWith(
+ 'promoteMilestoneModal.requestStarted',
+ milestoneMockData.url,
+ );
return Promise.resolve({
request: {
responseURL,
@@ -57,25 +64,34 @@ describe('Promote milestone modal', () => {
vm.onSubmit()
.then(() => {
- expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', { milestoneUrl: milestoneMockData.url, successful: true });
+ expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', {
+ milestoneUrl: milestoneMockData.url,
+ successful: true,
+ });
})
.then(done)
.catch(done.fail);
});
- it('displays an error if promoting a milestone failed', (done) => {
+ it('displays an error if promoting a milestone failed', done => {
const dummyError = new Error('promoting milestone failed');
dummyError.response = { status: 500 };
- spyOn(axios, 'post').and.callFake((url) => {
+ spyOn(axios, 'post').and.callFake(url => {
expect(url).toBe(milestoneMockData.url);
- expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestStarted', milestoneMockData.url);
+ expect(eventHub.$emit).toHaveBeenCalledWith(
+ 'promoteMilestoneModal.requestStarted',
+ milestoneMockData.url,
+ );
return Promise.reject(dummyError);
});
vm.onSubmit()
- .catch((error) => {
+ .catch(error => {
expect(error).toBe(dummyError);
- expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', { milestoneUrl: milestoneMockData.url, successful: false });
+ expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', {
+ milestoneUrl: milestoneMockData.url,
+ successful: false,
+ });
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/pages/profiles/show/emoji_menu_spec.js b/spec/javascripts/pages/profiles/show/emoji_menu_spec.js
index b70368fc92f..864bda65736 100644
--- a/spec/javascripts/pages/profiles/show/emoji_menu_spec.js
+++ b/spec/javascripts/pages/profiles/show/emoji_menu_spec.js
@@ -81,6 +81,7 @@ describe('EmojiMenu', () => {
'mouseenter focus',
jasmine.anything(),
);
+
expect(emojiMenu.registerEventListener).toHaveBeenCalledWith(
'on',
jasmine.anything(),
@@ -107,6 +108,7 @@ describe('EmojiMenu', () => {
it('renders the menu with custom menu class', () => {
const menuElement = () =>
document.body.querySelector(`.emoji-menu.${dummyMenuClass} .emoji-menu-content`);
+
expect(menuElement()).toBe(null);
emojiMenu.createEmojiMenu();
diff --git a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
index 4655e29eed0..b20bc96f9be 100644
--- a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
+++ b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
@@ -19,10 +19,10 @@ window.gl.pipelineScheduleFieldErrors = {
updateFormValidityState: () => {},
};
-describe('Interval Pattern Input Component', function () {
- describe('when prop initialCronInterval is passed (edit)', function () {
- describe('when prop initialCronInterval is custom', function () {
- beforeEach(function () {
+describe('Interval Pattern Input Component', function() {
+ describe('when prop initialCronInterval is passed (edit)', function() {
+ describe('when prop initialCronInterval is custom', function() {
+ beforeEach(function() {
this.initialCronInterval = '1 2 3 4 5';
this.intervalPatternComponent = new IntervalPatternInputComponent({
propsData: {
@@ -31,15 +31,15 @@ describe('Interval Pattern Input Component', function () {
}).$mount();
});
- it('is initialized as a Vue component', function () {
+ it('is initialized as a Vue component', function() {
expect(this.intervalPatternComponent).toBeDefined();
});
- it('prop initialCronInterval is set', function () {
+ it('prop initialCronInterval is set', function() {
expect(this.intervalPatternComponent.initialCronInterval).toBe(this.initialCronInterval);
});
- it('sets isEditable to true', function (done) {
+ it('sets isEditable to true', function(done) {
Vue.nextTick(() => {
expect(this.intervalPatternComponent.isEditable).toBe(true);
done();
@@ -47,8 +47,8 @@ describe('Interval Pattern Input Component', function () {
});
});
- describe('when prop initialCronInterval is preset', function () {
- beforeEach(function () {
+ describe('when prop initialCronInterval is preset', function() {
+ beforeEach(function() {
this.intervalPatternComponent = new IntervalPatternInputComponent({
propsData: {
inputNameAttribute,
@@ -57,11 +57,11 @@ describe('Interval Pattern Input Component', function () {
}).$mount();
});
- it('is initialized as a Vue component', function () {
+ it('is initialized as a Vue component', function() {
expect(this.intervalPatternComponent).toBeDefined();
});
- it('sets isEditable to false', function (done) {
+ it('sets isEditable to false', function(done) {
Vue.nextTick(() => {
expect(this.intervalPatternComponent.isEditable).toBe(false);
done();
@@ -70,8 +70,8 @@ describe('Interval Pattern Input Component', function () {
});
});
- describe('when prop initialCronInterval is not passed (new)', function () {
- beforeEach(function () {
+ describe('when prop initialCronInterval is not passed (new)', function() {
+ beforeEach(function() {
this.intervalPatternComponent = new IntervalPatternInputComponent({
propsData: {
inputNameAttribute,
@@ -79,16 +79,17 @@ describe('Interval Pattern Input Component', function () {
}).$mount();
});
- it('is initialized as a Vue component', function () {
+ it('is initialized as a Vue component', function() {
expect(this.intervalPatternComponent).toBeDefined();
});
- it('prop initialCronInterval is set', function () {
+ it('prop initialCronInterval is set', function() {
const defaultInitialCronInterval = '';
+
expect(this.intervalPatternComponent.initialCronInterval).toBe(defaultInitialCronInterval);
});
- it('sets isEditable to true', function (done) {
+ it('sets isEditable to true', function(done) {
Vue.nextTick(() => {
expect(this.intervalPatternComponent.isEditable).toBe(true);
done();
@@ -96,8 +97,8 @@ describe('Interval Pattern Input Component', function () {
});
});
- describe('User Actions', function () {
- beforeEach(function () {
+ describe('User Actions', function() {
+ beforeEach(function() {
// For an unknown reason, some browsers do not propagate click events
// on radio buttons in a way Vue can register. So, we have to mount
// to a fixture.
@@ -111,66 +112,79 @@ describe('Interval Pattern Input Component', function () {
}).$mount('#my-mount');
});
- it('cronInterval is updated when everyday preset interval is selected', function (done) {
+ it('cronInterval is updated when everyday preset interval is selected', function(done) {
this.intervalPatternComponent.$el.querySelector('#every-day').click();
Vue.nextTick(() => {
expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyDay);
- expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(cronIntervalPresets.everyDay);
+ expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(
+ cronIntervalPresets.everyDay,
+ );
done();
});
});
- it('cronInterval is updated when everyweek preset interval is selected', function (done) {
+ it('cronInterval is updated when everyweek preset interval is selected', function(done) {
this.intervalPatternComponent.$el.querySelector('#every-week').click();
Vue.nextTick(() => {
expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyWeek);
- expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(cronIntervalPresets.everyWeek);
+ expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(
+ cronIntervalPresets.everyWeek,
+ );
done();
});
});
- it('cronInterval is updated when everymonth preset interval is selected', function (done) {
+ it('cronInterval is updated when everymonth preset interval is selected', function(done) {
this.intervalPatternComponent.$el.querySelector('#every-month').click();
Vue.nextTick(() => {
expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyMonth);
- expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(cronIntervalPresets.everyMonth);
+ expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(
+ cronIntervalPresets.everyMonth,
+ );
done();
});
});
- it('only a space is added to cronInterval (trimmed later) when custom radio is selected', function (done) {
+ it('only a space is added to cronInterval (trimmed later) when custom radio is selected', function(done) {
this.intervalPatternComponent.$el.querySelector('#every-month').click();
this.intervalPatternComponent.$el.querySelector('#custom').click();
Vue.nextTick(() => {
const intervalWithSpaceAppended = `${cronIntervalPresets.everyMonth} `;
+
expect(this.intervalPatternComponent.cronInterval).toBe(intervalWithSpaceAppended);
- expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(intervalWithSpaceAppended);
+ expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(
+ intervalWithSpaceAppended,
+ );
done();
});
});
- it('text input is disabled when preset interval is selected', function (done) {
+ it('text input is disabled when preset interval is selected', function(done) {
this.intervalPatternComponent.$el.querySelector('#every-month').click();
Vue.nextTick(() => {
expect(this.intervalPatternComponent.isEditable).toBe(false);
- expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled).toBe(true);
+ expect(
+ this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled,
+ ).toBe(true);
done();
});
});
- it('text input is enabled when custom is selected', function (done) {
+ it('text input is enabled when custom is selected', function(done) {
this.intervalPatternComponent.$el.querySelector('#every-month').click();
this.intervalPatternComponent.$el.querySelector('#custom').click();
Vue.nextTick(() => {
expect(this.intervalPatternComponent.isEditable).toBe(true);
- expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled).toBe(false);
+ expect(
+ this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled,
+ ).toBe(false);
done();
});
});
diff --git a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
index fb7d2763b49..ea809e1f170 100644
--- a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
+++ b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
@@ -6,7 +6,7 @@ const PipelineSchedulesCalloutComponent = Vue.extend(PipelineSchedulesCallout);
const cookieKey = 'pipeline_schedules_callout_dismissed';
const docsUrl = 'help/ci/scheduled_pipelines';
-describe('Pipeline Schedule Callout', function () {
+describe('Pipeline Schedule Callout', function() {
beforeEach(() => {
setFixtures(`
<div id='pipeline-schedules-callout' data-docs-url=${docsUrl}></div>
@@ -76,7 +76,7 @@ describe('Pipeline Schedule Callout', function () {
expect(this.calloutComponent.$el.outerHTML).toContain(docsUrl);
});
- it('updates calloutDismissed when close button is clicked', (done) => {
+ it('updates calloutDismissed when close button is clicked', done => {
this.calloutComponent.$el.querySelector('#dismiss-callout-btn').click();
Vue.nextTick(() => {
@@ -85,7 +85,7 @@ describe('Pipeline Schedule Callout', function () {
});
});
- it('#dismissCallout updates calloutDismissed', (done) => {
+ it('#dismissCallout updates calloutDismissed', done => {
this.calloutComponent.dismissCallout();
Vue.nextTick(() => {
@@ -94,7 +94,7 @@ describe('Pipeline Schedule Callout', function () {
});
});
- it('is hidden when close button is clicked', (done) => {
+ it('is hidden when close button is clicked', done => {
this.calloutComponent.$el.querySelector('#dismiss-callout-btn').click();
Vue.nextTick(() => {
diff --git a/spec/javascripts/pdf/index_spec.js b/spec/javascripts/pdf/index_spec.js
index 69230bb0937..699cf4871aa 100644
--- a/spec/javascripts/pdf/index_spec.js
+++ b/spec/javascripts/pdf/index_spec.js
@@ -11,7 +11,7 @@ const Component = Vue.extend(PDFLab);
describe('PDF component', () => {
let vm;
- const checkLoaded = (done) => {
+ const checkLoaded = done => {
if (vm.loading) {
setTimeout(() => {
checkLoaded(done);
@@ -22,7 +22,7 @@ describe('PDF component', () => {
};
describe('without PDF data', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
pdf: '',
@@ -40,7 +40,7 @@ describe('PDF component', () => {
});
describe('with PDF data', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Component({
propsData: {
pdf,
diff --git a/spec/javascripts/pdf/page_spec.js b/spec/javascripts/pdf/page_spec.js
index ff1bfd7f650..ef967210b65 100644
--- a/spec/javascripts/pdf/page_spec.js
+++ b/spec/javascripts/pdf/page_spec.js
@@ -3,53 +3,46 @@ import pdfjsLib from 'vendor/pdf';
import workerSrc from 'vendor/pdf.worker.min';
import PageComponent from '~/pdf/page/index.vue';
-import testPDF from '../fixtures/blob/pdf/test.pdf';
-
-const Component = Vue.extend(PageComponent);
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import testPDF from 'spec/fixtures/blob/pdf/test.pdf';
describe('Page component', () => {
+ const Component = Vue.extend(PageComponent);
let vm;
let testPage;
- pdfjsLib.PDFJS.workerSrc = workerSrc;
-
- const checkRendered = (done) => {
- if (vm.rendering) {
- setTimeout(() => {
- checkRendered(done);
- }, 100);
- } else {
- done();
- }
- };
- beforeEach((done) => {
- pdfjsLib.getDocument(testPDF)
+ beforeEach(done => {
+ pdfjsLib.PDFJS.workerSrc = workerSrc;
+ pdfjsLib
+ .getDocument(testPDF)
.then(pdf => pdf.getPage(1))
- .then((page) => {
+ .then(page => {
testPage = page;
- done();
})
- .catch((error) => {
- done.fail(error);
- });
+ .then(done)
+ .catch(done.fail);
});
- describe('render', () => {
- beforeEach((done) => {
- vm = new Component({
- propsData: {
- page: testPage,
- number: 1,
- },
- });
-
- vm.$mount();
+ afterEach(() => {
+ vm.$destroy();
+ });
- checkRendered(done);
+ it('renders the page when mounting', done => {
+ const promise = Promise.resolve();
+ spyOn(testPage, 'render').and.callFake(() => promise);
+ vm = mountComponent(Component, {
+ page: testPage,
+ number: 1,
});
- it('renders first page', () => {
- expect(vm.$el.tagName).toBeDefined();
- });
+ expect(vm.rendering).toBe(true);
+
+ promise
+ .then(() => {
+ expect(testPage.render).toHaveBeenCalledWith(vm.renderContext);
+ expect(vm.rendering).toBe(false);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/performance_bar/components/detailed_metric_spec.js b/spec/javascripts/performance_bar/components/detailed_metric_spec.js
index c4611dc7662..a3b93280b4b 100644
--- a/spec/javascripts/performance_bar/components/detailed_metric_spec.js
+++ b/spec/javascripts/performance_bar/components/detailed_metric_spec.js
@@ -54,11 +54,9 @@ describe('detailedMetric', () => {
});
it('adds a modal with a table of the details', () => {
- vm.$el
- .querySelectorAll('.performance-bar-modal td strong')
- .forEach((duration, index) => {
- expect(duration.innerText).toContain(requestDetails[index].duration);
- });
+ vm.$el.querySelectorAll('.performance-bar-modal td strong').forEach((duration, index) => {
+ expect(duration.innerText).toContain(requestDetails[index].duration);
+ });
vm.$el
.querySelectorAll('.performance-bar-modal td:nth-child(2)')
diff --git a/spec/javascripts/performance_bar/components/request_selector_spec.js b/spec/javascripts/performance_bar/components/request_selector_spec.js
index 6108a29f8c4..a272e03c0e1 100644
--- a/spec/javascripts/performance_bar/components/request_selector_spec.js
+++ b/spec/javascripts/performance_bar/components/request_selector_spec.js
@@ -11,8 +11,7 @@ describe('request selector', () => {
},
{
id: '789',
- url:
- 'https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1.json?serializer=widget',
+ url: 'https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1.json?serializer=widget',
},
];
diff --git a/spec/javascripts/performance_bar/index_spec.js b/spec/javascripts/performance_bar/index_spec.js
index 1784bd64adb..1444d1bb3cb 100644
--- a/spec/javascripts/performance_bar/index_spec.js
+++ b/spec/javascripts/performance_bar/index_spec.js
@@ -63,10 +63,7 @@ describe('performance bar wrapper', () => {
it('adds the request immediately', () => {
vm.loadRequestDetails('123', 'https://gitlab.com/');
- expect(vm.store.addRequest).toHaveBeenCalledWith(
- '123',
- 'https://gitlab.com/',
- );
+ expect(vm.store.addRequest).toHaveBeenCalledWith('123', 'https://gitlab.com/');
});
it('makes an HTTP request for the request details', () => {
diff --git a/spec/javascripts/performance_bar/services/performance_bar_service_spec.js b/spec/javascripts/performance_bar/services/performance_bar_service_spec.js
new file mode 100644
index 00000000000..cfec4b779e4
--- /dev/null
+++ b/spec/javascripts/performance_bar/services/performance_bar_service_spec.js
@@ -0,0 +1,68 @@
+import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
+
+describe('PerformanceBarService', () => {
+ describe('callbackParams', () => {
+ describe('fireCallback', () => {
+ function fireCallback(response, peekUrl) {
+ return PerformanceBarService.callbackParams(response, peekUrl)[0];
+ }
+
+ it('returns false when the request URL is the peek URL', () => {
+ expect(
+ fireCallback({ headers: { 'x-request-id': '123' }, url: '/peek' }, '/peek'),
+ ).toBeFalsy();
+ });
+
+ it('returns false when there is no request ID', () => {
+ expect(fireCallback({ headers: {}, url: '/request' }, '/peek')).toBeFalsy();
+ });
+
+ it('returns false when the request is an API request', () => {
+ expect(
+ fireCallback({ headers: { 'x-request-id': '123' }, url: '/api/' }, '/peek'),
+ ).toBeFalsy();
+ });
+
+ it('returns false when the response is from the cache', () => {
+ expect(
+ fireCallback(
+ { headers: { 'x-request-id': '123', 'x-gitlab-from-cache': 'true' }, url: '/request' },
+ '/peek',
+ ),
+ ).toBeFalsy();
+ });
+
+ it('returns true when all conditions are met', () => {
+ expect(
+ fireCallback({ headers: { 'x-request-id': '123' }, url: '/request' }, '/peek'),
+ ).toBeTruthy();
+ });
+ });
+
+ describe('requestId', () => {
+ function requestId(response, peekUrl) {
+ return PerformanceBarService.callbackParams(response, peekUrl)[1];
+ }
+
+ it('gets the request ID from the headers', () => {
+ expect(requestId({ headers: { 'x-request-id': '123' } }, '/peek')).toEqual('123');
+ });
+ });
+
+ describe('requestUrl', () => {
+ function requestUrl(response, peekUrl) {
+ return PerformanceBarService.callbackParams(response, peekUrl)[2];
+ }
+
+ it('gets the request URL from the response object', () => {
+ expect(requestUrl({ headers: {}, url: '/request' }, '/peek')).toEqual('/request');
+ });
+
+ it('gets the request URL from response.config if present', () => {
+ expect(
+ requestUrl({ headers: {}, config: { url: '/config-url' }, url: '/request' }, '/peek'),
+ ).toEqual('/config-url');
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/blank_state_spec.js b/spec/javascripts/pipelines/blank_state_spec.js
index b7a9b60d85c..033bd5ccb73 100644
--- a/spec/javascripts/pipelines/blank_state_spec.js
+++ b/spec/javascripts/pipelines/blank_state_spec.js
@@ -9,12 +9,10 @@ describe('Pipelines Blank State', () => {
beforeEach(() => {
Component = Vue.extend(component);
- vm = mountComponent(Component,
- {
- svgPath: 'foo',
- message: 'Blank State',
- },
- );
+ vm = mountComponent(Component, {
+ svgPath: 'foo',
+ message: 'Blank State',
+ });
});
it('should render svg', () => {
@@ -22,8 +20,6 @@ describe('Pipelines Blank State', () => {
});
it('should render message', () => {
- expect(
- vm.$el.querySelector('h4').textContent.trim(),
- ).toEqual('Blank State');
+ expect(vm.$el.querySelector('h4').textContent.trim()).toEqual('Blank State');
});
});
diff --git a/spec/javascripts/pipelines/empty_state_spec.js b/spec/javascripts/pipelines/empty_state_spec.js
index 1e41a7bfe7c..f12950b8fce 100644
--- a/spec/javascripts/pipelines/empty_state_spec.js
+++ b/spec/javascripts/pipelines/empty_state_spec.js
@@ -24,20 +24,35 @@ describe('Pipelines Empty State', () => {
expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
});
- it('should render emtpy state information', () => {
+ it('should render empty state information', () => {
expect(component.$el.querySelector('h4').textContent).toContain('Build with confidence');
expect(
- component.$el.querySelector('p').innerHTML.trim().replace(/\n+\s+/m, ' ').replace(/\s\s+/g, ' '),
+ component.$el
+ .querySelector('p')
+ .innerHTML.trim()
+ .replace(/\n+\s+/m, ' ')
+ .replace(/\s\s+/g, ' '),
).toContain('Continuous Integration can help catch bugs by running your tests automatically,');
expect(
- component.$el.querySelector('p').innerHTML.trim().replace(/\n+\s+/m, ' ').replace(/\s\s+/g, ' '),
- ).toContain('while Continuous Deployment can help you deliver code to your product environment');
+ component.$el
+ .querySelector('p')
+ .innerHTML.trim()
+ .replace(/\n+\s+/m, ' ')
+ .replace(/\s\s+/g, ' '),
+ ).toContain(
+ 'while Continuous Deployment can help you deliver code to your product environment',
+ );
});
it('should render a link with provided help path', () => {
- expect(component.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual('foo');
- expect(component.$el.querySelector('.js-get-started-pipelines').textContent).toContain('Get started with Pipelines');
+ expect(component.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual(
+ 'foo',
+ );
+
+ expect(component.$el.querySelector('.js-get-started-pipelines').textContent).toContain(
+ 'Get started with Pipelines',
+ );
});
});
diff --git a/spec/javascripts/pipelines/graph/action_component_spec.js b/spec/javascripts/pipelines/graph/action_component_spec.js
index 568e679abe9..3d2232ff239 100644
--- a/spec/javascripts/pipelines/graph/action_component_spec.js
+++ b/spec/javascripts/pipelines/graph/action_component_spec.js
@@ -35,12 +35,13 @@ describe('pipeline graph action component', () => {
it('should update bootstrap tooltip when title changes', done => {
component.tooltipText = 'changed';
- component.$nextTick()
- .then(() => {
- expect(component.$el.getAttribute('data-original-title')).toBe('changed');
- })
- .then(done)
- .catch(done.fail);
+ component
+ .$nextTick()
+ .then(() => {
+ expect(component.$el.getAttribute('data-original-title')).toBe('changed');
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should render an svg', () => {
@@ -49,12 +50,13 @@ describe('pipeline graph action component', () => {
});
describe('on click', () => {
- it('emits `pipelineActionRequestComplete` after a successfull request', done => {
+ it('emits `pipelineActionRequestComplete` after a successful request', done => {
spyOn(component, '$emit');
component.$el.click();
- component.$nextTick()
+ component
+ .$nextTick()
.then(() => {
expect(component.$emit).toHaveBeenCalledWith('pipelineActionRequestComplete');
})
diff --git a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js
deleted file mode 100644
index ff584396d61..00000000000
--- a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import Vue from 'vue';
-import component from '~/pipelines/components/graph/dropdown_job_component.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('dropdown job component', () => {
- const Component = Vue.extend(component);
- let vm;
-
- const mock = {
- jobs: [
- {
- id: 4256,
- name: '<img src=x onerror=alert(document.domain)>',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- tooltip: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4256',
- has_details: true,
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4256/retry',
- method: 'post',
- },
- },
- },
- {
- id: 4299,
- name: 'test',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- tooltip: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4299',
- has_details: true,
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4299/retry',
- method: 'post',
- },
- },
- },
- ],
- name: 'rspec:linux',
- size: 2,
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- tooltip: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4256',
- has_details: true,
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4256/retry',
- method: 'post',
- },
- },
- };
-
- afterEach(() => {
- vm.$destroy();
- });
-
- beforeEach(() => {
- vm = mountComponent(Component, { job: mock });
- });
-
- it('renders button with job name and size', () => {
- expect(vm.$el.querySelector('button').textContent).toContain(mock.name);
- expect(vm.$el.querySelector('button').textContent).toContain(mock.size);
- });
-
- it('renders dropdown with jobs', () => {
- expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length);
- });
-
- it('escapes tooltip title', () => {
- expect(
- vm.$el.querySelector('.js-pipeline-graph-job-link').getAttribute('data-original-title'),
- ).toEqual(
- '&lt;img src=x onerror=alert(document.domain)&gt; - passed',
- );
- });
-});
diff --git a/spec/javascripts/pipelines/graph/graph_component_spec.js b/spec/javascripts/pipelines/graph/graph_component_spec.js
index b6fa4272c8b..96a2d5f62fa 100644
--- a/spec/javascripts/pipelines/graph/graph_component_spec.js
+++ b/spec/javascripts/pipelines/graph/graph_component_spec.js
@@ -40,7 +40,9 @@ describe('graph component', () => {
).toEqual(true);
expect(
- component.$el.querySelector('.stage-column:nth-child(2) .build:nth-child(1)').classList.contains('left-connector'),
+ component.$el
+ .querySelector('.stage-column:nth-child(2) .build:nth-child(1)')
+ .classList.contains('left-connector'),
).toEqual(true);
expect(component.$el.querySelector('loading-icon')).toBe(null);
@@ -56,7 +58,9 @@ describe('graph component', () => {
pipeline: graphJSON,
});
- expect(component.$el.querySelector('.stage-column:nth-child(2) .stage-name').textContent.trim()).toEqual('Deploy &lt;img src=x onerror=alert(document.domain)&gt;');
+ expect(
+ component.$el.querySelector('.stage-column:nth-child(2) .stage-name').textContent.trim(),
+ ).toEqual('Deploy &lt;img src=x onerror=alert(document.domain)&gt;');
});
});
});
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
deleted file mode 100644
index 215ce1e81b5..00000000000
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ /dev/null
@@ -1,184 +0,0 @@
-import Vue from 'vue';
-import jobComponent from '~/pipelines/components/graph/job_component.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('pipeline graph job component', () => {
- const JobComponent = Vue.extend(jobComponent);
- let component;
-
- const mockJob = {
- id: 4256,
- name: 'test',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- tooltip: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4256',
- has_details: true,
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4256/retry',
- method: 'post',
- },
- },
- };
-
- afterEach(() => {
- component.$destroy();
- });
-
- describe('name with link', () => {
- it('should render the job name and status with a link', (done) => {
- component = mountComponent(JobComponent, { job: mockJob });
-
- Vue.nextTick(() => {
- const link = component.$el.querySelector('a');
-
- expect(link.getAttribute('href')).toEqual(mockJob.status.details_path);
-
- expect(
- link.getAttribute('data-original-title'),
- ).toEqual(`${mockJob.name} - ${mockJob.status.label}`);
-
- expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined();
-
- expect(
- component.$el.querySelector('.ci-status-text').textContent.trim(),
- ).toEqual(mockJob.name);
-
- done();
- });
- });
- });
-
- describe('name without link', () => {
- it('it should render status and name', () => {
- component = mountComponent(JobComponent, {
- job: {
- id: 4257,
- name: 'test',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4257',
- has_details: false,
- },
- },
- });
-
- expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined();
- expect(component.$el.querySelector('a')).toBeNull();
-
- expect(
- component.$el.querySelector('.ci-status-text').textContent.trim(),
- ).toEqual(mockJob.name);
- });
- });
-
- describe('action icon', () => {
- it('it should render the action icon', () => {
- component = mountComponent(JobComponent, { job: mockJob });
-
- expect(component.$el.querySelector('a.ci-action-icon-container')).toBeDefined();
- expect(component.$el.querySelector('i.ci-action-icon-wrapper')).toBeDefined();
- });
- });
-
- it('should render provided class name', () => {
- component = mountComponent(JobComponent, {
- job: mockJob,
- cssClassJobName: 'css-class-job-name',
- });
-
- expect(
- component.$el.querySelector('a').classList.contains('css-class-job-name'),
- ).toBe(true);
- });
-
- describe('status label', () => {
- it('should not render status label when it is not provided', () => {
- component = mountComponent(JobComponent, {
- job: {
- id: 4258,
- name: 'test',
- status: {
- icon: 'status_success',
- },
- },
- });
-
- expect(component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title')).toEqual('test');
- });
-
- it('should not render status label when it is provided', () => {
- component = mountComponent(JobComponent, {
- job: {
- id: 4259,
- name: 'test',
- status: {
- icon: 'status_success',
- label: 'success',
- tooltip: 'success',
- },
- },
- });
-
- expect(component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title')).toEqual('test - success');
- });
- });
-
- describe('tooltip placement', () => {
- const tooltipBoundary = 'a[data-boundary="viewport"]';
-
- it('does not set tooltip boundary by default', () => {
- component = mountComponent(JobComponent, {
- job: mockJob,
- });
-
- expect(component.$el.querySelector(tooltipBoundary)).toBeNull();
- });
-
- it('sets tooltip boundary to viewport for small dropdowns', () => {
- component = mountComponent(JobComponent, {
- job: mockJob,
- dropdownLength: 1,
- });
-
- expect(component.$el.querySelector(tooltipBoundary)).not.toBeNull();
- });
-
- it('does not set tooltip boundary for large lists', () => {
- component = mountComponent(JobComponent, {
- job: mockJob,
- dropdownLength: 7,
- });
-
- expect(component.$el.querySelector(tooltipBoundary)).toBeNull();
- });
- });
-
- describe('tooltipText', () => {
- it('escapes job name', () => {
- component = mountComponent(JobComponent, {
- job: {
- id: 4259,
- name: '<img src=x onerror=alert(document.domain)>',
- status: {
- icon: 'status_success',
- label: 'success',
- tooltip: 'failed',
- },
- },
- });
-
- expect(
- component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title'),
- ).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; - failed');
- });
- });
-});
diff --git a/spec/javascripts/pipelines/graph/job_group_dropdown_spec.js b/spec/javascripts/pipelines/graph/job_group_dropdown_spec.js
new file mode 100644
index 00000000000..24631cc1c89
--- /dev/null
+++ b/spec/javascripts/pipelines/graph/job_group_dropdown_spec.js
@@ -0,0 +1,85 @@
+import Vue from 'vue';
+import JobGroupDropdown from '~/pipelines/components/graph/job_group_dropdown.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('job group dropdown component', () => {
+ const Component = Vue.extend(JobGroupDropdown);
+ let vm;
+
+ const group = {
+ jobs: [
+ {
+ id: 4256,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4256',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4256/retry',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 4299,
+ name: 'test',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4299',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4299/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ name: 'rspec:linux',
+ size: 2,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4256',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4256/retry',
+ method: 'post',
+ },
+ },
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ beforeEach(() => {
+ vm = mountComponent(Component, { group });
+ });
+
+ it('renders button with group name and size', () => {
+ expect(vm.$el.querySelector('button').textContent).toContain(group.name);
+ expect(vm.$el.querySelector('button').textContent).toContain(group.size);
+ });
+
+ it('renders dropdown with jobs', () => {
+ expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(group.jobs.length);
+ });
+});
diff --git a/spec/javascripts/pipelines/graph/job_item_spec.js b/spec/javascripts/pipelines/graph/job_item_spec.js
new file mode 100644
index 00000000000..88e1789184d
--- /dev/null
+++ b/spec/javascripts/pipelines/graph/job_item_spec.js
@@ -0,0 +1,195 @@
+import Vue from 'vue';
+import JobItem from '~/pipelines/components/graph/job_item.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('pipeline graph job item', () => {
+ const JobComponent = Vue.extend(JobItem);
+ let component;
+
+ const delayedJobFixture = getJSONFixture('jobs/delayed.json');
+ const mockJob = {
+ id: 4256,
+ name: 'test',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4256',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4256/retry',
+ method: 'post',
+ },
+ },
+ };
+
+ afterEach(() => {
+ component.$destroy();
+ });
+
+ describe('name with link', () => {
+ it('should render the job name and status with a link', done => {
+ component = mountComponent(JobComponent, { job: mockJob });
+
+ Vue.nextTick(() => {
+ const link = component.$el.querySelector('a');
+
+ expect(link.getAttribute('href')).toEqual(mockJob.status.details_path);
+
+ expect(link.getAttribute('data-original-title')).toEqual(
+ `${mockJob.name} - ${mockJob.status.label}`,
+ );
+
+ expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined();
+
+ expect(component.$el.querySelector('.ci-status-text').textContent.trim()).toEqual(
+ mockJob.name,
+ );
+
+ done();
+ });
+ });
+ });
+
+ describe('name without link', () => {
+ it('it should render status and name', () => {
+ component = mountComponent(JobComponent, {
+ job: {
+ id: 4257,
+ name: 'test',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4257',
+ has_details: false,
+ },
+ },
+ });
+
+ expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined();
+ expect(component.$el.querySelector('a')).toBeNull();
+
+ expect(component.$el.querySelector('.ci-status-text').textContent.trim()).toEqual(
+ mockJob.name,
+ );
+ });
+ });
+
+ describe('action icon', () => {
+ it('it should render the action icon', () => {
+ component = mountComponent(JobComponent, { job: mockJob });
+
+ expect(component.$el.querySelector('a.ci-action-icon-container')).toBeDefined();
+ expect(component.$el.querySelector('i.ci-action-icon-wrapper')).toBeDefined();
+ });
+ });
+
+ it('should render provided class name', () => {
+ component = mountComponent(JobComponent, {
+ job: mockJob,
+ cssClassJobName: 'css-class-job-name',
+ });
+
+ expect(component.$el.querySelector('a').classList.contains('css-class-job-name')).toBe(true);
+ });
+
+ describe('status label', () => {
+ it('should not render status label when it is not provided', () => {
+ component = mountComponent(JobComponent, {
+ job: {
+ id: 4258,
+ name: 'test',
+ status: {
+ icon: 'status_success',
+ },
+ },
+ });
+
+ expect(
+ component.$el
+ .querySelector('.js-job-component-tooltip')
+ .getAttribute('data-original-title'),
+ ).toEqual('test');
+ });
+
+ it('should not render status label when it is provided', () => {
+ component = mountComponent(JobComponent, {
+ job: {
+ id: 4259,
+ name: 'test',
+ status: {
+ icon: 'status_success',
+ label: 'success',
+ tooltip: 'success',
+ },
+ },
+ });
+
+ expect(
+ component.$el
+ .querySelector('.js-job-component-tooltip')
+ .getAttribute('data-original-title'),
+ ).toEqual('test - success');
+ });
+ });
+
+ describe('tooltip placement', () => {
+ it('does not set tooltip boundary by default', () => {
+ component = mountComponent(JobComponent, {
+ job: mockJob,
+ });
+
+ expect(component.tooltipBoundary).toBeNull();
+ });
+
+ it('sets tooltip boundary to viewport for small dropdowns', () => {
+ component = mountComponent(JobComponent, {
+ job: mockJob,
+ dropdownLength: 1,
+ });
+
+ expect(component.tooltipBoundary).toEqual('viewport');
+ });
+
+ it('does not set tooltip boundary for large lists', () => {
+ component = mountComponent(JobComponent, {
+ job: mockJob,
+ dropdownLength: 7,
+ });
+
+ expect(component.tooltipBoundary).toBeNull();
+ });
+ });
+
+ describe('for delayed job', () => {
+ beforeEach(() => {
+ const fifteenMinutesInMilliseconds = 900000;
+ spyOn(Date, 'now').and.callFake(
+ () => new Date(delayedJobFixture.scheduled_at).getTime() - fifteenMinutesInMilliseconds,
+ );
+ });
+
+ it('displays remaining time in tooltip', done => {
+ component = mountComponent(JobComponent, {
+ job: delayedJobFixture,
+ });
+
+ Vue.nextTick()
+ .then(() => {
+ expect(
+ component.$el
+ .querySelector('.js-pipeline-graph-job-link')
+ .getAttribute('data-original-title'),
+ ).toEqual('delayed job - delayed manual action (00:15:00)');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
index f6e6bd3132e..d0b8f877d6f 100644
--- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js
+++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
@@ -25,17 +25,16 @@ describe('stage column component', () => {
};
beforeEach(() => {
-
- const mockJobs = [];
+ const mockGroups = [];
for (let i = 0; i < 3; i += 1) {
const mockedJob = Object.assign({}, mockJob);
mockedJob.id += i;
- mockJobs.push(mockedJob);
+ mockGroups.push(mockedJob);
}
component = mountComponent(StageColumnComponent, {
title: 'foo',
- jobs: mockJobs,
+ groups: mockGroups,
});
});
@@ -43,14 +42,14 @@ describe('stage column component', () => {
expect(component.$el.querySelector('.stage-name').textContent.trim()).toEqual('foo');
});
- it('should render the provided jobs', () => {
+ it('should render the provided groups', () => {
expect(component.$el.querySelectorAll('.builds-container > ul > li').length).toEqual(3);
});
describe('jobId', () => {
it('escapes job name', () => {
component = mountComponent(StageColumnComponent, {
- jobs: [
+ groups: [
{
id: 4259,
name: '<img src=x onerror=alert(document.domain)>',
@@ -64,9 +63,9 @@ describe('stage column component', () => {
title: 'test',
});
- expect(
- component.$el.querySelector('.builds-container li').getAttribute('id'),
- ).toEqual('ci-badge-&lt;img src=x onerror=alert(document.domain)&gt;');
+ expect(component.$el.querySelector('.builds-container li').getAttribute('id')).toEqual(
+ 'ci-badge-&lt;img src=x onerror=alert(document.domain)&gt;',
+ );
});
});
});
diff --git a/spec/javascripts/pipelines/header_component_spec.js b/spec/javascripts/pipelines/header_component_spec.js
index 034d3b4957d..556a0976b29 100644
--- a/spec/javascripts/pipelines/header_component_spec.js
+++ b/spec/javascripts/pipelines/header_component_spec.js
@@ -47,13 +47,16 @@ describe('Pipeline details header', () => {
it('should render provided pipeline info', () => {
expect(
- vm.$el.querySelector('.header-main-content').textContent.replace(/\s+/g, ' ').trim(),
- ).toEqual('failed Pipeline #123 triggered 3 weeks ago by Foo');
+ vm.$el
+ .querySelector('.header-main-content')
+ .textContent.replace(/\s+/g, ' ')
+ .trim(),
+ ).toContain('failed Pipeline #123 triggered 3 weeks ago by Foo');
});
describe('action buttons', () => {
it('should call postAction when button action is clicked', () => {
- eventHub.$on('headerPostAction', (action) => {
+ eventHub.$on('headerPostAction', action => {
expect(action.path).toEqual('path');
});
diff --git a/spec/javascripts/pipelines/nav_controls_spec.js b/spec/javascripts/pipelines/nav_controls_spec.js
index d6232f5c567..7806cdf1477 100644
--- a/spec/javascripts/pipelines/nav_controls_spec.js
+++ b/spec/javascripts/pipelines/nav_controls_spec.js
@@ -24,7 +24,9 @@ describe('Pipelines Nav Controls', () => {
component = mountComponent(NavControlsComponent, mockData);
expect(component.$el.querySelector('.js-run-pipeline').textContent).toContain('Run Pipeline');
- expect(component.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(mockData.newPipelinePath);
+ expect(component.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(
+ mockData.newPipelinePath,
+ );
});
it('should not render link to create pipeline if no path is provided', () => {
@@ -50,7 +52,9 @@ describe('Pipelines Nav Controls', () => {
component = mountComponent(NavControlsComponent, mockData);
expect(component.$el.querySelector('.js-ci-lint').textContent.trim()).toContain('CI Lint');
- expect(component.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(mockData.ciLintPath);
+ expect(component.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(
+ mockData.ciLintPath,
+ );
});
describe('Reset Runners Cache', () => {
@@ -65,7 +69,9 @@ describe('Pipelines Nav Controls', () => {
});
it('should render button for resetting runner caches', () => {
- expect(component.$el.querySelector('.js-clear-cache').textContent.trim()).toContain('Clear Runner Caches');
+ expect(component.$el.querySelector('.js-clear-cache').textContent.trim()).toContain(
+ 'Clear Runner Caches',
+ );
});
it('should emit postAction event when reset runner cache button is clicked', () => {
diff --git a/spec/javascripts/pipelines/pipeline_store_spec.js b/spec/javascripts/pipelines/pipeline_store_spec.js
index ab2287cc344..1d5754d1f05 100644
--- a/spec/javascripts/pipelines/pipeline_store_spec.js
+++ b/spec/javascripts/pipelines/pipeline_store_spec.js
@@ -20,6 +20,7 @@ describe('Pipeline Store', () => {
it('should store received object', () => {
store.storePipeline({ foo: 'bar' });
+
expect(store.state.pipeline).toEqual({ foo: 'bar' });
});
});
diff --git a/spec/javascripts/pipelines/pipeline_url_spec.js b/spec/javascripts/pipelines/pipeline_url_spec.js
index ddd580ae8b7..d6c44f4c976 100644
--- a/spec/javascripts/pipelines/pipeline_url_spec.js
+++ b/spec/javascripts/pipelines/pipeline_url_spec.js
@@ -38,6 +38,7 @@ describe('Pipeline Url Component', () => {
expect(component.$el.querySelector('.js-pipeline-url-link').getAttribute('href')).toEqual(
'foo',
);
+
expect(component.$el.querySelector('.js-pipeline-url-link span').textContent).toEqual('#1');
});
@@ -62,11 +63,15 @@ describe('Pipeline Url Component', () => {
}).$mount();
const image = component.$el.querySelector('.js-pipeline-url-user img');
+ const tooltip = component.$el.querySelector(
+ '.js-pipeline-url-user .js-user-avatar-image-toolip',
+ );
expect(component.$el.querySelector('.js-pipeline-url-user').getAttribute('href')).toEqual(
mockData.pipeline.user.web_url,
);
- expect(image.getAttribute('data-original-title')).toEqual(mockData.pipeline.user.name);
+
+ expect(tooltip.textContent.trim()).toEqual(mockData.pipeline.user.name);
expect(image.getAttribute('src')).toEqual(`${mockData.pipeline.user.avatar_url}?width=20`);
});
@@ -105,6 +110,7 @@ describe('Pipeline Url Component', () => {
expect(component.$el.querySelector('.js-pipeline-url-yaml').textContent).toContain(
'yaml invalid',
);
+
expect(component.$el.querySelector('.js-pipeline-url-stuck').textContent).toContain('stuck');
});
diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js
index 72fb0a8f9ef..a7dcd532f4f 100644
--- a/spec/javascripts/pipelines/pipelines_actions_spec.js
+++ b/spec/javascripts/pipelines/pipelines_actions_spec.js
@@ -1,46 +1,104 @@
import Vue from 'vue';
-import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue';
+import eventHub from '~/pipelines/event_hub';
+import PipelinesActions from '~/pipelines/components/pipelines_actions.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { TEST_HOST } from 'spec/test_constants';
describe('Pipelines Actions dropdown', () => {
- let component;
- let actions;
- let ActionsComponent;
+ const Component = Vue.extend(PipelinesActions);
+ let vm;
- beforeEach(() => {
- ActionsComponent = Vue.extend(pipelinesActionsComp);
+ afterEach(() => {
+ vm.$destroy();
+ });
- actions = [
+ describe('manual actions', () => {
+ const actions = [
{
name: 'stop_review',
- path: '/root/review-app/builds/1893/play',
+ path: `${TEST_HOST}/root/review-app/builds/1893/play`,
},
{
name: 'foo',
- path: '#',
+ path: `${TEST_HOST}/disabled/pipeline/action`,
playable: false,
},
];
- component = new ActionsComponent({
- propsData: {
- actions,
- },
- }).$mount();
- });
+ beforeEach(() => {
+ vm = mountComponent(Component, { actions });
+ });
+
+ it('renders a dropdown with the provided actions', () => {
+ const dropdownItems = vm.$el.querySelectorAll('.dropdown-menu li');
- it('should render a dropdown with the provided actions', () => {
- expect(
- component.$el.querySelectorAll('.dropdown-menu li').length,
- ).toEqual(actions.length);
+ expect(dropdownItems.length).toEqual(actions.length);
+ });
+
+ it("renders a disabled action when it's not playable", () => {
+ const dropdownItem = vm.$el.querySelector('.dropdown-menu li:last-child button');
+
+ expect(dropdownItem).toBeDisabled();
+ });
});
- it('should render a disabled action when it\'s not playable', () => {
- expect(
- component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'),
- ).toEqual('disabled');
+ describe('scheduled jobs', () => {
+ const scheduledJobAction = {
+ name: 'scheduled action',
+ path: `${TEST_HOST}/scheduled/job/action`,
+ playable: true,
+ scheduled_at: '2063-04-05T00:42:00Z',
+ };
+ const expiredJobAction = {
+ name: 'expired action',
+ path: `${TEST_HOST}/expired/job/action`,
+ playable: true,
+ scheduled_at: '2018-10-05T08:23:00Z',
+ };
+ const findDropdownItem = action => {
+ const buttons = vm.$el.querySelectorAll('.dropdown-menu li button');
+ return Array.prototype.find.call(buttons, element =>
+ element.innerText.trim().startsWith(action.name),
+ );
+ };
+
+ beforeEach(done => {
+ spyOn(Date, 'now').and.callFake(() => new Date('2063-04-04T00:42:00Z').getTime());
+ vm = mountComponent(Component, { actions: [scheduledJobAction, expiredJobAction] });
+
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('emits postAction event after confirming', () => {
+ const emitSpy = jasmine.createSpy('emit');
+ eventHub.$on('postAction', emitSpy);
+ spyOn(window, 'confirm').and.callFake(() => true);
+
+ findDropdownItem(scheduledJobAction).click();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(emitSpy).toHaveBeenCalledWith(scheduledJobAction.path);
+ });
+
+ it('does not emit postAction event if confirmation is cancelled', () => {
+ const emitSpy = jasmine.createSpy('emit');
+ eventHub.$on('postAction', emitSpy);
+ spyOn(window, 'confirm').and.callFake(() => false);
+
+ findDropdownItem(scheduledJobAction).click();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(emitSpy).not.toHaveBeenCalled();
+ });
+
+ it('displays the remaining time in the dropdown', () => {
+ expect(findDropdownItem(scheduledJobAction)).toContainText('24:00:00');
+ });
- expect(
- component.$el.querySelector('.dropdown-menu li:last-child button').classList.contains('disabled'),
- ).toEqual(true);
+ it('displays 00:00:00 for expired jobs in the dropdown', () => {
+ expect(findDropdownItem(expiredJobAction)).toContainText('00:00:00');
+ });
});
});
diff --git a/spec/javascripts/pipelines/pipelines_artifacts_spec.js b/spec/javascripts/pipelines/pipelines_artifacts_spec.js
index a8a8e3e2cff..7705d5a19bf 100644
--- a/spec/javascripts/pipelines/pipelines_artifacts_spec.js
+++ b/spec/javascripts/pipelines/pipelines_artifacts_spec.js
@@ -23,18 +23,16 @@ describe('Pipelines Artifacts dropdown', () => {
});
it('should render a dropdown with the provided artifacts', () => {
- expect(
- component.$el.querySelectorAll('.dropdown-menu li').length,
- ).toEqual(artifacts.length);
+ expect(component.$el.querySelectorAll('.dropdown-menu li').length).toEqual(artifacts.length);
});
it('should render a link with the provided path', () => {
- expect(
- component.$el.querySelector('.dropdown-menu li a').getAttribute('href'),
- ).toEqual(artifacts[0].path);
+ expect(component.$el.querySelector('.dropdown-menu li a').getAttribute('href')).toEqual(
+ artifacts[0].path,
+ );
- expect(
- component.$el.querySelector('.dropdown-menu li a').textContent,
- ).toContain(artifacts[0].name);
+ expect(component.$el.querySelector('.dropdown-menu li a').textContent).toContain(
+ artifacts[0].name,
+ );
});
});
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index 50141bd99b4..97ded16db69 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -52,7 +52,7 @@ describe('Pipelines', () => {
describe('With permission', () => {
describe('With pipelines in main tab', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
vm = mountComponent(PipelinesComponent, {
@@ -72,7 +72,9 @@ describe('Pipelines', () => {
});
it('renders Run Pipeline link', () => {
- expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(paths.newPipelinePath);
+ expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(
+ paths.newPipelinePath,
+ );
});
it('renders CI Lint link', () => {
@@ -80,18 +82,20 @@ describe('Pipelines', () => {
});
it('renders Clear Runner Cache button', () => {
- expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual('Clear Runner Caches');
+ expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual(
+ 'Clear Runner Caches',
+ );
});
it('renders pipelines table', () => {
- expect(
- vm.$el.querySelectorAll('.gl-responsive-table-row').length,
- ).toEqual(pipelines.pipelines.length + 1);
+ expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual(
+ pipelines.pipelines.length + 1,
+ );
});
});
describe('Without pipelines on main tab with CI', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet('twitter/flight/pipelines.json').reply(200, {
pipelines: [],
count: {
@@ -118,7 +122,9 @@ describe('Pipelines', () => {
});
it('renders Run Pipeline link', () => {
- expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(paths.newPipelinePath);
+ expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(
+ paths.newPipelinePath,
+ );
});
it('renders CI Lint link', () => {
@@ -126,16 +132,20 @@ describe('Pipelines', () => {
});
it('renders Clear Runner Cache button', () => {
- expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual('Clear Runner Caches');
+ expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual(
+ 'Clear Runner Caches',
+ );
});
it('renders tab empty state', () => {
- expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual('There are currently no pipelines.');
+ expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual(
+ 'There are currently no pipelines.',
+ );
});
});
describe('Without pipelines nor CI', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet('twitter/flight/pipelines.json').reply(200, {
pipelines: [],
count: {
@@ -158,8 +168,13 @@ describe('Pipelines', () => {
});
it('renders empty state', () => {
- expect(vm.$el.querySelector('.js-empty-state h4').textContent.trim()).toEqual('Build with confidence');
- expect(vm.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual(paths.helpPagePath);
+ expect(vm.$el.querySelector('.js-empty-state h4').textContent.trim()).toEqual(
+ 'Build with confidence',
+ );
+
+ expect(vm.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual(
+ paths.helpPagePath,
+ );
});
it('does not render tabs nor buttons', () => {
@@ -171,7 +186,7 @@ describe('Pipelines', () => {
});
describe('When API returns error', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet('twitter/flight/pipelines.json').reply(500, {});
vm = mountComponent(PipelinesComponent, {
store: new Store(),
@@ -190,20 +205,27 @@ describe('Pipelines', () => {
});
it('renders buttons', () => {
- expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(paths.newPipelinePath);
+ expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(
+ paths.newPipelinePath,
+ );
+
expect(vm.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(paths.ciLintPath);
- expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual('Clear Runner Caches');
+ expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual(
+ 'Clear Runner Caches',
+ );
});
it('renders error state', () => {
- expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain('There was an error fetching the pipelines.');
+ expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain(
+ 'There was an error fetching the pipelines.',
+ );
});
});
});
describe('Without permission', () => {
describe('With pipelines in main tab', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
vm = mountComponent(PipelinesComponent, {
@@ -229,14 +251,14 @@ describe('Pipelines', () => {
});
it('renders pipelines table', () => {
- expect(
- vm.$el.querySelectorAll('.gl-responsive-table-row').length,
- ).toEqual(pipelines.pipelines.length + 1);
+ expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual(
+ pipelines.pipelines.length + 1,
+ );
});
});
describe('Without pipelines on main tab with CI', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet('twitter/flight/pipelines.json').reply(200, {
pipelines: [],
count: {
@@ -270,12 +292,14 @@ describe('Pipelines', () => {
});
it('renders tab empty state', () => {
- expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual('There are currently no pipelines.');
+ expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual(
+ 'There are currently no pipelines.',
+ );
});
});
describe('Without pipelines nor CI', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet('twitter/flight/pipelines.json').reply(200, {
pipelines: [],
count: {
@@ -299,7 +323,10 @@ describe('Pipelines', () => {
});
it('renders empty state without button to set CI', () => {
- expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toEqual('This project is not currently set up to run pipelines.');
+ expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toEqual(
+ 'This project is not currently set up to run pipelines.',
+ );
+
expect(vm.$el.querySelector('.js-get-started-pipelines')).toBeNull();
});
@@ -312,7 +339,7 @@ describe('Pipelines', () => {
});
describe('When API returns error', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet('twitter/flight/pipelines.json').reply(500, {});
vm = mountComponent(PipelinesComponent, {
@@ -338,12 +365,14 @@ describe('Pipelines', () => {
});
it('renders error state', () => {
- expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain('There was an error fetching the pipelines.');
+ expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain(
+ 'There was an error fetching the pipelines.',
+ );
});
});
});
- describe('successfull request', () => {
+ describe('successful request', () => {
describe('with pipelines', () => {
beforeEach(() => {
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
@@ -356,41 +385,44 @@ describe('Pipelines', () => {
});
});
- it('should render table', (done) => {
+ it('should render table', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.table-holder')).toBeDefined();
- expect(
- vm.$el.querySelectorAll('.gl-responsive-table-row').length,
- ).toEqual(pipelines.pipelines.length + 1);
+ expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual(
+ pipelines.pipelines.length + 1,
+ );
done();
});
});
- it('should render navigation tabs', (done) => {
+ it('should render navigation tabs', done => {
setTimeout(() => {
- expect(
- vm.$el.querySelector('.js-pipelines-tab-pending').textContent.trim(),
- ).toContain('Pending');
- expect(
- vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim(),
- ).toContain('All');
- expect(
- vm.$el.querySelector('.js-pipelines-tab-running').textContent.trim(),
- ).toContain('Running');
- expect(
- vm.$el.querySelector('.js-pipelines-tab-finished').textContent.trim(),
- ).toContain('Finished');
- expect(
- vm.$el.querySelector('.js-pipelines-tab-branches').textContent.trim(),
- ).toContain('Branches');
- expect(
- vm.$el.querySelector('.js-pipelines-tab-tags').textContent.trim(),
- ).toContain('Tags');
+ expect(vm.$el.querySelector('.js-pipelines-tab-pending').textContent.trim()).toContain(
+ 'Pending',
+ );
+
+ expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All');
+
+ expect(vm.$el.querySelector('.js-pipelines-tab-running').textContent.trim()).toContain(
+ 'Running',
+ );
+
+ expect(vm.$el.querySelector('.js-pipelines-tab-finished').textContent.trim()).toContain(
+ 'Finished',
+ );
+
+ expect(vm.$el.querySelector('.js-pipelines-tab-branches').textContent.trim()).toContain(
+ 'Branches',
+ );
+
+ expect(vm.$el.querySelector('.js-pipelines-tab-tags').textContent.trim()).toContain(
+ 'Tags',
+ );
done();
});
});
- it('should make an API request when using tabs', (done) => {
+ it('should make an API request when using tabs', done => {
setTimeout(() => {
spyOn(vm, 'updateContent');
vm.$el.querySelector('.js-pipelines-tab-finished').click();
@@ -401,7 +433,7 @@ describe('Pipelines', () => {
});
describe('with pagination', () => {
- it('should make an API request when using pagination', (done) => {
+ it('should make an API request when using pagination', done => {
setTimeout(() => {
spyOn(vm, 'updateContent');
// Mock pagination
@@ -415,6 +447,7 @@ describe('Pipelines', () => {
vm.$nextTick(() => {
vm.$el.querySelector('.js-next-button a').click();
+
expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'all', page: '2' });
done();
@@ -504,7 +537,7 @@ describe('Pipelines', () => {
});
describe('emptyTabMessage', () => {
- it('returns message with scope', (done) => {
+ it('returns message with scope', done => {
vm.scope = 'pending';
vm.$nextTick(() => {
@@ -523,7 +556,7 @@ describe('Pipelines', () => {
expect(vm.stateToRender).toEqual('loading');
});
- it('returns error state when app has error', (done) => {
+ it('returns error state when app has error', done => {
vm.hasError = true;
vm.isLoading = false;
@@ -533,7 +566,7 @@ describe('Pipelines', () => {
});
});
- it('returns table list when app has pipelines', (done) => {
+ it('returns table list when app has pipelines', done => {
vm.isLoading = false;
vm.hasError = false;
vm.state.pipelines = pipelines.pipelines;
@@ -545,7 +578,7 @@ describe('Pipelines', () => {
});
});
- it('returns empty tab when app does not have pipelines but project has pipelines', (done) => {
+ it('returns empty tab when app does not have pipelines but project has pipelines', done => {
vm.state.count.all = 10;
vm.isLoading = false;
@@ -556,7 +589,7 @@ describe('Pipelines', () => {
});
});
- it('returns empty tab when project has CI', (done) => {
+ it('returns empty tab when project has CI', done => {
vm.isLoading = false;
vm.$nextTick(() => {
expect(vm.stateToRender).toEqual('emptyTab');
@@ -565,7 +598,7 @@ describe('Pipelines', () => {
});
});
- it('returns empty state when project does not have pipelines nor CI', (done) => {
+ it('returns empty state when project does not have pipelines nor CI', done => {
vm.isLoading = false;
vm.hasGitlabCi = false;
vm.$nextTick(() => {
@@ -577,7 +610,7 @@ describe('Pipelines', () => {
});
describe('shouldRenderTabs', () => {
- it('returns true when state is loading & has already made the first request', (done) => {
+ it('returns true when state is loading & has already made the first request', done => {
vm.isLoading = true;
vm.hasMadeRequest = true;
@@ -588,7 +621,7 @@ describe('Pipelines', () => {
});
});
- it('returns true when state is tableList & has already made the first request', (done) => {
+ it('returns true when state is tableList & has already made the first request', done => {
vm.isLoading = false;
vm.state.pipelines = pipelines.pipelines;
vm.hasMadeRequest = true;
@@ -600,7 +633,7 @@ describe('Pipelines', () => {
});
});
- it('returns true when state is error & has already made the first request', (done) => {
+ it('returns true when state is error & has already made the first request', done => {
vm.isLoading = false;
vm.hasError = true;
vm.hasMadeRequest = true;
@@ -612,7 +645,7 @@ describe('Pipelines', () => {
});
});
- it('returns true when state is empty tab & has already made the first request', (done) => {
+ it('returns true when state is empty tab & has already made the first request', done => {
vm.isLoading = false;
vm.state.count.all = 10;
vm.hasMadeRequest = true;
@@ -624,7 +657,7 @@ describe('Pipelines', () => {
});
});
- it('returns false when has not made first request', (done) => {
+ it('returns false when has not made first request', done => {
vm.hasMadeRequest = false;
vm.$nextTick(() => {
@@ -634,7 +667,7 @@ describe('Pipelines', () => {
});
});
- it('returns false when state is emtpy state', (done) => {
+ it('returns false when state is empty state', done => {
vm.isLoading = false;
vm.hasMadeRequest = true;
vm.hasGitlabCi = false;
@@ -648,7 +681,7 @@ describe('Pipelines', () => {
});
describe('shouldRenderButtons', () => {
- it('returns true when it has paths & has made the first request', (done) => {
+ it('returns true when it has paths & has made the first request', done => {
vm.hasMadeRequest = true;
vm.$nextTick(() => {
@@ -658,7 +691,7 @@ describe('Pipelines', () => {
});
});
- it('returns false when it has not made the first request', (done) => {
+ it('returns false when it has not made the first request', done => {
vm.hasMadeRequest = false;
vm.$nextTick(() => {
@@ -675,19 +708,24 @@ describe('Pipelines', () => {
const copyPipeline = Object.assign({}, pipelineWithStages);
copyPipeline.id += 1;
mock
- .onGet('twitter/flight/pipelines.json').reply(200, {
- pipelines: [pipelineWithStages],
- count: {
- all: 1,
- finished: 1,
- pending: 0,
- running: 0,
+ .onGet('twitter/flight/pipelines.json')
+ .reply(
+ 200,
+ {
+ pipelines: [pipelineWithStages],
+ count: {
+ all: 1,
+ finished: 1,
+ pending: 0,
+ running: 0,
+ },
},
- }, {
- 'POLL-INTERVAL': 100,
- })
+ {
+ 'POLL-INTERVAL': 100,
+ },
+ )
.onGet(pipelineWithStages.details.stages[0].dropdown_path)
- .reply(200, stageReply);
+ .reply(200, stageReply);
vm = mountComponent(PipelinesComponent, {
store: new Store(),
@@ -698,7 +736,7 @@ describe('Pipelines', () => {
});
describe('when a request is being made', () => {
- it('stops polling, cancels the request, fetches pipelines & restarts polling', (done) => {
+ it('stops polling, cancels the request, fetches pipelines & restarts polling', done => {
spyOn(vm.poll, 'stop');
spyOn(vm.poll, 'restart');
spyOn(vm, 'getPipelines').and.returnValue(Promise.resolve());
@@ -706,7 +744,8 @@ describe('Pipelines', () => {
setTimeout(() => {
vm.isMakingRequest = true;
- return vm.$nextTick()
+ return vm
+ .$nextTick()
.then(() => {
vm.$el.querySelector('.js-builds-dropdown-button').click();
})
@@ -719,13 +758,14 @@ describe('Pipelines', () => {
expect(vm.poll.restart).toHaveBeenCalled();
done();
}, 0);
- });
+ })
+ .catch(done.fail);
}, 0);
});
});
describe('when no request is being made', () => {
- it('stops polling, fetches pipelines & restarts polling', (done) => {
+ it('stops polling, fetches pipelines & restarts polling', done => {
spyOn(vm.poll, 'stop');
spyOn(vm.poll, 'restart');
spyOn(vm, 'getPipelines').and.returnValue(Promise.resolve());
diff --git a/spec/javascripts/pipelines/pipelines_store_spec.js b/spec/javascripts/pipelines/pipelines_store_spec.js
index 10ff0c6bb84..ce21f788ed5 100644
--- a/spec/javascripts/pipelines/pipelines_store_spec.js
+++ b/spec/javascripts/pipelines/pipelines_store_spec.js
@@ -16,12 +16,14 @@ describe('Pipelines Store', () => {
describe('storePipelines', () => {
it('should use the default parameter if none is provided', () => {
store.storePipelines();
+
expect(store.state.pipelines).toEqual([]);
});
it('should store the provided array', () => {
const array = [{ id: 1, status: 'running' }, { id: 2, status: 'success' }];
store.storePipelines(array);
+
expect(store.state.pipelines).toEqual(array);
});
});
@@ -29,6 +31,7 @@ describe('Pipelines Store', () => {
describe('storeCount', () => {
it('should use the default parameter if none is provided', () => {
store.storeCount();
+
expect(store.state.count).toEqual({});
});
@@ -43,6 +46,7 @@ describe('Pipelines Store', () => {
describe('storePagination', () => {
it('should use the default parameter if none is provided', () => {
store.storePagination();
+
expect(store.state.pageInfo).toEqual({});
});
@@ -66,6 +70,7 @@ describe('Pipelines Store', () => {
};
store.storePagination(pagination);
+
expect(store.state.pageInfo).toEqual(expectedResult);
});
});
diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index 03ffc122795..4c575536f0e 100644
--- a/spec/javascripts/pipelines/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -37,6 +37,7 @@ describe('Pipelines Table Row', () => {
it('should render a table row', () => {
component = buildComponent(pipeline);
+
expect(component.$el.getAttribute('class')).toContain('gl-responsive-table-row');
});
@@ -85,8 +86,8 @@ describe('Pipelines Table Row', () => {
expect(
component.$el
- .querySelector('.table-section:nth-child(2) img')
- .getAttribute('data-original-title'),
+ .querySelector('.table-section:nth-child(2) .js-user-avatar-image-toolip')
+ .textContent.trim(),
).toEqual(pipeline.user.name);
});
});
@@ -97,6 +98,7 @@ describe('Pipelines Table Row', () => {
component = buildComponent(pipeline);
const commitLink = component.$el.querySelector('.branch-commit .commit-sha');
+
expect(commitLink.getAttribute('href')).toEqual(pipeline.commit.commit_path);
});
@@ -110,8 +112,8 @@ describe('Pipelines Table Row', () => {
const commitAuthorLink = commitAuthorElement.getAttribute('href');
const commitAuthorName = commitAuthorElement
- .querySelector('img.avatar')
- .getAttribute('data-original-title');
+ .querySelector('.js-user-avatar-image-toolip')
+ .textContent.trim();
return { commitAuthorElement, commitAuthorLink, commitAuthorName };
};
@@ -158,8 +160,13 @@ describe('Pipelines Table Row', () => {
});
describe('actions column', () => {
+ const scheduledJobAction = {
+ name: 'some scheduled job',
+ };
+
beforeEach(() => {
const withActions = Object.assign({}, pipeline);
+ withActions.details.scheduled_actions = [scheduledJobAction];
withActions.flags.cancelable = true;
withActions.flags.retryable = true;
withActions.cancel_path = '/cancel';
@@ -171,6 +178,9 @@ describe('Pipelines Table Row', () => {
it('should render the provided actions', () => {
expect(component.$el.querySelector('.js-pipelines-retry-button')).not.toBeNull();
expect(component.$el.querySelector('.js-pipelines-cancel-button')).not.toBeNull();
+ const dropdownMenu = component.$el.querySelectorAll('.dropdown-menu');
+
+ expect(dropdownMenu).toContainText(scheduledJobAction.name);
});
it('emits `retryPipeline` event when retry button is clicked and toggles loading', () => {
@@ -179,6 +189,7 @@ describe('Pipelines Table Row', () => {
});
component.$el.querySelector('.js-pipelines-retry-button').click();
+
expect(component.isRetrying).toEqual(true);
});
@@ -193,7 +204,8 @@ describe('Pipelines Table Row', () => {
it('renders a loading icon when `cancelingPipeline` matches pipeline id', done => {
component.cancelingPipeline = pipeline.id;
- component.$nextTick()
+ component
+ .$nextTick()
.then(() => {
expect(component.isCancelling).toEqual(true);
})
diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js
index d21ba35e96d..5c3387190ab 100644
--- a/spec/javascripts/pipelines/pipelines_table_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_spec.js
@@ -38,10 +38,21 @@ describe('Pipelines Table', () => {
});
it('should render table head with correct columns', () => {
- expect(component.$el.querySelector('.table-section.js-pipeline-status').textContent.trim()).toEqual('Status');
- expect(component.$el.querySelector('.table-section.js-pipeline-info').textContent.trim()).toEqual('Pipeline');
- expect(component.$el.querySelector('.table-section.js-pipeline-commit').textContent.trim()).toEqual('Commit');
- expect(component.$el.querySelector('.table-section.js-pipeline-stages').textContent.trim()).toEqual('Stages');
+ expect(
+ component.$el.querySelector('.table-section.js-pipeline-status').textContent.trim(),
+ ).toEqual('Status');
+
+ expect(
+ component.$el.querySelector('.table-section.js-pipeline-info').textContent.trim(),
+ ).toEqual('Pipeline');
+
+ expect(
+ component.$el.querySelector('.table-section.js-pipeline-commit').textContent.trim(),
+ ).toEqual('Commit');
+
+ expect(
+ component.$el.querySelector('.table-section.js-pipeline-stages').textContent.trim(),
+ ).toEqual('Stages');
});
});
@@ -54,6 +65,7 @@ describe('Pipelines Table', () => {
viewType: 'root',
},
}).$mount();
+
expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0);
});
});
diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js
index 3f6789759ae..3c8b8032de8 100644
--- a/spec/javascripts/pipelines/stage_spec.js
+++ b/spec/javascripts/pipelines/stage_spec.js
@@ -40,7 +40,7 @@ describe('Pipelines stage component', () => {
expect(component.$el.querySelector('button').getAttribute('data-toggle')).toEqual('dropdown');
});
- describe('with successfull request', () => {
+ describe('with successful request', () => {
beforeEach(() => {
mock.onGet('path.json').reply(200, stageReply);
});
@@ -53,6 +53,7 @@ describe('Pipelines stage component', () => {
expect(
component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(),
).toContain(stageReply.latest_statuses[0].name);
+
expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown');
done();
}, 0);
@@ -119,12 +120,13 @@ describe('Pipelines stage component', () => {
setTimeout(() => {
component.$el.querySelector('.js-ci-action').click();
- component.$nextTick()
- .then(() => {
- expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable');
- })
- .then(done)
- .catch(done.fail);
+ component
+ .$nextTick()
+ .then(() => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable');
+ })
+ .then(done)
+ .catch(done.fail);
}, 0);
});
});
diff --git a/spec/javascripts/pipelines_spec.js b/spec/javascripts/pipelines_spec.js
index c08a73851be..6b86f9ea437 100644
--- a/spec/javascripts/pipelines_spec.js
+++ b/spec/javascripts/pipelines_spec.js
@@ -12,6 +12,8 @@ describe('Pipelines', () => {
});
it('should create a `Pipelines` instance without options', () => {
- expect(() => { new Pipelines(); }).not.toThrow(); //eslint-disable-line
+ expect(() => {
+ new Pipelines(); // eslint-disable-line no-new
+ }).not.toThrow();
});
});
diff --git a/spec/javascripts/polyfills/element_spec.js b/spec/javascripts/polyfills/element_spec.js
index ecaaf1907ea..d35df595c72 100644
--- a/spec/javascripts/polyfills/element_spec.js
+++ b/spec/javascripts/polyfills/element_spec.js
@@ -1,6 +1,6 @@
import '~/commons/polyfills/element';
-describe('Element polyfills', function () {
+describe('Element polyfills', function() {
beforeEach(() => {
this.element = document.createElement('ul');
});
diff --git a/spec/javascripts/pretty_time_spec.js b/spec/javascripts/pretty_time_spec.js
deleted file mode 100644
index 084ffe08917..00000000000
--- a/spec/javascripts/pretty_time_spec.js
+++ /dev/null
@@ -1,133 +0,0 @@
-import { parseSeconds, abbreviateTime, stringifyTime } from '~/lib/utils/pretty_time';
-
-function assertTimeUnits(obj, minutes, hours, days, weeks) {
- expect(obj.minutes).toBe(minutes);
- expect(obj.hours).toBe(hours);
- expect(obj.days).toBe(days);
- expect(obj.weeks).toBe(weeks);
-}
-
-describe('prettyTime methods', () => {
- describe('parseSeconds', () => {
- it('should correctly parse a negative value', () => {
- const zeroSeconds = parseSeconds(-1000);
-
- assertTimeUnits(zeroSeconds, 16, 0, 0, 0);
- });
-
- it('should correctly parse a zero value', () => {
- const zeroSeconds = parseSeconds(0);
-
- assertTimeUnits(zeroSeconds, 0, 0, 0, 0);
- });
-
- it('should correctly parse a small non-zero second values', () => {
- const subOneMinute = parseSeconds(10);
- const aboveOneMinute = parseSeconds(100);
- const manyMinutes = parseSeconds(1000);
-
- assertTimeUnits(subOneMinute, 0, 0, 0, 0);
- assertTimeUnits(aboveOneMinute, 1, 0, 0, 0);
- assertTimeUnits(manyMinutes, 16, 0, 0, 0);
- });
-
- it('should correctly parse large second values', () => {
- const aboveOneHour = parseSeconds(4800);
- const aboveOneDay = parseSeconds(110000);
- const aboveOneWeek = parseSeconds(25000000);
-
- assertTimeUnits(aboveOneHour, 20, 1, 0, 0);
- assertTimeUnits(aboveOneDay, 33, 6, 3, 0);
- assertTimeUnits(aboveOneWeek, 26, 0, 3, 173);
- });
-
- it('should correctly accept a custom param for hoursPerDay', () => {
- const config = { hoursPerDay: 24 };
-
- const aboveOneHour = parseSeconds(4800, config);
- const aboveOneDay = parseSeconds(110000, config);
- const aboveOneWeek = parseSeconds(25000000, config);
-
- assertTimeUnits(aboveOneHour, 20, 1, 0, 0);
- assertTimeUnits(aboveOneDay, 33, 6, 1, 0);
- assertTimeUnits(aboveOneWeek, 26, 8, 4, 57);
- });
-
- it('should correctly accept a custom param for daysPerWeek', () => {
- const config = { daysPerWeek: 7 };
-
- const aboveOneHour = parseSeconds(4800, config);
- const aboveOneDay = parseSeconds(110000, config);
- const aboveOneWeek = parseSeconds(25000000, config);
-
- assertTimeUnits(aboveOneHour, 20, 1, 0, 0);
- assertTimeUnits(aboveOneDay, 33, 6, 3, 0);
- assertTimeUnits(aboveOneWeek, 26, 0, 0, 124);
- });
-
- it('should correctly accept custom params for daysPerWeek and hoursPerDay', () => {
- const config = { daysPerWeek: 55, hoursPerDay: 14 };
-
- const aboveOneHour = parseSeconds(4800, config);
- const aboveOneDay = parseSeconds(110000, config);
- const aboveOneWeek = parseSeconds(25000000, config);
-
- assertTimeUnits(aboveOneHour, 20, 1, 0, 0);
- assertTimeUnits(aboveOneDay, 33, 2, 2, 0);
- assertTimeUnits(aboveOneWeek, 26, 0, 1, 9);
- });
- });
-
- describe('stringifyTime', () => {
- it('should stringify values with all non-zero units', () => {
- const timeObject = {
- weeks: 1,
- days: 4,
- hours: 7,
- minutes: 20,
- };
-
- const timeString = stringifyTime(timeObject);
-
- expect(timeString).toBe('1w 4d 7h 20m');
- });
-
- it('should stringify values with some non-zero units', () => {
- const timeObject = {
- weeks: 0,
- days: 4,
- hours: 0,
- minutes: 20,
- };
-
- const timeString = stringifyTime(timeObject);
-
- expect(timeString).toBe('4d 20m');
- });
-
- it('should stringify values with no non-zero units', () => {
- const timeObject = {
- weeks: 0,
- days: 0,
- hours: 0,
- minutes: 0,
- };
-
- const timeString = stringifyTime(timeObject);
-
- expect(timeString).toBe('0m');
- });
- });
-
- describe('abbreviateTime', () => {
- it('should abbreviate stringified times for weeks', () => {
- const fullTimeString = '1w 3d 4h 5m';
- expect(abbreviateTime(fullTimeString)).toBe('1w');
- });
-
- it('should abbreviate stringified times for non-weeks', () => {
- const fullTimeString = '0w 3d 4h 5m';
- expect(abbreviateTime(fullTimeString)).toBe('3d');
- });
- });
-});
diff --git a/spec/javascripts/profile/account/components/delete_account_modal_spec.js b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
index a0939ff5c20..d5f5cabc63e 100644
--- a/spec/javascripts/profile/account/components/delete_account_modal_spec.js
+++ b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
@@ -28,7 +28,7 @@ describe('DeleteAccountModal component', () => {
};
describe('with password confirmation', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = mountComponent(Component, {
actionUrl,
confirmWithPassword: true,
@@ -42,7 +42,7 @@ describe('DeleteAccountModal component', () => {
.catch(done.fail);
});
- it('does not accept empty password', (done) => {
+ it('does not accept empty password', done => {
const { form, input, submitButton } = findElements();
spyOn(form, 'submit');
input.value = '';
@@ -53,13 +53,14 @@ describe('DeleteAccountModal component', () => {
expect(vm.enteredPassword).toBe(input.value);
expect(submitButton).toHaveAttr('disabled', 'disabled');
submitButton.click();
+
expect(form.submit).not.toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
});
- it('submits form with password', (done) => {
+ it('submits form with password', done => {
const { form, input, submitButton } = findElements();
spyOn(form, 'submit');
input.value = 'anything';
@@ -70,6 +71,7 @@ describe('DeleteAccountModal component', () => {
expect(vm.enteredPassword).toBe(input.value);
expect(submitButton).not.toHaveAttr('disabled', 'disabled');
submitButton.click();
+
expect(form.submit).toHaveBeenCalled();
})
.then(done)
@@ -78,7 +80,7 @@ describe('DeleteAccountModal component', () => {
});
describe('with username confirmation', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm = mountComponent(Component, {
actionUrl,
confirmWithPassword: false,
@@ -92,7 +94,7 @@ describe('DeleteAccountModal component', () => {
.catch(done.fail);
});
- it('does not accept wrong username', (done) => {
+ it('does not accept wrong username', done => {
const { form, input, submitButton } = findElements();
spyOn(form, 'submit');
input.value = 'this is wrong';
@@ -103,13 +105,14 @@ describe('DeleteAccountModal component', () => {
expect(vm.enteredUsername).toBe(input.value);
expect(submitButton).toHaveAttr('disabled', 'disabled');
submitButton.click();
+
expect(form.submit).not.toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
});
- it('submits form with correct username', (done) => {
+ it('submits form with correct username', done => {
const { form, input, submitButton } = findElements();
spyOn(form, 'submit');
input.value = username;
@@ -120,6 +123,7 @@ describe('DeleteAccountModal component', () => {
expect(vm.enteredUsername).toBe(input.value);
expect(submitButton).not.toHaveAttr('disabled', 'disabled');
submitButton.click();
+
expect(form.submit).toHaveBeenCalled();
})
.then(done)
diff --git a/spec/javascripts/profile/account/components/update_username_spec.js b/spec/javascripts/profile/account/components/update_username_spec.js
index 5311499fb73..cc07a5f6e43 100644
--- a/spec/javascripts/profile/account/components/update_username_spec.js
+++ b/spec/javascripts/profile/account/components/update_username_spec.js
@@ -113,6 +113,7 @@ describe('UpdateUsername component', () => {
Vue.nextTick()
.then(() => {
confirmModalBtn.click();
+
expect(axios.put).toHaveBeenCalledWith(actionUrl, { user: { username: newUsername } });
})
.then(done)
@@ -131,8 +132,7 @@ describe('UpdateUsername component', () => {
vm.newUsername = newUsername;
- vm
- .onConfirm()
+ vm.onConfirm()
.then(() => {
expect(vm.username).toBe(newUsername);
expect(vm.newUsername).toBe(newUsername);
@@ -157,8 +157,7 @@ describe('UpdateUsername component', () => {
const invalidUsername = 'anything.git';
vm.newUsername = invalidUsername;
- vm
- .onConfirm()
+ vm.onConfirm()
.then(() => done.fail('Expected onConfirm to throw!'))
.catch(() => {
expect(vm.username).toBe(username);
diff --git a/spec/javascripts/project_select_combo_button_spec.js b/spec/javascripts/project_select_combo_button_spec.js
index 1b65f767f96..109a5000f5d 100644
--- a/spec/javascripts/project_select_combo_button_spec.js
+++ b/spec/javascripts/project_select_combo_button_spec.js
@@ -3,10 +3,10 @@ import ProjectSelectComboButton from '~/project_select_combo_button';
const fixturePath = 'static/project_select_combo_button.html.raw';
-describe('Project Select Combo Button', function () {
+describe('Project Select Combo Button', function() {
preloadFixtures(fixturePath);
- beforeEach(function () {
+ beforeEach(function() {
this.defaults = {
label: 'Select project to create issue',
groupId: 12345,
@@ -28,43 +28,44 @@ describe('Project Select Combo Button', function () {
this.projectSelectInput = document.querySelector('.project-item-select');
});
- describe('on page load when localStorage is empty', function () {
- beforeEach(function () {
+ describe('on page load when localStorage is empty', function() {
+ beforeEach(function() {
this.comboButton = new ProjectSelectComboButton(this.projectSelectInput);
});
- it('newItemBtn href is null', function () {
+ it('newItemBtn href is null', function() {
expect(this.newItemBtn.getAttribute('href')).toBe('');
});
- it('newItemBtn text is the plain default label', function () {
+ it('newItemBtn text is the plain default label', function() {
expect(this.newItemBtn.textContent).toBe(this.defaults.label);
});
});
- describe('on page load when localStorage is filled', function () {
- beforeEach(function () {
- window.localStorage
- .setItem(this.defaults.localStorageKey, JSON.stringify(this.defaults.projectMeta));
+ describe('on page load when localStorage is filled', function() {
+ beforeEach(function() {
+ window.localStorage.setItem(
+ this.defaults.localStorageKey,
+ JSON.stringify(this.defaults.projectMeta),
+ );
this.comboButton = new ProjectSelectComboButton(this.projectSelectInput);
});
- it('newItemBtn href is correctly set', function () {
+ it('newItemBtn href is correctly set', function() {
expect(this.newItemBtn.getAttribute('href')).toBe(this.defaults.projectMeta.url);
});
- it('newItemBtn text is the cached label', function () {
- expect(this.newItemBtn.textContent)
- .toBe(`New issue in ${this.defaults.projectMeta.name}`);
+ it('newItemBtn text is the cached label', function() {
+ expect(this.newItemBtn.textContent).toBe(`New issue in ${this.defaults.projectMeta.name}`);
});
- afterEach(function () {
+ afterEach(function() {
window.localStorage.clear();
});
});
- describe('after selecting a new project', function () {
- beforeEach(function () {
+ describe('after selecting a new project', function() {
+ beforeEach(function() {
this.comboButton = new ProjectSelectComboButton(this.projectSelectInput);
// mock the effect of selecting an item from the projects dropdown (select2)
@@ -73,23 +74,21 @@ describe('Project Select Combo Button', function () {
.trigger('change');
});
- it('newItemBtn href is correctly set', function () {
- expect(this.newItemBtn.getAttribute('href'))
- .toBe('http://myothercoolproject.com/issues/new');
+ it('newItemBtn href is correctly set', function() {
+ expect(this.newItemBtn.getAttribute('href')).toBe('http://myothercoolproject.com/issues/new');
});
- it('newItemBtn text is the selected project label', function () {
- expect(this.newItemBtn.textContent)
- .toBe(`New issue in ${this.defaults.newProjectMeta.name}`);
+ it('newItemBtn text is the selected project label', function() {
+ expect(this.newItemBtn.textContent).toBe(`New issue in ${this.defaults.newProjectMeta.name}`);
});
- afterEach(function () {
+ afterEach(function() {
window.localStorage.clear();
});
});
- describe('deriveTextVariants', function () {
- beforeEach(function () {
+ describe('deriveTextVariants', function() {
+ beforeEach(function() {
this.mockExecutionContext = {
resourceType: '',
resourceLabel: '',
@@ -100,7 +99,7 @@ describe('Project Select Combo Button', function () {
this.method = this.comboButton.deriveTextVariants.bind(this.mockExecutionContext);
});
- it('correctly derives test variants for merge requests', function () {
+ it('correctly derives test variants for merge requests', function() {
this.mockExecutionContext.resourceType = 'merge_requests';
this.mockExecutionContext.resourceLabel = 'New merge request';
@@ -111,7 +110,7 @@ describe('Project Select Combo Button', function () {
expect(returnedVariants.presetTextSuffix).toBe('merge request');
});
- it('correctly derives text variants for issues', function () {
+ it('correctly derives text variants for issues', function() {
this.mockExecutionContext.resourceType = 'issues';
this.mockExecutionContext.resourceLabel = 'New issue';
@@ -123,4 +122,3 @@ describe('Project Select Combo Button', function () {
});
});
});
-
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js
index 21805ef0b28..fdecb823cd2 100644
--- a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js
@@ -90,14 +90,20 @@ describe('GkeMachineTypeDropdown', () => {
expect(vm.$el.querySelector('input').value).toBe('');
vm.$store.commit(SET_MACHINE_TYPES, gapiMachineTypesResponseMock.items);
- return vm.$nextTick().then(() => {
- vm.$el.querySelector('.dropdown-content button').click();
-
- return vm.$nextTick().then(() => {
- expect(vm.$el.querySelector('input').value).toBe(selectedMachineTypeMock);
- done();
- });
- });
+ return vm
+ .$nextTick()
+ .then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedMachineTypeMock);
+ done();
+ })
+ .catch(done.fail);
+ })
+ .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js
index d4fcb2dc8ff..030662b4d90 100644
--- a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js
@@ -48,45 +48,61 @@ describe('GkeProjectIdDropdown', () => {
it('returns project billing validation text', () => {
vm.setIsValidatingProjectBilling(true);
+
expect(vm.toggleText).toBe(LABELS.VALIDATING_PROJECT_BILLING);
});
it('returns default toggle text', done =>
- vm.$nextTick().then(() => {
- vm.setItem(emptyProjectMock);
+ vm
+ .$nextTick()
+ .then(() => {
+ vm.setItem(emptyProjectMock);
- expect(vm.toggleText).toBe(LABELS.DEFAULT);
- done();
- }));
+ expect(vm.toggleText).toBe(LABELS.DEFAULT);
+ done();
+ })
+ .catch(done.fail));
it('returns project name if project selected', done =>
- vm.$nextTick().then(() => {
- expect(vm.toggleText).toBe(selectedProjectMock.name);
- done();
- }));
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.toggleText).toBe(selectedProjectMock.name);
+ done();
+ })
+ .catch(done.fail));
it('returns empty toggle text', done =>
- vm.$nextTick().then(() => {
- vm.$store.commit(SET_PROJECTS, null);
- vm.setItem(emptyProjectMock);
+ vm
+ .$nextTick()
+ .then(() => {
+ vm.$store.commit(SET_PROJECTS, null);
+ vm.setItem(emptyProjectMock);
- expect(vm.toggleText).toBe(LABELS.EMPTY);
- done();
- }));
+ expect(vm.toggleText).toBe(LABELS.EMPTY);
+ done();
+ })
+ .catch(done.fail));
});
describe('selectItem', () => {
it('reflects new value when dropdown item is clicked', done => {
expect(vm.$el.querySelector('input').value).toBe('');
- return vm.$nextTick().then(() => {
- vm.$el.querySelector('.dropdown-content button').click();
-
- return vm.$nextTick().then(() => {
- expect(vm.$el.querySelector('input').value).toBe(selectedProjectMock.projectId);
- done();
- });
- });
+ return vm
+ .$nextTick()
+ .then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedProjectMock.projectId);
+ done();
+ })
+ .catch(done.fail);
+ })
+ .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js
index 89a4a7ea2ce..95186e19ca1 100644
--- a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js
@@ -75,14 +75,20 @@ describe('GkeZoneDropdown', () => {
expect(vm.$el.querySelector('input').value).toBe('');
vm.$store.commit(SET_ZONES, gapiZonesResponseMock.items);
- return vm.$nextTick().then(() => {
- vm.$el.querySelector('.dropdown-content button').click();
-
- return vm.$nextTick().then(() => {
- expect(vm.$el.querySelector('input').value).toBe(selectedZoneMock);
- done();
- });
- });
+ return vm
+ .$nextTick()
+ .then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedZoneMock);
+ done();
+ })
+ .catch(done.fail);
+ })
+ .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js
index 84515d2bf97..b61e0ac872f 100644
--- a/spec/javascripts/projects/project_new_spec.js
+++ b/spec/javascripts/projects/project_new_spec.js
@@ -4,12 +4,14 @@ import projectNew from '~/projects/project_new';
describe('New Project', () => {
let $projectImportUrl;
let $projectPath;
+ let $projectName;
beforeEach(() => {
setFixtures(`
<div class='toggle-import-form'>
<div class='import-url-data'>
<input id="project_import_url" />
+ <input id="project_name" />
<input id="project_path" />
</div>
</div>
@@ -17,6 +19,7 @@ describe('New Project', () => {
$projectImportUrl = $('#project_import_url');
$projectPath = $('#project_path');
+ $projectName = $('#project_name');
});
describe('deriveProjectPathFromUrl', () => {
@@ -24,7 +27,10 @@ describe('New Project', () => {
beforeEach(() => {
projectNew.bindEvents();
- $projectPath.val('').keyup().val(dummyImportUrl);
+ $projectPath
+ .val('')
+ .keyup()
+ .val(dummyImportUrl);
});
it('does not change project path for disabled $projectImportUrl', () => {
@@ -129,4 +135,31 @@ describe('New Project', () => {
});
});
});
+
+ describe('deriveSlugFromProjectName', () => {
+ beforeEach(() => {
+ projectNew.bindEvents();
+ $projectName.val('').keyup();
+ });
+
+ it('converts project name to lower case and dash-limited slug', () => {
+ const dummyProjectName = 'My Awesome Project';
+
+ $projectName.val(dummyProjectName);
+
+ projectNew.onProjectNameChange($projectName, $projectPath);
+
+ expect($projectPath.val()).toEqual('my-awesome-project');
+ });
+
+ it('does not add additional dashes in the slug if the project name already contains dashes', () => {
+ const dummyProjectName = 'My-Dash-Delimited Awesome Project';
+
+ $projectName.val(dummyProjectName);
+
+ projectNew.onProjectNameChange($projectName, $projectPath);
+
+ expect($projectPath.val()).toEqual('my-dash-delimited-awesome-project');
+ });
+ });
});
diff --git a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js
index 955ec6a531c..94e2f959d46 100644
--- a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js
+++ b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js
@@ -85,9 +85,17 @@ describe('PrometheusMetrics', () => {
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy();
expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeFalsy();
- expect(prometheusMetrics.$monitoredMetricsCount.text()).toEqual('3 exporters with 12 metrics were found');
+ expect(prometheusMetrics.$monitoredMetricsCount.text()).toEqual(
+ '3 exporters with 12 metrics were found',
+ );
+
expect($metricsListLi.length).toEqual(metrics.length);
- expect($metricsListLi.first().find('.badge').text()).toEqual(`${metrics[0].active_metrics}`);
+ expect(
+ $metricsListLi
+ .first()
+ .find('.badge')
+ .text(),
+ ).toEqual(`${metrics[0].active_metrics}`);
});
it('should show missing environment variables list', () => {
@@ -129,7 +137,7 @@ describe('PrometheusMetrics', () => {
mock.restore();
});
- it('should show loader animation while response is being loaded and hide it when request is complete', (done) => {
+ it('should show loader animation while response is being loaded and hide it when request is complete', done => {
mockSuccess();
prometheusMetrics.loadActiveMetrics();
@@ -143,7 +151,7 @@ describe('PrometheusMetrics', () => {
});
});
- it('should show empty state if response failed to load', (done) => {
+ it('should show empty state if response failed to load', done => {
mockError();
prometheusMetrics.loadActiveMetrics();
@@ -155,7 +163,7 @@ describe('PrometheusMetrics', () => {
});
});
- it('should populate metrics list once response is loaded', (done) => {
+ it('should populate metrics list once response is loaded', done => {
spyOn(prometheusMetrics, 'populateActiveMetrics');
mockSuccess();
diff --git a/spec/javascripts/raven/raven_config_spec.js b/spec/javascripts/raven/raven_config_spec.js
index c82658b9262..5cc59cc28d3 100644
--- a/spec/javascripts/raven/raven_config_spec.js
+++ b/spec/javascripts/raven/raven_config_spec.js
@@ -133,7 +133,7 @@ describe('RavenConfig', () => {
RavenConfig.setUser.call(ravenConfig);
});
- it('should call .setUserContext', function () {
+ it('should call .setUserContext', function() {
expect(Raven.setUserContext).toHaveBeenCalledWith({
id: ravenConfig.options.currentUserId,
});
diff --git a/spec/javascripts/read_more_spec.js b/spec/javascripts/read_more_spec.js
new file mode 100644
index 00000000000..b1af0f80a50
--- /dev/null
+++ b/spec/javascripts/read_more_spec.js
@@ -0,0 +1,23 @@
+import initReadMore from '~/read_more';
+
+describe('Read more click-to-expand functionality', () => {
+ const fixtureName = 'projects/overview.html.raw';
+
+ preloadFixtures(fixtureName);
+
+ beforeEach(() => {
+ loadFixtures(fixtureName);
+ });
+
+ describe('expands target element', () => {
+ it('adds "is-expanded" class to target element', () => {
+ const target = document.querySelector('.read-more-container');
+ const trigger = document.querySelector('.js-read-more-trigger');
+ initReadMore();
+
+ trigger.click();
+
+ expect(target.classList.contains('is-expanded')).toEqual(true);
+ });
+ });
+});
diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js
index cf1d0625397..92ff960277a 100644
--- a/spec/javascripts/registry/components/app_spec.js
+++ b/spec/javascripts/registry/components/app_spec.js
@@ -18,9 +18,11 @@ describe('Registry List', () => {
describe('with data', () => {
const interceptor = (request, next) => {
- next(request.respondWith(JSON.stringify(reposServerResponse), {
- status: 200,
- }));
+ next(
+ request.respondWith(JSON.stringify(reposServerResponse), {
+ status: 200,
+ }),
+ );
};
beforeEach(() => {
@@ -32,21 +34,21 @@ describe('Registry List', () => {
Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
});
- it('should render a list of repos', (done) => {
+ it('should render a list of repos', done => {
setTimeout(() => {
expect(vm.$store.state.repos.length).toEqual(reposServerResponse.length);
Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.container-image').length,
- ).toEqual(reposServerResponse.length);
+ expect(vm.$el.querySelectorAll('.container-image').length).toEqual(
+ reposServerResponse.length,
+ );
done();
});
}, 0);
});
describe('delete repository', () => {
- it('should be possible to delete a repo', (done) => {
+ it('should be possible to delete a repo', done => {
setTimeout(() => {
Vue.nextTick(() => {
expect(vm.$el.querySelector('.container-image-head .js-remove-repo')).toBeDefined();
@@ -57,12 +59,14 @@ describe('Registry List', () => {
});
describe('toggle repository', () => {
- it('should open the container', (done) => {
+ it('should open the container', done => {
setTimeout(() => {
Vue.nextTick(() => {
vm.$el.querySelector('.js-toggle-repo').click();
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.js-toggle-repo i').className).toEqual('fa fa-chevron-up');
+ expect(vm.$el.querySelector('.js-toggle-repo i').className).toEqual(
+ 'fa fa-chevron-up',
+ );
done();
});
});
@@ -73,9 +77,11 @@ describe('Registry List', () => {
describe('without data', () => {
const interceptor = (request, next) => {
- next(request.respondWith(JSON.stringify([]), {
- status: 200,
- }));
+ next(
+ request.respondWith(JSON.stringify([]), {
+ status: 200,
+ }),
+ );
};
beforeEach(() => {
@@ -87,11 +93,16 @@ describe('Registry List', () => {
Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
});
- it('should render empty message', (done) => {
+ it('should render empty message', done => {
setTimeout(() => {
expect(
- vm.$el.querySelector('p').textContent.trim().replace(/[\r\n]+/g, ' '),
- ).toEqual('No container images stored for this project. Add one by following the instructions above.');
+ vm.$el
+ .querySelector('p')
+ .textContent.trim()
+ .replace(/[\r\n]+/g, ' '),
+ ).toEqual(
+ 'No container images stored for this project. Add one by following the instructions above.',
+ );
done();
}, 0);
});
@@ -99,9 +110,11 @@ describe('Registry List', () => {
describe('while loading data', () => {
const interceptor = (request, next) => {
- next(request.respondWith(JSON.stringify(reposServerResponse), {
- status: 200,
- }));
+ next(
+ request.respondWith(JSON.stringify(reposServerResponse), {
+ status: 200,
+ }),
+ );
};
beforeEach(() => {
@@ -113,7 +126,7 @@ describe('Registry List', () => {
Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
});
- it('should render a loading spinner', (done) => {
+ it('should render a loading spinner', done => {
Vue.nextTick(() => {
expect(vm.$el.querySelector('.fa-spinner')).not.toBe(null);
done();
diff --git a/spec/javascripts/registry/components/collapsible_container_spec.js b/spec/javascripts/registry/components/collapsible_container_spec.js
index 5891921318a..256a242f784 100644
--- a/spec/javascripts/registry/components/collapsible_container_spec.js
+++ b/spec/javascripts/registry/components/collapsible_container_spec.js
@@ -24,26 +24,32 @@ describe('collapsible registry container', () => {
describe('toggle', () => {
it('should be closed by default', () => {
expect(vm.$el.querySelector('.container-image-tags')).toBe(null);
- expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-right');
+ expect(vm.$el.querySelector('.container-image-head i').className).toEqual(
+ 'fa fa-chevron-right',
+ );
});
- it('should be open when user clicks on closed repo', (done) => {
+ it('should be open when user clicks on closed repo', done => {
vm.$el.querySelector('.js-toggle-repo').click();
Vue.nextTick(() => {
expect(vm.$el.querySelector('.container-image-tags')).toBeDefined();
- expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-up');
+ expect(vm.$el.querySelector('.container-image-head i').className).toEqual(
+ 'fa fa-chevron-up',
+ );
done();
});
});
- it('should be closed when the user clicks on an opened repo', (done) => {
+ it('should be closed when the user clicks on an opened repo', done => {
vm.$el.querySelector('.js-toggle-repo').click();
Vue.nextTick(() => {
vm.$el.querySelector('.js-toggle-repo').click();
Vue.nextTick(() => {
expect(vm.$el.querySelector('.container-image-tags')).toBe(null);
- expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-right');
+ expect(vm.$el.querySelector('.container-image-head i').className).toEqual(
+ 'fa fa-chevron-right',
+ );
done();
});
});
diff --git a/spec/javascripts/registry/components/table_registry_spec.js b/spec/javascripts/registry/components/table_registry_spec.js
index 6aa61afc445..7f5252a7d6c 100644
--- a/spec/javascripts/registry/components/table_registry_spec.js
+++ b/spec/javascripts/registry/components/table_registry_spec.js
@@ -22,13 +22,15 @@ describe('table registry', () => {
});
it('should render a table with the registry list', () => {
- expect(
- vm.$el.querySelectorAll('table tbody tr').length,
- ).toEqual(repoPropsData.list.length);
+ expect(vm.$el.querySelectorAll('table tbody tr').length).toEqual(repoPropsData.list.length);
});
it('should render registry tag', () => {
- const textRendered = vm.$el.querySelector('.table tbody tr').textContent.trim().replace(/\s\s+/g, ' ');
+ const textRendered = vm.$el
+ .querySelector('.table tbody tr')
+ .textContent.trim()
+ .replace(/\s\s+/g, ' ');
+
expect(textRendered).toContain(repoPropsData.list[0].tag);
expect(textRendered).toContain(repoPropsData.list[0].shortRevision);
expect(textRendered).toContain(repoPropsData.list[0].layers);
@@ -36,9 +38,7 @@ describe('table registry', () => {
});
it('should be possible to delete a registry', () => {
- expect(
- vm.$el.querySelector('.table tbody tr .js-delete-registry'),
- ).toBeDefined();
+ expect(vm.$el.querySelector('.table tbody tr .js-delete-registry')).toBeDefined();
});
describe('pagination', () => {
diff --git a/spec/javascripts/registry/getters_spec.js b/spec/javascripts/registry/getters_spec.js
index 3d989541881..839aa718997 100644
--- a/spec/javascripts/registry/getters_spec.js
+++ b/spec/javascripts/registry/getters_spec.js
@@ -7,25 +7,28 @@ describe('Getters Registry Store', () => {
state = {
isLoading: false,
endpoint: '/root/empty-project/container_registry.json',
- repos: [{
- canDelete: true,
- destroyPath: 'bar',
- id: '134',
- isLoading: false,
- list: [],
- location: 'foo',
- name: 'gitlab-org/omnibus-gitlab/foo',
- tagsPath: 'foo',
- }, {
- canDelete: true,
- destroyPath: 'bar',
- id: '123',
- isLoading: false,
- list: [],
- location: 'foo',
- name: 'gitlab-org/omnibus-gitlab',
- tagsPath: 'foo',
- }],
+ repos: [
+ {
+ canDelete: true,
+ destroyPath: 'bar',
+ id: '134',
+ isLoading: false,
+ list: [],
+ location: 'foo',
+ name: 'gitlab-org/omnibus-gitlab/foo',
+ tagsPath: 'foo',
+ },
+ {
+ canDelete: true,
+ destroyPath: 'bar',
+ id: '123',
+ isLoading: false,
+ list: [],
+ location: 'foo',
+ name: 'gitlab-org/omnibus-gitlab',
+ tagsPath: 'foo',
+ },
+ ],
};
});
diff --git a/spec/javascripts/registry/mock_data.js b/spec/javascripts/registry/mock_data.js
index 6bffb47be55..22db203e77f 100644
--- a/spec/javascripts/registry/mock_data.js
+++ b/spec/javascripts/registry/mock_data.js
@@ -40,7 +40,8 @@ export const registryServerResponse = [
layers: 19,
location: 'location',
created_at: 1505828744434,
- }];
+ },
+];
export const parsedReposServerResponse = [
{
diff --git a/spec/javascripts/registry/stores/mutations_spec.js b/spec/javascripts/registry/stores/mutations_spec.js
index 2e4c0659daa..e19fe7a27cf 100644
--- a/spec/javascripts/registry/stores/mutations_spec.js
+++ b/spec/javascripts/registry/stores/mutations_spec.js
@@ -18,6 +18,7 @@ describe('Mutations Registry Store', () => {
it('should set the main endpoint', () => {
const expectedState = Object.assign({}, mockState, { endpoint: 'foo' });
mutations[types.SET_MAIN_ENDPOINT](mockState, 'foo');
+
expect(mockState).toEqual(expectedState);
});
});
@@ -25,6 +26,7 @@ describe('Mutations Registry Store', () => {
describe('SET_REPOS_LIST', () => {
it('should set a parsed repository list', () => {
mutations[types.SET_REPOS_LIST](mockState, reposServerResponse);
+
expect(mockState.repos).toEqual(parsedReposServerResponse);
});
});
@@ -32,6 +34,7 @@ describe('Mutations Registry Store', () => {
describe('TOGGLE_MAIN_LOADING', () => {
it('should set a parsed repository list', () => {
mutations[types.TOGGLE_MAIN_LOADING](mockState);
+
expect(mockState.isLoading).toEqual(true);
});
});
@@ -75,6 +78,7 @@ describe('Mutations Registry Store', () => {
});
mutations[types.TOGGLE_REGISTRY_LIST_LOADING](mockState, mockState.repos[0]);
+
expect(mockState.repos[0].isLoading).toEqual(true);
});
});
diff --git a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
index 333cefe5f8a..69767d9cf1c 100644
--- a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
+++ b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
@@ -42,6 +42,7 @@ describe('Grouped Test Reports App', () => {
expect(vm.$el.textContent).toContain(
'rspec:pg found no changed test results out of 8 total tests',
);
+
expect(vm.$el.textContent).toContain(
'java ant found no changed test results out of 3 total tests',
);
@@ -88,6 +89,7 @@ describe('Grouped Test Reports App', () => {
expect(vm.$el.textContent).toContain(
'rspec:pg found 2 failed test results out of 8 total tests',
);
+
expect(vm.$el.textContent).toContain('New');
expect(vm.$el.textContent).toContain(
'java ant found no changed test results out of 3 total tests',
@@ -115,6 +117,7 @@ describe('Grouped Test Reports App', () => {
expect(vm.$el.textContent).toContain(
'rspec:pg found 1 failed test result and 2 fixed test results out of 8 total tests',
);
+
expect(vm.$el.textContent).toContain('New');
expect(vm.$el.textContent).toContain(
' java ant found 1 failed test result out of 3 total tests',
@@ -148,10 +151,11 @@ describe('Grouped Test Reports App', () => {
it('renders resolved failures', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.js-mr-code-resolved-issues').textContent).toContain(
+ expect(vm.$el.querySelector('.report-block-container').textContent).toContain(
resolvedFailures.suites[0].resolved_failures[0].name,
);
- expect(vm.$el.querySelector('.js-mr-code-resolved-issues').textContent).toContain(
+
+ expect(vm.$el.querySelector('.report-block-container').textContent).toContain(
resolvedFailures.suites[0].resolved_failures[1].name,
);
done();
diff --git a/spec/javascripts/reports/components/modal_spec.js b/spec/javascripts/reports/components/modal_spec.js
index 3a567c40eca..6b8471381de 100644
--- a/spec/javascripts/reports/components/modal_spec.js
+++ b/spec/javascripts/reports/components/modal_spec.js
@@ -27,12 +27,19 @@ describe('Grouped Test Reports Modal', () => {
});
it('renders code block', () => {
- expect(vm.$el.querySelector('code').textContent).toEqual(modalDataStructure.system_output.value);
+ expect(vm.$el.querySelector('code').textContent).toEqual(
+ modalDataStructure.system_output.value,
+ );
});
it('renders link', () => {
- expect(vm.$el.querySelector('.js-modal-link').getAttribute('href')).toEqual(modalDataStructure.class.value);
- expect(trimText(vm.$el.querySelector('.js-modal-link').textContent)).toEqual(modalDataStructure.class.value);
+ expect(vm.$el.querySelector('.js-modal-link').getAttribute('href')).toEqual(
+ modalDataStructure.class.value,
+ );
+
+ expect(trimText(vm.$el.querySelector('.js-modal-link').textContent)).toEqual(
+ modalDataStructure.class.value,
+ );
});
it('renders miliseconds', () => {
@@ -40,6 +47,8 @@ describe('Grouped Test Reports Modal', () => {
});
it('render title', () => {
- expect(trimText(vm.$el.querySelector('.modal-title').textContent)).toEqual('Test#sum when a is 1 and b is 2 returns summary');
+ expect(trimText(vm.$el.querySelector('.modal-title').textContent)).toEqual(
+ 'Test#sum when a is 1 and b is 2 returns summary',
+ );
});
});
diff --git a/spec/javascripts/reports/components/report_link_spec.js b/spec/javascripts/reports/components/report_link_spec.js
index cd6911e2f59..f879899e9c5 100644
--- a/spec/javascripts/reports/components/report_link_spec.js
+++ b/spec/javascripts/reports/components/report_link_spec.js
@@ -45,8 +45,7 @@ describe('report link', () => {
vm = mountComponent(Component, {
issue: {
path: 'Gemfile.lock',
- urlPath:
- 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
+ urlPath: 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
line: 22,
},
});
@@ -60,8 +59,7 @@ describe('report link', () => {
vm = mountComponent(Component, {
issue: {
path: 'Gemfile.lock',
- urlPath:
- 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
+ urlPath: 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
},
});
diff --git a/spec/javascripts/reports/components/report_section_spec.js b/spec/javascripts/reports/components/report_section_spec.js
index 6f6eb161d14..b02af8baaec 100644
--- a/spec/javascripts/reports/components/report_section_spec.js
+++ b/spec/javascripts/reports/components/report_section_spec.js
@@ -86,6 +86,7 @@ describe('Report section', () => {
});
});
});
+
describe('when it is loading', () => {
it('should render loading indicator', () => {
vm = mountComponent(ReportSection, {
@@ -96,6 +97,7 @@ describe('Report section', () => {
successText: 'Code quality improved on 1 point and degraded on 1 point',
hasIssues: false,
});
+
expect(vm.$el.textContent.trim()).toEqual('Loading codeclimate report');
});
});
@@ -118,7 +120,7 @@ describe('Report section', () => {
'Code quality improved on 1 point and degraded on 1 point',
);
- expect(vm.$el.querySelectorAll('.js-mr-code-resolved-issues li').length).toEqual(
+ expect(vm.$el.querySelectorAll('.report-block-container li').length).toEqual(
resolvedIssues.length,
);
});
@@ -168,6 +170,7 @@ describe('Report section', () => {
successText: 'Code quality improved on 1 point and degraded on 1 point',
hasIssues: false,
});
+
expect(vm.$el.textContent.trim()).toEqual('Failed to load codeclimate report');
});
});
diff --git a/spec/javascripts/reports/components/test_issue_body_spec.js b/spec/javascripts/reports/components/test_issue_body_spec.js
index 0ea81f714e7..32baf904ad7 100644
--- a/spec/javascripts/reports/components/test_issue_body_spec.js
+++ b/spec/javascripts/reports/components/test_issue_body_spec.js
@@ -29,6 +29,7 @@ describe('Test Issue body', () => {
spyOn(vm, 'openModal');
vm.$el.querySelector('button').click();
+
expect(vm.openModal).toHaveBeenCalledWith({
issue: commonProps.issue,
});
diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js
index 7d19b16efb9..9446cd454ab 100644
--- a/spec/javascripts/reports/store/mutations_spec.js
+++ b/spec/javascripts/reports/store/mutations_spec.js
@@ -13,6 +13,7 @@ describe('Reports Store Mutations', () => {
describe('SET_ENDPOINT', () => {
it('should set endpoint', () => {
mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json');
+
expect(stateCopy.endpoint).toEqual('endpoint.json');
});
});
@@ -20,6 +21,7 @@ describe('Reports Store Mutations', () => {
describe('REQUEST_REPORTS', () => {
it('should set isLoading to true', () => {
mutations[types.REQUEST_REPORTS](stateCopy);
+
expect(stateCopy.isLoading).toEqual(true);
});
});
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 6d49536a712..992e17978c1 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -1,124 +1,87 @@
-/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-return-assign, vars-on-top, max-len */
-
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import '~/commons/bootstrap';
import axios from '~/lib/utils/axios_utils';
import Sidebar from '~/right_sidebar';
-(function() {
- var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState;
-
- $aside = null;
-
- $toggle = null;
-
- $icon = null;
-
- $page = null;
-
- $labelsIcon = null;
-
- assertSidebarState = function(state) {
- var shouldBeCollapsed, shouldBeExpanded;
- shouldBeExpanded = state === 'expanded';
- shouldBeCollapsed = state === 'collapsed';
- expect($aside.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded);
- expect($page.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded);
- expect($icon.hasClass('fa-angle-double-right')).toBe(shouldBeExpanded);
- expect($aside.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed);
- expect($page.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed);
- return expect($icon.hasClass('fa-angle-double-left')).toBe(shouldBeCollapsed);
- };
-
- describe('RightSidebar', function() {
- describe('fixture tests', () => {
- var fixtureName = 'issues/open-issue.html.raw';
- preloadFixtures(fixtureName);
- loadJSONFixtures('todos/todos.json');
- let mock;
-
- beforeEach(function() {
- loadFixtures(fixtureName);
- mock = new MockAdapter(axios);
- new Sidebar(); // eslint-disable-line no-new
- $aside = $('.right-sidebar');
- $page = $('.layout-page');
- $icon = $aside.find('i');
- $toggle = $aside.find('.js-sidebar-toggle');
- return $labelsIcon = $aside.find('.sidebar-collapsed-icon');
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('should expand/collapse the sidebar when arrow is clicked', function() {
- assertSidebarState('expanded');
- $toggle.click();
- assertSidebarState('collapsed');
- $toggle.click();
- assertSidebarState('expanded');
- });
- it('should float over the page and when sidebar icons clicked', function() {
- $labelsIcon.click();
- return assertSidebarState('expanded');
- });
- it('should collapse when the icon arrow clicked while it is floating on page', function() {
- $labelsIcon.click();
- assertSidebarState('expanded');
- $toggle.click();
- return assertSidebarState('collapsed');
- });
-
- it('should broadcast todo:toggle event when add todo clicked', function(done) {
- var todos = getJSONFixture('todos/todos.json');
- mock.onPost(/(.*)\/todos$/).reply(200, todos);
-
- var todoToggleSpy = spyOnEvent(document, 'todo:toggle');
-
- $('.issuable-sidebar-header .js-issuable-todo').click();
+let $aside = null;
+let $toggle = null;
+let $icon = null;
+let $page = null;
+let $labelsIcon = null;
+
+const assertSidebarState = function(state) {
+ const shouldBeExpanded = state === 'expanded';
+ const shouldBeCollapsed = state === 'collapsed';
+ expect($aside.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded);
+ expect($page.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded);
+ expect($icon.hasClass('fa-angle-double-right')).toBe(shouldBeExpanded);
+ expect($aside.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed);
+ expect($page.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed);
+ expect($icon.hasClass('fa-angle-double-left')).toBe(shouldBeCollapsed);
+};
+
+describe('RightSidebar', function() {
+ describe('fixture tests', () => {
+ const fixtureName = 'issues/open-issue.html.raw';
+ preloadFixtures(fixtureName);
+ loadJSONFixtures('todos/todos.json');
+ let mock;
+
+ beforeEach(function() {
+ loadFixtures(fixtureName);
+ mock = new MockAdapter(axios);
+ new Sidebar(); // eslint-disable-line no-new
+ $aside = $('.right-sidebar');
+ $page = $('.layout-page');
+ $icon = $aside.find('i');
+ $toggle = $aside.find('.js-sidebar-toggle');
+ $labelsIcon = $aside.find('.sidebar-collapsed-icon');
+ });
- setTimeout(() => {
- expect(todoToggleSpy.calls.count()).toEqual(1);
+ afterEach(() => {
+ mock.restore();
+ });
- done();
- });
- });
+ it('should expand/collapse the sidebar when arrow is clicked', function() {
+ assertSidebarState('expanded');
+ $toggle.click();
+ assertSidebarState('collapsed');
+ $toggle.click();
+ assertSidebarState('expanded');
+ });
- it('should not hide collapsed icons', () => {
- [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => {
- expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy();
- });
- });
+ it('should float over the page and when sidebar icons clicked', function() {
+ $labelsIcon.click();
+ assertSidebarState('expanded');
});
- describe('sidebarToggleClicked', () => {
- const event = jasmine.createSpyObj('event', ['preventDefault']);
+ it('should collapse when the icon arrow clicked while it is floating on page', function() {
+ $labelsIcon.click();
+ assertSidebarState('expanded');
+ $toggle.click();
+ assertSidebarState('collapsed');
+ });
- beforeEach(() => {
- spyOn($.fn, 'hasClass').and.returnValue(false);
- });
+ it('should broadcast todo:toggle event when add todo clicked', function(done) {
+ const todos = getJSONFixture('todos/todos.json');
+ mock.onPost(/(.*)\/todos$/).reply(200, todos);
- afterEach(() => {
- gl.lazyLoader = undefined;
- });
+ const todoToggleSpy = spyOnEvent(document, 'todo:toggle');
- it('calls loadCheck if lazyLoader is set', () => {
- gl.lazyLoader = jasmine.createSpyObj('lazyLoader', ['loadCheck']);
+ $('.issuable-sidebar-header .js-issuable-todo').click();
- Sidebar.prototype.sidebarToggleClicked(event);
+ setTimeout(() => {
+ expect(todoToggleSpy.calls.count()).toEqual(1);
- expect(gl.lazyLoader.loadCheck).toHaveBeenCalled();
+ done();
});
+ });
- it('does not throw if lazyLoader is not defined', () => {
- gl.lazyLoader = undefined;
-
- const toggle = Sidebar.prototype.sidebarToggleClicked.bind(null, event);
-
- expect(toggle).not.toThrow();
+ it('should not hide collapsed icons', () => {
+ [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), el => {
+ expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy();
});
});
});
-}).call(window);
+});
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index 86c001678c5..7a4ca587313 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -1,8 +1,8 @@
-/* eslint-disable max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, object-shorthand, prefer-template, vars-on-top, max-len */
+/* eslint-disable no-var, one-var, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, object-shorthand, vars-on-top */
import $ from 'jquery';
import '~/gl_dropdown';
-import SearchAutocomplete from '~/search_autocomplete';
+import initSearchAutocomplete from '~/search_autocomplete';
import '~/lib/utils/common_utils';
describe('Search autocomplete dropdown', () => {
@@ -109,15 +109,17 @@ describe('Search autocomplete dropdown', () => {
assertLinks = function(list, issuesPath, mrsPath) {
if (issuesPath) {
- const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_id=${userId}"]`;
- const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_id=${userId}"]`;
+ const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_username=${userName}"]`;
+ const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_username=${userName}"]`;
+
expect(list.find(issuesAssignedToMeLink).length).toBe(1);
expect(list.find(issuesAssignedToMeLink).text()).toBe('Issues assigned to me');
expect(list.find(issuesIHaveCreatedLink).length).toBe(1);
expect(list.find(issuesIHaveCreatedLink).text()).toBe("Issues I've created");
}
- const mrsAssignedToMeLink = `a[href="${mrsPath}/?assignee_id=${userId}"]`;
- const mrsIHaveCreatedLink = `a[href="${mrsPath}/?author_id=${userId}"]`;
+ const mrsAssignedToMeLink = `a[href="${mrsPath}/?assignee_username=${userName}"]`;
+ const mrsIHaveCreatedLink = `a[href="${mrsPath}/?author_username=${userName}"]`;
+
expect(list.find(mrsAssignedToMeLink).length).toBe(1);
expect(list.find(mrsAssignedToMeLink).text()).toBe('Merge requests assigned to me');
expect(list.find(mrsIHaveCreatedLink).length).toBe(1);
@@ -132,7 +134,7 @@ describe('Search autocomplete dropdown', () => {
window.gon.current_user_id = userId;
window.gon.current_username = userName;
- return (widget = new SearchAutocomplete());
+ return (widget = initSearchAutocomplete());
});
afterEach(function() {
@@ -140,6 +142,7 @@ describe('Search autocomplete dropdown', () => {
removeBodyAttributes();
window.gon = {};
});
+
it('should show Dashboard specific dropdown menu', function() {
var list;
addBodyAttributes();
@@ -148,6 +151,7 @@ describe('Search autocomplete dropdown', () => {
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, dashboardIssuesPath, dashboardMRsPath);
});
+
it('should show Group specific dropdown menu', function() {
var list;
addBodyAttributes('group');
@@ -156,6 +160,7 @@ describe('Search autocomplete dropdown', () => {
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, groupIssuesPath, groupMRsPath);
});
+
it('should show Project specific dropdown menu', function() {
var list;
addBodyAttributes('project');
@@ -164,6 +169,7 @@ describe('Search autocomplete dropdown', () => {
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, projectIssuesPath, projectMRsPath);
});
+
it('should show only Project mergeRequest dropdown menu items when project issues are disabled', function() {
addBodyAttributes('project');
disableProjectIssues();
@@ -172,6 +178,7 @@ describe('Search autocomplete dropdown', () => {
const list = widget.wrap.find('.dropdown-menu').find('ul');
assertLinks(list, null, projectMRsPath);
});
+
it('should not show category related menu if there is text in the input', function() {
var link, list;
addBodyAttributes('project');
@@ -179,9 +186,11 @@ describe('Search autocomplete dropdown', () => {
widget.searchInput.val('help');
widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
- link = "a[href='" + projectIssuesPath + '/?assignee_id=' + userId + "']";
- return expect(list.find(link).length).toBe(0);
+ link = `a[href='${projectIssuesPath}/?assignee_username=${userName}']`;
+
+ expect(list.find(link).length).toBe(0);
});
+
it('should not submit the search form when selecting an autocomplete row with the keyboard', function() {
var ENTER = 13;
var DOWN = 40;
diff --git a/spec/javascripts/search_spec.js b/spec/javascripts/search_spec.js
index 522851c584b..40bdbac7451 100644
--- a/spec/javascripts/search_spec.js
+++ b/spec/javascripts/search_spec.js
@@ -5,7 +5,7 @@ import Search from '~/pages/search/show/search';
describe('Search', () => {
const fixturePath = 'search/show.html.raw';
const searchTerm = 'some search';
- const fillDropdownInput = (dropdownSelector) => {
+ const fillDropdownInput = dropdownSelector => {
const dropdownElement = document.querySelector(dropdownSelector).parentNode;
const inputElement = dropdownElement.querySelector('.dropdown-input-field');
inputElement.value = searchTerm;
@@ -19,8 +19,8 @@ describe('Search', () => {
new Search(); // eslint-disable-line no-new
});
- it('requests groups from backend when filtering', (done) => {
- spyOn(Api, 'groups').and.callFake((term) => {
+ it('requests groups from backend when filtering', done => {
+ spyOn(Api, 'groups').and.callFake(term => {
expect(term).toBe(searchTerm);
done();
});
@@ -29,8 +29,8 @@ describe('Search', () => {
$(inputElement).trigger('input');
});
- it('requests projects from backend when filtering', (done) => {
- spyOn(Api, 'projects').and.callFake((term) => {
+ it('requests projects from backend when filtering', done => {
+ spyOn(Api, 'projects').and.callFake(term => {
expect(term).toBe(searchTerm);
done();
});
diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js
index c1a69bd7018..3b681a9ff28 100644
--- a/spec/javascripts/settings_panels_spec.js
+++ b/spec/javascripts/settings_panels_spec.js
@@ -1,10 +1,11 @@
+import $ from 'jquery';
import initSettingsPanels from '~/settings_panels';
describe('Settings Panels', () => {
- preloadFixtures('projects/ci_cd_settings.html.raw');
+ preloadFixtures('groups/edit.html.raw');
beforeEach(() => {
- loadFixtures('projects/ci_cd_settings.html.raw');
+ loadFixtures('groups/edit.html.raw');
});
describe('initSettingsPane', () => {
@@ -13,17 +14,32 @@ describe('Settings Panels', () => {
});
it('should expand linked hash fragment panel', () => {
- window.location.hash = '#autodevops-settings';
+ window.location.hash = '#js-general-settings';
- const pipelineSettingsPanel = document.querySelector('#autodevops-settings');
+ const panel = document.querySelector('#js-general-settings');
// Our test environment automatically expands everything so we need to clear that out first
- pipelineSettingsPanel.classList.remove('expanded');
+ panel.classList.remove('expanded');
- expect(pipelineSettingsPanel.classList.contains('expanded')).toBe(false);
+ expect(panel.classList.contains('expanded')).toBe(false);
initSettingsPanels();
- expect(pipelineSettingsPanel.classList.contains('expanded')).toBe(true);
+ expect(panel.classList.contains('expanded')).toBe(true);
});
});
+
+ it('does not change the text content of triggers', () => {
+ const panel = document.querySelector('#js-general-settings');
+ const trigger = panel.querySelector('.js-settings-toggle-trigger-only');
+ const originalText = trigger.textContent;
+
+ initSettingsPanels();
+
+ expect(panel.classList.contains('expanded')).toBe(true);
+
+ $(trigger).click();
+
+ expect(panel.classList.contains('expanded')).toBe(false);
+ expect(trigger.textContent).toEqual(originalText);
+ });
});
diff --git a/spec/javascripts/shared/popover_spec.js b/spec/javascripts/shared/popover_spec.js
index 1d574c9424b..85bde075b77 100644
--- a/spec/javascripts/shared/popover_spec.js
+++ b/spec/javascripts/shared/popover_spec.js
@@ -1,9 +1,5 @@
import $ from 'jquery';
-import {
- togglePopover,
- mouseleave,
- mouseenter,
-} from '~/shared/popover';
+import { togglePopover, mouseleave, mouseenter } from '~/shared/popover';
describe('popover', () => {
describe('togglePopover', () => {
@@ -26,14 +22,14 @@ describe('popover', () => {
expect(togglePopover.call(context, true)).toEqual(false);
});
- it('shows popover', (done) => {
+ it('shows popover', done => {
const context = {
hasClass: () => false,
popover: () => {},
toggleClass: () => {},
};
- spyOn(context, 'popover').and.callFake((method) => {
+ spyOn(context, 'popover').and.callFake(method => {
expect(method).toEqual('show');
done();
});
@@ -41,7 +37,7 @@ describe('popover', () => {
togglePopover.call(context, true);
});
- it('adds disable-animation and js-popover-show class', (done) => {
+ it('adds disable-animation and js-popover-show class', done => {
const context = {
hasClass: () => false,
popover: () => {},
@@ -77,14 +73,14 @@ describe('popover', () => {
expect(togglePopover.call(context, false)).toEqual(false);
});
- it('hides popover', (done) => {
+ it('hides popover', done => {
const context = {
hasClass: () => true,
popover: () => {},
toggleClass: () => {},
};
- spyOn(context, 'popover').and.callFake((method) => {
+ spyOn(context, 'popover').and.callFake(method => {
expect(method).toEqual('hide');
done();
});
@@ -92,7 +88,7 @@ describe('popover', () => {
togglePopover.call(context, false);
});
- it('removes disable-animation and js-popover-show class', (done) => {
+ it('removes disable-animation and js-popover-show class', done => {
const context = {
hasClass: () => true,
popover: () => {},
@@ -116,9 +112,12 @@ describe('popover', () => {
length: 0,
};
- spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn));
+ spyOn($.fn, 'init').and.callFake(
+ selector => (selector === '.popover:hover' ? fakeJquery : $.fn),
+ );
spyOn(togglePopover, 'call');
mouseleave();
+
expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), false);
});
@@ -127,9 +126,12 @@ describe('popover', () => {
length: 1,
};
- spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn));
+ spyOn($.fn, 'init').and.callFake(
+ selector => (selector === '.popover:hover' ? fakeJquery : $.fn),
+ );
spyOn(togglePopover, 'call');
mouseleave();
+
expect(togglePopover.call).not.toHaveBeenCalledWith(false);
});
});
@@ -140,12 +142,13 @@ describe('popover', () => {
it('shows popover', () => {
spyOn(togglePopover, 'call').and.returnValue(false);
mouseenter.call(context);
+
expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), true);
});
- it('registers mouseleave event if popover is showed', (done) => {
+ it('registers mouseleave event if popover is showed', done => {
spyOn(togglePopover, 'call').and.returnValue(true);
- spyOn($.fn, 'on').and.callFake((eventName) => {
+ spyOn($.fn, 'on').and.callFake(eventName => {
expect(eventName).toEqual('mouseleave');
done();
});
@@ -156,6 +159,7 @@ describe('popover', () => {
spyOn(togglePopover, 'call').and.returnValue(false);
const spy = spyOn($.fn, 'on').and.callFake(() => {});
mouseenter.call(context);
+
expect(spy).not.toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/shortcuts_dashboard_navigation_spec.js b/spec/javascripts/shortcuts_dashboard_navigation_spec.js
deleted file mode 100644
index 7cb201e01d8..00000000000
--- a/spec/javascripts/shortcuts_dashboard_navigation_spec.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import findAndFollowLink from '~/shortcuts_dashboard_navigation';
-
-describe('findAndFollowLink', () => {
- it('visits a link when the selector exists', () => {
- const href = '/some/path';
- const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl');
-
- setFixtures(`<a class="my-shortcut" href="${href}">link</a>`);
-
- findAndFollowLink('.my-shortcut');
-
- expect(visitUrl).toHaveBeenCalledWith(href);
- });
-
- it('does not throw an exception when the selector does not exist', () => {
- const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl');
-
- // this should not throw an exception
- findAndFollowLink('.this-selector-does-not-exist');
-
- expect(visitUrl).not.toHaveBeenCalled();
- });
-});
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
deleted file mode 100644
index a4753ab7cde..00000000000
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ /dev/null
@@ -1,106 +0,0 @@
-import $ from 'jquery';
-import initCopyAsGFM from '~/behaviors/markdown/copy_as_gfm';
-import ShortcutsIssuable from '~/shortcuts_issuable';
-
-initCopyAsGFM();
-
-const FORM_SELECTOR = '.js-main-target-form .js-vue-comment-form';
-
-describe('ShortcutsIssuable', function() {
- const fixtureName = 'snippets/show.html.raw';
- preloadFixtures(fixtureName);
-
- beforeEach(() => {
- loadFixtures(fixtureName);
- $('body').append(
- `<div class="js-main-target-form">
- <textare class="js-vue-comment-form"></textare>
- </div>`,
- );
- document.querySelector('.js-new-note-form').classList.add('js-main-target-form');
- this.shortcut = new ShortcutsIssuable(true);
- });
-
- afterEach(() => {
- $(FORM_SELECTOR).remove();
- });
-
- describe('replyWithSelectedText', () => {
- // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML.
- const stubSelection = html => {
- window.gl.utils.getSelectedFragment = () => {
- const node = document.createElement('div');
- node.innerHTML = html;
-
- return node;
- };
- };
- describe('with empty selection', () => {
- it('does not return an error', () => {
- ShortcutsIssuable.replyWithSelectedText(true);
-
- expect($(FORM_SELECTOR).val()).toBe('');
- });
-
- it('triggers `focus`', () => {
- const spy = spyOn(document.querySelector(FORM_SELECTOR), 'focus');
- ShortcutsIssuable.replyWithSelectedText(true);
-
- expect(spy).toHaveBeenCalled();
- });
- });
-
- describe('with any selection', () => {
- beforeEach(() => {
- stubSelection('<p>Selected text.</p>');
- });
-
- it('leaves existing input intact', () => {
- $(FORM_SELECTOR).val('This text was already here.');
- expect($(FORM_SELECTOR).val()).toBe('This text was already here.');
-
- ShortcutsIssuable.replyWithSelectedText(true);
- expect($(FORM_SELECTOR).val()).toBe('This text was already here.\n\n> Selected text.\n\n');
- });
-
- it('triggers `input`', () => {
- let triggered = false;
- $(FORM_SELECTOR).on('input', () => {
- triggered = true;
- });
-
- ShortcutsIssuable.replyWithSelectedText(true);
- expect(triggered).toBe(true);
- });
-
- it('triggers `focus`', () => {
- const spy = spyOn(document.querySelector(FORM_SELECTOR), 'focus');
- ShortcutsIssuable.replyWithSelectedText(true);
-
- expect(spy).toHaveBeenCalled();
- });
- });
-
- describe('with a one-line selection', () => {
- it('quotes the selection', () => {
- stubSelection('<p>This text has been selected.</p>');
- ShortcutsIssuable.replyWithSelectedText(true);
-
- expect($(FORM_SELECTOR).val()).toBe('> This text has been selected.\n\n');
- });
- });
-
- describe('with a multi-line selection', () => {
- it('quotes the selected lines as a group', () => {
- stubSelection(
- '<p>Selected line one.</p>\n<p>Selected line two.</p>\n<p>Selected line three.</p>',
- );
- ShortcutsIssuable.replyWithSelectedText(true);
-
- expect($(FORM_SELECTOR).val()).toBe(
- '> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n',
- );
- });
- });
- });
-});
diff --git a/spec/javascripts/shortcuts_spec.js b/spec/javascripts/shortcuts_spec.js
index 94cded7ee37..3ca6ecaa938 100644
--- a/spec/javascripts/shortcuts_spec.js
+++ b/spec/javascripts/shortcuts_spec.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import Shortcuts from '~/shortcuts';
+import Shortcuts from '~/behaviors/shortcuts/shortcuts';
describe('Shortcuts', () => {
const fixtureName = 'snippets/show.html.raw';
diff --git a/spec/javascripts/sidebar/assignees_spec.js b/spec/javascripts/sidebar/assignees_spec.js
index 843e7002180..eced4925489 100644
--- a/spec/javascripts/sidebar/assignees_spec.js
+++ b/spec/javascripts/sidebar/assignees_spec.js
@@ -22,6 +22,7 @@ describe('Assignee component', () => {
}).$mount();
const collapsed = component.$el.querySelector('.sidebar-collapsed-icon');
+
expect(collapsed.childElementCount).toEqual(1);
expect(collapsed.children[0].getAttribute('aria-label')).toEqual('No Assignee');
expect(collapsed.children[0].classList.contains('fa')).toEqual(true);
@@ -67,6 +68,7 @@ describe('Assignee component', () => {
spyOn(component, '$emit');
component.$el.querySelector('.assign-yourself .btn-link').click();
+
expect(component.$emit).toHaveBeenCalledWith('assign-self');
});
});
@@ -76,18 +78,20 @@ describe('Assignee component', () => {
component = new AssigneeComponent({
propsData: {
rootPath: 'http://localhost:3000',
- users: [
- UsersMock.user,
- ],
+ users: [UsersMock.user],
editable: false,
},
}).$mount();
const collapsed = component.$el.querySelector('.sidebar-collapsed-icon');
const assignee = collapsed.children[0];
+
expect(collapsed.childElementCount).toEqual(1);
expect(assignee.querySelector('.avatar').getAttribute('src')).toEqual(UsersMock.user.avatar);
- expect(assignee.querySelector('.avatar').getAttribute('alt')).toEqual(`${UsersMock.user.name}'s avatar`);
+ expect(assignee.querySelector('.avatar').getAttribute('alt')).toEqual(
+ `${UsersMock.user.name}'s avatar`,
+ );
+
expect(assignee.querySelector('.author').innerText.trim()).toEqual(UsersMock.user.name);
});
@@ -95,34 +99,38 @@ describe('Assignee component', () => {
component = new AssigneeComponent({
propsData: {
rootPath: 'http://localhost:3000/',
- users: [
- UsersMock.user,
- ],
+ users: [UsersMock.user],
editable: true,
},
}).$mount();
expect(component.$el.querySelector('.author-link')).not.toBeNull();
// The image
- expect(component.$el.querySelector('.author-link img').getAttribute('src')).toEqual(UsersMock.user.avatar);
+ expect(component.$el.querySelector('.author-link img').getAttribute('src')).toEqual(
+ UsersMock.user.avatar,
+ );
// Author name
- expect(component.$el.querySelector('.author-link .author').innerText.trim()).toEqual(UsersMock.user.name);
+ expect(component.$el.querySelector('.author-link .author').innerText.trim()).toEqual(
+ UsersMock.user.name,
+ );
// Username
- expect(component.$el.querySelector('.author-link .username').innerText.trim()).toEqual(`@${UsersMock.user.username}`);
+ expect(component.$el.querySelector('.author-link .username').innerText.trim()).toEqual(
+ `@${UsersMock.user.username}`,
+ );
});
it('has the root url present in the assigneeUrl method', () => {
component = new AssigneeComponent({
propsData: {
rootPath: 'http://localhost:3000/',
- users: [
- UsersMock.user,
- ],
+ users: [UsersMock.user],
editable: true,
},
}).$mount();
- expect(component.assigneeUrl(UsersMock.user).indexOf('http://localhost:3000/')).not.toEqual(-1);
+ expect(component.assigneeUrl(UsersMock.user).indexOf('http://localhost:3000/')).not.toEqual(
+ -1,
+ );
});
});
@@ -138,16 +146,25 @@ describe('Assignee component', () => {
}).$mount();
const collapsed = component.$el.querySelector('.sidebar-collapsed-icon');
+
expect(collapsed.childElementCount).toEqual(2);
const first = collapsed.children[0];
+
expect(first.querySelector('.avatar').getAttribute('src')).toEqual(users[0].avatar);
- expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[0].name}'s avatar`);
+ expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(
+ `${users[0].name}'s avatar`,
+ );
+
expect(first.querySelector('.author').innerText.trim()).toEqual(users[0].name);
const second = collapsed.children[1];
+
expect(second.querySelector('.avatar').getAttribute('src')).toEqual(users[1].avatar);
- expect(second.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[1].name}'s avatar`);
+ expect(second.querySelector('.avatar').getAttribute('alt')).toEqual(
+ `${users[1].name}'s avatar`,
+ );
+
expect(second.querySelector('.author').innerText.trim()).toEqual(users[1].name);
});
@@ -162,14 +179,20 @@ describe('Assignee component', () => {
}).$mount();
const collapsed = component.$el.querySelector('.sidebar-collapsed-icon');
+
expect(collapsed.childElementCount).toEqual(2);
const first = collapsed.children[0];
+
expect(first.querySelector('.avatar').getAttribute('src')).toEqual(users[0].avatar);
- expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[0].name}'s avatar`);
+ expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(
+ `${users[0].name}'s avatar`,
+ );
+
expect(first.querySelector('.author').innerText.trim()).toEqual(users[0].name);
const second = collapsed.children[1];
+
expect(second.querySelector('.avatar-counter').innerText.trim()).toEqual('+2');
});
@@ -187,7 +210,7 @@ describe('Assignee component', () => {
expect(component.$el.querySelector('.user-list-more')).toBe(null);
});
- it('Shows the "show-less" assignees label', (done) => {
+ it('Shows the "show-less" assignees label', done => {
const users = UsersMockHelper.createNumberRandomUsers(6);
component = new AssigneeComponent({
propsData: {
@@ -197,20 +220,26 @@ describe('Assignee component', () => {
},
}).$mount();
- expect(component.$el.querySelectorAll('.user-item').length).toEqual(component.defaultRenderCount);
+ expect(component.$el.querySelectorAll('.user-item').length).toEqual(
+ component.defaultRenderCount,
+ );
+
expect(component.$el.querySelector('.user-list-more')).not.toBe(null);
const usersLabelExpectation = users.length - component.defaultRenderCount;
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .not.toBe(`+${usersLabelExpectation} more`);
+
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).not.toBe(
+ `+${usersLabelExpectation} more`,
+ );
component.toggleShowLess();
Vue.nextTick(() => {
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .toBe('- show less');
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).toBe(
+ '- show less',
+ );
done();
});
});
- it('Shows the "show-less" when "n+ more " label is clicked', (done) => {
+ it('Shows the "show-less" when "n+ more " label is clicked', done => {
const users = UsersMockHelper.createNumberRandomUsers(6);
component = new AssigneeComponent({
propsData: {
@@ -222,8 +251,9 @@ describe('Assignee component', () => {
component.$el.querySelector('.user-list-more .btn-link').click();
Vue.nextTick(() => {
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .toBe('- show less');
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).toBe(
+ '- show less',
+ );
done();
});
});
@@ -254,16 +284,18 @@ describe('Assignee component', () => {
});
it('shows "+1 more" label', () => {
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .toBe('+ 1 more');
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).toBe(
+ '+ 1 more',
+ );
});
- it('shows "show less" label', (done) => {
+ it('shows "show less" label', done => {
component.toggleShowLess();
Vue.nextTick(() => {
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .toBe('- show less');
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).toBe(
+ '- show less',
+ );
done();
});
});
diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
index 9dff52a9d49..4c3dd713589 100644
--- a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
+++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
@@ -8,7 +8,12 @@ describe('Issuable Time Tracker', () => {
let initialData;
let vm;
- const initTimeTrackingComponent = opts => {
+ const initTimeTrackingComponent = ({
+ timeEstimate,
+ timeSpent,
+ timeEstimateHumanReadable,
+ timeSpentHumanReadable,
+ }) => {
setFixtures(`
<div>
<div id="mock-container"></div>
@@ -16,10 +21,10 @@ describe('Issuable Time Tracker', () => {
`);
initialData = {
- time_estimate: opts.timeEstimate,
- time_spent: opts.timeSpent,
- human_time_estimate: opts.timeEstimateHumanReadable,
- human_time_spent: opts.timeSpentHumanReadable,
+ timeEstimate,
+ timeSpent,
+ humanTimeEstimate: timeEstimateHumanReadable,
+ humanTimeSpent: timeSpentHumanReadable,
rootPath: '/',
};
@@ -43,8 +48,8 @@ describe('Issuable Time Tracker', () => {
describe('Initialization', () => {
beforeEach(() => {
initTimeTrackingComponent({
- timeEstimate: 100000,
- timeSpent: 5000,
+ timeEstimate: 10000, // 2h 46m
+ timeSpent: 5000, // 1h 23m
timeEstimateHumanReadable: '2h 46m',
timeSpentHumanReadable: '1h 23m',
});
@@ -56,14 +61,14 @@ describe('Issuable Time Tracker', () => {
it('should correctly set timeEstimate', done => {
Vue.nextTick(() => {
- expect(vm.timeEstimate).toBe(initialData.time_estimate);
+ expect(vm.timeEstimate).toBe(initialData.timeEstimate);
done();
});
});
it('should correctly set time_spent', done => {
Vue.nextTick(() => {
- expect(vm.timeSpent).toBe(initialData.time_spent);
+ expect(vm.timeSpent).toBe(initialData.timeSpent);
done();
});
});
@@ -74,8 +79,8 @@ describe('Issuable Time Tracker', () => {
describe('Comparison pane', () => {
beforeEach(() => {
initTimeTrackingComponent({
- timeEstimate: 100000,
- timeSpent: 5000,
+ timeEstimate: 100000, // 1d 3h
+ timeSpent: 5000, // 1h 23m
timeEstimateHumanReadable: '',
timeSpentHumanReadable: '',
});
@@ -85,6 +90,7 @@ describe('Issuable Time Tracker', () => {
Vue.nextTick(() => {
expect(vm.showComparisonState).toBe(true);
const $comparisonPane = vm.$el.querySelector('.time-tracking-comparison-pane');
+
expect($comparisonPane).toBeVisible();
done();
});
@@ -93,23 +99,29 @@ describe('Issuable Time Tracker', () => {
describe('Remaining meter', () => {
it('should display the remaining meter with the correct width', done => {
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[value="5"]')).not.toBeNull();
+ expect(
+ vm.$el.querySelector('.time-tracking-comparison-pane .progress[value="5"]'),
+ ).not.toBeNull();
done();
});
});
it('should display the remaining meter with the correct background color when within estimate', done => {
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="primary"]')).not.toBeNull();
+ expect(
+ vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="primary"]'),
+ ).not.toBeNull();
done();
});
});
it('should display the remaining meter with the correct background color when over estimate', done => {
- vm.time_estimate = 100000;
- vm.time_spent = 20000000;
+ vm.timeEstimate = 10000; // 2h 46m
+ vm.timeSpent = 20000000; // 231 days
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]')).not.toBeNull();
+ expect(
+ vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]'),
+ ).not.toBeNull();
done();
});
});
@@ -119,7 +131,7 @@ describe('Issuable Time Tracker', () => {
describe('Estimate only pane', () => {
beforeEach(() => {
initTimeTrackingComponent({
- timeEstimate: 100000,
+ timeEstimate: 10000, // 2h 46m
timeSpent: 0,
timeEstimateHumanReadable: '2h 46m',
timeSpentHumanReadable: '',
@@ -142,7 +154,7 @@ describe('Issuable Time Tracker', () => {
beforeEach(() => {
initTimeTrackingComponent({
timeEstimate: 0,
- timeSpent: 5000,
+ timeSpent: 5000, // 1h 23m
timeEstimateHumanReadable: '2h 46m',
timeSpentHumanReadable: '1h 23m',
});
diff --git a/spec/javascripts/sidebar/confidential_edit_buttons_spec.js b/spec/javascripts/sidebar/confidential_edit_buttons_spec.js
index 482be466aad..32da9f83112 100644
--- a/spec/javascripts/sidebar/confidential_edit_buttons_spec.js
+++ b/spec/javascripts/sidebar/confidential_edit_buttons_spec.js
@@ -7,8 +7,8 @@ describe('Edit Form Buttons', () => {
beforeEach(() => {
const Component = Vue.extend(editFormButtons);
- const toggleForm = () => { };
- const updateConfidentialAttribute = () => { };
+ const toggleForm = () => {};
+ const updateConfidentialAttribute = () => {};
vm1 = new Component({
propsData: {
@@ -28,12 +28,8 @@ describe('Edit Form Buttons', () => {
});
it('renders on or off text based on confidentiality', () => {
- expect(
- vm1.$el.innerHTML.includes('Turn Off'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('Turn Off')).toBe(true);
- expect(
- vm2.$el.innerHTML.includes('Turn On'),
- ).toBe(true);
+ expect(vm2.$el.innerHTML.includes('Turn On')).toBe(true);
});
});
diff --git a/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js b/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js
index 724f5126945..369088cb258 100644
--- a/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js
+++ b/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js
@@ -7,8 +7,8 @@ describe('Edit Form Dropdown', () => {
beforeEach(() => {
const Component = Vue.extend(editForm);
- const toggleForm = () => { };
- const updateConfidentialAttribute = () => { };
+ const toggleForm = () => {};
+ const updateConfidentialAttribute = () => {};
vm1 = new Component({
propsData: {
@@ -28,12 +28,8 @@ describe('Edit Form Dropdown', () => {
});
it('renders on the appropriate warning text', () => {
- expect(
- vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.')).toBe(true);
- expect(
- vm2.$el.innerHTML.includes('You are going to turn on the confidentiality.'),
- ).toBe(true);
+ expect(vm2.$el.innerHTML.includes('You are going to turn on the confidentiality.')).toBe(true);
});
});
diff --git a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js
index 6110d5d89ac..486a7241e33 100644
--- a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js
+++ b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js
@@ -29,20 +29,14 @@ describe('Confidential Issue Sidebar Block', () => {
});
it('shows if confidential and/or editable', () => {
- expect(
- vm1.$el.innerHTML.includes('Edit'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('Edit')).toBe(true);
- expect(
- vm1.$el.innerHTML.includes('This issue is confidential'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('This issue is confidential')).toBe(true);
- expect(
- vm2.$el.innerHTML.includes('Not confidential'),
- ).toBe(true);
+ expect(vm2.$el.innerHTML.includes('Not confidential')).toBe(true);
});
- it('displays the edit form when editable', (done) => {
+ it('displays the edit form when editable', done => {
expect(vm1.edit).toBe(false);
vm1.$el.querySelector('.confidential-edit').click();
@@ -50,17 +44,15 @@ describe('Confidential Issue Sidebar Block', () => {
expect(vm1.edit).toBe(true);
setTimeout(() => {
- expect(
- vm1.$el
- .innerHTML
- .includes('You are going to turn off the confidentiality.'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.')).toBe(
+ true,
+ );
done();
});
});
- it('displays the edit form when opened from collapsed state', (done) => {
+ it('displays the edit form when opened from collapsed state', done => {
expect(vm1.edit).toBe(false);
vm1.$el.querySelector('.sidebar-collapsed-icon').click();
@@ -68,11 +60,9 @@ describe('Confidential Issue Sidebar Block', () => {
expect(vm1.edit).toBe(true);
setTimeout(() => {
- expect(
- vm1.$el
- .innerHTML
- .includes('You are going to turn off the confidentiality.'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.')).toBe(
+ true,
+ );
done();
});
diff --git a/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js b/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js
index deeea669de8..330f59f08b2 100644
--- a/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js
+++ b/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js
@@ -8,8 +8,8 @@ describe('EditFormButtons', () => {
beforeEach(() => {
const Component = Vue.extend(editFormButtons);
- const toggleForm = () => { };
- const updateLockedAttribute = () => { };
+ const toggleForm = () => {};
+ const updateLockedAttribute = () => {};
vm1 = mountComponent(Component, {
isLocked: true,
@@ -25,12 +25,8 @@ describe('EditFormButtons', () => {
});
it('renders unlock or lock text based on locked state', () => {
- expect(
- vm1.$el.innerHTML.includes('Unlock'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('Unlock')).toBe(true);
- expect(
- vm2.$el.innerHTML.includes('Lock'),
- ).toBe(true);
+ expect(vm2.$el.innerHTML.includes('Lock')).toBe(true);
});
});
diff --git a/spec/javascripts/sidebar/lock/edit_form_spec.js b/spec/javascripts/sidebar/lock/edit_form_spec.js
index 7abd6997a18..ec10a999a40 100644
--- a/spec/javascripts/sidebar/lock/edit_form_spec.js
+++ b/spec/javascripts/sidebar/lock/edit_form_spec.js
@@ -7,8 +7,8 @@ describe('EditForm', () => {
beforeEach(() => {
const Component = Vue.extend(editForm);
- const toggleForm = () => { };
- const updateLockedAttribute = () => { };
+ const toggleForm = () => {};
+ const updateLockedAttribute = () => {};
vm1 = new Component({
propsData: {
@@ -30,12 +30,8 @@ describe('EditForm', () => {
});
it('renders on the appropriate warning text', () => {
- expect(
- vm1.$el.innerHTML.includes('Unlock this issue?'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true);
- expect(
- vm2.$el.innerHTML.includes('Lock this merge request?'),
- ).toBe(true);
+ expect(vm2.$el.innerHTML.includes('Lock this merge request?')).toBe(true);
});
});
diff --git a/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js b/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js
index 9abc3daf221..ca882032bdf 100644
--- a/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js
+++ b/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js
@@ -38,20 +38,14 @@ describe('LockIssueSidebar', () => {
});
it('shows if locked and/or editable', () => {
- expect(
- vm1.$el.innerHTML.includes('Edit'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('Edit')).toBe(true);
- expect(
- vm1.$el.innerHTML.includes('Locked'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('Locked')).toBe(true);
- expect(
- vm2.$el.innerHTML.includes('Unlocked'),
- ).toBe(true);
+ expect(vm2.$el.innerHTML.includes('Unlocked')).toBe(true);
});
- it('displays the edit form when editable', (done) => {
+ it('displays the edit form when editable', done => {
expect(vm1.isLockDialogOpen).toBe(false);
vm1.$el.querySelector('.lock-edit').click();
@@ -59,17 +53,13 @@ describe('LockIssueSidebar', () => {
expect(vm1.isLockDialogOpen).toBe(true);
vm1.$nextTick(() => {
- expect(
- vm1.$el
- .innerHTML
- .includes('Unlock this issue?'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true);
done();
});
});
- it('displays the edit form when opened from collapsed state', (done) => {
+ it('displays the edit form when opened from collapsed state', done => {
expect(vm1.isLockDialogOpen).toBe(false);
vm1.$el.querySelector('.sidebar-collapsed-icon').click();
@@ -77,11 +67,7 @@ describe('LockIssueSidebar', () => {
expect(vm1.isLockDialogOpen).toBe(true);
setTimeout(() => {
- expect(
- vm1.$el
- .innerHTML
- .includes('Unlock this issue?'),
- ).toBe(true);
+ expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true);
done();
});
diff --git a/spec/javascripts/sidebar/participants_spec.js b/spec/javascripts/sidebar/participants_spec.js
index e796ddee62f..eb360fd256a 100644
--- a/spec/javascripts/sidebar/participants_spec.js
+++ b/spec/javascripts/sidebar/participants_spec.js
@@ -11,13 +11,9 @@ const PARTICIPANT = {
avatar_url: 'gravatar.com/avatar/xxx',
};
-const PARTICIPANT_LIST = [
- PARTICIPANT,
- { ...PARTICIPANT, id: 2 },
- { ...PARTICIPANT, id: 3 },
-];
+const PARTICIPANT_LIST = [PARTICIPANT, { ...PARTICIPANT, id: 2 }, { ...PARTICIPANT, id: 3 }];
-describe('Participants', function () {
+describe('Participants', function() {
let vm;
let Participants;
@@ -69,7 +65,7 @@ describe('Participants', function () {
expect(vm.$el.querySelector('.js-participants-expanded-loading-icon')).toBeDefined();
});
- it('when only showing visible participants, shows an avatar only for each participant under the limit', (done) => {
+ it('when only showing visible participants, shows an avatar only for each participant under the limit', done => {
const numberOfLessParticipants = 2;
vm = mountComponent(Participants, {
loading: false,
@@ -88,7 +84,7 @@ describe('Participants', function () {
.catch(done.fail);
});
- it('when only showing all participants, each has an avatar', (done) => {
+ it('when only showing all participants, each has an avatar', done => {
const numberOfLessParticipants = 2;
vm = mountComponent(Participants, {
loading: false,
@@ -120,7 +116,7 @@ describe('Participants', function () {
expect(moreParticipantLink).toBeNull();
});
- it('when too many participants, has more participants link to show more', (done) => {
+ it('when too many participants, has more participants link to show more', done => {
vm = mountComponent(Participants, {
loading: false,
participants: PARTICIPANT_LIST,
@@ -138,7 +134,7 @@ describe('Participants', function () {
.catch(done.fail);
});
- it('when too many participants and already showing them, has more participants link to show less', (done) => {
+ it('when too many participants and already showing them, has more participants link to show less', done => {
vm = mountComponent(Participants, {
loading: false,
participants: PARTICIPANT_LIST,
@@ -182,6 +178,7 @@ describe('Participants', function () {
const participantsIconEl = vm.$el.querySelector('.sidebar-collapsed-icon');
participantsIconEl.click();
+
expect(vm.$emit).toHaveBeenCalledWith('toggleSidebar');
});
});
diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js
index ebaaa6e806b..3f0f67d71ca 100644
--- a/spec/javascripts/sidebar/sidebar_assignees_spec.js
+++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js
@@ -24,10 +24,14 @@ describe('sidebar assignees', () => {
const SidebarAssigneeComponent = Vue.extend(SidebarAssignees);
sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees');
- vm = mountComponent(SidebarAssigneeComponent, {
- mediator,
- field: sidebarAssigneesEl.dataset.field,
- }, sidebarAssigneesEl);
+ vm = mountComponent(
+ SidebarAssigneeComponent,
+ {
+ mediator,
+ field: sidebarAssigneesEl.dataset.field,
+ },
+ sidebarAssigneesEl,
+ );
});
afterEach(() => {
@@ -39,6 +43,7 @@ describe('sidebar assignees', () => {
it('calls the mediator when saves the assignees', () => {
vm.saveAssignees();
+
expect(mediator.saveAssignees).toHaveBeenCalled();
});
@@ -49,8 +54,9 @@ describe('sidebar assignees', () => {
expect(mediator.store.assignees.length).toEqual(1);
});
- it('hides assignees until fetched', (done) => {
+ it('hides assignees until fetched', done => {
const currentAssignee = sidebarAssigneesEl.querySelector('.value');
+
expect(currentAssignee).toBe(null);
vm.store.isFetching.assignees = false;
diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js
index da950258a94..2d853970fc4 100644
--- a/spec/javascripts/sidebar/sidebar_mediator_spec.js
+++ b/spec/javascripts/sidebar/sidebar_mediator_spec.js
@@ -25,20 +25,23 @@ describe('Sidebar mediator', function() {
expect(this.mediator.store.assignees[0]).toEqual(Mock.mediator.currentUser);
});
- it('saves assignees', (done) => {
- this.mediator.saveAssignees('issue[assignee_ids]')
- .then((resp) => {
+ it('saves assignees', done => {
+ this.mediator
+ .saveAssignees('issue[assignee_ids]')
+ .then(resp => {
expect(resp.status).toEqual(200);
done();
})
.catch(done.fail);
});
- it('fetches the data', (done) => {
- const mockData = Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'];
+ it('fetches the data', done => {
+ const mockData =
+ Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'];
spyOn(this.mediator, 'processFetchedData').and.callThrough();
- this.mediator.fetch()
+ this.mediator
+ .fetch()
.then(() => {
expect(this.mediator.processFetchedData).toHaveBeenCalledWith(mockData);
})
@@ -47,7 +50,8 @@ describe('Sidebar mediator', function() {
});
it('processes fetched data', () => {
- const mockData = Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'];
+ const mockData =
+ Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'];
this.mediator.processFetchedData(mockData);
expect(this.mediator.store.assignees).toEqual(mockData.assignees);
@@ -68,12 +72,13 @@ describe('Sidebar mediator', function() {
expect(this.mediator.store.setMoveToProjectId).toHaveBeenCalledWith(projectId);
});
- it('fetches autocomplete projects', (done) => {
+ it('fetches autocomplete projects', done => {
const searchTerm = 'foo';
spyOn(this.mediator.service, 'getProjectsAutocomplete').and.callThrough();
spyOn(this.mediator.store, 'setAutocompleteProjects').and.callThrough();
- this.mediator.fetchAutocompleteProjects(searchTerm)
+ this.mediator
+ .fetchAutocompleteProjects(searchTerm)
.then(() => {
expect(this.mediator.service.getProjectsAutocomplete).toHaveBeenCalledWith(searchTerm);
expect(this.mediator.store.setAutocompleteProjects).toHaveBeenCalled();
@@ -82,13 +87,14 @@ describe('Sidebar mediator', function() {
.catch(done.fail);
});
- it('moves issue', (done) => {
+ it('moves issue', done => {
const moveToProjectId = 7;
this.mediator.store.setMoveToProjectId(moveToProjectId);
spyOn(this.mediator.service, 'moveIssue').and.callThrough();
const visitUrl = spyOnDependency(SidebarMediator, 'visitUrl');
- this.mediator.moveIssue()
+ this.mediator
+ .moveIssue()
.then(() => {
expect(this.mediator.service.moveIssue).toHaveBeenCalledWith(moveToProjectId);
expect(visitUrl).toHaveBeenCalledWith('/root/some-project/issues/5');
@@ -97,11 +103,12 @@ describe('Sidebar mediator', function() {
.catch(done.fail);
});
- it('toggle subscription', (done) => {
+ it('toggle subscription', done => {
this.mediator.store.setSubscribedState(false);
spyOn(this.mediator.service, 'toggleSubscription').and.callThrough();
- this.mediator.toggleSubscription()
+ this.mediator
+ .toggleSubscription()
.then(() => {
expect(this.mediator.service.toggleSubscription).toHaveBeenCalled();
expect(this.mediator.store.subscribed).toEqual(true);
diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
index 8f35b9ca437..230e0a933a9 100644
--- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js
+++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
@@ -7,7 +7,7 @@ import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarMoveIssue from '~/sidebar/lib/sidebar_move_issue';
import Mock from './mock_data';
-describe('SidebarMoveIssue', function () {
+describe('SidebarMoveIssue', function() {
beforeEach(() => {
Vue.http.interceptors.push(Mock.sidebarMockInterceptor);
this.mediator = new SidebarMediator(Mock.mediator);
@@ -72,11 +72,13 @@ describe('SidebarMoveIssue', function () {
expect($.fn.glDropdown).toHaveBeenCalled();
});
- it('escapes html from project name', (done) => {
+ it('escapes html from project name', done => {
this.$toggleButton.dropdown('toggle');
setTimeout(() => {
- expect(this.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; foo / bar');
+ expect(this.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual(
+ '&lt;img src=x onerror=alert(document.domain)&gt; foo / bar',
+ );
done();
});
});
@@ -94,7 +96,7 @@ describe('SidebarMoveIssue', function () {
expect(this.$confirmButton.hasClass('is-loading')).toBe(true);
});
- it('should remove loading state from confirm button on failure', (done) => {
+ it('should remove loading state from confirm button on failure', done => {
spyOn(window, 'Flash');
spyOn(this.mediator, 'moveIssue').and.returnValue(Promise.reject());
this.mediator.setMoveToProjectId(7);
@@ -121,7 +123,7 @@ describe('SidebarMoveIssue', function () {
});
});
- it('should set moveToProjectId on dropdown item "No project" click', (done) => {
+ it('should set moveToProjectId on dropdown item "No project" click', done => {
spyOn(this.mediator, 'setMoveToProjectId');
// Open the dropdown
@@ -129,7 +131,10 @@ describe('SidebarMoveIssue', function () {
// Wait for the autocomplete request to finish
setTimeout(() => {
- this.$content.find('.js-move-issue-dropdown-item').eq(0).trigger('click');
+ this.$content
+ .find('.js-move-issue-dropdown-item')
+ .eq(0)
+ .trigger('click');
expect(this.mediator.setMoveToProjectId).toHaveBeenCalledWith(0);
expect(this.$confirmButton.prop('disabled')).toBeTruthy();
@@ -137,7 +142,7 @@ describe('SidebarMoveIssue', function () {
}, 0);
});
- it('should set moveToProjectId on dropdown item click', (done) => {
+ it('should set moveToProjectId on dropdown item click', done => {
spyOn(this.mediator, 'setMoveToProjectId');
// Open the dropdown
@@ -145,7 +150,10 @@ describe('SidebarMoveIssue', function () {
// Wait for the autocomplete request to finish
setTimeout(() => {
- this.$content.find('.js-move-issue-dropdown-item').eq(1).trigger('click');
+ this.$content
+ .find('.js-move-issue-dropdown-item')
+ .eq(1)
+ .trigger('click');
expect(this.mediator.setMoveToProjectId).toHaveBeenCalledWith(20);
expect(this.$confirmButton.attr('disabled')).toBe(undefined);
diff --git a/spec/javascripts/sidebar/sidebar_store_spec.js b/spec/javascripts/sidebar/sidebar_store_spec.js
index 08b112a54ba..85ff70fffbd 100644
--- a/spec/javascripts/sidebar/sidebar_store_spec.js
+++ b/spec/javascripts/sidebar/sidebar_store_spec.js
@@ -25,20 +25,17 @@ const PARTICIPANT = {
avatar_url: 'gravatar.com/avatar/xxx',
};
-const PARTICIPANT_LIST = [
- PARTICIPANT,
- { ...PARTICIPANT, id: 2 },
- { ...PARTICIPANT, id: 3 },
-];
+const PARTICIPANT_LIST = [PARTICIPANT, { ...PARTICIPANT, id: 2 }, { ...PARTICIPANT, id: 3 }];
-describe('Sidebar store', function () {
+describe('Sidebar store', function() {
beforeEach(() => {
this.store = new SidebarStore({
currentUser: {
id: 1,
name: 'Administrator',
username: 'root',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
},
editable: true,
rootPath: '/',
@@ -56,11 +53,13 @@ describe('Sidebar store', function () {
it('adds a new assignee', () => {
this.store.addAssignee(ASSIGNEE);
+
expect(this.store.assignees.length).toEqual(1);
});
it('removes an assignee', () => {
this.store.removeAssignee(ASSIGNEE);
+
expect(this.store.assignees.length).toEqual(0);
});
@@ -69,14 +68,17 @@ describe('Sidebar store', function () {
this.store.addAssignee(ASSIGNEE);
foundAssignee = this.store.findAssignee(ASSIGNEE);
+
expect(foundAssignee).toBeDefined();
expect(foundAssignee).toEqual(ASSIGNEE);
foundAssignee = this.store.findAssignee(ANOTHER_ASSINEE);
+
expect(foundAssignee).toBeUndefined();
});
it('removes all assignees', () => {
this.store.removeAllAssignees();
+
expect(this.store.assignees.length).toEqual(0);
});
@@ -108,6 +110,7 @@ describe('Sidebar store', function () {
};
this.store.setAssigneeData(users);
+
expect(this.store.isFetching.assignees).toBe(false);
expect(this.store.assignees.length).toEqual(3);
});
@@ -128,6 +131,7 @@ describe('Sidebar store', function () {
it('set time tracking data', () => {
this.store.setTimeTrackingData(Mock.time);
+
expect(this.store.timeEstimate).toEqual(Mock.time.time_estimate);
expect(this.store.totalTimeSpent).toEqual(Mock.time.total_time_spent);
expect(this.store.humanTimeEstimate).toEqual(Mock.time.human_time_estimate);
diff --git a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
index 9e437084224..88f64244237 100644
--- a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
+++ b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
@@ -6,13 +6,13 @@ import SidebarStore from '~/sidebar/stores/sidebar_store';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import Mock from './mock_data';
-describe('Sidebar Subscriptions', function () {
+describe('Sidebar Subscriptions', function() {
let vm;
let SidebarSubscriptions;
beforeEach(() => {
SidebarSubscriptions = Vue.extend(sidebarSubscriptions);
- // Setup the stores, services, etc
+ // Set up the stores, services, etc
// eslint-disable-next-line no-new
new SidebarMediator(Mock.mediator);
});
diff --git a/spec/javascripts/sidebar/subscriptions_spec.js b/spec/javascripts/sidebar/subscriptions_spec.js
index f0a53e573c3..32728e58b06 100644
--- a/spec/javascripts/sidebar/subscriptions_spec.js
+++ b/spec/javascripts/sidebar/subscriptions_spec.js
@@ -3,7 +3,7 @@ import subscriptions from '~/sidebar/components/subscriptions/subscriptions.vue'
import eventHub from '~/sidebar/event_hub';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-describe('Subscriptions', function () {
+describe('Subscriptions', function() {
let vm;
let Subscriptions;
@@ -22,7 +22,9 @@ describe('Subscriptions', function () {
});
expect(vm.$refs.toggleButton.isLoading).toBe(true);
- expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass('is-loading');
+ expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass(
+ 'is-loading',
+ );
});
it('is toggled "off" when currently not subscribed', () => {
@@ -30,7 +32,9 @@ describe('Subscriptions', function () {
subscribed: false,
});
- expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).not.toHaveClass('is-checked');
+ expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).not.toHaveClass(
+ 'is-checked',
+ );
});
it('is toggled "on" when currently subscribed', () => {
@@ -38,7 +42,9 @@ describe('Subscriptions', function () {
subscribed: true,
});
- expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass('is-checked');
+ expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass(
+ 'is-checked',
+ );
});
it('toggleSubscription method emits `toggleSubscription` event on eventHub and Component', () => {
@@ -47,6 +53,7 @@ describe('Subscriptions', function () {
spyOn(vm, '$emit');
vm.toggleSubscription();
+
expect(eventHub.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object));
expect(vm.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object));
});
@@ -56,6 +63,7 @@ describe('Subscriptions', function () {
spyOn(vm, '$emit');
vm.onClickCollapsedIcon();
+
expect(vm.$emit).toHaveBeenCalledWith('toggleSidebar');
});
});
diff --git a/spec/javascripts/sidebar/todo_spec.js b/spec/javascripts/sidebar/todo_spec.js
index a929b804a29..657e88ecb96 100644
--- a/spec/javascripts/sidebar/todo_spec.js
+++ b/spec/javascripts/sidebar/todo_spec.js
@@ -42,7 +42,9 @@ describe('SidebarTodo', () => {
vm.collapsed = true;
Vue.nextTick()
.then(() => {
- expect(vm.buttonClasses).toBe('btn-blank btn-todo sidebar-collapsed-icon dont-change-state');
+ expect(vm.buttonClasses).toBe(
+ 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state',
+ );
})
.then(done)
.catch(done.fail);
@@ -103,6 +105,7 @@ describe('SidebarTodo', () => {
it('emits `toggleTodo` event on component', () => {
spyOn(vm, '$emit');
vm.handleButtonClick();
+
expect(vm.$emit).toHaveBeenCalledWith('toggleTodo');
});
});
@@ -118,16 +121,18 @@ describe('SidebarTodo', () => {
container: 'body',
boundary: 'viewport',
};
+
expect(vm.$el.nodeName).toBe('BUTTON');
const elDataAttrs = vm.$el.dataset;
- Object.keys(elDataAttrs).forEach((attr) => {
+ Object.keys(elDataAttrs).forEach(attr => {
expect(elDataAttrs[attr]).toBe(dataAttributes[attr]);
});
});
it('renders button label element when `collapsed` prop is `false`', () => {
const buttonLabelEl = vm.$el.querySelector('span.issuable-todo-inner');
+
expect(buttonLabelEl).not.toBeNull();
expect(buttonLabelEl.innerText.trim()).toBe('Mark todo as done');
});
@@ -137,8 +142,11 @@ describe('SidebarTodo', () => {
Vue.nextTick()
.then(() => {
const buttonIconEl = vm.$el.querySelector('svg');
+
expect(buttonIconEl).not.toBeNull();
- expect(buttonIconEl.querySelector('use').getAttribute('xlink:href')).toContain('todo-done');
+ expect(buttonIconEl.querySelector('use').getAttribute('xlink:href')).toContain(
+ 'todo-done',
+ );
})
.then(done)
.catch(done.fail);
@@ -149,6 +157,7 @@ describe('SidebarTodo', () => {
Vue.nextTick()
.then(() => {
const loadingEl = vm.$el.querySelector('span.loading-container');
+
expect(loadingEl).not.toBeNull();
})
.then(done)
diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js b/spec/javascripts/signin_tabs_memoizer_spec.js
index 9d3905fa1d8..b688a299052 100644
--- a/spec/javascripts/signin_tabs_memoizer_spec.js
+++ b/spec/javascripts/signin_tabs_memoizer_spec.js
@@ -1,164 +1,170 @@
import AccessorUtilities from '~/lib/utils/accessor';
import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer';
-(() => {
- describe('SigninTabsMemoizer', () => {
- const fixtureTemplate = 'static/signin_tabs.html.raw';
- const tabSelector = 'ul.new-session-tabs';
- const currentTabKey = 'current_signin_tab';
- let memo;
-
- function createMemoizer() {
- memo = new SigninTabsMemoizer({
- currentTabKey,
- tabSelector,
- });
- return memo;
- }
+describe('SigninTabsMemoizer', () => {
+ const fixtureTemplate = 'static/signin_tabs.html.raw';
+ const tabSelector = 'ul.new-session-tabs';
+ const currentTabKey = 'current_signin_tab';
+ let memo;
+
+ function createMemoizer() {
+ memo = new SigninTabsMemoizer({
+ currentTabKey,
+ tabSelector,
+ });
+ return memo;
+ }
- preloadFixtures(fixtureTemplate);
+ preloadFixtures(fixtureTemplate);
- beforeEach(() => {
- loadFixtures(fixtureTemplate);
+ beforeEach(() => {
+ loadFixtures(fixtureTemplate);
- spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(true);
- });
+ spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(true);
+ });
- it('does nothing if no tab was previously selected', () => {
- createMemoizer();
+ it('does nothing if no tab was previously selected', () => {
+ createMemoizer();
- expect(document.querySelector(`${tabSelector} > li.active a`).getAttribute('href')).toEqual('#ldap');
- });
+ expect(document.querySelector(`${tabSelector} > li.active a`).getAttribute('href')).toEqual(
+ '#ldap',
+ );
+ });
- it('shows last selected tab on boot', () => {
- createMemoizer().saveData('#ldap');
- const fakeTab = {
- click: () => {},
- };
- spyOn(document, 'querySelector').and.returnValue(fakeTab);
- spyOn(fakeTab, 'click');
+ it('shows last selected tab on boot', () => {
+ createMemoizer().saveData('#ldap');
+ const fakeTab = {
+ click: () => {},
+ };
+ spyOn(document, 'querySelector').and.returnValue(fakeTab);
+ spyOn(fakeTab, 'click');
- memo.bootstrap();
+ memo.bootstrap();
- // verify that triggers click on the last selected tab
- expect(document.querySelector).toHaveBeenCalledWith(`${tabSelector} a[href="#ldap"]`);
- expect(fakeTab.click).toHaveBeenCalled();
- });
+ // verify that triggers click on the last selected tab
+ expect(document.querySelector).toHaveBeenCalledWith(`${tabSelector} a[href="#ldap"]`);
+ expect(fakeTab.click).toHaveBeenCalled();
+ });
- it('clicks the first tab if value in local storage is bad', () => {
- createMemoizer().saveData('#bogus');
- const fakeTab = {
- click: () => {},
- };
- spyOn(document, 'querySelector').and.callFake(selector => (selector === `${tabSelector} a[href="#bogus"]` ? null : fakeTab));
- spyOn(fakeTab, 'click');
+ it('clicks the first tab if value in local storage is bad', () => {
+ createMemoizer().saveData('#bogus');
+ const fakeTab = {
+ click: () => {},
+ };
+ spyOn(document, 'querySelector').and.callFake(
+ selector => (selector === `${tabSelector} a[href="#bogus"]` ? null : fakeTab),
+ );
+ spyOn(fakeTab, 'click');
+
+ memo.bootstrap();
+
+ // verify that triggers click on stored selector and fallback
+ expect(document.querySelector.calls.allArgs()).toEqual([
+ ['ul.new-session-tabs a[href="#bogus"]'],
+ ['ul.new-session-tabs a'],
+ ]);
+
+ expect(fakeTab.click).toHaveBeenCalled();
+ });
- memo.bootstrap();
+ it('saves last selected tab on change', () => {
+ createMemoizer();
- // verify that triggers click on stored selector and fallback
- expect(document.querySelector.calls.allArgs()).toEqual([['ul.new-session-tabs a[href="#bogus"]'], ['ul.new-session-tabs a']]);
- expect(fakeTab.click).toHaveBeenCalled();
- });
+ document.querySelector('a[href="#login-pane"]').click();
- it('saves last selected tab on change', () => {
- createMemoizer();
+ expect(memo.readData()).toEqual('#login-pane');
+ });
- document.querySelector('a[href="#login-pane"]').click();
+ it('overrides last selected tab with hash tag when given', () => {
+ window.location.hash = '#ldap';
+ createMemoizer();
- expect(memo.readData()).toEqual('#login-pane');
- });
+ expect(memo.readData()).toEqual('#ldap');
+ });
- it('overrides last selected tab with hash tag when given', () => {
- window.location.hash = '#ldap';
- createMemoizer();
+ describe('class constructor', () => {
+ beforeEach(() => {
+ memo = createMemoizer();
+ });
- expect(memo.readData()).toEqual('#ldap');
+ it('should set .isLocalStorageAvailable', () => {
+ expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
+ expect(memo.isLocalStorageAvailable).toBe(true);
});
+ });
- describe('class constructor', () => {
- beforeEach(() => {
- memo = createMemoizer();
- });
+ describe('saveData', () => {
+ beforeEach(() => {
+ memo = {
+ currentTabKey,
+ };
- it('should set .isLocalStorageAvailable', () => {
- expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
- expect(memo.isLocalStorageAvailable).toBe(true);
- });
+ spyOn(localStorage, 'setItem');
});
- describe('saveData', () => {
- beforeEach(() => {
- memo = {
- currentTabKey,
- };
+ describe('if .isLocalStorageAvailable is `false`', () => {
+ beforeEach(function() {
+ memo.isLocalStorageAvailable = false;
- spyOn(localStorage, 'setItem');
+ SigninTabsMemoizer.prototype.saveData.call(memo);
});
- describe('if .isLocalStorageAvailable is `false`', () => {
- beforeEach(function () {
- memo.isLocalStorageAvailable = false;
-
- SigninTabsMemoizer.prototype.saveData.call(memo);
- });
-
- it('should not call .setItem', () => {
- expect(localStorage.setItem).not.toHaveBeenCalled();
- });
+ it('should not call .setItem', () => {
+ expect(localStorage.setItem).not.toHaveBeenCalled();
});
+ });
- describe('if .isLocalStorageAvailable is `true`', () => {
- const value = 'value';
+ describe('if .isLocalStorageAvailable is `true`', () => {
+ const value = 'value';
- beforeEach(function () {
- memo.isLocalStorageAvailable = true;
+ beforeEach(function() {
+ memo.isLocalStorageAvailable = true;
- SigninTabsMemoizer.prototype.saveData.call(memo, value);
- });
+ SigninTabsMemoizer.prototype.saveData.call(memo, value);
+ });
- it('should call .setItem', () => {
- expect(localStorage.setItem).toHaveBeenCalledWith(currentTabKey, value);
- });
+ it('should call .setItem', () => {
+ expect(localStorage.setItem).toHaveBeenCalledWith(currentTabKey, value);
});
});
+ });
- describe('readData', () => {
- const itemValue = 'itemValue';
- let readData;
+ describe('readData', () => {
+ const itemValue = 'itemValue';
+ let readData;
- beforeEach(() => {
- memo = {
- currentTabKey,
- };
+ beforeEach(() => {
+ memo = {
+ currentTabKey,
+ };
- spyOn(localStorage, 'getItem').and.returnValue(itemValue);
- });
+ spyOn(localStorage, 'getItem').and.returnValue(itemValue);
+ });
- describe('if .isLocalStorageAvailable is `false`', () => {
- beforeEach(function () {
- memo.isLocalStorageAvailable = false;
+ describe('if .isLocalStorageAvailable is `false`', () => {
+ beforeEach(function() {
+ memo.isLocalStorageAvailable = false;
- readData = SigninTabsMemoizer.prototype.readData.call(memo);
- });
+ readData = SigninTabsMemoizer.prototype.readData.call(memo);
+ });
- it('should not call .getItem and should return `null`', () => {
- expect(localStorage.getItem).not.toHaveBeenCalled();
- expect(readData).toBe(null);
- });
+ it('should not call .getItem and should return `null`', () => {
+ expect(localStorage.getItem).not.toHaveBeenCalled();
+ expect(readData).toBe(null);
});
+ });
- describe('if .isLocalStorageAvailable is `true`', () => {
- beforeEach(function () {
- memo.isLocalStorageAvailable = true;
+ describe('if .isLocalStorageAvailable is `true`', () => {
+ beforeEach(function() {
+ memo.isLocalStorageAvailable = true;
- readData = SigninTabsMemoizer.prototype.readData.call(memo);
- });
+ readData = SigninTabsMemoizer.prototype.readData.call(memo);
+ });
- it('should call .getItem and return the localStorage value', () => {
- expect(window.localStorage.getItem).toHaveBeenCalledWith(currentTabKey);
- expect(readData).toBe(itemValue);
- });
+ it('should call .getItem and return the localStorage value', () => {
+ expect(window.localStorage.getItem).toHaveBeenCalledWith(currentTabKey);
+ expect(readData).toBe(itemValue);
});
});
});
-})();
+});
diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js
index d9b6dd1d487..c2c2a965e1d 100644
--- a/spec/javascripts/smart_interval_spec.js
+++ b/spec/javascripts/smart_interval_spec.js
@@ -3,7 +3,7 @@ import _ from 'underscore';
import SmartInterval from '~/smart_interval';
import waitForPromises from 'spec/helpers/wait_for_promises';
-describe('SmartInterval', function () {
+describe('SmartInterval', function() {
const DEFAULT_MAX_INTERVAL = 100;
const DEFAULT_STARTING_INTERVAL = 5;
const DEFAULT_SHORT_TIMEOUT = 75;
@@ -35,8 +35,8 @@ describe('SmartInterval', function () {
jasmine.clock().uninstall();
});
- describe('Increment Interval', function () {
- it('should increment the interval delay', (done) => {
+ describe('Increment Interval', function() {
+ it('should increment the interval delay', done => {
const smartInterval = createDefaultSmartInterval();
jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
@@ -45,8 +45,8 @@ describe('SmartInterval', function () {
.then(() => {
const intervalConfig = smartInterval.cfg;
const iterationCount = 4;
- const maxIntervalAfterIterations = intervalConfig.startingInterval *
- (intervalConfig.incrementByFactorOf ** iterationCount);
+ const maxIntervalAfterIterations =
+ intervalConfig.startingInterval * intervalConfig.incrementByFactorOf ** iterationCount;
const currentInterval = smartInterval.getCurrentInterval();
// Provide some flexibility for performance of testing environment
@@ -57,7 +57,7 @@ describe('SmartInterval', function () {
.catch(done.fail);
});
- it('should not increment past maxInterval', (done) => {
+ it('should not increment past maxInterval', done => {
const smartInterval = createDefaultSmartInterval({ maxInterval: DEFAULT_STARTING_INTERVAL });
jasmine.clock().tick(DEFAULT_STARTING_INTERVAL);
@@ -66,6 +66,7 @@ describe('SmartInterval', function () {
waitForPromises()
.then(() => {
const currentInterval = smartInterval.getCurrentInterval();
+
expect(currentInterval).toBe(smartInterval.cfg.maxInterval);
})
.then(done)
@@ -82,6 +83,7 @@ describe('SmartInterval', function () {
waitForPromises()
.then(() => {
const oneInterval = smartInterval.cfg.startingInterval * DEFAULT_INCREMENT_FACTOR;
+
expect(smartInterval.getCurrentInterval()).toEqual(oneInterval);
})
.then(done)
@@ -89,12 +91,12 @@ describe('SmartInterval', function () {
});
});
- describe('Public methods', function () {
- beforeEach(function () {
+ describe('Public methods', function() {
+ beforeEach(function() {
this.smartInterval = createDefaultSmartInterval();
});
- it('should cancel an interval', function (done) {
+ it('should cancel an interval', function(done) {
const interval = this.smartInterval;
jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
@@ -114,7 +116,7 @@ describe('SmartInterval', function () {
.catch(done.fail);
});
- it('should resume an interval', function (done) {
+ it('should resume an interval', function(done) {
const interval = this.smartInterval;
jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
@@ -126,6 +128,7 @@ describe('SmartInterval', function () {
waitForPromises()
.then(() => {
const { intervalId } = interval.state;
+
expect(intervalId).toBeTruthy();
})
.then(done)
@@ -133,15 +136,15 @@ describe('SmartInterval', function () {
});
});
- describe('DOM Events', function () {
- beforeEach(function () {
+ describe('DOM Events', function() {
+ beforeEach(function() {
// This ensures DOM and DOM events are initialized for these specs.
setFixtures('<div></div>');
this.smartInterval = createDefaultSmartInterval();
});
- it('should pause when page is not visible', function (done) {
+ it('should pause when page is not visible', function(done) {
const interval = this.smartInterval;
jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
@@ -168,8 +171,10 @@ describe('SmartInterval', function () {
waitForPromises()
.then(() => {
expect(interval.state.intervalId).toBeTruthy();
- expect(interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL &&
- interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL).toBeTruthy();
+ expect(
+ interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL &&
+ interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL,
+ ).toBeTruthy();
// simulates triggering of visibilitychange event
interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
@@ -181,7 +186,7 @@ describe('SmartInterval', function () {
.catch(done.fail);
});
- it('should resume when page is becomes visible at the previous interval', function (done) {
+ it('should resume when page is becomes visible at the previous interval', function(done) {
const interval = this.smartInterval;
jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
@@ -204,7 +209,7 @@ describe('SmartInterval', function () {
.catch(done.fail);
});
- it('should cancel on page unload', function (done) {
+ it('should cancel on page unload', function(done) {
const interval = this.smartInterval;
jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
@@ -212,6 +217,7 @@ describe('SmartInterval', function () {
waitForPromises()
.then(() => {
$(document).triggerHandler('beforeunload');
+
expect(interval.state.intervalId).toBeUndefined();
expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval);
})
@@ -219,8 +225,9 @@ describe('SmartInterval', function () {
.catch(done.fail);
});
- it('should execute callback before first interval', function () {
+ it('should execute callback before first interval', function() {
const interval = createDefaultSmartInterval({ immediateExecution: true });
+
expect(interval.cfg.immediateExecution).toBeFalsy();
});
});
diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js
index 1c3dac3584e..5438368ccbe 100644
--- a/spec/javascripts/syntax_highlight_spec.js
+++ b/spec/javascripts/syntax_highlight_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-var, no-return-assign, quotes */
+/* eslint-disable no-var, no-return-assign */
import $ from 'jquery';
import syntaxHighlight from '~/syntax_highlight';
@@ -9,35 +9,44 @@ describe('Syntax Highlighter', function() {
if (window.gon == null) {
window.gon = {};
}
- return window.gon.user_color_scheme = value;
+ return (window.gon.user_color_scheme = value);
};
describe('on a js-syntax-highlight element', function() {
beforeEach(function() {
return setFixtures('<div class="js-syntax-highlight"></div>');
});
- return it('applies syntax highlighting', function() {
+
+ it('applies syntax highlighting', function() {
stubUserColorScheme('monokai');
syntaxHighlight($('.js-syntax-highlight'));
- return expect($('.js-syntax-highlight')).toHaveClass('monokai');
+
+ expect($('.js-syntax-highlight')).toHaveClass('monokai');
});
});
- return describe('on a parent element', function() {
+
+ describe('on a parent element', function() {
beforeEach(function() {
- return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>");
+ return setFixtures(
+ '<div class="parent">\n <div class="js-syntax-highlight"></div>\n <div class="foo"></div>\n <div class="js-syntax-highlight"></div>\n</div>',
+ );
});
+
it('applies highlighting to all applicable children', function() {
stubUserColorScheme('monokai');
syntaxHighlight($('.parent'));
+
expect($('.parent, .foo')).not.toHaveClass('monokai');
- return expect($('.monokai').length).toBe(2);
+ expect($('.monokai').length).toBe(2);
});
- return it('prevents an infinite loop when no matches exist', function() {
+
+ it('prevents an infinite loop when no matches exist', function() {
var highlight;
setFixtures('<div></div>');
highlight = function() {
return syntaxHighlight($('div'));
};
- return expect(highlight).not.toThrow();
+
+ expect(highlight).not.toThrow();
});
});
});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 4452c470b82..96c0844f83c 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -41,8 +41,8 @@ jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH;
beforeAll(() => {
jasmine.addMatchers(
jasmineDiff(jasmine, {
- colors: true,
- inline: true,
+ colors: window.__karma__.config.color,
+ inline: window.__karma__.config.color,
}),
);
jasmine.addMatchers(customMatchers);
diff --git a/spec/javascripts/todos_spec.js b/spec/javascripts/todos_spec.js
index e74f4bdef7e..69e43274250 100644
--- a/spec/javascripts/todos_spec.js
+++ b/spec/javascripts/todos_spec.js
@@ -14,10 +14,10 @@ describe('Todos', () => {
});
describe('goToTodoUrl', () => {
- it('opens the todo url', (done) => {
+ it('opens the todo url', done => {
const todoLink = todoItem.dataset.url;
- spyOnDependency(Todos, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(Todos, 'visitUrl').and.callFake(url => {
expect(url).toEqual(todoLink);
done();
});
diff --git a/spec/javascripts/toggle_buttons_spec.js b/spec/javascripts/toggle_buttons_spec.js
index 17d0b94ebe0..09756ff76ec 100644
--- a/spec/javascripts/toggle_buttons_spec.js
+++ b/spec/javascripts/toggle_buttons_spec.js
@@ -24,11 +24,14 @@ describe('ToggleButtons', () => {
it('should initialize as checked', () => {
const wrapper = setupFixture(true);
- expect(wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked')).toEqual(true);
+ expect(
+ wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked'),
+ ).toEqual(true);
+
expect(wrapper.querySelector('.js-project-feature-toggle-input').value).toEqual('true');
});
- it('should toggle to unchecked when clicked', (done) => {
+ it('should toggle to unchecked when clicked', done => {
const wrapper = setupFixture(true);
const toggleButton = wrapper.querySelector('.js-project-feature-toggle');
@@ -48,11 +51,14 @@ describe('ToggleButtons', () => {
it('should initialize as unchecked', () => {
const wrapper = setupFixture(false);
- expect(wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked')).toEqual(false);
+ expect(
+ wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked'),
+ ).toEqual(false);
+
expect(wrapper.querySelector('.js-project-feature-toggle-input').value).toEqual('false');
});
- it('should toggle to checked when clicked', (done) => {
+ it('should toggle to checked when clicked', done => {
const wrapper = setupFixture(false);
const toggleButton = wrapper.querySelector('.js-project-feature-toggle');
@@ -68,7 +74,7 @@ describe('ToggleButtons', () => {
});
});
- it('should emit `trigger-change` event', (done) => {
+ it('should emit `trigger-change` event', done => {
const changeSpy = jasmine.createSpy('changeEventHandler');
const wrapper = setupFixture(false);
const toggleButton = wrapper.querySelector('.js-project-feature-toggle');
@@ -87,7 +93,7 @@ describe('ToggleButtons', () => {
});
describe('clickCallback', () => {
- it('should show loading indicator while waiting', (done) => {
+ it('should show loading indicator while waiting', done => {
const isChecked = true;
const clickCallback = (newValue, toggleButton) => {
const input = toggleButton.querySelector('.js-project-feature-toggle-input');
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index 57e0caa692c..ddb09811dda 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -3,7 +3,7 @@ import U2FAuthenticate from '~/u2f/authenticate';
import 'vendor/u2f';
import MockU2FDevice from './mock_u2f_device';
-describe('U2FAuthenticate', function () {
+describe('U2FAuthenticate', function() {
preloadFixtures('u2f/authenticate.html.raw');
beforeEach(() => {
@@ -32,30 +32,40 @@ describe('U2FAuthenticate', function () {
window.u2f = this.oldu2f;
});
- it('falls back to normal 2fa', (done) => {
- this.component.start().then(() => {
- expect(this.component.switchToFallbackUI).toHaveBeenCalled();
- done();
- }).catch(done.fail);
+ it('falls back to normal 2fa', done => {
+ this.component
+ .start()
+ .then(() => {
+ expect(this.component.switchToFallbackUI).toHaveBeenCalled();
+ done();
+ })
+ .catch(done.fail);
});
});
describe('with u2f available', () => {
- beforeEach((done) => {
+ beforeEach(done => {
// bypass automatic form submission within renderAuthenticated
spyOn(this.component, 'renderAuthenticated').and.returnValue(true);
this.u2fDevice = new MockU2FDevice();
- this.component.start().then(done).catch(done.fail);
+ this.component
+ .start()
+ .then(done)
+ .catch(done.fail);
});
it('allows authenticating via a U2F device', () => {
const inProgressMessage = this.container.find('p');
+
expect(inProgressMessage.text()).toContain('Trying to communicate with your device');
this.u2fDevice.respondToAuthenticateRequest({
deviceData: 'this is data from the device',
});
- expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
+
+ expect(this.component.renderAuthenticated).toHaveBeenCalledWith(
+ '{"deviceData":"this is data from the device"}',
+ );
});
describe('errors', () => {
@@ -66,7 +76,8 @@ describe('U2FAuthenticate', function () {
errorCode: 'error!',
});
const errorMessage = this.container.find('p');
- return expect(errorMessage.text()).toContain('There was a problem communicating with your device');
+
+ expect(errorMessage.text()).toContain('There was a problem communicating with your device');
});
return it('allows retrying authentication after an error', () => {
let setupButton = this.container.find('#js-login-u2f-device');
@@ -81,7 +92,10 @@ describe('U2FAuthenticate', function () {
this.u2fDevice.respondToAuthenticateRequest({
deviceData: 'this is data from the device',
});
- expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
+
+ expect(this.component.renderAuthenticated).toHaveBeenCalledWith(
+ '{"deviceData":"this is data from the device"}',
+ );
});
});
});
diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js
index 012a1cefbbf..26ddd8ade61 100644
--- a/spec/javascripts/u2f/mock_u2f_device.js
+++ b/spec/javascripts/u2f/mock_u2f_device.js
@@ -1,18 +1,18 @@
-/* eslint-disable wrap-iife, no-unused-expressions, no-return-assign, no-param-reassign */
+/* eslint-disable no-unused-expressions, no-return-assign, no-param-reassign */
export default class MockU2FDevice {
constructor() {
this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this);
this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this);
window.u2f || (window.u2f = {});
- window.u2f.register = (function (_this) {
- return function (appId, registerRequests, signRequests, callback) {
- return _this.registerCallback = callback;
+ window.u2f.register = (function(_this) {
+ return function(appId, registerRequests, signRequests, callback) {
+ return (_this.registerCallback = callback);
};
})(this);
- window.u2f.sign = (function (_this) {
- return function (appId, challenges, signRequests, callback) {
- return _this.authenticateCallback = callback;
+ window.u2f.sign = (function(_this) {
+ return function(appId, challenges, signRequests, callback) {
+ return (_this.authenticateCallback = callback);
};
})(this);
}
diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js
index d9383314891..261db3d66d7 100644
--- a/spec/javascripts/u2f/register_spec.js
+++ b/spec/javascripts/u2f/register_spec.js
@@ -3,41 +3,48 @@ import U2FRegister from '~/u2f/register';
import 'vendor/u2f';
import MockU2FDevice from './mock_u2f_device';
-describe('U2FRegister', function () {
+describe('U2FRegister', function() {
preloadFixtures('u2f/register.html.raw');
- beforeEach((done) => {
+ beforeEach(done => {
loadFixtures('u2f/register.html.raw');
this.u2fDevice = new MockU2FDevice();
this.container = $('#js-register-u2f');
this.component = new U2FRegister(this.container, $('#js-register-u2f-templates'), {}, 'token');
- this.component.start().then(done).catch(done.fail);
+ this.component
+ .start()
+ .then(done)
+ .catch(done.fail);
});
it('allows registering a U2F device', () => {
const setupButton = this.container.find('#js-setup-u2f-device');
- expect(setupButton.text()).toBe('Setup new U2F device');
+
+ expect(setupButton.text()).toBe('Set up new U2F device');
setupButton.trigger('click');
const inProgressMessage = this.container.children('p');
+
expect(inProgressMessage.text()).toContain('Trying to communicate with your device');
this.u2fDevice.respondToRegisterRequest({
deviceData: 'this is data from the device',
});
const registeredMessage = this.container.find('p');
const deviceResponse = this.container.find('#js-device-response');
+
expect(registeredMessage.text()).toContain('Your device was successfully set up!');
- return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}');
+ expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}');
});
- return describe('errors', () => {
- it('doesn\'t allow the same device to be registered twice (for the same user', () => {
+ describe('errors', () => {
+ it("doesn't allow the same device to be registered twice (for the same user", () => {
const setupButton = this.container.find('#js-setup-u2f-device');
setupButton.trigger('click');
this.u2fDevice.respondToRegisterRequest({
errorCode: 4,
});
const errorMessage = this.container.find('p');
- return expect(errorMessage.text()).toContain('already been registered with us');
+
+ expect(errorMessage.text()).toContain('already been registered with us');
});
it('displays an error message for other errors', () => {
@@ -47,10 +54,11 @@ describe('U2FRegister', function () {
errorCode: 'error!',
});
const errorMessage = this.container.find('p');
- return expect(errorMessage.text()).toContain('There was a problem communicating with your device');
+
+ expect(errorMessage.text()).toContain('There was a problem communicating with your device');
});
- return it('allows retrying registration after an error', () => {
+ it('allows retrying registration after an error', () => {
let setupButton = this.container.find('#js-setup-u2f-device');
setupButton.trigger('click');
this.u2fDevice.respondToRegisterRequest({
@@ -64,7 +72,8 @@ describe('U2FRegister', function () {
deviceData: 'this is data from the device',
});
const registeredMessage = this.container.find('p');
- return expect(registeredMessage.text()).toContain('Your device was successfully set up!');
+
+ expect(registeredMessage.text()).toContain('Your device was successfully set up!');
});
});
});
diff --git a/spec/javascripts/u2f/util_spec.js b/spec/javascripts/u2f/util_spec.js
index 4187183236f..32cd6891384 100644
--- a/spec/javascripts/u2f/util_spec.js
+++ b/spec/javascripts/u2f/util_spec.js
@@ -3,42 +3,58 @@ import { canInjectU2fApi } from '~/u2f/util';
describe('U2F Utils', () => {
describe('canInjectU2fApi', () => {
it('returns false for Chrome < 41', () => {
- const userAgent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.28 Safari/537.36';
+ const userAgent =
+ 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.28 Safari/537.36';
+
expect(canInjectU2fApi(userAgent)).toBe(false);
});
it('returns true for Chrome >= 41', () => {
- const userAgent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36';
+ const userAgent =
+ 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36';
+
expect(canInjectU2fApi(userAgent)).toBe(true);
});
it('returns false for Opera < 40', () => {
- const userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36 OPR/32.0.1948.25';
+ const userAgent =
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36 OPR/32.0.1948.25';
+
expect(canInjectU2fApi(userAgent)).toBe(false);
});
it('returns true for Opera >= 40', () => {
- const userAgent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991';
+ const userAgent =
+ 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991';
+
expect(canInjectU2fApi(userAgent)).toBe(true);
});
it('returns false for Safari', () => {
- const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4';
+ const userAgent =
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4';
+
expect(canInjectU2fApi(userAgent)).toBe(false);
});
it('returns false for Chrome on Android', () => {
- const userAgent = 'Mozilla/5.0 (Linux; Android 7.0; VS988 Build/NRD90U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3145.0 Mobile Safari/537.36';
+ const userAgent =
+ 'Mozilla/5.0 (Linux; Android 7.0; VS988 Build/NRD90U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3145.0 Mobile Safari/537.36';
+
expect(canInjectU2fApi(userAgent)).toBe(false);
});
it('returns false for Chrome on iOS', () => {
- const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1';
+ const userAgent =
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1';
+
expect(canInjectU2fApi(userAgent)).toBe(false);
});
it('returns false for Safari on iOS', () => {
- const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A356 Safari/604.1';
+ const userAgent =
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A356 Safari/604.1';
+
expect(canInjectU2fApi(userAgent)).toBe(false);
});
});
diff --git a/spec/javascripts/version_check_image_spec.js b/spec/javascripts/version_check_image_spec.js
index 5f963e8c11e..0e69fcc4c5f 100644
--- a/spec/javascripts/version_check_image_spec.js
+++ b/spec/javascripts/version_check_image_spec.js
@@ -2,17 +2,19 @@ import $ from 'jquery';
import VersionCheckImage from '~/version_check_image';
import ClassSpecHelper from './helpers/class_spec_helper';
-describe('VersionCheckImage', function () {
- describe('bindErrorEvent', function () {
+describe('VersionCheckImage', function() {
+ describe('bindErrorEvent', function() {
ClassSpecHelper.itShouldBeAStaticMethod(VersionCheckImage, 'bindErrorEvent');
- beforeEach(function () {
+ beforeEach(function() {
this.imageElement = $('<div></div>');
});
- it('registers an error event', function () {
+ it('registers an error event', function() {
spyOn($.prototype, 'on');
- spyOn($.prototype, 'off').and.callFake(function () { return this; });
+ spyOn($.prototype, 'off').and.callFake(function() {
+ return this;
+ });
VersionCheckImage.bindErrorEvent(this.imageElement);
@@ -20,7 +22,7 @@ describe('VersionCheckImage', function () {
expect($.prototype.on).toHaveBeenCalledWith('error', jasmine.any(Function));
});
- it('hides the imageElement on error', function () {
+ it('hides the imageElement on error', function() {
spyOn($.prototype, 'hide');
VersionCheckImage.bindErrorEvent(this.imageElement);
diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
index 50c2b0e2bd0..056b4df8fdc 100644
--- a/spec/javascripts/vue_mr_widget/components/deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
@@ -2,43 +2,52 @@ import Vue from 'vue';
import deploymentComponent from '~/vue_merge_request_widget/components/deployment.vue';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
import { getTimeago } from '~/lib/utils/datetime_utility';
+import mountComponent from '../../helpers/vue_mount_component_helper';
-const deploymentMockData = {
- id: 15,
- name: 'review/diplo',
- url: '/root/acets-review-apps/environments/15',
- stop_url: '/root/acets-review-apps/environments/15/stop',
- metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
- metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
- external_url: 'http://diplo.',
- external_url_formatted: 'diplo.',
- deployed_at: '2017-03-22T22:44:42.258Z',
- deployed_at_formatted: 'Mar 22, 2017 10:44pm',
-};
-const createComponent = () => {
+describe('Deployment component', () => {
const Component = Vue.extend(deploymentComponent);
+ const deploymentMockData = {
+ id: 15,
+ name: 'review/diplo',
+ url: '/root/review-apps/environments/15',
+ stop_url: '/root/review-apps/environments/15/stop',
+ metrics_url: '/root/review-apps/environments/15/deployments/1/metrics',
+ metrics_monitoring_url: '/root/review-apps/environments/15/metrics',
+ external_url: 'http://gitlab.com.',
+ external_url_formatted: 'gitlab',
+ deployed_at: '2017-03-22T22:44:42.258Z',
+ deployed_at_formatted: 'Mar 22, 2017 10:44pm',
+ changes: [
+ {
+ path: 'index.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
+ },
+ {
+ path: 'imgs/gallery.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
+ },
+ {
+ path: 'about/',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
+ },
+ ],
+ };
- return new Component({
- el: document.createElement('div'),
- propsData: { deployment: { ...deploymentMockData } },
- });
-};
-
-describe('Deployment component', () => {
let vm;
- beforeEach(() => {
- vm = createComponent();
- });
-
afterEach(() => {
vm.$destroy();
});
- describe('computed', () => {
+ describe('', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, { deployment: { ...deploymentMockData }, showMetrics: true });
+ });
+
describe('deployTimeago', () => {
it('return formatted date', () => {
const readable = getTimeago().format(deploymentMockData.deployed_at);
+
expect(vm.deployTimeago).toEqual(readable);
});
});
@@ -96,24 +105,23 @@ describe('Deployment component', () => {
expect(vm.hasDeploymentMeta).toEqual(false);
});
});
- });
- describe('methods', () => {
describe('stopEnvironment', () => {
const url = '/foo/bar';
- const returnPromise = () => new Promise((resolve) => {
- resolve({
- data: {
- redirect_url: url,
- },
+ const returnPromise = () =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ redirect_url: url,
+ },
+ });
});
- });
const mockStopEnvironment = () => {
vm.stopEnvironment(deploymentMockData);
return vm;
};
- it('should show a confirm dialog and call service.stopEnvironment when confirmed', (done) => {
+ it('should show a confirm dialog and call service.stopEnvironment when confirmed', done => {
spyOn(window, 'confirm').and.returnValue(true);
spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(true));
const visitUrl = spyOnDependency(deploymentComponent, 'visitUrl').and.returnValue(true);
@@ -136,36 +144,113 @@ describe('Deployment component', () => {
expect(MRWidgetService.stopEnvironment).not.toHaveBeenCalled();
});
});
- });
-
- describe('template', () => {
- let el;
-
- beforeEach(() => {
- vm = createComponent(deploymentMockData);
- el = vm.$el;
- });
it('renders deployment name', () => {
- expect(el.querySelector('.js-deploy-meta').getAttribute('href')).toEqual(deploymentMockData.url);
- expect(el.querySelector('.js-deploy-meta').innerText).toContain(deploymentMockData.name);
+ expect(vm.$el.querySelector('.js-deploy-meta').getAttribute('href')).toEqual(
+ deploymentMockData.url,
+ );
+
+ expect(vm.$el.querySelector('.js-deploy-meta').innerText).toContain(deploymentMockData.name);
});
it('renders external URL', () => {
- expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deploymentMockData.external_url);
- expect(el.querySelector('.js-deploy-url').innerText).toContain('View app');
+ expect(vm.$el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(
+ deploymentMockData.external_url,
+ );
+
+ expect(vm.$el.querySelector('.js-deploy-url').innerText).toContain('View app');
});
it('renders stop button', () => {
- expect(el.querySelector('.btn')).not.toBeNull();
+ expect(vm.$el.querySelector('.btn')).not.toBeNull();
});
it('renders deployment time', () => {
- expect(el.querySelector('.js-deploy-time').innerText).toContain(vm.deployTimeago);
+ expect(vm.$el.querySelector('.js-deploy-time').innerText).toContain(vm.deployTimeago);
});
it('renders metrics component', () => {
- expect(el.querySelector('.js-mr-memory-usage')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-mr-memory-usage')).not.toBeNull();
+ });
+ });
+
+ describe('with showMetrics enabled', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, { deployment: { ...deploymentMockData }, showMetrics: true });
+ });
+
+ it('shows metrics', () => {
+ expect(vm.$el).toContainElement('.js-mr-memory-usage');
+ });
+ });
+
+ describe('with showMetrics disabled', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, { deployment: { ...deploymentMockData }, showMetrics: false });
+ });
+
+ it('hides metrics', () => {
+ expect(vm.$el).not.toContainElement('.js-mr-memory-usage');
+ });
+ });
+
+ describe('without changes', () => {
+ beforeEach(() => {
+ delete deploymentMockData.changes;
+
+ vm = mountComponent(Component, { deployment: { ...deploymentMockData }, showMetrics: true });
+ });
+
+ it('renders the link to the review app without dropdown', () => {
+ expect(vm.$el.querySelector('.js-mr-wigdet-deployment-dropdown')).toBeNull();
+ expect(vm.$el.querySelector('.js-deploy-url-feature-flag')).not.toBeNull();
+ });
+ });
+
+ describe('deployment status', () => {
+ describe('running', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ deployment: Object.assign({}, deploymentMockData, { status: 'running' }),
+ showMetrics: true,
+ });
+ });
+
+ it('renders information about running deployment', () => {
+ expect(vm.$el.querySelector('.js-deployment-info').textContent).toContain('Deploying to');
+ });
+
+ it('renders disabled stop button', () => {
+ expect(vm.$el.querySelector('.js-stop-env').getAttribute('disabled')).toBe('disabled');
+ });
+ });
+
+ describe('success', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ deployment: Object.assign({}, deploymentMockData, { status: 'success' }),
+ showMetrics: true,
+ });
+ });
+
+ it('renders information about finished deployment', () => {
+ expect(vm.$el.querySelector('.js-deployment-info').textContent).toContain('Deployed to');
+ });
+ });
+
+ describe('failed', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ deployment: Object.assign({}, deploymentMockData, { status: 'failed' }),
+ showMetrics: true,
+ });
+ });
+
+ it('renders information about finished deployment', () => {
+ expect(vm.$el.querySelector('.js-deployment-info').textContent).toContain(
+ 'Failed to deploy to',
+ );
+ });
});
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js
index 00f4f2d7c39..b69082082ba 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js
@@ -13,9 +13,9 @@ describe('MrWidgetAuthor', () => {
name: 'Administrator',
username: 'root',
webUrl: 'http://localhost:3000/root',
- avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatarUrl:
+ 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
},
-
});
});
@@ -28,9 +28,9 @@ describe('MrWidgetAuthor', () => {
});
it('renders image with avatar url', () => {
- expect(
- vm.$el.querySelector('img').getAttribute('src'),
- ).toEqual('http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon');
+ expect(vm.$el.querySelector('img').getAttribute('src')).toEqual(
+ 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ );
});
it('renders author name', () => {
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js
index 10143402acf..787f44e478d 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js
@@ -14,7 +14,8 @@ describe('MrWidgetAuthorTime', () => {
name: 'Administrator',
username: 'root',
webUrl: 'http://localhost:3000/root',
- avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatarUrl:
+ 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
},
dateTitle: '2017-03-23T23:02:00.807Z',
dateReadable: '12 hours ago',
@@ -34,7 +35,10 @@ describe('MrWidgetAuthorTime', () => {
});
it('renders provided time', () => {
- expect(vm.$el.querySelector('time').getAttribute('data-original-title')).toEqual('2017-03-23T23:02:00.807Z');
+ expect(vm.$el.querySelector('time').getAttribute('data-original-title')).toEqual(
+ '2017-03-23T23:02:00.807Z',
+ );
+
expect(vm.$el.querySelector('time').textContent.trim()).toEqual('12 hours ago');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
index 8ac2f26979b..02c476f2871 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
@@ -41,11 +41,12 @@ describe('MRWidgetHeader', () => {
statusPath: 'abc',
},
});
+
expect(vm.shouldShowCommitsBehindText).toEqual(false);
});
});
- describe('commitsText', () => {
+ describe('commitsBehindText', () => {
it('returns singular when there is one commit', () => {
vm = mountComponent(Component, {
mr: {
@@ -53,11 +54,14 @@ describe('MRWidgetHeader', () => {
sourceBranch: 'mr-widget-refactor',
sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">Link</a>',
targetBranch: 'master',
+ targetBranchPath: '/foo/bar/master',
statusPath: 'abc',
},
});
- expect(vm.commitsText).toEqual('1 commit behind');
+ expect(vm.commitsBehindText).toEqual(
+ 'The source branch is <a href="/foo/bar/master">1 commit behind</a> the target branch',
+ );
});
it('returns plural when there is more than one commit', () => {
@@ -67,11 +71,14 @@ describe('MRWidgetHeader', () => {
sourceBranch: 'mr-widget-refactor',
sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">Link</a>',
targetBranch: 'master',
+ targetBranchPath: '/foo/bar/master',
statusPath: 'abc',
},
});
- expect(vm.commitsText).toEqual('2 commits behind');
+ expect(vm.commitsBehindText).toEqual(
+ 'The source branch is <a href="/foo/bar/master">2 commits behind</a> the target branch',
+ );
});
});
});
@@ -112,28 +119,31 @@ describe('MRWidgetHeader', () => {
});
describe('with an open merge request', () => {
+ const mrDefaultOptions = {
+ iid: 1,
+ divergedCommitsCount: 12,
+ sourceBranch: 'mr-widget-refactor',
+ sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">mr-widget-refactor</a>',
+ sourceBranchRemoved: false,
+ targetBranchPath: 'foo/bar/commits-path',
+ targetBranchTreePath: 'foo/bar/tree/path',
+ targetBranch: 'master',
+ isOpen: true,
+ canPushToSourceBranch: true,
+ emailPatchesPath: '/mr/email-patches',
+ plainDiffPath: '/mr/plainDiffPath',
+ statusPath: 'abc',
+ sourceProjectFullPath: 'root/gitlab-ce',
+ targetProjectFullPath: 'gitlab-org/gitlab-ce',
+ };
+
afterEach(() => {
vm.$destroy();
});
beforeEach(() => {
vm = mountComponent(Component, {
- mr: {
- iid: 1,
- divergedCommitsCount: 12,
- sourceBranch: 'mr-widget-refactor',
- sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">mr-widget-refactor</a>',
- sourceBranchRemoved: false,
- targetBranchPath: 'foo/bar/commits-path',
- targetBranchTreePath: 'foo/bar/tree/path',
- targetBranch: 'master',
- isOpen: true,
- emailPatchesPath: '/mr/email-patches',
- plainDiffPath: '/mr/plainDiffPath',
- statusPath: 'abc',
- sourceProjectFullPath: 'root/gitlab-ce',
- targetProjectFullPath: 'gitlab-org/gitlab-ce',
- },
+ mr: Object.assign({}, mrDefaultOptions),
});
});
@@ -149,11 +159,22 @@ describe('MRWidgetHeader', () => {
const button = vm.$el.querySelector('.js-web-ide');
expect(button.textContent.trim()).toEqual('Open in Web IDE');
+ expect(button.classList.contains('disabled')).toBe(false);
expect(button.getAttribute('href')).toEqual(
'/-/ide/project/root/gitlab-ce/merge_requests/1?target_project=gitlab-org%2Fgitlab-ce',
);
});
+ it('renders web ide button in disabled state with no href', () => {
+ const mr = Object.assign({}, mrDefaultOptions, { canPushToSourceBranch: false });
+ vm = mountComponent(Component, { mr });
+
+ const link = vm.$el.querySelector('.js-web-ide');
+
+ expect(link.classList.contains('disabled')).toBe(true);
+ expect(link.getAttribute('href')).toBeNull();
+ });
+
it('renders web ide button with blank query string if target & source project branch', done => {
vm.mr.targetProjectFullPath = 'root/gitlab-ce';
@@ -280,8 +301,17 @@ describe('MRWidgetHeader', () => {
});
it('renders diverged commits info', () => {
- expect(vm.$el.querySelector('.diverged-commits-count').textContent).toMatch(
- /(mr-widget-refactor[\s\S]+?is 12 commits behind[\s\S]+?master)/,
+ expect(vm.$el.querySelector('.diverged-commits-count').textContent).toEqual(
+ 'The source branch is 12 commits behind the target branch',
+ );
+
+ expect(vm.$el.querySelector('.diverged-commits-count a').textContent).toEqual(
+ '12 commits behind',
+ );
+
+ expect(vm.$el.querySelector('.diverged-commits-count a')).toHaveAttr(
+ 'href',
+ vm.mr.targetBranchPath,
);
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
index 91e81a0675a..4baaea9745a 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
@@ -128,16 +128,10 @@ describe('MemoryUsage', () => {
describe('computeGraphData', () => {
it('should populate sparkline graph', () => {
vm.computeGraphData(metrics, deployment_time);
- const {
- hasMetrics,
- memoryMetrics,
- deploymentTime,
- memoryFrom,
- memoryTo,
- } = vm;
+ const { hasMetrics, memoryMetrics, deploymentTime, memoryFrom, memoryTo } = vm;
expect(hasMetrics).toBeTruthy();
- expect(memoryMetrics.length > 0).toBeTruthy();
+ expect(memoryMetrics.length).toBeGreaterThan(0);
expect(deploymentTime).toEqual(deployment_time);
expect(memoryFrom).toEqual('9.13');
expect(memoryTo).toEqual('4.28');
@@ -153,18 +147,13 @@ describe('MemoryUsage', () => {
});
it('should load metrics data using MRWidgetService', done => {
- spyOn(MRWidgetService, 'fetchMetrics').and.returnValue(
- returnServicePromise(true),
- );
+ spyOn(MRWidgetService, 'fetchMetrics').and.returnValue(returnServicePromise(true));
spyOn(vm, 'computeGraphData');
vm.loadMetrics();
setTimeout(() => {
expect(MRWidgetService.fetchMetrics).toHaveBeenCalledWith(url);
- expect(vm.computeGraphData).toHaveBeenCalledWith(
- metrics,
- deployment_time,
- );
+ expect(vm.computeGraphData).toHaveBeenCalledWith(metrics, deployment_time);
done();
}, 333);
});
@@ -183,15 +172,11 @@ describe('MemoryUsage', () => {
vm.loadFailed = false;
Vue.nextTick(() => {
- expect(
- el.querySelector('.js-usage-info.usage-info-loading'),
- ).toBeDefined();
- expect(
- el.querySelector('.js-usage-info .usage-info-load-spinner'),
- ).toBeDefined();
- expect(el.querySelector('.js-usage-info').innerText).toContain(
- messages.loadingMetrics,
- );
+ expect(el.querySelector('.js-usage-info.usage-info-loading')).toBeDefined();
+
+ expect(el.querySelector('.js-usage-info .usage-info-load-spinner')).toBeDefined();
+
+ expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadingMetrics);
done();
});
});
@@ -203,9 +188,7 @@ describe('MemoryUsage', () => {
Vue.nextTick(() => {
expect(el.querySelector('.memory-graph-container')).toBeDefined();
- expect(el.querySelector('.js-usage-info').innerText).toContain(
- messages.hasMetrics,
- );
+ expect(el.querySelector('.js-usage-info').innerText).toContain(messages.hasMetrics);
done();
});
});
@@ -216,12 +199,9 @@ describe('MemoryUsage', () => {
vm.loadFailed = true;
Vue.nextTick(() => {
- expect(
- el.querySelector('.js-usage-info.usage-info-failed'),
- ).toBeDefined();
- expect(el.querySelector('.js-usage-info').innerText).toContain(
- messages.loadFailed,
- );
+ expect(el.querySelector('.js-usage-info.usage-info-failed')).toBeDefined();
+
+ expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadFailed);
done();
});
});
@@ -232,12 +212,9 @@ describe('MemoryUsage', () => {
vm.loadFailed = false;
Vue.nextTick(() => {
- expect(
- el.querySelector('.js-usage-info.usage-info-unavailable'),
- ).toBeDefined();
- expect(el.querySelector('.js-usage-info').innerText).toContain(
- messages.metricsUnavailable,
- );
+ expect(el.querySelector('.js-usage-info.usage-info-unavailable')).toBeDefined();
+
+ expect(el.querySelector('.js-usage-info').innerText).toContain(messages.metricsUnavailable);
done();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js
index 367c499daaf..2c554f3f3ab 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js
@@ -23,15 +23,23 @@ describe('MRWidgetMergeHelp', () => {
it('renders missing branch information', () => {
expect(
- vm.$el.textContent.trim().replace(/[\r\n]+/g, ' ').replace(/\s\s+/g, ' '),
+ vm.$el.textContent
+ .trim()
+ .replace(/[\r\n]+/g, ' ')
+ .replace(/\s\s+/g, ' '),
).toEqual(
'If the this-is-not-the-branch-you-are-looking-for branch exists in your local repository, you can merge this merge request manually using the command line',
);
});
it('renders button to open help modal', () => {
- expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual('#modal_merge_info');
- expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual('modal');
+ expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual(
+ '#modal_merge_info',
+ );
+
+ expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual(
+ 'modal',
+ );
});
});
@@ -42,15 +50,21 @@ describe('MRWidgetMergeHelp', () => {
it('renders information about how to merge manually', () => {
expect(
- vm.$el.textContent.trim().replace(/[\r\n]+/g, ' ').replace(/\s\s+/g, ' '),
- ).toEqual(
- 'You can merge this merge request manually using the command line',
- );
+ vm.$el.textContent
+ .trim()
+ .replace(/[\r\n]+/g, ' ')
+ .replace(/\s\s+/g, ' '),
+ ).toEqual('You can merge this merge request manually using the command line');
});
it('renders element to open a modal', () => {
- expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual('#modal_merge_info');
- expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual('modal');
+ expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual(
+ '#modal_merge_info',
+ );
+
+ expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual(
+ 'modal',
+ );
});
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
index ea8007d2029..d905bbe4040 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
@@ -22,6 +22,7 @@ describe('MRWidgetPipeline', () => {
pipeline: mockData.pipeline,
ciStatus: 'success',
hasCi: true,
+ troubleshootingDocsPath: 'help',
});
expect(vm.hasPipeline).toEqual(true);
@@ -30,6 +31,7 @@ describe('MRWidgetPipeline', () => {
it('should return false when there is no pipeline', () => {
vm = mountComponent(Component, {
pipeline: {},
+ troubleshootingDocsPath: 'help',
});
expect(vm.hasPipeline).toEqual(false);
@@ -42,6 +44,7 @@ describe('MRWidgetPipeline', () => {
pipeline: mockData.pipeline,
hasCi: true,
ciStatus: 'success',
+ troubleshootingDocsPath: 'help',
});
expect(vm.hasCIError).toEqual(false);
@@ -52,6 +55,7 @@ describe('MRWidgetPipeline', () => {
pipeline: mockData.pipeline,
hasCi: true,
ciStatus: null,
+ troubleshootingDocsPath: 'help',
});
expect(vm.hasCIError).toEqual(true);
@@ -65,11 +69,12 @@ describe('MRWidgetPipeline', () => {
pipeline: mockData.pipeline,
hasCi: true,
ciStatus: null,
+ troubleshootingDocsPath: 'help',
});
- expect(
- vm.$el.querySelector('.media-body').textContent.trim(),
- ).toEqual('Could not connect to the CI server. Please check your settings and try again');
+ expect(vm.$el.querySelector('.media-body').textContent.trim()).toContain(
+ 'Could not retrieve the pipeline status. For troubleshooting steps, read the documentation.',
+ );
});
describe('with a pipeline', () => {
@@ -78,38 +83,41 @@ describe('MRWidgetPipeline', () => {
pipeline: mockData.pipeline,
hasCi: true,
ciStatus: 'success',
+ troubleshootingDocsPath: 'help',
});
});
it('should render pipeline ID', () => {
- expect(
- vm.$el.querySelector('.pipeline-id').textContent.trim(),
- ).toEqual(`#${mockData.pipeline.id}`);
+ expect(vm.$el.querySelector('.pipeline-id').textContent.trim()).toEqual(
+ `#${mockData.pipeline.id}`,
+ );
});
it('should render pipeline status and commit id', () => {
- expect(
- vm.$el.querySelector('.media-body').textContent.trim(),
- ).toContain(mockData.pipeline.details.status.label);
+ expect(vm.$el.querySelector('.media-body').textContent.trim()).toContain(
+ mockData.pipeline.details.status.label,
+ );
- expect(
- vm.$el.querySelector('.js-commit-link').textContent.trim(),
- ).toEqual(mockData.pipeline.commit.short_id);
+ expect(vm.$el.querySelector('.js-commit-link').textContent.trim()).toEqual(
+ mockData.pipeline.commit.short_id,
+ );
- expect(
- vm.$el.querySelector('.js-commit-link').getAttribute('href'),
- ).toEqual(mockData.pipeline.commit.commit_path);
+ expect(vm.$el.querySelector('.js-commit-link').getAttribute('href')).toEqual(
+ mockData.pipeline.commit.commit_path,
+ );
});
it('should render pipeline graph', () => {
expect(vm.$el.querySelector('.mr-widget-pipeline-graph')).toBeDefined();
- expect(vm.$el.querySelectorAll('.stage-container').length).toEqual(mockData.pipeline.details.stages.length);
+ expect(vm.$el.querySelectorAll('.stage-container').length).toEqual(
+ mockData.pipeline.details.stages.length,
+ );
});
it('should render coverage information', () => {
- expect(
- vm.$el.querySelector('.media-body').textContent,
- ).toContain(`Coverage ${mockData.pipeline.coverage}`);
+ expect(vm.$el.querySelector('.media-body').textContent).toContain(
+ `Coverage ${mockData.pipeline.coverage}`,
+ );
});
});
@@ -122,34 +130,35 @@ describe('MRWidgetPipeline', () => {
pipeline: mockCopy.pipeline,
hasCi: true,
ciStatus: 'success',
+ troubleshootingDocsPath: 'help',
});
});
it('should render pipeline ID', () => {
- expect(
- vm.$el.querySelector('.pipeline-id').textContent.trim(),
- ).toEqual(`#${mockData.pipeline.id}`);
+ expect(vm.$el.querySelector('.pipeline-id').textContent.trim()).toEqual(
+ `#${mockData.pipeline.id}`,
+ );
});
it('should render pipeline status', () => {
- expect(
- vm.$el.querySelector('.media-body').textContent.trim(),
- ).toContain(mockData.pipeline.details.status.label);
+ expect(vm.$el.querySelector('.media-body').textContent.trim()).toContain(
+ mockData.pipeline.details.status.label,
+ );
- expect(
- vm.$el.querySelector('.js-commit-link'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.js-commit-link')).toBeNull();
});
it('should render pipeline graph', () => {
expect(vm.$el.querySelector('.mr-widget-pipeline-graph')).toBeDefined();
- expect(vm.$el.querySelectorAll('.stage-container').length).toEqual(mockData.pipeline.details.stages.length);
+ expect(vm.$el.querySelectorAll('.stage-container').length).toEqual(
+ mockData.pipeline.details.stages.length,
+ );
});
it('should render coverage information', () => {
- expect(
- vm.$el.querySelector('.media-body').textContent,
- ).toContain(`Coverage ${mockData.pipeline.coverage}`);
+ expect(vm.$el.querySelector('.media-body').textContent).toContain(
+ `Coverage ${mockData.pipeline.coverage}`,
+ );
});
});
@@ -162,11 +171,10 @@ describe('MRWidgetPipeline', () => {
pipeline: mockCopy.pipeline,
hasCi: true,
ciStatus: 'success',
+ troubleshootingDocsPath: 'help',
});
- expect(
- vm.$el.querySelector('.media-body').textContent,
- ).not.toContain('Coverage');
+ expect(vm.$el.querySelector('.media-body').textContent).not.toContain('Coverage');
});
});
@@ -179,6 +187,7 @@ describe('MRWidgetPipeline', () => {
pipeline: mockCopy.pipeline,
hasCi: true,
ciStatus: 'success',
+ troubleshootingDocsPath: 'help',
});
expect(vm.$el.querySelector('.js-mini-pipeline-graph')).toEqual(null);
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
index b453d180a40..14d6e8d7556 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
@@ -39,13 +39,16 @@ describe('Merge request widget rebase component', () => {
});
it('it should render rebase button and warning message', () => {
- const text = vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim();
+ const text = vm.$el
+ .querySelector('.rebase-state-find-class-convention span')
+ .textContent.trim();
+
expect(text).toContain('Fast-forward merge is not possible.');
expect(text).toContain('Rebase the source branch onto the target branch or merge target');
expect(text).toContain('branch into source branch to allow this merge request to be merged.');
});
- it('it should render error message when it fails', (done) => {
+ it('it should render error message when it fails', done => {
vm.rebasingError = 'Something went wrong!';
Vue.nextTick(() => {
@@ -68,7 +71,9 @@ describe('Merge request widget rebase component', () => {
service: {},
});
- const text = vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim();
+ const text = vm.$el
+ .querySelector('.rebase-state-find-class-convention span')
+ .textContent.trim();
expect(text).toContain('Fast-forward merge is not possible.');
expect(text).toContain('Rebase the source branch onto');
@@ -78,7 +83,7 @@ describe('Merge request widget rebase component', () => {
});
describe('methods', () => {
- it('checkRebaseStatus', (done) => {
+ it('checkRebaseStatus', done => {
spyOn(eventHub, '$emit');
vm = mountComponent(Component, {
mr: {},
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
index 5de6ac4079d..7a5d0efdea5 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
@@ -5,7 +5,7 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetRelatedLinks', () => {
let vm;
- const createComponent = (data) => {
+ const createComponent = data => {
const Component = Vue.extend(relatedLinksComponent);
return mountComponent(Component, data);
@@ -19,16 +19,19 @@ describe('MRWidgetRelatedLinks', () => {
describe('closesText', () => {
it('returns Closes text for open merge request', () => {
vm = createComponent({ state: 'open', relatedLinks: {} });
+
expect(vm.closesText).toEqual('Closes');
});
it('returns correct text for closed merge request', () => {
vm = createComponent({ state: 'closed', relatedLinks: {} });
+
expect(vm.closesText).toEqual('Did not close');
});
it('returns correct tense for merged request', () => {
vm = createComponent({ state: 'merged', relatedLinks: {} });
+
expect(vm.closesText).toEqual('Closed');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
index 0b25500caf4..a0a336ae604 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
@@ -17,6 +17,7 @@ describe('MR widget status icon component', () => {
describe('while loading', () => {
it('renders loading icon', () => {
vm = mountComponent(Component, { status: 'loading' });
+
expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner');
});
});
@@ -24,6 +25,7 @@ describe('MR widget status icon component', () => {
describe('with status icon', () => {
it('renders ci status icon', () => {
vm = mountComponent(Component, { status: 'failed' });
+
expect(vm.$el.querySelector('.js-ci-status-icon-failed')).not.toBeNull();
});
});
@@ -31,6 +33,7 @@ describe('MR widget status icon component', () => {
describe('with disabled button', () => {
it('renders a disabled button', () => {
vm = mountComponent(Component, { status: 'failed', showDisabledButton: true });
+
expect(vm.$el.querySelector('.js-disabled-merge-button').textContent.trim()).toEqual('Merge');
});
});
@@ -38,6 +41,7 @@ describe('MR widget status icon component', () => {
describe('without disabled button', () => {
it('does not render a disabled button', () => {
vm = mountComponent(Component, { status: 'failed' });
+
expect(vm.$el.querySelector('.js-disabled-merge-button')).toBeNull();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/review_app_link_spec.js b/spec/javascripts/vue_mr_widget/components/review_app_link_spec.js
new file mode 100644
index 00000000000..68a65bd21c6
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/review_app_link_spec.js
@@ -0,0 +1,38 @@
+import Vue from 'vue';
+import component from '~/vue_merge_request_widget/components/review_app_link.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('review app link', () => {
+ const Component = Vue.extend(component);
+ const props = {
+ link: '/review',
+ cssClass: 'js-link',
+ };
+ let vm;
+ let el;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ el = vm.$el;
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders provided link as href attribute', () => {
+ expect(el.getAttribute('href')).toEqual(props.link);
+ });
+
+ it('renders provided cssClass as class attribute', () => {
+ expect(el.getAttribute('class')).toEqual(props.cssClass);
+ });
+
+ it('renders View app text', () => {
+ expect(el.textContent.trim()).toEqual('View app');
+ });
+
+ it('renders svg icon', () => {
+ expect(el.querySelector('svg')).not.toBeNull();
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
index e818f87b4c8..b90f5881a4d 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
@@ -24,8 +24,8 @@ describe('MRWidgetArchived', () => {
});
it('renders information', () => {
- expect(
- vm.$el.querySelector('.bold').textContent.trim(),
- ).toEqual('This project is archived, write access has been disabled');
+ expect(vm.$el.querySelector('.bold').textContent.trim()).toEqual(
+ 'This project is archived, write access has been disabled',
+ );
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
index d069dc3fcc6..eb4fa0df727 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
@@ -30,7 +30,7 @@ describe('MRWidgetAutoMergeFailed', () => {
expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Refresh');
});
- it('emits event and shows loading icon when button is clicked', (done) => {
+ it('emits event and shows loading icon when button is clicked', done => {
spyOn(eventHub, '$emit');
vm.$el.querySelector('button').click();
@@ -38,9 +38,7 @@ describe('MRWidgetAutoMergeFailed', () => {
Vue.nextTick(() => {
expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
- expect(
- vm.$el.querySelector('button i').classList,
- ).toContain('fa-spinner');
+ expect(vm.$el.querySelector('button i').classList).toContain('fa-spinner');
done();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
index 658612aad3c..7da27bb8890 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
@@ -24,6 +24,8 @@ describe('MRWidgetChecking', () => {
});
it('renders information about merging', () => {
- expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual('Checking ability to merge automatically');
+ expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual(
+ 'Checking ability to merge automatically',
+ );
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
index 0e3c134d3ac..9523e7d5474 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
@@ -7,23 +7,26 @@ describe('MRWidgetClosed', () => {
beforeEach(() => {
const Component = Vue.extend(closedComponent);
- vm = mountComponent(Component, { mr: {
- metrics: {
- mergedBy: {},
- closedBy: {
- name: 'Administrator',
- username: 'root',
- webUrl: 'http://localhost:3000/root',
- avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ vm = mountComponent(Component, {
+ mr: {
+ metrics: {
+ mergedBy: {},
+ closedBy: {
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://localhost:3000/root',
+ avatarUrl:
+ 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ },
+ mergedAt: 'Jan 24, 2018 1:02pm GMT+0000',
+ closedAt: 'Jan 24, 2018 1:02pm GMT+0000',
+ readableMergedAt: '',
+ readableClosedAt: 'less than a minute ago',
},
- mergedAt: 'Jan 24, 2018 1:02pm GMT+0000',
- closedAt: 'Jan 24, 2018 1:02pm GMT+0000',
- readableMergedAt: '',
- readableClosedAt: 'less than a minute ago',
+ targetBranchPath: '/twitter/flight/commits/so_long_jquery',
+ targetBranch: 'so_long_jquery',
},
- targetBranchPath: '/twitter/flight/commits/so_long_jquery',
- targetBranch: 'so_long_jquery',
- } });
+ });
});
afterEach(() => {
@@ -36,23 +39,31 @@ describe('MRWidgetClosed', () => {
it('renders closed by information with author and time', () => {
expect(
- vm.$el.querySelector('.js-mr-widget-author').textContent.trim().replace(/\s\s+/g, ' '),
- ).toContain(
- 'Closed by Administrator less than a minute ago',
- );
+ vm.$el
+ .querySelector('.js-mr-widget-author')
+ .textContent.trim()
+ .replace(/\s\s+/g, ' '),
+ ).toContain('Closed by Administrator less than a minute ago');
});
it('links to the user that closed the MR', () => {
- expect(vm.$el.querySelector('.author-link').getAttribute('href')).toEqual('http://localhost:3000/root');
+ expect(vm.$el.querySelector('.author-link').getAttribute('href')).toEqual(
+ 'http://localhost:3000/root',
+ );
});
it('renders information about the changes not being merged', () => {
expect(
- vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' '),
+ vm.$el
+ .querySelector('.mr-info-list')
+ .textContent.trim()
+ .replace(/\s\s+/g, ' '),
).toContain('The changes were not merged into so_long_jquery');
});
it('renders link for target branch', () => {
- expect(vm.$el.querySelector('.label-branch').getAttribute('href')).toEqual('/twitter/flight/commits/so_long_jquery');
+ expect(vm.$el.querySelector('.label-branch').getAttribute('href')).toEqual(
+ '/twitter/flight/commits/so_long_jquery',
+ );
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
index 3d05dbfa305..f9cd5c8bd3c 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
@@ -59,7 +59,9 @@ describe('MRWidgetConflicts', () => {
});
it('should show proper message', () => {
- expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('ask someone with write access');
+ expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain(
+ 'ask someone with write access',
+ );
});
it('should not have action buttons', () => {
@@ -79,9 +81,9 @@ describe('MRWidgetConflicts', () => {
});
it('should tell you to rebase locally', () => {
- expect(
- removeBreakLine(vm.$el.textContent).trim(),
- ).toContain('Fast-forward merge is not possible. To merge this request, first rebase locally.');
+ expect(removeBreakLine(vm.$el.textContent).trim()).toContain(
+ 'Fast-forward merge is not possible. To merge this request, first rebase locally.',
+ );
});
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
index 8de99fd3c96..3229ddd5e27 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
@@ -43,6 +43,7 @@ describe('MRWidgetFailedToMerge', () => {
expect(vm.timerText).toEqual('Refreshing in 10 seconds to show the updated status...');
vm.timer = 1;
+
expect(vm.timerText).toEqual('Refreshing in a second to show the updated status...');
});
});
@@ -73,6 +74,7 @@ describe('MRWidgetFailedToMerge', () => {
expect(vm.isRefreshing).toEqual(false);
vm.refresh();
+
expect(vm.isRefreshing).toEqual(true);
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
expect(eventHub.$emit).toHaveBeenCalledWith('EnablePolling');
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
index d47815a5b5a..d46ad0acc9b 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
@@ -42,22 +42,27 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
it('should return false when user id is not the same with who set the MWPS', () => {
vm.mr.mergeUserId = 2;
+
expect(vm.canRemoveSourceBranch).toBeFalsy();
vm.mr.currentUserId = 2;
+
expect(vm.canRemoveSourceBranch).toBeTruthy();
vm.mr.currentUserId = 3;
+
expect(vm.canRemoveSourceBranch).toBeFalsy();
});
it('should return false when shouldRemoveSourceBranch set to false', () => {
vm.mr.shouldRemoveSourceBranch = true;
+
expect(vm.canRemoveSourceBranch).toBeFalsy();
});
it('should return false if user is not able to remove the source branch', () => {
vm.mr.canRemoveSourceBranch = false;
+
expect(vm.canRemoveSourceBranch).toBeFalsy();
});
});
@@ -65,15 +70,17 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
describe('methods', () => {
describe('cancelAutomaticMerge', () => {
- it('should set flag and call service then tell main component to update the widget with data', (done) => {
+ it('should set flag and call service then tell main component to update the widget with data', done => {
const mrObj = {
is_new_mr_data: true,
};
- spyOn(vm.service, 'cancelAutomaticMerge').and.returnValue(new Promise((resolve) => {
- resolve({
- data: mrObj,
- });
- }));
+ spyOn(vm.service, 'cancelAutomaticMerge').and.returnValue(
+ new Promise(resolve => {
+ resolve({
+ data: mrObj,
+ });
+ }),
+ );
vm.cancelAutomaticMerge();
setTimeout(() => {
@@ -85,12 +92,14 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
});
describe('removeSourceBranch', () => {
- it('should set flag and call service then request main component to update the widget', (done) => {
- spyOn(vm.service, 'merge').and.returnValue(Promise.resolve({
- data: {
- status: 'merge_when_pipeline_succeeds',
- },
- }));
+ it('should set flag and call service then request main component to update the widget', done => {
+ spyOn(vm.service, 'merge').and.returnValue(
+ Promise.resolve({
+ data: {
+ status: 'merge_when_pipeline_succeeds',
+ },
+ }),
+ );
vm.removeSourceBranch();
setTimeout(() => {
@@ -113,13 +122,19 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
expect(vm.$el.innerText).toContain('The changes will be merged into');
expect(vm.$el.innerText).toContain(targetBranch);
expect(vm.$el.innerText).toContain('The source branch will not be removed');
- expect(vm.$el.querySelector('.js-cancel-auto-merge').innerText).toContain('Cancel automatic merge');
+ expect(vm.$el.querySelector('.js-cancel-auto-merge').innerText).toContain(
+ 'Cancel automatic merge',
+ );
+
expect(vm.$el.querySelector('.js-cancel-auto-merge').getAttribute('disabled')).toBeFalsy();
- expect(vm.$el.querySelector('.js-remove-source-branch').innerText).toContain('Remove source branch');
+ expect(vm.$el.querySelector('.js-remove-source-branch').innerText).toContain(
+ 'Remove source branch',
+ );
+
expect(vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeFalsy();
});
- it('should disable cancel auto merge button when the action is in progress', (done) => {
+ it('should disable cancel auto merge button when the action is in progress', done => {
vm.isCancellingAutoMerge = true;
Vue.nextTick(() => {
@@ -128,18 +143,19 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
});
});
- it('should show source branch will be removed text when it source branch set to remove', (done) => {
+ it('should show source branch will be removed text when it source branch set to remove', done => {
vm.mr.shouldRemoveSourceBranch = true;
Vue.nextTick(() => {
const normalizedText = vm.$el.innerText.replace(/\s+/g, ' ');
+
expect(normalizedText).toContain('The source branch will be removed');
expect(normalizedText).not.toContain('The source branch will not be removed');
done();
});
});
- it('should not show remove source branch button when user not able to remove source branch', (done) => {
+ it('should not show remove source branch button when user not able to remove source branch', done => {
vm.mr.currentUserId = 4;
Vue.nextTick(() => {
@@ -148,11 +164,13 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
});
});
- it('should disable remove source branch button when the action is in progress', (done) => {
+ it('should disable remove source branch button when the action is in progress', done => {
vm.isRemovingSourceBranch = true;
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeTruthy();
+ expect(
+ vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled'),
+ ).toBeTruthy();
done();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index efa5c878678..da5cb752c6f 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -30,7 +30,8 @@ describe('MRWidgetMerged', () => {
name: 'Administrator',
username: 'root',
webUrl: 'http://localhost:3000/root',
- avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatarUrl:
+ 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
},
mergedAt: 'Jan 24, 2018 1:02pm GMT+0000',
readableMergedAt: '',
@@ -41,7 +42,8 @@ describe('MRWidgetMerged', () => {
updatedAt: 'mergedUpdatedAt',
shortMergeCommitSha: '958c0475',
mergeCommitSha: '958c047516e182dfc52317f721f696e8a1ee85ed',
- mergeCommitPath: 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d',
+ mergeCommitPath:
+ 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d',
sourceBranch: 'bar',
targetBranch,
};
@@ -63,23 +65,27 @@ describe('MRWidgetMerged', () => {
describe('shouldShowRemoveSourceBranch', () => {
it('returns true when sourceBranchRemoved is false', () => {
vm.mr.sourceBranchRemoved = false;
+
expect(vm.shouldShowRemoveSourceBranch).toEqual(true);
});
- it('returns false wehn sourceBranchRemoved is true', () => {
+ it('returns false when sourceBranchRemoved is true', () => {
vm.mr.sourceBranchRemoved = true;
+
expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
});
it('returns false when canRemoveSourceBranch is false', () => {
vm.mr.sourceBranchRemoved = false;
vm.mr.canRemoveSourceBranch = false;
+
expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
});
it('returns false when is making request', () => {
vm.mr.canRemoveSourceBranch = true;
vm.isMakingRequest = true;
+
expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
});
@@ -87,6 +93,7 @@ describe('MRWidgetMerged', () => {
vm.mr.isRemovingSourceBranch = true;
vm.mr.canRemoveSourceBranch = true;
vm.isMakingRequest = true;
+
expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
});
});
@@ -94,17 +101,21 @@ describe('MRWidgetMerged', () => {
describe('shouldShowSourceBranchRemoving', () => {
it('should correct value when fields changed', () => {
vm.mr.sourceBranchRemoved = false;
+
expect(vm.shouldShowSourceBranchRemoving).toEqual(false);
vm.mr.sourceBranchRemoved = true;
+
expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
vm.mr.sourceBranchRemoved = false;
vm.isMakingRequest = true;
+
expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
vm.isMakingRequest = false;
vm.mr.isRemovingSourceBranch = true;
+
expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
});
});
@@ -112,18 +123,21 @@ describe('MRWidgetMerged', () => {
describe('methods', () => {
describe('removeSourceBranch', () => {
- it('should set flag and call service then request main component to update the widget', (done) => {
- spyOn(vm.service, 'removeSourceBranch').and.returnValue(new Promise((resolve) => {
- resolve({
- data: {
- message: 'Branch was removed',
- },
- });
- }));
+ it('should set flag and call service then request main component to update the widget', done => {
+ spyOn(vm.service, 'removeSourceBranch').and.returnValue(
+ new Promise(resolve => {
+ resolve({
+ data: {
+ message: 'Branch was removed',
+ },
+ });
+ }),
+ );
vm.removeSourceBranch();
setTimeout(() => {
const args = eventHub.$emit.calls.argsFor(0);
+
expect(vm.isMakingRequest).toEqual(true);
expect(args[0]).toEqual('MRWidgetUpdateRequested');
expect(args[1]).not.toThrow();
@@ -154,7 +168,19 @@ describe('MRWidgetMerged', () => {
it('shows button to copy commit SHA to clipboard', () => {
expect(selectors.copyMergeShaButton).toExist();
- expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(vm.mr.mergeCommitSha);
+ expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(
+ vm.mr.mergeCommitSha,
+ );
+ });
+
+ it('hides button to copy commit SHA if SHA does not exist', done => {
+ vm.mr.mergeCommitSha = null;
+
+ Vue.nextTick(() => {
+ expect(selectors.copyMergeShaButton).not.toExist();
+ expect(vm.$el.querySelector('.mr-info-list').innerText).not.toContain('with');
+ done();
+ });
});
it('shows merge commit SHA link', () => {
@@ -163,7 +189,7 @@ describe('MRWidgetMerged', () => {
expect(selectors.mergeCommitShaLink.href).toBe(vm.mr.mergeCommitPath);
});
- it('should not show source branch removed text', (done) => {
+ it('should not show source branch removed text', done => {
vm.mr.sourceBranchRemoved = false;
Vue.nextTick(() => {
@@ -173,7 +199,7 @@ describe('MRWidgetMerged', () => {
});
});
- it('should show source branch removing text', (done) => {
+ it('should show source branch removing text', done => {
vm.mr.isRemovingSourceBranch = true;
vm.mr.sourceBranchRemoved = false;
@@ -186,8 +212,8 @@ describe('MRWidgetMerged', () => {
});
it('should use mergedEvent mergedAt as tooltip title', () => {
- expect(
- vm.$el.querySelector('time').getAttribute('data-original-title'),
- ).toBe('Jan 24, 2018 1:02pm GMT+0000');
+ expect(vm.$el.querySelector('time').getAttribute('data-original-title')).toBe(
+ 'Jan 24, 2018 1:02pm GMT+0000',
+ );
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
index d2d219e4bdb..57773d1648a 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
@@ -7,10 +7,12 @@ describe('MRWidgetMerging', () => {
beforeEach(() => {
const Component = Vue.extend(mergingComponent);
- vm = mountComponent(Component, { mr: {
- targetBranchPath: '/branch-path',
- targetBranch: 'branch',
- } });
+ vm = mountComponent(Component, {
+ mr: {
+ targetBranchPath: '/branch-path',
+ targetBranch: 'branch',
+ },
+ });
});
afterEach(() => {
@@ -19,16 +21,23 @@ describe('MRWidgetMerging', () => {
it('renders information about merge request being merged', () => {
expect(
- vm.$el.querySelector('.media-body').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '),
+ vm.$el
+ .querySelector('.media-body')
+ .textContent.trim()
+ .replace(/\s\s+/g, ' ')
+ .replace(/[\r\n]+/g, ' '),
).toContain('This merge request is in the process of being merged');
});
it('renders branch information', () => {
expect(
- vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '),
+ vm.$el
+ .querySelector('.mr-info-list')
+ .textContent.trim()
+ .replace(/\s\s+/g, ' ')
+ .replace(/[\r\n]+/g, ' '),
).toEqual('The changes will be merged into branch');
- expect(
- vm.$el.querySelector('a').getAttribute('href'),
- ).toEqual('/branch-path');
+
+ expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('/branch-path');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
index 34f76b39b28..096301837c4 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
@@ -20,6 +20,7 @@ describe('MRWidgetMissingBranch', () => {
expect(vm.missingBranchName).toEqual('source');
vm.mr.sourceBranchRemoved = false;
+
expect(vm.missingBranchName).toEqual('target');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js
index 9f8b96c118b..6b95ca3460b 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js
@@ -19,6 +19,8 @@ describe('MRWidgetNotAllowed', () => {
it('renders informative text', () => {
expect(vm.$el.innerText).toContain('Ready to be merged automatically.');
- expect(vm.$el.innerText).toContain('Ask someone with write access to this repository to merge this request');
+ expect(vm.$el.innerText).toContain(
+ 'Ask someone with write access to this repository to merge this request',
+ );
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js
index 2a762c9336e..babb8cea0ab 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js
@@ -15,7 +15,10 @@ describe('NothingToMerge', () => {
it('should have correct elements', () => {
expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
expect(vm.$el.querySelector('a').href).toContain(newBlobPath);
- expect(vm.$el.innerText).toContain('Currently there are no changes in this merge request\'s source branch');
+ expect(vm.$el.innerText).toContain(
+ "Currently there are no changes in this merge request's source branch",
+ );
+
expect(vm.$el.innerText).toContain('Please push new commits or use a different branch.');
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js
index ab096a56918..477041fa383 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js
@@ -19,8 +19,8 @@ describe('MRWidgetPipelineBlocked', () => {
});
it('renders information text', () => {
- expect(
- removeBreakLine(vm.$el.textContent).trim(),
- ).toContain('Pipeline blocked. The pipeline for this merge request requires a manual action to proceed');
+ expect(removeBreakLine(vm.$el.textContent).trim()).toContain(
+ 'Pipeline blocked. The pipeline for this merge request requires a manual action to proceed',
+ );
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js
index 5573d7c5c93..f7523a01963 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js
@@ -11,9 +11,9 @@ describe('PipelineFailed', () => {
it('should have correct elements', () => {
expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
expect(vm.$el.querySelector('button').getAttribute('disabled')).toBeTruthy();
- expect(
- removeBreakLine(vm.$el.innerText).trim(),
- ).toContain('The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure');
+ expect(removeBreakLine(vm.$el.innerText).trim()).toContain(
+ 'The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure',
+ );
});
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 81c16593eb4..2119a3b927a 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -76,11 +76,13 @@ describe('ReadyToMerge', () => {
describe('shouldShowMergeWhenPipelineSucceedsText', () => {
it('should return true with active pipeline', () => {
vm.mr.isPipelineActive = true;
+
expect(vm.shouldShowMergeWhenPipelineSucceedsText).toBeTruthy();
});
it('should return false with inactive pipeline', () => {
vm.mr.isPipelineActive = false;
+
expect(vm.shouldShowMergeWhenPipelineSucceedsText).toBeFalsy();
});
});
@@ -95,6 +97,7 @@ describe('ReadyToMerge', () => {
it('should return message without description', () => {
vm.useCommitMessageWithDescription = true;
+
expect(vm.commitMessageLinkTitle).toEqual(withoutDesc);
});
});
@@ -102,11 +105,13 @@ describe('ReadyToMerge', () => {
describe('status', () => {
it('defaults to success', () => {
vm.mr.pipeline = true;
+
expect(vm.status).toEqual('success');
});
it('returns failed when MR has CI but also has an unknown status', () => {
vm.mr.hasCI = true;
+
expect(vm.status).toEqual('failed');
});
@@ -117,12 +122,14 @@ describe('ReadyToMerge', () => {
it('returns pending when pipeline is active', () => {
vm.mr.pipeline = {};
vm.mr.isPipelineActive = true;
+
expect(vm.status).toEqual('pending');
});
it('returns failed when pipeline is failed', () => {
vm.mr.pipeline = {};
vm.mr.isPipelineFailed = true;
+
expect(vm.status).toEqual('failed');
});
});
@@ -138,17 +145,20 @@ describe('ReadyToMerge', () => {
it('returns success class for success status', () => {
vm.mr.pipeline = true;
+
expect(vm.mergeButtonClass).toEqual(defaultClass);
});
it('returns info class for pending status', () => {
vm.mr.pipeline = {};
vm.mr.isPipelineActive = true;
+
expect(vm.mergeButtonClass).toEqual(inActionClass);
});
it('returns failed class for failed status', () => {
vm.mr.hasCI = true;
+
expect(vm.mergeButtonClass).toEqual(failedClass);
});
});
@@ -160,22 +170,26 @@ describe('ReadyToMerge', () => {
it('shows tick for success status', () => {
vm.mr.pipeline = true;
+
expect(vm.iconClass).toEqual('success');
});
it('shows tick for pending status', () => {
vm.mr.pipeline = {};
vm.mr.isPipelineActive = true;
+
expect(vm.iconClass).toEqual('success');
});
it('shows warning icon for failed status', () => {
vm.mr.hasCI = true;
+
expect(vm.iconClass).toEqual('warning');
});
it('shows warning icon for merge not allowed', () => {
vm.mr.hasCI = true;
+
expect(vm.iconClass).toEqual('warning');
});
});
@@ -187,12 +201,14 @@ describe('ReadyToMerge', () => {
it('should return Merge in progress', () => {
vm.isMergingImmediately = true;
+
expect(vm.mergeButtonText).toEqual('Merge in progress');
});
it('should return Merge when pipeline succeeds', () => {
vm.isMergingImmediately = false;
vm.mr.isPipelineActive = true;
+
expect(vm.mergeButtonText).toEqual('Merge when pipeline succeeds');
});
});
@@ -204,12 +220,14 @@ describe('ReadyToMerge', () => {
it('should return true when pipeline active', () => {
vm.mr.isPipelineActive = true;
+
expect(vm.shouldShowMergeOptionsDropdown).toBeTruthy();
});
it('should return false when pipeline active but only merge when pipeline succeeds set in project options', () => {
vm.mr.isPipelineActive = true;
vm.mr.onlyAllowMergeIfPipelineSucceeds = true;
+
expect(vm.shouldShowMergeOptionsDropdown).toBeFalsy();
});
});
@@ -217,24 +235,28 @@ describe('ReadyToMerge', () => {
describe('isMergeButtonDisabled', () => {
it('should return false with initial data', () => {
vm.mr.isMergeAllowed = true;
+
expect(vm.isMergeButtonDisabled).toBeFalsy();
});
it('should return true when there is no commit message', () => {
vm.mr.isMergeAllowed = true;
vm.commitMessage = '';
+
expect(vm.isMergeButtonDisabled).toBeTruthy();
});
it('should return true if merge is not allowed', () => {
vm.mr.isMergeAllowed = false;
vm.mr.onlyAllowMergeIfPipelineSucceeds = true;
+
expect(vm.isMergeButtonDisabled).toBeTruthy();
});
it('should return true when the vm instance is making request', () => {
vm.mr.isMergeAllowed = true;
vm.isMakingRequest = true;
+
expect(vm.isMergeButtonDisabled).toBeTruthy();
});
});
@@ -245,24 +267,28 @@ describe('ReadyToMerge', () => {
it('should return false when an external pipeline is running and required to succeed', () => {
vm.mr.isMergeAllowed = false;
vm.mr.isPipelineActive = false;
+
expect(vm.shouldShowMergeControls()).toBeFalsy();
});
it('should return true when the build succeeded or build not required to succeed', () => {
vm.mr.isMergeAllowed = true;
vm.mr.isPipelineActive = false;
+
expect(vm.shouldShowMergeControls()).toBeTruthy();
});
it('should return true when showing the MWPS button and a pipeline is running that needs to be successful', () => {
vm.mr.isMergeAllowed = false;
vm.mr.isPipelineActive = true;
+
expect(vm.shouldShowMergeControls()).toBeTruthy();
});
it('should return true when showing the MWPS button but not required for the pipeline to succeed', () => {
vm.mr.isMergeAllowed = true;
vm.mr.isPipelineActive = true;
+
expect(vm.shouldShowMergeControls()).toBeTruthy();
});
});
@@ -272,9 +298,11 @@ describe('ReadyToMerge', () => {
expect(vm.useCommitMessageWithDescription).toBeFalsy();
expect(vm.commitMessage).toEqual(commitMessage);
vm.updateCommitMessage();
+
expect(vm.useCommitMessageWithDescription).toBeTruthy();
expect(vm.commitMessage).toEqual(commitMessageWithDescription);
vm.updateCommitMessage();
+
expect(vm.useCommitMessageWithDescription).toBeFalsy();
expect(vm.commitMessage).toEqual(commitMessage);
});
@@ -284,20 +312,22 @@ describe('ReadyToMerge', () => {
it('should toggle showCommitMessageEditor flag', () => {
expect(vm.showCommitMessageEditor).toBeFalsy();
vm.toggleCommitMessageEditor();
+
expect(vm.showCommitMessageEditor).toBeTruthy();
});
});
describe('handleMergeButtonClick', () => {
- const returnPromise = status => new Promise((resolve) => {
- resolve({
- data: {
- status,
- },
+ const returnPromise = status =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ status,
+ },
+ });
});
- });
- it('should handle merge when pipeline succeeds', (done) => {
+ it('should handle merge when pipeline succeeds', done => {
spyOn(eventHub, '$emit');
spyOn(vm.service, 'merge').and.returnValue(returnPromise('merge_when_pipeline_succeeds'));
vm.removeSourceBranch = false;
@@ -309,6 +339,7 @@ describe('ReadyToMerge', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
const params = vm.service.merge.calls.argsFor(0)[0];
+
expect(params.sha).toEqual(vm.mr.sha);
expect(params.commit_message).toEqual(vm.mr.commitMessage);
expect(params.should_remove_source_branch).toBeFalsy();
@@ -317,7 +348,7 @@ describe('ReadyToMerge', () => {
}, 333);
});
- it('should handle merge failed', (done) => {
+ it('should handle merge failed', done => {
spyOn(eventHub, '$emit');
spyOn(vm.service, 'merge').and.returnValue(returnPromise('failed'));
vm.handleMergeButtonClick(false, true);
@@ -328,13 +359,14 @@ describe('ReadyToMerge', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('FailedToMerge', undefined);
const params = vm.service.merge.calls.argsFor(0)[0];
+
expect(params.should_remove_source_branch).toBeTruthy();
expect(params.merge_when_pipeline_succeeds).toBeFalsy();
done();
}, 333);
});
- it('should handle merge action accepted case', (done) => {
+ it('should handle merge action accepted case', done => {
spyOn(vm.service, 'merge').and.returnValue(returnPromise('success'));
spyOn(vm, 'initiateMergePolling');
vm.handleMergeButtonClick();
@@ -345,6 +377,7 @@ describe('ReadyToMerge', () => {
expect(vm.initiateMergePolling).toHaveBeenCalled();
const params = vm.service.merge.calls.argsFor(0)[0];
+
expect(params.should_remove_source_branch).toBeTruthy();
expect(params.merge_when_pipeline_succeeds).toBeFalsy();
done();
@@ -356,25 +389,27 @@ describe('ReadyToMerge', () => {
it('should call simplePoll', () => {
const simplePoll = spyOnDependency(ReadyToMerge, 'simplePoll');
vm.initiateMergePolling();
+
expect(simplePoll).toHaveBeenCalled();
});
});
describe('handleMergePolling', () => {
- const returnPromise = state => new Promise((resolve) => {
- resolve({
- data: {
- state,
- source_branch_exists: true,
- },
+ const returnPromise = state =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ state,
+ source_branch_exists: true,
+ },
+ });
});
- });
beforeEach(() => {
loadFixtures('merge_requests/merge_request_of_current_user.html.raw');
});
- it('should call start and stop polling when MR merged', (done) => {
+ it('should call start and stop polling when MR merged', done => {
spyOn(eventHub, '$emit');
spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
@@ -382,7 +417,14 @@ describe('ReadyToMerge', () => {
let cpc = false; // continuePollingCalled
let spc = false; // stopPollingCalled
- vm.handleMergePolling(() => { cpc = true; }, () => { spc = true; });
+ vm.handleMergePolling(
+ () => {
+ cpc = true;
+ },
+ () => {
+ spc = true;
+ },
+ );
setTimeout(() => {
expect(vm.service.poll).toHaveBeenCalled();
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
@@ -395,7 +437,7 @@ describe('ReadyToMerge', () => {
}, 333);
});
- it('updates status box', (done) => {
+ it('updates status box', done => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
@@ -403,6 +445,7 @@ describe('ReadyToMerge', () => {
setTimeout(() => {
const statusBox = document.querySelector('.status-box');
+
expect(statusBox.classList.contains('status-box-mr-merged')).toBeTruthy();
expect(statusBox.textContent).toContain('Merged');
@@ -410,7 +453,7 @@ describe('ReadyToMerge', () => {
});
});
- it('hides close button', (done) => {
+ it('hides close button', done => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
@@ -423,7 +466,7 @@ describe('ReadyToMerge', () => {
});
});
- it('updates merge request count badge', (done) => {
+ it('updates merge request count badge', done => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
@@ -436,14 +479,21 @@ describe('ReadyToMerge', () => {
});
});
- it('should continue polling until MR is merged', (done) => {
+ it('should continue polling until MR is merged', done => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise('some_other_state'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
let cpc = false; // continuePollingCalled
let spc = false; // stopPollingCalled
- vm.handleMergePolling(() => { cpc = true; }, () => { spc = true; });
+ vm.handleMergePolling(
+ () => {
+ cpc = true;
+ },
+ () => {
+ spc = true;
+ },
+ );
setTimeout(() => {
expect(cpc).toBeTruthy();
expect(spc).toBeFalsy();
@@ -459,35 +509,46 @@ describe('ReadyToMerge', () => {
const simplePoll = spyOnDependency(ReadyToMerge, 'simplePoll');
vm.initiateRemoveSourceBranchPolling();
+
expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [true]);
expect(simplePoll).toHaveBeenCalled();
});
});
describe('handleRemoveBranchPolling', () => {
- const returnPromise = state => new Promise((resolve) => {
- resolve({
- data: {
- source_branch_exists: state,
- },
+ const returnPromise = state =>
+ new Promise(resolve => {
+ resolve({
+ data: {
+ source_branch_exists: state,
+ },
+ });
});
- });
- it('should call start and stop polling when MR merged', (done) => {
+ it('should call start and stop polling when MR merged', done => {
spyOn(eventHub, '$emit');
spyOn(vm.service, 'poll').and.returnValue(returnPromise(false));
let cpc = false; // continuePollingCalled
let spc = false; // stopPollingCalled
- vm.handleRemoveBranchPolling(() => { cpc = true; }, () => { spc = true; });
+ vm.handleRemoveBranchPolling(
+ () => {
+ cpc = true;
+ },
+ () => {
+ spc = true;
+ },
+ );
setTimeout(() => {
expect(vm.service.poll).toHaveBeenCalled();
const args = eventHub.$emit.calls.argsFor(0);
+
expect(args[0]).toEqual('MRWidgetUpdateRequested');
expect(args[1]).toBeDefined();
args[1]();
+
expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [false]);
expect(cpc).toBeFalsy();
@@ -497,13 +558,20 @@ describe('ReadyToMerge', () => {
}, 333);
});
- it('should continue polling until MR is merged', (done) => {
+ it('should continue polling until MR is merged', done => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise(true));
let cpc = false; // continuePollingCalled
let spc = false; // stopPollingCalled
- vm.handleRemoveBranchPolling(() => { cpc = true; }, () => { spc = true; });
+ vm.handleRemoveBranchPolling(
+ () => {
+ cpc = true;
+ },
+ () => {
+ spc = true;
+ },
+ );
setTimeout(() => {
expect(cpc).toBeTruthy();
expect(spc).toBeFalsy();
@@ -518,6 +586,7 @@ describe('ReadyToMerge', () => {
describe('when user can merge but cannot delete branch', () => {
it('should be disabled in the rendered output', () => {
const checkboxElement = vm.$el.querySelector('#remove-source-branch-input');
+
expect(checkboxElement).toBeNull();
});
});
@@ -537,6 +606,7 @@ describe('ReadyToMerge', () => {
it('should be enabled in rendered output', () => {
const checkboxElement = customVm.$el.querySelector('#remove-source-branch-input');
+
expect(checkboxElement).not.toBeNull();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js
index abf642c166a..36f8c7a9683 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js
@@ -18,8 +18,8 @@ describe('ShaMismatch', () => {
it('should render information message', () => {
expect(vm.$el.querySelector('button').disabled).toEqual(true);
- expect(
- removeBreakLine(vm.$el.textContent).trim(),
- ).toContain('The source branch HEAD has recently changed. Please reload the page and review the changes before merging');
+ expect(removeBreakLine(vm.$el.textContent).trim()).toContain(
+ 'The source branch HEAD has recently changed. Please reload the page and review the changes before merging',
+ );
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js
index d797f1266df..bd64d7b2926 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js
@@ -12,13 +12,18 @@ describe('UnresolvedDiscussions', () => {
describe('with discussions path', () => {
beforeEach(() => {
- vm = mountComponent(Component, { mr: {
- createIssueToResolveDiscussionsPath: gl.TEST_HOST,
- } });
+ vm = mountComponent(Component, {
+ mr: {
+ createIssueToResolveDiscussionsPath: gl.TEST_HOST,
+ },
+ });
});
it('should have correct elements', () => {
- expect(vm.$el.innerText).toContain('There are unresolved discussions. Please resolve these discussions');
+ expect(vm.$el.innerText).toContain(
+ 'There are unresolved discussions. Please resolve these discussions',
+ );
+
expect(vm.$el.innerText).toContain('Create an issue to resolve them later');
expect(vm.$el.querySelector('.js-create-issue').getAttribute('href')).toEqual(gl.TEST_HOST);
});
@@ -30,7 +35,10 @@ describe('UnresolvedDiscussions', () => {
});
it('should not show create issue link if user cannot create issue', () => {
- expect(vm.$el.innerText).toContain('There are unresolved discussions. Please resolve these discussions');
+ expect(vm.$el.innerText).toContain(
+ 'There are unresolved discussions. Please resolve these discussions',
+ );
+
expect(vm.$el.querySelector('.js-create-issue')).toEqual(null);
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
index cea603368bf..88937df2f7b 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
@@ -33,6 +33,7 @@ describe('Wip', () => {
describe('data', () => {
it('should have default data', () => {
const vm = createComponent();
+
expect(vm.isMakingRequest).toBeFalsy();
});
});
@@ -43,22 +44,27 @@ describe('Wip', () => {
};
describe('removeWIP', () => {
- it('should make a request to service and handle response', (done) => {
+ it('should make a request to service and handle response', done => {
const vm = createComponent();
spyOn(window, 'Flash').and.returnValue(true);
spyOn(eventHub, '$emit');
- spyOn(vm.service, 'removeWIP').and.returnValue(new Promise((resolve) => {
- resolve({
- data: mrObj,
- });
- }));
+ spyOn(vm.service, 'removeWIP').and.returnValue(
+ new Promise(resolve => {
+ resolve({
+ data: mrObj,
+ });
+ }),
+ );
vm.removeWIP();
setTimeout(() => {
expect(vm.isMakingRequest).toBeTruthy();
expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj);
- expect(window.Flash).toHaveBeenCalledWith('The merge request can now be merged.', 'notice');
+ expect(window.Flash).toHaveBeenCalledWith(
+ 'The merge request can now be merged.',
+ 'notice',
+ );
done();
}, 333);
});
@@ -82,7 +88,7 @@ describe('Wip', () => {
expect(el.querySelector('.js-remove-wip').innerText).toContain('Resolve WIP status');
});
- it('should not show removeWIP button is user cannot update MR', (done) => {
+ it('should not show removeWIP button is user cannot update MR', done => {
vm.mr.removeWIPPath = '';
Vue.nextTick(() => {
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 7fd1a2350f7..17554c4fe42 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -218,5 +218,7 @@ export default {
diverged_commits_count: 0,
only_allow_merge_if_pipeline_succeeds: false,
commit_change_content_path: '/root/acets-app/merge_requests/22/commit_change_content',
- merge_commit_path: 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775',
+ merge_commit_path:
+ 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775',
+ troubleshooting_docs_path: 'help',
};
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 6342ea00436..f72bf627c10 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -7,11 +7,12 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper';
import mockData from './mock_data';
import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from '../lib/utils/mock_data';
-const returnPromise = data => new Promise((resolve) => {
- resolve({
- data,
+const returnPromise = data =>
+ new Promise(resolve => {
+ resolve({
+ data,
+ });
});
-});
describe('mrWidgetOptions', () => {
let vm;
@@ -27,6 +28,10 @@ describe('mrWidgetOptions', () => {
});
});
+ afterEach(() => {
+ vm.$destroy();
+ });
+
describe('data', () => {
it('should instantiate Store and Service', () => {
expect(vm.mr).toBeDefined();
@@ -42,6 +47,7 @@ describe('mrWidgetOptions', () => {
it('should return conflicts component', () => {
vm.mr.state = 'conflicts';
+
expect(vm.componentName).toEqual('mr-widget-conflicts');
});
});
@@ -53,6 +59,7 @@ describe('mrWidgetOptions', () => {
it('should return true for a state which requires help widget', () => {
vm.mr.state = 'conflicts';
+
expect(vm.shouldRenderMergeHelp).toBeTruthy();
});
});
@@ -78,6 +85,7 @@ describe('mrWidgetOptions', () => {
it('should return true if there is relatedLinks in MR', () => {
Vue.set(vm.mr, 'relatedLinks', {});
+
expect(vm.shouldRenderRelatedLinks).toBeTruthy();
});
});
@@ -128,7 +136,7 @@ describe('mrWidgetOptions', () => {
describe('methods', () => {
describe('checkStatus', () => {
- it('should tell service to check status', (done) => {
+ it('should tell service to check status', done => {
spyOn(vm.service, 'checkStatus').and.returnValue(returnPromise(mockData));
spyOn(vm.mr, 'setData');
spyOn(vm, 'handleNotification');
@@ -178,10 +186,10 @@ describe('mrWidgetOptions', () => {
});
describe('fetchDeployments', () => {
- it('should fetch deployments', (done) => {
+ it('should fetch deployments', done => {
spyOn(vm.service, 'fetchDeployments').and.returnValue(returnPromise([{ id: 1 }]));
- vm.fetchDeployments();
+ vm.fetchPreMergeDeployments();
setTimeout(() => {
expect(vm.service.fetchDeployments).toHaveBeenCalled();
@@ -193,7 +201,7 @@ describe('mrWidgetOptions', () => {
});
describe('fetchActionsContent', () => {
- it('should fetch content of Cherry Pick and Revert modals', (done) => {
+ it('should fetch content of Cherry Pick and Revert modals', done => {
spyOn(vm.service, 'fetchMergeActionsContent').and.returnValue(returnPromise('hello world'));
vm.fetchActionsContent();
@@ -219,18 +227,23 @@ describe('mrWidgetOptions', () => {
vm.bindEventHubListeners();
eventHub.$emit('SetBranchRemoveFlag', ['flag']);
+
expect(vm.mr.isRemovingSourceBranch).toEqual('flag');
eventHub.$emit('FailedToMerge');
+
expect(vm.mr.state).toEqual('failedToMerge');
eventHub.$emit('UpdateWidgetData', mockData);
+
expect(vm.mr.setData).toHaveBeenCalledWith(mockData);
eventHub.$emit('EnablePolling');
+
expect(vm.resumePolling).toHaveBeenCalled();
eventHub.$emit('DisablePolling');
+
expect(vm.stopPolling).toHaveBeenCalled();
const listenersWithServiceRequest = {
@@ -239,7 +252,7 @@ describe('mrWidgetOptions', () => {
};
const allArgs = eventHub.$on.calls.allArgs();
- allArgs.forEach((params) => {
+ allArgs.forEach(params => {
const eventName = params[0];
const callback = params[1];
@@ -249,22 +262,12 @@ describe('mrWidgetOptions', () => {
});
listenersWithServiceRequest.MRWidgetUpdateRequested();
+
expect(vm.checkStatus).toHaveBeenCalled();
listenersWithServiceRequest.FetchActionsContent();
- expect(vm.fetchActionsContent).toHaveBeenCalled();
- });
- });
- describe('handleMounted', () => {
- it('should call required methods to do the initial kick-off', () => {
- spyOn(vm, 'initDeploymentsPolling');
- spyOn(vm, 'setFaviconHelper');
-
- vm.handleMounted();
-
- expect(vm.setFaviconHelper).toHaveBeenCalled();
- expect(vm.initDeploymentsPolling).toHaveBeenCalled();
+ expect(vm.fetchActionsContent).toHaveBeenCalled();
});
});
@@ -284,13 +287,14 @@ describe('mrWidgetOptions', () => {
document.body.removeChild(document.getElementById('favicon'));
});
- it('should call setFavicon method', (done) => {
+ it('should call setFavicon method', done => {
vm.mr.ciStatusFaviconPath = overlayDataUrl;
- vm.setFaviconHelper().then(() => {
- expect(faviconElement.getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
- done();
- })
- .catch(done.fail);
+ vm.setFaviconHelper()
+ .then(() => {
+ expect(faviconElement.getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
+ done();
+ })
+ .catch(done.fail);
});
it('should not call setFavicon when there is no ciStatusFaviconPath', () => {
@@ -348,6 +352,7 @@ describe('mrWidgetOptions', () => {
spyOn(vm.pollingInterval, 'resume');
vm.resumePolling();
+
expect(vm.pollingInterval.resume).toHaveBeenCalled();
});
});
@@ -357,13 +362,14 @@ describe('mrWidgetOptions', () => {
spyOn(vm.pollingInterval, 'stopTimer');
vm.stopPolling();
+
expect(vm.pollingInterval.stopTimer).toHaveBeenCalled();
});
});
});
describe('rendering relatedLinks', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.mr.relatedLinks = {
assignToMe: null,
closing: `
@@ -380,7 +386,7 @@ describe('mrWidgetOptions', () => {
expect(vm.$el.querySelector('.close-related-link')).toBeDefined();
});
- it('does not render if state is nothingToMerge', (done) => {
+ it('does not render if state is nothingToMerge', done => {
vm.mr.state = stateKey.nothingToMerge;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.close-related-link')).toBeNull();
@@ -390,7 +396,7 @@ describe('mrWidgetOptions', () => {
});
describe('rendering source branch removal status', () => {
- it('renders when user cannot remove branch and branch should be removed', (done) => {
+ it('renders when user cannot remove branch and branch should be removed', done => {
vm.mr.canRemoveSourceBranch = false;
vm.mr.shouldRemoveSourceBranch = true;
vm.mr.state = 'readyToMerge';
@@ -407,7 +413,7 @@ describe('mrWidgetOptions', () => {
});
});
- it('does not render in merged state', (done) => {
+ it('does not render in merged state', done => {
vm.mr.canRemoveSourceBranch = false;
vm.mr.shouldRemoveSourceBranch = true;
vm.mr.state = 'merged';
@@ -422,6 +428,20 @@ describe('mrWidgetOptions', () => {
});
describe('rendering deployments', () => {
+ const changes = [
+ {
+ path: 'index.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
+ },
+ {
+ path: 'imgs/gallery.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
+ },
+ {
+ path: 'about/',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
+ },
+ ];
const deploymentMockData = {
id: 15,
name: 'review/diplo',
@@ -433,15 +453,20 @@ describe('mrWidgetOptions', () => {
external_url_formatted: 'diplo.',
deployed_at: '2017-03-22T22:44:42.258Z',
deployed_at_formatted: 'Mar 22, 2017 10:44pm',
+ changes,
+ status: 'success',
};
- beforeEach((done) => {
- vm.mr.deployments.push({
- ...deploymentMockData,
- }, {
- ...deploymentMockData,
- id: deploymentMockData.id + 1,
- });
+ beforeEach(done => {
+ vm.mr.deployments.push(
+ {
+ ...deploymentMockData,
+ },
+ {
+ ...deploymentMockData,
+ id: deploymentMockData.id + 1,
+ },
+ );
vm.$nextTick(done);
});
@@ -449,5 +474,201 @@ describe('mrWidgetOptions', () => {
it('renders multiple deployments', () => {
expect(vm.$el.querySelectorAll('.deploy-heading').length).toBe(2);
});
+
+ it('renders dropdpown with multiple file changes', () => {
+ expect(
+ vm.$el
+ .querySelector('.js-mr-wigdet-deployment-dropdown')
+ .querySelectorAll('.js-filtered-dropdown-result').length,
+ ).toEqual(changes.length);
+ });
+ });
+
+ describe('pipeline for target branch after merge', () => {
+ describe('with information for target branch pipeline', () => {
+ beforeEach(done => {
+ vm.mr.state = 'merged';
+ vm.mr.mergePipeline = {
+ id: 127,
+ user: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url: null,
+ web_url: 'http://localhost:3000/root',
+ status_tooltip_html: null,
+ path: '/root',
+ },
+ active: true,
+ coverage: null,
+ source: 'push',
+ created_at: '2018-10-22T11:41:35.186Z',
+ updated_at: '2018-10-22T11:41:35.433Z',
+ path: '/root/ci-web-terminal/pipelines/127',
+ flags: {
+ latest: true,
+ stuck: true,
+ auto_devops: false,
+ yaml_errors: false,
+ retryable: false,
+ cancelable: true,
+ failure_reason: false,
+ },
+ details: {
+ status: {
+ icon: 'status_pending',
+ text: 'pending',
+ label: 'pending',
+ group: 'pending',
+ tooltip: 'pending',
+ has_details: true,
+ details_path: '/root/ci-web-terminal/pipelines/127',
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
+ },
+ duration: null,
+ finished_at: null,
+ stages: [
+ {
+ name: 'test',
+ title: 'test: pending',
+ status: {
+ icon: 'status_pending',
+ text: 'pending',
+ label: 'pending',
+ group: 'pending',
+ tooltip: 'pending',
+ has_details: true,
+ details_path: '/root/ci-web-terminal/pipelines/127#test',
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
+ },
+ path: '/root/ci-web-terminal/pipelines/127#test',
+ dropdown_path: '/root/ci-web-terminal/pipelines/127/stage.json?stage=test',
+ },
+ ],
+ artifacts: [],
+ manual_actions: [],
+ scheduled_actions: [],
+ },
+ ref: {
+ name: 'master',
+ path: '/root/ci-web-terminal/commits/master',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: 'aa1939133d373c94879becb79d91828a892ee319',
+ short_id: 'aa193913',
+ title: "Merge branch 'master-test' into 'master'",
+ created_at: '2018-10-22T11:41:33.000Z',
+ parent_ids: [
+ '4622f4dd792468993003caf2e3be978798cbe096',
+ '76598df914cdfe87132d0c3c40f80db9fa9396a4',
+ ],
+ message:
+ "Merge branch 'master-test' into 'master'\n\nUpdate .gitlab-ci.yml\n\nSee merge request root/ci-web-terminal!1",
+ author_name: 'Administrator',
+ author_email: 'admin@example.com',
+ authored_date: '2018-10-22T11:41:33.000Z',
+ committer_name: 'Administrator',
+ committer_email: 'admin@example.com',
+ committed_date: '2018-10-22T11:41:33.000Z',
+ author: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url: null,
+ web_url: 'http://localhost:3000/root',
+ status_tooltip_html: null,
+ path: '/root',
+ },
+ author_gravatar_url: null,
+ commit_url:
+ 'http://localhost:3000/root/ci-web-terminal/commit/aa1939133d373c94879becb79d91828a892ee319',
+ commit_path: '/root/ci-web-terminal/commit/aa1939133d373c94879becb79d91828a892ee319',
+ },
+ cancel_path: '/root/ci-web-terminal/pipelines/127/cancel',
+ };
+ vm.$nextTick(done);
+ });
+
+ it('renders pipeline block', () => {
+ expect(vm.$el.querySelector('.js-post-merge-pipeline')).not.toBeNull();
+ });
+
+ describe('with post merge deployments', () => {
+ beforeEach(done => {
+ vm.mr.postMergeDeployments = [
+ {
+ id: 15,
+ name: 'review/diplo',
+ url: '/root/acets-review-apps/environments/15',
+ stop_url: '/root/acets-review-apps/environments/15/stop',
+ metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
+ metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
+ external_url: 'http://diplo.',
+ external_url_formatted: 'diplo.',
+ deployed_at: '2017-03-22T22:44:42.258Z',
+ deployed_at_formatted: 'Mar 22, 2017 10:44pm',
+ changes: [
+ {
+ path: 'index.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
+ },
+ {
+ path: 'imgs/gallery.html',
+ external_url:
+ 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
+ },
+ {
+ path: 'about/',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
+ },
+ ],
+ status: 'success',
+ },
+ ];
+
+ vm.$nextTick(done);
+ });
+
+ it('renders post deployment information', () => {
+ expect(vm.$el.querySelector('.js-post-deployment')).not.toBeNull();
+ });
+ });
+ });
+
+ describe('without information for target branch pipeline', () => {
+ beforeEach(done => {
+ vm.mr.state = 'merged';
+
+ vm.$nextTick(done);
+ });
+
+ it('does not render pipeline block', () => {
+ expect(vm.$el.querySelector('.js-post-merge-pipeline')).toBeNull();
+ });
+ });
+
+ describe('when state is not merged', () => {
+ beforeEach(done => {
+ vm.mr.state = 'archived';
+
+ vm.$nextTick(done);
+ });
+
+ it('does not render pipeline block', () => {
+ expect(vm.$el.querySelector('.js-post-merge-pipeline')).toBeNull();
+ });
+
+ it('does not render post deployment information', () => {
+ expect(vm.$el.querySelector('.js-post-deployment')).toBeNull();
+ });
+ });
});
});
diff --git a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
index 179e42a7cc4..9d34bdd1084 100644
--- a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
@@ -20,46 +20,60 @@ describe('getStateKey', () => {
work_in_progress: false,
};
const bound = getStateKey.bind(context, data);
+
expect(bound()).toEqual(null);
context.canBeMerged = true;
+
expect(bound()).toEqual('readyToMerge');
context.canMerge = false;
+
expect(bound()).toEqual('notAllowedToMerge');
context.mergeWhenPipelineSucceeds = true;
+
expect(bound()).toEqual('mergeWhenPipelineSucceeds');
context.hasSHAChanged = true;
+
expect(bound()).toEqual('shaMismatch');
context.isPipelineBlocked = true;
+
expect(bound()).toEqual('pipelineBlocked');
context.hasMergeableDiscussionsState = true;
+
expect(bound()).toEqual('unresolvedDiscussions');
context.onlyAllowMergeIfPipelineSucceeds = true;
context.isPipelineFailed = true;
+
expect(bound()).toEqual('pipelineFailed');
data.work_in_progress = true;
+
expect(bound()).toEqual('workInProgress');
data.has_conflicts = true;
+
expect(bound()).toEqual('conflicts');
context.mergeStatus = 'unchecked';
+
expect(bound()).toEqual('checking');
data.commits_count = 0;
+
expect(bound()).toEqual('nothingToMerge');
data.branch_missing = true;
+
expect(bound()).toEqual('missingBranch');
data.project_archived = true;
+
expect(bound()).toEqual('archived');
});
});
diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
index 33d052aceb2..f5079147f60 100644
--- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
@@ -12,32 +12,38 @@ describe('MergeRequestStore', () => {
it('should set hasSHAChanged when the diff SHA changes', () => {
store.setData({ ...mockData, diff_head_sha: 'a-different-string' });
+
expect(store.hasSHAChanged).toBe(true);
});
it('should not set hasSHAChanged when other data changes', () => {
store.setData({ ...mockData, work_in_progress: !mockData.work_in_progress });
+
expect(store.hasSHAChanged).toBe(false);
});
describe('isPipelinePassing', () => {
it('is true when the CI status is `success`', () => {
store.setData({ ...mockData, ci_status: 'success' });
+
expect(store.isPipelinePassing).toBe(true);
});
it('is true when the CI status is `success_with_warnings`', () => {
store.setData({ ...mockData, ci_status: 'success_with_warnings' });
+
expect(store.isPipelinePassing).toBe(true);
});
it('is false when the CI status is `failed`', () => {
store.setData({ ...mockData, ci_status: 'failed' });
+
expect(store.isPipelinePassing).toBe(false);
});
it('is false when the CI status is anything except `success`', () => {
store.setData({ ...mockData, ci_status: 'foobarbaz' });
+
expect(store.isPipelinePassing).toBe(false);
});
});
@@ -45,11 +51,13 @@ describe('MergeRequestStore', () => {
describe('isPipelineSkipped', () => {
it('should set isPipelineSkipped=true when the CI status is `skipped`', () => {
store.setData({ ...mockData, ci_status: 'skipped' });
+
expect(store.isPipelineSkipped).toBe(true);
});
it('should set isPipelineSkipped=false when the CI status is anything except `skipped`', () => {
store.setData({ ...mockData, ci_status: 'foobarbaz' });
+
expect(store.isPipelineSkipped).toBe(false);
});
});
@@ -57,11 +65,13 @@ describe('MergeRequestStore', () => {
describe('isNothingToMergeState', () => {
it('returns true when nothingToMerge', () => {
store.state = stateKey.nothingToMerge;
+
expect(store.isNothingToMergeState).toEqual(true);
});
it('returns false when not nothingToMerge', () => {
store.state = 'state';
+
expect(store.isNothingToMergeState).toEqual(false);
});
});
diff --git a/spec/javascripts/vue_shared/components/bar_chart_spec.js b/spec/javascripts/vue_shared/components/bar_chart_spec.js
index 7e91cd6f63f..8f753876e44 100644
--- a/spec/javascripts/vue_shared/components/bar_chart_spec.js
+++ b/spec/javascripts/vue_shared/components/bar_chart_spec.js
@@ -71,12 +71,6 @@ describe('Bar chart component', () => {
expect(barChart.xAxisLocation).toEqual('translate(100, 250)');
});
- it('Contains a total of 4 ticks across the y axis', () => {
- const ticks = barChart.$el.querySelector('.y-axis').querySelectorAll('.tick').length;
-
- expect(ticks).toEqual(4);
- });
-
it('rotates the x axis labels a total of 90 degress (CCW)', () => {
const xAxisLabel = barChart.$el.querySelector('.x-axis').querySelectorAll('text')[0];
diff --git a/spec/javascripts/vue_shared/components/changed_file_icon_spec.js b/spec/javascripts/vue_shared/components/changed_file_icon_spec.js
new file mode 100644
index 00000000000..5b1038840c7
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/changed_file_icon_spec.js
@@ -0,0 +1,46 @@
+import Vue from 'vue';
+import changedFileIcon from '~/vue_shared/components/changed_file_icon.vue';
+import createComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Changed file icon', () => {
+ let vm;
+
+ beforeEach(() => {
+ const component = Vue.extend(changedFileIcon);
+
+ vm = createComponent(component, {
+ file: {
+ tempFile: false,
+ changed: true,
+ },
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('changedIcon', () => {
+ it('equals file-modified when not a temp file and has changes', () => {
+ expect(vm.changedIcon).toBe('file-modified');
+ });
+
+ it('equals file-addition when a temp file', () => {
+ vm.file.tempFile = true;
+
+ expect(vm.changedIcon).toBe('file-addition');
+ });
+ });
+
+ describe('changedIconClass', () => {
+ it('includes file-modified when not a temp file', () => {
+ expect(vm.changedIconClass).toContain('file-modified');
+ });
+
+ it('includes file-addition when a temp file', () => {
+ vm.file.tempFile = true;
+
+ expect(vm.changedIconClass).toContain('file-addition');
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
index 668742ebaee..4b0b7ba66e5 100644
--- a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
+++ b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
@@ -81,11 +81,12 @@ describe('CI Badge Link Component', () => {
});
it('should render each status badge', () => {
- Object.keys(statuses).map((status) => {
+ Object.keys(statuses).map(status => {
vm = mountComponent(CIBadge, { status: statuses[status] });
+
expect(vm.$el.getAttribute('href')).toEqual(statuses[status].details_path);
expect(vm.$el.textContent.trim()).toEqual(statuses[status].text);
- expect(vm.$el.getAttribute('class')).toEqual(`ci-status ci-${statuses[status].group}`);
+ expect(vm.$el.getAttribute('class')).toContain(`ci-status ci-${statuses[status].group}`);
expect(vm.$el.querySelector('svg')).toBeDefined();
return vm;
});
@@ -93,6 +94,7 @@ describe('CI Badge Link Component', () => {
it('should not render label', () => {
vm = mountComponent(CIBadge, { status: statuses.canceled, showText: false });
+
expect(vm.$el.textContent.trim()).toEqual('');
});
});
diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
index ea525b1e44f..fd17349d48f 100644
--- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js
+++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
@@ -27,8 +27,6 @@ describe('clipboard button', () => {
it('should have a tooltip with default values', () => {
expect(vm.$el.getAttribute('data-original-title')).toEqual('Copy this value into Clipboard!');
- expect(vm.$el.getAttribute('data-placement')).toEqual('top');
- expect(vm.$el.getAttribute('data-container')).toEqual(null);
});
it('should render provided classname', () => {
@@ -44,6 +42,7 @@ describe('clipboard button', () => {
title: 'Copy this value into Clipboard!',
cssClass: 'btn-danger',
});
+
expect(vm.$el.getAttribute('data-clipboard-text')).toEqual(
'{"text":"copy me","gfm":"`path/to/file`"}',
);
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index 7189e8cfcfa..18fcdf7ede1 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -78,6 +78,7 @@ describe('Commit component', () => {
expect(component.$el.querySelector('.commit-sha').getAttribute('href')).toEqual(
props.commitUrl,
);
+
expect(component.$el.querySelector('.commit-sha').textContent).toContain(props.shortSha);
});
@@ -97,9 +98,10 @@ describe('Commit component', () => {
it('Should render the author avatar with title and alt attributes', () => {
expect(
component.$el
- .querySelector('.commit-title .avatar-image-container img')
- .getAttribute('data-original-title'),
+ .querySelector('.commit-title .avatar-image-container .js-user-avatar-image-toolip')
+ .textContent.trim(),
).toContain(props.author.username);
+
expect(
component.$el
.querySelector('.commit-title .avatar-image-container img')
@@ -112,6 +114,7 @@ describe('Commit component', () => {
expect(component.$el.querySelector('a.commit-row-message').getAttribute('href')).toEqual(
props.commitUrl,
);
+
expect(component.$el.querySelector('a.commit-row-message').textContent).toContain(
props.title,
);
diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
index e2c34508b0d..4da8c6196b1 100644
--- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
@@ -47,7 +47,7 @@ describe('ContentViewer', () => {
});
setTimeout(() => {
- expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
+ expect(vm.$el.querySelector('img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
done();
});
diff --git a/spec/javascripts/vue_shared/components/deprecated_modal_spec.js b/spec/javascripts/vue_shared/components/deprecated_modal_spec.js
index 59d4e549a91..be75be92158 100644
--- a/spec/javascripts/vue_shared/components/deprecated_modal_spec.js
+++ b/spec/javascripts/vue_shared/components/deprecated_modal_spec.js
@@ -47,7 +47,7 @@ describe('DeprecatedModal', () => {
});
});
- it('works with data-toggle="modal"', (done) => {
+ it('works with data-toggle="modal"', done => {
setFixtures(`
<button id="modal-button" data-toggle="modal" data-target="#my-modal"></button>
<div id="modal-container"></div>
@@ -55,9 +55,13 @@ describe('DeprecatedModal', () => {
const modalContainer = document.getElementById('modal-container');
const modalButton = document.getElementById('modal-button');
- vm = mountComponent(modalComponent, {
- id: 'my-modal',
- }, modalContainer);
+ vm = mountComponent(
+ modalComponent,
+ {
+ id: 'my-modal',
+ },
+ modalContainer,
+ );
const modalElement = vm.$el.querySelector('#my-modal');
$(modalElement).on('shown.bs.modal', () => done());
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
index 71d9145bf22..67a3a2e08bc 100644
--- a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
@@ -30,11 +30,11 @@ describe('DiffViewer', () => {
});
setTimeout(() => {
- expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
+ expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe(
`//raw/DEF/${RED_BOX_IMAGE_URL}`,
);
- expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
+ expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(
`//raw/ABC/${GREEN_BOX_IMAGE_URL}`,
);
@@ -55,6 +55,7 @@ describe('DiffViewer', () => {
expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain(
'testold.abc',
);
+
expect(vm.$el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain(
'Download',
);
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
index dde49b4a5d7..7f2e246d656 100644
--- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
@@ -52,17 +52,15 @@ describe('ImageDiffViewer', () => {
});
setTimeout(() => {
- expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
- GREEN_BOX_IMAGE_URL,
- );
- expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
- RED_BOX_IMAGE_URL,
- );
+ expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
+
+ expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe(RED_BOX_IMAGE_URL);
expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe('2-up');
expect(vm.$el.querySelector('.view-modes-menu li:nth-child(2)').textContent.trim()).toBe(
'Swipe',
);
+
expect(vm.$el.querySelector('.view-modes-menu li:nth-child(3)').textContent.trim()).toBe(
'Onion skin',
);
@@ -79,9 +77,7 @@ describe('ImageDiffViewer', () => {
});
setTimeout(() => {
- expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
- GREEN_BOX_IMAGE_URL,
- );
+ expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
done();
});
@@ -95,9 +91,27 @@ describe('ImageDiffViewer', () => {
});
setTimeout(() => {
- expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
- RED_BOX_IMAGE_URL,
- );
+ expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe(RED_BOX_IMAGE_URL);
+
+ done();
+ });
+ });
+
+ it('renders image diff for renamed', done => {
+ vm = new Vue({
+ components: {
+ imageDiffViewer,
+ },
+ template: `
+ <image-diff-viewer diff-mode="renamed" new-path="${GREEN_BOX_IMAGE_URL}" old-path="">
+ <span slot="image-overlay" class="overlay">test</span>
+ </image-diff-viewer>
+ `,
+ }).$mount();
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
+ expect(vm.$el.querySelector('.overlay')).not.toBe(null);
done();
});
diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
index 2796cd088c6..2fc4943de30 100644
--- a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
+++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
@@ -55,6 +55,7 @@ describe('DropdownButtonComponent', () => {
it('renders dropdown toggle text element', () => {
const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text');
+
expect(dropdownToggleTextEl).not.toBeNull();
expect(dropdownToggleTextEl.innerText.trim()).toBe(defaultLabel);
});
@@ -67,9 +68,12 @@ describe('DropdownButtonComponent', () => {
});
it('renders slot, if default slot exists', () => {
- vm = createComponent({}, {
- default: ['Lorem Ipsum Dolar'],
- });
+ vm = createComponent(
+ {},
+ {
+ default: ['Lorem Ipsum Dolar'],
+ },
+ );
expect(vm.$el).not.toContainElement('.dropdown-toggle-text');
expect(vm.$el).toHaveText('Lorem Ipsum Dolar');
diff --git a/spec/javascripts/vue_shared/components/expand_button_spec.js b/spec/javascripts/vue_shared/components/expand_button_spec.js
index 98fee9a74a5..2af4abc299a 100644
--- a/spec/javascripts/vue_shared/components/expand_button_spec.js
+++ b/spec/javascripts/vue_shared/components/expand_button_spec.js
@@ -18,7 +18,7 @@ describe('expand button', () => {
vm.$destroy();
});
- it('renders a collpased button', () => {
+ it('renders a collapsed button', () => {
expect(vm.$children[0].iconTestClass).toEqual('ic-ellipsis_h');
});
diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js
index 1c666fc6c55..34c9b35e02a 100644
--- a/spec/javascripts/vue_shared/components/file_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/file_icon_spec.js
@@ -28,7 +28,9 @@ describe('File Icon component', () => {
fileName: 'test.js',
});
- expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#javascript`);
+ expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(
+ `${gon.sprite_file_icons}#javascript`,
+ );
});
it('should render a image icon based on file ending', () => {
@@ -36,7 +38,9 @@ describe('File Icon component', () => {
fileName: 'test.png',
});
- expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#image`);
+ expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(
+ `${gon.sprite_file_icons}#image`,
+ );
});
it('should render a webpack icon based on file namer', () => {
@@ -44,7 +48,9 @@ describe('File Icon component', () => {
fileName: 'webpack.js',
});
- expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#webpack`);
+ expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(
+ `${gon.sprite_file_icons}#webpack`,
+ );
});
it('should render a standard folder icon', () => {
@@ -53,7 +59,9 @@ describe('File Icon component', () => {
folder: true,
});
- expect(vm.$el.querySelector('span > svg > use').getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#folder`);
+ expect(vm.$el.querySelector('span > svg > use').getAttribute('xlink:href')).toBe(
+ `${gon.sprite_file_icons}#folder`,
+ );
});
it('should render a loading icon', () => {
@@ -62,9 +70,12 @@ describe('File Icon component', () => {
loading: true,
});
- expect(
- vm.$el.querySelector('i').getAttribute('class'),
- ).toEqual('fa fa-spin fa-spinner fa-1x');
+ const { classList } = vm.$el.querySelector('i');
+
+ expect(classList.contains('fa')).toEqual(true);
+ expect(classList.contains('fa-spin')).toEqual(true);
+ expect(classList.contains('fa-spinner')).toEqual(true);
+ expect(classList.contains('fa-1x')).toEqual(true);
});
it('should add a special class and a size class', () => {
@@ -77,6 +88,7 @@ describe('File Icon component', () => {
const { classList } = vm.$el.firstChild;
const containsSizeClass = classList.contains('s120');
const containsCustomClass = classList.contains('extraclasses');
+
expect(containsSizeClass).toBe(true);
expect(containsCustomClass).toBe(true);
});
diff --git a/spec/javascripts/vue_shared/components/file_row_spec.js b/spec/javascripts/vue_shared/components/file_row_spec.js
new file mode 100644
index 00000000000..67752c1c455
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/file_row_spec.js
@@ -0,0 +1,110 @@
+import Vue from 'vue';
+import FileRow from '~/vue_shared/components/file_row.vue';
+import { file } from 'spec/ide/helpers';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('RepoFile', () => {
+ let vm;
+
+ function createComponent(propsData) {
+ const FileRowComponent = Vue.extend(FileRow);
+
+ vm = mountComponent(FileRowComponent, propsData);
+ }
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders name', () => {
+ createComponent({
+ file: file('t4'),
+ level: 0,
+ });
+
+ const name = vm.$el.querySelector('.file-row-name');
+
+ expect(name.textContent.trim()).toEqual(vm.file.name);
+ });
+
+ it('emits toggleTreeOpen on click', () => {
+ createComponent({
+ file: {
+ ...file('t3'),
+ type: 'tree',
+ },
+ level: 0,
+ });
+ spyOn(vm, '$emit').and.stub();
+
+ vm.$el.querySelector('.file-row').click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('toggleTreeOpen', vm.file.path);
+ });
+
+ it('calls scrollIntoView if made active', done => {
+ createComponent({
+ file: {
+ ...file(),
+ type: 'blob',
+ active: false,
+ },
+ level: 0,
+ });
+
+ spyOn(vm, 'scrollIntoView').and.stub();
+
+ vm.file.active = true;
+
+ vm.$nextTick(() => {
+ expect(vm.scrollIntoView).toHaveBeenCalled();
+
+ done();
+ });
+ });
+
+ it('indents row based on level', () => {
+ createComponent({
+ file: file('t4'),
+ level: 2,
+ });
+
+ expect(vm.$el.querySelector('.file-row-name').style.marginLeft).toBe('32px');
+ });
+
+ describe('outputText', () => {
+ beforeEach(done => {
+ createComponent({
+ file: {
+ ...file(),
+ path: 'app/assets/index.js',
+ },
+ level: 0,
+ });
+
+ vm.displayTextKey = 'path';
+
+ vm.$nextTick(done);
+ });
+
+ it('returns text if truncateStart is 0', done => {
+ vm.truncateStart = 0;
+
+ vm.$nextTick(() => {
+ expect(vm.outputText).toBe('app/assets/index.js');
+
+ done();
+ });
+ });
+
+ it('returns text truncated at start', done => {
+ vm.truncateStart = 5;
+
+ vm.$nextTick(() => {
+ expect(vm.outputText).toBe('...ssets/index.js');
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js b/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js
new file mode 100644
index 00000000000..b84b5ae67a8
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js
@@ -0,0 +1,91 @@
+import Vue from 'vue';
+import component from '~/vue_shared/components/filtered_search_dropdown.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Filtered search dropdown', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with an empty array of items', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ items: [],
+ filterKey: '',
+ });
+ });
+
+ it('renders empty list', () => {
+ expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(0);
+ });
+
+ it('renders filter input', () => {
+ expect(vm.$el.querySelector('.js-filtered-dropdown-input')).not.toBeNull();
+ });
+ });
+
+ describe('when visible numbers is less than the items length', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ items: [{ title: 'One' }, { title: 'Two' }, { title: 'Three' }],
+ visibleItems: 2,
+ filterKey: 'title',
+ });
+ });
+
+ it('it renders only the maximum number provided', () => {
+ expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(2);
+ });
+ });
+
+ describe('when visible number is bigger than the items length', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ items: [{ title: 'One' }, { title: 'Two' }, { title: 'Three' }],
+ filterKey: 'title',
+ });
+ });
+
+ it('it renders the full list of items the maximum number provided', () => {
+ expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(3);
+ });
+ });
+
+ describe('while filtering', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ items: [
+ { title: 'One' },
+ { title: 'Two/three' },
+ { title: 'Three four' },
+ { title: 'Five' },
+ ],
+ filterKey: 'title',
+ });
+ });
+
+ it('updates the results to match the typed value', done => {
+ vm.$el.querySelector('.js-filtered-dropdown-input').value = 'three';
+ vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input'));
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(2);
+ done();
+ });
+ });
+
+ describe('when no value matches the typed one', () => {
+ it('does not render any result', done => {
+ vm.$el.querySelector('.js-filtered-dropdown-input').value = 'six';
+ vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input'));
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(0);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/gl_countdown_spec.js b/spec/javascripts/vue_shared/components/gl_countdown_spec.js
new file mode 100644
index 00000000000..929ffe219f4
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/gl_countdown_spec.js
@@ -0,0 +1,77 @@
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import Vue from 'vue';
+import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
+
+describe('GlCountdown', () => {
+ const Component = Vue.extend(GlCountdown);
+ let vm;
+ let now = '2000-01-01T00:00:00Z';
+
+ beforeEach(() => {
+ spyOn(Date, 'now').and.callFake(() => new Date(now).getTime());
+ jasmine.clock().install();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ jasmine.clock().uninstall();
+ });
+
+ describe('when there is time remaining', () => {
+ beforeEach(done => {
+ vm = mountComponent(Component, {
+ endDateString: '2000-01-01T01:02:03Z',
+ });
+
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays remaining time', () => {
+ expect(vm.$el).toContainText('01:02:03');
+ });
+
+ it('updates remaining time', done => {
+ now = '2000-01-01T00:00:01Z';
+ jasmine.clock().tick(1000);
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el).toContainText('01:02:02');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('when there is no time remaining', () => {
+ beforeEach(done => {
+ vm = mountComponent(Component, {
+ endDateString: '1900-01-01T00:00:00Z',
+ });
+
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays 00:00:00', () => {
+ expect(vm.$el).toContainText('00:00:00');
+ });
+ });
+
+ describe('when an invalid date is passed', () => {
+ it('throws a validation error', () => {
+ spyOn(Vue.config, 'warnHandler').and.stub();
+ vm = mountComponent(Component, {
+ endDateString: 'this is invalid',
+ });
+
+ expect(Vue.config.warnHandler).toHaveBeenCalledTimes(1);
+ const [errorMessage] = Vue.config.warnHandler.calls.argsFor(0);
+
+ expect(errorMessage).toMatch(/^Invalid prop: .* "endDateString"/);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js
index 263824a102a..19af8b5d2f7 100644
--- a/spec/javascripts/vue_shared/components/gl_modal_spec.js
+++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js
@@ -48,6 +48,7 @@ describe('GlModal', () => {
it('sets the modal title', () => {
const modalTitle = vm.$el.querySelector('.modal-title');
+
expect(modalTitle.innerHTML.trim()).toBe(props.headerTitleText);
});
});
@@ -63,6 +64,7 @@ describe('GlModal', () => {
it('sets the primary button class', () => {
const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type');
+
expect(primaryButton).toHaveClass(`btn-${props.footerPrimaryButtonVariant}`);
});
});
@@ -78,6 +80,7 @@ describe('GlModal', () => {
it('sets the primary button text', () => {
const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type');
+
expect(primaryButton.innerHTML.trim()).toBe(props.footerPrimaryButtonText);
});
});
@@ -173,6 +176,7 @@ describe('GlModal', () => {
it('sets the modal body', () => {
const modalBody = vm.$el.querySelector('.modal-body');
+
expect(modalBody.innerHTML).toBe(slotContent);
});
});
@@ -184,6 +188,7 @@ describe('GlModal', () => {
it('sets the modal header', () => {
const modalHeader = vm.$el.querySelector('.modal-header');
+
expect(modalHeader.innerHTML).toBe(slotContent);
});
});
@@ -195,6 +200,7 @@ describe('GlModal', () => {
it('sets the modal title', () => {
const modalTitle = vm.$el.querySelector('.modal-title');
+
expect(modalTitle.innerHTML).toBe(slotContent);
});
});
@@ -206,6 +212,7 @@ describe('GlModal', () => {
it('sets the modal footer', () => {
const modalFooter = vm.$el.querySelector('.modal-footer');
+
expect(modalFooter.innerHTML).toBe(slotContent);
});
});
diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
index f17818c17c7..7a741bdc067 100644
--- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js
+++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
@@ -59,9 +59,9 @@ describe('Header CI Component', () => {
it('should render status badge', () => {
expect(vm.$el.querySelector('.ci-failed')).toBeDefined();
expect(vm.$el.querySelector('.ci-status-icon-failed svg')).toBeDefined();
- expect(
- vm.$el.querySelector('.ci-failed').getAttribute('href'),
- ).toEqual(props.status.details_path);
+ expect(vm.$el.querySelector('.ci-failed').getAttribute('href')).toEqual(
+ props.status.details_path,
+ );
});
it('should render item name and id', () => {
@@ -73,7 +73,7 @@ describe('Header CI Component', () => {
});
it('should render user icon and name', () => {
- expect(vm.$el.querySelector('.js-user-link').textContent.trim()).toEqual(props.user.name);
+ expect(vm.$el.querySelector('.js-user-link').innerText.trim()).toContain(props.user.name);
});
it('should render provided actions', () => {
@@ -84,7 +84,7 @@ describe('Header CI Component', () => {
expect(vm.$el.querySelector('.link').getAttribute('href')).toEqual(props.actions[0].path);
});
- it('should show loading icon', (done) => {
+ it('should show loading icon', done => {
vm.actions[0].isLoading = true;
Vue.nextTick(() => {
@@ -94,7 +94,7 @@ describe('Header CI Component', () => {
});
it('should render sidebar toggle button', () => {
- expect(vm.$el.querySelector('.js-sidebar-build-toggle')).toBeDefined();
+ expect(vm.$el.querySelector('.js-sidebar-build-toggle')).not.toBeNull();
});
});
diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js
index 01f4649339e..45eef2ad737 100644
--- a/spec/javascripts/vue_shared/components/icon_spec.js
+++ b/spec/javascripts/vue_shared/components/icon_spec.js
@@ -2,11 +2,11 @@ import Vue from 'vue';
import Icon from '~/vue_shared/components/icon.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-describe('Sprite Icon Component', function () {
- describe('Initialization', function () {
+describe('Sprite Icon Component', function() {
+ describe('Initialization', function() {
let icon;
- beforeEach(function () {
+ beforeEach(function() {
const IconComponent = Vue.extend(Icon);
icon = mountComponent(IconComponent, {
@@ -21,20 +21,20 @@ describe('Sprite Icon Component', function () {
icon.$destroy();
});
- it('should return a defined Vue component', function () {
+ it('should return a defined Vue component', function() {
expect(icon).toBeDefined();
});
- it('should have <svg> as a child element', function () {
+ it('should have <svg> as a child element', function() {
expect(icon.$el.tagName).toBe('svg');
});
- it('should have <use> as a child element with the correct href', function () {
+ it('should have <use> as a child element with the correct href', function() {
expect(icon.$el.firstChild.tagName).toBe('use');
expect(icon.$el.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_icons}#commit`);
});
- it('should properly compute iconSizeClass', function () {
+ it('should properly compute iconSizeClass', function() {
expect(icon.iconSizeClass).toBe('s32');
});
@@ -44,10 +44,11 @@ describe('Sprite Icon Component', function () {
expect(icon.$options.props.size.validator(9001)).toBeFalsy();
});
- it('should properly render img css', function () {
+ it('should properly render img css', function() {
const { classList } = icon.$el;
const containsSizeClass = classList.contains('s32');
const containsCustomClass = classList.contains('extraclasses');
+
expect(containsSizeClass).toBe(true);
expect(containsCustomClass).toBe(true);
});
diff --git a/spec/javascripts/vue_shared/components/identicon_spec.js b/spec/javascripts/vue_shared/components/identicon_spec.js
index 0719800c682..0b3dbb61c96 100644
--- a/spec/javascripts/vue_shared/components/identicon_spec.js
+++ b/spec/javascripts/vue_shared/components/identicon_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import identiconComponent from '~/vue_shared/components/identicon.vue';
-const createComponent = (sizeClass) => {
+const createComponent = sizeClass => {
const Component = Vue.extend(identiconComponent);
return new Component({
diff --git a/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js b/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
index e6ed77dbb52..aa7d6ea2e34 100644
--- a/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
+++ b/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
@@ -6,7 +6,10 @@ const IssueWarning = Vue.extend(issueWarning);
function formatWarning(string) {
// Replace newlines with a space then replace multiple spaces with one space
- return string.trim().replace(/\n/g, ' ').replace(/\s\s+/g, ' ');
+ return string
+ .trim()
+ .replace(/\n/g, ' ')
+ .replace(/\s\s+/g, ' ');
}
describe('Issue Warning Component', () => {
@@ -17,7 +20,9 @@ describe('Issue Warning Component', () => {
});
expect(vm.$el.querySelector('.icon use').href.baseVal).toMatch(/lock$/);
- expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This issue is locked. Only project members can comment.');
+ expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual(
+ 'This issue is locked. Only project members can comment.',
+ );
});
});
@@ -28,7 +33,9 @@ describe('Issue Warning Component', () => {
});
expect(vm.$el.querySelector('.icon use').href.baseVal).toMatch(/eye-slash$/);
- expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This is a confidential issue. Your comment will not be visible to the public.');
+ expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual(
+ 'This is a confidential issue. Your comment will not be visible to the public.',
+ );
});
});
@@ -40,7 +47,9 @@ describe('Issue Warning Component', () => {
});
expect(vm.$el.querySelector('.icon')).toBeFalsy();
- expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This issue is confidential and locked. People without permission will never get a notification and won\'t be able to comment.');
+ expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual(
+ "This issue is confidential and locked. People without permission will never get a notification and won't be able to comment.",
+ );
});
});
});
diff --git a/spec/javascripts/vue_shared/components/loading_button_spec.js b/spec/javascripts/vue_shared/components/loading_button_spec.js
index 51c19cd4080..db89d4a934c 100644
--- a/spec/javascripts/vue_shared/components/loading_button_spec.js
+++ b/spec/javascripts/vue_shared/components/loading_button_spec.js
@@ -4,7 +4,7 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper';
const LABEL = 'Hello';
-describe('LoadingButton', function () {
+describe('LoadingButton', function() {
let vm;
let LoadingButton;
@@ -69,6 +69,7 @@ describe('LoadingButton', function () {
describe('container class', () => {
it('should default to btn btn-align-content', () => {
vm = mountComponent(LoadingButton, {});
+
expect(vm.$el.classList.contains('btn')).toEqual(true);
expect(vm.$el.classList.contains('btn-align-content')).toEqual(true);
});
@@ -77,6 +78,7 @@ describe('LoadingButton', function () {
vm = mountComponent(LoadingButton, {
containerClass: 'test-class',
});
+
expect(vm.$el.classList.contains('btn')).toEqual(false);
expect(vm.$el.classList.contains('btn-align-content')).toEqual(false);
expect(vm.$el.classList.contains('test-class')).toEqual(true);
diff --git a/spec/javascripts/vue_shared/components/loading_icon_spec.js b/spec/javascripts/vue_shared/components/loading_icon_spec.js
deleted file mode 100644
index 5cd3466f501..00000000000
--- a/spec/javascripts/vue_shared/components/loading_icon_spec.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import Vue from 'vue';
-import loadingIcon from '~/vue_shared/components/loading_icon.vue';
-
-describe('Loading Icon Component', () => {
- let LoadingIconComponent;
-
- beforeEach(() => {
- LoadingIconComponent = Vue.extend(loadingIcon);
- });
-
- it('should render a spinner font awesome icon', () => {
- const component = new LoadingIconComponent().$mount();
-
- expect(
- component.$el.querySelector('i').getAttribute('class'),
- ).toEqual('fa fa-spin fa-spinner fa-1x');
-
- expect(component.$el.tagName).toEqual('DIV');
- expect(component.$el.classList).toContain('text-center');
- expect(component.$el.classList).toContain('loading-container');
- });
-
- it('should render accessibility attributes', () => {
- const component = new LoadingIconComponent().$mount();
-
- const icon = component.$el.querySelector('i');
- expect(icon.getAttribute('aria-hidden')).toEqual('true');
- expect(icon.getAttribute('aria-label')).toEqual('Loading');
- });
-
- it('should render the provided label', () => {
- const component = new LoadingIconComponent({
- propsData: {
- label: 'This is a loading icon',
- },
- }).$mount();
-
- expect(
- component.$el.querySelector('i').getAttribute('aria-label'),
- ).toEqual('This is a loading icon');
- });
-
- it('should render the provided size', () => {
- const component = new LoadingIconComponent({
- propsData: {
- size: '2',
- },
- }).$mount();
-
- expect(
- component.$el.querySelector('i').classList.contains('fa-2x'),
- ).toEqual(true);
- });
-});
diff --git a/spec/javascripts/vue_shared/components/markdown/field_spec.js b/spec/javascripts/vue_shared/components/markdown/field_spec.js
index 69034975422..abb17440c0e 100644
--- a/spec/javascripts/vue_shared/components/markdown/field_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/field_spec.js
@@ -11,7 +11,7 @@ function assertMarkdownTabs(isWrite, writeLink, previewLink, vm) {
describe('Markdown field component', () => {
let vm;
- beforeEach((done) => {
+ beforeEach(done => {
vm = new Vue({
components: {
fieldComponent,
@@ -39,9 +39,7 @@ describe('Markdown field component', () => {
describe('mounted', () => {
it('renders textarea inside backdrop', () => {
- expect(
- vm.$el.querySelector('.zen-backdrop textarea'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('.zen-backdrop textarea')).not.toBeNull();
});
describe('markdown preview', () => {
@@ -49,73 +47,70 @@ describe('Markdown field component', () => {
let writeLink;
beforeEach(() => {
- spyOn(Vue.http, 'post').and.callFake(() => new Promise((resolve) => {
- setTimeout(() => {
- resolve({
- json() {
- return {
- body: '<p>markdown preview</p>',
- };
- },
- });
- });
- }));
+ spyOn(Vue.http, 'post').and.callFake(
+ () =>
+ new Promise(resolve => {
+ setTimeout(() => {
+ resolve({
+ json() {
+ return {
+ body: '<p>markdown preview</p>',
+ };
+ },
+ });
+ });
+ }),
+ );
previewLink = vm.$el.querySelector('.nav-links .js-preview-link');
writeLink = vm.$el.querySelector('.nav-links .js-write-link');
});
- it('sets preview link as active', (done) => {
+ it('sets preview link as active', done => {
previewLink.click();
Vue.nextTick(() => {
- expect(
- previewLink.parentNode.classList.contains('active'),
- ).toBeTruthy();
+ expect(previewLink.parentNode.classList.contains('active')).toBeTruthy();
done();
});
});
- it('shows preview loading text', (done) => {
+ it('shows preview loading text', done => {
previewLink.click();
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.md-preview').textContent.trim(),
- ).toContain('Loading...');
+ expect(vm.$el.querySelector('.md-preview').textContent.trim()).toContain('Loading...');
done();
});
});
- it('renders markdown preview', (done) => {
+ it('renders markdown preview', done => {
previewLink.click();
setTimeout(() => {
- expect(
- vm.$el.querySelector('.md-preview').innerHTML,
- ).toContain('<p>markdown preview</p>');
+ expect(vm.$el.querySelector('.md-preview').innerHTML).toContain(
+ '<p>markdown preview</p>',
+ );
done();
});
});
- it('renders GFM with jQuery', (done) => {
+ it('renders GFM with jQuery', done => {
spyOn($.fn, 'renderGFM');
previewLink.click();
setTimeout(() => {
- expect(
- $.fn.renderGFM,
- ).toHaveBeenCalled();
+ expect($.fn.renderGFM).toHaveBeenCalled();
done();
}, 0);
});
- it('clicking already active write or preview link does nothing', (done) => {
+ it('clicking already active write or preview link does nothing', done => {
writeLink.click();
Vue.nextTick()
.then(() => assertMarkdownTabs(true, writeLink, previewLink, vm))
@@ -134,46 +129,40 @@ describe('Markdown field component', () => {
});
describe('markdown buttons', () => {
- it('converts single words', (done) => {
+ it('converts single words', done => {
const textarea = vm.$el.querySelector('textarea');
textarea.setSelectionRange(0, 7);
vm.$el.querySelector('.js-md').click();
Vue.nextTick(() => {
- expect(
- textarea.value,
- ).toContain('**testing**');
+ expect(textarea.value).toContain('**testing**');
done();
});
});
- it('converts a line', (done) => {
+ it('converts a line', done => {
const textarea = vm.$el.querySelector('textarea');
textarea.setSelectionRange(0, 0);
- vm.$el.querySelectorAll('.js-md')[4].click();
+ vm.$el.querySelectorAll('.js-md')[5].click();
Vue.nextTick(() => {
- expect(
- textarea.value,
- ).toContain('* testing');
+ expect(textarea.value).toContain('* testing');
done();
});
});
- it('converts multiple lines', (done) => {
+ it('converts multiple lines', done => {
const textarea = vm.$el.querySelector('textarea');
textarea.setSelectionRange(0, 50);
- vm.$el.querySelectorAll('.js-md')[4].click();
+ vm.$el.querySelectorAll('.js-md')[5].click();
Vue.nextTick(() => {
- expect(
- textarea.value,
- ).toContain('* testing\n* 123');
+ expect(textarea.value).toContain('* testing\n* 123');
done();
});
diff --git a/spec/javascripts/vue_shared/components/markdown/header_spec.js b/spec/javascripts/vue_shared/components/markdown/header_spec.js
index 488575df401..59613faa49f 100644
--- a/spec/javascripts/vue_shared/components/markdown/header_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/header_spec.js
@@ -17,8 +17,24 @@ describe('Markdown field header component', () => {
Vue.nextTick(done);
});
- it('renders markdown buttons', () => {
- expect(vm.$el.querySelectorAll('.js-md').length).toBe(7);
+ it('renders markdown header buttons', () => {
+ const buttons = [
+ 'Add bold text',
+ 'Add italic text',
+ 'Insert a quote',
+ 'Insert code',
+ 'Add a link',
+ 'Add a bullet list',
+ 'Add a numbered list',
+ 'Add a task list',
+ 'Add a table',
+ 'Go full screen',
+ ];
+ const elements = vm.$el.querySelectorAll('.toolbar-btn');
+
+ elements.forEach((buttonEl, index) => {
+ expect(buttonEl.getAttribute('data-original-title')).toBe(buttons[index]);
+ });
});
it('renders `write` link as active when previewMarkdown is false', () => {
@@ -51,14 +67,16 @@ describe('Markdown field header component', () => {
spyOn(vm, '$emit');
$(document).triggerHandler('markdown-preview:show', [
- $('<form><div class="js-vue-markdown-field"><textarea class="markdown-area"></textarea></div></form>'),
+ $(
+ '<form><div class="js-vue-markdown-field"><textarea class="markdown-area"></textarea></div></form>',
+ ),
]);
expect(vm.$emit).not.toHaveBeenCalled();
});
it('blurs preview link after click', done => {
- const link = vm.$el.querySelector('li:nth-child(2) a');
+ const link = vm.$el.querySelector('li:nth-child(2) button');
spyOn(HTMLElement.prototype, 'blur');
link.click();
@@ -69,4 +87,10 @@ describe('Markdown field header component', () => {
done();
});
});
+
+ it('renders markdown table template', () => {
+ expect(vm.mdTable).toEqual(
+ '| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |',
+ );
+ });
});
diff --git a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js
index 3e708f865c8..e6c7abd9d3b 100644
--- a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js
@@ -25,9 +25,12 @@ describe('toolbar', () => {
describe('user cannot attach file', () => {
beforeEach(() => {
- vm = mountComponent(Toolbar, Object.assign({}, props, {
- canAttachFile: false,
- }));
+ vm = mountComponent(
+ Toolbar,
+ Object.assign({}, props, {
+ canAttachFile: false,
+ }),
+ );
});
it('should not render uploading-container', () => {
diff --git a/spec/javascripts/vue_shared/components/memory_graph_spec.js b/spec/javascripts/vue_shared/components/memory_graph_spec.js
index 65d8ed39ade..78c3ae3ddb3 100644
--- a/spec/javascripts/vue_shared/components/memory_graph_spec.js
+++ b/spec/javascripts/vue_shared/components/memory_graph_spec.js
@@ -52,8 +52,9 @@ describe('MemoryGraph', () => {
it('should show human readable median value based on provided median timestamp', () => {
vm.deploymentTime = mockMedian;
const formattedMedian = vm.getFormattedMedian;
- expect(formattedMedian.indexOf('Deployed') > -1).toBeTruthy();
- expect(formattedMedian.indexOf('ago') > -1).toBeTruthy();
+
+ expect(formattedMedian.indexOf('Deployed')).toBeGreaterThan(-1);
+ expect(formattedMedian.indexOf('ago')).toBeGreaterThan(-1);
});
});
});
@@ -62,6 +63,7 @@ describe('MemoryGraph', () => {
describe('getMedianMetricIndex', () => {
it('should return index of closest metric timestamp to that of median', () => {
const matchingIndex = vm.getMedianMetricIndex(mockMedian, mockMetrics);
+
expect(matchingIndex).toBe(mockMedianIndex);
});
});
@@ -69,6 +71,7 @@ describe('MemoryGraph', () => {
describe('getGraphPlotValues', () => {
it('should return Object containing values to plot graph', () => {
const plotValues = vm.getGraphPlotValues(mockMedian, mockMetrics);
+
expect(plotValues.pathD).toBeDefined();
expect(Array.isArray(plotValues.pathD)).toBeTruthy();
@@ -90,7 +93,7 @@ describe('MemoryGraph', () => {
expect(el.querySelector('svg')).toBeDefined();
});
- it('should render graph when renderGraph is called internally', (done) => {
+ it('should render graph when renderGraph is called internally', done => {
const { pathD, pathViewBox, dotX, dotY } = vm.getGraphPlotValues(mockMedian, mockMetrics);
vm.height = defaultHeight;
vm.width = defaultWidth;
@@ -101,16 +104,21 @@ describe('MemoryGraph', () => {
Vue.nextTick(() => {
const svgEl = el.querySelector('svg');
+
expect(svgEl).toBeDefined();
expect(svgEl.getAttribute('height')).toBe(defaultHeight);
expect(svgEl.getAttribute('width')).toBe(defaultWidth);
const pathEl = el.querySelector('path');
+
expect(pathEl).toBeDefined();
expect(pathEl.getAttribute('d')).toBe(`M ${pathD}`);
- expect(pathEl.getAttribute('viewBox')).toBe(`0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`);
+ expect(pathEl.getAttribute('viewBox')).toBe(
+ `0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`,
+ );
const circleEl = el.querySelector('circle');
+
expect(circleEl).toBeDefined();
expect(circleEl.getAttribute('r')).toBe('1.5');
expect(circleEl.getAttribute('transform')).toBe('translate(0 -1)');
diff --git a/spec/javascripts/vue_shared/components/navigation_tabs_spec.js b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js
index 09fda95d7d3..462bfc10664 100644
--- a/spec/javascripts/vue_shared/components/navigation_tabs_spec.js
+++ b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js
@@ -46,7 +46,9 @@ describe('navigation tabs component', () => {
it('should render badge', () => {
expect(vm.$el.querySelector('.js-pipelines-tab-all .badge').textContent.trim()).toEqual('1');
- expect(vm.$el.querySelector('.js-pipelines-tab-pending .badge').textContent.trim()).toEqual('0');
+ expect(vm.$el.querySelector('.js-pipelines-tab-pending .badge').textContent.trim()).toEqual(
+ '0',
+ );
});
it('should not render badge', () => {
@@ -56,6 +58,7 @@ describe('navigation tabs component', () => {
it('should trigger onTabClick', () => {
spyOn(vm, '$emit');
vm.$el.querySelector('.js-pipelines-tab-pending').click();
+
expect(vm.$emit).toHaveBeenCalledWith('onChangeTab', 'pending');
});
});
diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js b/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
index db665fdaad3..45f131194ca 100644
--- a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
+++ b/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
@@ -26,6 +26,7 @@ describe('issue placeholder system note component', () => {
expect(vm.$el.querySelector('.user-avatar-link').getAttribute('href')).toEqual(
userDataMock.path,
);
+
expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual(
`${userDataMock.avatar_url}?width=40`,
);
@@ -37,6 +38,7 @@ describe('issue placeholder system note component', () => {
expect(vm.$el.querySelector('.note-header-info a').getAttribute('href')).toEqual(
userDataMock.path,
);
+
expect(
vm.$el.querySelector('.note-header-info .note-headline-light').textContent.trim(),
).toEqual(`@${userDataMock.username}`);
diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js b/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js
index 262571efcb8..6013e85811a 100644
--- a/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js
+++ b/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js
@@ -20,6 +20,8 @@ describe('placeholder system note component', () => {
});
expect(vm.$el.tagName).toEqual('LI');
- expect(vm.$el.querySelector('.timeline-content em').textContent.trim()).toEqual('This is a placeholder');
+ expect(vm.$el.querySelector('.timeline-content em').textContent.trim()).toEqual(
+ 'This is a placeholder',
+ );
});
});
diff --git a/spec/javascripts/vue_shared/components/notes/system_note_spec.js b/spec/javascripts/vue_shared/components/notes/system_note_spec.js
index 2a6015fe35f..adcb1c858aa 100644
--- a/spec/javascripts/vue_shared/components/notes/system_note_spec.js
+++ b/spec/javascripts/vue_shared/components/notes/system_note_spec.js
@@ -9,7 +9,7 @@ describe('system note component', () => {
beforeEach(() => {
props = {
note: {
- id: 1424,
+ id: '1424',
author: {
id: 1,
name: 'Root',
diff --git a/spec/javascripts/vue_shared/components/pagination_links_spec.js b/spec/javascripts/vue_shared/components/pagination_links_spec.js
new file mode 100644
index 00000000000..d0cb3731050
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/pagination_links_spec.js
@@ -0,0 +1,59 @@
+import Vue from 'vue';
+import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
+import { s__ } from '~/locale';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Pagination links component', () => {
+ const paginationLinksComponent = Vue.extend(PaginationLinks);
+ const change = page => page;
+ const pageInfo = {
+ page: 3,
+ perPage: 5,
+ total: 30,
+ };
+ const translations = {
+ firstText: s__('Pagination|« First'),
+ prevText: s__('Pagination|Prev'),
+ nextText: s__('Pagination|Next'),
+ lastText: s__('Pagination|Last »'),
+ };
+
+ let paginationLinks;
+ let glPagination;
+ let destinationComponent;
+
+ beforeEach(() => {
+ paginationLinks = mountComponent(paginationLinksComponent, {
+ change,
+ pageInfo,
+ });
+ [glPagination] = paginationLinks.$children;
+ [destinationComponent] = glPagination.$children;
+ });
+
+ afterEach(() => {
+ paginationLinks.$destroy();
+ });
+
+ it('should provide translated text to GitLab UI pagination', () => {
+ Object.entries(translations).forEach(entry => {
+ expect(destinationComponent[entry[0]]).toBe(entry[1]);
+ });
+ });
+
+ it('should pass change to GitLab UI pagination', () => {
+ expect(Object.is(glPagination.change, change)).toBe(true);
+ });
+
+ it('should pass page from pageInfo to GitLab UI pagination', () => {
+ expect(destinationComponent.value).toBe(pageInfo.page);
+ });
+
+ it('should pass per page from pageInfo to GitLab UI pagination', () => {
+ expect(destinationComponent.perPage).toBe(pageInfo.perPage);
+ });
+
+ it('should pass total items from pageInfo to GitLab UI pagination', () => {
+ expect(destinationComponent.totalRows).toBe(pageInfo.total);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
index f1e62069462..49a580be06b 100644
--- a/spec/javascripts/vue_shared/components/panel_resizer_spec.js
+++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
@@ -8,8 +8,23 @@ describe('Panel Resizer component', () => {
const triggerEvent = (eventName, el = vm.$el, clientX = 0) => {
const event = document.createEvent('MouseEvents');
- event.initMouseEvent(eventName, true, true, window, 1, clientX, 0, clientX, 0, false, false,
- false, false, 0, null);
+ event.initMouseEvent(
+ eventName,
+ true,
+ true,
+ window,
+ 1,
+ clientX,
+ 0,
+ clientX,
+ 0,
+ false,
+ false,
+ false,
+ false,
+ 0,
+ null,
+ );
el.dispatchEvent(event);
};
@@ -53,7 +68,13 @@ describe('Panel Resizer component', () => {
triggerEvent('mousedown', vm.$el);
triggerEvent('mousemove', document);
triggerEvent('mouseup', document);
- expect(vm.$emit.calls.allArgs()).toEqual([['resize-start', 100], ['update:size', 100], ['resize-end', 100]]);
+
+ expect(vm.$emit.calls.allArgs()).toEqual([
+ ['resize-start', 100],
+ ['update:size', 100],
+ ['resize-end', 100],
+ ]);
+
expect(vm.size).toBe(100);
});
});
diff --git a/spec/javascripts/vue_shared/components/pikaday_spec.js b/spec/javascripts/vue_shared/components/pikaday_spec.js
index b349e2a2a81..61f05e7a230 100644
--- a/spec/javascripts/vue_shared/components/pikaday_spec.js
+++ b/spec/javascripts/vue_shared/components/pikaday_spec.js
@@ -24,6 +24,7 @@ describe('datePicker', () => {
vm.$on('hidePicker', hidePicker);
vm.$el.querySelector('.dropdown-menu-toggle').click();
+
expect(hidePicker).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
index 8c296af6652..6bff1521695 100644
--- a/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
@@ -30,6 +30,7 @@ describe('collapsedCalendarIcon', () => {
vm.$on('click', click);
vm.$el.click();
+
expect(click).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
index 9d60f9c758f..c507a97d37e 100644
--- a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
@@ -11,36 +11,22 @@ describe('collapsedGroupedDatePicker', () => {
});
});
- it('should render toggle sidebar if showToggleSidebar', (done) => {
- expect(vm.$el.querySelector('.issuable-sidebar-header')).toBeDefined();
-
- vm.showToggleSidebar = false;
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.issuable-sidebar-header')).toBeNull();
- done();
- });
- });
-
describe('toggleCollapse events', () => {
- beforeEach((done) => {
+ beforeEach(done => {
spyOn(vm, 'toggleSidebar');
vm.minDate = new Date('07/17/2016');
Vue.nextTick(done);
});
- it('should emit when sidebar is toggled', () => {
- vm.$el.querySelector('.gutter-toggle').click();
- expect(vm.toggleSidebar).toHaveBeenCalled();
- });
-
it('should emit when collapsed-calendar-icon is clicked', () => {
vm.$el.querySelector('.sidebar-collapsed-icon').click();
+
expect(vm.toggleSidebar).toHaveBeenCalled();
});
});
describe('minDate and maxDate', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.minDate = new Date('07/17/2016');
vm.maxDate = new Date('07/17/2017');
Vue.nextTick(done);
@@ -48,6 +34,7 @@ describe('collapsedGroupedDatePicker', () => {
it('should render both collapsed-calendar-icon', () => {
const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+
expect(icons.length).toEqual(2);
expect(icons[0].innerText.trim()).toEqual('Jul 17 2016');
expect(icons[1].innerText.trim()).toEqual('Jul 17 2017');
@@ -55,26 +42,28 @@ describe('collapsedGroupedDatePicker', () => {
});
describe('minDate', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.minDate = new Date('07/17/2016');
Vue.nextTick(done);
});
it('should render minDate in collapsed-calendar-icon', () => {
const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+
expect(icons.length).toEqual(1);
expect(icons[0].innerText.trim()).toEqual('From Jul 17 2016');
});
});
describe('maxDate', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.maxDate = new Date('07/17/2017');
Vue.nextTick(done);
});
it('should render maxDate in collapsed-calendar-icon', () => {
const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+
expect(icons.length).toEqual(1);
expect(icons[0].innerText.trim()).toEqual('Until Jul 17 2017');
});
@@ -83,8 +72,15 @@ describe('collapsedGroupedDatePicker', () => {
describe('no dates', () => {
it('should render None', () => {
const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+
expect(icons.length).toEqual(1);
expect(icons[0].innerText.trim()).toEqual('None');
});
+
+ it('should have tooltip as `Start and due date`', () => {
+ const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+
+ expect(icons[0].dataset.originalTitle).toBe('Start and due date');
+ });
});
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
index 8840a5a9dbf..805ba7b9947 100644
--- a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
@@ -17,6 +17,7 @@ describe('sidebarDatePicker', () => {
vm.$on('toggleCollapse', toggleCollapse);
vm.$el.querySelector('.issuable-sidebar-header .gutter-toggle').click();
+
expect(toggleCollapse).toHaveBeenCalled();
});
@@ -40,7 +41,7 @@ describe('sidebarDatePicker', () => {
expect(vm.$el.querySelector('.value-content span').innerText.trim()).toEqual('None');
});
- it('should render date-picker when editing', (done) => {
+ it('should render date-picker when editing', done => {
vm.editing = true;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.pika-label')).toBeDefined();
@@ -49,7 +50,7 @@ describe('sidebarDatePicker', () => {
});
describe('editable', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.editable = true;
Vue.nextTick(done);
});
@@ -58,17 +59,18 @@ describe('sidebarDatePicker', () => {
expect(vm.$el.querySelector('.title .btn-blank').innerText.trim()).toEqual('Edit');
});
- it('should enable editing when edit button is clicked', (done) => {
+ it('should enable editing when edit button is clicked', done => {
vm.isLoading = false;
Vue.nextTick(() => {
vm.$el.querySelector('.title .btn-blank').click();
+
expect(vm.editing).toEqual(true);
done();
});
});
});
- it('should render date if selectedDate', (done) => {
+ it('should render date if selectedDate', done => {
vm.selectedDate = new Date('07/07/2017');
Vue.nextTick(() => {
expect(vm.$el.querySelector('.value-content strong').innerText.trim()).toEqual('Jul 7, 2017');
@@ -77,7 +79,7 @@ describe('sidebarDatePicker', () => {
});
describe('selectedDate and editable', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.selectedDate = new Date('07/07/2017');
vm.editable = true;
Vue.nextTick(done);
@@ -92,12 +94,13 @@ describe('sidebarDatePicker', () => {
vm.$on('saveDate', saveDate);
vm.$el.querySelector('.value-content .btn-blank').click();
+
expect(saveDate).toHaveBeenCalled();
});
});
describe('showToggleSidebar', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.showToggleSidebar = true;
Vue.nextTick(done);
});
@@ -111,6 +114,7 @@ describe('sidebarDatePicker', () => {
vm.$on('toggleCollapse', toggleCollapse);
vm.$el.querySelector('.title .gutter-toggle').click();
+
expect(toggleCollapse).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js
index e8685ab48be..c44b04009ca 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js
@@ -33,6 +33,7 @@ describe('BaseComponent', () => {
it('returns correct string when showCreate prop is `false`', () => {
const mockConfigNonEditable = Object.assign({}, mockConfig, { showCreate: false });
const vmNonEditable = createComponent(mockConfigNonEditable);
+
expect(vmNonEditable.hiddenInputName).toBe('label_id[]');
vmNonEditable.$destroy();
});
@@ -46,6 +47,7 @@ describe('BaseComponent', () => {
it('return `Create group label` when `isProject` prop is false', () => {
const mockConfigGroup = Object.assign({}, mockConfig, { isProject: false });
const vmGroup = createComponent(mockConfigGroup);
+
expect(vmGroup.createLabelTitle).toBe('Create group label');
vmGroup.$destroy();
});
@@ -59,6 +61,7 @@ describe('BaseComponent', () => {
it('return `Manage group labels` when `isProject` prop is false', () => {
const mockConfigGroup = Object.assign({}, mockConfig, { isProject: false });
const vmGroup = createComponent(mockConfigGroup);
+
expect(vmGroup.manageLabelsTitle).toBe('Manage group labels');
vmGroup.$destroy();
});
@@ -70,6 +73,7 @@ describe('BaseComponent', () => {
it('emits onLabelClick event with label and list of labels as params', () => {
spyOn(vm, '$emit');
vm.handleClick(mockLabels[0]);
+
expect(vm.$emit).toHaveBeenCalledWith('onLabelClick', mockLabels[0]);
});
});
@@ -78,6 +82,7 @@ describe('BaseComponent', () => {
it('emits toggleCollapse event on component', () => {
spyOn(vm, '$emit');
vm.handleCollapsedValueClick();
+
expect(vm.$emit).toHaveBeenCalledWith('toggleCollapse');
});
});
@@ -86,6 +91,7 @@ describe('BaseComponent', () => {
it('emits onDropdownClose event on component', () => {
spyOn(vm, '$emit');
vm.handleDropdownHidden();
+
expect(vm.$emit).toHaveBeenCalledWith('onDropdownClose');
});
});
@@ -114,6 +120,7 @@ describe('BaseComponent', () => {
it('renders `.dropdown-menu` element', () => {
const dropdownMenuEl = vm.$el.querySelector('.dropdown-menu');
+
expect(dropdownMenuEl).not.toBeNull();
expect(dropdownMenuEl.querySelector('.dropdown-page-one')).not.toBeNull();
expect(dropdownMenuEl.querySelector('.dropdown-content')).not.toBeNull();
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js
index f25c70db125..5cf6afebd7e 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js
@@ -34,6 +34,7 @@ describe('DropdownButtonComponent', () => {
it('returns text as `Label` when `labels` prop is empty array', () => {
const mockEmptyLabels = Object.assign({}, componentConfig, { labels: [] });
const vmEmptyLabels = createComponent(mockEmptyLabels);
+
expect(vmEmptyLabels.dropdownToggleText).toBe('Label');
vmEmptyLabels.$destroy();
});
@@ -43,6 +44,7 @@ describe('DropdownButtonComponent', () => {
labels: mockLabels.concat(mockLabels),
});
const vmMoreLabels = createComponent(mockMoreLabels);
+
expect(vmMoreLabels.dropdownToggleText).toBe('Foo Label +1 more');
vmMoreLabels.$destroy();
});
@@ -69,12 +71,14 @@ describe('DropdownButtonComponent', () => {
it('renders dropdown toggle text element', () => {
const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text');
+
expect(dropdownToggleTextEl).not.toBeNull();
expect(dropdownToggleTextEl.innerText.trim()).toBe('Foo Label');
});
it('renders dropdown button icon', () => {
const dropdownIconEl = vm.$el.querySelector('i.fa');
+
expect(dropdownIconEl).not.toBeNull();
expect(dropdownIconEl.classList.contains('fa-chevron-down')).toBe(true);
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js
index ce559fe0335..b8f32f96332 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js
@@ -6,7 +6,7 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockSuggestedColors } from './mock_data';
-const createComponent = (headerTitle) => {
+const createComponent = headerTitle => {
const Component = Vue.extend(dropdownCreateLabelComponent);
return mountComponent(Component, {
@@ -38,13 +38,17 @@ describe('DropdownCreateLabelComponent', () => {
});
it('renders `Go back` button on component header', () => {
- const backButtonEl = vm.$el.querySelector('.dropdown-title button.dropdown-title-button.dropdown-menu-back');
+ const backButtonEl = vm.$el.querySelector(
+ '.dropdown-title button.dropdown-title-button.dropdown-menu-back',
+ );
+
expect(backButtonEl).not.toBe(null);
expect(backButtonEl.querySelector('.fa-arrow-left')).not.toBe(null);
});
it('renders component header element as `Create new label` when `headerTitle` prop is not provided', () => {
const headerEl = vm.$el.querySelector('.dropdown-title');
+
expect(headerEl.innerText.trim()).toContain('Create new label');
});
@@ -52,12 +56,16 @@ describe('DropdownCreateLabelComponent', () => {
const headerTitle = 'Create project label';
const vmWithHeaderTitle = createComponent(headerTitle);
const headerEl = vmWithHeaderTitle.$el.querySelector('.dropdown-title');
+
expect(headerEl.innerText.trim()).toContain(headerTitle);
vmWithHeaderTitle.$destroy();
});
it('renders `Close` button on component header', () => {
- const closeButtonEl = vm.$el.querySelector('.dropdown-title button.dropdown-title-button.dropdown-menu-close');
+ const closeButtonEl = vm.$el.querySelector(
+ '.dropdown-title button.dropdown-title-button.dropdown-menu-close',
+ );
+
expect(closeButtonEl).not.toBe(null);
expect(closeButtonEl.querySelector('.fa-times.dropdown-menu-close-icon')).not.toBe(null);
});
@@ -69,23 +77,29 @@ describe('DropdownCreateLabelComponent', () => {
it('renders suggested colors list elements', () => {
const colorsListContainerEl = vm.$el.querySelector('.suggest-colors.suggest-colors-dropdown');
+
expect(colorsListContainerEl).not.toBe(null);
expect(colorsListContainerEl.querySelectorAll('a').length).toBe(mockSuggestedColors.length);
const colorItemEl = colorsListContainerEl.querySelectorAll('a')[0];
+
expect(colorItemEl.dataset.color).toBe(vm.suggestedColors[0]);
expect(colorItemEl.getAttribute('style')).toBe('background-color: rgb(0, 51, 204);');
});
it('renders color input element', () => {
expect(vm.$el.querySelector('.dropdown-label-color-input')).not.toBe(null);
- expect(vm.$el.querySelector('.dropdown-label-color-preview.js-dropdown-label-color-preview')).not.toBe(null);
+ expect(
+ vm.$el.querySelector('.dropdown-label-color-preview.js-dropdown-label-color-preview'),
+ ).not.toBe(null);
+
expect(vm.$el.querySelector('input#new_label_color.default-dropdown-input')).not.toBe(null);
});
it('renders component action buttons', () => {
const createBtnEl = vm.$el.querySelector('button.js-new-label-btn');
const cancelBtnEl = vm.$el.querySelector('button.js-cancel-label-btn');
+
expect(createBtnEl).not.toBe(null);
expect(createBtnEl.innerText.trim()).toBe('Create');
expect(cancelBtnEl.innerText.trim()).toBe('Cancel');
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js
index debeab25bd6..3711e9dac8c 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js
@@ -36,19 +36,24 @@ describe('DropdownFooterComponent', () => {
describe('template', () => {
it('renders link element with `Create new label` when `createLabelTitle` prop is not provided', () => {
const createLabelEl = vm.$el.querySelector('.dropdown-footer-list .dropdown-toggle-page');
+
expect(createLabelEl).not.toBeNull();
expect(createLabelEl.innerText.trim()).toBe('Create new label');
});
it('renders link element with value of `createLabelTitle` prop', () => {
const vmWithCreateLabelTitle = createComponent(mockConfig.labelsWebUrl, createLabelTitle);
- const createLabelEl = vmWithCreateLabelTitle.$el.querySelector('.dropdown-footer-list .dropdown-toggle-page');
+ const createLabelEl = vmWithCreateLabelTitle.$el.querySelector(
+ '.dropdown-footer-list .dropdown-toggle-page',
+ );
+
expect(createLabelEl.innerText.trim()).toBe(createLabelTitle);
vmWithCreateLabelTitle.$destroy();
});
it('renders link element with `Manage labels` when `manageLabelsTitle` prop is not provided', () => {
const manageLabelsEl = vm.$el.querySelector('.dropdown-footer-list .dropdown-external-link');
+
expect(manageLabelsEl).not.toBeNull();
expect(manageLabelsEl.getAttribute('href')).toBe(vm.labelsWebUrl);
expect(manageLabelsEl.innerText.trim()).toBe('Manage labels');
@@ -60,7 +65,10 @@ describe('DropdownFooterComponent', () => {
createLabelTitle,
manageLabelsTitle,
);
- const manageLabelsEl = vmWithManageLabelsTitle.$el.querySelector('.dropdown-footer-list .dropdown-external-link');
+ const manageLabelsEl = vmWithManageLabelsTitle.$el.querySelector(
+ '.dropdown-footer-list .dropdown-external-link',
+ );
+
expect(manageLabelsEl.innerText.trim()).toBe(manageLabelsTitle);
vmWithManageLabelsTitle.$destroy();
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js
index cdf234bb0c4..115e21e4f9f 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js
@@ -24,11 +24,15 @@ describe('DropdownHeaderComponent', () => {
describe('template', () => {
it('renders header text element', () => {
const headerEl = vm.$el.querySelector('.dropdown-title span');
+
expect(headerEl.innerText.trim()).toBe('Assign labels');
});
it('renders `Close` button element', () => {
- const closeBtnEl = vm.$el.querySelector('.dropdown-title button.dropdown-title-button.dropdown-menu-close');
+ const closeBtnEl = vm.$el.querySelector(
+ '.dropdown-title button.dropdown-title-button.dropdown-menu-close',
+ );
+
expect(closeBtnEl).not.toBeNull();
expect(closeBtnEl.querySelector('.fa-times.dropdown-menu-close-icon')).not.toBeNull();
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js
index 57608d957e7..c30e619e76b 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js
@@ -24,6 +24,7 @@ describe('DropdownSearchInputComponent', () => {
describe('template', () => {
it('renders input element with type `search`', () => {
const inputEl = vm.$el.querySelector('input.dropdown-input-field');
+
expect(inputEl).not.toBeNull();
expect(inputEl.getAttribute('type')).toBe('search');
});
@@ -33,7 +34,9 @@ describe('DropdownSearchInputComponent', () => {
});
it('renders clear search icon element', () => {
- expect(vm.$el.querySelector('.fa-times.dropdown-input-clear.js-dropdown-input-clear')).not.toBeNull();
+ expect(
+ vm.$el.querySelector('.fa-times.dropdown-input-clear.js-dropdown-input-clear'),
+ ).not.toBeNull();
});
});
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js
index 7c3d2711f65..6c84d2e167c 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js
@@ -35,6 +35,7 @@ describe('DropdownTitleComponent', () => {
it('renders `Edit` button element', () => {
const editBtnEl = vm.$el.querySelector('button.edit-link.js-sidebar-dropdown-toggle');
+
expect(editBtnEl).not.toBeNull();
expect(editBtnEl.innerText.trim()).toBe('Edit');
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
index da74595bcdc..804b33422bd 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
@@ -29,12 +29,14 @@ describe('DropdownValueCollapsedComponent', () => {
describe('labelsList', () => {
it('returns empty text when `labels` prop is empty array', () => {
const vmEmptyLabels = createComponent([]);
+
expect(vmEmptyLabels.labelsList).toBe('');
vmEmptyLabels.$destroy();
});
it('returns labels names separated by coma when `labels` prop has more than one item', () => {
const vmMoreLabels = createComponent(mockLabels.concat(mockLabels));
+
expect(vmMoreLabels.labelsList).toBe('Foo Label, Foo Label');
vmMoreLabels.$destroy();
});
@@ -46,7 +48,10 @@ describe('DropdownValueCollapsedComponent', () => {
}
const vmMoreLabels = createComponent(mockMoreLabels);
- expect(vmMoreLabels.labelsList).toBe('Foo Label, Foo Label, Foo Label, Foo Label, Foo Label, and 2 more');
+
+ expect(vmMoreLabels.labelsList).toBe(
+ 'Foo Label, Foo Label, Foo Label, Foo Label, Foo Label, and 2 more',
+ );
vmMoreLabels.$destroy();
});
@@ -61,6 +66,7 @@ describe('DropdownValueCollapsedComponent', () => {
it('emits onValueClick event on component', () => {
spyOn(vm, '$emit');
vm.handleClick();
+
expect(vm.$emit).toHaveBeenCalledWith('onValueClick');
});
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js
index 370a296bd8f..3fff781594f 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js
@@ -33,6 +33,7 @@ describe('DropdownValueComponent', () => {
describe('isEmpty', () => {
it('returns true if `labels` prop is empty', () => {
const vmEmptyLabels = createComponent([]);
+
expect(vmEmptyLabels.isEmpty).toBe(true);
vmEmptyLabels.$destroy();
});
@@ -46,9 +47,11 @@ describe('DropdownValueComponent', () => {
describe('methods', () => {
describe('labelFilterUrl', () => {
it('returns URL string starting with labelFilterBasePath and encoded label.title', () => {
- expect(vm.labelFilterUrl({
- title: 'Foo bar',
- })).toBe('/gitlab-org/my-project/issues?label_name[]=Foo%20bar');
+ expect(
+ vm.labelFilterUrl({
+ title: 'Foo bar',
+ }),
+ ).toBe('/gitlab-org/my-project/issues?label_name[]=Foo%20bar');
});
});
@@ -68,21 +71,29 @@ describe('DropdownValueComponent', () => {
describe('template', () => {
it('renders component container element with classes `hide-collapsed value issuable-show-labels`', () => {
- expect(vm.$el.classList.contains('hide-collapsed', 'value', 'issuable-show-labels')).toBe(true);
+ expect(vm.$el.classList.contains('hide-collapsed', 'value', 'issuable-show-labels')).toBe(
+ true,
+ );
});
it('render slot content inside component when `labels` prop is empty', () => {
const vmEmptyLabels = createComponent([]);
- expect(vmEmptyLabels.$el.querySelector('.text-secondary').innerText.trim()).toBe(mockConfig.emptyValueText);
+
+ expect(vmEmptyLabels.$el.querySelector('.text-secondary').innerText.trim()).toBe(
+ mockConfig.emptyValueText,
+ );
vmEmptyLabels.$destroy();
});
it('renders label element with filter URL', () => {
- expect(vm.$el.querySelector('a').getAttribute('href')).toBe('/gitlab-org/my-project/issues?label_name[]=Foo%20Label');
+ expect(vm.$el.querySelector('a').getAttribute('href')).toBe(
+ '/gitlab-org/my-project/issues?label_name[]=Foo%20Label',
+ );
});
it('renders label element with tooltip and styles based on label details', () => {
const labelEl = vm.$el.querySelector('a span.badge.color-label');
+
expect(labelEl).not.toBeNull();
expect(labelEl.dataset.placement).toBe('bottom');
expect(labelEl.dataset.container).toBe('body');
diff --git a/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js b/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js
deleted file mode 100644
index 34487885cf0..00000000000
--- a/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import Vue from 'vue';
-import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('Skeleton loading container', () => {
- let vm;
-
- beforeEach(() => {
- const component = Vue.extend(skeletonLoadingContainer);
- vm = mountComponent(component);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders 3 skeleton lines by default', () => {
- expect(vm.$el.querySelector('.skeleton-line-3')).not.toBeNull();
- });
-
- it('renders in full mode by default', () => {
- expect(vm.$el.classList.contains('animation-container-small')).toBeFalsy();
- });
-
- describe('small', () => {
- beforeEach((done) => {
- vm.small = true;
-
- Vue.nextTick(done);
- });
-
- it('renders in small mode', () => {
- expect(vm.$el.classList.contains('animation-container-small')).toBeTruthy();
- });
- });
-
- describe('lines', () => {
- beforeEach((done) => {
- vm.lines = 5;
-
- Vue.nextTick(done);
- });
-
- it('renders 5 lines', () => {
- expect(vm.$el.querySelector('.skeleton-line-5')).not.toBeNull();
- expect(vm.$el.querySelector('.skeleton-line-6')).toBeNull();
- });
- });
-});
diff --git a/spec/javascripts/vue_shared/components/smart_virtual_list_spec.js b/spec/javascripts/vue_shared/components/smart_virtual_list_spec.js
new file mode 100644
index 00000000000..e723fead65e
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/smart_virtual_list_spec.js
@@ -0,0 +1,83 @@
+import Vue from 'vue';
+import SmartVirtualScrollList from '~/vue_shared/components/smart_virtual_list.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Toggle Button', () => {
+ let vm;
+
+ const createComponent = ({ length, remain }) => {
+ const smartListProperties = {
+ rtag: 'section',
+ wtag: 'ul',
+ wclass: 'test-class',
+ // Size in pixels does not matter for our tests here
+ size: 35,
+ length,
+ remain,
+ };
+
+ const Component = Vue.extend({
+ components: {
+ SmartVirtualScrollList,
+ },
+ smartListProperties,
+ items: Array(length).fill(1),
+ template: `
+ <smart-virtual-scroll-list v-bind="$options.smartListProperties">
+ <li v-for="(val, key) in $options.items" :key="key">{{ key + 1 }}</li>
+ </smart-virtual-scroll-list>`,
+ });
+
+ return mountComponent(Component);
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('if the list is shorter than the maximum shown elements', () => {
+ const listLength = 10;
+
+ beforeEach(() => {
+ vm = createComponent({ length: listLength, remain: 20 });
+ });
+
+ it('renders without the vue-virtual-scroll-list component', () => {
+ expect(vm.$el.classList).not.toContain('js-virtual-list');
+ expect(vm.$el.classList).toContain('js-plain-element');
+ });
+
+ it('renders list with provided tags and classes for the wrapper elements', () => {
+ expect(vm.$el.tagName).toEqual('SECTION');
+ expect(vm.$el.firstChild.tagName).toEqual('UL');
+ expect(vm.$el.firstChild.classList).toContain('test-class');
+ });
+
+ it('renders all children list elements', () => {
+ expect(vm.$el.querySelectorAll('li').length).toEqual(listLength);
+ });
+ });
+
+ describe('if the list is longer than the maximum shown elements', () => {
+ const maxItemsShown = 20;
+
+ beforeEach(() => {
+ vm = createComponent({ length: 1000, remain: maxItemsShown });
+ });
+
+ it('uses the vue-virtual-scroll-list component', () => {
+ expect(vm.$el.classList).toContain('js-virtual-list');
+ expect(vm.$el.classList).not.toContain('js-plain-element');
+ });
+
+ it('renders list with provided tags and classes for the wrapper elements', () => {
+ expect(vm.$el.tagName).toEqual('SECTION');
+ expect(vm.$el.firstChild.tagName).toEqual('UL');
+ expect(vm.$el.firstChild.classList).toContain('test-class');
+ });
+
+ it('renders at max twice the maximum shown elements', () => {
+ expect(vm.$el.querySelectorAll('li').length).toBeLessThanOrEqual(2 * maxItemsShown);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
index 076d940961d..073d111989c 100644
--- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
+++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
@@ -4,16 +4,20 @@ import stackedProgressBarComponent from '~/vue_shared/components/stacked_progres
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-const createComponent = (config) => {
+const createComponent = config => {
const Component = Vue.extend(stackedProgressBarComponent);
- const defaultConfig = Object.assign({}, {
- successLabel: 'Synced',
- failureLabel: 'Failed',
- neutralLabel: 'Out of sync',
- successCount: 25,
- failureCount: 10,
- totalCount: 5000,
- }, config);
+ const defaultConfig = Object.assign(
+ {},
+ {
+ successLabel: 'Synced',
+ failureLabel: 'Failed',
+ neutralLabel: 'Out of sync',
+ successCount: 25,
+ failureCount: 10,
+ totalCount: 5000,
+ },
+ config,
+ );
return mountComponent(Component, defaultConfig);
};
@@ -44,7 +48,11 @@ describe('StackedProgressBarComponent', () => {
});
it('returns percentage with decimal place from provided count based on `totalCount`', () => {
- expect(vm.getPercent(10)).toBe(0.2);
+ expect(vm.getPercent(67)).toBe(1.3);
+ });
+
+ it('returns percentage as `< 1` from provided count based on `totalCount` when evaluated value is less than 1', () => {
+ expect(vm.getPercent(10)).toBe('< 1');
});
});
@@ -68,6 +76,7 @@ describe('StackedProgressBarComponent', () => {
it('renders empty state when count is unavailable', () => {
const vmX = createComponent({ totalCount: 0, successCount: 0, failureCount: 0 });
+
expect(vmX.$el.querySelectorAll('.status-unavailable').length).not.toBe(0);
vmX.$destroy();
});
diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js
index c36b607a34e..0dcb712e720 100644
--- a/spec/javascripts/vue_shared/components/table_pagination_spec.js
+++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js
@@ -5,13 +5,13 @@ describe('Pagination component', () => {
let component;
let PaginationComponent;
let spy;
- let mountComponet;
+ let mountComponent;
beforeEach(() => {
spy = jasmine.createSpy('spy');
PaginationComponent = Vue.extend(paginationComp);
- mountComponet = function (props) {
+ mountComponent = function(props) {
return new PaginationComponent({
propsData: props,
}).$mount();
@@ -20,7 +20,7 @@ describe('Pagination component', () => {
describe('render', () => {
it('should not render anything', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 1,
page: 1,
@@ -37,7 +37,7 @@ describe('Pagination component', () => {
describe('prev button', () => {
it('should be disabled and non clickable', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 2,
page: 1,
@@ -59,7 +59,7 @@ describe('Pagination component', () => {
});
it('should be enabled and clickable', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 3,
page: 2,
@@ -72,13 +72,14 @@ describe('Pagination component', () => {
});
component.$el.querySelector('.js-previous-button a').click();
+
expect(spy).toHaveBeenCalledWith(1);
});
});
describe('first button', () => {
it('should call the change callback with the first page', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 3,
page: 2,
@@ -102,7 +103,7 @@ describe('Pagination component', () => {
describe('last button', () => {
it('should call the change callback with the last page', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 3,
page: 2,
@@ -126,7 +127,7 @@ describe('Pagination component', () => {
describe('next button', () => {
it('should be disabled and non clickable', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 5,
page: 5,
@@ -138,9 +139,7 @@ describe('Pagination component', () => {
change: spy,
});
- expect(
- component.$el.querySelector('.js-next-button').textContent.trim(),
- ).toEqual('Next');
+ expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next');
component.$el.querySelector('.js-next-button a').click();
@@ -148,7 +147,7 @@ describe('Pagination component', () => {
});
it('should be enabled and clickable', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 4,
page: 3,
@@ -168,7 +167,7 @@ describe('Pagination component', () => {
describe('numbered buttons', () => {
it('should render 5 pages', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 4,
page: 3,
@@ -185,7 +184,7 @@ describe('Pagination component', () => {
});
it('should render the spread operator', () => {
- component = mountComponet({
+ component = mountComponent({
pageInfo: {
nextPage: 4,
page: 3,
diff --git a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
index b4fb568f1d4..745571d0a97 100644
--- a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
+++ b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
@@ -22,9 +22,10 @@ describe('Time ago with tooltip component', () => {
}).$mount();
expect(vm.$el.tagName).toEqual('TIME');
- expect(
- vm.$el.getAttribute('data-original-title'),
- ).toEqual(formatDate('2017-05-08T14:57:39.781Z'));
+ expect(vm.$el.getAttribute('data-original-title')).toEqual(
+ formatDate('2017-05-08T14:57:39.781Z'),
+ );
+
expect(vm.$el.getAttribute('data-placement')).toEqual('top');
const timeago = getTimeago();
diff --git a/spec/javascripts/vue_shared/components/toggle_button_spec.js b/spec/javascripts/vue_shared/components/toggle_button_spec.js
index 71952cc39e0..444ca451534 100644
--- a/spec/javascripts/vue_shared/components/toggle_button_spec.js
+++ b/spec/javascripts/vue_shared/components/toggle_button_spec.js
@@ -51,9 +51,11 @@ describe('Toggle Button', () => {
it('sets aria-label representing toggle state', () => {
vm.value = true;
+
expect(vm.ariaLabel).toEqual('Toggle Status: ON');
vm.value = false;
+
expect(vm.ariaLabel).toEqual('Toggle Status: OFF');
});
diff --git a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js
new file mode 100644
index 00000000000..997d84dcc42
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js
@@ -0,0 +1,156 @@
+import { mountComponentWithRender } from 'spec/helpers/vue_mount_component_helper';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+
+const TEST_TITLE = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do';
+const CLASS_SHOW_TOOLTIP = 'js-show-tooltip';
+const STYLE_TRUNCATED = {
+ display: 'inline-block',
+ 'max-width': '20px',
+};
+const STYLE_NORMAL = {
+ display: 'inline-block',
+ 'max-width': '1000px',
+};
+
+function mountTooltipOnTruncate(options, createChildren) {
+ return mountComponentWithRender(h => h(TooltipOnTruncate, options, createChildren(h)), '#app');
+}
+
+describe('TooltipOnTruncate component', () => {
+ let vm;
+
+ beforeEach(() => {
+ const el = document.createElement('div');
+ el.id = 'app';
+ document.body.appendChild(el);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with default target', () => {
+ it('renders tooltip if truncated', done => {
+ const options = {
+ style: STYLE_TRUNCATED,
+ props: {
+ title: TEST_TITLE,
+ },
+ };
+
+ vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]);
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
+ expect(vm.$el).toHaveData('original-title', TEST_TITLE);
+ expect(vm.$el).toHaveData('placement', 'top');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not render tooltip if normal', done => {
+ const options = {
+ style: STYLE_NORMAL,
+ props: {
+ title: TEST_TITLE,
+ },
+ };
+
+ vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]);
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('with child target', () => {
+ it('renders tooltip if truncated', done => {
+ const options = {
+ style: STYLE_NORMAL,
+ props: {
+ title: TEST_TITLE,
+ truncateTarget: 'child',
+ },
+ };
+
+ vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]);
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not render tooltip if normal', done => {
+ const options = {
+ props: {
+ title: TEST_TITLE,
+ truncateTarget: 'child',
+ },
+ };
+
+ vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_NORMAL }, TEST_TITLE)]);
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('with fn target', () => {
+ it('renders tooltip if truncated', done => {
+ const options = {
+ style: STYLE_NORMAL,
+ props: {
+ title: TEST_TITLE,
+ truncateTarget: el => el.childNodes[1],
+ },
+ };
+
+ vm = mountTooltipOnTruncate(options, h => [
+ h('a', { style: STYLE_NORMAL }, TEST_TITLE),
+ h('span', { style: STYLE_TRUNCATED }, TEST_TITLE),
+ ]);
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('placement', () => {
+ it('sets data-placement when tooltip is rendered', done => {
+ const options = {
+ props: {
+ title: TEST_TITLE,
+ truncateTarget: 'child',
+ placement: 'bottom',
+ },
+ };
+
+ vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]);
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
+ expect(vm.$el).toHaveData('placement', options.props.placement);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
index dc7652c77f7..5c4aa7cf844 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { placeholderImage } from '~/lazy_loader';
import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import mountComponent, { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper';
const DEFAULT_PROPS = {
size: 99,
@@ -32,18 +32,12 @@ describe('User Avatar Image Component', function() {
});
it('should have <img> as a child element', function() {
- expect(vm.$el.tagName).toBe('IMG');
- expect(vm.$el.getAttribute('src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
- expect(vm.$el.getAttribute('data-src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
- expect(vm.$el.getAttribute('alt')).toBe(DEFAULT_PROPS.imgAlt);
- });
-
- it('should properly compute tooltipContainer', function() {
- expect(vm.tooltipContainer).toBe('body');
- });
+ const imageElement = vm.$el.querySelector('img');
- it('should properly render tooltipContainer', function() {
- expect(vm.$el.getAttribute('data-container')).toBe('body');
+ expect(imageElement).not.toBe(null);
+ expect(imageElement.getAttribute('src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
+ expect(imageElement.getAttribute('data-src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
+ expect(imageElement.getAttribute('alt')).toBe(DEFAULT_PROPS.imgAlt);
});
it('should properly compute avatarSizeClass', function() {
@@ -51,7 +45,7 @@ describe('User Avatar Image Component', function() {
});
it('should properly render img css', function() {
- const { classList } = vm.$el;
+ const { classList } = vm.$el.querySelector('img');
const containsAvatar = classList.contains('avatar');
const containsSizeClass = classList.contains('s99');
const containsCustomClass = classList.contains(DEFAULT_PROPS.cssClasses);
@@ -73,12 +67,41 @@ describe('User Avatar Image Component', function() {
});
it('should add lazy attributes', function() {
- const { classList } = vm.$el;
- const lazyClass = classList.contains('lazy');
+ const imageElement = vm.$el.querySelector('img');
+ const lazyClass = imageElement.classList.contains('lazy');
expect(lazyClass).toBe(true);
- expect(vm.$el.getAttribute('src')).toBe(placeholderImage);
- expect(vm.$el.getAttribute('data-src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
+ expect(imageElement.getAttribute('src')).toBe(placeholderImage);
+ expect(imageElement.getAttribute('data-src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
+ });
+ });
+
+ describe('dynamic tooltip content', () => {
+ const props = DEFAULT_PROPS;
+ const slots = {
+ default: ['Action!'],
+ };
+
+ beforeEach(() => {
+ vm = mountComponentWithSlots(UserAvatarImage, { props, slots }).$mount();
+ });
+
+ it('renders the tooltip slot', () => {
+ expect(vm.$el.querySelector('.js-user-avatar-image-toolip')).not.toBe(null);
+ });
+
+ it('renders the tooltip content', () => {
+ expect(vm.$el.querySelector('.js-user-avatar-image-toolip').textContent).toContain(
+ slots.default[0],
+ );
+ });
+
+ it('does not render tooltip data attributes for on avatar image', () => {
+ const avatarImg = vm.$el.querySelector('img');
+
+ expect(avatarImg.dataset.originalTitle).not.toBeDefined();
+ expect(avatarImg.dataset.placement).not.toBeDefined();
+ expect(avatarImg.dataset.container).not.toBeDefined();
});
});
});
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
index 4c5c242cbb3..0151ad23ba2 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
@@ -2,8 +2,8 @@ import _ from 'underscore';
import Vue from 'vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
-describe('User Avatar Link Component', function () {
- beforeEach(function () {
+describe('User Avatar Link Component', function() {
+ beforeEach(function() {
this.propsData = {
linkHref: 'myavatarurl.com',
imgSize: 99,
@@ -24,67 +24,80 @@ describe('User Avatar Link Component', function () {
[this.userAvatarImage] = this.userAvatarLink.$children;
});
- it('should return a defined Vue component', function () {
+ it('should return a defined Vue component', function() {
expect(this.userAvatarLink).toBeDefined();
});
- it('should have user-avatar-image registered as child component', function () {
+ it('should have user-avatar-image registered as child component', function() {
expect(this.userAvatarLink.$options.components.userAvatarImage).toBeDefined();
});
- it('user-avatar-link should have user-avatar-image as child component', function () {
+ it('user-avatar-link should have user-avatar-image as child component', function() {
expect(this.userAvatarImage).toBeDefined();
});
- it('should render <a> as a child element', function () {
+ it('should render <a> as a child element', function() {
expect(this.userAvatarLink.$el.tagName).toBe('A');
});
- it('should have <img> as a child element', function () {
+ it('should have <img> as a child element', function() {
expect(this.userAvatarLink.$el.querySelector('img')).not.toBeNull();
});
- it('should return neccessary props as defined', function () {
+ it('should return necessary props as defined', function() {
_.each(this.propsData, (val, key) => {
expect(this.userAvatarLink[key]).toBeDefined();
});
});
- describe('no username', function () {
- beforeEach(function (done) {
+ describe('no username', function() {
+ beforeEach(function(done) {
this.userAvatarLink.username = '';
Vue.nextTick(done);
});
- it('should only render image tag in link', function () {
+ it('should only render image tag in link', function() {
const childElements = this.userAvatarLink.$el.childNodes;
- expect(childElements[0].tagName).toBe('IMG');
+
+ expect(this.userAvatarLink.$el.querySelector('img')).not.toBe('null');
// Vue will render the hidden component as <!---->
expect(childElements[1].tagName).toBeUndefined();
});
- it('should render avatar image tooltip', function () {
- expect(this.userAvatarLink.$el.querySelector('img').dataset.originalTitle).toEqual(this.propsData.tooltipText);
+ it('should render avatar image tooltip', function() {
+ expect(this.userAvatarLink.shouldShowUsername).toBe(false);
+ expect(this.userAvatarLink.avatarTooltipText).toEqual(this.propsData.tooltipText);
});
});
- describe('username', function () {
- it('should not render avatar image tooltip', function () {
- expect(this.userAvatarLink.$el.querySelector('img').dataset.originalTitle).toEqual('');
+ describe('username', function() {
+ it('should not render avatar image tooltip', function() {
+ expect(
+ this.userAvatarLink.$el.querySelector('.js-user-avatar-image-toolip').innerText.trim(),
+ ).toEqual('');
});
- it('should render username prop in <span>', function () {
- expect(this.userAvatarLink.$el.querySelector('span').innerText.trim()).toEqual(this.propsData.username);
+ it('should render username prop in <span>', function() {
+ expect(
+ this.userAvatarLink.$el.querySelector('.js-user-avatar-link-username').innerText.trim(),
+ ).toEqual(this.propsData.username);
});
- it('should render text tooltip for <span>', function () {
- expect(this.userAvatarLink.$el.querySelector('span').dataset.originalTitle).toEqual(this.propsData.tooltipText);
+ it('should render text tooltip for <span>', function() {
+ expect(
+ this.userAvatarLink.$el.querySelector('.js-user-avatar-link-username').dataset
+ .originalTitle,
+ ).toEqual(this.propsData.tooltipText);
});
- it('should render text tooltip placement for <span>', function () {
- expect(this.userAvatarLink.$el.querySelector('span').getAttribute('tooltip-placement')).toEqual(this.propsData.tooltipPlacement);
+ it('should render text tooltip placement for <span>', function() {
+ expect(
+ this.userAvatarLink.$el
+ .querySelector('.js-user-avatar-link-username')
+ .getAttribute('tooltip-placement'),
+ ).toEqual(this.propsData.tooltipPlacement);
});
});
});
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js
index b8d639ffbec..9152fa8e12f 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js
@@ -4,9 +4,9 @@ import avatarSvg from 'icons/_icon_random.svg';
const UserAvatarSvgComponent = Vue.extend(UserAvatarSvg);
-describe('User Avatar Svg Component', function () {
- describe('Initialization', function () {
- beforeEach(function () {
+describe('User Avatar Svg Component', function() {
+ describe('Initialization', function() {
+ beforeEach(function() {
this.propsData = {
size: 99,
svg: avatarSvg,
@@ -17,11 +17,11 @@ describe('User Avatar Svg Component', function () {
}).$mount();
});
- it('should return a defined Vue component', function () {
+ it('should return a defined Vue component', function() {
expect(this.userAvatarSvg).toBeDefined();
});
- it('should have <svg> as a child element', function () {
+ it('should have <svg> as a child element', function() {
expect(this.userAvatarSvg.$el.tagName).toEqual('svg');
expect(this.userAvatarSvg.$el.innerHTML).toContain('<path');
});
diff --git a/spec/javascripts/vue_shared/directives/tooltip_spec.js b/spec/javascripts/vue_shared/directives/tooltip_spec.js
index 4a644913e44..1d516a280b0 100644
--- a/spec/javascripts/vue_shared/directives/tooltip_spec.js
+++ b/spec/javascripts/vue_shared/directives/tooltip_spec.js
@@ -13,24 +13,45 @@ describe('Tooltip directive', () => {
describe('with a single tooltip', () => {
beforeEach(() => {
- const SomeComponent = Vue.extend({
+ setFixtures('<div id="dummy-element"></div>');
+ vm = new Vue({
+ el: '#dummy-element',
directives: {
tooltip,
},
- template: `
- <div
- v-tooltip
- title="foo">
- </div>
- `,
+ data() {
+ return {
+ tooltip: 'some text',
+ };
+ },
+ template: '<div v-tooltip :title="tooltip"></div>',
});
-
- vm = new SomeComponent().$mount();
});
it('should have tooltip plugin applied', () => {
expect($(vm.$el).data('bs.tooltip')).toBeDefined();
});
+
+ it('displays the title as tooltip', () => {
+ $(vm.$el).tooltip('show');
+ const tooltipElement = document.querySelector('.tooltip-inner');
+
+ expect(tooltipElement.innerText).toContain('some text');
+ });
+
+ it('updates a visible tooltip', done => {
+ $(vm.$el).tooltip('show');
+ const tooltipElement = document.querySelector('.tooltip-inner');
+
+ vm.tooltip = 'other text';
+
+ Vue.nextTick()
+ .then(() => {
+ expect(tooltipElement).toContainText('other text');
+ done();
+ })
+ .catch(done.fail);
+ });
});
describe('with multiple tooltips', () => {
@@ -58,7 +79,11 @@ describe('Tooltip directive', () => {
});
it('should have tooltip plugin applied to all instances', () => {
- expect($(vm.$el).find('.js-look-for-tooltip').data('bs.tooltip')).toBeDefined();
+ expect(
+ $(vm.$el)
+ .find('.js-look-for-tooltip')
+ .data('bs.tooltip'),
+ ).toBeDefined();
});
});
});
diff --git a/spec/javascripts/vue_shared/translate_spec.js b/spec/javascripts/vue_shared/translate_spec.js
index cbb3cbdff46..adb5ff682f0 100644
--- a/spec/javascripts/vue_shared/translate_spec.js
+++ b/spec/javascripts/vue_shared/translate_spec.js
@@ -1,88 +1,249 @@
import Vue from 'vue';
-import Translate from '~/vue_shared/translate';
+import Jed from 'jed';
-Vue.use(Translate);
+import locale from '~/locale';
+import Translate from '~/vue_shared/translate';
+import { trimText } from 'spec/helpers/vue_component_helper';
describe('Vue translate filter', () => {
let el;
+ const createTranslationMock = (key, ...translations) => {
+ const fakeLocale = new Jed({
+ domain: 'app',
+ locale_data: {
+ app: {
+ '': {
+ domain: 'app',
+ lang: 'vo',
+ plural_forms: 'nplurals=2; plural=(n != 1);',
+ },
+ [key]: translations,
+ },
+ },
+ });
+
+ // eslint-disable-next-line no-underscore-dangle
+ locale.__Rewire__('locale', fakeLocale);
+ };
+
+ afterEach(() => {
+ // eslint-disable-next-line no-underscore-dangle
+ locale.__ResetDependency__('locale');
+ });
+
beforeEach(() => {
+ Vue.use(Translate);
+
el = document.createElement('div');
document.body.appendChild(el);
});
- it('translate single text', (done) => {
- const comp = new Vue({
+ it('translate singular text (`__`)', done => {
+ const key = 'singular';
+ const translation = 'singular_translated';
+ createTranslationMock(key, translation);
+
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ __('${key}') }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe(translation);
+
+ done();
+ });
+ });
+
+ it('translate plural text (`n__`) without any substituting text', done => {
+ const key = 'plural';
+ const translationPlural = 'plural_multiple translation';
+ createTranslationMock(key, 'plural_singular translation', translationPlural);
+
+ const vm = new Vue({
el,
template: `
<span>
- {{ __('testing') }}
+ {{ n__('${key}', 'plurals', 2) }}
</span>
`,
}).$mount();
Vue.nextTick(() => {
- expect(
- comp.$el.textContent.trim(),
- ).toBe('testing');
+ expect(trimText(vm.$el.textContent)).toBe(translationPlural);
done();
});
});
- it('translate plural text with single count', (done) => {
- const comp = new Vue({
+ describe('translate plural text (`n__`) with substituting %d', () => {
+ const key = '%d day';
+
+ beforeEach(() => {
+ createTranslationMock(key, '%d singular translated', '%d plural translated');
+ });
+
+ it('and n === 1', done => {
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ n__('${key}', '%d days', 1) }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe('1 singular translated');
+
+ done();
+ });
+ });
+
+ it('and n > 1', done => {
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ n__('${key}', '%d days', 2) }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe('2 plural translated');
+
+ done();
+ });
+ });
+ });
+
+ describe('translates text with context `s__`', () => {
+ const key = 'Context|Foobar';
+ const translation = 'Context|Foobar translated';
+ const expectation = 'Foobar translated';
+
+ beforeEach(() => {
+ createTranslationMock(key, translation);
+ });
+
+ it('and using two parameters', done => {
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ s__('Context', 'Foobar') }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe(expectation);
+
+ done();
+ });
+ });
+
+ it('and using the pipe syntax', done => {
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ s__('${key}') }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe(expectation);
+
+ done();
+ });
+ });
+ });
+
+ it('translate multi line text', done => {
+ const translation = 'multiline string translated';
+ createTranslationMock('multiline string', translation);
+
+ const vm = new Vue({
el,
template: `
<span>
- {{ n__('%d day', '%d days', 1) }}
+ {{ __(\`
+ multiline
+ string
+ \`) }}
</span>
`,
}).$mount();
Vue.nextTick(() => {
- expect(
- comp.$el.textContent.trim(),
- ).toBe('1 day');
+ expect(trimText(vm.$el.textContent)).toBe(translation);
done();
});
});
- it('translate plural text with multiple count', (done) => {
- const comp = new Vue({
+ it('translate pluralized multi line text', done => {
+ const translation = 'multiline string plural';
+
+ createTranslationMock('multiline string', 'multiline string singular', translation);
+
+ const vm = new Vue({
el,
template: `
<span>
- {{ n__('%d day', '%d days', 2) }}
+ {{ n__(
+ \`
+ multiline
+ string
+ \`,
+ \`
+ multiline
+ strings
+ \`,
+ 2
+ ) }}
</span>
`,
}).$mount();
Vue.nextTick(() => {
- expect(
- comp.$el.textContent.trim(),
- ).toBe('2 days');
+ expect(trimText(vm.$el.textContent)).toBe(translation);
done();
});
});
- it('translate plural without replacing any text', (done) => {
- const comp = new Vue({
+ it('translate pluralized multi line text with context', done => {
+ const translation = 'multiline string with context';
+
+ createTranslationMock('Context| multiline string', translation);
+
+ const vm = new Vue({
el,
template: `
<span>
- {{ n__('day', 'days', 2) }}
+ {{ s__(
+ \`
+ Context|
+ multiline
+ string
+ \`
+ ) }}
</span>
`,
}).$mount();
Vue.nextTick(() => {
- expect(
- comp.$el.textContent.trim(),
- ).toBe('days');
+ expect(trimText(vm.$el.textContent)).toBe(translation);
done();
});
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index bdeebe0de75..e5f1e6ae937 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -46,12 +46,14 @@ describe('ZenMode', () => {
it('should not call dropzone if element is not dropzone valid', () => {
$('.div-dropzone').addClass('js-invalid-dropzone');
exitZen();
+
expect(dropzoneForElementSpy.calls.count()).toEqual(0);
});
it('should call dropzone if element is dropzone valid', () => {
$('.div-dropzone').removeClass('js-invalid-dropzone');
exitZen();
+
expect(dropzoneForElementSpy.calls.count()).toEqual(2);
});
});
@@ -60,12 +62,14 @@ describe('ZenMode', () => {
it('pauses Mousetrap', () => {
const mouseTrapPauseSpy = spyOn(Mousetrap, 'pause');
enterZen();
+
expect(mouseTrapPauseSpy).toHaveBeenCalled();
});
it('removes textarea styling', () => {
$('.notes-form textarea').attr('style', 'height: 400px');
enterZen();
+
expect($('.notes-form textarea')).not.toHaveAttr('style');
});
});
@@ -75,6 +79,7 @@ describe('ZenMode', () => {
it('exits on Escape', () => {
escapeKeydown();
+
expect($('.notes-form .zen-backdrop')).not.toHaveClass('fullscreen');
});
});
@@ -85,12 +90,14 @@ describe('ZenMode', () => {
it('unpauses Mousetrap', () => {
const mouseTrapUnpauseSpy = spyOn(Mousetrap, 'unpause');
exitZen();
+
expect(mouseTrapUnpauseSpy).toHaveBeenCalled();
});
it('restores the scroll position', () => {
spyOn(zen, 'scrollTo');
exitZen();
+
expect(zen.scrollTo).toHaveBeenCalled();
});
});
diff --git a/spec/lib/api/helpers/custom_validators_spec.rb b/spec/lib/api/helpers/custom_validators_spec.rb
new file mode 100644
index 00000000000..41e6fb47b11
--- /dev/null
+++ b/spec/lib/api/helpers/custom_validators_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe API::Helpers::CustomValidators do
+ let(:scope) do
+ Struct.new(:opts) do
+ def full_name(attr_name)
+ attr_name
+ end
+ end
+ end
+
+ describe API::Helpers::CustomValidators::Absence do
+ subject do
+ described_class.new(['test'], {}, false, scope.new)
+ end
+
+ context 'empty param' do
+ it 'does not raise a validation error' do
+ expect_no_validation_error({})
+ end
+ end
+
+ context 'invalid parameters' do
+ it 'should raise a validation error' do
+ expect_validation_error({ 'test' => 'some_value' })
+ end
+ end
+ end
+
+ describe API::Helpers::CustomValidators::IntegerNoneAny do
+ subject do
+ described_class.new(['test'], {}, false, scope.new)
+ end
+
+ context 'valid parameters' do
+ it 'does not raise a validation error' do
+ expect_no_validation_error({ 'test' => 2 })
+ expect_no_validation_error({ 'test' => 100 })
+ expect_no_validation_error({ 'test' => 'None' })
+ expect_no_validation_error({ 'test' => 'Any' })
+ expect_no_validation_error({ 'test' => 'none' })
+ expect_no_validation_error({ 'test' => 'any' })
+ end
+ end
+
+ context 'invalid parameters' do
+ it 'should raise a validation error' do
+ expect_validation_error({ 'test' => 'some_other_string' })
+ end
+ end
+ end
+
+ def expect_no_validation_error(params)
+ expect { validate_test_param!(params) }.not_to raise_error
+ end
+
+ def expect_validation_error(params)
+ expect { validate_test_param!(params) }.to raise_error(Grape::Exceptions::Validation)
+ end
+
+ def validate_test_param!(params)
+ subject.validate_param!('test', params)
+ end
+end
diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb
index c73c6023b60..0a7682d906b 100644
--- a/spec/lib/api/helpers/pagination_spec.rb
+++ b/spec/lib/api/helpers/pagination_spec.rb
@@ -189,9 +189,9 @@ describe API::Helpers::Pagination do
it 'it returns the right link to the next page' do
allow(subject).to receive(:params)
.and_return({ pagination: 'keyset', ks_prev_id: projects[3].id, ks_prev_name: projects[3].name, per_page: 2 })
+
expect_header('X-Per-Page', '2')
expect_header('X-Next-Page', "#{value}?ks_prev_id=#{projects[6].id}&ks_prev_name=#{projects[6].name}&pagination=keyset&per_page=2")
-
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
end
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index ca319679e80..9633caac788 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -11,10 +11,6 @@ describe Backup::Manager do
allow(progress).to receive(:puts)
allow(progress).to receive(:print)
- allow_any_instance_of(String).to receive(:color) do |string, _color|
- string
- end
-
@old_progress = $progress # rubocop:disable Style/GlobalVars
$progress = progress # rubocop:disable Style/GlobalVars
end
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index c5a854b5660..fdeea814bb2 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -11,10 +11,6 @@ describe Backup::Repository do
allow(FileUtils).to receive(:mkdir_p).and_return(true)
allow(FileUtils).to receive(:mv).and_return(true)
- allow_any_instance_of(String).to receive(:color) do |string, _color|
- string
- end
-
allow_any_instance_of(described_class).to receive(:progress).and_return(progress)
end
diff --git a/spec/lib/banzai/cross_project_reference_spec.rb b/spec/lib/banzai/cross_project_reference_spec.rb
index aadfe7637dd..ba995e16be7 100644
--- a/spec/lib/banzai/cross_project_reference_spec.rb
+++ b/spec/lib/banzai/cross_project_reference_spec.rb
@@ -1,16 +1,21 @@
require 'spec_helper'
describe Banzai::CrossProjectReference do
- include described_class
+ let(:including_class) { Class.new.include(described_class).new }
+
+ before do
+ allow(including_class).to receive(:context).and_return({})
+ allow(including_class).to receive(:parent_from_ref).and_call_original
+ end
describe '#parent_from_ref' do
context 'when no project was referenced' do
it 'returns the project from context' do
project = double
- allow(self).to receive(:context).and_return({ project: project })
+ allow(including_class).to receive(:context).and_return({ project: project })
- expect(parent_from_ref(nil)).to eq project
+ expect(including_class.parent_from_ref(nil)).to eq project
end
end
@@ -18,15 +23,15 @@ describe Banzai::CrossProjectReference do
it 'returns the group from context' do
group = double
- allow(self).to receive(:context).and_return({ group: group })
+ allow(including_class).to receive(:context).and_return({ group: group })
- expect(parent_from_ref(nil)).to eq group
+ expect(including_class.parent_from_ref(nil)).to eq group
end
end
context 'when referenced project does not exist' do
it 'returns nil' do
- expect(parent_from_ref('invalid/reference')).to be_nil
+ expect(including_class.parent_from_ref('invalid/reference')).to be_nil
end
end
@@ -37,7 +42,7 @@ describe Banzai::CrossProjectReference do
expect(Project).to receive(:find_by_full_path)
.with('cross/reference').and_return(project2)
- expect(parent_from_ref('cross/reference')).to eq project2
+ expect(including_class.parent_from_ref('cross/reference')).to eq project2
end
end
end
diff --git a/spec/lib/banzai/filter/autolink_filter_spec.rb b/spec/lib/banzai/filter/autolink_filter_spec.rb
index a50329473ad..7a457403b51 100644
--- a/spec/lib/banzai/filter/autolink_filter_spec.rb
+++ b/spec/lib/banzai/filter/autolink_filter_spec.rb
@@ -76,7 +76,7 @@ describe Banzai::Filter::AutolinkFilter do
expect(doc.at_css('a')['href']).to eq link
end
- it 'autolinks multiple occurences of smb' do
+ it 'autolinks multiple occurrences of smb' do
link1 = 'smb:///Volumes/shared/foo.pdf'
link2 = 'smb:///Volumes/shared/bar.pdf'
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index e1af5a15371..cbff2fdab14 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -60,6 +60,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
exp = act = "See #{commit1.id.reverse}...#{commit2.id}"
allow(project.repository).to receive(:commit).with(commit1.id.reverse)
+ allow(project.repository).to receive(:commit).with(commit2.id)
expect(reference_filter(act).to_html).to eq exp
end
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index d9018a7e4fe..a0270d93d50 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -79,13 +79,9 @@ describe Banzai::Filter::ExternalIssueReferenceFilter do
expect(link).to eq helper.url_for_issue(issue_id, project, only_path: true)
end
- context 'with RequestStore enabled' do
+ context 'with RequestStore enabled', :request_store do
let(:reference_filter) { HTML::Pipeline.new([described_class]) }
- before do
- allow(RequestStore).to receive(:active?).and_return(true)
- end
-
it 'queries the collection on the first call' do
expect_any_instance_of(Project).to receive(:default_issues_tracker?).once.and_call_original
expect_any_instance_of(Project).to receive(:external_issue_reference_pattern).once.and_call_original
@@ -105,15 +101,24 @@ describe Banzai::Filter::ExternalIssueReferenceFilter do
context "redmine project" do
let(:project) { create(:redmine_project) }
- let(:issue) { ExternalIssue.new("#123", project) }
- let(:reference) { issue.to_reference }
before do
- project.issues_enabled = false
- project.save!
+ project.update!(issues_enabled: false)
end
- it_behaves_like "external issue tracker"
+ context "with a hash prefix" do
+ let(:issue) { ExternalIssue.new("#123", project) }
+ let(:reference) { issue.to_reference }
+
+ it_behaves_like "external issue tracker"
+ end
+
+ context "with a single-letter prefix" do
+ let(:issue) { ExternalIssue.new("T-123", project) }
+ let(:reference) { issue.to_reference }
+
+ it_behaves_like "external issue tracker"
+ end
end
context "jira project" do
@@ -126,6 +131,15 @@ describe Banzai::Filter::ExternalIssueReferenceFilter do
it_behaves_like "external issue tracker"
end
+ context "with a single-letter prefix" do
+ let(:issue) { ExternalIssue.new("J-123", project) }
+
+ it "ignores reference" do
+ exp = act = "Issue #{reference}"
+ expect(filter(act).to_html).to eq exp
+ end
+ end
+
context "with wrong markdown" do
let(:issue) { ExternalIssue.new("#123", project) }
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index a515d07b072..cf49249756a 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -40,6 +40,12 @@ describe Banzai::Filter::MarkdownFilter do
expect(result).to start_with("<pre><code>")
end
+
+ it 'works with utf8 chars in language' do
+ result = filter("```æ—¥\nsome code\n```")
+
+ expect(result).to start_with("<pre><code lang=\"æ—¥\">")
+ end
end
context 'using Redcarpet' do
@@ -60,4 +66,21 @@ describe Banzai::Filter::MarkdownFilter do
end
end
end
+
+ describe 'footnotes in tables' do
+ it 'processes footnotes in table cells' do
+ text = <<-MD.strip_heredoc
+ | Column1 |
+ | --------- |
+ | foot [^1] |
+
+ [^1]: a footnote
+ MD
+
+ result = filter(text)
+
+ expect(result).to include('<td>foot <sup')
+ expect(result).to include('<section class="footnotes">')
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index ba8dc68ceda..415ded05e6e 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -83,6 +83,11 @@ describe Banzai::Filter::RelativeLinkFilter do
expect { filter(act) }.not_to raise_error
end
+ it 'does not raise an exception with a space in the path' do
+ act = link("/uploads/d18213acd3732630991986120e167e3d/Landscape_8.jpg \nBut here's some more unexpected text :smile:)")
+ expect { filter(act) }.not_to raise_error
+ end
+
it 'ignores ref if commit is passed' do
doc = filter(link('non/existent.file'), commit: project.commit('empty-branch') )
expect(doc.at_css('a')['href'])
@@ -221,7 +226,7 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:ref) {'mark#\'@],+;-._/#@!$&()+down'}
it 'correctly escapes the ref' do
- # Adressable won't escape the '#', so we do this manually
+ # Addressable won't escape the '#', so we do this manually
ref_escaped = 'mark%23\'@%5D,+;-._/%23@!$&()+down'
# Stub this method so the branch doesn't actually need to be in the repo
diff --git a/spec/lib/banzai/filter/spaced_link_filter_spec.rb b/spec/lib/banzai/filter/spaced_link_filter_spec.rb
new file mode 100644
index 00000000000..1ad7f3ff567
--- /dev/null
+++ b/spec/lib/banzai/filter/spaced_link_filter_spec.rb
@@ -0,0 +1,96 @@
+require 'spec_helper'
+
+describe Banzai::Filter::SpacedLinkFilter do
+ include FilterSpecHelper
+
+ let(:link) { '[example](page slug)' }
+ let(:image) { '![example](img test.jpg)' }
+
+ context 'when a link is detected' do
+ it 'converts slug with spaces to a link' do
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a').text).to eq 'example'
+ expect(doc.at_css('a')['href']).to eq 'page%20slug'
+ expect(doc.at_css('a')['title']).to be_nil
+ expect(doc.at_css('p')).to be_nil
+ end
+
+ it 'converts slug with spaces and a title to a link' do
+ link = '[example](page slug "title")'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a').text).to eq 'example'
+ expect(doc.at_css('a')['href']).to eq 'page%20slug'
+ expect(doc.at_css('a')['title']).to eq 'title'
+ expect(doc.at_css('p')).to be_nil
+ end
+
+ it 'does nothing when markdown_engine is redcarpet' do
+ exp = act = link
+ expect(filter(act, markdown_engine: :redcarpet).to_html).to eq exp
+ end
+
+ it 'does nothing with empty text' do
+ link = '[](page slug)'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a')).to be_nil
+ end
+
+ it 'does nothing with an empty slug' do
+ link = '[example]()'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a')).to be_nil
+ end
+ end
+
+ context 'when an image is detected' do
+ it 'converts slug with spaces to an iamge' do
+ doc = filter("See #{image}")
+
+ expect(doc.at_css('img')['src']).to eq 'img%20test.jpg'
+ expect(doc.at_css('img')['alt']).to eq 'example'
+ expect(doc.at_css('p')).to be_nil
+ end
+
+ it 'converts slug with spaces and a title to an image' do
+ image = '![example](img test.jpg "title")'
+ doc = filter("See #{image}")
+
+ expect(doc.at_css('img')['src']).to eq 'img%20test.jpg'
+ expect(doc.at_css('img')['alt']).to eq 'example'
+ expect(doc.at_css('img')['title']).to eq 'title'
+ expect(doc.at_css('p')).to be_nil
+ end
+ end
+
+ it 'converts multiple URLs' do
+ link1 = '[first](slug one)'
+ link2 = '[second](http://example.com/slug two)'
+ doc = filter("See #{link1} and #{image} and #{link2}")
+
+ found_links = doc.css('a')
+
+ expect(found_links.size).to eq(2)
+ expect(found_links[0].text).to eq 'first'
+ expect(found_links[0]['href']).to eq 'slug%20one'
+ expect(found_links[1].text).to eq 'second'
+ expect(found_links[1]['href']).to eq 'http://example.com/slug%20two'
+
+ found_images = doc.css('img')
+
+ expect(found_images.size).to eq(1)
+ expect(found_images[0]['src']).to eq 'img%20test.jpg'
+ expect(found_images[0]['alt']).to eq 'example'
+ end
+
+ described_class::IGNORE_PARENTS.each do |elem|
+ it "ignores valid links contained inside '#{elem}' element" do
+ exp = act = "<#{elem}>See #{link}</#{elem}>"
+
+ expect(filter(act).to_html).to eq exp
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/wiki_link_filter_spec.rb b/spec/lib/banzai/filter/wiki_link_filter_spec.rb
index 50d053011b3..b9059b85fdc 100644
--- a/spec/lib/banzai/filter/wiki_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/wiki_link_filter_spec.rb
@@ -7,6 +7,7 @@ describe Banzai::Filter::WikiLinkFilter do
let(:project) { build_stubbed(:project, :public, name: "wiki_link_project", namespace: namespace) }
let(:user) { double }
let(:wiki) { ProjectWiki.new(project, user) }
+ let(:repository_upload_folder) { Wikis::CreateAttachmentService::ATTACHMENT_PATH }
it "doesn't rewrite absolute links" do
filtered_link = filter("<a href='http://example.com:8000/'>Link</a>", project_wiki: wiki).children[0]
@@ -20,6 +21,45 @@ describe Banzai::Filter::WikiLinkFilter do
expect(filtered_link.attribute('href').value).to eq('/uploads/a.test')
end
+ describe "when links point to the #{Wikis::CreateAttachmentService::ATTACHMENT_PATH} folder" do
+ context 'with an "a" html tag' do
+ it 'rewrites links' do
+ filtered_link = filter("<a href='#{repository_upload_folder}/a.test'>Link</a>", project_wiki: wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.test")
+ end
+ end
+
+ context 'with "img" html tag' do
+ let(:path) { "#{wiki.wiki_base_path}/#{repository_upload_folder}/a.jpg" }
+
+ context 'inside an "a" html tag' do
+ it 'rewrites links' do
+ filtered_elements = filter("<a href='#{repository_upload_folder}/a.jpg'><img src='#{repository_upload_folder}/a.jpg'>example</img></a>", project_wiki: wiki)
+
+ expect(filtered_elements.search('img').first.attribute('src').value).to eq(path)
+ expect(filtered_elements.search('a').first.attribute('href').value).to eq(path)
+ end
+ end
+
+ context 'outside an "a" html tag' do
+ it 'rewrites links' do
+ filtered_link = filter("<img src='#{repository_upload_folder}/a.jpg'>example</img>", project_wiki: wiki).children[0]
+
+ expect(filtered_link.attribute('src').value).to eq(path)
+ end
+ end
+ end
+
+ context 'with "video" html tag' do
+ it 'rewrites links' do
+ filtered_link = filter("<video src='#{repository_upload_folder}/a.mp4'></video>", project_wiki: wiki).children[0]
+
+ expect(filtered_link.attribute('src').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.mp4")
+ end
+ end
+ end
+
describe "invalid links" do
invalid_links = ["http://:8080", "http://", "http://:8080/path"]
diff --git a/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb b/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
index 75413596431..df24cef0b8b 100644
--- a/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
@@ -87,4 +87,22 @@ describe Banzai::Pipeline::GfmPipeline do
end
end
end
+
+ describe 'markdown link or image urls having spaces' do
+ let(:project) { create(:project, :public) }
+
+ it 'rewrites links with spaces in url' do
+ markdown = "[Link to Page](page slug)"
+ output = described_class.to_html(markdown, project: project)
+
+ expect(output).to include("href=\"page%20slug\"")
+ end
+
+ it 'rewrites images with spaces in url' do
+ markdown = "![My Image](test image.png)"
+ output = described_class.to_html(markdown, project: project)
+
+ expect(output).to include("src=\"test%20image.png\"")
+ end
+ end
end
diff --git a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
index 88ae4c1e07a..64ca3ec345d 100644
--- a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
@@ -121,6 +121,13 @@ describe Banzai::Pipeline::WikiPipeline do
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
end
+ it 'rewrites non-file links (with spaces) to be at the scope of the wiki root' do
+ markdown = "[Link to Page](page slug)"
+ output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page%20slug\"")
+ end
+
it "rewrites file links to be at the scope of the current directory" do
markdown = "[Link to Page](page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
@@ -134,6 +141,13 @@ describe Banzai::Pipeline::WikiPipeline do
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start-page#title\"")
end
+
+ it 'rewrites links (with spaces) with anchor' do
+ markdown = '[Link to Header](start page#title)'
+ output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start%20page#title\"")
+ end
end
describe "when creating root links" do
@@ -164,4 +178,25 @@ describe Banzai::Pipeline::WikiPipeline do
end
end
end
+
+ describe 'videos' do
+ let(:namespace) { create(:namespace, name: "wiki_link_ns") }
+ let(:project) { create(:project, :public, name: "wiki_link_project", namespace: namespace) }
+ let(:project_wiki) { ProjectWiki.new(project, double(:user)) }
+ let(:page) { build(:wiki_page, wiki: project_wiki, page: OpenStruct.new(url_path: 'nested/twice/start-page')) }
+
+ it 'generates video html structure' do
+ markdown = "![video_file](video_file_name.mp4)"
+ output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+ expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video_file_name.mp4"')
+ end
+
+ it 'rewrites and replaces video links names with white spaces to %20' do
+ markdown = "![video file](video file name.mp4)"
+ output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+ expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video%20file%20name.mp4"')
+ end
+ end
end
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
index 4e6e8eca38a..c6e9fc414a1 100644
--- a/spec/lib/banzai/reference_parser/base_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -263,11 +263,10 @@ describe Banzai::ReferenceParser::BaseParser do
end
end
- context 'with RequestStore enabled' do
+ context 'with RequestStore enabled', :request_store do
before do
cache = Hash.new { |hash, key| hash[key] = {} }
- allow(RequestStore).to receive(:active?).and_return(true)
allow(subject).to receive(:collection_cache).and_return(cache)
end
diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
index cca53a8b9b9..f558dea209f 100644
--- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
@@ -120,4 +120,22 @@ describe Banzai::ReferenceParser::CommitParser do
expect(subject.find_commits(project, %w{123})).to eq([])
end
end
+
+ context 'when checking commits on another projects' do
+ let(:control_links) do
+ [commit_link]
+ end
+
+ let(:actual_links) do
+ control_links + [commit_link, commit_link]
+ end
+
+ def commit_link
+ project = create(:project, :repository, :public)
+
+ Nokogiri::HTML.fragment(%Q{<a data-commit="#{project.commit.id}" data-project="#{project.id}"></a>}).children[0]
+ end
+
+ it_behaves_like 'no project N+1 queries'
+ end
end
diff --git a/spec/lib/bitbucket_server/client_spec.rb b/spec/lib/bitbucket_server/client_spec.rb
index f926ae963a4..5de0a9a65b5 100644
--- a/spec/lib/bitbucket_server/client_spec.rb
+++ b/spec/lib/bitbucket_server/client_spec.rb
@@ -13,7 +13,7 @@ describe BitbucketServer::Client do
let(:path) { "/projects/#{project}/repos/#{repo_slug}/pull-requests?state=ALL" }
it 'requests a collection' do
- expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :pull_request)
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :pull_request, page_offset: 0, limit: nil)
subject.pull_requests(project, repo_slug)
end
@@ -29,7 +29,7 @@ describe BitbucketServer::Client do
let(:path) { "/projects/#{project}/repos/#{repo_slug}/pull-requests/1/activities" }
it 'requests a collection' do
- expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :activity)
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :activity, page_offset: 0, limit: nil)
subject.activities(project, repo_slug, 1)
end
@@ -52,10 +52,16 @@ describe BitbucketServer::Client do
let(:path) { "/repos" }
it 'requests a collection' do
- expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :repo)
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :repo, page_offset: 0, limit: nil)
subject.repos
end
+
+ it 'requests a collection with an offset and limit' do
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :repo, page_offset: 10, limit: 25)
+
+ subject.repos(page_offset: 10, limit: 25)
+ end
end
describe '#create_branch' do
diff --git a/spec/lib/bitbucket_server/collection_spec.rb b/spec/lib/bitbucket_server/collection_spec.rb
new file mode 100644
index 00000000000..ddd02bac88a
--- /dev/null
+++ b/spec/lib/bitbucket_server/collection_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe BitbucketServer::Collection do
+ let(:connection) { instance_double(BitbucketServer::Connection) }
+ let(:page) { 1 }
+ let(:paginator) { BitbucketServer::Paginator.new(connection, 'http://more-data', :pull_request, page_offset: page) }
+
+ subject { described_class.new(paginator) }
+
+ describe '#current_page' do
+ it 'returns 1' do
+ expect(subject.current_page).to eq(1)
+ end
+ end
+
+ describe '#prev_page' do
+ it 'returns nil' do
+ expect(subject.prev_page).to be_nil
+ end
+ end
+
+ describe '#next_page' do
+ it 'returns 2' do
+ expect(subject.next_page).to eq(2)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/paginator_spec.rb b/spec/lib/bitbucket_server/paginator_spec.rb
index 2de50eba3c4..d268d4f23cf 100644
--- a/spec/lib/bitbucket_server/paginator_spec.rb
+++ b/spec/lib/bitbucket_server/paginator_spec.rb
@@ -20,6 +20,16 @@ describe BitbucketServer::Paginator do
expect { paginator.items }.to raise_error(StopIteration)
end
+ it 'obeys limits' do
+ limited = described_class.new(connection, 'http://more-data', :pull_request, page_offset: 0, limit: 1)
+ allow(limited).to receive(:fetch_next_page).and_return(first_page)
+
+ expect(limited.has_next_page?).to be_truthy
+ expect(limited.items).to match(['item_1'])
+ expect(limited.has_next_page?).to be_truthy
+ expect { limited.items }.to raise_error(StopIteration)
+ end
+
it 'calls the connection with different offsets' do
expect(connection).to receive(:get).with('http://more-data', start: 0, limit: BitbucketServer::Paginator::PAGE_LENGTH).and_return(page_attrs)
diff --git a/spec/lib/container_registry/blob_spec.rb b/spec/lib/container_registry/blob_spec.rb
index c73faa55513..d3fff5bad42 100644
--- a/spec/lib/container_registry/blob_spec.rb
+++ b/spec/lib/container_registry/blob_spec.rb
@@ -64,7 +64,7 @@ describe ContainerRegistry::Blob do
.to_return(status: 200)
end
- it 'returns true when blob has been successfuly deleted' do
+ it 'returns true when blob has been successfully deleted' do
expect(blob.delete).to be_truthy
end
end
diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb
index 87ae6b6cf01..30016da6828 100644
--- a/spec/lib/event_filter_spec.rb
+++ b/spec/lib/event_filter_spec.rb
@@ -1,58 +1,119 @@
require 'spec_helper'
describe EventFilter do
+ describe 'FILTERS' do
+ it 'returns a definite list of filters' do
+ expect(described_class::FILTERS).to eq(%w[all push merged issue comments team])
+ end
+ end
+
+ describe '#filter' do
+ it 'returns "all" if given filter is nil' do
+ expect(described_class.new(nil).filter).to eq(described_class::ALL)
+ end
+
+ it 'returns "all" if given filter is ""' do
+ expect(described_class.new('').filter).to eq(described_class::ALL)
+ end
+
+ it 'returns "all" if given filter is "foo"' do
+ expect(described_class.new('foo').filter).to eq('all')
+ end
+ end
+
describe '#apply_filter' do
- let(:source_user) { create(:user) }
- let!(:public_project) { create(:project, :public) }
+ set(:public_project) { create(:project, :public) }
+
+ set(:push_event) { create(:push_event, project: public_project) }
+ set(:merged_event) { create(:event, :merged, project: public_project, target: public_project) }
+ set(:created_event) { create(:event, :created, project: public_project, target: public_project) }
+ set(:updated_event) { create(:event, :updated, project: public_project, target: public_project) }
+ set(:closed_event) { create(:event, :closed, project: public_project, target: public_project) }
+ set(:reopened_event) { create(:event, :reopened, project: public_project, target: public_project) }
+ set(:comments_event) { create(:event, :commented, project: public_project, target: public_project) }
+ set(:joined_event) { create(:event, :joined, project: public_project, target: public_project) }
+ set(:left_event) { create(:event, :left, project: public_project, target: public_project) }
- let!(:push_event) { create(:push_event, project: public_project, author: source_user) }
- let!(:merged_event) { create(:event, :merged, project: public_project, target: public_project, author: source_user) }
- let!(:created_event) { create(:event, :created, project: public_project, target: public_project, author: source_user) }
- let!(:updated_event) { create(:event, :updated, project: public_project, target: public_project, author: source_user) }
- let!(:closed_event) { create(:event, :closed, project: public_project, target: public_project, author: source_user) }
- let!(:reopened_event) { create(:event, :reopened, project: public_project, target: public_project, author: source_user) }
- let!(:comments_event) { create(:event, :commented, project: public_project, target: public_project, author: source_user) }
- let!(:joined_event) { create(:event, :joined, project: public_project, target: public_project, author: source_user) }
- let!(:left_event) { create(:event, :left, project: public_project, target: public_project, author: source_user) }
+ let(:filtered_events) { described_class.new(filter).apply_filter(Event.all) }
- it 'applies push filter' do
- events = described_class.new(described_class.push).apply_filter(Event.all)
- expect(events).to contain_exactly(push_event)
+ context 'with the "push" filter' do
+ let(:filter) { described_class::PUSH }
+
+ it 'filters push events only' do
+ expect(filtered_events).to contain_exactly(push_event)
+ end
end
- it 'applies merged filter' do
- events = described_class.new(described_class.merged).apply_filter(Event.all)
- expect(events).to contain_exactly(merged_event)
+ context 'with the "merged" filter' do
+ let(:filter) { described_class::MERGED }
+
+ it 'filters merged events only' do
+ expect(filtered_events).to contain_exactly(merged_event)
+ end
end
- it 'applies issue filter' do
- events = described_class.new(described_class.issue).apply_filter(Event.all)
- expect(events).to contain_exactly(created_event, updated_event, closed_event, reopened_event)
+ context 'with the "issue" filter' do
+ let(:filter) { described_class::ISSUE }
+
+ it 'filters issue events only' do
+ expect(filtered_events).to contain_exactly(created_event, updated_event, closed_event, reopened_event)
+ end
end
- it 'applies comments filter' do
- events = described_class.new(described_class.comments).apply_filter(Event.all)
- expect(events).to contain_exactly(comments_event)
+ context 'with the "comments" filter' do
+ let(:filter) { described_class::COMMENTS }
+
+ it 'filters comment events only' do
+ expect(filtered_events).to contain_exactly(comments_event)
+ end
end
- it 'applies team filter' do
- events = described_class.new(described_class.team).apply_filter(Event.all)
- expect(events).to contain_exactly(joined_event, left_event)
+ context 'with the "team" filter' do
+ let(:filter) { described_class::TEAM }
+
+ it 'filters team events only' do
+ expect(filtered_events).to contain_exactly(joined_event, left_event)
+ end
end
- it 'applies all filter' do
- events = described_class.new(described_class.all).apply_filter(Event.all)
- expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event)
+ context 'with the "all" filter' do
+ let(:filter) { described_class::ALL }
+
+ it 'returns all events' do
+ expect(filtered_events).to eq(Event.all)
+ end
+ end
+
+ context 'with an unknown filter' do
+ let(:filter) { 'foo' }
+
+ it 'returns all events' do
+ expect(filtered_events).to eq(Event.all)
+ end
+ end
+
+ context 'with a nil filter' do
+ let(:filter) { nil }
+
+ it 'returns all events' do
+ expect(filtered_events).to eq(Event.all)
+ end
+ end
+ end
+
+ describe '#active?' do
+ let(:event_filter) { described_class.new(described_class::TEAM) }
+
+ it 'returns false if filter does not include the given key' do
+ expect(event_filter.active?('foo')).to eq(false)
end
- it 'applies no filter' do
- events = described_class.new(nil).apply_filter(Event.all)
- expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event)
+ it 'returns false if the given key is nil' do
+ expect(event_filter.active?(nil)).to eq(false)
end
- it 'applies unknown filter' do
- events = described_class.new('').apply_filter(Event.all)
- expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event)
+ it 'returns true if filter does not include the given key' do
+ expect(event_filter.active?(described_class::TEAM)).to eq(true)
end
end
end
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index f313e675654..9d56c62ae57 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -1,6 +1,13 @@
require 'spec_helper'
describe Feature do
+ before do
+ # We mock all calls to .enabled? to return true in order to force all
+ # specs to run the feature flag gated behavior, but here we need a clean
+ # behavior from the class
+ allow(described_class).to receive(:enabled?).and_call_original
+ end
+
describe '.get' do
let(:feature) { double(:feature) }
let(:key) { 'my_feature' }
@@ -84,7 +91,11 @@ describe Feature do
end
describe '.flipper' do
- shared_examples 'a memoized Flipper instance' do
+ before do
+ described_class.instance_variable_set(:@flipper, nil)
+ end
+
+ context 'when request store is inactive' do
it 'memoizes the Flipper instance' do
expect(Flipper).to receive(:new).once.and_call_original
@@ -94,16 +105,81 @@ describe Feature do
end
end
- context 'when request store is inactive' do
- before do
+ context 'when request store is active', :request_store do
+ it 'memoizes the Flipper instance' do
+ expect(Flipper).to receive(:new).once.and_call_original
+
+ described_class.flipper
described_class.instance_variable_set(:@flipper, nil)
+ described_class.flipper
end
+ end
+ end
+
+ describe '.enabled?' do
+ it 'returns false for undefined feature' do
+ expect(described_class.enabled?(:some_random_feature_flag)).to be_falsey
+ end
+
+ it 'returns true for undefined feature with default_enabled' do
+ expect(described_class.enabled?(:some_random_feature_flag, default_enabled: true)).to be_truthy
+ end
- it_behaves_like 'a memoized Flipper instance'
+ it 'returns false for existing disabled feature in the database' do
+ described_class.disable(:disabled_feature_flag)
+
+ expect(described_class.enabled?(:disabled_feature_flag)).to be_falsey
end
- context 'when request store is inactive', :request_store do
- it_behaves_like 'a memoized Flipper instance'
+ it 'returns true for existing enabled feature in the database' do
+ described_class.enable(:enabled_feature_flag)
+
+ expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy
+ end
+
+ context 'with an individual actor' do
+ CustomActor = Struct.new(:flipper_id)
+
+ let(:actor) { CustomActor.new(flipper_id: 'CustomActor:5') }
+ let(:another_actor) { CustomActor.new(flipper_id: 'CustomActor:10') }
+
+ before do
+ described_class.enable(:enabled_feature_flag, actor)
+ end
+
+ it 'returns true when same actor is informed' do
+ expect(described_class.enabled?(:enabled_feature_flag, actor)).to be_truthy
+ end
+
+ it 'returns false when different actor is informed' do
+ expect(described_class.enabled?(:enabled_feature_flag, another_actor)).to be_falsey
+ end
+
+ it 'returns false when no actor is informed' do
+ expect(described_class.enabled?(:enabled_feature_flag)).to be_falsey
+ end
+ end
+ end
+
+ describe '.disable?' do
+ it 'returns true for undefined feature' do
+ expect(described_class.disabled?(:some_random_feature_flag)).to be_truthy
+ end
+
+ it 'returns false for undefined feature with default_enabled' do
+ expect(described_class.disabled?(:some_random_feature_flag, default_enabled: true)).to be_falsey
+ end
+
+ it 'returns true for existing disabled feature in the database' do
+ described_class.disable(:disabled_feature_flag)
+
+ expect(described_class.disabled?(:disabled_feature_flag)).to be_truthy
+ end
+
+ it 'returns false for existing enabled feature in the database' do
+ described_class.enable(:enabled_feature_flag)
+
+ expect(described_class.disabled?(:enabled_feature_flag)).to be_falsey
end
end
end
diff --git a/spec/lib/forever_spec.rb b/spec/lib/forever_spec.rb
index cf40c467c72..494c0561975 100644
--- a/spec/lib/forever_spec.rb
+++ b/spec/lib/forever_spec.rb
@@ -7,6 +7,7 @@ describe Forever do
context 'when using PostgreSQL' do
it 'should return Postgresql future date' do
allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
+
expect(subject).to eq(described_class::POSTGRESQL_DATE)
end
end
@@ -14,6 +15,7 @@ describe Forever do
context 'when using MySQL' do
it 'should return MySQL future date' do
allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
+
expect(subject).to eq(described_class::MYSQL_DATE)
end
end
diff --git a/spec/lib/gitaly/server_spec.rb b/spec/lib/gitaly/server_spec.rb
index 09bf21b5946..292ab870dad 100644
--- a/spec/lib/gitaly/server_spec.rb
+++ b/spec/lib/gitaly/server_spec.rb
@@ -26,9 +26,7 @@ describe Gitaly::Server do
end
end
- context 'when the storage is not readable' do
- let(:server) { described_class.new('broken') }
-
+ context 'when the storage is not readable', :broken_storage do
it 'returns false' do
expect(server).not_to be_readable
end
@@ -42,9 +40,7 @@ describe Gitaly::Server do
end
end
- context 'when the storage is not writeable' do
- let(:server) { described_class.new('broken') }
-
+ context 'when the storage is not writeable', :broken_storage do
it 'returns false' do
expect(server).not_to be_writeable
end
diff --git a/spec/lib/gitlab/auth/ldap/access_spec.rb b/spec/lib/gitlab/auth/ldap/access_spec.rb
index eff21985108..662f899180b 100644
--- a/spec/lib/gitlab/auth/ldap/access_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/access_spec.rb
@@ -3,51 +3,61 @@ require 'spec_helper'
describe Gitlab::Auth::LDAP::Access do
include LdapHelpers
- let(:access) { described_class.new user }
let(:user) { create(:omniauth_user) }
+ subject(:access) { described_class.new(user) }
+
describe '.allowed?' do
- it 'updates the users `last_credential_check_at' do
+ before do
allow(access).to receive(:update_user)
- expect(access).to receive(:allowed?) { true }
- expect(described_class).to receive(:open).and_yield(access)
+ allow(access).to receive(:allowed?).and_return(true)
+ allow(described_class).to receive(:open).and_yield(access)
+ end
+ it "updates the user's `last_credential_check_at`" do
expect { described_class.allowed?(user) }
.to change { user.last_credential_check_at }
end
- end
- describe '#find_ldap_user' do
- it 'finds a user by dn first' do
- expect(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(:ldap_user)
+ it "does not update user's `last_credential_check_at` when in a read-only GitLab instance" do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
- access.find_ldap_user
+ expect { described_class.allowed?(user) }
+ .not_to change { user.last_credential_check_at }
end
end
describe '#allowed?' do
- subject { access.allowed? }
-
context 'when the user cannot be found' do
before do
- allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(nil)
- allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_email).and_return(nil)
+ stub_ldap_person_find_by_dn(nil)
+ stub_ldap_person_find_by_email(user.email, nil)
end
- it { is_expected.to be_falsey }
+ it 'returns false' do
+ expect(access.allowed?).to be_falsey
+ end
it 'blocks user in GitLab' do
- expect(access).to receive(:block_user).with(user, 'does not exist anymore')
+ access.allowed?
+
+ expect(user).to be_blocked
+ expect(user).to be_ldap_blocked
+ end
+
+ it 'logs the reason' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ "LDAP account \"123456\" does not exist anymore, " \
+ "blocking GitLab user \"#{user.name}\" (#{user.email})"
+ )
access.allowed?
end
end
context 'when the user is found' do
- let(:ldap_user) { Gitlab::Auth::LDAP::Person.new(Net::LDAP::Entry.new, 'ldapmain') }
-
before do
- allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(ldap_user)
+ stub_ldap_person_find_by_dn(Net::LDAP::Entry.new)
end
context 'and the user is disabled via active directory' do
@@ -55,10 +65,22 @@ describe Gitlab::Auth::LDAP::Access do
allow(Gitlab::Auth::LDAP::Person).to receive(:disabled_via_active_directory?).and_return(true)
end
- it { is_expected.to be_falsey }
+ it 'returns false' do
+ expect(access.allowed?).to be_falsey
+ end
it 'blocks user in GitLab' do
- expect(access).to receive(:block_user).with(user, 'is disabled in Active Directory')
+ access.allowed?
+
+ expect(user).to be_blocked
+ expect(user).to be_ldap_blocked
+ end
+
+ it 'logs the reason' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ "LDAP account \"123456\" is disabled in Active Directory, " \
+ "blocking GitLab user \"#{user.name}\" (#{user.email})"
+ )
access.allowed?
end
@@ -92,7 +114,17 @@ describe Gitlab::Auth::LDAP::Access do
end
it 'unblocks user in GitLab' do
- expect(access).to receive(:unblock_user).with(user, 'is not disabled anymore')
+ access.allowed?
+
+ expect(user).not_to be_blocked
+ expect(user).not_to be_ldap_blocked
+ end
+
+ it 'logs the reason' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ "LDAP account \"123456\" is not disabled anymore, " \
+ "unblocking GitLab user \"#{user.name}\" (#{user.email})"
+ )
access.allowed?
end
@@ -105,18 +137,32 @@ describe Gitlab::Auth::LDAP::Access do
allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive(:active_directory).and_return(false)
end
- it { is_expected.to be_truthy }
+ it 'returns true' do
+ expect(access.allowed?).to be_truthy
+ end
context 'when user cannot be found' do
before do
- allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(nil)
- allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_email).and_return(nil)
+ stub_ldap_person_find_by_dn(nil)
+ stub_ldap_person_find_by_email(user.email, nil)
end
- it { is_expected.to be_falsey }
+ it 'returns false' do
+ expect(access.allowed?).to be_falsey
+ end
it 'blocks user in GitLab' do
- expect(access).to receive(:block_user).with(user, 'does not exist anymore')
+ access.allowed?
+
+ expect(user).to be_blocked
+ expect(user).to be_ldap_blocked
+ end
+
+ it 'logs the reason' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ "LDAP account \"123456\" does not exist anymore, " \
+ "blocking GitLab user \"#{user.name}\" (#{user.email})"
+ )
access.allowed?
end
@@ -128,7 +174,17 @@ describe Gitlab::Auth::LDAP::Access do
end
it 'unblocks the user if it exists' do
- expect(access).to receive(:unblock_user).with(user, 'is available again')
+ access.allowed?
+
+ expect(user).not_to be_blocked
+ expect(user).not_to be_ldap_blocked
+ end
+
+ it 'logs the reason' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ "LDAP account \"123456\" is available again, " \
+ "unblocking GitLab user \"#{user.name}\" (#{user.email})"
+ )
access.allowed?
end
@@ -152,46 +208,4 @@ describe Gitlab::Auth::LDAP::Access do
end
end
end
-
- describe '#block_user' do
- before do
- user.activate
- allow(Gitlab::AppLogger).to receive(:info)
-
- access.block_user user, 'reason'
- end
-
- it 'blocks the user' do
- expect(user).to be_blocked
- expect(user).to be_ldap_blocked
- end
-
- it 'logs the reason' do
- expect(Gitlab::AppLogger).to have_received(:info).with(
- "LDAP account \"123456\" reason, " \
- "blocking Gitlab user \"#{user.name}\" (#{user.email})"
- )
- end
- end
-
- describe '#unblock_user' do
- before do
- user.ldap_block
- allow(Gitlab::AppLogger).to receive(:info)
-
- access.unblock_user user, 'reason'
- end
-
- it 'activates the user' do
- expect(user).not_to be_blocked
- expect(user).not_to be_ldap_blocked
- end
-
- it 'logs the reason' do
- Gitlab::AppLogger.info(
- "LDAP account \"123456\" reason, " \
- "unblocking Gitlab user \"#{user.name}\" (#{user.email})"
- )
- end
- end
end
diff --git a/spec/lib/gitlab/auth/o_auth/provider_spec.rb b/spec/lib/gitlab/auth/o_auth/provider_spec.rb
index fc35d430917..80d702cf9dc 100644
--- a/spec/lib/gitlab/auth/o_auth/provider_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/provider_spec.rb
@@ -1,6 +1,48 @@
require 'spec_helper'
describe Gitlab::Auth::OAuth::Provider do
+ describe '.enabled?' do
+ before do
+ allow(described_class).to receive(:providers).and_return([:ldapmain, :google_oauth2])
+ end
+
+ context 'when OmniAuth is disabled' do
+ before do
+ allow(Gitlab::Auth).to receive(:omniauth_enabled?).and_return(false)
+ end
+
+ it 'allows database auth' do
+ expect(described_class.enabled?('database')).to be_truthy
+ end
+
+ it 'allows LDAP auth' do
+ expect(described_class.enabled?('ldapmain')).to be_truthy
+ end
+
+ it 'does not allow other OmniAuth providers' do
+ expect(described_class.enabled?('google_oauth2')).to be_falsey
+ end
+ end
+
+ context 'when OmniAuth is enabled' do
+ before do
+ allow(Gitlab::Auth).to receive(:omniauth_enabled?).and_return(true)
+ end
+
+ it 'allows database auth' do
+ expect(described_class.enabled?('database')).to be_truthy
+ end
+
+ it 'allows LDAP auth' do
+ expect(described_class.enabled?('ldapmain')).to be_truthy
+ end
+
+ it 'allows other OmniAuth providers' do
+ expect(described_class.enabled?('google_oauth2')).to be_truthy
+ end
+ end
+ end
+
describe '#config_for' do
context 'for an LDAP provider' do
context 'when the provider exists' do
diff --git a/spec/lib/gitlab/auth/saml/auth_hash_spec.rb b/spec/lib/gitlab/auth/saml/auth_hash_spec.rb
index 76f49e778fb..3620e1afe25 100644
--- a/spec/lib/gitlab/auth/saml/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/saml/auth_hash_spec.rb
@@ -82,6 +82,17 @@ describe Gitlab::Auth::Saml::AuthHash do
end
end
+ context 'with SAML 2.0 response_object' do
+ before do
+ auth_hash_data[:extra][:response_object] = { document:
+ saml_xml(File.read('spec/fixtures/authentication/saml2_response.xml')) }
+ end
+
+ it 'can extract authn_context' do
+ expect(saml_auth_hash.authn_context).to eq 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
+ end
+ end
+
context 'without response_object' do
it 'returns an empty string' do
expect(saml_auth_hash.authn_context).to be_nil
diff --git a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
index e1c4f9cfea7..5076996474f 100644
--- a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
+++ b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
@@ -118,7 +118,7 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat
expect(fork_network_members.count).to eq(12)
end
- it 'knows when not all memberships withing a batch have been created' do
+ it 'knows when not all memberships within a batch have been created' do
expect(migration.missing_members?(8, 10)).to be_truthy
end
end
diff --git a/spec/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys_spec.rb
index 26d48cc8201..f92acf61682 100644
--- a/spec/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys_spec.rb
+++ b/spec/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::BackgroundMigration::CreateGpgKeySubkeysFromGpgKeys, :migration
let!(:gpg_key) { create(:gpg_key, key: GpgHelpers::User3.public_key) }
before do
- GpgKeySubkey.destroy_all
+ GpgKeySubkey.destroy_all # rubocop: disable DestroyAll
end
it 'generate the subkeys' do
diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
index 0735ebd6dcb..5dce3fcbcb6 100644
--- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
+++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :migration, schema: 20171114162227 do
+ include GitHelpers
+
let(:merge_request_diffs) { table(:merge_request_diffs) }
let(:merge_requests) { table(:merge_requests) }
@@ -9,11 +11,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:merge_request) { merge_requests.create!(iid: 1, target_project_id: project.id, source_project_id: project.id, target_branch: 'feature', source_branch: 'master').becomes(MergeRequest) }
let(:merge_request_diff) { MergeRequest.find(merge_request.id).create_merge_request_diff }
let(:updated_merge_request_diff) { MergeRequestDiff.find(merge_request_diff.id) }
- let(:rugged) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged
- end
- end
+ let(:rugged) { rugged_repo(project.repository) }
before do
allow_any_instance_of(MergeRequestDiff)
diff --git a/spec/lib/gitlab/background_migration/digest_column_spec.rb b/spec/lib/gitlab/background_migration/digest_column_spec.rb
new file mode 100644
index 00000000000..3e107ac3027
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/digest_column_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::DigestColumn, :migration, schema: 20180913142237 do
+ let(:personal_access_tokens) { table(:personal_access_tokens) }
+ let(:users) { table(:users) }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ context 'token is not yet hashed' do
+ before do
+ users.create(id: 1, email: 'user@example.com', projects_limit: 10)
+ personal_access_tokens.create!(id: 1, user_id: 1, name: 'pat-01', token: 'token-01')
+ end
+
+ it 'saves token digest' do
+ expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.to(
+ change { PersonalAccessToken.find(1).token_digest }.from(nil).to(Gitlab::CryptoHelper.sha256('token-01')))
+ end
+
+ it 'erases token' do
+ expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.to(
+ change { PersonalAccessToken.find(1).token }.from('token-01').to(nil))
+ end
+ end
+
+ context 'token is already hashed' do
+ before do
+ users.create(id: 1, email: 'user@example.com', projects_limit: 10)
+ personal_access_tokens.create!(id: 1, user_id: 1, name: 'pat-01', token_digest: 'token-digest-01')
+ end
+
+ it 'does not change existing token digest' do
+ expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.not_to(
+ change { PersonalAccessToken.find(1).token_digest })
+ end
+
+ it 'leaves token empty' do
+ expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.not_to(
+ change { PersonalAccessToken.find(1).token }.from(nil))
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/encrypt_columns_spec.rb b/spec/lib/gitlab/background_migration/encrypt_columns_spec.rb
new file mode 100644
index 00000000000..2a869446753
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/encrypt_columns_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::EncryptColumns, :migration, schema: 20180910115836 do
+ let(:model) { Gitlab::BackgroundMigration::Models::EncryptColumns::WebHook }
+ let(:web_hooks) { table(:web_hooks) }
+
+ let(:plaintext_attrs) do
+ {
+ 'encrypted_token' => nil,
+ 'encrypted_url' => nil,
+ 'token' => 'secret',
+ 'url' => 'http://example.com?access_token=secret'
+ }
+ end
+
+ let(:encrypted_attrs) do
+ {
+ 'encrypted_token' => be_present,
+ 'encrypted_url' => be_present,
+ 'token' => nil,
+ 'url' => nil
+ }
+ end
+
+ describe '#perform' do
+ it 'encrypts columns for the specified range' do
+ hooks = web_hooks.create([plaintext_attrs] * 5).sort_by(&:id)
+
+ # Encrypt all but the first and last rows
+ subject.perform(model, [:token, :url], hooks[1].id, hooks[3].id)
+
+ hooks = web_hooks.where(id: hooks.map(&:id)).order(:id)
+
+ aggregate_failures do
+ expect(hooks[0]).to have_attributes(plaintext_attrs)
+ expect(hooks[1]).to have_attributes(encrypted_attrs)
+ expect(hooks[2]).to have_attributes(encrypted_attrs)
+ expect(hooks[3]).to have_attributes(encrypted_attrs)
+ expect(hooks[4]).to have_attributes(plaintext_attrs)
+ end
+ end
+
+ it 'acquires an exclusive lock for the update' do
+ relation = double('relation', each: nil)
+
+ expect(model).to receive(:where) { relation }
+ expect(relation).to receive(:lock) { relation }
+
+ subject.perform(model, [:token, :url], 1, 1)
+ end
+
+ it 'skips already-encrypted columns' do
+ values = {
+ 'encrypted_token' => 'known encrypted token',
+ 'encrypted_url' => 'known encrypted url',
+ 'token' => 'token',
+ 'url' => 'url'
+ }
+
+ hook = web_hooks.create(values)
+
+ subject.perform(model, [:token, :url], hook.id, hook.id)
+
+ hook.reload
+
+ expect(hook).to have_attributes(values)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/migrate_legacy_artifacts_spec.rb b/spec/lib/gitlab/background_migration/migrate_legacy_artifacts_spec.rb
new file mode 100644
index 00000000000..2d1505dacfe
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/migrate_legacy_artifacts_spec.rb
@@ -0,0 +1,156 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::MigrateLegacyArtifacts, :migration, schema: 20180816161409 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:jobs) { table(:ci_builds) }
+ let(:job_artifacts) { table(:ci_job_artifacts) }
+
+ subject { described_class.new.perform(*range) }
+
+ context 'when a pipeline exists' do
+ let!(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let!(:project) { projects.create!(name: 'gitlab', path: 'gitlab-ce', namespace_id: namespace.id) }
+ let!(:pipeline) { pipelines.create!(project_id: project.id, ref: 'master', sha: 'adf43c3a') }
+
+ context 'when a legacy artifacts exists' do
+ let(:artifacts_expire_at) { 1.day.since.to_s }
+ let(:file_store) { ::ObjectStorage::Store::REMOTE }
+
+ let!(:job) do
+ jobs.create!(
+ commit_id: pipeline.id,
+ project_id: project.id,
+ status: :success,
+ **artifacts_archive_attributes,
+ **artifacts_metadata_attributes)
+ end
+
+ let(:artifacts_archive_attributes) do
+ {
+ artifacts_file: 'archive.zip',
+ artifacts_file_store: file_store,
+ artifacts_size: 123,
+ artifacts_expire_at: artifacts_expire_at
+ }
+ end
+
+ let(:artifacts_metadata_attributes) do
+ {
+ artifacts_metadata: 'metadata.gz',
+ artifacts_metadata_store: file_store
+ }
+ end
+
+ it 'has legacy artifacts' do
+ expect(jobs.pluck('artifacts_file, artifacts_file_store, artifacts_size, artifacts_expire_at')).to eq([artifacts_archive_attributes.values])
+ expect(jobs.pluck('artifacts_metadata, artifacts_metadata_store')).to eq([artifacts_metadata_attributes.values])
+ end
+
+ it 'does not have new artifacts yet' do
+ expect(job_artifacts.count).to be_zero
+ end
+
+ context 'when the record exists inside of the range of a background migration' do
+ let(:range) { [job.id, job.id] }
+
+ it 'migrates a legacy artifact to ci_job_artifacts table' do
+ expect { subject }.to change { job_artifacts.count }.by(2)
+
+ expect(job_artifacts.order(:id).pluck('project_id, job_id, file_type, file_store, size, expire_at, file, file_sha256, file_location'))
+ .to eq([[project.id,
+ job.id,
+ described_class::ARCHIVE_FILE_TYPE,
+ file_store,
+ artifacts_archive_attributes[:artifacts_size],
+ artifacts_expire_at,
+ 'archive.zip',
+ nil,
+ described_class::LEGACY_PATH_FILE_LOCATION],
+ [project.id,
+ job.id,
+ described_class::METADATA_FILE_TYPE,
+ file_store,
+ nil,
+ artifacts_expire_at,
+ 'metadata.gz',
+ nil,
+ described_class::LEGACY_PATH_FILE_LOCATION]])
+
+ expect(jobs.pluck('artifacts_file, artifacts_file_store, artifacts_size, artifacts_expire_at')).to eq([[nil, nil, nil, artifacts_expire_at]])
+ expect(jobs.pluck('artifacts_metadata, artifacts_metadata_store')).to eq([[nil, nil]])
+ end
+
+ context 'when file_store is nil' do
+ let(:file_store) { nil }
+
+ it 'has nullified file_store in all legacy artifacts' do
+ expect(jobs.pluck('artifacts_file_store, artifacts_metadata_store')).to eq([[nil, nil]])
+ end
+
+ it 'fills file_store by the value of local file store' do
+ subject
+
+ expect(job_artifacts.pluck('file_store')).to all(eq(::ObjectStorage::Store::LOCAL))
+ end
+ end
+
+ context 'when new artifacts has already existed' do
+ context 'when only archive.zip existed' do
+ before do
+ job_artifacts.create!(project_id: project.id, job_id: job.id, file_type: described_class::ARCHIVE_FILE_TYPE, size: 999, file: 'archive.zip')
+ end
+
+ it 'had archive.zip already' do
+ expect(job_artifacts.exists?(job_id: job.id, file_type: described_class::ARCHIVE_FILE_TYPE)).to be_truthy
+ end
+
+ it 'migrates metadata' do
+ expect { subject }.to change { job_artifacts.count }.by(1)
+
+ expect(job_artifacts.exists?(job_id: job.id, file_type: described_class::METADATA_FILE_TYPE)).to be_truthy
+ end
+ end
+
+ context 'when both archive and metadata existed' do
+ before do
+ job_artifacts.create!(project_id: project.id, job_id: job.id, file_type: described_class::ARCHIVE_FILE_TYPE, size: 999, file: 'archive.zip')
+ job_artifacts.create!(project_id: project.id, job_id: job.id, file_type: described_class::METADATA_FILE_TYPE, size: 999, file: 'metadata.zip')
+ end
+
+ it 'does not migrate' do
+ expect { subject }.not_to change { job_artifacts.count }
+ end
+ end
+ end
+ end
+
+ context 'when the record exists outside of the range of a background migration' do
+ let(:range) { [job.id + 1, job.id + 1] }
+
+ it 'does not migrate' do
+ expect { subject }.not_to change { job_artifacts.count }
+ end
+ end
+ end
+
+ context 'when the job does not have legacy artifacts' do
+ let!(:job) { jobs.create!(commit_id: pipeline.id, project_id: project.id, status: :success) }
+
+ it 'does not have the legacy artifacts in database' do
+ expect(jobs.count).to eq(1)
+ expect(jobs.pluck('artifacts_file, artifacts_file_store, artifacts_size, artifacts_expire_at')).to eq([[nil, nil, nil, nil]])
+ expect(jobs.pluck('artifacts_metadata, artifacts_metadata_store')).to eq([[nil, nil]])
+ end
+
+ context 'when the record exists inside of the range of a background migration' do
+ let(:range) { [job.id, job.id] }
+
+ it 'does not migrate' do
+ expect { subject }.not_to change { job_artifacts.count }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/populate_cluster_kubernetes_namespace_table_spec.rb b/spec/lib/gitlab/background_migration/populate_cluster_kubernetes_namespace_table_spec.rb
new file mode 100644
index 00000000000..4f1b01eed41
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_cluster_kubernetes_namespace_table_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::PopulateClusterKubernetesNamespaceTable, :migration, schema: 20181022173835 do
+ let(:migration) { described_class.new }
+ let(:clusters) { create_list(:cluster, 10, :project, :provided_by_gcp) }
+
+ before do
+ clusters
+ end
+
+ shared_examples 'consistent kubernetes namespace attributes' do
+ it 'should populate namespace and service account information' do
+ subject
+
+ clusters_with_namespace.each do |cluster|
+ project = cluster.project
+ cluster_project = cluster.cluster_projects.first
+ namespace = "#{project.path}-#{project.id}"
+ kubernetes_namespace = cluster.reload.kubernetes_namespace
+
+ expect(kubernetes_namespace).to be_present
+ expect(kubernetes_namespace.cluster_project).to eq(cluster_project)
+ expect(kubernetes_namespace.project).to eq(cluster_project.project)
+ expect(kubernetes_namespace.cluster).to eq(cluster_project.cluster)
+ expect(kubernetes_namespace.namespace).to eq(namespace)
+ expect(kubernetes_namespace.service_account_name).to eq("#{namespace}-service-account")
+ end
+ end
+ end
+
+ subject { migration.perform }
+
+ context 'when no Clusters::Project has a Clusters::KubernetesNamespace' do
+ let(:cluster_projects) { Clusters::Project.all }
+
+ it 'should create a Clusters::KubernetesNamespace per Clusters::Project' do
+ expect do
+ subject
+ end.to change(Clusters::KubernetesNamespace, :count).by(cluster_projects.count)
+ end
+
+ it_behaves_like 'consistent kubernetes namespace attributes' do
+ let(:clusters_with_namespace) { clusters }
+ end
+ end
+
+ context 'when every Clusters::Project has Clusters::KubernetesNamespace' do
+ before do
+ clusters.each do |cluster|
+ create(:cluster_kubernetes_namespace,
+ cluster_project: cluster.cluster_projects.first,
+ cluster: cluster,
+ project: cluster.project)
+ end
+ end
+
+ it 'should not create any Clusters::KubernetesNamespace' do
+ expect do
+ subject
+ end.not_to change(Clusters::KubernetesNamespace, :count)
+ end
+ end
+
+ context 'when only some Clusters::Project have Clusters::KubernetesNamespace related' do
+ let(:with_kubernetes_namespace) { clusters.first(6) }
+ let(:with_no_kubernetes_namespace) { clusters.last(4) }
+
+ before do
+ with_kubernetes_namespace.each do |cluster|
+ create(:cluster_kubernetes_namespace,
+ cluster_project: cluster.cluster_projects.first,
+ cluster: cluster,
+ project: cluster.project)
+ end
+ end
+
+ it 'creates limited number of Clusters::KubernetesNamespace' do
+ expect do
+ subject
+ end.to change(Clusters::KubernetesNamespace, :count).by(with_no_kubernetes_namespace.count)
+ end
+
+ it 'should not modify clusters with Clusters::KubernetesNamespace' do
+ subject
+
+ with_kubernetes_namespace.each do |cluster|
+ expect(cluster.kubernetes_namespaces.count).to eq(1)
+ end
+ end
+
+ it_behaves_like 'consistent kubernetes namespace attributes' do
+ let(:clusters_with_namespace) { with_no_kubernetes_namespace }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb b/spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb
new file mode 100644
index 00000000000..c7b272cd6ca
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::PopulateExternalPipelineSource, :migration, schema: 20180916011959 do
+ let(:migration) { described_class.new }
+
+ let!(:internal_pipeline) { create(:ci_pipeline, source: :web) }
+ let(:pipelines) { [internal_pipeline, unknown_pipeline].map(&:id) }
+
+ let!(:unknown_pipeline) do
+ build(:ci_pipeline, source: :unknown)
+ .tap { |pipeline| pipeline.save(validate: false) }
+ end
+
+ subject { migration.perform(pipelines.min, pipelines.max) }
+
+ shared_examples 'no changes' do
+ it 'does not change the pipeline source' do
+ expect { subject }.not_to change { unknown_pipeline.reload.source }
+ end
+ end
+
+ context 'when unknown pipeline is external' do
+ before do
+ create(:generic_commit_status, pipeline: unknown_pipeline)
+ end
+
+ it 'populates the pipeline source' do
+ subject
+
+ expect(unknown_pipeline.reload.source).to eq('external')
+ end
+
+ it 'can be repeated without effect' do
+ subject
+
+ expect { subject }.not_to change { unknown_pipeline.reload.source }
+ end
+ end
+
+ context 'when unknown pipeline has just a build' do
+ before do
+ create(:ci_build, pipeline: unknown_pipeline)
+ end
+
+ it_behaves_like 'no changes'
+ end
+
+ context 'when unknown pipeline has no statuses' do
+ it_behaves_like 'no changes'
+ end
+
+ context 'when unknown pipeline has a build and a status' do
+ before do
+ create(:generic_commit_status, pipeline: unknown_pipeline)
+ create(:ci_build, pipeline: unknown_pipeline)
+ end
+
+ it_behaves_like 'no changes'
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/redact_links_spec.rb b/spec/lib/gitlab/background_migration/redact_links_spec.rb
new file mode 100644
index 00000000000..a40e68069cc
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/redact_links_spec.rb
@@ -0,0 +1,96 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::RedactLinks, :migration, schema: 20181014121030 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:issues) { table(:issues) }
+ let(:notes) { table(:notes) }
+ let(:snippets) { table(:snippets) }
+ let(:users) { table(:users) }
+ let(:merge_requests) { table(:merge_requests) }
+ let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
+ let(:user) { users.create!(email: 'test@example.com', projects_limit: 100, username: 'test') }
+
+ def create_merge_request(id, params)
+ params.merge!(id: id,
+ target_project_id: project.id,
+ target_branch: 'master',
+ source_project_id: project.id,
+ source_branch: 'mr name',
+ title: "mr name#{id}")
+
+ merge_requests.create(params)
+ end
+
+ def create_issue(id, params)
+ params.merge!(id: id, title: "issue#{id}", project_id: project.id)
+
+ issues.create(params)
+ end
+
+ def create_note(id, params)
+ params[:id] = id
+
+ notes.create(params)
+ end
+
+ def create_snippet(id, params)
+ params.merge!(id: id, author_id: user.id)
+
+ snippets.create(params)
+ end
+
+ def create_resource(model, id, params)
+ send("create_#{model.name.underscore}", id, params)
+ end
+
+ shared_examples_for 'redactable resource' do
+ it 'updates only matching texts' do
+ matching_text = 'some text /sent_notifications/00000000000000000000000000000000/unsubscribe more text'
+ redacted_text = 'some text /sent_notifications/REDACTED/unsubscribe more text'
+ create_resource(model, 1, { field => matching_text })
+ create_resource(model, 2, { field => 'not matching text' })
+ create_resource(model, 3, { field => matching_text })
+ create_resource(model, 4, { field => redacted_text })
+ create_resource(model, 5, { field => matching_text })
+
+ expected = { field => 'some text /sent_notifications/REDACTED/unsubscribe more text',
+ "#{field}_html" => nil }
+ expect_any_instance_of("Gitlab::BackgroundMigration::RedactLinks::#{model}".constantize).to receive(:update_columns).with(expected).and_call_original
+
+ subject.perform(model, field, 2, 4)
+
+ expect(model.where(field => matching_text).pluck(:id)).to eq [1, 5]
+ expect(model.find(3).reload[field]).to eq redacted_text
+ end
+ end
+
+ context 'resource is Issue' do
+ it_behaves_like 'redactable resource' do
+ let(:model) { Issue }
+ let(:field) { :description }
+ end
+ end
+
+ context 'resource is Merge Request' do
+ it_behaves_like 'redactable resource' do
+ let(:model) { MergeRequest }
+ let(:field) { :description }
+ end
+ end
+
+ context 'resource is Note' do
+ it_behaves_like 'redactable resource' do
+ let(:model) { Note }
+ let(:field) { :note }
+ end
+ end
+
+ context 'resource is Snippet' do
+ it_behaves_like 'redactable resource' do
+ let(:model) { Snippet }
+ let(:field) { :description }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index 6e21c846c0a..3c63e601abc 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -10,9 +10,6 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
subject(:importer) { described_class.new(admin, bare_repository) }
before do
- @rainbow = Rainbow.enabled
- Rainbow.enabled = false
-
allow(described_class).to receive(:log)
end
@@ -20,7 +17,6 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
FileUtils.rm_rf(base_dir)
TestEnv.clean_test_path
ensure_seeds
- Rainbow.enabled = @rainbow
end
shared_examples 'importing a repository' do
diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
index ed6fa3d229f..e2bee22cf1f 100644
--- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::BitbucketImport::ProjectCreator do
owner: "asd",
full_name: 'Vim repo',
visibility_level: Gitlab::VisibilityLevel::PRIVATE,
- clone_url: 'ssh://git@bitbucket.org/asd/vim.git',
+ clone_url: 'http://bitbucket.org/asd/vim.git',
has_wiki?: false)
end
@@ -32,7 +32,7 @@ describe Gitlab::BitbucketImport::ProjectCreator do
project_creator = described_class.new(repo, 'vim', namespace, user, access_params)
project = project_creator.execute
- expect(project.import_url).to eq("ssh://git@bitbucket.org/asd/vim.git")
+ expect(project.import_url).to eq("http://bitbucket.org/asd/vim.git")
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
diff --git a/spec/lib/gitlab/blob_helper_spec.rb b/spec/lib/gitlab/blob_helper_spec.rb
new file mode 100644
index 00000000000..0b56f8687c3
--- /dev/null
+++ b/spec/lib/gitlab/blob_helper_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::BlobHelper do
+ include FakeBlobHelpers
+
+ let(:project) { create(:project) }
+ let(:blob) { fake_blob(path: 'file.txt') }
+ let(:large_blob) { fake_blob(path: 'test.pdf', size: 2.megabytes, binary: true) }
+
+ describe '#extname' do
+ it 'returns the extension' do
+ expect(blob.extname).to eq('.txt')
+ end
+ end
+
+ describe '#known_extension?' do
+ it 'returns true' do
+ expect(blob.known_extension?).to be_truthy
+ end
+ end
+
+ describe '#viewable' do
+ it 'returns true' do
+ expect(blob.viewable?).to be_truthy
+ end
+
+ it 'returns false' do
+ expect(large_blob.viewable?).to be_falsey
+ end
+ end
+
+ describe '#large?' do
+ it 'returns false' do
+ expect(blob.large?).to be_falsey
+ end
+
+ it 'returns true' do
+ expect(large_blob.large?).to be_truthy
+ end
+ end
+
+ describe '#binary?' do
+ it 'returns true' do
+ expect(large_blob.binary?).to be_truthy
+ end
+
+ it 'returns false' do
+ expect(blob.binary?).to be_falsey
+ end
+ end
+
+ describe '#text?' do
+ it 'returns true' do
+ expect(blob.text?).to be_truthy
+ end
+
+ it 'returns false' do
+ expect(large_blob.text?).to be_falsey
+ end
+ end
+
+ describe '#image?' do
+ it 'returns false' do
+ expect(blob.image?).to be_falsey
+ end
+ end
+
+ describe '#mime_type' do
+ it 'returns text/plain' do
+ expect(blob.mime_type).to eq('text/plain')
+ end
+
+ it 'returns application/pdf' do
+ expect(large_blob.mime_type).to eq('application/pdf')
+ end
+ end
+
+ describe '#binary_mime_type?' do
+ it 'returns false' do
+ expect(blob.binary_mime_type?).to be_falsey
+ end
+ end
+
+ describe '#lines' do
+ it 'returns the payload in an Array' do
+ expect(blob.lines).to eq(['foo'])
+ end
+ end
+
+ describe '#content_type' do
+ it 'returns text/plain' do
+ expect(blob.content_type).to eq('text/plain; charset=utf-8')
+ end
+
+ it 'returns text/plain' do
+ expect(large_blob.content_type).to eq('application/pdf')
+ end
+ end
+
+ describe '#encoded_newlines_re' do
+ it 'returns a regular expression' do
+ expect(blob.encoded_newlines_re).to eq(/\r\n|\r|\n/)
+ end
+ end
+
+ describe '#ruby_encoding' do
+ it 'returns UTF-8' do
+ expect(blob.ruby_encoding).to eq('UTF-8')
+ end
+ end
+
+ describe '#encoding' do
+ it 'returns UTF-8' do
+ expect(blob.ruby_encoding).to eq('UTF-8')
+ end
+ end
+
+ describe '#empty?' do
+ it 'returns false' do
+ expect(blob.empty?).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
index 18658588a40..e5999a1c509 100644
--- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
@@ -49,8 +49,6 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
end
it 'only connects to redis twice' do
- # Stub circuitbreaker so it doesn't count the redis connections in there
- stub_circuit_breaker(project_without_status)
expect(Gitlab::Redis::Cache).to receive(:with).exactly(2).and_call_original
described_class.load_in_batch_for_projects([project_without_status])
@@ -284,6 +282,21 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
expect(pipeline_status.status).to eq(status)
expect(pipeline_status.ref).to eq(ref)
end
+
+ context 'when status is empty string' do
+ before do
+ Gitlab::Redis::Cache.with do |redis|
+ redis.mapped_hmset(cache_key,
+ { sha: sha, status: '', ref: ref })
+ end
+ end
+
+ it 'reads the status as nil' do
+ pipeline_status.load_from_cache
+
+ expect(pipeline_status.status).to eq(nil)
+ end
+ end
end
describe '#has_cache?' do
@@ -302,13 +315,4 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
end
end
end
-
- def stub_circuit_breaker(project)
- fake_circuitbreaker = double
- allow(fake_circuitbreaker).to receive(:perform).and_yield
- allow(project.repository.raw_repository)
- .to receive(:circuit_breaker).and_return(fake_circuitbreaker)
- allow(project.repository)
- .to receive(:circuit_breaker).and_return(fake_circuitbreaker)
- end
end
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index 4df426c54ae..81804ba5c76 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -10,13 +10,16 @@ describe Gitlab::Checks::ChangeAccess do
let(:ref) { 'refs/heads/master' }
let(:changes) { { oldrev: oldrev, newrev: newrev, ref: ref } }
let(:protocol) { 'ssh' }
+ let(:timeout) { Gitlab::GitAccess::INTERNAL_TIMEOUT }
+ let(:logger) { Gitlab::Checks::TimedLogger.new(timeout: timeout) }
subject(:change_access) do
described_class.new(
changes,
project: project,
user_access: user_access,
- protocol: protocol
+ protocol: protocol,
+ logger: logger
)
end
@@ -30,6 +33,19 @@ describe Gitlab::Checks::ChangeAccess do
end
end
+ context 'when time limit was reached' do
+ it 'raises a TimeoutError' do
+ logger = Gitlab::Checks::TimedLogger.new(start_time: timeout.ago, timeout: timeout)
+ access = described_class.new(changes,
+ project: project,
+ user_access: user_access,
+ protocol: protocol,
+ logger: logger)
+
+ expect { access.exec }.to raise_error(Gitlab::Checks::TimedLogger::TimeoutError)
+ end
+ end
+
context 'when the user is not allowed to push to the repo' do
it 'raises an error' do
expect(user_access).to receive(:can_do_action?).with(:push_code).and_return(false)
diff --git a/spec/lib/gitlab/checks/lfs_integrity_spec.rb b/spec/lib/gitlab/checks/lfs_integrity_spec.rb
index ec22e3a198e..887ea8fc1e0 100644
--- a/spec/lib/gitlab/checks/lfs_integrity_spec.rb
+++ b/spec/lib/gitlab/checks/lfs_integrity_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe Gitlab::Checks::LfsIntegrity do
include ProjectForksHelper
+ let!(:time_left) { 50 }
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:newrev) do
@@ -15,7 +16,7 @@ describe Gitlab::Checks::LfsIntegrity do
operations.commit_tree('8856a329dd38ca86dfb9ce5aa58a16d88cc119bd', "New LFS objects")
end
- subject { described_class.new(project, newrev) }
+ subject { described_class.new(project, newrev, time_left) }
describe '#objects_missing?' do
let(:blob_object) { repository.blob_at_branch('lfs', 'files/lfs/lfs_object.iso') }
@@ -67,7 +68,7 @@ describe Gitlab::Checks::LfsIntegrity do
expect(subject.objects_missing?).to be_truthy
end
- it 'is false parent project already conatins LFS objects for the fork' do
+ it 'is false parent project already contains LFS objects for the fork' do
lfs_object = create(:lfs_object, oid: blob_object.lfs_oid)
create(:lfs_objects_project, project: parent_project, lfs_object: lfs_object)
diff --git a/spec/lib/gitlab/checks/timed_logger_spec.rb b/spec/lib/gitlab/checks/timed_logger_spec.rb
new file mode 100644
index 00000000000..0ed3940c038
--- /dev/null
+++ b/spec/lib/gitlab/checks/timed_logger_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Checks::TimedLogger do
+ let!(:timeout) { 50.seconds }
+ let!(:start) { Time.now }
+ let!(:ref) { "bar" }
+ let!(:logger) { described_class.new(start_time: start, timeout: timeout) }
+ let!(:log_messages) do
+ {
+ foo: "Foo message..."
+ }
+ end
+
+ before do
+ logger.append_message("Checking ref: #{ref}")
+ end
+
+ describe '#log_timed' do
+ it 'logs message' do
+ Timecop.freeze(start + 30.seconds) do
+ logger.log_timed(log_messages[:foo], start) { bar_check }
+ end
+
+ expect(logger.full_message).to eq("Checking ref: bar\nFoo message... (30000.0ms)")
+ end
+
+ context 'when time limit was reached' do
+ it 'cancels action' do
+ Timecop.freeze(start + 50.seconds) do
+ expect do
+ logger.log_timed(log_messages[:foo], start) do
+ bar_check
+ end
+ end.to raise_error(described_class::TimeoutError)
+ end
+
+ expect(logger.full_message).to eq("Checking ref: bar\nFoo message... (cancelled)")
+ end
+
+ it 'cancels action with time elapsed if work was performed' do
+ Timecop.freeze(start + 30.seconds) do
+ expect do
+ logger.log_timed(log_messages[:foo], start) do
+ grpc_check
+ end
+ end.to raise_error(described_class::TimeoutError)
+
+ expect(logger.full_message).to eq("Checking ref: bar\nFoo message... (cancelled after 30000.0ms)")
+ end
+ end
+ end
+ end
+
+ def bar_check
+ 2 + 2
+ end
+
+ def grpc_check
+ raise GRPC::DeadlineExceeded
+ end
+end
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb
index 7549e9941b6..5a5c071c639 100644
--- a/spec/lib/gitlab/ci/ansi2html_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2html_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::Ci::Ansi2html do
expect(convert_html("Hello")).to eq('Hello')
end
- it "strips non-color-changing controll sequences" do
+ it "strips non-color-changing control sequences" do
expect(convert_html("Hello \e[2Kworld")).to eq('Hello world')
end
diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb
new file mode 100644
index 00000000000..987c6b37aaa
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Artifacts::Adapters::GzipStream do
+ describe '#initialize' do
+ context 'when stream is passed' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
+
+ it 'initialized' do
+ expect { described_class.new(stream) }.not_to raise_error
+ end
+ end
+
+ context 'when stream is not passed' do
+ let(:stream) { nil }
+
+ it 'raises an error' do
+ expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
+ end
+ end
+ end
+
+ describe '#each_blob' do
+ let(:adapter) { described_class.new(stream) }
+
+ context 'when stream is gzip file' do
+ context 'when gzip file contains one file' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
+
+ it 'iterates content and file_name' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_with_args(fixture_file('junit/junit.xml'), 'rspec.xml')
+ end
+ end
+
+ context 'when gzip file contains three files' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit_with_three_testsuites.xml.gz'), 'rb') }
+
+ it 'iterates content and file_name' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_successive_args(
+ [fixture_file('junit/junit_with_three_testsuites_1.xml'), 'rspec-3.xml'],
+ [fixture_file('junit/junit_with_three_testsuites_2.xml'), 'rspec-1.xml'],
+ [fixture_file('junit/junit_with_three_testsuites_3.xml'), 'rspec-2.xml'])
+ end
+ end
+ end
+
+ context 'when stream is zip file' do
+ let(:stream) { File.open(expand_fixture_path('ci_build_artifacts.zip'), 'rb') }
+
+ it 'raises an error' do
+ expect { |b| adapter.each_blob(&b) }.to raise_error(described_class::InvalidStreamError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb
new file mode 100644
index 00000000000..ec2dd724b45
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Artifacts::Adapters::RawStream do
+ describe '#initialize' do
+ context 'when stream is passed' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml'), 'rb') }
+
+ it 'initialized' do
+ expect { described_class.new(stream) }.not_to raise_error
+ end
+ end
+
+ context 'when stream is not passed' do
+ let(:stream) { nil }
+
+ it 'raises an error' do
+ expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
+ end
+ end
+ end
+
+ describe '#each_blob' do
+ let(:adapter) { described_class.new(stream) }
+
+ context 'when file is not empty' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml'), 'rb') }
+
+ it 'iterates content' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_with_args(fixture_file('junit/junit.xml'), 'raw')
+ end
+ end
+
+ context 'when file is empty' do
+ let(:stream) { Tempfile.new }
+
+ after do
+ stream.unlink
+ end
+
+ it 'does not iterate content' do
+ expect { |b| adapter.each_blob(&b) }
+ .not_to yield_control
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb b/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb
deleted file mode 100644
index 384329dda18..00000000000
--- a/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Ci::Build::Artifacts::GzipFileAdapter do
- describe '#initialize' do
- context 'when stream is passed' do
- let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
-
- it 'initialized' do
- expect { described_class.new(stream) }.not_to raise_error
- end
- end
-
- context 'when stream is not passed' do
- let(:stream) { nil }
-
- it 'raises an error' do
- expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
- end
- end
- end
-
- describe '#each_blob' do
- let(:adapter) { described_class.new(stream) }
-
- context 'when stream is gzip file' do
- context 'when gzip file contains one file' do
- let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
-
- it 'iterates content and file_name' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_with_args(fixture_file('junit/junit.xml'), 'rspec.xml')
- end
- end
-
- context 'when gzip file contains three files' do
- let(:stream) { File.open(expand_fixture_path('junit/junit_with_three_testsuites.xml.gz'), 'rb') }
-
- it 'iterates content and file_name' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_successive_args(
- [fixture_file('junit/junit_with_three_testsuites_1.xml'), 'rspec-3.xml'],
- [fixture_file('junit/junit_with_three_testsuites_2.xml'), 'rspec-1.xml'],
- [fixture_file('junit/junit_with_three_testsuites_3.xml'), 'rspec-2.xml'])
- end
- end
- end
-
- context 'when stream is zip file' do
- let(:stream) { File.open(expand_fixture_path('ci_build_artifacts.zip'), 'rb') }
-
- it 'raises an error' do
- expect { |b| adapter.each_blob(&b) }.to raise_error(described_class::InvalidStreamError)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index e327399d82d..a9a4af1f455 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -112,4 +112,34 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
end
end
end
+
+ context 'generated metadata' do
+ let(:tmpfile) { Tempfile.new('test-metadata') }
+ let(:generator) { CiArtifactMetadataGenerator.new(tmpfile) }
+ let(:entry_count) { 5 }
+
+ before do
+ tmpfile.binmode
+
+ (1..entry_count).each do |index|
+ generator.add_entry("public/test-#{index}.txt")
+ end
+
+ generator.write
+ end
+
+ after do
+ File.unlink(tmpfile.path)
+ end
+
+ describe '#find_entries!' do
+ it 'reads expected number of entries' do
+ stream = File.open(tmpfile.path)
+
+ metadata = described_class.new(stream, 'public', { recursive: true })
+
+ expect(metadata.find_entries!.count).to eq entry_count
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/build/policy/changes_spec.rb b/spec/lib/gitlab/ci/build/policy/changes_spec.rb
new file mode 100644
index 00000000000..523d00c1272
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/policy/changes_spec.rb
@@ -0,0 +1,119 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Policy::Changes do
+ set(:project) { create(:project) }
+
+ describe '#satisfied_by?' do
+ describe 'paths matching matching' do
+ let(:pipeline) do
+ build(:ci_empty_pipeline, project: project,
+ ref: 'master',
+ source: :push,
+ sha: '1234abcd',
+ before_sha: '0123aabb')
+ end
+
+ let(:ci_build) do
+ build(:ci_build, pipeline: pipeline, project: project, ref: 'master')
+ end
+
+ let(:seed) { double('build seed', to_resource: ci_build) }
+
+ before do
+ allow(pipeline).to receive(:modified_paths) do
+ %w[some/modified/ruby/file.rb some/other_file.txt some/.dir/file]
+ end
+ end
+
+ it 'is satisfied by matching literal path' do
+ policy = described_class.new(%w[some/other_file.txt])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is satisfied by matching simple pattern' do
+ policy = described_class.new(%w[some/*.txt])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is satisfied by matching recusive pattern' do
+ policy = described_class.new(%w[some/**/*.rb])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is satisfied by matching a pattern with a dot' do
+ policy = described_class.new(%w[some/*/file])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is satisfied by matching a pattern with a glob' do
+ policy = described_class.new(%w[some/**/*.{rb,txt}])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is not satisfied when pattern does not match path' do
+ policy = described_class.new(%w[some/*.rb])
+
+ expect(policy).not_to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is not satisfied when pattern does not match' do
+ policy = described_class.new(%w[invalid/*.md])
+
+ expect(policy).not_to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is not satified when pattern with glob does not match' do
+ policy = described_class.new(%w[invalid/*.{md,rake}])
+
+ expect(policy).not_to be_satisfied_by(pipeline, seed)
+ end
+
+ context 'when pipelines does not run for a branch update' do
+ before do
+ pipeline.before_sha = Gitlab::Git::BLANK_SHA
+ end
+
+ it 'is always satisfied' do
+ policy = described_class.new(%w[invalid/*])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+ end
+ end
+
+ describe 'gitaly integration' do
+ set(:project) { create(:project, :repository) }
+
+ let(:pipeline) do
+ create(:ci_empty_pipeline, project: project,
+ ref: 'master',
+ source: :push,
+ sha: '498214d',
+ before_sha: '281d3a7')
+ end
+
+ let(:build) do
+ create(:ci_build, pipeline: pipeline, project: project, ref: 'master')
+ end
+
+ let(:seed) { double('build seed', to_resource: build) }
+
+ it 'is satisfied by changes introduced by a push' do
+ policy = described_class.new(['with space/*.md'])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is not satisfied by changes that are not in the push' do
+ policy = described_class.new(%w[files/js/commit.js])
+
+ expect(policy).not_to be_satisfied_by(pipeline, seed)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/policy/refs_spec.rb b/spec/lib/gitlab/ci/build/policy/refs_spec.rb
index 7211187e511..553fc0fb9bf 100644
--- a/spec/lib/gitlab/ci/build/policy/refs_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/refs_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::Ci::Build::Policy::Refs do
end
end
- context 'when maching tags' do
+ context 'when matching tags' do
context 'when pipeline runs for a tag' do
let(:pipeline) do
build_stubbed(:ci_pipeline, ref: 'feature', tag: true)
@@ -56,10 +56,10 @@ describe Gitlab::Ci::Build::Policy::Refs do
end
end
- context 'when maching a source' do
+ context 'when matching a source' do
let(:pipeline) { build_stubbed(:ci_pipeline, source: :push) }
- it 'is satisifed when provided source keyword matches' do
+ it 'is satisfied when provided source keyword matches' do
expect(described_class.new(%w[pushes]))
.to be_satisfied_by(pipeline)
end
diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
index 2ce858836e3..c2c0742efc3 100644
--- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
@@ -24,7 +24,7 @@ describe Gitlab::Ci::Build::Policy::Variables do
expect(policy).to be_satisfied_by(pipeline, seed)
end
- it 'is not satisfied by an overriden empty variable' do
+ it 'is not satisfied by an overridden empty variable' do
policy = described_class.new(['$CI_PROJECT_NAME'])
expect(policy).not_to be_satisfied_by(pipeline, seed)
@@ -54,7 +54,7 @@ describe Gitlab::Ci::Build::Policy::Variables do
expect(policy).not_to be_satisfied_by(pipeline, seed)
end
- it 'allows to evaluate regular secret variables' do
+ it 'allows to evaluate regular CI variables' do
create(:ci_variable, project: project, key: 'SECRET', value: 'my secret')
policy = described_class.new(["$SECRET == 'my secret'"])
diff --git a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
index d48aac15f28..bd1f2c92844 100644
--- a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Ci::Config::Entry::Artifacts do
let(:config) { { paths: %w[public/] } }
describe '#value' do
- it 'returns artifacs configuration' do
+ it 'returns artifacts configuration' do
expect(entry.value).to eq config
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb
index 1860ed79bfd..7c18514934e 100644
--- a/spec/lib/gitlab/ci/config/entry/global_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb
@@ -219,7 +219,7 @@ describe Gitlab::Ci::Config::Entry::Global do
##
# When nodes are specified but not defined, we assume that
- # configuration is valid, and we asume that entry is simply undefined,
+ # configuration is valid, and we assume that entry is simply undefined,
# despite the fact, that key is present. See issue #18775 for more
# details.
#
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 6769f64f950..57d4577a90c 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::Ci::Config::Entry::Job do
let(:result) do
%i[before_script script stage type after_script cache
image services only except variables artifacts
- environment coverage]
+ environment coverage retry]
end
it { is_expected.to match_array result }
@@ -38,6 +38,14 @@ describe Gitlab::Ci::Config::Entry::Job do
expect(entry.errors).to include "job name can't be blank"
end
end
+
+ context 'when delayed job' do
+ context 'when start_in is specified' do
+ let(:config) { { script: 'echo', when: 'delayed', start_in: '1 day' } }
+
+ it { expect(entry).to be_valid }
+ end
+ end
end
context 'when entry value is not correct' do
@@ -81,49 +89,107 @@ describe Gitlab::Ci::Config::Entry::Job do
end
end
- context 'when retry value is not correct' do
+ context 'when extends key is not a string' do
+ let(:config) { { extends: 123 } }
+
+ it 'returns error about wrong value type' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include "job extends should be a string"
+ end
+ end
+
+ context 'when parallel value is not correct' do
context 'when it is not a numeric value' do
- let(:config) { { retry: true } }
+ let(:config) { { parallel: true } }
it 'returns error about invalid type' do
expect(entry).not_to be_valid
- expect(entry.errors).to include 'job retry is not a number'
+ expect(entry.errors).to include 'job parallel is not a number'
end
end
- context 'when it is lower than zero' do
- let(:config) { { retry: -1 } }
+ context 'when it is lower than two' do
+ let(:config) { { parallel: 1 } }
it 'returns error about value too low' do
expect(entry).not_to be_valid
expect(entry.errors)
- .to include 'job retry must be greater than or equal to 0'
+ .to include 'job parallel must be greater than or equal to 2'
+ end
+ end
+
+ context 'when it is bigger than 50' do
+ let(:config) { { parallel: 51 } }
+
+ it 'returns error about value too high' do
+ expect(entry).not_to be_valid
+ expect(entry.errors)
+ .to include 'job parallel must be less than or equal to 50'
end
end
context 'when it is not an integer' do
- let(:config) { { retry: 1.5 } }
+ let(:config) { { parallel: 1.5 } }
it 'returns error about wrong value' do
expect(entry).not_to be_valid
- expect(entry.errors).to include 'job retry must be an integer'
+ expect(entry.errors).to include 'job parallel must be an integer'
end
end
+ end
- context 'when the value is too high' do
- let(:config) { { retry: 10 } }
+ context 'when delayed job' do
+ context 'when start_in is specified' do
+ let(:config) { { script: 'echo', when: 'delayed', start_in: '1 day' } }
- it 'returns error about value too high' do
+ it 'returns error about invalid type' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'when start_in is empty' do
+ let(:config) { { when: 'delayed', start_in: nil } }
+
+ it 'returns error about invalid type' do
expect(entry).not_to be_valid
- expect(entry.errors).to include 'job retry must be less than or equal to 2'
+ expect(entry.errors).to include 'job start in should be a duration'
+ end
+ end
+
+ context 'when start_in is not formatted as a duration' do
+ let(:config) { { when: 'delayed', start_in: 'test' } }
+
+ it 'returns error about invalid type' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'job start in should be a duration'
+ end
+ end
+
+ context 'when start_in is longer than one day' do
+ let(:config) { { when: 'delayed', start_in: '2 days' } }
+
+ it 'returns error about exceeding the limit' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'job start in should not exceed the limit'
end
end
end
+
+ context 'when start_in specified without delayed specification' do
+ let(:config) { { start_in: '1 day' } }
+
+ it 'returns error about invalid type' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'job start in must be blank'
+ end
+ end
end
end
describe '#relevant?' do
it 'is a relevant entry' do
+ entry = described_class.new({ script: 'rspec' }, name: :rspec)
+
expect(entry).to be_relevant
end
end
@@ -226,6 +292,24 @@ describe Gitlab::Ci::Config::Entry::Job do
end
end
+ describe '#delayed?' do
+ context 'when job is a delayed' do
+ let(:config) { { script: 'deploy', when: 'delayed' } }
+
+ it 'is a delayed' do
+ expect(entry).to be_delayed
+ end
+ end
+
+ context 'when job is not a delayed' do
+ let(:config) { { script: 'deploy' } }
+
+ it 'is not a delayed' do
+ expect(entry).not_to be_delayed
+ end
+ end
+ end
+
describe '#ignored?' do
context 'when job is a manual action' do
context 'when it is not specified if job is allowed to fail' do
diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
index 83d39b82068..83001b7fdd8 100644
--- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
@@ -1,4 +1,5 @@
-require 'spec_helper'
+require 'fast_spec_helper'
+require_dependency 'active_model'
describe Gitlab::Ci::Config::Entry::Policy do
let(:entry) { described_class.new(config) }
@@ -57,7 +58,7 @@ describe Gitlab::Ci::Config::Entry::Policy do
end
context 'when using complex policy' do
- context 'when specifiying refs policy' do
+ context 'when specifying refs policy' do
let(:config) { { refs: ['master'] } }
it 'is a correct configuraton' do
@@ -124,6 +125,23 @@ describe Gitlab::Ci::Config::Entry::Policy do
end
end
+ context 'when specifying a valid changes policy' do
+ let(:config) { { changes: %w[some/* paths/**/*.rb] } }
+
+ it 'is a correct configuraton' do
+ expect(entry).to be_valid
+ expect(entry.value).to eq(config)
+ end
+ end
+
+ context 'when changes policy is invalid' do
+ let(:config) { { changes: [1, 2] } }
+
+ it 'returns errors' do
+ expect(entry.errors).to include /changes should be an array of strings/
+ end
+ end
+
context 'when specifying unknown policy' do
let(:config) { { refs: ['master'], invalid: :something } }
diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
index b3a3a6bee1d..38943138cbf 100644
--- a/spec/lib/gitlab/ci/config/entry/reports_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
@@ -3,27 +3,56 @@ require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Reports do
let(:entry) { described_class.new(config) }
+ describe 'validates ALLOWED_KEYS' do
+ let(:artifact_file_types) { Ci::JobArtifact.file_types }
+
+ described_class::ALLOWED_KEYS.each do |keyword, _|
+ it "expects #{keyword} to be an artifact file_type" do
+ expect(artifact_file_types).to include(keyword)
+ end
+ end
+ end
+
describe 'validation' do
context 'when entry config value is correct' do
- let(:config) { { junit: %w[junit.xml] } }
+ using RSpec::Parameterized::TableSyntax
- describe '#value' do
- it 'returns artifacs configuration' do
- expect(entry.value).to eq config
+ shared_examples 'a valid entry' do |keyword, file|
+ describe '#value' do
+ it 'returns artifacts configuration' do
+ expect(entry.value).to eq({ "#{keyword}": [file] } )
+ end
end
- end
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
end
end
- context 'when value is not array' do
- let(:config) { { junit: 'junit.xml' } }
+ where(:keyword, :file) do
+ :junit | 'junit.xml'
+ :codequality | 'gl-code-quality-report.json'
+ :sast | 'gl-sast-report.json'
+ :dependency_scanning | 'gl-dependency-scanning-report.json'
+ :container_scanning | 'gl-container-scanning-report.json'
+ :dast | 'gl-dast-report.json'
+ :license_management | 'gl-license-management-report.json'
+ :performance | 'performance.json'
+ end
+
+ with_them do
+ context 'when value is an array' do
+ let(:config) { { "#{keyword}": [file] } }
- it 'converts to array' do
- expect(entry.value).to eq({ junit: ['junit.xml'] } )
+ it_behaves_like 'a valid entry', params[:keyword], params[:file]
+ end
+
+ context 'when value is not array' do
+ let(:config) { { "#{keyword}": file } }
+
+ it_behaves_like 'a valid entry', params[:keyword], params[:file]
end
end
end
@@ -31,11 +60,13 @@ describe Gitlab::Ci::Config::Entry::Reports do
context 'when entry value is not correct' do
describe '#errors' do
context 'when value of attribute is invalid' do
- let(:config) { { junit: 10 } }
+ where(key: described_class::ALLOWED_KEYS) do
+ let(:config) { { "#{key}": 10 } }
- it 'reports error' do
- expect(entry.errors)
- .to include 'reports junit should be an array of strings or a string'
+ it 'reports error' do
+ expect(entry.errors)
+ .to include "reports #{key} should be an array of strings or a string"
+ end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/retry_spec.rb b/spec/lib/gitlab/ci/config/entry/retry_spec.rb
new file mode 100644
index 00000000000..164a9ed4c3d
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/retry_spec.rb
@@ -0,0 +1,236 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Retry do
+ let(:entry) { described_class.new(config) }
+
+ shared_context 'when retry value is a numeric', :numeric do
+ let(:config) { max }
+ let(:max) {}
+ end
+
+ shared_context 'when retry value is a hash', :hash do
+ let(:config) { { max: max, when: public_send(:when) }.compact }
+ let(:when) {}
+ let(:max) {}
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ context 'when retry value is a numeric', :numeric do
+ let(:max) { 2 }
+
+ it 'is returned as a hash with max key' do
+ expect(value).to eq(max: 2)
+ end
+ end
+
+ context 'when retry value is a hash', :hash do
+ context 'and `when` is a string' do
+ let(:when) { 'unknown_failure' }
+
+ it 'returns when wrapped in an array' do
+ expect(value).to eq(when: ['unknown_failure'])
+ end
+ end
+
+ context 'and `when` is an array' do
+ let(:when) { %w[unknown_failure runner_system_failure] }
+
+ it 'returns when as it was passed' do
+ expect(value).to eq(when: %w[unknown_failure runner_system_failure])
+ end
+ end
+ end
+ end
+
+ describe 'validation' do
+ context 'when retry value is correct' do
+ context 'when it is a numeric', :numeric do
+ let(:max) { 2 }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'when it is a hash', :hash do
+ context 'with max' do
+ let(:max) { 2 }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'with string when' do
+ let(:when) { 'unknown_failure' }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'with string when always' do
+ let(:when) { 'always' }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'with array when' do
+ let(:when) { %w[unknown_failure runner_system_failure] }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ # Those values are documented at `doc/ci/yaml/README.md`. If any of
+ # those values gets invalid, documentation must be updated. To make
+ # sure this is catched, check explicitly that all of the documented
+ # values are valid. If they are not it means the documentation and this
+ # array must be updated.
+ RETRY_WHEN_IN_DOCUMENTATION = %w[
+ always
+ unknown_failure
+ script_failure
+ api_failure
+ stuck_or_timeout_failure
+ runner_system_failure
+ missing_dependency_failure
+ runner_unsupported
+ ].freeze
+
+ RETRY_WHEN_IN_DOCUMENTATION.each do |reason|
+ context "with when from documentation `#{reason}`" do
+ let(:when) { reason }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ ::Ci::Build.failure_reasons.each_key do |reason|
+ context "with when from CommitStatus.failure_reasons `#{reason}`" do
+ let(:when) { reason }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+ end
+ end
+
+ context 'when retry value is not correct' do
+ context 'when it is not a numeric nor an array' do
+ let(:config) { true }
+
+ it 'returns error about invalid type' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'retry config has to be either an integer or a hash'
+ end
+ end
+
+ context 'when it is a numeric', :numeric do
+ context 'when it is lower than zero' do
+ let(:max) { -1 }
+
+ it 'returns error about value too low' do
+ expect(entry).not_to be_valid
+ expect(entry.errors)
+ .to include 'retry config must be greater than or equal to 0'
+ end
+ end
+
+ context 'when it is not an integer' do
+ let(:max) { 1.5 }
+
+ it 'returns error about wrong value' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'retry config has to be either an integer or a hash'
+ end
+ end
+
+ context 'when the value is too high' do
+ let(:max) { 10 }
+
+ it 'returns error about value too high' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'retry config must be less than or equal to 2'
+ end
+ end
+ end
+
+ context 'when it is a hash', :hash do
+ context 'with unknown keys' do
+ let(:config) { { max: 2, unknown_key: :something, one_more: :key } }
+
+ it 'returns error about the unknown key' do
+ expect(entry).not_to be_valid
+ expect(entry.errors)
+ .to include 'retry config contains unknown keys: unknown_key, one_more'
+ end
+ end
+
+ context 'with max lower than zero' do
+ let(:max) { -1 }
+
+ it 'returns error about value too low' do
+ expect(entry).not_to be_valid
+ expect(entry.errors)
+ .to include 'retry max must be greater than or equal to 0'
+ end
+ end
+
+ context 'with max not an integer' do
+ let(:max) { 1.5 }
+
+ it 'returns error about wrong value' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'retry max must be an integer'
+ end
+ end
+
+ context 'iwth max too high' do
+ let(:max) { 10 }
+
+ it 'returns error about value too high' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'retry max must be less than or equal to 2'
+ end
+ end
+
+ context 'with when in wrong format' do
+ let(:when) { true }
+
+ it 'returns error about the wrong format' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'retry when should be an array of strings or a string'
+ end
+ end
+
+ context 'with an unknown when string' do
+ let(:when) { 'unknown_reason' }
+
+ it 'returns error about the wrong format' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'retry when is not included in the list'
+ end
+ end
+
+ context 'with an unknown failure reason in a when array' do
+ let(:when) { %w[unknown_reason runner_system_failure] }
+
+ it 'returns error about the wrong format' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'retry when contains unknown values: unknown_reason'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/extendable/entry_spec.rb b/spec/lib/gitlab/ci/config/extendable/entry_spec.rb
new file mode 100644
index 00000000000..0a148375d11
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/extendable/entry_spec.rb
@@ -0,0 +1,227 @@
+require 'fast_spec_helper'
+
+describe Gitlab::Ci::Config::Extendable::Entry do
+ describe '.new' do
+ context 'when entry key is not included in the context hash' do
+ it 'raises error' do
+ expect { described_class.new(:test, something: 'something') }
+ .to raise_error StandardError, 'Invalid entry key!'
+ end
+ end
+ end
+
+ describe '#value' do
+ it 'reads a hash value from the context' do
+ entry = described_class.new(:test, test: 'something')
+
+ expect(entry.value).to eq 'something'
+ end
+ end
+
+ describe '#extensible?' do
+ context 'when entry has inheritance defined' do
+ it 'is extensible' do
+ entry = described_class.new(:test, test: { extends: 'something' })
+
+ expect(entry).to be_extensible
+ end
+ end
+
+ context 'when entry does not have inheritance specified' do
+ it 'is not extensible' do
+ entry = described_class.new(:test, test: { script: 'something' })
+
+ expect(entry).not_to be_extensible
+ end
+ end
+
+ context 'when entry value is not a hash' do
+ it 'is not extensible' do
+ entry = described_class.new(:test, test: 'something')
+
+ expect(entry).not_to be_extensible
+ end
+ end
+ end
+
+ describe '#extends_key' do
+ context 'when entry is extensible' do
+ it 'returns symbolized extends key value' do
+ entry = described_class.new(:test, test: { extends: 'something' })
+
+ expect(entry.extends_key).to eq :something
+ end
+ end
+
+ context 'when entry is not extensible' do
+ it 'returns nil' do
+ entry = described_class.new(:test, test: 'something')
+
+ expect(entry.extends_key).to be_nil
+ end
+ end
+ end
+
+ describe '#ancestors' do
+ let(:parent) do
+ described_class.new(:test, test: { extends: 'something' })
+ end
+
+ let(:child) do
+ described_class.new(:job, { job: { script: 'something' } }, parent)
+ end
+
+ it 'returns ancestors keys' do
+ expect(child.ancestors).to eq [:test]
+ end
+ end
+
+ describe '#base_hash!' do
+ subject { described_class.new(:test, hash) }
+
+ context 'when base hash is not extensible' do
+ let(:hash) do
+ {
+ template: { script: 'rspec' },
+ test: { extends: 'template' }
+ }
+ end
+
+ it 'returns unchanged base hash' do
+ expect(subject.base_hash!).to eq(script: 'rspec')
+ end
+ end
+
+ context 'when base hash is extensible too' do
+ let(:hash) do
+ {
+ first: { script: 'rspec' },
+ second: { extends: 'first' },
+ test: { extends: 'second' }
+ }
+ end
+
+ it 'extends the base hash first' do
+ expect(subject.base_hash!).to eq(extends: 'first', script: 'rspec')
+ end
+
+ it 'mutates original context' do
+ subject.base_hash!
+
+ expect(hash.fetch(:second)).to eq(extends: 'first', script: 'rspec')
+ end
+ end
+ end
+
+ describe '#extend!' do
+ subject { described_class.new(:test, hash) }
+
+ context 'when extending a non-hash value' do
+ let(:hash) do
+ {
+ first: 'my value',
+ test: { extends: 'first' }
+ }
+ end
+
+ it 'raises an error' do
+ expect { subject.extend! }
+ .to raise_error(described_class::InvalidExtensionError,
+ /invalid base hash/)
+ end
+ end
+
+ context 'when extending unknown key' do
+ let(:hash) do
+ { test: { extends: 'something' } }
+ end
+
+ it 'raises an error' do
+ expect { subject.extend! }
+ .to raise_error(described_class::InvalidExtensionError,
+ /unknown key/)
+ end
+ end
+
+ context 'when extending a hash correctly' do
+ let(:hash) do
+ {
+ first: { script: 'my value' },
+ second: { extends: 'first' },
+ test: { extends: 'second' }
+ }
+ end
+
+ let(:result) do
+ {
+ first: { script: 'my value' },
+ second: { extends: 'first', script: 'my value' },
+ test: { extends: 'second', script: 'my value' }
+ }
+ end
+
+ it 'returns extended part of the hash' do
+ expect(subject.extend!).to eq result[:test]
+ end
+
+ it 'mutates original context' do
+ subject.extend!
+
+ expect(hash).to eq result
+ end
+ end
+
+ context 'when hash is not extensible' do
+ let(:hash) do
+ {
+ first: { script: 'my value' },
+ second: { extends: 'first' },
+ test: { value: 'something' }
+ }
+ end
+
+ it 'returns original key value' do
+ expect(subject.extend!).to eq(value: 'something')
+ end
+
+ it 'does not mutate orignal context' do
+ original = hash.deep_dup
+
+ subject.extend!
+
+ expect(hash).to eq original
+ end
+ end
+
+ context 'when circular depenency gets detected' do
+ let(:hash) do
+ { test: { extends: 'test' } }
+ end
+
+ it 'raises an error' do
+ expect { subject.extend! }
+ .to raise_error(described_class::CircularDependencyError,
+ /circular dependency detected/)
+ end
+ end
+
+ context 'when nesting level is too deep' do
+ before do
+ stub_const("#{described_class}::MAX_NESTING_LEVELS", 0)
+ end
+
+ let(:hash) do
+ {
+ first: { script: 'my value' },
+ second: { extends: 'first' },
+ test: { extends: 'second' }
+ }
+ end
+
+ it 'raises an error' do
+ expect { subject.extend! }
+ .to raise_error(described_class::NestingTooDeepError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/extendable_spec.rb b/spec/lib/gitlab/ci/config/extendable_spec.rb
new file mode 100644
index 00000000000..90213f6603d
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/extendable_spec.rb
@@ -0,0 +1,228 @@
+require 'fast_spec_helper'
+
+describe Gitlab::Ci::Config::Extendable do
+ subject { described_class.new(hash) }
+
+ describe '#each' do
+ context 'when there is extendable entry in the hash' do
+ let(:test) do
+ { extends: 'something', only: %w[master] }
+ end
+
+ let(:hash) do
+ { something: { script: 'ls' }, test: test }
+ end
+
+ it 'yields control' do
+ expect { |b| subject.each(&b) }.to yield_control
+ end
+ end
+ end
+
+ describe '#to_hash' do
+ context 'when hash does not contain extensions' do
+ let(:hash) do
+ {
+ test: { script: 'test' },
+ production: {
+ script: 'deploy',
+ only: { variables: %w[$SOMETHING] }
+ }
+ }
+ end
+
+ it 'does not modify the hash' do
+ expect(subject.to_hash).to eq hash
+ end
+ end
+
+ context 'when hash has a single simple extension' do
+ let(:hash) do
+ {
+ something: {
+ script: 'deploy',
+ only: { variables: %w[$SOMETHING] }
+ },
+
+ test: {
+ extends: 'something',
+ script: 'ls',
+ only: { refs: %w[master] }
+ }
+ }
+ end
+
+ it 'extends a hash with a deep reverse merge' do
+ expect(subject.to_hash).to eq(
+ something: {
+ script: 'deploy',
+ only: { variables: %w[$SOMETHING] }
+ },
+
+ test: {
+ extends: 'something',
+ script: 'ls',
+ only: {
+ refs: %w[master],
+ variables: %w[$SOMETHING]
+ }
+ }
+ )
+ end
+ end
+
+ context 'when a hash uses recursive extensions' do
+ let(:hash) do
+ {
+ test: {
+ extends: 'something',
+ script: 'ls',
+ only: { refs: %w[master] }
+ },
+
+ build: {
+ extends: 'something',
+ stage: 'build'
+ },
+
+ deploy: {
+ stage: 'deploy',
+ extends: '.first'
+ },
+
+ something: {
+ extends: '.first',
+ script: 'exec',
+ only: { variables: %w[$SOMETHING] }
+ },
+
+ '.first': {
+ script: 'run',
+ only: { kubernetes: 'active' }
+ }
+ }
+ end
+
+ it 'extends a hash with a deep reverse merge' do
+ expect(subject.to_hash).to eq(
+ '.first': {
+ script: 'run',
+ only: { kubernetes: 'active' }
+ },
+
+ something: {
+ extends: '.first',
+ script: 'exec',
+ only: {
+ kubernetes: 'active',
+ variables: %w[$SOMETHING]
+ }
+ },
+
+ deploy: {
+ script: 'run',
+ stage: 'deploy',
+ only: { kubernetes: 'active' },
+ extends: '.first'
+ },
+
+ build: {
+ extends: 'something',
+ script: 'exec',
+ stage: 'build',
+ only: {
+ kubernetes: 'active',
+ variables: %w[$SOMETHING]
+ }
+ },
+
+ test: {
+ extends: 'something',
+ script: 'ls',
+ only: {
+ refs: %w[master],
+ variables: %w[$SOMETHING],
+ kubernetes: 'active'
+ }
+ }
+ )
+ end
+ end
+
+ context 'when nested circular dependecy has been detected' do
+ let(:hash) do
+ {
+ test: {
+ extends: 'something',
+ script: 'ls',
+ only: { refs: %w[master] }
+ },
+
+ something: {
+ extends: '.first',
+ script: 'deploy',
+ only: { variables: %w[$SOMETHING] }
+ },
+
+ '.first': {
+ extends: 'something',
+ script: 'run',
+ only: { kubernetes: 'active' }
+ }
+ }
+ end
+
+ it 'raises an error about circular dependency' do
+ expect { subject.to_hash }
+ .to raise_error(described_class::Entry::CircularDependencyError)
+ end
+ end
+
+ context 'when circular dependecy to self has been detected' do
+ let(:hash) do
+ {
+ test: {
+ extends: 'test',
+ script: 'ls',
+ only: { refs: %w[master] }
+ }
+ }
+ end
+
+ it 'raises an error about circular dependency' do
+ expect { subject.to_hash }
+ .to raise_error(described_class::Entry::CircularDependencyError)
+ end
+ end
+
+ context 'when invalid extends value is specified' do
+ let(:hash) do
+ { something: { extends: 1, script: 'ls' } }
+ end
+
+ it 'raises an error about invalid extension' do
+ expect { subject.to_hash }
+ .to raise_error(described_class::Entry::InvalidExtensionError)
+ end
+ end
+
+ context 'when extensible entry has non-hash inheritance defined' do
+ let(:hash) do
+ {
+ test: {
+ extends: 'something',
+ script: 'ls',
+ only: { refs: %w[master] }
+ },
+
+ something: 'some text'
+ }
+ end
+
+ it 'raises an error about invalid base' do
+ expect { subject.to_hash }
+ .to raise_error(described_class::Entry::InvalidExtensionError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
new file mode 100644
index 00000000000..2e92d5204d6
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+describe Gitlab::Ci::Config::External::File::Base do
+ subject { described_class.new(location) }
+
+ before do
+ allow_any_instance_of(described_class)
+ .to receive(:content).and_return('key: value')
+ end
+
+ describe '#valid?' do
+ context 'when location is not a YAML file' do
+ let(:location) { 'some/file.txt' }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when location has not a valid naming scheme' do
+ let(:location) { 'some/file/.yml' }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when location is a valid .yml extension' do
+ let(:location) { 'some/file/config.yml' }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when location is a valid .yaml extension' do
+ let(:location) { 'some/file/config.yaml' }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when there are YAML syntax errors' do
+ let(:location) { 'some/file/config.yml' }
+
+ before do
+ allow_any_instance_of(described_class)
+ .to receive(:content).and_return('invalid_syntax')
+ end
+
+ it 'is not a valid file' do
+ expect(subject).not_to be_valid
+ expect(subject.error_message).to match /does not have valid YAML syntax/
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
new file mode 100644
index 00000000000..2708d8d5b6b
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::External::File::Local do
+ let(:project) { create(:project, :repository) }
+ let(:local_file) { described_class.new(location, { project: project, sha: '12345' }) }
+
+ describe '#valid?' do
+ context 'when is a valid local path' do
+ let(:location) { '/lib/gitlab/ci/templates/existent-file.yml' }
+
+ before do
+ allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return("image: 'ruby2:2'")
+ end
+
+ it 'should return true' do
+ expect(local_file.valid?).to be_truthy
+ end
+ end
+
+ context 'when is not a valid local path' do
+ let(:location) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
+
+ it 'should return false' do
+ expect(local_file.valid?).to be_falsy
+ end
+ end
+
+ context 'when is not a yaml file' do
+ let(:location) { '/config/application.rb' }
+
+ it 'should return false' do
+ expect(local_file.valid?).to be_falsy
+ end
+ end
+ end
+
+ describe '#content' do
+ context 'with a a valid file' do
+ let(:local_file_content) do
+ <<~HEREDOC
+ before_script:
+ - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
+ - ruby -v
+ - which ruby
+ - gem install bundler --no-ri --no-rdoc
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
+ HEREDOC
+ end
+ let(:location) { '/lib/gitlab/ci/templates/existent-file.yml' }
+
+ before do
+ allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return(local_file_content)
+ end
+
+ it 'should return the content of the file' do
+ expect(local_file.content).to eq(local_file_content)
+ end
+ end
+
+ context 'with an invalid file' do
+ let(:location) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
+
+ it 'should be nil' do
+ expect(local_file.content).to be_nil
+ end
+ end
+ end
+
+ describe '#error_message' do
+ let(:location) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
+
+ it 'should return an error message' do
+ expect(local_file.error_message).to eq("Local file `#{location}` does not exist!")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
new file mode 100644
index 00000000000..7c1a1c38736
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -0,0 +1,157 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::External::File::Remote do
+ let(:remote_file) { described_class.new(location) }
+ let(:location) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+ let(:remote_file_content) do
+ <<~HEREDOC
+ before_script:
+ - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
+ - ruby -v
+ - which ruby
+ - gem install bundler --no-ri --no-rdoc
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
+ HEREDOC
+ end
+
+ describe "#valid?" do
+ context 'when is a valid remote url' do
+ before do
+ WebMock.stub_request(:get, location).to_return(body: remote_file_content)
+ end
+
+ it 'should return true' do
+ expect(remote_file.valid?).to be_truthy
+ end
+ end
+
+ context 'with an irregular url' do
+ let(:location) { 'not-valid://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+
+ it 'should return false' do
+ expect(remote_file.valid?).to be_falsy
+ end
+ end
+
+ context 'with a timeout' do
+ before do
+ allow(Gitlab::HTTP).to receive(:get).and_raise(Timeout::Error)
+ end
+
+ it 'should be falsy' do
+ expect(remote_file.valid?).to be_falsy
+ end
+ end
+
+ context 'when is not a yaml file' do
+ let(:location) { 'https://asdasdasdaj48ggerexample.com' }
+
+ it 'should be falsy' do
+ expect(remote_file.valid?).to be_falsy
+ end
+ end
+
+ context 'with an internal url' do
+ let(:location) { 'http://localhost:8080' }
+
+ it 'should be falsy' do
+ expect(remote_file.valid?).to be_falsy
+ end
+ end
+ end
+
+ describe "#content" do
+ context 'with a valid remote file' do
+ before do
+ WebMock.stub_request(:get, location).to_return(body: remote_file_content)
+ end
+
+ it 'should return the content of the file' do
+ expect(remote_file.content).to eql(remote_file_content)
+ end
+ end
+
+ context 'with a timeout' do
+ before do
+ allow(Gitlab::HTTP).to receive(:get).and_raise(Timeout::Error)
+ end
+
+ it 'should be falsy' do
+ expect(remote_file.content).to be_falsy
+ end
+ end
+
+ context 'with an invalid remote url' do
+ let(:location) { 'https://asdasdasdaj48ggerexample.com' }
+
+ before do
+ WebMock.stub_request(:get, location).to_raise(SocketError.new('Some HTTP error'))
+ end
+
+ it 'should be nil' do
+ expect(remote_file.content).to be_nil
+ end
+ end
+
+ context 'with an internal url' do
+ let(:location) { 'http://localhost:8080' }
+
+ it 'should be nil' do
+ expect(remote_file.content).to be_nil
+ end
+ end
+ end
+
+ describe "#error_message" do
+ subject { remote_file.error_message }
+
+ context 'when remote file location is not valid' do
+ let(:location) { 'not-valid://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+
+ it 'returns an error message describing invalid address' do
+ expect(subject).to match /does not have a valid address!/
+ end
+ end
+
+ context 'when timeout error has been raised' do
+ before do
+ WebMock.stub_request(:get, location).to_timeout
+ end
+
+ it 'should returns error message about a timeout' do
+ expect(subject).to match /could not be fetched because of a timeout error!/
+ end
+ end
+
+ context 'when HTTP error has been raised' do
+ before do
+ WebMock.stub_request(:get, location).to_raise(Gitlab::HTTP::Error)
+ end
+
+ it 'should returns error message about a HTTP error' do
+ expect(subject).to match /could not be fetched because of HTTP error!/
+ end
+ end
+
+ context 'when response has 404 status' do
+ before do
+ WebMock.stub_request(:get, location).to_return(body: remote_file_content, status: 404)
+ end
+
+ it 'should returns error message about a timeout' do
+ expect(subject).to match /could not be fetched because of HTTP code `404` error!/
+ end
+ end
+
+ context 'when the URL is blocked' do
+ let(:location) { 'http://127.0.0.1/some/path/to/config.yaml' }
+
+ it 'should include details about blocked URL' do
+ expect(subject).to eq "Remote file could not be fetched because URL '#{location}' " \
+ 'is blocked: Requests to localhost are not allowed!'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
new file mode 100644
index 00000000000..5b236fe99f1
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::External::Mapper do
+ let(:project) { create(:project, :repository) }
+ let(:file_content) do
+ <<~HEREDOC
+ image: 'ruby:2.2'
+ HEREDOC
+ end
+
+ describe '#process' do
+ subject { described_class.new(values, project, '123456').process }
+
+ context "when 'include' keyword is defined as string" do
+ context 'when the string is a local file' do
+ let(:values) do
+ {
+ include: '/lib/gitlab/ci/templates/non-existent-file.yml',
+ image: 'ruby:2.2'
+ }
+ end
+
+ it 'returns an array' do
+ expect(subject).to be_an(Array)
+ end
+
+ it 'returns File instances' do
+ expect(subject.first)
+ .to be_an_instance_of(Gitlab::Ci::Config::External::File::Local)
+ end
+ end
+
+ context 'when the string is a remote file' do
+ let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+ let(:values) do
+ {
+ include: remote_url,
+ image: 'ruby:2.2'
+ }
+ end
+
+ before do
+ WebMock.stub_request(:get, remote_url).to_return(body: file_content)
+ end
+
+ it 'returns an array' do
+ expect(subject).to be_an(Array)
+ end
+
+ it 'returns File instances' do
+ expect(subject.first)
+ .to be_an_instance_of(Gitlab::Ci::Config::External::File::Remote)
+ end
+ end
+ end
+
+ context "when 'include' is defined as an array" do
+ let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+ let(:values) do
+ {
+ include:
+ [
+ remote_url,
+ '/lib/gitlab/ci/templates/template.yml'
+ ],
+ image: 'ruby:2.2'
+ }
+ end
+
+ before do
+ WebMock.stub_request(:get, remote_url).to_return(body: file_content)
+ end
+
+ it 'returns an array' do
+ expect(subject).to be_an(Array)
+ end
+
+ it 'returns Files instances' do
+ expect(subject).to all(respond_to(:valid?))
+ expect(subject).to all(respond_to(:content))
+ end
+ end
+
+ context "when 'include' is not defined" do
+ let(:values) do
+ {
+ image: 'ruby:2.2'
+ }
+ end
+
+ it 'returns an empty array' do
+ expect(subject).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
new file mode 100644
index 00000000000..1a05f716247
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::External::Processor do
+ let(:project) { create(:project, :repository) }
+ let(:processor) { described_class.new(values, project, '12345') }
+
+ describe "#perform" do
+ context 'when no external files defined' do
+ let(:values) { { image: 'ruby:2.2' } }
+
+ it 'should return the same values' do
+ expect(processor.perform).to eq(values)
+ end
+ end
+
+ context 'when an invalid local file is defined' do
+ let(:values) { { include: '/lib/gitlab/ci/templates/non-existent-file.yml', image: 'ruby:2.2' } }
+
+ it 'should raise an error' do
+ expect { processor.perform }.to raise_error(
+ described_class::IncludeError,
+ "Local file `/lib/gitlab/ci/templates/non-existent-file.yml` does not exist!"
+ )
+ end
+ end
+
+ context 'when an invalid remote file is defined' do
+ let(:remote_file) { 'http://doesntexist.com/.gitlab-ci-1.yml' }
+ let(:values) { { include: remote_file, image: 'ruby:2.2' } }
+
+ before do
+ WebMock.stub_request(:get, remote_file).to_raise(SocketError.new('Some HTTP error'))
+ end
+
+ it 'should raise an error' do
+ expect { processor.perform }.to raise_error(
+ described_class::IncludeError,
+ "Remote file `#{remote_file}` could not be fetched because of a socket error!"
+ )
+ end
+ end
+
+ context 'with a valid remote external file is defined' do
+ let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+ let(:values) { { include: remote_file, image: 'ruby:2.2' } }
+ let(:external_file_content) do
+ <<-HEREDOC
+ before_script:
+ - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
+ - ruby -v
+ - which ruby
+ - gem install bundler --no-ri --no-rdoc
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
+
+ rspec:
+ script:
+ - bundle exec rspec
+
+ rubocop:
+ script:
+ - bundle exec rubocop
+ HEREDOC
+ end
+
+ before do
+ WebMock.stub_request(:get, remote_file).to_return(body: external_file_content)
+ end
+
+ it 'should append the file to the values' do
+ output = processor.perform
+ expect(output.keys).to match_array([:image, :before_script, :rspec, :rubocop])
+ end
+
+ it "should remove the 'include' keyword" do
+ expect(processor.perform[:include]).to be_nil
+ end
+ end
+
+ context 'with a valid local external file is defined' do
+ let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.2' } }
+ let(:local_file_content) do
+ <<-HEREDOC
+ before_script:
+ - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
+ - ruby -v
+ - which ruby
+ - gem install bundler --no-ri --no-rdoc
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
+ HEREDOC
+ end
+
+ before do
+ allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
+ .to receive(:fetch_local_content).and_return(local_file_content)
+ end
+
+ it 'should append the file to the values' do
+ output = processor.perform
+ expect(output.keys).to match_array([:image, :before_script])
+ end
+
+ it "should remove the 'include' keyword" do
+ expect(processor.perform[:include]).to be_nil
+ end
+ end
+
+ context 'with multiple external files are defined' do
+ let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+ let(:external_files) do
+ [
+ '/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml',
+ remote_file
+ ]
+ end
+ let(:values) do
+ {
+ include: external_files,
+ image: 'ruby:2.2'
+ }
+ end
+
+ let(:remote_file_content) do
+ <<-HEREDOC
+ stages:
+ - build
+ - review
+ - cleanup
+ HEREDOC
+ end
+
+ before do
+ local_file_content = File.read(Rails.root.join('spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml'))
+
+ allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
+ .to receive(:fetch_local_content).and_return(local_file_content)
+
+ WebMock.stub_request(:get, remote_file).to_return(body: remote_file_content)
+ end
+
+ it 'should append the files to the values' do
+ expect(processor.perform.keys).to match_array([:image, :stages, :before_script, :rspec])
+ end
+
+ it "should remove the 'include' keyword" do
+ expect(processor.perform[:include]).to be_nil
+ end
+ end
+
+ context 'when external files are defined but not valid' do
+ let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.2' } }
+
+ let(:local_file_content) { 'invalid content file ////' }
+
+ before do
+ allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
+ .to receive(:fetch_local_content).and_return(local_file_content)
+ end
+
+ it 'should raise an error' do
+ expect { processor.perform }.to raise_error(
+ described_class::IncludeError,
+ "Included file `/lib/gitlab/ci/templates/template.yml` does not have valid YAML syntax!"
+ )
+ end
+ end
+
+ context "when both external files and values defined the same key" do
+ let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+ let(:values) do
+ {
+ include: remote_file,
+ image: 'ruby:2.2'
+ }
+ end
+
+ let(:remote_file_content) do
+ <<~HEREDOC
+ image: php:5-fpm-alpine
+ HEREDOC
+ end
+
+ it 'should take precedence' do
+ WebMock.stub_request(:get, remote_file).to_return(body: remote_file_content)
+ expect(processor.perform[:image]).to eq('ruby:2.2')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/normalizer_spec.rb b/spec/lib/gitlab/ci/config/normalizer_spec.rb
new file mode 100644
index 00000000000..97926695b6e
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/normalizer_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+describe Gitlab::Ci::Config::Normalizer do
+ let(:job_name) { :rspec }
+ let(:job_config) { { script: 'rspec', parallel: 5, name: 'rspec' } }
+ let(:config) { { job_name => job_config } }
+
+ describe '.normalize_jobs' do
+ subject { described_class.new(config).normalize_jobs }
+
+ it 'does not have original job' do
+ is_expected.not_to include(job_name)
+ end
+
+ it 'has parallelized jobs' do
+ job_names = [:"rspec 1/5", :"rspec 2/5", :"rspec 3/5", :"rspec 4/5", :"rspec 5/5"]
+
+ is_expected.to include(*job_names)
+ end
+
+ it 'sets job instance in options' do
+ expect(subject.values).to all(include(:instance))
+ end
+
+ it 'parallelizes jobs with original config' do
+ original_config = config[job_name].except(:name)
+ configs = subject.values.map { |config| config.except(:name, :instance) }
+
+ expect(configs).to all(eq(original_config))
+ end
+
+ context 'when the job is not parallelized' do
+ let(:job_config) { { script: 'rspec', name: 'rspec' } }
+
+ it 'returns the same hash' do
+ is_expected.to eq(config)
+ end
+ end
+
+ context 'when there is a job with a slash in it' do
+ let(:job_name) { :"rspec 35/2" }
+
+ it 'properly parallelizes job names' do
+ job_names = [:"rspec 35/2 1/5", :"rspec 35/2 2/5", :"rspec 35/2 3/5", :"rspec 35/2 4/5", :"rspec 35/2 5/5"]
+
+ is_expected.to include(*job_names)
+ end
+ end
+
+ context 'when jobs depend on parallelized jobs' do
+ let(:config) { { job_name => job_config, other_job: { script: 'echo 1', dependencies: [job_name.to_s] } } }
+
+ it 'parallelizes dependencies' do
+ job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"]
+
+ expect(subject[:other_job][:dependencies]).to include(*job_names)
+ end
+
+ it 'does not include original job name in dependencies' do
+ expect(subject[:other_job][:dependencies]).not_to include(job_name)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 2e204da307d..975e11e8cc1 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -42,6 +42,36 @@ describe Gitlab::Ci::Config do
end
end
+ context 'when using extendable hash' do
+ let(:yml) do
+ <<-EOS
+ image: ruby:2.2
+
+ rspec:
+ script: rspec
+
+ test:
+ extends: rspec
+ image: ruby:alpine
+ EOS
+ end
+
+ it 'correctly extends the hash' do
+ hash = {
+ image: 'ruby:2.2',
+ rspec: { script: 'rspec' },
+ test: {
+ extends: 'rspec',
+ image: 'ruby:alpine',
+ script: 'rspec'
+ }
+ }
+
+ expect(config).to be_valid
+ expect(config.to_hash).to eq hash
+ end
+ end
+
context 'when config is invalid' do
context 'when yml is incorrect' do
let(:yml) { '// invalid' }
@@ -49,7 +79,7 @@ describe Gitlab::Ci::Config do
describe '.new' do
it 'raises error' do
expect { config }.to raise_error(
- ::Gitlab::Ci::Config::Loader::FormatError,
+ described_class::ConfigError,
/Invalid configuration format/
)
end
@@ -75,5 +105,254 @@ describe Gitlab::Ci::Config do
end
end
end
+
+ context 'when invalid extended hash has been provided' do
+ let(:yml) do
+ <<-EOS
+ test:
+ extends: test
+ script: rspec
+ EOS
+ end
+
+ it 'raises an error' do
+ expect { config }.to raise_error(
+ described_class::ConfigError, /circular dependency detected/
+ )
+ end
+ end
+ end
+
+ context "when using 'include' directive" do
+ let(:project) { create(:project, :repository) }
+ let(:remote_location) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+ let(:local_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' }
+
+ let(:remote_file_content) do
+ <<~HEREDOC
+ variables:
+ AUTO_DEVOPS_DOMAIN: domain.example.com
+ POSTGRES_USER: user
+ POSTGRES_PASSWORD: testing-password
+ POSTGRES_ENABLED: "true"
+ POSTGRES_DB: $CI_ENVIRONMENT_SLUG
+ HEREDOC
+ end
+
+ let(:local_file_content) do
+ File.read(Rails.root.join(local_location))
+ end
+
+ let(:gitlab_ci_yml) do
+ <<~HEREDOC
+ include:
+ - #{local_location}
+ - #{remote_location}
+
+ image: ruby:2.2
+ HEREDOC
+ end
+
+ let(:config) do
+ described_class.new(gitlab_ci_yml, project: project, sha: '12345')
+ end
+
+ before do
+ WebMock.stub_request(:get, remote_location)
+ .to_return(body: remote_file_content)
+
+ allow(project.repository)
+ .to receive(:blob_data_at).and_return(local_file_content)
+ end
+
+ context "when gitlab_ci_yml has valid 'include' defined" do
+ it 'should return a composed hash' do
+ before_script_values = [
+ "apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs", "ruby -v",
+ "which ruby",
+ "gem install bundler --no-ri --no-rdoc",
+ "bundle install --jobs $(nproc) \"${FLAGS[@]}\""
+ ]
+ variables = {
+ AUTO_DEVOPS_DOMAIN: "domain.example.com",
+ POSTGRES_USER: "user",
+ POSTGRES_PASSWORD: "testing-password",
+ POSTGRES_ENABLED: "true",
+ POSTGRES_DB: "$CI_ENVIRONMENT_SLUG"
+ }
+ composed_hash = {
+ before_script: before_script_values,
+ image: "ruby:2.2",
+ rspec: { script: ["bundle exec rspec"] },
+ variables: variables
+ }
+
+ expect(config.to_hash).to eq(composed_hash)
+ end
+ end
+
+ context "when gitlab_ci.yml has invalid 'include' defined" do
+ let(:gitlab_ci_yml) do
+ <<~HEREDOC
+ include: invalid
+ HEREDOC
+ end
+
+ it 'raises error YamlProcessor validationError' do
+ expect { config }.to raise_error(
+ described_class::ConfigError,
+ "Included file `invalid` does not have YAML extension!"
+ )
+ end
+ end
+
+ describe 'external file version' do
+ context 'when external local file SHA is defined' do
+ it 'is using a defined value' do
+ expect(project.repository).to receive(:blob_data_at)
+ .with('eeff1122', local_location)
+
+ described_class.new(gitlab_ci_yml, project: project, sha: 'eeff1122')
+ end
+ end
+
+ context 'when external local file SHA is not defined' do
+ it 'is using latest SHA on the default branch' do
+ expect(project.repository).to receive(:root_ref_sha)
+
+ described_class.new(gitlab_ci_yml, project: project)
+ end
+ end
+ end
+
+ context "when both external files and gitlab_ci.yml defined the same key" do
+ let(:gitlab_ci_yml) do
+ <<~HEREDOC
+ include:
+ - #{remote_location}
+
+ image: ruby:2.2
+ HEREDOC
+ end
+
+ let(:remote_file_content) do
+ <<~HEREDOC
+ image: php:5-fpm-alpine
+ HEREDOC
+ end
+
+ it 'should take precedence' do
+ expect(config.to_hash).to eq({ image: 'ruby:2.2' })
+ end
+ end
+
+ context "when both external files and gitlab_ci.yml define a dictionary of distinct variables" do
+ let(:remote_file_content) do
+ <<~HEREDOC
+ variables:
+ A: 'alpha'
+ B: 'beta'
+ HEREDOC
+ end
+
+ let(:gitlab_ci_yml) do
+ <<~HEREDOC
+ include:
+ - #{remote_location}
+
+ variables:
+ C: 'gamma'
+ D: 'delta'
+ HEREDOC
+ end
+
+ it 'should merge the variables dictionaries' do
+ expect(config.to_hash).to eq({ variables: { A: 'alpha', B: 'beta', C: 'gamma', D: 'delta' } })
+ end
+ end
+
+ context "when both external files and gitlab_ci.yml define a dictionary of overlapping variables" do
+ let(:remote_file_content) do
+ <<~HEREDOC
+ variables:
+ A: 'alpha'
+ B: 'beta'
+ C: 'omnicron'
+ HEREDOC
+ end
+
+ let(:gitlab_ci_yml) do
+ <<~HEREDOC
+ include:
+ - #{remote_location}
+
+ variables:
+ C: 'gamma'
+ D: 'delta'
+ HEREDOC
+ end
+
+ it 'later declarations should take precedence' do
+ expect(config.to_hash).to eq({ variables: { A: 'alpha', B: 'beta', C: 'gamma', D: 'delta' } })
+ end
+ end
+
+ context 'when both external files and gitlab_ci.yml define a job' do
+ let(:remote_file_content) do
+ <<~HEREDOC
+ job1:
+ script:
+ - echo 'hello from remote file'
+ HEREDOC
+ end
+
+ let(:gitlab_ci_yml) do
+ <<~HEREDOC
+ include:
+ - #{remote_location}
+
+ job1:
+ variables:
+ VARIABLE_DEFINED_IN_MAIN_FILE: 'some value'
+ HEREDOC
+ end
+
+ it 'merges the jobs' do
+ expect(config.to_hash).to eq({
+ job1: {
+ script: ["echo 'hello from remote file'"],
+ variables: {
+ VARIABLE_DEFINED_IN_MAIN_FILE: 'some value'
+ }
+ }
+ })
+ end
+
+ context 'when the script key is in both' do
+ let(:gitlab_ci_yml) do
+ <<~HEREDOC
+ include:
+ - #{remote_location}
+
+ job1:
+ script:
+ - echo 'hello from main file'
+ variables:
+ VARIABLE_DEFINED_IN_MAIN_FILE: 'some value'
+ HEREDOC
+ end
+
+ it 'uses the script from the gitlab_ci.yml' do
+ expect(config.to_hash).to eq({
+ job1: {
+ script: ["echo 'hello from main file'"],
+ variables: {
+ VARIABLE_DEFINED_IN_MAIN_FILE: 'some value'
+ }
+ }
+ })
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/parsers/junit_spec.rb b/spec/lib/gitlab/ci/parsers/junit_spec.rb
deleted file mode 100644
index f7ec86f5385..00000000000
--- a/spec/lib/gitlab/ci/parsers/junit_spec.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Ci::Parsers::Junit do
- describe '#parse!' do
- subject { described_class.new.parse!(junit, test_suite) }
-
- let(:test_suite) { Gitlab::Ci::Reports::TestSuite.new('rspec') }
- let(:test_cases) { flattened_test_cases(test_suite) }
-
- context 'when data is JUnit style XML' do
- context 'when there are no test cases' do
- let(:junit) do
- <<-EOF.strip_heredoc
- <testsuite></testsuite>
- EOF
- end
-
- it 'raises an error and does not add any test cases' do
- expect { subject }.to raise_error(described_class::JunitParserError)
-
- expect(test_cases.count).to eq(0)
- end
- end
-
- context 'when there is a test case' do
- let(:junit) do
- <<-EOF.strip_heredoc
- <testsuite>
- <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
- </testsuite>
- EOF
- end
-
- it 'parses XML and adds a test case to a suite' do
- expect { subject }.not_to raise_error
-
- expect(test_cases[0].classname).to eq('Calculator')
- expect(test_cases[0].name).to eq('sumTest1')
- expect(test_cases[0].execution_time).to eq(0.01)
- end
- end
-
- context 'when there are two test cases' do
- let(:junit) do
- <<-EOF.strip_heredoc
- <testsuite>
- <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
- <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase>
- </testsuite>
- EOF
- end
-
- it 'parses XML and adds test cases to a suite' do
- expect { subject }.not_to raise_error
-
- expect(test_cases[0].classname).to eq('Calculator')
- expect(test_cases[0].name).to eq('sumTest1')
- expect(test_cases[0].execution_time).to eq(0.01)
- expect(test_cases[1].classname).to eq('Calculator')
- expect(test_cases[1].name).to eq('sumTest2')
- expect(test_cases[1].execution_time).to eq(0.02)
- end
- end
-
- context 'when there are two test suites' do
- let(:junit) do
- <<-EOF.strip_heredoc
- <testsuites>
- <testsuite>
- <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
- <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase>
- </testsuite>
- <testsuite>
- <testcase classname='Statemachine' name='happy path' time='100'></testcase>
- <testcase classname='Statemachine' name='unhappy path' time='200'></testcase>
- </testsuite>
- </testsuites>
- EOF
- end
-
- it 'parses XML and adds test cases to a suite' do
- expect { subject }.not_to raise_error
-
- expect(test_cases[0].classname).to eq('Calculator')
- expect(test_cases[0].name).to eq('sumTest1')
- expect(test_cases[0].execution_time).to eq(0.01)
- expect(test_cases[1].classname).to eq('Calculator')
- expect(test_cases[1].name).to eq('sumTest2')
- expect(test_cases[1].execution_time).to eq(0.02)
- expect(test_cases[2].classname).to eq('Statemachine')
- expect(test_cases[2].name).to eq('happy path')
- expect(test_cases[2].execution_time).to eq(100)
- expect(test_cases[3].classname).to eq('Statemachine')
- expect(test_cases[3].name).to eq('unhappy path')
- expect(test_cases[3].execution_time).to eq(200)
- end
- end
- end
-
- context 'when data is not JUnit style XML' do
- let(:junit) { { testsuite: 'abc' }.to_json }
-
- it 'raises an error' do
- expect { subject }.to raise_error(described_class::JunitParserError)
- end
- end
-
- private
-
- def flattened_test_cases(test_suite)
- test_suite.test_cases.map do |status, value|
- value.map do |key, test_case|
- test_case
- end
- end.flatten
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
new file mode 100644
index 00000000000..a49402c7398
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
@@ -0,0 +1,172 @@
+require 'fast_spec_helper'
+
+describe Gitlab::Ci::Parsers::Test::Junit do
+ describe '#parse!' do
+ subject { described_class.new.parse!(junit, test_suite) }
+
+ let(:test_suite) { Gitlab::Ci::Reports::TestSuite.new('rspec') }
+ let(:test_cases) { flattened_test_cases(test_suite) }
+
+ context 'when data is JUnit style XML' do
+ context 'when there are no <testcases> in <testsuite>' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite></testsuite>
+ EOF
+ end
+
+ it 'ignores the case' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases.count).to eq(0)
+ end
+ end
+
+ context 'when there are no <testcases> in <testsuites>' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuites><testsuite /></testsuites>
+ EOF
+ end
+
+ it 'ignores the case' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases.count).to eq(0)
+ end
+ end
+
+ context 'when there is only one <testcase> in <testsuite>' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ </testsuite>
+ EOF
+ end
+
+ it 'parses XML and adds a test case to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ end
+ end
+
+ context 'when there is only one <testsuite> in <testsuites>' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuites>
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ </testsuite>
+ </testsuites>
+ EOF
+ end
+
+ it 'parses XML and adds a test case to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ end
+ end
+
+ context 'PHPUnit' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuites>
+ <testsuite name="Project Test Suite" tests="1" assertions="1" failures="0" errors="0" time="1.376748">
+ <testsuite name="XXX\\FrontEnd\\WebBundle\\Tests\\Controller\\LogControllerTest" file="/Users/mcfedr/projects/xxx/server/tests/XXX/FrontEnd/WebBundle/Tests/Controller/LogControllerTest.php" tests="1" assertions="1" failures="0" errors="0" time="1.376748">
+ <testcase name="testIndexAction" class="XXX\\FrontEnd\\WebBundle\\Tests\\Controller\\LogControllerTest" file="/Users/mcfedr/projects/xxx/server/tests/XXX/FrontEnd/WebBundle/Tests/Controller/LogControllerTest.php" line="9" assertions="1" time="1.376748"/>
+ </testsuite>
+ </testsuite>
+ </testsuites>
+ EOF
+ end
+
+ it 'parses XML and adds a test case to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases.count).to eq(1)
+ end
+ end
+
+ context 'when there are two test cases' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase>
+ </testsuite>
+ EOF
+ end
+
+ it 'parses XML and adds test cases to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ expect(test_cases[1].classname).to eq('Calculator')
+ expect(test_cases[1].name).to eq('sumTest2')
+ expect(test_cases[1].execution_time).to eq(0.02)
+ end
+ end
+
+ context 'when there are two test suites' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuites>
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase>
+ </testsuite>
+ <testsuite>
+ <testcase classname='Statemachine' name='happy path' time='100'></testcase>
+ <testcase classname='Statemachine' name='unhappy path' time='200'></testcase>
+ </testsuite>
+ </testsuites>
+ EOF
+ end
+
+ it 'parses XML and adds test cases to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ expect(test_cases[1].classname).to eq('Calculator')
+ expect(test_cases[1].name).to eq('sumTest2')
+ expect(test_cases[1].execution_time).to eq(0.02)
+ expect(test_cases[2].classname).to eq('Statemachine')
+ expect(test_cases[2].name).to eq('happy path')
+ expect(test_cases[2].execution_time).to eq(100)
+ expect(test_cases[3].classname).to eq('Statemachine')
+ expect(test_cases[3].name).to eq('unhappy path')
+ expect(test_cases[3].execution_time).to eq(200)
+ end
+ end
+ end
+
+ context 'when data is not JUnit style XML' do
+ let(:junit) { { testsuite: 'abc' }.to_json }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(described_class::JunitParserError)
+ end
+ end
+
+ private
+
+ def flattened_test_cases(test_suite)
+ test_suite.test_cases.map do |status, value|
+ value.map do |key, test_case|
+ test_case
+ end
+ end.flatten
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/test_spec.rb b/spec/lib/gitlab/ci/parsers/test_spec.rb
new file mode 100644
index 00000000000..0b85b432677
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/test_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Parsers::Test do
+ describe '.fabricate!' do
+ subject { described_class.fabricate!(file_type) }
+
+ context 'when file_type exists' do
+ let(:file_type) { 'junit' }
+
+ it 'fabricates the class' do
+ is_expected.to be_a(described_class::Junit)
+ end
+ end
+
+ context 'when file_type does not exist' do
+ let(:file_type) { 'undefined' }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Ci::Parsers::Test::ParserNotFoundError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb
deleted file mode 100644
index 2fa83c4abae..00000000000
--- a/spec/lib/gitlab/ci/parsers_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Ci::Parsers do
- describe '.fabricate!' do
- subject { described_class.fabricate!(file_type) }
-
- context 'when file_type exists' do
- let(:file_type) { 'junit' }
-
- it 'fabricates the class' do
- is_expected.to be_a(described_class::Junit)
- end
- end
-
- context 'when file_type does not exist' do
- let(:file_type) { 'undefined' }
-
- it 'raises an error' do
- expect { subject }.to raise_error(NameError)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb
index d53a7d468e3..b379b08ad62 100644
--- a/spec/lib/gitlab/ci/status/build/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb
@@ -88,7 +88,7 @@ describe Gitlab::Ci::Status::Build::Factory do
expect(status.icon).to eq 'status_failed'
expect(status.favicon).to eq 'favicon_status_failed'
expect(status.label).to eq 'failed'
- expect(status.status_tooltip).to eq 'failed <br> (unknown failure)'
+ expect(status.status_tooltip).to eq 'failed - (unknown failure)'
expect(status).to have_details
expect(status).to have_action
end
@@ -319,4 +319,53 @@ describe Gitlab::Ci::Status::Build::Factory do
end
end
end
+
+ context 'when build is a delayed action' do
+ let(:build) { create(:ci_build, :scheduled) }
+
+ it 'matches correct core status' do
+ expect(factory.core_status).to be_a Gitlab::Ci::Status::Scheduled
+ end
+
+ it 'matches correct extended statuses' do
+ expect(factory.extended_statuses)
+ .to eq [Gitlab::Ci::Status::Build::Scheduled,
+ Gitlab::Ci::Status::Build::Unschedule,
+ Gitlab::Ci::Status::Build::Action]
+ end
+
+ it 'fabricates action detailed status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Action
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'delayed'
+ expect(status.group).to eq 'scheduled'
+ expect(status.icon).to eq 'status_scheduled'
+ expect(status.favicon).to eq 'favicon_status_scheduled'
+ expect(status.illustration).to include(:image, :size, :title, :content)
+ expect(status.label).to include 'unschedule action'
+ expect(status).to have_details
+ expect(status.action_path).to include 'unschedule'
+ end
+
+ context 'when user has ability to play action' do
+ it 'fabricates status that has action' do
+ expect(status).to have_action
+ end
+ end
+
+ context 'when user does not have ability to play action' do
+ before do
+ allow(build.project).to receive(:empty_repo?).and_return(false)
+
+ create(:protected_branch, :no_one_can_push,
+ name: build.ref, project: build.project)
+ end
+
+ it 'fabricates status that has no action' do
+ expect(status).not_to have_action
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
index bfaa508785e..af03d5a1308 100644
--- a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
@@ -76,7 +76,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do
let(:status) { described_class.new(build_status) }
it 'does override badge_tooltip' do
- expect(status.badge_tooltip).to eq('failed <br> (unknown failure)')
+ expect(status.badge_tooltip).to eq('failed - (unknown failure)')
end
end
@@ -87,7 +87,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do
let(:status) { described_class.new(build_status) }
it 'does override status_tooltip' do
- expect(status.status_tooltip).to eq 'failed <br> (unknown failure) (allowed to fail)'
+ expect(status.status_tooltip).to eq 'failed - (unknown failure) (allowed to fail)'
end
end
diff --git a/spec/lib/gitlab/ci/status/build/failed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_spec.rb
index b6676b40fd3..e424270f7c5 100644
--- a/spec/lib/gitlab/ci/status/build/failed_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/failed_spec.rb
@@ -52,7 +52,7 @@ describe Gitlab::Ci::Status::Build::Failed do
let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
it 'does override badge_tooltip' do
- expect(subject.badge_tooltip).to eq 'failed <br> (script failure)'
+ expect(subject.badge_tooltip).to eq 'failed - (script failure)'
end
end
@@ -61,7 +61,7 @@ describe Gitlab::Ci::Status::Build::Failed do
let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
it 'does override status_tooltip' do
- expect(subject.status_tooltip).to eq 'failed <br> (script failure)'
+ expect(subject.status_tooltip).to eq 'failed - (script failure)'
end
end
diff --git a/spec/lib/gitlab/ci/status/build/retried_spec.rb b/spec/lib/gitlab/ci/status/build/retried_spec.rb
index ee9acaf1c21..76c2fb01e3f 100644
--- a/spec/lib/gitlab/ci/status/build/retried_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/retried_spec.rb
@@ -66,7 +66,7 @@ describe Gitlab::Ci::Status::Build::Retried do
let(:status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) }
it 'does override status_tooltip' do
- expect(subject.status_tooltip).to eq 'failed <br> (unknown failure) (retried)'
+ expect(subject.status_tooltip).to eq 'failed - (unknown failure) (retried)'
end
end
diff --git a/spec/lib/gitlab/ci/status/build/scheduled_spec.rb b/spec/lib/gitlab/ci/status/build/scheduled_spec.rb
new file mode 100644
index 00000000000..68b87fea75d
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/scheduled_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Scheduled do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :stubbed_repository) }
+ let(:build) { create(:ci_build, :scheduled, project: project) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ subject { described_class.new(status) }
+
+ describe '#illustration' do
+ it { expect(subject.illustration).to include(:image, :size, :title) }
+ end
+
+ describe '#status_tooltip' do
+ let(:build) { create(:ci_build, scheduled_at: 1.minute.since, project: project) }
+
+ it 'has a placeholder for the remaining time' do
+ expect(subject.status_tooltip).to include('%{remainingTime}')
+ end
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, user) }
+
+ context 'when build is scheduled and scheduled_at is present' do
+ let(:build) { create(:ci_build, :expired_scheduled, project: project) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when build is scheduled' do
+ let(:build) { create(:ci_build, status: :scheduled, project: project) }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when scheduled_at is present' do
+ let(:build) { create(:ci_build, scheduled_at: 1.minute.since, project: project) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/unschedule_spec.rb b/spec/lib/gitlab/ci/status/build/unschedule_spec.rb
new file mode 100644
index 00000000000..ed046d66ca5
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/unschedule_spec.rb
@@ -0,0 +1,94 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Unschedule do
+ let(:status) { double('core status') }
+ let(:user) { double('user') }
+
+ subject do
+ described_class.new(status)
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'unschedule action' }
+ end
+
+ describe 'action details' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ describe '#has_action?' do
+ context 'when user is allowed to update build' do
+ before do
+ stub_not_protect_default_branch
+
+ build.project.add_developer(user)
+ end
+
+ it { is_expected.to have_action }
+ end
+
+ context 'when user is not allowed to update build' do
+ it { is_expected.not_to have_action }
+ end
+ end
+
+ describe '#action_path' do
+ it { expect(subject.action_path).to include "#{build.id}/unschedule" }
+ end
+
+ describe '#action_icon' do
+ it { expect(subject.action_icon).to eq 'time-out' }
+ end
+
+ describe '#action_title' do
+ it { expect(subject.action_title).to eq 'Unschedule' }
+ end
+
+ describe '#action_button_title' do
+ it { expect(subject.action_button_title).to eq 'Unschedule job' }
+ end
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, user) }
+
+ context 'when build is scheduled' do
+ context 'when build unschedules an delayed job' do
+ let(:build) { create(:ci_build, :scheduled) }
+
+ it 'is a correct match' do
+ expect(subject).to be true
+ end
+ end
+
+ context 'when build unschedules an normal job' do
+ let(:build) { create(:ci_build) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+ end
+
+ describe '#status_tooltip' do
+ it 'does not override status status_tooltip' do
+ expect(status).to receive(:status_tooltip)
+
+ subject.status_tooltip
+ end
+ end
+
+ describe '#badge_tooltip' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, :playable) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ it 'does not override status badge_tooltip' do
+ expect(status).to receive(:badge_tooltip)
+
+ subject.badge_tooltip
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb b/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb
new file mode 100644
index 00000000000..f89712d2b03
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pipeline::Delayed do
+ let(:pipeline) { double('pipeline') }
+
+ subject do
+ described_class.new(pipeline)
+ end
+
+ describe '#text' do
+ it 'overrides status text' do
+ expect(subject.text).to eq 'delayed'
+ end
+ end
+
+ describe '#label' do
+ it 'overrides status label' do
+ expect(subject.label).to eq 'waiting for delayed job'
+ end
+ end
+
+ describe '.matches?' do
+ let(:user) { double('user') }
+ subject { described_class.matches?(pipeline, user) }
+
+ context 'when pipeline is scheduled' do
+ let(:pipeline) { create(:ci_pipeline, :scheduled) }
+
+ it 'is a correct match' do
+ expect(subject).to be true
+ end
+ end
+
+ context 'when pipeline is not scheduled' do
+ let(:pipeline) { create(:ci_pipeline, :success) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
index defb3fdc0df..466087a0e31 100644
--- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
@@ -11,8 +11,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
end
context 'when pipeline has a core status' do
- (HasStatus::AVAILABLE_STATUSES - [HasStatus::BLOCKED_STATUS])
- .each do |simple_status|
+ (HasStatus::AVAILABLE_STATUSES - HasStatus::BLOCKED_STATUS).each do |simple_status|
context "when core status is #{simple_status}" do
let(:pipeline) { create(:ci_pipeline, status: simple_status) }
@@ -61,6 +60,27 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
.to include "pipelines/#{pipeline.id}"
end
end
+
+ context "when core status is scheduled" do
+ let(:pipeline) { create(:ci_pipeline, status: :scheduled) }
+
+ it "matches scheduled core status" do
+ expect(factory.core_status)
+ .to be_a Gitlab::Ci::Status::Scheduled
+ end
+
+ it 'matches a correct extended statuses' do
+ expect(factory.extended_statuses)
+ .to eq [Gitlab::Ci::Status::Pipeline::Delayed]
+ end
+
+ it 'extends core status with common pipeline methods' do
+ expect(status).to have_details
+ expect(status).not_to have_action
+ expect(status.details_path)
+ .to include "pipelines/#{pipeline.id}"
+ end
+ end
end
context 'when pipeline has warnings' do
diff --git a/spec/lib/gitlab/ci/status/scheduled_spec.rb b/spec/lib/gitlab/ci/status/scheduled_spec.rb
new file mode 100644
index 00000000000..b8ca3caa1f7
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/scheduled_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Scheduled do
+ subject do
+ described_class.new(double('subject'), double('user'))
+ end
+
+ describe '#text' do
+ it { expect(subject.text).to eq 'delayed' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'delayed' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'status_scheduled' }
+ end
+
+ describe '#favicon' do
+ it { expect(subject.favicon).to eq 'favicon_status_scheduled' }
+ end
+
+ describe '#group' do
+ it { expect(subject.group).to eq 'scheduled' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/templates_spec.rb b/spec/lib/gitlab/ci/templates/templates_spec.rb
new file mode 100644
index 00000000000..0dd74399a47
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/templates_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe "CI YML Templates" do
+ Gitlab::Template::GitlabCiYmlTemplate.all.each do |template|
+ it "#{template.name} should be valid" do
+ expect { Gitlab::Ci::YamlProcessor.new(template.content) }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
index 46874662edd..e1e0582cd11 100644
--- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
@@ -36,7 +36,7 @@ describe Gitlab::Ci::Variables::Collection::Item do
shared_examples 'raises error for invalid type' do
it do
expect { described_class.new(key: variable_key, value: variable_value) }
- .to raise_error ArgumentError, /`value` must be of type String, while it was:/
+ .to raise_error ArgumentError, /`#{variable_key}` must be of type String, while it was:/
end
end
@@ -46,7 +46,7 @@ describe Gitlab::Ci::Variables::Collection::Item do
let(:variable_value) { nil }
let(:expected_value) { nil }
- it_behaves_like 'creates variable'
+ it_behaves_like 'raises error for invalid type'
end
context "when it's an empty string" do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index e73cdc54a15..441e8214181 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -53,11 +53,11 @@ module Gitlab
describe 'retry entry' do
context 'when retry count is specified' do
let(:config) do
- YAML.dump(rspec: { script: 'rspec', retry: 1 })
+ YAML.dump(rspec: { script: 'rspec', retry: { max: 1 } })
end
it 'includes retry count in build options attribute' do
- expect(subject[:options]).to include(retry: 1)
+ expect(subject[:options]).to include(retry: { max: 1 })
end
end
@@ -121,6 +121,21 @@ module Gitlab
end
end
end
+
+ describe 'delayed job entry' do
+ context 'when delayed is defined' do
+ let(:config) do
+ YAML.dump(rspec: { script: 'rollout 10%',
+ when: 'delayed',
+ start_in: '1 day' })
+ end
+
+ it 'has the attributes' do
+ expect(subject[:when]).to eq 'delayed'
+ expect(subject[:options][:start_in]).to eq '1 day'
+ end
+ end
+ end
end
describe '#stages_attributes' do
@@ -562,6 +577,58 @@ module Gitlab
end
end
+ context 'when using `extends`' do
+ let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config) }
+
+ subject { config_processor.builds.first }
+
+ context 'when using simple `extends`' do
+ let(:config) do
+ <<~YAML
+ .template:
+ script: test
+
+ rspec:
+ extends: .template
+ image: ruby:alpine
+ YAML
+ end
+
+ it 'correctly extends rspec job' do
+ expect(config_processor.builds).to be_one
+ expect(subject.dig(:commands)).to eq 'test'
+ expect(subject.dig(:options, :image, :name)).to eq 'ruby:alpine'
+ end
+ end
+
+ context 'when using recursive `extends`' do
+ let(:config) do
+ <<~YAML
+ rspec:
+ extends: .test
+ script: rspec
+ when: always
+
+ .template:
+ before_script:
+ - bundle install
+
+ .test:
+ extends: .template
+ script: test
+ image: image:test
+ YAML
+ end
+
+ it 'correctly extends rspec job' do
+ expect(config_processor.builds).to be_one
+ expect(subject.dig(:commands)).to eq "bundle install\nrspec"
+ expect(subject.dig(:options, :image, :name)).to eq 'image:test'
+ expect(subject.dig(:when)).to eq 'always'
+ end
+ end
+ end
+
describe "When" do
%w(on_success on_failure always).each do |when_state|
it "returns #{when_state} when defined" do
@@ -578,6 +645,33 @@ module Gitlab
end
end
+ describe 'Parallel' do
+ context 'when job is parallelized' do
+ let(:parallel) { 5 }
+
+ let(:config) do
+ YAML.dump(rspec: { script: 'rspec',
+ parallel: parallel })
+ end
+
+ it 'returns parallelized jobs' do
+ config_processor = Gitlab::Ci::YamlProcessor.new(config)
+ builds = config_processor.stage_builds_attributes('test')
+ build_options = builds.map { |build| build[:options] }
+
+ expect(builds.size).to eq(5)
+ expect(build_options).to all(include(:instance, parallel: parallel))
+ end
+
+ it 'does not have the original job' do
+ config_processor = Gitlab::Ci::YamlProcessor.new(config)
+ builds = config_processor.stage_builds_attributes('test')
+
+ expect(builds).not_to include(:rspec)
+ end
+ end
+ end
+
describe 'cache' do
context 'when cache definition has unknown keys' do
it 'raises relevant validation error' do
@@ -1208,7 +1302,7 @@ module Gitlab
config = YAML.dump({ rspec: { script: "test", when: 1 } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec when should be on_success, on_failure, always or manual")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec when should be on_success, on_failure, always, manual or delayed")
end
it "returns errors if job artifacts:name is not an a string" do
@@ -1302,24 +1396,28 @@ module Gitlab
end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec dependencies should be an array of strings")
end
- it 'returns errors if pipeline variables expression is invalid' do
+ it 'returns errors if pipeline variables expression policy is invalid' do
config = YAML.dump({ rspec: { script: 'test', only: { variables: ['== null'] } } })
expect { Gitlab::Ci::YamlProcessor.new(config) }
.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:only variables invalid expression syntax')
end
- end
- describe "Validate configuration templates" do
- templates = Dir.glob("#{Rails.root.join('vendor/gitlab-ci-yml')}/**/*.gitlab-ci.yml")
+ it 'returns errors if pipeline changes policy is invalid' do
+ config = YAML.dump({ rspec: { script: 'test', only: { changes: [1] } } })
- templates.each do |file|
- it "does not return errors for #{file}" do
- file = File.read(file)
+ expect { Gitlab::Ci::YamlProcessor.new(config) }
+ .to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
+ 'jobs:rspec:only changes should be an array of strings')
+ end
- expect { Gitlab::Ci::YamlProcessor.new(file) }.not_to raise_error
- end
+ it 'returns errors if extended hash configuration is invalid' do
+ config = YAML.dump({ rspec: { extends: 'something', script: 'test' } })
+
+ expect { Gitlab::Ci::YamlProcessor.new(config) }
+ .to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
+ 'rspec: unknown key in `extends`')
end
end
diff --git a/spec/lib/gitlab/cleanup/project_uploads_spec.rb b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
index 37b38776775..bf130b8fabd 100644
--- a/spec/lib/gitlab/cleanup/project_uploads_spec.rb
+++ b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
@@ -132,7 +132,6 @@ describe Gitlab::Cleanup::ProjectUploads do
let!(:path) { File.join(FileUploader.root, orphaned.model.full_path, orphaned.path) }
before do
- stub_feature_flags(import_export_object_storage: true)
stub_uploads_object_storage(FileUploader)
FileUtils.mkdir_p(File.dirname(path))
@@ -156,7 +155,6 @@ describe Gitlab::Cleanup::ProjectUploads do
let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', 'wrong', orphaned.path) }
before do
- stub_feature_flags(import_export_object_storage: true)
stub_uploads_object_storage(FileUploader)
FileUtils.mkdir_p(File.dirname(path))
@@ -244,9 +242,11 @@ describe Gitlab::Cleanup::ProjectUploads do
orphaned1 = create(:upload, :personal_snippet_upload, :with_file)
orphaned2 = create(:upload, :namespace_upload, :with_file)
orphaned3 = create(:upload, :attachment_upload, :with_file)
+ orphaned4 = create(:upload, :favicon_upload, :with_file)
paths << orphaned1.absolute_path
paths << orphaned2.absolute_path
paths << orphaned3.absolute_path
+ paths << orphaned4.absolute_path
Upload.delete_all
expect(logger).not_to receive(:info).with(/move|fix/i)
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 1f35d1e4880..44568f2a653 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -338,6 +338,13 @@ describe Gitlab::ClosingIssueExtractor do
end
end
+ context "with an invalid keyword such as suffix insted of fix" do
+ it do
+ message = "suffix #{reference}"
+ expect(subject.closed_by_message(message)).to eq([])
+ end
+ end
+
context 'with multiple references' do
let(:other_issue) { create(:issue, project: project) }
let(:third_issue) { create(:issue, project: project) }
diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb
index 5b343920429..a955ce54e85 100644
--- a/spec/lib/gitlab/conflict/file_spec.rb
+++ b/spec/lib/gitlab/conflict/file_spec.rb
@@ -1,9 +1,11 @@
require 'spec_helper'
describe Gitlab::Conflict::File do
+ include GitHelpers
+
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
- let(:rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } }
+ let(:rugged) { rugged_repo(repository) }
let(:their_commit) { rugged.branches['conflict-start'].target }
let(:our_commit) { rugged.branches['conflict-resolvable'].target }
let(:merge_request) { create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project) }
@@ -69,10 +71,6 @@ describe Gitlab::Conflict::File do
CGI.unescapeHTML(ActionView::Base.full_sanitizer.sanitize(html)).delete("\n")
end
- it 'modifies the existing lines' do
- expect { conflict_file.highlight_lines! }.to change { conflict_file.lines.map(&:instance_variables) }
- end
-
it 'is called implicitly when rich_text is accessed on a line' do
expect(conflict_file).to receive(:highlight_lines!).once.and_call_original
@@ -269,11 +267,6 @@ FILE
it 'includes the full content of the conflict' do
expect(conflict_file.as_json(full_content: true)).to have_key(:content)
end
-
- it 'includes the detected language of the conflict file' do
- expect(conflict_file.as_json(full_content: true)[:blob_ace_mode])
- .to eq('ruby')
- end
end
end
end
diff --git a/spec/lib/gitlab/contributions_calendar_spec.rb b/spec/lib/gitlab/contributions_calendar_spec.rb
index 2c63f3b0455..6d29044ffd5 100644
--- a/spec/lib/gitlab/contributions_calendar_spec.rb
+++ b/spec/lib/gitlab/contributions_calendar_spec.rb
@@ -62,13 +62,16 @@ describe Gitlab::ContributionsCalendar do
expect(calendar.activity_dates).to eq(last_week => 2, today => 1)
end
- it "only shows private events to authorized users" do
- create_event(private_project, today)
- create_event(feature_project, today)
+ context "when the user has opted-in for private contributions" do
+ it "shows private and public events to all users" do
+ user.update_column(:include_private_contributions, true)
+ create_event(private_project, today)
+ create_event(public_project, today)
- expect(calendar.activity_dates[today]).to eq(0)
- expect(calendar(user).activity_dates[today]).to eq(0)
- expect(calendar(contributor).activity_dates[today]).to eq(2)
+ expect(calendar.activity_dates[today]).to eq(1)
+ expect(calendar(user).activity_dates[today]).to eq(1)
+ expect(calendar(contributor).activity_dates[today]).to eq(2)
+ end
end
it "counts the diff notes on merge request" do
@@ -128,7 +131,7 @@ describe Gitlab::ContributionsCalendar do
e3 = create_event(feature_project, today)
create_event(public_project, last_week)
- expect(calendar.events_by_date(today)).to contain_exactly(e1)
+ expect(calendar.events_by_date(today)).to contain_exactly(e1, e3)
expect(calendar(contributor).events_by_date(today)).to contain_exactly(e1, e2, e3)
end
diff --git a/spec/lib/gitlab/cross_project_access/check_info_spec.rb b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
index bc9dbf2bece..239fa364f5e 100644
--- a/spec/lib/gitlab/cross_project_access/check_info_spec.rb
+++ b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
@@ -50,7 +50,7 @@ describe Gitlab::CrossProjectAccess::CheckInfo do
expect(info.should_run?(dummy_controller)).to be_truthy
end
- it 'returns the the oposite of #should_skip? when the check is a skip' do
+ it 'returns the the opposite of #should_skip? when the check is a skip' do
info = described_class.new({}, nil, nil, true)
expect(info).to receive(:should_skip?).with(dummy_controller).and_return(false)
@@ -101,7 +101,7 @@ describe Gitlab::CrossProjectAccess::CheckInfo do
expect(info.should_skip?(dummy_controller)).to be_truthy
end
- it 'returns the the oposite of #should_run? when the check is not a skip' do
+ it 'returns the the opposite of #should_run? when the check is not a skip' do
info = described_class.new({}, nil, nil, false)
expect(info).to receive(:should_run?).with(dummy_controller).and_return(false)
diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
index 2e67c1c7f78..f8009709ce2 100644
--- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
@@ -44,15 +44,15 @@ describe Gitlab::CycleAnalytics::StageSummary do
describe "#deploys" do
it "finds the number of deploys made created after the 'from date'" do
- Timecop.freeze(5.days.ago) { create(:deployment, project: project) }
- Timecop.freeze(5.days.from_now) { create(:deployment, project: project) }
+ Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project) }
+ Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project) }
expect(subject.third[:value]).to eq(1)
end
it "doesn't find commits from other projects" do
Timecop.freeze(5.days.from_now) do
- create(:deployment, project: create(:project, :repository))
+ create(:deployment, :success, project: create(:project, :repository))
end
expect(subject.third[:value]).to eq(0)
diff --git a/spec/lib/gitlab/data_builder/build_spec.rb b/spec/lib/gitlab/data_builder/build_spec.rb
index ee91decafad..14fe196a986 100644
--- a/spec/lib/gitlab/data_builder/build_spec.rb
+++ b/spec/lib/gitlab/data_builder/build_spec.rb
@@ -15,6 +15,7 @@ describe Gitlab::DataBuilder::Build do
it { expect(data[:build_id]).to eq(build.id) }
it { expect(data[:build_status]).to eq(build.status) }
it { expect(data[:build_allow_failure]).to eq(false) }
+ it { expect(data[:build_failure_reason]).to eq(build.failure_reason) }
it { expect(data[:project_id]).to eq(build.project.id) }
it { expect(data[:project_name]).to eq(build.project.full_name) }
diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb
index 9ca960502c8..98f1696badb 100644
--- a/spec/lib/gitlab/data_builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb
@@ -6,10 +6,10 @@ describe Gitlab::DataBuilder::Pipeline do
let(:pipeline) do
create(:ci_pipeline,
- project: project,
- status: 'success',
- sha: project.commit.sha,
- ref: project.default_branch)
+ project: project,
+ status: 'success',
+ sha: project.commit.sha,
+ ref: project.default_branch)
end
let!(:build) { create(:ci_build, pipeline: pipeline) }
@@ -20,18 +20,35 @@ describe Gitlab::DataBuilder::Pipeline do
let(:build_data) { data[:builds].first }
let(:project_data) { data[:project] }
- it { expect(attributes).to be_a(Hash) }
- it { expect(attributes[:ref]).to eq(pipeline.ref) }
- it { expect(attributes[:sha]).to eq(pipeline.sha) }
- it { expect(attributes[:tag]).to eq(pipeline.tag) }
- it { expect(attributes[:id]).to eq(pipeline.id) }
- it { expect(attributes[:status]).to eq(pipeline.status) }
- it { expect(attributes[:detailed_status]).to eq('passed') }
+ it 'has correct attributes' do
+ expect(attributes).to be_a(Hash)
+ expect(attributes[:ref]).to eq(pipeline.ref)
+ expect(attributes[:sha]).to eq(pipeline.sha)
+ expect(attributes[:tag]).to eq(pipeline.tag)
+ expect(attributes[:id]).to eq(pipeline.id)
+ expect(attributes[:status]).to eq(pipeline.status)
+ expect(attributes[:detailed_status]).to eq('passed')
+ expect(build_data).to be_a(Hash)
+ expect(build_data[:id]).to eq(build.id)
+ expect(build_data[:status]).to eq(build.status)
+ expect(project_data).to eq(project.hook_attrs(backward: false))
+ end
- it { expect(build_data).to be_a(Hash) }
- it { expect(build_data[:id]).to eq(build.id) }
- it { expect(build_data[:status]).to eq(build.status) }
+ context 'pipeline without variables' do
+ it 'has empty variables hash' do
+ expect(attributes[:variables]).to be_a(Array)
+ expect(attributes[:variables]).to be_empty()
+ end
+ end
- it { expect(project_data).to eq(project.hook_attrs(backward: false)) }
+ context 'pipeline with variables' do
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:data) { described_class.build(pipeline) }
+ let(:attributes) { data[:object_attributes] }
+ let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline, key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1') }
+
+ it { expect(attributes[:variables]).to be_a(Array) }
+ it { expect(attributes[:variables]).to contain_exactly({ key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1' }) }
+ end
end
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index eb7148ff108..23f27939dd2 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -48,10 +48,10 @@ describe Gitlab::Database::MigrationHelpers do
allow(model).to receive(:transaction_open?).and_return(false)
end
- context 'using PostgreSQL' do
+ context 'using PostgreSQL', :postgresql do
before do
allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
- allow(model).to receive(:disable_statement_timeout)
+ allow(model).to receive(:disable_statement_timeout).and_call_original
end
it 'creates the index concurrently' do
@@ -114,12 +114,12 @@ describe Gitlab::Database::MigrationHelpers do
before do
allow(model).to receive(:transaction_open?).and_return(false)
allow(model).to receive(:index_exists?).and_return(true)
+ allow(model).to receive(:disable_statement_timeout).and_call_original
end
context 'using PostgreSQL' do
before do
allow(model).to receive(:supports_drop_index_concurrently?).and_return(true)
- allow(model).to receive(:disable_statement_timeout)
end
describe 'by column name' do
@@ -162,7 +162,7 @@ describe Gitlab::Database::MigrationHelpers do
context 'using MySQL' do
it 'removes an index' do
- expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(false).twice
expect(model).to receive(:remove_index)
.with(:users, { column: :foo })
@@ -224,21 +224,26 @@ describe Gitlab::Database::MigrationHelpers do
context 'using PostgreSQL' do
before do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
allow(Gitlab::Database).to receive(:mysql?).and_return(false)
end
it 'creates a concurrent foreign key and validates it' do
- expect(model).to receive(:disable_statement_timeout)
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
end
it 'appends a valid ON DELETE statement' do
- expect(model).to receive(:disable_statement_timeout)
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/ON DELETE SET NULL/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
model.add_concurrent_foreign_key(:projects, :users,
column: :user_id,
@@ -291,13 +296,68 @@ describe Gitlab::Database::MigrationHelpers do
describe '#disable_statement_timeout' do
context 'using PostgreSQL' do
- it 'disables statement timeouts' do
+ it 'disables statement timeouts to current transaction only' do
expect(Gitlab::Database).to receive(:postgresql?).and_return(true)
- expect(model).to receive(:execute).with('SET statement_timeout TO 0')
+ expect(model).to receive(:execute).with('SET LOCAL statement_timeout TO 0')
model.disable_statement_timeout
end
+
+ # this specs runs without an enclosing transaction (:delete truncation method for db_cleaner)
+ context 'with real environment', :postgresql, :delete do
+ before do
+ model.execute("SET statement_timeout TO '20000'")
+ end
+
+ after do
+ model.execute('RESET ALL')
+ end
+
+ it 'defines statement to 0 only for current transaction' do
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('20s')
+
+ model.connection.transaction do
+ model.disable_statement_timeout
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('0')
+ end
+
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('20s')
+ end
+ end
+
+ context 'when passing a blocks' do
+ it 'disables statement timeouts on session level and executes the block' do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(true)
+ expect(model).to receive(:execute).with('SET statement_timeout TO 0')
+ expect(model).to receive(:execute).with('RESET ALL')
+
+ expect { |block| model.disable_statement_timeout(&block) }.to yield_control
+ end
+
+ # this specs runs without an enclosing transaction (:delete truncation method for db_cleaner)
+ context 'with real environment', :postgresql, :delete do
+ before do
+ model.execute("SET statement_timeout TO '20000'")
+ end
+
+ after do
+ model.execute('RESET ALL')
+ end
+
+ it 'defines statement to 0 for any code run inside the block' do
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('20s')
+
+ model.disable_statement_timeout do
+ model.connection.transaction do
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('0')
+ end
+
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('0')
+ end
+ end
+ end
+ end
end
context 'using MySQL' do
@@ -308,6 +368,16 @@ describe Gitlab::Database::MigrationHelpers do
model.disable_statement_timeout
end
+
+ context 'when passing a blocks' do
+ it 'executes the block of code' do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
+
+ expect(model).not_to receive(:execute)
+
+ expect { |block| model.disable_statement_timeout(&block) }.to yield_control
+ end
+ end
end
end
@@ -1268,7 +1338,12 @@ describe Gitlab::Database::MigrationHelpers do
end
describe '#index_exists_by_name?' do
- it 'returns true if an index exists' do
+ # TODO: remove rails5-only after removing rails4 tests
+ # rails 4 can not handle multiple indexes on the same column set if
+ # index was added by 't.index' - t.index is used by default in schema.rb in
+ # rails 5. Let's run this test only in rails 5 env:
+ # see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21492#note_113602758
+ it 'returns true if an index exists', :rails5 do
expect(model.index_exists_by_name?(:projects, 'index_projects_on_path'))
.to be_truthy
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
index cc7cb3f23fd..248cca25a2c 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
@@ -20,7 +20,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :delete
end
describe "#remove_last_ocurrence" do
- it "removes only the last occurance of a string" do
+ it "removes only the last occurrence of a string" do
input = "this/is/a-word-to-replace/namespace/with/a-word-to-replace"
expect(subject.remove_last_occurrence(input, "a-word-to-replace"))
diff --git a/spec/lib/gitlab/database/subquery_spec.rb b/spec/lib/gitlab/database/subquery_spec.rb
new file mode 100644
index 00000000000..70380e02f16
--- /dev/null
+++ b/spec/lib/gitlab/database/subquery_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Subquery do
+ describe '.self_join' do
+ set(:project) { create(:project) }
+
+ it 'allows you to delete_all rows with WHERE and LIMIT' do
+ events = create_list(:event, 8, project: project)
+
+ expect do
+ described_class.self_join(Event.where('id < ?', events[5]).recent.limit(2)).delete_all
+ end.to change { Event.count }.by(-2)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/file_collection/commit_spec.rb b/spec/lib/gitlab/diff/file_collection/commit_spec.rb
new file mode 100644
index 00000000000..6d1b66deb6a
--- /dev/null
+++ b/spec/lib/gitlab/diff/file_collection/commit_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::FileCollection::Commit do
+ let(:project) { create(:project, :repository) }
+
+ it_behaves_like 'diff statistics' do
+ let(:collection_default_args) do
+ { diff_options: {} }
+ end
+ let(:diffable) { project.commit }
+ let(:stub_path) { 'bar/branch-test.txt' }
+ end
+end
diff --git a/spec/lib/gitlab/diff/file_collection/compare_spec.rb b/spec/lib/gitlab/diff/file_collection/compare_spec.rb
new file mode 100644
index 00000000000..f330f299ac1
--- /dev/null
+++ b/spec/lib/gitlab/diff/file_collection/compare_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::FileCollection::Compare do
+ include RepoHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.commit }
+ let(:start_commit) { sample_image_commit }
+ let(:head_commit) { sample_commit }
+ let(:raw_compare) do
+ Gitlab::Git::Compare.new(project.repository.raw_repository,
+ start_commit.id,
+ head_commit.id)
+ end
+
+ it_behaves_like 'diff statistics' do
+ let(:collection_default_args) do
+ {
+ project: diffable.project,
+ diff_options: {},
+ diff_refs: diffable.diff_refs
+ }
+ end
+ let(:diffable) { Compare.new(raw_compare, project) }
+ let(:stub_path) { '.gitignore' }
+ end
+end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index 79287021981..4578da70bfc 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -29,6 +29,14 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
expect(mr_diff.cache_key).not_to eq(key)
end
+ it_behaves_like 'diff statistics' do
+ let(:collection_default_args) do
+ { diff_options: {} }
+ end
+ let(:diffable) { merge_request.merge_request_diff }
+ let(:stub_path) { '.gitignore' }
+ end
+
shared_examples 'initializes a DiffCollection' do
it 'returns a valid instance of a DiffCollection' do
expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index ebeb05d6e02..3417896e259 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -41,6 +41,52 @@ describe Gitlab::Diff::File do
end
end
+ describe '#unfold_diff_lines' do
+ let(:unfolded_lines) { double('expanded-lines') }
+ let(:unfolder) { instance_double(Gitlab::Diff::LinesUnfolder) }
+ let(:position) { instance_double(Gitlab::Diff::Position, old_line: 10) }
+
+ before do
+ allow(Gitlab::Diff::LinesUnfolder).to receive(:new) { unfolder }
+ end
+
+ context 'when unfold required' do
+ before do
+ allow(unfolder).to receive(:unfold_required?) { true }
+ allow(unfolder).to receive(:unfolded_diff_lines) { unfolded_lines }
+ end
+
+ it 'changes @unfolded to true' do
+ diff_file.unfold_diff_lines(position)
+
+ expect(diff_file).to be_unfolded
+ end
+
+ it 'updates @diff_lines' do
+ diff_file.unfold_diff_lines(position)
+
+ expect(diff_file.diff_lines).to eq(unfolded_lines)
+ end
+ end
+
+ context 'when unfold not required' do
+ before do
+ allow(unfolder).to receive(:unfold_required?) { false }
+ end
+
+ it 'keeps @unfolded false' do
+ diff_file.unfold_diff_lines(position)
+
+ expect(diff_file).not_to be_unfolded
+ end
+
+ it 'does not update @diff_lines' do
+ expect { diff_file.unfold_diff_lines(position) }
+ .not_to change(diff_file, :diff_lines)
+ end
+ end
+ end
+
describe '#mode_changed?' do
it { expect(diff_file.mode_changed?).to be_falsey }
end
@@ -186,6 +232,70 @@ describe Gitlab::Diff::File do
end
end
+ context 'diff file stats' do
+ let(:diff_file) do
+ described_class.new(diff,
+ diff_refs: commit.diff_refs,
+ repository: project.repository,
+ stats: stats)
+ end
+
+ let(:raw_diff) do
+ <<~EOS
+ --- a/files/ruby/popen.rb
+ +++ b/files/ruby/popen.rb
+ @@ -6,12 +6,18 @@ module Popen
+
+ def popen(cmd, path=nil)
+ unless cmd.is_a?(Array)
+ - raise "System commands must be given as an array of strings"
+ + raise RuntimeError, "System commands must be given as an array of strings"
+ + # foobar
+ end
+ EOS
+ end
+
+ describe '#added_lines' do
+ context 'when stats argument given' do
+ let(:stats) { double(Gitaly::DiffStats, additions: 10, deletions: 15) }
+
+ it 'returns added lines from stats' do
+ expect(diff_file.added_lines).to eq(stats.additions)
+ end
+ end
+
+ context 'when stats argument not given' do
+ let(:stats) { nil }
+
+ it 'returns added lines by parsing raw diff' do
+ allow(diff_file).to receive(:raw_diff) { raw_diff }
+
+ expect(diff_file.added_lines).to eq(2)
+ end
+ end
+ end
+
+ describe '#removed_lines' do
+ context 'when stats argument given' do
+ let(:stats) { double(Gitaly::DiffStats, additions: 10, deletions: 15) }
+
+ it 'returns removed lines from stats' do
+ expect(diff_file.removed_lines).to eq(stats.deletions)
+ end
+ end
+
+ context 'when stats argument not given' do
+ let(:stats) { nil }
+
+ it 'returns removed lines by parsing raw diff' do
+ allow(diff_file).to receive(:raw_diff) { raw_diff }
+
+ expect(diff_file.removed_lines).to eq(1)
+ end
+ end
+ end
+ end
+
describe '#simple_viewer' do
context 'when the file is not diffable' do
before do
diff --git a/spec/lib/gitlab/diff/highlight_cache_spec.rb b/spec/lib/gitlab/diff/highlight_cache_spec.rb
new file mode 100644
index 00000000000..bfcfed4231f
--- /dev/null
+++ b/spec/lib/gitlab/diff/highlight_cache_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::HighlightCache do
+ let(:merge_request) { create(:merge_request_with_diffs) }
+
+ subject(:cache) { described_class.new(merge_request.diffs, backend: backend) }
+
+ describe '#decorate' do
+ let(:backend) { double('backend').as_null_object }
+
+ # Manually creates a Diff::File object to avoid triggering the cache on
+ # the FileCollection::MergeRequestDiff
+ let(:diff_file) do
+ diffs = merge_request.diffs
+ raw_diff = diffs.diffable.raw_diffs(diffs.diff_options.merge(paths: ['CHANGELOG'])).first
+ Gitlab::Diff::File.new(raw_diff,
+ repository: diffs.project.repository,
+ diff_refs: diffs.diff_refs,
+ fallback_diff_refs: diffs.fallback_diff_refs)
+ end
+
+ it 'does not calculate highlighting when reading from cache' do
+ cache.write_if_empty
+ cache.decorate(diff_file)
+
+ expect_any_instance_of(Gitlab::Diff::Highlight).not_to receive(:highlight)
+
+ diff_file.highlighted_diff_lines
+ end
+
+ it 'assigns highlighted diff lines to the DiffFile' do
+ cache.write_if_empty
+ cache.decorate(diff_file)
+
+ expect(diff_file.highlighted_diff_lines.size).to be > 5
+ end
+
+ it 'submits a single reading from the cache' do
+ cache.decorate(diff_file)
+ cache.decorate(diff_file)
+
+ expect(backend).to have_received(:read).with(cache.key).once
+ end
+ end
+
+ describe '#write_if_empty' do
+ let(:backend) { double('backend', read: {}).as_null_object }
+
+ it 'submits a single writing to the cache' do
+ cache.write_if_empty
+ cache.write_if_empty
+
+ expect(backend).to have_received(:write).with(cache.key,
+ hash_including('CHANGELOG-false-false-false'),
+ expires_in: 1.week).once
+ end
+ end
+
+ describe '#clear' do
+ let(:backend) { double('backend').as_null_object }
+
+ it 'clears cache' do
+ cache.clear
+
+ expect(backend).to have_received(:delete).with(cache.key)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb
index 7c9e8c8d04e..5d0a603d11d 100644
--- a/spec/lib/gitlab/diff/highlight_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_spec.rb
@@ -8,6 +8,20 @@ describe Gitlab::Diff::Highlight do
let(:diff) { commit.raw_diffs.first }
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: project.repository) }
+ shared_examples 'without inline diffs' do
+ let(:code) { '<h2 onmouseover="alert(2)">Test</h2>' }
+
+ before do
+ allow(Gitlab::Diff::InlineDiff).to receive(:for_lines).and_return([])
+ allow_any_instance_of(Gitlab::Diff::Line).to receive(:text).and_return(code)
+ end
+
+ it 'returns html escaped diff text' do
+ expect(subject[1].rich_text).to eq html_escape(code)
+ expect(subject[1].rich_text).to be_html_safe
+ end
+ end
+
describe '#highlight' do
context "with a diff file" do
let(:subject) { described_class.new(diff_file, repository: project.repository).highlight }
@@ -24,19 +38,29 @@ describe Gitlab::Diff::Highlight do
it 'highlights and marks unchanged lines' do
code = %Q{ <span id="LC7" class="line" lang="ruby"> <span class="k">def</span> <span class="nf">popen</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span></span>\n}
- expect(subject[2].text).to eq(code)
+ expect(subject[2].rich_text).to eq(code)
end
it 'highlights and marks removed lines' do
code = %Q{-<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>\n}
- expect(subject[4].text).to eq(code)
+ expect(subject[4].rich_text).to eq(code)
end
it 'highlights and marks added lines' do
code = %Q{+<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="no"><span class="idiff left">RuntimeError</span></span><span class="p"><span class="idiff">,</span></span><span class="idiff right"> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n}
- expect(subject[5].text).to eq(code)
+ expect(subject[5].rich_text).to eq(code)
+ end
+
+ context 'when no diff_refs' do
+ before do
+ allow(diff_file).to receive(:diff_refs).and_return(nil)
+ end
+
+ context 'when no inline diffs' do
+ it_behaves_like 'without inline diffs'
+ end
end
end
@@ -69,8 +93,8 @@ describe Gitlab::Diff::Highlight do
it 'marks added lines' do
code = %q{+ raise <span class="idiff left right">RuntimeError, </span>&quot;System commands must be given as an array of strings&quot;}
- expect(subject[5].text).to eq(code)
- expect(subject[5].text).to be_html_safe
+ expect(subject[5].rich_text).to eq(code)
+ expect(subject[5].rich_text).to be_html_safe
end
context 'when the inline diff marker has an invalid range' do
@@ -93,6 +117,10 @@ describe Gitlab::Diff::Highlight do
expect { subject }. to raise_exception(RangeError)
end
end
+
+ context 'when no inline diffs' do
+ it_behaves_like 'without inline diffs'
+ end
end
end
end
diff --git a/spec/lib/gitlab/diff/line_spec.rb b/spec/lib/gitlab/diff/line_spec.rb
new file mode 100644
index 00000000000..76d411973c7
--- /dev/null
+++ b/spec/lib/gitlab/diff/line_spec.rb
@@ -0,0 +1,22 @@
+describe Gitlab::Diff::Line do
+ describe '.init_from_hash' do
+ it 'round-trips correctly with to_hash' do
+ line = described_class.new('<input>', 'match', 0, 0, 1,
+ parent_file: double(:file),
+ line_code: double(:line_code),
+ rich_text: '&lt;input&gt;')
+
+ expect(described_class.init_from_hash(line.to_hash).to_hash)
+ .to eq(line.to_hash)
+ end
+ end
+
+ context "when setting rich text" do
+ it 'escapes any HTML special characters in the diff chunk header' do
+ subject = described_class.new("<input>", "", 0, 0, 0)
+ line = subject.as_json
+
+ expect(line[:rich_text]).to eq("&lt;input&gt;")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/lines_unfolder_spec.rb b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
new file mode 100644
index 00000000000..8e00c8e0e30
--- /dev/null
+++ b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
@@ -0,0 +1,750 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::LinesUnfolder do
+ let(:raw_diff) do
+ <<-DIFF.strip_heredoc
+ @@ -7,9 +7,6 @@
+ "tags": ["devel", "development", "nightly"],
+ "desktop-file-name-prefix": "(Development) ",
+ "finish-args": [
+ - "--share=ipc", "--socket=x11",
+ - "--socket=wayland",
+ - "--talk-name=org.gnome.OnlineAccounts",
+ "--talk-name=org.freedesktop.Tracker1",
+ "--filesystem=home",
+ "--talk-name=org.gtk.vfs", "--talk-name=org.gtk.vfs.*",
+ @@ -62,7 +59,7 @@
+ },
+ {
+ "name": "gnome-desktop",
+ - "config-opts": ["--disable-debug-tools", "--disable-udev"],
+ + "config-opts": ["--disable-debug-tools", "--disable-"],
+ "sources": [
+ {
+ "type": "git",
+ @@ -83,11 +80,6 @@
+ "buildsystem": "meson",
+ "builddir": true,
+ "name": "nautilus",
+ - "config-opts": [
+ - "-Denable-desktop=false",
+ - "-Denable-selinux=false",
+ - "--libdir=/app/lib"
+ - ],
+ "sources": [
+ {
+ "type": "git",
+ DIFF
+ end
+
+ let(:raw_old_blob) do
+ <<-BLOB.strip_heredoc
+ {
+ "app-id": "org.gnome.Nautilus",
+ "runtime": "org.gnome.Platform",
+ "runtime-version": "master",
+ "sdk": "org.gnome.Sdk",
+ "command": "nautilus",
+ "tags": ["devel", "development", "nightly"],
+ "desktop-file-name-prefix": "(Development) ",
+ "finish-args": [
+ "--share=ipc", "--socket=x11",
+ "--socket=wayland",
+ "--talk-name=org.gnome.OnlineAccounts",
+ "--talk-name=org.freedesktop.Tracker1",
+ "--filesystem=home",
+ "--talk-name=org.gtk.vfs", "--talk-name=org.gtk.vfs.*",
+ "--filesystem=xdg-run/dconf", "--filesystem=~/.config/dconf:ro",
+ "--talk-name=ca.desrt.dconf", "--env=DCONF_USER_CONFIG_DIR=.config/dconf"
+ ],
+ "cleanup": [ "/include", "/share/bash-completion" ],
+ "modules": [
+ {
+ "name": "exiv2",
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://exiv2.org/builds/exiv2-0.26-trunk.tar.gz",
+ "sha256": "c75e3c4a0811bf700d92c82319373b7a825a2331c12b8b37d41eb58e4f18eafb"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "cp -f /usr/share/gnu-config/config.sub ./config/",
+ "cp -f /usr/share/gnu-config/config.guess ./config/"
+ ]
+ }
+ ]
+ },
+ {
+ "name": "gexiv2",
+ "config-opts": [ "--disable-introspection" ],
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://git.gnome.org/browse/gexiv2"
+ }
+ ]
+ },
+ {
+ "name": "tracker",
+ "cleanup": [ "/bin", "/etc", "/libexec" ],
+ "config-opts": [ "--disable-miner-apps", "--disable-static",
+ "--disable-tracker-extract", "--disable-tracker-needle",
+ "--disable-tracker-preferences", "--disable-artwork",
+ "--disable-tracker-writeback", "--disable-miner-user-guides",
+ "--with-bash-completion-dir=no" ],
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://git.gnome.org/browse/tracker"
+ }
+ ]
+ },
+ {
+ "name": "gnome-desktop",
+ "config-opts": ["--disable-debug-tools", "--disable-udev"],
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://git.gnome.org/browse/gnome-desktop"
+ }
+ ]
+ },
+ {
+ "name": "gnome-autoar",
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://git.gnome.org/browse/gnome-autoar"
+ }
+ ]
+ },
+ {
+ "buildsystem": "meson",
+ "builddir": true,
+ "name": "nautilus",
+ "config-opts": [
+ "-Denable-desktop=false",
+ "-Denable-selinux=false",
+ "--libdir=/app/lib"
+ ],
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://gitlab.gnome.org/GNOME/nautilus.git"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "app-id": "foo",
+ "runtime": "foo",
+ "runtime-version": "foo",
+ "sdk": "foo",
+ "command": "foo",
+ "tags": ["foo", "bar", "kux"],
+ "desktop-file-name-prefix": "(Foo) ",
+ {
+ "buildsystem": "meson",
+ "builddir": true,
+ "name": "nautilus",
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://gitlab.gnome.org/GNOME/nautilus.git"
+ }
+ ]
+ }
+ },
+ {
+ "app-id": "foo",
+ "runtime": "foo",
+ "runtime-version": "foo",
+ "sdk": "foo",
+ "command": "foo",
+ "tags": ["foo", "bar", "kux"],
+ "desktop-file-name-prefix": "(Foo) ",
+ {
+ "buildsystem": "meson",
+ "builddir": true,
+ "name": "nautilus",
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://gitlab.gnome.org/GNOME/nautilus.git"
+ }
+ ]
+ }
+ }
+ BLOB
+ end
+
+ let(:project) { create(:project) }
+
+ let(:old_blob) { Gitlab::Git::Blob.new(data: raw_old_blob) }
+
+ let(:diff) do
+ Gitlab::Git::Diff.new(diff: raw_diff,
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ a_mode: "100644",
+ b_mode: "100644",
+ new_file: false,
+ renamed_file: false,
+ deleted_file: false,
+ too_large: false)
+ end
+
+ let(:diff_file) do
+ Gitlab::Diff::File.new(diff, repository: project.repository)
+ end
+
+ before do
+ allow(old_blob).to receive(:load_all_data!)
+ allow(diff_file).to receive(:old_blob) { old_blob }
+ end
+
+ subject { described_class.new(diff_file, position) }
+
+ context 'position requires a middle expansion and new match lines' do
+ let(:position) do
+ Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ head_sha: "1487062132228de836236c522fe52fed4980a46c",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ position_type: "text",
+ old_line: 43,
+ new_line: 40)
+ end
+
+ context 'blob lines' do
+ let(:expected_blob_lines) do
+ [[40, 40, " \"config-opts\": [ \"--disable-introspection\" ],"],
+ [41, 41, " \"sources\": ["],
+ [42, 42, " {"],
+ [43, 43, " \"type\": \"git\","],
+ [44, 44, " \"url\": \"https://git.gnome.org/browse/gexiv2\""],
+ [45, 45, " }"],
+ [46, 46, " ]"]]
+ end
+
+ it 'returns the extracted blob lines correctly' do
+ extracted_lines = subject.blob_lines
+
+ expect(extracted_lines.size).to eq(7)
+
+ extracted_lines.each_with_index do |line, i|
+ expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i])
+ end
+ end
+ end
+
+ context 'diff lines' do
+ let(:expected_diff_lines) do
+ [[7, 7, "@@ -7,9 +7,6 @@"],
+ [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"],
+ [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","],
+ [9, 9, " \"finish-args\": ["],
+ [10, 10, "- \"--share=ipc\", \"--socket=x11\","],
+ [11, 10, "- \"--socket=wayland\","],
+ [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","],
+ [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","],
+ [14, 11, " \"--filesystem=home\","],
+ [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","],
+
+ # New match line
+ [40, 37, "@@ -40,7+37,7 @@"],
+
+ # Injected blob lines
+ [40, 37, " \"config-opts\": [ \"--disable-introspection\" ],"],
+ [41, 38, " \"sources\": ["],
+ [42, 39, " {"],
+ [43, 40, " \"type\": \"git\","], # comment
+ [44, 41, " \"url\": \"https://git.gnome.org/browse/gexiv2\""],
+ [45, 42, " }"],
+ [46, 43, " ]"],
+ # end
+
+ # Second match line
+ [62, 59, "@@ -62,7+59,7 @@"],
+
+ [62, 59, " },"],
+ [63, 60, " {"],
+ [64, 61, " \"name\": \"gnome-desktop\","],
+ [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"],
+ [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"],
+ [66, 63, " \"sources\": ["],
+ [67, 64, " {"],
+ [68, 65, " \"type\": \"git\","],
+ [83, 80, "@@ -83,11 +80,6 @@"],
+ [83, 80, " \"buildsystem\": \"meson\","],
+ [84, 81, " \"builddir\": true,"],
+ [85, 82, " \"name\": \"nautilus\","],
+ [86, 83, "- \"config-opts\": ["],
+ [87, 83, "- \"-Denable-desktop=false\","],
+ [88, 83, "- \"-Denable-selinux=false\","],
+ [89, 83, "- \"--libdir=/app/lib\""],
+ [90, 83, "- ],"],
+ [91, 83, " \"sources\": ["],
+ [92, 84, " {"],
+ [93, 85, " \"type\": \"git\","]]
+ end
+
+ it 'return merge of blob lines with diff lines correctly' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ expected_diff_lines.each_with_index do |expected_line, i|
+ line = new_diff_lines[i]
+
+ expect([line.old_pos, line.new_pos, line.text])
+ .to eq([expected_line[0], expected_line[1], expected_line[2]])
+ end
+ end
+
+ it 'merged lines have correct line codes' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ new_diff_lines.each_with_index do |line, i|
+ old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1]
+
+ unless line.type == 'match'
+ expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos))
+ end
+ end
+ end
+ end
+ end
+
+ context 'position requires a middle expansion and no top match line' do
+ let(:position) do
+ Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ head_sha: "1487062132228de836236c522fe52fed4980a46c",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ position_type: "text",
+ old_line: 16,
+ new_line: 17)
+ end
+
+ context 'blob lines' do
+ let(:expected_blob_lines) do
+ [[16, 16, " \"--filesystem=xdg-run/dconf\", \"--filesystem=~/.config/dconf:ro\","],
+ [17, 17, " \"--talk-name=ca.desrt.dconf\", \"--env=DCONF_USER_CONFIG_DIR=.config/dconf\""],
+ [18, 18, " ],"],
+ [19, 19, " \"cleanup\": [ \"/include\", \"/share/bash-completion\" ],"]]
+ end
+
+ it 'returns the extracted blob lines correctly' do
+ extracted_lines = subject.blob_lines
+
+ expect(extracted_lines.size).to eq(4)
+
+ extracted_lines.each_with_index do |line, i|
+ expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i])
+ end
+ end
+ end
+
+ context 'diff lines' do
+ let(:expected_diff_lines) do
+ [[7, 7, "@@ -7,9 +7,6 @@"],
+ [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"],
+ [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","],
+ [9, 9, " \"finish-args\": ["],
+ [10, 10, "- \"--share=ipc\", \"--socket=x11\","],
+ [11, 10, "- \"--socket=wayland\","],
+ [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","],
+ [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","],
+ [14, 11, " \"--filesystem=home\","],
+ [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","],
+ # No new match needed
+
+ # Injected blob lines
+ [16, 13, " \"--filesystem=xdg-run/dconf\", \"--filesystem=~/.config/dconf:ro\","],
+ [17, 14, " \"--talk-name=ca.desrt.dconf\", \"--env=DCONF_USER_CONFIG_DIR=.config/dconf\""],
+ [18, 15, " ],"],
+ [19, 16, " \"cleanup\": [ \"/include\", \"/share/bash-completion\" ],"],
+ # end
+
+ # Second match line
+ [62, 59, "@@ -62,4+59,4 @@"],
+
+ [62, 59, " },"],
+ [63, 60, " {"],
+ [64, 61, " \"name\": \"gnome-desktop\","],
+ [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"],
+ [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"],
+ [66, 63, " \"sources\": ["],
+ [67, 64, " {"],
+ [68, 65, " \"type\": \"git\","],
+ [83, 80, "@@ -83,11 +80,6 @@"],
+ [83, 80, " \"buildsystem\": \"meson\","],
+ [84, 81, " \"builddir\": true,"],
+ [85, 82, " \"name\": \"nautilus\","],
+ [86, 83, "- \"config-opts\": ["],
+ [87, 83, "- \"-Denable-desktop=false\","],
+ [88, 83, "- \"-Denable-selinux=false\","],
+ [89, 83, "- \"--libdir=/app/lib\""],
+ [90, 83, "- ],"],
+ [91, 83, " \"sources\": ["],
+ [92, 84, " {"],
+ [93, 85, " \"type\": \"git\","]]
+ end
+
+ it 'return merge of blob lines with diff lines correctly' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ expected_diff_lines.each_with_index do |expected_line, i|
+ line = new_diff_lines[i]
+
+ expect([line.old_pos, line.new_pos, line.text])
+ .to eq([expected_line[0], expected_line[1], expected_line[2]])
+ end
+ end
+
+ it 'merged lines have correct line codes' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ new_diff_lines.each_with_index do |line, i|
+ old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1]
+
+ unless line.type == 'match'
+ expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos))
+ end
+ end
+ end
+ end
+ end
+
+ context 'position requires a middle expansion and no bottom match line' do
+ let(:position) do
+ Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ head_sha: "1487062132228de836236c522fe52fed4980a46c",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ position_type: "text",
+ old_line: 82,
+ new_line: 79)
+ end
+
+ context 'blob lines' do
+ let(:expected_blob_lines) do
+ [[79, 79, " }"],
+ [80, 80, " ]"],
+ [81, 81, " },"],
+ [82, 82, " {"]]
+ end
+
+ it 'returns the extracted blob lines correctly' do
+ extracted_lines = subject.blob_lines
+
+ expect(extracted_lines.size).to eq(4)
+
+ extracted_lines.each_with_index do |line, i|
+ expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i])
+ end
+ end
+ end
+
+ context 'diff lines' do
+ let(:expected_diff_lines) do
+ [[7, 7, "@@ -7,9 +7,6 @@"],
+ [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"],
+ [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","],
+ [9, 9, " \"finish-args\": ["],
+ [10, 10, "- \"--share=ipc\", \"--socket=x11\","],
+ [11, 10, "- \"--socket=wayland\","],
+ [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","],
+ [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","],
+ [14, 11, " \"--filesystem=home\","],
+ [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","],
+ [62, 59, "@@ -62,7 +59,7 @@"],
+ [62, 59, " },"],
+ [63, 60, " {"],
+ [64, 61, " \"name\": \"gnome-desktop\","],
+ [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"],
+ [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"],
+ [66, 63, " \"sources\": ["],
+ [67, 64, " {"],
+ [68, 65, " \"type\": \"git\","],
+
+ # New top match line
+ [79, 76, "@@ -79,4+76,4 @@"],
+
+ # Injected blob lines
+ [79, 76, " }"],
+ [80, 77, " ]"],
+ [81, 78, " },"],
+ [82, 79, " {"],
+ # end
+
+ # No new second match line
+ [83, 80, " \"buildsystem\": \"meson\","],
+ [84, 81, " \"builddir\": true,"],
+ [85, 82, " \"name\": \"nautilus\","],
+ [86, 83, "- \"config-opts\": ["],
+ [87, 83, "- \"-Denable-desktop=false\","],
+ [88, 83, "- \"-Denable-selinux=false\","],
+ [89, 83, "- \"--libdir=/app/lib\""],
+ [90, 83, "- ],"],
+ [91, 83, " \"sources\": ["],
+ [92, 84, " {"],
+ [93, 85, " \"type\": \"git\","]]
+ end
+
+ it 'return merge of blob lines with diff lines correctly' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ expected_diff_lines.each_with_index do |expected_line, i|
+ line = new_diff_lines[i]
+
+ expect([line.old_pos, line.new_pos, line.text])
+ .to eq([expected_line[0], expected_line[1], expected_line[2]])
+ end
+ end
+
+ it 'merged lines have correct line codes' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ new_diff_lines.each_with_index do |line, i|
+ old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1]
+
+ unless line.type == 'match'
+ expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos))
+ end
+ end
+ end
+ end
+ end
+
+ context 'position requires a short top expansion' do
+ let(:position) do
+ Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ head_sha: "1487062132228de836236c522fe52fed4980a46c",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ position_type: "text",
+ old_line: 6,
+ new_line: 6)
+ end
+
+ context 'blob lines' do
+ let(:expected_blob_lines) do
+ [[3, 3, " \"runtime\": \"org.gnome.Platform\","],
+ [4, 4, " \"runtime-version\": \"master\","],
+ [5, 5, " \"sdk\": \"org.gnome.Sdk\","],
+ [6, 6, " \"command\": \"nautilus\","]]
+ end
+
+ it 'returns the extracted blob lines correctly' do
+ extracted_lines = subject.blob_lines
+
+ expect(extracted_lines.size).to eq(4)
+
+ extracted_lines.each_with_index do |line, i|
+ expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i])
+ end
+ end
+ end
+
+ context 'diff lines' do
+ let(:expected_diff_lines) do
+ # New match line
+ [[3, 3, "@@ -3,4+3,4 @@"],
+
+ # Injected blob lines
+ [3, 3, " \"runtime\": \"org.gnome.Platform\","],
+ [4, 4, " \"runtime-version\": \"master\","],
+ [5, 5, " \"sdk\": \"org.gnome.Sdk\","],
+ [6, 6, " \"command\": \"nautilus\","],
+ # end
+ [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"],
+ [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","],
+ [9, 9, " \"finish-args\": ["],
+ [10, 10, "- \"--share=ipc\", \"--socket=x11\","],
+ [11, 10, "- \"--socket=wayland\","],
+ [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","],
+ [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","],
+ [14, 11, " \"--filesystem=home\","],
+ [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","],
+ [62, 59, "@@ -62,7 +59,7 @@"],
+ [62, 59, " },"],
+ [63, 60, " {"],
+ [64, 61, " \"name\": \"gnome-desktop\","],
+ [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"],
+ [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"],
+ [66, 63, " \"sources\": ["],
+ [67, 64, " {"],
+ [68, 65, " \"type\": \"git\","],
+ [83, 80, "@@ -83,11 +80,6 @@"],
+ [83, 80, " \"buildsystem\": \"meson\","],
+ [84, 81, " \"builddir\": true,"],
+ [85, 82, " \"name\": \"nautilus\","],
+ [86, 83, "- \"config-opts\": ["],
+ [87, 83, "- \"-Denable-desktop=false\","],
+ [88, 83, "- \"-Denable-selinux=false\","],
+ [89, 83, "- \"--libdir=/app/lib\""],
+ [90, 83, "- ],"],
+ [91, 83, " \"sources\": ["],
+ [92, 84, " {"],
+ [93, 85, " \"type\": \"git\","]]
+ end
+
+ it 'return merge of blob lines with diff lines correctly' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ expected_diff_lines.each_with_index do |expected_line, i|
+ line = new_diff_lines[i]
+
+ expect([line.old_pos, line.new_pos, line.text])
+ .to eq([expected_line[0], expected_line[1], expected_line[2]])
+ end
+ end
+
+ it 'merged lines have correct line codes' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ new_diff_lines.each_with_index do |line, i|
+ old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1]
+
+ unless line.type == 'match'
+ expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos))
+ end
+ end
+ end
+ end
+ end
+
+ context 'position sits between two match lines (no expasion needed)' do
+ let(:position) do
+ Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ head_sha: "1487062132228de836236c522fe52fed4980a46c",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ position_type: "text",
+ old_line: 64,
+ new_line: 61)
+ end
+
+ context 'diff lines' do
+ it 'returns nil' do
+ expect(subject.unfolded_diff_lines).to be_nil
+ end
+ end
+ end
+
+ context 'position requires bottom expansion and new match lines' do
+ let(:position) do
+ Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19",
+ head_sha: "1487062132228de836236c522fe52fed4980a46c",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ position_type: "text",
+ old_line: 107,
+ new_line: 99)
+ end
+
+ context 'blob lines' do
+ let(:expected_blob_lines) do
+ [[104, 104, " \"sdk\": \"foo\","],
+ [105, 105, " \"command\": \"foo\","],
+ [106, 106, " \"tags\": [\"foo\", \"bar\", \"kux\"],"],
+ [107, 107, " \"desktop-file-name-prefix\": \"(Foo) \","],
+ [108, 108, " {"],
+ [109, 109, " \"buildsystem\": \"meson\","],
+ [110, 110, " \"builddir\": true,"]]
+ end
+
+ it 'returns the extracted blob lines correctly' do
+ extracted_lines = subject.blob_lines
+
+ expect(extracted_lines.size).to eq(7)
+
+ extracted_lines.each_with_index do |line, i|
+ expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i])
+ end
+ end
+ end
+
+ context 'diff lines' do
+ let(:expected_diff_lines) do
+ [[7, 7, "@@ -7,9 +7,6 @@"],
+ [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"],
+ [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","],
+ [9, 9, " \"finish-args\": ["],
+ [10, 10, "- \"--share=ipc\", \"--socket=x11\","],
+ [11, 10, "- \"--socket=wayland\","],
+ [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","],
+ [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","],
+ [14, 11, " \"--filesystem=home\","],
+ [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","],
+ [62, 59, "@@ -62,7 +59,7 @@"],
+ [62, 59, " },"],
+ [63, 60, " {"],
+ [64, 61, " \"name\": \"gnome-desktop\","],
+ [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"],
+ [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"],
+ [66, 63, " \"sources\": ["],
+ [67, 64, " {"],
+ [68, 65, " \"type\": \"git\","],
+ [83, 80, "@@ -83,11 +80,6 @@"],
+ [83, 80, " \"buildsystem\": \"meson\","],
+ [84, 81, " \"builddir\": true,"],
+ [85, 82, " \"name\": \"nautilus\","],
+ [86, 83, "- \"config-opts\": ["],
+ [87, 83, "- \"-Denable-desktop=false\","],
+ [88, 83, "- \"-Denable-selinux=false\","],
+ [89, 83, "- \"--libdir=/app/lib\""],
+ [90, 83, "- ],"],
+ [91, 83, " \"sources\": ["],
+ [92, 84, " {"],
+ [93, 85, " \"type\": \"git\","],
+ # New match line
+ [104, 96, "@@ -104,7+96,7 @@"],
+
+ # Injected blob lines
+ [104, 96, " \"sdk\": \"foo\","],
+ [105, 97, " \"command\": \"foo\","],
+ [106, 98, " \"tags\": [\"foo\", \"bar\", \"kux\"],"],
+ [107, 99, " \"desktop-file-name-prefix\": \"(Foo) \","],
+ [108, 100, " {"],
+ [109, 101, " \"buildsystem\": \"meson\","],
+ [110, 102, " \"builddir\": true,"]]
+ # end
+ end
+
+ it 'return merge of blob lines with diff lines correctly' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ expected_diff_lines.each_with_index do |expected_line, i|
+ line = new_diff_lines[i]
+
+ expect([line.old_pos, line.new_pos, line.text])
+ .to eq([expected_line[0], expected_line[1], expected_line[2]])
+ end
+ end
+
+ it 'merged lines have correct line codes' do
+ new_diff_lines = subject.unfolded_diff_lines
+
+ new_diff_lines.each_with_index do |line, i|
+ old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1]
+
+ unless line.type == 'match'
+ expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index 677eb373d22..cc4faf6f10b 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -5,6 +5,34 @@ describe Gitlab::Diff::Position do
let(:project) { create(:project, :repository) }
+ let(:args_for_img) do
+ {
+ old_path: "files/any.img",
+ new_path: "files/any.img",
+ base_sha: nil,
+ head_sha: nil,
+ start_sha: nil,
+ width: 100,
+ height: 100,
+ x: 1,
+ y: 100,
+ position_type: "image"
+ }
+ end
+
+ let(:args_for_text) do
+ {
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ base_sha: nil,
+ head_sha: nil,
+ start_sha: nil,
+ position_type: "text"
+ }
+ end
+
describe "position for an added text file" do
let(:commit) { project.commit("2ea1f3dec713d940208fb5ce4a38765ecb5d3f73") }
@@ -529,53 +557,49 @@ describe Gitlab::Diff::Position do
end
end
+ describe "#as_json" do
+ shared_examples "diff position json" do
+ let(:diff_position) { described_class.new(args) }
+
+ it "returns the position as JSON" do
+ expect(diff_position.as_json).to eq(args.stringify_keys)
+ end
+ end
+
+ context "for text position" do
+ let(:args) { args_for_text }
+
+ it_behaves_like "diff position json"
+ end
+
+ context "for image position" do
+ let(:args) { args_for_img }
+
+ it_behaves_like "diff position json"
+ end
+ end
+
describe "#to_json" do
shared_examples "diff position json" do
+ let(:diff_position) { described_class.new(args) }
+
it "returns the position as JSON" do
- expect(JSON.parse(diff_position.to_json)).to eq(hash.stringify_keys)
+ expect(JSON.parse(diff_position.to_json)).to eq(args.stringify_keys)
end
it "works when nested under another hash" do
- expect(JSON.parse(JSON.generate(pos: diff_position))).to eq('pos' => hash.stringify_keys)
+ expect(JSON.parse(JSON.generate(pos: diff_position))).to eq('pos' => args.stringify_keys)
end
end
- context "for text positon" do
- let(:hash) do
- {
- old_path: "files/ruby/popen.rb",
- new_path: "files/ruby/popen.rb",
- old_line: nil,
- new_line: 14,
- base_sha: nil,
- head_sha: nil,
- start_sha: nil,
- position_type: "text"
- }
- end
-
- let(:diff_position) { described_class.new(hash) }
+ context "for text position" do
+ let(:args) { args_for_text }
it_behaves_like "diff position json"
end
- context "for image positon" do
- let(:hash) do
- {
- old_path: "files/any.img",
- new_path: "files/any.img",
- base_sha: nil,
- head_sha: nil,
- start_sha: nil,
- width: 100,
- height: 100,
- x: 1,
- y: 100,
- position_type: "image"
- }
- end
-
- let(:diff_position) { described_class.new(hash) }
+ context "for image position" do
+ let(:args) { args_for_img }
it_behaves_like "diff position json"
end
diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb
index ddc4f6c5b5c..a2eed07ca55 100644
--- a/spec/lib/gitlab/diff/position_tracer_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer_spec.rb
@@ -43,7 +43,7 @@ describe Gitlab::Diff::PositionTracer do
#
# In any case, all of this means that the tests below will be extremely
# (excessively, unjustifiably) thorough for scenarios where "the file was
- # created in the old diff" and then drop off to comparitively lackluster
+ # created in the old diff" and then drop off to comparatively lackluster
# testing of other scenarios.
#
# I did still try to cover most of the obvious and potentially tricky
diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
index 154ab4b3856..1d75e8cb5da 100644
--- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Email::Handler::CreateIssueHandler do
diff --git a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
index 43c6280f251..f276f1a8ddf 100644
--- a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Email::Handler::CreateMergeRequestHandler do
@@ -91,5 +93,74 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
end
end
+
+ context 'when the email contains patch attachments' do
+ let(:email_raw) { fixture_file("emails/valid_merge_request_with_patch.eml") }
+
+ it 'creates the source branch and applies the patches' do
+ receiver.execute
+
+ branch = project.repository.find_branch('new-branch-with-a-patch')
+
+ expect(branch).not_to be_nil
+ expect(branch.dereferenced_target.message).to include('A commit from a patch')
+ end
+
+ it 'creates the merge request' do
+ expect { receiver.execute }
+ .to change { project.merge_requests.where(source_branch: 'new-branch-with-a-patch').size }.by(1)
+ end
+
+ it 'does not mention the patches in the created merge request' do
+ receiver.execute
+
+ merge_request = project.merge_requests.find_by!(source_branch: 'new-branch-with-a-patch')
+
+ expect(merge_request.description).not_to include('0001-A-commit-from-a-patch.patch')
+ end
+
+ context 'when the patch could not be applied' do
+ let(:email_raw) { fixture_file("emails/merge_request_with_conflicting_patch.eml") }
+
+ it 'raises an error' do
+ expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidAttachment)
+ end
+ end
+
+ context 'when specifying the target branch using quick actions' do
+ let(:email_raw) { fixture_file('emails/merge_request_with_patch_and_target_branch.eml') }
+
+ it 'creates the merge request with the correct target branch' do
+ receiver.execute
+
+ merge_request = project.merge_requests.find_by!(source_branch: 'new-branch-with-a-patch')
+
+ expect(merge_request.target_branch).to eq('with-codeowners')
+ end
+
+ it 'based the merge request of the target_branch' do
+ receiver.execute
+
+ merge_request = project.merge_requests.find_by!(source_branch: 'new-branch-with-a-patch')
+
+ expect(merge_request.diff_base_commit).to eq(project.repository.commit('with-codeowners'))
+ end
+ end
+ end
+ end
+
+ describe '#patch_attachments' do
+ let(:email_raw) { fixture_file('emails/merge_request_multiple_patches.eml') }
+ let(:mail) { Mail::Message.new(email_raw) }
+ subject(:handler) { described_class.new(mail, mail_key) }
+
+ it 'orders attachments ending in `.patch` by name' do
+ expected_filenames = ["0001-A-commit-from-a-patch.patch",
+ "0002-This-does-not-apply-to-the-feature-branch.patch"]
+
+ attachments = handler.__send__(:patch_attachments).map(&:filename)
+
+ expect(attachments).to eq(expected_filenames)
+ end
end
end
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index 950a7dd7d6c..b1f48c15c21 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Email::Handler::CreateNoteHandler do
diff --git a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
index ce160e11de2..b8660b133ec 100644
--- a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Email::Handler::UnsubscribeHandler do
diff --git a/spec/lib/gitlab/email/handler_spec.rb b/spec/lib/gitlab/email/handler_spec.rb
index cedbfcc0d18..c651765dc0f 100644
--- a/spec/lib/gitlab/email/handler_spec.rb
+++ b/spec/lib/gitlab/email/handler_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Email::Handler do
@@ -40,7 +42,7 @@ describe Gitlab::Email::Handler do
end
def ce_handlers
- @ce_handlers ||= Gitlab::Email::Handler::HANDLERS.reject do |handler|
+ @ce_handlers ||= Gitlab::Email::Handler.handlers.reject do |handler|
handler.name.start_with?('Gitlab::Email::Handler::EE::')
end
end
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index e68c9850f6b..a5bf2f2b3df 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
require "spec_helper"
describe Gitlab::EncodingHelper do
@@ -187,4 +188,15 @@ describe Gitlab::EncodingHelper do
end
end
end
+
+ describe '#binary_stringio' do
+ it 'does not mutate the original string encoding' do
+ test = 'my-test'
+
+ io_stream = ext_class.binary_stringio(test)
+
+ expect(io_stream.external_encoding.name).to eq('ASCII-8BIT')
+ expect(test.encoding.name).to eq('UTF-8')
+ end
+ end
end
diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb
index 68abcb3520a..49a423191bb 100644
--- a/spec/lib/gitlab/favicon_spec.rb
+++ b/spec/lib/gitlab/favicon_spec.rb
@@ -58,6 +58,7 @@ RSpec.describe Gitlab::Favicon, :request_store do
favicon_status_not_found
favicon_status_pending
favicon_status_running
+ favicon_status_scheduled
favicon_status_skipped
favicon_status_success
favicon_status_warning
diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb
index 8e524f9b05a..4ba9094b24e 100644
--- a/spec/lib/gitlab/file_detector_spec.rb
+++ b/spec/lib/gitlab/file_detector_spec.rb
@@ -15,7 +15,20 @@ describe Gitlab::FileDetector do
describe '.type_of' do
it 'returns the type of a README file' do
- expect(described_class.type_of('README.md')).to eq(:readme)
+ filenames = Gitlab::MarkupHelper::PLAIN_FILENAMES + Gitlab::MarkupHelper::PLAIN_FILENAMES.map(&:upcase)
+ extensions = Gitlab::MarkupHelper::EXTENSIONS + Gitlab::MarkupHelper::EXTENSIONS.map(&:upcase)
+
+ filenames.each do |filename|
+ expect(described_class.type_of(filename)).to eq(:readme)
+
+ extensions.each do |extname|
+ expect(described_class.type_of("#{filename}.#{extname}")).to eq(:readme)
+ end
+ end
+ end
+
+ it 'returns nil for a README.rb file' do
+ expect(described_class.type_of('README.rb')).to be_nil
end
it 'returns nil for a README file in a directory' do
@@ -29,11 +42,15 @@ describe Gitlab::FileDetector do
end
it 'returns the type of a license file' do
- %w(LICENSE LICENCE COPYING).each do |file|
+ %w(LICENSE LICENCE COPYING UNLICENSE UNLICENCE).each do |file|
expect(described_class.type_of(file)).to eq(:license)
end
end
+ it 'returns nil for an UNCOPYING file' do
+ expect(described_class.type_of('UNCOPYING')).to be_nil
+ end
+
it 'returns the type of a version file' do
expect(described_class.type_of('VERSION')).to eq(:version)
end
@@ -42,10 +59,6 @@ describe Gitlab::FileDetector do
expect(described_class.type_of('.gitignore')).to eq(:gitignore)
end
- it 'returns the type of a Koding config file' do
- expect(described_class.type_of('.koding.yml')).to eq(:koding)
- end
-
it 'returns the type of a GitLab CI config file' do
expect(described_class.type_of('.gitlab-ci.yml')).to eq(:gitlab_ci)
end
diff --git a/spec/lib/gitlab/file_markdown_link_builder_spec.rb b/spec/lib/gitlab/file_markdown_link_builder_spec.rb
new file mode 100644
index 00000000000..feb2776c5d0
--- /dev/null
+++ b/spec/lib/gitlab/file_markdown_link_builder_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+require 'rails_helper'
+
+describe Gitlab::FileMarkdownLinkBuilder do
+ let(:custom_class) do
+ Class.new do
+ include Gitlab::FileMarkdownLinkBuilder
+ end.new
+ end
+
+ before do
+ allow(custom_class).to receive(:filename).and_return(filename)
+ end
+
+ describe 'markdown_link' do
+ let(:url) { "/uploads/#{filename}"}
+
+ before do
+ allow(custom_class).to receive(:secure_url).and_return(url)
+ end
+
+ context 'when file name has the character ]' do
+ let(:filename) { 'd]k.png' }
+
+ it 'escapes the character' do
+ expect(custom_class.markdown_link).to eq '![d\\]k](/uploads/d]k.png)'
+ end
+ end
+
+ context 'when file is an image or video' do
+ let(:filename) { 'dk.png' }
+
+ it 'returns preview markdown link' do
+ expect(custom_class.markdown_link).to eq '![dk](/uploads/dk.png)'
+ end
+ end
+
+ context 'when file is not an image or video' do
+ let(:filename) { 'dk.zip' }
+
+ it 'returns markdown link' do
+ expect(custom_class.markdown_link).to eq '[dk.zip](/uploads/dk.zip)'
+ end
+ end
+
+ context 'when file name is blank' do
+ let(:filename) { nil }
+
+ it 'returns nil' do
+ expect(custom_class.markdown_link).to eq nil
+ end
+ end
+ end
+
+ describe 'mardown_name' do
+ context 'when file is an image or video' do
+ let(:filename) { 'dk.png' }
+
+ it 'retrieves the name without the extension' do
+ expect(custom_class.markdown_name).to eq 'dk'
+ end
+ end
+
+ context 'when file is not an image or video' do
+ let(:filename) { 'dk.zip' }
+
+ it 'retrieves the name with the extesion' do
+ expect(custom_class.markdown_name).to eq 'dk.zip'
+ end
+ end
+
+ context 'when file name is blank' do
+ let(:filename) { nil }
+
+ it 'returns nil' do
+ expect(custom_class.markdown_name).to eq nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/file_type_detection_spec.rb b/spec/lib/gitlab/file_type_detection_spec.rb
new file mode 100644
index 00000000000..5e9b8988cc8
--- /dev/null
+++ b/spec/lib/gitlab/file_type_detection_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+require 'rails_helper'
+
+describe Gitlab::FileTypeDetection do
+ def upload_fixture(filename)
+ fixture_file_upload(File.join('spec', 'fixtures', filename))
+ end
+
+ describe '#image_or_video?' do
+ context 'when class is an uploader' do
+ let(:uploader) do
+ example_uploader = Class.new(CarrierWave::Uploader::Base) do
+ include Gitlab::FileTypeDetection
+
+ storage :file
+ end
+
+ example_uploader.new
+ end
+
+ it 'returns true for an image file' do
+ uploader.store!(upload_fixture('dk.png'))
+
+ expect(uploader).to be_image_or_video
+ end
+
+ it 'returns true for a video file' do
+ uploader.store!(upload_fixture('video_sample.mp4'))
+
+ expect(uploader).to be_image_or_video
+ end
+
+ it 'returns false for other extensions' do
+ uploader.store!(upload_fixture('doc_sample.txt'))
+
+ expect(uploader).not_to be_image_or_video
+ end
+
+ it 'returns false if filename is blank' do
+ uploader.store!(upload_fixture('dk.png'))
+
+ allow(uploader).to receive(:filename).and_return(nil)
+
+ expect(uploader).not_to be_image_or_video
+ end
+ end
+
+ context 'when class is a regular class' do
+ let(:custom_class) do
+ custom_class = Class.new do
+ include Gitlab::FileTypeDetection
+ end
+
+ custom_class.new
+ end
+
+ it 'returns true for an image file' do
+ allow(custom_class).to receive(:filename).and_return('dk.png')
+
+ expect(custom_class).to be_image_or_video
+ end
+
+ it 'returns true for a video file' do
+ allow(custom_class).to receive(:filename).and_return('video_sample.mp4')
+
+ expect(custom_class).to be_image_or_video
+ end
+
+ it 'returns false for other extensions' do
+ allow(custom_class).to receive(:filename).and_return('doc_sample.txt')
+
+ expect(custom_class).not_to be_image_or_video
+ end
+
+ it 'returns false if filename is blank' do
+ allow(custom_class).to receive(:filename).and_return(nil)
+
+ expect(custom_class).not_to be_image_or_video
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
index ca067a29174..134bd5657e7 100644
--- a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
@@ -17,7 +17,7 @@ describe Gitlab::Git::AttributesAtRefParser, :seed_helper do
end
it 'handles missing blobs' do
- expect { described_class.new(repository, 'non-existant-branch') }.not_to raise_error
+ expect { described_class.new(repository, 'non-existent-branch') }.not_to raise_error
end
describe '#attributes' do
diff --git a/spec/lib/gitlab/git/attributes_parser_spec.rb b/spec/lib/gitlab/git/attributes_parser_spec.rb
index 18ebfef38f0..f431d4e2a53 100644
--- a/spec/lib/gitlab/git/attributes_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_parser_spec.rb
@@ -94,7 +94,7 @@ describe Gitlab::Git::AttributesParser, :seed_helper do
# It's a bit hard to test for something _not_ being processed. As such we'll
# just test the number of entries.
it 'ignores any comments and empty lines' do
- expect(subject.patterns.length).to eq(10)
+ expect(subject.patterns.length).to eq(12)
end
end
@@ -126,7 +126,7 @@ describe Gitlab::Git::AttributesParser, :seed_helper do
describe '#each_line' do
it 'iterates over every line in the attributes file' do
- args = [String] * 14 # the number of lines in the file
+ args = [String] * 16 # the number of lines in the file
expect { |b| subject.each_line(&b) }.to yield_successive_args(*args)
end
diff --git a/spec/lib/gitlab/git/blob_snippet_spec.rb b/spec/lib/gitlab/git/blob_snippet_spec.rb
deleted file mode 100644
index 6effec8295c..00000000000
--- a/spec/lib/gitlab/git/blob_snippet_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# encoding: UTF-8
-
-require "spec_helper"
-
-describe Gitlab::Git::BlobSnippet, :seed_helper do
- describe '#data' do
- context 'empty lines' do
- let(:snippet) { Gitlab::Git::BlobSnippet.new('master', nil, nil, nil) }
-
- it { expect(snippet.data).to be_nil }
- end
-
- context 'present lines' do
- let(:snippet) { Gitlab::Git::BlobSnippet.new('master', %w(wow much), 1, 'wow.rb') }
-
- it { expect(snippet.data).to eq("wow\nmuch") }
- end
- end
-end
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index ea49502ae2e..b243f0dacae 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -4,6 +4,9 @@ require "spec_helper"
describe Gitlab::Git::Blob, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:rugged) do
+ Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH))
+ end
describe 'initialize' do
let(:blob) { Gitlab::Git::Blob.new(name: 'test') }
@@ -139,9 +142,7 @@ describe Gitlab::Git::Blob, :seed_helper do
it 'limits the size of a large file' do
blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1
buffer = Array.new(blob_size, 0)
- rugged_blob = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Rugged::Blob.from_buffer(repository.rugged, buffer.join(''))
- end
+ rugged_blob = Rugged::Blob.from_buffer(rugged, buffer.join(''))
blob = Gitlab::Git::Blob.raw(repository, rugged_blob)
expect(blob.size).to eq(blob_size)
@@ -156,9 +157,7 @@ describe Gitlab::Git::Blob, :seed_helper do
context 'when sha references a tree' do
it 'returns nil' do
- tree = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged.rev_parse('master^{tree}')
- end
+ tree = rugged.rev_parse('master^{tree}')
blob = Gitlab::Git::Blob.raw(repository, tree.oid)
@@ -262,11 +261,7 @@ describe Gitlab::Git::Blob, :seed_helper do
end
describe '.batch_lfs_pointers' do
- let(:tree_object) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged.rev_parse('master^{tree}')
- end
- end
+ let(:tree_object) { rugged.rev_parse('master^{tree}') }
let(:non_lfs_blob) do
Gitlab::Git::Blob.find(
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index 79ccbb79966..0df282d0ae3 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -3,9 +3,7 @@ require "spec_helper"
describe Gitlab::Git::Branch, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:rugged) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged
- end
+ Rugged::Repository.new(File.join(TestEnv.repos_path, repository.relative_path))
end
subject { repository.branches }
@@ -74,9 +72,7 @@ describe Gitlab::Git::Branch, :seed_helper do
Gitlab::Git.committer_hash(email: user.email, name: user.name)
end
let(:params) do
- parents = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- [repository.rugged.head.target]
- end
+ parents = [rugged.head.target]
tree = parents.first.tree
{
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 2718a3c5e49..6be35eee0fd 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -1,19 +1,17 @@
require "spec_helper"
describe Gitlab::Git::Commit, :seed_helper do
+ include GitHelpers
+
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
- let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
- let(:rugged_commit) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged.lookup(SeedRepo::Commit::ID)
- end
+ let(:rugged_repo) do
+ Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH))
end
+ let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
+ let(:rugged_commit) { rugged_repo.lookup(SeedRepo::Commit::ID) }
+
describe "Commit info" do
before do
- repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- end
-
@committer = {
email: 'mike@smith.com',
name: "Mike Smith",
@@ -26,12 +24,12 @@ describe Gitlab::Git::Commit, :seed_helper do
time: Time.now
}
- @parents = [repo.head.target]
+ @parents = [rugged_repo.head.target]
@gitlab_parents = @parents.map { |c| described_class.find(repository, c.oid) }
@tree = @parents.first.tree
sha = Rugged::Commit.create(
- repo,
+ rugged_repo,
author: @author,
committer: @committer,
tree: @tree,
@@ -40,7 +38,7 @@ describe Gitlab::Git::Commit, :seed_helper do
update_ref: "HEAD"
)
- @raw_commit = repo.lookup(sha)
+ @raw_commit = rugged_repo.lookup(sha)
@commit = described_class.find(repository, sha)
end
@@ -61,10 +59,7 @@ describe Gitlab::Git::Commit, :seed_helper do
after do
# Erase the new commit so other tests get the original repo
- repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- end
- repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID)
+ rugged_repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID)
end
end
@@ -99,7 +94,7 @@ describe Gitlab::Git::Commit, :seed_helper do
context 'body_size less than threshold' do
let(:body_size) { 123 }
- it 'fetches commit message seperately' do
+ it 'fetches commit message separately' do
expect(described_class).to receive(:get_message).with(repository, id)
commit.safe_message
@@ -120,9 +115,7 @@ describe Gitlab::Git::Commit, :seed_helper do
describe '.find' do
it "should return first head commit if without params" do
expect(described_class.last(repository).id).to eq(
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged.head.target.oid
- end
+ rugged_repo.head.target.oid
)
end
diff --git a/spec/lib/gitlab/git/committer_with_hooks_spec.rb b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
deleted file mode 100644
index c7626058acd..00000000000
--- a/spec/lib/gitlab/git/committer_with_hooks_spec.rb
+++ /dev/null
@@ -1,156 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::CommitterWithHooks, :seed_helper do
- # TODO https://gitlab.com/gitlab-org/gitaly/issues/1234
- skip 'needs to be moved to gitaly-ruby test suite' do
- shared_examples 'calling wiki hooks' do
- let(:project) { create(:project) }
- let(:user) { project.owner }
- let(:project_wiki) { ProjectWiki.new(project, user) }
- let(:wiki) { project_wiki.wiki }
- let(:options) do
- {
- id: user.id,
- username: user.username,
- name: user.name,
- email: user.email,
- message: 'commit message'
- }
- end
-
- subject { described_class.new(wiki, options) }
-
- before do
- project_wiki.create_page('home', 'test content')
- end
-
- shared_examples 'failing pre-receive hook' do
- before do
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([false, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('update')
- expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
- end
-
- it 'raises exception' do
- expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
- end
-
- it 'does not create a new commit inside the repository' do
- current_rev = find_current_rev
-
- expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
-
- expect(current_rev).to eq find_current_rev
- end
- end
-
- shared_examples 'failing update hook' do
- before do
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([false, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
- end
-
- it 'raises exception' do
- expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
- end
-
- it 'does not create a new commit inside the repository' do
- current_rev = find_current_rev
-
- expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
-
- expect(current_rev).to eq find_current_rev
- end
- end
-
- shared_examples 'failing post-receive hook' do
- before do
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([true, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('post-receive').and_return([false, ''])
- end
-
- it 'does not raise exception' do
- expect { subject.commit }.not_to raise_error
- end
-
- it 'creates the commit' do
- current_rev = find_current_rev
-
- subject.commit
-
- expect(current_rev).not_to eq find_current_rev
- end
- end
-
- shared_examples 'when hooks call succceeds' do
- let(:hook) { double(:hook) }
-
- it 'calls the three hooks' do
- expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook)
- expect(hook).to receive(:trigger).exactly(3).times.and_return([true, nil])
-
- subject.commit
- end
-
- it 'creates the commit' do
- current_rev = find_current_rev
-
- subject.commit
-
- expect(current_rev).not_to eq find_current_rev
- end
- end
-
- context 'when creating a page' do
- before do
- project_wiki.create_page('index', 'test content')
- end
-
- it_behaves_like 'failing pre-receive hook'
- it_behaves_like 'failing update hook'
- it_behaves_like 'failing post-receive hook'
- it_behaves_like 'when hooks call succceeds'
- end
-
- context 'when updating a page' do
- before do
- project_wiki.update_page(find_page('home'), content: 'some other content', format: :markdown)
- end
-
- it_behaves_like 'failing pre-receive hook'
- it_behaves_like 'failing update hook'
- it_behaves_like 'failing post-receive hook'
- it_behaves_like 'when hooks call succceeds'
- end
-
- context 'when deleting a page' do
- before do
- project_wiki.delete_page(find_page('home'))
- end
-
- it_behaves_like 'failing pre-receive hook'
- it_behaves_like 'failing update hook'
- it_behaves_like 'failing post-receive hook'
- it_behaves_like 'when hooks call succceeds'
- end
-
- def find_current_rev
- wiki.gollum_wiki.repo.commits.first&.sha
- end
-
- def find_page(name)
- wiki.page(title: name)
- end
- end
-
- context 'when Gitaly is enabled' do
- it_behaves_like 'calling wiki hooks'
- end
-
- context 'when Gitaly is disabled', :disable_gitaly do
- it_behaves_like 'calling wiki hooks'
- end
- end
-end
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 87d9fcee39e..8a4415506c4 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -2,12 +2,24 @@ require "spec_helper"
describe Gitlab::Git::Diff, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:gitaly_diff) do
+ Gitlab::GitalyClient::Diff.new(
+ from_path: '.gitmodules',
+ to_path: '.gitmodules',
+ old_mode: 0100644,
+ new_mode: 0100644,
+ from_id: '0792c58905eff3432b721f8c4a64363d8e28d9ae',
+ to_id: 'efd587ccb47caf5f31fc954edb21f0a713d9ecc3',
+ overflow_marker: false,
+ collapsed: false,
+ too_large: false,
+ patch: "@@ -4,3 +4,6 @@\n [submodule \"gitlab-shell\"]\n \tpath = gitlab-shell\n \turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n"
+ )
+ end
before do
@raw_diff_hash = {
diff: <<EOT.gsub(/^ {8}/, "").sub(/\n$/, ""),
- --- a/.gitmodules
- +++ b/.gitmodules
@@ -4,3 +4,6 @@
[submodule "gitlab-shell"]
\tpath = gitlab-shell
@@ -26,12 +38,6 @@ EOT
deleted_file: false,
too_large: false
}
-
- # TODO use a Gitaly diff object instead
- @rugged_diff = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths:
- [".gitmodules"]).patches.first
- end
end
describe '.new' do
@@ -58,9 +64,22 @@ EOT
end
end
- context 'using a Rugged::Patch' do
+ context 'using a GitalyClient::Diff' do
+ let(:gitaly_diff) do
+ Gitlab::GitalyClient::Diff.new(
+ to_path: ".gitmodules",
+ from_path: ".gitmodules",
+ old_mode: 0100644,
+ new_mode: 0100644,
+ from_id: '357406f3075a57708d0163752905cc1576fceacc',
+ to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0',
+ patch: raw_patch
+ )
+ end
+ let(:diff) { described_class.new(gitaly_diff) }
+
context 'with a small diff' do
- let(:diff) { described_class.new(@rugged_diff) }
+ let(:raw_patch) { @raw_diff_hash[:diff] }
it 'initializes the diff' do
expect(diff.to_hash).to eq(@raw_diff_hash)
@@ -72,28 +91,20 @@ EOT
end
context 'using a diff that is too large' do
- it 'prunes the diff' do
- expect_any_instance_of(String).to receive(:bytesize)
- .and_return(1024 * 1024 * 1024)
-
- diff = described_class.new(@rugged_diff)
+ let(:raw_patch) { 'a' * 204800 }
+ it 'prunes the diff' do
expect(diff.diff).to be_empty
expect(diff).to be_too_large
end
end
context 'using a collapsable diff that is too large' do
- before do
- # The patch total size is 200, with lines between 21 and 54.
- # This is a quick-and-dirty way to test this. Ideally, a new patch is
- # added to the test repo with a size that falls between the real limits.
- stub_const("#{described_class}::SIZE_LIMIT", 150)
- stub_const("#{described_class}::COLLAPSE_LIMIT", 100)
- end
+ let(:raw_patch) { 'a' * 204800 }
it 'prunes the diff as a large diff instead of as a collapsed diff' do
- diff = described_class.new(@rugged_diff, expanded: false)
+ gitaly_diff.too_large = true
+ diff = described_class.new(gitaly_diff, expanded: false)
expect(diff.diff).to be_empty
expect(diff).to be_too_large
@@ -101,54 +112,6 @@ EOT
end
end
- context 'using a large binary diff' do
- it 'does not prune the diff' do
- expect_any_instance_of(Rugged::Diff::Delta).to receive(:binary?)
- .and_return(true)
-
- diff = described_class.new(@rugged_diff)
-
- expect(diff.diff).not_to be_empty
- end
- end
- end
-
- context 'using a GitalyClient::Diff' do
- let(:diff) do
- described_class.new(
- Gitlab::GitalyClient::Diff.new(
- to_path: ".gitmodules",
- from_path: ".gitmodules",
- old_mode: 0100644,
- new_mode: 0100644,
- from_id: '357406f3075a57708d0163752905cc1576fceacc',
- to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0',
- patch: raw_patch
- )
- )
- end
-
- context 'with a small diff' do
- let(:raw_patch) { @raw_diff_hash[:diff] }
-
- it 'initializes the diff' do
- expect(diff.to_hash).to eq(@raw_diff_hash)
- end
-
- it 'does not prune the diff' do
- expect(diff).not_to be_too_large
- end
- end
-
- context 'using a diff that is too large' do
- let(:raw_patch) { 'a' * 204800 }
-
- it 'prunes the diff' do
- expect(diff.diff).to be_empty
- expect(diff).to be_too_large
- end
- end
-
context 'when the patch passed is not UTF-8-encoded' do
let(:raw_patch) { @raw_diff_hash[:diff].encode(Encoding::ASCII_8BIT) }
@@ -259,31 +222,37 @@ EOT
end
it 'leave non-binary diffs as-is' do
- diff = described_class.new(@rugged_diff)
+ diff = described_class.new(gitaly_diff)
expect(diff.json_safe_diff).to eq(diff.diff)
end
end
describe '#submodule?' do
- before do
- # TODO use a Gitaly diff object instead
- rugged_commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged.rev_parse('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- end
-
- @diffs = rugged_commit.parents[0].diff(rugged_commit).patches
+ let(:gitaly_submodule_diff) do
+ Gitlab::GitalyClient::Diff.new(
+ from_path: 'gitlab-grack',
+ to_path: 'gitlab-grack',
+ old_mode: 0,
+ new_mode: 57344,
+ from_id: '0000000000000000000000000000000000000000',
+ to_id: '645f6c4c82fd3f5e06f67134450a570b795e55a6',
+ overflow_marker: false,
+ collapsed: false,
+ too_large: false,
+ patch: "@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n"
+ )
end
- it { expect(described_class.new(@diffs[0]).submodule?).to eq(false) }
- it { expect(described_class.new(@diffs[1]).submodule?).to eq(true) }
+ it { expect(described_class.new(gitaly_diff).submodule?).to eq(false) }
+ it { expect(described_class.new(gitaly_submodule_diff).submodule?).to eq(true) }
end
describe '#line_count' do
it 'returns the correct number of lines' do
- diff = described_class.new(@rugged_diff)
+ diff = described_class.new(gitaly_diff)
- expect(diff.line_count).to eq(9)
+ expect(diff.line_count).to eq(7)
end
end
diff --git a/spec/lib/gitlab/git/diff_stats_collection_spec.rb b/spec/lib/gitlab/git/diff_stats_collection_spec.rb
new file mode 100644
index 00000000000..b07690ef39c
--- /dev/null
+++ b/spec/lib/gitlab/git/diff_stats_collection_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+describe Gitlab::Git::DiffStatsCollection do
+ let(:stats_a) do
+ double(Gitaly::DiffStats, additions: 10, deletions: 15, path: 'foo')
+ end
+
+ let(:stats_b) do
+ double(Gitaly::DiffStats, additions: 5, deletions: 1, path: 'bar')
+ end
+
+ let(:diff_stats) { [stats_a, stats_b] }
+ let(:collection) { described_class.new(diff_stats) }
+
+ describe '#find_by_path' do
+ it 'returns stats by path when found' do
+ expect(collection.find_by_path('foo')).to eq(stats_a)
+ end
+
+ it 'returns nil when stats is not found by path' do
+ expect(collection.find_by_path('no-file')).to be_nil
+ end
+ end
+
+ describe '#paths' do
+ it 'returns only modified paths' do
+ expect(collection.paths).to eq %w[foo bar]
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/gitlab_projects_spec.rb b/spec/lib/gitlab/git/gitlab_projects_spec.rb
deleted file mode 100644
index f5d8503c30c..00000000000
--- a/spec/lib/gitlab/git/gitlab_projects_spec.rb
+++ /dev/null
@@ -1,321 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::GitlabProjects do
- after do
- TestEnv.clean_test_path
- end
-
- around do |example|
- # TODO move this spec to gitaly-ruby. GitlabProjects is not used in gitlab-ce
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- let(:project) { create(:project, :repository) }
-
- if $VERBOSE
- let(:logger) { Logger.new(STDOUT) }
- else
- let(:logger) { double('logger').as_null_object }
- end
-
- let(:tmp_repos_path) { TestEnv.repos_path }
- let(:repo_name) { project.disk_path + '.git' }
- let(:tmp_repo_path) { File.join(tmp_repos_path, repo_name) }
- let(:gl_projects) { build_gitlab_projects(TestEnv::REPOS_STORAGE, repo_name) }
-
- describe '#initialize' do
- it { expect(gl_projects.shard_path).to eq(tmp_repos_path) }
- it { expect(gl_projects.repository_relative_path).to eq(repo_name) }
- it { expect(gl_projects.repository_absolute_path).to eq(File.join(tmp_repos_path, repo_name)) }
- it { expect(gl_projects.logger).to eq(logger) }
- end
-
- describe '#push_branches' do
- let(:remote_name) { 'remote-name' }
- let(:branch_name) { 'master' }
- let(:cmd) { %W(#{Gitlab.config.git.bin_path} push -- #{remote_name} #{branch_name}) }
- let(:force) { false }
-
- subject { gl_projects.push_branches(remote_name, 600, force, [branch_name]) }
-
- it 'executes the command' do
- stub_spawn(cmd, 600, tmp_repo_path, success: true)
-
- is_expected.to be_truthy
- end
-
- it 'fails' do
- stub_spawn(cmd, 600, tmp_repo_path, success: false)
-
- is_expected.to be_falsy
- end
-
- context 'with --force' do
- let(:cmd) { %W(#{Gitlab.config.git.bin_path} push --force -- #{remote_name} #{branch_name}) }
- let(:force) { true }
-
- it 'executes the command' do
- stub_spawn(cmd, 600, tmp_repo_path, success: true)
-
- is_expected.to be_truthy
- end
- end
- end
-
- describe '#fetch_remote' do
- let(:remote_name) { 'remote-name' }
- let(:branch_name) { 'master' }
- let(:force) { false }
- let(:prune) { true }
- let(:tags) { true }
- let(:args) { { force: force, tags: tags, prune: prune }.merge(extra_args) }
- let(:extra_args) { {} }
- let(:cmd) { %W(#{Gitlab.config.git.bin_path} fetch #{remote_name} --quiet --prune --tags) }
-
- subject { gl_projects.fetch_remote(remote_name, 600, args) }
-
- def stub_tempfile(name, filename, opts = {})
- chmod = opts.delete(:chmod)
- file = StringIO.new
-
- allow(file).to receive(:close!)
- allow(file).to receive(:path).and_return(name)
-
- expect(Tempfile).to receive(:new).with(filename).and_return(file)
- expect(file).to receive(:chmod).with(chmod) if chmod
-
- file
- end
-
- context 'with default args' do
- it 'executes the command' do
- stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
-
- is_expected.to be_truthy
- end
-
- it 'fails' do
- stub_spawn(cmd, 600, tmp_repo_path, {}, success: false)
-
- is_expected.to be_falsy
- end
- end
-
- context 'with --force' do
- let(:force) { true }
- let(:cmd) { %W(#{Gitlab.config.git.bin_path} fetch #{remote_name} --quiet --prune --force --tags) }
-
- it 'executes the command with forced option' do
- stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
-
- is_expected.to be_truthy
- end
- end
-
- context 'with --no-tags' do
- let(:tags) { false }
- let(:cmd) { %W(#{Gitlab.config.git.bin_path} fetch #{remote_name} --quiet --prune --no-tags) }
-
- it 'executes the command' do
- stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
-
- is_expected.to be_truthy
- end
- end
-
- context 'with no prune' do
- let(:prune) { false }
- let(:cmd) { %W(#{Gitlab.config.git.bin_path} fetch #{remote_name} --quiet --tags) }
-
- it 'executes the command' do
- stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
-
- is_expected.to be_truthy
- end
- end
-
- describe 'with an SSH key' do
- let(:extra_args) { { ssh_key: 'SSH KEY' } }
-
- it 'sets GIT_SSH to a custom script' do
- script = stub_tempfile('scriptFile', 'gitlab-shell-ssh-wrapper', chmod: 0o755)
- key = stub_tempfile('/tmp files/keyFile', 'gitlab-shell-key-file', chmod: 0o400)
-
- stub_spawn(cmd, 600, tmp_repo_path, { 'GIT_SSH' => 'scriptFile' }, success: true)
-
- is_expected.to be_truthy
-
- expect(script.string).to eq("#!/bin/sh\nexec ssh '-oIdentityFile=\"/tmp files/keyFile\"' '-oIdentitiesOnly=\"yes\"' \"$@\"")
- expect(key.string).to eq('SSH KEY')
- end
- end
-
- describe 'with known_hosts data' do
- let(:extra_args) { { known_hosts: 'KNOWN HOSTS' } }
-
- it 'sets GIT_SSH to a custom script' do
- script = stub_tempfile('scriptFile', 'gitlab-shell-ssh-wrapper', chmod: 0o755)
- key = stub_tempfile('/tmp files/knownHosts', 'gitlab-shell-known-hosts', chmod: 0o400)
-
- stub_spawn(cmd, 600, tmp_repo_path, { 'GIT_SSH' => 'scriptFile' }, success: true)
-
- is_expected.to be_truthy
-
- expect(script.string).to eq("#!/bin/sh\nexec ssh '-oStrictHostKeyChecking=\"yes\"' '-oUserKnownHostsFile=\"/tmp files/knownHosts\"' \"$@\"")
- expect(key.string).to eq('KNOWN HOSTS')
- end
- end
- end
-
- describe '#import_project' do
- let(:project) { create(:project) }
- let(:import_url) { TestEnv.factory_repo_path_bare }
- let(:cmd) { %W(#{Gitlab.config.git.bin_path} clone --bare -- #{import_url} #{tmp_repo_path}) }
- let(:timeout) { 600 }
-
- subject { gl_projects.import_project(import_url, timeout) }
-
- shared_examples 'importing repository' do
- context 'success import' do
- it 'imports a repo' do
- expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
-
- is_expected.to be_truthy
-
- expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_truthy
- end
- end
-
- context 'already exists' do
- it "doesn't import" do
- FileUtils.mkdir_p(tmp_repo_path)
-
- is_expected.to be_falsy
- end
- end
- end
-
- describe 'logging' do
- it 'imports a repo' do
- message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>."
- expect(logger).to receive(:info).with(message)
-
- subject
- end
- end
-
- context 'timeout' do
- it 'does not import a repo' do
- stub_spawn_timeout(cmd, timeout, nil)
-
- message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed."
- expect(logger).to receive(:error).with(message)
-
- is_expected.to be_falsy
-
- expect(gl_projects.output).to eq("Timed out\n")
- expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
- end
- end
-
- it_behaves_like 'importing repository'
- end
-
- describe '#fork_repository' do
- let(:dest_repos) { TestEnv::REPOS_STORAGE }
- let(:dest_repos_path) { tmp_repos_path }
- let(:dest_repo_name) { File.join('@hashed', 'aa', 'bb', 'xyz.git') }
- let(:dest_repo) { File.join(dest_repos_path, dest_repo_name) }
-
- subject { gl_projects.fork_repository(dest_repos, dest_repo_name) }
-
- before do
- FileUtils.mkdir_p(dest_repos_path)
- end
-
- after do
- FileUtils.rm_rf(dest_repos_path)
- end
-
- shared_examples 'forking a repository' do
- it 'forks the repository' do
- is_expected.to be_truthy
-
- expect(File.exist?(dest_repo)).to be_truthy
- expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy
- expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy
- end
-
- it 'does not fork if a project of the same name already exists' do
- # create a fake project at the intended destination
- FileUtils.mkdir_p(dest_repo)
-
- is_expected.to be_falsy
- end
- end
-
- it_behaves_like 'forking a repository'
-
- # We seem to be stuck to having only one working Gitaly storage in tests, changing
- # that is not very straight-forward so I'm leaving this test here for now till
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/41393 is fixed.
- context 'different storages' do
- let(:dest_repos) { 'alternative' }
- let(:dest_repos_path) { File.join(File.dirname(tmp_repos_path), dest_repos) }
-
- before do
- stub_storage_settings(dest_repos => { 'path' => dest_repos_path })
- end
-
- it 'forks the repo' do
- is_expected.to be_truthy
-
- expect(File.exist?(dest_repo)).to be_truthy
- expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy
- expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy
- end
- end
-
- describe 'log messages' do
- describe 'successful fork' do
- it do
- message = "Forking repository from <#{tmp_repo_path}> to <#{dest_repo}>."
- expect(logger).to receive(:info).with(message)
-
- subject
- end
- end
-
- describe 'failed fork due existing destination' do
- it do
- FileUtils.mkdir_p(dest_repo)
- message = "fork-repository failed: destination repository <#{dest_repo}> already exists."
- expect(logger).to receive(:error).with(message)
-
- subject
- end
- end
- end
- end
-
- def build_gitlab_projects(*args)
- described_class.new(
- *args,
- global_hooks_path: Gitlab.config.gitlab_shell.hooks_path,
- logger: logger
- )
- end
-
- def stub_spawn(*args, success: true)
- exitstatus = success ? 0 : nil
- expect(gl_projects).to receive(:popen_with_timeout).with(*args)
- .and_return(["output", exitstatus])
- end
-
- def stub_spawn_timeout(*args)
- expect(gl_projects).to receive(:popen_with_timeout).with(*args)
- .and_raise(Timeout::Error)
- end
-end
diff --git a/spec/lib/gitlab/git/hook_env_spec.rb b/spec/lib/gitlab/git/hook_env_spec.rb
index e6aa5ad8c90..5e49ea6da7a 100644
--- a/spec/lib/gitlab/git/hook_env_spec.rb
+++ b/spec/lib/gitlab/git/hook_env_spec.rb
@@ -4,11 +4,7 @@ describe Gitlab::Git::HookEnv do
let(:gl_repository) { 'project-123' }
describe ".set" do
- context 'with RequestStore.store disabled' do
- before do
- allow(RequestStore).to receive(:active?).and_return(false)
- end
-
+ context 'with RequestStore disabled' do
it 'does not store anything' do
described_class.set(gl_repository, GIT_OBJECT_DIRECTORY_RELATIVE: 'foo')
@@ -16,11 +12,7 @@ describe Gitlab::Git::HookEnv do
end
end
- context 'with RequestStore.store enabled' do
- before do
- allow(RequestStore).to receive(:active?).and_return(true)
- end
-
+ context 'with RequestStore enabled', :request_store do
it 'whitelist some `GIT_*` variables and stores them using RequestStore' do
described_class.set(
gl_repository,
@@ -41,9 +33,8 @@ describe Gitlab::Git::HookEnv do
end
describe ".all" do
- context 'with RequestStore.store enabled' do
+ context 'with RequestStore enabled', :request_store do
before do
- allow(RequestStore).to receive(:active?).and_return(true)
described_class.set(
gl_repository,
GIT_OBJECT_DIRECTORY_RELATIVE: 'foo',
@@ -60,7 +51,7 @@ describe Gitlab::Git::HookEnv do
end
describe ".to_env_hash" do
- context 'with RequestStore.store enabled' do
+ context 'with RequestStore enabled', :request_store do
using RSpec::Parameterized::TableSyntax
let(:key) { 'GIT_OBJECT_DIRECTORY_RELATIVE' }
@@ -76,7 +67,6 @@ describe Gitlab::Git::HookEnv do
with_them do
before do
- allow(RequestStore).to receive(:active?).and_return(true)
described_class.set(gl_repository, key.to_sym => input)
end
@@ -92,7 +82,7 @@ describe Gitlab::Git::HookEnv do
end
describe 'thread-safety' do
- context 'with RequestStore.store enabled' do
+ context 'with RequestStore enabled', :request_store do
before do
allow(RequestStore).to receive(:active?).and_return(true)
described_class.set(gl_repository, GIT_OBJECT_DIRECTORY_RELATIVE: 'foo')
diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb
deleted file mode 100644
index a45c8510b15..00000000000
--- a/spec/lib/gitlab/git/hook_spec.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-require 'spec_helper'
-require 'fileutils'
-
-describe Gitlab::Git::Hook do
- before do
- # We need this because in the spec/spec_helper.rb we define it like this:
- # allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
- allow_any_instance_of(described_class).to receive(:trigger).and_call_original
- end
-
- around do |example|
- # TODO move hook tests to gitaly-ruby. Hook will disappear from gitlab-ce
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- describe "#trigger" do
- set(:project) { create(:project, :repository) }
- let(:repository) { project.repository.raw_repository }
- let(:repo_path) { repository.path }
- let(:hooks_dir) { File.join(repo_path, 'hooks') }
- let(:user) { create(:user) }
- let(:gl_id) { Gitlab::GlId.gl_id(user) }
- let(:gl_username) { user.username }
-
- def create_hook(name)
- FileUtils.mkdir_p(hooks_dir)
- hook_path = File.join(hooks_dir, name)
- File.open(hook_path, 'w', 0755) do |f|
- f.write(<<~HOOK)
- #!/bin/sh
- exit 0
- HOOK
- end
- end
-
- def create_failing_hook(name)
- FileUtils.mkdir_p(hooks_dir)
- hook_path = File.join(hooks_dir, name)
- File.open(hook_path, 'w', 0755) do |f|
- f.write(<<~HOOK)
- #!/bin/sh
- echo 'regular message from the hook'
- echo 'error message from the hook' 1>&2
- echo 'error message from the hook line 2' 1>&2
- exit 1
- HOOK
- end
- end
-
- ['pre-receive', 'post-receive', 'update'].each do |hook_name|
- context "when triggering a #{hook_name} hook" do
- context "when the hook is successful" do
- let(:hook_path) { File.join(hooks_dir, hook_name) }
- let(:gl_repository) { Gitlab::GlRepository.gl_repository(project, false) }
- let(:env) do
- {
- 'GL_ID' => gl_id,
- 'GL_USERNAME' => gl_username,
- 'PWD' => repo_path,
- 'GL_PROTOCOL' => 'web',
- 'GL_REPOSITORY' => gl_repository
- }
- end
-
- it "returns success with no errors" do
- create_hook(hook_name)
- hook = described_class.new(hook_name, repository)
- blank = Gitlab::Git::BLANK_SHA
- ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch'
-
- if hook_name != 'update'
- expect(Open3).to receive(:popen3)
- .with(env, hook_path, chdir: repo_path).and_call_original
- end
-
- status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref)
- expect(status).to be true
- expect(errors).to be_blank
- end
- end
-
- context "when the hook is unsuccessful" do
- it "returns failure with errors" do
- create_failing_hook(hook_name)
- hook = described_class.new(hook_name, repository)
- blank = Gitlab::Git::BLANK_SHA
- ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch'
-
- status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref)
- expect(status).to be false
- expect(errors).to eq("error message from the hook\nerror message from the hook line 2\n")
- end
- end
- end
- end
-
- context "when the hook doesn't exist" do
- it "returns success with no errors" do
- hook = described_class.new('unknown_hook', repository)
- blank = Gitlab::Git::BLANK_SHA
- ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch'
-
- status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref)
- expect(status).to be true
- expect(errors).to be_nil
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb
deleted file mode 100644
index 55ffced36ac..00000000000
--- a/spec/lib/gitlab/git/hooks_service_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::HooksService, :seed_helper do
- let(:gl_id) { 'user-456' }
- let(:gl_username) { 'janedoe' }
- let(:user) { Gitlab::Git::User.new(gl_username, 'Jane Doe', 'janedoe@example.com', gl_id) }
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, 'project-123') }
- let(:service) { described_class.new }
- let(:blankrev) { Gitlab::Git::BLANK_SHA }
- let(:oldrev) { SeedRepo::Commit::PARENT_ID }
- let(:newrev) { SeedRepo::Commit::ID }
- let(:ref) { 'refs/heads/feature' }
-
- describe '#execute' do
- context 'when receive hooks were successful' do
- let(:hook) { double(:hook) }
-
- it 'calls all three hooks' do
- expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook)
- expect(hook).to receive(:trigger).with(gl_id, gl_username, blankrev, newrev, ref)
- .exactly(3).times.and_return([true, nil])
-
- service.execute(user, repository, blankrev, newrev, ref) { }
- end
- end
-
- context 'when pre-receive hook failed' do
- it 'does not call post-receive hook' do
- expect(service).to receive(:run_hook).with('pre-receive').and_return([false, 'hello world'])
- expect(service).not_to receive(:run_hook).with('post-receive')
-
- expect do
- service.execute(user, repository, blankrev, newrev, ref)
- end.to raise_error(Gitlab::Git::PreReceiveError, 'hello world')
- end
- end
-
- context 'when update hook failed' do
- it 'does not call post-receive hook' do
- expect(service).to receive(:run_hook).with('pre-receive').and_return([true, nil])
- expect(service).to receive(:run_hook).with('update').and_return([false, 'hello world'])
- expect(service).not_to receive(:run_hook).with('post-receive')
-
- expect do
- service.execute(user, repository, blankrev, newrev, ref)
- end.to raise_error(Gitlab::Git::PreReceiveError, 'hello world')
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb
deleted file mode 100644
index c4edd6961e1..00000000000
--- a/spec/lib/gitlab/git/index_spec.rb
+++ /dev/null
@@ -1,239 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::Index, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
- let(:index) { described_class.new(repository) }
-
- before do
- index.read_tree(lookup('master').tree)
- end
-
- around do |example|
- # TODO move these specs to gitaly-ruby. The Index class will disappear from gitlab-ce
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- describe '#create' do
- let(:options) do
- {
- content: 'Lorem ipsum...',
- file_path: 'documents/story.txt'
- }
- end
-
- context 'when no file at that path exists' do
- it 'creates the file in the index' do
- index.create(options)
-
- entry = index.get(options[:file_path])
-
- expect(entry).not_to be_nil
- expect(lookup(entry[:oid]).content).to eq(options[:content])
- end
- end
-
- context 'when a file at that path exists' do
- before do
- options[:file_path] = 'files/executables/ls'
- end
-
- it 'raises an error' do
- expect { index.create(options) }.to raise_error('A file with this name already exists')
- end
- end
-
- context 'when content is in base64' do
- before do
- options[:content] = Base64.encode64(options[:content])
- options[:encoding] = 'base64'
- end
-
- it 'decodes base64' do
- index.create(options)
-
- entry = index.get(options[:file_path])
- expect(lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
- end
- end
-
- context 'when content contains CRLF' do
- before do
- repository.autocrlf = :input
- options[:content] = "Hello,\r\nWorld"
- end
-
- it 'converts to LF' do
- index.create(options)
-
- entry = index.get(options[:file_path])
- expect(lookup(entry[:oid]).content).to eq("Hello,\nWorld")
- end
- end
- end
-
- describe '#create_dir' do
- let(:options) do
- {
- file_path: 'newdir'
- }
- end
-
- context 'when no file or dir at that path exists' do
- it 'creates the dir in the index' do
- index.create_dir(options)
-
- entry = index.get(options[:file_path] + '/.gitkeep')
-
- expect(entry).not_to be_nil
- end
- end
-
- context 'when a file at that path exists' do
- before do
- options[:file_path] = 'files/executables/ls'
- end
-
- it 'raises an error' do
- expect { index.create_dir(options) }.to raise_error('A file with this name already exists')
- end
- end
-
- context 'when a directory at that path exists' do
- before do
- options[:file_path] = 'files/executables'
- end
-
- it 'raises an error' do
- expect { index.create_dir(options) }.to raise_error('A directory with this name already exists')
- end
- end
- end
-
- describe '#update' do
- let(:options) do
- {
- content: 'Lorem ipsum...',
- file_path: 'README.md'
- }
- end
-
- context 'when no file at that path exists' do
- before do
- options[:file_path] = 'documents/story.txt'
- end
-
- it 'raises an error' do
- expect { index.update(options) }.to raise_error("A file with this name doesn't exist")
- end
- end
-
- context 'when a file at that path exists' do
- it 'updates the file in the index' do
- index.update(options)
-
- entry = index.get(options[:file_path])
-
- expect(lookup(entry[:oid]).content).to eq(options[:content])
- end
-
- it 'preserves file mode' do
- options[:file_path] = 'files/executables/ls'
-
- index.update(options)
-
- entry = index.get(options[:file_path])
-
- expect(entry[:mode]).to eq(0100755)
- end
- end
- end
-
- describe '#move' do
- let(:options) do
- {
- content: 'Lorem ipsum...',
- previous_path: 'README.md',
- file_path: 'NEWREADME.md'
- }
- end
-
- context 'when no file at that path exists' do
- it 'raises an error' do
- options[:previous_path] = 'documents/story.txt'
-
- expect { index.move(options) }.to raise_error("A file with this name doesn't exist")
- end
- end
-
- context 'when a file at the new path already exists' do
- it 'raises an error' do
- options[:file_path] = 'CHANGELOG'
-
- expect { index.move(options) }.to raise_error("A file with this name already exists")
- end
- end
-
- context 'when a file at that path exists' do
- it 'removes the old file in the index' do
- index.move(options)
-
- entry = index.get(options[:previous_path])
-
- expect(entry).to be_nil
- end
-
- it 'creates the new file in the index' do
- index.move(options)
-
- entry = index.get(options[:file_path])
-
- expect(entry).not_to be_nil
- expect(lookup(entry[:oid]).content).to eq(options[:content])
- end
-
- it 'preserves file mode' do
- options[:previous_path] = 'files/executables/ls'
-
- index.move(options)
-
- entry = index.get(options[:file_path])
-
- expect(entry[:mode]).to eq(0100755)
- end
- end
- end
-
- describe '#delete' do
- let(:options) do
- {
- file_path: 'README.md'
- }
- end
-
- context 'when no file at that path exists' do
- before do
- options[:file_path] = 'documents/story.txt'
- end
-
- it 'raises an error' do
- expect { index.delete(options) }.to raise_error("A file with this name doesn't exist")
- end
- end
-
- context 'when a file at that path exists' do
- it 'removes the file in the index' do
- index.delete(options)
-
- entry = index.get(options[:file_path])
-
- expect(entry).to be_nil
- end
- end
- end
-
- def lookup(revision)
- repository.rugged.rev_parse(revision)
- end
-end
diff --git a/spec/lib/gitlab/git/lfs_changes_spec.rb b/spec/lib/gitlab/git/lfs_changes_spec.rb
index c5e7ab959b2..d035df7e0c2 100644
--- a/spec/lib/gitlab/git/lfs_changes_spec.rb
+++ b/spec/lib/gitlab/git/lfs_changes_spec.rb
@@ -15,5 +15,9 @@ describe Gitlab::Git::LfsChanges do
it 'limits new_objects using object_limit' do
expect(subject.new_pointers(object_limit: 1)).to eq([])
end
+
+ it 'times out if given a small dynamic timeout' do
+ expect { subject.new_pointers(dynamic_timeout: 0.001) }.to raise_error(GRPC::DeadlineExceeded)
+ end
end
end
diff --git a/spec/lib/gitlab/git/patches/collection_spec.rb b/spec/lib/gitlab/git/patches/collection_spec.rb
new file mode 100644
index 00000000000..080be141c59
--- /dev/null
+++ b/spec/lib/gitlab/git/patches/collection_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Git::Patches::Collection do
+ let(:patches_folder) { Rails.root.join('spec/fixtures/patchfiles') }
+ let(:patch_content1) do
+ File.read(File.join(patches_folder, "0001-This-does-not-apply-to-the-feature-branch.patch"))
+ end
+ let(:patch_content2) do
+ File.read(File.join(patches_folder, "0001-A-commit-from-a-patch.patch"))
+ end
+
+ subject(:collection) { described_class.new([patch_content1, patch_content2]) }
+
+ describe '#size' do
+ it 'combines the size of the patches' do
+ expect(collection.size).to eq(549.bytes + 424.bytes)
+ end
+ end
+
+ describe '#valid_size?' do
+ it 'is not valid if the total size is bigger than 2MB' do
+ expect(collection).to receive(:size).and_return(2500.kilobytes)
+
+ expect(collection).not_to be_valid_size
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/patches/commit_patches_spec.rb b/spec/lib/gitlab/git/patches/commit_patches_spec.rb
new file mode 100644
index 00000000000..760112155ce
--- /dev/null
+++ b/spec/lib/gitlab/git/patches/commit_patches_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Git::Patches::CommitPatches do
+ describe '#commit' do
+ let(:patches) do
+ patches_folder = Rails.root.join('spec/fixtures/patchfiles')
+ content_1 = File.read(File.join(patches_folder, "0001-This-does-not-apply-to-the-feature-branch.patch"))
+ content_2 = File.read(File.join(patches_folder, "0001-A-commit-from-a-patch.patch"))
+
+ Gitlab::Git::Patches::Collection.new([content_1, content_2])
+ end
+ let(:user) { build(:user) }
+ let(:branch_name) { 'branch-with-patches' }
+ let(:repository) { create(:project, :repository).repository }
+
+ subject(:commit_patches) do
+ described_class.new(user, repository, branch_name, patches)
+ end
+
+ it 'applies the patches' do
+ new_rev = commit_patches.commit
+
+ expect(repository.commit(new_rev)).not_to be_nil
+ end
+
+ it 'updates the branch cache' do
+ expect(repository).to receive(:after_create_branch)
+
+ commit_patches.commit
+ end
+
+ context 'when the repository does not exist' do
+ let(:repository) { create(:project).repository }
+
+ it 'raises the correct error' do
+ expect { commit_patches.commit }.to raise_error(Gitlab::Git::Repository::NoRepository)
+ end
+ end
+
+ context 'when the patch does not apply' do
+ let(:branch_name) { 'feature' }
+
+ it 'raises the correct error' do
+ expect { commit_patches.commit }.to raise_error(Gitlab::Git::CommandError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/patches/patch_spec.rb b/spec/lib/gitlab/git/patches/patch_spec.rb
new file mode 100644
index 00000000000..7466e853b65
--- /dev/null
+++ b/spec/lib/gitlab/git/patches/patch_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Git::Patches::Patch do
+ let(:patches_folder) { Rails.root.join('spec/fixtures/patchfiles') }
+ let(:patch_content) do
+ File.read(File.join(patches_folder, "0001-This-does-not-apply-to-the-feature-branch.patch"))
+ end
+ let(:patch) { described_class.new(patch_content) }
+
+ describe '#size' do
+ it 'is correct' do
+ expect(patch.size).to eq(549.bytes)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/popen_spec.rb b/spec/lib/gitlab/git/popen_spec.rb
deleted file mode 100644
index 074e66d2a5d..00000000000
--- a/spec/lib/gitlab/git/popen_spec.rb
+++ /dev/null
@@ -1,179 +0,0 @@
-require 'spec_helper'
-
-describe 'Gitlab::Git::Popen' do
- let(:path) { Rails.root.join('tmp').to_s }
- let(:test_string) { 'The quick brown fox jumped over the lazy dog' }
- # The pipe buffer is typically 64K. This string is about 440K.
- let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] }
-
- let(:klass) do
- Class.new(Object) do
- include Gitlab::Git::Popen
- end
- end
-
- context 'popen' do
- context 'zero status' do
- let(:result) { klass.new.popen(%w(ls), path) }
- let(:output) { result.first }
- let(:status) { result.last }
-
- it { expect(status).to be_zero }
- it { expect(output).to include('tests') }
- end
-
- context 'non-zero status' do
- let(:result) { klass.new.popen(%w(cat NOTHING), path) }
- let(:output) { result.first }
- let(:status) { result.last }
-
- it { expect(status).to eq(1) }
- it { expect(output).to include('No such file or directory') }
- end
-
- context 'unsafe string command' do
- it 'raises an error when it gets called with a string argument' do
- expect { klass.new.popen('ls', path) }.to raise_error(RuntimeError)
- end
- end
-
- context 'with custom options' do
- let(:vars) { { 'foobar' => 123, 'PWD' => path } }
- let(:options) { { chdir: path } }
-
- it 'calls popen3 with the provided environment variables' do
- expect(Open3).to receive(:popen3).with(vars, 'ls', options)
-
- klass.new.popen(%w(ls), path, { 'foobar' => 123 })
- end
- end
-
- context 'use stdin' do
- let(:result) { klass.new.popen(%w[cat], path) { |stdin| stdin.write 'hello' } }
- let(:output) { result.first }
- let(:status) { result.last }
-
- it { expect(status).to be_zero }
- it { expect(output).to eq('hello') }
- end
-
- context 'with lazy block' do
- it 'yields a lazy io' do
- expect_lazy_io = lambda do |io|
- expect(io).to be_a Enumerator::Lazy
- expect(io.inspect).to include('#<IO:fd')
- end
-
- klass.new.popen(%w[ls], path, lazy_block: expect_lazy_io)
- end
-
- it "doesn't wait for process exit" do
- Timeout.timeout(2) do
- klass.new.popen(%w[yes], path, lazy_block: ->(io) {})
- end
- end
- end
-
- context 'with a process that writes a lot of data to stderr' do
- it 'returns zero' do
- output, status = klass.new.popen(spew_command, path)
-
- expect(output).to include(test_string)
- expect(status).to eq(0)
- end
- end
- end
-
- context 'popen_with_timeout' do
- let(:timeout) { 1.second }
-
- context 'no timeout' do
- context 'zero status' do
- let(:result) { klass.new.popen_with_timeout(%w(ls), timeout, path) }
- let(:output) { result.first }
- let(:status) { result.last }
-
- it { expect(status).to be_zero }
- it { expect(output).to include('tests') }
- end
-
- context 'multi-line string' do
- let(:test_string) { "this is 1 line\n2nd line\n3rd line\n" }
- let(:result) { klass.new.popen_with_timeout(['echo', test_string], timeout, path) }
- let(:output) { result.first }
- let(:status) { result.last }
-
- it { expect(status).to be_zero }
- # echo adds its own line
- it { expect(output).to eq(test_string + "\n") }
- end
-
- context 'non-zero status' do
- let(:result) { klass.new.popen_with_timeout(%w(cat NOTHING), timeout, path) }
- let(:output) { result.first }
- let(:status) { result.last }
-
- it { expect(status).to eq(1) }
- it { expect(output).to include('No such file or directory') }
- end
-
- context 'unsafe string command' do
- it 'raises an error when it gets called with a string argument' do
- expect { klass.new.popen_with_timeout('ls', timeout, path) }.to raise_error(RuntimeError)
- end
- end
- end
-
- context 'timeout' do
- context 'timeout' do
- it "raises a Timeout::Error" do
- expect { klass.new.popen_with_timeout(%w(sleep 1000), timeout, path) }.to raise_error(Timeout::Error)
- end
-
- it "handles processes that do not shutdown correctly" do
- expect { klass.new.popen_with_timeout(['bash', '-c', "trap -- '' SIGTERM; sleep 1000"], timeout, path) }.to raise_error(Timeout::Error)
- end
-
- it 'handles process that writes a lot of data to stderr' do
- output, status = klass.new.popen_with_timeout(spew_command, timeout, path)
-
- expect(output).to include(test_string)
- expect(status).to eq(0)
- end
- end
-
- context 'timeout period' do
- let(:time_taken) do
- begin
- start = Time.now
- klass.new.popen_with_timeout(%w(sleep 1000), timeout, path)
- rescue
- Time.now - start
- end
- end
-
- it { expect(time_taken).to be >= timeout }
- end
-
- context 'clean up' do
- let(:instance) { klass.new }
-
- it 'kills the child process' do
- expect(instance).to receive(:kill_process_group_for_pid).and_wrap_original do |m, *args|
- # is the PID, and it should not be running at this point
- m.call(*args)
-
- pid = args.first
- begin
- Process.getpgid(pid)
- raise "The child process should have been killed"
- rescue Errno::ESRCH
- end
- end
-
- expect { instance.popen_with_timeout(['bash', '-c', "trap -- '' SIGTERM; sleep 1000"], timeout, path) }.to raise_error(Timeout::Error)
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/git/push_spec.rb b/spec/lib/gitlab/git/push_spec.rb
new file mode 100644
index 00000000000..566c8209504
--- /dev/null
+++ b/spec/lib/gitlab/git/push_spec.rb
@@ -0,0 +1,166 @@
+require 'spec_helper'
+
+describe Gitlab::Git::Push do
+ set(:project) { create(:project, :repository) }
+
+ let(:oldrev) { project.commit('HEAD~2').id }
+ let(:newrev) { project.commit.id }
+ let(:ref) { 'refs/heads/some-branch' }
+
+ subject { described_class.new(project, oldrev, newrev, ref) }
+
+ describe '#branch_name' do
+ context 'when it is a branch push' do
+ let(:ref) { 'refs/heads/my-branch' }
+
+ it 'returns branch name' do
+ expect(subject.branch_name).to eq 'my-branch'
+ end
+ end
+
+ context 'when it is a tag push' do
+ let(:ref) { 'refs/tags/my-branch' }
+
+ it 'returns nil' do
+ expect(subject.branch_name).to be_nil
+ end
+ end
+ end
+
+ describe '#branch_push?' do
+ context 'when pushing a branch ref' do
+ let(:ref) { 'refs/heads/my-branch' }
+
+ it { is_expected.to be_branch_push }
+ end
+
+ context 'when it is a tag push' do
+ let(:ref) { 'refs/tags/my-tag' }
+
+ it { is_expected.not_to be_branch_push }
+ end
+ end
+
+ describe '#branch_updated?' do
+ context 'when it is a branch push with correct old and new revisions' do
+ it { is_expected.to be_branch_updated }
+ end
+
+ context 'when it is not a branch push' do
+ let(:ref) { 'refs/tags/my-tag' }
+
+ it { is_expected.not_to be_branch_updated }
+ end
+
+ context 'when old revision is blank' do
+ let(:oldrev) { Gitlab::Git::BLANK_SHA }
+
+ it { is_expected.not_to be_branch_updated }
+ end
+
+ context 'when it is not a branch push' do
+ let(:newrev) { Gitlab::Git::BLANK_SHA }
+
+ it { is_expected.not_to be_branch_updated }
+ end
+
+ context 'when oldrev is nil' do
+ let(:oldrev) { nil }
+
+ it { is_expected.not_to be_branch_updated }
+ end
+ end
+
+ describe '#force_push?' do
+ context 'when old revision is an ancestor of the new revision' do
+ let(:oldrev) { 'HEAD~3' }
+ let(:newrev) { 'HEAD~1' }
+
+ it { is_expected.not_to be_force_push }
+ end
+
+ context 'when old revision is not an ancestor of the new revision' do
+ let(:oldrev) { 'HEAD~3' }
+ let(:newrev) { '123456' }
+
+ it { is_expected.to be_force_push }
+ end
+ end
+
+ describe '#branch_added?' do
+ context 'when old revision is defined' do
+ it { is_expected.not_to be_branch_added }
+ end
+
+ context 'when old revision is not defined' do
+ let(:oldrev) { Gitlab::Git::BLANK_SHA }
+
+ it { is_expected.to be_branch_added }
+ end
+ end
+
+ describe '#branch_removed?' do
+ context 'when new revision is defined' do
+ it { is_expected.not_to be_branch_removed }
+ end
+
+ context 'when new revision is not defined' do
+ let(:newrev) { Gitlab::Git::BLANK_SHA }
+
+ it { is_expected.to be_branch_removed }
+ end
+ end
+
+ describe '#modified_paths' do
+ context 'when a push is a branch update' do
+ let(:newrev) { '498214d' }
+ let(:oldrev) { '281d3a7' }
+
+ it 'returns modified paths' do
+ expect(subject.modified_paths).to eq ['bar/branch-test.txt',
+ 'files/js/commit.coffee',
+ 'with space/README.md']
+ end
+ end
+
+ context 'when a push is not a branch update' do
+ let(:oldrev) { Gitlab::Git::BLANK_SHA }
+
+ it 'raises an error' do
+ expect { subject.modified_paths }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe '#oldrev' do
+ context 'when a valid oldrev is provided' do
+ it 'returns oldrev' do
+ expect(subject.oldrev).to eq oldrev
+ end
+ end
+
+ context 'when a nil valud is provided' do
+ let(:oldrev) { nil }
+
+ it 'returns blank SHA' do
+ expect(subject.oldrev).to eq Gitlab::Git::BLANK_SHA
+ end
+ end
+ end
+
+ describe '#newrev' do
+ context 'when valid newrev is provided' do
+ it 'returns newrev' do
+ expect(subject.newrev).to eq newrev
+ end
+ end
+
+ context 'when a nil valud is provided' do
+ let(:newrev) { nil }
+
+ it 'returns blank SHA' do
+ expect(subject.newrev).to eq Gitlab::Git::BLANK_SHA
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 35a6fc94753..1fe73c12fc0 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -19,7 +19,10 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
+ let(:mutable_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:repository_path) { File.join(TestEnv.repos_path, repository.relative_path) }
+ let(:repository_rugged) { Rugged::Repository.new(repository_path) }
let(:storage_path) { TestEnv.repos_path }
let(:user) { build(:user) }
@@ -71,7 +74,6 @@ describe Gitlab::Git::Repository, :seed_helper do
describe "Respond to" do
subject { repository }
- it { is_expected.to respond_to(:rugged) }
it { is_expected.to respond_to(:root_ref) }
it { is_expected.to respond_to(:tags) }
end
@@ -91,57 +93,6 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
- describe "#rugged" do
- describe 'when storage is broken', :broken_storage do
- it 'raises a storage exception when storage is not available' do
- broken_repo = described_class.new('broken', 'a/path.git', '')
-
- expect { broken_repo.rugged }.to raise_error(Gitlab::Git::Storage::Inaccessible)
- end
- end
-
- it 'raises a no repository exception when there is no repo' do
- broken_repo = described_class.new('default', 'a/path.git', '')
-
- expect do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access { broken_repo.rugged }
- end.to raise_error(Gitlab::Git::Repository::NoRepository)
- end
-
- describe 'alternates keyword argument' do
- context 'with no Git env stored' do
- before do
- allow(Gitlab::Git::HookEnv).to receive(:all).and_return({})
- end
-
- it "is passed an empty array" do
- expect(Rugged::Repository).to receive(:new).with(repository_path, alternates: [])
-
- repository_rugged
- end
- end
-
- context 'with absolute and relative Git object dir envvars stored' do
- before do
- allow(Gitlab::Git::HookEnv).to receive(:all).and_return({
- 'GIT_OBJECT_DIRECTORY_RELATIVE' => './objects/foo',
- 'GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE' => ['./objects/bar', './objects/baz'],
- 'GIT_OBJECT_DIRECTORY' => 'ignored',
- 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => %w[ignored ignored],
- 'GIT_OTHER' => 'another_env'
- })
- end
-
- it "is passed the relative object dir envvars after being converted to absolute ones" do
- alternates = %w[foo bar baz].map { |d| File.join(repository_path, './objects', d) }
- expect(Rugged::Repository).to receive(:new).with(repository_path, alternates: alternates)
-
- repository_rugged
- end
- end
- end
- end
-
describe '#branch_names' do
subject { repository.branch_names }
@@ -284,7 +235,6 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe '#submodule_url_for' do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:ref) { 'master' }
def submodule_url(path)
@@ -322,21 +272,12 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe '#commit_count' do
- shared_examples 'simple commit counting' do
- it { expect(repository.commit_count("master")).to eq(25) }
- it { expect(repository.commit_count("feature")).to eq(9) }
- it { expect(repository.commit_count("does-not-exist")).to eq(0) }
- end
+ it { expect(repository.commit_count("master")).to eq(25) }
+ it { expect(repository.commit_count("feature")).to eq(9) }
+ it { expect(repository.commit_count("does-not-exist")).to eq(0) }
- context 'when Gitaly commit_count feature is enabled' do
- it_behaves_like 'simple commit counting'
- it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::CommitService, :commit_count do
- subject { repository.commit_count('master') }
- end
- end
-
- context 'when Gitaly commit_count feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'simple commit counting'
+ it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::CommitService, :commit_count do
+ subject { repository.commit_count('master') }
end
end
@@ -345,7 +286,7 @@ describe Gitlab::Git::Repository, :seed_helper do
it { expect(repository.has_local_branches?).to eq(true) }
context 'mutable' do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:repository) { mutable_repository }
after do
ensure_seeds
@@ -378,118 +319,82 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe "#delete_branch" do
- shared_examples "deleting a branch" do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:repository) { mutable_repository }
- after do
- ensure_seeds
- end
-
- it "removes the branch from the repo" do
- branch_name = "to-be-deleted-soon"
-
- repository.create_branch(branch_name)
- expect(repository_rugged.branches[branch_name]).not_to be_nil
+ after do
+ ensure_seeds
+ end
- repository.delete_branch(branch_name)
- expect(repository_rugged.branches[branch_name]).to be_nil
- end
+ it "removes the branch from the repo" do
+ branch_name = "to-be-deleted-soon"
- context "when branch does not exist" do
- it "raises a DeleteBranchError exception" do
- expect { repository.delete_branch("this-branch-does-not-exist") }.to raise_error(Gitlab::Git::Repository::DeleteBranchError)
- end
- end
- end
+ repository.create_branch(branch_name)
+ expect(repository_rugged.branches[branch_name]).not_to be_nil
- context "when Gitaly delete_branch is enabled" do
- it_behaves_like "deleting a branch"
+ repository.delete_branch(branch_name)
+ expect(repository_rugged.branches[branch_name]).to be_nil
end
- context "when Gitaly delete_branch is disabled", :skip_gitaly_mock do
- it_behaves_like "deleting a branch"
+ context "when branch does not exist" do
+ it "raises a DeleteBranchError exception" do
+ expect { repository.delete_branch("this-branch-does-not-exist") }.to raise_error(Gitlab::Git::Repository::DeleteBranchError)
+ end
end
end
describe "#create_branch" do
- shared_examples 'creating a branch' do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
-
- after do
- ensure_seeds
- end
+ let(:repository) { mutable_repository }
- it "should create a new branch" do
- expect(repository.create_branch('new_branch', 'master')).not_to be_nil
- end
-
- it "should create a new branch with the right name" do
- expect(repository.create_branch('another_branch', 'master').name).to eq('another_branch')
- end
+ after do
+ ensure_seeds
+ end
- it "should fail if we create an existing branch" do
- repository.create_branch('duplicated_branch', 'master')
- expect {repository.create_branch('duplicated_branch', 'master')}.to raise_error("Branch duplicated_branch already exists")
- end
+ it "should create a new branch" do
+ expect(repository.create_branch('new_branch', 'master')).not_to be_nil
+ end
- it "should fail if we create a branch from a non existing ref" do
- expect {repository.create_branch('branch_based_in_wrong_ref', 'master_2_the_revenge')}.to raise_error("Invalid reference master_2_the_revenge")
- end
+ it "should create a new branch with the right name" do
+ expect(repository.create_branch('another_branch', 'master').name).to eq('another_branch')
end
- context 'when Gitaly create_branch feature is enabled' do
- it_behaves_like 'creating a branch'
+ it "should fail if we create an existing branch" do
+ repository.create_branch('duplicated_branch', 'master')
+ expect {repository.create_branch('duplicated_branch', 'master')}.to raise_error("Branch duplicated_branch already exists")
end
- context 'when Gitaly create_branch feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'creating a branch'
+ it "should fail if we create a branch from a non existing ref" do
+ expect {repository.create_branch('branch_based_in_wrong_ref', 'master_2_the_revenge')}.to raise_error("Invalid reference master_2_the_revenge")
end
end
describe '#delete_refs' do
- shared_examples 'deleting refs' do
- let(:repo) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
-
- def repo_rugged
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repo.rugged
- end
- end
+ let(:repository) { mutable_repository }
- after do
- ensure_seeds
- end
-
- it 'deletes the ref' do
- repo.delete_refs('refs/heads/feature')
-
- expect(repo_rugged.references['refs/heads/feature']).to be_nil
- end
+ after do
+ ensure_seeds
+ end
- it 'deletes all refs' do
- refs = %w[refs/heads/wip refs/tags/v1.1.0]
- repo.delete_refs(*refs)
+ it 'deletes the ref' do
+ repository.delete_refs('refs/heads/feature')
- refs.each do |ref|
- expect(repo_rugged.references[ref]).to be_nil
- end
- end
+ expect(repository_rugged.references['refs/heads/feature']).to be_nil
+ end
- it 'does not fail when deleting an empty list of refs' do
- expect { repo.delete_refs(*[]) }.not_to raise_error
- end
+ it 'deletes all refs' do
+ refs = %w[refs/heads/wip refs/tags/v1.1.0]
+ repository.delete_refs(*refs)
- it 'raises an error if it failed' do
- expect { repo.delete_refs('refs\heads\fix') }.to raise_error(Gitlab::Git::Repository::GitError)
+ refs.each do |ref|
+ expect(repository_rugged.references[ref]).to be_nil
end
end
- context 'when Gitaly delete_refs feature is enabled' do
- it_behaves_like 'deleting refs'
+ it 'does not fail when deleting an empty list of refs' do
+ expect { repository.delete_refs(*[]) }.not_to raise_error
end
- context 'when Gitaly delete_refs feature is disabled', :disable_gitaly do
- it_behaves_like 'deleting refs'
+ it 'raises an error if it failed' do
+ expect { repository.delete_refs('refs\heads\fix') }.to raise_error(Gitlab::Git::Repository::GitError)
end
end
@@ -542,44 +447,84 @@ describe Gitlab::Git::Repository, :seed_helper do
Gitlab::Shell.new.remove_repository('default', 'my_project')
end
- shared_examples 'repository mirror fecthing' do
- it 'fetches a repository as a mirror remote' do
+ it 'fetches a repository as a mirror remote' do
+ subject
+
+ expect(refs(new_repository_path)).to eq(refs(repository_path))
+ end
+
+ context 'with keep-around refs' do
+ let(:sha) { SeedRepo::Commit::ID }
+ let(:keep_around_ref) { "refs/keep-around/#{sha}" }
+ let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" }
+
+ before do
+ repository_rugged.references.create(keep_around_ref, sha, force: true)
+ repository_rugged.references.create(tmp_ref, sha, force: true)
+ end
+
+ it 'includes the temporary and keep-around refs' do
subject
- expect(refs(new_repository_path)).to eq(refs(repository_path))
+ expect(refs(new_repository_path)).to include(keep_around_ref)
+ expect(refs(new_repository_path)).to include(tmp_ref)
end
+ end
- context 'with keep-around refs' do
- let(:sha) { SeedRepo::Commit::ID }
- let(:keep_around_ref) { "refs/keep-around/#{sha}" }
- let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" }
+ def new_repository_path
+ File.join(TestEnv.repos_path, new_repository.relative_path)
+ end
+ end
- before do
- repository_rugged.references.create(keep_around_ref, sha, force: true)
- repository_rugged.references.create(tmp_ref, sha, force: true)
- end
+ describe '#fetch_remote' do
+ it 'delegates to the gitaly RepositoryService' do
+ ssh_auth = double(:ssh_auth)
+ expected_opts = {
+ ssh_auth: ssh_auth,
+ forced: true,
+ no_tags: true,
+ timeout: described_class::GITLAB_PROJECTS_TIMEOUT,
+ prune: false
+ }
- it 'includes the temporary and keep-around refs' do
- subject
+ expect(repository.gitaly_repository_client).to receive(:fetch_remote).with('remote-name', expected_opts)
- expect(refs(new_repository_path)).to include(keep_around_ref)
- expect(refs(new_repository_path)).to include(tmp_ref)
- end
- end
+ repository.fetch_remote('remote-name', ssh_auth: ssh_auth, forced: true, no_tags: true, prune: false)
end
- context 'with gitaly enabled' do
- it_behaves_like 'repository mirror fecthing'
+ it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RepositoryService, :fetch_remote do
+ subject { repository.fetch_remote('remote-name') }
end
+ end
- context 'with gitaly enabled', :skip_gitaly_mock do
- it_behaves_like 'repository mirror fecthing'
+ describe '#find_remote_root_ref' do
+ it 'gets the remote root ref from GitalyClient' do
+ expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
+ .to receive(:find_remote_root_ref).and_call_original
+
+ expect(repository.find_remote_root_ref('origin')).to eq 'master'
end
- def new_repository_path
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- new_repository.path
- end
+ it 'returns UTF-8' do
+ expect(repository.find_remote_root_ref('origin')).to be_utf8
+ end
+
+ it 'returns nil when remote name is nil' do
+ expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
+ .not_to receive(:find_remote_root_ref)
+
+ expect(repository.find_remote_root_ref(nil)).to be_nil
+ end
+
+ it 'returns nil when remote name is empty' do
+ expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
+ .not_to receive(:find_remote_root_ref)
+
+ expect(repository.find_remote_root_ref('')).to be_nil
+ end
+
+ it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RemoteService, :find_remote_root_ref do
+ subject { repository.find_remote_root_ref('origin') }
end
end
@@ -595,18 +540,16 @@ describe Gitlab::Git::Repository, :seed_helper do
Gitlab::Git::Commit.find(repository, @rename_commit_id)
end
- before(:context) do
+ before do
# Add new commits so that there's a renamed file in the commit history
- repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- @commit_with_old_name_id = new_commit_edit_old_file(repo).oid
- @rename_commit_id = new_commit_move_file(repo).oid
- @commit_with_new_name_id = new_commit_edit_new_file(repo).oid
+ @commit_with_old_name_id = new_commit_edit_old_file(repository_rugged).oid
+ @rename_commit_id = new_commit_move_file(repository_rugged).oid
+ @commit_with_new_name_id = new_commit_edit_new_file(repository_rugged).oid
end
- after(:context) do
+ after do
# Erase our commits so other tests get the original repo
- repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID)
+ repository_rugged.references.update("refs/heads/master", SeedRepo::LastCommit::ID)
end
context "where 'follow' == true" do
@@ -887,25 +830,15 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe '#merge_base' do
- shared_examples '#merge_base' do
- where(:from, :to, :result) do
- '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d'
- '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d'
- '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | 'foobar' | nil
- 'foobar' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | nil
- end
-
- with_them do
- it { expect(repository.merge_base(from, to)).to eq(result) }
- end
+ where(:from, :to, :result) do
+ '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d'
+ '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d'
+ '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | 'foobar' | nil
+ 'foobar' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | nil
end
- context 'with gitaly' do
- it_behaves_like '#merge_base'
- end
-
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#merge_base'
+ with_them do
+ it { expect(repository.merge_base(from, to)).to eq(result) }
end
end
@@ -997,54 +930,6 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
- describe '#autocrlf' do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- @repo.rugged.config['core.autocrlf'] = true
- end
-
- around do |example|
- # OK because autocrlf is only used in gitaly-ruby
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- it 'return the value of the autocrlf option' do
- expect(@repo.autocrlf).to be(true)
- end
-
- after(:all) do
- @repo.rugged.config.delete('core.autocrlf')
- end
- end
-
- describe '#autocrlf=' do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- @repo.rugged.config['core.autocrlf'] = false
- end
-
- around do |example|
- # OK because autocrlf= is only used in gitaly-ruby
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- it 'should set the autocrlf option to the provided option' do
- @repo.autocrlf = :input
-
- File.open(File.join(SEED_STORAGE_PATH, TEST_MUTABLE_REPO_PATH, 'config')) do |config_file|
- expect(config_file.read).to match('autocrlf = input')
- end
- end
-
- after(:all) do
- @repo.rugged.config.delete('core.autocrlf')
- end
- end
-
describe '#find_branch' do
it 'should return a Branch for master' do
branch = repository.find_branch('master')
@@ -1086,12 +971,10 @@ describe Gitlab::Git::Repository, :seed_helper do
subject { repository.branches }
context 'with local and remote branches' do
- let(:repository) do
- Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- end
+ let(:repository) { mutable_repository }
before do
- create_remote_branch(repository, 'joe', 'remote_branch', 'master')
+ create_remote_branch('joe', 'remote_branch', 'master')
repository.create_branch('local_branch', 'master')
end
@@ -1114,12 +997,10 @@ describe Gitlab::Git::Repository, :seed_helper do
end
context 'with local and remote branches' do
- let(:repository) do
- Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- end
+ let(:repository) { mutable_repository }
before do
- create_remote_branch(repository, 'joe', 'remote_branch', 'master')
+ create_remote_branch('joe', 'remote_branch', 'master')
repository.create_branch('local_branch', 'master')
end
@@ -1144,57 +1025,95 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe '#merged_branch_names' do
- shared_examples 'finding merged branch names' do
- context 'when branch names are passed' do
- it 'only returns the names we are asking' do
- names = repository.merged_branch_names(%w[merge-test])
+ context 'when branch names are passed' do
+ it 'only returns the names we are asking' do
+ names = repository.merged_branch_names(%w[merge-test])
- expect(names).to contain_exactly('merge-test')
- end
+ expect(names).to contain_exactly('merge-test')
+ end
- it 'does not return unmerged branch names' do
- names = repository.merged_branch_names(%w[feature])
+ it 'does not return unmerged branch names' do
+ names = repository.merged_branch_names(%w[feature])
- expect(names).to be_empty
- end
+ expect(names).to be_empty
end
+ end
- context 'when no root ref is available' do
- it 'returns empty list' do
- project = create(:project, :empty_repo)
+ context 'when no root ref is available' do
+ it 'returns empty list' do
+ project = create(:project, :empty_repo)
- names = project.repository.merged_branch_names(%w[feature])
+ names = project.repository.merged_branch_names(%w[feature])
- expect(names).to be_empty
- end
+ expect(names).to be_empty
end
+ end
- context 'when no branch names are specified' do
- before do
- repository.create_branch('identical', 'master')
- end
+ context 'when no branch names are specified' do
+ before do
+ repository.create_branch('identical', 'master')
+ end
- after do
- ensure_seeds
- end
+ after do
+ ensure_seeds
+ end
- it 'returns all merged branch names except for identical one' do
- names = repository.merged_branch_names
+ it 'returns all merged branch names except for identical one' do
+ names = repository.merged_branch_names
- expect(names).to include('merge-test')
- expect(names).to include('fix-mode')
- expect(names).not_to include('feature')
- expect(names).not_to include('identical')
- end
+ expect(names).to include('merge-test')
+ expect(names).to include('fix-mode')
+ expect(names).not_to include('feature')
+ expect(names).not_to include('identical')
end
end
+ end
+
+ describe '#diff_stats' do
+ let(:left_commit_id) { 'feature' }
+ let(:right_commit_id) { 'master' }
- context 'when Gitaly merged_branch_names feature is enabled' do
- it_behaves_like 'finding merged branch names'
+ it 'returns a DiffStatsCollection' do
+ collection = repository.diff_stats(left_commit_id, right_commit_id)
+
+ expect(collection).to be_a(Gitlab::Git::DiffStatsCollection)
+ expect(collection).to be_a(Enumerable)
+ end
+
+ it 'yields Gitaly::DiffStats objects' do
+ collection = repository.diff_stats(left_commit_id, right_commit_id)
+
+ expect(collection.to_a).to all(be_a(Gitaly::DiffStats))
+ end
+
+ it 'returns no Gitaly::DiffStats when SHAs are invalid' do
+ collection = repository.diff_stats('foo', 'bar')
+
+ expect(collection).to be_a(Gitlab::Git::DiffStatsCollection)
+ expect(collection).to be_a(Enumerable)
+ expect(collection.to_a).to be_empty
end
- context 'when Gitaly merged_branch_names feature is disabled', :disable_gitaly do
- it_behaves_like 'finding merged branch names'
+ it 'returns no Gitaly::DiffStats when there is a nil SHA' do
+ expect_any_instance_of(Gitlab::GitalyClient::CommitService)
+ .not_to receive(:diff_stats)
+
+ collection = repository.diff_stats(nil, 'master')
+
+ expect(collection).to be_a(Gitlab::Git::DiffStatsCollection)
+ expect(collection).to be_a(Enumerable)
+ expect(collection.to_a).to be_empty
+ end
+
+ it 'returns no Gitaly::DiffStats when there is a BLANK_SHA' do
+ expect_any_instance_of(Gitlab::GitalyClient::CommitService)
+ .not_to receive(:diff_stats)
+
+ collection = repository.diff_stats(Gitlab::Git::BLANK_SHA, 'master')
+
+ expect(collection).to be_a(Gitlab::Git::DiffStatsCollection)
+ expect(collection).to be_a(Enumerable)
+ expect(collection.to_a).to be_empty
end
end
@@ -1310,99 +1229,97 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
- describe '#ref_exists?' do
- shared_examples 'checks the existence of refs' do
- it 'returns true for an existing tag' do
- expect(repository.ref_exists?('refs/heads/master')).to eq(true)
- end
+ describe '#gitattribute' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '') }
- it 'returns false for a non-existing tag' do
- expect(repository.ref_exists?('refs/tags/THIS_TAG_DOES_NOT_EXIST')).to eq(false)
- end
-
- it 'raises an ArgumentError for an empty string' do
- expect { repository.ref_exists?('') }.to raise_error(ArgumentError)
- end
+ after do
+ ensure_seeds
+ end
- it 'raises an ArgumentError for an invalid ref' do
- expect { repository.ref_exists?('INVALID') }.to raise_error(ArgumentError)
- end
+ it 'returns matching language attribute' do
+ expect(repository.gitattribute("custom-highlighting/test.gitlab-custom", 'gitlab-language')).to eq('ruby')
end
- context 'when Gitaly ref_exists feature is enabled' do
- it_behaves_like 'checks the existence of refs'
+ it 'returns matching language attribute with additional options' do
+ expect(repository.gitattribute("custom-highlighting/test.gitlab-cgi", 'gitlab-language')).to eq('erb?parent=json')
end
- context 'when Gitaly ref_exists feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'checks the existence of refs'
+ it 'returns nil if nothing matches' do
+ expect(repository.gitattribute("report.xslt", 'gitlab-language')).to eq(nil)
end
- end
- describe '#tag_exists?' do
- shared_examples 'checks the existence of tags' do
- it 'returns true for an existing tag' do
- tag = repository.tag_names.first
+ context 'without gitattributes file' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
- expect(repository.tag_exists?(tag)).to eq(true)
+ it 'returns nil' do
+ expect(repository.gitattribute("README.md", 'gitlab-language')).to eq(nil)
end
+ end
+ end
- it 'returns false for a non-existing tag' do
- expect(repository.tag_exists?('v9000')).to eq(false)
- end
+ describe '#ref_exists?' do
+ it 'returns true for an existing tag' do
+ expect(repository.ref_exists?('refs/heads/master')).to eq(true)
end
- context 'when Gitaly ref_exists_tags feature is enabled' do
- it_behaves_like 'checks the existence of tags'
+ it 'returns false for a non-existing tag' do
+ expect(repository.ref_exists?('refs/tags/THIS_TAG_DOES_NOT_EXIST')).to eq(false)
end
- context 'when Gitaly ref_exists_tags feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'checks the existence of tags'
+ it 'raises an ArgumentError for an empty string' do
+ expect { repository.ref_exists?('') }.to raise_error(ArgumentError)
+ end
+
+ it 'raises an ArgumentError for an invalid ref' do
+ expect { repository.ref_exists?('INVALID') }.to raise_error(ArgumentError)
end
end
- describe '#branch_exists?' do
- shared_examples 'checks the existence of branches' do
- it 'returns true for an existing branch' do
- expect(repository.branch_exists?('master')).to eq(true)
- end
+ describe '#tag_exists?' do
+ it 'returns true for an existing tag' do
+ tag = repository.tag_names.first
- it 'returns false for a non-existing branch' do
- expect(repository.branch_exists?('kittens')).to eq(false)
- end
+ expect(repository.tag_exists?(tag)).to eq(true)
+ end
- it 'returns false when using an invalid branch name' do
- expect(repository.branch_exists?('.bla')).to eq(false)
- end
+ it 'returns false for a non-existing tag' do
+ expect(repository.tag_exists?('v9000')).to eq(false)
+ end
+ end
+
+ describe '#branch_exists?' do
+ it 'returns true for an existing branch' do
+ expect(repository.branch_exists?('master')).to eq(true)
end
- context 'when Gitaly ref_exists_branches feature is enabled' do
- it_behaves_like 'checks the existence of branches'
+ it 'returns false for a non-existing branch' do
+ expect(repository.branch_exists?('kittens')).to eq(false)
end
- context 'when Gitaly ref_exists_branches feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'checks the existence of branches'
+ it 'returns false when using an invalid branch name' do
+ expect(repository.branch_exists?('.bla')).to eq(false)
end
end
describe '#local_branches' do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
+ let(:repository) { mutable_repository }
+
+ before do
+ create_remote_branch('joe', 'remote_branch', 'master')
+ repository.create_branch('local_branch', 'master')
end
- after(:all) do
+ after do
ensure_seeds
end
it 'returns the local branches' do
- create_remote_branch(@repo, 'joe', 'remote_branch', 'master')
- @repo.create_branch('local_branch', 'master')
-
- expect(@repo.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false)
- expect(@repo.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true)
+ expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false)
+ expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true)
end
it 'returns a Branch with UTF-8 fields' do
- branches = @repo.local_branches.to_a
+ branches = repository.local_branches.to_a
expect(branches.size).to be > 0
branches.each do |branch|
expect(branch.name).to be_utf8
@@ -1413,11 +1330,11 @@ describe Gitlab::Git::Repository, :seed_helper do
it 'gets the branches from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:local_branches)
.and_return([])
- @repo.local_branches
+ repository.local_branches
end
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :local_branches do
- subject { @repo.local_branches }
+ subject { repository.local_branches }
end
end
@@ -1471,56 +1388,9 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
- describe '#with_repo_branch_commit' do
- context 'when comparing with the same repository' do
- let(:start_repository) { repository }
-
- context 'when the branch exists' do
- let(:start_branch_name) { 'master' }
-
- it 'yields the commit' do
- expect { |b| repository.with_repo_branch_commit(start_repository, start_branch_name, &b) }
- .to yield_with_args(an_instance_of(Gitlab::Git::Commit))
- end
- end
-
- context 'when the branch does not exist' do
- let(:start_branch_name) { 'definitely-not-master' }
-
- it 'yields nil' do
- expect { |b| repository.with_repo_branch_commit(start_repository, start_branch_name, &b) }
- .to yield_with_args(nil)
- end
- end
- end
-
- context 'when comparing with another repository' do
- let(:start_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
-
- context 'when the branch exists' do
- let(:start_branch_name) { 'master' }
-
- it 'yields the commit' do
- expect { |b| repository.with_repo_branch_commit(start_repository, start_branch_name, &b) }
- .to yield_with_args(an_instance_of(Gitlab::Git::Commit))
- end
- end
-
- context 'when the branch does not exist' do
- let(:start_branch_name) { 'definitely-not-master' }
-
- it 'yields nil' do
- expect { |b| repository.with_repo_branch_commit(start_repository, start_branch_name, &b) }
- .to yield_with_args(nil)
- end
- end
- end
- end
-
describe '#fetch_source_branch!' do
let(:local_ref) { 'refs/merge-requests/1/head' }
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
- let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:source_repository) { mutable_repository }
after do
ensure_seeds
@@ -1529,7 +1399,8 @@ describe Gitlab::Git::Repository, :seed_helper do
context 'when the branch exists' do
context 'when the commit does not exist locally' do
let(:source_branch) { 'new-branch-for-fetch-source-branch' }
- let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } }
+ let(:source_path) { File.join(TestEnv.repos_path, source_repository.relative_path) }
+ let(:source_rugged) { Rugged::Repository.new(source_path) }
let(:new_oid) { new_commit_edit_old_file(source_rugged).oid }
before do
@@ -1567,29 +1438,19 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe '#rm_branch' do
- shared_examples "user deleting a branch" do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository.raw }
- let(:branch_name) { "to-be-deleted-soon" }
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository.raw }
+ let(:branch_name) { "to-be-deleted-soon" }
- before do
- project.add_developer(user)
- repository.create_branch(branch_name)
- end
-
- it "removes the branch from the repo" do
- repository.rm_branch(branch_name, user: user)
-
- expect(repository_rugged.branches[branch_name]).to be_nil
- end
+ before do
+ project.add_developer(user)
+ repository.create_branch(branch_name)
end
- context "when Gitaly user_delete_branch is enabled" do
- it_behaves_like "user deleting a branch"
- end
+ it "removes the branch from the repo" do
+ repository.rm_branch(branch_name, user: user)
- context "when Gitaly user_delete_branch is disabled", :skip_gitaly_mock do
- it_behaves_like "user deleting a branch"
+ expect(repository_rugged.branches[branch_name]).to be_nil
end
end
@@ -1651,8 +1512,7 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe '#set_config' do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
- let(:rugged) { repository_rugged }
+ let(:repository) { mutable_repository }
let(:entries) do
{
'test.foo1' => 'bla bla',
@@ -1664,19 +1524,18 @@ describe Gitlab::Git::Repository, :seed_helper do
it 'can set config settings' do
expect(repository.set_config(entries)).to be_nil
- expect(rugged.config['test.foo1']).to eq('bla bla')
- expect(rugged.config['test.foo2']).to eq('1234')
- expect(rugged.config['test.foo3']).to eq('true')
+ expect(repository_rugged.config['test.foo1']).to eq('bla bla')
+ expect(repository_rugged.config['test.foo2']).to eq('1234')
+ expect(repository_rugged.config['test.foo3']).to eq('true')
end
after do
- entries.keys.each { |k| rugged.config.delete(k) }
+ entries.keys.each { |k| repository_rugged.config.delete(k) }
end
end
describe '#delete_config' do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
- let(:rugged) { repository_rugged }
+ let(:repository) { mutable_repository }
let(:entries) do
{
'test.foo1' => 'bla bla',
@@ -1687,21 +1546,19 @@ describe Gitlab::Git::Repository, :seed_helper do
it 'can delete config settings' do
entries.each do |key, value|
- rugged.config[key] = value
+ repository_rugged.config[key] = value
end
expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil
- config_keys = rugged.config.each_key.to_a
+ config_keys = repository_rugged.config.each_key.to_a
expect(config_keys).not_to include('test.foo1')
expect(config_keys).not_to include('test.foo2')
end
end
describe '#merge' do
- let(:repository) do
- Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- end
+ let(:repository) { mutable_repository }
let(:source_sha) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' }
let(:target_branch) { 'test-merge-target-branch' }
@@ -1713,46 +1570,34 @@ describe Gitlab::Git::Repository, :seed_helper do
ensure_seeds
end
- shared_examples '#merge' do
- it 'can perform a merge' do
- merge_commit_id = nil
- result = repository.merge(user, source_sha, target_branch, 'Test merge') do |commit_id|
- merge_commit_id = commit_id
- end
-
- expect(result.newrev).to eq(merge_commit_id)
- expect(result.repo_created).to eq(false)
- expect(result.branch_created).to eq(false)
+ it 'can perform a merge' do
+ merge_commit_id = nil
+ result = repository.merge(user, source_sha, target_branch, 'Test merge') do |commit_id|
+ merge_commit_id = commit_id
end
- it 'returns nil if there was a concurrent branch update' do
- concurrent_update_id = '33f3729a45c02fc67d00adb1b8bca394b0e761d9'
- result = repository.merge(user, source_sha, target_branch, 'Test merge') do
- # This ref update should make the merge fail
- repository.write_ref(Gitlab::Git::BRANCH_REF_PREFIX + target_branch, concurrent_update_id)
- end
-
- # This 'nil' signals that the merge was not applied
- expect(result).to be_nil
+ expect(result.newrev).to eq(merge_commit_id)
+ expect(result.repo_created).to eq(false)
+ expect(result.branch_created).to eq(false)
+ end
- # Our concurrent ref update should not have been undone
- expect(repository.find_branch(target_branch).target).to eq(concurrent_update_id)
+ it 'returns nil if there was a concurrent branch update' do
+ concurrent_update_id = '33f3729a45c02fc67d00adb1b8bca394b0e761d9'
+ result = repository.merge(user, source_sha, target_branch, 'Test merge') do
+ # This ref update should make the merge fail
+ repository.write_ref(Gitlab::Git::BRANCH_REF_PREFIX + target_branch, concurrent_update_id)
end
- end
- context 'with gitaly' do
- it_behaves_like '#merge'
- end
+ # This 'nil' signals that the merge was not applied
+ expect(result).to be_nil
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#merge'
+ # Our concurrent ref update should not have been undone
+ expect(repository.find_branch(target_branch).target).to eq(concurrent_update_id)
end
end
describe '#ff_merge' do
- let(:repository) do
- Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- end
+ let(:repository) { mutable_repository }
let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
let(:source_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
let(:target_branch) { 'test-ff-target-branch' }
@@ -1815,9 +1660,7 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe '#delete_all_refs_except' do
- let(:repository) do
- Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- end
+ let(:repository) { mutable_repository }
before do
repository.write_ref("refs/delete/a", "0b4bc9a49b562e85de7cc9e834518ea6828729b9")
@@ -1841,12 +1684,7 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe 'remotes' do
- let(:repository) do
- Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- end
- let(:rugged) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged }
- end
+ let(:repository) { mutable_repository }
let(:remote_name) { 'my-remote' }
let(:url) { 'http://my-repo.git' }
@@ -1857,88 +1695,47 @@ describe Gitlab::Git::Repository, :seed_helper do
describe '#add_remote' do
let(:mirror_refmap) { '+refs/*:refs/*' }
- shared_examples 'add_remote' do
- it 'added the remote' do
- begin
- rugged.remotes.delete(remote_name)
- rescue Rugged::ConfigError
- end
-
- repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
-
- expect(rugged.remotes[remote_name]).not_to be_nil
- expect(rugged.config["remote.#{remote_name}.mirror"]).to eq('true')
- expect(rugged.config["remote.#{remote_name}.prune"]).to eq('true')
- expect(rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap)
+ it 'added the remote' do
+ begin
+ repository_rugged.remotes.delete(remote_name)
+ rescue Rugged::ConfigError
end
- end
- context 'using Gitaly' do
- it_behaves_like 'add_remote'
- end
+ repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
- context 'with Gitaly disabled', :disable_gitaly do
- it_behaves_like 'add_remote'
+ expect(repository_rugged.remotes[remote_name]).not_to be_nil
+ expect(repository_rugged.config["remote.#{remote_name}.mirror"]).to eq('true')
+ expect(repository_rugged.config["remote.#{remote_name}.prune"]).to eq('true')
+ expect(repository_rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap)
end
end
describe '#remove_remote' do
- shared_examples 'remove_remote' do
- it 'removes the remote' do
- rugged.remotes.create(remote_name, url)
-
- repository.remove_remote(remote_name)
-
- expect(rugged.remotes[remote_name]).to be_nil
- end
- end
-
- context 'using Gitaly' do
- it_behaves_like 'remove_remote'
- end
-
- context 'with Gitaly disabled', :disable_gitaly do
- it_behaves_like 'remove_remote'
- end
- end
- end
+ it 'removes the remote' do
+ repository_rugged.remotes.create(remote_name, url)
- describe '#gitlab_projects' do
- subject { repository.gitlab_projects }
+ repository.remove_remote(remote_name)
- it do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- expect(subject.shard_path).to eq(storage_path)
+ expect(repository_rugged.remotes[remote_name]).to be_nil
end
end
- it { expect(subject.repository_relative_path).to eq(repository.relative_path) }
end
describe '#bundle_to_disk' do
- shared_examples 'bundling to disk' do
- let(:save_path) { File.join(Dir.tmpdir, "repo-#{SecureRandom.hex}.bundle") }
+ let(:save_path) { File.join(Dir.tmpdir, "repo-#{SecureRandom.hex}.bundle") }
- after do
- FileUtils.rm_rf(save_path)
- end
-
- it 'saves a bundle to disk' do
- repository.bundle_to_disk(save_path)
-
- success = system(
- *%W(#{Gitlab.config.git.bin_path} -C #{repository_path} bundle verify #{save_path}),
- [:out, :err] => '/dev/null'
- )
- expect(success).to be true
- end
+ after do
+ FileUtils.rm_rf(save_path)
end
- context 'when Gitaly bundle_to_disk feature is enabled' do
- it_behaves_like 'bundling to disk'
- end
+ it 'saves a bundle to disk' do
+ repository.bundle_to_disk(save_path)
- context 'when Gitaly bundle_to_disk feature is disabled', :disable_gitaly do
- it_behaves_like 'bundling to disk'
+ success = system(
+ *%W(#{Gitlab.config.git.bin_path} -C #{repository_path} bundle verify #{save_path}),
+ [:out, :err] => '/dev/null'
+ )
+ expect(success).to be true
end
end
@@ -1975,7 +1772,7 @@ describe Gitlab::Git::Repository, :seed_helper do
describe '#checksum' do
it 'calculates the checksum for non-empty repo' do
- expect(repository.checksum).to eq '4be7d24ce7e8d845502d599b72d567d23e6a40c0'
+ expect(repository.checksum).to eq '51d0a9662681f93e1fee547a6b7ba2bcaf716059'
end
it 'returns 0000000000000000000000000000000000000000 for an empty repo' do
@@ -2013,138 +1810,41 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
- context 'gitlab_projects commands' do
- let(:gitlab_projects) { repository.gitlab_projects }
- let(:timeout) { Gitlab.config.gitlab_shell.git_timeout }
+ describe '#clean_stale_repository_files' do
+ let(:worktree_path) { File.join(repository_path, 'worktrees', 'delete-me') }
- describe '#push_remote_branches' do
- subject do
- repository.push_remote_branches('downstream-remote', ['master'])
- end
+ it 'cleans up the files' do
+ create_worktree = %W[git -C #{repository_path} worktree add --detach #{worktree_path} master]
+ raise 'preparation failed' unless system(*create_worktree, err: '/dev/null')
- it 'executes the command' do
- expect(gitlab_projects).to receive(:push_branches)
- .with('downstream-remote', timeout, true, ['master'])
- .and_return(true)
+ FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
+ # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
+ # but the HEAD must be 40 characters long or git will ignore it.
+ File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
- is_expected.to be_truthy
- end
+ # git 2.16 fails with "fatal: bad object HEAD"
+ expect(rev_list_all).to be false
- it 'raises an error if the command fails' do
- allow(gitlab_projects).to receive(:output) { 'error' }
- expect(gitlab_projects).to receive(:push_branches)
- .with('downstream-remote', timeout, true, ['master'])
- .and_return(false)
+ repository.clean_stale_repository_files
- expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
- end
+ expect(rev_list_all).to be true
+ expect(File.exist?(worktree_path)).to be_falsey
end
- describe '#delete_remote_branches' do
- subject do
- repository.delete_remote_branches('downstream-remote', ['master'])
- end
-
- it 'executes the command' do
- expect(gitlab_projects).to receive(:delete_remote_branches)
- .with('downstream-remote', ['master'])
- .and_return(true)
-
- is_expected.to be_truthy
- end
-
- it 'raises an error if the command fails' do
- allow(gitlab_projects).to receive(:output) { 'error' }
- expect(gitlab_projects).to receive(:delete_remote_branches)
- .with('downstream-remote', ['master'])
- .and_return(false)
-
- expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
- end
+ def rev_list_all
+ system(*%W[git -C #{repository_path} rev-list --all], out: '/dev/null', err: '/dev/null')
end
- describe '#delete_remote_branches' do
- subject do
- repository.delete_remote_branches('downstream-remote', ['master'])
- end
-
- it 'executes the command' do
- expect(gitlab_projects).to receive(:delete_remote_branches)
- .with('downstream-remote', ['master'])
- .and_return(true)
+ it 'increments a counter upon an error' do
+ expect(repository.gitaly_repository_client).to receive(:cleanup).and_raise(Gitlab::Git::CommandError)
- is_expected.to be_truthy
- end
+ counter = double(:counter)
- it 'raises an error if the command fails' do
- allow(gitlab_projects).to receive(:output) { 'error' }
- expect(gitlab_projects).to receive(:delete_remote_branches)
- .with('downstream-remote', ['master'])
- .and_return(false)
+ expect(counter).to receive(:increment)
+ expect(Gitlab::Metrics).to receive(:counter).with(:failed_repository_cleanup_total,
+ 'Number of failed repository cleanup events').and_return(counter)
- expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
- end
- end
-
- describe '#clean_stale_repository_files' do
- let(:worktree_path) { File.join(repository_path, 'worktrees', 'delete-me') }
-
- it 'cleans up the files' do
- create_worktree = %W[git -C #{repository_path} worktree add --detach #{worktree_path} master]
- raise 'preparation failed' unless system(*create_worktree, err: '/dev/null')
-
- FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
- # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
- # but the HEAD must be 40 characters long or git will ignore it.
- File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
-
- # git 2.16 fails with "fatal: bad object HEAD"
- expect(rev_list_all).to be false
-
- repository.clean_stale_repository_files
-
- expect(rev_list_all).to be true
- expect(File.exist?(worktree_path)).to be_falsey
- end
-
- def rev_list_all
- system(*%W[git -C #{repository_path} rev-list --all], out: '/dev/null', err: '/dev/null')
- end
-
- it 'increments a counter upon an error' do
- expect(repository.gitaly_repository_client).to receive(:cleanup).and_raise(Gitlab::Git::CommandError)
-
- counter = double(:counter)
-
- expect(counter).to receive(:increment)
- expect(Gitlab::Metrics).to receive(:counter).with(:failed_repository_cleanup_total,
- 'Number of failed repository cleanup events').and_return(counter)
-
- repository.clean_stale_repository_files
- end
- end
-
- describe '#delete_remote_branches' do
- subject do
- repository.delete_remote_branches('downstream-remote', ['master'])
- end
-
- it 'executes the command' do
- expect(gitlab_projects).to receive(:delete_remote_branches)
- .with('downstream-remote', ['master'])
- .and_return(true)
-
- is_expected.to be_truthy
- end
-
- it 'raises an error if the command fails' do
- allow(gitlab_projects).to receive(:output) { 'error' }
- expect(gitlab_projects).to receive(:delete_remote_branches)
- .with('downstream-remote', ['master'])
- .and_return(false)
-
- expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
- end
+ repository.clean_stale_repository_files
end
end
@@ -2187,13 +1887,11 @@ describe Gitlab::Git::Repository, :seed_helper do
end
context 'when the diff contains a rename' do
- let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged }
- let(:end_sha) { new_commit_move_file(repo).oid }
+ let(:end_sha) { new_commit_move_file(repository_rugged).oid }
after do
# Erase our commits so other tests get the original repo
- repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID)
+ repository_rugged.references.update('refs/heads/master', SeedRepo::LastCommit::ID)
end
it 'does not include the renamed file in the sparse checkout' do
@@ -2240,10 +1938,9 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
- def create_remote_branch(repository, remote_name, branch_name, source_branch_name)
+ def create_remote_branch(remote_name, branch_name, source_branch_name)
source_branch = repository.branches.find { |branch| branch.name == source_branch_name }
- rugged = repository_rugged
- rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha)
+ repository_rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha)
end
# Build the options hash that's passed to Rugged::Commit#create
@@ -2321,16 +2018,4 @@ describe Gitlab::Git::Repository, :seed_helper do
line.split("\t").last
end
end
-
- def repository_rugged
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged
- end
- end
-
- def repository_path
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.path
- end
- end
end
diff --git a/spec/lib/gitlab/git/storage/checker_spec.rb b/spec/lib/gitlab/git/storage/checker_spec.rb
deleted file mode 100644
index d74c3bcb04c..00000000000
--- a/spec/lib/gitlab/git/storage/checker_spec.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::Storage::Checker, :clean_gitlab_redis_shared_state do
- let(:storage_name) { 'default' }
- let(:hostname) { Gitlab::Environment.hostname }
- let(:cache_key) { "storage_accessible:#{storage_name}:#{hostname}" }
-
- subject(:checker) { described_class.new(storage_name) }
-
- def value_from_redis(name)
- Gitlab::Git::Storage.redis.with do |redis|
- redis.hmget(cache_key, name)
- end.first
- end
-
- def set_in_redis(name, value)
- Gitlab::Git::Storage.redis.with do |redis|
- redis.hmset(cache_key, name, value)
- end.first
- end
-
- describe '.check_all' do
- it 'calls a check for each storage' do
- fake_checker_default = double
- fake_checker_broken = double
- fake_logger = fake_logger
-
- expect(described_class).to receive(:new).with('default', fake_logger) { fake_checker_default }
- expect(described_class).to receive(:new).with('broken', fake_logger) { fake_checker_broken }
- expect(fake_checker_default).to receive(:check_with_lease)
- expect(fake_checker_broken).to receive(:check_with_lease)
-
- described_class.check_all(fake_logger)
- end
-
- context 'with broken storage', :broken_storage do
- it 'returns the results' do
- expected_result = [
- { storage: 'default', success: true },
- { storage: 'broken', success: false }
- ]
-
- expect(described_class.check_all).to eq(expected_result)
- end
- end
- end
-
- describe '#initialize' do
- it 'assigns the settings' do
- expect(checker.hostname).to eq(hostname)
- expect(checker.storage).to eq('default')
- expect(checker.storage_path).to eq(TestEnv.repos_path)
- end
- end
-
- describe '#check_with_lease' do
- it 'only allows one check at a time' do
- expect(checker).to receive(:check).once { sleep 1 }
-
- thread = Thread.new { checker.check_with_lease }
- checker.check_with_lease
- thread.join
- end
-
- it 'returns a result hash' do
- expect(checker.check_with_lease).to eq(storage: 'default', success: true)
- end
- end
-
- describe '#check' do
- it 'tracks that the storage was accessible' do
- set_in_redis(:failure_count, 10)
- set_in_redis(:last_failure, Time.now.to_f)
-
- checker.check
-
- expect(value_from_redis(:failure_count).to_i).to eq(0)
- expect(value_from_redis(:last_failure)).to be_empty
- expect(value_from_redis(:first_failure)).to be_empty
- end
-
- it 'calls the check with the correct arguments' do
- stub_application_setting(circuitbreaker_storage_timeout: 30,
- circuitbreaker_access_retries: 3)
-
- expect(Gitlab::Git::Storage::ForkedStorageCheck)
- .to receive(:storage_available?).with(TestEnv.repos_path, 30, 3)
- .and_call_original
-
- checker.check
- end
-
- it 'returns `true`' do
- expect(checker.check).to eq(true)
- end
-
- it 'maintains known storage keys' do
- Timecop.freeze do
- # Insert an old key to expire
- old_entry = Time.now.to_i - 3.days.to_i
- Gitlab::Git::Storage.redis.with do |redis|
- redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, old_entry, 'to_be_removed')
- end
-
- checker.check
-
- known_keys = Gitlab::Git::Storage.redis.with do |redis|
- redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1)
- end
-
- expect(known_keys).to contain_exactly(cache_key)
- end
- end
-
- context 'the storage is not available', :broken_storage do
- let(:storage_name) { 'broken' }
-
- it 'tracks that the storage was inaccessible' do
- Timecop.freeze do
- expect { checker.check }.to change { value_from_redis(:failure_count).to_i }.by(1)
-
- expect(value_from_redis(:last_failure)).not_to be_empty
- expect(value_from_redis(:first_failure)).not_to be_empty
- end
- end
-
- it 'returns `false`' do
- expect(checker.check).to eq(false)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb
deleted file mode 100644
index 210b90bfba9..00000000000
--- a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb
+++ /dev/null
@@ -1,187 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::Storage::CircuitBreaker, :broken_storage do
- let(:storage_name) { 'default' }
- let(:circuit_breaker) { described_class.new(storage_name, hostname) }
- let(:hostname) { Gitlab::Environment.hostname }
- let(:cache_key) { "storage_accessible:#{storage_name}:#{hostname}" }
-
- def set_in_redis(name, value)
- Gitlab::Git::Storage.redis.with do |redis|
- redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key)
- redis.hmset(cache_key, name, value)
- end.first
- end
-
- before do
- # Override test-settings for the circuitbreaker with something more realistic
- # for these specs.
- stub_storage_settings('default' => {
- 'path' => TestEnv.repos_path
- },
- 'broken' => {
- 'path' => 'tmp/tests/non-existent-repositories'
- },
- 'nopath' => { 'path' => nil }
- )
- end
-
- describe '.for_storage', :request_store do
- it 'only builds a single circuitbreaker per storage' do
- expect(described_class).to receive(:new).once.and_call_original
-
- breaker = described_class.for_storage('default')
-
- expect(breaker).to be_a(described_class)
- expect(described_class.for_storage('default')).to eq(breaker)
- end
-
- it 'returns a broken circuit breaker for an unknown storage' do
- expect(described_class.for_storage('unknown').circuit_broken?).to be_truthy
- end
-
- it 'returns a broken circuit breaker when the path is not set' do
- expect(described_class.for_storage('nopath').circuit_broken?).to be_truthy
- end
- end
-
- describe '#initialize' do
- it 'assigns the settings' do
- expect(circuit_breaker.hostname).to eq(hostname)
- expect(circuit_breaker.storage).to eq('default')
- end
- end
-
- context 'circuitbreaker settings' do
- before do
- stub_application_setting(circuitbreaker_failure_count_threshold: 0,
- circuitbreaker_failure_wait_time: 1,
- circuitbreaker_failure_reset_time: 2,
- circuitbreaker_storage_timeout: 3,
- circuitbreaker_access_retries: 4,
- circuitbreaker_backoff_threshold: 5)
- end
-
- describe '#failure_count_threshold' do
- it 'reads the value from settings' do
- expect(circuit_breaker.failure_count_threshold).to eq(0)
- end
- end
-
- describe '#check_interval' do
- it 'reads the value from settings' do
- expect(circuit_breaker.check_interval).to eq(1)
- end
- end
-
- describe '#failure_reset_time' do
- it 'reads the value from settings' do
- expect(circuit_breaker.failure_reset_time).to eq(2)
- end
- end
-
- describe '#storage_timeout' do
- it 'reads the value from settings' do
- expect(circuit_breaker.storage_timeout).to eq(3)
- end
- end
-
- describe '#access_retries' do
- it 'reads the value from settings' do
- expect(circuit_breaker.access_retries).to eq(4)
- end
- end
- end
-
- describe '#perform' do
- it 'raises the correct exception when the circuit is open' do
- set_in_redis(:last_failure, 1.day.ago.to_f)
- set_in_redis(:failure_count, 999)
-
- expect { |b| circuit_breaker.perform(&b) }
- .to raise_error do |exception|
- expect(exception).to be_kind_of(Gitlab::Git::Storage::CircuitOpen)
- expect(exception.retry_after).to eq(1800)
- end
- end
-
- it 'yields the block' do
- expect { |b| circuit_breaker.perform(&b) }
- .to yield_control
- end
-
- it 'checks if the storage is available' do
- expect(circuit_breaker).to receive(:check_storage_accessible!)
- .and_call_original
-
- circuit_breaker.perform { 'hello world' }
- end
-
- it 'returns the value of the block' do
- result = circuit_breaker.perform { 'return value' }
-
- expect(result).to eq('return value')
- end
-
- it 'raises possible errors' do
- expect { circuit_breaker.perform { raise Rugged::OSError.new('Broken') } }
- .to raise_error(Rugged::OSError)
- end
-
- context 'with the feature disabled' do
- before do
- stub_feature_flags(git_storage_circuit_breaker: false)
- end
-
- it 'returns the block without checking accessibility' do
- expect(circuit_breaker).not_to receive(:check_storage_accessible!)
-
- result = circuit_breaker.perform { 'hello' }
-
- expect(result).to eq('hello')
- end
-
- it 'allows enabling the feature using an ENV var' do
- stub_env('GIT_STORAGE_CIRCUIT_BREAKER', 'true')
- expect(circuit_breaker).to receive(:check_storage_accessible!)
-
- result = circuit_breaker.perform { 'hello' }
-
- expect(result).to eq('hello')
- end
- end
- end
-
- describe '#circuit_broken?' do
- it 'is working when there is no last failure' do
- set_in_redis(:last_failure, nil)
- set_in_redis(:failure_count, 0)
-
- expect(circuit_breaker.circuit_broken?).to be_falsey
- end
-
- it 'is broken when there are too many failures' do
- set_in_redis(:last_failure, 1.day.ago.to_f)
- set_in_redis(:failure_count, 200)
-
- expect(circuit_breaker.circuit_broken?).to be_truthy
- end
- end
-
- describe '#last_failure' do
- it 'returns the last failure time' do
- time = Time.parse("2017-05-26 17:52:30")
- set_in_redis(:last_failure, time.to_i)
-
- expect(circuit_breaker.last_failure).to eq(time)
- end
- end
-
- describe '#failure_count' do
- it 'returns the failure count' do
- set_in_redis(:failure_count, 7)
-
- expect(circuit_breaker.failure_count).to eq(7)
- end
- end
-end
diff --git a/spec/lib/gitlab/git/storage/failure_info_spec.rb b/spec/lib/gitlab/git/storage/failure_info_spec.rb
deleted file mode 100644
index bae88fdda86..00000000000
--- a/spec/lib/gitlab/git/storage/failure_info_spec.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::Storage::FailureInfo, :broken_storage do
- let(:storage_name) { 'default' }
- let(:hostname) { Gitlab::Environment.hostname }
- let(:cache_key) { "storage_accessible:#{storage_name}:#{hostname}" }
-
- def value_from_redis(name)
- Gitlab::Git::Storage.redis.with do |redis|
- redis.hmget(cache_key, name)
- end.first
- end
-
- def set_in_redis(name, value)
- Gitlab::Git::Storage.redis.with do |redis|
- redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key)
- redis.hmset(cache_key, name, value)
- end.first
- end
-
- describe '.reset_all!' do
- it 'clears all entries form redis' do
- set_in_redis(:failure_count, 10)
-
- described_class.reset_all!
-
- key_exists = Gitlab::Git::Storage.redis.with { |redis| redis.exists(cache_key) }
-
- expect(key_exists).to be_falsey
- end
-
- it 'does not break when there are no keys in redis' do
- expect { described_class.reset_all! }.not_to raise_error
- end
- end
-
- describe '.load' do
- it 'loads failure information for a storage on a host' do
- first_failure = Time.parse("2017-11-14 17:52:30")
- last_failure = Time.parse("2017-11-14 18:54:37")
- failure_count = 11
-
- set_in_redis(:first_failure, first_failure.to_i)
- set_in_redis(:last_failure, last_failure.to_i)
- set_in_redis(:failure_count, failure_count.to_i)
-
- info = described_class.load(cache_key)
-
- expect(info.first_failure).to eq(first_failure)
- expect(info.last_failure).to eq(last_failure)
- expect(info.failure_count).to eq(failure_count)
- end
- end
-
- describe '#no_failures?' do
- it 'is true when there are no failures' do
- info = described_class.new(nil, nil, 0)
-
- expect(info.no_failures?).to be_truthy
- end
-
- it 'is false when there are failures' do
- info = described_class.new(Time.parse("2017-11-14 17:52:30"),
- Time.parse("2017-11-14 18:54:37"),
- 20)
-
- expect(info.no_failures?).to be_falsy
- end
- end
-end
diff --git a/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb b/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb
deleted file mode 100644
index 39a5d020bb4..00000000000
--- a/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::Storage::ForkedStorageCheck, broken_storage: true, skip_database_cleaner: true do
- let(:existing_path) do
- existing_path = TestEnv.repos_path
- FileUtils.mkdir_p(existing_path)
- existing_path
- end
-
- describe '.storage_accessible?' do
- it 'detects when a storage is not available' do
- expect(described_class.storage_available?('/non/existant/path')).to be_falsey
- end
-
- it 'detects when a storage is available' do
- expect(described_class.storage_available?(existing_path)).to be_truthy
- end
-
- it 'returns false when the check takes to long' do
- # We're forking a process here that takes too long
- # It will be killed it's parent process will be killed by it's parent
- # and waited for inside `Gitlab::Git::Storage::ForkedStorageCheck.timeout_check`
- allow(described_class).to receive(:check_filesystem_in_process) do
- Process.spawn("sleep 10")
- end
- result = true
-
- runtime = Benchmark.realtime do
- result = described_class.storage_available?(existing_path, 0.5)
- end
-
- expect(result).to be_falsey
- expect(runtime).to be < 1.0
- end
-
- it 'will try the specified amount of times before failing' do
- allow(described_class).to receive(:check_filesystem_in_process) do
- Process.spawn("sleep 10")
- end
-
- expect(Process).to receive(:spawn).with('sleep 10').twice
- .and_call_original
-
- runtime = Benchmark.realtime do
- described_class.storage_available?(existing_path, 0.5, 2)
- end
-
- expect(runtime).to be < 1.0
- end
-
- describe 'when using paths with spaces' do
- let(:test_dir) { Rails.root.join('tmp', 'tests', 'storage_check') }
- let(:path_with_spaces) { File.join(test_dir, 'path with spaces') }
-
- around do |example|
- FileUtils.mkdir_p(path_with_spaces)
- example.run
- FileUtils.rm_r(test_dir)
- end
-
- it 'works for paths with spaces' do
- expect(described_class.storage_available?(path_with_spaces)).to be_truthy
- end
-
- it 'works for a realpath with spaces' do
- symlink_location = File.join(test_dir, 'a symlink')
- FileUtils.ln_s(path_with_spaces, symlink_location)
-
- expect(described_class.storage_available?(symlink_location)).to be_truthy
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/git/storage/health_spec.rb b/spec/lib/gitlab/git/storage/health_spec.rb
deleted file mode 100644
index bb670fc5d94..00000000000
--- a/spec/lib/gitlab/git/storage/health_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::Storage::Health, broken_storage: true do
- let(:host1_key) { 'storage_accessible:broken:web01' }
- let(:host2_key) { 'storage_accessible:default:kiq01' }
-
- def set_in_redis(cache_key, value)
- Gitlab::Git::Storage.redis.with do |redis|
- redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key)
- redis.hmset(cache_key, :failure_count, value)
- end.first
- end
-
- describe '.for_failing_storages' do
- it 'only includes health status for failures' do
- set_in_redis(host1_key, 10)
- set_in_redis(host2_key, 0)
-
- expect(described_class.for_failing_storages.map(&:storage_name))
- .to contain_exactly('broken')
- end
- end
-
- describe '.for_all_storages' do
- it 'loads health status for all configured storages' do
- healths = described_class.for_all_storages
-
- expect(healths.map(&:storage_name)).to contain_exactly('default', 'broken')
- end
- end
-
- describe '#failing_info' do
- it 'only contains storages that have failures' do
- health = described_class.new('broken', [{ name: host1_key, failure_count: 0 },
- { name: host2_key, failure_count: 3 }])
-
- expect(health.failing_info).to contain_exactly({ name: host2_key, failure_count: 3 })
- end
- end
-
- describe '#total_failures' do
- it 'sums up all the failures' do
- health = described_class.new('broken', [{ name: host1_key, failure_count: 2 },
- { name: host2_key, failure_count: 3 }])
-
- expect(health.total_failures).to eq(5)
- end
- end
-
- describe '#failing_on_hosts' do
- it 'collects only the failing hostnames' do
- health = described_class.new('broken', [{ name: host1_key, failure_count: 2 },
- { name: host2_key, failure_count: 0 }])
-
- expect(health.failing_on_hosts).to contain_exactly('web01')
- end
- end
-end
diff --git a/spec/lib/gitlab/git/storage/null_circuit_breaker_spec.rb b/spec/lib/gitlab/git/storage/null_circuit_breaker_spec.rb
deleted file mode 100644
index 93ad20011de..00000000000
--- a/spec/lib/gitlab/git/storage/null_circuit_breaker_spec.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::Storage::NullCircuitBreaker do
- let(:storage) { 'default' }
- let(:hostname) { 'localhost' }
- let(:error) { nil }
-
- subject(:breaker) { described_class.new(storage, hostname, error: error) }
-
- context 'with an error' do
- let(:error) { Gitlab::Git::Storage::Misconfiguration.new('error') }
-
- describe '#perform' do
- it { expect { breaker.perform { 'ok' } }.to raise_error(error) }
- end
-
- describe '#circuit_broken?' do
- it { expect(breaker.circuit_broken?).to be_truthy }
- end
-
- describe '#last_failure' do
- it { Timecop.freeze { expect(breaker.last_failure).to eq(Time.now) } }
- end
-
- describe '#failure_count' do
- it { expect(breaker.failure_count).to eq(breaker.failure_count_threshold) }
- end
-
- describe '#failure_info' do
- it { expect(breaker.failure_info.no_failures?).to be_falsy }
- end
- end
-
- context 'not broken' do
- describe '#perform' do
- it { expect(breaker.perform { 'ok' }).to eq('ok') }
- end
-
- describe '#circuit_broken?' do
- it { expect(breaker.circuit_broken?).to be_falsy }
- end
-
- describe '#last_failure' do
- it { expect(breaker.last_failure).to be_nil }
- end
-
- describe '#failure_count' do
- it { expect(breaker.failure_count).to eq(0) }
- end
-
- describe '#failure_info' do
- it { expect(breaker.failure_info.no_failures?).to be_truthy }
- end
- end
-
- describe '#failure_count_threshold' do
- before do
- stub_application_setting(circuitbreaker_failure_count_threshold: 1)
- end
-
- it { expect(breaker.failure_count_threshold).to eq(1) }
- end
-
- it 'implements the CircuitBreaker interface' do
- ours = described_class.public_instance_methods
- theirs = Gitlab::Git::Storage::CircuitBreaker.public_instance_methods
-
- expect(theirs - ours).to be_empty
- end
-end
diff --git a/spec/lib/gitlab/git/tag_spec.rb b/spec/lib/gitlab/git/tag_spec.rb
index 2d9db576a6c..c5bad062c2a 100644
--- a/spec/lib/gitlab/git/tag_spec.rb
+++ b/spec/lib/gitlab/git/tag_spec.rb
@@ -68,7 +68,7 @@ describe Gitlab::Git::Tag, :seed_helper do
context 'message_size less than threshold' do
let(:message_size) { 123 }
- it 'fetches tag message seperately' do
+ it 'fetches tag message separately' do
expect(described_class).to receive(:get_message).with(repository, gitaly_tag.id)
tag.message
diff --git a/spec/lib/gitlab/git/user_spec.rb b/spec/lib/gitlab/git/user_spec.rb
index 99d850e1df9..d9d338206f8 100644
--- a/spec/lib/gitlab/git/user_spec.rb
+++ b/spec/lib/gitlab/git/user_spec.rb
@@ -22,10 +22,19 @@ describe Gitlab::Git::User do
end
describe '.from_gitlab' do
- let(:user) { build(:user) }
- subject { described_class.from_gitlab(user) }
+ context 'when no commit_email has been set' do
+ let(:user) { build(:user, email: 'alice@example.com', commit_email: nil) }
+ subject { described_class.from_gitlab(user) }
- it { expect(subject).to eq(described_class.new(user.username, user.name, user.email, 'user-')) }
+ it { expect(subject).to eq(described_class.new(user.username, user.name, user.email, 'user-')) }
+ end
+
+ context 'when commit_email has been set' do
+ let(:user) { build(:user, email: 'alice@example.com', commit_email: 'bob@example.com') }
+ subject { described_class.from_gitlab(user) }
+
+ it { expect(subject).to eq(described_class.new(user.username, user.name, user.commit_email, 'user-')) }
+ end
end
describe '#==' do
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index c5666e4ec61..ded5d7576df 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -1,10 +1,13 @@
require 'spec_helper'
describe Gitlab::Git::Wiki do
+ using RSpec::Parameterized::TableSyntax
+
let(:project) { create(:project) }
let(:user) { project.owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
- subject { project_wiki.wiki }
+
+ subject(:wiki) { project_wiki.wiki }
describe '#pages' do
before do
@@ -64,8 +67,44 @@ describe Gitlab::Git::Wiki do
end
end
- def create_page(name, content)
- subject.write_page(name, :markdown, content, commit_details(name))
+ describe '#preview_slug' do
+ where(:title, :format, :expected_slug) do
+ 'The Best Thing' | :markdown | 'The-Best-Thing'
+ 'The Best Thing' | :md | 'The-Best-Thing'
+ 'The Best Thing' | :txt | 'The-Best-Thing'
+ 'A Subject/Title Here' | :txt | 'A-Subject/Title-Here'
+ 'A subject' | :txt | 'A-subject'
+ 'A 1/B 2/C 3' | :txt | 'A-1/B-2/C-3'
+ 'subject/title' | :txt | 'subject/title'
+ 'subject/title.md' | :txt | 'subject/title.md'
+ 'foo<bar>+baz' | :txt | 'foo-bar--baz'
+ 'foo%2Fbar' | :txt | 'foo%2Fbar'
+ '' | :markdown | '.md'
+ '' | :md | '.md'
+ '' | :txt | '.txt'
+ end
+
+ with_them do
+ subject { wiki.preview_slug(title, format) }
+
+ let(:gitaly_slug) { wiki.pages.first }
+
+ it { is_expected.to eq(expected_slug) }
+
+ it 'matches the slug generated by gitaly' do
+ skip('Gitaly cannot generate a slug for an empty title') unless title.present?
+
+ create_page(title, 'content', format: format)
+
+ gitaly_slug = wiki.pages.first.url_path
+
+ is_expected.to eq(gitaly_slug)
+ end
+ end
+ end
+
+ def create_page(name, content, format: :markdown)
+ wiki.write_page(name, format, content, commit_details(name))
end
def commit_details(name)
@@ -73,7 +112,7 @@ describe Gitlab::Git::Wiki do
end
def destroy_page(title, dir = '')
- page = subject.page(title: title, dir: dir)
+ page = wiki.page(title: title, dir: dir)
project_wiki.delete_page(page, "test commit")
end
end
diff --git a/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb b/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb
new file mode 100644
index 00000000000..bcf4814edb6
--- /dev/null
+++ b/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Gitlab::Git::WrapsGitalyErrors do
+ subject(:wrapper) do
+ klazz = Class.new { include Gitlab::Git::WrapsGitalyErrors }
+ klazz.new
+ end
+
+ describe "#wrapped_gitaly_errors" do
+ mapping = {
+ GRPC::NotFound => Gitlab::Git::Repository::NoRepository,
+ GRPC::InvalidArgument => ArgumentError,
+ GRPC::BadStatus => Gitlab::Git::CommandError
+ }
+
+ mapping.each do |grpc_error, error|
+ it "wraps #{grpc_error} in a #{error}" do
+ expect { wrapper.wrapped_gitaly_errors { raise grpc_error.new('wrapped') } }
+ .to raise_error(error)
+ end
+ end
+
+ it 'does not swallow other errors' do
+ expect { wrapper.wrapped_gitaly_errors { raise 'raised' } }
+ .to raise_error(RuntimeError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index dbd64c4bec0..a417ef77c9e 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe Gitlab::GitAccess do
include TermsHelper
+ include GitHelpers
let(:user) { create(:user) }
@@ -736,21 +737,19 @@ describe Gitlab::GitAccess do
def merge_into_protected_branch
@protected_branch_merge_commit ||= begin
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.add_branch(user, unprotected_branch, 'feature')
- rugged = project.repository.rugged
- target_branch = rugged.rev_parse('feature')
- source_branch = project.repository.create_file(
- user,
- 'filename',
- 'This is the file content',
- message: 'This is a good commit message',
- branch_name: unprotected_branch)
- author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
-
- merge_index = rugged.merge_commits(target_branch, source_branch)
- Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged))
- end
+ project.repository.add_branch(user, unprotected_branch, 'feature')
+ rugged = rugged_repo(project.repository)
+ target_branch = rugged.rev_parse('feature')
+ source_branch = project.repository.create_file(
+ user,
+ 'filename',
+ 'This is the file content',
+ message: 'This is a good commit message',
+ branch_name: unprotected_branch)
+ author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
+
+ merge_index = rugged.merge_commits(target_branch, source_branch)
+ Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged))
end
end
@@ -935,6 +934,16 @@ describe Gitlab::GitAccess do
# There is still an N+1 query with protected branches
expect { access.check('git-receive-pack', changes) }.not_to exceed_query_limit(control_count).with_threshold(1)
end
+
+ it 'raises TimeoutError when #check_single_change_access raises a timeout error' do
+ message = "Push operation timed out\n\nTiming information for debugging purposes:\nRunning checks for ref: wow"
+
+ expect_next_instance_of(Gitlab::Checks::ChangeAccess) do |check|
+ expect(check).to receive(:exec).and_raise(Gitlab::Checks::TimedLogger::TimeoutError)
+ end
+
+ expect { access.check('git-receive-pack', changes) }.to raise_error(described_class::TimeoutError, message)
+ end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 54f2ea33f90..d7bd757149d 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -19,7 +19,14 @@ describe Gitlab::GitalyClient::CommitService do
right_commit_id: commit.id,
collapse_diffs: false,
enforce_limits: true,
- **Gitlab::Git::DiffCollection.collection_limits.to_h
+ # Tests limitation parameters explicitly
+ max_files: 100,
+ max_lines: 5000,
+ max_bytes: 512000,
+ safe_max_files: 100,
+ safe_max_lines: 5000,
+ safe_max_bytes: 512000,
+ max_patch_bytes: 102400
)
expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash))
@@ -37,7 +44,14 @@ describe Gitlab::GitalyClient::CommitService do
right_commit_id: initial_commit.id,
collapse_diffs: false,
enforce_limits: true,
- **Gitlab::Git::DiffCollection.collection_limits.to_h
+ # Tests limitation parameters explicitly
+ max_files: 100,
+ max_lines: 5000,
+ max_bytes: 512000,
+ safe_max_files: 100,
+ safe_max_lines: 5000,
+ safe_max_bytes: 512000,
+ max_patch_bytes: 102400
)
expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash))
@@ -104,6 +118,22 @@ describe Gitlab::GitalyClient::CommitService do
end
end
+ describe '#diff_stats' do
+ let(:left_commit_id) { 'master' }
+ let(:right_commit_id) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
+
+ it 'sends an RPC request' do
+ request = Gitaly::DiffStatsRequest.new(repository: repository_message,
+ left_commit_id: left_commit_id,
+ right_commit_id: right_commit_id)
+
+ expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:diff_stats)
+ .with(request, kind_of(Hash)).and_return([])
+
+ described_class.new(repository).diff_stats(left_commit_id, right_commit_id)
+ end
+ end
+
describe '#tree_entries' do
let(:path) { '/' }
diff --git a/spec/lib/gitlab/gitaly_client/diff_spec.rb b/spec/lib/gitlab/gitaly_client/diff_spec.rb
index 00a31ac0b96..ec7ab2fdedb 100644
--- a/spec/lib/gitlab/gitaly_client/diff_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/diff_spec.rb
@@ -9,7 +9,9 @@ describe Gitlab::GitalyClient::Diff do
new_mode: 0100644,
from_id: '357406f3075a57708d0163752905cc1576fceacc',
to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0',
- patch: 'a' * 100
+ patch: 'a' * 100,
+ collapsed: false,
+ too_large: false
}
end
@@ -22,6 +24,8 @@ describe Gitlab::GitalyClient::Diff do
it { is_expected.to respond_to(:from_id) }
it { is_expected.to respond_to(:to_id) }
it { is_expected.to respond_to(:patch) }
+ it { is_expected.to respond_to(:collapsed) }
+ it { is_expected.to respond_to(:too_large) }
describe '#==' do
it { expect(subject).to eq(described_class.new(diff_fields)) }
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index eaf64e3c9b4..b37fe2686b6 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -335,4 +335,37 @@ describe Gitlab::GitalyClient::OperationService do
end
end
end
+
+ describe '#user_commit_patches' do
+ let(:patches_folder) { Rails.root.join('spec/fixtures/patchfiles') }
+ let(:patch_content) do
+ patch_names.map { |name| File.read(File.join(patches_folder, name)) }.join("\n")
+ end
+ let(:patch_names) { %w(0001-This-does-not-apply-to-the-feature-branch.patch) }
+ let(:branch_name) { 'branch-with-patches' }
+
+ subject(:commit_patches) do
+ client.user_commit_patches(user, branch_name, patch_content)
+ end
+
+ it 'applies the patch correctly' do
+ branch_update = commit_patches
+
+ expect(branch_update).to be_branch_created
+
+ commit = repository.commit(branch_update.newrev)
+ expect(commit.author_email).to eq('patchuser@gitlab.org')
+ expect(commit.committer_email).to eq(user.email)
+ expect(commit.message.chomp).to eq('This does not apply to the `feature` branch')
+ end
+
+ context 'when the patch could not be applied' do
+ let(:patch_names) { %w(0001-This-does-not-apply-to-the-feature-branch.patch) }
+ let(:branch_name) { 'feature' }
+
+ it 'raises the correct error' do
+ expect { commit_patches }.to raise_error(GRPC::FailedPrecondition)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
index f03c7e3f04b..9030a49983d 100644
--- a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
@@ -45,6 +45,26 @@ describe Gitlab::GitalyClient::RemoteService do
end
end
+ describe '#find_remote_root_ref' do
+ it 'sends an find_remote_root_ref message and returns the root ref' do
+ expect_any_instance_of(Gitaly::RemoteService::Stub)
+ .to receive(:find_remote_root_ref)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(ref: 'master'))
+
+ expect(client.find_remote_root_ref('origin')).to eq 'master'
+ end
+
+ it 'ensure ref is a valid UTF-8 string' do
+ expect_any_instance_of(Gitaly::RemoteService::Stub)
+ .to receive(:find_remote_root_ref)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(ref: "an_invalid_ref_\xE5"))
+
+ expect(client.find_remote_root_ref('origin')).to eq "an_invalid_ref_Ã¥"
+ end
+ end
+
describe '#update_remote_mirror' do
let(:ref_name) { 'remote_mirror_1' }
let(:only_branches_matching) { ['my-branch', 'master'] }
diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
index 1547d447197..d605fcbafee 100644
--- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Gitlab::GitalyClient::RepositoryService do
+ using RSpec::Parameterized::TableSyntax
+
let(:project) { create(:project) }
let(:storage_name) { project.repository_storage }
let(:relative_path) { project.disk_path + '.git' }
@@ -107,16 +109,67 @@ describe Gitlab::GitalyClient::RepositoryService do
end
describe '#fetch_remote' do
- let(:ssh_auth) { double(:ssh_auth, ssh_import?: true, ssh_key_auth?: false, ssh_known_hosts: nil) }
- let(:import_url) { 'ssh://example.com' }
+ let(:remote) { 'remote-name' }
it 'sends a fetch_remote_request message' do
+ expected_request = gitaly_request_with_params(
+ remote: remote,
+ ssh_key: '',
+ known_hosts: '',
+ force: false,
+ no_tags: false,
+ no_prune: false
+ )
+
expect_any_instance_of(Gitaly::RepositoryService::Stub)
.to receive(:fetch_remote)
- .with(gitaly_request_with_params(no_prune: false), kind_of(Hash))
+ .with(expected_request, kind_of(Hash))
.and_return(double(value: true))
- client.fetch_remote(import_url, ssh_auth: ssh_auth, forced: false, no_tags: false, timeout: 60)
+ client.fetch_remote(remote, ssh_auth: nil, forced: false, no_tags: false, timeout: 1)
+ end
+
+ context 'SSH auth' do
+ where(:ssh_import, :ssh_key_auth, :ssh_private_key, :ssh_known_hosts, :expected_params) do
+ false | false | 'key' | 'known_hosts' | {}
+ false | true | 'key' | 'known_hosts' | {}
+ true | false | 'key' | 'known_hosts' | { known_hosts: 'known_hosts' }
+ true | true | 'key' | 'known_hosts' | { ssh_key: 'key', known_hosts: 'known_hosts' }
+ true | true | 'key' | nil | { ssh_key: 'key' }
+ true | true | nil | 'known_hosts' | { known_hosts: 'known_hosts' }
+ true | true | nil | nil | {}
+ true | true | '' | '' | {}
+ end
+
+ with_them do
+ let(:ssh_auth) do
+ double(
+ :ssh_auth,
+ ssh_import?: ssh_import,
+ ssh_key_auth?: ssh_key_auth,
+ ssh_private_key: ssh_private_key,
+ ssh_known_hosts: ssh_known_hosts
+ )
+ end
+
+ it do
+ expected_request = gitaly_request_with_params({
+ remote: remote,
+ ssh_key: '',
+ known_hosts: '',
+ force: false,
+ no_tags: false,
+ no_prune: false
+ }.update(expected_params))
+
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:fetch_remote)
+ .with(expected_request, kind_of(Hash))
+ .and_return(double(value: true))
+
+ client.fetch_remote(remote, ssh_auth: ssh_auth, forced: false, no_tags: false, timeout: 1)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
index 5f67fe6b952..d82c9c28da0 100644
--- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
@@ -39,6 +39,10 @@ describe Gitlab::GitalyClient::WikiService do
expect(wiki_page.title).to eq('My Page')
expect(wiki_page.raw_data).to eq('ab')
expect(wiki_page_version.format).to eq('markdown')
+
+ expect(wiki_page.title).to be_utf8
+ expect(wiki_page.path).to be_utf8
+ expect(wiki_page.name).to be_utf8
end
end
diff --git a/spec/lib/gitlab/github_import/bulk_importing_spec.rb b/spec/lib/gitlab/github_import/bulk_importing_spec.rb
index 91229d9c7d4..861710f7e9b 100644
--- a/spec/lib/gitlab/github_import/bulk_importing_spec.rb
+++ b/spec/lib/gitlab/github_import/bulk_importing_spec.rb
@@ -58,5 +58,17 @@ describe Gitlab::GithubImport::BulkImporting do
importer.bulk_insert(model, rows, batch_size: 5)
end
+
+ it 'calls pre_hook for each slice if given' do
+ rows = [{ title: 'Foo' }] * 10
+ model = double(:model, table_name: 'kittens')
+ pre_hook = double('pre_hook', call: nil)
+ allow(Gitlab::Database).to receive(:bulk_insert)
+
+ expect(pre_hook).to receive(:call).with(rows[0..4])
+ expect(pre_hook).to receive(:call).with(rows[5..9])
+
+ importer.bulk_insert(model, rows, batch_size: 5, pre_hook: pre_hook)
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
index 81fe97c1e49..65a2e1cb5cb 100644
--- a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
@@ -78,6 +78,11 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach
.to receive(:id_for)
.with(issue)
.and_return(milestone.id)
+
+ allow(importer.user_finder)
+ .to receive(:author_id_for)
+ .with(issue)
+ .and_return([user.id, true])
end
context 'when the issue author could be found' do
@@ -87,7 +92,7 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach
.with(issue)
.and_return([user.id, true])
- expect(Gitlab::GithubImport)
+ expect(importer)
.to receive(:insert_and_return_id)
.with(
{
@@ -116,7 +121,7 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach
.with(issue)
.and_return([project.creator_id, false])
- expect(Gitlab::GithubImport)
+ expect(importer)
.to receive(:insert_and_return_id)
.with(
{
@@ -145,7 +150,7 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach
.with(issue)
.and_return([user.id, true])
- expect(Gitlab::GithubImport)
+ expect(importer)
.to receive(:insert_and_return_id)
.and_raise(ActiveRecord::InvalidForeignKey, 'invalid foreign key')
@@ -172,6 +177,23 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach
expect(importer.create_issue).to be_a_kind_of(Numeric)
end
+
+ it 'triggers internal_id functionality to track greatest iids' do
+ allow(importer.user_finder)
+ .to receive(:author_id_for)
+ .with(issue)
+ .and_return([user.id, true])
+
+ issue = build_stubbed(:issue, project: project)
+ allow(importer)
+ .to receive(:insert_and_return_id)
+ .and_return(issue.id)
+ allow(project.issues).to receive(:find).with(issue.id).and_return(issue)
+
+ expect(issue).to receive(:ensure_project_iid!)
+
+ importer.create_issue
+ end
end
describe '#create_assignees' do
diff --git a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
index b1cac3b6e46..db0be760c7b 100644
--- a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
@@ -29,13 +29,25 @@ describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis
expect(importer)
.to receive(:bulk_insert)
- .with(Milestone, [milestone_hash])
+ .with(Milestone, [milestone_hash], any_args)
expect(importer)
.to receive(:build_milestones_cache)
importer.execute
end
+
+ it 'tracks internal ids' do
+ milestone_hash = { iid: 1, title: '1.0', project_id: project.id }
+ allow(importer)
+ .to receive(:build_milestones)
+ .and_return([milestone_hash])
+
+ expect(InternalId).to receive(:track_greatest)
+ .with(nil, { project: project }, :milestones, 1, any_args)
+
+ importer.execute
+ end
end
describe '#build_milestones' do
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
index 3422a1e82fc..efca8564894 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
@@ -80,7 +80,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
end
it 'imports the pull request with the pull request author as the merge request author' do
- expect(Gitlab::GithubImport)
+ expect(importer)
.to receive(:insert_and_return_id)
.with(
{
@@ -111,6 +111,16 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
expect(mr).to be_instance_of(MergeRequest)
expect(exists).to eq(false)
end
+
+ it 'triggers internal_id functionality to track greatest iids' do
+ mr = build_stubbed(:merge_request, source_project: project, target_project: project)
+ allow(importer).to receive(:insert_and_return_id).and_return(mr.id)
+ allow(project.merge_requests).to receive(:find).with(mr.id).and_return(mr)
+
+ expect(mr).to receive(:ensure_target_project_iid!)
+
+ importer.create_merge_request
+ end
end
context 'when the author could not be found' do
@@ -125,7 +135,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
.with(pull_request)
.and_return(user.id)
- expect(Gitlab::GithubImport)
+ expect(importer)
.to receive(:insert_and_return_id)
.with(
{
@@ -171,7 +181,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
.to receive(:source_branch)
.and_return('master')
- expect(Gitlab::GithubImport)
+ expect(importer)
.to receive(:insert_and_return_id)
.with(
{
@@ -209,7 +219,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
.with(pull_request)
.and_return(user.id)
- expect(Gitlab::GithubImport)
+ expect(importer)
.to receive(:insert_and_return_id)
.and_raise(ActiveRecord::InvalidForeignKey, 'invalid foreign key')
@@ -230,7 +240,12 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
.and_return(user.id)
end
- it 'returns the existing merge request' do
+ # TODO: remove rails5-only after removing rails4 tests
+ # rails 4 can not handle multiple indexes on the same column set if
+ # index was added by 't.index' - t.index is used by default in schema.rb in
+ # rails 5. Let's run this test only in rails 5 env:
+ # see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21492#note_113602758
+ it 'returns the existing merge request', :rails5 do
mr1, exists1 = importer.create_merge_request
mr2, exists2 = importer.create_merge_request
diff --git a/spec/lib/gitlab/github_import_spec.rb b/spec/lib/gitlab/github_import_spec.rb
index 51414800e8c..496244c91bf 100644
--- a/spec/lib/gitlab/github_import_spec.rb
+++ b/spec/lib/gitlab/github_import_spec.rb
@@ -27,39 +27,6 @@ describe Gitlab::GithubImport do
end
end
- describe '.insert_and_return_id' do
- let(:attributes) { { iid: 1, title: 'foo' } }
- let(:project) { create(:project) }
-
- context 'on PostgreSQL' do
- it 'returns the ID returned by the query' do
- expect(Gitlab::Database)
- .to receive(:bulk_insert)
- .with(Issue.table_name, [attributes], return_ids: true)
- .and_return([10])
-
- id = described_class.insert_and_return_id(attributes, project.issues)
-
- expect(id).to eq(10)
- end
- end
-
- context 'on MySQL' do
- it 'uses a separate query to retrieve the ID' do
- issue = create(:issue, project: project, iid: attributes[:iid])
-
- expect(Gitlab::Database)
- .to receive(:bulk_insert)
- .with(Issue.table_name, [attributes], return_ids: true)
- .and_return([])
-
- id = described_class.insert_and_return_id(attributes, project.issues)
-
- expect(id).to eq(issue.id)
- end
- end
- end
-
describe '.ghost_user_id', :clean_gitlab_redis_cache do
it 'returns the ID of the ghost user' do
expect(described_class.ghost_user_id).to eq(User.ghost.id)
diff --git a/spec/lib/gitlab/gon_helper_spec.rb b/spec/lib/gitlab/gon_helper_spec.rb
new file mode 100644
index 00000000000..c6f09ca2112
--- /dev/null
+++ b/spec/lib/gitlab/gon_helper_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::GonHelper do
+ let(:helper) do
+ Class.new do
+ include Gitlab::GonHelper
+ end.new
+ end
+
+ describe '#push_frontend_feature_flag' do
+ it 'pushes a feature flag to the frontend' do
+ gon = instance_double('gon')
+
+ allow(helper)
+ .to receive(:gon)
+ .and_return(gon)
+
+ expect(Feature)
+ .to receive(:enabled?)
+ .with(:my_feature_flag, 10)
+ .and_return(true)
+
+ expect(gon)
+ .to receive(:push)
+ .with({ features: { 'myFeatureFlag' => true } }, true)
+
+ helper.push_frontend_feature_flag(:my_feature_flag, 10)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb
index 47f37cae98f..39d09c49989 100644
--- a/spec/lib/gitlab/gpg_spec.rb
+++ b/spec/lib/gitlab/gpg_spec.rb
@@ -96,7 +96,7 @@ describe Gitlab::Gpg do
expect(described_class.current_home_dir).to eq default_home_dir
end
- it 'returns the explicitely set home dir' do
+ it 'returns the explicitly set home dir' do
GPGME::Engine.home_dir = '/tmp/gpg'
expect(described_class.current_home_dir).to eq '/tmp/gpg'
@@ -104,7 +104,7 @@ describe Gitlab::Gpg do
GPGME::Engine.home_dir = GPGME::Engine.dirinfo('homedir')
end
- it 'returns the default value when explicitely setting the home dir to nil' do
+ it 'returns the default value when explicitly setting the home dir to nil' do
GPGME::Engine.home_dir = nil
expect(described_class.current_home_dir).to eq default_home_dir
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index 29e61d15726..fe0e9702f8a 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -5,44 +5,85 @@ describe Gitlab::Highlight do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
- let(:commit) { project.commit(sample_commit.id) }
-
- describe 'custom highlighting from .gitattributes' do
- let(:branch) { 'gitattributes' }
- let(:blob) { repository.blob_at_branch(branch, path) }
+ describe 'language provided' do
let(:highlighter) do
- described_class.new(blob.path, blob.data, repository: repository)
+ described_class.new('foo.erb', 'bar', language: 'erb?parent=json')
end
- before do
- project.change_head('gitattributes')
+ it 'sets correct lexer' do
+ expect(highlighter.lexer.tag).to eq 'erb'
+ expect(highlighter.lexer.parent.tag).to eq 'json'
end
+ end
- describe 'basic language selection' do
- let(:path) { 'custom-highlighting/test.gitlab-custom' }
- it 'highlights as ruby' do
- expect(highlighter.lexer.tag).to eq 'ruby'
- end
+ describe '#highlight' do
+ let(:file_name) { 'test.lisp' }
+ let(:no_context_content) { ":type \"assem\"))" }
+ let(:content) { "(make-pathname :defaults name\n#{no_context_content}" }
+ let(:multiline_content) do
+ %q(
+ def test(input):
+ """This is line 1 of a multi-line comment.
+ This is line 2.
+ """
+ )
+ end
+
+ it 'highlights' do
+ expected = %Q[<span id="LC1" class="line" lang="common_lisp"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
+<span id="LC2" class="line" lang="common_lisp"><span class="ss">:type</span> <span class="s">"assem"</span><span class="p">))</span></span>]
+
+ expect(described_class.highlight(file_name, content)).to eq(expected)
+ end
+
+ it 'returns plain version for unknown lexer context' do
+ result = described_class.highlight(file_name, no_context_content)
+
+ expect(result).to eq(%[<span id="LC1" class="line" lang="">:type "assem"))</span>])
+ end
+
+ it 'returns plain version for long content' do
+ stub_const('Gitlab::Highlight::MAXIMUM_TEXT_HIGHLIGHT_SIZE', 1)
+ result = described_class.highlight(file_name, content)
+
+ expect(result).to eq(%[<span id="LC1" class="line" lang="">(make-pathname :defaults name</span>\n<span id="LC2" class="line" lang="">:type "assem"))</span>])
+ end
+
+ it 'highlights multi-line comments' do
+ result = described_class.highlight(file_name, multiline_content)
+ html = Nokogiri::HTML(result)
+ lines = html.search('.s')
+
+ expect(lines.count).to eq(3)
+ expect(lines[0].text).to eq('"""This is line 1 of a multi-line comment.')
+ expect(lines[1].text).to eq(' This is line 2.')
+ expect(lines[2].text).to eq(' """')
end
- describe 'cgi options' do
- let(:path) { 'custom-highlighting/test.gitlab-cgi' }
+ context 'diff highlighting' do
+ let(:file_name) { 'test.diff' }
+ let(:content) { "+aaa\n+bbb\n- ccc\n ddd\n"}
+ let(:expected) do
+ %q(<span id="LC1" class="line" lang="diff"><span class="gi">+aaa</span></span>
+<span id="LC2" class="line" lang="diff"><span class="gi">+bbb</span></span>
+<span id="LC3" class="line" lang="diff"><span class="gd">- ccc</span></span>
+<span id="LC4" class="line" lang="diff"> ddd</span>)
+ end
+
+ it 'highlights each line properly' do
+ result = described_class.highlight(file_name, content)
- it 'highlights as json with erb' do
- expect(highlighter.lexer.tag).to eq 'erb'
- expect(highlighter.lexer.parent.tag).to eq 'json'
+ expect(result).to eq(expected)
end
end
- end
- describe '#highlight' do
describe 'with CRLF' do
let(:branch) { 'crlf-diff' }
let(:path) { 'files/whitespace' }
let(:blob) { repository.blob_at_branch(branch, path) }
let(:lines) do
- described_class.highlight(blob.path, blob.data, repository: repository).lines
+ described_class.highlight(blob.path, blob.data).lines
end
it 'strips extra LFs' do
@@ -56,5 +97,22 @@ describe Gitlab::Highlight do
described_class.highlight('file.name', 'Contents')
end
+
+ context 'timeout' do
+ subject { described_class.new('file.name', 'Contents') }
+
+ it 'utilizes timeout for web' do
+ expect(Timeout).to receive(:timeout).with(described_class::TIMEOUT_FOREGROUND).and_call_original
+
+ subject.highlight("Content")
+ end
+
+ it 'utilizes longer timeout for sidekiq' do
+ allow(Sidekiq).to receive(:server?).and_return(true)
+ expect(Timeout).to receive(:timeout).with(described_class::TIMEOUT_BACKGROUND).and_call_original
+
+ subject.highlight("Content")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/hook_data/base_builder_spec.rb b/spec/lib/gitlab/hook_data/base_builder_spec.rb
index a921dd766c3..e3c5ee3b905 100644
--- a/spec/lib/gitlab/hook_data/base_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/base_builder_spec.rb
@@ -8,57 +8,94 @@ describe Gitlab::HookData::BaseBuilder do
end
end
- subject { subclass.new(nil) }
-
using RSpec::Parameterized::TableSyntax
- where do
- {
- 'relative image URL' => {
- input: '![an image](foo.png)',
- output: "![an image](#{Gitlab.config.gitlab.url}/foo.png)"
- },
- 'HTTP URL' => {
- input: '![an image](http://example.com/foo.png)',
- output: '![an image](http://example.com/foo.png)'
- },
- 'HTTPS URL' => {
- input: '![an image](https://example.com/foo.png)',
- output: '![an image](https://example.com/foo.png)'
- },
- 'protocol-relative URL' => {
- input: '![an image](//example.com/foo.png)',
- output: '![an image](//example.com/foo.png)'
- },
- 'URL reference by title' => {
- input: "![foo]\n\n[foo]: foo.png",
- output: "![foo]\n\n[foo]: foo.png"
- },
- 'URL reference by label' => {
- input: "![][foo]\n\n[foo]: foo.png",
- output: "![][foo]\n\n[foo]: foo.png"
- },
- 'in Markdown inline code block' => {
- input: '`![an image](foo.png)`',
- output: "`![an image](#{Gitlab.config.gitlab.url}/foo.png)`"
- },
- 'in HTML tag on the same line' => {
- input: '<p>![an image](foo.png)</p>',
- output: "<p>![an image](#{Gitlab.config.gitlab.url}/foo.png)</p>"
- },
- 'in Markdown multi-line code block' => {
- input: "```\n![an image](foo.png)\n```",
- output: "```\n![an image](foo.png)\n```"
- },
- 'in HTML tag on different lines' => {
- input: "<p>\n![an image](foo.png)\n</p>",
- output: "<p>\n![an image](foo.png)\n</p>"
+ context 'with an upload prefix specified' do
+ let(:project_with_path) { double(full_path: 'baz/bar') }
+ let(:object_with_project) { double(project: project_with_path) }
+ subject { subclass.new(object_with_project) }
+
+ where do
+ {
+ 'relative image URL' => {
+ input: '![an image](foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/foo.png)"
+ },
+ 'absolute upload URL' => {
+ input: '![an image](/uploads/foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/baz/bar/uploads/foo.png)"
+ },
+ 'absolute non-upload URL' => {
+ input: '![an image](/downloads/foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/downloads/foo.png)"
+ }
}
- }
+ end
+
+ with_them do
+ it { expect(subject.absolute_image_urls(input)).to eq(output) }
+ end
end
- with_them do
- it { expect(subject.absolute_image_urls(input)).to eq(output) }
+ context 'without an upload prefix specified' do
+ subject { subclass.new(nil) }
+
+ where do
+ {
+ 'relative image URL' => {
+ input: '![an image](foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/foo.png)"
+ },
+ 'absolute upload URL' => {
+ input: '![an image](/uploads/foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/uploads/foo.png)"
+ },
+ 'absolute non-upload URL' => {
+ input: '![an image](/downloads/foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/downloads/foo.png)"
+ },
+ 'HTTP URL' => {
+ input: '![an image](http://example.com/foo.png)',
+ output: '![an image](http://example.com/foo.png)'
+ },
+ 'HTTPS URL' => {
+ input: '![an image](https://example.com/foo.png)',
+ output: '![an image](https://example.com/foo.png)'
+ },
+ 'protocol-relative URL' => {
+ input: '![an image](//example.com/foo.png)',
+ output: '![an image](//example.com/foo.png)'
+ },
+ 'URL reference by title' => {
+ input: "![foo]\n\n[foo]: foo.png",
+ output: "![foo]\n\n[foo]: foo.png"
+ },
+ 'URL reference by label' => {
+ input: "![][foo]\n\n[foo]: foo.png",
+ output: "![][foo]\n\n[foo]: foo.png"
+ },
+ 'in Markdown inline code block' => {
+ input: '`![an image](foo.png)`',
+ output: "`![an image](#{Gitlab.config.gitlab.url}/foo.png)`"
+ },
+ 'in HTML tag on the same line' => {
+ input: '<p>![an image](foo.png)</p>',
+ output: "<p>![an image](#{Gitlab.config.gitlab.url}/foo.png)</p>"
+ },
+ 'in Markdown multi-line code block' => {
+ input: "```\n![an image](foo.png)\n```",
+ output: "```\n![an image](foo.png)\n```"
+ },
+ 'in HTML tag on different lines' => {
+ input: "<p>\n![an image](foo.png)\n</p>",
+ output: "<p>\n![an image](foo.png)\n</p>"
+ }
+ }
+ end
+
+ with_them do
+ it { expect(subject.absolute_image_urls(input)).to eq(output) }
+ end
end
end
end
diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
index 60093474f8a..f066c0e3813 100644
--- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
@@ -46,7 +46,10 @@ describe Gitlab::HookData::IssueBuilder do
let(:builder) { described_class.new(issue_with_description) }
it 'sets the image to use an absolute URL' do
- expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ expected_path = "#{issue_with_description.project.path_with_namespace}/uploads/abc/Issue_Image.png"
+
+ expect(data[:description])
+ .to eq("test![Issue_Image](#{Settings.gitlab.url}/#{expected_path})")
end
end
end
diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
index dd586af6118..9ce697adbba 100644
--- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
@@ -58,11 +58,14 @@ describe Gitlab::HookData::MergeRequestBuilder do
end
context 'when the MR has an image in the description' do
- let(:mr_with_description) { create(:merge_request, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:mr_with_description) { create(:merge_request, description: 'test![MR_Image](/uploads/abc/MR_Image.png)') }
let(:builder) { described_class.new(mr_with_description) }
it 'sets the image to use an absolute URL' do
- expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ expected_path = "#{mr_with_description.project.path_with_namespace}/uploads/abc/MR_Image.png"
+
+ expect(data[:description])
+ .to eq("test![MR_Image](#{Settings.gitlab.url}/#{expected_path})")
end
end
end
diff --git a/spec/lib/gitlab/http_spec.rb b/spec/lib/gitlab/http_spec.rb
index d0dadfa78da..6c37c157f5d 100644
--- a/spec/lib/gitlab/http_spec.rb
+++ b/spec/lib/gitlab/http_spec.rb
@@ -46,4 +46,30 @@ describe Gitlab::HTTP do
end
end
end
+
+ describe 'handle redirect loops' do
+ before do
+ WebMock.stub_request(:any, "http://example.org").to_raise(HTTParty::RedirectionTooDeep.new("Redirection Too Deep"))
+ end
+
+ it 'handles GET requests' do
+ expect { described_class.get('http://example.org') }.to raise_error(Gitlab::HTTP::RedirectionTooDeep)
+ end
+
+ it 'handles POST requests' do
+ expect { described_class.post('http://example.org') }.to raise_error(Gitlab::HTTP::RedirectionTooDeep)
+ end
+
+ it 'handles PUT requests' do
+ expect { described_class.put('http://example.org') }.to raise_error(Gitlab::HTTP::RedirectionTooDeep)
+ end
+
+ it 'handles DELETE requests' do
+ expect { described_class.delete('http://example.org') }.to raise_error(Gitlab::HTTP::RedirectionTooDeep)
+ end
+
+ it 'handles HEAD requests' do
+ expect { described_class.head('http://example.org') }.to raise_error(Gitlab::HTTP::RedirectionTooDeep)
+ end
+ end
end
diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb
index 0385dd762c2..1e583f4cee2 100644
--- a/spec/lib/gitlab/identifier_spec.rb
+++ b/spec/lib/gitlab/identifier_spec.rb
@@ -11,11 +11,8 @@ describe Gitlab::Identifier do
describe '#identify' do
context 'without an identifier' do
- it 'identifies the user using a commit' do
- expect(identifier).to receive(:identify_using_commit)
- .with(project, '123')
-
- identifier.identify('', project, '123')
+ it 'returns nil' do
+ expect(identifier.identify('')).to be nil
end
end
@@ -24,7 +21,7 @@ describe Gitlab::Identifier do
expect(identifier).to receive(:identify_using_user)
.with("user-#{user.id}")
- identifier.identify("user-#{user.id}", project, '123')
+ identifier.identify("user-#{user.id}")
end
end
@@ -33,49 +30,11 @@ describe Gitlab::Identifier do
expect(identifier).to receive(:identify_using_ssh_key)
.with("key-#{key.id}")
- identifier.identify("key-#{key.id}", project, '123')
+ identifier.identify("key-#{key.id}")
end
end
end
- describe '#identify_using_commit' do
- it "returns the User for an existing commit author's Email address" do
- commit = double(:commit, author: user, author_email: user.email)
-
- expect(project).to receive(:commit).with('123').and_return(commit)
-
- expect(identifier.identify_using_commit(project, '123')).to eq(user)
- end
-
- it 'returns nil when no user could be found' do
- allow(project).to receive(:commit).with('123').and_return(nil)
-
- expect(identifier.identify_using_commit(project, '123')).to be_nil
- end
-
- it 'returns nil when the commit does not have an author Email' do
- commit = double(:commit, author_email: nil)
-
- expect(project).to receive(:commit).with('123').and_return(commit)
-
- expect(identifier.identify_using_commit(project, '123')).to be_nil
- end
-
- it 'caches the found users per Email' do
- commit = double(:commit, author: user, author_email: user.email)
-
- expect(project).to receive(:commit).with('123').twice.and_return(commit)
-
- 2.times do
- expect(identifier.identify_using_commit(project, '123')).to eq(user)
- end
- end
-
- it 'returns nil if the project & ref are not present' do
- expect(identifier.identify_using_commit(nil, nil)).to be_nil
- end
- end
-
describe '#identify_using_user' do
it 'returns the User for an existing ID in the identifier' do
found = identifier.identify_using_user("user-#{user.id}")
diff --git a/spec/lib/gitlab/import/database_helpers_spec.rb b/spec/lib/gitlab/import/database_helpers_spec.rb
new file mode 100644
index 00000000000..e716155b7d5
--- /dev/null
+++ b/spec/lib/gitlab/import/database_helpers_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Import::DatabaseHelpers do
+ let(:database_helper) do
+ Class.new do
+ include Gitlab::Import::DatabaseHelpers
+ end
+ end
+
+ subject { database_helper.new }
+
+ describe '.insert_and_return_id' do
+ let(:attributes) { { iid: 1, title: 'foo' } }
+ let(:project) { create(:project) }
+
+ context 'on PostgreSQL' do
+ it 'returns the ID returned by the query' do
+ expect(Gitlab::Database)
+ .to receive(:bulk_insert)
+ .with(Issue.table_name, [attributes], return_ids: true)
+ .and_return([10])
+
+ id = subject.insert_and_return_id(attributes, project.issues)
+
+ expect(id).to eq(10)
+ end
+ end
+
+ context 'on MySQL' do
+ it 'uses a separate query to retrieve the ID' do
+ issue = create(:issue, project: project, iid: attributes[:iid])
+
+ expect(Gitlab::Database)
+ .to receive(:bulk_insert)
+ .with(Issue.table_name, [attributes], return_ids: true)
+ .and_return([])
+
+ id = subject.insert_and_return_id(attributes, project.issues)
+
+ expect(id).to eq(issue.id)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import/merge_request_creator_spec.rb b/spec/lib/gitlab/import/merge_request_creator_spec.rb
new file mode 100644
index 00000000000..7c73e9b39f7
--- /dev/null
+++ b/spec/lib/gitlab/import/merge_request_creator_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Import::MergeRequestCreator do
+ let(:project) { create(:project, :repository) }
+
+ subject { described_class.new(project) }
+
+ describe '#execute' do
+ context 'merge request already exists' do
+ let(:merge_request) { create(:merge_request, target_project: project, source_project: project) }
+ let(:commits) { merge_request.merge_request_diffs.first.commits }
+ let(:attributes) { HashWithIndifferentAccess.new(merge_request.attributes.except("merge_params")) }
+
+ it 'updates the data' do
+ commits_count = commits.count
+ merge_request.merge_request_diffs.destroy_all # rubocop: disable DestroyAll
+
+ expect(merge_request.merge_request_diffs.count).to eq(0)
+
+ subject.execute(attributes)
+
+ expect(merge_request.reload.merge_request_diffs.count).to eq(1)
+ expect(merge_request.reload.merge_request_diffs.first.commits.count).to eq(commits_count)
+ end
+ end
+
+ context 'new merge request' do
+ let(:merge_request) { build(:merge_request, target_project: project, source_project: project) }
+ let(:attributes) { HashWithIndifferentAccess.new(merge_request.attributes.except("merge_params")) }
+
+ it 'creates a new merge request' do
+ attributes.delete(:id)
+
+ expect { subject.execute(attributes) }.to change { MergeRequest.count }.by(1)
+
+ new_mr = MergeRequest.last
+ expect(new_mr.merge_request_diffs.count).to eq(1)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb
deleted file mode 100644
index 5059d68e54b..00000000000
--- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
- let!(:service) { described_class.new }
- let!(:project) { create(:project, :with_object_export) }
- let(:shared) { project.import_export_shared }
- let!(:user) { create(:user) }
-
- describe '#execute' do
- before do
- allow(service).to receive(:strategy_execute)
- stub_feature_flags(import_export_object_storage: true)
- end
-
- it 'returns if project exported file is not found' do
- allow(project).to receive(:export_project_object_exists?).and_return(false)
-
- expect(service).not_to receive(:strategy_execute)
-
- service.execute(user, project)
- end
-
- it 'creates a lock file in the export dir' do
- allow(service).to receive(:delete_after_export_lock)
-
- service.execute(user, project)
-
- expect(lock_path_exist?).to be_truthy
- end
-
- context 'when the method succeeds' do
- it 'removes the lock file' do
- service.execute(user, project)
-
- expect(lock_path_exist?).to be_falsey
- end
- end
-
- context 'when the method fails' do
- before do
- allow(service).to receive(:strategy_execute).and_call_original
- end
-
- context 'when validation fails' do
- before do
- allow(service).to receive(:invalid?).and_return(true)
- end
-
- it 'does not create the lock file' do
- expect(service).not_to receive(:create_or_update_after_export_lock)
-
- service.execute(user, project)
- end
-
- it 'does not execute main logic' do
- expect(service).not_to receive(:strategy_execute)
-
- service.execute(user, project)
- end
-
- it 'logs validation errors in shared context' do
- expect(service).to receive(:log_validation_errors)
-
- service.execute(user, project)
- end
- end
-
- context 'when an exception is raised' do
- it 'removes the lock' do
- expect { service.execute(user, project) }.to raise_error(NotImplementedError)
-
- expect(lock_path_exist?).to be_falsey
- end
- end
- end
- end
-
- describe '#log_validation_errors' do
- it 'add the message to the shared context' do
- errors = %w(test_message test_message2)
-
- allow(service).to receive(:invalid?).and_return(true)
- allow(service.errors).to receive(:full_messages).and_return(errors)
-
- expect(shared).to receive(:add_error_message).twice.and_call_original
-
- service.execute(user, project)
-
- expect(shared.errors).to eq errors
- end
- end
-
- describe '#to_json' do
- it 'adds the current strategy class to the serialized attributes' do
- params = { param1: 1 }
- result = params.merge(klass: described_class.to_s).to_json
-
- expect(described_class.new(params).to_json).to eq result
- end
- end
-
- def lock_path_exist?
- File.exist?(described_class.lock_file_path(project))
- end
-end
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
index 566b7f46c87..9a442de2900 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
@@ -9,11 +9,10 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
describe '#execute' do
before do
allow(service).to receive(:strategy_execute)
- stub_feature_flags(import_export_object_storage: false)
end
it 'returns if project exported file is not found' do
- allow(project).to receive(:export_project_path).and_return(nil)
+ allow(project).to receive(:export_file_exists?).and_return(false)
expect(service).not_to receive(:strategy_execute)
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
index 7f2e0a4ee2c..ec17ad8541f 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
@@ -24,34 +24,13 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
end
describe '#execute' do
- context 'without object storage' do
- before do
- stub_feature_flags(import_export_object_storage: false)
- end
-
- it 'removes the exported project file after the upload' do
- allow(strategy).to receive(:send_file)
- allow(strategy).to receive(:handle_response_error)
-
- expect(project).to receive(:remove_exported_project_file)
-
- strategy.execute(user, project)
- end
- end
-
- context 'with object storage' do
- before do
- stub_feature_flags(import_export_object_storage: true)
- end
+ it 'removes the exported project file after the upload' do
+ allow(strategy).to receive(:send_file)
+ allow(strategy).to receive(:handle_response_error)
- it 'removes the exported project file after the upload' do
- allow(strategy).to receive(:send_file)
- allow(strategy).to receive(:handle_response_error)
+ expect(project).to receive(:remove_exports)
- expect(project).to receive(:remove_exported_project_file)
-
- strategy.execute(user, project)
- end
+ strategy.execute(user, project)
end
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index e9a1932407d..1d184375a52 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -117,6 +117,7 @@ pipelines:
- retryable_builds
- cancelable_statuses
- manual_actions
+- scheduled_actions
- artifacts
- pipeline_schedule
- merge_requests
@@ -197,6 +198,7 @@ project:
- last_event
- services
- campfire_service
+- discord_service
- drone_ci_service
- emails_on_push_service
- pipelines_email_service
@@ -210,7 +212,6 @@ project:
- flowdock_service
- assembla_service
- asana_service
-- gemnasium_service
- slack_service
- microsoft_teams_service
- mattermost_service
@@ -229,9 +230,8 @@ project:
- mock_ci_service
- mock_deployment_service
- mock_monitoring_service
-- forked_project_link
+- forked_to_members
- forked_from_project
-- forked_project_links
- forks
- merge_requests
- fork_merge_requests
@@ -289,6 +289,7 @@ project:
- fork_network_member
- fork_network
- custom_attributes
+- prometheus_metrics
- lfs_file_locks
- project_badges
- source_of_merge_requests
@@ -299,11 +300,14 @@ project:
- ci_cd_settings
- import_export_upload
- repository_languages
+- pool_repository
award_emoji:
- awardable
- user
priorities:
- label
+prometheus_metrics:
+- project
timelogs:
- issue
- merge_request
@@ -322,3 +326,9 @@ metrics:
- latest_closed_by
- merged_by
- pipeline
+resource_label_events:
+- user
+- issue
+- merge_request
+- epic
+- label
diff --git a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
index 65f073b2df3..87ab81d8169 100644
--- a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
@@ -23,15 +23,23 @@ describe 'Import/Export attribute configuration' do
let(:safe_attributes_file) { 'spec/lib/gitlab/import_export/safe_model_attributes.yml' }
let(:safe_model_attributes) { YAML.load_file(safe_attributes_file) }
+ let(:ee_safe_attributes_file) { 'ee/spec/lib/gitlab/import_export/safe_model_attributes.yml' }
+ let(:ee_safe_model_attributes) { File.exist?(ee_safe_attributes_file) ? YAML.load_file(ee_safe_attributes_file) : {} }
+
it 'has no new columns' do
relation_names.each do |relation_name|
relation_class = relation_class_for_name(relation_name)
relation_attributes = relation_class.new.attributes.keys
- expect(safe_model_attributes[relation_class.to_s]).not_to be_nil, "Expected exported class #{relation_class} to exist in safe_model_attributes"
-
current_attributes = parsed_attributes(relation_name, relation_attributes)
- safe_attributes = safe_model_attributes[relation_class.to_s]
+ safe_attributes = safe_model_attributes[relation_class.to_s].dup || []
+
+ ee_safe_model_attributes[relation_class.to_s].to_a.each do |attribute|
+ safe_attributes << attribute
+ end
+
+ expect(safe_attributes).not_to be_nil, "Expected exported class #{relation_class} to exist in safe_model_attributes"
+
new_attributes = current_attributes - safe_attributes
expect(new_attributes).to be_empty, failure_message(relation_class.to_s, new_attributes)
@@ -43,6 +51,7 @@ describe 'Import/Export attribute configuration' do
It looks like #{relation_class}, which is exported using the project Import/Export, has new attributes: #{new_attributes.join(',')}
Please add the attribute(s) to SAFE_MODEL_ATTRIBUTES if you consider this can be exported.
+ #{"If the model/associations are EE-specific, use `#{File.expand_path(ee_safe_attributes_file)}`.\n" if ee_safe_model_attributes.any?}
Otherwise, please blacklist the attribute(s) in IMPORT_EXPORT_CONFIG by adding it to its correspondent
model in the +excluded_attributes+ section.
diff --git a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
index 4897d604bc1..e44ff6bbcbd 100644
--- a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
@@ -6,22 +6,35 @@ describe Gitlab::ImportExport::AvatarRestorer do
let(:shared) { project.import_export_shared }
let(:project) { create(:project) }
- before do
- allow_any_instance_of(described_class).to receive(:avatar_export_file)
- .and_return(uploaded_image_temp_path)
- end
-
after do
project.remove_avatar!
end
- it 'restores a project avatar' do
- expect(described_class.new(project: project, shared: shared).restore).to be true
+ context 'with avatar' do
+ before do
+ allow_any_instance_of(described_class).to receive(:avatar_export_file)
+ .and_return(uploaded_image_temp_path)
+ end
+
+ it 'restores a project avatar' do
+ expect(described_class.new(project: project, shared: shared).restore).to be true
+ end
+
+ it 'saves the avatar into the project' do
+ described_class.new(project: project, shared: shared).restore
+
+ expect(project.reload.avatar.file.exists?).to be true
+ end
end
- it 'saves the avatar into the project' do
- described_class.new(project: project, shared: shared).restore
+ it 'does not break if there is just a directory' do
+ Dir.mktmpdir do |tmpdir|
+ FileUtils.mkdir_p("#{tmpdir}/a/b")
+
+ allow_any_instance_of(described_class).to receive(:avatar_export_path)
+ .and_return("#{tmpdir}/a")
- expect(project.reload.avatar.file.exists?).to be true
+ expect(described_class.new(project: project, shared: shared).restore).to be true
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
index 90e6d653d34..2bd1b9924c6 100644
--- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -8,8 +8,7 @@ describe Gitlab::ImportExport::AvatarSaver do
before do
FileUtils.mkdir_p("#{shared.export_path}/avatar/")
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
- stub_feature_flags(import_export_object_storage: false)
+ allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:export_path).and_return(export_path)
end
after do
@@ -19,7 +18,7 @@ describe Gitlab::ImportExport::AvatarSaver do
it 'saves a project avatar' do
described_class.new(project: project_with_avatar, shared: shared).save
- expect(File).to exist("#{shared.export_path}/avatar/dk.png")
+ expect(File).to exist(Dir["#{shared.export_path}/avatar/**/dk.png"].first)
end
it 'is fine not to have an avatar' do
diff --git a/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb b/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb
deleted file mode 100644
index 287745eb40e..00000000000
--- a/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::ImportExport::FileImporter do
- let(:shared) { Gitlab::ImportExport::Shared.new(nil) }
- let(:storage_path) { "#{Dir.tmpdir}/file_importer_spec" }
- let(:valid_file) { "#{shared.export_path}/valid.json" }
- let(:symlink_file) { "#{shared.export_path}/invalid.json" }
- let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" }
- let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" }
- let(:evil_symlink_file) { "#{shared.export_path}/.\nevil" }
-
- before do
- stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
- stub_feature_flags(import_export_object_storage: true)
- stub_uploads_object_storage(FileUploader)
-
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(storage_path)
- allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true)
- allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:relative_archive_path).and_return('test')
- allow(SecureRandom).to receive(:hex).and_return('abcd')
- setup_files
- end
-
- after do
- FileUtils.rm_rf(storage_path)
- end
-
- context 'normal run' do
- before do
- described_class.import(project: build(:project), archive_file: '', shared: shared)
- end
-
- it 'removes symlinks in root folder' do
- expect(File.exist?(symlink_file)).to be false
- end
-
- it 'removes hidden symlinks in root folder' do
- expect(File.exist?(hidden_symlink_file)).to be false
- end
-
- it 'removes evil symlinks in root folder' do
- expect(File.exist?(evil_symlink_file)).to be false
- end
-
- it 'removes symlinks in subfolders' do
- expect(File.exist?(subfolder_symlink_file)).to be false
- end
-
- it 'does not remove a valid file' do
- expect(File.exist?(valid_file)).to be true
- end
-
- it 'creates the file in the right subfolder' do
- expect(shared.export_path).to include('test/abcd')
- end
- end
-
- context 'error' do
- before do
- allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
- described_class.import(project: build(:project), archive_file: '', shared: shared)
- end
-
- it 'removes symlinks in root folder' do
- expect(File.exist?(symlink_file)).to be false
- end
-
- it 'removes hidden symlinks in root folder' do
- expect(File.exist?(hidden_symlink_file)).to be false
- end
-
- it 'removes symlinks in subfolders' do
- expect(File.exist?(subfolder_symlink_file)).to be false
- end
-
- it 'does not remove a valid file' do
- expect(File.exist?(valid_file)).to be true
- end
- end
-
- def setup_files
- FileUtils.mkdir_p("#{shared.export_path}/subfolder/")
- FileUtils.touch(valid_file)
- FileUtils.ln_s(valid_file, symlink_file)
- FileUtils.ln_s(valid_file, subfolder_symlink_file)
- FileUtils.ln_s(valid_file, hidden_symlink_file)
- FileUtils.ln_s(valid_file, evil_symlink_file)
- end
-end
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
index 78fccdf1dfc..bf34cefe18f 100644
--- a/spec/lib/gitlab/import_export/file_importer_spec.rb
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::FileImporter do
let(:shared) { Gitlab::ImportExport::Shared.new(nil) }
- let(:export_path) { "#{Dir.tmpdir}/file_importer_spec" }
+ let(:storage_path) { "#{Dir.tmpdir}/file_importer_spec" }
let(:valid_file) { "#{shared.export_path}/valid.json" }
let(:symlink_file) { "#{shared.export_path}/invalid.json" }
let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" }
@@ -11,7 +11,9 @@ describe Gitlab::ImportExport::FileImporter do
before do
stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_uploads_object_storage(FileUploader)
+
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(storage_path)
allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true)
allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:relative_archive_path).and_return('test')
allow(SecureRandom).to receive(:hex).and_return('abcd')
@@ -19,12 +21,12 @@ describe Gitlab::ImportExport::FileImporter do
end
after do
- FileUtils.rm_rf(export_path)
+ FileUtils.rm_rf(storage_path)
end
context 'normal run' do
before do
- described_class.import(project: nil, archive_file: '', shared: shared)
+ described_class.import(project: build(:project), archive_file: '', shared: shared)
end
it 'removes symlinks in root folder' do
@@ -55,7 +57,7 @@ describe Gitlab::ImportExport::FileImporter do
context 'error' do
before do
allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
- described_class.import(project: nil, archive_file: '', shared: shared)
+ described_class.import(project: build(:project), archive_file: '', shared: shared)
end
it 'removes symlinks in root folder' do
diff --git a/spec/lib/gitlab/import_export/importer_object_storage_spec.rb b/spec/lib/gitlab/import_export/importer_object_storage_spec.rb
deleted file mode 100644
index 24a994b3611..00000000000
--- a/spec/lib/gitlab/import_export/importer_object_storage_spec.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::ImportExport::Importer do
- let(:user) { create(:user) }
- let(:test_path) { "#{Dir.tmpdir}/importer_spec" }
- let(:shared) { project.import_export_shared }
- let(:project) { create(:project) }
- let(:import_file) { fixture_file_upload('spec/features/projects/import_export/test_project_export.tar.gz') }
-
- subject(:importer) { described_class.new(project) }
-
- before do
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path)
- allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file)
- stub_feature_flags(import_export_object_storage: true)
- stub_uploads_object_storage(FileUploader)
-
- FileUtils.mkdir_p(shared.export_path)
- ImportExportUpload.create(project: project, import_file: import_file)
- end
-
- after do
- FileUtils.rm_rf(test_path)
- end
-
- describe '#execute' do
- it 'succeeds' do
- importer.execute
-
- expect(shared.errors).to be_empty
- end
-
- it 'extracts the archive' do
- expect(Gitlab::ImportExport::FileImporter).to receive(:import).and_call_original
-
- importer.execute
- end
-
- it 'checks the version' do
- expect(Gitlab::ImportExport::VersionChecker).to receive(:check!).and_call_original
-
- importer.execute
- end
-
- context 'all restores are executed' do
- [
- Gitlab::ImportExport::AvatarRestorer,
- Gitlab::ImportExport::RepoRestorer,
- Gitlab::ImportExport::WikiRestorer,
- Gitlab::ImportExport::UploadsRestorer,
- Gitlab::ImportExport::LfsRestorer,
- Gitlab::ImportExport::StatisticsRestorer
- ].each do |restorer|
- it "calls the #{restorer}" do
- fake_restorer = double(restorer.to_s)
-
- expect(fake_restorer).to receive(:restore).and_return(true).at_least(1)
- expect(restorer).to receive(:new).and_return(fake_restorer).at_least(1)
-
- importer.execute
- end
- end
-
- it 'restores the ProjectTree' do
- expect(Gitlab::ImportExport::ProjectTreeRestorer).to receive(:new).and_call_original
-
- importer.execute
- end
-
- it 'removes the import file' do
- expect(importer).to receive(:remove_import_file).and_call_original
-
- importer.execute
-
- expect(project.import_export_upload.import_file&.file).to be_nil
- end
- end
-
- context 'when project successfully restored' do
- let!(:existing_project) { create(:project, namespace: user.namespace) }
- let(:project) { create(:project, namespace: user.namespace, name: 'whatever', path: 'whatever') }
-
- before do
- restorers = double(:restorers, all?: true)
-
- allow(subject).to receive(:import_file).and_return(true)
- allow(subject).to receive(:check_version!).and_return(true)
- allow(subject).to receive(:restorers).and_return(restorers)
- allow(project).to receive(:import_data).and_return(double(data: { 'original_path' => existing_project.path }))
- end
-
- context 'when import_data' do
- context 'has original_path' do
- it 'overwrites existing project' do
- expect_any_instance_of(::Projects::OverwriteProjectService).to receive(:execute).with(existing_project)
-
- subject.execute
- end
- end
-
- context 'has not original_path' do
- before do
- allow(project).to receive(:import_data).and_return(double(data: {}))
- end
-
- it 'does not call the overwrite service' do
- expect_any_instance_of(::Projects::OverwriteProjectService).not_to receive(:execute).with(existing_project)
-
- subject.execute
- end
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb
index f07946824c4..11f98d782b1 100644
--- a/spec/lib/gitlab/import_export/importer_spec.rb
+++ b/spec/lib/gitlab/import_export/importer_spec.rb
@@ -4,16 +4,18 @@ describe Gitlab::ImportExport::Importer do
let(:user) { create(:user) }
let(:test_path) { "#{Dir.tmpdir}/importer_spec" }
let(:shared) { project.import_export_shared }
- let(:project) { create(:project, import_source: File.join(test_path, 'test_project_export.tar.gz')) }
+ let(:project) { create(:project) }
+ let(:import_file) { fixture_file_upload('spec/features/projects/import_export/test_project_export.tar.gz') }
subject(:importer) { described_class.new(project) }
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path)
allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file)
+ stub_uploads_object_storage(FileUploader)
FileUtils.mkdir_p(shared.export_path)
- FileUtils.cp(Rails.root.join('spec/features/projects/import_export/test_project_export.tar.gz'), test_path)
+ ImportExportUpload.create(project: project, import_file: import_file)
end
after do
@@ -63,6 +65,24 @@ describe Gitlab::ImportExport::Importer do
importer.execute
end
+
+ it 'removes the import file' do
+ expect(importer).to receive(:remove_import_file).and_call_original
+
+ importer.execute
+
+ expect(project.import_export_upload.import_file&.file).to be_nil
+ end
+
+ it 'sets the correct visibility_level when visibility level is a string' do
+ project.create_or_update_import_data(
+ data: { override_params: { visibility_level: Gitlab::VisibilityLevel::PRIVATE.to_s } }
+ )
+
+ importer.execute
+
+ expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
end
context 'when project successfully restored' do
@@ -75,7 +95,6 @@ describe Gitlab::ImportExport::Importer do
allow(subject).to receive(:import_file).and_return(true)
allow(subject).to receive(:check_version!).and_return(true)
allow(subject).to receive(:restorers).and_return(restorers)
- allow(restorers).to receive(:all?).and_return(true)
allow(project).to receive(:import_data).and_return(double(data: { 'original_path' => existing_project.path }))
end
diff --git a/spec/lib/gitlab/import_export/model_configuration_spec.rb b/spec/lib/gitlab/import_export/model_configuration_spec.rb
index 5cb8f2589c8..2e28f978c3a 100644
--- a/spec/lib/gitlab/import_export/model_configuration_spec.rb
+++ b/spec/lib/gitlab/import_export/model_configuration_spec.rb
@@ -16,14 +16,30 @@ describe 'Import/Export model configuration' do
# - User, Author... Models we do not care about for checking models
names.flatten.uniq - %w(milestones labels user author) + ['project']
end
+ let(:ce_models_yml) { 'spec/lib/gitlab/import_export/all_models.yml' }
+ let(:ce_models_hash) { YAML.load_file(ce_models_yml) }
+
+ let(:ee_models_yml) { 'ee/spec/lib/gitlab/import_export/all_models.yml' }
+ let(:ee_models_hash) { File.exist?(ee_models_yml) ? YAML.load_file(ee_models_yml) : {} }
- let(:all_models_yml) { 'spec/lib/gitlab/import_export/all_models.yml' }
- let(:all_models) { YAML.load_file(all_models_yml) }
let(:current_models) { setup_models }
+ let(:all_models_hash) do
+ all_models_hash = ce_models_hash.dup
+
+ all_models_hash.each do |model, associations|
+ associations.concat(ee_models_hash[model] || [])
+ end
+
+ ee_models_hash.each do |model, associations|
+ all_models_hash[model] ||= associations
+ end
+
+ all_models_hash
+ end
it 'has no new models' do
model_names.each do |model_name|
- new_models = Array(current_models[model_name]) - Array(all_models[model_name])
+ new_models = Array(current_models[model_name]) - Array(all_models_hash[model_name])
expect(new_models).to be_empty, failure_message(model_name.classify, new_models)
end
end
@@ -31,27 +47,21 @@ describe 'Import/Export model configuration' do
# List of current models between models, in the format of
# {model: [model_2, model3], ...}
def setup_models
- all_models_hash = {}
-
- model_names.each do |model_name|
- model_class = relation_class_for_name(model_name)
-
- all_models_hash[model_name] = associations_for(model_class) - ['project']
+ model_names.each_with_object({}) do |model_name, hash|
+ hash[model_name] = associations_for(relation_class_for_name(model_name)) - ['project']
end
-
- all_models_hash
end
def failure_message(parent_model_name, new_models)
- <<-MSG
+ <<~MSG
New model(s) <#{new_models.join(',')}> have been added, related to #{parent_model_name}, which is exported by
the Import/Export feature.
- If you think this model should be included in the export, please add it to IMPORT_EXPORT_CONFIG.
- Definitely add it to MODELS_JSON to signal that you've handled this error and to prevent it from showing up in the future.
+ If you think this model should be included in the export, please add it to `#{Gitlab::ImportExport.config_file}`.
- MODELS_JSON: #{File.expand_path(all_models_yml)}
- IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
+ Definitely add it to `#{File.expand_path(ce_models_yml)}`
+ #{"or `#{File.expand_path(ee_models_yml)}` if the model/associations are EE-specific\n" if ee_models_hash.any?}
+ to signal that you've handled this error and to prevent it from showing up in the future.
MSG
end
end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 1b7fa11cb3c..58949f76bd6 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -331,6 +331,28 @@
},
"events": []
}
+ ],
+ "resource_label_events": [
+ {
+ "id":244,
+ "action":"remove",
+ "issue_id":40,
+ "merge_request_id":null,
+ "label_id":2,
+ "user_id":1,
+ "created_at":"2018-08-28T08:24:00.494Z",
+ "label": {
+ "id": 2,
+ "title": "test2",
+ "color": "#428bca",
+ "project_id": 8,
+ "created_at": "2016-07-22T08:55:44.161Z",
+ "updated_at": "2016-07-22T08:55:44.161Z",
+ "template": false,
+ "description": "",
+ "type": "ProjectLabel"
+ }
+ }
]
},
{
@@ -2515,6 +2537,17 @@
"events": []
}
],
+ "resource_label_events": [
+ {
+ "id":243,
+ "action":"add",
+ "issue_id":null,
+ "merge_request_id":27,
+ "label_id":null,
+ "user_id":1,
+ "created_at":"2018-08-28T08:24:00.494Z"
+ }
+ ],
"merge_request_diff": {
"id": 27,
"state": "collected",
@@ -2523,7 +2556,7 @@
"merge_request_diff_id": 27,
"relative_order": 0,
"sha": "bb5206fee213d983da88c47f9cf4cc6caf9c66dc",
- "message": "Feature conflcit added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n",
+ "message": "Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n",
"authored_date": "2014-08-06T08:35:52.000+02:00",
"author_name": "Dmitriy Zaporozhets",
"author_email": "dmitriy.zaporozhets@gmail.com",
@@ -3572,7 +3605,7 @@
"merge_request_diff_id": 14,
"relative_order": 8,
"sha": "08f22f255f082689c0d7d39d19205085311542bc",
- "message": "remove emtpy file.(beacase git ignore empty file)\nadd whitespace test file.\n",
+ "message": "remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n",
"authored_date": "2015-11-13T06:00:16.000+01:00",
"author_name": "윤민ì‹",
"author_email": "minsik.yoon@samsung.com",
@@ -4257,7 +4290,7 @@
"merge_request_diff_id": 13,
"relative_order": 8,
"sha": "08f22f255f082689c0d7d39d19205085311542bc",
- "message": "remove emtpy file.(beacase git ignore empty file)\nadd whitespace test file.\n",
+ "message": "remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n",
"authored_date": "2015-11-13T06:00:16.000+01:00",
"author_name": "윤민ì‹",
"author_email": "minsik.yoon@samsung.com",
@@ -4766,7 +4799,7 @@
"merge_request_diff_id": 12,
"relative_order": 8,
"sha": "08f22f255f082689c0d7d39d19205085311542bc",
- "message": "remove emtpy file.(beacase git ignore empty file)\nadd whitespace test file.\n",
+ "message": "remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n",
"authored_date": "2015-11-13T06:00:16.000+01:00",
"author_name": "윤민ì‹",
"author_email": "minsik.yoon@samsung.com",
@@ -5474,7 +5507,7 @@
"merge_request_diff_id": 10,
"relative_order": 8,
"sha": "08f22f255f082689c0d7d39d19205085311542bc",
- "message": "remove emtpy file.(beacase git ignore empty file)\nadd whitespace test file.\n",
+ "message": "remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n",
"authored_date": "2015-11-13T06:00:16.000+01:00",
"author_name": "윤민ì‹",
"author_email": "minsik.yoon@samsung.com",
@@ -6110,7 +6143,7 @@
"id": 36,
"project_id": 5,
"ref": "master",
- "sha": "be93687618e4b132087f430a4d8fc3a609c9b77c",
+ "sha": "sha-notes",
"before_sha": null,
"push_data": null,
"created_at": "2016-03-22T15:20:35.755Z",
@@ -6121,6 +6154,7 @@
"status": "failed",
"started_at": null,
"finished_at": null,
+ "user_id": 9999,
"duration": null,
"notes": [
{
@@ -6320,6 +6354,7 @@
},
{
"id": 38,
+ "iid": 1,
"project_id": 5,
"ref": "master",
"sha": "5f923865dde3436854e9ceb9cdb7815618d4e849",
diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json
index ba2248073f5..2971ca0f0f8 100644
--- a/spec/lib/gitlab/import_export/project.light.json
+++ b/spec/lib/gitlab/import_export/project.light.json
@@ -101,6 +101,28 @@
]
}
],
+ "services": [
+ {
+ "id": 100,
+ "title": "JetBrains TeamCity CI",
+ "project_id": 5,
+ "created_at": "2016-06-14T15:01:51.315Z",
+ "updated_at": "2016-06-14T15:01:51.315Z",
+ "active": false,
+ "properties": {},
+ "template": true,
+ "push_events": true,
+ "issues_events": true,
+ "merge_requests_events": true,
+ "tag_push_events": true,
+ "note_events": true,
+ "job_events": true,
+ "type": "TeamcityService",
+ "category": "ci",
+ "default": false,
+ "wiki_page_events": true
+ }
+ ],
"snippets": [],
"hooks": []
}
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index a88ac0a091e..7171e12a849 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -59,7 +59,11 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
it 'creates a valid pipeline note' do
- expect(Ci::Pipeline.first.notes).not_to be_empty
+ expect(Ci::Pipeline.find_by_sha('sha-notes').notes).not_to be_empty
+ end
+
+ it 'pipeline has the correct user ID' do
+ expect(Ci::Pipeline.find_by_sha('sha-notes').user_id).to eq(@user.id)
end
it 'restores pipelines with missing ref' do
@@ -89,6 +93,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(ProtectedTag.first.create_access_levels).not_to be_empty
end
+ it 'restores issue resource label events' do
+ expect(Issue.find_by(title: 'Voluptatem').resource_label_events).not_to be_empty
+ end
+
+ it 'restores merge requests resource label events' do
+ expect(MergeRequest.find_by(title: 'MR1').resource_label_events).not_to be_empty
+ end
+
context 'event at forth level of the tree' do
let(:event) { Event.where(action: 6).first }
@@ -285,7 +297,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
issues: 1,
labels: 1,
milestones: 1,
- first_issue_labels: 1
+ first_issue_labels: 1,
+ services: 1
context 'project.json file access check' do
it 'does not read a symlink' do
@@ -309,7 +322,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
end
- context 'when the project has overriden params in import data' do
+ context 'when the project has overridden params in import data' do
it 'overwrites the params stored in the JSON' do
project.create_import_data(data: { override_params: { description: "Overridden" } })
@@ -323,7 +336,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
restored_project_json
- expect(project.lfs_enabled).to be_nil
+ expect(project.lfs_enabled).to be_falsey
end
end
@@ -370,6 +383,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json")
end
+ it 'does not import any templated services' do
+ restored_project_json
+
+ expect(project.services.where(template: true).count).to eq(0)
+ end
+
it 'imports labels' do
create(:group_label, name: 'Another label', group: project.group)
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index fec8a2af9ab..5dc372263ad 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -169,6 +169,14 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(priorities.flatten).not_to be_empty
end
+ it 'has issue resource label events' do
+ expect(saved_project_json['issues'].first['resource_label_events']).not_to be_empty
+ end
+
+ it 'has merge request resource label events' do
+ expect(saved_project_json['merge_requests'].first['resource_label_events']).not_to be_empty
+ end
+
it 'saves the correct service type' do
expect(saved_project_json['services'].first['type']).to eq('CustomIssueTrackerService')
end
@@ -291,6 +299,9 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
project: project,
commit_id: ci_build.pipeline.sha)
+ create(:resource_label_event, label: project_label, issue: issue)
+ create(:resource_label_event, label: group_label, merge_request: merge_request)
+
create(:event, :created, target: milestone, project: project, author: user)
create(:service, project: project, type: 'CustomIssueTrackerService', category: 'issue_tracker', properties: { one: 'value' })
diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb
index cf9e0f71910..a31f77484d8 100644
--- a/spec/lib/gitlab/import_export/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb
@@ -191,9 +191,7 @@ describe Gitlab::ImportExport::RelationFactory do
"author" => {
"name" => "Administrator"
},
- "events" => [
-
- ]
+ "events" => []
}
end
diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
index 7ffa84f906d..8a699eb1461 100644
--- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Gitlab::ImportExport::RepoRestorer do
+ include GitHelpers
+
describe 'bundle a project Git repo' do
let(:user) { create(:user) }
let!(:project_with_repo) { create(:project, :repository, name: 'test-repo-restorer', path: 'test-repo-restorer') }
@@ -36,9 +38,7 @@ describe Gitlab::ImportExport::RepoRestorer do
it 'has the webhooks' do
restorer.restore
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- expect(Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository)).to exist
- end
+ expect(project_hook_exists?(project)).to be true
end
end
end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 0a1e3eb83d3..f7935149b23 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -300,6 +300,7 @@ CommitStatus:
- retried
- protected
- failure_reason
+- scheduled_at
Ci::Variable:
- id
- project_id
@@ -416,6 +417,7 @@ ProjectHook:
- type
- service_id
- push_events
+- push_events_branch_filter
- issues_events
- merge_requests_events
- tag_push_events
@@ -491,6 +493,7 @@ ProjectFeature:
- snippets_access_level
- builds_access_level
- repository_access_level
+- pages_access_level
- created_at
- updated_at
ProtectedBranch::MergeAccessLevel:
@@ -554,6 +557,19 @@ ProjectCustomAttribute:
- project_id
- key
- value
+PrometheusMetric:
+- id
+- created_at
+- updated_at
+- project_id
+- y_label
+- unit
+- legend
+- title
+- query
+- group
+- common
+- identifier
Badge:
- id
- link_url
@@ -565,3 +581,11 @@ Badge:
- type
ProjectCiCdSetting:
- group_runners_enabled
+ResourceLabelEvent:
+- id
+- action
+- issue_id
+- merge_request_id
+- label_id
+- user_id
+- created_at
diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb
index 02f1a4b81aa..d185ff2dfcc 100644
--- a/spec/lib/gitlab/import_export/saver_spec.rb
+++ b/spec/lib/gitlab/import_export/saver_spec.rb
@@ -18,26 +18,12 @@ describe Gitlab::ImportExport::Saver do
FileUtils.rm_rf(export_path)
end
- context 'local archive' do
- it 'saves the repo to disk' do
- stub_feature_flags(import_export_object_storage: false)
+ it 'saves the repo using object storage' do
+ stub_uploads_object_storage(ImportExportUploader)
- subject.save
+ subject.save
- expect(shared.errors).to be_empty
- expect(Dir.empty?(shared.archive_path)).to be false
- end
- end
-
- context 'object storage' do
- it 'saves the repo using object storage' do
- stub_feature_flags(import_export_object_storage: true)
- stub_uploads_object_storage(ImportExportUploader)
-
- subject.save
-
- expect(ImportExportUpload.find_by(project: project).export_file.url)
- .to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*])
- end
+ expect(ImportExportUpload.find_by(project: project).export_file.url)
+ .to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*])
end
end
diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
index 9c3870a0af8..792117e1df1 100644
--- a/spec/lib/gitlab/import_export/uploads_manager_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
@@ -4,6 +4,7 @@ describe Gitlab::ImportExport::UploadsManager do
let(:shared) { project.import_export_shared }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
let(:project) { create(:project) }
+ let(:upload) { create(:upload, :issuable_upload, :object_storage, model: project) }
let(:exported_file_path) { "#{shared.export_path}/uploads/#{upload.secret}/#{File.basename(upload.path)}" }
subject(:manager) { described_class.new(project: project, shared: shared) }
@@ -36,45 +37,53 @@ describe Gitlab::ImportExport::UploadsManager do
expect(File).to exist(exported_file_path)
end
- end
- context 'using object storage' do
- let!(:upload) { create(:upload, :issuable_upload, :object_storage, model: project) }
+ context 'with orphaned project upload files' do
+ let(:orphan_path) { File.join(FileUploader.absolute_base_dir(project), 'f93f088ddf492ffd950cf059002cbbb6', 'orphan.jpg') }
+ let(:exported_orphan_path) { "#{shared.export_path}/uploads/f93f088ddf492ffd950cf059002cbbb6/orphan.jpg" }
- before do
- stub_feature_flags(import_export_object_storage: true)
- stub_uploads_object_storage(FileUploader)
- end
-
- it 'saves the file' do
- fake_uri = double
+ before do
+ FileUtils.mkdir_p(File.dirname(orphan_path))
+ FileUtils.touch(orphan_path)
+ end
- expect(fake_uri).to receive(:open).and_return(StringIO.new('File content'))
- expect(URI).to receive(:parse).and_return(fake_uri)
+ after do
+ File.delete(orphan_path) if File.exist?(orphan_path)
+ end
- manager.save
+ it 'excludes orphaned upload files' do
+ manager.save
- expect(File.read(exported_file_path)).to eq('File content')
+ expect(File).not_to exist(exported_orphan_path)
+ end
end
- end
- describe '#restore' do
- context 'using object storage' do
+ context 'with an upload missing its file' do
before do
- stub_feature_flags(import_export_object_storage: true)
- stub_uploads_object_storage(FileUploader)
-
- FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038'))
- FileUtils.touch(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038', "dummy.txt"))
+ File.delete(upload.absolute_path)
end
- it 'restores the file' do
- manager.restore
+ it 'does not cause errors' do
+ manager.save
- expect(project.uploads.size).to eq(1)
- expect(project.uploads.first.build_uploader.filename).to eq('dummy.txt')
+ expect(shared.errors).to be_empty
end
end
end
end
+
+ describe '#restore' do
+ before do
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038'))
+ FileUtils.touch(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038', "dummy.txt"))
+ end
+
+ it 'restores the file' do
+ manager.restore
+
+ expect(project.uploads.map { |u| u.build_uploader.filename }).to include('dummy.txt')
+ end
+ end
end
diff --git a/spec/lib/gitlab/import_export/uploads_restorer_spec.rb b/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
index acef97459b8..6072f18b8c7 100644
--- a/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::ImportExport::UploadsRestorer do
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/random'))
- FileUtils.touch(File.join(shared.export_path, 'uploads/random', "dummy.txt"))
+ FileUtils.touch(File.join(shared.export_path, 'uploads/random', 'dummy.txt'))
end
after do
@@ -27,9 +27,7 @@ describe Gitlab::ImportExport::UploadsRestorer do
it 'copies the uploads to the project path' do
subject.restore
- uploads = Dir.glob(File.join(subject.uploads_path, '**/*')).map { |file| File.basename(file) }
-
- expect(uploads).to include('dummy.txt')
+ expect(project.uploads.map { |u| u.build_uploader.filename }).to include('dummy.txt')
end
end
@@ -45,9 +43,7 @@ describe Gitlab::ImportExport::UploadsRestorer do
it 'copies the uploads to the project path' do
subject.restore
- uploads = Dir.glob(File.join(subject.uploads_path, '**/*')).map { |file| File.basename(file) }
-
- expect(uploads).to include('dummy.txt')
+ expect(project.uploads.map { |u| u.build_uploader.filename }).to include('dummy.txt')
end
end
end
diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
index c716edd9397..24993460e51 100644
--- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
@@ -7,7 +7,6 @@ describe Gitlab::ImportExport::UploadsSaver do
let(:shared) { project.import_export_shared }
before do
- stub_feature_flags(import_export_object_storage: false)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
diff --git a/spec/lib/gitlab/kubernetes/cluster_role_binding_spec.rb b/spec/lib/gitlab/kubernetes/cluster_role_binding_spec.rb
new file mode 100644
index 00000000000..4a669408025
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/cluster_role_binding_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::ClusterRoleBinding do
+ let(:cluster_role_binding) { described_class.new(name, cluster_role_name, subjects) }
+ let(:name) { 'cluster-role-binding-name' }
+ let(:cluster_role_name) { 'cluster-admin' }
+
+ let(:subjects) { [{ kind: 'ServiceAccount', name: 'sa', namespace: 'ns' }] }
+
+ describe '#generate' do
+ let(:role_ref) do
+ {
+ apiGroup: 'rbac.authorization.k8s.io',
+ kind: 'ClusterRole',
+ name: cluster_role_name
+ }
+ end
+
+ let(:resource) do
+ ::Kubeclient::Resource.new(
+ metadata: { name: name },
+ roleRef: role_ref,
+ subjects: subjects
+ )
+ end
+
+ subject { cluster_role_binding.generate }
+
+ it 'should build a Kubeclient Resource' do
+ is_expected.to eq(resource)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index 341f71a3e49..8bce7a4cdf5 100644
--- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -5,9 +5,18 @@ describe Gitlab::Kubernetes::Helm::Api do
let(:helm) { described_class.new(client) }
let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client) }
- let(:application) { create(:clusters_applications_prometheus) }
-
- let(:command) { application.install_command }
+ let(:application_name) { 'app-name' }
+ let(:rbac) { false }
+ let(:files) { {} }
+
+ let(:command) do
+ Gitlab::Kubernetes::Helm::InstallCommand.new(
+ name: application_name,
+ chart: 'chart-name',
+ rbac: rbac,
+ files: files
+ )
+ end
subject { helm }
@@ -27,7 +36,10 @@ describe Gitlab::Kubernetes::Helm::Api do
describe '#install' do
before do
allow(client).to receive(:create_pod).and_return(nil)
+ allow(client).to receive(:get_config_map).and_return(nil)
allow(client).to receive(:create_config_map).and_return(nil)
+ allow(client).to receive(:create_service_account).and_return(nil)
+ allow(client).to receive(:create_cluster_role_binding).and_return(nil)
allow(namespace).to receive(:ensure_exists!).once
end
@@ -39,13 +51,152 @@ describe Gitlab::Kubernetes::Helm::Api do
end
context 'with a ConfigMap' do
- let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application.name, application.files).generate }
+ let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application_name, files).generate }
it 'creates a ConfigMap on kubeclient' do
expect(client).to receive(:create_config_map).with(resource).once
subject.install(command)
end
+
+ context 'config map already exists' do
+ before do
+ expect(client).to receive(:get_config_map).with("values-content-configuration-#{application_name}", gitlab_namespace).and_return(resource)
+ end
+
+ it 'updates the config map' do
+ expect(client).to receive(:update_config_map).with(resource).once
+
+ subject.install(command)
+ end
+ end
+ end
+
+ context 'without a service account' do
+ it 'does not create a service account on kubeclient' do
+ expect(client).not_to receive(:create_service_account)
+ expect(client).not_to receive(:create_cluster_role_binding)
+
+ subject.install(command)
+ end
+ end
+
+ context 'with a service account' do
+ let(:command) { Gitlab::Kubernetes::Helm::InitCommand.new(name: application_name, files: files, rbac: rbac) }
+
+ context 'rbac-enabled cluster' do
+ let(:rbac) { true }
+
+ let(:service_account_resource) do
+ Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' })
+ end
+
+ let(:cluster_role_binding_resource) do
+ Kubeclient::Resource.new(
+ metadata: { name: 'tiller-admin' },
+ roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' },
+ subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }]
+ )
+ end
+
+ context 'service account and cluster role binding does not exist' do
+ before do
+ expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_raise(Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil))
+ expect(client).to receive(:get_cluster_role_binding).with('tiller-admin').and_raise(Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil))
+ end
+
+ it 'creates a service account, followed the cluster role binding on kubeclient' do
+ expect(client).to receive(:create_service_account).with(service_account_resource).once.ordered
+ expect(client).to receive(:create_cluster_role_binding).with(cluster_role_binding_resource).once.ordered
+
+ subject.install(command)
+ end
+ end
+
+ context 'service account already exists' do
+ before do
+ expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_return(service_account_resource)
+ expect(client).to receive(:get_cluster_role_binding).with('tiller-admin').and_raise(Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil))
+ end
+
+ it 'updates the service account, followed by creating the cluster role binding' do
+ expect(client).to receive(:update_service_account).with(service_account_resource).once.ordered
+ expect(client).to receive(:create_cluster_role_binding).with(cluster_role_binding_resource).once.ordered
+
+ subject.install(command)
+ end
+ end
+
+ context 'service account and cluster role binding already exists' do
+ before do
+ expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_return(service_account_resource)
+ expect(client).to receive(:get_cluster_role_binding).with('tiller-admin').and_return(cluster_role_binding_resource)
+ end
+
+ it 'updates the service account, followed by creating the cluster role binding' do
+ expect(client).to receive(:update_service_account).with(service_account_resource).once.ordered
+ expect(client).to receive(:update_cluster_role_binding).with(cluster_role_binding_resource).once.ordered
+
+ subject.install(command)
+ end
+ end
+
+ context 'a non-404 error is thrown' do
+ before do
+ expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil))
+ end
+
+ it 'raises an error' do
+ expect { subject.install(command) }.to raise_error(Kubeclient::HttpError)
+ end
+ end
+ end
+
+ context 'legacy abac cluster' do
+ it 'does not create a service account on kubeclient' do
+ expect(client).not_to receive(:create_service_account)
+ expect(client).not_to receive(:create_cluster_role_binding)
+
+ subject.install(command)
+ end
+ end
+ end
+ end
+
+ describe '#update' do
+ let(:rbac) { false }
+
+ let(:command) do
+ Gitlab::Kubernetes::Helm::UpgradeCommand.new(
+ application_name,
+ chart: 'chart-name',
+ files: files,
+ rbac: rbac
+ )
+ end
+
+ before do
+ allow(namespace).to receive(:ensure_exists!).once
+
+ allow(client).to receive(:update_config_map).and_return(nil)
+ allow(client).to receive(:create_pod).and_return(nil)
+ end
+
+ it 'ensures the namespace exists before creating the pod' do
+ expect(namespace).to receive(:ensure_exists!).once.ordered
+ expect(client).to receive(:create_pod).once.ordered
+
+ subject.update(command)
+ end
+
+ it 'updates the config map on kubeclient when one exists' do
+ resource = Gitlab::Kubernetes::ConfigMap.new(
+ application_name, files
+ ).generate
+
+ expect(client).to receive(:update_config_map).with(resource).once
+
+ subject.update(command)
end
end
@@ -78,4 +229,25 @@ describe Gitlab::Kubernetes::Helm::Api do
subject.delete_pod!(command.pod_name)
end
end
+
+ describe '#get_config_map' do
+ before do
+ allow(namespace).to receive(:ensure_exists!).once
+ allow(client).to receive(:get_config_map).and_return(nil)
+ end
+
+ it 'ensures the namespace exists before retrieving the config map' do
+ expect(namespace).to receive(:ensure_exists!).once
+
+ subject.get_config_map('example-config-map-name')
+ end
+
+ it 'gets the config map on kubeclient' do
+ expect(client).to receive(:get_config_map)
+ .with('example-config-map-name', namespace.name)
+ .once
+
+ subject.get_config_map('example-config-map-name')
+ end
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
index d50616e95e8..aacae78be43 100644
--- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
@@ -2,14 +2,24 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::BaseCommand do
let(:application) { create(:clusters_applications_helm) }
+ let(:rbac) { false }
+
let(:test_class) do
Class.new do
include Gitlab::Kubernetes::Helm::BaseCommand
+ def initialize(rbac)
+ @rbac = rbac
+ end
+
def name
"test-class-name"
end
+ def rbac?
+ @rbac
+ end
+
def files
{
some: 'value'
@@ -19,7 +29,7 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
end
let(:base_command) do
- test_class.new
+ test_class.new(rbac)
end
subject { base_command }
@@ -34,6 +44,14 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
it 'should returns a kubeclient resoure with pod content for application' do
is_expected.to be_an_instance_of ::Kubeclient::Resource
end
+
+ context 'when rbac is true' do
+ let(:rbac) { true }
+
+ it 'also returns a kubeclient resource' do
+ is_expected.to be_an_instance_of ::Kubeclient::Resource
+ end
+ end
end
describe '#pod_name' do
diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
index dcbc046cf00..4a3b9d4bf6a 100644
--- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
@@ -2,9 +2,135 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InitCommand do
let(:application) { create(:clusters_applications_helm) }
- let(:commands) { 'helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem >/dev/null' }
+ let(:rbac) { false }
+ let(:files) { {} }
+ let(:init_command) { described_class.new(name: application.name, files: files, rbac: rbac) }
- subject { described_class.new(name: application.name, files: {}) }
+ let(:commands) do
+ <<~EOS
+ helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem
+ EOS
+ end
+
+ subject { init_command }
it_behaves_like 'helm commands'
+
+ context 'on a rbac-enabled cluster' do
+ let(:rbac) { true }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem --service-account tiller
+ EOS
+ end
+ end
+ end
+
+ describe '#rbac?' do
+ subject { init_command.rbac? }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#config_map_resource' do
+ let(:metadata) do
+ {
+ name: 'values-content-configuration-helm',
+ namespace: 'gitlab-managed-apps',
+ labels: { name: 'values-content-configuration-helm' }
+ }
+ end
+
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
+
+ subject { init_command.config_map_resource }
+
+ it 'returns a KubeClient resource with config map content for the application' do
+ is_expected.to eq(resource)
+ end
+ end
+
+ describe '#pod_resource' do
+ subject { init_command.pod_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a pod that uses the tiller serviceAccountName' do
+ expect(subject.spec.serviceAccountName).to eq('tiller')
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates a pod that uses the default serviceAccountName' do
+ expect(subject.spec.serviceAcccountName).to be_nil
+ end
+ end
+ end
+
+ describe '#service_account_resource' do
+ let(:resource) do
+ Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' })
+ end
+
+ subject { init_command.service_account_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a Kubeclient resource for the tiller ServiceAccount' do
+ is_expected.to eq(resource)
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates nothing' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#cluster_role_binding_resource' do
+ let(:resource) do
+ Kubeclient::Resource.new(
+ metadata: { name: 'tiller-admin' },
+ roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' },
+ subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }]
+ )
+ end
+
+ subject { init_command.cluster_role_binding_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a Kubeclient resource for the ClusterRoleBinding for tiller' do
+ is_expected.to eq(resource)
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates nothing' do
+ is_expected.to be_nil
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index 982e2f41043..39852b7fe29 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -3,14 +3,21 @@ require 'rails_helper'
describe Gitlab::Kubernetes::Helm::InstallCommand do
let(:files) { { 'ca.pem': 'some file content' } }
let(:repository) { 'https://repository.example.com' }
+ let(:rbac) { false }
let(:version) { '1.2.3' }
+ let(:preinstall) { nil }
+ let(:postinstall) { nil }
let(:install_command) do
described_class.new(
name: 'app-name',
chart: 'chart-name',
+ rbac: rbac,
files: files,
- version: version, repository: repository
+ version: version,
+ repository: repository,
+ preinstall: preinstall,
+ postinstall: postinstall
)
end
@@ -19,21 +26,131 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --client-only >/dev/null
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
helm repo add app-name https://repository.example.com
- helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
+ helm repo update
+ #{helm_install_comand}
+ EOS
+ end
+
+ let(:helm_install_comand) do
+ <<~EOS.squish
+ helm install chart-name
+ --name app-name
+ --tls
+ --tls-ca-cert /data/helm/app-name/config/ca.pem
+ --tls-cert /data/helm/app-name/config/cert.pem
+ --tls-key /data/helm/app-name/config/key.pem
+ --version 1.2.3
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
EOS
end
end
+ context 'when rbac is true' do
+ let(:rbac) { true }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_install_command}
+ EOS
+ end
+
+ let(:helm_install_command) do
+ <<~EOS.squish
+ helm install chart-name
+ --name app-name
+ --tls
+ --tls-ca-cert /data/helm/app-name/config/ca.pem
+ --tls-cert /data/helm/app-name/config/cert.pem
+ --tls-key /data/helm/app-name/config/key.pem
+ --version 1.2.3
+ --set rbac.create\\=true,rbac.enabled\\=true
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+ end
+
context 'when there is no repository' do
let(:repository) { nil }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --client-only >/dev/null
- helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ #{helm_install_command}
+ EOS
+ end
+
+ let(:helm_install_command) do
+ <<~EOS.squish
+ helm install chart-name
+ --name app-name
+ --tls
+ --tls-ca-cert /data/helm/app-name/config/ca.pem
+ --tls-cert /data/helm/app-name/config/cert.pem
+ --tls-key /data/helm/app-name/config/key.pem
+ --version 1.2.3
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ context 'when there is a pre-install script' do
+ let(:preinstall) { ['/bin/date', '/bin/true'] }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_install_command}
+ EOS
+ end
+
+ let(:helm_install_command) do
+ <<~EOS.strip
+ /bin/date
+ /bin/true
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ context 'when there is a post-install script' do
+ let(:postinstall) { ['/bin/date', "/bin/false\n"] }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_install_command}
+ EOS
+ end
+
+ let(:helm_install_command) do
+ <<~EOS.strip
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml
+ /bin/date
+ /bin/false
EOS
end
end
@@ -45,9 +162,21 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --client-only >/dev/null
- helm repo add app-name https://repository.example.com
- helm install chart-name --name app-name --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_install_command}
+ EOS
+ end
+
+ let(:helm_install_command) do
+ <<~EOS.squish
+ helm install chart-name
+ --name app-name
+ --version 1.2.3
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
EOS
end
end
@@ -59,11 +188,62 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --client-only >/dev/null
- helm repo add app-name https://repository.example.com
- helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_install_command}
EOS
end
+
+ let(:helm_install_command) do
+ <<~EOS.squish
+ helm install chart-name
+ --name app-name
+ --tls
+ --tls-ca-cert /data/helm/app-name/config/ca.pem
+ --tls-cert /data/helm/app-name/config/cert.pem
+ --tls-key /data/helm/app-name/config/key.pem
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ describe '#rbac?' do
+ subject { install_command.rbac? }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#pod_resource' do
+ subject { install_command.pod_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a pod that uses the tiller serviceAccountName' do
+ expect(subject.spec.serviceAccountName).to eq('tiller')
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates a pod that uses the default serviceAccountName' do
+ expect(subject.spec.serviceAcccountName).to be_nil
+ end
end
end
@@ -84,4 +264,20 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
is_expected.to eq(resource)
end
end
+
+ describe '#service_account_resource' do
+ subject { install_command.service_account_resource }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
+
+ describe '#cluster_role_binding_resource' do
+ subject { install_command.cluster_role_binding_resource }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
index ec64193c0b2..2dd3a570a1d 100644
--- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -5,8 +5,9 @@ describe Gitlab::Kubernetes::Helm::Pod do
let(:app) { create(:clusters_applications_prometheus) }
let(:command) { app.install_command }
let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
+ let(:service_account_name) { nil }
- subject { described_class.new(command, namespace) }
+ subject { described_class.new(command, namespace, service_account_name: service_account_name) }
context 'with a command' do
it 'should generate a Kubeclient::Resource' do
@@ -29,7 +30,7 @@ describe Gitlab::Kubernetes::Helm::Pod do
it 'should generate the appropriate specifications for the container' do
container = subject.generate.spec.containers.first
expect(container.name).to eq('helm')
- expect(container.image).to eq('alpine:3.6')
+ expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.11.0-kube-1.11.0')
expect(container.env.count).to eq(3)
expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT])
expect(container.command).to match_array(["/bin/sh"])
@@ -58,6 +59,20 @@ describe Gitlab::Kubernetes::Helm::Pod do
expect(volume.configMap['items'].first['key']).to eq(:'values.yaml')
expect(volume.configMap['items'].first['path']).to eq(:'values.yaml')
end
+
+ it 'should have no serviceAccountName' do
+ spec = subject.generate.spec
+ expect(spec.serviceAccountName).to be_nil
+ end
+
+ context 'with a service_account_name' do
+ let(:service_account_name) { 'sa' }
+
+ it 'should use the serviceAccountName provided' do
+ spec = subject.generate.spec
+ expect(spec.serviceAccountName).to eq(service_account_name)
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/upgrade_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/upgrade_command_spec.rb
new file mode 100644
index 00000000000..9b201dae417
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/helm/upgrade_command_spec.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Gitlab::Kubernetes::Helm::UpgradeCommand do
+ let(:application) { build(:clusters_applications_prometheus) }
+ let(:files) { { 'ca.pem': 'some file content' } }
+ let(:namespace) { ::Gitlab::Kubernetes::Helm::NAMESPACE }
+ let(:rbac) { false }
+ let(:upgrade_command) do
+ described_class.new(
+ application.name,
+ chart: application.chart,
+ files: files,
+ rbac: rbac
+ )
+ end
+
+ subject { upgrade_command }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm upgrade #{application.name} #{application.chart} --tls --tls-ca-cert /data/helm/#{application.name}/config/ca.pem --tls-cert /data/helm/#{application.name}/config/cert.pem --tls-key /data/helm/#{application.name}/config/key.pem --reset-values --install --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml
+ EOS
+ end
+ end
+
+ context 'rbac is true' do
+ let(:rbac) { true }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm upgrade #{application.name} #{application.chart} --tls --tls-ca-cert /data/helm/#{application.name}/config/ca.pem --tls-cert /data/helm/#{application.name}/config/cert.pem --tls-key /data/helm/#{application.name}/config/key.pem --reset-values --install --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ context 'with an application with a repository' do
+ let(:ci_runner) { create(:ci_runner) }
+ let(:application) { build(:clusters_applications_runner, runner: ci_runner) }
+ let(:upgrade_command) do
+ described_class.new(
+ application.name,
+ chart: application.chart,
+ files: files,
+ rbac: rbac,
+ repository: application.repository
+ )
+ end
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm repo add #{application.name} #{application.repository}
+ helm upgrade #{application.name} #{application.chart} --tls --tls-ca-cert /data/helm/#{application.name}/config/ca.pem --tls-cert /data/helm/#{application.name}/config/cert.pem --tls-key /data/helm/#{application.name}/config/key.pem --reset-values --install --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ context 'when there is no ca.pem file' do
+ let(:files) { { 'file.txt': 'some content' } }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
+ helm upgrade #{application.name} #{application.chart} --reset-values --install --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ describe '#pod_resource' do
+ subject { upgrade_command.pod_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a pod that uses the tiller serviceAccountName' do
+ expect(subject.spec.serviceAccountName).to eq('tiller')
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates a pod that uses the default serviceAccountName' do
+ expect(subject.spec.serviceAcccountName).to be_nil
+ end
+ end
+ end
+
+ describe '#config_map_resource' do
+ let(:metadata) do
+ {
+ name: "values-content-configuration-#{application.name}",
+ namespace: namespace,
+ labels: { name: "values-content-configuration-#{application.name}" }
+ }
+ end
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
+
+ it 'returns a KubeClient resource with config map content for the application' do
+ expect(subject.config_map_resource).to eq(resource)
+ end
+ end
+
+ describe '#rbac?' do
+ subject { upgrade_command.rbac? }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#pod_name' do
+ it 'returns the pod name' do
+ expect(subject.pod_name).to eq("upgrade-#{application.name}")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/kube_client_spec.rb b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
new file mode 100644
index 00000000000..3979a43216c
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::KubeClient do
+ include KubernetesHelpers
+
+ let(:api_url) { 'https://kubernetes.example.com/prefix' }
+ let(:kubeclient_options) { { auth_options: { bearer_token: 'xyz' } } }
+
+ let(:client) { described_class.new(api_url, kubeclient_options) }
+
+ before do
+ stub_kubeclient_discover(api_url)
+ end
+
+ shared_examples 'a Kubeclient' do
+ it 'is a Kubeclient::Client' do
+ is_expected.to be_an_instance_of Kubeclient::Client
+ end
+
+ it 'has the kubeclient options' do
+ expect(subject.auth_options).to eq({ bearer_token: 'xyz' })
+ end
+ end
+
+ describe '#core_client' do
+ subject { client.core_client }
+
+ it_behaves_like 'a Kubeclient'
+
+ it 'has the core API endpoint' do
+ expect(subject.api_endpoint.to_s).to match(%r{\/api\Z})
+ end
+
+ it 'has the api_version' do
+ expect(subject.instance_variable_get(:@api_version)).to eq('v1')
+ end
+ end
+
+ describe '#rbac_client' do
+ subject { client.rbac_client }
+
+ it_behaves_like 'a Kubeclient'
+
+ it 'has the RBAC API group endpoint' do
+ expect(subject.api_endpoint.to_s).to match(%r{\/apis\/rbac.authorization.k8s.io\Z})
+ end
+
+ it 'has the api_version' do
+ expect(subject.instance_variable_get(:@api_version)).to eq('v1')
+ end
+ end
+
+ describe '#extensions_client' do
+ subject { client.extensions_client }
+
+ it_behaves_like 'a Kubeclient'
+
+ it 'has the extensions API group endpoint' do
+ expect(subject.api_endpoint.to_s).to match(%r{\/apis\/extensions\Z})
+ end
+
+ it 'has the api_version' do
+ expect(subject.instance_variable_get(:@api_version)).to eq('v1beta1')
+ end
+ end
+
+ describe '#knative_client' do
+ subject { client.knative_client }
+
+ it_behaves_like 'a Kubeclient'
+
+ it 'has the extensions API group endpoint' do
+ expect(subject.api_endpoint.to_s).to match(%r{\/apis\/serving.knative.dev\Z})
+ end
+
+ it 'has the api_version' do
+ expect(subject.instance_variable_get(:@api_version)).to eq('v1alpha1')
+ end
+ end
+
+ describe 'core API' do
+ let(:core_client) { client.core_client }
+
+ [
+ :get_pods,
+ :get_secrets,
+ :get_config_map,
+ :get_pod,
+ :get_namespace,
+ :get_secret,
+ :get_service,
+ :get_service_account,
+ :delete_pod,
+ :create_config_map,
+ :create_namespace,
+ :create_pod,
+ :create_secret,
+ :create_service_account,
+ :update_config_map,
+ :update_service_account
+ ].each do |method|
+ describe "##{method}" do
+ it 'delegates to the core client' do
+ expect(client).to delegate_method(method).to(:core_client)
+ end
+
+ it 'responds to the method' do
+ expect(client).to respond_to method
+ end
+ end
+ end
+ end
+
+ describe 'rbac API group' do
+ let(:rbac_client) { client.rbac_client }
+
+ [
+ :create_cluster_role_binding,
+ :get_cluster_role_binding,
+ :update_cluster_role_binding
+ ].each do |method|
+ describe "##{method}" do
+ it 'delegates to the rbac client' do
+ expect(client).to delegate_method(method).to(:rbac_client)
+ end
+
+ it 'responds to the method' do
+ expect(client).to respond_to method
+ end
+ end
+ end
+ end
+
+ describe 'extensions API group' do
+ let(:api_groups) { ['apis/extensions'] }
+ let(:extensions_client) { client.extensions_client }
+
+ describe '#get_deployments' do
+ it 'delegates to the extensions client' do
+ expect(client).to delegate_method(:get_deployments).to(:extensions_client)
+ end
+
+ it 'responds to the method' do
+ expect(client).to respond_to :get_deployments
+ end
+ end
+ end
+
+ describe 'non-entity methods' do
+ it 'does not proxy for non-entity methods' do
+ expect(client).not_to respond_to :proxy_url
+ end
+
+ it 'throws an error' do
+ expect { client.proxy_url }.to raise_error(NoMethodError)
+ end
+ end
+
+ describe '#get_pod_log' do
+ let(:core_client) { client.core_client }
+
+ it 'is delegated to the core client' do
+ expect(client).to delegate_method(:get_pod_log).to(:core_client)
+ end
+ end
+
+ describe '#watch_pod_log' do
+ let(:core_client) { client.core_client }
+
+ it 'is delegated to the core client' do
+ expect(client).to delegate_method(:watch_pod_log).to(:core_client)
+ end
+ end
+
+ describe 'methods that do not exist on any client' do
+ it 'throws an error' do
+ expect { client.non_existent_method }.to raise_error(NoMethodError)
+ end
+
+ it 'returns false for respond_to' do
+ expect(client.respond_to?(:non_existent_method)).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/namespace_spec.rb b/spec/lib/gitlab/kubernetes/namespace_spec.rb
index e098612f6fb..e1c35c355f4 100644
--- a/spec/lib/gitlab/kubernetes/namespace_spec.rb
+++ b/spec/lib/gitlab/kubernetes/namespace_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::Kubernetes::Namespace do
describe '#exists?' do
context 'when namespace do not exits' do
- let(:exception) { ::Kubeclient::HttpError.new(404, "namespace #{name} not found", nil) }
+ let(:exception) { ::Kubeclient::ResourceNotFoundError.new(404, "namespace #{name} not found", nil) }
it 'returns false' do
expect(client).to receive(:get_namespace).with(name).once.and_raise(exception)
diff --git a/spec/lib/gitlab/kubernetes/role_binding_spec.rb b/spec/lib/gitlab/kubernetes/role_binding_spec.rb
new file mode 100644
index 00000000000..a1a59533bfb
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/role_binding_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::RoleBinding, '#generate' do
+ let(:role_name) { 'edit' }
+ let(:namespace) { 'my-namespace' }
+ let(:service_account_name) { 'my-service-account' }
+
+ let(:subjects) do
+ [
+ {
+ kind: 'ServiceAccount',
+ name: service_account_name,
+ namespace: namespace
+ }
+ ]
+ end
+
+ let(:role_ref) do
+ {
+ apiGroup: 'rbac.authorization.k8s.io',
+ kind: 'ClusterRole',
+ name: role_name
+ }
+ end
+
+ let(:resource) do
+ ::Kubeclient::Resource.new(
+ metadata: { name: "gitlab-#{namespace}", namespace: namespace },
+ roleRef: role_ref,
+ subjects: subjects
+ )
+ end
+
+ subject do
+ described_class.new(
+ name: "gitlab-#{namespace}",
+ role_name: role_name,
+ namespace: namespace,
+ service_account_name: service_account_name
+ ).generate
+ end
+
+ it 'should build a Kubeclient Resource' do
+ is_expected.to eq(resource)
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/service_account_spec.rb b/spec/lib/gitlab/kubernetes/service_account_spec.rb
new file mode 100644
index 00000000000..8da9e932dc3
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/service_account_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::ServiceAccount do
+ let(:name) { 'a_service_account' }
+ let(:namespace_name) { 'a_namespace' }
+ let(:service_account) { described_class.new(name, namespace_name) }
+
+ it { expect(service_account.name).to eq(name) }
+ it { expect(service_account.namespace_name).to eq(namespace_name) }
+
+ describe '#generate' do
+ let(:resource) do
+ ::Kubeclient::Resource.new(metadata: { name: name, namespace: namespace_name })
+ end
+
+ subject { service_account.generate }
+
+ it 'should build a Kubeclient Resource' do
+ is_expected.to eq(resource)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/service_account_token_spec.rb b/spec/lib/gitlab/kubernetes/service_account_token_spec.rb
new file mode 100644
index 00000000000..0773d3d9aec
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/service_account_token_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::ServiceAccountToken do
+ let(:name) { 'token-name' }
+ let(:service_account_name) { 'a_service_account' }
+ let(:namespace_name) { 'a_namespace' }
+ let(:service_account_token) { described_class.new(name, service_account_name, namespace_name) }
+
+ it { expect(service_account_token.name).to eq(name) }
+ it { expect(service_account_token.service_account_name).to eq(service_account_name) }
+ it { expect(service_account_token.namespace_name).to eq(namespace_name) }
+
+ describe '#generate' do
+ let(:resource) do
+ ::Kubeclient::Resource.new(
+ metadata: {
+ name: name,
+ namespace: namespace_name,
+ annotations: {
+ 'kubernetes.io/service-account.name': service_account_name
+ }
+ },
+ type: 'kubernetes.io/service-account-token'
+ )
+ end
+
+ subject { service_account_token.generate }
+
+ it 'should build a Kubeclient Resource' do
+ is_expected.to eq(resource)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/language_data_spec.rb b/spec/lib/gitlab/language_data_spec.rb
new file mode 100644
index 00000000000..b08150855fe
--- /dev/null
+++ b/spec/lib/gitlab/language_data_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::LanguageData do
+ describe '#extensions' do
+ before do
+ described_class.clear_extensions!
+ end
+
+ it 'loads the extensions once' do
+ expect(YAML).to receive(:load_file).once.and_call_original
+
+ 2.times do
+ expect(described_class.extensions).to be_a(Set)
+ expect(described_class.extensions.count).to be > 0
+ # Sanity check for known extensions
+ expect(described_class.extensions).to include(*%w(.rb .yml .json))
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index 4e7bd433a9c..ee6d6fc961f 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -42,6 +42,65 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do
subscriber.sql(event)
end
+
+ context 'events are internal to Rails or irrelevant' do
+ let(:schema_event) do
+ double(
+ :event,
+ name: 'sql.active_record',
+ payload: {
+ sql: "SELECT attr.attname FROM pg_attribute attr INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey) WHERE cons.contype = 'p' AND cons.conrelid = '\"projects\"'::regclass",
+ name: 'SCHEMA',
+ connection_id: 135,
+ statement_name: nil,
+ binds: []
+ },
+ duration: 0.7
+ )
+ end
+
+ let(:begin_event) do
+ double(
+ :event,
+ name: 'sql.active_record',
+ payload: {
+ sql: "BEGIN",
+ name: nil,
+ connection_id: 231,
+ statement_name: nil,
+ binds: []
+ },
+ duration: 1.1
+ )
+ end
+
+ let(:commit_event) do
+ double(
+ :event,
+ name: 'sql.active_record',
+ payload: {
+ sql: "COMMIT",
+ name: nil,
+ connection_id: 212,
+ statement_name: nil,
+ binds: []
+ },
+ duration: 1.6
+ )
+ end
+
+ it 'skips schema/begin/commit sql commands' do
+ expect(subscriber).to receive(:current_transaction)
+ .at_least(:once)
+ .and_return(transaction)
+
+ expect(transaction).not_to receive(:increment)
+
+ subscriber.sql(schema_event)
+ subscriber.sql(begin_event)
+ subscriber.sql(commit_event)
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb
index f788f8ee276..daf454665b0 100644
--- a/spec/lib/gitlab/middleware/multipart_spec.rb
+++ b/spec/lib/gitlab/middleware/multipart_spec.rb
@@ -75,6 +75,26 @@ describe Gitlab::Middleware::Multipart do
it_behaves_like 'multipart upload files'
end
+ it 'allows files in uploads/tmp directory' do
+ Dir.mktmpdir do |dir|
+ uploads_dir = File.join(dir, 'public/uploads/tmp')
+ FileUtils.mkdir_p(uploads_dir)
+
+ allow(Rails).to receive(:root).and_return(dir)
+ allow(Dir).to receive(:tmpdir).and_return(File.join(Dir.tmpdir, 'tmpsubdir'))
+
+ Tempfile.open('top-level', uploads_dir) do |tempfile|
+ env = post_env({ 'file' => tempfile.path }, { 'file.name' => original_filename, 'file.path' => tempfile.path }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect(app).to receive(:call) do |env|
+ expect(Rack::Request.new(env).params['file']).to be_a(::UploadedFile)
+ end
+
+ middleware.call(env)
+ end
+ end
+ end
+
it 'allows symlinks for uploads dir' do
Tempfile.open('two-levels') do |tempfile|
symlinked_dir = '/some/dir/uploads'
diff --git a/spec/lib/gitlab/middleware/read_only_spec.rb b/spec/lib/gitlab/middleware/read_only_spec.rb
index 8fbeaa065fa..ac3bc6b2dfe 100644
--- a/spec/lib/gitlab/middleware/read_only_spec.rb
+++ b/spec/lib/gitlab/middleware/read_only_spec.rb
@@ -34,7 +34,7 @@ describe Gitlab::Middleware::ReadOnly do
end
end
- context 'normal requests to a read-only Gitlab instance' do
+ context 'normal requests to a read-only GitLab instance' do
let(:fake_app) { lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['OK']] } }
before do
diff --git a/spec/lib/gitlab/null_request_store_spec.rb b/spec/lib/gitlab/null_request_store_spec.rb
new file mode 100644
index 00000000000..c023dac53ad
--- /dev/null
+++ b/spec/lib/gitlab/null_request_store_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::NullRequestStore do
+ let(:null_store) { described_class.new }
+
+ describe '#store' do
+ it 'returns an empty hash' do
+ expect(null_store.store).to eq({})
+ end
+ end
+
+ describe '#active?' do
+ it 'returns falsey' do
+ expect(null_store.active?).to be_falsey
+ end
+ end
+
+ describe '#read' do
+ it 'returns nil' do
+ expect(null_store.read('foo')).to be nil
+ end
+ end
+
+ describe '#[]' do
+ it 'returns nil' do
+ expect(null_store['foo']).to be nil
+ end
+ end
+
+ describe '#write' do
+ it 'returns the same value' do
+ expect(null_store.write('key', 'value')).to eq('value')
+ end
+ end
+
+ describe '#[]=' do
+ it 'returns the same value' do
+ expect(null_store['key'] = 'value').to eq('value')
+ end
+ end
+
+ describe '#exist?' do
+ it 'returns falsey' do
+ expect(null_store.exist?('foo')).to be_falsey
+ end
+ end
+
+ describe '#fetch' do
+ it 'returns the block result' do
+ expect(null_store.fetch('key') { 'block result' }).to eq('block result')
+ end
+ end
+
+ describe '#delete' do
+ context 'when a block is given' do
+ it 'yields the key to the block' do
+ expect do |b|
+ null_store.delete('foo', &b)
+ end.to yield_with_args('foo')
+ end
+
+ it 'returns the block result' do
+ expect(null_store.delete('foo') { |key| 'block result' }).to eq('block result')
+ end
+ end
+
+ context 'when a block is not given' do
+ it 'returns nil' do
+ expect(null_store.delete('foo')).to be nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/patch/draw_route_spec.rb b/spec/lib/gitlab/patch/draw_route_spec.rb
new file mode 100644
index 00000000000..4009b903dc3
--- /dev/null
+++ b/spec/lib/gitlab/patch/draw_route_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+describe Gitlab::Patch::DrawRoute do
+ subject do
+ Class.new do
+ include Gitlab::Patch::DrawRoute
+
+ def route_path(route_name)
+ File.expand_path("../../../../#{route_name}", __dir__)
+ end
+ end.new
+ end
+
+ before do
+ allow(subject).to receive(:instance_eval)
+ end
+
+ it 'evaluates CE only route' do
+ subject.draw(:help)
+
+ expect(subject).to have_received(:instance_eval)
+ .with(File.read(subject.route_path('config/routes/help.rb')))
+ .once
+
+ expect(subject).to have_received(:instance_eval)
+ .once
+ end
+end
diff --git a/spec/lib/gitlab/patch/prependable_spec.rb b/spec/lib/gitlab/patch/prependable_spec.rb
new file mode 100644
index 00000000000..725d733d176
--- /dev/null
+++ b/spec/lib/gitlab/patch/prependable_spec.rb
@@ -0,0 +1,234 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+# Patching ActiveSupport::Concern
+require_relative '../../../../config/initializers/0_as_concern'
+
+describe Gitlab::Patch::Prependable do
+ before do
+ @prepended_modules = []
+ end
+
+ let(:ee) do
+ # So that block in Module.new could see them
+ prepended_modules = @prepended_modules
+
+ Module.new do
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def class_name
+ super.tr('C', 'E')
+ end
+ end
+
+ this = self
+ prepended do
+ prepended_modules << [self, this]
+ end
+
+ def name
+ super.tr('c', 'e')
+ end
+ end
+ end
+
+ let(:ce) do
+ # So that block in Module.new could see them
+ prepended_modules = @prepended_modules
+ ee_ = ee
+
+ Module.new do
+ extend ActiveSupport::Concern
+ prepend ee_
+
+ class_methods do
+ def class_name
+ 'CE'
+ end
+ end
+
+ this = self
+ prepended do
+ prepended_modules << [self, this]
+ end
+
+ def name
+ 'ce'
+ end
+ end
+ end
+
+ describe 'a class including a concern prepending a concern' do
+ subject { Class.new.include(ce) }
+
+ it 'returns values from prepended module ee' do
+ expect(subject.new.name).to eq('ee')
+ expect(subject.class_name).to eq('EE')
+ end
+
+ it 'has the expected ancestors' do
+ expect(subject.ancestors.take(3)).to eq([subject, ee, ce])
+ expect(subject.singleton_class.ancestors.take(3))
+ .to eq([subject.singleton_class,
+ ee.const_get(:ClassMethods),
+ ce.const_get(:ClassMethods)])
+ end
+
+ it 'prepends only once even if called twice' do
+ 2.times { ce.prepend(ee) }
+
+ subject
+
+ expect(@prepended_modules).to eq([[ce, ee]])
+ end
+
+ context 'overriding methods' do
+ before do
+ subject.module_eval do
+ def self.class_name
+ 'Custom'
+ end
+
+ def name
+ 'custom'
+ end
+ end
+ end
+
+ it 'returns values from the class' do
+ expect(subject.new.name).to eq('custom')
+ expect(subject.class_name).to eq('Custom')
+ end
+ end
+ end
+
+ describe 'a class prepending a concern prepending a concern' do
+ subject { Class.new.prepend(ce) }
+
+ it 'returns values from prepended module ee' do
+ expect(subject.new.name).to eq('ee')
+ expect(subject.class_name).to eq('EE')
+ end
+
+ it 'has the expected ancestors' do
+ expect(subject.ancestors.take(3)).to eq([ee, ce, subject])
+ expect(subject.singleton_class.ancestors.take(3))
+ .to eq([ee.const_get(:ClassMethods),
+ ce.const_get(:ClassMethods),
+ subject.singleton_class])
+ end
+
+ it 'prepends only once' do
+ subject.prepend(ce)
+
+ expect(@prepended_modules).to eq([[ce, ee], [subject, ce]])
+ end
+ end
+
+ describe 'a class prepending a concern' do
+ subject do
+ ee_ = ee
+
+ Class.new do
+ prepend ee_
+
+ def self.class_name
+ 'CE'
+ end
+
+ def name
+ 'ce'
+ end
+ end
+ end
+
+ it 'returns values from prepended module ee' do
+ expect(subject.new.name).to eq('ee')
+ expect(subject.class_name).to eq('EE')
+ end
+
+ it 'has the expected ancestors' do
+ expect(subject.ancestors.take(2)).to eq([ee, subject])
+ expect(subject.singleton_class.ancestors.take(2))
+ .to eq([ee.const_get(:ClassMethods),
+ subject.singleton_class])
+ end
+
+ it 'prepends only once' do
+ subject.prepend(ee)
+
+ expect(@prepended_modules).to eq([[subject, ee]])
+ end
+ end
+
+ describe 'simple case' do
+ subject do
+ foo_ = foo
+
+ Class.new do
+ prepend foo_
+
+ def value
+ 10
+ end
+ end
+ end
+
+ let(:foo) do
+ Module.new do
+ extend ActiveSupport::Concern
+
+ prepended do
+ def self.class_value
+ 20
+ end
+ end
+
+ def value
+ super * 10
+ end
+ end
+ end
+
+ context 'class methods' do
+ it "has a method" do
+ expect(subject).to respond_to(:class_value)
+ end
+
+ it 'can execute a method' do
+ expect(subject.class_value).to eq(20)
+ end
+ end
+
+ context 'instance methods' do
+ it "has a method" do
+ expect(subject.new).to respond_to(:value)
+ end
+
+ it 'chains a method execution' do
+ expect(subject.new.value).to eq(100)
+ end
+ end
+ end
+
+ context 'having two prepended blocks' do
+ subject do
+ Module.new do
+ extend ActiveSupport::Concern
+
+ prepended do
+ end
+
+ prepended do
+ end
+ end
+ end
+
+ it "raises an error" do
+ expect { subject }
+ .to raise_error(described_class::MultiplePrependedBlocks)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/private_commit_email_spec.rb b/spec/lib/gitlab/private_commit_email_spec.rb
new file mode 100644
index 00000000000..10bf624bbdd
--- /dev/null
+++ b/spec/lib/gitlab/private_commit_email_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::PrivateCommitEmail do
+ let(:hostname) { Gitlab::CurrentSettings.current_application_settings.commit_email_hostname }
+ let(:id) { 1 }
+ let(:valid_email) { "#{id}-foo@#{hostname}" }
+ let(:invalid_email) { "#{id}-foo@users.noreply.bar.com" }
+
+ context '.regex' do
+ subject { described_class.regex }
+
+ it { is_expected.to match("1-foo@#{hostname}") }
+ it { is_expected.not_to match("1-foo@#{hostname}.foo") }
+ it { is_expected.not_to match('1-foo@users.noreply.gitlab.com') }
+ it { is_expected.not_to match('foo-1@users.noreply.gitlab.com') }
+ it { is_expected.not_to match('foobar@gitlab.com') }
+ end
+
+ context '.user_id_for_email' do
+ it 'parses user id from email' do
+ expect(described_class.user_id_for_email(valid_email)).to eq(id)
+ end
+
+ it 'returns nil on invalid commit email' do
+ expect(described_class.user_id_for_email(invalid_email)).to be_nil
+ end
+ end
+
+ context '.user_ids_for_email' do
+ it 'returns deduplicated user IDs for each valid email' do
+ result = described_class.user_ids_for_emails([valid_email, valid_email, invalid_email])
+
+ expect(result).to eq([id])
+ end
+
+ it 'returns an empty array with no valid emails' do
+ result = described_class.user_ids_for_emails([invalid_email])
+ expect(result).to eq([])
+ end
+ end
+
+ context '.for_user' do
+ it 'returns email in the format id-username@hostname' do
+ user = create(:user)
+
+ expect(described_class.for_user(user)).to eq("#{user.id}-#{user.username}@#{hostname}")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb b/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb
index 5589db92b1d..1a108003bc2 100644
--- a/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb
+++ b/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::Prometheus::AdditionalMetricsParser do
let(:parser_error_class) { Gitlab::Prometheus::ParsingError }
describe '#load_groups_from_yaml' do
- subject { described_class.load_groups_from_yaml }
+ subject { described_class.load_groups_from_yaml('dummy.yaml') }
describe 'parsing sample yaml' do
let(:sample_yaml) do
diff --git a/spec/lib/gitlab/prometheus/metric_group_spec.rb b/spec/lib/gitlab/prometheus/metric_group_spec.rb
new file mode 100644
index 00000000000..e7d16e73663
--- /dev/null
+++ b/spec/lib/gitlab/prometheus/metric_group_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Gitlab::Prometheus::MetricGroup do
+ describe '.common_metrics' do
+ let!(:project_metric) { create(:prometheus_metric) }
+ let!(:common_metric_group_a) { create(:prometheus_metric, :common, group: :aws_elb) }
+ let!(:common_metric_group_b_q1) { create(:prometheus_metric, :common, group: :kubernetes) }
+ let!(:common_metric_group_b_q2) { create(:prometheus_metric, :common, group: :kubernetes) }
+
+ subject { described_class.common_metrics }
+
+ it 'returns exactly two groups' do
+ expect(subject.map(&:name)).to contain_exactly(
+ 'Response metrics (AWS ELB)', 'System metrics (Kubernetes)')
+ end
+
+ it 'returns exactly three metric queries' do
+ expect(subject.map(&:metrics).flatten.map(&:id)).to contain_exactly(
+ common_metric_group_a.id, common_metric_group_b_q1.id,
+ common_metric_group_b_q2.id)
+ end
+ end
+
+ describe '.for_project' do
+ let!(:other_project) { create(:project) }
+ let!(:project_metric) { create(:prometheus_metric) }
+ let!(:common_metric) { create(:prometheus_metric, :common, group: :aws_elb) }
+
+ subject do
+ described_class.for_project(other_project)
+ .map(&:metrics).flatten
+ .map(&:id)
+ end
+
+ it 'returns exactly one common metric' do
+ is_expected.to contain_exactly(common_metric.id)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/quick_actions/command_definition_spec.rb b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
index b03c1e23ca3..5dae82a63b4 100644
--- a/spec/lib/gitlab/quick_actions/command_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
@@ -210,6 +210,19 @@ describe Gitlab::QuickActions::CommandDefinition do
end
end
+ context 'when warning is set' do
+ before do
+ subject.explanation = 'Explanation'
+ subject.warning = 'dangerous!'
+ end
+
+ it 'returns this static string' do
+ result = subject.explain({}, nil)
+
+ expect(result).to eq 'Explanation (dangerous!)'
+ end
+ end
+
context 'when the explanation is dynamic' do
before do
subject.explanation = proc { |arg| "Dynamic #{arg}" }
diff --git a/spec/lib/gitlab/quick_actions/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
index 067a30fd7e2..fd4df8694ba 100644
--- a/spec/lib/gitlab/quick_actions/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -12,6 +12,7 @@ describe Gitlab::QuickActions::Dsl do
params 'The first argument'
explanation 'Static explanation'
+ warning 'Possible problem!'
command :explanation_with_aliases, :once, :first do |arg|
arg
end
@@ -64,6 +65,7 @@ describe Gitlab::QuickActions::Dsl do
expect(no_args_def.condition_block).to be_nil
expect(no_args_def.action_block).to be_a_kind_of(Proc)
expect(no_args_def.parse_params_block).to be_nil
+ expect(no_args_def.warning).to eq('')
expect(explanation_with_aliases_def.name).to eq(:explanation_with_aliases)
expect(explanation_with_aliases_def.aliases).to eq([:once, :first])
@@ -73,6 +75,7 @@ describe Gitlab::QuickActions::Dsl do
expect(explanation_with_aliases_def.condition_block).to be_nil
expect(explanation_with_aliases_def.action_block).to be_a_kind_of(Proc)
expect(explanation_with_aliases_def.parse_params_block).to be_nil
+ expect(explanation_with_aliases_def.warning).to eq('Possible problem!')
expect(dynamic_description_def.name).to eq(:dynamic_description)
expect(dynamic_description_def.aliases).to eq([])
@@ -82,6 +85,7 @@ describe Gitlab::QuickActions::Dsl do
expect(dynamic_description_def.condition_block).to be_nil
expect(dynamic_description_def.action_block).to be_a_kind_of(Proc)
expect(dynamic_description_def.parse_params_block).to be_nil
+ expect(dynamic_description_def.warning).to eq('')
expect(cc_def.name).to eq(:cc)
expect(cc_def.aliases).to eq([])
@@ -91,6 +95,7 @@ describe Gitlab::QuickActions::Dsl do
expect(cc_def.condition_block).to be_nil
expect(cc_def.action_block).to be_nil
expect(cc_def.parse_params_block).to be_nil
+ expect(cc_def.warning).to eq('')
expect(cond_action_def.name).to eq(:cond_action)
expect(cond_action_def.aliases).to eq([])
@@ -100,6 +105,7 @@ describe Gitlab::QuickActions::Dsl do
expect(cond_action_def.condition_block).to be_a_kind_of(Proc)
expect(cond_action_def.action_block).to be_a_kind_of(Proc)
expect(cond_action_def.parse_params_block).to be_nil
+ expect(cond_action_def.warning).to eq('')
expect(with_params_parsing_def.name).to eq(:with_params_parsing)
expect(with_params_parsing_def.aliases).to eq([])
@@ -109,6 +115,7 @@ describe Gitlab::QuickActions::Dsl do
expect(with_params_parsing_def.condition_block).to be_nil
expect(with_params_parsing_def.action_block).to be_a_kind_of(Proc)
expect(with_params_parsing_def.parse_params_block).to be_a_kind_of(Proc)
+ expect(with_params_parsing_def.warning).to eq('')
expect(substitution_def.name).to eq(:something)
expect(substitution_def.aliases).to eq([])
@@ -118,6 +125,7 @@ describe Gitlab::QuickActions::Dsl do
expect(substitution_def.condition_block).to be_nil
expect(substitution_def.action_block.call('text')).to eq('text Some complicated thing you want in here')
expect(substitution_def.parse_params_block).to be_nil
+ expect(substitution_def.warning).to eq('')
end
end
end
diff --git a/spec/lib/gitlab/quick_actions/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb
index 0166f6c2ee0..873bb359d6e 100644
--- a/spec/lib/gitlab/quick_actions/extractor_spec.rb
+++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb
@@ -272,5 +272,24 @@ describe Gitlab::QuickActions::Extractor do
expect(commands).to be_empty
expect(msg).to eq expected
end
+
+ it 'limits to passed commands when they are passed' do
+ msg = <<~MSG.strip
+ Hello, we should only extract the commands passed
+ /reopen
+ /labels hello world
+ /power
+ MSG
+ expected_msg = <<~EXPECTED.strip
+ Hello, we should only extract the commands passed
+ /power
+ EXPECTED
+ expected_commands = [['reopen'], ['labels', 'hello world']]
+
+ msg, commands = extractor.extract_commands(msg, only: [:open, :labels])
+
+ expect(commands).to eq(expected_commands)
+ expect(msg).to eq expected_msg
+ end
end
end
diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb
index 5bd4d6c6a48..0295138fc3a 100644
--- a/spec/lib/gitlab/repository_cache_adapter_spec.rb
+++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb
@@ -65,6 +65,144 @@ describe Gitlab::RepositoryCacheAdapter do
end
end
+ describe '#cache_method_output_asymmetrically', :use_clean_rails_memory_store_caching, :request_store do
+ let(:request_store_cache) { repository.send(:request_store_cache) }
+
+ context 'with a non-existing repository' do
+ let(:project) { create(:project) } # No repository
+ let(:object) { double }
+
+ subject do
+ repository.cache_method_output_asymmetrically(:cats) do
+ object.cats_call_stub
+ end
+ end
+
+ it 'returns the output of the original method' do
+ expect(object).to receive(:cats_call_stub).and_return('output')
+
+ expect(subject).to eq('output')
+ end
+ end
+
+ context 'with a method throwing a non-existing-repository error' do
+ subject do
+ repository.cache_method_output_asymmetrically(:cats) do
+ raise Gitlab::Git::Repository::NoRepository
+ end
+ end
+
+ it 'returns nil' do
+ expect(subject).to eq(nil)
+ end
+
+ it 'does not cache the data' do
+ subject
+
+ expect(repository.instance_variable_defined?(:@cats)).to eq(false)
+ expect(cache.exist?(:cats)).to eq(false)
+ end
+ end
+
+ context 'with an existing repository' do
+ let(:object) { double }
+
+ context 'when it returns truthy' do
+ before do
+ expect(object).to receive(:cats).once.and_return('truthy output')
+ end
+
+ it 'caches the output in RequestStore' do
+ expect do
+ repository.cache_method_output_asymmetrically(:cats) { object.cats }
+ end.to change { request_store_cache.read(:cats) }.from(nil).to('truthy output')
+ end
+
+ it 'caches the output in RepositoryCache' do
+ expect do
+ repository.cache_method_output_asymmetrically(:cats) { object.cats }
+ end.to change { cache.read(:cats) }.from(nil).to('truthy output')
+ end
+ end
+
+ context 'when it returns false' do
+ before do
+ expect(object).to receive(:cats).once.and_return(false)
+ end
+
+ it 'caches the output in RequestStore' do
+ expect do
+ repository.cache_method_output_asymmetrically(:cats) { object.cats }
+ end.to change { request_store_cache.read(:cats) }.from(nil).to(false)
+ end
+
+ it 'does NOT cache the output in RepositoryCache' do
+ expect do
+ repository.cache_method_output_asymmetrically(:cats) { object.cats }
+ end.not_to change { cache.read(:cats) }.from(nil)
+ end
+ end
+ end
+ end
+
+ describe '#memoize_method_output' do
+ let(:fallback) { 10 }
+
+ context 'with a non-existing repository' do
+ let(:project) { create(:project) } # No repository
+
+ subject do
+ repository.memoize_method_output(:cats, fallback: fallback) do
+ repository.cats_call_stub
+ end
+ end
+
+ it 'returns the fallback value' do
+ expect(subject).to eq(fallback)
+ end
+
+ it 'avoids calling the original method' do
+ expect(repository).not_to receive(:cats_call_stub)
+
+ subject
+ end
+
+ it 'does not set the instance variable' do
+ subject
+
+ expect(repository.instance_variable_defined?(:@cats)).to eq(false)
+ end
+ end
+
+ context 'with a method throwing a non-existing-repository error' do
+ subject do
+ repository.memoize_method_output(:cats, fallback: fallback) do
+ raise Gitlab::Git::Repository::NoRepository
+ end
+ end
+
+ it 'returns the fallback value' do
+ expect(subject).to eq(fallback)
+ end
+
+ it 'does not set the instance variable' do
+ subject
+
+ expect(repository.instance_variable_defined?(:@cats)).to eq(false)
+ end
+ end
+
+ context 'with an existing repository' do
+ it 'sets the instance variable' do
+ repository.memoize_method_output(:cats, fallback: fallback) do
+ 'block output'
+ end
+
+ expect(repository.instance_variable_get(:@cats)).to eq('block output')
+ end
+ end
+ end
+
describe '#expire_method_caches' do
it 'expires the caches of the given methods' do
expect(cache).to receive(:expire).with(:rendered_readme)
diff --git a/spec/lib/gitlab/repository_cache_spec.rb b/spec/lib/gitlab/repository_cache_spec.rb
index fc259cf1208..1b9a8b4ab0d 100644
--- a/spec/lib/gitlab/repository_cache_spec.rb
+++ b/spec/lib/gitlab/repository_cache_spec.rb
@@ -4,14 +4,14 @@ describe Gitlab::RepositoryCache do
let(:backend) { double('backend').as_null_object }
let(:project) { create(:project) }
let(:repository) { project.repository }
- let(:namespace) { "#{repository.full_path}:#{project.id}" }
+ let(:namespace) { "project:#{project.id}" }
let(:cache) { described_class.new(repository, backend: backend) }
describe '#cache_key' do
subject { cache.cache_key(:foo) }
it 'includes the namespace' do
- expect(subject).to eq "foo:#{namespace}"
+ expect(subject).to eq "#{namespace}:foo"
end
context 'with a given namespace' do
@@ -22,7 +22,7 @@ describe Gitlab::RepositoryCache do
end
it 'includes the full namespace' do
- expect(subject).to eq "foo:#{namespace}:#{extra_namespace}"
+ expect(subject).to eq "#{namespace}:#{extra_namespace}:foo"
end
end
end
@@ -30,21 +30,106 @@ describe Gitlab::RepositoryCache do
describe '#expire' do
it 'expires the given key from the cache' do
cache.expire(:foo)
- expect(backend).to have_received(:delete).with("foo:#{namespace}")
+ expect(backend).to have_received(:delete).with("#{namespace}:foo")
end
end
describe '#fetch' do
it 'fetches the given key from the cache' do
cache.fetch(:bar)
- expect(backend).to have_received(:fetch).with("bar:#{namespace}")
+ expect(backend).to have_received(:fetch).with("#{namespace}:bar")
end
it 'accepts a block' do
p = -> {}
cache.fetch(:baz, &p)
- expect(backend).to have_received(:fetch).with("baz:#{namespace}", &p)
+ expect(backend).to have_received(:fetch).with("#{namespace}:baz", &p)
+ end
+ end
+
+ describe '#fetch_without_caching_false', :use_clean_rails_memory_store_caching do
+ let(:key) { :foo }
+ let(:backend) { Rails.cache }
+
+ it 'requires a block' do
+ expect do
+ cache.fetch_without_caching_false(key)
+ end.to raise_error(LocalJumpError)
+ end
+
+ context 'when the key does not exist in the cache' do
+ context 'when the result of the block is truthy' do
+ it 'returns the result of the block' do
+ result = cache.fetch_without_caching_false(key) { true }
+
+ expect(result).to be true
+ end
+
+ it 'caches the value' do
+ expect(backend).to receive(:write).with("#{namespace}:#{key}", true)
+
+ cache.fetch_without_caching_false(key) { true }
+ end
+ end
+
+ context 'when the result of the block is falsey' do
+ let(:p) { -> { false } }
+
+ it 'returns the result of the block' do
+ result = cache.fetch_without_caching_false(key, &p)
+
+ expect(result).to be false
+ end
+
+ it 'does not cache the value' do
+ expect(backend).not_to receive(:write).with("#{namespace}:#{key}", true)
+
+ cache.fetch_without_caching_false(key, &p)
+ end
+ end
+ end
+
+ context 'when the cached value is truthy' do
+ before do
+ backend.write("#{namespace}:#{key}", true)
+ end
+
+ it 'returns the cached value' do
+ result = cache.fetch_without_caching_false(key) { 'block result' }
+
+ expect(result).to be true
+ end
+
+ it 'does not execute the block' do
+ expect do |b|
+ cache.fetch_without_caching_false(key, &b)
+ end.not_to yield_control
+ end
+
+ it 'does not write to the cache' do
+ expect(backend).not_to receive(:write)
+
+ cache.fetch_without_caching_false(key) { 'block result' }
+ end
+ end
+
+ context 'when the cached value is falsey' do
+ before do
+ backend.write("#{namespace}:#{key}", false)
+ end
+
+ it 'returns the result of the block' do
+ result = cache.fetch_without_caching_false(key) { 'block result' }
+
+ expect(result).to eq 'block result'
+ end
+
+ it 'writes the truthy value to the cache' do
+ expect(backend).to receive(:write).with("#{namespace}:#{key}", 'block result')
+
+ cache.fetch_without_caching_false(key) { 'block result' }
+ end
end
end
end
diff --git a/spec/lib/gitlab/safe_request_store_spec.rb b/spec/lib/gitlab/safe_request_store_spec.rb
new file mode 100644
index 00000000000..c797171dbe2
--- /dev/null
+++ b/spec/lib/gitlab/safe_request_store_spec.rb
@@ -0,0 +1,245 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SafeRequestStore do
+ describe '.store' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect(described_class.store).to eq(RequestStore)
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'does not use RequestStore' do
+ expect(described_class.store).to be_a(Gitlab::NullRequestStore)
+ end
+ end
+ end
+
+ describe '.begin!' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect(RequestStore).to receive(:begin!)
+
+ described_class.begin!
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'uses RequestStore' do
+ expect(RequestStore).to receive(:begin!)
+
+ described_class.begin!
+ end
+ end
+ end
+
+ describe '.clear!' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect(RequestStore).to receive(:clear!).twice.and_call_original
+
+ described_class.clear!
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'uses RequestStore' do
+ expect(RequestStore).to receive(:clear!).and_call_original
+
+ described_class.clear!
+ end
+ end
+ end
+
+ describe '.end!' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect(RequestStore).to receive(:end!).twice.and_call_original
+
+ described_class.end!
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'uses RequestStore' do
+ expect(RequestStore).to receive(:end!).and_call_original
+
+ described_class.end!
+ end
+ end
+ end
+
+ describe '.write' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect do
+ described_class.write('foo', true)
+ end.to change { described_class.read('foo') }.from(nil).to(true)
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'does not use RequestStore' do
+ expect do
+ described_class.write('foo', true)
+ end.not_to change { described_class.read('foo') }.from(nil)
+ end
+ end
+ end
+
+ describe '.[]=' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect do
+ described_class['foo'] = true
+ end.to change { described_class.read('foo') }.from(nil).to(true)
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'does not use RequestStore' do
+ expect do
+ described_class['foo'] = true
+ end.not_to change { described_class.read('foo') }.from(nil)
+ end
+ end
+ end
+
+ describe '.read' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect do
+ RequestStore.write('foo', true)
+ end.to change { described_class.read('foo') }.from(nil).to(true)
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'does not use RequestStore' do
+ expect do
+ RequestStore.write('foo', true)
+ end.not_to change { described_class.read('foo') }.from(nil)
+
+ RequestStore.clear! # Clean up
+ end
+ end
+ end
+
+ describe '.[]' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect do
+ RequestStore.write('foo', true)
+ end.to change { described_class['foo'] }.from(nil).to(true)
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'does not use RequestStore' do
+ expect do
+ RequestStore.write('foo', true)
+ end.not_to change { described_class['foo'] }.from(nil)
+
+ RequestStore.clear! # Clean up
+ end
+ end
+ end
+
+ describe '.exist?' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect do
+ RequestStore.write('foo', 'not nil')
+ end.to change { described_class.exist?('foo') }.from(false).to(true)
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'does not use RequestStore' do
+ expect do
+ RequestStore.write('foo', 'not nil')
+ end.not_to change { described_class.exist?('foo') }.from(false)
+
+ RequestStore.clear! # Clean up
+ end
+ end
+ end
+
+ describe '.fetch' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ expect do
+ described_class.fetch('foo') { 'block result' }
+ end.to change { described_class.read('foo') }.from(nil).to('block result')
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ it 'does not use RequestStore' do
+ RequestStore.clear! # Ensure clean
+
+ expect do
+ described_class.fetch('foo') { 'block result' }
+ end.not_to change { described_class.read('foo') }.from(nil)
+
+ RequestStore.clear! # Clean up
+ end
+ end
+ end
+
+ describe '.delete' do
+ context 'when RequestStore is active', :request_store do
+ it 'uses RequestStore' do
+ described_class.write('foo', true)
+
+ expect do
+ described_class.delete('foo')
+ end.to change { described_class.read('foo') }.from(true).to(nil)
+ end
+
+ context 'when given a block and the key exists' do
+ it 'does not execute the block' do
+ described_class.write('foo', true)
+
+ expect do |b|
+ described_class.delete('foo', &b)
+ end.not_to yield_control
+ end
+ end
+
+ context 'when given a block and the key does not exist' do
+ it 'yields the key and returns the block result' do
+ result = described_class.delete('foo') { |key| "#{key} block result" }
+
+ expect(result).to eq('foo block result')
+ end
+ end
+ end
+
+ context 'when RequestStore is NOT active' do
+ before do
+ RequestStore.write('foo', true)
+ end
+
+ after do
+ RequestStore.clear! # Clean up
+ end
+
+ it 'does not use RequestStore' do
+ expect do
+ described_class.delete('foo')
+ end.not_to change { RequestStore.read('foo') }.from(true)
+ end
+
+ context 'when given a block' do
+ it 'yields the key and returns the block result' do
+ result = described_class.delete('foo') { |key| "#{key} block result" }
+
+ expect(result).to eq('foo block result')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sentry_spec.rb b/spec/lib/gitlab/sentry_spec.rb
index 499757da061..d3b41b27b80 100644
--- a/spec/lib/gitlab/sentry_spec.rb
+++ b/spec/lib/gitlab/sentry_spec.rb
@@ -52,4 +52,28 @@ describe Gitlab::Sentry do
end
end
end
+
+ context '.track_acceptable_exception' do
+ let(:exception) { RuntimeError.new('boom') }
+
+ before do
+ allow(described_class).to receive(:enabled?).and_return(true)
+ end
+
+ it 'calls Raven.capture_exception' do
+ expected_extras = {
+ some_other_info: 'info',
+ issue_url: 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1'
+ }
+
+ expect(Raven).to receive(:capture_exception)
+ .with(exception, extra: a_hash_including(expected_extras))
+
+ described_class.track_acceptable_exception(
+ exception,
+ issue_url: 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1',
+ extra: { some_other_info: 'info' }
+ )
+ end
+ end
end
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index f8bf896950e..6ce9d515a0f 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -7,15 +7,10 @@ describe Gitlab::Shell do
let(:repository) { project.repository }
let(:gitlab_shell) { described_class.new }
let(:popen_vars) { { 'GIT_TERMINAL_PROMPT' => ENV['GIT_TERMINAL_PROMPT'] } }
- let(:gitlab_projects) { double('gitlab_projects') }
let(:timeout) { Gitlab.config.gitlab_shell.git_timeout }
before do
allow(Project).to receive(:find).and_return(project)
-
- allow(gitlab_shell).to receive(:gitlab_projects)
- .with(project.repository_storage, project.disk_path + '.git')
- .and_return(gitlab_projects)
end
it { is_expected.to respond_to :add_key }
@@ -503,126 +498,6 @@ describe Gitlab::Shell do
end
end
- describe '#fetch_remote' do
- def fetch_remote(ssh_auth = nil, prune = true)
- gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', ssh_auth: ssh_auth, prune: prune)
- end
-
- def expect_call(fail, options = {})
- receive_fetch_remote =
- if fail
- receive(:fetch_remote).and_raise(GRPC::NotFound)
- else
- receive(:fetch_remote).and_return(true)
- end
-
- expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive_fetch_remote
- end
-
- def build_ssh_auth(opts = {})
- defaults = {
- ssh_import?: true,
- ssh_key_auth?: false,
- ssh_known_hosts: nil,
- ssh_private_key: nil
- }
-
- double(:ssh_auth, defaults.merge(opts))
- end
-
- it 'returns true when the command succeeds' do
- expect_call(false, force: false, tags: true, prune: true)
-
- expect(fetch_remote).to be_truthy
- end
-
- it 'returns true when the command succeeds' do
- expect_call(false, force: false, tags: true, prune: false)
-
- expect(fetch_remote(nil, false)).to be_truthy
- end
-
- it 'raises an exception when the command fails' do
- expect_call(true, force: false, tags: true, prune: true)
-
- expect { fetch_remote }.to raise_error(Gitlab::Shell::Error)
- end
-
- it 'allows forced and no_tags to be changed' do
- expect_call(false, force: true, tags: false, prune: true)
-
- result = gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', forced: true, no_tags: true, prune: true)
- expect(result).to be_truthy
- end
-
- context 'SSH auth' do
- it 'passes the SSH key if specified' do
- expect_call(false, force: false, tags: true, prune: true, ssh_key: 'foo')
-
- ssh_auth = build_ssh_auth(ssh_key_auth?: true, ssh_private_key: 'foo')
-
- expect(fetch_remote(ssh_auth)).to be_truthy
- end
-
- it 'does not pass an empty SSH key' do
- expect_call(false, force: false, tags: true, prune: true)
-
- ssh_auth = build_ssh_auth(ssh_key_auth: true, ssh_private_key: '')
-
- expect(fetch_remote(ssh_auth)).to be_truthy
- end
-
- it 'does not pass the key unless SSH key auth is to be used' do
- expect_call(false, force: false, tags: true, prune: true)
-
- ssh_auth = build_ssh_auth(ssh_key_auth: false, ssh_private_key: 'foo')
-
- expect(fetch_remote(ssh_auth)).to be_truthy
- end
-
- it 'passes the known_hosts data if specified' do
- expect_call(false, force: false, tags: true, prune: true, known_hosts: 'foo')
-
- ssh_auth = build_ssh_auth(ssh_known_hosts: 'foo')
-
- expect(fetch_remote(ssh_auth)).to be_truthy
- end
-
- it 'does not pass empty known_hosts data' do
- expect_call(false, force: false, tags: true, prune: true)
-
- ssh_auth = build_ssh_auth(ssh_known_hosts: '')
-
- expect(fetch_remote(ssh_auth)).to be_truthy
- end
-
- it 'does not pass known_hosts data unless SSH is to be used' do
- expect_call(false, force: false, tags: true, prune: true)
-
- ssh_auth = build_ssh_auth(ssh_import?: false, ssh_known_hosts: 'foo')
-
- expect(fetch_remote(ssh_auth)).to be_truthy
- end
- end
-
- context 'gitaly call' do
- let(:remote_name) { 'remote-name' }
- let(:ssh_auth) { double(:ssh_auth) }
-
- subject do
- gitlab_shell.fetch_remote(repository.raw_repository, remote_name,
- forced: true, no_tags: true, ssh_auth: ssh_auth)
- end
-
- it 'passes the correct params to the gitaly service' do
- expect(repository.gitaly_repository_client).to receive(:fetch_remote)
- .with(remote_name, ssh_auth: ssh_auth, forced: true, no_tags: true, prune: true, timeout: timeout)
-
- subject
- end
- end
- end
-
describe '#import_repository' do
let(:import_url) { 'https://gitlab.com/gitlab-org/gitlab-ce.git' }
diff --git a/spec/lib/gitlab/sidekiq_throttler_spec.rb b/spec/lib/gitlab/sidekiq_throttler_spec.rb
deleted file mode 100644
index 2dbb7bb7c34..00000000000
--- a/spec/lib/gitlab/sidekiq_throttler_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::SidekiqThrottler do
- describe '#execute!' do
- context 'when job throttling is enabled' do
- before do
- Sidekiq.options[:concurrency] = 35
-
- stub_application_setting(
- sidekiq_throttling_enabled: true,
- sidekiq_throttling_factor: 0.1,
- sidekiq_throttling_queues: %w[build project_cache]
- )
- end
-
- it 'requires sidekiq-limit_fetch' do
- expect(described_class).to receive(:require).with('sidekiq-limit_fetch').and_call_original
-
- described_class.execute!
- end
-
- it 'sets limits on the selected queues' do
- described_class.execute!
-
- expect(Sidekiq::Queue['build'].limit).to eq 4
- expect(Sidekiq::Queue['project_cache'].limit).to eq 4
- end
-
- it 'does not set limits on other queues' do
- described_class.execute!
-
- expect(Sidekiq::Queue['merge'].limit).to be_nil
- end
- end
-
- context 'when job throttling is disabled' do
- it 'does not require sidekiq-limit_fetch' do
- expect(described_class).not_to receive(:require).with('sidekiq-limit_fetch')
-
- described_class.execute!
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb
index 194cae8c645..eceacac58af 100644
--- a/spec/lib/gitlab/slash_commands/command_spec.rb
+++ b/spec/lib/gitlab/slash_commands/command_spec.rb
@@ -44,7 +44,7 @@ describe Gitlab::SlashCommands::Command do
let!(:build) { create(:ci_build, pipeline: pipeline) }
let!(:pipeline) { create(:ci_pipeline, project: project) }
let!(:staging) { create(:environment, name: 'staging', project: project) }
- let!(:deployment) { create(:deployment, environment: staging, deployable: build) }
+ let!(:deployment) { create(:deployment, :success, environment: staging, deployable: build) }
let!(:manual) do
create(:ci_build, :manual, pipeline: pipeline,
diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb
index 0d57334aa4c..25f3e8a0409 100644
--- a/spec/lib/gitlab/slash_commands/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb
@@ -31,7 +31,7 @@ describe Gitlab::SlashCommands::Deploy do
let!(:staging) { create(:environment, name: 'staging', project: project) }
let!(:pipeline) { create(:ci_pipeline, project: project) }
let!(:build) { create(:ci_build, pipeline: pipeline) }
- let!(:deployment) { create(:deployment, environment: staging, deployable: build) }
+ let!(:deployment) { create(:deployment, :success, environment: staging, deployable: build) }
context 'without actions' do
it 'does not execute an action' do
diff --git a/spec/lib/gitlab/storage_check/cli_spec.rb b/spec/lib/gitlab/storage_check/cli_spec.rb
deleted file mode 100644
index 6db0925899c..00000000000
--- a/spec/lib/gitlab/storage_check/cli_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::StorageCheck::CLI do
- let(:options) { Gitlab::StorageCheck::Options.new('unix://tmp/socket.sock', nil, 1, false) }
- subject(:runner) { described_class.new(options) }
-
- describe '#update_settings' do
- it 'updates the interval when changed in a valid response and logs the change' do
- fake_response = double
- expect(fake_response).to receive(:valid?).and_return(true)
- expect(fake_response).to receive(:check_interval).and_return(42)
- expect(runner.logger).to receive(:info)
-
- runner.update_settings(fake_response)
-
- expect(options.interval).to eq(42)
- end
- end
-end
diff --git a/spec/lib/gitlab/storage_check/gitlab_caller_spec.rb b/spec/lib/gitlab/storage_check/gitlab_caller_spec.rb
deleted file mode 100644
index d869022fd31..00000000000
--- a/spec/lib/gitlab/storage_check/gitlab_caller_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::StorageCheck::GitlabCaller do
- let(:options) { Gitlab::StorageCheck::Options.new('unix://tmp/socket.sock', nil, nil, false) }
- subject(:gitlab_caller) { described_class.new(options) }
-
- describe '#call!' do
- context 'when a socket is given' do
- it 'calls a socket' do
- fake_connection = double
- expect(fake_connection).to receive(:post)
- expect(Excon).to receive(:new).with('unix://tmp/socket.sock', socket: "tmp/socket.sock") { fake_connection }
-
- gitlab_caller.call!
- end
- end
-
- context 'when a host is given' do
- let(:options) { Gitlab::StorageCheck::Options.new('http://localhost:8080', nil, nil, false) }
-
- it 'it calls a http response' do
- fake_connection = double
- expect(Excon).to receive(:new).with('http://localhost:8080', socket: nil) { fake_connection }
- expect(fake_connection).to receive(:post)
-
- gitlab_caller.call!
- end
- end
- end
-
- describe '#headers' do
- it 'Adds the JSON header' do
- headers = gitlab_caller.headers
-
- expect(headers['Content-Type']).to eq('application/json')
- end
-
- context 'when a token was provided' do
- let(:options) { Gitlab::StorageCheck::Options.new('unix://tmp/socket.sock', 'atoken', nil, false) }
-
- it 'adds it to the headers' do
- expect(gitlab_caller.headers['TOKEN']).to eq('atoken')
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/storage_check/option_parser_spec.rb b/spec/lib/gitlab/storage_check/option_parser_spec.rb
deleted file mode 100644
index cad4dfbefcf..00000000000
--- a/spec/lib/gitlab/storage_check/option_parser_spec.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::StorageCheck::OptionParser do
- describe '.parse!' do
- it 'assigns all options' do
- args = %w(--target unix://tmp/hello/world.sock --token thetoken --interval 42)
-
- options = described_class.parse!(args)
-
- expect(options.token).to eq('thetoken')
- expect(options.interval).to eq(42)
- expect(options.target).to eq('unix://tmp/hello/world.sock')
- end
-
- it 'requires the interval to be a number' do
- args = %w(--target unix://tmp/hello/world.sock --interval fortytwo)
-
- expect { described_class.parse!(args) }.to raise_error(OptionParser::InvalidArgument)
- end
-
- it 'raises an error if the scheme is not included' do
- args = %w(--target tmp/hello/world.sock)
-
- expect { described_class.parse!(args) }.to raise_error(OptionParser::InvalidArgument)
- end
-
- it 'raises an error if both socket and host are missing' do
- expect { described_class.parse!([]) }.to raise_error(OptionParser::InvalidArgument)
- end
- end
-end
diff --git a/spec/lib/gitlab/storage_check/response_spec.rb b/spec/lib/gitlab/storage_check/response_spec.rb
deleted file mode 100644
index 0ff2963e443..00000000000
--- a/spec/lib/gitlab/storage_check/response_spec.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::StorageCheck::Response do
- let(:fake_json) do
- {
- check_interval: 42,
- results: [
- { storage: 'working', success: true },
- { storage: 'skipped', success: nil },
- { storage: 'failing', success: false }
- ]
- }.to_json
- end
-
- let(:fake_http_response) do
- fake_response = instance_double("Excon::Response - Status check")
- allow(fake_response).to receive(:status).and_return(200)
- allow(fake_response).to receive(:body).and_return(fake_json)
- allow(fake_response).to receive(:headers).and_return('Content-Type' => 'application/json')
-
- fake_response
- end
- let(:response) { described_class.new(fake_http_response) }
-
- describe '#valid?' do
- it 'is valid for a success response with parseable JSON' do
- expect(response).to be_valid
- end
- end
-
- describe '#check_interval' do
- it 'returns the result from the JSON' do
- expect(response.check_interval).to eq(42)
- end
- end
-
- describe '#responsive_shards' do
- it 'contains the names of working shards' do
- expect(response.responsive_shards).to contain_exactly('working')
- end
- end
-
- describe '#skipped_shards' do
- it 'contains the names of skipped shards' do
- expect(response.skipped_shards).to contain_exactly('skipped')
- end
- end
-
- describe '#failing_shards' do
- it 'contains the name of failing shards' do
- expect(response.failing_shards).to contain_exactly('failing')
- end
- end
-end
diff --git a/spec/lib/gitlab/template/finders/repo_template_finders_spec.rb b/spec/lib/gitlab/template/finders/repo_template_finders_spec.rb
new file mode 100644
index 00000000000..2eabccd5dff
--- /dev/null
+++ b/spec/lib/gitlab/template/finders/repo_template_finders_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Gitlab::Template::Finders::RepoTemplateFinder do
+ set(:project) { create(:project, :repository) }
+
+ let(:categories) { { 'HTML' => 'html' } }
+
+ subject(:finder) { described_class.new(project, 'files/', '.html', categories) }
+
+ describe '#read' do
+ it 'returns the content of the given path' do
+ result = finder.read('files/html/500.html')
+
+ expect(result).to be_present
+ end
+
+ it 'raises an error if the path does not exist' do
+ expect { finder.read('does/not/exist') }.to raise_error(described_class::FileNotFoundError)
+ end
+ end
+
+ describe '#find' do
+ it 'returns the full path of the found template' do
+ result = finder.find('500')
+
+ expect(result).to eq('files/html/500.html')
+ end
+ end
+
+ describe '#list_files_for' do
+ it 'returns the full path of the found files' do
+ result = finder.list_files_for('files/html')
+
+ expect(result).to contain_exactly('files/html/500.html')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb b/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
index e2fa76522bc..fe46c67a920 100644
--- a/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
+++ b/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
@@ -40,7 +40,7 @@ describe Gitlab::Template::GitlabCiYmlTemplate do
describe '#content' do
it 'loads the full file' do
- gitignore = subject.new(Rails.root.join('vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml'))
+ gitignore = subject.new(Rails.root.join('lib/gitlab/ci/templates/Ruby.gitlab-ci.yml'))
expect(gitignore.name).to eq 'Ruby'
expect(gitignore.content).to start_with('#')
diff --git a/spec/lib/gitlab/tree_summary_spec.rb b/spec/lib/gitlab/tree_summary_spec.rb
new file mode 100644
index 00000000000..7ffcef2baef
--- /dev/null
+++ b/spec/lib/gitlab/tree_summary_spec.rb
@@ -0,0 +1,202 @@
+require 'spec_helper'
+
+describe Gitlab::TreeSummary do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:project) { create(:project, :empty_repo) }
+ let(:repo) { project.repository }
+ let(:commit) { repo.head_commit }
+
+ let(:path) { nil }
+ let(:offset) { nil }
+ let(:limit) { nil }
+
+ subject(:summary) { described_class.new(commit, project, path: path, offset: offset, limit: limit) }
+
+ describe '#initialize' do
+ it 'defaults offset to 0' do
+ expect(summary.offset).to eq(0)
+ end
+
+ it 'defaults limit to 25' do
+ expect(summary.limit).to eq(25)
+ end
+ end
+
+ describe '#summarize' do
+ let(:project) { create(:project, :custom_repo, files: { 'a.txt' => '' }) }
+
+ subject(:summarized) { summary.summarize }
+
+ it 'returns an array of entries, and an array of commits' do
+ expect(summarized).to be_a(Array)
+ expect(summarized.size).to eq(2)
+
+ entries, commits = *summarized
+ aggregate_failures do
+ expect(entries).to contain_exactly(
+ a_hash_including(file_name: 'a.txt', commit: have_attributes(id: commit.id))
+ )
+
+ expect(commits).to match_array(entries.map { |entry| entry[:commit] })
+ end
+ end
+ end
+
+ describe '#summarize (entries)' do
+ let(:limit) { 2 }
+
+ custom_files = {
+ 'a.txt' => '',
+ 'b.txt' => '',
+ 'directory/c.txt' => ''
+ }
+
+ let(:project) { create(:project, :custom_repo, files: custom_files) }
+ let(:commit) { repo.head_commit }
+
+ subject(:entries) { summary.summarize.first }
+
+ it 'summarizes the entries within the window' do
+ is_expected.to contain_exactly(
+ a_hash_including(type: :tree, file_name: 'directory'),
+ a_hash_including(type: :blob, file_name: 'a.txt')
+ # b.txt is excluded by the limit
+ )
+ end
+
+ it 'references the commit and commit path in entries' do
+ entry = entries.first
+ expected_commit_path = Gitlab::Routing.url_helpers.project_commit_path(project, commit)
+
+ expect(entry[:commit]).to be_a(::Commit)
+ expect(entry[:commit_path]).to eq expected_commit_path
+ end
+
+ context 'in a good subdirectory' do
+ let(:path) { 'directory' }
+
+ it 'summarizes the entries in the subdirectory' do
+ is_expected.to contain_exactly(a_hash_including(type: :blob, file_name: 'c.txt'))
+ end
+ end
+
+ context 'in a non-existent subdirectory' do
+ let(:path) { 'tmp' }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'custom offset and limit' do
+ let(:offset) { 2 }
+
+ it 'returns entries from the offset' do
+ is_expected.to contain_exactly(a_hash_including(type: :blob, file_name: 'b.txt'))
+ end
+ end
+ end
+
+ describe '#summarize (commits)' do
+ # This is a commit in the master branch of the gitlab-test repository that
+ # satisfies certain assumptions these tests depend on
+ let(:test_commit_sha) { '7975be0116940bf2ad4321f79d02a55c5f7779aa' }
+ let(:whitespace_commit_sha) { '66eceea0db202bb39c4e445e8ca28689645366c5' }
+
+ let(:project) { create(:project, :repository) }
+ let(:commit) { repo.commit(test_commit_sha) }
+ let(:limit) { nil }
+ let(:entries) { summary.summarize.first }
+
+ subject(:commits) do
+ summary.summarize.last
+ end
+
+ it 'returns an Array of ::Commit objects' do
+ is_expected.not_to be_empty
+ is_expected.to all(be_kind_of(::Commit))
+ end
+
+ it 'deduplicates commits when multiple entries reference the same commit' do
+ expect(commits.size).to be < entries.size
+ end
+
+ context 'in a subdirectory' do
+ let(:path) { 'files' }
+
+ it 'returns commits for entries in the subdirectory' do
+ expect(commits).to satisfy_one { |c| c.id == whitespace_commit_sha }
+ end
+ end
+ end
+
+ describe '#more?' do
+ let(:path) { 'tmp/more' }
+
+ where(:num_entries, :offset, :limit, :expected_result) do
+ 0 | 0 | 0 | false
+ 0 | 0 | 1 | false
+
+ 1 | 0 | 0 | true
+ 1 | 0 | 1 | false
+ 1 | 1 | 0 | false
+ 1 | 1 | 1 | false
+
+ 2 | 0 | 0 | true
+ 2 | 0 | 1 | true
+ 2 | 0 | 2 | false
+ 2 | 0 | 3 | false
+ 2 | 1 | 0 | true
+ 2 | 1 | 1 | false
+ 2 | 2 | 0 | false
+ 2 | 2 | 1 | false
+ end
+
+ with_them do
+ before do
+ create_file('dummy', path: 'other') if num_entries.zero?
+ 1.upto(num_entries) { |n| create_file(n, path: path) }
+ end
+
+ subject { summary.more? }
+
+ it { is_expected.to eq(expected_result) }
+ end
+ end
+
+ describe '#next_offset' do
+ let(:path) { 'tmp/next_offset' }
+
+ where(:num_entries, :offset, :limit, :expected_result) do
+ 0 | 0 | 0 | 0
+ 0 | 0 | 1 | 1
+ 0 | 1 | 0 | 1
+ 0 | 1 | 1 | 1
+
+ 1 | 0 | 0 | 0
+ 1 | 0 | 1 | 1
+ 1 | 1 | 0 | 1
+ 1 | 1 | 1 | 2
+ end
+
+ with_them do
+ before do
+ create_file('dummy', path: 'other') if num_entries.zero?
+ 1.upto(num_entries) { |n| create_file(n, path: path) }
+ end
+
+ subject { summary.next_offset }
+
+ it { is_expected.to eq(expected_result) }
+ end
+ end
+
+ def create_file(unique, path:)
+ repo.create_file(
+ project.creator,
+ "#{path}/file-#{unique}.txt",
+ 'content',
+ message: "Commit message #{unique}",
+ branch_name: 'master'
+ )
+ end
+end
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index 6f5f9938eca..8df0facdab3 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
require 'spec_helper'
describe Gitlab::UrlBlocker do
@@ -28,6 +29,16 @@ describe Gitlab::UrlBlocker do
expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', protocols: ['http'])).to be true
end
+ it 'returns true for localhost IPs' do
+ expect(described_class.blocked_url?('https://0.0.0.0/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://[::1]/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://127.0.0.1/foo/foo.git')).to be true
+ end
+
+ it 'returns true for loopback IP' do
+ expect(described_class.blocked_url?('https://127.0.0.2/foo/foo.git')).to be true
+ end
+
it 'returns true for alternative version of 127.0.0.1 (0177.1)' do
expect(described_class.blocked_url?('https://0177.1:65535/foo/foo.git')).to be true
end
@@ -82,6 +93,27 @@ describe Gitlab::UrlBlocker do
expect(described_class).not_to be_blocked_url("http://#{ip}")
end
end
+
+ it 'allows localhost endpoints' do
+ expect(described_class).not_to be_blocked_url('http://0.0.0.0', allow_localhost: true)
+ expect(described_class).not_to be_blocked_url('http://localhost', allow_localhost: true)
+ expect(described_class).not_to be_blocked_url('http://127.0.0.1', allow_localhost: true)
+ end
+
+ it 'allows loopback endpoints' do
+ expect(described_class).not_to be_blocked_url('http://127.0.0.2', allow_localhost: true)
+ end
+
+ it 'allows IPv4 link-local endpoints' do
+ expect(described_class).not_to be_blocked_url('http://169.254.169.254')
+ expect(described_class).not_to be_blocked_url('http://169.254.168.100')
+ end
+
+ # This is blocked due to the hostname check: https://gitlab.com/gitlab-org/gitlab-ce/issues/50227
+ it 'blocks IPv6 link-local endpoints' do
+ expect(described_class).to be_blocked_url('http://[::ffff:169.254.169.254]')
+ expect(described_class).to be_blocked_url('http://[::ffff:169.254.168.100]')
+ end
end
context 'false' do
@@ -96,10 +128,21 @@ describe Gitlab::UrlBlocker do
expect(described_class).to be_blocked_url("http://#{ip}", allow_local_network: false)
end
end
+
+ it 'blocks IPv4 link-local endpoints' do
+ expect(described_class).to be_blocked_url('http://169.254.169.254', allow_local_network: false)
+ expect(described_class).to be_blocked_url('http://169.254.168.100', allow_local_network: false)
+ end
+
+ it 'blocks IPv6 link-local endpoints' do
+ expect(described_class).to be_blocked_url('http://[::ffff:169.254.169.254]', allow_local_network: false)
+ expect(described_class).to be_blocked_url('http://[::ffff:169.254.168.100]', allow_local_network: false)
+ expect(described_class).to be_blocked_url('http://[FE80::C800:EFF:FE74:8]', allow_local_network: false)
+ end
end
def stub_domain_resolv(domain, ip)
- allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([double(ip_address: ip, ipv4_private?: true)])
+ allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([double(ip_address: ip, ipv4_private?: true, ipv6_link_local?: false, ipv4_loopback?: false, ipv6_loopback?: false)])
end
def unstub_domain_resolv
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index a19b3c0ba66..b212d2b05f2 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -8,6 +8,7 @@ describe Gitlab::UsageData do
before do
create(:jira_service, project: projects[0])
create(:jira_service, project: projects[1])
+ create(:jira_cloud_service, project: projects[2])
create(:prometheus_service, project: projects[1])
create(:service, project: projects[0], type: 'SlackSlashCommandsService', active: true)
create(:service, project: projects[1], type: 'SlackService', active: true)
@@ -20,6 +21,7 @@ describe Gitlab::UsageData do
create(:clusters_applications_ingress, :installed, cluster: gcp_cluster)
create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster)
create(:clusters_applications_runner, :installed, cluster: gcp_cluster)
+ create(:clusters_applications_knative, :installed, cluster: gcp_cluster)
end
subject { described_class.data }
@@ -46,6 +48,7 @@ describe Gitlab::UsageData do
git
database
avg_cycle_analytics
+ web_ide_commits
))
end
@@ -56,6 +59,7 @@ describe Gitlab::UsageData do
expect(count_data[:projects]).to eq(3)
expect(count_data.keys).to match_array(%i(
+ assignee_lists
boards
ci_builds
ci_internal_pipelines
@@ -79,18 +83,23 @@ describe Gitlab::UsageData do
clusters_applications_ingress
clusters_applications_prometheus
clusters_applications_runner
+ clusters_applications_knative
in_review_folder
groups
issues
keys
+ label_lists
labels
lfs_objects
merge_requests
+ milestone_lists
milestones
notes
projects
projects_imported_from_github
projects_jira_active
+ projects_jira_server_active
+ projects_jira_cloud_active
projects_slack_notifications_active
projects_slack_slash_active
projects_prometheus_active
@@ -110,7 +119,9 @@ describe Gitlab::UsageData do
expect(count_data[:projects]).to eq(3)
expect(count_data[:projects_prometheus_active]).to eq(1)
- expect(count_data[:projects_jira_active]).to eq(2)
+ expect(count_data[:projects_jira_active]).to eq(3)
+ expect(count_data[:projects_jira_server_active]).to eq(2)
+ expect(count_data[:projects_jira_cloud_active]).to eq(1)
expect(count_data[:projects_slack_notifications_active]).to eq(2)
expect(count_data[:projects_slack_slash_active]).to eq(1)
@@ -122,6 +133,14 @@ describe Gitlab::UsageData do
expect(count_data[:clusters_applications_ingress]).to eq(1)
expect(count_data[:clusters_applications_prometheus]).to eq(1)
expect(count_data[:clusters_applications_runner]).to eq(1)
+ expect(count_data[:clusters_applications_knative]).to eq(1)
+ end
+
+ it 'works when queries time out' do
+ allow_any_instance_of(ActiveRecord::Relation)
+ .to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
+
+ expect { subject }.not_to raise_error
end
end
@@ -163,4 +182,20 @@ describe Gitlab::UsageData do
expect(subject[:recorded_at]).to be_a(Time)
end
end
+
+ describe '#count' do
+ let(:relation) { double(:relation) }
+
+ it 'returns the count when counting succeeds' do
+ allow(relation).to receive(:count).and_return(1)
+
+ expect(described_class.count(relation)).to eq(1)
+ end
+
+ it 'returns the fallback value when counting fails' do
+ allow(relation).to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
+
+ expect(described_class.count(relation, fallback: 15)).to eq(15)
+ end
+ end
end
diff --git a/spec/lib/gitlab/user_extractor_spec.rb b/spec/lib/gitlab/user_extractor_spec.rb
new file mode 100644
index 00000000000..fcc05ab3a0c
--- /dev/null
+++ b/spec/lib/gitlab/user_extractor_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::UserExtractor do
+ let(:text) do
+ <<~TXT
+ This is a long texth that mentions some users.
+ @user-1, @user-2 and user@gitlab.org take a walk in the park.
+ There they meet @user-4 that was out with other-user@gitlab.org.
+ @user-1 thought it was late, so went home straight away
+ TXT
+ end
+ subject(:extractor) { described_class.new(text) }
+
+ describe '#users' do
+ it 'returns an empty relation when nil was passed' do
+ extractor = described_class.new(nil)
+
+ expect(extractor.users).to be_empty
+ expect(extractor.users).to be_a(ActiveRecord::Relation)
+ end
+
+ it 'returns the user case insensitive for usernames' do
+ user = create(:user, username: "USER-4")
+
+ expect(extractor.users).to include(user)
+ end
+
+ it 'returns users by primary email' do
+ user = create(:user, email: 'user@gitlab.org')
+
+ expect(extractor.users).to include(user)
+ end
+
+ it 'returns users by secondary email' do
+ user = create(:email, email: 'other-user@gitlab.org').user
+
+ expect(extractor.users).to include(user)
+ end
+ end
+
+ describe '#matches' do
+ it 'includes all mentioned email adresses' do
+ expect(extractor.matches[:emails]).to contain_exactly('user@gitlab.org', 'other-user@gitlab.org')
+ end
+
+ it 'includes all mentioned usernames' do
+ expect(extractor.matches[:usernames]).to contain_exactly('user-1', 'user-2', 'user-4')
+ end
+ end
+
+ describe '#references' do
+ it 'includes all user-references once' do
+ expect(extractor.references).to contain_exactly('user-1', 'user-2', 'user@gitlab.org', 'user-4', 'other-user@gitlab.org')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index 4ba99009855..ad2c9d7f2af 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::Utils do
delegate :to_boolean, :boolean_to_yes_no, :slugify, :random_string, :which, :ensure_array_from_string,
- :bytes_to_megabytes, to: :described_class
+ :bytes_to_megabytes, :append_path, to: :described_class
describe '.slugify' do
{
@@ -106,4 +106,25 @@ describe Gitlab::Utils do
expect(bytes_to_megabytes(bytes)).to eq(1)
end
end
+
+ describe '.append_path' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:host, :path, :result) do
+ 'http://test/' | '/foo/bar' | 'http://test/foo/bar'
+ 'http://test/' | '//foo/bar' | 'http://test/foo/bar'
+ 'http://test//' | '/foo/bar' | 'http://test/foo/bar'
+ 'http://test' | 'foo/bar' | 'http://test/foo/bar'
+ 'http://test//' | '' | 'http://test/'
+ 'http://test//' | nil | 'http://test/'
+ '' | '/foo/bar' | '/foo/bar'
+ nil | '/foo/bar' | '/foo/bar'
+ end
+
+ with_them do
+ it 'makes sure there is only one slash as path separator' do
+ expect(append_path(host, path)).to eq(result)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/version_info_spec.rb b/spec/lib/gitlab/version_info_spec.rb
index c8a1e433d59..30035c79e58 100644
--- a/spec/lib/gitlab/version_info_spec.rb
+++ b/spec/lib/gitlab/version_info_spec.rb
@@ -57,6 +57,9 @@ describe 'Gitlab::VersionInfo' do
context 'parse' do
it { expect(Gitlab::VersionInfo.parse("1.0.0")).to eq(@v1_0_0) }
it { expect(Gitlab::VersionInfo.parse("1.0.0.1")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("1.0.0-ee")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("1.0.0-rc1")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("1.0.0-rc1-ee")).to eq(@v1_0_0) }
it { expect(Gitlab::VersionInfo.parse("git 1.0.0b1")).to eq(@v1_0_0) }
it { expect(Gitlab::VersionInfo.parse("git 1.0b1")).not_to be_valid }
end
diff --git a/spec/lib/gitlab/view/presenter/base_spec.rb b/spec/lib/gitlab/view/presenter/base_spec.rb
index 4eca53032a2..02c2fd47197 100644
--- a/spec/lib/gitlab/view/presenter/base_spec.rb
+++ b/spec/lib/gitlab/view/presenter/base_spec.rb
@@ -40,7 +40,7 @@ describe Gitlab::View::Presenter::Base do
end
end
- context 'subject is overriden' do
+ context 'subject is overridden' do
it 'returns true' do
presenter = presenter_class.new(build_stubbed(:project, :public))
diff --git a/spec/lib/gitlab/web_ide_commits_counter_spec.rb b/spec/lib/gitlab/web_ide_commits_counter_spec.rb
new file mode 100644
index 00000000000..c51889a1c63
--- /dev/null
+++ b/spec/lib/gitlab/web_ide_commits_counter_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::WebIdeCommitsCounter, :clean_gitlab_redis_shared_state do
+ describe '.increment' do
+ it 'increments the web ide commits counter by 1' do
+ expect do
+ described_class.increment
+ end.to change { described_class.total_count }.from(0).to(1)
+ end
+ end
+
+ describe '.total_count' do
+ it 'returns the total amount of web ide commits' do
+ expect(described_class.total_count).to eq(0)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 23869f3d2da..b3f55a2e1bd 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -336,6 +336,22 @@ describe Gitlab::Workhorse do
it { expect { subject }.to raise_exception('Unsupported action: download') }
end
end
+
+ context 'when receive_max_input_size has been updated' do
+ it 'returns custom git config' do
+ allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { 1 }
+
+ expect(subject[:GitConfigOptions]).to be_present
+ end
+ end
+
+ context 'when receive_max_input_size is empty' do
+ it 'returns an empty git config' do
+ allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { nil }
+
+ expect(subject[:GitConfigOptions]).to be_empty
+ end
+ end
end
describe '.set_key_and_notify' do
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index 27cb3198e5b..e2134dc279c 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -66,25 +66,30 @@ describe GoogleApi::CloudPlatform::Client do
describe '#projects_zones_clusters_create' do
subject do
client.projects_zones_clusters_create(
- spy, spy, cluster_name, cluster_size, machine_type: machine_type)
+ project_id, zone, cluster_name, cluster_size, machine_type: machine_type, legacy_abac: legacy_abac)
end
+ let(:project_id) { 'project-123' }
+ let(:zone) { 'us-central1-a' }
let(:cluster_name) { 'test-cluster' }
let(:cluster_size) { 1 }
let(:machine_type) { 'n1-standard-2' }
+ let(:legacy_abac) { true }
+ let(:create_cluster_request_body) { double('Google::Apis::ContainerV1::CreateClusterRequest') }
let(:operation) { double }
before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
- .to receive(:create_cluster).with(any_args, options: user_agent_options)
+ .to receive(:create_cluster).with(any_args)
.and_return(operation)
end
- it { is_expected.to eq(operation) }
-
it 'sets corresponded parameters' do
- expect_any_instance_of(Google::Apis::ContainerV1::CreateClusterRequest)
- .to receive(:initialize).with(
+ expect_any_instance_of(Google::Apis::ContainerV1::ContainerService)
+ .to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options)
+
+ expect(Google::Apis::ContainerV1::CreateClusterRequest)
+ .to receive(:new).with(
{
"cluster": {
"name": cluster_name,
@@ -96,9 +101,35 @@ describe GoogleApi::CloudPlatform::Client do
"enabled": true
}
}
- } )
+ } ).and_return(create_cluster_request_body)
+
+ expect(subject).to eq operation
+ end
+
+ context 'create without legacy_abac' do
+ let(:legacy_abac) { false }
+
+ it 'sets corresponded parameters' do
+ expect_any_instance_of(Google::Apis::ContainerV1::ContainerService)
+ .to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options)
+
+ expect(Google::Apis::ContainerV1::CreateClusterRequest)
+ .to receive(:new).with(
+ {
+ "cluster": {
+ "name": cluster_name,
+ "initial_node_count": cluster_size,
+ "node_config": {
+ "machine_type": machine_type
+ },
+ "legacy_abac": {
+ "enabled": false
+ }
+ }
+ } ).and_return(create_cluster_request_body)
- subject
+ expect(subject).to eq operation
+ end
end
end
diff --git a/spec/lib/json_web_token/hmac_token_spec.rb b/spec/lib/json_web_token/hmac_token_spec.rb
new file mode 100644
index 00000000000..f2cbc381967
--- /dev/null
+++ b/spec/lib/json_web_token/hmac_token_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+require 'json'
+require 'timecop'
+
+describe JSONWebToken::HMACToken do
+ let(:secret) { 'shh secret squirrel' }
+
+ shared_examples 'a valid, non-expired token' do
+ it 'is an Array with two elements' do
+ expect(decoded_token).to be_a(Array)
+ expect(decoded_token.count).to eq(2)
+ end
+
+ it 'contains the following keys in the first Array element Hash - jti, iat, nbf, exp' do
+ expect(decoded_token[0].keys).to include('jti', 'iat', 'nbf', 'exp')
+ end
+
+ it 'contains the following keys in the second Array element Hash - typ and alg' do
+ expect(decoded_token[1]['typ']).to eql('JWT')
+ expect(decoded_token[1]['alg']).to eql('HS256')
+ end
+ end
+
+ describe '.decode' do
+ let(:leeway) { described_class::IAT_LEEWAY }
+ let(:decoded_token) { described_class.decode(encoded_token, secret, leeway: leeway) }
+
+ context 'with an invalid token' do
+ context 'that is junk' do
+ let(:encoded_token) { 'junk' }
+
+ it "raises exception saying 'Not enough or too many segments'" do
+ expect { decoded_token }.to raise_error(JWT::DecodeError, 'Not enough or too many segments')
+ end
+ end
+
+ context 'that has been fiddled with' do
+ let(:encoded_token) do
+ described_class.new(secret).encoded.tap { |token| token[0] = 'E' }
+ end
+
+ it "raises exception saying 'Invalid segment encoding'" do
+ expect { decoded_token }.to raise_error(JWT::DecodeError, 'Invalid segment encoding')
+ end
+ end
+
+ context 'that was generated using a different secret' do
+ let(:encoded_token) { described_class.new('some other secret').encoded }
+
+ it "raises exception saying 'Signature verification raised" do
+ expect { decoded_token }.to raise_error(JWT::VerificationError, 'Signature verification raised')
+ end
+ end
+
+ context 'that is expired' do
+ # Needs the ! so Timecop.freeze() is effective
+ let!(:encoded_token) { described_class.new(secret).encoded }
+
+ it "raises exception saying 'Signature has expired'" do
+ # Needs to be 120 seconds, because the default expiry is 60 seconds
+ # with an additional 60 second leeway.
+ Timecop.freeze(Time.now + 120) do
+ expect { decoded_token }.to raise_error(JWT::ExpiredSignature, 'Signature has expired')
+ end
+ end
+ end
+ end
+
+ context 'with a valid token' do
+ let(:encoded_token) do
+ hmac_token = described_class.new(secret)
+ hmac_token.expire_time = Time.now + expire_time
+ hmac_token.encoded
+ end
+
+ context 'that has expired' do
+ let(:expire_time) { 0 }
+
+ context 'with the default leeway' do
+ Timecop.freeze(Time.now + 1) do
+ it_behaves_like 'a valid, non-expired token'
+ end
+ end
+
+ context 'with a leeway of 0 seconds' do
+ let(:leeway) { 0 }
+
+ it "raises exception saying 'Signature has expired'" do
+ Timecop.freeze(Time.now + 1) do
+ expect { decoded_token }.to raise_error(JWT::ExpiredSignature, 'Signature has expired')
+ end
+ end
+ end
+ end
+
+ context 'that has not expired' do
+ let(:expire_time) { described_class::DEFAULT_EXPIRE_TIME }
+
+ it_behaves_like 'a valid, non-expired token'
+ end
+ end
+ end
+
+ describe '#encoded' do
+ let(:decoded_token) { described_class.decode(encoded_token, secret) }
+
+ context 'without data' do
+ let(:encoded_token) { described_class.new(secret).encoded }
+
+ it_behaves_like 'a valid, non-expired token'
+ end
+
+ context 'with data' do
+ let(:data) { { secret_key: 'secret value' }.to_json }
+ let(:encoded_token) do
+ ec = described_class.new(secret)
+ ec[:data] = data
+ ec.encoded
+ end
+
+ it_behaves_like 'a valid, non-expired token'
+
+ it "contains the 'data' key in the first Array element Hash" do
+ expect(decoded_token[0]).to have_key('data')
+ end
+
+ it 'can re-read back the data' do
+ expect(decoded_token[0]['data']).to eql(data)
+ end
+ end
+ end
+end
diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb
index b7687d48c68..f18f97a9c6a 100644
--- a/spec/lib/mattermost/session_spec.rb
+++ b/spec/lib/mattermost/session_spec.rb
@@ -82,7 +82,7 @@ describe Mattermost::Session, type: :request do
.to_return(headers: { Authorization: 'token thisworksnow' }, status: 200)
end
- it 'can setup a session' do
+ it 'can set up a session' do
subject.with_session do |session|
end
@@ -106,7 +106,7 @@ describe Mattermost::Session, type: :request do
expect_to_obtain_exclusive_lease(lease_key, 'uuid')
expect_to_cancel_exclusive_lease(lease_key, 'uuid')
- # Cannot setup a session, but we should still cancel the lease
+ # Cannot set up a session, but we should still cancel the lease
expect { subject.with_session }.to raise_error(Mattermost::NoSessionError)
end
diff --git a/spec/lib/microsoft_teams/notifier_spec.rb b/spec/lib/microsoft_teams/notifier_spec.rb
index c9756544bd6..2aaa7c24ad8 100644
--- a/spec/lib/microsoft_teams/notifier_spec.rb
+++ b/spec/lib/microsoft_teams/notifier_spec.rb
@@ -48,7 +48,7 @@ describe MicrosoftTeams::Notifier do
stub_request(:post, webhook_url).with(body: JSON(body), headers: { 'Content-Type' => 'application/json' }).to_return(status: 200, body: "", headers: {})
end
- it 'expects to receive successfull answer' do
+ it 'expects to receive successful answer' do
expect(subject.ping(options)).to be true
end
end
diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb
index e0569218d78..1024e1a25ea 100644
--- a/spec/lib/object_storage/direct_upload_spec.rb
+++ b/spec/lib/object_storage/direct_upload_spec.rb
@@ -61,6 +61,8 @@ describe ObjectStorage::DirectUpload do
expect(subject[:GetURL]).to start_with(storage_url)
expect(subject[:StoreURL]).to start_with(storage_url)
expect(subject[:DeleteURL]).to start_with(storage_url)
+ expect(subject[:CustomPutHeaders]).to be_truthy
+ expect(subject[:PutHeaders]).to eq({})
end
end
@@ -81,6 +83,16 @@ describe ObjectStorage::DirectUpload do
expect(subject[:MultipartUpload][:AbortURL]).to start_with(storage_url)
expect(subject[:MultipartUpload][:AbortURL]).to include('uploadId=myUpload')
end
+
+ it 'uses only strings in query parameters' do
+ expect(direct_upload.send(:connection)).to receive(:signed_url).at_least(:once) do |params|
+ if params[:query]
+ expect(params[:query].keys.all? { |key| key.is_a?(String) }).to be_truthy
+ end
+ end
+
+ subject
+ end
end
shared_examples 'a valid upload without multipart data' do
diff --git a/spec/lib/quality/helm_client_spec.rb b/spec/lib/quality/helm_client_spec.rb
new file mode 100644
index 00000000000..7abb9688d5a
--- /dev/null
+++ b/spec/lib/quality/helm_client_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Quality::HelmClient do
+ let(:namespace) { 'review-apps-ee' }
+ let(:release_name) { 'my-release' }
+ let(:raw_helm_list_page1) do
+ <<~OUTPUT
+ {"Next":"review-6709-group-t40qbv",
+ "Releases":[
+ {"Name":"review-qa-60-reor-1mugd1", "Revision":1,"Updated":"Thu Oct 4 17:52:31 2018","Status":"FAILED", "Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-7846-fix-s-261vd6","Revision":1,"Updated":"Thu Oct 4 17:33:29 2018","Status":"FAILED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-7867-snowp-lzo3iy","Revision":1,"Updated":"Thu Oct 4 17:22:14 2018","Status":"DEPLOYED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-rename-geo-o4a780","Revision":1,"Updated":"Thu Oct 4 17:14:57 2018","Status":"DEPLOYED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-5781-opera-0k93fx","Revision":1,"Updated":"Thu Oct 4 17:06:15 2018","Status":"FAILED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-6709-group-2pzeec","Revision":1,"Updated":"Thu Oct 4 16:36:59 2018","Status":"FAILED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-ce-to-ee-2-l554mn","Revision":1,"Updated":"Thu Oct 4 16:27:02 2018","Status":"FAILED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-epics-e2e-m690eb","Revision":1,"Updated":"Thu Oct 4 16:08:26 2018","Status":"DEPLOYED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-7126-admin-06fae2","Revision":1,"Updated":"Thu Oct 4 15:56:35 2018","Status":"FAILED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"},
+ {"Name":"review-6983-promo-xyou11","Revision":1,"Updated":"Thu Oct 4 15:15:34 2018","Status":"FAILED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"}
+ ]}
+ OUTPUT
+ end
+ let(:raw_helm_list_page2) do
+ <<~OUTPUT
+ {"Releases":[
+ {"Name":"review-6709-group-t40qbv","Revision":1,"Updated":"Thu Oct 4 17:52:31 2018","Status":"FAILED","Chart":"gitlab-1.1.3","AppVersion":"master","Namespace":"#{namespace}"}
+ ]}
+ OUTPUT
+ end
+
+ subject { described_class.new(namespace: namespace) }
+
+ describe '#releases' do
+ it 'raises an error if the Helm command fails' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json)])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+
+ expect { subject.releases.to_a }.to raise_error(described_class::CommandFailedError)
+ end
+
+ it 'calls helm list with default arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json)])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ subject.releases.to_a
+ end
+
+ it 'calls helm list with extra arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json --deployed)])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ subject.releases(args: ['--deployed']).to_a
+ end
+
+ it 'returns a list of Release objects' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json --deployed)])
+ .and_return(Gitlab::Popen::Result.new([], raw_helm_list_page2, '', double(success?: true)))
+
+ releases = subject.releases(args: ['--deployed']).to_a
+
+ expect(releases.size).to eq(1)
+ expect(releases[0]).to have_attributes(
+ name: 'review-6709-group-t40qbv',
+ revision: 1,
+ last_update: Time.parse('Thu Oct 4 17:52:31 2018'),
+ status: 'FAILED',
+ chart: 'gitlab-1.1.3',
+ app_version: 'master',
+ namespace: namespace
+ )
+ end
+
+ it 'automatically paginates releases' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail).ordered
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json)])
+ .and_return(Gitlab::Popen::Result.new([], raw_helm_list_page1, '', double(success?: true)))
+ expect(Gitlab::Popen).to receive(:popen_with_detail).ordered
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json --offset review-6709-group-t40qbv)])
+ .and_return(Gitlab::Popen::Result.new([], raw_helm_list_page2, '', double(success?: true)))
+
+ releases = subject.releases.to_a
+
+ expect(releases.size).to eq(11)
+ expect(releases.last.name).to eq('review-6709-group-t40qbv')
+ end
+ end
+
+ describe '#delete' do
+ it 'raises an error if the Helm command fails' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+
+ expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
+ end
+
+ it 'calls helm delete with default arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ expect(subject.delete(release_name: release_name)).to eq('')
+ end
+ end
+end
diff --git a/spec/lib/quality/kubernetes_client_spec.rb b/spec/lib/quality/kubernetes_client_spec.rb
new file mode 100644
index 00000000000..f35d9464d48
--- /dev/null
+++ b/spec/lib/quality/kubernetes_client_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Quality::KubernetesClient do
+ let(:namespace) { 'review-apps-ee' }
+ let(:release_name) { 'my-release' }
+
+ subject { described_class.new(namespace: namespace) }
+
+ describe '#cleanup' do
+ it 'raises an error if the Kubernetes command fails' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl --namespace "#{namespace}" delete ) \
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
+ "--now -l release=\"#{release_name}\""])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+
+ expect { subject.cleanup(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
+ end
+
+ it 'calls kubectl with the correct arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl --namespace "#{namespace}" delete ) \
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
+ "--now -l release=\"#{release_name}\""])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ # We're not verifying the output here, just silencing it
+ expect { subject.cleanup(release_name: release_name) }.to output.to_stdout
+ end
+ end
+end
diff --git a/spec/lib/system_check/simple_executor_spec.rb b/spec/lib/system_check/simple_executor_spec.rb
index 9da3648400e..e71e9da369d 100644
--- a/spec/lib/system_check/simple_executor_spec.rb
+++ b/spec/lib/system_check/simple_executor_spec.rb
@@ -98,15 +98,6 @@ describe SystemCheck::SimpleExecutor do
end
end
- before do
- @rainbow = Rainbow.enabled
- Rainbow.enabled = false
- end
-
- after do
- Rainbow.enabled = @rainbow
- end
-
describe '#component' do
it 'returns stored component name' do
expect(subject.component).to eq('Test')
diff --git a/spec/mailers/emails/auto_devops_spec.rb b/spec/mailers/emails/auto_devops_spec.rb
new file mode 100644
index 00000000000..839caf3f50e
--- /dev/null
+++ b/spec/mailers/emails/auto_devops_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Emails::AutoDevops do
+ include EmailSpec::Matchers
+
+ describe '#auto_devops_disabled_email' do
+ let(:owner) { create(:user) }
+ let(:namespace) { create(:namespace, owner: owner) }
+ let(:project) { create(:project, :repository, :auto_devops) }
+ let(:pipeline) { create(:ci_pipeline, :failed, project: project) }
+
+ subject { Notify.autodevops_disabled_email(pipeline, owner.email) }
+
+ it 'sents email with correct subject' do
+ is_expected.to have_subject("#{project.name} | Auto DevOps pipeline was disabled for #{project.name}")
+ end
+
+ it 'sents an email to the user' do
+ recipient = subject.header[:to].addrs.map(&:address).first
+
+ expect(recipient).to eq(owner.email)
+ end
+
+ it 'is sent as GitLab email' do
+ sender = subject.header[:from].addrs[0].address
+
+ expect(sender).to match(/gitlab/)
+ end
+ end
+end
diff --git a/spec/migrations/add_pages_access_level_to_project_feature_spec.rb b/spec/migrations/add_pages_access_level_to_project_feature_spec.rb
new file mode 100644
index 00000000000..3946602c5be
--- /dev/null
+++ b/spec/migrations/add_pages_access_level_to_project_feature_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180423204600_add_pages_access_level_to_project_feature.rb')
+
+describe AddPagesAccessLevelToProjectFeature, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:features) { table(:project_features) }
+ let!(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab') }
+ let!(:first_project) { projects.create(name: 'gitlab1', path: 'gitlab1', namespace_id: namespace.id) }
+ let!(:first_project_features) { features.create(project_id: first_project.id) }
+ let!(:second_project) { projects.create(name: 'gitlab2', path: 'gitlab2', namespace_id: namespace.id) }
+ let!(:second_project_features) { features.create(project_id: second_project.id) }
+
+ it 'correctly migrate pages for old projects to be public' do
+ migrate!
+
+ # For old projects pages should be public
+ expect(first_project_features.reload.pages_access_level).to eq ProjectFeature::PUBLIC
+ expect(second_project_features.reload.pages_access_level).to eq ProjectFeature::PUBLIC
+ end
+
+ it 'after migration pages are enabled as default' do
+ migrate!
+
+ # For new project default is enabled
+ third_project = projects.create(name: 'gitlab3', path: 'gitlab3', namespace_id: namespace.id)
+ third_project_features = features.create(project_id: third_project.id)
+ expect(third_project_features.reload.pages_access_level).to eq ProjectFeature::ENABLED
+ end
+end
diff --git a/spec/migrations/delete_inconsistent_internal_id_records_spec.rb b/spec/migrations/delete_inconsistent_internal_id_records_spec.rb
new file mode 100644
index 00000000000..4af51217031
--- /dev/null
+++ b/spec/migrations/delete_inconsistent_internal_id_records_spec.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+# rubocop:disable RSpec/FactoriesInMigrationSpecs
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180723130817_delete_inconsistent_internal_id_records.rb')
+
+describe DeleteInconsistentInternalIdRecords, :migration do
+ let!(:project1) { create(:project) }
+ let!(:project2) { create(:project) }
+ let!(:project3) { create(:project) }
+
+ let(:internal_id_query) { ->(project) { InternalId.where(usage: InternalId.usages[scope.to_s.tableize], project: project) } }
+
+ let(:create_models) do
+ 3.times { create(scope, project: project1) }
+ 3.times { create(scope, project: project2) }
+ 3.times { create(scope, project: project3) }
+ end
+
+ shared_examples_for 'deleting inconsistent internal_id records' do
+ before do
+ create_models
+
+ internal_id_query.call(project1).first.tap do |iid|
+ iid.last_value = iid.last_value - 2
+ # This is an inconsistent record
+ iid.save!
+ end
+
+ internal_id_query.call(project3).first.tap do |iid|
+ iid.last_value = iid.last_value + 2
+ # This is a consistent record
+ iid.save!
+ end
+ end
+
+ it "deletes inconsistent issues" do
+ expect { migrate! }.to change { internal_id_query.call(project1).size }.from(1).to(0)
+ end
+
+ it "retains consistent issues" do
+ expect { migrate! }.not_to change { internal_id_query.call(project2).size }
+ end
+
+ it "retains consistent records, especially those with a greater last_value" do
+ expect { migrate! }.not_to change { internal_id_query.call(project3).size }
+ end
+ end
+
+ context 'for issues' do
+ let(:scope) { :issue }
+ it_behaves_like 'deleting inconsistent internal_id records'
+ end
+
+ context 'for merge_requests' do
+ let(:scope) { :merge_request }
+
+ let(:create_models) do
+ 3.times { |i| create(scope, target_project: project1, source_project: project1, source_branch: i.to_s) }
+ 3.times { |i| create(scope, target_project: project2, source_project: project2, source_branch: i.to_s) }
+ 3.times { |i| create(scope, target_project: project3, source_project: project3, source_branch: i.to_s) }
+ end
+
+ it_behaves_like 'deleting inconsistent internal_id records'
+ end
+
+ context 'for deployments' do
+ let(:scope) { :deployment }
+ let(:deployments) { table(:deployments) }
+ let(:internal_ids) { table(:internal_ids) }
+
+ before do
+ internal_ids.create!(project_id: project1.id, usage: 2, last_value: 2)
+ internal_ids.create!(project_id: project2.id, usage: 2, last_value: 2)
+ internal_ids.create!(project_id: project3.id, usage: 2, last_value: 2)
+ end
+
+ let(:create_models) do
+ 3.times { |i| deployments.create!(project_id: project1.id, iid: i, environment_id: 1, ref: 'master', sha: 'a', tag: false) }
+ 3.times { |i| deployments.create!(project_id: project2.id, iid: i, environment_id: 1, ref: 'master', sha: 'a', tag: false) }
+ 3.times { |i| deployments.create!(project_id: project3.id, iid: i, environment_id: 1, ref: 'master', sha: 'a', tag: false) }
+ end
+
+ it_behaves_like 'deleting inconsistent internal_id records'
+ end
+
+ context 'for milestones (by project)' do
+ let(:scope) { :milestone }
+ it_behaves_like 'deleting inconsistent internal_id records'
+ end
+
+ context 'for ci_pipelines' do
+ let(:scope) { :ci_pipeline }
+ it_behaves_like 'deleting inconsistent internal_id records'
+ end
+
+ context 'for milestones (by group)' do
+ # milestones (by group) is a little different than all of the other models
+ let!(:group1) { create(:group) }
+ let!(:group2) { create(:group) }
+ let!(:group3) { create(:group) }
+
+ let(:internal_id_query) { ->(group) { InternalId.where(usage: InternalId.usages['milestones'], namespace: group) } }
+
+ before do
+ 3.times { create(:milestone, group: group1) }
+ 3.times { create(:milestone, group: group2) }
+ 3.times { create(:milestone, group: group3) }
+
+ internal_id_query.call(group1).first.tap do |iid|
+ iid.last_value = iid.last_value - 2
+ # This is an inconsistent record
+ iid.save!
+ end
+
+ internal_id_query.call(group3).first.tap do |iid|
+ iid.last_value = iid.last_value + 2
+ # This is a consistent record
+ iid.save!
+ end
+ end
+
+ it "deletes inconsistent issues" do
+ expect { migrate! }.to change { internal_id_query.call(group1).size }.from(1).to(0)
+ end
+
+ it "retains consistent issues" do
+ expect { migrate! }.not_to change { internal_id_query.call(group2).size }
+ end
+
+ it "retains consistent records, especially those with a greater last_value" do
+ expect { migrate! }.not_to change { internal_id_query.call(group3).size }
+ end
+ end
+end
diff --git a/spec/migrations/drop_duplicate_protected_tags_spec.rb b/spec/migrations/drop_duplicate_protected_tags_spec.rb
new file mode 100644
index 00000000000..acfb6850722
--- /dev/null
+++ b/spec/migrations/drop_duplicate_protected_tags_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180711103851_drop_duplicate_protected_tags.rb')
+
+describe DropDuplicateProtectedTags, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:protected_tags) { table(:protected_tags) }
+
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 2)
+
+ namespaces.create(id: 1, name: 'gitlab-org', path: 'gitlab-org')
+ projects.create!(id: 1, namespace_id: 1, name: 'gitlab1', path: 'gitlab1')
+ projects.create!(id: 2, namespace_id: 1, name: 'gitlab2', path: 'gitlab2')
+ end
+
+ it 'removes duplicated protected tags' do
+ protected_tags.create!(id: 1, project_id: 1, name: 'foo')
+ tag2 = protected_tags.create!(id: 2, project_id: 1, name: 'foo1')
+ protected_tags.create!(id: 3, project_id: 1, name: 'foo')
+ tag4 = protected_tags.create!(id: 4, project_id: 1, name: 'foo')
+ tag5 = protected_tags.create!(id: 5, project_id: 2, name: 'foo')
+
+ migrate!
+
+ expect(protected_tags.all.count).to eq 3
+ expect(protected_tags.all.pluck(:id)).to contain_exactly(tag2.id, tag4.id, tag5.id)
+ end
+
+ it 'does not remove unique protected tags' do
+ tag1 = protected_tags.create!(id: 1, project_id: 1, name: 'foo1')
+ tag2 = protected_tags.create!(id: 2, project_id: 1, name: 'foo2')
+ tag3 = protected_tags.create!(id: 3, project_id: 1, name: 'foo3')
+
+ migrate!
+
+ expect(protected_tags.all.count).to eq 3
+ expect(protected_tags.all.pluck(:id)).to contain_exactly(tag1.id, tag2.id, tag3.id)
+ end
+end
diff --git a/spec/migrations/enqueue_redact_links_spec.rb b/spec/migrations/enqueue_redact_links_spec.rb
new file mode 100644
index 00000000000..a5da76977b7
--- /dev/null
+++ b/spec/migrations/enqueue_redact_links_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20181014121030_enqueue_redact_links.rb')
+
+describe EnqueueRedactLinks, :migration, :sidekiq do
+ let(:merge_requests) { table(:merge_requests) }
+ let(:issues) { table(:issues) }
+ let(:notes) { table(:notes) }
+ let(:projects) { table(:projects) }
+ let(:namespaces) { table(:namespaces) }
+ let(:snippets) { table(:snippets) }
+ let(:users) { table(:users) }
+ let(:user) { users.create!(email: 'test@example.com', projects_limit: 100, username: 'test') }
+
+ before do
+ stub_const("#{described_class.name}::BATCH_SIZE", 1)
+
+ text = 'some text /sent_notifications/00000000000000000000000000000000/unsubscribe more text'
+ group = namespaces.create!(name: 'gitlab', path: 'gitlab')
+ project = projects.create!(namespace_id: group.id)
+
+ merge_requests.create!(id: 1, target_project_id: project.id, source_project_id: project.id, target_branch: 'feature', source_branch: 'master', description: text)
+ issues.create!(id: 1, description: text)
+ notes.create!(id: 1, note: text)
+ notes.create!(id: 2, note: text)
+ snippets.create!(id: 1, description: text, author_id: user.id)
+ end
+
+ it 'correctly schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ migrate!
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, "Note", "note", 1, 1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, "Note", "note", 2, 2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, "Issue", "description", 1, 1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, "MergeRequest", "description", 1, 1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, "Snippet", "description", 1, 1)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 5
+ end
+ end
+ end
+end
diff --git a/spec/migrations/fill_empty_finished_at_in_deployments_spec.rb b/spec/migrations/fill_empty_finished_at_in_deployments_spec.rb
new file mode 100644
index 00000000000..cf5c10f77e1
--- /dev/null
+++ b/spec/migrations/fill_empty_finished_at_in_deployments_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20181030135124_fill_empty_finished_at_in_deployments')
+
+describe FillEmptyFinishedAtInDeployments, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:environments) { table(:environments) }
+ let(:deployments) { table(:deployments) }
+
+ context 'when a deployment row does not have a value on finished_at' do
+ context 'when a deployment succeeded' do
+ before do
+ namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
+ projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1', namespace_id: 123)
+ environments.create!(id: 1, name: 'production', slug: 'production', project_id: 1)
+ deployments.create!(id: 1, iid: 1, project_id: 1, environment_id: 1, ref: 'master', sha: 'xxx', tag: false)
+ end
+
+ it 'correctly replicates finished_at by created_at' do
+ expect(deployments.last.created_at).not_to be_nil
+ expect(deployments.last.finished_at).to be_nil
+
+ migrate!
+
+ expect(deployments.last.created_at).not_to be_nil
+ expect(deployments.last.finished_at).to eq(deployments.last.created_at)
+ end
+ end
+
+ context 'when a deployment is running' do
+ before do
+ namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
+ projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1', namespace_id: 123)
+ environments.create!(id: 1, name: 'production', slug: 'production', project_id: 1)
+ deployments.create!(id: 1, iid: 1, project_id: 1, environment_id: 1, ref: 'master', sha: 'xxx', tag: false, status: 1)
+ end
+
+ it 'does not fill finished_at' do
+ expect(deployments.last.created_at).not_to be_nil
+ expect(deployments.last.finished_at).to be_nil
+
+ migrate!
+
+ expect(deployments.last.created_at).not_to be_nil
+ expect(deployments.last.finished_at).to be_nil
+ end
+ end
+ end
+
+ context 'when a deployment row does has a value on finished_at' do
+ let(:finished_at) { '2018-10-30 11:12:02 UTC' }
+
+ before do
+ namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
+ projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1', namespace_id: 123)
+ environments.create!(id: 1, name: 'production', slug: 'production', project_id: 1)
+ deployments.create!(id: 1, iid: 1, project_id: 1, environment_id: 1, ref: 'master', sha: 'xxx', tag: false, finished_at: finished_at)
+ end
+
+ it 'does not affect existing value' do
+ expect(deployments.last.created_at).not_to be_nil
+ expect(deployments.last.finished_at).not_to be_nil
+
+ migrate!
+
+ expect(deployments.last.created_at).not_to be_nil
+ expect(deployments.last.finished_at).to eq(finished_at)
+ end
+ end
+end
diff --git a/spec/migrations/import_common_metrics_spec.rb b/spec/migrations/import_common_metrics_spec.rb
new file mode 100644
index 00000000000..1001629007c
--- /dev/null
+++ b/spec/migrations/import_common_metrics_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180831164910_import_common_metrics.rb')
+
+describe ImportCommonMetrics, :migration do
+ describe '#up' do
+ it "imports all prometheus metrics" do
+ expect(PrometheusMetric.common).to be_empty
+
+ migrate!
+
+ expect(PrometheusMetric.common).not_to be_empty
+ end
+ end
+end
diff --git a/spec/migrations/migrate_legacy_artifacts_to_job_artifacts_spec.rb b/spec/migrations/migrate_legacy_artifacts_to_job_artifacts_spec.rb
new file mode 100644
index 00000000000..df82672f254
--- /dev/null
+++ b/spec/migrations/migrate_legacy_artifacts_to_job_artifacts_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180816161409_migrate_legacy_artifacts_to_job_artifacts.rb')
+
+describe MigrateLegacyArtifactsToJobArtifacts, :migration, :sidekiq do
+ let(:migration_class) { Gitlab::BackgroundMigration::MigrateLegacyArtifacts }
+ let(:migration_name) { migration_class.to_s.demodulize }
+
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:jobs) { table(:ci_builds) }
+ let(:job_artifacts) { table(:ci_job_artifacts) }
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create!(name: 'gitlab', path: 'gitlab-ce', namespace_id: namespace.id) }
+ let(:pipeline) { pipelines.create!(project_id: project.id, ref: 'master', sha: 'adf43c3a') }
+ let(:archive_file_type) { Gitlab::BackgroundMigration::MigrateLegacyArtifacts::ARCHIVE_FILE_TYPE }
+ let(:metadata_file_type) { Gitlab::BackgroundMigration::MigrateLegacyArtifacts::METADATA_FILE_TYPE }
+ let(:local_store) { ::ObjectStorage::Store::LOCAL }
+ let(:remote_store) { ::ObjectStorage::Store::REMOTE }
+ let(:legacy_location) { Gitlab::BackgroundMigration::MigrateLegacyArtifacts::LEGACY_PATH_FILE_LOCATION }
+
+ context 'when legacy artifacts exist' do
+ before do
+ jobs.create!(id: 1, commit_id: pipeline.id, project_id: project.id, status: :success, artifacts_file: 'archive.zip')
+ jobs.create!(id: 2, commit_id: pipeline.id, project_id: project.id, status: :failed, artifacts_metadata: 'metadata.gz')
+ jobs.create!(id: 3, commit_id: pipeline.id, project_id: project.id, status: :failed, artifacts_file: 'archive.zip', artifacts_metadata: 'metadata.gz')
+ jobs.create!(id: 4, commit_id: pipeline.id, project_id: project.id, status: :running)
+ jobs.create!(id: 5, commit_id: pipeline.id, project_id: project.id, status: :success, artifacts_file: 'archive.zip', artifacts_file_store: remote_store, artifacts_metadata: 'metadata.gz')
+ jobs.create!(id: 6, commit_id: pipeline.id, project_id: project.id, status: :failed, artifacts_file: 'archive.zip', artifacts_metadata: 'metadata.gz')
+ end
+
+ it 'schedules a background migration' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ migrate!
+
+ expect(migration_name).to be_scheduled_delayed_migration(5.minutes, 1, 6)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 1
+ end
+ end
+ end
+
+ it 'migrates legacy artifacts to ci_job_artifacts table' do
+ migrate!
+
+ expect(job_artifacts.order(:job_id, :file_type).pluck('project_id, job_id, file_type, file_store, size, expire_at, file, file_sha256, file_location'))
+ .to eq([[project.id, 1, archive_file_type, local_store, nil, nil, 'archive.zip', nil, legacy_location],
+ [project.id, 3, archive_file_type, local_store, nil, nil, 'archive.zip', nil, legacy_location],
+ [project.id, 3, metadata_file_type, local_store, nil, nil, 'metadata.gz', nil, legacy_location],
+ [project.id, 5, archive_file_type, remote_store, nil, nil, 'archive.zip', nil, legacy_location],
+ [project.id, 5, metadata_file_type, local_store, nil, nil, 'metadata.gz', nil, legacy_location],
+ [project.id, 6, archive_file_type, local_store, nil, nil, 'archive.zip', nil, legacy_location],
+ [project.id, 6, metadata_file_type, local_store, nil, nil, 'metadata.gz', nil, legacy_location]])
+ end
+ end
+
+ context 'when legacy artifacts do not exist' do
+ before do
+ jobs.create!(id: 1, commit_id: pipeline.id, project_id: project.id, status: :success)
+ jobs.create!(id: 2, commit_id: pipeline.id, project_id: project.id, status: :failed, artifacts_metadata: 'metadata.gz')
+ end
+
+ it 'does not schedule background migrations' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ migrate!
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/migrate_null_wiki_access_levels_spec.rb b/spec/migrations/migrate_null_wiki_access_levels_spec.rb
new file mode 100644
index 00000000000..f99273072a2
--- /dev/null
+++ b/spec/migrations/migrate_null_wiki_access_levels_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180809195358_migrate_null_wiki_access_levels.rb')
+
+describe MigrateNullWikiAccessLevels, :migration do
+ let(:namespaces) { table('namespaces') }
+ let(:projects) { table(:projects) }
+ let(:project_features) { table(:project_features) }
+ let(:migration) { described_class.new }
+
+ before do
+ namespace = namespaces.create(name: 'foo', path: 'foo')
+
+ projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1', namespace_id: namespace.id)
+ projects.create!(id: 2, name: 'gitlab2', path: 'gitlab2', namespace_id: namespace.id)
+ projects.create!(id: 3, name: 'gitlab3', path: 'gitlab3', namespace_id: namespace.id)
+
+ project_features.create!(id: 1, project_id: 1, wiki_access_level: nil)
+ project_features.create!(id: 2, project_id: 2, wiki_access_level: 10)
+ project_features.create!(id: 3, project_id: 3, wiki_access_level: 20)
+ end
+
+ describe '#up' do
+ it 'migrates existing project_features with wiki_access_level NULL to 20' do
+ expect { migration.up }.to change { project_features.where(wiki_access_level: 20).count }.by(1)
+ end
+ end
+end
diff --git a/spec/migrations/migrate_old_artifacts_spec.rb b/spec/migrations/migrate_old_artifacts_spec.rb
index 4187ab149a5..af77d64fdbf 100644
--- a/spec/migrations/migrate_old_artifacts_spec.rb
+++ b/spec/migrations/migrate_old_artifacts_spec.rb
@@ -76,7 +76,7 @@ describe MigrateOldArtifacts do
end
end
- context 'when there are aritfacts in old and new directory' do
+ context 'when there are artifacts in old and new directory' do
before do
store_artifacts_in_legacy_path(build2)
diff --git a/spec/migrations/remove_orphaned_label_links_spec.rb b/spec/migrations/remove_orphaned_label_links_spec.rb
new file mode 100644
index 00000000000..13b8919343e
--- /dev/null
+++ b/spec/migrations/remove_orphaned_label_links_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180906051323_remove_orphaned_label_links.rb')
+
+describe RemoveOrphanedLabelLinks, :migration do
+ let(:label_links) { table(:label_links) }
+ let(:labels) { table(:labels) }
+
+ let(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ let(:label) { create_label }
+
+ context 'add foreign key on label_id' do
+ let!(:label_link_with_label) { create_label_link(label_id: label.id) }
+ let!(:label_link_without_label) { create_label_link(label_id: nil) }
+
+ it 'removes orphaned labels without corresponding label' do
+ expect { migrate! }.to change { LabelLink.count }.from(2).to(1)
+ end
+
+ it 'does not remove entries with valid label_id' do
+ expect { migrate! }.not_to change { label_link_with_label.reload }
+ end
+ end
+
+ def create_label(**opts)
+ labels.create!(
+ project_id: project.id,
+ **opts
+ )
+ end
+
+ def create_label_link(**opts)
+ label_links.create!(
+ target_id: 1,
+ target_type: 'Issue',
+ **opts
+ )
+ end
+end
diff --git a/spec/migrations/rename_more_reserved_project_names_spec.rb b/spec/migrations/rename_more_reserved_project_names_spec.rb
index 034e8a6a4e5..baf16c2ce53 100644
--- a/spec/migrations/rename_more_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_more_reserved_project_names_spec.rb
@@ -31,7 +31,16 @@ describe RenameMoreReservedProjectNames, :delete do
context 'when exception is raised during rename' do
before do
- allow(project).to receive(:rename_repo).and_raise(StandardError)
+ service = instance_double('service')
+
+ allow(service)
+ .to receive(:execute)
+ .and_raise(Projects::AfterRenameService::RenameFailedError)
+
+ allow(Projects::AfterRenameService)
+ .to receive(:new)
+ .with(project)
+ .and_return(service)
end
it 'captures exception from project rename' do
diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb
index 592ac2b5fb9..7818aa0d560 100644
--- a/spec/migrations/rename_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_reserved_project_names_spec.rb
@@ -35,7 +35,16 @@ describe RenameReservedProjectNames, :migration, schema: :latest do
context 'when exception is raised during rename' do
before do
- allow(project).to receive(:rename_repo).and_raise(StandardError)
+ service = instance_double('service')
+
+ allow(service)
+ .to receive(:execute)
+ .and_raise(Projects::AfterRenameService::RenameFailedError)
+
+ allow(Projects::AfterRenameService)
+ .to receive(:new)
+ .with(project)
+ .and_return(service)
end
it 'captures exception from project rename' do
diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
index 96bef107599..c4427910518 100644
--- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
+++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
@@ -6,7 +6,7 @@ describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do
create(:gpg_key, id: 1, key: GpgHelpers::User1.public_key) # rubocop:disable RSpec/FactoriesInMigrationSpecs
create(:gpg_key, id: 2, key: GpgHelpers::User3.public_key) # rubocop:disable RSpec/FactoriesInMigrationSpecs
# Delete all subkeys so they can be recreated
- GpgKeySubkey.destroy_all
+ GpgKeySubkey.destroy_all # rubocop: disable DestroyAll
end
it 'correctly schedules background migrations' do
diff --git a/spec/migrations/schedule_digest_personal_access_tokens_spec.rb b/spec/migrations/schedule_digest_personal_access_tokens_spec.rb
new file mode 100644
index 00000000000..6d155f78342
--- /dev/null
+++ b/spec/migrations/schedule_digest_personal_access_tokens_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180913142237_schedule_digest_personal_access_tokens.rb')
+
+describe ScheduleDigestPersonalAccessTokens, :migration, :sidekiq do
+ let(:personal_access_tokens) { table(:personal_access_tokens) }
+ let(:users) { table(:users) }
+
+ before do
+ stub_const("#{described_class.name}::BATCH_SIZE", 4)
+
+ users.create(id: 1, email: 'user@example.com', projects_limit: 10)
+
+ personal_access_tokens.create!(id: 1, user_id: 1, name: 'pat-01', token: 'token-01')
+ personal_access_tokens.create!(id: 2, user_id: 1, name: 'pat-02', token: 'token-02')
+ personal_access_tokens.create!(id: 3, user_id: 1, name: 'pat-03', token_digest: 'token_digest')
+ personal_access_tokens.create!(id: 4, user_id: 1, name: 'pat-04', token: 'token-04')
+ personal_access_tokens.create!(id: 5, user_id: 1, name: 'pat-05', token: 'token-05')
+ personal_access_tokens.create!(id: 6, user_id: 1, name: 'pat-06', token: 'token-06')
+ end
+
+ it 'correctly schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ migrate!
+
+ expect(described_class::MIGRATION).to(
+ be_scheduled_delayed_migration(
+ 5.minutes, 'PersonalAccessToken', 'token', 'token_digest', 1, 5))
+ expect(described_class::MIGRATION).to(
+ be_scheduled_delayed_migration(
+ 10.minutes, 'PersonalAccessToken', 'token', 'token_digest', 6, 6))
+ expect(BackgroundMigrationWorker.jobs.size).to eq 2
+ end
+ end
+
+ it 'schedules background migrations' do
+ perform_enqueued_jobs do
+ plain_text_token = 'token IS NOT NULL'
+
+ expect(personal_access_tokens.where(plain_text_token).count).to eq 5
+
+ migrate!
+
+ expect(personal_access_tokens.where(plain_text_token).count).to eq 0
+ end
+ end
+end
diff --git a/spec/migrations/steal_fill_store_upload_spec.rb b/spec/migrations/steal_fill_store_upload_spec.rb
new file mode 100644
index 00000000000..ed809baf2b5
--- /dev/null
+++ b/spec/migrations/steal_fill_store_upload_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20181105201455_steal_fill_store_upload.rb')
+
+describe StealFillStoreUpload, :migration do
+ let(:uploads) { table(:uploads) }
+
+ describe '#up' do
+ it 'steals the FillStoreUpload background migration' do
+ expect(Gitlab::BackgroundMigration).to receive(:steal).with('FillStoreUpload').and_call_original
+
+ migrate!
+ end
+
+ it 'does not run migration if not needed' do
+ uploads.create(size: 100.kilobytes,
+ uploader: 'AvatarUploader',
+ path: 'uploads/-/system/avatar.jpg',
+ store: 1)
+
+ expect_any_instance_of(Gitlab::BackgroundMigration::FillStoreUpload).not_to receive(:perform)
+
+ migrate!
+ end
+
+ it 'ensures all rows are migrated' do
+ uploads.create(size: 100.kilobytes,
+ uploader: 'AvatarUploader',
+ path: 'uploads/-/system/avatar.jpg',
+ store: nil)
+
+ expect_any_instance_of(Gitlab::BackgroundMigration::FillStoreUpload).to receive(:perform).and_call_original
+
+ expect do
+ migrate!
+ end.to change { uploads.where(store: nil).count }.from(1).to(0)
+ end
+ end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 02f74e2ea54..96aa9a82b71 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -25,6 +25,9 @@ describe ApplicationSetting do
it { is_expected.to allow_value(https).for(:after_sign_out_path) }
it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) }
+ it { is_expected.to allow_value("dev.gitlab.com").for(:commit_email_hostname) }
+ it { is_expected.not_to allow_value("@dev.gitlab").for(:commit_email_hostname) }
+
describe 'default_artifacts_expire_in' do
it 'sets an error if it cannot parse' do
setting.update(default_artifacts_expire_in: 'a')
@@ -107,6 +110,14 @@ describe ApplicationSetting do
it { expect(setting.repository_storages).to eq(['default']) }
end
+ context '#commit_email_hostname' do
+ it 'returns configured gitlab hostname if commit_email_hostname is not defined' do
+ setting.update(commit_email_hostname: nil)
+
+ expect(setting.commit_email_hostname).to eq("users.noreply.#{Gitlab.config.gitlab.host}")
+ end
+ end
+
context 'auto_devops_domain setting' do
context 'when auto_devops_enabled? is true' do
before do
@@ -141,19 +152,6 @@ describe ApplicationSetting do
end
end
- context 'circuitbreaker settings' do
- [:circuitbreaker_failure_count_threshold,
- :circuitbreaker_check_interval,
- :circuitbreaker_failure_reset_time,
- :circuitbreaker_storage_timeout].each do |field|
- it "Validates #{field} as number" do
- is_expected.to validate_numericality_of(field)
- .only_integer
- .is_greater_than_or_equal_to(0)
- end
- end
- end
-
context 'repository storages' do
before do
storages = {
@@ -305,6 +303,36 @@ describe ApplicationSetting do
end
end
+ describe 'setting Sentry DSNs' do
+ context 'server DSN' do
+ it 'strips leading and trailing whitespace' do
+ subject.update(sentry_dsn: ' http://test ')
+
+ expect(subject.sentry_dsn).to eq('http://test')
+ end
+
+ it 'handles nil values' do
+ subject.update(sentry_dsn: nil)
+
+ expect(subject.sentry_dsn).to be_nil
+ end
+ end
+
+ context 'client-side DSN' do
+ it 'strips leading and trailing whitespace' do
+ subject.update(clientside_sentry_dsn: ' http://test ')
+
+ expect(subject.clientside_sentry_dsn).to eq('http://test')
+ end
+
+ it 'handles nil values' do
+ subject.update(clientside_sentry_dsn: nil)
+
+ expect(subject.clientside_sentry_dsn).to be_nil
+ end
+ end
+ end
+
describe '#disabled_oauth_sign_in_sources=' do
before do
allow(Devise).to receive(:omniauth_providers).and_return([:github])
@@ -538,4 +566,63 @@ describe ApplicationSetting do
expect(setting.allow_signup?).to be_falsey
end
end
+
+ describe '#user_default_internal_regex_enabled?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_default_external, :user_default_internal_regex, :result) do
+ false | nil | false
+ false | '' | false
+ false | '^(?:(?!\.ext@).)*$\r?\n?' | false
+ true | '' | false
+ true | nil | false
+ true | '^(?:(?!\.ext@).)*$\r?\n?' | true
+ end
+
+ with_them do
+ before do
+ setting.update(user_default_external: user_default_external)
+ setting.update(user_default_internal_regex: user_default_internal_regex)
+ end
+
+ subject { setting.user_default_internal_regex_enabled? }
+
+ it { is_expected.to eq(result) }
+ end
+ end
+
+ context 'diff limit settings' do
+ describe '#diff_max_patch_bytes' do
+ context 'validations' do
+ it { is_expected.to validate_presence_of(:diff_max_patch_bytes) }
+
+ it do
+ is_expected.to validate_numericality_of(:diff_max_patch_bytes)
+ .only_integer
+ .is_greater_than_or_equal_to(Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES)
+ .is_less_than_or_equal_to(Gitlab::Git::Diff::MAX_PATCH_BYTES_UPPER_BOUND)
+ end
+ end
+ end
+ end
+
+ describe '#archive_builds_older_than' do
+ subject { setting.archive_builds_older_than }
+
+ context 'when the archive_builds_in_seconds is set' do
+ before do
+ setting.archive_builds_in_seconds = 3600
+ end
+
+ it { is_expected.to be_within(1.minute).of(1.hour.ago) }
+ end
+
+ context 'when the archive_builds_in_seconds is set' do
+ before do
+ setting.archive_builds_in_seconds = nil
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
end
diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb
index b909e04dfc3..3f52091698c 100644
--- a/spec/models/award_emoji_spec.rb
+++ b/spec/models/award_emoji_spec.rb
@@ -77,4 +77,27 @@ describe AwardEmoji do
end
end
end
+
+ describe '.award_counts_for_user' do
+ let(:user) { create(:user) }
+
+ before do
+ create(:award_emoji, user: user, name: 'thumbsup')
+ create(:award_emoji, user: user, name: 'thumbsup')
+ create(:award_emoji, user: user, name: 'thumbsdown')
+ create(:award_emoji, user: user, name: '+1')
+ end
+
+ it 'returns the awarded emoji in descending order' do
+ awards = described_class.award_counts_for_user(user)
+
+ expect(awards).to eq('thumbsup' => 2, 'thumbsdown' => 1, '+1' => 1)
+ end
+
+ it 'limits the returned number of rows' do
+ awards = described_class.award_counts_for_user(user, 1)
+
+ expect(awards).to eq('thumbsup' => 2)
+ end
+ end
end
diff --git a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb
index bed364a8c14..01c555a7a90 100644
--- a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb
+++ b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb
@@ -2,22 +2,24 @@ require 'spec_helper'
describe BlobViewer::GitlabCiYml do
include FakeBlobHelpers
+ include RepoHelpers
- let(:project) { build_stubbed(:project) }
+ let(:project) { create(:project, :repository) }
let(:data) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
let(:blob) { fake_blob(path: '.gitlab-ci.yml', data: data) }
+ let(:sha) { sample_commit.id }
subject { described_class.new(blob) }
describe '#validation_message' do
it 'calls prepare! on the viewer' do
expect(subject).to receive(:prepare!)
- subject.validation_message
+ subject.validation_message(project, sha)
end
context 'when the configuration is valid' do
it 'returns nil' do
- expect(subject.validation_message).to be_nil
+ expect(subject.validation_message(project, sha)).to be_nil
end
end
@@ -25,7 +27,7 @@ describe BlobViewer::GitlabCiYml do
let(:data) { 'oof' }
it 'returns the error message' do
- expect(subject.validation_message).to eq('Invalid configuration format')
+ expect(subject.validation_message(project, sha)).to eq('Invalid configuration format')
end
end
end
diff --git a/spec/models/blob_viewer/package_json_spec.rb b/spec/models/blob_viewer/package_json_spec.rb
index 5ed2f4400bc..fbaa8d47a71 100644
--- a/spec/models/blob_viewer/package_json_spec.rb
+++ b/spec/models/blob_viewer/package_json_spec.rb
@@ -40,13 +40,14 @@ describe BlobViewer::PackageJson do
end
context 'when package.json has "private": true' do
+ let(:homepage) { 'http://example.com' }
let(:data) do
<<-SPEC.strip_heredoc
{
"name": "module-name",
"version": "10.3.1",
"private": true,
- "homepage": "myawesomepackage.com"
+ "homepage": #{homepage.to_json}
}
SPEC
end
@@ -54,10 +55,22 @@ describe BlobViewer::PackageJson do
subject { described_class.new(blob) }
describe '#package_url' do
- it 'returns homepage if any' do
- expect(subject).to receive(:prepare!)
+ context 'when the homepage has a valid URL' do
+ it 'returns homepage URL' do
+ expect(subject).to receive(:prepare!)
+
+ expect(subject.package_url).to eq(homepage)
+ end
+ end
+
+ context 'when the homepage has an invalid URL' do
+ let(:homepage) { 'javascript:alert()' }
+
+ it 'returns nil' do
+ expect(subject).to receive(:prepare!)
- expect(subject.package_url).to eq('myawesomepackage.com')
+ expect(subject.package_url).to be_nil
+ end
end
end
diff --git a/spec/models/board_group_recent_visit_spec.rb b/spec/models/board_group_recent_visit_spec.rb
new file mode 100644
index 00000000000..59ad4e5417e
--- /dev/null
+++ b/spec/models/board_group_recent_visit_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe BoardGroupRecentVisit do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:board) { create(:board, group: group) }
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to belong_to(:group) }
+ it { is_expected.to belong_to(:board) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:user) }
+ it { is_expected.to validate_presence_of(:group) }
+ it { is_expected.to validate_presence_of(:board) }
+ end
+
+ describe '#visited' do
+ it 'creates a visit if one does not exists' do
+ expect { described_class.visited!(user, board) }.to change(described_class, :count).by(1)
+ end
+
+ shared_examples 'was visited previously' do
+ let!(:visit) { create :board_group_recent_visit, group: board.group, board: board, user: user, updated_at: 7.days.ago }
+
+ it 'updates the timestamp' do
+ Timecop.freeze do
+ described_class.visited!(user, board)
+
+ expect(described_class.count).to eq 1
+ expect(described_class.first.updated_at).to be_like_time(Time.zone.now)
+ end
+ end
+ end
+
+ it_behaves_like 'was visited previously'
+
+ context 'when we try to create a visit that is not unique' do
+ before do
+ expect(described_class).to receive(:find_or_create_by).and_raise(ActiveRecord::RecordNotUnique, 'record not unique')
+ expect(described_class).to receive(:find_or_create_by).and_return(visit)
+ end
+
+ it_behaves_like 'was visited previously'
+ end
+ end
+
+ describe '#latest' do
+ it 'returns the most recent visited' do
+ board2 = create(:board, group: group)
+ board3 = create(:board, group: group)
+
+ create :board_group_recent_visit, group: board.group, board: board, user: user, updated_at: 7.days.ago
+ create :board_group_recent_visit, group: board2.group, board: board2, user: user, updated_at: 5.days.ago
+ recent = create :board_group_recent_visit, group: board3.group, board: board3, user: user, updated_at: 1.day.ago
+
+ expect(described_class.latest(user, group)).to eq recent
+ end
+ end
+end
diff --git a/spec/models/board_project_recent_visit_spec.rb b/spec/models/board_project_recent_visit_spec.rb
new file mode 100644
index 00000000000..275581945fa
--- /dev/null
+++ b/spec/models/board_project_recent_visit_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe BoardProjectRecentVisit do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:board) { create(:board, project: project) }
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:board) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:user) }
+ it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:board) }
+ end
+
+ describe '#visited' do
+ it 'creates a visit if one does not exists' do
+ expect { described_class.visited!(user, board) }.to change(described_class, :count).by(1)
+ end
+
+ shared_examples 'was visited previously' do
+ let!(:visit) { create :board_project_recent_visit, project: board.project, board: board, user: user, updated_at: 7.days.ago }
+
+ it 'updates the timestamp' do
+ Timecop.freeze do
+ described_class.visited!(user, board)
+
+ expect(described_class.count).to eq 1
+ expect(described_class.first.updated_at).to be_like_time(Time.zone.now)
+ end
+ end
+ end
+
+ it_behaves_like 'was visited previously'
+
+ context 'when we try to create a visit that is not unique' do
+ before do
+ expect(described_class).to receive(:find_or_create_by).and_raise(ActiveRecord::RecordNotUnique, 'record not unique')
+ expect(described_class).to receive(:find_or_create_by).and_return(visit)
+ end
+
+ it_behaves_like 'was visited previously'
+ end
+ end
+
+ describe '#latest' do
+ it 'returns the most recent visited' do
+ board2 = create(:board, project: project)
+ board3 = create(:board, project: project)
+
+ create :board_project_recent_visit, project: board.project, board: board, user: user, updated_at: 7.days.ago
+ create :board_project_recent_visit, project: board2.project, board: board2, user: user, updated_at: 5.days.ago
+ recent = create :board_project_recent_visit, project: board3.project, board: board3, user: user, updated_at: 1.day.ago
+
+ expect(described_class.latest(user, project)).to eq recent
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 42b627b6823..d02c3a5765f 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -17,8 +17,8 @@ describe Ci::Build do
it { is_expected.to belong_to(:runner) }
it { is_expected.to belong_to(:trigger_request) }
it { is_expected.to belong_to(:erased_by) }
- it { is_expected.to have_many(:deployments) }
it { is_expected.to have_many(:trace_sections)}
+ it { is_expected.to have_one(:deployment) }
it { is_expected.to have_one(:runner_session)}
it { is_expected.to validate_presence_of(:ref) }
it { is_expected.to respond_to(:has_trace?) }
@@ -176,9 +176,7 @@ describe Ci::Build do
it 'does not execute a query for selecting job artifact one by one' do
recorded = ActiveRecord::QueryRecorder.new do
subject.each do |build|
- Ci::JobArtifact::TEST_REPORT_FILE_TYPES.each do |file_type|
- build.public_send("job_artifacts_#{file_type}").file.exists?
- end
+ build.job_artifacts.map { |a| a.file.exists? }
end
end
@@ -211,6 +209,143 @@ describe Ci::Build do
end
end
+ describe '#schedulable?' do
+ subject { build.schedulable? }
+
+ context 'when build is schedulable' do
+ let(:build) { create(:ci_build, :created, :schedulable, project: project) }
+
+ it { expect(subject).to be_truthy }
+ end
+
+ context 'when build is not schedulable' do
+ let(:build) { create(:ci_build, :created, project: project) }
+
+ it { expect(subject).to be_falsy }
+ end
+ end
+
+ describe '#schedule' do
+ subject { build.schedule }
+
+ before do
+ project.add_developer(user)
+ end
+
+ let(:build) { create(:ci_build, :created, :schedulable, user: user, project: project) }
+
+ it 'transits to scheduled' do
+ allow(Ci::BuildScheduleWorker).to receive(:perform_at)
+
+ subject
+
+ expect(build).to be_scheduled
+ end
+
+ it 'updates scheduled_at column' do
+ allow(Ci::BuildScheduleWorker).to receive(:perform_at)
+
+ subject
+
+ expect(build.scheduled_at).not_to be_nil
+ end
+
+ it 'schedules BuildScheduleWorker at the right time' do
+ Timecop.freeze do
+ expect(Ci::BuildScheduleWorker)
+ .to receive(:perform_at).with(be_like_time(1.minute.since), build.id)
+
+ subject
+ end
+ end
+ end
+
+ describe '#unschedule' do
+ subject { build.unschedule }
+
+ context 'when build is scheduled' do
+ let(:build) { create(:ci_build, :scheduled, pipeline: pipeline) }
+
+ it 'cleans scheduled_at column' do
+ subject
+
+ expect(build.scheduled_at).to be_nil
+ end
+
+ it 'transits to manual' do
+ subject
+
+ expect(build).to be_manual
+ end
+ end
+
+ context 'when build is not scheduled' do
+ let(:build) { create(:ci_build, :created, pipeline: pipeline) }
+
+ it 'does not transit status' do
+ subject
+
+ expect(build).to be_created
+ end
+ end
+ end
+
+ describe '#options_scheduled_at' do
+ subject { build.options_scheduled_at }
+
+ let(:build) { build_stubbed(:ci_build, options: option) }
+
+ context 'when start_in is 1 day' do
+ let(:option) { { start_in: '1 day' } }
+
+ it 'returns date after 1 day' do
+ Timecop.freeze do
+ is_expected.to eq(1.day.since)
+ end
+ end
+ end
+
+ context 'when start_in is 1 week' do
+ let(:option) { { start_in: '1 week' } }
+
+ it 'returns date after 1 week' do
+ Timecop.freeze do
+ is_expected.to eq(1.week.since)
+ end
+ end
+ end
+ end
+
+ describe '#enqueue_scheduled' do
+ subject { build.enqueue_scheduled }
+
+ context 'when build is scheduled and the right time has not come yet' do
+ let(:build) { create(:ci_build, :scheduled, pipeline: pipeline) }
+
+ it 'does not transits the status' do
+ subject
+
+ expect(build).to be_scheduled
+ end
+ end
+
+ context 'when build is scheduled and the right time has already come' do
+ let(:build) { create(:ci_build, :expired_scheduled, pipeline: pipeline) }
+
+ it 'cleans scheduled_at column' do
+ subject
+
+ expect(build.scheduled_at).to be_nil
+ end
+
+ it 'transits to pending' do
+ subject
+
+ expect(build).to be_pending
+ end
+ end
+ end
+
describe '#any_runners_online?' do
subject { build.any_runners_online? }
@@ -550,44 +685,22 @@ describe Ci::Build do
end
end
- describe '#has_test_reports?' do
- subject { build.has_test_reports? }
+ describe '#has_job_artifacts?' do
+ subject { build.has_job_artifacts? }
- context 'when build has a test report' do
- let(:build) { create(:ci_build, :test_reports) }
+ context 'when build has a job artifact' do
+ let(:build) { create(:ci_build, :artifacts) }
it { is_expected.to be_truthy }
end
- context 'when build does not have test reports' do
- let(:build) { create(:ci_build, :artifacts) }
+ context 'when build does not have job artifacts' do
+ let(:build) { create(:ci_build, :legacy_artifacts) }
it { is_expected.to be_falsy }
end
end
- describe '#erase_test_reports!' do
- subject { build.erase_test_reports! }
-
- context 'when build has a test report' do
- let!(:build) { create(:ci_build, :test_reports) }
-
- it 'removes a test report' do
- subject
-
- expect(build.has_test_reports?).to be_falsy
- end
- end
-
- context 'when build does not have test reports' do
- let!(:build) { create(:ci_build, :artifacts) }
-
- it 'does not erase anything' do
- expect { subject }.not_to change { Ci::JobArtifact.count }
- end
- end
- end
-
describe '#has_old_trace?' do
subject { build.has_old_trace? }
@@ -686,17 +799,100 @@ describe Ci::Build do
end
end
+ describe 'state transition as a deployable' do
+ let!(:build) { create(:ci_build, :start_review_app) }
+ let(:deployment) { build.deployment }
+ let(:environment) { deployment.environment }
+
+ it 'has deployments record with created status' do
+ expect(deployment).to be_created
+ expect(environment.name).to eq('review/master')
+ end
+
+ context 'when transits to running' do
+ before do
+ build.run!
+ end
+
+ it 'transits deployment status to running' do
+ expect(deployment).to be_running
+ end
+ end
+
+ context 'when transits to success' do
+ before do
+ allow(Deployments::SuccessWorker).to receive(:perform_async)
+ build.success!
+ end
+
+ it 'transits deployment status to success' do
+ expect(deployment).to be_success
+ end
+ end
+
+ context 'when transits to failed' do
+ before do
+ build.drop!
+ end
+
+ it 'transits deployment status to failed' do
+ expect(deployment).to be_failed
+ end
+ end
+
+ context 'when transits to skipped' do
+ before do
+ build.skip!
+ end
+
+ it 'transits deployment status to canceled' do
+ expect(deployment).to be_canceled
+ end
+ end
+
+ context 'when transits to canceled' do
+ before do
+ build.cancel!
+ end
+
+ it 'transits deployment status to canceled' do
+ expect(deployment).to be_canceled
+ end
+ end
+ end
+
+ describe '#on_stop' do
+ subject { build.on_stop }
+
+ context 'when a job has a specification that it can be stopped from the other job' do
+ let(:build) { create(:ci_build, :start_review_app) }
+
+ it 'returns the other job name' do
+ is_expected.to eq('stop_review_app')
+ end
+ end
+
+ context 'when a job does not have environment information' do
+ let(:build) { create(:ci_build) }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
describe 'deployment' do
- describe '#last_deployment' do
- subject { build.last_deployment }
+ describe '#has_deployment?' do
+ subject { build.has_deployment? }
- context 'when multiple deployments are created' do
- let!(:deployment1) { create(:deployment, deployable: build) }
- let!(:deployment2) { create(:deployment, deployable: build) }
+ context 'when build has a deployment' do
+ let!(:deployment) { create(:deployment, deployable: build) }
- it 'returns the latest one' do
- is_expected.to eq(deployment2)
- end
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when build does not have a deployment' do
+ it { is_expected.to be_falsy }
end
end
@@ -705,14 +901,14 @@ describe Ci::Build do
context 'when build succeeded' do
let(:build) { create(:ci_build, :success) }
- let!(:deployment) { create(:deployment, deployable: build) }
+ let!(:deployment) { create(:deployment, :success, deployable: build) }
context 'current deployment is latest' do
it { is_expected.to be_falsey }
end
context 'current deployment is not latest on environment' do
- let!(:deployment2) { create(:deployment, environment: deployment.environment) }
+ let!(:deployment2) { create(:deployment, :success, environment: deployment.environment) }
it { is_expected.to be_truthy }
end
@@ -850,8 +1046,8 @@ describe Ci::Build do
expect(build.artifacts_metadata.exists?).to be_falsy
end
- it 'removes test reports' do
- expect(build.job_artifacts.test_reports.count).to eq(0)
+ it 'removes all job_artifacts' do
+ expect(build.job_artifacts.count).to eq(0)
end
it 'erases build trace in trace file' do
@@ -1022,6 +1218,32 @@ describe Ci::Build do
end
end
+ describe '#erase_erasable_artifacts!' do
+ let!(:build) { create(:ci_build, :success) }
+
+ subject { build.erase_erasable_artifacts! }
+
+ before do
+ Ci::JobArtifact.file_types.keys.each do |file_type|
+ create(:ci_job_artifact, job: build, file_type: file_type, file_format: Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[file_type.to_sym])
+ end
+ end
+
+ it "erases erasable artifacts" do
+ subject
+
+ expect(build.job_artifacts.erasable).to be_empty
+ end
+
+ it "keeps non erasable artifacts" do
+ subject
+
+ Ci::JobArtifact::NON_ERASABLE_FILE_TYPES.each do |file_type|
+ expect(build.send("job_artifacts_#{file_type}")).not_to be_nil
+ end
+ end
+ end
+
describe '#first_pending' do
let!(:first) { create(:ci_build, pipeline: pipeline, status: 'pending', created_at: Date.yesterday) }
let!(:second) { create(:ci_build, pipeline: pipeline, status: 'pending') }
@@ -1175,6 +1397,14 @@ describe Ci::Build do
it { is_expected.not_to be_retryable }
end
+
+ context 'when build is degenerated' do
+ before do
+ build.degenerate!
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
end
end
@@ -1191,6 +1421,12 @@ describe Ci::Build do
it { is_expected.to be_truthy }
end
+ context 'when is set to delayed' do
+ let(:value) { 'delayed' }
+
+ it { is_expected.to be_truthy }
+ end
+
context 'when set to something else' do
let(:value) { 'something else' }
@@ -1236,21 +1472,127 @@ describe Ci::Build do
end
describe '#retries_max' do
- context 'when max retries value is defined' do
- subject { create(:ci_build, options: { retry: 1 }) }
+ context 'with retries max config option' do
+ subject { create(:ci_build, options: { retry: { max: 1 } }) }
- it 'returns a number of configured max retries' do
+ it 'returns the number of configured max retries' do
expect(subject.retries_max).to eq 1
end
end
- context 'when max retries value is not defined' do
+ context 'without retries max config option' do
subject { create(:ci_build) }
it 'returns zero' do
expect(subject.retries_max).to eq 0
end
end
+
+ context 'when build is degenerated' do
+ subject { create(:ci_build, :degenerated) }
+
+ it 'returns zero' do
+ expect(subject.retries_max).to eq 0
+ end
+ end
+
+ context 'with integer only config option' do
+ subject { create(:ci_build, options: { retry: 1 }) }
+
+ it 'returns the number of configured max retries' do
+ expect(subject.retries_max).to eq 1
+ end
+ end
+ end
+
+ describe '#retry_when' do
+ context 'with retries when config option' do
+ subject { create(:ci_build, options: { retry: { when: ['some_reason'] } }) }
+
+ it 'returns the configured when' do
+ expect(subject.retry_when).to eq ['some_reason']
+ end
+ end
+
+ context 'without retries when config option' do
+ subject { create(:ci_build) }
+
+ it 'returns always array' do
+ expect(subject.retry_when).to eq ['always']
+ end
+ end
+
+ context 'with integer only config option' do
+ subject { create(:ci_build, options: { retry: 1 }) }
+
+ it 'returns always array' do
+ expect(subject.retry_when).to eq ['always']
+ end
+ end
+ end
+
+ describe '#retry_failure?' do
+ subject { create(:ci_build) }
+
+ context 'when retries max is zero' do
+ before do
+ expect(subject).to receive(:retries_max).at_least(:once).and_return(0)
+ end
+
+ it 'returns false' do
+ expect(subject.retry_failure?).to eq false
+ end
+ end
+
+ context 'when retries max equals retries count' do
+ before do
+ expect(subject).to receive(:retries_max).at_least(:once).and_return(1)
+ expect(subject).to receive(:retries_count).at_least(:once).and_return(1)
+ end
+
+ it 'returns false' do
+ expect(subject.retry_failure?).to eq false
+ end
+ end
+
+ context 'when retries max is higher than retries count' do
+ before do
+ expect(subject).to receive(:retries_max).at_least(:once).and_return(2)
+ expect(subject).to receive(:retries_count).at_least(:once).and_return(1)
+ end
+
+ context 'and retry when is always' do
+ before do
+ expect(subject).to receive(:retry_when).at_least(:once).and_return(['always'])
+ end
+
+ it 'returns true' do
+ expect(subject.retry_failure?).to eq true
+ end
+ end
+
+ context 'and retry when includes the failure_reason' do
+ before do
+ expect(subject).to receive(:failure_reason).at_least(:once).and_return('some_reason')
+ expect(subject).to receive(:retry_when).at_least(:once).and_return(['some_reason'])
+ end
+
+ it 'returns true' do
+ expect(subject.retry_failure?).to eq true
+ end
+ end
+
+ context 'and retry when does not include failure_reason' do
+ before do
+ expect(subject).to receive(:failure_reason).at_least(:once).and_return('some_reason')
+ expect(subject).to receive(:retry_when).at_least(:once).and_return(['some', 'other failure'])
+ end
+
+ it 'returns false' do
+ expect(subject.retry_failure?).to eq false
+ end
+ end
+ end
end
end
@@ -1276,6 +1618,19 @@ describe Ci::Build do
end
end
+ describe '#artifacts_file_for_type' do
+ let(:build) { create(:ci_build, :artifacts) }
+ let(:file_type) { :archive }
+
+ subject { build.artifacts_file_for_type(file_type) }
+
+ it 'queries artifacts for type' do
+ expect(build).to receive_message_chain(:job_artifacts, :find_by).with(file_type: Ci::JobArtifact.file_types[file_type])
+
+ subject
+ end
+ end
+
describe '#merge_request' do
def create_mr(build, pipeline, factory: :merge_request, created_at: Time.now)
create(factory, source_project: pipeline.project,
@@ -1353,11 +1708,11 @@ describe Ci::Build do
end
end
- describe '#other_actions' do
+ describe '#other_manual_actions' do
let(:build) { create(:ci_build, :manual, pipeline: pipeline) }
let!(:other_build) { create(:ci_build, :manual, pipeline: pipeline, name: 'other action') }
- subject { build.other_actions }
+ subject { build.other_manual_actions }
before do
project.add_developer(user)
@@ -1388,6 +1743,48 @@ describe Ci::Build do
end
end
+ describe '#other_scheduled_actions' do
+ let(:build) { create(:ci_build, :scheduled, pipeline: pipeline) }
+
+ subject { build.other_scheduled_actions }
+
+ before do
+ project.add_developer(user)
+ end
+
+ context "when other build's status is success" do
+ let!(:other_build) { create(:ci_build, :schedulable, :success, pipeline: pipeline, name: 'other action') }
+
+ it 'returns other actions' do
+ is_expected.to contain_exactly(other_build)
+ end
+ end
+
+ context "when other build's status is failed" do
+ let!(:other_build) { create(:ci_build, :schedulable, :failed, pipeline: pipeline, name: 'other action') }
+
+ it 'returns other actions' do
+ is_expected.to contain_exactly(other_build)
+ end
+ end
+
+ context "when other build's status is running" do
+ let!(:other_build) { create(:ci_build, :schedulable, :running, pipeline: pipeline, name: 'other action') }
+
+ it 'does not return other actions' do
+ is_expected.to be_empty
+ end
+ end
+
+ context "when other build's status is scheduled" do
+ let!(:other_build) { create(:ci_build, :scheduled, pipeline: pipeline, name: 'other action') }
+
+ it 'does not return other actions' do
+ is_expected.to contain_exactly(other_build)
+ end
+ end
+ end
+
describe '#persisted_environment' do
let!(:environment) do
create(:environment, project: project, name: "foo-#{project.default_branch}")
@@ -1459,6 +1856,18 @@ describe Ci::Build do
it { is_expected.to be_playable }
end
+
+ context 'when build is a manual and degenerated' do
+ subject { build_stubbed(:ci_build, :manual, :degenerated, status: :manual) }
+
+ it { is_expected.not_to be_playable }
+ end
+ end
+
+ context 'when build is scheduled' do
+ subject { build_stubbed(:ci_build, :scheduled) }
+
+ it { is_expected.to be_playable }
end
context 'when build is not a manual action' do
@@ -1519,12 +1928,26 @@ describe Ci::Build do
describe '#repo_url' do
subject { build.repo_url }
- it { is_expected.to be_a(String) }
- it { is_expected.to end_with(".git") }
- it { is_expected.to start_with(project.web_url[0..6]) }
- it { is_expected.to include(build.token) }
- it { is_expected.to include('gitlab-ci-token') }
- it { is_expected.to include(project.web_url[7..-1]) }
+ context 'when token is set' do
+ before do
+ build.ensure_token
+ end
+
+ it { is_expected.to be_a(String) }
+ it { is_expected.to end_with(".git") }
+ it { is_expected.to start_with(project.web_url[0..6]) }
+ it { is_expected.to include(build.token) }
+ it { is_expected.to include('gitlab-ci-token') }
+ it { is_expected.to include(project.web_url[7..-1]) }
+ end
+
+ context 'when token is empty' do
+ before do
+ build.token = nil
+ end
+
+ it { is_expected.to be_nil}
+ end
end
describe '#stuck?' do
@@ -1634,7 +2057,8 @@ describe Ci::Build do
end
context 'use from gitlab-ci.yml' do
- let(:pipeline) { create(:ci_pipeline) }
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
before do
stub_ci_pipeline_yaml_file(config)
@@ -1676,51 +2100,6 @@ describe Ci::Build do
describe '#variables' do
let(:container_registry_enabled) { false }
- let(:predefined_variables) do
- [
- { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
- { key: 'CI_PIPELINE_URL', value: project.web_url + "/pipelines/#{pipeline.id}", public: true },
- { key: 'CI_JOB_ID', value: build.id.to_s, public: true },
- { key: 'CI_JOB_URL', value: project.web_url + "/-/jobs/#{build.id}", public: true },
- { key: 'CI_JOB_TOKEN', value: build.token, public: false },
- { key: 'CI_BUILD_ID', value: build.id.to_s, public: true },
- { key: 'CI_BUILD_TOKEN', value: build.token, public: false },
- { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true },
- { key: 'CI_REGISTRY_PASSWORD', value: build.token, public: false },
- { key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false },
- { key: 'CI', value: 'true', public: true },
- { key: 'GITLAB_CI', value: 'true', public: true },
- { key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true },
- { key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
- { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
- { key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true },
- { key: 'CI_JOB_NAME', value: 'test', public: true },
- { key: 'CI_JOB_STAGE', value: 'test', public: true },
- { key: 'CI_COMMIT_SHA', value: build.sha, public: true },
- { key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true },
- { key: 'CI_COMMIT_REF_NAME', value: build.ref, public: true },
- { key: 'CI_COMMIT_REF_SLUG', value: build.ref_slug, public: true },
- { key: 'CI_BUILD_REF', value: build.sha, public: true },
- { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true },
- { key: 'CI_BUILD_REF_NAME', value: build.ref, public: true },
- { key: 'CI_BUILD_REF_SLUG', value: build.ref_slug, public: true },
- { key: 'CI_BUILD_NAME', value: 'test', public: true },
- { key: 'CI_BUILD_STAGE', value: 'test', public: true },
- { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true },
- { key: 'CI_PROJECT_NAME', value: project.path, public: true },
- { key: 'CI_PROJECT_PATH', value: project.full_path, public: true },
- { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true },
- { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true },
- { key: 'CI_PROJECT_URL', value: project.web_url, public: true },
- { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true },
- { key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true },
- { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true },
- { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true },
- { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true },
- { key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true },
- { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true }
- ]
- end
before do
stub_container_registry_config(enabled: container_registry_enabled, host_port: 'registry.example.com')
@@ -1729,11 +2108,174 @@ describe Ci::Build do
subject { build.variables }
context 'returns variables' do
+ let(:predefined_variables) do
+ [
+ { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
+ { key: 'CI_PIPELINE_URL', value: project.web_url + "/pipelines/#{pipeline.id}", public: true },
+ { key: 'CI_JOB_ID', value: build.id.to_s, public: true },
+ { key: 'CI_JOB_URL', value: project.web_url + "/-/jobs/#{build.id}", public: true },
+ { key: 'CI_JOB_TOKEN', value: 'my-token', public: false },
+ { key: 'CI_BUILD_ID', value: build.id.to_s, public: true },
+ { key: 'CI_BUILD_TOKEN', value: 'my-token', public: false },
+ { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true },
+ { key: 'CI_REGISTRY_PASSWORD', value: 'my-token', public: false },
+ { key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false },
+ { key: 'CI', value: 'true', public: true },
+ { key: 'GITLAB_CI', value: 'true', public: true },
+ { key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true },
+ { key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
+ { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
+ { key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s, public: true },
+ { key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s, public: true },
+ { key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s, public: true },
+ { key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true },
+ { key: 'CI_JOB_NAME', value: 'test', public: true },
+ { key: 'CI_JOB_STAGE', value: 'test', public: true },
+ { key: 'CI_COMMIT_SHA', value: build.sha, public: true },
+ { key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true },
+ { key: 'CI_COMMIT_REF_NAME', value: build.ref, public: true },
+ { key: 'CI_COMMIT_REF_SLUG', value: build.ref_slug, public: true },
+ { key: 'CI_NODE_TOTAL', value: '1', public: true },
+ { key: 'CI_BUILD_REF', value: build.sha, public: true },
+ { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true },
+ { key: 'CI_BUILD_REF_NAME', value: build.ref, public: true },
+ { key: 'CI_BUILD_REF_SLUG', value: build.ref_slug, public: true },
+ { key: 'CI_BUILD_NAME', value: 'test', public: true },
+ { key: 'CI_BUILD_STAGE', value: 'test', public: true },
+ { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true },
+ { key: 'CI_PROJECT_NAME', value: project.path, public: true },
+ { key: 'CI_PROJECT_PATH', value: project.full_path, public: true },
+ { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true },
+ { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true },
+ { key: 'CI_PROJECT_URL', value: project.web_url, public: true },
+ { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true },
+ { key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true },
+ { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true },
+ { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true },
+ { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true },
+ { key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true },
+ { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true }
+ ]
+ end
+
before do
+ build.token = 'my-token'
build.yaml_variables = []
end
it { is_expected.to include(*predefined_variables) }
+
+ context 'when yaml variables are undefined' do
+ let(:pipeline) do
+ create(:ci_pipeline, project: project,
+ sha: project.commit.id,
+ ref: project.default_branch)
+ end
+
+ before do
+ build.yaml_variables = nil
+ end
+
+ context 'use from gitlab-ci.yml' do
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ context 'when config is not found' do
+ let(:config) { nil }
+
+ it { is_expected.to include(*predefined_variables) }
+ end
+
+ context 'when config does not have a questioned job' do
+ let(:config) do
+ YAML.dump({
+ test_other: {
+ script: 'Hello World'
+ }
+ })
+ end
+
+ it { is_expected.to include(*predefined_variables) }
+ end
+
+ context 'when config has variables' do
+ let(:config) do
+ YAML.dump({
+ test: {
+ script: 'Hello World',
+ variables: {
+ KEY: 'value'
+ }
+ }
+ })
+ end
+
+ let(:variables) do
+ [{ key: 'KEY', value: 'value', public: true }]
+ end
+
+ it { is_expected.to include(*predefined_variables) }
+ it { is_expected.to include(*variables) }
+ end
+ end
+ end
+
+ describe 'variables ordering' do
+ context 'when variables hierarchy is stubbed' do
+ let(:build_pre_var) { { key: 'build', value: 'value', public: true } }
+ let(:project_pre_var) { { key: 'project', value: 'value', public: true } }
+ let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true } }
+ let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true } }
+
+ before do
+ allow(build).to receive(:predefined_variables) { [build_pre_var] }
+ allow(build).to receive(:yaml_variables) { [build_yaml_var] }
+ allow(build).to receive(:persisted_variables) { [] }
+
+ allow_any_instance_of(Project)
+ .to receive(:predefined_variables) { [project_pre_var] }
+
+ project.variables.create!(key: 'secret', value: 'value')
+
+ allow_any_instance_of(Ci::Pipeline)
+ .to receive(:predefined_variables) { [pipeline_pre_var] }
+ end
+
+ it 'returns variables in order depending on resource hierarchy' do
+ is_expected.to eq(
+ [build_pre_var,
+ project_pre_var,
+ pipeline_pre_var,
+ build_yaml_var,
+ { key: 'secret', value: 'value', public: false }])
+ end
+ end
+
+ context 'when build has environment and user-provided variables' do
+ let(:expected_variables) do
+ predefined_variables.map { |variable| variable.fetch(:key) } +
+ %w[YAML_VARIABLE CI_ENVIRONMENT_NAME CI_ENVIRONMENT_SLUG
+ CI_ENVIRONMENT_URL]
+ end
+
+ before do
+ create(:environment, project: build.project,
+ name: 'staging')
+
+ build.yaml_variables = [{ key: 'YAML_VARIABLE',
+ value: 'var',
+ public: true }]
+ build.environment = 'staging'
+ end
+
+ it 'matches explicit variables ordering' do
+ received_variables = subject.map { |variable| variable.fetch(:key) }
+
+ expect(received_variables).to eq expected_variables
+ end
+ end
+ end
end
context 'when build has user' do
@@ -1847,17 +2389,17 @@ describe Ci::Build do
it { is_expected.to include(tag_variable) }
end
- context 'when secret variable is defined' do
- let(:secret_variable) do
+ context 'when CI variable is defined' do
+ let(:ci_variable) do
{ key: 'SECRET_KEY', value: 'secret_value', public: false }
end
before do
create(:ci_variable,
- secret_variable.slice(:key, :value).merge(project: project))
+ ci_variable.slice(:key, :value).merge(project: project))
end
- it { is_expected.to include(secret_variable) }
+ it { is_expected.to include(ci_variable) }
end
context 'when protected variable is defined' do
@@ -1892,17 +2434,17 @@ describe Ci::Build do
end
end
- context 'when group secret variable is defined' do
- let(:secret_variable) do
+ context 'when group CI variable is defined' do
+ let(:ci_variable) do
{ key: 'SECRET_KEY', value: 'secret_value', public: false }
end
before do
create(:ci_group_variable,
- secret_variable.slice(:key, :value).merge(group: group))
+ ci_variable.slice(:key, :value).merge(group: group))
end
- it { is_expected.to include(secret_variable) }
+ it { is_expected.to include(ci_variable) }
end
context 'when group protected variable is defined' do
@@ -1995,75 +2537,20 @@ describe Ci::Build do
end
before do
- pipeline_schedule.pipelines << pipeline
+ pipeline_schedule.pipelines << pipeline.reload
pipeline_schedule.reload
end
it { is_expected.to include(pipeline_schedule_variable.to_runner_variable) }
end
- context 'when yaml_variables are undefined' do
- let(:pipeline) do
- create(:ci_pipeline, project: project,
- sha: project.commit.id,
- ref: project.default_branch)
- end
-
- before do
- build.yaml_variables = nil
- end
-
- context 'use from gitlab-ci.yml' do
- before do
- stub_ci_pipeline_yaml_file(config)
- end
-
- context 'when config is not found' do
- let(:config) { nil }
-
- it { is_expected.to include(*predefined_variables) }
- end
-
- context 'when config does not have a questioned job' do
- let(:config) do
- YAML.dump({
- test_other: {
- script: 'Hello World'
- }
- })
- end
-
- it { is_expected.to include(*predefined_variables) }
- end
-
- context 'when config has variables' do
- let(:config) do
- YAML.dump({
- test: {
- script: 'Hello World',
- variables: {
- KEY: 'value'
- }
- }
- })
- end
- let(:variables) do
- [{ key: 'KEY', value: 'value', public: true }]
- end
-
- it { is_expected.to include(*predefined_variables) }
- it { is_expected.to include(*variables) }
- end
- end
- end
-
context 'when container registry is enabled' do
let(:container_registry_enabled) { true }
let(:ci_registry) do
- { key: 'CI_REGISTRY', value: 'registry.example.com', public: true }
+ { key: 'CI_REGISTRY', value: 'registry.example.com', public: true }
end
let(:ci_registry_image) do
- { key: 'CI_REGISTRY_IMAGE', value: project.container_registry_url, public: true }
+ { key: 'CI_REGISTRY_IMAGE', value: project.container_registry_url, public: true }
end
context 'and is disabled for project' do
@@ -2161,63 +2648,26 @@ describe Ci::Build do
end
end
- describe 'variables ordering' do
- context 'when variables hierarchy is stubbed' do
- let(:build_pre_var) { { key: 'build', value: 'value', public: true } }
- let(:project_pre_var) { { key: 'project', value: 'value', public: true } }
- let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true } }
- let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true } }
-
- before do
- allow(build).to receive(:predefined_variables) { [build_pre_var] }
- allow(build).to receive(:yaml_variables) { [build_yaml_var] }
- allow(build).to receive(:persisted_variables) { [] }
-
- allow_any_instance_of(Project)
- .to receive(:predefined_variables) { [project_pre_var] }
-
- allow_any_instance_of(Project)
- .to receive(:secret_variables_for)
- .with(ref: 'master', environment: nil) do
- [create(:ci_variable, key: 'secret', value: 'value')]
- end
-
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:predefined_variables) { [pipeline_pre_var] }
- end
+ context 'when build is parallelized' do
+ let(:total) { 5 }
+ let(:index) { 3 }
- it 'returns variables in order depending on resource hierarchy' do
- is_expected.to eq(
- [build_pre_var,
- project_pre_var,
- pipeline_pre_var,
- build_yaml_var,
- { key: 'secret', value: 'value', public: false }])
- end
+ before do
+ build.options[:parallel] = total
+ build.options[:instance] = index
+ build.name = "#{build.name} #{index}/#{total}"
end
- context 'when build has environment and user-provided variables' do
- let(:expected_variables) do
- predefined_variables.map { |variable| variable.fetch(:key) } +
- %w[YAML_VARIABLE CI_ENVIRONMENT_NAME CI_ENVIRONMENT_SLUG
- CI_ENVIRONMENT_URL]
- end
-
- before do
- create(:environment, project: build.project,
- name: 'staging')
-
- build.yaml_variables = [{ key: 'YAML_VARIABLE',
- value: 'var',
- public: true }]
- build.environment = 'staging'
- end
-
- it 'matches explicit variables ordering' do
- received_variables = subject.map { |variable| variable.fetch(:key) }
+ it 'includes CI_NODE_INDEX' do
+ is_expected.to include(
+ { key: 'CI_NODE_INDEX', value: index.to_s, public: true }
+ )
+ end
- expect(received_variables).to eq expected_variables
- end
+ it 'includes correct CI_NODE_TOTAL' do
+ is_expected.to include(
+ { key: 'CI_NODE_TOTAL', value: total.to_s, public: true }
+ )
end
end
@@ -2328,7 +2778,7 @@ describe Ci::Build do
end
describe '#scoped_variables_hash' do
- context 'when overriding secret variables' do
+ context 'when overriding CI variables' do
before do
project.variables.create!(key: 'MY_VAR', value: 'my value 1')
pipeline.variables.create!(key: 'MY_VAR', value: 'my value 2')
@@ -2548,7 +2998,7 @@ describe Ci::Build do
end
context 'when build is configured to be retried' do
- subject { create(:ci_build, :running, options: { retry: 3 }, project: project, user: user) }
+ subject { create(:ci_build, :running, options: { retry: { max: 3 } }, project: project, user: user) }
it 'retries build and assigns the same user to it' do
expect(described_class).to receive(:retry)
@@ -2844,16 +3294,10 @@ describe Ci::Build do
end
it 'raises an error' do
- expect { subject }.to raise_error(Gitlab::Ci::Parsers::Junit::JunitParserError)
+ expect { subject }.to raise_error(Gitlab::Ci::Parsers::Test::Junit::JunitParserError)
end
end
end
-
- context 'when build does not have test reports' do
- it 'raises an error' do
- expect { subject }.to raise_error(NoMethodError)
- end
- end
end
describe '#artifacts_metadata_entry' do
@@ -2981,4 +3425,93 @@ describe Ci::Build do
end
end
end
+
+ describe '#deployment_status' do
+ before do
+ allow_any_instance_of(described_class).to receive(:create_deployment)
+ end
+
+ context 'when build is a last deployment' do
+ let(:build) { create(:ci_build, :success, environment: 'production') }
+ let(:environment) { create(:environment, name: 'production', project: build.project) }
+ let!(:deployment) { create(:deployment, :success, environment: environment, project: environment.project, deployable: build) }
+
+ it { expect(build.deployment_status).to eq(:last) }
+ end
+
+ context 'when there is a newer build with deployment' do
+ let(:build) { create(:ci_build, :success, environment: 'production') }
+ let(:environment) { create(:environment, name: 'production', project: build.project) }
+ let!(:deployment) { create(:deployment, :success, environment: environment, project: environment.project, deployable: build) }
+ let!(:last_deployment) { create(:deployment, :success, environment: environment, project: environment.project) }
+
+ it { expect(build.deployment_status).to eq(:out_of_date) }
+ end
+
+ context 'when build with deployment has failed' do
+ let(:build) { create(:ci_build, :failed, environment: 'production') }
+ let(:environment) { create(:environment, name: 'production', project: build.project) }
+ let!(:deployment) { create(:deployment, :success, environment: environment, project: environment.project, deployable: build) }
+
+ it { expect(build.deployment_status).to eq(:failed) }
+ end
+
+ context 'when build with deployment is running' do
+ let(:build) { create(:ci_build, environment: 'production') }
+ let(:environment) { create(:environment, name: 'production', project: build.project) }
+ let!(:deployment) { create(:deployment, :success, environment: environment, project: environment.project, deployable: build) }
+
+ it { expect(build.deployment_status).to eq(:creating) }
+ end
+ end
+
+ describe '#degenerated?' do
+ context 'when build is degenerated' do
+ subject { create(:ci_build, :degenerated) }
+
+ it { is_expected.to be_degenerated }
+ end
+
+ context 'when build is valid' do
+ subject { create(:ci_build) }
+
+ it { is_expected.not_to be_degenerated }
+
+ context 'and becomes degenerated' do
+ before do
+ subject.degenerate!
+ end
+
+ it { is_expected.to be_degenerated }
+ end
+ end
+ end
+
+ describe '#archived?' do
+ context 'when build is degenerated' do
+ subject { create(:ci_build, :degenerated) }
+
+ it { is_expected.to be_archived }
+ end
+
+ context 'for old build' do
+ subject { create(:ci_build, created_at: 1.day.ago) }
+
+ context 'when archive_builds_in is set' do
+ before do
+ stub_application_setting(archive_builds_in_seconds: 3600)
+ end
+
+ it { is_expected.to be_archived }
+ end
+
+ context 'when archive_builds_in is not set' do
+ before do
+ stub_application_setting(archive_builds_in_seconds: nil)
+ end
+
+ it { is_expected.not_to be_archived }
+ end
+ end
+ end
end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 1bf338f4c70..fb5bec4108a 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -31,6 +31,22 @@ describe Ci::JobArtifact do
end
end
+ describe '.erasable' do
+ subject { described_class.erasable }
+
+ context 'when there is an erasable artifact' do
+ let!(:artifact) { create(:ci_job_artifact, :junit) }
+
+ it { is_expected.to eq([artifact]) }
+ end
+
+ context 'when there are no erasable artifacts' do
+ let!(:artifact) { create(:ci_job_artifact, :trace) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe 'callbacks' do
subject { create(:ci_job_artifact, :archive) }
@@ -106,34 +122,46 @@ describe Ci::JobArtifact do
describe 'validates file format' do
subject { artifact }
- context 'when archive type with zip format' do
- let(:artifact) { build(:ci_job_artifact, :archive, file_format: :zip) }
+ described_class::TYPE_AND_FORMAT_PAIRS.except(:trace).each do |file_type, file_format|
+ context "when #{file_type} type with #{file_format} format" do
+ let(:artifact) { build(:ci_job_artifact, file_type: file_type, file_format: file_format) }
- it { is_expected.to be_valid }
- end
+ it { is_expected.to be_valid }
+ end
- context 'when archive type with gzip format' do
- let(:artifact) { build(:ci_job_artifact, :archive, file_format: :gzip) }
+ context "when #{file_type} type without format specification" do
+ let(:artifact) { build(:ci_job_artifact, file_type: file_type, file_format: nil) }
- it { is_expected.not_to be_valid }
- end
+ it { is_expected.not_to be_valid }
+ end
- context 'when archive type without format specification' do
- let(:artifact) { build(:ci_job_artifact, :archive, file_format: nil) }
+ context "when #{file_type} type with other formats" do
+ described_class.file_formats.except(file_format).values.each do |other_format|
+ let(:artifact) { build(:ci_job_artifact, file_type: file_type, file_format: other_format) }
- it { is_expected.not_to be_valid }
+ it { is_expected.not_to be_valid }
+ end
+ end
end
+ end
- context 'when junit type with zip format' do
- let(:artifact) { build(:ci_job_artifact, :junit, file_format: :zip) }
+ describe 'validates DEFAULT_FILE_NAMES' do
+ subject { described_class::DEFAULT_FILE_NAMES }
- it { is_expected.not_to be_valid }
+ described_class.file_types.each do |file_type, _|
+ it "expects #{file_type} to be included" do
+ is_expected.to include(file_type.to_sym)
+ end
end
+ end
- context 'when junit type with gzip format' do
- let(:artifact) { build(:ci_job_artifact, :junit, file_format: :gzip) }
+ describe 'validates TYPE_AND_FORMAT_PAIRS' do
+ subject { described_class::TYPE_AND_FORMAT_PAIRS }
- it { is_expected.to be_valid }
+ described_class.file_types.each do |file_type, _|
+ it "expects #{file_type} to be included" do
+ expect(described_class.file_formats).to include(subject[file_type.to_sym])
+ end
end
end
@@ -166,6 +194,14 @@ describe Ci::JobArtifact do
end
end
+ context 'when file format is raw' do
+ let(:artifact) { build(:ci_job_artifact, :codequality, file_format: :raw) }
+
+ it 'iterates blob once' do
+ expect { |b| artifact.each_blob(&b) }.to yield_control.once
+ end
+ end
+
context 'when there are no adapters for the file format' do
let(:artifact) { build(:ci_job_artifact, :junit, file_format: :zip) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 77b7332a761..9e6146b8a44 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -75,6 +75,18 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#delay' do
+ subject { pipeline.delay }
+
+ let(:pipeline) { build(:ci_pipeline, status: :created) }
+
+ it 'changes pipeline status to schedule' do
+ subject
+
+ expect(pipeline).to be_scheduled
+ end
+ end
+
describe '#valid_commit_sha' do
context 'commit.sha can not start with 00000000' do
before do
@@ -767,6 +779,41 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe 'ref_exists?' do
+ context 'when repository exists' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:project) { create(:project, :repository) }
+
+ where(:tag, :ref, :result) do
+ false | 'master' | true
+ false | 'non-existent-branch' | false
+ true | 'v1.1.0' | true
+ true | 'non-existent-tag' | false
+ end
+
+ with_them do
+ let(:pipeline) do
+ create(:ci_empty_pipeline, project: project, tag: tag, ref: ref)
+ end
+
+ it "correctly detects ref" do
+ expect(pipeline.ref_exists?).to be result
+ end
+ end
+ end
+
+ context 'when repository does not exist' do
+ let(:pipeline) do
+ create(:ci_empty_pipeline, project: project, ref: 'master')
+ end
+
+ it 'always returns false' do
+ expect(pipeline.ref_exists?).to eq false
+ end
+ end
+ end
+
context 'with non-empty project' do
let(:project) { create(:project, :repository) }
@@ -825,6 +872,57 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#branch_updated?' do
+ context 'when pipeline has before SHA' do
+ before do
+ pipeline.update_column(:before_sha, 'a1b2c3d4')
+ end
+
+ it 'runs on a branch update push' do
+ expect(pipeline.before_sha).not_to be Gitlab::Git::BLANK_SHA
+ expect(pipeline.branch_updated?).to be true
+ end
+ end
+
+ context 'when pipeline does not have before SHA' do
+ before do
+ pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
+ end
+
+ it 'does not run on a branch updating push' do
+ expect(pipeline.branch_updated?).to be false
+ end
+ end
+ end
+
+ describe '#modified_paths' do
+ context 'when old and new revisions are set' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ pipeline.update(before_sha: '1234abcd', sha: '2345bcde')
+ end
+
+ it 'fetches stats for changes between commits' do
+ expect(project.repository)
+ .to receive(:diff_stats).with('1234abcd', '2345bcde')
+ .and_call_original
+
+ pipeline.modified_paths
+ end
+ end
+
+ context 'when either old or new revision is missing' do
+ before do
+ pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
+ end
+
+ it 'raises an error' do
+ expect { pipeline.modified_paths }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
describe '#has_kubernetes_active?' do
context 'when kubernetes is active' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
@@ -945,6 +1043,11 @@ describe Ci::Pipeline, :mailer do
expect(described_class.newest_first.pluck(:status))
.to eq(%w[skipped failed success canceled])
end
+
+ it 'searches limited backlog' do
+ expect(described_class.newest_first(limit: 1).pluck(:status))
+ .to eq(%w[skipped])
+ end
end
describe '.latest_status' do
@@ -1050,6 +1153,19 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '.latest_successful_ids_per_project' do
+ let(:projects) { create_list(:project, 2) }
+ let!(:pipeline1) { create(:ci_pipeline, :success, project: projects[0]) }
+ let!(:pipeline2) { create(:ci_pipeline, :success, project: projects[0]) }
+ let!(:pipeline3) { create(:ci_pipeline, :failed, project: projects[0]) }
+ let!(:pipeline4) { create(:ci_pipeline, :success, project: projects[1]) }
+
+ it 'returns expected pipeline ids' do
+ expect(described_class.latest_successful_ids_per_project)
+ .to contain_exactly(pipeline2, pipeline4)
+ end
+ end
+
describe '.internal_sources' do
subject { described_class.internal_sources }
@@ -1151,7 +1267,11 @@ describe Ci::Pipeline, :mailer do
end
describe '#set_config_source' do
- context 'when pipelines does not contain needed data' do
+ context 'when pipelines does not contain needed data and auto devops is disabled' do
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+ end
+
it 'defines source to be unknown' do
pipeline.set_config_source
@@ -1196,7 +1316,6 @@ describe Ci::Pipeline, :mailer do
context 'auto devops enabled' do
before do
- stub_application_setting(auto_devops_enabled: true)
allow(project).to receive(:ci_config_path) { 'custom' }
end
@@ -1285,6 +1404,19 @@ describe Ci::Pipeline, :mailer do
end
end
+ context 'when updating status to scheduled' do
+ before do
+ allow(pipeline)
+ .to receive_message_chain(:statuses, :latest, :status)
+ .and_return(:scheduled)
+ end
+
+ it 'updates pipeline status to scheduled' do
+ expect { pipeline.update_status }
+ .to change { pipeline.reload.status }.to 'scheduled'
+ end
+ end
+
context 'when statuses status was not recognized' do
before do
allow(pipeline)
@@ -1743,7 +1875,7 @@ describe Ci::Pipeline, :mailer do
create(:ci_pipeline, config: { rspec: { script: 'rake test' } })
end
- it 'does not containyaml errors' do
+ it 'does not contain yaml errors' do
expect(pipeline).not_to have_yaml_errors
end
end
@@ -1941,4 +2073,58 @@ describe Ci::Pipeline, :mailer do
expect(pipeline.total_size).to eq(5)
end
end
+
+ describe '#status' do
+ context 'when transitioning to failed' do
+ context 'when pipeline has autodevops as source' do
+ let(:pipeline) { create(:ci_pipeline, :running, :auto_devops_source) }
+
+ it 'calls autodevops disable service' do
+ expect(AutoDevops::DisableWorker).to receive(:perform_async).with(pipeline.id)
+
+ pipeline.drop
+ end
+ end
+
+ context 'when pipeline has other source' do
+ let(:pipeline) { create(:ci_pipeline, :running, :repository_source) }
+
+ it 'does not call auto devops disable service' do
+ expect(AutoDevops::DisableWorker).not_to receive(:perform_async)
+
+ pipeline.drop
+ end
+ end
+ end
+ end
+
+ describe '#default_branch?' do
+ let(:default_branch) { 'master'}
+
+ subject { pipeline.default_branch? }
+
+ before do
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ context 'when pipeline ref is the default branch of the project' do
+ let(:pipeline) do
+ build(:ci_empty_pipeline, status: :created, project: project, ref: default_branch)
+ end
+
+ it "returns true" do
+ expect(subject).to be_truthy
+ end
+ end
+
+ context 'when pipeline ref is not the default branch of the project' do
+ let(:pipeline) do
+ build(:ci_empty_pipeline, status: :created, project: project, ref: 'another_branch')
+ end
+
+ it "returns false" do
+ expect(subject).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb
index 889c243c8d8..03d09cb31d6 100644
--- a/spec/models/ci/pipeline_variable_spec.rb
+++ b/spec/models/ci/pipeline_variable_spec.rb
@@ -5,4 +5,13 @@ describe Ci::PipelineVariable do
it { is_expected.to include_module(HasVariable) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:pipeline_id) }
+
+ describe '#hook_attrs' do
+ let(:variable) { create(:ci_pipeline_variable, key: 'foo', value: 'bar') }
+
+ subject { variable.hook_attrs }
+
+ it { is_expected.to be_a(Hash) }
+ it { is_expected.to eq({ key: 'foo', value: 'bar' }) }
+ end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 953af2c4710..b545e036aa1 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -223,7 +223,7 @@ describe Ci::Runner do
subject { described_class.online }
before do
- @runner1 = create(:ci_runner, :instance, contacted_at: 1.year.ago)
+ @runner1 = create(:ci_runner, :instance, contacted_at: 1.hour.ago)
@runner2 = create(:ci_runner, :instance, contacted_at: 1.second.ago)
end
@@ -300,6 +300,17 @@ describe Ci::Runner do
end
end
+ describe '.offline' do
+ subject { described_class.offline }
+
+ before do
+ @runner1 = create(:ci_runner, :instance, contacted_at: 1.hour.ago)
+ @runner2 = create(:ci_runner, :instance, contacted_at: 1.second.ago)
+ end
+
+ it { is_expected.to eq([@runner1])}
+ end
+
describe '#can_pick?' do
set(:pipeline) { create(:ci_pipeline) }
let(:build) { create(:ci_build, pipeline: pipeline) }
@@ -786,4 +797,22 @@ describe Ci::Runner do
expect { subject.destroy }.to change { described_class.count }.by(-1)
end
end
+
+ describe '.order_by' do
+ it 'supports ordering by the contact date' do
+ runner1 = create(:ci_runner, contacted_at: 1.year.ago)
+ runner2 = create(:ci_runner, contacted_at: 1.month.ago)
+ runners = described_class.order_by('contacted_asc')
+
+ expect(runners).to eq([runner1, runner2])
+ end
+
+ it 'supports ordering by the creation date' do
+ runner1 = create(:ci_runner, created_at: 1.year.ago)
+ runner2 = create(:ci_runner, created_at: 1.month.ago)
+ runners = described_class.order_by('created_asc')
+
+ expect(runners).to eq([runner2, runner1])
+ end
+ end
end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 22a4556c10c..5076f7faeac 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -89,6 +89,18 @@ describe Ci::Stage, :models do
end
end
+ context 'when stage is scheduled because of scheduled builds' do
+ before do
+ create(:ci_build, :scheduled, stage_id: stage.id)
+ end
+
+ it 'updates status to scheduled' do
+ expect { stage.update_status }
+ .to change { stage.reload.status }
+ .to 'scheduled'
+ end
+ end
+
context 'when stage is skipped because is empty' do
it 'updates status to skipped' do
expect { stage.update_status }
@@ -188,6 +200,18 @@ describe Ci::Stage, :models do
end
end
+ describe '#delay' do
+ subject { stage.delay }
+
+ let(:stage) { create(:ci_stage_entity, status: :created) }
+
+ it 'updates stage status' do
+ subject
+
+ expect(stage).to be_scheduled
+ end
+ end
+
describe '#position' do
context 'when stage has been imported and does not have position index set' do
before do
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index e5b2bdc8a4e..2c37cd20ecc 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -47,5 +47,19 @@ describe Clusters::Applications::Helm do
cert = OpenSSL::X509::Certificate.new(subject.files[:'cert.pem'])
expect(cert.not_after).to be > 999.years.from_now
end
+
+ describe 'rbac' do
+ context 'non rbac cluster' do
+ it { expect(subject).not_to be_rbac }
+ end
+
+ context 'rbac cluster' do
+ before do
+ helm.cluster.platform_kubernetes.rbac!
+ end
+
+ it { expect(subject).to be_rbac }
+ end
+ end
end
end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index 21f75ced8c3..6b0b23eeab3 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -4,7 +4,8 @@ describe Clusters::Applications::Ingress do
let(:ingress) { create(:clusters_applications_ingress) }
include_examples 'cluster application core specs', :clusters_applications_ingress
- include_examples 'cluster application status specs', :cluster_application_ingress
+ include_examples 'cluster application status specs', :clusters_applications_ingress
+ include_examples 'cluster application helm specs', :clusters_applications_knative
before do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
@@ -88,9 +89,18 @@ describe Clusters::Applications::Ingress do
expect(subject.name).to eq('ingress')
expect(subject.chart).to eq('stable/nginx-ingress')
expect(subject.version).to eq('0.23.0')
+ expect(subject).not_to be_rbac
expect(subject.files).to eq(ingress.files)
end
+ context 'on a rbac enabled cluster' do
+ before do
+ ingress.cluster.platform_kubernetes.rbac!
+ end
+
+ it { is_expected.to be_rbac }
+ end
+
context 'application failed to install previously' do
let(:ingress) { create(:clusters_applications_ingress, :errored, version: 'nginx') }
@@ -112,28 +122,5 @@ describe Clusters::Applications::Ingress do
expect(values).to include('stats')
expect(values).to include('podAnnotations')
end
-
- context 'when the helm application does not have a ca_cert' do
- before do
- application.cluster.application_helm.ca_cert = nil
- end
-
- it 'should not include cert files' do
- expect(subject[:'ca.pem']).not_to be_present
- expect(subject[:'cert.pem']).not_to be_present
- expect(subject[:'key.pem']).not_to be_present
- end
- end
-
- it 'should include cert files' do
- expect(subject[:'ca.pem']).to be_present
- expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
-
- expect(subject[:'cert.pem']).to be_present
- expect(subject[:'key.pem']).to be_present
-
- cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
- expect(cert.not_after).to be < 60.minutes.from_now
- end
end
end
diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb
index 027b732681b..faaabafddb7 100644
--- a/spec/models/clusters/applications/jupyter_spec.rb
+++ b/spec/models/clusters/applications/jupyter_spec.rb
@@ -2,6 +2,7 @@ require 'rails_helper'
describe Clusters::Applications::Jupyter do
include_examples 'cluster application core specs', :clusters_applications_jupyter
+ include_examples 'cluster application helm specs', :clusters_applications_knative
it { is_expected.to belong_to(:oauth_application) }
@@ -51,10 +52,19 @@ describe Clusters::Applications::Jupyter do
expect(subject.name).to eq('jupyter')
expect(subject.chart).to eq('jupyter/jupyterhub')
expect(subject.version).to eq('v0.6')
+ expect(subject).not_to be_rbac
expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/')
expect(subject.files).to eq(jupyter.files)
end
+ context 'on a rbac enabled cluster' do
+ before do
+ jupyter.cluster.platform_kubernetes.rbac!
+ end
+
+ it { is_expected.to be_rbac }
+ end
+
context 'application failed to install previously' do
let(:jupyter) { create(:clusters_applications_jupyter, :errored, version: '0.0.1') }
@@ -70,37 +80,21 @@ describe Clusters::Applications::Jupyter do
subject { application.files }
- it 'should include cert files' do
- expect(subject[:'ca.pem']).to be_present
- expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
-
- expect(subject[:'cert.pem']).to be_present
- expect(subject[:'key.pem']).to be_present
-
- cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
- expect(cert.not_after).to be < 60.minutes.from_now
- end
-
- context 'when the helm application does not have a ca_cert' do
- before do
- application.cluster.application_helm.ca_cert = nil
- end
-
- it 'should not include cert files' do
- expect(subject[:'ca.pem']).not_to be_present
- expect(subject[:'cert.pem']).not_to be_present
- expect(subject[:'key.pem']).not_to be_present
- end
- end
-
it 'should include valid values' do
expect(values).to include('ingress')
expect(values).to include('hub')
expect(values).to include('rbac')
expect(values).to include('proxy')
expect(values).to include('auth')
+ expect(values).to include('singleuser')
expect(values).to match(/clientId: '?#{application.oauth_application.uid}/)
expect(values).to match(/callbackUrl: '?#{application.callback_url}/)
end
+
+ context 'when cluster belongs to a project' do
+ it 'sets GitLab project id' do
+ expect(values).to match(/GITLAB_CLUSTER_ID: '?#{application.cluster.id}/)
+ end
+ end
end
end
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
new file mode 100644
index 00000000000..be2a91d566b
--- /dev/null
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -0,0 +1,77 @@
+require 'rails_helper'
+
+describe Clusters::Applications::Knative do
+ let(:knative) { create(:clusters_applications_knative) }
+
+ include_examples 'cluster application core specs', :clusters_applications_knative
+ include_examples 'cluster application status specs', :clusters_applications_knative
+ include_examples 'cluster application helm specs', :clusters_applications_knative
+
+ describe '.installed' do
+ subject { described_class.installed }
+
+ let!(:cluster) { create(:clusters_applications_knative, :installed) }
+
+ before do
+ create(:clusters_applications_knative, :errored)
+ end
+
+ it { is_expected.to contain_exactly(cluster) }
+ end
+
+ describe '#make_installing!' do
+ before do
+ application.make_installing!
+ end
+
+ context 'application install previously errored with older version' do
+ let(:application) { create(:clusters_applications_knative, :scheduled, version: '0.1.3') }
+
+ it 'updates the application version' do
+ expect(application.reload.version).to eq('0.1.3')
+ end
+ end
+ end
+
+ describe '#make_installed' do
+ subject { described_class.installed }
+
+ let!(:cluster) { create(:clusters_applications_knative, :installed) }
+
+ before do
+ create(:clusters_applications_knative, :errored)
+ end
+
+ it { is_expected.to contain_exactly(cluster) }
+ end
+
+ describe '#install_command' do
+ subject { knative.install_command }
+
+ it 'should be an instance of Helm::InstallCommand' do
+ expect(subject).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand)
+ end
+
+ it 'should be initialized with knative arguments' do
+ expect(subject.name).to eq('knative')
+ expect(subject.chart).to eq('knative/knative')
+ expect(subject.version).to eq('0.1.3')
+ expect(subject.files).to eq(knative.files)
+ end
+ end
+
+ describe '#files' do
+ let(:application) { knative }
+ let(:values) { subject[:'values.yaml'] }
+
+ subject { application.files }
+
+ it 'should include knative specific keys in the values.yaml file' do
+ expect(values).to include('domain')
+ end
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:hostname) }
+ end
+end
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index 7454be3ab2f..b5aa1dcece5 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -1,8 +1,11 @@
require 'rails_helper'
describe Clusters::Applications::Prometheus do
+ include KubernetesHelpers
+
include_examples 'cluster application core specs', :clusters_applications_prometheus
- include_examples 'cluster application status specs', :cluster_application_prometheus
+ include_examples 'cluster application status specs', :clusters_applications_prometheus
+ include_examples 'cluster application helm specs', :clusters_applications_knative
describe '.installed' do
subject { described_class.installed }
@@ -32,7 +35,7 @@ describe Clusters::Applications::Prometheus do
describe 'transition to installed' do
let(:project) { create(:project) }
- let(:cluster) { create(:cluster, projects: [project]) }
+ let(:cluster) { create(:cluster, :with_installed_helm, projects: [project]) }
let(:prometheus_service) { double('prometheus_service') }
subject { create(:clusters_applications_prometheus, :installing, cluster: cluster) }
@@ -107,26 +110,20 @@ describe Clusters::Applications::Prometheus do
end
context 'cluster has kubeclient' do
- let(:kubernetes_url) { 'http://example.com' }
- let(:k8s_discover_response) do
- {
- resources: [
- {
- name: 'service',
- kind: 'Service'
- }
- ]
- }
- end
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+ let(:kubernetes_url) { subject.cluster.platform_kubernetes.api_url }
+ let(:kube_client) { subject.cluster.kubeclient.core_client }
- let(:kube_client) { Kubeclient::Client.new(kubernetes_url) }
-
- let(:cluster) { create(:cluster) }
subject { create(:clusters_applications_prometheus, cluster: cluster) }
before do
- allow(kube_client.rest_client).to receive(:get).and_return(k8s_discover_response.to_json)
- allow(subject.cluster).to receive(:kubeclient).and_return(kube_client)
+ subject.cluster.platform_kubernetes.namespace = 'a-namespace'
+ stub_kubeclient_discover(cluster.platform_kubernetes.api_url)
+
+ create(:cluster_kubernetes_namespace,
+ cluster: cluster,
+ cluster_project: cluster.cluster_project,
+ project: cluster.cluster_project.project)
end
it 'creates proxy prometheus rest client' do
@@ -134,7 +131,7 @@ describe Clusters::Applications::Prometheus do
end
it 'creates proper url' do
- expect(subject.prometheus_client.url).to eq('http://example.com/api/v1/namespaces/gitlab-managed-apps/service/prometheus-prometheus-server:80/proxy')
+ expect(subject.prometheus_client.url).to eq("#{kubernetes_url}/api/v1/namespaces/gitlab-managed-apps/services/prometheus-prometheus-server:80/proxy")
end
it 'copies options and headers from kube client to proxy client' do
@@ -154,20 +151,26 @@ describe Clusters::Applications::Prometheus do
end
describe '#install_command' do
- let(:kubeclient) { double('kubernetes client') }
let(:prometheus) { create(:clusters_applications_prometheus) }
- it 'returns an instance of Gitlab::Kubernetes::Helm::InstallCommand' do
- expect(prometheus.install_command).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand)
- end
+ subject { prometheus.install_command }
+
+ it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) }
it 'should be initialized with 3 arguments' do
- command = prometheus.install_command
+ expect(subject.name).to eq('prometheus')
+ expect(subject.chart).to eq('stable/prometheus')
+ expect(subject.version).to eq('6.7.3')
+ expect(subject).not_to be_rbac
+ expect(subject.files).to eq(prometheus.files)
+ end
+
+ context 'on a rbac enabled cluster' do
+ before do
+ prometheus.cluster.platform_kubernetes.rbac!
+ end
- expect(command.name).to eq('prometheus')
- expect(command.chart).to eq('stable/prometheus')
- expect(command.version).to eq('6.7.3')
- expect(command.files).to eq(prometheus.files)
+ it { is_expected.to be_rbac }
end
context 'application failed to install previously' do
@@ -185,29 +188,6 @@ describe Clusters::Applications::Prometheus do
subject { application.files }
- it 'should include cert files' do
- expect(subject[:'ca.pem']).to be_present
- expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
-
- expect(subject[:'cert.pem']).to be_present
- expect(subject[:'key.pem']).to be_present
-
- cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
- expect(cert.not_after).to be < 60.minutes.from_now
- end
-
- context 'when the helm application does not have a ca_cert' do
- before do
- application.cluster.application_helm.ca_cert = nil
- end
-
- it 'should not include cert files' do
- expect(subject[:'ca.pem']).not_to be_present
- expect(subject[:'cert.pem']).not_to be_present
- expect(subject[:'key.pem']).not_to be_present
- end
- end
-
it 'should include prometheus valid values' do
expect(values).to include('alertmanager')
expect(values).to include('kubeStateMetrics')
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index d84f125e246..052cfdbc4b1 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -4,7 +4,8 @@ describe Clusters::Applications::Runner do
let(:ci_runner) { create(:ci_runner) }
include_examples 'cluster application core specs', :clusters_applications_runner
- include_examples 'cluster application status specs', :cluster_application_runner
+ include_examples 'cluster application status specs', :clusters_applications_runner
+ include_examples 'cluster application helm specs', :clusters_applications_knative
it { is_expected.to belong_to(:runner) }
@@ -17,7 +18,7 @@ describe Clusters::Applications::Runner do
let(:application) { create(:clusters_applications_runner, :scheduled, version: '0.1.30') }
it 'updates the application version' do
- expect(application.reload.version).to eq('0.1.31')
+ expect(application.reload.version).to eq('0.1.35')
end
end
end
@@ -45,16 +46,25 @@ describe Clusters::Applications::Runner do
it 'should be initialized with 4 arguments' do
expect(subject.name).to eq('runner')
expect(subject.chart).to eq('runner/gitlab-runner')
- expect(subject.version).to eq('0.1.31')
+ expect(subject.version).to eq('0.1.35')
+ expect(subject).not_to be_rbac
expect(subject.repository).to eq('https://charts.gitlab.io')
expect(subject.files).to eq(gitlab_runner.files)
end
+ context 'on a rbac enabled cluster' do
+ before do
+ gitlab_runner.cluster.platform_kubernetes.rbac!
+ end
+
+ it { is_expected.to be_rbac }
+ end
+
context 'application failed to install previously' do
let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') }
it 'should be initialized with the locked version' do
- expect(subject.version).to eq('0.1.31')
+ expect(subject.version).to eq('0.1.35')
end
end
end
@@ -65,29 +75,6 @@ describe Clusters::Applications::Runner do
subject { application.files }
- it 'should include cert files' do
- expect(subject[:'ca.pem']).to be_present
- expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
-
- expect(subject[:'cert.pem']).to be_present
- expect(subject[:'key.pem']).to be_present
-
- cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
- expect(cert.not_after).to be < 60.minutes.from_now
- end
-
- context 'when the helm application does not have a ca_cert' do
- before do
- application.cluster.application_helm.ca_cert = nil
- end
-
- it 'should not include cert files' do
- expect(subject[:'ca.pem']).not_to be_present
- expect(subject[:'cert.pem']).not_to be_present
- expect(subject[:'key.pem']).not_to be_present
- end
- end
-
it 'should include runner valid values' do
expect(values).to include('concurrent')
expect(values).to include('checkInterval')
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 6f66515b45f..98d7e799d67 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -1,18 +1,33 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Clusters::Cluster do
it { is_expected.to belong_to(:user) }
+ it { is_expected.to have_many(:cluster_projects) }
it { is_expected.to have_many(:projects) }
+ it { is_expected.to have_many(:cluster_groups) }
+ it { is_expected.to have_many(:groups) }
it { is_expected.to have_one(:provider_gcp) }
it { is_expected.to have_one(:platform_kubernetes) }
it { is_expected.to have_one(:application_helm) }
it { is_expected.to have_one(:application_ingress) }
it { is_expected.to have_one(:application_prometheus) }
it { is_expected.to have_one(:application_runner) }
+ it { is_expected.to have_many(:kubernetes_namespaces) }
+ it { is_expected.to have_one(:kubernetes_namespace) }
+ it { is_expected.to have_one(:cluster_project) }
+
it { is_expected.to delegate_method(:status).to(:provider) }
it { is_expected.to delegate_method(:status_reason).to(:provider) }
it { is_expected.to delegate_method(:status_name).to(:provider) }
it { is_expected.to delegate_method(:on_creation?).to(:provider) }
+ it { is_expected.to delegate_method(:active?).to(:platform_kubernetes).with_prefix }
+ it { is_expected.to delegate_method(:rbac?).to(:platform_kubernetes).with_prefix }
+ it { is_expected.to delegate_method(:available?).to(:application_helm).with_prefix }
+ it { is_expected.to delegate_method(:available?).to(:application_ingress).with_prefix }
+ it { is_expected.to delegate_method(:available?).to(:application_prometheus).with_prefix }
+
it { is_expected.to respond_to :project }
describe '.enabled' do
@@ -167,6 +182,53 @@ describe Clusters::Cluster do
it { expect(cluster.update(enabled: false)).to be_truthy }
end
end
+
+ describe 'cluster_type validations' do
+ let(:instance_cluster) { create(:cluster, :instance) }
+ let(:group_cluster) { create(:cluster, :group) }
+ let(:project_cluster) { create(:cluster, :project) }
+
+ it 'validates presence' do
+ cluster = build(:cluster, :project, cluster_type: nil)
+
+ expect(cluster).not_to be_valid
+ expect(cluster.errors.full_messages).to include("Cluster type can't be blank")
+ end
+
+ context 'project_type cluster' do
+ it 'does not allow setting group' do
+ project_cluster.groups << build(:group)
+
+ expect(project_cluster).not_to be_valid
+ expect(project_cluster.errors.full_messages).to include('Cluster cannot have groups assigned')
+ end
+ end
+
+ context 'group_type cluster' do
+ it 'does not allow setting project' do
+ group_cluster.projects << build(:project)
+
+ expect(group_cluster).not_to be_valid
+ expect(group_cluster.errors.full_messages).to include('Cluster cannot have projects assigned')
+ end
+ end
+
+ context 'instance_type cluster' do
+ it 'does not allow setting group' do
+ instance_cluster.groups << build(:group)
+
+ expect(instance_cluster).not_to be_valid
+ expect(instance_cluster.errors.full_messages).to include('Cluster cannot have groups assigned')
+ end
+
+ it 'does not allow setting project' do
+ instance_cluster.projects << build(:project)
+
+ expect(instance_cluster).not_to be_valid
+ expect(instance_cluster.errors.full_messages).to include('Cluster cannot have projects assigned')
+ end
+ end
+ end
end
describe '#provider' do
@@ -218,6 +280,23 @@ describe Clusters::Cluster do
end
end
+ describe '#group' do
+ subject { cluster.group }
+
+ context 'when cluster belongs to a group' do
+ let(:cluster) { create(:cluster, :group) }
+ let(:group) { cluster.groups.first }
+
+ it { is_expected.to eq(group) }
+ end
+
+ context 'when cluster does not belong to any group' do
+ let(:cluster) { create(:cluster) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
describe '#applications' do
set(:cluster) { create(:cluster) }
@@ -235,9 +314,10 @@ describe Clusters::Cluster do
let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) }
let!(:runner) { create(:clusters_applications_runner, cluster: cluster) }
let!(:jupyter) { create(:clusters_applications_jupyter, cluster: cluster) }
+ let!(:knative) { create(:clusters_applications_knative, cluster: cluster) }
it 'returns a list of created applications' do
- is_expected.to contain_exactly(helm, ingress, prometheus, runner, jupyter)
+ is_expected.to contain_exactly(helm, ingress, prometheus, runner, jupyter, knative)
end
end
end
@@ -263,4 +343,26 @@ describe Clusters::Cluster do
it { is_expected.to eq(false) }
end
end
+
+ describe '#allow_user_defined_namespace?' do
+ let(:cluster) { create(:cluster, :provided_by_gcp) }
+
+ subject { cluster.allow_user_defined_namespace? }
+
+ context 'project type cluster' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'group type cluster' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, :group) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'instance type cluster' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, :instance) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
end
diff --git a/spec/models/clusters/group_spec.rb b/spec/models/clusters/group_spec.rb
new file mode 100644
index 00000000000..ba145342cb8
--- /dev/null
+++ b/spec/models/clusters/group_spec.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Group do
+ it { is_expected.to belong_to(:cluster) }
+ it { is_expected.to belong_to(:group) }
+end
diff --git a/spec/models/clusters/kubernetes_namespace_spec.rb b/spec/models/clusters/kubernetes_namespace_spec.rb
new file mode 100644
index 00000000000..56c98d016c9
--- /dev/null
+++ b/spec/models/clusters/kubernetes_namespace_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::KubernetesNamespace, type: :model do
+ it { is_expected.to belong_to(:cluster_project) }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:cluster) }
+ it { is_expected.to have_one(:platform_kubernetes) }
+
+ describe 'has_service_account_token' do
+ subject { described_class.has_service_account_token }
+
+ context 'namespace has service_account_token' do
+ let!(:namespace) { create(:cluster_kubernetes_namespace, :with_token) }
+
+ it { is_expected.to include(namespace) }
+ end
+
+ context 'namespace has no service_account_token' do
+ let!(:namespace) { create(:cluster_kubernetes_namespace) }
+
+ it { is_expected.not_to include(namespace) }
+ end
+ end
+
+ describe 'namespace uniqueness validation' do
+ let(:cluster_project) { create(:cluster_project) }
+ let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace, namespace: 'my-namespace') }
+
+ subject { kubernetes_namespace }
+
+ context 'when cluster is using the namespace' do
+ before do
+ create(:cluster_kubernetes_namespace,
+ cluster: kubernetes_namespace.cluster,
+ namespace: 'my-namespace')
+ end
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when cluster is not using the namespace' do
+ it { is_expected.to be_valid }
+ end
+ end
+
+ describe '#set_defaults' do
+ let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace) }
+ let(:cluster) { kubernetes_namespace.cluster }
+ let(:platform) { kubernetes_namespace.platform_kubernetes }
+
+ subject { kubernetes_namespace.set_defaults }
+
+ describe '#namespace' do
+ before do
+ platform.update_column(:namespace, namespace)
+ end
+
+ context 'when platform has a namespace assigned' do
+ let(:namespace) { 'platform-namespace' }
+
+ it 'should copy the namespace' do
+ subject
+
+ expect(kubernetes_namespace.namespace).to eq('platform-namespace')
+ end
+ end
+
+ context 'when platform does not have namespace assigned' do
+ let(:project) { kubernetes_namespace.project }
+ let(:namespace) { nil }
+ let(:project_slug) { "#{project.path}-#{project.id}" }
+
+ it 'should fallback to project namespace' do
+ subject
+
+ expect(kubernetes_namespace.namespace).to eq(project_slug)
+ end
+ end
+ end
+
+ describe '#service_account_name' do
+ let(:service_account_name) { "#{kubernetes_namespace.namespace}-service-account" }
+
+ it 'should set a service account name based on namespace' do
+ subject
+
+ expect(kubernetes_namespace.service_account_name).to eq(service_account_name)
+ end
+ end
+ end
+
+ describe '#predefined_variables' do
+ let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, service_account_token: token) }
+ let(:cluster) { create(:cluster, :project, platform_kubernetes: platform) }
+ let(:platform) { create(:cluster_platform_kubernetes, api_url: api_url, ca_cert: ca_pem, token: token) }
+
+ let(:api_url) { 'https://kube.domain.com' }
+ let(:ca_pem) { 'CA PEM DATA' }
+ let(:token) { 'token' }
+
+ let(:kubeconfig) do
+ config_file = expand_fixture_path('config/kubeconfig.yml')
+ config = YAML.safe_load(File.read(config_file))
+ config.dig('users', 0, 'user')['token'] = token
+ config.dig('contexts', 0, 'context')['namespace'] = kubernetes_namespace.namespace
+ config.dig('clusters', 0, 'cluster')['certificate-authority-data'] =
+ Base64.strict_encode64(ca_pem)
+
+ YAML.dump(config)
+ end
+
+ it 'sets the variables' do
+ expect(kubernetes_namespace.predefined_variables).to include(
+ { key: 'KUBE_SERVICE_ACCOUNT', value: kubernetes_namespace.service_account_name, public: true },
+ { key: 'KUBE_NAMESPACE', value: kubernetes_namespace.namespace, public: true },
+ { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false },
+ { key: 'KUBECONFIG', value: kubeconfig, public: false, file: true }
+ )
+ end
+ end
+end
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index ab7f89f9bf4..99fd6ccc4d8 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -9,6 +9,15 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { is_expected.to be_kind_of(ReactiveCaching) }
it { is_expected.to respond_to :ca_pem }
+ it { is_expected.to validate_exclusion_of(:namespace).in_array(%w(gitlab-managed-apps)) }
+ it { is_expected.to validate_presence_of(:api_url) }
+ it { is_expected.to validate_presence_of(:token) }
+
+ it { is_expected.to delegate_method(:project).to(:cluster) }
+ it { is_expected.to delegate_method(:enabled?).to(:cluster) }
+ it { is_expected.to delegate_method(:managed?).to(:cluster) }
+ it { is_expected.to delegate_method(:kubernetes_namespace).to(:cluster) }
+
describe 'before_validation' do
context 'when namespace includes upper case' do
let(:kubernetes) { create(:cluster_platform_kubernetes, :configured, namespace: namespace) }
@@ -49,6 +58,18 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { is_expected.to be_truthy }
end
+
+ context 'for group cluster' do
+ let(:namespace) { 'namespace-123' }
+ let(:cluster) { build(:cluster, :group, :provided_by_user) }
+ let(:kubernetes) { cluster.platform_kubernetes }
+
+ before do
+ kubernetes.namespace = namespace
+ end
+
+ it { is_expected.to be_falsey }
+ end
end
context 'when validates api_url' do
@@ -90,78 +111,141 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { expect(kubernetes.save).to be_falsey }
end
end
- end
- describe '#actual_namespace' do
- subject { kubernetes.actual_namespace }
+ describe 'when using reserved namespaces' do
+ subject { build(:cluster_platform_kubernetes, namespace: namespace) }
- let!(:cluster) { create(:cluster, :project, platform_kubernetes: kubernetes) }
- let(:project) { cluster.project }
- let(:kubernetes) { create(:cluster_platform_kubernetes, :configured, namespace: namespace) }
+ context 'when no namespace is manually assigned' do
+ let(:namespace) { nil }
- context 'when namespace is present' do
- let(:namespace) { 'namespace-123' }
+ it { is_expected.to be_valid }
+ end
- it { is_expected.to eq(namespace) }
+ context 'when no reserved namespace is assigned' do
+ let(:namespace) { 'my-namespace' }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when reserved namespace is assigned' do
+ let(:namespace) { 'gitlab-managed-apps' }
+
+ it { is_expected.not_to be_valid }
+ end
end
+ end
- context 'when namespace is not present' do
- let(:namespace) { nil }
+ describe '#kubeclient' do
+ let(:cluster) { create(:cluster, :project) }
+ let(:kubernetes) { build(:cluster_platform_kubernetes, :configured, namespace: 'a-namespace', cluster: cluster) }
- it { is_expected.to eq("#{project.path}-#{project.id}") }
+ subject { kubernetes.kubeclient }
+
+ before do
+ create(:cluster_kubernetes_namespace,
+ cluster: kubernetes.cluster,
+ cluster_project: kubernetes.cluster.cluster_project,
+ project: kubernetes.cluster.cluster_project.project)
end
+
+ it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::KubeClient) }
end
- describe '#default_namespace' do
- subject { kubernetes.send(:default_namespace) }
+ describe '#rbac?' do
+ subject { kubernetes.rbac? }
- let(:kubernetes) { create(:cluster_platform_kubernetes, :configured) }
+ let(:kubernetes) { build(:cluster_platform_kubernetes, :configured) }
- context 'when cluster belongs to a project' do
- let!(:cluster) { create(:cluster, :project, platform_kubernetes: kubernetes) }
- let(:project) { cluster.project }
+ context 'when authorization type is rbac' do
+ let(:kubernetes) { build(:cluster_platform_kubernetes, :rbac_enabled, :configured) }
- it { is_expected.to eq("#{project.path}-#{project.id}") }
+ it { is_expected.to be_truthy }
end
- context 'when cluster belongs to nothing' do
- let!(:cluster) { create(:cluster, platform_kubernetes: kubernetes) }
+ context 'when authorization type is nil' do
+ it { is_expected.to be_falsey }
+ end
+ end
- it { is_expected.to be_nil }
+ describe '#actual_namespace' do
+ let(:cluster) { create(:cluster, :project) }
+ let(:project) { cluster.project }
+
+ let(:platform) do
+ create(:cluster_platform_kubernetes,
+ cluster: cluster,
+ namespace: namespace)
+ end
+
+ subject { platform.actual_namespace }
+
+ context 'with a namespace assigned' do
+ let(:namespace) { 'namespace-123' }
+
+ it { is_expected.to eq(namespace) }
+ end
+
+ context 'with no namespace assigned' do
+ let(:namespace) { nil }
+
+ context 'when kubernetes namespace is present' do
+ let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
+
+ before do
+ kubernetes_namespace
+ end
+
+ it { is_expected.to eq(kubernetes_namespace.namespace) }
+ end
+
+ context 'when kubernetes namespace is not present' do
+ it { is_expected.to eq("#{project.path}-#{project.id}") }
+ end
end
end
describe '#predefined_variables' do
let!(:cluster) { create(:cluster, :project, platform_kubernetes: kubernetes) }
- let(:kubernetes) { create(:cluster_platform_kubernetes, api_url: api_url, ca_cert: ca_pem, token: token) }
+ let(:kubernetes) { create(:cluster_platform_kubernetes, api_url: api_url, ca_cert: ca_pem) }
let(:api_url) { 'https://kube.domain.com' }
let(:ca_pem) { 'CA PEM DATA' }
- let(:token) { 'token' }
- let(:kubeconfig) do
- config_file = expand_fixture_path('config/kubeconfig.yml')
- config = YAML.load(File.read(config_file))
- config.dig('users', 0, 'user')['token'] = token
- config.dig('contexts', 0, 'context')['namespace'] = namespace
- config.dig('clusters', 0, 'cluster')['certificate-authority-data'] =
- Base64.strict_encode64(ca_pem)
-
- YAML.dump(config)
- end
+ subject { kubernetes.predefined_variables(project: cluster.project) }
shared_examples 'setting variables' do
it 'sets the variables' do
- expect(kubernetes.predefined_variables).to include(
+ expect(subject).to include(
{ key: 'KUBE_URL', value: api_url, public: true },
- { key: 'KUBE_TOKEN', value: token, public: false },
- { key: 'KUBE_NAMESPACE', value: namespace, public: true },
- { key: 'KUBECONFIG', value: kubeconfig, public: false, file: true },
{ key: 'KUBE_CA_PEM', value: ca_pem, public: true },
{ key: 'KUBE_CA_PEM_FILE', value: ca_pem, public: true, file: true }
)
end
end
+ context 'kubernetes namespace is created with no service account token' do
+ let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
+
+ it_behaves_like 'setting variables'
+
+ it 'sets KUBE_TOKEN' do
+ expect(subject).to include(
+ { key: 'KUBE_TOKEN', value: kubernetes.token, public: false }
+ )
+ end
+ end
+
+ context 'kubernetes namespace is created with no service account token' do
+ let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster) }
+
+ it_behaves_like 'setting variables'
+
+ it 'sets KUBE_TOKEN' do
+ expect(subject).to include(
+ { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false }
+ )
+ end
+ end
+
context 'namespace is provided' do
let(:namespace) { 'my-project' }
@@ -170,6 +254,12 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
it_behaves_like 'setting variables'
+
+ it 'sets KUBE_TOKEN' do
+ expect(subject).to include(
+ { key: 'KUBE_TOKEN', value: kubernetes.token, public: false }
+ )
+ end
end
context 'no namespace provided' do
@@ -177,11 +267,10 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it_behaves_like 'setting variables'
- it 'sets the KUBE_NAMESPACE' do
- kube_namespace = kubernetes.predefined_variables.find { |h| h[:key] == 'KUBE_NAMESPACE' }
-
- expect(kube_namespace).not_to be_nil
- expect(kube_namespace[:value]).to match(/\A#{Gitlab::PathRegex::PATH_REGEX_STR}-\d+\z/)
+ it 'sets KUBE_TOKEN' do
+ expect(subject).to include(
+ { key: 'KUBE_TOKEN', value: kubernetes.token, public: false }
+ )
end
end
end
@@ -266,4 +355,27 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { is_expected.to include(pods: []) }
end
end
+
+ describe '#update_kubernetes_namespace' do
+ let(:cluster) { create(:cluster, :provided_by_gcp) }
+ let(:platform) { cluster.platform }
+
+ context 'when namespace is updated' do
+ it 'should call ConfigureWorker' do
+ expect(ClusterPlatformConfigureWorker).to receive(:perform_async).with(cluster.id).once
+
+ platform.namespace = 'new-namespace'
+ platform.save
+ end
+ end
+
+ context 'when namespace is not updated' do
+ it 'should not call ConfigureWorker' do
+ expect(ClusterPlatformConfigureWorker).not_to receive(:perform_async)
+
+ platform.username = "new-username"
+ platform.save
+ end
+ end
+ end
end
diff --git a/spec/models/clusters/project_spec.rb b/spec/models/clusters/project_spec.rb
index 7d75d6ab345..82ef5a23c18 100644
--- a/spec/models/clusters/project_spec.rb
+++ b/spec/models/clusters/project_spec.rb
@@ -3,4 +3,6 @@ require 'spec_helper'
describe Clusters::Project do
it { is_expected.to belong_to(:cluster) }
it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_many(:kubernetes_namespaces) }
+ it { is_expected.to have_one(:kubernetes_namespace) }
end
diff --git a/spec/models/clusters/providers/gcp_spec.rb b/spec/models/clusters/providers/gcp_spec.rb
index b38b5e6bcad..d134608b538 100644
--- a/spec/models/clusters/providers/gcp_spec.rb
+++ b/spec/models/clusters/providers/gcp_spec.rb
@@ -74,6 +74,24 @@ describe Clusters::Providers::Gcp do
end
end
+ describe '#legacy_abac?' do
+ let(:gcp) { build(:cluster_provider_gcp) }
+
+ subject { gcp }
+
+ it 'should default to true' do
+ is_expected.to be_legacy_abac
+ end
+
+ context 'legacy_abac is set to false' do
+ let(:gcp) { build(:cluster_provider_gcp, legacy_abac: false) }
+
+ it 'is false' do
+ is_expected.not_to be_legacy_abac
+ end
+ end
+ end
+
describe '#state_machine' do
context 'when any => [:created]' do
let(:gcp) { build(:cluster_provider_gcp, :creating) }
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 5157d8fc645..2a0039a0635 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -65,13 +65,14 @@ describe Commit do
key = "Commit:author:#{commit.author_email.downcase}"
- expect(RequestStore.store[key]).to eq(user)
+ expect(Gitlab::SafeRequestStore[key]).to eq(user)
expect(commit.author).to eq(user)
end
context 'using eager loading' do
let!(:alice) { create(:user, email: 'alice@example.com') }
let!(:bob) { create(:user, email: 'hunter2@example.com') }
+ let!(:jeff) { create(:user) }
let(:alice_commit) do
described_class.new(RepoHelpers.sample_commit, project).tap do |c|
@@ -93,7 +94,14 @@ describe Commit do
end
end
- let!(:commits) { [alice_commit, bob_commit, eve_commit] }
+ let(:jeff_commit) do
+ # The commit for Jeff uses his private commit email
+ described_class.new(RepoHelpers.sample_commit, project).tap do |c|
+ c.author_email = jeff.private_commit_email
+ end
+ end
+
+ let!(:commits) { [alice_commit, bob_commit, eve_commit, jeff_commit] }
before do
create(:email, user: bob, email: 'bob@example.com')
@@ -125,6 +133,20 @@ describe Commit do
expect(bob_commit.author).to eq(bob)
end
+ it "preloads the authors for Commits using a User's private commit Email" do
+ commits.each(&:lazy_author)
+
+ expect(jeff_commit.author).to eq(jeff)
+ end
+
+ it "preloads the authors for Commits using a User's outdated private commit Email" do
+ jeff.update!(username: 'new-username')
+
+ commits.each(&:lazy_author)
+
+ expect(jeff_commit.author).to eq(jeff)
+ end
+
it 'sets the author to Nil if an author could not be found for a Commit' do
commits.each(&:lazy_author)
@@ -225,6 +247,12 @@ eos
end
describe 'description' do
+ it 'returns no_commit_message when safe_message is blank' do
+ allow(commit).to receive(:safe_message).and_return(nil)
+
+ expect(commit.description).to eq('--no commit message')
+ end
+
it 'returns description of commit message if title less than 100 characters' do
message = <<eos
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit.
@@ -264,11 +292,11 @@ eos
let(:issue) { create :issue, project: project }
let(:other_project) { create(:project, :public) }
let(:other_issue) { create :issue, project: other_project }
- let(:commiter) { create :user }
+ let(:committer) { create :user }
before do
- project.add_developer(commiter)
- other_project.add_developer(commiter)
+ project.add_developer(committer)
+ other_project.add_developer(committer)
end
it 'detects issues that this commit is marked as closing' do
@@ -276,7 +304,7 @@ eos
allow(commit).to receive_messages(
safe_message: "Fixes ##{issue.iid} and #{ext_ref}",
- committer_email: commiter.email
+ committer_email: committer.email
)
expect(commit.closes_issues).to include(issue)
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index f3f2bc28d2c..917685399d4 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -129,6 +129,20 @@ describe CommitStatus do
end
end
+ describe '#cancel' do
+ subject { job.cancel }
+
+ context 'when status is scheduled' do
+ let(:job) { build(:commit_status, :scheduled) }
+
+ it 'updates the status' do
+ subject
+
+ expect(job).to be_canceled
+ end
+ end
+ end
+
describe '#auto_canceled?' do
subject { commit_status.auto_canceled? }
@@ -564,6 +578,12 @@ describe CommitStatus do
it_behaves_like 'commit status enqueued'
end
+
+ context 'when initial state is :scheduled' do
+ let(:commit_status) { create(:commit_status, :scheduled) }
+
+ it_behaves_like 'commit status enqueued'
+ end
end
describe '#present' do
diff --git a/spec/models/compare_spec.rb b/spec/models/compare_spec.rb
index 8e88bb81162..0bc3ee014e6 100644
--- a/spec/models/compare_spec.rb
+++ b/spec/models/compare_spec.rb
@@ -92,4 +92,33 @@ describe Compare do
expect(subject.diff_refs.head_sha).to eq(head_commit.id)
end
end
+
+ describe '#modified_paths' do
+ context 'changes are present' do
+ let(:raw_compare) do
+ Gitlab::Git::Compare.new(
+ project.repository.raw_repository, 'before-create-delete-modify-move', 'after-create-delete-modify-move'
+ )
+ end
+
+ it 'returns affected file paths, without duplication' do
+ expect(subject.modified_paths).to contain_exactly(*%w{
+ foo/for_move.txt
+ foo/bar/for_move.txt
+ foo/for_create.txt
+ foo/for_delete.txt
+ foo/for_edit.txt
+ })
+ end
+ end
+
+ context 'changes are absent' do
+ let(:start_commit) { sample_commit }
+ let(:head_commit) { sample_commit }
+
+ it 'returns empty array' do
+ expect(subject.modified_paths).to eq([])
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb
index 76f734079b7..7d617cb7b19 100644
--- a/spec/models/concerns/avatarable_spec.rb
+++ b/spec/models/concerns/avatarable_spec.rb
@@ -12,6 +12,26 @@ describe Avatarable do
stub_config_setting(relative_url_root: relative_url_root)
end
+ describe '#update' do
+ let(:validator) { project._validators[:avatar].detect { |v| v.is_a?(FileSizeValidator) } }
+
+ context 'when avatar changed' do
+ it 'validates the file size' do
+ expect(validator).to receive(:validate_each).and_call_original
+
+ project.update(avatar: 'uploads/avatar.png')
+ end
+ end
+
+ context 'when avatar was not changed' do
+ it 'skips validation of file size' do
+ expect(validator).not_to receive(:validate_each)
+
+ project.update(name: 'Hello world')
+ end
+ end
+ end
+
describe '#avatar_path' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb
index a980cff28fb..5713106418d 100644
--- a/spec/models/concerns/awardable_spec.rb
+++ b/spec/models/concerns/awardable_spec.rb
@@ -24,13 +24,29 @@ describe Awardable do
end
end
- describe ".awarded" do
+ describe "#awarded" do
it "filters by user and emoji name" do
expect(Issue.awarded(award_emoji.user, "thumbsup")).to be_empty
expect(Issue.awarded(award_emoji.user, "thumbsdown")).to eq [issue]
expect(Issue.awarded(award_emoji2.user, "thumbsup")).to eq [issue2]
expect(Issue.awarded(award_emoji2.user, "thumbsdown")).to be_empty
end
+
+ it "filters by user and any emoji" do
+ issue3 = create(:issue)
+ create(:award_emoji, awardable: issue3, name: "star", user: award_emoji.user)
+ create(:award_emoji, awardable: issue3, name: "star", user: award_emoji2.user)
+
+ expect(Issue.awarded(award_emoji.user)).to contain_exactly(issue, issue3)
+ expect(Issue.awarded(award_emoji2.user)).to contain_exactly(issue2, issue3)
+ end
+ end
+
+ describe "#not_awarded" do
+ it "returns issues not awarded by user" do
+ expect(Issue.not_awarded(award_emoji.user)).to eq [issue2]
+ expect(Issue.not_awarded(award_emoji2.user)).to eq [issue]
+ end
end
end
@@ -53,21 +69,14 @@ describe Awardable do
issue.project.add_guest(user)
end
- it 'does not allow upvoting or downvoting your own issue' do
- issue.update!(author: user)
-
- expect(issue.user_can_award?(user, AwardEmoji::DOWNVOTE_NAME)).to be_falsy
- expect(issue.user_can_award?(user, AwardEmoji::UPVOTE_NAME)).to be_falsy
- end
-
it 'is truthy when the user is allowed to award emoji' do
- expect(issue.user_can_award?(user, AwardEmoji::UPVOTE_NAME)).to be_truthy
+ expect(issue.user_can_award?(user)).to be_truthy
end
it 'is falsy when the project is archived' do
issue.project.update!(archived: true)
- expect(issue.user_can_award?(user, AwardEmoji::UPVOTE_NAME)).to be_falsy
+ expect(issue.user_can_award?(user)).to be_falsy
end
end
diff --git a/spec/models/concerns/blob_language_from_git_attributes_spec.rb b/spec/models/concerns/blob_language_from_git_attributes_spec.rb
new file mode 100644
index 00000000000..7f05073b08e
--- /dev/null
+++ b/spec/models/concerns/blob_language_from_git_attributes_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe BlobLanguageFromGitAttributes do
+ include FakeBlobHelpers
+
+ let(:project) { build(:project, :repository) }
+
+ describe '#language_from_gitattributes' do
+ subject(:blob) { fake_blob(path: 'file.md') }
+
+ it 'returns return value from gitattribute' do
+ expect(blob.project.repository).to receive(:gitattribute).with(blob.path, 'gitlab-language').and_return('erb?parent=json')
+
+ expect(blob.language_from_gitattributes).to eq('erb?parent=json')
+ end
+
+ it 'returns nil if project is absent' do
+ allow(blob).to receive(:project).and_return(nil)
+
+ expect(blob.language_from_gitattributes).to eq(nil)
+ end
+ end
+end
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index da26d802688..f8d50e89d40 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -331,11 +331,12 @@ describe CacheMarkdownField do
end
context 'with a project' do
- let(:thing) { thing_subclass(:project).new(foo: markdown, foo_html: html, project: :project_value) }
+ let(:project) { create(:project, group: create(:group)) }
+ let(:thing) { thing_subclass(:project).new(foo: markdown, foo_html: html, project: project) }
it 'sets the project in the context' do
is_expected.to have_key(:project)
- expect(context[:project]).to eq(:project_value)
+ expect(context[:project]).to eq(project)
end
it 'invalidates the cache when project changes' do
diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb
index f8c2e29fadd..827fbc9d7d5 100644
--- a/spec/models/concerns/cacheable_attributes_spec.rb
+++ b/spec/models/concerns/cacheable_attributes_spec.rb
@@ -41,7 +41,7 @@ describe CacheableAttributes do
expect(minimal_test_class.current_without_cache).to eq(minimal_test_class.last)
end
- it 'can be overriden' do
+ it 'can be overridden' do
minimal_test_class.define_singleton_method(:current_without_cache) do
first
end
@@ -64,7 +64,7 @@ describe CacheableAttributes do
context 'with defaults defined' do
include_context 'with defaults'
- it 'can be overriden' do
+ it 'can be overridden' do
expect(minimal_test_class.defaults).to eq({ foo: 'a', bar: 'b', baz: 'c' })
end
end
diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb
index 5c0dfaeb4d3..1bf6c9b3404 100644
--- a/spec/models/concerns/case_sensitivity_spec.rb
+++ b/spec/models/concerns/case_sensitivity_spec.rb
@@ -3,186 +3,50 @@ require 'spec_helper'
describe CaseSensitivity do
describe '.iwhere' do
let(:connection) { ActiveRecord::Base.connection }
- let(:model) { Class.new { include CaseSensitivity } }
-
- describe 'using PostgreSQL' do
- before do
- allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
- allow(Gitlab::Database).to receive(:mysql?).and_return(false)
- end
-
- describe 'with a single column/value pair' do
- it 'returns the criteria for a column and a value' do
- criteria = double(:criteria)
-
- expect(connection).to receive(:quote_table_name)
- .with(:foo)
- .and_return('"foo"')
-
- expect(model).to receive(:where)
- .with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar')
- .and_return(criteria)
-
- expect(model.iwhere(foo: 'bar')).to eq(criteria)
- end
-
- it 'returns the criteria for a column with a table, and a value' do
- criteria = double(:criteria)
-
- expect(connection).to receive(:quote_table_name)
- .with(:'foo.bar')
- .and_return('"foo"."bar"')
-
- expect(model).to receive(:where)
- .with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar')
- .and_return(criteria)
-
- expect(model.iwhere('foo.bar'.to_sym => 'bar')).to eq(criteria)
- end
- end
-
- describe 'with multiple column/value pairs' do
- it 'returns the criteria for a column and a value' do
- initial = double(:criteria)
- final = double(:criteria)
-
- expect(connection).to receive(:quote_table_name)
- .with(:foo)
- .and_return('"foo"')
-
- expect(connection).to receive(:quote_table_name)
- .with(:bar)
- .and_return('"bar"')
-
- expect(model).to receive(:where)
- .with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar')
- .and_return(initial)
-
- expect(initial).to receive(:where)
- .with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz')
- .and_return(final)
-
- got = model.iwhere(foo: 'bar', bar: 'baz')
-
- expect(got).to eq(final)
- end
-
- it 'returns the criteria for a column with a table, and a value' do
- initial = double(:criteria)
- final = double(:criteria)
-
- expect(connection).to receive(:quote_table_name)
- .with(:'foo.bar')
- .and_return('"foo"."bar"')
-
- expect(connection).to receive(:quote_table_name)
- .with(:'foo.baz')
- .and_return('"foo"."baz"')
-
- expect(model).to receive(:where)
- .with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar')
- .and_return(initial)
-
- expect(initial).to receive(:where)
- .with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz')
- .and_return(final)
-
- got = model.iwhere('foo.bar'.to_sym => 'bar',
- 'foo.baz'.to_sym => 'baz')
-
- expect(got).to eq(final)
- end
+ let(:model) do
+ Class.new(ActiveRecord::Base) do
+ include CaseSensitivity
+ self.table_name = 'namespaces'
end
end
- describe 'using MySQL' do
- before do
- allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
- allow(Gitlab::Database).to receive(:mysql?).and_return(true)
- end
-
- describe 'with a single column/value pair' do
- it 'returns the criteria for a column and a value' do
- criteria = double(:criteria)
-
- expect(connection).to receive(:quote_table_name)
- .with(:foo)
- .and_return('`foo`')
-
- expect(model).to receive(:where)
- .with(%q{`foo` = :value}, value: 'bar')
- .and_return(criteria)
+ let!(:model_1) { model.create(path: 'mOdEl-1', name: 'mOdEl 1') }
+ let!(:model_2) { model.create(path: 'mOdEl-2', name: 'mOdEl 2') }
- expect(model.iwhere(foo: 'bar')).to eq(criteria)
- end
+ it 'finds a single instance by a single attribute regardless of case' do
+ expect(model.iwhere(path: 'MODEL-1')).to contain_exactly(model_1)
+ end
- it 'returns the criteria for a column with a table, and a value' do
- criteria = double(:criteria)
+ it 'finds multiple instances by a single attribute regardless of case' do
+ expect(model.iwhere(path: %w(MODEL-1 model-2))).to contain_exactly(model_1, model_2)
+ end
- expect(connection).to receive(:quote_table_name)
- .with(:'foo.bar')
- .and_return('`foo`.`bar`')
+ it 'finds instances by multiple attributes' do
+ expect(model.iwhere(path: %w(MODEL-1 model-2), name: 'model 1'))
+ .to contain_exactly(model_1)
+ end
- expect(model).to receive(:where)
- .with(%q{`foo`.`bar` = :value}, value: 'bar')
- .and_return(criteria)
+ # Using `mysql` & `postgresql` metadata-tags here because both adapters build
+ # the query slightly differently
+ context 'for MySQL', :mysql do
+ it 'builds a simple query' do
+ query = model.iwhere(path: %w(MODEL-1 model-2), name: 'model 1').to_sql
+ expected_query = <<~QRY.strip
+ SELECT `namespaces`.* FROM `namespaces` WHERE (`namespaces`.`path` IN ('MODEL-1', 'model-2')) AND (`namespaces`.`name` = 'model 1')
+ QRY
- expect(model.iwhere('foo.bar'.to_sym => 'bar'))
- .to eq(criteria)
- end
+ expect(query).to eq(expected_query)
end
+ end
- describe 'with multiple column/value pairs' do
- it 'returns the criteria for a column and a value' do
- initial = double(:criteria)
- final = double(:criteria)
-
- expect(connection).to receive(:quote_table_name)
- .with(:foo)
- .and_return('`foo`')
-
- expect(connection).to receive(:quote_table_name)
- .with(:bar)
- .and_return('`bar`')
-
- expect(model).to receive(:where)
- .with(%q{`foo` = :value}, value: 'bar')
- .and_return(initial)
-
- expect(initial).to receive(:where)
- .with(%q{`bar` = :value}, value: 'baz')
- .and_return(final)
-
- got = model.iwhere(foo: 'bar', bar: 'baz')
-
- expect(got).to eq(final)
- end
-
- it 'returns the criteria for a column with a table, and a value' do
- initial = double(:criteria)
- final = double(:criteria)
-
- expect(connection).to receive(:quote_table_name)
- .with(:'foo.bar')
- .and_return('`foo`.`bar`')
-
- expect(connection).to receive(:quote_table_name)
- .with(:'foo.baz')
- .and_return('`foo`.`baz`')
-
- expect(model).to receive(:where)
- .with(%q{`foo`.`bar` = :value}, value: 'bar')
- .and_return(initial)
-
- expect(initial).to receive(:where)
- .with(%q{`foo`.`baz` = :value}, value: 'baz')
- .and_return(final)
-
- got = model.iwhere('foo.bar'.to_sym => 'bar',
- 'foo.baz'.to_sym => 'baz')
+ context 'for PostgreSQL', :postgresql do
+ it 'builds a query using LOWER' do
+ query = model.iwhere(path: %w(MODEL-1 model-2), name: 'model 1').to_sql
+ expected_query = <<~QRY.strip
+ SELECT \"namespaces\".* FROM \"namespaces\" WHERE (LOWER(\"namespaces\".\"path\") IN (LOWER('MODEL-1'), LOWER('model-2'))) AND (LOWER(\"namespaces\".\"name\") = LOWER('model 1'))
+ QRY
- expect(got).to eq(final)
- end
+ expect(query).to eq(expected_query)
end
end
end
diff --git a/spec/models/concerns/deployable_spec.rb b/spec/models/concerns/deployable_spec.rb
new file mode 100644
index 00000000000..6951be903fe
--- /dev/null
+++ b/spec/models/concerns/deployable_spec.rb
@@ -0,0 +1,74 @@
+require 'rails_helper'
+
+describe Deployable do
+ describe '#create_deployment' do
+ let(:deployment) { job.deployment }
+ let(:environment) { deployment&.environment }
+
+ before do
+ job.reload
+ end
+
+ context 'when the deployable object will deploy to production' do
+ let!(:job) { create(:ci_build, :start_review_app) }
+
+ it 'creates a deployment and environment record' do
+ expect(deployment.project).to eq(job.project)
+ expect(deployment.ref).to eq(job.ref)
+ expect(deployment.tag).to eq(job.tag)
+ expect(deployment.sha).to eq(job.sha)
+ expect(deployment.user).to eq(job.user)
+ expect(deployment.deployable).to eq(job)
+ expect(deployment.on_stop).to eq('stop_review_app')
+ expect(environment.name).to eq('review/master')
+ end
+ end
+
+ context 'when the deployable object will stop an environment' do
+ let!(:job) { create(:ci_build, :stop_review_app) }
+
+ it 'does not create a deployment record' do
+ expect(deployment).to be_nil
+ end
+ end
+
+ context 'when the deployable object has already had a deployment' do
+ let!(:job) { create(:ci_build, :start_review_app, deployment: race_deployment) }
+ let!(:race_deployment) { create(:deployment, :success) }
+
+ it 'does not create a new deployment' do
+ expect(deployment).to eq(race_deployment)
+ end
+ end
+
+ context 'when the deployable object will not deploy' do
+ let!(:job) { create(:ci_build) }
+
+ it 'does not create a deployment and environment record' do
+ expect(deployment).to be_nil
+ expect(environment).to be_nil
+ end
+ end
+
+ context 'when environment scope contains invalid character' do
+ let(:job) do
+ create(
+ :ci_build,
+ name: 'job:deploy-to-test-site',
+ environment: '$CI_JOB_NAME',
+ options: {
+ environment: {
+ name: '$CI_JOB_NAME',
+ url: 'http://staging.example.com/$CI_JOB_NAME',
+ on_stop: 'stop_review_app'
+ }
+ })
+ end
+
+ it 'does not create a deployment and environment record' do
+ expect(deployment).to be_nil
+ expect(environment).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/each_batch_spec.rb b/spec/models/concerns/each_batch_spec.rb
index 951690a217b..17224c09693 100644
--- a/spec/models/concerns/each_batch_spec.rb
+++ b/spec/models/concerns/each_batch_spec.rb
@@ -14,40 +14,45 @@ describe EachBatch do
5.times { create(:user, updated_at: 1.day.ago) }
end
- it 'yields an ActiveRecord::Relation when a block is given' do
- model.each_batch do |relation|
- expect(relation).to be_a_kind_of(ActiveRecord::Relation)
+ shared_examples 'each_batch handling' do |kwargs|
+ it 'yields an ActiveRecord::Relation when a block is given' do
+ model.each_batch(kwargs) do |relation|
+ expect(relation).to be_a_kind_of(ActiveRecord::Relation)
+ end
end
- end
- it 'yields a batch index as the second argument' do
- model.each_batch do |_, index|
- expect(index).to eq(1)
+ it 'yields a batch index as the second argument' do
+ model.each_batch(kwargs) do |_, index|
+ expect(index).to eq(1)
+ end
end
- end
- it 'accepts a custom batch size' do
- amount = 0
+ it 'accepts a custom batch size' do
+ amount = 0
- model.each_batch(of: 1) { amount += 1 }
+ model.each_batch(kwargs.merge({ of: 1 })) { amount += 1 }
- expect(amount).to eq(5)
- end
+ expect(amount).to eq(5)
+ end
- it 'does not include ORDER BYs in the yielded relations' do
- model.each_batch do |relation|
- expect(relation.to_sql).not_to include('ORDER BY')
+ it 'does not include ORDER BYs in the yielded relations' do
+ model.each_batch do |relation|
+ expect(relation.to_sql).not_to include('ORDER BY')
+ end
end
- end
- it 'allows updating of the yielded relations' do
- time = Time.now
+ it 'allows updating of the yielded relations' do
+ time = Time.now
- model.each_batch do |relation|
- relation.update_all(updated_at: time)
- end
+ model.each_batch do |relation|
+ relation.update_all(updated_at: time)
+ end
- expect(model.where(updated_at: time).count).to eq(5)
+ expect(model.where(updated_at: time).count).to eq(5)
+ end
end
+
+ it_behaves_like 'each_batch handling', {}
+ it_behaves_like 'each_batch handling', { order_hint: :updated_at }
end
end
diff --git a/spec/models/concerns/from_union_spec.rb b/spec/models/concerns/from_union_spec.rb
new file mode 100644
index 00000000000..ee427a667c6
--- /dev/null
+++ b/spec/models/concerns/from_union_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe FromUnion do
+ describe '.from_union' do
+ let(:model) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'users'
+
+ include FromUnion
+ end
+ end
+
+ it 'selects from the results of the UNION' do
+ query = model.from_union([model.where(id: 1), model.where(id: 2)])
+
+ expect(query.to_sql).to match(/FROM \(SELECT.+UNION.+SELECT.+\) users/m)
+ end
+
+ it 'supports the use of a custom alias for the sub query' do
+ query = model.from_union(
+ [model.where(id: 1), model.where(id: 2)],
+ alias_as: 'kittens'
+ )
+
+ expect(query.to_sql).to match(/FROM \(SELECT.+UNION.+SELECT.+\) kittens/m)
+ end
+
+ it 'supports keeping duplicate rows' do
+ query = model.from_union(
+ [model.where(id: 1), model.where(id: 2)],
+ remove_duplicates: false
+ )
+
+ expect(query.to_sql)
+ .to match(/FROM \(SELECT.+UNION ALL.+SELECT.+\) users/m)
+ end
+ end
+end
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index 6866b43432c..6b1038cb8fd 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -270,11 +270,11 @@ describe HasStatus do
describe '.cancelable' do
subject { CommitStatus.cancelable }
- %i[running pending created].each do |status|
+ %i[running pending created scheduled].each do |status|
it_behaves_like 'containing the job', status
end
- %i[failed success skipped canceled].each do |status|
+ %i[failed success skipped canceled manual].each do |status|
it_behaves_like 'not containing the job', status
end
end
@@ -290,6 +290,18 @@ describe HasStatus do
it_behaves_like 'not containing the job', status
end
end
+
+ describe '.scheduled' do
+ subject { CommitStatus.scheduled }
+
+ %i[scheduled].each do |status|
+ it_behaves_like 'containing the job', status
+ end
+
+ %i[failed success skipped canceled].each do |status|
+ it_behaves_like 'not containing the job', status
+ end
+ end
end
describe '::DEFAULT_STATUS' do
@@ -300,7 +312,41 @@ describe HasStatus do
describe '::BLOCKED_STATUS' do
it 'is a status manual' do
- expect(described_class::BLOCKED_STATUS).to eq 'manual'
+ expect(described_class::BLOCKED_STATUS).to eq %w[manual scheduled]
+ end
+ end
+
+ describe 'blocked?' do
+ subject { object.blocked? }
+
+ %w[ci_pipeline ci_stage ci_build generic_commit_status].each do |type|
+ let(:object) { build(type, status: status) }
+
+ context 'when status is scheduled' do
+ let(:status) { :scheduled }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when status is manual' do
+ let(:status) { :manual }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when status is created' do
+ let(:status) { :created }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+ end
+
+ describe '.status_sql' do
+ subject { Ci::Build.status_sql }
+
+ it 'returns SQL' do
+ puts subject
end
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index ec6374f3963..a4bf3e2350a 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -519,7 +519,7 @@ describe Issuable do
end
end
- context 'substracting time' do
+ context 'subtracting time' do
before do
spend_time(1800)
end
@@ -530,7 +530,7 @@ describe Issuable do
expect(issue.total_time_spent).to eq(900)
end
- context 'when time to substract exceeds the total time spent' do
+ context 'when time to subtract exceeds the total time spent' do
it 'raise a validation error' do
Timecop.travel(1.minute.from_now) do
expect do
diff --git a/spec/models/concerns/optionally_search_spec.rb b/spec/models/concerns/optionally_search_spec.rb
new file mode 100644
index 00000000000..ff4212ddf18
--- /dev/null
+++ b/spec/models/concerns/optionally_search_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe OptionallySearch do
+ let(:model) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'users'
+
+ include OptionallySearch
+ end
+ end
+
+ describe '.search' do
+ it 'raises NotImplementedError' do
+ expect { model.search('foo') }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '.optionally_search' do
+ context 'when a query is given' do
+ it 'delegates to the search method' do
+ expect(model)
+ .to receive(:search)
+ .with('foo')
+
+ model.optionally_search('foo')
+ end
+ end
+
+ context 'when no query is given' do
+ it 'returns the current relation' do
+ expect(model.optionally_search).to be_a_kind_of(ActiveRecord::Relation)
+ end
+ end
+
+ context 'when an empty query is given' do
+ it 'returns the current relation' do
+ expect(model.optionally_search(''))
+ .to be_a_kind_of(ActiveRecord::Relation)
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/redactable_spec.rb b/spec/models/concerns/redactable_spec.rb
new file mode 100644
index 00000000000..7d320edd492
--- /dev/null
+++ b/spec/models/concerns/redactable_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe Redactable do
+ shared_examples 'model with redactable field' do
+ it 'redacts unsubscribe token' do
+ model[field] = 'some text /sent_notifications/00000000000000000000000000000000/unsubscribe more text'
+
+ model.save!
+
+ expect(model[field]).to eq 'some text /sent_notifications/REDACTED/unsubscribe more text'
+ end
+
+ it 'ignores not hexadecimal tokens' do
+ text = 'some text /sent_notifications/token/unsubscribe more text'
+ model[field] = text
+
+ model.save!
+
+ expect(model[field]).to eq text
+ end
+
+ it 'ignores not matching texts' do
+ text = 'some text /sent_notifications/.*/unsubscribe more text'
+ model[field] = text
+
+ model.save!
+
+ expect(model[field]).to eq text
+ end
+
+ it 'redacts the field when saving the model before creating markdown cache' do
+ model[field] = 'some text /sent_notifications/00000000000000000000000000000000/unsubscribe more text'
+
+ model.save!
+
+ expected = 'some text /sent_notifications/REDACTED/unsubscribe more text'
+ expect(model[field]).to eq expected
+ expect(model["#{field}_html"]).to eq "<p dir=\"auto\">#{expected}</p>"
+ end
+ end
+
+ context 'when model is an issue' do
+ it_behaves_like 'model with redactable field' do
+ let(:model) { create(:issue) }
+ let(:field) { :description }
+ end
+ end
+
+ context 'when model is a merge request' do
+ it_behaves_like 'model with redactable field' do
+ let(:model) { create(:merge_request) }
+ let(:field) { :description }
+ end
+ end
+
+ context 'when model is a note' do
+ it_behaves_like 'model with redactable field' do
+ let(:model) { create(:note) }
+ let(:field) { :note }
+ end
+ end
+
+ context 'when model is a snippet' do
+ it_behaves_like 'model with redactable field' do
+ let(:model) { create(:snippet) }
+ let(:field) { :description }
+ end
+ end
+end
diff --git a/spec/models/concerns/relative_positioning_spec.rb b/spec/models/concerns/relative_positioning_spec.rb
index 729056b6abc..66c1f47d12b 100644
--- a/spec/models/concerns/relative_positioning_spec.rb
+++ b/spec/models/concerns/relative_positioning_spec.rb
@@ -6,9 +6,13 @@ describe RelativePositioning do
let(:issue1) { create(:issue, project: project) }
let(:new_issue) { create(:issue, project: project) }
- before do
- [issue, issue1].each do |issue|
- issue.move_to_end && issue.save
+ describe '.move_to_end' do
+ it 'moves the object to the end' do
+ Issue.move_to_end([issue, issue1])
+
+ expect(issue1.prev_relative_position).to eq issue.relative_position
+ expect(issue.prev_relative_position).to eq nil
+ expect(issue1.next_relative_position).to eq nil
end
end
@@ -59,6 +63,12 @@ describe RelativePositioning do
end
describe '#move_to_end' do
+ before do
+ [issue, issue1].each do |issue|
+ issue.move_to_end && issue.save
+ end
+ end
+
it 'moves issue to the end' do
new_issue.move_to_end
@@ -67,6 +77,12 @@ describe RelativePositioning do
end
describe '#shift_after?' do
+ before do
+ [issue, issue1].each do |issue|
+ issue.move_to_end && issue.save
+ end
+ end
+
it 'returns true' do
issue.update(relative_position: issue1.relative_position - 1)
@@ -81,6 +97,12 @@ describe RelativePositioning do
end
describe '#shift_before?' do
+ before do
+ [issue, issue1].each do |issue|
+ issue.move_to_end && issue.save
+ end
+ end
+
it 'returns true' do
issue.update(relative_position: issue1.relative_position + 1)
@@ -95,6 +117,12 @@ describe RelativePositioning do
end
describe '#move_between' do
+ before do
+ [issue, issue1].each do |issue|
+ issue.move_to_end && issue.save
+ end
+ end
+
it 'positions issue between two other' do
new_issue.move_between(issue, issue1)
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 9b804429138..782687516ae 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -2,8 +2,6 @@ require 'spec_helper'
shared_examples 'TokenAuthenticatable' do
describe 'dynamically defined methods' do
- it { expect(described_class).to be_private_method_defined(:generate_token) }
- it { expect(described_class).to be_private_method_defined(:write_new_token) }
it { expect(described_class).to respond_to("find_by_#{token_field}") }
it { is_expected.to respond_to("ensure_#{token_field}") }
it { is_expected.to respond_to("set_#{token_field}") }
@@ -66,13 +64,275 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
end
describe 'multiple token fields' do
- before do
+ before(:all) do
described_class.send(:add_authentication_token_field, :yet_another_token)
end
- describe '.token_fields' do
- subject { described_class.authentication_token_fields }
- it { is_expected.to include(:runners_registration_token, :yet_another_token) }
+ it { is_expected.to respond_to(:ensure_runners_registration_token) }
+ it { is_expected.to respond_to(:ensure_yet_another_token) }
+ end
+
+ describe 'setting same token field multiple times' do
+ subject { described_class.send(:add_authentication_token_field, :runners_registration_token) }
+
+ it 'raises error' do
+ expect {subject}.to raise_error(ArgumentError)
+ end
+ end
+end
+
+describe PersonalAccessToken, 'TokenAuthenticatable' do
+ let(:personal_access_token_name) { 'test-pat-01' }
+ let(:token_value) { 'token' }
+ let(:user) { create(:user) }
+ let(:personal_access_token) do
+ described_class.new(name: personal_access_token_name,
+ user_id: user.id,
+ scopes: [:api],
+ token: token,
+ token_digest: token_digest)
+ end
+
+ before do
+ allow(Devise).to receive(:friendly_token).and_return(token_value)
+ end
+
+ describe '.find_by_token' do
+ subject { PersonalAccessToken.find_by_token(token_value) }
+
+ before do
+ personal_access_token.save
+ end
+
+ context 'token_digest already exists' do
+ let(:token) { nil }
+ let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
+
+ it 'finds the token' do
+ expect(subject).not_to be_nil
+ expect(subject.name).to eql(personal_access_token_name)
+ end
+ end
+
+ context 'token_digest does not exist' do
+ let(:token) { token_value }
+ let(:token_digest) { nil }
+
+ it 'finds the token' do
+ expect(subject).not_to be_nil
+ expect(subject.name).to eql(personal_access_token_name)
+ end
+ end
+ end
+
+ describe '#set_token' do
+ let(:new_token_value) { 'new-token' }
+ subject { personal_access_token.set_token(new_token_value) }
+
+ context 'token_digest already exists' do
+ let(:token) { nil }
+ let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
+
+ it 'overwrites token_digest' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(new_token_value)
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(new_token_value))
+ end
+ end
+
+ context 'token_digest does not exist but token does' do
+ let(:token) { token_value }
+ let(:token_digest) { nil }
+
+ it 'creates new token_digest and clears token' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(new_token_value)
+ expect(personal_access_token.token_digest).to eql(Gitlab::CryptoHelper.sha256(new_token_value))
+ end
+ end
+
+ context 'token_digest does not exist, nor token' do
+ let(:token) { nil }
+ let(:token_digest) { nil }
+
+ it 'creates new token_digest' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(new_token_value)
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(new_token_value))
+ end
+ end
+ end
+
+ describe '#ensure_token' do
+ subject { personal_access_token.ensure_token }
+
+ context 'token_digest already exists' do
+ let(:token) { nil }
+ let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
+
+ it 'does not change token fields' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to be_nil
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+
+ context 'token_digest does not exist but token does' do
+ let(:token) { token_value }
+ let(:token_digest) { nil }
+
+ it 'does not change token fields' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to eql(token_value)
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to be_nil
+ end
+ end
+
+ context 'token_digest does not exist, nor token' do
+ let(:token) { nil }
+ let(:token_digest) { nil }
+
+ it 'creates token_digest' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+ end
+
+ describe '#ensure_token!' do
+ subject { personal_access_token.ensure_token! }
+
+ context 'token_digest already exists' do
+ let(:token) { nil }
+ let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
+
+ it 'does not change token fields' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to be_nil
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+
+ context 'token_digest does not exist but token does' do
+ let(:token) { token_value }
+ let(:token_digest) { nil }
+
+ it 'does not change token fields' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to eql(token_value)
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to be_nil
+ end
+ end
+
+ context 'token_digest does not exist, nor token' do
+ let(:token) { nil }
+ let(:token_digest) { nil }
+
+ it 'creates token_digest' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+ end
+
+ describe '#reset_token!' do
+ subject { personal_access_token.reset_token! }
+
+ context 'token_digest already exists' do
+ let(:token) { nil }
+ let(:token_digest) { Gitlab::CryptoHelper.sha256('old-token') }
+
+ it 'creates new token_digest' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+
+ context 'token_digest does not exist but token does' do
+ let(:token) { 'old-token' }
+ let(:token_digest) { nil }
+
+ it 'creates new token_digest and clears token' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to eql(Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+
+ context 'token_digest does not exist, nor token' do
+ let(:token) { nil }
+ let(:token_digest) { nil }
+
+ it 'creates new token_digest' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+
+ context 'token_digest exists and newly generated token would be the same' do
+ let(:token) { nil }
+ let(:token_digest) { Gitlab::CryptoHelper.sha256('old-token') }
+
+ before do
+ personal_access_token.save
+ allow(Devise).to receive(:friendly_token).and_return(
+ 'old-token', token_value, 'boom!')
+ end
+
+ it 'regenerates a new token_digest' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+
+ context 'token exists and newly generated token would be the same' do
+ let(:token) { 'old-token' }
+ let(:token_digest) { nil }
+
+ before do
+ personal_access_token.save
+ allow(Devise).to receive(:friendly_token).and_return(
+ 'old-token', token_value, 'boom!')
+ end
+
+ it 'regenerates a new token_digest' do
+ subject
+
+ expect(personal_access_token.read_attribute('token')).to be_nil
+ expect(personal_access_token.token).to eql(token_value)
+ expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
+ end
end
end
end
diff --git a/spec/models/concerns/triggerable_hooks_spec.rb b/spec/models/concerns/triggerable_hooks_spec.rb
index 621d2d38eae..265abd6bd72 100644
--- a/spec/models/concerns/triggerable_hooks_spec.rb
+++ b/spec/models/concerns/triggerable_hooks_spec.rb
@@ -40,4 +40,28 @@ RSpec.describe TriggerableHooks do
end
end
end
+
+ describe '.select_active' do
+ it 'returns hooks that match the active filter' do
+ TestableHook.create!(url: 'http://example1.com', push_events: true)
+ TestableHook.create!(url: 'http://example2.com', push_events: true)
+ filter1 = double(:filter1)
+ filter2 = double(:filter2)
+ allow(ActiveHookFilter).to receive(:new).exactly(2).times.and_return(filter1, filter2)
+ expect(filter1).to receive(:matches?).and_return(true)
+ expect(filter2).to receive(:matches?).and_return(false)
+
+ hooks = TestableHook.push_hooks.order_id_asc
+ expect(hooks.select_active(:push_hooks, {})).to eq [hooks.first]
+ end
+
+ it 'returns empty list if no hooks match the active filter' do
+ TestableHook.create!(url: 'http://example1.com', push_events: true)
+ filter = double(:filter)
+ allow(ActiveHookFilter).to receive(:new).and_return(filter)
+ expect(filter).to receive(:matches?).and_return(false)
+
+ expect(TestableHook.push_hooks.select_active(:push_hooks, {})).to eq []
+ end
+ end
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index b335e0fbeb3..270b2767c68 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -16,6 +16,22 @@ describe Deployment do
it { is_expected.to validate_presence_of(:ref) }
it { is_expected.to validate_presence_of(:sha) }
+ describe '#scheduled_actions' do
+ subject { deployment.scheduled_actions }
+
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+ let(:deployment) { create(:deployment, deployable: build) }
+
+ it 'delegates to other_scheduled_actions' do
+ expect_any_instance_of(Ci::Build)
+ .to receive(:other_scheduled_actions)
+
+ subject
+ end
+ end
+
describe 'modules' do
it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid }
@@ -26,16 +42,197 @@ describe Deployment do
end
end
- describe 'after_create callbacks' do
- let(:environment) { create(:environment) }
- let(:store) { Gitlab::EtagCaching::Store.new }
+ describe '.success' do
+ subject { described_class.success }
+
+ context 'when deployment status is success' do
+ let(:deployment) { create(:deployment, :success) }
+
+ it { is_expected.to eq([deployment]) }
+ end
+
+ context 'when deployment status is created' do
+ let(:deployment) { create(:deployment, :created) }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'when deployment status is running' do
+ let(:deployment) { create(:deployment, :running) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ describe 'state machine' do
+ context 'when deployment runs' do
+ let(:deployment) { create(:deployment) }
+
+ before do
+ deployment.run!
+ end
+
+ it 'starts running' do
+ Timecop.freeze do
+ expect(deployment).to be_running
+ expect(deployment.finished_at).to be_nil
+ end
+ end
+ end
- it 'invalidates the environment etag cache' do
- old_value = store.get(environment.etag_cache_key)
+ context 'when deployment succeeded' do
+ let(:deployment) { create(:deployment, :running) }
- create(:deployment, environment: environment)
+ it 'has correct status' do
+ Timecop.freeze do
+ deployment.succeed!
- expect(store.get(environment.etag_cache_key)).not_to eq(old_value)
+ expect(deployment).to be_success
+ expect(deployment.finished_at).to be_like_time(Time.now)
+ end
+ end
+
+ it 'executes Deployments::SuccessWorker asynchronously' do
+ expect(Deployments::SuccessWorker)
+ .to receive(:perform_async).with(deployment.id)
+
+ deployment.succeed!
+ end
+ end
+
+ context 'when deployment failed' do
+ let(:deployment) { create(:deployment, :running) }
+
+ it 'has correct status' do
+ Timecop.freeze do
+ deployment.drop!
+
+ expect(deployment).to be_failed
+ expect(deployment.finished_at).to be_like_time(Time.now)
+ end
+ end
+ end
+
+ context 'when deployment was canceled' do
+ let(:deployment) { create(:deployment, :running) }
+
+ it 'has correct status' do
+ Timecop.freeze do
+ deployment.cancel!
+
+ expect(deployment).to be_canceled
+ expect(deployment.finished_at).to be_like_time(Time.now)
+ end
+ end
+ end
+ end
+
+ describe '#success?' do
+ subject { deployment.success? }
+
+ context 'when deployment status is success' do
+ let(:deployment) { create(:deployment, :success) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when deployment status is failed' do
+ let(:deployment) { create(:deployment, :failed) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '#status_name' do
+ subject { deployment.status_name }
+
+ context 'when deployment status is success' do
+ let(:deployment) { create(:deployment, :success) }
+
+ it { is_expected.to eq(:success) }
+ end
+
+ context 'when deployment status is failed' do
+ let(:deployment) { create(:deployment, :failed) }
+
+ it { is_expected.to eq(:failed) }
+ end
+ end
+
+ describe '#finished_at' do
+ subject { deployment.finished_at }
+
+ context 'when deployment status is created' do
+ let(:deployment) { create(:deployment) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when deployment status is success' do
+ let(:deployment) { create(:deployment, :success) }
+
+ it { is_expected.to eq(deployment.read_attribute(:finished_at)) }
+ end
+
+ context 'when deployment status is success' do
+ let(:deployment) { create(:deployment, :success, finished_at: nil) }
+
+ before do
+ deployment.update_column(:finished_at, nil)
+ end
+
+ it { is_expected.to eq(deployment.read_attribute(:created_at)) }
+ end
+
+ context 'when deployment status is running' do
+ let(:deployment) { create(:deployment, :running) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#deployed_at' do
+ subject { deployment.deployed_at }
+
+ context 'when deployment status is created' do
+ let(:deployment) { create(:deployment) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when deployment status is success' do
+ let(:deployment) { create(:deployment, :success) }
+
+ it { is_expected.to eq(deployment.read_attribute(:finished_at)) }
+ end
+
+ context 'when deployment status is running' do
+ let(:deployment) { create(:deployment, :running) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe 'scopes' do
+ describe 'last_for_environment' do
+ let(:production) { create(:environment) }
+ let(:staging) { create(:environment) }
+ let(:testing) { create(:environment) }
+
+ let!(:deployments) do
+ [
+ create(:deployment, environment: production),
+ create(:deployment, environment: staging),
+ create(:deployment, environment: production)
+ ]
+ end
+
+ it 'retrieves last deployments for environments' do
+ last_deployments = described_class.last_for_environment([staging, production, testing])
+
+ expect(last_deployments.size).to eq(2)
+ expect(last_deployments).to match_array(deployments.last(2))
+ end
end
end
@@ -73,7 +270,7 @@ describe Deployment do
end
describe '#metrics' do
- let(:deployment) { create(:deployment) }
+ let(:deployment) { create(:deployment, :success) }
let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) }
subject { deployment.metrics }
@@ -102,7 +299,7 @@ describe Deployment do
describe '#additional_metrics' do
let(:project) { create(:project, :repository) }
- let(:deployment) { create(:deployment, project: project) }
+ let(:deployment) { create(:deployment, :succeed, project: project) }
subject { deployment.additional_metrics }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index c65e0b81451..9a3f1f1c5a1 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -53,6 +53,25 @@ describe Environment do
end
end
+ describe '.with_deployment' do
+ subject { described_class.with_deployment(sha) }
+
+ let(:environment) { create(:environment) }
+ let(:sha) { RepoHelpers.sample_commit.id }
+
+ context 'when deployment has the specified sha' do
+ let!(:deployment) { create(:deployment, environment: environment, sha: sha) }
+
+ it { is_expected.to eq([environment]) }
+ end
+
+ context 'when deployment does not have the specified sha' do
+ let!(:deployment) { create(:deployment, environment: environment, sha: 'abc') }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe '#folder_name' do
context 'when it is inside a folder' do
subject(:environment) do
@@ -95,7 +114,7 @@ describe Environment do
context 'with a last deployment' do
let!(:deployment) do
- create(:deployment, environment: environment, sha: project.commit('master').id)
+ create(:deployment, :success, environment: environment, sha: project.commit('master').id)
end
context 'in the same branch' do
@@ -136,8 +155,8 @@ describe Environment do
describe '#first_deployment_for' do
let(:project) { create(:project, :repository) }
- let!(:deployment) { create(:deployment, environment: environment, ref: commit.parent.id) }
- let!(:deployment1) { create(:deployment, environment: environment, ref: commit.id) }
+ let!(:deployment) { create(:deployment, :succeed, environment: environment, ref: commit.parent.id) }
+ let!(:deployment1) { create(:deployment, :succeed, environment: environment, ref: commit.id) }
let(:head_commit) { project.commit }
let(:commit) { project.commit.parent }
@@ -181,7 +200,8 @@ describe Environment do
let(:build) { create(:ci_build) }
let!(:deployment) do
- create(:deployment, environment: environment,
+ create(:deployment, :success,
+ environment: environment,
deployable: build,
on_stop: 'close_app')
end
@@ -249,7 +269,8 @@ describe Environment do
let(:build) { create(:ci_build, pipeline: pipeline) }
let!(:deployment) do
- create(:deployment, environment: environment,
+ create(:deployment, :success,
+ environment: environment,
deployable: build,
on_stop: 'close_app')
end
@@ -304,7 +325,7 @@ describe Environment do
context 'when last deployment to environment is the most recent one' do
before do
- create(:deployment, environment: environment, ref: 'feature')
+ create(:deployment, :success, environment: environment, ref: 'feature')
end
it { is_expected.to be true }
@@ -312,8 +333,8 @@ describe Environment do
context 'when last deployment to environment is not the most recent' do
before do
- create(:deployment, environment: environment, ref: 'feature')
- create(:deployment, environment: environment, ref: 'master')
+ create(:deployment, :success, environment: environment, ref: 'feature')
+ create(:deployment, :success, environment: environment, ref: 'master')
end
it { is_expected.to be false }
@@ -321,7 +342,7 @@ describe Environment do
end
describe '#actions_for' do
- let(:deployment) { create(:deployment, environment: environment) }
+ let(:deployment) { create(:deployment, :success, environment: environment) }
let(:pipeline) { deployment.deployable.pipeline }
let!(:review_action) { create(:ci_build, :manual, name: 'review-apps', pipeline: pipeline, environment: 'review/$CI_COMMIT_REF_NAME' )}
let!(:production_action) { create(:ci_build, :manual, name: 'production', pipeline: pipeline, environment: 'production' )}
@@ -331,14 +352,78 @@ describe Environment do
end
end
+ describe '.deployments' do
+ subject { environment.deployments }
+
+ context 'when there is a deployment record with created status' do
+ let(:deployment) { create(:deployment, :created, environment: environment) }
+
+ it 'does not return the record' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when there is a deployment record with running status' do
+ let(:deployment) { create(:deployment, :running, environment: environment) }
+
+ it 'does not return the record' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when there is a deployment record with success status' do
+ let(:deployment) { create(:deployment, :success, environment: environment) }
+
+ it 'returns the record' do
+ is_expected.to eq([deployment])
+ end
+ end
+ end
+
+ describe '.last_deployment' do
+ subject { environment.last_deployment }
+
+ before do
+ allow_any_instance_of(Deployment).to receive(:create_ref)
+ end
+
+ context 'when there is an old deployment record' do
+ let!(:previous_deployment) { create(:deployment, :success, environment: environment) }
+
+ context 'when there is a deployment record with created status' do
+ let!(:deployment) { create(:deployment, environment: environment) }
+
+ it 'returns the previous deployment' do
+ is_expected.to eq(previous_deployment)
+ end
+ end
+
+ context 'when there is a deployment record with running status' do
+ let!(:deployment) { create(:deployment, :running, environment: environment) }
+
+ it 'returns the previous deployment' do
+ is_expected.to eq(previous_deployment)
+ end
+ end
+
+ context 'when there is a deployment record with success status' do
+ let!(:deployment) { create(:deployment, :success, environment: environment) }
+
+ it 'returns the latest successful deployment' do
+ is_expected.to eq(deployment)
+ end
+ end
+ end
+ end
+
describe '#has_terminals?' do
subject { environment.has_terminals? }
- context 'when the enviroment is available' do
+ context 'when the environment is available' do
context 'with a deployment service' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
context 'and a deployment' do
- let!(:deployment) { create(:deployment, environment: environment) }
+ let!(:deployment) { create(:deployment, :success, environment: environment) }
it { is_expected.to be_truthy }
end
@@ -447,7 +532,7 @@ describe Environment do
describe '#has_metrics?' do
subject { environment.has_metrics? }
- context 'when the enviroment is available' do
+ context 'when the environment is available' do
context 'with a deployment service' do
let(:project) { create(:prometheus_project) }
@@ -456,8 +541,8 @@ describe Environment do
it { is_expected.to be_truthy }
end
- context 'but no deployments' do
- it { is_expected.to be_falsy }
+ context 'and no deployments' do
+ it { is_expected.to be_truthy }
end
end
@@ -504,39 +589,6 @@ describe Environment do
end
end
- describe '#has_metrics?' do
- subject { environment.has_metrics? }
-
- context 'when the enviroment is available' do
- context 'with a deployment service' do
- let(:project) { create(:prometheus_project) }
-
- context 'and a deployment' do
- let!(:deployment) { create(:deployment, environment: environment) }
- it { is_expected.to be_truthy }
- end
-
- context 'but no deployments' do
- it { is_expected.to be_falsy }
- end
- end
-
- context 'without a monitoring service' do
- it { is_expected.to be_falsy }
- end
- end
-
- context 'when the environment is unavailable' do
- let(:project) { create(:prometheus_project) }
-
- before do
- environment.stop
- end
-
- it { is_expected.to be_falsy }
- end
- end
-
describe '#additional_metrics' do
let(:project) { create(:prometheus_project) }
subject { environment.additional_metrics }
diff --git a/spec/models/environment_status_spec.rb b/spec/models/environment_status_spec.rb
new file mode 100644
index 00000000000..90f7e4a4590
--- /dev/null
+++ b/spec/models/environment_status_spec.rb
@@ -0,0 +1,165 @@
+require 'spec_helper'
+
+describe EnvironmentStatus do
+ include ProjectForksHelper
+
+ let(:deployment) { create(:deployment, :succeed, :review_app) }
+ let(:environment) { deployment.environment }
+ let(:project) { deployment.project }
+ let(:merge_request) { create(:merge_request, :deployed_review_app, deployment: deployment) }
+ let(:sha) { deployment.sha }
+
+ subject(:environment_status) { described_class.new(environment, merge_request, sha) }
+
+ it { is_expected.to delegate_method(:id).to(:environment) }
+ it { is_expected.to delegate_method(:name).to(:environment) }
+ it { is_expected.to delegate_method(:project).to(:environment) }
+ it { is_expected.to delegate_method(:deployed_at).to(:deployment) }
+ it { is_expected.to delegate_method(:status).to(:deployment) }
+
+ describe '#project' do
+ subject { environment_status.project }
+
+ it { is_expected.to eq(project) }
+ end
+
+ describe '#merge_request' do
+ subject { environment_status.merge_request }
+
+ it { is_expected.to eq(merge_request) }
+ end
+
+ describe '#deployment' do
+ subject { environment_status.deployment }
+
+ it { is_expected.to eq(deployment) }
+ end
+
+ # $ git diff --stat pages-deploy-target...pages-deploy
+ # .gitlab/route-map.yml | 5 +++++
+ # files/html/500.html | 13 -------------
+ # files/html/page.html | 3 +++
+ # files/js/application.js | 3 +++
+ # files/markdown/ruby-style-guide.md | 4 ++++
+ # pages-deploy.txt | 1 +
+ #
+ # $ cat .gitlab/route-map.yml
+ # - source: /files\/markdown\/(.+)\.md$/
+ # public: '\1.html'
+ #
+ # - source: /files\/(.+)/
+ # public: '\1'
+ describe '#changes' do
+ it 'contains only added and modified public pages' do
+ expect(environment_status.changes).to contain_exactly(
+ {
+ path: 'ruby-style-guide.html',
+ external_url: "#{environment.external_url}/ruby-style-guide.html"
+ }, {
+ path: 'html/page.html',
+ external_url: "#{environment.external_url}/html/page.html"
+ }
+ )
+ end
+ end
+
+ describe '.for_merge_request' do
+ let(:admin) { create(:admin) }
+ let(:pipeline) { create(:ci_pipeline, sha: sha) }
+
+ it 'is based on merge_request.diff_head_sha' do
+ expect(merge_request).to receive(:diff_head_sha)
+ expect(merge_request).not_to receive(:merge_commit_sha)
+
+ described_class.for_merge_request(merge_request, admin)
+ end
+ end
+
+ describe '.after_merge_request' do
+ let(:admin) { create(:admin) }
+ let(:pipeline) { create(:ci_pipeline, sha: sha) }
+
+ before do
+ merge_request.mark_as_merged!
+ end
+
+ it 'is based on merge_request.merge_commit_sha' do
+ expect(merge_request).to receive(:merge_commit_sha)
+ expect(merge_request).not_to receive(:diff_head_sha)
+
+ described_class.after_merge_request(merge_request, admin)
+ end
+ end
+
+ describe '.build_environments_status' do
+ subject { described_class.send(:build_environments_status, merge_request, user, sha) }
+
+ let!(:build) { create(:ci_build, :deploy_to_production, pipeline: pipeline) }
+ let(:environment) { build.deployment.environment }
+ let(:user) { project.owner }
+
+ before do
+ build.deployment&.update!(sha: sha)
+ end
+
+ context 'when environment is created on a forked project' do
+ let(:project) { create(:project, :repository) }
+ let(:forked) { fork_project(project, user, repository: true) }
+ let(:sha) { forked.commit.sha }
+ let(:pipeline) { create(:ci_pipeline, sha: sha, project: forked) }
+
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: forked,
+ target_project: project,
+ target_branch: 'master',
+ head_pipeline: pipeline)
+ end
+
+ it 'returns environment status' do
+ expect(subject.count).to eq(1)
+ expect(subject[0].environment).to eq(environment)
+ expect(subject[0].merge_request).to eq(merge_request)
+ expect(subject[0].sha).to eq(sha)
+ end
+ end
+
+ context 'when environment is created on a target project' do
+ let(:project) { create(:project, :repository) }
+ let(:sha) { project.commit.sha }
+ let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
+
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: 'feature',
+ target_project: project,
+ target_branch: 'master',
+ head_pipeline: pipeline)
+ end
+
+ it 'returns environment status' do
+ expect(subject.count).to eq(1)
+ expect(subject[0].environment).to eq(environment)
+ expect(subject[0].merge_request).to eq(merge_request)
+ expect(subject[0].sha).to eq(sha)
+ end
+
+ context 'when the build stops an environment' do
+ let!(:build) { create(:ci_build, :stop_review_app, pipeline: pipeline) }
+
+ it 'does not return environment status' do
+ expect(subject.count).to eq(0)
+ end
+ end
+
+ context 'when user does not have a permission to see the environment' do
+ let(:user) { create(:user) }
+
+ it 'does not return environment status' do
+ expect(subject.count).to eq(0)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/event_collection_spec.rb b/spec/models/event_collection_spec.rb
index e0a87c18cc7..6078f429bdc 100644
--- a/spec/models/event_collection_spec.rb
+++ b/spec/models/event_collection_spec.rb
@@ -41,7 +41,7 @@ describe EventCollection do
end
it 'allows filtering of events using an EventFilter' do
- filter = EventFilter.new(EventFilter.issue)
+ filter = EventFilter.new(EventFilter::ISSUE)
events = described_class.new(projects, filter: filter).to_a
expect(events.length).to eq(1)
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index c1eac4fa489..81748681528 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -148,9 +148,14 @@ describe Event do
let(:admin) { create(:admin) }
let(:issue) { create(:issue, project: project, author: author, assignees: [assignee]) }
let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignees: [assignee]) }
+ let(:project_snippet) { create(:project_snippet, :public, project: project, author: author) }
+ let(:personal_snippet) { create(:personal_snippet, :public, author: author) }
let(:note_on_commit) { create(:note_on_commit, project: project) }
let(:note_on_issue) { create(:note_on_issue, noteable: issue, project: project) }
let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project) }
+ let(:note_on_project_snippet) { create(:note_on_project_snippet, author: author, noteable: project_snippet, project: project) }
+ let(:note_on_personal_snippet) { create(:note_on_personal_snippet, author: author, noteable: personal_snippet, project: nil) }
+ let(:milestone_on_project) { create(:milestone, project: project) }
let(:event) { described_class.new(project: project, target: target, author_id: author.id) }
before do
@@ -268,6 +273,125 @@ describe Event do
end
end
end
+
+ context 'milestone event' do
+ let(:target) { milestone_on_project }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_truthy
+ expect(event.visible_to_user?(non_member)).to be_truthy
+ expect(event.visible_to_user?(member)).to be_truthy
+ expect(event.visible_to_user?(guest)).to be_truthy
+ expect(event.visible_to_user?(admin)).to be_truthy
+ end
+
+ context 'on public project with private issue tracker and merge requests' do
+ let(:project) { create(:project, :public, :issues_private, :merge_requests_private) }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_falsy
+ expect(event.visible_to_user?(non_member)).to be_falsy
+ expect(event.visible_to_user?(member)).to be_truthy
+ expect(event.visible_to_user?(guest)).to be_truthy
+ expect(event.visible_to_user?(admin)).to be_truthy
+ end
+ end
+
+ context 'on private project' do
+ let(:project) { create(:project, :private) }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_falsy
+ expect(event.visible_to_user?(non_member)).to be_falsy
+ expect(event.visible_to_user?(member)).to be_truthy
+ expect(event.visible_to_user?(guest)).to be_truthy
+ expect(event.visible_to_user?(admin)).to be_truthy
+ end
+ end
+ end
+
+ context 'project snippet note event' do
+ let(:target) { note_on_project_snippet }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_truthy
+ expect(event.visible_to_user?(non_member)).to be_truthy
+ expect(event.visible_to_user?(author)).to be_truthy
+ expect(event.visible_to_user?(member)).to be_truthy
+ expect(event.visible_to_user?(guest)).to be_truthy
+ expect(event.visible_to_user?(admin)).to be_truthy
+ end
+
+ context 'on public project with private snippets' do
+ let(:project) { create(:project, :public, :snippets_private) }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_falsy
+ expect(event.visible_to_user?(non_member)).to be_falsy
+
+ # Normally, we'd expect the author of a comment to be able to view it.
+ # However, this doesn't seem to be the case for comments on snippets.
+ expect(event.visible_to_user?(author)).to be_falsy
+
+ expect(event.visible_to_user?(member)).to be_truthy
+ expect(event.visible_to_user?(guest)).to be_truthy
+ expect(event.visible_to_user?(admin)).to be_truthy
+ end
+ end
+
+ context 'on private project' do
+ let(:project) { create(:project, :private) }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_falsy
+ expect(event.visible_to_user?(non_member)).to be_falsy
+
+ # Normally, we'd expect the author of a comment to be able to view it.
+ # However, this doesn't seem to be the case for comments on snippets.
+ expect(event.visible_to_user?(author)).to be_falsy
+
+ expect(event.visible_to_user?(member)).to be_truthy
+ expect(event.visible_to_user?(guest)).to be_truthy
+ expect(event.visible_to_user?(admin)).to be_truthy
+ end
+ end
+ end
+
+ context 'personal snippet note event' do
+ let(:target) { note_on_personal_snippet }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_truthy
+ expect(event.visible_to_user?(non_member)).to be_truthy
+ expect(event.visible_to_user?(author)).to be_truthy
+ expect(event.visible_to_user?(admin)).to be_truthy
+ end
+
+ context 'on internal snippet' do
+ let(:personal_snippet) { create(:personal_snippet, :internal, author: author) }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_falsy
+ expect(event.visible_to_user?(non_member)).to be_truthy
+ expect(event.visible_to_user?(author)).to be_truthy
+ expect(event.visible_to_user?(admin)).to be_truthy
+ end
+ end
+
+ context 'on private snippet' do
+ let(:personal_snippet) { create(:personal_snippet, :private, author: author) }
+
+ it do
+ expect(event.visible_to_user?(nil)).to be_falsy
+ expect(event.visible_to_user?(non_member)).to be_falsy
+ expect(event.visible_to_user?(author)).to be_truthy
+
+ # It is very unexpected that a private personal snippet is not visible
+ # to an instance administrator. This should be fixed in the future.
+ expect(event.visible_to_user?(admin)).to be_falsy
+ end
+ end
+ end
end
describe '.limit_recent' do
diff --git a/spec/models/fork_network_member_spec.rb b/spec/models/fork_network_member_spec.rb
index 25bf596fddc..60d04562e6c 100644
--- a/spec/models/fork_network_member_spec.rb
+++ b/spec/models/fork_network_member_spec.rb
@@ -11,7 +11,7 @@ describe ForkNetworkMember do
let(:fork_network) { fork_network_member.fork_network }
it 'removes the fork network if it was the last member' do
- fork_network.fork_network_members.destroy_all
+ fork_network.fork_network_members.destroy_all # rubocop: disable DestroyAll
expect(ForkNetwork.count).to eq(0)
end
diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb
deleted file mode 100644
index 32e33e8f42f..00000000000
--- a/spec/models/forked_project_link_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'spec_helper'
-
-describe ForkedProjectLink, "add link on fork" do
- include ProjectForksHelper
-
- let(:project_from) { create(:project, :repository) }
- let(:project_to) { fork_project(project_from, user) }
- let(:user) { create(:user) }
-
- before do
- project_from.add_reporter(user)
- end
-
- it 'project_from knows its forks' do
- _ = project_to
-
- expect(project_from.forks.count).to eq(1)
- end
-
- it "project_to knows it is forked" do
- expect(project_to.forked?).to be_truthy
- end
-
- it "project knows who it is forked from" do
- expect(project_to.forked_from_project).to eq(project_from)
- end
-
- context 'project_to is pending_delete' do
- before do
- project_to.update!(pending_delete: true)
- end
-
- it { expect(project_from.forks.count).to eq(0) }
- end
-
- context 'project_from is pending_delete' do
- before do
- project_from.update!(pending_delete: true)
- end
-
- it { expect(project_to.forked_from_project).to be_nil }
- end
-
- describe '#forked?' do
- let(:project_to) { create(:project, :repository, forked_project_link: forked_project_link) }
- let(:forked_project_link) { create(:forked_project_link) }
-
- before do
- forked_project_link.forked_from_project = project_from
- forked_project_link.forked_to_project = project_to
- forked_project_link.save!
- end
-
- it "project_to knows it is forked" do
- expect(project_to.forked?).to be_truthy
- end
-
- it "project_from is not forked" do
- expect(project_from.forked?).to be_falsey
- end
-
- it "project_to.destroy destroys fork_link" do
- project_to.destroy
-
- expect(ForkedProjectLink.exists?(id: forked_project_link.id)).to eq(false)
- end
- end
-end
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
index ab58f5c5021..b6355455c1d 100644
--- a/spec/models/global_milestone_spec.rb
+++ b/spec/models/global_milestone_spec.rb
@@ -92,41 +92,6 @@ describe GlobalMilestone do
end
end
- describe '.states_count' do
- context 'when the projects have milestones' do
- before do
- create(:closed_milestone, title: 'Active Group Milestone', project: project3)
- create(:active_milestone, title: 'Active Group Milestone', project: project1)
- create(:active_milestone, title: 'Active Group Milestone', project: project2)
- create(:closed_milestone, title: 'Closed Group Milestone', project: project1)
- create(:closed_milestone, title: 'Closed Group Milestone', project: project2)
- create(:closed_milestone, title: 'Closed Group Milestone', project: project3)
- end
-
- it 'returns the quantity of global milestones in each possible state' do
- expected_count = { opened: 1, closed: 2, all: 2 }
-
- count = described_class.states_count(Project.all)
-
- expect(count).to eq(expected_count)
- end
- end
-
- context 'when the projects do not have milestones' do
- before do
- project1
- end
-
- it 'returns 0 as the quantity of global milestones in each state' do
- expected_count = { opened: 0, closed: 0, all: 0 }
-
- count = described_class.states_count(Project.all)
-
- expect(count).to eq(expected_count)
- end
- end
- end
-
describe '#initialize' do
let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) }
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 0729eb99e78..ada00f03928 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -19,6 +19,8 @@ describe Group do
it { is_expected.to have_one(:chat_team) }
it { is_expected.to have_many(:custom_attributes).class_name('GroupCustomAttribute') }
it { is_expected.to have_many(:badges).class_name('GroupBadge') }
+ it { is_expected.to have_many(:cluster_groups).class_name('Clusters::Group') }
+ it { is_expected.to have_many(:clusters).class_name('Clusters::Cluster') }
describe '#members & #requesters' do
let(:requester) { create(:user) }
@@ -169,22 +171,42 @@ describe Group do
end
end
- describe '.visible_to_user' do
- let!(:group) { create(:group) }
- let!(:user) { create(:user) }
+ describe '.public_or_visible_to_user' do
+ let!(:private_group) { create(:group, :private) }
+ let!(:internal_group) { create(:group, :internal) }
- subject { described_class.visible_to_user(user) }
+ subject { described_class.public_or_visible_to_user(user) }
- describe 'when the user has access to a group' do
- before do
- group.add_user(user, Gitlab::Access::MAINTAINER)
- end
+ context 'when user is nil' do
+ let!(:user) { nil }
- it { is_expected.to eq([group]) }
+ it { is_expected.to match_array([group]) }
end
- describe 'when the user does not have access to any groups' do
- it { is_expected.to eq([]) }
+ context 'when user' do
+ let!(:user) { create(:user) }
+
+ context 'when user does not have access to any private group' do
+ it { is_expected.to match_array([internal_group, group]) }
+ end
+
+ context 'when user is a member of private group' do
+ before do
+ private_group.add_user(user, Gitlab::Access::DEVELOPER)
+ end
+
+ it { is_expected.to match_array([private_group, internal_group, group]) }
+ end
+
+ context 'when user is a member of private subgroup', :postgresql do
+ let!(:private_subgroup) { create(:group, :private, parent: private_group) }
+
+ before do
+ private_subgroup.add_user(user, Gitlab::Access::DEVELOPER)
+ end
+
+ it { is_expected.to match_array([private_subgroup, internal_group, group]) }
+ end
end
end
@@ -631,10 +653,10 @@ describe Group do
end
end
- describe '#secret_variables_for' do
+ describe '#ci_variables_for' do
let(:project) { create(:project, group: group) }
- let!(:secret_variable) do
+ let!(:ci_variable) do
create(:ci_group_variable, value: 'secret', group: group)
end
@@ -642,11 +664,11 @@ describe Group do
create(:ci_group_variable, :protected, value: 'protected', group: group)
end
- subject { group.secret_variables_for('ref', project) }
+ subject { group.ci_variables_for('ref', project) }
shared_examples 'ref is protected' do
it 'contains all the variables' do
- is_expected.to contain_exactly(secret_variable, protected_variable)
+ is_expected.to contain_exactly(ci_variable, protected_variable)
end
end
@@ -656,8 +678,8 @@ describe Group do
default_branch_protection: Gitlab::Access::PROTECTION_NONE)
end
- it 'contains only the secret variables' do
- is_expected.to contain_exactly(secret_variable)
+ it 'contains only the CI variables' do
+ is_expected.to contain_exactly(ci_variable)
end
end
@@ -690,9 +712,9 @@ describe Group do
end
it 'returns all variables belong to the group and parent groups' do
- expected_array1 = [protected_variable, secret_variable]
+ expected_array1 = [protected_variable, ci_variable]
expected_array2 = [variable_child, variable_child_2, variable_child_3]
- got_array = group_child_3.secret_variables_for('ref', project).to_a
+ got_array = group_child_3.ci_variables_for('ref', project).to_a
expect(got_array.shift(2)).to contain_exactly(*expected_array1)
expect(got_array).to eq(expected_array2)
diff --git a/spec/models/hooks/active_hook_filter_spec.rb b/spec/models/hooks/active_hook_filter_spec.rb
new file mode 100644
index 00000000000..df7edda2213
--- /dev/null
+++ b/spec/models/hooks/active_hook_filter_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe ActiveHookFilter do
+ subject(:filter) { described_class.new(hook) }
+
+ describe '#matches?' do
+ context 'for push event hooks' do
+ let(:hook) do
+ create(:project_hook, push_events: true, push_events_branch_filter: branch_filter)
+ end
+
+ context 'branch filter is specified' do
+ let(:branch_filter) { 'master' }
+
+ it 'returns true if branch matches' do
+ expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
+ end
+
+ it 'returns false if branch does not match' do
+ expect(filter.matches?(:push_hooks, { ref: 'refs/heads/my_branch' })).to be false
+ end
+
+ it 'returns false if ref is nil' do
+ expect(filter.matches?(:push_hooks, {})).to be false
+ end
+
+ context 'branch filter contains wildcard' do
+ let(:branch_filter) { 'features/*' }
+
+ it 'returns true if branch matches' do
+ expect(filter.matches?(:push_hooks, { ref: 'refs/heads/features/my-branch' })).to be true
+ expect(filter.matches?(:push_hooks, { ref: 'refs/heads/features/my-branch/something' })).to be true
+ end
+
+ it 'returns false if branch does not match' do
+ expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be false
+ end
+ end
+ end
+
+ context 'branch filter is not specified' do
+ let(:branch_filter) { nil }
+
+ it 'returns true' do
+ expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
+ end
+ end
+
+ context 'branch filter is empty string' do
+ let(:branch_filter) { '' }
+
+ it 'acts like branch is not specified' do
+ expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
+ end
+ end
+ end
+
+ context 'for non-push-events hooks' do
+ let(:hook) do
+ create(:project_hook, issues_events: true, push_events: false, push_events_branch_filter: '')
+ end
+
+ it 'returns true as branch filters are not yet supported for these' do
+ expect(filter.matches?(:issues_events, { ref: 'refs/heads/master' })).to be true
+ end
+ end
+ end
+end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 01129df1107..edd1cb455af 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -73,7 +73,7 @@ describe SystemHook do
it "project_destroy hook" do
project.add_maintainer(user)
- project.project_members.destroy_all
+ project.project_members.destroy_all # rubocop: disable DestroyAll
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_team/,
@@ -110,7 +110,7 @@ describe SystemHook do
it 'group member destroy hook' do
group.add_maintainer(user)
- group.group_members.destroy_all
+ group.group_members.destroy_all # rubocop: disable DestroyAll
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_group/,
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index ea6d6e53ef5..a308ac6e33a 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -35,6 +35,32 @@ describe WebHook do
it { is_expected.not_to allow_values("foo\nbar", "foo\r\nbar").for(:token) }
end
+
+ describe 'push_events_branch_filter' do
+ it { is_expected.to allow_values("good_branch_name", "another/good-branch_name").for(:push_events_branch_filter) }
+ it { is_expected.to allow_values("").for(:push_events_branch_filter) }
+ it { is_expected.not_to allow_values("bad branch name", "bad~branchname").for(:push_events_branch_filter) }
+
+ it 'gets rid of whitespace' do
+ hook.push_events_branch_filter = ' branch '
+ hook.save
+
+ expect(hook.push_events_branch_filter).to eq('branch')
+ end
+
+ it 'stores whitespace only as empty' do
+ hook.push_events_branch_filter = ' '
+ hook.save
+
+ expect(hook.push_events_branch_filter).to eq('')
+ end
+ end
+ end
+
+ describe 'encrypted attributes' do
+ subject { described_class.encrypted_attributes.keys }
+
+ it { is_expected.to contain_exactly(:token, :url) }
end
describe 'execute' do
diff --git a/spec/models/instance_configuration_spec.rb b/spec/models/instance_configuration_spec.rb
index 8548fff5c76..cb3d6c7cda2 100644
--- a/spec/models/instance_configuration_spec.rb
+++ b/spec/models/instance_configuration_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-RSpec.describe InstanceConfiguration do
+describe InstanceConfiguration do
context 'without cache' do
describe '#settings' do
describe '#ssh_algorithms_hashes' do
- let(:md5) { '54:e0:f8:70:d6:4f:4c:b1:b3:02:44:77:cf:cd:0d:fc' }
- let(:sha256) { '9327f0d15a48c4d9f6a3aee65a1825baf9a3412001c98169c5fd022ac27762fc' }
+ let(:md5) { '5a:65:6c:4d:d4:4c:6d:e6:59:25:b8:cf:ba:34:e7:64' }
+ let(:sha256) { 'SHA256:2KJDT7xf2i68mBgJ3TVsjISntg4droLbXYLfQj0VvSY' }
it 'does not return anything if file does not exist' do
stub_pub_file(exist: false)
@@ -52,7 +52,7 @@ RSpec.describe InstanceConfiguration do
expect(gitlab_pages).to eq(Settings.pages.symbolize_keys)
end
- it 'returns the Gitlab\'s pages host ip address' do
+ it 'returns the GitLab\'s pages host ip address' do
expect(gitlab_pages.keys).to include(:ip_address)
end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 20600f5fa38..52c00a74b4b 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -30,7 +30,7 @@ describe InternalId do
context 'with existing issues' do
before do
- rand(1..10).times { create(:issue, project: project) }
+ create_list(:issue, 2, project: project)
described_class.delete_all
end
@@ -54,7 +54,7 @@ describe InternalId do
end
it 'generates a strictly monotone, gapless sequence' do
- seq = (0..rand(100)).map do
+ seq = Array.new(10).map do
described_class.generate_next(issue, scope, usage, init)
end
normalized = seq.map { |i| i - seq.min }
@@ -65,7 +65,8 @@ describe InternalId do
context 'with an insufficient schema version' do
before do
described_class.reset_column_information
- expect(ActiveRecord::Migrator).to receive(:current_version).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
+ # Project factory will also call the current_version
+ expect(ActiveRecord::Migrator).to receive(:current_version).twice.and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
end
let(:init) { double('block') }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 84edfc3ff00..6f900a60213 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -84,15 +84,32 @@ describe Issue do
end
end
- describe '#closed_at' do
- it 'sets closed_at to Time.now when issue is closed' do
- issue = create(:issue, state: 'opened')
+ describe '#close' do
+ subject(:issue) { create(:issue, state: 'opened') }
- expect(issue.closed_at).to be_nil
+ it 'sets closed_at to Time.now when an issue is closed' do
+ expect { issue.close }.to change { issue.closed_at }.from(nil)
+ end
+
+ it 'changes the state to closed' do
+ expect { issue.close }.to change { issue.state }.from('opened').to('closed')
+ end
+ end
- issue.close
+ describe '#reopen' do
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, state: 'closed', closed_at: Time.now, closed_by: user) }
+
+ it 'sets closed_at to nil when an issue is reopend' do
+ expect { issue.reopen }.to change { issue.closed_at }.to(nil)
+ end
- expect(issue.closed_at).to be_present
+ it 'sets closed_by to nil when an issue is reopend' do
+ expect { issue.reopen }.to change { issue.closed_by }.from(user).to(nil)
+ end
+
+ it 'changes the state to opened' do
+ expect { issue.reopen }.to change { issue.state }.from('closed').to('opened')
end
end
@@ -188,98 +205,6 @@ describe Issue do
end
end
- describe '#closed_by_merge_requests' do
- let(:project) { create(:project, :repository) }
- let(:issue) { create(:issue, project: project)}
- let(:closed_issue) { build(:issue, :closed, project: project)}
-
- let(:mr) do
- opts = {
- title: 'Awesome merge_request',
- description: "Fixes #{issue.to_reference}",
- source_branch: 'feature',
- target_branch: 'master'
- }
- MergeRequests::CreateService.new(project, project.owner, opts).execute
- end
-
- let(:closed_mr) do
- opts = {
- title: 'Awesome merge_request 2',
- description: "Fixes #{issue.to_reference}",
- source_branch: 'feature',
- target_branch: 'master',
- state: 'closed'
- }
- MergeRequests::CreateService.new(project, project.owner, opts).execute
- end
-
- it 'returns the merge request to close this issue' do
- expect(issue.closed_by_merge_requests(mr.author)).to eq([mr])
- end
-
- it "returns an empty array when the merge request is closed already" do
- expect(issue.closed_by_merge_requests(closed_mr.author)).to eq([])
- end
-
- it "returns an empty array when the current issue is closed already" do
- expect(closed_issue.closed_by_merge_requests(closed_issue.author)).to eq([])
- end
- end
-
- describe '#referenced_merge_requests' do
- let(:project) { create(:project, :public) }
- let(:issue) do
- create(:issue, description: merge_request.to_reference, project: project)
- end
- let!(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: 'master',
- target_branch: 'feature')
- end
-
- it 'returns the referenced merge requests' do
- mr2 = create(:merge_request,
- source_project: project,
- source_branch: 'feature',
- target_branch: 'master')
-
- create(:note_on_issue,
- noteable: issue,
- note: mr2.to_reference,
- project_id: project.id)
-
- expect(issue.referenced_merge_requests).to eq([merge_request, mr2])
- end
-
- it 'returns cross project referenced merge requests' do
- other_project = create(:project, :public)
- cross_project_merge_request = create(:merge_request, source_project: other_project)
- create(:note_on_issue,
- noteable: issue,
- note: cross_project_merge_request.to_reference(issue.project),
- project_id: issue.project.id)
-
- expect(issue.referenced_merge_requests).to eq([merge_request, cross_project_merge_request])
- end
-
- it 'excludes cross project references if the user cannot read cross project' do
- user = create(:user)
- allow(Ability).to receive(:allowed?).and_call_original
- expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
-
- other_project = create(:project, :public)
- cross_project_merge_request = create(:merge_request, source_project: other_project)
- create(:note_on_issue,
- noteable: issue,
- note: cross_project_merge_request.to_reference(issue.project),
- project_id: issue.project.id)
-
- expect(issue.referenced_merge_requests(user)).to eq([merge_request])
- end
- end
-
describe '#can_move?' do
let(:user) { create(:user) }
let(:issue) { create(:issue) }
@@ -343,40 +268,6 @@ describe Issue do
end
end
- describe '#related_branches' do
- let(:user) { create(:admin) }
-
- before do
- allow(subject.project.repository).to receive(:branch_names)
- .and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"])
-
- # Without this stub, the `create(:merge_request)` above fails because it can't find
- # the source branch. This seems like a reasonable compromise, in comparison with
- # setting up a full repo here.
- allow_any_instance_of(MergeRequest).to receive(:create_merge_request_diff)
- end
-
- it "selects the right branches when there are no referenced merge requests" do
- expect(subject.related_branches(user)).to eq([subject.to_branch_name, "#{subject.iid}-branch"])
- end
-
- it "selects the right branches when there is a referenced merge request" do
- merge_request = create(:merge_request, { description: "Closes ##{subject.iid}",
- source_project: subject.project,
- source_branch: "#{subject.iid}-branch" })
- merge_request.create_cross_references!(user)
- expect(subject.referenced_merge_requests(user)).not_to be_empty
- expect(subject.related_branches(user)).to eq([subject.to_branch_name])
- end
-
- it 'excludes stable branches from the related branches' do
- allow(subject.project.repository).to receive(:branch_names)
- .and_return(["#{subject.iid}-0-stable"])
-
- expect(subject.related_branches(user)).to eq []
- end
- end
-
describe '#suggested_branch_name' do
let(:repository) { double }
diff --git a/spec/models/label_note_spec.rb b/spec/models/label_note_spec.rb
new file mode 100644
index 00000000000..f69874d94aa
--- /dev/null
+++ b/spec/models/label_note_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe LabelNote do
+ set(:project) { create(:project, :repository) }
+ set(:user) { create(:user) }
+ set(:label) { create(:label, project: project) }
+ set(:label2) { create(:label, project: project) }
+ let(:resource_parent) { project }
+
+ context 'when resource is issue' do
+ set(:resource) { create(:issue, project: project) }
+
+ it_behaves_like 'label note created from events'
+ end
+
+ context 'when resource is merge request' do
+ set(:resource) { create(:merge_request, source_project: project, target_project: project) }
+
+ it_behaves_like 'label note created from events'
+ end
+end
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 99670af786a..3fc6c06b7fa 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -155,4 +155,40 @@ describe Label do
expect(described_class.search('feature')).to be_empty
end
end
+
+ describe '.subscribed_by' do
+ let!(:user) { create(:user) }
+ let!(:label) { create(:label) }
+ let!(:label2) { create(:label) }
+
+ before do
+ label.subscribe(user)
+ end
+
+ it 'returns subscribed labels' do
+ expect(described_class.subscribed_by(user.id)).to eq([label])
+ end
+
+ it 'returns nothing' do
+ expect(described_class.subscribed_by(0)).to be_empty
+ end
+ end
+
+ describe '.optionally_subscribed_by' do
+ let!(:user) { create(:user) }
+ let!(:label) { create(:label) }
+ let!(:label2) { create(:label) }
+
+ before do
+ label.subscribe(user)
+ end
+
+ it 'returns subscribed labels' do
+ expect(described_class.optionally_subscribed_by(user.id)).to eq([label])
+ end
+
+ it 'returns all labels if user_id is nil' do
+ expect(described_class.optionally_subscribed_by(nil)).to match_array([label, label2])
+ end
+ end
end
diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb
index 6e35511e848..3f929710862 100644
--- a/spec/models/lfs_object_spec.rb
+++ b/spec/models/lfs_object_spec.rb
@@ -2,19 +2,13 @@ require 'spec_helper'
describe LfsObject do
describe '#local_store?' do
- it 'returns true when file_store is nil' do
- subject.file_store = nil
-
- expect(subject.local_store?).to eq true
- end
-
it 'returns true when file_store is equal to LfsObjectUploader::Store::LOCAL' do
subject.file_store = LfsObjectUploader::Store::LOCAL
expect(subject.local_store?).to eq true
end
- it 'returns false whe file_store is equal to LfsObjectUploader::Store::REMOTE' do
+ it 'returns false when file_store is equal to LfsObjectUploader::Store::REMOTE' do
subject.file_store = LfsObjectUploader::Store::REMOTE
expect(subject.local_store?).to eq false
@@ -83,19 +77,6 @@ describe LfsObject do
describe 'file is being stored' do
let(:lfs_object) { create(:lfs_object, :with_file) }
- context 'when object has nil store' do
- before do
- lfs_object.update_column(:file_store, nil)
- lfs_object.reload
- end
-
- it 'is stored locally' do
- expect(lfs_object.file_store).to be(nil)
- expect(lfs_object.file).to be_file_storage
- expect(lfs_object.file.object_store).to eq(ObjectStorage::Store::LOCAL)
- end
- end
-
context 'when existing object has local store' do
it 'is stored locally' do
expect(lfs_object.file_store).to be(ObjectStorage::Store::LOCAL)
diff --git a/spec/models/license_template_spec.rb b/spec/models/license_template_spec.rb
new file mode 100644
index 00000000000..dd912eefac1
--- /dev/null
+++ b/spec/models/license_template_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe LicenseTemplate do
+ describe '#content' do
+ it 'calls a proc exactly once if provided' do
+ lazy = build_template(-> { 'bar' })
+ content = lazy.content
+
+ expect(content).to eq('bar')
+ expect(content.object_id).to eq(lazy.content.object_id)
+
+ content.replace('foo')
+ expect(lazy.content).to eq('foo')
+ end
+
+ it 'returns a string if provided' do
+ lazy = build_template('bar')
+
+ expect(lazy.content).to eq('bar')
+ end
+ end
+
+ describe '#resolve!' do
+ let(:content) do
+ <<~TEXT
+ Pretend License
+
+ [project]
+
+ Copyright (c) [year] [fullname]
+ TEXT
+ end
+
+ let(:expected) do
+ <<~TEXT
+ Pretend License
+
+ Foo Project
+
+ Copyright (c) 1985 Nick Thomas
+ TEXT
+ end
+
+ let(:template) { build_template(content) }
+
+ it 'updates placeholders in a copy of the template content' do
+ expect(template.content.object_id).to eq(content.object_id)
+
+ template.resolve!(project_name: "Foo Project", fullname: "Nick Thomas", year: "1985")
+
+ expect(template.content).to eq(expected)
+ expect(template.content.object_id).not_to eq(content.object_id)
+ end
+ end
+
+ def build_template(content)
+ described_class.new(key: 'foo', name: 'foo', category: :Other, content: content)
+ end
+end
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 90cce826b6c..cbe60b3a4a5 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -52,9 +52,9 @@ describe MergeRequestDiff do
context 'when it was not cleaned by the system' do
it 'returns persisted diffs' do
- expect(diff).to receive(:load_diffs)
+ expect(diff).to receive(:load_diffs).and_call_original
- diff.diffs
+ diff.diffs.diff_files
end
end
@@ -76,19 +76,19 @@ describe MergeRequestDiff do
end
it 'returns persisted diffs if cannot compare with diff refs' do
- expect(diff).to receive(:load_diffs)
+ expect(diff).to receive(:load_diffs).and_call_original
diff.update!(head_commit_sha: 'invalid-sha')
- diff.diffs
+ diff.diffs.diff_files
end
it 'returns persisted diffs if diff refs does not exist' do
- expect(diff).to receive(:load_diffs)
+ expect(diff).to receive(:load_diffs).and_call_original
diff.update!(start_commit_sha: nil, base_commit_sha: nil)
- diff.diffs
+ diff.diffs.diff_files
end
end
end
@@ -211,4 +211,38 @@ describe MergeRequestDiff do
expect(diff_with_commits.commits_count).to eq(29)
end
end
+
+ describe '#commits_by_shas' do
+ let(:commit_shas) { diff_with_commits.commit_shas }
+
+ it 'returns empty if no SHAs were provided' do
+ expect(diff_with_commits.commits_by_shas([])).to be_empty
+ end
+
+ it 'returns one SHA' do
+ commits = diff_with_commits.commits_by_shas([commit_shas.first, Gitlab::Git::BLANK_SHA])
+
+ expect(commits.count).to eq(1)
+ end
+
+ it 'returns all matching SHAs' do
+ commits = diff_with_commits.commits_by_shas(commit_shas)
+
+ expect(commits.count).to eq(commit_shas.count)
+ expect(commits.map(&:sha)).to match_array(commit_shas)
+ end
+ end
+
+ describe '#modified_paths' do
+ subject do
+ diff = create(:merge_request_diff)
+ create(:merge_request_diff_file, :new_file, merge_request_diff: diff)
+ create(:merge_request_diff_file, :renamed_file, merge_request_diff: diff)
+ diff
+ end
+
+ it 'returns affected file paths' do
+ expect(subject.modified_paths).to eq(%w{foo bar baz})
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 6258bfa232f..a58dc8e25e8 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -13,6 +13,20 @@ describe MergeRequest do
it { is_expected.to belong_to(:merge_user).class_name("User") }
it { is_expected.to belong_to(:assignee) }
it { is_expected.to have_many(:merge_request_diffs) }
+
+ context 'for forks' do
+ let!(:project) { create(:project) }
+ let!(:fork) { fork_project(project) }
+ let!(:merge_request) { create(:merge_request, target_project: project, source_project: fork) }
+
+ it 'does not load another project due to inverse relationship' do
+ expect(project.merge_requests.first.target_project.object_id).to eq(project.object_id)
+ end
+
+ it 'finds the associated merge request' do
+ expect(project.merge_requests.find(merge_request.id)).to eq(merge_request)
+ end
+ end
end
describe '#squash_in_progress?' do
@@ -538,9 +552,9 @@ describe MergeRequest do
it 'delegates to the MR diffs' do
merge_request.save
- expect(merge_request.merge_request_diff).to receive(:raw_diffs).with(hash_including(options))
+ expect(merge_request.merge_request_diff).to receive(:raw_diffs).with(hash_including(options)).and_call_original
- merge_request.diffs(options)
+ merge_request.diffs(options).diff_files
end
end
@@ -617,6 +631,44 @@ describe MergeRequest do
end
end
+ describe '#modified_paths' do
+ let(:paths) { double(:paths) }
+ subject(:merge_request) { build(:merge_request) }
+
+ before do
+ expect(diff).to receive(:modified_paths).and_return(paths)
+ end
+
+ context 'when past_merge_request_diff is specified' do
+ let(:another_diff) { double(:merge_request_diff) }
+ let(:diff) { another_diff }
+
+ it 'returns affected file paths from specified past_merge_request_diff' do
+ expect(merge_request.modified_paths(past_merge_request_diff: another_diff)).to eq(paths)
+ end
+ end
+
+ context 'when compare is present' do
+ let(:compare) { double(:compare) }
+ let(:diff) { compare }
+
+ it 'returns affected file paths from compare' do
+ merge_request.compare = compare
+
+ expect(merge_request.modified_paths).to eq(paths)
+ end
+ end
+
+ context 'when no arguments provided' do
+ let(:diff) { merge_request.merge_request_diff }
+ subject(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master') }
+
+ it 'returns affected file paths for merge_request_diff' do
+ expect(merge_request.modified_paths).to eq(paths)
+ end
+ end
+ end
+
describe "#related_notes" do
let!(:merge_request) { create(:merge_request) }
@@ -746,7 +798,7 @@ describe MergeRequest do
end
describe "#wipless_title" do
- ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
+ ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', '[WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
it "removes the '#{wip_prefix}' prefix" do
wipless_title = subject.title
subject.title = "#{wip_prefix}#{subject.title}"
@@ -1058,6 +1110,26 @@ describe MergeRequest do
end
end
+ describe '#merge_pipeline' do
+ it 'returns nil when not merged' do
+ expect(subject.merge_pipeline).to be_nil
+ end
+
+ context 'when the MR is merged' do
+ let(:sha) { subject.target_project.commit.id }
+ let(:pipeline) { create(:ci_empty_pipeline, sha: sha, ref: subject.target_branch, project: subject.target_project) }
+
+ before do
+ subject.mark_as_merged!
+ subject.update_attribute(:merge_commit_sha, pipeline.sha)
+ end
+
+ it 'returns the post-merge pipeline' do
+ expect(subject.merge_pipeline).to eq(pipeline)
+ end
+ end
+ end
+
describe '#has_ci?' do
let(:merge_request) { build_stubbed(:merge_request) }
@@ -1710,7 +1782,7 @@ describe MergeRequest do
allow(subject).to receive(:head_pipeline) { nil }
end
- it { expect(subject.mergeable_ci_state?).to be_truthy }
+ it { expect(subject.mergeable_ci_state?).to be_falsey }
end
end
@@ -1764,7 +1836,7 @@ describe MergeRequest do
context 'with no discussions' do
before do
- merge_request.notes.destroy_all
+ merge_request.notes.destroy_all # rubocop: disable DestroyAll
end
it 'returns true' do
@@ -1802,8 +1874,8 @@ describe MergeRequest do
let(:environments) { create_list(:environment, 3, project: project) }
before do
- create(:deployment, environment: environments.first, ref: 'master', sha: project.commit('master').id)
- create(:deployment, environment: environments.second, ref: 'feature', sha: project.commit('feature').id)
+ create(:deployment, :success, environment: environments.first, ref: 'master', sha: project.commit('master').id)
+ create(:deployment, :success, environment: environments.second, ref: 'feature', sha: project.commit('feature').id)
end
it 'selects deployed environments' do
@@ -1823,7 +1895,7 @@ describe MergeRequest do
let(:source_environment) { create(:environment, project: source_project) }
before do
- create(:deployment, environment: source_environment, ref: 'feature', sha: merge_request.diff_head_sha)
+ create(:deployment, :success, environment: source_environment, ref: 'feature', sha: merge_request.diff_head_sha)
end
it 'selects deployed environments' do
@@ -1834,7 +1906,7 @@ describe MergeRequest do
let(:target_environment) { create(:environment, project: project) }
before do
- create(:deployment, environment: target_environment, tag: true, sha: merge_request.diff_head_sha)
+ create(:deployment, :success, environment: target_environment, tag: true, sha: merge_request.diff_head_sha)
end
it 'selects deployed environments' do
@@ -2577,6 +2649,36 @@ describe MergeRequest do
end
end
+ describe '#includes_any_commits?' do
+ it 'returns false' do
+ expect(subject.includes_any_commits?([])).to be_falsey
+ end
+
+ it 'returns false' do
+ expect(subject.includes_any_commits?([Gitlab::Git::BLANK_SHA])).to be_falsey
+ end
+
+ it 'returns true' do
+ expect(subject.includes_any_commits?([subject.merge_request_diff.head_commit_sha])).to be_truthy
+ end
+
+ it 'returns true even when there is a non-existent comit' do
+ expect(subject.includes_any_commits?([Gitlab::Git::BLANK_SHA, subject.merge_request_diff.head_commit_sha])).to be_truthy
+ end
+
+ context 'unpersisted merge request' do
+ let(:new_mr) { build(:merge_request) }
+
+ it 'returns false' do
+ expect(new_mr.includes_any_commits?([Gitlab::Git::BLANK_SHA])).to be_falsey
+ end
+
+ it 'returns true' do
+ expect(new_mr.includes_any_commits?([subject.merge_request_diff.head_commit_sha])).to be_truthy
+ end
+ end
+ end
+
describe '#can_allow_collaboration?' do
let(:target_project) { create(:project, :public) }
let(:source_project) { fork_project(target_project) }
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 55b984faecf..d11eb46159e 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -95,6 +95,24 @@ describe Milestone do
end
end
+ describe '.order_by_name_asc' do
+ it 'sorts by name ascending' do
+ milestone1 = create(:milestone, title: 'Foo')
+ milestone2 = create(:milestone, title: 'Bar')
+
+ expect(described_class.order_by_name_asc).to eq([milestone2, milestone1])
+ end
+ end
+
+ describe '.reorder_by_due_date_asc' do
+ it 'reorders the input relation' do
+ milestone1 = create(:milestone, due_date: Date.new(2018, 9, 30))
+ milestone2 = create(:milestone, due_date: Date.new(2018, 10, 20))
+
+ expect(described_class.reorder_by_due_date_asc).to eq([milestone1, milestone2])
+ end
+ end
+
describe "#percent_complete" do
it "does not count open issues" do
milestone.issues << issue
@@ -330,4 +348,41 @@ describe Milestone do
end
end
end
+
+ describe '.states_count' do
+ context 'when the projects have milestones' do
+ before do
+ project_1 = create(:project)
+ project_2 = create(:project)
+ group_1 = create(:group)
+ group_2 = create(:group)
+
+ create(:active_milestone, title: 'Active Group Milestone', project: project_1)
+ create(:closed_milestone, title: 'Closed Group Milestone', project: project_1)
+ create(:active_milestone, title: 'Active Group Milestone', project: project_2)
+ create(:closed_milestone, title: 'Closed Group Milestone', project: project_2)
+ create(:closed_milestone, title: 'Active Group Milestone', group: group_1)
+ create(:closed_milestone, title: 'Closed Group Milestone', group: group_1)
+ create(:closed_milestone, title: 'Active Group Milestone', group: group_2)
+ create(:closed_milestone, title: 'Closed Group Milestone', group: group_2)
+ end
+
+ it 'returns the quantity of milestones in each possible state' do
+ expected_count = { opened: 5, closed: 6, all: 11 }
+
+ count = described_class.states_count(Project.all, Group.all)
+ expect(count).to eq(expected_count)
+ end
+ end
+
+ context 'when the projects do not have milestones' do
+ it 'returns 0 as the quantity of global milestones in each state' do
+ expected_count = { opened: 0, closed: 0, all: 0 }
+
+ count = described_class.states_count([project])
+
+ expect(count).to eq(expected_count)
+ end
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 9b7f932ec3a..2db42fe802a 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe Namespace do
include ProjectForksHelper
+ include GitHelpers
let!(:namespace) { create(:namespace) }
let(:gitlab_shell) { Gitlab::Shell.new }
@@ -82,6 +83,27 @@ describe Namespace do
it { expect(namespace.human_name).to eq(namespace.owner_name) }
end
+ describe '#first_project_with_container_registry_tags' do
+ let(:container_repository) { create(:container_repository) }
+ let!(:project) { create(:project, namespace: namespace, container_repositories: [container_repository]) }
+
+ before do
+ stub_container_registry_config(enabled: true)
+ end
+
+ it 'returns the project' do
+ stub_container_registry_tags(repository: :any, tags: ['tag'])
+
+ expect(namespace.first_project_with_container_registry_tags).to eq(project)
+ end
+
+ it 'returns no project' do
+ stub_container_registry_tags(repository: :any, tags: nil)
+
+ expect(namespace.first_project_with_container_registry_tags).to be_nil
+ end
+ end
+
describe '.search' do
let(:namespace) { create(:namespace) }
@@ -184,7 +206,8 @@ describe Namespace do
end
it 'raises an error about not movable project' do
- expect { namespace.move_dir }.to raise_error(/Namespace cannot be moved/)
+ expect { namespace.move_dir }.to raise_error(Gitlab::UpdatePathError,
+ /Namespace .* cannot be moved/)
end
end
end
@@ -339,9 +362,7 @@ describe Namespace do
end
def project_rugged(project)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged
- end
+ rugged_repo(project.repository)
end
end
@@ -394,12 +415,6 @@ describe Namespace do
child.destroy
end
end
-
- it 'removes the exports folder' do
- expect(namespace).to receive(:remove_exports!)
-
- namespace.destroy
- end
end
context 'hashed storage' do
@@ -414,12 +429,6 @@ describe Namespace do
expect(File.exist?(deleted_path_in_dir)).to be(false)
end
-
- it 'removes the exports folder' do
- expect(namespace).to receive(:remove_exports!)
-
- namespace.destroy
- end
end
end
@@ -553,6 +562,17 @@ describe Namespace do
it { expect(group.all_projects.to_a).to match_array([project2, project1]) }
end
+ describe '#all_pipelines' do
+ let(:group) { create(:group) }
+ let(:child) { create(:group, parent: group) }
+ let!(:project1) { create(:project_empty_repo, namespace: group) }
+ let!(:project2) { create(:project_empty_repo, namespace: child) }
+ let!(:pipeline1) { create(:ci_empty_pipeline, project: project1) }
+ let!(:pipeline2) { create(:ci_empty_pipeline, project: project2) }
+
+ it { expect(group.all_pipelines.to_a).to match_array([pipeline1, pipeline2]) }
+ end
+
describe '#share_with_group_lock with subgroups', :nested_groups do
context 'when creating a subgroup' do
let(:subgroup) { create(:group, parent: root_group )}
@@ -706,26 +726,6 @@ describe Namespace do
end
end
- describe '#remove_exports' do
- let(:legacy_project) { create(:project, :with_export, :legacy_storage, namespace: namespace) }
- let(:hashed_project) { create(:project, :with_export, namespace: namespace) }
- let(:export_path) { Dir.mktmpdir('namespace_remove_exports_spec') }
- let(:legacy_export) { legacy_project.export_project_path }
- let(:hashed_export) { hashed_project.export_project_path }
-
- it 'removes exports for legacy and hashed projects' do
- allow(Gitlab::ImportExport).to receive(:storage_path) { export_path }
-
- expect(File.exist?(legacy_export)).to be_truthy
- expect(File.exist?(hashed_export)).to be_truthy
-
- namespace.remove_exports!
-
- expect(File.exist?(legacy_export)).to be_falsy
- expect(File.exist?(hashed_export)).to be_falsy
- end
- end
-
describe '#full_path_was' do
context 'when the group has no parent' do
it 'should return the path was' do
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 947be44c903..f9be61e4768 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -231,33 +231,60 @@ describe Note do
let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
- let(:note) do
- create :note,
- noteable: ext_issue, project: ext_proj,
- note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
- system: true
- end
+ shared_examples "checks references" do
+ it "returns true" do
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
- it "returns true" do
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
- end
+ it "returns false" do
+ expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
+ end
- it "returns false" do
- expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
+ it "returns false if user visible reference count set" do
+ note.user_visible_reference_count = 1
+ note.total_reference_count = 1
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_falsy
+ end
+
+ it "returns true if ref count is 0" do
+ note.user_visible_reference_count = 0
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
end
- it "returns false if user visible reference count set" do
- note.user_visible_reference_count = 1
+ context "when there is one reference in note" do
+ let(:note) do
+ create :note,
+ noteable: ext_issue, project: ext_proj,
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
+ system: true
+ end
- expect(note).not_to receive(:reference_mentionables)
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_falsy
+ it_behaves_like "checks references"
end
- it "returns true if ref count is 0" do
- note.user_visible_reference_count = 0
+ context "when there are two references in note" do
+ let(:note) do
+ create :note,
+ noteable: ext_issue, project: ext_proj,
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)} and " \
+ "public issue #{ext_issue.to_reference(ext_proj)}",
+ system: true
+ end
+
+ it_behaves_like "checks references"
- expect(note).not_to receive(:reference_mentionables)
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ it "returns true if user visible reference count set and there is a private reference" do
+ note.user_visible_reference_count = 1
+ note.total_reference_count = 2
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
end
end
@@ -269,7 +296,7 @@ describe Note do
end
context 'when the note might contain cross references' do
- SystemNoteMetadata::TYPES_WITH_CROSS_REFERENCES.each do |type|
+ SystemNoteMetadata.new.cross_reference_types.each do |type|
let(:note) { create(:note, :system) }
let!(:metadata) { create(:system_note_metadata, note: note, action: type) }
@@ -838,5 +865,29 @@ describe Note do
note.save!
end
end
+
+ describe '#with_notes_filter' do
+ let!(:comment) { create(:note) }
+ let!(:system_note) { create(:note, system: true) }
+
+ context 'when notes filter is nil' do
+ subject { described_class.with_notes_filter(nil) }
+
+ it { is_expected.to include(comment, system_note) }
+ end
+
+ context 'when notes filter is set to all notes' do
+ subject { described_class.with_notes_filter(UserPreference::NOTES_FILTERS[:all_notes]) }
+
+ it { is_expected.to include(comment, system_note) }
+ end
+
+ context 'when notes filter is set to only comments' do
+ subject { described_class.with_notes_filter(UserPreference::NOTES_FILTERS[:only_comments]) }
+
+ it { is_expected.to include(comment) }
+ it { is_expected.not_to include(system_note) }
+ end
+ end
end
end
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 77c475b9f52..e545b674b4f 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -94,9 +94,39 @@ RSpec.describe NotificationSetting do
end
end
- context 'email events' do
- it 'includes EXCLUDED_WATCHER_EVENTS in EMAIL_EVENTS' do
- expect(described_class::EMAIL_EVENTS).to include(*described_class::EXCLUDED_WATCHER_EVENTS)
+ describe '.email_events' do
+ subject { described_class.email_events }
+
+ it 'returns email events' do
+ expect(subject).to include(
+ :new_note,
+ :new_issue,
+ :reopen_issue,
+ :close_issue,
+ :reassign_issue,
+ :new_merge_request,
+ :reopen_merge_request,
+ :close_merge_request,
+ :reassign_merge_request,
+ :merge_merge_request,
+ :failed_pipeline,
+ :success_pipeline
+ )
+ end
+
+ it 'includes EXCLUDED_WATCHER_EVENTS' do
+ expect(subject).to include(*described_class::EXCLUDED_WATCHER_EVENTS)
+ end
+ end
+
+ describe '#email_events' do
+ let(:source) { build(:group) }
+
+ subject { build(:notification_setting, source: source) }
+
+ it 'calls email_events' do
+ expect(described_class).to receive(:email_events).with(source)
+ subject.email_events
end
end
end
diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb
index 2bb1c49b740..c82ab9c9e62 100644
--- a/spec/models/personal_access_token_spec.rb
+++ b/spec/models/personal_access_token_spec.rb
@@ -49,18 +49,36 @@ describe PersonalAccessToken do
describe 'Redis storage' do
let(:user_id) { 123 }
- let(:token) { 'abc000foo' }
+ let(:token) { 'KS3wegQYXBLYhQsciwsj' }
- before do
- subject.redis_store!(user_id, token)
+ context 'reading encrypted data' do
+ before do
+ subject.redis_store!(user_id, token)
+ end
+
+ it 'returns stored data' do
+ expect(subject.redis_getdel(user_id)).to eq(token)
+ end
end
- it 'returns stored data' do
- expect(subject.redis_getdel(user_id)).to eq(token)
+ context 'reading unencrypted data' do
+ before do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(described_class.redis_shared_state_key(user_id),
+ token,
+ ex: PersonalAccessToken::REDIS_EXPIRY_TIME)
+ end
+ end
+
+ it 'returns stored data unmodified' do
+ expect(subject.redis_getdel(user_id)).to eq(token)
+ end
end
context 'after deletion' do
before do
+ subject.redis_store!(user_id, token)
+
expect(subject.redis_getdel(user_id)).to eq(token)
end
diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb
index 919a7526803..e100af7ddc7 100644
--- a/spec/models/postgresql/replication_slot_spec.rb
+++ b/spec/models/postgresql/replication_slot_spec.rb
@@ -3,7 +3,27 @@
require 'spec_helper'
describe Postgresql::ReplicationSlot, :postgresql do
+ describe '.in_use?' do
+ it 'returns true when replication slots are present' do
+ expect(described_class).to receive(:exists?).and_return(true)
+ expect(described_class.in_use?).to be_truthy
+ end
+
+ it 'returns false when replication slots are not present' do
+ expect(described_class.in_use?).to be_falsey
+ end
+
+ it 'returns false if the existence check is invalid' do
+ expect(described_class).to receive(:exists?).and_raise(ActiveRecord::StatementInvalid.new('PG::FeatureNotSupported'))
+ expect(described_class.in_use?).to be_falsey
+ end
+ end
+
describe '.lag_too_great?' do
+ before do
+ expect(described_class).to receive(:in_use?).and_return(true)
+ end
+
it 'returns true when replication lag is too great' do
expect(described_class)
.to receive(:pluck)
diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb
index 749b2094787..342798f730b 100644
--- a/spec/models/project_auto_devops_spec.rb
+++ b/spec/models/project_auto_devops_spec.rb
@@ -70,24 +70,31 @@ describe ProjectAutoDevops do
end
context 'when deploy_strategy is manual' do
- let(:domain) { 'example.com' }
-
- before do
- auto_devops.deploy_strategy = 'manual'
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :manual_deployment, project: project) }
+ let(:expected_variables) do
+ [
+ { key: 'INCREMENTAL_ROLLOUT_MODE', value: 'manual' },
+ { key: 'STAGING_ENABLED', value: '1' },
+ { key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1' }
+ ]
end
+ it { expect(auto_devops.predefined_variables).to include(*expected_variables) }
+ end
+
+ context 'when deploy_strategy is continuous' do
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :continuous_deployment, project: project) }
+
it do
expect(auto_devops.predefined_variables.map { |var| var[:key] })
- .to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED")
+ .not_to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED")
end
end
- context 'when deploy_strategy is continuous' do
- let(:domain) { 'example.com' }
+ context 'when deploy_strategy is timed_incremental' do
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :timed_incremental_deployment, project: project) }
- before do
- auto_devops.deploy_strategy = 'continuous'
- end
+ it { expect(auto_devops.predefined_variables).to include(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'timed') }
it do
expect(auto_devops.predefined_variables.map { |var| var[:key] })
@@ -100,7 +107,7 @@ describe ProjectAutoDevops do
end
end
- describe '#set_gitlab_deploy_token' do
+ describe '#create_gitlab_deploy_token' do
let(:auto_devops) { build(:project_auto_devops, project: project) }
context 'when the project is public' do
@@ -144,9 +151,9 @@ describe ProjectAutoDevops do
end
end
- context 'when autodevops is enabled at instancel level' do
+ context 'when autodevops is enabled at instance level' do
let(:project) { create(:project, :repository, :internal) }
- let(:auto_devops) { build(:project_auto_devops, :disabled, project: project) }
+ let(:auto_devops) { build(:project_auto_devops, enabled: nil, project: project) }
it 'should create a deploy token' do
allow(Gitlab::CurrentSettings).to receive(:auto_devops_enabled?).and_return(true)
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 10617edec0f..fee7d65c217 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -17,7 +17,7 @@ describe ProjectFeature do
end
describe '#feature_available?' do
- let(:features) { %w(issues wiki builds merge_requests snippets repository) }
+ let(:features) { %w(issues wiki builds merge_requests snippets repository pages) }
context 'when features are disabled' do
it "returns false" do
@@ -73,6 +73,22 @@ describe ProjectFeature do
end
end
end
+
+ context 'when feature is disabled by a feature flag' do
+ it 'returns false' do
+ stub_feature_flags(issues: false)
+
+ expect(project.feature_available?(:issues, user)).to eq(false)
+ end
+ end
+
+ context 'when feature is enabled by a feature flag' do
+ it 'returns true' do
+ stub_feature_flags(issues: true)
+
+ expect(project.feature_available?(:issues, user)).to eq(true)
+ end
+ end
end
context 'repository related features' do
@@ -96,6 +112,19 @@ describe ProjectFeature do
end
end
+ context 'public features' do
+ it "does not allow public for other than pages" do
+ features = %w(issues wiki builds merge_requests snippets repository)
+ project_feature = project.project_feature
+
+ features.each do |feature|
+ field = "#{feature}_access_level".to_sym
+ project_feature.update_attribute(field, ProjectFeature::PUBLIC)
+ expect(project_feature.valid?).to be_falsy
+ end
+ end
+ end
+
describe '#*_enabled?' do
let(:features) { %w(wiki builds merge_requests) }
@@ -119,46 +148,4 @@ describe ProjectFeature do
end
end
end
-
- context 'Site Statistics' do
- set(:project_with_wiki) { create(:project, :wiki_enabled) }
- set(:project_without_wiki) { create(:project, :wiki_disabled) }
-
- context 'when creating a project' do
- it 'tracks wiki availability when wikis are enabled by default' do
- expect { create(:project) }.to change { SiteStatistic.fetch.wikis_count }.by(1)
- end
-
- it 'does not track wiki availability when wikis are disabled by default' do
- expect { create(:project, :wiki_disabled) }.not_to change { SiteStatistic.fetch.wikis_count }
- end
- end
-
- context 'when updating a project_feature' do
- it 'untracks wiki availability when disabling wiki access' do
- expect { project_with_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) }
- .to change { SiteStatistic.fetch.wikis_count }.by(-1)
- end
-
- it 'tracks again wiki availability when re-enabling wiki access as public' do
- expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED) }
- .to change { SiteStatistic.fetch.wikis_count }.by(1)
- end
-
- it 'tracks again wiki availability when re-enabling wiki access as private' do
- expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::PRIVATE) }
- .to change { SiteStatistic.fetch.wikis_count }.by(1)
- end
- end
-
- context 'when removing a project' do
- it 'untracks wiki availability when removing a project with previous wiki access' do
- expect { project_with_wiki.destroy }.to change { SiteStatistic.fetch.wikis_count }.by(-1)
- end
-
- it 'does not untrack wiki availability when removing a project without wiki access' do
- expect { project_without_wiki.destroy }.not_to change { SiteStatistic.fetch.wikis_count }
- end
- end
- end
end
diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb
index 1fccf92627a..5bea21427d4 100644
--- a/spec/models/project_group_link_spec.rb
+++ b/spec/models/project_group_link_spec.rb
@@ -41,7 +41,7 @@ describe ProjectGroupLink do
project.project_group_links.create(group: group)
group_users.each { |user| expect(user.authorized_projects).to include(project) }
- project.project_group_links.destroy_all
+ project.project_group_links.destroy_all # rubocop: disable DestroyAll
group_users.each { |user| expect(user.authorized_projects).not_to include(project) }
end
end
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index f4f7afb1b92..ee84fa95f0e 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -245,6 +245,7 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end
def bamboo_response(result_key: 42, build_state: 'success', size: 1)
- %Q({"results":{"results":{"size":"#{size}","result":{"buildState":"#{build_state}","planResultKey":{"key":"#{result_key}"}}}}})
+ # reference: https://docs.atlassian.com/atlassian-bamboo/REST/6.2.5/#d2e786
+ %Q({"results":{"results":{"size":"#{size}","result":[{"buildState":"#{build_state}","planResultKey":{"key":"#{result_key}"}}]}}})
end
end
diff --git a/spec/models/project_services/chat_message/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb
index 184a07ae0f9..7997b5bb6b9 100644
--- a/spec/models/project_services/chat_message/merge_message_spec.rb
+++ b/spec/models/project_services/chat_message/merge_message_spec.rb
@@ -27,13 +27,30 @@ describe ChatMessage::MergeMessage do
}
end
+ # Integration point in EE
+ context 'when state is overridden' do
+ it 'respects the overridden state' do
+ allow(subject).to receive(:state_or_action_text) { 'devoured' }
+
+ aggregate_failures do
+ expect(subject.summary).not_to include('opened')
+ expect(subject.summary).to include('devoured')
+
+ activity_title = subject.activity[:title]
+
+ expect(activity_title).not_to include('opened')
+ expect(activity_title).to include('devoured')
+ end
+ end
+ end
+
context 'without markdown' do
let(:color) { '#345' }
context 'open' do
it 'returns a message regarding opening of merge requests' do
expect(subject.pretext).to eq(
- 'Test User (test.user) opened <http://somewhere.com/merge_requests/100|!100 *Merge Request title*> in <http://somewhere.com|project_name>: *Merge Request title*')
+ 'Test User (test.user) opened <http://somewhere.com/merge_requests/100|!100 *Merge Request title*> in <http://somewhere.com|project_name>')
expect(subject.attachments).to be_empty
end
end
@@ -44,7 +61,7 @@ describe ChatMessage::MergeMessage do
end
it 'returns a message regarding closing of merge requests' do
expect(subject.pretext).to eq(
- 'Test User (test.user) closed <http://somewhere.com/merge_requests/100|!100 *Merge Request title*> in <http://somewhere.com|project_name>: *Merge Request title*')
+ 'Test User (test.user) closed <http://somewhere.com/merge_requests/100|!100 *Merge Request title*> in <http://somewhere.com|project_name>')
expect(subject.attachments).to be_empty
end
end
@@ -58,7 +75,7 @@ describe ChatMessage::MergeMessage do
context 'open' do
it 'returns a message regarding opening of merge requests' do
expect(subject.pretext).to eq(
- 'Test User (test.user) opened [!100 *Merge Request title*](http://somewhere.com/merge_requests/100) in [project_name](http://somewhere.com): *Merge Request title*')
+ 'Test User (test.user) opened [!100 *Merge Request title*](http://somewhere.com/merge_requests/100) in [project_name](http://somewhere.com)')
expect(subject.attachments).to be_empty
expect(subject.activity).to eq({
title: 'Merge Request opened by Test User (test.user)',
@@ -76,7 +93,7 @@ describe ChatMessage::MergeMessage do
it 'returns a message regarding closing of merge requests' do
expect(subject.pretext).to eq(
- 'Test User (test.user) closed [!100 *Merge Request title*](http://somewhere.com/merge_requests/100) in [project_name](http://somewhere.com): *Merge Request title*')
+ 'Test User (test.user) closed [!100 *Merge Request title*](http://somewhere.com/merge_requests/100) in [project_name](http://somewhere.com)')
expect(subject.attachments).to be_empty
expect(subject.activity).to eq({
title: 'Merge Request closed by Test User (test.user)',
diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb
index 19c2862264f..973d6bdb2a0 100644
--- a/spec/models/project_services/chat_message/push_message_spec.rb
+++ b/spec/models/project_services/chat_message/push_message_spec.rb
@@ -48,12 +48,12 @@ describe ChatMessage::PushMessage do
'test.user pushed to branch [master](http://url.com/commits/master) of [project_name](http://url.com) ([Compare changes](http://url.com/compare/before...after))')
expect(subject.attachments).to eq(
"[abcdefgh](http://url1.com): message1 - author1\n\n[12345678](http://url2.com): message2 - author2")
- expect(subject.activity).to eq({
- title: 'test.user pushed to branch',
+ expect(subject.activity).to eq(
+ title: 'test.user pushed to branch [master](http://url.com/commits/master)',
subtitle: 'in [project_name](http://url.com)',
text: '[Compare changes](http://url.com/compare/before...after)',
image: 'http://someavatar.com'
- })
+ )
end
end
end
@@ -89,12 +89,53 @@ describe ChatMessage::PushMessage do
expect(subject.pretext).to eq(
'test.user pushed new tag [new_tag](http://url.com/commits/new_tag) to [project_name](http://url.com)')
expect(subject.attachments).to be_empty
- expect(subject.activity).to eq({
- title: 'test.user created tag',
+ expect(subject.activity).to eq(
+ title: 'test.user pushed new tag [new_tag](http://url.com/commits/new_tag)',
subtitle: 'in [project_name](http://url.com)',
text: '[Compare changes](http://url.com/compare/0000000000000000000000000000000000000000...after)',
image: 'http://someavatar.com'
- })
+ )
+ end
+ end
+ end
+
+ context 'removed tag' do
+ let(:args) do
+ {
+ after: Gitlab::Git::BLANK_SHA,
+ before: 'before',
+ project_name: 'project_name',
+ ref: 'refs/tags/new_tag',
+ user_name: 'test.user',
+ user_avatar: 'http://someavatar.com',
+ project_url: 'http://url.com'
+ }
+ end
+
+ context 'without markdown' do
+ it 'returns a message regarding removal of tags' do
+ expect(subject.pretext).to eq('test.user removed tag ' \
+ 'new_tag from ' \
+ '<http://url.com|project_name>')
+ expect(subject.attachments).to be_empty
+ end
+ end
+
+ context 'with markdown' do
+ before do
+ args[:markdown] = true
+ end
+
+ it 'returns a message regarding removal of tags' do
+ expect(subject.pretext).to eq(
+ 'test.user removed tag new_tag from [project_name](http://url.com)')
+ expect(subject.attachments).to be_empty
+ expect(subject.activity).to eq(
+ title: 'test.user removed tag new_tag',
+ subtitle: 'in [project_name](http://url.com)',
+ text: '[Compare changes](http://url.com/compare/before...0000000000000000000000000000000000000000)',
+ image: 'http://someavatar.com'
+ )
end
end
end
@@ -122,12 +163,12 @@ describe ChatMessage::PushMessage do
expect(subject.pretext).to eq(
'test.user pushed new branch [master](http://url.com/commits/master) to [project_name](http://url.com)')
expect(subject.attachments).to be_empty
- expect(subject.activity).to eq({
- title: 'test.user created branch',
+ expect(subject.activity).to eq(
+ title: 'test.user pushed new branch [master](http://url.com/commits/master)',
subtitle: 'in [project_name](http://url.com)',
text: '[Compare changes](http://url.com/compare/0000000000000000000000000000000000000000...after)',
image: 'http://someavatar.com'
- })
+ )
end
end
end
@@ -154,12 +195,12 @@ describe ChatMessage::PushMessage do
expect(subject.pretext).to eq(
'test.user removed branch master from [project_name](http://url.com)')
expect(subject.attachments).to be_empty
- expect(subject.activity).to eq({
- title: 'test.user removed branch',
+ expect(subject.activity).to eq(
+ title: 'test.user removed branch master',
subtitle: 'in [project_name](http://url.com)',
text: '[Compare changes](http://url.com/compare/before...0000000000000000000000000000000000000000)',
image: 'http://someavatar.com'
- })
+ )
end
end
end
diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/project_services/chat_notification_service_spec.rb
index 3aa1039d8bf..46713df77da 100644
--- a/spec/models/project_services/chat_notification_service_spec.rb
+++ b/spec/models/project_services/chat_notification_service_spec.rb
@@ -26,4 +26,54 @@ describe ChatNotificationService do
end
end
end
+
+ describe '#execute' do
+ let(:chat_service) { described_class.new }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:webhook_url) { 'https://example.gitlab.com/' }
+
+ before do
+ allow(chat_service).to receive_messages(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+
+ subject.active = true
+ end
+
+ context 'with a repository' do
+ it 'returns true' do
+ subject.project = project
+ data = Gitlab::DataBuilder::Push.build_sample(project, user)
+
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, {})
+ .and_return(
+ double(:slack_service).as_null_object
+ )
+
+ expect(chat_service.execute(data)).to be true
+ end
+ end
+
+ context 'with an empty repository' do
+ it 'returns true' do
+ subject.project = create(:project, :empty_repo)
+ data = Gitlab::DataBuilder::Push.build_sample(subject.project, user)
+
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, {})
+ .and_return(
+ double(:slack_service).as_null_object
+ )
+
+ expect(chat_service.execute(data)).to be true
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/discord_service_spec.rb b/spec/models/project_services/discord_service_spec.rb
new file mode 100644
index 00000000000..be82f223478
--- /dev/null
+++ b/spec/models/project_services/discord_service_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+describe DiscordService do
+ it_behaves_like "chat service", "Discord notifications" do
+ let(:client) { Discordrb::Webhooks::Client }
+ let(:client_arguments) { { url: webhook_url } }
+ let(:content_key) { :content }
+ end
+end
diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb
deleted file mode 100644
index 6e417323e40..00000000000
--- a/spec/models/project_services/gemnasium_service_spec.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-require 'spec_helper'
-
-describe GemnasiumService do
- describe "Associations" do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
- describe 'Validations' do
- context 'when service is active' do
- before do
- subject.active = true
- end
-
- it { is_expected.to validate_presence_of(:token) }
- it { is_expected.to validate_presence_of(:api_key) }
- end
-
- context 'when service is inactive' do
- before do
- subject.active = false
- end
-
- it { is_expected.not_to validate_presence_of(:token) }
- it { is_expected.not_to validate_presence_of(:api_key) }
- end
- end
-
- describe "deprecated?" do
- let(:project) { create(:project, :repository) }
- let(:gemnasium_service) { described_class.new }
-
- before do
- allow(gemnasium_service).to receive_messages(
- project_id: project.id,
- project: project,
- service_hook: true,
- token: 'verySecret',
- api_key: 'GemnasiumUserApiKey'
- )
- end
-
- it "is true" do
- expect(gemnasium_service.deprecated?).to be true
- end
-
- it "can't create a new service" do
- expect(gemnasium_service.save).to be false
- expect(gemnasium_service.errors[:base].first)
- .to eq('Gemnasium has been acquired by GitLab in January 2018. Since May 15, 2018, the service provided by Gemnasium is no longer available.')
- end
- end
-
- describe "Execute" do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:gemnasium_service) { described_class.new }
- let(:sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
-
- before do
- allow(gemnasium_service).to receive_messages(
- project_id: project.id,
- project: project,
- service_hook: true,
- token: 'verySecret',
- api_key: 'GemnasiumUserApiKey'
- )
- end
- it "calls Gemnasium service" do
- expect(Gemnasium::GitlabService).to receive(:execute).with(an_instance_of(Hash)).once
- gemnasium_service.execute(sample_data)
- end
- end
-end
diff --git a/spec/models/project_services/hangouts_chat_service_spec.rb b/spec/models/project_services/hangouts_chat_service_spec.rb
index cfa55188a64..0505ac9b49c 100644
--- a/spec/models/project_services/hangouts_chat_service_spec.rb
+++ b/spec/models/project_services/hangouts_chat_service_spec.rb
@@ -1,246 +1,11 @@
-require 'spec_helper'
+# frozen_string_literal: true
-describe HangoutsChatService do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
- describe 'Validations' do
- context 'when service is active' do
- before do
- subject.active = true
- end
-
- it { is_expected.to validate_presence_of(:webhook) }
- it_behaves_like 'issue tracker service URL attribute', :webhook
- end
-
- context 'when service is inactive' do
- before do
- subject.active = false
- end
-
- it { is_expected.not_to validate_presence_of(:webhook) }
- end
- end
-
- describe '#execute' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:webhook_url) { 'https://example.gitlab.com/' }
-
- before do
- allow(subject).to receive_messages(
- project: project,
- project_id: project.id,
- service_hook: true,
- webhook: webhook_url
- )
-
- WebMock.stub_request(:post, webhook_url)
- end
-
- shared_examples 'Hangouts Chat service' do
- it 'calls Hangouts Chat API' do
- subject.execute(sample_data)
-
- expect(WebMock)
- .to have_requested(:post, webhook_url)
- .with { |req| req.body =~ /\A{"text":.+}\Z/ }
- .once
- end
- end
-
- context 'with push events' do
- let(:sample_data) do
- Gitlab::DataBuilder::Push.build_sample(project, user)
- end
-
- it_behaves_like 'Hangouts Chat service'
-
- it 'specifies the webhook when it is configured' do
- expect(HangoutsChat::Sender).to receive(:new).with(webhook_url).and_return(double(:hangouts_chat_service).as_null_object)
-
- subject.execute(sample_data)
- end
-
- context 'with not default branch' do
- let(:sample_data) do
- Gitlab::DataBuilder::Push.build(project, user, nil, nil, 'not-the-default-branch')
- end
-
- context 'when notify_only_default_branch enabled' do
- before do
- subject.notify_only_default_branch = true
- end
-
- it 'does not call the Hangouts Chat API' do
- result = subject.execute(sample_data)
-
- expect(result).to be_falsy
- end
- end
-
- context 'when notify_only_default_branch disabled' do
- before do
- subject.notify_only_default_branch = false
- end
-
- it_behaves_like 'Hangouts Chat service'
- end
- end
- end
-
- context 'with issue events' do
- let(:opts) { { title: 'Awesome issue', description: 'please fix' } }
- let(:sample_data) do
- service = Issues::CreateService.new(project, user, opts)
- issue = service.execute
- service.hook_data(issue, 'open')
- end
-
- it_behaves_like 'Hangouts Chat service'
- end
-
- context 'with merge events' do
- let(:opts) do
- {
- title: 'Awesome merge_request',
- description: 'please fix',
- source_branch: 'feature',
- target_branch: 'master'
- }
- end
-
- let(:sample_data) do
- service = MergeRequests::CreateService.new(project, user, opts)
- merge_request = service.execute
- service.hook_data(merge_request, 'open')
- end
-
- before do
- project.add_developer(user)
- end
+require "spec_helper"
- it_behaves_like 'Hangouts Chat service'
- end
-
- context 'with wiki page events' do
- let(:opts) do
- {
- title: 'Awesome wiki_page',
- content: 'Some text describing some thing or another',
- format: 'md',
- message: 'user created page: Awesome wiki_page'
- }
- end
- let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) }
- let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
-
- it_behaves_like 'Hangouts Chat service'
- end
-
- context 'with note events' do
- let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) }
-
- context 'with commit comment' do
- let(:note) do
- create(:note_on_commit, author: user,
- project: project,
- commit_id: project.repository.commit.id,
- note: 'a comment on a commit')
- end
-
- it_behaves_like 'Hangouts Chat service'
- end
-
- context 'with merge request comment' do
- let(:note) do
- create(:note_on_merge_request, project: project,
- note: 'merge request note')
- end
-
- it_behaves_like 'Hangouts Chat service'
- end
-
- context 'with issue comment' do
- let(:note) do
- create(:note_on_issue, project: project, note: 'issue note')
- end
-
- it_behaves_like 'Hangouts Chat service'
- end
-
- context 'with snippet comment' do
- let(:note) do
- create(:note_on_project_snippet, project: project,
- note: 'snippet note')
- end
-
- it_behaves_like 'Hangouts Chat service'
- end
- end
-
- context 'with pipeline events' do
- let(:pipeline) do
- create(:ci_pipeline,
- project: project, status: status,
- sha: project.commit.sha, ref: project.default_branch)
- end
- let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
-
- context 'with failed pipeline' do
- let(:status) { 'failed' }
-
- it_behaves_like 'Hangouts Chat service'
- end
-
- context 'with succeeded pipeline' do
- let(:status) { 'success' }
-
- context 'with default notify_only_broken_pipelines' do
- it 'does not call Hangouts Chat API' do
- result = subject.execute(sample_data)
-
- expect(result).to be_falsy
- end
- end
-
- context 'when notify_only_broken_pipelines is false' do
- before do
- subject.notify_only_broken_pipelines = false
- end
-
- it_behaves_like 'Hangouts Chat service'
- end
- end
-
- context 'with not default branch' do
- let(:pipeline) do
- create(:ci_pipeline, project: project, status: 'failed', ref: 'not-the-default-branch')
- end
-
- context 'when notify_only_default_branch enabled' do
- before do
- subject.notify_only_default_branch = true
- end
-
- it 'does not call the Hangouts Chat API' do
- result = subject.execute(sample_data)
-
- expect(result).to be_falsy
- end
- end
-
- context 'when notify_only_default_branch disabled' do
- before do
- subject.notify_only_default_branch = false
- end
-
- it_behaves_like 'Hangouts Chat service'
- end
- end
- end
+describe HangoutsChatService do
+ it_behaves_like "chat service", "Hangouts Chat" do
+ let(:client) { HangoutsChat::Sender }
+ let(:client_arguments) { webhook_url }
+ let(:content_key) { :text }
end
end
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index 0cd712e2f40..b0fd2ceead0 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -387,4 +387,22 @@ describe HipchatService do
end
end
end
+
+ context 'with UrlBlocker' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:hipchat) { described_class.new(project: project) }
+ let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
+
+ describe '#execute' do
+ before do
+ hipchat.server = 'http://localhost:9123'
+ end
+
+ it 'raises UrlBlocker for localhost' do
+ expect(Gitlab::UrlBlocker).to receive(:validate!).and_call_original
+ expect { hipchat.execute(push_sample_data) }.to raise_error(Gitlab::HTTP::BlockedUrlError)
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index ac9ff59b9b5..788b3179b01 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -27,13 +27,13 @@ describe JiraService do
end
end
- describe "Associations" do
+ describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
it { is_expected.to allow_value(nil).for(:jira_issue_transition_id) }
- it { is_expected.to allow_value("1,2,3").for(:jira_issue_transition_id) }
- it { is_expected.to allow_value("1;2;3").for(:jira_issue_transition_id) }
- it { is_expected.not_to allow_value("a,b,cd").for(:jira_issue_transition_id) }
+ it { is_expected.to allow_value('1,2,3').for(:jira_issue_transition_id) }
+ it { is_expected.to allow_value('1;2;3').for(:jira_issue_transition_id) }
+ it { is_expected.not_to allow_value('a,b,cd').for(:jira_issue_transition_id) }
end
describe 'Validations' do
@@ -116,143 +116,142 @@ describe JiraService do
describe '#close_issue' do
let(:custom_base_url) { 'http://custom_url' }
let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:merge_request) { create(:merge_request) }
-
- before do
- @jira_service = described_class.new
- allow(@jira_service).to receive_messages(
- project_id: project.id,
- project: project,
- service_hook: true,
- url: 'http://jira.example.com',
- username: 'gitlab_jira_username',
- password: 'gitlab_jira_password',
- jira_issue_transition_id: "999"
- )
+ let(:project) { create(:project, :repository) }
- # These stubs are needed to test JiraService#close_issue.
- # We close the issue then do another request to API to check if it got closed.
- # Here is stubbed the API return with a closed and an opened issues.
- open_issue = JIRA::Resource::Issue.new(@jira_service.client, attrs: { "id" => "JIRA-123" })
- closed_issue = open_issue.dup
- allow(open_issue).to receive(:resolution).and_return(false)
- allow(closed_issue).to receive(:resolution).and_return(true)
- allow(JIRA::Resource::Issue).to receive(:find).and_return(open_issue, closed_issue)
-
- allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return("JIRA-123")
- allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
-
- @jira_service.save
-
- project_issues_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123'
- @transitions_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/transitions'
- @comment_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/comment'
- @remote_link_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/remotelink'
-
- WebMock.stub_request(:get, project_issues_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
- WebMock.stub_request(:post, @transitions_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
- WebMock.stub_request(:post, @comment_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
- WebMock.stub_request(:post, @remote_link_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
- end
+ shared_examples 'close_issue' do
+ before do
+ @jira_service = described_class.new
+ allow(@jira_service).to receive_messages(
+ project_id: project.id,
+ project: project,
+ service_hook: true,
+ url: 'http://jira.example.com',
+ username: 'gitlab_jira_username',
+ password: 'gitlab_jira_password',
+ jira_issue_transition_id: '999'
+ )
- it "calls JIRA API" do
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ # These stubs are needed to test JiraService#close_issue.
+ # We close the issue then do another request to API to check if it got closed.
+ # Here is stubbed the API return with a closed and an opened issues.
+ open_issue = JIRA::Resource::Issue.new(@jira_service.client, attrs: { 'id' => 'JIRA-123' })
+ closed_issue = open_issue.dup
+ allow(open_issue).to receive(:resolution).and_return(false)
+ allow(closed_issue).to receive(:resolution).and_return(true)
+ allow(JIRA::Resource::Issue).to receive(:find).and_return(open_issue, closed_issue)
- expect(WebMock).to have_requested(:post, @comment_url).with(
- body: /Issue solved with/
- ).once
- end
+ allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return('JIRA-123')
+ allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
- # Check https://developer.atlassian.com/jiradev/jira-platform/guides/other/guide-jira-remote-issue-links/fields-in-remote-issue-links
- # for more information
- it "creates Remote Link reference in JIRA for comment" do
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
-
- favicon_path = "http://localhost/assets/#{find_asset('favicon.png').digest_path}"
-
- # Creates comment
- expect(WebMock).to have_requested(:post, @comment_url)
- # Creates Remote Link in JIRA issue fields
- expect(WebMock).to have_requested(:post, @remote_link_url).with(
- body: hash_including(
- GlobalID: "GitLab",
- object: {
- url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}",
- title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.",
- icon: { title: "GitLab", url16x16: favicon_path },
- status: { resolved: true }
- }
- )
- ).once
- end
+ @jira_service.save
- it "does not send comment or remote links to issues already closed" do
- allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(true)
+ project_issues_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123'
+ @transitions_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/transitions'
+ @comment_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/comment'
+ @remote_link_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/remotelink'
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ WebMock.stub_request(:get, project_issues_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
+ WebMock.stub_request(:post, @transitions_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
+ WebMock.stub_request(:post, @comment_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
+ WebMock.stub_request(:post, @remote_link_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
+ end
- expect(WebMock).not_to have_requested(:post, @comment_url)
- expect(WebMock).not_to have_requested(:post, @remote_link_url)
- end
+ it 'calls JIRA API' do
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
- it "does not send comment or remote links to issues with unknown resolution" do
- allow_any_instance_of(JIRA::Resource::Issue).to receive(:respond_to?).with(:resolution).and_return(false)
+ expect(WebMock).to have_requested(:post, @comment_url).with(
+ body: /Issue solved with/
+ ).once
+ end
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ # Check https://developer.atlassian.com/jiradev/jira-platform/guides/other/guide-jira-remote-issue-links/fields-in-remote-issue-links
+ # for more information
+ it 'creates Remote Link reference in JIRA for comment' do
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
+
+ favicon_path = "http://localhost/assets/#{find_asset('favicon.png').digest_path}"
+
+ # Creates comment
+ expect(WebMock).to have_requested(:post, @comment_url)
+ # Creates Remote Link in JIRA issue fields
+ expect(WebMock).to have_requested(:post, @remote_link_url).with(
+ body: hash_including(
+ GlobalID: 'GitLab',
+ object: {
+ url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{commit_id}",
+ title: "GitLab: Solved by commit #{commit_id}.",
+ icon: { title: 'GitLab', url16x16: favicon_path },
+ status: { resolved: true }
+ }
+ )
+ ).once
+ end
- expect(WebMock).not_to have_requested(:post, @comment_url)
- expect(WebMock).not_to have_requested(:post, @remote_link_url)
- end
+ it 'does not send comment or remote links to issues already closed' do
+ allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(true)
- it "references the GitLab commit/merge request" do
- stub_config_setting(base_url: custom_base_url)
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ expect(WebMock).not_to have_requested(:post, @comment_url)
+ expect(WebMock).not_to have_requested(:post, @remote_link_url)
+ end
- expect(WebMock).to have_requested(:post, @comment_url).with(
- body: %r{#{custom_base_url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}}
- ).once
- end
+ it 'does not send comment or remote links to issues with unknown resolution' do
+ allow_any_instance_of(JIRA::Resource::Issue).to receive(:respond_to?).with(:resolution).and_return(false)
- it "references the GitLab commit/merge request (relative URL)" do
- stub_config_setting(relative_url_root: '/gitlab')
- stub_config_setting(url: Settings.send(:build_gitlab_url))
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
- allow(described_class).to receive(:default_url_options) do
- { script_name: '/gitlab' }
+ expect(WebMock).not_to have_requested(:post, @comment_url)
+ expect(WebMock).not_to have_requested(:post, @remote_link_url)
end
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ it 'references the GitLab commit' do
+ stub_config_setting(base_url: custom_base_url)
- expect(WebMock).to have_requested(:post, @comment_url).with(
- body: %r{#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}}
- ).once
- end
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
+
+ expect(WebMock).to have_requested(:post, @comment_url).with(
+ body: %r{#{custom_base_url}/#{project.full_path}/commit/#{commit_id}}
+ ).once
+ end
+
+ it 'references the GitLab commit' do
+ stub_config_setting(relative_url_root: '/gitlab')
+ stub_config_setting(url: Settings.send(:build_gitlab_url))
+
+ allow(described_class).to receive(:default_url_options) do
+ { script_name: '/gitlab' }
+ end
+
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
- context '#close_issue' do
- it "logs exception when transition id is not valid" do
- allow(Rails.logger).to receive(:info)
+ expect(WebMock).to have_requested(:post, @comment_url).with(
+ body: %r{#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{commit_id}}
+ ).once
+ end
+
+ it 'logs exception when transition id is not valid' do
+ allow(@jira_service).to receive(:log_error)
WebMock.stub_request(:post, @transitions_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password)).and_raise("Bad Request")
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
- expect(Rails.logger).to have_received(:info).with("JiraService Issue Transition failed message ERROR: http://jira.example.com - Bad Request")
+ expect(@jira_service).to have_received(:log_error).with("Issue transition failed", error: "Bad Request", client_url: "http://jira.example.com")
end
- it "calls the api with jira_issue_transition_id" do
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ it 'calls the api with jira_issue_transition_id' do
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
expect(WebMock).to have_requested(:post, @transitions_url).with(
body: /999/
).once
end
- context "when have multiple transition ids" do
- it "calls the api with transition ids separated by comma" do
- allow(@jira_service).to receive_messages(jira_issue_transition_id: "1,2,3")
+ context 'when have multiple transition ids' do
+ it 'calls the api with transition ids separated by comma' do
+ allow(@jira_service).to receive_messages(jira_issue_transition_id: '1,2,3')
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
1.upto(3) do |transition_id|
expect(WebMock).to have_requested(:post, @transitions_url).with(
@@ -261,10 +260,10 @@ describe JiraService do
end
end
- it "calls the api with transition ids separated by semicolon" do
- allow(@jira_service).to receive_messages(jira_issue_transition_id: "1;2;3")
+ it 'calls the api with transition ids separated by semicolon' do
+ allow(@jira_service).to receive_messages(jira_issue_transition_id: '1;2;3')
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
1.upto(3) do |transition_id|
expect(WebMock).to have_requested(:post, @transitions_url).with(
@@ -274,6 +273,20 @@ describe JiraService do
end
end
end
+
+ context 'when resource is a merge request' do
+ let(:resource) { create(:merge_request) }
+ let(:commit_id) { resource.diff_head_sha }
+
+ it_behaves_like 'close_issue'
+ end
+
+ context 'when resource is a commit' do
+ let(:resource) { project.commit('master') }
+ let(:commit_id) { resource.id }
+
+ it_behaves_like 'close_issue'
+ end
end
describe '#test_settings' do
@@ -321,17 +334,17 @@ describe JiraService do
end
end
- describe "Stored password invalidation" do
+ describe 'Stored password invalidation' do
let(:project) { create(:project) }
- context "when a password was previously set" do
+ context 'when a password was previously set' do
before do
@jira_service = described_class.create!(
project: project,
properties: {
url: 'http://jira.example.com/web',
username: 'mic',
- password: "password"
+ password: 'password'
}
)
end
@@ -370,10 +383,10 @@ describe JiraService do
@jira_service.url = 'http://jira_edited.example.com/rweb'
@jira_service.save
- expect(@jira_service.password).to eq("password")
+ expect(@jira_service.password).to eq('password')
end
- it 'reset password if api url set to ""' do
+ it 'reset password if api url set to empty' do
@jira_service.api_url = ''
@jira_service.save
@@ -440,7 +453,7 @@ describe JiraService do
it 'is initialized' do
expect(@service.title).to eq('JIRA')
- expect(@service.description).to eq("Jira issue tracker")
+ expect(@service.description).to eq('Jira issue tracker')
end
end
@@ -454,7 +467,7 @@ describe JiraService do
@service.destroy!
end
- it "is correct" do
+ it 'is correct' do
expect(@service.title).to eq('Jira One')
expect(@service.description).to eq('Jira One issue tracker')
end
@@ -476,7 +489,7 @@ describe JiraService do
it 'is initialized' do
expect(@service.options[:use_cookies]).to eq(true)
- expect(@service.options[:additional_cookies]).to eq(["OBBasicAuth=fromDialog"])
+ expect(@service.options[:additional_cookies]).to eq(['OBBasicAuth=fromDialog'])
end
end
end
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index 68ab9fd08ec..9c27357ffaf 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -253,7 +253,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
end
end
- describe '#predefined_variables' do
+ describe '#predefined_variable' do
let(:kubeconfig) do
config_file = expand_fixture_path('config/kubeconfig.yml')
config = YAML.load(File.read(config_file))
@@ -274,7 +274,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
shared_examples 'setting variables' do
it 'sets the variables' do
- expect(subject.predefined_variables).to include(
+ expect(subject.predefined_variables(project: project)).to include(
{ key: 'KUBE_URL', value: 'https://kube.domain.com', public: true },
{ key: 'KUBE_TOKEN', value: 'token', public: false },
{ key: 'KUBE_NAMESPACE', value: namespace, public: true },
@@ -301,7 +301,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
it_behaves_like 'setting variables'
it 'sets the KUBE_NAMESPACE' do
- kube_namespace = subject.predefined_variables.find { |h| h[:key] == 'KUBE_NAMESPACE' }
+ kube_namespace = subject.predefined_variables(project: project).find { |h| h[:key] == 'KUBE_NAMESPACE' }
expect(kube_namespace).not_to be_nil
expect(kube_namespace[:value]).to match(/\A#{Gitlab::PathRegex::PATH_REGEX_STR}-\d+\z/)
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index 7afb1b4a8e3..f2cb927df37 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe PrometheusService, :use_clean_rails_memory_store_caching do
@@ -83,13 +85,22 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
end
end
- describe '#prometheus_installed?' do
+ describe '#prometheus_available?' do
context 'clusters with installed prometheus' do
let!(:cluster) { create(:cluster, projects: [project]) }
let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) }
it 'returns true' do
- expect(service.prometheus_installed?).to be(true)
+ expect(service.prometheus_available?).to be(true)
+ end
+ end
+
+ context 'clusters with updated prometheus' do
+ let!(:cluster) { create(:cluster, projects: [project]) }
+ let!(:prometheus) { create(:clusters_applications_prometheus, :updated, cluster: cluster) }
+
+ it 'returns true' do
+ expect(service.prometheus_available?).to be(true)
end
end
@@ -98,7 +109,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) }
it 'returns false' do
- expect(service.prometheus_installed?).to be(false)
+ expect(service.prometheus_available?).to be(false)
end
end
@@ -106,13 +117,13 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
let(:cluster) { create(:cluster, projects: [project]) }
it 'returns false' do
- expect(service.prometheus_installed?).to be(false)
+ expect(service.prometheus_available?).to be(false)
end
end
context 'no clusters' do
it 'returns false' do
- expect(service.prometheus_installed?).to be(false)
+ expect(service.prometheus_available?).to be(false)
end
end
end
@@ -150,7 +161,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
context 'with prometheus installed in the cluster' do
before do
- allow(service).to receive(:prometheus_installed?).and_return(true)
+ allow(service).to receive(:prometheus_available?).and_return(true)
end
context 'when service is inactive' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 076de06cf99..51278836604 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2,11 +2,13 @@ require 'spec_helper'
describe Project do
include ProjectForksHelper
+ include GitHelpers
describe 'associations' do
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:namespace) }
it { is_expected.to belong_to(:creator).class_name('User') }
+ it { is_expected.to belong_to(:pool_repository) }
it { is_expected.to have_many(:users) }
it { is_expected.to have_many(:services) }
it { is_expected.to have_many(:events) }
@@ -22,7 +24,6 @@ describe Project do
it { is_expected.to have_many(:deploy_keys) }
it { is_expected.to have_many(:hooks) }
it { is_expected.to have_many(:protected_branches) }
- it { is_expected.to have_one(:forked_project_link) }
it { is_expected.to have_one(:slack_service) }
it { is_expected.to have_one(:microsoft_teams_service) }
it { is_expected.to have_one(:mattermost_service) }
@@ -32,6 +33,7 @@ describe Project do
it { is_expected.to have_one(:asana_service) }
it { is_expected.to have_many(:boards) }
it { is_expected.to have_one(:campfire_service) }
+ it { is_expected.to have_one(:discord_service) }
it { is_expected.to have_one(:drone_ci_service) }
it { is_expected.to have_one(:emails_on_push_service) }
it { is_expected.to have_one(:pipelines_email_service) }
@@ -42,7 +44,6 @@ describe Project do
it { is_expected.to have_one(:assembla_service) }
it { is_expected.to have_one(:slack_slash_commands_service) }
it { is_expected.to have_one(:mattermost_slash_commands_service) }
- it { is_expected.to have_one(:gemnasium_service) }
it { is_expected.to have_one(:buildkite_service) }
it { is_expected.to have_one(:bamboo_service) }
it { is_expected.to have_one(:teamcity_service) }
@@ -56,7 +57,7 @@ describe Project do
it { is_expected.to have_one(:statistics).class_name('ProjectStatistics') }
it { is_expected.to have_one(:import_data).class_name('ProjectImportData') }
it { is_expected.to have_one(:last_event).class_name('Event') }
- it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) }
+ it { is_expected.to have_one(:forked_from_project).through(:fork_network_member) }
it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') }
it { is_expected.to have_many(:commit_statuses) }
it { is_expected.to have_many(:pipelines) }
@@ -77,7 +78,8 @@ describe Project do
it { is_expected.to have_many(:lfs_objects_projects) }
it { is_expected.to have_many(:project_group_links) }
it { is_expected.to have_many(:notification_settings).dependent(:delete_all) }
- it { is_expected.to have_many(:forks).through(:forked_project_links) }
+ it { is_expected.to have_many(:forked_to_members).class_name('ForkNetworkMember') }
+ it { is_expected.to have_many(:forks).through(:forked_to_members) }
it { is_expected.to have_many(:uploads) }
it { is_expected.to have_many(:pipeline_schedules) }
it { is_expected.to have_many(:members_and_requesters) }
@@ -88,6 +90,10 @@ describe Project do
it { is_expected.to have_many(:project_deploy_tokens) }
it { is_expected.to have_many(:deploy_tokens).through(:project_deploy_tokens) }
+ it 'has an inverse relationship with merge requests' do
+ expect(described_class.reflect_on_association(:merge_requests).has_inverse?).to eq(:target_project)
+ end
+
context 'after initialized' do
it "has a project_feature" do
expect(described_class.new.project_feature).to be_present
@@ -229,54 +235,75 @@ describe Project do
end
it 'does not allow an invalid URI as import_url' do
- project2 = build(:project, import_url: 'invalid://')
+ project = build(:project, import_url: 'invalid://')
- expect(project2).not_to be_valid
+ expect(project).not_to be_valid
+ end
+
+ it 'does allow a SSH URI as import_url for persisted projects' do
+ project = create(:project)
+ project.import_url = 'ssh://test@gitlab.com/project.git'
+
+ expect(project).to be_valid
+ end
+
+ it 'does not allow a SSH URI as import_url for new projects' do
+ project = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
+
+ expect(project).not_to be_valid
end
it 'does allow a valid URI as import_url' do
- project2 = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
+ project = build(:project, import_url: 'http://gitlab.com/project.git')
- expect(project2).to be_valid
+ expect(project).to be_valid
end
it 'allows an empty URI' do
- project2 = build(:project, import_url: '')
+ project = build(:project, import_url: '')
- expect(project2).to be_valid
+ expect(project).to be_valid
end
it 'does not produce import data on an empty URI' do
- project2 = build(:project, import_url: '')
+ project = build(:project, import_url: '')
- expect(project2.import_data).to be_nil
+ expect(project.import_data).to be_nil
end
it 'does not produce import data on an invalid URI' do
- project2 = build(:project, import_url: 'test://')
+ project = build(:project, import_url: 'test://')
- expect(project2.import_data).to be_nil
+ expect(project.import_data).to be_nil
end
it "does not allow import_url pointing to localhost" do
- project2 = build(:project, import_url: 'http://localhost:9000/t.git')
+ project = build(:project, import_url: 'http://localhost:9000/t.git')
- expect(project2).to be_invalid
- expect(project2.errors[:import_url].first).to include('Requests to localhost are not allowed')
+ expect(project).to be_invalid
+ expect(project.errors[:import_url].first).to include('Requests to localhost are not allowed')
end
- it "does not allow import_url with invalid ports" do
- project2 = build(:project, import_url: 'http://github.com:25/t.git')
+ it "does not allow import_url with invalid ports for new projects" do
+ project = build(:project, import_url: 'http://github.com:25/t.git')
- expect(project2).to be_invalid
- expect(project2.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443')
+ expect(project).to be_invalid
+ expect(project.errors[:import_url].first).to include('Only allowed ports are 80, 443')
+ end
+
+ it "does not allow import_url with invalid ports for persisted projects" do
+ project = create(:project)
+ project.import_url = 'http://github.com:25/t.git'
+
+ expect(project).to be_invalid
+ expect(project.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443')
end
it "does not allow import_url with invalid user" do
- project2 = build(:project, import_url: 'http://$user:password@github.com/t.git')
+ project = build(:project, import_url: 'http://$user:password@github.com/t.git')
- expect(project2).to be_invalid
- expect(project2.errors[:import_url].first).to include('Username needs to start with an alphanumeric character')
+ expect(project).to be_invalid
+ expect(project.errors[:import_url].first).to include('Username needs to start with an alphanumeric character')
end
describe 'project pending deletion' do
@@ -506,28 +533,28 @@ describe Project do
end
describe "#readme_url" do
- let(:project) { create(:project, :repository, path: "somewhere") }
-
context 'with a non-existing repository' do
- it 'returns nil' do
- allow(project.repository).to receive(:tree).with(:head).and_return(nil)
+ let(:project) { create(:project) }
+ it 'returns nil' do
expect(project.readme_url).to be_nil
end
end
context 'with an existing repository' do
context 'when no README exists' do
- it 'returns nil' do
- allow_any_instance_of(Tree).to receive(:readme).and_return(nil)
+ let(:project) { create(:project, :empty_repo) }
+ it 'returns nil' do
expect(project.readme_url).to be_nil
end
end
context 'when a README exists' do
+ let(:project) { create(:project, :repository) }
+
it 'returns the README' do
- expect(project.readme_url).to eql("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere/blob/master/README.md")
+ expect(project.readme_url).to eq("#{project.web_url}/blob/master/README.md")
end
end
end
@@ -1073,6 +1100,18 @@ describe Project do
it { expect(project.builds_enabled?).to be_truthy }
end
+ describe '.sort_by_attribute' do
+ it 'reorders the input relation by start count desc' do
+ project1 = create(:project, star_count: 2)
+ project2 = create(:project, star_count: 1)
+ project3 = create(:project)
+
+ projects = described_class.sort_by_attribute(:stars_desc)
+
+ expect(projects).to eq([project1, project2, project3])
+ end
+ end
+
describe '.with_shared_runners' do
subject { described_class.with_shared_runners }
@@ -1347,7 +1386,7 @@ describe Project do
context 'when checking on forked project' do
let(:project) { create(:project, :internal) }
- let(:forked_project) { create(:project, forked_from_project: project) }
+ let(:forked_project) { fork_project(project) }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
@@ -1478,6 +1517,53 @@ describe Project do
end
end
+ describe '.optionally_search' do
+ let(:project) { create(:project) }
+
+ it 'searches for projects matching the query if one is given' do
+ relation = described_class.optionally_search(project.name)
+
+ expect(relation).to eq([project])
+ end
+
+ it 'returns the current relation if no search query is given' do
+ relation = described_class.where(id: project.id)
+
+ expect(relation.optionally_search).to eq(relation)
+ end
+ end
+
+ describe '.paginate_in_descending_order_using_id' do
+ let!(:project1) { create(:project) }
+ let!(:project2) { create(:project) }
+
+ it 'orders the relation in descending order' do
+ expect(described_class.paginate_in_descending_order_using_id)
+ .to eq([project2, project1])
+ end
+
+ it 'applies a limit to the relation' do
+ expect(described_class.paginate_in_descending_order_using_id(limit: 1))
+ .to eq([project2])
+ end
+
+ it 'limits projects by and ID when given' do
+ expect(described_class.paginate_in_descending_order_using_id(before: project2.id))
+ .to eq([project1])
+ end
+ end
+
+ describe '.including_namespace_and_owner' do
+ it 'eager loads the namespace and namespace owner' do
+ create(:project)
+
+ row = described_class.eager_load_namespace_and_owner.to_a.first
+ recorder = ActiveRecord::QueryRecorder.new { row.namespace.owner }
+
+ expect(recorder.count).to be_zero
+ end
+ end
+
describe '#expire_caches_before_rename' do
let(:project) { create(:project, :repository) }
let(:repo) { double(:repo, exists?: true) }
@@ -1912,9 +1998,12 @@ describe Project do
let(:import_jid) { '123' }
context 'forked' do
- let(:forked_project_link) { create(:forked_project_link, :forked_to_empty_project) }
- let(:forked_from_project) { forked_project_link.forked_from_project }
- let(:project) { forked_project_link.forked_to_project }
+ let(:forked_from_project) { create(:project, :repository) }
+ let(:project) { create(:project) }
+
+ before do
+ fork_project(forked_from_project, nil, target_project: project)
+ end
it 'schedules a RepositoryForkWorker job' do
expect(RepositoryForkWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
@@ -2201,6 +2290,12 @@ describe Project do
end
end
+ describe '#forks' do
+ it 'includes direct forks of the project' do
+ expect(project.forks).to contain_exactly(forked_project)
+ end
+ end
+
describe '#lfs_storage_project' do
it 'returns self for non-forks' do
expect(project.lfs_storage_project).to eq project
@@ -2312,12 +2407,24 @@ describe Project do
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
- context 'when user configured kubernetes from CI/CD > Clusters' do
+ context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has not been executed' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
+
+ context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has been executed' do
+ let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token) }
+ let!(:cluster) { kubernetes_namespace.cluster }
+ let(:project) { kubernetes_namespace.project }
+
+ it 'should return token from kubernetes namespace' do
+ expect(project.deployment_variables).to include(
+ { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false }
+ )
+ end
+ end
end
end
@@ -2343,10 +2450,10 @@ describe Project do
end
end
- describe '#secret_variables_for' do
+ describe '#ci_variables_for' do
let(:project) { create(:project) }
- let!(:secret_variable) do
+ let!(:ci_variable) do
create(:ci_variable, value: 'secret', project: project)
end
@@ -2354,7 +2461,7 @@ describe Project do
create(:ci_variable, :protected, value: 'protected', project: project)
end
- subject { project.reload.secret_variables_for(ref: 'ref') }
+ subject { project.reload.ci_variables_for(ref: 'ref') }
before do
stub_application_setting(
@@ -2363,13 +2470,13 @@ describe Project do
shared_examples 'ref is protected' do
it 'contains all the variables' do
- is_expected.to contain_exactly(secret_variable, protected_variable)
+ is_expected.to contain_exactly(ci_variable, protected_variable)
end
end
context 'when the ref is not protected' do
- it 'contains only the secret variables' do
- is_expected.to contain_exactly(secret_variable)
+ it 'contains only the CI variables' do
+ is_expected.to contain_exactly(ci_variable)
end
end
@@ -2657,7 +2764,7 @@ describe Project do
.to raise_error(ActiveRecord::RecordNotSaved, error_message)
end
- it 'updates the project succesfully' do
+ it 'updates the project successfully' do
merge_request = create(:merge_request, target_project: project, source_project: project)
expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }
@@ -2808,73 +2915,12 @@ describe Project do
end
describe '#remove_export' do
- let(:legacy_project) { create(:project, :legacy_storage, :with_export) }
let(:project) { create(:project, :with_export) }
- before do
- stub_feature_flags(import_export_object_storage: false)
- end
-
- it 'removes the exports directory for the project' do
- expect(File.exist?(project.export_path)).to be_truthy
-
- allow(FileUtils).to receive(:rm_rf).and_call_original
- expect(FileUtils).to receive(:rm_rf).with(project.export_path).and_call_original
+ it 'removes the export' do
project.remove_exports
- expect(File.exist?(project.export_path)).to be_falsy
- end
-
- it 'is a no-op on legacy projects when there is no namespace' do
- export_path = legacy_project.export_path
-
- legacy_project.namespace.delete
- legacy_project.reload
-
- expect(FileUtils).not_to receive(:rm_rf).with(export_path)
-
- legacy_project.remove_exports
-
- expect(File.exist?(export_path)).to be_truthy
- end
-
- it 'runs on hashed storage projects when there is no namespace' do
- export_path = project.export_path
-
- project.namespace.delete
- legacy_project.reload
-
- allow(FileUtils).to receive(:rm_rf).and_call_original
- expect(FileUtils).to receive(:rm_rf).with(export_path).and_call_original
-
- project.remove_exports
-
- expect(File.exist?(export_path)).to be_falsy
- end
-
- it 'is run when the project is destroyed' do
- expect(project).to receive(:remove_exports).and_call_original
-
- project.destroy
- end
- end
-
- describe '#remove_exported_project_file' do
- let(:project) { create(:project, :with_export) }
-
- it 'removes the exported project file' do
- stub_feature_flags(import_export_object_storage: false)
-
- exported_file = project.export_project_path
-
- expect(File.exist?(exported_file)).to be_truthy
-
- allow(FileUtils).to receive(:rm_rf).and_call_original
- expect(FileUtils).to receive(:rm_rf).with(exported_file).and_call_original
-
- project.remove_exported_project_file
-
- expect(File.exist?(exported_file)).to be_falsy
+ expect(project.export_file_exists?).to be_falsey
end
end
@@ -2937,87 +2983,6 @@ describe Project do
end
end
- describe '#rename_repo' do
- before do
- # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
- # call. This makes testing a bit easier.
- allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
- allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
- end
-
- it 'renames a repository' do
- stub_container_registry_config(enabled: false)
-
- expect(gitlab_shell).to receive(:mv_repository)
- .ordered
- .with(project.repository_storage, "#{project.namespace.full_path}/foo", "#{project.full_path}")
- .and_return(true)
-
- expect(gitlab_shell).to receive(:mv_repository)
- .ordered
- .with(project.repository_storage, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki")
- .and_return(true)
-
- expect_any_instance_of(SystemHooksService)
- .to receive(:execute_hooks_for)
- .with(project, :rename)
-
- expect_any_instance_of(Gitlab::UploadsTransfer)
- .to receive(:rename_project)
- .with('foo', project.path, project.namespace.full_path)
-
- expect(project).to receive(:expire_caches_before_rename)
-
- project.rename_repo
- end
-
- context 'container registry with images' do
- let(:container_repository) { create(:container_repository) }
-
- before do
- stub_container_registry_config(enabled: true)
- stub_container_registry_tags(repository: :any, tags: ['tag'])
- project.container_repositories << container_repository
- end
-
- subject { project.rename_repo }
-
- it { expect { subject }.to raise_error(StandardError) }
- end
-
- context 'gitlab pages' do
- before do
- expect(project_storage).to receive(:rename_repo) { true }
- end
-
- it 'moves pages folder to new location' do
- expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
-
- project.rename_repo
- end
- end
-
- context 'attachments' do
- before do
- expect(project_storage).to receive(:rename_repo) { true }
- end
-
- it 'moves uploads folder to new location' do
- expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)
-
- project.rename_repo
- end
- end
-
- it 'updates project full path in .git/config' do
- allow(project_storage).to receive(:rename_repo).and_return(true)
-
- project.rename_repo
-
- expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
- end
- end
-
describe '#pages_path' do
it 'returns a path where pages are stored' do
expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path))
@@ -3108,90 +3073,6 @@ describe Project do
end
end
- describe '#rename_repo' do
- before do
- # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
- # call. This makes testing a bit easier.
- allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
- allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
- end
-
- context 'migration to hashed storage' do
- it 'calls HashedStorageMigrationService with correct options' do
- project = create(:project, :repository, :legacy_storage)
- allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
-
- expect_next_instance_of(::Projects::HashedStorageMigrationService) do |service|
- expect(service).to receive(:execute).and_return(true)
- end
-
- project.rename_repo
- end
- end
-
- it 'renames a repository' do
- stub_container_registry_config(enabled: false)
-
- expect(gitlab_shell).not_to receive(:mv_repository)
-
- expect_any_instance_of(SystemHooksService)
- .to receive(:execute_hooks_for)
- .with(project, :rename)
-
- expect(project).to receive(:expire_caches_before_rename)
-
- project.rename_repo
- end
-
- context 'container registry with images' do
- let(:container_repository) { create(:container_repository) }
-
- before do
- stub_container_registry_config(enabled: true)
- stub_container_registry_tags(repository: :any, tags: ['tag'])
- project.container_repositories << container_repository
- end
-
- subject { project.rename_repo }
-
- it { expect { subject }.to raise_error(StandardError) }
- end
-
- context 'gitlab pages' do
- it 'moves pages folder to new location' do
- expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
-
- project.rename_repo
- end
- end
-
- context 'attachments' do
- it 'keeps uploads folder location unchanged' do
- expect_any_instance_of(Gitlab::UploadsTransfer).not_to receive(:rename_project)
-
- project.rename_repo
- end
-
- context 'when not rolled out' do
- let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) }
-
- it 'moves pages folder to hashed storage' do
- expect_next_instance_of(Projects::HashedStorage::MigrateAttachmentsService) do |service|
- expect(service).to receive(:execute)
- end
-
- project.rename_repo
- end
- end
- end
-
- it 'updates project full path in .git/config' do
- project.rename_repo
-
- expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
- end
- end
-
describe '#pages_path' do
it 'returns a path where pages are stored' do
expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path))
@@ -3206,6 +3087,14 @@ describe Project do
it 'does not flag as read-only' do
expect { project.migrate_to_hashed_storage! }.not_to change { project.repository_read_only }
end
+
+ context 'when partially migrated' do
+ it 'returns true' do
+ project = create(:project, storage_version: 1, skip_disk_validation: true)
+
+ expect(project.migrate_to_hashed_storage!).to be_truthy
+ end
+ end
end
end
@@ -3242,23 +3131,28 @@ describe Project do
expect(repository).to receive(:gitlab_ci_yml) { nil }
end
- it "CI is not available" do
- expect(project).not_to have_ci
+ it "CI is available" do
+ expect(project).to have_ci
end
- context 'when auto devops is enabled' do
+ context 'when auto devops is disabled' do
before do
- stub_application_setting(auto_devops_enabled: true)
+ stub_application_setting(auto_devops_enabled: false)
end
- it "CI is available" do
- expect(project).to have_ci
+ it "CI is not available" do
+ expect(project).not_to have_ci
end
end
end
end
describe '#auto_devops_enabled?' do
+ before do
+ allow(Feature).to receive(:enabled?).and_call_original
+ Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
+ end
+
set(:project) { create(:project) }
subject { project.auto_devops_enabled? }
@@ -3268,19 +3162,14 @@ describe Project do
stub_application_setting(auto_devops_enabled: true)
end
- it 'auto devops is implicitly enabled' do
- expect(project.auto_devops).to be_nil
- expect(project).to be_auto_devops_enabled
- end
+ it { is_expected.to be_truthy }
context 'when explicitly enabled' do
before do
create(:project_auto_devops, project: project)
end
- it "auto devops is enabled" do
- expect(project).to be_auto_devops_enabled
- end
+ it { is_expected.to be_truthy }
end
context 'when explicitly disabled' do
@@ -3288,9 +3177,7 @@ describe Project do
create(:project_auto_devops, project: project, enabled: false)
end
- it "auto devops is disabled" do
- expect(project).not_to be_auto_devops_enabled
- end
+ it { is_expected.to be_falsey }
end
end
@@ -3299,19 +3186,22 @@ describe Project do
stub_application_setting(auto_devops_enabled: false)
end
- it 'auto devops is implicitly disabled' do
- expect(project.auto_devops).to be_nil
- expect(project).not_to be_auto_devops_enabled
- end
+ it { is_expected.to be_falsey }
context 'when explicitly enabled' do
before do
create(:project_auto_devops, project: project)
end
- it "auto devops is enabled" do
- expect(project).to be_auto_devops_enabled
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when force_autodevops_on_by_default is enabled for the project' do
+ before do
+ Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(100)
end
+
+ it { is_expected.to be_truthy }
end
end
end
@@ -3361,6 +3251,11 @@ describe Project do
end
describe '#has_auto_devops_implicitly_disabled?' do
+ before do
+ allow(Feature).to receive(:enabled?).and_call_original
+ Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
+ end
+
set(:project) { create(:project) }
context 'when enabled in settings' do
@@ -3382,6 +3277,16 @@ describe Project do
expect(project).to have_auto_devops_implicitly_disabled
end
+ context 'when force_autodevops_on_by_default is enabled for the project' do
+ before do
+ Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(100)
+ end
+
+ it 'does not have auto devops implicitly disabled' do
+ expect(project).not_to have_auto_devops_implicitly_disabled
+ end
+ end
+
context 'when explicitly disabled' do
before do
create(:project_auto_devops, project: project, enabled: false)
@@ -3435,7 +3340,7 @@ describe Project do
end
end
- context 'when explicitely enabled' do
+ context 'when explicitly enabled' do
context 'when domain is empty' do
before do
create(:project_auto_devops, project: project, domain: nil)
@@ -3670,21 +3575,45 @@ describe Project do
end
describe '#execute_hooks' do
- it 'executes the projects hooks with the specified scope' do
- hook1 = create(:project_hook, merge_requests_events: true, tag_push_events: false)
- hook2 = create(:project_hook, merge_requests_events: false, tag_push_events: true)
- project = create(:project, hooks: [hook1, hook2])
+ let(:data) { { ref: 'refs/heads/master', data: 'data' } }
+ it 'executes active projects hooks with the specified scope' do
+ hook = create(:project_hook, merge_requests_events: false, push_events: true)
+ expect(ProjectHook).to receive(:select_active)
+ .with(:push_hooks, data)
+ .and_return([hook])
+ project = create(:project, hooks: [hook])
expect_any_instance_of(ProjectHook).to receive(:async_execute).once
- project.execute_hooks({}, :tag_push_hooks)
+ project.execute_hooks(data, :push_hooks)
+ end
+
+ it 'does not execute project hooks that dont match the specified scope' do
+ hook = create(:project_hook, merge_requests_events: true, push_events: false)
+ project = create(:project, hooks: [hook])
+
+ expect_any_instance_of(ProjectHook).not_to receive(:async_execute).once
+
+ project.execute_hooks(data, :push_hooks)
+ end
+
+ it 'does not execute project hooks which are not active' do
+ hook = create(:project_hook, push_events: true)
+ expect(ProjectHook).to receive(:select_active)
+ .with(:push_hooks, data)
+ .and_return([])
+ project = create(:project, hooks: [hook])
+
+ expect_any_instance_of(ProjectHook).not_to receive(:async_execute).once
+
+ project.execute_hooks(data, :push_hooks)
end
it 'executes the system hooks with the specified scope' do
- expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with({ data: 'data' }, :merge_request_hooks)
+ expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(data, :merge_request_hooks)
project = build(:project)
- project.execute_hooks({ data: 'data' }, :merge_request_hooks)
+ project.execute_hooks(data, :merge_request_hooks)
end
it 'executes the system hooks when inside a transaction' do
@@ -3699,7 +3628,7 @@ describe Project do
# actually get to the `after_commit` hook that queues these jobs.
expect do
project.transaction do
- project.execute_hooks({ data: 'data' }, :merge_request_hooks)
+ project.execute_hooks(data, :merge_request_hooks)
end
end.not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
end
@@ -3956,9 +3885,163 @@ describe Project do
end
end
- def rugged_config
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged.config
+ context '#members_among' do
+ let(:users) { create_list(:user, 3) }
+ set(:group) { create(:group) }
+ set(:project) { create(:project, namespace: group) }
+
+ before do
+ project.add_guest(users.first)
+ project.group.add_maintainer(users.last)
+ end
+
+ context 'when users is an Array' do
+ it 'returns project members among the users' do
+ expect(project.members_among(users)).to eq([users.first, users.last])
+ end
+
+ it 'maintains input order' do
+ expect(project.members_among(users.reverse)).to eq([users.last, users.first])
+ end
+
+ it 'returns empty array if users is empty' do
+ result = project.members_among([])
+
+ expect(result).to be_empty
+ end
end
+
+ context 'when users is a relation' do
+ it 'returns project members among the users' do
+ result = project.members_among(User.where(id: users.map(&:id)))
+
+ expect(result).to be_a(ActiveRecord::Relation)
+ expect(result).to eq([users.first, users.last])
+ end
+
+ it 'returns empty relation if users is empty' do
+ result = project.members_among(User.none)
+
+ expect(result).to be_a(ActiveRecord::Relation)
+ expect(result).to be_empty
+ end
+ end
+ end
+
+ describe "#find_or_initialize_services" do
+ subject { build(:project) }
+
+ it 'returns only enabled services' do
+ allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
+ allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
+
+ services = subject.find_or_initialize_services
+
+ expect(services.count).to eq 1
+ expect(services).to include(PushoverService)
+ end
+ end
+
+ describe "#find_or_initialize_service" do
+ subject { build(:project) }
+
+ it 'avoids N+1 database queries' do
+ allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
+
+ control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_service('prometheus') }.count
+
+ allow(Service).to receive(:available_services_names).and_call_original
+
+ expect { subject.find_or_initialize_service('prometheus') }.not_to exceed_query_limit(control_count)
+ end
+
+ it 'returns nil if service is disabled' do
+ allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
+
+ expect(subject.find_or_initialize_service('prometheus')).to be_nil
+ end
+ end
+
+ describe '.find_without_deleted' do
+ it 'returns nil if the project is about to be removed' do
+ project = create(:project, pending_delete: true)
+
+ expect(described_class.find_without_deleted(project.id)).to be_nil
+ end
+
+ it 'returns a project when it is not about to be removed' do
+ project = create(:project)
+
+ expect(described_class.find_without_deleted(project.id)).to eq(project)
+ end
+ end
+
+ describe '.for_group' do
+ it 'returns the projects for a given group' do
+ group = create(:group)
+ project = create(:project, namespace: group)
+
+ expect(described_class.for_group(group)).to eq([project])
+ end
+ end
+
+ describe '.deployments' do
+ subject { project.deployments }
+
+ let(:project) { create(:project) }
+
+ before do
+ allow_any_instance_of(Deployment).to receive(:create_ref)
+ end
+
+ context 'when there is a deployment record with created status' do
+ let(:deployment) { create(:deployment, :created, project: project) }
+
+ it 'does not return the record' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when there is a deployment record with running status' do
+ let(:deployment) { create(:deployment, :running, project: project) }
+
+ it 'does not return the record' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when there is a deployment record with success status' do
+ let(:deployment) { create(:deployment, :success, project: project) }
+
+ it 'returns the record' do
+ is_expected.to eq([deployment])
+ end
+ end
+ end
+
+ describe '#snippets_visible?' do
+ it 'returns true when a logged in user can read snippets' do
+ project = create(:project, :public)
+ user = create(:user)
+
+ expect(project.snippets_visible?(user)).to eq(true)
+ end
+
+ it 'returns true when an anonymous user can read snippets' do
+ project = create(:project, :public)
+
+ expect(project.snippets_visible?).to eq(true)
+ end
+
+ it 'returns false when a user can not read snippets' do
+ project = create(:project, :private)
+ user = create(:user)
+
+ expect(project.snippets_visible?(user)).to eq(false)
+ end
+ end
+
+ def rugged_config
+ rugged_repo(project.repository).config
end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 528f5b610d7..cc5e34782ec 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -2,12 +2,13 @@
require "spec_helper"
describe ProjectWiki do
- let(:project) { create(:project, :wiki_repo) }
+ let(:user) { create(:user, :commit_email) }
+ let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:repository) { project.repository }
- let(:user) { project.owner }
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project_wiki) { described_class.new(project, user) }
let(:raw_repository) { Gitlab::Git::Repository.new(project.repository_storage, subject.disk_path + '.git', 'foo') }
+ let(:commit) { project_wiki.repository.head_commit }
subject { project_wiki }
@@ -144,7 +145,7 @@ describe ProjectWiki do
end
it "returns nil if the page does not exist" do
- expect(subject.find_page("non-existant")).to eq(nil)
+ expect(subject.find_page("non-existent")).to eq(nil)
end
it "can find a page by slug" do
@@ -225,7 +226,7 @@ describe ProjectWiki do
end
it 'returns nil if the page does not exist' do
- expect(subject.find_file('non-existant')).to eq(nil)
+ expect(subject.find_file('non-existent')).to eq(nil)
end
it 'returns a Gitlab::Git::WikiFile instance' do
@@ -276,6 +277,14 @@ describe ProjectWiki do
expect(subject.pages.first.page.version.message).to eq("commit message")
end
+ it 'sets the correct commit email' do
+ subject.create_page('test page', 'content')
+
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ end
+
it 'updates project activity' do
subject.create_page('Test Page', 'This is content')
@@ -320,6 +329,12 @@ describe ProjectWiki do
expect(@page.version.message).to eq("updated page")
end
+ it 'sets the correct commit email' do
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ end
+
it 'updates project activity' do
subject.update_page(
@gitlab_git_wiki_page,
@@ -347,6 +362,14 @@ describe ProjectWiki do
expect(subject.pages.count).to eq(0)
end
+ it 'sets the correct commit email' do
+ subject.delete_page(@page)
+
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ end
+
it 'updates project activity' do
subject.delete_page(@page)
@@ -420,7 +443,7 @@ describe ProjectWiki do
end
def commit_details
- Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "test commit")
+ Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.commit_email, "test commit")
end
def create_page(name, content)
diff --git a/spec/models/prometheus_metric_spec.rb b/spec/models/prometheus_metric_spec.rb
new file mode 100644
index 00000000000..a83a31ae88c
--- /dev/null
+++ b/spec/models/prometheus_metric_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PrometheusMetric do
+ subject { build(:prometheus_metric) }
+ let(:other_project) { build(:project) }
+
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to validate_presence_of(:query) }
+ it { is_expected.to validate_presence_of(:group) }
+
+ describe 'common metrics' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:common, :project, :result) do
+ false | other_project | true
+ false | nil | false
+ true | other_project | false
+ true | nil | true
+ end
+
+ with_them do
+ before do
+ subject.common = common
+ subject.project = project
+ end
+
+ it { expect(subject.valid?).to eq(result) }
+ end
+ end
+
+ describe '#query_series' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:legend, :type) do
+ 'Some other legend' | NilClass
+ 'Status Code' | Array
+ end
+
+ with_them do
+ before do
+ subject.legend = legend
+ end
+
+ it { expect(subject.query_series).to be_a(type) }
+ end
+ end
+
+ describe '#group_title' do
+ shared_examples 'group_title' do |group, title|
+ subject { build(:prometheus_metric, group: group).group_title }
+
+ it "returns text #{title} for group #{group}" do
+ expect(subject).to eq(title)
+ end
+ end
+
+ it_behaves_like 'group_title', :business, 'Business metrics (Custom)'
+ it_behaves_like 'group_title', :response, 'Response metrics (Custom)'
+ it_behaves_like 'group_title', :system, 'System metrics (Custom)'
+ end
+
+ describe '#to_query_metric' do
+ it 'converts to queryable metric object' do
+ expect(subject.to_query_metric).to be_instance_of(Gitlab::Prometheus::Metric)
+ end
+
+ it 'queryable metric object has title' do
+ expect(subject.to_query_metric.title).to eq(subject.title)
+ end
+
+ it 'queryable metric object has y_label' do
+ expect(subject.to_query_metric.y_label).to eq(subject.y_label)
+ end
+
+ it 'queryable metric has no required_metric' do
+ expect(subject.to_query_metric.required_metrics).to eq([])
+ end
+
+ it 'queryable metric has weight 0' do
+ expect(subject.to_query_metric.weight).to eq(0)
+ end
+
+ it 'queryable metrics has query description' do
+ queries = [
+ {
+ query_range: subject.query,
+ unit: subject.unit,
+ label: subject.legend
+ }
+ ]
+
+ expect(subject.to_query_metric.queries).to eq(queries)
+ end
+ end
+end
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index c2ef0435c8e..3d316fb3c5b 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -1,6 +1,8 @@
require 'rails_helper'
describe RemoteMirror do
+ include GitHelpers
+
describe 'URL validation' do
context 'with a valid URL' do
it 'should be valid' do
@@ -74,9 +76,7 @@ describe RemoteMirror do
mirror.update_attribute(:url, 'http://foo:baz@test.com')
- config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repo.raw_repository.rugged.config
- end
+ config = rugged_repo(repo).config
expect(config["remote.#{mirror.remote_name}.url"]).to eq('http://foo:baz@test.com')
end
@@ -220,6 +220,18 @@ describe RemoteMirror do
end
end
+ context '#ensure_remote!' do
+ let(:remote_mirror) { create(:project, :repository, :remote_mirror).remote_mirrors.first }
+
+ it 'adds a remote multiple times with no errors' do
+ expect(remote_mirror.project.repository).to receive(:add_remote).with(remote_mirror.remote_name, remote_mirror.url).twice.and_call_original
+
+ 2.times do
+ remote_mirror.ensure_remote!
+ end
+ end
+ end
+
context '#updated_since?' do
let(:remote_mirror) { create(:project, :repository, :remote_mirror).remote_mirrors.first }
let(:timestamp) { Time.now - 5.minutes }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 52ec8dbe25a..56edb0fd6da 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
describe Repository do
include RepoHelpers
+ include GitHelpers
+
TestBlob = Struct.new(:path)
let(:project) { create(:project, :repository) }
@@ -28,7 +30,7 @@ describe Repository do
def expect_to_raise_storage_error
expect { yield }.to raise_error do |exception|
- storage_exceptions = [Gitlab::Git::Storage::Inaccessible, Gitlab::Git::CommandError, GRPC::Unavailable]
+ storage_exceptions = [Gitlab::Git::CommandError, GRPC::Unavailable]
known_exception = storage_exceptions.select { |e| exception.is_a?(e) }
expect(known_exception).not_to be_nil
@@ -137,9 +139,7 @@ describe Repository do
options = { message: 'test tag message\n',
tagger: { name: 'John Smith', email: 'john@gmail.com' } }
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged.tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options)
- end
+ rugged_repo(repository).tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options)
double_first = double(committed_date: Time.now - 1.second)
double_last = double(committed_date: Time.now)
@@ -151,9 +151,7 @@ describe Repository do
it { is_expected.to eq(['v1.1.0', 'v1.0.0', annotated_tag_name]) }
after do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged.tags.delete(annotated_tag_name)
- end
+ rugged_repo(repository).tags.delete(annotated_tag_name)
end
end
end
@@ -188,6 +186,57 @@ describe Repository do
end
end
+ describe '#list_last_commits_for_tree' do
+ let(:path_to_commit) do
+ {
+ "encoding" => "913c66a37b4a45b9769037c55c2d238bd0942d2e",
+ "files" => "570e7b2abdd848b95f2f578043fc23bd6f6fd24d",
+ ".gitignore" => "c1acaa58bbcbc3eafe538cb8274ba387047b69f8",
+ ".gitmodules" => "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9",
+ "CHANGELOG" => "913c66a37b4a45b9769037c55c2d238bd0942d2e",
+ "CONTRIBUTING.md" => "6d394385cf567f80a8fd85055db1ab4c5295806f",
+ "Gemfile.zip" => "ae73cb07c9eeaf35924a10f713b364d32b2dd34f",
+ "LICENSE" => "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863",
+ "MAINTENANCE.md" => "913c66a37b4a45b9769037c55c2d238bd0942d2e",
+ "PROCESS.md" => "913c66a37b4a45b9769037c55c2d238bd0942d2e",
+ "README.md" => "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863",
+ "VERSION" => "913c66a37b4a45b9769037c55c2d238bd0942d2e",
+ "gitlab-shell" => "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9",
+ "six" => "cfe32cf61b73a0d5e9f13e774abde7ff789b1660"
+ }
+ end
+
+ subject { repository.list_last_commits_for_tree(sample_commit.id, '.').id }
+
+ it 'returns the last commits for every entry in the current path' do
+ result = repository.list_last_commits_for_tree(sample_commit.id, '.')
+
+ result.each do |key, value|
+ result[key] = value.id
+ end
+
+ expect(result).to include(path_to_commit)
+ end
+
+ it 'returns the last commits for every entry in the current path starting from the offset' do
+ result = repository.list_last_commits_for_tree(sample_commit.id, '.', offset: path_to_commit.size - 1)
+
+ expect(result.size).to eq(1)
+ end
+
+ it 'returns a limited number of last commits for every entry in the current path starting from the offset' do
+ result = repository.list_last_commits_for_tree(sample_commit.id, '.', limit: 1)
+
+ expect(result.size).to eq(1)
+ end
+
+ it 'returns an empty hash when offset is out of bounds' do
+ result = repository.list_last_commits_for_tree(sample_commit.id, '.', offset: path_to_commit.size)
+
+ expect(result.size).to eq(0)
+ end
+ end
+
describe '#last_commit_for_path' do
shared_examples 'getting last commit for path' do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
@@ -296,41 +345,31 @@ describe Repository do
end
describe '#new_commits' do
- shared_examples 'finding unreferenced commits' do
- set(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ set(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
- subject { repository.new_commits(rev) }
+ subject { repository.new_commits(rev) }
- context 'when there are no new commits' do
- let(:rev) { repository.commit.id }
+ context 'when there are no new commits' do
+ let(:rev) { repository.commit.id }
- it 'returns an empty array' do
- expect(subject).to eq([])
- end
+ it 'returns an empty array' do
+ expect(subject).to eq([])
end
+ end
- context 'when new commits are found' do
- let(:branch) { 'orphaned-branch' }
- let!(:rev) { repository.commit(branch).id }
+ context 'when new commits are found' do
+ let(:branch) { 'orphaned-branch' }
+ let!(:rev) { repository.commit(branch).id }
- it 'returns the commits' do
- repository.delete_branch(branch)
+ it 'returns the commits' do
+ repository.delete_branch(branch)
- expect(subject).not_to be_empty
- expect(subject).to all( be_a(::Commit) )
- expect(subject.size).to eq(1)
- end
+ expect(subject).not_to be_empty
+ expect(subject).to all( be_a(::Commit) )
+ expect(subject.size).to eq(1)
end
end
-
- context 'when Gitaly handles the request' do
- it_behaves_like 'finding unreferenced commits'
- end
-
- context 'when Gitaly is disabled', :disable_gitaly do
- it_behaves_like 'finding unreferenced commits'
- end
end
describe '#commits_by' do
@@ -422,6 +461,24 @@ describe Repository do
it { is_expected.to be_nil }
end
+
+ context 'regular blob' do
+ subject { repository.blob_at(repository.head_commit.sha, '.gitignore') }
+
+ it { is_expected.to be_an_instance_of(::Blob) }
+ end
+
+ context 'readme blob on HEAD' do
+ subject { repository.blob_at(repository.head_commit.sha, 'README.md') }
+
+ it { is_expected.to be_an_instance_of(::ReadmeBlob) }
+ end
+
+ context 'readme blob not on HEAD' do
+ subject { repository.blob_at(repository.find_branch('feature').target, 'README.md') }
+
+ it { is_expected.to be_an_instance_of(::Blob) }
+ end
end
describe '#merged_to_root_ref?' do
@@ -1038,208 +1095,61 @@ describe Repository do
end
end
- describe '#update_branch_with_hooks' do
- let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
- let(:new_rev) { 'a74ae73c1ccde9b974a70e82b901588071dc142a' } # commit whose parent is old_rev
- let(:updating_ref) { 'refs/heads/feature' }
- let(:target_project) { project }
- let(:target_repository) { target_project.repository }
-
- around do |example|
- # TODO Gitlab::Git::OperationService will be moved to gitaly-ruby and disappear from this repo
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
+ describe '#exists?' do
+ it 'returns true when a repository exists' do
+ expect(repository.exists?).to be(true)
end
- context 'when pre hooks were successful' do
- before do
- service = Gitlab::Git::HooksService.new
- expect(Gitlab::Git::HooksService).to receive(:new).and_return(service)
- expect(service).to receive(:execute)
- .with(git_user, target_repository.raw_repository, old_rev, new_rev, updating_ref)
- .and_yield(service).and_return(true)
- end
-
- it 'runs without errors' do
- expect do
- Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch('feature') do
- new_rev
- end
- end.not_to raise_error
- end
-
- it 'ensures the autocrlf Git option is set to :input' do
- service = Gitlab::Git::OperationService.new(git_user, repository.raw_repository)
+ it 'returns false if no full path can be constructed' do
+ allow(repository).to receive(:full_path).and_return(nil)
- expect(service).to receive(:update_autocrlf_option)
+ expect(repository.exists?).to be(false)
+ end
- service.with_branch('feature') { new_rev }
+ context 'with broken storage', :broken_storage do
+ it 'should raise a storage error' do
+ expect_to_raise_storage_error { broken_repository.exists? }
end
+ end
- context "when the branch wasn't empty" do
- it 'updates the head' do
- expect(repository.find_branch('feature').dereferenced_target.id).to eq(old_rev)
-
- Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch('feature') do
- new_rev
- end
+ context 'asymmetric caching', :use_clean_rails_memory_store_caching, :request_store do
+ let(:cache) { repository.send(:cache) }
+ let(:request_store_cache) { repository.send(:request_store_cache) }
- expect(repository.find_branch('feature').dereferenced_target.id).to eq(new_rev)
+ context 'when it returns true' do
+ before do
+ expect(repository.raw_repository).to receive(:exists?).once.and_return(true)
end
- end
-
- context 'when target project does not have the commit' do
- let(:target_project) { create(:project, :empty_repo) }
- let(:old_rev) { Gitlab::Git::BLANK_SHA }
- let(:new_rev) { project.commit('feature').sha }
- let(:updating_ref) { 'refs/heads/master' }
- it 'fetch_ref and create the branch' do
- expect(target_project.repository.raw_repository).to receive(:fetch_ref)
- .and_call_original
-
- Gitlab::Git::OperationService.new(git_user, target_repository.raw_repository)
- .with_branch(
- 'master',
- start_repository: project.repository.raw_repository,
- start_branch_name: 'feature') { new_rev }
-
- expect(target_repository.branch_names).to contain_exactly('master')
+ it 'caches the output in RequestStore' do
+ expect do
+ repository.exists?
+ end.to change { request_store_cache.read(:exists?) }.from(nil).to(true)
end
- end
- context 'when target project already has the commit' do
- let(:target_project) { create(:project, :repository) }
-
- it 'does not fetch_ref and just pass the commit' do
- expect(target_repository).not_to receive(:fetch_ref)
-
- Gitlab::Git::OperationService.new(git_user, target_repository.raw_repository)
- .with_branch('feature', start_repository: project.repository.raw_repository) { new_rev }
+ it 'caches the output in RepositoryCache' do
+ expect do
+ repository.exists?
+ end.to change { cache.read(:exists?) }.from(nil).to(true)
end
end
- end
-
- context 'when temporary ref failed to be created from other project' do
- let(:target_project) { create(:project, :empty_repo) }
-
- before do
- expect(target_project.repository.raw_repository).to receive(:run_git)
- end
-
- it 'raises Rugged::ReferenceError' do
- expect do
- Gitlab::Git::OperationService.new(git_user, target_project.repository.raw_repository)
- .with_branch('feature',
- start_repository: project.repository.raw_repository,
- &:itself)
- end.to raise_error(Gitlab::Git::CommandError)
- end
- end
-
- context 'when the update adds more than one commit' do
- let(:old_rev) { '33f3729a45c02fc67d00adb1b8bca394b0e761d9' }
-
- it 'runs without errors' do
- # old_rev is an ancestor of new_rev
- expect(repository.merge_base(old_rev, new_rev)).to eq(old_rev)
-
- # old_rev is not a direct ancestor (parent) of new_rev
- expect(repository.rugged.lookup(new_rev).parent_ids).not_to include(old_rev)
-
- branch = 'feature-ff-target'
- repository.add_branch(user, branch, old_rev)
-
- expect do
- Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch(branch) do
- new_rev
- end
- end.not_to raise_error
- end
- end
-
- context 'when the update would remove commits from the target branch' do
- let(:branch) { 'master' }
- let(:old_rev) { repository.find_branch(branch).dereferenced_target.sha }
-
- it 'raises an exception' do
- # The 'master' branch is NOT an ancestor of new_rev.
- expect(repository.merge_base(old_rev, new_rev)).not_to eq(old_rev)
-
- # Updating 'master' to new_rev would lose the commits on 'master' that
- # are not contained in new_rev. This should not be allowed.
- expect do
- Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch(branch) do
- new_rev
- end
- end.to raise_error(Gitlab::Git::CommitError)
- end
- end
-
- context 'when pre hooks failed' do
- it 'gets an error' do
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
-
- expect do
- Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch('feature') do
- new_rev
- end
- end.to raise_error(Gitlab::Git::PreReceiveError)
- end
- end
- context 'when target branch is different from source branch' do
- before do
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, ''])
- end
-
- subject do
- Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch('new-feature') do
- new_rev
+ context 'when it returns false' do
+ before do
+ expect(repository.raw_repository).to receive(:exists?).once.and_return(false)
end
- end
-
- it 'returns branch_created as true' do
- expect(subject).not_to be_repo_created
- expect(subject).to be_branch_created
- end
- end
- context 'when repository is empty' do
- before do
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, ''])
- end
-
- it 'expires creation and branch cache' do
- empty_repository = create(:project, :empty_repo).repository
-
- expect(empty_repository).to receive(:expire_exists_cache)
- expect(empty_repository).to receive(:expire_root_ref_cache)
- expect(empty_repository).to receive(:expire_emptiness_caches)
- expect(empty_repository).to receive(:expire_branches_cache)
-
- empty_repository.create_file(user, 'CHANGELOG', 'Changelog!',
- message: 'Updates file content',
- branch_name: 'master')
- end
- end
- end
-
- describe '#exists?' do
- it 'returns true when a repository exists' do
- expect(repository.exists?).to be(true)
- end
-
- it 'returns false if no full path can be constructed' do
- allow(repository).to receive(:full_path).and_return(nil)
-
- expect(repository.exists?).to be(false)
- end
+ it 'caches the output in RequestStore' do
+ expect do
+ repository.exists?
+ end.to change { request_store_cache.read(:exists?) }.from(nil).to(false)
+ end
- context 'with broken storage', :broken_storage do
- it 'should raise a storage error' do
- expect_to_raise_storage_error { broken_repository.exists? }
+ it 'does NOT cache the output in RepositoryCache' do
+ expect do
+ repository.exists?
+ end.not_to change { cache.read(:exists?) }.from(nil)
+ end
end
end
end
@@ -1308,40 +1218,6 @@ describe Repository do
end
end
- describe '#update_autocrlf_option' do
- around do |example|
- # TODO Gitlab::Git::OperationService will be moved to gitaly-ruby and disappear from this repo
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- describe 'when autocrlf is not already set to :input' do
- before do
- repository.raw_repository.autocrlf = true
- end
-
- it 'sets autocrlf to :input' do
- Gitlab::Git::OperationService.new(nil, repository.raw_repository).send(:update_autocrlf_option)
-
- expect(repository.raw_repository.autocrlf).to eq(:input)
- end
- end
-
- describe 'when autocrlf is already set to :input' do
- before do
- repository.raw_repository.autocrlf = :input
- end
-
- it 'does nothing' do
- expect(repository.raw_repository).not_to receive(:autocrlf=)
- .with(:input)
-
- Gitlab::Git::OperationService.new(nil, repository.raw_repository).send(:update_autocrlf_option)
- end
- end
- end
-
describe '#empty?' do
let(:empty_repository) { create(:project_empty_repo).repository }
@@ -1709,7 +1585,6 @@ describe Repository do
:license_blob,
:license_key,
:gitignore,
- :koding_yml,
:gitlab_ci_yml,
:branch_names,
:tag_names,
@@ -1818,10 +1693,7 @@ describe Repository do
it 'returns the number of branches' do
expect(repository.branch_count).to be_an(Integer)
- # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync
- rugged_count = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.raw_repository.rugged.branches.count
- end
+ rugged_count = rugged_repo(repository).branches.count
expect(repository.branch_count).to eq(rugged_count)
end
@@ -1831,10 +1703,7 @@ describe Repository do
it 'returns the number of tags' do
expect(repository.tag_count).to be_an(Integer)
- # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync
- rugged_count = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.raw_repository.rugged.tags.count
- end
+ rugged_count = rugged_repo(repository).tags.count
expect(repository.tag_count).to eq(rugged_count)
end
@@ -1948,12 +1817,19 @@ describe Repository do
describe '#expire_exists_cache' do
let(:cache) { repository.send(:cache) }
+ let(:request_store_cache) { repository.send(:request_store_cache) }
it 'expires the cache' do
expect(cache).to receive(:expire).with(:exists?)
repository.expire_exists_cache
end
+
+ it 'expires the request store cache', :request_store do
+ expect(request_store_cache).to receive(:expire).with(:exists?)
+
+ repository.expire_exists_cache
+ end
end
describe '#xcode_project?' do
@@ -2015,26 +1891,23 @@ describe Repository do
File.delete(path)
end
- end
- describe '#update_ref' do
- around do |example|
- # TODO Gitlab::Git::OperationService will be moved to gitaly-ruby and disappear from this repo
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
+ context 'for multiple SHAs' do
+ it 'skips non-existent SHAs' do
+ repository.keep_around('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', sample_commit.id)
+
+ expect(repository.kept_around?(sample_commit.id)).to be_truthy
end
- end
- it 'can create a ref' do
- Gitlab::Git::OperationService.new(nil, repository.raw_repository).send(:update_ref, 'refs/heads/foobar', 'refs/heads/master', Gitlab::Git::BLANK_SHA)
+ it 'skips already-kept-around SHAs' do
+ repository.keep_around(sample_commit.id)
- expect(repository.find_branch('foobar')).not_to be_nil
- end
+ expect(repository.raw_repository).to receive(:write_ref).exactly(1).and_call_original
- it 'raises CommitError when the ref update fails' do
- expect do
- Gitlab::Git::OperationService.new(nil, repository.raw_repository).send(:update_ref, 'refs/heads/master', 'refs/heads/master', Gitlab::Git::BLANK_SHA)
- end.to raise_error(Gitlab::Git::CommitError)
+ repository.keep_around(sample_commit.id, another_sample_commit.id)
+
+ expect(repository.kept_around?(another_sample_commit.id)).to be_truthy
+ end
end
end
@@ -2065,38 +1938,27 @@ describe Repository do
end
end
- describe '#koding_yml', :use_clean_rails_memory_store_caching do
- it 'returns and caches the output' do
- expect(repository).to receive(:file_on_head)
- .with(:koding)
- .and_return(Gitlab::Git::Tree.new(path: '.koding.yml'))
- .once
-
- 2.times do
- expect(repository.koding_yml).to be_an_instance_of(Gitlab::Git::Tree)
- end
- end
- end
-
describe '#readme', :use_clean_rails_memory_store_caching do
context 'with a non-existing repository' do
- it 'returns nil' do
- allow(repository).to receive(:tree).with(:head).and_return(nil)
+ let(:project) { create(:project) }
+ it 'returns nil' do
expect(repository.readme).to be_nil
end
end
context 'with an existing repository' do
context 'when no README exists' do
- it 'returns nil' do
- allow_any_instance_of(Tree).to receive(:readme).and_return(nil)
+ let(:project) { create(:project, :empty_repo) }
+ it 'returns nil' do
expect(repository.readme).to be_nil
end
end
context 'when a README exists' do
+ let(:project) { create(:project, :repository) }
+
it 'returns the README' do
expect(repository.readme).to be_an_instance_of(ReadmeBlob)
end
@@ -2127,7 +1989,7 @@ describe Repository do
match[1].to_sym if match
end.compact
- expect(methods).to match_array(Repository::CACHED_METHODS + Repository::MEMOIZED_CACHED_METHODS)
+ expect(Repository::CACHED_METHODS + Repository::MEMOIZED_CACHED_METHODS).to include(*methods)
end
end
@@ -2320,9 +2182,7 @@ describe Repository do
end
def create_remote_branch(remote_name, branch_name, target)
- rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.rugged
- end
+ rugged = rugged_repo(repository)
rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
end
@@ -2532,4 +2392,33 @@ describe Repository do
end
end
end
+
+ describe '#merge_base' do
+ set(:project) { create(:project, :repository) }
+ subject(:repository) { project.repository }
+
+ it 'only makes one gitaly call' do
+ expect(Gitlab::GitalyClient).to receive(:call).once.and_call_original
+
+ repository.merge_base('master', 'fix')
+ end
+ end
+
+ describe '#cache' do
+ subject(:cache) { repository.send(:cache) }
+
+ it 'returns a RepositoryCache' do
+ expect(subject).to be_kind_of Gitlab::RepositoryCache
+ end
+
+ it 'when is_wiki it includes wiki as part of key' do
+ allow(repository).to receive(:is_wiki) { true }
+
+ expect(subject.namespace).to include('wiki')
+ end
+
+ it 'when is_wiki is false extra_namespace is nil' do
+ expect(subject.namespace).not_to include('wiki')
+ end
+ end
end
diff --git a/spec/models/resource_label_event_spec.rb b/spec/models/resource_label_event_spec.rb
index 4756caa1b97..da6e1b5610d 100644
--- a/spec/models/resource_label_event_spec.rb
+++ b/spec/models/resource_label_event_spec.rb
@@ -3,7 +3,7 @@
require 'rails_helper'
RSpec.describe ResourceLabelEvent, type: :model do
- subject { build(:resource_label_event) }
+ subject { build(:resource_label_event, issue: issue) }
let(:issue) { create(:issue) }
let(:merge_request) { create(:merge_request) }
@@ -16,8 +16,6 @@ RSpec.describe ResourceLabelEvent, type: :model do
describe 'validations' do
it { is_expected.to be_valid }
- it { is_expected.to validate_presence_of(:label) }
- it { is_expected.to validate_presence_of(:user) }
describe 'Issuable validation' do
it 'is invalid if issue_id and merge_request_id are missing' do
@@ -45,4 +43,52 @@ RSpec.describe ResourceLabelEvent, type: :model do
end
end
end
+
+ describe '#expire_etag_cache' do
+ def expect_expiration(issue)
+ expect_any_instance_of(Gitlab::EtagCaching::Store)
+ .to receive(:touch)
+ .with("/#{issue.project.namespace.to_param}/#{issue.project.to_param}/noteable/issue/#{issue.id}/notes")
+ end
+
+ it 'expires resource note etag cache on event save' do
+ expect_expiration(subject.issuable)
+
+ subject.save!
+ end
+
+ it 'expires resource note etag cache on event destroy' do
+ subject.save!
+
+ expect_expiration(subject.issuable)
+
+ subject.destroy!
+ end
+ end
+
+ describe '#outdated_markdown?' do
+ it 'returns true if label is missing and reference is not empty' do
+ subject.attributes = { reference: 'ref', label_id: nil }
+
+ expect(subject.outdated_markdown?).to be true
+ end
+
+ it 'returns true if reference is not set yet' do
+ subject.attributes = { reference: nil }
+
+ expect(subject.outdated_markdown?).to be true
+ end
+
+ it 'returns true markdown is outdated' do
+ subject.attributes = { cached_markdown_version: 0 }
+
+ expect(subject.outdated_markdown?).to be true
+ end
+
+ it 'returns false if label and reference are set' do
+ subject.attributes = { reference: 'whatever', cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION }
+
+ expect(subject.outdated_markdown?).to be false
+ end
+ end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 029ad7f3e9f..25eecb3f909 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -345,4 +345,31 @@ describe Service do
expect(service.api_field_names).to eq(['safe_field'])
end
end
+
+ context 'logging' do
+ let(:project) { create(:project) }
+ let(:service) { create(:service, project: project) }
+ let(:test_message) { "test message" }
+ let(:arguments) do
+ {
+ service_class: service.class.name,
+ project_path: project.full_path,
+ project_id: project.id,
+ message: test_message,
+ additional_argument: 'some argument'
+ }
+ end
+
+ it 'logs info messages using json logger' do
+ expect(Gitlab::JsonLogger).to receive(:info).with(arguments)
+
+ service.log_info(test_message, additional_argument: 'some argument')
+ end
+
+ it 'logs error messages using json logger' do
+ expect(Gitlab::JsonLogger).to receive(:error).with(arguments)
+
+ service.log_error(test_message, additional_argument: 'some argument')
+ end
+ end
end
diff --git a/spec/models/shard_spec.rb b/spec/models/shard_spec.rb
new file mode 100644
index 00000000000..83104711b55
--- /dev/null
+++ b/spec/models/shard_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literals: true
+require 'spec_helper'
+
+describe Shard do
+ describe '.populate!' do
+ it 'creates shards based on the config file' do
+ expect(described_class.all).to be_empty
+
+ stub_storage_settings(foo: {}, bar: {}, baz: {})
+
+ described_class.populate!
+
+ expect(described_class.all.map(&:name)).to match_array(%w[default foo bar baz])
+ end
+ end
+
+ describe '.by_name' do
+ let(:default_shard) { described_class.find_by(name: 'default') }
+
+ before do
+ described_class.populate!
+ end
+
+ it 'returns an existing shard' do
+ expect(described_class.by_name('default')).to eq(default_shard)
+ end
+
+ it 'creates a new shard' do
+ result = described_class.by_name('foo')
+
+ expect(result).not_to eq(default_shard)
+ expect(result.name).to eq('foo')
+ end
+
+ it 'retries if creation races' do
+ expect(described_class)
+ .to receive(:find_or_create_by)
+ .with(name: 'default')
+ .and_raise(ActiveRecord::RecordNotUnique, 'fail')
+ .once
+
+ expect(described_class)
+ .to receive(:find_or_create_by)
+ .with(name: 'default')
+ .and_call_original
+
+ expect(described_class.by_name('default')).to eq(default_shard)
+ end
+ end
+end
diff --git a/spec/models/site_statistic_spec.rb b/spec/models/site_statistic_spec.rb
index 9b056fbf332..0e739900065 100644
--- a/spec/models/site_statistic_spec.rb
+++ b/spec/models/site_statistic_spec.rb
@@ -25,7 +25,6 @@ describe SiteStatistic do
it 'increases the attribute counter' do
expect { described_class.track('repositories_count') }.to change { statistics.reload.repositories_count }.by(1)
- expect { described_class.track('wikis_count') }.to change { statistics.reload.wikis_count }.by(1)
end
it 'doesnt increase the attribute counter when an exception happens during transaction' do
@@ -56,7 +55,6 @@ describe SiteStatistic do
it 'decreases the attribute counter' do
expect { described_class.untrack('repositories_count') }.to change { statistics.reload.repositories_count }.by(-1)
- expect { described_class.untrack('wikis_count') }.to change { statistics.reload.wikis_count }.by(-1)
end
it 'doesnt decrease the attribute counter when an exception happens during transaction' do
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index e09d89d235d..7a7272ccb60 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -131,6 +131,217 @@ describe Snippet do
end
end
+ describe '.with_optional_visibility' do
+ context 'when a visibility level is provided' do
+ it 'returns snippets with the given visibility' do
+ create(:snippet, :private)
+
+ snippet = create(:snippet, :public)
+ snippets = described_class
+ .with_optional_visibility(Gitlab::VisibilityLevel::PUBLIC)
+
+ expect(snippets).to eq([snippet])
+ end
+ end
+
+ context 'when a visibility level is not provided' do
+ it 'returns all snippets' do
+ snippet1 = create(:snippet, :public)
+ snippet2 = create(:snippet, :private)
+ snippets = described_class.with_optional_visibility
+
+ expect(snippets).to include(snippet1, snippet2)
+ end
+ end
+ end
+
+ describe '.only_global_snippets' do
+ it 'returns snippets not associated with any projects' do
+ create(:project_snippet)
+
+ snippet = create(:snippet)
+ snippets = described_class.only_global_snippets
+
+ expect(snippets).to eq([snippet])
+ end
+ end
+
+ describe '.only_include_projects_visible_to' do
+ let!(:project1) { create(:project, :public) }
+ let!(:project2) { create(:project, :internal) }
+ let!(:project3) { create(:project, :private) }
+ let!(:snippet1) { create(:project_snippet, project: project1) }
+ let!(:snippet2) { create(:project_snippet, project: project2) }
+ let!(:snippet3) { create(:project_snippet, project: project3) }
+
+ context 'when a user is provided' do
+ it 'returns snippets visible to the user' do
+ user = create(:user)
+
+ snippets = described_class.only_include_projects_visible_to(user)
+
+ expect(snippets).to include(snippet1, snippet2)
+ expect(snippets).not_to include(snippet3)
+ end
+ end
+
+ context 'when a user is not provided' do
+ it 'returns snippets visible to anonymous users' do
+ snippets = described_class.only_include_projects_visible_to
+
+ expect(snippets).to include(snippet1)
+ expect(snippets).not_to include(snippet2, snippet3)
+ end
+ end
+ end
+
+ describe 'only_include_projects_with_snippets_enabled' do
+ context 'when the include_private option is enabled' do
+ it 'includes snippets for projects with snippets set to private' do
+ project = create(:project)
+
+ project.project_feature
+ .update(snippets_access_level: ProjectFeature::PRIVATE)
+
+ snippet = create(:project_snippet, project: project)
+
+ snippets = described_class
+ .only_include_projects_with_snippets_enabled(include_private: true)
+
+ expect(snippets).to eq([snippet])
+ end
+ end
+
+ context 'when the include_private option is not enabled' do
+ it 'does not include snippets for projects that have snippets set to private' do
+ project = create(:project)
+
+ project.project_feature
+ .update(snippets_access_level: ProjectFeature::PRIVATE)
+
+ create(:project_snippet, project: project)
+
+ snippets = described_class.only_include_projects_with_snippets_enabled
+
+ expect(snippets).to be_empty
+ end
+ end
+
+ it 'includes snippets for projects with snippets enabled' do
+ project = create(:project)
+
+ project.project_feature
+ .update(snippets_access_level: ProjectFeature::ENABLED)
+
+ snippet = create(:project_snippet, project: project)
+ snippets = described_class.only_include_projects_with_snippets_enabled
+
+ expect(snippets).to eq([snippet])
+ end
+ end
+
+ describe '.only_include_authorized_projects' do
+ it 'only includes snippets for projects the user is authorized to see' do
+ user = create(:user)
+ project1 = create(:project, :private)
+ project2 = create(:project, :private)
+
+ project1.team.add_developer(user)
+
+ create(:project_snippet, project: project2)
+
+ snippet = create(:project_snippet, project: project1)
+ snippets = described_class.only_include_authorized_projects(user)
+
+ expect(snippets).to eq([snippet])
+ end
+ end
+
+ describe '.for_project_with_user' do
+ context 'when a user is provided' do
+ it 'returns an empty collection if the user can not view the snippets' do
+ project = create(:project, :private)
+ user = create(:user)
+
+ project.project_feature
+ .update(snippets_access_level: ProjectFeature::ENABLED)
+
+ create(:project_snippet, :public, project: project)
+
+ expect(described_class.for_project_with_user(project, user)).to be_empty
+ end
+
+ it 'returns the snippets if the user is a member of the project' do
+ project = create(:project, :private)
+ user = create(:user)
+ snippet = create(:project_snippet, project: project)
+
+ project.team.add_developer(user)
+
+ snippets = described_class.for_project_with_user(project, user)
+
+ expect(snippets).to eq([snippet])
+ end
+
+ it 'returns public snippets for a public project the user is not a member of' do
+ project = create(:project, :public)
+
+ project.project_feature
+ .update(snippets_access_level: ProjectFeature::ENABLED)
+
+ user = create(:user)
+ snippet = create(:project_snippet, :public, project: project)
+
+ create(:project_snippet, :private, project: project)
+
+ snippets = described_class.for_project_with_user(project, user)
+
+ expect(snippets).to eq([snippet])
+ end
+ end
+
+ context 'when a user is not provided' do
+ it 'returns an empty collection for a private project' do
+ project = create(:project, :private)
+
+ project.project_feature
+ .update(snippets_access_level: ProjectFeature::ENABLED)
+
+ create(:project_snippet, :public, project: project)
+
+ expect(described_class.for_project_with_user(project)).to be_empty
+ end
+
+ it 'returns public snippets for a public project' do
+ project = create(:project, :public)
+ snippet = create(:project_snippet, :public, project: project)
+
+ project.project_feature
+ .update(snippets_access_level: ProjectFeature::PUBLIC)
+
+ create(:project_snippet, :private, project: project)
+
+ snippets = described_class.for_project_with_user(project)
+
+ expect(snippets).to eq([snippet])
+ end
+ end
+ end
+
+ describe '.visible_to_or_authored_by' do
+ it 'returns snippets visible to the user' do
+ user = create(:user)
+ snippet1 = create(:snippet, :public)
+ snippet2 = create(:snippet, :private, author: user)
+ snippet3 = create(:snippet, :private)
+
+ snippets = described_class.visible_to_or_authored_by(user)
+
+ expect(snippets).to include(snippet1, snippet2)
+ expect(snippets).not_to include(snippet3)
+ end
+ end
+
describe '#participants' do
let(:project) { create(:project, :public) }
let(:snippet) { create(:snippet, content: 'foo', project: project) }
diff --git a/spec/models/ssh_host_key_spec.rb b/spec/models/ssh_host_key_spec.rb
new file mode 100644
index 00000000000..75db43b3d56
--- /dev/null
+++ b/spec/models/ssh_host_key_spec.rb
@@ -0,0 +1,164 @@
+require 'spec_helper'
+
+describe SshHostKey do
+ using RSpec::Parameterized::TableSyntax
+ include ReactiveCachingHelpers
+
+ let(:key1) do
+ 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3UpyF2iLqy1d63M6k3jH1vuEnq/NWtE+o' \
+ 'rJe1Xn7JoRbduKd6zpsJ0JhBGWgcQK0ph0aGW5PcudzzBSc+SlYfCc4GTaxDtmj41hW0o72m' \
+ 'NiuDW3oKXXShOiVRde2ZOquH8Z865jGiZIC8BI/bXZD29IGUih0hPu7Rjp70VYiE+35QRf/p' \
+ 'sD0Ddrz8QUIG3A/2dMzLI5F5ZORk3BIX2F3mJwJOvZxRhR/SqyphDMZ5eZ0EzqbFBCDE6HAB' \
+ 'Woz9ck8RBGLvCIggmDHj3FmMLcQGMDiy6wKp7QdnBtxjCP6vtE6YPUM223AqsWt+9NTtCfB8' \
+ 'YdNAH7YcHHOR1FgtSk1x'
+ end
+
+ let(:key2) do
+ 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLIp+4ciR2YO9f9rpldc7InNQw/TBUtcNb' \
+ 'J2XR0rr15/5ytz7YM16xXG0Qjx576PNSmqs4gbTrvTuFZak+v1Jx/9deHRq/yqp9f+tv33+i' \
+ 'aJGCQCX/+OVY7aWgV2R9YsS7XQ4mnv4XlOTEssib/rGAIT+ATd/GcdYSEOO+dh4O09/6O/jI' \
+ 'MGSeP+NNetgn1nPCnLOjrXFZUnUtNDi6EEKeIlrliJjSb7Jr4f7gjvZnv4RskWHHFo8FgAAq' \
+ 't0gOMT6EmKrnypBe2vLGSAXbtkXr01q6/DNPH+n9VA1LTV6v1KN/W5CN5tQV11wRSKiM8g5O' \
+ 'Ebi86VjJRi2sOuYoXQU1'
+ end
+
+ # Purposefully ordered so that `sort` will make changes
+ let(:known_hosts) do
+ <<~EOF
+ example.com #{key1} git@localhost
+ @revoked other.example.com #{key2} git@localhost
+ EOF
+ end
+
+ let(:extra) { known_hosts + "foo\nbar\n" }
+ let(:reversed) { known_hosts.lines.reverse.join }
+
+ let(:compare_host_keys) { nil }
+
+ def stub_ssh_keyscan(args, status: true, stdout: "", stderr: "")
+ stdin = StringIO.new
+ stdout = double(:stdout, read: stdout)
+ stderr = double(:stderr, read: stderr)
+ wait_thr = double(:wait_thr, value: double(success?: status))
+
+ expect(Open3).to receive(:popen3).with({}, 'ssh-keyscan', *args).and_yield(stdin, stdout, stderr, wait_thr)
+
+ stdin
+ end
+
+ let(:project) { build(:project) }
+
+ subject(:ssh_host_key) { described_class.new(project: project, url: 'ssh://example.com:2222', compare_host_keys: compare_host_keys) }
+
+ describe '#fingerprints', :use_clean_rails_memory_store_caching do
+ it 'returns an array of indexed fingerprints when the cache is filled' do
+ stub_reactive_cache(ssh_host_key, known_hosts: known_hosts)
+
+ expected = [key1, key2]
+ .map { |data| Gitlab::SSHPublicKey.new(data) }
+ .each_with_index
+ .map { |key, i| { bits: key.bits, fingerprint: key.fingerprint, type: key.type, index: i } }
+
+ expect(ssh_host_key.fingerprints.as_json).to eq(expected)
+ end
+
+ it 'returns an empty array when the cache is empty' do
+ expect(ssh_host_key.fingerprints).to eq([])
+ end
+ end
+
+ describe '#fingerprints', :use_clean_rails_memory_store_caching do
+ it 'returns an array of indexed fingerprints when the cache is filled' do
+ stub_reactive_cache(ssh_host_key, known_hosts: known_hosts)
+
+ expect(ssh_host_key.fingerprints.as_json).to eq(
+ [
+ { bits: 2048, fingerprint: Gitlab::SSHPublicKey.new(key1).fingerprint, type: :rsa, index: 0 },
+ { bits: 2048, fingerprint: Gitlab::SSHPublicKey.new(key2).fingerprint, type: :rsa, index: 1 }
+ ]
+ )
+ end
+
+ it 'returns an empty array when the cache is empty' do
+ expect(ssh_host_key.fingerprints).to eq([])
+ end
+ end
+
+ describe '#host_keys_changed?' do
+ where(:known_hosts_a, :known_hosts_b, :result) do
+ known_hosts | extra | true
+ known_hosts | "foo\n" | true
+ known_hosts | '' | true
+ known_hosts | nil | true
+ known_hosts | known_hosts | false
+ reversed | known_hosts | false
+ extra | "foo\n" | true
+ '' | '' | false
+ nil | nil | false
+ '' | nil | false
+ end
+
+ with_them do
+ let(:compare_host_keys) { known_hosts_b }
+
+ subject { ssh_host_key.host_keys_changed? }
+
+ context '(normal)' do
+ let(:compare_host_keys) { known_hosts_b }
+
+ before do
+ expect(ssh_host_key).to receive(:known_hosts).and_return(known_hosts_a)
+ end
+
+ it { is_expected.to eq(result) }
+ end
+
+ # Comparisons should be symmetrical, so test the reverse too
+ context '(reversed)' do
+ let(:compare_host_keys) { known_hosts_a }
+
+ before do
+ expect(ssh_host_key).to receive(:known_hosts).and_return(known_hosts_b)
+ end
+
+ it { is_expected.to eq(result) }
+ end
+ end
+ end
+
+ describe '#calculate_reactive_cache' do
+ subject(:cache) { ssh_host_key.calculate_reactive_cache }
+
+ it 'writes the hostname to STDIN' do
+ stdin = stub_ssh_keyscan(%w[-T 5 -p 2222 -f-])
+
+ cache
+
+ expect(stdin.string).to eq("example.com\n")
+ end
+
+ context 'successful key scan' do
+ it 'stores the cleaned known_hosts data' do
+ stub_ssh_keyscan(%w[-T 5 -p 2222 -f-], stdout: "KEY 1\nKEY 1\n\n# comment\nKEY 2\n")
+
+ is_expected.to eq(known_hosts: "KEY 1\nKEY 2\n")
+ end
+ end
+
+ context 'failed key scan (exit code 1)' do
+ it 'returns a generic error' do
+ stub_ssh_keyscan(%w[-T 5 -p 2222 -f-], stdout: 'blarg', status: false)
+
+ is_expected.to eq(error: 'Failed to detect SSH host keys')
+ end
+ end
+
+ context 'failed key scan (exit code 0)' do
+ it 'returns a generic error' do
+ stub_ssh_keyscan(%w[-T 5 -p 2222 -f-], stderr: 'Unknown host')
+
+ is_expected.to eq(error: 'Failed to detect SSH host keys')
+ end
+ end
+ end
+end
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index f29abcf536e..82ff2a002e0 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -173,4 +173,129 @@ describe Todo do
expect(subject).not_to be_self_assigned
end
end
+
+ describe '.for_action' do
+ it 'returns the todos for a given action' do
+ create(:todo, action: Todo::MENTIONED)
+
+ todo = create(:todo, action: Todo::ASSIGNED)
+
+ expect(described_class.for_action(Todo::ASSIGNED)).to eq([todo])
+ end
+ end
+
+ describe '.for_author' do
+ it 'returns the todos for a given author' do
+ user1 = create(:user)
+ user2 = create(:user)
+ todo = create(:todo, author: user1)
+
+ create(:todo, author: user2)
+
+ expect(described_class.for_author(user1)).to eq([todo])
+ end
+ end
+
+ describe '.for_project' do
+ it 'returns the todos for a given project' do
+ project1 = create(:project)
+ project2 = create(:project)
+ todo = create(:todo, project: project1)
+
+ create(:todo, project: project2)
+
+ expect(described_class.for_project(project1)).to eq([todo])
+ end
+ end
+
+ describe '.for_group' do
+ it 'returns the todos for a given group' do
+ group1 = create(:group)
+ group2 = create(:group)
+ todo = create(:todo, group: group1)
+
+ create(:todo, group: group2)
+
+ expect(described_class.for_group(group1)).to eq([todo])
+ end
+ end
+
+ describe '.for_type' do
+ it 'returns the todos for a given target type' do
+ todo = create(:todo, target: create(:issue))
+
+ create(:todo, target: create(:merge_request))
+
+ expect(described_class.for_type(Issue.name)).to eq([todo])
+ end
+ end
+
+ describe '.for_target' do
+ it 'returns the todos for a given target' do
+ todo = create(:todo, target: create(:issue))
+
+ create(:todo, target: create(:merge_request))
+
+ expect(described_class.for_target(todo.target)).to eq([todo])
+ end
+ end
+
+ describe '.for_commit' do
+ it 'returns the todos for a commit ID' do
+ todo = create(:todo, commit_id: '123')
+
+ create(:todo, commit_id: '456')
+
+ expect(described_class.for_commit('123')).to eq([todo])
+ end
+ end
+
+ describe '.for_group_and_descendants' do
+ it 'returns the todos for a group and its descendants' do
+ parent_group = create(:group)
+ child_group = create(:group, parent: parent_group)
+
+ todo1 = create(:todo, group: parent_group)
+ todo2 = create(:todo, group: child_group)
+ todos = described_class.for_group_and_descendants(parent_group)
+
+ expect(todos).to include(todo1)
+
+ # Nested groups only work on PostgreSQL, so on MySQL todo2 won't be
+ # present.
+ expect(todos).to include(todo2) if Gitlab::Database.postgresql?
+ end
+ end
+
+ describe '.any_for_target?' do
+ it 'returns true if there are todos for a given target' do
+ todo = create(:todo)
+
+ expect(described_class.any_for_target?(todo.target)).to eq(true)
+ end
+
+ it 'returns false if there are no todos for a given target' do
+ issue = create(:issue)
+
+ expect(described_class.any_for_target?(issue)).to eq(false)
+ end
+ end
+
+ describe '.update_state' do
+ it 'updates the state of todos' do
+ todo = create(:todo, :pending)
+ ids = described_class.update_state(:done)
+
+ todo.reload
+
+ expect(ids).to eq([todo.id])
+ expect(todo.state).to eq('done')
+ end
+
+ it 'does not update todos that already have the given state' do
+ create(:todo, :pending)
+
+ expect(described_class.update_state(:pending)).to be_empty
+ end
+ end
end
diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb
index 36b8e5d304f..5a0df9fbbb0 100644
--- a/spec/models/upload_spec.rb
+++ b/spec/models/upload_spec.rb
@@ -21,7 +21,8 @@ describe Upload do
path: __FILE__,
size: described_class::CHECKSUM_THRESHOLD + 1.kilobyte,
model: build_stubbed(:user),
- uploader: double('ExampleUploader')
+ uploader: double('ExampleUploader'),
+ store: ObjectStorage::Store::LOCAL
)
expect(UploadChecksumWorker)
@@ -35,7 +36,8 @@ describe Upload do
path: __FILE__,
size: described_class::CHECKSUM_THRESHOLD,
model: build_stubbed(:user),
- uploader: double('ExampleUploader')
+ uploader: double('ExampleUploader'),
+ store: ObjectStorage::Store::LOCAL
)
expect { upload.save }
@@ -60,7 +62,7 @@ describe Upload do
describe '#absolute_path' do
it 'returns the path directly when already absolute' do
path = '/path/to/namespace/project/secret/file.jpg'
- upload = described_class.new(path: path)
+ upload = described_class.new(path: path, store: ObjectStorage::Store::LOCAL)
expect(upload).not_to receive(:uploader_class)
@@ -69,7 +71,7 @@ describe Upload do
it "delegates to the uploader's absolute_path method" do
uploader = spy('FakeUploader')
- upload = described_class.new(path: 'secret/file.jpg')
+ upload = described_class.new(path: 'secret/file.jpg', store: ObjectStorage::Store::LOCAL)
expect(upload).to receive(:uploader_class).and_return(uploader)
upload.absolute_path
@@ -81,7 +83,8 @@ describe Upload do
describe '#calculate_checksum!' do
let(:upload) do
described_class.new(path: __FILE__,
- size: described_class::CHECKSUM_THRESHOLD - 1.megabyte)
+ size: described_class::CHECKSUM_THRESHOLD - 1.megabyte,
+ store: ObjectStorage::Store::LOCAL)
end
it 'sets `checksum` to SHA256 sum of the file' do
@@ -91,7 +94,7 @@ describe Upload do
.to change { upload.checksum }.from(nil).to(expected)
end
- it 'sets `checksum` to nil for a non-existant file' do
+ it 'sets `checksum` to nil for a non-existent file' do
expect(upload).to receive(:exist?).and_return(false)
checksum = Digest::SHA256.file(__FILE__).hexdigest
@@ -104,15 +107,56 @@ describe Upload do
describe '#exist?' do
it 'returns true when the file exists' do
- upload = described_class.new(path: __FILE__)
+ upload = described_class.new(path: __FILE__, store: ObjectStorage::Store::LOCAL)
expect(upload).to exist
end
- it 'returns false when the file does not exist' do
- upload = described_class.new(path: "#{__FILE__}-nope")
+ context 'when the file does not exist' do
+ it 'returns false' do
+ upload = described_class.new(path: "#{__FILE__}-nope", store: ObjectStorage::Store::LOCAL)
- expect(upload).not_to exist
+ expect(upload).not_to exist
+ end
+
+ context 'when the record is persisted' do
+ it 'sends a message to Sentry' do
+ upload = create(:upload, :issuable_upload)
+
+ expect(Gitlab::Sentry).to receive(:enabled?).and_return(true)
+ expect(Raven).to receive(:capture_message).with("Upload file does not exist", extra: upload.attributes)
+
+ upload.exist?
+ end
+
+ it 'increments a metric counter to signal a problem' do
+ upload = create(:upload, :issuable_upload)
+
+ counter = double(:counter)
+ expect(counter).to receive(:increment)
+ expect(Gitlab::Metrics).to receive(:counter).with(:upload_file_does_not_exist_total, 'The number of times an upload record could not find its file').and_return(counter)
+
+ upload.exist?
+ end
+ end
+
+ context 'when the record is not persisted' do
+ it 'does not send a message to Sentry' do
+ upload = described_class.new(path: "#{__FILE__}-nope", store: ObjectStorage::Store::LOCAL)
+
+ expect(Raven).not_to receive(:capture_message)
+
+ upload.exist?
+ end
+
+ it 'does not increment a metric counter' do
+ upload = described_class.new(path: "#{__FILE__}-nope", store: ObjectStorage::Store::LOCAL)
+
+ expect(Gitlab::Metrics).not_to receive(:counter)
+
+ upload.exist?
+ end
+ end
end
end
diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb
new file mode 100644
index 00000000000..2898613545c
--- /dev/null
+++ b/spec/models/user_preference_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe UserPreference do
+ describe '#set_notes_filter' do
+ let(:issuable) { build_stubbed(:issue) }
+ let(:user_preference) { create(:user_preference) }
+
+ shared_examples 'setting system notes' do
+ it 'returns updated discussion filter' do
+ filter_name =
+ user_preference.set_notes_filter(filter, issuable)
+
+ expect(filter_name).to eq(filter)
+ end
+
+ it 'updates discussion filter for issuable class' do
+ user_preference.set_notes_filter(filter, issuable)
+
+ expect(user_preference.reload.issue_notes_filter).to eq(filter)
+ end
+ end
+
+ context 'when filter is set to all notes' do
+ let(:filter) { described_class::NOTES_FILTERS[:all_notes] }
+
+ it_behaves_like 'setting system notes'
+ end
+
+ context 'when filter is set to only comments' do
+ let(:filter) { described_class::NOTES_FILTERS[:only_comments] }
+
+ it_behaves_like 'setting system notes'
+ end
+
+ context 'when filter is set to only activity' do
+ let(:filter) { described_class::NOTES_FILTERS[:only_activity] }
+
+ it_behaves_like 'setting system notes'
+ end
+
+ context 'when notes_filter parameter is invalid' do
+ let(:only_comments) { described_class::NOTES_FILTERS[:only_comments] }
+
+ it 'returns the current notes filter' do
+ user_preference.set_notes_filter(only_comments, issuable)
+
+ expect(user_preference.set_notes_filter(9999, issuable)).to eq(only_comments)
+ end
+ end
+ end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index f5e2c977104..733c1c49f08 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -167,6 +167,61 @@ describe User do
subject { build(:user).tap { |user| user.emails << build(:email, email: email_value) } }
end
+ describe '#commit_email' do
+ subject(:user) { create(:user) }
+
+ it 'defaults to the primary email' do
+ expect(user.email).to be_present
+ expect(user.commit_email).to eq(user.email)
+ end
+
+ it 'defaults to the primary email when the column in the database is null' do
+ user.update_column(:commit_email, nil)
+
+ found_user = described_class.find_by(id: user.id)
+
+ expect(found_user.commit_email).to eq(user.email)
+ end
+
+ it 'returns the private commit email when commit_email has _private' do
+ user.update_column(:commit_email, Gitlab::PrivateCommitEmail::TOKEN)
+
+ expect(user.commit_email).to eq(user.private_commit_email)
+ end
+
+ it 'can be set to a confirmed email' do
+ confirmed = create(:email, :confirmed, user: user)
+ user.commit_email = confirmed.email
+
+ expect(user).to be_valid
+ expect(user.commit_email).to eq(confirmed.email)
+ end
+
+ it 'can not be set to an unconfirmed email' do
+ unconfirmed = create(:email, user: user)
+ user.commit_email = unconfirmed.email
+
+ # This should set the commit_email attribute to the primary email
+ expect(user).to be_valid
+ expect(user.commit_email).to eq(user.email)
+ end
+
+ it 'can not be set to a non-existent email' do
+ user.commit_email = 'non-existent-email@nonexistent.nonexistent'
+
+ # This should set the commit_email attribute to the primary email
+ expect(user).to be_valid
+ expect(user.commit_email).to eq(user.email)
+ end
+
+ it 'can not be set to an invalid email, even if confirmed' do
+ confirmed = create(:email, :confirmed, :skip_validate, user: user, email: 'invalid')
+ user.commit_email = confirmed.email
+
+ expect(user).not_to be_valid
+ end
+ end
+
describe 'email' do
context 'when no signup domains whitelisted' do
before do
@@ -284,6 +339,40 @@ describe User do
expect(user).to be_valid
end
end
+
+ context 'set_commit_email' do
+ it 'keeps commit email when private commit email is being used' do
+ user = create(:user, commit_email: Gitlab::PrivateCommitEmail::TOKEN)
+
+ expect(user.read_attribute(:commit_email)).to eq(Gitlab::PrivateCommitEmail::TOKEN)
+ end
+
+ it 'keeps the commit email when nil' do
+ user = create(:user, commit_email: nil)
+
+ expect(user.read_attribute(:commit_email)).to be_nil
+ end
+
+ it 'reverts to nil when email is not verified' do
+ user = create(:user, commit_email: "foo@bar.com")
+
+ expect(user.read_attribute(:commit_email)).to be_nil
+ end
+ end
+
+ context 'owns_commit_email' do
+ it 'accepts private commit email' do
+ user = build(:user, commit_email: Gitlab::PrivateCommitEmail::TOKEN)
+
+ expect(user).to be_valid
+ end
+
+ it 'accepts nil commit email' do
+ user = build(:user, commit_email: nil)
+
+ expect(user).to be_valid
+ end
+ end
end
end
@@ -315,6 +404,14 @@ describe User do
expect(users_with_two_factor).to eq([user_with_2fa.id])
expect(users_with_two_factor).not_to include(user_without_2fa.id)
end
+
+ it 'works with ORDER BY' do
+ user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f)
+
+ expect(described_class
+ .with_two_factor
+ .reorder_by_name).to eq([user_with_2fa])
+ end
end
describe ".without_two_factor" do
@@ -346,17 +443,72 @@ describe User do
end
end
- describe '.todo_authors' do
- it 'filters users' do
- create :user
- user_2 = create :user
- user_3 = create :user
- current_user = create :user
- create(:todo, user: current_user, author: user_2, state: :done)
- create(:todo, user: current_user, author: user_3, state: :pending)
+ describe '.limit_to_todo_authors' do
+ context 'when filtering by todo authors' do
+ let(:user1) { create(:user) }
+ let(:user2) { create(:user) }
+
+ before do
+ create(:todo, user: user1, author: user1, state: :done)
+ create(:todo, user: user2, author: user2, state: :pending)
+ end
+
+ it 'only returns users that have authored todos' do
+ users = described_class.limit_to_todo_authors(
+ user: user2,
+ with_todos: true,
+ todo_state: :pending
+ )
+
+ expect(users).to eq([user2])
+ end
+
+ it 'ignores users that do not have a todo in the matching state' do
+ users = described_class.limit_to_todo_authors(
+ user: user1,
+ with_todos: true,
+ todo_state: :pending
+ )
+
+ expect(users).to be_empty
+ end
+ end
+
+ context 'when not filtering by todo authors' do
+ it 'returns the input relation' do
+ user1 = create(:user)
+ user2 = create(:user)
+ rel = described_class.limit_to_todo_authors(user: user1)
+
+ expect(rel).to include(user1, user2)
+ end
+ end
+
+ context 'when no user is provided' do
+ it 'returns the input relation' do
+ user1 = create(:user)
+ user2 = create(:user)
+ rel = described_class.limit_to_todo_authors
+
+ expect(rel).to include(user1, user2)
+ end
+ end
+ end
+
+ describe '.by_username' do
+ it 'finds users regardless of the case passed' do
+ user = create(:user, username: 'CaMeLcAsEd')
+ user2 = create(:user, username: 'UPPERCASE')
+
+ expect(described_class.by_username(%w(CAMELCASED uppercase)))
+ .to contain_exactly(user, user2)
+ end
+
+ it 'finds a single user regardless of the case passed' do
+ user = create(:user, username: 'CaMeLcAsEd')
- expect(described_class.todo_authors(current_user.id, 'pending')).to eq [user_3]
- expect(described_class.todo_authors(current_user.id, 'done')).to eq [user_2]
+ expect(described_class.by_username('CAMELCASED'))
+ .to contain_exactly(user)
end
end
end
@@ -603,12 +755,29 @@ describe User do
end
end
+ describe 'ensure user preference' do
+ it 'has user preference upon user initialization' do
+ user = build(:user)
+
+ expect(user.user_preference).to be_present
+ expect(user.user_preference).not_to be_persisted
+ end
+ end
+
describe 'ensure incoming email token' do
it 'has incoming email token' do
user = create(:user)
expect(user.incoming_email_token).not_to be_blank
end
+
+ it 'uses SecureRandom to generate the incoming email token' do
+ expect(SecureRandom).to receive(:hex).and_return('3b8ca303')
+
+ user = create(:user)
+
+ expect(user.incoming_email_token).to eql('gitlab')
+ end
end
describe '#ensure_user_rights_and_limits' do
@@ -946,6 +1115,14 @@ describe User do
end
describe '.find_by_any_email' do
+ it 'finds user through private commit email' do
+ user = create(:user)
+ private_email = user.private_commit_email
+
+ expect(described_class.find_by_any_email(private_email)).to eq(user)
+ expect(described_class.find_by_any_email(private_email, confirmed: true)).to eq(user)
+ end
+
it 'finds by primary email' do
user = create(:user, email: 'foo@example.com')
@@ -953,6 +1130,13 @@ describe User do
expect(described_class.find_by_any_email(user.email, confirmed: true)).to eq user
end
+ it 'finds by uppercased email' do
+ user = create(:user, email: 'foo@example.com')
+
+ expect(described_class.find_by_any_email(user.email.upcase)).to eq user
+ expect(described_class.find_by_any_email(user.email.upcase, confirmed: true)).to eq user
+ end
+
it 'finds by secondary email' do
email = create(:email, email: 'foo@example.com')
user = email.user
@@ -990,6 +1174,22 @@ describe User do
expect(described_class.by_any_email(user.email, confirmed: true)).to eq([user])
end
+
+ it 'finds user through a private commit email' do
+ user = create(:user)
+ private_email = user.private_commit_email
+
+ expect(described_class.by_any_email(private_email)).to eq([user])
+ expect(described_class.by_any_email(private_email, confirmed: true)).to eq([user])
+ end
+
+ it 'finds user through a private commit email in an array' do
+ user = create(:user)
+ private_email = user.private_commit_email
+
+ expect(described_class.by_any_email([private_email])).to eq([user])
+ expect(described_class.by_any_email([private_email], confirmed: true)).to eq([user])
+ end
end
describe '.search' do
@@ -1317,7 +1517,12 @@ describe User do
email_unconfirmed = create :email, user: user
user.reload
- expect(user.all_emails).to match_array([user.email, email_unconfirmed.email, email_confirmed.email])
+ expect(user.all_emails).to contain_exactly(
+ user.email,
+ user.private_commit_email,
+ email_unconfirmed.email,
+ email_confirmed.email
+ )
end
end
@@ -1327,9 +1532,12 @@ describe User do
it 'returns only confirmed emails' do
email_confirmed = create :email, user: user, confirmed_at: Time.now
create :email, user: user
- user.reload
- expect(user.verified_emails).to match_array([user.email, email_confirmed.email])
+ expect(user.verified_emails).to contain_exactly(
+ user.email,
+ user.private_commit_email,
+ email_confirmed.email
+ )
end
end
@@ -1345,6 +1553,18 @@ describe User do
expect(user.verified_email?(email_confirmed.email.titlecase)).to be_truthy
end
+ it 'returns true when user is found through private commit email' do
+ expect(user.verified_email?(user.private_commit_email)).to be_truthy
+ end
+
+ it 'returns true for an outdated private commit email' do
+ old_email = user.private_commit_email
+
+ user.update!(username: 'changed-username')
+
+ expect(user.verified_email?(old_email)).to be_truthy
+ end
+
it 'returns false when the email is not verified/confirmed' do
email_unconfirmed = create :email, user: user
user.reload
@@ -1540,6 +1760,24 @@ describe User do
end
end
+ describe '.find_by_private_commit_email' do
+ context 'with email' do
+ set(:user) { create(:user) }
+
+ it 'returns user through private commit email' do
+ expect(described_class.find_by_private_commit_email(user.private_commit_email)).to eq(user)
+ end
+
+ it 'returns nil when email other than private_commit_email is used' do
+ expect(described_class.find_by_private_commit_email(user.email)).to be_nil
+ end
+ end
+
+ it 'returns nil when email is nil' do
+ expect(described_class.find_by_private_commit_email(nil)).to be_nil
+ end
+ end
+
describe '#sort_by_attribute' do
before do
described_class.delete_all
@@ -2432,6 +2670,34 @@ describe User do
end
end
+ describe '#assigned_open_merge_requests_count' do
+ it 'returns number of open merge requests from non-archived projects' do
+ user = create(:user)
+ project = create(:project, :public)
+ archived_project = create(:project, :public, :archived)
+
+ create(:merge_request, source_project: project, author: user, assignee: user)
+ create(:merge_request, :closed, source_project: project, author: user, assignee: user)
+ create(:merge_request, source_project: archived_project, author: user, assignee: user)
+
+ expect(user.assigned_open_merge_requests_count(force: true)).to eq 1
+ end
+ end
+
+ describe '#assigned_open_issues_count' do
+ it 'returns number of open issues from non-archived projects' do
+ user = create(:user)
+ project = create(:project, :public)
+ archived_project = create(:project, :public, :archived)
+
+ create(:issue, project: project, author: user, assignees: [user])
+ create(:issue, :closed, project: project, author: user, assignees: [user])
+ create(:issue, project: archived_project, author: user, assignees: [user])
+
+ expect(user.assigned_open_issues_count(force: true)).to eq 1
+ end
+ end
+
describe '#personal_projects_count' do
it 'returns the number of personal projects using a single query' do
user = build(:user)
@@ -2894,6 +3160,48 @@ describe User do
end
end
+ describe '#requires_usage_stats_consent?' do
+ let(:user) { create(:user, created_at: 8.days.ago) }
+
+ before do
+ allow(user).to receive(:has_current_license?).and_return false
+ end
+
+ context 'in single-user environment' do
+ it 'requires user consent after one week' do
+ create(:user, ghost: true)
+
+ expect(user.requires_usage_stats_consent?).to be true
+ end
+
+ it 'requires user consent after one week if there is another ghost user' do
+ expect(user.requires_usage_stats_consent?).to be true
+ end
+
+ it 'does not require consent in the first week' do
+ user.created_at = 6.days.ago
+
+ expect(user.requires_usage_stats_consent?).to be false
+ end
+
+ it 'does not require consent if usage stats were set by this user' do
+ allow(Gitlab::CurrentSettings).to receive(:usage_stats_set_by_user_id).and_return(user.id)
+
+ expect(user.requires_usage_stats_consent?).to be false
+ end
+ end
+
+ context 'in multi-user environment' do
+ before do
+ create(:user)
+ end
+
+ it 'does not require consent' do
+ expect(user.requires_usage_stats_consent?).to be false
+ end
+ end
+ end
+
context 'with uploads' do
it_behaves_like 'model with mounted uploader', false do
let(:model_object) { create(:user, :with_avatar) }
@@ -2901,4 +3209,86 @@ describe User do
let(:uploader_class) { AttachmentUploader }
end
end
+
+ describe '.union_with_user' do
+ context 'when no user ID is provided' do
+ it 'returns the input relation' do
+ user = create(:user)
+
+ expect(described_class.union_with_user).to eq([user])
+ end
+ end
+
+ context 'when a user ID is provided' do
+ it 'includes the user object in the returned relation' do
+ user1 = create(:user)
+ user2 = create(:user)
+ users = described_class.where(id: user1.id).union_with_user(user2.id)
+
+ expect(users).to include(user1)
+ expect(users).to include(user2)
+ end
+
+ it 'does not re-apply any WHERE conditions on the outer query' do
+ relation = described_class.where(id: 1).union_with_user(2)
+
+ expect(relation.arel.where_sql).to be_nil
+ end
+ end
+ end
+
+ describe '.optionally_search' do
+ context 'using nil as the argument' do
+ it 'returns the current relation' do
+ user = create(:user)
+
+ expect(described_class.optionally_search).to eq([user])
+ end
+ end
+
+ context 'using an empty String as the argument' do
+ it 'returns the current relation' do
+ user = create(:user)
+
+ expect(described_class.optionally_search('')).to eq([user])
+ end
+ end
+
+ context 'using a non-empty String' do
+ it 'returns users matching the search query' do
+ user1 = create(:user)
+ create(:user)
+
+ expect(described_class.optionally_search(user1.name)).to eq([user1])
+ end
+ end
+ end
+
+ describe '.where_not_in' do
+ context 'without an argument' do
+ it 'returns the current relation' do
+ user = create(:user)
+
+ expect(described_class.where_not_in).to eq([user])
+ end
+ end
+
+ context 'using a list of user IDs' do
+ it 'excludes the users from the returned relation' do
+ user1 = create(:user)
+ user2 = create(:user)
+
+ expect(described_class.where_not_in([user2.id])).to eq([user1])
+ end
+ end
+ end
+
+ describe '.reorder_by_name' do
+ it 'reorders the input relation' do
+ user1 = create(:user, name: 'A')
+ user2 = create(:user, name: 'B')
+
+ expect(described_class.reorder_by_name).to eq([user1, user2])
+ end
+ end
end
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 63850939be1..b87a2d871e5 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -77,7 +77,7 @@ describe WikiPage do
end
describe "#initialize" do
- context "when initialized with an existing gollum page" do
+ context "when initialized with an existing page" do
before do
create_page("test page", "test content")
@page = wiki.wiki.page(title: "test page")
@@ -126,23 +126,34 @@ describe WikiPage do
end
end
- before do
- @wiki_attr = { title: "Index", content: "Home Page", format: "markdown" }
- end
-
describe "#create" do
+ let(:wiki_attr) do
+ {
+ title: "Index",
+ content: "Home Page",
+ format: "markdown",
+ message: 'Custom Commit Message'
+ }
+ end
+
after do
destroy_page("Index")
end
context "with valid attributes" do
it "saves the wiki page" do
- subject.create(@wiki_attr)
+ subject.create(wiki_attr)
expect(wiki.find_page("Index")).not_to be_nil
end
it "returns true" do
- expect(subject.create(@wiki_attr)).to eq(true)
+ expect(subject.create(wiki_attr)).to eq(true)
+ end
+
+ it 'saves the wiki page with message' do
+ subject.create(wiki_attr)
+
+ expect(wiki.find_page("Index").message).to eq 'Custom Commit Message'
end
end
end
@@ -457,6 +468,12 @@ describe WikiPage do
end
describe '#historical?' do
+ let(:page) { wiki.find_page('Update') }
+ let(:old_version) { page.versions.last.id }
+ let(:old_page) { wiki.find_page('Update', old_version) }
+ let(:latest_version) { page.versions.first.id }
+ let(:latest_page) { wiki.find_page('Update', latest_version) }
+
before do
create_page('Update', 'content')
@page = wiki.find_page('Update')
@@ -468,23 +485,27 @@ describe WikiPage do
end
it 'returns true when requesting an old version' do
- old_version = @page.versions.last.id
- old_page = wiki.find_page('Update', old_version)
-
- expect(old_page.historical?).to eq true
+ expect(old_page.historical?).to be_truthy
end
it 'returns false when requesting latest version' do
- latest_version = @page.versions.first.id
- latest_page = wiki.find_page('Update', latest_version)
-
- expect(latest_page.historical?).to eq false
+ expect(latest_page.historical?).to be_falsy
end
it 'returns false when version is nil' do
- latest_page = wiki.find_page('Update', nil)
+ expect(latest_page.historical?).to be_falsy
+ end
+
+ it 'returns false when the last version is nil' do
+ expect(old_page).to receive(:last_version) { nil }
+
+ expect(old_page.historical?).to be_falsy
+ end
+
+ it 'returns false when the version is nil' do
+ expect(old_page).to receive(:version) { nil }
- expect(latest_page.historical?).to eq false
+ expect(old_page.historical?).to be_falsy
end
end
diff --git a/spec/policies/ci/pipeline_policy_spec.rb b/spec/policies/ci/pipeline_policy_spec.rb
index bd32faf06ef..8022f61e67d 100644
--- a/spec/policies/ci/pipeline_policy_spec.rb
+++ b/spec/policies/ci/pipeline_policy_spec.rb
@@ -74,5 +74,23 @@ describe Ci::PipelinePolicy, :models do
expect(policy).to be_allowed :update_pipeline
end
end
+
+ describe 'destroy_pipeline' do
+ let(:project) { create(:project, :public) }
+
+ context 'when user has owner access' do
+ let(:user) { project.owner }
+
+ it 'is enabled' do
+ expect(policy).to be_allowed :destroy_pipeline
+ end
+ end
+
+ context 'when user is not owner' do
+ it 'is disabled' do
+ expect(policy).not_to be_allowed :destroy_pipeline
+ end
+ end
+ end
end
end
diff --git a/spec/policies/clusters/cluster_policy_spec.rb b/spec/policies/clusters/cluster_policy_spec.rb
index ced969830d8..b2f0ca1bc30 100644
--- a/spec/policies/clusters/cluster_policy_spec.rb
+++ b/spec/policies/clusters/cluster_policy_spec.rb
@@ -24,5 +24,47 @@ describe Clusters::ClusterPolicy, :models do
it { expect(policy).to be_allowed :update_cluster }
it { expect(policy).to be_allowed :admin_cluster }
end
+
+ context 'group cluster' do
+ let(:cluster) { create(:cluster, :group) }
+ let(:group) { cluster.group }
+ let(:project) { create(:project, namespace: group) }
+
+ context 'when group developer' do
+ before do
+ group.add_developer(user)
+ end
+
+ it { expect(policy).to be_disallowed :update_cluster }
+ it { expect(policy).to be_disallowed :admin_cluster }
+ end
+
+ context 'when group maintainer' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it { expect(policy).to be_allowed :update_cluster }
+ it { expect(policy).to be_allowed :admin_cluster }
+ end
+
+ context 'when project maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it { expect(policy).to be_disallowed :update_cluster }
+ it { expect(policy).to be_disallowed :admin_cluster }
+ end
+
+ context 'when project developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it { expect(policy).to be_disallowed :update_cluster }
+ it { expect(policy).to be_disallowed :admin_cluster }
+ end
+ end
end
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 35951251bc5..9d0093e8159 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -17,11 +17,15 @@ describe GroupPolicy do
let(:reporter_permissions) { [:admin_label] }
- let(:developer_permissions) { [:admin_milestones] }
+ let(:developer_permissions) { [:admin_milestone] }
let(:maintainer_permissions) do
[
- :create_projects
+ :create_projects,
+ :read_cluster,
+ :create_cluster,
+ :update_cluster,
+ :admin_cluster
]
end
@@ -31,6 +35,7 @@ describe GroupPolicy do
:admin_namespace,
:admin_group_member,
:change_visibility_level,
+ :set_note_created_at,
(Gitlab::Database.postgresql? ? :create_subgroup : nil)
].compact
end
@@ -205,7 +210,7 @@ describe GroupPolicy do
nested_group.add_guest(developer)
nested_group.add_guest(maintainer)
- group.owners.destroy_all
+ group.owners.destroy_all # rubocop: disable DestroyAll
group.add_guest(owner)
nested_group.add_owner(owner)
diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb
index 793b724bfca..008d118b557 100644
--- a/spec/policies/issue_policy_spec.rb
+++ b/spec/policies/issue_policy_spec.rb
@@ -112,6 +112,7 @@ describe IssuePolicy do
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, assignees: [assignee], author: author) }
let(:issue_no_assignee) { create(:issue, project: project) }
+ let(:issue_locked) { create(:issue, :locked, project: project, author: author, assignees: [assignee]) }
before do
project.add_guest(guest)
@@ -124,36 +125,49 @@ describe IssuePolicy do
it 'allows guests to read issues' do
expect(permissions(guest, issue)).to be_allowed(:read_issue, :read_issue_iid)
- expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue)
+ expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue, :reopen_issue)
expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
- expect(permissions(guest, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
+ expect(permissions(guest, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue, :reopen_issue)
+
+ expect(permissions(guest, issue_locked)).to be_allowed(:read_issue, :read_issue_iid)
+ expect(permissions(guest, issue_locked)).to be_disallowed(:update_issue, :admin_issue, :reopen_issue)
end
- it 'allows reporters to read, update, and admin issues' do
- expect(permissions(reporter, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
- expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ it 'allows reporters to read, update, reopen, and admin issues' do
+ expect(permissions(reporter, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue, :reopen_issue)
+ expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue, :reopen_issue)
+ expect(permissions(reporter, issue_locked)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue_locked)).to be_disallowed(:reopen_issue)
end
- it 'allows reporters from group links to read, update, and admin issues' do
- expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ it 'allows reporters from group links to read, update, reopen and admin issues' do
+ expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue, :reopen_issue)
+ expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue, :reopen_issue)
+ expect(permissions(reporter_from_group_link, issue_locked)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue_locked)).to be_disallowed(:reopen_issue)
end
- it 'allows issue authors to read and update their issues' do
- expect(permissions(author, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
+ it 'allows issue authors to read, reopen and update their issues' do
+ expect(permissions(author, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :reopen_issue)
expect(permissions(author, issue)).to be_disallowed(:admin_issue)
expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
- expect(permissions(author, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
+ expect(permissions(author, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue, :reopen_issue)
+
+ expect(permissions(author, issue_locked)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
+ expect(permissions(author, issue_locked)).to be_disallowed(:admin_issue, :reopen_issue)
end
- it 'allows issue assignees to read and update their issues' do
- expect(permissions(assignee, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
+ it 'allows issue assignees to read, reopen and update their issues' do
+ expect(permissions(assignee, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :reopen_issue)
expect(permissions(assignee, issue)).to be_disallowed(:admin_issue)
expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
- expect(permissions(assignee, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
+ expect(permissions(assignee, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue, :reopen_issue)
+
+ expect(permissions(assignee, issue_locked)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
+ expect(permissions(assignee, issue_locked)).to be_disallowed(:admin_issue, :reopen_issue)
end
context 'with confidential issues' do
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index dd3fa4e6a51..d6bc67a9d70 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -64,6 +64,7 @@ describe ProjectPolicy do
%i[
change_namespace change_visibility_level rename_project remove_project
archive_project remove_fork_project destroy_merge_request destroy_issue
+ set_issue_iid set_issue_created_at set_note_created_at
]
end
@@ -162,7 +163,7 @@ describe ProjectPolicy do
:create_build, :read_build, :update_build, :admin_build, :destroy_build,
:create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
:create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
- :create_cluster, :read_cluster, :update_cluster, :admin_cluster, :destroy_cluster,
+ :create_cluster, :read_cluster, :update_cluster, :admin_cluster,
:create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment
]
@@ -181,7 +182,7 @@ describe ProjectPolicy do
:create_build, :read_build, :update_build, :admin_build, :destroy_build,
:create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
:create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
- :create_cluster, :read_cluster, :update_cluster, :admin_cluster, :destroy_cluster,
+ :create_cluster, :read_cluster, :update_cluster, :admin_cluster,
:create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment
]
diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb
new file mode 100644
index 00000000000..e85e7a41017
--- /dev/null
+++ b/spec/presenters/blob_presenter_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe BlobPresenter, :seed_helper do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+
+ let(:git_blob) do
+ Gitlab::Git::Blob.find(
+ repository,
+ 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
+ 'files/ruby/regex.rb'
+ )
+ end
+ let(:blob) { Blob.new(git_blob) }
+
+ describe '#highlight' do
+ subject { described_class.new(blob) }
+
+ it 'returns highlighted content' do
+ expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: nil)
+
+ subject.highlight
+ end
+
+ it 'returns plain content when :plain is true' do
+ expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: true, language: nil)
+
+ subject.highlight(plain: true)
+ end
+
+ context 'gitlab-language contains a match' do
+ before do
+ allow(blob).to receive(:language_from_gitattributes).and_return('ruby')
+ end
+
+ it 'passes language to inner call' do
+ expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby')
+
+ subject.highlight
+ end
+ end
+ end
+end
diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb
index 6dfaa3b72f7..676835b3880 100644
--- a/spec/presenters/ci/build_presenter_spec.rb
+++ b/spec/presenters/ci/build_presenter_spec.rb
@@ -78,7 +78,7 @@ describe Ci::BuildPresenter do
it 'returns the reason of failure' do
status_title = presenter.status_title
- expect(status_title).to eq('Failed <br> (unknown failure)')
+ expect(status_title).to eq('Failed - (unknown failure)')
end
end
@@ -89,7 +89,7 @@ describe Ci::BuildPresenter do
status_title = presenter.status_title
expect(status_title).not_to include('(retried)')
- expect(status_title).to eq('Failed <br> (unknown failure)')
+ expect(status_title).to eq('Failed - (unknown failure)')
end
end
@@ -99,7 +99,7 @@ describe Ci::BuildPresenter do
it 'returns the reason of failure' do
status_title = presenter.status_title
- expect(status_title).to eq('Failed <br> (unknown failure)')
+ expect(status_title).to eq('Failed - (unknown failure)')
end
end
@@ -173,7 +173,7 @@ describe Ci::BuildPresenter do
it 'returns the reason of failure' do
tooltip = subject.tooltip_message
- expect(tooltip).to eq("#{build.name} - failed <br> (script failure)")
+ expect(tooltip).to eq("#{build.name} - failed - (script failure)")
end
end
@@ -183,7 +183,7 @@ describe Ci::BuildPresenter do
it 'should include the reason of failure and the retried title' do
tooltip = subject.tooltip_message
- expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (retried)")
+ expect(tooltip).to eq("#{build.name} - failed - (script failure) (retried)")
end
end
@@ -193,7 +193,7 @@ describe Ci::BuildPresenter do
it 'should include the reason of failure' do
tooltip = subject.tooltip_message
- expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (allowed to fail)")
+ expect(tooltip).to eq("#{build.name} - failed - (script failure) (allowed to fail)")
end
end
@@ -218,6 +218,42 @@ describe Ci::BuildPresenter do
end
end
+ describe '#execute_in' do
+ subject { presenter.execute_in }
+
+ context 'when build is scheduled' do
+ context 'when schedule is not expired' do
+ let(:build) { create(:ci_build, :scheduled) }
+
+ it 'returns execution time' do
+ Timecop.freeze do
+ is_expected.to be_like_time(60.0)
+ end
+ end
+ end
+
+ context 'when schedule is expired' do
+ let(:build) { create(:ci_build, :expired_scheduled) }
+
+ it 'returns execution time' do
+ Timecop.freeze do
+ is_expected.to eq(0)
+ end
+ end
+ end
+ end
+
+ context 'when build is not delayed' do
+ let(:build) { create(:ci_build) }
+
+ it 'does not return execution time' do
+ Timecop.freeze do
+ is_expected.to be_falsy
+ end
+ end
+ end
+ end
+
describe '#callout_failure_message' do
let(:build) { create(:ci_build, :failed, :api_failure) }
@@ -231,7 +267,7 @@ describe Ci::BuildPresenter do
let(:build) { create(:ci_build, :failed, :script_failure) }
context 'when is a script or missing dependency failure' do
- let(:failure_reasons) { %w(script_failure missing_dependency_failure) }
+ let(:failure_reasons) { %w(script_failure missing_dependency_failure archived_failure) }
it 'should return false' do
failure_reasons.each do |failure_reason|
diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb
index e7019b990dd..170e0ac5717 100644
--- a/spec/presenters/ci/build_runner_presenter_spec.rb
+++ b/spec/presenters/ci/build_runner_presenter_spec.rb
@@ -3,7 +3,6 @@ require 'spec_helper'
describe Ci::BuildRunnerPresenter do
let(:presenter) { described_class.new(build) }
let(:archive) { { paths: ['sample.txt'] } }
- let(:junit) { { junit: ['junit.xml'] } }
let(:archive_expectation) do
{
@@ -14,16 +13,6 @@ describe Ci::BuildRunnerPresenter do
}
end
- let(:junit_expectation) do
- {
- name: 'junit.xml',
- artifact_type: :junit,
- artifact_format: :gzip,
- paths: ['junit.xml'],
- when: 'always'
- }
- end
-
describe '#artifacts' do
context "when option contains archive-type artifacts" do
let(:build) { create(:ci_build, options: { artifacts: archive } ) }
@@ -49,20 +38,46 @@ describe Ci::BuildRunnerPresenter do
end
end
- context "when option has 'junit' keyword" do
- let(:build) { create(:ci_build, options: { artifacts: { reports: junit } } ) }
-
- it 'presents correct hash' do
- expect(presenter.artifacts.first).to include(junit_expectation)
+ context "with reports" do
+ Ci::JobArtifact::DEFAULT_FILE_NAMES.each do |file_type, filename|
+ context file_type.to_s do
+ let(:report) { { "#{file_type}": [filename] } }
+ let(:build) { create(:ci_build, options: { artifacts: { reports: report } } ) }
+
+ let(:report_expectation) do
+ {
+ name: filename,
+ artifact_type: :"#{file_type}",
+ artifact_format: Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS.fetch(file_type),
+ paths: [filename],
+ when: 'always'
+ }
+ end
+
+ it 'presents correct hash' do
+ expect(presenter.artifacts.first).to include(report_expectation)
+ end
+ end
end
end
context "when option has both archive and reports specification" do
- let(:build) { create(:ci_build, options: { script: 'echo', artifacts: { **archive, reports: junit } } ) }
+ let(:report) { { junit: ['junit.xml'] } }
+ let(:build) { create(:ci_build, options: { script: 'echo', artifacts: { **archive, reports: report } } ) }
+
+ let(:report_expectation) do
+ {
+ name: 'junit.xml',
+ artifact_type: :junit,
+ artifact_format: :gzip,
+ paths: ['junit.xml'],
+ when: 'always'
+ }
+ end
it 'presents correct hash' do
expect(presenter.artifacts.first).to include(archive_expectation)
- expect(presenter.artifacts.second).to include(junit_expectation)
+ expect(presenter.artifacts.second).to include(report_expectation)
end
context "when archive specifies 'expire_in' keyword" do
@@ -70,7 +85,7 @@ describe Ci::BuildRunnerPresenter do
it 'inherits expire_in from archive' do
expect(presenter.artifacts.first).to include({ **archive_expectation, expire_in: '3 mins 4 sec' })
- expect(presenter.artifacts.second).to include({ **junit_expectation, expire_in: '3 mins 4 sec' })
+ expect(presenter.artifacts.second).to include({ **report_expectation, expire_in: '3 mins 4 sec' })
end
end
end
diff --git a/spec/presenters/clusterable_presenter_spec.rb b/spec/presenters/clusterable_presenter_spec.rb
new file mode 100644
index 00000000000..4f4ae5e07c5
--- /dev/null
+++ b/spec/presenters/clusterable_presenter_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ClusterablePresenter do
+ include Gitlab::Routing.url_helpers
+
+ describe '.fabricate' do
+ let(:project) { create(:project) }
+
+ subject { described_class.fabricate(project) }
+
+ it 'creates an object from a descendant presenter' do
+ expect(subject).to be_kind_of(ProjectClusterablePresenter)
+ end
+ end
+end
diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb
index e96dbfb73c0..72c5eac3ede 100644
--- a/spec/presenters/clusters/cluster_presenter_spec.rb
+++ b/spec/presenters/clusters/cluster_presenter_spec.rb
@@ -1,7 +1,9 @@
require 'spec_helper'
describe Clusters::ClusterPresenter do
- let(:cluster) { create(:cluster, :provided_by_gcp) }
+ include Gitlab::Routing.url_helpers
+
+ let(:cluster) { create(:cluster, :provided_by_gcp, :project) }
subject(:presenter) do
described_class.new(cluster)
@@ -71,4 +73,21 @@ describe Clusters::ClusterPresenter do
it { is_expected.to eq(false) }
end
end
+
+ describe '#show_path' do
+ subject { described_class.new(cluster).show_path }
+
+ context 'project_type cluster' do
+ let(:project) { cluster.project }
+
+ it { is_expected.to eq(project_cluster_path(project, cluster)) }
+ end
+
+ context 'group_type cluster' do
+ let(:group) { cluster.group }
+ let(:cluster) { create(:cluster, :provided_by_gcp, :group) }
+
+ it { is_expected.to eq(group_cluster_path(group, cluster)) }
+ end
+ end
end
diff --git a/spec/presenters/group_clusterable_presenter_spec.rb b/spec/presenters/group_clusterable_presenter_spec.rb
new file mode 100644
index 00000000000..205160742bf
--- /dev/null
+++ b/spec/presenters/group_clusterable_presenter_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GroupClusterablePresenter do
+ include Gitlab::Routing.url_helpers
+
+ let(:presenter) { described_class.new(group) }
+ let(:cluster) { create(:cluster, :provided_by_gcp, :group) }
+ let(:group) { cluster.group }
+
+ describe '#can_create_cluster?' do
+ let(:user) { create(:user) }
+
+ subject { presenter.can_create_cluster? }
+
+ before do
+ allow(presenter).to receive(:current_user).and_return(user)
+ end
+
+ context 'when user can create' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when user cannot create' do
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#index_path' do
+ subject { presenter.index_path }
+
+ it { is_expected.to eq(group_clusters_path(group)) }
+ end
+
+ describe '#new_path' do
+ subject { presenter.new_path }
+
+ it { is_expected.to eq(new_group_cluster_path(group)) }
+ end
+
+ describe '#create_user_clusters_path' do
+ subject { presenter.create_user_clusters_path }
+
+ it { is_expected.to eq(create_user_group_clusters_path(group)) }
+ end
+
+ describe '#create_gcp_clusters_path' do
+ subject { presenter.create_gcp_clusters_path }
+
+ it { is_expected.to eq(create_gcp_group_clusters_path(group)) }
+ end
+
+ describe '#cluster_status_cluster_path' do
+ subject { presenter.cluster_status_cluster_path(cluster) }
+
+ it { is_expected.to eq(cluster_status_group_cluster_path(group, cluster)) }
+ end
+
+ describe '#install_applications_cluster_path' do
+ let(:application) { :helm }
+
+ subject { presenter.install_applications_cluster_path(cluster, application) }
+
+ it { is_expected.to eq(install_applications_group_cluster_path(group, cluster, application)) }
+ end
+
+ describe '#cluster_path' do
+ subject { presenter.cluster_path(cluster) }
+
+ it { is_expected.to eq(group_cluster_path(group, cluster)) }
+ end
+end
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index a1b52d8692d..bafcddebbb7 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -403,6 +403,15 @@ describe MergeRequestPresenter do
is_expected
.to eq("<a href=\"/#{resource.source_project.full_path}/tree/#{resource.source_branch}\">#{resource.source_branch}</a>")
end
+
+ it 'escapes html, when source_branch does not exist' do
+ xss_attempt = "<img src='x' onerror=alert('bad stuff') />"
+
+ allow(resource).to receive(:source_branch) { xss_attempt }
+ allow(resource).to receive(:source_branch_exists?) { false }
+
+ is_expected.to eq(ERB::Util.html_escape(xss_attempt))
+ end
end
describe '#rebase_path' do
diff --git a/spec/presenters/project_clusterable_presenter_spec.rb b/spec/presenters/project_clusterable_presenter_spec.rb
new file mode 100644
index 00000000000..c50d90ae1e8
--- /dev/null
+++ b/spec/presenters/project_clusterable_presenter_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectClusterablePresenter do
+ include Gitlab::Routing.url_helpers
+
+ let(:presenter) { described_class.new(project) }
+ let(:project) { create(:project) }
+ let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+
+ describe '#can_create_cluster?' do
+ let(:user) { create(:user) }
+
+ subject { presenter.can_create_cluster? }
+
+ before do
+ allow(presenter).to receive(:current_user).and_return(user)
+ end
+
+ context 'when user can create' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when user cannot create' do
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#index_path' do
+ subject { presenter.index_path }
+
+ it { is_expected.to eq(project_clusters_path(project)) }
+ end
+
+ describe '#new_path' do
+ subject { presenter.new_path }
+
+ it { is_expected.to eq(new_project_cluster_path(project)) }
+ end
+
+ describe '#create_user_clusters_path' do
+ subject { presenter.create_user_clusters_path }
+
+ it { is_expected.to eq(create_user_project_clusters_path(project)) }
+ end
+
+ describe '#create_gcp_clusters_path' do
+ subject { presenter.create_gcp_clusters_path }
+
+ it { is_expected.to eq(create_gcp_project_clusters_path(project)) }
+ end
+
+ describe '#cluster_status_cluster_path' do
+ subject { presenter.cluster_status_cluster_path(cluster) }
+
+ it { is_expected.to eq(cluster_status_project_cluster_path(project, cluster)) }
+ end
+
+ describe '#install_applications_cluster_path' do
+ let(:application) { :helm }
+
+ subject { presenter.install_applications_cluster_path(cluster, application) }
+
+ it { is_expected.to eq(install_applications_project_cluster_path(project, cluster, application)) }
+ end
+
+ describe '#cluster_path' do
+ subject { presenter.cluster_path(cluster) }
+
+ it { is_expected.to eq(project_cluster_path(project, cluster)) }
+ end
+end
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index 01085dbcb49..7b0192fa9c8 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -159,39 +159,76 @@ describe ProjectPresenter do
end
end
+ context 'statistics anchors (empty repo)' do
+ let(:project) { create(:project, :empty_repo) }
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ describe '#files_anchor_data' do
+ it 'returns files data' do
+ expect(presenter.files_anchor_data).to have_attributes(enabled: true,
+ label: 'Files (0 Bytes)',
+ link: nil)
+ end
+ end
+
+ describe '#commits_anchor_data' do
+ it 'returns commits data' do
+ expect(presenter.commits_anchor_data).to have_attributes(enabled: true,
+ label: 'Commits (0)',
+ link: nil)
+ end
+ end
+
+ describe '#branches_anchor_data' do
+ it 'returns branches data' do
+ expect(presenter.branches_anchor_data).to have_attributes(enabled: true,
+ label: "Branches (0)",
+ link: nil)
+ end
+ end
+
+ describe '#tags_anchor_data' do
+ it 'returns tags data' do
+ expect(presenter.tags_anchor_data).to have_attributes(enabled: true,
+ label: "Tags (0)",
+ link: nil)
+ end
+ end
+ end
+
context 'statistics anchors' do
let(:project) { create(:project, :repository) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#files_anchor_data' do
it 'returns files data' do
- expect(presenter.files_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: 'Files (0 Bytes)',
- link: presenter.project_tree_path(project)))
+ expect(presenter.files_anchor_data).to have_attributes(enabled: true,
+ label: 'Files (0 Bytes)',
+ link: presenter.project_tree_path(project))
end
end
describe '#commits_anchor_data' do
it 'returns commits data' do
- expect(presenter.commits_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: 'Commits (0)',
- link: presenter.project_commits_path(project, project.repository.root_ref)))
+ expect(presenter.commits_anchor_data).to have_attributes(enabled: true,
+ label: 'Commits (0)',
+ link: presenter.project_commits_path(project, project.repository.root_ref))
end
end
describe '#branches_anchor_data' do
it 'returns branches data' do
- expect(presenter.branches_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: "Branches (#{project.repository.branches.size})",
- link: presenter.project_branches_path(project)))
+ expect(presenter.branches_anchor_data).to have_attributes(enabled: true,
+ label: "Branches (#{project.repository.branches.size})",
+ link: presenter.project_branches_path(project))
end
end
describe '#tags_anchor_data' do
it 'returns tags data' do
- expect(presenter.tags_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: "Tags (#{project.repository.tags.size})",
- link: presenter.project_tags_path(project)))
+ expect(presenter.tags_anchor_data).to have_attributes(enabled: true,
+ label: "Tags (#{project.repository.tags.size})",
+ link: presenter.project_tags_path(project))
end
end
@@ -199,10 +236,10 @@ describe ProjectPresenter do
it 'returns new file data if user can push' do
project.add_developer(user)
- expect(presenter.new_file_anchor_data).to eq(OpenStruct.new(enabled: false,
- label: "New file",
- link: presenter.project_new_blob_path(project, 'master'),
- class_modifier: 'new'))
+ expect(presenter.new_file_anchor_data).to have_attributes(enabled: false,
+ label: "New file",
+ link: presenter.project_new_blob_path(project, 'master'),
+ class_modifier: 'success')
end
it 'returns nil if user cannot push' do
@@ -227,9 +264,9 @@ describe ProjectPresenter do
project.add_developer(user)
allow(project.repository).to receive(:readme).and_return(nil)
- expect(presenter.readme_anchor_data).to eq(OpenStruct.new(enabled: false,
- label: 'Add Readme',
- link: presenter.add_readme_path))
+ expect(presenter.readme_anchor_data).to have_attributes(enabled: false,
+ label: 'Add Readme',
+ link: presenter.add_readme_path)
end
end
@@ -237,9 +274,9 @@ describe ProjectPresenter do
it 'returns anchor data' do
allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
- expect(presenter.readme_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: 'Readme',
- link: presenter.readme_path))
+ expect(presenter.readme_anchor_data).to have_attributes(enabled: true,
+ label: 'Readme',
+ link: presenter.readme_path)
end
end
end
@@ -250,9 +287,9 @@ describe ProjectPresenter do
project.add_developer(user)
allow(project.repository).to receive(:changelog).and_return(nil)
- expect(presenter.changelog_anchor_data).to eq(OpenStruct.new(enabled: false,
- label: 'Add Changelog',
- link: presenter.add_changelog_path))
+ expect(presenter.changelog_anchor_data).to have_attributes(enabled: false,
+ label: 'Add Changelog',
+ link: presenter.add_changelog_path)
end
end
@@ -260,9 +297,9 @@ describe ProjectPresenter do
it 'returns anchor data' do
allow(project.repository).to receive(:changelog).and_return(double(name: 'foo'))
- expect(presenter.changelog_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: 'Changelog',
- link: presenter.changelog_path))
+ expect(presenter.changelog_anchor_data).to have_attributes(enabled: true,
+ label: 'Changelog',
+ link: presenter.changelog_path)
end
end
end
@@ -273,9 +310,9 @@ describe ProjectPresenter do
project.add_developer(user)
allow(project.repository).to receive(:license_blob).and_return(nil)
- expect(presenter.license_anchor_data).to eq(OpenStruct.new(enabled: false,
- label: 'Add License',
- link: presenter.add_license_path))
+ expect(presenter.license_anchor_data).to have_attributes(enabled: false,
+ label: 'Add license',
+ link: presenter.add_license_path)
end
end
@@ -283,9 +320,9 @@ describe ProjectPresenter do
it 'returns anchor data' do
allow(project.repository).to receive(:license_blob).and_return(double(name: 'foo'))
- expect(presenter.license_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: presenter.license_short_name,
- link: presenter.license_path))
+ expect(presenter.license_anchor_data).to have_attributes(enabled: true,
+ label: presenter.license_short_name,
+ link: presenter.license_path)
end
end
end
@@ -296,9 +333,9 @@ describe ProjectPresenter do
project.add_developer(user)
allow(project.repository).to receive(:contribution_guide).and_return(nil)
- expect(presenter.contribution_guide_anchor_data).to eq(OpenStruct.new(enabled: false,
- label: 'Add Contribution guide',
- link: presenter.add_contribution_guide_path))
+ expect(presenter.contribution_guide_anchor_data).to have_attributes(enabled: false,
+ label: 'Add Contribution guide',
+ link: presenter.add_contribution_guide_path)
end
end
@@ -306,9 +343,9 @@ describe ProjectPresenter do
it 'returns anchor data' do
allow(project.repository).to receive(:contribution_guide).and_return(double(name: 'foo'))
- expect(presenter.contribution_guide_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: 'Contribution guide',
- link: presenter.contribution_guide_path))
+ expect(presenter.contribution_guide_anchor_data).to have_attributes(enabled: true,
+ label: 'Contribution guide',
+ link: presenter.contribution_guide_path)
end
end
end
@@ -318,9 +355,9 @@ describe ProjectPresenter do
it 'returns anchor data' do
allow(project).to receive(:auto_devops_enabled?).and_return(true)
- expect(presenter.autodevops_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: 'Auto DevOps enabled',
- link: nil))
+ expect(presenter.autodevops_anchor_data).to have_attributes(enabled: true,
+ label: 'Auto DevOps enabled',
+ link: nil)
end
end
@@ -330,9 +367,9 @@ describe ProjectPresenter do
allow(project).to receive(:auto_devops_enabled?).and_return(false)
allow(project.repository).to receive(:gitlab_ci_yml).and_return(nil)
- expect(presenter.autodevops_anchor_data).to eq(OpenStruct.new(enabled: false,
- label: 'Enable Auto DevOps',
- link: presenter.project_settings_ci_cd_path(project, anchor: 'autodevops-settings')))
+ expect(presenter.autodevops_anchor_data).to have_attributes(enabled: false,
+ label: 'Enable Auto DevOps',
+ link: presenter.project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
end
end
@@ -343,9 +380,9 @@ describe ProjectPresenter do
project.add_maintainer(user)
cluster = create(:cluster, projects: [project])
- expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: 'Kubernetes configured',
- link: presenter.project_cluster_path(project, cluster)))
+ expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: true,
+ label: 'Kubernetes configured',
+ link: presenter.project_cluster_path(project, cluster))
end
it 'returns link to clusters page if more than one exists' do
@@ -353,17 +390,17 @@ describe ProjectPresenter do
create(:cluster, :production_environment, projects: [project])
create(:cluster, projects: [project])
- expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
- label: 'Kubernetes configured',
- link: presenter.project_clusters_path(project)))
+ expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: true,
+ label: 'Kubernetes configured',
+ link: presenter.project_clusters_path(project))
end
it 'returns link to create a cluster if no cluster exists' do
project.add_maintainer(user)
- expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: false,
- label: 'Add Kubernetes cluster',
- link: presenter.new_project_cluster_path(project)))
+ expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: false,
+ label: 'Add Kubernetes cluster',
+ link: presenter.new_project_cluster_path(project))
end
end
@@ -373,36 +410,5 @@ describe ProjectPresenter do
end
end
end
-
- describe '#koding_anchor_data' do
- it 'returns link to setup Koding if user can push and no koding YML exists' do
- project.add_developer(user)
- allow(project.repository).to receive(:koding_yml).and_return(nil)
- allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(true)
-
- expect(presenter.koding_anchor_data).to eq(OpenStruct.new(enabled: false,
- label: 'Set up Koding',
- link: presenter.add_koding_stack_path))
- end
-
- it 'returns nil if user cannot push' do
- expect(presenter.koding_anchor_data).to be_nil
- end
-
- it 'returns nil if koding is not enabled' do
- project.add_developer(user)
- allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(false)
-
- expect(presenter.koding_anchor_data).to be_nil
- end
-
- it 'returns nil if koding YML already exists' do
- project.add_developer(user)
- allow(project.repository).to receive(:koding_yml).and_return(double)
- allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(true)
-
- expect(presenter.koding_anchor_data).to be_nil
- end
- end
end
end
diff --git a/spec/rack_servers/configs/config.ru b/spec/rack_servers/configs/config.ru
new file mode 100644
index 00000000000..63daeb9eec5
--- /dev/null
+++ b/spec/rack_servers/configs/config.ru
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+app = proc do |env|
+ if env['REQUEST_METHOD'] == 'GET'
+ [200, {}, ["#{Process.pid}"]]
+ else
+ Process.kill(env['QUERY_STRING'], Process.pid)
+ [200, {}, ['Bye!']]
+ end
+end
+
+run app
diff --git a/spec/rack_servers/configs/puma.rb b/spec/rack_servers/configs/puma.rb
new file mode 100644
index 00000000000..d6b6d83d648
--- /dev/null
+++ b/spec/rack_servers/configs/puma.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+# Note: this file is used for testing puma in `spec/rack_servers/puma_spec.rb` only
+# Note: as per the convention in `config/puma.example.development.rb`,
+# this file will replace `/home/git` with the actual working directory
+
+directory '/home/git'
+threads 1, 10
+queue_requests false
+pidfile '/home/git/gitlab/tmp/pids/puma.pid'
+bind 'unix:///home/git/gitlab/tmp/tests/puma.socket'
+workers 1
+preload_app!
+worker_timeout 60
+
+require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
+require_relative "/home/git/gitlab/lib/gitlab/cluster/puma_worker_killer_initializer"
+
+before_fork do
+ Gitlab::Cluster::PumaWorkerKillerInitializer.start @config.options
+ Gitlab::Cluster::LifecycleEvents.do_before_fork
+end
+
+Gitlab::Cluster::LifecycleEvents.set_puma_options @config.options
+on_worker_boot do
+ Gitlab::Cluster::LifecycleEvents.do_worker_start
+ File.write('/home/git/gitlab/tmp/tests/puma-worker-ready', Process.pid)
+end
+
+on_restart do
+ Gitlab::Cluster::LifecycleEvents.do_master_restart
+end
diff --git a/spec/rack_servers/puma_spec.rb b/spec/rack_servers/puma_spec.rb
new file mode 100644
index 00000000000..431fab87857
--- /dev/null
+++ b/spec/rack_servers/puma_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'fileutils'
+
+require 'excon'
+
+require 'spec_helper'
+
+describe 'Puma' do
+ before(:all) do
+ project_root = File.expand_path('../..', __dir__)
+
+ config_lines = File.read('spec/rack_servers/configs/puma.rb')
+ .gsub('/home/git/gitlab', project_root)
+ .gsub('/home/git', project_root)
+
+ config_path = File.join(project_root, "tmp/tests/puma.rb")
+ @socket_path = File.join(project_root, 'tmp/tests/puma.socket')
+
+ File.write(config_path, config_lines)
+
+ cmd = %W[puma -e test -C #{config_path} #{File.join(__dir__, 'configs/config.ru')}]
+ @puma_master_pid = spawn(*cmd)
+ wait_puma_boot!(@puma_master_pid, File.join(project_root, 'tmp/tests/puma-worker-ready'))
+ WebMock.allow_net_connect!
+ end
+
+ %w[SIGQUIT SIGTERM SIGKILL].each do |signal|
+ it "has a worker that self-terminates on signal #{signal}" do
+ response = Excon.get('unix://', socket: @socket_path)
+ expect(response.status).to eq(200)
+
+ worker_pid = response.body.to_i
+ expect(worker_pid).to be > 0
+
+ begin
+ Excon.post("unix://?#{signal}", socket: @socket_path)
+ rescue Excon::Error::Socket
+ # The connection may be closed abruptly
+ end
+
+ expect(pid_gone?(worker_pid)).to eq(true)
+ end
+ end
+
+ after(:all) do
+ begin
+ WebMock.disable_net_connect!(allow_localhost: true)
+ Process.kill('TERM', @puma_master_pid)
+ rescue Errno::ESRCH
+ end
+ end
+
+ def wait_puma_boot!(master_pid, ready_file)
+ # We have seen the boot timeout after 2 minutes in CI so let's set it to 5 minutes.
+ timeout = 5 * 60
+ timeout.times do
+ return if File.exist?(ready_file)
+
+ pid = Process.waitpid(master_pid, Process::WNOHANG)
+ raise "puma failed to boot: #{$?}" unless pid.nil?
+
+ sleep 1
+ end
+
+ raise "puma boot timed out after #{timeout} seconds"
+ end
+
+ def pid_gone?(pid)
+ # Worker termination should take less than a second. That makes 10
+ # seconds a generous timeout.
+ 10.times do
+ begin
+ Process.kill(0, pid)
+ rescue Errno::ESRCH
+ return true
+ end
+
+ sleep 1
+ end
+
+ false
+ end
+end
diff --git a/spec/rack_servers/unicorn_spec.rb b/spec/rack_servers/unicorn_spec.rb
new file mode 100644
index 00000000000..6a02ebcd048
--- /dev/null
+++ b/spec/rack_servers/unicorn_spec.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+require 'fileutils'
+
+require 'excon'
+
+require 'spec_helper'
+
+describe 'Unicorn' do
+ before(:all) do
+ project_root = File.expand_path('../..', __dir__)
+
+ config_lines = File.read('config/unicorn.rb.example')
+ .gsub('/home/git/gitlab', project_root)
+ .gsub('/home/git', project_root)
+ .split("\n")
+
+ # Remove these because they make setup harder.
+ config_lines = config_lines.reject do |line|
+ %w[
+ worker_processes
+ listen
+ pid
+ stderr_path
+ stdout_path
+ ].any? { |prefix| line.start_with?(prefix) }
+ end
+
+ config_lines << "working_directory '#{Rails.root}'"
+
+ # We want to have exactly 1 worker process because that makes it
+ # predictable which process will handle our requests.
+ config_lines << 'worker_processes 1'
+
+ @socket_path = File.join(project_root, 'tmp/tests/unicorn.socket')
+ config_lines << "listen '#{@socket_path}'"
+
+ ready_file = File.join(project_root, 'tmp/tests/unicorn-worker-ready')
+ FileUtils.rm_f(ready_file)
+ after_fork_index = config_lines.index { |l| l.start_with?('after_fork') }
+ config_lines.insert(after_fork_index + 1, "File.write('#{ready_file}', Process.pid)")
+
+ config_path = File.join(project_root, 'tmp/tests/unicorn.rb')
+ File.write(config_path, config_lines.join("\n") + "\n")
+
+ cmd = %W[unicorn -E test -c #{config_path} spec/rack_servers/configs/config.ru]
+ @unicorn_master_pid = spawn(*cmd)
+ wait_unicorn_boot!(@unicorn_master_pid, ready_file)
+ WebMock.allow_net_connect!
+ end
+
+ %w[SIGQUIT SIGTERM SIGKILL].each do |signal|
+ it "has a worker that self-terminates on signal #{signal}" do
+ response = Excon.get('unix://', socket: @socket_path)
+ expect(response.status).to eq(200)
+
+ worker_pid = response.body.to_i
+ expect(worker_pid).to be > 0
+
+ begin
+ Excon.post("unix://?#{signal}", socket: @socket_path)
+ rescue Excon::Error::Socket
+ # The connection may be closed abruptly
+ end
+
+ expect(pid_gone?(worker_pid)).to eq(true)
+ end
+ end
+
+ after(:all) do
+ WebMock.disable_net_connect!(allow_localhost: true)
+ Process.kill('TERM', @unicorn_master_pid)
+ end
+
+ def wait_unicorn_boot!(master_pid, ready_file)
+ # We have seen the boot timeout after 2 minutes in CI so let's set it to 5 minutes.
+ timeout = 5 * 60
+ timeout.times do
+ return if File.exist?(ready_file)
+
+ pid = Process.waitpid(master_pid, Process::WNOHANG)
+ raise "unicorn failed to boot: #{$?}" unless pid.nil?
+
+ sleep 1
+ end
+
+ raise "unicorn boot timed out after #{timeout} seconds"
+ end
+
+ def pid_gone?(pid)
+ # Worker termination should take less than a second. That makes 10
+ # seconds a generous timeout.
+ 10.times do
+ begin
+ Process.kill(0, pid)
+ rescue Errno::ESRCH
+ return true
+ end
+
+ sleep 1
+ end
+
+ false
+ end
+end
diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb
index f56bc932f40..270e12bf201 100644
--- a/spec/requests/api/applications_spec.rb
+++ b/spec/requests/api/applications_spec.rb
@@ -5,6 +5,7 @@ describe API::Applications, :api do
let(:admin_user) { create(:user, admin: true) }
let(:user) { create(:user, admin: false) }
+ let!(:application) { create(:application, name: 'another_application', redirect_uri: 'http://other_application.url', scopes: '') }
describe 'POST /applications' do
context 'authenticated and authorized user' do
@@ -15,7 +16,7 @@ describe API::Applications, :api do
application = Doorkeeper::Application.find_by(name: 'application_name', redirect_uri: 'http://application.url')
- expect(response).to have_http_status 201
+ expect(response).to have_gitlab_http_status(201)
expect(json_response).to be_a Hash
expect(json_response['application_id']).to eq application.uid
expect(json_response['secret']).to eq application.secret
@@ -27,7 +28,7 @@ describe API::Applications, :api do
post api('/applications', admin_user), name: 'application_name', redirect_uri: 'wrong_url_format', scopes: ''
end.not_to change { Doorkeeper::Application.count }
- expect(response).to have_http_status 400
+ expect(response).to have_gitlab_http_status(400)
expect(json_response).to be_a Hash
expect(json_response['message']['redirect_uri'][0]).to eq('must be an absolute URI.')
end
@@ -37,7 +38,7 @@ describe API::Applications, :api do
post api('/applications', admin_user), redirect_uri: 'http://application.url', scopes: ''
end.not_to change { Doorkeeper::Application.count }
- expect(response).to have_http_status 400
+ expect(response).to have_gitlab_http_status(400)
expect(json_response).to be_a Hash
expect(json_response['error']).to eq('name is missing')
end
@@ -47,7 +48,7 @@ describe API::Applications, :api do
post api('/applications', admin_user), name: 'application_name', scopes: ''
end.not_to change { Doorkeeper::Application.count }
- expect(response).to have_http_status 400
+ expect(response).to have_gitlab_http_status(400)
expect(json_response).to be_a Hash
expect(json_response['error']).to eq('redirect_uri is missing')
end
@@ -57,7 +58,7 @@ describe API::Applications, :api do
post api('/applications', admin_user), name: 'application_name', redirect_uri: 'http://application.url'
end.not_to change { Doorkeeper::Application.count }
- expect(response).to have_http_status 400
+ expect(response).to have_gitlab_http_status(400)
expect(json_response).to be_a Hash
expect(json_response['error']).to eq('scopes is missing')
end
@@ -69,7 +70,7 @@ describe API::Applications, :api do
post api('/applications', user), name: 'application_name', redirect_uri: 'http://application.url', scopes: ''
end.not_to change { Doorkeeper::Application.count }
- expect(response).to have_http_status 403
+ expect(response).to have_gitlab_http_status(403)
end
end
@@ -79,7 +80,62 @@ describe API::Applications, :api do
post api('/applications'), name: 'application_name', redirect_uri: 'http://application.url'
end.not_to change { Doorkeeper::Application.count }
- expect(response).to have_http_status 401
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+ end
+
+ describe 'GET /applications' do
+ context 'authenticated and authorized user' do
+ it 'can list application' do
+ get api('/applications', admin_user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_a(Array)
+ end
+ end
+
+ context 'authorized user without authorization' do
+ it 'cannot list application' do
+ get api('/applications', user)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ context 'non-authenticated user' do
+ it 'cannot list application' do
+ get api('/applications')
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+ end
+
+ describe 'DELETE /applications/:id' do
+ context 'authenticated and authorized user' do
+ it 'can delete an application' do
+ expect do
+ delete api("/applications/#{application.id}", admin_user)
+ end.to change { Doorkeeper::Application.count }.by(-1)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+ end
+
+ context 'authorized user without authorization' do
+ it 'cannot delete an application' do
+ delete api("/applications/#{application.id}", user)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ context 'non-authenticated user' do
+ it 'cannot delete an application' do
+ delete api("/applications/#{application.id}")
+
+ expect(response).to have_gitlab_http_status(401)
end
end
end
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index 0921fecb933..7f3f3ab0977 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -167,12 +167,6 @@ describe API::AwardEmoji do
expect(response).to have_gitlab_http_status(401)
end
- it "returns a 404 error if the user authored issue" do
- post api("/projects/#{project.id}/issues/#{issue2.id}/award_emoji", user), name: 'thumbsup'
-
- expect(response).to have_gitlab_http_status(404)
- end
-
it "normalizes +1 as thumbsup award" do
post api("/projects/#{project.id}/issues/#{issue.iid}/award_emoji", user), name: '+1'
@@ -215,12 +209,6 @@ describe API::AwardEmoji do
expect(json_response['user']['username']).to eq(user.username)
end
- it "it returns 404 error when user authored note" do
- post api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{note2.id}/award_emoji", user), name: 'thumbsup'
-
- expect(response).to have_gitlab_http_status(404)
- end
-
it "normalizes +1 as thumbsup award" do
post api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{note.id}/award_emoji", user), name: '+1'
diff --git a/spec/requests/api/circuit_breakers_spec.rb b/spec/requests/api/circuit_breakers_spec.rb
index fe76f057115..6c7cb151c74 100644
--- a/spec/requests/api/circuit_breakers_spec.rb
+++ b/spec/requests/api/circuit_breakers_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
describe API::CircuitBreakers do
- let(:user) { create(:user) }
- let(:admin) { create(:admin) }
+ set(:user) { create(:user) }
+ set(:admin) { create(:admin) }
describe 'GET circuit_breakers/repository_storage' do
it 'returns a 401 for anonymous users' do
@@ -18,37 +18,26 @@ describe API::CircuitBreakers do
end
it 'returns an Array of storages' do
- expect(Gitlab::Git::Storage::Health).to receive(:for_all_storages) do
- [Gitlab::Git::Storage::Health.new('broken', [{ name: 'prefix:broken:web01', failure_count: 4 }])]
- end
-
get api('/circuit_breakers/repository_storage', admin)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_kind_of(Array)
- expect(json_response.first['storage_name']).to eq('broken')
- expect(json_response.first['failing_on_hosts']).to eq(['web01'])
- expect(json_response.first['total_failures']).to eq(4)
+ expect(json_response).to be_empty
end
describe 'GET circuit_breakers/repository_storage/failing' do
it 'returns an array of failing storages' do
- expect(Gitlab::Git::Storage::Health).to receive(:for_failing_storages) do
- [Gitlab::Git::Storage::Health.new('broken', [{ name: 'prefix:broken:web01', failure_count: 4 }])]
- end
-
get api('/circuit_breakers/repository_storage/failing', admin)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_kind_of(Array)
+ expect(json_response).to be_empty
end
end
end
describe 'DELETE circuit_breakers/repository_storage' do
it 'clears all circuit_breakers' do
- expect(Gitlab::Git::Storage::FailureInfo).to receive(:reset_all!)
-
delete api('/circuit_breakers/repository_storage', admin)
expect(response).to have_gitlab_http_status(204)
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 246947e58a8..329d069ef3d 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -238,7 +238,7 @@ describe API::Commits do
describe 'create' do
let(:message) { 'Created file' }
- let!(:invalid_c_params) do
+ let(:invalid_c_params) do
{
branch: 'master',
commit_message: message,
@@ -251,7 +251,7 @@ describe API::Commits do
]
}
end
- let!(:valid_c_params) do
+ let(:valid_c_params) do
{
branch: 'master',
commit_message: message,
@@ -264,7 +264,7 @@ describe API::Commits do
]
}
end
- let!(:valid_utf8_c_params) do
+ let(:valid_utf8_c_params) do
{
branch: 'master',
commit_message: message,
@@ -278,6 +278,12 @@ describe API::Commits do
}
end
+ it 'does not increment the usage counters using access token authentication' do
+ expect(::Gitlab::WebIdeCommitsCounter).not_to receive(:increment)
+
+ post api(url, user), valid_c_params
+ end
+
it 'a new file in project repo' do
post api(url, user), valid_c_params
@@ -315,7 +321,7 @@ describe API::Commits do
describe 'delete' do
let(:message) { 'Deleted file' }
- let!(:invalid_d_params) do
+ let(:invalid_d_params) do
{
branch: 'markdown',
commit_message: message,
@@ -327,7 +333,7 @@ describe API::Commits do
]
}
end
- let!(:valid_d_params) do
+ let(:valid_d_params) do
{
branch: 'markdown',
commit_message: message,
@@ -356,7 +362,7 @@ describe API::Commits do
describe 'move' do
let(:message) { 'Moved file' }
- let!(:invalid_m_params) do
+ let(:invalid_m_params) do
{
branch: 'feature',
commit_message: message,
@@ -370,7 +376,7 @@ describe API::Commits do
]
}
end
- let!(:valid_m_params) do
+ let(:valid_m_params) do
{
branch: 'feature',
commit_message: message,
@@ -401,7 +407,7 @@ describe API::Commits do
describe 'update' do
let(:message) { 'Updated file' }
- let!(:invalid_u_params) do
+ let(:invalid_u_params) do
{
branch: 'master',
commit_message: message,
@@ -414,7 +420,7 @@ describe API::Commits do
]
}
end
- let!(:valid_u_params) do
+ let(:valid_u_params) do
{
branch: 'master',
commit_message: message,
@@ -442,9 +448,57 @@ describe API::Commits do
end
end
+ describe 'chmod' do
+ let(:message) { 'Chmod +x file' }
+ let(:file_path) { 'files/ruby/popen.rb' }
+ let(:execute_filemode) { true }
+ let(:params) do
+ {
+ branch: 'master',
+ commit_message: message,
+ actions: [
+ {
+ action: 'chmod',
+ file_path: file_path,
+ execute_filemode: execute_filemode
+ }
+ ]
+ }
+ end
+
+ it 'responds with success' do
+ post api(url, user), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['title']).to eq(message)
+ end
+
+ context 'when execute_filemode is false' do
+ let(:execute_filemode) { false }
+
+ it 'responds with success' do
+ post api(url, user), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['title']).to eq(message)
+ end
+ end
+
+ context "when the file doesn't exists" do
+ let(:file_path) { 'foo/bar.baz' }
+
+ it "responds with 400" do
+ post api(url, user), params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("A file with this name doesn't exist")
+ end
+ end
+ end
+
describe 'multiple operations' do
let(:message) { 'Multiple actions' }
- let!(:invalid_mo_params) do
+ let(:invalid_mo_params) do
{
branch: 'master',
commit_message: message,
@@ -468,11 +522,16 @@ describe API::Commits do
action: 'update',
file_path: 'foo/bar.baz',
content: 'puts 8'
+ },
+ {
+ action: 'chmod',
+ file_path: 'files/ruby/popen.rb',
+ execute_filemode: true
}
]
}
end
- let!(:valid_mo_params) do
+ let(:valid_mo_params) do
{
branch: 'master',
commit_message: message,
@@ -496,18 +555,37 @@ describe API::Commits do
action: 'update',
file_path: 'files/ruby/popen.rb',
content: 'puts 8'
+ },
+ {
+ action: 'chmod',
+ file_path: 'files/ruby/popen.rb',
+ execute_filemode: true
}
]
}
end
- it 'are commited as one in project repo' do
+ it 'are committed as one in project repo' do
post api(url, user), valid_mo_params
expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq(message)
end
+ it 'includes the commit stats' do
+ post api(url, user), valid_mo_params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response).to include 'stats'
+ end
+
+ it "doesn't include the commit stats when stats is false" do
+ post api(url, user), valid_mo_params.merge(stats: false)
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response).not_to include 'stats'
+ end
+
it 'return a 400 bad request if there are any issues' do
post api(url, user), invalid_mo_params
@@ -1040,6 +1118,14 @@ describe API::Commits do
end
end
+ context 'when branch is empty' do
+ ['', ' '].each do |branch|
+ it_behaves_like '400 response' do
+ let(:request) { post api(route, current_user), branch: branch }
+ end
+ end
+ end
+
context 'when branch does not exist' do
it_behaves_like '404 response' do
let(:request) { post api(route, current_user), branch: 'foo' }
@@ -1122,6 +1208,118 @@ describe API::Commits do
end
end
+ describe 'POST :id/repository/commits/:sha/revert' do
+ let(:commit_id) { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' }
+ let(:commit) { project.commit(commit_id) }
+ let(:branch) { 'master' }
+ let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/revert" }
+
+ shared_examples_for 'ref revert' do
+ context 'when ref exists' do
+ it 'reverts the ref commit' do
+ post api(route, current_user), branch: branch
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/commit/basic')
+
+ expect(json_response['message']).to eq(commit.revert_message(user))
+ expect(json_response['author_name']).to eq(user.name)
+ expect(json_response['committer_name']).to eq(user.name)
+ expect(json_response['parent_ids']).to contain_exactly(commit_id)
+ end
+ end
+
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
+
+ it_behaves_like '403 response' do
+ let(:request) { post api(route, current_user), branch: branch }
+ end
+ end
+ end
+
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like '403 response' do
+ let(:request) { post api(route), branch: branch }
+ end
+ end
+
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { post api(route), branch: branch }
+ let(:message) { '404 Project Not Found' }
+ end
+ end
+
+ context 'when authenticated', 'as an owner' do
+ let(:current_user) { user }
+
+ it_behaves_like 'ref revert'
+
+ context 'when ref does not exist' do
+ let(:commit_id) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { post api(route, current_user), branch: branch }
+ let(:message) { '404 Commit Not Found' }
+ end
+ end
+
+ context 'when branch is missing' do
+ it_behaves_like '400 response' do
+ let(:request) { post api(route, current_user) }
+ end
+ end
+
+ context 'when branch is empty' do
+ ['', ' '].each do |branch|
+ it_behaves_like '400 response' do
+ let(:request) { post api(route, current_user), branch: branch }
+ end
+ end
+ end
+
+ context 'when branch does not exist' do
+ it_behaves_like '404 response' do
+ let(:request) { post api(route, current_user), branch: 'foo' }
+ let(:message) { '404 Branch Not Found' }
+ end
+ end
+
+ context 'when ref contains a dot' do
+ let(:commit_id) { branch_with_dot.name }
+ let(:commit) { project.repository.commit(commit_id) }
+
+ it_behaves_like '400 response' do
+ let(:request) { post api(route, current_user) }
+ end
+ end
+ end
+
+ context 'when authenticated', 'as a developer' do
+ let(:current_user) { user }
+
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when branch is protected' do
+ before do
+ create(:protected_branch, project: project, name: 'feature')
+ end
+
+ it 'returns 400 if you are not allowed to push to the target branch' do
+ post api(route, current_user), branch: 'feature'
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to match(/You are not allowed to push into this branch/)
+ end
+ end
+ end
+ end
+
describe 'POST /projects/:id/repository/commits/:sha/comments' do
let(:commit) { project.repository.commit }
let(:commit_id) { commit.id }
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index 61ae053cea7..3dac7225b7a 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -10,9 +10,9 @@ describe API::Deployments do
describe 'GET /projects/:id/deployments' do
let(:project) { create(:project) }
- let!(:deployment_1) { create(:deployment, project: project, iid: 11, ref: 'master', created_at: Time.now) }
- let!(:deployment_2) { create(:deployment, project: project, iid: 12, ref: 'feature', created_at: 1.day.ago) }
- let!(:deployment_3) { create(:deployment, project: project, iid: 8, ref: 'feature', created_at: 2.days.ago) }
+ let!(:deployment_1) { create(:deployment, :success, project: project, iid: 11, ref: 'master', created_at: Time.now) }
+ let!(:deployment_2) { create(:deployment, :success, project: project, iid: 12, ref: 'feature', created_at: 1.day.ago) }
+ let!(:deployment_3) { create(:deployment, :success, project: project, iid: 8, ref: 'patch', created_at: 2.days.ago) }
context 'as member of the project' do
it 'returns projects deployments sorted by id asc' do
@@ -53,8 +53,8 @@ describe API::Deployments do
'id' | 'desc' | [:deployment_3, :deployment_2, :deployment_1]
'iid' | 'asc' | [:deployment_3, :deployment_1, :deployment_2]
'iid' | 'desc' | [:deployment_2, :deployment_1, :deployment_3]
- 'ref' | 'asc' | [:deployment_2, :deployment_3, :deployment_1]
- 'ref' | 'desc' | [:deployment_1, :deployment_2, :deployment_3]
+ 'ref' | 'asc' | [:deployment_2, :deployment_1, :deployment_3]
+ 'ref' | 'desc' | [:deployment_3, :deployment_1, :deployment_2]
end
with_them do
@@ -76,7 +76,7 @@ describe API::Deployments do
describe 'GET /projects/:id/deployments/:deployment_id' do
let(:project) { deployment.environment.project }
- let!(:deployment) { create(:deployment) }
+ let!(:deployment) { create(:deployment, :success) }
context 'as a member of the project' do
it 'returns the projects deployment' do
diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb
index e6a61fdcf39..573eebe314d 100644
--- a/spec/requests/api/events_spec.rb
+++ b/spec/requests/api/events_spec.rb
@@ -2,9 +2,9 @@ require 'spec_helper'
describe API::Events do
include ApiHelpers
+
let(:user) { create(:user) }
let(:non_member) { create(:user) }
- let(:other_user) { create(:user, username: 'otheruser') }
let(:private_project) { create(:project, :private, creator_id: user.id, namespace: user.namespace) }
let(:closed_issue) { create(:closed_issue, project: private_project, author: user) }
let!(:closed_issue_event) { create(:event, project: private_project, author: user, target: closed_issue, action: Event::CLOSED, created_at: Date.new(2016, 12, 30)) }
@@ -28,12 +28,52 @@ describe API::Events do
expect(json_response.size).to eq(1)
end
end
+
+ context 'when the requesting token has "read_user" scope' do
+ let(:token) { create(:personal_access_token, scopes: ['read_user'], user: user) }
+
+ it 'returns users events' do
+ get api('/events?action=closed&target_type=issue&after=2016-12-1&before=2016-12-31', personal_access_token: token)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ end
+ end
+
+ context 'when the requesting token does not have "read_user" or "api" scope' do
+ let(:token_without_scopes) { create(:personal_access_token, scopes: ['read_repository'], user: user) }
+
+ it 'returns a "403" response' do
+ get api('/events', personal_access_token: token_without_scopes)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
end
describe 'GET /users/:id/events' do
- context "as a user that cannot see the event's project" do
- it 'returns no events' do
- get api("/users/#{user.id}/events", other_user)
+ context "as a user that cannot see another user" do
+ it 'returns a "404" response' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(non_member, :read_user, user).and_return(false)
+
+ get api("/users/#{user.id}/events", non_member)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_empty
+ end
+ end
+
+ context "as a user token that cannot see another user" do
+ let(:non_member_token) { create(:personal_access_token, scopes: ['read_user'], user: non_member) }
+
+ it 'returns a "404" response' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(non_member, :read_user, user).and_return(false)
+
+ get api("/users/#{user.id}/events", personal_access_token: non_member_token)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_empty
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 0aec186f738..a2b41d56b8b 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -313,7 +313,7 @@ describe API::Files do
describe "POST /projects/:id/repository/files/:file_path" do
let!(:file_path) { "new_subfolder%2Fnewfile%2Erb" }
- let(:valid_params) do
+ let(:params) do
{
branch: "master",
content: "puts 8",
@@ -322,7 +322,7 @@ describe API::Files do
end
it "creates a new file in project repo" do
- post api(route(file_path), user), valid_params
+ post api(route(file_path), user), params
expect(response).to have_gitlab_http_status(201)
expect(json_response["file_path"]).to eq(CGI.unescape(file_path))
@@ -337,20 +337,28 @@ describe API::Files do
expect(response).to have_gitlab_http_status(400)
end
+ it 'returns a 400 bad request if the commit message is empty' do
+ params[:commit_message] = ''
+
+ post api(route(file_path), user), params
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
it "returns a 400 if editor fails to create file" do
allow_any_instance_of(Repository).to receive(:create_file)
.and_raise(Gitlab::Git::CommitError, 'Cannot create file')
- post api(route("any%2Etxt"), user), valid_params
+ post api(route("any%2Etxt"), user), params
expect(response).to have_gitlab_http_status(400)
end
context "when specifying an author" do
it "creates a new file with the specified author" do
- valid_params.merge!(author_email: author_email, author_name: author_name)
+ params.merge!(author_email: author_email, author_name: author_name)
- post api(route("new_file_with_author%2Etxt"), user), valid_params
+ post api(route("new_file_with_author%2Etxt"), user), params
expect(response).to have_gitlab_http_status(201)
expect(response.content_type).to eq('application/json')
@@ -364,7 +372,7 @@ describe API::Files do
let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }
it "creates a new file in project repo" do
- post api(route("newfile%2Erb"), user), valid_params
+ post api(route("newfile%2Erb"), user), params
expect(response).to have_gitlab_http_status(201)
expect(json_response['file_path']).to eq('newfile.rb')
@@ -376,7 +384,7 @@ describe API::Files do
end
describe "PUT /projects/:id/repository/files" do
- let(:valid_params) do
+ let(:params) do
{
branch: 'master',
content: 'puts 8',
@@ -385,7 +393,7 @@ describe API::Files do
end
it "updates existing file in project repo" do
- put api(route(file_path), user), valid_params
+ put api(route(file_path), user), params
expect(response).to have_gitlab_http_status(200)
expect(json_response['file_path']).to eq(CGI.unescape(file_path))
@@ -394,8 +402,16 @@ describe API::Files do
expect(last_commit.author_name).to eq(user.name)
end
+ it 'returns a 400 bad request if the commit message is empty' do
+ params[:commit_message] = ''
+
+ put api(route(file_path), user), params
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
it "returns a 400 bad request if update existing file with stale last commit id" do
- params_with_stale_id = valid_params.merge(last_commit_id: 'stale')
+ params_with_stale_id = params.merge(last_commit_id: 'stale')
put api(route(file_path), user), params_with_stale_id
@@ -406,7 +422,7 @@ describe API::Files do
it "updates existing file in project repo with accepts correct last commit id" do
last_commit = Gitlab::Git::Commit
.last_for_path(project.repository, 'master', URI.unescape(file_path))
- params_with_correct_id = valid_params.merge(last_commit_id: last_commit.id)
+ params_with_correct_id = params.merge(last_commit_id: last_commit.id)
put api(route(file_path), user), params_with_correct_id
@@ -421,9 +437,9 @@ describe API::Files do
context "when specifying an author" do
it "updates a file with the specified author" do
- valid_params.merge!(author_email: author_email, author_name: author_name, content: "New content")
+ params.merge!(author_email: author_email, author_name: author_name, content: "New content")
- put api(route(file_path), user), valid_params
+ put api(route(file_path), user), params
expect(response).to have_gitlab_http_status(200)
last_commit = project.repository.commit.raw
@@ -434,7 +450,7 @@ describe API::Files do
end
describe "DELETE /projects/:id/repository/files" do
- let(:valid_params) do
+ let(:params) do
{
branch: 'master',
commit_message: 'Changed file'
@@ -442,7 +458,7 @@ describe API::Files do
end
it "deletes existing file in project repo" do
- delete api(route(file_path), user), valid_params
+ delete api(route(file_path), user), params
expect(response).to have_gitlab_http_status(204)
end
@@ -453,19 +469,27 @@ describe API::Files do
expect(response).to have_gitlab_http_status(400)
end
+ it 'returns a 400 bad request if the commit message is empty' do
+ params[:commit_message] = ''
+
+ delete api(route(file_path), user), params
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
it "returns a 400 if fails to delete file" do
allow_any_instance_of(Repository).to receive(:delete_file).and_raise(Gitlab::Git::CommitError, 'Cannot delete file')
- delete api(route(file_path), user), valid_params
+ delete api(route(file_path), user), params
expect(response).to have_gitlab_http_status(400)
end
context "when specifying an author" do
it "removes a file with the specified author" do
- valid_params.merge!(author_email: author_email, author_name: author_name)
+ params.merge!(author_email: author_email, author_name: author_name)
- delete api(route(file_path), user), valid_params
+ delete api(route(file_path), user), params
expect(response).to have_gitlab_http_status(204)
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 3a8948f8477..3802b5c6848 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -155,7 +155,7 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(response_groups).to eq(Group.visible_to_user(user1).order(:name).pluck(:name))
+ expect(response_groups).to eq(groups_visible_to_user(user1).order(:name).pluck(:name))
end
it "sorts in descending order when passed" do
@@ -164,7 +164,7 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(response_groups).to eq(Group.visible_to_user(user1).order(name: :desc).pluck(:name))
+ expect(response_groups).to eq(groups_visible_to_user(user1).order(name: :desc).pluck(:name))
end
it "sorts by path in order_by param" do
@@ -173,7 +173,7 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(response_groups).to eq(Group.visible_to_user(user1).order(:path).pluck(:name))
+ expect(response_groups).to eq(groups_visible_to_user(user1).order(:path).pluck(:name))
end
it "sorts by id in the order_by param" do
@@ -182,7 +182,7 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(response_groups).to eq(Group.visible_to_user(user1).order(:id).pluck(:name))
+ expect(response_groups).to eq(groups_visible_to_user(user1).order(:id).pluck(:name))
end
it "sorts also by descending id with pagination fix" do
@@ -191,7 +191,7 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(response_groups).to eq(Group.visible_to_user(user1).order(id: :desc).pluck(:name))
+ expect(response_groups).to eq(groups_visible_to_user(user1).order(id: :desc).pluck(:name))
end
it "sorts identical keys by id for good pagination" do
@@ -211,6 +211,10 @@ describe API::Groups do
expect(json_response).to be_an Array
expect(response_groups_ids).to eq(Group.select { |group| group['name'] == 'same-name' }.map { |group| group['id'] }.sort)
end
+
+ def groups_visible_to_user(user)
+ Group.where(id: user.authorized_groups.select(:id).reorder(nil))
+ end
end
context 'when using owned in the request' do
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index 0a789d58fd8..cca449e9e56 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -368,6 +368,14 @@ describe API::Helpers do
it_behaves_like 'successful sudo'
end
+ context 'when providing username (case insensitive)' do
+ before do
+ env[API::Helpers::SUDO_HEADER] = user.username.upcase
+ end
+
+ it_behaves_like 'successful sudo'
+ end
+
context 'when providing user ID' do
before do
env[API::Helpers::SUDO_HEADER] = user.id.to_s
@@ -386,6 +394,14 @@ describe API::Helpers do
it_behaves_like 'successful sudo'
end
+ context 'when providing username (case insensitive)' do
+ before do
+ set_param(API::Helpers::SUDO_PARAM, user.username.upcase)
+ end
+
+ it_behaves_like 'successful sudo'
+ end
+
context 'when providing user ID' do
before do
set_param(API::Helpers::SUDO_PARAM, user.id.to_s)
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 85c93f35c20..2ebcb787d06 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -369,6 +369,26 @@ describe API::Internal do
expect(user.reload.last_activity_on).to be_nil
end
end
+
+ context 'when receive_max_input_size has been updated' do
+ it 'returns custom git config' do
+ allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { 1 }
+
+ push(key, project)
+
+ expect(json_response["git_config_options"]).to be_present
+ end
+ end
+
+ context 'when receive_max_input_size is empty' do
+ it 'returns an empty git config' do
+ allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { nil }
+
+ push(key, project)
+
+ expect(json_response["git_config_options"]).to be_empty
+ end
+ end
end
end
@@ -381,7 +401,7 @@ describe API::Internal do
it do
pull(key, project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(401)
expect(json_response["status"]).to be_falsey
expect(user.reload.last_activity_on).to be_nil
end
@@ -391,13 +411,61 @@ describe API::Internal do
it do
push(key, project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(401)
expect(json_response["status"]).to be_falsey
expect(user.reload.last_activity_on).to be_nil
end
end
end
+ context "custom action" do
+ let(:access_checker) { double(Gitlab::GitAccess) }
+ let(:message) { 'CustomActionError message' }
+ let(:payload) do
+ {
+ 'action' => 'geo_proxy_to_primary',
+ 'data' => {
+ 'api_endpoints' => %w{geo/proxy_git_push_ssh/info_refs geo/proxy_git_push_ssh/push},
+ 'gl_username' => 'testuser',
+ 'primary_repo' => 'http://localhost:3000/testuser/repo.git'
+ }
+ }
+ end
+
+ let(:custom_action_result) { Gitlab::GitAccessResult::CustomAction.new(payload, message) }
+
+ before do
+ project.add_guest(user)
+ expect(Gitlab::GitAccess).to receive(:new).with(
+ key,
+ project,
+ 'ssh',
+ {
+ authentication_abilities: [:read_project, :download_code, :push_code],
+ namespace_path: project.namespace.name,
+ project_path: project.path,
+ redirected_path: nil
+ }
+ ).and_return(access_checker)
+ expect(access_checker).to receive(:check).with(
+ 'git-receive-pack',
+ 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master'
+ ).and_return(custom_action_result)
+ end
+
+ context "git push" do
+ it do
+ push(key, project)
+
+ expect(response).to have_gitlab_http_status(300)
+ expect(json_response['status']).to be_truthy
+ expect(json_response['message']).to eql(message)
+ expect(json_response['payload']).to eql(payload)
+ expect(user.reload.last_activity_on).to be_nil
+ end
+ end
+ end
+
context "blocked user" do
let(:personal_project) { create(:project, namespace: user.namespace) }
@@ -409,7 +477,7 @@ describe API::Internal do
it do
pull(key, personal_project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(401)
expect(json_response["status"]).to be_falsey
expect(user.reload.last_activity_on).to be_nil
end
@@ -419,13 +487,31 @@ describe API::Internal do
it do
push(key, personal_project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(401)
expect(json_response["status"]).to be_falsey
expect(user.reload.last_activity_on).to be_nil
end
end
end
+ context 'request times out' do
+ context 'git push' do
+ it 'responds with a gateway timeout' do
+ personal_project = create(:project, namespace: user.namespace)
+
+ expect_next_instance_of(Gitlab::GitAccess) do |access|
+ expect(access).to receive(:check).and_raise(Gitlab::GitAccess::TimeoutError, "Foo")
+ end
+ push(key, personal_project)
+
+ expect(response).to have_gitlab_http_status(503)
+ expect(json_response['status']).to be_falsey
+ expect(json_response['message']).to eq("Foo")
+ expect(user.reload.last_activity_on).to be_nil
+ end
+ end
+ end
+
context "archived project" do
before do
project.add_developer(user)
@@ -445,7 +531,7 @@ describe API::Internal do
it do
push(key, project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(401)
expect(json_response["status"]).to be_falsey
end
end
@@ -477,7 +563,7 @@ describe API::Internal do
it do
archive(key, project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(404)
expect(json_response["status"]).to be_falsey
end
end
@@ -489,7 +575,7 @@ describe API::Internal do
pull(key, project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(404)
expect(json_response["status"]).to be_falsey
end
end
@@ -498,7 +584,7 @@ describe API::Internal do
it do
pull(OpenStruct.new(id: 0), project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(404)
expect(json_response["status"]).to be_falsey
end
end
@@ -511,7 +597,7 @@ describe API::Internal do
it 'rejects the SSH push' do
push(key, project)
- expect(response.status).to eq(200)
+ expect(response.status).to eq(401)
expect(json_response['status']).to be_falsey
expect(json_response['message']).to eq 'Git access over SSH is not allowed'
end
@@ -519,7 +605,7 @@ describe API::Internal do
it 'rejects the SSH pull' do
pull(key, project)
- expect(response.status).to eq(200)
+ expect(response.status).to eq(401)
expect(json_response['status']).to be_falsey
expect(json_response['message']).to eq 'Git access over SSH is not allowed'
end
@@ -533,7 +619,7 @@ describe API::Internal do
it 'rejects the HTTP push' do
push(key, project, 'http')
- expect(response.status).to eq(200)
+ expect(response.status).to eq(401)
expect(json_response['status']).to be_falsey
expect(json_response['message']).to eq 'Git access over HTTP is not allowed'
end
@@ -541,7 +627,7 @@ describe API::Internal do
it 'rejects the HTTP pull' do
pull(key, project, 'http')
- expect(response.status).to eq(200)
+ expect(response.status).to eq(401)
expect(json_response['status']).to be_falsey
expect(json_response['message']).to eq 'Git access over HTTP is not allowed'
end
@@ -571,14 +657,14 @@ describe API::Internal do
it 'rejects the push' do
push(key, project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(404)
expect(json_response['status']).to be_falsy
end
it 'rejects the SSH pull' do
pull(key, project)
- expect(response).to have_gitlab_http_status(200)
+ expect(response).to have_gitlab_http_status(404)
expect(json_response['status']).to be_falsy
end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 28ba00c7293..3d532dd83c7 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -55,7 +55,8 @@ describe API::Issues do
end
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
- let(:no_milestone_title) { URI.escape(Milestone::None.title) }
+ let(:no_milestone_title) { "None" }
+ let(:any_milestone_title) { "Any" }
before(:all) do
project.add_reporter(user)
@@ -168,16 +169,53 @@ describe API::Issues do
expect(first_issue['id']).to eq(issue2.id)
end
- it 'returns issues reacted by the authenticated user by the given emoji' do
- issue2 = create(:issue, project: project, author: user, assignees: [user])
- award_emoji = create(:award_emoji, awardable: issue2, user: user2, name: 'star')
+ it 'returns issues with no assignee' do
+ issue2 = create(:issue, author: user2, project: project)
- get api('/issues', user2), my_reaction_emoji: award_emoji.name, scope: 'all'
+ get api('/issues', user), assignee_id: 0, scope: 'all'
expect_paginated_array_response(size: 1)
expect(first_issue['id']).to eq(issue2.id)
end
+ it 'returns issues with no assignee' do
+ issue2 = create(:issue, author: user2, project: project)
+
+ get api('/issues', user), assignee_id: 'None', scope: 'all'
+
+ expect_paginated_array_response(size: 1)
+ expect(first_issue['id']).to eq(issue2.id)
+ end
+
+ it 'returns issues with any assignee' do
+ # This issue without assignee should not be returned
+ create(:issue, author: user2, project: project)
+
+ get api('/issues', user), assignee_id: 'Any', scope: 'all'
+
+ expect_paginated_array_response(size: 3)
+ end
+
+ it 'returns issues reacted by the authenticated user' do
+ issue2 = create(:issue, project: project, author: user, assignees: [user])
+ create(:award_emoji, awardable: issue2, user: user2, name: 'star')
+
+ create(:award_emoji, awardable: issue, user: user2, name: 'thumbsup')
+
+ get api('/issues', user2), my_reaction_emoji: 'Any', scope: 'all'
+
+ expect_paginated_array_response(size: 2)
+ end
+
+ it 'returns issues not reacted by the authenticated user' do
+ issue2 = create(:issue, project: project, author: user, assignees: [user])
+ create(:award_emoji, awardable: issue2, user: user2, name: 'star')
+
+ get api('/issues', user2), my_reaction_emoji: 'None', scope: 'all'
+
+ expect_paginated_array_response(size: 2)
+ end
+
it 'returns issues matching given search string for title' do
get api("/issues", user), search: issue.title
@@ -802,6 +840,15 @@ describe API::Issues do
expect(json_response.first['id']).to eq(confidential_issue.id)
end
+ it 'returns an array of issues with any milestone' do
+ get api("#{base_url}/issues?milestone=#{any_milestone_title}", user)
+
+ response_ids = json_response.map { |issue| issue['id'] }
+
+ expect_paginated_array_response(size: 2)
+ expect(response_ids).to contain_exactly(closed_issue.id, issue.id)
+ end
+
it 'sorts by created_at descending by default' do
get api("#{base_url}/issues", user)
@@ -1023,6 +1070,20 @@ describe API::Issues do
end
end
+ context 'by a group owner' do
+ let(:group) { create(:group) }
+ let(:group_project) { create(:project, :public, namespace: group) }
+
+ it 'sets the internal ID on the new issue' do
+ group.add_owner(user2)
+ post api("/projects/#{group_project.id}/issues", user2),
+ title: 'new issue', iid: 9001
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['iid']).to eq 9001
+ end
+ end
+
context 'by another user' do
it 'ignores the given internal ID' do
post api("/projects/#{project.id}/issues", user2),
@@ -1154,14 +1215,47 @@ describe API::Issues do
end
end
- context 'when an admin or owner makes the request' do
- it 'accepts the creation date to be set' do
- creation_time = 2.weeks.ago
- post api("/projects/#{project.id}/issues", user),
- title: 'new issue', labels: 'label, label2', created_at: creation_time
+ context 'setting created_at' do
+ let(:creation_time) { 2.weeks.ago }
+ let(:params) { { title: 'new issue', labels: 'label, label2', created_at: creation_time } }
- expect(response).to have_gitlab_http_status(201)
- expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ context 'by an admin' do
+ it 'sets the creation time on the new issue' do
+ post api("/projects/#{project.id}/issues", admin), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ end
+ end
+
+ context 'by a project owner' do
+ it 'sets the creation time on the new issue' do
+ post api("/projects/#{project.id}/issues", user), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ end
+ end
+
+ context 'by a group owner' do
+ it 'sets the creation time on the new issue' do
+ group = create(:group)
+ group_project = create(:project, :public, namespace: group)
+ group.add_owner(user2)
+ post api("/projects/#{group_project.id}/issues", user2), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ end
+ end
+
+ context 'by another user' do
+ it 'ignores the given creation time' do
+ post api("/projects/#{project.id}/issues", user2), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(Time.parse(json_response['created_at'])).not_to be_like_time(creation_time)
+ end
end
end
@@ -1707,6 +1801,74 @@ describe API::Issues do
end
end
+ describe 'GET :id/issues/:issue_iid/related_merge_requests' do
+ def get_related_merge_requests(project_id, issue_iid, user = nil)
+ get api("/projects/#{project_id}/issues/#{issue_iid}/related_merge_requests", user)
+ end
+
+ def create_referencing_mr(user, project, issue)
+ attributes = {
+ author: user,
+ source_project: project,
+ target_project: project,
+ source_branch: "master",
+ target_branch: "test",
+ description: "See #{issue.to_reference}"
+ }
+ create(:merge_request, attributes).tap do |merge_request|
+ create(:note, :system, project: project, noteable: issue, author: user, note: merge_request.to_reference(full: true))
+ end
+ end
+
+ let!(:related_mr) { create_referencing_mr(user, project, issue) }
+
+ context 'when unauthenticated' do
+ it 'return list of referenced merge requests from issue' do
+ get_related_merge_requests(project.id, issue.iid)
+
+ expect_paginated_array_response(size: 1)
+ end
+
+ it 'renders 404 if project is not visible' do
+ private_project = create(:project, :private)
+ private_issue = create(:issue, project: private_project)
+ create_referencing_mr(user, private_project, private_issue)
+
+ get_related_merge_requests(private_project.id, private_issue.iid)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ it 'returns merge requests that mentioned a issue' do
+ create(:merge_request,
+ :simple,
+ author: user,
+ source_project: project,
+ target_project: project,
+ description: "Some description")
+
+ get_related_merge_requests(project.id, issue.iid, user)
+
+ expect_paginated_array_response(size: 1)
+ expect(json_response.first['id']).to eq(related_mr.id)
+ end
+
+ context 'no merge request mentioned a issue' do
+ it 'returns empty array' do
+ get_related_merge_requests(project.id, closed_issue.iid, user)
+
+ expect_paginated_array_response(size: 0)
+ end
+ end
+
+ it "returns 404 when issue doesn't exists" do
+ get_related_merge_requests(project.id, 999999, user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
describe "GET /projects/:id/issues/:issue_iid/user_agent_detail" do
let!(:user_agent_detail) { create(:user_agent_detail, subject: issue) }
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 5814d834572..8770365c893 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -3,6 +3,32 @@ require 'spec_helper'
describe API::Jobs do
include HttpIOHelpers
+ shared_examples 'a job with artifacts and trace' do |result_is_array: true|
+ context 'with artifacts and trace' do
+ let!(:second_job) { create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) }
+
+ it 'returns artifacts and trace data', :skip_before_request do
+ get api(api_endpoint, api_user)
+ json_job = result_is_array ? json_response.select { |job| job['id'] == second_job.id }.first : json_response
+
+ expect(json_job['artifacts_file']).not_to be_nil
+ expect(json_job['artifacts_file']).not_to be_empty
+ expect(json_job['artifacts_file']['filename']).to eq(second_job.artifacts_file.filename)
+ expect(json_job['artifacts_file']['size']).to eq(second_job.artifacts_file.size)
+ expect(json_job['artifacts']).not_to be_nil
+ expect(json_job['artifacts']).to be_an Array
+ expect(json_job['artifacts'].size).to eq(second_job.job_artifacts.length)
+ json_job['artifacts'].each do |artifact|
+ expect(artifact).not_to be_nil
+ file_type = Ci::JobArtifact.file_types[artifact['file_type']]
+ expect(artifact['size']).to eq(second_job.job_artifacts.where(file_type: file_type).first.size)
+ expect(artifact['filename']).to eq(second_job.job_artifacts.where(file_type: file_type).first.filename)
+ expect(artifact['file_format']).to eq(second_job.job_artifacts.where(file_type: file_type).first.file_format)
+ end
+ end
+ end
+ end
+
set(:project) do
create(:project, :repository, public_builds: false)
end
@@ -49,6 +75,20 @@ describe API::Jobs do
expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
end
+ context 'without artifacts and trace' do
+ it 'returns no artifacts nor trace data' do
+ json_job = json_response.first
+
+ expect(json_job['artifacts_file']).to be_nil
+ expect(json_job['artifacts']).to be_an Array
+ expect(json_job['artifacts']).to be_empty
+ end
+ end
+
+ it_behaves_like 'a job with artifacts and trace' do
+ let(:api_endpoint) { "/projects/#{project.id}/jobs" }
+ end
+
it 'returns pipeline data' do
json_job = json_response.first
@@ -60,7 +100,7 @@ describe API::Jobs do
end
it 'avoids N+1 queries', :skip_before_request do
- first_build = create(:ci_build, :artifacts, pipeline: pipeline)
+ first_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline)
first_build.runner = create(:ci_runner)
first_build.user = create(:user)
first_build.save
@@ -68,7 +108,7 @@ describe API::Jobs do
control_count = ActiveRecord::QueryRecorder.new { go }.count
second_pipeline = create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch)
- second_build = create(:ci_build, :artifacts, pipeline: second_pipeline)
+ second_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: second_pipeline)
second_build.runner = create(:ci_runner)
second_build.user = create(:user)
second_build.save
@@ -117,9 +157,11 @@ describe API::Jobs do
describe 'GET /projects/:id/pipelines/:pipeline_id/jobs' do
let(:query) { Hash.new }
- before do
- job
- get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
+ before do |example|
+ unless example.metadata[:skip_before_request]
+ job
+ get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
+ end
end
context 'authorized user' do
@@ -133,6 +175,13 @@ describe API::Jobs do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
+ expect(json_response.first['artifacts_file']).to be_nil
+ expect(json_response.first['artifacts']).to be_an Array
+ expect(json_response.first['artifacts']).to be_empty
+ end
+
+ it_behaves_like 'a job with artifacts and trace' do
+ let(:api_endpoint) { "/projects/#{project.id}/pipelines/#{pipeline.id}/jobs" }
end
it 'returns pipeline data' do
@@ -183,7 +232,7 @@ describe API::Jobs do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
end.count
- 3.times { create(:ci_build, :artifacts, pipeline: pipeline) }
+ 3.times { create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) }
expect do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
@@ -201,8 +250,10 @@ describe API::Jobs do
end
describe 'GET /projects/:id/jobs/:job_id' do
- before do
- get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
+ before do |example|
+ unless example.metadata[:skip_before_request]
+ get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
+ end
end
context 'authorized user' do
@@ -219,10 +270,17 @@ describe API::Jobs do
expect(Time.parse(json_response['started_at'])).to be_like_time(job.started_at)
expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at)
expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
+ expect(json_response['artifacts_file']).to be_nil
+ expect(json_response['artifacts']).to be_an Array
+ expect(json_response['artifacts']).to be_empty
expect(json_response['duration']).to eq(job.duration)
expect(json_response['web_url']).to be_present
end
+ it_behaves_like 'a job with artifacts and trace', result_is_array: false do
+ let(:api_endpoint) { "/projects/#{project.id}/jobs/#{second_job.id}" }
+ end
+
it 'returns pipeline data' do
json_job = json_response
@@ -663,7 +721,7 @@ describe API::Jobs do
expect(job.trace.exist?).to be_falsy
expect(job.artifacts_file.exists?).to be_falsy
expect(job.artifacts_metadata.exists?).to be_falsy
- expect(job.has_test_reports?).to be_falsy
+ expect(job.has_job_artifacts?).to be_falsy
end
it 'updates job' do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index a4220f5b2be..b8a4a04a7e4 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -306,16 +306,16 @@ describe API::Labels do
end
it 'returns 400 for too long color code' do
- post api("/projects/#{project.id}/labels", user),
- name: 'Foo',
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
color: '#FFAAFFFF'
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
it 'returns 400 for invalid priority' do
- post api("/projects/#{project.id}/labels", user),
- name: 'Foo',
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
priority: 'foo'
expect(response).to have_gitlab_http_status(400)
diff --git a/spec/requests/api/markdown_spec.rb b/spec/requests/api/markdown_spec.rb
index a55796cf343..e369c1435f0 100644
--- a/spec/requests/api/markdown_spec.rb
+++ b/spec/requests/api/markdown_spec.rb
@@ -106,6 +106,52 @@ describe API::Markdown do
.and include("#1</a>")
end
end
+
+ context 'with a public project and confidential issue' do
+ let(:public_project) { create(:project, :public) }
+ let(:confidential_issue) { create(:issue, :confidential, project: public_project, title: 'Confidential title') }
+
+ let(:text) { ":tada: Hello world! :100: #{confidential_issue.to_reference}" }
+ let(:params) { { text: text, gfm: true, project: public_project.full_path } }
+
+ shared_examples 'user without proper access' do
+ it 'does not render the title or link' do
+ expect(response).to have_http_status(201)
+ expect(json_response["html"]).not_to include('Confidential title')
+ expect(json_response["html"]).not_to include('<a href=')
+ expect(json_response["html"]).to include('Hello world!')
+ .and include('data-name="tada"')
+ .and include('data-name="100"')
+ .and include('#1</p>')
+ end
+ end
+
+ context 'when not logged in' do
+ let(:user) { }
+
+ it_behaves_like 'user without proper access'
+ end
+
+ context 'when logged in as user without access' do
+ let(:user) { create(:user) }
+
+ it_behaves_like 'user without proper access'
+ end
+
+ context 'when logged in as author' do
+ let(:user) { confidential_issue.author }
+
+ it 'renders the title or link' do
+ expect(response).to have_http_status(201)
+ expect(json_response["html"]).to include('Confidential title')
+ expect(json_response["html"]).to include('Hello world!')
+ .and include('data-name="tada"')
+ .and include('data-name="100"')
+ .and include("<a href=\"#{IssuesHelper.url_for_issue(confidential_issue.iid, public_project)}\"")
+ .and include("#1</a>")
+ end
+ end
+ end
end
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 4de834bf93a..e4e0ca285e0 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -81,6 +81,35 @@ describe API::MergeRequests do
let(:user2) { create(:user) }
it 'returns an array of all merge requests except unauthorized ones' do
+ get api('/merge_requests', user), scope: :all
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |mr| mr['id'] })
+ .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request_locked.id, merge_request2.id)
+ end
+
+ it "returns an array of no merge_requests when wip=yes" do
+ get api("/merge_requests", user), wip: 'yes'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
+ it "returns an array of no merge_requests when wip=no" do
+ get api("/merge_requests", user), wip: 'no'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |mr| mr['id'] })
+ .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request_locked.id, merge_request2.id)
+ end
+
+ it 'does not return unauthorized merge requests' do
private_project = create(:project, :private)
merge_request3 = create(:merge_request, :simple, source_project: private_project, target_project: private_project, source_branch: 'other-branch')
@@ -114,6 +143,23 @@ describe API::MergeRequests do
expect_response_ordered_exactly(merge_request3)
end
+ it 'returns an array of merge requests with no assignee' do
+ merge_request3 = create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, source_branch: 'other-branch')
+
+ get api('/merge_requests', user), assignee_id: 'None', scope: :all
+
+ expect_response_ordered_exactly(merge_request3)
+ end
+
+ it 'returns an array of merge requests with any assignee' do
+ # This MR with no assignee should not be returned
+ create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, source_branch: 'other-branch')
+
+ get api('/merge_requests', user), assignee_id: 'Any', scope: :all
+
+ expect_response_contain_exactly(merge_request, merge_request2, merge_request_closed, merge_request_merged, merge_request_locked)
+ end
+
it 'returns an array of merge requests assigned to me' do
merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch')
@@ -244,6 +290,15 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(404)
end
+ it "returns an array of no merge_requests when wip=yes" do
+ get api("/projects/#{project.id}/merge_requests", user), wip: 'yes'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
it 'returns merge_request by "iids" array' do
get api(endpoint_path, user), iids: [merge_request.iid, merge_request_closed.iid]
@@ -353,6 +408,15 @@ describe API::MergeRequests do
end
end
+ it 'returns the commits behind the target branch when include_diverged_commits_count is present' do
+ allow_any_instance_of(merge_request.class).to receive(:diverged_commits_count).and_return(1)
+
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), include_diverged_commits_count: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['diverged_commits_count']).to eq(1)
+ end
+
it "returns a 404 error if merge_request_iid not found" do
get api("/projects/#{project.id}/merge_requests/999", user)
expect(response).to have_gitlab_http_status(404)
diff --git a/spec/requests/api/pages/internal_access_spec.rb b/spec/requests/api/pages/internal_access_spec.rb
new file mode 100644
index 00000000000..c41eabe0a48
--- /dev/null
+++ b/spec/requests/api/pages/internal_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Internal Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :internal, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be internal" do
+ describe '#internal?' do
+ subject { project.internal? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 403
+ ProjectFeature::DISABLED | nil | 404
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 200
+ ProjectFeature::PUBLIC | nil | 404
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 200
+ ProjectFeature::ENABLED | nil | 404
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 403
+ ProjectFeature::PRIVATE | nil | 404
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/pages/private_access_spec.rb b/spec/requests/api/pages/private_access_spec.rb
new file mode 100644
index 00000000000..d69c15b0477
--- /dev/null
+++ b/spec/requests/api/pages/private_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Private Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :private, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be private" do
+ describe '#private?' do
+ subject { project.private? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 404
+ ProjectFeature::DISABLED | nil | 404
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 404
+ ProjectFeature::PUBLIC | nil | 404
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 404
+ ProjectFeature::ENABLED | nil | 404
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 404
+ ProjectFeature::PRIVATE | nil | 404
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/pages/public_access_spec.rb b/spec/requests/api/pages/public_access_spec.rb
new file mode 100644
index 00000000000..882ca26ac51
--- /dev/null
+++ b/spec/requests/api/pages/public_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Public Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :public, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be public" do
+ describe '#public?' do
+ subject { project.public? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 403
+ ProjectFeature::DISABLED | nil | 403
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 200
+ ProjectFeature::PUBLIC | nil | 200
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 200
+ ProjectFeature::ENABLED | nil | 200
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 403
+ ProjectFeature::PRIVATE | nil | 403
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index 342a97b6a69..638cc9767d4 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -370,12 +370,18 @@ describe API::Pipelines do
end
context 'without gitlab-ci.yml' do
- it 'fails to create pipeline' do
- post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch
+ context 'without auto devops enabled' do
+ before do
+ project.update!(auto_devops_attributes: { enabled: false })
+ end
- expect(response).to have_gitlab_http_status(400)
- expect(json_response['message']['base'].first).to eq 'Missing .gitlab-ci.yml file'
- expect(json_response).not_to be_an Array
+ it 'fails to create pipeline' do
+ post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']['base'].first).to eq 'Missing .gitlab-ci.yml file'
+ expect(json_response).not_to be_an Array
+ end
end
end
end
@@ -432,6 +438,67 @@ describe API::Pipelines do
end
end
+ describe 'DELETE /projects/:id/pipelines/:pipeline_id' do
+ context 'authorized user' do
+ let(:owner) { project.owner }
+
+ it 'destroys the pipeline' do
+ delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", owner)
+
+ expect(response).to have_gitlab_http_status(204)
+ expect { pipeline.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'returns 404 when it does not exist' do
+ delete api("/projects/#{project.id}/pipelines/123456", owner)
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq '404 Not found'
+ end
+
+ it 'logs an audit event' do
+ expect { delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", owner) }.to change { SecurityEvent.count }.by(1)
+ end
+
+ context 'when the pipeline has jobs' do
+ let!(:build) { create(:ci_build, project: project, pipeline: pipeline) }
+
+ it 'destroys associated jobs' do
+ delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", owner)
+
+ expect(response).to have_gitlab_http_status(204)
+ expect { build.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
+
+ context 'unauthorized user' do
+ context 'when user is not member' do
+ it 'should return a 404' do
+ delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", non_member)
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq '404 Project Not Found'
+ end
+ end
+
+ context 'when user is developer' do
+ let(:developer) { create(:user) }
+
+ before do
+ project.add_developer(developer)
+ end
+
+ it 'should return a 403' do
+ delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", developer)
+
+ expect(response).to have_gitlab_http_status(403)
+ expect(json_response['message']).to eq '403 Forbidden'
+ end
+ end
+ end
+ end
+
describe 'POST /projects/:id/pipelines/:pipeline_id/retry' do
context 'authorized user' do
let!(:pipeline) do
diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb
index 45e4e35d773..0586025956f 100644
--- a/spec/requests/api/project_export_spec.rb
+++ b/spec/requests/api/project_export_spec.rb
@@ -4,8 +4,8 @@ describe API::ProjectExport do
set(:project) { create(:project) }
set(:project_none) { create(:project) }
set(:project_started) { create(:project) }
- set(:project_finished) { create(:project) }
- set(:project_after_export) { create(:project) }
+ let(:project_finished) { create(:project, :with_export) }
+ let(:project_after_export) { create(:project, :with_export) }
set(:user) { create(:user) }
set(:admin) { create(:admin) }
@@ -29,13 +29,7 @@ describe API::ProjectExport do
# simulate exporting work directory
FileUtils.mkdir_p File.join(project_started.export_path, 'securerandom-hex')
- # simulate exported
- FileUtils.mkdir_p project_finished.export_path
- FileUtils.touch File.join(project_finished.export_path, '_export.tar.gz')
-
# simulate in after export action
- FileUtils.mkdir_p project_after_export.export_path
- FileUtils.touch File.join(project_after_export.export_path, '_export.tar.gz')
FileUtils.touch Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy.lock_file_path(project_after_export)
end
@@ -191,14 +185,11 @@ describe API::ProjectExport do
context 'when upload complete' do
before do
- FileUtils.rm_rf(project_after_export.export_path)
-
- if project_after_export.export_project_object_exists?
- upload = project_after_export.import_export_upload
+ project_after_export.remove_exports
+ end
- upload.remove_export_file!
- upload.save
- end
+ it 'has removed the export' do
+ expect(project_after_export.export_file_exists?).to be_falsey
end
it_behaves_like '404 response' do
@@ -273,13 +264,13 @@ describe API::ProjectExport do
before do
stub_uploads_object_storage(ImportExportUploader)
- [project, project_finished, project_after_export].each do |p|
- p.add_maintainer(user)
+ project.add_maintainer(user)
+ project_finished.add_maintainer(user)
+ project_after_export.add_maintainer(user)
- upload = ImportExportUpload.new(project: p)
- upload.export_file = fixture_file_upload('spec/fixtures/project_export.tar.gz', "`/tar.gz")
- upload.save!
- end
+ upload = ImportExportUpload.new(project: project)
+ upload.export_file = fixture_file_upload('spec/fixtures/project_export.tar.gz', "`/tar.gz")
+ upload.save!
end
it_behaves_like 'get project download by strategy'
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index bc45a63d9f1..87997a48dc9 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -9,7 +9,8 @@ describe API::ProjectHooks, 'ProjectHooks' do
:all_events_enabled,
project: project,
url: 'http://example.com',
- enable_ssl_verification: true)
+ enable_ssl_verification: true,
+ push_events_branch_filter: 'master')
end
before do
@@ -38,6 +39,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(json_response.first['pipeline_events']).to eq(true)
expect(json_response.first['wiki_page_events']).to eq(true)
expect(json_response.first['enable_ssl_verification']).to eq(true)
+ expect(json_response.first['push_events_branch_filter']).to eq('master')
end
end
@@ -83,11 +85,6 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(response).to have_gitlab_http_status(403)
end
end
-
- it "returns a 404 error if hook id is not available" do
- get api("/projects/#{project.id}/hooks/1234", user)
- expect(response).to have_gitlab_http_status(404)
- end
end
describe "POST /projects/:id/hooks" do
@@ -95,7 +92,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect do
post api("/projects/#{project.id}/hooks", user),
url: "http://example.com", issues_events: true, confidential_issues_events: true, wiki_page_events: true,
- job_events: true
+ job_events: true, push_events_branch_filter: 'some-feature-branch'
end.to change {project.hooks.count}.by(1)
expect(response).to have_gitlab_http_status(201)
@@ -111,6 +108,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(json_response['pipeline_events']).to eq(false)
expect(json_response['wiki_page_events']).to eq(true)
expect(json_response['enable_ssl_verification']).to eq(true)
+ expect(json_response['push_events_branch_filter']).to eq('some-feature-branch')
expect(json_response).not_to include('token')
end
@@ -137,7 +135,12 @@ describe API::ProjectHooks, 'ProjectHooks' do
end
it "returns a 422 error if url not valid" do
- post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"
+ post api("/projects/#{project.id}/hooks", user), url: "ftp://example.com"
+ expect(response).to have_gitlab_http_status(422)
+ end
+
+ it "returns a 422 error if branch filter is not valid" do
+ post api("/projects/#{project.id}/hooks", user), url: "http://example.com", push_events_branch_filter: '~badbranchname/'
expect(response).to have_gitlab_http_status(422)
end
end
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index e3fb6cecce9..c8fa4754810 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -7,7 +7,6 @@ describe API::ProjectImport do
let(:namespace) { create(:group) }
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
- stub_feature_flags(import_export_object_storage: true)
stub_uploads_object_storage(FileUploader)
namespace.add_owner(user)
@@ -42,7 +41,7 @@ describe API::ProjectImport do
expect(response).to have_gitlab_http_status(201)
end
- it 'does not shedule an import for a nampespace that does not exist' do
+ it 'does not schedule an import for a namespace that does not exist' do
expect_any_instance_of(Project).not_to receive(:import_schedule)
expect(::Projects::CreateService).not_to receive(:new)
diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb
index 6c05c166bd6..62613aa5938 100644
--- a/spec/requests/api/project_milestones_spec.rb
+++ b/spec/requests/api/project_milestones_spec.rb
@@ -39,19 +39,6 @@ describe API::ProjectMilestones do
expect(response).to have_gitlab_http_status(404)
end
-
- it "rejects a member with reporter access from deleting a milestone" do
- delete api("/projects/#{project.id}/milestones/#{milestone.id}", reporter)
-
- expect(response).to have_gitlab_http_status(403)
- end
-
- it 'deletes the milestone when the user has developer access to the project' do
- delete api("/projects/#{project.id}/milestones/#{milestone.id}", user)
-
- expect(project.milestones.find_by_id(milestone.id)).to be_nil
- expect(response).to have_gitlab_http_status(204)
- end
end
describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index a3b5e8c6223..5dec0bc778c 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -116,6 +116,14 @@ describe API::ProjectSnippets do
expect(response).to have_gitlab_http_status(400)
end
+ it 'returns 400 for empty code field' do
+ params[:code] = ''
+
+ post api("/projects/#{project.id}/snippets/", admin), params
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
context 'when the snippet is spam' do
def create_snippet(project, snippet_params = {})
project.add_developer(user)
@@ -180,6 +188,14 @@ describe API::ProjectSnippets do
expect(response).to have_gitlab_http_status(400)
end
+ it 'returns 400 for empty code field' do
+ new_content = ''
+
+ put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
context 'when the snippet is spam' do
def update_snippet(snippet_params = {})
put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}", admin), snippet_params
diff --git a/spec/requests/api/project_templates_spec.rb b/spec/requests/api/project_templates_spec.rb
new file mode 100644
index 00000000000..86e33f23951
--- /dev/null
+++ b/spec/requests/api/project_templates_spec.rb
@@ -0,0 +1,145 @@
+require 'spec_helper'
+
+describe API::ProjectTemplates do
+ let(:public_project) { create(:project, :public) }
+ let(:private_project) { create(:project, :private) }
+ let(:developer) { create(:user) }
+
+ before do
+ private_project.add_developer(developer)
+ end
+
+ describe 'GET /projects/:id/templates/:type' do
+ it 'returns dockerfiles' do
+ get api("/projects/#{public_project.id}/templates/dockerfiles")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response).to satisfy_one { |template| template['key'] == 'Binary' }
+ end
+
+ it 'returns gitignores' do
+ get api("/projects/#{public_project.id}/templates/gitignores")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response).to satisfy_one { |template| template['key'] == 'Actionscript' }
+ end
+
+ it 'returns gitlab_ci_ymls' do
+ get api("/projects/#{public_project.id}/templates/gitlab_ci_ymls")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response).to satisfy_one { |template| template['key'] == 'Android' }
+ end
+
+ it 'returns licenses' do
+ get api("/projects/#{public_project.id}/templates/licenses")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response).to satisfy_one { |template| template['key'] == 'mit' }
+ end
+
+ it 'returns 400 for an unknown template type' do
+ get api("/projects/#{public_project.id}/templates/unknown")
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'denies access to an anonymous user on a private project' do
+ get api("/projects/#{private_project.id}/templates/licenses")
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'permits access to a developer on a private project' do
+ get api("/projects/#{private_project.id}/templates/licenses", developer)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ end
+ end
+
+ describe 'GET /projects/:id/templates/licenses' do
+ it 'returns key and name for the listed licenses' do
+ get api("/projects/#{public_project.id}/templates/licenses")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ end
+ end
+
+ describe 'GET /projects/:id/templates/:type/:key' do
+ it 'returns a specific dockerfile' do
+ get api("/projects/#{public_project.id}/templates/dockerfiles/Binary")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('Binary')
+ end
+
+ it 'returns a specific gitignore' do
+ get api("/projects/#{public_project.id}/templates/gitignores/Actionscript")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('Actionscript')
+ end
+
+ it 'returns a specific gitlab_ci_yml' do
+ get api("/projects/#{public_project.id}/templates/gitlab_ci_ymls/Android")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('Android')
+ end
+
+ it 'returns a specific license' do
+ get api("/projects/#{public_project.id}/templates/licenses/mit")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/license')
+ end
+
+ it 'returns 404 for an unknown specific template' do
+ get api("/projects/#{public_project.id}/templates/licenses/unknown")
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'denies access to an anonymous user on a private project' do
+ get api("/projects/#{private_project.id}/templates/licenses/mit")
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'permits access to a developer on a private project' do
+ get api("/projects/#{private_project.id}/templates/licenses/mit", developer)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/license')
+ end
+ end
+
+ describe 'GET /projects/:id/templates/licenses/:key' do
+ it 'fills placeholders in the license' do
+ get api("/projects/#{public_project.id}/templates/licenses/agpl-3.0"),
+ project: 'Project Placeholder',
+ fullname: 'Fullname Placeholder'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/license')
+
+ content = json_response['content']
+
+ expect(content).to include('Project Placeholder')
+ expect(content).to include("Copyright (C) #{Time.now.year} Fullname Placeholder")
+ end
+ end
+end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index eb41750bf47..62b6a3ce42e 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -20,7 +20,6 @@ describe API::Projects do
let(:admin) { create(:admin) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
let(:project2) { create(:project, namespace: user.namespace) }
- let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
let(:project3) do
@@ -149,6 +148,16 @@ describe API::Projects do
expect(json_response.first.keys).to include('open_issues_count')
end
+ it 'does not include projects marked for deletion' do
+ project.update(pending_delete: true)
+
+ get api('/projects', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.map { |p| p['id'] }).not_to include(project.id)
+ end
+
it 'does not include open_issues_count if issues are disabled' do
project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
@@ -191,6 +200,24 @@ describe API::Projects do
expect(json_response.first).to include 'statistics'
end
+ it "does not include license by default" do
+ get api('/projects', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('license', 'license_url')
+ end
+
+ it "does not include license if requested" do
+ get api('/projects', user), license: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('license', 'license_url')
+ end
+
context 'when external issue tracker is enabled' do
let!(:jira_service) { create(:jira_service, project: project) }
@@ -403,7 +430,7 @@ describe API::Projects do
context 'and with min_access_level' do
before do
- project2.add_master(user2)
+ project2.add_maintainer(user2)
project3.add_developer(user2)
project4.add_reporter(user2)
end
@@ -558,6 +585,14 @@ describe API::Projects do
expect(json_response['visibility']).to eq('private')
end
+ it 'creates a new project initialized with a README.md' do
+ project = attributes_for(:project, initialize_with_readme: 1, name: 'somewhere')
+
+ post api('/projects', user), project
+
+ expect(json_response['readme_url']).to eql("#{Gitlab.config.gitlab.url}/#{json_response['namespace']['full_path']}/somewhere/blob/master/README.md")
+ end
+
it 'sets tag list to a project' do
project = attributes_for(:project, tag_list: %w[tagFirst tagSecond])
@@ -575,7 +610,7 @@ describe API::Projects do
expect(json_response['avatar_url']).to eq("http://localhost/uploads/-/system/project/avatar/#{project_id}/banana_sample.gif")
end
- it 'sets a project as allowing outdated diff discussions to automatically resolve' do
+ it 'sets a project as not allowing outdated diff discussions to automatically resolve' do
project = attributes_for(:project, resolve_outdated_diff_discussions: false)
post api('/projects', user), project
@@ -583,7 +618,7 @@ describe API::Projects do
expect(json_response['resolve_outdated_diff_discussions']).to be_falsey
end
- it 'sets a project as allowing outdated diff discussions to automatically resolve if resolve_outdated_diff_discussions' do
+ it 'sets a project as allowing outdated diff discussions to automatically resolve' do
project = attributes_for(:project, resolve_outdated_diff_discussions: true)
post api('/projects', user), project
@@ -698,7 +733,7 @@ describe API::Projects do
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
end
- it 'returns projects filetered by minimal access level' do
+ it 'returns projects filtered by minimal access level' do
private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace)
private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace)
private_project1.add_developer(user2)
@@ -789,7 +824,7 @@ describe API::Projects do
expect(json_response['visibility']).to eq('private')
end
- it 'sets a project as allowing outdated diff discussions to automatically resolve' do
+ it 'sets a project as not allowing outdated diff discussions to automatically resolve' do
project = attributes_for(:project, resolve_outdated_diff_discussions: false)
post api("/projects/user/#{user.id}", admin), project
@@ -914,12 +949,28 @@ describe API::Projects do
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
+ expect(json_response['shared_with_groups'][0]['expires_at']).to be_nil
expect(json_response['only_allow_merge_if_pipeline_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
expect(json_response['merge_method']).to eq(project.merge_method.to_s)
expect(json_response['readme_url']).to eq(project.readme_url)
end
+ it 'returns a group link with expiration date' do
+ group = create(:group)
+ expires_at = 5.days.from_now.to_date
+ link = create(:project_group_link, project: project, group: group, expires_at: expires_at)
+
+ get api("/projects/#{project.id}", user)
+
+ expect(json_response['shared_with_groups']).to be_an Array
+ expect(json_response['shared_with_groups'].length).to eq(1)
+ expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
+ expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
+ expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
+ expect(json_response['shared_with_groups'][0]['expires_at']).to eq(expires_at.to_s)
+ end
+
it 'returns a project by path name' do
get api("/projects/#{project.id}", user)
expect(response).to have_gitlab_http_status(200)
@@ -961,6 +1012,26 @@ describe API::Projects do
})
end
+ it "does not include license fields by default" do
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).not_to include('license', 'license_url')
+ end
+
+ it 'includes license fields when requested' do
+ get api("/projects/#{project.id}", user), license: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['license']).to eq({
+ 'key' => project.repository.license.key,
+ 'name' => project.repository.license.name,
+ 'nickname' => project.repository.license.nickname,
+ 'html_url' => project.repository.license.url,
+ 'source_url' => project.repository.license.meta['source']
+ })
+ end
+
it "does not include statistics by default" do
get api("/projects/#{project.id}", user)
@@ -989,6 +1060,15 @@ describe API::Projects do
expect(json_response).not_to include("import_error")
end
+ it 'returns 404 when project is marked for deletion' do
+ project.update(pending_delete: true)
+
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
+
context 'links exposure' do
it 'exposes related resources full URIs' do
get api("/projects/#{project.id}", user)
@@ -1119,144 +1199,90 @@ describe API::Projects do
end
end
- describe 'GET /projects/:id/snippets' do
- before do
- snippet
- end
-
- it 'returns an array of project snippets' do
- get api("/projects/#{project.id}/snippets", user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.first['title']).to eq(snippet.title)
- end
- end
-
- describe 'GET /projects/:id/snippets/:snippet_id' do
- it 'returns a project snippet' do
- get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['title']).to eq(snippet.title)
- end
+ describe 'fork management' do
+ let(:project_fork_target) { create(:project) }
+ let(:project_fork_source) { create(:project, :public) }
+ let(:private_project_fork_source) { create(:project, :private) }
- it 'returns a 404 error if snippet id not found' do
- get api("/projects/#{project.id}/snippets/1234", user)
- expect(response).to have_gitlab_http_status(404)
- end
- end
+ describe 'POST /projects/:id/fork/:forked_from_id' do
+ context 'user is a developer' do
+ before do
+ project_fork_target.add_developer(user)
+ end
- describe 'POST /projects/:id/snippets' do
- it 'creates a new project snippet' do
- post api("/projects/#{project.id}/snippets", user),
- title: 'api test', file_name: 'sample.rb', code: 'test', visibility: 'private'
- expect(response).to have_gitlab_http_status(201)
- expect(json_response['title']).to eq('api test')
- end
+ it 'denies project to be forked from an existing project' do
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
- it 'returns a 400 error if invalid snippet is given' do
- post api("/projects/#{project.id}/snippets", user)
- expect(status).to eq(400)
- end
- end
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
- describe 'PUT /projects/:id/snippets/:snippet_id' do
- it 'updates an existing project snippet' do
- put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
- code: 'updated code'
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['title']).to eq('example')
- expect(snippet.reload.content).to eq('updated code')
- end
+ it 'refreshes the forks count cache' do
+ expect(project_fork_source.forks_count).to be_zero
+ end
- it 'updates an existing project snippet with new title' do
- put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
- title: 'other api test'
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['title']).to eq('other api test')
- end
- end
+ context 'user is maintainer' do
+ before do
+ project_fork_target.add_maintainer(user)
+ end
- describe 'DELETE /projects/:id/snippets/:snippet_id' do
- before do
- snippet
- end
+ it 'allows project to be forked from an existing project' do
+ expect(project_fork_target).not_to be_forked
- it 'deletes existing project snippet' do
- expect do
- delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
+ project_fork_target.reload
- expect(response).to have_gitlab_http_status(204)
- end.to change { Snippet.count }.by(-1)
- end
+ expect(response).to have_gitlab_http_status(201)
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target.fork_network_member).to be_present
+ expect(project_fork_target).to be_forked
+ end
- it 'returns 404 when deleting unknown snippet id' do
- delete api("/projects/#{project.id}/snippets/1234", user)
- expect(response).to have_gitlab_http_status(404)
- end
+ it 'denies project to be forked from a private project' do
+ post api("/projects/#{project_fork_target.id}/fork/#{private_project_fork_source.id}", user)
- it_behaves_like '412 response' do
- let(:request) { api("/projects/#{project.id}/snippets/#{snippet.id}", user) }
- end
- end
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
- describe 'GET /projects/:id/snippets/:snippet_id/raw' do
- it 'gets a raw project snippet' do
- get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
- expect(response).to have_gitlab_http_status(200)
- end
+ context 'user is admin' do
+ it 'allows project to be forked from an existing project' do
+ expect(project_fork_target).not_to be_forked
- it 'returns a 404 error if raw project snippet not found' do
- get api("/projects/#{project.id}/snippets/5555/raw", user)
- expect(response).to have_gitlab_http_status(404)
- end
- end
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
- describe 'fork management' do
- let(:project_fork_target) { create(:project) }
- let(:project_fork_source) { create(:project, :public) }
+ expect(response).to have_gitlab_http_status(201)
+ end
- describe 'POST /projects/:id/fork/:forked_from_id' do
- let(:new_project_fork_source) { create(:project, :public) }
+ it 'allows project to be forked from a private project' do
+ post api("/projects/#{project_fork_target.id}/fork/#{private_project_fork_source.id}", admin)
- it "is not available for non admin users" do
- post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
- expect(response).to have_gitlab_http_status(403)
- end
+ expect(response).to have_gitlab_http_status(201)
+ end
- it 'allows project to be forked from an existing project' do
- expect(project_fork_target.forked?).not_to be_truthy
- post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
- expect(response).to have_gitlab_http_status(201)
- project_fork_target.reload
- expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
- expect(project_fork_target.forked_project_link).not_to be_nil
- expect(project_fork_target.forked?).to be_truthy
- end
+ it 'refreshes the forks count cachce' do
+ expect do
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ end.to change(project_fork_source, :forks_count).by(1)
+ end
- it 'refreshes the forks count cachce' do
- expect(project_fork_source.forks_count).to be_zero
+ it 'fails if forked_from project which does not exist' do
+ post api("/projects/#{project_fork_target.id}/fork/9999", admin)
+ expect(response).to have_gitlab_http_status(404)
+ end
- post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ it 'fails with 409 if already forked' do
+ other_project_fork_source = create(:project, :public)
- expect(project_fork_source.forks_count).to eq(1)
- end
+ Projects::ForkService.new(project_fork_source, admin).execute(project_fork_target)
- it 'fails if forked_from project which does not exist' do
- post api("/projects/#{project_fork_target.id}/fork/9999", admin)
- expect(response).to have_gitlab_http_status(404)
- end
+ post api("/projects/#{project_fork_target.id}/fork/#{other_project_fork_source.id}", admin)
+ project_fork_target.reload
- it 'fails with 409 if already forked' do
- post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
- project_fork_target.reload
- expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
- post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
- expect(response).to have_gitlab_http_status(409)
- project_fork_target.reload
- expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
- expect(project_fork_target.forked?).to be_truthy
+ expect(response).to have_gitlab_http_status(409)
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target).to be_forked
+ end
end
end
@@ -1278,8 +1304,8 @@ describe API::Projects do
before do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
project_fork_target.reload
- expect(project_fork_target.forked_from_project).not_to be_nil
- expect(project_fork_target.forked?).to be_truthy
+ expect(project_fork_target.forked_from_project).to be_present
+ expect(project_fork_target).to be_forked
end
it 'makes forked project unforked' do
@@ -1288,7 +1314,7 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(204)
project_fork_target.reload
expect(project_fork_target.forked_from_project).to be_nil
- expect(project_fork_target.forked?).not_to be_truthy
+ expect(project_fork_target).not_to be_forked
end
it_behaves_like '412 response' do
@@ -1323,8 +1349,8 @@ describe API::Projects do
before do
post api("/projects/#{private_fork.id}/fork/#{project_fork_source.id}", admin)
private_fork.reload
- expect(private_fork.forked_from_project).not_to be_nil
- expect(private_fork.forked?).to be_truthy
+ expect(private_fork.forked_from_project).to be_present
+ expect(private_fork).to be_forked
project_fork_source.reload
expect(project_fork_source.forks.length).to eq(1)
expect(project_fork_source.forks).to include(private_fork)
diff --git a/spec/requests/api/protected_tags_spec.rb b/spec/requests/api/protected_tags_spec.rb
new file mode 100644
index 00000000000..f4f3ef31bc3
--- /dev/null
+++ b/spec/requests/api/protected_tags_spec.rb
@@ -0,0 +1,202 @@
+require 'spec_helper'
+
+describe API::ProtectedTags do
+ let(:user) { create(:user) }
+ let!(:project) { create(:project, :repository) }
+ let(:project2) { create(:project, path: 'project2', namespace: user.namespace) }
+ let(:protected_name) { 'feature' }
+ let(:tag_name) { protected_name }
+ let!(:protected_tag) do
+ create(:protected_tag, project: project, name: protected_name)
+ end
+
+ describe 'GET /projects/:id/protected_tags' do
+ let(:route) { "/projects/#{project.id}/protected_tags" }
+
+ shared_examples_for 'protected tags' do
+ it 'returns the protected tags' do
+ get api(route, user), per_page: 100
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+
+ protected_tag_names = json_response.map { |x| x['name'] }
+ expected_tags_names = project.protected_tags.map { |x| x['name'] }
+ expect(protected_tag_names).to match_array(expected_tags_names)
+ end
+ end
+
+ context 'when authenticated as a maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it_behaves_like 'protected tags'
+ end
+
+ context 'when authenticated as a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, user) }
+ end
+ end
+ end
+
+ describe 'GET /projects/:id/protected_tags/:tag' do
+ let(:route) { "/projects/#{project.id}/protected_tags/#{tag_name}" }
+
+ shared_examples_for 'protected tag' do
+ it 'returns the protected tag' do
+ get api(route, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['name']).to eq(tag_name)
+ expect(json_response['create_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MAINTAINER)
+ end
+
+ context 'when protected tag does not exist' do
+ let(:tag_name) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, user) }
+ let(:message) { '404 Not found' }
+ end
+ end
+ end
+
+ context 'when authenticated as a maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it_behaves_like 'protected tag'
+
+ context 'when protected tag contains a wildcard' do
+ let(:protected_name) { 'feature*' }
+
+ it_behaves_like 'protected tag'
+ end
+ end
+
+ context 'when authenticated as a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, user) }
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/protected_tags' do
+ let(:tag_name) { 'new_tag' }
+
+ context 'when authenticated as a maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'protects a single tag with maintainers can create tags' do
+ post api("/projects/#{project.id}/protected_tags", user), name: tag_name
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(tag_name)
+ expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
+ end
+
+ it 'protects a single tag with developers can create tags' do
+ post api("/projects/#{project.id}/protected_tags", user),
+ name: tag_name, create_access_level: 30
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(tag_name)
+ expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
+ end
+
+ it 'protects a single tag with no one can create tags' do
+ post api("/projects/#{project.id}/protected_tags", user),
+ name: tag_name, create_access_level: 0
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(tag_name)
+ expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
+ end
+
+ it 'returns a 422 error if the same tag is protected twice' do
+ post api("/projects/#{project.id}/protected_tags", user), name: protected_name
+
+ expect(response).to have_gitlab_http_status(422)
+ expect(json_response['message'][0]).to eq('Name has already been taken')
+ end
+
+ it 'returns 201 if the same tag is proteted on different projects' do
+ post api("/projects/#{project.id}/protected_tags", user), name: protected_name
+ post api("/projects/#{project2.id}/protected_tags", user), name: protected_name
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(protected_name)
+ end
+
+ context 'when tag has a wildcard in its name' do
+ let(:tag_name) { 'feature/*' }
+
+ it 'protects multiple tags with a wildcard in the name' do
+ post api("/projects/#{project.id}/protected_tags", user), name: tag_name
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(tag_name)
+ expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
+ end
+ end
+ end
+
+ context 'when authenticated as a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'returns a 403 error if guest' do
+ post api("/projects/#{project.id}/protected_tags/", user), name: tag_name
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+ end
+
+ describe 'DELETE /projects/:id/protected_tags/unprotect/:tag' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'unprotects a single tag' do
+ delete api("/projects/#{project.id}/protected_tags/#{tag_name}", user)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+
+ it_behaves_like '412 response' do
+ let(:request) { api("/projects/#{project.id}/protected_tags/#{tag_name}", user) }
+ end
+
+ it "returns 404 if tag does not exist" do
+ delete api("/projects/#{project.id}/protected_tags/barfoo", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ context 'when tag has a wildcard in its name' do
+ let(:protected_name) { 'feature*' }
+
+ it 'unprotects a wildcard tag' do
+ delete api("/projects/#{project.id}/protected_tags/#{tag_name}", user)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/redacted_events_spec.rb b/spec/requests/api/redacted_events_spec.rb
new file mode 100644
index 00000000000..086dd3df9ba
--- /dev/null
+++ b/spec/requests/api/redacted_events_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe 'Redacted events in API::Events' do
+ shared_examples 'private events are redacted' do
+ it 'redacts events the user does not have access to' do
+ expect_any_instance_of(Event).to receive(:visible_to_user?).and_call_original
+
+ get api(path), user
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to contain_exactly(
+ 'project_id' => nil,
+ 'action_name' => nil,
+ 'target_id' => nil,
+ 'target_iid' => nil,
+ 'target_type' => nil,
+ 'author_id' => nil,
+ 'target_title' => 'Confidential event',
+ 'created_at' => nil,
+ 'author_username' => nil
+ )
+ end
+ end
+
+ describe '/users/:id/events' do
+ let(:project) { create(:project, :public) }
+ let(:path) { "/users/#{project.owner.id}/events" }
+ let(:issue) { create(:issue, :confidential, project: project) }
+
+ before do
+ EventCreateService.new.open_issue(issue, issue.author)
+ end
+
+ context 'unauthenticated user views another user with private events' do
+ let(:user) { nil }
+
+ include_examples 'private events are redacted'
+ end
+
+ context 'authenticated user without access views another user with private events' do
+ let(:user) { create(:user) }
+
+ include_examples 'private events are redacted'
+ end
+ end
+
+ describe '/projects/:id/events' do
+ let(:project) { create(:project, :public) }
+ let(:path) { "/projects/#{project.id}/events" }
+ let(:issue) { create(:issue, :confidential, project: project) }
+
+ before do
+ EventCreateService.new.open_issue(issue, issue.author)
+ end
+
+ context 'unauthenticated user views public project' do
+ let(:user) { nil }
+
+ include_examples 'private events are redacted'
+ end
+
+ context 'authenticated user without access views public project' do
+ let(:user) { create(:user) }
+
+ include_examples 'private events are redacted'
+ end
+ end
+end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 519638ebb82..fa38751fe58 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -468,7 +468,7 @@ describe API::Repositories do
describe 'GET :id/repository/merge_base' do
let(:refs) do
- %w(304d257dcb821665ab5110318fc58a007bd104ed 0031876facac3f2b2702a0e53a26e89939a42209)
+ %w(304d257dcb821665ab5110318fc58a007bd104ed 0031876facac3f2b2702a0e53a26e89939a42209 570e7b2abdd848b95f2f578043fc23bd6f6fd24d)
end
subject(:request) do
@@ -534,7 +534,7 @@ describe API::Repositories do
request
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq('Provide exactly 2 refs')
+ expect(json_response['message']).to eq('Provide at least 2 refs')
end
end
end
diff --git a/spec/requests/api/resource_label_events_spec.rb b/spec/requests/api/resource_label_events_spec.rb
new file mode 100644
index 00000000000..b7d4a5152cc
--- /dev/null
+++ b/spec/requests/api/resource_label_events_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe API::ResourceLabelEvents do
+ set(:user) { create(:user) }
+ set(:project) { create(:project, :public, :repository, namespace: user.namespace) }
+ set(:private_user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ shared_examples 'resource_label_events API' do |parent_type, eventable_type, id_name|
+ describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_label_events" do
+ it "returns an array of resource label events" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.first['id']).to eq(event.id)
+ end
+
+ it "returns a 404 error when eventable id not found" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/12345/resource_label_events", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it "returns 404 when not authorized" do
+ parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events", private_user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_label_events/:event_id" do
+ it "returns a resource label event by id" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events/#{event.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq(event.id)
+ end
+
+ it "returns a 404 error if resource label event not found" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events/12345", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
+ context 'when eventable is an Issue' do
+ let(:issue) { create(:issue, project: project, author: user) }
+
+ it_behaves_like 'resource_label_events API', 'projects', 'issues', 'iid' do
+ let(:parent) { project }
+ let(:eventable) { issue }
+ let!(:event) { create(:resource_label_event, issue: issue) }
+ end
+ end
+
+ context 'when eventable is a Merge Request' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
+
+ it_behaves_like 'resource_label_events API', 'projects', 'merge_requests', 'iid' do
+ let(:parent) { project }
+ let(:eventable) { merge_request }
+ let!(:event) { create(:resource_label_event, merge_request: merge_request) }
+ end
+ end
+end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 43ceb332cfb..909703a8d47 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -797,6 +797,24 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
it { expect(job).to be_runner_system_failure }
end
+
+ context 'when failure_reason is unrecognized value' do
+ before do
+ update_job(state: 'failed', failure_reason: 'what_is_this')
+ job.reload
+ end
+
+ it { expect(job).to be_unknown_failure }
+ end
+
+ context 'when failure_reason is job_execution_timeout' do
+ before do
+ update_job(state: 'failed', failure_reason: 'job_execution_timeout')
+ job.reload
+ end
+
+ it { expect(job).to be_job_execution_timeout }
+ end
end
context 'when trace is given' do
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index 3ebdb54f71f..49a79d2ccf9 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -25,36 +25,71 @@ describe API::Runners do
describe 'GET /runners' do
context 'authorized user' do
- it 'returns user available runners' do
+ it 'returns response status and headers' do
get api('/runners', user)
- shared = json_response.any? { |r| r['is_shared'] }
- descriptions = json_response.map { |runner| runner['description'] }
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response[0]).to have_key('ip_address')
- expect(descriptions).to contain_exactly(
- 'Project runner', 'Two projects runner', 'Group runner'
- )
- expect(shared).to be_falsey
+ end
+
+ it 'returns user available runners' do
+ get api('/runners', user)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Project runner'),
+ a_hash_including('description' => 'Two projects runner'),
+ a_hash_including('description' => 'Group runner')
+ ]
end
it 'filters runners by scope' do
- get api('/runners?scope=active', user)
+ create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
+
+ get api('/runners?scope=paused', user)
- shared = json_response.any? { |r| r['is_shared'] }
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response[0]).to have_key('ip_address')
- expect(shared).to be_falsey
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Inactive project runner')
+ ]
end
it 'avoids filtering if scope is invalid' do
get api('/runners?scope=unknown', user)
expect(response).to have_gitlab_http_status(400)
end
+
+ it 'filters runners by type' do
+ get api('/runners?type=project_type', user)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Project runner'),
+ a_hash_including('description' => 'Two projects runner')
+ ]
+ end
+
+ it 'does not filter by invalid type' do
+ get api('/runners?type=bogus', user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'filters runners by status' do
+ create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
+
+ get api('/runners?status=paused', user)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Inactive project runner')
+ ]
+ end
+
+ it 'does not filter by invalid status' do
+ get api('/runners?status=bogus', user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
end
context 'unauthorized user' do
@@ -69,51 +104,91 @@ describe API::Runners do
describe 'GET /runners/all' do
context 'authorized user' do
context 'with admin privileges' do
+ it 'returns response status and headers' do
+ get api('/runners/all', admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ end
+
it 'returns all runners' do
get api('/runners/all', admin)
- shared = json_response.any? { |r| r['is_shared'] }
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Project runner'),
+ a_hash_including('description' => 'Two projects runner'),
+ a_hash_including('description' => 'Group runner'),
+ a_hash_including('description' => 'Shared runner')
+ ]
+ end
+
+ it 'filters runners by scope' do
+ get api('/runners/all?scope=shared', admin)
+
+ shared = json_response.all? { |r| r['is_shared'] }
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response[0]).to have_key('ip_address')
expect(shared).to be_truthy
end
- end
- context 'without admin privileges' do
- it 'does not return runners list' do
- get api('/runners/all', user)
+ it 'filters runners by scope' do
+ get api('/runners/all?scope=specific', admin)
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Project runner'),
+ a_hash_including('description' => 'Two projects runner'),
+ a_hash_including('description' => 'Group runner')
+ ]
end
- end
- it 'filters runners by scope' do
- get api('/runners/all?scope=shared', admin)
+ it 'avoids filtering if scope is invalid' do
+ get api('/runners/all?scope=unknown', admin)
+ expect(response).to have_gitlab_http_status(400)
+ end
- shared = json_response.all? { |r| r['is_shared'] }
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response[0]).to have_key('ip_address')
- expect(shared).to be_truthy
- end
+ it 'filters runners by type' do
+ get api('/runners/all?type=project_type', admin)
- it 'filters runners by scope' do
- get api('/runners/all?scope=specific', admin)
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Project runner'),
+ a_hash_including('description' => 'Two projects runner')
+ ]
+ end
- shared = json_response.any? { |r| r['is_shared'] }
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response[0]).to have_key('ip_address')
- expect(shared).to be_falsey
+ it 'does not filter by invalid type' do
+ get api('/runners/all?type=bogus', admin)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'filters runners by status' do
+ create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
+
+ get api('/runners/all?status=paused', admin)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Inactive project runner')
+ ]
+ end
+
+ it 'does not filter by invalid status' do
+ get api('/runners/all?status=bogus', admin)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
end
- it 'avoids filtering if scope is invalid' do
- get api('/runners?scope=unknown', admin)
- expect(response).to have_gitlab_http_status(400)
+ context 'without admin privileges' do
+ it 'does not return runners list' do
+ get api('/runners/all', user)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
end
end
@@ -577,15 +652,69 @@ describe API::Runners do
describe 'GET /projects/:id/runners' do
context 'authorized user with maintainer privileges' do
- it "returns project's runners" do
+ it 'returns response status and headers' do
+ get api('/runners/all', admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ end
+
+ it 'returns all runners' do
get api("/projects/#{project.id}/runners", user)
- shared = json_response.any? { |r| r['is_shared'] }
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Project runner'),
+ a_hash_including('description' => 'Two projects runner'),
+ a_hash_including('description' => 'Shared runner')
+ ]
+ end
+
+ it 'filters runners by scope' do
+ get api("/projects/#{project.id}/runners?scope=specific", user)
+
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response[0]).to have_key('ip_address')
- expect(shared).to be_truthy
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Project runner'),
+ a_hash_including('description' => 'Two projects runner')
+ ]
+ end
+
+ it 'avoids filtering if scope is invalid' do
+ get api("/projects/#{project.id}/runners?scope=unknown", user)
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'filters runners by type' do
+ get api("/projects/#{project.id}/runners?type=project_type", user)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Project runner'),
+ a_hash_including('description' => 'Two projects runner')
+ ]
+ end
+
+ it 'does not filter by invalid type' do
+ get api("/projects/#{project.id}/runners?type=bogus", user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'filters runners by status' do
+ create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
+
+ get api("/projects/#{project.id}/runners?status=paused", user)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Inactive project runner')
+ ]
+ end
+
+ it 'does not filter by invalid status' do
+ get api("/projects/#{project.id}/runners?status=bogus", user)
+
+ expect(response).to have_gitlab_http_status(400)
end
end
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 3e0f47b84a1..84c7210f6bb 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -2,18 +2,17 @@ require 'spec_helper'
describe API::Settings, 'Settings' do
let(:user) { create(:user) }
- let(:admin) { create(:admin) }
+ set(:admin) { create(:admin) }
describe "GET /application/settings" do
it "returns application settings" do
get api("/application/settings", admin)
+
expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Hash
expect(json_response['default_projects_limit']).to eq(42)
expect(json_response['password_authentication_enabled_for_web']).to be_truthy
expect(json_response['repository_storages']).to eq(['default'])
- expect(json_response['koding_enabled']).to be_falsey
- expect(json_response['koding_url']).to be_nil
expect(json_response['plantuml_enabled']).to be_falsey
expect(json_response['plantuml_url']).to be_nil
expect(json_response['default_project_visibility']).to be_a String
@@ -23,7 +22,6 @@ describe API::Settings, 'Settings' do
expect(json_response['dsa_key_restriction']).to eq(0)
expect(json_response['ecdsa_key_restriction']).to eq(0)
expect(json_response['ed25519_key_restriction']).to eq(0)
- expect(json_response['circuitbreaker_failure_count_threshold']).not_to be_nil
expect(json_response['performance_bar_allowed_group_id']).to be_nil
expect(json_response['instance_statistics_visibility_private']).to be(false)
expect(json_response).not_to have_key('performance_bar_allowed_group_path')
@@ -47,8 +45,6 @@ describe API::Settings, 'Settings' do
default_projects_limit: 3,
password_authentication_enabled_for_web: false,
repository_storages: ['custom'],
- koding_enabled: true,
- koding_url: 'http://koding.example.com',
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
default_snippet_visibility: 'internal',
@@ -62,18 +58,16 @@ describe API::Settings, 'Settings' do
dsa_key_restriction: 2048,
ecdsa_key_restriction: 384,
ed25519_key_restriction: 256,
- circuitbreaker_check_interval: 2,
enforce_terms: true,
terms: 'Hello world!',
performance_bar_allowed_group_path: group.full_path,
- instance_statistics_visibility_private: true
+ instance_statistics_visibility_private: true,
+ diff_max_patch_bytes: 150_000
expect(response).to have_gitlab_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
expect(json_response['password_authentication_enabled_for_web']).to be_falsey
expect(json_response['repository_storages']).to eq(['custom'])
- expect(json_response['koding_enabled']).to be_truthy
- expect(json_response['koding_url']).to eq('http://koding.example.com')
expect(json_response['plantuml_enabled']).to be_truthy
expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
expect(json_response['default_snippet_visibility']).to eq('internal')
@@ -87,11 +81,11 @@ describe API::Settings, 'Settings' do
expect(json_response['dsa_key_restriction']).to eq(2048)
expect(json_response['ecdsa_key_restriction']).to eq(384)
expect(json_response['ed25519_key_restriction']).to eq(256)
- expect(json_response['circuitbreaker_check_interval']).to eq(2)
expect(json_response['enforce_terms']).to be(true)
expect(json_response['terms']).to eq('Hello world!')
expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
expect(json_response['instance_statistics_visibility_private']).to be(true)
+ expect(json_response['diff_max_patch_bytes']).to eq(150_000)
end
end
@@ -112,15 +106,6 @@ describe API::Settings, 'Settings' do
expect(json_response['performance_bar_allowed_group_id']).to be_nil
end
- context "missing koding_url value when koding_enabled is true" do
- it "returns a blank parameter error message" do
- put api("/application/settings", admin), koding_enabled: true
-
- expect(response).to have_gitlab_http_status(400)
- expect(json_response['error']).to eq('koding_url is missing')
- end
- end
-
context "missing plantuml_url value when plantuml_enabled is true" do
it "returns a blank parameter error message" do
put api("/application/settings", admin), plantuml_enabled: true
diff --git a/spec/requests/api/submodules_spec.rb b/spec/requests/api/submodules_spec.rb
new file mode 100644
index 00000000000..fa447c028c2
--- /dev/null
+++ b/spec/requests/api/submodules_spec.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe API::Submodules do
+ let(:user) { create(:user) }
+ let!(:project) { create(:project, :repository, namespace: user.namespace ) }
+ let(:guest) { create(:user) { |u| project.add_guest(u) } }
+ let(:submodule) { 'six' }
+ let(:commit_sha) { 'e25eda1fece24ac7a03624ed1320f82396f35bd8' }
+ let(:branch) { 'master' }
+ let(:commit_message) { 'whatever' }
+
+ let(:params) do
+ {
+ submodule: submodule,
+ commit_sha: commit_sha,
+ branch: branch,
+ commit_message: commit_message
+ }
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ def route(submodule = nil)
+ "/projects/#{project.id}/repository/submodules/#{submodule}"
+ end
+
+ describe "PUT /projects/:id/repository/submodule/:submodule" do
+ context 'when unauthenticated' do
+ it 'returns 401' do
+ put api(route(submodule)), params
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+
+ context 'when authenticated', 'as a guest' do
+ it 'returns 403' do
+ put api(route(submodule), guest), params
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ context 'when authenticated', 'as a developer' do
+ it 'returns 400 if params is missing' do
+ put api(route(submodule), user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'returns 400 if branch is missing' do
+ put api(route(submodule), user), params.except(:branch)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'returns 400 if commit_sha is missing' do
+ put api(route(submodule), user), params.except(:commit_sha)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'returns the commmit' do
+ head_commit = project.repository.commit.id
+
+ put api(route(submodule), user), params
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['message']).to eq commit_message
+ expect(json_response['author_name']).to eq user.name
+ expect(json_response['committer_name']).to eq user.name
+ expect(json_response['parent_ids'].first).to eq head_commit
+ end
+
+ context 'when the submodule name is urlencoded' do
+ let(:submodule) { 'test_inside_folder/another_folder/six' }
+ let(:branch) { 'submodule_inside_folder' }
+ let(:encoded_submodule) { CGI.escape(submodule) }
+
+ it 'returns the commmit' do
+ expect(Submodules::UpdateService)
+ .to receive(:new)
+ .with(any_args, hash_including(submodule: submodule))
+ .and_call_original
+
+ put api(route(encoded_submodule), user), params
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq project.repository.commit(branch).id
+ expect(project.repository.blob_at(branch, submodule).id).to eq commit_sha
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb
index 6bb53fdc98d..d1e16ab9ca9 100644
--- a/spec/requests/api/templates_spec.rb
+++ b/spec/requests/api/templates_spec.rb
@@ -56,6 +56,8 @@ describe API::Templates do
end
it 'returns a license template' do
+ expect(response).to have_gitlab_http_status(200)
+
expect(json_response['key']).to eq('mit')
expect(json_response['name']).to eq('MIT License')
expect(json_response['nickname']).to be_nil
@@ -181,6 +183,7 @@ describe API::Templates do
it 'replaces the copyright owner placeholder with the name of the current user' do
get api('/templates/licenses/mit', user)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['content']).to include("Copyright (c) #{Time.now.year} #{user.name}")
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index d48d577afa1..bb913ae0e79 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -51,6 +51,15 @@ describe API::Users do
expect(json_response[0]['username']).to eq(user.username)
end
+ it "returns the user when a valid `username` parameter is passed (case insensitive)" do
+ get api("/users"), username: user.username.upcase
+
+ expect(response).to match_response_schema('public_api/v4/user/basics')
+ expect(json_response.size).to eq(1)
+ expect(json_response[0]['id']).to eq(user.id)
+ expect(json_response[0]['username']).to eq(user.username)
+ end
+
it "returns an empty response when an invalid `username` parameter is passed" do
get api("/users"), username: 'invalid'
@@ -132,6 +141,14 @@ describe API::Users do
expect(json_response.first['username']).to eq(omniauth_user.username)
end
+ it "returns one user (case insensitive)" do
+ get api("/users?username=#{omniauth_user.username.upcase}", user)
+
+ expect(response).to match_response_schema('public_api/v4/user/basics')
+ expect(response).to include_pagination_headers
+ expect(json_response.first['username']).to eq(omniauth_user.username)
+ end
+
it "returns a 403 when non-admin user searches by external UID" do
get api("/users?extern_uid=#{omniauth_user.identities.first.extern_uid}&provider=#{omniauth_user.identities.first.provider}", user)
@@ -343,6 +360,12 @@ describe API::Users do
let(:path) { "/users/#{user.username}/status" }
end
end
+
+ context 'when finding the user by username (case insensitive)' do
+ it_behaves_like 'rendering user status' do
+ let(:path) { "/users/#{user.username.upcase}/status" }
+ end
+ end
end
describe "POST /users" do
@@ -528,6 +551,18 @@ describe API::Users do
expect(json_response['message']).to eq('Username has already been taken')
end
+ it 'returns 409 conflict error if same username exists (case insensitive)' do
+ expect do
+ post api('/users', admin),
+ name: 'foo',
+ email: 'foo@example.com',
+ password: 'password',
+ username: 'TEST'
+ end.to change { User.count }.by(0)
+ expect(response).to have_gitlab_http_status(409)
+ expect(json_response['message']).to eq('Username has already been taken')
+ end
+
it 'creates user with new identity' do
post api("/users", admin), attributes_for(:user, provider: 'github', extern_uid: '67890')
@@ -749,6 +784,14 @@ describe API::Users do
expect(response).to have_gitlab_http_status(409)
expect(@user.reload.username).to eq(@user.username)
end
+
+ it 'returns 409 conflict error if username taken (case insensitive)' do
+ @user_id = User.all.last.id
+ put api("/users/#{@user.id}", admin), username: 'TEST'
+
+ expect(response).to have_gitlab_http_status(409)
+ expect(@user.reload.username).to eq(@user.username)
+ end
end
end
@@ -785,35 +828,25 @@ describe API::Users do
end
describe 'GET /user/:id/keys' do
- before do
- admin
- end
+ it 'returns 404 for non-existing user' do
+ user_id = not_existing_user_id
- context 'when unauthenticated' do
- it 'returns authentication error' do
- get api("/users/#{user.id}/keys")
- expect(response).to have_gitlab_http_status(401)
- end
- end
+ get api("/users/#{user_id}/keys")
- context 'when authenticated' do
- it 'returns 404 for non-existing user' do
- get api('/users/999999/keys', admin)
- expect(response).to have_gitlab_http_status(404)
- expect(json_response['message']).to eq('404 User Not Found')
- end
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
- it 'returns array of ssh keys' do
- user.keys << key
- user.save
+ it 'returns array of ssh keys' do
+ user.keys << key
+ user.save
- get api("/users/#{user.id}/keys", admin)
+ get api("/users/#{user.id}/keys")
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.first['title']).to eq(key.title)
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(key.title)
end
end
@@ -1031,11 +1064,14 @@ describe API::Users do
expect(json_response['error']).to eq('email is missing')
end
- it "creates email" do
+ it "creates unverified email" do
email_attrs = attributes_for :email
expect do
post api("/users/#{user.id}/emails", admin), email_attrs
end.to change { user.emails.count }.by(1)
+
+ email = Email.find_by(user_id: user.id, email: email_attrs[:email])
+ expect(email).not_to be_confirmed
end
it "returns a 400 for invalid ID" do
@@ -1043,6 +1079,18 @@ describe API::Users do
expect(response).to have_gitlab_http_status(400)
end
+
+ it "creates verified email" do
+ email_attrs = attributes_for :email
+ email_attrs[:skip_confirmation] = true
+
+ post api("/users/#{user.id}/emails", admin), email_attrs
+
+ expect(response).to have_gitlab_http_status(201)
+
+ email = Email.find_by(user_id: user.id, email: email_attrs[:email])
+ expect(email).to be_confirmed
+ end
end
describe 'GET /user/:id/emails' do
@@ -1970,11 +2018,11 @@ describe API::Users do
expect(json_response['message']).to eq('403 Forbidden')
end
- it 'returns a personal access token' do
+ it 'returns an impersonation token' do
get api("/users/#{user.id}/impersonation_tokens/#{impersonation_token.id}", admin)
expect(response).to have_gitlab_http_status(200)
- expect(json_response['token']).to be_present
+ expect(json_response['token']).not_to be_present
expect(json_response['impersonation']).to be_truthy
end
end
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index 489cb001b82..08bada44178 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -139,6 +139,37 @@ describe API::Wikis do
end
end
+ shared_examples_for 'uploads wiki attachment' do
+ it 'pushes attachment to the wiki repository' do
+ allow(SecureRandom).to receive(:hex).and_return('fixed_hex')
+
+ post(api(url, user), payload)
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response).to eq result_hash.deep_stringify_keys
+ end
+
+ it 'responds with validation error on empty file' do
+ payload.delete(:file)
+
+ post(api(url, user), payload)
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response.size).to eq(1)
+ expect(json_response['error']).to eq('file is missing')
+ end
+
+ it 'responds with validation error on invalid temp file' do
+ payload[:file] = { tempfile: '/etc/hosts' }
+
+ post(api(url, user), payload)
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response.size).to eq(1)
+ expect(json_response['error']).to eq('file is invalid')
+ end
+ end
+
describe 'GET /projects/:id/wikis' do
let(:url) { "/projects/#{project.id}/wikis" }
@@ -698,4 +729,107 @@ describe API::Wikis do
include_examples '204 No Content'
end
end
+
+ describe 'POST /projects/:id/wikis/attachments' do
+ let(:payload) { { file: fixture_file_upload('spec/fixtures/dk.png') } }
+ let(:url) { "/projects/#{project.id}/wikis/attachments" }
+ let(:file_path) { "#{Wikis::CreateAttachmentService::ATTACHMENT_PATH}/fixed_hex/dk.png" }
+ let(:result_hash) do
+ {
+ file_name: 'dk.png',
+ file_path: file_path,
+ branch: 'master',
+ link: {
+ url: file_path,
+ markdown: "![dk](#{file_path})"
+ }
+ }
+ end
+
+ context 'when wiki is disabled' do
+ let(:project) { create(:project, :wiki_disabled, :wiki_repo) }
+
+ context 'when user is guest' do
+ before do
+ post(api(url), payload)
+ end
+
+ include_examples '404 Project Not Found'
+ end
+
+ context 'when user is developer' do
+ before do
+ project.add_developer(user)
+ post(api(url, user), payload)
+ end
+
+ include_examples '403 Forbidden'
+ end
+
+ context 'when user is maintainer' do
+ before do
+ project.add_maintainer(user)
+ post(api(url, user), payload)
+ end
+
+ include_examples '403 Forbidden'
+ end
+ end
+
+ context 'when wiki is available only for team members' do
+ let(:project) { create(:project, :wiki_private, :wiki_repo) }
+
+ context 'when user is guest' do
+ before do
+ post(api(url), payload)
+ end
+
+ include_examples '404 Project Not Found'
+ end
+
+ context 'when user is developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ include_examples 'uploads wiki attachment'
+ end
+
+ context 'when user is maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ include_examples 'uploads wiki attachment'
+ end
+ end
+
+ context 'when wiki is available for everyone with access' do
+ let(:project) { create(:project, :wiki_repo) }
+
+ context 'when user is guest' do
+ before do
+ post(api(url), payload)
+ end
+
+ include_examples '404 Project Not Found'
+ end
+
+ context 'when user is developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ include_examples 'uploads wiki attachment'
+ end
+
+ context 'when user is maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ include_examples 'uploads wiki attachment'
+ end
+ end
+ end
end
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index b14d4b8fb6e..b1cf7a531f4 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -121,7 +121,7 @@ describe 'OpenID Connect requests' do
expect(@payload).to match(a_hash_including(id_token_claims))
end
- it 'includes the Gitlab root URL' do
+ it 'includes the GitLab root URL' do
expect(@payload['iss']).to eq Gitlab.config.gitlab.url
end
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index 98df5f787f7..77baaef7afd 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -103,11 +103,11 @@ describe Admin::HooksController, "routing" do
end
end
-# admin_hook_hook_log_retry GET /admin/hooks/:hook_id/hook_logs/:id/retry(.:format) admin/hook_logs#retry
+# admin_hook_hook_log_retry POST /admin/hooks/:hook_id/hook_logs/:id/retry(.:format) admin/hook_logs#retry
# admin_hook_hook_log GET /admin/hooks/:hook_id/hook_logs/:id(.:format) admin/hook_logs#show
describe Admin::HookLogsController, 'routing' do
it 'to #retry' do
- expect(get('/admin/hooks/1/hook_logs/1/retry')).to route_to('admin/hook_logs#retry', hook_id: '1', id: '1')
+ expect(post('/admin/hooks/1/hook_logs/1/retry')).to route_to('admin/hook_logs#retry', hook_id: '1', id: '1')
end
it 'to #show' do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 70a7707826e..bdfb12dc5df 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -133,8 +133,9 @@ describe 'project routing' do
# labels_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/labels(.:format) projects/autocomplete_sources#labels
# milestones_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/milestones(.:format) projects/autocomplete_sources#milestones
# commands_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/commands(.:format) projects/autocomplete_sources#commands
+ # snippets_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/snippets(.:format) projects/autocomplete_sources#snippets
describe Projects::AutocompleteSourcesController, 'routing' do
- [:members, :issues, :merge_requests, :labels, :milestones, :commands].each do |action|
+ [:members, :issues, :merge_requests, :labels, :milestones, :commands, :snippets].each do |action|
it "to ##{action}" do
expect(get("/gitlab/gitlabhq/autocomplete_sources/#{action}")).to route_to("projects/autocomplete_sources##{action}", namespace_id: 'gitlab', project_id: 'gitlabhq')
end
@@ -258,10 +259,10 @@ describe 'project routing' do
end
it 'to #logs_tree' do
- expect(get('/gitlab/gitlabhq/refs/stable/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable')
- expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
- expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
- expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
+ expect(get('/gitlab/gitlabhq/refs/stable/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable')
+ expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
+ expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
+ expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
@@ -381,7 +382,7 @@ describe 'project routing' do
end
end
- # test_project_hook GET /:project_id/hooks/:id/test(.:format) hooks#test
+ # test_project_hook POST /:project_id/hooks/:id/test(.:format) hooks#test
# project_hooks GET /:project_id/hooks(.:format) hooks#index
# POST /:project_id/hooks(.:format) hooks#create
# edit_project_hook GET /:project_id/hooks/:id/edit(.:format) hooks#edit
@@ -398,11 +399,11 @@ describe 'project routing' do
end
end
- # retry_namespace_project_hook_hook_log GET /:project_id/hooks/:hook_id/hook_logs/:id/retry(.:format) projects/hook_logs#retry
+ # retry_namespace_project_hook_hook_log POST /:project_id/hooks/:hook_id/hook_logs/:id/retry(.:format) projects/hook_logs#retry
# namespace_project_hook_hook_log GET /:project_id/hooks/:hook_id/hook_logs/:id(.:format) projects/hook_logs#show
describe Projects::HookLogsController, 'routing' do
it 'to #retry' do
- expect(get('/gitlab/gitlabhq/hooks/1/hook_logs/1/retry')).to route_to('projects/hook_logs#retry', namespace_id: 'gitlab', project_id: 'gitlabhq', hook_id: '1', id: '1')
+ expect(post('/gitlab/gitlabhq/hooks/1/hook_logs/1/retry')).to route_to('projects/hook_logs#retry', namespace_id: 'gitlab', project_id: 'gitlabhq', hook_id: '1', id: '1')
end
it 'to #show' do
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index dd8f6239587..a170bb14144 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -140,13 +140,6 @@ describe HelpController, "routing" do
end
end
-# koding GET /koding(.:format) koding#index
-describe KodingController, "routing" do
- it "to #index" do
- expect(get("/koding")).to route_to('koding#index')
- end
-end
-
# profile_account GET /profile/account(.:format) profile#account
# profile_history GET /profile/history(.:format) profile#history
# profile_password PUT /profile/password(.:format) profile#password_update
diff --git a/spec/rubocop/code_reuse_helpers_spec.rb b/spec/rubocop/code_reuse_helpers_spec.rb
new file mode 100644
index 00000000000..2720141aad2
--- /dev/null
+++ b/spec/rubocop/code_reuse_helpers_spec.rb
@@ -0,0 +1,249 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'parser/current'
+require_relative '../../rubocop/code_reuse_helpers'
+
+describe RuboCop::CodeReuseHelpers do
+ def parse_source(source, path = 'foo.rb')
+ buffer = Parser::Source::Buffer.new(path)
+ buffer.source = source
+
+ builder = RuboCop::AST::Builder.new
+ parser = Parser::CurrentRuby.new(builder)
+
+ parser.parse(buffer)
+ end
+
+ let(:cop) do
+ Class.new do
+ include RuboCop::CodeReuseHelpers
+ end.new
+ end
+
+ describe '#send_to_constant?' do
+ it 'returns true when sending to a constant' do
+ node = parse_source('Foo.bar')
+
+ expect(cop.send_to_constant?(node)).to eq(true)
+ end
+
+ it 'returns false when sending to something other than a constant' do
+ node = parse_source('10')
+
+ expect(cop.send_to_constant?(node)).to eq(false)
+ end
+ end
+
+ describe '#send_receiver_name_ends_with?' do
+ it 'returns true when the receiver ends with a suffix' do
+ node = parse_source('FooFinder.new')
+
+ expect(cop.send_receiver_name_ends_with?(node, 'Finder')).to eq(true)
+ end
+
+ it 'returns false when the receiver is the same as a suffix' do
+ node = parse_source('Finder.new')
+
+ expect(cop.send_receiver_name_ends_with?(node, 'Finder')).to eq(false)
+ end
+ end
+
+ describe '#file_path_for_node' do
+ it 'returns the file path of a node' do
+ node = parse_source('10')
+ path = cop.file_path_for_node(node)
+
+ expect(path).to eq('foo.rb')
+ end
+ end
+
+ describe '#name_of_constant' do
+ it 'returns the name of a constant' do
+ node = parse_source('Foo')
+
+ expect(cop.name_of_constant(node)).to eq(:Foo)
+ end
+ end
+
+ describe '#in_finder?' do
+ it 'returns true for a node in the finders directory' do
+ node = parse_source('10', Rails.root.join('app', 'finders', 'foo.rb'))
+
+ expect(cop.in_finder?(node)).to eq(true)
+ end
+
+ it 'returns false for a node outside the finders directory' do
+ node = parse_source('10', Rails.root.join('app', 'foo', 'foo.rb'))
+
+ expect(cop.in_finder?(node)).to eq(false)
+ end
+ end
+
+ describe '#in_model?' do
+ it 'returns true for a node in the models directory' do
+ node = parse_source('10', Rails.root.join('app', 'models', 'foo.rb'))
+
+ expect(cop.in_model?(node)).to eq(true)
+ end
+
+ it 'returns false for a node outside the models directory' do
+ node = parse_source('10', Rails.root.join('app', 'foo', 'foo.rb'))
+
+ expect(cop.in_model?(node)).to eq(false)
+ end
+ end
+
+ describe '#in_service_class?' do
+ it 'returns true for a node in the services directory' do
+ node = parse_source('10', Rails.root.join('app', 'services', 'foo.rb'))
+
+ expect(cop.in_service_class?(node)).to eq(true)
+ end
+
+ it 'returns false for a node outside the services directory' do
+ node = parse_source('10', Rails.root.join('app', 'foo', 'foo.rb'))
+
+ expect(cop.in_service_class?(node)).to eq(false)
+ end
+ end
+
+ describe '#in_presenter?' do
+ it 'returns true for a node in the presenters directory' do
+ node = parse_source('10', Rails.root.join('app', 'presenters', 'foo.rb'))
+
+ expect(cop.in_presenter?(node)).to eq(true)
+ end
+
+ it 'returns false for a node outside the presenters directory' do
+ node = parse_source('10', Rails.root.join('app', 'foo', 'foo.rb'))
+
+ expect(cop.in_presenter?(node)).to eq(false)
+ end
+ end
+
+ describe '#in_serializer?' do
+ it 'returns true for a node in the serializers directory' do
+ node = parse_source('10', Rails.root.join('app', 'serializers', 'foo.rb'))
+
+ expect(cop.in_serializer?(node)).to eq(true)
+ end
+
+ it 'returns false for a node outside the serializers directory' do
+ node = parse_source('10', Rails.root.join('app', 'foo', 'foo.rb'))
+
+ expect(cop.in_serializer?(node)).to eq(false)
+ end
+ end
+
+ describe '#in_worker?' do
+ it 'returns true for a node in the workers directory' do
+ node = parse_source('10', Rails.root.join('app', 'workers', 'foo.rb'))
+
+ expect(cop.in_worker?(node)).to eq(true)
+ end
+
+ it 'returns false for a node outside the workers directory' do
+ node = parse_source('10', Rails.root.join('app', 'foo', 'foo.rb'))
+
+ expect(cop.in_worker?(node)).to eq(false)
+ end
+ end
+
+ describe '#in_api?' do
+ it 'returns true for a node in the API directory' do
+ node = parse_source('10', Rails.root.join('lib', 'api', 'foo.rb'))
+
+ expect(cop.in_api?(node)).to eq(true)
+ end
+
+ it 'returns false for a node outside the API directory' do
+ node = parse_source('10', Rails.root.join('lib', 'foo', 'foo.rb'))
+
+ expect(cop.in_api?(node)).to eq(false)
+ end
+ end
+
+ describe '#in_directory?' do
+ it 'returns true for a directory in the CE app/ directory' do
+ node = parse_source('10', Rails.root.join('app', 'models', 'foo.rb'))
+
+ expect(cop.in_directory?(node, 'models')).to eq(true)
+ end
+
+ it 'returns true for a directory in the EE app/ directory' do
+ node =
+ parse_source('10', Rails.root.join('ee', 'app', 'models', 'foo.rb'))
+
+ expect(cop.in_directory?(node, 'models')).to eq(true)
+ end
+
+ it 'returns false for a directory in the lib/ directory' do
+ node =
+ parse_source('10', Rails.root.join('lib', 'models', 'foo.rb'))
+
+ expect(cop.in_directory?(node, 'models')).to eq(false)
+ end
+ end
+
+ describe '#name_of_receiver' do
+ it 'returns the name of a send receiver' do
+ node = parse_source('Foo.bar')
+
+ expect(cop.name_of_receiver(node)).to eq('Foo')
+ end
+ end
+
+ describe '#each_class_method' do
+ it 'yields every class method to the supplied block' do
+ node = parse_source(<<~RUBY)
+ class Foo
+ class << self
+ def first
+ end
+ end
+
+ def self.second
+ end
+ end
+ RUBY
+
+ nodes = cop.each_class_method(node).to_a
+
+ expect(nodes.length).to eq(2)
+
+ expect(nodes[0].children[0]).to eq(:first)
+ expect(nodes[1].children[1]).to eq(:second)
+ end
+ end
+
+ describe '#each_send_node' do
+ it 'yields every send node to the supplied block' do
+ node = parse_source("foo\nbar")
+ nodes = cop.each_send_node(node).to_a
+
+ expect(nodes.length).to eq(2)
+ expect(nodes[0].children[1]).to eq(:foo)
+ expect(nodes[1].children[1]).to eq(:bar)
+ end
+ end
+
+ describe '#disallow_send_to' do
+ it 'disallows sending a message to a constant' do
+ def_node = parse_source(<<~RUBY)
+ def foo
+ FooFinder.new
+ end
+ RUBY
+
+ send_node = def_node.each_child_node(:send).first
+
+ expect(cop)
+ .to receive(:add_offense)
+ .with(send_node, location: :expression, message: 'oops')
+
+ cop.disallow_send_to(def_node, 'Finder', 'oops')
+ end
+ end
+end
diff --git a/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb b/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb
new file mode 100644
index 00000000000..c9eb61ccc72
--- /dev/null
+++ b/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require_relative '../../../rubocop/cop/avoid_route_redirect_leading_slash'
+
+describe RuboCop::Cop::AvoidRouteRedirectLeadingSlash do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ before do
+ allow(cop).to receive(:in_routes?).and_return(true)
+ end
+
+ it 'registers an offense when redirect has a leading slash' do
+ expect_offense(<<~PATTERN.strip_indent)
+ root to: redirect("/-/route")
+ ^^^^^^^^^^^^^^^^^^^^ Do not use a leading "/" in route redirects
+ PATTERN
+ end
+
+ it 'does not register an offense when redirect does not have a leading slash' do
+ expect_no_offenses(<<~PATTERN.strip_indent)
+ root to: redirect("-/route")
+ PATTERN
+ end
+
+ it 'autocorrect `/-/route` to `-/route`' do
+ expect(autocorrect_source('redirect("/-/route")')).to eq('redirect("-/route")')
+ end
+end
diff --git a/spec/rubocop/cop/code_reuse/active_record_spec.rb b/spec/rubocop/cop/code_reuse/active_record_spec.rb
new file mode 100644
index 00000000000..a30fc52d26f
--- /dev/null
+++ b/spec/rubocop/cop/code_reuse/active_record_spec.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/code_reuse/active_record'
+
+describe RuboCop::Cop::CodeReuse::ActiveRecord do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of "where" without any arguments' do
+ expect_offense(<<~SOURCE)
+ def foo
+ User.where
+ ^^^^^ This method can only be used inside an ActiveRecord model
+ end
+ SOURCE
+ end
+
+ it 'flags the use of "where" with arguments' do
+ expect_offense(<<~SOURCE)
+ def foo
+ User.where(id: 10)
+ ^^^^^ This method can only be used inside an ActiveRecord model
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of "group" without any arguments' do
+ expect_no_offenses(<<~SOURCE)
+ def foo
+ project.group
+ end
+ SOURCE
+ end
+
+ it 'flags the use of "group" with arguments' do
+ expect_offense(<<~SOURCE)
+ def foo
+ project.group(:name)
+ ^^^^^ This method can only be used inside an ActiveRecord model
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of ActiveRecord models in a model' do
+ path = Rails.root.join('app', 'models', 'foo.rb').to_s
+
+ expect_no_offenses(<<~SOURCE, path)
+ def foo
+ project.group(:name)
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of ActiveRecord models in a spec' do
+ path = Rails.root.join('spec', 'foo_spec.rb').to_s
+
+ expect_no_offenses(<<~SOURCE, path)
+ def foo
+ project.group(:name)
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of ActiveRecord models in a background migration' do
+ path = Rails
+ .root
+ .join('lib', 'gitlab', 'background_migration', 'foo.rb')
+ .to_s
+
+ expect_no_offenses(<<~SOURCE, path)
+ def foo
+ project.group(:name)
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of ActiveRecord models in lib/gitlab/database' do
+ path = Rails.root.join('lib', 'gitlab', 'database', 'foo.rb').to_s
+
+ expect_no_offenses(<<~SOURCE, path)
+ def foo
+ project.group(:name)
+ end
+ SOURCE
+ end
+
+ it 'autocorrects offenses in instance methods by whitelisting them' do
+ corrected = autocorrect_source(<<~SOURCE)
+ def foo
+ User.where
+ end
+ SOURCE
+
+ expect(corrected).to eq(<<~SOURCE)
+ # rubocop: disable CodeReuse/ActiveRecord
+ def foo
+ User.where
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ SOURCE
+ end
+
+ it 'autocorrects offenses in class methods by whitelisting them' do
+ corrected = autocorrect_source(<<~SOURCE)
+ def self.foo
+ User.where
+ end
+ SOURCE
+
+ expect(corrected).to eq(<<~SOURCE)
+ # rubocop: disable CodeReuse/ActiveRecord
+ def self.foo
+ User.where
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ SOURCE
+ end
+
+ it 'autocorrects offenses in blocks by whitelisting them' do
+ corrected = autocorrect_source(<<~SOURCE)
+ get '/' do
+ User.where
+ end
+ SOURCE
+
+ expect(corrected).to eq(<<~SOURCE)
+ # rubocop: disable CodeReuse/ActiveRecord
+ get '/' do
+ User.where
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ SOURCE
+ end
+end
diff --git a/spec/rubocop/cop/code_reuse/finder_spec.rb b/spec/rubocop/cop/code_reuse/finder_spec.rb
new file mode 100644
index 00000000000..b04e053a4c3
--- /dev/null
+++ b/spec/rubocop/cop/code_reuse/finder_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/code_reuse/finder'
+
+describe RuboCop::Cop::CodeReuse::Finder do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of a Finder inside another Finder' do
+ allow(cop)
+ .to receive(:in_finder?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooFinder
+ def execute
+ BarFinder.new.execute
+ ^^^^^^^^^^^^^ Finders can not be used inside a Finder.
+ end
+ end
+ SOURCE
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'flags the use of a Finder inside a model class method' do
+ allow(cop)
+ .to receive(:in_model?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class User
+ class << self
+ def second_method
+ BarFinder.new
+ ^^^^^^^^^^^^^ Finders can not be used inside model class methods.
+ end
+ end
+
+ def self.second_method
+ FooFinder.new
+ ^^^^^^^^^^^^^ Finders can not be used inside model class methods.
+ end
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of a Finder in a non Finder file' do
+ expect_no_offenses(<<~SOURCE)
+ class FooFinder
+ def execute
+ BarFinder.new.execute
+ end
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of a Finder in a regular class method' do
+ expect_no_offenses(<<~SOURCE)
+ class User
+ class << self
+ def second_method
+ BarFinder.new
+ end
+ end
+
+ def self.second_method
+ FooFinder.new
+ end
+ end
+ SOURCE
+ end
+end
diff --git a/spec/rubocop/cop/code_reuse/presenter_spec.rb b/spec/rubocop/cop/code_reuse/presenter_spec.rb
new file mode 100644
index 00000000000..4fe72619273
--- /dev/null
+++ b/spec/rubocop/cop/code_reuse/presenter_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/code_reuse/presenter'
+
+describe RuboCop::Cop::CodeReuse::Presenter do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of a Presenter in a Service class' do
+ allow(cop)
+ .to receive(:in_service_class?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooService
+ def execute
+ FooPresenter.new.execute
+ ^^^^^^^^^^^^^^^^ Presenters can not be used in a Service class.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Presenter in a Finder' do
+ allow(cop)
+ .to receive(:in_finder?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooFinder
+ def execute
+ FooPresenter.new.execute
+ ^^^^^^^^^^^^^^^^ Presenters can not be used in a Finder.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Service class in a Presenter' do
+ allow(cop)
+ .to receive(:in_presenter?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooPresenter
+ def execute
+ FooPresenter.new.execute
+ ^^^^^^^^^^^^^^^^ Presenters can not be used in a Presenter.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Presenter in a Serializer' do
+ allow(cop)
+ .to receive(:in_serializer?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooSerializer
+ def execute
+ FooPresenter.new.execute
+ ^^^^^^^^^^^^^^^^ Presenters can not be used in a Serializer.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Presenter in a model instance method' do
+ allow(cop)
+ .to receive(:in_model?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class User < ActiveRecord::Base
+ def execute
+ FooPresenter.new.execute
+ ^^^^^^^^^^^^^^^^ Presenters can not be used in a model.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Presenter in a model class method' do
+ allow(cop)
+ .to receive(:in_model?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class User < ActiveRecord::Base
+ def self.execute
+ FooPresenter.new.execute
+ ^^^^^^^^^^^^^^^^ Presenters can not be used in a model.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Presenter in a worker' do
+ allow(cop)
+ .to receive(:in_worker?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooWorker
+ def perform
+ FooPresenter.new.execute
+ ^^^^^^^^^^^^^^^^ Presenters can not be used in a worker.
+ end
+ end
+ SOURCE
+ end
+end
diff --git a/spec/rubocop/cop/code_reuse/serializer_spec.rb b/spec/rubocop/cop/code_reuse/serializer_spec.rb
new file mode 100644
index 00000000000..4530b15eed7
--- /dev/null
+++ b/spec/rubocop/cop/code_reuse/serializer_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/code_reuse/serializer'
+
+describe RuboCop::Cop::CodeReuse::Serializer do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of a Serializer in a Service class' do
+ allow(cop)
+ .to receive(:in_service_class?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooService
+ def execute
+ FooSerializer.new.execute
+ ^^^^^^^^^^^^^^^^^ Serializers can not be used in a Service class.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Serializer in a Finder' do
+ allow(cop)
+ .to receive(:in_finder?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooFinder
+ def execute
+ FooSerializer.new.execute
+ ^^^^^^^^^^^^^^^^^ Serializers can not be used in a Finder.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Serializer in a Presenter' do
+ allow(cop)
+ .to receive(:in_presenter?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooPresenter
+ def execute
+ FooSerializer.new.execute
+ ^^^^^^^^^^^^^^^^^ Serializers can not be used in a Presenter.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Serializer in a Serializer' do
+ allow(cop)
+ .to receive(:in_serializer?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooSerializer
+ def execute
+ FooSerializer.new.execute
+ ^^^^^^^^^^^^^^^^^ Serializers can not be used in a Serializer.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Serializer in a model instance method' do
+ allow(cop)
+ .to receive(:in_model?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class User < ActiveRecord::Base
+ def execute
+ FooSerializer.new.execute
+ ^^^^^^^^^^^^^^^^^ Serializers can not be used in a model.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Serializer in a model class method' do
+ allow(cop)
+ .to receive(:in_model?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class User < ActiveRecord::Base
+ def self.execute
+ FooSerializer.new.execute
+ ^^^^^^^^^^^^^^^^^ Serializers can not be used in a model.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Serializer in a worker' do
+ allow(cop)
+ .to receive(:in_worker?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooWorker
+ def perform
+ FooSerializer.new.execute
+ ^^^^^^^^^^^^^^^^^ Serializers can not be used in a worker.
+ end
+ end
+ SOURCE
+ end
+end
diff --git a/spec/rubocop/cop/code_reuse/service_class_spec.rb b/spec/rubocop/cop/code_reuse/service_class_spec.rb
new file mode 100644
index 00000000000..7b8d82f332e
--- /dev/null
+++ b/spec/rubocop/cop/code_reuse/service_class_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/code_reuse/service_class'
+
+describe RuboCop::Cop::CodeReuse::ServiceClass do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of a Service class in a Finder' do
+ allow(cop)
+ .to receive(:in_finder?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooFinder
+ def execute
+ FooService.new.execute
+ ^^^^^^^^^^^^^^ Service classes can not be used in a Finder.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Service class in a Presenter' do
+ allow(cop)
+ .to receive(:in_presenter?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooPresenter
+ def execute
+ FooService.new.execute
+ ^^^^^^^^^^^^^^ Service classes can not be used in a Presenter.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Service class in a Serializer' do
+ allow(cop)
+ .to receive(:in_serializer?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooSerializer
+ def execute
+ FooService.new.execute
+ ^^^^^^^^^^^^^^ Service classes can not be used in a Serializer.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a Service class in a model' do
+ allow(cop)
+ .to receive(:in_model?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class User < ActiveRecord::Model
+ class << self
+ def first
+ FooService.new.execute
+ ^^^^^^^^^^^^^^ Service classes can not be used in a model.
+ end
+ end
+
+ def second
+ FooService.new.execute
+ ^^^^^^^^^^^^^^ Service classes can not be used in a model.
+ end
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of a Service class in a regular class' do
+ expect_no_offenses(<<~SOURCE)
+ class Foo
+ def execute
+ FooService.new.execute
+ end
+ end
+ SOURCE
+ end
+end
diff --git a/spec/rubocop/cop/code_reuse/worker_spec.rb b/spec/rubocop/cop/code_reuse/worker_spec.rb
new file mode 100644
index 00000000000..97acaeb7643
--- /dev/null
+++ b/spec/rubocop/cop/code_reuse/worker_spec.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/code_reuse/worker'
+
+describe RuboCop::Cop::CodeReuse::Worker do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of a worker in a controller' do
+ allow(cop)
+ .to receive(:in_controller?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooController
+ def index
+ FooWorker.perform_async
+ ^^^^^^^^^^^^^^^^^^^^^^^ Workers can not be used in a controller.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a worker in an API' do
+ allow(cop)
+ .to receive(:in_api?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class Foo < Grape::API
+ resource :projects do
+ get '/' do
+ FooWorker.perform_async
+ ^^^^^^^^^^^^^^^^^^^^^^^ Workers can not be used in a Grape API.
+ end
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a worker in a Finder' do
+ allow(cop)
+ .to receive(:in_finder?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooFinder
+ def execute
+ FooWorker.perform_async
+ ^^^^^^^^^^^^^^^^^^^^^^^ Workers can not be used in a Finder.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a worker in a Presenter' do
+ allow(cop)
+ .to receive(:in_presenter?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooPresenter
+ def execute
+ FooWorker.perform_async
+ ^^^^^^^^^^^^^^^^^^^^^^^ Workers can not be used in a Presenter.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a worker in a Serializer' do
+ allow(cop)
+ .to receive(:in_serializer?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class FooSerializer
+ def execute
+ FooWorker.perform_async
+ ^^^^^^^^^^^^^^^^^^^^^^^ Workers can not be used in a Serializer.
+ end
+ end
+ SOURCE
+ end
+
+ it 'flags the use of a worker in a model class method' do
+ allow(cop)
+ .to receive(:in_model?)
+ .and_return(true)
+
+ expect_offense(<<~SOURCE)
+ class User < ActiveRecord::Base
+ def self.execute
+ FooWorker.perform_async
+ ^^^^^^^^^^^^^^^^^^^^^^^ Workers can not be used in model class methods.
+ end
+ end
+ SOURCE
+ end
+end
diff --git a/spec/rubocop/cop/destroy_all_spec.rb b/spec/rubocop/cop/destroy_all_spec.rb
new file mode 100644
index 00000000000..b0bc40552b3
--- /dev/null
+++ b/spec/rubocop/cop/destroy_all_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../rubocop/cop/destroy_all'
+
+describe RuboCop::Cop::DestroyAll do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of destroy_all with a send receiver' do
+ inspect_source('foo.destroy_all # rubocop: disable DestroyAll')
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'flags the use of destroy_all with a constant receiver' do
+ inspect_source('User.destroy_all # rubocop: disable DestroyAll')
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'flags the use of destroy_all when passing arguments' do
+ inspect_source('User.destroy_all([])')
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'flags the use of destroy_all with a local variable receiver' do
+ inspect_source(<<~RUBY)
+ users = User.all
+ users.destroy_all # rubocop: disable DestroyAll
+ RUBY
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'does not flag the use of delete_all' do
+ inspect_source('foo.delete_all')
+
+ expect(cop.offenses).to be_empty
+ end
+end
diff --git a/spec/rubocop/cop/gitlab/union_spec.rb b/spec/rubocop/cop/gitlab/union_spec.rb
new file mode 100644
index 00000000000..5b06f30b25f
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/union_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/gitlab/union'
+
+describe RuboCop::Cop::Gitlab::Union do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of Gitlab::SQL::Union.new' do
+ expect_offense(<<~SOURCE)
+ Gitlab::SQL::Union.new([foo])
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use the `FromUnion` concern, instead of using `Gitlab::SQL::Union` directly
+ SOURCE
+ end
+
+ it 'does not flag the use of Gitlab::SQL::Union in a spec' do
+ allow(cop).to receive(:in_spec?).and_return(true)
+
+ expect_no_offenses('Gitlab::SQL::Union.new([foo])')
+ end
+end
diff --git a/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb b/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
new file mode 100644
index 00000000000..7b5235a3da7
--- /dev/null
+++ b/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../rubocop/cop/group_public_or_visible_to_user'
+
+describe RuboCop::Cop::GroupPublicOrVisibleToUser do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of Group.public_or_visible_to_user with a constant receiver' do
+ inspect_source('Group.public_or_visible_to_user')
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'does not flat the use of public_or_visible_to_user with a constant that is not Group' do
+ inspect_source('Project.public_or_visible_to_user')
+
+ expect(cop.offenses.size).to eq(0)
+ end
+
+ it 'does not flag the use of Group.public_or_visible_to_user with a send receiver' do
+ inspect_source('foo.public_or_visible_to_user')
+
+ expect(cop.offenses.size).to eq(0)
+ end
+end
diff --git a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
index 03eeffe6483..892b393c307 100644
--- a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
+++ b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
@@ -328,6 +328,22 @@ describe RuboCop::Cop::LineBreakAroundConditionalBlock do
expect(cop.offenses).to be_empty
end
+ it "doesn't flag violation for #{conditional} preceded by a rescue" do
+ source = <<~RUBY
+ def a_method
+ do_something
+ rescue
+ #{conditional} condition
+ do_something
+ end
+ end
+ RUBY
+
+ inspect_source(source)
+
+ expect(cop.offenses).to be_empty
+ end
+
it "doesn't flag violation for #{conditional} followed by a rescue" do
source = <<~RUBY
def a_method
diff --git a/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb b/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb
new file mode 100644
index 00000000000..4739f0e6c47
--- /dev/null
+++ b/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../rubocop/cop/prefer_class_methods_over_module'
+
+describe RuboCop::Cop::PreferClassMethodsOverModule do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags violation when using module ClassMethods' do
+ expect_offense(<<~RUBY)
+ module Foo
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ ^^^^^^^^^^^^^^^^^^^ Do not use module ClassMethods, use class_methods block instead.
+ def a_class_method
+ end
+ end
+ end
+ RUBY
+ end
+
+ it "doesn't flag violation when using class_methods" do
+ expect_no_offenses(<<~RUBY)
+ module Foo
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def a_class_method
+ end
+ end
+ end
+ RUBY
+ end
+
+ it "doesn't flag violation when module is not extending ActiveSupport::Concern" do
+ expect_no_offenses(<<~RUBY)
+ module Foo
+ module ClassMethods
+ def a_class_method
+ end
+ end
+ end
+ RUBY
+ end
+
+ it "doesn't flag violation when ClassMethods is used inside a class" do
+ expect_no_offenses(<<~RUBY)
+ class Foo
+ module ClassMethods
+ def a_class_method
+ end
+ end
+ end
+ RUBY
+ end
+
+ it "doesn't flag violation when not using either class_methods or ClassMethods" do
+ expect_no_offenses(<<~RUBY)
+ module Foo
+ extend ActiveSupport::Concern
+
+ def a_method
+ end
+ end
+ RUBY
+ end
+
+ it 'autocorrects ClassMethods into class_methods' do
+ source = <<~RUBY
+ module Foo
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def a_class_method
+ end
+ end
+ end
+ RUBY
+ autocorrected = autocorrect_source(source)
+
+ expected_source = <<~RUBY
+ module Foo
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def a_class_method
+ end
+ end
+ end
+ RUBY
+ expect(autocorrected).to eq(expected_source)
+ end
+end
diff --git a/spec/rubocop/cop/qa/element_with_pattern_spec.rb b/spec/rubocop/cop/qa/element_with_pattern_spec.rb
new file mode 100644
index 00000000000..c5beb40f9fd
--- /dev/null
+++ b/spec/rubocop/cop/qa/element_with_pattern_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../../rubocop/cop/qa/element_with_pattern'
+
+describe RuboCop::Cop::QA::ElementWithPattern do
+ include CopHelper
+
+ let(:source_file) { 'qa/page.rb' }
+
+ subject(:cop) { described_class.new }
+
+ context 'in a QA file' do
+ before do
+ allow(cop).to receive(:in_qa_file?).and_return(true)
+ end
+
+ it "registers an offense for elements with a pattern" do
+ expect_offense(<<-RUBY)
+ view 'app/views/shared/groups/_search_form.html.haml' do
+ element :groups_filter, 'search_field_tag :filter'
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't use a pattern for element, create a corresponding `qa-groups-filter` instead.
+ element :groups_filter_placeholder, /Search by name/
+ ^^^^^^^^^^^^^^^^ Don't use a pattern for element, create a corresponding `qa-groups-filter-placeholder` instead.
+ end
+ RUBY
+ end
+
+ it "does not register an offense for element without a pattern" do
+ expect_no_offenses(<<-RUBY)
+ view 'app/views/shared/groups/_search_form.html.haml' do
+ element :groups_filter
+ element :groups_filter_placeholder
+ end
+ RUBY
+ end
+ end
+
+ context 'outside of a migration spec file' do
+ it "does not register an offense" do
+ expect_no_offenses(<<-RUBY)
+ describe 'foo' do
+ let(:user) { create(:user) }
+ end
+ RUBY
+ end
+ end
+end
diff --git a/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
new file mode 100644
index 00000000000..7bd50866577
--- /dev/null
+++ b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../rubocop/cop/ruby_interpolation_in_translation'
+
+# Disabling interpolation check as we deliberately want to have #{} in strings.
+# rubocop:disable Lint/InterpolationCheck
+describe RuboCop::Cop::RubyInterpolationInTranslation do
+ subject(:cop) { described_class.new }
+
+ it 'does not add an offence for a regular messages' do
+ inspect_source('_("Hello world")')
+
+ expect(cop.offenses).to be_empty
+ end
+
+ it 'adds the correct offence when using interpolation in a string' do
+ inspect_source('_("Hello #{world}")')
+
+ offense = cop.offenses.first
+
+ expect(offense.location.source).to eq('#{world}')
+ expect(offense.message).to eq('Don\'t use ruby interpolation #{} inside translated strings, instead use %{}')
+ end
+
+ it 'detects when using a ruby interpolation in the first argument of a pluralized string' do
+ inspect_source('n_("Hello #{world}", "Hello world")')
+
+ expect(cop.offenses).not_to be_empty
+ end
+
+ it 'detects when using a ruby interpolation in the second argument of a pluralized string' do
+ inspect_source('n_("Hello world", "Hello #{world}")')
+
+ expect(cop.offenses).not_to be_empty
+ end
+
+ it 'detects when using interpolation in a namespaced translation' do
+ inspect_source('s_("Hello|#{world}")')
+
+ expect(cop.offenses).not_to be_empty
+ end
+
+ it 'does not add an offence for messages defined over multiple lines' do
+ source = <<~SRC
+ _("Hello "\
+ "world ")
+ SRC
+
+ inspect_source(source)
+ expect(cop.offenses).to be_empty
+ end
+
+ it 'adds an offence for violations in a message defined over multiple lines' do
+ source = <<~SRC
+ _("Hello "\
+ "\#{world} ")
+ SRC
+
+ inspect_source(source)
+ expect(cop.offenses).not_to be_empty
+ end
+end
+# rubocop:enable Lint/InterpolationCheck
diff --git a/spec/rubocop/qa_helpers_spec.rb b/spec/rubocop/qa_helpers_spec.rb
new file mode 100644
index 00000000000..26e4c1ca6f0
--- /dev/null
+++ b/spec/rubocop/qa_helpers_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require_relative '../../rubocop/qa_helpers'
+
+describe RuboCop::QAHelpers do
+ def parse_source(source, path = 'foo.rb')
+ buffer = Parser::Source::Buffer.new(path)
+ buffer.source = source
+
+ builder = RuboCop::AST::Builder.new
+ parser = Parser::CurrentRuby.new(builder)
+
+ parser.parse(buffer)
+ end
+
+ let(:cop) do
+ Class.new do
+ include RuboCop::QAHelpers
+ end.new
+ end
+
+ describe '#in_qa_file?' do
+ it 'returns true for a node in the qa/ directory' do
+ node = parse_source('10', Rails.root.join('qa', 'qa', 'page', 'dashboard', 'groups.rb'))
+
+ expect(cop.in_qa_file?(node)).to eq(true)
+ end
+
+ it 'returns false for a node outside the qa/ directory' do
+ node = parse_source('10', Rails.root.join('app', 'foo', 'foo.rb'))
+
+ expect(cop.in_qa_file?(node)).to eq(false)
+ end
+ end
+end
diff --git a/spec/serializers/build_action_entity_spec.rb b/spec/serializers/build_action_entity_spec.rb
index 15720d86583..ea88951ebc6 100644
--- a/spec/serializers/build_action_entity_spec.rb
+++ b/spec/serializers/build_action_entity_spec.rb
@@ -22,5 +22,21 @@ describe BuildActionEntity do
it 'contains whether it is playable' do
expect(subject[:playable]).to eq job.playable?
end
+
+ context 'when job is scheduled' do
+ let(:job) { create(:ci_build, :scheduled) }
+
+ it 'returns scheduled' do
+ expect(subject[:scheduled]).to be_truthy
+ end
+
+ it 'returns scheduled_at' do
+ expect(subject[:scheduled_at]).to eq(job.scheduled_at)
+ end
+
+ it 'returns unschedule path' do
+ expect(subject[:unschedule_path]).to include "jobs/#{job.id}/unschedule"
+ end
+ end
end
end
diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb
index 52459cd369d..302ef147eb2 100644
--- a/spec/serializers/build_serializer_spec.rb
+++ b/spec/serializers/build_serializer_spec.rb
@@ -37,7 +37,7 @@ describe BuildSerializer do
it 'serializes only status' do
expect(subject[:text]).to eq(status.text)
expect(subject[:label]).to eq('failed')
- expect(subject[:tooltip]).to eq('failed <br> (unknown failure)')
+ expect(subject[:tooltip]).to eq('failed - (unknown failure)')
expect(subject[:icon]).to eq(status.icon)
expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png")
end
diff --git a/spec/serializers/commit_entity_spec.rb b/spec/serializers/commit_entity_spec.rb
index 04247c78549..b9995818e98 100644
--- a/spec/serializers/commit_entity_spec.rb
+++ b/spec/serializers/commit_entity_spec.rb
@@ -1,10 +1,11 @@
require 'spec_helper'
describe CommitEntity do
+ SIGNATURE_HTML = 'TEST'.freeze
+
let(:entity) do
described_class.new(commit, request: request)
end
-
let(:request) { double('request') }
let(:project) { create(:project, :repository) }
let(:commit) { project.commit }
@@ -12,7 +13,11 @@ describe CommitEntity do
subject { entity.as_json }
before do
+ render = double('render')
+ allow(render).to receive(:call).and_return(SIGNATURE_HTML)
+
allow(request).to receive(:project).and_return(project)
+ allow(request).to receive(:render).and_return(render)
end
context 'when commit author is a user' do
@@ -51,4 +56,56 @@ describe CommitEntity do
it 'exposes gravatar url that belongs to author' do
expect(subject.fetch(:author_gravatar_url)).to match /gravatar/
end
+
+ context 'when type is not set' do
+ it 'does not expose extra properties' do
+ expect(subject).not_to include(:description_html)
+ expect(subject).not_to include(:title_html)
+ end
+ end
+
+ context 'when type is "full"' do
+ let(:entity) do
+ described_class.new(commit, request: request, type: :full, pipeline_ref: project.default_branch, pipeline_project: project)
+ end
+
+ it 'exposes extra properties' do
+ expect(subject).to include(:description_html)
+ expect(subject).to include(:title_html)
+ expect(subject.fetch(:description_html)).not_to be_nil
+ expect(subject.fetch(:title_html)).not_to be_nil
+ end
+
+ context 'when commit has signature' do
+ let(:commit) { project.commit(TestEnv::BRANCH_SHA['signed-commits']) }
+
+ it 'exposes "signature_html"' do
+ expect(request.render).to receive(:call)
+ expect(subject.fetch(:signature_html)).to be SIGNATURE_HTML
+ end
+ end
+
+ context 'when commit has pipeline' do
+ before do
+ create(:ci_pipeline, project: project, sha: commit.id)
+ end
+
+ it 'exposes "pipeline_status_path"' do
+ expect(subject.fetch(:pipeline_status_path)).not_to be_nil
+ end
+ end
+ end
+
+ context 'when commit_url_params is set' do
+ let(:entity) do
+ params = { merge_request_iid: 3 }
+
+ described_class.new(commit, request: request, commit_url_params: params)
+ end
+
+ it 'adds commit_url_params to url and path' do
+ expect(subject[:commit_path]).to include "?merge_request_iid=3"
+ expect(subject[:commit_url]).to include "?merge_request_iid=3"
+ end
+ end
end
diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb
index 522c92ce295..8793a762f9d 100644
--- a/spec/serializers/deployment_entity_spec.rb
+++ b/spec/serializers/deployment_entity_spec.rb
@@ -22,4 +22,26 @@ describe DeploymentEntity do
it 'exposes creation date' do
expect(subject).to include(:created_at)
end
+
+ describe 'scheduled_actions' do
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create(:ci_pipeline, project: project, user: user) }
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+ let(:deployment) { create(:deployment, deployable: build) }
+
+ context 'when the same pipeline has a scheduled action' do
+ let(:other_build) { create(:ci_build, :schedulable, :success, pipeline: pipeline, name: 'other build') }
+ let!(:other_deployment) { create(:deployment, deployable: other_build) }
+
+ it 'returns other scheduled actions' do
+ expect(subject[:scheduled_actions][0][:name]).to eq 'other build'
+ end
+ end
+
+ context 'when the same pipeline does not have a scheduled action' do
+ it 'does not return other actions' do
+ expect(subject[:scheduled_actions]).to be_empty
+ end
+ end
+ end
end
diff --git a/spec/serializers/deployment_serializer_spec.rb b/spec/serializers/deployment_serializer_spec.rb
new file mode 100644
index 00000000000..4834f5ede3c
--- /dev/null
+++ b/spec/serializers/deployment_serializer_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe DeploymentSerializer do
+ set(:project) { create(:project, :repository) }
+ set(:user) { create(:user, email: project.commit.author_email) }
+
+ let(:resource) { create(:deployment, project: project, sha: project.commit.id) }
+ let(:serializer) { described_class.new(request) }
+
+ shared_examples 'json schema' do
+ let(:json_entity) { subject.as_json }
+
+ it 'matches deployment entity schema' do
+ expect(json_entity).to match_schema('deployment')
+ end
+ end
+
+ describe '#represent' do
+ subject { serializer.represent(resource) }
+
+ let(:request) { { project: project, current_user: user } }
+
+ it_behaves_like 'json schema'
+ end
+
+ describe '#represent_concise' do
+ subject { serializer.represent_concise(resource) }
+
+ let(:request) { { project: project } }
+
+ it_behaves_like 'json schema'
+ end
+end
diff --git a/spec/serializers/detailed_status_entity_spec.rb b/spec/serializers/detailed_status_entity_spec.rb
new file mode 100644
index 00000000000..62f57ca8689
--- /dev/null
+++ b/spec/serializers/detailed_status_entity_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe DetailedStatusEntity do
+ let(:entity) { described_class.new(status) }
+
+ let(:status) do
+ Gitlab::Ci::Status::Success.new(double('object'), double('user'))
+ end
+
+ before do
+ allow(status).to receive(:has_details?).and_return(true)
+ allow(status).to receive(:details_path).and_return('some/path')
+ end
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ it 'contains status details' do
+ expect(subject).to include :text, :icon, :favicon, :label, :group, :tooltip
+ expect(subject).to include :has_details, :details_path
+ expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png')
+ end
+ end
+end
diff --git a/spec/serializers/diff_file_entity_spec.rb b/spec/serializers/diff_file_entity_spec.rb
index 00b2146dc86..7497b8f27bd 100644
--- a/spec/serializers/diff_file_entity_spec.rb
+++ b/spec/serializers/diff_file_entity_spec.rb
@@ -26,6 +26,11 @@ describe DiffFileEntity do
)
end
+ it 'includes viewer' do
+ expect(subject[:viewer].with_indifferent_access)
+ .to match_schema('entities/diff_viewer')
+ end
+
# Converted diff files from GitHub import does not contain blob file
# and content sha.
context 'when diff file does not have a blob and content sha' do
@@ -67,4 +72,21 @@ describe DiffFileEntity do
end
end
end
+
+ context '#parallel_diff_lines' do
+ it 'exposes parallel diff lines correctly' do
+ response = subject
+
+ lines = response[:parallel_diff_lines]
+
+ # make sure at least one line is present for each side
+ expect(lines.map { |line| line[:right] }.compact).to be_present
+ expect(lines.map { |line| line[:left] }.compact).to be_present
+ # make sure all lines are in correct format
+ lines.each do |parallel_line|
+ expect(parallel_line[:left].as_json).to match_schema('entities/diff_line') if parallel_line[:left]
+ expect(parallel_line[:right].as_json).to match_schema('entities/diff_line') if parallel_line[:right]
+ end
+ end
+ end
end
diff --git a/spec/serializers/diff_line_entity_spec.rb b/spec/serializers/diff_line_entity_spec.rb
new file mode 100644
index 00000000000..2549f64bcd3
--- /dev/null
+++ b/spec/serializers/diff_line_entity_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe DiffLineEntity do
+ include RepoHelpers
+
+ let(:code) { 'hello world' }
+ let(:line) { Gitlab::Diff::Line.new(code, 'new', 1, nil, 1) }
+ let(:entity) { described_class.new(line, request: {}) }
+
+ subject { entity.as_json }
+
+ it 'exposes correct attributes' do
+ expect(subject).to include(
+ :line_code, :type, :old_line, :new_line, :text, :meta_data, :rich_text
+ )
+ end
+
+ describe '#rich_text' do
+ let(:code) { '<h2 onmouseover="alert(2)">Test</h2>' }
+ let(:rich_text_value) { nil }
+
+ before do
+ line.instance_variable_set(:@rich_text, rich_text_value)
+ end
+
+ shared_examples 'escapes html tags' do
+ it do
+ expect(subject[:rich_text]).to eq html_escape(code)
+ expect(subject[:rich_text]).to be_html_safe
+ end
+ end
+
+ context 'when rich_line is present' do
+ let(:rich_text_value) { code }
+
+ it_behaves_like 'escapes html tags'
+ end
+
+ context 'when rich_line is not present' do
+ it_behaves_like 'escapes html tags'
+ end
+ end
+end
diff --git a/spec/serializers/diff_line_serializer_spec.rb b/spec/serializers/diff_line_serializer_spec.rb
new file mode 100644
index 00000000000..6dd8abd0579
--- /dev/null
+++ b/spec/serializers/diff_line_serializer_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe DiffLineSerializer do
+ let(:line) { Gitlab::Diff::Line.new('hello world', 'new', 1, nil, 1) }
+ let(:serializer) { described_class.new.represent(line) }
+
+ describe '#to_json' do
+ subject { serializer.to_json }
+
+ it 'matches the schema' do
+ expect(subject).to match_schema('entities/diff_line')
+ end
+
+ context 'when lines are parallel' do
+ let(:right_line) { Gitlab::Diff::Line.new('right line', 'new', 1, nil, 1) }
+ let(:left_line) { Gitlab::Diff::Line.new('left line', 'match', 1, nil, 1) }
+ let(:parallel_line) { [{ right: right_line, left: left_line }] }
+ let(:serializer) { described_class.new.represent(parallel_line, {}, DiffLineParallelEntity) }
+
+ it 'matches the schema' do
+ expect(subject).to match_schema('entities/diff_line_parallel')
+ end
+ end
+ end
+end
diff --git a/spec/serializers/diff_viewer_entity_spec.rb b/spec/serializers/diff_viewer_entity_spec.rb
new file mode 100644
index 00000000000..66ac6ef2adc
--- /dev/null
+++ b/spec/serializers/diff_viewer_entity_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe DiffViewerEntity do
+ include RepoHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:commit) { project.commit(sample_commit.id) }
+ let(:diff_refs) { commit.diff_refs }
+ let(:diff) { commit.raw_diffs.first }
+ let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) }
+ let(:viewer) { diff_file.simple_viewer }
+
+ subject { described_class.new(viewer).as_json }
+
+ it 'serializes diff file viewer' do
+ expect(subject.with_indifferent_access).to match_schema('entities/diff_viewer')
+ end
+end
diff --git a/spec/serializers/discussion_entity_spec.rb b/spec/serializers/discussion_entity_spec.rb
index 378540a35b6..0590304e832 100644
--- a/spec/serializers/discussion_entity_spec.rb
+++ b/spec/serializers/discussion_entity_spec.rb
@@ -36,6 +36,13 @@ describe DiscussionEntity do
)
end
+ it 'resolved_by matches note_user_entity schema' do
+ Notes::ResolveService.new(note.project, user).execute(note)
+
+ expect(subject[:resolved_by].with_indifferent_access)
+ .to match_schema('entities/note_user_entity')
+ end
+
context 'when is LegacyDiffDiscussion' do
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
index 0f0ab5ac796..87493a28d1f 100644
--- a/spec/serializers/environment_serializer_spec.rb
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -14,7 +14,8 @@ describe EnvironmentSerializer do
let(:project) { create(:project, :repository) }
let(:deployable) { create(:ci_build) }
let(:deployment) do
- create(:deployment, deployable: deployable,
+ create(:deployment, :success,
+ deployable: deployable,
user: user,
project: project,
sha: project.commit.id)
diff --git a/spec/serializers/environment_status_entity_spec.rb b/spec/serializers/environment_status_entity_spec.rb
new file mode 100644
index 00000000000..8a6a38fe5f8
--- /dev/null
+++ b/spec/serializers/environment_status_entity_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe EnvironmentStatusEntity do
+ let(:user) { create(:user) }
+ let(:request) { double('request') }
+
+ let(:deployment) { create(:deployment, :succeed, :review_app) }
+ let(:environment) { deployment.environment }
+ let(:project) { deployment.project }
+ let(:merge_request) { create(:merge_request, :deployed_review_app, deployment: deployment) }
+
+ let(:environment_status) { EnvironmentStatus.new(environment, merge_request, merge_request.diff_head_sha) }
+ let(:entity) { described_class.new(environment_status, request: request) }
+
+ subject { entity.as_json }
+
+ before do
+ deployment.update(sha: merge_request.diff_head_sha)
+ allow(request).to receive(:current_user).and_return(user)
+ end
+
+ it { is_expected.to include(:id) }
+ it { is_expected.to include(:name) }
+ it { is_expected.to include(:url) }
+ it { is_expected.to include(:external_url) }
+ it { is_expected.to include(:external_url_formatted) }
+ it { is_expected.to include(:deployed_at) }
+ it { is_expected.to include(:deployed_at_formatted) }
+ it { is_expected.to include(:changes) }
+ it { is_expected.to include(:status) }
+
+ it { is_expected.not_to include(:stop_url) }
+ it { is_expected.not_to include(:metrics_url) }
+ it { is_expected.not_to include(:metrics_monitoring_url) }
+
+ context 'when the user is project maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it { is_expected.to include(:stop_url) }
+ end
+
+ context 'when deployment has metrics' do
+ let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) }
+
+ let(:simple_metrics) do
+ {
+ success: true,
+ metrics: {},
+ last_update: 42
+ }
+ end
+
+ before do
+ project.add_maintainer(user)
+ allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter)
+ allow(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics)
+ allow(entity).to receive(:deployment).and_return(deployment)
+ end
+
+ context 'when deployment succeeded' do
+ let(:deployment) { create(:deployment, :succeed, :review_app) }
+
+ it 'returns metrics url' do
+ expect(subject[:metrics_url])
+ .to eq("/#{project.namespace.name}/#{project.name}/environments/#{environment.id}/deployments/#{deployment.iid}/metrics")
+ end
+ end
+
+ context 'when deployment is running' do
+ let(:deployment) { create(:deployment, :running, :review_app) }
+
+ it 'does not return metrics url' do
+ expect(subject[:metrics_url]).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/serializers/job_entity_spec.rb b/spec/serializers/job_entity_spec.rb
index a5581a34517..851b41a7f7e 100644
--- a/spec/serializers/job_entity_spec.rb
+++ b/spec/serializers/job_entity_spec.rb
@@ -109,6 +109,19 @@ describe JobEntity do
end
end
+ context 'when job is scheduled' do
+ let(:job) { create(:ci_build, :scheduled) }
+
+ it 'contains path to unschedule action' do
+ expect(subject).to include(:unschedule_path)
+ end
+
+ it 'contains scheduled_at' do
+ expect(subject[:scheduled]).to be_truthy
+ expect(subject[:scheduled_at]).to eq(job.scheduled_at)
+ end
+ end
+
context 'when job is generic commit status' do
let(:job) { create(:generic_commit_status, target_url: 'http://google.com') }
@@ -142,7 +155,7 @@ describe JobEntity do
end
it 'should indicate the failure reason on tooltip' do
- expect(subject[:status][:tooltip]).to eq('failed <br> (API failure)')
+ expect(subject[:status][:tooltip]).to eq('failed - (API failure)')
end
it 'should include a callout message with a verbose output' do
@@ -166,7 +179,7 @@ describe JobEntity do
end
it 'should indicate the failure reason on tooltip' do
- expect(subject[:status][:tooltip]).to eq('failed <br> (API failure) (allowed to fail)')
+ expect(subject[:status][:tooltip]).to eq('failed - (API failure) (allowed to fail)')
end
it 'should include a callout message with a verbose output' do
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index 0ba2539a717..561421d5ac8 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe MergeRequestWidgetEntity do
+ include ProjectForksHelper
+
let(:project) { create :project, :repository }
let(:resource) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
@@ -50,6 +52,40 @@ describe MergeRequestWidgetEntity do
end
end
+ describe 'merge_pipeline' do
+ it 'returns nil' do
+ expect(subject[:merge_pipeline]).to be_nil
+ end
+
+ context 'when is merged' do
+ let(:resource) { create(:merged_merge_request, source_project: project, merge_commit_sha: project.commit.id) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.target_branch, sha: resource.merge_commit_sha) }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'returns merge_pipeline' do
+ pipeline.reload
+ pipeline_payload = PipelineDetailsEntity
+ .represent(pipeline, request: request)
+ .as_json
+
+ expect(subject[:merge_pipeline]).to eq(pipeline_payload)
+ end
+
+ context 'when user cannot read pipelines on target project' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'returns nil' do
+ expect(subject[:merge_pipeline]).to be_nil
+ end
+ end
+ end
+ end
+
describe 'metrics' do
context 'when metrics record exists with merged data' do
before do
@@ -206,12 +242,12 @@ describe MergeRequestWidgetEntity do
describe 'when source project is deleted' do
let(:project) { create(:project, :repository) }
- let(:fork_project) { create(:project, :repository, forked_from_project: project) }
- let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) }
+ let(:forked_project) { fork_project(project) }
+ let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: project) }
it 'returns a blank rebase_path' do
allow(merge_request).to receive(:should_be_rebased?).and_return(true)
- fork_project.destroy
+ forked_project.destroy
merge_request.reload
entity = described_class.new(merge_request, request: request).as_json
diff --git a/spec/serializers/move_to_project_entity_spec.rb b/spec/serializers/move_to_project_entity_spec.rb
new file mode 100644
index 00000000000..ac495eadb68
--- /dev/null
+++ b/spec/serializers/move_to_project_entity_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe MoveToProjectEntity do
+ describe '#as_json' do
+ let(:project) { build(:project, id: 1) }
+
+ subject { described_class.new(project).as_json }
+
+ it 'includes the project ID' do
+ expect(subject[:id]).to eq(project.id)
+ end
+
+ it 'includes the full path' do
+ expect(subject[:name_with_namespace]).to eq(project.name_with_namespace)
+ end
+ end
+end
diff --git a/spec/serializers/move_to_project_serializer_spec.rb b/spec/serializers/move_to_project_serializer_spec.rb
new file mode 100644
index 00000000000..841ac969eeb
--- /dev/null
+++ b/spec/serializers/move_to_project_serializer_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe MoveToProjectSerializer do
+ describe '#represent' do
+ it 'includes the name and name with namespace' do
+ project = build(:project, id: 1)
+ output = described_class.new.represent(project)
+
+ expect(output).to include(:id, :name_with_namespace)
+ end
+ end
+end
diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb
index 45e18086894..8e73a3e67c6 100644
--- a/spec/serializers/pipeline_details_entity_spec.rb
+++ b/spec/serializers/pipeline_details_entity_spec.rb
@@ -29,7 +29,7 @@ describe PipelineDetailsEntity do
expect(subject[:details])
.to include :duration, :finished_at
expect(subject[:details])
- .to include :stages, :artifacts, :manual_actions
+ .to include :stages, :artifacts, :manual_actions, :scheduled_actions
expect(subject[:details][:status]).to include :icon, :favicon, :text, :label
end
diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb
deleted file mode 100644
index 0b010ebd507..00000000000
--- a/spec/serializers/status_entity_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'spec_helper'
-
-describe StatusEntity do
- let(:entity) { described_class.new(status) }
-
- let(:status) do
- Gitlab::Ci::Status::Success.new(double('object'), double('user'))
- end
-
- before do
- allow(status).to receive(:has_details?).and_return(true)
- allow(status).to receive(:details_path).and_return('some/path')
- end
-
- describe '#as_json' do
- subject { entity.as_json }
-
- it 'contains status details' do
- expect(subject).to include :text, :icon, :favicon, :label, :group, :tooltip
- expect(subject).to include :has_details, :details_path
- expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png')
- end
- end
-end
diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb
index 6337ee7d724..daf5dfba6b1 100644
--- a/spec/services/application_settings/update_service_spec.rb
+++ b/spec/services/application_settings/update_service_spec.rb
@@ -10,6 +10,9 @@ describe ApplicationSettings::UpdateService do
before do
# So the caching behaves like it would in production
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+
+ # Creating these settings first ensures they're used by other factories
+ application_settings
end
describe 'updating terms' do
diff --git a/spec/services/applications/create_service_spec.rb b/spec/services/applications/create_service_spec.rb
index 9c43b56744b..c8134087fa1 100644
--- a/spec/services/applications/create_service_spec.rb
+++ b/spec/services/applications/create_service_spec.rb
@@ -1,17 +1,14 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe ::Applications::CreateService do
+ include TestRequestHelpers
+
let(:user) { create(:user) }
let(:params) { attributes_for(:application) }
- let(:request) do
- if Gitlab.rails5?
- ActionController::TestRequest.new({ remote_ip: "127.0.0.1" }, ActionController::TestSession.new)
- else
- ActionController::TestRequest.new(remote_ip: "127.0.0.1")
- end
- end
subject { described_class.new(user, params) }
- it { expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1) }
+ it { expect { subject.execute(test_request) }.to change { Doorkeeper::Application.count }.by(1) }
end
diff --git a/spec/services/audit_event_service_spec.rb b/spec/services/audit_event_service_spec.rb
new file mode 100644
index 00000000000..32fd98e6ef9
--- /dev/null
+++ b/spec/services/audit_event_service_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe AuditEventService do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:project_member) { create(:project_member, user: user) }
+ let(:service) { described_class.new(user, project, { action: :destroy }) }
+ let(:logger) { instance_double(Gitlab::AuditJsonLogger) }
+
+ describe '#security_event' do
+ before do
+ expect(service).to receive(:file_logger).and_return(logger)
+ end
+
+ it 'creates an event and logs to a file' do
+ expect(logger).to receive(:info).with(author_id: user.id,
+ entity_id: project.id,
+ entity_type: "Project",
+ action: :destroy)
+
+ expect { service.security_event }.to change(SecurityEvent, :count).by(1)
+ end
+ end
+end
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index c7f88e45c84..f2e9799452a 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -145,7 +145,7 @@ describe Auth::ContainerRegistryAuthenticationService do
{ scopes: ["registry:catalog:*"] }
end
- context 'disallow browsing for users without Gitlab admin rights' do
+ context 'disallow browsing for users without GitLab admin rights' do
it_behaves_like 'an inaccessible'
it_behaves_like 'not a container repository factory'
end
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 27a7bf0e605..010679b5360 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -24,7 +24,7 @@ describe Boards::Issues::ListService do
let!(:opened_issue1) { create(:labeled_issue, project: project, milestone: m1, title: 'Issue 1', labels: [bug]) }
let!(:opened_issue2) { create(:labeled_issue, project: project, milestone: m2, title: 'Issue 2', labels: [p2]) }
- let!(:reopened_issue1) { create(:issue, :opened, project: project, title: 'Issue 3' ) }
+ let!(:reopened_issue1) { create(:issue, :opened, project: project, title: 'Reopened Issue 1' ) }
let!(:list1_issue1) { create(:labeled_issue, project: project, milestone: m1, labels: [p2, development]) }
let!(:list1_issue2) { create(:labeled_issue, project: project, milestone: m2, labels: [development]) }
@@ -44,12 +44,19 @@ describe Boards::Issues::ListService do
end
it_behaves_like 'issues list service'
+
+ context 'when project is archived' do
+ let(:project) { create(:project, :archived) }
+
+ it_behaves_like 'issues list service'
+ end
end
context 'when parent is a group' do
let(:user) { create(:user) }
let(:project) { create(:project, :empty_repo, namespace: group) }
let(:project1) { create(:project, :empty_repo, namespace: group) }
+ let(:project_archived) { create(:project, :empty_repo, :archived, namespace: group) }
let(:m1) { create(:milestone, group: group) }
let(:m2) { create(:milestone, group: group) }
@@ -77,7 +84,8 @@ describe Boards::Issues::ListService do
let!(:opened_issue1) { create(:labeled_issue, project: project, milestone: m1, title: 'Issue 1', labels: [bug]) }
let!(:opened_issue2) { create(:labeled_issue, project: project, milestone: m2, title: 'Issue 2', labels: [p2, p2_project]) }
- let!(:reopened_issue1) { create(:issue, state: 'opened', project: project, title: 'Issue 3', closed_at: Time.now ) }
+ let!(:opened_issue3) { create(:labeled_issue, project: project_archived, milestone: m1, title: 'Issue 3', labels: [bug]) }
+ let!(:reopened_issue1) { create(:issue, state: 'opened', project: project, title: 'Reopened Issue 1', closed_at: Time.now ) }
let!(:list1_issue1) { create(:labeled_issue, project: project, milestone: m1, labels: [p2, p2_project, development]) }
let!(:list1_issue2) { create(:labeled_issue, project: project, milestone: m2, labels: [development]) }
diff --git a/spec/services/boards/visits/create_service_spec.rb b/spec/services/boards/visits/create_service_spec.rb
new file mode 100644
index 00000000000..6baf7ac9deb
--- /dev/null
+++ b/spec/services/boards/visits/create_service_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Boards::Visits::CreateService do
+ describe '#execute' do
+ let(:user) { create(:user) }
+
+ context 'when a project board' do
+ let(:project) { create(:project) }
+ let(:project_board) { create(:board, project: project) }
+
+ subject(:service) { described_class.new(project_board.parent, user) }
+
+ it 'returns nil when there is no user' do
+ service.current_user = nil
+
+ expect(service.execute(project_board)).to eq nil
+ end
+
+ it 'returns nil when database is read only' do
+ allow(Gitlab::Database).to receive(:read_only?) { true }
+
+ expect(service.execute(project_board)).to eq nil
+ end
+
+ it 'records the visit' do
+ expect(BoardProjectRecentVisit).to receive(:visited!).once
+
+ service.execute(project_board)
+ end
+ end
+
+ context 'when a group board' do
+ let(:group) { create(:group) }
+ let(:group_board) { create(:board, group: group) }
+
+ subject(:service) { described_class.new(group_board.parent, user) }
+
+ it 'returns nil when there is no user' do
+ service.current_user = nil
+
+ expect(service.execute(group_board)).to eq nil
+ end
+
+ it 'records the visit' do
+ expect(BoardGroupRecentVisit).to receive(:visited!).once
+
+ service.execute(group_board)
+ end
+ end
+ end
+end
diff --git a/spec/services/boards/visits/latest_service_spec.rb b/spec/services/boards/visits/latest_service_spec.rb
new file mode 100644
index 00000000000..e55d599e2cc
--- /dev/null
+++ b/spec/services/boards/visits/latest_service_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Boards::Visits::LatestService do
+ describe '#execute' do
+ let(:user) { create(:user) }
+
+ context 'when a project board' do
+ let(:project) { create(:project) }
+ let(:project_board) { create(:board, project: project) }
+
+ subject(:service) { described_class.new(project_board.parent, user) }
+
+ it 'returns nil when there is no user' do
+ service.current_user = nil
+
+ expect(service.execute).to eq nil
+ end
+
+ it 'queries for most recent visit' do
+ expect(BoardProjectRecentVisit).to receive(:latest).once
+
+ service.execute
+ end
+ end
+
+ context 'when a group board' do
+ let(:group) { create(:group) }
+ let(:group_board) { create(:board, group: group) }
+
+ subject(:service) { described_class.new(group_board.parent, user) }
+
+ it 'returns nil when there is no user' do
+ service.current_user = nil
+
+ expect(service.execute).to eq nil
+ end
+
+ it 'queries for most recent visit' do
+ expect(BoardGroupRecentVisit).to receive(:latest).once
+
+ service.execute
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 054b7b1561c..4d9c5aabbda 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -387,15 +387,24 @@ describe Ci::CreatePipelineService do
context 'with environment' do
before do
- config = YAML.dump(deploy: { environment: { name: "review/$CI_COMMIT_REF_NAME" }, script: 'ls' })
+ config = YAML.dump(
+ deploy: {
+ environment: { name: "review/$CI_COMMIT_REF_NAME" },
+ script: 'ls',
+ tags: ['hello']
+ })
+
stub_ci_pipeline_yaml_file(config)
end
- it 'creates the environment' do
+ it 'creates the environment with tags' do
result = execute_service
expect(result).to be_persisted
expect(Environment.find_by(name: "review/master")).to be_present
+ expect(result.builds.first.tag_list).to contain_exactly('hello')
+ expect(result.builds.first.deployment).to be_persisted
+ expect(result.builds.first.deployment.deployable).to be_a(Ci::Build)
end
end
@@ -435,16 +444,34 @@ describe Ci::CreatePipelineService do
end
context 'when builds with auto-retries are configured' do
- before do
- config = YAML.dump(rspec: { script: 'rspec', retry: 2 })
- stub_ci_pipeline_yaml_file(config)
+ context 'as an integer' do
+ before do
+ config = YAML.dump(rspec: { script: 'rspec', retry: 2 })
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'correctly creates builds with auto-retry value configured' do
+ pipeline = execute_service
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.builds.find_by(name: 'rspec').retries_max).to eq 2
+ expect(pipeline.builds.find_by(name: 'rspec').retry_when).to eq ['always']
+ end
end
- it 'correctly creates builds with auto-retry value configured' do
- pipeline = execute_service
+ context 'as hash' do
+ before do
+ config = YAML.dump(rspec: { script: 'rspec', retry: { max: 2, when: 'runner_system_failure' } })
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'correctly creates builds with auto-retry value configured' do
+ pipeline = execute_service
- expect(pipeline).to be_persisted
- expect(pipeline.builds.find_by(name: 'rspec').retries_max).to eq 2
+ expect(pipeline).to be_persisted
+ expect(pipeline.builds.find_by(name: 'rspec').retries_max).to eq 2
+ expect(pipeline.builds.find_by(name: 'rspec').retry_when).to eq ['runner_system_failure']
+ end
end
end
@@ -581,5 +608,53 @@ describe Ci::CreatePipelineService do
.to eq variables_attributes.map(&:with_indifferent_access)
end
end
+
+ context 'when pipeline has a job with environment' do
+ let(:pipeline) { execute_service }
+
+ before do
+ stub_ci_pipeline_yaml_file(YAML.dump(config))
+ end
+
+ context 'when environment name is valid' do
+ let(:config) do
+ {
+ review_app: {
+ script: 'deploy',
+ environment: {
+ name: 'review/${CI_COMMIT_REF_NAME}',
+ url: 'http://${CI_COMMIT_REF_SLUG}-staging.example.com'
+ }
+ }
+ }
+ end
+
+ it 'has a job with environment' do
+ expect(pipeline.builds.count).to eq(1)
+ expect(pipeline.builds.first.persisted_environment.name).to eq('review/master')
+ expect(pipeline.builds.first.deployment).to be_created
+ end
+ end
+
+ context 'when environment name is invalid' do
+ let(:config) do
+ {
+ 'job:deploy-to-test-site': {
+ script: 'deploy',
+ environment: {
+ name: '${CI_JOB_NAME}',
+ url: 'https://$APP_URL'
+ }
+ }
+ }
+ end
+
+ it 'has a job without environment' do
+ expect(pipeline.builds.count).to eq(1)
+ expect(pipeline.builds.first.persisted_environment).to be_nil
+ expect(pipeline.builds.first.deployment).to be_nil
+ end
+ end
+ end
end
end
diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb
new file mode 100644
index 00000000000..097daf67feb
--- /dev/null
+++ b/spec/services/ci/destroy_pipeline_service_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ::Ci::DestroyPipelineService do
+ let(:project) { create(:project) }
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+
+ subject { described_class.new(project, user).execute(pipeline) }
+
+ context 'user is owner' do
+ let(:user) { project.owner }
+
+ it 'destroys the pipeline' do
+ subject
+
+ expect { pipeline.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'logs an audit event' do
+ expect { subject }.to change { SecurityEvent.count }.by(1)
+ end
+
+ context 'when the pipeline has jobs' do
+ let!(:build) { create(:ci_build, project: project, pipeline: pipeline) }
+
+ it 'destroys associated jobs' do
+ subject
+
+ expect { build.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'destroys associated stages' do
+ stages = pipeline.stages
+
+ subject
+
+ expect(stages).to all(raise_error(ActiveRecord::RecordNotFound))
+ end
+
+ context 'when job has artifacts' do
+ let!(:artifact) { create(:ci_job_artifact, :archive, job: build) }
+
+ it 'destroys associated artifacts' do
+ subject
+
+ expect { artifact.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
+ end
+
+ context 'user is not owner' do
+ let(:user) { create(:user) }
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
+ end
+end
diff --git a/spec/services/ci/enqueue_build_service_spec.rb b/spec/services/ci/enqueue_build_service_spec.rb
deleted file mode 100644
index e41b8e4800b..00000000000
--- a/spec/services/ci/enqueue_build_service_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-describe Ci::EnqueueBuildService, '#execute' do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:ci_build) { create(:ci_build, :created) }
-
- subject { described_class.new(project, user).execute(ci_build) }
-
- it 'enqueues the build' do
- subject
-
- expect(ci_build.pending?).to be_truthy
- end
-end
diff --git a/spec/services/ci/fetch_kubernetes_token_service_spec.rb b/spec/services/ci/fetch_kubernetes_token_service_spec.rb
deleted file mode 100644
index 1d05c9671a9..00000000000
--- a/spec/services/ci/fetch_kubernetes_token_service_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-require 'spec_helper'
-
-describe Ci::FetchKubernetesTokenService do
- describe '#execute' do
- subject { described_class.new(api_url, ca_pem, username, password).execute }
-
- let(:api_url) { 'http://111.111.111.111' }
- let(:ca_pem) { '' }
- let(:username) { 'admin' }
- let(:password) { 'xxx' }
-
- context 'when params correct' do
- let(:token) { 'xxx.token.xxx' }
-
- let(:secrets_json) do
- [
- {
- 'metadata': {
- name: metadata_name
- },
- 'data': {
- 'token': Base64.encode64(token)
- }
- }
- ]
- end
-
- before do
- allow_any_instance_of(Kubeclient::Client)
- .to receive(:get_secrets).and_return(secrets_json)
- end
-
- context 'when default-token exists' do
- let(:metadata_name) { 'default-token-123' }
-
- it { is_expected.to eq(token) }
- end
-
- context 'when default-token does not exist' do
- let(:metadata_name) { 'another-token-123' }
-
- it { is_expected.to be_nil }
- end
- end
-
- context 'when api_url is nil' do
- let(:api_url) { nil }
-
- it { expect { subject }.to raise_error("Incomplete settings") }
- end
-
- context 'when username is nil' do
- let(:username) { nil }
-
- it { expect { subject }.to raise_error("Incomplete settings") }
- end
-
- context 'when password is nil' do
- let(:password) { nil }
-
- it { expect { subject }.to raise_error("Incomplete settings") }
- end
- end
-end
diff --git a/spec/services/ci/process_build_service_spec.rb b/spec/services/ci/process_build_service_spec.rb
new file mode 100644
index 00000000000..704685417bb
--- /dev/null
+++ b/spec/services/ci/process_build_service_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Ci::ProcessBuildService, '#execute' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ subject { described_class.new(project, user).execute(build, current_status) }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'when build has on_success option' do
+ let(:build) { create(:ci_build, :created, when: :on_success, user: user, project: project) }
+
+ context 'when current status is success' do
+ let(:current_status) { 'success' }
+
+ it 'changes the build status' do
+ expect { subject }.to change { build.status }.to('pending')
+ end
+ end
+
+ context 'when current status is failed' do
+ let(:current_status) { 'failed' }
+
+ it 'does not change the build status' do
+ expect { subject }.to change { build.status }.to('skipped')
+ end
+ end
+ end
+
+ context 'when build has on_failure option' do
+ let(:build) { create(:ci_build, :created, when: :on_failure, user: user, project: project) }
+
+ context 'when current status is success' do
+ let(:current_status) { 'success' }
+
+ it 'changes the build status' do
+ expect { subject }.to change { build.status }.to('skipped')
+ end
+ end
+
+ context 'when current status is failed' do
+ let(:current_status) { 'failed' }
+
+ it 'does not change the build status' do
+ expect { subject }.to change { build.status }.to('pending')
+ end
+ end
+ end
+
+ context 'when build has always option' do
+ let(:build) { create(:ci_build, :created, when: :always, user: user, project: project) }
+
+ context 'when current status is success' do
+ let(:current_status) { 'success' }
+
+ it 'changes the build status' do
+ expect { subject }.to change { build.status }.to('pending')
+ end
+ end
+
+ context 'when current status is failed' do
+ let(:current_status) { 'failed' }
+
+ it 'does not change the build status' do
+ expect { subject }.to change { build.status }.to('pending')
+ end
+ end
+ end
+
+ context 'when build has manual option' do
+ let(:build) { create(:ci_build, :created, :actionable, user: user, project: project) }
+
+ context 'when current status is success' do
+ let(:current_status) { 'success' }
+
+ it 'changes the build status' do
+ expect { subject }.to change { build.status }.to('manual')
+ end
+ end
+
+ context 'when current status is failed' do
+ let(:current_status) { 'failed' }
+
+ it 'does not change the build status' do
+ expect { subject }.to change { build.status }.to('skipped')
+ end
+ end
+ end
+
+ context 'when build has delayed option' do
+ before do
+ allow(Ci::BuildScheduleWorker).to receive(:perform_at) { }
+ end
+
+ let(:build) { create(:ci_build, :created, :schedulable, user: user, project: project) }
+
+ context 'when current status is success' do
+ let(:current_status) { 'success' }
+
+ it 'changes the build status' do
+ expect { subject }.to change { build.status }.to('scheduled')
+ end
+ end
+
+ context 'when current status is failed' do
+ let(:current_status) { 'failed' }
+
+ it 'does not change the build status' do
+ expect { subject }.to change { build.status }.to('skipped')
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb
index feb5120bc68..538992b621e 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -31,17 +31,14 @@ describe Ci::ProcessPipelineService, '#execute' do
succeed_pending
expect(builds.success.count).to eq(2)
- expect(process_pipeline).to be_truthy
succeed_pending
expect(builds.success.count).to eq(4)
- expect(process_pipeline).to be_truthy
succeed_pending
expect(builds.success.count).to eq(5)
- expect(process_pipeline).to be_falsey
end
it 'does not process pipeline if existing stage is running' do
@@ -242,6 +239,187 @@ describe Ci::ProcessPipelineService, '#execute' do
end
end
+ context 'when delayed jobs are defined' do
+ context 'when the scene is timed incremental rollout' do
+ before do
+ create_build('build', stage_idx: 0)
+ create_build('rollout10%', **delayed_options, stage_idx: 1)
+ create_build('rollout100%', **delayed_options, stage_idx: 2)
+ create_build('cleanup', stage_idx: 3)
+
+ allow(Ci::BuildScheduleWorker).to receive(:perform_at)
+ end
+
+ context 'when builds are successful' do
+ it 'properly processes the pipeline' do
+ expect(process_pipeline).to be_truthy
+ expect(builds_names_and_statuses).to eq({ 'build': 'pending' })
+
+ succeed_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'scheduled' })
+
+ enqueue_scheduled('rollout10%')
+ succeed_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'success', 'rollout100%': 'scheduled' })
+
+ enqueue_scheduled('rollout100%')
+ succeed_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'success', 'rollout100%': 'success', 'cleanup': 'pending' })
+
+ succeed_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'success', 'rollout100%': 'success', 'cleanup': 'success' })
+ expect(pipeline.reload.status).to eq 'success'
+ end
+ end
+
+ context 'when build job fails' do
+ it 'properly processes the pipeline' do
+ expect(process_pipeline).to be_truthy
+ expect(builds_names_and_statuses).to eq({ 'build': 'pending' })
+
+ fail_running_or_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'failed' })
+ expect(pipeline.reload.status).to eq 'failed'
+ end
+ end
+
+ context 'when rollout 10% is unscheduled' do
+ it 'properly processes the pipeline' do
+ expect(process_pipeline).to be_truthy
+ expect(builds_names_and_statuses).to eq({ 'build': 'pending' })
+
+ succeed_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'scheduled' })
+
+ unschedule
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'manual' })
+ expect(pipeline.reload.status).to eq 'manual'
+ end
+
+ context 'when user plays rollout 10%' do
+ it 'schedules rollout100%' do
+ process_pipeline
+ succeed_pending
+ unschedule
+ play_manual_action('rollout10%')
+ succeed_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'success', 'rollout100%': 'scheduled' })
+ expect(pipeline.reload.status).to eq 'scheduled'
+ end
+ end
+ end
+
+ context 'when rollout 10% fails' do
+ it 'properly processes the pipeline' do
+ expect(process_pipeline).to be_truthy
+ expect(builds_names_and_statuses).to eq({ 'build': 'pending' })
+
+ succeed_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'scheduled' })
+
+ enqueue_scheduled('rollout10%')
+ fail_running_or_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'failed' })
+ expect(pipeline.reload.status).to eq 'failed'
+ end
+
+ context 'when user retries rollout 10%' do
+ it 'does not schedule rollout10% again' do
+ process_pipeline
+ succeed_pending
+ enqueue_scheduled('rollout10%')
+ fail_running_or_pending
+ retry_build('rollout10%')
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'pending' })
+ expect(pipeline.reload.status).to eq 'running'
+ end
+ end
+ end
+
+ context 'when rollout 10% is played immidiately' do
+ it 'properly processes the pipeline' do
+ expect(process_pipeline).to be_truthy
+ expect(builds_names_and_statuses).to eq({ 'build': 'pending' })
+
+ succeed_pending
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'scheduled' })
+
+ play_manual_action('rollout10%')
+
+ expect(builds_names_and_statuses).to eq({ 'build': 'success', 'rollout10%': 'pending' })
+ expect(pipeline.reload.status).to eq 'running'
+ end
+ end
+ end
+
+ context 'when only one scheduled job exists in a pipeline' do
+ before do
+ create_build('delayed', **delayed_options, stage_idx: 0)
+
+ allow(Ci::BuildScheduleWorker).to receive(:perform_at)
+ end
+
+ it 'properly processes the pipeline' do
+ expect(process_pipeline).to be_truthy
+ expect(builds_names_and_statuses).to eq({ 'delayed': 'scheduled' })
+
+ expect(pipeline.reload.status).to eq 'scheduled'
+ end
+ end
+
+ context 'when there are two delayed jobs in a stage' do
+ before do
+ create_build('delayed1', **delayed_options, stage_idx: 0)
+ create_build('delayed2', **delayed_options, stage_idx: 0)
+ create_build('job', stage_idx: 1)
+
+ allow(Ci::BuildScheduleWorker).to receive(:perform_at)
+ end
+
+ it 'blocks the stage until all scheduled jobs finished' do
+ expect(process_pipeline).to be_truthy
+ expect(builds_names_and_statuses).to eq({ 'delayed1': 'scheduled', 'delayed2': 'scheduled' })
+
+ enqueue_scheduled('delayed1')
+
+ expect(builds_names_and_statuses).to eq({ 'delayed1': 'pending', 'delayed2': 'scheduled' })
+ expect(pipeline.reload.status).to eq 'running'
+ end
+ end
+
+ context 'when a delayed job is allowed to fail' do
+ before do
+ create_build('delayed', **delayed_options, allow_failure: true, stage_idx: 0)
+ create_build('job', stage_idx: 1)
+
+ allow(Ci::BuildScheduleWorker).to receive(:perform_at)
+ end
+
+ it 'blocks the stage and continues after it failed' do
+ expect(process_pipeline).to be_truthy
+ expect(builds_names_and_statuses).to eq({ 'delayed': 'scheduled' })
+
+ enqueue_scheduled('delayed')
+ fail_running_or_pending
+
+ expect(builds_names_and_statuses).to eq({ 'delayed': 'failed', 'job': 'pending' })
+ expect(pipeline.reload.status).to eq 'pending'
+ end
+ end
+ end
+
context 'when there are manual action in earlier stages' do
context 'when first stage has only optional manual actions' do
before do
@@ -493,9 +671,9 @@ describe Ci::ProcessPipelineService, '#execute' do
context 'when builds with auto-retries are configured' do
before do
- create_build('build:1', stage_idx: 0, user: user, options: { retry: 2 })
+ create_build('build:1', stage_idx: 0, user: user, options: { retry: { max: 2 } })
create_build('test:1', stage_idx: 1, user: user, when: :on_failure)
- create_build('test:2', stage_idx: 1, user: user, options: { retry: 1 })
+ create_build('test:2', stage_idx: 1, user: user, options: { retry: { max: 1 } })
end
it 'automatically retries builds in a valid order' do
@@ -536,6 +714,13 @@ describe Ci::ProcessPipelineService, '#execute' do
builds.pluck(:name)
end
+ def builds_names_and_statuses
+ builds.each_with_object({}) do |b, h|
+ h[b.name.to_sym] = b.status
+ h
+ end
+ end
+
def all_builds_names
all_builds.pluck(:name)
end
@@ -549,7 +734,7 @@ describe Ci::ProcessPipelineService, '#execute' do
end
def succeed_pending
- builds.pending.update_all(status: 'success')
+ builds.pending.map(&:success)
end
def succeed_running_or_pending
@@ -568,6 +753,14 @@ describe Ci::ProcessPipelineService, '#execute' do
builds.find_by(name: name).play(user)
end
+ def enqueue_scheduled(name)
+ builds.scheduled.find_by(name: name).enqueue
+ end
+
+ def retry_build(name)
+ Ci::Build.retry(builds.find_by(name: name), user)
+ end
+
def manual_actions
pipeline.manual_actions(true)
end
@@ -575,4 +768,12 @@ describe Ci::ProcessPipelineService, '#execute' do
def create_build(name, **opts)
create(:ci_build, :created, pipeline: pipeline, name: name, **opts)
end
+
+ def delayed_options
+ { when: 'delayed', options: { start_in: '1 minute' } }
+ end
+
+ def unschedule
+ pipeline.builds.scheduled.map(&:unschedule)
+ end
end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index a6565709641..56e2a405bcd 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -478,6 +478,20 @@ module Ci
it_behaves_like 'validation is not active'
end
end
+
+ context 'when build is degenerated' do
+ let!(:pending_job) { create(:ci_build, :pending, :degenerated, pipeline: pipeline) }
+
+ subject { execute(specific_runner, {}) }
+
+ it 'does not pick the build and drops the build' do
+ expect(subject).to be_nil
+
+ pending_job.reload
+ expect(pending_job).to be_failed
+ expect(pending_job).to be_archived_failure
+ end
+ end
end
describe '#register_success' do
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index 18d52082399..e779675744c 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -24,11 +24,15 @@ describe Ci::RetryBuildService do
artifacts_file artifacts_metadata artifacts_size created_at
updated_at started_at finished_at queued_at erased_by
erased_at auto_canceled_by job_artifacts job_artifacts_archive
- job_artifacts_metadata job_artifacts_trace job_artifacts_junit].freeze
+ job_artifacts_metadata job_artifacts_trace job_artifacts_junit
+ job_artifacts_sast job_artifacts_dependency_scanning
+ job_artifacts_container_scanning job_artifacts_dast
+ job_artifacts_license_management job_artifacts_performance
+ job_artifacts_codequality scheduled_at].freeze
IGNORE_ACCESSORS =
%i[type lock_version target_url base_tags trace_sections
- commit_id deployments erased_by_id last_deployment project_id
+ commit_id deployment erased_by_id project_id
runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason
artifacts_file_store artifacts_metadata_store
@@ -38,11 +42,11 @@ describe Ci::RetryBuildService do
let(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
let(:build) do
- create(:ci_build, :failed, :artifacts, :test_reports, :expired, :erased,
- :queued, :coverage, :tags, :allowed_to_fail, :on_tag,
- :triggered, :trace_artifact, :teardown_environment,
+ create(:ci_build, :failed, :expired, :erased, :queued, :coverage, :tags,
+ :allowed_to_fail, :on_tag, :triggered, :teardown_environment,
description: 'my-job', stage: 'test', stage_id: stage.id,
- pipeline: pipeline, auto_canceled_by: another_pipeline)
+ pipeline: pipeline, auto_canceled_by: another_pipeline,
+ scheduled_at: 10.seconds.since)
end
before do
@@ -50,6 +54,15 @@ describe Ci::RetryBuildService do
# can reset one of the fields when assigning another. We plan to deprecate
# and remove legacy `stage` column in the future.
build.update(stage: 'test', stage_id: stage.id)
+
+ # Make sure we have one instance for every possible job_artifact_X
+ # associations to check they are correctly rejected on build duplication.
+ Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS.each do |file_type, file_format|
+ create(:ci_job_artifact, file_format,
+ file_type: file_type, job: build, expire_at: build.artifacts_expire_at)
+ end
+
+ build.reload
end
describe 'clone accessors' do
diff --git a/spec/services/ci/run_scheduled_build_service_spec.rb b/spec/services/ci/run_scheduled_build_service_spec.rb
new file mode 100644
index 00000000000..be2aad33ef4
--- /dev/null
+++ b/spec/services/ci/run_scheduled_build_service_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+
+describe Ci::RunScheduledBuildService do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ subject { described_class.new(project, user).execute(build) }
+
+ context 'when user can update build' do
+ before do
+ project.add_developer(user)
+
+ create(:protected_branch, :developers_can_merge,
+ name: pipeline.ref, project: project)
+ end
+
+ context 'when build is scheduled' do
+ context 'when scheduled_at is expired' do
+ let(:build) { create(:ci_build, :expired_scheduled, user: user, project: project, pipeline: pipeline) }
+
+ it 'can run the build' do
+ expect { subject }.not_to raise_error
+
+ expect(build).to be_pending
+ end
+ end
+
+ context 'when scheduled_at is not expired' do
+ let(:build) { create(:ci_build, :scheduled, user: user, project: project, pipeline: pipeline) }
+
+ it 'can not run the build' do
+ expect { subject }.to raise_error(StateMachines::InvalidTransition)
+
+ expect(build).to be_scheduled
+ end
+ end
+ end
+
+ context 'when build is not scheduled' do
+ let(:build) { create(:ci_build, :created, user: user, project: project, pipeline: pipeline) }
+
+ it 'can not run the build' do
+ expect { subject }.to raise_error(StateMachines::InvalidTransition)
+
+ expect(build).to be_created
+ end
+ end
+ end
+
+ context 'when user can not update build' do
+ context 'when build is scheduled' do
+ let(:build) { create(:ci_build, :scheduled, user: user, project: project, pipeline: pipeline) }
+
+ it 'can not run the build' do
+ expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError)
+
+ expect(build).to be_scheduled
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/applications/check_installation_progress_service_spec.rb b/spec/services/clusters/applications/check_installation_progress_service_spec.rb
index 986f11410fd..ea17f2bb423 100644
--- a/spec/services/clusters/applications/check_installation_progress_service_spec.rb
+++ b/spec/services/clusters/applications/check_installation_progress_service_spec.rb
@@ -19,6 +19,10 @@ describe Clusters::Applications::CheckInstallationProgressService do
shared_examples 'a not yet terminated installation' do |a_phase|
let(:phase) { a_phase }
+ before do
+ expect(service).to receive(:installation_phase).once.and_return(phase)
+ end
+
context "when phase is #{a_phase}" do
context 'when not timeouted' do
it 'reschedule a new check' do
@@ -50,8 +54,6 @@ describe Clusters::Applications::CheckInstallationProgressService do
end
before do
- expect(service).to receive(:installation_phase).once.and_return(phase)
-
allow(service).to receive(:installation_errors).and_return(errors)
allow(service).to receive(:remove_installation_pod).and_return(nil)
end
@@ -60,6 +62,10 @@ describe Clusters::Applications::CheckInstallationProgressService do
context 'when installation POD succeeded' do
let(:phase) { Gitlab::Kubernetes::Pod::SUCCEEDED }
+ before do
+ expect(service).to receive(:installation_phase).once.and_return(phase)
+ end
+
it_behaves_like 'a terminated installation'
it 'make the application installed' do
@@ -76,16 +82,37 @@ describe Clusters::Applications::CheckInstallationProgressService do
let(:phase) { Gitlab::Kubernetes::Pod::FAILED }
let(:errors) { 'test installation failed' }
+ before do
+ expect(service).to receive(:installation_phase).once.and_return(phase)
+ end
+
it_behaves_like 'a terminated installation'
it 'make the application errored' do
service.execute
expect(application).to be_errored
- expect(application.status_reason).to eq(errors)
+ expect(application.status_reason).to eq("Installation failed")
end
end
RESCHEDULE_PHASES.each { |phase| it_behaves_like 'a not yet terminated installation', phase }
+
+ context 'when installation raises a Kubeclient::HttpError' do
+ let(:cluster) { create(:cluster, :provided_by_user, :project) }
+
+ before do
+ application.update!(cluster: cluster)
+
+ expect(service).to receive(:installation_phase).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil))
+ end
+
+ it 'shows the response code from the error' do
+ service.execute
+
+ expect(application).to be_errored
+ expect(application.status_reason).to eq('Kubernetes error: 401')
+ end
+ end
end
end
diff --git a/spec/services/clusters/applications/create_service_spec.rb b/spec/services/clusters/applications/create_service_spec.rb
new file mode 100644
index 00000000000..0bd7719345e
--- /dev/null
+++ b/spec/services/clusters/applications/create_service_spec.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::CreateService do
+ include TestRequestHelpers
+
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+ let(:user) { create(:user) }
+ let(:params) { { application: 'helm' } }
+ let(:service) { described_class.new(cluster, user, params) }
+
+ describe '#execute' do
+ before do
+ allow(ClusterInstallAppWorker).to receive(:perform_async)
+ end
+
+ subject { service.execute(test_request) }
+
+ it 'creates an application' do
+ expect do
+ subject
+
+ cluster.reload
+ end.to change(cluster, :application_helm)
+ end
+
+ it 'schedules an install via worker' do
+ expect(ClusterInstallAppWorker).to receive(:perform_async).with('helm', anything).once
+
+ subject
+ end
+
+ context 'jupyter application' do
+ let(:params) do
+ {
+ application: 'jupyter',
+ hostname: 'example.com'
+ }
+ end
+
+ before do
+ allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute)
+ end
+
+ it 'creates the application' do
+ expect do
+ subject
+
+ cluster.reload
+ end.to change(cluster, :application_jupyter)
+ end
+
+ it 'sets the hostname' do
+ expect(subject.hostname).to eq('example.com')
+ end
+
+ it 'sets the oauth_application' do
+ expect(subject.oauth_application).to be_present
+ end
+ end
+
+ context 'knative application' do
+ let(:params) do
+ {
+ application: 'knative',
+ hostname: 'example.com'
+ }
+ end
+
+ before do
+ allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute)
+ end
+
+ it 'creates the application' do
+ expect do
+ subject
+
+ cluster.reload
+ end.to change(cluster, :application_knative)
+ end
+
+ it 'sets the hostname' do
+ expect(subject.hostname).to eq('example.com')
+ end
+ end
+
+ context 'invalid application' do
+ let(:params) { { application: 'non-existent' } }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Clusters::Applications::CreateService::InvalidApplicationError)
+ end
+ end
+
+ context 'group cluster' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, :group) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ before do
+ allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute)
+ end
+
+ where(:application, :association, :allowed) do
+ 'helm' | :application_helm | true
+ 'ingress' | :application_ingress | true
+ 'runner' | :application_runner | false
+ 'jupyter' | :application_jupyter | false
+ 'prometheus' | :application_prometheus | false
+ end
+
+ with_them do
+ let(:params) { { application: application } }
+
+ it 'executes for each application' do
+ if allowed
+ expect do
+ subject
+
+ cluster.reload
+ end.to change(cluster, association)
+ else
+ expect { subject }.to raise_error(Clusters::Applications::CreateService::InvalidApplicationError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/applications/install_service_spec.rb b/spec/services/clusters/applications/install_service_spec.rb
index a744ec30b65..2f801d019fe 100644
--- a/spec/services/clusters/applications/install_service_spec.rb
+++ b/spec/services/clusters/applications/install_service_spec.rb
@@ -42,7 +42,7 @@ describe Clusters::Applications::InstallService do
service.execute
expect(application).to be_errored
- expect(application.status_reason).to match(/kubernetes error:/i)
+ expect(application.status_reason).to match('Kubernetes error: 500')
end
end
diff --git a/spec/services/clusters/applications/schedule_installation_service_spec.rb b/spec/services/clusters/applications/schedule_installation_service_spec.rb
index bca1e71bef2..21797edd533 100644
--- a/spec/services/clusters/applications/schedule_installation_service_spec.rb
+++ b/spec/services/clusters/applications/schedule_installation_service_spec.rb
@@ -10,14 +10,13 @@ describe Clusters::Applications::ScheduleInstallationService do
expect(ClusterInstallAppWorker).not_to receive(:perform_async)
count_before = count_scheduled
- expect { service.execute(application) }.to raise_error(StandardError)
+ expect { service.execute }.to raise_error(StandardError)
expect(count_scheduled).to eq(count_before)
end
end
describe '#execute' do
- let(:project) { double(:project) }
- let(:service) { described_class.new(project, nil) }
+ let(:service) { described_class.new(application) }
context 'when application is installable' do
let(:application) { create(:clusters_applications_helm, :installable) }
@@ -25,7 +24,7 @@ describe Clusters::Applications::ScheduleInstallationService do
it 'make the application scheduled' do
expect(ClusterInstallAppWorker).to receive(:perform_async).with(application.name, kind_of(Numeric)).once
- expect { service.execute(application) }.to change { application.class.with_status(:scheduled).count }.by(1)
+ expect { service.execute }.to change { application.class.with_status(:scheduled).count }.by(1)
end
end
diff --git a/spec/services/clusters/create_service_spec.rb b/spec/services/clusters/create_service_spec.rb
index 1685dc748bd..274880f2c49 100644
--- a/spec/services/clusters/create_service_spec.rb
+++ b/spec/services/clusters/create_service_spec.rb
@@ -5,18 +5,43 @@ describe Clusters::CreateService do
let(:project) { create(:project) }
let(:user) { create(:user) }
- subject { described_class.new(project, user, params).execute(access_token) }
+ subject { described_class.new(user, params).execute(access_token: access_token) }
context 'when provider is gcp' do
context 'when project has no clusters' do
context 'when correct params' do
- include_context 'valid cluster create params'
+ let(:params) do
+ {
+ name: 'test-cluster',
+ provider_type: :gcp,
+ provider_gcp_attributes: {
+ gcp_project_id: 'gcp-project',
+ zone: 'us-central1-a',
+ num_nodes: 1,
+ machine_type: 'machine_type-a',
+ legacy_abac: 'true'
+ },
+ clusterable: project
+ }
+ end
include_examples 'create cluster service success'
end
context 'when invalid params' do
- include_context 'invalid cluster create params'
+ let(:params) do
+ {
+ name: 'test-cluster',
+ provider_type: :gcp,
+ provider_gcp_attributes: {
+ gcp_project_id: '!!!!!!!',
+ zone: 'us-central1-a',
+ num_nodes: 1,
+ machine_type: 'machine_type-a'
+ },
+ clusterable: project
+ }
+ end
include_examples 'create cluster service error'
end
diff --git a/spec/services/clusters/gcp/fetch_operation_service_spec.rb b/spec/services/clusters/gcp/fetch_operation_service_spec.rb
index e2fa93904c5..55f123ee786 100644
--- a/spec/services/clusters/gcp/fetch_operation_service_spec.rb
+++ b/spec/services/clusters/gcp/fetch_operation_service_spec.rb
@@ -24,7 +24,7 @@ describe Clusters::Gcp::FetchOperationService do
end
end
- context 'when suceeded to fetch operation' do
+ context 'when succeeded to fetch operation' do
before do
stub_cloud_platform_get_zone_operation(gcp_project_id, zone, operation_id)
end
diff --git a/spec/services/clusters/gcp/finalize_creation_service_spec.rb b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
index 0cf91307589..efee158739d 100644
--- a/spec/services/clusters/gcp/finalize_creation_service_spec.rb
+++ b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
@@ -1,111 +1,176 @@
+# frozen_string_literal: true
+
require 'spec_helper'
-describe Clusters::Gcp::FinalizeCreationService do
+describe Clusters::Gcp::FinalizeCreationService, '#execute' do
include GoogleApi::CloudPlatformHelpers
include KubernetesHelpers
- describe '#execute' do
- let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
- let(:provider) { cluster.provider }
- let(:platform) { cluster.platform }
- let(:gcp_project_id) { provider.gcp_project_id }
- let(:zone) { provider.zone }
- let(:cluster_name) { cluster.name }
+ let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
+ let(:provider) { cluster.provider }
+ let(:platform) { cluster.platform }
+ let(:endpoint) { '111.111.111.111' }
+ let(:api_url) { 'https://' + endpoint }
+ let(:username) { 'sample-username' }
+ let(:password) { 'sample-password' }
+ let(:secret_name) { 'gitlab-token' }
+ let(:token) { 'sample-token' }
+ let(:namespace) { "#{cluster.project.path}-#{cluster.project.id}" }
- shared_examples 'success' do
- it 'configures provider and kubernetes' do
- described_class.new.execute(provider)
+ subject { described_class.new.execute(provider) }
- expect(provider).to be_created
- end
+ shared_examples 'success' do
+ it 'configures provider and kubernetes' do
+ subject
+
+ expect(provider).to be_created
end
- shared_examples 'error' do
- it 'sets an error to provider object' do
- described_class.new.execute(provider)
+ it 'properly configures database models' do
+ subject
- expect(provider.reload).to be_errored
- end
+ cluster.reload
+
+ expect(provider.endpoint).to eq(endpoint)
+ expect(platform.api_url).to eq(api_url)
+ expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert).strip)
+ expect(platform.username).to eq(username)
+ expect(platform.password).to eq(password)
+ expect(platform.token).to eq(token)
end
- context 'when suceeded to fetch gke cluster info' do
- let(:endpoint) { '111.111.111.111' }
- let(:api_url) { 'https://' + endpoint }
- let(:username) { 'sample-username' }
- let(:password) { 'sample-password' }
+ it 'creates kubernetes namespace model' do
+ subject
- before do
- stub_cloud_platform_get_zone_cluster(
- gcp_project_id, zone, cluster_name,
- {
- endpoint: endpoint,
- username: username,
- password: password
- }
- )
-
- stub_kubeclient_discover(api_url)
- end
+ kubernetes_namespace = cluster.reload.kubernetes_namespace
+ expect(kubernetes_namespace).to be_persisted
+ expect(kubernetes_namespace.namespace).to eq(namespace)
+ expect(kubernetes_namespace.service_account_name).to eq("#{namespace}-service-account")
+ expect(kubernetes_namespace.service_account_token).to be_present
+ end
+ end
- context 'when suceeded to fetch kuberenetes token' do
- let(:token) { 'sample-token' }
-
- before do
- stub_kubeclient_get_secrets(
- api_url,
- {
- token: Base64.encode64(token)
- } )
- end
-
- it_behaves_like 'success'
-
- it 'has corresponded data' do
- described_class.new.execute(provider)
- cluster.reload
- provider.reload
- platform.reload
-
- expect(provider.endpoint).to eq(endpoint)
- expect(platform.api_url).to eq(api_url)
- expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert))
- expect(platform.username).to eq(username)
- expect(platform.password).to eq(password)
- expect(platform.token).to eq(token)
- end
- end
+ shared_examples 'error' do
+ it 'sets an error to provider object' do
+ subject
- context 'when default-token is not found' do
- before do
- stub_kubeclient_get_secrets(api_url, metadata_name: 'aaaa')
- end
+ expect(provider.reload).to be_errored
+ end
+ end
- it_behaves_like 'error'
+ shared_examples 'kubernetes information not successfully fetched' do
+ context 'when failed to fetch gke cluster info' do
+ before do
+ stub_cloud_platform_get_zone_cluster_error(provider.gcp_project_id, provider.zone, cluster.name)
end
- context 'when token is empty' do
- before do
- stub_kubeclient_get_secrets(api_url, token: '')
- end
+ it_behaves_like 'error'
+ end
- it_behaves_like 'error'
- end
+ context 'when token is empty' do
+ let(:token) { '' }
- context 'when failed to fetch kuberenetes token' do
- before do
- stub_kubeclient_get_secrets_error(api_url)
- end
+ it_behaves_like 'error'
+ end
- it_behaves_like 'error'
+ context 'when failed to fetch kubernetes token' do
+ before do
+ stub_kubeclient_get_secret_error(api_url, secret_name, namespace: 'default')
end
+
+ it_behaves_like 'error'
end
- context 'when failed to fetch gke cluster info' do
+ context 'when service account fails to create' do
before do
- stub_cloud_platform_get_zone_cluster_error(gcp_project_id, zone, cluster_name)
+ stub_kubeclient_create_service_account_error(api_url, namespace: 'default')
end
it_behaves_like 'error'
end
end
+
+ shared_context 'kubernetes information successfully fetched' do
+ before do
+ stub_cloud_platform_get_zone_cluster(
+ provider.gcp_project_id, provider.zone, cluster.name,
+ {
+ endpoint: endpoint,
+ username: username,
+ password: password
+ }
+ )
+
+ stub_kubeclient_discover(api_url)
+ stub_kubeclient_get_namespace(api_url)
+ stub_kubeclient_create_namespace(api_url)
+ stub_kubeclient_create_service_account(api_url)
+ stub_kubeclient_create_secret(api_url)
+
+ stub_kubeclient_get_secret(
+ api_url,
+ {
+ metadata_name: secret_name,
+ token: Base64.encode64(token),
+ namespace: 'default'
+ }
+ )
+
+ stub_kubeclient_get_namespace(api_url, namespace: namespace)
+ stub_kubeclient_create_service_account(api_url, namespace: namespace)
+ stub_kubeclient_create_secret(api_url, namespace: namespace)
+
+ stub_kubeclient_get_secret(
+ api_url,
+ {
+ metadata_name: "#{namespace}-token",
+ token: Base64.encode64(token),
+ namespace: namespace
+ }
+ )
+ end
+ end
+
+ context 'With a legacy ABAC cluster' do
+ before do
+ provider.legacy_abac = true
+ end
+
+ include_context 'kubernetes information successfully fetched'
+
+ it_behaves_like 'success'
+
+ it 'uses ABAC authorization type' do
+ subject
+ cluster.reload
+
+ expect(platform).to be_abac
+ expect(platform.authorization_type).to eq('abac')
+ end
+
+ it_behaves_like 'kubernetes information not successfully fetched'
+ end
+
+ context 'With an RBAC cluster' do
+ before do
+ provider.legacy_abac = false
+
+ stub_kubeclient_create_cluster_role_binding(api_url)
+ stub_kubeclient_create_role_binding(api_url, namespace: namespace)
+ end
+
+ include_context 'kubernetes information successfully fetched'
+
+ it_behaves_like 'success'
+
+ it 'uses RBAC authorization type' do
+ subject
+ cluster.reload
+
+ expect(platform).to be_rbac
+ expect(platform.authorization_type).to eq('rbac')
+ end
+
+ it_behaves_like 'kubernetes information not successfully fetched'
+ end
end
diff --git a/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb b/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb
new file mode 100644
index 00000000000..661364ac765
--- /dev/null
+++ b/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do
+ include KubernetesHelpers
+
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+ let(:platform) { cluster.platform }
+ let(:api_url) { 'https://kubernetes.example.com' }
+ let(:project) { cluster.project }
+ let(:cluster_project) { cluster.cluster_project }
+
+ subject do
+ described_class.new(
+ cluster: cluster,
+ kubernetes_namespace: kubernetes_namespace
+ ).execute
+ end
+
+ shared_context 'kubernetes requests' do
+ before do
+ stub_kubeclient_discover(api_url)
+ stub_kubeclient_get_namespace(api_url)
+ stub_kubeclient_create_service_account(api_url)
+ stub_kubeclient_create_secret(api_url)
+
+ stub_kubeclient_get_namespace(api_url, namespace: namespace)
+ stub_kubeclient_create_service_account(api_url, namespace: namespace)
+ stub_kubeclient_create_secret(api_url, namespace: namespace)
+
+ stub_kubeclient_get_secret(
+ api_url,
+ {
+ metadata_name: "#{namespace}-token",
+ token: Base64.encode64('sample-token'),
+ namespace: namespace
+ }
+ )
+ end
+ end
+
+ context 'when kubernetes namespace is not persisted' do
+ let(:namespace) { "#{project.path}-#{project.id}" }
+
+ let(:kubernetes_namespace) do
+ create(:cluster_kubernetes_namespace,
+ cluster: cluster,
+ project: cluster_project.project,
+ cluster_project: cluster_project)
+ end
+
+ include_context 'kubernetes requests'
+
+ it 'creates a Clusters::KubernetesNamespace' do
+ expect do
+ subject
+ end.to change(Clusters::KubernetesNamespace, :count).by(1)
+ end
+
+ it 'creates project service account' do
+ expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateServiceAccountService).to receive(:execute).once
+
+ subject
+ end
+
+ it 'configures kubernetes token' do
+ subject
+
+ kubernetes_namespace.reload
+ expect(kubernetes_namespace.namespace).to eq(namespace)
+ expect(kubernetes_namespace.service_account_name).to eq("#{namespace}-service-account")
+ expect(kubernetes_namespace.encrypted_service_account_token).to be_present
+ end
+ end
+
+ context 'when there is a Kubernetes Namespace associated' do
+ let(:namespace) { 'new-namespace' }
+
+ let(:kubernetes_namespace) do
+ create(:cluster_kubernetes_namespace,
+ cluster: cluster,
+ project: cluster_project.project,
+ cluster_project: cluster_project)
+ end
+
+ include_context 'kubernetes requests'
+
+ before do
+ platform.update_column(:namespace, 'new-namespace')
+ end
+
+ it 'does not create any Clusters::KubernetesNamespace' do
+ subject
+
+ expect(cluster.kubernetes_namespace).to eq(kubernetes_namespace)
+ end
+
+ it 'creates project service account' do
+ expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateServiceAccountService).to receive(:execute).once
+
+ subject
+ end
+
+ it 'updates Clusters::KubernetesNamespace' do
+ subject
+
+ kubernetes_namespace.reload
+
+ expect(kubernetes_namespace.namespace).to eq(namespace)
+ expect(kubernetes_namespace.service_account_name).to eq("#{namespace}-service-account")
+ expect(kubernetes_namespace.encrypted_service_account_token).to be_present
+ end
+ end
+end
diff --git a/spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb b/spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb
new file mode 100644
index 00000000000..588edff85d4
--- /dev/null
+++ b/spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb
@@ -0,0 +1,166 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do
+ include KubernetesHelpers
+
+ let(:api_url) { 'http://111.111.111.111' }
+ let(:platform_kubernetes) { cluster.platform_kubernetes }
+ let(:cluster_project) { cluster.cluster_project }
+ let(:project) { cluster_project.project }
+ let(:cluster) do
+ create(:cluster,
+ :project, :provided_by_gcp,
+ platform_kubernetes: create(:cluster_platform_kubernetes, :configured))
+ end
+
+ let(:kubeclient) do
+ Gitlab::Kubernetes::KubeClient.new(
+ api_url,
+ auth_options: { username: 'admin', password: 'xxx' }
+ )
+ end
+
+ shared_examples 'creates service account and token' do
+ it 'creates a kubernetes service account' do
+ subject
+
+ expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts").with(
+ body: hash_including(
+ kind: 'ServiceAccount',
+ metadata: { name: service_account_name, namespace: namespace }
+ )
+ )
+ end
+
+ it 'creates a kubernetes secret' do
+ subject
+
+ expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets").with(
+ body: hash_including(
+ kind: 'Secret',
+ metadata: {
+ name: token_name,
+ namespace: namespace,
+ annotations: {
+ 'kubernetes.io/service-account.name': service_account_name
+ }
+ },
+ type: 'kubernetes.io/service-account-token'
+ )
+ )
+ end
+ end
+
+ before do
+ stub_kubeclient_discover(api_url)
+ stub_kubeclient_get_namespace(api_url, namespace: namespace)
+ stub_kubeclient_create_service_account(api_url, namespace: namespace )
+ stub_kubeclient_create_secret(api_url, namespace: namespace)
+ end
+
+ describe '.gitlab_creator' do
+ let(:namespace) { 'default' }
+ let(:service_account_name) { 'gitlab' }
+ let(:token_name) { 'gitlab-token' }
+
+ subject { described_class.gitlab_creator(kubeclient, rbac: rbac).execute }
+
+ context 'with ABAC cluster' do
+ let(:rbac) { false }
+
+ it_behaves_like 'creates service account and token'
+ end
+
+ context 'with RBAC cluster' do
+ let(:rbac) { true }
+
+ before do
+ cluster.platform_kubernetes.rbac!
+
+ stub_kubeclient_create_cluster_role_binding(api_url)
+ end
+
+ it_behaves_like 'creates service account and token'
+
+ it 'should create a cluster role binding with cluster-admin access' do
+ subject
+
+ expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings").with(
+ body: hash_including(
+ kind: 'ClusterRoleBinding',
+ metadata: { name: 'gitlab-admin' },
+ roleRef: {
+ apiGroup: 'rbac.authorization.k8s.io',
+ kind: 'ClusterRole',
+ name: 'cluster-admin'
+ },
+ subjects: [
+ {
+ kind: 'ServiceAccount',
+ name: service_account_name,
+ namespace: namespace
+ }
+ ]
+ )
+ )
+ end
+ end
+ end
+
+ describe '.namespace_creator' do
+ let(:namespace) { "#{project.path}-#{project.id}" }
+ let(:service_account_name) { "#{namespace}-service-account" }
+ let(:token_name) { "#{namespace}-token" }
+
+ subject do
+ described_class.namespace_creator(
+ kubeclient,
+ service_account_name: service_account_name,
+ service_account_namespace: namespace,
+ rbac: rbac
+ ).execute
+ end
+
+ context 'with ABAC cluster' do
+ let(:rbac) { false }
+
+ it_behaves_like 'creates service account and token'
+ end
+
+ context 'With RBAC enabled cluster' do
+ let(:rbac) { true }
+
+ before do
+ cluster.platform_kubernetes.rbac!
+
+ stub_kubeclient_create_role_binding(api_url, namespace: namespace)
+ end
+
+ it_behaves_like 'creates service account and token'
+
+ it 'creates a namespaced role binding with edit access' do
+ subject
+
+ expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings").with(
+ body: hash_including(
+ kind: 'RoleBinding',
+ metadata: { name: "gitlab-#{namespace}", namespace: "#{namespace}" },
+ roleRef: {
+ apiGroup: 'rbac.authorization.k8s.io',
+ kind: 'ClusterRole',
+ name: 'edit'
+ },
+ subjects: [
+ {
+ kind: 'ServiceAccount',
+ name: service_account_name,
+ namespace: namespace
+ }
+ ]
+ )
+ )
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service_spec.rb b/spec/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service_spec.rb
new file mode 100644
index 00000000000..a5806559b14
--- /dev/null
+++ b/spec/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Gcp::Kubernetes::FetchKubernetesTokenService do
+ include KubernetesHelpers
+
+ describe '#execute' do
+ let(:api_url) { 'http://111.111.111.111' }
+ let(:namespace) { 'my-namespace' }
+ let(:service_account_token_name) { 'gitlab-token' }
+
+ let(:kubeclient) do
+ Gitlab::Kubernetes::KubeClient.new(
+ api_url,
+ auth_options: { username: 'admin', password: 'xxx' }
+ )
+ end
+
+ subject { described_class.new(kubeclient, service_account_token_name, namespace).execute }
+
+ before do
+ stub_kubeclient_discover(api_url)
+ end
+
+ context 'when params correct' do
+ let(:decoded_token) { 'xxx.token.xxx' }
+ let(:token) { Base64.encode64(decoded_token) }
+
+ context 'when gitlab-token exists' do
+ before do
+ stub_kubeclient_get_secret(
+ api_url,
+ {
+ metadata_name: service_account_token_name,
+ namespace: namespace,
+ token: token
+ }
+ )
+ end
+
+ it { is_expected.to eq(decoded_token) }
+ end
+
+ context 'when there is a 500 error' do
+ before do
+ stub_kubeclient_get_secret_error(api_url, service_account_token_name, namespace: namespace, status: 500)
+ end
+
+ it { expect { subject }.to raise_error(Kubeclient::HttpError) }
+ end
+
+ context 'when gitlab-token does not exist' do
+ before do
+ stub_kubeclient_get_secret_error(api_url, service_account_token_name, namespace: namespace, status: 404)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/gcp/provision_service_spec.rb b/spec/services/clusters/gcp/provision_service_spec.rb
index f48afdc83b2..c0bdac40938 100644
--- a/spec/services/clusters/gcp/provision_service_spec.rb
+++ b/spec/services/clusters/gcp/provision_service_spec.rb
@@ -26,7 +26,7 @@ describe Clusters::Gcp::ProvisionService do
end
end
- context 'when suceeded to request provision' do
+ context 'when succeeded to request provision' do
before do
stub_cloud_platform_create_cluster(gcp_project_id, zone)
end
diff --git a/spec/services/clusters/update_service_spec.rb b/spec/services/clusters/update_service_spec.rb
index 2d91a21035d..73f9be242a3 100644
--- a/spec/services/clusters/update_service_spec.rb
+++ b/spec/services/clusters/update_service_spec.rb
@@ -1,8 +1,10 @@
require 'spec_helper'
describe Clusters::UpdateService do
+ include KubernetesHelpers
+
describe '#execute' do
- subject { described_class.new(cluster.project, cluster.user, params).execute(cluster) }
+ subject { described_class.new(cluster.user, params).execute(cluster) }
let(:cluster) { create(:cluster, :project, :provided_by_user) }
@@ -34,6 +36,11 @@ describe Clusters::UpdateService do
}
end
+ before do
+ allow(ClusterPlatformConfigureWorker).to receive(:perform_async)
+ stub_kubeclient_get_namespace('https://kubernetes.example.com', namespace: 'my-namespace')
+ end
+
it 'updates namespace' do
is_expected.to eq(true)
expect(cluster.platform.namespace).to eq('custom-namespace')
@@ -55,5 +62,32 @@ describe Clusters::UpdateService do
expect(cluster.errors[:"platform_kubernetes.namespace"]).to be_present
end
end
+
+ context 'when cluster is provided by GCP' do
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+
+ let(:params) do
+ {
+ name: 'my-new-name'
+ }
+ end
+
+ it 'does not change cluster name' do
+ is_expected.to eq(false)
+
+ cluster.reload
+ expect(cluster.name).to eq('test-cluster')
+ end
+
+ context 'when cluster is being created' do
+ let(:cluster) { create(:cluster, :providing_by_gcp) }
+
+ it 'rejects changes' do
+ is_expected.to eq(false)
+
+ expect(cluster.errors.full_messages).to include('cannot modify during creation')
+ end
+ end
+ end
end
end
diff --git a/spec/services/commits/commit_patch_service_spec.rb b/spec/services/commits/commit_patch_service_spec.rb
new file mode 100644
index 00000000000..f4fcec2fbc2
--- /dev/null
+++ b/spec/services/commits/commit_patch_service_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Commits::CommitPatchService do
+ describe '#execute' do
+ let(:patches) do
+ patches_folder = Rails.root.join('spec/fixtures/patchfiles')
+ content_1 = File.read(File.join(patches_folder, "0001-This-does-not-apply-to-the-feature-branch.patch"))
+ content_2 = File.read(File.join(patches_folder, "0001-A-commit-from-a-patch.patch"))
+
+ [content_1, content_2]
+ end
+ let(:user) { project.creator }
+ let(:branch_name) { 'branch-with-patches' }
+ let(:project) { create(:project, :repository) }
+ let(:start_branch) { nil }
+ let(:params) { { branch_name: branch_name, patches: patches, start_branch: start_branch } }
+
+ subject(:service) do
+ described_class.new(project, user, params)
+ end
+
+ it 'returns a successful result' do
+ result = service.execute
+
+ branch = project.repository.find_branch(branch_name)
+
+ expect(result[:status]).to eq(:success)
+ expect(result[:result]).to eq(branch.target)
+ end
+
+ it 'is based off HEAD when no start ref is passed' do
+ service.execute
+
+ merge_base = project.repository.merge_base(project.repository.root_ref, branch_name)
+
+ expect(merge_base).to eq(project.repository.commit('HEAD').sha)
+ end
+
+ context 'when specifying a different start branch' do
+ let(:start_branch) { 'with-codeowners' }
+
+ it 'is based of the correct branch' do
+ service.execute
+
+ merge_base = project.repository.merge_base(start_branch, branch_name)
+
+ expect(merge_base).to eq(project.repository.commit(start_branch).sha)
+ end
+ end
+
+ shared_examples 'an error response' do |expected_message|
+ it 'returns the correct error' do
+ result = service.execute
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to match(expected_message)
+ end
+ end
+
+ context 'when the user does not have access' do
+ let(:user) { create(:user) }
+
+ it_behaves_like 'an error response',
+ 'You are not allowed to push into this branch'
+ end
+
+ context 'when the patches are not valid' do
+ let(:patches) { "a" * 2.1.megabytes }
+
+ it_behaves_like 'an error response', 'Patches are too big'
+ end
+
+ context 'when the new branch name is invalid' do
+ let(:branch_name) { 'HEAD' }
+
+ it_behaves_like 'an error response', 'Branch name is invalid'
+ end
+
+ context 'when the patches do not apply' do
+ let(:branch_name) { 'feature' }
+
+ it_behaves_like 'an error response', 'Patch failed at'
+ end
+
+ context 'when specifying a non existent start branch' do
+ let(:start_branch) { 'does-not-exist' }
+
+ it_behaves_like 'an error response', 'Invalid reference name'
+ end
+ end
+end
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
deleted file mode 100644
index b9bfbb11511..00000000000
--- a/spec/services/create_deployment_service_spec.rb
+++ /dev/null
@@ -1,335 +0,0 @@
-require 'spec_helper'
-
-describe CreateDeploymentService do
- let(:user) { create(:user) }
- let(:options) { nil }
-
- let(:job) do
- create(:ci_build,
- ref: 'master',
- tag: false,
- environment: 'production',
- options: { environment: options })
- end
-
- let(:project) { job.project }
-
- let!(:environment) do
- create(:environment, project: project, name: 'production')
- end
-
- let(:service) { described_class.new(job) }
-
- before do
- allow_any_instance_of(Deployment).to receive(:create_ref)
- end
-
- describe '#execute' do
- subject { service.execute }
-
- context 'when environment exists' do
- it 'creates a deployment' do
- expect(subject).to be_persisted
- end
- end
-
- context 'when environment does not exist' do
- let(:environment) {}
-
- it 'does not create a deployment' do
- expect do
- expect(subject).to be_nil
- end.not_to change { Deployment.count }
- end
- end
-
- context 'when start action is defined' do
- let(:options) { { action: 'start' } }
-
- context 'and environment is stopped' do
- before do
- environment.stop
- end
-
- it 'makes environment available' do
- subject
-
- expect(environment.reload).to be_available
- end
-
- it 'creates a deployment' do
- expect(subject).to be_persisted
- end
- end
- end
-
- context 'when stop action is defined' do
- let(:options) { { action: 'stop' } }
-
- context 'and environment is available' do
- before do
- environment.start
- end
-
- it 'makes environment stopped' do
- subject
-
- expect(environment.reload).to be_stopped
- end
-
- it 'does not create a deployment' do
- expect(subject).to be_nil
- end
- end
- end
-
- context 'when variables are used' do
- let(:options) do
- { name: 'review-apps/$CI_COMMIT_REF_NAME',
- url: 'http://$CI_COMMIT_REF_NAME.review-apps.gitlab.com' }
- end
-
- before do
- environment.update(name: 'review-apps/master')
- job.update(environment: 'review-apps/$CI_COMMIT_REF_NAME')
- end
-
- it 'creates a new deployment' do
- expect(subject).to be_persisted
- end
-
- it 'does not create a new environment' do
- expect { subject }.not_to change { Environment.count }
- end
-
- it 'updates external url' do
- subject
-
- expect(subject.environment.name).to eq('review-apps/master')
- expect(subject.environment.external_url).to eq('http://master.review-apps.gitlab.com')
- end
- end
-
- context 'when project was removed' do
- let(:environment) {}
-
- before do
- job.update(project: nil)
- end
-
- it 'does not create deployment or environment' do
- expect { subject }.not_to raise_error
-
- expect(Environment.count).to be_zero
- expect(Deployment.count).to be_zero
- end
- end
- end
-
- describe '#expanded_environment_url' do
- subject { service.send(:expanded_environment_url) }
-
- context 'when yaml environment uses $CI_COMMIT_REF_NAME' do
- let(:job) do
- create(:ci_build,
- ref: 'master',
- options: { environment: { url: 'http://review/$CI_COMMIT_REF_NAME' } })
- end
-
- it { is_expected.to eq('http://review/master') }
- end
-
- context 'when yaml environment uses $CI_ENVIRONMENT_SLUG' do
- let(:job) do
- create(:ci_build,
- ref: 'master',
- environment: 'production',
- options: { environment: { url: 'http://review/$CI_ENVIRONMENT_SLUG' } })
- end
-
- let!(:environment) do
- create(:environment,
- project: job.project,
- name: 'production',
- slug: 'prod-slug',
- external_url: 'http://review/old')
- end
-
- it { is_expected.to eq('http://review/prod-slug') }
- end
-
- context 'when yaml environment uses yaml_variables containing symbol keys' do
- let(:job) do
- create(:ci_build,
- yaml_variables: [{ key: :APP_HOST, value: 'host' }],
- options: { environment: { url: 'http://review/$APP_HOST' } })
- end
-
- it { is_expected.to eq('http://review/host') }
- end
-
- context 'when yaml environment does not have url' do
- let(:job) { create(:ci_build, environment: 'staging') }
-
- let!(:environment) do
- create(:environment, project: job.project, name: job.environment)
- end
-
- it 'returns the external_url from persisted environment' do
- is_expected.to be_nil
- end
- end
- end
-
- describe 'processing of builds' do
- shared_examples 'does not create deployment' do
- it 'does not create a new deployment' do
- expect { subject }.not_to change { Deployment.count }
- end
-
- it 'does not call a service' do
- expect_any_instance_of(described_class).not_to receive(:execute)
-
- subject
- end
- end
-
- shared_examples 'creates deployment' do
- it 'creates a new deployment' do
- expect { subject }.to change { Deployment.count }.by(1)
- end
-
- it 'calls a service' do
- expect_any_instance_of(described_class).to receive(:execute)
-
- subject
- end
-
- it 'is set as deployable' do
- subject
-
- expect(Deployment.last.deployable).to eq(deployable)
- end
-
- it 'updates environment URL' do
- subject
-
- expect(Deployment.last.environment.external_url).not_to be_nil
- end
- end
-
- context 'without environment specified' do
- let(:job) { create(:ci_build) }
-
- it_behaves_like 'does not create deployment' do
- subject { job.success }
- end
- end
-
- context 'when environment is specified' do
- let(:deployable) { job }
-
- let(:options) do
- { environment: { name: 'production', url: 'http://gitlab.com' } }
- end
-
- context 'when job succeeds' do
- it_behaves_like 'creates deployment' do
- subject { job.success }
- end
- end
-
- context 'when job fails' do
- it_behaves_like 'does not create deployment' do
- subject { job.drop }
- end
- end
-
- context 'when job is retried' do
- it_behaves_like 'creates deployment' do
- before do
- stub_not_protect_default_branch
-
- project.add_developer(user)
- end
-
- let(:deployable) { Ci::Build.retry(job, user) }
-
- subject { deployable.success }
- end
- end
- end
- end
-
- describe "merge request metrics" do
- let(:merge_request) { create(:merge_request, target_branch: 'master', source_branch: 'feature', source_project: project) }
-
- context "while updating the 'first_deployed_to_production_at' time" do
- before do
- merge_request.metrics.update!(merged_at: Time.now)
- end
-
- context "for merge requests merged before the current deploy" do
- it "sets the time if the deploy's environment is 'production'" do
- time = Time.now
- Timecop.freeze(time) { service.execute }
-
- expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(time)
- end
-
- it "doesn't set the time if the deploy's environment is not 'production'" do
- job.update(environment: 'staging')
- service = described_class.new(job)
- service.execute
-
- expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_nil
- end
-
- it 'does not raise errors if the merge request does not have a metrics record' do
- merge_request.metrics.destroy
-
- expect(merge_request.reload.metrics).to be_nil
- expect { service.execute }.not_to raise_error
- end
- end
-
- context "for merge requests merged before the previous deploy" do
- context "if the 'first_deployed_to_production_at' time is already set" do
- it "does not overwrite the older 'first_deployed_to_production_at' time" do
- # Previous deploy
- time = Time.now
- Timecop.freeze(time) { service.execute }
-
- expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(time)
-
- # Current deploy
- service = described_class.new(job)
- Timecop.freeze(time + 12.hours) { service.execute }
-
- expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(time)
- end
- end
-
- context "if the 'first_deployed_to_production_at' time is not already set" do
- it "does not overwrite the older 'first_deployed_to_production_at' time" do
- # Previous deploy
- time = 5.minutes.from_now
- Timecop.freeze(time) { service.execute }
-
- expect(merge_request.reload.metrics.merged_at).to be < merge_request.reload.metrics.first_deployed_to_production_at
-
- merge_request.reload.metrics.update(first_deployed_to_production_at: nil)
-
- expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_nil
-
- # Current deploy
- service = described_class.new(job)
- Timecop.freeze(time + 12.hours) { service.execute }
-
- expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_nil
- end
- end
- end
- end
- end
-end
diff --git a/spec/services/files/create_service_spec.rb b/spec/services/files/create_service_spec.rb
index 30d94e4318d..751b7160276 100644
--- a/spec/services/files/create_service_spec.rb
+++ b/spec/services/files/create_service_spec.rb
@@ -3,7 +3,7 @@ require "spec_helper"
describe Files::CreateService do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
- let(:user) { create(:user) }
+ let(:user) { create(:user, :commit_email) }
let(:file_content) { 'Test file content' }
let(:branch_name) { project.default_branch }
let(:start_branch) { branch_name }
@@ -20,6 +20,8 @@ describe Files::CreateService do
}
end
+ let(:commit) { repository.head_commit }
+
subject { described_class.new(project, user, commit_params) }
before do
@@ -75,4 +77,16 @@ describe Files::CreateService do
end
end
end
+
+ context 'commit attribute' do
+ let(:file_path) { 'test-commit-attributes.txt' }
+
+ it 'uses the commit email' do
+ subject.execute
+
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ end
+ end
end
diff --git a/spec/services/files/delete_service_spec.rb b/spec/services/files/delete_service_spec.rb
index 73566afe8c8..309802ce733 100644
--- a/spec/services/files/delete_service_spec.rb
+++ b/spec/services/files/delete_service_spec.rb
@@ -4,10 +4,11 @@ describe Files::DeleteService do
subject { described_class.new(project, user, commit_params) }
let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
+ let(:user) { create(:user, :commit_email) }
let(:file_path) { 'files/ruby/popen.rb' }
let(:branch_name) { project.default_branch }
let(:last_commit_sha) { nil }
+ let(:commit) { project.repository.head_commit }
let(:commit_params) do
{
@@ -34,6 +35,14 @@ describe Files::DeleteService do
expect(blob).to be_nil
end
+
+ it 'uses the commit email' do
+ subject.execute
+
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ end
end
before do
diff --git a/spec/services/files/multi_service_spec.rb b/spec/services/files/multi_service_spec.rb
index 3bdedaf3770..5f3c8e82715 100644
--- a/spec/services/files/multi_service_spec.rb
+++ b/spec/services/files/multi_service_spec.rb
@@ -11,6 +11,7 @@ describe Files::MultiService do
let(:new_file_path) { 'files/ruby/popen.rb' }
let(:file_content) { 'New content' }
let(:action) { 'update' }
+ let(:commit_message) { 'Update File' }
let!(:original_commit_id) do
Gitlab::Git::Commit.last_for_path(project.repository, branch_name, original_file_path).sha
@@ -30,7 +31,7 @@ describe Files::MultiService do
let(:commit_params) do
{
- commit_message: "Update File",
+ commit_message: commit_message,
branch_name: branch_name,
start_branch: branch_name,
actions: actions
@@ -84,6 +85,39 @@ describe Files::MultiService do
end
end
+ describe 'changing execute_filemode of a file' do
+ let(:commit_message) { 'Chmod +x file' }
+ let(:file_path) { original_file_path }
+ let(:default_action) do
+ {
+ action: 'chmod',
+ file_path: file_path,
+ execute_filemode: true
+ }
+ end
+
+ it 'accepts the commit' do
+ results = subject.execute
+
+ expect(results[:status]).to eq(:success)
+ end
+
+ it 'updates the execute_filemode of the file' do
+ expect { subject.execute }.to change { repository.blob_at_branch(branch_name, file_path).mode }.from('100644').to('100755')
+ end
+
+ context "when the file doesn't exists" do
+ let(:file_path) { 'files/wrong_path.rb' }
+
+ it 'rejects the commit' do
+ results = subject.execute
+
+ expect(results[:status]).to eq(:error)
+ expect(results[:message]).to eq("A file with this name doesn't exist")
+ end
+ end
+ end
+
context 'when moving a file' do
let(:action) { 'move' }
let(:new_file_path) { 'files/ruby/new_popen.rb' }
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index e01fe487ffa..23db35c2418 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -4,11 +4,12 @@ describe Files::UpdateService do
subject { described_class.new(project, user, commit_params) }
let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
+ let(:user) { create(:user, :commit_email) }
let(:file_path) { 'files/ruby/popen.rb' }
let(:new_contents) { 'New Content' }
let(:branch_name) { project.default_branch }
let(:last_commit_sha) { nil }
+ let(:commit) { project.repository.commit }
let(:commit_params) do
{
@@ -54,6 +55,14 @@ describe Files::UpdateService do
expect(results.data).to eq(new_contents)
end
+
+ it 'uses the commit email' do
+ subject.execute
+
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ end
end
context "when the last_commit_sha is not supplied" do
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index a3c9a660c2f..45ef26aebbd 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -204,16 +204,37 @@ describe GitPushService, services: true do
end
describe "Push Event" do
- let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) }
- let(:event) { Event.find_by_action(Event::PUSHED) }
+ context "with an existing branch" do
+ let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) }
+ let(:event) { Event.find_by_action(Event::PUSHED) }
- it { expect(event).to be_an_instance_of(PushEvent) }
- it { expect(event.project).to eq(project) }
- it { expect(event.action).to eq(Event::PUSHED) }
- it { expect(event.push_event_payload).to be_an_instance_of(PushEventPayload) }
- it { expect(event.push_event_payload.commit_from).to eq(oldrev) }
- it { expect(event.push_event_payload.commit_to).to eq(newrev) }
- it { expect(event.push_event_payload.ref).to eq('master') }
+ it 'generates a push event with one commit' do
+ expect(event).to be_an_instance_of(PushEvent)
+ expect(event.project).to eq(project)
+ expect(event.action).to eq(Event::PUSHED)
+ expect(event.push_event_payload).to be_an_instance_of(PushEventPayload)
+ expect(event.push_event_payload.commit_from).to eq(oldrev)
+ expect(event.push_event_payload.commit_to).to eq(newrev)
+ expect(event.push_event_payload.ref).to eq('master')
+ expect(event.push_event_payload.commit_count).to eq(1)
+ end
+ end
+
+ context "with a new branch" do
+ let!(:new_branch_data) { push_data_from_service(project, user, Gitlab::Git::BLANK_SHA, newrev, ref) }
+ let(:event) { Event.find_by_action(Event::PUSHED) }
+
+ it 'generates a push event with more than one commit' do
+ expect(event).to be_an_instance_of(PushEvent)
+ expect(event.project).to eq(project)
+ expect(event.action).to eq(Event::PUSHED)
+ expect(event.push_event_payload).to be_an_instance_of(PushEventPayload)
+ expect(event.push_event_payload.commit_from).to be_nil
+ expect(event.push_event_payload.commit_to).to eq(newrev)
+ expect(event.push_event_payload.ref).to eq('master')
+ expect(event.push_event_payload.commit_count).to be > 1
+ end
+ end
context "Updates merge requests" do
it "when pushing a new branch for the first time" do
@@ -223,10 +244,17 @@ describe GitPushService, services: true do
end
end
- context "Sends System Push data" do
- it "when pushing on a branch" do
- expect(SystemHookPushWorker).to receive(:perform_async).with(push_data, :push_hooks)
+ describe 'system hooks' do
+ let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) }
+ let!(:system_hooks_service) { SystemHooksService.new }
+
+ it "sends a system hook after pushing a branch" do
+ allow(SystemHooksService).to receive(:new).and_return(system_hooks_service)
+ allow(system_hooks_service).to receive(:execute_hooks)
+
execute_service(project, user, oldrev, newrev, ref)
+
+ expect(system_hooks_service).to have_received(:execute_hooks).with(push_data, :push_hooks)
end
end
end
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index 92159e1e372..2699f6e7bcd 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe GitTagPushService do
include RepoHelpers
+ include GitHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
@@ -118,9 +119,7 @@ describe GitTagPushService do
before do
# Create the lightweight tag
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.raw_repository.rugged.tags.create(tag_name, newrev)
- end
+ rugged_repo(project.repository).tags.create(tag_name, newrev)
# Clear tag list cache
project.repository.expire_tags_cache
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index b54491cf5f9..d80d0f5a8a8 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -135,6 +135,17 @@ describe Groups::DestroyService do
it_behaves_like 'group destruction', false
end
+ context 'repository removal status is taken into account' do
+ it 'raises exception' do
+ expect_next_instance_of(::Projects::DestroyService) do |destroy_service|
+ expect(destroy_service).to receive(:execute).and_return(false)
+ end
+
+ expect { destroy_group(group, user, false) }
+ .to raise_error(Groups::DestroyService::DestroyError, "Project #{project.id} can't be deleted" )
+ end
+ end
+
describe 'repository removal' do
before do
destroy_group(group, user, false)
diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb
index 999677cfaaa..dd8a1cee074 100644
--- a/spec/services/groups/transfer_service_spec.rb
+++ b/spec/services/groups/transfer_service_spec.rb
@@ -22,7 +22,7 @@ describe Groups::TransferService, :postgresql do
end
end
- context "when there's an exception on Gitlab shell directories" do
+ context "when there's an exception on GitLab shell directories" do
let(:new_parent_group) { create(:group, :public) }
before do
@@ -177,7 +177,7 @@ describe Groups::TransferService, :postgresql do
it 'should add an error on group' do
transfer_service.execute(new_parent_group)
- expect(transfer_service.error).to eq('Transfer failed: Validation failed: Path has already been taken')
+ expect(transfer_service.error).to eq('Transfer failed: Validation failed: Group URL has already been taken')
end
end
@@ -347,7 +347,7 @@ describe Groups::TransferService, :postgresql do
end
end
- context 'when transfering a group with nested groups and projects' do
+ context 'when transferring a group with nested groups and projects' do
let!(:group) { create(:group, :public) }
let!(:project1) { create(:project, :repository, :private, namespace: group) }
let!(:subgroup1) { create(:group, :private, parent: group) }
diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb
index 7c5c7409cc1..84cfa53ea05 100644
--- a/spec/services/groups/update_service_spec.rb
+++ b/spec/services/groups/update_service_spec.rb
@@ -24,6 +24,12 @@ describe Groups::UpdateService do
expect(TodosDestroyer::GroupPrivateWorker).not_to receive(:perform_in)
end
+
+ it "returns false if save failed" do
+ allow(public_group).to receive(:save).and_return(false)
+
+ expect(service.execute).to be_falsey
+ end
end
context "internal group with internal project" do
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index 53c85f73cde..f0b0f7956ce 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Issuable::BulkUpdateService do
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:project, :repository, namespace: user.namespace) }
def bulk_update(issuables, extra_params = {})
bulk_update_params = extra_params
diff --git a/spec/services/issuable/clone/attributes_rewriter_spec.rb b/spec/services/issuable/clone/attributes_rewriter_spec.rb
new file mode 100644
index 00000000000..20bda6984bd
--- /dev/null
+++ b/spec/services/issuable/clone/attributes_rewriter_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Issuable::Clone::AttributesRewriter do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project1) { create(:project, :public, group: group) }
+ let(:project2) { create(:project, :public, group: group) }
+ let(:original_issue) { create(:issue, project: project1) }
+ let(:new_issue) { create(:issue, project: project2) }
+
+ subject { described_class.new(user, original_issue, new_issue) }
+
+ context 'setting labels' do
+ it 'sets labels present in the new project and group labels' do
+ project1_label_1 = create(:label, title: 'label1', project: project1)
+ project1_label_2 = create(:label, title: 'label2', project: project1)
+ project2_label_1 = create(:label, title: 'label1', project: project2)
+ group_label = create(:group_label, title: 'group_label', group: group)
+ create(:label, title: 'label3', project: project2)
+
+ original_issue.update(labels: [project1_label_1, project1_label_2, group_label])
+
+ subject.execute
+
+ expect(new_issue.reload.labels).to match_array([project2_label_1, group_label])
+ end
+
+ it 'does not set any labels when not used on the original issue' do
+ subject.execute
+
+ expect(new_issue.reload.labels).to be_empty
+ end
+
+ it 'copies the resource label events' do
+ resource_label_events = create_list(:resource_label_event, 2, issue: original_issue)
+
+ subject.execute
+
+ expected = resource_label_events.map(&:label_id)
+
+ expect(new_issue.resource_label_events.map(&:label_id)).to match_array(expected)
+ end
+ end
+
+ context 'setting milestones' do
+ it 'sets milestone to nil when old issue milestone is not in the new project' do
+ milestone = create(:milestone, title: 'milestone', project: project1)
+
+ original_issue.update(milestone: milestone)
+
+ subject.execute
+
+ expect(new_issue.reload.milestone).to be_nil
+ end
+
+ it 'copies the milestone when old issue milestone title is in the new project' do
+ milestone_project1 = create(:milestone, title: 'milestone', project: project1)
+ milestone_project2 = create(:milestone, title: 'milestone', project: project2)
+
+ original_issue.update(milestone: milestone_project1)
+
+ subject.execute
+
+ expect(new_issue.reload.milestone).to eq(milestone_project2)
+ end
+
+ it 'copies the milestone when old issue milestone is a group milestone' do
+ milestone = create(:milestone, title: 'milestone', group: group)
+
+ original_issue.update(milestone: milestone)
+
+ subject.execute
+
+ expect(new_issue.reload.milestone).to eq(milestone)
+ end
+ end
+end
diff --git a/spec/services/issuable/clone/content_rewriter_spec.rb b/spec/services/issuable/clone/content_rewriter_spec.rb
new file mode 100644
index 00000000000..4d3cb0bd254
--- /dev/null
+++ b/spec/services/issuable/clone/content_rewriter_spec.rb
@@ -0,0 +1,153 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Issuable::Clone::ContentRewriter do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project1) { create(:project, :public, group: group) }
+ let(:project2) { create(:project, :public, group: group) }
+
+ let(:other_issue) { create(:issue, project: project1) }
+ let(:merge_request) { create(:merge_request) }
+
+ subject { described_class.new(user, original_issue, new_issue)}
+
+ let(:description) { 'Simple text' }
+ let(:original_issue) { create(:issue, description: description, project: project1) }
+ let(:new_issue) { create(:issue, project: project2) }
+
+ context 'rewriting award emojis' do
+ it 'copies the award emojis' do
+ create(:award_emoji, awardable: original_issue, name: 'thumbsup')
+ create(:award_emoji, awardable: original_issue, name: 'thumbsdown')
+
+ expect { subject.execute }.to change { AwardEmoji.count }.by(2)
+
+ expect(new_issue.award_emoji.map(&:name)).to match_array(%w(thumbsup thumbsdown))
+ end
+ end
+
+ context 'rewriting description' do
+ before do
+ subject.execute
+ end
+
+ context 'when description is a simple text' do
+ it 'does not rewrite the description' do
+ expect(new_issue.reload.description).to eq(original_issue.description)
+ end
+ end
+
+ context 'when description contains a local reference' do
+ let(:description) { "See ##{other_issue.iid}" }
+
+ it 'rewrites the local reference correctly' do
+ expected_description = "See #{project1.path}##{other_issue.iid}"
+
+ expect(new_issue.reload.description).to eq(expected_description)
+ end
+ end
+
+ context 'when description contains a cross reference' do
+ let(:description) { "See #{merge_request.project.full_path}!#{merge_request.iid}" }
+
+ it 'rewrites the cross reference correctly' do
+ expected_description = "See #{merge_request.project.full_path}!#{merge_request.iid}"
+
+ expect(new_issue.reload.description).to eq(expected_description)
+ end
+ end
+
+ context 'when description contains a user reference' do
+ let(:description) { "FYU #{user.to_reference}" }
+
+ it 'works with a user reference' do
+ expect(new_issue.reload.description).to eq("FYU #{user.to_reference}")
+ end
+ end
+
+ context 'when description contains uploads' do
+ let(:uploader) { build(:file_uploader, project: project1) }
+ let(:description) { "Text and #{uploader.markdown_link}" }
+
+ it 'rewrites uploads in the description' do
+ upload = Upload.last
+
+ expect(new_issue.description).not_to eq(description)
+ expect(new_issue.description).to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/)
+ expect(upload.secret).not_to eq(uploader.secret)
+ expect(new_issue.description).to include(upload.secret)
+ expect(new_issue.description).to include(upload.path)
+ end
+ end
+ end
+
+ context 'rewriting notes' do
+ context 'simple notes' do
+ let!(:notes) do
+ [
+ create(:note, noteable: original_issue, project: project1,
+ created_at: 2.weeks.ago, updated_at: 1.week.ago),
+ create(:note, noteable: original_issue, project: project1),
+ create(:note, system: true, noteable: original_issue, project: project1)
+ ]
+ end
+ let!(:system_note_metadata) { create(:system_note_metadata, note: notes.last) }
+ let!(:award_emoji) { create(:award_emoji, awardable: notes.first, name: 'thumbsup')}
+
+ before do
+ subject.execute
+ end
+
+ it 'rewrites existing notes in valid order' do
+ expect(new_issue.notes.order('id ASC').pluck(:note).first(3)).to eq(notes.map(&:note))
+ end
+
+ it 'copies all the issue notes' do
+ expect(new_issue.notes.count).to eq(3)
+ end
+
+ it 'does not change the note attributes' do
+ subject.execute
+
+ new_note = new_issue.notes.first
+
+ expect(new_note.note).to eq(notes.first.note)
+ expect(new_note.author).to eq(notes.first.author)
+ end
+
+ it 'copies the award emojis' do
+ subject.execute
+
+ new_note = new_issue.notes.first
+ new_note.award_emoji.first.name = 'thumbsup'
+ end
+
+ it 'copies system_note_metadata for system note' do
+ new_note = new_issue.notes.last
+
+ expect(new_note.system_note_metadata.action).to eq(system_note_metadata.action)
+ expect(new_note.system_note_metadata.id).not_to eq(system_note_metadata.id)
+ end
+ end
+
+ context 'notes with reference' do
+ let(:text) do
+ "See ##{other_issue.iid} and #{merge_request.project.full_path}!#{merge_request.iid}"
+ end
+ let!(:note) { create(:note, noteable: original_issue, note: text, project: project1) }
+
+ it 'rewrites the references correctly' do
+ subject.execute
+
+ new_note = new_issue.notes.first
+
+ expected_text = "See #{other_issue.project.path}##{other_issue.iid} and #{merge_request.project.full_path}!#{merge_request.iid}"
+
+ expect(new_note.note).to eq(expected_text)
+ expect(new_note.author).to eq(note.author)
+ end
+ end
+ end
+end
diff --git a/spec/services/issuable/common_system_notes_service_spec.rb b/spec/services/issuable/common_system_notes_service_spec.rb
index dcf4503ef9c..fa1a421d528 100644
--- a/spec/services/issuable/common_system_notes_service_spec.rb
+++ b/spec/services/issuable/common_system_notes_service_spec.rb
@@ -12,12 +12,21 @@ describe Issuable::CommonSystemNotesService do
it_behaves_like 'system note creation', { time_estimate: 5 }, 'changed time estimate'
context 'when new label is added' do
+ let(:label) { create(:label, project: project) }
+
before do
- label = create(:label, project: project)
issuable.labels << label
+ issuable.save
end
- it_behaves_like 'system note creation', {}, /added ~\w+ label/
+ it 'creates a resource label event' do
+ described_class.new(project, user).execute(issuable, [])
+ event = issuable.reload.resource_label_events.last
+
+ expect(event).not_to be_nil
+ expect(event.label_id).to eq label.id
+ expect(event.user_id).to eq user.id
+ end
end
context 'when new milestone is assigned' do
diff --git a/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb b/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb
deleted file mode 100644
index 4e58179f45f..00000000000
--- a/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'spec_helper.rb'
-
-describe Issues::FetchReferencedMergeRequestsService do
- let(:project) { create(:project) }
- let(:issue) { create(:issue, project: project) }
- let(:other_project) { create(:project) }
-
- let(:mr) { create(:merge_request, source_project: project, target_project: project, id: 2)}
- let(:other_mr) { create(:merge_request, source_project: other_project, target_project: other_project, id: 1)}
-
- let(:user) { create(:user) }
- let(:service) { described_class.new(project, user) }
-
- context 'with mentioned merge requests' do
- it 'returns a list of sorted merge requests' do
- allow(issue).to receive(:referenced_merge_requests).with(user).and_return([other_mr, mr])
-
- mrs, closed_by_mrs = service.execute(issue)
-
- expect(mrs).to match_array([mr, other_mr])
- expect(closed_by_mrs).to match_array([])
- end
- end
-
- context 'with closed-by merge requests' do
- it 'returns a list of sorted merge requests' do
- allow(issue).to receive(:closed_by_merge_requests).with(user).and_return([other_mr, mr])
-
- mrs, closed_by_mrs = service.execute(issue)
-
- expect(mrs).to match_array([])
- expect(closed_by_mrs).to match_array([mr, other_mr])
- end
- end
-end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index 609eef76d2c..1e088bc7d9b 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -10,11 +10,9 @@ describe Issues::MoveService do
let(:sub_group_2) { create(:group, :private, parent: group) }
let(:old_project) { create(:project, namespace: sub_group_1) }
let(:new_project) { create(:project, namespace: sub_group_2) }
- let(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') }
let(:old_issue) do
- create(:issue, title: title, description: description,
- project: old_project, author: author, milestone: milestone1)
+ create(:issue, title: title, description: description, project: old_project, author: author)
end
subject(:move_service) do
@@ -25,16 +23,6 @@ describe Issues::MoveService do
before do
old_project.add_reporter(user)
new_project.add_reporter(user)
-
- labels = Array.new(2) { |x| "label%d" % (x + 1) }
-
- labels.each do |label|
- old_issue.labels << create(:label,
- project_id: old_project.id,
- title: label)
-
- new_project.labels << create(:label, title: label)
- end
end
end
@@ -48,80 +36,6 @@ describe Issues::MoveService do
context 'issue movable' do
include_context 'user can move issue'
- context 'move to new milestone' do
- let(:new_issue) { move_service.execute(old_issue, new_project) }
-
- context 'project milestone' do
- let!(:milestone2) do
- create(:milestone, project_id: new_project.id, title: 'v9.0')
- end
-
- it 'assigns milestone to new issue' do
- expect(new_issue.reload.milestone.title).to eq 'v9.0'
- expect(new_issue.reload.milestone).to eq(milestone2)
- end
- end
-
- context 'group milestones' do
- let!(:group) { create(:group, :private) }
- let!(:group_milestone_1) do
- create(:milestone, group_id: group.id, title: 'v9.0_group')
- end
-
- before do
- old_issue.update(milestone: group_milestone_1)
- old_project.update(namespace: group)
- new_project.update(namespace: group)
-
- group.add_users([user], GroupMember::DEVELOPER)
- end
-
- context 'when moving to a project of the same group' do
- it 'keeps the same group milestone' do
- expect(new_issue.reload.project).to eq(new_project)
- expect(new_issue.reload.milestone).to eq(group_milestone_1)
- end
- end
-
- context 'when moving to a project of a different group' do
- let!(:group_2) { create(:group, :private) }
-
- let!(:group_milestone_2) do
- create(:milestone, group_id: group_2.id, title: 'v9.0_group')
- end
-
- before do
- old_issue.update(milestone: group_milestone_1)
- new_project.update(namespace: group_2)
-
- group_2.add_users([user], GroupMember::DEVELOPER)
- end
-
- it 'assigns to new group milestone of same title' do
- expect(new_issue.reload.project).to eq(new_project)
- expect(new_issue.reload.milestone).to eq(group_milestone_2)
- end
- end
- end
- end
-
- context 'issue with group labels', :nested_groups do
- it 'assigns group labels to new issue' do
- label = create(:group_label, group: group)
- label_issue = create(:labeled_issue, description: description, project: old_project,
- milestone: milestone1, labels: [label])
- old_project.add_reporter(user)
- new_project.add_reporter(user)
-
- new_issue = move_service.execute(label_issue, new_project)
-
- expect(new_issue).to have_attributes(
- project: new_project,
- labels: include(label)
- )
- end
- end
-
context 'generic issue' do
include_context 'issue move executed'
@@ -129,18 +43,6 @@ describe Issues::MoveService do
expect(new_issue.project).to eq new_project
end
- it 'assign labels to new issue' do
- expected_label_titles = new_issue.reload.labels.map(&:title)
- expect(expected_label_titles).to include 'label1'
- expect(expected_label_titles).to include 'label2'
- expect(expected_label_titles.size).to eq 2
-
- new_issue.labels.each do |label|
- expect(new_project.labels).to include(label)
- expect(old_project.labels).not_to include(label)
- end
- end
-
it 'rewrites issue title' do
expect(new_issue.title).to eq title
end
@@ -192,140 +94,25 @@ describe Issues::MoveService do
end
end
- context 'issue with notes' do
- context 'notes without references' do
- let(:notes_params) do
- [{ system: false, note: 'Some comment 1' },
- { system: true, note: 'Some system note' },
- { system: false, note: 'Some comment 2' }]
- end
- let(:award_names) { %w(thumbsup thumbsdown facepalm) }
- let(:notes_contents) { notes_params.map { |n| n[:note] } }
-
- before do
- note_params = { noteable: old_issue, project: old_project, author: author }
- notes_params.each_with_index do |note, index|
- new_note = create(:note, note_params.merge(note))
- award_emoji_params = { awardable: new_note, name: award_names[index] }
- create(:award_emoji, award_emoji_params)
- end
- end
-
- include_context 'issue move executed'
-
- let(:all_notes) { new_issue.notes.order('id ASC') }
- let(:system_notes) { all_notes.system }
- let(:user_notes) { all_notes.user }
-
- it 'rewrites existing notes in valid order' do
- expect(all_notes.pluck(:note).first(3)).to eq notes_contents
- end
-
- it 'creates new emojis for the new notes' do
- expect(all_notes.map(&:award_emoji).to_a.flatten.map(&:name)).to eq award_names
- end
-
- it 'adds a system note about move after rewritten notes' do
- expect(system_notes.last.note).to match /^moved from/
- end
-
- it 'preserves orignal author of comment' do
- expect(user_notes.pluck(:author_id)).to all(eq(author.id))
- end
- end
-
- context 'note that has been updated' do
- let!(:note) do
- create(:note, noteable: old_issue, project: old_project,
- author: author, updated_at: Date.yesterday,
- created_at: Date.yesterday)
- end
-
- include_context 'issue move executed'
-
- it 'preserves time when note has been created at' do
- expect(new_issue.notes.first.created_at).to eq note.created_at
- end
-
- it 'preserves time when note has been updated at' do
- expect(new_issue.notes.first.updated_at).to eq note.updated_at
- end
- end
+ context 'issue with assignee' do
+ let(:assignee) { create(:user) }
- context 'issue with assignee' do
- let(:assignee) { create(:user) }
-
- before do
- old_issue.assignees = [assignee]
- end
-
- it 'preserves assignee with access to the new issue' do
- new_project.add_reporter(assignee)
-
- new_issue = move_service.execute(old_issue, new_project)
-
- expect(new_issue.assignees).to eq([assignee])
- end
-
- it 'ignores assignee without access to the new issue' do
- new_issue = move_service.execute(old_issue, new_project)
-
- expect(new_issue.assignees).to be_empty
- end
- end
-
- context 'notes with references' do
- before do
- create(:merge_request, source_project: old_project)
- create(:note, noteable: old_issue, project: old_project, author: author,
- note: 'Note with reference to merge request !1')
- end
-
- include_context 'issue move executed'
- let(:new_note) { new_issue.notes.first }
-
- it 'rewrites references using a cross reference to old project' do
- expect(new_note.note)
- .to eq "Note with reference to merge request #{old_project.to_reference(new_project)}!1"
- end
- end
-
- context 'issue description with uploads' do
- let(:uploader) { build(:file_uploader, project: old_project) }
- let(:description) { "Text and #{uploader.markdown_link}" }
-
- include_context 'issue move executed'
-
- it 'rewrites uploads in description' do
- expect(new_issue.description).not_to eq description
- expect(new_issue.description)
- .to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/)
- expect(new_issue.description).not_to include uploader.secret
- end
+ before do
+ old_issue.assignees = [assignee]
end
- end
- describe 'rewriting references' do
- include_context 'issue move executed'
+ it 'preserves assignee with access to the new issue' do
+ new_project.add_reporter(assignee)
- context 'issue references' do
- let(:another_issue) { create(:issue, project: old_project) }
- let(:description) { "Some description #{another_issue.to_reference}" }
+ new_issue = move_service.execute(old_issue, new_project)
- it 'rewrites referenced issues creating cross project reference' do
- expect(new_issue.description)
- .to eq "Some description #{another_issue.to_reference(new_project)}"
- end
+ expect(new_issue.assignees).to eq([assignee])
end
- context "user references" do
- let(:another_issue) { create(:issue, project: old_project) }
- let(:description) { "Some description #{user.to_reference}" }
+ it 'ignores assignee without access to the new issue' do
+ new_issue = move_service.execute(old_issue, new_project)
- it "doesn't throw any errors for issues containing user references" do
- expect(new_issue.description)
- .to eq "Some description #{user.to_reference}"
- end
+ expect(new_issue.assignees).to be_empty
end
end
@@ -405,25 +192,5 @@ describe Issues::MoveService do
it { expect { move }.to raise_error(StandardError, /permissions/) }
end
end
-
- context 'movable issue with no assigned labels' do
- before do
- old_project.add_reporter(user)
- new_project.add_reporter(user)
-
- labels = Array.new(2) { |x| "label%d" % (x + 1) }
-
- labels.each do |label|
- new_project.labels << create(:label, title: label)
- end
- end
-
- include_context 'issue move executed'
-
- it 'does not assign labels to new issue' do
- expected_label_titles = new_issue.reload.labels.map(&:title)
- expect(expected_label_titles.size).to eq 0
- end
- end
end
end
diff --git a/spec/services/issues/referenced_merge_requests_service_spec.rb b/spec/services/issues/referenced_merge_requests_service_spec.rb
new file mode 100644
index 00000000000..61d1612829f
--- /dev/null
+++ b/spec/services/issues/referenced_merge_requests_service_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+require 'spec_helper.rb'
+
+describe Issues::ReferencedMergeRequestsService do
+ def create_referencing_mr(attributes = {})
+ create(:merge_request, attributes).tap do |merge_request|
+ create(:note, :system, project: project, noteable: issue, author: user, note: merge_request.to_reference(full: true))
+ end
+ end
+
+ def create_closing_mr(attributes = {})
+ create_referencing_mr(attributes).tap do |merge_request|
+ create(:merge_requests_closing_issues, issue: issue, merge_request: merge_request)
+ end
+ end
+
+ set(:user) { create(:user) }
+ set(:project) { create(:project, :public, :repository) }
+ set(:other_project) { create(:project, :public, :repository) }
+ set(:issue) { create(:issue, author: user, project: project) }
+
+ set(:closing_mr) { create_closing_mr(source_project: project) }
+ set(:closing_mr_other_project) { create_closing_mr(source_project: other_project) }
+
+ set(:referencing_mr) { create_referencing_mr(source_project: project, source_branch: 'csv') }
+ set(:referencing_mr_other_project) { create_referencing_mr(source_project: other_project, source_branch: 'csv') }
+
+ let(:service) { described_class.new(project, user) }
+
+ describe '#execute' do
+ it 'returns a list of sorted merge requests' do
+ mrs, closed_by_mrs = service.execute(issue)
+
+ expect(mrs).to eq([closing_mr, referencing_mr, closing_mr_other_project, referencing_mr_other_project])
+ expect(closed_by_mrs).to eq([closing_mr, closing_mr_other_project])
+ end
+
+ context 'performance' do
+ it 'does not run extra queries when extra namespaces are included', :use_clean_rails_memory_store_caching do
+ service.execute(issue) # warm cache
+ control_count = ActiveRecord::QueryRecorder.new { service.execute(issue) }.count
+
+ third_project = create(:project, :public)
+ create_closing_mr(source_project: third_project)
+ service.execute(issue) # warm cache
+
+ expect { service.execute(issue) }.not_to exceed_query_limit(control_count)
+ end
+
+ it 'preloads the head pipeline for each merge request, and its routes' do
+ # Hack to ensure no data is preserved on issue before starting the spec,
+ # to avoid false negatives
+ reloaded_issue = Issue.find(issue.id)
+
+ pipeline_routes = lambda do |merge_requests|
+ merge_requests.map { |mr| mr.head_pipeline&.project&.full_path }
+ end
+
+ closing_mr_other_project.update!(head_pipeline: create(:ci_pipeline))
+ control_count = ActiveRecord::QueryRecorder.new { service.execute(reloaded_issue).each(&pipeline_routes) }
+
+ closing_mr.update!(head_pipeline: create(:ci_pipeline))
+
+ expect { service.execute(issue).each(&pipeline_routes) }
+ .not_to exceed_query_limit(control_count)
+ end
+
+ it 'only loads issue notes once' do
+ expect(issue).to receive(:notes).once.and_call_original
+
+ service.execute(issue)
+ end
+ end
+ end
+
+ describe '#referenced_merge_requests' do
+ it 'returns the referenced merge requests' do
+ expect(service.referenced_merge_requests(issue)).to match_array([
+ closing_mr,
+ closing_mr_other_project,
+ referencing_mr,
+ referencing_mr_other_project
+ ])
+ end
+
+ it 'excludes cross project references if the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project).at_least(:once).and_return(false)
+
+ expect(service.referenced_merge_requests(issue)).not_to include(closing_mr_other_project)
+ expect(service.referenced_merge_requests(issue)).not_to include(referencing_mr_other_project)
+ end
+
+ context 'performance' do
+ it 'does not run a query for each note author', :use_clean_rails_memory_store_caching do
+ service.referenced_merge_requests(issue) # warm cache
+ control_count = ActiveRecord::QueryRecorder.new { service.referenced_merge_requests(issue) }.count
+
+ create(:note, project: project, noteable: issue, author: create(:user))
+ service.referenced_merge_requests(issue) # warm cache
+
+ expect { service.referenced_merge_requests(issue) }.not_to exceed_query_limit(control_count)
+ end
+ end
+ end
+
+ describe '#closed_by_merge_requests' do
+ let(:closed_issue) { build(:issue, :closed, project: project)}
+
+ it 'returns the open merge requests that close this issue' do
+ create_closing_mr(source_project: project, state: 'closed')
+
+ expect(service.closed_by_merge_requests(issue)).to match_array([closing_mr, closing_mr_other_project])
+ end
+
+ it 'returns an empty array when the current issue is closed already' do
+ expect(service.closed_by_merge_requests(closed_issue)).to eq([])
+ end
+
+ context 'performance' do
+ it 'does not run a query for each note author', :use_clean_rails_memory_store_caching do
+ service.closed_by_merge_requests(issue) # warm cache
+ control_count = ActiveRecord::QueryRecorder.new { service.closed_by_merge_requests(issue) }.count
+
+ create(:note, :system, project: project, noteable: issue, author: create(:user))
+ service.closed_by_merge_requests(issue) # warm cache
+
+ expect { service.closed_by_merge_requests(issue) }.not_to exceed_query_limit(control_count)
+ end
+ end
+ end
+end
diff --git a/spec/services/issues/related_branches_service_spec.rb b/spec/services/issues/related_branches_service_spec.rb
new file mode 100644
index 00000000000..c2e1eba6a63
--- /dev/null
+++ b/spec/services/issues/related_branches_service_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe Issues::RelatedBranchesService do
+ let(:user) { create(:admin) }
+ let(:issue) { create(:issue) }
+
+ subject { described_class.new(issue.project, user) }
+
+ describe '#execute' do
+ before do
+ allow(issue.project.repository).to receive(:branch_names).and_return(["mpempe", "#{issue.iid}mepmep", issue.to_branch_name, "#{issue.iid}-branch"])
+ end
+
+ it "selects the right branches when there are no referenced merge requests" do
+ expect(subject.execute(issue)).to eq([issue.to_branch_name, "#{issue.iid}-branch"])
+ end
+
+ it "selects the right branches when there is a referenced merge request" do
+ merge_request = create(:merge_request, { description: "Closes ##{issue.iid}",
+ source_project: issue.project,
+ source_branch: "#{issue.iid}-branch" })
+ merge_request.create_cross_references!(user)
+
+ referenced_merge_requests = Issues::ReferencedMergeRequestsService
+ .new(issue.project, user)
+ .referenced_merge_requests(issue)
+
+ expect(referenced_merge_requests).not_to be_empty
+ expect(subject.execute(issue)).to eq([issue.to_branch_name])
+ end
+
+ it 'excludes stable branches from the related branches' do
+ allow(issue.project.repository).to receive(:branch_names)
+ .and_return(["#{issue.iid}-0-stable"])
+
+ expect(subject.execute(issue)).to eq []
+ end
+ end
+end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 5bcfef46b75..bd519e7f077 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -189,11 +189,12 @@ describe Issues::UpdateService, :mailer do
expect(note.note).to include "assigned to #{user2.to_reference}"
end
- it 'creates system note about issue label edit' do
- note = find_note('added ~')
+ it 'creates a resource label event' do
+ event = issue.resource_label_events.last
- expect(note).not_to be_nil
- expect(note.note).to include "added #{label.to_reference} label"
+ expect(event).not_to be_nil
+ expect(event.label_id).to eq label.id
+ expect(event.user_id).to eq user.id
end
it 'creates system note about title change' do
@@ -342,7 +343,42 @@ describe Issues::UpdateService, :mailer do
end
end
- context 'when the milestone change' do
+ context 'when the milestone is removed' do
+ let!(:non_subscriber) { create(:user) }
+
+ let!(:subscriber) do
+ create(:user) do |u|
+ issue.toggle_subscription(u, project)
+ project.add_developer(u)
+ end
+ end
+
+ it_behaves_like 'system notes for milestones'
+
+ it 'sends notifications for subscribers of changed milestone' do
+ issue.milestone = create(:milestone)
+
+ issue.save
+
+ perform_enqueued_jobs do
+ update_issue(milestone_id: "")
+ end
+
+ should_email(subscriber)
+ should_not_email(non_subscriber)
+ end
+ end
+
+ context 'when the milestone is changed' do
+ let!(:non_subscriber) { create(:user) }
+
+ let!(:subscriber) do
+ create(:user) do |u|
+ issue.toggle_subscription(u, project)
+ project.add_developer(u)
+ end
+ end
+
it 'marks todos as done' do
update_issue(milestone: create(:milestone))
@@ -350,6 +386,15 @@ describe Issues::UpdateService, :mailer do
end
it_behaves_like 'system notes for milestones'
+
+ it 'sends notifications for subscribers of changed milestone' do
+ perform_enqueued_jobs do
+ update_issue(milestone: create(:milestone))
+ end
+
+ should_email(subscriber)
+ should_not_email(non_subscriber)
+ end
end
context 'when the labels change' do
@@ -373,7 +418,7 @@ describe Issues::UpdateService, :mailer do
let!(:non_subscriber) { create(:user) }
let!(:subscriber) do
- create(:user).tap do |u|
+ create(:user) do |u|
label.toggle_subscription(u, project)
project.add_developer(u)
end
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 6aed481939e..c9a668994eb 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -13,6 +13,8 @@ describe MergeRequests::BuildService do
let(:description) { nil }
let(:source_branch) { 'feature-branch' }
let(:target_branch) { 'master' }
+ let(:milestone_id) { nil }
+ let(:label_ids) { [] }
let(:merge_request) { service.execute }
let(:compare) { double(:compare, commits: commits) }
let(:commit_1) { double(:commit_1, sha: 'f00ba7', safe_message: "Initial commit\n\nCreate the app") }
@@ -25,7 +27,9 @@ describe MergeRequests::BuildService do
source_branch: source_branch,
target_branch: target_branch,
source_project: source_project,
- target_project: target_project)
+ target_project: target_project,
+ milestone_id: milestone_id,
+ label_ids: label_ids)
end
before do
@@ -169,12 +173,43 @@ describe MergeRequests::BuildService do
end
end
+ it 'uses the title of the commit as the title of the merge request' do
+ expect(merge_request.title).to eq('Initial commit')
+ end
+
it 'appends the closing description' do
expected_description = [commit_description, closing_message].compact.join("\n\n")
expect(merge_request.description).to eq(expected_description)
end
end
+
+ context 'when the source branch matches an internal issue' do
+ let(:label) { create(:label, project: project) }
+ let(:milestone) { create(:milestone, project: project) }
+ let(:source_branch) { '123-fix-issue' }
+
+ before do
+ issue.update!(iid: 123, labels: [label], milestone: milestone)
+ end
+
+ it 'assigns the issue label and milestone' do
+ expect(merge_request.milestone).to eq(milestone)
+ expect(merge_request.labels).to match_array([label])
+ end
+
+ context 'when milestone_id and label_ids are shared in the params' do
+ let(:label2) { create(:label, project: project) }
+ let(:milestone2) { create(:milestone, project: project) }
+ let(:label_ids) { [label2.id] }
+ let(:milestone_id) { milestone2.id }
+
+ it 'assigns milestone_id and label_ids instead of issue labels and milestone' do
+ expect(merge_request.milestone).to eq(milestone2)
+ expect(merge_request.labels).to match_array([label2])
+ end
+ end
+ end
end
end
@@ -357,5 +392,13 @@ describe MergeRequests::BuildService do
expect(merge_request.source_project).to eq(project)
end
end
+
+ context 'when specifying target branch in the description' do
+ let(:description) { "A merge request targeting another branch\n\n/target_branch with-codeowners" }
+
+ it 'sets the attribute from the quick actions' do
+ expect(merge_request.target_branch).to eq('with-codeowners')
+ end
+ end
end
end
diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb
index b1882df732d..393299cce00 100644
--- a/spec/services/merge_requests/create_from_issue_service_spec.rb
+++ b/spec/services/merge_requests/create_from_issue_service_spec.rb
@@ -61,7 +61,15 @@ describe MergeRequests::CreateFromIssueService do
expect(project.repository.branch_exists?(custom_source_branch)).to be_truthy
end
- it 'creates a system note' do
+ it 'creates the new_merge_request system note' do
+ expect(SystemNoteService).to receive(:new_merge_request).with(issue, project, user, instance_of(MergeRequest))
+
+ service.execute
+ end
+
+ it 'creates the new_issue_branch system note when the branch could be created but the merge_request cannot be created' do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
+
expect(SystemNoteService).to receive(:new_issue_branch).with(issue, project, user, issue.to_branch_name)
service.execute
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 06fb61baf33..74bcc15f912 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -134,9 +134,11 @@ describe MergeRequests::CreateService do
let!(:pipeline_3) { create(:ci_pipeline, project: project, ref: "other_branch", project_id: project.id) }
before do
+ # rubocop: disable DestroyAll
project.merge_requests
.where(source_branch: opts[:source_branch], target_branch: opts[:target_branch])
.destroy_all
+ # rubocop: enable DestroyAll
end
it 'sets head pipeline' do
diff --git a/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb b/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb
index 1c632847940..6268c149fc6 100644
--- a/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb
+++ b/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb
@@ -46,10 +46,12 @@ describe MergeRequests::DeleteNonLatestDiffsService, :clean_gitlab_redis_shared_
end
it 'schedules no removal if there is no non-latest diffs' do
+ # rubocop: disable DestroyAll
merge_request
.merge_request_diffs
.where.not(id: merge_request.latest_merge_request_diff_id)
.destroy_all
+ # rubocop: enable DestroyAll
expect(DeleteDiffFilesWorker).not_to receive(:bulk_perform_in)
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
index 2703da7ae44..427a2d63a88 100644
--- a/spec/services/merge_requests/rebase_service_spec.rb
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -87,7 +87,7 @@ describe MergeRequests::RebaseService do
expect(merge_request.reload.rebase_commit_sha).to eq(head_sha)
end
- it 'logs correct author and commiter' do
+ it 'logs correct author and committer' do
head_commit = merge_request.source_project.repository.commit(merge_request.source_branch)
expect(head_commit.author_email).to eq('dmitriy.zaporozhets@gmail.com')
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 2536c6e2514..61c6ba7d550 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -306,6 +306,66 @@ describe MergeRequests::RefreshService do
end
end
+ context 'forked projects with the same source branch name as target branch' do
+ let!(:first_commit) do
+ @fork_project.repository.create_file(@user, 'test1.txt', 'Test data',
+ message: 'Test commit',
+ branch_name: 'master')
+ end
+ let!(:second_commit) do
+ @fork_project.repository.create_file(@user, 'test2.txt', 'More test data',
+ message: 'Second test commit',
+ branch_name: 'master')
+ end
+ let!(:forked_master_mr) do
+ create(:merge_request,
+ source_project: @fork_project,
+ source_branch: 'master',
+ target_branch: 'master',
+ target_project: @project)
+ end
+ let(:force_push_commit) { @project.commit('feature').id }
+
+ it 'should reload a new diff for a push to the forked project' do
+ expect do
+ service.new(@fork_project, @user).execute(@oldrev, first_commit, 'refs/heads/master')
+ reload_mrs
+ end.to change { forked_master_mr.merge_request_diffs.count }.by(1)
+ end
+
+ it 'should reload a new diff for a force push to the source branch' do
+ expect do
+ service.new(@fork_project, @user).execute(@oldrev, force_push_commit, 'refs/heads/master')
+ reload_mrs
+ end.to change { forked_master_mr.merge_request_diffs.count }.by(1)
+ end
+
+ it 'should reload a new diff for a force push to the target branch' do
+ expect do
+ service.new(@project, @user).execute(@oldrev, force_push_commit, 'refs/heads/master')
+ reload_mrs
+ end.to change { forked_master_mr.merge_request_diffs.count }.by(1)
+ end
+
+ it 'should reload a new diff for a push to the target project that contains a commit in the MR' do
+ expect do
+ service.new(@project, @user).execute(@oldrev, first_commit, 'refs/heads/master')
+ reload_mrs
+ end.to change { forked_master_mr.merge_request_diffs.count }.by(1)
+ end
+
+ it 'should not increase the diff count for a new push to target branch' do
+ new_commit = @project.repository.create_file(@user, 'new-file.txt', 'A new file',
+ message: 'This is a test',
+ branch_name: 'master')
+
+ expect do
+ service.new(@project, @user).execute(@newrev, new_commit, 'refs/heads/master')
+ reload_mrs
+ end.not_to change { forked_master_mr.merge_request_diffs.count }
+ end
+ end
+
context 'push to origin repo target branch after fork project was removed' do
before do
@fork_project.destroy
diff --git a/spec/services/merge_requests/reload_diffs_service_spec.rb b/spec/services/merge_requests/reload_diffs_service_spec.rb
index a0a27d247fc..5acd01828cb 100644
--- a/spec/services/merge_requests/reload_diffs_service_spec.rb
+++ b/spec/services/merge_requests/reload_diffs_service_spec.rb
@@ -31,34 +31,25 @@ describe MergeRequests::ReloadDiffsService, :use_clean_rails_memory_store_cachin
end
context 'cache clearing' do
- before do
- allow_any_instance_of(Gitlab::Diff::File).to receive(:text?).and_return(true)
- allow_any_instance_of(Gitlab::Diff::File).to receive(:diffable?).and_return(true)
- end
-
- it 'retrieves the diff files to cache the highlighted result' do
- new_diff = merge_request.create_merge_request_diff
- cache_key = new_diff.diffs_collection.cache_key
-
- expect(merge_request).to receive(:create_merge_request_diff).and_return(new_diff)
- expect(Rails.cache).to receive(:read).with(cache_key).and_call_original
- expect(Rails.cache).to receive(:write).with(cache_key, anything, anything).and_call_original
-
- subject.execute
- end
-
it 'clears the cache for older diffs on the merge request' do
old_diff = merge_request.merge_request_diff
old_cache_key = old_diff.diffs_collection.cache_key
- new_diff = merge_request.create_merge_request_diff
- new_cache_key = new_diff.diffs_collection.cache_key
- expect(merge_request).to receive(:create_merge_request_diff).and_return(new_diff)
expect(Rails.cache).to receive(:delete).with(old_cache_key).and_call_original
- expect(Rails.cache).to receive(:read).with(new_cache_key).and_call_original
- expect(Rails.cache).to receive(:write).with(new_cache_key, anything, anything).and_call_original
+
subject.execute
end
+
+ it 'avoids N+1 queries', :request_store do
+ current_user
+ merge_request
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ subject.execute
+ end.count
+
+ expect { subject.execute }.not_to exceed_query_limit(control_count)
+ end
end
end
end
diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb
index 8ab09412f55..53bce15735c 100644
--- a/spec/services/merge_requests/squash_service_spec.rb
+++ b/spec/services/merge_requests/squash_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe MergeRequests::SquashService do
+ include GitHelpers
+
let(:service) { described_class.new(project, user, {}) }
let(:user) { project.owner }
let(:project) { create(:project, :repository) }
@@ -63,9 +65,7 @@ describe MergeRequests::SquashService do
end
it 'has the same diff as the merge request, but a different SHA' do
- rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged
- end
+ rugged = rugged_repo(project.repository)
mr_diff = rugged.diff(merge_request.diff_base_sha, merge_request.diff_head_sha)
squash_diff = rugged.diff(merge_request.diff_start_sha, squash_sha)
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index f0029af83cc..be5ad849ba7 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -109,11 +109,12 @@ describe MergeRequests::UpdateService, :mailer do
expect(note.note).to include "assigned to #{user2.to_reference}"
end
- it 'creates system note about merge_request label edit' do
- note = find_note('added ~')
+ it 'creates a resource label event' do
+ event = merge_request.resource_label_events.last
- expect(note).not_to be_nil
- expect(note.note).to include "added #{label.to_reference} label"
+ expect(event).not_to be_nil
+ expect(event.label_id).to eq label.id
+ expect(event.user_id).to eq user.id
end
it 'creates system note about title change' do
@@ -314,7 +315,42 @@ describe MergeRequests::UpdateService, :mailer do
end
end
- context 'when the milestone change' do
+ context 'when the milestone is removed' do
+ let!(:non_subscriber) { create(:user) }
+
+ let!(:subscriber) do
+ create(:user) do |u|
+ merge_request.toggle_subscription(u, project)
+ project.add_developer(u)
+ end
+ end
+
+ it_behaves_like 'system notes for milestones'
+
+ it 'sends notifications for subscribers of changed milestone' do
+ merge_request.milestone = create(:milestone)
+
+ merge_request.save
+
+ perform_enqueued_jobs do
+ update_merge_request(milestone_id: "")
+ end
+
+ should_email(subscriber)
+ should_not_email(non_subscriber)
+ end
+ end
+
+ context 'when the milestone is changed' do
+ let!(:non_subscriber) { create(:user) }
+
+ let!(:subscriber) do
+ create(:user) do |u|
+ merge_request.toggle_subscription(u, project)
+ project.add_developer(u)
+ end
+ end
+
it 'marks pending todos as done' do
update_merge_request({ milestone: create(:milestone) })
@@ -322,6 +358,15 @@ describe MergeRequests::UpdateService, :mailer do
end
it_behaves_like 'system notes for milestones'
+
+ it 'sends notifications for subscribers of changed milestone' do
+ perform_enqueued_jobs do
+ update_merge_request(milestone: create(:milestone))
+ end
+
+ should_email(subscriber)
+ should_not_email(non_subscriber)
+ end
end
context 'when the labels change' do
@@ -548,8 +593,8 @@ describe MergeRequests::UpdateService, :mailer do
end
context 'setting `allow_collaboration`' do
- let(:target_project) { create(:project, :public) }
- let(:source_project) { fork_project(target_project) }
+ let(:target_project) { create(:project, :repository, :public) }
+ let(:source_project) { fork_project(target_project, nil, repository: true) }
let(:user) { create(:user) }
let(:merge_request) do
create(:merge_request,
diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb
index 6f3612501f4..9d2be30c636 100644
--- a/spec/services/milestones/destroy_service_spec.rb
+++ b/spec/services/milestones/destroy_service_spec.rb
@@ -2,10 +2,8 @@ require 'spec_helper'
describe Milestones::DestroyService do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:milestone) { create(:milestone, title: 'Milestone v1.0', project: project) }
- let!(:issue) { create(:issue, project: project, milestone: milestone) }
- let!(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) }
before do
project.add_maintainer(user)
@@ -23,12 +21,23 @@ describe Milestones::DestroyService do
end
it 'deletes milestone id from issuables' do
+ issue = create(:issue, project: project, milestone: milestone)
+ merge_request = create(:merge_request, source_project: project, milestone: milestone)
+
service.execute(milestone)
expect(issue.reload.milestone).to be_nil
expect(merge_request.reload.milestone).to be_nil
end
+ it 'logs destroy event' do
+ service.execute(milestone)
+
+ event = Event.where(project_id: milestone.project_id, target_type: 'Milestone')
+
+ expect(event.count).to eq(1)
+ end
+
context 'group milestones' do
let(:group) { create(:group) }
let(:group_milestone) { create(:milestone, group: group) }
@@ -38,13 +47,20 @@ describe Milestones::DestroyService do
group.add_developer(user)
end
- it { expect(service.execute(group_milestone)).to be_nil }
+ it { expect(service.execute(group_milestone)).to eq(group_milestone) }
- it 'does not update milestone issuables' do
- expect(MergeRequests::UpdateService).not_to receive(:new)
- expect(Issues::UpdateService).not_to receive(:new)
+ it 'deletes milestone id from issuables' do
+ issue = create(:issue, project: project, milestone: group_milestone)
+ merge_request = create(:merge_request, source_project: project, milestone: group_milestone)
service.execute(group_milestone)
+
+ expect(issue.reload.milestone).to be_nil
+ expect(merge_request.reload.milestone).to be_nil
+ end
+
+ it 'does not log destroy event' do
+ expect { service.execute(group_milestone) }.not_to change { Event.count }
end
end
end
diff --git a/spec/services/notes/build_service_spec.rb b/spec/services/notes/build_service_spec.rb
index 6e1c1fe6c02..ff85c261cd4 100644
--- a/spec/services/notes/build_service_spec.rb
+++ b/spec/services/notes/build_service_spec.rb
@@ -4,6 +4,8 @@ describe Notes::BuildService do
let(:note) { create(:discussion_note_on_issue) }
let(:project) { note.project }
let(:author) { note.author }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:mr_note) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: author) }
describe '#execute' do
context 'when in_reply_to_discussion_id is specified' do
@@ -12,6 +14,19 @@ describe Notes::BuildService do
new_note = described_class.new(project, author, note: 'Test', in_reply_to_discussion_id: note.discussion_id).execute
expect(new_note).to be_valid
expect(new_note.in_reply_to?(note)).to be_truthy
+ expect(new_note.resolved?).to be_falsey
+ end
+
+ context 'when discussion is resolved' do
+ before do
+ mr_note.resolve!(author)
+ end
+
+ it 'resolves the note' do
+ new_note = described_class.new(project, author, note: 'Test', in_reply_to_discussion_id: mr_note.discussion_id).execute
+ expect(new_note).to be_valid
+ expect(new_note.resolved?).to be_truthy
+ end
end
end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index b1290fd0d47..80b015d4cd0 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -57,6 +57,57 @@ describe Notes::CreateService do
end
end
+ context 'noteable highlight cache clearing' do
+ let(:project_with_repo) { create(:project, :repository) }
+ let(:merge_request) do
+ create(:merge_request, source_project: project_with_repo,
+ target_project: project_with_repo)
+ end
+
+ let(:position) do
+ Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ diff_refs: merge_request.diff_refs)
+ end
+
+ let(:new_opts) do
+ opts.merge(in_reply_to_discussion_id: nil,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: position.to_h)
+ end
+
+ before do
+ allow_any_instance_of(Gitlab::Diff::Position)
+ .to receive(:unfolded_diff?) { true }
+ end
+
+ it 'clears noteable diff cache when it was unfolded for the note position' do
+ expect_any_instance_of(Gitlab::Diff::HighlightCache).to receive(:clear)
+
+ described_class.new(project_with_repo, user, new_opts).execute
+ end
+
+ it 'does not clear cache when note is not the first of the discussion' do
+ prev_note =
+ create(:diff_note_on_merge_request, noteable: merge_request,
+ project: project_with_repo)
+ reply_opts =
+ opts.merge(in_reply_to_discussion_id: prev_note.discussion_id,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: position.to_h)
+
+ expect(merge_request).not_to receive(:diffs)
+
+ described_class.new(project_with_repo, user, reply_opts).execute
+ end
+ end
+
context 'note diff file' do
let(:project_with_repo) { create(:project, :repository) }
let(:merge_request) do
diff --git a/spec/services/notes/destroy_service_spec.rb b/spec/services/notes/destroy_service_spec.rb
index 64445be560e..b1f4e87e8ea 100644
--- a/spec/services/notes/destroy_service_spec.rb
+++ b/spec/services/notes/destroy_service_spec.rb
@@ -21,5 +21,38 @@ describe Notes::DestroyService do
expect { described_class.new(project, user).execute(note) }
.to change { user.todos_pending_count }.from(1).to(0)
end
+
+ context 'noteable highlight cache clearing' do
+ let(:repo_project) { create(:project, :repository) }
+ let(:merge_request) do
+ create(:merge_request, source_project: repo_project,
+ target_project: repo_project)
+ end
+
+ let(:note) do
+ create(:diff_note_on_merge_request, project: repo_project,
+ noteable: merge_request)
+ end
+
+ before do
+ allow(note.position).to receive(:unfolded_diff?) { true }
+ end
+
+ it 'clears noteable diff cache when it was unfolded for the note position' do
+ expect(merge_request).to receive_message_chain(:diffs, :clear_cache)
+
+ described_class.new(repo_project, user).execute(note)
+ end
+
+ it 'does not clear cache when note is not the first of the discussion' do
+ reply_note = create(:diff_note_on_merge_request, in_reply_to: note,
+ project: repo_project,
+ noteable: merge_request)
+
+ expect(merge_request).not_to receive(:diffs)
+
+ described_class.new(repo_project, user).execute(reply_note)
+ end
+ end
end
end
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index a8c994c101c..14d62763a5b 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Notes::QuickActionsService do
shared_context 'note on noteable' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:assignee) { create(:user) }
diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb
index 14ba6b7bed2..cea5ea125b9 100644
--- a/spec/services/notification_recipient_service_spec.rb
+++ b/spec/services/notification_recipient_service_spec.rb
@@ -10,27 +10,50 @@ describe NotificationRecipientService do
let(:issue) { create(:issue, project: project, assignees: [assignee]) }
let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id) }
- def create_watcher
- watcher = create(:user)
- create(:notification_setting, source: project, user: watcher, level: :watch)
+ shared_examples 'no N+1 queries' do
+ it 'avoids N+1 queries', :request_store do
+ create_user
- other_projects.each do |other_project|
- create(:notification_setting, source: other_project, user: watcher, level: :watch)
+ service.build_new_note_recipients(note)
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ service.build_new_note_recipients(note)
+ end
+
+ create_user
+
+ expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count)
end
end
- it 'avoids N+1 queries', :request_store do
- create_watcher
+ context 'when there are multiple watchers' do
+ def create_user
+ watcher = create(:user)
+ create(:notification_setting, source: project, user: watcher, level: :watch)
+
+ other_projects.each do |other_project|
+ create(:notification_setting, source: other_project, user: watcher, level: :watch)
+ end
+ end
- service.build_new_note_recipients(note)
+ include_examples 'no N+1 queries'
+ end
- control_count = ActiveRecord::QueryRecorder.new do
- service.build_new_note_recipients(note)
+ context 'when there are multiple subscribers' do
+ def create_user
+ subscriber = create(:user)
+ issue.subscriptions.create(user: subscriber, project: project, subscribed: true)
end
- create_watcher
+ include_examples 'no N+1 queries'
- expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count)
+ context 'when the project is private' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ include_examples 'no N+1 queries'
+ end
end
end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index c442f6fe32f..2d8da7673dc 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -13,6 +13,54 @@ describe NotificationService, :mailer do
end
end
+ shared_examples 'altered milestone notification on issue' do
+ it 'sends the email to the correct people' do
+ should_email(subscriber_to_new_milestone)
+ issue.assignees.each do |a|
+ should_email(a)
+ end
+ should_email(@u_watcher)
+ should_email(@u_guest_watcher)
+ should_email(@u_participant_mentioned)
+ should_email(@subscriber)
+ should_email(@subscribed_participant)
+ should_email(@watcher_and_subscriber)
+ should_not_email(@u_guest_custom)
+ should_not_email(@u_committer)
+ should_not_email(@unsubscriber)
+ should_not_email(@u_participating)
+ should_not_email(@u_lazy_participant)
+ should_not_email(issue.author)
+ should_not_email(@u_disabled)
+ should_not_email(@u_custom_global)
+ should_not_email(@u_mentioned)
+ end
+ end
+
+ shared_examples 'altered milestone notification on merge request' do
+ it 'sends the email to the correct people' do
+ should_email(subscriber_to_new_milestone)
+ merge_request.assignees.each do |a|
+ should_email(a)
+ end
+ should_email(@u_watcher)
+ should_email(@u_guest_watcher)
+ should_email(@u_participant_mentioned)
+ should_email(@subscriber)
+ should_email(@subscribed_participant)
+ should_email(@watcher_and_subscriber)
+ should_not_email(@u_guest_custom)
+ should_not_email(@u_committer)
+ should_not_email(@unsubscriber)
+ should_not_email(@u_participating)
+ should_not_email(@u_lazy_participant)
+ should_not_email(merge_request.author)
+ should_not_email(@u_disabled)
+ should_not_email(@u_custom_global)
+ should_not_email(@u_mentioned)
+ end
+ end
+
shared_examples 'notifications for new mentions' do
it 'sends no emails when no new mentions are present' do
send_notifications
@@ -952,6 +1000,96 @@ describe NotificationService, :mailer do
end
end
+ describe '#removed_milestone_issue' do
+ it_behaves_like 'altered milestone notification on issue' do
+ let(:milestone) { create(:milestone, project: project, issues: [issue]) }
+ let!(:subscriber_to_new_milestone) { create(:user) { |u| issue.toggle_subscription(u, project) } }
+
+ before do
+ notification.removed_milestone_issue(issue, issue.author)
+ end
+ end
+
+ context 'confidential issues' do
+ let(:author) { create(:user) }
+ let(:assignee) { create(:user) }
+ let(:non_member) { create(:user) }
+ let(:member) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:admin) { create(:admin) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignees: [assignee]) }
+ let(:milestone) { create(:milestone, project: project, issues: [confidential_issue]) }
+
+ it "emails subscribers of the issue's milestone that can read the issue" do
+ project.add_developer(member)
+ project.add_guest(guest)
+
+ confidential_issue.subscribe(non_member, project)
+ confidential_issue.subscribe(author, project)
+ confidential_issue.subscribe(assignee, project)
+ confidential_issue.subscribe(member, project)
+ confidential_issue.subscribe(guest, project)
+ confidential_issue.subscribe(admin, project)
+
+ reset_delivered_emails!
+
+ notification.removed_milestone_issue(confidential_issue, @u_disabled)
+
+ should_not_email(non_member)
+ should_not_email(guest)
+ should_email(author)
+ should_email(assignee)
+ should_email(member)
+ should_email(admin)
+ end
+ end
+ end
+
+ describe '#changed_milestone_issue' do
+ it_behaves_like 'altered milestone notification on issue' do
+ let(:new_milestone) { create(:milestone, project: project, issues: [issue]) }
+ let!(:subscriber_to_new_milestone) { create(:user) { |u| issue.toggle_subscription(u, project) } }
+
+ before do
+ notification.changed_milestone_issue(issue, new_milestone, issue.author)
+ end
+ end
+
+ context 'confidential issues' do
+ let(:author) { create(:user) }
+ let(:assignee) { create(:user) }
+ let(:non_member) { create(:user) }
+ let(:member) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:admin) { create(:admin) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignees: [assignee]) }
+ let(:new_milestone) { create(:milestone, project: project, issues: [confidential_issue]) }
+
+ it "emails subscribers of the issue's milestone that can read the issue" do
+ project.add_developer(member)
+ project.add_guest(guest)
+
+ confidential_issue.subscribe(non_member, project)
+ confidential_issue.subscribe(author, project)
+ confidential_issue.subscribe(assignee, project)
+ confidential_issue.subscribe(member, project)
+ confidential_issue.subscribe(guest, project)
+ confidential_issue.subscribe(admin, project)
+
+ reset_delivered_emails!
+
+ notification.changed_milestone_issue(confidential_issue, new_milestone, @u_disabled)
+
+ should_not_email(non_member)
+ should_not_email(guest)
+ should_email(author)
+ should_email(assignee)
+ should_email(member)
+ should_email(admin)
+ end
+ end
+ end
+
describe '#close_issue' do
before do
update_custom_notification(:close_issue, @u_guest_custom, resource: project)
@@ -1304,6 +1442,28 @@ describe NotificationService, :mailer do
end
end
+ describe '#removed_milestone_merge_request' do
+ it_behaves_like 'altered milestone notification on merge request' do
+ let(:milestone) { create(:milestone, project: project, merge_requests: [merge_request]) }
+ let!(:subscriber_to_new_milestone) { create(:user) { |u| merge_request.toggle_subscription(u, project) } }
+
+ before do
+ notification.removed_milestone_merge_request(merge_request, merge_request.author)
+ end
+ end
+ end
+
+ describe '#changed_milestone_merge_request' do
+ it_behaves_like 'altered milestone notification on merge request' do
+ let(:new_milestone) { create(:milestone, project: project, merge_requests: [merge_request]) }
+ let!(:subscriber_to_new_milestone) { create(:user) { |u| merge_request.toggle_subscription(u, project) } }
+
+ before do
+ notification.changed_milestone_merge_request(merge_request, new_milestone, merge_request.author)
+ end
+ end
+ end
+
describe '#merge_request_unmergeable' do
it "sends email to merge request author" do
notification.merge_request_unmergeable(merge_request)
@@ -1969,6 +2129,23 @@ describe NotificationService, :mailer do
end
end
+ context 'Auto DevOps notifications' do
+ describe '#autodevops_disabled' do
+ let(:owner) { create(:user) }
+ let(:namespace) { create(:namespace, owner: owner) }
+ let(:project) { create(:project, :repository, :auto_devops, namespace: namespace) }
+ let(:pipeline_user) { create(:user) }
+ let(:pipeline) { create(:ci_pipeline, :failed, project: project, user: pipeline_user) }
+
+ it 'emails project owner and user that triggered the pipeline' do
+ notification.autodevops_disabled(pipeline, [owner.email, pipeline_user.email])
+
+ should_email(owner)
+ should_email(pipeline_user)
+ end
+ end
+ end
+
def build_team(project)
@u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create_global_setting_for(create(:user), :participating)
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index 507909d9231..b69977c812a 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -101,4 +101,11 @@ describe PreviewMarkdownService do
expect(result[:markdown_engine]).to eq :common_mark
end
+
+ it 'honors the legacy_render parameter' do
+ service = described_class.new(project, user, { legacy_render: '1' })
+ result = service.execute
+
+ expect(result[:markdown_engine]).to eq :redcarpet
+ end
end
diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb
index cd52bc88f4c..4dd6c6dab86 100644
--- a/spec/services/projects/after_import_service_spec.rb
+++ b/spec/services/projects/after_import_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Projects::AfterImportService do
+ include GitHelpers
+
subject { described_class.new(project) }
let(:project) { create(:project, :repository) }
@@ -53,7 +55,7 @@ describe Projects::AfterImportService do
end
def rugged
- Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged }
+ rugged_repo(repository)
end
end
end
diff --git a/spec/services/projects/after_rename_service_spec.rb b/spec/services/projects/after_rename_service_spec.rb
new file mode 100644
index 00000000000..b4718a07204
--- /dev/null
+++ b/spec/services/projects/after_rename_service_spec.rb
@@ -0,0 +1,198 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::AfterRenameService do
+ let(:rugged_config) { rugged_repo(project.repository).config }
+
+ describe '#execute' do
+ context 'using legacy storage' do
+ let(:project) { create(:project, :repository, :legacy_storage) }
+ let(:gitlab_shell) { Gitlab::Shell.new }
+ let(:project_storage) { project.send(:storage) }
+
+ before do
+ # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
+ # call. This makes testing a bit easier.
+ allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
+
+ allow(project)
+ .to receive(:previous_changes)
+ .and_return('path' => ['foo'])
+
+ allow(project)
+ .to receive(:path_was)
+ .and_return('foo')
+
+ stub_feature_flags(skip_hashed_storage_upgrade: false)
+ end
+
+ it 'renames a repository' do
+ stub_container_registry_config(enabled: false)
+
+ expect(gitlab_shell).to receive(:mv_repository)
+ .ordered
+ .with(project.repository_storage, "#{project.namespace.full_path}/foo", "#{project.full_path}")
+ .and_return(true)
+
+ expect(gitlab_shell).to receive(:mv_repository)
+ .ordered
+ .with(project.repository_storage, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki")
+ .and_return(true)
+
+ expect_any_instance_of(SystemHooksService)
+ .to receive(:execute_hooks_for)
+ .with(project, :rename)
+
+ expect_any_instance_of(Gitlab::UploadsTransfer)
+ .to receive(:rename_project)
+ .with('foo', project.path, project.namespace.full_path)
+
+ expect(project).to receive(:expire_caches_before_rename)
+
+ described_class.new(project).execute
+ end
+
+ context 'container registry with images' do
+ let(:container_repository) { create(:container_repository) }
+
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: :any, tags: ['tag'])
+ project.container_repositories << container_repository
+ end
+
+ it 'raises a RenameFailedError' do
+ expect { described_class.new(project).execute }
+ .to raise_error(described_class::RenameFailedError)
+ end
+ end
+
+ context 'gitlab pages' do
+ before do
+ expect(project_storage).to receive(:rename_repo) { true }
+ end
+
+ it 'moves pages folder to new location' do
+ expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
+
+ described_class.new(project).execute
+ end
+ end
+
+ context 'attachments' do
+ before do
+ expect(project_storage).to receive(:rename_repo) { true }
+ end
+
+ it 'moves uploads folder to new location' do
+ expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)
+
+ described_class.new(project).execute
+ end
+ end
+
+ it 'updates project full path in .git/config' do
+ allow(project_storage).to receive(:rename_repo).and_return(true)
+
+ described_class.new(project).execute
+
+ expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
+ end
+ end
+
+ context 'using hashed storage' do
+ let(:project) { create(:project, :repository, skip_disk_validation: true) }
+ let(:gitlab_shell) { Gitlab::Shell.new }
+ let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
+ let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) }
+ let(:hashed_path) { File.join(hashed_prefix, hash) }
+
+ before do
+ # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
+ # call. This makes testing a bit easier.
+ allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
+ allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
+
+ stub_feature_flags(skip_hashed_storage_upgrade: false)
+ stub_application_setting(hashed_storage_enabled: true)
+ end
+
+ context 'migration to hashed storage' do
+ it 'calls HashedStorageMigrationService with correct options' do
+ project = create(:project, :repository, :legacy_storage)
+ allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
+
+ expect_next_instance_of(::Projects::HashedStorageMigrationService) do |service|
+ expect(service).to receive(:execute).and_return(true)
+ end
+
+ described_class.new(project).execute
+ end
+ end
+
+ it 'renames a repository' do
+ stub_container_registry_config(enabled: false)
+
+ expect(gitlab_shell).not_to receive(:mv_repository)
+
+ expect_any_instance_of(SystemHooksService)
+ .to receive(:execute_hooks_for)
+ .with(project, :rename)
+
+ expect(project).to receive(:expire_caches_before_rename)
+
+ described_class.new(project).execute
+ end
+
+ context 'container registry with images' do
+ let(:container_repository) { create(:container_repository) }
+
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: :any, tags: ['tag'])
+ project.container_repositories << container_repository
+ end
+
+ it 'raises a RenameFailedError' do
+ expect { described_class.new(project).execute }
+ .to raise_error(described_class::RenameFailedError)
+ end
+ end
+
+ context 'gitlab pages' do
+ it 'moves pages folder to new location' do
+ expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
+
+ described_class.new(project).execute
+ end
+ end
+
+ context 'attachments' do
+ it 'keeps uploads folder location unchanged' do
+ expect_any_instance_of(Gitlab::UploadsTransfer).not_to receive(:rename_project)
+
+ described_class.new(project).execute
+ end
+
+ context 'when not rolled out' do
+ let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) }
+
+ it 'moves pages folder to hashed storage' do
+ expect_next_instance_of(Projects::HashedStorage::MigrateAttachmentsService) do |service|
+ expect(service).to receive(:execute)
+ end
+
+ described_class.new(project).execute
+ end
+ end
+ end
+
+ it 'updates project full path in .git/config' do
+ described_class.new(project).execute
+
+ expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/auto_devops/disable_service_spec.rb b/spec/services/projects/auto_devops/disable_service_spec.rb
new file mode 100644
index 00000000000..76977d7a1a7
--- /dev/null
+++ b/spec/services/projects/auto_devops/disable_service_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Projects::AutoDevops::DisableService, '#execute' do
+ let(:project) { create(:project, :repository, :auto_devops) }
+ let(:auto_devops) { project.auto_devops }
+
+ subject { described_class.new(project).execute }
+
+ context 'when Auto DevOps disabled at instance level' do
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+ end
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when Auto DevOps enabled at instance level' do
+ before do
+ stub_application_setting(auto_devops_enabled: true)
+ end
+
+ context 'when Auto DevOps explicitly enabled on project' do
+ before do
+ auto_devops.update!(enabled: true)
+ end
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when Auto DevOps explicitly disabled on project' do
+ before do
+ auto_devops.update!(enabled: false)
+ end
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when Auto DevOps is implicitly enabled' do
+ before do
+ auto_devops.update!(enabled: nil)
+ end
+
+ context 'when is the first pipeline failure' do
+ before do
+ create(:ci_pipeline, :failed, :auto_devops_source, project: project)
+ end
+
+ it 'should disable Auto DevOps for project' do
+ subject
+
+ expect(auto_devops.enabled).to eq(false)
+ end
+ end
+
+ context 'when it is not the first pipeline failure' do
+ before do
+ create_list(:ci_pipeline, 2, :failed, :auto_devops_source, project: project)
+ end
+
+ it 'should explicitly disable Auto DevOps for project' do
+ subject
+
+ expect(auto_devops.reload.enabled).to eq(false)
+ end
+ end
+
+ context 'when an Auto DevOps pipeline has succeeded before' do
+ before do
+ create(:ci_pipeline, :success, :auto_devops_source, project: project)
+ end
+
+ it 'should not disable Auto DevOps for project' do
+ subject
+
+ expect(auto_devops.reload.enabled).to be_nil
+ end
+ end
+ end
+
+ context 'when project does not have an Auto DevOps record related' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ create(:ci_pipeline, :failed, :auto_devops_source, project: project)
+ end
+
+ it 'should disable Auto DevOps for project' do
+ subject
+ auto_devops = project.reload.auto_devops
+
+ expect(auto_devops.enabled).to eq(false)
+ end
+
+ it 'should create a ProjectAutoDevops record' do
+ expect { subject }.to change { ProjectAutoDevops.count }.from(0).to(1)
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index e98df375d48..373fe7cb7dd 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -148,7 +148,7 @@ describe Projects::AutocompleteService do
let!(:label1) { create(:label, project: project) }
let!(:label2) { create(:label, project: project) }
let!(:sub_group_label) { create(:group_label, group: sub_group) }
- let!(:parent_group_label) { create(:group_label, group: group.parent) }
+ let!(:parent_group_label) { create(:group_label, group: group.parent, group_id: group.id) }
before do
create(:group_member, group: group, user: user)
@@ -156,7 +156,7 @@ describe Projects::AutocompleteService do
it 'returns labels from project and ancestor groups' do
service = described_class.new(project, user)
- results = service.labels_as_hash
+ results = service.labels_as_hash(nil)
expected_labels = [label1, label2, parent_group_label]
expect_labels_to_equal(results, expected_labels)
diff --git a/spec/services/projects/container_repository/destroy_service_spec.rb b/spec/services/projects/container_repository/destroy_service_spec.rb
new file mode 100644
index 00000000000..affcc66d2bb
--- /dev/null
+++ b/spec/services/projects/container_repository/destroy_service_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::ContainerRepository::DestroyService do
+ set(:user) { create(:user) }
+ set(:project) { create(:project, :private) }
+
+ subject { described_class.new(project, user) }
+
+ before do
+ stub_container_registry_config(enabled: true)
+ end
+
+ context 'when user does not have access to registry' do
+ let!(:repository) { create(:container_repository, :root, project: project) }
+
+ it 'does not delete a repository' do
+ expect { subject.execute(repository) }.not_to change { ContainerRepository.all.count }
+ end
+ end
+
+ context 'when user has access to registry' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when root container repository exists' do
+ let!(:repository) { create(:container_repository, :root, project: project) }
+
+ before do
+ stub_container_registry_tags(repository: :any, tags: [])
+ end
+
+ it 'deletes the repository' do
+ expect(repository).to receive(:delete_tags!).and_call_original
+ expect { described_class.new(project, user).execute(repository) }.to change { ContainerRepository.all.count }.by(-1)
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index bb3f1501f0e..08de27ca44a 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Projects::CreateService, '#execute' do
+ include GitHelpers
+
let(:gitlab_shell) { Gitlab::Shell.new }
let(:user) { create :user }
let(:opts) do
@@ -293,11 +295,20 @@ describe Projects::CreateService, '#execute' do
end
end
+ it 'calls the passed block' do
+ fake_block = double('block')
+ opts[:relations_block] = fake_block
+
+ expect_next_instance_of(Project) do |project|
+ expect(fake_block).to receive(:call).with(project)
+ end
+
+ create_project(user, opts)
+ end
+
it 'writes project full path to .git/config' do
project = create_project(user, opts)
- rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged
- end
+ rugged = rugged_repo(project.repository)
expect(rugged.config['gitlab.fullpath']).to eq project.full_path
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index e428808ab68..12ddf8447bd 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -65,10 +65,12 @@ describe Projects::DestroyService do
context 'Sidekiq inline' do
before do
- # Run sidekiq immediatly to check that renamed repository will be removed
+ # Run sidekiq immediately to check that renamed repository will be removed
perform_enqueued_jobs { destroy_project(project, user, {}) }
end
+ it_behaves_like 'deleting the project'
+
context 'when has remote mirrors' do
let!(:project) do
create(:project, :repository, namespace: user.namespace).tap do |project|
@@ -82,13 +84,28 @@ describe Projects::DestroyService do
end
end
- it_behaves_like 'deleting the project'
-
it 'invalidates personal_project_count cache' do
expect(user).to receive(:invalidate_personal_projects_count)
destroy_project(project, user)
end
+
+ context 'when project has exports' do
+ let!(:project_with_export) do
+ create(:project, :repository, namespace: user.namespace).tap do |project|
+ create(:import_export_upload,
+ project: project,
+ export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+ end
+ end
+ let!(:async) { true }
+
+ it 'destroys project and export' do
+ expect { destroy_project(project_with_export, user) }.to change(ImportExportUpload, :count).by(-1)
+
+ expect(Project.all).not_to include(project_with_export)
+ end
+ end
end
context 'Sidekiq fake' do
@@ -204,7 +221,7 @@ describe Projects::DestroyService do
context 'when image repository deletion fails' do
it 'raises an exception' do
expect_any_instance_of(ContainerRepository)
- .to receive(:delete_tags!).and_return(false)
+ .to receive(:delete_tags!).and_raise(RuntimeError)
expect(destroy_project(project, user)).to be false
end
@@ -242,7 +259,6 @@ describe Projects::DestroyService do
before do
project.lfs_objects << create(:lfs_object)
- forked_project.forked_project_link.destroy
forked_project.reload
end
diff --git a/spec/services/projects/detect_repository_languages_service_spec.rb b/spec/services/projects/detect_repository_languages_service_spec.rb
index f90d558938f..deea1189cdf 100644
--- a/spec/services/projects/detect_repository_languages_service_spec.rb
+++ b/spec/services/projects/detect_repository_languages_service_spec.rb
@@ -5,10 +5,6 @@ describe Projects::DetectRepositoryLanguagesService, :clean_gitlab_redis_shared_
subject { described_class.new(project, project.owner) }
- before do
- allow(Feature).to receive(:disabled?).and_return(false)
- end
-
describe '#execute' do
context 'without previous detection' do
it 'inserts new programming languages in the database' do
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index f89f9b54f53..a3d24ae312a 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -31,6 +31,10 @@ describe Projects::ForkService do
it { is_expected.not_to be_persisted }
it { expect(subject.errors[:forked_from_project_id]).to eq(['is forbidden']) }
+
+ it 'does not create a fork network' do
+ expect { subject }.not_to change { @from_project.reload.fork_network }
+ end
end
describe "successfully creates project in the user namespace" do
@@ -70,6 +74,12 @@ describe Projects::ForkService do
expect(fork_network.root_project).to eq(@from_project)
expect(fork_network.projects).to contain_exactly(@from_project, to_project)
end
+
+ it 'imports the repository of the forked project' do
+ to_project = fork_project(@from_project, @to_user, repository: true)
+
+ expect(to_project.empty_repo?).to be_falsy
+ end
end
context 'creating a fork of a fork' do
@@ -247,11 +257,13 @@ describe Projects::ForkService do
context 'if project is not forked' do
it 'creates fork relation' do
- expect(fork_to_project.forked?).to be false
+ expect(fork_to_project.forked?).to be_falsy
expect(forked_from_project(fork_to_project)).to be_nil
subject.execute(fork_to_project)
+ fork_to_project.reload
+
expect(fork_to_project.forked?).to be true
expect(forked_from_project(fork_to_project)).to eq fork_from_project
expect(fork_to_project.forked_from_project).to eq fork_from_project
@@ -264,6 +276,25 @@ describe Projects::ForkService do
expect(fork_from_project.forks_count).to eq(1)
end
+
+ it 'leaves no LFS objects dangling' do
+ create(:lfs_objects_project, project: fork_to_project)
+
+ expect { subject.execute(fork_to_project) }
+ .to change { fork_to_project.lfs_objects_projects.count }
+ .to(0)
+ end
+
+ context 'if the fork is not allowed' do
+ let(:fork_from_project) { create(:project, :private) }
+
+ it 'does not delete the LFS objects' do
+ create(:lfs_objects_project, project: fork_to_project)
+
+ expect { subject.execute(fork_to_project) }
+ .not_to change { fork_to_project.lfs_objects_projects.size }
+ end
+ end
end
end
end
diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
index 5f67c325223..0e82194e9ee 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Projects::HashedStorage::MigrateRepositoryService do
+ include GitHelpers
+
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
@@ -38,9 +40,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do
it 'writes project full path to .git/config' do
service.execute
- rugged_config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged.config['gitlab.fullpath']
- end
+ rugged_config = rugged_repo(project.repository).config['gitlab.fullpath']
expect(rugged_config).to eq project.full_path
end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index e2a600d12d1..06f865dc848 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -125,7 +125,7 @@ describe Projects::ImportService do
project.import_type = 'bitbucket'
end
- it 'succeeds if repository import is successfull' do
+ it 'succeeds if repository import is successful' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_return(true)
expect_any_instance_of(Gitlab::BitbucketImport::Importer).to receive(:execute).and_return(true)
expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return({})
@@ -235,7 +235,7 @@ describe Projects::ImportService do
result = described_class.new(project, user).execute
expect(result[:status]).to eq :error
- expect(result[:message]).to include('Only allowed ports are 22, 80, 443')
+ expect(result[:message]).to include('Only allowed ports are 80, 443')
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 1a85c52fc97..2e07d4f8013 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Projects::TransferService do
+ include GitHelpers
+
let(:gitlab_shell) { Gitlab::Shell.new }
let(:user) { create(:user) }
let(:group) { create(:group) }
@@ -123,7 +125,7 @@ describe Projects::TransferService do
it { expect(project.errors.messages[:new_namespace].first).to eq 'Please select a new namespace for your project.' }
end
- context 'disallow transfering of project with tags' do
+ context 'disallow transferring of project with tags' do
let(:container_repository) { create(:container_repository) }
before do
@@ -169,6 +171,35 @@ describe Projects::TransferService do
it { expect(project.errors[:new_namespace]).to include('Cannot move project') }
end
+ context 'target namespace containing the same project name' do
+ before do
+ group.add_owner(user)
+ project.update(name: 'new_name')
+
+ create(:project, name: 'new_name', group: group, path: 'other')
+
+ @result = transfer_project(project, user, group)
+ end
+
+ it { expect(@result).to eq false }
+ it { expect(project.namespace).to eq(user.namespace) }
+ it { expect(project.errors[:new_namespace]).to include('Project with same name or path in target namespace already exists') }
+ end
+
+ context 'target namespace containing the same project path' do
+ before do
+ group.add_owner(user)
+
+ create(:project, name: 'other-name', path: project.path, group: group)
+
+ @result = transfer_project(project, user, group)
+ end
+
+ it { expect(@result).to eq false }
+ it { expect(project.namespace).to eq(user.namespace) }
+ it { expect(project.errors[:new_namespace]).to include('Project with same name or path in target namespace already exists') }
+ end
+
def transfer_project(project, user, new_namespace)
service = Projects::TransferService.new(project, user)
@@ -262,8 +293,6 @@ describe Projects::TransferService do
end
def rugged_config
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged.config
- end
+ rugged_repo(project.repository).config
end
end
diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb
index 3ec6139bfa6..014aab44281 100644
--- a/spec/services/projects/unlink_fork_service_spec.rb
+++ b/spec/services/projects/unlink_fork_service_spec.rb
@@ -5,13 +5,12 @@ describe Projects::UnlinkForkService do
subject { described_class.new(forked_project, user) }
- let(:fork_link) { forked_project.forked_project_link }
let(:project) { create(:project, :public) }
let(:forked_project) { fork_project(project, user) }
let(:user) { create(:user) }
context 'with opened merge request on the source project' do
- let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: fork_link.forked_from_project) }
+ let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: forked_project.forked_from_project) }
let(:merge_request2) { create(:merge_request, source_project: forked_project, target_project: fork_project(project)) }
let(:merge_request_in_fork) { create(:merge_request, source_project: forked_project, target_project: forked_project) }
@@ -35,12 +34,6 @@ describe Projects::UnlinkForkService do
end
end
- it 'remove fork relation' do
- expect(forked_project.forked_project_link).to receive(:destroy)
-
- subject.execute
- end
-
it 'removes the link to the fork network' do
expect(forked_project.fork_network_member).to be_present
expect(forked_project.fork_network).to be_present
diff --git a/spec/services/projects/update_remote_mirror_service_spec.rb b/spec/services/projects/update_remote_mirror_service_spec.rb
index 5c2e79ff9af..cd903bfe8a5 100644
--- a/spec/services/projects/update_remote_mirror_service_spec.rb
+++ b/spec/services/projects/update_remote_mirror_service_spec.rb
@@ -1,106 +1,88 @@
require 'spec_helper'
describe Projects::UpdateRemoteMirrorService do
- set(:project) { create(:project, :repository) }
- let(:owner) { project.owner }
+ let(:project) { create(:project, :repository) }
let(:remote_project) { create(:forked_project_with_submodules) }
- let(:repository) { project.repository }
- let(:raw_repository) { repository.raw }
let(:remote_mirror) { project.remote_mirrors.create!(url: remote_project.http_url_to_repo, enabled: true, only_protected_branches: false) }
+ let(:remote_name) { remote_mirror.remote_name }
- subject { described_class.new(project, project.creator) }
+ subject(:service) { described_class.new(project, project.creator) }
describe "#execute" do
before do
- repository.add_branch(owner, 'existing-branch', 'master')
+ project.repository.add_branch(project.owner, 'existing-branch', 'master')
allow(remote_mirror).to receive(:update_repository).and_return(true)
end
+ it "ensures the remote exists" do
+ stub_fetch_remote(project, remote_name: remote_name)
+
+ expect(remote_mirror).to receive(:ensure_remote!)
+
+ service.execute(remote_mirror)
+ end
+
it "fetches the remote repository" do
- expect(repository).to receive(:fetch_remote).with(remote_mirror.remote_name, no_tags: true) do
- sync_remote(repository, remote_mirror.remote_name, local_branch_names)
- end
+ expect(project.repository)
+ .to receive(:fetch_remote)
+ .with(remote_mirror.remote_name, no_tags: true)
- subject.execute(remote_mirror)
+ service.execute(remote_mirror)
end
- it "succeeds" do
- allow(repository).to receive(:fetch_remote) { sync_remote(repository, remote_mirror.remote_name, local_branch_names) }
+ it "returns success when updated succeeds" do
+ stub_fetch_remote(project, remote_name: remote_name)
- result = subject.execute(remote_mirror)
+ result = service.execute(remote_mirror)
expect(result[:status]).to eq(:success)
end
context 'when syncing all branches' do
it "push all the branches the first time" do
- allow(repository).to receive(:fetch_remote)
+ stub_fetch_remote(project, remote_name: remote_name)
expect(remote_mirror).to receive(:update_repository).with({})
- subject.execute(remote_mirror)
+ service.execute(remote_mirror)
end
end
context 'when only syncing protected branches' do
- let(:unprotected_branch_name) { 'existing-branch' }
- let(:protected_branch_name) do
- project.repository.branch_names.find { |n| n != unprotected_branch_name }
- end
- let!(:protected_branch) do
- create(:protected_branch, project: project, name: protected_branch_name)
- end
-
- before do
- project.reload
+ it "sync updated protected branches" do
+ stub_fetch_remote(project, remote_name: remote_name)
+ protected_branch = create_protected_branch(project)
remote_mirror.only_protected_branches = true
- end
- it "sync updated protected branches" do
- allow(repository).to receive(:fetch_remote)
- expect(remote_mirror).to receive(:update_repository).with(only_branches_matching: [protected_branch_name])
+ expect(remote_mirror)
+ .to receive(:update_repository)
+ .with(only_branches_matching: [protected_branch.name])
- subject.execute(remote_mirror)
+ service.execute(remote_mirror)
end
- end
- end
- def sync_remote(repository, remote_name, local_branch_names)
- local_branch_names.each do |branch|
- commit = repository.commit(branch)
- repository.write_ref("refs/remotes/#{remote_name}/#{branch}", commit.id) if commit
+ def create_protected_branch(project)
+ branch_name = project.repository.branch_names.find { |n| n != 'existing-branch' }
+ create(:protected_branch, project: project, name: branch_name)
+ end
end
end
- def update_remote_branch(repository, remote_name, branch)
- masterrev = repository.commit('master').id
-
- repository.write_ref("refs/remotes/#{remote_name}/#{branch}", masterrev, force: true)
- repository.expire_branches_cache
+ def stub_fetch_remote(project, remote_name:)
+ allow(project.repository)
+ .to receive(:fetch_remote)
+ .with(remote_name, no_tags: true) { fetch_remote(project.repository, remote_name) }
end
- def update_branch(repository, branch)
- masterrev = repository.commit('master').id
-
- repository.write_ref("refs/heads/#{branch}", masterrev, force: true)
- repository.expire_branches_cache
- end
-
- def generate_tags(repository, *tag_names)
- tag_names.each_with_object([]) do |name, tags|
- tag = repository.find_tag(name)
- target = tag.try(:target)
- target_commit = tag.try(:dereferenced_target)
- tags << Gitlab::Git::Tag.new(repository.raw_repository, {
- name: name,
- target: target,
- target_commit: target_commit
- })
+ def fetch_remote(repository, remote_name)
+ local_branch_names(repository).each do |branch|
+ commit = repository.commit(branch)
+ repository.write_ref("refs/remotes/#{remote_name}/#{branch}", commit.id) if commit
end
end
- def local_branch_names
+ def local_branch_names(repository)
branch_names = repository.branches.map(&:name)
# we want the protected branch to be pushed first
branch_names.unshift(branch_names.delete('master'))
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 9572b4110d5..d58ff2cedc0 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -249,9 +249,20 @@ describe Projects::UpdateService do
expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk')
end
- context 'when hashed storage enabled' do
+ it 'renames the project without upgrading it' do
+ result = update_project(project, admin, path: 'new-path')
+
+ expect(result).not_to include(status: :error)
+ expect(project).to be_valid
+ expect(project.errors).to be_empty
+ expect(project.disk_path).to include('new-path')
+ expect(project.reload.hashed_storage?(:repository)).to be_falsey
+ end
+
+ context 'when hashed storage is enabled' do
before do
stub_application_setting(hashed_storage_enabled: true)
+ stub_feature_flags(skip_hashed_storage_upgrade: false)
end
it 'migrates project to a hashed storage instead of renaming the repo to another legacy name' do
@@ -262,6 +273,22 @@ describe Projects::UpdateService do
expect(project.errors).to be_empty
expect(project.reload.hashed_storage?(:repository)).to be_truthy
end
+
+ context 'when skip_hashed_storage_upgrade feature flag is enabled' do
+ before do
+ stub_feature_flags(skip_hashed_storage_upgrade: true)
+ end
+
+ it 'renames the project without upgrading it' do
+ result = update_project(project, admin, path: 'new-path')
+
+ expect(result).not_to include(status: :error)
+ expect(project).to be_valid
+ expect(project.errors).to be_empty
+ expect(project.disk_path).to include('new-path')
+ expect(project.reload.hashed_storage?(:repository)).to be_falsey
+ end
+ end
end
end
@@ -313,6 +340,27 @@ describe Projects::UpdateService do
call_service
end
end
+
+ context 'when updating #pages_access_level' do
+ subject(:call_service) do
+ update_project(project, admin, project_feature_attributes: { pages_access_level: ProjectFeature::PRIVATE })
+ end
+
+ it 'updates the attribute' do
+ expect { call_service }
+ .to change { project.project_feature.pages_access_level }
+ .to(ProjectFeature::PRIVATE)
+ end
+
+ it 'calls Projects::UpdatePagesConfigurationService' do
+ expect(Projects::UpdatePagesConfigurationService)
+ .to receive(:new)
+ .with(project)
+ .and_call_original
+
+ call_service
+ end
+ end
end
describe '#run_auto_devops_pipeline?' do
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index bf1c157c4a2..938764f40b0 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe QuickActions::InterpretService do
@@ -272,6 +274,28 @@ describe QuickActions::InterpretService do
end
end
+ shared_examples 'lock command' do
+ let(:issue) { create(:issue, project: project, discussion_locked: false) }
+ let(:merge_request) { create(:merge_request, source_project: project, discussion_locked: false) }
+
+ it 'returns discussion_locked: true if content contains /lock' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(discussion_locked: true)
+ end
+ end
+
+ shared_examples 'unlock command' do
+ let(:issue) { create(:issue, project: project, discussion_locked: true) }
+ let(:merge_request) { create(:merge_request, source_project: project, discussion_locked: true) }
+
+ it 'returns discussion_locked: true if content contains /unlock' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(discussion_locked: false)
+ end
+ end
+
shared_examples 'empty command' do
it 'populates {} if content contains an unsupported command' do
_, updates = service.execute(content, issuable)
@@ -291,7 +315,7 @@ describe QuickActions::InterpretService do
end
shared_examples 'award command' do
- it 'toggle award 100 emoji if content containts /award :100:' do
+ it 'toggle award 100 emoji if content contains /award :100:' do
_, updates = service.execute(content, issuable)
expect(updates).to eq(emoji_award: "100")
@@ -356,6 +380,14 @@ describe QuickActions::InterpretService do
end
end
+ shared_examples 'assign command' do
+ it 'assigns to a single user' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(assignee_ids: [developer.id])
+ end
+ end
+
it_behaves_like 'reopen command' do
let(:content) { '/reopen' }
let(:issuable) { issue }
@@ -450,67 +482,56 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
- context 'assign command' do
- let(:content) { "/assign @#{developer.username}" }
-
- context 'Issue' do
- it 'fetches assignee and populates assignee_ids if content contains /assign' do
- _, updates = service.execute(content, issue)
-
- expect(updates[:assignee_ids]).to match_array([developer.id])
- end
+ context 'assign command with one user' do
+ it_behaves_like 'assign command' do
+ let(:content) { "/assign @#{developer.username}" }
+ let(:issuable) { issue }
end
- context 'Merge Request' do
- it 'fetches assignee and populates assignee_ids if content contains /assign' do
- _, updates = service.execute(content, merge_request)
-
- expect(updates).to eq(assignee_ids: [developer.id])
- end
+ it_behaves_like 'assign command' do
+ let(:content) { "/assign @#{developer.username}" }
+ let(:issuable) { merge_request }
end
end
+ # CE does not have multiple assignees
context 'assign command with multiple assignees' do
- let(:content) { "/assign @#{developer.username} @#{developer2.username}" }
-
before do
project.add_developer(developer2)
end
- context 'Issue' do
- it 'fetches assignee and populates assignee_ids if content contains /assign' do
- _, updates = service.execute(content, issue)
-
- expect(updates[:assignee_ids]).to match_array([developer.id])
- end
+ it_behaves_like 'assign command' do
+ let(:content) { "/assign @#{developer.username} @#{developer2.username}" }
+ let(:issuable) { issue }
end
- context 'Merge Request' do
- it 'fetches assignee and populates assignee_ids if content contains /assign' do
- _, updates = service.execute(content, merge_request)
-
- expect(updates).to eq(assignee_ids: [developer.id])
- end
+ it_behaves_like 'assign command' do
+ let(:content) { "/assign @#{developer.username} @#{developer2.username}" }
+ let(:issuable) { merge_request }
end
end
context 'assign command with me alias' do
- let(:content) { "/assign me" }
-
- context 'Issue' do
- it 'fetches assignee and populates assignee_ids if content contains /assign' do
- _, updates = service.execute(content, issue)
+ it_behaves_like 'assign command' do
+ let(:content) { '/assign me' }
+ let(:issuable) { issue }
+ end
- expect(updates).to eq(assignee_ids: [developer.id])
- end
+ it_behaves_like 'assign command' do
+ let(:content) { '/assign me' }
+ let(:issuable) { merge_request }
end
+ end
- context 'Merge Request' do
- it 'fetches assignee and populates assignee_ids if content contains /assign' do
- _, updates = service.execute(content, merge_request)
+ context 'assign command with me alias and whitespace' do
+ it_behaves_like 'assign command' do
+ let(:content) { '/assign me ' }
+ let(:issuable) { issue }
+ end
- expect(updates).to eq(assignee_ids: [developer.id])
- end
+ it_behaves_like 'assign command' do
+ let(:content) { '/assign me ' }
+ let(:issuable) { merge_request }
end
end
@@ -786,6 +807,26 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
+ it_behaves_like 'lock command' do
+ let(:content) { '/lock' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'lock command' do
+ let(:content) { '/lock' }
+ let(:issuable) { merge_request }
+ end
+
+ it_behaves_like 'unlock command' do
+ let(:content) { '/unlock' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'unlock command' do
+ let(:content) { '/unlock' }
+ let(:issuable) { merge_request }
+ end
+
context '/todo' do
let(:content) { '/todo' }
@@ -821,6 +862,13 @@ describe QuickActions::InterpretService do
let(:source_issuable) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) }
let(:content) { "/copy_metadata #{source_issuable.to_reference}" }
+ let(:issuable) { build(:issue, project: project) }
+ end
+
+ it_behaves_like 'copy_metadata command' do
+ let(:source_issuable) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) }
+
+ let(:content) { "/copy_metadata #{source_issuable.to_reference}" }
let(:issuable) { issue }
end
@@ -961,6 +1009,16 @@ describe QuickActions::InterpretService do
let(:content) { '/duplicate #{issue.to_reference}' }
let(:issuable) { issue }
end
+
+ it_behaves_like 'empty command' do
+ let(:content) { '/lock' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'empty command' do
+ let(:content) { '/unlock' }
+ let(:issuable) { issue }
+ end
end
context '/award command' do
@@ -1155,6 +1213,46 @@ describe QuickActions::InterpretService do
end
end
end
+
+ it 'limits to commands passed ' do
+ content = "/shrug\n/close"
+
+ text, commands = service.execute(content, issue, only: [:shrug])
+
+ expect(commands).to be_empty
+ expect(text).to eq("#{described_class::SHRUG}\n/close")
+ end
+
+ context '/create_merge_request command' do
+ let(:branch_name) { '1-feature' }
+ let(:content) { "/create_merge_request #{branch_name}" }
+ let(:issuable) { issue }
+
+ context 'if issuable is not an Issue' do
+ let(:issuable) { merge_request }
+
+ it_behaves_like 'empty command'
+ end
+
+ context "when logged user cannot create_merge_requests in the project" do
+ let(:project) { create(:project, :archived) }
+
+ it_behaves_like 'empty command'
+ end
+
+ context 'when logged user cannot push code to the project' do
+ let(:project) { create(:project, :private) }
+ let(:service) { described_class.new(project, create(:user)) }
+
+ it_behaves_like 'empty command'
+ end
+
+ it 'populates create_merge_request with branch_name and issue iid' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(create_merge_request: { branch_name: branch_name, issue_iid: issuable.iid })
+ end
+ end
end
describe '#explain' do
@@ -1337,7 +1435,7 @@ describe QuickActions::InterpretService do
it 'includes the formatted duration and proper verb' do
_, explanations = service.explain(content, issue)
- expect(explanations).to eq(['Substracts 2h spent time.'])
+ expect(explanations).to eq(['Subtracts 2h spent time.'])
end
end
@@ -1406,5 +1504,27 @@ describe QuickActions::InterpretService do
end
end
end
+
+ describe 'create a merge request' do
+ context 'with no branch name' do
+ let(:content) { '/create_merge_request' }
+
+ it 'uses the default branch name' do
+ _, explanations = service.explain(content, issue)
+
+ expect(explanations).to eq(['Creates a branch and a merge request to resolve this issue'])
+ end
+ end
+
+ context 'with a branch name' do
+ let(:content) { '/create_merge_request foo' }
+
+ it 'uses the given branch name' do
+ _, explanations = service.explain(content, issue)
+
+ expect(explanations).to eq(["Creates branch 'foo' and a merge request to resolve this issue"])
+ end
+ end
+ end
end
end
diff --git a/spec/services/resource_events/change_labels_service_spec.rb b/spec/services/resource_events/change_labels_service_spec.rb
index 41b0fb3eea3..4c9138fb1ef 100644
--- a/spec/services/resource_events/change_labels_service_spec.rb
+++ b/spec/services/resource_events/change_labels_service_spec.rb
@@ -18,6 +18,14 @@ describe ResourceEvents::ChangeLabelsService do
expect(event.action).to eq(action)
end
+ it 'expires resource note etag cache' do
+ expect_any_instance_of(Gitlab::EtagCaching::Store)
+ .to receive(:touch)
+ .with("/#{resource.project.namespace.to_param}/#{resource.project.to_param}/noteable/issue/#{resource.id}/notes")
+
+ described_class.new(resource, author).execute(added_labels: [labels[0]])
+ end
+
context 'when adding a label' do
let(:added) { [labels[0]] }
let(:removed) { [] }
diff --git a/spec/services/resource_events/merge_into_notes_service_spec.rb b/spec/services/resource_events/merge_into_notes_service_spec.rb
new file mode 100644
index 00000000000..c76f6e6f77e
--- /dev/null
+++ b/spec/services/resource_events/merge_into_notes_service_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ResourceEvents::MergeIntoNotesService do
+ def create_event(params)
+ event_params = { action: :add, label: label, issue: resource,
+ user: user }
+
+ create(:resource_label_event, event_params.merge(params))
+ end
+
+ def create_note(params)
+ opts = { noteable: resource, project: project }
+
+ create(:note_on_issue, opts.merge(params))
+ end
+
+ set(:project) { create(:project) }
+ set(:user) { create(:user) }
+ set(:resource) { create(:issue, project: project) }
+ set(:label) { create(:label, project: project) }
+ set(:label2) { create(:label, project: project) }
+ let(:time) { Time.now }
+
+ describe '#execute' do
+ it 'merges label events into notes in order of created_at' do
+ note1 = create_note(created_at: 4.days.ago)
+ note2 = create_note(created_at: 2.days.ago)
+ event1 = create_event(created_at: 3.days.ago)
+ event2 = create_event(created_at: 1.day.ago)
+
+ notes = described_class.new(resource, user).execute([note1, note2])
+
+ expected = [note1, event1, note2, event2].map(&:discussion_id)
+ expect(notes.map(&:discussion_id)).to eq expected
+ end
+
+ it 'squashes events with same time and author into single note' do
+ user2 = create(:user)
+
+ create_event(created_at: time)
+ create_event(created_at: time, label: label2, action: :remove)
+ create_event(created_at: time, user: user2)
+ create_event(created_at: 1.day.ago, label: label2)
+
+ notes = described_class.new(resource, user).execute()
+
+ expected = [
+ "added #{label.to_reference} label and removed #{label2.to_reference} label",
+ "added #{label.to_reference} label",
+ "added #{label2.to_reference} label"
+ ]
+
+ expect(notes.count).to eq 3
+ expect(notes.map(&:note)).to match_array expected
+ end
+
+ it 'fetches only notes created after last_fetched_at' do
+ create_event(created_at: 4.days.ago)
+ event = create_event(created_at: 1.day.ago)
+
+ notes = described_class.new(resource, user,
+ last_fetched_at: 2.days.ago.to_i).execute()
+
+ expect(notes.count).to eq 1
+ expect(notes.first.discussion_id).to eq event.discussion_id
+ end
+
+ it "preloads the note author's status" do
+ event = create_event(created_at: time)
+ create(:user_status, user: event.user)
+
+ notes = described_class.new(resource, user).execute
+
+ expect(notes.first.author.association(:status)).to be_loaded
+ end
+ end
+end
diff --git a/spec/services/submodules/update_service_spec.rb b/spec/services/submodules/update_service_spec.rb
new file mode 100644
index 00000000000..cf92350c1b2
--- /dev/null
+++ b/spec/services/submodules/update_service_spec.rb
@@ -0,0 +1,212 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Submodules::UpdateService do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:user) { create(:user, :commit_email) }
+ let(:branch_name) { project.default_branch }
+ let(:submodule) { 'six' }
+ let(:commit_sha) { 'e25eda1fece24ac7a03624ed1320f82396f35bd8' }
+ let(:commit_message) { 'whatever' }
+ let(:current_sha) { repository.blob_at('HEAD', submodule).id }
+ let(:commit_params) do
+ {
+ submodule: submodule,
+ commit_message: commit_message,
+ commit_sha: commit_sha,
+ branch_name: branch_name
+ }
+ end
+
+ subject { described_class.new(project, user, commit_params) }
+
+ describe "#execute" do
+ shared_examples 'returns error result' do
+ it do
+ result = subject.execute
+
+ expect(result[:status]).to eq :error
+ expect(result[:message]).to eq error_message
+ end
+ end
+
+ context 'when the user is not authorized' do
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'You are not allowed to push into this branch' }
+ end
+ end
+
+ context 'when the user is authorized' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'when the branch is protected' do
+ before do
+ create(:protected_branch, :no_one_can_push, project: project, name: branch_name)
+ end
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'You are not allowed to push into this branch' }
+ end
+ end
+
+ context 'validations' do
+ context 'when submodule' do
+ context 'is empty' do
+ let(:submodule) { '' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'Invalid parameters' }
+ end
+ end
+
+ context 'is not present' do
+ let(:submodule) { nil }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'Invalid parameters' }
+ end
+ end
+
+ context 'is invalid' do
+ let(:submodule) { 'VERSION' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'Invalid submodule path' }
+ end
+ end
+
+ context 'does not exist' do
+ let(:submodule) { 'non-existent-submodule' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'Invalid submodule path' }
+ end
+ end
+
+ context 'has traversal path' do
+ let(:submodule) { '../six' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'Invalid parameters' }
+ end
+ end
+ end
+
+ context 'commit_sha' do
+ context 'is empty' do
+ let(:commit_sha) { '' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'Invalid parameters' }
+ end
+ end
+
+ context 'is not present' do
+ let(:commit_sha) { nil }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'Invalid parameters' }
+ end
+ end
+
+ context 'is invalid' do
+ let(:commit_sha) { '1' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'Invalid parameters' }
+ end
+ end
+
+ context 'is the same as the current ref' do
+ let(:commit_sha) { current_sha }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { "The submodule #{submodule} is already at #{commit_sha}" }
+ end
+ end
+ end
+
+ context 'branch_name' do
+ context 'is empty' do
+ let(:branch_name) { '' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'You can only create or edit files when you are on a branch' }
+ end
+ end
+
+ context 'is not present' do
+ let(:branch_name) { nil }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'You can only create or edit files when you are on a branch' }
+ end
+ end
+
+ context 'does not exist' do
+ let(:branch_name) { 'non/existent-branch' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'You can only create or edit files when you are on a branch' }
+ end
+ end
+
+ context 'when commit message is empty' do
+ let(:commit_message) { '' }
+
+ it 'a default commit message is set' do
+ message = "Update submodule #{submodule} with oid #{commit_sha}"
+
+ expect(repository).to receive(:update_submodule).with(any_args, hash_including(message: message))
+
+ subject.execute
+ end
+ end
+ end
+ end
+
+ context 'when there is an unexpected error' do
+ before do
+ allow(repository).to receive(:update_submodule).and_raise(StandardError, 'error message')
+ end
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'error message' }
+ end
+ end
+
+ it 'updates the submodule reference' do
+ result = subject.execute
+
+ expect(result[:status]).to eq :success
+ expect(result[:result]).to eq repository.head_commit.id
+ expect(repository.blob_at('HEAD', submodule).id).to eq commit_sha
+ end
+
+ context 'when submodule is inside a directory' do
+ let(:submodule) { 'test_inside_folder/another_folder/six' }
+ let(:branch_name) { 'submodule_inside_folder' }
+
+ it 'updates the submodule reference' do
+ expect(repository.blob_at(branch_name, submodule).id).not_to eq commit_sha
+
+ subject.execute
+
+ expect(repository.blob_at(branch_name, submodule).id).to eq commit_sha
+ end
+ end
+
+ context 'when repository is empty' do
+ let(:project) { create(:project, :empty_repo) }
+ let(:branch_name) { 'master' }
+
+ it_behaves_like 'returns error result' do
+ let(:error_message) { 'The repository is empty' }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index e0335880e8e..81b2c17fdb5 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -32,7 +32,7 @@ describe SystemHooksService do
end
it do
- project.old_path_with_namespace = 'transfered_from_path'
+ project.old_path_with_namespace = 'transferred_from_path'
expect(event_data(project, :transfer)).to include(
:event_name, :name, :created_at, :updated_at, :path, :project_id,
:owner_name, :owner_email, :project_visibility,
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 442de61f69b..0fbfcb34e50 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -197,45 +197,6 @@ describe SystemNoteService do
end
end
- describe '.change_label' do
- subject { described_class.change_label(noteable, project, author, added, removed) }
-
- let(:labels) { create_list(:label, 2, project: project) }
- let(:added) { [] }
- let(:removed) { [] }
-
- it_behaves_like 'a system note' do
- let(:action) { 'label' }
- end
-
- context 'with added labels' do
- let(:added) { labels }
- let(:removed) { [] }
-
- it 'sets the note text' do
- expect(subject.note).to eq "added ~#{labels[0].id} ~#{labels[1].id} labels"
- end
- end
-
- context 'with removed labels' do
- let(:added) { [] }
- let(:removed) { labels }
-
- it 'sets the note text' do
- expect(subject.note).to eq "removed ~#{labels[0].id} ~#{labels[1].id} labels"
- end
- end
-
- context 'with added and removed labels' do
- let(:added) { [labels[0]] }
- let(:removed) { [labels[1]] }
-
- it 'sets the note text' do
- expect(subject.note).to eq "added ~#{labels[0].id} and removed ~#{labels[1].id} labels"
- end
- end
- end
-
describe '.change_milestone' do
context 'for a project milestone' do
subject { described_class.change_milestone(noteable, project, author, milestone) }
@@ -288,6 +249,30 @@ describe SystemNoteService do
end
end
+ describe '.change_due_date' do
+ subject { described_class.change_due_date(noteable, project, author, due_date) }
+
+ let(:due_date) { Date.today }
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'due_date' }
+ end
+
+ context 'when due date added' do
+ it 'sets the note text' do
+ expect(subject.note).to eq "changed due date to #{Date.today.to_s(:long)}"
+ end
+ end
+
+ context 'when due date removed' do
+ let(:due_date) { nil }
+
+ it 'sets the note text' do
+ expect(subject.note).to eq 'removed due date'
+ end
+ end
+ end
+
describe '.change_status' do
subject { described_class.change_status(noteable, project, author, status, source) }
@@ -339,7 +324,7 @@ describe SystemNoteService do
end
it "posts the 'merge when pipeline succeeds' system note" do
- expect(subject.note).to eq "canceled the automatic merge"
+ expect(subject.note).to eq "canceled the automatic merge"
end
end
@@ -447,6 +432,20 @@ describe SystemNoteService do
end
end
+ describe '.new_merge_request' do
+ subject { described_class.new_merge_request(noteable, project, author, merge_request) }
+
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'merge' }
+ end
+
+ it 'sets the new merge request note text' do
+ expect(subject.note).to eq("created merge request #{merge_request.to_reference} to address this issue")
+ end
+ end
+
describe '.cross_reference' do
subject { described_class.cross_reference(noteable, mentioner, author) }
@@ -725,7 +724,7 @@ describe SystemNoteService do
let(:jira_tracker) { project.jira_service }
let(:commit) { project.commit }
let(:comment_url) { jira_api_comment_url(jira_issue.id) }
- let(:success_message) { "JiraService SUCCESS: Successfully posted to http://jira.example.net." }
+ let(:success_message) { "SUCCESS: Successfully posted to http://jira.example.net." }
before do
stub_jira_urls(jira_issue.id)
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 9a51c873b30..c52515aefd8 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -10,7 +10,7 @@ describe TodoService do
let(:john_doe) { create(:user) }
let(:skipped) { create(:user) }
let(:skip_users) { [skipped] }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:mentions) { 'FYI: ' + [author, assignee, john_doe, member, guest, non_member, admin, skipped].map(&:to_reference).join(' ') }
let(:directly_addressed) { [author, assignee, john_doe, member, guest, non_member, admin, skipped].map(&:to_reference).join(' ') }
let(:directly_addressed_and_mentioned) { member.to_reference + ", what do you think? cc: " + [guest, admin, skipped].map(&:to_reference).join(' ') }
@@ -280,7 +280,7 @@ describe TodoService do
end
it 'does not create a todo if unassigned' do
- issue.assignees.destroy_all
+ issue.assignees.destroy_all # rubocop: disable DestroyAll
should_not_create_any_todo { service.reassigned_issue(issue, author) }
end
diff --git a/spec/services/update_deployment_service_spec.rb b/spec/services/update_deployment_service_spec.rb
new file mode 100644
index 00000000000..3c55dd9659a
--- /dev/null
+++ b/spec/services/update_deployment_service_spec.rb
@@ -0,0 +1,217 @@
+require 'spec_helper'
+
+describe UpdateDeploymentService do
+ let(:user) { create(:user) }
+ let(:options) { { name: 'production' } }
+
+ let(:job) do
+ create(:ci_build,
+ ref: 'master',
+ tag: false,
+ environment: 'production',
+ options: { environment: options },
+ project: project)
+ end
+
+ let(:project) { create(:project, :repository) }
+ let(:environment) { deployment.environment }
+ let(:deployment) { job.deployment }
+ let(:service) { described_class.new(deployment) }
+
+ before do
+ job.success! # Create/Succeed deployment
+ end
+
+ describe '#execute' do
+ subject { service.execute }
+
+ let(:store) { Gitlab::EtagCaching::Store.new }
+
+ it 'invalidates the environment etag cache' do
+ old_value = store.get(environment.etag_cache_key)
+
+ subject
+
+ expect(store.get(environment.etag_cache_key)).not_to eq(old_value)
+ end
+
+ it 'creates ref' do
+ expect_any_instance_of(Repository)
+ .to receive(:create_ref)
+ .with(deployment.ref, deployment.send(:ref_path))
+
+ subject
+ end
+
+ it 'updates merge request metrics' do
+ expect_any_instance_of(Deployment)
+ .to receive(:update_merge_request_metrics!)
+
+ subject
+ end
+
+ context 'when start action is defined' do
+ let(:options) { { name: 'production', action: 'start' } }
+
+ context 'and environment is stopped' do
+ before do
+ environment.stop
+ end
+
+ it 'makes environment available' do
+ subject
+
+ expect(environment.reload).to be_available
+ end
+ end
+ end
+
+ context 'when variables are used' do
+ let(:options) do
+ { name: 'review-apps/$CI_COMMIT_REF_NAME',
+ url: 'http://$CI_COMMIT_REF_NAME.review-apps.gitlab.com' }
+ end
+
+ before do
+ environment.update(name: 'review-apps/master')
+ job.update(environment: 'review-apps/$CI_COMMIT_REF_NAME')
+ end
+
+ it 'does not create a new environment' do
+ expect { subject }.not_to change { Environment.count }
+ end
+
+ it 'updates external url' do
+ subject
+
+ expect(subject.environment.name).to eq('review-apps/master')
+ expect(subject.environment.external_url).to eq('http://master.review-apps.gitlab.com')
+ end
+ end
+ end
+
+ describe '#expanded_environment_url' do
+ subject { service.send(:expanded_environment_url) }
+
+ context 'when yaml environment uses $CI_COMMIT_REF_NAME' do
+ let(:job) do
+ create(:ci_build,
+ ref: 'master',
+ environment: 'production',
+ project: project,
+ options: { environment: { name: 'production', url: 'http://review/$CI_COMMIT_REF_NAME' } })
+ end
+
+ it { is_expected.to eq('http://review/master') }
+ end
+
+ context 'when yaml environment uses $CI_ENVIRONMENT_SLUG' do
+ let(:job) do
+ create(:ci_build,
+ ref: 'master',
+ environment: 'prod-slug',
+ project: project,
+ options: { environment: { name: 'prod-slug', url: 'http://review/$CI_ENVIRONMENT_SLUG' } })
+ end
+
+ it { is_expected.to eq('http://review/prod-slug') }
+ end
+
+ context 'when yaml environment uses yaml_variables containing symbol keys' do
+ let(:job) do
+ create(:ci_build,
+ yaml_variables: [{ key: :APP_HOST, value: 'host' }],
+ environment: 'production',
+ project: project,
+ options: { environment: { name: 'production', url: 'http://review/$APP_HOST' } })
+ end
+
+ it { is_expected.to eq('http://review/host') }
+ end
+
+ context 'when yaml environment does not have url' do
+ let(:job) { create(:ci_build, environment: 'staging', project: project) }
+
+ it 'returns the external_url from persisted environment' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe "merge request metrics" do
+ let(:merge_request) { create(:merge_request, target_branch: 'master', source_branch: 'feature', source_project: project) }
+
+ context "while updating the 'first_deployed_to_production_at' time" do
+ before do
+ merge_request.metrics.update!(merged_at: 1.hour.ago)
+ end
+
+ context "for merge requests merged before the current deploy" do
+ it "sets the time if the deploy's environment is 'production'" do
+ service.execute
+
+ expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(deployment.finished_at)
+ end
+
+ context 'when job deploys to staging' do
+ let(:job) do
+ create(:ci_build,
+ ref: 'master',
+ tag: false,
+ environment: 'staging',
+ options: { environment: { name: 'staging' } },
+ project: project)
+ end
+
+ it "doesn't set the time if the deploy's environment is not 'production'" do
+ service.execute
+
+ expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_nil
+ end
+ end
+
+ it 'does not raise errors if the merge request does not have a metrics record' do
+ merge_request.metrics.destroy
+
+ expect(merge_request.reload.metrics).to be_nil
+ expect { service.execute }.not_to raise_error
+ end
+ end
+
+ context "for merge requests merged before the previous deploy" do
+ context "if the 'first_deployed_to_production_at' time is already set" do
+ it "does not overwrite the older 'first_deployed_to_production_at' time" do
+ # Previous deploy
+ service.execute
+
+ expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(deployment.finished_at)
+
+ # Current deploy
+ Timecop.travel(12.hours.from_now) do
+ service.execute
+
+ expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(deployment.finished_at)
+ end
+ end
+ end
+
+ context "if the 'first_deployed_to_production_at' time is not already set" do
+ it "does not overwrite the older 'first_deployed_to_production_at' time" do
+ # Previous deploy
+ time = 5.minutes.from_now
+ Timecop.freeze(time) { service.execute }
+
+ expect(merge_request.reload.metrics.merged_at).to be < merge_request.reload.metrics.first_deployed_to_production_at
+
+ previous_time = merge_request.reload.metrics.first_deployed_to_production_at
+
+ # Current deploy
+ Timecop.freeze(time + 12.hours) { service.execute }
+
+ expect(merge_request.reload.metrics.first_deployed_to_production_at).to eq(previous_time)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/users/build_service_spec.rb b/spec/services/users/build_service_spec.rb
index 677d4a622e1..b7b9817efdb 100644
--- a/spec/services/users/build_service_spec.rb
+++ b/spec/services/users/build_service_spec.rb
@@ -8,11 +8,107 @@ describe Users::BuildService do
context 'with an admin user' do
let(:admin_user) { create(:admin) }
- let(:service) { described_class.new(admin_user, params) }
+ let(:service) { described_class.new(admin_user, ActionController::Parameters.new(params).permit!) }
it 'returns a valid user' do
expect(service.execute).to be_valid
end
+
+ context 'allowed params' do
+ let(:params) do
+ {
+ access_level: 1,
+ admin: 1,
+ avatar: anything,
+ bio: 1,
+ can_create_group: 1,
+ color_scheme_id: 1,
+ email: 1,
+ external: 1,
+ force_random_password: 1,
+ hide_no_password: 1,
+ hide_no_ssh_key: 1,
+ linkedin: 1,
+ name: 1,
+ password: 1,
+ password_automatically_set: 1,
+ password_expires_at: 1,
+ projects_limit: 1,
+ remember_me: 1,
+ skip_confirmation: 1,
+ skype: 1,
+ theme_id: 1,
+ twitter: 1,
+ username: 1,
+ website_url: 1,
+ private_profile: 1,
+ organization: 1,
+ location: 1,
+ public_email: 1
+ }
+ end
+
+ it 'sets all allowed attributes' do
+ admin_user # call first so the admin gets created before setting `expect`
+
+ expect(User).to receive(:new).with(hash_including(params)).and_call_original
+
+ service.execute
+ end
+ end
+
+ context 'with "user_default_external" application setting' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_default_external, :external, :email, :user_default_internal_regex, :result) do
+ true | nil | 'fl@example.com' | nil | true
+ true | true | 'fl@example.com' | nil | true
+ true | false | 'fl@example.com' | nil | false
+
+ true | nil | 'fl@example.com' | '' | true
+ true | true | 'fl@example.com' | '' | true
+ true | false | 'fl@example.com' | '' | false
+
+ true | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ true | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ true | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+
+ true | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ true | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ true | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false
+
+ false | nil | 'fl@example.com' | nil | false
+ false | true | 'fl@example.com' | nil | true
+ false | false | 'fl@example.com' | nil | false
+
+ false | nil | 'fl@example.com' | '' | false
+ false | true | 'fl@example.com' | '' | true
+ false | false | 'fl@example.com' | '' | false
+
+ false | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ false | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ false | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+
+ false | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ false | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ false | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ end
+
+ with_them do
+ before do
+ stub_application_setting(user_default_external: user_default_external)
+ stub_application_setting(user_default_internal_regex: user_default_internal_regex)
+
+ params.merge!({ external: external, email: email }.compact)
+ end
+
+ subject(:user) { service.execute }
+
+ it 'correctly sets user.external' do
+ expect(user.external).to eq(result)
+ end
+ end
+ end
end
context 'with non admin user' do
@@ -50,6 +146,59 @@ describe Users::BuildService do
expect(service.execute).to be_confirmed
end
end
+
+ context 'with "user_default_external" application setting' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_default_external, :external, :email, :user_default_internal_regex, :result) do
+ true | nil | 'fl@example.com' | nil | true
+ true | true | 'fl@example.com' | nil | true
+ true | false | 'fl@example.com' | nil | true
+
+ true | nil | 'fl@example.com' | '' | true
+ true | true | 'fl@example.com' | '' | true
+ true | false | 'fl@example.com' | '' | true
+
+ true | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ true | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ true | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+
+ true | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ true | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true
+ true | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | true
+
+ false | nil | 'fl@example.com' | nil | false
+ false | true | 'fl@example.com' | nil | false
+ false | false | 'fl@example.com' | nil | false
+
+ false | nil | 'fl@example.com' | '' | false
+ false | true | 'fl@example.com' | '' | false
+ false | false | 'fl@example.com' | '' | false
+
+ false | nil | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ false | true | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ false | false | 'fl@example.com' | '^(?:(?!\.ext@).)*$\r?' | false
+
+ false | nil | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ false | true | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ false | false | 'tester.ext@domain.com' | '^(?:(?!\.ext@).)*$\r?' | false
+ end
+
+ with_them do
+ before do
+ stub_application_setting(user_default_external: user_default_external)
+ stub_application_setting(user_default_internal_regex: user_default_internal_regex)
+
+ params.merge!({ external: external, email: email }.compact)
+ end
+
+ subject(:user) { service.execute }
+
+ it 'sets the value of Gitlab::CurrentSettings.user_default_external' do
+ expect(user.external).to eq(result)
+ end
+ end
+ end
end
end
end
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index 3bae8bfbd42..83f1495a1c6 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -20,7 +20,7 @@ describe Users::DestroyService do
it 'will delete the project' do
expect_next_instance_of(Projects::DestroyService) do |destroy_service|
- expect(destroy_service).to receive(:execute).once
+ expect(destroy_service).to receive(:execute).once.and_return(true)
end
service.execute(user)
@@ -35,7 +35,7 @@ describe Users::DestroyService do
it 'destroys a project in pending_delete' do
expect_next_instance_of(Projects::DestroyService) do |destroy_service|
- expect(destroy_service).to receive(:execute).once
+ expect(destroy_service).to receive(:execute).once.and_return(true)
end
service.execute(user)
@@ -172,23 +172,36 @@ describe Users::DestroyService do
end
describe "user personal's repository removal" do
- before do
- perform_enqueued_jobs { service.execute(user) }
- end
+ context 'storages' do
+ before do
+ perform_enqueued_jobs { service.execute(user) }
+ end
+
+ context 'legacy storage' do
+ let!(:project) { create(:project, :empty_repo, :legacy_storage, namespace: user.namespace) }
+
+ it 'removes repository' do
+ expect(gitlab_shell.exists?(project.repository_storage, "#{project.disk_path}.git")).to be_falsey
+ end
+ end
- context 'legacy storage' do
- let!(:project) { create(:project, :empty_repo, :legacy_storage, namespace: user.namespace) }
+ context 'hashed storage' do
+ let!(:project) { create(:project, :empty_repo, namespace: user.namespace) }
- it 'removes repository' do
- expect(gitlab_shell.exists?(project.repository_storage, "#{project.disk_path}.git")).to be_falsey
+ it 'removes repository' do
+ expect(gitlab_shell.exists?(project.repository_storage, "#{project.disk_path}.git")).to be_falsey
+ end
end
end
- context 'hashed storage' do
- let!(:project) { create(:project, :empty_repo, namespace: user.namespace) }
+ context 'repository removal status is taken into account' do
+ it 'raises exception' do
+ expect_next_instance_of(::Projects::DestroyService) do |destroy_service|
+ expect(destroy_service).to receive(:execute).and_return(false)
+ end
- it 'removes repository' do
- expect(gitlab_shell.exists?(project.repository_storage, "#{project.disk_path}.git")).to be_falsey
+ expect { service.execute(user) }
+ .to raise_error(Users::DestroyService::DestroyError, "Project #{project.id} can't be deleted" )
end
end
end
diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb
index 622e56e1da5..b1cc6d2eb83 100644
--- a/spec/services/web_hook_service_spec.rb
+++ b/spec/services/web_hook_service_spec.rb
@@ -97,7 +97,7 @@ describe WebHookService do
end
it 'handles exceptions' do
- exceptions = [SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError]
+ exceptions = [SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError, Gitlab::HTTP::RedirectionTooDeep]
exceptions.each do |exception_class|
exception = exception_class.new('Exception message')
diff --git a/spec/services/wikis/create_attachment_service_spec.rb b/spec/services/wikis/create_attachment_service_spec.rb
new file mode 100644
index 00000000000..f5899f292c8
--- /dev/null
+++ b/spec/services/wikis/create_attachment_service_spec.rb
@@ -0,0 +1,224 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Wikis::CreateAttachmentService do
+ let(:project) { create(:project, :wiki_repo) }
+ let(:user) { create(:user) }
+ let(:file_name) { 'filename.txt' }
+ let(:file_path_regex) { %r{#{described_class::ATTACHMENT_PATH}/\h{32}/#{file_name}} }
+
+ let(:file_opts) do
+ {
+ file_name: file_name,
+ file_content: 'Content of attachment'
+ }
+ end
+ let(:opts) { file_opts }
+
+ subject(:service) { described_class.new(project, user, opts) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ describe 'initialization' do
+ context 'author commit info' do
+ it 'does not raise error if user is nil' do
+ service = described_class.new(project, nil, opts)
+
+ expect(service.instance_variable_get(:@author_email)).to be_nil
+ expect(service.instance_variable_get(:@author_name)).to be_nil
+ end
+
+ it 'fills file_path from the repository uploads folder' do
+ expect(service.instance_variable_get(:@file_path)).to match(file_path_regex)
+ end
+
+ context 'when no author info provided' do
+ it 'fills author_email and author_name from current_user info' do
+ expect(service.instance_variable_get(:@author_email)).to eq user.email
+ expect(service.instance_variable_get(:@author_name)).to eq user.name
+ end
+ end
+
+ context 'when author info provided' do
+ let(:author_email) { 'author_email' }
+ let(:author_name) { 'author_name' }
+ let(:opts) { file_opts.merge(author_email: author_email, author_name: author_name) }
+
+ it 'fills author_email and author_name from params' do
+ expect(service.instance_variable_get(:@author_email)).to eq author_email
+ expect(service.instance_variable_get(:@author_name)).to eq author_name
+ end
+ end
+ end
+
+ context 'commit message' do
+ context 'when no commit message provided' do
+ it 'sets a default commit message' do
+ expect(service.instance_variable_get(:@commit_message)).to eq "Upload attachment #{opts[:file_name]}"
+ end
+ end
+
+ context 'when commit message provided' do
+ let(:commit_message) { 'whatever' }
+ let(:opts) { file_opts.merge(commit_message: commit_message) }
+
+ it 'use the commit message from params' do
+ expect(service.instance_variable_get(:@commit_message)).to eq commit_message
+ end
+ end
+ end
+
+ context 'branch name' do
+ context 'when no branch provided' do
+ it 'sets the branch from the wiki default_branch' do
+ expect(service.instance_variable_get(:@branch_name)).to eq project.wiki.default_branch
+ end
+ end
+
+ context 'when branch provided' do
+ let(:branch_name) { 'whatever' }
+ let(:opts) { file_opts.merge(branch_name: branch_name) }
+
+ it 'use the commit message from params' do
+ expect(service.instance_variable_get(:@branch_name)).to eq branch_name
+ end
+ end
+ end
+ end
+
+ describe '#parse_file_name' do
+ context 'when file_name' do
+ context 'has white spaces' do
+ let(:file_name) { 'file with spaces' }
+
+ it "replaces all of them with '_'" do
+ result = service.execute
+
+ expect(result[:status]).to eq :success
+ expect(result[:result][:file_name]).to eq 'file_with_spaces'
+ end
+ end
+
+ context 'has other invalid characters' do
+ let(:file_name) { "file\twith\tinvalid chars" }
+
+ it "replaces all of them with '_'" do
+ result = service.execute
+
+ expect(result[:status]).to eq :success
+ expect(result[:result][:file_name]).to eq 'file_with_invalid_chars'
+ end
+ end
+
+ context 'is not present' do
+ let(:file_name) { nil }
+
+ it 'returns error' do
+ result = service.execute
+
+ expect(result[:status]).to eq :error
+ expect(result[:message]).to eq 'The file name cannot be empty'
+ end
+ end
+
+ context 'length' do
+ context 'is bigger than 255' do
+ let(:file_name) { "#{'0' * 256}.jpg" }
+
+ it 'truncates file name' do
+ result = service.execute
+
+ expect(result[:status]).to eq :success
+ expect(result[:result][:file_name].length).to eq 255
+ expect(result[:result][:file_name]).to match(/0{251}\.jpg/)
+ end
+ end
+
+ context 'is less or equal to 255 does not return error' do
+ let(:file_name) { '0' * 255 }
+
+ it 'does not return error' do
+ result = service.execute
+
+ expect(result[:status]).to eq :success
+ end
+ end
+ end
+ end
+
+ context 'when user' do
+ shared_examples 'wiki attachment user validations' do
+ it 'returns error' do
+ result = described_class.new(project, user2, opts).execute
+
+ expect(result[:status]).to eq :error
+ expect(result[:message]).to eq 'You are not allowed to push to the wiki'
+ end
+ end
+
+ context 'does not have permission' do
+ let(:user2) { create(:user) }
+
+ it_behaves_like 'wiki attachment user validations'
+ end
+
+ context 'is nil' do
+ let(:user2) { nil }
+
+ it_behaves_like 'wiki attachment user validations'
+ end
+ end
+ end
+
+ describe '#execute' do
+ let(:wiki) { project.wiki }
+ subject(:service_execute) { service.execute[:result] }
+
+ context 'creates branch if it does not exists' do
+ let(:branch_name) { 'new_branch' }
+ let(:opts) { file_opts.merge(branch_name: branch_name) }
+
+ it do
+ expect(wiki.repository.branches).to be_empty
+ expect { service.execute }.to change { wiki.repository.branches.count }.by(1)
+ expect(wiki.repository.branches.first.name).to eq branch_name
+ end
+ end
+
+ it 'adds file to the repository' do
+ expect(wiki.repository.ls_files('HEAD')).to be_empty
+
+ service.execute
+
+ files = wiki.repository.ls_files('HEAD')
+ expect(files.count).to eq 1
+ expect(files.first).to match(file_path_regex)
+ end
+
+ context 'returns' do
+ before do
+ allow(SecureRandom).to receive(:hex).and_return('fixed_hex')
+
+ service_execute
+ end
+
+ it 'returns the file name' do
+ expect(service_execute[:file_name]).to eq file_name
+ end
+
+ it 'returns the path where file was stored' do
+ expect(service_execute[:file_path]).to eq 'uploads/fixed_hex/filename.txt'
+ end
+
+ it 'returns the branch where the file was pushed' do
+ expect(service_execute[:branch]).to eq wiki.default_branch
+ end
+
+ it 'returns the commit id' do
+ expect(service_execute[:commit]).not_to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index bd564cc60a6..3fedb9ed48c 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -29,10 +29,16 @@ end
# require rainbow gem String monkeypatch, so we can test SystemChecks
require 'rainbow/ext/string'
+Rainbow.enabled = false
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
# Requires helpers, and shared contexts/examples first since they're used in other support files
+
+# Load these first since they may be required by other helpers
+require Rails.root.join("spec/support/helpers/git_helpers.rb")
+
+# Then the rest
Dir[Rails.root.join("spec/support/helpers/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f }
@@ -41,6 +47,7 @@ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false
+ config.fixture_path = Rails.root
config.verbose_retry = true
config.display_try_failure_messages = true
@@ -111,6 +118,13 @@ RSpec.configure do |config|
config.before(:example) do
# Enable all features by default for testing
allow(Feature).to receive(:enabled?) { true }
+
+ # The following can be removed when we remove the staged rollout strategy
+ # and we can just enable it using instance wide settings
+ # (ie. ApplicationSetting#auto_devops_enabled)
+ allow(Feature).to receive(:enabled?)
+ .with(:force_autodevops_on_by_default, anything)
+ .and_return(false)
end
config.before(:example, :request_store) do
@@ -126,6 +140,10 @@ RSpec.configure do |config|
Fog.unmock! if Fog.mock?
end
+ config.after(:example) do
+ Gitlab::CurrentSettings.clear_in_memory_application_settings!
+ end
+
config.before(:example, :mailer) do
reset_delivered_emails!
end
@@ -217,6 +235,10 @@ RSpec.configure do |config|
example.run if Gitlab::Database.mysql?
end
+ config.around(:each, :rails5) do |example|
+ example.run if Gitlab.rails5?
+ end
+
# This makes sure the `ApplicationController#can?` method is stubbed with the
# original implementation for all view specs.
config.before(:each, type: :view) do
diff --git a/spec/support/api/milestones_shared_examples.rb b/spec/support/api/milestones_shared_examples.rb
index a15189db35f..3bebb7aae90 100644
--- a/spec/support/api/milestones_shared_examples.rb
+++ b/spec/support/api/milestones_shared_examples.rb
@@ -102,14 +102,6 @@ shared_examples_for 'group and project milestones' do |route_definition|
expect(json_response['iid']).to eq(milestone.iid)
end
- it 'returns a milestone by id' do
- get api(resource_route, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['title']).to eq(milestone.title)
- expect(json_response['iid']).to eq(milestone.iid)
- end
-
it 'returns 401 error if user not authenticated' do
get api(resource_route)
@@ -204,6 +196,24 @@ shared_examples_for 'group and project milestones' do |route_definition|
end
end
+ describe "DELETE #{route_definition}/:milestone_id" do
+ it "rejects a member with reporter access from deleting a milestone" do
+ reporter = create(:user)
+ milestone.parent.add_reporter(reporter)
+
+ delete api(resource_route, reporter)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ it 'deletes the milestone when the user has developer access to the project' do
+ delete api(resource_route, user)
+
+ expect(project.milestones.find_by_id(milestone.id)).to be_nil
+ expect(response).to have_gitlab_http_status(204)
+ end
+ end
+
describe "GET #{route_definition}/:milestone_id/issues" do
let(:issues_route) { "#{route}/#{milestone.id}/issues" }
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index c0ceb0f6605..18a7a392c12 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -100,7 +100,7 @@ RSpec.configure do |config|
# capybara/rspec already calls Capybara.reset_sessions! in an `after` hook,
# but `block_and_wait_for_requests_complete` is called before it so by
- # calling it explicitely here, we prevent any new requests from being fired
+ # calling it explicitly here, we prevent any new requests from being fired
# See https://github.com/teamcapybara/capybara/blob/ffb41cfad620de1961bb49b1562a9fa9b28c0903/lib/capybara/rspec.rb#L20-L25
# We don't reset the session when the example failed, because we need capybara-screenshot to have access to it.
Capybara.reset_sessions! unless example.exception
diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb
index 1c1b68c12a2..140490f2dc2 100644
--- a/spec/support/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb
@@ -22,6 +22,18 @@ shared_examples 'a GitHub-ish import controller: POST personal_access_token' do
expect(session[:"#{provider}_access_token"]).to eq(token)
expect(controller).to redirect_to(status_import_url)
end
+
+ it "strips access token with spaces" do
+ token = 'asdfasdf9876'
+
+ allow_any_instance_of(Gitlab::LegacyGithubImport::Client)
+ .to receive(:user).and_return(true)
+
+ post :personal_access_token, personal_access_token: " #{token} "
+
+ expect(session[:"#{provider}_access_token"]).to eq(token)
+ expect(controller).to redirect_to(status_import_url)
+ end
end
shared_examples 'a GitHub-ish import controller: GET new' do
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index 80604395adf..18cf08f0b9e 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -150,17 +150,25 @@ shared_examples 'discussion comments' do |resource_name|
end
if resource_name == 'merge request'
- let(:note_id) { find("#{comments_selector} .note", match: :first)['data-note-id'] }
+ let(:note_id) { find("#{comments_selector} .note:first-child", match: :first)['data-note-id'] }
+ let(:reply_id) { find("#{comments_selector} .note:last-child", match: :first)['data-note-id'] }
it 'shows resolved discussion when toggled' do
+ find("#{comments_selector} .js-vue-discussion-reply").click
+ find("#{comments_selector} .note-textarea").send_keys('a')
+
+ click_button "Comment"
+ wait_for_requests
+
click_button "Resolve discussion"
+ wait_for_requests
expect(page).to have_selector(".note-row-#{note_id}", visible: true)
refresh
- click_button "Toggle discussion"
+ click_button "1 reply"
- expect(page).to have_selector(".note-row-#{note_id}", visible: true)
+ expect(page).to have_selector(".note-row-#{reply_id}", visible: true)
end
end
end
diff --git a/spec/support/features/issuable_quick_actions_shared_examples.rb b/spec/support/features/issuable_quick_actions_shared_examples.rb
new file mode 100644
index 00000000000..2a883ce1074
--- /dev/null
+++ b/spec/support/features/issuable_quick_actions_shared_examples.rb
@@ -0,0 +1,389 @@
+# Specifications for behavior common to all objects with executable attributes.
+# It takes a `issuable_type`, and expect an `issuable`.
+
+shared_examples 'issuable record that supports quick actions in its description and notes' do |issuable_type|
+ include Spec::Support::Helpers::Features::NotesHelpers
+
+ let(:maintainer) { create(:user) }
+ let(:project) do
+ case issuable_type
+ when :merge_request
+ create(:project, :public, :repository)
+ when :issue
+ create(:project, :public)
+ end
+ end
+ let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
+ let!(:label_bug) { create(:label, project: project, title: 'bug') }
+ let!(:label_feature) { create(:label, project: project, title: 'feature') }
+ let(:new_url_opts) { {} }
+
+ before do
+ project.add_maintainer(maintainer)
+
+ gitlab_sign_in(maintainer)
+ end
+
+ after do
+ # Ensure all outstanding Ajax requests are complete to avoid database deadlocks
+ wait_for_requests
+ end
+
+ describe "new #{issuable_type}", :js do
+ context 'with commands in the description' do
+ it "creates the #{issuable_type} and interpret commands accordingly" do
+ case issuable_type
+ when :merge_request
+ visit public_send("namespace_project_new_merge_request_path", project.namespace, project, new_url_opts)
+ when :issue
+ visit public_send("new_namespace_project_issue_path", project.namespace, project, new_url_opts)
+ end
+ fill_in "#{issuable_type}_title", with: 'bug 345'
+ fill_in "#{issuable_type}_description", with: "bug description\n/label ~bug\n/milestone %\"ASAP\""
+ click_button "Submit #{issuable_type}".humanize
+
+ issuable = project.public_send(issuable_type.to_s.pluralize).first
+
+ expect(issuable.description).to eq "bug description"
+ expect(issuable.labels).to eq [label_bug]
+ expect(issuable.milestone).to eq milestone
+ expect(page).to have_content 'bug 345'
+ expect(page).to have_content 'bug description'
+ end
+ end
+ end
+
+ describe "note on #{issuable_type}", :js do
+ before do
+ visit public_send("project_#{issuable_type}_path", project, issuable)
+ end
+
+ context 'with a note containing commands' do
+ it 'creates a note without the commands and interpret the commands accordingly' do
+ assignee = create(:user, username: 'bob')
+ add_note("Awesome!\n\n/assign @bob\n\n/label ~bug\n\n/milestone %\"ASAP\"")
+
+ expect(page).to have_content 'Awesome!'
+ expect(page).not_to have_content '/assign @bob'
+ expect(page).not_to have_content '/label ~bug'
+ expect(page).not_to have_content '/milestone %"ASAP"'
+
+ wait_for_requests
+ issuable.reload
+ note = issuable.notes.user.first
+
+ expect(note.note).to eq "Awesome!"
+ expect(issuable.assignees).to eq [assignee]
+ expect(issuable.labels).to eq [label_bug]
+ expect(issuable.milestone).to eq milestone
+ end
+
+ it 'removes the quick action from note and explains it in the preview' do
+ preview_note("Awesome!\n\n/close")
+
+ expect(page).to have_content 'Awesome!'
+ expect(page).not_to have_content '/close'
+ issuable_name = issuable.is_a?(Issue) ? 'issue' : 'merge request'
+ expect(page).to have_content "Closes this #{issuable_name}."
+ end
+ end
+
+ context 'with a note containing only commands' do
+ it 'does not create a note but interpret the commands accordingly' do
+ assignee = create(:user, username: 'bob')
+ add_note("/assign @bob\n\n/label ~bug\n\n/milestone %\"ASAP\"")
+
+ expect(page).not_to have_content '/assign @bob'
+ expect(page).not_to have_content '/label ~bug'
+ expect(page).not_to have_content '/milestone %"ASAP"'
+ expect(page).to have_content 'Commands applied'
+
+ issuable.reload
+
+ expect(issuable.notes.user).to be_empty
+ expect(issuable.assignees).to eq [assignee]
+ expect(issuable.labels).to eq [label_bug]
+ expect(issuable.milestone).to eq milestone
+ end
+ end
+
+ context "with a note closing the #{issuable_type}" do
+ before do
+ expect(issuable).to be_open
+ end
+
+ context "when current user can close #{issuable_type}" do
+ it "closes the #{issuable_type}" do
+ add_note("/close")
+
+ expect(page).not_to have_content '/close'
+ expect(page).to have_content 'Commands applied'
+
+ expect(issuable.reload).to be_closed
+ end
+ end
+
+ context "when current user cannot close #{issuable_type}" do
+ before do
+ guest = create(:user)
+ project.add_guest(guest)
+
+ gitlab_sign_out
+ gitlab_sign_in(guest)
+ visit public_send("project_#{issuable_type}_path", project, issuable)
+ end
+
+ it "does not close the #{issuable_type}" do
+ add_note("/close")
+
+ expect(page).not_to have_content 'Commands applied'
+
+ expect(issuable).to be_open
+ end
+ end
+ end
+
+ context "with a note reopening the #{issuable_type}" do
+ before do
+ issuable.close
+ expect(issuable).to be_closed
+ end
+
+ context "when current user can reopen #{issuable_type}" do
+ it "reopens the #{issuable_type}" do
+ add_note("/reopen")
+
+ expect(page).not_to have_content '/reopen'
+ expect(page).to have_content 'Commands applied'
+
+ expect(issuable.reload).to be_open
+ end
+ end
+
+ context "when current user cannot reopen #{issuable_type}" do
+ before do
+ guest = create(:user)
+ project.add_guest(guest)
+
+ gitlab_sign_out
+ gitlab_sign_in(guest)
+ visit public_send("project_#{issuable_type}_path", project, issuable)
+ end
+
+ it "does not reopen the #{issuable_type}" do
+ add_note("/reopen")
+
+ expect(page).not_to have_content 'Commands applied'
+
+ expect(issuable).to be_closed
+ end
+ end
+ end
+
+ context "with a note changing the #{issuable_type}'s title" do
+ context "when current user can change title of #{issuable_type}" do
+ it "reopens the #{issuable_type}" do
+ add_note("/title Awesome new title")
+
+ expect(page).not_to have_content '/title'
+ expect(page).to have_content 'Commands applied'
+
+ expect(issuable.reload.title).to eq 'Awesome new title'
+ end
+ end
+
+ context "when current user cannot change title of #{issuable_type}" do
+ before do
+ guest = create(:user)
+ project.add_guest(guest)
+
+ gitlab_sign_out
+ gitlab_sign_in(guest)
+ visit public_send("project_#{issuable_type}_path", project, issuable)
+ end
+
+ it "does not change the #{issuable_type} title" do
+ add_note("/title Awesome new title")
+
+ expect(page).not_to have_content 'Commands applied'
+
+ expect(issuable.reload.title).not_to eq 'Awesome new title'
+ end
+ end
+ end
+
+ context "with a note marking the #{issuable_type} as todo" do
+ it "creates a new todo for the #{issuable_type}" do
+ add_note("/todo")
+
+ expect(page).not_to have_content '/todo'
+ expect(page).to have_content 'Commands applied'
+
+ todos = TodosFinder.new(maintainer).execute
+ todo = todos.first
+
+ expect(todos.size).to eq 1
+ expect(todo).to be_pending
+ expect(todo.target).to eq issuable
+ expect(todo.author).to eq maintainer
+ expect(todo.user).to eq maintainer
+ end
+ end
+
+ context "with a note marking the #{issuable_type} as done" do
+ before do
+ TodoService.new.mark_todo(issuable, maintainer)
+ end
+
+ it "creates a new todo for the #{issuable_type}" do
+ todos = TodosFinder.new(maintainer).execute
+ todo = todos.first
+
+ expect(todos.size).to eq 1
+ expect(todos.first).to be_pending
+ expect(todo.target).to eq issuable
+ expect(todo.author).to eq maintainer
+ expect(todo.user).to eq maintainer
+
+ add_note("/done")
+
+ expect(page).not_to have_content '/done'
+ expect(page).to have_content 'Commands applied'
+
+ expect(todo.reload).to be_done
+ end
+ end
+
+ context "with a note subscribing to the #{issuable_type}" do
+ it "creates a new todo for the #{issuable_type}" do
+ expect(issuable.subscribed?(maintainer, project)).to be_falsy
+
+ add_note("/subscribe")
+
+ expect(page).not_to have_content '/subscribe'
+ expect(page).to have_content 'Commands applied'
+
+ expect(issuable.subscribed?(maintainer, project)).to be_truthy
+ end
+ end
+
+ context "with a note unsubscribing to the #{issuable_type} as done" do
+ before do
+ issuable.subscribe(maintainer, project)
+ end
+
+ it "creates a new todo for the #{issuable_type}" do
+ expect(issuable.subscribed?(maintainer, project)).to be_truthy
+
+ add_note("/unsubscribe")
+
+ expect(page).not_to have_content '/unsubscribe'
+ expect(page).to have_content 'Commands applied'
+
+ expect(issuable.subscribed?(maintainer, project)).to be_falsy
+ end
+ end
+
+ context "with a note assigning the #{issuable_type} to the current user" do
+ it "assigns the #{issuable_type} to the current user" do
+ add_note("/assign me")
+
+ expect(page).not_to have_content '/assign me'
+ expect(page).to have_content 'Commands applied'
+
+ expect(issuable.reload.assignees).to eq [maintainer]
+ end
+ end
+
+ context "with a note locking the #{issuable_type} discussion" do
+ before do
+ issuable.update(discussion_locked: false)
+ expect(issuable).not_to be_discussion_locked
+ end
+
+ context "when current user can lock #{issuable_type} discussion" do
+ it "locks the #{issuable_type} discussion" do
+ add_note("/lock")
+
+ expect(page).not_to have_content '/lock'
+ expect(page).to have_content 'Commands applied'
+
+ expect(issuable.reload).to be_discussion_locked
+ end
+ end
+
+ context "when current user cannot lock #{issuable_type}" do
+ before do
+ guest = create(:user)
+ project.add_guest(guest)
+
+ gitlab_sign_out
+ sign_in(guest)
+ visit public_send("project_#{issuable_type}_path", project, issuable)
+ end
+
+ it "does not lock the #{issuable_type} discussion" do
+ add_note("/lock")
+
+ expect(page).not_to have_content 'Commands applied'
+
+ expect(issuable).not_to be_discussion_locked
+ end
+ end
+ end
+
+ context "with a note unlocking the #{issuable_type} discussion" do
+ before do
+ issuable.update(discussion_locked: true)
+ expect(issuable).to be_discussion_locked
+ end
+
+ context "when current user can unlock #{issuable_type} discussion" do
+ it "unlocks the #{issuable_type} discussion" do
+ add_note("/unlock")
+
+ expect(page).not_to have_content '/unlock'
+ expect(page).to have_content 'Commands applied'
+
+ expect(issuable.reload).not_to be_discussion_locked
+ end
+ end
+
+ context "when current user cannot unlock #{issuable_type}" do
+ before do
+ guest = create(:user)
+ project.add_guest(guest)
+
+ gitlab_sign_out
+ sign_in(guest)
+ visit public_send("project_#{issuable_type}_path", project, issuable)
+ end
+
+ it "does not unlock the #{issuable_type} discussion" do
+ add_note("/unlock")
+
+ expect(page).not_to have_content 'Commands applied'
+
+ expect(issuable).to be_discussion_locked
+ end
+ end
+ end
+ end
+
+ describe "preview of note on #{issuable_type}", :js do
+ it 'removes quick actions from note and explains them' do
+ create(:user, username: 'bob')
+
+ visit public_send("project_#{issuable_type}_path", project, issuable)
+
+ page.within('.js-main-target-form') do
+ fill_in 'note[note]', with: "Awesome!\n/assign @bob "
+ click_on 'Preview'
+
+ expect(page).to have_content 'Awesome!'
+ expect(page).not_to have_content '/assign @bob'
+ expect(page).to have_content 'Assigns @bob.'
+ end
+ end
+ end
+end
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
deleted file mode 100644
index 9b44c532ff6..00000000000
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ /dev/null
@@ -1,306 +0,0 @@
-# Specifications for behavior common to all objects with executable attributes.
-# It takes a `issuable_type`, and expect an `issuable`.
-
-shared_examples 'issuable record that supports quick actions in its description and notes' do |issuable_type|
- include Spec::Support::Helpers::Features::NotesHelpers
-
- let(:maintainer) { create(:user) }
- let(:project) do
- case issuable_type
- when :merge_request
- create(:project, :public, :repository)
- when :issue
- create(:project, :public)
- end
- end
- let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
- let!(:label_bug) { create(:label, project: project, title: 'bug') }
- let!(:label_feature) { create(:label, project: project, title: 'feature') }
- let(:new_url_opts) { {} }
-
- before do
- project.add_maintainer(maintainer)
-
- gitlab_sign_in(maintainer)
- end
-
- after do
- # Ensure all outstanding Ajax requests are complete to avoid database deadlocks
- wait_for_requests
- end
-
- describe "new #{issuable_type}", :js do
- context 'with commands in the description' do
- it "creates the #{issuable_type} and interpret commands accordingly" do
- case issuable_type
- when :merge_request
- visit public_send("namespace_project_new_merge_request_path", project.namespace, project, new_url_opts)
- when :issue
- visit public_send("new_namespace_project_issue_path", project.namespace, project, new_url_opts)
- end
- fill_in "#{issuable_type}_title", with: 'bug 345'
- fill_in "#{issuable_type}_description", with: "bug description\n/label ~bug\n/milestone %\"ASAP\""
- click_button "Submit #{issuable_type}".humanize
-
- issuable = project.public_send(issuable_type.to_s.pluralize).first
-
- expect(issuable.description).to eq "bug description"
- expect(issuable.labels).to eq [label_bug]
- expect(issuable.milestone).to eq milestone
- expect(page).to have_content 'bug 345'
- expect(page).to have_content 'bug description'
- end
- end
- end
-
- describe "note on #{issuable_type}", :js do
- before do
- visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
- end
-
- context 'with a note containing commands' do
- it 'creates a note without the commands and interpret the commands accordingly' do
- assignee = create(:user, username: 'bob')
- add_note("Awesome!\n\n/assign @bob\n\n/label ~bug\n\n/milestone %\"ASAP\"")
-
- expect(page).to have_content 'Awesome!'
- expect(page).not_to have_content '/assign @bob'
- expect(page).not_to have_content '/label ~bug'
- expect(page).not_to have_content '/milestone %"ASAP"'
-
- wait_for_requests
- issuable.reload
- note = issuable.notes.user.first
-
- expect(note.note).to eq "Awesome!"
- expect(issuable.assignees).to eq [assignee]
- expect(issuable.labels).to eq [label_bug]
- expect(issuable.milestone).to eq milestone
- end
- end
-
- context 'with a note containing only commands' do
- it 'does not create a note but interpret the commands accordingly' do
- assignee = create(:user, username: 'bob')
- add_note("/assign @bob\n\n/label ~bug\n\n/milestone %\"ASAP\"")
-
- expect(page).not_to have_content '/assign @bob'
- expect(page).not_to have_content '/label ~bug'
- expect(page).not_to have_content '/milestone %"ASAP"'
- expect(page).to have_content 'Commands applied'
-
- issuable.reload
-
- expect(issuable.notes.user).to be_empty
- expect(issuable.assignees).to eq [assignee]
- expect(issuable.labels).to eq [label_bug]
- expect(issuable.milestone).to eq milestone
- end
- end
-
- context "with a note closing the #{issuable_type}" do
- before do
- expect(issuable).to be_open
- end
-
- context "when current user can close #{issuable_type}" do
- it "closes the #{issuable_type}" do
- add_note("/close")
-
- expect(page).not_to have_content '/close'
- expect(page).to have_content 'Commands applied'
-
- expect(issuable.reload).to be_closed
- end
- end
-
- context "when current user cannot close #{issuable_type}" do
- before do
- guest = create(:user)
- project.add_guest(guest)
-
- gitlab_sign_out
- gitlab_sign_in(guest)
- visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
- end
-
- it "does not close the #{issuable_type}" do
- add_note("/close")
-
- expect(page).not_to have_content 'Commands applied'
-
- expect(issuable).to be_open
- end
- end
- end
-
- context "with a note reopening the #{issuable_type}" do
- before do
- issuable.close
- expect(issuable).to be_closed
- end
-
- context "when current user can reopen #{issuable_type}" do
- it "reopens the #{issuable_type}" do
- add_note("/reopen")
-
- expect(page).not_to have_content '/reopen'
- expect(page).to have_content 'Commands applied'
-
- expect(issuable.reload).to be_open
- end
- end
-
- context "when current user cannot reopen #{issuable_type}" do
- before do
- guest = create(:user)
- project.add_guest(guest)
-
- gitlab_sign_out
- gitlab_sign_in(guest)
- visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
- end
-
- it "does not reopen the #{issuable_type}" do
- add_note("/reopen")
-
- expect(page).not_to have_content 'Commands applied'
-
- expect(issuable).to be_closed
- end
- end
- end
-
- context "with a note changing the #{issuable_type}'s title" do
- context "when current user can change title of #{issuable_type}" do
- it "reopens the #{issuable_type}" do
- add_note("/title Awesome new title")
-
- expect(page).not_to have_content '/title'
- expect(page).to have_content 'Commands applied'
-
- expect(issuable.reload.title).to eq 'Awesome new title'
- end
- end
-
- context "when current user cannot change title of #{issuable_type}" do
- before do
- guest = create(:user)
- project.add_guest(guest)
-
- gitlab_sign_out
- gitlab_sign_in(guest)
- visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
- end
-
- it "does not change the #{issuable_type} title" do
- add_note("/title Awesome new title")
-
- expect(page).not_to have_content 'Commands applied'
-
- expect(issuable.reload.title).not_to eq 'Awesome new title'
- end
- end
- end
-
- context "with a note marking the #{issuable_type} as todo" do
- it "creates a new todo for the #{issuable_type}" do
- add_note("/todo")
-
- expect(page).not_to have_content '/todo'
- expect(page).to have_content 'Commands applied'
-
- todos = TodosFinder.new(maintainer).execute
- todo = todos.first
-
- expect(todos.size).to eq 1
- expect(todo).to be_pending
- expect(todo.target).to eq issuable
- expect(todo.author).to eq maintainer
- expect(todo.user).to eq maintainer
- end
- end
-
- context "with a note marking the #{issuable_type} as done" do
- before do
- TodoService.new.mark_todo(issuable, maintainer)
- end
-
- it "creates a new todo for the #{issuable_type}" do
- todos = TodosFinder.new(maintainer).execute
- todo = todos.first
-
- expect(todos.size).to eq 1
- expect(todos.first).to be_pending
- expect(todo.target).to eq issuable
- expect(todo.author).to eq maintainer
- expect(todo.user).to eq maintainer
-
- add_note("/done")
-
- expect(page).not_to have_content '/done'
- expect(page).to have_content 'Commands applied'
-
- expect(todo.reload).to be_done
- end
- end
-
- context "with a note subscribing to the #{issuable_type}" do
- it "creates a new todo for the #{issuable_type}" do
- expect(issuable.subscribed?(maintainer, project)).to be_falsy
-
- add_note("/subscribe")
-
- expect(page).not_to have_content '/subscribe'
- expect(page).to have_content 'Commands applied'
-
- expect(issuable.subscribed?(maintainer, project)).to be_truthy
- end
- end
-
- context "with a note unsubscribing to the #{issuable_type} as done" do
- before do
- issuable.subscribe(maintainer, project)
- end
-
- it "creates a new todo for the #{issuable_type}" do
- expect(issuable.subscribed?(maintainer, project)).to be_truthy
-
- add_note("/unsubscribe")
-
- expect(page).not_to have_content '/unsubscribe'
- expect(page).to have_content 'Commands applied'
-
- expect(issuable.subscribed?(maintainer, project)).to be_falsy
- end
- end
-
- context "with a note assigning the #{issuable_type} to the current user" do
- it "assigns the #{issuable_type} to the current user" do
- add_note("/assign me")
-
- expect(page).not_to have_content '/assign me'
- expect(page).to have_content 'Commands applied'
-
- expect(issuable.reload.assignees).to eq [maintainer]
- end
- end
- end
-
- describe "preview of note on #{issuable_type}", :js do
- it 'removes quick actions from note and explains them' do
- create(:user, username: 'bob')
-
- visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
-
- page.within('.js-main-target-form') do
- fill_in 'note[note]', with: "Awesome!\n/assign @bob "
- click_on 'Preview'
-
- expect(page).to have_content 'Awesome!'
- expect(page).not_to have_content '/assign @bob'
- expect(page).to have_content 'Assigns @bob.'
- end
- end
- end
-end
diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb
index 89a5518239d..8cfce49da8a 100644
--- a/spec/support/features/reportable_note_shared_examples.rb
+++ b/spec/support/features/reportable_note_shared_examples.rb
@@ -20,7 +20,7 @@ shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- expect(dropdown).to have_link('Report as abuse', href: abuse_report_path)
+ expect(dropdown).to have_link('Report abuse to GitLab', href: abuse_report_path)
if type == 'issue' || type == 'merge_request'
expect(dropdown).to have_button('Delete comment')
@@ -33,7 +33,7 @@ shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- dropdown.click_link('Report as abuse')
+ dropdown.click_link('Report abuse to GitLab')
expect(find('#user_name')['value']).to match(note.author.username)
expect(find('#abuse_report_message')['value']).to match(noteable_note_url(note))
diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb
index f7f851eb1eb..bce1fb01355 100644
--- a/spec/support/features/variable_list_shared_examples.rb
+++ b/spec/support/features/variable_list_shared_examples.rb
@@ -5,7 +5,7 @@ shared_examples 'variable list' do
end
end
- it 'adds new secret variable' do
+ it 'adds new CI variable' do
page.within('.js-ci-variable-list-section .js-row:last-child') do
find('.js-ci-variable-input-key').set('key')
find('.js-ci-variable-input-value').set('key value')
diff --git a/spec/support/helpers/ci_artifact_metadata_generator.rb b/spec/support/helpers/ci_artifact_metadata_generator.rb
new file mode 100644
index 00000000000..ef638d59d2d
--- /dev/null
+++ b/spec/support/helpers/ci_artifact_metadata_generator.rb
@@ -0,0 +1,48 @@
+# frozen_sting_literal: true
+
+# This generates fake CI metadata .gz for testing
+# Based off https://gitlab.com/gitlab-org/gitlab-workhorse/blob/master/internal/zipartifacts/metadata.go
+class CiArtifactMetadataGenerator
+ attr_accessor :entries, :output
+
+ ARTIFACT_METADATA = "GitLab Build Artifacts Metadata 0.0.2\n".freeze
+
+ def initialize(stream)
+ @entries = {}
+ @output = Zlib::GzipWriter.new(stream)
+ end
+
+ def add_entry(filename)
+ @entries[filename] = { CRC: rand(0xfffffff), Comment: FFaker::Lorem.sentence(10) }
+ end
+
+ def write
+ write_version
+ write_errors
+ write_entries
+ output.close
+ end
+
+ private
+
+ def write_version
+ write_string(ARTIFACT_METADATA)
+ end
+
+ def write_errors
+ write_string('{}')
+ end
+
+ def write_entries
+ entries.each do |filename, metadata|
+ write_string(filename)
+ write_string(metadata.to_json + "\n")
+ end
+ end
+
+ def write_string(data)
+ bytes = [data.length].pack('L>')
+ output.write(bytes)
+ output.write(data)
+ end
+end
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index c228bd2393b..ecefdc23811 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -1,4 +1,6 @@
module CycleAnalyticsHelpers
+ include GitHelpers
+
def create_commit_referencing_issue(issue, branch_name: generate(:branch))
project.repository.add_branch(user, branch_name, 'master')
create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name)
@@ -9,7 +11,7 @@ module CycleAnalyticsHelpers
oldrev = repository.commit(branch_name)&.sha || Gitlab::Git::BLANK_SHA
if Timecop.frozen? && Gitlab::GitalyClient.feature_enabled?(:operation_user_commit_files)
- mock_gitaly_multi_action_dates(repository.raw, commit_time)
+ mock_gitaly_multi_action_dates(repository, commit_time)
end
commit_shas = Array.new(count) do |index|
@@ -65,7 +67,9 @@ module CycleAnalyticsHelpers
end
def merge_merge_requests_closing_issue(user, project, issue)
- merge_requests = issue.closed_by_merge_requests(user)
+ merge_requests = Issues::ReferencedMergeRequestsService
+ .new(project, user)
+ .closed_by_merge_requests(issue)
merge_requests.each { |merge_request| MergeRequests::MergeService.new(project, user).execute(merge_request) }
end
@@ -81,7 +85,7 @@ module CycleAnalyticsHelpers
raise ArgumentError
end
- CreateDeploymentService.new(dummy_job).execute
+ dummy_job.success! # State machine automatically update associated deployment/environment record
end
def dummy_production_job(user, project)
@@ -93,7 +97,7 @@ module CycleAnalyticsHelpers
end
def dummy_pipeline(project)
- Ci::Pipeline.new(
+ create(:ci_pipeline,
sha: project.repository.commit('master').sha,
ref: 'master',
source: :push,
@@ -102,9 +106,7 @@ module CycleAnalyticsHelpers
end
def new_dummy_job(user, project, environment)
- project.environments.find_or_create_by(name: environment)
-
- Ci::Build.new(
+ create(:ci_build,
project: project,
user: user,
environment: environment,
@@ -116,18 +118,15 @@ module CycleAnalyticsHelpers
protected: false)
end
- def mock_gitaly_multi_action_dates(raw_repository, commit_time)
- allow(raw_repository).to receive(:multi_action).and_wrap_original do |m, *args|
+ def mock_gitaly_multi_action_dates(repository, commit_time)
+ allow(repository.raw).to receive(:multi_action).and_wrap_original do |m, *args|
new_date = commit_time || Time.now
branch_update = m.call(*args)
if branch_update.newrev
_, opts = args
- commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- rugged = raw_repository.rugged
- rugged.rev_parse(branch_update.newrev)
- end
+ commit = rugged_repo(repository).rev_parse(branch_update.newrev)
branch_update.newrev = commit.amend(
update_ref: "#{Gitlab::Git::BRANCH_REF_PREFIX}#{opts[:branch_name]}",
diff --git a/spec/support/helpers/devise_helpers.rb b/spec/support/helpers/devise_helpers.rb
index 66874e10f38..d32bc2424c0 100644
--- a/spec/support/helpers/devise_helpers.rb
+++ b/spec/support/helpers/devise_helpers.rb
@@ -8,8 +8,15 @@ module DeviseHelpers
end
def env_from_context(context)
+ # When we modify env_config, that is on the global
+ # Rails.application, and we need to stub it and allow it to be
+ # modified in-place, without polluting later tests.
if context.respond_to?(:env_config)
- context.env_config
+ context.env_config.deep_dup.tap do |env|
+ allow(context).to receive(:env_config).and_return(env)
+ end
+ # When we modify env, then the context is a request, or something
+ # else that only lives for a single spec.
elsif context.respond_to?(:env)
context.env
end
diff --git a/spec/support/helpers/filter_item_select_helper.rb b/spec/support/helpers/filter_item_select_helper.rb
deleted file mode 100644
index 519e84d359e..00000000000
--- a/spec/support/helpers/filter_item_select_helper.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# Helper allows you to select value from filter-items
-#
-# Params
-# value - value for select
-# selector - css selector of item
-#
-# Usage:
-#
-# filter_item_select('Any Author', '.js-author-search')
-#
-module FilterItemSelectHelper
- def filter_item_select(value, selector)
- find(selector).click
- wait_for_requests
- page.within('.dropdown-content') do
- click_link value
- end
- end
-end
diff --git a/spec/support/helpers/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb
index 5f42ff77fb2..6569feec39b 100644
--- a/spec/support/helpers/filtered_search_helpers.rb
+++ b/spec/support/helpers/filtered_search_helpers.rb
@@ -120,8 +120,12 @@ module FilteredSearchHelpers
create_token('Label', label_name, symbol)
end
- def emoji_token(emoji_name = nil)
- { name: 'My-Reaction', emoji_name: emoji_name }
+ def reaction_token(reaction_name = nil, is_emoji = true)
+ if is_emoji
+ { name: 'My-Reaction', emoji_name: reaction_name }
+ else
+ create_token('My-Reaction', reaction_name)
+ end
end
def default_placeholder
diff --git a/spec/support/helpers/git_helpers.rb b/spec/support/helpers/git_helpers.rb
new file mode 100644
index 00000000000..99a7c39852e
--- /dev/null
+++ b/spec/support/helpers/git_helpers.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module GitHelpers
+ def rugged_repo(repository)
+ path = File.join(TestEnv.repos_path, repository.disk_path + '.git')
+
+ Rugged::Repository.new(path)
+ end
+
+ def project_hook_exists?(project)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project_path = project.repository.raw_repository.path
+
+ File.exist?(File.join(project_path, 'hooks', 'post-receive'))
+ end
+ end
+end
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index 683a64504a1..ccaf86aa3a6 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -16,6 +16,8 @@ module KubernetesHelpers
def stub_kubeclient_discover(api_url)
WebMock.stub_request(:get, api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body))
WebMock.stub_request(:get, api_url + '/apis/extensions/v1beta1').to_return(kube_response(kube_v1beta1_discovery_body))
+ WebMock.stub_request(:get, api_url + '/apis/rbac.authorization.k8s.io/v1').to_return(kube_response(kube_v1_rbac_authorization_discovery_body))
+ WebMock.stub_request(:get, api_url + '/apis/serving.knative.dev/v1alpha1').to_return(kube_response(kube_v1alpha1_serving_knative_discovery_body))
end
def stub_kubeclient_pods(response = nil)
@@ -32,31 +34,65 @@ module KubernetesHelpers
WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response)
end
- def stub_kubeclient_get_secrets(api_url, **options)
- WebMock.stub_request(:get, api_url + '/api/v1/secrets')
- .to_return(kube_response(kube_v1_secrets_body(options)))
+ def stub_kubeclient_get_secret(api_url, **options)
+ options[:metadata_name] ||= "default-token-1"
+ options[:namespace] ||= "default"
+
+ WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}")
+ .to_return(kube_response(kube_v1_secret_body(options)))
+ end
+
+ def stub_kubeclient_get_secret_error(api_url, name, namespace: 'default', status: 404)
+ WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{name}")
+ .to_return(status: [status, "Internal Server Error"])
+ end
+
+ def stub_kubeclient_create_service_account(api_url, namespace: 'default')
+ WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts")
+ .to_return(kube_response({}))
+ end
+
+ def stub_kubeclient_create_service_account_error(api_url, namespace: 'default')
+ WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts")
+ .to_return(status: [500, "Internal Server Error"])
+ end
+
+ def stub_kubeclient_create_secret(api_url, namespace: 'default')
+ WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets")
+ .to_return(kube_response({}))
end
- def stub_kubeclient_get_secrets_error(api_url)
- WebMock.stub_request(:get, api_url + '/api/v1/secrets')
- .to_return(status: [404, "Internal Server Error"])
+ def stub_kubeclient_create_cluster_role_binding(api_url)
+ WebMock.stub_request(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings')
+ .to_return(kube_response({}))
end
- def kube_v1_secrets_body(**options)
+ def stub_kubeclient_create_role_binding(api_url, namespace: 'default')
+ WebMock.stub_request(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings")
+ .to_return(kube_response({}))
+ end
+
+ def stub_kubeclient_create_namespace(api_url)
+ WebMock.stub_request(:post, api_url + "/api/v1/namespaces")
+ .to_return(kube_response({}))
+ end
+
+ def stub_kubeclient_get_namespace(api_url, namespace: 'default')
+ WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}")
+ .to_return(kube_response({}))
+ end
+
+ def kube_v1_secret_body(**options)
{
"kind" => "SecretList",
"apiVersion": "v1",
- "items" => [
- {
- "metadata": {
- "name": options[:metadata_name] || "default-token-1",
- "namespace": "kube-system"
- },
- "data": {
- "token": options[:token] || Base64.encode64('token-sample-123')
- }
- }
- ]
+ "metadata": {
+ "name": options[:metadata_name] || "default-token-1",
+ "namespace": "kube-system"
+ },
+ "data": {
+ "token": options[:token] || Base64.encode64('token-sample-123')
+ }
}
end
@@ -66,7 +102,10 @@ module KubernetesHelpers
"resources" => [
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
- { "name" => "secrets", "namespaced" => true, "kind" => "Secret" }
+ { "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
+ { "name" => "serviceaccounts", "namespaced" => true, "kind" => "ServiceAccount" },
+ { "name" => "services", "namespaced" => true, "kind" => "Service" },
+ { "name" => "namespaces", "namespaced" => true, "kind" => "Namespace" }
]
}
end
@@ -77,7 +116,33 @@ module KubernetesHelpers
"resources" => [
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
- { "name" => "secrets", "namespaced" => true, "kind" => "Secret" }
+ { "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
+ { "name" => "serviceaccounts", "namespaced" => true, "kind" => "ServiceAccount" },
+ { "name" => "services", "namespaced" => true, "kind" => "Service" }
+ ]
+ }
+ end
+
+ def kube_v1_rbac_authorization_discovery_body
+ {
+ "kind" => "APIResourceList",
+ "resources" => [
+ { "name" => "clusterrolebindings", "namespaced" => false, "kind" => "ClusterRoleBinding" },
+ { "name" => "clusterroles", "namespaced" => false, "kind" => "ClusterRole" },
+ { "name" => "rolebindings", "namespaced" => true, "kind" => "RoleBinding" },
+ { "name" => "roles", "namespaced" => true, "kind" => "Role" }
+ ]
+ }
+ end
+
+ def kube_v1alpha1_serving_knative_discovery_body
+ {
+ "kind" => "APIResourceList",
+ "resources" => [
+ { "name" => "revisions", "namespaced" => true, "kind" => "Revision" },
+ { "name" => "services", "namespaced" => true, "kind" => "Service" },
+ { "name" => "configurations", "namespaced" => true, "kind" => "Configuration" },
+ { "name" => "routes", "namespaced" => true, "kind" => "Route" }
]
}
end
diff --git a/spec/support/helpers/ldap_helpers.rb b/spec/support/helpers/ldap_helpers.rb
index b90bbc4b106..66ca5d7f0a3 100644
--- a/spec/support/helpers/ldap_helpers.rb
+++ b/spec/support/helpers/ldap_helpers.rb
@@ -37,6 +37,23 @@ module LdapHelpers
.to receive(:find_by_uid).with(uid, any_args).and_return(return_value)
end
+ def stub_ldap_person_find_by_dn(entry, provider = 'ldapmain')
+ person = ::Gitlab::Auth::LDAP::Person.new(entry, provider) if entry.present?
+
+ allow(::Gitlab::Auth::LDAP::Person)
+ .to receive(:find_by_dn)
+ .and_return(person)
+ end
+
+ def stub_ldap_person_find_by_email(email, entry, provider = 'ldapmain')
+ person = ::Gitlab::Auth::LDAP::Person.new(entry, provider) if entry.present?
+
+ allow(::Gitlab::Auth::LDAP::Person)
+ .to receive(:find_by_email)
+ .with(email, anything)
+ .and_return(person)
+ end
+
# Create a simple LDAP user entry.
def ldap_user_entry(uid)
entry = Net::LDAP::Entry.new
diff --git a/spec/support/helpers/markdown_feature.rb b/spec/support/helpers/markdown_feature.rb
index 346f5b1cc4d..96401379cf0 100644
--- a/spec/support/helpers/markdown_feature.rb
+++ b/spec/support/helpers/markdown_feature.rb
@@ -10,6 +10,12 @@
class MarkdownFeature
include FactoryBot::Syntax::Methods
+ attr_reader :fixture_path
+
+ def initialize(fixture_path = Rails.root.join('spec/fixtures/markdown.md.erb'))
+ @fixture_path = fixture_path
+ end
+
def user
@user ||= create(:user)
end
@@ -122,7 +128,7 @@ class MarkdownFeature
end
def raw_markdown
- markdown = File.read(Rails.root.join('spec/fixtures/markdown.md.erb'))
+ markdown = File.read(fixture_path)
ERB.new(markdown).result(binding)
end
end
diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb
index 0bc235701eb..5887c3eab74 100644
--- a/spec/support/helpers/migrations_helpers.rb
+++ b/spec/support/helpers/migrations_helpers.rb
@@ -1,8 +1,16 @@
module MigrationsHelpers
+ def active_record_base
+ ActiveRecord::Base
+ end
+
def table(name)
- Class.new(ActiveRecord::Base) do
+ Class.new(active_record_base) do
self.table_name = name
self.inheritance_column = :_type_disabled
+
+ def self.name
+ table_name.singularize.camelcase
+ end
end
end
@@ -15,7 +23,7 @@ module MigrationsHelpers
end
def clear_schema_cache!
- ActiveRecord::Base.connection_pool.connections.each do |conn|
+ active_record_base.connection_pool.connections.each do |conn|
conn.schema_cache.clear!
end
end
@@ -36,11 +44,18 @@ module MigrationsHelpers
# Reset column information for the most offending classes **after** we
# migrated the schema up, otherwise, column information could be
# outdated. We have a separate method for this so we can override it in EE.
- ActiveRecord::Base.descendants.each(&method(:reset_column_information))
+ active_record_base.descendants.each(&method(:reset_column_information))
+ end
- # Without that, we get errors because of missing attributes, e.g.
+ def refresh_attribute_methods
+ # Without this, we get errors because of missing attributes, e.g.
# super: no superclass method `elasticsearch_indexing' for #<ApplicationSetting:0x00007f85628508d8>
- ApplicationSetting.define_attribute_methods
+ # attr_encrypted also expects ActiveRecord attribute methods to be
+ # defined, or it will override the accessors:
+ # https://gitlab.com/gitlab-org/gitlab-ee/issues/8234#note_113976421
+ [ApplicationSetting, SystemHook].each do |model|
+ model.define_attribute_methods
+ end
end
def reset_column_information(klass)
@@ -80,6 +95,7 @@ module MigrationsHelpers
end
reset_column_in_all_models
+ refresh_attribute_methods
end
def disable_migrations_output
diff --git a/spec/support/helpers/project_forks_helper.rb b/spec/support/helpers/project_forks_helper.rb
index 2c501a2a27c..9a86560da2a 100644
--- a/spec/support/helpers/project_forks_helper.rb
+++ b/spec/support/helpers/project_forks_helper.rb
@@ -24,7 +24,7 @@ module ProjectForksHelper
allow(service).to receive(:gitlab_shell).and_return(shell)
end
- forked_project = service.execute
+ forked_project = service.execute(params[:target_project])
# Reload the both projects so they know about their newly created fork_network
if forked_project.persisted?
@@ -35,7 +35,7 @@ module ProjectForksHelper
if create_repository
# The call to project.repository.after_import in RepositoryForkWorker does
# not reset the @exists variable of this forked_project.repository
- # so we have to explicitely call this method to clear the @exists variable.
+ # so we have to explicitly call this method to clear the @exists variable.
# of the instance we're returning here.
forked_project.repository.after_import
end
diff --git a/spec/support/helpers/reference_parser_helpers.rb b/spec/support/helpers/reference_parser_helpers.rb
index c01897ed1a1..9f27502aa52 100644
--- a/spec/support/helpers/reference_parser_helpers.rb
+++ b/spec/support/helpers/reference_parser_helpers.rb
@@ -3,7 +3,7 @@ module ReferenceParserHelpers
Nokogiri::HTML.fragment('<a></a>').children[0]
end
- shared_examples 'no N+1 queries' do
+ shared_examples 'no project N+1 queries' do
it 'avoids N+1 queries in #nodes_visible_to_user', :request_store do
context = Banzai::RenderContext.new(project, user)
@@ -19,6 +19,10 @@ module ReferenceParserHelpers
expect(actual.count).to be <= control.count
expect(actual.cached_count).to be <= control.cached_count
end
+ end
+
+ shared_examples 'no N+1 queries' do
+ it_behaves_like 'no project N+1 queries'
it 'avoids N+1 queries in #records_for_nodes', :request_store do
context = Banzai::RenderContext.new(project, user)
diff --git a/spec/support/helpers/seed_helper.rb b/spec/support/helpers/seed_helper.rb
index 25781f5e679..90d7f60fdeb 100644
--- a/spec/support/helpers/seed_helper.rb
+++ b/spec/support/helpers/seed_helper.rb
@@ -1,15 +1,18 @@
+# frozen_string_literal: true
+
require_relative 'test_env'
# This file is specific to specs in spec/lib/gitlab/git/
SEED_STORAGE_PATH = TestEnv.repos_path
-TEST_REPO_PATH = 'gitlab-git-test.git'.freeze
-TEST_NORMAL_REPO_PATH = 'not-bare-repo.git'.freeze
-TEST_MUTABLE_REPO_PATH = 'mutable-repo.git'.freeze
-TEST_BROKEN_REPO_PATH = 'broken-repo.git'.freeze
+TEST_REPO_PATH = 'gitlab-git-test.git'
+TEST_NORMAL_REPO_PATH = 'not-bare-repo.git'
+TEST_MUTABLE_REPO_PATH = 'mutable-repo.git'
+TEST_BROKEN_REPO_PATH = 'broken-repo.git'
+TEST_GITATTRIBUTES_REPO_PATH = 'with-git-attributes.git'
module SeedHelper
- GITLAB_GIT_TEST_REPO_URL = File.expand_path('../gitlab-git-test.git', __dir__).freeze
+ GITLAB_GIT_TEST_REPO_URL = File.expand_path('../gitlab-git-test.git', __dir__)
def ensure_seeds
if File.exist?(SEED_STORAGE_PATH)
@@ -66,6 +69,11 @@ module SeedHelper
end
def create_git_attributes
+ system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --bare #{TEST_REPO_PATH} #{TEST_GITATTRIBUTES_REPO_PATH}),
+ chdir: SEED_STORAGE_PATH,
+ out: '/dev/null',
+ err: '/dev/null')
+
dir = File.join(SEED_STORAGE_PATH, 'with-git-attributes.git', 'info')
FileUtils.mkdir_p(dir)
@@ -82,6 +90,8 @@ foo/bar.* foo
*.cgi key=value?p1=v1&p2=v2
/*.png gitlab-language=png
*.binary binary
+/custom-highlighting/*.gitlab-custom gitlab-language=ruby
+/custom-highlighting/*.gitlab-cgi gitlab-language=erb?parent=json
# This uses a tab instead of spaces to ensure the parser also supports this.
*.md\tgitlab-language=markdown
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index 1823099dd9c..776119564ec 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -1,5 +1,8 @@
require 'active_support/core_ext/hash/transform_values'
require 'active_support/hash_with_indifferent_access'
+require 'active_support/dependencies'
+
+require_dependency 'gitlab'
module StubConfiguration
def stub_application_setting(messages)
@@ -68,6 +71,10 @@ module StubConfiguration
allow(Gitlab.config.repositories).to receive(:storages).and_return(Settingslogic.new(messages))
end
+ def stub_kerberos_setting(messages)
+ allow(Gitlab.config.kerberos).to receive_messages(to_settings(messages))
+ end
+
private
# Modifies stubbed messages to also stub possible predicate versions
diff --git a/spec/support/helpers/stub_feature_flags.rb b/spec/support/helpers/stub_feature_flags.rb
index b96338bf548..4061a8d1bc9 100644
--- a/spec/support/helpers/stub_feature_flags.rb
+++ b/spec/support/helpers/stub_feature_flags.rb
@@ -1,8 +1,11 @@
module StubFeatureFlags
+ # Stub Feature flags with `flag_name: true/false`
+ #
+ # @param [Hash] features where key is feature name and value is boolean whether enabled or not
def stub_feature_flags(features)
features.each do |feature_name, enabled|
- allow(Feature).to receive(:enabled?).with(feature_name) { enabled }
- allow(Feature).to receive(:enabled?).with(feature_name.to_s) { enabled }
+ allow(Feature).to receive(:enabled?).with(feature_name, any_args) { enabled }
+ allow(Feature).to receive(:enabled?).with(feature_name.to_s, any_args) { enabled }
end
end
end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index f392660d2c7..1f00cdf7e92 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -31,6 +31,8 @@ module TestEnv
'symlink-expand-diff' => '81e6355',
'expand-collapse-files' => '025db92',
'expand-collapse-lines' => '238e82d',
+ 'pages-deploy' => '7897d5b',
+ 'pages-deploy-target' => '7975be0',
'video' => '8879059',
'add-balsamiq-file' => 'b89b56d',
'crlf-diff' => '5938907',
@@ -52,7 +54,12 @@ module TestEnv
'add_images_and_changes' => '010d106',
'update-gitlab-shell-v-6-0-1' => '2f61d70',
'update-gitlab-shell-v-6-0-3' => 'de78448',
- '2-mb-file' => 'bf12d25'
+ '2-mb-file' => 'bf12d25',
+ 'before-create-delete-modify-move' => '845009f',
+ 'between-create-delete-modify-move' => '3f5f443',
+ 'after-create-delete-modify-move' => 'ba3faa7',
+ 'with-codeowners' => '219560e',
+ 'submodule_inside_folder' => 'b491b92'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
@@ -83,7 +90,7 @@ module TestEnv
clean_test_path
- # Setup GitLab shell for test instance
+ # Set up GitLab shell for test instance
setup_gitlab_shell
setup_gitaly
@@ -105,10 +112,6 @@ module TestEnv
.and_call_original
end
- def disable_pre_receive
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
- end
-
# Clean /tmp/tests
#
# Keeps gitlab-shell and gitlab-test
@@ -157,13 +160,11 @@ module TestEnv
component_timed_setup('Gitaly',
install_dir: gitaly_dir,
version: Gitlab::GitalyClient.expected_server_version,
- task: "gitlab:gitaly:install[#{gitaly_dir}]") do
-
- # Always re-create config, in case it's outdated. This is fast anyway.
- Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, force: true)
+ task: "gitlab:gitaly:install[#{gitaly_dir},#{repos_path}]") do
- start_gitaly(gitaly_dir)
- end
+ Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
+ start_gitaly(gitaly_dir)
+ end
end
def start_gitaly(gitaly_dir)
@@ -172,6 +173,8 @@ module TestEnv
return
end
+ FileUtils.mkdir_p("tmp/tests/second_storage") unless File.exist?("tmp/tests/second_storage")
+
spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s
Bundler.with_original_env do
raise "gitaly spawn failed" unless system(spawn_script)
@@ -364,7 +367,7 @@ module TestEnv
FileUtils.rm_rf(install_dir)
exit 1
ensure
- puts " #{component} setup in #{Time.now - start} seconds...\n"
+ puts " #{component} set up in #{Time.now - start} seconds...\n"
end
def ensure_component_dir_name_is_correct!(component, path)
diff --git a/spec/support/helpers/test_request_helpers.rb b/spec/support/helpers/test_request_helpers.rb
new file mode 100644
index 00000000000..187a0e07891
--- /dev/null
+++ b/spec/support/helpers/test_request_helpers.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module TestRequestHelpers
+ def test_request(remote_ip: '127.0.0.1')
+ if Gitlab.rails5?
+ ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new)
+ else
+ ActionController::TestRequest.new(remote_ip: remote_ip)
+ end
+ end
+end
diff --git a/spec/support/helpers/user_login_helper.rb b/spec/support/helpers/user_login_helper.rb
new file mode 100644
index 00000000000..36c002f53af
--- /dev/null
+++ b/spec/support/helpers/user_login_helper.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module UserLoginHelper
+ def ensure_tab_pane_correctness(visit_path = true)
+ if visit_path
+ visit new_user_session_path
+ end
+
+ ensure_tab_pane_counts
+ ensure_one_active_tab
+ ensure_one_active_pane
+ end
+
+ def ensure_tab_pane_counts
+ tabs_count = page.all('[role="tab"]').size
+ expect(page).to have_selector('[role="tabpanel"]', count: tabs_count)
+ end
+
+ def ensure_one_active_tab
+ expect(page).to have_selector('ul.new-session-tabs > li > a.active', count: 1)
+ end
+
+ def ensure_one_active_pane
+ expect(page).to have_selector('.tab-pane.active', count: 1)
+ end
+end
diff --git a/spec/support/helpers/wiki_helpers.rb b/spec/support/helpers/wiki_helpers.rb
new file mode 100644
index 00000000000..8165403cb60
--- /dev/null
+++ b/spec/support/helpers/wiki_helpers.rb
@@ -0,0 +1,13 @@
+module WikiHelpers
+ extend self
+
+ def upload_file_to_wiki(project, user, file_name)
+ opts = {
+ file_name: file_name,
+ file_content: File.read(expand_fixture_path(file_name))
+ }
+
+ ::Wikis::CreateAttachmentService.new(project, user, opts)
+ .execute[:result][:file_path]
+ end
+end
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index 4d925ac77f4..d9ed405baf4 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -52,7 +52,7 @@ module ExportFileHelper
# Expands the compressed file for an exported project into +tmpdir+
def in_directory_with_expanded_export(project)
Dir.mktmpdir do |tmpdir|
- export_file = project.export_project_path
+ export_file = project.export_file.path
_output, exit_status = Gitlab::Popen.popen(%W{tar -zxf #{export_file} -C #{tmpdir}})
yield(exit_status, tmpdir)
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
index 9b8bcebcb3a..b38c5dfe60b 100644
--- a/spec/support/rspec.rb
+++ b/spec/support/rspec.rb
@@ -11,6 +11,4 @@ RSpec.configure do |config|
config.include StubMetrics
config.include StubObjectStorage
config.include StubENV
-
- config.fixture_path = Rails.root if defined?(Rails)
end
diff --git a/spec/support/services/clusters/create_service_shared.rb b/spec/support/services/clusters/create_service_shared.rb
index 43a2fd05498..b0bf942aa09 100644
--- a/spec/support/services/clusters/create_service_shared.rb
+++ b/spec/support/services/clusters/create_service_shared.rb
@@ -7,7 +7,8 @@ shared_context 'valid cluster create params' do
gcp_project_id: 'gcp-project',
zone: 'us-central1-a',
num_nodes: 1,
- machine_type: 'machine_type-a'
+ machine_type: 'machine_type-a',
+ legacy_abac: 'true'
}
}
end
@@ -44,6 +45,7 @@ shared_examples 'create cluster service success' do
expect(subject.provider.num_nodes).to eq(1)
expect(subject.provider.machine_type).to eq('machine_type-a')
expect(subject.provider.access_token).to eq(access_token)
+ expect(subject.provider).to be_legacy_abac
expect(subject.platform).to be_nil
end
end
diff --git a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
new file mode 100644
index 00000000000..95e69328080
--- /dev/null
+++ b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
@@ -0,0 +1,66 @@
+shared_examples 'issuable notes filter' do
+ it 'sets discussion filter' do
+ notes_filter = UserPreference::NOTES_FILTERS[:only_comments]
+
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter
+
+ expect(user.reload.notes_filter_for(issuable)).to eq(notes_filter)
+ expect(UserPreference.count).to eq(1)
+ end
+
+ it 'expires notes e-tag cache for issuable if filter changed' do
+ notes_filter = UserPreference::NOTES_FILTERS[:only_comments]
+
+ expect_any_instance_of(issuable.class).to receive(:expire_note_etag_cache)
+
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter
+ end
+
+ it 'does not expires notes e-tag cache for issuable if filter did not change' do
+ notes_filter = UserPreference::NOTES_FILTERS[:only_comments]
+ user.set_notes_filter(notes_filter, issuable)
+
+ expect_any_instance_of(issuable.class).not_to receive(:expire_note_etag_cache)
+
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter
+ end
+
+ it 'does not set notes filter when database is in read only mode' do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ notes_filter = UserPreference::NOTES_FILTERS[:only_comments]
+
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter
+
+ expect(user.reload.notes_filter_for(issuable)).to eq(0)
+ end
+
+ it 'returns only user comments' do
+ user.set_notes_filter(UserPreference::NOTES_FILTERS[:only_comments], issuable)
+
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid
+ discussions = JSON.parse(response.body)
+
+ expect(discussions.count).to eq(1)
+ expect(discussions.first["notes"].first["system"]).to be(false)
+ end
+
+ it 'returns only activity notes' do
+ user.set_notes_filter(UserPreference::NOTES_FILTERS[:only_activity], issuable)
+
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid
+ discussions = JSON.parse(response.body)
+
+ expect(discussions.count).to eq(1)
+ expect(discussions.first["notes"].first["system"]).to be(true)
+ end
+
+ context 'when filter is set to "only_comments"' do
+ it 'does not merge label event notes' do
+ user.set_notes_filter(UserPreference::NOTES_FILTERS[:only_comments], issuable)
+
+ expect(ResourceEvents::MergeIntoNotesService).not_to receive(:new)
+
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid
+ end
+ end
+end
diff --git a/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb b/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb
new file mode 100644
index 00000000000..a3d31e26498
--- /dev/null
+++ b/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+# Shared examples for controllers that load and send files from the git repository
+# (like Projects::RawController or Projects::AvatarsController)
+
+# These examples requires the following variables:
+# - `project`
+# - `filename`: filename of the file
+# - `filepath`: path of the file (contains filename)
+# - `subject`: the request to be made to the controller. Example:
+# subject { get :show, namespace_id: project.namespace, project_id: project }
+shared_examples 'repository lfs file load' do
+ context 'when file is stored in lfs' do
+ let(:lfs_oid) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
+ let(:lfs_size) { '1575078' }
+ let!(:lfs_object) { create(:lfs_object, oid: lfs_oid, size: lfs_size) }
+
+ context 'when lfs is enabled' do
+ before do
+ allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(true)
+ end
+
+ context 'when project has access' do
+ before do
+ project.lfs_objects << lfs_object
+ allow_any_instance_of(LfsObjectUploader).to receive(:exists?).and_return(true)
+ allow(controller).to receive(:send_file) { controller.head :ok }
+ end
+
+ it 'serves the file' do
+ expect(controller).to receive(:send_file).with("#{LfsObjectUploader.root}/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", filename: filename, disposition: 'attachment')
+
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ context 'and lfs uses object storage' do
+ let(:lfs_object) { create(:lfs_object, :with_file, oid: lfs_oid, size: lfs_size) }
+
+ before do
+ stub_lfs_object_storage
+ lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
+ end
+
+ it 'responds with redirect to file' do
+ subject
+
+ expect(response).to have_gitlab_http_status(302)
+ expect(response.location).to include(lfs_object.reload.file.path)
+ end
+
+ it 'sets content disposition' do
+ subject
+
+ file_uri = URI.parse(response.location)
+ params = CGI.parse(file_uri.query)
+
+ expect(params["response-content-disposition"].first).to eq "attachment;filename=\"#{filename}\""
+ end
+ end
+ end
+
+ context 'when project does not have access' do
+ it 'does not serve the file' do
+ subject
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
+ context 'when lfs is not enabled' do
+ before do
+ allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(false)
+ end
+
+ it 'delivers ASCII file' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
+ expect(response.header['Content-Disposition'])
+ .to eq('inline')
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/diff_file_collections.rb b/spec/support/shared_examples/diff_file_collections.rb
new file mode 100644
index 00000000000..55ce160add0
--- /dev/null
+++ b/spec/support/shared_examples/diff_file_collections.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+shared_examples 'diff statistics' do |test_include_stats_flag: true|
+ def stub_stats_find_by_path(path, stats_mock)
+ expect_next_instance_of(Gitlab::Git::DiffStatsCollection) do |collection|
+ allow(collection).to receive(:find_by_path).and_call_original
+ expect(collection).to receive(:find_by_path).with(path).and_return(stats_mock)
+ end
+ end
+
+ context 'when should request diff stats' do
+ it 'Repository#diff_stats is called' do
+ subject = described_class.new(diffable, collection_default_args)
+
+ expect(diffable.project.repository)
+ .to receive(:diff_stats)
+ .with(diffable.diff_refs.base_sha, diffable.diff_refs.head_sha)
+ .and_call_original
+
+ subject.diff_files
+ end
+
+ it 'Gitlab::Diff::File is initialized with diff stats' do
+ subject = described_class.new(diffable, collection_default_args)
+
+ stats_mock = double(Gitaly::DiffStats, path: '.gitignore', additions: 758, deletions: 120)
+ stub_stats_find_by_path(stub_path, stats_mock)
+
+ diff_file = subject.diff_files.find { |file| file.new_path == stub_path }
+
+ expect(diff_file.added_lines).to eq(stats_mock.additions)
+ expect(diff_file.removed_lines).to eq(stats_mock.deletions)
+ end
+ end
+
+ context 'when should not request diff stats' do
+ it 'Repository#diff_stats is not called' do
+ collection_default_args[:diff_options][:include_stats] = false
+
+ subject = described_class.new(diffable, collection_default_args)
+
+ expect(diffable.project.repository).not_to receive(:diff_stats)
+
+ subject.diff_files
+ end
+ end
+end
diff --git a/spec/support/shared_examples/dirty_submit_form_shared_examples.rb b/spec/support/shared_examples/dirty_submit_form_shared_examples.rb
new file mode 100644
index 00000000000..ba363593120
--- /dev/null
+++ b/spec/support/shared_examples/dirty_submit_form_shared_examples.rb
@@ -0,0 +1,24 @@
+shared_examples 'dirty submit form' do |selector_args|
+ selectors = selector_args.is_a?(Array) ? selector_args : [selector_args]
+
+ selectors.each do |selector|
+ it "disables #{selector[:form]} submit until there are changes", :js do
+ form = find(selector[:form])
+ submit = form.first('.js-dirty-submit')
+ input = form.first(selector[:input])
+ original_value = input.value
+
+ expect(submit.disabled?).to be true
+
+ input.set("#{original_value} changes")
+
+ form.find('.js-dirty-submit:not([disabled])', match: :first)
+ expect(submit.disabled?).to be false
+
+ input.set(original_value)
+
+ form.find('.js-dirty-submit[disabled]', match: :first)
+ expect(submit.disabled?).to be true
+ end
+ end
+end
diff --git a/spec/support/shared_examples/fast_destroy_all.rb b/spec/support/shared_examples/fast_destroy_all.rb
index 5448ddcfe33..a8079b6d864 100644
--- a/spec/support/shared_examples/fast_destroy_all.rb
+++ b/spec/support/shared_examples/fast_destroy_all.rb
@@ -4,8 +4,8 @@ shared_examples_for 'fast destroyable' do
expect(external_data_counter).to be > 0
expect(subjects.count).to be > 0
- expect { subjects.first.destroy }.to raise_error('`destroy` and `destroy_all` are forbbiden. Please use `fast_destroy_all`')
- expect { subjects.destroy_all }.to raise_error('`destroy` and `destroy_all` are forbbiden. Please use `fast_destroy_all`')
+ expect { subjects.first.destroy }.to raise_error('`destroy` and `destroy_all` are forbidden. Please use `fast_destroy_all`')
+ expect { subjects.destroy_all }.to raise_error('`destroy` and `destroy_all` are forbidden. Please use `fast_destroy_all`') # rubocop: disable DestroyAll
expect(subjects.count).to be > 0
expect(external_data_counter).to be > 0
diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
index 3057845061b..a096627ee62 100644
--- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
@@ -73,9 +73,13 @@ RSpec.shared_examples 'an editable merge request' do
it 'description has autocomplete', :js do
find('#merge_request_description').native.send_keys('')
- fill_in 'merge_request_description', with: '@'
+ fill_in 'merge_request_description', with: user.to_reference[0..4]
- expect(page).to have_selector('.atwho-view')
+ wait_for_requests
+
+ page.within('.atwho-view') do
+ expect(page).to have_content(user2.name)
+ end
end
it 'has class js-quick-submit in form' do
diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb
index ef9bb7f5533..ba9b7d3bdcf 100644
--- a/spec/support/shared_examples/helm_generated_script.rb
+++ b/spec/support/shared_examples/helm_generated_script.rb
@@ -2,18 +2,12 @@ shared_examples 'helm commands' do
describe '#generate_script' do
let(:helm_setup) do
<<~EOS
- set -eo pipefail
- ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
- echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
- echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
- apk add -U wget ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.2-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
+ set -xeo pipefail
EOS
end
it 'should return appropriate command' do
- expect(subject.generate_script).to eq(helm_setup + commands)
+ expect(subject.generate_script.strip).to eq((helm_setup + commands).strip)
end
end
end
diff --git a/spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb b/spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb
index 5334af841e1..8ea307c7c61 100644
--- a/spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb
+++ b/spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb
@@ -5,6 +5,8 @@ shared_examples 'instance statistics availability' do
before do
sign_in(user)
+
+ stub_application_setting(usage_ping_enabled: true)
end
describe 'GET #index' do
diff --git a/spec/support/shared_examples/models/chat_service_spec.rb b/spec/support/shared_examples/models/chat_service_spec.rb
new file mode 100644
index 00000000000..cf1d52a9616
--- /dev/null
+++ b/spec/support/shared_examples/models/chat_service_spec.rb
@@ -0,0 +1,242 @@
+require "spec_helper"
+
+shared_examples_for "chat service" do |service_name|
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Validations" do
+ context "when service is active" do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of(:webhook) }
+ it_behaves_like "issue tracker service URL attribute", :webhook
+ end
+
+ context "when service is inactive" do
+ before do
+ subject.active = false
+ end
+
+ it { is_expected.not_to validate_presence_of(:webhook) }
+ end
+ end
+
+ describe "#execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:webhook_url) { "https://example.gitlab.com/" }
+
+ before do
+ allow(subject).to receive_messages(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+ end
+
+ shared_examples "#{service_name} service" do
+ it "calls #{service_name} API" do
+ subject.execute(sample_data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).with { |req| req.body =~ /\A{"#{content_key}":.+}\Z/ }.once
+ end
+ end
+
+ context "with push events" do
+ let(:sample_data) do
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+ end
+
+ it_behaves_like "#{service_name} service"
+
+ it "specifies the webhook when it is configured" do
+ expect(client).to receive(:new).with(client_arguments).and_return(double(:chat_service).as_null_object)
+
+ subject.execute(sample_data)
+ end
+
+ context "with not default branch" do
+ let(:sample_data) do
+ Gitlab::DataBuilder::Push.build(project, user, nil, nil, "not-the-default-branch")
+ end
+
+ context "when notify_only_default_branch enabled" do
+ before do
+ subject.notify_only_default_branch = true
+ end
+
+ it "does not call the Discord Webhooks API" do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context "when notify_only_default_branch disabled" do
+ before do
+ subject.notify_only_default_branch = false
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+ end
+ end
+
+ context "with issue events" do
+ let(:opts) { { title: "Awesome issue", description: "please fix" } }
+ let(:sample_data) do
+ service = Issues::CreateService.new(project, user, opts)
+ issue = service.execute
+ service.hook_data(issue, "open")
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+
+ context "with merge events" do
+ let(:opts) do
+ {
+ title: "Awesome merge_request",
+ description: "please fix",
+ source_branch: "feature",
+ target_branch: "master"
+ }
+ end
+
+ let(:sample_data) do
+ service = MergeRequests::CreateService.new(project, user, opts)
+ merge_request = service.execute
+ service.hook_data(merge_request, "open")
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+
+ context "with wiki page events" do
+ let(:opts) do
+ {
+ title: "Awesome wiki_page",
+ content: "Some text describing some thing or another",
+ format: "md",
+ message: "user created page: Awesome wiki_page"
+ }
+ end
+ let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) }
+ let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, "create") }
+
+ it_behaves_like "#{service_name} service"
+ end
+
+ context "with note events" do
+ let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) }
+
+ context "with commit comment" do
+ let(:note) do
+ create(:note_on_commit,
+ author: user,
+ project: project,
+ commit_id: project.repository.commit.id,
+ note: "a comment on a commit")
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+
+ context "with merge request comment" do
+ let(:note) do
+ create(:note_on_merge_request, project: project, note: "merge request note")
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+
+ context "with issue comment" do
+ let(:note) do
+ create(:note_on_issue, project: project, note: "issue note")
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+
+ context "with snippet comment" do
+ let(:note) do
+ create(:note_on_project_snippet, project: project, note: "snippet note")
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+ end
+
+ context "with pipeline events" do
+ let(:pipeline) do
+ create(:ci_pipeline,
+ project: project, status: status,
+ sha: project.commit.sha, ref: project.default_branch)
+ end
+ let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
+
+ context "with failed pipeline" do
+ let(:status) { "failed" }
+
+ it_behaves_like "#{service_name} service"
+ end
+
+ context "with succeeded pipeline" do
+ let(:status) { "success" }
+
+ context "with default notify_only_broken_pipelines" do
+ it "does not call Discord Webhooks API" do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context "when notify_only_broken_pipelines is false" do
+ before do
+ subject.notify_only_broken_pipelines = false
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+ end
+
+ context "with not default branch" do
+ let(:pipeline) do
+ create(:ci_pipeline, project: project, status: "failed", ref: "not-the-default-branch")
+ end
+
+ context "when notify_only_default_branch enabled" do
+ before do
+ subject.notify_only_default_branch = true
+ end
+
+ it "does not call the Discord Webhooks API" do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context "when notify_only_default_branch disabled" do
+ before do
+ subject.notify_only_default_branch = false
+ end
+
+ it_behaves_like "#{service_name} service"
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
index 87d12a784ba..1f76b981292 100644
--- a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
@@ -11,60 +11,4 @@ shared_examples 'cluster application core specs' do |application_name|
expect(Clusters::Cluster::APPLICATIONS[subject.name]).to eq(described_class)
end
end
-
- describe 'status state machine' do
- describe '#make_installing' do
- subject { create(application_name, :scheduled) }
-
- it 'is installing' do
- subject.make_installing!
-
- expect(subject).to be_installing
- end
- end
-
- describe '#make_installed' do
- subject { create(application_name, :installing) }
-
- it 'is installed' do
- subject.make_installed
-
- expect(subject).to be_installed
- end
- end
-
- describe '#make_errored' do
- subject { create(application_name, :installing) }
- let(:reason) { 'some errors' }
-
- it 'is errored' do
- subject.make_errored(reason)
-
- expect(subject).to be_errored
- expect(subject.status_reason).to eq(reason)
- end
- end
-
- describe '#make_scheduled' do
- subject { create(application_name, :installable) }
-
- it 'is scheduled' do
- subject.make_scheduled
-
- expect(subject).to be_scheduled
- end
-
- describe 'when was errored' do
- subject { create(application_name, :errored) }
-
- it 'clears #status_reason' do
- expect(subject.status_reason).not_to be_nil
-
- subject.make_scheduled!
-
- expect(subject.status_reason).to be_nil
- end
- end
- end
- end
end
diff --git a/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb b/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb
new file mode 100644
index 00000000000..d87b3181e80
--- /dev/null
+++ b/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb
@@ -0,0 +1,25 @@
+shared_examples 'cluster application helm specs' do |application_name|
+ let(:application) { create(application_name) }
+
+ describe '#files' do
+ subject { application.files }
+
+ context 'when the helm application does not have a ca_cert' do
+ before do
+ application.cluster.application_helm.ca_cert = nil
+ end
+
+ it 'should not include cert files when there is no ca_cert entry' do
+ expect(subject).not_to include(:'ca.pem', :'cert.pem', :'key.pem')
+ end
+ end
+
+ it 'should include cert files when there is a ca_cert entry' do
+ expect(subject).to include(:'ca.pem', :'cert.pem', :'key.pem')
+ expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
+
+ cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
+ expect(cert.not_after).to be < 60.minutes.from_now
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
index 765dd32f4ba..c391cc48f4e 100644
--- a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
@@ -28,4 +28,117 @@ shared_examples 'cluster application status specs' do |application_name|
end
end
end
+
+ describe 'status state machine' do
+ describe '#make_installing' do
+ subject { create(application_name, :scheduled) }
+
+ it 'is installing' do
+ subject.make_installing!
+
+ expect(subject).to be_installing
+ end
+ end
+
+ describe '#make_installed' do
+ subject { create(application_name, :installing) }
+
+ it 'is installed' do
+ subject.make_installed!
+
+ expect(subject).to be_installed
+ end
+
+ it 'updates helm version' do
+ subject.cluster.application_helm.update!(version: '1.2.3')
+
+ subject.make_installed!
+
+ subject.cluster.application_helm.reload
+
+ expect(subject.cluster.application_helm.version).to eq(Gitlab::Kubernetes::Helm::HELM_VERSION)
+ end
+ end
+
+ describe '#make_updated' do
+ subject { create(application_name, :updating) }
+
+ it 'is updated' do
+ subject.make_updated!
+
+ expect(subject).to be_updated
+ end
+
+ it 'updates helm version' do
+ subject.cluster.application_helm.update!(version: '1.2.3')
+
+ subject.make_updated!
+
+ subject.cluster.application_helm.reload
+
+ expect(subject.cluster.application_helm.version).to eq(Gitlab::Kubernetes::Helm::HELM_VERSION)
+ end
+ end
+
+ describe '#make_errored' do
+ subject { create(application_name, :installing) }
+ let(:reason) { 'some errors' }
+
+ it 'is errored' do
+ subject.make_errored(reason)
+
+ expect(subject).to be_errored
+ expect(subject.status_reason).to eq(reason)
+ end
+ end
+
+ describe '#make_scheduled' do
+ subject { create(application_name, :installable) }
+
+ it 'is scheduled' do
+ subject.make_scheduled
+
+ expect(subject).to be_scheduled
+ end
+
+ describe 'when was errored' do
+ subject { create(application_name, :errored) }
+
+ it 'clears #status_reason' do
+ expect(subject.status_reason).not_to be_nil
+
+ subject.make_scheduled!
+
+ expect(subject.status_reason).to be_nil
+ end
+ end
+ end
+ end
+
+ describe '#available?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:trait, :available) do
+ :not_installable | false
+ :installable | false
+ :scheduled | false
+ :installing | false
+ :installed | true
+ :updating | false
+ :updated | true
+ :errored | false
+ :update_errored | false
+ :timeouted | false
+ end
+
+ with_them do
+ subject { build(application_name, trait) }
+
+ if params[:available]
+ it { is_expected.to be_available }
+ else
+ it { is_expected.not_to be_available }
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/models/label_note_shared_examples.rb b/spec/support/shared_examples/models/label_note_shared_examples.rb
new file mode 100644
index 00000000000..406385c13bd
--- /dev/null
+++ b/spec/support/shared_examples/models/label_note_shared_examples.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+shared_examples 'label note created from events' do
+ def create_event(params = {})
+ event_params = { action: :add, label: label, user: user }
+ resource_key = resource.class.name.underscore.to_s
+ event_params[resource_key] = resource
+
+ build(:resource_label_event, event_params.merge(params))
+ end
+
+ def label_refs(events)
+ labels = events.map(&:label).compact
+
+ labels.map { |l| l.to_reference}.sort.join(' ')
+ end
+
+ let(:time) { Time.now }
+ let(:local_label_ids) { [label.id, label2.id] }
+
+ describe '.from_events' do
+ it 'returns system note with expected attributes' do
+ event = create_event
+
+ note = described_class.from_events([event, create_event])
+
+ expect(note.system).to be true
+ expect(note.author_id).to eq event.user_id
+ expect(note.discussion_id).to eq event.discussion_id
+ expect(note.noteable).to eq event.issuable
+ expect(note.note).to be_present
+ expect(note.note_html).to be_present
+ end
+
+ it 'updates markdown cache if reference is not set yet' do
+ event = create_event(reference: nil)
+
+ described_class.from_events([event])
+
+ expect(event.reference).not_to be_nil
+ end
+
+ it 'updates markdown cache if label was deleted' do
+ event = create_event(reference: 'some_ref', label: nil)
+
+ described_class.from_events([event])
+
+ expect(event.reference).to eq ''
+ end
+
+ it 'returns html note' do
+ events = [create_event(created_at: time)]
+
+ note = described_class.from_events(events)
+
+ expect(note.note_html).to include label.title
+ end
+
+ it 'returns text note for added labels' do
+ events = [create_event(created_at: time),
+ create_event(created_at: time, label: label2),
+ create_event(created_at: time, label: nil)]
+
+ note = described_class.from_events(events)
+
+ expect(note.note).to eq "added #{label_refs(events)} + 1 deleted label"
+ end
+
+ it 'returns text note for removed labels' do
+ events = [create_event(action: :remove, created_at: time),
+ create_event(action: :remove, created_at: time, label: label2),
+ create_event(action: :remove, created_at: time, label: nil)]
+
+ note = described_class.from_events(events)
+
+ expect(note.note).to eq "removed #{label_refs(events)} + 1 deleted label"
+ end
+
+ it 'returns text note for added and removed labels' do
+ add_events = [create_event(created_at: time),
+ create_event(created_at: time, label: nil)]
+
+ remove_events = [create_event(action: :remove, created_at: time),
+ create_event(action: :remove, created_at: time, label: nil)]
+
+ note = described_class.from_events(add_events + remove_events)
+
+ expect(note.note).to eq "added #{label_refs(add_events)} + 1 deleted label and removed #{label_refs(remove_events)} + 1 deleted label"
+ end
+
+ it 'returns text note for cross-project label' do
+ other_label = create(:label)
+ event = create_event(label: other_label)
+
+ note = described_class.from_events([event])
+
+ expect(note.note).to eq "added #{other_label.to_reference(resource_parent)} label"
+ end
+
+ it 'returns text note for cross-group label' do
+ other_label = create(:group_label)
+ event = create_event(label: other_label)
+
+ note = described_class.from_events([event])
+
+ expect(note.note).to eq "added #{other_label.to_reference(other_label.group, target_project: project, full: true)} label"
+ end
+ end
+end
diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb
index 5fb9ced3b63..66536e80db2 100644
--- a/spec/support/shared_examples/notify_shared_examples.rb
+++ b/spec/support/shared_examples/notify_shared_examples.rb
@@ -45,6 +45,20 @@ shared_examples 'an email that contains a header with author username' do
end
end
+shared_examples 'an email with X-GitLab headers containing IDs' do
+ it 'has X-GitLab-*-ID header' do
+ is_expected.to have_header "X-GitLab-#{model.class.name}-ID", "#{model.id}"
+ end
+
+ it 'has X-GitLab-*-IID header if model has iid defined' do
+ if model.respond_to?(:iid)
+ is_expected.to have_header "X-GitLab-#{model.class.name}-IID", "#{model.iid}"
+ else
+ expect(subject.header["X-GitLab-#{model.class.name}-IID"]).to eq nil
+ end
+ end
+end
+
shared_examples 'an email with X-GitLab headers containing project details' do
it 'has X-GitLab-Project headers' do
aggregate_failures do
@@ -69,6 +83,7 @@ end
shared_examples 'a thread answer email with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details'
+ include_examples 'an email with X-GitLab headers containing IDs'
it 'has the characteristics of a threaded reply' do
host = Gitlab.config.gitlab.host
@@ -85,6 +100,7 @@ end
shared_examples 'an email starting a new thread with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details'
+ include_examples 'an email with X-GitLab headers containing IDs'
include_examples 'a new thread email with reply-by-email enabled'
it 'includes "Reply to this email directly or <View it on GitLab>"' do
@@ -109,6 +125,7 @@ end
shared_examples 'an answer to an existing thread with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details'
+ include_examples 'an email with X-GitLab headers containing IDs'
include_examples 'a thread answer email with reply-by-email enabled'
context 'when reply-by-email is enabled with incoming address with %{key}' do
diff --git a/spec/support/shared_examples/requests/api/merge_requests_list.rb b/spec/support/shared_examples/requests/api/merge_requests_list.rb
index 1aed8ab0113..668a390b5d2 100644
--- a/spec/support/shared_examples/requests/api/merge_requests_list.rb
+++ b/spec/support/shared_examples/requests/api/merge_requests_list.rb
@@ -16,7 +16,12 @@ shared_examples 'merge requests list' do
create(:merge_request, state: 'closed', milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: 'Test', created_at: base_time)
- create(:merge_request, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: 'Test', created_at: base_time)
+ merge_request = create(:merge_request, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: 'Test', created_at: base_time)
+
+ merge_request.metrics.update!(merged_by: user,
+ latest_closed_by: user,
+ latest_closed_at: 1.hour.ago,
+ merged_at: 2.hours.ago)
expect do
get api(endpoint_path, user)
diff --git a/spec/support/shared_examples/requests/api/notes.rb b/spec/support/shared_examples/requests/api/notes.rb
index 1b563021244..0e20dfe0725 100644
--- a/spec/support/shared_examples/requests/api/notes.rb
+++ b/spec/support/shared_examples/requests/api/notes.rb
@@ -111,17 +111,79 @@ shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), body: 'hi!'
end
- context 'when an admin or owner makes the request' do
- it 'accepts the creation date to be set' do
- creation_time = 2.weeks.ago
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user),
- body: 'hi!', created_at: creation_time
+ context 'setting created_at' do
+ let(:creation_time) { 2.weeks.ago }
+ let(:params) { { body: 'hi!', created_at: creation_time } }
+
+ context 'by an admin' do
+ it 'sets the creation time on the new note' do
+ admin = create(:admin)
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", admin), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(admin.username)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time)
+ end
+ end
- expect(response).to have_gitlab_http_status(201)
- expect(json_response['body']).to eq('hi!')
- expect(json_response['author']['username']).to eq(user.username)
- expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
- expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time)
+ if parent_type == 'projects'
+ context 'by a project owner' do
+ it 'sets the creation time on the new note' do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(user.username)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time)
+ end
+ end
+
+ context 'by a group owner' do
+ it 'sets the creation time on the new note' do
+ user2 = create(:user)
+ group = create(:group)
+ group.add_owner(user2)
+ parent.update!(namespace: group)
+ user2.refresh_authorized_projects
+
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user2), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(user2.username)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time)
+ end
+ end
+ elsif parent_type == 'groups'
+ context 'by a group owner' do
+ it 'sets the creation time on the new note' do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(user.username)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time)
+ end
+ end
+ end
+
+ context 'by another user' do
+ it 'ignores the given creation time' do
+ user2 = create(:user)
+ parent.add_developer(user2)
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user2), params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(user2.username)
+ expect(Time.parse(json_response['created_at'])).not_to be_like_time(creation_time)
+ expect(Time.parse(json_response['updated_at'])).not_to be_like_time(creation_time)
+ end
end
end
diff --git a/spec/support/shared_examples/services/boards/issues_move_service.rb b/spec/support/shared_examples/services/boards/issues_move_service.rb
index 6d29a97c56d..ec44b99d10e 100644
--- a/spec/support/shared_examples/services/boards/issues_move_service.rb
+++ b/spec/support/shared_examples/services/boards/issues_move_service.rb
@@ -34,7 +34,7 @@ shared_examples 'issues move service' do |group|
described_class.new(parent, user, params).execute(issue)
issue.reload
- expect(issue.labels).to contain_exactly(bug)
+ expect(issue.labels).to contain_exactly(bug, regression)
expect(issue).to be_closed
end
end
diff --git a/spec/support/shared_examples/services/boards/lists_move_service.rb b/spec/support/shared_examples/services/boards/lists_move_service.rb
index 07c98cb29b7..2cdb968a45d 100644
--- a/spec/support/shared_examples/services/boards/lists_move_service.rb
+++ b/spec/support/shared_examples/services/boards/lists_move_service.rb
@@ -14,7 +14,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'keeps position of lists when new positon is equal to old position' do
+ it 'keeps position of lists when new position is equal to old position' do
service = described_class.new(parent, user, position: planning.position)
service.execute(planning)
@@ -22,7 +22,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'keeps position of lists when new positon is negative' do
+ it 'keeps position of lists when new position is negative' do
service = described_class.new(parent, user, position: -1)
service.execute(planning)
@@ -30,7 +30,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'keeps position of lists when new positon is equal to number of labels lists' do
+ it 'keeps position of lists when new position is equal to number of labels lists' do
service = described_class.new(parent, user, position: board.lists.label.size)
service.execute(planning)
@@ -38,7 +38,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'keeps position of lists when new positon is greater than number of labels lists' do
+ it 'keeps position of lists when new position is greater than number of labels lists' do
service = described_class.new(parent, user, position: board.lists.label.size + 1)
service.execute(planning)
@@ -46,7 +46,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'increments position of intermediate lists when new positon is equal to first position' do
+ it 'increments position of intermediate lists when new position is equal to first position' do
service = described_class.new(parent, user, position: 0)
service.execute(staging)
@@ -54,7 +54,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [1, 2, 3, 0]
end
- it 'decrements position of intermediate lists when new positon is equal to last position' do
+ it 'decrements position of intermediate lists when new position is equal to last position' do
service = described_class.new(parent, user, position: board.lists.label.last.position)
service.execute(planning)
diff --git a/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb b/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb
index 93c21a99e59..1190863d88e 100644
--- a/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb
+++ b/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb
@@ -33,6 +33,14 @@ shared_examples "builds correct paths" do |**patterns|
it_behaves_like "matches the method pattern", :upload_path
end
+ describe "#relative_path" do
+ it 'is relative' do
+ skip 'Path not set, skipping.' unless subject.path
+
+ expect(Pathname.new(subject.relative_path)).to be_relative
+ end
+ end
+
describe ".absolute_path" do
it_behaves_like "matches the method pattern", :absolute_path do
let(:target) { subject.class }
diff --git a/spec/support/shared_examples/wiki_file_attachments_examples.rb b/spec/support/shared_examples/wiki_file_attachments_examples.rb
new file mode 100644
index 00000000000..b6fb2a66b0e
--- /dev/null
+++ b/spec/support/shared_examples/wiki_file_attachments_examples.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+# Requires a context containing:
+# project
+
+shared_examples 'wiki file attachments' do
+ include DropzoneHelper
+
+ context 'uploading attachments', :js do
+ let(:wiki) { project.wiki }
+
+ def attach_with_dropzone(wait = false)
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, wait)
+ end
+
+ context 'before uploading' do
+ it 'shows "Attach a file" button' do
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
+ end
+
+ context 'uploading is in progress' do
+ it 'cancels uploading on clicking to "Cancel" button' do
+ slow_requests do
+ attach_with_dropzone
+
+ click_button 'Cancel'
+ end
+
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_button('Cancel')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
+
+ it 'shows "Attaching a file" message on uploading 1 file' do
+ slow_requests do
+ attach_with_dropzone
+
+ expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching a file -')
+ end
+ end
+ end
+
+ context 'uploading is complete' do
+ it 'shows "Attach a file" button on uploading complete' do
+ attach_with_dropzone
+ wait_for_requests
+
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
+
+ it 'the markdown link is added to the page' do
+ fill_in(:wiki_content, with: '')
+ attach_with_dropzone(true)
+ wait_for_requests
+
+ expect(page.find('#wiki_content').value)
+ .to match(%r{\!\[dk\]\(uploads/\h{32}/dk\.png\)$})
+ end
+
+ it 'the links point to the wiki root url' do
+ attach_with_dropzone(true)
+ wait_for_requests
+
+ find('.js-md-preview-button').click
+ file_path = page.find('input[name="files[]"]', visible: :hidden).value
+ link = page.find('a.no-attachment-icon')['href']
+ img_link = page.find('a.no-attachment-icon img')['src']
+
+ expect(link).to eq img_link
+ expect(URI.parse(link).path).to eq File.join(wiki.wiki_base_path, file_path)
+ end
+
+ it 'the file has been added to the wiki repository' do
+ expect do
+ attach_with_dropzone(true)
+ wait_for_requests
+ end.to change { wiki.repository.ls_files('HEAD').count }.by(1)
+
+ file_path = page.find('input[name="files[]"]', visible: :hidden).value
+
+ expect(wiki.find_file(file_path, 'HEAD').path).not_to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb
index d143014692d..6c4e11910d3 100644
--- a/spec/support/sidekiq.rb
+++ b/spec/support/sidekiq.rb
@@ -1,7 +1,27 @@
require 'sidekiq/testing/inline'
+# If Sidekiq::Testing.inline! is used, SQL transactions done inside
+# Sidekiq worker are included in the SQL query limit (in a real
+# deployment sidekiq worker is executed separately). To avoid
+# increasing SQL limit counter, the request is marked as whitelisted
+# during Sidekiq block
+class DisableQueryLimit
+ def call(worker_instance, msg, queue)
+ transaction = Gitlab::QueryLimiting::Transaction.current
+
+ if !transaction.respond_to?(:whitelisted) || transaction.whitelisted
+ yield
+ else
+ transaction.whitelisted = true
+ yield
+ transaction.whitelisted = false
+ end
+ end
+end
+
Sidekiq::Testing.server_middleware do |chain|
chain.add Gitlab::SidekiqStatus::ServerMiddleware
+ chain.add DisableQueryLimit
end
RSpec.configure do |config|
diff --git a/spec/support/stored_repositories.rb b/spec/support/stored_repositories.rb
index 26f823cb6ef..55212355daa 100644
--- a/spec/support/stored_repositories.rb
+++ b/spec/support/stored_repositories.rb
@@ -1,30 +1,7 @@
RSpec.configure do |config|
- config.before(:all, :broken_storage) do
- FileUtils.rm_rf Gitlab.config.repositories.storages.broken.legacy_disk_path
- end
-
config.before(:each, :broken_storage) do
allow(Gitlab::GitalyClient).to receive(:call) do
raise GRPC::Unavailable.new('Gitaly broken in this spec')
end
-
- # Track the maximum number of failures
- first_failure = Time.parse("2017-11-14 17:52:30")
- last_failure = Time.parse("2017-11-14 18:54:37")
- failure_count = Gitlab::CurrentSettings.circuitbreaker_failure_count_threshold + 1
- cache_key = "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}broken:#{Gitlab::Environment.hostname}"
-
- Gitlab::Git::Storage.redis.with do |redis|
- redis.pipelined do
- redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key)
- redis.hset(cache_key, :first_failure, first_failure.to_i)
- redis.hset(cache_key, :last_failure, last_failure.to_i)
- redis.hset(cache_key, :failure_count, failure_count.to_i)
- end
- end
- end
-
- config.after(:each, :broken_storage) do
- Gitlab::Git::Storage.redis.with(&:flushall)
end
end
diff --git a/spec/support/stub_version.rb b/spec/support/stub_version.rb
new file mode 100644
index 00000000000..594ab64e7c6
--- /dev/null
+++ b/spec/support/stub_version.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module StubVersion
+ def stub_version(version, revision)
+ stub_const('Gitlab::VERSION', version)
+ allow(Gitlab).to receive(:revision).and_return(revision)
+ end
+end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 3ba6caf1337..8c4360d4cf0 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -251,7 +251,7 @@ describe 'gitlab:app namespace rake task' do
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
- # Avoid asking gitaly about the root ref (which will fail beacuse of the
+ # Avoid asking gitaly about the root ref (which will fail because of the
# mocked storages)
allow_any_instance_of(Repository).to receive(:empty?).and_return(false)
end
diff --git a/spec/tasks/gitlab/cleanup_rake_spec.rb b/spec/tasks/gitlab/cleanup_rake_spec.rb
index cc2cca10f58..19794227d9f 100644
--- a/spec/tasks/gitlab/cleanup_rake_spec.rb
+++ b/spec/tasks/gitlab/cleanup_rake_spec.rb
@@ -6,6 +6,8 @@ describe 'gitlab:cleanup rake tasks' do
end
describe 'cleanup namespaces and repos' do
+ let(:gitlab_shell) { Gitlab::Shell.new }
+ let(:storage) { storages.keys.first }
let(:storages) do
{
'default' => Gitlab::GitalyClient::StorageSettings.new(@default_storage_hash.merge('path' => 'tmp/tests/default_storage'))
@@ -17,53 +19,56 @@ describe 'gitlab:cleanup rake tasks' do
end
before do
- FileUtils.mkdir(Settings.absolute('tmp/tests/default_storage'))
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
end
after do
- FileUtils.rm_rf(Settings.absolute('tmp/tests/default_storage'))
+ Gitlab::GitalyClient::StorageService.new(storage).delete_all_repositories
end
describe 'cleanup:repos' do
before do
- FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/broken/project.git'))
- FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/12/34/5678.git'))
+ gitlab_shell.add_namespace(storage, 'broken/project.git')
+ gitlab_shell.add_namespace(storage, '@hashed/12/34/5678.git')
end
it 'moves it to an orphaned path' do
- run_rake_task('gitlab:cleanup:repos')
- repo_list = Dir['tmp/tests/default_storage/broken/*']
+ now = Time.now
+
+ Timecop.freeze(now) do
+ run_rake_task('gitlab:cleanup:repos')
+ repo_list = Gitlab::GitalyClient::StorageService.new(storage).list_directories(depth: 0)
- expect(repo_list.first).to include('+orphaned+')
+ expect(repo_list.last).to include("broken+orphaned+#{now.to_i}")
+ end
end
it 'ignores @hashed repos' do
run_rake_task('gitlab:cleanup:repos')
- expect(Dir.exist?(Settings.absolute('tmp/tests/default_storage/@hashed/12/34/5678.git'))).to be_truthy
+ expect(gitlab_shell.exists?(storage, '@hashed/12/34/5678.git')).to be(true)
end
end
describe 'cleanup:dirs' do
it 'removes missing namespaces' do
- FileUtils.mkdir_p(Settings.absolute("tmp/tests/default_storage/namespace_1/project.git"))
- FileUtils.mkdir_p(Settings.absolute("tmp/tests/default_storage/namespace_2/project.git"))
- allow(Namespace).to receive(:pluck).and_return('namespace_1')
+ gitlab_shell.add_namespace(storage, "namespace_1/project.git")
+ gitlab_shell.add_namespace(storage, "namespace_2/project.git")
+ allow(Namespace).to receive(:pluck).and_return(['namespace_1'])
stub_env('REMOVE', 'true')
run_rake_task('gitlab:cleanup:dirs')
- expect(Dir.exist?(Settings.absolute('tmp/tests/default_storage/namespace_1'))).to be_truthy
- expect(Dir.exist?(Settings.absolute('tmp/tests/default_storage/namespace_2'))).to be_falsey
+ expect(gitlab_shell.exists?(storage, 'namespace_1')).to be(true)
+ expect(gitlab_shell.exists?(storage, 'namespace_2')).to be(false)
end
it 'ignores @hashed directory' do
- FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/12/34/5678.git'))
+ gitlab_shell.add_namespace(storage, '@hashed/12/34/5678.git')
run_rake_task('gitlab:cleanup:dirs')
- expect(Dir.exist?(Settings.absolute('tmp/tests/default_storage/@hashed/12/34/5678.git'))).to be_truthy
+ expect(gitlab_shell.exists?(storage, '@hashed/12/34/5678.git')).to be(true)
end
end
end
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index b81aea23306..5818892d56a 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -61,6 +61,39 @@ describe 'gitlab:db namespace rake task' do
expect(Rake::Task['db:migrate']).not_to receive(:invoke)
expect { run_rake_task('gitlab:db:configure') }.to raise_error(RuntimeError, 'error')
end
+
+ context 'SKIP_POST_DEPLOYMENT_MIGRATIONS environment variable set' do
+ let(:rails_paths) { { 'db' => ['db'], 'db/migrate' => ['db/migrate'] } }
+
+ before do
+ allow(ENV).to receive(:[]).and_call_original
+ allow(ENV).to receive(:[]).with('SKIP_POST_DEPLOYMENT_MIGRATIONS').and_return true
+
+ # Our environment has already been loaded, so we need to pretend like post_migrations were not
+ allow(Rails.application.config).to receive(:paths).and_return(rails_paths)
+ allow(ActiveRecord::Migrator).to receive(:migrations_paths).and_return(rails_paths['db/migrate'].dup)
+ end
+
+ it 'adds post deployment migrations before schema load if the schema is not already loaded' do
+ allow(ActiveRecord::Base.connection).to receive(:tables).and_return([])
+ expect(Gitlab::Database).to receive(:add_post_migrate_path_to_rails).and_call_original
+ expect(Rake::Task['db:schema:load']).to receive(:invoke)
+ expect(Rake::Task['db:seed_fu']).to receive(:invoke)
+ expect(Rake::Task['db:migrate']).not_to receive(:invoke)
+ expect { run_rake_task('gitlab:db:configure') }.not_to raise_error
+ expect(rails_paths['db/migrate'].include?(File.join(Rails.root, 'db', 'post_migrate'))).to be(true)
+ end
+
+ it 'ignores post deployment migrations when schema has already been loaded' do
+ allow(ActiveRecord::Base.connection).to receive(:tables).and_return(%w[table1 table2])
+ expect(Rake::Task['db:migrate']).to receive(:invoke)
+ expect(Gitlab::Database).not_to receive(:add_post_migrate_path_to_rails)
+ expect(Rake::Task['db:schema:load']).not_to receive(:invoke)
+ expect(Rake::Task['db:seed_fu']).not_to receive(:invoke)
+ expect { run_rake_task('gitlab:db:configure') }.not_to raise_error
+ expect(rails_paths['db/migrate'].include?(File.join(Rails.root, 'db', 'post_migrate'))).to be(false)
+ end
+ end
end
def run_rake_task(task_name)
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index 4545226d78c..e6e4d9504d9 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -8,13 +8,23 @@ describe 'gitlab:gitaly namespace rake task' do
describe 'install' do
let(:repo) { 'https://gitlab.com/gitlab-org/gitaly.git' }
let(:clone_path) { Rails.root.join('tmp/tests/gitaly').to_s }
+ let(:storage_path) { Rails.root.join('tmp/tests/repositories').to_s }
let(:version) { File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp }
+ subject { run_rake_task('gitlab:gitaly:install', clone_path, storage_path) }
+
context 'no dir given' do
it 'aborts and display a help message' do
# avoid writing task output to spec progress
allow($stderr).to receive :write
- expect { run_rake_task('gitlab:gitaly:install') }.to raise_error /Please specify the directory where you want to install gitaly/
+ expect { run_rake_task('gitlab:gitaly:install') }.to raise_error /Please specify the directory where you want to install gitaly and the path for the default storage/
+ end
+ end
+
+ context 'no storage path given' do
+ it 'aborts and display a help message' do
+ allow($stderr).to receive :write
+ expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error /Please specify the directory where you want to install gitaly and the path for the default storage/
end
end
@@ -23,7 +33,7 @@ describe 'gitlab:gitaly namespace rake task' do
expect(main_object)
.to receive(:checkout_or_clone_version).and_raise 'Git error'
- expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error 'Git error'
+ expect { subject }.to raise_error 'Git error'
end
end
@@ -36,7 +46,7 @@ describe 'gitlab:gitaly namespace rake task' do
expect(main_object)
.to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
- run_rake_task('gitlab:gitaly:install', clone_path)
+ subject
end
end
@@ -59,7 +69,7 @@ describe 'gitlab:gitaly namespace rake task' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
expect(main_object).to receive(:run_command!).with(command_preamble + %w[gmake]).and_return(true)
- run_rake_task('gitlab:gitaly:install', clone_path)
+ subject
end
end
@@ -72,7 +82,7 @@ describe 'gitlab:gitaly namespace rake task' do
it 'calls make in the gitaly directory' do
expect(main_object).to receive(:run_command!).with(command_preamble + %w[make]).and_return(true)
- run_rake_task('gitlab:gitaly:install', clone_path)
+ subject
end
context 'when Rails.env is test' do
@@ -89,55 +99,10 @@ describe 'gitlab:gitaly namespace rake task' do
it 'calls make in the gitaly directory with --no-deployment flag for bundle' do
expect(main_object).to receive(:run_command!).with(command_preamble + command).and_return(true)
- run_rake_task('gitlab:gitaly:install', clone_path)
+ subject
end
end
end
end
end
-
- describe 'storage_config' do
- it 'prints storage configuration in a TOML format' do
- config = {
- 'default' => Gitlab::GitalyClient::StorageSettings.new(
- 'path' => '/path/to/default',
- 'gitaly_address' => 'unix:/path/to/my.socket'
- ),
- 'nfs_01' => Gitlab::GitalyClient::StorageSettings.new(
- 'path' => '/path/to/nfs_01',
- 'gitaly_address' => 'unix:/path/to/my.socket'
- )
- }
- allow(Gitlab.config.repositories).to receive(:storages).and_return(config)
- allow(Rails.env).to receive(:test?).and_return(false)
-
- expected_output = ''
- Timecop.freeze do
- expected_output = <<~TOML
- # Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}
- # This is in TOML format suitable for use in Gitaly's config.toml file.
- bin_dir = "tmp/tests/gitaly"
- socket_path = "/path/to/my.socket"
- [gitlab-shell]
- dir = "#{Gitlab.config.gitlab_shell.path}"
- [[storage]]
- name = "default"
- path = "/path/to/default"
- [[storage]]
- name = "nfs_01"
- path = "/path/to/nfs_01"
- TOML
- end
-
- expect { run_rake_task('gitlab:gitaly:storage_config')}
- .to output(expected_output).to_stdout
-
- parsed_output = TomlRB.parse(expected_output)
- config.each do |name, params|
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- expect(parsed_output['storage']).to include({ 'name' => name, 'path' => params.legacy_disk_path })
- end
- end
- end
- end
end
diff --git a/spec/tasks/gitlab/site_statistics_rake_spec.rb b/spec/tasks/gitlab/site_statistics_rake_spec.rb
new file mode 100644
index 00000000000..c43ce25a540
--- /dev/null
+++ b/spec/tasks/gitlab/site_statistics_rake_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+require 'rake_helper'
+
+describe 'rake gitlab:refresh_site_statistics' do
+ before do
+ Rake.application.rake_require 'tasks/gitlab/site_statistics'
+
+ create(:project)
+ SiteStatistic.fetch.update(repositories_count: 0)
+ end
+
+ let(:task) { 'gitlab:refresh_site_statistics' }
+
+ it 'recalculates existing counters' do
+ run_rake_task(task)
+
+ expect(SiteStatistic.fetch.repositories_count).to eq(1)
+ end
+
+ it 'displays message listing counters' do
+ expect { run_rake_task(task) }.to output(/Updating Site Statistics counters:.* Repositories\.\.\. OK!/m).to_stdout
+ end
+end
diff --git a/spec/tasks/gitlab/traces_rake_spec.rb b/spec/tasks/gitlab/traces_rake_spec.rb
index bd18e8ffc1e..aaf0d7242dd 100644
--- a/spec/tasks/gitlab/traces_rake_spec.rb
+++ b/spec/tasks/gitlab/traces_rake_spec.rb
@@ -5,51 +5,109 @@ describe 'gitlab:traces rake tasks' do
Rake.application.rake_require 'tasks/gitlab/traces'
end
- shared_examples 'passes the job id to worker' do
- it do
- expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[job.id]])
+ describe 'gitlab:traces:archive' do
+ shared_examples 'passes the job id to worker' do
+ it do
+ expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[job.id]])
- run_rake_task('gitlab:traces:archive')
+ run_rake_task('gitlab:traces:archive')
+ end
end
- end
- shared_examples 'does not pass the job id to worker' do
- it do
- expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async)
+ shared_examples 'does not pass the job id to worker' do
+ it do
+ expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async)
- run_rake_task('gitlab:traces:archive')
+ run_rake_task('gitlab:traces:archive')
+ end
end
- end
- context 'when trace file stored in default path' do
- let!(:job) { create(:ci_build, :success, :trace_live) }
+ context 'when trace file stored in default path' do
+ let!(:job) { create(:ci_build, :success, :trace_live) }
- it_behaves_like 'passes the job id to worker'
- end
+ it_behaves_like 'passes the job id to worker'
+ end
- context 'when trace is stored in database' do
- let!(:job) { create(:ci_build, :success) }
+ context 'when trace is stored in database' do
+ let!(:job) { create(:ci_build, :success) }
- before do
- job.update_column(:trace, 'trace in db')
+ before do
+ job.update_column(:trace, 'trace in db')
+ end
+
+ it_behaves_like 'passes the job id to worker'
end
- it_behaves_like 'passes the job id to worker'
+ context 'when job has trace artifact' do
+ let!(:job) { create(:ci_build, :success) }
+
+ before do
+ create(:ci_job_artifact, :trace, job: job)
+ end
+
+ it_behaves_like 'does not pass the job id to worker'
+ end
+
+ context 'when job is not finished yet' do
+ let!(:build) { create(:ci_build, :running, :trace_live) }
+
+ it_behaves_like 'does not pass the job id to worker'
+ end
end
- context 'when job has trace artifact' do
- let!(:job) { create(:ci_build, :success) }
+ describe 'gitlab:traces:migrate' do
+ let(:object_storage_enabled) { false }
before do
- create(:ci_job_artifact, :trace, job: job)
+ stub_artifacts_object_storage(enabled: object_storage_enabled)
end
- it_behaves_like 'does not pass the job id to worker'
- end
+ subject { run_rake_task('gitlab:traces:migrate') }
- context 'when job is not finished yet' do
- let!(:build) { create(:ci_build, :running, :trace_live) }
+ let!(:job_trace) { create(:ci_job_artifact, :trace, file_store: store) }
- it_behaves_like 'does not pass the job id to worker'
+ context 'when local storage is used' do
+ let(:store) { ObjectStorage::Store::LOCAL }
+
+ context 'and job does not have file store defined' do
+ let(:object_storage_enabled) { true }
+ let(:store) { nil }
+
+ it "migrates file to remote storage" do
+ subject
+
+ expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE)
+ end
+ end
+
+ context 'and remote storage is defined' do
+ let(:object_storage_enabled) { true }
+
+ it "migrates file to remote storage" do
+ subject
+
+ expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE)
+ end
+ end
+
+ context 'and remote storage is not defined' do
+ it "fails to migrate to remote storage" do
+ subject
+
+ expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::LOCAL)
+ end
+ end
+ end
+
+ context 'when remote storage is used' do
+ let(:object_storage_enabled) { true }
+ let(:store) { ObjectStorage::Store::REMOTE }
+
+ it "file stays on remote storage" do
+ subject
+
+ expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE)
+ end
+ end
end
end
diff --git a/spec/tasks/gitlab/uploads/migrate_rake_spec.rb b/spec/tasks/gitlab/uploads/migrate_rake_spec.rb
index 6fcfae358ec..9588e8be5dc 100644
--- a/spec/tasks/gitlab/uploads/migrate_rake_spec.rb
+++ b/spec/tasks/gitlab/uploads/migrate_rake_spec.rb
@@ -38,14 +38,6 @@ describe 'gitlab:uploads:migrate rake tasks' do
let!(:projects) { create_list(:project, 10, :with_avatar) }
it_behaves_like 'enqueue jobs in batch', batch: 4
-
- context 'Upload has store = nil' do
- before do
- Upload.where(model: projects).update_all(store: nil)
- end
-
- it_behaves_like 'enqueue jobs in batch', batch: 4
- end
end
context "for Group" do
diff --git a/spec/unicorn/unicorn_spec.rb b/spec/unicorn/unicorn_spec.rb
deleted file mode 100644
index a4cf479a339..00000000000
--- a/spec/unicorn/unicorn_spec.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-require 'fileutils'
-
-require 'excon'
-
-require 'spec_helper'
-
-describe 'Unicorn' do
- before(:all) do
- config_lines = File.read('config/unicorn.rb.example').split("\n")
-
- # Remove these because they make setup harder.
- config_lines = config_lines.reject do |line|
- %w[
- working_directory
- worker_processes
- listen
- pid
- stderr_path
- stdout_path
- ].any? { |prefix| line.start_with?(prefix) }
- end
-
- config_lines << "working_directory '#{Rails.root}'"
-
- # We want to have exactly 1 worker process because that makes it
- # predictable which process will handle our requests.
- config_lines << 'worker_processes 1'
-
- @socket_path = File.join(Dir.pwd, 'tmp/tests/unicorn.socket')
- config_lines << "listen '#{@socket_path}'"
-
- ready_file = 'tmp/tests/unicorn-worker-ready'
- FileUtils.rm_f(ready_file)
- after_fork_index = config_lines.index { |l| l.start_with?('after_fork') }
- config_lines.insert(after_fork_index + 1, "File.write('#{ready_file}', Process.pid)")
-
- config_path = 'tmp/tests/unicorn.rb'
- File.write(config_path, config_lines.join("\n") + "\n")
-
- rackup_path = 'tmp/tests/config.ru'
- File.write(rackup_path, <<~EOS)
- app =
- proc do |env|
- if env['REQUEST_METHOD'] == 'GET'
- [200, {}, [Process.pid]]
- else
- Process.kill(env['QUERY_STRING'], Process.pid)
- [200, {}, ['Bye!']]
- end
- end
-
- run app
- EOS
-
- cmd = %W[unicorn -E test -c #{config_path} #{rackup_path}]
- @unicorn_master_pid = spawn(*cmd)
- wait_unicorn_boot!(@unicorn_master_pid, ready_file)
- WebMock.allow_net_connect!
- end
-
- %w[SIGQUIT SIGTERM SIGKILL].each do |signal|
- it "has a worker that self-terminates on signal #{signal}" do
- response = Excon.get('unix://', socket: @socket_path)
- expect(response.status).to eq(200)
-
- worker_pid = response.body.to_i
- expect(worker_pid).to be > 0
-
- begin
- Excon.post("unix://?#{signal}", socket: @socket_path)
- rescue Excon::Error::Socket
- # The connection may be closed abruptly
- end
-
- expect(pid_gone?(worker_pid)).to eq(true)
- end
- end
-
- after(:all) do
- WebMock.disable_net_connect!(allow_localhost: true)
- Process.kill('TERM', @unicorn_master_pid)
- end
-
- def wait_unicorn_boot!(master_pid, ready_file)
- # We have seen the boot timeout after 2 minutes in CI so let's set it to 5 minutes.
- timeout = 5 * 60
- timeout.times do
- return if File.exist?(ready_file)
-
- pid = Process.waitpid(master_pid, Process::WNOHANG)
- raise "unicorn failed to boot: #{$?}" unless pid.nil?
-
- sleep 1
- end
-
- raise "unicorn boot timed out after #{timeout} seconds"
- end
-
- def pid_gone?(pid)
- # Worker termination should take less than a second. That makes 10
- # seconds a generous timeout.
- 10.times do
- begin
- Process.kill(0, pid)
- rescue Errno::ESRCH
- return true
- end
-
- sleep 1
- end
-
- false
- end
-end
diff --git a/spec/uploaders/avatar_uploader_spec.rb b/spec/uploaders/avatar_uploader_spec.rb
index b0468bc35ff..6aaec7a4fef 100644
--- a/spec/uploaders/avatar_uploader_spec.rb
+++ b/spec/uploaders/avatar_uploader_spec.rb
@@ -35,5 +35,13 @@ describe AvatarUploader do
it_behaves_like "migrates", to_store: described_class::Store::REMOTE
it_behaves_like "migrates", from_store: described_class::Store::REMOTE, to_store: described_class::Store::LOCAL
+
+ it 'sets the right absolute path' do
+ storage_path = Gitlab.config.uploads.storage_path
+ absolute_path = File.join(storage_path, upload.path)
+
+ expect(uploader.absolute_path.scan(storage_path).size).to eq(1)
+ expect(uploader.absolute_path).to eq(absolute_path)
+ end
end
end
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index 7e24efda5dd..c74e0bf1955 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -81,19 +81,24 @@ describe FileUploader do
end
describe 'copy_to' do
+ let(:new_project) { create(:project) }
+ let(:moved) { described_class.copy_to(subject, new_project) }
+
shared_examples 'returns a valid uploader' do
describe 'returned uploader' do
- let(:new_project) { create(:project) }
- let(:moved) { described_class.copy_to(subject, new_project) }
-
it 'generates a new secret' do
expect(subject).to be
expect(described_class).to receive(:generate_secret).once.and_call_original
expect(moved).to be
end
- it 'create new upload' do
- expect(moved.upload).not_to eq(subject.upload)
+ it 'creates new upload correctly' do
+ upload = moved.upload
+
+ expect(upload).not_to eq(subject.upload)
+ expect(upload.model).to eq(new_project)
+ expect(upload.uploader).to eq('FileUploader')
+ expect(upload.secret).not_to eq(subject.upload.secret)
end
it 'copies the file' do
@@ -111,6 +116,12 @@ describe FileUploader do
end
include_examples 'returns a valid uploader'
+
+ it 'copies the file to the correct location' do
+ expect(moved.upload.path).to eq("#{moved.upload.secret}/dk.png")
+ expect(moved.file.path).to end_with("public/uploads/#{new_project.disk_path}/#{moved.upload.secret}/dk.png")
+ expect(moved.filename).to eq('dk.png')
+ end
end
context 'files are stored remotely' do
@@ -121,6 +132,12 @@ describe FileUploader do
end
include_examples 'returns a valid uploader'
+
+ it 'copies the file to the correct location' do
+ expect(moved.upload.path).to eq("#{new_project.disk_path}/#{moved.upload.secret}/dk.png")
+ expect(moved.file.path).to eq("#{new_project.disk_path}/#{moved.upload.secret}/dk.png")
+ expect(moved.filename).to eq('dk.png')
+ end
end
end
diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb
index 3ad5fe7e3b3..061432f082a 100644
--- a/spec/uploaders/job_artifact_uploader_spec.rb
+++ b/spec/uploaders/job_artifact_uploader_spec.rb
@@ -40,6 +40,53 @@ describe JobArtifactUploader do
it { is_expected.to end_with("ci_build_artifacts.zip") }
end
+ describe '#dynamic_segment' do
+ let(:uploaded_content) { File.binread(Rails.root + 'spec/fixtures/ci_build_artifacts.zip') }
+ let(:model) { uploader.model }
+
+ shared_examples_for 'Read file from legacy path' do
+ it 'store_path returns the legacy path' do
+ expect(model.file.store_path).to eq(File.join(model.created_at.utc.strftime('%Y_%m'), model.project_id.to_s, model.job_id.to_s, 'ci_build_artifacts.zip'))
+ end
+
+ it 'has exactly the same content' do
+ expect(::File.binread(model.file.path)).to eq(uploaded_content)
+ end
+ end
+
+ shared_examples_for 'Read file from hashed path' do
+ it 'store_path returns hashed path' do
+ expect(model.file.store_path).to eq(File.join(disk_hash[0..1], disk_hash[2..3], disk_hash, creation_date, model.job_id.to_s, model.id.to_s, 'ci_build_artifacts.zip'))
+ end
+
+ it 'has exactly the same content' do
+ expect(::File.binread(model.file.path)).to eq(uploaded_content)
+ end
+ end
+
+ context 'when a job artifact is stored in legacy_path' do
+ let(:job_artifact) { create(:ci_job_artifact, :legacy_archive) }
+
+ it_behaves_like 'Read file from legacy path'
+ end
+
+ context 'when the artifact file is stored in hashed_path' do
+ let(:job_artifact) { create(:ci_job_artifact, :archive) }
+ let(:disk_hash) { Digest::SHA2.hexdigest(model.project_id.to_s) }
+ let(:creation_date) { model.created_at.utc.strftime('%Y_%m_%d') }
+
+ it_behaves_like 'Read file from hashed path'
+
+ context 'when file_location column is empty' do
+ before do
+ job_artifact.update_column(:file_location, nil)
+ end
+
+ it_behaves_like 'Read file from hashed path'
+ end
+ end
+ end
+
describe "#migrate!" do
before do
uploader.store!(fixture_file_upload('spec/fixtures/trace/sample_trace'))
diff --git a/spec/uploaders/namespace_file_uploader_spec.rb b/spec/uploaders/namespace_file_uploader_spec.rb
index 71fe2c353c0..d09725ee4be 100644
--- a/spec/uploaders/namespace_file_uploader_spec.rb
+++ b/spec/uploaders/namespace_file_uploader_spec.rb
@@ -26,6 +26,26 @@ describe NamespaceFileUploader do
upload_path: IDENTIFIER
end
+ context '.base_dir' do
+ it 'returns local storage base_dir without store param' do
+ expect(described_class.base_dir(group)).to eq("uploads/-/system/namespace/#{group.id}")
+ end
+
+ it 'returns local storage base_dir when store param is Store::LOCAL' do
+ expect(described_class.base_dir(group, ObjectStorage::Store::LOCAL)).to eq("uploads/-/system/namespace/#{group.id}")
+ end
+
+ it 'returns remote base_dir when store param is Store::REMOTE' do
+ expect(described_class.base_dir(group, ObjectStorage::Store::REMOTE)).to eq("namespace/#{group.id}")
+ end
+ end
+
+ describe '#workhorse_local_upload_path' do
+ it 'returns the correct path in uploads directory' do
+ expect(described_class.workhorse_local_upload_path).to end_with('/uploads/tmp/uploads')
+ end
+ end
+
describe "#migrate!" do
before do
uploader.store!(fixture_file_upload(File.join('spec/fixtures/doc_sample.txt')))
@@ -35,4 +55,62 @@ describe NamespaceFileUploader do
it_behaves_like "migrates", to_store: described_class::Store::REMOTE
it_behaves_like "migrates", from_store: described_class::Store::REMOTE, to_store: described_class::Store::LOCAL
end
+
+ describe 'copy_to' do
+ let(:group) { create(:group) }
+ let(:moved) { described_class.copy_to(subject, group) }
+
+ shared_examples 'returns a valid uploader' do
+ it 'generates a new secret' do
+ expect(subject).to be
+ expect(described_class).to receive(:generate_secret).once.and_call_original
+ expect(moved).to be
+ end
+
+ it 'creates new upload correctly' do
+ upload = moved.upload
+
+ expect(upload).not_to eq(subject.upload)
+ expect(upload.model).to eq(group)
+ expect(upload.uploader).to eq('NamespaceFileUploader')
+ expect(upload.secret).not_to eq(subject.upload.secret)
+ end
+
+ it 'copies the file' do
+ expect(subject.file).to exist
+ expect(moved.file).to exist
+ expect(subject.file).not_to eq(moved.file)
+ expect(subject.object_store).to eq(moved.object_store)
+ end
+ end
+
+ context 'files are stored locally' do
+ before do
+ subject.store!(fixture_file_upload('spec/fixtures/dk.png'))
+ end
+
+ include_examples 'returns a valid uploader'
+
+ it 'copies the file to the correct location' do
+ expect(moved.upload.path).to eq("#{moved.upload.secret}/dk.png")
+ expect(moved.file.path).to end_with("system/namespace/#{group.id}/#{moved.upload.secret}/dk.png")
+ expect(moved.filename).to eq('dk.png')
+ end
+ end
+
+ context 'files are stored remotely' do
+ before do
+ stub_uploads_object_storage
+ subject.store!(fixture_file_upload('spec/fixtures/dk.png'))
+ subject.migrate!(ObjectStorage::Store::REMOTE)
+ end
+
+ include_examples 'returns a valid uploader'
+
+ it 'copies the file to the correct location' do
+ expect(moved.file.path).to eq("namespace/#{group.id}/#{moved.upload.secret}/dk.png")
+ expect(moved.filename).to eq('dk.png')
+ end
+ end
+ end
end
diff --git a/spec/uploaders/uploader_helper_spec.rb b/spec/uploaders/uploader_helper_spec.rb
index 33da93cc9d0..fd6712d4645 100644
--- a/spec/uploaders/uploader_helper_spec.rb
+++ b/spec/uploaders/uploader_helper_spec.rb
@@ -11,27 +11,10 @@ describe UploaderHelper do
example_uploader.new
end
- def upload_fixture(filename)
- fixture_file_upload(File.join('spec', 'fixtures', filename))
- end
-
- describe '#image_or_video?' do
- it 'returns true for an image file' do
- uploader.store!(upload_fixture('dk.png'))
-
- expect(uploader).to be_image_or_video
- end
-
- it 'it returns true for a video file' do
- uploader.store!(upload_fixture('video_sample.mp4'))
-
- expect(uploader).to be_image_or_video
- end
-
- it 'returns false for other extensions' do
- uploader.store!(upload_fixture('doc_sample.txt'))
-
- expect(uploader).not_to be_image_or_video
+ describe '#extension_match?' do
+ it 'returns false if file does not exists' do
+ expect(uploader.file).to be_nil
+ expect(uploader.send(:extension_match?, 'jpg')).to eq false
end
end
end
diff --git a/spec/validators/branch_filter_validator_spec.rb b/spec/validators/branch_filter_validator_spec.rb
new file mode 100644
index 00000000000..3be54827431
--- /dev/null
+++ b/spec/validators/branch_filter_validator_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe BranchFilterValidator do
+ let(:validator) { described_class.new(attributes: [:push_events_branch_filter]) }
+ let(:hook) { build(:project_hook) }
+
+ describe '#validates_each' do
+ it 'allows valid branch names' do
+ validator.validate_each(hook, :push_events_branch_filter, "good_branch_name")
+ validator.validate_each(hook, :push_events_branch_filter, "another/good_branch_name")
+ expect(hook.errors.empty?).to be true
+ end
+
+ it 'disallows bad branch names' do
+ validator.validate_each(hook, :push_events_branch_filter, "bad branch~name")
+ expect(hook.errors[:push_events_branch_filter].empty?).to be false
+ end
+
+ it 'allows wildcards' do
+ validator.validate_each(hook, :push_events_branch_filter, "features/*")
+ validator.validate_each(hook, :push_events_branch_filter, "features/*/bla")
+ validator.validate_each(hook, :push_events_branch_filter, "*-stable")
+ expect(hook.errors.empty?).to be true
+ end
+
+ it 'gets rid of whitespace' do
+ filter = ' master '
+ validator.validate_each(hook, :push_events_branch_filter, filter)
+
+ expect(filter).to eq 'master'
+ end
+
+ # Branch names can be quite long but in practice aren't over 255 so 4000 should
+ # be enough space for a list of branch names but we can increase if needed.
+ it 'limits length to 4000 chars' do
+ filter = 'a' * 4001
+ validator.validate_each(hook, :push_events_branch_filter, filter)
+
+ expect(hook.errors[:push_events_branch_filter].empty?).to be false
+ end
+ end
+end
diff --git a/spec/validators/js_regex_validator_spec.rb b/spec/validators/js_regex_validator_spec.rb
new file mode 100644
index 00000000000..aeb55cdc0e5
--- /dev/null
+++ b/spec/validators/js_regex_validator_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe JsRegexValidator do
+ describe '#validates_each' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:validator) { described_class.new(attributes: [:user_default_internal_regex]) }
+ let(:application_setting) { build(:application_setting, user_default_external: true) }
+
+ where(:user_default_internal_regex, :result) do
+ nil | []
+ '' | []
+ '(?#comment)' | ['Regex Pattern (?#comment) can not be expressed in Javascript']
+ '(?(a)b|c)' | ['invalid conditional pattern: /(?(a)b|c)/i']
+ '[a-z&&[^uo]]' | ["Dropped unsupported set intersection '[a-z&&[^uo]]' at index 0",
+ "Dropped unsupported nested negative set data '[^uo]' at index 6"]
+ end
+
+ with_them do
+ it 'generates correct errors' do
+ validator.validate_each(application_setting, :user_default_internal_regex, user_default_internal_regex)
+
+ expect(application_setting.errors[:user_default_internal_regex]).to eq result
+ end
+ end
+ end
+end
diff --git a/spec/validators/url_validator_spec.rb b/spec/validators/url_validator_spec.rb
index 93fe013d11c..ab6100509a6 100644
--- a/spec/validators/url_validator_spec.rb
+++ b/spec/validators/url_validator_spec.rb
@@ -24,6 +24,21 @@ describe UrlValidator do
expect(badge.errors.empty?).to be true
end
+
+ it 'strips urls' do
+ badge.link_url = "\n\r\n\nhttps://127.0.0.1\r\n\r\n\n\n\n"
+
+ # It's unusual for a validator to modify its arguments. Some extensions,
+ # such as attr_encrypted, freeze the string to signal that modifications
+ # will not be persisted, so freeze this string to ensure the scheme is
+ # compatible with them.
+ badge.link_url.freeze
+
+ subject
+
+ expect(badge.errors).to be_empty
+ expect(badge.link_url).to eq('https://127.0.0.1')
+ end
end
context 'when allow_localhost is set to false' do
diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb
index 836d452304c..34e93d929a7 100644
--- a/spec/views/help/index.html.haml_spec.rb
+++ b/spec/views/help/index.html.haml_spec.rb
@@ -1,11 +1,18 @@
+# frozen_string_literal: true
+
require 'rails_helper'
describe 'help/index' do
+ include StubVersion
+
describe 'version information' do
+ before do
+ stub_helpers
+ end
+
it 'is hidden from guests' do
stub_user(nil)
stub_version('8.0.2', 'abcdefg')
- stub_helpers
render
@@ -13,15 +20,28 @@ describe 'help/index' do
expect(rendered).not_to match 'abcdefg'
end
- it 'is shown to users' do
- stub_user
- stub_version('8.0.2', 'abcdefg')
- stub_helpers
+ context 'when logged in' do
+ before do
+ stub_user
+ end
- render
+ it 'shows a link to the tag to users' do
+ stub_version('8.0.2', 'abcdefg')
+
+ render
+
+ expect(rendered).to match '8.0.2'
+ expect(rendered).to have_link('8.0.2', href: %r{https://gitlab.com/gitlab-org/gitlab-(ce|ee)/tags/v8.0.2})
+ end
+
+ it 'shows a link to the commit for pre-releases' do
+ stub_version('8.0.2-pre', 'abcdefg')
- expect(rendered).to match '8.0.2'
- expect(rendered).to have_link('abcdefg', 'https://gitlab.com/gitlab-org/gitlab-ce/commits/abcdefg')
+ render
+
+ expect(rendered).to match '8.0.2'
+ expect(rendered).to have_link('abcdefg', href: %r{https://gitlab.com/gitlab-org/gitlab-(ce|ee)/commits/abcdefg})
+ end
end
end
@@ -29,7 +49,7 @@ describe 'help/index' do
it 'is visible to guests' do
render
- expect(rendered).to have_link(nil, help_instance_configuration_url)
+ expect(rendered).to have_link(nil, href: help_instance_configuration_url)
end
end
@@ -37,11 +57,6 @@ describe 'help/index' do
allow(view).to receive(:user_signed_in?).and_return(user)
end
- def stub_version(version, revision)
- stub_const('Gitlab::VERSION', version)
- allow(Gitlab).to receive(:revision).and_return(revision)
- end
-
def stub_helpers
allow(view).to receive(:markdown).and_return('')
allow(view).to receive(:version_status_badge).and_return('')
diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb
index b56940a9613..fc1fe5739c3 100644
--- a/spec/views/projects/_home_panel.html.haml_spec.rb
+++ b/spec/views/projects/_home_panel.html.haml_spec.rb
@@ -9,6 +9,7 @@ describe 'projects/_home_panel' do
allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
+ allow(project).to receive(:license_anchor_data).and_return(false)
end
context 'when user is signed in' do
@@ -63,6 +64,7 @@ describe 'projects/_home_panel' do
allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
+ allow(project).to receive(:license_anchor_data).and_return(false)
end
context 'has no badges' do
@@ -71,8 +73,7 @@ describe 'projects/_home_panel' do
it 'should not render any badge' do
render
- expect(rendered).to have_selector('.project-badges')
- expect(rendered).not_to have_selector('.project-badges > a')
+ expect(rendered).not_to have_selector('.project-badges')
end
end
@@ -118,6 +119,7 @@ describe 'projects/_home_panel' do
assign(:project, project)
allow(view).to receive(:current_user).and_return(user)
+ allow(project).to receive(:license_anchor_data).and_return(false)
end
context 'user can read project' do
diff --git a/spec/views/projects/blob/_viewer.html.haml_spec.rb b/spec/views/projects/blob/_viewer.html.haml_spec.rb
index aedbaa66d34..95f7f87d37b 100644
--- a/spec/views/projects/blob/_viewer.html.haml_spec.rb
+++ b/spec/views/projects/blob/_viewer.html.haml_spec.rb
@@ -29,6 +29,8 @@ describe 'projects/blob/_viewer.html.haml' do
controller.params[:namespace_id] = project.namespace.to_param
controller.params[:project_id] = project.to_param
controller.params[:id] = File.join('master', blob.path)
+
+ allow(project.repository).to receive(:gitattribute).and_return(nil)
end
def render_view
diff --git a/spec/views/projects/jobs/show.html.haml_spec.rb b/spec/views/projects/jobs/show.html.haml_spec.rb
index c93152b88e3..e06a9ecb98b 100644
--- a/spec/views/projects/jobs/show.html.haml_spec.rb
+++ b/spec/views/projects/jobs/show.html.haml_spec.rb
@@ -18,161 +18,6 @@ describe 'projects/jobs/show' do
allow(view).to receive(:can?).and_return(true)
end
- describe 'environment info in job view' do
- context 'job with latest deployment' do
- let(:build) do
- create(:ci_build, :success, :trace_artifact, environment: 'staging')
- end
-
- before do
- create(:environment, name: 'staging')
- create(:deployment, deployable: build)
- end
-
- it 'shows deployment message' do
- expected_text = 'This job is the most recent deployment'
- render
-
- expect(rendered).to have_css(
- '.environment-information', text: expected_text)
- end
- end
-
- context 'job with outdated deployment' do
- let(:build) do
- create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline)
- end
-
- let(:second_build) do
- create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline)
- end
-
- let(:environment) do
- create(:environment, name: 'staging', project: project)
- end
-
- let!(:first_deployment) do
- create(:deployment, environment: environment, deployable: build)
- end
-
- let!(:second_deployment) do
- create(:deployment, environment: environment, deployable: second_build)
- end
-
- it 'shows deployment message' do
- expected_text = 'This job is an out-of-date deployment ' \
- "to staging.\nView the most recent deployment ##{second_deployment.iid}."
- render
-
- expect(rendered).to have_css('.environment-information', text: expected_text)
- end
- end
-
- context 'job failed to deploy' do
- let(:build) do
- create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline)
- end
-
- let!(:environment) do
- create(:environment, name: 'staging', project: project)
- end
-
- it 'shows deployment message' do
- expected_text = 'The deployment of this job to staging did not succeed.'
- render
-
- expect(rendered).to have_css(
- '.environment-information', text: expected_text)
- end
- end
-
- context 'job will deploy' do
- let(:build) do
- create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline)
- end
-
- context 'when environment exists' do
- let!(:environment) do
- create(:environment, name: 'staging', project: project)
- end
-
- it 'shows deployment message' do
- expected_text = 'This job is creating a deployment to staging'
- render
-
- expect(rendered).to have_css(
- '.environment-information', text: expected_text)
- end
-
- context 'when it has deployment' do
- let!(:deployment) do
- create(:deployment, environment: environment)
- end
-
- it 'shows that deployment will be overwritten' do
- expected_text = 'This job is creating a deployment to staging'
- render
-
- expect(rendered).to have_css(
- '.environment-information', text: expected_text)
- expect(rendered).to have_css(
- '.environment-information', text: 'latest deployment')
- end
- end
- end
-
- context 'when environment does not exist' do
- it 'shows deployment message' do
- expected_text = 'This job is creating a deployment to staging'
- render
-
- expect(rendered).to have_css(
- '.environment-information', text: expected_text)
- expect(rendered).not_to have_css(
- '.environment-information', text: 'latest deployment')
- end
- end
- end
-
- context 'job that failed to deploy and environment has not been created' do
- let(:build) do
- create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline)
- end
-
- let!(:environment) do
- create(:environment, name: 'staging', project: project)
- end
-
- it 'shows deployment message' do
- expected_text = 'The deployment of this job to staging did not succeed'
- render
-
- expect(rendered).to have_css(
- '.environment-information', text: expected_text)
- end
- end
-
- context 'job that will deploy and environment has not been created' do
- let(:build) do
- create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline)
- end
-
- let!(:environment) do
- create(:environment, name: 'staging', project: project)
- end
-
- it 'shows deployment message' do
- expected_text = 'This job is creating a deployment to staging'
- render
-
- expect(rendered).to have_css(
- '.environment-information', text: expected_text)
- expect(rendered).not_to have_css(
- '.environment-information', text: 'latest deployment')
- end
- end
- end
-
context 'when job is running' do
let(:build) { create(:ci_build, :trace_live, :running, pipeline: pipeline) }
@@ -188,40 +33,4 @@ describe 'projects/jobs/show' do
expect(rendered).not_to have_link('New issue')
end
end
-
- context 'when incomplete trigger_request is used' do
- before do
- build.trigger_request = FactoryBot.build(:ci_trigger_request, trigger: nil)
- end
-
- it 'test should not render token block' do
- render
-
- expect(rendered).not_to have_content('Token')
- end
- end
-
- context 'when complete trigger_request is used' do
- before do
- build.trigger_request = FactoryBot.build(:ci_trigger_request)
- end
-
- it 'should render token' do
- render
-
- expect(rendered).to have_content('Token')
- expect(rendered).to have_content(build.trigger_request.trigger.short_token)
- end
- end
-
- describe 'commit title in sidebar' do
- let(:commit_title) { project.commit.title }
-
- it 'shows commit title and not show commit message' do
- render
-
- expect(rendered).to have_css('p.build-light-text.append-bottom-0',
- text: /\A\n#{Regexp.escape(commit_title)}\n\Z/)
- end
- end
end
diff --git a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb
index b1c6565c08a..a7628548de6 100644
--- a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb
@@ -20,6 +20,7 @@ describe 'projects/merge_requests/_commits.html.haml' do
assign(:merge_request, merge_request)
assign(:commits, merge_request.commits)
+ assign(:hidden_commit_count, 0)
end
it 'shows commits from source project' do
@@ -30,4 +31,16 @@ describe 'projects/merge_requests/_commits.html.haml' do
expect(rendered).to have_link(href: href)
end
+
+ context 'when there are hidden commits' do
+ before do
+ assign(:hidden_commit_count, 1)
+ end
+
+ it 'shows notice about omitted commits' do
+ render
+
+ expect(rendered).to match(/1 additional commit has been omitted to prevent performance issues/)
+ end
+ end
end
diff --git a/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb b/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb
index 9ab105c3238..0206928a211 100644
--- a/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb
@@ -9,7 +9,10 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do
assign(:merge_request, merge_request)
assign(:commits, merge_request.commits)
+ assign(:hidden_commit_count, 0)
+ assign(:total_commit_count, merge_request.commits.count)
assign(:project, merge_request.target_project)
+ assign(:mr_presenter, merge_request.present(current_user: merge_request.author))
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:url_for).and_return('#')
@@ -29,4 +32,17 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do
expect(rendered).not_to have_text('Builds')
end
end
+
+ context 'when there are hidden commits' do
+ before do
+ assign(:pipelines, Ci::Pipeline.none)
+ assign(:hidden_commit_count, 2)
+ end
+
+ it 'shows notice about omitted commits' do
+ render
+
+ expect(rendered).to match(/2 additional commits have been omitted to prevent performance issues/)
+ end
+ end
end
diff --git a/spec/views/projects/merge_requests/edit.html.haml_spec.rb b/spec/views/projects/merge_requests/edit.html.haml_spec.rb
index 9b74a7e1946..c13eab30054 100644
--- a/spec/views/projects/merge_requests/edit.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/edit.html.haml_spec.rb
@@ -24,6 +24,7 @@ describe 'projects/merge_requests/edit.html.haml' do
before do
assign(:project, project)
assign(:merge_request, closed_merge_request)
+ assign(:mr_presenter, closed_merge_request.present(current_user: user))
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:current_user)
diff --git a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
index 9c0be249a50..8a9ab02eaca 100644
--- a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
+++ b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
@@ -12,10 +12,10 @@ describe 'projects/notes/_more_actions_dropdown' do
assign(:project, project)
end
- it 'shows Report as abuse button if not editable and not current users comment' do
+ it 'shows Report abuse to GitLab button if not editable and not current users comment' do
render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: false, note: note
- expect(rendered).to have_link('Report as abuse')
+ expect(rendered).to have_link('Report abuse to GitLab')
end
it 'does not show the More actions button if not editable and current users comment' do
@@ -24,10 +24,10 @@ describe 'projects/notes/_more_actions_dropdown' do
expect(rendered).not_to have_selector('.dropdown.more-actions')
end
- it 'shows Report as abuse and Delete buttons if editable and not current users comment' do
+ it 'shows Report abuse to GitLab and Delete buttons if editable and not current users comment' do
render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: true, note: note
- expect(rendered).to have_link('Report as abuse')
+ expect(rendered).to have_link('Report abuse to GitLab')
expect(rendered).to have_link('Delete comment')
end
diff --git a/spec/views/projects/tree/_blob_item.html.haml_spec.rb b/spec/views/projects/tree/_blob_item.html.haml_spec.rb
deleted file mode 100644
index 6a477c712ff..00000000000
--- a/spec/views/projects/tree/_blob_item.html.haml_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'spec_helper'
-
-describe 'projects/tree/_blob_item' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
- let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first }
-
- before do
- assign(:project, project)
- assign(:repository, repository)
- assign(:id, File.join('master', ''))
- assign(:lfs_blob_ids, [])
- end
-
- it 'renders blob item' do
- render_partial(blob_item)
-
- expect(rendered).to have_content(blob_item.name)
- expect(rendered).not_to have_selector('.label-lfs', text: 'LFS')
- end
-
- describe 'LFS blob' do
- before do
- assign(:lfs_blob_ids, [blob_item].map(&:id))
-
- render_partial(blob_item)
- end
-
- it 'renders LFS badge' do
- expect(rendered).to have_selector('.label-lfs', text: 'LFS')
- end
- end
-
- def render_partial(blob_item)
- render partial: 'projects/tree/blob_item', locals: {
- blob_item: blob_item,
- type: 'blob'
- }
- end
-end
diff --git a/spec/views/projects/tree/_tree_row.html.haml_spec.rb b/spec/views/projects/tree/_tree_row.html.haml_spec.rb
new file mode 100644
index 00000000000..3353b7665e2
--- /dev/null
+++ b/spec/views/projects/tree/_tree_row.html.haml_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe 'projects/tree/_tree_row' do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first }
+
+ before do
+ assign(:project, project)
+ assign(:repository, repository)
+ assign(:id, File.join('master', ''))
+ assign(:lfs_blob_ids, [])
+ end
+
+ it 'renders blob item' do
+ render_partial(blob_item)
+
+ expect(rendered).to have_content(blob_item.name)
+ expect(rendered).not_to have_selector('.label-lfs', text: 'LFS')
+ end
+
+ describe 'LFS blob' do
+ before do
+ assign(:lfs_blob_ids, [blob_item].map(&:id))
+
+ render_partial(blob_item)
+ end
+
+ it 'renders LFS badge' do
+ expect(rendered).to have_selector('.label-lfs', text: 'LFS')
+ end
+ end
+
+ def render_partial(items)
+ render partial: 'projects/tree/tree_row', collection: [items].flatten
+ end
+end
diff --git a/spec/views/shared/runners/show.html.haml_spec.rb b/spec/views/shared/runners/show.html.haml_spec.rb
new file mode 100644
index 00000000000..5e92928b143
--- /dev/null
+++ b/spec/views/shared/runners/show.html.haml_spec.rb
@@ -0,0 +1,155 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'shared/runners/show.html.haml' do
+ include PageLayoutHelper
+
+ let(:runner) do
+ create(:ci_runner, name: 'test runner',
+ version: '11.4.0',
+ ip_address: '127.1.2.3',
+ revision: 'abcd1234',
+ architecture: 'amd64' )
+ end
+
+ before do
+ assign(:runner, runner)
+ end
+
+ subject do
+ render
+ rendered
+ end
+
+ describe 'Page title' do
+ before do
+ expect_any_instance_of(PageLayoutHelper).to receive(:page_title).with("#{runner.description} ##{runner.id}", 'Runners')
+ end
+
+ it 'sets proper page title' do
+ render
+ end
+ end
+
+ describe 'Runner id and type' do
+ context 'when runner is of type instance' do
+ it { is_expected.to have_content("Runner ##{runner.id} Shared") }
+ end
+
+ context 'when runner is of type group' do
+ let(:runner) { create(:ci_runner, :group) }
+
+ it { is_expected.to have_content("Runner ##{runner.id} Group") }
+ end
+
+ context 'when runner is of type project' do
+ let(:runner) { create(:ci_runner, :project) }
+
+ it { is_expected.to have_content("Runner ##{runner.id} Specific") }
+ end
+ end
+
+ describe 'Active value' do
+ context 'when runner is active' do
+ it { is_expected.to have_content('Active Yes') }
+ end
+
+ context 'when runner is inactive' do
+ let(:runner) { create(:ci_runner, :inactive) }
+
+ it { is_expected.to have_content('Active No') }
+ end
+ end
+
+ describe 'Protected value' do
+ context 'when runner is not protected' do
+ it { is_expected.to have_content('Protected No') }
+ end
+
+ context 'when runner is protected' do
+ let(:runner) { create(:ci_runner, :ref_protected) }
+
+ it { is_expected.to have_content('Protected Yes') }
+ end
+ end
+
+ describe 'Can run untagged jobs value' do
+ context 'when runner run untagged job is set' do
+ it { is_expected.to have_content('Can run untagged jobs Yes') }
+ end
+
+ context 'when runner run untagged job is unset' do
+ let(:runner) { create(:ci_runner, :tagged_only) }
+
+ it { is_expected.to have_content('Can run untagged jobs No') }
+ end
+ end
+
+ describe 'Locked to this project value' do
+ context 'when runner locked is not set' do
+ it { is_expected.to have_content('Locked to this project No') }
+
+ context 'when runner is of type group' do
+ let(:runner) { create(:ci_runner, :group) }
+
+ it { is_expected.not_to have_content('Locked to this project') }
+ end
+ end
+
+ context 'when runner locked is set' do
+ let(:runner) { create(:ci_runner, :locked) }
+
+ it { is_expected.to have_content('Locked to this project Yes') }
+
+ context 'when runner is of type group' do
+ let(:runner) { create(:ci_runner, :group, :locked) }
+
+ it { is_expected.not_to have_content('Locked to this project') }
+ end
+ end
+ end
+
+ describe 'Tags value' do
+ context 'when runner does not have tags' do
+ it { is_expected.to have_content('Tags') }
+ it { is_expected.not_to have_selector('span.badge.badge-primary')}
+ end
+
+ context 'when runner have tags' do
+ let(:runner) { create(:ci_runner, tag_list: %w(tag2 tag3 tag1)) }
+
+ it { is_expected.to have_content('Tags tag1 tag2 tag3') }
+ it { is_expected.to have_selector('span.badge.badge-primary')}
+ end
+ end
+
+ describe 'Metadata values' do
+ it { is_expected.to have_content("Name #{runner.name}") }
+ it { is_expected.to have_content("Version #{runner.version}") }
+ it { is_expected.to have_content("IP Address #{runner.ip_address}") }
+ it { is_expected.to have_content("Revision #{runner.revision}") }
+ it { is_expected.to have_content("Platform #{runner.platform}") }
+ it { is_expected.to have_content("Architecture #{runner.architecture}") }
+ it { is_expected.to have_content("Description #{runner.description}") }
+ end
+
+ describe 'Maximum job timeout value' do
+ let(:runner) { create(:ci_runner, maximum_timeout: 5400) }
+
+ it { is_expected.to have_content('Maximum job timeout 1h 30m') }
+ end
+
+ describe 'Last contact value' do
+ context 'when runner have not contacted yet' do
+ it { is_expected.to have_content('Last contact Never') }
+ end
+
+ context 'when runner have already contacted' do
+ let(:runner) { create(:ci_runner, contacted_at: DateTime.now - 6.days) }
+ let(:expected_contacted_at) { I18n.localize(runner.contacted_at, format: "%b %d, %Y") }
+
+ it { is_expected.to have_content("Last contact #{expected_contacted_at}") }
+ end
+ end
+end
diff --git a/spec/workers/auto_devops/disable_worker_spec.rb b/spec/workers/auto_devops/disable_worker_spec.rb
new file mode 100644
index 00000000000..800a07e41a5
--- /dev/null
+++ b/spec/workers/auto_devops/disable_worker_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe AutoDevops::DisableWorker, '#perform' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository, :auto_devops) }
+ let(:auto_devops) { project.auto_devops }
+ let(:pipeline) { create(:ci_pipeline, :failed, :auto_devops_source, project: project, user: user) }
+
+ subject { described_class.new }
+
+ before do
+ stub_application_setting(auto_devops_enabled: true)
+ auto_devops.update_attribute(:enabled, nil)
+ end
+
+ it 'disables auto devops for project' do
+ subject.perform(pipeline.id)
+
+ expect(auto_devops.reload.enabled).to eq(false)
+ end
+
+ context 'when project owner is a user' do
+ let(:owner) { create(:user) }
+ let(:namespace) { create(:namespace, owner: owner) }
+ let(:project) { create(:project, :repository, :auto_devops, namespace: namespace) }
+
+ it 'sends an email to pipeline user and project owner' do
+ expect(NotificationService).to receive_message_chain(:new, :autodevops_disabled).with(pipeline, [user.email, owner.email])
+
+ subject.perform(pipeline.id)
+ end
+ end
+
+ context 'when project does not have owner' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :repository, :auto_devops, namespace: group) }
+
+ it 'sends an email to pipeline user' do
+ expect(NotificationService).to receive_message_chain(:new, :autodevops_disabled).with(pipeline, [user.email])
+
+ subject.perform(pipeline.id)
+ end
+ end
+
+ context 'when pipeline is not related to a user and project does not have owner' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :repository, :auto_devops, namespace: group) }
+ let(:pipeline) { create(:ci_pipeline, :failed, project: project) }
+
+ it 'does not send an email' do
+ expect(NotificationService).not_to receive(:new)
+
+ subject.perform(pipeline.id)
+ end
+ end
+end
diff --git a/spec/workers/build_success_worker_spec.rb b/spec/workers/build_success_worker_spec.rb
index dba70883130..5eb9709ded9 100644
--- a/spec/workers/build_success_worker_spec.rb
+++ b/spec/workers/build_success_worker_spec.rb
@@ -2,15 +2,39 @@ require 'spec_helper'
describe BuildSuccessWorker do
describe '#perform' do
+ subject { described_class.new.perform(build.id) }
+
+ before do
+ allow_any_instance_of(Deployment).to receive(:create_ref)
+ end
+
context 'when build exists' do
- context 'when build belogs to the environment' do
- let!(:build) { create(:ci_build, environment: 'production') }
+ context 'when deployment was not created with the build creation' do # An edge case during the transition period
+ let!(:build) { create(:ci_build, :deploy_to_production) }
+
+ before do
+ Deployment.delete_all
+ build.reload
+ end
- it 'executes deployment service' do
- expect_any_instance_of(CreateDeploymentService)
- .to receive(:execute)
+ it 'creates a successful deployment' do
+ expect(build).not_to be_has_deployment
- described_class.new.perform(build.id)
+ subject
+
+ build.reload
+ expect(build).to be_has_deployment
+ expect(build.deployment).to be_success
+ end
+ end
+
+ context 'when deployment was created with the build creation' do # Counter part of the above edge case
+ let!(:build) { create(:ci_build, :deploy_to_production) }
+
+ it 'does not create a new deployment' do
+ expect(build).to be_has_deployment
+
+ expect { subject }.not_to change { Deployment.count }
end
end
@@ -18,10 +42,22 @@ describe BuildSuccessWorker do
let!(:build) { create(:ci_build, project: nil) }
it 'does not create deployment' do
- expect_any_instance_of(CreateDeploymentService)
- .not_to receive(:execute)
+ subject
+
+ expect(build.reload).not_to be_has_deployment
+ end
+ end
+
+ context 'when the build will stop an environment' do
+ let!(:build) { create(:ci_build, :stop_review_app, environment: environment.name, project: environment.project) }
+ let(:environment) { create(:environment, state: :available) }
+
+ it 'stops the environment' do
+ expect(environment).to be_available
+
+ subject
- described_class.new.perform(build.id)
+ expect(environment.reload).to be_stopped
end
end
end
diff --git a/spec/workers/ci/build_schedule_worker_spec.rb b/spec/workers/ci/build_schedule_worker_spec.rb
new file mode 100644
index 00000000000..4a3fe84d7f7
--- /dev/null
+++ b/spec/workers/ci/build_schedule_worker_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe Ci::BuildScheduleWorker do
+ subject { described_class.new.perform(build.id) }
+
+ context 'when build is found' do
+ context 'when build is scheduled' do
+ let(:build) { create(:ci_build, :scheduled) }
+
+ it 'executes RunScheduledBuildService' do
+ expect_any_instance_of(Ci::RunScheduledBuildService)
+ .to receive(:execute).once
+
+ subject
+ end
+ end
+
+ context 'when build is not scheduled' do
+ let(:build) { create(:ci_build, :created) }
+
+ it 'executes RunScheduledBuildService' do
+ expect_any_instance_of(Ci::RunScheduledBuildService)
+ .not_to receive(:execute)
+
+ subject
+ end
+ end
+ end
+
+ context 'when build is not found' do
+ let(:build) { build_stubbed(:ci_build, :scheduled) }
+
+ it 'does nothing' do
+ expect_any_instance_of(Ci::RunScheduledBuildService)
+ .not_to receive(:execute)
+
+ subject
+ end
+ end
+end
diff --git a/spec/workers/cluster_platform_configure_worker_spec.rb b/spec/workers/cluster_platform_configure_worker_spec.rb
new file mode 100644
index 00000000000..b51f6e07c6a
--- /dev/null
+++ b/spec/workers/cluster_platform_configure_worker_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ClusterPlatformConfigureWorker, '#execute' do
+ context 'when provider type is gcp' do
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+
+ it 'configures kubernetes platform' do
+ expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
+
+ described_class.new.perform(cluster.id)
+ end
+ end
+
+ context 'when provider type is user' do
+ let(:cluster) { create(:cluster, :project, :provided_by_user) }
+
+ it 'configures kubernetes platform' do
+ expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
+
+ described_class.new.perform(cluster.id)
+ end
+ end
+
+ context 'when cluster does not exist' do
+ it 'does not provision a cluster' do
+ expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:execute)
+
+ described_class.new.perform(123)
+ end
+ end
+
+ context 'when kubeclient raises error' do
+ let(:cluster) { create(:cluster, :project) }
+
+ it 'rescues and logs the error' do
+ allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute).and_raise(::Kubeclient::HttpError.new(500, 'something baaaad happened', ''))
+
+ expect(Rails.logger)
+ .to receive(:error)
+ .with("Failed to create/update Kubernetes namespace for cluster_id: #{cluster.id} with error: something baaaad happened")
+
+ described_class.new.perform(cluster.id)
+ end
+ end
+end
diff --git a/spec/workers/cluster_provision_worker_spec.rb b/spec/workers/cluster_provision_worker_spec.rb
index 8054ec11a48..0a2dfef36a4 100644
--- a/spec/workers/cluster_provision_worker_spec.rb
+++ b/spec/workers/cluster_provision_worker_spec.rb
@@ -14,18 +14,25 @@ describe ClusterProvisionWorker do
end
context 'when provider type is user' do
- let(:cluster) { create(:cluster, provider_type: :user) }
+ let(:cluster) { create(:cluster, :provided_by_user) }
it 'does not provision a cluster' do
expect_any_instance_of(Clusters::Gcp::ProvisionService).not_to receive(:execute)
described_class.new.perform(cluster.id)
end
+
+ it 'configures kubernetes platform' do
+ expect(ClusterPlatformConfigureWorker).to receive(:perform_async).with(cluster.id)
+
+ described_class.new.perform(cluster.id)
+ end
end
context 'when cluster does not exist' do
it 'does not provision a cluster' do
expect_any_instance_of(Clusters::Gcp::ProvisionService).not_to receive(:execute)
+ expect(ClusterPlatformConfigureWorker).not_to receive(:perform_async)
described_class.new.perform(123)
end
diff --git a/spec/workers/delete_container_repository_worker_spec.rb b/spec/workers/delete_container_repository_worker_spec.rb
new file mode 100644
index 00000000000..8c40611a959
--- /dev/null
+++ b/spec/workers/delete_container_repository_worker_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe DeleteContainerRepositoryWorker do
+ let(:registry) { create(:container_repository) }
+ let(:project) { registry.project }
+ let(:user) { project.owner }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ it 'executes the destroy service' do
+ service = instance_double(Projects::ContainerRepository::DestroyService)
+ expect(service).to receive(:execute)
+ expect(Projects::ContainerRepository::DestroyService).to receive(:new).with(project, user).and_return(service)
+
+ subject.perform(user.id, registry.id)
+ end
+
+ it 'does not raise error when user could not be found' do
+ expect do
+ subject.perform(-1, registry.id)
+ end.not_to raise_error
+ end
+
+ it 'does not raise error when registry could not be found' do
+ expect do
+ subject.perform(user.id, -1)
+ end.not_to raise_error
+ end
+ end
+end
diff --git a/spec/workers/deployments/success_worker_spec.rb b/spec/workers/deployments/success_worker_spec.rb
new file mode 100644
index 00000000000..ba7d45eca01
--- /dev/null
+++ b/spec/workers/deployments/success_worker_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe Deployments::SuccessWorker do
+ subject { described_class.new.perform(deployment&.id) }
+
+ context 'when successful deployment' do
+ let(:deployment) { create(:deployment, :success) }
+
+ it 'executes UpdateDeploymentService' do
+ expect(UpdateDeploymentService)
+ .to receive(:new).with(deployment).and_call_original
+
+ subject
+ end
+ end
+
+ context 'when canceled deployment' do
+ let(:deployment) { create(:deployment, :canceled) }
+
+ it 'does not execute UpdateDeploymentService' do
+ expect(UpdateDeploymentService).not_to receive(:new)
+
+ subject
+ end
+ end
+
+ context 'when deploy record does not exist' do
+ let(:deployment) { nil }
+
+ it 'does not execute UpdateDeploymentService' do
+ expect(UpdateDeploymentService).not_to receive(:new)
+
+ subject
+ end
+ end
+end
diff --git a/spec/workers/email_receiver_worker_spec.rb b/spec/workers/email_receiver_worker_spec.rb
index e4e77c667b3..045135255d6 100644
--- a/spec/workers/email_receiver_worker_spec.rb
+++ b/spec/workers/email_receiver_worker_spec.rb
@@ -46,6 +46,21 @@ describe EmailReceiverWorker, :mailer do
should_not_email_anyone
end
end
+
+ context 'when the error is Gitlab::Email::InvalidAttachment' do
+ let(:error) { Gitlab::Email::InvalidAttachment.new("Could not deal with that") }
+
+ it 'reports the error to the sender' do
+ perform_enqueued_jobs do
+ described_class.new.perform(raw_message)
+ end
+
+ email = ActionMailer::Base.deliveries.last
+ expect(email).not_to be_nil
+ expect(email.to).to eq(["jake@adventuretime.ooo"])
+ expect(email.body.parts.last.to_s).to include("Could not deal with that")
+ end
+ end
end
end
diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb
index f17c5ac6aac..05b4fb49ea3 100644
--- a/spec/workers/emails_on_push_worker_spec.rb
+++ b/spec/workers/emails_on_push_worker_spec.rb
@@ -101,7 +101,7 @@ describe EmailsOnPushWorker, :mailer do
context "when there are multiple recipients" do
before do
- # This is a hack because we modify the mail object before sending, for efficency,
+ # This is a hack because we modify the mail object before sending, for efficiency,
# but the TestMailer adapter just appends the objects to an array. To clone a mail
# object, create a new one!
# https://github.com/mikel/mail/issues/314#issuecomment-12750108
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index 30e67e67e0e..a159f24f876 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -3,6 +3,8 @@ require 'fileutils'
require 'spec_helper'
describe GitGarbageCollectWorker do
+ include GitHelpers
+
let(:project) { create(:project, :repository) }
let(:shell) { Gitlab::Shell.new }
let!(:lease_uuid) { SecureRandom.uuid }
@@ -197,9 +199,7 @@ describe GitGarbageCollectWorker do
# Create a new commit on a random new branch
def create_objects(project)
- rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged
- end
+ rugged = rugged_repo(project.repository)
old_commit = rugged.branches.first.target
new_commit_sha = Rugged::Commit.create(
rugged,
diff --git a/spec/workers/namespaceless_project_destroy_worker_spec.rb b/spec/workers/namespaceless_project_destroy_worker_spec.rb
index eec110dfbfb..2f21a1321e1 100644
--- a/spec/workers/namespaceless_project_destroy_worker_spec.rb
+++ b/spec/workers/namespaceless_project_destroy_worker_spec.rb
@@ -71,10 +71,10 @@ describe NamespacelessProjectDestroyWorker do
expect(merge_request.reload).to be_closed
end
- it 'destroys the link' do
+ it 'destroys fork network members' do
subject.perform(project.id)
- expect(parent_project.forked_project_links).to be_empty
+ expect(parent_project.forked_to_members).to be_empty
end
end
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index cd6661f09a1..9176eb12b12 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -6,7 +6,7 @@ describe PostReceive do
let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) }
let(:gl_repository) { "project-#{project.id}" }
let(:key) { create(:key, user: project.owner) }
- let(:key_id) { key.shell_id }
+ let!(:key_id) { key.shell_id }
let(:project) do
create(:project, :repository, auto_cancel_pending_pipelines: 'disabled')
@@ -31,85 +31,108 @@ describe PostReceive do
end
describe "#process_project_changes" do
- before do
- allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner)
+ context 'empty changes' do
+ it "does not call any PushService but runs after project hooks" do
+ expect(GitPushService).not_to receive(:new)
+ expect(GitTagPushService).not_to receive(:new)
+ expect_next_instance_of(SystemHooksService) { |service| expect(service).to receive(:execute_hooks) }
+
+ described_class.new.perform(gl_repository, key_id, "")
+ end
end
- context "branches" do
- let(:changes) { "123456 789012 refs/heads/tést" }
+ context 'unidentified user' do
+ let!(:key_id) { "" }
- it "calls GitTagPushService" do
- expect_any_instance_of(GitPushService).to receive(:execute).and_return(true)
- expect_any_instance_of(GitTagPushService).not_to receive(:execute)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ it 'returns false' do
+ expect(GitPushService).not_to receive(:new)
+ expect(GitTagPushService).not_to receive(:new)
+
+ expect(described_class.new.perform(gl_repository, key_id, base64_changes)).to be false
end
end
- context "tags" do
- let(:changes) { "123456 789012 refs/tags/tag" }
+ context 'with changes' do
+ before do
+ allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner)
+ end
+
+ context "branches" do
+ let(:changes) { "123456 789012 refs/heads/tést" }
- it "calls GitTagPushService" do
- expect_any_instance_of(GitPushService).not_to receive(:execute)
- expect_any_instance_of(GitTagPushService).to receive(:execute).and_return(true)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ it "calls GitPushService" do
+ expect_any_instance_of(GitPushService).to receive(:execute).and_return(true)
+ expect_any_instance_of(GitTagPushService).not_to receive(:execute)
+ described_class.new.perform(gl_repository, key_id, base64_changes)
+ end
end
- end
- context "merge-requests" do
- let(:changes) { "123456 789012 refs/merge-requests/123" }
+ context "tags" do
+ let(:changes) { "123456 789012 refs/tags/tag" }
- it "does not call any of the services" do
- expect_any_instance_of(GitPushService).not_to receive(:execute)
- expect_any_instance_of(GitTagPushService).not_to receive(:execute)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ it "calls GitTagPushService" do
+ expect_any_instance_of(GitPushService).not_to receive(:execute)
+ expect_any_instance_of(GitTagPushService).to receive(:execute).and_return(true)
+ described_class.new.perform(gl_repository, key_id, base64_changes)
+ end
end
- end
- context "gitlab-ci.yml" do
- let(:changes) { "123456 789012 refs/heads/feature\n654321 210987 refs/tags/tag" }
+ context "merge-requests" do
+ let(:changes) { "123456 789012 refs/merge-requests/123" }
- subject { described_class.new.perform(gl_repository, key_id, base64_changes) }
+ it "does not call any of the services" do
+ expect_any_instance_of(GitPushService).not_to receive(:execute)
+ expect_any_instance_of(GitTagPushService).not_to receive(:execute)
+ described_class.new.perform(gl_repository, key_id, base64_changes)
+ end
+ end
- context "creates a Ci::Pipeline for every change" do
- before do
- stub_ci_pipeline_to_return_yaml_file
+ context "gitlab-ci.yml" do
+ let(:changes) { "123456 789012 refs/heads/feature\n654321 210987 refs/tags/tag" }
- allow_any_instance_of(Project)
- .to receive(:commit)
- .and_return(project.commit)
+ subject { described_class.new.perform(gl_repository, key_id, base64_changes) }
- allow_any_instance_of(Repository)
- .to receive(:branch_exists?)
- .and_return(true)
- end
+ context "creates a Ci::Pipeline for every change" do
+ before do
+ stub_ci_pipeline_to_return_yaml_file
- it { expect { subject }.to change { Ci::Pipeline.count }.by(2) }
- end
+ allow_any_instance_of(Project)
+ .to receive(:commit)
+ .and_return(project.commit)
- context "does not create a Ci::Pipeline" do
- before do
- stub_ci_pipeline_yaml_file(nil)
+ allow_any_instance_of(Repository)
+ .to receive(:branch_exists?)
+ .and_return(true)
+ end
+
+ it { expect { subject }.to change { Ci::Pipeline.count }.by(2) }
end
- it { expect { subject }.not_to change { Ci::Pipeline.count } }
+ context "does not create a Ci::Pipeline" do
+ before do
+ stub_ci_pipeline_yaml_file(nil)
+ end
+
+ it { expect { subject }.not_to change { Ci::Pipeline.count } }
+ end
end
- end
- context 'after project changes hooks' do
- let(:changes) { '123456 789012 refs/heads/tést' }
- let(:fake_hook_data) { Hash.new(event_name: 'repository_update') }
+ context 'after project changes hooks' do
+ let(:changes) { '123456 789012 refs/heads/tést' }
+ let(:fake_hook_data) { Hash.new(event_name: 'repository_update') }
- before do
- allow_any_instance_of(Gitlab::DataBuilder::Repository).to receive(:update).and_return(fake_hook_data)
- # silence hooks so we can isolate
- allow_any_instance_of(Key).to receive(:post_create_hook).and_return(true)
- allow_any_instance_of(GitPushService).to receive(:execute).and_return(true)
- end
+ before do
+ allow_any_instance_of(Gitlab::DataBuilder::Repository).to receive(:update).and_return(fake_hook_data)
+ # silence hooks so we can isolate
+ allow_any_instance_of(Key).to receive(:post_create_hook).and_return(true)
+ allow_any_instance_of(GitPushService).to receive(:execute).and_return(true)
+ end
- it 'calls SystemHooksService' do
- expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(fake_hook_data, :repository_update_hooks).and_return(true)
+ it 'calls SystemHooksService' do
+ expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(fake_hook_data, :repository_update_hooks).and_return(true)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ described_class.new.perform(gl_repository, key_id, base64_changes)
+ end
end
end
end
diff --git a/spec/workers/project_destroy_worker_spec.rb b/spec/workers/project_destroy_worker_spec.rb
index 42e1d86e3bb..6132f145f8d 100644
--- a/spec/workers/project_destroy_worker_spec.rb
+++ b/spec/workers/project_destroy_worker_spec.rb
@@ -18,13 +18,6 @@ describe ProjectDestroyWorker do
expect(Dir.exist?(path)).to be_falsey
end
- it 'deletes the project but skips repo deletion' do
- subject.perform(project.id, project.owner.id, { "skip_repo" => true })
-
- expect(Project.all).not_to include(project)
- expect(Dir.exist?(path)).to be_truthy
- end
-
it 'does not raise error when project could not be found' do
expect do
subject.perform(-1, project.owner.id, {})
diff --git a/spec/workers/project_service_worker_spec.rb b/spec/workers/project_service_worker_spec.rb
new file mode 100644
index 00000000000..56934f122e4
--- /dev/null
+++ b/spec/workers/project_service_worker_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe ProjectServiceWorker, '#perform' do
+ let(:worker) { described_class.new }
+ let(:service) { JiraService.new }
+
+ before do
+ allow(Service).to receive(:find).and_return(service)
+ end
+
+ it 'executes service with given data' do
+ data = { test: 'test' }
+ expect(service).to receive(:execute).with(data)
+
+ worker.perform(1, data)
+ end
+
+ it 'logs error messages' do
+ allow(service).to receive(:execute).and_raise(StandardError, 'invalid URL')
+ expect(Sidekiq.logger).to receive(:error).with({ class: described_class.name, service_class: service.class.name, message: "invalid URL" })
+
+ worker.perform(1, {})
+ end
+end
diff --git a/spec/workers/prune_old_events_worker_spec.rb b/spec/workers/prune_old_events_worker_spec.rb
index ea974355050..b999a6fd5b6 100644
--- a/spec/workers/prune_old_events_worker_spec.rb
+++ b/spec/workers/prune_old_events_worker_spec.rb
@@ -4,23 +4,29 @@ describe PruneOldEventsWorker do
describe '#perform' do
let(:user) { create(:user) }
- let!(:expired_event) { create(:event, :closed, author: user, created_at: 13.months.ago) }
- let!(:not_expired_event) { create(:event, :closed, author: user, created_at: 1.day.ago) }
- let!(:exactly_12_months_event) { create(:event, :closed, author: user, created_at: 12.months.ago) }
+ let!(:expired_event) { create(:event, :closed, author: user, created_at: 25.months.ago) }
+ let!(:not_expired_1_day_event) { create(:event, :closed, author: user, created_at: 1.day.ago) }
+ let!(:not_expired_13_month_event) { create(:event, :closed, author: user, created_at: 13.months.ago) }
+ let!(:not_expired_2_years_event) { create(:event, :closed, author: user, created_at: 2.years.ago) }
- it 'prunes events older than 12 months' do
+ it 'prunes events older than 2 years' do
expect { subject.perform }.to change { Event.count }.by(-1)
expect(Event.find_by(id: expired_event.id)).to be_nil
end
it 'leaves fresh events' do
subject.perform
- expect(not_expired_event.reload).to be_present
+ expect(not_expired_1_day_event.reload).to be_present
end
- it 'leaves events from exactly 12 months ago' do
+ it 'leaves events from 13 months ago' do
subject.perform
- expect(exactly_12_months_event).to be_present
+ expect(not_expired_13_month_event.reload).to be_present
+ end
+
+ it 'leaves events from 2 years ago' do
+ subject.perform
+ expect(not_expired_2_years_event).to be_present
end
end
end
diff --git a/spec/workers/rebase_worker_spec.rb b/spec/workers/rebase_worker_spec.rb
index 20aff020dbb..936b9deaecc 100644
--- a/spec/workers/rebase_worker_spec.rb
+++ b/spec/workers/rebase_worker_spec.rb
@@ -1,25 +1,23 @@
require 'spec_helper'
describe RebaseWorker, '#perform' do
+ include ProjectForksHelper
+
context 'when rebasing an MR from a fork where upstream has protected branches' do
let(:upstream_project) { create(:project, :repository) }
- let(:fork_project) { create(:project, :repository) }
+ let(:forked_project) { fork_project(upstream_project, nil, repository: true) }
let(:merge_request) do
create(:merge_request,
- source_project: fork_project,
+ source_project: forked_project,
source_branch: 'feature_conflict',
target_project: upstream_project,
target_branch: 'master')
end
- before do
- create(:forked_project_link, forked_to_project: fork_project, forked_from_project: upstream_project)
- end
-
it 'sets the correct project for running hooks' do
expect(MergeRequests::RebaseService)
- .to receive(:new).with(fork_project, merge_request.author).and_call_original
+ .to receive(:new).with(forked_project, merge_request.author).and_call_original
subject.perform(merge_request, merge_request.author)
end
diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb
index ede271b2cdd..50b93fce2dc 100644
--- a/spec/workers/repository_check/batch_worker_spec.rb
+++ b/spec/workers/repository_check/batch_worker_spec.rb
@@ -51,7 +51,7 @@ describe RepositoryCheck::BatchWorker do
it 'does nothing when shard is unhealthy' do
shard_name = 'broken'
- create(:project, created_at: 1.week.ago, repository_storage: shard_name)
+ create(:project, :broken_storage, created_at: 1.week.ago)
expect(subject.perform(shard_name)).to eq(nil)
end
diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb
index 22fc64c1536..f11875cffd1 100644
--- a/spec/workers/repository_check/single_repository_worker_spec.rb
+++ b/spec/workers/repository_check/single_repository_worker_spec.rb
@@ -6,7 +6,7 @@ describe RepositoryCheck::SingleRepositoryWorker do
it 'skips when the project has no push events' do
project = create(:project, :repository, :wiki_disabled)
- project.events.destroy_all
+ project.events.destroy_all # rubocop: disable DestroyAll
break_project(project)
expect(worker).not_to receive(:git_fsck)
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index ac8716ecfb1..781f91ac9ca 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe RepositoryForkWorker do
+ include ProjectForksHelper
+
describe 'modules' do
it 'includes ProjectImportOptions' do
expect(described_class).to include_module(ProjectImportOptions)
@@ -8,9 +10,13 @@ describe RepositoryForkWorker do
end
describe "#perform" do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :public, :repository) }
let(:shell) { Gitlab::Shell.new }
- let(:fork_project) { create(:project, :repository, :import_scheduled, forked_from_project: project) }
+ let(:forked_project) { create(:project, :repository, :import_scheduled) }
+
+ before do
+ fork_project(project, forked_project.creator, target_project: forked_project, repository: true)
+ end
shared_examples 'RepositoryForkWorker performing' do
before do
@@ -21,8 +27,8 @@ describe RepositoryForkWorker do
expect(shell).to receive(:fork_repository).with(
'default',
project.disk_path,
- fork_project.repository_storage,
- fork_project.disk_path
+ forked_project.repository_storage,
+ forked_project.disk_path
)
end
@@ -49,28 +55,28 @@ describe RepositoryForkWorker do
perform!
- expect(fork_project.protected_branches.first.name).to eq(fork_project.default_branch)
+ expect(forked_project.protected_branches.first.name).to eq(forked_project.default_branch)
end
it 'flushes various caches' do
expect_fork_repository.and_return(true)
# Works around https://github.com/rspec/rspec-mocks/issues/910
- expect(Project).to receive(:find).with(fork_project.id).and_return(fork_project)
- expect(fork_project.repository).to receive(:expire_emptiness_caches)
+ expect(Project).to receive(:find).with(forked_project.id).and_return(forked_project)
+ expect(forked_project.repository).to receive(:expire_emptiness_caches)
.and_call_original
- expect(fork_project.repository).to receive(:expire_exists_cache)
+ expect(forked_project.repository).to receive(:expire_exists_cache)
.and_call_original
- expect(fork_project.wiki.repository).to receive(:expire_emptiness_caches)
+ expect(forked_project.wiki.repository).to receive(:expire_emptiness_caches)
.and_call_original
- expect(fork_project.wiki.repository).to receive(:expire_exists_cache)
+ expect(forked_project.wiki.repository).to receive(:expire_exists_cache)
.and_call_original
perform!
end
it "handles bad fork" do
- error_message = "Unable to fork project #{fork_project.id} for repository #{project.disk_path} -> #{fork_project.disk_path}"
+ error_message = "Unable to fork project #{forked_project.id} for repository #{project.disk_path} -> #{forked_project.disk_path}"
expect_fork_repository.and_return(false)
@@ -80,7 +86,7 @@ describe RepositoryForkWorker do
context 'only project ID passed' do
def perform!
- subject.perform(fork_project.id)
+ subject.perform(forked_project.id)
end
it_behaves_like 'RepositoryForkWorker performing'
@@ -88,7 +94,7 @@ describe RepositoryForkWorker do
context 'project ID, storage and repo paths passed' do
def perform!
- subject.perform(fork_project.id, TestEnv.repos_path, project.disk_path)
+ subject.perform(forked_project.id, TestEnv.repos_path, project.disk_path)
end
it_behaves_like 'RepositoryForkWorker performing'
diff --git a/spec/workers/repository_remove_remote_worker_spec.rb b/spec/workers/repository_remove_remote_worker_spec.rb
index a653f6f926c..6ddb653d142 100644
--- a/spec/workers/repository_remove_remote_worker_spec.rb
+++ b/spec/workers/repository_remove_remote_worker_spec.rb
@@ -2,6 +2,7 @@ require 'rails_helper'
describe RepositoryRemoveRemoteWorker do
include ExclusiveLeaseHelpers
+ include GitHelpers
describe '#perform' do
let!(:project) { create(:project, :repository) }
@@ -50,9 +51,7 @@ describe RepositoryRemoveRemoteWorker do
end
def create_remote_branch(remote_name, branch_name, target)
- rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.rugged
- end
+ rugged = rugged_repo(project.repository)
rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
end
diff --git a/spec/workers/stuck_ci_jobs_worker_spec.rb b/spec/workers/stuck_ci_jobs_worker_spec.rb
index 856886e3df5..557934346c9 100644
--- a/spec/workers/stuck_ci_jobs_worker_spec.rb
+++ b/spec/workers/stuck_ci_jobs_worker_spec.rb
@@ -127,6 +127,47 @@ describe StuckCiJobsWorker do
end
end
+ describe 'drop stale scheduled builds' do
+ let(:status) { 'scheduled' }
+ let(:updated_at) { }
+
+ context 'when scheduled at 2 hours ago but it is not executed yet' do
+ let!(:job) { create(:ci_build, :scheduled, scheduled_at: 2.hours.ago) }
+
+ it 'drops the stale scheduled build' do
+ expect(Ci::Build.scheduled.count).to eq(1)
+ expect(job).to be_scheduled
+
+ worker.perform
+ job.reload
+
+ expect(Ci::Build.scheduled.count).to eq(0)
+ expect(job).to be_failed
+ expect(job).to be_stale_schedule
+ end
+ end
+
+ context 'when scheduled at 30 minutes ago but it is not executed yet' do
+ let!(:job) { create(:ci_build, :scheduled, scheduled_at: 30.minutes.ago) }
+
+ it 'does not drop the stale scheduled build yet' do
+ expect(Ci::Build.scheduled.count).to eq(1)
+ expect(job).to be_scheduled
+
+ worker.perform
+
+ expect(Ci::Build.scheduled.count).to eq(1)
+ expect(job).to be_scheduled
+ end
+ end
+
+ context 'when there are no stale scheduled builds' do
+ it 'does not drop the stale scheduled build yet' do
+ expect { worker.perform }.not_to raise_error
+ end
+ end
+ end
+
describe 'exclusive lease' do
let(:status) { 'running' }
let(:updated_at) { 2.days.ago }
diff --git a/spec/workers/stuck_import_jobs_worker_spec.rb b/spec/workers/stuck_import_jobs_worker_spec.rb
index 2169c14218b..e94d2be9850 100644
--- a/spec/workers/stuck_import_jobs_worker_spec.rb
+++ b/spec/workers/stuck_import_jobs_worker_spec.rb
@@ -8,29 +8,29 @@ describe StuckImportJobsWorker do
context 'when the import status was already updated' do
before do
allow(Gitlab::SidekiqStatus).to receive(:completed_jids) do
- project.import_start
- project.import_finish
+ import_state.start
+ import_state.finish
- [project.import_jid]
+ [import_state.jid]
end
end
it 'does not mark the project as failed' do
worker.perform
- expect(project.reload.import_status).to eq('finished')
+ expect(import_state.reload.status).to eq('finished')
end
end
context 'when the import status was not updated' do
before do
- allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([project.import_jid])
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([import_state.jid])
end
it 'marks the project as failed' do
worker.perform
- expect(project.reload.import_status).to eq('failed')
+ expect(import_state.reload.status).to eq('failed')
end
end
end
@@ -41,27 +41,27 @@ describe StuckImportJobsWorker do
end
it 'does not mark the project as failed' do
- expect { worker.perform }.not_to change { project.reload.import_status }
+ expect { worker.perform }.not_to change { import_state.reload.status }
end
end
end
describe 'with scheduled import_status' do
it_behaves_like 'project import job detection' do
- let(:project) { create(:project, :import_scheduled) }
+ let(:import_state) { create(:project, :import_scheduled).import_state }
before do
- project.import_state.update(jid: '123')
+ import_state.update(jid: '123')
end
end
end
describe 'with started import_status' do
it_behaves_like 'project import job detection' do
- let(:project) { create(:project, :import_started) }
+ let(:import_state) { create(:project, :import_started).import_state }
before do
- project.import_state.update(jid: '123')
+ import_state.update(jid: '123')
end
end
end
diff --git a/vendor/Dockerfile/Node-alpine.Dockerfile b/vendor/Dockerfile/Node-alpine.Dockerfile
index 5b9b495644a..24f92dd92cd 100644
--- a/vendor/Dockerfile/Node-alpine.Dockerfile
+++ b/vendor/Dockerfile/Node-alpine.Dockerfile
@@ -1,15 +1,17 @@
-FROM node:8.11-alpine
+FROM node:10.6-alpine
-WORKDIR /usr/src/app
+# Uncomment if use of `process.dlopen` is necessary
+# apk add --no-cache libc6-compat
+
+ENV PORT 8080
+EXPOSE 8080 # replace this with your application's default port, if necessary
-ARG NODE_ENV
+ARG NODE_ENV=production
ENV NODE_ENV $NODE_ENV
-COPY package.json /usr/src/app/
+WORKDIR /usr/src/app
+COPY package.json .
RUN npm install
+COPY . .
-COPY . /usr/src/app
-
-# replace this with your application's default port
-EXPOSE 8888
CMD [ "npm", "start" ]
diff --git a/vendor/Dockerfile/OpenJDK.Dockerfile b/vendor/Dockerfile/OpenJDK.Dockerfile
index 8a2ae62d93b..c68420b453a 100644
--- a/vendor/Dockerfile/OpenJDK.Dockerfile
+++ b/vendor/Dockerfile/OpenJDK.Dockerfile
@@ -1,8 +1,12 @@
-FROM openjdk:9
+FROM maven:3.5-jdk-11 as BUILD
-COPY . /usr/src/myapp
-WORKDIR /usr/src/myapp
+COPY . /usr/src/app
+RUN mvn --batch-mode -f /usr/src/app/pom.xml clean package
-RUN javac Main.java
+FROM openjdk:11-jdk
+ENV PORT 4567
+EXPOSE 4567
+COPY --from=BUILD /usr/src/app/target /opt/target
+WORKDIR /opt/target
-CMD ["java", "Main"]
+CMD ["/bin/bash", "-c", "find -type f -name '*-with-dependencies.jar' | xargs java -jar"]
diff --git a/vendor/Dockerfile/Ruby-alpine.Dockerfile b/vendor/Dockerfile/Ruby-alpine.Dockerfile
index dffe9a65116..0f748d84b5d 100644
--- a/vendor/Dockerfile/Ruby-alpine.Dockerfile
+++ b/vendor/Dockerfile/Ruby-alpine.Dockerfile
@@ -7,21 +7,21 @@ RUN apk --no-cache add nodejs postgresql-client tzdata
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
-RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
-COPY Gemfile Gemfile.lock /usr/src/app/
+COPY Gemfile Gemfile.lock .
# Install build dependencies - required for gems with native dependencies
RUN apk add --no-cache --virtual build-deps build-base postgresql-dev && \
bundle install && \
apk del build-deps
-COPY . /usr/src/app
+COPY . .
# For Sinatra
#EXPOSE 4567
#CMD ["ruby", "./config.rb"]
# For Rails
+ENV PORT 3000
EXPOSE 3000
CMD ["bundle", "exec", "rails", "server"]
diff --git a/vendor/gitignore/Android.gitignore b/vendor/gitignore/Android.gitignore
index 39b6783cef8..69eda01429a 100644
--- a/vendor/gitignore/Android.gitignore
+++ b/vendor/gitignore/Android.gitignore
@@ -1,6 +1,7 @@
# Built application files
*.apk
*.ap_
+*.aab
# Files for the ART/Dalvik VM
*.dex
@@ -43,8 +44,9 @@ captures/
.idea/caches
# Keystore files
-# Uncomment the following line if you do not want to check your keystore files in.
+# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
+#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
diff --git a/vendor/gitignore/Delphi.gitignore b/vendor/gitignore/Delphi.gitignore
index 000ee5f104b..9532800ba22 100644
--- a/vendor/gitignore/Delphi.gitignore
+++ b/vendor/gitignore/Delphi.gitignore
@@ -64,3 +64,6 @@ __recovery/
# Castalia statistics file (since XE7 Castalia is distributed with Delphi)
*.stat
+
+# Boss dependency manager vendor folder https://github.com/HashLoad/boss
+modules/
diff --git a/vendor/gitignore/Elixir.gitignore b/vendor/gitignore/Elixir.gitignore
index 86e4c3f3905..b263cd10f37 100644
--- a/vendor/gitignore/Elixir.gitignore
+++ b/vendor/gitignore/Elixir.gitignore
@@ -7,3 +7,4 @@ erl_crash.dump
*.ez
*.beam
/config/*.secret.exs
+.elixir_ls/
diff --git a/vendor/gitignore/Global/Diff.gitignore b/vendor/gitignore/Global/Diff.gitignore
new file mode 100644
index 00000000000..59491b4440c
--- /dev/null
+++ b/vendor/gitignore/Global/Diff.gitignore
@@ -0,0 +1,2 @@
+*.patch
+*.diff
diff --git a/vendor/gitignore/Global/Images.gitignore b/vendor/gitignore/Global/Images.gitignore
new file mode 100644
index 00000000000..97dcdbe6a95
--- /dev/null
+++ b/vendor/gitignore/Global/Images.gitignore
@@ -0,0 +1,63 @@
+# JPEG
+*.jpg
+*.jpeg
+*.jpe
+*.jif
+*.jfif
+*.jfi
+
+# JPEG 2000
+*.jp2
+*.j2k
+*.jpf
+*.jpx
+*.jpm
+*.mj2
+
+# JPEG XR
+*.jxr
+*.hdp
+*.wdp
+
+# Graphics Interchange Format
+*.gif
+
+# RAW
+*.raw
+
+# Web P
+*.webp
+
+# Portable Network Graphics
+*.png
+
+# Animated Portable Network Graphics
+*.apng
+
+# Multiple-image Network Graphics
+*.mng
+
+# Tagged Image File Format
+*.tiff
+*.tif
+
+# Scalable Vector Graphics
+*.svg
+*.svgz
+
+# Portable Document Format
+*.pdf
+
+# X BitMap
+*.xbm
+
+# BMP
+*.bmp
+*.dib
+
+# ICO
+*.ico
+
+# 3D Images
+*.3dm
+*.max
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index 0d95b087f19..72f4d988a19 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -8,6 +8,9 @@
.idea/**/dictionaries
.idea/**/shelf
+# Generated files
+.idea/**/contentModel.xml
+
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
@@ -58,3 +61,6 @@ fabric.properties
# Editor-based Rest Client
.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
diff --git a/vendor/gitignore/Global/MicrosoftOffice.gitignore b/vendor/gitignore/Global/MicrosoftOffice.gitignore
index 0c203662d39..ddcc9cf6e38 100644
--- a/vendor/gitignore/Global/MicrosoftOffice.gitignore
+++ b/vendor/gitignore/Global/MicrosoftOffice.gitignore
@@ -3,6 +3,9 @@
# Word temporary
~$*.doc*
+# Word Auto Backup File
+Backup of *.doc*
+
# Excel temporary
~$*.xls*
diff --git a/vendor/gitignore/Global/NetBeans.gitignore b/vendor/gitignore/Global/NetBeans.gitignore
index 254108cd23b..863bc7fa66e 100644
--- a/vendor/gitignore/Global/NetBeans.gitignore
+++ b/vendor/gitignore/Global/NetBeans.gitignore
@@ -1,4 +1,4 @@
-nbproject/private/
+**/nbproject/private/
build/
nbbuild/
dist/
diff --git a/vendor/gitignore/Global/PSoCCreator.gitignore b/vendor/gitignore/Global/PSoCCreator.gitignore
new file mode 100644
index 00000000000..15ae040bcda
--- /dev/null
+++ b/vendor/gitignore/Global/PSoCCreator.gitignore
@@ -0,0 +1,18 @@
+# Project Settings
+*.cywrk.*
+*.cyprj.*
+
+# Generated Assets and Resources
+Debug/
+Release/
+Export/
+*/codegentemp
+*/Generated_Source
+*_datasheet.pdf
+*_timing.html
+*.cycdx
+*.cyfit
+*.rpt
+*.svd
+*.log
+*.zip
diff --git a/vendor/gitignore/Global/Xcode.gitignore b/vendor/gitignore/Global/Xcode.gitignore
index cd0c7d3e45a..b01314d3a64 100644
--- a/vendor/gitignore/Global/Xcode.gitignore
+++ b/vendor/gitignore/Global/Xcode.gitignore
@@ -2,17 +2,11 @@
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
-## User settings
-xcuserdata/
-
-## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
-*.xcscmblueprint
-*.xccheckout
-
-## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
+## Build generated
build/
DerivedData/
-*.moved-aside
+
+## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
@@ -21,3 +15,65 @@ DerivedData/
!default.mode2v3
*.perspectivev3
!default.perspectivev3
+xcuserdata/
+
+## Other
+*.moved-aside
+*.xccheckout
+*.xcscmblueprint
+
+## Obj-C/Swift specific
+*.hmap
+*.ipa
+*.dSYM.zip
+*.dSYM
+
+## Playgrounds
+timeline.xctimeline
+playground.xcworkspace
+
+# Swift Package Manager
+#
+# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
+# Packages/
+# Package.pins
+# Package.resolved
+.build/
+
+# CocoaPods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# Pods/
+#
+# Add this line if you want to avoid checking in source code from the Xcode workspace
+# *.xcworkspace
+
+# Carthage
+#
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://docs.fastlane.tools/best-practices/source-control/#source-control
+
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots/**/*.png
+fastlane/test_output
+
+# Code Injection
+#
+# After new code Injection tools there's a generated folder /iOSInjectionProject
+# https://github.com/johnno1962/injectionforxcode
+
+iOSInjectionProject/
+
diff --git a/vendor/gitignore/KiCad.gitignore b/vendor/gitignore/KiCad.gitignore
index 198392e551e..15fdf72ed48 100644
--- a/vendor/gitignore/KiCad.gitignore
+++ b/vendor/gitignore/KiCad.gitignore
@@ -9,7 +9,6 @@
*~
_autosave-*
*.tmp
-*-cache.lib
*-rescue.lib
*-save.pro
*-save.kicad_pcb
diff --git a/vendor/gitignore/Laravel.gitignore b/vendor/gitignore/Laravel.gitignore
index 67e2146f2bc..6552ddf8a06 100644
--- a/vendor/gitignore/Laravel.gitignore
+++ b/vendor/gitignore/Laravel.gitignore
@@ -1,4 +1,4 @@
-vendor/
+/vendor/
node_modules/
npm-debug.log
yarn-error.log
diff --git a/vendor/gitignore/Magento.gitignore b/vendor/gitignore/Magento.gitignore
index 6f1fa223992..abe6d79fedb 100644
--- a/vendor/gitignore/Magento.gitignore
+++ b/vendor/gitignore/Magento.gitignore
@@ -2,6 +2,8 @@
# Magento Default Files #
#--------------------------#
+/PATCH_*.sh
+
/app/etc/local.xml
/media/*
diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore
index 3a4c8581b3a..e1da6ae8ea5 100644
--- a/vendor/gitignore/Node.gitignore
+++ b/vendor/gitignore/Node.gitignore
@@ -20,7 +20,7 @@ coverage
# nyc test coverage
.nyc_output
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
@@ -71,3 +71,6 @@ typings/
# Serverless directories
.serverless
+
+# FuseBox cache
+.fusebox/
diff --git a/vendor/gitignore/Processing.gitignore b/vendor/gitignore/Processing.gitignore
index 85f269a89f6..333c0e0890a 100644
--- a/vendor/gitignore/Processing.gitignore
+++ b/vendor/gitignore/Processing.gitignore
@@ -1,5 +1,7 @@
.DS_Store
applet
+application.linux-arm64
+application.linux-armv6hf
application.linux32
application.linux64
application.windows32
diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore
index 894a44cc066..510c73d0fdb 100644
--- a/vendor/gitignore/Python.gitignore
+++ b/vendor/gitignore/Python.gitignore
@@ -38,6 +38,7 @@ pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
+.nox/
.coverage
.coverage.*
.cache
@@ -72,6 +73,10 @@ target/
# Jupyter Notebook
.ipynb_checkpoints
+# IPython
+profile_default/
+ipython_config.py
+
# pyenv
.python-version
@@ -102,3 +107,8 @@ venv.bak/
# mypy
.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
diff --git a/vendor/gitignore/Rails.gitignore b/vendor/gitignore/Rails.gitignore
index e62f78e17bc..38ba1b5b38c 100644
--- a/vendor/gitignore/Rails.gitignore
+++ b/vendor/gitignore/Rails.gitignore
@@ -47,3 +47,16 @@ bower.json
# Ignore node_modules
node_modules/
+# Ignore precompiled javascript packs
+/public/packs
+/public/packs-test
+/public/assets
+
+# Ignore yarn files
+/yarn-error.log
+yarn-debug.log*
+.yarn-integrity
+
+# Ignore uploaded files in development
+/storage/*
+!/storage/.keep \ No newline at end of file
diff --git a/vendor/gitignore/Swift.gitignore b/vendor/gitignore/Swift.gitignore
index b8e04d98e33..7b0d62bc23a 100644
--- a/vendor/gitignore/Swift.gitignore
+++ b/vendor/gitignore/Swift.gitignore
@@ -69,3 +69,10 @@ fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
+
+# Code Injection
+#
+# After new code Injection tools there's a generated folder /iOSInjectionProject
+# https://github.com/johnno1962/injectionforxcode
+
+iOSInjectionProject/
diff --git a/vendor/gitignore/Symfony.gitignore b/vendor/gitignore/Symfony.gitignore
index d098259ffb0..3dab634c188 100644
--- a/vendor/gitignore/Symfony.gitignore
+++ b/vendor/gitignore/Symfony.gitignore
@@ -15,6 +15,10 @@
!var/logs/.gitkeep
!var/sessions/.gitkeep
+# Logs (Symfony4)
+/var/log/*
+!var/log/.gitkeep
+
# Parameters
/app/config/parameters.yml
/app/config/parameters.ini
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index 79a66f9ebfa..753f2b954ff 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -64,6 +64,9 @@ acs-*.bib
# changes
*.soc
+# comment
+*.cut
+
# cprotect
*.cpt
@@ -188,6 +191,9 @@ sympy-plots-for-*.tex/
*.pytxcode
pythontex-files-*/
+# tcolorbox
+*.listing
+
# thmtools
*.loe
@@ -202,6 +208,9 @@ pythontex-files-*/
# easy-todo
*.lod
+# xcolor
+*.xcp
+
# xmpincl
*.xmpi
diff --git a/vendor/gitignore/Terraform.gitignore b/vendor/gitignore/Terraform.gitignore
index d9397e2d7d9..a8935803468 100644
--- a/vendor/gitignore/Terraform.gitignore
+++ b/vendor/gitignore/Terraform.gitignore
@@ -13,3 +13,14 @@ crash.log
# version control.
#
# example.tfvars
+
+# Ignore override files as they are usually used to override resources locally and so
+# are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Include override files you do wish to add to version control using negated pattern
+#
+# !example_override.tf
diff --git a/vendor/gitignore/Unity.gitignore b/vendor/gitignore/Unity.gitignore
index 0210746b1a5..833e6d4291c 100644
--- a/vendor/gitignore/Unity.gitignore
+++ b/vendor/gitignore/Unity.gitignore
@@ -23,6 +23,7 @@ ExportedObj/
*.svd
*.pdb
*.opendb
+*.VC.db
# Unity3D generated meta files
*.pidb.meta
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index 94b41b913fb..4d13c54854e 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -4,6 +4,7 @@
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
+*.rsuser
*.suo
*.user
*.userosscache
@@ -76,6 +77,7 @@ StyleCopReport.xml
*.tlh
*.tmp
*.tmp_proj
+*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
@@ -290,8 +292,8 @@ paket-files/
.idea/
*.sln.iml
-# CodeRush
-.cr/
+# CodeRush personal settings
+.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
diff --git a/vendor/gitlab-ci-yml/.gitlab-ci.yml b/vendor/gitlab-ci-yml/.gitlab-ci.yml
deleted file mode 100644
index e2a55163682..00000000000
--- a/vendor/gitlab-ci-yml/.gitlab-ci.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-image: ruby:2.4-alpine
-
-test:
- script: ./verify_templates.rb
diff --git a/vendor/gitlab-ci-yml/Android.gitlab-ci.yml b/vendor/gitlab-ci-yml/Android.gitlab-ci.yml
deleted file mode 100644
index 5f9d54ff574..00000000000
--- a/vendor/gitlab-ci-yml/Android.gitlab-ci.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-# Read more about this script on this blog post https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/, by Greyson Parrelli
-image: openjdk:8-jdk
-
-variables:
- ANDROID_COMPILE_SDK: "25"
- ANDROID_BUILD_TOOLS: "24.0.0"
- ANDROID_SDK_TOOLS: "24.4.1"
-
-before_script:
- - apt-get --quiet update --yes
- - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
- - wget --quiet --output-document=android-sdk.tgz https://dl.google.com/android/android-sdk_r${ANDROID_SDK_TOOLS}-linux.tgz
- - tar --extract --gzip --file=android-sdk.tgz
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter android-${ANDROID_COMPILE_SDK}
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter platform-tools
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter build-tools-${ANDROID_BUILD_TOOLS}
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-android-m2repository
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-google-google_play_services
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-google-m2repository
- - export ANDROID_HOME=$PWD/android-sdk-linux
- - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
- - chmod +x ./gradlew
-
-stages:
- - build
- - test
-
-build:
- stage: build
- script:
- - ./gradlew assembleDebug
- artifacts:
- paths:
- - app/build/outputs/
-
-unitTests:
- stage: test
- script:
- - ./gradlew test
-
-functionalTests:
- stage: test
- script:
- - wget --quiet --output-document=android-wait-for-emulator https://raw.githubusercontent.com/travis-ci/travis-cookbooks/0f497eb71291b52a703143c5cd63a217c8766dc9/community-cookbooks/android-sdk/files/default/android-wait-for-emulator
- - chmod +x android-wait-for-emulator
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter sys-img-x86-google_apis-${ANDROID_COMPILE_SDK}
- - echo no | android-sdk-linux/tools/android create avd -n test -t android-${ANDROID_COMPILE_SDK} --abi google_apis/x86
- - android-sdk-linux/tools/emulator64-x86 -avd test -no-window -no-audio &
- - ./android-wait-for-emulator
- - adb shell input keyevent 82
- - ./gradlew cAT
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
deleted file mode 100644
index dae80c6542d..00000000000
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ /dev/null
@@ -1,833 +0,0 @@
-# Auto DevOps
-# This CI/CD configuration provides a standard pipeline for
-# * building a Docker image (using a buildpack if necessary),
-# * storing the image in the container registry,
-# * running tests from a buildpack,
-# * running code quality analysis,
-# * creating a review app for each topic branch,
-# * and continuous deployment to production
-#
-# Test jobs may be disabled by setting environment variables:
-# * test: TEST_DISABLED
-# * code_quality: CODE_QUALITY_DISABLED
-# * license_management: LICENSE_MANAGEMENT_DISABLED
-# * performance: PERFORMANCE_DISABLED
-# * sast: SAST_DISABLED
-# * dependency_scanning: DEPENDENCY_SCANNING_DISABLED
-# * container_scanning: CONTAINER_SCANNING_DISABLED
-# * dast: DAST_DISABLED
-# * review: REVIEW_DISABLED
-# * stop_review: REVIEW_DISABLED
-#
-# In order to deploy, you must have a Kubernetes cluster configured either
-# via a project integration, or via group/project variables.
-# AUTO_DEVOPS_DOMAIN must also be set as a variable at the group or project
-# level, or manually added below.
-#
-# Continuous deployment to production is enabled by default.
-# If you want to deploy to staging first, or enable incremental rollouts,
-# set STAGING_ENABLED or INCREMENTAL_ROLLOUT_ENABLED environment variables.
-# If you want to use canary deployments, set CANARY_ENABLED environment variable.
-#
-# If Auto DevOps fails to detect the proper buildpack, or if you want to
-# specify a custom buildpack, set a project variable `BUILDPACK_URL` to the
-# repository URL of the buildpack.
-# e.g. BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-ruby.git#v142
-# If you need multiple buildpacks, add a file to your project called
-# `.buildpacks` that contains the URLs, one on each line, in order.
-# Note: Auto CI does not work with multiple buildpacks yet
-
-image: alpine:latest
-
-variables:
- # AUTO_DEVOPS_DOMAIN is the application deployment domain and should be set as a variable at the group or project level.
- # AUTO_DEVOPS_DOMAIN: domain.example.com
-
- POSTGRES_USER: user
- POSTGRES_PASSWORD: testing-password
- POSTGRES_ENABLED: "true"
- POSTGRES_DB: $CI_ENVIRONMENT_SLUG
-
- KUBERNETES_VERSION: 1.8.6
- HELM_VERSION: 2.6.1
-
-stages:
- - build
- - test
- - review
- - dast
- - staging
- - canary
- - production
- - performance
- - cleanup
-
-build:
- stage: build
- image: docker:stable-git
- services:
- - docker:stable-dind
- variables:
- DOCKER_DRIVER: overlay2
- script:
- - setup_docker
- - build
- only:
- - branches
-
-test:
- services:
- - postgres:latest
- variables:
- POSTGRES_DB: test
- stage: test
- image: gliderlabs/herokuish:latest
- script:
- - setup_test_db
- - cp -R . /tmp/app
- - /bin/herokuish buildpack test
- only:
- - branches
- except:
- variables:
- - $TEST_DISABLED
-
-code_quality:
- stage: test
- image: docker:stable
- variables:
- DOCKER_DRIVER: overlay2
- allow_failure: true
- services:
- - docker:stable-dind
- script:
- - setup_docker
- - code_quality
- artifacts:
- paths: [gl-code-quality-report.json]
- only:
- - branches
- except:
- variables:
- - $CODE_QUALITY_DISABLED
-
-license_management:
- stage: test
- image: docker:stable
- variables:
- DOCKER_DRIVER: overlay2
- allow_failure: true
- services:
- - docker:stable-dind
- script:
- - setup_docker
- - license_management
- artifacts:
- paths: [gl-license-management-report.json]
- only:
- - branches
- except:
- variables:
- - $LICENSE_MANAGEMENT_DISABLED
-
-performance:
- stage: performance
- image: docker:stable
- variables:
- DOCKER_DRIVER: overlay2
- allow_failure: true
- services:
- - docker:stable-dind
- script:
- - setup_docker
- - performance
- artifacts:
- paths:
- - performance.json
- - sitespeed-results/
- only:
- refs:
- - branches
- kubernetes: active
- except:
- variables:
- - $PERFORMANCE_DISABLED
-
-sast:
- stage: test
- image: docker:stable
- variables:
- DOCKER_DRIVER: overlay2
- allow_failure: true
- services:
- - docker:stable-dind
- script:
- - setup_docker
- - sast
- artifacts:
- paths: [gl-sast-report.json]
- only:
- - branches
- except:
- variables:
- - $SAST_DISABLED
-
-dependency_scanning:
- stage: test
- image: docker:stable
- variables:
- DOCKER_DRIVER: overlay2
- allow_failure: true
- services:
- - docker:stable-dind
- script:
- - setup_docker
- - dependency_scanning
- artifacts:
- paths: [gl-dependency-scanning-report.json]
- only:
- - branches
- except:
- variables:
- - $DEPENDENCY_SCANNING_DISABLED
-
-container_scanning:
- stage: test
- image: docker:stable
- variables:
- DOCKER_DRIVER: overlay2
- allow_failure: true
- services:
- - docker:stable-dind
- script:
- - setup_docker
- - container_scanning
- artifacts:
- paths: [gl-container-scanning-report.json]
- only:
- - branches
- except:
- variables:
- - $CONTAINER_SCANNING_DISABLED
-
-dast:
- stage: dast
- allow_failure: true
- image: registry.gitlab.com/gitlab-org/security-products/zaproxy
- variables:
- POSTGRES_DB: "false"
- script:
- - dast
- artifacts:
- paths: [gl-dast-report.json]
- only:
- refs:
- - branches
- kubernetes: active
- except:
- refs:
- - master
- variables:
- - $DAST_DISABLED
-
-review:
- stage: review
- script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - install_tiller
- - create_secret
- - deploy
- - persist_environment_url
- environment:
- name: review/$CI_COMMIT_REF_NAME
- url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$AUTO_DEVOPS_DOMAIN
- on_stop: stop_review
- artifacts:
- paths: [environment_url.txt]
- only:
- refs:
- - branches
- kubernetes: active
- except:
- refs:
- - master
- variables:
- - $REVIEW_DISABLED
-
-stop_review:
- stage: cleanup
- variables:
- GIT_STRATEGY: none
- script:
- - install_dependencies
- - delete
- environment:
- name: review/$CI_COMMIT_REF_NAME
- action: stop
- when: manual
- allow_failure: true
- only:
- refs:
- - branches
- kubernetes: active
- except:
- refs:
- - master
- variables:
- - $REVIEW_DISABLED
-
-# Keys that start with a dot (.) will not be processed by GitLab CI.
-# Staging and canary jobs are disabled by default, to enable them
-# remove the dot (.) before the job name.
-# https://docs.gitlab.com/ee/ci/yaml/README.html#hidden-keys
-
-# Staging deploys are disabled by default since
-# continuous deployment to production is enabled by default
-# If you prefer to automatically deploy to staging and
-# only manually promote to production, enable this job by setting
-# STAGING_ENABLED.
-
-staging:
- stage: staging
- script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - install_tiller
- - create_secret
- - deploy
- environment:
- name: staging
- url: http://$CI_PROJECT_PATH_SLUG-staging.$AUTO_DEVOPS_DOMAIN
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $STAGING_ENABLED
-
-# Canaries are also disabled by default, but if you want them,
-# and know what the downsides are, you can enable this by setting
-# CANARY_ENABLED.
-
-canary:
- stage: canary
- script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - install_tiller
- - create_secret
- - deploy canary
- environment:
- name: production
- url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
- when: manual
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $CANARY_ENABLED
-
-.production: &production_template
- stage: production
- script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - install_tiller
- - create_secret
- - deploy
- - delete canary
- - delete rollout
- - persist_environment_url
- environment:
- name: production
- url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
- artifacts:
- paths: [environment_url.txt]
-
-production:
- <<: *production_template
- only:
- refs:
- - master
- kubernetes: active
- except:
- variables:
- - $STAGING_ENABLED
- - $CANARY_ENABLED
- - $INCREMENTAL_ROLLOUT_ENABLED
-
-production_manual:
- <<: *production_template
- when: manual
- allow_failure: false
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $STAGING_ENABLED
- - $CANARY_ENABLED
- except:
- variables:
- - $INCREMENTAL_ROLLOUT_ENABLED
-
-# This job implements incremental rollout on for every push to `master`.
-
-.rollout: &rollout_template
- stage: production
- script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - install_tiller
- - create_secret
- - deploy rollout $ROLLOUT_PERCENTAGE
- - scale stable $((100-ROLLOUT_PERCENTAGE))
- - delete canary
- - persist_environment_url
- environment:
- name: production
- url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
- artifacts:
- paths: [environment_url.txt]
-
-rollout 10%:
- <<: *rollout_template
- variables:
- ROLLOUT_PERCENTAGE: 10
- when: manual
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $INCREMENTAL_ROLLOUT_ENABLED
-
-rollout 25%:
- <<: *rollout_template
- variables:
- ROLLOUT_PERCENTAGE: 25
- when: manual
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $INCREMENTAL_ROLLOUT_ENABLED
-
-rollout 50%:
- <<: *rollout_template
- variables:
- ROLLOUT_PERCENTAGE: 50
- when: manual
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $INCREMENTAL_ROLLOUT_ENABLED
-
-rollout 100%:
- <<: *production_template
- when: manual
- allow_failure: false
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $INCREMENTAL_ROLLOUT_ENABLED
-
-# ---------------------------------------------------------------------------
-
-.auto_devops: &auto_devops |
- # Auto DevOps variables and functions
- [[ "$TRACE" ]] && set -x
- auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${CI_ENVIRONMENT_SLUG}-postgres:5432/${POSTGRES_DB}
- export DATABASE_URL=${DATABASE_URL-$auto_database_url}
- export CI_APPLICATION_REPOSITORY=$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG
- export CI_APPLICATION_TAG=$CI_COMMIT_SHA
- export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID}
- export TILLER_NAMESPACE=$KUBE_NAMESPACE
- # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
-
- function container_scanning() {
- if [[ -n "$CI_REGISTRY_USER" ]]; then
- echo "Logging to GitLab Container Registry with CI credentials..."
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
- echo ""
- fi
-
- docker run -d --name db arminc/clair-db:latest
- docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.1
- apk add -U wget ca-certificates
- docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
- wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
- mv clair-scanner_linux_amd64 clair-scanner
- chmod +x clair-scanner
- touch clair-whitelist.yml
- retries=0
- echo "Waiting for clair daemon to start"
- while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done
- ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
- }
-
- function code_quality() {
- docker run --env SOURCE_CODE="$PWD" \
- --volume "$PWD":/code \
- --volume /var/run/docker.sock:/var/run/docker.sock \
- "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
- }
-
- function license_management() {
- if echo $GITLAB_FEATURES |grep license_management > /dev/null ; then
- # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable"
- LICENSE_MANAGEMENT_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
-
- docker run --volume "$PWD:/code" \
- "registry.gitlab.com/gitlab-org/security-products/license-management:$LICENSE_MANAGEMENT_VERSION" analyze /code
- else
- echo "License management is not available in your subscription"
- fi
- }
-
- function sast() {
- case "$CI_SERVER_VERSION" in
- *-ee)
-
- # Deprecation notice for CONFIDENCE_LEVEL variable
- if [ -z "$SAST_CONFIDENCE_LEVEL" -a "$CONFIDENCE_LEVEL" ]; then
- SAST_CONFIDENCE_LEVEL="$CONFIDENCE_LEVEL"
- echo "WARNING: CONFIDENCE_LEVEL is deprecated and MUST be replaced with SAST_CONFIDENCE_LEVEL"
- fi
-
- docker run --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" \
- --volume "$PWD:/code" \
- --volume /var/run/docker.sock:/var/run/docker.sock \
- "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
- ;;
- *)
- echo "GitLab EE is required"
- ;;
- esac
- }
-
- function dependency_scanning() {
- case "$CI_SERVER_VERSION" in
- *-ee)
- docker run --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}" \
- --volume "$PWD:/code" \
- --volume /var/run/docker.sock:/var/run/docker.sock \
- "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
- ;;
- *)
- echo "GitLab EE is required"
- ;;
- esac
- }
-
- function get_replicas() {
- track="${1:-stable}"
- percentage="${2:-100}"
-
- env_track=$( echo $track | tr -s '[:lower:]' '[:upper:]' )
- env_slug=$( echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]' )
-
- if [[ "$track" == "stable" ]] || [[ "$track" == "rollout" ]]; then
- # for stable track get number of replicas from `PRODUCTION_REPLICAS`
- eval new_replicas=\$${env_slug}_REPLICAS
- if [[ -z "$new_replicas" ]]; then
- new_replicas=$REPLICAS
- fi
- else
- # for all tracks get number of replicas from `CANARY_PRODUCTION_REPLICAS`
- eval new_replicas=\$${env_track}_${env_slug}_REPLICAS
- if [[ -z "$new_replicas" ]]; then
- eval new_replicas=\${env_track}_REPLICAS
- fi
- fi
-
- replicas="${new_replicas:-1}"
- replicas="$(($replicas * $percentage / 100))"
-
- # always return at least one replicas
- if [[ $replicas -gt 0 ]]; then
- echo "$replicas"
- else
- echo 1
- fi
- }
-
- function deploy() {
- track="${1-stable}"
- percentage="${2:-100}"
- name="$CI_ENVIRONMENT_SLUG"
-
- replicas="1"
- service_enabled="true"
- postgres_enabled="$POSTGRES_ENABLED"
-
- # if track is different than stable,
- # re-use all attached resources
- if [[ "$track" != "stable" ]]; then
- name="$name-$track"
- service_enabled="false"
- postgres_enabled="false"
- fi
-
- replicas=$(get_replicas "$track" "$percentage")
-
- if [[ "$CI_PROJECT_VISIBILITY" != "public" ]]; then
- secret_name='gitlab-registry'
- else
- secret_name=''
- fi
-
- helm upgrade --install \
- --wait \
- --set service.enabled="$service_enabled" \
- --set releaseOverride="$CI_ENVIRONMENT_SLUG" \
- --set image.repository="$CI_APPLICATION_REPOSITORY" \
- --set image.tag="$CI_APPLICATION_TAG" \
- --set image.pullPolicy=IfNotPresent \
- --set image.secrets[0].name="$secret_name" \
- --set application.track="$track" \
- --set application.database_url="$DATABASE_URL" \
- --set service.url="$CI_ENVIRONMENT_URL" \
- --set replicaCount="$replicas" \
- --set postgresql.enabled="$postgres_enabled" \
- --set postgresql.nameOverride="postgres" \
- --set postgresql.postgresUser="$POSTGRES_USER" \
- --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
- --set postgresql.postgresDatabase="$POSTGRES_DB" \
- --namespace="$KUBE_NAMESPACE" \
- --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
- "$name" \
- chart/
- }
-
- function scale() {
- track="${1-stable}"
- percentage="${2-100}"
- name="$CI_ENVIRONMENT_SLUG"
-
- if [[ "$track" != "stable" ]]; then
- name="$name-$track"
- fi
-
- replicas=$(get_replicas "$track" "$percentage")
-
- if [[ -n "$(helm ls -q "^$name$")" ]]; then
- helm upgrade --reuse-values \
- --wait \
- --set replicaCount="$replicas" \
- --namespace="$KUBE_NAMESPACE" \
- "$name" \
- chart/
- fi
- }
-
- function install_dependencies() {
- apk add -U openssl curl tar gzip bash ca-certificates git
- wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://raw.githubusercontent.com/sgerrand/alpine-pkg-glibc/master/sgerrand.rsa.pub
- wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.23-r3/glibc-2.23-r3.apk
- apk add glibc-2.23-r3.apk
- rm glibc-2.23-r3.apk
-
- curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
- mv linux-amd64/helm /usr/bin/
- helm version --client
-
- curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
- chmod +x /usr/bin/kubectl
- kubectl version --client
- }
-
- function setup_docker() {
- if ! docker info &>/dev/null; then
- if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
- export DOCKER_HOST='tcp://localhost:2375'
- fi
- fi
- }
-
- function setup_test_db() {
- if [ -z ${KUBERNETES_PORT+x} ]; then
- DB_HOST=postgres
- else
- DB_HOST=localhost
- fi
- export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DB_HOST}:5432/${POSTGRES_DB}"
- }
-
- function download_chart() {
- if [[ ! -d chart ]]; then
- auto_chart=${AUTO_DEVOPS_CHART:-gitlab/auto-deploy-app}
- auto_chart_name=$(basename $auto_chart)
- auto_chart_name=${auto_chart_name%.tgz}
- auto_chart_name=${auto_chart_name%.tar.gz}
- else
- auto_chart="chart"
- auto_chart_name="chart"
- fi
-
- helm init --client-only
- helm repo add gitlab https://charts.gitlab.io
- if [[ ! -d "$auto_chart" ]]; then
- helm fetch ${auto_chart} --untar
- fi
- if [ "$auto_chart_name" != "chart" ]; then
- mv ${auto_chart_name} chart
- fi
-
- helm dependency update chart/
- helm dependency build chart/
- }
-
- function ensure_namespace() {
- kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
- }
-
- function check_kube_domain() {
- if [ -z ${AUTO_DEVOPS_DOMAIN+x} ]; then
- echo "In order to deploy or use Review Apps, AUTO_DEVOPS_DOMAIN variable must be set"
- echo "You can do it in Auto DevOps project settings or defining a variable at group or project level"
- echo "You can also manually add it in .gitlab-ci.yml"
- false
- else
- true
- fi
- }
-
- function build() {
- if [[ -n "$CI_REGISTRY_USER" ]]; then
- echo "Logging to GitLab Container Registry with CI credentials..."
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
- echo ""
- fi
-
- if [[ -f Dockerfile ]]; then
- echo "Building Dockerfile-based application..."
- docker build \
- --build-arg HTTP_PROXY="$HTTP_PROXY" \
- --build-arg http_proxy="$http_proxy" \
- --build-arg HTTPS_PROXY="$HTTPS_PROXY" \
- --build-arg https_proxy="$https_proxy" \
- --build-arg FTP_PROXY="$FTP_PROXY" \
- --build-arg ftp_proxy="$ftp_proxy" \
- --build-arg NO_PROXY="$NO_PROXY" \
- --build-arg no_proxy="$no_proxy" \
- -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
- else
- echo "Building Heroku-based application using gliderlabs/herokuish docker image..."
- docker run -i \
- -e BUILDPACK_URL \
- -e HTTP_PROXY \
- -e http_proxy \
- -e HTTPS_PROXY \
- -e https_proxy \
- -e FTP_PROXY \
- -e ftp_proxy \
- -e NO_PROXY \
- -e no_proxy \
- --name="$CI_CONTAINER_NAME" -v "$(pwd):/tmp/app:ro" gliderlabs/herokuish /bin/herokuish buildpack build
- docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
- docker rm "$CI_CONTAINER_NAME" >/dev/null
- echo ""
-
- echo "Configuring $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG docker image..."
- docker create --expose 5000 --env PORT=5000 --name="$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" /bin/herokuish procfile start web
- docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
- docker rm "$CI_CONTAINER_NAME" >/dev/null
- echo ""
- fi
-
- echo "Pushing to GitLab Container Registry..."
- docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
- echo ""
- }
-
- function install_tiller() {
- echo "Checking Tiller..."
- helm init --upgrade
- kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy"
- if ! helm version --debug; then
- echo "Failed to init Tiller."
- return 1
- fi
- echo ""
- }
-
- function create_secret() {
- echo "Create secret..."
- if [[ "$CI_PROJECT_VISIBILITY" == "public" ]]; then
- return
- fi
-
- kubectl create secret -n "$KUBE_NAMESPACE" \
- docker-registry gitlab-registry \
- --docker-server="$CI_REGISTRY" \
- --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \
- --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \
- --docker-email="$GITLAB_USER_EMAIL" \
- -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f -
- }
-
- function dast() {
- export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
-
- mkdir /zap/wrk/
- /zap/zap-baseline.py -J gl-dast-report.json -t "$CI_ENVIRONMENT_URL" || true
- cp /zap/wrk/gl-dast-report.json .
- }
-
- function performance() {
- export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
-
- mkdir gitlab-exporter
- wget -O gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js
-
- mkdir sitespeed-results
-
- if [ -f .gitlab-urls.txt ]
- then
- sed -i -e 's@^@'"$CI_ENVIRONMENT_URL"'@' .gitlab-urls.txt
- docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results .gitlab-urls.txt
- else
- docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL"
- fi
-
- mv sitespeed-results/data/performance.json performance.json
- }
-
- function persist_environment_url() {
- echo $CI_ENVIRONMENT_URL > environment_url.txt
- }
-
- function delete() {
- track="${1-stable}"
- name="$CI_ENVIRONMENT_SLUG"
-
- if [[ "$track" != "stable" ]]; then
- name="$name-$track"
- fi
-
- if [[ -n "$(helm ls -q "^$name$")" ]]; then
- helm delete "$name"
- fi
- }
-
-before_script:
- - *auto_devops
diff --git a/vendor/gitlab-ci-yml/CONTRIBUTING.md b/vendor/gitlab-ci-yml/CONTRIBUTING.md
deleted file mode 100644
index d33a1f06f26..00000000000
--- a/vendor/gitlab-ci-yml/CONTRIBUTING.md
+++ /dev/null
@@ -1,46 +0,0 @@
-## Developer Certificate of Origin + License
-
-By contributing to GitLab B.V., You accept and agree to the following terms and
-conditions for Your present and future Contributions submitted to GitLab B.V.
-Except for the license granted herein to GitLab B.V. and recipients of software
-distributed by GitLab B.V., You reserve all right, title, and interest in and to
-Your Contributions. All Contributions are subject to the following DCO + License
-terms.
-
-[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md)
-
-_This notice should stay as the first item in the CONTRIBUTING.md file._
-
-## Code of conduct
-
-As contributors and maintainers of this project, we pledge to respect all people
-who contribute through reporting issues, posting feature requests, updating
-documentation, submitting pull requests or patches, and other activities.
-
-We are committed to making participation in this project a harassment-free
-experience for everyone, regardless of level of experience, gender, gender
-identity and expression, sexual orientation, disability, personal appearance,
-body size, race, ethnicity, age, or religion.
-
-Examples of unacceptable behavior by participants include the use of sexual
-language or imagery, derogatory comments or personal attacks, trolling, public
-or private harassment, insults, or other unprofessional conduct.
-
-Project maintainers have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are
-not aligned to this Code of Conduct. Project maintainers who do not follow the
-Code of Conduct may be removed from the project team.
-
-This code of conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community.
-
-Instances of abusive, harassing, or otherwise unacceptable behavior can be
-reported by emailing contact@gitlab.com.
-
-This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
-available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
-
-[contributor-covenant]: http://contributor-covenant.org
-[individual-agreement]: https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html
-[corporate-agreement]: https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html
-
diff --git a/vendor/gitlab-ci-yml/Julia.gitlab-ci.yml b/vendor/gitlab-ci-yml/Julia.gitlab-ci.yml
deleted file mode 100644
index 140cb4635f3..00000000000
--- a/vendor/gitlab-ci-yml/Julia.gitlab-ci.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-# An example .gitlab-ci.yml file to test (and optionally report the coverage
-# results of) your [Julia][1] packages. Please refer to the [documentation][2]
-# for more information about package development in Julia.
-#
-# Here, it is assumed that your Julia package is named `MyPackage`. Change it to
-# whatever name you have given to your package.
-#
-# [1]: http://julialang.org/
-# [2]: http://julia.readthedocs.org/
-
-# Below is the template to run your tests in Julia
-.test_template: &test_definition
- # Uncomment below if you would like to run the tests on specific references
- # only, such as the branches `master`, `development`, etc.
- # only:
- # - master
- # - development
- script:
- # Let's run the tests. Substitute `coverage = false` below, if you do not
- # want coverage results.
- - /opt/julia/bin/julia -e 'Pkg.clone(pwd()); Pkg.test("MyPackage",
- coverage = true)'
- # Comment out below if you do not want coverage results.
- - /opt/julia/bin/julia -e 'Pkg.add("Coverage"); cd(Pkg.dir("MyPackage"));
- using Coverage; cl, tl = get_summary(process_folder());
- println("(", cl/tl*100, "%) covered")'
-
-# Name a test and select an appropriate image.
-test:0.4.6:
- image: julialang/julia:v0.4.6
- <<: *test_definition
-
-# Maybe you would like to test your package against the development branch:
-test:0.5.0-dev:
- image: julialang/julia:v0.5.0-dev
- # ... allowing for failures, since we are testing against the development
- # branch:
- allow_failure: true
- <<: *test_definition
-
-# REMARK: Do not forget to enable the coverage feature for your project, if you
-# are using code coverage reporting above. This can be done by
-#
-# - Navigating to the `CI/CD Pipelines` settings of your project,
-# - Copying and pasting the default `Simplecov` regex example provided, i.e.,
-# `\(\d+.\d+\%\) covered` in the `test coverage parsing` textfield.
-#
-# WARNING: This template is using the `julialang/julia` images from [Docker
-# Hub][3]. One can use custom Julia images and/or the official ones found
-# in the same place. However, care must be taken to correctly locate the binary
-# file (`/opt/julia/bin/julia` above), which is usually given on the image's
-# description page.
-#
-# [3]: http://hub.docker.com/
diff --git a/vendor/gitlab-ci-yml/LICENSE b/vendor/gitlab-ci-yml/LICENSE
deleted file mode 100644
index 27a215686e7..00000000000
--- a/vendor/gitlab-ci-yml/LICENSE
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 2011-2017 GitLab B.V.
-
-With regard to the GitLab Software:
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-For all third party components incorporated into the GitLab Software, those
-components are licensed under the original license provided by the owner of the
-applicable component.
diff --git a/vendor/gitlab-ci-yml/Laravel.gitlab-ci.yml b/vendor/gitlab-ci-yml/Laravel.gitlab-ci.yml
deleted file mode 100644
index 0688f77a1d2..00000000000
--- a/vendor/gitlab-ci-yml/Laravel.gitlab-ci.yml
+++ /dev/null
@@ -1,82 +0,0 @@
-# Official framework image. Look for the different tagged releases at:
-# https://hub.docker.com/r/library/php
-image: php:latest
-
-# Pick zero or more services to be used on all builds.
-# Only needed when using a docker container to run your tests in.
-# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
-services:
- - mysql:latest
-
-variables:
- MYSQL_DATABASE: project_name
- MYSQL_ROOT_PASSWORD: secret
-
-# This folder is cached between builds
-# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
-cache:
- paths:
- - vendor/
- - node_modules/
-
-# This is a basic example for a gem or script which doesn't use
-# services such as redis or postgres
-before_script:
- # Update packages
- - apt-get update -yqq
-
- # Upgrade to Node 7
- - curl -sL https://deb.nodesource.com/setup_7.x | bash -
-
- # Install dependencies
- - apt-get install git nodejs libcurl4-gnutls-dev libicu-dev libmcrypt-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libpq-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev -yqq
-
- # Install php extensions
- - docker-php-ext-install mbstring pdo_mysql curl json intl gd xml zip bz2 opcache
-
- # Install & enable Xdebug for code coverage reports
- - pecl install xdebug
- - docker-php-ext-enable xdebug
-
- # Install Composer and project dependencies.
- - curl -sS https://getcomposer.org/installer | php
- - php composer.phar install
-
- # Install Node dependencies.
- # comment this out if you don't have a node dependency
- - npm install
-
- # Copy over testing configuration.
- # Don't forget to set the database config in .env.testing correctly
- # DB_HOST=mysql
- # DB_DATABASE=project_name
- # DB_USERNAME=root
- # DB_PASSWORD=secret
- - cp .env.testing .env
-
- # Run npm build
- # comment this out if you don't have a frontend build
- # you can change this to to your frontend building script like
- # npm run build
- - npm run dev
-
- # Generate an application key. Re-cache.
- - php artisan key:generate
- - php artisan config:cache
-
- # Run database migrations.
- - php artisan migrate
-
- # Run database seed
- - php artisan db:seed
-
-test:
- script:
- # run laravel tests
- - php vendor/bin/phpunit --coverage-text --colors=never
-
- # run frontend tests
- # if you have any task for testing frontend
- # set it in your package.json script
- # comment this out if you don't have a frontend test
- - npm test
diff --git a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml
deleted file mode 100644
index 5f9c9b2c965..00000000000
--- a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml
+++ /dev/null
@@ -1,102 +0,0 @@
----
-# Build JAVA applications using Apache Maven (http://maven.apache.org)
-# For docker image tags see https://hub.docker.com/_/maven/
-#
-# For general lifecycle information see https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
-#
-# This template will build and test your projects as well as create the documentation.
-#
-# * Caches downloaded dependencies and plugins between invocation.
-# * Verify but don't deploy merge requests.
-# * Deploy built artifacts from master branch only.
-# * Shows how to use multiple jobs in test stage for verifying functionality
-# with multiple JDKs.
-# * Uses site:stage to collect the documentation for multi-module projects.
-# * Publishes the documentation for `master` branch.
-
-variables:
- # This will supress any download for dependencies and plugins or upload messages which would clutter the console log.
- # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
- MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
- # As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used
- # when running from the command line.
- # `installAtEnd` and `deployAtEnd` are only effective with recent version of the corresponding plugins.
- MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"
-
-# Cache downloaded dependencies and plugins between builds.
-# To keep cache across branches add 'key: "$CI_JOB_NAME"'
-cache:
- paths:
- - .m2/repository
-
-# This will only validate and compile stuff and run e.g. maven-enforcer-plugin.
-# Because some enforcer rules might check dependency convergence and class duplications
-# we use `test-compile` here instead of `validate`, so the correct classpath is picked up.
-.validate: &validate
- stage: build
- script:
- - 'mvn $MAVEN_CLI_OPTS test-compile'
-
-# For merge requests do not `deploy` but only run `verify`.
-# See https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
-.verify: &verify
- stage: test
- script:
- - 'mvn $MAVEN_CLI_OPTS verify site site:stage'
- except:
- - master
-
-# Validate merge requests using JDK7
-validate:jdk7:
- <<: *validate
- image: maven:3.3.9-jdk-7
-
-# Validate merge requests using JDK8
-validate:jdk8:
- <<: *validate
- image: maven:3.3.9-jdk-8
-
-# Verify merge requests using JDK7
-verify:jdk7:
- <<: *verify
- image: maven:3.3.9-jdk-7
-
-# Verify merge requests using JDK8
-verify:jdk8:
- <<: *verify
- image: maven:3.3.9-jdk-8
-
-
-# For `master` branch run `mvn deploy` automatically.
-# Here you need to decide whether you want to use JDK7 or 8.
-# To get this working you need to define a volume while configuring your gitlab-ci-multi-runner.
-# Mount your `settings.xml` as `/root/.m2/settings.xml` which holds your secrets.
-# See https://maven.apache.org/settings.html
-deploy:jdk8:
- # Use stage test here, so the pages job may later pickup the created site.
- stage: test
- script:
- - 'mvn $MAVEN_CLI_OPTS deploy site site:stage'
- only:
- - master
- # Archive up the built documentation site.
- artifacts:
- paths:
- - target/staging
- image: maven:3.3.9-jdk-8
-
-
-pages:
- image: busybox:latest
- stage: deploy
- script:
- # Because Maven appends the artifactId automatically to the staging path if you did define a parent pom,
- # you might need to use `mv target/staging/YOUR_ARTIFACT_ID public` instead.
- - mv target/staging public
- dependencies:
- - deploy:jdk8
- artifacts:
- paths:
- - public
- only:
- - master
diff --git a/vendor/gitlab-ci-yml/Python.gitlab-ci.yml b/vendor/gitlab-ci-yml/Python.gitlab-ci.yml
deleted file mode 100644
index 2e0589de652..00000000000
--- a/vendor/gitlab-ci-yml/Python.gitlab-ci.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-# Official language image. Look for the different tagged releases at:
-# https://hub.docker.com/r/library/python/tags/
-image: python:latest
-
-# Change pip's cache directory to be inside the project directory since we can
-# only cache local items.
-variables:
- PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache"
-
-# Pip's cache doesn't store the python packages
-# https://pip.pypa.io/en/stable/reference/pip_install/#caching
-#
-# If you want to also cache the installed packages, you have to install
-# them in a virtualenv and cache it as well.
-cache:
- paths:
- - .cache/pip
- - venv/
-
-before_script:
- - python -V # Print out python version for debugging
- - pip install virtualenv
- - virtualenv venv
- - source venv/bin/activate
-
-test:
- script:
- - python setup.py test
- - pip install tox flake8 # you can also use tox
- - tox -e py36,flake8
-
-run:
- script:
- - python setup.py bdist_wheel
- # an alternative approach is to install and run:
- - pip install dist/*
- # run the command here
- artifacts:
- paths:
- - dist/*.whl
-
-pages:
- script:
- - pip install sphinx sphinx-rtd-theme
- - cd doc ; make html
- - mv build/html/ ../public/
- artifacts:
- paths:
- - public
- only:
- - master
diff --git a/vendor/gitlab-ci-yml/Swift.gitlab-ci.yml b/vendor/gitlab-ci-yml/Swift.gitlab-ci.yml
deleted file mode 100644
index 10d0b05d9f8..00000000000
--- a/vendor/gitlab-ci-yml/Swift.gitlab-ci.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-# Lifted from: https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/
-# This file assumes an own GitLab CI runner, setup on an macOS system.
-stages:
- - build
- - archive
-
-build_project:
- stage: build
- script:
- - xcodebuild clean -project ProjectName.xcodeproj -scheme SchemeName | xcpretty
- - xcodebuild test -project ProjectName.xcodeproj -scheme SchemeName -destination 'platform=iOS Simulator,name=iPhone 8,OS=11.3' | xcpretty -s
- tags:
- - ios_11-3
- - xcode_9-3
- - macos_10-13
-
-archive_project:
- stage: archive
- script:
- - xcodebuild clean archive -archivePath build/ProjectName -scheme SchemeName
- - xcodebuild -exportArchive -exportFormat ipa -archivePath "build/ProjectName.xcarchive" -exportPath "build/ProjectName.ipa" -exportProvisioningProfile "ProvisioningProfileName"
- only:
- - master
- artifacts:
- paths:
- - build/ProjectName.ipa
- tags:
- - ios_11-3
- - xcode_9-3
- - macos_10-13
diff --git a/vendor/jupyter/values.yaml b/vendor/jupyter/values.yaml
index 4ea5b44c59c..24136a7aca5 100644
--- a/vendor/jupyter/values.yaml
+++ b/vendor/jupyter/values.yaml
@@ -13,6 +13,10 @@ auth:
singleuser:
defaultUrl: "/lab"
+ lifecycleHooks:
+ postStart:
+ exec:
+ command: ["sh", "-c", "git clone https://gitlab.com/gitlab-org/nurtch-demo.git DevOps-Runbook-Demo || true"]
ingress:
enabled: true
diff --git a/vendor/languages.yml b/vendor/languages.yml
new file mode 100755
index 00000000000..a6edb0415b2
--- /dev/null
+++ b/vendor/languages.yml
@@ -0,0 +1,5488 @@
+# Extracted from https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
+
+# Defines all Languages known to GitHub.
+#
+# type - Either data, programming, markup, prose, or nil
+# aliases - An Array of additional aliases (implicitly
+# includes name.downcase)
+# ace_mode - A String name of the Ace Mode used for highlighting whenever
+# a file is edited. This must match one of the filenames in http://git.io/3XO_Cg.
+# Use "text" if a mode does not exist.
+# codemirror_mode - A String name of the CodeMirror Mode used for highlighting whenever a file is edited.
+# This must match a mode from https://git.io/vi9Fx
+# codemirror_mime_type - A String name of the file mime type used for highlighting whenever a file is edited.
+# This should match the `mime` associated with the mode from https://git.io/f4SoQ
+# wrap - Boolean wrap to enable line wrapping (default: false)
+# extensions - An Array of associated extensions (the first one is
+# considered the primary extension, the others should be
+# listed alphabetically)
+# filenames - An Array of filenames commonly associated with the language
+# interpreters - An Array of associated interpreters
+# searchable - Boolean flag to enable searching (defaults to true)
+# language_id - Integer used as a language-name-independent indexed field so that we can rename
+# languages in Linguist without reindexing all the code on GitHub. Must not be
+# changed for existing languages without the explicit permission of GitHub staff.
+# color - CSS hex color to represent the language. Only used if type is "programming" or "prose".
+# tm_scope - The TextMate scope that represents this programming
+# language. This should match one of the scopes listed in
+# the grammars.yml file. Use "none" if there is no grammar
+# for this language.
+# group - Name of the parent language. Languages in a group are counted
+# in the statistics as the parent language.
+#
+# Any additions or modifications (even trivial) should have corresponding
+# test changes in `test/test_blob.rb`.
+#
+# Please keep this list alphabetized. Capitalization comes before lowercase.
+
+---
+1C Enterprise:
+ type: programming
+ color: "#814CCC"
+ extensions:
+ - ".bsl"
+ - ".os"
+ tm_scope: source.bsl
+ ace_mode: text
+ language_id: 0
+ABAP:
+ type: programming
+ color: "#E8274B"
+ extensions:
+ - ".abap"
+ ace_mode: abap
+ language_id: 1
+ABNF:
+ type: data
+ ace_mode: text
+ extensions:
+ - ".abnf"
+ tm_scope: source.abnf
+ language_id: 429
+AGS Script:
+ type: programming
+ color: "#B9D9FF"
+ aliases:
+ - ags
+ extensions:
+ - ".asc"
+ - ".ash"
+ tm_scope: source.c++
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-c++src
+ language_id: 2
+AMPL:
+ type: programming
+ color: "#E6EFBB"
+ extensions:
+ - ".ampl"
+ - ".mod"
+ tm_scope: source.ampl
+ ace_mode: text
+ language_id: 3
+ANTLR:
+ type: programming
+ color: "#9DC3FF"
+ extensions:
+ - ".g4"
+ ace_mode: text
+ language_id: 4
+API Blueprint:
+ type: markup
+ color: "#2ACCA8"
+ ace_mode: markdown
+ extensions:
+ - ".apib"
+ tm_scope: text.html.markdown.source.gfm.apib
+ language_id: 5
+APL:
+ type: programming
+ color: "#5A8164"
+ extensions:
+ - ".apl"
+ - ".dyalog"
+ interpreters:
+ - apl
+ - aplx
+ - dyalog
+ tm_scope: source.apl
+ ace_mode: text
+ codemirror_mode: apl
+ codemirror_mime_type: text/apl
+ language_id: 6
+ASN.1:
+ type: data
+ extensions:
+ - ".asn"
+ - ".asn1"
+ tm_scope: source.asn
+ ace_mode: text
+ codemirror_mode: asn.1
+ codemirror_mime_type: text/x-ttcn-asn
+ language_id: 7
+ASP:
+ type: programming
+ color: "#6a40fd"
+ tm_scope: text.html.asp
+ aliases:
+ - aspx
+ - aspx-vb
+ extensions:
+ - ".asp"
+ - ".asax"
+ - ".ascx"
+ - ".ashx"
+ - ".asmx"
+ - ".aspx"
+ - ".axd"
+ ace_mode: text
+ codemirror_mode: htmlembedded
+ codemirror_mime_type: application/x-aspx
+ language_id: 8
+ATS:
+ type: programming
+ color: "#1ac620"
+ aliases:
+ - ats2
+ extensions:
+ - ".dats"
+ - ".hats"
+ - ".sats"
+ tm_scope: source.ats
+ ace_mode: ocaml
+ language_id: 9
+ActionScript:
+ type: programming
+ tm_scope: source.actionscript.3
+ color: "#882B0F"
+ aliases:
+ - actionscript 3
+ - actionscript3
+ - as3
+ extensions:
+ - ".as"
+ ace_mode: actionscript
+ language_id: 10
+Ada:
+ type: programming
+ color: "#02f88c"
+ extensions:
+ - ".adb"
+ - ".ada"
+ - ".ads"
+ aliases:
+ - ada95
+ - ada2005
+ ace_mode: ada
+ language_id: 11
+Adobe Font Metrics:
+ type: data
+ tm_scope: source.afm
+ extensions:
+ - ".afm"
+ aliases:
+ - acfm
+ - adobe composite font metrics
+ - adobe multiple font metrics
+ - amfm
+ ace_mode: text
+ language_id: 147198098
+Agda:
+ type: programming
+ color: "#315665"
+ extensions:
+ - ".agda"
+ ace_mode: text
+ language_id: 12
+Alloy:
+ type: programming
+ color: "#64C800"
+ extensions:
+ - ".als"
+ ace_mode: text
+ language_id: 13
+Alpine Abuild:
+ type: programming
+ group: Shell
+ aliases:
+ - abuild
+ - apkbuild
+ filenames:
+ - APKBUILD
+ tm_scope: source.shell
+ ace_mode: sh
+ codemirror_mode: shell
+ codemirror_mime_type: text/x-sh
+ language_id: 14
+AngelScript:
+ type: programming
+ color: "#C7D7DC"
+ extensions:
+ - ".as"
+ - ".angelscript"
+ tm_scope: source.angelscript
+ ace_mode: text
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-c++src
+ language_id: 389477596
+Ant Build System:
+ type: data
+ tm_scope: text.xml.ant
+ filenames:
+ - ant.xml
+ - build.xml
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: application/xml
+ language_id: 15
+ApacheConf:
+ type: data
+ aliases:
+ - aconf
+ - apache
+ extensions:
+ - ".apacheconf"
+ - ".vhost"
+ filenames:
+ - ".htaccess"
+ - apache2.conf
+ - httpd.conf
+ tm_scope: source.apache-config
+ ace_mode: apache_conf
+ language_id: 16
+Apex:
+ type: programming
+ extensions:
+ - ".cls"
+ tm_scope: source.java
+ ace_mode: java
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-java
+ language_id: 17
+Apollo Guidance Computer:
+ type: programming
+ group: Assembly
+ extensions:
+ - ".agc"
+ tm_scope: source.agc
+ ace_mode: assembly_x86
+ language_id: 18
+AppleScript:
+ type: programming
+ aliases:
+ - osascript
+ extensions:
+ - ".applescript"
+ - ".scpt"
+ interpreters:
+ - osascript
+ ace_mode: applescript
+ color: "#101F1F"
+ language_id: 19
+Arc:
+ type: programming
+ color: "#aa2afe"
+ extensions:
+ - ".arc"
+ tm_scope: none
+ ace_mode: text
+ language_id: 20
+AsciiDoc:
+ type: prose
+ ace_mode: asciidoc
+ wrap: true
+ extensions:
+ - ".asciidoc"
+ - ".adoc"
+ - ".asc"
+ tm_scope: text.html.asciidoc
+ language_id: 22
+AspectJ:
+ type: programming
+ color: "#a957b0"
+ extensions:
+ - ".aj"
+ tm_scope: source.aspectj
+ ace_mode: text
+ language_id: 23
+Assembly:
+ type: programming
+ color: "#6E4C13"
+ aliases:
+ - asm
+ - nasm
+ extensions:
+ - ".asm"
+ - ".a51"
+ - ".inc"
+ - ".nasm"
+ tm_scope: source.assembly
+ ace_mode: assembly_x86
+ language_id: 24
+Augeas:
+ type: programming
+ extensions:
+ - ".aug"
+ tm_scope: none
+ ace_mode: text
+ language_id: 25
+AutoHotkey:
+ type: programming
+ color: "#6594b9"
+ aliases:
+ - ahk
+ extensions:
+ - ".ahk"
+ - ".ahkl"
+ tm_scope: source.ahk
+ ace_mode: autohotkey
+ language_id: 26
+AutoIt:
+ type: programming
+ color: "#1C3552"
+ aliases:
+ - au3
+ - AutoIt3
+ - AutoItScript
+ extensions:
+ - ".au3"
+ tm_scope: source.autoit
+ ace_mode: autohotkey
+ language_id: 27
+Awk:
+ type: programming
+ extensions:
+ - ".awk"
+ - ".auk"
+ - ".gawk"
+ - ".mawk"
+ - ".nawk"
+ interpreters:
+ - awk
+ - gawk
+ - mawk
+ - nawk
+ ace_mode: text
+ language_id: 28
+Ballerina:
+ type: programming
+ extensions:
+ - ".bal"
+ tm_scope: source.ballerina
+ ace_mode: text
+ color: "#FF5000"
+ language_id: 720859680
+Batchfile:
+ type: programming
+ aliases:
+ - bat
+ - batch
+ - dosbatch
+ - winbatch
+ extensions:
+ - ".bat"
+ - ".cmd"
+ tm_scope: source.batchfile
+ ace_mode: batchfile
+ color: "#C1F12E"
+ language_id: 29
+Befunge:
+ type: programming
+ extensions:
+ - ".befunge"
+ ace_mode: text
+ language_id: 30
+Bison:
+ type: programming
+ group: Yacc
+ tm_scope: source.bison
+ extensions:
+ - ".bison"
+ ace_mode: text
+ language_id: 31
+BitBake:
+ type: programming
+ tm_scope: none
+ extensions:
+ - ".bb"
+ ace_mode: text
+ language_id: 32
+Blade:
+ type: markup
+ group: HTML
+ extensions:
+ - ".blade"
+ - ".blade.php"
+ tm_scope: text.html.php.blade
+ ace_mode: text
+ language_id: 33
+BlitzBasic:
+ type: programming
+ aliases:
+ - b3d
+ - blitz3d
+ - blitzplus
+ - bplus
+ extensions:
+ - ".bb"
+ - ".decls"
+ tm_scope: source.blitzmax
+ ace_mode: text
+ language_id: 34
+BlitzMax:
+ type: programming
+ color: "#cd6400"
+ extensions:
+ - ".bmx"
+ aliases:
+ - bmax
+ ace_mode: text
+ language_id: 35
+Bluespec:
+ type: programming
+ extensions:
+ - ".bsv"
+ tm_scope: source.bsv
+ ace_mode: verilog
+ language_id: 36
+Boo:
+ type: programming
+ color: "#d4bec1"
+ extensions:
+ - ".boo"
+ ace_mode: text
+ tm_scope: source.boo
+ language_id: 37
+Brainfuck:
+ type: programming
+ color: "#2F2530"
+ extensions:
+ - ".b"
+ - ".bf"
+ tm_scope: source.bf
+ ace_mode: text
+ codemirror_mode: brainfuck
+ codemirror_mime_type: text/x-brainfuck
+ language_id: 38
+Brightscript:
+ type: programming
+ extensions:
+ - ".brs"
+ tm_scope: source.brightscript
+ ace_mode: text
+ language_id: 39
+Bro:
+ type: programming
+ extensions:
+ - ".bro"
+ ace_mode: text
+ language_id: 40
+C:
+ type: programming
+ color: "#555555"
+ extensions:
+ - ".c"
+ - ".cats"
+ - ".h"
+ - ".idc"
+ interpreters:
+ - tcc
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csrc
+ language_id: 41
+C#:
+ type: programming
+ ace_mode: csharp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csharp
+ tm_scope: source.cs
+ color: "#178600"
+ aliases:
+ - csharp
+ extensions:
+ - ".cs"
+ - ".cake"
+ - ".cshtml"
+ - ".csx"
+ language_id: 42
+C++:
+ type: programming
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-c++src
+ color: "#f34b7d"
+ aliases:
+ - cpp
+ extensions:
+ - ".cpp"
+ - ".c++"
+ - ".cc"
+ - ".cp"
+ - ".cxx"
+ - ".h"
+ - ".h++"
+ - ".hh"
+ - ".hpp"
+ - ".hxx"
+ - ".inc"
+ - ".inl"
+ - ".ino"
+ - ".ipp"
+ - ".re"
+ - ".tcc"
+ - ".tpp"
+ language_id: 43
+C-ObjDump:
+ type: data
+ extensions:
+ - ".c-objdump"
+ tm_scope: objdump.x86asm
+ ace_mode: assembly_x86
+ language_id: 44
+C2hs Haskell:
+ type: programming
+ group: Haskell
+ aliases:
+ - c2hs
+ extensions:
+ - ".chs"
+ tm_scope: source.haskell
+ ace_mode: haskell
+ codemirror_mode: haskell
+ codemirror_mime_type: text/x-haskell
+ language_id: 45
+CLIPS:
+ type: programming
+ extensions:
+ - ".clp"
+ tm_scope: source.clips
+ ace_mode: text
+ language_id: 46
+CMake:
+ type: programming
+ extensions:
+ - ".cmake"
+ - ".cmake.in"
+ filenames:
+ - CMakeLists.txt
+ ace_mode: text
+ codemirror_mode: cmake
+ codemirror_mime_type: text/x-cmake
+ language_id: 47
+COBOL:
+ type: programming
+ extensions:
+ - ".cob"
+ - ".cbl"
+ - ".ccp"
+ - ".cobol"
+ - ".cpy"
+ ace_mode: cobol
+ codemirror_mode: cobol
+ codemirror_mime_type: text/x-cobol
+ language_id: 48
+COLLADA:
+ type: data
+ extensions:
+ - ".dae"
+ tm_scope: text.xml
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ language_id: 49
+CSON:
+ type: data
+ group: CoffeeScript
+ tm_scope: source.coffee
+ ace_mode: coffee
+ codemirror_mode: coffeescript
+ codemirror_mime_type: text/x-coffeescript
+ searchable: false
+ extensions:
+ - ".cson"
+ language_id: 424
+CSS:
+ type: markup
+ tm_scope: source.css
+ ace_mode: css
+ codemirror_mode: css
+ codemirror_mime_type: text/css
+ color: "#563d7c"
+ extensions:
+ - ".css"
+ language_id: 50
+CSV:
+ type: data
+ ace_mode: text
+ tm_scope: none
+ extensions:
+ - ".csv"
+ language_id: 51
+CWeb:
+ type: programming
+ extensions:
+ - ".w"
+ tm_scope: none
+ ace_mode: text
+ language_id: 657332628
+Cap'n Proto:
+ type: programming
+ tm_scope: source.capnp
+ extensions:
+ - ".capnp"
+ ace_mode: text
+ language_id: 52
+CartoCSS:
+ type: programming
+ aliases:
+ - Carto
+ extensions:
+ - ".mss"
+ ace_mode: text
+ tm_scope: source.css.mss
+ language_id: 53
+Ceylon:
+ type: programming
+ color: "#dfa535"
+ extensions:
+ - ".ceylon"
+ tm_scope: source.ceylon
+ ace_mode: text
+ language_id: 54
+Chapel:
+ type: programming
+ color: "#8dc63f"
+ aliases:
+ - chpl
+ extensions:
+ - ".chpl"
+ ace_mode: text
+ language_id: 55
+Charity:
+ type: programming
+ extensions:
+ - ".ch"
+ tm_scope: none
+ ace_mode: text
+ language_id: 56
+ChucK:
+ type: programming
+ extensions:
+ - ".ck"
+ tm_scope: source.java
+ ace_mode: java
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-java
+ language_id: 57
+Cirru:
+ type: programming
+ color: "#ccccff"
+ ace_mode: cirru
+ extensions:
+ - ".cirru"
+ language_id: 58
+Clarion:
+ type: programming
+ color: "#db901e"
+ ace_mode: text
+ extensions:
+ - ".clw"
+ tm_scope: source.clarion
+ language_id: 59
+Clean:
+ type: programming
+ color: "#3F85AF"
+ extensions:
+ - ".icl"
+ - ".dcl"
+ tm_scope: source.clean
+ ace_mode: text
+ language_id: 60
+Click:
+ type: programming
+ color: "#E4E6F3"
+ extensions:
+ - ".click"
+ tm_scope: source.click
+ ace_mode: text
+ language_id: 61
+Clojure:
+ type: programming
+ ace_mode: clojure
+ codemirror_mode: clojure
+ codemirror_mime_type: text/x-clojure
+ color: "#db5855"
+ extensions:
+ - ".clj"
+ - ".boot"
+ - ".cl2"
+ - ".cljc"
+ - ".cljs"
+ - ".cljs.hl"
+ - ".cljscm"
+ - ".cljx"
+ - ".hic"
+ filenames:
+ - riemann.config
+ language_id: 62
+Closure Templates:
+ type: markup
+ group: HTML
+ ace_mode: soy_template
+ codemirror_mode: soy
+ codemirror_mime_type: text/x-soy
+ aliases:
+ - soy
+ extensions:
+ - ".soy"
+ tm_scope: text.html.soy
+ language_id: 357046146
+Cloud Firestore Security Rules:
+ type: data
+ ace_mode: less
+ codemirror_mode: css
+ codemirror_mime_type: text/css
+ tm_scope: source.firestore
+ filenames:
+ - firestore.rules
+ language_id: 407996372
+CoNLL-U:
+ type: data
+ extensions:
+ - ".conllu"
+ - ".conll"
+ tm_scope: text.conllu
+ ace_mode: text
+ aliases:
+ - CoNLL
+ - CoNLL-X
+ language_id: 421026389
+CoffeeScript:
+ type: programming
+ tm_scope: source.coffee
+ ace_mode: coffee
+ codemirror_mode: coffeescript
+ codemirror_mime_type: text/x-coffeescript
+ color: "#244776"
+ aliases:
+ - coffee
+ - coffee-script
+ extensions:
+ - ".coffee"
+ - "._coffee"
+ - ".cake"
+ - ".cjsx"
+ - ".iced"
+ filenames:
+ - Cakefile
+ interpreters:
+ - coffee
+ language_id: 63
+ColdFusion:
+ type: programming
+ ace_mode: coldfusion
+ color: "#ed2cd6"
+ aliases:
+ - cfm
+ - cfml
+ - coldfusion html
+ extensions:
+ - ".cfm"
+ - ".cfml"
+ tm_scope: text.html.cfm
+ language_id: 64
+ColdFusion CFC:
+ type: programming
+ group: ColdFusion
+ ace_mode: coldfusion
+ aliases:
+ - cfc
+ extensions:
+ - ".cfc"
+ tm_scope: source.cfscript
+ language_id: 65
+Common Lisp:
+ type: programming
+ tm_scope: source.lisp
+ color: "#3fb68b"
+ aliases:
+ - lisp
+ extensions:
+ - ".lisp"
+ - ".asd"
+ - ".cl"
+ - ".l"
+ - ".lsp"
+ - ".ny"
+ - ".podsl"
+ - ".sexp"
+ interpreters:
+ - lisp
+ - sbcl
+ - ccl
+ - clisp
+ - ecl
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ language_id: 66
+Common Workflow Language:
+ aliases:
+ - cwl
+ type: programming
+ ace_mode: yaml
+ codemirror_mode: yaml
+ codemirror_mime_type: text/x-yaml
+ extensions:
+ - ".cwl"
+ interpreters:
+ - cwl-runner
+ color: "#B5314C"
+ tm_scope: source.cwl
+ language_id: 988547172
+Component Pascal:
+ type: programming
+ color: "#B0CE4E"
+ extensions:
+ - ".cp"
+ - ".cps"
+ tm_scope: source.pascal
+ aliases:
+ - delphi
+ - objectpascal
+ ace_mode: pascal
+ codemirror_mode: pascal
+ codemirror_mime_type: text/x-pascal
+ language_id: 67
+Cool:
+ type: programming
+ extensions:
+ - ".cl"
+ tm_scope: source.cool
+ ace_mode: text
+ language_id: 68
+Coq:
+ type: programming
+ extensions:
+ - ".coq"
+ - ".v"
+ ace_mode: text
+ language_id: 69
+Cpp-ObjDump:
+ type: data
+ extensions:
+ - ".cppobjdump"
+ - ".c++-objdump"
+ - ".c++objdump"
+ - ".cpp-objdump"
+ - ".cxx-objdump"
+ tm_scope: objdump.x86asm
+ aliases:
+ - c++-objdump
+ ace_mode: assembly_x86
+ language_id: 70
+Creole:
+ type: prose
+ wrap: true
+ extensions:
+ - ".creole"
+ tm_scope: text.html.creole
+ ace_mode: text
+ language_id: 71
+Crystal:
+ type: programming
+ color: "#776791"
+ extensions:
+ - ".cr"
+ ace_mode: ruby
+ codemirror_mode: crystal
+ codemirror_mime_type: text/x-crystal
+ tm_scope: source.crystal
+ interpreters:
+ - crystal
+ language_id: 72
+Csound:
+ type: programming
+ aliases:
+ - csound-orc
+ extensions:
+ - ".orc"
+ - ".udo"
+ tm_scope: source.csound
+ ace_mode: csound_orchestra
+ language_id: 73
+Csound Document:
+ type: programming
+ aliases:
+ - csound-csd
+ extensions:
+ - ".csd"
+ tm_scope: source.csound-document
+ ace_mode: csound_document
+ language_id: 74
+Csound Score:
+ type: programming
+ aliases:
+ - csound-sco
+ extensions:
+ - ".sco"
+ tm_scope: source.csound-score
+ ace_mode: csound_score
+ language_id: 75
+Cuda:
+ type: programming
+ extensions:
+ - ".cu"
+ - ".cuh"
+ tm_scope: source.cuda-c++
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-c++src
+ color: "#3A4E3A"
+ language_id: 77
+Cycript:
+ type: programming
+ extensions:
+ - ".cy"
+ tm_scope: source.js
+ ace_mode: javascript
+ codemirror_mode: javascript
+ codemirror_mime_type: text/javascript
+ language_id: 78
+Cython:
+ type: programming
+ group: Python
+ extensions:
+ - ".pyx"
+ - ".pxd"
+ - ".pxi"
+ aliases:
+ - pyrex
+ ace_mode: text
+ codemirror_mode: python
+ codemirror_mime_type: text/x-cython
+ language_id: 79
+D:
+ type: programming
+ color: "#ba595e"
+ extensions:
+ - ".d"
+ - ".di"
+ ace_mode: d
+ codemirror_mode: d
+ codemirror_mime_type: text/x-d
+ language_id: 80
+D-ObjDump:
+ type: data
+ extensions:
+ - ".d-objdump"
+ tm_scope: objdump.x86asm
+ ace_mode: assembly_x86
+ language_id: 81
+DIGITAL Command Language:
+ type: programming
+ aliases:
+ - dcl
+ extensions:
+ - ".com"
+ tm_scope: none
+ ace_mode: text
+ language_id: 82
+DM:
+ type: programming
+ color: "#447265"
+ extensions:
+ - ".dm"
+ aliases:
+ - byond
+ tm_scope: source.dm
+ ace_mode: c_cpp
+ language_id: 83
+DNS Zone:
+ type: data
+ extensions:
+ - ".zone"
+ - ".arpa"
+ tm_scope: text.zone_file
+ ace_mode: text
+ language_id: 84
+DTrace:
+ type: programming
+ aliases:
+ - dtrace-script
+ extensions:
+ - ".d"
+ interpreters:
+ - dtrace
+ tm_scope: source.c
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csrc
+ language_id: 85
+Darcs Patch:
+ type: data
+ aliases:
+ - dpatch
+ extensions:
+ - ".darcspatch"
+ - ".dpatch"
+ tm_scope: none
+ ace_mode: text
+ language_id: 86
+Dart:
+ type: programming
+ color: "#00B4AB"
+ extensions:
+ - ".dart"
+ interpreters:
+ - dart
+ ace_mode: dart
+ codemirror_mode: dart
+ codemirror_mime_type: application/dart
+ language_id: 87
+DataWeave:
+ type: programming
+ color: "#003a52"
+ extensions:
+ - ".dwl"
+ ace_mode: text
+ tm_scope: source.data-weave
+ language_id: 974514097
+Diff:
+ type: data
+ extensions:
+ - ".diff"
+ - ".patch"
+ aliases:
+ - udiff
+ tm_scope: source.diff
+ ace_mode: diff
+ codemirror_mode: diff
+ codemirror_mime_type: text/x-diff
+ language_id: 88
+Dockerfile:
+ type: programming
+ color: "#0db7ed"
+ tm_scope: source.dockerfile
+ extensions:
+ - ".dockerfile"
+ filenames:
+ - Dockerfile
+ ace_mode: dockerfile
+ codemirror_mode: dockerfile
+ codemirror_mime_type: text/x-dockerfile
+ language_id: 89
+Dogescript:
+ type: programming
+ color: "#cca760"
+ extensions:
+ - ".djs"
+ tm_scope: none
+ ace_mode: text
+ language_id: 90
+Dylan:
+ type: programming
+ color: "#6c616e"
+ extensions:
+ - ".dylan"
+ - ".dyl"
+ - ".intr"
+ - ".lid"
+ ace_mode: text
+ codemirror_mode: dylan
+ codemirror_mime_type: text/x-dylan
+ language_id: 91
+E:
+ type: programming
+ color: "#ccce35"
+ extensions:
+ - ".E"
+ interpreters:
+ - rune
+ tm_scope: none
+ ace_mode: text
+ language_id: 92
+EBNF:
+ type: data
+ extensions:
+ - ".ebnf"
+ tm_scope: source.ebnf
+ ace_mode: text
+ codemirror_mode: ebnf
+ codemirror_mime_type: text/x-ebnf
+ language_id: 430
+ECL:
+ type: programming
+ color: "#8a1267"
+ extensions:
+ - ".ecl"
+ - ".eclxml"
+ tm_scope: none
+ ace_mode: text
+ codemirror_mode: ecl
+ codemirror_mime_type: text/x-ecl
+ language_id: 93
+ECLiPSe:
+ type: programming
+ group: prolog
+ extensions:
+ - ".ecl"
+ tm_scope: source.prolog.eclipse
+ ace_mode: prolog
+ language_id: 94
+EJS:
+ type: markup
+ group: HTML
+ extensions:
+ - ".ejs"
+ tm_scope: text.html.js
+ ace_mode: ejs
+ language_id: 95
+EQ:
+ type: programming
+ color: "#a78649"
+ extensions:
+ - ".eq"
+ tm_scope: source.cs
+ ace_mode: csharp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csharp
+ language_id: 96
+Eagle:
+ type: data
+ extensions:
+ - ".sch"
+ - ".brd"
+ tm_scope: text.xml
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ language_id: 97
+Easybuild:
+ type: data
+ group: Python
+ ace_mode: python
+ codemirror_mode: python
+ codemirror_mime_type: text/x-python
+ tm_scope: source.python
+ extensions:
+ - ".eb"
+ language_id: 342840477
+Ecere Projects:
+ type: data
+ group: JavaScript
+ extensions:
+ - ".epj"
+ tm_scope: source.json
+ ace_mode: json
+ codemirror_mode: javascript
+ codemirror_mime_type: application/json
+ language_id: 98
+Edje Data Collection:
+ type: data
+ extensions:
+ - ".edc"
+ tm_scope: source.json
+ ace_mode: json
+ codemirror_mode: javascript
+ codemirror_mime_type: application/json
+ language_id: 342840478
+Eiffel:
+ type: programming
+ color: "#946d57"
+ extensions:
+ - ".e"
+ ace_mode: eiffel
+ codemirror_mode: eiffel
+ codemirror_mime_type: text/x-eiffel
+ language_id: 99
+Elixir:
+ type: programming
+ color: "#6e4a7e"
+ extensions:
+ - ".ex"
+ - ".exs"
+ ace_mode: elixir
+ filenames:
+ - mix.lock
+ interpreters:
+ - elixir
+ language_id: 100
+Elm:
+ type: programming
+ color: "#60B5CC"
+ extensions:
+ - ".elm"
+ tm_scope: source.elm
+ ace_mode: elm
+ codemirror_mode: elm
+ codemirror_mime_type: text/x-elm
+ language_id: 101
+Emacs Lisp:
+ type: programming
+ tm_scope: source.emacs.lisp
+ color: "#c065db"
+ aliases:
+ - elisp
+ - emacs
+ filenames:
+ - ".abbrev_defs"
+ - ".emacs"
+ - ".emacs.desktop"
+ - ".gnus"
+ - ".spacemacs"
+ - ".viper"
+ - Cask
+ - Project.ede
+ - _emacs
+ - abbrev_defs
+ extensions:
+ - ".el"
+ - ".emacs"
+ - ".emacs.desktop"
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ language_id: 102
+EmberScript:
+ type: programming
+ color: "#FFF4F3"
+ extensions:
+ - ".em"
+ - ".emberscript"
+ tm_scope: source.coffee
+ ace_mode: coffee
+ codemirror_mode: coffeescript
+ codemirror_mime_type: text/x-coffeescript
+ language_id: 103
+Erlang:
+ type: programming
+ color: "#B83998"
+ extensions:
+ - ".erl"
+ - ".app.src"
+ - ".es"
+ - ".escript"
+ - ".hrl"
+ - ".xrl"
+ - ".yrl"
+ filenames:
+ - Emakefile
+ - rebar.config
+ - rebar.config.lock
+ - rebar.lock
+ ace_mode: erlang
+ codemirror_mode: erlang
+ codemirror_mime_type: text/x-erlang
+ interpreters:
+ - escript
+ language_id: 104
+F#:
+ type: programming
+ color: "#b845fc"
+ aliases:
+ - fsharp
+ extensions:
+ - ".fs"
+ - ".fsi"
+ - ".fsx"
+ tm_scope: source.fsharp
+ ace_mode: text
+ codemirror_mode: mllike
+ codemirror_mime_type: text/x-fsharp
+ language_id: 105
+FLUX:
+ type: programming
+ color: "#88ccff"
+ extensions:
+ - ".fx"
+ - ".flux"
+ tm_scope: none
+ ace_mode: text
+ language_id: 106
+Factor:
+ type: programming
+ color: "#636746"
+ extensions:
+ - ".factor"
+ filenames:
+ - ".factor-boot-rc"
+ - ".factor-rc"
+ ace_mode: text
+ codemirror_mode: factor
+ codemirror_mime_type: text/x-factor
+ language_id: 108
+Fancy:
+ type: programming
+ color: "#7b9db4"
+ extensions:
+ - ".fy"
+ - ".fancypack"
+ filenames:
+ - Fakefile
+ ace_mode: text
+ language_id: 109
+Fantom:
+ type: programming
+ color: "#14253c"
+ extensions:
+ - ".fan"
+ tm_scope: source.fan
+ ace_mode: text
+ language_id: 110
+Filebench WML:
+ type: programming
+ extensions:
+ - ".f"
+ tm_scope: none
+ ace_mode: text
+ language_id: 111
+Filterscript:
+ type: programming
+ group: RenderScript
+ extensions:
+ - ".fs"
+ tm_scope: none
+ ace_mode: text
+ language_id: 112
+Formatted:
+ type: data
+ extensions:
+ - ".for"
+ - ".eam.fs"
+ tm_scope: none
+ ace_mode: text
+ language_id: 113
+Forth:
+ type: programming
+ color: "#341708"
+ extensions:
+ - ".fth"
+ - ".4th"
+ - ".f"
+ - ".for"
+ - ".forth"
+ - ".fr"
+ - ".frt"
+ - ".fs"
+ ace_mode: forth
+ codemirror_mode: forth
+ codemirror_mime_type: text/x-forth
+ language_id: 114
+Fortran:
+ type: programming
+ color: "#4d41b1"
+ extensions:
+ - ".f90"
+ - ".f"
+ - ".f03"
+ - ".f08"
+ - ".f77"
+ - ".f95"
+ - ".for"
+ - ".fpp"
+ tm_scope: source.fortran.modern
+ ace_mode: text
+ codemirror_mode: fortran
+ codemirror_mime_type: text/x-fortran
+ language_id: 107
+FreeMarker:
+ type: programming
+ color: "#0050b2"
+ aliases:
+ - ftl
+ extensions:
+ - ".ftl"
+ tm_scope: text.html.ftl
+ ace_mode: ftl
+ language_id: 115
+Frege:
+ type: programming
+ color: "#00cafe"
+ extensions:
+ - ".fr"
+ tm_scope: source.haskell
+ ace_mode: haskell
+ language_id: 116
+G-code:
+ type: data
+ extensions:
+ - ".g"
+ - ".gco"
+ - ".gcode"
+ tm_scope: source.gcode
+ ace_mode: gcode
+ language_id: 117
+GAMS:
+ type: programming
+ extensions:
+ - ".gms"
+ tm_scope: none
+ ace_mode: text
+ language_id: 118
+GAP:
+ type: programming
+ extensions:
+ - ".g"
+ - ".gap"
+ - ".gd"
+ - ".gi"
+ - ".tst"
+ tm_scope: source.gap
+ ace_mode: text
+ language_id: 119
+GCC Machine Description:
+ type: programming
+ extensions:
+ - ".md"
+ tm_scope: source.lisp
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ language_id: 121
+GDB:
+ type: programming
+ extensions:
+ - ".gdb"
+ - ".gdbinit"
+ tm_scope: source.gdb
+ ace_mode: text
+ language_id: 122
+GDScript:
+ type: programming
+ color: "#355570"
+ extensions:
+ - ".gd"
+ tm_scope: source.gdscript
+ ace_mode: text
+ language_id: 123
+GLSL:
+ type: programming
+ extensions:
+ - ".glsl"
+ - ".fp"
+ - ".frag"
+ - ".frg"
+ - ".fs"
+ - ".fsh"
+ - ".fshader"
+ - ".geo"
+ - ".geom"
+ - ".glslv"
+ - ".gshader"
+ - ".shader"
+ - ".tesc"
+ - ".tese"
+ - ".vert"
+ - ".vrx"
+ - ".vsh"
+ - ".vshader"
+ ace_mode: glsl
+ language_id: 124
+GN:
+ type: data
+ extensions:
+ - ".gn"
+ - ".gni"
+ interpreters:
+ - gn
+ filenames:
+ - ".gn"
+ tm_scope: source.gn
+ ace_mode: python
+ codemirror_mode: python
+ codemirror_mime_type: text/x-python
+ language_id: 302957008
+Game Maker Language:
+ type: programming
+ color: "#8fb200"
+ extensions:
+ - ".gml"
+ tm_scope: source.c++
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-c++src
+ language_id: 125
+Genie:
+ type: programming
+ ace_mode: text
+ extensions:
+ - ".gs"
+ color: "#fb855d"
+ tm_scope: none
+ language_id: 792408528
+Genshi:
+ type: programming
+ extensions:
+ - ".kid"
+ tm_scope: text.xml.genshi
+ aliases:
+ - xml+genshi
+ - xml+kid
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ language_id: 126
+Gentoo Ebuild:
+ type: programming
+ group: Shell
+ extensions:
+ - ".ebuild"
+ tm_scope: source.shell
+ ace_mode: sh
+ codemirror_mode: shell
+ codemirror_mime_type: text/x-sh
+ language_id: 127
+Gentoo Eclass:
+ type: programming
+ group: Shell
+ extensions:
+ - ".eclass"
+ tm_scope: source.shell
+ ace_mode: sh
+ codemirror_mode: shell
+ codemirror_mime_type: text/x-sh
+ language_id: 128
+Gerber Image:
+ type: data
+ aliases:
+ - rs-274x
+ extensions:
+ - ".gbr"
+ - ".gbl"
+ - ".gbo"
+ - ".gbp"
+ - ".gbs"
+ - ".gko"
+ - ".gpb"
+ - ".gpt"
+ - ".gtl"
+ - ".gto"
+ - ".gtp"
+ - ".gts"
+ interpreters:
+ - gerbv
+ - gerbview
+ tm_scope: source.gerber
+ ace_mode: text
+ language_id: 404627610
+Gettext Catalog:
+ type: prose
+ searchable: false
+ aliases:
+ - pot
+ extensions:
+ - ".po"
+ - ".pot"
+ tm_scope: source.po
+ ace_mode: text
+ language_id: 129
+Gherkin:
+ type: programming
+ extensions:
+ - ".feature"
+ tm_scope: text.gherkin.feature
+ aliases:
+ - cucumber
+ ace_mode: text
+ color: "#5B2063"
+ language_id: 76
+Glyph:
+ type: programming
+ color: "#c1ac7f"
+ extensions:
+ - ".glf"
+ tm_scope: source.tcl
+ ace_mode: tcl
+ codemirror_mode: tcl
+ codemirror_mime_type: text/x-tcl
+ language_id: 130
+Gnuplot:
+ type: programming
+ color: "#f0a9f0"
+ extensions:
+ - ".gp"
+ - ".gnu"
+ - ".gnuplot"
+ - ".plot"
+ - ".plt"
+ interpreters:
+ - gnuplot
+ ace_mode: text
+ language_id: 131
+Go:
+ type: programming
+ color: "#375eab"
+ aliases:
+ - golang
+ extensions:
+ - ".go"
+ ace_mode: golang
+ codemirror_mode: go
+ codemirror_mime_type: text/x-go
+ language_id: 132
+Golo:
+ type: programming
+ color: "#88562A"
+ extensions:
+ - ".golo"
+ tm_scope: source.golo
+ ace_mode: text
+ language_id: 133
+Gosu:
+ type: programming
+ color: "#82937f"
+ extensions:
+ - ".gs"
+ - ".gst"
+ - ".gsx"
+ - ".vark"
+ tm_scope: source.gosu.2
+ ace_mode: text
+ language_id: 134
+Grace:
+ type: programming
+ extensions:
+ - ".grace"
+ tm_scope: source.grace
+ ace_mode: text
+ language_id: 135
+Gradle:
+ type: data
+ extensions:
+ - ".gradle"
+ tm_scope: source.groovy.gradle
+ ace_mode: text
+ language_id: 136
+Grammatical Framework:
+ type: programming
+ aliases:
+ - gf
+ wrap: false
+ extensions:
+ - ".gf"
+ searchable: true
+ color: "#79aa7a"
+ tm_scope: source.haskell
+ ace_mode: haskell
+ codemirror_mode: haskell
+ codemirror_mime_type: text/x-haskell
+ language_id: 137
+Graph Modeling Language:
+ type: data
+ extensions:
+ - ".gml"
+ tm_scope: none
+ ace_mode: text
+ language_id: 138
+GraphQL:
+ type: data
+ extensions:
+ - ".graphql"
+ - ".gql"
+ tm_scope: source.graphql
+ ace_mode: text
+ language_id: 139
+Graphviz (DOT):
+ type: data
+ tm_scope: source.dot
+ extensions:
+ - ".dot"
+ - ".gv"
+ ace_mode: text
+ language_id: 140
+Groovy:
+ type: programming
+ ace_mode: groovy
+ codemirror_mode: groovy
+ codemirror_mime_type: text/x-groovy
+ color: "#e69f56"
+ extensions:
+ - ".groovy"
+ - ".grt"
+ - ".gtpl"
+ - ".gvy"
+ interpreters:
+ - groovy
+ filenames:
+ - Jenkinsfile
+ language_id: 142
+Groovy Server Pages:
+ type: programming
+ group: Groovy
+ aliases:
+ - gsp
+ - java server page
+ extensions:
+ - ".gsp"
+ tm_scope: text.html.jsp
+ ace_mode: jsp
+ codemirror_mode: htmlembedded
+ codemirror_mime_type: application/x-jsp
+ language_id: 143
+HCL:
+ type: programming
+ extensions:
+ - ".hcl"
+ - ".tf"
+ - ".tfvars"
+ ace_mode: ruby
+ codemirror_mode: ruby
+ codemirror_mime_type: text/x-ruby
+ tm_scope: source.terraform
+ language_id: 144
+HLSL:
+ type: programming
+ extensions:
+ - ".hlsl"
+ - ".cginc"
+ - ".fx"
+ - ".fxh"
+ - ".hlsli"
+ ace_mode: text
+ tm_scope: source.hlsl
+ language_id: 145
+HTML:
+ type: markup
+ tm_scope: text.html.basic
+ ace_mode: html
+ codemirror_mode: htmlmixed
+ codemirror_mime_type: text/html
+ color: "#e34c26"
+ aliases:
+ - xhtml
+ extensions:
+ - ".html"
+ - ".htm"
+ - ".html.hl"
+ - ".inc"
+ - ".st"
+ - ".xht"
+ - ".xhtml"
+ language_id: 146
+HTML+Django:
+ type: markup
+ tm_scope: text.html.django
+ group: HTML
+ extensions:
+ - ".jinja"
+ - ".jinja2"
+ - ".mustache"
+ - ".njk"
+ aliases:
+ - django
+ - html+django/jinja
+ - html+jinja
+ - htmldjango
+ - njk
+ - nunjucks
+ ace_mode: django
+ codemirror_mode: django
+ codemirror_mime_type: text/x-django
+ language_id: 147
+HTML+ECR:
+ type: markup
+ tm_scope: text.html.ecr
+ group: HTML
+ aliases:
+ - ecr
+ extensions:
+ - ".ecr"
+ ace_mode: text
+ codemirror_mode: htmlmixed
+ codemirror_mime_type: text/html
+ language_id: 148
+HTML+EEX:
+ type: markup
+ tm_scope: text.html.elixir
+ group: HTML
+ aliases:
+ - eex
+ extensions:
+ - ".eex"
+ ace_mode: text
+ codemirror_mode: htmlmixed
+ codemirror_mime_type: text/html
+ language_id: 149
+HTML+ERB:
+ type: markup
+ tm_scope: text.html.erb
+ group: HTML
+ aliases:
+ - erb
+ extensions:
+ - ".erb"
+ - ".erb.deface"
+ ace_mode: text
+ codemirror_mode: htmlembedded
+ codemirror_mime_type: application/x-erb
+ language_id: 150
+HTML+PHP:
+ type: markup
+ tm_scope: text.html.php
+ group: HTML
+ extensions:
+ - ".phtml"
+ ace_mode: php
+ codemirror_mode: php
+ codemirror_mime_type: application/x-httpd-php
+ language_id: 151
+HTTP:
+ type: data
+ extensions:
+ - ".http"
+ tm_scope: source.httpspec
+ ace_mode: text
+ codemirror_mode: http
+ codemirror_mime_type: message/http
+ language_id: 152
+HXML:
+ type: data
+ ace_mode: text
+ extensions:
+ - ".hxml"
+ tm_scope: source.hxml
+ language_id: 786683730
+Hack:
+ type: programming
+ ace_mode: php
+ codemirror_mode: php
+ codemirror_mime_type: application/x-httpd-php
+ extensions:
+ - ".hh"
+ - ".php"
+ tm_scope: text.html.php
+ color: "#878787"
+ language_id: 153
+Haml:
+ group: HTML
+ type: markup
+ extensions:
+ - ".haml"
+ - ".haml.deface"
+ ace_mode: haml
+ codemirror_mode: haml
+ codemirror_mime_type: text/x-haml
+ language_id: 154
+Handlebars:
+ type: markup
+ group: HTML
+ aliases:
+ - hbs
+ - htmlbars
+ extensions:
+ - ".handlebars"
+ - ".hbs"
+ tm_scope: text.html.handlebars
+ ace_mode: handlebars
+ language_id: 155
+Harbour:
+ type: programming
+ color: "#0e60e3"
+ extensions:
+ - ".hb"
+ tm_scope: source.harbour
+ ace_mode: text
+ language_id: 156
+Haskell:
+ type: programming
+ color: "#5e5086"
+ extensions:
+ - ".hs"
+ - ".hsc"
+ interpreters:
+ - runhaskell
+ ace_mode: haskell
+ codemirror_mode: haskell
+ codemirror_mime_type: text/x-haskell
+ language_id: 157
+Haxe:
+ type: programming
+ ace_mode: haxe
+ codemirror_mode: haxe
+ codemirror_mime_type: text/x-haxe
+ color: "#df7900"
+ extensions:
+ - ".hx"
+ - ".hxsl"
+ tm_scope: source.hx
+ language_id: 158
+HiveQL:
+ type: programming
+ extensions:
+ - ".q"
+ color: "#dce200"
+ tm_scope: source.hql
+ ace_mode: sql
+ language_id: 931814087
+Hy:
+ type: programming
+ ace_mode: text
+ color: "#7790B2"
+ extensions:
+ - ".hy"
+ interpreters:
+ - "hy"
+ aliases:
+ - hylang
+ tm_scope: none
+ language_id: 159
+HyPhy:
+ type: programming
+ ace_mode: text
+ extensions:
+ - ".bf"
+ tm_scope: none
+ language_id: 160
+IDL:
+ type: programming
+ color: "#a3522f"
+ extensions:
+ - ".pro"
+ - ".dlm"
+ ace_mode: text
+ codemirror_mode: idl
+ codemirror_mime_type: text/x-idl
+ language_id: 161
+IGOR Pro:
+ type: programming
+ extensions:
+ - ".ipf"
+ aliases:
+ - igor
+ - igorpro
+ tm_scope: none
+ ace_mode: text
+ language_id: 162
+INI:
+ type: data
+ extensions:
+ - ".ini"
+ - ".cfg"
+ - ".prefs"
+ - ".pro"
+ - ".properties"
+ filenames:
+ - ".editorconfig"
+ - ".gitconfig"
+ - buildozer.spec
+ tm_scope: source.ini
+ aliases:
+ - dosini
+ ace_mode: ini
+ codemirror_mode: properties
+ codemirror_mime_type: text/x-properties
+ language_id: 163
+IRC log:
+ type: data
+ aliases:
+ - irc
+ - irc logs
+ extensions:
+ - ".irclog"
+ - ".weechatlog"
+ tm_scope: none
+ ace_mode: text
+ codemirror_mode: mirc
+ codemirror_mime_type: text/mirc
+ language_id: 164
+Idris:
+ type: programming
+ color: "#b30000"
+ extensions:
+ - ".idr"
+ - ".lidr"
+ ace_mode: text
+ tm_scope: source.idris
+ language_id: 165
+Inform 7:
+ type: programming
+ wrap: true
+ extensions:
+ - ".ni"
+ - ".i7x"
+ tm_scope: source.inform7
+ aliases:
+ - i7
+ - inform7
+ ace_mode: text
+ language_id: 166
+Inno Setup:
+ type: programming
+ extensions:
+ - ".iss"
+ tm_scope: none
+ ace_mode: text
+ language_id: 167
+Io:
+ type: programming
+ color: "#a9188d"
+ extensions:
+ - ".io"
+ interpreters:
+ - io
+ ace_mode: io
+ language_id: 168
+Ioke:
+ type: programming
+ color: "#078193"
+ extensions:
+ - ".ik"
+ interpreters:
+ - ioke
+ ace_mode: text
+ language_id: 169
+Isabelle:
+ type: programming
+ color: "#FEFE00"
+ extensions:
+ - ".thy"
+ tm_scope: source.isabelle.theory
+ ace_mode: text
+ language_id: 170
+Isabelle ROOT:
+ type: programming
+ group: Isabelle
+ filenames:
+ - ROOT
+ tm_scope: source.isabelle.root
+ ace_mode: text
+ language_id: 171
+J:
+ type: programming
+ color: "#9EEDFF"
+ extensions:
+ - ".ijs"
+ interpreters:
+ - jconsole
+ tm_scope: source.j
+ ace_mode: text
+ language_id: 172
+JFlex:
+ type: programming
+ group: Lex
+ extensions:
+ - ".flex"
+ - ".jflex"
+ tm_scope: source.jflex
+ ace_mode: text
+ language_id: 173
+JSON:
+ type: data
+ tm_scope: source.json
+ group: JavaScript
+ ace_mode: json
+ codemirror_mode: javascript
+ codemirror_mime_type: application/json
+ searchable: false
+ extensions:
+ - ".json"
+ - ".avsc"
+ - ".geojson"
+ - ".gltf"
+ - ".JSON-tmLanguage"
+ - ".jsonl"
+ - ".tfstate"
+ - ".tfstate.backup"
+ - ".topojson"
+ - ".webapp"
+ - ".webmanifest"
+ filenames:
+ - ".arcconfig"
+ - ".htmlhintrc"
+ - ".tern-config"
+ - ".tern-project"
+ - composer.lock
+ - mcmod.info
+ language_id: 174
+JSON with Comments:
+ type: data
+ group: JSON
+ tm_scope: source.js
+ ace_mode: javascript
+ codemirror_mode: javascript
+ codemirror_mime_type: text/javascript
+ aliases:
+ - jsonc
+ extensions:
+ - ".sublime-build"
+ - ".sublime-commands"
+ - ".sublime-completions"
+ - ".sublime-keymap"
+ - ".sublime-macro"
+ - ".sublime-menu"
+ - ".sublime-mousemap"
+ - ".sublime-project"
+ - ".sublime-settings"
+ - ".sublime-theme"
+ - ".sublime-workspace"
+ - ".sublime_metrics"
+ - ".sublime_session"
+ filenames:
+ - ".babelrc"
+ - ".eslintrc.json"
+ - ".jscsrc"
+ - ".jshintrc"
+ - ".jslintrc"
+ - tsconfig.json
+ language_id: 423
+JSON5:
+ type: data
+ extensions:
+ - ".json5"
+ tm_scope: source.js
+ ace_mode: javascript
+ codemirror_mode: javascript
+ codemirror_mime_type: application/json
+ language_id: 175
+JSONLD:
+ type: data
+ group: JavaScript
+ ace_mode: javascript
+ extensions:
+ - ".jsonld"
+ tm_scope: source.js
+ language_id: 176
+JSONiq:
+ color: "#40d47e"
+ type: programming
+ ace_mode: jsoniq
+ codemirror_mode: javascript
+ codemirror_mime_type: application/json
+ extensions:
+ - ".jq"
+ tm_scope: source.jq
+ language_id: 177
+JSX:
+ type: programming
+ group: JavaScript
+ extensions:
+ - ".jsx"
+ tm_scope: source.js.jsx
+ ace_mode: javascript
+ codemirror_mode: jsx
+ codemirror_mime_type: text/jsx
+ language_id: 178
+Jasmin:
+ type: programming
+ ace_mode: java
+ extensions:
+ - ".j"
+ tm_scope: source.jasmin
+ language_id: 180
+Java:
+ type: programming
+ ace_mode: java
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-java
+ color: "#b07219"
+ extensions:
+ - ".java"
+ language_id: 181
+Java Server Pages:
+ type: programming
+ group: Java
+ aliases:
+ - jsp
+ extensions:
+ - ".jsp"
+ tm_scope: text.html.jsp
+ ace_mode: jsp
+ codemirror_mode: htmlembedded
+ codemirror_mime_type: application/x-jsp
+ language_id: 182
+JavaScript:
+ type: programming
+ tm_scope: source.js
+ ace_mode: javascript
+ codemirror_mode: javascript
+ codemirror_mime_type: text/javascript
+ color: "#f1e05a"
+ aliases:
+ - js
+ - node
+ extensions:
+ - ".js"
+ - "._js"
+ - ".bones"
+ - ".es"
+ - ".es6"
+ - ".frag"
+ - ".gs"
+ - ".jake"
+ - ".jsb"
+ - ".jscad"
+ - ".jsfl"
+ - ".jsm"
+ - ".jss"
+ - ".mjs"
+ - ".njs"
+ - ".pac"
+ - ".sjs"
+ - ".ssjs"
+ - ".xsjs"
+ - ".xsjslib"
+ filenames:
+ - Jakefile
+ interpreters:
+ - node
+ language_id: 183
+Jison:
+ type: programming
+ group: Yacc
+ extensions:
+ - ".jison"
+ tm_scope: source.jison
+ ace_mode: text
+ language_id: 284531423
+Jison Lex:
+ type: programming
+ group: Lex
+ extensions:
+ - ".jisonlex"
+ tm_scope: source.jisonlex
+ ace_mode: text
+ language_id: 406395330
+Jolie:
+ type: programming
+ extensions:
+ - ".ol"
+ - ".iol"
+ interpreters:
+ - jolie
+ color: "#843179"
+ ace_mode: text
+ tm_scope: source.jolie
+ language_id: 998078858
+Julia:
+ type: programming
+ extensions:
+ - ".jl"
+ interpreters:
+ - julia
+ color: "#a270ba"
+ ace_mode: julia
+ codemirror_mode: julia
+ codemirror_mime_type: text/x-julia
+ language_id: 184
+Jupyter Notebook:
+ type: markup
+ ace_mode: json
+ codemirror_mode: javascript
+ codemirror_mime_type: application/json
+ tm_scope: source.json
+ color: "#DA5B0B"
+ extensions:
+ - ".ipynb"
+ filenames:
+ - Notebook
+ aliases:
+ - IPython Notebook
+ language_id: 185
+KRL:
+ type: programming
+ color: "#28430A"
+ extensions:
+ - ".krl"
+ tm_scope: none
+ ace_mode: text
+ language_id: 186
+KiCad Layout:
+ type: data
+ aliases:
+ - pcbnew
+ extensions:
+ - ".kicad_pcb"
+ - ".kicad_mod"
+ - ".kicad_wks"
+ filenames:
+ - fp-lib-table
+ tm_scope: source.pcb.sexp
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ language_id: 187
+KiCad Legacy Layout:
+ type: data
+ extensions:
+ - ".brd"
+ tm_scope: source.pcb.board
+ ace_mode: text
+ language_id: 140848857
+KiCad Schematic:
+ type: data
+ aliases:
+ - eeschema schematic
+ extensions:
+ - ".sch"
+ tm_scope: source.pcb.schematic
+ ace_mode: text
+ language_id: 622447435
+Kit:
+ type: markup
+ ace_mode: html
+ codemirror_mode: htmlmixed
+ codemirror_mime_type: text/html
+ extensions:
+ - ".kit"
+ tm_scope: text.html.basic
+ language_id: 188
+Kotlin:
+ type: programming
+ color: "#F18E33"
+ extensions:
+ - ".kt"
+ - ".ktm"
+ - ".kts"
+ tm_scope: source.kotlin
+ ace_mode: text
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-kotlin
+ language_id: 189
+LFE:
+ type: programming
+ color: "#4C3023"
+ extensions:
+ - ".lfe"
+ tm_scope: source.lisp
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ language_id: 190
+LLVM:
+ type: programming
+ extensions:
+ - ".ll"
+ ace_mode: text
+ color: "#185619"
+ language_id: 191
+LOLCODE:
+ type: programming
+ extensions:
+ - ".lol"
+ color: "#cc9900"
+ tm_scope: none
+ ace_mode: text
+ language_id: 192
+LSL:
+ type: programming
+ ace_mode: lsl
+ extensions:
+ - ".lsl"
+ - ".lslp"
+ interpreters:
+ - lsl
+ color: "#3d9970"
+ language_id: 193
+LabVIEW:
+ type: programming
+ extensions:
+ - ".lvproj"
+ tm_scope: text.xml
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ language_id: 194
+Lasso:
+ type: programming
+ color: "#999999"
+ extensions:
+ - ".lasso"
+ - ".las"
+ - ".lasso8"
+ - ".lasso9"
+ - ".ldml"
+ tm_scope: file.lasso
+ aliases:
+ - lassoscript
+ ace_mode: text
+ language_id: 195
+Latte:
+ type: markup
+ group: HTML
+ extensions:
+ - ".latte"
+ tm_scope: text.html.smarty
+ ace_mode: smarty
+ codemirror_mode: smarty
+ codemirror_mime_type: text/x-smarty
+ language_id: 196
+Lean:
+ type: programming
+ extensions:
+ - ".lean"
+ - ".hlean"
+ ace_mode: text
+ language_id: 197
+Less:
+ type: markup
+ group: CSS
+ extensions:
+ - ".less"
+ tm_scope: source.css.less
+ ace_mode: less
+ codemirror_mode: css
+ codemirror_mime_type: text/css
+ language_id: 198
+Lex:
+ type: programming
+ color: "#DBCA00"
+ aliases:
+ - flex
+ extensions:
+ - ".l"
+ - ".lex"
+ tm_scope: none
+ ace_mode: text
+ language_id: 199
+LilyPond:
+ type: programming
+ extensions:
+ - ".ly"
+ - ".ily"
+ ace_mode: text
+ language_id: 200
+Limbo:
+ type: programming
+ extensions:
+ - ".b"
+ - ".m"
+ tm_scope: none
+ ace_mode: text
+ language_id: 201
+Linker Script:
+ type: data
+ extensions:
+ - ".ld"
+ - ".lds"
+ - ".x"
+ filenames:
+ - ld.script
+ tm_scope: none
+ ace_mode: text
+ language_id: 202
+Linux Kernel Module:
+ type: data
+ extensions:
+ - ".mod"
+ tm_scope: none
+ ace_mode: text
+ language_id: 203
+Liquid:
+ type: markup
+ extensions:
+ - ".liquid"
+ tm_scope: text.html.liquid
+ ace_mode: liquid
+ language_id: 204
+Literate Agda:
+ type: programming
+ group: Agda
+ extensions:
+ - ".lagda"
+ tm_scope: none
+ ace_mode: text
+ language_id: 205
+Literate CoffeeScript:
+ type: programming
+ tm_scope: source.litcoffee
+ group: CoffeeScript
+ ace_mode: text
+ wrap: true
+ aliases:
+ - litcoffee
+ extensions:
+ - ".litcoffee"
+ language_id: 206
+Literate Haskell:
+ type: programming
+ group: Haskell
+ aliases:
+ - lhaskell
+ - lhs
+ extensions:
+ - ".lhs"
+ tm_scope: text.tex.latex.haskell
+ ace_mode: text
+ codemirror_mode: haskell-literate
+ codemirror_mime_type: text/x-literate-haskell
+ language_id: 207
+LiveScript:
+ type: programming
+ color: "#499886"
+ aliases:
+ - live-script
+ - ls
+ extensions:
+ - ".ls"
+ - "._ls"
+ filenames:
+ - Slakefile
+ ace_mode: livescript
+ codemirror_mode: livescript
+ codemirror_mime_type: text/x-livescript
+ language_id: 208
+Logos:
+ type: programming
+ extensions:
+ - ".xm"
+ - ".x"
+ - ".xi"
+ ace_mode: text
+ tm_scope: source.logos
+ language_id: 209
+Logtalk:
+ type: programming
+ extensions:
+ - ".lgt"
+ - ".logtalk"
+ ace_mode: text
+ language_id: 210
+LookML:
+ type: programming
+ ace_mode: yaml
+ codemirror_mode: yaml
+ codemirror_mime_type: text/x-yaml
+ color: "#652B81"
+ extensions:
+ - ".lookml"
+ - ".model.lkml"
+ - ".view.lkml"
+ tm_scope: source.yaml
+ language_id: 211
+LoomScript:
+ type: programming
+ extensions:
+ - ".ls"
+ tm_scope: source.loomscript
+ ace_mode: text
+ language_id: 212
+Lua:
+ type: programming
+ ace_mode: lua
+ codemirror_mode: lua
+ codemirror_mime_type: text/x-lua
+ color: "#000080"
+ extensions:
+ - ".lua"
+ - ".fcgi"
+ - ".nse"
+ - ".p8"
+ - ".pd_lua"
+ - ".rbxs"
+ - ".wlua"
+ interpreters:
+ - lua
+ language_id: 213
+M:
+ type: programming
+ aliases:
+ - mumps
+ extensions:
+ - ".mumps"
+ - ".m"
+ ace_mode: text
+ codemirror_mode: mumps
+ codemirror_mime_type: text/x-mumps
+ language_id: 214
+ tm_scope: none
+M4:
+ type: programming
+ extensions:
+ - ".m4"
+ tm_scope: none
+ ace_mode: text
+ language_id: 215
+M4Sugar:
+ type: programming
+ group: M4
+ aliases:
+ - autoconf
+ extensions:
+ - ".m4"
+ filenames:
+ - configure.ac
+ tm_scope: none
+ ace_mode: text
+ language_id: 216
+MAXScript:
+ type: programming
+ color: "#00a6a6"
+ extensions:
+ - ".ms"
+ - ".mcr"
+ tm_scope: source.maxscript
+ ace_mode: text
+ language_id: 217
+MQL4:
+ type: programming
+ color: "#62A8D6"
+ extensions:
+ - ".mq4"
+ - ".mqh"
+ tm_scope: source.mql5
+ ace_mode: c_cpp
+ language_id: 426
+MQL5:
+ type: programming
+ color: "#4A76B8"
+ extensions:
+ - ".mq5"
+ - ".mqh"
+ tm_scope: source.mql5
+ ace_mode: c_cpp
+ language_id: 427
+MTML:
+ type: markup
+ color: "#b7e1f4"
+ extensions:
+ - ".mtml"
+ tm_scope: text.html.basic
+ ace_mode: html
+ codemirror_mode: htmlmixed
+ codemirror_mime_type: text/html
+ language_id: 218
+MUF:
+ type: programming
+ group: Forth
+ extensions:
+ - ".muf"
+ - ".m"
+ tm_scope: none
+ ace_mode: forth
+ codemirror_mode: forth
+ codemirror_mime_type: text/x-forth
+ language_id: 219
+Makefile:
+ type: programming
+ color: "#427819"
+ aliases:
+ - bsdmake
+ - make
+ - mf
+ extensions:
+ - ".mak"
+ - ".d"
+ - ".make"
+ - ".mk"
+ - ".mkfile"
+ filenames:
+ - BSDmakefile
+ - GNUmakefile
+ - Kbuild
+ - Makefile
+ - Makefile.am
+ - Makefile.boot
+ - Makefile.frag
+ - Makefile.in
+ - Makefile.inc
+ - Makefile.wat
+ - makefile
+ - makefile.sco
+ - mkfile
+ interpreters:
+ - make
+ ace_mode: makefile
+ codemirror_mode: cmake
+ codemirror_mime_type: text/x-cmake
+ language_id: 220
+Mako:
+ type: programming
+ extensions:
+ - ".mako"
+ - ".mao"
+ tm_scope: text.html.mako
+ ace_mode: text
+ language_id: 221
+Markdown:
+ type: prose
+ aliases:
+ - pandoc
+ ace_mode: markdown
+ codemirror_mode: gfm
+ codemirror_mime_type: text/x-gfm
+ wrap: true
+ extensions:
+ - ".md"
+ - ".markdown"
+ - ".mdown"
+ - ".mdwn"
+ - ".mkd"
+ - ".mkdn"
+ - ".mkdown"
+ - ".ronn"
+ - ".workbook"
+ tm_scope: source.gfm
+ language_id: 222
+Marko:
+ group: HTML
+ type: markup
+ tm_scope: text.marko
+ extensions:
+ - ".marko"
+ aliases:
+ - markojs
+ ace_mode: text
+ codemirror_mode: htmlmixed
+ codemirror_mime_type: text/html
+ language_id: 932782397
+Mask:
+ type: markup
+ color: "#f97732"
+ ace_mode: mask
+ extensions:
+ - ".mask"
+ tm_scope: source.mask
+ language_id: 223
+Mathematica:
+ type: programming
+ extensions:
+ - ".mathematica"
+ - ".cdf"
+ - ".m"
+ - ".ma"
+ - ".mt"
+ - ".nb"
+ - ".nbp"
+ - ".wl"
+ - ".wlt"
+ aliases:
+ - mma
+ ace_mode: text
+ codemirror_mode: mathematica
+ codemirror_mime_type: text/x-mathematica
+ language_id: 224
+Matlab:
+ type: programming
+ color: "#e16737"
+ aliases:
+ - octave
+ extensions:
+ - ".matlab"
+ - ".m"
+ ace_mode: matlab
+ codemirror_mode: octave
+ codemirror_mime_type: text/x-octave
+ language_id: 225
+Maven POM:
+ type: data
+ tm_scope: text.xml.pom
+ filenames:
+ - pom.xml
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ language_id: 226
+Max:
+ type: programming
+ color: "#c4a79c"
+ aliases:
+ - max/msp
+ - maxmsp
+ extensions:
+ - ".maxpat"
+ - ".maxhelp"
+ - ".maxproj"
+ - ".mxt"
+ - ".pat"
+ tm_scope: source.json
+ ace_mode: json
+ codemirror_mode: javascript
+ codemirror_mime_type: application/json
+ language_id: 227
+MediaWiki:
+ type: prose
+ wrap: true
+ extensions:
+ - ".mediawiki"
+ - ".wiki"
+ tm_scope: text.html.mediawiki
+ ace_mode: text
+ language_id: 228
+Mercury:
+ type: programming
+ color: "#ff2b2b"
+ ace_mode: prolog
+ interpreters:
+ - mmi
+ extensions:
+ - ".m"
+ - ".moo"
+ tm_scope: source.mercury
+ language_id: 229
+Meson:
+ type: programming
+ color: "#007800"
+ filenames:
+ - meson.build
+ - meson_options.txt
+ tm_scope: source.meson
+ ace_mode: text
+ language_id: 799141244
+Metal:
+ type: programming
+ color: "#8f14e9"
+ extensions:
+ - ".metal"
+ tm_scope: source.c++
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-c++src
+ language_id: 230
+MiniD:
+ type: programming
+ searchable: false
+ extensions:
+ - ".minid"
+ tm_scope: none
+ ace_mode: text
+ language_id: 231
+Mirah:
+ type: programming
+ color: "#c7a938"
+ extensions:
+ - ".druby"
+ - ".duby"
+ - ".mirah"
+ tm_scope: source.ruby
+ ace_mode: ruby
+ codemirror_mode: ruby
+ codemirror_mime_type: text/x-ruby
+ language_id: 232
+Modelica:
+ type: programming
+ extensions:
+ - ".mo"
+ tm_scope: source.modelica
+ ace_mode: text
+ codemirror_mode: modelica
+ codemirror_mime_type: text/x-modelica
+ language_id: 233
+Modula-2:
+ type: programming
+ extensions:
+ - ".mod"
+ tm_scope: source.modula2
+ ace_mode: text
+ language_id: 234
+Modula-3:
+ type: programming
+ extensions:
+ - ".i3"
+ - ".ig"
+ - ".m3"
+ - ".mg"
+ color: "#223388"
+ ace_mode: text
+ tm_scope: source.modula-3
+ language_id: 564743864
+Module Management System:
+ type: programming
+ extensions:
+ - ".mms"
+ - ".mmk"
+ filenames:
+ - descrip.mmk
+ - descrip.mms
+ tm_scope: none
+ ace_mode: text
+ language_id: 235
+Monkey:
+ type: programming
+ extensions:
+ - ".monkey"
+ - ".monkey2"
+ ace_mode: text
+ tm_scope: source.monkey
+ language_id: 236
+Moocode:
+ type: programming
+ extensions:
+ - ".moo"
+ tm_scope: none
+ ace_mode: text
+ language_id: 237
+MoonScript:
+ type: programming
+ extensions:
+ - ".moon"
+ interpreters:
+ - moon
+ ace_mode: text
+ language_id: 238
+Myghty:
+ type: programming
+ extensions:
+ - ".myt"
+ tm_scope: none
+ ace_mode: text
+ language_id: 239
+NCL:
+ type: programming
+ color: "#28431f"
+ extensions:
+ - ".ncl"
+ tm_scope: source.ncl
+ ace_mode: text
+ language_id: 240
+NL:
+ type: data
+ extensions:
+ - ".nl"
+ tm_scope: none
+ ace_mode: text
+ language_id: 241
+NSIS:
+ type: programming
+ extensions:
+ - ".nsi"
+ - ".nsh"
+ ace_mode: text
+ codemirror_mode: nsis
+ codemirror_mime_type: text/x-nsis
+ language_id: 242
+Nearley:
+ type: programming
+ ace_mode: text
+ color: "#990000"
+ extensions:
+ - ".ne"
+ - ".nearley"
+ tm_scope: source.ne
+ language_id: 521429430
+Nemerle:
+ type: programming
+ color: "#3d3c6e"
+ extensions:
+ - ".n"
+ ace_mode: text
+ language_id: 243
+NetLinx:
+ type: programming
+ color: "#0aa0ff"
+ extensions:
+ - ".axs"
+ - ".axi"
+ tm_scope: source.netlinx
+ ace_mode: text
+ language_id: 244
+NetLinx+ERB:
+ type: programming
+ color: "#747faa"
+ extensions:
+ - ".axs.erb"
+ - ".axi.erb"
+ tm_scope: source.netlinx.erb
+ ace_mode: text
+ language_id: 245
+NetLogo:
+ type: programming
+ color: "#ff6375"
+ extensions:
+ - ".nlogo"
+ tm_scope: source.lisp
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ language_id: 246
+NewLisp:
+ type: programming
+ color: "#87AED7"
+ extensions:
+ - ".nl"
+ - ".lisp"
+ - ".lsp"
+ interpreters:
+ - newlisp
+ tm_scope: source.lisp
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ language_id: 247
+Nextflow:
+ type: programming
+ ace_mode: groovy
+ tm_scope: source.nextflow
+ color: "#3ac486"
+ extensions:
+ - ".nf"
+ filenames:
+ - nextflow.config
+ interpreters:
+ - nextflow
+ language_id: 506780613
+Nginx:
+ type: data
+ extensions:
+ - ".nginxconf"
+ - ".vhost"
+ filenames:
+ - nginx.conf
+ tm_scope: source.nginx
+ aliases:
+ - nginx configuration file
+ ace_mode: text
+ codemirror_mode: nginx
+ codemirror_mime_type: text/x-nginx-conf
+ language_id: 248
+Nim:
+ type: programming
+ color: "#37775b"
+ extensions:
+ - ".nim"
+ - ".nimrod"
+ ace_mode: text
+ tm_scope: source.nim
+ language_id: 249
+Ninja:
+ type: data
+ tm_scope: source.ninja
+ extensions:
+ - ".ninja"
+ ace_mode: text
+ language_id: 250
+Nit:
+ type: programming
+ color: "#009917"
+ extensions:
+ - ".nit"
+ tm_scope: source.nit
+ ace_mode: text
+ language_id: 251
+Nix:
+ type: programming
+ color: "#7e7eff"
+ extensions:
+ - ".nix"
+ aliases:
+ - nixos
+ tm_scope: source.nix
+ ace_mode: nix
+ language_id: 252
+Nu:
+ type: programming
+ color: "#c9df40"
+ aliases:
+ - nush
+ extensions:
+ - ".nu"
+ filenames:
+ - Nukefile
+ tm_scope: source.nu
+ ace_mode: scheme
+ codemirror_mode: scheme
+ codemirror_mime_type: text/x-scheme
+ interpreters:
+ - nush
+ language_id: 253
+NumPy:
+ type: programming
+ group: Python
+ extensions:
+ - ".numpy"
+ - ".numpyw"
+ - ".numsc"
+ tm_scope: none
+ ace_mode: text
+ codemirror_mode: python
+ codemirror_mime_type: text/x-python
+ language_id: 254
+OCaml:
+ type: programming
+ ace_mode: ocaml
+ codemirror_mode: mllike
+ codemirror_mime_type: text/x-ocaml
+ color: "#3be133"
+ extensions:
+ - ".ml"
+ - ".eliom"
+ - ".eliomi"
+ - ".ml4"
+ - ".mli"
+ - ".mll"
+ - ".mly"
+ interpreters:
+ - ocaml
+ - ocamlrun
+ - ocamlscript
+ tm_scope: source.ocaml
+ language_id: 255
+ObjDump:
+ type: data
+ extensions:
+ - ".objdump"
+ tm_scope: objdump.x86asm
+ ace_mode: assembly_x86
+ language_id: 256
+Objective-C:
+ type: programming
+ tm_scope: source.objc
+ color: "#438eff"
+ aliases:
+ - obj-c
+ - objc
+ - objectivec
+ extensions:
+ - ".m"
+ - ".h"
+ ace_mode: objectivec
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-objectivec
+ language_id: 257
+Objective-C++:
+ type: programming
+ tm_scope: source.objc++
+ color: "#6866fb"
+ aliases:
+ - obj-c++
+ - objc++
+ - objectivec++
+ extensions:
+ - ".mm"
+ ace_mode: objectivec
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-objectivec
+ language_id: 258
+Objective-J:
+ type: programming
+ color: "#ff0c5a"
+ aliases:
+ - obj-j
+ - objectivej
+ - objj
+ extensions:
+ - ".j"
+ - ".sj"
+ tm_scope: source.js.objj
+ ace_mode: text
+ language_id: 259
+Omgrofl:
+ type: programming
+ extensions:
+ - ".omgrofl"
+ color: "#cabbff"
+ tm_scope: none
+ ace_mode: text
+ language_id: 260
+Opa:
+ type: programming
+ extensions:
+ - ".opa"
+ ace_mode: text
+ language_id: 261
+Opal:
+ type: programming
+ color: "#f7ede0"
+ extensions:
+ - ".opal"
+ tm_scope: source.opal
+ ace_mode: text
+ language_id: 262
+OpenCL:
+ type: programming
+ group: C
+ extensions:
+ - ".cl"
+ - ".opencl"
+ tm_scope: source.c
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csrc
+ language_id: 263
+OpenEdge ABL:
+ type: programming
+ aliases:
+ - progress
+ - openedge
+ - abl
+ extensions:
+ - ".p"
+ - ".cls"
+ - ".w"
+ tm_scope: source.abl
+ ace_mode: text
+ language_id: 264
+OpenRC runscript:
+ type: programming
+ group: Shell
+ aliases:
+ - openrc
+ interpreters:
+ - openrc-run
+ tm_scope: source.shell
+ ace_mode: sh
+ codemirror_mode: shell
+ codemirror_mime_type: text/x-sh
+ language_id: 265
+OpenSCAD:
+ type: programming
+ extensions:
+ - ".scad"
+ tm_scope: source.scad
+ ace_mode: scad
+ language_id: 266
+OpenType Feature File:
+ type: data
+ aliases:
+ - AFDKO
+ extensions:
+ - ".fea"
+ tm_scope: source.opentype
+ ace_mode: text
+ language_id: 374317347
+Org:
+ type: prose
+ wrap: true
+ extensions:
+ - ".org"
+ tm_scope: none
+ ace_mode: text
+ language_id: 267
+Ox:
+ type: programming
+ extensions:
+ - ".ox"
+ - ".oxh"
+ - ".oxo"
+ tm_scope: source.ox
+ ace_mode: text
+ language_id: 268
+Oxygene:
+ type: programming
+ color: "#cdd0e3"
+ extensions:
+ - ".oxygene"
+ tm_scope: none
+ ace_mode: text
+ language_id: 269
+Oz:
+ type: programming
+ color: "#fab738"
+ extensions:
+ - ".oz"
+ tm_scope: source.oz
+ ace_mode: text
+ codemirror_mode: oz
+ codemirror_mime_type: text/x-oz
+ language_id: 270
+P4:
+ type: programming
+ color: "#7055b5"
+ extensions:
+ - ".p4"
+ tm_scope: source.p4
+ ace_mode: text
+ language_id: 348895984
+PAWN:
+ type: programming
+ color: "#dbb284"
+ extensions:
+ - ".pwn"
+ - ".inc"
+ tm_scope: source.pawn
+ ace_mode: text
+ language_id: 271
+PHP:
+ type: programming
+ tm_scope: text.html.php
+ ace_mode: php
+ codemirror_mode: php
+ codemirror_mime_type: application/x-httpd-php
+ color: "#4F5D95"
+ extensions:
+ - ".php"
+ - ".aw"
+ - ".ctp"
+ - ".fcgi"
+ - ".inc"
+ - ".php3"
+ - ".php4"
+ - ".php5"
+ - ".phps"
+ - ".phpt"
+ filenames:
+ - ".php"
+ - ".php_cs"
+ - ".php_cs.dist"
+ - Phakefile
+ interpreters:
+ - php
+ aliases:
+ - inc
+ language_id: 272
+PLSQL:
+ type: programming
+ ace_mode: sql
+ codemirror_mode: sql
+ codemirror_mime_type: text/x-plsql
+ tm_scope: none
+ color: "#dad8d8"
+ extensions:
+ - ".pls"
+ - ".bdy"
+ - ".ddl"
+ - ".fnc"
+ - ".pck"
+ - ".pkb"
+ - ".pks"
+ - ".plb"
+ - ".plsql"
+ - ".prc"
+ - ".spc"
+ - ".sql"
+ - ".tpb"
+ - ".tps"
+ - ".trg"
+ - ".vw"
+ language_id: 273
+PLpgSQL:
+ type: programming
+ ace_mode: pgsql
+ codemirror_mode: sql
+ codemirror_mime_type: text/x-sql
+ tm_scope: source.sql
+ extensions:
+ - ".sql"
+ language_id: 274
+POV-Ray SDL:
+ type: programming
+ aliases:
+ - pov-ray
+ - povray
+ extensions:
+ - ".pov"
+ - ".inc"
+ ace_mode: text
+ language_id: 275
+Pan:
+ type: programming
+ color: "#cc0000"
+ extensions:
+ - ".pan"
+ tm_scope: source.pan
+ ace_mode: text
+ language_id: 276
+Papyrus:
+ type: programming
+ color: "#6600cc"
+ extensions:
+ - ".psc"
+ tm_scope: source.papyrus.skyrim
+ ace_mode: text
+ language_id: 277
+Parrot:
+ type: programming
+ color: "#f3ca0a"
+ extensions:
+ - ".parrot"
+ tm_scope: none
+ ace_mode: text
+ language_id: 278
+Parrot Assembly:
+ group: Parrot
+ type: programming
+ aliases:
+ - pasm
+ extensions:
+ - ".pasm"
+ interpreters:
+ - parrot
+ tm_scope: none
+ ace_mode: text
+ language_id: 279
+Parrot Internal Representation:
+ group: Parrot
+ tm_scope: source.parrot.pir
+ type: programming
+ aliases:
+ - pir
+ extensions:
+ - ".pir"
+ interpreters:
+ - parrot
+ ace_mode: text
+ language_id: 280
+Pascal:
+ type: programming
+ color: "#E3F171"
+ extensions:
+ - ".pas"
+ - ".dfm"
+ - ".dpr"
+ - ".inc"
+ - ".lpr"
+ - ".pascal"
+ - ".pp"
+ interpreters:
+ - instantfpc
+ ace_mode: pascal
+ codemirror_mode: pascal
+ codemirror_mime_type: text/x-pascal
+ language_id: 281
+Pep8:
+ type: programming
+ color: "#C76F5B"
+ extensions:
+ - ".pep"
+ ace_mode: text
+ tm_scope: source.pep8
+ language_id: 840372442
+Perl:
+ type: programming
+ tm_scope: source.perl
+ ace_mode: perl
+ codemirror_mode: perl
+ codemirror_mime_type: text/x-perl
+ color: "#0298c3"
+ extensions:
+ - ".pl"
+ - ".al"
+ - ".cgi"
+ - ".fcgi"
+ - ".perl"
+ - ".ph"
+ - ".plx"
+ - ".pm"
+ - ".psgi"
+ - ".t"
+ filenames:
+ - Makefile.PL
+ - Rexfile
+ - ack
+ - cpanfile
+ interpreters:
+ - cperl
+ - perl
+ aliases:
+ - cperl
+ language_id: 282
+Perl 6:
+ type: programming
+ color: "#0000fb"
+ extensions:
+ - ".6pl"
+ - ".6pm"
+ - ".nqp"
+ - ".p6"
+ - ".p6l"
+ - ".p6m"
+ - ".pl"
+ - ".pl6"
+ - ".pm"
+ - ".pm6"
+ - ".t"
+ interpreters:
+ - perl6
+ aliases:
+ - perl6
+ tm_scope: source.perl6fe
+ ace_mode: perl
+ codemirror_mode: perl
+ codemirror_mime_type: text/x-perl
+ language_id: 283
+Pic:
+ type: markup
+ group: Roff
+ tm_scope: source.pic
+ extensions:
+ - ".pic"
+ - ".chem"
+ ace_mode: text
+ codemirror_mode: troff
+ codemirror_mime_type: text/troff
+ language_id: 425
+Pickle:
+ type: data
+ extensions:
+ - ".pkl"
+ tm_scope: none
+ ace_mode: text
+ language_id: 284
+PicoLisp:
+ type: programming
+ extensions:
+ - ".l"
+ interpreters:
+ - picolisp
+ - pil
+ tm_scope: source.lisp
+ ace_mode: lisp
+ language_id: 285
+PigLatin:
+ type: programming
+ color: "#fcd7de"
+ extensions:
+ - ".pig"
+ tm_scope: source.pig_latin
+ ace_mode: text
+ language_id: 286
+Pike:
+ type: programming
+ color: "#005390"
+ extensions:
+ - ".pike"
+ - ".pmod"
+ interpreters:
+ - pike
+ ace_mode: text
+ language_id: 287
+Pod:
+ type: prose
+ ace_mode: perl
+ codemirror_mode: perl
+ codemirror_mime_type: text/x-perl
+ wrap: true
+ extensions:
+ - ".pod"
+ interpreters:
+ - perl
+ tm_scope: none
+ language_id: 288
+PogoScript:
+ type: programming
+ color: "#d80074"
+ extensions:
+ - ".pogo"
+ tm_scope: source.pogoscript
+ ace_mode: text
+ language_id: 289
+Pony:
+ type: programming
+ extensions:
+ - ".pony"
+ tm_scope: source.pony
+ ace_mode: text
+ language_id: 290
+PostCSS:
+ type: markup
+ tm_scope: source.postcss
+ group: CSS
+ extensions:
+ - ".pcss"
+ ace_mode: text
+ language_id: 262764437
+PostScript:
+ type: markup
+ color: "#da291c"
+ extensions:
+ - ".ps"
+ - ".eps"
+ - ".pfa"
+ tm_scope: source.postscript
+ aliases:
+ - postscr
+ ace_mode: text
+ language_id: 291
+PowerBuilder:
+ type: programming
+ color: "#8f0f8d"
+ extensions:
+ - ".pbt"
+ - ".sra"
+ - ".sru"
+ - ".srw"
+ tm_scope: none
+ ace_mode: text
+ language_id: 292
+PowerShell:
+ type: programming
+ color: "#012456"
+ ace_mode: powershell
+ codemirror_mode: powershell
+ codemirror_mime_type: application/x-powershell
+ aliases:
+ - posh
+ - pwsh
+ extensions:
+ - ".ps1"
+ - ".psd1"
+ - ".psm1"
+ interpreters:
+ - pwsh
+ language_id: 293
+Processing:
+ type: programming
+ color: "#0096D8"
+ extensions:
+ - ".pde"
+ ace_mode: text
+ language_id: 294
+Prolog:
+ type: programming
+ color: "#74283c"
+ extensions:
+ - ".pl"
+ - ".pro"
+ - ".prolog"
+ - ".yap"
+ interpreters:
+ - swipl
+ - yap
+ tm_scope: source.prolog
+ ace_mode: prolog
+ language_id: 295
+Propeller Spin:
+ type: programming
+ color: "#7fa2a7"
+ extensions:
+ - ".spin"
+ tm_scope: source.spin
+ ace_mode: text
+ language_id: 296
+Protocol Buffer:
+ type: data
+ aliases:
+ - protobuf
+ - Protocol Buffers
+ extensions:
+ - ".proto"
+ tm_scope: source.protobuf
+ ace_mode: protobuf
+ codemirror_mode: protobuf
+ codemirror_mime_type: text/x-protobuf
+ language_id: 297
+Public Key:
+ type: data
+ extensions:
+ - ".asc"
+ - ".pub"
+ tm_scope: none
+ ace_mode: text
+ codemirror_mode: asciiarmor
+ codemirror_mime_type: application/pgp
+ language_id: 298
+Pug:
+ group: HTML
+ type: markup
+ extensions:
+ - ".jade"
+ - ".pug"
+ tm_scope: text.jade
+ ace_mode: jade
+ codemirror_mode: pug
+ codemirror_mime_type: text/x-pug
+ language_id: 179
+Puppet:
+ type: programming
+ color: "#302B6D"
+ extensions:
+ - ".pp"
+ filenames:
+ - Modulefile
+ ace_mode: text
+ codemirror_mode: puppet
+ codemirror_mime_type: text/x-puppet
+ tm_scope: source.puppet
+ language_id: 299
+Pure Data:
+ type: data
+ extensions:
+ - ".pd"
+ tm_scope: none
+ ace_mode: text
+ language_id: 300
+PureBasic:
+ type: programming
+ color: "#5a6986"
+ extensions:
+ - ".pb"
+ - ".pbi"
+ tm_scope: none
+ ace_mode: text
+ language_id: 301
+PureScript:
+ type: programming
+ color: "#1D222D"
+ extensions:
+ - ".purs"
+ tm_scope: source.purescript
+ ace_mode: haskell
+ codemirror_mode: haskell
+ codemirror_mime_type: text/x-haskell
+ language_id: 302
+Python:
+ type: programming
+ ace_mode: python
+ codemirror_mode: python
+ codemirror_mime_type: text/x-python
+ color: "#3572A5"
+ extensions:
+ - ".py"
+ - ".bzl"
+ - ".cgi"
+ - ".fcgi"
+ - ".gyp"
+ - ".gypi"
+ - ".lmi"
+ - ".py3"
+ - ".pyde"
+ - ".pyi"
+ - ".pyp"
+ - ".pyt"
+ - ".pyw"
+ - ".rpy"
+ - ".spec"
+ - ".tac"
+ - ".wsgi"
+ - ".xpy"
+ filenames:
+ - ".gclient"
+ - BUCK
+ - BUILD
+ - BUILD.bazel
+ - SConscript
+ - SConstruct
+ - Snakefile
+ - WORKSPACE
+ - wscript
+ interpreters:
+ - python
+ - python2
+ - python3
+ aliases:
+ - rusthon
+ - python3
+ language_id: 303
+Python console:
+ type: programming
+ group: Python
+ searchable: false
+ aliases:
+ - pycon
+ tm_scope: text.python.console
+ ace_mode: text
+ language_id: 428
+Python traceback:
+ type: data
+ group: Python
+ searchable: false
+ extensions:
+ - ".pytb"
+ tm_scope: text.python.traceback
+ ace_mode: text
+ language_id: 304
+QML:
+ type: programming
+ color: "#44a51c"
+ extensions:
+ - ".qml"
+ - ".qbs"
+ tm_scope: source.qml
+ ace_mode: text
+ language_id: 305
+QMake:
+ type: programming
+ extensions:
+ - ".pro"
+ - ".pri"
+ interpreters:
+ - qmake
+ ace_mode: text
+ language_id: 306
+Quake:
+ type: programming
+ filenames:
+ - m3makefile
+ - m3overrides
+ color: "#882233"
+ ace_mode: text
+ tm_scope: source.quake
+ language_id: 375265331
+R:
+ type: programming
+ color: "#198CE7"
+ aliases:
+ - R
+ - Rscript
+ - splus
+ extensions:
+ - ".r"
+ - ".rd"
+ - ".rsx"
+ filenames:
+ - ".Rprofile"
+ - expr-dist
+ interpreters:
+ - Rscript
+ ace_mode: r
+ codemirror_mode: r
+ codemirror_mime_type: text/x-rsrc
+ language_id: 307
+RAML:
+ type: markup
+ ace_mode: yaml
+ codemirror_mode: yaml
+ codemirror_mime_type: text/x-yaml
+ tm_scope: source.yaml
+ color: "#77d9fb"
+ extensions:
+ - ".raml"
+ language_id: 308
+RDoc:
+ type: prose
+ ace_mode: rdoc
+ wrap: true
+ extensions:
+ - ".rdoc"
+ tm_scope: text.rdoc
+ language_id: 309
+REALbasic:
+ type: programming
+ extensions:
+ - ".rbbas"
+ - ".rbfrm"
+ - ".rbmnu"
+ - ".rbres"
+ - ".rbtbar"
+ - ".rbuistate"
+ tm_scope: source.vbnet
+ ace_mode: text
+ language_id: 310
+REXX:
+ type: programming
+ aliases:
+ - arexx
+ extensions:
+ - ".rexx"
+ - ".pprx"
+ - ".rex"
+ interpreters:
+ - regina
+ - rexx
+ tm_scope: source.rexx
+ ace_mode: text
+ language_id: 311
+RHTML:
+ type: markup
+ group: HTML
+ extensions:
+ - ".rhtml"
+ tm_scope: text.html.erb
+ aliases:
+ - html+ruby
+ ace_mode: rhtml
+ codemirror_mode: htmlembedded
+ codemirror_mime_type: application/x-erb
+ language_id: 312
+RMarkdown:
+ type: prose
+ wrap: true
+ ace_mode: markdown
+ codemirror_mode: gfm
+ codemirror_mime_type: text/x-gfm
+ extensions:
+ - ".rmd"
+ tm_scope: source.gfm
+ language_id: 313
+RPC:
+ type: programming
+ aliases:
+ - rpcgen
+ - oncrpc
+ - xdr
+ ace_mode: c_cpp
+ extensions:
+ - ".x"
+ tm_scope: source.c
+ language_id: 1031374237
+RPM Spec:
+ type: data
+ tm_scope: source.rpm-spec
+ extensions:
+ - ".spec"
+ aliases:
+ - specfile
+ ace_mode: text
+ codemirror_mode: rpm
+ codemirror_mime_type: text/x-rpm-spec
+ language_id: 314
+RUNOFF:
+ type: markup
+ color: "#665a4e"
+ extensions:
+ - ".rnh"
+ - ".rno"
+ tm_scope: text.runoff
+ ace_mode: text
+ language_id: 315
+Racket:
+ type: programming
+ color: "#22228f"
+ extensions:
+ - ".rkt"
+ - ".rktd"
+ - ".rktl"
+ - ".scrbl"
+ interpreters:
+ - racket
+ tm_scope: source.racket
+ ace_mode: lisp
+ language_id: 316
+Ragel:
+ type: programming
+ color: "#9d5200"
+ extensions:
+ - ".rl"
+ aliases:
+ - ragel-rb
+ - ragel-ruby
+ tm_scope: none
+ ace_mode: text
+ language_id: 317
+Rascal:
+ type: programming
+ color: "#fffaa0"
+ extensions:
+ - ".rsc"
+ tm_scope: source.rascal
+ ace_mode: text
+ language_id: 173616037
+Raw token data:
+ type: data
+ aliases:
+ - raw
+ extensions:
+ - ".raw"
+ tm_scope: none
+ ace_mode: text
+ language_id: 318
+Reason:
+ type: programming
+ group: OCaml
+ ace_mode: rust
+ codemirror_mode: rust
+ codemirror_mime_type: text/x-rustsrc
+ extensions:
+ - ".re"
+ - ".rei"
+ interpreters:
+ - ocaml
+ tm_scope: source.reason
+ language_id: 869538413
+Rebol:
+ type: programming
+ color: "#358a5b"
+ extensions:
+ - ".reb"
+ - ".r"
+ - ".r2"
+ - ".r3"
+ - ".rebol"
+ ace_mode: text
+ tm_scope: source.rebol
+ language_id: 319
+Red:
+ type: programming
+ color: "#f50000"
+ extensions:
+ - ".red"
+ - ".reds"
+ aliases:
+ - red/system
+ tm_scope: source.red
+ ace_mode: text
+ language_id: 320
+Redcode:
+ type: programming
+ extensions:
+ - ".cw"
+ tm_scope: none
+ ace_mode: text
+ language_id: 321
+Regular Expression:
+ type: data
+ extensions:
+ - ".regexp"
+ - ".regex"
+ aliases:
+ - regexp
+ - regex
+ ace_mode: text
+ tm_scope: source.regexp
+ language_id: 363378884
+Ren'Py:
+ type: programming
+ aliases:
+ - renpy
+ color: "#ff7f7f"
+ extensions:
+ - ".rpy"
+ tm_scope: source.renpy
+ ace_mode: python
+ language_id: 322
+RenderScript:
+ type: programming
+ extensions:
+ - ".rs"
+ - ".rsh"
+ tm_scope: none
+ ace_mode: text
+ language_id: 323
+Ring:
+ type: programming
+ color: "#2D54CB"
+ extensions:
+ - ".ring"
+ tm_scope: source.ring
+ ace_mode: text
+ language_id: 431
+RobotFramework:
+ type: programming
+ extensions:
+ - ".robot"
+ tm_scope: text.robot
+ ace_mode: text
+ language_id: 324
+Roff:
+ type: markup
+ color: "#ecdebe"
+ extensions:
+ - ".man"
+ - ".1"
+ - ".1in"
+ - ".1m"
+ - ".1x"
+ - ".2"
+ - ".3"
+ - ".3in"
+ - ".3m"
+ - ".3qt"
+ - ".3x"
+ - ".4"
+ - ".5"
+ - ".6"
+ - ".7"
+ - ".8"
+ - ".9"
+ - ".l"
+ - ".me"
+ - ".ms"
+ - ".n"
+ - ".nr"
+ - ".rno"
+ - ".roff"
+ - ".tmac"
+ filenames:
+ - mmn
+ - mmt
+ tm_scope: text.roff
+ aliases:
+ - nroff
+ ace_mode: text
+ codemirror_mode: troff
+ codemirror_mime_type: text/troff
+ language_id: 141
+Rouge:
+ type: programming
+ ace_mode: clojure
+ codemirror_mode: clojure
+ codemirror_mime_type: text/x-clojure
+ color: "#cc0088"
+ extensions:
+ - ".rg"
+ tm_scope: source.clojure
+ language_id: 325
+Ruby:
+ type: programming
+ ace_mode: ruby
+ codemirror_mode: ruby
+ codemirror_mime_type: text/x-ruby
+ color: "#701516"
+ aliases:
+ - jruby
+ - macruby
+ - rake
+ - rb
+ - rbx
+ extensions:
+ - ".rb"
+ - ".builder"
+ - ".eye"
+ - ".fcgi"
+ - ".gemspec"
+ - ".god"
+ - ".jbuilder"
+ - ".mspec"
+ - ".pluginspec"
+ - ".podspec"
+ - ".rabl"
+ - ".rake"
+ - ".rbuild"
+ - ".rbw"
+ - ".rbx"
+ - ".ru"
+ - ".ruby"
+ - ".spec"
+ - ".thor"
+ - ".watchr"
+ interpreters:
+ - ruby
+ - macruby
+ - rake
+ - jruby
+ - rbx
+ filenames:
+ - ".irbrc"
+ - ".pryrc"
+ - Appraisals
+ - Berksfile
+ - Brewfile
+ - Buildfile
+ - Capfile
+ - Dangerfile
+ - Deliverfile
+ - Fastfile
+ - Gemfile
+ - Gemfile.lock
+ - Guardfile
+ - Jarfile
+ - Mavenfile
+ - Podfile
+ - Puppetfile
+ - Rakefile
+ - Snapfile
+ - Thorfile
+ - Vagrantfile
+ - buildfile
+ language_id: 326
+Rust:
+ type: programming
+ color: "#dea584"
+ extensions:
+ - ".rs"
+ - ".rs.in"
+ ace_mode: rust
+ codemirror_mode: rust
+ codemirror_mime_type: text/x-rustsrc
+ language_id: 327
+SAS:
+ type: programming
+ color: "#B34936"
+ extensions:
+ - ".sas"
+ tm_scope: source.sas
+ ace_mode: text
+ codemirror_mode: sas
+ codemirror_mime_type: text/x-sas
+ language_id: 328
+SCSS:
+ type: markup
+ tm_scope: source.scss
+ group: CSS
+ ace_mode: scss
+ codemirror_mode: css
+ codemirror_mime_type: text/x-scss
+ extensions:
+ - ".scss"
+ language_id: 329
+SMT:
+ type: programming
+ extensions:
+ - ".smt2"
+ - ".smt"
+ interpreters:
+ - boolector
+ - cvc4
+ - mathsat5
+ - opensmt
+ - smtinterpol
+ - smt-rat
+ - stp
+ - verit
+ - yices2
+ - z3
+ tm_scope: source.smt
+ ace_mode: text
+ language_id: 330
+SPARQL:
+ type: data
+ tm_scope: source.sparql
+ ace_mode: text
+ codemirror_mode: sparql
+ codemirror_mime_type: application/sparql-query
+ extensions:
+ - ".sparql"
+ - ".rq"
+ language_id: 331
+SQF:
+ type: programming
+ color: "#3F3F3F"
+ extensions:
+ - ".sqf"
+ - ".hqf"
+ tm_scope: source.sqf
+ ace_mode: text
+ language_id: 332
+SQL:
+ type: data
+ tm_scope: source.sql
+ ace_mode: sql
+ codemirror_mode: sql
+ codemirror_mime_type: text/x-sql
+ extensions:
+ - ".sql"
+ - ".cql"
+ - ".ddl"
+ - ".inc"
+ - ".mysql"
+ - ".prc"
+ - ".tab"
+ - ".udf"
+ - ".viw"
+ language_id: 333
+SQLPL:
+ type: programming
+ ace_mode: sql
+ codemirror_mode: sql
+ codemirror_mime_type: text/x-sql
+ tm_scope: source.sql
+ extensions:
+ - ".sql"
+ - ".db2"
+ language_id: 334
+SRecode Template:
+ type: markup
+ color: "#348a34"
+ tm_scope: source.lisp
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ extensions:
+ - ".srt"
+ language_id: 335
+STON:
+ type: data
+ group: Smalltalk
+ extensions:
+ - ".ston"
+ tm_scope: source.smalltalk
+ ace_mode: text
+ language_id: 336
+SVG:
+ type: data
+ extensions:
+ - ".svg"
+ tm_scope: text.xml
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ language_id: 337
+Sage:
+ type: programming
+ group: Python
+ extensions:
+ - ".sage"
+ - ".sagews"
+ tm_scope: source.python
+ ace_mode: python
+ codemirror_mode: python
+ codemirror_mime_type: text/x-python
+ language_id: 338
+SaltStack:
+ type: programming
+ color: "#646464"
+ aliases:
+ - saltstate
+ - salt
+ extensions:
+ - ".sls"
+ tm_scope: source.yaml.salt
+ ace_mode: yaml
+ codemirror_mode: yaml
+ codemirror_mime_type: text/x-yaml
+ language_id: 339
+Sass:
+ type: markup
+ tm_scope: source.sass
+ group: CSS
+ extensions:
+ - ".sass"
+ ace_mode: sass
+ codemirror_mode: sass
+ codemirror_mime_type: text/x-sass
+ language_id: 340
+Scala:
+ type: programming
+ ace_mode: scala
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-scala
+ color: "#c22d40"
+ extensions:
+ - ".scala"
+ - ".kojo"
+ - ".sbt"
+ - ".sc"
+ interpreters:
+ - scala
+ language_id: 341
+Scaml:
+ group: HTML
+ type: markup
+ extensions:
+ - ".scaml"
+ tm_scope: source.scaml
+ ace_mode: text
+ language_id: 342
+Scheme:
+ type: programming
+ color: "#1e4aec"
+ extensions:
+ - ".scm"
+ - ".sch"
+ - ".sld"
+ - ".sls"
+ - ".sps"
+ - ".ss"
+ interpreters:
+ - guile
+ - bigloo
+ - chicken
+ - csi
+ - gosh
+ - r6rs
+ ace_mode: scheme
+ codemirror_mode: scheme
+ codemirror_mime_type: text/x-scheme
+ language_id: 343
+Scilab:
+ type: programming
+ extensions:
+ - ".sci"
+ - ".sce"
+ - ".tst"
+ ace_mode: text
+ language_id: 344
+Self:
+ type: programming
+ color: "#0579aa"
+ extensions:
+ - ".self"
+ tm_scope: none
+ ace_mode: text
+ language_id: 345
+ShaderLab:
+ type: programming
+ extensions:
+ - ".shader"
+ ace_mode: text
+ tm_scope: source.shaderlab
+ language_id: 664257356
+Shell:
+ type: programming
+ color: "#89e051"
+ aliases:
+ - sh
+ - shell-script
+ - bash
+ - zsh
+ extensions:
+ - ".sh"
+ - ".bash"
+ - ".bats"
+ - ".cgi"
+ - ".command"
+ - ".fcgi"
+ - ".ksh"
+ - ".sh.in"
+ - ".tmux"
+ - ".tool"
+ - ".zsh"
+ filenames:
+ - ".bash_history"
+ - ".bash_logout"
+ - ".bash_profile"
+ - ".bashrc"
+ - ".cshrc"
+ - ".login"
+ - ".profile"
+ - ".zlogin"
+ - ".zlogout"
+ - ".zprofile"
+ - ".zshenv"
+ - ".zshrc"
+ - 9fs
+ - PKGBUILD
+ - bash_logout
+ - bash_profile
+ - bashrc
+ - cshrc
+ - gradlew
+ - login
+ - man
+ - profile
+ - zlogin
+ - zlogout
+ - zprofile
+ - zshenv
+ - zshrc
+ interpreters:
+ - ash
+ - bash
+ - dash
+ - ksh
+ - mksh
+ - pdksh
+ - rc
+ - sh
+ - zsh
+ ace_mode: sh
+ codemirror_mode: shell
+ codemirror_mime_type: text/x-sh
+ language_id: 346
+ShellSession:
+ type: programming
+ extensions:
+ - ".sh-session"
+ aliases:
+ - bash session
+ - console
+ tm_scope: text.shell-session
+ ace_mode: sh
+ codemirror_mode: shell
+ codemirror_mime_type: text/x-sh
+ language_id: 347
+Shen:
+ type: programming
+ color: "#120F14"
+ extensions:
+ - ".shen"
+ tm_scope: source.shen
+ ace_mode: text
+ language_id: 348
+Slash:
+ type: programming
+ color: "#007eff"
+ extensions:
+ - ".sl"
+ tm_scope: text.html.slash
+ ace_mode: text
+ language_id: 349
+Slim:
+ group: HTML
+ type: markup
+ extensions:
+ - ".slim"
+ tm_scope: text.slim
+ ace_mode: text
+ codemirror_mode: slim
+ codemirror_mime_type: text/x-slim
+ language_id: 350
+Smali:
+ type: programming
+ extensions:
+ - ".smali"
+ ace_mode: text
+ tm_scope: source.smali
+ language_id: 351
+Smalltalk:
+ type: programming
+ color: "#596706"
+ extensions:
+ - ".st"
+ - ".cs"
+ aliases:
+ - squeak
+ ace_mode: text
+ codemirror_mode: smalltalk
+ codemirror_mime_type: text/x-stsrc
+ language_id: 352
+Smarty:
+ type: programming
+ extensions:
+ - ".tpl"
+ ace_mode: smarty
+ codemirror_mode: smarty
+ codemirror_mime_type: text/x-smarty
+ tm_scope: text.html.smarty
+ language_id: 353
+Solidity:
+ type: programming
+ color: "#AA6746"
+ ace_mode: text
+ tm_scope: source.solidity
+ language_id: 237469032
+SourcePawn:
+ type: programming
+ color: "#5c7611"
+ aliases:
+ - sourcemod
+ extensions:
+ - ".sp"
+ - ".inc"
+ - ".sma"
+ tm_scope: source.sp
+ ace_mode: text
+ language_id: 354
+Spline Font Database:
+ type: data
+ extensions:
+ - ".sfd"
+ tm_scope: text.sfd
+ ace_mode: yaml
+ language_id: 767169629
+Squirrel:
+ type: programming
+ color: "#800000"
+ extensions:
+ - ".nut"
+ tm_scope: source.c++
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-c++src
+ language_id: 355
+Stan:
+ type: programming
+ color: "#b2011d"
+ extensions:
+ - ".stan"
+ ace_mode: text
+ tm_scope: source.stan
+ language_id: 356
+Standard ML:
+ type: programming
+ color: "#dc566d"
+ aliases:
+ - sml
+ extensions:
+ - ".ML"
+ - ".fun"
+ - ".sig"
+ - ".sml"
+ tm_scope: source.ml
+ ace_mode: text
+ codemirror_mode: mllike
+ codemirror_mime_type: text/x-ocaml
+ language_id: 357
+Stata:
+ type: programming
+ extensions:
+ - ".do"
+ - ".ado"
+ - ".doh"
+ - ".ihlp"
+ - ".mata"
+ - ".matah"
+ - ".sthlp"
+ ace_mode: text
+ language_id: 358
+Stylus:
+ type: markup
+ group: CSS
+ extensions:
+ - ".styl"
+ tm_scope: source.stylus
+ ace_mode: stylus
+ language_id: 359
+SubRip Text:
+ type: data
+ extensions:
+ - ".srt"
+ ace_mode: text
+ tm_scope: text.srt
+ language_id: 360
+SugarSS:
+ type: markup
+ tm_scope: source.css.postcss.sugarss
+ group: CSS
+ extensions:
+ - ".sss"
+ ace_mode: text
+ language_id: 826404698
+SuperCollider:
+ type: programming
+ color: "#46390b"
+ extensions:
+ - ".sc"
+ - ".scd"
+ interpreters:
+ - sclang
+ - scsynth
+ tm_scope: source.supercollider
+ ace_mode: text
+ language_id: 361
+Swift:
+ type: programming
+ color: "#ffac45"
+ extensions:
+ - ".swift"
+ ace_mode: text
+ codemirror_mode: swift
+ codemirror_mime_type: text/x-swift
+ language_id: 362
+SystemVerilog:
+ type: programming
+ color: "#DAE1C2"
+ extensions:
+ - ".sv"
+ - ".svh"
+ - ".vh"
+ ace_mode: verilog
+ codemirror_mode: verilog
+ codemirror_mime_type: text/x-systemverilog
+ language_id: 363
+TI Program:
+ type: programming
+ ace_mode: text
+ color: "#A0AA87"
+ extensions:
+ - ".8xp"
+ - ".8xk"
+ - ".8xk.txt"
+ - ".8xp.txt"
+ language_id: 422
+ tm_scope: none
+TLA:
+ type: programming
+ extensions:
+ - ".tla"
+ tm_scope: source.tla
+ ace_mode: text
+ language_id: 364
+TOML:
+ type: data
+ extensions:
+ - ".toml"
+ filenames:
+ - Cargo.lock
+ - Gopkg.lock
+ tm_scope: source.toml
+ ace_mode: toml
+ codemirror_mode: toml
+ codemirror_mime_type: text/x-toml
+ language_id: 365
+TXL:
+ type: programming
+ extensions:
+ - ".txl"
+ tm_scope: source.txl
+ ace_mode: text
+ language_id: 366
+Tcl:
+ type: programming
+ color: "#e4cc98"
+ extensions:
+ - ".tcl"
+ - ".adp"
+ - ".tm"
+ filenames:
+ - owh
+ - starfield
+ interpreters:
+ - tclsh
+ - wish
+ ace_mode: tcl
+ codemirror_mode: tcl
+ codemirror_mime_type: text/x-tcl
+ language_id: 367
+Tcsh:
+ type: programming
+ group: Shell
+ extensions:
+ - ".tcsh"
+ - ".csh"
+ tm_scope: source.shell
+ ace_mode: sh
+ codemirror_mode: shell
+ codemirror_mime_type: text/x-sh
+ language_id: 368
+TeX:
+ type: markup
+ color: "#3D6117"
+ ace_mode: tex
+ codemirror_mode: stex
+ codemirror_mime_type: text/x-stex
+ wrap: true
+ aliases:
+ - latex
+ extensions:
+ - ".tex"
+ - ".aux"
+ - ".bbx"
+ - ".bib"
+ - ".cbx"
+ - ".cls"
+ - ".dtx"
+ - ".ins"
+ - ".lbx"
+ - ".ltx"
+ - ".mkii"
+ - ".mkiv"
+ - ".mkvi"
+ - ".sty"
+ - ".toc"
+ language_id: 369
+Tea:
+ type: markup
+ extensions:
+ - ".tea"
+ tm_scope: source.tea
+ ace_mode: text
+ language_id: 370
+Terra:
+ type: programming
+ extensions:
+ - ".t"
+ color: "#00004c"
+ ace_mode: lua
+ codemirror_mode: lua
+ codemirror_mime_type: text/x-lua
+ interpreters:
+ - lua
+ language_id: 371
+Text:
+ type: prose
+ wrap: true
+ aliases:
+ - fundamental
+ extensions:
+ - ".txt"
+ - ".fr"
+ - ".nb"
+ - ".ncl"
+ - ".no"
+ filenames:
+ - COPYING
+ - COPYING.regex
+ - COPYRIGHT.regex
+ - FONTLOG
+ - INSTALL
+ - INSTALL.mysql
+ - LICENSE
+ - LICENSE.mysql
+ - NEWS
+ - README.1ST
+ - README.me
+ - README.mysql
+ - click.me
+ - delete.me
+ - keep.me
+ - read.me
+ - readme.1st
+ - test.me
+ tm_scope: none
+ ace_mode: text
+ language_id: 372
+Textile:
+ type: prose
+ ace_mode: textile
+ codemirror_mode: textile
+ codemirror_mime_type: text/x-textile
+ wrap: true
+ extensions:
+ - ".textile"
+ tm_scope: none
+ language_id: 373
+Thrift:
+ type: programming
+ tm_scope: source.thrift
+ extensions:
+ - ".thrift"
+ ace_mode: text
+ language_id: 374
+Turing:
+ type: programming
+ color: "#cf142b"
+ extensions:
+ - ".t"
+ - ".tu"
+ tm_scope: source.turing
+ ace_mode: text
+ language_id: 375
+Turtle:
+ type: data
+ extensions:
+ - ".ttl"
+ tm_scope: source.turtle
+ ace_mode: text
+ codemirror_mode: turtle
+ codemirror_mime_type: text/turtle
+ language_id: 376
+Twig:
+ type: markup
+ group: HTML
+ extensions:
+ - ".twig"
+ tm_scope: text.html.twig
+ ace_mode: twig
+ codemirror_mode: twig
+ codemirror_mime_type: text/x-twig
+ language_id: 377
+Type Language:
+ type: data
+ aliases:
+ - tl
+ extensions:
+ - ".tl"
+ tm_scope: source.tl
+ ace_mode: text
+ language_id: 632765617
+TypeScript:
+ type: programming
+ color: "#2b7489"
+ aliases:
+ - ts
+ extensions:
+ - ".ts"
+ - ".tsx"
+ tm_scope: source.ts
+ ace_mode: typescript
+ codemirror_mode: javascript
+ codemirror_mime_type: application/typescript
+ language_id: 378
+Unified Parallel C:
+ type: programming
+ group: C
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csrc
+ extensions:
+ - ".upc"
+ tm_scope: source.c
+ language_id: 379
+Unity3D Asset:
+ type: data
+ ace_mode: yaml
+ codemirror_mode: yaml
+ codemirror_mime_type: text/x-yaml
+ extensions:
+ - ".anim"
+ - ".asset"
+ - ".mat"
+ - ".meta"
+ - ".prefab"
+ - ".unity"
+ tm_scope: source.yaml
+ language_id: 380
+Unix Assembly:
+ type: programming
+ group: Assembly
+ extensions:
+ - ".s"
+ - ".ms"
+ tm_scope: source.x86
+ ace_mode: assembly_x86
+ language_id: 120
+Uno:
+ type: programming
+ extensions:
+ - ".uno"
+ ace_mode: csharp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csharp
+ tm_scope: source.cs
+ language_id: 381
+UnrealScript:
+ type: programming
+ color: "#a54c4d"
+ extensions:
+ - ".uc"
+ tm_scope: source.java
+ ace_mode: java
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-java
+ language_id: 382
+UrWeb:
+ type: programming
+ aliases:
+ - Ur/Web
+ - Ur
+ extensions:
+ - ".ur"
+ - ".urs"
+ tm_scope: source.ur
+ ace_mode: text
+ language_id: 383
+VCL:
+ type: programming
+ color: "#148AA8"
+ extensions:
+ - ".vcl"
+ tm_scope: source.varnish.vcl
+ ace_mode: text
+ language_id: 384
+VHDL:
+ type: programming
+ color: "#adb2cb"
+ extensions:
+ - ".vhdl"
+ - ".vhd"
+ - ".vhf"
+ - ".vhi"
+ - ".vho"
+ - ".vhs"
+ - ".vht"
+ - ".vhw"
+ ace_mode: vhdl
+ codemirror_mode: vhdl
+ codemirror_mime_type: text/x-vhdl
+ language_id: 385
+Vala:
+ type: programming
+ color: "#fbe5cd"
+ extensions:
+ - ".vala"
+ - ".vapi"
+ ace_mode: vala
+ language_id: 386
+Verilog:
+ type: programming
+ color: "#b2b7f8"
+ extensions:
+ - ".v"
+ - ".veo"
+ ace_mode: verilog
+ codemirror_mode: verilog
+ codemirror_mime_type: text/x-verilog
+ language_id: 387
+Vim script:
+ type: programming
+ color: "#199f4b"
+ tm_scope: source.viml
+ aliases:
+ - vim
+ - viml
+ - nvim
+ extensions:
+ - ".vim"
+ filenames:
+ - ".gvimrc"
+ - ".nvimrc"
+ - ".vimrc"
+ - _vimrc
+ - gvimrc
+ - nvimrc
+ - vimrc
+ ace_mode: text
+ language_id: 388
+Visual Basic:
+ type: programming
+ color: "#945db7"
+ extensions:
+ - ".vb"
+ - ".bas"
+ - ".cls"
+ - ".frm"
+ - ".frx"
+ - ".vba"
+ - ".vbhtml"
+ - ".vbs"
+ tm_scope: source.vbnet
+ aliases:
+ - vb.net
+ - vbnet
+ ace_mode: text
+ codemirror_mode: vb
+ codemirror_mime_type: text/x-vb
+ language_id: 389
+Volt:
+ type: programming
+ color: "#1F1F1F"
+ extensions:
+ - ".volt"
+ tm_scope: source.d
+ ace_mode: d
+ codemirror_mode: d
+ codemirror_mime_type: text/x-d
+ language_id: 390
+Vue:
+ type: markup
+ color: "#2c3e50"
+ extensions:
+ - ".vue"
+ tm_scope: text.html.vue
+ ace_mode: html
+ language_id: 391
+Wavefront Material:
+ type: data
+ extensions:
+ - ".mtl"
+ tm_scope: source.wavefront.mtl
+ ace_mode: text
+ language_id: 392
+Wavefront Object:
+ type: data
+ extensions:
+ - ".obj"
+ tm_scope: source.wavefront.obj
+ ace_mode: text
+ language_id: 393
+Web Ontology Language:
+ type: data
+ extensions:
+ - ".owl"
+ tm_scope: text.xml
+ ace_mode: xml
+ language_id: 394
+WebAssembly:
+ type: programming
+ color: "#04133b"
+ extensions:
+ - ".wast"
+ - ".wat"
+ aliases:
+ - wast
+ - wasm
+ tm_scope: source.webassembly
+ ace_mode: lisp
+ codemirror_mode: commonlisp
+ codemirror_mime_type: text/x-common-lisp
+ language_id: 956556503
+WebIDL:
+ type: programming
+ extensions:
+ - ".webidl"
+ tm_scope: source.webidl
+ ace_mode: text
+ codemirror_mode: webidl
+ codemirror_mime_type: text/x-webidl
+ language_id: 395
+World of Warcraft Addon Data:
+ type: data
+ extensions:
+ - ".toc"
+ tm_scope: source.toc
+ ace_mode: text
+ language_id: 396
+X BitMap:
+ type: data
+ group: C
+ aliases:
+ - xbm
+ extensions:
+ - ".xbm"
+ ace_mode: c_cpp
+ tm_scope: source.c
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csrc
+ language_id: 782911107
+X PixMap:
+ type: data
+ group: C
+ aliases:
+ - xpm
+ extensions:
+ - ".xpm"
+ - ".pm"
+ ace_mode: c_cpp
+ tm_scope: source.c
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csrc
+ language_id: 781846279
+X10:
+ type: programming
+ aliases:
+ - xten
+ ace_mode: text
+ extensions:
+ - ".x10"
+ color: "#4B6BEF"
+ tm_scope: source.x10
+ language_id: 397
+XC:
+ type: programming
+ color: "#99DA07"
+ extensions:
+ - ".xc"
+ tm_scope: source.xc
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csrc
+ language_id: 398
+XCompose:
+ type: data
+ filenames:
+ - ".XCompose"
+ - XCompose
+ - xcompose
+ tm_scope: config.xcompose
+ ace_mode: text
+ language_id: 225167241
+XML:
+ type: data
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ aliases:
+ - rss
+ - xsd
+ - wsdl
+ extensions:
+ - ".xml"
+ - ".adml"
+ - ".admx"
+ - ".ant"
+ - ".axml"
+ - ".builds"
+ - ".ccproj"
+ - ".ccxml"
+ - ".clixml"
+ - ".cproject"
+ - ".cscfg"
+ - ".csdef"
+ - ".csl"
+ - ".csproj"
+ - ".ct"
+ - ".depproj"
+ - ".dita"
+ - ".ditamap"
+ - ".ditaval"
+ - ".dll.config"
+ - ".dotsettings"
+ - ".filters"
+ - ".fsproj"
+ - ".fxml"
+ - ".glade"
+ - ".gml"
+ - ".grxml"
+ - ".iml"
+ - ".ivy"
+ - ".jelly"
+ - ".jsproj"
+ - ".kml"
+ - ".launch"
+ - ".mdpolicy"
+ - ".mjml"
+ - ".mm"
+ - ".mod"
+ - ".mxml"
+ - ".natvis"
+ - ".ncl"
+ - ".ndproj"
+ - ".nproj"
+ - ".nuspec"
+ - ".odd"
+ - ".osm"
+ - ".pkgproj"
+ - ".plist"
+ - ".pluginspec"
+ - ".proj"
+ - ".props"
+ - ".ps1xml"
+ - ".psc1"
+ - ".pt"
+ - ".rdf"
+ - ".resx"
+ - ".rss"
+ - ".sch"
+ - ".scxml"
+ - ".sfproj"
+ - ".shproj"
+ - ".srdf"
+ - ".storyboard"
+ - ".stTheme"
+ - ".sublime-snippet"
+ - ".targets"
+ - ".tmCommand"
+ - ".tml"
+ - ".tmLanguage"
+ - ".tmPreferences"
+ - ".tmSnippet"
+ - ".tmTheme"
+ - ".ts"
+ - ".tsx"
+ - ".ui"
+ - ".urdf"
+ - ".ux"
+ - ".vbproj"
+ - ".vcxproj"
+ - ".vsixmanifest"
+ - ".vssettings"
+ - ".vstemplate"
+ - ".vxml"
+ - ".wixproj"
+ - ".wsdl"
+ - ".wsf"
+ - ".wxi"
+ - ".wxl"
+ - ".wxs"
+ - ".x3d"
+ - ".xacro"
+ - ".xaml"
+ - ".xib"
+ - ".xlf"
+ - ".xliff"
+ - ".xmi"
+ - ".xml.dist"
+ - ".xproj"
+ - ".xsd"
+ - ".xspec"
+ - ".xul"
+ - ".zcml"
+ filenames:
+ - ".classpath"
+ - ".cproject"
+ - ".project"
+ - App.config
+ - NuGet.config
+ - Settings.StyleCop
+ - Web.Debug.config
+ - Web.Release.config
+ - Web.config
+ - packages.config
+ language_id: 399
+XPages:
+ type: data
+ extensions:
+ - ".xsp-config"
+ - ".xsp.metadata"
+ tm_scope: text.xml
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ language_id: 400
+XProc:
+ type: programming
+ extensions:
+ - ".xpl"
+ - ".xproc"
+ tm_scope: text.xml
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ language_id: 401
+XQuery:
+ type: programming
+ color: "#5232e7"
+ extensions:
+ - ".xquery"
+ - ".xq"
+ - ".xql"
+ - ".xqm"
+ - ".xqy"
+ ace_mode: xquery
+ codemirror_mode: xquery
+ codemirror_mime_type: application/xquery
+ tm_scope: source.xq
+ language_id: 402
+XS:
+ type: programming
+ extensions:
+ - ".xs"
+ tm_scope: source.c
+ ace_mode: c_cpp
+ codemirror_mode: clike
+ codemirror_mime_type: text/x-csrc
+ language_id: 403
+XSLT:
+ type: programming
+ aliases:
+ - xsl
+ extensions:
+ - ".xslt"
+ - ".xsl"
+ tm_scope: text.xml.xsl
+ ace_mode: xml
+ codemirror_mode: xml
+ codemirror_mime_type: text/xml
+ color: "#EB8CEB"
+ language_id: 404
+Xojo:
+ type: programming
+ extensions:
+ - ".xojo_code"
+ - ".xojo_menu"
+ - ".xojo_report"
+ - ".xojo_script"
+ - ".xojo_toolbar"
+ - ".xojo_window"
+ tm_scope: source.vbnet
+ ace_mode: text
+ language_id: 405
+Xtend:
+ type: programming
+ extensions:
+ - ".xtend"
+ ace_mode: text
+ language_id: 406
+YAML:
+ type: data
+ tm_scope: source.yaml
+ aliases:
+ - yml
+ extensions:
+ - ".yml"
+ - ".mir"
+ - ".reek"
+ - ".rviz"
+ - ".sublime-syntax"
+ - ".syntax"
+ - ".yaml"
+ - ".yaml-tmlanguage"
+ - ".yml.mysql"
+ filenames:
+ - ".clang-format"
+ - ".clang-tidy"
+ - ".gemrc"
+ - glide.lock
+ ace_mode: yaml
+ codemirror_mode: yaml
+ codemirror_mime_type: text/x-yaml
+ language_id: 407
+YANG:
+ type: data
+ extensions:
+ - ".yang"
+ tm_scope: source.yang
+ ace_mode: text
+ language_id: 408
+YARA:
+ type: data
+ ace_mode: text
+ extensions:
+ - ".yar"
+ - ".yara"
+ tm_scope: source.yara
+ language_id: 805122868
+Yacc:
+ type: programming
+ extensions:
+ - ".y"
+ - ".yacc"
+ - ".yy"
+ tm_scope: source.bison
+ ace_mode: text
+ color: "#4B6C4B"
+ language_id: 409
+Zephir:
+ type: programming
+ color: "#118f9e"
+ extensions:
+ - ".zep"
+ tm_scope: source.php.zephir
+ ace_mode: php
+ language_id: 410
+Zimpl:
+ type: programming
+ extensions:
+ - ".zimpl"
+ - ".zmpl"
+ - ".zpl"
+ tm_scope: none
+ ace_mode: text
+ language_id: 411
+desktop:
+ type: data
+ extensions:
+ - ".desktop"
+ - ".desktop.in"
+ tm_scope: source.desktop
+ ace_mode: text
+ language_id: 412
+eC:
+ type: programming
+ color: "#913960"
+ extensions:
+ - ".ec"
+ - ".eh"
+ tm_scope: source.c.ec
+ ace_mode: text
+ language_id: 413
+edn:
+ type: data
+ ace_mode: clojure
+ codemirror_mode: clojure
+ codemirror_mime_type: text/x-clojure
+ extensions:
+ - ".edn"
+ tm_scope: source.clojure
+ language_id: 414
+fish:
+ type: programming
+ group: Shell
+ interpreters:
+ - fish
+ extensions:
+ - ".fish"
+ tm_scope: source.fish
+ ace_mode: text
+ language_id: 415
+mupad:
+ type: programming
+ extensions:
+ - ".mu"
+ ace_mode: text
+ language_id: 416
+nesC:
+ type: programming
+ color: "#94B0C7"
+ extensions:
+ - ".nc"
+ ace_mode: text
+ tm_scope: source.nesc
+ language_id: 417
+ooc:
+ type: programming
+ color: "#b0b77e"
+ extensions:
+ - ".ooc"
+ ace_mode: text
+ language_id: 418
+q:
+ type: programming
+ extensions:
+ - ".q"
+ tm_scope: source.q
+ ace_mode: text
+ color: "#0040cd"
+ language_id: 970539067
+reStructuredText:
+ type: prose
+ wrap: true
+ aliases:
+ - rst
+ extensions:
+ - ".rst"
+ - ".rest"
+ - ".rest.txt"
+ - ".rst.txt"
+ ace_mode: text
+ codemirror_mode: rst
+ codemirror_mime_type: text/x-rst
+ language_id: 419
+sed:
+ type: programming
+ color: "#64b970"
+ extensions:
+ - ".sed"
+ interpreters:
+ - gsed
+ - minised
+ - sed
+ - ssed
+ ace_mode: text
+ tm_scope: source.sed
+ language_id: 847830017
+wdl:
+ type: programming
+ color: "#42f1f4"
+ extensions:
+ - ".wdl"
+ tm_scope: source.wdl
+ ace_mode: text
+ language_id: 374521672
+wisp:
+ type: programming
+ ace_mode: clojure
+ codemirror_mode: clojure
+ codemirror_mime_type: text/x-clojure
+ color: "#7582D1"
+ extensions:
+ - ".wisp"
+ tm_scope: source.clojure
+ language_id: 420
+xBase:
+ type: programming
+ color: "#403a40"
+ aliases:
+ - advpl
+ - clipper
+ - foxpro
+ extensions:
+ - ".prg"
+ - ".ch"
+ - ".prw"
+ tm_scope: source.harbour
+ ace_mode: text
+ language_id: 421
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index a462daf3067..f6fd1efaa83 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -1,45 +1,103 @@
-@babel/code-frame,7.0.0-beta.44,MIT
-@babel/generator,7.0.0-beta.44,MIT
-@babel/helper-function-name,7.0.0-beta.44,MIT
-@babel/helper-get-function-arity,7.0.0-beta.44,MIT
-@babel/helper-split-export-declaration,7.0.0-beta.44,MIT
-@babel/highlight,7.0.0-beta.44,MIT
-@babel/template,7.0.0-beta.44,MIT
-@babel/traverse,7.0.0-beta.44,MIT
-@babel/types,7.0.0-beta.44,MIT
-@gitlab-org/gitlab-svgs,1.27.0,SEE LICENSE IN LICENSE
-@gitlab-org/gitlab-ui,1.0.5,UNKNOWN
+@babel/code-frame,7.0.0,MIT
+@babel/core,7.1.2,MIT
+@babel/generator,7.1.2,MIT
+@babel/helper-annotate-as-pure,7.0.0,MIT
+@babel/helper-builder-binary-assignment-operator-visitor,7.1.0,MIT
+@babel/helper-call-delegate,7.1.0,MIT
+@babel/helper-define-map,7.1.0,MIT
+@babel/helper-explode-assignable-expression,7.1.0,MIT
+@babel/helper-function-name,7.1.0,MIT
+@babel/helper-get-function-arity,7.0.0,MIT
+@babel/helper-hoist-variables,7.0.0,MIT
+@babel/helper-member-expression-to-functions,7.0.0,MIT
+@babel/helper-module-imports,7.0.0,MIT
+@babel/helper-module-transforms,7.1.0,MIT
+@babel/helper-optimise-call-expression,7.0.0,MIT
+@babel/helper-plugin-utils,7.0.0,MIT
+@babel/helper-regex,7.0.0,MIT
+@babel/helper-remap-async-to-generator,7.1.0,MIT
+@babel/helper-replace-supers,7.1.0,MIT
+@babel/helper-simple-access,7.1.0,MIT
+@babel/helper-split-export-declaration,7.0.0,MIT
+@babel/helper-wrap-function,7.1.0,MIT
+@babel/helpers,7.1.2,MIT
+@babel/highlight,7.0.0,MIT
+@babel/parser,7.1.2,MIT
+@babel/plugin-proposal-async-generator-functions,7.1.0,MIT
+@babel/plugin-proposal-class-properties,7.1.0,MIT
+@babel/plugin-proposal-json-strings,7.0.0,MIT
+@babel/plugin-proposal-object-rest-spread,7.0.0,MIT
+@babel/plugin-proposal-optional-catch-binding,7.0.0,MIT
+@babel/plugin-proposal-unicode-property-regex,7.0.0,MIT
+@babel/plugin-syntax-async-generators,7.0.0,MIT
+@babel/plugin-syntax-class-properties,7.0.0,MIT
+@babel/plugin-syntax-dynamic-import,7.0.0,MIT
+@babel/plugin-syntax-import-meta,7.0.0,MIT
+@babel/plugin-syntax-json-strings,7.0.0,MIT
+@babel/plugin-syntax-object-rest-spread,7.0.0,MIT
+@babel/plugin-syntax-optional-catch-binding,7.0.0,MIT
+@babel/plugin-transform-arrow-functions,7.0.0,MIT
+@babel/plugin-transform-async-to-generator,7.1.0,MIT
+@babel/plugin-transform-block-scoped-functions,7.0.0,MIT
+@babel/plugin-transform-block-scoping,7.0.0,MIT
+@babel/plugin-transform-classes,7.1.0,MIT
+@babel/plugin-transform-computed-properties,7.0.0,MIT
+@babel/plugin-transform-destructuring,7.1.2,MIT
+@babel/plugin-transform-dotall-regex,7.0.0,MIT
+@babel/plugin-transform-duplicate-keys,7.0.0,MIT
+@babel/plugin-transform-exponentiation-operator,7.1.0,MIT
+@babel/plugin-transform-for-of,7.0.0,MIT
+@babel/plugin-transform-function-name,7.1.0,MIT
+@babel/plugin-transform-literals,7.0.0,MIT
+@babel/plugin-transform-modules-amd,7.1.0,MIT
+@babel/plugin-transform-modules-commonjs,7.1.0,MIT
+@babel/plugin-transform-modules-systemjs,7.0.0,MIT
+@babel/plugin-transform-modules-umd,7.1.0,MIT
+@babel/plugin-transform-new-target,7.0.0,MIT
+@babel/plugin-transform-object-super,7.1.0,MIT
+@babel/plugin-transform-parameters,7.1.0,MIT
+@babel/plugin-transform-regenerator,7.0.0,MIT
+@babel/plugin-transform-shorthand-properties,7.0.0,MIT
+@babel/plugin-transform-spread,7.0.0,MIT
+@babel/plugin-transform-sticky-regex,7.0.0,MIT
+@babel/plugin-transform-template-literals,7.0.0,MIT
+@babel/plugin-transform-typeof-symbol,7.0.0,MIT
+@babel/plugin-transform-unicode-regex,7.0.0,MIT
+@babel/preset-env,7.1.0,MIT
+@babel/template,7.1.2,MIT
+@babel/traverse,7.1.0,MIT
+@babel/types,7.1.2,MIT
+@gitlab/svgs,1.35.0,MIT
+@gitlab/ui,1.11.0,MIT
@sindresorhus/is,0.7.0,MIT
@types/jquery,2.0.48,MIT
-@vue/component-compiler-utils,1.2.1,MIT
-@webassemblyjs/ast,1.5.13,MIT
-@webassemblyjs/floating-point-hex-parser,1.5.13,MIT
-@webassemblyjs/helper-api-error,1.5.13,MIT
-@webassemblyjs/helper-buffer,1.5.13,MIT
-@webassemblyjs/helper-code-frame,1.5.13,MIT
-@webassemblyjs/helper-fsm,1.5.13,ISC
-@webassemblyjs/helper-module-context,1.5.13,MIT
-@webassemblyjs/helper-wasm-bytecode,1.5.13,MIT
-@webassemblyjs/helper-wasm-section,1.5.13,MIT
-@webassemblyjs/ieee754,1.5.13,MIT
-@webassemblyjs/leb128,1.5.13,MIT
-@webassemblyjs/utf8,1.5.13,MIT
-@webassemblyjs/wasm-edit,1.5.13,MIT
-@webassemblyjs/wasm-gen,1.5.13,MIT
-@webassemblyjs/wasm-opt,1.5.13,MIT
-@webassemblyjs/wasm-parser,1.5.13,MIT
-@webassemblyjs/wast-parser,1.5.13,MIT
-@webassemblyjs/wast-printer,1.5.13,MIT
+@vue/component-compiler-utils,2.2.0,MIT
+@webassemblyjs/ast,1.7.6,MIT
+@webassemblyjs/floating-point-hex-parser,1.7.6,MIT
+@webassemblyjs/helper-api-error,1.7.6,MIT
+@webassemblyjs/helper-buffer,1.7.6,MIT
+@webassemblyjs/helper-code-frame,1.7.6,MIT
+@webassemblyjs/helper-fsm,1.7.6,ISC
+@webassemblyjs/helper-module-context,1.7.6,MIT
+@webassemblyjs/helper-wasm-bytecode,1.7.6,MIT
+@webassemblyjs/helper-wasm-section,1.7.6,MIT
+@webassemblyjs/ieee754,1.7.6,MIT
+@webassemblyjs/leb128,1.7.6,MIT
+@webassemblyjs/utf8,1.7.6,MIT
+@webassemblyjs/wasm-edit,1.7.6,MIT
+@webassemblyjs/wasm-gen,1.7.6,MIT
+@webassemblyjs/wasm-opt,1.7.6,MIT
+@webassemblyjs/wasm-parser,1.7.6,MIT
+@webassemblyjs/wast-parser,1.7.6,MIT
+@webassemblyjs/wast-printer,1.7.6,MIT
+@xtuc/ieee754,1.2.0,New BSD
+@xtuc/long,4.2.1,Apache 2.0
RedCloth,4.3.2,MIT
abbrev,1.0.9,ISC
-abbrev,1.1.1,ISC
-accepts,1.3.4,MIT
+accepts,1.3.5,MIT
ace-rails-ap,4.1.2,MIT
-acorn,3.3.0,MIT
-acorn,5.6.2,MIT
-acorn,5.7.1,MIT
+acorn,5.7.3,MIT
acorn-dynamic-import,3.0.0,MIT
-acorn-jsx,3.0.1,MIT
actionmailer,4.2.10,MIT
actionpack,4.2.10,MIT
actionview,4.2.10,MIT
@@ -49,226 +107,91 @@ activerecord,4.2.10,MIT
activesupport,4.2.10,MIT
acts-as-taggable-on,5.0.0,MIT
addressable,2.5.2,Apache 2.0
-addressparser,1.0.1,MIT
aes_key_wrap,1.0.1,MIT
-after,0.8.2,MIT
-agent-base,4.2.1,MIT
-ajv,5.5.2,MIT
-ajv,6.1.1,MIT
-ajv-keywords,2.1.1,MIT
-ajv-keywords,3.1.0,MIT
+ajv,6.5.3,MIT
+ajv-errors,1.0.0,MIT
+ajv-keywords,3.2.0,MIT
akismet,2.0.0,MIT
-align-text,0.1.4,MIT
-amdefine,1.0.1,BSD-3-Clause OR MIT
-amqplib,0.5.2,MIT
-ansi-align,2.0.0,ISC
ansi-escapes,1.4.0,MIT
ansi-escapes,3.0.0,MIT
-ansi-html,0.0.7,Apache 2.0
ansi-regex,2.1.1,MIT
ansi-regex,3.0.0,MIT
ansi-styles,2.2.1,MIT
ansi-styles,3.2.1,MIT
anymatch,2.0.0,ISC
-append-transform,0.4.0,MIT
aproba,1.2.0,ISC
are-we-there-yet,1.1.4,ISC
arel,6.0.4,MIT
-argparse,1.0.9,MIT
arr-diff,4.0.0,MIT
arr-flatten,1.1.0,MIT
arr-union,3.1.0,MIT
-array-find,1.0.0,MIT
-array-find-index,1.0.2,MIT
array-flatten,1.1.1,MIT
-array-flatten,2.1.1,MIT
-array-includes,3.0.3,MIT
-array-slice,0.2.3,MIT
-array-union,1.0.2,MIT
array-uniq,1.0.3,MIT
-array-unique,0.2.1,MIT
array-unique,0.3.2,MIT
-arraybuffer.slice,0.0.7,MIT
-arrify,1.0.1,MIT
asana,0.6.0,MIT
asciidoctor,1.5.6.2,MIT
asciidoctor-plantuml,0.0.8,MIT
-asn1,0.2.3,MIT
asn1.js,4.10.1,MIT
assert,1.4.1,MIT
-assert-plus,0.2.0,MIT
-assert-plus,1.0.0,MIT
-asset_sync,2.4.0,MIT
assign-symbols,1.0.0,MIT
-ast-types,0.11.3,MIT
-async,1.5.2,MIT
-async,2.6.0,MIT
-async,2.6.1,MIT
async-each,1.0.1,MIT
async-limiter,1.0.0,MIT
-asynckit,0.4.0,MIT
atob,2.0.3,(MIT OR Apache-2.0)
atomic,1.1.99,Apache 2.0
attr_encrypted,3.1.0,MIT
attr_required,1.0.0,MIT
autosize,4.0.0,MIT
-aws-sign2,0.6.0,Apache 2.0
-aws-sign2,0.7.0,Apache 2.0
-aws4,1.6.0,MIT
axiom-types,0.1.1,MIT
-axios,0.15.3,MIT
axios,0.17.1,MIT
-axios-mock-adapter,1.15.0,MIT
babel-code-frame,6.26.0,MIT
-babel-core,6.26.3,MIT
-babel-eslint,8.2.3,MIT
-babel-generator,6.26.0,MIT
-babel-helper-bindify-decorators,6.24.1,MIT
-babel-helper-builder-binary-assignment-operator-visitor,6.24.1,MIT
-babel-helper-call-delegate,6.24.1,MIT
-babel-helper-define-map,6.26.0,MIT
-babel-helper-explode-assignable-expression,6.24.1,MIT
-babel-helper-explode-class,6.24.1,MIT
-babel-helper-function-name,6.24.1,MIT
-babel-helper-get-function-arity,6.24.1,MIT
-babel-helper-hoist-variables,6.24.1,MIT
-babel-helper-optimise-call-expression,6.24.1,MIT
-babel-helper-regex,6.26.0,MIT
-babel-helper-remap-async-to-generator,6.24.1,MIT
-babel-helper-replace-supers,6.24.1,MIT
-babel-helpers,6.24.1,MIT
-babel-loader,7.1.5,MIT
-babel-messages,6.23.0,MIT
-babel-plugin-check-es2015-constants,6.22.0,MIT
-babel-plugin-istanbul,4.1.6,New BSD
-babel-plugin-rewire,1.1.0,ISC
-babel-plugin-syntax-async-functions,6.13.0,MIT
-babel-plugin-syntax-async-generators,6.13.0,MIT
-babel-plugin-syntax-class-properties,6.13.0,MIT
-babel-plugin-syntax-decorators,6.13.0,MIT
-babel-plugin-syntax-dynamic-import,6.18.0,MIT
-babel-plugin-syntax-exponentiation-operator,6.13.0,MIT
-babel-plugin-syntax-object-rest-spread,6.13.0,MIT
-babel-plugin-syntax-trailing-function-commas,6.22.0,MIT
-babel-plugin-transform-async-generator-functions,6.24.1,MIT
-babel-plugin-transform-async-to-generator,6.24.1,MIT
-babel-plugin-transform-class-properties,6.24.1,MIT
-babel-plugin-transform-decorators,6.24.1,MIT
-babel-plugin-transform-define,1.3.0,MIT
-babel-plugin-transform-es2015-arrow-functions,6.22.0,MIT
-babel-plugin-transform-es2015-block-scoped-functions,6.22.0,MIT
-babel-plugin-transform-es2015-block-scoping,6.26.0,MIT
-babel-plugin-transform-es2015-classes,6.24.1,MIT
-babel-plugin-transform-es2015-computed-properties,6.24.1,MIT
-babel-plugin-transform-es2015-destructuring,6.23.0,MIT
-babel-plugin-transform-es2015-duplicate-keys,6.24.1,MIT
-babel-plugin-transform-es2015-for-of,6.23.0,MIT
-babel-plugin-transform-es2015-function-name,6.24.1,MIT
-babel-plugin-transform-es2015-literals,6.22.0,MIT
-babel-plugin-transform-es2015-modules-amd,6.24.1,MIT
-babel-plugin-transform-es2015-modules-commonjs,6.26.0,MIT
-babel-plugin-transform-es2015-modules-systemjs,6.24.1,MIT
-babel-plugin-transform-es2015-modules-umd,6.24.1,MIT
-babel-plugin-transform-es2015-object-super,6.24.1,MIT
-babel-plugin-transform-es2015-parameters,6.24.1,MIT
-babel-plugin-transform-es2015-shorthand-properties,6.24.1,MIT
-babel-plugin-transform-es2015-spread,6.22.0,MIT
-babel-plugin-transform-es2015-sticky-regex,6.24.1,MIT
-babel-plugin-transform-es2015-template-literals,6.22.0,MIT
-babel-plugin-transform-es2015-typeof-symbol,6.23.0,MIT
-babel-plugin-transform-es2015-unicode-regex,6.24.1,MIT
-babel-plugin-transform-exponentiation-operator,6.24.1,MIT
-babel-plugin-transform-object-rest-spread,6.23.0,MIT
-babel-plugin-transform-regenerator,6.26.0,MIT
-babel-plugin-transform-strict-mode,6.24.1,MIT
+babel-loader,8.0.4,MIT
babel-polyfill,6.23.0,MIT
-babel-preset-es2015,6.24.1,MIT
-babel-preset-es2016,6.24.1,MIT
-babel-preset-es2017,6.24.1,MIT
-babel-preset-latest,6.24.1,MIT
-babel-preset-stage-2,6.24.1,MIT
-babel-preset-stage-3,6.24.1,MIT
-babel-register,6.26.0,MIT
babel-runtime,6.26.0,MIT
-babel-template,6.26.0,MIT
-babel-traverse,6.26.0,MIT
-babel-types,6.26.0,MIT
babosa,1.0.2,MIT
-babylon,6.18.0,MIT
-babylon,7.0.0-beta.44,MIT
-backo2,1.0.2,MIT
balanced-match,1.0.0,MIT
base,0.11.2,MIT
base32,0.3.2,MIT
-base64-arraybuffer,0.1.5,MIT
base64-js,1.2.3,MIT
-base64id,1.0.0,MIT
-batch,0.6.1,MIT
batch-loader,1.2.1,MIT
bcrypt,3.1.12,MIT
-bcrypt-pbkdf,1.0.1,New BSD
bcrypt_pbkdf,1.0.0,MIT
-better-assert,1.0.2,MIT
-bfj-node4,5.2.1,MIT
-big.js,3.1.3,MIT
+bfj,6.1.1,MIT
+big.js,3.2.0,MIT
binary-extensions,1.11.0,MIT
binaryextensions,2.1.1,MIT
bindata,2.4.3,ruby
-bitsyntax,0.0.4,UNKNOWN
-bl,1.1.2,MIT
-blackst0ne-mermaid,7.1.0-fixed,MIT
-blob,0.0.4,MIT*
bluebird,3.5.1,MIT
bn.js,4.11.8,MIT
body-parser,1.18.2,MIT
-bonjour,3.5.0,MIT
-boom,2.10.1,New BSD
-boom,4.3.1,New BSD
-boom,5.2.0,New BSD
bootstrap,4.1.1,MIT
-bootstrap,4.1.2,MIT
bootstrap-vue,2.0.0-rc.11,MIT
bootstrap_form,2.7.0,MIT
-boxen,1.3.0,MIT
brace-expansion,1.1.11,MIT
-braces,0.1.5,MIT
braces,2.3.1,MIT
brorand,1.1.0,MIT
-browser,2.2.0,MIT
+browser,2.5.3,MIT
browserify-aes,1.1.1,MIT
browserify-cipher,1.0.0,MIT
browserify-des,1.0.0,MIT
browserify-rsa,4.0.1,MIT
browserify-sign,4.0.4,ISC
browserify-zlib,0.2.0,MIT
+browserslist,4.1.1,MIT
buffer,4.9.1,MIT
buffer-from,1.0.0,MIT
-buffer-indexof,1.1.0,MIT
-buffer-more-ints,0.0.2,MIT
buffer-xor,1.0.3,MIT
builder,3.2.3,MIT
-buildmail,4.0.1,MIT
-builtin-modules,1.1.1,MIT
builtin-status-codes,3.0.0,MIT
-bytes,2.5.0,MIT
bytes,3.0.0,MIT
cacache,10.0.4,ISC
+cacache,11.2.0,ISC
cache-base,1.0.1,MIT
cache-loader,1.2.2,MIT
cacheable-request,2.1.4,MIT
-caller-path,0.1.0,MIT
-callsite,1.0.0,MIT*
-callsites,0.2.0,MIT
-camelcase,1.2.1,MIT
-camelcase,2.1.1,MIT
camelcase,4.1.0,MIT
-camelcase-keys,2.1.0,MIT
-capture-stack-trace,1.0.0,MIT
+caniuse-lite,1.0.30000888,CC-BY-4.0
carrierwave,1.2.3,MIT
-caseless,0.11.0,Apache 2.0
-caseless,0.12.0,Apache 2.0
cause,0.1,MIT
-center-align,0.1.3,MIT
chalk,1.1.3,MIT
chalk,2.4.1,MIT
chardet,0.4.2,MIT
@@ -277,7 +200,6 @@ charenc,0.0.2,New BSD
charlock_holmes,0.7.6,MIT
chart.js,1.0.2,MIT
check-types,7.3.0,MIT
-chokidar,2.0.2,MIT
chokidar,2.0.4,MIT
chownr,1.0.1,ISC
chrome-trace-event,1.0.0,MIT
@@ -285,86 +207,63 @@ chronic,0.10.2,MIT
chronic_duration,0.10.6,MIT
chunky_png,1.3.5,MIT
cipher-base,1.0.4,MIT
-circular-json,0.3.3,MIT
-circular-json,0.5.5,MIT
citrus,3.0.2,MIT
class-utils,0.3.6,MIT
classlist-polyfill,1.2.0,Unlicense
-cli-boxes,1.0.0,MIT
cli-cursor,2.1.0,MIT
cli-width,2.1.0,ISC
clipboard,1.7.1,MIT
-cliui,2.1.0,ISC
cliui,4.0.0,ISC
clone-response,1.0.2,MIT
-co,4.6.0,MIT
code-point-at,1.1.0,MIT
codesandbox-api,0.0.18,MIT
-codesandbox-import-util-types,1.2.11,UNKNOWN
-codesandbox-import-utils,1.2.11,UNKNOWN
+codesandbox-import-util-types,1.2.11,LGPL
+codesandbox-import-utils,1.2.11,LGPL
coercible,1.0.0,MIT
collection-visit,1.0.0,MIT
-color-convert,1.9.1,MIT
-color-name,1.1.2,MIT
-colors,1.1.2,MIT
-combine-lists,1.0.1,MIT
-combined-stream,1.0.6,MIT
+color-convert,1.9.3,MIT
+color-name,1.1.3,MIT
commander,2.13.0,MIT
-commander,2.15.1,MIT
+commander,2.18.0,MIT
commondir,1.0.1,MIT
-commonmarker,0.17.8,MIT
-component-bind,1.0.0,MIT*
+commonmarker,0.17.13,MIT
component-emitter,1.2.1,MIT
-component-inherit,0.0.3,MIT*
-compressible,2.0.11,MIT
-compression,1.7.0,MIT
-compression-webpack-plugin,1.1.11,MIT
+compression-webpack-plugin,2.0.0,MIT
concat-map,0.0.1,MIT
concat-stream,1.6.2,MIT
concurrent-ruby-ext,1.0.5,MIT
-configstore,3.1.1,Simplified BSD
-connect,3.6.6,MIT
-connect-history-api-fallback,1.3.0,MIT
-connection_pool,2.2.1,MIT
+connection_pool,2.2.2,MIT
console-browserify,1.1.0,MIT
console-control-strings,1.1.0,ISC
consolidate,0.15.1,MIT
constants-browserify,1.0.0,MIT
-contains-path,0.1.0,MIT
content-disposition,0.5.2,MIT
content-type,1.0.4,MIT
-convert-source-map,1.5.1,MIT
+convert-source-map,1.6.0,MIT
cookie,0.3.1,MIT
cookie-signature,1.0.6,MIT
copy-concurrently,1.0.5,ISC
copy-descriptor,0.1.1,MIT
core-js,2.3.0,MIT
-core-js,2.5.3,MIT
+core-js,2.5.7,MIT
core-util-is,1.0.2,MIT
crack,0.4.3,MIT
crass,1.0.4,MIT
create-ecdh,4.0.0,MIT
-create-error-class,3.0.2,MIT
create-hash,1.1.3,MIT
create-hmac,1.1.6,MIT
creole,0.5.0,ruby
cropper,2.3.0,MIT
-cross-spawn,5.1.0,MIT
cross-spawn,6.0.5,MIT
crypt,0.0.2,New BSD
-cryptiles,2.0.5,New BSD
-cryptiles,3.1.2,New BSD
crypto-browserify,3.12.0,MIT
-crypto-random-string,1.0.0,MIT
css-loader,1.0.0,MIT
css-selector-tokenizer,0.7.0,MIT
css_parser,1.5.0,MIT
cssesc,0.1.0,MIT
-currently-unhandled,0.4.1,MIT
-custom-event,1.0.1,MIT
cyclist,0.2.2,MIT*
-d3,3.5.17,New BSD
d3,4.12.2,New BSD
+d3,4.13.0,New BSD
d3-array,1.2.1,New BSD
d3-axis,1.0.8,New BSD
d3-brush,1.0.4,New BSD
@@ -377,6 +276,7 @@ d3-dsv,1.0.8,New BSD
d3-ease,1.0.3,New BSD
d3-force,1.1.0,New BSD
d3-format,1.2.1,New BSD
+d3-format,1.2.2,New BSD
d3-geo,1.9.1,New BSD
d3-hierarchy,1.1.5,New BSD
d3-interpolate,1.1.6,New BSD
@@ -388,6 +288,7 @@ d3-random,1.1.0,New BSD
d3-request,1.0.6,New BSD
d3-scale,1.0.7,New BSD
d3-selection,1.2.0,New BSD
+d3-selection,1.3.0,New BSD
d3-shape,1.2.0,New BSD
d3-time,1.0.8,New BSD
d3-time-format,2.1.1,New BSD
@@ -395,37 +296,26 @@ d3-timer,1.0.7,New BSD
d3-transition,1.1.1,New BSD
d3-voronoi,1.1.2,New BSD
d3-zoom,1.7.1,New BSD
-dagre-d3-renderer,0.4.24,MIT
-dagre-layout,0.8.0,MIT
-dashdash,1.14.1,MIT
-data-uri-to-buffer,1.2.0,MIT
-date-format,1.2.0,MIT
+dagre-d3-renderer,0.5.8,MIT
+dagre-layout,0.8.8,MIT
date-now,0.1.4,MIT
dateformat,3.0.3,MIT
de-indent,1.0.2,MIT
-debug,2.6.8,MIT
debug,2.6.9,MIT
-debug,3.1.0,MIT
+debug,3.2.5,MIT
debugger-ruby_core_source,1.3.8,MIT
-decamelize,1.2.0,MIT
+decamelize,2.0.0,MIT
deckar01-task_list,2.0.0,MIT
declarative,0.0.10,MIT
declarative-option,0.1.0,MIT
decode-uri-component,0.2.0,MIT
decompress-response,3.3.0,MIT
-deep-equal,1.0.1,MIT
deep-extend,0.4.2,MIT
-deep-is,0.1.3,MIT
-default-require-extensions,1.0.0,MIT
default_value_for,3.0.2,MIT
-define-properties,1.1.2,MIT
+define-properties,1.1.3,MIT
define-property,0.2.5,MIT
define-property,1.0.0,MIT
define-property,2.0.2,MIT
-degenerator,1.0.4,MIT
-del,2.2.2,MIT
-del,3.0.0,MIT
-delayed-stream,1.0.0,MIT
delegate,3.1.2,MIT
delegates,1.0.0,MIT
depd,1.1.1,MIT
@@ -433,24 +323,14 @@ depd,1.1.2,MIT
des.js,1.0.0,MIT
descendants_tracker,0.0.4,MIT
destroy,1.0.4,MIT
-detect-indent,4.0.0,MIT
detect-libc,1.0.3,Apache 2.0
-detect-node,2.0.3,ISC
device_detector,1.0.0,LGPL
devise,4.4.3,MIT
devise-two-factor,3.0.0,MIT
-di,0.0.1,MIT
diff,3.5.0,New BSD
-diff-lcs,1.3,"MIT,Artistic-2.0,GPL-2.0+"
diffie-hellman,5.0.2,MIT
diffy,3.1.0,MIT
-dns-equal,1.0.0,MIT
-dns-packet,1.2.2,MIT
-dns-txt,2.0.2,MIT
-doctrine,1.5.0,Simplified BSD
-doctrine,2.1.0,Apache 2.0
document-register-element,1.3.0,MIT
-dom-serialize,2.2.1,MIT
dom-serializer,0.1.0,MIT
domain-browser,1.1.7,MIT
domain_name,0.5.20180417,"Simplified BSD,New BSD,Mozilla Public License 2.0"
@@ -461,17 +341,15 @@ domutils,1.6.2,Simplified BSD
doorkeeper,4.3.2,MIT
doorkeeper-openid_connect,1.5.0,MIT
dot-prop,4.2.0,MIT
-double-ended-queue,2.1.0-0,MIT
dropzone,4.2.0,MIT
-dropzonejs-rails,0.7.2,MIT
duplexer,0.1.1,MIT
duplexer3,0.1.4,New BSD
duplexify,3.5.3,MIT
-ecc-jsbn,0.1.1,MIT
ed25519,1.2.4,MIT
editions,1.3.4,MIT
ee-first,1.1.1,MIT
-ejs,2.5.9,Apache 2.0
+ejs,2.6.1,Apache 2.0
+electron-to-chromium,1.3.73,ISC
elliptic,6.4.0,MIT
email_reply_trimmer,0.1.6,MIT
emoji-unicode-version,0.2.1,MIT
@@ -480,102 +358,58 @@ encodeurl,1.0.2,MIT
encoding,0.1.12,MIT
encryptor,3.0.0,MIT
end-of-stream,1.4.1,MIT
-engine.io,3.1.5,MIT
-engine.io-client,3.1.5,MIT
-engine.io-parser,2.1.2,MIT
-enhanced-resolve,0.9.1,MIT
-enhanced-resolve,4.0.0,MIT
enhanced-resolve,4.1.0,MIT
-ent,2.2.0,MIT
entities,1.1.1,Simplified BSD
equalizer,0.0.11,MIT
-errno,0.1.4,MIT
errno,0.1.7,MIT
-error-ex,1.3.0,MIT
erubis,2.7.0,MIT
-es-abstract,1.10.0,MIT
+es-abstract,1.12.0,MIT
es-to-primitive,1.1.1,MIT
es6-promise,3.0.2,MIT
-es6-promise,4.2.4,MIT
-es6-promisify,5.0.0,MIT
escape-html,1.0.3,MIT
escape-string-regexp,1.0.5,MIT
escape_utils,1.1.1,MIT
-escodegen,1.8.1,Simplified BSD
-escodegen,1.9.0,Simplified BSD
-eslint,4.12.1,MIT
-eslint-config-airbnb-base,12.1.0,MIT
-eslint-import-resolver-node,0.3.2,MIT
-eslint-import-resolver-webpack,0.10.0,MIT
-eslint-module-utils,2.2.0,MIT
-eslint-plugin-filenames,1.2.0,MIT
-eslint-plugin-html,4.0.3,ISC
-eslint-plugin-import,2.12.0,MIT
-eslint-plugin-jasmine,2.2.0,MIT
-eslint-plugin-promise,3.8.0,ISC
-eslint-plugin-vue,4.5.0,MIT
-eslint-restricted-globals,0.1.1,MIT
-eslint-scope,3.7.1,Simplified BSD
-eslint-visitor-keys,1.0.0,Apache 2.0
-espree,3.5.4,Simplified BSD
-esprima,2.7.3,Simplified BSD
-esprima,3.1.3,Simplified BSD
-esprima,4.0.0,Simplified BSD
-esquery,1.0.1,New BSD
+escaper,2.5.3,MIT
+eslint-scope,4.0.0,Simplified BSD
esrecurse,4.2.1,Simplified BSD
-estraverse,1.9.3,Simplified BSD
estraverse,4.2.0,Simplified BSD
esutils,2.0.2,Simplified BSD
et-orbi,1.0.3,MIT
etag,1.8.1,MIT
eve-raphael,0.5.0,Apache 2.0
-event-stream,3.3.4,MIT
-eventemitter3,1.2.0,MIT
events,1.1.1,MIT
-eventsource,0.1.6,MIT
evp_bytestokey,1.0.3,MIT
excon,0.62.0,MIT
-execa,0.7.0,MIT
+execa,0.10.0,MIT
execjs,2.6.0,MIT
-expand-braces,0.1.2,MIT
expand-brackets,2.1.4,MIT
-expand-range,0.1.1,MIT
exports-loader,0.7.0,MIT
-express,4.16.2,MIT
+express,4.16.3,MIT
expression_parser,0.9.0,MIT
-extend,3.0.1,MIT
extend-shallow,2.0.1,MIT
extend-shallow,3.0.2,MIT
external-editor,2.2.0,MIT
external-editor,3.0.0,MIT
extglob,2.0.4,MIT
-extsprintf,1.3.0,MIT
-extsprintf,1.4.0,MIT
faraday,0.12.2,MIT
faraday_middleware,0.12.2,MIT
faraday_middleware-multi_json,0.0.6,MIT
-fast-deep-equal,1.0.0,MIT
+fast-deep-equal,2.0.1,MIT
fast-json-stable-stringify,2.0.0,MIT
-fast-levenshtein,2.0.6,MIT
fast_blank,1.0.0,MIT
fast_gettext,1.6.0,"MIT,ruby"
fastparse,1.1.1,MIT
-faye-websocket,0.10.0,MIT
-faye-websocket,0.11.1,MIT
-ffi,1.9.18,New BSD
+ffi,1.9.25,New BSD
+figgy-pudding,3.5.1,ISC
figures,2.0.0,MIT
-file-entry-cache,2.0.0,MIT
-file-loader,1.1.11,MIT
-file-uri-to-path,1.0.0,MIT
-fileset,2.0.3,MIT
-filesize,3.6.0,New BSD
+file-loader,2.0.0,MIT
+filesize,3.6.1,New BSD
fill-range,4.0.0,MIT
-finalhandler,1.1.0,MIT
+finalhandler,1.1.1,MIT
find-cache-dir,1.0.0,MIT
-find-root,1.1.0,MIT
-find-up,1.1.2,MIT
+find-cache-dir,2.0.0,MIT
find-up,2.1.0,MIT
-flat-cache,1.2.2,MIT
+find-up,3.0.0,MIT
flipper,0.13.0,MIT
flipper-active_record,0.13.0,MIT
flipper-active_support_cache_store,0.13.0,MIT
@@ -584,103 +418,70 @@ flush-write-stream,1.0.2,MIT
fog-aliyun,0.2.0,MIT
fog-aws,2.0.1,MIT
fog-core,1.45.0,MIT
-fog-google,1.3.3,MIT
+fog-google,1.7.1,MIT
fog-json,1.0.2,MIT
fog-local,0.3.1,MIT
fog-openstack,0.1.21,MIT
fog-rackspace,0.1.1,MIT
fog-xml,0.1.3,MIT
-follow-redirects,1.0.0,MIT
follow-redirects,1.2.6,MIT
font-awesome-rails,4.7.0.1,"MIT,SIL Open Font License"
for-in,1.0.2,MIT
-foreach,2.0.5,MIT
-forever-agent,0.6.1,Apache 2.0
-form-data,2.0.0,MIT
-form-data,2.3.2,MIT
formatador,0.2.5,MIT
formdata-polyfill,3.0.11,MIT
forwarded,0.1.2,MIT
fragment-cache,0.2.1,MIT
fresh,0.5.2,MIT
-from,0.1.7,MIT
from2,2.3.0,MIT
-fs-access,1.0.1,MIT
fs-minipass,1.2.5,ISC
fs-write-stream-atomic,1.0.10,ISC
fs.realpath,1.0.0,ISC
fsevents,1.2.4,MIT
-ftp,0.3.10,MIT
function-bind,1.1.1,MIT
-functional-red-black-tree,1.0.1,MIT
fuzzaldrin-plus,0.5.0,MIT
gauge,2.7.4,ISC
-gemnasium-gitlab-service,0.2.6,MIT
gemojione,3.3.0,MIT
-generate-function,2.0.0,MIT
-generate-object-property,1.2.0,MIT
get-caller-file,1.0.2,ISC
-get-stdin,4.0.1,MIT
get-stream,3.0.0,MIT
-get-uri,2.0.2,MIT
get-value,2.0.6,MIT
get_process_mem,0.2.0,MIT
-getpass,0.1.7,MIT
gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.3.0,MIT
-gitaly-proto,0.112.0,MIT
-github-linguist,5.3.3,MIT
+gitaly-proto,0.123.0,MIT
github-markup,1.7.0,MIT
-gitlab-flowdock-git-hook,1.0.1,MIT
-gitlab-gollum-lib,4.2.7.5,MIT
-gitlab-gollum-rugged_adapter,0.4.4.1,MIT
-gitlab-grit,2.8.2,MIT
gitlab-markup,1.6.4,MIT
+gitlab-sidekiq-fetcher,0.3.0,LGPL
gitlab_omniauth-ldap,2.0.4,MIT
-glob,5.0.15,ISC
-glob,7.1.2,ISC
+glob,7.1.3,ISC
glob-parent,3.1.0,ISC
-global-dirs,0.1.1,MIT
global-modules-path,2.1.0,Apache 2.0
globalid,0.4.1,MIT
-globals,11.5.0,MIT
-globals,9.18.0,MIT
-globby,5.0.0,MIT
-globby,6.1.0,MIT
-gollum-grit_adapter,1.0.1,MIT
+globals,11.7.0,MIT
gon,6.2.0,MIT
good-listener,1.2.2,MIT
-google-api-client,0.19.8,Apache 2.0
-google-protobuf,3.5.1,New BSD
-googleapis-common-protos-types,1.0.1,Apache 2.0
-googleauth,0.6.2,Apache 2.0
-got,6.7.1,MIT
+google-api-client,0.23.4,Apache 2.0
+google-protobuf,3.6.1,New BSD
+googleapis-common-protos-types,1.0.2,Apache 2.0
+googleauth,0.6.6,Apache 2.0
got,8.3.0,MIT
gpgme,2.0.13,LGPL-2.1+
graceful-fs,4.1.11,ISC
-grape,1.0.3,MIT
+grape,1.1.0,MIT
grape-entity,0.7.1,MIT
grape-path-helpers,1.0.6,MIT
grape_logging,1.7.0,MIT
graphiql-rails,1.4.10,MIT
-graphlib,2.1.1,MIT
+graphlibrary,2.2.0,MIT
graphql,1.8.1,MIT
-grpc,1.11.0,Apache 2.0
-gzip-size,4.1.0,MIT
+grpc,1.15.0,Apache 2.0
+gzip-size,5.0.0,MIT
hamlit,2.8.8,MIT
-handle-thing,1.2.5,MIT
-handlebars,4.0.6,MIT
hangouts-chat,0.0.5,MIT
-har-schema,2.0.0,ISC
-har-validator,2.0.6,ISC
-har-validator,5.0.3,ISC
has,1.0.1,MIT
has-ansi,2.0.0,MIT
-has-binary2,1.0.2,MIT
-has-cors,1.1.0,MIT
-has-flag,1.0.0,MIT
has-flag,3.0.0,MIT
has-symbol-support-x,1.3.0,MIT
+has-symbols,1.0.0,MIT
has-to-string-tag-x,1.3.0,MIT
has-unicode,2.0.1,ISC
has-value,0.3.1,MIT
@@ -693,19 +494,11 @@ hash-sum,1.0.2,MIT
hash.js,1.1.3,MIT
hashie,3.5.7,MIT
hashie-forbidden_attributes,0.1.1,MIT
-hawk,3.1.3,New BSD
-hawk,6.0.2,New BSD
he,1.1.1,MIT
health_check,2.6.0,MIT
hipchat,1.5.2,MIT
-hipchat-notifier,1.1.0,MIT
hmac-drbg,1.0.1,MIT
-hoek,2.16.3,New BSD
-hoek,4.2.1,New BSD
-home-or-tmp,2.0.0,MIT
-hosted-git-info,2.2.0,ISC
-hpack.js,2.1.6,MIT
-html-entities,1.2.0,MIT
+hoopy,0.1.4,MIT
html-pipeline,2.8.4,MIT
html2text,0.2.0,MIT
htmlentities,4.3.4,MIT
@@ -713,68 +506,46 @@ htmlparser2,3.9.2,MIT
http,2.2.2,MIT
http-cache-semantics,3.8.1,Simplified BSD
http-cookie,1.0.3,MIT
-http-deceiver,1.2.7,MIT
http-errors,1.6.2,MIT
-http-errors,1.6.3,MIT
http-form_data,1.0.3,MIT
-http-proxy,1.16.2,MIT
-http-proxy-agent,2.1.0,MIT
-http-proxy-middleware,0.18.0,MIT
-http-signature,1.1.1,MIT
-http-signature,1.2.0,MIT
http_parser.rb,0.6.0,MIT
httparty,0.13.7,MIT
httpclient,2.8.3,ruby
-httpntlm,1.6.1,MIT
-httpreq,0.4.24,MIT
https-browserify,1.0.0,MIT
-https-proxy-agent,2.2.1,MIT
i18n,0.9.5,MIT
icalendar,2.4.1,ruby
ice_nine,0.11.2,MIT
-iconv-lite,0.4.15,MIT
iconv-lite,0.4.19,MIT
iconv-lite,0.4.23,MIT
icss-replace-symbols,1.1.0,ISC
icss-utils,2.1.0,ISC
ieee754,1.1.11,New BSD
iferr,0.1.5,MIT
-ignore,3.3.8,MIT
-ignore-by-default,1.0.1,ISC
ignore-walk,3.0.1,ISC
immediate,3.0.6,MIT
-import-lazy,2.1.0,MIT
import-local,1.0.0,MIT
imports-loader,0.8.0,MIT
imurmurhash,0.1.4,MIT
-indent-string,2.1.0,MIT
indexes-of,1.0.1,MIT
indexof,0.0.1,MIT*
-inflection,1.12.0,MIT
-inflection,1.3.8,MIT
inflight,1.0.6,ISC
influxdb,0.2.3,MIT
inherits,2.0.1,ISC
inherits,2.0.3,ISC
ini,1.3.5,ISC
inquirer,3.0.6,MIT
-inquirer,3.3.0,MIT
-inquirer,6.0.0,MIT
-internal-ip,1.2.0,MIT
+inquirer,6.2.0,MIT
interpret,1.1.0,MIT
into-stream,3.1.0,MIT
-invariant,2.2.2,New BSD
-invert-kv,1.0.0,MIT
-ip,1.1.5,MIT
-ipaddr.js,1.6.0,MIT
+invariant,2.2.4,MIT
+invert-kv,2.0.0,MIT
+ipaddr.js,1.8.0,MIT
ipaddress,0.8.3,MIT
is-accessor-descriptor,0.1.6,MIT
is-accessor-descriptor,1.0.0,MIT
-is-arrayish,0.2.1,MIT
is-binary-path,1.0.1,MIT
is-buffer,1.1.6,MIT
-is-builtin-module,1.0.0,MIT
-is-callable,1.1.3,MIT
+is-callable,1.1.4,MIT
is-data-descriptor,0.1.4,MIT
is-data-descriptor,1.0.0,MIT
is-date-object,1.0.1,MIT
@@ -783,60 +554,30 @@ is-descriptor,1.0.2,MIT
is-extendable,0.1.1,MIT
is-extendable,1.0.1,MIT
is-extglob,2.1.1,MIT
-is-finite,1.0.2,MIT
is-fullwidth-code-point,1.0.0,MIT
is-fullwidth-code-point,2.0.0,MIT
is-glob,3.1.0,MIT
is-glob,4.0.0,MIT
-is-installed-globally,0.1.0,MIT
-is-my-ip-valid,1.0.0,MIT
-is-my-json-valid,2.17.2,MIT
-is-npm,1.0.0,MIT
-is-number,0.1.1,MIT
is-number,3.0.0,MIT
is-number,4.0.0,MIT
is-obj,1.0.1,MIT
is-object,1.0.1,MIT
is-odd,2.0.0,MIT
-is-path-cwd,1.0.0,MIT
-is-path-in-cwd,1.0.0,MIT
-is-path-inside,1.0.0,MIT
is-plain-obj,1.1.0,MIT
is-plain-object,2.0.4,MIT
is-promise,2.1.0,MIT
-is-property,1.0.2,MIT
-is-redirect,1.0.0,MIT
is-regex,1.0.4,MIT
-is-resolvable,1.0.0,MIT
+is-regexp,1.0.0,MIT
is-retry-allowed,1.1.0,MIT
is-stream,1.1.0,MIT
-is-symbol,1.0.1,MIT
-is-typedarray,1.0.0,MIT
-is-utf8,0.2.1,MIT
+is-symbol,1.0.2,MIT
is-windows,1.0.2,MIT
-is-wsl,1.1.0,MIT
-isarray,0.0.1,MIT
isarray,1.0.0,MIT
-isarray,2.0.1,MIT
-isbinaryfile,3.0.2,MIT
isexe,2.0.0,ISC
isobject,2.1.0,MIT
isobject,3.0.1,MIT
-isstream,0.1.2,MIT
-istanbul,0.4.5,New BSD
-istanbul-api,1.2.1,New BSD
-istanbul-lib-coverage,1.1.1,New BSD
-istanbul-lib-coverage,1.2.0,New BSD
-istanbul-lib-hook,1.1.0,New BSD
-istanbul-lib-instrument,1.10.1,New BSD
-istanbul-lib-report,1.1.2,New BSD
-istanbul-lib-source-maps,1.2.2,New BSD
-istanbul-reports,1.1.3,New BSD
istextorbinary,2.2.1,MIT
isurl,1.0.0,MIT
-jasmine-core,2.9.0,MIT
-jasmine-diff,0.1.3,MIT
-jasmine-jquery,2.1.1,MIT
jed,1.1.1,MIT
jira-ruby,1.4.1,MIT
jquery,3.3.1,MIT
@@ -844,24 +585,18 @@ jquery-atwho-rails,1.3.2,MIT
jquery-ujs,1.2.2,MIT
jquery.waitforimages,2.2.0,MIT
js-cookie,2.1.3,MIT
+js-levenshtein,1.1.4,MIT
js-tokens,3.0.2,MIT
-js-yaml,3.11.0,MIT
-jsbn,0.1.1,MIT
+js-tokens,4.0.0,MIT
+js_regex,2.2.1,MIT
jsesc,0.5.0,MIT
-jsesc,1.3.0,MIT
jsesc,2.5.1,MIT
json,1.8.6,ruby
json-buffer,3.0.0,MIT
json-jwt,1.9.4,MIT
json-parse-better-errors,1.0.2,MIT
-json-schema,0.2.3,BSD
-json-schema-traverse,0.3.1,MIT
-json-stable-stringify-without-jsonify,1.0.1,MIT
-json-stringify-safe,5.0.1,ISC
-json3,3.3.2,MIT
+json-schema-traverse,0.4.1,MIT
json5,0.5.1,MIT
-jsonpointer,4.0.1,MIT
-jsprim,1.4.1,MIT
jszip,3.1.3,(MIT OR GPL-3.0)
jszip-utils,0.0.2,MIT or GPLv3
jwt,1.5.6,MIT
@@ -869,96 +604,60 @@ kaminari,1.0.1,MIT
kaminari-actionview,1.0.1,MIT
kaminari-activerecord,1.0.1,MIT
kaminari-core,1.0.1,MIT
-karma,2.0.4,MIT
-karma-chrome-launcher,2.2.0,MIT
-karma-coverage-istanbul-reporter,1.4.2,MIT
-karma-jasmine,1.1.2,MIT
-karma-mocha-reporter,2.2.5,MIT
-karma-sourcemap-loader,0.3.7,MIT
-karma-webpack,4.0.0-beta.0,MIT
-katex,0.8.3,MIT
+katex,0.9.0,MIT
keyv,3.0.0,MIT
kgio,2.10.0,LGPL-2.1+
-killable,1.0.0,ISC
kind-of,3.2.2,MIT
kind-of,4.0.0,MIT
kind-of,5.1.0,MIT
kind-of,6.0.2,MIT
kubeclient,3.1.0,MIT
-latest-version,3.1.0,MIT
-lazy-cache,1.0.4,MIT
lazy-cache,2.0.2,MIT
-lcid,1.0.0,MIT
-levn,0.3.0,MIT
-libbase64,0.1.0,MIT
-libmime,3.0.0,MIT
-libqp,1.1.0,MIT
+lcid,2.0.0,MIT
licensee,8.9.2,MIT
lie,3.1.1,MIT
-little-plugger,1.1.4,MIT
-load-json-file,1.1.0,MIT
-load-json-file,2.0.0,MIT
loader-runner,2.3.0,MIT
loader-utils,1.1.0,MIT
locale,2.1.2,"ruby,LGPLv3+"
locate-path,2.0.0,MIT
-lodash,4.17.10,MIT
-lodash,4.17.4,MIT
+locate-path,3.0.0,MIT
+lodash,4.17.11,MIT
lodash.camelcase,4.3.0,MIT
lodash.clonedeep,4.5.0,MIT
lodash.debounce,4.0.8,MIT
lodash.escaperegexp,4.1.2,MIT
lodash.get,4.4.2,MIT
lodash.isequal,4.5.0,MIT
-lodash.kebabcase,4.1.1,MIT
lodash.mergewith,4.6.0,MIT
-lodash.snakecase,4.1.1,MIT
lodash.startcase,4.4.0,MIT
-lodash.upperfirst,4.3.1,MIT
-log-symbols,2.2.0,MIT
-log4js,2.11.0,Apache 2.0
-logging,2.2.2,MIT
-loggly,1.1.1,MIT
-loglevel,1.4.1,MIT
-loglevelnext,1.0.3,MIT
lograge,0.10.0,MIT
-long,3.2.0,Apache 2.0
-long,4.0.0,Apache 2.0
-longest,1.0.1,MIT
loofah,2.2.2,MIT
-loose-envify,1.3.1,MIT
-loud-rejection,1.6.0,MIT
+loose-envify,1.4.0,MIT
lowercase-keys,1.0.0,MIT
-lru-cache,2.2.4,MIT
lru-cache,4.1.3,ISC
lz-string,1.4.4,WTFPL
mail,2.7.0,MIT
mail_room,0.9.1,MIT
-mailcomposer,4.0.1,MIT
-mailgun-js,0.18.1,MIT
-make-dir,1.2.0,MIT
+make-dir,1.3.0,MIT
mamacro,0.0.3,MIT
+map-age-cleaner,0.1.2,MIT
map-cache,0.2.2,MIT
-map-obj,1.0.1,MIT
-map-stream,0.1.0,UNKNOWN
map-visit,1.0.0,MIT
marked,0.3.12,MIT
match-at,0.1.1,MIT
md5.js,1.3.4,MIT
media-typer,0.3.0,MIT
-mem,1.1.0,MIT
+mem,4.0.0,MIT
memoist,0.16.0,MIT
-memory-fs,0.2.0,MIT
memory-fs,0.4.1,MIT
-meow,3.7.0,MIT
merge-descriptors,1.0.1,MIT
merge-source-map,1.1.0,MIT
-method_source,0.8.2,MIT
+mermaid,8.0.0-rc.8,MIT
+method_source,0.9.0,MIT
methods,1.1.2,MIT
micromatch,3.1.10,MIT
miller-rabin,4.0.1,MIT
mime,1.4.1,MIT
-mime,1.6.0,MIT
mime,2.3.1,MIT
mime-db,1.33.0,MIT
mime-types,2.1.18,MIT
@@ -968,85 +667,66 @@ mimemagic,0.3.0,MIT
mimic-fn,1.1.0,MIT
mimic-response,1.0.0,MIT
mini_magick,4.8.0,MIT
-mini_mime,1.0.0,MIT
+mini_mime,1.0.1,MIT
mini_portile2,2.3.0,MIT
minimalistic-assert,1.0.0,ISC
minimalistic-crypto-utils,1.0.1,MIT
minimatch,3.0.4,ISC
-minimist,0.0.10,MIT
minimist,0.0.8,MIT
minimist,1.2.0,MIT
minipass,2.3.3,ISC
minizlib,1.1.0,MIT
mississippi,2.0.0,Simplified BSD
+mississippi,3.0.0,Simplified BSD
mixin-deep,1.3.1,MIT
mkdirp,0.5.1,MIT
-moment,2.19.2,MIT
-monaco-editor,0.13.1,MIT
-monaco-editor-webpack-plugin,1.4.0,MIT
+moment,2.22.2,MIT
+monaco-editor,0.14.3,MIT
+monaco-editor-webpack-plugin,1.5.4,MIT
mousetrap,1.4.6,Apache 2.0
-mousetrap-rails,1.4.6,"MIT,Apache"
move-concurrently,1.0.1,ISC
ms,2.0.0,MIT
+ms,2.1.1,MIT
msgpack,1.2.4,Apache 2.0
multi_json,1.13.1,MIT
multi_xml,0.6.0,MIT
-multicast-dns,6.1.1,MIT
-multicast-dns-service-types,1.1.0,MIT
multipart-post,2.0.0,MIT
-mustermann,1.0.2,MIT
+mustermann,1.0.3,MIT
mustermann-grape,1.0.0,MIT
mute-stream,0.0.7,ISC
mysql2,0.4.10,MIT
nan,2.10.0,MIT
nanomatch,1.2.9,MIT
-natural-compare,1.4.0,MIT
needle,2.2.1,MIT
negotiator,0.6.1,MIT
neo-async,2.5.0,MIT
net-ldap,0.16.0,MIT
net-ssh,5.0.1,MIT
-netmask,1.0.6,MIT
netrc,0.11.0,MIT
nice-try,1.0.4,MIT
node-fetch,1.6.3,MIT
-node-forge,0.6.33,New BSD
node-libs-browser,2.1.0,MIT
node-pre-gyp,0.10.0,New BSD
-node-uuid,1.4.8,MIT
-nodemailer,2.7.2,MIT
-nodemailer-direct-transport,3.3.2,MIT
-nodemailer-fetch,1.6.0,MIT
-nodemailer-shared,1.1.0,MIT
-nodemailer-smtp-pool,2.8.2,MIT
-nodemailer-smtp-transport,2.7.2,MIT
-nodemailer-wellknown,0.1.10,MIT
-nodemon,1.18.2,MIT
+node-releases,1.0.0-alpha.12,CC-BY-4.0
nokogiri,1.8.4,MIT
nokogumbo,1.5.0,Apache 2.0
-nopt,1.0.10,MIT
-nopt,3.0.6,ISC
nopt,4.0.1,ISC
-normalize-package-data,2.4.0,Simplified BSD
normalize-path,2.1.1,MIT
normalize-url,2.0.1,MIT
npm-bundled,1.0.3,ISC
npm-packlist,1.1.10,ISC
npm-run-path,2.0.2,MIT
npmlog,4.1.2,ISC
-null-check,1.0.0,MIT
number-is-nan,1.0.1,MIT
numerizer,0.1.1,MIT
oauth,0.5.4,MIT
-oauth-sign,0.8.2,Apache 2.0
oauth2,1.4.0,MIT
object-assign,4.1.1,MIT
-object-component,0.0.3,MIT*
object-copy,0.1.0,MIT
-object-keys,1.0.11,MIT
+object-keys,1.0.12,MIT
object-visit,1.0.1,MIT
+object.getownpropertydescriptors,2.0.3,MIT
object.pick,1.3.0,MIT
-obuf,1.1.1,MIT
octokit,4.9.0,MIT
omniauth,1.8.1,MIT
omniauth-auth0,2.0.0,MIT
@@ -1067,56 +747,42 @@ omniauth-shibboleth,1.3.0,MIT
omniauth-twitter,1.4.0,MIT
omniauth_crowd,2.2.3,MIT
on-finished,2.3.0,MIT
-on-headers,1.0.1,MIT
once,1.4.0,ISC
onetime,2.0.1,MIT
opencollective,1.0.3,MIT
-opener,1.4.3,(WTFPL OR MIT)
+opener,1.5.1,(WTFPL OR MIT)
opn,4.0.2,MIT
-opn,5.2.0,MIT
-optimist,0.6.1,MIT
-optionator,0.8.2,MIT
org-ruby,0.9.12,MIT
-original,1.0.0,MIT
orm_adapter,0.5.0,MIT
-os,0.9.6,MIT
+os,1.0.0,MIT
os-browserify,0.3.0,MIT
os-homedir,1.0.2,MIT
-os-locale,2.1.0,MIT
+os-locale,3.0.1,MIT
os-tmpdir,1.0.2,MIT
osenv,0.1.5,ISC
p-cancelable,0.4.1,MIT
+p-defer,1.0.0,MIT
p-finally,1.0.0,MIT
p-is-promise,1.1.0,MIT
p-limit,1.2.0,MIT
+p-limit,2.0.0,MIT
p-locate,2.0.0,MIT
-p-map,1.1.1,MIT
+p-locate,3.0.0,MIT
p-timeout,2.0.1,MIT
p-try,1.0.0,MIT
-pac-proxy-agent,2.0.2,MIT
-pac-resolver,3.0.0,MIT
-package-json,4.0.1,MIT
+p-try,2.0.0,MIT
pako,1.0.6,(MIT AND Zlib)
parallel-transform,1.1.0,MIT
parse-asn1,5.1.0,ISC
-parse-json,2.2.0,MIT
-parseqs,0.0.5,MIT
-parseuri,0.0.5,MIT
parseurl,1.3.2,MIT
pascalcase,0.1.1,MIT
path-browserify,0.0.0,MIT
path-dirname,1.0.2,MIT
-path-exists,2.1.0,MIT
path-exists,3.0.0,MIT
path-is-absolute,1.0.1,MIT
-path-is-inside,1.0.2,(WTFPL OR MIT)
path-key,2.0.1,MIT
-path-parse,1.0.5,MIT
-path-proxy,1.0.0,MIT
+path-parse,1.0.6,MIT
path-to-regexp,0.1.7,MIT
-path-type,1.1.0,MIT
-path-type,2.0.0,MIT
-pause-stream,0.0.11,Apache 2.0
pbkdf2,3.0.14,MIT
peek,1.0.1,MIT
peek-gc,0.0.2,MIT
@@ -1124,23 +790,16 @@ peek-mysql2,1.1.0,MIT
peek-pg,1.3.0,MIT
peek-rblineprof,0.2.0,MIT
peek-redis,1.2.0,MIT
-peek-sidekiq,1.0.3,MIT
-performance-now,2.1.0,MIT
pg,0.18.4,"BSD,ruby,GPL"
-pify,2.3.0,MIT
pify,3.0.0,MIT
pikaday,1.6.1,MIT
pinkie,2.0.4,MIT
pinkie-promise,2.0.1,MIT
-pkg-dir,1.0.0,MIT
pkg-dir,2.0.0,MIT
-pluralize,7.0.0,MIT
+pkg-dir,3.0.0,MIT
po_to_json,1.0.1,MIT
popper.js,1.14.3,MIT
-portfinder,1.0.13,MIT
posix-character-classes,0.1.1,MIT
-posix-spawn,0.3.13,MIT
-postcss,6.0.22,MIT
postcss,6.0.23,MIT
postcss-modules-extract-imports,1.2.0,ISC
postcss-modules-local-by-default,1.2.0,MIT
@@ -1148,50 +807,40 @@ postcss-modules-scope,1.1.0,ISC
postcss-modules-values,1.3.0,ISC
postcss-selector-parser,3.1.1,MIT
postcss-value-parser,3.3.0,MIT
-prelude-ls,1.1.2,MIT
premailer,1.10.4,New BSD
premailer-rails,1.9.7,MIT
-prepend-http,1.0.4,MIT
prepend-http,2.0.0,MIT
-prettier,1.12.1,MIT
+prettier,1.13.7,MIT
prismjs,1.6.0,MIT
private,0.1.8,MIT
process,0.11.10,MIT
process-nextick-args,1.0.7,MIT
process-nextick-args,2.0.0,MIT
-progress,2.0.0,MIT
prometheus-client-mmap,0.9.4,Apache 2.0
promise-inflight,1.0.1,ISC
-promisify-call,2.0.4,MIT
-proxy-addr,2.0.3,MIT
-proxy-agent,3.0.1,MIT
-proxy-from-env,1.0.0,MIT
-prr,0.0.0,MIT
+proxy-addr,2.0.4,MIT
prr,1.0.1,MIT
-ps-tree,1.1.0,MIT
pseudomap,1.0.2,ISC
-pstree.remy,1.1.0,MIT
public-encrypt,4.0.0,MIT
-public_suffix,3.0.2,MIT
+public_suffix,3.0.3,MIT
+puma,3.12.0,New BSD
+puma_worker_killer,0.1.0,MIT
pump,2.0.1,MIT
+pump,3.0.0,MIT
pumpify,1.4.0,MIT
punycode,1.3.2,MIT
-punycode,1.4.1,MIT
+punycode,2.1.1,MIT
pyu-ruby-sasl,0.0.3.3,MIT
-qjobs,1.2.0,MIT
-qs,6.2.3,New BSD
qs,6.5.1,New BSD
query-string,5.1.1,MIT
querystring,0.2.0,MIT
querystring-es3,0.2.1,MIT
-querystringify,0.0.4,MIT
-querystringify,1.0.0,MIT
rack,1.6.10,MIT
rack-accept,0.4.5,MIT
rack-attack,4.4.1,MIT
rack-cors,1.0.2,MIT
rack-oauth2,1.2.3,MIT
-rack-protection,2.0.1,MIT
+rack-protection,2.0.3,MIT
rack-proxy,0.6.0,MIT
rack-test,0.6.3,MIT
rails,4.2.10,MIT
@@ -1200,7 +849,7 @@ rails-dom-testing,1.0.9,MIT
rails-html-sanitizer,1.0.4,MIT
rails-i18n,4.0.9,MIT
railties,4.2.10,MIT
-rainbow,2.2.2,MIT
+rainbow,3.0.0,MIT
raindrops,0.18.0,LGPL-2.1+
rake,12.3.1,MIT
randombytes,2.0.6,MIT
@@ -1209,7 +858,6 @@ range-parser,1.2.0,MIT
raphael,2.2.7,MIT
raven-js,3.22.1,Simplified BSD
raw-body,2.3.2,MIT
-raw-body,2.3.3,MIT
raw-loader,0.5.1,MIT
rb-fsevent,0.10.2,MIT
rb-inotify,0.9.10,MIT
@@ -1217,58 +865,41 @@ rbtrace,0.4.10,MIT
rc,1.2.5,(BSD-2-Clause OR MIT OR Apache-2.0)
rdoc,6.0.4,ruby
re2,1.1.1,New BSD
-read-pkg,1.1.0,MIT
-read-pkg,2.0.0,MIT
-read-pkg-up,1.0.1,MIT
-read-pkg-up,2.0.0,MIT
-readable-stream,1.1.14,MIT
readable-stream,2.0.6,MIT
-readable-stream,2.3.4,MIT
readable-stream,2.3.6,MIT
readdirp,2.1.0,MIT
recaptcha,3.0.0,MIT
recursive-open-struct,1.1.0,MIT
redcarpet,3.4.0,MIT
-redent,1.0.0,MIT
-redis,2.8.0,MIT
redis,3.3.5,MIT
redis-actionpack,5.0.2,MIT
redis-activesupport,5.0.4,MIT
-redis-commands,1.3.1,MIT
redis-namespace,1.6.0,MIT
-redis-parser,2.6.0,MIT
redis-rack,2.0.4,MIT
redis-rails,5.0.2,MIT
redis-store,1.4.1,MIT
-regenerate,1.3.2,MIT
+regenerate,1.4.0,MIT
+regenerate-unicode-properties,7.0.0,MIT
regenerator-runtime,0.10.5,MIT
-regenerator-runtime,0.11.0,MIT
-regenerator-transform,0.10.1,BSD
+regenerator-runtime,0.11.1,MIT
+regenerator-transform,0.13.3,MIT
regex-not,1.0.2,MIT
+regexp_parser,0.5.0,MIT
regexpu-core,1.0.0,MIT
-regexpu-core,2.0.0,MIT
-registry-auth-token,3.3.2,MIT
-registry-url,3.1.0,MIT
+regexpu-core,4.2.0,MIT
regjsgen,0.2.0,MIT
+regjsgen,0.4.0,MIT
regjsparser,0.1.5,Simplified BSD
+regjsparser,0.3.0,Simplified BSD
remove-trailing-separator,1.1.0,ISC
repeat-element,1.1.2,MIT
-repeat-string,0.2.2,MIT
repeat-string,1.6.1,MIT
-repeating,2.0.1,MIT
representable,3.0.4,MIT
-request,2.75.0,Apache 2.0
-request,2.83.0,Apache 2.0
request_store,1.3.1,MIT
-requestretry,1.13.0,MIT
require-directory,2.1.1,MIT
require-main-filename,1.0.1,ISC
-require-uncached,1.0.3,MIT
-requires-port,1.0.0,MIT
-resolve,1.1.7,MIT
-resolve,1.7.1,MIT
+resolve,1.8.1,MIT
resolve-cwd,2.0.0,MIT
-resolve-from,1.0.1,MIT
resolve-from,3.0.0,MIT
resolve-url,0.2.1,MIT
responders,2.4.0,MIT
@@ -1276,13 +907,12 @@ responselike,1.0.2,MIT
rest-client,2.0.2,MIT
restore-cursor,2.0.0,MIT
ret,0.1.15,MIT
-retriable,3.1.1,MIT
-right-align,0.1.3,MIT
+retriable,3.1.2,MIT
rimraf,2.6.2,ISC
rinku,2.0.0,ISC
ripemd160,2.0.1,MIT
rotp,2.1.2,MIT
-rouge,3.2.0,MIT
+rouge,3.3.0,MIT
rqrcode,0.7.0,MIT
rqrcode-rails3,0.1.7,MIT
ruby-enum,0.7.2,MIT
@@ -1294,13 +924,11 @@ ruby_parser,3.9.0,MIT
rubyntlm,0.6.2,MIT
rubypants,0.2.0,BSD
rufus-scheduler,3.4.0,MIT
-rugged,0.27.2,MIT
+rugged,0.27.5,MIT
run-async,2.3.0,MIT
run-queue,1.0.3,ISC
rw,1.3.3,New BSD
rx,4.1.0,Apache 2.0
-rx-lite,4.0.8,Apache 2.0
-rx-lite-aggregates,4.0.8,Apache 2.0
rxjs,6.2.1,Apache 2.0
safe-buffer,5.1.1,MIT
safe-buffer,5.1.2,MIT
@@ -1315,19 +943,17 @@ sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT
sax,1.2.4,ISC
schema-utils,0.4.5,MIT
+schema-utils,1.0.0,MIT
+scope-css,1.2.1,MIT
seed-fu,2.3.7,MIT
select,1.1.2,MIT
-select-hose,2.0.0,MIT
select2,3.5.2-browserify,Apache*
select2-rails,3.5.9.3,MIT
-selfsigned,1.10.1,MIT
-semver,5.5.0,ISC
-semver-diff,2.1.0,MIT
-send,0.16.1,MIT
+semver,5.5.1,ISC
+send,0.16.2,MIT
sentry-raven,2.7.2,Apache 2.0
serialize-javascript,1.4.0,New BSD
-serve-index,1.9.0,MIT
-serve-static,1.13.1,MIT
+serve-static,1.13.2,MIT
set-blocking,2.0.0,ISC
set-getter,0.1.0,MIT
set-immediate-shim,1.0.1,MIT
@@ -1342,106 +968,61 @@ sha.js,2.4.10,MIT
sha1,1.1.1,New BSD
shebang-command,1.2.0,MIT
shebang-regex,1.0.0,MIT
-sidekiq,5.1.3,LGPL
+sidekiq,5.2.1,LGPL
sidekiq-cron,0.6.0,MIT
-sidekiq-limit_fetch,3.4.0,MIT
signal-exit,3.0.2,ISC
-signet,0.8.1,Apache 2.0
-slack-node,0.2.0,MIT
+signet,0.11.0,Apache 2.0
slack-notifier,1.5.1,MIT
-slash,1.0.0,MIT
-slice-ansi,1.0.0,MIT
-smart-buffer,1.1.15,MIT
-smart-buffer,4.0.1,MIT
-smooshpack,0.0.48,SEE LICENSE.MD IN ROOT
-smtp-connection,2.12.0,MIT
+slugify,1.3.1,MIT
+smooshpack,0.0.48,LGPL
snapdragon,0.8.1,MIT
snapdragon-node,2.1.1,MIT
snapdragon-util,3.0.1,MIT
-sntp,1.0.9,BSD
-sntp,2.1.0,BSD
-socket.io,2.0.4,MIT
-socket.io-adapter,1.1.1,MIT
-socket.io-client,2.0.4,MIT
-socket.io-parser,3.1.2,MIT
-sockjs,0.3.19,MIT
-sockjs-client,1.1.4,MIT
-socks,1.1.10,MIT
-socks,1.1.9,MIT
-socks,2.2.1,MIT
-socks-proxy-agent,3.0.1,MIT
-socks-proxy-agent,4.0.1,MIT
sort-keys,2.0.0,MIT
sortablejs,1.7.0,MIT
source-list-map,2.0.0,MIT
-source-map,0.2.0,New BSD
-source-map,0.4.4,New BSD
source-map,0.5.0,New BSD
-source-map,0.5.6,New BSD
source-map,0.5.7,New BSD
source-map,0.6.1,New BSD
source-map-resolve,0.5.1,MIT
-source-map-support,0.4.18,MIT
source-map-url,0.4.0,MIT
-spdx-correct,1.0.2,Apache 2.0
-spdx-expression-parse,1.0.4,(MIT AND CC-BY-3.0)
-spdx-license-ids,1.2.2,Unlicense
-spdy,3.4.7,MIT
-spdy-transport,2.0.20,MIT
-split,0.3.3,MIT
split-string,3.1.0,MIT
-sprintf-js,1.0.3,New BSD
sprockets,3.7.2,MIT
sprockets-rails,3.2.1,MIT
sql.js,0.4.0,MIT
srcset,1.0.0,MIT
sshkey,1.9.0,MIT
-sshpk,1.13.1,MIT
ssri,5.2.4,ISC
+ssri,6.0.1,ISC
state_machines,0.5.0,MIT
state_machines-activemodel,0.5.1,MIT
state_machines-activerecord,0.5.1,MIT
static-extend,0.1.2,MIT
-statuses,1.3.1,MIT
statuses,1.4.0,MIT
-statuses,1.5.0,MIT
stickyfilljs,2.0.5,MIT
stream-browserify,2.0.1,MIT
-stream-combiner,0.0.4,MIT
stream-each,1.2.2,MIT
stream-http,2.8.2,MIT
stream-shift,1.0.0,MIT
-streamroller,0.7.0,MIT
strict-uri-encode,1.1.0,MIT
string-width,1.0.2,MIT
string-width,2.1.1,MIT
string_decoder,0.10.31,MIT
-string_decoder,1.0.3,MIT
string_decoder,1.1.1,MIT
-stringex,2.8.4,MIT
-stringstream,0.0.5,MIT
strip-ansi,3.0.1,MIT
strip-ansi,4.0.0,MIT
-strip-bom,2.0.0,MIT
-strip-bom,3.0.0,MIT
+strip-css-comments,3.0.0,MIT
strip-eof,1.0.0,MIT
-strip-indent,1.0.1,MIT
strip-json-comments,2.0.1,MIT
-style-loader,0.21.0,MIT
+style-loader,0.23.0,MIT
supports-color,2.0.0,MIT
-supports-color,3.2.3,MIT
-supports-color,5.4.0,MIT
+supports-color,5.5.0,MIT
svg4everybody,2.1.9,CC0-1.0
sys-filesystem,1.1.6,Artistic 2.0
-table,4.0.2,New BSD
-tapable,0.1.10,MIT
-tapable,1.0.0,MIT
+tapable,1.1.0,MIT
tar,4.4.4,ISC
temple,0.8.0,MIT
-term-size,1.2.0,MIT
-test-exclude,4.2.1,ISC
text,1.3.1,MIT
-text-table,0.2.0,MIT
textextensions,2.2.0,MIT
thor,0.19.4,MIT
thread_safe,0.3.6,Apache 2.0
@@ -1450,40 +1031,25 @@ three-orbit-controls,82.1.0,MIT
three-stl-loader,1.0.4,MIT
through,2.3.8,MIT
through2,2.0.3,MIT
-thunkify,2.1.2,MIT
-thunky,0.1.0,MIT*
tilt,2.0.8,MIT
timeago.js,3.0.2,MIT
timed-out,4.0.1,MIT
timers-browserify,2.0.10,MIT
-timespan,2.3.0,MIT
timfel-krb5-auth,0.8.3,LGPL
tiny-emitter,2.0.2,MIT
tmp,0.0.33,MIT
-to-array,0.1.4,MIT
to-arraybuffer,1.0.1,MIT
-to-fast-properties,1.0.3,MIT
to-fast-properties,2.0.0,MIT
to-object-path,0.3.0,MIT
to-regex,3.0.2,MIT
to-regex-range,2.1.1,MIT
toml-rb,1.0.0,MIT
-touch,3.1.0,ISC
-tough-cookie,2.3.3,New BSD
-traverse,0.6.6,MIT
-trim-newlines,1.0.0,MIT
trim-right,1.0.1,MIT
trollop,2.1.3,MIT
truncato,0.7.10,MIT
tryer,1.0.0,MIT
-tryit,1.0.3,MIT
tslib,1.9.3,Apache 2.0
-tsscmp,1.0.5,MIT
tty-browserify,0.0.0,MIT
-tunnel-agent,0.4.3,Apache 2.0
-tunnel-agent,0.6.0,Apache 2.0
-tweetnacl,0.14.5,Unlicense
-type-check,0.3.2,MIT
type-is,1.6.16,MIT
typedarray,0.0.6,MIT
tzinfo,1.2.5,MIT
@@ -1491,114 +1057,77 @@ u2f,0.2.1,MIT
uber,0.1.0,MIT
uglifier,2.7.2,MIT
uglify-es,3.3.9,Simplified BSD
-uglify-js,2.8.29,Simplified BSD
-uglify-to-browserify,1.0.2,MIT
uglifyjs-webpack-plugin,1.2.5,MIT
-ultron,1.1.1,MIT
-undefsafe,2.0.2,MIT
-underscore,1.7.0,MIT
underscore,1.9.0,MIT
unf,0.1.4,BSD
unf_ext,0.0.7.5,MIT
+unicode-canonical-property-names-ecmascript,1.0.4,MIT
+unicode-match-property-ecmascript,1.0.4,MIT
+unicode-match-property-value-ecmascript,1.0.2,MIT
+unicode-property-aliases-ecmascript,1.0.4,MIT
unicorn,5.1.0,ruby
unicorn-worker-killer,0.4.4,ruby
union-value,1.0.0,MIT
uniq,1.0.1,MIT
unique-filename,1.1.0,ISC
unique-slug,2.0.0,ISC
-unique-string,1.0.0,MIT
unpipe,1.0.0,MIT
unset-value,1.0.0,MIT
-unzip-response,2.0.1,MIT
-upath,1.0.5,MIT
upath,1.1.0,MIT
-update-notifier,2.3.0,Simplified BSD
+uri-js,4.2.2,Simplified BSD
urix,0.1.0,MIT
url,0.11.0,MIT
-url-join,4.0.0,MIT
-url-loader,1.0.1,MIT
-url-parse,1.0.5,MIT
-url-parse,1.1.9,MIT
-url-parse-lax,1.0.0,MIT
+url-loader,1.1.1,MIT
url-parse-lax,3.0.0,MIT
url-to-options,1.0.1,MIT
use,2.0.2,MIT
-useragent,2.2.1,MIT
util,0.10.3,MIT
util-deprecate,1.0.2,MIT
+util.promisify,1.0.0,MIT
utils-merge,1.0.1,MIT
-uuid,3.2.1,MIT
-uws,9.14.0,Zlib
v8-compile-cache,2.0.0,MIT
-validate-npm-package-license,3.0.1,Apache 2.0
validates_hostname,1.0.6,MIT
-vary,1.1.1,MIT
vary,1.1.2,MIT
-verror,1.10.0,MIT
version_sorter,2.1.0,MIT
virtus,1.0.5,MIT
visibilityjs,1.2.4,MIT
vm-browserify,0.0.4,MIT
vmstat,2.3.0,MIT
-void-elements,2.0.1,MIT
-vue,2.5.16,MIT
-vue-eslint-parser,2.0.3,MIT
+vue,2.5.17,MIT
vue-functional-data-merge,2.0.6,MIT
vue-hot-reload-api,2.3.0,MIT
-vue-loader,15.2.4,MIT
+vue-loader,15.4.2,MIT
vue-resource,1.5.0,MIT
vue-router,3.0.1,MIT
vue-style-loader,4.1.0,MIT
-vue-template-compiler,2.5.16,MIT
+vue-template-compiler,2.5.17,MIT
vue-template-es2015-compiler,1.6.0,MIT
vue-virtual-scroll-list,1.2.5,MIT
vuex,3.0.1,MIT
warden,1.2.7,MIT
watchpack,1.5.0,MIT
-wbuf,1.7.2,MIT
-webpack,4.16.0,MIT
-webpack-bundle-analyzer,2.13.1,MIT
-webpack-cli,3.0.8,MIT
-webpack-dev-middleware,3.1.3,MIT
-webpack-dev-server,3.1.4,MIT
-webpack-log,1.2.0,MIT
-webpack-rails,0.9.10,MIT
-webpack-sources,1.1.0,MIT
+webpack,4.19.1,MIT
+webpack-bundle-analyzer,3.0.2,MIT
+webpack-cli,3.1.0,MIT
+webpack-rails,0.9.11,MIT
+webpack-sources,1.3.0,MIT
webpack-stats-plugin,0.2.1,MIT
-websocket-driver,0.6.5,MIT
-websocket-extensions,0.1.1,MIT
-when,3.7.8,MIT
which,1.3.0,ISC
which-module,2.0.0,ISC
wide-align,1.1.2,ISC
-widest-line,2.0.0,MIT
wikicloth,0.8.1,MIT
-window-size,0.1.0,MIT
-with-callback,1.0.2,MIT
-wordwrap,0.0.2,MIT
-wordwrap,0.0.3,MIT
-wordwrap,1.0.0,MIT
worker-farm,1.5.2,MIT
worker-loader,2.0.0,MIT
wrap-ansi,2.1.0,MIT
wrappy,1.0.2,ISC
-write,0.2.1,MIT
-write-file-atomic,2.3.0,ISC
-ws,3.3.3,MIT
-ws,4.0.0,MIT
-xdg-basedir,3.0.0,MIT
+ws,6.0.0,MIT
xml-simple,1.1.5,ruby
xmlhttprequest,1.8.0,MIT
-xmlhttprequest-ssl,1.5.5,MIT
-xregexp,2.0.0,MIT
+xregexp,4.0.0,MIT
xtend,4.0.1,MIT
xterm,3.5.0,MIT
-y18n,3.2.1,ISC
y18n,4.0.0,ISC
yallist,2.1.2,ISC
yallist,3.0.2,ISC
-yargs,11.0.0,MIT
-yargs,11.1.0,MIT
-yargs,3.10.0,MIT
-yargs-parser,9.0.2,ISC
-yeast,0.1.2,MIT
+yargs,12.0.2,MIT
+yargs-parser,10.1.0,ISC
diff --git a/vendor/prometheus/values.yaml b/vendor/prometheus/values.yaml
index c432be72163..02ec3e2d9fe 100644
--- a/vendor/prometheus/values.yaml
+++ b/vendor/prometheus/values.yaml
@@ -1,5 +1,7 @@
alertmanager:
enabled: false
+ image:
+ tag: v0.15.2
kubeStateMetrics:
enabled: true
@@ -16,7 +18,7 @@ rbac:
server:
fullnameOverride: "prometheus-prometheus-server"
image:
- tag: v2.1.0
+ tag: v2.4.3
serverFiles:
alerts: {}
diff --git a/yarn.lock b/yarn.lock
index c1e9d0ab73e..3ee1b2525be 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,105 +2,706 @@
# yarn lockfile v1
-"@babel/code-frame@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9"
+"@babel/code-frame@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
+ integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==
dependencies:
- "@babel/highlight" "7.0.0-beta.44"
+ "@babel/highlight" "^7.0.0"
+
+"@babel/core@^7.1.2":
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.1.2.tgz#f8d2a9ceb6832887329a7b60f9d035791400ba4e"
+ integrity sha512-IFeSSnjXdhDaoysIlev//UzHZbdEmm7D0EIH2qtse9xK7mXEZQpYjs2P00XlP1qYsYvid79p+Zgg6tz1mp6iVw==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/generator" "^7.1.2"
+ "@babel/helpers" "^7.1.2"
+ "@babel/parser" "^7.1.2"
+ "@babel/template" "^7.1.2"
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.1.2"
+ convert-source-map "^1.1.0"
+ debug "^3.1.0"
+ json5 "^0.5.0"
+ lodash "^4.17.10"
+ resolve "^1.3.2"
+ semver "^5.4.1"
+ source-map "^0.5.0"
-"@babel/generator@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42"
+"@babel/generator@^7.0.0", "@babel/generator@^7.1.2":
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.1.2.tgz#fde75c072575ce7abbd97322e8fef5bae67e4630"
+ integrity sha512-70A9HWLS/1RHk3Ck8tNHKxOoKQuSKocYgwDN85Pyl/RBduss6AKxUR7RIZ/lzduQMSYfWEM4DDBu6A+XGbkFig==
dependencies:
- "@babel/types" "7.0.0-beta.44"
+ "@babel/types" "^7.1.2"
jsesc "^2.5.1"
- lodash "^4.2.0"
+ lodash "^4.17.10"
source-map "^0.5.0"
trim-right "^1.0.1"
-"@babel/helper-function-name@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd"
+"@babel/helper-annotate-as-pure@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32"
+ integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f"
+ integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==
+ dependencies:
+ "@babel/helper-explode-assignable-expression" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-call-delegate@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.1.0.tgz#6a957f105f37755e8645343d3038a22e1449cc4a"
+ integrity sha512-YEtYZrw3GUK6emQHKthltKNZwszBcHK58Ygcis+gVUrF4/FmTVr5CCqQNSfmvg2y+YDEANyYoaLz/SHsnusCwQ==
+ dependencies:
+ "@babel/helper-hoist-variables" "^7.0.0"
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-define-map@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz#3b74caec329b3c80c116290887c0dd9ae468c20c"
+ integrity sha512-yPPcW8dc3gZLN+U1mhYV91QU3n5uTbx7DUdf8NnPbjS0RMwBuHi9Xt2MUgppmNz7CJxTBWsGczTiEp1CSOTPRg==
+ dependencies:
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/types" "^7.0.0"
+ lodash "^4.17.10"
+
+"@babel/helper-explode-assignable-expression@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6"
+ integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==
+ dependencies:
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-function-name@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
+ integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.0.0"
+ "@babel/template" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-get-function-arity@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
+ integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-hoist-variables@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0.tgz#46adc4c5e758645ae7a45deb92bab0918c23bb88"
+ integrity sha512-Ggv5sldXUeSKsuzLkddtyhyHe2YantsxWKNi7A+7LeD12ExRDWTRk29JCXpaHPAbMaIPZSil7n+lq78WY2VY7w==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-member-expression-to-functions@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz#8cd14b0a0df7ff00f009e7d7a436945f47c7a16f"
+ integrity sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==
dependencies:
- "@babel/helper-get-function-arity" "7.0.0-beta.44"
- "@babel/template" "7.0.0-beta.44"
- "@babel/types" "7.0.0-beta.44"
+ "@babel/types" "^7.0.0"
-"@babel/helper-get-function-arity@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15"
+"@babel/helper-module-imports@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d"
+ integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==
dependencies:
- "@babel/types" "7.0.0-beta.44"
+ "@babel/types" "^7.0.0"
-"@babel/helper-split-export-declaration@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc"
+"@babel/helper-module-transforms@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.1.0.tgz#470d4f9676d9fad50b324cdcce5fbabbc3da5787"
+ integrity sha512-0JZRd2yhawo79Rcm4w0LwSMILFmFXjugG3yqf+P/UsKsRS1mJCmMwwlHDlMg7Avr9LrvSpp4ZSULO9r8jpCzcw==
dependencies:
- "@babel/types" "7.0.0-beta.44"
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/helper-simple-access" "^7.1.0"
+ "@babel/helper-split-export-declaration" "^7.0.0"
+ "@babel/template" "^7.1.0"
+ "@babel/types" "^7.0.0"
+ lodash "^4.17.10"
-"@babel/highlight@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5"
+"@babel/helper-optimise-call-expression@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5"
+ integrity sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-plugin-utils@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
+ integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
+
+"@babel/helper-regex@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0.tgz#2c1718923b57f9bbe64705ffe5640ac64d9bdb27"
+ integrity sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg==
+ dependencies:
+ lodash "^4.17.10"
+
+"@babel/helper-remap-async-to-generator@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f"
+ integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-wrap-function" "^7.1.0"
+ "@babel/template" "^7.1.0"
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-replace-supers@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.1.0.tgz#5fc31de522ec0ef0899dc9b3e7cf6a5dd655f362"
+ integrity sha512-BvcDWYZRWVuDeXTYZWxekQNO5D4kO55aArwZOTFXw6rlLQA8ZaDicJR1sO47h+HrnCiDFiww0fSPV0d713KBGQ==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.0.0"
+ "@babel/helper-optimise-call-expression" "^7.0.0"
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-simple-access@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c"
+ integrity sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==
+ dependencies:
+ "@babel/template" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-split-export-declaration@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813"
+ integrity sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-wrap-function@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz#8cf54e9190706067f016af8f75cb3df829cc8c66"
+ integrity sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA==
+ dependencies:
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/template" "^7.1.0"
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helpers@^7.1.2":
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.1.2.tgz#ab752e8c35ef7d39987df4e8586c63b8846234b5"
+ integrity sha512-Myc3pUE8eswD73aWcartxB16K6CGmHDv9KxOmD2CeOs/FaEAQodr3VYGmlvOmog60vNQ2w8QbatuahepZwrHiA==
+ dependencies:
+ "@babel/template" "^7.1.2"
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.1.2"
+
+"@babel/highlight@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4"
+ integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==
dependencies:
chalk "^2.0.0"
esutils "^2.0.2"
- js-tokens "^3.0.0"
-
-"@babel/template@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
- dependencies:
- "@babel/code-frame" "7.0.0-beta.44"
- "@babel/types" "7.0.0-beta.44"
- babylon "7.0.0-beta.44"
- lodash "^4.2.0"
-
-"@babel/traverse@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966"
- dependencies:
- "@babel/code-frame" "7.0.0-beta.44"
- "@babel/generator" "7.0.0-beta.44"
- "@babel/helper-function-name" "7.0.0-beta.44"
- "@babel/helper-split-export-declaration" "7.0.0-beta.44"
- "@babel/types" "7.0.0-beta.44"
- babylon "7.0.0-beta.44"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.2":
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.2.tgz#85c5c47af6d244fab77bce6b9bd830e38c978409"
+ integrity sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ==
+
+"@babel/plugin-proposal-async-generator-functions@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.1.0.tgz#41c1a702e10081456e23a7b74d891922dd1bb6ce"
+ integrity sha512-Fq803F3Jcxo20MXUSDdmZZXrPe6BWyGcWBPPNB/M7WaUYESKDeKMOGIxEzQOjGSmW/NWb6UaPZrtTB2ekhB/ew==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-remap-async-to-generator" "^7.1.0"
+ "@babel/plugin-syntax-async-generators" "^7.0.0"
+
+"@babel/plugin-proposal-class-properties@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.1.0.tgz#9af01856b1241db60ec8838d84691aa0bd1e8df4"
+ integrity sha512-/PCJWN+CKt5v1xcGn4vnuu13QDoV+P7NcICP44BoonAJoPSGwVkgrXihFIQGiEjjPlUDBIw1cM7wYFLARS2/hw==
+ dependencies:
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-member-expression-to-functions" "^7.0.0"
+ "@babel/helper-optimise-call-expression" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-replace-supers" "^7.1.0"
+ "@babel/plugin-syntax-class-properties" "^7.0.0"
+
+"@babel/plugin-proposal-json-strings@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz#3b4d7b5cf51e1f2e70f52351d28d44fc2970d01e"
+ integrity sha512-kfVdUkIAGJIVmHmtS/40i/fg/AGnw/rsZBCaapY5yjeO5RA9m165Xbw9KMOu2nqXP5dTFjEjHdfNdoVcHv133Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-json-strings" "^7.0.0"
+
+"@babel/plugin-proposal-object-rest-spread@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0.tgz#9a17b547f64d0676b6c9cecd4edf74a82ab85e7e"
+ integrity sha512-14fhfoPcNu7itSen7Py1iGN0gEm87hX/B+8nZPqkdmANyyYWYMY2pjA3r8WXbWVKMzfnSNS0xY8GVS0IjXi/iw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-object-rest-spread" "^7.0.0"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0.tgz#b610d928fe551ff7117d42c8bb410eec312a6425"
+ integrity sha512-JPqAvLG1s13B/AuoBjdBYvn38RqW6n1TzrQO839/sIpqLpbnXKacsAgpZHzLD83Sm8SDXMkkrAvEnJ25+0yIpw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.0.0"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0.tgz#498b39cd72536cd7c4b26177d030226eba08cd33"
+ integrity sha512-tM3icA6GhC3ch2SkmSxv7J/hCWKISzwycub6eGsDrFDgukD4dZ/I+x81XgW0YslS6mzNuQ1Cbzh5osjIMgepPQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-regex" "^7.0.0"
+ regexpu-core "^4.2.0"
+
+"@babel/plugin-syntax-async-generators@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0.tgz#bf0891dcdbf59558359d0c626fdc9490e20bc13c"
+ integrity sha512-im7ged00ddGKAjcZgewXmp1vxSZQQywuQXe2B1A7kajjZmDeY/ekMPmWr9zJgveSaQH0k7BcGrojQhcK06l0zA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-class-properties@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0.tgz#e051af5d300cbfbcec4a7476e37a803489881634"
+ integrity sha512-cR12g0Qzn4sgkjrbrzWy2GE7m9vMl/sFkqZ3gIpAQdrvPDnLM8180i+ANDFIXfjHo9aqp0ccJlQ0QNZcFUbf9w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-dynamic-import@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.0.0.tgz#6dfb7d8b6c3be14ce952962f658f3b7eb54c33ee"
+ integrity sha512-Gt9xNyRrCHCiyX/ZxDGOcBnlJl0I3IWicpZRC4CdC0P5a/I07Ya2OAMEBU+J7GmRFVmIetqEYRko6QYRuKOESw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-import-meta@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.0.0.tgz#ca946b73216c29c39a55ef2d739097fee8a85d69"
+ integrity sha512-FEoGvhXVAiWzpDjyZIlBGzKyNk/lnRPy7aPke3PjVkiAY0QFsvFfkjUg5diRwVfowBA8SJqvFt0ZoXNSjl70hQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-json-strings@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz#0d259a68090e15b383ce3710e01d5b23f3770cbd"
+ integrity sha512-UlSfNydC+XLj4bw7ijpldc1uZ/HB84vw+U6BTuqMdIEmz/LDe63w/GHtpQMdXWdqQZFeAI9PjnHe/vDhwirhKA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-object-rest-spread@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz#37d8fbcaf216bd658ea1aebbeb8b75e88ebc549b"
+ integrity sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0.tgz#886f72008b3a8b185977f7cb70713b45e51ee475"
+ integrity sha512-Wc+HVvwjcq5qBg1w5RG9o9RVzmCaAg/Vp0erHCKpAYV8La6I94o4GQAmFYNmkzoMO6gzoOSulpKeSSz6mPEoZw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-arrow-functions@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz#a6c14875848c68a3b4b3163a486535ef25c7e749"
+ integrity sha512-2EZDBl1WIO/q4DIkIp4s86sdp4ZifL51MoIviLY/gG/mLSuOIEg7J8o6mhbxOTvUJkaN50n+8u41FVsr5KLy/w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-async-to-generator@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.1.0.tgz#109e036496c51dd65857e16acab3bafdf3c57811"
+ integrity sha512-rNmcmoQ78IrvNCIt/R9U+cixUHeYAzgusTFgIAv+wQb9HJU4szhpDD6e5GCACmj/JP5KxuCwM96bX3L9v4ZN/g==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-remap-async-to-generator" "^7.1.0"
+
+"@babel/plugin-transform-block-scoped-functions@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0.tgz#482b3f75103927e37288b3b67b65f848e2aa0d07"
+ integrity sha512-AOBiyUp7vYTqz2Jibe1UaAWL0Hl9JUXEgjFvvvcSc9MVDItv46ViXFw2F7SVt1B5k+KWjl44eeXOAk3UDEaJjQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-block-scoping@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0.tgz#1745075edffd7cdaf69fab2fb6f9694424b7e9bc"
+ integrity sha512-GWEMCrmHQcYWISilUrk9GDqH4enf3UmhOEbNbNrlNAX1ssH3MsS1xLOS6rdjRVPgA7XXVPn87tRkdTEoA/dxEg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ lodash "^4.17.10"
+
+"@babel/plugin-transform-classes@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.1.0.tgz#ab3f8a564361800cbc8ab1ca6f21108038432249"
+ integrity sha512-rNaqoD+4OCBZjM7VaskladgqnZ1LO6o2UxuWSDzljzW21pN1KXkB7BstAVweZdxQkHAujps5QMNOTWesBciKFg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-define-map" "^7.1.0"
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-optimise-call-expression" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-replace-supers" "^7.1.0"
+ "@babel/helper-split-export-declaration" "^7.0.0"
+ globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0.tgz#2fbb8900cd3e8258f2a2ede909b90e7556185e31"
+ integrity sha512-ubouZdChNAv4AAWAgU7QKbB93NU5sHwInEWfp+/OzJKA02E6Woh9RVoX4sZrbRwtybky/d7baTUqwFx+HgbvMA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-destructuring@^7.0.0":
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.1.2.tgz#5fa77d473f5a0a3f5266ad7ce2e8c995a164d60a"
+ integrity sha512-cvToXvp/OsYxtEn57XJu9BvsGSEYjAh9UeUuXpoi7x6QHB7YdWyQ4lRU/q0Fu1IJNT0o0u4FQ1DMQBzJ8/8vZg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-dotall-regex@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0.tgz#73a24da69bc3c370251f43a3d048198546115e58"
+ integrity sha512-00THs8eJxOJUFVx1w8i1MBF4XH4PsAjKjQ1eqN/uCH3YKwP21GCKfrn6YZFZswbOk9+0cw1zGQPHVc1KBlSxig==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-regex" "^7.0.0"
+ regexpu-core "^4.1.3"
+
+"@babel/plugin-transform-duplicate-keys@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0.tgz#a0601e580991e7cace080e4cf919cfd58da74e86"
+ integrity sha512-w2vfPkMqRkdxx+C71ATLJG30PpwtTpW7DDdLqYt2acXU7YjztzeWW2Jk1T6hKqCLYCcEA5UQM/+xTAm+QCSnuQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-exponentiation-operator@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.1.0.tgz#9c34c2ee7fd77e02779cfa37e403a2e1003ccc73"
+ integrity sha512-uZt9kD1Pp/JubkukOGQml9tqAeI8NkE98oZnHZ2qHRElmeKCodbTZgOEUtujSCSLhHSBWbzNiFSDIMC4/RBTLQ==
+ dependencies:
+ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-for-of@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0.tgz#f2ba4eadb83bd17dc3c7e9b30f4707365e1c3e39"
+ integrity sha512-TlxKecN20X2tt2UEr2LNE6aqA0oPeMT1Y3cgz8k4Dn1j5ObT8M3nl9aA37LLklx0PBZKETC9ZAf9n/6SujTuXA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-function-name@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.1.0.tgz#29c5550d5c46208e7f730516d41eeddd4affadbb"
+ integrity sha512-VxOa1TMlFMtqPW2IDYZQaHsFrq/dDoIjgN098NowhexhZcz3UGlvPgZXuE1jEvNygyWyxRacqDpCZt+par1FNg==
+ dependencies:
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-literals@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0.tgz#2aec1d29cdd24c407359c930cdd89e914ee8ff86"
+ integrity sha512-1NTDBWkeNXgpUcyoVFxbr9hS57EpZYXpje92zv0SUzjdu3enaRwF/l3cmyRnXLtIdyJASyiS6PtybK+CgKf7jA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-modules-amd@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.1.0.tgz#f9e0a7072c12e296079b5a59f408ff5b97bf86a8"
+ integrity sha512-wt8P+xQ85rrnGNr2x1iV3DW32W8zrB6ctuBkYBbf5/ZzJY99Ob4MFgsZDFgczNU76iy9PWsy4EuxOliDjdKw6A==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-modules-commonjs@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.1.0.tgz#0a9d86451cbbfb29bd15186306897c67f6f9a05c"
+ integrity sha512-wtNwtMjn1XGwM0AXPspQgvmE6msSJP15CX2RVfpTSTNPLhKhaOjaIfBaVfj4iUZ/VrFSodcFedwtPg/NxwQlPA==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-simple-access" "^7.1.0"
+
+"@babel/plugin-transform-modules-systemjs@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0.tgz#8873d876d4fee23209decc4d1feab8f198cf2df4"
+ integrity sha512-8EDKMAsitLkiF/D4Zhe9CHEE2XLh4bfLbb9/Zf3FgXYQOZyZYyg7EAel/aT2A7bHv62jwHf09q2KU/oEexr83g==
+ dependencies:
+ "@babel/helper-hoist-variables" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-modules-umd@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.1.0.tgz#a29a7d85d6f28c3561c33964442257cc6a21f2a8"
+ integrity sha512-enrRtn5TfRhMmbRwm7F8qOj0qEYByqUvTttPEGimcBH4CJHphjyK1Vg7sdU7JjeEmgSpM890IT/efS2nMHwYig==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-new-target@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0.tgz#ae8fbd89517fa7892d20e6564e641e8770c3aa4a"
+ integrity sha512-yin069FYjah+LbqfGeTfzIBODex/e++Yfa0rH0fpfam9uTbuEeEOx5GLGr210ggOV77mVRNoeqSYqeuaqSzVSw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-object-super@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.1.0.tgz#b1ae194a054b826d8d4ba7ca91486d4ada0f91bb"
+ integrity sha512-/O02Je1CRTSk2SSJaq0xjwQ8hG4zhZGNjE8psTsSNPXyLRCODv7/PBozqT5AmQMzp7MI3ndvMhGdqp9c96tTEw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-replace-supers" "^7.1.0"
+
+"@babel/plugin-transform-parameters@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.1.0.tgz#44f492f9d618c9124026e62301c296bf606a7aed"
+ integrity sha512-vHV7oxkEJ8IHxTfRr3hNGzV446GAb+0hgbA7o/0Jd76s+YzccdWuTU296FOCOl/xweU4t/Ya4g41yWz80RFCRw==
+ dependencies:
+ "@babel/helper-call-delegate" "^7.1.0"
+ "@babel/helper-get-function-arity" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-regenerator@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0.tgz#5b41686b4ed40bef874d7ed6a84bdd849c13e0c1"
+ integrity sha512-sj2qzsEx8KDVv1QuJc/dEfilkg3RRPvPYx/VnKLtItVQRWt1Wqf5eVCOLZm29CiGFfYYsA3VPjfizTCV0S0Dlw==
+ dependencies:
+ regenerator-transform "^0.13.3"
+
+"@babel/plugin-transform-shorthand-properties@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0.tgz#85f8af592dcc07647541a0350e8c95c7bf419d15"
+ integrity sha512-g/99LI4vm5iOf5r1Gdxq5Xmu91zvjhEG5+yZDJW268AZELAu4J1EiFLnkSG3yuUsZyOipVOVUKoGPYwfsTymhw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-spread@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0.tgz#93583ce48dd8c85e53f3a46056c856e4af30b49b"
+ integrity sha512-L702YFy2EvirrR4shTj0g2xQp7aNwZoWNCkNu2mcoU0uyzMl0XRwDSwzB/xp6DSUFiBmEXuyAyEN16LsgVqGGQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-sticky-regex@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0.tgz#30a9d64ac2ab46eec087b8530535becd90e73366"
+ integrity sha512-LFUToxiyS/WD+XEWpkx/XJBrUXKewSZpzX68s+yEOtIbdnsRjpryDw9U06gYc6klYEij/+KQVRnD3nz3AoKmjw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-regex" "^7.0.0"
+
+"@babel/plugin-transform-template-literals@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0.tgz#084f1952efe5b153ddae69eb8945f882c7a97c65"
+ integrity sha512-vA6rkTCabRZu7Nbl9DfLZE1imj4tzdWcg5vtdQGvj+OH9itNNB6hxuRMHuIY8SGnEt1T9g5foqs9LnrHzsqEFg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-typeof-symbol@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0.tgz#4dcf1e52e943e5267b7313bff347fdbe0f81cec9"
+ integrity sha512-1r1X5DO78WnaAIvs5uC48t41LLckxsYklJrZjNKcevyz83sF2l4RHbw29qrCPr/6ksFsdfRpT/ZgxNWHXRnffg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-unicode-regex@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0.tgz#c6780e5b1863a76fe792d90eded9fcd5b51d68fc"
+ integrity sha512-uJBrJhBOEa3D033P95nPHu3nbFwFE9ZgXsfEitzoIXIwqAZWk7uXcg06yFKXz9FSxBH5ucgU/cYdX0IV8ldHKw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-regex" "^7.0.0"
+ regexpu-core "^4.1.3"
+
+"@babel/preset-env@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.1.0.tgz#e67ea5b0441cfeab1d6f41e9b5c79798800e8d11"
+ integrity sha512-ZLVSynfAoDHB/34A17/JCZbyrzbQj59QC1Anyueb4Bwjh373nVPq5/HMph0z+tCmcDjXDe+DlKQq9ywQuvWrQg==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-proposal-async-generator-functions" "^7.1.0"
+ "@babel/plugin-proposal-json-strings" "^7.0.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.0.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.0.0"
+ "@babel/plugin-proposal-unicode-property-regex" "^7.0.0"
+ "@babel/plugin-syntax-async-generators" "^7.0.0"
+ "@babel/plugin-syntax-object-rest-spread" "^7.0.0"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.0.0"
+ "@babel/plugin-transform-arrow-functions" "^7.0.0"
+ "@babel/plugin-transform-async-to-generator" "^7.1.0"
+ "@babel/plugin-transform-block-scoped-functions" "^7.0.0"
+ "@babel/plugin-transform-block-scoping" "^7.0.0"
+ "@babel/plugin-transform-classes" "^7.1.0"
+ "@babel/plugin-transform-computed-properties" "^7.0.0"
+ "@babel/plugin-transform-destructuring" "^7.0.0"
+ "@babel/plugin-transform-dotall-regex" "^7.0.0"
+ "@babel/plugin-transform-duplicate-keys" "^7.0.0"
+ "@babel/plugin-transform-exponentiation-operator" "^7.1.0"
+ "@babel/plugin-transform-for-of" "^7.0.0"
+ "@babel/plugin-transform-function-name" "^7.1.0"
+ "@babel/plugin-transform-literals" "^7.0.0"
+ "@babel/plugin-transform-modules-amd" "^7.1.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.1.0"
+ "@babel/plugin-transform-modules-systemjs" "^7.0.0"
+ "@babel/plugin-transform-modules-umd" "^7.1.0"
+ "@babel/plugin-transform-new-target" "^7.0.0"
+ "@babel/plugin-transform-object-super" "^7.1.0"
+ "@babel/plugin-transform-parameters" "^7.1.0"
+ "@babel/plugin-transform-regenerator" "^7.0.0"
+ "@babel/plugin-transform-shorthand-properties" "^7.0.0"
+ "@babel/plugin-transform-spread" "^7.0.0"
+ "@babel/plugin-transform-sticky-regex" "^7.0.0"
+ "@babel/plugin-transform-template-literals" "^7.0.0"
+ "@babel/plugin-transform-typeof-symbol" "^7.0.0"
+ "@babel/plugin-transform-unicode-regex" "^7.0.0"
+ browserslist "^4.1.0"
+ invariant "^2.2.2"
+ js-levenshtein "^1.1.3"
+ semver "^5.3.0"
+
+"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2":
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.2.tgz#090484a574fef5a2d2d7726a674eceda5c5b5644"
+ integrity sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/parser" "^7.1.2"
+ "@babel/types" "^7.1.2"
+
+"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.1.0.tgz#503ec6669387efd182c3888c4eec07bcc45d91b2"
+ integrity sha512-bwgln0FsMoxm3pLOgrrnGaXk18sSM9JNf1/nHC/FksmNGFbYnPWY4GYCfLxyP1KRmfsxqkRpfoa6xr6VuuSxdw==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/generator" "^7.0.0"
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-split-export-declaration" "^7.0.0"
+ "@babel/parser" "^7.1.0"
+ "@babel/types" "^7.0.0"
debug "^3.1.0"
globals "^11.1.0"
- invariant "^2.2.0"
- lodash "^4.2.0"
+ lodash "^4.17.10"
-"@babel/types@7.0.0-beta.44":
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757"
+"@babel/types@^7.0.0", "@babel/types@^7.1.2":
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.1.2.tgz#183e7952cf6691628afdc2e2b90d03240bac80c0"
+ integrity sha512-pb1I05sZEKiSlMUV9UReaqsCPUpgbHHHu2n1piRm7JkuBkm6QxcaIzKu6FMnMtCbih/cEYTR+RGYYC96Yk9HAg==
dependencies:
esutils "^2.0.2"
- lodash "^4.2.0"
+ lodash "^4.17.10"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.27.0":
- version "1.27.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.27.0.tgz#638e70399ebd59e503732177316bb9a18bf7a13f"
-
-"@gitlab-org/gitlab-ui@1.0.5":
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.0.5.tgz#a64b402650494115c8b494a44b72c2d6fbf33fff"
+"@gitlab/eslint-config@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/eslint-config/-/eslint-config-1.1.0.tgz#9757764b3a78b6bacfbcd9533331cb6345ffdd59"
+ integrity sha512-tTdHj8nmZbgl7ygzZYYgfETgvFX/+Z/xA5abqbmwcejpI4fmJUhQwERTHu7P+NwQ2ywzPCS6dO4LljlT/r1jBw==
+ dependencies:
+ babel-eslint "^10.0.1"
+ eslint-config-airbnb-base "^13.1.0"
+ eslint-config-prettier "^3.1.0"
+ eslint-plugin-filenames "^1.3.2"
+ eslint-plugin-import "^2.14.0"
+ eslint-plugin-promise "^4.0.1"
+ eslint-plugin-vue "^5.0.0-beta.3"
+
+"@gitlab/svgs@^1.38.0":
+ version "1.38.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.38.0.tgz#e2f6e73379d60c7c63af4df8242a94c4671a1dfe"
+ integrity sha512-Mzv6PxVbWEPvvMgXHaGxk8UE1Gard2gifca6loLgfLH7BtjXfESiZyJdQkkTSeBYp5MoqQa88Kw+vJYobwjsSw==
+
+"@gitlab/ui@^1.11.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-1.11.0.tgz#b771c2c3d627cf9efbe98c71ee5739624f2ff51f"
+ integrity sha512-hGMHM45kcv9725R6G+n/HxvF3KfVb9oBGRNf1+4n3xAGmtXJ2NlPdIXIsDaye3EeVF9PTOtjLuaqrcp6AGNqZg==
dependencies:
- "@gitlab-org/gitlab-svgs" "^1.23.0"
+ babel-standalone "^6.26.0"
bootstrap-vue "^2.0.0-rc.11"
+ copy-to-clipboard "^3.0.8"
+ highlight.js "^9.13.1"
+ js-beautify "^1.8.8"
+ lodash "^4.17.11"
+ url-search-params-polyfill "^5.0.0"
vue "^2.5.16"
+ vue-loader "^15.4.2"
"@sindresorhus/is@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
+ integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
+
+"@types/events@*":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
+ integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==
+
+"@types/glob@^5":
+ version "5.0.35"
+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a"
+ integrity sha512-wc+VveszMLyMWFvXLkloixT4n0harUIVZjnpzztaZ0nKLuul7Z32iMt2fUFGAaZ4y1XWjFRMtCI5ewvyh4aIeg==
+ dependencies:
+ "@types/events" "*"
+ "@types/minimatch" "*"
+ "@types/node" "*"
"@types/jquery@^2.0.40":
version "2.0.48"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.48.tgz#3e90d8cde2d29015e5583017f7830cb3975b2eef"
+ integrity sha512-nNLzUrVjaRV/Ds1eHZLYTd7IZxs38cwwLSaqMJj8OTXY8xNUbxSK69bi9cMLvQ7dm/IBeQ1wHwQ0S1uYa0rd2w==
-"@vue/component-compiler-utils@^1.2.1":
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-1.2.1.tgz#3d543baa75cfe5dab96e29415b78366450156ef6"
+"@types/minimatch@*":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
+"@types/node@*":
+ version "10.5.2"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.2.tgz#f19f05314d5421fe37e74153254201a7bf00a707"
+ integrity sha512-m9zXmifkZsMHZBOyxZWilMwmTlpC8x5Ty360JKTiXvlXZfBWYpsg9ZZvP/Ye+iZUh+Q+MxDLjItVTWIsfwz+8Q==
+
+"@types/node@^10.11.7":
+ version "10.12.9"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.9.tgz#a07bfa74331471e1dc22a47eb72026843f7b95c8"
+ integrity sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA==
+
+"@types/parse5@^5":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.0.tgz#9ae2106efc443d7c1e26570aa8247828c9c80f11"
+ integrity sha512-J5D3z703XTDIGQFYXsnU9uRCW9e9mMEFO0Kpe6kykyiboqziru/RlZ0hM2P+PKTG4NHG1SjLrqae/NrV2iJApQ==
+
+"@types/semver@^5.5.0":
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
+ integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==
+
+"@vue/component-compiler-utils@^2.0.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.2.0.tgz#bbbb7ed38a9a8a7c93abe7ef2e54a90a04b631b4"
+ integrity sha512-pS4zlcdD7BvedyB+IfiTfrbi6C977UMIfulSk8r6uL0BU46ZE2+fUj/zbSNSfVxeaj9ElmnSni5OMwF9np+b+w==
dependencies:
consolidate "^0.15.1"
hash-sum "^1.0.2"
@@ -108,1054 +709,531 @@
merge-source-map "^1.1.0"
postcss "^6.0.20"
postcss-selector-parser "^3.1.1"
- prettier "^1.11.1"
+ prettier "1.13.7"
source-map "^0.5.6"
vue-template-es2015-compiler "^1.6.0"
-"@webassemblyjs/ast@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25"
+"@webassemblyjs/ast@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.6.tgz#3ef8c45b3e5e943a153a05281317474fef63e21e"
+ integrity sha512-8nkZS48EVsMUU0v6F1LCIOw4RYWLm2plMtbhFTjNgeXmsTNLuU3xTRtnljt9BFQB+iPbLRobkNrCWftWnNC7wQ==
dependencies:
- "@webassemblyjs/helper-module-context" "1.5.13"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
- "@webassemblyjs/wast-parser" "1.5.13"
- debug "^3.1.0"
+ "@webassemblyjs/helper-module-context" "1.7.6"
+ "@webassemblyjs/helper-wasm-bytecode" "1.7.6"
+ "@webassemblyjs/wast-parser" "1.7.6"
mamacro "^0.0.3"
-"@webassemblyjs/floating-point-hex-parser@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298"
+"@webassemblyjs/floating-point-hex-parser@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.6.tgz#7cb37d51a05c3fe09b464ae7e711d1ab3837801f"
+ integrity sha512-VBOZvaOyBSkPZdIt5VBMg3vPWxouuM13dPXGWI1cBh3oFLNcFJ8s9YA7S9l4mPI7+Q950QqOmqj06oa83hNWBA==
-"@webassemblyjs/helper-api-error@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59"
+"@webassemblyjs/helper-api-error@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.6.tgz#99b7e30e66f550a2638299a109dda84a622070ef"
+ integrity sha512-SCzhcQWHXfrfMSKcj8zHg1/kL9kb3aa5TN4plc/EREOs5Xop0ci5bdVBApbk2yfVi8aL+Ly4Qpp3/TRAUInjrg==
-"@webassemblyjs/helper-buffer@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e"
- dependencies:
- debug "^3.1.0"
+"@webassemblyjs/helper-buffer@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.6.tgz#ba0648be12bbe560c25c997e175c2018df39ca3e"
+ integrity sha512-1/gW5NaGsEOZ02fjnFiU8/OEEXU1uVbv2um0pQ9YVL3IHSkyk6xOwokzyqqO1qDZQUAllb+V8irtClPWntbVqw==
-"@webassemblyjs/helper-code-frame@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58"
+"@webassemblyjs/helper-code-frame@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.6.tgz#5a94d21b0057b69a7403fca0c253c3aaca95b1a5"
+ integrity sha512-+suMJOkSn9+vEvDvgyWyrJo5vJsWSDXZmJAjtoUq4zS4eqHyXImpktvHOZwXp1XQjO5H+YQwsBgqTQEc0J/5zg==
dependencies:
- "@webassemblyjs/wast-printer" "1.5.13"
+ "@webassemblyjs/wast-printer" "1.7.6"
-"@webassemblyjs/helper-fsm@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924"
+"@webassemblyjs/helper-fsm@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.6.tgz#ae1741c6f6121213c7a0b587fb964fac492d3e49"
+ integrity sha512-HCS6KN3wgxUihGBW7WFzEC/o8Eyvk0d56uazusnxXthDPnkWiMv+kGi9xXswL2cvfYfeK5yiM17z2K5BVlwypw==
-"@webassemblyjs/helper-module-context@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5"
+"@webassemblyjs/helper-module-context@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.6.tgz#116d19a51a6cebc8900ad53ca34ff8269c668c23"
+ integrity sha512-e8/6GbY7OjLM+6OsN7f2krC2qYVNaSr0B0oe4lWdmq5sL++8dYDD1TFbD1TdAdWMRTYNr/Qq7ovXWzia2EbSjw==
dependencies:
- debug "^3.1.0"
mamacro "^0.0.3"
-"@webassemblyjs/helper-wasm-bytecode@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747"
-
-"@webassemblyjs/helper-wasm-section@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d"
- dependencies:
- "@webassemblyjs/ast" "1.5.13"
- "@webassemblyjs/helper-buffer" "1.5.13"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
- "@webassemblyjs/wasm-gen" "1.5.13"
- debug "^3.1.0"
-
-"@webassemblyjs/ieee754@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364"
- dependencies:
- ieee754 "^1.1.11"
-
-"@webassemblyjs/leb128@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee"
- dependencies:
- long "4.0.0"
-
-"@webassemblyjs/utf8@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469"
-
-"@webassemblyjs/wasm-edit@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8"
- dependencies:
- "@webassemblyjs/ast" "1.5.13"
- "@webassemblyjs/helper-buffer" "1.5.13"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
- "@webassemblyjs/helper-wasm-section" "1.5.13"
- "@webassemblyjs/wasm-gen" "1.5.13"
- "@webassemblyjs/wasm-opt" "1.5.13"
- "@webassemblyjs/wasm-parser" "1.5.13"
- "@webassemblyjs/wast-printer" "1.5.13"
- debug "^3.1.0"
-
-"@webassemblyjs/wasm-gen@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e"
- dependencies:
- "@webassemblyjs/ast" "1.5.13"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
- "@webassemblyjs/ieee754" "1.5.13"
- "@webassemblyjs/leb128" "1.5.13"
- "@webassemblyjs/utf8" "1.5.13"
-
-"@webassemblyjs/wasm-opt@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138"
- dependencies:
- "@webassemblyjs/ast" "1.5.13"
- "@webassemblyjs/helper-buffer" "1.5.13"
- "@webassemblyjs/wasm-gen" "1.5.13"
- "@webassemblyjs/wasm-parser" "1.5.13"
- debug "^3.1.0"
-
-"@webassemblyjs/wasm-parser@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f"
- dependencies:
- "@webassemblyjs/ast" "1.5.13"
- "@webassemblyjs/helper-api-error" "1.5.13"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
- "@webassemblyjs/ieee754" "1.5.13"
- "@webassemblyjs/leb128" "1.5.13"
- "@webassemblyjs/utf8" "1.5.13"
-
-"@webassemblyjs/wast-parser@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea"
- dependencies:
- "@webassemblyjs/ast" "1.5.13"
- "@webassemblyjs/floating-point-hex-parser" "1.5.13"
- "@webassemblyjs/helper-api-error" "1.5.13"
- "@webassemblyjs/helper-code-frame" "1.5.13"
- "@webassemblyjs/helper-fsm" "1.5.13"
- long "^3.2.0"
+"@webassemblyjs/helper-wasm-bytecode@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.6.tgz#98e515eaee611aa6834eb5f6a7f8f5b29fefb6f1"
+ integrity sha512-PzYFCb7RjjSdAOljyvLWVqd6adAOabJW+8yRT+NWhXuf1nNZWH+igFZCUK9k7Cx7CsBbzIfXjJc7u56zZgFj9Q==
+
+"@webassemblyjs/helper-wasm-section@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.6.tgz#783835867bdd686df7a95377ab64f51a275e8333"
+ integrity sha512-3GS628ppDPSuwcYlQ7cDCGr4W2n9c4hLzvnRKeuz+lGsJSmc/ADVoYpm1ts2vlB1tGHkjtQMni+yu8mHoMlKlA==
+ dependencies:
+ "@webassemblyjs/ast" "1.7.6"
+ "@webassemblyjs/helper-buffer" "1.7.6"
+ "@webassemblyjs/helper-wasm-bytecode" "1.7.6"
+ "@webassemblyjs/wasm-gen" "1.7.6"
+
+"@webassemblyjs/ieee754@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.6.tgz#c34fc058f2f831fae0632a8bb9803cf2d3462eb1"
+ integrity sha512-V4cIp0ruyw+hawUHwQLn6o2mFEw4t50tk530oKsYXQhEzKR+xNGDxs/SFFuyTO7X3NzEu4usA3w5jzhl2RYyzQ==
+ dependencies:
+ "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.6.tgz#197f75376a29f6ed6ace15898a310d871d92f03b"
+ integrity sha512-ojdlG8WpM394lBow4ncTGJoIVZ4aAtNOWHhfAM7m7zprmkVcKK+2kK5YJ9Bmj6/ketTtOn7wGSHCtMt+LzqgYQ==
+ dependencies:
+ "@xtuc/long" "4.2.1"
+
+"@webassemblyjs/utf8@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.6.tgz#eb62c66f906af2be70de0302e29055d25188797d"
+ integrity sha512-oId+tLxQ+AeDC34ELRYNSqJRaScB0TClUU6KQfpB8rNT6oelYlz8axsPhf6yPTg7PBJ/Z5WcXmUYiHEWgbbHJw==
+
+"@webassemblyjs/wasm-edit@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.6.tgz#fa41929160cd7d676d4c28ecef420eed5b3733c5"
+ integrity sha512-pTNjLO3o41v/Vz9VFLl+I3YLImpCSpodFW77pNoH4agn5I6GgSxXHXtvWDTvYJFty0jSeXZWLEmbaSIRUDlekg==
+ dependencies:
+ "@webassemblyjs/ast" "1.7.6"
+ "@webassemblyjs/helper-buffer" "1.7.6"
+ "@webassemblyjs/helper-wasm-bytecode" "1.7.6"
+ "@webassemblyjs/helper-wasm-section" "1.7.6"
+ "@webassemblyjs/wasm-gen" "1.7.6"
+ "@webassemblyjs/wasm-opt" "1.7.6"
+ "@webassemblyjs/wasm-parser" "1.7.6"
+ "@webassemblyjs/wast-printer" "1.7.6"
+
+"@webassemblyjs/wasm-gen@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.6.tgz#695ac38861ab3d72bf763c8c75e5f087ffabc322"
+ integrity sha512-mQvFJVumtmRKEUXMohwn8nSrtjJJl6oXwF3FotC5t6e2hlKMh8sIaW03Sck2MDzw9xPogZD7tdP5kjPlbH9EcQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.7.6"
+ "@webassemblyjs/helper-wasm-bytecode" "1.7.6"
+ "@webassemblyjs/ieee754" "1.7.6"
+ "@webassemblyjs/leb128" "1.7.6"
+ "@webassemblyjs/utf8" "1.7.6"
+
+"@webassemblyjs/wasm-opt@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.6.tgz#fbafa78e27e1a75ab759a4b658ff3d50b4636c21"
+ integrity sha512-go44K90fSIsDwRgtHhX14VtbdDPdK2sZQtZqUcMRvTojdozj5tLI0VVJAzLCfz51NOkFXezPeVTAYFqrZ6rI8Q==
+ dependencies:
+ "@webassemblyjs/ast" "1.7.6"
+ "@webassemblyjs/helper-buffer" "1.7.6"
+ "@webassemblyjs/wasm-gen" "1.7.6"
+ "@webassemblyjs/wasm-parser" "1.7.6"
+
+"@webassemblyjs/wasm-parser@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.6.tgz#84eafeeff405ad6f4c4b5777d6a28ae54eed51fe"
+ integrity sha512-t1T6TfwNY85pDA/HWPA8kB9xA4sp9ajlRg5W7EKikqrynTyFo+/qDzIpvdkOkOGjlS6d4n4SX59SPuIayR22Yg==
+ dependencies:
+ "@webassemblyjs/ast" "1.7.6"
+ "@webassemblyjs/helper-api-error" "1.7.6"
+ "@webassemblyjs/helper-wasm-bytecode" "1.7.6"
+ "@webassemblyjs/ieee754" "1.7.6"
+ "@webassemblyjs/leb128" "1.7.6"
+ "@webassemblyjs/utf8" "1.7.6"
+
+"@webassemblyjs/wast-parser@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.6.tgz#ca4d20b1516e017c91981773bd7e819d6bd9c6a7"
+ integrity sha512-1MaWTErN0ziOsNUlLdvwS+NS1QWuI/kgJaAGAMHX8+fMJFgOJDmN/xsG4h/A1Gtf/tz5VyXQciaqHZqp2q0vfg==
+ dependencies:
+ "@webassemblyjs/ast" "1.7.6"
+ "@webassemblyjs/floating-point-hex-parser" "1.7.6"
+ "@webassemblyjs/helper-api-error" "1.7.6"
+ "@webassemblyjs/helper-code-frame" "1.7.6"
+ "@webassemblyjs/helper-fsm" "1.7.6"
+ "@xtuc/long" "4.2.1"
mamacro "^0.0.3"
-"@webassemblyjs/wast-printer@1.5.13":
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95"
+"@webassemblyjs/wast-printer@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.6.tgz#a6002c526ac5fa230fe2c6d2f1bdbf4aead43a5e"
+ integrity sha512-vHdHSK1tOetvDcl1IV1OdDeGNe/NDDQ+KzuZHMtqTVP1xO/tZ/IKNpj5BaGk1OYFdsDWQqb31PIwdEyPntOWRQ==
dependencies:
- "@webassemblyjs/ast" "1.5.13"
- "@webassemblyjs/wast-parser" "1.5.13"
- long "^3.2.0"
+ "@webassemblyjs/ast" "1.7.6"
+ "@webassemblyjs/wast-parser" "1.7.6"
+ "@xtuc/long" "4.2.1"
-abbrev@1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+"@xtuc/ieee754@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+ integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.1":
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.1.tgz#5c85d662f76fa1d34575766c5dcd6615abcd30d8"
+ integrity sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==
-abbrev@1.0.x:
+abbrev@1, abbrev@1.0.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
+ integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU=
-accepts@~1.3.3, accepts@~1.3.4:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f"
+accepts@~1.3.3, accepts@~1.3.4, accepts@~1.3.5:
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
+ integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I=
dependencies:
- mime-types "~2.1.16"
+ mime-types "~2.1.18"
negotiator "0.6.1"
acorn-dynamic-import@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278"
+ integrity sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==
dependencies:
acorn "^5.0.0"
-acorn-jsx@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+acorn-jsx@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e"
+ integrity sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==
dependencies:
- acorn "^3.0.4"
+ acorn "^5.0.3"
-acorn@^3.0.4:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
-
-acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0:
- version "5.6.2"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.2.tgz#b1da1d7be2ac1b4a327fb9eab851702c5045b4e7"
-
-acorn@^5.6.2:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
-
-addressparser@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746"
+acorn@^5.0.0, acorn@^5.0.3, acorn@^5.6.0, acorn@^5.6.2, acorn@^5.7.3:
+ version "5.7.3"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
+ integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
after@0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
+ integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=
-agent-base@4, agent-base@^4.1.0, agent-base@^4.2.0, agent-base@~4.2.0:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
- dependencies:
- es6-promisify "^5.0.0"
-
-ajv-keywords@^2.1.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
-
-ajv-keywords@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be"
+ajv-errors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
+ integrity sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=
-ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0:
- version "5.5.2"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
- dependencies:
- co "^4.6.0"
- fast-deep-equal "^1.0.0"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.3.0"
+ajv-keywords@^3.0.0, ajv-keywords@^3.1.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
+ integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=
-ajv@^6.1.0:
- version "6.1.1"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.1.1.tgz#978d597fbc2b7d0e5a5c3ddeb149a682f2abfa0e"
+ajv@^6.0.1, ajv@^6.1.0, ajv@^6.5.3:
+ version "6.5.3"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9"
+ integrity sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==
dependencies:
- fast-deep-equal "^1.0.0"
+ fast-deep-equal "^2.0.1"
fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.3.0"
-
-align-text@^0.1.1, align-text@^0.1.3:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
- dependencies:
- kind-of "^3.0.2"
- longest "^1.0.1"
- repeat-string "^1.5.2"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
-
-amqplib@^0.5.2:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.5.2.tgz#d2d7313c7ffaa4d10bcf1e6252de4591b6cc7b63"
- dependencies:
- bitsyntax "~0.0.4"
- bluebird "^3.4.6"
- buffer-more-ints "0.0.2"
- readable-stream "1.x >=1.1.9"
- safe-buffer "^5.0.1"
+ integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
ansi-align@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
+ integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=
dependencies:
string-width "^2.0.0"
+ansi-colors@^3.0.0:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.0.5.tgz#cb9dc64993b64fd6945485f797fc3853137d9a7b"
+ integrity sha512-VVjWpkfaphxUBFarydrQ3n26zX5nIK7hcbT3/ielrvwDDyBBjuh2vuSw1P9zkPq0cfqvdw7lkYHnu+OLSfIBsg==
+
ansi-escapes@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+ integrity sha1-06ioOzGapneTZisT52HHkRQiMG4=
ansi-escapes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
+ integrity sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==
ansi-html@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
+ integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4=
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+ integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+ integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+ integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
+ integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
dependencies:
micromatch "^3.1.4"
normalize-path "^2.1.1"
-append-transform@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991"
+append-transform@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab"
+ integrity sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==
dependencies:
- default-require-extensions "^1.0.0"
+ default-require-extensions "^2.0.0"
aproba@^1.0.3, aproba@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+ integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
are-we-there-yet@~1.1.2:
version "1.1.4"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d"
+ integrity sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=
dependencies:
delegates "^1.0.0"
readable-stream "^2.0.6"
argparse@^1.0.7:
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+ integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
arr-flatten@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+ integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
arr-union@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+ integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
array-find-index@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
+ integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
array-find@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8"
+ integrity sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+ integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
array-flatten@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296"
-
-array-includes@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
- dependencies:
- define-properties "^1.1.2"
- es-abstract "^1.7.0"
+ integrity sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=
array-slice@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5"
+ integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU=
array-union@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
dependencies:
array-uniq "^1.0.1"
array-uniq@^1.0.1, array-uniq@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+ integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
array-unique@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+ integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=
array-unique@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+ integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
+ integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==
arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+ integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
asn1.js@^4.0.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
+ integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
dependencies:
bn.js "^4.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
-asn1@~0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
-
-assert-plus@1.0.0, assert-plus@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
-
-assert-plus@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
-
assert@^1.1.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
+ integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=
dependencies:
util "0.10.3"
assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
-
-ast-types@0.x.x:
- version "0.11.3"
- resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8"
+ integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
+ integrity sha1-GdOGodntxufByF04iu28xW0zYC0=
async-limiter@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
+ integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
-async@1.x, async@^1.4.0, async@^1.5.2:
+async@1.x, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+ integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
-async@^2.0.0, async@^2.1.4:
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
- dependencies:
- lodash "^4.14.0"
-
-async@~2.6.0:
+async@^2.0.0, async@^2.5.0, async@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
+ integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==
dependencies:
lodash "^4.17.10"
-asynckit@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
-
atob@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d"
+ integrity sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=
autosize@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.0.tgz#7a0599b1ba84d73bd7589b0d9da3870152c69237"
-
-aws-sign2@~0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
-
-aws-sign2@~0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
-
-aws4@^1.2.1, aws4@^1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
+ integrity sha1-egWZsbqE1zvXWJsNnaOHAVLGkjc=
axios-mock-adapter@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.15.0.tgz#fbc06825d8302c95c3334d21023bba996255d45d"
+ integrity sha1-+8BoJdgwLJXDM00hAju6mWJV1F0=
dependencies:
deep-equal "^1.0.1"
-axios@^0.15.3:
- version "0.15.3"
- resolved "https://registry.yarnpkg.com/axios/-/axios-0.15.3.tgz#2c9d638b2e191a08ea1d6cc988eadd6ba5bdc053"
- dependencies:
- follow-redirects "1.0.0"
-
axios@^0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.1.tgz#2d8e3e5d0bdbd7327f91bc814f5c57660f81824d"
+ integrity sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=
dependencies:
follow-redirects "^1.2.5"
is-buffer "^1.1.5"
-babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
+babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+ integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
dependencies:
chalk "^1.1.3"
esutils "^2.0.2"
js-tokens "^3.0.2"
-babel-core@^6.26.0, babel-core@^6.26.3:
- version "6.26.3"
- resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207"
+babel-eslint@^10.0.1:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed"
+ integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==
dependencies:
- babel-code-frame "^6.26.0"
- babel-generator "^6.26.0"
- babel-helpers "^6.24.1"
- babel-messages "^6.23.0"
- babel-register "^6.26.0"
- babel-runtime "^6.26.0"
- babel-template "^6.26.0"
- babel-traverse "^6.26.0"
- babel-types "^6.26.0"
- babylon "^6.18.0"
- convert-source-map "^1.5.1"
- debug "^2.6.9"
- json5 "^0.5.1"
- lodash "^4.17.4"
- minimatch "^3.0.4"
- path-is-absolute "^1.0.1"
- private "^0.1.8"
- slash "^1.0.0"
- source-map "^0.5.7"
-
-babel-eslint@^8.2.3:
- version "8.2.3"
- resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.3.tgz#1a2e6681cc9bc4473c32899e59915e19cd6733cf"
- dependencies:
- "@babel/code-frame" "7.0.0-beta.44"
- "@babel/traverse" "7.0.0-beta.44"
- "@babel/types" "7.0.0-beta.44"
- babylon "7.0.0-beta.44"
- eslint-scope "~3.7.1"
+ "@babel/code-frame" "^7.0.0"
+ "@babel/parser" "^7.0.0"
+ "@babel/traverse" "^7.0.0"
+ "@babel/types" "^7.0.0"
+ eslint-scope "3.7.1"
eslint-visitor-keys "^1.0.0"
-babel-generator@^6.18.0, babel-generator@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
- dependencies:
- babel-messages "^6.23.0"
- babel-runtime "^6.26.0"
- babel-types "^6.26.0"
- detect-indent "^4.0.0"
- jsesc "^1.3.0"
- lodash "^4.17.4"
- source-map "^0.5.6"
- trim-right "^1.0.1"
-
-babel-helper-bindify-decorators@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330"
- dependencies:
- babel-runtime "^6.22.0"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664"
- dependencies:
- babel-helper-explode-assignable-expression "^6.24.1"
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
-
-babel-helper-call-delegate@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d"
- dependencies:
- babel-helper-hoist-variables "^6.24.1"
- babel-runtime "^6.22.0"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-helper-define-map@^6.24.1:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f"
- dependencies:
- babel-helper-function-name "^6.24.1"
- babel-runtime "^6.26.0"
- babel-types "^6.26.0"
- lodash "^4.17.4"
-
-babel-helper-explode-assignable-expression@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa"
- dependencies:
- babel-runtime "^6.22.0"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-helper-explode-class@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb"
- dependencies:
- babel-helper-bindify-decorators "^6.24.1"
- babel-runtime "^6.22.0"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-helper-function-name@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
- dependencies:
- babel-helper-get-function-arity "^6.24.1"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-helper-get-function-arity@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
- dependencies:
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
-
-babel-helper-hoist-variables@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76"
- dependencies:
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
-
-babel-helper-optimise-call-expression@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
- dependencies:
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
-
-babel-helper-regex@^6.24.1:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72"
- dependencies:
- babel-runtime "^6.26.0"
- babel-types "^6.26.0"
- lodash "^4.17.4"
-
-babel-helper-remap-async-to-generator@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b"
- dependencies:
- babel-helper-function-name "^6.24.1"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-helper-replace-supers@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
- dependencies:
- babel-helper-optimise-call-expression "^6.24.1"
- babel-messages "^6.23.0"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-helpers@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
- dependencies:
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
-
-babel-loader@^7.1.5:
- version "7.1.5"
- resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.5.tgz#e3ee0cd7394aa557e013b02d3e492bfd07aa6d68"
+babel-loader@^8.0.4:
+ version "8.0.4"
+ resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.4.tgz#7bbf20cbe4560629e2e41534147692d3fecbdce6"
+ integrity sha512-fhBhNkUToJcW9nV46v8w87AJOwAJDz84c1CL57n3Stj73FANM/b9TbCUK4YhdOwEyZ+OxhYpdeZDNzSI29Firw==
dependencies:
find-cache-dir "^1.0.0"
loader-utils "^1.0.2"
mkdirp "^0.5.1"
+ util.promisify "^1.0.0"
babel-messages@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
+ integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=
dependencies:
babel-runtime "^6.22.0"
-babel-plugin-check-es2015-constants@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-istanbul@^4.1.6:
- version "4.1.6"
- resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45"
- dependencies:
- babel-plugin-syntax-object-rest-spread "^6.13.0"
- find-up "^2.1.0"
- istanbul-lib-instrument "^1.10.1"
- test-exclude "^4.2.1"
-
-babel-plugin-rewire@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-rewire/-/babel-plugin-rewire-1.1.0.tgz#a6b966d9d8c06c03d95dcda2eec4e2521519549b"
-
-babel-plugin-syntax-async-functions@^6.8.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
-
-babel-plugin-syntax-async-generators@^6.5.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a"
-
-babel-plugin-syntax-class-properties@^6.8.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de"
-
-babel-plugin-syntax-decorators@^6.13.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b"
-
-babel-plugin-syntax-dynamic-import@^6.18.0:
- version "6.18.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da"
-
-babel-plugin-syntax-exponentiation-operator@^6.8.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
-
-babel-plugin-syntax-object-rest-spread@^6.13.0, babel-plugin-syntax-object-rest-spread@^6.8.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
-
-babel-plugin-syntax-trailing-function-commas@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
-
-babel-plugin-transform-async-generator-functions@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db"
- dependencies:
- babel-helper-remap-async-to-generator "^6.24.1"
- babel-plugin-syntax-async-generators "^6.5.0"
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-async-to-generator@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761"
- dependencies:
- babel-helper-remap-async-to-generator "^6.24.1"
- babel-plugin-syntax-async-functions "^6.8.0"
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-class-properties@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac"
- dependencies:
- babel-helper-function-name "^6.24.1"
- babel-plugin-syntax-class-properties "^6.8.0"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
-
-babel-plugin-transform-decorators@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d"
- dependencies:
- babel-helper-explode-class "^6.24.1"
- babel-plugin-syntax-decorators "^6.13.0"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
- babel-types "^6.24.1"
-
-babel-plugin-transform-define@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.0.tgz#94c5f9459c810c738cc7c50cbd44a31829d6f319"
- dependencies:
- lodash "4.17.4"
- traverse "0.6.6"
-
-babel-plugin-transform-es2015-arrow-functions@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-block-scoped-functions@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-block-scoping@^6.24.1:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f"
- dependencies:
- babel-runtime "^6.26.0"
- babel-template "^6.26.0"
- babel-traverse "^6.26.0"
- babel-types "^6.26.0"
- lodash "^4.17.4"
-
-babel-plugin-transform-es2015-classes@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
- dependencies:
- babel-helper-define-map "^6.24.1"
- babel-helper-function-name "^6.24.1"
- babel-helper-optimise-call-expression "^6.24.1"
- babel-helper-replace-supers "^6.24.1"
- babel-messages "^6.23.0"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-computed-properties@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
- dependencies:
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-destructuring@^6.22.0:
- version "6.23.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-duplicate-keys@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e"
- dependencies:
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-for-of@^6.22.0:
- version "6.23.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-function-name@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
- dependencies:
- babel-helper-function-name "^6.24.1"
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-literals@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-modules-amd@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
- dependencies:
- babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a"
- dependencies:
- babel-plugin-transform-strict-mode "^6.24.1"
- babel-runtime "^6.26.0"
- babel-template "^6.26.0"
- babel-types "^6.26.0"
-
-babel-plugin-transform-es2015-modules-systemjs@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23"
- dependencies:
- babel-helper-hoist-variables "^6.24.1"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-modules-umd@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468"
- dependencies:
- babel-plugin-transform-es2015-modules-amd "^6.24.1"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-object-super@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d"
- dependencies:
- babel-helper-replace-supers "^6.24.1"
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-parameters@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
- dependencies:
- babel-helper-call-delegate "^6.24.1"
- babel-helper-get-function-arity "^6.24.1"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
- babel-traverse "^6.24.1"
- babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-shorthand-properties@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
- dependencies:
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-spread@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-sticky-regex@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
- dependencies:
- babel-helper-regex "^6.24.1"
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-template-literals@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-typeof-symbol@^6.22.0:
- version "6.23.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
- dependencies:
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-unicode-regex@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9"
- dependencies:
- babel-helper-regex "^6.24.1"
- babel-runtime "^6.22.0"
- regexpu-core "^2.0.0"
-
-babel-plugin-transform-exponentiation-operator@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e"
- dependencies:
- babel-helper-builder-binary-assignment-operator-visitor "^6.24.1"
- babel-plugin-syntax-exponentiation-operator "^6.8.0"
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-object-rest-spread@^6.22.0:
- version "6.23.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921"
- dependencies:
- babel-plugin-syntax-object-rest-spread "^6.8.0"
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-regenerator@^6.24.1:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f"
+babel-plugin-istanbul@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.0.tgz#6892f529eff65a3e2d33d87dc5888ffa2ecd4a30"
+ integrity sha512-CLoXPRSUWiR8yao8bShqZUIC6qLfZVVY3X1wj+QPNXu0wfmrRRfarh1LYy+dYMVI+bDj0ghy3tuqFFRFZmL1Nw==
dependencies:
- regenerator-transform "^0.10.0"
+ find-up "^3.0.0"
+ istanbul-lib-instrument "^3.0.0"
+ test-exclude "^5.0.0"
-babel-plugin-transform-strict-mode@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
- dependencies:
- babel-runtime "^6.22.0"
- babel-types "^6.24.1"
+babel-plugin-rewire@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-rewire/-/babel-plugin-rewire-1.2.0.tgz#822562d72ed2c84e47c0f95ee232c920853e9d89"
+ integrity sha512-JBZxczHw3tScS+djy6JPLMjblchGhLI89ep15H3SyjujIzlxo5nr6Yjo7AXotdeVczeBmWs0tF8PgJWDdgzAkQ==
babel-polyfill@6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d"
+ integrity sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=
dependencies:
babel-runtime "^6.22.0"
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
-babel-preset-es2015@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939"
- dependencies:
- babel-plugin-check-es2015-constants "^6.22.0"
- babel-plugin-transform-es2015-arrow-functions "^6.22.0"
- babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
- babel-plugin-transform-es2015-block-scoping "^6.24.1"
- babel-plugin-transform-es2015-classes "^6.24.1"
- babel-plugin-transform-es2015-computed-properties "^6.24.1"
- babel-plugin-transform-es2015-destructuring "^6.22.0"
- babel-plugin-transform-es2015-duplicate-keys "^6.24.1"
- babel-plugin-transform-es2015-for-of "^6.22.0"
- babel-plugin-transform-es2015-function-name "^6.24.1"
- babel-plugin-transform-es2015-literals "^6.22.0"
- babel-plugin-transform-es2015-modules-amd "^6.24.1"
- babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
- babel-plugin-transform-es2015-modules-systemjs "^6.24.1"
- babel-plugin-transform-es2015-modules-umd "^6.24.1"
- babel-plugin-transform-es2015-object-super "^6.24.1"
- babel-plugin-transform-es2015-parameters "^6.24.1"
- babel-plugin-transform-es2015-shorthand-properties "^6.24.1"
- babel-plugin-transform-es2015-spread "^6.22.0"
- babel-plugin-transform-es2015-sticky-regex "^6.24.1"
- babel-plugin-transform-es2015-template-literals "^6.22.0"
- babel-plugin-transform-es2015-typeof-symbol "^6.22.0"
- babel-plugin-transform-es2015-unicode-regex "^6.24.1"
- babel-plugin-transform-regenerator "^6.24.1"
-
-babel-preset-es2016@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-preset-es2016/-/babel-preset-es2016-6.24.1.tgz#f900bf93e2ebc0d276df9b8ab59724ebfd959f8b"
- dependencies:
- babel-plugin-transform-exponentiation-operator "^6.24.1"
-
-babel-preset-es2017@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-preset-es2017/-/babel-preset-es2017-6.24.1.tgz#597beadfb9f7f208bcfd8a12e9b2b29b8b2f14d1"
- dependencies:
- babel-plugin-syntax-trailing-function-commas "^6.22.0"
- babel-plugin-transform-async-to-generator "^6.24.1"
-
-babel-preset-latest@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-preset-latest/-/babel-preset-latest-6.24.1.tgz#677de069154a7485c2d25c577c02f624b85b85e8"
- dependencies:
- babel-preset-es2015 "^6.24.1"
- babel-preset-es2016 "^6.24.1"
- babel-preset-es2017 "^6.24.1"
-
-babel-preset-stage-2@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1"
- dependencies:
- babel-plugin-syntax-dynamic-import "^6.18.0"
- babel-plugin-transform-class-properties "^6.24.1"
- babel-plugin-transform-decorators "^6.24.1"
- babel-preset-stage-3 "^6.24.1"
-
-babel-preset-stage-3@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395"
- dependencies:
- babel-plugin-syntax-trailing-function-commas "^6.22.0"
- babel-plugin-transform-async-generator-functions "^6.24.1"
- babel-plugin-transform-async-to-generator "^6.24.1"
- babel-plugin-transform-exponentiation-operator "^6.24.1"
- babel-plugin-transform-object-rest-spread "^6.22.0"
-
-babel-register@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
- dependencies:
- babel-core "^6.26.0"
- babel-runtime "^6.26.0"
- core-js "^2.5.0"
- home-or-tmp "^2.0.0"
- lodash "^4.17.4"
- mkdirp "^0.5.1"
- source-map-support "^0.4.15"
-
-babel-runtime@^6.0.0, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+babel-runtime@^6.22.0, babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+ integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
-babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
+babel-standalone@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-standalone/-/babel-standalone-6.26.0.tgz#15fb3d35f2c456695815ebf1ed96fe7f015b6886"
+ integrity sha1-Ffs9NfLEVmlYFevx7Zb+fwFbaIY=
+
+babel-template@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
+ integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=
dependencies:
babel-runtime "^6.26.0"
babel-traverse "^6.26.0"
@@ -1163,9 +1241,10 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
babylon "^6.18.0"
lodash "^4.17.4"
-babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
+babel-traverse@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
+ integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
dependencies:
babel-code-frame "^6.26.0"
babel-messages "^6.23.0"
@@ -1177,46 +1256,50 @@ babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
invariant "^2.2.2"
lodash "^4.17.4"
-babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
+babel-types@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
+ integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
dependencies:
babel-runtime "^6.26.0"
esutils "^2.0.2"
lodash "^4.17.4"
to-fast-properties "^1.0.3"
-babylon@7.0.0-beta.44:
- version "7.0.0-beta.44"
- resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d"
-
babylon@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
+ integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
backo2@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
+ integrity sha1-MasayLEpNjRj41s+u2n038+6eUc=
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base64-arraybuffer@0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
+ integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
base64-js@^1.0.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.3.tgz#fb13668233d9614cf5fb4bce95a9ba4096cdf801"
+ integrity sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==
base64id@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
+ integrity sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=
base@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+ integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
dependencies:
cache-base "^1.0.1"
class-utils "^0.3.5"
@@ -1229,77 +1312,59 @@ base@^0.11.1:
batch@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
-
-bcrypt-pbkdf@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
- dependencies:
- tweetnacl "^0.14.3"
+ integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
better-assert@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
+ integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=
dependencies:
callsite "1.0.0"
-bfj-node4@^5.2.0:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/bfj-node4/-/bfj-node4-5.2.1.tgz#3a6aa2730cf6911ba2afb836c2f88f015d718f3f"
+bfj@^6.1.1:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.1.tgz#05a3b7784fbd72cfa3c22e56002ef99336516c48"
+ integrity sha512-+GUNvzHR4nRyGybQc2WpNJL4MJazMuvf92ueIyA0bIkPRwhhQu3IfZQ2PSoVPpCBJfmoSdOxu5rnotfFLlvYRQ==
dependencies:
bluebird "^3.5.1"
check-types "^7.3.0"
+ hoopy "^0.1.2"
tryer "^1.0.0"
big.js@^3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978"
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
+ integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==
binary-extensions@^1.0.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
+ integrity sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=
binaryextensions@2:
version "2.1.1"
resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935"
-
-bitsyntax@~0.0.4:
- version "0.0.4"
- resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82"
- dependencies:
- buffer-more-ints "0.0.2"
-
-bl@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398"
- dependencies:
- readable-stream "~2.0.5"
-
-blackst0ne-mermaid@^7.1.0-fixed:
- version "7.1.0-fixed"
- resolved "https://registry.yarnpkg.com/blackst0ne-mermaid/-/blackst0ne-mermaid-7.1.0-fixed.tgz#3707b3a113d78610e3068e18a588f46b4688de49"
- dependencies:
- d3 "3.5.17"
- dagre-d3-renderer "^0.4.24"
- dagre-layout "^0.8.0"
- he "^1.1.1"
- lodash "^4.17.4"
- moment "^2.18.1"
+ integrity sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==
blob@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921"
+ integrity sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=
-bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.4.6, bluebird@^3.5.1:
+bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
+ integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
+ integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
body-parser@1.18.2, body-parser@^1.16.1:
version "1.18.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454"
+ integrity sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=
dependencies:
bytes "3.0.0"
content-type "~1.0.4"
@@ -1315,6 +1380,7 @@ body-parser@1.18.2, body-parser@^1.16.1:
bonjour@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5"
+ integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU=
dependencies:
array-flatten "^2.1.0"
deep-equal "^1.0.1"
@@ -1323,27 +1389,10 @@ bonjour@^3.5.0:
multicast-dns "^6.0.1"
multicast-dns-service-types "^1.1.0"
-boom@2.x.x:
- version "2.10.1"
- resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
- dependencies:
- hoek "2.x.x"
-
-boom@4.x.x:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
- dependencies:
- hoek "4.x.x"
-
-boom@5.x.x:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
- dependencies:
- hoek "4.x.x"
-
bootstrap-vue@^2.0.0-rc.11:
version "2.0.0-rc.11"
resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.0.0-rc.11.tgz#47aaa6d2a8d390477de75e636d8ea652b1d03f59"
+ integrity sha512-LxR+oL8yKr1DVoWUWTX+XhiT0xaTMH6142u2VSFDm4tewTH8HIrzN2YIl7HLZrw2DIuE9bRMIdWJqqn3aQe7Hw==
dependencies:
bootstrap "^4.1.1"
lodash.get "^4.4.2"
@@ -1352,17 +1401,15 @@ bootstrap-vue@^2.0.0-rc.11:
popper.js "^1.12.9"
vue-functional-data-merge "^2.0.5"
-bootstrap@^4.1.1:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.2.tgz#aee2a93472e61c471fc79fb475531dcbc87de326"
-
-bootstrap@~4.1.1:
+bootstrap@4.1.1, bootstrap@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.1.tgz#3aec85000fa619085da8d2e4983dfd67cf2114cb"
+ integrity sha512-SpiDSOcbg4J/PjVSt4ny5eY6j74VbVSjROY4Fb/WIUXBV9cnb5luyR4KnPvNoXuGnBK1T+nJIWqRsvU3yP8Mcg==
boxen@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
+ integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==
dependencies:
ansi-align "^2.0.0"
camelcase "^4.0.0"
@@ -1375,6 +1422,7 @@ boxen@^1.2.1:
brace-expansion@^1.1.7, brace-expansion@^1.1.8:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
@@ -1382,12 +1430,14 @@ brace-expansion@^1.1.7, brace-expansion@^1.1.8:
braces@^0.1.2:
version "0.1.5"
resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6"
+ integrity sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=
dependencies:
expand-range "^0.1.0"
braces@^2.3.0, braces@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb"
+ integrity sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==
dependencies:
arr-flatten "^1.1.0"
array-unique "^0.3.2"
@@ -1405,10 +1455,12 @@ braces@^2.3.0, braces@^2.3.1:
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+ integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.1.1"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f"
+ integrity sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==
dependencies:
buffer-xor "^1.0.3"
cipher-base "^1.0.0"
@@ -1420,6 +1472,7 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4:
browserify-cipher@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a"
+ integrity sha1-mYgkSHS/XtTijalWZtzWasj8Njo=
dependencies:
browserify-aes "^1.0.4"
browserify-des "^1.0.0"
@@ -1428,6 +1481,7 @@ browserify-cipher@^1.0.0:
browserify-des@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd"
+ integrity sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=
dependencies:
cipher-base "^1.0.1"
des.js "^1.0.0"
@@ -1436,6 +1490,7 @@ browserify-des@^1.0.0:
browserify-rsa@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
+ integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
dependencies:
bn.js "^4.1.0"
randombytes "^2.0.1"
@@ -1443,6 +1498,7 @@ browserify-rsa@^4.0.0:
browserify-sign@^4.0.0:
version "4.0.4"
resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
+ integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=
dependencies:
bn.js "^4.1.1"
browserify-rsa "^4.0.0"
@@ -1455,64 +1511,67 @@ browserify-sign@^4.0.0:
browserify-zlib@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
+ integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
dependencies:
pako "~1.0.5"
+browserslist@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.1.1.tgz#328eb4ff1215b12df6589e9ab82f8adaa4fc8cd6"
+ integrity sha512-VBorw+tgpOtZ1BYhrVSVTzTt/3+vSE3eFUh0N2GCFK1HffceOaf32YS/bs6WiFhjDAblAFrx85jMy3BG9fBK2Q==
+ dependencies:
+ caniuse-lite "^1.0.30000884"
+ electron-to-chromium "^1.3.62"
+ node-releases "^1.0.0-alpha.11"
+
buffer-from@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
+ integrity sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==
buffer-indexof@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.0.tgz#f54f647c4f4e25228baa656a2e57e43d5f270982"
-
-buffer-more-ints@0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c"
+ integrity sha1-9U9kfE9OJSKLqmVqLlfkPV8nCYI=
buffer-xor@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+ integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
buffer@^4.3.0:
version "4.9.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
+ integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
isarray "^1.0.0"
-buildmail@4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/buildmail/-/buildmail-4.0.1.tgz#877f7738b78729871c9a105e3b837d2be11a7a72"
- dependencies:
- addressparser "1.0.1"
- libbase64 "0.1.0"
- libmime "3.0.0"
- libqp "1.1.0"
- nodemailer-fetch "1.6.0"
- nodemailer-shared "1.1.0"
- punycode "1.4.1"
-
builtin-modules@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+ integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
+ integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
bytes@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.5.0.tgz#4c9423ea2d252c270c41b2bdefeff9bb6b62c06a"
+ integrity sha1-TJQj6i0lLCcMQbK97+/5u2tiwGo=
bytes@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+ integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
-cacache@^10.0.1, cacache@^10.0.4:
+cacache@^10.0.4:
version "10.0.4"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460"
+ integrity sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==
dependencies:
bluebird "^3.5.1"
chownr "^1.0.1"
@@ -1528,9 +1587,30 @@ cacache@^10.0.1, cacache@^10.0.4:
unique-filename "^1.1.0"
y18n "^4.0.0"
+cacache@^11.2.0:
+ version "11.2.0"
+ resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.2.0.tgz#617bdc0b02844af56310e411c0878941d5739965"
+ integrity sha512-IFWl6lfK6wSeYCHUXh+N1lY72UDrpyrYQJNIVQf48paDuWbv5RbAtJYf/4gUQFObTCHZwdZ5sI8Iw7nqwP6nlQ==
+ dependencies:
+ bluebird "^3.5.1"
+ chownr "^1.0.1"
+ figgy-pudding "^3.1.0"
+ glob "^7.1.2"
+ graceful-fs "^4.1.11"
+ lru-cache "^4.1.3"
+ mississippi "^3.0.0"
+ mkdirp "^0.5.1"
+ move-concurrently "^1.0.1"
+ promise-inflight "^1.0.1"
+ rimraf "^2.6.2"
+ ssri "^6.0.0"
+ unique-filename "^1.1.0"
+ y18n "^4.0.0"
+
cache-base@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+ integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
dependencies:
collection-visit "^1.0.0"
component-emitter "^1.2.1"
@@ -1545,6 +1625,7 @@ cache-base@^1.0.1:
cache-loader@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-1.2.2.tgz#6d5c38ded959a09cc5d58190ab5af6f73bd353f5"
+ integrity sha512-rsGh4SIYyB9glU+d0OcHwiXHXBoUgDhHZaQ1KAbiXqfz1CDPxtTboh1gPbJ0q2qdO8a9lfcjgC5CJ2Ms32y5bw==
dependencies:
loader-utils "^1.1.0"
mkdirp "^0.5.1"
@@ -1554,6 +1635,7 @@ cache-loader@^1.2.2:
cacheable-request@^2.1.1:
version "2.1.4"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d"
+ integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=
dependencies:
clone-response "1.0.2"
get-stream "3.0.0"
@@ -1566,58 +1648,39 @@ cacheable-request@^2.1.1:
caller-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
+ integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=
dependencies:
callsites "^0.2.0"
callsite@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
+ integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA=
callsites@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
-
-camelcase-keys@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
- dependencies:
- camelcase "^2.0.0"
- map-obj "^1.0.0"
-
-camelcase@^1.0.2:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
-
-camelcase@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
+ integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=
camelcase@^4.0.0, camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
+ integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
+
+caniuse-lite@^1.0.30000884:
+ version "1.0.30000888"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000888.tgz#22edb50d91dd70612b5898e3b36f460600c6492f"
+ integrity sha512-vftg+5p/lPsQGpnhSo/yBuYL36ai/cyjLvU3dOPJY1kkKrekLWIy8SLm+wzjX0hpCUdFTasC4/ZT7uqw4rKOnQ==
capture-stack-trace@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
+ integrity sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=
-caseless@~0.11.0:
- version "0.11.0"
- resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
-
-caseless@~0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
-
-center-align@^0.1.1:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
- dependencies:
- align-text "^0.1.3"
- lazy-cache "^1.0.3"
-
-chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
+chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
dependencies:
ansi-styles "^2.2.1"
escape-string-regexp "^1.0.2"
@@ -1625,9 +1688,10 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
-chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1:
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
+ integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
@@ -1636,44 +1700,32 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1:
chardet@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+ integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=
chardet@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029"
+ integrity sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==
"charenc@>= 0.0.1":
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
+ integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
chart.js@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-1.0.2.tgz#ad57d2229cfd8ccf5955147e8121b4911e69dfe7"
+ integrity sha1-rVfSIpz9jM9ZVRR+gSG0kR5p3+c=
check-types@^7.3.0:
version "7.3.0"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d"
+ integrity sha1-Ro9XGkQ1wkJI9f0MsOjYfDw0Hn0=
-chokidar@^2.0.0, chokidar@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7"
- dependencies:
- anymatch "^2.0.0"
- async-each "^1.0.0"
- braces "^2.3.0"
- glob-parent "^3.1.0"
- inherits "^2.0.1"
- is-binary-path "^1.0.0"
- is-glob "^4.0.0"
- normalize-path "^2.1.1"
- path-is-absolute "^1.0.0"
- readdirp "^2.0.0"
- upath "^1.0.0"
- optionalDependencies:
- fsevents "^1.0.0"
-
-chokidar@^2.0.3:
+chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26"
+ integrity sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==
dependencies:
anymatch "^2.0.0"
async-each "^1.0.0"
@@ -1693,16 +1745,19 @@ chokidar@^2.0.3:
chownr@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
+ integrity sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=
chrome-trace-event@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48"
+ integrity sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==
dependencies:
tslib "^1.9.0"
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
+ integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
@@ -1710,14 +1765,17 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
circular-json@^0.3.1:
version "0.3.3"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
+ integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==
-circular-json@^0.5.4:
+circular-json@^0.5.5:
version "0.5.5"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.5.tgz#64182ef359042d37cd8e767fc9de878b1e9447d3"
+ integrity sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==
class-utils@^0.3.5:
version "0.3.6"
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+ integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
dependencies:
arr-union "^3.1.0"
define-property "^0.2.5"
@@ -1727,40 +1785,38 @@ class-utils@^0.3.5:
classlist-polyfill@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e"
+ integrity sha1-k1vC39lFiodrJ5YXUUY4vKqWSi4=
cli-boxes@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
+ integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM=
cli-cursor@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+ integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
dependencies:
restore-cursor "^2.0.0"
cli-width@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
+ integrity sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=
clipboard@^1.5.5, clipboard@^1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-1.7.1.tgz#360d6d6946e99a7a1fef395e42ba92b5e9b5a16b"
+ integrity sha1-Ng1taUbpmnof7zleQrqStem1oWs=
dependencies:
good-listener "^1.2.2"
select "^1.1.2"
tiny-emitter "^2.0.0"
-cliui@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
- dependencies:
- center-align "^0.1.1"
- right-align "^0.1.1"
- wordwrap "0.0.2"
-
cliui@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.0.0.tgz#743d4650e05f36d1ed2575b59638d87322bfbbcc"
+ integrity sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==
dependencies:
string-width "^2.1.1"
strip-ansi "^4.0.0"
@@ -1769,28 +1825,29 @@ cliui@^4.0.0:
clone-response@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
+ integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
dependencies:
mimic-response "^1.0.0"
-co@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
-
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+ integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codesandbox-api@^0.0.18:
version "0.0.18"
resolved "https://registry.yarnpkg.com/codesandbox-api/-/codesandbox-api-0.0.18.tgz#56b96b37533f80d20c21861e5e477d3557e613ca"
+ integrity sha512-DHLR8QQpMplNDF9GDbV8EevwmF9mlMBQwiWB8bZBnP2NQQbklthqjpBwNjah8qlDgfD7vQNNcwT8uIZ24WZb7Q==
codesandbox-import-util-types@^1.2.11:
version "1.2.11"
resolved "https://registry.yarnpkg.com/codesandbox-import-util-types/-/codesandbox-import-util-types-1.2.11.tgz#68e812f21d6b309e9a52eec5cf027c3e63b4c703"
+ integrity sha512-n1PC/OQ0tcD9o6N5TStBB/A7tKOggUjuhnNxUU5GnVol8vmKMMLvmC6tK+8iDovQb2X2+xoDCBnl5BBgZ5OcIQ==
codesandbox-import-utils@^1.2.3:
version "1.2.11"
resolved "https://registry.yarnpkg.com/codesandbox-import-utils/-/codesandbox-import-utils-1.2.11.tgz#b88423a4a7c785175c784c84e87f5950820280e1"
+ integrity sha512-KPuf7tR/SMPSRfqjWbTrYvIaW6Yt9Ajt/1FB64RsOv4BLjBNo6CwLCCPoRHYcrAKSafpWkghTZ2Bffyz7EX7AA==
dependencies:
codesandbox-import-util-types "^1.2.11"
istextorbinary "^2.2.1"
@@ -1799,79 +1856,103 @@ codesandbox-import-utils@^1.2.3:
collection-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+ integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
dependencies:
map-visit "^1.0.0"
object-visit "^1.0.0"
color-convert@^1.9.0:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
- color-name "^1.1.1"
+ color-name "1.1.3"
-color-name@^1.1.1:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d"
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
colors@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+ integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
combine-lists@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dacd1d2cb7f6"
+ integrity sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=
dependencies:
lodash "^4.5.0"
-combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
- dependencies:
- delayed-stream "~1.0.0"
+commander@2, commander@^2.18.0:
+ version "2.18.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
+ integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==
-commander@2, commander@^2.13.0, commander@^2.15.1, commander@^2.9.0:
- version "2.15.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
+commander@^2.19.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
+ integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
commander@~2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
+ integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
+
+commander@~2.17.1:
+ version "2.17.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
+ integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+ integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
+
+compare-versions@^3.2.1:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.4.0.tgz#e0747df5c9cb7f054d6d3dc3e1dbc444f9e92b26"
+ integrity sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==
component-bind@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
+ integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=
component-emitter@1.2.1, component-emitter@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
+ integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
component-inherit@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
+ integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=
compressible@~2.0.10:
version "2.0.11"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.11.tgz#16718a75de283ed8e604041625a2064586797d8a"
+ integrity sha1-FnGKdd4oPtjmBAQWJaIGRYZ5fYo=
dependencies:
mime-db ">= 1.29.0 < 2"
-compression-webpack-plugin@^1.1.11:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-1.1.11.tgz#8384c7a6ead1d2e2efb190bdfcdcf35878ed8266"
+compression-webpack-plugin@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-2.0.0.tgz#46476350c1eb27f783dccc79ac2f709baa2cffbc"
+ integrity sha512-bDgd7oTUZC8EkRx8j0sjyCfeiO+e5sFcfgaFcjVhfQf5lLya7oY2BczxcJ7IUuVjz5m6fy8IECFmVFew3xLk8Q==
dependencies:
- cacache "^10.0.1"
- find-cache-dir "^1.0.0"
+ cacache "^11.2.0"
+ find-cache-dir "^2.0.0"
neo-async "^2.5.0"
+ schema-utils "^1.0.0"
serialize-javascript "^1.4.0"
webpack-sources "^1.0.1"
compression@^1.5.2:
version "1.7.0"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.0.tgz#030c9f198f1643a057d776a738e922da4373012d"
+ integrity sha1-AwyfGY8WQ6BX13anOOki2kNzAS0=
dependencies:
accepts "~1.3.3"
bytes "2.5.0"
@@ -1884,19 +1965,30 @@ compression@^1.5.2:
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
-concat-stream@^1.5.0, concat-stream@^1.6.0:
+concat-stream@^1.5.0:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+ integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
+config-chain@~1.1.5:
+ version "1.1.12"
+ resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
+ integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
+ dependencies:
+ ini "^1.3.4"
+ proto-list "~1.2.1"
+
configstore@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90"
+ integrity sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==
dependencies:
dot-prop "^4.1.0"
graceful-fs "^4.1.2"
@@ -1908,10 +2000,12 @@ configstore@^3.0.0:
connect-history-api-fallback@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169"
+ integrity sha1-5R0X+PDvDbkKZP20feMFFVbp8Wk=
connect@^3.6.0:
version "3.6.6"
resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.6.tgz#09eff6c55af7236e137135a72574858b6786f524"
+ integrity sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=
dependencies:
debug "2.6.9"
finalhandler "1.1.0"
@@ -1921,50 +2015,63 @@ connect@^3.6.0:
console-browserify@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
+ integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=
dependencies:
date-now "^0.1.4"
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+ integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
consolidate@^0.15.1:
version "0.15.1"
resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7"
+ integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==
dependencies:
bluebird "^3.1.1"
constants-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
+ integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
contains-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
+ integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=
content-disposition@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
+ integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ=
content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
+ integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
-convert-source-map@^1.5.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
+convert-source-map@^1.1.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
+ integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
+ dependencies:
+ safe-buffer "~5.1.1"
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+ integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
+ integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=
copy-concurrently@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
+ integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==
dependencies:
aproba "^1.1.1"
fs-write-stream-atomic "^1.0.8"
@@ -1976,22 +2083,34 @@ copy-concurrently@^1.0.0:
copy-descriptor@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+ integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
-core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0:
- version "2.5.3"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
+copy-to-clipboard@^3.0.8:
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz#f4e82f4a8830dce4666b7eb8ded0c9bcc313aba9"
+ integrity sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==
+ dependencies:
+ toggle-selection "^1.0.3"
+
+core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1:
+ version "2.5.7"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
+ integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==
core-js@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65"
+ integrity sha1-+rg/uwstjchfpjbEudNMdUIMbWU=
-core-util-is@1.0.2, core-util-is@~1.0.0:
+core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+ integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
create-ecdh@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d"
+ integrity sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=
dependencies:
bn.js "^4.1.0"
elliptic "^6.0.0"
@@ -1999,12 +2118,14 @@ create-ecdh@^4.0.0:
create-error-class@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
+ integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=
dependencies:
capture-stack-trace "^1.0.0"
create-hash@^1.1.0, create-hash@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
+ integrity sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=
dependencies:
cipher-base "^1.0.1"
inherits "^2.0.1"
@@ -2014,6 +2135,7 @@ create-hash@^1.1.0, create-hash@^1.1.2:
create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
version "1.1.6"
resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06"
+ integrity sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=
dependencies:
cipher-base "^1.0.3"
create-hash "^1.1.0"
@@ -2025,20 +2147,23 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
cropper@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/cropper/-/cropper-2.3.0.tgz#607461d4e7aa7a7fe15a26834b14b7f0c2801562"
+ integrity sha1-YHRh1Oeqen/hWiaDSxS38MKAFWI=
dependencies:
jquery ">= 1.9.1"
-cross-spawn@^5.0.1, cross-spawn@^5.1.0:
+cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
+ integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
dependencies:
lru-cache "^4.0.1"
shebang-command "^1.2.0"
which "^1.2.9"
-cross-spawn@^6.0.5:
+cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+ integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
@@ -2049,22 +2174,12 @@ cross-spawn@^6.0.5:
"crypt@>= 0.0.1":
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
-
-cryptiles@2.x.x:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
- dependencies:
- boom "2.x.x"
-
-cryptiles@3.x.x:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
- dependencies:
- boom "5.x.x"
+ integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
+ integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
dependencies:
browserify-cipher "^1.0.0"
browserify-sign "^4.0.0"
@@ -2081,10 +2196,12 @@ crypto-browserify@^3.11.0:
crypto-random-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
+ integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
css-loader@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.0.tgz#9f46aaa5ca41dbe31860e3b62b8e23c42916bf56"
+ integrity sha512-tMXlTYf3mIMt3b0dDCOQFJiVvxbocJ5Ho577WiGPYPZcqVEO218L2iU22pDXzkTZCLDE+9AmGSUkWxeh/nZReA==
dependencies:
babel-code-frame "^6.26.0"
css-selector-tokenizer "^0.7.0"
@@ -2099,9 +2216,15 @@ css-loader@^1.0.0:
postcss-value-parser "^3.3.0"
source-list-map "^2.0.0"
+css-selector-parser@^1.3:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.3.0.tgz#5f1ad43e2d8eefbfdc304fcd39a521664943e3eb"
+ integrity sha1-XxrUPi2O77/cME/NOaUhZklD4+s=
+
css-selector-tokenizer@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
+ integrity sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=
dependencies:
cssesc "^0.1.0"
fastparse "^1.1.1"
@@ -2110,32 +2233,39 @@ css-selector-tokenizer@^0.7.0:
cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
+ integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
+ integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
dependencies:
array-find-index "^1.0.1"
custom-event@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
+ integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=
cyclist@~0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
+ integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=
d3-array@1, d3-array@1.2.1, d3-array@^1.2.0, d3-array@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc"
+ integrity sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw==
d3-axis@1.0.8, d3-axis@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.8.tgz#31a705a0b535e65759de14173a31933137f18efa"
+ integrity sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo=
d3-brush@1.0.4, d3-brush@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.0.4.tgz#00c2f238019f24f6c0a194a26d41a1530ffe7bc4"
+ integrity sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=
dependencies:
d3-dispatch "1"
d3-drag "1"
@@ -2146,6 +2276,7 @@ d3-brush@1.0.4, d3-brush@^1.0.4:
d3-chord@1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.4.tgz#7dec4f0ba886f713fe111c45f763414f6f74ca2c"
+ integrity sha1-fexPC6iG9xP+ERxF92NBT290yiw=
dependencies:
d3-array "1"
d3-path "1"
@@ -2153,18 +2284,22 @@ d3-chord@1.0.4:
d3-collection@1, d3-collection@1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2"
+ integrity sha1-NC39EoN8kJdPM/HMCnha6lcNzcI=
d3-color@1, d3-color@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.3.tgz#bc7643fca8e53a8347e2fbdaffa236796b58509b"
+ integrity sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs=
d3-dispatch@1, d3-dispatch@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.3.tgz#46e1491eaa9b58c358fce5be4e8bed626e7871f8"
+ integrity sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=
d3-drag@1, d3-drag@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.1.tgz#df8dd4c502fb490fc7462046a8ad98a5c479282d"
+ integrity sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==
dependencies:
d3-dispatch "1"
d3-selection "1"
@@ -2172,6 +2307,7 @@ d3-drag@1, d3-drag@1.2.1:
d3-dsv@1, d3-dsv@1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.8.tgz#907e240d57b386618dc56468bacfe76bf19764ae"
+ integrity sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==
dependencies:
commander "2"
iconv-lite "0.4"
@@ -2180,10 +2316,12 @@ d3-dsv@1, d3-dsv@1.0.8:
d3-ease@1, d3-ease@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.3.tgz#68bfbc349338a380c44d8acc4fbc3304aa2d8c0e"
+ integrity sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=
d3-force@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.1.0.tgz#cebf3c694f1078fcc3d4daf8e567b2fbd70d4ea3"
+ integrity sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==
dependencies:
d3-collection "1"
d3-dispatch "1"
@@ -2193,46 +2331,61 @@ d3-force@1.1.0:
d3-format@1, d3-format@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.1.tgz#4e19ecdb081a341dafaf5f555ee956bcfdbf167f"
+ integrity sha512-U4zRVLDXW61bmqoo+OJ/V687e1T5nVd3TAKAJKgtpZ/P1JsMgyod0y9br+mlQOryTAACdiXI3wCjuERHFNp91w==
+
+d3-format@1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.2.tgz#1a39c479c8a57fe5051b2e67a3bee27061a74e7a"
+ integrity sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw==
d3-geo@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356"
+ integrity sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==
dependencies:
d3-array "1"
d3-hierarchy@1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz#a1c845c42f84a206bcf1c01c01098ea4ddaa7a26"
+ integrity sha1-ochFxC+Eoga88cAcAQmOpN2qeiY=
d3-interpolate@1, d3-interpolate@1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6"
+ integrity sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==
dependencies:
d3-color "1"
d3-path@1, d3-path@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.5.tgz#241eb1849bd9e9e8021c0d0a799f8a0e8e441764"
+ integrity sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q=
d3-polygon@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.3.tgz#16888e9026460933f2b179652ad378224d382c62"
+ integrity sha1-FoiOkCZGCTPysXllKtN4Ik04LGI=
d3-quadtree@1, d3-quadtree@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.3.tgz#ac7987e3e23fe805a990f28e1b50d38fcb822438"
+ integrity sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg=
d3-queue@3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/d3-queue/-/d3-queue-3.0.7.tgz#c93a2e54b417c0959129d7d73f6cf7d4292e7618"
+ integrity sha1-yTouVLQXwJWRKdfXP2z31Ckudhg=
d3-random@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.0.tgz#6642e506c6fa3a648595d2b2469788a8d12529d3"
+ integrity sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM=
d3-request@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/d3-request/-/d3-request-1.0.6.tgz#a1044a9ef4ec28c824171c9379fae6d79474b19f"
+ integrity sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==
dependencies:
d3-collection "1"
d3-dispatch "1"
@@ -2242,6 +2395,7 @@ d3-request@1.0.6:
d3-scale@1.0.7, d3-scale@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.7.tgz#fa90324b3ea8a776422bd0472afab0b252a0945d"
+ integrity sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==
dependencies:
d3-array "^1.2.0"
d3-collection "1"
@@ -2254,30 +2408,41 @@ d3-scale@1.0.7, d3-scale@^1.0.7:
d3-selection@1, d3-selection@1.2.0, d3-selection@^1.1.0, d3-selection@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.2.0.tgz#1b8ec1c7cedadfb691f2ba20a4a3cfbeb71bbc88"
+ integrity sha512-xW2Pfcdzh1gOaoI+LGpPsLR2VpBQxuFoxvrvguK8ZmrJbPIVvfNG6pU6GNfK41D6Qz15sj61sbW/AFYuukwaLQ==
+
+d3-selection@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.3.0.tgz#d53772382d3dc4f7507bfb28bcd2d6aed2a0ad6d"
+ integrity sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA==
d3-shape@1.2.0, d3-shape@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777"
+ integrity sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=
dependencies:
d3-path "1"
d3-time-format@2, d3-time-format@2.1.1, d3-time-format@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31"
+ integrity sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==
dependencies:
d3-time "1"
d3-time@1, d3-time@1.0.8, d3-time@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84"
+ integrity sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ==
d3-timer@1, d3-timer@1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.7.tgz#df9650ca587f6c96607ff4e60cc38229e8dd8531"
+ integrity sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA==
d3-transition@1, d3-transition@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.1.1.tgz#d8ef89c3b848735b060e54a39b32aaebaa421039"
+ integrity sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==
dependencies:
d3-color "1"
d3-dispatch "1"
@@ -2289,10 +2454,12 @@ d3-transition@1, d3-transition@1.1.1:
d3-voronoi@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c"
+ integrity sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=
d3-zoom@1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.7.1.tgz#02f43b3c3e2db54f364582d7e4a236ccc5506b63"
+ integrity sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==
dependencies:
d3-dispatch "1"
d3-drag "1"
@@ -2300,13 +2467,10 @@ d3-zoom@1.7.1:
d3-selection "1"
d3-transition "1"
-d3@3.5.17:
- version "3.5.17"
- resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
-
d3@4.12.2:
version "4.12.2"
resolved "https://registry.yarnpkg.com/d3/-/d3-4.12.2.tgz#12f775564c6a9de229f63db03446e2cb7bb56c8f"
+ integrity sha512-aKAlpgTmpuGeEpezB+GvPpX1x+gCMs/PHpuse6sCpkgw4Un3ZeqUobIc87eIy9adcl+wxPAnEyKyO5oulH3MOw==
dependencies:
d3-array "1.2.1"
d3-axis "1.0.8"
@@ -2339,139 +2503,193 @@ d3@4.12.2:
d3-voronoi "1.1.2"
d3-zoom "1.7.1"
-dagre-d3-renderer@^0.4.24:
- version "0.4.24"
- resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.4.24.tgz#b36ce2fe4ea20de43e7698627c6ede2a9f15ec45"
+d3@^4.13.0:
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/d3/-/d3-4.13.0.tgz#ab236ff8cf0cfc27a81e69bf2fb7518bc9b4f33d"
+ integrity sha512-l8c4+0SldjVKLaE2WG++EQlqD7mh/dmQjvi2L2lKPadAVC+TbJC4ci7Uk9bRi+To0+ansgsS0iWfPjD7DBy+FQ==
dependencies:
- d3 "3.5.17"
- dagre-layout "^0.8.0"
- graphlib "^2.1.1"
- lodash "^4.17.4"
+ d3-array "1.2.1"
+ d3-axis "1.0.8"
+ d3-brush "1.0.4"
+ d3-chord "1.0.4"
+ d3-collection "1.0.4"
+ d3-color "1.0.3"
+ d3-dispatch "1.0.3"
+ d3-drag "1.2.1"
+ d3-dsv "1.0.8"
+ d3-ease "1.0.3"
+ d3-force "1.1.0"
+ d3-format "1.2.2"
+ d3-geo "1.9.1"
+ d3-hierarchy "1.1.5"
+ d3-interpolate "1.1.6"
+ d3-path "1.0.5"
+ d3-polygon "1.0.3"
+ d3-quadtree "1.0.3"
+ d3-queue "3.0.7"
+ d3-random "1.1.0"
+ d3-request "1.0.6"
+ d3-scale "1.0.7"
+ d3-selection "1.3.0"
+ d3-shape "1.2.0"
+ d3-time "1.0.8"
+ d3-time-format "2.1.1"
+ d3-timer "1.0.7"
+ d3-transition "1.1.1"
+ d3-voronoi "1.1.2"
+ d3-zoom "1.7.1"
-dagre-layout@^0.8.0:
- version "0.8.0"
- resolved "https://registry.yarnpkg.com/dagre-layout/-/dagre-layout-0.8.0.tgz#7147b6afb655602f855158dfea171db9aa98d4ff"
+dagre-d3-renderer@^0.5.8:
+ version "0.5.8"
+ resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.5.8.tgz#aa071bb71d3c4d67426925906f3f6ddead49c1a3"
+ integrity sha512-XH2a86isUHRxzIYbjQVEuZtJnWEufb64H5DuXIUmn8esuB40jgLEbUUclulWOW62/ZoXlj2ZDyL8SJ+YRxs+jQ==
dependencies:
- graphlib "^2.1.1"
- lodash "^4.17.4"
+ dagre-layout "^0.8.8"
+ lodash "^4.17.5"
-dashdash@^1.12.0:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+dagre-layout@^0.8.8:
+ version "0.8.8"
+ resolved "https://registry.yarnpkg.com/dagre-layout/-/dagre-layout-0.8.8.tgz#9b6792f24229f402441c14162c1049e3f261f6d9"
+ integrity sha512-ZNV15T9za7X+fV8Z07IZquUKugCxm5owoiPPxfEx6OJRD331nkiIaF3vSt0JEY5FkrY0KfRQxcpQ3SpXB7pLPQ==
dependencies:
- assert-plus "^1.0.0"
-
-data-uri-to-buffer@1:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
+ graphlibrary "^2.2.0"
+ lodash "^4.17.5"
date-format@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8"
+ integrity sha1-YV6CjiM90aubua4JUODOzPpuytg=
date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
+ integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
dateformat@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
+ integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
+ integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
-debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9, debug@~2.6.4, debug@~2.6.6:
+debug@2.6.8:
+ version "2.6.8"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
+ integrity sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=
+ dependencies:
+ ms "2.0.0"
+
+debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
-debug@2.6.8:
- version "2.6.8"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
+debug@^3.1.0:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.5.tgz#c2418fbfd7a29f4d4f70ff4cea604d4b64c46407"
+ integrity sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==
dependencies:
- ms "2.0.0"
+ ms "^2.1.1"
-debug@3.1.0, debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
+debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+ integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
-decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+decamelize@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7"
+ integrity sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==
+ dependencies:
+ xregexp "4.0.0"
deckar01-task_list@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/deckar01-task_list/-/deckar01-task_list-2.0.0.tgz#7f7a595430d21b3036ed5dfbf97d6b65de18e2c9"
+ integrity sha1-f3pZVDDSGzA27V37+X1rZd4Y4sk=
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+ integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
+ integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
dependencies:
mimic-response "^1.0.0"
deep-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
+ integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
deep-extend@~0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
+ integrity sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=
deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+ integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
-default-require-extensions@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
+default-gateway@^2.6.0:
+ version "2.7.2"
+ resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f"
+ integrity sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==
dependencies:
- strip-bom "^2.0.0"
+ execa "^0.10.0"
+ ip-regex "^2.1.0"
+
+default-require-extensions@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7"
+ integrity sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=
+ dependencies:
+ strip-bom "^3.0.0"
define-properties@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+ integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
dependencies:
- foreach "^2.0.5"
- object-keys "^1.0.8"
+ object-keys "^1.0.12"
define-property@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+ integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
dependencies:
is-descriptor "^0.1.0"
define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+ integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
dependencies:
is-descriptor "^1.0.0"
define-property@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+ integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
dependencies:
is-descriptor "^1.0.2"
isobject "^3.0.1"
-degenerator@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095"
- dependencies:
- ast-types "0.x.x"
- escodegen "1.x.x"
- esprima "3.x.x"
-
del@^2.0.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
+ integrity sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=
dependencies:
globby "^5.0.0"
is-path-cwd "^1.0.0"
@@ -2484,6 +2702,7 @@ del@^2.0.2:
del@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
+ integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
dependencies:
globby "^6.1.0"
is-path-cwd "^1.0.0"
@@ -2492,29 +2711,30 @@ del@^3.0.0:
pify "^3.0.0"
rimraf "^2.2.8"
-delayed-stream@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
-
delegate@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.1.2.tgz#1e1bc6f5cadda6cb6cbf7e6d05d0bcdd5712aebe"
+ integrity sha1-HhvG9crdpstsv35tBdC83VcSrr4=
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+ integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
-depd@1.1.1, depd@~1.1.1:
+depd@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
+ integrity sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=
-depd@~1.1.2:
+depd@~1.1.1, depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+ integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
des.js@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
+ integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=
dependencies:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
@@ -2522,32 +2742,32 @@ des.js@^1.0.0:
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
-
-detect-indent@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
- dependencies:
- repeating "^2.0.0"
+ integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
detect-libc@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+ integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
detect-node@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127"
+ integrity sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=
di@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
+ integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=
diff@^3.2.0, diff@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
+ integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
diffie-hellman@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"
+ integrity sha1-tYNXOScM/ias9jIJn97SoH8gnl4=
dependencies:
bn.js "^4.1.0"
miller-rabin "^4.0.0"
@@ -2556,10 +2776,12 @@ diffie-hellman@^5.0.0:
dns-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
+ integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0=
dns-packet@^1.0.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.2.2.tgz#a8a26bec7646438963fc86e06f8f8b16d6c8bf7a"
+ integrity sha512-kN+DjfGF7dJGUL7nWRktL9Z18t1rWP3aQlyZdY8XlpvU3Nc6GeFTQApftcjtWKxAZfiggZSGrCEoszNgvnpwDg==
dependencies:
ip "^1.1.0"
safe-buffer "^5.0.1"
@@ -2567,29 +2789,34 @@ dns-packet@^1.0.1:
dns-txt@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6"
+ integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=
dependencies:
buffer-indexof "^1.0.0"
doctrine@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
+ integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=
dependencies:
esutils "^2.0.2"
isarray "^1.0.0"
-doctrine@^2.0.2:
+doctrine@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+ integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
dependencies:
esutils "^2.0.2"
document-register-element@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/document-register-element/-/document-register-element-1.3.0.tgz#fb3babb523c74662be47be19c6bc33e71990d940"
+ integrity sha1-+zurtSPHRmK+R74Zxrwz5xmQ2UA=
dom-serialize@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
+ integrity sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=
dependencies:
custom-event "~1.0.0"
ent "~2.2.0"
@@ -2599,6 +2826,7 @@ dom-serialize@^2.2.0:
dom-serializer@0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
+ integrity sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=
dependencies:
domelementtype "~1.1.1"
entities "~1.1.1"
@@ -2606,24 +2834,29 @@ dom-serializer@0:
domain-browser@^1.1.1:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
+ integrity sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=
domelementtype@1, domelementtype@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
+ integrity sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=
domelementtype@~1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+ integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=
domhandler@^2.3.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259"
+ integrity sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=
dependencies:
domelementtype "1"
domutils@^1.5.1:
version "1.6.2"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff"
+ integrity sha1-GVjMC0yUJuntNn+xyOhUiRsPo/8=
dependencies:
dom-serializer "0"
domelementtype "1"
@@ -2631,55 +2864,71 @@ domutils@^1.5.1:
dot-prop@^4.1.0, dot-prop@^4.1.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
+ integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
dependencies:
is-obj "^1.0.0"
-double-ended-queue@^2.1.0-0:
- version "2.1.0-0"
- resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
-
dropzone@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3"
+ integrity sha1-++esu5kY4HBkiQcu9mPv/u+KefM=
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
+ integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
duplexer@^0.1.1, duplexer@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
+ integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
duplexify@^3.4.2, duplexify@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.3.tgz#8b5818800df92fd0125b27ab896491912858243e"
+ integrity sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==
dependencies:
end-of-stream "^1.0.0"
inherits "^2.0.1"
readable-stream "^2.0.0"
stream-shift "^1.0.0"
-ecc-jsbn@~0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
- dependencies:
- jsbn "~0.1.0"
-
editions@^1.3.3:
version "1.3.4"
resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b"
+ integrity sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==
+
+editorconfig@^0.15.0:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.2.tgz#047be983abb9ab3c2eefe5199cb2b7c5689f0702"
+ integrity sha512-GWjSI19PVJAM9IZRGOS+YKI8LN+/sjkSjNyvxL5ucqP9/IqtYNXBaQ/6c/hkPNYQHyOHra2KoXZI/JVpuqwmcQ==
+ dependencies:
+ "@types/node" "^10.11.7"
+ "@types/semver" "^5.5.0"
+ commander "^2.19.0"
+ lru-cache "^4.1.3"
+ semver "^5.6.0"
+ sigmund "^1.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+ integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
-ejs@^2.5.7:
- version "2.5.9"
- resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
+ejs@^2.6.1:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
+ integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
+
+electron-to-chromium@^1.3.62:
+ version "1.3.73"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.73.tgz#aa67787067d58cc3920089368b3b8d6fe0fc12f6"
+ integrity sha512-6PIg7v9zRoVGh6EheRF8h6Plti+3Yo/qtHobS4/Htyt53DNHmKKGFqSae1AIk0k1S4gCQvt7I2WgpbuZNcDY+g==
elliptic@^6.0.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
+ integrity sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
@@ -2692,30 +2941,36 @@ elliptic@^6.0.0:
emoji-unicode-version@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/emoji-unicode-version/-/emoji-unicode-version-0.2.1.tgz#0ebf3666b5414097971d34994e299fce75cdbafc"
+ integrity sha1-Dr82ZrVBQJeXHTSZTimfznXNuvw=
emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
+ integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
-encodeurl@~1.0.1:
+encodeurl@~1.0.1, encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+ integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
encoding@^0.1.11:
version "0.1.12"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+ integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
dependencies:
iconv-lite "~0.4.13"
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
+ integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
dependencies:
once "^1.4.0"
-engine.io-client@~3.1.0:
- version "3.1.5"
- resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.5.tgz#85de17666560327ef1817978f6e3f8101ded2c47"
+engine.io-client@~3.2.0:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.2.1.tgz#6f54c0475de487158a1a7c77d10178708b6add36"
+ integrity sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
@@ -2732,6 +2987,7 @@ engine.io-client@~3.1.0:
engine.io-parser@~2.1.0, engine.io-parser@~2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.2.tgz#4c0f4cff79aaeecbbdcfdea66a823c6085409196"
+ integrity sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==
dependencies:
after "0.8.2"
arraybuffer.slice "~0.0.7"
@@ -2739,9 +2995,10 @@ engine.io-parser@~2.1.0, engine.io-parser@~2.1.1:
blob "0.0.4"
has-binary2 "~1.0.2"
-engine.io@~3.1.0:
- version "3.1.5"
- resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.5.tgz#0e7ef9d690eb0b35597f1d4ad02a26ca2dba3845"
+engine.io@~3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.2.0.tgz#54332506f42f2edc71690d2f2a42349359f3bf7d"
+ integrity sha512-mRbgmAtQ4GAlKwuPnnAvXXwdPhEx+jkc0OBCLrXuD/CRvwNK3AxRSnqK4FSqmAMRRHryVJP8TopOvmEaA64fKw==
dependencies:
accepts "~1.3.4"
base64id "1.0.0"
@@ -2749,20 +3006,11 @@ engine.io@~3.1.0:
debug "~3.1.0"
engine.io-parser "~2.1.0"
ws "~3.3.1"
- optionalDependencies:
- uws "~9.14.0"
-
-enhanced-resolve@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz#e34a6eaa790f62fccd71d93959f56b2b432db10a"
- dependencies:
- graceful-fs "^4.1.2"
- memory-fs "^0.4.0"
- tapable "^1.0.0"
-enhanced-resolve@^4.1.0:
+enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f"
+ integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==
dependencies:
graceful-fs "^4.1.2"
memory-fs "^0.4.0"
@@ -2771,6 +3019,7 @@ enhanced-resolve@^4.1.0:
enhanced-resolve@~0.9.0:
version "0.9.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e"
+ integrity sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=
dependencies:
graceful-fs "^4.1.2"
memory-fs "^0.2.0"
@@ -2779,32 +3028,31 @@ enhanced-resolve@~0.9.0:
ent@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
+ integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0=
entities@^1.1.1, entities@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+ integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA=
-errno@^0.1.3:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
- dependencies:
- prr "~0.0.0"
-
-errno@^0.1.4:
+errno@^0.1.3, errno@^0.1.4:
version "0.1.7"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
+ integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
dependencies:
prr "~1.0.1"
-error-ex@^1.2.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.0.tgz#e67b43f3e82c96ea3a584ffee0b9fc3325d802d9"
+error-ex@^1.2.0, error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
dependencies:
is-arrayish "^0.2.1"
-es-abstract@^1.7.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864"
+es-abstract@^1.5.1, es-abstract@^1.6.1:
+ version "1.12.0"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
+ integrity sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==
dependencies:
es-to-primitive "^1.1.1"
function-bind "^1.1.1"
@@ -2815,36 +3063,36 @@ es-abstract@^1.7.0:
es-to-primitive@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
+ integrity sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=
dependencies:
is-callable "^1.1.1"
is-date-object "^1.0.1"
is-symbol "^1.0.1"
-es6-promise@^4.0.3:
- version "4.2.4"
- resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
-
es6-promise@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
-
-es6-promisify@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
- dependencies:
- es6-promise "^4.0.3"
+ integrity sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+ integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escaper@^2.5.3:
+ version "2.5.3"
+ resolved "https://registry.yarnpkg.com/escaper/-/escaper-2.5.3.tgz#8b8fe90ba364054151ab7eff18b4ce43b1e13ab5"
+ integrity sha512-QGb9sFxBVpbzMggrKTX0ry1oiI4CSDAl9vIL702hzl1jGW8VZs7qfqTRX7WDOjoNDoEVGcEtu1ZOQgReSfT2kQ==
escodegen@1.8.x:
version "1.8.1"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018"
+ integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=
dependencies:
esprima "^2.7.1"
estraverse "^1.9.1"
@@ -2853,33 +3101,34 @@ escodegen@1.8.x:
optionalDependencies:
source-map "~0.2.0"
-escodegen@1.x.x:
- version "1.9.0"
- resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852"
+eslint-config-airbnb-base@^13.1.0:
+ version "13.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz#b5a1b480b80dfad16433d6c4ad84e6605052c05c"
+ integrity sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==
dependencies:
- esprima "^3.1.3"
- estraverse "^4.2.0"
- esutils "^2.0.2"
- optionator "^0.8.1"
- optionalDependencies:
- source-map "~0.5.6"
+ eslint-restricted-globals "^0.1.1"
+ object.assign "^4.1.0"
+ object.entries "^1.0.4"
-eslint-config-airbnb-base@^12.1.0:
- version "12.1.0"
- resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944"
+eslint-config-prettier@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-3.1.0.tgz#2c26d2cdcfa3a05f0642cd7e6e4ef3316cdabfa2"
+ integrity sha512-QYGfmzuc4q4J6XIhlp8vRKdI/fI0tQfQPy1dME3UOLprE+v4ssH/3W9LM2Q7h5qBcy5m0ehCrBDU2YF8q6OY8w==
dependencies:
- eslint-restricted-globals "^0.1.1"
+ get-stdin "^6.0.0"
eslint-import-resolver-node@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a"
+ integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==
dependencies:
debug "^2.6.9"
resolve "^1.5.0"
-eslint-import-resolver-webpack@^0.10.0:
- version "0.10.0"
- resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.10.0.tgz#b6f2468dc3e8b4ea076e5d75bece8da932789b07"
+eslint-import-resolver-webpack@^0.10.1:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.10.1.tgz#4cbceed2c0c43e488a74775c30861e58e00fb290"
+ integrity sha512-RN49nnyQpBCP3TqVhct+duJjH8kaVg08fFevWvA+4Cr1xeN7OFQRse4wMvzBto9/4VmOJWvqPfdmNTEG3jc8SQ==
dependencies:
array-find "^1.0.0"
debug "^2.6.8"
@@ -2895,28 +3144,32 @@ eslint-import-resolver-webpack@^0.10.0:
eslint-module-utils@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746"
+ integrity sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=
dependencies:
debug "^2.6.8"
pkg-dir "^1.0.0"
-eslint-plugin-filenames@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-filenames/-/eslint-plugin-filenames-1.2.0.tgz#aee9c1c90189c95d2e49902c160eceefecd99f53"
+eslint-plugin-filenames@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz#7094f00d7aefdd6999e3ac19f72cea058e590cf7"
+ integrity sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==
dependencies:
lodash.camelcase "4.3.0"
lodash.kebabcase "4.1.1"
lodash.snakecase "4.1.1"
lodash.upperfirst "4.3.1"
-eslint-plugin-html@4.0.3:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/eslint-plugin-html/-/eslint-plugin-html-4.0.3.tgz#97d52dcf9e22724505d02719fbd02754013c8a17"
+eslint-plugin-html@4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-html/-/eslint-plugin-html-4.0.5.tgz#e8ec7e16485124460f3bff312016feb0a54d9659"
+ integrity sha512-yULqYldzhYXTwZEaJXM30HhfgJdtTzuVH3LeoANybESHZ5+2ztLD72BsB2wR124/kk/PvQqZofDFSdNIk+kykw==
dependencies:
htmlparser2 "^3.8.2"
-eslint-plugin-import@^2.12.0:
- version "2.12.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.12.0.tgz#dad31781292d6664b25317fd049d2e2b2f02205d"
+eslint-plugin-import@^2.14.0:
+ version "2.14.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz#6b17626d2e3e6ad52cfce8807a845d15e22111a8"
+ integrity sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==
dependencies:
contains-path "^0.1.0"
debug "^2.6.8"
@@ -2929,131 +3182,159 @@ eslint-plugin-import@^2.12.0:
read-pkg-up "^2.0.0"
resolve "^1.6.0"
-eslint-plugin-jasmine@^2.1.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-jasmine/-/eslint-plugin-jasmine-2.2.0.tgz#7135879383c39a667c721d302b9f20f0389543de"
+eslint-plugin-jasmine@^2.10.1:
+ version "2.10.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-jasmine/-/eslint-plugin-jasmine-2.10.1.tgz#5733b709e751f4bc40e31e1c16989bd2cdfbec97"
+ integrity sha1-VzO3CedR9LxA4x4cFpib0s377Jc=
-eslint-plugin-promise@^3.8.0:
- version "3.8.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz#65ebf27a845e3c1e9d6f6a5622ddd3801694b621"
+eslint-plugin-promise@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz#2d074b653f35a23d1ba89d8e976a985117d1c6a2"
+ integrity sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg==
-eslint-plugin-vue@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-4.5.0.tgz#09d6597f4849e31a3846c2c395fccf17685b69c3"
+eslint-plugin-vue@^5.0.0-beta.3:
+ version "5.0.0-beta.3"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.0.0-beta.3.tgz#f3fa9f109b76e20fc1e45a71ce7c6d567118924e"
+ integrity sha512-EOQo3ax4CIM6Itcl522p4cGlSBgR/KZBJo2Xc29PWknbYH/DRZorGutF8NATUpbZ4HYOG+Gcyd1nL08iyYF3Tg==
dependencies:
- vue-eslint-parser "^2.0.3"
+ vue-eslint-parser "^3.2.1"
eslint-restricted-globals@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
+ integrity sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=
-eslint-scope@^3.7.1, eslint-scope@~3.7.1:
+eslint-scope@3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
+ integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=
dependencies:
esrecurse "^4.1.0"
estraverse "^4.1.1"
+eslint-scope@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172"
+ integrity sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==
+ dependencies:
+ esrecurse "^4.1.0"
+ estraverse "^4.1.1"
+
+eslint-utils@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512"
+ integrity sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==
+
eslint-visitor-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
+ integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==
-eslint@~4.12.1:
- version "4.12.1"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.12.1.tgz#5ec1973822b4a066b353770c3c6d69a2a188e880"
+eslint@~5.6.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.6.0.tgz#b6f7806041af01f71b3f1895cbb20971ea4b6223"
+ integrity sha512-/eVYs9VVVboX286mBK7bbKnO1yamUy2UCRjiY6MryhQL2PaaXCExsCQ2aO83OeYRhU2eCU/FMFP+tVMoOrzNrA==
dependencies:
- ajv "^5.3.0"
- babel-code-frame "^6.22.0"
+ "@babel/code-frame" "^7.0.0"
+ ajv "^6.5.3"
chalk "^2.1.0"
- concat-stream "^1.6.0"
- cross-spawn "^5.1.0"
- debug "^3.0.1"
- doctrine "^2.0.2"
- eslint-scope "^3.7.1"
- espree "^3.5.2"
- esquery "^1.0.0"
- estraverse "^4.2.0"
+ cross-spawn "^6.0.5"
+ debug "^3.1.0"
+ doctrine "^2.1.0"
+ eslint-scope "^4.0.0"
+ eslint-utils "^1.3.1"
+ eslint-visitor-keys "^1.0.0"
+ espree "^4.0.0"
+ esquery "^1.0.1"
esutils "^2.0.2"
file-entry-cache "^2.0.0"
functional-red-black-tree "^1.0.1"
glob "^7.1.2"
- globals "^11.0.1"
- ignore "^3.3.3"
+ globals "^11.7.0"
+ ignore "^4.0.6"
imurmurhash "^0.1.4"
- inquirer "^3.0.6"
- is-resolvable "^1.0.0"
- js-yaml "^3.9.1"
+ inquirer "^6.1.0"
+ is-resolvable "^1.1.0"
+ js-yaml "^3.12.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.3.0"
- lodash "^4.17.4"
- minimatch "^3.0.2"
+ lodash "^4.17.5"
+ minimatch "^3.0.4"
mkdirp "^0.5.1"
natural-compare "^1.4.0"
optionator "^0.8.2"
path-is-inside "^1.0.2"
pluralize "^7.0.0"
progress "^2.0.0"
+ regexpp "^2.0.0"
require-uncached "^1.0.3"
- semver "^5.3.0"
+ semver "^5.5.1"
strip-ansi "^4.0.0"
- strip-json-comments "~2.0.1"
- table "^4.0.1"
- text-table "~0.2.0"
+ strip-json-comments "^2.0.1"
+ table "^4.0.3"
+ text-table "^0.2.0"
-espree@^3.5.2:
- version "3.5.4"
- resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
+espree@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-4.0.0.tgz#253998f20a0f82db5d866385799d912a83a36634"
+ integrity sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==
dependencies:
- acorn "^5.5.0"
- acorn-jsx "^3.0.0"
+ acorn "^5.6.0"
+ acorn-jsx "^4.1.1"
esprima@2.7.x, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
-
-esprima@3.x.x, esprima@^3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+ integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
esprima@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-esquery@^1.0.0:
+esquery@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
+ integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
dependencies:
estraverse "^4.0.0"
esrecurse@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
+ integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
dependencies:
estraverse "^4.1.0"
estraverse@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
+ integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=
-estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
+estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
+ integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+ integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+ integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eve-raphael@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/eve-raphael/-/eve-raphael-0.5.0.tgz#17c754b792beef3fa6684d79cf5a47c63c4cda30"
+ integrity sha1-F8dUt5K+7z+maE15z1pHxjxM2jA=
event-stream@~3.3.0:
version "3.3.4"
resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
+ integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=
dependencies:
duplexer "~0.1.1"
from "~0"
@@ -3066,27 +3347,45 @@ event-stream@~3.3.0:
eventemitter3@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
+ integrity sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=
events@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
+ integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=
eventsource@0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232"
+ integrity sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=
dependencies:
original ">=0.0.5"
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
+ integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
dependencies:
md5.js "^1.3.4"
safe-buffer "^5.1.1"
+execa@^0.10.0:
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50"
+ integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==
+ dependencies:
+ cross-spawn "^6.0.0"
+ get-stream "^3.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
+ integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
dependencies:
cross-spawn "^5.0.1"
get-stream "^3.0.0"
@@ -3099,6 +3398,7 @@ execa@^0.7.0:
expand-braces@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea"
+ integrity sha1-SIsdHSRRyz06axks/AMPRMWFX+o=
dependencies:
array-slice "^0.2.3"
array-unique "^0.2.1"
@@ -3107,6 +3407,7 @@ expand-braces@^0.1.1:
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+ integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
dependencies:
debug "^2.3.3"
define-property "^0.2.5"
@@ -3119,6 +3420,7 @@ expand-brackets@^2.1.4:
expand-range@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044"
+ integrity sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=
dependencies:
is-number "^0.1.1"
repeat-string "^0.2.2"
@@ -3126,15 +3428,17 @@ expand-range@^0.1.0:
exports-loader@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5"
+ integrity sha512-RKwCrO4A6IiKm0pG3c9V46JxIHcDplwwGJn6+JJ1RcVnh/WSGJa0xkmk5cRVtgOPzCAtTMGj2F7nluh9L0vpSA==
dependencies:
loader-utils "^1.1.0"
source-map "0.5.0"
-express@^4.16.2:
- version "4.16.2"
- resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
+express@^4.16.2, express@^4.16.3:
+ version "4.16.3"
+ resolved "http://registry.npmjs.org/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
+ integrity sha1-avilAjUNsyRuzEvs9rWjTSL37VM=
dependencies:
- accepts "~1.3.4"
+ accepts "~1.3.5"
array-flatten "1.1.1"
body-parser "1.18.2"
content-disposition "0.5.2"
@@ -3142,49 +3446,53 @@ express@^4.16.2:
cookie "0.3.1"
cookie-signature "1.0.6"
debug "2.6.9"
- depd "~1.1.1"
- encodeurl "~1.0.1"
+ depd "~1.1.2"
+ encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
- finalhandler "1.1.0"
+ finalhandler "1.1.1"
fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
parseurl "~1.3.2"
path-to-regexp "0.1.7"
- proxy-addr "~2.0.2"
+ proxy-addr "~2.0.3"
qs "6.5.1"
range-parser "~1.2.0"
safe-buffer "5.1.1"
- send "0.16.1"
- serve-static "1.13.1"
+ send "0.16.2"
+ serve-static "1.13.2"
setprototypeof "1.1.0"
- statuses "~1.3.1"
- type-is "~1.6.15"
+ statuses "~1.4.0"
+ type-is "~1.6.16"
utils-merge "1.0.1"
vary "~1.1.2"
extend-shallow@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+ integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
dependencies:
is-extendable "^0.1.0"
extend-shallow@^3.0.0, extend-shallow@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+ integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
dependencies:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
-extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
+extend@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
+ integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=
-external-editor@^2.0.1, external-editor@^2.0.4:
+external-editor@^2.0.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
+ integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==
dependencies:
chardet "^0.4.0"
iconv-lite "^0.4.17"
@@ -3193,6 +3501,7 @@ external-editor@^2.0.1, external-editor@^2.0.4:
external-editor@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6"
+ integrity sha512-mpkfj0FEdxrIhOC04zk85X7StNtr0yXnG7zCb+8ikO8OJi2jsHh5YGoknNTyXgsbHOf1WOOcVU3kPFWT2WgCkQ==
dependencies:
chardet "^0.5.0"
iconv-lite "^0.4.22"
@@ -3201,6 +3510,7 @@ external-editor@^3.0.0:
extglob@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+ integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
dependencies:
array-unique "^0.3.2"
define-property "^1.0.0"
@@ -3211,80 +3521,85 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-extsprintf@1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
-
-extsprintf@^1.2.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
-
-fast-deep-equal@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
+fast-deep-equal@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
+ integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+ integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
fast-levenshtein@~2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
fastparse@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
+ integrity sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=
faye-websocket@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
+ integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=
dependencies:
websocket-driver ">=0.5.1"
faye-websocket@~0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38"
+ integrity sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=
dependencies:
websocket-driver ">=0.5.1"
+figgy-pudding@^3.1.0, figgy-pudding@^3.5.1:
+ version "3.5.1"
+ resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
+ integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
+
figures@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+ integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
dependencies:
escape-string-regexp "^1.0.5"
file-entry-cache@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
+ integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=
dependencies:
flat-cache "^1.2.1"
object-assign "^4.0.1"
-file-loader@^1.1.11:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.11.tgz#6fe886449b0f2a936e43cabaac0cdbfb369506f8"
+file-loader@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-2.0.0.tgz#39749c82f020b9e85901dcff98e8004e6401cfde"
+ integrity sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==
dependencies:
loader-utils "^1.0.2"
- schema-utils "^0.4.5"
+ schema-utils "^1.0.0"
-file-uri-to-path@1:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
-
-fileset@^2.0.2:
+fileset@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0"
+ integrity sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=
dependencies:
glob "^7.0.3"
minimatch "^3.0.3"
-filesize@^3.5.11:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.0.tgz#22d079615624bb6fd3c04026120628a41b3f4efa"
+filesize@^3.6.1:
+ version "3.6.1"
+ resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317"
+ integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+ integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
dependencies:
extend-shallow "^2.0.1"
is-number "^3.0.0"
@@ -3294,6 +3609,7 @@ fill-range@^4.0.0:
finalhandler@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5"
+ integrity sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=
dependencies:
debug "2.6.9"
encodeurl "~1.0.1"
@@ -3303,21 +3619,46 @@ finalhandler@1.1.0:
statuses "~1.3.1"
unpipe "~1.0.0"
+finalhandler@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
+ integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==
+ dependencies:
+ debug "2.6.9"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ on-finished "~2.3.0"
+ parseurl "~1.3.2"
+ statuses "~1.4.0"
+ unpipe "~1.0.0"
+
find-cache-dir@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f"
+ integrity sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=
dependencies:
commondir "^1.0.1"
make-dir "^1.0.0"
pkg-dir "^2.0.0"
+find-cache-dir@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d"
+ integrity sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==
+ dependencies:
+ commondir "^1.0.1"
+ make-dir "^1.0.0"
+ pkg-dir "^3.0.0"
+
find-root@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
+ integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=
dependencies:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
@@ -3325,12 +3666,21 @@ find-up@^1.0.0:
find-up@^2.0.0, find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
dependencies:
locate-path "^2.0.0"
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+ dependencies:
+ locate-path "^3.0.0"
+
flat-cache@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96"
+ integrity sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=
dependencies:
circular-json "^0.3.1"
del "^2.0.2"
@@ -3340,71 +3690,49 @@ flat-cache@^1.2.1:
flush-write-stream@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417"
+ integrity sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=
dependencies:
inherits "^2.0.1"
readable-stream "^2.0.4"
-follow-redirects@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37"
- dependencies:
- debug "^2.2.0"
-
follow-redirects@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.6.tgz#4dcdc7e4ab3dd6765a97ff89c3b4c258117c79bf"
+ integrity sha512-FrMqZ/FONtHnbqO651UPpfRUVukIEwJhXMfdr/JWAmrDbeYBu773b1J6gdWDyRIj4hvvzQEHoEOTrdR8o6KLYA==
dependencies:
debug "^3.1.0"
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
-
-foreach@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
-
-forever-agent@~0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
-
-form-data@~2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.0.0.tgz#6f0aebadcc5da16c13e1ecc11137d85f9b883b25"
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.5"
- mime-types "^2.1.11"
-
-form-data@~2.3.0, form-data@~2.3.1:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
- dependencies:
- asynckit "^0.4.0"
- combined-stream "1.0.6"
- mime-types "^2.1.12"
+ integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
formdata-polyfill@^3.0.11:
version "3.0.11"
resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-3.0.11.tgz#c82b4b4bea3356c0a6752219e54ce1edb2a7fb5b"
+ integrity sha512-lDyjdlptnGL1Fk7q+hketv31EN9rWaVC/SLz1tRaUktGrsCijyueIcjn7Tw3xKEdCjS5SeBrWp5aNLWUQq+QLg==
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
+ integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
fragment-cache@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+ integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
dependencies:
map-cache "^0.2.2"
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+ integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
from2@^2.1.0, from2@^2.1.1:
version "2.3.0"
resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
+ integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
dependencies:
inherits "^2.0.1"
readable-stream "^2.0.0"
@@ -3412,22 +3740,26 @@ from2@^2.1.0, from2@^2.1.1:
from@~0:
version "0.1.7"
resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
+ integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=
fs-access@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a"
+ integrity sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=
dependencies:
null-check "^1.0.0"
fs-minipass@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
+ integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==
dependencies:
minipass "^2.2.1"
fs-write-stream-atomic@^1.0.8:
version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
+ integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=
dependencies:
graceful-fs "^4.1.2"
iferr "^0.1.5"
@@ -3437,36 +3769,35 @@ fs-write-stream-atomic@^1.0.8:
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-fsevents@^1.0.0, fsevents@^1.2.2:
+fsevents@^1.2.2:
version "1.2.4"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426"
+ integrity sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==
dependencies:
nan "^2.9.2"
node-pre-gyp "^0.10.0"
-ftp@~0.3.10:
- version "0.3.10"
- resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d"
- dependencies:
- readable-stream "1.1.x"
- xregexp "2.0.0"
-
-function-bind@^1.0.2, function-bind@^1.1.1:
+function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
functional-red-black-tree@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
fuzzaldrin-plus@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/fuzzaldrin-plus/-/fuzzaldrin-plus-0.5.0.tgz#ef5f26f0c2fc7e9e9a16ea149a802d6cb4804b1e"
+ integrity sha1-718m8ML8fp6aFuoUmoAtbLSASx4=
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+ integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
dependencies:
aproba "^1.0.3"
console-control-strings "^1.0.0"
@@ -3477,98 +3808,105 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
-generate-function@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74"
-
-generate-object-property@^1.1.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0"
- dependencies:
- is-property "^1.0.0"
-
get-caller-file@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
+ integrity sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=
-get-stdin@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
+get-stdin@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
+ integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
get-stream@3.0.0, get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
-
-get-uri@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.2.tgz#5c795e71326f6ca1286f2fc82575cd2bab2af578"
- dependencies:
- data-uri-to-buffer "1"
- debug "2"
- extend "3"
- file-uri-to-path "1"
- ftp "~0.3.10"
- readable-stream "2"
+ integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+ integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
-getpass@^0.1.1:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+gettext-extractor-vue@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/gettext-extractor-vue/-/gettext-extractor-vue-4.0.1.tgz#69d2737eb8f1938803ffcf9317133ed59fb2372f"
+ integrity sha512-UnkWVO5jQQrs17L7HSlKj3O7U8C4+AQFzE05MK/I+JkMZdQdB6JMjA0IK0c4GObSlkgx4aiCCG6zWqIBnDR95w==
dependencies:
- assert-plus "^1.0.0"
+ bluebird "^3.5.1"
+ glob "^7.1.2"
+ vue-template-compiler "^2.5.0"
+
+gettext-extractor@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/gettext-extractor/-/gettext-extractor-3.3.2.tgz#d5172ba8d175678bd40a5abe7f908fa2a9d9473b"
+ integrity sha1-1RcrqNF1Z4vUClq+f5CPoqnZRzs=
+ dependencies:
+ "@types/glob" "^5"
+ "@types/parse5" "^5"
+ css-selector-parser "^1.3"
+ glob "5 - 7"
+ parse5 "^5"
+ pofile "^1"
+ typescript "^2"
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
+ integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
dependencies:
is-glob "^3.1.0"
path-dirname "^1.0.0"
-glob@^5.0.15:
- version "5.0.15"
- resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+"glob@5 - 7", glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+ integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
dependencies:
+ fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
- minimatch "2 || 3"
+ minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
- version "7.1.2"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+glob@^5.0.15:
+ version "5.0.15"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+ integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=
dependencies:
- fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
- minimatch "^3.0.4"
+ minimatch "2 || 3"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-dirs@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
+ integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=
dependencies:
ini "^1.3.4"
global-modules-path@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.1.0.tgz#923ec524e8726bb0c1a4ed4b8e21e1ff80c88bbb"
+ integrity sha512-3DrmGj2TP+96cABk9TfMp6f3knH/Y46dqvWznTU3Tf6/bDGLDAn15tFluQ7BcloykOcdY16U0WGq0BQblYOxJQ==
-globals@^11.0.1, globals@^11.1.0:
- version "11.5.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-11.5.0.tgz#6bc840de6771173b191f13d3a9c94d441ee92642"
+globals@^11.1.0, globals@^11.7.0:
+ version "11.7.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673"
+ integrity sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==
globals@^9.18.0:
version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
+ integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
globby@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
+ integrity sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=
dependencies:
array-union "^1.0.1"
arrify "^1.0.0"
@@ -3580,6 +3918,7 @@ globby@^5.0.0:
globby@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
+ integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
dependencies:
array-union "^1.0.1"
glob "^7.0.3"
@@ -3590,12 +3929,14 @@ globby@^6.1.0:
good-listener@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+ integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
dependencies:
delegate "^3.1.2"
got@^6.7.1:
version "6.7.1"
resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
+ integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=
dependencies:
create-error-class "^3.0.0"
duplexer3 "^0.1.4"
@@ -3612,6 +3953,7 @@ got@^6.7.1:
got@^8.0.3:
version "8.3.0"
resolved "https://registry.yarnpkg.com/got/-/got-8.3.0.tgz#6ba26e75f8a6cc4c6b3eb1fe7ce4fec7abac8533"
+ integrity sha512-kBNy/S2CGwrYgDSec5KTWGKUvupwkkTVAjIsVFF2shXO13xpZdFP4d4kxa//CLX2tN/rV0aYwK8vY6UKWGn2vQ==
dependencies:
"@sindresorhus/is" "^0.7.0"
cacheable-request "^2.1.1"
@@ -3634,16 +3976,19 @@ got@^8.0.3:
graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+ integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=
-graphlib@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.1.tgz#42352c52ba2f4d035cb566eb91f7395f76ebc951"
+graphlibrary@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/graphlibrary/-/graphlibrary-2.2.0.tgz#017a14899775228dec4497a39babfdd6bf56eac6"
+ integrity sha512-XTcvT55L8u4MBZrM37zXoUxsgxs/7sow7YSygd9CIwfWTVO8RVu7AYXhhCiTuFEf+APKgx6Jk4SuQbYR0vYKmQ==
dependencies:
- lodash "^4.11.1"
+ lodash "^4.17.5"
-gzip-size@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c"
+gzip-size@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80"
+ integrity sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA==
dependencies:
duplexer "^0.1.1"
pify "^3.0.0"
@@ -3651,78 +3996,74 @@ gzip-size@^4.1.0:
handle-thing@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
+ integrity sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=
-handlebars@^4.0.1, handlebars@^4.0.3:
- version "4.0.6"
- resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7"
+handlebars@^4.0.1, handlebars@^4.0.11:
+ version "4.0.12"
+ resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5"
+ integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==
dependencies:
- async "^1.4.0"
+ async "^2.5.0"
optimist "^0.6.1"
- source-map "^0.4.4"
+ source-map "^0.6.1"
optionalDependencies:
- uglify-js "^2.6"
-
-har-schema@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
-
-har-validator@~2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d"
- dependencies:
- chalk "^1.1.1"
- commander "^2.9.0"
- is-my-json-valid "^2.12.4"
- pinkie-promise "^2.0.0"
-
-har-validator@~5.0.3:
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
- dependencies:
- ajv "^5.1.0"
- har-schema "^2.0.0"
+ uglify-js "^3.1.4"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
dependencies:
ansi-regex "^2.0.0"
has-binary2@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.2.tgz#e83dba49f0b9be4d026d27365350d9f03f54be98"
+ integrity sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=
dependencies:
isarray "2.0.1"
has-cors@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
+ integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=
has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+ integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
has-symbol-support-x@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.3.0.tgz#588bd6927eaa0e296afae24160659167fc2be4f8"
+ integrity sha512-kLtS+N9qwz+Buc6TUfcW5iGb59hLLr5qfxTACi/0uGpH1u5NMNWsdU57KoYRBywvPykeRmu5qfB5x0chpDSWlg==
+
+has-symbols@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
+ integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
has-to-string-tag-x@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.3.0.tgz#78e3d98c3c0ec9413e970eb8d766249a1e13058f"
+ integrity sha512-Fu9Nwv8/VNJMvKjkldzXHO+yeX+TCelwUQ9dGW2LrAfHfHi6zVqQt+Qjilf0qGHvpl6Fap6o8aDhWhMt5hY/1g==
dependencies:
has-symbol-support-x "^1.3.0"
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+ integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
has-value@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+ integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
dependencies:
get-value "^2.0.3"
has-values "^0.1.4"
@@ -3731,6 +4072,7 @@ has-value@^0.3.1:
has-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+ integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
dependencies:
get-value "^2.0.6"
has-values "^1.0.0"
@@ -3739,10 +4081,12 @@ has-value@^1.0.0:
has-values@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+ integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
has-values@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+ integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
dependencies:
is-number "^3.0.0"
kind-of "^4.0.0"
@@ -3750,18 +4094,21 @@ has-values@^1.0.0:
has@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
+ integrity sha1-hGFzP1OLCDfJNh45qauelwTcLyg=
dependencies:
function-bind "^1.0.2"
hash-base@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
+ integrity sha1-ZuodhW206KVHDK32/OI65SRO8uE=
dependencies:
inherits "^2.0.1"
hash-base@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
+ integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
@@ -3769,73 +4116,49 @@ hash-base@^3.0.0:
hash-sum@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
+ integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=
hash.js@^1.0.0, hash.js@^1.0.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846"
+ integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==
dependencies:
inherits "^2.0.3"
minimalistic-assert "^1.0.0"
-hawk@~3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
- dependencies:
- boom "2.x.x"
- cryptiles "2.x.x"
- hoek "2.x.x"
- sntp "1.x.x"
-
-hawk@~6.0.2:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
- dependencies:
- boom "4.x.x"
- cryptiles "3.x.x"
- hoek "4.x.x"
- sntp "2.x.x"
-
he@^1.1.0, he@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
+ integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
-hipchat-notifier@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz#b6d249755437c191082367799d3ba9a0f23b231e"
- dependencies:
- lodash "^4.0.0"
- request "^2.0.0"
+highlight.js@^9.13.1:
+ version "9.13.1"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e"
+ integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+ integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
dependencies:
hash.js "^1.0.3"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoek@2.x.x:
- version "2.16.3"
- resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
-
-hoek@4.x.x:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
-
-home-or-tmp@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
- dependencies:
- os-homedir "^1.0.0"
- os-tmpdir "^1.0.1"
+hoopy@^0.1.2:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d"
+ integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==
hosted-git-info@^2.1.4:
version "2.2.0"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5"
+ integrity sha1-eg0JeGPYhsD6u9zTe/F1jYvs+KU=
hpack.js@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
+ integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=
dependencies:
inherits "^2.0.1"
obuf "^1.0.0"
@@ -3845,10 +4168,12 @@ hpack.js@^2.1.6:
html-entities@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
+ integrity sha1-QZSMr4XOgv7Tbk5qDtNxpmZDeeI=
htmlparser2@^3.8.2, htmlparser2@^3.9.0:
version "3.9.2"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
+ integrity sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=
dependencies:
domelementtype "^1.3.0"
domhandler "^2.3.0"
@@ -3860,39 +4185,27 @@ htmlparser2@^3.8.2, htmlparser2@^3.9.0:
http-cache-semantics@3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
+ integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==
http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
+ integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=
http-errors@1.6.2, http-errors@~1.6.1, http-errors@~1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
+ integrity sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=
dependencies:
depd "1.1.1"
inherits "2.0.3"
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
-http-errors@1.6.3:
- version "1.6.3"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
- dependencies:
- depd "~1.1.2"
- inherits "2.0.3"
- setprototypeof "1.1.0"
- statuses ">= 1.4.0 < 2"
-
-http-proxy-agent@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
- dependencies:
- agent-base "4"
- debug "3.1.0"
-
http-proxy-middleware@~0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz#0987e6bb5a5606e5a69168d8f967a87f15dd8aab"
+ integrity sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==
dependencies:
http-proxy "^1.16.2"
is-glob "^4.0.0"
@@ -3902,112 +4215,97 @@ http-proxy-middleware@~0.18.0:
http-proxy@^1.13.0, http-proxy@^1.16.2:
version "1.16.2"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742"
+ integrity sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=
dependencies:
eventemitter3 "1.x.x"
requires-port "1.x.x"
-http-signature@~1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
- dependencies:
- assert-plus "^0.2.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
-
-http-signature@~1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
- dependencies:
- assert-plus "^1.0.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
-
-httpntlm@1.6.1:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/httpntlm/-/httpntlm-1.6.1.tgz#ad01527143a2e8773cfae6a96f58656bb52a34b2"
- dependencies:
- httpreq ">=0.4.22"
- underscore "~1.7.0"
-
-httpreq@>=0.4.22:
- version "0.4.24"
- resolved "https://registry.yarnpkg.com/httpreq/-/httpreq-0.4.24.tgz#4335ffd82cd969668a39465c929ac61d6393627f"
-
https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
+ integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
-https-proxy-agent@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
- dependencies:
- agent-base "^4.1.0"
- debug "^3.1.0"
-
-iconv-lite@0.4, iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
+iconv-lite@0.4, iconv-lite@^0.4.17, iconv-lite@^0.4.22, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.23"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
+ integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
-iconv-lite@0.4.15:
- version "0.4.15"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
-
-iconv-lite@0.4.19, iconv-lite@^0.4.17:
+iconv-lite@0.4.19:
version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
+ integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==
icss-replace-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+ integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
icss-utils@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962"
+ integrity sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=
dependencies:
postcss "^6.0.1"
-ieee754@^1.1.11, ieee754@^1.1.4:
+ieee754@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
+ integrity sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==
iferr@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
+ integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
ignore-by-default@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
+ integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk=
ignore-walk@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8"
+ integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==
dependencies:
minimatch "^3.0.4"
-ignore@^3.3.3, ignore@^3.3.7:
- version "3.3.8"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.8.tgz#3f8e9c35d38708a3a7e0e9abb6c73e7ee7707b2b"
+ignore@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+ integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+ integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
import-lazy@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
+ integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
import-local@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
+ integrity sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==
dependencies:
pkg-dir "^2.0.0"
resolve-cwd "^2.0.0"
+import-local@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
+ integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==
+ dependencies:
+ pkg-dir "^3.0.0"
+ resolve-cwd "^2.0.0"
+
imports-loader@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.8.0.tgz#030ea51b8ca05977c40a3abfd9b4088fe0be9a69"
+ integrity sha512-kXWL7Scp8KQ4552ZcdVTeaQCZSLW+e6nJfp3cwUMB673T7Hr98Xjx5JK+ql7ADlJUvj1JS5O01RLbKoutN5QDQ==
dependencies:
loader-utils "^1.0.2"
source-map "^0.6.1"
@@ -4015,32 +4313,22 @@ imports-loader@^0.8.0:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
-
-indent-string@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
- dependencies:
- repeating "^2.0.0"
+ integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
indexes-of@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+ integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
indexof@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
-
-inflection@~1.12.0:
- version "1.12.0"
- resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.12.0.tgz#a200935656d6f5f6bc4dc7502e1aecb703228416"
-
-inflection@~1.3.0:
- version "1.3.8"
- resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.3.8.tgz#cbd160da9f75b14c3cc63578d4f396784bf3014e"
+ integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
dependencies:
once "^1.3.0"
wrappy "1"
@@ -4048,18 +4336,22 @@ inflight@^1.0.4:
inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+ integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+ integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
ini@^1.3.4, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+ integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
inquirer@3.0.6:
version "3.0.6"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347"
+ integrity sha1-4EqqnQW3o8ubD0B9BDdfBEcZA0c=
dependencies:
ansi-escapes "^1.1.0"
chalk "^1.0.0"
@@ -4075,28 +4367,10 @@ inquirer@3.0.6:
strip-ansi "^3.0.0"
through "^2.3.6"
-inquirer@^3.0.6:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
- dependencies:
- ansi-escapes "^3.0.0"
- chalk "^2.0.0"
- cli-cursor "^2.1.0"
- cli-width "^2.0.0"
- external-editor "^2.0.4"
- figures "^2.0.0"
- lodash "^4.3.0"
- mute-stream "0.0.7"
- run-async "^2.2.0"
- rx-lite "^4.0.8"
- rx-lite-aggregates "^4.0.8"
- string-width "^2.1.0"
- strip-ansi "^4.0.0"
- through "^2.3.6"
-
-inquirer@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.0.0.tgz#e8c20303ddc15bbfc2c12a6213710ccd9e1413d8"
+inquirer@^6.0.0, inquirer@^6.1.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8"
+ integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==
dependencies:
ansi-escapes "^3.0.0"
chalk "^2.0.0"
@@ -4104,7 +4378,7 @@ inquirer@^6.0.0:
cli-width "^2.0.0"
external-editor "^3.0.0"
figures "^2.0.0"
- lodash "^4.3.0"
+ lodash "^4.17.10"
mute-stream "0.0.7"
run-async "^2.2.0"
rxjs "^6.1.0"
@@ -4112,96 +4386,120 @@ inquirer@^6.0.0:
strip-ansi "^4.0.0"
through "^2.3.6"
-internal-ip@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c"
+internal-ip@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-3.0.1.tgz#df5c99876e1d2eb2ea2d74f520e3f669a00ece27"
+ integrity sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==
dependencies:
- meow "^3.3.0"
+ default-gateway "^2.6.0"
+ ipaddr.js "^1.5.2"
interpret@^1.0.0, interpret@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
+ integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=
into-stream@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6"
+ integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=
dependencies:
from2 "^2.1.1"
p-is-promise "^1.1.0"
-invariant@^2.2.0, invariant@^2.2.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
+invariant@^2.2.2:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+ integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
dependencies:
loose-envify "^1.0.0"
-invert-kv@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+invert-kv@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
+ integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
-ip@^1.1.0, ip@^1.1.2, ip@^1.1.4, ip@^1.1.5:
+ip-regex@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
+ integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
+
+ip@^1.1.0, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
+ integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
-ipaddr.js@1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
+ipaddr.js@1.8.0, ipaddr.js@^1.5.2:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e"
+ integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4=
is-accessor-descriptor@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+ integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
dependencies:
kind-of "^3.0.2"
is-accessor-descriptor@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+ integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
dependencies:
kind-of "^6.0.0"
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
is-binary-path@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+ integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
dependencies:
binary-extensions "^1.0.0"
is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+ integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-builtin-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+ integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74=
dependencies:
builtin-modules "^1.0.0"
is-callable@^1.1.1, is-callable@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
+ integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
is-data-descriptor@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+ integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
dependencies:
kind-of "^3.0.2"
is-data-descriptor@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+ integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
dependencies:
kind-of "^6.0.0"
is-date-object@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
+ integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
is-descriptor@^0.1.0:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+ integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
dependencies:
is-accessor-descriptor "^0.1.6"
is-data-descriptor "^0.1.4"
@@ -4210,6 +4508,7 @@ is-descriptor@^0.1.0:
is-descriptor@^1.0.0, is-descriptor@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+ integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
dependencies:
is-accessor-descriptor "^1.0.0"
is-data-descriptor "^1.0.0"
@@ -4218,280 +4517,284 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2:
is-extendable@^0.1.0, is-extendable@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+ integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
is-extendable@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+ integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
dependencies:
is-plain-object "^2.0.4"
is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
-
-is-finite@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
- dependencies:
- number-is-nan "^1.0.0"
+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
is-fullwidth-code-point@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
dependencies:
number-is-nan "^1.0.0"
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+ integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-glob@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
+ integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
dependencies:
is-extglob "^2.1.0"
is-glob@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0"
+ integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=
dependencies:
is-extglob "^2.1.1"
is-installed-globally@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
+ integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=
dependencies:
global-dirs "^0.1.0"
is-path-inside "^1.0.0"
-is-my-ip-valid@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824"
-
-is-my-json-valid@^2.12.4:
- version "2.17.2"
- resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c"
- dependencies:
- generate-function "^2.0.0"
- generate-object-property "^1.1.0"
- is-my-ip-valid "^1.0.0"
- jsonpointer "^4.0.0"
- xtend "^4.0.0"
-
is-npm@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4"
+ integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ=
is-number@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
+ integrity sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+ integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
dependencies:
kind-of "^3.0.2"
is-number@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
+ integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
is-obj@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+ integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
is-object@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
+ integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA=
is-odd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24"
+ integrity sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==
dependencies:
is-number "^4.0.0"
is-path-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+ integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
is-path-in-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc"
+ integrity sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=
dependencies:
is-path-inside "^1.0.0"
is-path-inside@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f"
+ integrity sha1-/AbloWg/vaE95mev9xe7wQpI838=
dependencies:
path-is-inside "^1.0.1"
is-plain-obj@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
+ integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
dependencies:
isobject "^3.0.1"
is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
-
-is-property@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
+ integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
is-redirect@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
+ integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=
is-regex@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+ integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
dependencies:
has "^1.0.1"
-is-resolvable@^1.0.0:
+is-regexp@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62"
- dependencies:
- tryit "^1.0.1"
+ resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
+ integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
+
+is-resolvable@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
+ integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
+ integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=
is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+ integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
is-symbol@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
-
-is-typedarray@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
-
-is-utf8@^0.2.0:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
+ integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==
+ dependencies:
+ has-symbols "^1.0.0"
is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+ integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
is-wsl@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
-
-isarray@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+ integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isarray@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
+ integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=
isbinaryfile@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621"
+ integrity sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+ integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
dependencies:
isarray "1.0.0"
isobject@^3.0.0, isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
-isstream@~0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
-
-istanbul-api@^1.1.14:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.2.1.tgz#0c60a0515eb11c7d65c6b50bba2c6e999acd8620"
- dependencies:
- async "^2.1.4"
- fileset "^2.0.2"
- istanbul-lib-coverage "^1.1.1"
- istanbul-lib-hook "^1.1.0"
- istanbul-lib-instrument "^1.9.1"
- istanbul-lib-report "^1.1.2"
- istanbul-lib-source-maps "^1.2.2"
- istanbul-reports "^1.1.3"
- js-yaml "^3.7.0"
- mkdirp "^0.5.1"
+istanbul-api@^2.0.5:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-2.0.6.tgz#cd7b33ee678f6c01531d05f5e567ebbcd25f8ecc"
+ integrity sha512-8W5oeAGWXhtTJjAyVfvavOLVyZCTNCKsyF6GON/INKlBdO7uJ/bv3qnPj5M6ERKzmMCJS1kntnjjGuJ86fn3rQ==
+ dependencies:
+ async "^2.6.1"
+ compare-versions "^3.2.1"
+ fileset "^2.0.3"
+ istanbul-lib-coverage "^2.0.1"
+ istanbul-lib-hook "^2.0.1"
+ istanbul-lib-instrument "^3.0.0"
+ istanbul-lib-report "^2.0.2"
+ istanbul-lib-source-maps "^2.0.1"
+ istanbul-reports "^2.0.1"
+ js-yaml "^3.12.0"
+ make-dir "^1.3.0"
once "^1.4.0"
-istanbul-lib-coverage@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"
-
-istanbul-lib-coverage@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341"
+istanbul-lib-coverage@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#2aee0e073ad8c5f6a0b00e0dfbf52b4667472eda"
+ integrity sha512-nPvSZsVlbG9aLhZYaC3Oi1gT/tpyo3Yt5fNyf6NmcKIayz4VV/txxJFFKAK/gU4dcNn8ehsanBbVHVl0+amOLA==
-istanbul-lib-hook@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b"
+istanbul-lib-hook@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.1.tgz#918a57b75a0f951d552a08487ca1fa5336433d72"
+ integrity sha512-ufiZoiJ8CxY577JJWEeFuxXZoMqiKpq/RqZtOAYuQLvlkbJWscq9n3gc4xrCGH9n4pW0qnTxOz1oyMmVtk8E1w==
dependencies:
- append-transform "^0.4.0"
+ append-transform "^1.0.0"
-istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.9.1:
- version "1.10.1"
- resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b"
- dependencies:
- babel-generator "^6.18.0"
- babel-template "^6.16.0"
- babel-traverse "^6.18.0"
- babel-types "^6.18.0"
- babylon "^6.18.0"
- istanbul-lib-coverage "^1.2.0"
- semver "^5.3.0"
+istanbul-lib-instrument@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.0.tgz#b5f066b2a161f75788be17a9d556f40a0cf2afc9"
+ integrity sha512-eQY9vN9elYjdgN9Iv6NS/00bptm02EBBk70lRMaVjeA6QYocQgenVrSgC28TJurdnZa80AGO3ASdFN+w/njGiQ==
+ dependencies:
+ "@babel/generator" "^7.0.0"
+ "@babel/parser" "^7.0.0"
+ "@babel/template" "^7.0.0"
+ "@babel/traverse" "^7.0.0"
+ "@babel/types" "^7.0.0"
+ istanbul-lib-coverage "^2.0.1"
+ semver "^5.5.0"
-istanbul-lib-report@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.2.tgz#922be27c13b9511b979bd1587359f69798c1d425"
+istanbul-lib-report@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.2.tgz#430a2598519113e1da7af274ba861bd42dd97535"
+ integrity sha512-rJ8uR3peeIrwAxoDEbK4dJ7cqqtxBisZKCuwkMtMv0xYzaAnsAi3AHrHPAAtNXzG/bcCgZZ3OJVqm1DTi9ap2Q==
dependencies:
- istanbul-lib-coverage "^1.1.1"
- mkdirp "^0.5.1"
- path-parse "^1.0.5"
- supports-color "^3.1.2"
+ istanbul-lib-coverage "^2.0.1"
+ make-dir "^1.3.0"
+ supports-color "^5.4.0"
-istanbul-lib-source-maps@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.2.tgz#750578602435f28a0c04ee6d7d9e0f2960e62c1c"
+istanbul-lib-source-maps@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-2.0.1.tgz#ce8b45131d8293fdeaa732f4faf1852d13d0a97e"
+ integrity sha512-30l40ySg+gvBLcxTrLzR4Z2XTRj3HgRCA/p2rnbs/3OiTaoj054gAbuP5DcLOtwqmy4XW8qXBHzrmP2/bQ9i3A==
dependencies:
debug "^3.1.0"
- istanbul-lib-coverage "^1.1.1"
- mkdirp "^0.5.1"
- rimraf "^2.6.1"
- source-map "^0.5.3"
+ istanbul-lib-coverage "^2.0.1"
+ make-dir "^1.3.0"
+ rimraf "^2.6.2"
+ source-map "^0.6.1"
-istanbul-reports@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.3.tgz#3b9e1e8defb6d18b1d425da8e8b32c5a163f2d10"
+istanbul-reports@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.0.1.tgz#fb8d6ea850701a3984350b977a969e9a556116a7"
+ integrity sha512-CT0QgMBJqs6NJLF678ZHcquUAZIoBIUNzdJrRJfpkI9OnzG6MkUfHxbJC3ln981dMswC7/B1mfX3LNkhgJxsuw==
dependencies:
- handlebars "^4.0.3"
+ handlebars "^4.0.11"
istanbul@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b"
+ integrity sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=
dependencies:
abbrev "1.0.x"
async "1.x"
@@ -4511,6 +4814,7 @@ istanbul@^0.4.5:
istextorbinary@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.2.1.tgz#a5231a08ef6dd22b268d0895084cf8d58b5bec53"
+ integrity sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==
dependencies:
binaryextensions "2"
editions "^1.3.3"
@@ -4519,6 +4823,7 @@ istextorbinary@^2.2.1:
isurl@^1.0.0-alpha5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
+ integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==
dependencies:
has-to-string-tag-x "^1.2.0"
is-object "^1.0.1"
@@ -4526,118 +4831,129 @@ isurl@^1.0.0-alpha5:
jasmine-core@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.9.0.tgz#bfbb56defcd30789adec5a3fbba8504233289c72"
+ integrity sha1-v7tW3vzTB4mt7Fo/u6hQQjMonHI=
jasmine-diff@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/jasmine-diff/-/jasmine-diff-0.1.3.tgz#93ccc2dcc41028c5ddd4606558074839f2deeaa8"
+ integrity sha1-k8zC3MQQKMXd1GBlWAdIOfLe6qg=
dependencies:
diff "^3.2.0"
jasmine-jquery@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jasmine-jquery/-/jasmine-jquery-2.1.1.tgz#d4095e646944a26763235769ab018d9f30f0d47b"
+ integrity sha1-1AleZGlEomdjI1dpqwGNnzDw1Hs=
jed@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/jed/-/jed-1.1.1.tgz#7a549bbd9ffe1585b0cd0a191e203055bee574b4"
+ integrity sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ=
jquery-ujs@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.2.tgz#6a8ef1020e6b6dda385b90a4bddc128c21c56397"
+ integrity sha1-ao7xAg5rbdo4W5CkvdwSjCHFY5c=
dependencies:
jquery ">=1.8.0"
jquery.waitforimages@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b"
+ integrity sha1-Y/IxMQVaGwYNyRPm2HS8ybnmsWs=
"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^3.2.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
+ integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
+
+js-beautify@^1.8.8:
+ version "1.8.8"
+ resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.8.8.tgz#1eb175b73a3571a5f1ed8d98e7cf2b05bfa98471"
+ integrity sha512-qVNq7ZZ7ZbLdzorvSlRDadS0Rh5oyItaE95v6I4wbbuSiijxn7SnnsV6dvKlcXuO2jX7lK8tn9fBulx34K/Ejg==
+ dependencies:
+ config-chain "~1.1.5"
+ editorconfig "^0.15.0"
+ mkdirp "~0.5.0"
+ nopt "~4.0.1"
js-cookie@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.3.tgz#48071625217ac9ecfab8c343a13d42ec09ff0526"
+ integrity sha1-SAcWJSF6yez6uMNDoT1C7An/BSY=
-js-tokens@^3.0.0, js-tokens@^3.0.2:
+js-levenshtein@^1.1.3:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.4.tgz#3a56e3cbf589ca0081eb22cd9ba0b1290a16d26e"
+ integrity sha512-PxfGzSs0ztShKrUYPIn5r0MtyAhYcCwmndozzpz8YObbPnD1jFxzlBGbRnX2mIu6Z13xN6+PTu05TQFnZFlzow==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-tokens@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+ integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
-js-yaml@3.x, js-yaml@^3.7.0, js-yaml@^3.9.1:
- version "3.11.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
+js-yaml@3.x, js-yaml@^3.12.0:
+ version "3.12.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
+ integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
-jsbn@~0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
-
-jsesc@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
-
jsesc@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe"
+ integrity sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=
jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+ integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
+ integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
-json-parse-better-errors@^1.0.2:
+json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+ integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
-json-schema-traverse@^0.3.0:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
-
-json-schema@0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
-
-json-stringify-safe@5.0.x, json-stringify-safe@~5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+ integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
json3@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
+ integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=
-json5@^0.5.0, json5@^0.5.1:
+json5@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
-
-jsonpointer@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
-
-jsprim@^1.2.2:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
- dependencies:
- assert-plus "1.0.0"
- extsprintf "1.3.0"
- json-schema "0.2.3"
- verror "1.10.0"
+ integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
jszip-utils@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/jszip-utils/-/jszip-utils-0.0.2.tgz#457d5cbca60a1c2e0706e9da2b544e8e7bc50bf8"
+ integrity sha1-RX1cvKYKHC4HBunaK1ROjnvFC/g=
jszip@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.3.tgz#8a920403b2b1651c0fc126be90192d9080957c37"
+ integrity sha1-ipIEA7KxZRwPwSa+kBktkICVfDc=
dependencies:
core-js "~2.3.0"
es6-promise "~3.0.2"
@@ -4648,24 +4964,36 @@ jszip@^3.1.3:
karma-chrome-launcher@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz#cf1b9d07136cc18fe239327d24654c3dbc368acf"
+ integrity sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==
dependencies:
fs-access "^1.0.0"
which "^1.2.1"
-karma-coverage-istanbul-reporter@^1.4.2:
- version "1.4.2"
- resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.2.tgz#a8d0c8815c7d6f6cea15a394a7c4b39ef151a939"
+karma-coverage-istanbul-reporter@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.4.tgz#402ae4ed6eadb9d9dafbd408ffda17897c0d003a"
+ integrity sha512-xJS7QSQIVU6VK9HuJ/ieE5yynxKhjCCkd96NLY/BX/HXsx0CskU9JJiMQbd4cHALiddMwI4OWh1IIzeWrsavJw==
dependencies:
- istanbul-api "^1.1.14"
+ istanbul-api "^2.0.5"
minimatch "^3.0.4"
karma-jasmine@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.2.tgz#394f2b25ffb4a644b9ada6f22d443e2fd08886c3"
+ integrity sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=
+
+karma-junit-reporter@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/karma-junit-reporter/-/karma-junit-reporter-1.2.0.tgz#4f9c40cedfb1a395f8aef876abf96189917c6396"
+ integrity sha1-T5xAzt+xo5X4rvh2q/lhiZF8Y5Y=
+ dependencies:
+ path-is-absolute "^1.0.0"
+ xmlbuilder "8.2.2"
karma-mocha-reporter@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz#15120095e8ed819186e47a0b012f3cd741895560"
+ integrity sha1-FRIAlejtgZGG5HoLAS8810GJVWA=
dependencies:
chalk "^2.1.0"
log-symbols "^2.1.0"
@@ -4674,23 +5002,25 @@ karma-mocha-reporter@^2.2.5:
karma-sourcemap-loader@^0.3.7:
version "0.3.7"
resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz#91322c77f8f13d46fed062b042e1009d4c4505d8"
+ integrity sha1-kTIsd/jxPUb+0GKwQuEAnUxFBdg=
dependencies:
graceful-fs "^4.1.2"
karma-webpack@^4.0.0-beta.0:
- version "4.0.0-beta.0"
- resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-4.0.0-beta.0.tgz#2b386df6c364f588f896ffbdae57c2e51513d1ba"
+ version "4.0.0-rc.2"
+ resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-4.0.0-rc.2.tgz#4c194e94789842af7f0ffa0de77ee7715739c7c1"
+ integrity sha512-Wuiq/xFBsbJMsHhYy5SYXxSp7Q0b8uzAG8+Siuo56ntoi5GluPE5LK3Mzl2UtD4k1leFwL6IeIE6Q+tk4F6k9Q==
dependencies:
async "^2.0.0"
- babel-runtime "^6.0.0"
- loader-utils "^1.0.0"
- lodash "^4.0.0"
+ loader-utils "^1.1.0"
+ lodash "^4.17.10"
source-map "^0.5.6"
- webpack-dev-middleware "^3.0.1"
+ webpack-dev-middleware "^3.2.0"
-karma@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.4.tgz#b399785f57e9bab1d3c4384db33fef4dec8ae349"
+karma@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/karma/-/karma-3.0.0.tgz#6da83461a8a28d8224575c3b5b874e271b4730c3"
+ integrity sha512-ZTjyuDXVXhXsvJ1E4CnZzbCjSxD6sEdzEsFYogLuZM0yqvg/mgz+O+R1jb0J7uAQeuzdY8kJgx6hSNXLwFuHIQ==
dependencies:
bluebird "^3.3.0"
body-parser "^1.16.1"
@@ -4707,132 +5037,127 @@ karma@^2.0.4:
http-proxy "^1.13.0"
isbinaryfile "^3.0.0"
lodash "^4.17.4"
- log4js "^2.5.3"
- mime "^1.3.4"
+ log4js "^3.0.0"
+ mime "^2.3.1"
minimatch "^3.0.2"
optimist "^0.6.1"
qjobs "^1.1.4"
range-parser "^1.2.0"
rimraf "^2.6.0"
safe-buffer "^5.0.1"
- socket.io "2.0.4"
+ socket.io "2.1.1"
source-map "^0.6.1"
tmp "0.0.33"
useragent "2.2.1"
-katex@^0.8.3:
- version "0.8.3"
- resolved "https://registry.yarnpkg.com/katex/-/katex-0.8.3.tgz#909d99864baf964c3ccae39c4a99a8e0fb9a1bd0"
+katex@^0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/katex/-/katex-0.9.0.tgz#26a7d082c21d53725422d2d71da9b2d8455fbd4a"
+ integrity sha512-lp3x90LT1tDZBW2tjLheJ98wmRMRjUHwk4QpaswT9bhqoQZ+XA4cPcjcQBxgOQNwaOSt6ZeL/a6GKQ1of3LFxQ==
dependencies:
- match-at "^0.1.0"
+ match-at "^0.1.1"
keyv@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373"
+ integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==
dependencies:
json-buffer "3.0.0"
killable@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b"
+ integrity sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
dependencies:
is-buffer "^1.1.5"
kind-of@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+ integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
dependencies:
is-buffer "^1.1.5"
kind-of@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+ integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
kind-of@^6.0.0, kind-of@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
+ integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
latest-version@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15"
+ integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=
dependencies:
package-json "^4.0.0"
-lazy-cache@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
-
lazy-cache@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264"
+ integrity sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=
dependencies:
set-getter "^0.1.0"
-lcid@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
+lcid@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
+ integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==
dependencies:
- invert-kv "^1.0.0"
+ invert-kv "^2.0.0"
levn@^0.3.0, levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
dependencies:
prelude-ls "~1.1.2"
type-check "~0.3.2"
-libbase64@0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-0.1.0.tgz#62351a839563ac5ff5bd26f12f60e9830bb751e6"
-
-libmime@3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/libmime/-/libmime-3.0.0.tgz#51a1a9e7448ecbd32cda54421675bb21bc093da6"
- dependencies:
- iconv-lite "0.4.15"
- libbase64 "0.1.0"
- libqp "1.1.0"
-
-libqp@1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8"
-
lie@~3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
+ integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
dependencies:
immediate "~3.0.5"
-load-json-file@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
- dependencies:
- graceful-fs "^4.1.2"
- parse-json "^2.2.0"
- pify "^2.0.0"
- pinkie-promise "^2.0.0"
- strip-bom "^2.0.0"
-
load-json-file@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+ integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=
dependencies:
graceful-fs "^4.1.2"
parse-json "^2.2.0"
pify "^2.0.0"
strip-bom "^3.0.0"
+load-json-file@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
+ integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs=
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^4.0.0"
+ pify "^3.0.0"
+ strip-bom "^3.0.0"
+
loader-runner@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
+ integrity sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=
loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
+ integrity sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=
dependencies:
big.js "^3.1.3"
emojis-list "^2.0.0"
@@ -4841,124 +5166,113 @@ loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0:
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
dependencies:
p-locate "^2.0.0"
path-exists "^3.0.0"
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
lodash.camelcase@4.3.0, lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+ integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+ integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+ integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.escaperegexp@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
+ integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+ integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+ integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.kebabcase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
+ integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY=
lodash.mergewith@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
+ integrity sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=
lodash.snakecase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
+ integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=
lodash.startcase@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8"
+ integrity sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=
lodash.upperfirst@4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
+ integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=
-lodash@4.17.4:
- version "4.17.4"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
-
-lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
- version "4.17.10"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
+lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0:
+ version "4.17.11"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+ integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
log-symbols@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
+ integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
dependencies:
chalk "^2.0.1"
-log4js@^2.5.3:
- version "2.11.0"
- resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.11.0.tgz#bf3902eff65c6923d9ce9cfbd2db54160e34005a"
+log4js@^3.0.0:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/log4js/-/log4js-3.0.5.tgz#b80146bfebad68b430d4f3569556d8a6edfef303"
+ integrity sha512-IX5c3G/7fuTtdr0JjOT2OIR12aTESVhsH6cEsijloYwKgcPRlO6DgOU72v0UFhWcoV1HN6+M3dwT89qVPLXm0w==
dependencies:
- circular-json "^0.5.4"
+ circular-json "^0.5.5"
date-format "^1.2.0"
debug "^3.1.0"
- semver "^5.5.0"
+ rfdc "^1.1.2"
streamroller "0.7.0"
- optionalDependencies:
- amqplib "^0.5.2"
- axios "^0.15.3"
- hipchat-notifier "^1.1.0"
- loggly "^1.1.0"
- mailgun-js "^0.18.0"
- nodemailer "^2.5.0"
- redis "^2.7.1"
- slack-node "~0.2.0"
-
-loggly@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/loggly/-/loggly-1.1.1.tgz#0a0fc1d3fa3a5ec44fdc7b897beba2a4695cebee"
- dependencies:
- json-stringify-safe "5.0.x"
- request "2.75.x"
- timespan "2.3.x"
loglevel@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.4.1.tgz#95b383f91a3c2756fd4ab093667e4309161f2bcd"
-
-loglevelnext@^1.0.1:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.3.tgz#0f69277e73bbbf2cd61b94d82313216bf87ac66e"
-
-long@4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
-
-long@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
-
-longest@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
+ integrity sha1-lbOD+Ro8J1b9SrCTZn5DCRYfK80=
loose-envify@^1.0.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
- js-tokens "^3.0.0"
+ js-tokens "^3.0.0 || ^4.0.0"
-loud-rejection@^1.0.0, loud-rejection@^1.6.0:
+loud-rejection@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
+ integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=
dependencies:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
@@ -4966,14 +5280,17 @@ loud-rejection@^1.0.0, loud-rejection@^1.6.0:
lowercase-keys@1.0.0, lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
+ integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=
lru-cache@2.2.x:
version "2.2.4"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
+ integrity sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=
-lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2:
+lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2, lru-cache@^4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
+ integrity sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==
dependencies:
pseudomap "^1.0.2"
yallist "^2.1.2"
@@ -4981,67 +5298,58 @@ lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2:
lz-string@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
+ integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
-mailcomposer@4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-4.0.1.tgz#0e1c44b2a07cf740ee17dc149ba009f19cadfeb4"
- dependencies:
- buildmail "4.0.1"
- libmime "3.0.0"
-
-mailgun-js@^0.18.0:
- version "0.18.1"
- resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.18.1.tgz#ee39aa18d7bb598a5ce9ede84afb681defc8a6b0"
- dependencies:
- async "~2.6.0"
- debug "~3.1.0"
- form-data "~2.3.0"
- inflection "~1.12.0"
- is-stream "^1.1.0"
- path-proxy "~1.0.0"
- promisify-call "^2.0.2"
- proxy-agent "~3.0.0"
- tsscmp "~1.0.0"
-
-make-dir@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
+make-dir@^1.0.0, make-dir@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
+ integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
dependencies:
pify "^3.0.0"
mamacro@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4"
+ integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==
+
+map-age-cleaner@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz#098fb15538fd3dbe461f12745b0ca8568d4e3f74"
+ integrity sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==
+ dependencies:
+ p-defer "^1.0.0"
map-cache@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
-
-map-obj@^1.0.0, map-obj@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
+ integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
map-stream@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
+ integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=
map-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+ integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
dependencies:
object-visit "^1.0.0"
marked@^0.3.12:
version "0.3.12"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519"
+ integrity sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA==
-match-at@^0.1.0:
+match-at@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.1.tgz#25d040d291777704d5e6556bbb79230ec2de0540"
+ integrity sha512-h4Yd392z9mST+dzc+yjuybOGFNOZjmXIPKWjxBd1Bb23r4SmDOsk2NYCU2BMUBGbSpZqwVsZYNq26QS3xfaT3Q==
md5.js@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d"
+ integrity sha1-6b296UogpawYsENA/Fdk1bCdkB0=
dependencies:
hash-base "^3.0.0"
inherits "^2.0.1"
@@ -5049,56 +5357,65 @@ md5.js@^1.3.4:
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+ integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
-mem@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
+mem@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/mem/-/mem-4.0.0.tgz#6437690d9471678f6cc83659c00cbafcd6b0cdaf"
+ integrity sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==
dependencies:
+ map-age-cleaner "^0.1.1"
mimic-fn "^1.0.0"
+ p-is-promise "^1.1.0"
memory-fs@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290"
+ integrity sha1-8rslNovBIeORwlIN6Slpyu4KApA=
memory-fs@^0.4.0, memory-fs@~0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
+ integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=
dependencies:
errno "^0.1.3"
readable-stream "^2.0.1"
-meow@^3.3.0:
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
- dependencies:
- camelcase-keys "^2.0.0"
- decamelize "^1.1.2"
- loud-rejection "^1.0.0"
- map-obj "^1.0.1"
- minimist "^1.1.3"
- normalize-package-data "^2.3.4"
- object-assign "^4.0.1"
- read-pkg-up "^1.0.1"
- redent "^1.0.0"
- trim-newlines "^1.0.0"
-
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+ integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
merge-source-map@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646"
+ integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==
dependencies:
source-map "^0.6.1"
+mermaid@^8.0.0-rc.8:
+ version "8.0.0-rc.8"
+ resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.0.0-rc.8.tgz#74ed54d0d46e9ee71c4db2730b2d83d516a21e72"
+ integrity sha512-GbF9jHWfqE7YGx9vQySmBxy2Ahlclxmpk4tJ9ntNyafENl96s96ggUK/NQS5ydYoFab6MavTm4YMTIPKqWVvPQ==
+ dependencies:
+ d3 "^4.13.0"
+ dagre-d3-renderer "^0.5.8"
+ dagre-layout "^0.8.8"
+ graphlibrary "^2.2.0"
+ he "^1.1.1"
+ lodash "^4.17.5"
+ moment "^2.21.0"
+ scope-css "^1.0.5"
+
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+ integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+ integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
dependencies:
arr-diff "^4.0.0"
array-unique "^0.3.2"
@@ -5117,6 +5434,7 @@ micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9:
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
+ integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
dependencies:
bn.js "^4.0.0"
brorand "^1.0.1"
@@ -5124,62 +5442,71 @@ miller-rabin@^4.0.0:
"mime-db@>= 1.29.0 < 2", mime-db@~1.33.0:
version "1.33.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
+ integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
-mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7:
+mime-types@~2.1.15, mime-types@~2.1.18:
version "2.1.18"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
+ integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==
dependencies:
mime-db "~1.33.0"
mime@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
+ integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
-mime@^1.3.4:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
-
-mime@^2.0.3, mime@^2.1.0:
+mime@^2.0.3, mime@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369"
+ integrity sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==
mimic-fn@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
+ integrity sha1-5md4PZLonb00KBi1IwudYqZyrRg=
mimic-response@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e"
+ integrity sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=
minimalistic-assert@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
+ integrity sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=
minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+ integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
minimist@0.0.8:
version "0.0.8"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+ resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+ integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0:
+minimist@1.2.0, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+ integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
minimist@~0.0.1:
version "0.0.10"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+ resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+ integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
minipass@^2.2.1, minipass@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233"
+ integrity sha512-/jAn9/tEX4gnpyRATxgHEOV6xbcyxgT7iUnxo9Y3+OB0zX00TgKIv/2FZCf5brBbICcwbLqVv2ImjvWWrQMSYw==
dependencies:
safe-buffer "^5.1.2"
yallist "^3.0.0"
@@ -5187,12 +5514,14 @@ minipass@^2.2.1, minipass@^2.3.3:
minizlib@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb"
+ integrity sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==
dependencies:
minipass "^2.2.1"
mississippi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f"
+ integrity sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==
dependencies:
concat-stream "^1.5.0"
duplexify "^3.4.2"
@@ -5205,38 +5534,61 @@ mississippi@^2.0.0:
stream-each "^1.1.0"
through2 "^2.0.0"
+mississippi@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
+ integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==
+ dependencies:
+ concat-stream "^1.5.0"
+ duplexify "^3.4.2"
+ end-of-stream "^1.1.0"
+ flush-write-stream "^1.0.0"
+ from2 "^2.1.0"
+ parallel-transform "^1.1.0"
+ pump "^3.0.0"
+ pumpify "^1.3.3"
+ stream-each "^1.1.0"
+ through2 "^2.0.0"
+
mixin-deep@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
+ integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==
dependencies:
for-in "^1.0.2"
is-extendable "^1.0.1"
mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
version "0.5.1"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
-moment@2.x, moment@^2.18.1:
- version "2.19.2"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
+moment@2.x, moment@^2.21.0:
+ version "2.22.2"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
+ integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=
-monaco-editor-webpack-plugin@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.4.0.tgz#7324258ab3695464cfe3bc12edb2e8c55b80d92f"
+monaco-editor-webpack-plugin@^1.5.4:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.5.4.tgz#6781a130e3e1379bb8f4cd190132f4af6dcd2c16"
+ integrity sha512-9YmWYQdZoAoZ1RLy/uvoDbCcb0EKy5O2qoMQn+UIVQxk+VTCXfJDgANczDIWko+UOzg0MY0P+sA8bl4XI14RJg==
-monaco-editor@0.13.1:
- version "0.13.1"
- resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.13.1.tgz#6b9ce20e4d1c945042d256825eb133cb23315a52"
+monaco-editor@^0.14.3:
+ version "0.14.3"
+ resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.14.3.tgz#7cc4a4096a3821f52fea9b10489b527ef3034e22"
+ integrity sha512-RhaO4xXmWn/p0WrkEOXe4PoZj6xOcvDYjoAh0e1kGUrQnP1IOpc0m86Ceuaa2CLEMDINqKijBSmqhvBQnsPLHQ==
mousetrap@^1.4.6:
version "1.4.6"
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.4.6.tgz#eaca72e22e56d5b769b7555873b688c3332e390a"
+ integrity sha1-6spy4i5W1bdpt1VYc7aIwzMuOQo=
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
+ integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=
dependencies:
aproba "^1.1.1"
copy-concurrently "^1.0.0"
@@ -5248,14 +5600,22 @@ move-concurrently@^1.0.1:
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+ integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
multicast-dns-service-types@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
+ integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=
multicast-dns@^6.0.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.1.1.tgz#6e7de86a570872ab17058adea7160bbeca814dde"
+ integrity sha1-bn3oalcIcqsXBYrepxYLvsqBTd4=
dependencies:
dns-packet "^1.0.1"
thunky "^0.1.0"
@@ -5263,14 +5623,17 @@ multicast-dns@^6.0.1:
mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+ integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
nan@^2.9.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
+ integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==
nanomatch@^1.2.9:
version "1.2.9"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2"
+ integrity sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==
dependencies:
arr-diff "^4.0.0"
array-unique "^0.3.2"
@@ -5288,10 +5651,12 @@ nanomatch@^1.2.9:
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
needle@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d"
+ integrity sha512-t/ZswCM9JTWjAdXS9VpvqhI2Ct2sL2MdY4fUXqGJaGBk13ge99ObqRksRTbBE56K+wxUXwwfZYOuZHifFW9q+Q==
dependencies:
debug "^2.1.2"
iconv-lite "^0.4.4"
@@ -5300,22 +5665,22 @@ needle@^2.2.0:
negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
+ integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
neo-async@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f"
-
-netmask@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
+ integrity sha512-nJmSswG4As/MkRq7QZFuH/sf/yuv8ODdMZrY4Bedjp77a5MK4A6s7YbBB64c9u79EBUOfXUXBvArmvzTD0X+6g==
nice-try@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
+ integrity sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==
node-fetch@1.6.3:
version "1.6.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
+ integrity sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
@@ -5323,10 +5688,12 @@ node-fetch@1.6.3:
node-forge@0.6.33:
version "0.6.33"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc"
+ integrity sha1-RjgRh59XPUUVWtap9D3ClujoXrw=
"node-libs-browser@^1.0.0 || ^2.0.0", node-libs-browser@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df"
+ integrity sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==
dependencies:
assert "^1.1.1"
browserify-zlib "^0.2.0"
@@ -5355,6 +5722,7 @@ node-forge@0.6.33:
node-pre-gyp@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz#6e4ef5bb5c5203c6552448828c852c40111aac46"
+ integrity sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
@@ -5367,62 +5735,17 @@ node-pre-gyp@^0.10.0:
semver "^5.3.0"
tar "^4"
-node-uuid@~1.4.7:
- version "1.4.8"
- resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907"
-
-nodemailer-direct-transport@3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz#e96fafb90358560947e569017d97e60738a50a86"
- dependencies:
- nodemailer-shared "1.1.0"
- smtp-connection "2.12.0"
-
-nodemailer-fetch@1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz#79c4908a1c0f5f375b73fe888da9828f6dc963a4"
-
-nodemailer-shared@1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz#cf5994e2fd268d00f5cf0fa767a08169edb07ec0"
- dependencies:
- nodemailer-fetch "1.6.0"
-
-nodemailer-smtp-pool@2.8.2:
- version "2.8.2"
- resolved "https://registry.yarnpkg.com/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz#2eb94d6cf85780b1b4725ce853b9cbd5e8da8c72"
- dependencies:
- nodemailer-shared "1.1.0"
- nodemailer-wellknown "0.1.10"
- smtp-connection "2.12.0"
-
-nodemailer-smtp-transport@2.7.2:
- version "2.7.2"
- resolved "https://registry.yarnpkg.com/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz#03d71c76314f14ac7dbc7bf033a6a6d16d67fb77"
- dependencies:
- nodemailer-shared "1.1.0"
- nodemailer-wellknown "0.1.10"
- smtp-connection "2.12.0"
-
-nodemailer-wellknown@0.1.10:
- version "0.1.10"
- resolved "https://registry.yarnpkg.com/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz#586db8101db30cb4438eb546737a41aad0cf13d5"
-
-nodemailer@^2.5.0:
- version "2.7.2"
- resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-2.7.2.tgz#f242e649aeeae39b6c7ed740ef7b061c404d30f9"
+node-releases@^1.0.0-alpha.11:
+ version "1.0.0-alpha.12"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.0-alpha.12.tgz#32e461b879ea76ac674e511d9832cf29da345268"
+ integrity sha512-VPB4rTPqpVyWKBHbSa4YPFme3+8WHsOSpvbp0Mfj0bWsC8TEjt4HQrLl1hsBDELlp1nB4lflSgSuGTYiuyaP7Q==
dependencies:
- libmime "3.0.0"
- mailcomposer "4.0.1"
- nodemailer-direct-transport "3.3.2"
- nodemailer-shared "1.1.0"
- nodemailer-smtp-pool "2.8.2"
- nodemailer-smtp-transport "2.7.2"
- socks "1.1.9"
+ semver "^5.3.0"
-nodemon@^1.18.2:
- version "1.18.2"
- resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.2.tgz#36b89c790da70c4f270e2cc0718723131bc04abb"
+nodemon@^1.18.4:
+ version "1.18.4"
+ resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.4.tgz#873f65fdb53220eb166180cf106b1354ac5d714d"
+ integrity sha512-hyK6vl65IPnky/ee+D3IWvVGgJa/m3No2/Xc/3wanS6Ce1MWjCzH6NnhPJ/vZM+6JFym16jtHx51lmCMB9HDtg==
dependencies:
chokidar "^2.0.2"
debug "^3.1.0"
@@ -5438,12 +5761,14 @@ nodemon@^1.18.2:
nopt@3.x:
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
+ integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
dependencies:
abbrev "1"
-nopt@^4.0.1:
+nopt@^4.0.1, nopt@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+ integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
dependencies:
abbrev "1"
osenv "^0.1.4"
@@ -5451,12 +5776,14 @@ nopt@^4.0.1:
nopt@~1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
+ integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=
dependencies:
abbrev "1"
-normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
+normalize-package-data@^2.3.2:
version "2.4.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
+ integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==
dependencies:
hosted-git-info "^2.1.4"
is-builtin-module "^1.0.0"
@@ -5466,12 +5793,14 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+ integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
dependencies:
remove-trailing-separator "^1.0.1"
normalize-url@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
+ integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
dependencies:
prepend-http "^2.0.0"
query-string "^5.0.1"
@@ -5480,10 +5809,12 @@ normalize-url@2.0.1:
npm-bundled@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308"
+ integrity sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==
npm-packlist@^1.1.6:
version "1.1.10"
resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.10.tgz#1039db9e985727e464df066f4cf0ab6ef85c398a"
+ integrity sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==
dependencies:
ignore-walk "^3.0.1"
npm-bundled "^1.0.1"
@@ -5491,12 +5822,14 @@ npm-packlist@^1.1.6:
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+ integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
dependencies:
path-key "^2.0.0"
npmlog@^4.0.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+ integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
dependencies:
are-we-there-yet "~1.1.2"
console-control-strings "~1.1.0"
@@ -5506,76 +5839,114 @@ npmlog@^4.0.2:
null-check@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
+ integrity sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
-
-oauth-sign@~0.8.1, oauth-sign@~0.8.2:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+ integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-component@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
+ integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=
object-copy@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+ integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
dependencies:
copy-descriptor "^0.1.0"
define-property "^0.2.5"
kind-of "^3.0.3"
-object-keys@^1.0.8:
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
+object-keys@^1.0.11, object-keys@^1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2"
+ integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==
object-visit@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+ integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
dependencies:
isobject "^3.0.0"
+object.assign@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+ integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+ dependencies:
+ define-properties "^1.1.2"
+ function-bind "^1.1.1"
+ has-symbols "^1.0.0"
+ object-keys "^1.0.11"
+
+object.entries@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f"
+ integrity sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=
+ dependencies:
+ define-properties "^1.1.2"
+ es-abstract "^1.6.1"
+ function-bind "^1.1.0"
+ has "^1.0.1"
+
+object.getownpropertydescriptors@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
+ integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=
+ dependencies:
+ define-properties "^1.1.2"
+ es-abstract "^1.5.1"
+
object.pick@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+ integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
dependencies:
isobject "^3.0.1"
obuf@^1.0.0, obuf@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e"
+ integrity sha1-EEEktsYCxnlogaBCVB0220OlJk4=
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+ integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
dependencies:
ee-first "1.1.1"
on-headers@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
+ integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=
once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
onetime@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+ integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
dependencies:
mimic-fn "^1.0.0"
opencollective@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1"
+ integrity sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE=
dependencies:
babel-polyfill "6.23.0"
chalk "1.1.3"
@@ -5584,13 +5955,15 @@ opencollective@^1.0.3:
node-fetch "1.6.3"
opn "4.0.2"
-opener@^1.4.3:
- version "1.4.3"
- resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
+opener@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed"
+ integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==
opn@4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
+ integrity sha1-erwi5kTf9jsKltWrfyeQwPAavJU=
dependencies:
object-assign "^4.0.1"
pinkie-promise "^2.0.0"
@@ -5598,12 +5971,14 @@ opn@4.0.2:
opn@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225"
+ integrity sha512-Jd/GpzPyHF4P2/aNOVmS3lfMSWV9J7cOhCG1s08XCEAsPkB7lp6ddiU0J7XzyQRDUh8BqJ7PchfINjR8jyofRQ==
dependencies:
is-wsl "^1.1.0"
optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+ integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
dependencies:
minimist "~0.0.1"
wordwrap "~0.0.2"
@@ -5611,6 +5986,7 @@ optimist@^0.6.1:
optionator@^0.8.1, optionator@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+ integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=
dependencies:
deep-is "~0.1.3"
fast-levenshtein "~2.0.4"
@@ -5622,32 +5998,38 @@ optionator@^0.8.1, optionator@^0.8.2:
original@>=0.0.5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b"
+ integrity sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=
dependencies:
url-parse "1.0.x"
os-browserify@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
+ integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
os-homedir@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+ integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
-os-locale@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
+os-locale@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.0.1.tgz#3b014fbf01d87f60a1e5348d80fe870dc82c4620"
+ integrity sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==
dependencies:
- execa "^0.7.0"
- lcid "^1.0.0"
- mem "^1.1.0"
+ execa "^0.10.0"
+ lcid "^2.0.0"
+ mem "^4.0.0"
-os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
+os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
osenv@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
+ integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
dependencies:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
@@ -5655,67 +6037,77 @@ osenv@^0.1.4:
p-cancelable@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0"
+ integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==
+
+p-defer@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
+ integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+ integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
p-is-promise@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e"
+ integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=
p-limit@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c"
+ integrity sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==
dependencies:
p-try "^1.0.0"
+p-limit@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec"
+ integrity sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==
+ dependencies:
+ p-try "^2.0.0"
+
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
dependencies:
p-limit "^1.1.0"
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+ dependencies:
+ p-limit "^2.0.0"
+
p-map@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a"
+ integrity sha1-BfXkrpegaDcbwqXMhr+9vBnErno=
p-timeout@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038"
+ integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==
dependencies:
p-finally "^1.0.0"
p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+ integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
-pac-proxy-agent@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz#90d9f6730ab0f4d2607dcdcd4d3d641aa26c3896"
- dependencies:
- agent-base "^4.2.0"
- debug "^3.1.0"
- get-uri "^2.0.0"
- http-proxy-agent "^2.1.0"
- https-proxy-agent "^2.2.1"
- pac-resolver "^3.0.0"
- raw-body "^2.2.0"
- socks-proxy-agent "^3.0.0"
-
-pac-resolver@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26"
- dependencies:
- co "^4.6.0"
- degenerator "^1.0.4"
- ip "^1.1.5"
- netmask "^1.0.6"
- thunkify "^2.1.2"
+p-try@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
+ integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
package-json@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed"
+ integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=
dependencies:
got "^6.7.1"
registry-auth-token "^3.0.1"
@@ -5725,10 +6117,12 @@ package-json@^4.0.0:
pako@~1.0.2, pako@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
+ integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==
parallel-transform@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06"
+ integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=
dependencies:
cyclist "~0.2.2"
inherits "^2.0.3"
@@ -5737,6 +6131,7 @@ parallel-transform@^1.1.0:
parse-asn1@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712"
+ integrity sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=
dependencies:
asn1.js "^4.0.0"
browserify-aes "^1.0.0"
@@ -5747,96 +6142,119 @@ parse-asn1@^5.0.0:
parse-json@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
dependencies:
error-ex "^1.2.0"
+parse-json@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+ integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+ dependencies:
+ error-ex "^1.3.1"
+ json-parse-better-errors "^1.0.1"
+
+parse5@^5:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.0.0.tgz#4d02710d44f3c3846197a11e205d4ef17842b81a"
+ integrity sha512-0ywuiUOnpWWeil5grH2rxjyTJoeQVwyBuO2si6QIU9dWtj2npjuyK1HaY1RbLnVfDhEbhyAPNUBKRK0Xj2xE0w==
+
parseqs@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
+ integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=
dependencies:
better-assert "~1.0.0"
parseuri@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
+ integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=
dependencies:
better-assert "~1.0.0"
parseurl@~1.3.1, parseurl@~1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
+ integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=
pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+ integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
path-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
+ integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=
path-dirname@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
+ integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
path-exists@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=
dependencies:
pinkie-promise "^2.0.0"
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
-path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
+path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
path-is-inside@^1.0.1, path-is-inside@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+ integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+ integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
path-parse@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
-
-path-proxy@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/path-proxy/-/path-proxy-1.0.0.tgz#18e8a36859fc9d2f1a53b48dee138543c020de5e"
- dependencies:
- inflection "~1.3.0"
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
-
-path-type@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
- dependencies:
- graceful-fs "^4.1.2"
- pify "^2.0.0"
- pinkie-promise "^2.0.0"
+ integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
path-type@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
+ integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=
dependencies:
pify "^2.0.0"
+path-type@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
+ integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
+ dependencies:
+ pify "^3.0.0"
+
pause-stream@0.0.11:
version "0.0.11"
resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
+ integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=
dependencies:
through "~2.3"
pbkdf2@^3.0.3:
version "3.0.14"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.14.tgz#a35e13c64799b06ce15320f459c230e68e73bade"
+ integrity sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==
dependencies:
create-hash "^1.1.2"
create-hmac "^1.1.4"
@@ -5844,57 +6262,75 @@ pbkdf2@^3.0.3:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-performance-now@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
-
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+ integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
pikaday@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/pikaday/-/pikaday-1.6.1.tgz#b91bcb9b8539cedd8d6d08e4e7465e12095671b0"
+ integrity sha512-B+pxVcSGuzLblMe4dnhCF3dnI2zkyj5GAqanGX9cVcOk90fp2ULo1OZFUPRXQXUE5tmcimnk1tPOFs8tUHQetQ==
optionalDependencies:
moment "2.x"
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
dependencies:
pinkie "^2.0.0"
pinkie@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+ integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
pkg-dir@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
+ integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q=
dependencies:
find-up "^1.0.0"
pkg-dir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
+ integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
dependencies:
find-up "^2.1.0"
+pkg-dir@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+ integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+ dependencies:
+ find-up "^3.0.0"
+
pluralize@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
+ integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==
+
+pofile@^1:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.11.tgz#35aff58c17491d127a07336d5522ebc9df57c954"
+ integrity sha512-Vy9eH1dRD9wHjYt/QqXcTz+RnX/zg53xK+KljFSX30PvdDMb2z+c6uDUeblUGqqJgz3QFsdlA0IJvHziPmWtQg==
popper.js@^1.12.9, popper.js@^1.14.3:
version "1.14.3"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.3.tgz#1438f98d046acf7b4d78cd502bf418ac64d4f095"
+ integrity sha1-FDj5jQRqz3tNeM1QK/QYrGTU8JU=
portfinder@^1.0.9:
version "1.0.13"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
+ integrity sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=
dependencies:
async "^1.5.2"
debug "^2.2.0"
@@ -5903,16 +6339,19 @@ portfinder@^1.0.9:
posix-character-classes@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+ integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
postcss-modules-extract-imports@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85"
+ integrity sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=
dependencies:
postcss "^6.0.1"
postcss-modules-local-by-default@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
+ integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=
dependencies:
css-selector-tokenizer "^0.7.0"
postcss "^6.0.1"
@@ -5920,6 +6359,7 @@ postcss-modules-local-by-default@^1.2.0:
postcss-modules-scope@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
+ integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A=
dependencies:
css-selector-tokenizer "^0.7.0"
postcss "^6.0.1"
@@ -5927,6 +6367,7 @@ postcss-modules-scope@^1.1.0:
postcss-modules-values@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
+ integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=
dependencies:
icss-replace-symbols "^1.1.0"
postcss "^6.0.1"
@@ -5934,6 +6375,7 @@ postcss-modules-values@^1.3.0:
postcss-selector-parser@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
+ integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=
dependencies:
dot-prop "^4.1.1"
indexes-of "^1.0.1"
@@ -5942,18 +6384,12 @@ postcss-selector-parser@^3.1.1:
postcss-value-parser@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
+ integrity sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=
-postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.20:
- version "6.0.22"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.22.tgz#e23b78314905c3b90cbd61702121e7a78848f2a3"
- dependencies:
- chalk "^2.4.1"
- source-map "^0.6.1"
- supports-color "^5.4.0"
-
-postcss@^6.0.23:
+postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.20, postcss@^6.0.23:
version "6.0.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+ integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
dependencies:
chalk "^2.4.1"
source-map "^0.6.1"
@@ -5962,106 +6398,106 @@ postcss@^6.0.23:
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+ integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
prepend-http@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
+ integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
+ integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
+
+prettier@1.13.7:
+ version "1.13.7"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281"
+ integrity sha512-KIU72UmYPGk4MujZGYMFwinB7lOf2LsDNGSOC8ufevsrPLISrZbNJlWstRi3m0AMuszbH+EFSQ/r6w56RSPK6w==
-prettier@1.12.1, prettier@^1.11.1:
- version "1.12.1"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
+prettier@1.14.3:
+ version "1.14.3"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895"
+ integrity sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==
prismjs@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.6.0.tgz#118d95fb7a66dba2272e343b345f5236659db365"
+ integrity sha1-EY2V+3pm26InLjQ7NF9SNmWds2U=
optionalDependencies:
clipboard "^1.5.5"
-private@^0.1.6, private@^0.1.8:
+private@^0.1.6:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+ integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
process-nextick-args@~1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+ integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
+ integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
process@^0.11.10:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+ integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
progress@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
+ integrity sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=
promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
+ integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
-promisify-call@^2.0.2:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/promisify-call/-/promisify-call-2.0.4.tgz#d48c2d45652ccccd52801ddecbd533a6d4bd5fba"
- dependencies:
- with-callback "^1.0.2"
+proto-list@~1.2.1:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
+ integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
-proxy-addr@~2.0.2:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
+proxy-addr@~2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
+ integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==
dependencies:
forwarded "~0.1.2"
- ipaddr.js "1.6.0"
-
-proxy-agent@~3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.0.1.tgz#4fb7b61b1476d0fe8e3a3384d90e2460bbded3f9"
- dependencies:
- agent-base "^4.2.0"
- debug "^3.1.0"
- http-proxy-agent "^2.1.0"
- https-proxy-agent "^2.2.1"
- lru-cache "^4.1.2"
- pac-proxy-agent "^2.0.1"
- proxy-from-env "^1.0.0"
- socks-proxy-agent "^4.0.1"
-
-proxy-from-env@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
-
-prr@~0.0.0:
- version "0.0.0"
- resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
+ ipaddr.js "1.8.0"
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
+ integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
ps-tree@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014"
+ integrity sha1-tCGyQUDWID8e08dplrRCewjowBQ=
dependencies:
event-stream "~3.3.0"
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+ integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
pstree.remy@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.0.tgz#f2af27265bd3e5b32bbfcc10e80bac55ba78688b"
+ integrity sha512-q5I5vLRMVtdWa8n/3UEzZX7Lfghzrg9eG2IKk2ENLSofKRCXVqMvMUHxCKgXNaqH/8ebhBxrqftHWnyTFweJ5Q==
dependencies:
ps-tree "^1.1.0"
public-encrypt@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6"
+ integrity sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=
dependencies:
bn.js "^4.1.0"
browserify-rsa "^4.0.0"
@@ -6072,6 +6508,15 @@ public-encrypt@^4.0.0:
pump@^2.0.0, pump@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
+ integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+pump@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+ integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
@@ -6079,34 +6524,36 @@ pump@^2.0.0, pump@^2.0.1:
pumpify@^1.3.3:
version "1.4.0"
resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb"
+ integrity sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==
dependencies:
duplexify "^3.5.3"
inherits "^2.0.3"
pump "^2.0.0"
-punycode@1.3.2:
+punycode@1.3.2, punycode@^1.2.4:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+ integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
-punycode@1.4.1, punycode@^1.2.4, punycode@^1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qjobs@^1.1.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071"
+ integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==
-qs@6.5.1, qs@~6.5.1:
+qs@6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
-
-qs@~6.2.0:
- version "6.2.3"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe"
+ integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==
query-string@^5.0.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
+ integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
dependencies:
decode-uri-component "^0.2.0"
object-assign "^4.1.0"
@@ -6115,28 +6562,34 @@ query-string@^5.0.1:
querystring-es3@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
+ integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
querystring@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+ integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
querystringify@0.0.x:
version "0.0.4"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c"
+ integrity sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=
querystringify@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb"
+ integrity sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80"
+ integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==
dependencies:
safe-buffer "^5.1.0"
randomfill@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
+ integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
dependencies:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
@@ -6144,102 +6597,83 @@ randomfill@^1.0.3:
range-parser@^1.0.3, range-parser@^1.2.0, range-parser@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
+ integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
raphael@^2.2.7:
version "2.2.7"
resolved "https://registry.yarnpkg.com/raphael/-/raphael-2.2.7.tgz#231b19141f8d086986d8faceb66f8b562ee2c810"
+ integrity sha1-IxsZFB+NCGmG2PrOtm+LVi7iyBA=
dependencies:
eve-raphael "0.5.0"
raven-js@^3.22.1:
version "3.22.1"
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.1.tgz#1117f00dfefaa427ef6e1a7d50bbb1fb998a24da"
+ integrity sha512-2Y8czUl5a9usbvXbpV8a+GPAiDXjxQjaHImZL0TyJWI5k5jV/6o+wceaBAg9g6RpO9OOJp0/w2mMs6pBoqOyDA==
raw-body@2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
+ integrity sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=
dependencies:
bytes "3.0.0"
http-errors "1.6.2"
iconv-lite "0.4.19"
unpipe "1.0.0"
-raw-body@^2.2.0:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
- dependencies:
- bytes "3.0.0"
- http-errors "1.6.3"
- iconv-lite "0.4.23"
- unpipe "1.0.0"
-
raw-loader@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
+ integrity sha1-DD0L6u2KAclm2Xh793goElKpeao=
rc@^1.0.1, rc@^1.1.6, rc@^1.1.7:
version "1.2.5"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.5.tgz#275cd687f6e3b36cc756baa26dfee80a790301fd"
+ integrity sha1-J1zWh/bjs2zHVrqibf7oCnkDAf0=
dependencies:
deep-extend "~0.4.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-read-pkg-up@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
- dependencies:
- find-up "^1.0.0"
- read-pkg "^1.0.0"
-
read-pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
+ integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=
dependencies:
find-up "^2.0.0"
read-pkg "^2.0.0"
-read-pkg@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+read-pkg-up@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978"
+ integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==
dependencies:
- load-json-file "^1.0.0"
- normalize-package-data "^2.3.2"
- path-type "^1.0.0"
+ find-up "^3.0.0"
+ read-pkg "^3.0.0"
read-pkg@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
+ integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=
dependencies:
load-json-file "^2.0.0"
normalize-package-data "^2.3.2"
path-type "^2.0.0"
-"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3:
- version "2.3.4"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071"
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.3"
- isarray "~1.0.0"
- process-nextick-args "~2.0.0"
- safe-buffer "~5.1.1"
- string_decoder "~1.0.3"
- util-deprecate "~1.0.1"
-
-readable-stream@1.1.x, "readable-stream@1.x >=1.1.9":
- version "1.1.14"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
+read-pkg@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
+ integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=
dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.1"
- isarray "0.0.1"
- string_decoder "~0.10.x"
+ load-json-file "^4.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^3.0.0"
-readable-stream@^2.3.6:
+"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
+ integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
@@ -6249,9 +6683,10 @@ readable-stream@^2.3.6:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
-readable-stream@~2.0.5, readable-stream@~2.0.6:
+readable-stream@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
+ integrity sha1-j5A0HmilPMySh4jaz80Rs265t44=
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
@@ -6263,81 +6698,80 @@ readable-stream@~2.0.5, readable-stream@~2.0.6:
readdirp@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
+ integrity sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=
dependencies:
graceful-fs "^4.1.2"
minimatch "^3.0.2"
readable-stream "^2.0.2"
set-immediate-shim "^1.0.1"
-redent@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
- dependencies:
- indent-string "^2.1.0"
- strip-indent "^1.0.1"
-
-redis-commands@^1.2.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.3.1.tgz#81d826f45fa9c8b2011f4cd7a0fe597d241d442b"
-
-redis-parser@^2.6.0:
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b"
-
-redis@^2.7.1:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02"
+regenerate-unicode-properties@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c"
+ integrity sha512-s5NGghCE4itSlUS+0WUj88G6cfMVMmH8boTPNvABf8od+2dhT9WDlWu8n01raQAJZMOK8Ch6jSexaRO7swd6aw==
dependencies:
- double-ended-queue "^2.1.0-0"
- redis-commands "^1.2.0"
- redis-parser "^2.6.0"
+ regenerate "^1.4.0"
-regenerate@^1.2.1:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
+regenerate@^1.2.1, regenerate@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
+ integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
regenerator-runtime@^0.10.0:
version "0.10.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+ integrity sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=
regenerator-runtime@^0.11.0:
- version "0.11.0"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
+ version "0.11.1"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+ integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
-regenerator-transform@^0.10.0:
- version "0.10.1"
- resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
+regenerator-transform@^0.13.3:
+ version "0.13.3"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb"
+ integrity sha512-5ipTrZFSq5vU2YoGoww4uaRVAK4wyYC4TSICibbfEPOruUu8FFP7ErV0BjmbIOEpn3O/k9na9UEdYR/3m7N6uA==
dependencies:
- babel-runtime "^6.18.0"
- babel-types "^6.19.0"
private "^0.1.6"
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+ integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
dependencies:
extend-shallow "^3.0.2"
safe-regex "^1.1.0"
+regexpp@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.0.tgz#b2a7534a85ca1b033bcf5ce9ff8e56d4e0755365"
+ integrity sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==
+
regexpu-core@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
+ integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=
dependencies:
regenerate "^1.2.1"
regjsgen "^0.2.0"
regjsparser "^0.1.4"
-regexpu-core@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
+regexpu-core@^4.1.3, regexpu-core@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.2.0.tgz#a3744fa03806cffe146dea4421a3e73bdcc47b1d"
+ integrity sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw==
dependencies:
- regenerate "^1.2.1"
- regjsgen "^0.2.0"
- regjsparser "^0.1.4"
+ regenerate "^1.4.0"
+ regenerate-unicode-properties "^7.0.0"
+ regjsgen "^0.4.0"
+ regjsparser "^0.3.0"
+ unicode-match-property-ecmascript "^1.0.4"
+ unicode-match-property-value-ecmascript "^1.0.2"
registry-auth-token@^3.0.1:
version "3.3.2"
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
+ integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==
dependencies:
rc "^1.1.6"
safe-buffer "^5.0.1"
@@ -6345,114 +6779,68 @@ registry-auth-token@^3.0.1:
registry-url@^3.0.3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
+ integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI=
dependencies:
rc "^1.0.1"
regjsgen@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
+ integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=
+
+regjsgen@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.4.0.tgz#c1eb4c89a209263f8717c782591523913ede2561"
+ integrity sha512-X51Lte1gCYUdlwhF28+2YMO0U6WeN0GLpgpA7LK7mbdDnkQYiwvEpmpe0F/cv5L14EbxgrdayAG3JETBv0dbXA==
regjsparser@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
+ integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=
+ dependencies:
+ jsesc "~0.5.0"
+
+regjsparser@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.3.0.tgz#3c326da7fcfd69fa0d332575a41c8c0cdf588c96"
+ integrity sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA==
dependencies:
jsesc "~0.5.0"
remove-trailing-separator@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+ integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
repeat-element@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
+ integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=
repeat-string@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae"
+ integrity sha1-x6jTI2BoNiBZp+RlH8aITosftK4=
-repeat-string@^1.5.2, repeat-string@^1.6.1:
+repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
-
-repeating@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
- dependencies:
- is-finite "^1.0.0"
-
-request@2.75.x:
- version "2.75.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93"
- dependencies:
- aws-sign2 "~0.6.0"
- aws4 "^1.2.1"
- bl "~1.1.2"
- caseless "~0.11.0"
- combined-stream "~1.0.5"
- extend "~3.0.0"
- forever-agent "~0.6.1"
- form-data "~2.0.0"
- har-validator "~2.0.6"
- hawk "~3.1.3"
- http-signature "~1.1.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.7"
- node-uuid "~1.4.7"
- oauth-sign "~0.8.1"
- qs "~6.2.0"
- stringstream "~0.0.4"
- tough-cookie "~2.3.0"
- tunnel-agent "~0.4.1"
-
-request@^2.0.0, request@^2.74.0:
- version "2.83.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
- dependencies:
- aws-sign2 "~0.7.0"
- aws4 "^1.6.0"
- caseless "~0.12.0"
- combined-stream "~1.0.5"
- extend "~3.0.1"
- forever-agent "~0.6.1"
- form-data "~2.3.1"
- har-validator "~5.0.3"
- hawk "~6.0.2"
- http-signature "~1.2.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.17"
- oauth-sign "~0.8.2"
- performance-now "^2.1.0"
- qs "~6.5.1"
- safe-buffer "^5.1.1"
- stringstream "~0.0.5"
- tough-cookie "~2.3.3"
- tunnel-agent "^0.6.0"
- uuid "^3.1.0"
-
-requestretry@^1.2.2:
- version "1.13.0"
- resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.13.0.tgz#213ec1006eeb750e8b8ce54176283d15a8d55d94"
- dependencies:
- extend "^3.0.0"
- lodash "^4.15.0"
- request "^2.74.0"
- when "^3.7.7"
+ integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+ integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
require-uncached@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
+ integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=
dependencies:
caller-path "^0.1.0"
resolve-from "^1.0.0"
@@ -6460,44 +6848,53 @@ require-uncached@^1.0.3:
requires-port@1.0.x, requires-port@1.x.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+ integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
+ integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=
dependencies:
resolve-from "^3.0.0"
resolve-from@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
+ integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=
resolve-from@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+ integrity sha1-six699nWiBvItuZTM17rywoYh0g=
resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+ integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@1.1.x:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+ integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
-resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0:
- version "1.7.1"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3"
+resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
+ integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==
dependencies:
path-parse "^1.0.5"
responselike@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
+ integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
dependencies:
lowercase-keys "^1.0.0"
restore-cursor@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+ integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
dependencies:
onetime "^2.0.0"
signal-exit "^3.0.2"
@@ -6505,22 +6902,24 @@ restore-cursor@^2.0.0:
ret@~0.1.10:
version "0.1.15"
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+ integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
-right-align@^0.1.1:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
- dependencies:
- align-text "^0.1.1"
+rfdc@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.2.tgz#e6e72d74f5dc39de8f538f65e00c36c18018e349"
+ integrity sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==
rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
+ integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==
dependencies:
glob "^7.0.5"
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
+ integrity sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=
dependencies:
hash-base "^2.0.0"
inherits "^2.0.1"
@@ -6528,60 +6927,60 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
run-async@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+ integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
dependencies:
is-promise "^2.1.0"
run-queue@^1.0.0, run-queue@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
+ integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=
dependencies:
aproba "^1.1.1"
rw@1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
-
-rx-lite-aggregates@^4.0.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
- dependencies:
- rx-lite "*"
-
-rx-lite@*, rx-lite@^4.0.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+ integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
rx@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+ integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=
rxjs@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1"
+ integrity sha512-OwMxHxmnmHTUpgO+V7dZChf3Tixf4ih95cmXjzzadULziVl/FKhHScGLj4goEw9weePVOH2Q0+GcCBUhKCZc/g==
dependencies:
tslib "^1.9.0"
-safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+safe-buffer@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
+ integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==
-safe-buffer@^5.1.2:
+safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+ integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
dependencies:
ret "~0.1.10"
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sanitize-html@^1.16.1:
version "1.16.3"
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.16.3.tgz#96c1b44a36ff7312e1c22a14b05274370ac8bd56"
+ integrity sha512-XpAJGnkMfNM7AzXLRw225blBB/pE4dM4jzRn98g4r88cfxwN6g+5IsRmCAh/gbhYGm6u6i97zsatMOM7Lr8wyw==
dependencies:
htmlparser2 "^3.9.0"
lodash.clonedeep "^4.5.0"
@@ -6594,50 +6993,82 @@ sanitize-html@^1.16.1:
sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+ integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.3, schema-utils@^0.4.4, schema-utils@^0.4.5:
+schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.4, schema-utils@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
+ integrity sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==
+ dependencies:
+ ajv "^6.1.0"
+ ajv-keywords "^3.1.0"
+
+schema-utils@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
+ integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
dependencies:
ajv "^6.1.0"
+ ajv-errors "^1.0.0"
ajv-keywords "^3.1.0"
+scope-css@^1.0.5:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/scope-css/-/scope-css-1.2.1.tgz#c35768bc900cad030a3e0d663a818c0f6a57f40e"
+ integrity sha512-UjLRmyEYaDNiOS673xlVkZFlVCtckJR/dKgr434VMm7Lb+AOOqXKdAcY7PpGlJYErjXXJzKN7HWo4uRPiZZG0Q==
+ dependencies:
+ escaper "^2.5.3"
+ slugify "^1.3.1"
+ strip-css-comments "^3.0.0"
+
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
+ integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
select2@3.5.2-browserify:
version "3.5.2-browserify"
resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d"
+ integrity sha1-3E2v2jjWenNOipekbw01Ka4FOR0=
select@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+ integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
selfsigned@^1.9.1:
version "1.10.1"
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.1.tgz#bf8cb7b83256c4551e31347c6311778db99eec52"
+ integrity sha1-v4y3uDJWxFUeMTR8YxF3jbme7FI=
dependencies:
node-forge "0.6.33"
semver-diff@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
+ integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=
dependencies:
semver "^5.0.3"
-"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0:
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
+"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1:
+ version "5.5.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
+ integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==
+
+semver@^5.6.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
+ integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
-send@0.16.1:
- version "0.16.1"
- resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3"
+send@0.16.2:
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
+ integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==
dependencies:
debug "2.6.9"
- depd "~1.1.1"
+ depd "~1.1.2"
destroy "~1.0.4"
- encodeurl "~1.0.1"
+ encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
@@ -6646,15 +7077,17 @@ send@0.16.1:
ms "2.0.0"
on-finished "~2.3.0"
range-parser "~1.2.0"
- statuses "~1.3.1"
+ statuses "~1.4.0"
serialize-javascript@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005"
+ integrity sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=
serve-index@^1.7.2:
version "1.9.0"
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.0.tgz#d2b280fc560d616ee81b48bf0fa82abed2485ce7"
+ integrity sha1-0rKA/FYNYW7oG0i/D6gqvtJIXOc=
dependencies:
accepts "~1.3.3"
batch "0.6.1"
@@ -6664,32 +7097,37 @@ serve-index@^1.7.2:
mime-types "~2.1.15"
parseurl "~1.3.1"
-serve-static@1.13.1:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719"
+serve-static@1.13.2:
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1"
+ integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==
dependencies:
- encodeurl "~1.0.1"
+ encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.2"
- send "0.16.1"
+ send "0.16.2"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-getter@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
+ integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
dependencies:
to-object-path "^0.3.0"
set-immediate-shim@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+ integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
set-value@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
+ integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE=
dependencies:
extend-shallow "^2.0.1"
is-extendable "^0.1.1"
@@ -6699,6 +7137,7 @@ set-value@^0.4.3:
set-value@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274"
+ integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==
dependencies:
extend-shallow "^2.0.1"
is-extendable "^0.1.1"
@@ -6708,18 +7147,22 @@ set-value@^2.0.0:
setimmediate@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+ integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
setprototypeof@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
+ integrity sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=
setprototypeof@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
+ integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
sha.js@^2.4.0, sha.js@^2.4.8:
version "2.4.10"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b"
+ integrity sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
@@ -6727,6 +7170,7 @@ sha.js@^2.4.0, sha.js@^2.4.8:
sha1@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848"
+ integrity sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg=
dependencies:
charenc ">= 0.0.1"
crypt ">= 0.0.1"
@@ -6734,59 +7178,50 @@ sha1@^1.1.1:
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+ integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+ integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+sigmund@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
+ integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
-
-slack-node@~0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/slack-node/-/slack-node-0.2.0.tgz#de4b8dddaa8b793f61dbd2938104fdabf37dfa30"
- dependencies:
- requestretry "^1.2.2"
-
-slash@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
+ integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
slice-ansi@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
+ integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==
dependencies:
is-fullwidth-code-point "^2.0.0"
-smart-buffer@^1.0.13, smart-buffer@^1.0.4:
- version "1.1.15"
- resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
-
-smart-buffer@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3"
+slugify@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.1.tgz#f572127e8535329fbc6c1edb74ab856b61ad7de2"
+ integrity sha512-6BwyhjF5tG5P8s+0DPNyJmBSBePG6iMyhjvIW5zGdA3tFik9PtK+yNkZgTeiroCRGZYgkHftFA62tGVK1EI9Kw==
smooshpack@^0.0.48:
version "0.0.48"
resolved "https://registry.yarnpkg.com/smooshpack/-/smooshpack-0.0.48.tgz#6fbeaaf59226a1fe500f56aa17185eed377d2823"
+ integrity sha512-BmaIr6fK6w7WBCI4V7tcZIg78WeE6X56zrhGSNk5vXavT1bQPXH+brZFq6Hwi833upY/yusG2FMVkf7TZsVv/w==
dependencies:
codesandbox-api "^0.0.18"
codesandbox-import-utils "^1.2.3"
lodash.isequal "^4.5.0"
-smtp-connection@2.12.0:
- version "2.12.0"
- resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1"
- dependencies:
- httpntlm "1.6.1"
- nodemailer-shared "1.1.0"
-
snapdragon-node@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+ integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
dependencies:
define-property "^1.0.0"
isobject "^3.0.0"
@@ -6795,12 +7230,14 @@ snapdragon-node@^2.0.1:
snapdragon-util@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+ integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
dependencies:
kind-of "^3.2.0"
snapdragon@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370"
+ integrity sha1-4StUh/re0+PeoKyR6UAL91tAE3A=
dependencies:
base "^0.11.1"
debug "^2.2.0"
@@ -6811,62 +7248,56 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^2.0.0"
-sntp@1.x.x:
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
- dependencies:
- hoek "2.x.x"
-
-sntp@2.x.x:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
- dependencies:
- hoek "4.x.x"
-
socket.io-adapter@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b"
+ integrity sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=
-socket.io-client@2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.4.tgz#0918a552406dc5e540b380dcd97afc4a64332f8e"
+socket.io-client@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.1.1.tgz#dcb38103436ab4578ddb026638ae2f21b623671f"
+ integrity sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==
dependencies:
backo2 "1.0.2"
base64-arraybuffer "0.1.5"
component-bind "1.0.0"
component-emitter "1.2.1"
- debug "~2.6.4"
- engine.io-client "~3.1.0"
+ debug "~3.1.0"
+ engine.io-client "~3.2.0"
+ has-binary2 "~1.0.2"
has-cors "1.1.0"
indexof "0.0.1"
object-component "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
- socket.io-parser "~3.1.1"
+ socket.io-parser "~3.2.0"
to-array "0.1.4"
-socket.io-parser@~3.1.1:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.1.2.tgz#dbc2282151fc4faebbe40aeedc0772eba619f7f2"
+socket.io-parser@~3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.2.0.tgz#e7c6228b6aa1f814e6148aea325b51aa9499e077"
+ integrity sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==
dependencies:
component-emitter "1.2.1"
- debug "~2.6.4"
- has-binary2 "~1.0.2"
+ debug "~3.1.0"
isarray "2.0.1"
-socket.io@2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.0.4.tgz#c1a4590ceff87ecf13c72652f046f716b29e6014"
+socket.io@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.1.1.tgz#a069c5feabee3e6b214a75b40ce0652e1cfb9980"
+ integrity sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==
dependencies:
- debug "~2.6.6"
- engine.io "~3.1.0"
+ debug "~3.1.0"
+ engine.io "~3.2.0"
+ has-binary2 "~1.0.2"
socket.io-adapter "~1.1.0"
- socket.io-client "2.0.4"
- socket.io-parser "~3.1.1"
+ socket.io-client "2.1.1"
+ socket.io-parser "~3.2.0"
-sockjs-client@1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12"
+sockjs-client@1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83"
+ integrity sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=
dependencies:
debug "^2.6.6"
eventsource "0.1.6"
@@ -6878,62 +7309,32 @@ sockjs-client@1.1.4:
sockjs@0.3.19:
version "0.3.19"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d"
+ integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==
dependencies:
faye-websocket "^0.10.0"
uuid "^3.0.1"
-socks-proxy-agent@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz#2eae7cf8e2a82d34565761539a7f9718c5617659"
- dependencies:
- agent-base "^4.1.0"
- socks "^1.1.10"
-
-socks-proxy-agent@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473"
- dependencies:
- agent-base "~4.2.0"
- socks "~2.2.0"
-
-socks@1.1.9:
- version "1.1.9"
- resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691"
- dependencies:
- ip "^1.1.2"
- smart-buffer "^1.0.4"
-
-socks@^1.1.10:
- version "1.1.10"
- resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a"
- dependencies:
- ip "^1.1.4"
- smart-buffer "^1.0.13"
-
-socks@~2.2.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.1.tgz#68ad678b3642fbc5d99c64c165bc561eab0215f9"
- dependencies:
- ip "^1.1.5"
- smart-buffer "^4.0.1"
-
sort-keys@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
+ integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
dependencies:
is-plain-obj "^1.0.0"
sortablejs@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.7.0.tgz#80a2b2370abd568e1cec8c271131ef30a904fa28"
+ integrity sha1-gKKyNwq9Vo4c7IwnETHvMKkE+ig=
source-list-map@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
+ integrity sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==
source-map-resolve@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a"
+ integrity sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==
dependencies:
atob "^2.0.0"
decode-uri-component "^0.2.0"
@@ -6941,61 +7342,54 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0"
urix "^0.1.0"
-source-map-support@^0.4.15:
- version "0.4.18"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
- dependencies:
- source-map "^0.5.6"
-
source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+ integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
source-map@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86"
+ integrity sha1-D+llA6yGpa213mP05BKuSHLNvoY=
-source-map@^0.4.4:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
- dependencies:
- amdefine ">=0.0.4"
-
-source-map@^0.5.0, source-map@^0.5.7, source-map@~0.5.6:
+source-map@^0.5.0, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
-
-source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
- version "0.5.6"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+ integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
+ integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50=
dependencies:
amdefine ">=0.0.4"
spdx-correct@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
+ integrity sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=
dependencies:
spdx-license-ids "^1.0.2"
spdx-expression-parse@~1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c"
+ integrity sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=
spdx-license-ids@^1.0.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
+ integrity sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=
spdy-transport@^2.0.18:
version "2.0.20"
resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.20.tgz#735e72054c486b2354fe89e702256004a39ace4d"
+ integrity sha1-c15yBUxIayNU/onnAiVgBKOazk0=
dependencies:
debug "^2.6.8"
detect-node "^2.0.3"
@@ -7008,6 +7402,7 @@ spdy-transport@^2.0.18:
spdy@^3.4.1:
version "3.4.7"
resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.7.tgz#42ff41ece5cc0f99a3a6c28aabb73f5c3b03acbc"
+ integrity sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=
dependencies:
debug "^2.6.8"
handle-thing "^1.2.5"
@@ -7019,76 +7414,76 @@ spdy@^3.4.1:
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+ integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
dependencies:
extend-shallow "^3.0.0"
split@0.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
+ integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=
dependencies:
through "2"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
sql.js@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/sql.js/-/sql.js-0.4.0.tgz#23be9635520eb0ff43a741e7e830397266e88445"
+ integrity sha1-I76WNVIOsP9Dp0Hn6DA5cmbohEU=
srcset@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/srcset/-/srcset-1.0.0.tgz#a5669de12b42f3b1d5e83ed03c71046fc48f41ef"
+ integrity sha1-pWad4StC87HV6D7QPHEEb8SPQe8=
dependencies:
array-uniq "^1.0.2"
number-is-nan "^1.0.0"
-sshpk@^1.7.0:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
- dependencies:
- asn1 "~0.2.3"
- assert-plus "^1.0.0"
- dashdash "^1.12.0"
- getpass "^0.1.1"
- optionalDependencies:
- bcrypt-pbkdf "^1.0.0"
- ecc-jsbn "~0.1.1"
- jsbn "~0.1.0"
- tweetnacl "~0.14.0"
-
ssri@^5.2.4:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.2.4.tgz#9985e14041e65fc397af96542be35724ac11da52"
+ integrity sha512-UnEAgMZa15973iH7cUi0AHjJn1ACDIkaMyZILoqwN6yzt+4P81I8tBc5Hl+qwi5auMplZtPQsHrPBR5vJLcQtQ==
dependencies:
safe-buffer "^5.1.1"
+ssri@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
+ integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
+ dependencies:
+ figgy-pudding "^3.5.1"
+
static-extend@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+ integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
dependencies:
define-property "^0.2.5"
object-copy "^0.1.0"
-"statuses@>= 1.3.1 < 2":
+"statuses@>= 1.3.1 < 2", statuses@~1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
-
-"statuses@>= 1.4.0 < 2":
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+ integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
statuses@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
+ integrity sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=
stickyfilljs@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/stickyfilljs/-/stickyfilljs-2.0.5.tgz#d229e372d2199ddf5d283bbe34ac1f7d2529c2fc"
+ integrity sha512-KGKdqKbv1jXit54ltFPIWw/XVeuSrJmTUS8viT1Pmdpp1Jyv3SMpFmhvPBdddX9FHDlHbm9s8cPAhPviBaBVpA==
stream-browserify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
+ integrity sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=
dependencies:
inherits "~2.0.1"
readable-stream "^2.0.2"
@@ -7096,12 +7491,14 @@ stream-browserify@^2.0.1:
stream-combiner@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
+ integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=
dependencies:
duplexer "~0.1.1"
stream-each@^1.1.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd"
+ integrity sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==
dependencies:
end-of-stream "^1.1.0"
stream-shift "^1.0.0"
@@ -7109,6 +7506,7 @@ stream-each@^1.1.0:
stream-http@^2.7.2:
version "2.8.2"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.2.tgz#4126e8c6b107004465918aa2fc35549e77402c87"
+ integrity sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==
dependencies:
builtin-status-codes "^3.0.0"
inherits "^2.0.1"
@@ -7119,10 +7517,12 @@ stream-http@^2.7.2:
stream-shift@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
+ integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=
streamroller@0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b"
+ integrity sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==
dependencies:
date-format "^1.2.0"
debug "^3.1.0"
@@ -7132,10 +7532,12 @@ streamroller@0.7.0:
strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
+ integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
string-width@^1.0.1, string-width@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
dependencies:
code-point-at "^1.0.0"
is-fullwidth-code-point "^1.0.0"
@@ -7144,6 +7546,7 @@ string-width@^1.0.1, string-width@^1.0.2:
string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+ integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
@@ -7151,62 +7554,55 @@ string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
string_decoder@^1.0.0, string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
-
-string_decoder@~1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
- dependencies:
- safe-buffer "~5.1.0"
-
-stringstream@~0.0.4, stringstream@~0.0.5:
- version "0.0.5"
- resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
+ integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
dependencies:
ansi-regex "^3.0.0"
-strip-bom@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
- dependencies:
- is-utf8 "^0.2.0"
-
strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+ integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
+strip-css-comments@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-css-comments/-/strip-css-comments-3.0.0.tgz#7a5625eff8a2b226cf8947a11254da96e13dae89"
+ integrity sha1-elYl7/iisibPiUehElTaluE9rok=
+ dependencies:
+ is-regexp "^1.0.0"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+ integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
-strip-indent@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
- dependencies:
- get-stdin "^4.0.1"
-
-strip-json-comments@~2.0.1:
+strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+ integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
-style-loader@^0.21.0:
- version "0.21.0"
- resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.21.0.tgz#68c52e5eb2afc9ca92b6274be277ee59aea3a852"
+style-loader@^0.23.0:
+ version "0.23.0"
+ resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.0.tgz#8377fefab68416a2e05f1cabd8c3a3acfcce74f1"
+ integrity sha512-uCcN7XWHkqwGVt7skpInW6IGO1tG6ReyFQ1Cseh0VcN6VdcFQi62aG/2F3Y9ueA8x4IVlfaSUxpmQXQD9QrEuQ==
dependencies:
loader-utils "^1.1.0"
schema-utils "^0.4.5"
@@ -7214,29 +7610,34 @@ style-loader@^0.21.0:
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+ integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
-supports-color@^3.1.0, supports-color@^3.1.2:
+supports-color@^3.1.0:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+ integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
dependencies:
has-flag "^1.0.0"
supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0:
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
svg4everybody@2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/svg4everybody/-/svg4everybody-2.1.9.tgz#5bd9f6defc133859a044646d4743fabc28db7e2d"
+ integrity sha1-W9n23vwTOFmgRGRtR0P6vCjbfi0=
-table@^4.0.1:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
+table@^4.0.3:
+ version "4.0.3"
+ resolved "http://registry.npmjs.org/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc"
+ integrity sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==
dependencies:
- ajv "^5.2.3"
- ajv-keywords "^2.1.0"
+ ajv "^6.0.1"
+ ajv-keywords "^3.0.0"
chalk "^2.1.0"
lodash "^4.17.4"
slice-ansi "1.0.0"
@@ -7245,14 +7646,17 @@ table@^4.0.1:
tapable@^0.1.8:
version "0.1.10"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4"
+ integrity sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=
-tapable@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2"
+tapable@^1.0.0, tapable@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.0.tgz#0d076a172e3d9ba088fd2272b2668fb8d194b78c"
+ integrity sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==
tar@^4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd"
+ integrity sha512-mq9ixIYfNF9SK0IS/h2HKMu8Q2iaCuhDDsZhdEag/FHv8fOaYld4vN7ouMgcSSt5WKZzPs8atclTcJm36OTh4w==
dependencies:
chownr "^1.0.1"
fs-minipass "^1.2.5"
@@ -7265,42 +7669,49 @@ tar@^4:
term-size@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
+ integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=
dependencies:
execa "^0.7.0"
-test-exclude@^4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa"
+test-exclude@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.0.0.tgz#cdce7cece785e0e829cd5c2b27baf18bc583cfb7"
+ integrity sha512-bO3Lj5+qFa9YLfYW2ZcXMOV1pmQvw+KS/DpjqhyX6Y6UZ8zstpZJ+mA2ERkXfpOqhxsJlQiLeVXD3Smsrs6oLw==
dependencies:
arrify "^1.0.1"
- micromatch "^3.1.8"
- object-assign "^4.1.0"
- read-pkg-up "^1.0.1"
+ minimatch "^3.0.4"
+ read-pkg-up "^4.0.0"
require-main-filename "^1.0.1"
-text-table@~0.2.0:
+text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
textextensions@2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286"
+ integrity sha512-j5EMxnryTvKxwH2Cq+Pb43tsf6sdEgw6Pdwxk83mPaq0ToeFJt6WE4J3s5BqY7vmjlLgkgXvhtXUxo80FyBhCA==
three-orbit-controls@^82.1.0:
version "82.1.0"
resolved "https://registry.yarnpkg.com/three-orbit-controls/-/three-orbit-controls-82.1.0.tgz#11a7f33d0a20ecec98f098b37780f6537374fab4"
+ integrity sha1-EafzPQog7OyY8Jizd4D2U3N0+rQ=
three-stl-loader@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/three-stl-loader/-/three-stl-loader-1.0.4.tgz#6b3319a31e3b910aab1883d19b00c81a663c3e03"
+ integrity sha1-azMZox47kQqrGIPRmwDIGmY8PgM=
three@^0.84.0:
version "0.84.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.84.0.tgz#95be85a55a0fa002aa625ed559130957dcffd918"
+ integrity sha1-lb6FpVoPoAKqYl7VWRMJV9z/2Rg=
through2@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
+ integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=
dependencies:
readable-stream "^2.1.5"
xtend "~4.0.1"
@@ -7308,70 +7719,75 @@ through2@^2.0.0:
through@2, through@^2.3.6, through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
-
-thunkify@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d"
+ integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
thunky@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e"
+ integrity sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4=
timeago.js@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-3.0.2.tgz#32a67e7c0d887ea42ca588d3aae26f77de5e76cc"
+ integrity sha1-MqZ+fA2IfqQspYjTquJvd95edsw=
dependencies:
"@types/jquery" "^2.0.40"
timed-out@^4.0.0, timed-out@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
+ integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=
timers-browserify@^2.0.4:
version "2.0.10"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae"
+ integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==
dependencies:
setimmediate "^1.0.4"
-timespan@2.3.x:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929"
-
tiny-emitter@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
+ integrity sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==
tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
dependencies:
os-tmpdir "~1.0.2"
to-array@0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
+ integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA=
to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
+ integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
to-fast-properties@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+ integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
to-object-path@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+ integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
dependencies:
kind-of "^3.0.2"
to-regex-range@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+ integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
dependencies:
is-number "^3.0.0"
repeat-string "^1.6.1"
@@ -7379,79 +7795,56 @@ to-regex-range@^2.1.0:
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+ integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
dependencies:
define-property "^2.0.2"
extend-shallow "^3.0.2"
regex-not "^1.0.2"
safe-regex "^1.1.0"
+toggle-selection@^1.0.3:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
+ integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
+
touch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
+ integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==
dependencies:
nopt "~1.0.10"
-tough-cookie@~2.3.0, tough-cookie@~2.3.3:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561"
- dependencies:
- punycode "^1.4.1"
-
-traverse@0.6.6:
- version "0.6.6"
- resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
-
-trim-newlines@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
-
trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+ integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
tryer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7"
-
-tryit@^1.0.1:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
+ integrity sha1-Antp+oIyJeVRys4+8DsR9qs3wdc=
tslib@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
-
-tsscmp@~1.0.0:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97"
+ integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
-
-tunnel-agent@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
- dependencies:
- safe-buffer "^5.0.1"
-
-tunnel-agent@~0.4.1:
- version "0.4.3"
- resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
-
-tweetnacl@^0.14.3, tweetnacl@~0.14.0:
- version "0.14.5"
- resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+ integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
dependencies:
prelude-ls "~1.1.2"
-type-is@~1.6.15:
+type-is@~1.6.15, type-is@~1.6.16:
version "1.6.16"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194"
+ integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.18"
@@ -7459,30 +7852,33 @@ type-is@~1.6.15:
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+ integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+
+typescript@^2:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
+ integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
uglify-es@^3.3.4:
version "3.3.9"
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
+ integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==
dependencies:
commander "~2.13.0"
source-map "~0.6.1"
-uglify-js@^2.6:
- version "2.8.29"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
+uglify-js@^3.1.4:
+ version "3.4.9"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
+ integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==
dependencies:
- source-map "~0.5.1"
- yargs "~3.10.0"
- optionalDependencies:
- uglify-to-browserify "~1.0.0"
-
-uglify-to-browserify@~1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
+ commander "~2.17.1"
+ source-map "~0.6.1"
uglifyjs-webpack-plugin@^1.2.4:
version "1.2.5"
resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz#2ef8387c8f1a903ec5e44fa36f9f3cbdcea67641"
+ integrity sha512-hIQJ1yxAPhEA2yW/i7Fr+SXZVMp+VEI3d42RTHBgQd2yhp/1UdBcR3QEWPV5ahBxlqQDMEMTuTEvDHSFINfwSw==
dependencies:
cacache "^10.0.4"
find-cache-dir "^1.0.0"
@@ -7496,24 +7892,47 @@ uglifyjs-webpack-plugin@^1.2.4:
ultron@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
+ integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==
undefsafe@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76"
+ integrity sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=
dependencies:
debug "^2.2.0"
underscore@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.0.tgz#31dbb314cfcc88f169cd3692d9149d81a00a73e4"
+ integrity sha512-4IV1DSSxC1QK48j9ONFK1MoIAKKkbE8i7u55w2R6IqBqbT7A/iG7aZBCR2Bi8piF0Uz+i/MG1aeqLwl/5vqF+A==
-underscore@~1.7.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
+unicode-canonical-property-names-ecmascript@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
+ integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
+ integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+ dependencies:
+ unicode-canonical-property-names-ecmascript "^1.0.4"
+ unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz#9f1dc76926d6ccf452310564fd834ace059663d4"
+ integrity sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0"
+ integrity sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==
union-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
+ integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=
dependencies:
arr-union "^3.1.0"
get-value "^2.0.6"
@@ -7523,32 +7942,38 @@ union-value@^1.0.0:
uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+ integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
unique-filename@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3"
+ integrity sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=
dependencies:
unique-slug "^2.0.0"
unique-slug@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab"
+ integrity sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=
dependencies:
imurmurhash "^0.1.4"
unique-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a"
+ integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=
dependencies:
crypto-random-string "^1.0.0"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
unset-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+ integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
dependencies:
has-value "^0.3.1"
isobject "^3.0.0"
@@ -7556,18 +7981,17 @@ unset-value@^1.0.0:
unzip-response@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
-
-upath@^1.0.0:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.5.tgz#02cab9ecebe95bbec6d5fc2566325725ab6d1a73"
+ integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=
upath@^1.0.5:
version "1.1.0"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd"
+ integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==
update-notifier@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451"
+ integrity sha1-TognpruRUUCrCTVZ1wFOPruDdFE=
dependencies:
boxen "^1.2.1"
chalk "^2.0.1"
@@ -7579,37 +8003,50 @@ update-notifier@^2.3.0:
semver-diff "^2.0.0"
xdg-basedir "^3.0.0"
+uri-js@^4.2.2:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+ integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+ dependencies:
+ punycode "^2.1.0"
+
urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+ integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url-join@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.0.tgz#4d3340e807d3773bda9991f8305acdcc2a665d2a"
+ integrity sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=
-url-loader@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.0.1.tgz#61bc53f1f184d7343da2728a1289ef8722ea45ee"
+url-loader@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.1.tgz#4d1f3b4f90dde89f02c008e662d604d7511167c1"
+ integrity sha512-vugEeXjyYFBCUOpX+ZuaunbK3QXMKaQ3zUnRfIpRBlGkY7QizCnzyyn2ASfcxsvyU3ef+CJppVywnl3Kgf13Gg==
dependencies:
loader-utils "^1.1.0"
mime "^2.0.3"
- schema-utils "^0.4.3"
+ schema-utils "^1.0.0"
url-parse-lax@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
+ integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=
dependencies:
prepend-http "^1.0.1"
url-parse-lax@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
+ integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
dependencies:
prepend-http "^2.0.0"
url-parse@1.0.x:
version "1.0.5"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b"
+ integrity sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=
dependencies:
querystringify "0.0.x"
requires-port "1.0.x"
@@ -7617,17 +8054,25 @@ url-parse@1.0.x:
url-parse@^1.1.8:
version "1.1.9"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.9.tgz#c67f1d775d51f0a18911dd7b3ffad27bb9e5bd19"
+ integrity sha1-xn8dd11R8KGJEd17P/rSe7nlvRk=
dependencies:
querystringify "~1.0.0"
requires-port "1.0.x"
+url-search-params-polyfill@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/url-search-params-polyfill/-/url-search-params-polyfill-5.0.0.tgz#09b98337c89dcf6c6a6a0bfeb096f6ba83b7526b"
+ integrity sha512-+SCD22QJp4UnqPOI5UTTR0Ljuh8cHbjEf1lIiZrZ8nHTlTixqwVsVQTSfk5vrmDz7N09/Y+ka5jQr0ff35FnQQ==
+
url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
+ integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=
url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
+ integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=
dependencies:
punycode "1.3.2"
querystring "0.2.0"
@@ -7635,6 +8080,7 @@ url@^0.11.0:
use@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8"
+ integrity sha1-riig1y+TvyJCKhii43mZMRLeyOg=
dependencies:
define-property "^0.2.5"
isobject "^3.0.0"
@@ -7643,6 +8089,7 @@ use@^2.0.0:
useragent@2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.2.1.tgz#cf593ef4f2d175875e8bb658ea92e18a4fd06d8e"
+ integrity sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=
dependencies:
lru-cache "2.2.x"
tmp "0.0.x"
@@ -7650,90 +8097,96 @@ useragent@2.2.1:
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+util.promisify@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
+ integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
+ dependencies:
+ define-properties "^1.1.2"
+ object.getownpropertydescriptors "^2.0.3"
util@0.10.3, util@^0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+ integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
dependencies:
inherits "2.0.1"
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+ integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
-uuid@^3.0.1, uuid@^3.1.0:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
-
-uws@~9.14.0:
- version "9.14.0"
- resolved "https://registry.yarnpkg.com/uws/-/uws-9.14.0.tgz#fac8386befc33a7a3705cbd58dc47b430ca4dd95"
+uuid@^3.0.1, uuid@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
+ integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
v8-compile-cache@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz#526492e35fc616864284700b7043e01baee09f0a"
+ integrity sha512-qNdTUMaCjPs4eEnM3W9H94R3sU70YCuT+/ST7nUf+id1bVOrdjrpUaeZLqPBPRph3hsgn4a4BvwpxhHZx+oSDg==
validate-npm-package-license@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
+ integrity sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=
dependencies:
spdx-correct "~1.0.0"
spdx-expression-parse "~1.0.0"
-vary@~1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37"
-
-vary@~1.1.2:
+vary@~1.1.1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
-
-verror@1.10.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
- dependencies:
- assert-plus "^1.0.0"
- core-util-is "1.0.2"
- extsprintf "^1.2.0"
+ integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
visibilityjs@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/visibilityjs/-/visibilityjs-1.2.4.tgz#bff8663da62c8c10ad4ee5ae6a1ae6fac4259d63"
+ integrity sha1-v/hmPaYsjBCtTuWuahrm+sQlnWM=
vm-browserify@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
+ integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=
dependencies:
indexof "0.0.1"
void-elements@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
+ integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
-vue-eslint-parser@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz#c268c96c6d94cfe3d938a5f7593959b0ca3360d1"
+vue-eslint-parser@^3.2.1:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-3.2.2.tgz#47c971ee4c39b0ee7d7f5e154cb621beb22f7a34"
+ integrity sha512-dprI6ggKCTwV22r+i8dtUGquiOCn063xyDmb7BV/BjG5Oc/m5EoMNrWevpvTcrlGuFZmYVPs5fgsu8UIxmMKzg==
dependencies:
debug "^3.1.0"
- eslint-scope "^3.7.1"
+ eslint-scope "^4.0.0"
eslint-visitor-keys "^1.0.0"
- espree "^3.5.2"
- esquery "^1.0.0"
- lodash "^4.17.4"
+ espree "^4.0.0"
+ esquery "^1.0.1"
+ lodash "^4.17.10"
vue-functional-data-merge@^2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/vue-functional-data-merge/-/vue-functional-data-merge-2.0.6.tgz#f08055adfb92458debcf2ad10c3aa712277f7fc2"
+ integrity sha512-eivElFOJwhXJopKlq71/8onDxOKK4quPwWGFF9yIVjpU2sNzxISRpufu18bh674ivSADuEAPU2OhT+vrH0E9Mg==
vue-hot-reload-api@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
+ integrity sha512-2j/t+wIbyVMP5NvctQoSUvLkYKoWAAk2QlQiilrM2a6/ulzFgdcLUJfTvs4XQ/3eZhHiBmmEojbjmM4AzZj8JA==
-vue-loader@^15.2.4:
- version "15.2.4"
- resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.2.4.tgz#a7b923123d3cf87230a8ff54a1c16d31a6c5dbb4"
+vue-loader@^15.4.2:
+ version "15.4.2"
+ resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.4.2.tgz#812bb26e447dd3b84c485eb634190d914ce125e2"
+ integrity sha512-nVV27GNIA9MeoD8yQ3dkUzwlAaAsWeYSWZHsu/K04KCD339lW0Jv2sJWsjj3721SP7sl2lYdPmjcHgkWQSp5bg==
dependencies:
- "@vue/component-compiler-utils" "^1.2.1"
+ "@vue/component-compiler-utils" "^2.0.0"
hash-sum "^1.0.2"
loader-utils "^1.1.0"
vue-hot-reload-api "^2.3.0"
@@ -7742,23 +8195,27 @@ vue-loader@^15.2.4:
vue-resource@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.5.0.tgz#ba0c6ef7af2eeace03cf24a91f529471be974c72"
+ integrity sha512-em+Ihe+duUWQv4uKO8aFTGK+e/lvNtk5EBEmWaBYcfQzBmHhKR4jJAeVIHcG6otugmsme/DmYrOEPfbss+2XfQ==
dependencies:
got "^8.0.3"
vue-router@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
+ integrity sha512-vLLoY452L+JBpALMP5UHum9+7nzR9PeIBCghU9ZtJ1eWm6ieUI8Zb/DI3MYxH32bxkjzYV1LRjNv4qr8d+uX/w==
vue-style-loader@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.0.tgz#7588bd778e2c9f8d87bfc3c5a4a039638da7a863"
+ integrity sha512-IsSiXDrLW2QIjyBsCqa35e45l5AceMbJ2jO8DxoEQv75xu/UmtXkSC0ybESq/LpbmmIW47MAWDQvErUw+Hrz/A==
dependencies:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
-vue-template-compiler@^2.5.16:
- version "2.5.16"
- resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.16.tgz#93b48570e56c720cdf3f051cc15287c26fbd04cb"
+vue-template-compiler@^2.5.0, vue-template-compiler@^2.5.17:
+ version "2.5.17"
+ resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.17.tgz#52a4a078c327deb937482a509ae85c06f346c3cb"
+ integrity sha512-63uI4syCwtGR5IJvZM0LN5tVsahrelomHtCxvRkZPJ/Tf3ADm1U1wG6KWycK3qCfqR+ygM5vewUvmJ0REAYksg==
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
@@ -7766,22 +8223,27 @@ vue-template-compiler@^2.5.16:
vue-template-es2015-compiler@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
+ integrity sha512-x3LV3wdmmERhVCYy3quqA57NJW7F3i6faas++pJQWtknWT+n7k30F4TVdHvCLn48peTJFRvCpxs3UuFPqgeELg==
vue-virtual-scroll-list@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/vue-virtual-scroll-list/-/vue-virtual-scroll-list-1.2.5.tgz#bcbd010f7cdb035eba8958ebf807c6214d9a167a"
+ integrity sha1-vL0BD3zbA166iVjr+AfGIU2aFno=
-vue@^2.5.16:
- version "2.5.16"
- resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
+vue@^2.5.16, vue@^2.5.17:
+ version "2.5.17"
+ resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.17.tgz#0f8789ad718be68ca1872629832ed533589c6ada"
+ integrity sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ==
vuex@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"
+ integrity sha512-wLoqz0B7DSZtgbWL1ShIBBCjv22GV5U+vcBFox658g6V0s4wZV9P4YjCNyoHSyIBpj1f29JBoNQIqD82cR4O3w==
watchpack@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.5.0.tgz#231e783af830a22f8966f65c4c4bacc814072eed"
+ integrity sha512-RSlipNQB1u48cq0wH/BNfCu1tD/cJ8ydFIkNYhp9o+3d+8unClkIovpW5qpFPgmL9OE48wfAnlZydXByWP82AA==
dependencies:
chokidar "^2.0.2"
graceful-fs "^4.1.2"
@@ -7790,29 +8252,32 @@ watchpack@^1.5.0:
wbuf@^1.1.0, wbuf@^1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe"
+ integrity sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4=
dependencies:
minimalistic-assert "^1.0.0"
-webpack-bundle-analyzer@^2.13.1:
- version "2.13.1"
- resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz#07d2176c6e86c3cdce4c23e56fae2a7b6b4ad526"
+webpack-bundle-analyzer@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.2.tgz#22f19ea6d1b5a15fd7a90baae0bc0f39bd1e4d48"
+ integrity sha512-cZG4wSQtKrSpk5RJ33dxiaAyo8bP0V+JvycAyIDFEiDIhw4LHhhVKhn40YT1w6TR9E4scHA00LnIoBtTA13Mow==
dependencies:
- acorn "^5.3.0"
- bfj-node4 "^5.2.0"
- chalk "^2.3.0"
- commander "^2.13.0"
- ejs "^2.5.7"
- express "^4.16.2"
- filesize "^3.5.11"
- gzip-size "^4.1.0"
- lodash "^4.17.4"
+ acorn "^5.7.3"
+ bfj "^6.1.1"
+ chalk "^2.4.1"
+ commander "^2.18.0"
+ ejs "^2.6.1"
+ express "^4.16.3"
+ filesize "^3.6.1"
+ gzip-size "^5.0.0"
+ lodash "^4.17.10"
mkdirp "^0.5.1"
- opener "^1.4.3"
- ws "^4.0.0"
+ opener "^1.5.1"
+ ws "^6.0.0"
-webpack-cli@^3.0.8:
- version "3.0.8"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.8.tgz#90eddcf04a4bfc31aa8c0edc4c76785bc4f1ccd9"
+webpack-cli@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.1.0.tgz#d71a83687dcfeb758fdceeb0fe042f96bcf62994"
+ integrity sha512-p5NeKDtYwjZozUWq6kGNs9w+Gtw/CPvyuXjXn2HMdz8Tie+krjEg8oAtonvIyITZdvpF7XG9xDHwscLr2c+ugQ==
dependencies:
chalk "^2.4.1"
cross-spawn "^6.0.5"
@@ -7824,26 +8289,27 @@ webpack-cli@^3.0.8:
loader-utils "^1.1.0"
supports-color "^5.4.0"
v8-compile-cache "^2.0.0"
- yargs "^11.1.0"
+ yargs "^12.0.1"
-webpack-dev-middleware@3.1.3, webpack-dev-middleware@^3.0.1:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz#8b32aa43da9ae79368c1bf1183f2b6cf5e1f39ed"
+webpack-dev-middleware@3.2.0, webpack-dev-middleware@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.2.0.tgz#a20ceef194873710052da678f3c6ee0aeed92552"
+ integrity sha512-YJLMF/96TpKXaEQwaLEo+Z4NDK8aV133ROF6xp9pe3gQoS7sxfpXh4Rv9eC+8vCvWfmDjRQaMSlRPbO+9G6jgA==
dependencies:
loud-rejection "^1.6.0"
memory-fs "~0.4.1"
- mime "^2.1.0"
+ mime "^2.3.1"
path-is-absolute "^1.0.0"
range-parser "^1.0.3"
url-join "^4.0.0"
- webpack-log "^1.0.1"
+ webpack-log "^2.0.0"
-webpack-dev-server@^3.1.4:
- version "3.1.4"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.4.tgz#9a08d13c4addd1e3b6d8ace116e86715094ad5b4"
+webpack-dev-server@^3.1.8:
+ version "3.1.8"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.8.tgz#eb7a95945d1108170f902604fb3b939533d9daeb"
+ integrity sha512-c+tcJtDqnPdxCAzEEZKdIPmg3i5i7cAHe+B+0xFNK0BlCc2HF/unYccbU7xTgfGc5xxhCztCQzFmsqim+KhI+A==
dependencies:
ansi-html "0.0.7"
- array-includes "^3.0.3"
bonjour "^3.5.0"
chokidar "^2.0.0"
compression "^1.5.2"
@@ -7853,36 +8319,37 @@ webpack-dev-server@^3.1.4:
express "^4.16.2"
html-entities "^1.2.0"
http-proxy-middleware "~0.18.0"
- import-local "^1.0.0"
- internal-ip "1.2.0"
+ import-local "^2.0.0"
+ internal-ip "^3.0.1"
ip "^1.1.5"
killable "^1.0.0"
loglevel "^1.4.1"
opn "^5.1.0"
portfinder "^1.0.9"
+ schema-utils "^1.0.0"
selfsigned "^1.9.1"
serve-index "^1.7.2"
sockjs "0.3.19"
- sockjs-client "1.1.4"
+ sockjs-client "1.1.5"
spdy "^3.4.1"
strip-ansi "^3.0.0"
supports-color "^5.1.0"
- webpack-dev-middleware "3.1.3"
- webpack-log "^1.1.2"
- yargs "11.0.0"
+ webpack-dev-middleware "3.2.0"
+ webpack-log "^2.0.0"
+ yargs "12.0.2"
-webpack-log@^1.0.1, webpack-log@^1.1.2:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d"
+webpack-log@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f"
+ integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==
dependencies:
- chalk "^2.1.0"
- log-symbols "^2.1.0"
- loglevelnext "^1.0.1"
- uuid "^3.1.0"
+ ansi-colors "^3.0.0"
+ uuid "^3.3.2"
-webpack-sources@^1.0.1, webpack-sources@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54"
+webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.2.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85"
+ integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==
dependencies:
source-list-map "^2.0.0"
source-map "~0.6.1"
@@ -7890,23 +8357,24 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0:
webpack-stats-plugin@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.2.1.tgz#1f5bac13fc25d62cbb5fd0ff646757dc802b8595"
+ integrity sha512-OYMZLpZrK/qLA79NE4kC4DCt85h/5ipvWJcsefKe9MMw0qU4/ck/IJg+4OmWA+5EfrZZpHXDq92IptfYDWVfkw==
-webpack@^4.16.0:
- version "4.16.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.0.tgz#660dae90890e55b8ed17c6f9d17bebb01dab5b4c"
+webpack@^4.19.1:
+ version "4.19.1"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.19.1.tgz#096674bc3b573f8756c762754366e5b333d6576f"
+ integrity sha512-j7Q/5QqZRqIFXJvC0E59ipLV5Hf6lAnS3ezC3I4HMUybwEDikQBVad5d+IpPtmaQPQArvgUZLXIN6lWijHBn4g==
dependencies:
- "@webassemblyjs/ast" "1.5.13"
- "@webassemblyjs/helper-module-context" "1.5.13"
- "@webassemblyjs/wasm-edit" "1.5.13"
- "@webassemblyjs/wasm-opt" "1.5.13"
- "@webassemblyjs/wasm-parser" "1.5.13"
+ "@webassemblyjs/ast" "1.7.6"
+ "@webassemblyjs/helper-module-context" "1.7.6"
+ "@webassemblyjs/wasm-edit" "1.7.6"
+ "@webassemblyjs/wasm-parser" "1.7.6"
acorn "^5.6.2"
acorn-dynamic-import "^3.0.0"
ajv "^6.1.0"
ajv-keywords "^3.1.0"
chrome-trace-event "^1.0.0"
enhanced-resolve "^4.1.0"
- eslint-scope "^3.7.1"
+ eslint-scope "^4.0.0"
json-parse-better-errors "^1.0.2"
loader-runner "^2.3.0"
loader-utils "^1.1.0"
@@ -7916,70 +8384,63 @@ webpack@^4.16.0:
neo-async "^2.5.0"
node-libs-browser "^2.0.0"
schema-utils "^0.4.4"
- tapable "^1.0.0"
+ tapable "^1.1.0"
uglifyjs-webpack-plugin "^1.2.4"
watchpack "^1.5.0"
- webpack-sources "^1.0.1"
+ webpack-sources "^1.2.0"
websocket-driver@>=0.5.1:
version "0.6.5"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36"
+ integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=
dependencies:
websocket-extensions ">=0.1.1"
websocket-extensions@>=0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7"
-
-when@^3.7.7:
- version "3.7.8"
- resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82"
+ integrity sha1-domUmcGEtu91Q3fC27DNbLVdKec=
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+ integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
which@^1.1.1, which@^1.2.1, which@^1.2.9:
version "1.3.0"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
+ integrity sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==
dependencies:
isexe "^2.0.0"
wide-align@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
+ integrity sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==
dependencies:
string-width "^1.0.2"
widest-line@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273"
+ integrity sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=
dependencies:
string-width "^2.1.1"
-window-size@0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
-
-with-callback@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/with-callback/-/with-callback-1.0.2.tgz#a09629b9a920028d721404fb435bdcff5c91bc21"
-
-wordwrap@0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
-
wordwrap@^1.0.0, wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+ integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+ integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
worker-farm@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.5.2.tgz#32b312e5dc3d5d45d79ef44acc2587491cd729ae"
+ integrity sha512-XxiQ9kZN5n6mmnW+mFJ+wXjNNI/Nx4DIdaAKLX1Bn6LYBWlN/zaBhu34DQYPZ1AJobQuu67S2OfDdNSVULvXkQ==
dependencies:
errno "^0.1.4"
xtend "^4.0.1"
@@ -7987,6 +8448,7 @@ worker-farm@^1.5.2:
worker-loader@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-2.0.0.tgz#45fda3ef76aca815771a89107399ee4119b430ac"
+ integrity sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==
dependencies:
loader-utils "^1.0.0"
schema-utils "^0.4.0"
@@ -7994,6 +8456,7 @@ worker-loader@^2.0.0:
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+ integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
dependencies:
string-width "^1.0.1"
strip-ansi "^3.0.1"
@@ -8001,10 +8464,12 @@ wrap-ansi@^2.0.0:
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
write-file-atomic@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
+ integrity sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==
dependencies:
graceful-fs "^4.1.11"
imurmurhash "^0.1.4"
@@ -8013,20 +8478,21 @@ write-file-atomic@^2.0.0:
write@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
+ integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=
dependencies:
mkdirp "^0.5.1"
-ws@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/ws/-/ws-4.0.0.tgz#bfe1da4c08eeb9780b986e0e4d10eccd7345999f"
+ws@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-6.0.0.tgz#eaa494aded00ac4289d455bac8d84c7c651cef35"
+ integrity sha512-c2UlYcAZp1VS8AORtpq6y4RJIkJ9dQz18W32SpR/qXGfLDZ2jU4y4wKvvZwqbi7U6gxFQTeE+urMbXU/tsDy4w==
dependencies:
async-limiter "~1.0.0"
- safe-buffer "~5.1.0"
- ultron "~1.1.0"
ws@~3.3.1:
version "3.3.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
+ integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==
dependencies:
async-limiter "~1.0.0"
safe-buffer "~5.1.0"
@@ -8035,92 +8501,79 @@ ws@~3.3.1:
xdg-basedir@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
+ integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
+
+xmlbuilder@8.2.2:
+ version "8.2.2"
+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773"
+ integrity sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=
xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
+ integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
xmlhttprequest@1:
version "1.8.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
+ integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=
-xregexp@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
+xregexp@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
+ integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+ integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68=
xterm@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.5.0.tgz#ba3f464bc5730c9d259ebe62131862224db9ddcc"
+ integrity sha512-IpG3P3gkT0/xDPS0j3igpk92JYlUajaEHk3/EQSUeIRJmPiF2lyham3Xt/GD3o98uOrRluvowjNj0AFeYK+AXQ==
-y18n@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
-
-y18n@^4.0.0:
+"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+ integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+ integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.0, yallist@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
+ integrity sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=
-yargs-parser@^9.0.2:
- version "9.0.2"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077"
+yargs-parser@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"
+ integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==
dependencies:
camelcase "^4.1.0"
-yargs@11.0.0:
- version "11.0.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b"
- dependencies:
- cliui "^4.0.0"
- decamelize "^1.1.1"
- find-up "^2.1.0"
- get-caller-file "^1.0.1"
- os-locale "^2.0.0"
- require-directory "^2.1.1"
- require-main-filename "^1.0.1"
- set-blocking "^2.0.0"
- string-width "^2.0.0"
- which-module "^2.0.0"
- y18n "^3.2.1"
- yargs-parser "^9.0.2"
-
-yargs@^11.1.0:
- version "11.1.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"
+yargs@12.0.2, yargs@^12.0.1:
+ version "12.0.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.2.tgz#fe58234369392af33ecbef53819171eff0f5aadc"
+ integrity sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==
dependencies:
cliui "^4.0.0"
- decamelize "^1.1.1"
- find-up "^2.1.0"
+ decamelize "^2.0.0"
+ find-up "^3.0.0"
get-caller-file "^1.0.1"
- os-locale "^2.0.0"
+ os-locale "^3.0.0"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^2.0.0"
which-module "^2.0.0"
- y18n "^3.2.1"
- yargs-parser "^9.0.2"
-
-yargs@~3.10.0:
- version "3.10.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
- dependencies:
- camelcase "^1.0.2"
- cliui "^2.1.0"
- decamelize "^1.0.0"
- window-size "0.1.0"
+ y18n "^3.2.1 || ^4.0.0"
+ yargs-parser "^10.1.0"
yeast@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
+ integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=